acl2-sources/0002775002132200015000000000000012222334002012543 5ustar kaufmannacl2acl2-sources/acl2-characters0000664002132200015000000000040011702441416015426 0ustar kaufmannacl2  !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~acl2-sources/acl2-check.lisp0000664002132200015000000003756512222115527015357 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; Because of the last form in this file, this file should not be loaded as part ; of an executable ACL2 image. ; Warning: This file should not be compiled. The intention is to load the ; .lisp file each time we build ACL2, regardless of the host Lisp. (in-package "ACL2") ; See section "CHECKS" of acl2.lisp for more checks. ; We begin with a basic test related to the CR/NL character pairs that can ; occur in some systems, but which ACL2 assumes are not routinely present. (or (equal (length "ab cd") 5) (error "We have tested that the string ab\\ncd (where \\n denotes a Newline character) has length 5, but it does not. Perhaps your system is using two characters to indicate a new line?")) ; We put the following check here to be sure that the function ; legal-variable-or-constant-namep checks for all possible lambda list ; keywords. (dolist (name lambda-list-keywords) (cond ; The check from legal-variable-or-constant-namep is here. ((let ((s (symbol-name name))) (or (= (length s) 0) (not (eql (char s 0) #\&)))) (error "We assume that all lambda-list-keywords start with the ~ character #\&.~%However, ~s does not. ACL2 will not work in ~ this Common Lisp." name)))) (or (and (integerp char-code-limit) (<= 256 char-code-limit) (typep 256 'fixnum)) (error "We assume that 256 is a fixnum not exceeding char-code-limit, for ~ purposes of~%character manipulation. ACL2 will not work in this ~ Common Lisp.")) ; Essay on Fixnum Declarations ; To the best of our knowledge, the values of most-positive-fixnum in various ; lisps are as follows, so we feel safe in using (signed-byte 30) and hence ; (unsigned-byte 29) to represent fixnums. At worst, if a lisp is used for ; which (signed-byte 30) is not a subtype of fixnum, a compiler may simply fail ; to create efficient code. Note: ; (the (signed-byte 30) 536870911) ; succeeds ; (the (signed-byte 30) 536870912) ; fails ; (the (unsigned-byte 29) 536870911) ; succeeds ; (the (unsigned-byte 29) 536870912) ; fails ; Values of most-positive-fixnum in 32-bit Lisps: ; AKCL, GCL: 2147483647 ; Allegro: 536870911 ; Lucid: 536870911 ; cmulisp: 536870911 ; SBCL: 536870911 ; CCL: 536870911 ; MCL: 268435455 ; not supported after ACL2 Version_3.1 ; CLISP: 16777215 ; Lispworks: 536870911 [version 6.0.1; but observed 8388607 in versions 4.2.0 ; and 4.4.6] ; We have made many type declarations in the sources of (signed-byte 30). ; Performance could be seriously degraded if these were not fixnum ; declarations. If the following check fails, then we should consider lowering ; 30. However, clisp has 24-bit fixnums. Clisp maintainer Sam Steingold has ; assured us that "CLISP has a very efficient bignum implementation." Lispworks ; Version 4.2.0 on Linux, 32-bit, had most-positive-fixnum = 8388607 and ; most-negative-fixnum = -8388608; and we have been informed (email 10/22/02) ; that "this is an architectural limit on this platform and the LispWorks fixnum ; size cannot be reconfigured." But Lispworks 6 is back to supporting larger ; fixnums. #-(or clisp (and lispworks (not lispworks-64bit))) (or (and (<= (1- (ash 1 29)) most-positive-fixnum) (<= most-negative-fixnum (- (ash 1 29)))) (error "We assume for performance reasons that numbers from (- (ash 1 29)) to (1- (ash 1 29)) are fixnums in Common Lisp implementations. If you see this error, then please contact the ACL2 implementors and tell them which Common Lisp implementation you are using, and that in that Lisp, most-positive-fixnum = ~s and most-negative-fixnum = ~s." most-positive-fixnum most-negative-fixnum)) ; Now we deal with the existence of case-sensitive images in Allegro. #+(and allegro allegro-version>= (version>= 6 0)) (when (not (eq excl::*current-case-mode* :case-insensitive-upper)) (error " This Allegro Common Lisp image is not compatible with ACL2 because *current-case-mode* has the value ~s rather than the value ~s. You can try executing the form (set-case-mode :case-insensitive-upper) before building ACL2 and that should solve the problem, although the Allegro 6.0 documentation claims: \"... it is much better to use an image with the desired case mode rather than changing the case mode after the image has started.\"" *current-case-mode* :case-insensitive-upper)) (or (equal (symbol-name 'a) "A") (error "This Common Lisp image appears to be case-sensitive:~%~ (equal (symbol-name 'a) \"A\") evaluates to NIL.~%~ It is therefore not suitable for ACL2.")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ACL2 CHARACTERS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; It is difficult or impossible to ensure that #\c is read in as the same ; character in different ACL2 sessions, for any ACL2 character c. (Even ; #\Newline is potentially problematic, as its char-code is 10 in most Common ; Lisps but is 13 in [now-obsolete] MCL!) Thus, the soundness of ACL2 rests on ; a caveat that all books are certified using the same Lisp image. When we say ; ``same lisp image'' we don't mean the same exact process necessarily, but ; rather, an invocation of the same saved image. Another reason for such a ; caveat, independent of the character-reading issue, is that different saved ; images may be tied into different compilers, thus making the object files of ; the books incompatible. ; Nevertheless, it would of course be nice if all host Lisp implementations of ; ACL2 actually do agree on character and string constants provided by the Lisp ; reader (based on *acl2-readtable*). Our first check is intended to address ; this point, by providing some confidence that the host Lisp has an ASCII ; character set. As explained above, we only intend soundness in the case that ; all books are certified from scratch using the same host Lisp, and we do not ; actually assume ASCII characters -- more precisely, we do not assume any ; particular values for code-char and code-char -- so this check is not really ; necessary, except for a claim about ASCII characters in "Precise Description ; of the ACL2 Logic", which should perhaps be removed. However, as of January ; 2012 we seem to be able to make the following check in all supported Lisps, ; at least on our Linux and Mac OS platforms. It should be sound for users to ; comment out this check, but with the understanding that ACL2 is taking ; whatever the Lisp reader (using *acl2-readtable*) gives it. (let ((filename "acl2-characters")) ; Why do we read from a separate file, rather than just saving the relevant ; characters in the present file? For example, it may seem reasonable to use a ; trusted host Lisp to obtain an alist by evaluating the following expression. ; (loop for i from 0 to 255 do (princ (cons i (code-char i)))) ; Could the resulting the alist be placed into this file and used for the check ; below? ; We run into a problem right away with that approach because of Control-D. ; For example, try this ; (princ (cons 4 (code-char 4))) ; and then try quoting the output and reading it back in. Or, ; try evaluating the following. ; (read-from-string (format nil "~a" (code-char 4))) ; We have seen an error in both cases. ; So instead, we originally generated the file acl2-characters as follows, ; using an ACL2 image based on CCL. ; (with-open-file (str "acl2-characters" :direction :output) ; (loop for i from 0 to 255 ; do (write-char (code-char i) str))) ; We explicitly specify the :external-format in the case of host Lisps for ; which we set the character encoding after the build, on the command line ; written by save-acl2. (with-open-file (str filename :direction :input) (loop for i from 0 to 255 ; The function legal-acl2-character-p, called in bad-lisp-objectp for ; characters and strings, checks that the char-code of any character that is ; read must be between 0 and 255. do (let ((temp (read-char str))) (when #-clisp (not (eql (char-code temp) i)) ; In CLISP we find character 10 (Newline) at position 13 (expected Return). ; Perhaps this has something to do CLISP's attempt to comply with the CL ; HyperSpec Section 13.1.8, "Treatment of Newline during Input and Output". ; But something is amiss. Consider for example the following log (with some ; output edited in the case of the first two forms, but no editing of output ; for the third form). ; ACL2 [RAW LISP]> (with-open-file (str "tmp" :direction :output) ; (write-char (code-char 13) str)) ; #\Return ; ACL2 [RAW LISP]> (with-open-file (str "tmp" :direction :input) ; (equal (read-char str) (code-char 10))) ; T ; ACL2 [RAW LISP]> (format nil "~a" (code-char 13)) ; " ; ACL2 [RAW LISP]> ; So our check for CLISP is incomplete, but as explained in the comment just ; above this check, we can live with that. #+clisp (and (not (eql (char-code temp) i)) (not (eql i 13))) (error "Bad character in file ~s: character ~s at position ~s." filename (char-code temp) i)))))) ; The check just above does not say anything about the five character names ; that we support (see acl2-read-character-string), as described in :doc ; characters; so we add suitable checks on these here. (loop for pair in (pairlis '(#\Space #\Tab #\Newline #\Page #\Rubout) '(32 9 10 12 127)) do (let* ((ch (car pair)) (code (cdr pair)) (val (char-code ch))) (or (eql val code) (error "(char-code ~s) evaluated to ~s (expected ~s)." ch val code)))) ; Test that all purportedly standard characters are standard, and vice versa. (dotimes (i 256) (let ((ch (code-char i))) (or (equal (standard-char-p ch) (or #+(and mcl (not ccl)) (= i 13) #-(and mcl (not ccl)) (= i 10) (and (>= i 32) (<= i 126)))) (error "This Common Lisp is unsuitable for ACL2 because the ~ character~%with char-code ~d ~a standard in this ~ Common Lisp but should~%~abe." i (if (standard-char-p ch) "is" "is not") (if (standard-char-p ch) "not " ""))))) ; Check that char-upcase and char-downcase have the same values in all lisps, ; and in particular, keep us in the realm of ACL2 characters. Starting with ; Version_2.6 we limit our check to the standard characters (and we no longer ; avoid the check for mcl) because the guard to char-upcase and char-downcase ; limits the use of these functions to standard characters. (dotimes (i 256) (let ((ch (code-char i))) (or (not (standard-char-p ch)) (eql (char-downcase ch) (if (and (>= i 65) (<= i 90)) (code-char (+ i 32)) ch)) (error "This Common Lisp is unsuitable for ACL2 because ~ (char-downcase ~s)~%is ~s but should be ~s." ch (char-downcase ch) (if (and (>= i 65) (<= i 90)) (code-char (+ i 32)) ch))))) (dotimes (i 256) (let ((ch (code-char i))) (or (not (standard-char-p ch)) (eql (char-upcase ch) (if (and (>= i 97) (<= i 122)) (code-char (- (char-code ch) 32)) ch)) (error "This Common Lisp is unsuitable for ACL2 because ~ (char-upcase ~s)~%is ~s but should be ~s." ch (char-upcase ch) (if (and (>= i 65) (<= i 90)) (code-char (- (char-code ch) 32)) ch))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; FEATURES, MISC. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The conditional reader macro doesn't care about the package when it looks for ; the symbol. In fact, :UNIX is not a member of *features* in gcl; LISP:UNIX ; is. #-(or unix apple mswindows) (error "This Common Lisp is unsuitable for ACL2 because~%~ neither :UNIX nor :APPLE nor :MSWINDOWS is a member of *features*.") (or (typep (1- array-dimension-limit) 'fixnum) (error "We assume that (1- ARRAY-DIMENSION-LIMIT) is a fixnum. CLTL2 ~ requires this. ACL2 will not work in this Common Lisp.")) (or (>= multiple-values-limit *number-of-return-values*) (error "We assume that multiple-values-limit is at least ~s, but in this ~ Common Lisp its value is ~s. Please contact the ACL2 implementors ~ about lowering the value of ACL2 constant ~ *NUMBER-OF-RETURN-VALUES*." multiple-values-limit *number-of-return-values*)) ; The following check must be put last in this file, since we don't entirely ; trust that it won't corrupt the current image. We collect all symbols in ; *common-lisp-symbols-from-main-lisp-package*, other than those that have the ; syntax of a lambda list keyword, that are special. (let ((badvars nil)) (dolist (var *copy-of-common-lisp-symbols-from-main-lisp-package*) (or (member var *copy-of-common-lisp-specials-and-constants* :test #'eq) (if (and (let ((s (symbol-name var))) (or (= (length s) 0) (not (eql (char s 0) #\&)))) (eval `(let ((,var (gensym))) ; We are not aware of any predicate, defined in all Common Lisp ; implementations, for checking that a variable is special; so we write our own ; behavioral test here. If var is special, then the above binding will make it ; boundp and update its symbol-value. Conversely, if var is not special, then ; there are two cases: either it is not boundp before the binding above in ; which case it remains not boundp, or else its global value is not the above ; gensym value. (and (boundp ',var) (eq ,var (symbol-value ',var)))))) (setq badvars (cons var badvars))))) (if badvars (error "The following constants or special variables in the main~%~ Lisp package needs to be included in the list~%~ *common-lisp-specials-and-constants*:~%~ ~s." badvars))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (format t "Check completed.~%") ; Do not put any forms below! See comment above. acl2-sources/acl2-fns.lisp0000664002132200015000000020003712222115527015052 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PRELIMINARIES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro qfuncall (fn &rest args) ; Avoid noise in CCL about undefined functions, not avoided by funcall alone. ; But this doesn't help in ANSI GCL or CMU CL 19e on Linux, and even has broken ; onCMU CL 18d on Solaris, so we just punt on this trick for those Lisps. (if (not (symbolp fn)) (error "~s requires a symbol, not ~s" 'qfuncall fn) #+(and cltl2 (not cmu) (not gcl)) `(let () (declare (ftype function ,fn)) (,fn ,@args)) #-(and cltl2 (not cmu) (not gcl)) `(funcall ',fn ,@args))) (defmacro defun-one-output (&rest args) ; Use this for raw Lisp functions that are known to return a single value in ; raw Lisp, since make-defun-declare-form uses that assumption to make an ; appropriate declaration. (cons 'defun args)) ; The following alist associates package names with Common Lisp packages, and ; is used in function find-package-fast, which is used by princ$ in place of ; find-package in order to save perhaps 15% of the print time. (defparameter *package-alist* nil) (defun-one-output find-package-fast (string) (or (cdr (assoc string *package-alist* :test 'equal)) (let ((pkg (find-package string))) (push (cons string pkg) *package-alist*) pkg))) (defvar *global-symbol-key* (make-symbol "*GLOBAL-SYMBOL-KEY*")) (defun global-symbol (x) (or (get x *global-symbol-key*) (setf (get x *global-symbol-key*) (intern (symbol-name x) (find-package-fast (concatenate 'string *global-package-prefix* (symbol-package-name x))))))) (defmacro live-state-p (x) (list 'eq x '*the-live-state*)) #-acl2-loop-only (defun get-global (x state-state) ; Keep this in sync with the #+acl2-loop-only definition of get-global (which ; doesn't use qfuncall). (cond ((live-state-p state-state) (return-from get-global (symbol-value (the symbol (global-symbol x)))))) (cdr (assoc x (qfuncall global-table state-state)))) (defmacro f-get-global (x st) (cond ((and (consp x) (eq 'quote (car x)) (symbolp (cadr x)) (null (cddr x))) ; The cmulisp compiler complains about unreachable code every (perhaps) time ; that f-get-global is called in which st is *the-live-state*. The following ; optimization is included primarily in order to eliminate those warnings; ; the extra efficiency is pretty minor, though a nice side effect. (if (eq st '*the-live-state*) `(let () (declare (special ,(global-symbol (cadr x)))) ,(global-symbol (cadr x))) (let ((s (gensym))) `(let ((,s ,st)) (declare (special ,(global-symbol (cadr x)))) (cond ((live-state-p ,s) ,(global-symbol (cadr x))) (t (get-global ,x ,s))))))) (t `(get-global ,x ,st)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUPPORT FOR NON-STANDARD ANALYSIS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Amazingly, some lisps do not have a definition for realp. In those ; implementations (apparently including at least one early version of GCL), we ; will use rationalp as a poor substitute which however suffices for ACL2 ; objects. #+:non-standard-analysis (defun acl2-realp (x) (rationalp x)) #+(and :non-standard-analysis CLTL2) (if (not (fboundp 'common-lisp::realp)) (setf (symbol-function 'common-lisp::realp) (symbol-function 'acl2-realp))) #+(and :non-standard-analysis (not CLTL2)) (if (not (fboundp 'lisp::realp)) (setf (symbol-function 'lisp::realp) (symbol-function 'acl2-realp))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; GCL VERSION QUERIES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #+gcl (defun gcl-version-> (major minor extra &optional weak) ; When true, this guarantees that the current GCL version is greater than ; major.minor.extra (or if weak is non-nil, than greater than or equal to). ; The converse holds for versions of GCL past perhaps 2.0. (and (boundp 'si::*gcl-major-version*) (integerp si::*gcl-major-version*) (if (= si::*gcl-major-version* major) (and (boundp 'si::*gcl-minor-version*) (integerp si::*gcl-minor-version*) (if (= si::*gcl-minor-version* minor) (and (boundp 'si::*gcl-extra-version*) (integerp si::*gcl-extra-version*) (if weak (>= si::*gcl-extra-version* extra) (> si::*gcl-extra-version* extra))) (if weak (>= si::*gcl-minor-version* minor) (> si::*gcl-minor-version* minor)))) (if weak (>= si::*gcl-major-version* major) (> si::*gcl-major-version* major))))) #+gcl (defun gcl-version->= (major minor extra) (gcl-version-> major minor extra t)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; COMPILED LISP FIXES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Fix for GCL compiler. ; Small example: ; (defmacro my-mac (b) ; (list 'list (if (and (consp b) (stringp (car b))) (list 'quote b) b))) ; (defun foo () (my-mac ("Guards"))) ; (compile 'foo) #+(and gcl (not cltl2)) (when (and (fboundp 'compiler::wrap-literals) (not (gcl-version-> 2 6 7))) (setf (symbol-function 'compiler::wrap-literals) (symbol-function 'identity))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PROCLAIMING ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The variable acl2::*do-proclaims* determines whether or not we proclaim ACL2 ; functions before compiling them. The intent of proclaiming is to improve ; performance, though we once observed proclaiming to have had the opposite ; effect for some combination of ACL2 and Allegro CL; see a comment in ; proclaim-file. ; In compile-acl2 we load each source file, then proclaim it, and finally ; compile it, provided acl2::*suppress-compile-build-time* is false. in ; compile-acl2 we load each source file, then proclaim it, and finally ; Otherwise we skip all that: we can't proclaim before loading because macros ; may not all have been defined, and it seems dicey to do the sequence ; load,proclaim,load because the second load redefines functions. So in this ; case, we could do the build as follow (though this isn't the default; see ; variable ACL2_PROCLAIMS_ACTION in GNUmakefile): first call ; generate-acl2-proclaims to do the entire load/initialization sequence, then ; write out file acl2-proclaims.lisp, then compile again using that file. At ; the makefile level (see GNUmakefile), this sequence is accomplished with ; target "large" as follows: target "full" does a compile (basically a no-op if ; *suppress-compile-build-time* is true), then target "init" first invokes ; target "make-acl2-proclaims" to do the load/initialization and writing out of ; file acl2-proclaims.lisp, and finally "init" does a new load/initialization ; but this time loading the existing acl2-proclaims.lisp to proclaim functions. ; We considered using the above two-step process for GCL, expecting that better ; proclaim forms would be generated after doing initialization of the ; boot-strap world, since our code that generates proclaims tries to take ; advantage of stobjs-out properties. However, we found (August 8, 2013) that ; the generated proclaim forms were the same whether we did that, or we merely ; generated them during the load/proclaim/compile of each source file. So, we ; do the latter, which speeds up the build (see :DOC note-6-3). ; At one time we proclaimed for CCL, but one profiling run of a ; compute-intensive include-book form showed that this was costing us some 10% ; of the time. After checking with Gary Byers we decided that there was little ; if any benefit in CCL for proclaiming functions, so we no longer do it. ; Perhaps we should reconsider some time; in fact we have done so on 8/9/2013 ; (see comments below in *do-proclaims*). ; We considered adding &OPTIONAL to the end of every VALUES form (see comments ; below), based on output (since forgotten) from SBCL. But GCL issued several ; dozen warnings during the build when this happened, so for now, since we are ; only proclaiming functions for GCL, we omit the &optional. (defvar *do-proclaims* ; We may want to experiment for proclaiming with other Lisps besides GCL. But ; this might not be a good idea, in particular for Allegro CL and CCL (see ; above). ; In fact we experimented with CCL and ACL2(h) on 8/9/2013, by temporarily ; setting this variable to T. We got these results from "time" for make -j 8 ; with target regression-fresh on dunnottar.cs.utexas.edu. The results are ; inconclusive, so we keep things simple (and avoid stepping on the possibility ; of future CCL improvements) by the lskipping of function proclaiming in CCL. ; Built as follows (not showing here the setting PREFIX=): ; make ACL2_HONS=h LISP=ccl-trunk ACL2_SIZE=3000000 ; 27815.314u 1395.775s 1:09:03.35 705.0% 0+0k 2008+1736952io 34pf+0w ; Built as follows (not showing here the setting PREFIX=): ; make ACL2_HONS=h LISP=ccl-trunk ACL2_SIZE=3000000 \ ; ACL2_PROCLAIMS_ACTION=generate_and_reuse ; 27272.420u 1401.555s 1:09:11.18 690.7% 0+0k 333088+1750384io 303pf+0w #+gcl t #-gcl nil) (defun macroexpand-till (form sym) ; In order to find the THEs that we want to find to do automatic proclaiming of ; the output types of functions, we need to do macroexpansion at proclaim-time. ; It is possible that a given implementation of Common Lisp will macroexpand ; THE forms further. Hence we gently do the macroexpansion we need, one ; expansion at a time, looking for the THE we want to find. (loop (cond ((and (consp form) (eq (car form) sym)) (return form)) (t (multiple-value-bind (new-form flg) (macroexpand-1 form) (cond ((null flg) (return form)) (t (setq form new-form)))))))) (defun get-type-from-dcls (var dcls) (cond ((endp dcls) t) ((and (consp (car dcls)) (eq 'type (caar dcls)) (member var (cddr (car dcls)))) (cadr (car dcls))) (t (get-type-from-dcls var (cdr dcls))))) (defun arg-declarations (formals dcls) (cond ((endp formals) nil) (t (cons (get-type-from-dcls (car formals) dcls) (arg-declarations (cdr formals) dcls))))) (defun collect-types (l) (cond ((null (cdr l)) nil) ((stringp (car l)) (collect-types (cdr l))) ((consp (car l)) (let ((exp (car l))) (cond ((and (consp exp) (eq (car exp) 'declare)) (append (cdr exp) (collect-types (cdr l)))) (t nil)))) (t nil))) (defun convert-type-to-integer-pair (typ) ; Typ is (integer i j), (signed-byte i), or (unsigned-byte i). We return an ; equivalent type (integer i' j'). (case (car typ) (integer (cdr typ)) (signed-byte (let ((n (expt 2 (1- (cadr typ))))) (list (- n) (1- n)))) (unsigned-byte (list 0 (1- (expt 2 (cadr typ))))) (t (error "Unexpected type for convert-to-integer-type ~s" typ)))) (defvar *acl2-output-type-abort* nil) (defun min-integer-* (x y) (cond ((and (integerp x) (integerp y)) (min x y)) (t '*))) (defun max-integer-* (x y) (cond ((and (integerp x) (integerp y)) (max x y)) (t '*))) (defun max-output-type-for-declare-form (type1 type2) ; We return a supertype of type1 and type2, preferably as small as possible, ; else nil. We assume that each typei that is not null is (values ...) or is ; some sort of integer type. (cond ((equal type1 type2) type1) ((or (eq type1 '*) (eq type2 '*)) '*) #+acl2-mv-as-values ((not (equal (and (consp type1) (eq (car type1) 'values)) (and (consp type2) (eq (car type2) 'values)))) '*) ((and (or (eq type1 'integer) (and (consp type1) (eq (car type1) 'integer) (or (null (cddr type1)) (member '* (cdr type1) :test 'eq)))) (or (eq type2 'integer) (and (consp type2) (eq (car type2) 'integer) (or (null (cddr type2)) (member '* (cdr type2) :test 'eq))))) 'integer) ((or (atom type1) (atom type2)) ; so, type is t since neither is * t) ((cdr (last type1)) ; (not (true-listp type1)) (error "Non-atom, non-true-listp type for max-output-type-for-declare-form: ~s" type1)) ((cdr (last type2)) ; (not (true-listp type2)) (error "Non-atom, non-true-listp type for max-output-type-for-declare-form: ~s" type2)) (t (let ((sym1 (car type1)) (sym2 (car type2))) (cond ((eq sym1 sym2) (case sym1 ((signed-byte unsigned-byte) (if (< (cadr type1) (cadr type2)) type2 type1)) (integer (list 'integer (min-integer-* (cadr type1) (cadr type2)) (max-integer-* (caddr type1) (caddr type2)))) #+acl2-mv-as-values (values (cons 'values (max-output-type-for-declare-form-lst (cdr type1) (cdr type2)))) (otherwise (error "Unexpected type for max-output-type-for-declare-form: ~s" type1)))) #+acl2-mv-as-values ((or (eq sym1 'values) (eq sym2 'values)) ; but not both '*) (t (let* ((pair1 (convert-type-to-integer-pair type1)) (pair2 (convert-type-to-integer-pair type2)) (lower1 (car pair1)) (upper1 (cadr pair1)) (lower2 (car pair2)) (upper2 (cadr pair2)) (lower-min (min-integer-* lower1 lower2)) (upper-max (max-integer-* upper1 upper2))) (cond ((and (eql lower1 lower-min) (eql upper1 upper-max)) type1) ((and (eql lower2 lower-min) (eql upper2 upper-max)) type2) (t (list 'integer lower-min upper-max)))))))))) #+acl2-mv-as-values (defun max-output-type-for-declare-form-lst (type-list1 type-list2) ; Type-list1 and type-list2 are known to be true lists (null-terminated ; lists). (cond ((or (null type-list1) (null type-list2)) (cond ((and (null type-list1) (null type-list2)) nil) ((and *acl2-output-type-abort* (or (atom type-list1) (atom type-list2))) (cons '* (max-output-type-for-declare-form-lst (cdr type-list1) (cdr type-list2)))) (t (error "Implementation error:~%~ max-output-type-for-declare-form-lst called on lists of~%~ different length:~%~ ~s~% ~s~%~ Please contact the ACL2 implementors." type-list1 type-list2)))) (t (cons (max-output-type-for-declare-form (car type-list1) (car type-list2)) (max-output-type-for-declare-form-lst (cdr type-list1) (cdr type-list2)))))) (declaim (ftype (function (t t) (values t) ; see comment above about &optional ) output-type-for-declare-form-rec)) (declaim (ftype (function (t t) (values t) ; see comment above about &optional ) output-type-for-declare-form-rec-list)) (defun output-type-for-declare-form-rec (form flet-alist) ; We return either nil or *, or else a type for form that ideally is as small ; as possible. (cond ((integerp form) `(integer ,form ,form)) ((characterp form) 'character) ((atom form) t) ((member (car form) '(return-last return-from when) :test 'eq) (output-type-for-declare-form-rec (car (last form)) flet-alist)) ((assoc (car form) flet-alist :test 'eq) (cdr (assoc (car form) flet-alist :test 'eq))) #+acl2-mv-as-values ((member (car form) '(mv values) :test 'eq) (cond ((null (cdr form)) (setq *acl2-output-type-abort* '*)) ((null (cddr form)) ; form is (values &) (let* ((*acl2-output-type-abort* nil) (tmp (output-type-for-declare-form-rec (cadr form) flet-alist))) (cond ((and (symbolp tmp) (not (eq tmp '*)) (not *acl2-output-type-abort*)) tmp) (t t)))) (t (cons 'values (loop for x in (cdr form) collect (let* ((*acl2-output-type-abort* nil) (tmp (output-type-for-declare-form-rec x flet-alist))) (cond ((and (symbolp tmp) (not (eq tmp '*)) (not *acl2-output-type-abort*)) tmp) (t t)))))))) #-acl2-mv-as-values ((eq (car form) 'mv) (output-type-for-declare-form-rec (cadr form) flet-alist)) #-acl2-mv-as-values ((eq (car form) 'values) (setq *acl2-output-type-abort* '*)) ((member (car form) '(flet labels) :test 'eq) ; (flet bindings val) (let ((temp flet-alist)) (dolist (binding (cadr form)) (let ((fn (car binding)) (body (car (last binding))) *acl2-output-type-abort*) (let ((typ (output-type-for-declare-form-rec body flet-alist))) (push (cons fn (if *acl2-output-type-abort* '* typ)) temp)))) (output-type-for-declare-form-rec (car (last form)) temp))) ((eq (car form) 'the) (let ((typ (cadr form))) (cond ((member typ '(integer fixnum character) :test 'eq) typ) ((and (consp typ) (member (car typ) '(integer signed-byte unsigned-byte #+acl2-mv-as-values values) :test 'eq)) typ) (t t)))) ((eq (car form) 'if) (cond ((eq (cadr form) t) ; as generated for final cond branch in CCL (output-type-for-declare-form-rec (caddr form) flet-alist)) ((eq (cadr form) nil) ; perhaps not necessary (output-type-for-declare-form-rec (cadddr form) flet-alist)) (t (let ((type1 (output-type-for-declare-form-rec (caddr form) flet-alist))) (if (eq type1 '*) ; optimization '* (max-output-type-for-declare-form type1 (output-type-for-declare-form-rec (cadddr form) flet-alist))))))) ((member (car form) '(let let*) :test 'eq) (cond ((cddr form) (output-type-for-declare-form-rec (car (last form)) flet-alist)) (t t))) #+acl2-mv-as-values ((eq (car form) 'multiple-value-bind) (cond ((cdddr form) (output-type-for-declare-form-rec (car (last form)) flet-alist)) (t t))) ((eq (car form) 'unwind-protect) (output-type-for-declare-form-rec (cadr form) flet-alist)) ((member (car form) '(time progn ec-call) :test 'eq) (output-type-for-declare-form-rec (car (last form)) flet-alist)) ((member (car form) '(tagbody ; e.g., ld-fn throw-raw-ev-fncall ; e.g., from defchoose eval error ) :test 'eq) (setq *acl2-output-type-abort* '*)) ((eq (car form) 'mv-list) t) (t (multiple-value-bind (new-form flg) (macroexpand-1 form) (cond ((null flg) (cond ((atom form) t) ((eq (car form) 'multiple-value-prog1) (and (consp (cdr form)) (output-type-for-declare-form-rec (cadr form) flet-alist))) ; Note: We don't expect multiple-value-setq to show up in ACL2. ((and (consp (car form)) (eq (caar form) 'lambda)) (output-type-for-declare-form-rec (caddr (car form)) flet-alist)) ((not (symbolp (car form))) ; should always be false '*) #-acl2-mv-as-values (t t) #+acl2-mv-as-values (t (let ((x (and ; check that (w *the-live-state*) is bound (boundp 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD) (fboundp 'get-stobjs-out-for-declare-form) (qfuncall get-stobjs-out-for-declare-form (car form))))) (cond ((consp (cdr x)) (cons 'values (make-list (length x) :initial-element t))) (x t) (t (setq *acl2-output-type-abort* '*))))))) (t (output-type-for-declare-form-rec new-form flet-alist))))))) (defun output-type-for-declare-form-rec-list (forms flet-alist) (cond ((atom forms) nil) (t (cons (let ((tp (output-type-for-declare-form-rec (car forms) flet-alist))) (if (member tp '(nil *) :test 'eq) t tp)) (output-type-for-declare-form-rec-list (cdr forms) flet-alist))))) (defun output-type-for-declare-form (fn form) ; We return a list of output types, one per value. So if #-acl2-mv-as-values, ; then we always return a list of length one. (when (eq fn 'return-last) (return-from output-type-for-declare-form '*)) #-acl2-mv-as-values (let* ((*acl2-output-type-abort* nil) ; protect for call on next line (result (output-type-for-declare-form-rec form nil))) (cond (*acl2-output-type-abort* '*) (t (list 'values result)))) #+acl2-mv-as-values (let* ((*acl2-output-type-abort* nil) ; protect for call on next line (result (output-type-for-declare-form-rec form nil)) (stobjs-out (and ; check that (w *the-live-state*) is bound (boundp 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD) (fboundp 'get-stobjs-out-for-declare-form) (qfuncall get-stobjs-out-for-declare-form fn)))) (when (and stobjs-out (not (eq (and (consp result) (eq (car result) 'values)) (consp (cdr stobjs-out)))) (not *acl2-output-type-abort*)) (error "Implementation error in ~s:~%~ stobjs-out and result don't mesh.~%~ Stobjs-out = ~s~%~ Result = ~s~%~ Please contact the ACL2 implementors." (list 'output-type-for-declare-form fn '|defun...|) stobjs-out result)) (cond ((and (consp result) (eq (car result) 'values)) result ; see comment above about &optional ) ((or *acl2-output-type-abort* (eq result '*)) '*) (t (list 'values result) ; see comment above about &optional )))) (defun make-defun-declare-form (fn form &optional (output-type nil output-type-p)) ; See the comment in proclaim-file for why we don't proclaim in more lisps. (when *do-proclaims* (let* ((output-type (if output-type-p output-type (output-type-for-declare-form fn (car (last form)))))) (let ((formals (caddr form))) (cond ((null (intersection formals lambda-list-keywords :test 'eq)) `(declaim (ftype (function ,(arg-declarations formals (collect-types (cdddr form))) ,output-type) ; WARNING: Do not replace (cadr form) by fn below. These can differ! Fn is ; passed to output-type-for-declare-form in order to get its 'stobjs-out, but ; (cadr form) can be the *1* function for fn. The mistaken placement of fn ; below caused a factor of 4 slowdown in GCL in the first lemma5 in community ; book books/unicode/utf8-decode.lisp, because the proclaim for function ; utf8-combine4-guard was overwritten by a subsequent weaker proclaimed type ; that was supposed to be generated for the *1* function, but instead was ; generated for utf8-combine4-guard. ,(cadr form)))) (t nil)))))) (defun make-defconst-declare-form (form) ; We assume that the form has already been evaluated. (when *do-proclaims* (let* ((output (macroexpand-till (caddr form) 'the)) (output-type (cond ((and (consp output) (eq 'the (car output))) (cadr output)) (t nil)))) (cond (output-type `(declaim (type ,output-type ,(cadr form)))) (t (let ((val (symbol-value (cadr form)))) (if (integerp val) `(declaim (type (integer ,val ,val) ,(cadr form))) nil))))))) (defun make-defstobj-declare-form (form) (when *do-proclaims* (let* ((name (cadr form)) (args (cddr form)) ; The loss of efficiency caused by using symbol-value below should be more than ; compensated for by the lack of a warning here when building the system. (template (qfuncall defstobj-template name args nil)) (raw-defs (qfuncall defstobj-raw-defs name template ; We do not want to rely on having the world available here, so we pass in nil ; for the final argument of defstobj-raw-defs. The only effect of using nil ; instead of a world in such a context is that additional checking by ; translate-declaration-to-guard is missing. We also pass in nil for ; congruent-to, since we don't think it makes any difference in the resulting ; declare form. nil nil))) (cons 'progn (mapcar (function (lambda (def) (if (member (symbol-value '*stobj-inline-declare*) def :test (function equal)) nil (make-defun-declare-form (car def) (cons 'defun def))))) raw-defs))))) (defmacro eval-or-print (form stream) `(let ((form ,form) (stream ,stream)) (when form (if stream (format stream "~s~%" form) (eval form))))) (defun proclaim-form (form &optional stream) ; We assume that this function is called under proclaim-file, which binds ; *package*. See the comment below for the in-package case. (when *do-proclaims* (cond ((consp form) (case (car form) ((in-package) (eval-or-print form stream) (when stream ; We make sure that when we're merely printing, nevertheless we are in the ; correct package as we read the rest of the file. (eval form)) nil) ((defmacro defvar defparameter) nil) ((defconst) (eval-or-print (make-defconst-declare-form form) stream) nil) ((defstobj) (eval-or-print (make-defstobj-declare-form form) stream)) ((eval-when) (dolist (x (cddr form)) (proclaim-form x stream)) nil) ((progn mutual-recursion) (dolist (x (cdr form)) (proclaim-form x stream)) nil) ((defun defund) (eval-or-print (make-defun-declare-form (cadr form) form) stream) nil) (defun-one-output (eval-or-print (make-defun-declare-form (cadr form) form '(values t) ; see comment above about &optional ) stream) nil) (otherwise nil))) (t nil)))) (defun proclaim-file (name &optional stream) ; Proclaims the functions in the file name that are either at the top-level, or ; within a progn, mutual-recursion, or eval-when. IMPORTANT: This function ; assumes that the defconst forms in the given file have already been ; evaluated. One way to achieve this state of affairs, of course, is to load ; the file first. ; Just before releasing Version_2.5 we decided to consider proclaiming for ; Lisps other than GCL. However, our tests in Allegro suggested that this may ; not help. The comment below gives some details. Perhaps we will proclaim ; for MCL in the future. At any rate, CCL (OpenMCL) is supported starting with ; Version_2.8. We tried proclaiming for that Lisp, but no longer do so; see ; Section "PROCLAIMING" above. ; Here is a summary of three comparable user times from certifying all the ACL2 ; books in June 2000, just before Release 2.5 is complete. The first column, ; labeled "Comp", is the one to be looked at for comparison purposes. These are ; all done on the same Sun workstation, using Allegro 5.0.1. The meanings of ; these numbers are explained below. ; ; Comp Actual Comments ; Recent ACL2 without proclaim: 5:41 5:36:42 no meta ; Recent ACL2 *with* proclaim: 5:54 5:53:58 ; April ACL2 (before non-std.): 5:48 5:35:58 missing some pipeline and ~40 ; sec. user time on powerlists ; ; The "Comp" column estimates how long the run would have taken if all books had ; certified, except that no run gets past book batcher-sort in the powerlists/ ; directory. (The April run bogs down even slightly earlier.) The first row is ; adjusted by about 4 minutes because the run started with book meta-plus-lessp. ; The April run broke on book basic-def from case-studies/pipeline and hence ; missed the rest of that directory's books. The above points account for the ; addition of time from "Actual" to the appropriate comparison time in the first ; column. (when *do-proclaims* (format t "Note: Proclaiming file ~s.~%" name) (with-open-file (file name :direction :input) (let ((eof (cons nil nil)) (*package* *package*)) (loop (let ((form (read file nil eof))) (cond ((eq eof form) (return nil)) (t (proclaim-form form stream))))) nil)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ACL2's Implementation of the Backquote Readmacro ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defparameter *comma* (make-symbol "COMMA") "*comma* is used by the backquote reader. When the reader encounters <,foo>, it returns (list *comma* read:>).") (defparameter *comma-atsign* (make-symbol "COMMA-ATSIGN") "*comma-atsign* is used by the backquote reader. When the reader encounters <,@foo>, it returns (list *comma-atsign* read:).") (defparameter *backquote-counter* 0 "READ cannot handle a comma or comma-atsign unless there is a pending backquote that will eliminate the *comma* or *comma-atsign*. In the SPECIAL variable *backquote-counter* we keep track of the number of backquotes that are currently pending. It is crucial that this variable be SPECIAL.") (defun backquote (x) "The two functions BACKQUOTE and BACKQUOTE-LST implement backquote as described on pp. 349-350 of CLTL1 except that (a) use of vector notation causes an error and (b) the use of ,. is not permitted." ; It must be emphasized that the ACL2 implementation of backquote is ; only one of many implementations that are consistent with the Common ; Lisp specification of backquote. That spec requires only that ; backquote produce a form that when evaluated will produce a ; designated object. We impose the requirement that *acl2-readtable* ; be used both when checking files with ACL2 and when later compiling ; or using those files. This requirement guarantees that we obtain ; the same behavior of backquote across all Common Lisps. For ; example, it is an ACL2 theorem, across all Common Lisps that ; (equal (car '`(,a)) 'cons) ; This theorem is definitely not true about the implementation of ; backquote provided by the implementors of each Common Lisp. In ; fact, the lefthand side of this theorem represents an informal ; misuse of the backquote notation because one is intended to consider ; the effects of evaluating backquoted forms, not the forms ; themselves. (In some Common Lisps, the lefthand side might even ; evaluate to a symbol in a nonstandard package.) Nevertheless, ; because we inflict our definition of backquote on the ACL2 user at ; all times, the above equation is a theorem throughout, so no harm is ; done. On the other hand, if we used the local implementation of ; backquote of each particular Common Lisp, we would get different ; ACL2 theorems in different Common Lisps, which would be bad. ; Unlike most implementors of backquote, we do no serious ; optimization. We feel this inattention to efficiency is justified ; at the moment because in the usage we expect, the only serious costs ; will be small ones, during compilation. (cond ((and (vectorp x) (not (stringp x))) (error "ACL2 does not handle vectors in backquote.")) ((atom x) (list 'quote x)) ((eq (car x) *comma*) (cadr x)) ((eq (car x) *comma-atsign*) (error "`,@ is an error")) (t (backquote-lst x)))) (defun backquote-lst (l) (cond ((atom l) (list 'quote l)) ((eq (car l) *comma*) (cadr l)) ((eq (car l) *comma-atsign*) (error ". ,@ is illegal.")) ((and (consp (car l)) (eq (caar l) *comma*)) (list 'cons (cadr (car l)) (backquote-lst (cdr l)))) ((and (consp (car l)) (eq (caar l) *comma-atsign*)) (cond ((null (cdr l)) (cadr (car l))) (t (list 'append (cadr (car l)) (backquote-lst (cdr l)))))) (t (list 'cons (backquote (car l)) (backquote-lst (cdr l)))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUPPORT FOR ACL2 CHARACTER READER ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun rev1@ (x acc) ; A strange version of linear reverse, which treats the final cdr of x ; as the final element of x. (cond ((atom x) (cons x acc)) (t (rev1@ (cdr x) (cons (car x) acc))))) (defun acl2-read-character-string (s acc) ; The reason we're so picky about what we allow as readable character notation ; is the existence of certain restrictions imposed by dpANS. From the ; documentation for Sharpsign Backslash in dpANS: ; When the token x is more than one character long, the x must have ; the syntax of a symbol with no embedded package markers. In this ; case, the sharpsign backslash notation parses as the character ; whose name is (string-upcase x); see *See Character Names::. ; And from the documentation for char-name in dpANS: ; Returns a string that is the name of the character, or nil if the ; character has no name. ; However, in akcl for example, (char-name #\\346) evaluates to NIL. Even if ; it didn't, surely different lisps will define char-name differently. So, ; we can't allow notation such as #\\346. (let ((ch (read-char s))) (cond ((member ch *acl2-read-character-terminators*) (unread-char ch s) (cond ((characterp acc) acc) (t (let ((x (coerce (rev1@ acc nil) 'string))) (cond ((string-equal x "SPACE") #\Space) ((string-equal x "TAB") #\Tab) ((string-equal x "NEWLINE") #\Newline) ((string-equal x "PAGE") #\Page) ((string-equal x "RUBOUT") #\Rubout) #+clisp ; Currently we only allow #\Null in CLISP. We have to allow it there in some ; fashion because it is written to compiled (.fas) files. The current approach ; seems to avoid any soundness issue: presumably #\Null is the same in every ; CLISP, and if one tries then to use a book containing #\Null that was ; certified using CLISP, then one will simply get an error. ((string-equal x "NULL") #\Null) #+(and cmu18 solaris) ; We have seen code with #\Newline generate #\Linefeed in CMU CL 18d on ; Sun/Solaris, so here we allow #\Linefeed in order to avoid an error during ; the ACL2 build. This would seem a harmless fix, for the sort of reason ; described above in the case of NULL. ((and (string-equal x "LINEFEED") (eql #\Newline #\Linefeed)) #\Newline) (t (funcall (if (fboundp 'interface-er) 'interface-er 'error) "When the ACL2 reader tries to read #\\x, then ~ x must either be a single character followed ~ by a character in the list ~x0, ~ or else x must be one of Space, Tab, Newline, ~ Page, or Rubout (where case is ignored). ~ However, ~s1 is none of these." *acl2-read-character-terminators* x))))))) (t (acl2-read-character-string s (cons ch acc)))))) (defun acl2-character-reader (s c n) (declare (ignore n c)) (let ((ch (read-char s))) (acl2-read-character-string s ch))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUPPORT FOR #, ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar *inside-sharp-dot-read* nil) (defvar *inhibit-sharp-comma-warning* nil) (defvar *inside-sharp-u-read* nil) (defun sharp-comma-read (stream char n) (or *inhibit-sharp-comma-warning* (format t "WARNING: The sharp-comma read macro (#,) has been replaced~%~ by sharp-dot (#.). To inhibit this warning, submit the~%~ following form to raw Lisp:~%~s~%~ Note that sharp-comma may be eliminated in future versions~%~ of ACL2 unless perhaps there are strong objections.~%" '(setq *inhibit-sharp-comma-warning* t))) (sharp-dot-read stream char n)) (defun sharp-dot-read (stream char n) (declare (ignore char n)) (let ((whitespace-chars '(#\Backspace #\Tab #\Newline #\Linefeed #\Page #\Return #\Space))) (when (member (peek-char nil stream nil nil t) whitespace-chars) (error "#. must be followed immediately by a non-whitespace character.~%~ See :DOC sharp-dot-reader."))) (let* ((*inside-sharp-dot-read* (or (not *inside-sharp-dot-read*) (error "Recursive attempt to read a sharp-dot (#.)~%expression ~ while inside a sharp-dot expression. This is not~%~ allowed in ACL2."))) (sym (read stream t nil t)) (val (and (symbolp sym) (qfuncall fgetprop sym 'const nil (qfuncall w *the-live-state*))))) (cond (val (cond ((and (consp val) (eq (car val) 'quote) (consp (cdr val)) (null (cddr val))) (cadr val)) (t (error "(Implementation error) Found non-quotep 'const ~%~ property for ~s." sym)))) (sym (error "ACL2 supports #. syntax only for #.*a*, where *a* has been ~%~ defined by ~s. Thus the form #.~s is illegal." 'defconst sym)) (t ; surprising case (error "ACL2 supports #. syntax only for #.*a*, where *a* has been ~%~ defined by ~s." 'defconst))))) (defun sharp-bang-read (stream char n) ; Thanks to Pascal J. Bourguignon for suggesting this approach. (declare (ignore char n)) (let* ((package-name (read stream t nil t)) (package-string (cond ((symbolp package-name) (symbol-name package-name)) ((stringp package-name) package-name) (t nil))) (*package* (cond (*read-suppress* *package*) ((assoc package-string (qfuncall known-package-alist *the-live-state*) :test 'equal) (qfuncall find-package-fast package-string)) (t (error "There is no package named ~S that is known to ~ ACL2 in this context." package-name))))) (read stream t nil t))) (defun sharp-u-read (stream char n) (declare (ignore char n)) (let* ((*inside-sharp-u-read* (or (not *inside-sharp-u-read*) (error "Recursive attempt to read a sharp-u (#u)~%expression ~ while inside a sharp-u expression. This is not~%~ allowed."))) (x (read stream t nil t))) (cond ((numberp x) x) ((not (symbolp x)) (error "Failure to read #u expression:~%~ #u was not followed by a symbol.")) (t (let* ((name (symbol-name x)) (c (and (not (equal name "")) (char name 0)))) (cond ((member c '(#\B #\O #\X)) (values ; seems necessary in GCL to return a single value (read-from-string (concatenate 'string "#" (remove #\_ name))))) (t (let ((n (read-from-string (remove #\_ name)))) (cond ((numberp n) n) (*read-suppress* nil) (t (error "Failure to read #u expression:~%~ Result ~s is not a numeral." n))))))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUPPORT FOR #@ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro sharp-atsign-read-er (str &rest format-args) `(progn (loop (when (null (read-char-no-hang stream nil nil t)) (return))) (error (concatenate 'string ,str ". See :DOC set-iprint.") ,@format-args))) (defun sharp-atsign-read (stream char n) (declare (ignore char n)) (let (ch bad-ch (zero-code (char-code #\0)) (index 0)) (loop (when (eql (setq ch (read-char stream t nil t)) #\#) (return)) (let ((digit (- (char-code ch) zero-code))) (cond ((or (< digit 0) (> digit 9)) (when (not bad-ch) (setq bad-ch ch)) (return)) (t (setq index (+ digit (* 10 index))))))) (cond (bad-ch (sharp-atsign-read-er "Non-digit character ~s following #@~s" bad-ch index)) ((eval '(f-get-global 'certify-book-info *the-live-state*)) (sharp-atsign-read-er "Illegal reader macro during certify-book, #@~s#" index)) ((qfuncall iprint-ar-illegal-index index *the-live-state*) (sharp-atsign-read-er "Out-of-bounds index in #@~s#" index)) (t (qfuncall iprint-ar-aref1 index *the-live-state*))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUPPORT FOR FAST #n= and #n# ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The handling of #n= in CCL 1.4, and we suspect some other Lisp ; implementations as well, can be quite slow because of the need to handle ; circular objects and to check for illegal use of #n=(... #n=... ...). But ; ACL2 controls how it writes out certain files, notably expansion files (see ; the Essay on Hash Table Support for Compilation). In those cases, we are ; willing to take responsibility that such illegal uses of #n= do not occur, ; and that #n# is used only after the "definition" of n with #n=. (It is easy ; to take such responsibility, since we print each form of the file in a legal ; manner. We cannot similarly take responsibility for user books, which can be ; hand-edited.) (defvar *sharp-reader-array-size* ; While this is initially 65536, it may increase, each time by a factor of ; *sharp-reader-array-size-multiplier*. It never decreases. (expt 2 16)) (defvar *sharp-reader-array* (make-array *sharp-reader-array-size*)) (defvar *sharp-reader-array-size-multiplier* ; Resize *sharp-reader-array* by this factor each time its size limit is hit, ; but never to a size past most-positive-fixnum. 2) (defconstant *sharp-reader-max-array-size* ; We keep this a fixnum in all reasonable Lisps, to guarantee in particular ; that the expression (1+ *sharp-reader-max-index*) in with-reckless-read will ; always evaluate to a fixnum. (1- (expt 2 29))) (defvar *sharp-reader-max-index* ; We hold the maximum index assigned to *sharp-reader-array* in this variable. ; (It is also OK if this variable exceeds that index; initially, its value is 0 ; even though no index has been assigned.) We use this value to wipe clean the ; *sharp-reader-array* after its use (see with-reckless-read), so that it ; doesn't defeat potential garbage collection of its elements. We were tempted ; to use a one-element array so that Lisps like GCL can avoid boxing, but a ; little experimentation seems to suggest that GC for even 1,000,000 fixnums is ; very fast compared to what we might expect from reading that many objects. 0) (defun update-sharp-reader-max-index (index) (when (< *sharp-reader-max-index* index) (when (>= index *sharp-reader-array-size*) ; Grow the array. (when (>= index *sharp-reader-max-array-size*) (error "Lisp reader encountered #=~s (maximum index is ~s)." index (1- *sharp-reader-max-array-size*))) (let* ((new-sharp-reader-array-size (max (1+ index) (min (* *sharp-reader-array-size-multiplier* *sharp-reader-array-size*) *sharp-reader-max-array-size*))) (new-sharp-reader-array (make-array new-sharp-reader-array-size)) (bound (the (unsigned-byte 29) (1+ *sharp-reader-max-index*)))) (do ((i 0 (the (unsigned-byte 29) (1+ i)))) ((eql i bound)) (declare (type (unsigned-byte 29) i)) (setf (svref new-sharp-reader-array i) (svref *sharp-reader-array* i))) (setq *sharp-reader-array* new-sharp-reader-array *sharp-reader-array-size* new-sharp-reader-array-size))) ; End growing of the array. (setq *sharp-reader-max-index* index))) (defun reckless-sharp-sharp-read (stream char arg) (declare (ignore stream char)) (cond (*read-suppress* nil) (t (svref *sharp-reader-array* arg)))) (defun reckless-sharp-equal-read (stream char arg) (declare (ignore char)) (cond (*read-suppress* (values)) ; as for CCL, but unlikely to arise in ACL2 (t (let ((val (read stream t nil t))) (update-sharp-reader-max-index arg) (setf (svref *sharp-reader-array* arg) val))))) (defmacro with-reckless-read (&rest forms) ; To experiment with disabling the reckless reader, replace the body below ; simply with `(progn ,@forms). `(let ((*readtable* *reckless-acl2-readtable*)) (unwind-protect ,@forms (let ((bound (the (unsigned-byte 29) (1+ *sharp-reader-max-index*)))) (declare (type (unsigned-byte 29) bound)) (do ((i 0 (the (unsigned-byte 29) (1+ i)))) ((eql i bound)) (declare (type (unsigned-byte 29) i)) (setf (svref *sharp-reader-array* i) nil)) (setq *sharp-reader-max-index* 0))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; DUAL PACKAGES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The following function used to be defined in axioms.lisp (with ; #-acl2-loop-only), but we need it here. (defun symbol-package-name (x) ; Warning: This function assumes that x is not a bad-lisp-objectp. In ; particular, see the Invariant on Symbols in the Common Lisp Package, ; discussed in a comment in bad-lisp-objectp, which allows us to assume that if ; x resides in the "COMMON-LISP" package and does not have its ; *initial-lisp-symbol-mark* property set, then its symbol-package is the ; *main-lisp-package*. (cond ((get x *initial-lisp-symbol-mark*)) ((let ((p (symbol-package x))) (cond ((eq p *main-lisp-package*) ; We could just return *main-lisp-package-name-raw* in this case (but do not ; skip this case, since in non-ANSI GCL, (package-name *main-lisp-package*) is ; "LISP", not "COMMON-LISP" (which is what we need here). But we go ahead and ; set *initial-lisp-symbol-mark* in order to bypass this code next time. (setf (get x *initial-lisp-symbol-mark*) *main-lisp-package-name-raw*)) (t (and p (package-name p)))))) ; We use ERROR now because we cannot print symbols without packages ; with ACL2 functions. (t (error "The symbol ~a, which has no package, was encountered~%~ by ACL2. This is an inconsistent state of affairs, one that~%~ may have arisen by undoing a defpkg but holding onto a symbol~%~ in the package being flushed, contrary to warnings printed.~%~%" x)))) (defmacro gv (fn args val) (sublis `((funny-fn . ,fn) (funny-args . ,args)) `(let ((gc-on (not (gc-off *the-live-state*)))) (if (or gc-on (f-get-global 'safe-mode *the-live-state*)) (throw-raw-ev-fncall (list 'ev-fncall-guard-er 'funny-fn ,(cons 'list 'funny-args) (untranslate* (guard 'funny-fn nil (w *the-live-state*)) t (w *the-live-state*)) (stobjs-in 'funny-fn (w *the-live-state*)) (not gc-on))) ,val)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ENVIRONMENT SUPPORT ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The following is first used in acl2-init.lisp, so we define it here. (defun getenv$-raw (string) ; The following either returns the value of the given environment variable or ; returns nil (in lisps where we do not yet know how to get that value). ; WARNING: Keep this in sync with the #-acl2-loop-only definition of setenv$. #+cmu ; We might consider using unix:unix-getenv for newer CMUCL versions (and (boundp 'ext::*environment-list*) (cdr (assoc (intern string :keyword) ext::*environment-list* :test #'eq))) #+(or gcl allegro lispworks ccl sbcl clisp) (let ((fn #+gcl 'si::getenv #+allegro 'sys::getenv #+lispworks 'hcl::getenv #+ccl 'ccl::getenv #+sbcl 'sb-ext::posix-getenv #+clisp 'ext:getenv)) (and (fboundp fn) (funcall fn string)))) (defun get-os () ; The following are in priority order. #+unix (return-from get-os :unix) #+mswindows (return-from get-os :mswindows) #+apple (return-from get-os :apple) #-(or unix apple mswindows) nil) (defmacro our-ignore-errors (x) #+cltl2 `(ignore-errors ,x) #-cltl2 x) (defmacro safe-open (&rest args) `(our-ignore-errors (open ,@args))) (defun our-truename (filename &optional namestringp) ; For now, assume that namestringp is nil (or not supplied). ; Filename can be a pathname, in which case we treat it as its namestring. ; This function is intended to return nil if filename does not exist. We thus ; rely on the CL HyperSpec, where it says of truename that "An error of type ; file-error is signaled if an appropriate file cannot be located within the ; file system for the given filespec"; and we also rely on the Allegro CL ; documentation for function pathname-resolve-symbolic-links, which says: "When ; pathname names a non-existent pathname, an error is signaled...." ; We return (ignore-errors (truename x)) instead of (probe-file x) because we ; have seen CLISP 2.48 cause an error when probe-file is called on a directory ; name. Unfortunately, we can't do that with GCL 2.6.7, which doesn't have ; ignore-errors. Also unfortunately, Allegro CL 8.0 (also probably earlier ; versions, and maybe later versions) does not fully resolve symbolic links ; using truename, even with value T for keyword :follow-symlinks. ; Finally, consider namestringp. If nil, then as above we either return nil or ; the truename (a pathname object). Otherwise, we return the namestring of ; such a truename, with the following treatment if that truename is nil: return ; nil if namestringp is :safe, else cause an error, where if namestringp is a ; msgp then incorporate it into the error message. (when (pathnamep filename) (setq filename (namestring filename))) (let ((truename (cond #+allegro ((fboundp 'excl::pathname-resolve-symbolic-links) (ignore-errors (qfuncall excl::pathname-resolve-symbolic-links filename))) #+(and gcl (not cltl2)) ((fboundp 'si::stat) ; try to avoid some errors (and (or (qfuncall si::stat filename) ; But filename might be a directory, in which case the si::stat call above ; could return nil; so we try again. (and (or (equal filename "") (not (eql (char filename (1- (length filename))) #\/))) (qfuncall si::stat (concatenate 'string filename "/")))) (truename filename))) #+(and gcl (not cltl2)) (t (truename filename)) #-(and gcl (not cltl2)) (t ; Here we also catch the case of #+allegro if ; excl::pathname-resolve-symbolic-links is not defined. (ignore-errors (truename filename)))))) (cond ((null namestringp) truename) ((null truename) (cond ((eq namestringp :safe) nil) (t (qfuncall interface-er "Unable to obtain the truename of file ~x0.~@1" filename (if (qfuncall msgp namestringp) (qfuncall msg " ~@0" namestringp) ""))))) (t (namestring truename))))) (defun our-pwd () ; Warning: Do not be tempted to use (getenv$-raw "PWD"). The PWD environment ; variable is not necessarily maintained, for example in Solaris/SunOS as one ; make invokes another make in a different directory. (qfuncall pathname-os-to-unix (our-truename "" "Note: Calling OUR-TRUENAME from OUR-PWD.") (get-os) *the-live-state*)) (defun cancel-dot-dots (full-pathname) (let ((p (search "/.." full-pathname))) (cond (p (cancel-dot-dots (qfuncall merge-using-dot-dot (subseq full-pathname 0 p) (subseq full-pathname (1+ p) (length full-pathname))))) (t full-pathname)))) (defun unix-full-pathname (name &optional extension) ; We formerly used Common Lisp function merge-pathnames. But in CCL, ; merge-pathnames can insert an extra backslash (\), as follows: ; ? (MERGE-PATHNAMES "foo.xxx.lx86cl64" "/u/kaufmann/temp/") ; #P"/u/kaufmann/temp/foo\\.xxx.lx86cl64" ; ? ; Gary Byers has explained that while this behavior may not be ideal, it is ; legal for Common Lisp. So we avoid merge-pathnames here. (let* ((os (get-os)) (state *the-live-state*) (name (qfuncall pathname-os-to-unix (if extension (concatenate 'string name "." extension) name) os state))) (cancel-dot-dots (cond ((qfuncall absolute-pathname-string-p name nil os) name) (t (concatenate 'string (our-pwd) name)))))) (defun our-user-homedir-pathname () ; For ACL2 Version_4.2, Hanbing Liu reported the following error using Allegro ; CL Enterprise Edition 8.0 for Linux, apparently printed before printing the ; optimization settings. ; An unhandled error occurred during initialization: ; There is no file named /home/rcash/ ; The error was likely caused by our calling user-homedir-pathname without a ; host, for a user without a home directory. Quoting the Common Lisp Hyperspec ; on (user-homedir-pathname &optional host): ; If it is impossible to determine the user's home directory on host, then ; nil is returned. user-homedir-pathname never returns nil if host is not ; supplied. ; It's not clear that there is a meaningful notion of host on Linux. So we ; play it safe and define our own, error-free (for CLtL2) version of ; user-homedir-pathname. We have observed in the past that we needed our own ; version of user-homedir-pathname for GCL 2.7.0 anyhow (see below). #+gcl (cond ((gcl-version->= 2 7 0) ; It seems that GCL 2.7.0 has had problems with user-homedir-pathname in static ; versions because of how some system functions are relocated. So we define ; our own version for use by all GCLs 2.7.0 and beyond. (let ((home (si::getenv "HOME"))) (and home (pathname (concatenate 'string home "/"))))) (t (our-ignore-errors (pathname (user-homedir-pathname))))) #-gcl ; presumably CLtL2 (our-ignore-errors (user-homedir-pathname))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SUPPORT FOR SERIALIZE INTEGRATION INTO ACL2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; The following definitions might seem to belong serialize-raw.lisp, and ;; that's where they lived when we only built ACL2(h) with CCL. But Jared ;; Davis noticed that SBCL 1.0.46 didn't let him add undefined functions into ;; the readtable. Note also that it doesn't seem sufficient to give the ;; function symbols temporary definitions and redefine them later: the ;; readtable still uses the old functions. So to solve this, we move the ;; functions over from serialize-raw.lisp to here. (declaim (ftype (function (t t t) (values t)) ser-decode-from-stream)) (defun ser-cons-reader-macro (stream subchar arg) (declare (ignorable subchar arg)) ;; This is the reader macro for #z. When it is called the #z part has ;; already been read, so we just want to read the serialized object. (ser-decode-from-stream nil :never stream)) (defun ser-hons-reader-macro (stream subchar arg) (declare (ignorable subchar arg)) ;; This is the reader macro for #Z. When it is called the #Z part has ;; already been read, so we just want to read the serialized object. (ser-decode-from-stream nil :smart stream)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; MISCELLANY ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The following definition was originally in interface-raw.lisp. But it was ; also needed in hons-raw.lisp, which is compiled earlier, and CCL (at ; least) needs to know that we indeed have a macro here -- so the definition ; can't be put off until loading interface-raw.lisp. As of Version_3.2, ; hons-raw.lisp is only included in special builds, so we don't want to put ; this definition there; thus, we are putting it here. (defmacro special-form-or-op-p (name) ; At one time we thought that special-operator-p does not work in all Lisps. ; But if that was true, it no longer seems to be the case in October 2011. `(special-operator-p ,name)) (defvar *startup-package-name* "ACL2") (defmacro save-def (def-form) ; For each definition (save-def (defun name formals ... body)), where defun ; could be replaced by other macros that take the same arguments (like ; defun-one-output or defn1), this macro executes the definition and for the ; hons version, also saves (name formals ... body) as the 'acl2-saved-def ; property of name. We use this property to obtain the raw Lisp definition of ; name for memoize-fn, for Lisps like SBCL where function-lambda-expression ; doesn't seem to work for us. ; This macro is intended only for raw Lisp definitions. For definitions in the ; loop, we expect that cltl-def-from-name will give us the definition. #+hons (let* ((def (cdr def-form)) (name (car def))) `(progn ,def-form (setf (get ',name 'acl2-saved-def) ',def-form))) #-hons def-form) ; [Comment from Jared]: We probably should work toward getting rid of ; defg/defv's in favor of regular parameters... (defmacro defg (&rest r) "DEFG is a short name for DEFPARAMETER. However, in Clozure Common Lisp (CCL) its use includes two promises: (1) never to locally bind the variable, e.g., with LET or LAMBDA, and (2) never to call MAKUNBOUND on the variable. CCL uses fewer machine instructions to reference such a variable than an ordinary special, which may have a per-thread binding that needs to be locked up." #-Clozure `(defparameter ,@r) #+Clozure `(ccl::defstatic ,@r)) (defmacro defv (&rest r) "DEFV is a short name for DEFVAR. However, in Clozure Common Lisp (CCL) its use includes two promises: (1) never to locally bind the variable, e.g., with LET or LAMBDA, and (2) never to call MAKUNBOUND on the variable. CCL uses fewer machine instructions to reference such a variable than an ordinary special, which may have a per-thread binding that needs to be locked up. Unlike for DEFVAR, the second argument of DEFV is not optional." #-Clozure `(defparameter ,@r) #+Clozure `(ccl::defstaticvar ,@r)) acl2-sources/acl2-init.lisp0000664002132200015000000024743712222115527015246 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; This file cannot be compiled because it changes packages in the middle. ; Allow taking advantage of threads in SBCL, CCL, and Lispworks (where we may ; want to build a parallel version, which needs this to take place). At the ; time that we add (perhaps once again) support for HONS in other lisps besides ; CCL (August, 2012), there has been code developed that depends on mv-let ; being the same as multiple-value-bind; see for example community book ; books/centaur/aig/bddify.lisp, in particular the raw Lisp definition of ; count-branches-to, specifically the use of b* in the labels definition of ; lookup. So we also use multiple-value-bind for mv-let when building the HONS ; version. #+(or (and sbcl sb-thread) ccl lispworks hons) (push :acl2-mv-as-values *features*) ; Essay on Parallelism, Parallelism Warts, Parallelism Blemishes, Parallelism ; No-fixes, Parallelism Hazards, and #+ACL2-PAR notes. ; These sources incorporate code for an experimental extension for parallelism ; contributed by David Rager during his master's and Ph.D. dissertation work. ; That extension may be built by setting the :acl2-par feature, for example ; using make (see :DOC compiling-acl2p). The incorporation of code supporting ; parallelism has been carried out while taking great pains to preserve the ; functionality of the ACL2 system proper (i.e., without the experimental ; extension for parallelism). ; At the lowest level, parallel computation is carried out by Lisp threads, ; which provide Lisp-level abstractions of OS threads. A thread can be running ; or blocked (e.g., waiting for a semaphore signal). Threads can create other ; threads. We manage the creation and use of threads to reflect the resources ; we have available, for example the number of available cpu cores according to ; our own tracking. ; The implementation of the multi-threading primitives -- futures, spec-mv-let, ; plet, pargs, pand, and por -- has a dependency structure shown as follows. ; For example, multi-threading primitives are at the base of everything, ; futures and plet/pargs/pand/por are built on top of these primitives, and so ; forth, as indicated by the indentations. ; multi-threading primitives (semaphores, locks, condition variables, etc) ; futures ; spec-mv-let ; waterfall parallelism ; user-level program parallelism (no examples as of April 2012) ; plet, pargs, pand, por ; user-level program parallelism (e.g., community book ; books/parallel/fibonacci.lisp) ; This dependency structure roughly correlates to the following file structure. ; #+acl2-par ; multi-threading-raw.lisp (defines multi-threading primitives) ; parallel-raw.lisp (provides raw Lisp defs. of plet, pargs, pand, and por) ; futures-raw.lisp (defines futures and uses some helper functions ; from parallel-raw.lisp) ; parallel.lisp ; #-acl2-par ; parallel.lisp ; One might wonder how we use threads to execute pieces of parallelism work ; (where "pieces of parallelism work" can mean either futures, bindings of ; plet, arguments of calls surrounded by pargs, or arguments given to pand or ; por). For futures, the story is as follows. ; (1) A primitive adds a piece of parallelism work to one of the two ; parallelism queues (either *future-array* or *work-queue*, as ; appropriate). ; ; (2) The primitive checks to see if there are enough threads already in ; existence to process that piece of parallelism work. If so, the ; primitive returns and execution continues until the result of the ; parallelized computation is needed. If not, the primitive creates one to ; many "worker threads" to consume pieces of parallelism work. ; ; (3) The primitive may eventually need the result from the piece of ; parallelism work. In this case, it will read the value from the piece of ; parallelism work (once it is available) and use it as appropriate. In ; the case that the primitive does not need the value (as can happen with a ; pand/por or a spec-mv-let, when the speculative computation is determined ; to be useless), the primitive will abort (or early terminate) the ; parallel execution of the piece of parallelism work. ; ; When a worker thread is created, it performs the following sequence of ; steps. ; ; (A) Waits until there is a piece of parallelism work to consume. A worker ; thread will wait between 10 and 120 seconds before "giving up", unwinding ; itself, and freeing itself as a resource for the operating system to ; collect. The reader might be tempted to assume that there would ; immediately be work, but this is not guaranteed to be the case (because, ; for efficiency reasons, we typically create a handful more threads than ; are needed). ; ; (B) Waits until there is an idle CPU core available, as determined by our ; resource management using the multi-threading primitives available to us ; in the Lisp (as opposed to trying to tell the operating system how to ; schedule our threads). There is no timeout associated with this wait. ; ; (C) Making it to (C) requires that the thread first made it through (A) and ; then also made it through (B). Thus, the thread has both a piece of ; parallelism work and a CPU core. At this point, the thread executes that ; piece of parallelism work. ; ; (D) Perhaps the piece of parallelism work itself encounters a parallelism ; primitive and decides to further parallelize execution. In this case, ; the worker thread will do (1), (2), and (3), as explained above. ; ; (E) At this point, the worker thread has finished executing the piece of ; parallelism work and stores the result of that execution in the ; appropriate place (e.g., for futures, it stores the execution result in ; the "value" slot of the future). ; ; (F) After performing some cleanup, the thread goes back to (A). ; We use the phrase "Parallelism wart:" to label comments about known issues ; for the #+acl2-par build of ACL2 that we would like to fix, time permitting. ; We also use the phrase "Parallelism blemish:" to identify known issues for ; the #+acl2-par build of ACL2 that we intend to fix only if led to do so by ; user complaints. Finally, we use the phrase "Parallelism no-fix:" to label ; comments about known issues for the #+acl2-par build of ACL2 that we do not ; intend to fix, though we could reclassify these if there are sufficiently ; strong user complaints. Searching through the parallism warts, blemishes, ; and no-fixes could be useful when a user reports a bug in #+acl2-par that ; cannot be replicated in #-acl2-par. ; Parallelism hazards are unrelated to parallelism warts, blemishes, and ; no-fixes. Parallelism hazards are macros or functions that are known to be ; theoretically unsafe when performing multi-threaded execution. We originally ; did not expect users to encounter parallelism hazards (because we should have ; programmed such that the hazards never occur). However, in practice, these ; parallelism hazards are somewhat common and we have disabled the automatic ; warning that occurs everytime a hazard occurs. Once we re-enable that ; warning, in the event that users encounter a parallelism hazard, they will be ; asked to report the associated warning to the ACL2 maintainers. For example, ; if state-global-let* is called while executing concurrently, we want to know ; about it and develop a work-around. See *possible-parallelism-hazards* and ; warn-about-parallelism-hazard for more information. ; #+ACL2-PAR notes contain documentation that only applies to #+acl2-par. ; In an effort to avoid code duplication, we created a definition scheme that ; supports defining both serial and parallel versions of a function with one ; call to a defun-like macro. See the definitions of @par-mappings and ; defun@par for an explanation of this scheme. ; Developer note on emacs and parallelism. When comparing with versions up ; through svn revision 335 (May 27, 2011), it may be useful to ignore "@par" ; when ignoring whitespace with meta-x compare-windows in emacs. ; (setq compare-windows-whitespace "\\(@par\\)?[ \t\n]+") ; To revert to the default behavior: ; (setq compare-windows-whitespace ; "\\(\\s-\\| ; \\)+") ; We indent certain calls as we indent calls of defun. (These forms are in ; emacs/emacs-acl2.el.) ; (put 'er@par 'lisp-indent-function 'defun) ; (put 'warning$@par 'lisp-indent-function 'defun) ; To revert: ; (put 'er@par 'lisp-indent-function nil) ; (put 'warning$@par 'lisp-indent-function nil) ; Only allow the feature :acl2-par in environments that support ; multi-threading. Keep this in sync with the error message about CCL, ; Lispworks, and SBCL in set-parallel-execution-fn and with :doc ; compiling-acl2p. If we add support for non-ANSI GCL, consider providing a ; call of reset-parallelism-variables analogous to the one generated by setting ; *reset-parallelism-variables* in our-abort. #+(and acl2-par (not ccl) (not (and sbcl sb-thread)) (not lispworks)) (error "It is currently illegal to build the parallel version of ACL2 in this Common Lisp. See source file acl2-init.lisp for this error message, which is preceded by information (Lisp features) indicating the legal Lisp implementations.") #+akcl (setq si:*notify-gbc* t) ; The following has been superseded; see the section on reading characters from ; files in acl2.lisp. ; ; Dave Greve reported a problem: the saved_acl2 script in CLISP had characters ; ; that, contrary to expectation, were not being interpreter as newlines. The ; ; CLISP folks explained that "CUSTOM:*DEFAULT-FILE-ENCODING* defaults to :DOS ; ; on cygwin, so #\Newline is printed as '\r\n' (CRLF)." We expect that the ; ; following setting will fix the problem; Dave tried an experiment for us that ; ; seemed to validate this expectation. ; #+clisp ; (setq CUSTOM:*DEFAULT-FILE-ENCODING* :unix) #+(and lispworks (not acl2-par)) (setq system::*stack-overflow-behaviour* ; The following could reasonably be nil or :warn. David Rager did some ; experiments suggesting that ACL2(p) regressions (as of July 2011) may be more ; robust with the :warn setting. However, that setting causes warnings during ; the build, and it's easy to imagine that ACL2 users would also see that ; cryptic warning -- very un-ACL2-like! So we use nil rather than :warn here. nil) #+(and lispworks acl2-par) (setq system:*stack-overflow-behaviour* ; Since a setting of nil is at least sometimes (if not always) ignored when ; safety is set to 0 (according to an email communication between David Rager ; and Martin Simmons), we choose to use the warn setting for the #+acl2-par ; build. :warn) ; We have observed a significant speedup with Allegro CL when turning off ; its cross-referencing capability. Here are the times before and after ; evaluating the setq form below, in an example from Dave Greve that spends ; a lot of time loading compiled files. ; ; 165.43 seconds realtime, 163.84 seconds runtime. ; 120.23 seconds realtime, 118.32 seconds runtime. ; ; The user is welcome to edit the form below. Note that it doesn't seem to ; affect the profiler. #+allegro (setq excl::*record-xref-info* nil excl::*load-xref-info* nil excl::*record-source-file-info* nil excl::*load-source-file-info* nil) ; Create the packages we use. (load "acl2.lisp") ; We allow ACL2(h) code to take advantage of Ansi CL features. It's ; conceivable that we don't need this restriction (which only applies to GCL), ; but it doesn't currently seem worth the trouble to figure that out. #+(and hons (not cltl2)) (progn (format t "~%ERROR: It is illegal to build the experimental HONS version of ACL2 in this non-ANSI Common Lisp.~%~%") (acl2::exit-lisp)) ; Fix a bug in SBCL 1.0.49 (https://bugs.launchpad.net/bugs/795705), thanks to ; patch provided by Nikodemus Siivola. #+sbcl (in-package :sb-c) #+sbcl (when (equal (lisp-implementation-version) "1.0.49") (without-package-locks (defun undefine-fun-name (name) (when name (macrolet ((frob (type &optional val) `(unless (eq (info :function ,type name) ,val) (setf (info :function ,type name) ,val)))) (frob :info) (frob :type (specifier-type 'function)) (frob :where-from :assumed) (frob :inlinep) (frob :kind) (frob :macro-function) (frob :inline-expansion-designator) (frob :source-transform) (frob :structure-accessor) (frob :assumed-type))) (values)) )) ; Fix a bug in CMUCL 20D. It seems sad to test (reverse "") twice, but ; attempts to avoid that produced warnings about variable *our-old-reverse* ; being undefined, even when using with-compilation-unit. #+cmucl (progn (when (null (ignore-errors (reverse ""))) (defconstant *our-old-reverse* (symbol-function 'reverse))) (without-package-locks (when (boundp '*our-old-reverse*) (defun reverse (x) (if (equal x "") "" (funcall (symbol-value '*our-old-reverse*) x))) (compile 'reverse)))) ; WARNING: The next form should be an in-package (see in-package form for sbcl ; just above). ; Now over to the "ACL2" package for the rest of this file. (in-package "ACL2") (defconstant *current-acl2-world-key* ; *Current-acl2-world-key* is the property used for the current-acl2-world ; world. We formerly used a defvar, but there seemed to be no reason to use a ; special variable, which Gary Byers has pointed out takes about 5 instructions ; to read in CCL in order to check if a thread-local binding is dynamically in ; effect. So we tried using a defconstant. Unfortunately, that trick failed ; in Allegro CL; even if we use a boundp test to guard the defconstant, when we ; used make-symbol to create the value; the build failed. So the value is now ; an interned symbol. 'acl2_invisible::*CURRENT-ACL2-WORLD-KEY*) #+cltl2 (when (not (boundp 'COMMON-LISP::*PRINT-PPRINT-DISPATCH*)) ; Many improvements were made to ANSI GCL in May, 2013. If ; COMMON-LISP::*PRINT-PPRINT-DISPATCH* is unbound, then something is wrong with ; this Lisp. In particular, with-standard-io-syntax might not work correctly. (format t "ERROR: We do not support building ACL2 in~%~ a host ANSI Common Lisp when variable ~s is unbound. Please~%~ obtain a more recent version of your Lisp implementation." 'COMMON-LISP::*PRINT-PPRINT-DISPATCH*) (exit-lisp)) #+(and gcl cltl2) ; Deal with undefined cltl2 symbols in ANSI GCL, using values that would be ; assigned by with-standard-io-syntax. (loop for pair in '((COMMON-LISP::*PRINT-LINES* . nil) (COMMON-LISP::*PRINT-MISER-WIDTH* . nil) (COMMON-LISP::*PRINT-RIGHT-MARGIN* . nil) (COMMON-LISP::*READ-EVAL* . t)) when (not (boundp (car pair))) do (progn (proclaim `(special ,(car pair))) (setf (symbol-value (car pair)) (cdr pair)))) ; It is a mystery why the following proclamation is necessary, but it ; SEEMS to be necessary in order to permit the interaction of tracing ; with the redefinition of si::break-level. #+akcl (declaim (special si::arglist)) #+akcl (let ((v (symbol-function 'si::break-level))) (setf (symbol-function 'si::break-level) (function (lambda (&rest rst) (format t "~%Raw Lisp Break.~%") (apply v rst))))) (defun system-call (string arguments) ; Warning: Keep this in sync with system-call+. #+gcl (si::system (let ((result string)) (dolist (x arguments) (setq result (concatenate 'string result " " x))) result)) #+lispworks (system::call-system (let ((result string)) (dolist (x arguments) (setq result (concatenate 'string result " " x))) result)) #+allegro (let ((result string)) (dolist (x arguments) (setq result (concatenate 'string result " " x))) #-unix (excl::shell result) #+unix ; In Allegro CL in Unix, we can avoid spawning a new shell by calling run-shell-command ; on a simple vector. So we parse the resulting string "cmd arg1 ... argk" and ; run with the simple vector #(cmd cmd arg1 ... argk). (excl::run-shell-command (let ((lst nil) (len (length result)) (n 0)) (loop (if (>= n len) (return)) ; else get next word (let ((start n) (ch (char result n))) (cond ((member ch '(#\Space #\Tab)) (setq n (1+ n))) (t (loop (if (or (>= n len) (member (setq ch (char result n)) '(#\Space #\Tab))) (return) (setq n (1+ n)))) (setq lst (cons (subseq result start n) lst)))))) (setq result (nreverse lst)) (setq result (coerce (cons (car result) result) 'vector))))) #+cmu (ext:process-exit-code (common-lisp-user::run-program string arguments :output t)) #+sbcl (sb-ext:process-exit-code (sb-ext:run-program string arguments :output t :search t)) #+clisp (let ((result (ext:run-program string :arguments arguments))) (or result 0)) #+ccl (let* ((proc (ccl::run-program string arguments :output t)) (status (multiple-value-list (ccl::external-process-status proc)))) (if (not (and (consp status) (eq (car status) :EXITED) (consp (cdr status)) (integerp (cadr status)))) 1 ; just some non-zero exit code here (cadr status))) #-(or gcl lispworks allegro cmu sbcl clisp ccl) (declare (ignore string arguments)) #-(or gcl lispworks allegro cmu sbcl clisp ccl) (error "SYSTEM-CALL is not yet defined in this Lisp.")) (defun copy-acl2 (dir) (system-call "cp" (append '("makefile" "acl2.lisp" "acl2-check.lisp" "acl2-fns.lisp" "init.lisp" "acl2-init.lisp") (append (let ((result (list (format nil "~a" dir)))) (dolist (x *acl2-files*) (setq result (cons (format nil "~a.lisp" x) result))) result))))) (defun our-probe-file (filename) ; Use this function instead of probe-file if filename might be a directory. ; We noticed that GCL 2.6.7 on 64-bit Linux doesn't recognize directories with ; probe-file. So we use directory instead, which we have found to work in both ; 32-bit and 64-bit Linux environments. ; BUG: It appears that this function returns nil in GCL 2.6.7 when given an ; existing but empty directory. #+gcl (or (probe-file filename) (let ((x (and (not (equal filename "")) (cond ((eql (char filename (1- (length filename))) #\/) filename) (t (concatenate 'string filename "/")))))) (directory x))) #-gcl (probe-file filename)) (defun copy-distribution (output-file source-directory target-directory &optional (all-files "all-files.txt") (use-existing-target nil)) ; We check that all files and directories exist that are supposed to exist. We ; cause an error if not, which ultimately will cause the Unix process that ; calls this function to return an error status, thus halting the make of which ; this operation is a part. Wart: Since probe-file does not check names with ; wildcards, we skip those. ; Note: This function does not actually do any copying or directory creation; ; rather, it creates a file that can be executed. ; FIRST, we make sure we are in the expected directory. (cond ((not (and (stringp source-directory) (not (equal source-directory "")))) (error "The source directory specified for COPY-DISTRIBUTION~%~ must be a non-empty string, but~%~s~%is not." source-directory))) (cond ((not (and (stringp target-directory) (not (equal target-directory "")))) (error "The target directory specified for COPY-DISTRIBUTION~%must ~ be a non-empty string, but~%~s~%is not. (If you invoked ~ \"make copy-distribution\", perhaps you forgot to set DIR.)" target-directory))) (cond ((eql (char source-directory (1- (length source-directory))) #\/) ; In this code we treat all directories as names without the trailing slash. (setq source-directory (subseq source-directory 0 (1- (length source-directory)))))) (cond ((not (equal (our-truename (format nil "~a/" source-directory) :safe) (our-truename "" "Note: Calling OUR-TRUENAME from COPY-DISTRIBUTION."))) (error "We expected to be in the directory~%~s~%~ but instead are apparently in the directory~%~s .~%~ Either issue, in Unix, the command~%~ cd ~a~%~ or else edit the file (presumably, makefile) from~%~ which the function COPY-DISTRIBUTION was called,~%~ in order to give it the correct second argument." source-directory (our-truename "" t) source-directory))) ; Next, check that everything exists that is supposed to. (cond ((and (not use-existing-target) (our-probe-file target-directory)) (error "Aborting copying of the distribution. The target ~%~ distribution directory~%~s~%~ already exists! You may wish to execute the following~%~ Unix command to remove it and all its contents:~%~ rm -r ~a" target-directory target-directory))) (format t "Checking that distribution files are all present.~%") (let (missing-files) (with-open-file (str (concatenate 'string source-directory "/" all-files) :direction :input) (let (filename (dir nil)) (loop (setq filename (read-line str nil)) (cond ((null filename) (return)) ((or (equal filename "") (equal (char filename 0) #\#))) ((find #\Tab filename) (error "Found a line with a Tab in it: ~s" filename)) ((find #\Space filename) (error "Found a line with a Space in it: ~s" filename)) ((find #\* filename) (format t "Skipping wildcard file name, ~s.~%" filename)) ((eql (char filename (1- (length filename))) #\:) ; No need to check for directories here; they'll get checked elsewhere. But ; it's harmless enough to do so. (let* ((new-dir (subseq filename 0 (1- (length filename)))) (absolute-dir (format nil "~a/~a" source-directory new-dir))) (cond ((our-probe-file absolute-dir) (setq dir new-dir)) (t (setq missing-files (cons absolute-dir missing-files)) (error "Failed to find directory ~a ." absolute-dir))))) (t (let ((absolute-filename (if dir (format nil "~a/~a/~a" source-directory dir filename) (format nil "~a/~a" source-directory filename)))) (cond ((not (our-probe-file absolute-filename)) (setq missing-files (cons absolute-filename missing-files)) (format t "Failed to find file ~a.~%" absolute-filename))))))))) (cond (missing-files (error "~%Missing the following files (and/or directories):~%~s" missing-files)) (t (format t "Distribution files are all present.~%")))) (format t "Preparing to copy distribution files from~%~a/~%to~%~a/ .~%" source-directory target-directory) (let (all-dirs) ; In this pass, we look only for directory names. (with-open-file (str (concatenate 'string source-directory "/" all-files) :direction :input) (let (filename) (loop (setq filename (read-line str nil)) (cond ((null filename) (return)) ((or (equal filename "") (equal (char filename 0) #\#))) ((find #\Tab filename) (error "Found a line with a Tab in it: ~s" filename)) ((find #\Space filename) (error "Found a line with a Space in it: ~s" filename)) ((eql (char filename (1- (length filename))) #\:) (setq all-dirs (cons (subseq filename 0 (1- (length filename))) all-dirs))))))) ; In the final pass we do our writing. (with-open-file (str (concatenate 'string source-directory "/" all-files) :direction :input) (with-open-file (outstr output-file :direction :output) (let (filename (dir nil)) (if (not use-existing-target) (format outstr "mkdir ~a~%~%" target-directory)) (loop (setq filename (read-line str nil)) (cond ((null filename) (return)) ((or (equal filename "") (equal (char filename 0) #\#))) ((eql (char filename (1- (length filename))) #\:) (setq dir (subseq filename 0 (1- (length filename)))) (format outstr "~%mkdir ~a/~a~%" target-directory dir)) ((null dir) (cond ((not (member filename all-dirs :test 'equal)) (format outstr "cp -p ~a/~a ~a~%" source-directory filename target-directory)))) (t (cond ((not (member (format nil "~a/~a" dir filename) all-dirs :test 'equal)) (format outstr "cp -p ~a/~a/~a ~a/~a~%" source-directory dir filename target-directory dir))))))))) (format t "Finished creating a command file for copying distribution files."))) (defun make-tags () #-(or ccl sbcl cmu) ; We disallow ccl and sbcl for the following check. We have found that the ; result of the system-call is a process, (typep 'external-process) in ; ccl and (typep 'sb-impl::process) in sbcl, which can probably be ; made to yield the status. But the status is 0 even for commands not found, ; so why bother? Since cmucl seems to fall victim in the same way as sbcl, we ; treat these two the same here. (when (not (eql (system-call "which" '("etags")) 0)) (format t "SKIPPING etags: No such program is in the path.") (return-from make-tags 1)) (system-call "etags" (let* ((fmt-str #+(or cmu sbcl clisp ccl) "~a.lisp" #-(or cmu sbcl clisp ccl) " ~a.lisp") (lst (append '("acl2.lisp" "acl2-check.lisp" "acl2-fns.lisp" "init.lisp" "acl2-init.lisp" "akcl-acl2-trace.lisp" "allegro-acl2-trace.lisp" "openmcl-acl2-trace.lisp") (let ((result nil)) (dolist (x *acl2-files*) (setq result (cons (format nil fmt-str x) result))) (reverse result))))) ; We want to be sure to include the *-raw.lisp files even if we are not ; building the hons version, in order to assist in maintaining both versions. (append lst (list "hons-raw.lisp" "memoize-raw.lisp" "multi-threading-raw.lisp" "futures-raw.lisp" "parallel-raw.lisp" "serialize-raw.lisp"))))) (defvar *saved-build-date-lst*) (defvar *saved-mode*) (defun svn-revision-from-line (s) ; S is a string such as "$Revision: 1053 $" (as in acl2-startup-info.txt) or ; "Revision: 1998" (as printed by "svn info"). In general, it is a string for ; which we want the object that is read immediately after the first #\: . If ; there is none, then we return nil. (let ((p (position #\: s))) (and p (read-from-string s nil nil :start (1+ p))))) (defconstant *acl2-svn-revision-string* (let ((file "acl2-startup-info.txt")) (cond ((probe-file file) (let ((val (with-open-file (str file :direction :input) (read str)))) (cond ((eq val :release) nil) ((stringp val) (let ((n (svn-revision-from-line val))) (or n (error "Unexpected error in getting svn revision ~ from string:~%~s~%" val)) (format nil " ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + WARNING: This is NOT an ACL2 release; it is svn revision ~a. + + The authors of ACL2 consider svn distributions to be experimental; + + they may be incomplete, fragile, and unable to pass our own + + regression. Bug reports should include the following line: + + ACL2 svn revision ~a; community books svn revision ~a + ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ " n n "~a"))) (t (error "Illegal value in file ~s: ~s" file val))))) (t (error "File ~s appears not to exist." file))))) (defvar *saved-string* (concatenate 'string "~% ~a built ~a.~ ~% Copyright (C) 2013, Regents of the University of Texas" "~% ACL2 comes with ABSOLUTELY NO WARRANTY. This is free software and you~ ~% are welcome to redistribute it under certain conditions. For details,~ ~% see the LICENSE file distributed with ACL2.~%" (or *acl2-svn-revision-string* ; If acl2-startup-info.txt begins with the symbol :release, then ; *acl2-svn-revision-string* is a string with ~a expecting to be bound to the ; svn revision of the books. Otherwise we put "~a" here to be bound to "". "~a") "~a" #+hons "~%~% Experimental modification for HONS, memoization, and applicative hash~ ~% tables.~%" #+acl2-par "~%~% Experimental modification for parallel evaluation. Please expect at~ ~% most limited maintenance for this version~%" "~% See the documentation topic ~a for recent changes." "~% Note: We have modified the prompt in some underlying Lisps to further~ ~% distinguish it from the ACL2 prompt.~%")) (defun maybe-load-acl2-init () (let* ((home (our-user-homedir-pathname)) (fl (and home (probe-file (merge-pathnames home "acl2-init.lsp"))))) (when fl (load fl)))) (defun chmod-executable (sysout-name) (system-call "chmod" (list "+x" sysout-name))) (defun saved-build-dates (separator) (let* ((date-lst (reverse *saved-build-date-lst*)) (result (car date-lst)) (sep (concatenate 'string (string #\Newline) (if (eq separator :terminal) (concatenate 'string (make-string (+ 3 (length *copy-of-acl2-version*)) :initial-element #\Space) "then ") separator)))) (dolist (s (cdr date-lst)) (setq result (concatenate 'string result sep s))) result)) (defmacro our-with-standard-io-syntax (&rest args) (cons #-cltl2 'progn #+cltl2 'with-standard-io-syntax args)) (defun user-args-string (inert-args &optional (separator '"--")) ; This function is used when saving executable scripts, which may specify that ; certain command line arguments are not to be processed by Lisp (other than to ; affect the value of a variable or function such as ; ccl::*unprocessed-command-line-arguments*; see books/oslib/argv.lisp). Also ; see :doc save-exec. A common convention is that arguments after `--' are not ; processed by Lisp. (cond ((null inert-args) "\"$@\"") ((eq inert-args t) (concatenate 'string separator " \"$@\"")) (t (concatenate 'string separator " " inert-args " \"$@\"")))) (defmacro write-exec-file (stream prefix string &rest args) ; Prefix is generally nil, but can be (string . fmt-args). String is the ; actual command invocation, with the indicated format args, args. `(our-with-standard-io-syntax ; Thus, we hope that progn is OK for GCL! (format ,stream "#!/bin/sh~%~%") (format ,stream "# Saved ~a~%~%" (saved-build-dates "# then ")) ,@(and prefix `((format ,stream ,(car prefix) ,@(cdr prefix)))) (format ,stream ; We generally take Noah Friedman's suggestion of using "exec" since there is ; no reason to keep the saved_acl2 shell script in the process table. However, ; we have seen Windows write out "C:..." as the path of the executable, because ; that is what truename produces. But then the "exec" seems to consider that ; to be a relative pathname, and invoking ./saved_acl2 thus fails. So we ; eliminate the "exec" in Windows; we have found that this works fine, at least ; for GCL and SBCL. #-mswindows (concatenate 'string "exec " ,string) #+mswindows ,string ,@args))) (defun proclaim-files (&optional outfilename infilename infile-optional-p) ; IMPORTANT: This function assumes that the defconst and defmacro forms in the ; given files have already been evaluated. One way to achieve this state of ; affairs, of course, is to load the files first. (when (and outfilename infilename) (error "It is illegal to supply non-nil values for both optional ~ arguments of proclaim-files.")) (when (not *do-proclaims*) (return-from proclaim-files nil)) (cond (outfilename (format t "Writing proclaim forms for ACL2 source files to file ~s.~%" outfilename)) (t (when infilename (cond ((probe-file infilename) (format t "Loading nontrivial generated file of proclaim forms, ~ ~s...~%" infilename) (load infilename) (format t "Completed load of ~s.~%" infilename) (return-from proclaim-files nil)) (infile-optional-p) ; fall through as though infilename is nil (t (error "File ~s is to be loaded by proclaim-files, but does ~ not exist." infilename)))) (format t "Generating and evaluating proclaim forms for ACL2 source ~ files.~%"))) (let (str) (when outfilename (if (probe-file outfilename) (delete-file outfilename)) (or (setq str (safe-open outfilename :direction :output)) (error "Unable to open file ~s for output." outfilename))) ; It is tempting to print an in-package form, but we leave that task to ; proclaim-file, which presumably finds the first form to be an in-package ; form. (dolist (fl *acl2-files*) (proclaim-file (format nil "~a.lisp" fl) str)) (when str ; equivalently, when outfilename is non-nil (close str)))) (defun insert-string (s) (cond ((null s) "") (t (concatenate 'string " " s)))) #+gcl (defvar *saved-system-banner* ; This variable is only used in GCL 2.6.9 and later, and the following comments ; pertain only to that case. ; Set this variable to nil before save-exec in order to save an image without a ; GCL startup banner, as this will leave si::*system-banner* unbound; see ; below. ; ACL2 keeps this value at nil except when acl2-default-restart unbinds ; si::*system-banner*, in which case *saved-system-banner* is set to the value ; of si::*system-banner* just before that unbinding takes place. When ; save-exec saves an image, it first checks whether si::*system-banner* is ; unbound and *saved-system-banner* is non-nil, in which case it sets ; si::*system-banner* to *saved-system-banner*. Even if si::*system-banner* is ; bound, *saved-system-banner* is set to nil before saving an image. nil) #+akcl (defun save-acl2-in-akcl-aux (sysout-name gcl-exec-name write-worklispext set-optimize-maximum-pages host-lisp-args inert-args) (when (and (gcl-version-> 2 6 9 t) *saved-system-banner*) (when (not (boundp 'si::*system-banner*)) ; true, unless user intervened (setq si::*system-banner* *saved-system-banner*)) (setq *saved-system-banner* nil)) (if (and write-worklispext (probe-file "worklispext")) (delete-file "worklispext")) (let* ((ext "gcl") (ext+ ; We deal with the apparent fact that Windows implementations of GCL append ; ".exe" to the filename created by save-system. #+mswindows "gcl.exe" #-mswindows "gcl") (gcl-exec-file (unix-full-pathname gcl-exec-name ext+))) (if write-worklispext (with-open-file (str "worklispext" :direction :output) (format str ext+))) (if (probe-file sysout-name) (delete-file sysout-name)) (if (probe-file gcl-exec-file) (delete-file gcl-exec-file)) (with-open-file (str sysout-name :direction :output) (write-exec-file str nil "~s~s ~a~%" gcl-exec-file (insert-string host-lisp-args) (user-args-string inert-args))) (cond ((and set-optimize-maximum-pages (boundp 'si::*optimize-maximum-pages*)) ; We follow a suggestion of Camm Maguire by setting ; 'si::*optimize-maximum-pages* to t just before the save. We avoid the ; combination of 'si::*optimize-maximum-pages* and sgc-on for GCL versions ; through 2.6.3, because of problematic interactions between SGC and ; si::*optimize-maximum-pages*. This issue has been fixed starting with GCL ; 2.6.4. Since si::*optimize-maximum-pages* is only bound starting with ; sub-versions of 2.6, the problem only exists there. (cond ((or (not (fboundp 'si::sgc-on)) (gcl-version-> 2 6 3)) (setq si::*optimize-maximum-pages* t))))) (chmod-executable sysout-name) (si::save-system (concatenate 'string sysout-name "." ext)))) #+akcl (defun save-acl2-in-akcl (sysout-name gcl-exec-name &optional mode do-not-save-gcl) (setq *saved-mode* mode) (setq *acl2-allocation-alist* ; If *acl2-allocation-alist* is rebound before allocation is done in ; si::*top-level-hook*, e.g., if it is bound in one's init.lisp or ; acl2-init.lsp file, then such binding will override this one. The package ; name shouldn't matter for the keys in user's alist, but in the code below we ; need to keep 'hole in the ACL2 package because we refer to it below. ; Historical Comments: ; Where did these numbers come from? At CLInc we have used the numbers from ; the non-small-p case for some time, and they seem satisfactory. When we ; moved to a "small" image in Version 1.8, we wanted to have roughly the same ; number of free cells as we've had all along, as a default. The cons number ; below is obtained by seeing how many pages we had free (pages in use ; multiplied by percent free, as shown by (room)) the last time we built ACL2, ; before modifying ACL2 to support small images, and adding that to the number ; of pages in use in the small image when no extra pages were allocated at ; start-up. The total was 2917, so that is what we use below. The relocatable ; size is rather arbitrary, and the hole size has been suggested by Bill ; Schelter. Finally, the other numbers were unchanged when we used the same ; algorithm described above for cons (except for fixnum, which came out to 99 ; -- close enough!). ; Warning: as of this writing (5/95), there are versions of Linux in which the ; page size is half of that in GCL on a Sparc. In that case, we should double ; the number of pages in each case in order to have the same amount of free ; objects available. We do this below; see the next comment. (We assume that ; there are still the same number of bytes per object; at least, in one ; instance in Linux that appears to be the case for cons, namely, 12 bytes per ; cons.) ; Additional comments during Version_2.9 development: ; When built with GCL 2.6.1-38 and *acl2-allocation-alist* = nil, we have: ; ACL2>(room) ; ; 4972/4972 61.7% CONS RATIO LONG-FLOAT COMPLEX STRUCTURE ; 133/274 14.0% FIXNUM SHORT-FLOAT CHARACTER RANDOM-STATE READTABLE NIL ; 210/462 97.5% SYMBOL STREAM ; 1/2 37.2% PACKAGE ; 69/265 1.0% ARRAY HASH-TABLE VECTOR BIT-VECTOR PATHNAME CCLOSURE FAT-STRING ; 1290/1884 7.4% STRING ; 711/779 0.9% CFUN BIGNUM ; 29/115 82.8% SFUN GFUN CFDATA SPICE NIL ; ; 1302/1400 contiguous (176 blocks) ; 13107 hole ; 5242 0.0% relocatable ; ; 7415 pages for cells ; 27066 total pages ; 93462 pages available ; 10544 pages in heap but not gc'd + pages needed for gc marking ; 131072 maximum pages ; ; ACL2> ; So as an experiment we used some really large numbers below (but not for hole or ; relocatable). They seemed to work well, but see comment just below. ; End of Historical Comments. (cond ((gcl-version-> 2 6 1) ; In GCL 2.6.5, and in fact starting (we believe) with GCL 2.6.2, GCL does not ; need preallocation to do the job well. We got this opinion after discussions ; with Bob Boyer and Camm Maguire. In a pre-release of Version_2.9, we found ; there was no noticeable change in regression time or image size when avoiding ; preallocation. So it seems reasonable to stop messing with such numbers so ; that they do not become stale and interfere with GCL doing its job. nil) (t `((hole) (relocatable) (cons . 10000) (fixnum . 300) ; Apparently bignums are in CFUN space starting with GCL 2.4.0. So we make ; sure explicitly that there is enough room for bignums. Before GCL 2.4.0, ; bignums are in CONS space so the following should be irrelevant. (bignum . 800) (symbol . 500) (package) (array . 300) (string . 2000) ;;(cfun . 32) ; same as bignum (sfun . 200))))) ; Now adjust if the page size differs from that for GCL/AKCL running on a ; Sparc. See comment above. (let ((multiplier (/ 4096 si::lisp-pagesize))) (cond ((not (= multiplier 1)) (setq *acl2-allocation-alist* (loop for (type . n) in *acl2-allocation-alist* collect (cons type (and n (round (* multiplier n))))))))) (setq si::*top-level-hook* #'(lambda () (acl2-default-restart) (cond (*acl2-allocation-alist* ; (format ; t ; "Beginning allocations. Set acl2::*acl2-allocation-alist* to NIL~%~ ; in ~~/acl2-init.lsp if you must make your running image smaller.~%") (loop for (type . n) in *acl2-allocation-alist* when n do ; (format t "Allocating ~s to ~s.~%" type n) (let ((x (symbol-name type))) (cond ((equal x "HOLE") (si::set-hole-size n)) ((equal x "RELOCATABLE") (si::allocate-relocatable-pages n)) (t (si::allocate type n t))))))) (lp))) (load "akcl-acl2-trace.lisp") ; Return to normal allocation growth. Keep this in sync with load-acl2, which ; had presumably already set the allocation growth to be particularly slow. (loop for type in '(cons fixnum symbol array string cfun sfun ; In akcl, at least some versions of it, we cannot call allocate-growth on the ; following two types. ; Camm Maguire has told us on 9/22/2013 that certain allocations for contiguous ; pages, as we now do in acl2.lisp for GCL 2.6.10 and later (which includes GCL ; 2.6.10pre as of 9/22/2013). ; #+gcl contiguous #+gcl relocatable ) do (cond ((or (boundp 'si::*gcl-major-version*) ;GCL 2.0 or greater (and (boundp 'si::*gcl-version*) ;GCL 1.1 (= si::*gcl-version* 1))) (si::allocate-growth type 0 0 0 0)) (t (si::allocate-growth type 0 0 0)))) ; (print "Start (si::gbc nil)") ;debugging GC ; Camm Maguire suggests leaving the hole size alone for GCL 2.6.10pre as of ; 9/22/2013: ; (si::set-hole-size 500) ; wfs suggestion ; Camm Maguire says (7/04) that "the gc algorithm skips over any pages which ; have not been written to since sgc-on was invoked. So gc really needs to be ; done before turning [sgc] on (not off)...." (si::gbc t) ; wfs suggestion [at least if we turn on SGC] -- formerly nil ; (don't know why...) (cond ((fboundp 'si::sgc-on) (print "Executing (si::sgc-on t)") ;debugging GC (funcall 'si::sgc-on t))) ; Set the hole to be sufficiently large so that ACL2 can do all the allocations ; quickly when it starts up, without any GC, leaving the desired size hole when ; finished. (let ((new-hole-size (or (cdr (assoc 'hole *acl2-allocation-alist*)) (si::get-hole-size)))) (loop for (type . n) in *acl2-allocation-alist* with space when (and n (not (equal (symbol-name type) "HOLE")) (< (setq space #+gcl (cond ;2.0 or later? ((boundp 'si::*gcl-major-version*) (nth 1 (multiple-value-list (si::allocated type)))) (t (caddr (si::allocated type)))) #-gcl (cond ((equal (symbol-name type) "RELOCATABLE") (si::allocated-relocatable-pages)) (t (si::allocated-pages type)))) n)) do (setq new-hole-size (+ new-hole-size (- n space)))) ; (print "Set hole size") ;debugging ; Camm Maguire suggests leaving the hole size alone for GCL 2.6.10pre as of ; 9/22/2013: ; (si::set-hole-size new-hole-size) ) ; The calculation above is legacy. Now we increment the hole size to 20% of ; max-pages instead of the default 10%. Camm Maguire says that "Larger values ; allow quick allocation of pages without triggering gc" and that the hole is ; part of the virtual (not resident) memory size, rather than being saved to ; disk. ; Camm Maguire suggests leaving the hole size alone for GCL 2.6.10pre as of ; 9/22/2013: ; (let ((new-size (floor si:*lisp-maxpages* 5))) ; (if (< (si:get-hole-size) new-size) ; (si::set-hole-size new-size))) ; (print (true-listp (w *the-live-state*))) ;swap in the world's pages ; (print "Save the system") ;debugging (when (not do-not-save-gcl) (save-acl2-in-akcl-aux sysout-name gcl-exec-name t t nil nil))) #+akcl (defun save-exec-raw (sysout-name host-lisp-args inert-args) (setq *acl2-allocation-alist* nil) ; Don't meddle with allocations. (setq *acl2-default-restart-complete* nil) (save-acl2-in-akcl-aux sysout-name sysout-name nil nil host-lisp-args inert-args)) (defvar *acl2-default-restart-complete* nil) (defun fix-default-pathname-defaults () ; Some Lisps save *default-pathname-defaults* and do not reset it at startup. ; According to our experiments: ; - CCL, CMUCL, LispWorks, and GCL retain *default-pathname-defaults*. ; - SBCL and Allegro CL apparently do not retain *default-pathname-defaults*, ; but instead setting it at startup according to the current working ; directory. ; - CLISP sets *default-pathname-defaults* to #P"" at startup. ; But since *default-pathname-defaults* can affect truename, we want it to ; reflect the current working directory. #+(or ccl cmu gcl lispworks) (when (pathname-directory *default-pathname-defaults*) (let ((p (make-pathname))) (format t "~%Note: Resetting *default-pathname-defaults* to ~s.~%" p) (setq *default-pathname-defaults* p))) nil) (defvar *print-startup-banner* ; One might want to set this variable to nil in raw Lisp before calling ; save-exec, in order to avoid seeing startup information. We do not comment ; here on whether that is legally appropriate; for example, it suppresses ; copyright information for ACL2 and, for CCL at least, information about the ; host Lisp. We also do not guarantee that this behavior (suppressing printing ; of startup information) is supported for every host Lisp. ; Note that LD always prints some startup information, regardless of the value ; of *print-startup-banner*. To suppress that information, evaluate ; (set-ld-verbose nil state) in the ACL2 loop. t) (defvar *lp-ever-entered-p* nil) (defun acl2-default-restart () (if *acl2-default-restart-complete* (return-from acl2-default-restart nil)) (setq *lp-ever-entered-p* nil) (#+cltl2 common-lisp-user::acl2-set-character-encoding #-cltl2 user::acl2-set-character-encoding) (fix-default-pathname-defaults) #+ccl (progn ; In CCL, print greeting now, rather than upon first re-entry to ACL2 loop. ; Here we follow a suggestion from Gary Byers. (when *print-startup-banner* (format t "~&Welcome to ~A ~A!~%" (lisp-implementation-type) (lisp-implementation-version))) (setq ccl::*inhibit-greeting* t)) #+gcl (progn ; Some recent versions of GCL (specifically, 2.6.9 in Sept. 2013) do not print ; the startup banner until we first exit the loop. So we handle that situation ; much as we handle a similar issue for CCL above, following GCL source file ; lsp/gcl_top.lsp. (when (and *print-startup-banner* (gcl-version-> 2 6 9 t) (boundp 'si::*system-banner*)) (format t si::*system-banner*) (setq *saved-system-banner* si::*system-banner*) (makunbound 'si::*system-banner*) (when (boundp 'si::*tmp-dir*) (format t "Temporary directory for compiler files set to ~a~%" si::*tmp-dir*)))) #+hons (qfuncall acl2h-init) (when *print-startup-banner* (format t *saved-string* *copy-of-acl2-version* (saved-build-dates :terminal) (if (null *acl2-svn-revision-string*) "" (qfuncall acl2-books-revision)) (cond (*saved-mode* (format nil "~% Initialized with ~a." *saved-mode*)) (t "")) (eval '(latest-release-note-string)) ; avoid possible warning )) (maybe-load-acl2-init) (eval `(in-package ,*startup-package-name*)) ; The following two lines follow the recommendation in Allegro CL's ; documentation file doc/delivery.htm. #+allegro (tpl:setq-default *package* (find-package *startup-package-name*)) #+allegro (rplacd (assoc 'tpl::*saved-package* tpl:*default-lisp-listener-bindings*) 'common-lisp:*package*) #+allegro (lp) #+lispworks (lp) #+ccl (eval '(lp)) ; using eval to avoid compiler warning (setq *acl2-default-restart-complete* t) nil) #+cmu (defun cmulisp-restart () (when *print-startup-banner* (extensions::print-herald t)) (acl2-default-restart) (lp)) #+sbcl (defun sbcl-restart () (acl2-default-restart) ; Use eval to avoid style-warning for undefined function LP. (eval '(lp))) #+lucid (defun save-acl2-in-lucid (sysout-name &optional mode) (setq *saved-mode* mode) (user::disksave sysout-name :restart-function 'acl2-default-restart :full-gc t)) #+lispworks (defun lispworks-save-exec-aux (sysout-name eventual-sysout-name host-lisp-args inert-args) ; LispWorks support (Dave Fox) pointed out, in the days of LispWorks 4, that we ; need to be sure to call (mp:initialize-multiprocessing) when starting up. Up ; through ACL2 Version_4.2 we did that by making that call in ; acl2-default-restart. But when testing with LispWorks 6.0, we noticed that ; some processes hang, and we wondered if that has to do with the fact that ; (mp:initialize-multiprocessing) does not return. That also got in the way of ; our running (LP) in acl2-default-restart. We experimented with removing ; (mp:initialize-multiprocessing) from acl2-default-restart, instead passing ; :multiprocessing t to system::save-image. But with that change, we noticed ; that upon exiting the ACL2 loop with :q, we got the following rather scary ; message. ; ;; No live processes except internal servers - stopping multiprocessing ; So we have decided not to call :multiprocessing t, and also not to call ; (mp:initialize-multiprocessing) in the #-acl2-par case. #+acl2-par (when mp::*multiprocessing* (send-die-to-worker-threads) (mp::stop-multiprocessing) (gc$)) #+acl2-par (when *lp-ever-entered-p* ; don't print during compliation (format t "If you wish to continue using this image, you will need to call ~%~ 'mp:initialize-multiprocessing' instead of calling 'lp'. This ~%~ is necessary because of the way multiprocessing works in ~%~ Lispworks.~%~%")) ; We just make a guess that Lispworks can be handled the way that GCL is ; handled. (if (probe-file "worklispext") (delete-file "worklispext")) (let* ((ext "lw") (ext+ ; We deal with the apparent fact that Windows implementations of GCL append ; ".exe" to the filename created by save-system -- and assume, until someone ; tells us otherwise, that Lispworks does similarly. #+mswindows "lw.exe" #-mswindows "lw") (lw-exec-file (unix-full-pathname sysout-name ext+)) (eventual-lw-exec-file (unix-full-pathname eventual-sysout-name ext+))) (with-open-file (str "worklispext" :direction :output) (format str ext+)) (if (probe-file sysout-name) (delete-file sysout-name)) (if (probe-file lw-exec-file) (delete-file lw-exec-file)) (with-open-file (str sysout-name :direction :output) (write-exec-file str nil ; We pass options "-init -" and "-siteinit -" to inhibit loading init and patch ; files because because we assume that whatever such files were to be loaded, ; were in fact loaded at the time the original Lispworks executable was saved. ; Of course, individual users who doesn't like this decision and know better ; could always edit this script file, i.e., lw-exec-file, in the same spirit as ; changing the underlying Lisp implementation before building ACL2 (again, ; presumably based on knowledge of the host Lisp implementation). "~s -init - -siteinit -~a ~a~%" eventual-lw-exec-file (insert-string host-lisp-args) (user-args-string inert-args))) (chmod-executable sysout-name) (cond ((and system::*init-file-loaded* system::*complain-about-init-file-loaded*) ; We hope it's fine to save an image when an init-file has been loaded. Maybe ; somebody can explain to us why LispWorks causes a break in such a situation ; by default (which explains the binding of ; system::*complain-about-init-file-loaded* below). (format t "Warning: Overriding LispWorks hesitation to save an image~%~ after init-file has been loaded.~%") (let ((system::*complain-about-init-file-loaded* nil)) (system::save-image lw-exec-file :restart-function 'acl2-default-restart #+acl2-par :multiprocessing #+acl2-par t :gc t))) (t (system::save-image lw-exec-file :restart-function 'acl2-default-restart #+acl2-par :multiprocessing #+acl2-par t :gc t))))) #+lispworks (defun save-acl2-in-lispworks (sysout-name mode eventual-sysout-name) (setq *saved-mode* mode) (if (probe-file "worklispext") (delete-file "worklispext")) (with-open-file (str "worklispext" :direction :output) (format str "lw")) (lispworks-save-exec-aux sysout-name eventual-sysout-name nil nil)) #+lispworks (defun save-exec-raw (sysout-name host-lisp-args inert-args) ; See the comment above about :multiprocessing t. (setq *acl2-default-restart-complete* nil) (lispworks-save-exec-aux sysout-name sysout-name host-lisp-args inert-args)) #+cmu (defun save-acl2-in-cmulisp-aux (sysout-name core-name host-lisp-args inert-args) (let ((eventual-sysout-core (unix-full-pathname core-name "core")) (sysout-core (unix-full-pathname sysout-name "core"))) (if (probe-file sysout-name) (delete-file sysout-name)) (if (probe-file eventual-sysout-core) (delete-file eventual-sysout-core)) (with-open-file ; write to nsaved_acl2 (str sysout-name :direction :output) (let* ((prog1 (car extensions::*command-line-strings*)) (len (length prog1)) (prog2 (cond ((< len 4) ; If cmucl is installed by extracting to /usr/local/ then the cmucl command is ; simply "lisp" (thanks to Bill Pase for pointing this out). "lisp") ; The next two cases apply in 18e (and probably earlier) but not 19a (and ; probably later), which has the correct path (doesn't need "lisp" appended). ((equal (subseq prog1 (- len 4) len) "bin/") (concatenate 'string prog1 "lisp")) ((equal (subseq prog1 (- len 3) len) "bin") (concatenate 'string prog1 "/lisp")) (t prog1)))) (write-exec-file str nil "~s -core ~s -dynamic-space-size ~s -eval ~ '(acl2::cmulisp-restart)'~a ~a~%" prog2 eventual-sysout-core ; In our testing for ACL2 Version_6.2 we found that certification failed for ; ACL2(h) built on CMUCL for the book tau/bounders/elementary-bounders.lisp, ; with the error: "CMUCL has run out of dynamic heap space (512 MB)." This ; failure doesn't seem to be fully reproduceable, but it seems safest to ; increase the stack size. Our CMUCL image, even though on 64-bit linux, ; reported the following when we tried a value of 2000 here: ; -dynamic-space-size must be no greater than 1632 MBytes. ; Indeed, we have exceeded that in a version of community book ; books/centaur/gl/solutions.lisp using ACL2(h) built on CMUCL. So we use the ; maximum possible value just below. 1632 (insert-string host-lisp-args) (user-args-string inert-args)))) (chmod-executable sysout-name) (system::gc) (extensions::save-lisp sysout-core :load-init-file nil :site-init nil ; We call print-herald in cmulisp-restart, so that the herald is printed ; before the ACL2-specific information (and before the call of lp). :print-herald nil))) #+cmu (defun save-acl2-in-cmulisp (sysout-name &optional mode core-name) (setq *saved-mode* mode) (if (probe-file "worklispext") (delete-file "worklispext")) (with-open-file (str "worklispext" :direction :output) (format str "core")) (save-acl2-in-cmulisp-aux sysout-name core-name nil nil)) #+cmu (defun save-exec-raw (sysout-name host-lisp-args inert-args) (setq *acl2-default-restart-complete* nil) (save-acl2-in-cmulisp-aux sysout-name sysout-name host-lisp-args inert-args)) #+sbcl (defvar *sbcl-dynamic-space-size* ; The user is welcome to set this value, either by setting this variable before ; saving an ACL2 image, or by editing the resulting script (e.g., saved_acl2 or ; saved_acl2h). Here we explain the defaults that we provide for this ; variable. ; We observed during development of Version_5.0 that --dynamic-space-size 2000 ; is necessary in order to complete an ACL2(h) regression with SBCL 1.0.55 on a ; Mac OS 10.6 laptop; otherwise Lisp dies during a GC when certifying community ; book books/centaur/tutorial/intro.lisp, even with (clear-memoize-tables) ; executed at the start of acl2-compile-file and with (gc$ :full t) executed ; there as well, and also at the start of write-expansion-file and immediately ; before and after include-book-fn in certify-book-fn. We believe that it has ; been necessary to use such a --dynamic-space-size setting even to build ACL2 ; (not only ACL2(h)) with SBCL on some platforms, so we decided to use this ; option for ACL2, not just ACL2(h). ; But in December 2012 we found that 2000 is not sufficient using SBCL 1.0.49 ; on our 64-bit linux system. Our first such failure was in certifying ; community book ; books/models/y86/y86-two-level-abs/common/x86-state-concrete.lisp in ACL2(h). ; We tried increasing the --dynamic-space-size to 4000, but that was not ; sufficient for community book ; books/models/y86/y86-basic/common/x86-state.lisp; in fact, 8000 also was not ; sufficient for that book. Fortunately, 16000 was sufficient. ; These are unusual books, in that they allocate an array of size 2^32. ; Therefore we only increase the value to 16000 under #+hons; after all, the ; ACL2 regression (as opposed to ACL2(h)) does not certify the y86 books in ; ACL2. If --dynamic-space-size 16000 causes a problem for some ACL2(h) users, ; a simple solution will be for them to edit saved_acl2 or for them to build ; ACL2 after defining this variable to be smaller than 16000 (though some ; community book certifications may fail under under books/models/y86/, which ; are done by default for ACL2(h)). ; On 32-bit systems, 16000 may be too large. We tried it on a 32-bit Linux ; system and got an error upon starting ACL2: "--dynamic-space-size argument is ; out of range: 16000". So we revert to our earlier value of 2000 for such ; systems, even if we are doing an ACL2(h) build. (The y86 books will likely ; fail in this case, but we expect ACL2(h) users will generally be on 64-bit ; systems.) #+(and x86-64 hons) 16000 #-(and x86-64 hons) 2000) #+sbcl (defvar *sbcl-contrib-dir* nil) #+sbcl (defun save-acl2-in-sbcl-aux (sysout-name core-name host-lisp-args toplevel-args inert-args) ; Note that host-lisp-args specifies what the SBCL manual calls "runtime ; options", while toplevel-args is what it calls "toplevel options". (declaim (optimize (sb-ext:inhibit-warnings 3))) (let ((eventual-sysout-core (unix-full-pathname core-name "core")) (sysout-core (unix-full-pathname sysout-name "core"))) (if (probe-file sysout-name) (delete-file sysout-name)) (if (probe-file eventual-sysout-core) (delete-file eventual-sysout-core)) (with-open-file ; write to nsaved_acl2 (str sysout-name :direction :output) (let* ((prog (car sb-ext:*posix-argv*))) (write-exec-file str ; In order to profile, Nikodemus Siivola has told us that we "need to set ; SBCL_HOME to the location of the contribs". Using apropos we found the ; function SB-INT:SBCL-HOMEDIR-PATHNAME that returns the right directory in ; SBCL Version 1.0.23. So we'll use that. ("~a~%" (let ((contrib-dir (or *sbcl-contrib-dir* (and (boundp 'sb-ext::*core-pathname*) (ignore-errors (let* ((core-dir (pathname-directory sb-ext::*core-pathname*)) (contrib-dir-pathname (and (equal (car (last core-dir)) "output") (make-pathname :directory (append (butlast core-dir 1) (list "contrib")))))) (and (probe-file contrib-dir-pathname) (setq *sbcl-contrib-dir* (namestring contrib-dir-pathname))))))))) (if contrib-dir (format nil "export SBCL_HOME=~s" contrib-dir) ""))) ; We have observed with SBCL 1.0.49 that "make HTML" fails on our 64-bit linux ; system unless we start sbcl with --control-stack-size 4 [or larger]. The ; default, according to http://www.sbcl.org/manual/, is 2. The problem seems ; to be stack overflow from fmt0, which is not tail recursive. More recently, ; community book centaur/misc/defapply.lisp causes a stack overflow even with ; --control-stack-size 4 (though that might disappear after we added (comp t) ; in a couple of places). Yet more recently, community books ; books/centaur/regression/common.lisp and books/centaur/tutorial/intro.lisp ; fail with --control-stack-size 8, due to calls of def-gl-clause-processor. ; So we use --control-stack-size 16. We might increase 16 to 32 or greater in ; the future. ; See *sbcl-dynamic-space-size* for an explanation of the --dynamic-space-size ; setting below. ; Note that --no-userinit was introduced into SBCL in Version 0.9.13, hence has ; been part of SBCL since 2007 (perhaps earlier). So when Jared Davis pointed ; out this option to us after ACL2 Version_6.2, we started using it in place of ; " --userinit /dev/null", which had not worked on Windows. "~s --dynamic-space-size ~s --control-stack-size 16 --core ~s~a ~ --end-runtime-options --no-userinit --eval '(acl2::sbcl-restart)'~a ~a~%" prog *sbcl-dynamic-space-size* eventual-sysout-core (insert-string host-lisp-args) (insert-string toplevel-args) (user-args-string inert-args "--end-toplevel-options")))) (chmod-executable sysout-name) ;; In SBCL 0.9.3 the read-only space is too small for dumping ACL2 on x86, ;; so we have to specify :PURIFY NIL. This will unfortunately result in ;; some core file bloat, and slightly slower startup. (sb-ext:gc) (sb-ext:save-lisp-and-die sysout-core :purify #+(or x86 x86-64 ppc) nil #-(or x86 x86-64 ppc) t))) #+sbcl (defun save-acl2-in-sbcl (sysout-name &optional mode core-name) (with-warnings-suppressed (setq *saved-mode* mode) (if (probe-file "worklispext") (delete-file "worklispext")) (with-open-file (str "worklispext" :direction :output) (format str "core")) (save-acl2-in-sbcl-aux sysout-name core-name nil nil nil))) #+sbcl (defun save-exec-raw (sysout-name host-lisp-args toplevel-args inert-args) (with-warnings-suppressed (setq *acl2-default-restart-complete* nil) (save-acl2-in-sbcl-aux sysout-name sysout-name host-lisp-args toplevel-args inert-args))) #+allegro (defun save-acl2-in-allegro-aux (sysout-name dxl-name host-lisp-args inert-args) (excl:gc t) ; Suggestions are welcome for better gc call(s)! #+(and allegro-version>= (version>= 4 3)) (progn (tpl:setq-default *PACKAGE* (find-package "ACL2")) (setq EXCL:*RESTART-INIT-FUNCTION* 'acl2-default-restart) #+(and allegro-version>= (version>= 5 0)) (progn ; Allegro 5.0 and later no longer supports standalone images. Instead, one ; creates a .dxl file using dumplisp and then starts up ACL2 using the original ; Lisp executable, but with the .dxl file specified using option -I. Our ; saved_acl2 executable is then a one-line script that makes this Lisp ; invocation. Note that :checkpoint is no longer supported starting in 5.0. (let* ((eventual-sysout-dxl (if dxl-name (unix-full-pathname dxl-name "dxl") (error "An image file must be specified when building ACL2 in ~ Allegro 5.0 or later."))) (sysout-dxl (unix-full-pathname sysout-name "dxl"))) (write-acl2rc (our-pwd)) (with-open-file ; write to nsaved_acl2 (str sysout-name :direction :output) (write-exec-file str nil ; We use ~s instead of ~a below because John Cowles has told us that in Windows ; 98, the string quotes seem necessary for the first string and desirable for ; the second. The string quotes do not hurt on Unix platforms. ; We omit the trailing " -L ~s" for now from the following string, which would ; go with format arg (rc-filename save-dir), because we know of no way in ; Allegro 6.2 to avoid getting Allegro copyright information printed upon :q if ; we start up in the ACL2 read-eval-print loop. ; "~s -I ~s -L ~s ~s~%" "~s -I ~s~s ~a~%" (system::command-line-argument 0) eventual-sysout-dxl (insert-string host-lisp-args) (user-args-string inert-args))) (chmod-executable sysout-name) (excl:dumplisp :name sysout-dxl))) #-(and allegro-version>= (version>= 5 0)) (excl:dumplisp :name sysout-name :checkpoint nil)) #-(and allegro-version>= (version>= 4 3)) (progn (excl:dumplisp :name sysout-name :checkpoint t :restart-function 'acl2-default-restart))) #+allegro (defun save-acl2-in-allegro (sysout-name &optional mode dxl-name) ; Note that dxl-name should, if supplied, be a relative pathname string, not ; absolute. (setq *saved-mode* mode) (if (probe-file "worklispext") (delete-file "worklispext")) (with-open-file (str "worklispext" :direction :output) (format str "dxl")) (load "allegro-acl2-trace.lisp") ; Robert Krug's trace patch (save-acl2-in-allegro-aux sysout-name dxl-name nil nil)) #+allegro (defun save-exec-raw (sysout-name host-lisp-args inert-args) (setq *acl2-default-restart-complete* nil) (save-acl2-in-allegro-aux sysout-name sysout-name host-lisp-args inert-args)) (defun rc-filename (dir) (concatenate 'string dir ".acl2rc")) (defun write-acl2rc (dir) (let ((rc-filename (concatenate 'string dir ".acl2rc"))) (if (not (probe-file rc-filename)) (with-open-file ; write to .acl2rc (str (rc-filename dir) :direction :output) ; We call acl2-default-restart before lp so that the banner will be printed ; and (optionally) ~/acl2-init.lsp file will be loaded before entering the ACL2 ; read-eval-print loop. (format str "; enter ACL2 read-eval-print loop~%~ (ACL2::ACL2-DEFAULT-RESTART)~%~ #+ALLEGRO (EXCL::PRINT-STARTUP-INFO T)~%~ #+ALLEGRO (SETQ EXCL::*PRINT-STARTUP-MESSAGE* NIL)~%~ (ACL2::LP)~%"))))) #+clisp (defun save-acl2-in-clisp-aux (sysout-name mem-name host-lisp-args inert-args) (let ((save-dir (our-pwd)) (eventual-sysout-mem (unix-full-pathname mem-name "mem")) (sysout-mem (unix-full-pathname sysout-name "mem"))) (if (probe-file sysout-name) (delete-file sysout-name)) (if (probe-file eventual-sysout-mem) (delete-file eventual-sysout-mem)) (write-acl2rc save-dir) (with-open-file ; write to nsaved_acl2 (str sysout-name :direction :output) (write-exec-file str nil "~s -i ~s -p ACL2 -M ~s -m ~dMB -E ISO-8859-1~a ~a~%" (or (ext:getenv "LISP") "clisp") (rc-filename save-dir) eventual-sysout-mem ; Here we choose a value of 10 for the -m argument. We have found that without ; setting -m, we get a stack overflow in community book ; books/unicode/read-utf8.lisp at (verify-guards read-utf8-fast ...) when ; running on CLISP 2.34 on Linux. CLISP documentation at ; http://clisp.cons.org/clisp.html#opt-memsize says that it is "common to ; specify 10 MB" for the value of -m; since that suffices to eliminate the ; stack overflow mentioned above, we use that value. Note that we use ~dMB ; instead of ~sMB because the (our-)with-standard-io-syntax wrapper in ; write-exec-file seems to put a decimal point after the number when using ~s, ; and CLISP complains about that when starting up. 10 (insert-string host-lisp-args) (user-args-string inert-args))) (chmod-executable sysout-name) (ext:gc) (ext:saveinitmem sysout-mem :quiet nil ; We call acl2-default-restart here, even though above we take pains to call it ; in the .acl2rc file, because someone could edit that file but we still want ; the banner printed. :init-function 'acl2-default-restart))) #+clisp (defun save-acl2-in-clisp (sysout-name &optional mode mem-name) (setq *saved-mode* mode) (if (probe-file "worklispext") (delete-file "worklispext")) (with-open-file (str "worklispext" :direction :output) (format str "mem")) (save-acl2-in-clisp-aux sysout-name mem-name nil nil)) #+clisp (defun save-exec-raw (sysout-name host-lisp-args inert-args) (setq *acl2-default-restart-complete* nil) (save-acl2-in-clisp-aux sysout-name sysout-name host-lisp-args inert-args)) #+ccl (defun save-acl2-in-ccl-aux (sysout-name core-name &optional (host-lisp-args nil save-exec-p) inert-args) (let* ((ccl-program0 (or (car ccl::*command-line-argument-list*) ; Gary Byers suggestion (error "Unable to determine CCL program pathname!"))) (ccl-program (qfuncall pathname-os-to-unix ccl-program0 (get-os) *the-live-state*)) (core-name (unix-full-pathname core-name (pathname-name ccl-program)))) (when (probe-file sysout-name) ; At one point we supplied :if-exists :overwrite in the first argument to ; with-open-file below. Robert Krug reported problems with that solution in ; OpenMCL 0.14 (CCL). A safe solution seems to be simply to delete the file ; before attempting to write to it. (delete-file sysout-name)) (when (probe-file core-name) (delete-file core-name)) (with-open-file ; write to nsaved_acl2 (str sysout-name :direction :output) (write-exec-file str ; Gary Byers has pointed out (Feb. 2009) that: ; In order for REQUIRE (and many other things) to work, the lisp needs ; to know where its installation directory (the "ccl" directory) is. ; (More accurately, the "ccl" logical host has to has its logical-pathname ; translations set up to refer to that directory:) ; ; ? (truename "ccl:") ; #P"/usr/local/src/ccl-dev/" ; So we make an effort to set CCL_DEFAULT_DIRECTORY correctly so that the above ; truename will be correct. ("~a~%" (let ((default-dir (or (ccl::getenv "CCL_DEFAULT_DIRECTORY") (let ((path (our-truename "ccl:"))) (and path (qfuncall pathname-os-to-unix (namestring path) (get-os) *the-live-state*)))))) (if default-dir (format nil "export CCL_DEFAULT_DIRECTORY=~s" default-dir) ""))) ; See the section on "reading characters from files" in file acl2.lisp for an ; explanation of the -K argument below. ; It is probably important to use -e just below instead of :toplevel-function, ; at least for #+hons. Jared Davis and Sol Swords have told us that it seems ; that with :toplevel-function one gets a new "toplevel" thread at start-up, ; which "plays badly with the thread-local hash tables that make up the hons ; space". "~s -I ~s~a -K ISO-8859-1 -e ~ \"(acl2::acl2-default-restart)\"~a ~a~%" ccl-program core-name (if save-exec-p ; For an ACL2 built from sources, the saved script will include "-Z 64M"; see ; comment below. But with save-exec, no -Z option will be written. The new ; script can then be expected to invoke ACL2 with the same stack sizes as did ; the original (which had -Z 64M explicitly), unless an explicit -Z option is ; given to save-exec or globals such as ; ccl::*initial-listener-default-control-stack-size* (see community book ; books/centaur/ccl-config.lsp) are set before the save-exec call. ; Turning now to the case of building from sources, as opposed to save-exec: ; We use -Z 64M even though the default for -Z (as of mid-2013) is 2M, in order ; to get larger stacks. We have ample evidence that a larger stack would be ; useful: an ACL2 example from David Russinoff in August 2013 for which 8M was ; insufficient (defining a constant function with body '(...), a quoted list of ; length 65536; our own x86 model requiring 4M for an ACL2 proof using ; def-gl-thm; and more generally, Centaur's routine use of large stacks, ; equivalent to -Z 256M. Not surprisingly, we that performance was not hurt ; using a larger stack size, for two pairs of ACL2(h) regressions as follows. ; We ran one pair of runs on a Linux system with 32GB of RAM, and one pair of ; runs on a MacBook Pro with 8GB of RAM, all in August 2013. For each pair we ; ran with -Z 64M and also omitting -Z (equivalent to using -Z 2M). Our main ; concern was potentially larger backtraces when using (set-debugger-enable ; :bt), as during a regression. We solved that by restricting backtrace counts ; using *ccl-print-call-history-count*. "" " -Z 64M") (insert-string host-lisp-args) (user-args-string inert-args))) (chmod-executable sysout-name) (ccl::gc) (ccl:save-application core-name))) #+ccl (defun save-acl2-in-ccl (sysout-name &optional mode core-name) (setq *saved-mode* mode) (load "openmcl-acl2-trace.lisp") (save-acl2-in-ccl-aux sysout-name core-name)) #+ccl (defun save-exec-raw (sysout-name host-lisp-args inert-args) (setq *acl2-default-restart-complete* nil) (save-acl2-in-ccl-aux sysout-name sysout-name host-lisp-args inert-args)) ; Since saved-build-date-string is avoided for MCL, we avoid the following too ; (which is not applicable to MCL sessions anyhow). #-(and mcl (not ccl)) (defun save-acl2 (&optional mode other-info ; Currently do-not-save-gcl is ignored for other than GCL. It was added in ; order to assist in the building of Debian packages for ACL2 based on GCL, in ; case Camm Maguire uses compiler::link. do-not-save-gcl) #-akcl (declare (ignore do-not-save-gcl)) #-(or akcl allegro cmu sbcl clisp ccl) (declare (ignore other-info)) #+akcl (if (boundp 'si::*optimize-maximum-pages*) (setq si::*optimize-maximum-pages* nil)) ; Camm Maguire suggestion ; Consider adding something like ; (ccl::save-application "acl2-image" :size (expt 2 24)) ; for the Mac. (load-acl2) (setq *saved-build-date-lst* ; The call of eval below should avoid a warning in cmucl version 18d. Note ; that saved-build-date-string is defined in interface-raw.lisp. (list (eval '(saved-build-date-string)))) (eval mode) (princ " ****************************************************************************** Initialization complete, beginning the check and save. ****************************************************************************** ") (cond ((or (not (probe-file *acl2-status-file*)) (with-open-file (str *acl2-status-file* :direction :input) (not (eq (read str nil) :initialized)))) (error "Initialization has failed. Try INITIALIZE-ACL2 again."))) #+akcl (save-acl2-in-akcl "nsaved_acl2" other-info mode do-not-save-gcl) #+lucid (save-acl2-in-lucid "nsaved_acl2" mode) #+lispworks (save-acl2-in-lispworks "nsaved_acl2" mode other-info) #+allegro (save-acl2-in-allegro "nsaved_acl2" mode other-info) #+cmu (save-acl2-in-cmulisp "nsaved_acl2" mode other-info) #+sbcl (save-acl2-in-sbcl "nsaved_acl2" mode other-info) #+clisp (save-acl2-in-clisp "nsaved_acl2" mode other-info) #+ccl (save-acl2-in-ccl "nsaved_acl2" mode other-info) #-(or akcl lucid lispworks allegro clisp ccl cmu sbcl) (error "We do not know how to save ACL2 in this Common Lisp.") (format t "Saving of ACL2 is complete.~%")) (defun generate-acl2-proclaims () ; See the section "PROCLAIMING" in acl2-fns.lisp. (let ((filename "acl2-proclaims.lisp")) (cond (*do-proclaims* (format t "Beginning load-acl2 and initialize-acl2 on behalf of ~ generate-acl2-proclaims.~%") (load-acl2 t) ; Use funcall to avoid compiler warning in (at least) CCL. (qfuncall initialize-acl2 'include-book nil nil t) (proclaim-files filename)) (t (if (probe-file filename) (delete-file filename)) (with-open-file (str filename :direction :output) (format str "(in-package \"ACL2\")~%~%") (format str "; No proclaims are generated here for this host Lisp.~%")) nil)))) ; The following avoids core being dumped in certain circumstances ; resulting from very hard errors. #+akcl (si::catch-fatal 1) acl2-sources/acl2.lisp0000664002132200015000000030764612222115527014304 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; Acknowledgments: Bob Boyer contributed crucially to the design and ; implementation of early versions of ACL2. Many others, largely at CLI, have ; also contributed to the design of certain features. We especially thank ; Bishop Brock and John Cowles for their contributions. See the documentation ; topic ACKNOWLEDGMENTS for more information. ; This research was supported in part at Computational Logic, Inc. by the ; Defense Advanced Research Projects Agency (DARPA), ARPA Orders #6082, 9151 ; and 7406 and by the Office of Naval Research, contracts numbers ; N00014-88-C-0454, N00014-91-C-0130, and N00014-94-C-0193. The views and ; conclusions contained in this document are those of the author(s) and should ; not be interpreted as representing the official policies, either expressed or ; implied, of Computational Logic, Inc., Office of Naval Research, DARPA or the ; U.S. Government. ; This file, acl2.lisp, (a) builds the packages for the ACL2 system, (b) ; defines the functions for loading and compiling ACL2 and for running code ; verified using ACL2, and (c) makes a couple of checks concerning minor, ; non-CLTL, assumptions that we make about Common Lisps in which ACL2 is to ; run. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CLTL1/CLTL2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; For the most part, we program in a subset both of CLTL1 and CLTL2. ; However, there are a few places, notably the name of the main ; package for Lisp symbols, where this is impossible. So we use the ; read conditional #+CLTL2. If one wishes to run ACL2 under CLTL2, ; execute the following form before commencing compiling or loading: ; (push :CLTL2 *features*) ; For example, for Allegro and lispworks (respectively) we have the following. #+(or ansi-cl draft-ansi-cl-2 lispworks clisp ccl) (push :CLTL2 *features*) ; We use IN-PACKAGE in a way that is consistent with both CLTL1 and ; CLTL2, namely as a macro (i.e., whose argument is not evaluated) for ; switching *package* to an existing package, value ignored. (in-package #-CLTL2 "USER" #+CLTL2 "COMMON-LISP-USER") ; Avoid warning, at least in Allegro CL, for use of this variable below. Note ; that it is set to nil using GNUmakefile when ACL2_COMPILER_DISABLED is set. (defvar *acl2-compiler-enabled*) ; Changes Made in Support of CMU Lisp ; (0) (The following issue with Cmulisp no longer seems to be true, at least ; as of Version 19e on Linux.) ; Cmulisp doesn't support EVAL-WHEN. This meant that in the #+cmu case ; I had to put down special code to try to do what other lisps do. ; Generally, this involved just not checking for certain errors (compiling ; files that weren't supposed to be compiled) in #+cmu that were checked ; in other lisps. In one case, namely the initialization of ; current-acl2-world, it involved a little more. ; (1) cmulisp switches from *standard-input* to *terminal-io* when the input ; stream reaches eof; our other lisps all exit to the operating system. ; This means all the batch jobs we submit via make have to be arranged to ; exit from cmulisp before eof. This required changing the top-level ; makefile and the makefiles of all the community books. I generally put a ; `:q #+cmu (lisp::quit)' at the end of each workxxx. ; These could be replaced simply by `:q (acl2::exit-lisp)'. ; (2) Cmulisp checks type assertions at runtime. I found two of our assertions ; violated by actual use. In fmt-char we mistakenly claimed the string's ; length was less than 101. This was a typo -- elsewhere in the same ; function we claimed it was just a fixnum -- apparently caused by ; copying a type-declaration and failing to edit it thoroughly. (Another ; variable was correctly limited to 101.) ; In maximal-elements1, used in the selection of induction candidates, ; we claimed (by using int=) that the scores for an induction candidate ; are integers when in fact they are rationals. ; (3) Evidently, all functions in cmulisp pass the compiled-function-p test. ; If you defun foo and immediately get its symbol-function you get ; an object like #. If you ask whether ; this object is a compiled-function-p you get t. If you compile ; foo the symbol-function changes to an object like #, which also passes the test. ; The fact that everything in a symbol-function field looks like a compiled ; function killed us in an interesting way. Most locally, it made ; compile-uncompiled-*1*-defuns write out an empty file of functions to ; compile, because everything looked compiled already. But where that ; really got us was that we use that function to create TMP1.lisp during ; the bootstrapping. TMP1.lisp, recall, contains the mechanically ; generated executable counterparts of logic mode functions defined in ; axioms.lisp. By not generating these we built an image in which the ; relevant functions were undefined. Because of the rugged way we invoke ; them, catching errors and producing a HIDE term if we can't eval them, ; we survived the certification of many books before we finally got to a ; proof that couldn't be done without running one of those functions. ; The proof, in the bdd community books, required evaling (nth -1 nil), which ; according to the axioms is nil but which we couldn't deduce because ; ACL2_*1*_COMMON-LISP::NTH was undefined. ; My fix was to define and use compiled-function-p! instead of the standard ; compiled-function-p. The new function has a #+/-cmu split in it. In the ; cmu case I ask ; (not (eq (type-of (symbol-function fn)) 'eval:interpreted-function)) ; So I say fn is compiled if its symbol-function is not an object like ; #. ; (4) CMU Lisp does not allow us to "undefine" a macro-function. That is, ; one is not permitted to store a nil into the macro-function ; field because nil is not a function. We do this when we ; implement undo. We handle it by storing a dummy function ; instead, and change the notion of when a symbol is virgin to ; recognize the case that its symbol-function or macro-function is ; the dummy. ; (5) We made a few fixes and cleanups in order to avoid compiler warnings. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; SAFETY AND PROCLAIMING ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (proclaim `(optimize #+cltl2 (compilation-speed 0) ; The user is welcome to modify this proclaim form. Warning: Keep it in sync ; with the settings in compile-acl2 under #+sbcl. ; The following may allow more tail recursion elimination (from "Lisp ; Knowledgebase" at lispworks.com); might consider for Allegro CL too. #+(or lispworks ccl) (debug 0) #+cmu (extensions:inhibit-warnings 3) #+sbcl (sb-ext:inhibit-warnings 3) (speed 3) ; Consider replacing cmu on the next line with (or cmu sbcl). The SBCL manual ; says the following, but a quick test with (or cmu sbcl) yielded no smaller ; .core file size and no quicker (mini-proveall). ; The value of space mostly influences the compiler's decision whether to ; inline operations, which tend to increase the size of programs. Use the ; value 0 with caution, since it can cause the compiler to inline operations ; so indiscriminately that the net effect is to slow the program by causing ; cache misses or even swapping. (space #+cmu 1 #-cmu 0) ; WARNING: Do not proclaim (cl-user::fixnum-safety 0) for LispWorks. Any ; fixnum-safety less than 3 expects all integers to be fixnums! (safety ; Consider using (safety 3) if there is a problem with LispWorks. It enabled ; us to see a stack overflow involving collect-assumptions in the proof of ; bitn-lam0 from community book books/rtl/rel2/support/lop3.lisp. The safety ; setting of 3 helped in this example, because, when the safety is set to 0, ; the stack is not always automatically extended upon overflow (despite setting ; system::*stack-overflow-behaviour* to nil in acl2-init.lisp). ; Safety 1 for CCL has avoided the kernel debugger, e.g. for compiled calls ; of car on a non-nil atom. The following results for CCL show why we have ; decided to keep the safety at 0 by default and why safety 3 is not too bad. ; ; Safety 0: ; 12955.537u 542.877s 3:46:24.68 99.3% 0+0k 0+0io 0pf+0w ; ; Safety 1: ; 15343.578u 562.207s 4:27:03.67 99.2% 0+0k 0+0io 0pf+0w ; Try safety 2 or 3 to find violations with Allegro CL like the car of a ; non-nil atom. (Note: safety 3 has failed in GCL due to a stack overflow.) ; Here are some numbers with Allegro CL, 8 processors with make -j 8: ; Safety 0: ; 11262.699u 291.730s 38:23.72 501.5% 0+0k 0+0io 16pf+0w ; Safety 2: ; 15588.206u 285.249s 54:19.72 486.9% 0+0k 0+0io 0pf+0w ; Safety 3: ; 16450.508u 284.473s 57:46.03 482.8% 0+0k 0+0io 0pf+0w ; Here are GCL numbers, again with make -j 8 (and using "fast builds"). ; Safety 0: ; 10013.573u 566.983s 33:33.80 525.4% 0+0k 0+0io 0pf+0w ; Safety 2: ; [Note: community book ; books/clause-processors/SULFA/books/sat-tests/test-incremental.lisp ; ran out of space, saving perhaps a minute] ; 15637.669u 511.811s 52:02.78 517.1% 0+0k 0+0io 0pf+0w ,(let ((our-safety #-CLTL2 (if (boundp 'user::*acl2-safety*) (symbol-value 'user::*acl2-safety*) nil) #+CLTL2 (if (boundp 'common-lisp-user::*acl2-safety*) (symbol-value 'common-lisp-user::*acl2-safety*) nil))) (if our-safety (progn (format t "Note: Setting SAFETY to ~s." our-safety) our-safety) 0)) ))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; FILES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This is the file acl2.lisp, the first source file for ACL2. The names of the ; other ACL2 source files are listed under *acl2-files*. ; All of ACL2 is written in Common Lisp and we expect that ACL2 will run in any ; Common Lisp except for those Common Lisps which fail the tests we execute ; upon loading this file, acl2.lisp. With the exception of this and other ; initialiation files, files *-raw.lisp, and those constructs after the ; #-acl2-loop-only readtime conditional, ACL2 is written in the applicative ; Common Lisp for which ACL2 is a verification system. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; READING CHARACTERS FROM FILES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Files may be viewed as sequences of bytes. But Lisp can interpret these byte ; sequences as sequences of characters, depending on so-called character ; encodings. ; For example, through about March 2012 the default character encoding in CCL ; was ISO-8859-1, sometimes known as LATIN-1. When this changed to UTF-8 a ; failure was reported when attempting to certify community book ; books/workshops/2006/cowles-gamboa-euclid/Euclid/fld-u-poly/coe-fld.lisp, ; apparently because of the following line, where the next-to-last character is ; actually an accented `o' sometimes known as ; #\LATIN_SMALL_LETTER_O_WITH_ACUTE, having code 243. (The CLISP build fails ; if we use that character here, because at this point we have not yet made the ; adjustments to the character encoding that are done below!) ;;; Inverso de la primera operacion ; The accented `o' above is encoded as a single byte in LATIN-1 but as two ; bytes in UTF-8. ; Jared Davis then suggested that we explicitly specify ISO-8859-1, which might ; be slightly more efficient for reading files. Note that GCL (non-Ansi, circa ; 2010 but probably later) only supports ISO-8859-1 as far as we can tell. We ; follow Jared's suggestion below. It seems that character encoding for ; reading from files is controlled differently from character encoding for ACL2 ; reading from the terminal. Jared also suggested setting the terminal ; encoding to ISO-8859-1 as well, and showed us how to do that in CCL. We have ; been unable to figure out how to do that in some other host Lisps, but since ; files are typically shared between users and (of course) ACL2 reading from ; the terminal is not, the encoding for the terminal seems less important than ; for files. ; Even for files, we assert there is no soundness issue, in the sense that for ; maximum trust we expect each user to certify all books from scratch in a ; single environment. But in practice, users share books (in particular, ; via the community books); so it is desirable to enforce uniform character ; encodings for files. ; The use of latin-1 could in principle cause problems for those whose default ; Emacs buffer encoding is utf-8. That's in fact the case for us at UT CS, but ; not on one of our Mac laptops as of this writing (April 2012), probably ; because environment variable LANG is en_US.UTF-8 at UT CS. But ACL2 users ; don't often save Lisp files with nonstandard characters. If they have ; occasion to do so, they can evaluate ; (setq save-buffer-coding-system 'iso-8859-1) ; in Emacs buffers before saving into files. This will happen automatically ; for users who load file emacs/emacs-acl2.el into their emacs sessions. ; At any rate, it seems best to standardize file encodings and leave it to ; individuals to cope with editing issues. ; Without further ado, we set the default encoding for files. Below, we make ; some attempt to do so for the terminal. We wrap all this into a function, ; because we have found that for sbcl, upon restart we lose the effect of the ; assignment below. It seems safest then to do these same assignments at ; startup; see the call of the following function in acl2-default-restart. (defun acl2-set-character-encoding () ; We set the character encoding (see discussion above). #+allegro ; Alternatively, we could set the locale on the command line using -locale C: ; see http://www.franz.com/support/documentation/6.1/doc/iacl.htm#locales-1 ; Note that (setq *default-external-format* :ISO-8859-1) is obsolete: ; http://www.franz.com/support/documentation/6.1/doc/iacl.htm#older-ef-compatibility-2 (setq *locale* (find-locale "C")) #+ccl (setq ccl:*default-file-character-encoding* :iso-8859-1) ; #+clisp ; Set using -E ISO-8859-1 command-line option from save-exec. ; Note that the setting below for custom:*default-file-encoding* works for ; files, but we were unable to do the analogous thing successfully for ; custom:*terminal-encoding*, even when restricting that assignment to ; (interactive-stream-p *terminal-io*). #+cmu (setq *default-external-format* :iso-8859-1) ; #+gcl -- already iso-8859-1, it seems, and nothing we can do to change ; the encoding anyhow #+lispworks ; This the default on our linux system, at least on both 32- and 64-bit, ; version 6.1.0. But it doesn't seem to suffice; see ; our-lispworks-file-encoding below. (setq stream::*default-external-format* '(:LATIN-1 :EOL-STYLE :LF)) #+sbcl (setq sb-impl::*default-external-format* :iso-8859-1) ; ;;; ; We have made only limited attempts to set the character encoding at the ; terminal, as discussed above. ; ;;; ; #+allegro ; Handled by *locale* setting above. Formerly the following was later in ; this file; now we include it only as a comment. ; #+(and allegro allegro-version>= (version>= 6 0)) ; (setf (stream-external-format *terminal-io*) ; (excl::find-external-format ; #+unix :latin1-base ; #-unix :latin1)) ; #+ccl ; For terminal, set using -K ISO-8859-1 command-line option from save-exec. ; #+cmucl -- Probably no setting is necessary. ; #+clisp -- Set using command-line option (see above). ; #+gcl -- There seems to be nothing we can do. ; #+lispworks -- No support seems to be available. ; #+sbcl ; We found that "unsetenv LANG" results in (stream-external-format ; *terminal-io*) being ascii at the terminal instead of utf-8; or, just start ; up sbcl like this to get ascii: ; LANG=C ; XTERM_LOCALE=C ; sbcl ; But we don't know how to get latin-1. We even tried each of these, but got ; ascii: ; LANG=en_US.LATIN-1 ; XTERM_LOCALE=en_US.LATIN-1 ; LANG=en_US.ISO-8859-1 ; XTERM_LOCALE=en_US.ISO-8859-1 ) ; Here, we invoke the function defined above, so that a suitable character ; encoding is used during the build, not only when starting up a built image ; (which is why we call acl2-set-character-encoding in acl2-default-restart). (acl2-set-character-encoding) ; We also do the following for clisp, since we set the encoding on the command ; line (see comment above) but we want to be able to read our own source files ; during the build. See the comment in (defdoc character-encoding ...) in ; axioms.lisp. #+clisp (setq custom:*default-file-encoding* (ext:make-encoding :charset 'charset:iso-8859-1 ; The following is consistent with what we used to do in acl2-init.lisp; see ; the commented-out call there that sets custom:*default-file-encoding*. ; Unfortunately, according to http://www.clisp.org/impnotes/clhs-newline.html, ; this doesn't treat CR/LF as two characters when reading files -- for example, ; the file "foo.lisp" defined in a comment below for dealing with LispWorks ; provides (len *c*) = 3, not 4. :line-terminator :unix)) ; We seem to need to do more for LispWorks. To see why, create a book ; "foo.lisp" as follows. ; ; (in-package "ACL2") ; (defconst *c* ; "x ; y") ; ; Next, if you have arranged in emacs to set save-buffer-coding-system ; 'iso-8859-1, as in emacs/emacs-acl2.el, turn that off and bring foo.lisp into ; a buffer; then add control-M at the end of every line; and finally, save the ; buffer, which will save the control-M at the end of every line and, in ; particular, in the middle of the string. (And now restore your handling of ; save-buffer-coding-system.) In a Lispworks image of ACL2, execute (ld ; "foo.lisp"), and you can evaluate (length *c*) to get 3, where 4 is expected ; because of the control-M. We adopt here a solution found on the web at: ; http://www.lispworks.com/documentation/lw60/LW/html/lw-470.htm #+lispworks (defun our-file-encoding (pathname ef-spec buffer length) (system:merge-ef-specs ef-spec '(:LATIN-1 :EOL-STYLE :LF))) #+lispworks (setq system::*file-encoding-detection-algorithm* '(our-file-encoding)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; LISP BUGS AND QUIRKS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; See acl2-fns.lisp for a fix to user-homedir-pathname for some versions of ; GCL. ; To use ACL2 under LispWorks 3.2.0, execute the following to work around a ; bug. ; #+lispworks ; (setf (svref ccl::*funny-symbol-char-table* (char-code #\.)) t) ; Next, we correct *features* for Windows. #+(and (or winnt win32 windows) (not mswindows)) (setq *features* (cons :mswindows *features*)) #+(and (or mswindows winnt) unix) (setq *features* (delete :unix *features* :test (function (lambda (x y) (equal (symbol-name x) (symbol-name y)))))) ; Turn off automatic declaration of special variables, in particular since we ; do not want state declared special; see the comment above ; (eval '(setq state *the-live-state*)) in load-acl2. #+cmu (setq ext:*top-level-auto-declare* nil) ; Turn off compiler verbosity going to error stream, since even >& does not ; seem to redirect that stream to a file. #+(or cmu sbcl) (setq *compile-verbose* nil) #+(or cmu sbcl) (setq *compile-print* nil) ; Turn off gc verbosity (same reason as above). #+cmu (setq ext:*gc-verbose* nil) #+ccl (when (fboundp 'ccl::gc-verbose) ; not in OpenMCL 1.0 (CCL) (apply 'ccl::gc-verbose nil nil)) ; See later in this file for with-warnings-suppressed (after we introduce and ; enter the ACL2 package). ; Avoid saving source file information, which could cause some slowdown and ; isn't typically exploited by ACL2 users. #+ccl (setq ccl::*record-source-file* nil) ; Camm Maguire has suggested, on 9/22/2013, the following forms, which allowed ; him to complete an ACL2 regresssion using 2.6.10pre. #+gcl (progn (si::allocate 'contiguous 15000 t) (si::allocate-sgc 'contiguous 15000 100000 10)) ; The following avoids errors from extra right parentheses, but we leave it ; commented out since it doesn't seem important enough to merit messing around ; at this low level, and for just one Lisp. ; #+ccl ; (setq ccl::*ignore-extra-close-parenthesis* t) ; We have tried to build under ECL (Embeddable Common-Lisp), and with some ; modifications, we made progress -- except there appears (as of Sept. 2011) to ; be no good way for us to save an executable image. Specifically, it appears ; that c:build-program not will suffice for saving state (properties etc.) -- ; it's just for saving specified .o files. (This impression seems to be ; confirmed at http://stackoverflow.com/questions/7686246/saving-lisp-state .) ; Here we document steps to take towards possibly porting to ECL in the future. ; If state-global-let* expansion causes an error due to excessive code blow-up, ; then consider replacing its definition by placing the following after ; state-free-global-let*. HOWEVER, first think about whether this is right; we ; haven't thought through the effect of mixing a stack of let*-bindings of ; state global variables with the acl2-unwind-protect mechanism. Also, ; comments are omitted here; be sure to restore them. ;;; (defmacro state-global-let*-logical (bindings body) ;;; (declare (xargs :guard (and (state-global-let*-bindings-p bindings) ;;; (no-duplicatesp-equal (strip-cars bindings))))) ;;; ;;; `(let ((state-global-let*-cleanup-lst ;;; (list ,@(state-global-let*-get-globals bindings)))) ;;; ,@(and (null bindings) ;;; '((declare (ignore state-global-let*-cleanup-lst)))) ;;; (acl2-unwind-protect ;;; "state-global-let*" ;;; (pprogn ,@(state-global-let*-put-globals bindings) ;;; (check-vars-not-free (state-global-let*-cleanup-lst) ,body)) ;;; (pprogn ;;; ,@(state-global-let*-cleanup bindings 0) ;;; state) ;;; (pprogn ;;; ,@(state-global-let*-cleanup bindings 0) ;;; state)))) ;;; ;;; #-acl2-loop-only ;;; (defmacro enforce-live-state-p (form) ;;; ;;; ; Note that STATE is intended to be lexically bound at the point where this ;;; ; macro is called. ;;; ;;; `(progn (when (not (live-state-p state)) ; (er hard! ...) ;;; (let ((*hard-error-returns-nilp* nil)) ;;; (illegal 'enforce-live-state-p ;;; "The state was expected to be live in the context of ~ ;;; an evaluation of the form:~|~x0" ;;; (list (cons #\0 ',form))))) ;;; ,form)) ;;; ;;; (defmacro state-global-let* (bindings body) ;;; (cond #-acl2-loop-only ;;; ((and (symbol-doublet-listp bindings) ;;; (not (assoc-eq 'acl2-raw-mode-p bindings))) ;;; ;;; ; The test above guarantees that we merely have bindings of state globals. A ;;; ; triple requires cleanup using a setter function. Also we avoid giving this ;;; ; simple treatment to 'acl2-raw-mode-p because the semantics of ;;; ; state-global-let* are to call f-put-global, which has side effects in the ;;; ; case of 'acl2-raw-mode-p. ;;; ;;; `(enforce-live-state-p ;;; (warn-about-parallelism-hazard ;;; '(state-global-let* ,bindings ,body) ;;; (state-free-global-let* ,bindings ,body)))) ;;; (t `(state-global-let*-logical ,bindings ,body)))) ; Also, place the following forms in file acl2-fns.lisp, just above qfuncall ; (but there may be better ways to do this). ;;; ; The following is in acl2.lisp, but seems to be needed here as well. ;;; #+ecl ;;; (ext:package-lock "COMMON-LISP" nil) ;;; ;;; Similarly in acl2.lisp, just before handling of package-lock on ;;; "COMMON-LISP" for clisp: ;;; ;;; #+ecl ;;; (ext:package-lock "COMMON-LISP" nil) ; Finally, consider these additional notes. ;;; We need (require "cmp") if we're to use c:build-program. ;;; Special-form-or-op-p: treat ecl like sbcl. ;;; System-call: treat ecl like akcl (actually replace #+akcl by #+(or akcl ;;; ecl)). ;;; Initialize-state-globals: treat ecl just like lispworks. ;;; Where we have the binding (compiler:*suppress-compiler-notes* t) for akcl, ;;; perhaps include the binding (*compile-verbose* t) for ecl. ;;; Modify exit-lisp to treat ecl like akcl, except using ext::quit instead of ;;; lisp::bye. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PACKAGES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We never intend that this file should be compiled, and hence we do ; not need to obey the CLTL1 strictures about putting all IN-PACKAGE ; forms near the front. (let ((lisp-pkg (find-package "LISP"))) (if lisp-pkg (let ((cl-pkg (find-package "COMMON-LISP"))) (cond ((and cl-pkg (eq cl-pkg lisp-pkg)) ; Then all is well. ) #+(or cmu (and gcl cltl2)) ; Starting with CMUCL 19a, lisp-pkg and cl-pkg are no longer the same. We view ; CMUCL as CLTL2; see (push :CLTL2 *features*) above, noting that :ANSI-CL is ; in *features*. So in this case, we simply ignore lisp-pkg. Probably we can ; do the same for most other lisps, and in fact we do so for ANSI GCL as well. ; However, non-ANSI GCL is handled differently, since the "LISP" package is ; populated there but the "COMMON-LISP" appears to be largely irrelevant. (t nil) #-(or cmu (and gcl cltl2)) (t (when cl-pkg ; but by the test above, cl-pkg is not lisp-pkg #-gcl ; not non-ANSI GCL ; Perhaps we can just add the present lisp to the case for #+(or cmu (and gcl ; cltl2)), above. (error "This Lisp is unsuitable for ACL2, because the ~ COMMON-LISP~% package is defined but is not the LISP ~ package.") #+gcl ; non-ANSI GCL ; Early versions of GCL 2.4.3/2.5.0 had a "COMMON-LISP" package that was ; initially populated only with LISP::T and LISP::NIL. It seems safe to move ; any GCL COMMON-LISP package out of the way before we make "COMMON-LISP" a ; nickname for "LISP". (rename-package "COMMON-LISP" "COMMON-LISP-renamed")) (let ((old-name (package-name lisp-pkg)) ; reuse old name, nicknames (old-nicknames (package-nicknames lisp-pkg))) (rename-package "LISP" old-name (cons "COMMON-LISP" old-nicknames)))))))) (eval-when #-cltl2 (compile) #+cltl2 (:compile-toplevel) (error "The file acl2.lisp should never be compiled.")) (dolist (p (list-all-packages)) (cond ((equal 4 (string< "ACL2" (package-name p))) (format t "~%~%Warning: There is already a package with the ~ name ~a. ~%The ACL2 implementation depends upon ~ having complete knowledge of ~%and control over any ~ packge whose name begins with the ~%four letters ``ACL2'', ~ so ACL2 may not work in this Lisp." (package-name p)) (cond ((package-use-list p) (format t "~%~%Warning: The package with name ~a ~ USES the packages in the list ~a. ACL2 will not work ~ in state of affairs." (package-name p) (package-use-list p))))))) (or (find-package "ACL2") (make-package "ACL2" :use nil)) ; The definition of defconst appears just below because we use it ; early in axioms.lisp. But first, we define the constant ; *the-live-state* because it is used below in the definition of ; defconst and cmulisp warns if we use it before it has been defined. ; And, in order to define *the-live-state* we need the ACL2_INVISIBLE ; package, which we define here. This package is used for a few odd ; objects that are to be hidden from the ACL2 user. Symbols in this ; package having names that start with "HONS" are reserved for the ; hons/memoization implementation. (let ((inv "ACL2_INVISIBLE")) (or (find-package inv) (make-package inv :use nil))) ; LispWorks has a package named "DEF", and that name conflicts with an ACL2 ; package of that name introduced in community book books/coi/. We deal with ; that issue here. Thanks to Martin Simmons for providing this code in place ; of the original code, which instead invoked (rename-package "DEF" ; "DEF-FROM-LW-RENAMED"). #+lispworks (when (find-package "DEF") (unless (equal (package-name "DEF") "DSPEC") (error "Expected LispWorks DEF package to be called DSPEC")) (rename-package "DSPEC" "DSPEC" (remove "DEF" (package-nicknames "DSPEC") :test 'equal))) ; The value of the constant *the-live-state* is actually just a ; symbol, but that symbol is the unique representative of the one ; single active, global, real-time state of ACL2, which is represented ; both in real-time (e.g., characters not yet typed) and also rather ; efficiently, using typical von Neumann storage techniques. ; Functions that wish to access the global state must have received ; *the-live-state* as an arg. Functions that modify this global state ; must receive it as an arg and return it. (defconstant acl2::*the-live-state* 'acl2_invisible::|The Live State Itself|) ; (Defconst *var* term) is essentially just (defparameter *var* term). But ; things are made complicated by the desire not to evaluate term unnecessarily. ; Suppose (defconst *var* term) appears in a certified book, say "book.lisp". ; Then when the events in "book.lisp" are processed, a CLTL-COMMAND is executed ; which causes (defconst *var* term) to be evaluated in the underlying raw ; lisp, assigning a value to *var* in Lisp. But now suppose that the compiled ; file for another book, say "book2.o", is loaded on behalf of include-book. ; If defconst were just defparameter, term would be evaluated again (yielding a ; presumably EQUAL value), which is an unfortunate waste of computation. ; We avoid this in the code below by saving, on the raw Lisp property list of ; *var*, under the key 'REDUNDANT-RAW-LISP-DISCRIMINATOR, a triple, (DEFCONST ; term . val), giving the term we evaluated to produce the setting of the var ; and the value, val, produced. When a defconst (defconst *var* term) is ; evaluated, we ask whether *var* has a value. If not, we just proceed ; normally. But if it has a value, we insist that the discriminator be present ; and appropriate or else we cause a hard error. By appropriate in this case ; we mean that it be a DEFCONST such that the value produced last time is EQ to ; the current setting of *var*, and moreover, either the old and new DEFCONST ; have the same (EQUAL) term to evaluate or else the new value is EQUAL to the ; old. The EQ check is meant to provide some safety if the user has manually ; set *var* in raw lisp, as with setq, since the last defconst to it. ; We anticipate that redundant-raw-lisp-discriminator may be used by the ; support functions for other events, e.g., defstobj. So the value of that ; property is not necessarily (DEFCONST term . val) but may depend on the kind ; of event that stored it. The reason we put the discriminator on the raw lisp ; property list of *var* rather than looking in the world of *the-live-state*, ; where we could in principle find an event-landmark, is that we execute many ; defconsts in axioms.lisp, before the world processing functions, like ; getprop, are defined and so the defmacro below must expand to code that can ; be executed in a partially booted ACL2. (defvar acl2::*compiling-certified-file* nil) (defun acl2::defconst-redeclare-error (name) (let ((stk (symbol-value 'acl2::*load-compiled-stack*))) (cond (stk (error "Illegal attempt to redeclare the constant ~s.~%~ The problem appears to be that you are including a book,~%~ ~2T~a,~%~ that attempts to give a definition of this constant that~%~ is incompatible with its existing definition. The ~%~ discrepancy is being discovered while loading that book's~%~ compiled (or expansion) file~:[, as the last such load for~%~ the following nested sequence of included books (outermost~%~ to innermost):~%~{ ~a~%~}~;.~]" name (caar stk) (null (cdr stk)) (reverse (loop for x in stk collect (car x))))) (t (error "Illegal attempt to redeclare the constant ~s." name))))) (defparameter acl2::*safe-mode-verified-p* ; This global may be bound to t when we are evaluating a form that we know will ; not lead to an ill-guarded call in raw Lisp (say, because the form was ; previously evaluated by ACL2 in safe-mode). See also the comment in ; ec-call1-raw. nil) (defmacro acl2::defconst (name term &rest rst) (declare (ignore rst)) ; e.g., documentation (let ((disc (gensym))) `(defparameter ,name (let ((acl2::*safe-mode-verified-p* t)) (cond ((boundp ',name) (cond (acl2::*compiling-certified-file* ; We avoid the potentially expensive redundancy check done below, which is ; legitimate since we are simply loading a compiled file at the end of ; certify-book. To see how important this optimization can be, try removing it ; before certifying the following book (thanks to Sol Swords for this ; example). ; (in-package "ACL2") ; (defun tree (n) ; (if (zp n) ; nil ; (let ((sub (tree (1- n)))) ; (cons sub sub)))) ; (defmacro deftree (name n) ; `(defconst ,name ',(tree n))) ; (deftree *big* 35) (symbol-value ',name)) (t ; Even though ',name is bound, we refer to its value with ; (symbol-value ',name) rather than simply an in-line ,name, to avoid ; compiler warnings. (let ((,disc (get ',name 'acl2::redundant-raw-lisp-discriminator))) (cond ((and (consp ,disc) (eq (car ,disc) 'acl2::defconst)) (assert (consp (cdr ,disc))) (cond ((and (eq (cdr (cdr ,disc)) (symbol-value ',name)) (or (equal (car (cdr ,disc)) ',term) (equal (cdr (cdr ,disc)) ,term))) (symbol-value ',name)) (t (acl2::defconst-redeclare-error ',name)))) ((acl2::raw-mode-p acl2::*the-live-state*) ; In this case we allow redeclaration of the constant; this is, after all, raw ; mode, where there are no guarantees. ,term) (t (acl2::defconst-redeclare-error ',name))))))) ; If ',name is not bound, we must evaluate ,term. Note that we do so ; outside of all local bindings, so as not to disturb variables in ; term. (There should be none -- this is supposed to be a constant, ; but you never know!) We may want to enforce that this code is only executed ; during the boot-strap; see the Essay on Guard Checking. (t (let* ((val ,term) (d (list* 'acl2::defconst ',term val))) (setf (get ',name 'acl2::redundant-raw-lisp-discriminator) d) (cdr (cdr d))))))))) ; We now get our imports for package ACL2, putting them into the ; variable acl2::*common-lisp-symbols-from-main-lisp-package*. ; We use different variables for common-lisp-symbols-from-main-lisp-package*, ; *acl2-version*, and *acl2-files*, in order to avoid compiler warnings for ; redefined variables. Actually, *acl2-version* no longer exists starting with ; Version_2.9.1, but we keep the name below nonetheless. (defvar acl2::*copy-of-common-lisp-symbols-from-main-lisp-package*) (defvar acl2::*copy-of-common-lisp-specials-and-constants*) (defvar acl2::*copy-of-acl2-version*) (defconstant acl2::*acl2-files* ; The order of these files determines compilation order. ; Note regarding backups at UT CS: ; Even though it's convenient to refer to our UT CS development directory as ; /projects/acl2/devel/, we'll need to get backups from ; /v/filer4b/v11q002/acl2space/acl2/.snapshot/*/devel, not from ; /projects/acl2/.snapshot/*/devel. The latter is just a soft link to ; /projects/acl2/devel, i.e., to /v/filer4b/v11q002/acl2space/acl2/devel. '( #+acl2-par "multi-threading-raw" #+hons "serialize-raw" "axioms" "memoize" ; but only get special under-the-hood treatment with #+hons "hons" ; but only get special under-the-hood treatment with #+hons "serialize" ; but only get special under-the-hood treatment with #+hons "basis" "parallel" ; but only get special under-the-hood treatment with #+acl2-par #+acl2-par "futures-raw" #+acl2-par "parallel-raw" #+hons "hons-raw" #+hons "memoize-raw" "translate" "type-set-a" "linear-a" "type-set-b" "linear-b" "non-linear" "tau" "rewrite" "simplify" "bdd" "other-processes" "induct" "history-management" "prove" "defuns" "proof-checker-pkg" "proof-checker-a" "defthm" "other-events" "ld" "proof-checker-b" "tutorial" "interface-raw" "defpkgs" "boot-strap-pass-2" ; at the end so that it is compiled last ) "*acl2-files* is the list of all the files necessary to build ACL2 from scratch.") ; CLISP version 2.30 (along with perhaps other versions) locks the LISP ; package. That causes problems when we try to read the second form in ; axioms.lisp, which defines ; acl2::*common-lisp-symbols-from-main-lisp-package*, when CLISP tries to read ; some of the symbols in that list (e.g., CALL-METHOD) into the COMMON-LISP ; package. (Specifically, INTERN breaks.) We use eval below to avoid any ; chance (we hope) of getting an "undefined function" warning with earlier ; CLISP versions. #+clisp (if (fboundp 'package-lock) (eval '(setf (package-lock "LISP") nil))) ; CLISP version 2.33 defines the symbol custom::*suppress-check-redefinition*, ; but version 2.30 does not. We temporarily unlock that package if necessary ; in order to define this variable. While we are at it we make the variable ; special, in order to avoid potential compiler warnings in 2.30 when we bind ; it but never use it. #+clisp (if (not (find-symbol "*SUPPRESS-CHECK-REDEFINITION*" "CUSTOM")) (if (fboundp 'package-lock) (let ((old-lock (package-lock "CUSTOM"))) (eval '(setf (package-lock "CUSTOM") nil)) (let ((sym (intern "*SUPPRESS-CHECK-REDEFINITION*" "CUSTOM"))) (eval `(defvar ,sym nil))) (eval `(setf (package-lock "CUSTOM") ',old-lock))) (let ((sym (intern "*SUPPRESS-CHECK-REDEFINITION*" "CUSTOM"))) (eval `(defvar ,sym nil))))) (with-open-file (fl "axioms.lisp" :direction :input) ; Get into the main lisp package in order to read in ; *common-lisp-symbols-from-main-lisp-package*. (let ((*package* (find-package #-CLTL2 "LISP" #+CLTL2 "COMMON-LISP"))) ; Skip the in-package (read fl) ; Do the defconst for *common-lisp-symbols-from-main-lisp-package*. (setq acl2::*copy-of-common-lisp-symbols-from-main-lisp-package* (eval (caddr (read fl)))) (import acl2::*copy-of-common-lisp-symbols-from-main-lisp-package* "ACL2") (setq acl2::*copy-of-acl2-version* ; Keep this in sync with the value of acl2-version in *initial-global-table*. (concatenate 'string "ACL2 Version 6.3" #+non-standard-analysis "(r)" #+(and mcl (not ccl)) "(mcl)")) ; Do the defconst for *common-lisp-specials-and-constants*. (setq acl2::*copy-of-common-lisp-specials-and-constants* (eval (caddr (read fl)))))) (in-package "ACL2") (defparameter *compiled-file-extension* ; Note that for the community books, files books/Makefile-generic, ; books/Makefile-subdirs, and books/Makefile-psubdirs all deal with compiled ; file extensions. Thanks to Gary Byers for suggested the following approach ; for #+ansi-cl, which however appears to work for all Lisps supported as of ; early 2006. (pathname-type (compile-file-pathname "foo.lisp"))) (defmacro initialize-state-globals () (let ((acl2-compiler-enabled-var #+cltl2 'common-lisp-user::*acl2-compiler-enabled* #-cltl2 'user::*acl2-compiler-enabled*)) `(progn (dolist (pair *initial-global-table*) (f-put-global (car pair) (cdr pair) *the-live-state*)) (f-put-global 'acl2-sources-dir (our-pwd) *the-live-state*) (f-put-global 'iprint-ar (compress1 'iprint-ar (f-get-global 'iprint-ar *the-live-state*)) *the-live-state*) (f-put-global 'compiler-enabled ; Either t, nil, or :books is fine here. For example, it might be reasonable ; to initialize to (not *suppress-compile-build-time*). But for now we enable ; compilation of books for all Lisps. (cond ((boundp ',acl2-compiler-enabled-var) (or (member ,acl2-compiler-enabled-var '(t nil :books)) (error "Illegal value for ~ user::*acl2-compiler-enabled*, ~s" ,acl2-compiler-enabled-var)) ,acl2-compiler-enabled-var) (t ; Warning: Keep the following "compile on the fly" readtime conditional in sync ; with the one in acl2-compile-file. #+(or ccl sbcl) :books #-(or ccl sbcl) t)) *the-live-state*) (f-put-global 'host-lisp ,(let () ; such empty binding has avoided errors in GCL 2.6.7 #+gcl :gcl #+ccl :ccl #+sbcl :sbcl #+allegro :allegro #+clisp :clisp #+cmu :cmu #+lispworks :lispworks #-(or gcl ccl sbcl allegro clisp cmu lispworks) (error "Error detected in initialize-state-globals: ~%~ The underlying host Lisp appears not to support ACL2. ~%~ Contact the ACL2 implementors to request such support.")) *the-live-state*) (f-put-global 'compiled-file-extension ,*compiled-file-extension* *the-live-state*) #+unix (f-put-global 'tmp-dir "/tmp" *the-live-state*) #-acl2-mv-as-values (f-put-global 'raw-arity-alist *initial-raw-arity-alist* *the-live-state*)))) (defconstant *suppress-compile-build-time* ; This flag controls whether explicit calls to the compiler during the build, ; that is via compile and compile-file, are suppressed. The "interpreters" of ; SBCL and CCL compile on-the-fly, so a nice optimization is for us to ; avoid calling the compiler ourselves. #+(or sbcl ccl) ; these compile on-the-fly t #-(or sbcl ccl) nil) (defparameter *global-package-prefix* "ACL2_GLOBAL_") (defparameter *1*-package-prefix* "ACL2_*1*_") (defun make-global-package (x) (let* ((n (concatenate 'string *global-package-prefix* x)) (p (find-package n))) (cond (p (do-symbols (sym p) (makunbound sym))) (t (make-package n :use nil))))) (defun make-*1*-package (x) (let* ((n (concatenate 'string *1*-package-prefix* x))) ; Unlike make-global-package, here we really don't have to worry about bound ; (or fbound) symbols. Presumably ``bound'' is irrelevant, and ``fbound'' is ; taken care of by our undoing mechanisms. (make-package n :use nil))) (or (find-package "ACL2-INPUT-CHANNEL") (make-package "ACL2-INPUT-CHANNEL" :use nil)) (or (find-package "ACL2-OUTPUT-CHANNEL") (make-package "ACL2-OUTPUT-CHANNEL" :use nil)) ; Next we define the initial global and *1* packages. (defconstant *main-lisp-package* (find-package "COMMON-LISP")) (defconstant *main-lisp-package-name-raw* ; Keep this in sync with *main-lisp-package-name*. "COMMON-LISP") ; Keep the following in sync with *main-lisp-package-name*. (make-global-package *main-lisp-package-name-raw*) (make-global-package "KEYWORD") (make-global-package "ACL2") (make-global-package "ACL2-INPUT-CHANNEL") (make-global-package "ACL2-OUTPUT-CHANNEL") ; Keep the following in sync with *main-lisp-package-name*. (make-*1*-package *main-lisp-package-name-raw*) ; Functions cannot be defined in the keyword package, so we do not do so. ; (make-*1*-package "KEYWORD") (make-*1*-package "ACL2") (make-*1*-package "ACL2-INPUT-CHANNEL") (make-*1*-package "ACL2-OUTPUT-CHANNEL") ; Common Lisp does not require that the symbol-package of the basic ; Lisp symbols be the basic LISP or COMMON-LISP package, but merely ; that those symbols be exported from that package. (defparameter acl2::*initial-lisp-symbol-mark* 'acl2_invisible::initial-lisp-symbol-mark) (let ((cl-pkg-name *main-lisp-package-name-raw*)) (dolist (sym *copy-of-common-lisp-symbols-from-main-lisp-package*) ; We support the Invariant on Symbols in the Common Lisp Package. See the long ; comment about this invariant in the symbolp case of bad-lisp-objectp. (setf (get sym *initial-lisp-symbol-mark*) cl-pkg-name))) (defconstant *acl2-package* (find-package "ACL2")) (dolist (x *features*) (cond ((or (equal "AKCL-SET-MV" (symbol-name x)) (equal "ACL2-LOOP-ONLY" (symbol-name x)) (equal "ACL2-METERING" (symbol-name x))) (format t "~%~%Warning: This Common Lisp may be ~ unsuitable for ACL2 because a symbol with~ ~%the name \"ACL2-LOOP-ONLY\", ~ \"AKCL-SET-MV\" or \"ACL2-METERING\" is ~ a member of *FEATURES*.")))) #+akcl (if (fboundp 'si::set-mv) (push :akcl-set-mv *features*) (error "Use a version of ACKL after 206")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CHECKS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; See acl2-check.lisp for more checks. ; The following macro turns off some style warnings. It is defined here ; instead of the "LISP BUGS AND QUIRKS" section because we want to define this ; in the ACL2 package. (defmacro with-redefinition-suppressed (&rest forms) ; This macro is equivalent to progn for SBCL and CMUCL, where we typically ; suppress more warnings anyhow; see with-warnings-suppressed. `(let (#+lispworks (compiler::*redefinition-action* :QUIET) #+allegro (excl:*redefinition-warnings* nil) #+clisp (custom::*suppress-check-redefinition* ; This binding may not suffice during the build. Below is a comment that is ; probably old as of this writing (Oct. 9, 2011). ; Unfortunately, the above binding seems to be ignored when in the scope of ; with-compilation-unit, so we get redefinition warnings from clisp during the ; build. Here is an example that exhibits that behavior CLISP 2.44.1 (even ; without ACL2); and the same problem occurs if we switch the order of ; with-compilation-unit and the binding of *suppress-check-redefinition*. ; (common-lisp::with-compilation-unit ; () ; (let ((custom::*suppress-check-redefinition* t)) ; (dolist (name '("foo" "bar")) ; (let ((source (make-pathname :name name :type "lisp"))) ; (load source) ; (progn ; (compile-file source) ; (load (make-pathname :name name :type "fas"))))))) t) #+ccl (ccl::*compiler-warn-on-duplicate-definitions* nil)) ,@forms)) (defmacro with-warnings-suppressed (&rest forms) ; See also the handler-bind call in init.lisp. Thanks to Juho Snellman for his ; assistance with handler-bind. ; We are happy to turn off redefinition warnings because we trust that ; functions such as add-trip know what they are doing. Without this code, for ; example, :oops can cause many screens of such warnings. #+sbcl ; Warning: We turn off all warnings in SBCL because they are so noisy. We ; eliminate most of them in the proclaim form earlier in this file, using ; (sb-ext:inhibit-warnings 3), because without that the following doesn't work; ; just start up sbcl and submit the following form without the leading ; backquote and replacing ,@forms by (defun f (x y) x). ; `(handler-bind ; ((warning (lambda (c) ; (declare (ignore c)) ; (invoke-restart 'muffle-warning))) ; (style-warning (lambda (c) ; (declare (ignore c)) ; (invoke-restart 'muffle-warning)))) ; ,@forms) ; However, even with the above proclaim form we still get redefinition ; warnings. So we wrap the following handler-bind around forms in order to ; eliminate redefinition warnings as well. `(handler-bind ((warning (lambda (c) (declare (ignore c)) (invoke-restart 'muffle-warning)))) ,@forms) #+cmu `(progn (setq ext:*gc-verbose* nil) (handler-bind ((warning (lambda (c) (declare (ignore c)) (invoke-restart 'muffle-warning)))) ,@forms)) #-(or sbcl cmu) `(with-redefinition-suppressed ,@forms)) (defmacro with-more-warnings-suppressed (&rest forms) ; We add additional warning suppression beyond what is provided by ; with-warnings-suppressed. Warning: We do not necessarily suppress all ; warnings that are suppressed by with-warnings-suppressed, so you will ; probably want to call this macro in a context underneath ; with-warnings-suppressed. ; The handler-bind form given in with-warnings-suppressed for sbcl and cmucl is ; sufficient; we do not need anything further here. But even with the addition ; of style-warnings (as commented there), that form doesn't seem to work for ; CCL, Allegro CL, Lispworks, or CLISP. So we bind some globals instead. #+lispworks `(let ((compiler::*compiler-warnings* nil)) ,@forms) #+allegro `(handler-bind ((style-warning (lambda (c) (declare (ignore c)) (invoke-restart 'muffle-warning)))) ,@forms) #+clisp `(let ((*compile-verbose* nil)) ,@forms) #+ccl `(let ((ccl::*suppress-compiler-warnings* t)) ,@forms) #-(or lispworks allegro clisp ccl) (if (cdr forms) `(progn ,@forms) (car forms))) (defmacro with-suppression (&rest forms) ; Since "COMMON-LISP" is a package known to ACL2, a user should have permission ; to type 'common-lisp::foo (say) and not get a reader error due to a package ; lock. This macro suppresses the package lock on "COMMON-LISP" for Lisps ; where we know this is necessary, and also inhibits some warnings. #-(or sbcl clisp) `(with-warnings-suppressed ,@forms) #+sbcl `(sb-ext:with-unlocked-packages ("COMMON-LISP") (with-warnings-suppressed ,@forms)) #+clisp `(ext:without-package-lock ("COMMON-LISP") (with-warnings-suppressed ,@forms))) (defconstant acl2::*acl2-status-file* (make-pathname :name "acl2-status" :type "txt")) (defun acl2::check-suitability-for-acl2 () ; As of version 18a, cmulisp spews gc messages to the terminal even when ; standard and error output are redirected. So we turn them off. (with-warnings-suppressed (or (not (probe-file *acl2-status-file*)) (delete-file *acl2-status-file*)) (load "acl2-check.lisp") (with-open-file (str *acl2-status-file* :direction :output) (format str "~s" :checked)) t)) (defun note-compile-ok () (progn (or (not (probe-file *acl2-status-file*)) (delete-file *acl2-status-file*)) (with-open-file (str *acl2-status-file* :direction :output) (format str "~s" :compiled)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; COMPILING and LOADING, PART 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; To compile ACL2, invoke (load "acl2.lisp") and then invoke ; (acl2::compile-acl2). Having compiled ACL2, to build ACL2, invoke ; (load "acl2.lisp") and then invoke (acl2::load-acl2). To run ACL2 ; verified code, one may either load all of ACL2 as above, or ; alternatively, having complied ACL2, invoke (load "acl2.lisp") and ; invoke (acl2::load-acl2-execution-environment). The top-level user ; functions for ACL2 are in package "ACL2", so invoke (in-package ; "ACL2") to obviate typing package names. ; NOTE: In order to compile ACL2, checks must first be run on the suitability ; of the underlying Common Lisp implementation, by executing ; (check-suitability-for-acl2). If the Common Lisp is suitable, this form will ; write the file acl2-status.txt with the symbol :CHECKED. Successful ; compilation should write out that same file with the symbol :COMPILED. ; Compiling is a no-op if *suppress-compile-build-time* is non-nil, but we ; still write :COMPILED as indicated above. (defvar *lisp-extension* "lisp") #+sbcl ; turn off compiler notes (noisy) (declaim (sb-ext:muffle-conditions sb-ext:compiler-note)) ; The Allegro CL documentation tells us that the following variable is ; "Initially true only if speed is 3 and safety is 0", and that it allows the ; compiler to assume that the sum of two declared fixnums is a fixnum, etc. We ; hope that by setting this variable to nil we avoid seeing occasional checksum ; errors during include-book when certifying the regression suite. #+allegro (setf comp:declared-fixnums-remain-fixnums-switch nil) ; Previous value for the above: ; (eval (read-from-string " ; (SETQ COMPILER::DECLARED-FIXNUMS-REMAIN-FIXNUMS-SWITCH ; #'(LAMBDA (X Y Z #+(VERSION>= 4 1) D) NIL)) ")) ; The following appears to allow tail recursion elimination for functions ; locally defined using LABELS. This is important for efficiency since we ; use LABELS in defining executable counterparts (see e.g. oneify-cltl-code). #+allegro (setq compiler:tail-call-non-self-merge-switch t) ; LispWorks Version 4.2.0 has issued the following complaint during compilation ; until the next form was executed: ; **++++ Error in NTH-UPDATE-REWRITER1: ; Function size 73824 is too large. ; But even with the next form, we have seen the following: ; **++++ Error in XTRANS-EVAL: ; Function size 67910 is too large. ; This problem has disappeared with LispWorks 6.0. But it seems reasonable to ; leave the following in place. #+lispworks (cl-user::toggle-source-debugging nil) (defmacro our-with-compilation-unit (form) ; In fact, with-compilation-unit is only defined in dpANS, not CLtL2. But MCL ; and CCL do seem to support it, so we allow it with #+cltl2 and #+ccl. ; We also have noticed that while :CLTL2 belongs to *features* in Version ; :CMU17 (presumably 17f), it does not belong in a cmucl version 18d that we ; obtained for Linux, even though with-compilation-unit continues to be ; defined. #+cltl2 `(common-lisp::with-compilation-unit () ,form) #-cltl2 form) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; BASIC FUNCTIONS TO BE COMPILED ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Functions for proclaiming and for defining ACL2's Implementation of the ; Backquote Readmacro are to be compiled, so we do not include them in the ; present file. Instead we put them in acl2-fns.lisp, after defining the ; following constant. (We could put this defconstant in acl2-fns.lisp, but ; CLISP would warn unless we made it conditional on a (not (boundp ..)) ; check. That sort of fix has gotten us into trouble with Allegro, so we take ; the simpler solution of putting the defconstant in this file, which is ; loaded only once (there is no compiled version of this file to load). (defconstant *acl2-read-character-terminators* '(#\Tab #\Newline #\Page #\Space #\" #\' #\( #\) #\; #\` #\,)) (our-with-compilation-unit ; At one time we avoided recompiling and had the following code inside a COND: ; ((and (probe-file object-file) ; (<= (file-write-date "acl2-fns.lisp") ; (file-write-date object-file))) ; (load object-file)) ; But for example, if we compile with Allegro under SunOS and then later build ; the system with Allegro under Linux, the same ".fasl" extension could fool us ; into thinking that recompilation is not necessary. (progn (load "acl2-fns.lisp") ;we like to load before compiling (let ((acl2-fns-compiled (make-pathname :name "acl2-fns" :type *compiled-file-extension*))) (when (probe-file acl2-fns-compiled) (delete-file acl2-fns-compiled)) (when (not *suppress-compile-build-time*) (compile-file "acl2-fns.lisp") ; Note that load-compiled is not used below, but on the other hand we are still ; using the original readtable here so that's not a problem. (load acl2-fns-compiled))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ACL2-READTABLE ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defparameter *acl2-readtable* (copy-readtable nil) "*acl2-readtable* is the readtable we use (a) to restrict the use #. to cause evaluation during READing (b) and to define our own version of backquote.") (defparameter *host-readtable* (copy-readtable) "*host-readtable* is the original readtable provided by the host Lisp, which is saved just in case it's needed later.") (defun set-new-dispatch-macro-character (char subchar fn) ; This function currently causes an error when attempting to build ACL2(h) on ; top of CLISP, where (get-dispatch-macro-character #\# #\Y) evaluates to ; #. Here is a discussion of that ; issue. ; With some thought we might be able to avoid the special cases below for which ; char is #\# and subchar is, for example, #\Y -- i.e., smashing (in that ; example) which reader is invoked by #\Y. We certainly have in mind our own ; semantics for ACL2 source files, to be read using *acl2-readtable*, while the ; host Lisp's semantics are expected for compiled files. But consider files ; like .cert and @expansion.lsp files, i.e., files that may be written by the ; host Lisp (see the call of prin1 in print-object$-ser) but are read by ACL2. ; Perhaps the issue goes away if we are using the serialize reader and writer, ; as must be the case when we install a reader for #\Y. We may think all this ; through when there is sufficient reason to do so. For now, the only problem ; pertaining to our handling of dispatch macro characters is in the case of ; CLISP and ACL2(h), since #\Y is already defined in CLISP -- this function ; causes an error when attempting to build ACL2(h) on CLISP. Since CLISP is ; much slower than the other six host Lisps that we support, and since ACL2(h) ; is optimized for CCL such that it is really only intended for CCL at this ; point (June 2013), we can live without CLISP support for ACL2(h). (let ((old (get-dispatch-macro-character char subchar))) (cond ((or (null old) (eql fn old) #+cmu ; This is really just a guess, but it's a pretty good one, as several values of ; subchar (for char equal to #\#) give this function, at least in our copies of ; CMU CL 19e and CMU CL 20D. (eq old (symbol-function 'LISP::DISPATCH-CHAR-ERROR)) #+gcl ; For GCL, Camm Maguire has told us that he believes that it's safe for us to ; make this redefinition for these characters. (and (eql char #\#) (member subchar '(#\@ #\! #\u #\Y #\Z))) #+allegro ; The #u reader has been defined in Allegro CL to invoke parse-uri. It seems ; harmless to overwrite it (especially since we only smash #u in ; *acl2-readtable*). David Margolies of Franz Inc. has kindly pointed us to ; the web page ; http://www.franz.com/support/documentation/8.1/doc/uri.htm#acl-implementation-1 ; where we find this: ; 4. #u"..." is shorthand for (parse-uri "...") but if an existing #u ; dispatch macro definition exists, it will not be overridden. (and (eql char #\#) (member subchar '(#\u)))) (set-dispatch-macro-character char subchar fn)) (t (error "ACL2 cannot be built in this host Lisp, because ~%~ ~s is already defined, to be: ~s" `(get-dispatch-macro-character ,char ,subchar) old))))) (defun define-sharp-dot () (set-dispatch-macro-character #\# #\. #'sharp-dot-read)) (defun define-sharp-comma () (set-dispatch-macro-character #\# #\, #'sharp-comma-read)) (defun define-sharp-atsign () (set-new-dispatch-macro-character #\# #\@ #'sharp-atsign-read)) (defun define-sharp-bang () (set-new-dispatch-macro-character #\# #\! #'sharp-bang-read)) (defun define-sharp-u () (set-new-dispatch-macro-character #\# #\u #'sharp-u-read)) (defvar *old-character-reader* (get-dispatch-macro-character #\# #\\)) (defun modify-acl2-readtable (do-all-changes) (let ((*readtable* *acl2-readtable*)) ; Thanks to Jared Davis for contributing the code for #\Y and #\Z (see ; serialize-raw.lisp). Note that p. 531 of CLtL2 specifies that #\Y and #\Z ; may be available to us (though we check this by using ; set-new-dispatch-macro-character). Keep these two settings in sync with ; *reckless-acl2-readtable*. #+hons ; SBCL requires #+hons (same restriction as ser-hons-reader-macro) (set-new-dispatch-macro-character #\# #\Z 'ser-hons-reader-macro) #+hons ; SBCL requires #+hons (same restriction as ser-cons-reader-macro) (set-new-dispatch-macro-character #\# #\Y 'ser-cons-reader-macro) ; Backquote (set-macro-character #\` #'(lambda (stream char) (declare (ignore char)) (let ((*backquote-counter* (1+ *backquote-counter*))) (backquote (read stream t nil t))))) ; Comma (set-macro-character #\, #'(lambda (stream char) (declare (ignore char)) (let ((*backquote-counter* (1- *backquote-counter*))) (cond ((< *backquote-counter* 0) (clear-input stream) (error "Illegal comma encountered by READ."))) (case (peek-char nil stream t nil t) (#\@ (read-char stream t nil t) (list *comma-atsign* (read stream t nil t))) (#\. (error ",. not allowed in ACL2 backquote forms.")) (otherwise (list *comma* (read stream t nil t))))))) ; Restrict #. (when do-all-changes (define-sharp-dot) (define-sharp-comma) (define-sharp-atsign) (define-sharp-bang) (define-sharp-u)) ; Keep control of character reader. However, we do not need to keep such ; control when reading in a .fas file for CLISP, and in fact, the set-theory ; book's use of (code-char 255) generates ; #\LATIN_SMALL_LETTER_Y_WITH_DIAERESIS in set-theory.fas. We see no reason ; to restrict reading of such characters in .fas files. (when do-all-changes (set-dispatch-macro-character #\# #\\ #'acl2-character-reader)))) (eval-when #-cltl2 (load eval compile) #+cltl2 (:load-toplevel :execute :compile-toplevel) #-clisp (modify-acl2-readtable t) #+clisp (progn (modify-acl2-readtable nil) (defparameter *acl2-readtable-clisp-fas* (copy-readtable *acl2-readtable*)) (let ((*readtable* *acl2-readtable*)) (define-sharp-dot) (define-sharp-comma) (define-sharp-atsign) (define-sharp-bang) (define-sharp-u) (set-dispatch-macro-character #\# #\\ #'acl2-character-reader)))) (defvar *reckless-acl2-readtable* ; See "SUPPORT FOR FAST #n= and #n#" in acl2-fns.lisp. (let ((*readtable* (copy-readtable *acl2-readtable*))) (set-dispatch-macro-character #\# #\# #'reckless-sharp-sharp-read) (set-dispatch-macro-character #\# #\= #'reckless-sharp-equal-read) (set-dispatch-macro-character #\# #\\ *old-character-reader*) *readtable*)) (defvar *load-compiled-verbose* nil) (defun load-compiled (filename &optional verbose) ; It may be useful to implement the maybe-verbose argument for Lisps that do ; not print a "loading" message. For now, we comment out code below that would ; do this. (when (and verbose *load-compiled-verbose*) (eval `(cw "~%Note: loading file ~s0.~|" ',filename))) #+clisp (let ((*readtable* *acl2-readtable-clisp-fas*)) (load filename)) #-clisp (load filename)) ; Remarks on *acl2-readtable* ; ; ; Because read-object$ calls the Common Lisp function read, read-object$ ; is a function of the values of the Common Lisp symbols (a) ; *readtable*, (b) *package*, and (c) *features*. In ACL2, the user can ; specify the package to use, via in-package, which sets the global ; current-package. The user of ACL2 has no (legitimate) control over the ; readtable, which is set above and discussed below. ; ; As for *features*, we currently permit full use of the *features* ; facility of the Common Lisp reader, with this caveat: it is an ; official part of the ACL2 story that *features* should have the same ; setting throughout all phases of ACL2 usage. That is, the user must ; set up *features* at the beginning, before starting to use ACL2 and ; the user must then leave *features* alone (even though the ; implementors of ACL2 put :acl2-loop-only onto *features* during ; boot-strapping). One bad consequence of our *features* policy is that ; verified files will not in general be verifiable or useable in other ; Lisp implementations or installations unless the settings of ; *features* relevant to one's usages of the #+ and #- are the same in ; the two Lisp implementations. One simple way to obtain peace of mind ; on this topic is for the user not to use #+ or #- at all! It might be ; cleaner for us simply to prohibit the use of the #+ and #- readmacros ; all together in ACL2. This could be done at the cost of replacing the ; many uses of #+ and #- in axioms.lisp, and a few other places, with ; some sort of regular macro. ; ; Below is a detailed examination of the default Common Lisp readtable ; from the perspective of ACL2. We want to make sure that when we read, ; we do not have side effects (e.g. via #.) of ACL2 but will merely ; either (a) cause an error or (b) generate a Lisp object, which we then ; will check with bad-lisp-object before handing it to ACL2 functions. ; ; All of the standard Common Lisp characters are either white space or ; constituent, with the following exceptions, which are read macros: ; ; " quote ; # number sign ; ' quote ; () parentheses ; , comma ; ; comment ; \ single escape ; ` backquote ; | multiple escape ; ; With the exception of number sign, backquote and comma, we certainly ; want all of these readmacros to have their usual Common Lisp ; semantics. ; ; We have defined our own backquote and discussed it above. ; ; We now review the # situation: ; ; ## and #= are for reading possibly circular stuff ; bad-lisp-object may run forever ; #' reads as function ; won't hurt anything ; #( vector ; will be rejected by bad-lisp-object ; #) signals an error ; enough said ; #* bit vector ; will be rejected by bad-lisp-object ; #, load-time evaluation ; we shut it off ; #0-#9 used for infix arguments ; ok ; #: uninterned symbol ; will be rejected by bad-lisp-object ; #< signals an error ; enough said ; #\ character object ; will be checked by bad-lisp-object; see also below ; #| start comment, ended by |# ; ok ; # ; signals an error -- ok ; #+ and #- ; see the discussion of *features* above ; #. read time evaluation ; we restrict it ; #A, #a arrays ; will be checked by bad-lisp-object ; #B, #b binary rational ; ok ; #C, #c complex ; ok (rejected by bad-lisp-object except for rational components) ; #O, #o octal ; ok ; #P, #p pathname ; will be checked by bad-lisp-object ; #R, #r radix-n ; fine ; #S, #s structure ; will be rejected by bad-lisp-object ; #X, #x hex ; ok ; ; Eventually, it will be best to define a read function for ACL2 solely in terms ; of ACL2 character reading primitives. Common Lisp read is ambiguous. There is ; the ambiguity of backquote described above. There is the abiguity of which ; tokens get read as numbers. To make matters a little more scary, there is ; nothing that prevents a Common Lisp implementation from adding, for example, a ; new # readmacro option that would provide something as potentially catastrophic ; as full-blown sharp-dot. One obstacle to doing a read within ACL2 this is ; efficiency. For example, ACL2 does not now support unread-char. And given the ; requirement that whatever is specified in a guard must be checkable, it is ; unclear now how best to add unread-char since Common Lisp does permit one to ; detect whether a stream is in a ``just unread'' state. ACL2 could enable one ; to answer such a question, but at the cost of having to store the information ; every time that a character was unread. ; ACL2's Implementation of the character reader ; We have decided to take full charge of everything that # reader ; does, which is just a part of the way towards writing READ totally ; from scratch. And we are pretty conservative about what #\ does ; accept; one can always get the characters one wants by using ; code-char. Notice for example that if we're not careful, then ACL2 ; could be potentially unsound when we have 8-bit characters, because ; it's conceivable that ; ; (equal (char-code "#\fifth-control-character") 5) ; ; is a theorem in one Common Lisp and yet ; ; (equal (char-code "#\fifth-control-character") 6) ; ; is a theorem in another. Bizarre, but apparently not ruled out by ; dpANS. ; ; So, we manage this simply by modifying the character reader so that ; the #\ notation only works for single characters and for Space, Tab, ; Newline, Page, and Rubout; an error is caused otherwise. ; Our algorithm for reading character objects starting with #\ is ; quite simple. We accumulate characters until encountering a ; whitespace character or a terminating macro character, from the list ; *acl2-read-character-terminators*. The result must be either a ; single standard character or else one of the names (up to case, ; which we ignore in the multiple-character case) SPACE, TAB, NEWLINE, ; PAGE, and RUBOUT. Otherwise we cause an error. Note that if we do ; NOT cause an error, then any dpANS-compliant Common Lisp ; implementation's character reader would behave the same way, because ; dpANS says (in the section ``Sharpsign Backslash'') the following. ; ..... After #\ is read, the reader backs up ; over the slash and then reads a token, treating the initial slash ; as a single escape character (whether it really is or not in the ; current readtable). ; The rather involved description from dpANS in the section ``Reader ; Algorithm'' implies that when a token is terminated without error, ; it must be upon first encountering a whitespace character or a ; terminating macro character. ; Finally, here is an argument that we cannot reasonably allow ACL2 to ; accept character notations of the sort akcl allows, such as #\\112 ; for #\J for example. (By the way, 112 is octal for 74, which is ; (char-code #\J).) This is sad, because it would have been nice to ; provide a way of reading arbitrary 8-bit characters in ACL2. ; In the following, we refer to documentation from Bill Schelter's ; info version of dpANS. ; So, assume that #\J parses the same as #\\112 (and we'll derive a ; ``contradiction'' of sorts). The documentation from ``Sharpsign ; Backslash'' below implies that #\\112 parses as the character whose ; name is [STRING-UPCASE applied to] the 4-character string \112. So ; if #\\112 parses as #\J, then the name of the character #\J is ; "\\112". Then, the documentation for ``char-name'' (part of which ; is also below) implies that CHAR-NAME returns the character name, ; and hence (CHAR-NAME #\J) must be "\\112". But probably this isn't ; true of the implementation (it's not true for akcl or allegro, for ; example). And, it seems really dangerous for us to redefine ; CHAR-NAME in ACL2. ; What's worse, if we apply the first part of this argument to ; #\Newline, we see that the name of the character #\Newline is ; "\\12", which directly contradicts the final passage below from ; dpANS. ; In what follows we'll quote from dpANS (an emacs Info version). We ; quote here from three sections, separated by ++++++++++++++++++. ; ; Sharpsign Backslash ; ................... ; ; Syntax: #\<> ; ; [[[ text omitted ]]] ; ; When the token x is more than one character long, the x must have the ; syntax of a symbol with no embedded package markers. In this case, the ; sharpsign backslash notation parses as the character whose name is ; (string-upcase x); see *See Character Names::. ; ; ++++++++++++++++++ ; ; char-name [Function] ; --------------------------------------------------------------------------- ; ; `char-name' character => name ; ; Arguments and Values:: ; ...................... ; ; character--a character. ; ; name--a string or nil. ; ; Description:: ; ............. ; ; Returns a string that is the name of the character, or nil if the ; character has no name. ; ; ++++++++++++++++++ ; ; Character Names ; --------------- ; ; The following character names must be present in all conforming ; implementations: ; ; Newline ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; COMPILING and LOADING, PART 2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun non-trivial-acl2-proclaims-file-p () (with-open-file (str "acl2-proclaims.lisp" :direction :input :if-does-not-exist nil) (and str (let* ((new-cons (cons nil nil)) (val (read str nil new-cons))) (and (not (eq new-cons val)) ; We might not need the following, but just in case we decide always to print ; an in-package form into the file, we require that the file have at least two ; forms. (not (eq new-cons (read str nil new-cons)))))))) (defun compile-acl2 (&optional use-acl2-proclaims) (with-warnings-suppressed #+sbcl (declaim (optimize (safety 0) (space 0) (speed 3) (debug 0))) ; Here is a natural place to put compiler options. In fact, we put them above, ; globally. ; (declaim (optimize (safety 0) (space 0) (speed 3))) ; However, Juho Snellman points out that SBCL resets the compiler policy on ; entry to LOAD / COMPILE-FILE, and restores the old value once the file has ; been loaded / compiled; thus the global declaim is no longer in effect once ; COMPILE-ACL2 gets called. ; Note on Illegal Instructions: If ACL2 causes an illegal instruction ; trap it is difficult to figure out what is happening. Here is a ; brute force way to do it. ; (0) create an events file or book that will recreate the ; the state up to the event that causes the illegal instruction. ; (1) Copy saved_acl2 to old_saved_acl2 ; (2) replace the declaim above with ; (declaim (optimize (safety 3) (space 0) (speed 0))); ; (3) make full init ; this will fail because of stack overflows during booting. ; (4) fire up old_saved_acl2 ; (5) load the saved events file or book, recreating the state. ; (6) :q from (LP) ; (7) (progn (setq old-world-pair (get 'current-acl2-world 'acl2-world-pair)) ; t) ; (8) (dolist (name *acl2-files*) ; (or (equal name "defpkgs") ; (load source))) ; load the (safety 3) .o files ; Actually, the last time I did this I loaded each file manually ; and I did not load ld or interface-raw. I think they can be ; loaded here though. ; (9) (progn (f-put-global 'current-acl2-world ; (car old-world-pair) *the-live-state*) ; t) ;(10) (si::use-fast-links nil) ;(11) enter (LP) and execute the offending event. ;(12) once the bug is fixed, be sure to change the declaim ; back to (safety 0) (speed 3) before recompiling. ; Note on loading before compiling. We load each ACL2 source file before ; compiling it to make sure that the functions needed to execute macros have ; been defun-ed before they are called. Normal Common Lisp compilation does ; not do this. So we cause all forms to be executed before we start the ; compilation. This guarantees that when macros run, the functions they call ; have been defined. ; In general, and for the same reason, all ACL2 user checked files are also ; loaded before they are compiled. ; As of version 18a, cmulisp spews gc messages to the terminal even when ; standard and error output are redirected. So we turn them off. (when (and use-acl2-proclaims (not (non-trivial-acl2-proclaims-file-p))) ; Note that GNUmakefile provides special treatment for the value :REUSE of ; environment/make variable USE_ACL2_PROCLAIMS. With that treatment, we avoid ; calling compile-acl2 with use-acl2-proclaims = t, and thus we don't get to ; this point. (error "Note: Skipping compilation that is intended to use generated ~ file \"acl2-proclaims.lisp\", because that file is missing or ~ has no forms in it.")) (when (and (not use-acl2-proclaims) (probe-file "acl2-proclaims.lisp")) (delete-file "acl2-proclaims.lisp")) (cond ((or (not (probe-file *acl2-status-file*)) (with-open-file (str *acl2-status-file* :direction :input) (not (eq (read str nil) :checked)))) (check-suitability-for-acl2))) (when (not *suppress-compile-build-time*) (our-with-compilation-unit (let ((*readtable* *acl2-readtable*) #+akcl ; AKCL compiler note stuff. We have so many tail recursive functions ; that the notes about tail recursion optimization are just too much ; to take. (compiler:*suppress-compiler-notes* t)) (when use-acl2-proclaims (proclaim-files nil "acl2-proclaims.lisp" nil)) (dolist (name *acl2-files*) (or (equal name "defpkgs") (let ((source (make-pathname :name name :type *lisp-extension*))) (load source) (or (equal name "proof-checker-pkg") (progn (when (not use-acl2-proclaims) ; So, we have not loaded acl2-proclaims.lisp. (proclaim-file source)) (compile-file source) (load-compiled (make-pathname :name name :type *compiled-file-extension*)))))))))) (note-compile-ok))) #+gcl (defvar user::*fast-acl2-gcl-build* nil) (defun load-acl2 (&optional fast) ; If fast is true, then we are welcome to avoid optimizations that might make ; for a better saved image. For example, we use fast = t when building simply ; to write proclaim forms into acl2-proclaims.lisp. (declare (ignorable fast)) (our-with-compilation-unit ; only needed when *suppress-compile-build-time* (with-warnings-suppressed (when (and *suppress-compile-build-time* (not fast)) ; When we rely on Lisp to compile on-the-fly, we want to proclaim before ; loading. This might actually be broken; we got an error when trying to do ; proclaiming here for GCL, complaining that "The variable ; *COMMON-LISP-SYMBOLS-FROM-MAIN-LISP-PACKAGE* is unbound." If we ever decide ; to proclaim when *suppress-compile-build-time* is true, we can deal with that ; problem then, perhaps by using ; *copy-of-common-lisp-symbols-from-main-lisp-package* instead (though we ; haven't tried that). (proclaim-files nil "acl2-proclaims.lisp" t)) ; If we are in the first pass of two passes, then don't waste time doing the ; slow build for GCL (where we compile all *1* functions as we go through ; initialization). #+(and gcl acl2-mv-as-values) (when fast (setq user::*fast-acl2-gcl-build* t)) #+akcl ; We grow the image slowly, since we now do allocation on start-up. We are ; assuming that people will be using load-acl2 only as part of the process of ; building a saved image, and hence that this slow growth policy will be undone ; by the function save-acl2-in-akcl. If we are (when (not fast) (loop for type in '(cons fixnum symbol array string cfun sfun ; In akcl, at least some versions of it, we cannot call allocate-growth on the ; following two types. #+gcl contiguous #+gcl relocatable ) do (cond ((or (boundp 'si::*gcl-major-version*) ;GCL 2.0 or greater (and (boundp 'si::*gcl-version*) ;GCL 1.1 (= si::*gcl-version* 1))) (si::allocate-growth type 1 10 50 2)) (t (si::allocate-growth type 1 10 50))))) (cond ((or (not (probe-file *acl2-status-file*)) (with-open-file (str *acl2-status-file* :direction :input) (not (member (read str nil) '(:compiled :initialized))))) (error "Please compile ACL2 using ~s, which will write~%~ the token :COMPILED to the file acl2-status.txt." '(compile-acl2)))) (let ((*readtable* *acl2-readtable*) (extension (if *suppress-compile-build-time* *lisp-extension* *compiled-file-extension*))) (dolist (name *acl2-files*) (or (equal name "defpkgs") (if (equal name "proof-checker-pkg") (load "proof-checker-pkg.lisp") (load-compiled (make-pathname :name name :type extension))))) (load "defpkgs.lisp") (when (and (not *suppress-compile-build-time*) ; other case is above (not fast)) (proclaim-files nil "acl2-proclaims.lisp" t)) (in-package "ACL2") ; Do not make state special, as that can interfere with tail recursion removal. ; The following form is provided merely as a convenience to the user, who may ; want to execute ACL2 forms in raw Lisp. The use of set instead of setq is to ; avoid getting a warning in cmulisp that state is undefined. (set 'state *the-live-state*) "ACL2")))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; DECLARATIONS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We use XARGS in DECLARE forms. By making this proclamation, we ; suppress compiler warnings. (declaim (declaration xargs)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; EXITING LISP ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defparameter *acl2-panic-exit-status* nil) (defun exit-lisp (&optional (status '0 status-p)) ; Parallelism blemish: In ACL2(p), LispWorks 6.0.1 hasn't always successfully ; exited when exit-lisp was called. The call below of stop-multiprocessing is ; an attempt to improve the chance of a succesful exit. In practice, the call ; does not fix the problem. However, we leave it for now since we don't think ; it can hurt. If exit-lisp starts working reliably without the following ; calls to send-die-to-worker-threads and stop-multiprocessing, they should be ; removed. #+(and acl2-par lispworks) (when mp::*multiprocessing* (send-die-to-worker-threads) (mp::stop-multiprocessing)) ; The status (an integer) will be returned as the exit status (shell variable ; $?). We avoid passing the status argument when it is 0, in case Windows or ; other operating systems get confused by it. However, this function is a ; no-op if we are already in the process of quitting via this function; see the ; comment below the occurrence below of *acl2-panic-exit-status*. ; It appeared at one point that (ccl::quit 0) is more reliable than ; (ccl::quit), but that's no longer clear. Still, it seems reasonable to pass ; the status explicitly to the individual Lisp's exit function if that status ; is passed explicitly here -- hence the use of status-p. (cond ((null status) ; shouldn't happen (error "Passed null status to exit-lisp!")) (*acl2-panic-exit-status* ; We have seen various type errors and bus errors when attempting to quit in ; CCL. Gary Byers pointed out that this may be caused by attempting to quit ; while in the process of quitting. So, we avoid doing a quit if already in ; the process of quitting. (return-from exit-lisp nil))) (setq *acl2-panic-exit-status* status) #+clisp (if status-p (user::exit status) (user::exit)) #+lispworks ; Version 4.2.0; older versions have used bye (if status-p (lispworks:quit :status status) (lispworks:quit)) #+akcl (if status-p (lisp::bye status) (lisp::bye)) #+lucid (lisp::exit) ; don't know how to handle status, but don't support lucid #+ccl (if status-p (ccl::quit status) (ccl::quit)) #+cmu (cond ((null status-p) (common-lisp-user::quit t)) (t ; quit does not take an exit status as of CMUCL version 19e (unix:unix-exit status))) #+allegro (user::exit status :no-unwind t) #+(and mcl (not ccl)) (cl-user::quit) ; mcl support is deprecated, so we don't worry about status #+sbcl (let ((sym (or (find-symbol "EXIT" 'sb-ext) (find-symbol "QUIT" 'sb-ext)))) ; Quoting http://www.sbcl.org/manual/#Exit, regarding sb-ext:quit: ; Deprecated in favor of sb-ext:exit as of 1.0.56.55 in May 2012. Expected to ; move into late deprecation in May 2013. (cond ((or (null sym) (not (fboundp sym))) (error "No function named \"EXIT\" or \"QUIT\" is defined in the ~%~ \"SB-EXT\" package. Perhaps you are using a very old or ~%~ very new version of SBCL. If you are surprised by this ~%~ message, feel free to contact the ACL2 implementors.")) ((equal (symbol-name sym) "EXIT") (if status-p (funcall sym :code status) (funcall sym))) (t ; (equal (symbol-name sym) "QUIT") (if status-p (funcall sym :unix-status status) (funcall sym))))) ; Return status (to avoid an ignore declaration) if we cannot exit lisp. The ; caller of this function should complain if Lisp survives the call. The panic ; flag may help though. (progn status-p status)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; CONSTANTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Allegro 6.0 (and probably later versions) warns when the following two (at ; least) constants are in axioms.lisp, even with boundp checks (as in ACL2 ; Version_2.5). The warning is a result of evaluating the defconstant twice: ; when loading the source file and when subsequently loading the compiled file. ; So starting with Version_2.6 we put the constants here, since this file ; (acl2.lisp) is not compiled and hence is only loaded once. ; A slight complication is that *slashable-array* uses *slashable-chars*, which ; in turn is used in the definition of function some-slashable. (There are ; other such examples, but we'll focus on that one.) So, defconst ; *slashable-chars* needs to be defined in the ACL2 loop even though, as of ; Version_2.5, it was used in the definition of *slashable-array*, which we ; want to include in the present file. So we inline the defconsts below. (defconstant *slashable-array* (let ((ar (make-array 256 :initial-element nil))) (dolist (ch ; Inline *slashable-chars*; see the comment above. '(#\Newline #\Page #\Space #\" #\# #\' #\( #\) #\, #\: #\; #\\ #\` #\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\|)) (setf (aref ar (char-code ch)) t)) ar)) (defconstant *suspiciously-first-numeric-array* (let ((ar (make-array 256 :initial-element nil))) (dolist (x ; Inline *suspiciously-first-numeric-chars*; see the comment above. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\+ #\- #\. #\^ #\_)) (setf (aref ar (char-code x)) t)) ar)) (defconstant *suspiciously-first-hex-array* (let ((ar (make-array 256 :initial-element nil))) (dolist (x ; Inline *suspiciously-first-hex-chars*; see the comment above. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\A #\B #\C #\D #\E #\F #\a #\b #\c #\d #\e #\f #\+ #\- #\. #\^ #\_)) (setf (aref ar (char-code x)) t)) ar)) (defconstant *base-10-array* (let ((ar (make-array 256 :initial-element nil))) (dolist (x ; Inline *base-10-chars*; see the comment above. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) (setf (aref ar (char-code x)) t)) ar)) (defconstant *hex-array* (let ((ar (make-array 256 :initial-element nil))) (dolist (x ; Inline *hex-digits*; see the comment above. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\A #\B #\C #\D #\E #\F #\a #\b #\c #\d #\e #\f)) (setf (aref ar (char-code x)) t)) ar)) (defconstant *letter-array* (let ((ar (make-array 256 :initial-element nil))) (dolist (ch ; Inline *letter-array*; see the comment above. '(#\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z #\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z)) (setf (aref ar (char-code ch)) t)) ar)) (defmacro suspiciously-first-numeric-array (print-base) `(if (eql ,print-base 16) *suspiciously-first-hex-array* *suspiciously-first-numeric-array*)) (defmacro numeric-array (print-base) `(if (eql ,print-base 16) *hex-array* *base-10-array*)) (defconstant *char-code-backslash* (char-code #\\)) (defconstant *char-code-slash* (char-code #\/)) (defconstant *char-code-double-gritch* (char-code #\")) ; The following constant was originally in translate.lisp, but CLISP warned ; that it was being redefined. This seems to be the same problem as mentioned ; above for Allegro, so we define it here. (defconstant *big-n-special-object* '(nil . nil)) (defconstant *number-of-return-values* ; Keep this in sync with related code in translate11. 32) (defconstant *boole-array* ; Keep this in sync with the defconst forms just above the definition of ; boole$. (let ((ar (make-array 16 :element-type 'fixnum)) (i 0)) (declare (type (simple-array fixnum (*)) ar)) (dolist (x `((boole-1 . ,boole-1) (boole-2 . ,boole-2) (boole-and . ,boole-and) (boole-andc1 . ,boole-andc1) (boole-andc2 . ,boole-andc2) (boole-c1 . ,boole-c1) (boole-c2 . ,boole-c2) (boole-clr . ,boole-clr) (boole-eqv . ,boole-eqv) (boole-ior . ,boole-ior) (boole-nand . ,boole-nand) (boole-nor . ,boole-nor) (boole-orc1 . ,boole-orc1) (boole-orc2 . ,boole-orc2) (boole-set . ,boole-set) (boole-xor . ,boole-xor))) (or (typep (cdr x) 'fixnum) (error "We expected the value of ~s to be a fixnum, but it is ~s!" (car x) (cdr x))) (setf (aref ar i) (cdr x)) (incf i)) ar)) ; The following constants were originally in memoize-raw.lisp, but CMUCL caused ; a redefinition error. This may be the same problem as mentioned above for ; Allegro. #+hons (progn ; locals used in memoize-on and memoize-off (defconstant *mo-f* (make-symbol "F")) (defconstant *mo-h* (make-symbol "H")) (defconstant *mo-o* (make-symbol "O")) ; locals used in functions generated by memoize-fn (defconstant *mf-old-caller* (make-symbol "OLD-CALLER")) (defconstant *mf-start-hons* (make-symbol "START-HONS")) (defconstant *mf-start-pons* (make-symbol "START-PONS")) (defconstant *mf-start-bytes* (make-symbol "START-BYTES")) (defconstant *mf-ans* (make-symbol "ANS")) (defconstant *mf-ans-p* (make-symbol "ANS-P")) (defconstant *mf-ma* (make-symbol "MA")) (defconstant *mf-args* (make-symbol "ARGS")) (defconstant *mf-2mmf* (make-symbol "MF-2MMF")) (defconstant *mf-2mmf-fnn* (make-symbol "MF-2MMF-FNN")) (defconstant *mf-count-loc* (make-symbol "MF-COUNT-LOC")) (defconstant *attached-fn-temp* (make-symbol "ATTACHED-FN-TEMP")) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; STATISTICS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; See the comment in *rewrite-depth-max* about rewrite stack depth: ; (push :acl2-rewrite-meter *features*) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; PROMPTS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; New ACL2 users sometimes do not notice that they are outside the ACL2 ; read-eval-print loop when in a break. See the discussion of "PROMPTS" in ; interface-raw.lisp for how we deal with this. For GCL, we currently (as of ; GCL version 2.6.6) need a patch for built-in function si::break-level. This ; requires a package change, so we put that patch in a file that is not ; compiled; the present file serves nicely. ; However, we abandon this prompts mess for ANSI GCL. #+(and gcl (not cltl2)) ; Let's avoid this mess for the more recent ANSI GCL (in-package "SYSTEM") #+(and gcl (not cltl2)) (progn (defvar *debug-prompt-suffix* "") ; See comment about ACL2 for how the following is patched from si::break-level. (defun break-level-for-acl2 (at &optional env) (eval '(acl2::print-proof-tree-finish acl2::*the-live-state*)) (let* ((*break-message* (if (stringp at) at *break-message*)) (*quit-tags* (cons (cons *break-level* *quit-tag*) *quit-tags*)) (*quit-tag* (cons nil nil)) (*break-level* (if (not at) *break-level* (cons t *break-level*))) (*ihs-base* (1+ *ihs-top*)) (*ihs-top* (1- (ihs-top))) (*current-ihs* *ihs-top*) (*frs-base* (or (sch-frs-base *frs-top* *ihs-base*) (1+ (frs-top)))) (*frs-top* (frs-top)) (*break-env* nil) (be *break-enable*) (*break-enable* (progn (if (stringp at) nil be))) ;(*standard-input* *terminal-io*) (*readtable* (or *break-readtable* *readtable*)) (*read-suppress* nil) (+ +) (++ ++) (+++ +++) (- -) (* *) (** **) (*** ***) (/ /) (// //) (/// ///) ) ; (terpri *error-output*) (unless (or be (not (stringp at))) (simple-backtrace) (break-quit (length (cdr *break-level*)))) (catch-fatal 1) (setq *interrupt-enable* t) (cond ((stringp at) (set-current)(terpri *error-output*) (setq *no-prompt* nil) ) (t (set-back at env))) (loop (setq +++ ++ ++ + + -) (cond (*no-prompt* (setq *no-prompt* nil)) (t ; ACL2 patch is in the following form, only (format *debug-io* "~&~a~a~a>~{~*>~}" (if (stringp at) "" "dbl:") (if (eq *package* (find-package 'user)) "" (package-name *package*)) *debug-prompt-suffix* *break-level*))) (force-output *error-output*) (when (catch 'step-continue (catch *quit-tag* (setq - (locally (declare (notinline read)) (dbl-read *debug-io* nil *top-eof*))) (when (eq - *top-eof*) (lisp::bye -1)) (let* ( break-command (values (multiple-value-list (LOCALLY (declare (notinline break-call evalhook)) (if (keywordp -)(setq - (cons - nil))) (cond ((and (consp -) (keywordp (car -))) (setq break-command t) (break-call (car -) (cdr -) 'si::break-command)) (t (evalhook - nil nil *break-env*))))))) (and break-command (eq (car values) :resume )(return)) (setq /// // // / / values *** ** ** * * (car /)) (fresh-line *debug-io*) (dolist (val /) (locally (declare (notinline prin1)) (prin1 val *debug-io*)) (terpri *debug-io*))) nil)) (terpri *debug-io*) (break-current)))))) #+(and gcl (not cltl2)) (in-package "ACL2") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Additional hacks for CCL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Also see the acl2h-init code. ; Bob Boyer uses the following at times. ; #+ccl ; (in-package "CCL") ; #+ccl ; (flet ((go-slow ; () ; (set-current-compiler-policy (new-compiler-policy :inline-self-calls #'false)) ; (set-current-file-compiler-policy (new-compiler-policy :inline-self-calls #'false)) ; (proclaim '(optimize (debug 3) (compilation-speed 0) ; (safety 3) (speed 0) (space 0))))) ; (go-slow)) ; The following two assignments seemed to speed up regression runs by about ; 7.7% as opposed to only the second, which seemed to have little effect. ; Binding these variables in LP, instead of this, didn't seem to provide any ; of that speed-up. ; NOTE: If you don't like these defaults, try Bob Boyer's approach: put these ; same forms in your ~/ccl-init.lisp file, but replacing nil with t. ; NOTE: The first of these seemed to be necessary even in 32-bit CCL r13193, ; where one might have expected that not to be the case. #+ccl (when (boundp 'ccl::*save-interactive-source-locations*) (setq ccl::*save-interactive-source-locations* nil)) #+ccl (when (boundp 'ccl::*save-source-locations*) (setq ccl::*save-source-locations* nil)) acl2-sources/acl2-startup-info.txt0000664002132200015000000000001112014713655016562 0ustar kaufmannacl2:release acl2-sources/akcl-acl2-trace.lisp0000666002132200015000000002061312222115527016274 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We don't intend this file to be compiled. ; TRACE stuff ; We put over into old-trace the macro for trace that comes with ACKL. ; Thus one can type (old-trace foo) and get the effect that (trace ; foo) would have previously provided. We do not guarantee that using ; old-trace works well with trace$, however. (cond ((null (macro-function 'old-trace)) (setf (macro-function 'old-trace) (macro-function 'trace)))) (cond ((null (macro-function 'old-untrace)) (setf (macro-function 'old-untrace) (macro-function 'untrace)))) (defmacro trace (&rest fns) (let (acl2-fns non-acl2-fns all-fns) (loop for fn in fns do (if (function-symbolp (cond ((symbolp fn) fn) ((and (consp fn) (symbolp (car fn))) (car fn)) (t (error "Not a symbol or a cons of a symbol: ~s" fn))) (w *the-live-state*)) (push fn acl2-fns) (push fn non-acl2-fns))) (cons 'old-trace (progn ; Turn every element of acl2-fns into (list* fn fn ...). (setq acl2-fns (loop for x in acl2-fns collect (cond ((symbolp x) (list x x)) (t (list* (car x) (car x) (cdr x)))))) ; Trace the *1* functions too. Then every element of acl2-fns will have the ; form (list* fn original-fn ...). (dolist (x acl2-fns) (push (cons (*1*-symbol (car x)) (cdr x)) acl2-fns)) (dolist (fn acl2-fns) (push (progn (cond ((member :break (cdr fn)) (interface-er "Use of :break is not permitted in ~ TRACE. Consider :entry (progn ~ (break$) arglist) instead."))) (cons (car fn) (trace-fix-cond (trace-fix-entry (car fn) (trace-fix-exit (car fn) (cadr fn) (cddr fn)))))) all-fns)) (dolist (fn non-acl2-fns) (let* ((spec (if (symbolp fn) (list fn) fn)) (fn (car spec))) (push (cons fn (trace-fix-entry-raw fn (trace-fix-exit-raw fn (cdr spec)))) all-fns))) all-fns)))) (defmacro untrace (&rest fns) (cons 'old-untrace (let ((ans fns)) (dolist (fn fns) (push (*1*-symbol fn) ans)) ans))) (defun trace-ppr-gcl (direction x &aux (state *the-live-state*)) ; Unfortunately, in native GCL trace :entry and :exit cause a return ; value to be printed in addition to the side-effect printing. The > character ; is a pretty innocuous value to print. (let ((*inside-trace$* t)) (declare (special *inside-trace$*)) (cond ((eq direction :in) (f-put-global 'trace-level (1+f (f-get-global 'trace-level state)) state))) (let ((trace-evisc-tuple (trace-evisc-tuple)) (x (trace-hide-world-and-state x))) (ppr (eviscerate-top x (cadr trace-evisc-tuple) ;;; print-level (caddr trace-evisc-tuple) ;;; print-length (car trace-evisc-tuple) ;;; alist (table-alist 'evisc-table (w state)) (car (cddddr trace-evisc-tuple)) ;;; hiding-cars state) (+ 2 ; GCL starts "1>" in column 2 (first-trace-printing-column state)) (f-get-global 'trace-co state) state t)) (cond ((eq direction :out) (f-put-global 'trace-level (1-f (f-get-global 'trace-level state)) state))) '>)) (defun trace-fix-entry-raw (name l) (cond ((null l) (list :entry `(cons ',name (trace-hide-world-and-state si::arglist)))) ((or (atom l) (and (cdr l) (atom (cdr l)))) (error "A trace spec must be a true-list, but yours ends in ~s." l)) ((eq (car l) :entry) (list* :entry `(cons ',name (trace-hide-world-and-state (let ((arglist si::arglist)) ,(cadr l)))) (cddr l))) (t (list* (car l) (cadr l) (trace-fix-entry-raw name (cddr l)))))) (defun trace-fix-entry (name l) (cond ((endp l) (list :entry `(trace-ppr-gcl :in (cons ',name si::arglist)))) ((eq (car l) :entry) (list* :entry `(trace-ppr-gcl :in (cons ',name (let ((arglist si::arglist)) ,(cadr l)))) (cddr l))) (t (list* (car l) (cadr l) (trace-fix-entry name (cddr l)))))) #-acl2-mv-as-values (defun trace-values (name) (list* 'list '(car values) (let ((mul (trace-multiplicity name *the-live-state*))) (cond ((or (null mul) (eql mul 1)) nil) (t (mv-refs-fn (1- mul))))))) #+acl2-mv-as-values (defun trace-values (name) (declare (ignore name)) 'values) #-acl2-mv-as-values (defun make-nths (i n var) (if (zerop n) nil (cons `(nth ,i ,var) (make-nths (1+ i) (1- n) var)))) (defun trace-fix-exit-raw (name l) (cond ((endp l) (list :exit `(cons ',name (trace-hide-world-and-state ,(trace-values name))))) ((eq (car l) :exit) (list* :exit `(cons ',name (trace-hide-world-and-state (let ((arglist si::arglist)) (declare (ignorable arglist)) ,(cadr l)))) (cddr l))) (t (list* (car l) (cadr l) (trace-fix-exit-raw name (cddr l)))))) (defun trace-fix-exit (name original-name l &aux (state *the-live-state*)) (cond ((endp l) (list :exit (protect-mv `(trace-ppr-gcl :out (cons ',name ,(trace-values original-name))) (trace-multiplicity original-name state)))) ((eq (car l) :exit) (list* :exit (protect-mv `(trace-ppr-gcl :out (cons ',name (let* ((values ,(trace-values original-name)) (arglist si::arglist)) (declare (ignorable values arglist)) ,(cadr l)))) (trace-multiplicity original-name state)) (cddr l))) (t (list* (car l) (cadr l) (trace-fix-exit name original-name (cddr l)))))) (defun trace-fix-cond (trace-spec) (if (member-eq :cond trace-spec) ; optimization (loop for tail on trace-spec append (if (eq (car tail) :cond) (prog1 (list :cond `(let ((arglist si::arglist)) ,(cadr tail))) (setq tail (cdr tail))) (list (car tail)))) trace-spec)) acl2-sources/allegro-acl2-trace.lisp0000666002132200015000000002337512222115527017017 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; This file originally written by: Robert Krug ; email: rkrug@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We don't intend this file to be compiled. ; TRACE stuff ; Allegro's trace facilities are somewhat limited. However it does have a ; function, advise, which is sufficiently general to allow it to imitate GCL's ; trace facilities as provided within ACL2. See Franz's documentation for ; information on advise and unadvise. ; We put over into old-trace the macro for trace that comes with Allegro. ; Thus one can type (old-trace foo) and get the effect that (trace ; foo) would have previously provided. We do not guarantee that using ; old-trace works well with trace$, however. (cond ((null (macro-function 'old-trace)) (setf (macro-function 'old-trace) (macro-function 'trace)))) (cond ((null (macro-function 'old-untrace)) (setf (macro-function 'old-untrace) (macro-function 'untrace)))) ; The variables *trace-arglist* and *trace-values* will contain the ; cleaned up arglist and values of a traced function. The alist ; *trace-sublis* allows one to refer to these variables by more ; common names. (defvar *trace-arglist*) (defvar *trace-values*) (defconst *trace-sublis* '((values . *trace-values*) (si::values . *trace-values*) (arglist . *trace-arglist*) (si::arglist . *trace-arglist*))) ; What I am trying to do: ; Trace is called with a list of functions to be traced. Each element of ; this list can be; ; (Let foo be defined in common lisp but not in ACL2, and ; bar be defined within ACL2.) ; 1. the name of a non-acl2 function --- foo, ; 2. the name of an acl2 function --- bar, ; 3. a list whose only element is the name of a non-acl2 function --- (foo), ; 4. a list whose only element is the name of an acl2 function --- (bar), ; 5. a list whose car is the name of a non-acl2 function and whose ; cdr may contain the keywords :entry and :exit, each of which ; are followed by a function whose value will be printed upon ; entry and exit respectively of the function being traced --- ; (foo :entry (list (list 'arg-one (car si::arglist)) ; (list 'arg-two (nth 1 si::arglist))))), ; 6. a list whose car is the name of an acl2 function and whose ; cdr may contain the keywords :entry and :exit, each of which ; are followed by a function whose value will be printed upon ; entry and exit respectively of the function being traced --- ; (bar :entry si::arglist ; :exit (if (eql (nth 1 si::arglist) ; (nth 0 values)) ; 'FAILED ; (pretty-print-arg (nth 1 values)))) ; ; In trace-pre-process we generate a new list as follows, where *1*bar denotes ; (*1*-symbol bar). ; 1. replacing foo with (foo foo), ; 2. replacing bar with (bar bar) & adding (*1*bar bar), ; 3, replacing (foo ...) with (foo foo ...) ; 4. replacing (bar ...) with (bar bar ...) & adding ((*1*bar ...) (bar ...)). ; ; In trace-process we generate suitable calls of advise and unadvise. ; ; Unless explicitly overridden by the :entry or :exit keywords, ; and print the ; arguments and returned values respectively. A minor amount of ; cleaning up is done, such as printing |*STATE*| instead of the ; entire state if it is one of the arguements or values. (defun trace-pre-process (lst) (let ((new-lst nil)) (dolist (x lst new-lst) (let ((sym (cond ((symbolp x) x) ((and (consp x) (symbolp (car x))) (car x)) (t (interface-er "Not a symbol or a cons of a symbol: ~s0" x))))) (if (function-symbolp sym (w *the-live-state*)) ; We have an ACL2 function. (cond ((symbolp x) (push (list (*1*-symbol x) x) new-lst) (push (list x x) new-lst)) (t (push (list* (*1*-symbol (car x)) (car x) (cdr x)) new-lst) (push (list* (car x) (car x) (cdr x)) new-lst))) ; We do not have an ACL2 function. (if (fboundp sym) (if (symbolp x) (push (list x x) new-lst) (push (list* (car x) (car x) (cdr x)) new-lst)) (interface-er "~s0 is not a bound function symbol." sym))))))) (defun trace-entry (name l) ; We construct the (excl:advise :before ...) form that performs the ; tracing on entry. (cond ((null l) `(excl:advise ,name :before nil nil (progn (setq *trace-arglist* si::arglist) (custom-trace-ppr :in (cons ',name (trace-hide-world-and-state *trace-arglist*)))))) ((eq (car l) :entry) `(excl:advise ,name :before nil nil (progn (setq *trace-arglist* si::arglist) (custom-trace-ppr :in (cons ',name (trace-hide-world-and-state ,(sublis *trace-sublis* (cadr l)))))))) (t (trace-entry name (cdr l))))) ; These next two functions were blindly copied from akcl-acl2-trace.lisp #-acl2-mv-as-values (defun trace-values (name) (list* 'list '(car values) (let ((mul (trace-multiplicity name *the-live-state*))) (cond ((or (null mul) (eql mul 1)) nil) (t (mv-refs-fn (1- mul))))))) #+acl2-mv-as-values (defun trace-values (name) (declare (ignore name)) 'values) #-acl2-mv-as-values (defun make-nths (i n var) (if (zerop n) nil (cons `(nth ,i ,var) (make-nths (1+ i) (1- n) var)))) (defun trace-exit (name original-name l &aux (state *the-live-state*)) ; We construct the (excl:advise :after ...) form that performs the ; tracing on entry. (cond ((null l) `(excl:advise ,name :after nil nil (progn (setq *trace-values* (trace-hide-world-and-state ,(trace-values original-name))) ,(protect-mv `(custom-trace-ppr :out (cons ',name *trace-values*)) (trace-multiplicity original-name state))))) ((eq (car l) :exit) (let ((multiplicity (trace-multiplicity original-name state))) `(excl:advise ,name :after nil nil (progn ,(protect-mv `(progn (setq *trace-values* (trace-hide-world-and-state ,(trace-values original-name))) (setq *trace-arglist* (trace-hide-world-and-state si::arglist))) multiplicity) ,(protect-mv `(custom-trace-ppr :out (cons ',name ,(sublis *trace-sublis* (cadr l)))) multiplicity))))) (t (trace-exit name original-name (cdr l))))) (defun traced-fns-lst (lst) (list 'QUOTE (mapcar #'car lst))) (defun trace-process (lst) ; We perform a little error checking, and gather together all the (excl:advise ; ...) functions. (let ((new-lst (list (traced-fns-lst lst)))) ; for the returned value (dolist (x lst new-lst) (cond ((member :cond (cddr x)) (interface-er "The use of :cond is not supported in ~ Allegro.")) ((member :break (cddr x)) (interface-er "The use of :break is not supported in ~ Allegro. However, you can use either ~ (~s0 :entry (break$)) or (~s0 :exit (break$)). ~ See any Lisp documentation for more on ~ break and its options." (car x))) (t (push (trace-exit (car x) (cadr x) (cddr x)) new-lst) (push (trace-entry (car x) (cddr x)) new-lst) (push `(excl:unadvise ,(car x)) new-lst)))))) (excl:without-package-locks (defmacro trace (&rest fns) (if fns (cons 'progn (trace-process (trace-pre-process fns))) '(excl::advised-functions)))) (excl:without-package-locks (defmacro untrace (&rest fns) (if (null fns) '(prog1 (excl::advised-functions) (excl:unadvise)) (cons 'progn (let ((ans nil)) (dolist (fn fns ans) (push `(excl:unadvise ,fn) ans) (push `(excl:unadvise ,(*1*-symbol fn)) ans))))))) acl2-sources/all-files.txt0000664002132200015000000000612312063721627015174 0ustar kaufmannacl2acl2-characters acl2-check.lisp acl2-customization-files acl2-fns.lisp acl2-init.lisp acl2.lisp # acl2-proclaims.lisp -- generated file # acl2-status.txt -- generated file acl2-startup-info.txt # acl2r.lisp -- generated file akcl-acl2-trace.lisp # all-files-developers.txt -- developer file # all-files-extra.txt -- developer file allegro-acl2-trace.lisp all-files.txt axioms.lisp basis.lisp bdd.lisp # bin -- under svn control but not distributed # books -- no longer distributed at UT boot-strap-pass-2.lisp build-allegro-exe.cl defpkgs.lisp defthm.lisp defuns.lisp doc emacs futures-raw.lisp GNUmakefile graphics history-management.lisp hons.lisp hons-raw.lisp induct.lisp init.lisp installation interface-raw.lisp ld.lisp LICENSE linear-a.lisp linear-b.lisp Makefile mcl-acl2-startup.lisp memoize.lisp memoize-raw.lisp multi-threading-raw.lisp new.html non-linear.lisp openmcl-acl2-trace.lisp other-events.lisp other-processes.lisp other-releases.html parallel.lisp parallel-raw.lisp # p.lisp -- developer file proof-checker-a.lisp proof-checker-b.lisp proof-checker-pkg.lisp prove.lisp rewrite.lisp # The following is distributed but is not in the svn repository: saved save-gprof.lsp serialize.lisp serialize-raw.lisp simplify.lisp sum-list-example.lisp # The following is distributed but is not in the svn repository: TAGS tau.lisp translate.lisp tutorial.lisp type-set-a.lisp type-set-b.lisp # unversioned-files.txt -- under svn control but not distributed workshops.html acl2-customization-files: parallel-full.lisp parallel-resource-based.lisp parallel-top-level.lisp pseudo-parallel.lisp README serial.lisp doc: # The following is distributed but is not in the svn repository: acl2-code-size.txt create-acl2-code-size create-acl2-html create-acl2-tex create-acl2-texinfo # The following is distributed but is not in the svn repository: EMACS # The following is distributed but is not in the svn repository: HTML make-texinfo.el README # The following is distributed but is not in the svn repository: TEX # unversioned-files.txt -- under svn control but not distributed write-acl2-code-size.lisp write-acl2-html.lisp write-acl2-texinfo.lisp # The following is distributed but is not in the svn repository: doc/EMACS: acl2-doc-emacs.info acl2-doc-emacs.info-* # acl2-doc-emacs.texinfo -- optional temporary file # The following is distributed but is not in the svn repository: doc/HTML: LICENSE installation *.html *.gif # The following is distributed but is not in the svn repository: doc/HTML/installation: installation.html installing-make.html misc.html obtaining-and-installing.html requirements.html using.html windows7.html windows-gcl-jared.html # The following is distributed but is not in the svn repository: doc/TEX: # No longer distributed: acl2-book.dvi.gz # No longer distributed: acl2-book.ps.gz (but available from publications page) emacs: emacs-acl2.el monitor.el README graphics: *.gif installation: installation.html installing-make.html misc.html obtaining-and-installing.html requirements.html using.html windows7.html windows-gcl-jared.html # The following is distributed but is not in the svn repository: saved: acl2-sources/axioms.lisp0000664002132200015000000770411612222115527014763 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; This file, axioms.lisp, serves two purposes. First, it describes ; the theory of ACL2 by enumerating the axioms and definitions. ; Second, it implements in Common Lisp those functions of the theory ; which are not already provided in Common Lisp. In some cases, the ; implementation of a function is identical to its axiomatization (cf. ; implies). In other cases, we provide functions whose semantics are ; applicative but whose implementations are decidely ``von ; Neumann-esque''. For example, we implement the array, property ; list, and io primitives with non-applicative techniques. ; This file is read by Common Lisp in two ways. First, we bring ACL2 ; into its initial state with the function boot-strap, which loads ; this file. Second, this file is read and compiled in the ; implementation of ACL2 itself. To support these two readings, we ; use the #+ and #- read macro feature of Common Lisp. While we are ; loading this file in boot-strap, we arrange for *features* to ; contain the symbol :acl2-loop-only; otherwise, *features* does not ; contain :acl2-loop-only. Thus, during boot-strap, forms immediately ; preceded by #+acl2-loop-only are ``seen'', whereas those ; immediately preceded by #-acl2-loop-only are invisible. The ; converse is true when we are compiling and loading the code for ; ACL2. ; If a symbol described in CLTL is axiomatized here, then we give it ; exactly the same semantics as it has in CLTL, under restrictions for ; which we check. (Actually, this is currently a lie about DEFUN, ; DEFMACRO, and PROGN, but we will provide someday a check that that ; those are only used in files in ways such that their ACL2 and Common ; Lisp meanings are prefectly consistent.) Thus, when we talk about ; +, we really mean the Common Lisp +. However, our + does not handle ; floating point numbers, so there is a guard on + that checks that ; its args are rationals. The symbols in the list ; acl2::*common-lisp-symbols-from-main-lisp-package* are the symbols ; that we take as having a meaning in Common Lisp. If a user wishes ; access to these in a package, then he can use the permanent value of ; the global *common-lisp-symbols-from-main-lisp-package* as an import ; list for defpkg. ; If we use a symbol that has a $ suffix, it is a symbol we have ; defined with a meaning that it is similar to the Common Lisp symbol ; without the $ suffix, but different in some way, e.g. princ$ takes a ; state arg and returns a state. (in-package "ACL2") ; Leave the following as the second form in axioms.lisp. It is read ; by acl2.lisp. Leave the acl2:: prefix there, too. ; We are aware that as of this writing, various Lisp implementations deviate ; from the dpANS specification of the external symbols of the main Lisp ; package. However, we will guarantee that variable names and logical names ; that lie in the main Lisp package will all come from this list, and in the ; case of variables, we will guarantee that they are not special variables. ; Note that however we handle this constant, it is crucial that its value be ; independent of the implementation, lest we can prove something about its ; length (say) in one Lisp that is false in another. Our requirement on this ; list is that it allow the compiler to deal correctly with Common Lisp functions ; such as CAR that we are bringing into the ACL2 environment, and the dpANS list ; certainly satisfies that requirement. (acl2::defconst acl2::*common-lisp-symbols-from-main-lisp-package* ; From the info page for dpANS, node "Symbols in the COMMON-LISP Package." ; The comments are from that page as well, though we have inserted "; " ; in front of each. '( ; The figures on the next twelve pages contain a complete enumeration of the ; 978 external symbols in the COMMON-LISP package. &allow-other-keys *print-miser-width* &aux *print-pprint-dispatch* &body *print-pretty* &environment *print-radix* &key *print-readably* &optional *print-right-margin* &rest *query-io* &whole *random-state* * *read-base* ** *read-default-float-format* *** *read-eval* *break-on-signals* *read-suppress* *compile-file-pathname* *readtable* *compile-file-truename* *standard-input* *compile-print* *standard-output* *compile-verbose* *terminal-io* *debug-io* *trace-output* *debugger-hook* + *default-pathname-defaults* ++ *error-output* +++ *features* - *gensym-counter* / *load-pathname* // *load-print* /// *load-truename* /= *load-verbose* 1+ *macroexpand-hook* 1- *modules* < *package* <= *print-array* = *print-base* > *print-case* >= *print-circle* abort *print-escape* abs *print-gensym* acons *print-length* acos *print-level* acosh *print-lines* add-method ; Figure 1-4: Symbols in the COMMON-LISP package (part one of twelve). adjoin atom boundp adjust-array base-char break adjustable-array-p base-string broadcast-stream allocate-instance bignum broadcast-stream-streams alpha-char-p bit built-in-class alphanumericp bit-and butlast and bit-andc1 byte append bit-andc2 byte-position apply bit-eqv byte-size apropos bit-ior caaaar apropos-list bit-nand caaadr aref bit-nor caaar arithmetic-error bit-not caadar arithmetic-error-operands bit-orc1 caaddr arithmetic-error-operation bit-orc2 caadr array bit-vector caar array-dimension bit-vector-p cadaar array-dimension-limit bit-xor cadadr array-dimensions block cadar array-displacement boole caddar array-element-type boole-1 cadddr array-has-fill-pointer-p boole-2 caddr array-in-bounds-p boole-and cadr array-rank boole-andc1 call-arguments-limit array-rank-limit boole-andc2 call-method array-row-major-index boole-c1 call-next-method array-total-size boole-c2 car array-total-size-limit boole-clr case arrayp boole-eqv catch ash boole-ior ccase asin boole-nand cdaaar asinh boole-nor cdaadr assert boole-orc1 cdaar assoc boole-orc2 cdadar assoc-if boole-set cdaddr assoc-if-not boole-xor cdadr atan boolean cdar atanh both-case-p cddaar ; Figure 1-5: Symbols in the COMMON-LISP package (part two of twelve). cddadr clear-input copy-tree cddar clear-output cos cdddar close cosh cddddr clrhash count cdddr code-char count-if cddr coerce count-if-not cdr compilation-speed ctypecase ceiling compile debug cell-error compile-file decf cell-error-name compile-file-pathname declaim cerror compiled-function declaration change-class compiled-function-p declare char compiler-macro decode-float char-code compiler-macro-function decode-universal-time char-code-limit complement defclass char-downcase complex defconstant char-equal complexp defgeneric char-greaterp compute-applicable-methods define-compiler-macro char-int compute-restarts define-condition char-lessp concatenate define-method-combination char-name concatenated-stream define-modify-macro char-not-equal concatenated-stream-streams define-setf-expander char-not-greaterp cond define-symbol-macro char-not-lessp condition defmacro char-upcase conjugate defmethod char/= cons defpackage char< consp defparameter char<= constantly defsetf char= constantp defstruct char> continue deftype char>= control-error defun character copy-alist defvar characterp copy-list delete check-type copy-pprint-dispatch delete-duplicates cis copy-readtable delete-file class copy-seq delete-if class-name copy-structure delete-if-not class-of copy-symbol delete-package ; Figure 1-6: Symbols in the COMMON-LISP package (part three of twelve). denominator eq deposit-field eql describe equal describe-object equalp destructuring-bind error digit-char etypecase digit-char-p eval directory eval-when directory-namestring evenp disassemble every division-by-zero exp do export do* expt do-all-symbols extended-char do-external-symbols fboundp do-symbols fceiling documentation fdefinition dolist ffloor dotimes fifth double-float file-author double-float-epsilon file-error double-float-negative-epsilon file-error-pathname dpb file-length dribble file-namestring dynamic-extent file-position ecase file-stream echo-stream file-string-length echo-stream-input-stream file-write-date echo-stream-output-stream fill ed fill-pointer eighth find elt find-all-symbols encode-universal-time find-class end-of-file find-if endp find-if-not enough-namestring find-method ensure-directories-exist find-package ensure-generic-function find-restart ; Figure 1-7: Symbols in the COMMON-LISP package (part four of twelve). find-symbol get-internal-run-time finish-output get-macro-character first get-output-stream-string fixnum get-properties flet get-setf-expansion float get-universal-time float-digits getf float-precision gethash float-radix go float-sign graphic-char-p floating-point-inexact handler-bind floating-point-invalid-operation handler-case floating-point-overflow hash-table floating-point-underflow hash-table-count floatp hash-table-p floor hash-table-rehash-size fmakunbound hash-table-rehash-threshold force-output hash-table-size format hash-table-test formatter host-namestring fourth identity fresh-line if fround ignorable ftruncate ignore ftype ignore-errors funcall imagpart function import function-keywords in-package function-lambda-expression incf functionp initialize-instance gcd inline generic-function input-stream-p gensym inspect gentemp integer get integer-decode-float get-decoded-time integer-length get-dispatch-macro-character integerp get-internal-real-time interactive-stream-p ; Figure 1-8: Symbols in the COMMON-LISP package (part five of twelve). intern lisp-implementation-type internal-time-units-per-second lisp-implementation-version intersection list invalid-method-error list* invoke-debugger list-all-packages invoke-restart list-length invoke-restart-interactively listen isqrt listp keyword load keywordp load-logical-pathname-translations labels load-time-value lambda locally lambda-list-keywords log lambda-parameters-limit logand last logandc1 lcm logandc2 ldb logbitp ldb-test logcount ldiff logeqv least-negative-double-float logical-pathname least-negative-long-float logical-pathname-translations least-negative-normalized-double-float logior least-negative-normalized-long-float lognand least-negative-normalized-short-float lognor least-negative-normalized-single-float lognot least-negative-short-float logorc1 least-negative-single-float logorc2 least-positive-double-float logtest least-positive-long-float logxor least-positive-normalized-double-float long-float least-positive-normalized-long-float long-float-epsilon least-positive-normalized-short-float long-float-negative-epsilon least-positive-normalized-single-float long-site-name least-positive-short-float loop least-positive-single-float loop-finish length lower-case-p let machine-instance let* machine-type ; Figure 1-9: Symbols in the COMMON-LISP package (part six of twelve). machine-version mask-field macro-function max macroexpand member macroexpand-1 member-if macrolet member-if-not make-array merge make-broadcast-stream merge-pathnames make-concatenated-stream method make-condition method-combination make-dispatch-macro-character method-combination-error make-echo-stream method-qualifiers make-hash-table min make-instance minusp make-instances-obsolete mismatch make-list mod make-load-form most-negative-double-float make-load-form-saving-slots most-negative-fixnum make-method most-negative-long-float make-package most-negative-short-float make-pathname most-negative-single-float make-random-state most-positive-double-float make-sequence most-positive-fixnum make-string most-positive-long-float make-string-input-stream most-positive-short-float make-string-output-stream most-positive-single-float make-symbol muffle-warning make-synonym-stream multiple-value-bind make-two-way-stream multiple-value-call makunbound multiple-value-list map multiple-value-prog1 map-into multiple-value-setq mapc multiple-values-limit mapcan name-char mapcar namestring mapcon nbutlast maphash nconc mapl next-method-p maplist nil ; Figure 1-10: Symbols in the COMMON-LISP package (part seven of twelve). nintersection package-error ninth package-error-package no-applicable-method package-name no-next-method package-nicknames not package-shadowing-symbols notany package-use-list notevery package-used-by-list notinline packagep nreconc pairlis nreverse parse-error nset-difference parse-integer nset-exclusive-or parse-namestring nstring-capitalize pathname nstring-downcase pathname-device nstring-upcase pathname-directory nsublis pathname-host nsubst pathname-match-p nsubst-if pathname-name nsubst-if-not pathname-type nsubstitute pathname-version nsubstitute-if pathnamep nsubstitute-if-not peek-char nth phase nth-value pi nthcdr plusp null pop number position numberp position-if numerator position-if-not nunion pprint oddp pprint-dispatch open pprint-exit-if-list-exhausted open-stream-p pprint-fill optimize pprint-indent or pprint-linear otherwise pprint-logical-block output-stream-p pprint-newline package pprint-pop ; Figure 1-11: Symbols in the COMMON-LISP package (part eight of twelve). pprint-tab read-char pprint-tabular read-char-no-hang prin1 read-delimited-list prin1-to-string read-from-string princ read-line princ-to-string read-preserving-whitespace print read-sequence print-not-readable reader-error print-not-readable-object readtable print-object readtable-case print-unreadable-object readtablep probe-file real proclaim realp prog realpart prog* reduce prog1 reinitialize-instance prog2 rem progn remf program-error remhash progv remove provide remove-duplicates psetf remove-if psetq remove-if-not push remove-method pushnew remprop quote rename-file random rename-package random-state replace random-state-p require rassoc rest rassoc-if restart rassoc-if-not restart-bind ratio restart-case rational restart-name rationalize return rationalp return-from read revappend read-byte reverse ; Figure 1-12: Symbols in the COMMON-LISP package (part nine of twelve). room simple-bit-vector rotatef simple-bit-vector-p round simple-condition row-major-aref simple-condition-format-arguments rplaca simple-condition-format-control rplacd simple-error safety simple-string satisfies simple-string-p sbit simple-type-error scale-float simple-vector schar simple-vector-p search simple-warning second sin sequence single-float serious-condition single-float-epsilon set single-float-negative-epsilon set-difference sinh set-dispatch-macro-character sixth set-exclusive-or sleep set-macro-character slot-boundp set-pprint-dispatch slot-exists-p set-syntax-from-char slot-makunbound setf slot-missing setq slot-unbound seventh slot-value shadow software-type shadowing-import software-version shared-initialize some shiftf sort short-float space short-float-epsilon special short-float-negative-epsilon special-operator-p short-site-name speed signal sqrt signed-byte stable-sort signum standard simple-array standard-char simple-base-string standard-char-p ; Figure 1-13: Symbols in the COMMON-LISP package (part ten of twelve). standard-class sublis standard-generic-function subseq standard-method subsetp standard-object subst step subst-if storage-condition subst-if-not store-value substitute stream substitute-if stream-element-type substitute-if-not stream-error subtypep stream-error-stream svref stream-external-format sxhash streamp symbol string symbol-function string-capitalize symbol-macrolet string-downcase symbol-name string-equal symbol-package string-greaterp symbol-plist string-left-trim symbol-value string-lessp symbolp string-not-equal synonym-stream string-not-greaterp synonym-stream-symbol string-not-lessp t string-right-trim tagbody string-stream tailp string-trim tan string-upcase tanh string/= tenth string< terpri string<= the string= third string> throw string>= time stringp trace structure translate-logical-pathname structure-class translate-pathname structure-object tree-equal style-warning truename ; Figure 1-14: Symbols in the COMMON-LISP package (part eleven of twelve). truncate values-list two-way-stream variable two-way-stream-input-stream vector two-way-stream-output-stream vector-pop type vector-push type-error vector-push-extend type-error-datum vectorp type-error-expected-type warn type-of warning typecase when typep wild-pathname-p unbound-slot with-accessors unbound-slot-instance with-compilation-unit unbound-variable with-condition-restarts undefined-function with-hash-table-iterator unexport with-input-from-string unintern with-open-file union with-open-stream unless with-output-to-string unread-char with-package-iterator unsigned-byte with-simple-restart untrace with-slots unuse-package with-standard-io-syntax unwind-protect write update-instance-for-different-class write-byte update-instance-for-redefined-class write-char upgraded-array-element-type write-line upgraded-complex-part-type write-sequence upper-case-p write-string use-package write-to-string use-value y-or-n-p user-homedir-pathname yes-or-no-p values zerop ; Figure 1-15: Symbols in the COMMON-LISP package (part twelve of twelve). )) ; Leave this here. It is read when loading acl2.lisp. (defconst *common-lisp-specials-and-constants* ; In acl2-check.lisp we ensure that this constant is consistent with the ; underlying Common Lisp. The draft proposed ANSI standard for Common Lisp ; specifies (see "The COMMON-LISP Package") exactly which symbols are external ; symbols of the Common Lisp package (not just initially, but always). It also ; states, in "Constraints on the COMMON-LISP Package for Conforming ; Implementations," that: "conforming programs can use external symbols of the ; COMMON-LISP package as the names of local lexical variables with confidence ; that those names have not been proclaimed special by the implementation ; unless those symbols are names of standardized global variables." ; Unfortunately, we cannot seem to find out in a direct fashion just which ; variables are standardized global variables, i.e., global variables defined ; in the standard. Our check handles this. ; Shortly before releasing Version 2.5 (6/00), we have checked that the above ; form returns NIL on Unix systems running Allegro 5.0 and 5.0.1 and GCL 2.2.1 ; and 2.2.2, on a Windows 98 system (via John Cowles) running Allegro 5.0.1, ; and (after defining the requisite constants) on CMU Common Lisp 18a on a Unix ; system at UT. ; It is completely acceptable to add symbols to this list. If one certifies a ; book in such an ACL2, it will be a legal certification in an ACL2 in which ; the following list has not been modified. The only potential source of ; concern here is if one certifies a book in an ACL2 where this list has not ; been modified and then includes it, without recertification, in an ACL2 where ; this list has been added to. At this point we have not checked that such an ; include-book would catch an inappropriate use of one of those added symbols. ; But that seems a relatively minor concern. '(* ** *** *BREAK-ON-SIGNALS* *COMPILE-FILE-PATHNAME* *COMPILE-FILE-TRUENAME* *COMPILE-PRINT* *COMPILE-VERBOSE* *DEBUG-IO* *DEBUGGER-HOOK* *DEFAULT-PATHNAME-DEFAULTS* *ERROR-OUTPUT* *FEATURES* *GENSYM-COUNTER* *LOAD-PATHNAME* *LOAD-PRINT* *LOAD-TRUENAME* *LOAD-VERBOSE* *MACROEXPAND-HOOK* *MODULES* *PACKAGE* *PRINT-ARRAY* *PRINT-BASE* *PRINT-CASE* *PRINT-CIRCLE* *PRINT-ESCAPE* *PRINT-GENSYM* *PRINT-LENGTH* *PRINT-LEVEL* *PRINT-LINES* *PRINT-MISER-WIDTH* *PRINT-PPRINT-DISPATCH* *PRINT-PRETTY* *PRINT-RADIX* *PRINT-READABLY* *PRINT-RIGHT-MARGIN* *QUERY-IO* *RANDOM-STATE* *READ-BASE* *READ-DEFAULT-FLOAT-FORMAT* *READ-EVAL* *READ-SUPPRESS* *READTABLE* *STANDARD-INPUT* *STANDARD-OUTPUT* *TERMINAL-IO* *TRACE-OUTPUT* + ++ +++ - / // /// ARRAY-DIMENSION-LIMIT ARRAY-RANK-LIMIT ARRAY-TOTAL-SIZE-LIMIT BOOLE-1 BOOLE-2 BOOLE-AND BOOLE-ANDC1 BOOLE-ANDC2 BOOLE-C1 BOOLE-C2 BOOLE-CLR BOOLE-EQV BOOLE-IOR BOOLE-NAND BOOLE-NOR BOOLE-ORC1 BOOLE-ORC2 BOOLE-SET BOOLE-XOR CALL-ARGUMENTS-LIMIT CHAR-CODE-LIMIT DOUBLE-FLOAT-EPSILON DOUBLE-FLOAT-NEGATIVE-EPSILON INTERNAL-TIME-UNITS-PER-SECOND LAMBDA-LIST-KEYWORDS LAMBDA-PARAMETERS-LIMIT LEAST-NEGATIVE-DOUBLE-FLOAT LEAST-NEGATIVE-LONG-FLOAT LEAST-NEGATIVE-NORMALIZED-DOUBLE-FLOAT LEAST-NEGATIVE-NORMALIZED-LONG-FLOAT LEAST-NEGATIVE-NORMALIZED-SHORT-FLOAT LEAST-NEGATIVE-NORMALIZED-SINGLE-FLOAT LEAST-NEGATIVE-SHORT-FLOAT LEAST-NEGATIVE-SINGLE-FLOAT LEAST-POSITIVE-DOUBLE-FLOAT LEAST-POSITIVE-LONG-FLOAT LEAST-POSITIVE-NORMALIZED-DOUBLE-FLOAT LEAST-POSITIVE-NORMALIZED-LONG-FLOAT LEAST-POSITIVE-NORMALIZED-SHORT-FLOAT LEAST-POSITIVE-NORMALIZED-SINGLE-FLOAT LEAST-POSITIVE-SHORT-FLOAT LEAST-POSITIVE-SINGLE-FLOAT LONG-FLOAT-EPSILON LONG-FLOAT-NEGATIVE-EPSILON MOST-NEGATIVE-DOUBLE-FLOAT MOST-NEGATIVE-FIXNUM MOST-NEGATIVE-LONG-FLOAT MOST-NEGATIVE-SHORT-FLOAT MOST-NEGATIVE-SINGLE-FLOAT MOST-POSITIVE-DOUBLE-FLOAT MOST-POSITIVE-FIXNUM MOST-POSITIVE-LONG-FLOAT MOST-POSITIVE-SHORT-FLOAT MOST-POSITIVE-SINGLE-FLOAT MULTIPLE-VALUES-LIMIT NIL PI SHORT-FLOAT-EPSILON SHORT-FLOAT-NEGATIVE-EPSILON SINGLE-FLOAT-EPSILON SINGLE-FLOAT-NEGATIVE-EPSILON T ; Added in Version 2.6 to support Allegro 6.0 on Windows 2000: REPLACE FILL CHARACTER = ; Added in Version 2.6 to support GCL on Windows: BREAK PRIN1 )) (defconst *stobj-inline-declare* ; This constant is being introduced in v2-8. In this file it is only used in ; raw Lisp, specifically in the progn just below. But it is also used in ; defstobj-field-fns-raw-defs so we define it in the ACL2 loop. '(declare (stobj-inline-fn t))) ; Essay on Hidden Packages ; Before Version_2.8, ACL2 was unsound because of a hole in its handling of ; packages. The books in the example below can all be certified in ; Version_2.7, including the top-level book top.lisp, which concludes with a ; proof of nil. The details are slightly tricky, but the basic idea is simple: ; it was possible for traces of a defpkg event, including the axiom it added ; about symbol-package-name, to disappear by making include-books local. And ; thus, it was possible to prove contradictory theorems, using contradictory ; defpkg events in different locally included books, about the ; symbol-package-name of a symbol. One solution would be to disallow defpkg ; events in the context of a local include-book (much as we do for defaxiom), ; but that is too restrictive to be practical, especially since non-local ; include-book forms are prohibited inside encapsulate. So instead we track ; such "hidden" defpkg events; more on that below. ; Here is the example promised above. The idea is to define a package "FOO" ; that does not import any symbol of name "A", so that the symbol FOO::A has ; symbol-package-name "FOO". But we do this twice, where one time package ; "FOO" imports ACL2::B and the other time it does not. The two cases ; introduce symbols (wit1) and (wit2), which we can prove are equal, basically ; because both are FOO::A. But the result of interning "B" in the package of ; (wit1) or (wit2) is "FOO" in one case and "ACL2" in the other, which allows ; us to prove nil. We have tried simpler approaches but ACL2 caught us in ; those cases. We use local include-books below in order to avoid some of ; those catches by avoiding the use of FOO:: in wit1.lisp and wit2.lisp. ; ;;; file top.lisp ; ; (in-package "ACL2") ; ; (include-book "wit1") ; (include-book "wit2") ; ; ; The idea: ; ; (wit1) = (wit2) by symbol-equality ; ; But by evaluation (see wit1-prop and wit2-prop in the included books): ; ; (symbol-package-name (intern-in-package-of-symbol "B" (wit1))) = "FOO" ; ; (symbol-package-name (intern-in-package-of-symbol "B" (wit2))) = "ACL2" ; ; (defthm bug ; nil ; :hints (("Goal" :use (wit1-prop ; wit2-prop ; (:instance symbol-equality ; (s1 (wit1)) ; (s2 (wit2)))))) ; :rule-classes nil) ; ; ;;; file wit1.lisp ; ; (in-package "ACL2") ; ; (local (include-book "sub1")) ; ; (encapsulate ; ((wit1 () t)) ; (local (defun wit1 () (sub1))) ; (local (in-theory (disable (wit1)))) ; (defthm wit1-prop ; (and (symbolp (wit1)) ; (equal (symbol-name (wit1)) "A") ; (equal (symbol-package-name (wit1)) "FOO") ; (equal (symbol-package-name ; (intern-in-package-of-symbol "B" (wit1))) ; "FOO")) ; :rule-classes nil)) ; ; ;;; file sub1.lisp ; ; (in-package "ACL2") ; ; ; Portcullis: ; ; (defpkg "FOO" nil) ; ; (encapsulate ; ((sub1 () t)) ; (local (defun sub1 () 'foo::a)) ; (defthm sub1-prop ; (and (symbolp (sub1)) ; (equal (symbol-name (sub1)) "A") ; (equal (symbol-package-name (sub1)) "FOO") ; (equal (symbol-package-name ; (intern-in-package-of-symbol "B" (sub1))) ; "FOO")))) ; ; ;;; file wit2.lisp ; ; (in-package "ACL2") ; ; (local (include-book "sub2")) ; ; (encapsulate ; ((wit2 () t)) ; (local (defun wit2 () (sub2))) ; (local (in-theory (disable (wit2)))) ; (defthm wit2-prop ; (and (symbolp (wit2)) ; (equal (symbol-name (wit2)) "A") ; (equal (symbol-package-name (wit2)) "FOO") ; (equal (symbol-package-name ; (intern-in-package-of-symbol "B" (wit2))) ; "ACL2")) ; :rule-classes nil)) ; ; ;;; file sub2.lisp ; ; (in-package "ACL2") ; ; ; Portcullis: ; ; (defpkg "FOO" '(b)) ; ; (encapsulate ; ((sub2 () t)) ; (local (defun sub2 () 'foo::a)) ; (defthm sub2-prop ; (and (symbolp (sub2)) ; (equal (symbol-name (sub2)) "A") ; (equal (symbol-package-name (sub2)) "FOO") ; (equal (symbol-package-name ; (intern-in-package-of-symbol "B" (sub2))) ; "ACL2")))) ; ; ;;; file sub1.acl2 (portcullis for sub1.lisp) ; ; (value :q) ; (lp) ; (defpkg "FOO" nil) ; (certify-book "sub1" 1) ; ; ;;; file sub2.acl2 (portcullis for sub2.lisp) ; ; (value :q) ; (lp) ; (defpkg "FOO" '(b)) ; (certify-book "sub2" 1) ; The key to disallowing this unfortunate exploitation of defpkg axioms is to ; maintain an invariant, which we call "the package invariant on logical ; worlds." Roughly put, this invariant states that if the world depends in any ; way on a defpkg event, then that defpkg event occurs explicitly in that ; world. (This invariant, like many others, depends on not having executed any ; event in the world when state global ld-skip-proofsp has a non-nil value. ; Note that we guarantee that this property holds for any certification world; ; see chk-acceptable-certify-book.) Let us say that a defpkg event "supports" ; a world if it is either in that world or it is in some book (including its ; portcullis) that is hereditarily included in the current world via a chain of ; include-book events, some of which may be local to books or to encapsulate ; events. Then we can be more precise by stating the package invariant on ; logical worlds as follows: Every defpkg event that supports a logical world ; is present in the known-package-alist of that world. ; It is convenient to introduce the notion of a "hidden" defpkg event in a ; logical world as one that supports that world but is not present as an event ; in that world. The discussion below relies on the presence of several fields ; in a known-package-alist entry; see make-package-entry. ; We guarantee the (above) package invariant on logical worlds starting with ; Version_2.8 by way of the following two actions, which allow include-book and ; encapsulate (respectively) to preserve this invariant. Roughly speaking: ; action (1) extends a book's portcullis by any hidden defpkg supporting the ; book, so that the defpkg will not be missing from the world (thus violating ; the invariant) when we include the book; and action (2) puts a ; known-package-alist entry for each (hidden) defpkg introduced by a given ; encapsulate. ; (1) Recall that when a book is successfully certified in an existing ; certification world, we write the commands of that world to the book's ; certificate, as so-called "portcullis commands." We extend those ; portcullis commands with defpkg events in two ways. First, we add a defpkg ; at the end of the portcullis commands for every known-package-alist entry ; that has hidden-p fields equal to t (for example, because of a local ; include-book in a top-level encapsulate), and hence is not an event in the ; certification world. We will of course not count these extra defpkgs when ; checking against a numeric argument given to certify-book. Second, for ; each package entry present in the known-package-alist at the end of the ; proof pass of certify-book that is not present at the end of the ; include-book pass, we add a corresponding defpkg event to the end of the ; portcullis commands. ; Each defpkg event added to the portcullis as described above will have a ; :book-path argument derived from the book-path field of a package-entry in ; the known-package-alist, intended to represent the list of full book names ; leading from the innermost book actually containing the corresponding ; defpkg (in the car), up to the top-level such include-book (at the end of ; the list). Thus, when we evaluate that defpkg, the new package-entry in ; known-package-alist is obtained by appending the current world's ; include-book-path to the event's book-path. The book-path field in the ; package-entry can be used later when reporting an error during a package ; conflict, so that the user can see the source of the defpkg that was added ; to the portcullis under the hood. Documentation topic hidden-death-package ; explains hidden defpkgs in detail, and is referenced during such errors. ; In order to keep the certificate size under control, we will check whether ; the body of a hidden defpkg event to be added to the portcullis is a term ; in the world where it will be evaluated, and that this term's value is ; indeed the list of symbols associated with that package in the ; known-package-alist (a necessary check for a hidden defpkg since that term ; may have a different value in the world present at the time of the ; executing of the defpkg). If so, then we leave that term in place. ; Otherwise, we replace it by the appropriate quoted list of symbols, though ; we might still optimize by removing subsets that are commonly-used ; constants (e.g. *acl2-exports* and ; *common-lisp-symbols-from-main-lisp-package*), in favor of suitable calls ; of append or union-eq. Note that for hidden defpkg events encountered in a ; book during its certification, our decision to put them at the end of the ; certificate's portcullis commands, rather than the beginning, increases the ; chances that the original defpkg's term can be retained. ; (2) At the end of any encapsulate, the known-package-alist will be extended ; with an entry for each introduced defpkg. (We do this for every package in ; the known-package-alist at the end of the first pass of the encapsulate ; that was not there in the beginning, since these must all have been ; introduced by include-book, and only local include-books are allowed by ; encapsulate.) Each such entry will have appropriate package-entry fields, ; including hidden-p = t. ; Note that when we evaluate a defpkg in a world where that package exists but ; is hidden, the event will not be redundant, and we will change the hidden-p ; field to nil in the known-package-alist entry. Other fields can be used for ; error reporting. For example, if we attempt to introduce a defpkg when there ; is already a hidden defpkg conflicting with it, we can report the ; include-book path to the defpkg. ; Finally, we discuss how to ensure that :puff preserves the package invariant. ; Recall that the basic idea behind the implementation of :puff is the ; execution of function puffed-command-sequence to obtain a sequence of ; commands to execute after backing up through the given command. It is ; straightforward to find the hidden defpkg events that occur in the ; known-package-alist of the world just after the command but not just before, ; and add corresponding defpkg events to the front of the ; puffed-command-sequence. This preserves the invariant. ; End of Essay on Hidden Packages (defmacro make-package-entry (&key name imports hidden-p book-path defpkg-event-form tterm) ; Normally we would use defrec here. But defrec is defined in basis.lisp. ; Rather than move all the code relevant to defrec into axioms.lisp, we make ; our lives easy for now and simply define the relevant macros directly. For ; the record (pun intended), here is the defrecord: ; (defrec package-entry ; (name imports hidden-p book-path defpkg-event-form . tterm) ; t) ; WARNING: We allow assoc-equal (actually its macro form, find-package-entry) ; to look up names in the known-package-alist, so keep the name as the car. ; Also note that name, imports, and hidden-p are accessed much more frequently ; than the rest, so these should all get relatively fast access. `(list* ,name ; the package name ,imports ; the list of imported symbols ,hidden-p ; t if the introducing defpkg is hidden, else nil ; The remaining fields are used for messages only; they have no logical import. ,book-path ; a true list of full book names, where the path ; from the first to the last in the list is intended to ; give the location of the introducing defpkg, starting ; with the innermost book ; The final fields are def and tterm, where def is the defpkg event that ; introduced this package and tterm is the translation of the body of that ; defpkg. If this package-entry becomes hidden, we may use these fields to ; extend the portcullis commands in a book's certificate file. In doing so, we ; use tterm if it is a term in the world w that is present at the point of ; insertion into the portcullis commands, except that better yet, we will use ; the originating untranslated term from the defpkg if that is the result of ; untranslating tterm in w. ,defpkg-event-form ,tterm )) (defmacro find-package-entry (name known-package-alist) `(assoc-equal ,name ,known-package-alist)) (defmacro package-entry-name (package-entry) `(car ,package-entry)) (defmacro package-entry-imports (package-entry) `(cadr ,package-entry)) (defmacro package-entry-hidden-p (package-entry) `(caddr ,package-entry)) (defmacro package-entry-book-path (package-entry) `(cadddr ,package-entry)) (defmacro package-entry-defpkg-event-form (package-entry) `(car (cddddr ,package-entry))) (defmacro package-entry-tterm (package-entry) `(cdr (cddddr ,package-entry))) (defmacro find-non-hidden-package-entry (name known-package-alist) `(let ((entry (assoc-equal ,name ,known-package-alist))) (and (not (package-entry-hidden-p entry)) entry))) (defmacro remove-package-entry (name known-package-alist) `(delete-assoc-equal ,name ,known-package-alist)) (defmacro change-package-entry-hidden-p (entry value) `(let ((entry ,entry)) (make-package-entry :name (package-entry-name entry) :imports (package-entry-imports entry) :hidden-p ,value :book-path (package-entry-book-path entry) :defpkg-event-form (package-entry-defpkg-event-form entry) :tterm (package-entry-tterm entry)))) (defmacro getprop (symb key default world-name world-alist) ; This definition formerly occurred after fgetprop and sgetprop, but since ; getprop is used in defpkg-raw we move it before defpkg-raw. This move would ; not be necessary if we were always to load a source file before we load the ; corresponding compiled file, but with *suppress-compile-build-time* we do not ; load the latter (nor do we re-load the source file, as of this writing, for ; efficiency). ; We avoid cond here because it hasn't been defined yet! (if (equal world-name ''current-acl2-world) `(fgetprop ,symb ,key ,default ,world-alist) `(sgetprop ,symb ,key ,default ,world-name ,world-alist))) #-acl2-loop-only (progn (defvar *user-stobj-alist* nil) ; The value of the above variable is an alist that pairs user-defined ; single-threaded object names with their live ones. It does NOT ; contain an entry for STATE, which is not user-defined. ; The following SPECIAL VARIABLE, *wormholep*, when non-nil, means that we ; are within a wormhole and are obliged to undo every change visited upon ; *the-live-state*. Clearly, we can undo some of them, e.g., f-put-globals, by ; remembering the first time we make a change to some component. But other ; changes, e.g., printing to a file, we can't undo and so must simply disallow. ; We disallow all modifications to user stobjs. ; This feature is implemented so that we can permit the "wormhole window" to ; manipulate a "copy" of state without changing it. The story is that wormhole, ; which does not take state as an arg and which always returns nil, is ; "actually" implemented by calling the familiar LD on a near image of the ; current state. That near image is like the current state except that certain ; state globals have been set for wormhole. In addition, we assume that the ; physical map between ACL2 channels and the outside world has been altered so ; that *standard-co*, *standard-ci*, and *standard-oi* now actually interact ; with the "wormhole window" streams. Thus, even when *wormholep* is non-nil, we ; can allow i/o to those standard channels because it causes no change to the ; streams normally identified with those channels. If, while *wormholep* is ; non-nil we are asked to make a change that would undoably alter the state, we ; print a soft-looking error message and abort. If the requested change can be ; undone, we make the change after remembering enough to undo it. When we exit ; the wormhole we undo the changes. (defparameter *wormholep* nil) ; Below we define the function that generates the error message when ; non-undoable state changes are attempted within wormholes. It throws ; to a tag that is set up within LP. We do all that later. Right now ; we just define the error handler so we can code the primitives. (defun-one-output replace-bad-lisp-object (x) (if (bad-lisp-objectp x) (let ((pair (rassoc x *user-stobj-alist*))) (if pair (car pair) ; The following will be printed if we are looking at the value of a local stobj ; or of a stobj bound by stobj-let. '||)) x)) (defun-one-output replace-bad-lisp-object-list (x) (if (null x) nil (cons (replace-bad-lisp-object (car x)) (replace-bad-lisp-object-list (cdr x))))) (defun-one-output wormhole-er (fn args) (error-fms nil 'wormhole "It is not possible to apply ~x0~#1~[~/ to ~&2~] in the current ~ context because we are in a wormhole state." (list (cons #\0 fn) (cons #\1 (if args 1 0)) (cons #\2 (replace-bad-lisp-object-list args))) *the-live-state*) (throw 'local-top-level :wormhole-er)) ; The following parameter is where we will accumulate changes to ; state components that we will undo. (defparameter *wormhole-cleanup-form* nil) ; The value of *wormhole-cleanup-form* is a lisp (but not ACL2) form that will ; be executed to cleanup the live state. This form is built up incrementally ; by certain state changing primitives (e.g., f-put-global) so as to enable us ; to "undo" the effects of those primitives. We store this undo information ; as an executable form (rather than, say, a list of "undo tuples") because of ; the interaction between this mechanism and our acl2-unwind-protect ; mechanism. In particular, it will just happen to be the case that the ; *wormhole-cleanup-form* is always on the unwind protection stack (a true ; lisp global variable) so that if an abort happens while executing in a ; wormhole and we get ripped all the way out because of perfectly timed ; aborts, the undo cleanup form(s) will be at their proper places on the stack ; of cleanup forms and it will just look like certain acl2-unwind-protects were ; interrupted. See the discussion in and around LD-FN. The value of ; *wormhole-cleanup-form* is (PROGN save-globals undo-form1 ... undo-formk ; safety-set STATE). The individual undo-formi are created and added to the ; *wormhole-cleanup-form* by push-wormhole-undo- formi, below. The initial ; value of the cleanup form is (PROGN save-globals safety-set STATE) and new ; formis are added immediately after save-globals, making the final form a ; stack with save-globals always on top and the formi succeeding it in reverse ; order of their storage. The save-globals form will save into a lisp special ; the final values of the global variables that are available only in the ; wormhole. The save-globals form is complicated because it also contains a ; check that the cleanup form has never been completely executed. It does ; this by checking the car of a cons that ``belongs'' to this incarnation of ; the form. The safety-set at the end of the form sets the car of that cons ; to t. We cannot prevent the possible partial re-execution of the unwind ; protection form in the face of repeated ill-timed ctrl-c's and we cannot ; really guarantee that a ctrl-c doesn't prevent the execution of the ; safety-set even though the ``real'' cleanup work has been successfully done. ; But the re-execution of the cleanup form can confuse the tracking of the ; brr-stack gstack and we installed this check just for an increased sense of ; sanity. See the comment after wormhole1. ; We introduce a CLTL structure for the sole purpose of preventing the ; accidental printing of huge objects like the world. If, in raw lisp, you ; write (make-cloaking-device :hint "world" :obj (w *the-live-state*)) then you ; get an object, x, that CLTL will print as and from which the ; actual world can be recovered via (cloaking-device-obj x). (defstruct (cloaking-device (:print-function (lambda (x stream k) (declare (ignore k)) (format stream "" (cloaking-device-hint x))))) hint obj) (defun-one-output cloaked-set-w! (x state) ; We invented this function, which is merely set-w! but takes a cloaked world, ; just so we can print the *acl2-unwind-protect-stack* during debugging without ; getting the world printed. (set-w! (cloaking-device-obj x) state)) (defun-one-output assoc-eq-butlast-2 (x alist) ; This variant of assoc-eq is used in push-wormhole-undo-formi, for which alist ; is not a true alist but rather has two final elements that we do not want to ; consider. It is run only in raw Lisp on "alists" of the form mentioned ; above. (cond ((endp (cddr alist)) nil) ((eq x (car (car alist))) (car alist)) (t (assoc-eq-butlast-2 x (cdr alist))))) (defun-one-output assoc-eq-equal-butlast-2 (x y alist) ; This variant of assoc-eq-equal is used in push-wormhole-undo-formi, for which ; alist is not a true alist but rather has two final elements that we do not ; want to consider. It is run only in raw Lisp on "alists" of the form ; mentioned above. (cond ((endp (cddr alist)) nil) ((and (eq (car (car alist)) x) (equal (car (cdr (car alist))) y)) (car alist)) (t (assoc-eq-equal-butlast-2 x y (cdr alist))))) (defun-one-output push-wormhole-undo-formi (op arg1 arg2) ; When a primitive state changing function is called while *wormholep* ; is non-nil it actually carries out the change (in many cases) but ; saves some undo information on the special *wormhole-cleanup-form*. ; The value of that special is (PROGN save-globals form1 ... formk ; safety-set STATE). In response to this call we will add a new form, ; say form0, and will destructively modify *wormhole-cleanup-form* so ; that it becomes (PROGN save-globals form0 form1 ... formk ; safety-set STATE). ; We modify *wormhole-cleanup-form* destructively because it shares ; structure with the *acl2-unwind-protect-stack* as described above. ; The convention is that the primitive state changer calls this function before ; making any change. It passes us the essential information about the ; operation that must be performed to undo what it is about to do. Thus, if we ; store a new value for a global var, v, whose old value was x, then op will be ; 'put-global, arg1 will be v, and arg2 will be x. The formi we create will be ; (put-global 'v 'x *the-live-state*) and when that is executed it will undo ; the primitive state change. Note that we do not know what the primitive ; actually was, e.g., it might have been a put-global but it might also have ; been a makunbound-global. The point is that the 'put-global in our note is ; the operation that must be done at undo-time, not the operation that we are ; undoing. ; Furthermore, we need not save undo information after the first time ; we smash v. So we don't necessarily store a formi. But to implement this we ; have to know every possible formi and what its effects are. That is why we ; insist that this function (rather than our callers) create the forms. ; To think about the avoidance of formi saving, consider the fact that the ; cleanup form, being a PROGN, will be executed sequentially -- -- undoing the ; state changes in the reverse order of their original execution. Imagine that ; we in fact added a new formi at the front of the PROGN for each state change. ; Now think about it: if later on down the PROGN there is a form that will ; overwrite the effects of the form we are about to add, then there is no need ; to add it. In particular, the result of evaluating all the forms is the same ; whether we add the redundant one or not. (cond ((null *wormhole-cleanup-form*) (interface-er "push-wormhole-undo-formi was called with an empty ~ *wormhole-cleanup-form*. Supposedly, push-wormhole-undo-formi is ~ only called when *wormholep* is non-nil and, supposedly, when ~ *wormholep* is non-nil, the *wormhole-cleanup-form* is too."))) (let ((qarg1 (list 'quote arg1)) (undo-forms-and-last-two (cddr *wormhole-cleanup-form*))) (case op (put-global ; So we want to push (put-global 'arg1 'arg2 state). But if there is already a ; form that will set arg1 or one that unbinds arg1, there is no point. (or (assoc-eq-equal-butlast-2 'put-global qarg1 undo-forms-and-last-two) (assoc-eq-equal-butlast-2 'makunbound-global qarg1 undo-forms-and-last-two) (and (eq arg1 'current-acl2-world) (assoc-eq-butlast-2 'cloaked-set-w! undo-forms-and-last-two)) (setf (cddr *wormhole-cleanup-form*) (cons (let ((put-global-form `(put-global ,qarg1 (quote ,arg2) *the-live-state*))) ; We compress arrays for side-effect only, to ensure that we do not install a ; different global value than was there before. Fortunately, we know that the ; arrays in question are already in compressed form, i.e., they satisfy ; array1p; so we believe that these side-effects do not change the array's ; alist (in the sense of eq), and hence the restored global value will be ; installed as an ACL2 array. (If we're wrong, it's not a soundness issue -- ; rather, we will see slow-array-warning messages.) (cond ((eq arg1 'global-enabled-structure) `(progn (let ((qarg2 (quote ,arg2))) (compress1 (access enabled-structure qarg2 :array-name) (access enabled-structure qarg2 :theory-array))) ,put-global-form)) ((and (eq arg1 'iprint-ar) arg2) `(progn (let ((qarg2 (quote ,arg2))) (compress1 'iprint-ar qarg2)) ,put-global-form)) ((eq arg1 'trace-specs) nil) ; handled by fix-trace-specs (t put-global-form))) (cddr *wormhole-cleanup-form*))))) (makunbound-global ; We want to push (makunbound-global 'arg1 state). But if there is already ; a form that will make arg1 unbound or if there is a form that will ; give it a binding, this is redundant. (or (assoc-eq-equal-butlast-2 'put-global qarg1 undo-forms-and-last-two) (assoc-eq-equal-butlast-2 'makunbound-global qarg1 undo-forms-and-last-two) (and (eq arg1 'current-acl2-world) (assoc-eq-butlast-2 'cloaked-set-w! undo-forms-and-last-two)) (setf (cddr *wormhole-cleanup-form*) (cons `(makunbound-global ,qarg1 *the-live-state*) (cddr *wormhole-cleanup-form*))))) (cloaked-set-w! (or (assoc-eq-butlast-2 'cloaked-set-w! undo-forms-and-last-two) (setf (cddr *wormhole-cleanup-form*) (cons `(cloaked-set-w! ,(make-cloaking-device :hint "world" :obj arg1) *the-live-state*) (cddr *wormhole-cleanup-form*))))) (otherwise (interface-er "Unrecognized op in push-wormhole-undo-formi,~ ~x0." op))))) ; The following symbol is the property under which we store Common ; Lisp streams on the property lists of channels. (defconstant *open-input-channel-key* 'acl2_invisible::|Open Input Channel Key|) ; The following symbol is the property under which we store the types ; of Common Lisp streams on the property lists of channels. (defconstant *open-input-channel-type-key* 'acl2_invisible::|Open Input Channel Type Key|) (defconstant *open-output-channel-key* 'acl2_invisible::|Open Output Channel Key|) (defconstant *open-output-channel-type-key* 'acl2_invisible::|Open Output Channel Type Key|) (defconstant *non-existent-stream* 'acl2_invisible::|A Non-Existent Stream|) ; We get ready to handle errors in such a way that they return to the ; top level logic loop if we are under it. (defvar *acl2-error-p* nil) (defun interface-er (&rest args) ; This function can conceivably be called before ACL2 has been fully ; compiled and loaded, so we check whether the usual error handler is ; around. (cond ((macro-function 'er) (eval `(let ((state *the-live-state*) (*acl2-error-p* t)) (er soft 'acl2-interface ,@(let (ans) (dolist (a args) (push (list 'quote a) ans)) (reverse ans))) (error "ACL2 Halted")))) (t (error "ACL2 error: ~a." args)))) #-acl2-loop-only (declaim (inline ; Here we take a suggestion from Jared Davis and inline built-in functions, ; starting after Version_6.2, based on successful use of such inlining at ; Centaur Technology for many months on their local copy of ACL2. Indeed, the ; original list below (added on June 16, 2013) comes directly from that copy, ; except for inclusion of aref1 and aref2 (as noted below). As Jared said in a ; log message when he added inline declarations for 33 functions to a local ; copy of ACL2 at Centaur: ; This should give us a useful speedup on CCL for many functions that recur ; with ZP at the end. I measured a 12% speedup for a naive FIB function. ; We are seeing perhaps 2% speedup on regressions, but we believe that this ; inlining could provide much greater benefit in some cases. ; Some of these functions could probably be inlined using the defun-inline ; feature of ACL2, but we prefer not to fight with the likely resulting ; boot-strapping problem during the ACL2 build. ; We may modify this list from time to time, for example based on user request. ; It surely is safe to add any function symbol to the list that is not defined ; recursively in raw Lisp (and maybe even if it is). But of course that could ; interfere with tracing and redefinition, so care should be taken before ; adding a function symbol that might be traced or redefined. ; We endeavor to keep the list sorted alphabetically, simply to make it easy to ; search visually. acl2-numberp add-to-set-eq-exec aref1 ; already inlined in Version_6.2 and before aref2 ; already inlined in Version_6.2 and before booleanp complex-rationalp eqlablep fix fn-symb iff ifix implies integer-abs integer-range-p len member-equal natp nfix peek-char$ posp quotep random$ read-byte$ read-char$ realfix rfix signed-byte-p strip-cars strip-cdrs symbol-< unsigned-byte-p xor zip zp zpf ) ; For ACL2 built on CMUCL 20D Unicode, an attempt failed on 9/12/2013 to ; certify the community book books/models/jvm/m1/defsys.lisp. During ; debugging, we found a note that mentioned "*Inline-Expansion-Limit* (400) ; exceeded". The following declaim form, which may be quite harmless, solves ; the problem. #+cmu (notinline len)) ; We provide here ``raw'' implementations of basic functions that we ; ``wish'' were already in Common Lisp, to support primitives of the ; ACL2 logic. ; Some of the Common Lisp arithmetic primitives are n-ary functions. ; However, ACL2 supports only functions of fixed arity, to keep the ; logic simple. But in practice we find we want to use the n-ary ; arithmetic symbols ourselves. So in the logic we have binary-+ as ; the primitive binary addition function symbol, but we also have the ; macro +, which expands into a suitable number of uses of binary-+. ; Similarly for *, -, and /. (The ACL2 user cannot invoke ; symbol-function, fboundp, macro-function or macroexpand, so it is no ; concern to the user whether we implement + as a macro or a ; function.) (defun-one-output acl2-numberp (x) (numberp x)) (defun-one-output binary-+ (x y) (+ x y)) (defun-one-output binary-* (x y) (* x y)) (defun-one-output unary-- (x) (- x)) (defun-one-output unary-/ (x) (/ x)) ; Below we define our top-level events as seen by the Common Lisp ; compiler. For example, (defuns a b c) expands into a progn of defun ; forms, (defthm ...) is a no-op, etc. (defparameter *in-recover-world-flg* nil) ; Warning: Keep the initial value of the following defparameter identical to ; that of the ACL2 constant *initial-known-package-alist* below. (defparameter *ever-known-package-alist* (list (make-package-entry :name "ACL2-INPUT-CHANNEL" :imports nil) (make-package-entry :name "ACL2-OUTPUT-CHANNEL" :imports nil) (make-package-entry :name "ACL2" :imports *common-lisp-symbols-from-main-lisp-package*) (make-package-entry :name ; Warning: The following is just *main-lisp-package-name* but that is not ; defined yet. If you change the following line, change the defconst of ; *main-lisp-package-name* below. "COMMON-LISP" :imports nil) (make-package-entry :name "KEYWORD" :imports nil))) ; The known-package-alist of the state will grow and shrink as packages are ; defined and undone. But *ever-known-package-alist* will just grow. A ; package can be redefined only if its imports list is identical to that in its ; old definition. (defvar **1*-symbol-key* (make-symbol "**1*-SYMBOL-KEY*")) (defun *1*-symbol (x) ; Keep this in sync with *1*-symbol?. (or (get x **1*-symbol-key*) (setf (get x **1*-symbol-key*) (intern (symbol-name x) (find-package-fast (concatenate 'string *1*-package-prefix* (symbol-package-name x))))))) (defun *1*-symbol? (x) ; Keep this in sync with *1*-symbol. Returns nil if the *1* package doesn't ; exist. (let ((pack (find-package-fast (concatenate 'string *1*-package-prefix* (symbol-package-name x))))) (and pack (or (get x **1*-symbol-key*) (setf (get x **1*-symbol-key*) (intern (symbol-name x) pack)))))) (defmacro defun-*1* (fn &rest args) `(defun ,(*1*-symbol fn) ,@args)) (defparameter *defun-overrides* nil) (defmacro defun-overrides (name formals &rest rest) ; This is basically defun, for a function that takes the live state and has a ; guard of t. We push name onto *defun-overrides* so that add-trip knows to ; leave the *1* definition in place. (assert (member 'state formals :test 'eq)) `(progn (push ',name *defun-overrides*) ; see add-trip (defun ,name ,formals ,@(butlast rest 1) (progn (chk-live-state-p ',name state) ,(car (last rest)))) (defun-*1* ,name ,formals (,name ,@formals)))) (defmacro defpkg (&whole event-form name imports &optional doc book-path hidden-p) ; Keep this in sync with get-cmds-from-portcullis1, make-hidden-defpkg, ; equal-modulo-hidden-defpkgs, and (of course) the #+acl2-loop-only definition ; of defpkg. (declare (ignore doc hidden-p)) (or (stringp name) (interface-er "Attempt to call defpkg on a non-string, ~x0." name)) `(defpkg-raw ,name ,imports ',book-path ',event-form)) (defmacro defuns (&rest lst) `(progn ,@(mapcar #'(lambda (x) `(defun ,@x)) lst))) #+:non-standard-analysis (defmacro defun-std (name formals &rest args) (list* 'defun name formals (append (butlast args 1) (list (non-std-body name formals (car (last args))))))) #+:non-standard-analysis (defmacro defuns-std (&rest args) `(defuns ,@args)) (defmacro defthm (&rest args) (declare (ignore args)) nil) (defmacro defthmd (&rest args) (declare (ignore args)) nil) #+:non-standard-analysis (defmacro defthm-std (&rest args) (declare (ignore args)) nil) (defmacro defaxiom (&rest args) (declare (ignore args)) nil) (defmacro skip-proofs (arg) arg) (defmacro deflabel (&rest args) (declare (ignore args)) nil) (defmacro defdoc (&rest args) (declare (ignore args)) nil) (defmacro deftheory (&rest args) (declare (ignore args)) nil) (defun-one-output stobj-initial-statep-arr (n i arr init) (or (zp n) (and (equal (aref arr i) init) (stobj-initial-statep-arr (1- n) (1+ i) arr init)))) (defun-one-output stobj-initial-statep-entry (temp entry) ; Keep this function in sync with defstobj-raw-init-fields. (See the comments ; about this function, below.) (let ((type (cadr temp)) (init (caddr temp))) (cond ((and (consp type) (eq (car type) 'ARRAY)) ; For stobj array fields, we need to check each entry in the array to make sure ; it is the initial value and we also need to check that the array has not been ; resized to a size different than the initial size. (let ((size (car (caddr type)))) (and (equal (length entry) size) (stobj-initial-statep-arr size 0 entry init)))) ((equal type t) ; For type "T", the stobj field is not "boxed" by defstobj-raw-init-fields. (equal entry init)) (t ; For other types, the value is "boxed" by defstobj-raw-init-fields in a single ; entry array. (equal (aref entry 0) init))))) (defun-one-output stobj-initial-statep1 (field-templates ndx stobj) (cond ((endp field-templates) t) (t (and (stobj-initial-statep-entry (car field-templates) (aref stobj ndx)) (stobj-initial-statep1 (cdr field-templates) (1+ ndx) stobj))))) (defun-one-output stobj-initial-statep (stobj field-templates) ; Stobj is the live object corresponding to some defstobj and ; field-templates is the field templates for the defstobj. We return ; t or nil according to whether the live object is in the initial ; state. ; Each element of field-templates is of the form (recog-fn type ; init-val acc-fn upd-fn ...). If type indicates an array, then it ; has the form (ARRAY typ (max)), and the indices of the array range ; from 0 to max-1, i.e., max is the first illegal index. (stobj-initial-statep1 field-templates 0 stobj)) (defun remove-stobj-inline-declare (x) (cond ((atom x) x) ((equal (car x) *stobj-inline-declare*) (cdr x)) (t (cons (car x) (remove-stobj-inline-declare (cdr x)))))) (defun congruent-stobj-rep-raw (name) (assert name) (let* ((d (get (the-live-var name) 'redundant-raw-lisp-discriminator)) (ans (cddddr d))) (assert ans) ans)) ; Note: The code below bothers me a little because of its impact on ; the toothbrush model. In particular, it uses defstobj-raw-defs, ; which is defined far away in other-events.lisp. (defmacro defstobj (name &rest args) ; Warning: If you change this definition, consider the possibility of making ; corresponding changes to the #-acl2-loop-only definition of defabsstobj. ; This function is run when we evaluate (defstobj name . args) in raw lisp. ; A typical such form is ; (defstobj $st ; (flag :type t :initially run) ; (pc :type (integer 0 255) :initially 128) ; (mem :type (array (integer 0 255) (256)) :initially 0)) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ; This function must contend with a problem analogous to the one addressed by ; acl2::defconst in acl2.lisp: the need to avoid re-declaration of the same ; stobj. We use redundant-raw-lisp-discriminator in much the same way as in ; the raw lisp defmacro of acl2::defconst. (let* ((template (defstobj-template name args nil)) (congruent-to (sixth template)) (congruent-stobj-rep (if congruent-to (congruent-stobj-rep-raw congruent-to) name)) (init (defstobj-raw-init template)) (the-live-name (the-live-var name))) `(progn ; We place the defvar above the subsequent let*, in order to avoid ; warnings in Lisps such as CCL that compile on-the-fly. (defvar ,the-live-name) #+hons ,@(and (null congruent-to) `((defg ,(st-lst name) nil))) ; Now we lay down the defuns of the recognizers, accessors and updaters as ; generated by defstob-raw-defs. The boilerplate below just adds the DEFUN to ; the front of each def generated, preserving the order of the defs as ; generated. We deal here with the :inline case; note that ; *stobj-inline-declare* was added in defstobj-field-fns-raw-defs. ,@(mapcar (function (lambda (def) (if (member-equal *stobj-inline-declare* def) (cons 'DEFABBREV (remove-stobj-inline-declare def)) (cons 'DEFUN def)))) (defstobj-raw-defs name template congruent-stobj-rep nil)) ,@(defstobj-defconsts (strip-accessor-names (caddr template)) 0) (let* ((template ',template) (congruent-stobj-rep ',congruent-stobj-rep) (boundp (boundp ',the-live-name)) (d (and boundp (get ',the-live-name 'redundant-raw-lisp-discriminator))) ; d is expected to be of the form (DEFSTOBJ namep creator field-templates ; . congruent-stobj-rep). (ok-p (and boundp (consp d) (eq (car d) 'defstobj) (consp (cdr d)) (eq (cadr d) (car template)) (consp (cddr d)) (eq (caddr d) (cadr template)) (equal (cadddr d) (caddr template)) (eq (cddddr d) congruent-stobj-rep) ; We also formerly required: ; (stobj-initial-statep (symbol-value ',the-live-name) ; (caddr template)) ; However, the stobj need not have its initial value; consider a redundant ; defstobj in a book whose certification world has already modified the stobj, ; or a defstobj in a book whose value is modified in a make-event later in that ; book. Either way, ok-p would be false when this code is executed by loading ; the compiled file. ; We do not check the :doc, :inline, or :congruent-to fields, because these ; incur no proof obligations. If a second pass of encapsulate, or inclusion of ; a book, exposes a later non-local defstobj that is redundant with an earlier ; local one, then any problems will be caught during local compatibility ; checks. ))) (cond (ok-p ',name) ((and boundp (not (raw-mode-p *the-live-state*))) (interface-er "Illegal attempt to redeclare the single-threaded object ~s0." ',name)) (t ; Memoize-flush expects the variable (st-lst name) to be bound. (setf ,the-live-name ,init) (setf (get ',the-live-name 'redundant-raw-lisp-discriminator) (list* 'defstobj (car template) (cadr template) (caddr template) congruent-stobj-rep)) (let ((old (and boundp ; Since boundp, then by a test made above, we also know (raw-mode-p state). ; This boundp test could be omitted, since otherwise we know that the assoc-eq ; call below will return nil; the boundp check is just an optimization. (assoc-eq ',name *user-stobj-alist*)))) (cond (old ; hence raw-mode (fms "Note: Redefining and reinitializing stobj ~x0 in raw ~ mode.~%" (list (cons #\0 ',name)) (standard-co *the-live-state*) *the-live-state* nil) (setf (cdr old) (symbol-value ',the-live-name))) (t (assert$ (not (assoc-eq ',name *user-stobj-alist*)) (setq *user-stobj-alist* (cons (cons ',name (symbol-value ',the-live-name)) *user-stobj-alist*)))))) ',name)))))) (defmacro value-triple (&rest args) (declare (ignore args)) nil) (defmacro verify-termination-boot-strap (&rest args) (declare (ignore args)) nil) (defmacro verify-guards (&rest args) (declare (ignore args)) nil) (defmacro in-theory (&rest args) (declare (ignore args)) nil) (defmacro in-arithmetic-theory (&rest args) (declare (ignore args)) nil) (defmacro regenerate-tau-database (&rest args) (declare (ignore args)) nil) (defmacro push-untouchable (&rest args) (declare (ignore args)) nil) (defmacro remove-untouchable (&rest args) (declare (ignore args)) nil) (defmacro set-body (&rest args) (declare (ignore args)) nil) (defmacro table (&rest args) ; Note: The decision to make table a no-op in compiled files was not ; taken lightly. But table, like defthm, has no effect on the logic. ; Indeed, like defthm, table merely modifies the world and if it is ; permitted in compiled code to ignore defthm's effects on the world ; then so too the effects of table. (declare (ignore args)) nil) (defmacro encapsulate (signatures &rest lst) ; The code we generate for the constrained functions in signatures is ; the same (except, possibly, for the formals) as executed in ; extend-world1 when we introduce an undefined function. ; Sig below may take on any of several forms, illustrated by ; the examples: ; ((fn * * $S * STATE) => (MV * STATE)) ; (fn (x y $S z STATE) (MV t STATE)) ; (fn (x y $S z STATE) (MV t STATE) :stobjs ($S)) ; Because the first form above does not provide explicit formals, we ; generate them with gen-formals-from-pretty-flags when we process ; ENCAPSULATE in the logic. So what do we do here in raw Lisp when an ; encapsulate is loaded? We ignore all but the arity and generate (x1 ; x2 ... xn). We did not want to have to include ; gen-formals-from-pretty-flags in the toothbrush model. ; See the comment in defproxy about benign redefinition in raw Lisp by an ; encapsulate of a function introduced by defproxy. `(progn ,@(mapcar (function (lambda (sig) (let* ((fn (if (consp (car sig)) (caar sig) (car sig))) (formals (if (consp (car sig)) (let ((i 0)) (mapcar (function (lambda (v) (declare (ignore v)) (setq i (+ 1 i)) (intern (format nil "X~a" i) "ACL2"))) (cdar sig))) (cadr sig)))) (list 'defun fn formals (null-body-er fn formals t))))) signatures) ,@lst)) (defparameter *inside-include-book-fn* ; We trust include-book-fn and certify-book-fn to take care of all include-book ; processing without any need to call the raw Lisp include-book. It seems that ; the only way this could be a bad idea is if include-book or certify-book ; could be called from a user utility, rather than at the top level, while ; inside a call of include-book-fn or certify-book-fn. We disallow this in ; translate11. nil) (defmacro include-book (user-book-name &key (load-compiled-file ':default) uncertified-okp defaxioms-okp skip-proofs-okp ttags dir doc) (declare (ignore uncertified-okp defaxioms-okp skip-proofs-okp ttags doc)) `(include-book-raw ,user-book-name nil ,load-compiled-file ,dir '(include-book . ,user-book-name) *the-live-state*)) (defmacro certify-book (&rest args) (declare (ignore args)) ; Unlike the embedded event forms such as DEFTHM, it is safe to cause an error ; here. We want embedded event forms such as DEFTHM to be quietly ignored ; when books are included, but CERTIFY-BOOK is not an embedded event form, so ; it has no business being called from raw Lisp. (interface-er "Apparently you have called CERTIFY-BOOK from outside the ~ top-level ACL2 loop. Perhaps you need to call (LP) first.")) (defmacro local (x) (declare (ignore x)) nil) (defmacro defchoose (&rest args) (let ((free-vars (caddr args))) `(defun ,(car args) ,free-vars ,(null-body-er (car args) free-vars nil)))) ; Although defuns provides us conceptually with the right function for ; packaging together mutually recursive functions, we never use it ; because it hides things from standard Lisp editor indexing programs ; such as etags. Instead, we use mutual-recursion. (defmacro mutual-recursion (&rest lst) (cons 'progn lst)) (defmacro make-event (&whole event-form form &key expansion? check-expansion on-behalf-of) (declare (ignore form on-behalf-of)) (cond ((consp check-expansion) check-expansion) (expansion?) (t `(error ; not er; so certify-book and include-book fail "It is illegal to execute make-event in raw Lisp (including ~%~ raw mode) unless :check-expansion is a cons, which represents ~%~ the expected expansion. If this error occurs when executing ~%~ an include-book form in raw mode or raw Lisp, consider loading a ~%~ corresponding file *@expansion.lsp instead; see :DOC ~%~ certify-book. If you are not in raw Lisp, then this is an ~%~ ACL2 bug; please contact the ACL2 implementors and report the ~%~ offending form:~%~%~s~%" ',event-form)))) ) (deflabel programming ; Be sure to include documentation for all functions in ; primitive-formals-and-guards. :doc ":Doc-Section Programming programming in ACL2~/ This ~il[documentation] topic is a parent topic under which we include documentation topics for built-in functions, macros, and special forms (~pl[acl2-built-ins]) as well as topics for notions important to programming with ACL2. If you don't find what you're looking for, see the Index or see individual topics that may be more directly appropriate; for example, ~pl[events] for top-level event constructorsr like ~ilc[defun].~/~/") (deflabel acl2-built-ins :doc ":Doc-Section ACL2::Programming built-in ACL2 functions~/ This ~il[documentation] topic is a parent topic under which we include documentation for built-in functions, macros, and special forms that are typically used in programming. For others, including those typically used as top-level commands or those that create ~il[events] (~ilc[defun], ~ilc[defthm], and so on), documentation may be found as a subtopic of some other parent topic. We do not document some of the more obscure functions provided by ACL2 that do not correspond to functions of Common Lisp.~/ See any documentation for Common Lisp for more details on many of these functions.~/") (deflabel miscellaneous :doc ":Doc-Section Miscellaneous a miscellany of documented functions and concepts (often cited in more accessible ~il[documentation])~/~/ Perhaps as the system matures this section will become more structured.~/") ; STANDARD CHANNELS ; Documentation is deferred until after (deflabel IO ...). (defconst *standard-co* 'acl2-output-channel::standard-character-output-0) (defconst *standard-oi* 'acl2-input-channel::standard-object-input-0) (defconst *standard-ci* 'acl2-input-channel::standard-character-input-0) ; IF and EQUAL ; Convention: when a term t is used as a formula it means ; (not (equal t nil)) ; The following four axioms define if and equal but are not expressed ; in the ACL2 language. ; (if NIL y z) = z ; x/=NIL -> (if x y z) = y ; (equal x x) = T ; x/=y -> (equal x y) = NIL ; LOGIC #+acl2-loop-only (defconst nil 'nil ; We cannot document a NIL symbol. " NIL, a symbol, represents in Common Lisp both the false truth value and the empty list.") #+acl2-loop-only (defconst t 't ; We cannot document a NIL symbol. So, we do not document T either. "T, a symbol, represents the true truth value in Common Lisp.") (defun insist (x) ; This function is used in guard-clauses-for-fn, so in order to be sure that ; it's in place early, we define it now. (declare (xargs :guard x :mode :logic :verify-guards t) (ignore x)) nil) (defun iff (p q) ":Doc-Section ACL2::ACL2-built-ins logical ``if and only if''~/ ~c[Iff] is the ACL2 biconditional, ``if and only if''. ~c[(iff P Q)] means that either ~c[P] and ~c[Q] are both false (i.e., ~c[nil]) or both true (i.e., not ~c[nil]). To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (if p (if q t nil) (if q nil t))) (defun xor (p q) ":Doc-Section ACL2::ACL2-built-ins logical ``exclusive or''~/ ~c[Xor] is the ACL2 exclusive-or function. ~c[(xor P Q)] means that either ~c[P] or ~c[Q], but not both, is false (i.e., ~c[nil]). To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (if p (if q nil t) (if q t nil))) #+acl2-loop-only (defun eq (x y) ":Doc-Section ACL2::ACL2-built-ins equality of symbols~/ ~c[Eq] is the function for determining whether two objects are identical (i.e., have the exact same store address in the current von Neumann implementation of Common Lisp). It is the same as ~ilc[equal] in the ACL2 logic.~/ ~c[Eq] is a Common Lisp function. In order to ensure conformance with Common Lisp, the ACL2 ~il[guard] on ~c[eq] requires at least one of the arguments to ~c[eq] to be a symbol. Common Lisp guarantees that if ~c[x] is a symbol, then ~c[x] is ~c[eq] to ~c[y] if and only if ~c[x] is ~ilc[equal] to ~c[y]. Thus, the ACL2 user should think of ~c[eq] as nothing besides a fast means for checking ~ilc[equal] when one argument is known to be a symbol. In particular, it is possible that an ~c[eq] test will not even require the cost of a function call but will be as fast as a single machine instruction.~/" (declare (xargs :guard (if (symbolp x) t (symbolp y)) :mode :logic :verify-guards t)) (equal x y)) (defun booleanp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for booleans~/ ~c[(Booleanp x)] is ~c[t] if ~c[x] is ~c[t] or ~c[nil], and is ~c[nil] otherwise.~/ ~l[generalized-booleans] for a discussion of a potential soundness problem for ACL2 related to the question: Which Common Lisp functions are known to return Boolean values? To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (if (eq x t) t (eq x nil))) ; We do not want to try to define defequiv at this point, so we use the ; expansion of (defequiv iff). (defthm iff-is-an-equivalence (and (booleanp (iff x y)) (iff x x) (implies (iff x y) (iff y x)) (implies (and (iff x y) (iff y z)) (iff x z))) :rule-classes (:equivalence)) (defun implies (p q) ":Doc-Section ACL2::ACL2-built-ins logical implication~/ ~c[Implies] is the ACL2 implication function. ~c[(implies P Q)] means that either ~c[P] is false (i.e., ~c[nil]) or ~c[Q] is true (i.e., not ~c[nil]). To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :mode :logic :guard t)) (if p (if q t nil) t)) (defthm iff-implies-equal-implies-1 (implies (iff y y-equiv) (equal (implies x y) (implies x y-equiv))) :rule-classes (:congruence)) (defthm iff-implies-equal-implies-2 (implies (iff x x-equiv) (equal (implies x y) (implies x-equiv y))) :rule-classes (:congruence)) #+acl2-loop-only (defun not (p) ":Doc-Section ACL2::ACL2-built-ins logical negation~/ ~c[Not] is the ACL2 negation function. The negation of ~c[nil] is ~c[t] and the negation of anything else is ~c[nil].~/ ~c[Not] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard t)) (if p nil t)) (defthm iff-implies-equal-not (implies (iff x x-equiv) (equal (not x) (not x-equiv))) :rule-classes (:congruence)) (defun hide (x) ":Doc-Section Miscellaneous hide a term from the rewriter~/ ~c[Hide] is actually the ~il[identity] function: ~c[(hide x) = x] for all ~c[x]. However, terms of the form ~c[(hide x)] are ignored by the ACL2 rewriter, except when explicit ~c[:expand] ~il[hints] are given for such terms (~pl[hints]) or when rewrite rules explicitly about ~c[hide] are available. An ~c[:expand] hint that removes all calls of ~c[hide] is: ~bv[] :expand ((:free (x) (hide x))) ~ev[] The above hint can be particularly useful when ACL2's equality heuristics apply ~c[hide] to an equality after substituting it into the rest of the goal, if that goal (or a subgoal of it) fails to be proved. ~c[Hide] terms are also ignored by the induction heuristics.~/ Sometimes the ACL2 simplifier inserts ~c[hide] terms into a proof attempt out of the blue, as it were. Why and what can you do about it? Suppose you have a constrained function, say ~c[constrained-fn], and you define another function, say ~c[another-fn], in terms of it, as in: ~bv[] (defun another-fn (x y z) (if (big-hairy-test x y z) (constrained-fn x y z) t)) ~ev[] Suppose the term ~c[(another-fn 'a 'b 'c)] arises in a proof. Since the arguments are all constants, ACL2 will try to reduce such a term to a constant by executing the definition of ~c[another-fn]. However, after a possibly extensive computation (because of ~c[big-hairy-test]) the execution fails because of the unevaluable call of ~c[constrained-fn]. To avoid subsequent attempts to evaluate the term, ACL2 embeds it in a ~c[hide] expression, i.e., rewrites it to ~c[(hide (another-fn 'a 'b 'c))]. You might think this rarely occurs since all the arguments of ~c[another-fn] must be constants. You would be right except for one special case: if ~c[another-fn] takes no arguments, i.e., is a constant function, then every call of it fits this case. Thus, if you define a function of no arguments in terms of a constrained function, you will often see ~c[(another-fn)] rewrite to ~c[(hide (another-fn))]. We do not hide the term if the executable counterpart of the function is disabled -- because we do not try to evaluate it in the first place. Thus, to prevent the insertion of a ~c[hide] term into the proof attempt, you can globally disable the executable counterpart of the offending defined function, e.g., ~bv[] (in-theory (disable (:executable-counterpart another-fn))). ~ev[] It is conceivable that you cannot afford to do this: perhaps some calls of the offending function must be computed while others cannot be. One way to handle this situation is to leave the executable counterpart enabled, so that ~c[hide] terms are introduced on the calls that cannot be computed, but prove explicit :~ilc[rewrite] rules for each of those ~c[hide] terms. For example, suppose that in the proof of some theorem, thm, it is necessary to leave the executable counterpart of ~c[another-fn] enabled but that the call ~c[(another-fn 1 2 3)] arises in the proof and cannot be computed. Thus the proof attempt will introduce the term ~c[(hide (another-fn 1 2 3))]. Suppose that you can show that ~c[(another-fn 1 2 3)] is ~c[(contrained-fn 1 2 3)] and that such a step is necessary to the proof. Unfortunately, proving the rewrite rule ~bv[] (defthm thm-helper (equal (another-fn 1 2 3) (constrained-fn 1 2 3))) ~ev[] would not help the proof of thm because the target term is hidden inside the ~c[hide]. However, ~bv[] (defthm thm-helper (equal (hide (another-fn 1 2 3)) (constrained-fn 1 2 3))) ~ev[] would be applied in the proof of thm and is the rule you should prove. Now to prove ~c[thm-helper] you need to use the two ``tricks'' which have already been discussed. First, to eliminate the ~c[hide] term in the proof of ~c[thm-helper] you should include the hint ~c[:expand] ~c[(hide (another-fn 1 2 3))]. Second, to prevent the ~c[hide] term from being reintroduced when the system tries and fails to evaluate ~c[(another-fn 1 2 3)] you should include the hint ~c[:in-theory] ~c[(disable (:executable-counterpart another-fn))]. Thus, ~c[thm-helper] will actually be: ~bv[] (defthm thm-helper (equal (hide (another-fn 1 2 3)) (constrained-fn 1 2 3)) :hints ((\"Goal\" :expand (hide (another-fn 1 2 3)) :in-theory (disable (:executable-counterpart another-fn))))) ~ev[] ~l[eviscerate-hide-terms] for how to affect the printing of ~c[hide] terms." (declare (xargs :guard t)) x) (defun rewrite-equiv (x) ; Documentation to be written. This is experimental for Version_3.1, to be ; tried out by Dave Greve. (declare (xargs :mode :logic :guard t)) x) ; As of ACL2 Version_2.5, we can compile with or without support for ; non-standard analysis. To make maintenance of the two versions simpler, ; we define the macro "real/rationalp" which is defined as either realp or ; rationalp depending on whether the reals exist in the current ACL2 ; universe or not. (defmacro real/rationalp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for rational numbers (including real number in ACL2(r))~/ For most ACL2 users, this is a macro abbreviating ~ilc[rationalp]. In ACL2(r) (~pl[real]), this macro abbreviates the predicate ~c[realp], which holds for real numbers as well (including rationals). Most ACL2 users can ignore this macro and use ~ilc[rationalp] instead, but many community books use ~c[real/rationalp] so that these books will be suitable for ACL2(r) as well.~/~/" #+:non-standard-analysis `(realp ,x) #-:non-standard-analysis `(rationalp ,x)) (defmacro complex/complex-rationalp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for complex numbers~/ For most ACL2 users, this is a macro abbreviating ~c[complex-rationalp]; ~pl[complex-rationalp]. In ACL2(r) (~pl[real]), a complex number ~c[x] may have irrational real and imaginary parts. This macro abbreviates the predicate ~c[complexp] in ACL2(r), which holds for such ~c[x]. Most ACL2 users can ignore this macro and use ~ilc[complex-rationalp] instead. Some community books use ~c[complex/complex-rationalp] so that they are suitable for ACL2(r) as well.~/~/" #+:non-standard-analysis `(complexp ,x) #-:non-standard-analysis `(complex-rationalp ,x)) ; Comments labeled "RAG" are from Ruben Gamboa, pertaining to his work ; in creating ACL2(r) (see :doc real). (defun true-listp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for proper (null-terminated) lists~/ ~c[True-listp] is the function that checks whether its argument is a list that ends in, or equals, ~c[nil]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t :mode :logic)) (if (consp x) (true-listp (cdr x)) (eq x nil))) (defun list-macro (lst) (declare (xargs :guard t)) (if (consp lst) (cons 'cons (cons (car lst) (cons (list-macro (cdr lst)) nil))) nil)) #+acl2-loop-only (defmacro list (&rest args) ":Doc-Section ACL2::ACL2-built-ins build a list~/ ~c[List] is the macro for building a list of objects. For example, ~c[(list 5 6 7)] returns a list of length 3 whose elements are ~c[5], ~c[6], and ~c[7] respectively. Also ~pl[list*].~/ ~c[List] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (list-macro args)) (defun and-macro (lst) (declare (xargs :guard t)) (if (consp lst) (if (consp (cdr lst)) (list 'if (car lst) (and-macro (cdr lst)) nil) (car lst)) t)) #+acl2-loop-only (defmacro and (&rest args) ":Doc-Section ACL2::ACL2-built-ins conjunction~/ ~c[And] is the macro for conjunctions. ~c[And] takes any number of arguments. ~c[And] returns ~c[nil] if one of the arguments is ~c[nil], but otherwise returns the last argument. If there are no arguments, ~c[and] returns ~c[t].~/ ~c[And] is a Common Lisp macro. See any Common Lisp documentation for more information.~/" (and-macro args)) (defun or-macro (lst) (declare (xargs :guard t)) (if (consp lst) (if (consp (cdr lst)) (list 'if (car lst) (car lst) (or-macro (cdr lst))) (car lst)) nil)) #+acl2-loop-only (defmacro or (&rest args) ":Doc-Section ACL2::ACL2-built-ins disjunction~/ ~c[Or] is the macro for disjunctions. ~c[Or] takes any number of arguments and returns the first that is non-~c[nil], or ~c[nil] if there is no non-~c[nil] element.~/ In the ACL2 logic, the macroexpansion of ~c[(or x y)] is an ~c[IF] term that appears to cause ~c[x] to be evaluated twice: ~bv[] ACL2 !>:trans (or x y) (IF X X Y) => * ACL2 !> ~ev[] If ~c[x] were replaced by an expression whose evaluation takes a long time, then such an expansion would be ineffecient. However, don't be fooled: you can expect Common Lisp implementations to avoid this problem, say by generating a new variable, for example: ~bv[] ACL2 !>:q ; Exit the ACL2 loop and go into raw Common Lisp Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ACL2>(macroexpand '(or x y)) (LET ((#:G5374 X)) (IF #:G5374 #:G5374 Y)) T ACL2> ~ev[] ~c[Or] is a Common Lisp macro. See any Common Lisp documentation for more information.~/" (or-macro args)) #+acl2-loop-only (defmacro - (x &optional (y 'nil binary-casep)) ; In the general case, (- x y) expands to (binary-+ x (unary-- y)). But in the ; special case that y is a numeric constant we go ahead and run the unary-- ; and we put it in front of x in the binary-+ expression so that it is in the ; expected "normal" form. Thus, (- x 1) expands to (binary-+ -1 x). Two forms ; of y allow this "constant folding": explicit numbers and the quotations of ; explicit numbers. ; Constant folding is important in processing definitions. If the user has ; written (1- x), we translate that to (binary-+ -1 x) instead of to the more ; mechanical (binary-+ (unary-- 1) x). Note that the type of the former is ; easier to determine that the latter because type-set knows about the effect ; of adding the constant -1 to a positive, but not about adding the term (- 1). (if binary-casep ; First we map 'n to n so we don't have so many cases. (let ((y (if (and (consp y) (eq (car y) 'quote) (consp (cdr y)) (acl2-numberp (car (cdr y))) (eq (cdr (cdr y)) nil)) (car (cdr y)) y))) (if (acl2-numberp y) (cons 'binary-+ (cons (unary-- y) (cons x nil))) (cons 'binary-+ (cons x (cons (cons 'unary-- (cons y nil)) nil))))) (let ((x (if (and (consp x) (eq (car x) 'quote) (consp (cdr x)) (acl2-numberp (car (cdr x))) (eq (cdr (cdr x)) nil)) (car (cdr x)) x))) (if (acl2-numberp x) (unary-- x) (cons 'unary-- (cons x nil)))))) (defthm booleanp-compound-recognizer (equal (booleanp x) (or (equal x t) (equal x nil))) :rule-classes :compound-recognizer) (in-theory (disable booleanp)) ; integer-abs is just abs if x is an integer and is 0 otherwise. ; integer-abs is used because we don't know that that (abs x) is a ; nonnegative integer when x is an integer. By using integer-abs in ; the defun of acl2-count below we get that the type-prescription for ; acl2-count is a nonnegative integer. (defun integer-abs (x) (declare (xargs :guard t)) (if (integerp x) (if (< x 0) (- x) x) 0)) (defun xxxjoin (fn args) " (xxxjoin fn args) spreads the binary function symbol fn over args, a list of arguments. For example, (xxxjoin '+ '(1 2 3)) = '(+ 1 (+ 2 3)))." (declare (xargs :guard (if (true-listp args) (cdr args) nil) :mode :program)) (if (cdr (cdr args)) (cons fn (cons (car args) (cons (xxxjoin fn (cdr args)) nil))) (cons fn args))) #+acl2-loop-only (defmacro + (&rest rst) (if rst (if (cdr rst) (xxxjoin 'binary-+ rst) (cons 'binary-+ (cons 0 (cons (car rst) nil)))) 0)) ; We now define length (and its subroutine len) so we can use them in ; acl2-count. #-acl2-loop-only (defun-one-output len2 (x acc) (cond ((atom x) acc) (t (len2 (cdr x) (1+ acc))))) #-acl2-loop-only (defun len1 (x acc) ; This function is an optimized version of len2 above, which is a simple ; tail-recursive implementation of len. (declare (type fixnum acc)) (the fixnum ; to assist in ACL2's proclaiming (cond ((atom x) acc) ((eql (the fixnum acc) most-positive-fixnum) #+(or gcl ccl allegro sbcl cmu (and lispworks lispworks-64bit)) ; The error below is entirely optional, and can be safely removed from the ; code. Here is the story. ; We cause an error for the Lisps listed above in order to highlight the ; violation of the following expectation for those Lisps: the length of a list ; is always bounded by most-positive-fixnum. To be safe, we omit CLISP and ; 32-bit LispWorks (where most-positive-fixnum is only 16777215 and 8388607, ; respectively; see the Essay on Fixnum Declarations). But for the Lisps in ; the above readtime conditional, we believe the above expectation because a ; cons takes at least 8 bytes and each of the lisps below has ; most-positive-fixnum of at least approximately 2^29. (error "We have encountered a list whose length exceeds ~ most-positive-fixnum!") -1) (t (len1 (cdr x) (the fixnum (+ (the fixnum acc) 1))))))) (defun len (x) ":Doc-Section ACL2::ACL2-built-ins length of a list~/ ~c[Len] returns the length of a list.~/ A Common Lisp function that is appropriate for both strings and proper lists is ~c[length]; ~pl[length]. The guard for ~c[len] is ~c[t]. (Low-level implementation note. ACL2 provides a highly-optimized implementation of ~c[len], which is tail-recursive and fixnum-aware, that differs from its simple ACL2 definition.) To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t :mode :logic)) #-acl2-loop-only (return-from len (let ((val (len1 x 0))) (if (eql val -1) (len2 x 0) val))) (if (consp x) (+ 1 (len (cdr x))) 0)) #+acl2-loop-only (defun length (x) ":Doc-Section ACL2::ACL2-built-ins length of a string or proper list~/ ~c[Length] is the function for determining the length of a sequence. In ACL2, the argument is required to be either a ~ilc[true-listp] or a string.~/ ~c[Length] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (if (true-listp x) t (stringp x)) :mode :logic)) (if (stringp x) (len (coerce x 'list)) (len x))) #-acl2-loop-only (defun-one-output complex-rationalp (x) (complexp x)) (defun acl2-count (x) ":Doc-Section ACL2::ACL2-built-ins a commonly used measure for justifying recursion~/ ~c[(Acl2-count x)] returns a nonnegative integer that indicates the ``size'' of its argument ~c[x].~/ All ~il[characters] and symbols have ~c[acl2-count 0]. The ~c[acl2-count] of a string is the number of ~il[characters] in it, i.e., its length. The ~c[acl2-count] of a ~ilc[cons] is one greater than the sum of the ~c[acl2-count]s of the ~ilc[car] and ~ilc[cdr]. The ~c[acl2-count] of an integer is its absolute value. The ~c[acl2-count] of a rational is the sum of the ~c[acl2-count]s of the numerator and denominator. The ~c[acl2-count] of a complex rational is one greater than the sum of the ~c[acl2-count]s of the real and imaginary parts." ; We used to define the acl2-count of symbols to be (+ 1 (length ; (symbol-name x))) but then found it useful to make the acl2-count of ; NIL be 0 so that certain normalizations didn't explode the count. ; We then made the count of all symbols 0. This broad stroke was not ; strictly necessary, as far as we can see, it just simplifies the ; definition of acl2-count and does not seem to affect the common ; recursions and inductions. (declare (xargs :guard t)) (if (consp x) (+ 1 (acl2-count (car x)) (acl2-count (cdr x))) (if (rationalp x) (if (integerp x) (integer-abs x) (+ (integer-abs (numerator x)) (denominator x))) (if (complex/complex-rationalp x) (+ 1 (acl2-count (realpart x)) (acl2-count (imagpart x))) (if (stringp x) (length x) 0))))) ; The following rewrite rule may be useful for termination proofs, but ; at this point it seems premature to claim any kind of understanding ; of how to integrate such rules with appropriate linear rules. ; (defthm acl2-count-consp ; (implies (consp x) ; (equal (acl2-count x) ; (+ 1 ; (acl2-count (car x)) ; (acl2-count (cdr x)))))) (defun cond-clausesp (clauses) (declare (xargs :guard t)) (if (consp clauses) (and (consp (car clauses)) (true-listp (car clauses)) (< (len (car clauses)) 3) (cond-clausesp (cdr clauses))) (eq clauses nil))) (defun cond-macro (clauses) (declare (xargs :guard (cond-clausesp clauses))) (if (consp clauses) (if (and (eq (car (car clauses)) t) (eq (cdr clauses) nil)) (if (cdr (car clauses)) (car (cdr (car clauses))) (car (car clauses))) (if (cdr (car clauses)) (list 'if (car (car clauses)) (car (cdr (car clauses))) (cond-macro (cdr clauses))) ; We could instead generate the IF term corresponding to the expansion of the ; following OR term, and that is what we did through Version_3.3. But the ; extra cost of further expanding this OR call is perhaps outweighed by the ; advantage that tools using macroexpand1 can see the OR, which is an odd macro ; in that its logical expansion can result in evaluating the first argument ; twice. (list 'or (car (car clauses)) (cond-macro (cdr clauses))))) nil)) #+acl2-loop-only (defmacro cond (&rest clauses) ":Doc-Section ACL2::ACL2-built-ins conditional based on if-then-else~/ ~c[Cond] is the construct for IF, THEN, ELSE IF, ... The test is against ~c[nil]. The argument list for ~c[cond] is a list of ``clauses'', each of which is a list. In ACL2, clauses must have length 1 or 2.~/ ~c[Cond] is a Common Lisp macro. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (cond-clausesp clauses))) (cond-macro clauses)) ; The function eqlablep is :common-lisp-compliant even during the first pass, ; in order to support the definition of eql, which is in ; *expandable-boot-strap-non-rec-fns* and hence needs to be ; :common-lisp-compliant. (defun eqlablep (x) ":Doc-Section ACL2::ACL2-built-ins the ~il[guard] for the function ~ilc[eql]~/ The predicate ~c[eqlablep] tests whether its argument is suitable for ~ilc[eql], at least one of whose arguments must satisfy this predicate in Common Lisp. ~c[(Eqlablep x)] is true if and only if its argument is a number, a symbol, or a character. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :mode :logic :guard t)) (or (acl2-numberp x) (symbolp x) (characterp x))) ; Note: Eqlablep is the guard on the function eql. Eql is on *expandable-boot- ; strap-non-rec-fns* and is hence expanded by type-set and assume-true-false ; when its guard is established. Thus, the system works best if eqlablep is ; known to be a compound recognizer so that type-set can work with it when it ; sees it in the guard of eql. (defthm eqlablep-recog (equal (eqlablep x) (or (acl2-numberp x) (symbolp x) (characterp x))) :rule-classes :compound-recognizer) (in-theory (disable eqlablep)) (defun eqlable-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of objects each suitable for ~ilc[eql]~/ The predicate ~c[eqlable-listp] tests whether its argument is a ~ilc[true-listp] of objects satisfying ~ilc[eqlablep]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :mode :logic :guard t)) (if (consp l) (and (eqlablep (car l)) (eqlable-listp (cdr l))) (equal l nil))) #+acl2-loop-only (defun eql (x y) (declare (xargs :mode :logic :guard (or (eqlablep x) (eqlablep y)))) ":Doc-Section ACL2::ACL2-built-ins test equality (of two numbers, symbols, or ~il[characters])~/ ~c[(eql x y)] is logically equivalent to ~c[(equal x y)].~/ Unlike ~ilc[equal], ~c[eql] has a ~il[guard] requiring at least one of its arguments to be a number, a symbol, or a character. Generally, ~c[eql] is executed more efficiently than ~ilc[equal]. For a discussion of the various ways to test against 0, ~l[zero-test-idioms]. ~c[Eql] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (equal x y)) #+acl2-loop-only (defun atom (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for atoms~/ ~c[(atom x)] is true if and only if ~c[x] is an atom, i.e., not a ~ilc[cons] pair.~/ ~c[Atom] has a ~il[guard] of ~c[t], and is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard t)) (not (consp x))) ; We use this in the *1* code for coerce. (defun make-character-list (x) ":Doc-Section ACL2::ACL2-built-ins ~il[coerce] to a list of characters~/ Non-characters in the given list are ~il[coerce]d to the character with code 0. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) nil) ((characterp (car x)) (cons (car x) (make-character-list (cdr x)))) (t ; There's nothing special about (code-char 0), but at least it will look ; strange when people come across it. (cons (code-char 0) (make-character-list (cdr x)))))) (defun eqlable-alistp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of pairs whose ~ilc[car]s are suitable for ~ilc[eql]~/ The predicate ~c[eqlable-alistp] tests whether its argument is a ~ilc[true-listp] of ~ilc[consp] objects whose ~ilc[car]s all satisfy ~ilc[eqlablep]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (consp (car x)) (eqlablep (car (car x))) (eqlable-alistp (cdr x)))))) (defun alistp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for association lists~/ ~c[(alistp x)] is true if and only if ~c[x] is a list of ~ilc[cons] pairs.~/ ~c[(alistp x)] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (consp (car l)) (alistp (cdr l)))))) (defthm alistp-forward-to-true-listp (implies (alistp x) (true-listp x)) :rule-classes :forward-chaining) (defthm eqlable-alistp-forward-to-alistp (implies (eqlable-alistp x) (alistp x)) :rule-classes :forward-chaining) #+acl2-loop-only (defun acons (key datum alist) ":Doc-Section ACL2::ACL2-built-ins constructor for association lists~/ ~c[(Acons key datum alist)] equals the result of consing the pair ~c[(cons key datum)] to the front of the association list ~c[alist].~/ ~c[(Acons key datum alist)] has a ~il[guard] of ~c[(alistp alist)]. ~c[Acons] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (alistp alist))) (cons (cons key datum) alist)) #+acl2-loop-only (defun endp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for empty lists~/ In the ACL2 logic, ~c[(endp x)] is the same as ~c[(atom x)]. ~l[atom].~/ Unlike ~ilc[atom], the ~il[guard] for ~c[endp] requires that ~c[x] is a ~ilc[cons] pair or is ~c[nil]. Thus, ~c[endp] is typically used as a termination test for functions that recur on a ~ilc[true-listp] argument. ~l[guard] for general information about ~il[guard]s. ~c[Endp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard (or (consp x) (eq x nil)))) (atom x)) #+acl2-loop-only (defmacro caar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[car]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'car x))) #+acl2-loop-only (defmacro cadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cdr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cdr x))) #+acl2-loop-only (defmacro cdar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[car]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'car x))) #+acl2-loop-only (defmacro cddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cdr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cdr x))) #+acl2-loop-only (defmacro caaar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[caar]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'caar x))) #+acl2-loop-only (defmacro caadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cadr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cadr x))) #+acl2-loop-only (defmacro cadar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cdar]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cdar x))) #+acl2-loop-only (defmacro caddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cddr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cddr x))) #+acl2-loop-only (defmacro cdaar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[caar]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'caar x))) #+acl2-loop-only (defmacro cdadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cadr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cadr x))) #+acl2-loop-only (defmacro cddar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cdar]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cdar x))) #+acl2-loop-only (defmacro cdddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cddr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cddr x))) #+acl2-loop-only (defmacro caaaar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[caaar]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'caaar x))) #+acl2-loop-only (defmacro caaadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[caadr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'caadr x))) #+acl2-loop-only (defmacro caadar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cadar]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cadar x))) #+acl2-loop-only (defmacro caaddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[caddr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'caddr x))) #+acl2-loop-only (defmacro cadaar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cdaar]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cdaar x))) #+acl2-loop-only (defmacro cadadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cdadr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cdadr x))) #+acl2-loop-only (defmacro caddar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cddar]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cddar x))) #+acl2-loop-only (defmacro cadddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[car] of the ~ilc[cdddr]~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cdddr x))) #+acl2-loop-only (defmacro cdaaar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[caaar]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'caaar x))) #+acl2-loop-only (defmacro cdaadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[caadr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'caadr x))) #+acl2-loop-only (defmacro cdadar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cadar]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cadar x))) #+acl2-loop-only (defmacro cdaddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[caddr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'caddr x))) #+acl2-loop-only (defmacro cddaar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cdaar]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cdaar x))) #+acl2-loop-only (defmacro cddadr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cdadr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cdadr x))) #+acl2-loop-only (defmacro cdddar (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cddar]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cddar x))) #+acl2-loop-only (defmacro cddddr (x) ":Doc-Section ACL2::ACL2-built-ins ~ilc[cdr] of the ~ilc[cdddr]~/ See any Common Lisp documentation for details.~/~/" (list 'cdr (list 'cdddr x))) #+acl2-loop-only (defun null (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for the empty list~/ ~c[Null] is the function that checks whether its argument is ~c[nil]. For recursive definitions it is often preferable to test for the end of a list using ~ilc[endp] instead of ~c[null]; ~pl[endp].~/ ~c[Null] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard t)) (eq x nil)) (defun symbol-listp (lst) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of symbols~/ The predicate ~c[symbol-listp] tests whether its argument is a true list of symbols. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t :mode :logic)) (cond ((atom lst) (eq lst nil)) (t (and (symbolp (car lst)) (symbol-listp (cdr lst)))))) (defthm symbol-listp-forward-to-true-listp (implies (symbol-listp x) (true-listp x)) :rule-classes :forward-chaining) (defun symbol-doublet-listp (lst) ; This function returns t iff lst is a true-list and each element is ; a doublet of the form (symbolp anything). (declare (xargs :guard t)) (cond ((atom lst) (eq lst nil)) (t (and (consp (car lst)) (symbolp (caar lst)) (consp (cdar lst)) (null (cddar lst)) (symbol-doublet-listp (cdr lst)))))) ; Essay on Strip-cars -- To Tail Recur or not to Tail Recur? ; We have seen instances where strip-cdrs causes a segmentation fault because ; it overflows the stack. We therefore decided to recode strip-cdrs in a ; tail-recursive way. We therefore decided to do the same thing to strip-cars. ; This essay is about strip-cars but the issues are the same for strip-cdrs, we ; believe. ; First, what is the longest list you can strip-cars without a segmentation ; fault. The answer for ; GCL (GNU Common Lisp) Version(2.2.1) Wed Mar 12 00:47:19 CST 1997 ; is 74790, when the test form is (length (strip-cars test-lst)). Because our ; test forms below are a little more elaborate, we will do our tests on a list ; of length 74000: ; (defvar test-lst ; (loop for i from 1 to 74000 collect (cons i i))) ; Just for the record, how long does it take to do strip-cars 30 times on this ; test-lst? Answer: 6.190 seconds. ; (proclaim-form ; (defun test1 (n) ; (loop for i from 1 to n do (strip-cars test-lst)))) ; ; (compile 'test1) ; ; (time (test1 30)) ; Now the obvious tail recursive version of strip-cars is: ; (proclaim-form ; (defun strip-cars2 (x a) ; (if (endp x) ; (reverse a) ; (strip-cars2 (cdr x) (cons (car (car x)) a))))) ; ; (compile 'strip-cars2) ; ; (proclaim-form ; (defun test2 (n) ; (loop for i from 1 to n do (strip-cars2 test-lst)))) ; ; (compile 'test2) ; ; (time (test2 30)) ; This function is actually faster than strip-cars: 5.530 seconds! That is ; surprising because this function does TWICE as many conses, since it conses ; up the final answer from the accumulated partial one. The reason this ; function beats strip-cars can only be that that the tail-recursive jump is ; quite a lot faster than a function call. ; But Common Lisp allows to avoid consing to do a reverse if we are willing to ; smash the existing spine. And in this case we are, since we have just consed ; it up. So here is a revised function that only does as many conses as ; strip-cars: ; (proclaim-form ; (defun strip-cars3 (x a) ; (if (endp x) ; (nreverse a) ;;; Note destructive reverse! ; (strip-cars3 (cdr x) (cons (car (car x)) a))))) ; ; (compile 'strip-cars3) ; ; (proclaim-form ; (defun test3 (n) ; (loop for i from 1 to n do (strip-cars3 test-lst)))) ; ; (compile 'test3) ; ; (time (test3 30)) ; This function takes 2.490 seconds. ; Therefore, we decided to code strip-cars (and strip-cdrs) in the style of ; strip-cars3 above. ; However, we did not want to do define strip-cars tail-recursively because ; proofs about strip-cars -- both in our system build and in user theorems ; about strip-cars -- would have to use the accumulator-style generalization. ; So we decided to keep strip-cars defined, logically, just as it was and to ; make its #-acl2-loop-only executable code be tail recursive, as above. ; The next paragraph is bogus! But it used to read as follows (where ; strip-cars1 was essentially what we now call reverse-strip-cars). ; Furthermore, we decided that strip-cars1 is a perfectly nice ; function the user might want, so we added it to the logic first -- ; changing the nreverse to a reverse for logical purposes but leaving ; the nreverse in for execution. This way, if the user wants an ; accumulator-version of strip-cars, he can have it and it will be ; very fast. But if he wants a simple recursive version he can have ; it too. ; That is unsound because we don't know that the accumulator is all new conses ; and so we can't smash it! So the use of nreverse is hidden from the user. ; We could, of course, use mbe (which was not available when strip-cars and ; strip-cdrs were originally defined in ACL2). However, we wish to cheat using ; nreverse, so it doesn't seem that nreverse buys us anything. We do note that ; ACL2 can prove the following theorems. ; (defthm reverse-strip-cars-property ; (equal (reverse-strip-cars x acc) ; (revappend (strip-cars x) acc))) ; ; (defthm reverse-strip-cdrs-property ; (equal (reverse-strip-cdrs x acc) ; (revappend (strip-cdrs x) acc))) (defun reverse-strip-cars (x a) (declare (xargs :guard (alistp x))) (cond ((endp x) a) (t (reverse-strip-cars (cdr x) (cons (car (car x)) a))))) (defun strip-cars (x) ":Doc-Section ACL2::ACL2-built-ins collect up all first components of pairs in a list~/ ~c[(strip-cars x)] is the list obtained by walking through the list ~c[x] and collecting up all first components (~ilc[car]s). This function is implemented in a tail-recursive way, despite its logical definition.~/ ~c[(strip-cars x)] has a ~il[guard] of ~c[(alistp x)]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (alistp x))) ; See the Essay on Strip-cars -- To Tail Recur or not to Tail Recur? above. #-acl2-loop-only (nreverse (reverse-strip-cars x nil)) #+acl2-loop-only (cond ((endp x) nil) (t (cons (car (car x)) (strip-cars (cdr x)))))) (defun reverse-strip-cdrs (x a) (declare (xargs :guard (alistp x))) (cond ((endp x) a) (t (reverse-strip-cdrs (cdr x) (cons (cdr (car x)) a))))) (defun strip-cdrs (x) ":Doc-Section ACL2::ACL2-built-ins collect up all second components of pairs in a list~/ ~c[(strip-cdrs x)] has a ~il[guard] of ~c[(alistp x)], and returns the list obtained by walking through the list ~c[x] and collecting up all second components (~ilc[cdr]s). This function is implemented in a tail-recursive way, despite its logical definition. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard (alistp x))) ; See the Essay on Strip-cars -- To Tail Recur or not to Tail Recur? above. #-acl2-loop-only (nreverse (reverse-strip-cdrs x nil)) #+acl2-loop-only (cond ((endp x) nil) (t (cons (cdr (car x)) (strip-cdrs (cdr x)))))) (defmacro let-mbe (bindings &key logic exec) `(let ,bindings (mbe :logic ,logic :exec ,exec))) #+acl2-loop-only (defun return-last (fn eager-arg last-arg) ; Return-last is the one "function" in ACL2 that has no fixed output signature. ; Rather, (return-last expr1 expr2) inherits its stobjs-out from expr2. ; Because of this, we make it illegal to call stobjs-out on the symbol ; return-last. We think of expr1 as being evaluated eagerly because even in ; the raw Lisp implementation of return-last, that argument is always evaluated ; first just as with a function call. By contrast, if fn is a macro then it ; can manipulate last-arg arbitrarily before corresponding evaluation occurs. ; In many applications of return-last, eager-arg will be nil; for others, such ; as with-prover-time-limit, eager-arg will be used to control the evaluation ; of (some version of) last-arg. ; The following little example provides a small check on our handling of ; return-last, both via ev-rec (for evaluating top-level forms) and via more ; direct function evaluation (either *1* functions or their raw Lisp ; counterparts). ; (defun foo (x) ; (time$ (mbe :logic (prog2$ (cw "**LOGIC~%") x) ; :exec (prog2$ (cw "**EXEC~%") x)))) ; (defun bar (x) (foo x)) ; (foo 3) ; logic ; (bar 3) ; logic ; (verify-guards foo) ; (foo 3) ; exec ; (bar 3) ; exec ":Doc-Section ACL2::ACL2-built-ins return the last argument, perhaps with side effects~/ ~c[Return-last] is an ACL2 function that returns its last argument. It is used to implement common utilities such as ~ilc[prog2$] and ~ilc[time$]. For most users, this may already be more than one needs to know about ~c[return-last]; for example, ACL2 tends to avoid printing calls of ~c[return-last] in its output, printing calls of ~ilc[prog2$] or ~ilc[time$] (or other such utilities) instead. If you encounter a call of ~c[return-last] during a proof, then you may find it most useful to consider ~c[return-last] simply as a function defined by the following equation. ~bv[] (equal (return-last x y z) z) ~ev[] It may also be useful to know that unlike other ACL2 functions, ~c[return-last] can take a multiple value as its last argument, in which case this multiple value is returned. The following contrived definition illustrates this point. ~bv[] ACL2 !>(defun foo (fn x y z) (return-last fn x (mv y z))) Since FOO is non-recursive, its admission is trivial. We observe that the type of FOO is described by the theorem (AND (CONSP (FOO FN X Y Z)) (TRUE-LISTP (FOO FN X Y Z))). We used primitive type reasoning. (FOO * * * *) => (MV * *). Summary Form: ( DEFUN FOO ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FOO ACL2 !>(foo 'bar 3 4 5) (4 5) ACL2 !>(mv-let (a b) (foo 'bar 3 4 5) (cons b a)) (5 . 4) ACL2 !> ~ev[] Most readers would be well served to avoid reading the rest of this documentation of ~c[return-last]. For reference, however, below we document it in some detail. We include some discussion of its evaluation, in particular its behavior in raw Lisp, because we expect that most who read further are working with raw Lisp code (and trust tags).~/ ~c[Return-last] is an ACL2 function that can arise from macroexpansion of certain utilities that return their last argument, which may be a multiple value. Consider for example the simplest of these, ~ilc[prog2$]: ~bv[] ACL2 !>:trans1 (prog2$ (cw \"Some CW printing...~~%\") (+ 3 4)) (RETURN-LAST 'PROGN (CW \"Some CW printing...~~%\") (+ 3 4)) ACL2 !> ~ev[] Notice that a call of ~c[prog2$] macroexpands to a call of ~c[return-last] in which the first argument is ~c[(quote progn)]. Although ~c[return-last] is a function in the ACL2 world, it is implemented ``under the hood'' as a macro in raw Lisp, as the following log (continuing the example above) illustrates. ~bv[] ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'PROGN (CW \"Some CW printing...~~%\") (+ 3 4))) (PROGN (LET ((*AOKP* T)) (CW \"SOME CW PRINTING...~~%\")) (+ 3 4)) T ? [RAW LISP] ~ev[] Thus, the original ~c[prog2$] call generates a corresponding call of ~c[progn] in raw Lisp, which in turn causes evaluation of each argument and returns whatever is returned by evaluation of the last (second) argument. (Remark for those who use ~ilc[defattach]. The binding of ~c[*aokp*] to ~c[t] is always included for the second argument as shown except when the first argument is of the form ~c[(QUOTE M)] where ~c[M] is a macro, or (less important) when the first argument is a symbol or a cons whose car is ~c[QUOTE]. This binding allows ACL2 to use attachments in the second argument of ~c[return-last] (hence, in the first argument of ~ilc[prog2$]), even in contexts such as proofs in which attachments are normally not allowed. Those who use the experimental HONS version of ACL2 (~pl[hons-and-memoization]) will see an additional binding in the above single-step macroexpansion, which allows the storing of memoized results even when that would otherwise be prevented because of the use of attachments.) In general, a form ~c[(return-last (quote F) X Y)] macroexpands to ~c[(F X Y)], where ~c[F] is defined in raw Lisp to return its last argument. The case that ~c[F] is ~c[progn] is a bit misleading, because it is so simple. More commonly, macroexpansion produces a call of a macro defined in raw Lisp that may produce side effects. Consider for example the ACL2 utility ~ilc[with-guard-checking], which is intended to change the ~il[guard]-checking mode to the indicated value (~pl[with-guard-checking]). ~bv[] ACL2 !>(with-guard-checking :none (car 3)) ; no guard violation NIL ACL2 !>:trans1 (with-guard-checking :none (car 3)) (WITH-GUARD-CHECKING1 (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) ACL2 !>:trans1 (WITH-GUARD-CHECKING1 (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) (RETURN-LAST 'WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3))) (WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) T ? [RAW LISP] (pprint (macroexpand-1 '(WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)))) (LET ((ACL2_GLOBAL_ACL2::GUARD-CHECKING-ON (CHK-WITH-GUARD-CHECKING-ARG :NONE))) (DECLARE (SPECIAL ACL2_GLOBAL_ACL2::GUARD-CHECKING-ON)) (CAR 3)) ? [RAW LISP] ~ev[] The above raw Lisp code binds the state global variable ~c[guard-checking-on] to ~c[:none], as ~c[chk-with-guard-checking-arg] is just the identity function except for causing a hard error for an illegal input. The intended use of ~c[return-last] is that the second argument is evaluated first in a normal manner, and then the third argument is evaluated in an environment that may depend on the value of the second argument. (For example, the macro ~ilc[with-prover-time-limit] macroexpands to a call of ~c[return-last] with a first argument of ~c['WITH-PROVER-TIME-LIMIT1-RAW], a second argument that evaluates to a numeric time limit, and a third argument that is evaluated in an environment where the theorem prover is restricted to avoid running longer than that time limit.) Although this intended usage model is not strictly enforced, it is useful to keep in mind in the following description of how calls of ~c[return-last] are handled by the ACL2 evaluator. When a form is submitted in the top-level loop, it is handled by ACL2's custom evaluator. That evaluator is specified to respect the semantics of the expression supplied to it: briefly put, if an expression ~c[E] evaluates to a value ~c[V], then the equality ~c[(equal E (quote V))] should be a theorem. Notice that this specification does not discuss the side-effects that may occur when evaluating a call of ~c[return-last], so we discuss that now. Suppose that the ACL2 evaluator encounters the call ~c[(return-last 'fn expr1 expr2)]. First it evaluates ~c[expr1]. If this evaluation succeeds without error, then it constructs an expression of the form ~c[(fn *x* ev-form)], where *x* is a Lisp variable bound to the result of evaluating ~c[expr1] and ~c[ev-form] is a call of the evaluator for ~c[expr2]. (Those who want implementation details are invited to look at function ~c[ev-rec-return-last] in ACL2 source file ~c[translate.lisp].) There are exceptions if ~c[fn] is ~c[progn], ~c[ec-call1-raw], ~c[with-guard-checking1-raw], or ~c[mbe1-raw], but the main idea is the same: do a reasonable job emulating the behavior of a raw-Lisp call of ~c[return-last]. The following log shows how a ~ilc[time$] call can generate a call of the evaluator for the last argument of ~c[return-last] (arguent ~c[expr2], above). We use ~c[:]~ilc[trans1] to show single-step macroexpansions, which indicate how a call of ~ilc[time$] expands to a call of ~c[return-last]. The implementation actually binds the Lisp variable ~c[*RETURN-LAST-ARG3*] to ~c[expr2] before calling the ACL2 evaluator, ~c[ev-rec]. ~bv[] ACL2 !>:trans1 (time$ (+ 3 4)) (TIME$1 (LIST 0 NIL NIL NIL NIL) (+ 3 4)) ACL2 !>:trans1 (TIME$1 (LIST 0 NIL NIL NIL NIL) (+ 3 4)) (RETURN-LAST 'TIME$1-RAW (LIST 0 NIL NIL NIL NIL) (+ 3 4)) ACL2 !>(time$ (+ 3 4)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.00 seconds realtime, 0.00 seconds runtime ; (1,120 bytes allocated). 7 ACL2 !> ~ev[] We now show how things can go wrong in other than the ``intended use'' case described above. In the example below, the macro ~c[mac-raw] is operating directly on the syntactic representation of its first argument, which it obtains of course as the second argument of a ~c[return-last] call. Again this ``intended use'' of ~c[return-last] requires that argument to be evaluated and then only its result is relevant; its syntax is not supposed to matter. We emphasize that only top-level evaluation depends on this ``intended use''; once evaluation is passed to Lisp, the issue disappears. We illustrate below how to use the ~ilc[top-level] utility to avoid this issue; ~pl[top-level]. The example uses the utility ~c[defmacro-last] to ``install'' special handling of the raw-Lisp macro ~c[mac-raw] by ~c[return-last]; later below we discuss ~c[defmacro-last]. ~bv[] ACL2 !>(defttag t) TTAG NOTE: Adding ttag :T from the top level loop. T ACL2 !>(progn! (set-raw-mode t) (defmacro mac-raw (x y) `(progn (print (quote ,(cadr x))) (terpri) ; newline ,y))) Summary Form: ( PROGN! (SET-RAW-MODE T) ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) NIL ACL2 !>(defmacro-last mac) [[ ... output omitted ... ]] RETURN-LAST-TABLE ACL2 !>(return-last 'mac-raw '3 nil) *********************************************** ************ ABORTING from raw Lisp *********** Error: Fault during read of memory address #x120000300006 *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !>(top-level (return-last 'mac-raw '3 nil)) 3 NIL ACL2 !> ~ev[] We next describe how to extend the behavior of ~c[return-last]. This requires an active trust tag (~pl[defttag]), and is accomplished by extending a ~il[table] provided by ACL2, ~pl[return-last-table]. Rather than using ~ilc[table] ~il[events] directly for this purpose, it is probably more convenient to use a macro, ~c[defmacro-last]. We describe the community book ~c[books/misc/profiling.lisp] in order to illustrate how this works. The events in that book include the following, which are described below. ~bv[] (defttag :profiling) (progn! (set-raw-mode t) (load (concatenate 'string (cbd) \"profiling-raw.lsp\"))) (defmacro-last with-profiling) ~ev[] The first event introduces a trust tag. The second loads a file into raw Lisp that defines a macro, ~c[with-profiling-raw], which can do profiling for the form to be evaluated. The third introduces an ACL2 macro ~c[with-profiling], whose calls expand into calls of the form ~c[(return-last (quote with-profiling-raw) & &)]. The third event also extends ~ilc[return-last-table] so that these calls will expand in raw Lisp to calls of ~c[with-profiling-raw]. The example above illustrates the following methodology for introducing a macro that returns its last argument but produces useful side-effects with raw Lisp code. ~bq[] (1) Introduce a trust tag (~pl[defttag]). (2) Using ~ilc[progn!], load into raw Lisp a file defining a macro, ~c[RAW-NAME], that takes two arguments, returning its last (second) argument but using the first argument to produce desired side effects during evaluation of that last argument. (3) Evaluate the form ~c[(defmacro-last NAME :raw RAW-NAME)]. This will introduce ~c[NAME] as an ACL2 macro that expands to a corresponding call of ~c[RAW-NAME] (see (2) above) in raw Lisp. The specification of keyword value of ~c[:raw] as ~c[RAW-NAME] may be omitted if ~c[RAW-NAME] is the result of modifying the symbol ~c[NAME] by suffixing the string ~c[\"-RAW\"] to the ~ilc[symbol-name] of ~c[NAME].~eq[] WARNING: Not every use of ~c[return-last] can be soundly evaluated outside a function body. The reason is that ACL2's evaluator, ~c[ev-rec], recurs through terms that are presented in the top-level loop, and handles ~c[return-last] calls in a special manner: basically, the call of ~c[ev-rec] on the form ~c[(return-last 'mac-raw x y)] leads to evaluation of a macro call of the form ~c[(mac-raw *return-last-arg2* (ev-rec ...))], where *return-last-arg2* is a global variable bound to the result of evaluating ~c[x] with ~c[ev-rec]. Consider the following example. ~bv[] (defttag t) (set-raw-mode-on state) (defmacro mac-raw (str y) ; print message is an atom `(let ((result (consp ,y)) (str ,str)) (or result (prog2$ (fmx ,str ',y) nil)))) (set-raw-mode-off state) (defmacro-last mac) ; Horrible error: (mac \"Not a cons: ~~x0\~~%\" 17) ; Works, but probably many would consider it awkward to use top-level: (top-level (mac \"Not a cons: ~~x0\~~%\" 17)) ~ev[] In such cases we suggest supplying keyword ~c[:top-level-ok nil] to the call of ~c[defmacro-last], for example: ~bv[] (defmacro-last mac :top-level-ok nil) ~ev[] Then any attempt to call ~c[mac] at the top level, as opposed to inside a function body, will cause a clean error before evaluation begins. It is useful to explore what is done by ~c[defmacro-last]. ~bv[] ACL2 !>:trans1 (defmacro-last with-profiling) (PROGN (DEFMACRO WITH-PROFILING (X Y) (LIST 'RETURN-LAST (LIST 'QUOTE 'WITH-PROFILING-RAW) X Y)) (TABLE RETURN-LAST-TABLE 'WITH-PROFILING-RAW 'WITH-PROFILING)) ACL2 !>:trans1 (with-profiling '(assoc-eq fgetprop rewrite) (mini-proveall)) (RETURN-LAST 'WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL)) ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL))) (WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL)) T ? [RAW LISP] ~ev[] To understand the macro ~c[with-profiling-raw] you could look at the community book loaded above: ~c[books/misc/profiling-raw.lsp]. We mentioned above that ACL2 tends to print calls of ~ilc[prog2$] or ~ilc[time$] (or other such utilities) instead of calls of ~c[return-last]. Here we elaborate that point. ACL2's `~c[untranslate]' utility treats ~c[(return-last (quote F) X Y)] as ~c[(G X Y)] if ~c[F] corresponds to the symbol ~c[G] in ~c[return-last-table]. However, it is generally rare to encounter such a term during a proof, since calls of ~c[return-last] are generally expanded away early during a proof. Calls of ~c[return-last] that occur in code ~-[] forms submitted in the top-level ACL2 loop, and definition bodies other than those marked as ~ilc[non-executable] (~pl[defun-nx]) ~-[] have the following restriction: if the first argument is of the form ~c[(quote F)], then ~c[F] must be an entry in ~c[return-last-table]. There are however four exceptions: the following symbols are considered to be keys of ~c[return-last-table] even if they are no longer associated with non-~c[nil] values, say because of a ~ilc[table] event with keyword ~c[:clear]. ~bq[] * ~c[progn], associated with ~ilc[prog2$]~nl[] * ~c[mbe1-raw], associated with ~c[mbe1], a version of ~c[mbe]~nl[] * ~c[ec-call1-raw], associated with ~c[ec-call1] (a variant of ~ilc[ec-call])~nl[] * ~c[with-guard-checking1-raw], associated with ~c[with-guard-checking1] (a variant of ~ilc[with-guard-checking]) ~eq[] Note that because of its special status, it is illegal to trace ~c[return-last]. We conclude by warning that as a user, you take responsibility for not compromising the soundness or error handling of ACL2 when you define a macro in raw Lisp and especially when you install it as a key of ~ilc[return-last-table], either directly or (more likely) using ~c[defmacro-last]. In particular, be sure that you are defining a macro of two arguments that always returns the value of its last argument, returning the complete multiple value if that last argument evaluates to a multiple value. The following is correct, and illustrates care taken to return multiple values. ~bv[] :q (defmacro my-time1-raw (val form) (declare (ignore val)) `(let ((start-time (get-internal-run-time)) (result (multiple-value-list ,form)) (end-time (get-internal-run-time))) (format t \"Total time: ~~s~~%\" (float (/ (- end-time start-time) internal-time-units-per-second))) (values-list result))) (lp) (defttag t) (defmacro-last my-time1) (defmacro my-time (form) `(my-time1 nil ,form)) ~ev[] Then for example: ~bv[] ACL2 !>(my-time (equal (make-list 1000000) (make-list 1000000))) Total time: 0.12 T ACL2 !> ~ev[] But if instead we provide the following more naive implementation, of the above raw Lisp macro, the above evaluation can produce an error, for example if the host Lisp is CCL. ~bv[] (defmacro my-time1-raw (val form) (declare (ignore val)) `(let ((start-time (get-internal-run-time)) (result ,form) (end-time (get-internal-run-time))) (format t \"Total time: ~~s~~%\" (float (/ (- end-time start-time) internal-time-units-per-second))) result)) ; WRONG -- need multiple values returned! ~ev[] Here is a second, similar example. This time we'll start with the error; can you spot it? ~bv[] (defttag t) (progn! (set-raw-mode t) (defmacro foo-raw (x y) `(prog1 ,y (cw \"Message showing argument 1: ~~x0~~%\" ,x)))) (defmacro-last foo) ~ev[] We then can wind up with a hard Lisp error: ~bv[] ACL2 !>(foo 3 (mv 4 5)) Message showing argument 1: 3 *********************************************** ************ ABORTING from raw Lisp *********** Error: value NIL is not of the expected type REAL. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !> ~ev[] Here is a corrected version of the above macro. The point here is that ~c[prog1] returns a single value, while ~c[our-multiple-value-prog1] returns all the values that are returned by its first argument. ~bv[] (progn! (set-raw-mode t) (defmacro foo-raw (x y) `(our-multiple-value-prog1 ;; better ,y (cw \"Message showing argument 1: ~~x0~~%\" ,x)))) ~ev[] To see the ACL2 definition of this function, ~pl[pf].~/" (declare (ignore fn eager-arg) (xargs :guard ; Warning: If you change this guard, also consider changing the handling of ; return-last in oneify, which assumes that the guard is t except for the ; 'mbe1-raw case. ; We produce a guard to handle the mbe1 case (from expansion of mbe forms). In ; practice, fn is likely to be a constant, in which case we expect this guard ; to resolve to its true branch or its false branch. (if (equal fn 'mbe1-raw) (equal last-arg eager-arg) t) :mode :logic)) last-arg) #-acl2-loop-only (defmacro return-last (qfn arg2 arg3) (let* ((fn (and (consp qfn) (eq (car qfn) 'quote) (consp (cdr qfn)) (symbolp (cadr qfn)) (null (cddr qfn)) (cadr qfn))) (arg2 ; There is no logical problem with using attachments when evaluating the second ; argument of return-last, because logically the third argument provides the ; value(s) of a return-last call -- the exception being the evaluation of the ; :exec argument of an mbe call (or, equivalent evaluation by way of mbe1, ; etc.). We not only bind *aokp* to t, but we also bind *attached-fn-called* ; so that no changes to this variable will prevent the storing of memoization ; results. ; See also the related treatment of aokp in ev-rec-return-last. (cond ((or (eq fn 'mbe1-raw) ; good test, though subsumed by the next line (and fn (macro-function fn)) (symbolp arg2) ; no point in doing extra bindings below (and (consp arg2) (eq (car arg2) ; no point in doing extra bindings below 'quote))) arg2) (t `(let ((*aokp* t) #+hons (*attached-fn-called* t)) ,arg2))))) (cond ((and fn (fboundp fn)) ; Translation for evaluation requires that if the first argument is a quoted ; non-nil symbol, then that symbol (here, fn) must be a key in ; return-last-table. The function chk-return-last-entry checks that when fn ; was added to the table, it was fboundp in raw Lisp. Note that fboundp holds ; for functions, macros, and special operators. ; An alternative may seem to be to lay down code that checks to see if fn is in ; return-last-table, and if not then replace it by progn. But during early ; load of compiled files we skip table events (which are always skipped in raw ; Lisp), yet the user may expect a call of return-last on a quoted symbol to ; have the desired side-effects in that case. (list fn arg2 arg3)) (t (list 'progn arg2 arg3))))) #-acl2-loop-only (defmacro mbe1-raw (exec logic) ; We rely on this macroexpansion in raw Common Lisp. See in particular the ; code and comment regarding mbe1-raw in guard-clauses. (declare (ignore logic)) exec) (defmacro mbe1 (exec logic) ; See also must-be-equal. ; Suppose that during a proof we encounter a term such as (return-last ; 'mbe1-raw exec logic), but we don't know that logic and exec are equal. ; Fortunately, ev-rec will only evaluate the logic code for this return-last ; form, as one might expect. ":Doc-Section ACL2::ACL2-built-ins attach code for execution~/ The form ~c[(mbe1 exec logic)] is equivalent to the forms ~c[(mbe :logic logic :exec exec)] and ~c[(must-be-equal logic exec)]. ~l[mbe].~/~/" `(return-last 'mbe1-raw ,exec ,logic)) (defmacro must-be-equal (logic exec) ; We handle must-be-equal using return-last, so that must-be-equal isn't a ; second function that needs special stobjs-out handling. But then we need a ; version of must-be-equal with the logic input as the last argument, since ; that is what is returned in the logic. We call that mbe1, but we leave ; must-be-equal as we move the the return-last implementation (after v4-1, ; released Sept., 2010), since must-be-equal has been around since v2-8 (March, ; 2004). ":Doc-Section ACL2::ACL2-built-ins attach code for execution~/~/ The form ~c[(must-be-equal logic exec)] evaluates to ~c[logic] in the ACL2 logic but evaluates to ~c[exec] in raw Lisp. The point is to be able to write one definition to reason about logically but another for evaluation. Please ~pl[mbe] and ~pl[mbt] for appropriate macros to use, rather than calling ~c[must-be-equal] directly, since it is easy to commute the arguments of ~c[must-be-equal] by accident. In essence, the guard for ~c[(must-be-equal x y)] is ~c[(equal x y)]. However, note that ~c[must-be-equal] is a macro: ~c[(must-be-equal logic exec)] expands to ~c[(mbe1 exec logic)], which expands to a call of ~ilc[return-last]." `(mbe1 ,exec ,logic)) (defmacro mbe (&key (exec 'nil exec-p) (logic 'nil logic-p)) ":Doc-Section ACL2::ACL2-built-ins attach code for execution~/ The macro ~c[mbe] (``must be equal'') can be used in function definitions in order to cause evaluation to use alternate code to that provided for the logic. An example is given below. However, the use of ~c[mbe] can lead to non-terminating computations. ~l[defexec], perhaps after reading the present documentation, for a way to prove termination. In the ACL2 logic, ~c[(mbe :exec exec-code :logic logic-code)] equals ~c[logic-code]; the value of ~c[exec-code] is ignored. However, in raw Lisp it is the other way around: this form macroexpands simply to ~c[exec-code]. ACL2's ~il[guard] verification mechanism ensures that the raw Lisp code is only evaluated when appropriate, since the guard proof obligations generated for (the macroexpansion of) this call of ~c[mbe] include not only the guard proof obligations from ~c[exec-code], but also, under suitable contextual assumptions, the term ~c[(equal exec-code logic-code)]. ~l[verify-guards] (in particular, for discussion of the contextual assumptions from the ~c[:guard] and ~ilc[IF]-tests) and, for general discussion of guards, ~pl[guard]. Normally, during evaluation of an ~c[mbe] call, only the ~c[:logic] code is evaluated unless the call is in the body of a ~il[guard]-verified function, in which case only the ~c[:exec] code is evaluated. This implies that equality of ~c[:exec] and ~c[:logic] code is never checked at runtime. (Rather, such equality is proved when verifying guards.) We started with ``normally'' above because there is an exception: during a ``safe mode'', which is used in macroexpansion and evaluation of ~ilc[defconst] forms, the ~c[:logic] and ~c[:exec] code are both evaluated and their equality is checked. Note that the ~c[:exec] and the ~c[:logic] code in an ~c[mbe] call must have the same return type. For example, one cannot return ~c[(]~ilc[mv]~c[ * *)] while the other returns just a single value. Also ~pl[mbt], which stands for ``must be true.'' You may find it more natural to use ~ilc[mbt] for certain applications, as described in its ~il[documentation].~/ Here is an example of the use of ~c[mbe]. Suppose that you want to define factorial in the usual recursive manner, as follows. ~bv[] (defun fact (n) (if (zp n) 1 (* n (fact (1- n))))) ~ev[] But perhaps you want to be able to execute calls of ~c[fact] on large arguments that cause stack overflows, perhaps during proofs. (This isn't a particularly realistic example, but it should serve.) So, instead you can define this tail-recursive version of factorial: ~bv[] (defun fact1 (n acc) (declare (xargs :guard (and (integerp n) (>= n 0) (integerp acc)))) (if (zp n) acc (fact1 (1- n) (* n acc)))) ~ev[] We are now ready to define ~c[fact] using ~c[mbe]. Our intention is that logically, ~c[fact] is as shown in the first definition above, but that ~c[fact] should be executed by calling ~c[fact1]. Notice that we defer ~il[guard] verification, since we are not ready to prove the correspondence between ~c[fact1] and ~c[fact]. ~bv[] (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)) :verify-guards nil)) (mbe :exec (fact1 n 1) :logic (if (zp n) 1 (* n (fact (1- n)))))) ~ev[] Next, we prove the necessary correspondence lemmas. Notice the inclusion of a community book to help with the arithmetic reasoning. ~bv[] (include-book \"books/arithmetic/top-with-meta\") (defthm fact1-fact (implies (integerp acc) (equal (fact1 n acc) (* acc (fact n))))) ~ev[] We may now do guard verification for ~c[fact], which will allow the execution of the raw Lisp ~c[fact] function, where the above ~c[mbe] call expands simply to ~c[(fact1 n 1)]. ~bv[] (verify-guards fact) ~ev[] Now that guards have been verified, a trace of function calls illustrates that the evaluation of calls of ~c[fact] is passed to evaluation of calls of ~c[fact1]. The outermost call below is of the logical function stored for the definition of ~c[fact]; all the others are of actual raw Common Lisp functions. ~bv[] ACL2 !>(trace$ fact fact1) NIL ACL2 !>(fact 3) 1> (ACL2_*1*_ACL2::FACT 3) 2> (FACT 3) 3> (FACT1 3 1) 4> (FACT1 2 3) 5> (FACT1 1 6) 6> (FACT1 0 6) <6 (FACT1 6) <5 (FACT1 6) <4 (FACT1 6) <3 (FACT1 6) <2 (FACT 6) <1 (ACL2_*1*_ACL2::FACT 6) 6 ACL2 !> ~ev[] You may occasionally get warnings when you compile functions defined using ~c[mbe]. (For commands that invoke the compiler, ~pl[compilation].) These can be inhibited by using an ~c[ignorable] ~ilc[declare] form. Here is a simple but illustrative example. Note that the declarations can optionally be separated into two ~ilc[declare] forms. ~bv[] (defun foo (x y) (declare (ignorable x) (xargs :guard (equal x y))) (mbe :logic x :exec y)) ~ev[] Finally, we observe that when the body of a function contains a term of the form ~c[(mbe :exec exec-code :logic logic-code)], the user is very unlikely to see any logical difference than if this were replaced by ~c[logic-code]. ACL2 takes various steps to ensure this. For example, the proof obligations generated for admitting a function treat the above ~c[mbe] term simply as ~c[logic-code]. Function expansion, ~c[:use] ~il[hints], ~c[:]~ilc[definition] rules, generation of ~il[constraint]s for functional instantiation, and creation of rules of class ~c[:]~ilc[rewrite] and ~c[:]~ilc[forward-chaining] also treat ~c[mbe] calls as their ~c[:logic] code." (declare (xargs :guard (and exec-p logic-p)) (ignorable exec-p logic-p)) `(mbe1 ,exec ,logic)) (defmacro mbt (x) ":Doc-Section ACL2::ACL2-built-ins introduce a test not to be evaluated~/ The macro ~c[mbt] (``must be true'') can be used in order to add code in order to admit function definitions in ~c[:]~ilc[logic] mode, without paying a cost in execution efficiency. Examples below illustrate its intended use. Semantically, ~c[(mbt x)] equals ~c[x]. However, in raw Lisp ~c[(mbt x)] ignores ~c[x] entirely, and macroexpands to ~c[t]. ACL2's ~il[guard] verification mechanism ensures that the raw Lisp code is only evaluated when appropriate, since a guard proof obligation ~c[(equal x t)] is generated. ~l[verify-guards] and, for general discussion of guards, ~pl[guard]. Also ~pl[mbe], which stands for ``must be equal.'' Although ~c[mbt] is more natural in many cases, ~c[mbe] has more general applicability. In fact, ~c[(mbt x)] is essentially defined to be ~c[(mbe :logic x :exec t)].~/ We can illustrate the use of ~c[mbt] on the following generic example, where ~c[], ~c[], ~c[], and ~c[] are intended to be terms involving only the variable ~c[x]. ~bv[] (defun foo (x) (declare (xargs :guard )) (if (foo ) )) ~ev[] In order to admit this function, ACL2 needs to discharge the proof obligation that ~c[] is smaller than ~c[x], namely: ~bv[] (implies (o< (acl2-count ~c[]) (acl2-count x))) ~ev[] But suppose we need to know that ~c[] is true in order to prove this. Since ~c[] is only the ~il[guard], it is not part of the logical definition of ~c[foo]. A solution is to add the guard to the definition of ~c[foo], as follows. ~bv[] (defun foo (x) (declare (xargs :guard )) (if (mbt ) (if (foo ) ) nil)) ~ev[] If we do this using ~c[] rather than ~c[(mbt )], then evaluation of every recursive call of ~c[foo] will cause the evaluation of (the appropriate instance of) ~c[]. But since ~c[(mbt )] expands to ~c[t] in raw Lisp, then once we verify the guards of ~c[foo], the evaluations of ~c[] will be avoided (except at the top level, when we check the guard before allowing evaluation to take place in Common Lisp). Other times, the guard isn't the issue, but rather, the problem is that a recursive call has an argument that itself is a recursive call. For example, suppose that ~c[] is of the form ~c[(foo )]. There is no way we can hope to discharge the termination proof obligation shown above. A standard solution is to add some version of this test: ~bv[] (mbt (o< (acl2-count ~c[]) (acl2-count x))) ~ev[] Here is a specific example based on one sent by Vernon Austel. ~bv[] (defun recurX2 (n) (declare (xargs :guard (and (integerp n) (<= 0 n)) :verify-guards nil)) (cond ((zp n) 0) (t (let ((call (recurX2 (1- n)))) (if (mbt (< (acl2-count call) n)) (recurX2 call) 1 ;; this branch is never actually taken ))))) (defthm recurX2-0 (equal (recurX2 n) 0)) (verify-guards recurX2) ~ev[] If you ~c[(]~ilc[trace$]~c[ acl2-count)], you will see that evaluation of ~c[(recurX2 2)] causes several calls of ~ilc[acl2-count] before the ~ilc[verify-guards]. But this evaluation does not call ~c[acl2-count] after the ~c[verify-guards], because the ACL2 evaluation mechanism uses raw Lisp to do the evaluation, and the form ~c[(mbt (< (acl2-count call) n))] macroexpands to ~c[t] in Common Lisp. You may occasionally get warnings when you compile functions defined using ~c[mbt]. (For commands that invoke the compiler, ~pl[compilation].) These can be inhibited by using an ~c[ignorable] ~ilc[declare] form. Here is a simple but illustrative example. Note that the declarations can optionally be separated into two ~ilc[declare] forms. ~bv[] (defun foo (x y) (declare (ignorable x) (xargs :guard (equal x t))) (and (mbt x) y)) ~ev[]" `(mbe1 t ,x)) (defdoc equality-variants ; Consider position, remove-duplicates, and remove. In Common Lisp, all three ; of these primitives can take strings. Through Version_4.2, position and ; remove-duplicates supported string arguments, while remove did not. Note ; however that remove-duplicates-eql and remove-duplicates-equal did not ; support string arguments. For backward compatibility with Version_4.2 and ; earlier, we leave remove-duplicates-equal unchanged. When there is ; sufficient demand we can extend remove. ":Doc-Section ACL2::Programming versions of a function using different equality tests~/ The ACL2 environment includes not only a logic but also a programming language, which is based on Common Lisp. Execution efficiency may be increased by using fast equality tests: ~ilc[eq] for symbols and ~ilc[eql] for numbers, symbols, and characters (~pl[eqlablep]). Several list-processing functions built into ACL2 thus have three variants, depending on whether the equality function used is ~ilc[eq], ~ilc[eql], or ~ilc[equal]; a list is provided below. ACL2 has taken measures to ensure that one can reason about a single logical function even when one uses these different variants. Consider for example the case of list membership. Common Lisp provides a utility for this purposes, ~ilc[member], which can take a ~c[:TEST] keyword argument, default ~ilc[eql]. So for example, one might write ~bv[] (member a x :TEST 'eq) ~ev[] if either ~c[a] is a symbol or ~c[x] is a list of symbols, so that the fastest equality test (~ilc[eq]) may be used when comparing ~c[a] to successive elements of the list, ~c[x]. One might elsewhere write ~c[(member b (foo y))], which is equivalent to ~c[(member b (foo y) :TEST 'eql)], for example if ~c[b] is a number. If one wants to reason about both ~c[(member a x :TEST 'eq)] and ~c[(member b y)], it might be helpful for both calls of ~c[member] to be the same logically, even though Common Lisp will execute them differently (using ~ilc[eq] or ~ilc[eql], respectively). ACL2 arranges that in fact, both references to ~ilc[member] generate calls of ~ilc[member-equal] in the theorem prover. In fact, since ~ilc[member] can take the optional ~c[:TEST] keyword argument, then in ACl2 it must be defined as a macro, not a function (~pl[defun]). ACL2 arranges that a call of ~c[member] generates a corresponding call of the function ~ilc[member-equal], regardless of the value of ~c[TEST], in a manner that produces ~ilc[member-equal] in prover output. More generally, you can expect ACL2 to treat your use of ~ilc[member] as though you had written ~ilc[member-equal], for example in the way it stores ~ilc[rewrite] rules and other kinds of rules as well (~pl[rule-classes]). We say little here about how this is all arranged by ACL2, other than to mention that ~ilc[mbe] is utilized (so, you might see mention in proof logs) of the function ~ilc[return-last] that implements ~ilc[mbe]. Such details, which probably can be ignored by most users, may be found elsewhere; ~pl[equality-variants-details]. As a convenience to the user, the macro ~c[member-eq] is provided that expands to a corresponding call of ~c[member] with ~c[:TEST 'eq], as follows. ~bv[] ACL2 !>:trans1 (member-eq (foo x) (bar y)) (MEMBER (FOO X) (BAR Y) :TEST 'EQ) ACL2 !> ~ev[] For efficiency we recommend using the ~c[-equal] equality variant, for example ~ilc[member-equal] or ~ilc[member ... :TEST 'equal], in certain contexts: ~ilc[defmacro], ~ilc[defpkg], ~ilc[defconst], and ~ilc[value-triple] forms. However, the implementation of equality variants has been designed so that when defining a function, one may choose freely in a definition an equality variant of primitive ~c[F], to get efficient execution but where subsequent reasoning is about ~c[F-equal]. For details about the above recommendation and for a discussion of the implementation, ~pl[equality-variants-details]. The following alphabetical list includes all primitives that have equality variants. Each macro ~c[F] listed below takes an optional ~c[:TEST] keyword argument of ~c['eq], ~c['eql], or ~c['equal], where ~c['eql] is the default. For each such ~c[F], a function ~c[F-equal] is defined such that for logical purposes (in particular theorem proving), each call of ~c[F] expands to a corresponding call of ~c[F-equal]. For convenience, a macro ~c[F-eq] is also defined, so that a call of ~c[F-eq] expands to a corresponding call of ~c[F] with ~c[:TEST 'eq]. ~bf[] ~ilc[add-to-set] ~ilc[assoc] ~ilc[delete-assoc] ~ilc[intersection$] ; (see Note below) ~ilc[intersectp] ~ilc[member] ~ilc[no-duplicatesp] ~c[position-ac] ~ilc[position] ~ilc[put-assoc] ~ilc[rassoc] ~ilc[remove-duplicates] ~ilc[remove1] ~ilc[remove] ~ilc[set-difference$] ; (see Note below) ~ilc[subsetp] ~ilc[union$] ; (see Note below) ~ef[] Note: Three of the macros above have names ending with the character, `~c[$]': ~ilc[intersection$], ~ilc[set-difference$], and ~ilc[union$]. In each case there is a corresponding Common Lisp primitive without the trailing `~c[$]': ~c[intersection], ~c[set-difference], and ~c[union]. However, Common Lisp does not specify the order of elements in the list returned by those primitives, so ACL2 has its own. Nevertheless, the only use of the trailing `~c[$]' is to distinguish the primitives; associated functions and macros, for example ~c[union-eq] and ~c[intersection-equal], do not include the `~c[$]' character in their names.~/~/") (defdoc equality-variants-details ":Doc-Section equality-variants details about ~il[equality-variants]~/ Here we present details about equality variants, none of which is likely to be important to the majority of ACL2 users. Please ~pl[equality-variants] for relevant background. We begin by presenting ~il[events] that implement the equality variants for ~ilc[member], as these illustrate the events introduced for all macros having equality variants. The definition of ~ilc[member], just below, calls the macro ~c[let-mbe], which in turn is just an abbreviation for a combination of ~ilc[let] and ~ilc[mbe]. ~bv[] (defmacro let-mbe (bindings &key logic exec) `(let ,bindings (mbe :logic ,logic :exec ,exec))) ~ev[] This use of ~ilc[let] arranges that each argument of a call of ~c[member] is evaluated only once. ~bv[] (defmacro member (x l &key (test ''eql)) (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (l ,l)) :logic (member-equal x l) :exec (member-eq-exec x l))) ((equal test ''eql) `(let-mbe ((x ,x) (l ,l)) :logic (member-equal x l) :exec (member-eql-exec x l))) (t ; (equal test 'equal) `(member-equal ,x ,l)))) ~ev[] Inspection of the definition above shows that every call of ~ilc[member] expands to one that is logically equivalent to the corresponding call of ~ilc[member-equal], which is defined as follows. ~bv[] (defun member-equal (x lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((equal x (car lst)) lst) (t (member-equal x (cdr lst))))) ~ev[] The following two definitions model equality variants of ~ilc[member] for tests ~ilc[eq] and ~ilc[eql], respectively. ~bv[] (defun member-eq-exec (x lst) (declare (xargs :guard (if (symbolp x) (true-listp lst) (symbol-listp lst)))) (cond ((endp lst) nil) ((eq x (car lst)) lst) (t (member-eq-exec x (cdr lst))))) (defun member-eql-exec (x lst) (declare (xargs :guard (if (eqlablep x) (true-listp lst) (eqlable-listp lst)))) (cond ((endp lst) nil) ((eql x (car lst)) lst) (t (member-eql-exec x (cdr lst))))) ~ev[] At this point the user can write ~c[(member x y)] or ~c[(member-equal x y)] to call equality variants of ~c[member] with test ~c[eql] or ~c[equal], respectively. We thus provide the following macro for the ~c[eq] variant. ~bv[] (defmacro member-eq (x lst) `(member ,x ,lst :test 'eq)) ~ev[] ~il[Guard] proof obligations generated by calls of ~c[member] will include those based on its use of ~c[mbe], and are supported by the following two lemmas. ~bv[] (defthm member-eq-exec-is-member-equal (equal (member-eq-exec x l) (member-equal x l))) (defthm member-eql-exec-is-member-equal (equal (member-eql-exec x l) (member-equal x l))) ~ev[] Finally, the following two events arrange that in certain contexts such as ~il[theories] (including the use of ~ilc[in-theory] in ~il[events] and ~il[hints]), ~ilc[member-eq] and ~ilc[member] are treated as references to ~ilc[member-equal]. ~bv[] (add-macro-alias member-eq member-equal) (add-macro-alias member member-equal) ~ev[] We conclude this topic by exploring the following recommendation made in the ~il[documentation] for ~il[equality-variants]. ~bq[] For efficiency we recommend using the ~c[-equal] equality variant, for example ~ilc[member-equal] or ~ilc[member ... :TEST 'equal], in certain contexts: ~ilc[defmacro], ~ilc[defpkg], ~ilc[defconst], and ~ilc[value-triple] forms.~eq[] ACL2 reliies on the underlying Common Lisp for evaluation. It also processes events in the ACL2 logic. In order to guarantee consistency of its logical and Common Lisp evaluations, ACL2 uses a ``safe mode'' to avoid ill-guarded calls. In particular, consider the use of ~ilc[mbe] in execution of a call of an equality variant of a primitive, ~c[F], other than its ~c[F-equal] variant. The ~ilc[mbe] call discussed above requires a connection to be established between the ~c[:logic] and ~c[:exec] forms. For example, if ~c[F] is called with ~c[:TEST 'eql] (either explicitly or as the default), then ACL2 will call both ~c[F-eql-exec] and ~c[F-equal], and check that the two results are equal. The following partial log illustrates the point above. We define a macro that calls ~ilc[member], and when a call of this macro is expanded during processing of a subsequent definition, we see that two membership functions are called on the same arguments. ~bv[] ACL2 !>(defmacro mac (lst) (list 'quote (and (true-listp lst) (member 'c lst :test 'eq)))) Summary Form: ( DEFMACRO MAC ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) MAC ACL2 !>(trace$ member-equal member-eq-exec) ((MEMBER-EQUAL) (MEMBER-EQ-EXEC)) ACL2 !>(defun f () (mac (a b c d))) 1> (ACL2_*1*_ACL2::MEMBER-EQ-EXEC C (A B C D)) 2> (MEMBER-EQ-EXEC C (A B C D)) <2 (MEMBER-EQ-EXEC (C D)) <1 (ACL2_*1*_ACL2::MEMBER-EQ-EXEC (C D)) 1> (ACL2_*1*_ACL2::MEMBER-EQUAL C (A B C D)) 2> (MEMBER-EQUAL C (A B C D)) <2 (MEMBER-EQUAL (C D)) <1 (ACL2_*1*_ACL2::MEMBER-EQUAL (C D)) Since F is non-recursive, its admission is trivial. ~ev[] If performance is an issue then we can avoid such a problem, for example as follows. In a fresh session, let us define a suitable wrapper for calling ~ilc[member] with ~c[:TEST 'eq]. This time, the trace in our partial log shows that we have avoided calling two membership functions. ~bv[] ACL2 !>(defun mem-eq (x lst) (declare (xargs :guard (if (symbolp x) (true-listp lst) (symbol-listp lst)))) (member x lst :test 'eq)) [[ ... output omitted here ... ]] MEM-EQ ACL2 !>(defmacro mac (lst) (list 'quote (and (true-listp lst) (mem-eq 'c lst)))) Summary Form: ( DEFMACRO MAC ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) MAC ACL2 !>(trace$ member-equal member-eq-exec mem-eq) ((MEMBER-EQUAL) (MEMBER-EQ-EXEC) (MEM-EQ)) ACL2 !>(defun f () (mac (a b c d))) 1> (ACL2_*1*_ACL2::MEM-EQ C (A B C D)) 2> (MEM-EQ C (A B C D)) <2 (MEM-EQ (C D)) <1 (ACL2_*1*_ACL2::MEM-EQ (C D)) Since F is non-recursive, its admission is trivial. ~ev[]~/~/") ; Member (defun member-eq-exec (x lst) (declare (xargs :guard (if (symbolp x) (true-listp lst) (symbol-listp lst)))) (cond ((endp lst) nil) ((eq x (car lst)) lst) (t (member-eq-exec x (cdr lst))))) (defun member-eql-exec (x lst) (declare (xargs :guard (if (eqlablep x) (true-listp lst) (eqlable-listp lst)))) (cond ((endp lst) nil) ((eql x (car lst)) lst) (t (member-eql-exec x (cdr lst))))) (defun member-equal (x lst) (declare (xargs :guard (true-listp lst))) #-acl2-loop-only ; for assoc-equal, Jared Davis found native assoc efficient (member x lst :test #'equal) #+acl2-loop-only (cond ((endp lst) nil) ((equal x (car lst)) lst) (t (member-equal x (cdr lst))))) (defmacro member-eq (x lst) `(member ,x ,lst :test 'eq)) (defthm member-eq-exec-is-member-equal (equal (member-eq-exec x l) (member-equal x l))) (defthm member-eql-exec-is-member-equal (equal (member-eql-exec x l) (member-equal x l))) #+acl2-loop-only (defmacro member (x l &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins membership predicate~/ ~bv[] General Forms: (member x lst) (member x lst :test 'eql) ; same as above (eql as equality test) (member x lst :test 'eq) ; same, but eq is equality test (member x lst :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Member x lst)] equals the longest tail of the list ~c[lst] that begins with ~c[x], or else ~c[nil] if no such tail exists. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with successive elements of ~c[lst].~/ The ~il[guard] for a call of ~c[member] depends on the test. In all cases, the second argument must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[member] and its variants: ~bq[] ~c[(member-eq x lst)] is equivalent to ~c[(member x lst :test 'eq)]; ~c[(member-equal x lst)] is equivalent to ~c[(member x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[member-equal]. ~c[Member] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (l ,l)) :logic (member-equal x l) :exec (member-eq-exec x l))) ((equal test ''eql) `(let-mbe ((x ,x) (l ,l)) :logic (member-equal x l) :exec (member-eql-exec x l))) (t ; (equal test 'equal) `(member-equal ,x ,l)))) ; Subsetp (defun subsetp-eq-exec (x y) (declare (xargs :guard (if (symbol-listp y) (true-listp x) (if (symbol-listp x) (true-listp y) nil)))) (cond ((endp x) t) ((member-eq (car x) y) (subsetp-eq-exec (cdr x) y)) (t nil))) (defun subsetp-eql-exec (x y) (declare (xargs :guard (if (eqlable-listp y) (true-listp x) (if (eqlable-listp x) (true-listp y) nil)))) (cond ((endp x) t) ((member (car x) y) (subsetp-eql-exec (cdr x) y)) (t nil))) (defun subsetp-equal (x y) (declare (xargs :guard (and (true-listp y) (true-listp x)))) #-acl2-loop-only ; for assoc-eq, Jared Davis found native assoc efficient (subsetp x y :test #'equal) #+acl2-loop-only (cond ((endp x) t) ((member-equal (car x) y) (subsetp-equal (cdr x) y)) (t nil))) (defmacro subsetp-eq (x y) `(subsetp ,x ,y :test 'eq)) (defthm subsetp-eq-exec-is-subsetp-equal (equal (subsetp-eq-exec x y) (subsetp-equal x y))) (defthm subsetp-eql-exec-is-subsetp-equal (equal (subsetp-eql-exec x y) (subsetp-equal x y))) #+acl2-loop-only (defmacro subsetp (x y &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins test if every ~ilc[member] of one list is a ~ilc[member] of the other~/ ~bv[] General Forms: (subsetp x y) (subsetp x y :test 'eql) ; same as above (eql as equality test) (subsetp x y :test 'eq) ; same, but eq is equality test (subsetp x y :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Subsetp x y)] is true if and only if every ~ilc[member] of the list ~c[x] is a ~c[member] of the list ~c[y]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing members of the two lists.~/ The ~il[guard] for a call of ~c[subsetp] depends on the test. In all cases, both arguments must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then one of the arguments must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then one of the arguments must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[subsetp] and its variants: ~bq[] ~c[(subsetp-eq x lst)] is equivalent to ~c[(subsetp x lst :test 'eq)]; ~c[(subsetp-equal x lst)] is equivalent to ~c[(subsetp x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[subsetp-equal]. ~c[Subsetp] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (y ,y)) :logic (subsetp-equal x y) :exec (subsetp-eq-exec x y))) ((equal test ''eql) `(let-mbe ((x ,x) (y ,y)) :logic (subsetp-equal x y) :exec (subsetp-eql-exec x y))) (t ; (equal test 'equal) `(subsetp-equal ,x ,y)))) (defun symbol-alistp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for association lists with symbols as keys~/ ~c[(Symbol-alistp x)] is true if and only if ~c[x] is a list of pairs of the form ~c[(cons key val)] where ~c[key] is a ~ilc[symbolp]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (consp (car x)) (symbolp (car (car x))) (symbol-alistp (cdr x)))))) (defthm symbol-alistp-forward-to-eqlable-alistp (implies (symbol-alistp x) (eqlable-alistp x)) :rule-classes :forward-chaining) ; Assoc (defun assoc-eq-exec (x alist) (declare (xargs :guard (if (symbolp x) (alistp alist) (symbol-alistp alist)))) (cond ((endp alist) nil) ((eq x (car (car alist))) (car alist)) (t (assoc-eq-exec x (cdr alist))))) (defun assoc-eql-exec (x alist) (declare (xargs :guard (if (eqlablep x) (alistp alist) (eqlable-alistp alist)))) (cond ((endp alist) nil) ((eql x (car (car alist))) (car alist)) (t (assoc-eql-exec x (cdr alist))))) (defun assoc-equal (x alist) (declare (xargs :guard (alistp alist))) #-acl2-loop-only ; Jared Davis found efficiencies in using native assoc (assoc x alist :test #'equal) #+acl2-loop-only (cond ((endp alist) nil) ((equal x (car (car alist))) (car alist)) (t (assoc-equal x (cdr alist))))) (defmacro assoc-eq (x lst) `(assoc ,x ,lst :test 'eq)) (defthm assoc-eq-exec-is-assoc-equal (equal (assoc-eq-exec x l) (assoc-equal x l))) (defthm assoc-eql-exec-is-assoc-equal (equal (assoc-eql-exec x l) (assoc-equal x l))) #+acl2-loop-only (defmacro assoc (x alist &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins look up key in association list~/ ~bv[] General Forms: (assoc x alist) (assoc x alist :test 'eql) ; same as above (eql as equality test) (assoc x alist :test 'eq) ; same, but eq is equality test (assoc x alist :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Assoc x alist)] is the first member of ~c[alist] whose ~ilc[car] is ~c[x], or ~c[nil] if no such member exists. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with the ~ilc[car]s of successive elements of ~c[alist].~/ The ~il[guard] for a call of ~c[assoc] depends on the test. In all cases, the second argument must satisfy ~ilc[alistp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-alistp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-alistp]. ~l[equality-variants] for a discussion of the relation between ~c[assoc] and its variants: ~bq[] ~c[(assoc-eq x alist)] is equivalent to ~c[(assoc x alist :test 'eq)]; ~c[(assoc-equal x alist)] is equivalent to ~c[(assoc x alist :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[assoc-equal]. ~c[Assoc] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (alist ,alist)) :logic (assoc-equal x alist) :exec (assoc-eq-exec x alist))) ((equal test ''eql) `(let-mbe ((x ,x) (alist ,alist)) :logic (assoc-equal x alist) :exec (assoc-eql-exec x alist))) (t ; (equal test 'equal) `(assoc-equal ,x ,alist)))) (defun assoc-eq-equal-alistp (x) (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (consp (car x)) (symbolp (car (car x))) (consp (cdr (car x))) (assoc-eq-equal-alistp (cdr x)))))) (defun assoc-eq-equal (x y alist) ; We look for a pair on alist of the form (x y . val) where we compare the ; first key using eq and the second using equal. We return the pair or nil. ; The guard could be weakened so that if x is a symbol, then alist need only be ; a true-listp whose elements are of the form (x y . val). But there seems to ; be little advantage in having such a guard, considering the case splits that ; it could induce. (declare (xargs :guard (assoc-eq-equal-alistp alist))) (cond ((endp alist) nil) ((and (eq (car (car alist)) x) (equal (car (cdr (car alist))) y)) (car alist)) (t (assoc-eq-equal x y (cdr alist))))) ; DATA TYPES #+acl2-loop-only (defmacro <= (x y) ":Doc-Section ACL2::ACL2-built-ins less-than-or-equal test~/ ~c[<=] is a macro, and ~c[(<= x y)] expands to the same thing as ~c[(not (< y x))]. ~l[<].~/ ~c[<=] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (List 'not (list '< y x))) #+acl2-loop-only (defun = (x y) ":Doc-Section ACL2::ACL2-built-ins test equality of two numbers~/ ~c[(= x y)] is logically equivalent to ~c[(equal x y)].~/ Unlike ~ilc[equal], ~c[=] has a ~il[guard] requiring both of its arguments to be numbers. Generally, ~c[=] is executed more efficiently than ~ilc[equal]. For a discussion of the various ways to test against 0, ~l[zero-test-idioms]. ~c[=] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (declare (xargs :mode :logic :guard (and (acl2-numberp x) (acl2-numberp y)))) (equal x y)) #+acl2-loop-only (defun /= (x y) ":Doc-Section ACL2::ACL2-built-ins test inequality of two numbers~/ ~c[(/= x y)] is logically equivalent to ~c[(not (equal x y))].~/ Unlike ~ilc[equal], ~c[/=] has a ~il[guard] requiring both of its arguments to be numbers. Generally, ~c[/=] is executed more efficiently than a combination of ~ilc[not] and ~ilc[equal]. For a discussion of the various ways to test against 0, ~l[zero-test-idioms]. ~c[/=] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (Declare (xargs :mode :logic :guard (and (acl2-numberp x) (acl2-numberp y)))) (not (equal x y))) #+acl2-loop-only (defmacro > (x y) ":Doc-Section ACL2::ACL2-built-ins greater-than test~/ ~c[>] is a macro, and ~c[(> x y)] expands to the same thing as ~c[(< y x)]. ~l[<].~/ ~c[>] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (list '< y x)) #+acl2-loop-only (defmacro >= (x y) ":Doc-Section ACL2::ACL2-built-ins greater-than-or-equal test~/ ~c[>=] is a macro, and ~c[(>= x y)] expands to the same thing as ~c[(not (< x y))]. ~l[<].~/ ~c[>=] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (list 'not (list '< x y))) (deflabel zero-test-idioms :doc ":Doc-Section ACL2::Programming how to test for 0~/ Below are six commonly used idioms for testing whether ~c[x] is ~c[0]. ~ilc[Zip] and ~ilc[zp] are the preferred termination tests for recursions down the integers and naturals, respectively. ~bv[] idiom logical guard primary meaning compiled code* (equal x 0) (equal x 0) t (equal x 0) (eql x 0) (equal x 0) t (eql x 0) (zerop x) (equal x 0) x is a number (= x 0) (= x 0) (equal x 0) x is a number (= x 0) (zip x) (equal (ifix x) 0) x is an integer (= x 0) (zp x) (equal (nfix x) 0) x is a natural (int= x 0) (zpf x) (equal (nfix x) 0) x is a fixnum >= 0 (eql (the-fixnum x) 0) ~ev[] *~l[guards-and-evaluation], especially the subsection titled ``Guards and evaluation V: efficiency issues''. Primary code is relevant only if ~il[guard]s are verified. The ``compiled code'' shown is only suggestive.~/ The first four idioms all have the same logical meaning and differ only with respect to their executability and efficiency. In the absence of compiler optimizing, ~c[(= x 0)] is probably the most efficient, ~c[(equal x 0)] is probably the least efficient, and ~c[(eql x 0)] is in between. However, an optimizing compiler could always choose to compile ~c[(equal x 0)] as ~c[(eql x 0)] and, in situations where ~c[x] is known at compile-time to be numeric, ~c[(eql x 0)] as ~c[(= x 0)]. So efficiency considerations must, of course, be made in the context of the host compiler. Note also that ~c[(zerop x)] and ~c[(= x 0)] are indistinguishable. They have the same meaning and the same ~il[guard], and can reasonably be expected to generate equally efficient code. Note that ~c[(zip x)] and ~c[(zp x)] do not have the same logical meanings as the others or each other. They are not simple tests for equality to ~c[0]. They each coerce ~c[x] into a restricted domain, ~ilc[zip] to the integers and ~ilc[zp] to the natural numbers, choosing ~c[0] for ~c[x] when ~c[x] is outside the domain. Thus, ~c[1/2], ~c[#c(1 3)], and ~c['abc], for example, are all ``recognized'' as zero by both ~ilc[zip] and ~ilc[zp]. But ~ilc[zip] reports that ~c[-1] is different from ~c[0] while ~ilc[zp] reports that ~c[-1] ``is'' ~c[0]. More precisely, ~c[(zip -1)] is ~c[nil] while ~c[(zp -1)] is ~c[t]. Note that the last five idioms all have ~il[guard]s that restrict their Common Lisp executability. If these last five are used in situations in which ~il[guard]s are to be verified, then proof obligations are incurred as the price of using them. If guard verification is not involved in your project, then the first five can be thought of as synonymous. ~ilc[Zip] and ~ilc[zp] are not provided by Common Lisp but are ACL2-specific functions. Why does ACL2 provide these functions? The answer has to do with the admission of recursively defined functions and efficiency. ~ilc[Zp] is provided as the zero-test in situations where the controlling formal parameter is understood to be a natural number. ~ilc[Zip] is analogously provided for the integer case. We illustrate below. Here is an admissible definition of factorial ~bv[] (defun fact (n) (if (zp n) 1 (* n (fact (1- n))))) ~ev[] Observe the classic recursion scheme: a test against ~c[0] and recursion by ~ilc[1-]. Note however that the test against ~c[0] is expressed with the ~ilc[zp] idiom. Note also the absence of a ~il[guard] making explicit our intention that ~c[n] is a natural number. This definition of factorial is readily admitted because when ~c[(zp n)] is false (i.e., ~c[nil]) then ~c[n] is a natural number other than ~c[0] and so ~c[(1- n)] is less than ~c[n]. The base case, where ~c[(zp n)] is true, handles all the ``unexpected'' inputs, such as arise with ~c[(fact -1)] and ~c[(fact 'abc)]. When calls of ~c[fact] are evaluated, ~c[(zp n)] checks ~c[(integerp n)] and ~c[(> n 0)]. ~il[Guard] verification is unsuccessful for this definition of ~c[fact] because ~ilc[zp] requires its argument to be a natural number and there is no ~il[guard] on ~c[fact], above. Thus the primary raw lisp for ~c[fact] is inaccessible and only the ~c[:]~ilc[logic] definition (which does runtime ``type'' checking) is used in computation. In summary, this definition of factorial is easily admitted and easily manipulated by the prover but is not executed as efficiently as it could be. Runtime efficiency can be improved by adding a ~il[guard] to the definition. ~bv[] (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (zp n) 1 (* n (fact (1- n))))) ~ev[] This ~il[guard]ed definition has the same termination conditions as before -- termination is not sensitive to the ~il[guard]. But the ~il[guard]s can be verified. This makes the primary raw lisp definition accessible during execution. In that definition, the ~c[(zp n)] above is compiled as ~c[(= n 0)], because ~c[n] will always be a natural number when the primary code is executed. Thus, by adding a ~il[guard] and verifying it, the elegant and easily used definition of factorial is also efficiently executed on natural numbers. Now let us consider an alternative definition of factorial in which ~c[(= n 0)] is used in place of ~c[(zp n)]. ~bv[] (defun fact (n) (if (= n 0) 1 (* n (fact (1- n))))) ~ev[] This definition does not terminate. For example ~c[(fact -1)] gives rise to a call of ~c[(fact -2)], etc. Hence, this alternative is inadmissible. A plausible response is the addition of a ~il[guard] restricting ~c[n] to the naturals: ~bv[] (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (= n 0) 1 (* n (fact (1- n))))) ~ev[] But because the termination argument is not sensitive to the ~il[guard], it is still impossible to admit this definition. To influence the termination argument one must change the conditions tested. Adding a runtime test that ~c[n] is a natural number would suffice and allow both admission and ~il[guard] verification. But such a test would slow down the execution of the compiled function. The use of ~c[(zp n)] as the test avoids this dilemma. ~ilc[Zp] provides the logical equivalent of a runtime test that ~c[n] is a natural number but the execution efficiency of a direct ~ilc[=] comparison with ~c[0], at the expense of a ~il[guard] conjecture to prove. In addition, if ~il[guard] verification and most-efficient execution are not needed, then the use of ~c[(zp n)] allows the admission of the function without a ~il[guard] or other extraneous verbiage. While general rules are made to be broken, it is probably a good idea to get into the habit of using ~c[(zp n)] as your terminating ``~c[0] test'' idiom when recursing down the natural numbers. It provides the logical power of testing that ~c[n] is a non-~c[0] natural number and allows efficient execution. We now turn to the analogous function, ~ilc[zip]. ~ilc[Zip] is the preferred ~c[0]-test idiom when recursing through the integers toward ~c[0]. ~ilc[Zip] considers any non-integer to be ~c[0] and otherwise just recognizes ~c[0]. A typical use of ~ilc[zip] is in the definition of ~ilc[integer-length], shown below. (ACL2 can actually accept this definition, but only after appropriate lemmas have been proved.) ~bv[] (defun integer-length (i) (declare (xargs :guard (integerp i))) (if (zip i) 0 (if (= i -1) 0 (+ 1 (integer-length (floor i 2)))))) ~ev[] Observe that the function recurses by ~c[(floor i 2)]. Hence, calling the function on ~c[25] causes calls on ~c[12], ~c[6], ~c[3], ~c[1], and ~c[0], while calling it on ~c[-25] generates calls on ~c[-13], ~c[-7], ~c[-4], ~c[-2], and ~c[-1]. By making ~c[(zip i)] the first test, we terminate the recursion immediately on non-integers. The ~il[guard], if present, can be verified and allows the primary raw lisp definition to check ~c[(= i 0)] as the first terminating condition (because the primary code is executed only on integers).") (defmacro int= (i j) ":Doc-Section ACL2::ACL2-built-ins test equality of two integers~/ ~c[(int= x y)] is logically equivalent to ~c[(equal x y)].~/ Unlike ~ilc[equal], ~c[int=] requires its arguments to be numbers (or else causes a ~il[guard] violation; ~pl[guard]). Generally, ~c[int=] is executed more efficiently than ~ilc[equal] or ~ilc[=] on integers." (list 'eql ; The extra care taken below not to wrap (the integer ...) around integers is ; there to overcome an inefficiency in Allegro 5.0.1 (and probably other ; Allegro releases). Rob Sumners has reported this problem (6/25/00) to Franz. (if (integerp i) i (list 'the 'integer i)) (if (integerp j) j (list 'the 'integer j)))) #+acl2-loop-only (defun zp (x) (declare (xargs :mode :logic :guard (and (integerp x) (<= 0 x)))) ":Doc-Section ACL2::ACL2-built-ins testing a ``natural'' against 0~/ ~c[(Zp n)] is logically equivalent to ~c[(equal (nfix n) 0)] and is the preferred termination test for recursion down the natural numbers. ~c[(Zp n)] returns ~c[t] if ~c[n] is ~c[0] or not a natural number; it returns ~c[nil] otherwise. Thus, in the ACL2 logic (ignoring the issue of ~il[guard]s): ~bv[] n (zp n) 3 nil 0 t -1 t 5/2 t #c(1 3) t 'abc t ~ev[]~/ ~c[(Zp n)] has a ~il[guard] requiring ~c[n] to be a natural number. For a discussion of the various idioms for testing against ~c[0], ~pl[zero-test-idioms]. ~c[Zp] is typically used as the termination test in recursions down the natural numbers. It has the advantage of ``coercing'' its argument to a natural and hence allows the definition to be admitted without an explicit type check in the body. ~il[Guard] verification allows ~c[zp] to be compiled as a direct ~ilc[=]-comparision with ~c[0]." (if (integerp x) (<= x 0) t)) #-acl2-loop-only ; Consider using mbe to avoid this cheat. (defun-one-output zp (x) (declare (type integer x)) (int= x 0)) (defthm zp-compound-recognizer ; This rule improves the ability of ACL2 to compute useful type prescriptions ; for functions. For example, the following function is typed using ; acl2-numberp instead of integerp unless we have this rule: ; (defun foo (index lst) ; (if (zp index) ; nil ; (let ((i (1- index))) (or (foo i lst) (and (not (bar i lst)) i))))) (equal (zp x) (or (not (integerp x)) (<= x 0))) :rule-classes :compound-recognizer) (defthm zp-open ; The preceding event avoids some case-splitting when the ; zp-compound-recognizer (above) provides all the information needed about an ; argument of zp. However, the following example illustrates the need to open ; up zp on some non-variable terms: ; (thm (implies (and (zp (+ (- k) n)) ; (integerp k) ; (integerp n) ; (<= k j)) ; (<= n j))) ; The present rule allows the theorem above to go through. This example ; theorem was distilled from the failure (without this rule) of event ; compress11-assoc-property-1 in community book ; books/data-structures/array1.lisp. (implies (syntaxp (not (variablep x))) (equal (zp x) (if (integerp x) (<= x 0) t)))) (in-theory (disable zp)) #+acl2-loop-only (defun zip (x) (declare (xargs :mode :logic :guard (integerp x))) ":Doc-Section ACL2::ACL2-built-ins testing an ``integer'' against 0~/ ~c[(Zip i)] is logically equivalent to ~c[(equal (ifix i) 0)] and is the preferred termination test for recursion through the integers. ~c[(Zip i)] returns ~c[t] if ~c[i] is ~c[0] or not an integer; it returns ~c[nil] otherwise. Thus, ~bv[] i (zip i) 3 nil 0 t -2 nil 5/2 t #c(1 3) t 'abc t ~ev[]~/ ~c[(Zip i)] has a ~il[guard] requiring ~c[i] to be an integer. For a discussion of the various idioms for testing against ~c[0], ~pl[zero-test-idioms]. ~c[Zip] is typically used as the termination test in recursions through the integers. It has the advantage of ``coercing'' its argument to an integer and hence allows the definition to be admitted without an explicit type check in the body. ~il[Guard] verification allows ~c[zip] to be compiled as a direct ~ilc[=]-comparision with ~c[0]." (if (integerp x) (= x 0) t)) #-acl2-loop-only ; If we had :body we wouldn't need this cheat. (defun-one-output zip (x) (= x 0)) (defthm zip-compound-recognizer ; See the comment for zp-compound-recognizer. (equal (zip x) (or (not (integerp x)) (equal x 0))) :rule-classes :compound-recognizer) (defthm zip-open (implies (syntaxp (not (variablep x))) (equal (zip x) (or (not (integerp x)) (equal x 0))))) (in-theory (disable zip)) #+acl2-loop-only (defun nth (n l) ":Doc-Section ACL2::ACL2-built-ins the nth element (zero-based) of a list~/ ~c[(Nth n l)] is the ~c[n]th element of ~c[l], zero-based. If ~c[n] is greater than or equal to the length of ~c[l], then ~c[nth] returns ~c[nil].~/ ~c[(Nth n l)] has a ~il[guard] that ~c[n] is a non-negative integer and ~c[l] is a ~ilc[true-listp]. ~c[Nth] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp n) (>= n 0) (true-listp l)))) (if (endp l) nil (if (zp n) (car l) (nth (- n 1) (cdr l))))) #+acl2-loop-only (defun char (s n) ":Doc-Section ACL2::ACL2-built-ins the ~il[nth] element (zero-based) of a string~/ ~c[(Char s n)] is the ~c[n]th element of ~c[s], zero-based. If ~c[n] is greater than or equal to the length of ~c[s], then ~c[char] returns ~c[nil].~/ ~c[(Char s n)] has a ~il[guard] that ~c[n] is a non-negative integer and ~c[s] is a ~ilc[stringp]. ~c[Char] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp s) (integerp n) (>= n 0) (< n (length s))))) (nth n (coerce s 'list))) (defun proper-consp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for proper (null-terminated) non-empty lists~/ ~c[Proper-consp] is the function that checks whether its argument is a non-empty list that ends in ~c[nil]. Also ~pl[true-listp]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (and (consp x) (true-listp x))) (defun improper-consp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for improper (non-null-terminated) non-empty lists~/ ~c[Improper-consp] is the function that checks whether its argument is a non-empty list that ends in other than ~c[nil]. ~l[proper-consp] and also ~pl[true-listp]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (and (consp x) (not (true-listp x)))) #+acl2-loop-only (defmacro * (&rest rst) ":Doc-Section ACL2::ACL2-built-ins multiplication macro~/ ~c[*] is really a macro that expands to calls of the function ~ilc[binary-*]. So for example ~bv[] (* x y 4 z) ~ev[] represents the same term as ~bv[] (binary-* x (binary-* y (binary-* 4 z))). ~ev[]~/ ~l[binary-*]. ~c[*] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (cond ((null rst) 1) ((null (cdr rst)) (list 'binary-* 1 (car rst))) (t (xxxjoin 'binary-* rst)))) ;; RAG - This function was modified to accept all complex arguments, ;; not just the complex-rationalps #+acl2-loop-only (defun conjugate (x) ":Doc-Section ACL2::ACL2-built-ins complex number conjugate~/ ~c[Conjugate] takes an ACL2 number as an argument, and returns its complex conjugate (i.e., the result of negating its imaginary part.).~/ ~c[Conjugate] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (acl2-numberp x))) (if (complex/complex-rationalp x) (complex (realpart x) (- (imagpart x))) x)) (defmacro prog2$ (x y) ; This odd little duck is not as useless as it may seem. Its original purpose ; was to serve as a messenger for translate to use to send a message to the ; guard checker. Guards that are created by declarations in lets and other ; places are put into the first arg of a prog2$. Once the guards required by x ; have been noted, x's value may be ignored. If this definition is changed, ; consider the places prog2$ is mentioned, including the mention of 'prog2$ in ; distribute-first-if. ; We have since found other uses for prog2$, which are documented in the doc ; string below. ":Doc-Section ACL2::ACL2-built-ins execute two forms and return the value of the second one~/ ~l[hard-error], ~pl[illegal], and ~pl[cw] for examples of functions to call in the first argument of ~c[prog2$]. Also ~pl[progn$] for an extension of ~c[prog2$] that handles than two arguments.~/ Semantically, ~c[(Prog2$ x y)] equals ~c[y]; the value of ~c[x] is ignored. However, ~c[x] is first evaluated for side effect. Since the ACL2 ~il[programming] language is applicative, there can be no logical impact of evaluating ~c[x]. However, ~c[x] may involve a call of a function such as ~ilc[hard-error] or ~ilc[illegal], which can cause so-called ``hard errors'', or a call of ~ilc[cw] to perform output. Here is a simple, contrived example using ~ilc[hard-error]. The intention is to check at run-time that the input is appropriate before calling function ~c[bar]. ~bv[] (defun foo-a (x) (declare (xargs :guard (consp x))) (prog2$ (or (good-car-p (car x)) (hard-error 'foo-a \"Bad value for x: ~~p0\" (list (cons #\\0 x)))) (bar x))) ~ev[] The following similar function uses ~ilc[illegal] instead of ~c[hard-error]. Since ~c[illegal] has a guard of ~c[nil], ~il[guard] verification would guarantee that the call of ~c[illegal] below will never be made (at least when guard checking is on; ~pl[set-guard-checking]). ~bv[] (defun foo-b (x) (declare (xargs :guard (and (consp x) (good-car-p (car x))))) (prog2$ (or (good-car-p (car x)) (illegal 'foo-b \"Bad value for x: ~~p0\" (list (cons #\\0 x)))) (bar x))) ~ev[] We conclude with a simple example using ~ilc[cw] from the ACL2 sources. ~bv[] (defun print-terms (terms iff-flg wrld) ; Print untranslations of the given terms with respect to iff-flg, following ; each with a newline. ; We use cw instead of the fmt functions because we want to be able to use this ; function in print-type-alist-segments (used in brkpt1), which does not return ; state. (if (endp terms) terms (prog2$ (cw \"~~q0\" (untranslate (car terms) iff-flg wrld)) (print-terms (cdr terms) iff-flg wrld)))) ~ev[]~/" `(return-last 'progn ,x ,y)) (deflabel Other :doc ":Doc-Section Other other commonly used top-level functions~/~/ This section contains an assortment of top-level functions that fit into none of the other categories and yet are suffiently useful as to merit ``~c[advertisement]'' in the ~c[:]~ilc[help] command.~/") (deflabel acl2-help :doc ":Doc-Section Other the acl2-help mailing list~/ You can email questions about ACL2 usage to the acl2-help mailing list: ~c[acl2-help@utlists.utexas.edu]. If you have more general questions about ACL2, for example, about projects completed using ACL2, you may prefer the acl2 mailing list, ~c[acl2@utlists.utexas.edu], which tends to have wider distribution.~/~/") #-acl2-loop-only (defmacro ec-call1-raw (ign x) (declare (ignore ign)) (assert (and (consp x) (symbolp (car x)))) ; checked by translate11 (let ((*1*fn (*1*-symbol (car x)))) `(funcall (cond (*safe-mode-verified-p* ; see below for discussion of this case ',(car x)) ((fboundp ',*1*fn) ',*1*fn) (t ; We should never hit this case, unless the user is employing trust tags or raw ; Lisp. For ACL2 events that might hit this case, such as a defconst using ; ec-call in a book (see below), we should ensure that *safe-mode-verified-p* ; is bound to t. For example, we do so in the raw Lisp definition of defconst, ; which is justified because when ACL2 processes the defconst it will evaluate ; in safe-mode to ensure that no raw Lisp error could occur. ; Why is the use above of *safe-mode-verified-p* necessary? If an event in a ; book calls ec-call in raw Lisp, then we believe that the event is a defpkg or ; defconst event. In such cases, ec-call may be expected to invoke a *1* ; function. Unfortunately, the *1* function definitions are laid down (by ; write-expansion-file) at the end of the expansion file. However, we cannot ; simply move the *1* definitions to the front of the expansion file, because ; some may refer to constants or packages defined in the book. We might wish ; to consider interleaving *1* definitions with events from the book but that ; seems difficult to do. Instead, we arrange with *safe-mode-verified-p* to ; avoid the *1* function calls entirely when loading the expansion file (or its ; compilation). (error "Undefined function, ~s. Please contact the ACL2 implementors." ',*1*fn))) ,@(cdr x)))) (defmacro ec-call1 (ign x) ; We introduce ec-call1 inbetween the utlimate macroexpansion of an ec-call ; form to a return-last form, simply because untranslate will produce (ec-call1 ; nil x) from (return-last 'ec-call1-raw nil x). `(return-last 'ec-call1-raw ,ign ,x)) (defmacro ec-call (x) ":Doc-Section ACL2::ACL2-built-ins execute a call in the ACL2 logic instead of raw Lisp~/ The name ``~c[ec-call]'' represents ``executable-counterpart call.'' This utility is intended for users who are familiar with guards. ~l[guard] for a general discussion of guards. Logically, ~c[ec-call] behaves like the identity macro; during proofs, ~c[(ec-call TERM)] is typically replaced quickly by ~c[TERM] during a proof attempt. However, ~c[ec-call] causes function calls to be evaluated in the ACL2 logic rather than raw Lisp, as explained below.~/ ~bv[] General Form: (ec-call (fn term1 ... termk)) ~ev[] where ~c[fn] is a known function symbol other than those in the list that is the value of the constant ~c[*ec-call-bad-ops*]. In particular, ~c[fn] is not a macro. Semantically, ~c[(ec-call (fn term1 ... termk))] equals ~c[(fn term1 ... termk)]. However, this use of ~c[ec-call] has two effects. ~bq[] (1) ~il[Guard] verification generates no proof obligations from the guard of ~c[fn] for this call. Indeed, guards need not have been verified for ~c[fn]. (2) During evaluation, after the arguments of ~c[fn] are evaluated as usual, the executable counterpart of ~c[fn] is called, rather than ~c[fn] as defined in raw Lisp. That is, the call of ~c[fn] is made on its evaluated arguments as though this call is being made in the ACL2 top-level loop, rather than in raw Lisp. In particular, the ~il[guard] of ~c[fn] is checked, at least by default (~pl[set-guard-checking]).~eq[] Note that in the term (ec-call (fn term1 ... termk))~c[], only the indicated call of ~c[fn] is made in the logic; each ~c[termi] is evaluated in the normal manner. If you want an entire term evaluated in the logic, wrap ~c[ec-call] around each function call in the term (other than calls of ~c[if] and ~c[ec-call]). ~st[Technical Remark] (probably best ignored). During evaluation of a call of ~ilc[defconst] or ~ilc[defpkg] in raw Lisp, a form ~c[(ec-call (fn term1 ... termk))] is treated as ~c[(fn term1 ... termk)], that is, without calling the executable counterpart of ~c[fn]. This situation occurs when loading a compiled file (or expansion file) on behalf of an ~ilc[include-book] event. The reason is technical: executable counterparts are defined below a book's events in the book's compiled file. End of Technical Remark. Here is a small example. We define ~c[foo] recursively but with guard verification inhibited on the recursive call, which is to be evaluated in the ACL2 logic. ~bv[] ACL2 !>(defun foo (x y) (declare (xargs :guard (consp y))) (if (consp x) (cons (car x) (ec-call (foo (cdr x) (cdr y)))) (car y))) The admission of FOO is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We could deduce no constraints on the type of FOO. Computing the guard conjecture for FOO.... The guard conjecture for FOO is trivial to prove. FOO is compliant with Common Lisp. Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo '(2 3 4 5) '(6 7)) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X Y), which is (CONSP Y), is violated by the arguments in the call (FOO '(4 5) NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> ~ev[] The error above arises because eventually, ~c[foo] recurs down to a value of parameter ~c[y] that violates the guard. This is clear from tracing (~pl[trace$] and ~pl[trace]). Each call of the executable counterpart of ~c[foo] (the so-called ``*1*'' function for ~c[foo]) checks the guard and then invokes the raw Lisp version of ~c[foo]. The raw Lisp version calls the executable counterpart on the recursive call. When the guard check fails we get a violation. ~bv[] ACL2 !>(trace$ foo) ((FOO)) ACL2 !>(foo '(2 3 4 5) '(6 7)) 1> (ACL2_*1*_ACL2::FOO (2 3 4 5) (6 7)) 2> (FOO (2 3 4 5) (6 7)) 3> (ACL2_*1*_ACL2::FOO (3 4 5) (7)) 4> (FOO (3 4 5) (7)) 5> (ACL2_*1*_ACL2::FOO (4 5) NIL) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X Y), which is (CONSP Y), is violated by the arguments in the call (FOO '(4 5) NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> ~ev[] If we turn off guard errors then we can see the trace as above, but where we avoid calling the raw Lisp function when the guard fails to hold. ~bv[] ACL2 !>:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(foo '(2 3 4 5) '(6 7)) 1> (ACL2_*1*_ACL2::FOO (2 3 4 5) (6 7)) 2> (FOO (2 3 4 5) (6 7)) 3> (ACL2_*1*_ACL2::FOO (3 4 5) (7)) 4> (FOO (3 4 5) (7)) 5> (ACL2_*1*_ACL2::FOO (4 5) NIL) 6> (ACL2_*1*_ACL2::FOO (5) NIL) 7> (ACL2_*1*_ACL2::FOO NIL NIL) <7 (ACL2_*1*_ACL2::FOO NIL) <6 (ACL2_*1*_ACL2::FOO (5)) <5 (ACL2_*1*_ACL2::FOO (4 5)) <4 (FOO (3 4 5)) <3 (ACL2_*1*_ACL2::FOO (3 4 5)) <2 (FOO (2 3 4 5)) <1 (ACL2_*1*_ACL2::FOO (2 3 4 5)) (2 3 4 5) ACL2 > ~ev[] ~/" (declare (xargs :guard t)) `(ec-call1 nil ,x)) (defmacro non-exec (x) ":Doc-Section ACL2::ACL2-built-ins mark code as non-executable~/ ~c[Non-exec] is a macro such that logically, ~c[(non-exec x)] is equal to ~c[x]. However, the argument to a call of ~c[non-exec] need not obey the usual syntactic restrictions for executable code, and indeed, evaluation of a call of ~c[non-exec] will result in an error. Moreover, for any form occurring in the body of a function (~pl[defun]) that is a call of ~c[non-exec], no guard proof obligations are generated for that form. The following example, although rather contrived, illustrates the use of ~c[non-exec]. One can imagine a less contrived example that efficiently computes return values for a small number of fixed inputs and, for other inputs, returns something logically ``consistent'' with those return values. ~bv[] (defun double (x) (case x (1 2) (2 4) (3 6) (otherwise (non-exec (* 2 x))))) ~ev[] We can prove that ~c[double] is compliant with Common Lisp (~pl[guard]) and that it always computes ~c[(* 2 x)]. ~bv[] (verify-guards double) (thm (equal (double x) (* 2 x))) ~ev[] We can evaluate double on the specified arguments. But a call of ~c[non-exec] results in an error message that reports the form that was supplied to ~c[non-exec]. ~bv[] ACL2 !>(double 3) 6 ACL2 !>(double 10) ACL2 Error in TOP-LEVEL: ACL2 has been instructed to cause an error because of an attempt to evaluate the following form (see :DOC non- exec): (* 2 X). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !> ~ev[]~/ During proofs, the error is silent; it is ``caught'' by the proof mechanism and generally results in the introduction of a call of ~ilc[hide] during a proof. Also ~pl[defun-nx] for a utility that makes every call of a function non-executable, rather than a specified form. The following examples contrast ~c[non-exec] with ~ilc[defun-nx], in particular illustratating the role of ~ilc[non-exec] in avoiding guard proof obligations. ~bv[] ; Guard verification fails: (defun-nx f1 (x) (declare (xargs :guard t)) (car x)) ; Guard verification succeeds after changing the guard above: (defun-nx f1 (x) (declare (xargs :guard (consp x))) (car x)) ; Guard verification succeeds: (defun f2 (x) (declare (xargs :guard t)) (non-exec (car x))) ; Evaluating (g1) prints \"Hello\" before signaling an error. (defun g1 () (f1 (cw \"Hello\"))) ; Evaluating (g2) does not print before signaling an error. (defun g2 () (non-exec (cw \"Hello\"))) ; Evaluating (h1) gives a guard violation for taking reciprocal of 0. (defun h1 () (f1 (/ 1 0))) ; Evaluating (h2) does not take a reciprocal, hence there is no guard ; violation for that; we just get the error expected from using non-exec. (defun h2 () (non-exec (/ 0))) ~ev[]" (declare (xargs :guard t)) `(prog2$ (throw-nonexec-error :non-exec ',x) ,x)) #+acl2-loop-only (defmacro / (x &optional (y 'nil binary-casep)) ":Doc-Section ACL2::ACL2-built-ins macro for division and reciprocal~/ ~l[binary-*] for multiplication and ~pl[unary-/] for reciprocal.~/ Note that ~c[/] represents division as follows: ~bv[] (/ x y) ~ev[] represents the same term as ~bv[] (* x (/ y)) ~ev[] which is really ~bv[] (binary-* x (unary-/ y)). ~ev[] Also note that ~c[/] represents reciprocal as follows: ~bv[] (/ x) ~ev[] expands to ~bv[] (unary-/ x). ~ev[] ~c[/] is a Common Lisp macro. See any Common Lisp documentation for more information.~/" (cond (binary-casep (list 'binary-* x (list 'unary-/ y))) (t (list 'unary-/ x)))) ; This, and many of the axioms that follow, could be defthms. However, we want ; to make explicit what our axioms are, rather than relying on (e.g.) linear ; arithmetic. This is a start. (defaxiom closure (and (acl2-numberp (+ x y)) (acl2-numberp (* x y)) (acl2-numberp (- x)) (acl2-numberp (/ x))) :rule-classes nil) (defaxiom Associativity-of-+ (equal (+ (+ x y) z) (+ x (+ y z)))) (defaxiom Commutativity-of-+ (equal (+ x y) (+ y x))) (defun fix (x) ":Doc-Section ACL2::ACL2-built-ins coerce to a number~/ ~c[Fix] simply returns any numeric argument unchanged, returning ~c[0] on a non-numeric argument. Also ~pl[nfix], ~pl[ifix], and ~pl[rfix] for analogous functions that coerce to a natural number, an integer, and a rational number, respectively.~/ ~c[Fix] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (if (acl2-numberp x) x 0)) (defaxiom Unicity-of-0 (equal (+ 0 x) (fix x))) (defaxiom Inverse-of-+ (equal (+ x (- x)) 0)) (defaxiom Associativity-of-* (equal (* (* x y) z) (* x (* y z)))) (defaxiom Commutativity-of-* (equal (* x y) (* y x))) (defaxiom Unicity-of-1 (equal (* 1 x) (fix x))) (defaxiom Inverse-of-* (implies (and (acl2-numberp x) (not (equal x 0))) (equal (* x (/ x)) 1))) (defaxiom Distributivity (equal (* x (+ y z)) (+ (* x y) (* x z)))) (defaxiom <-on-others (equal (< x y) (< (+ x (- y)) 0)) :rule-classes nil) (defaxiom Zero (not (< 0 0)) :rule-classes nil) (defaxiom Trichotomy (and (implies (acl2-numberp x) (or (< 0 x) (equal x 0) (< 0 (- x)))) (or (not (< 0 x)) (not (< 0 (- x))))) :rule-classes nil) ;; RAG - This axiom was weakened to accomodate real x and y (defaxiom Positive (and (implies (and (< 0 x) (< 0 y)) (< 0 (+ x y))) (implies (and (real/rationalp x) (real/rationalp y) (< 0 x) (< 0 y)) (< 0 (* x y)))) :rule-classes nil) (defaxiom Rational-implies1 (implies (rationalp x) (and (integerp (denominator x)) (integerp (numerator x)) (< 0 (denominator x)))) :rule-classes nil) (defaxiom Rational-implies2 (implies (rationalp x) ; We use the left-hand side below out of respect for the fact that ; unary-/ is invisible with respect to binary-*. (equal (* (/ (denominator x)) (numerator x)) x))) (defaxiom integer-implies-rational (implies (integerp x) (rationalp x)) :rule-classes nil) #+:non-standard-analysis (defaxiom rational-implies-real (implies (rationalp x) (realp x)) :rule-classes nil) ;; RAG - This axiom was weakened to accomodate the reals. (defaxiom complex-implies1 (and (real/rationalp (realpart x)) (real/rationalp (imagpart x))) :rule-classes nil) ;; RAG - This axiom was strengthened to include the reals. (defaxiom complex-definition (implies (and (real/rationalp x) (real/rationalp y)) (equal (complex x y) (+ x (* #c(0 1) y)))) :rule-classes nil) ;; RAG - This axiom was weakened to accomodate the reals. ; This rule was called complex-rationalp-has-nonzero-imagpart before ; Version_2.5. (defaxiom nonzero-imagpart (implies (complex/complex-rationalp x) (not (equal 0 (imagpart x)))) :rule-classes nil) (defaxiom realpart-imagpart-elim (implies (acl2-numberp x) (equal (complex (realpart x) (imagpart x)) x)) :rule-classes (:REWRITE :ELIM)) ; We think that the following two axioms can be proved from the others. ;; RAG - This axiom was strengthened to include the reals. (defaxiom realpart-complex (implies (and (real/rationalp x) (real/rationalp y)) (equal (realpart (complex x y)) x))) ;; RAG - This axiom was also strengthened to include the reals. (defaxiom imagpart-complex (implies (and (real/rationalp x) (real/rationalp y)) (equal (imagpart (complex x y)) y))) ;; RAG - Another axiom strengthened to include the reals. (defthm complex-equal (implies (and (real/rationalp x1) (real/rationalp y1) (real/rationalp x2) (real/rationalp y2)) (equal (equal (complex x1 y1) (complex x2 y2)) (and (equal x1 x2) (equal y1 y2)))) :hints (("Goal" :use ((:instance imagpart-complex (x x1) (y y1)) (:instance imagpart-complex (x x2) (y y2)) (:instance realpart-complex (x x1) (y y1)) (:instance realpart-complex (x x2) (y y2))) :in-theory (disable imagpart-complex realpart-complex)))) (defun force (x) ":Doc-Section Miscellaneous identity function used to force a hypothesis~/ ~c[Force] is the identity function: ~c[(force x)] is equal to ~c[x]. However, for rules of many classes (~pl[rule-classes]), a hypothesis of the form ~c[(force term)] is given special treatment, as described below. This treatment takes place for rule classes ~c[:]~ilc[rewrite], ~c[:]~ilc[linear], ~c[:]~ilc[type-prescription], ~c[:]~ilc[definition], ~c[:]~ilc[meta] (actually in that case, the result of evaluating the hypothesis metafunction call), and ~c[:]~ilc[forward-chaining]. When a hypothesis of a conditional rule (of one of the classes listed above) has the form ~c[(force hyp)], it is logically equivalent to ~c[hyp] but has a pragmatic effect. In particular, when the rule is considered, the needed instance of the hypothesis, ~c[hyp'], may be assumed if the usual process fails to prove it or its negation. In that situation, if the rule is eventually applied, then a special case is generated, requiring the system to prove that ~c[hyp'] is true in the current context. The proofs of all such ``forced assumptions'' are, by default, delayed until the successful completion of the main goal. ~l[forcing-round] and ~pl[immediate-force-modep]. Forcing is generally used on hypotheses that are always expected to be true, as is commonly the case for ~il[guard]s of functions. All the power of the theorem prover is brought to bear on a forced hypothesis and no backtracking is possible. Forced goals can be attacked immediately (~pl[immediate-force-modep]) or in a subsequent forcing round (~pl[forcing-round]). Also ~pl[case-split] for a related utility. If the ~c[:]~ilc[executable-counterpart] of the function ~c[force] is ~il[disable]d, then no hypothesis is forced. For more on enabling and disabling forcing, ~pl[enable-forcing] and ~pl[disable-forcing].~/ It sometimes happens that a conditional rule is not applied because some hypothesis, ~c[hyp], could not be relieved, even though the required instance of ~c[hyp], ~c[hyp'], can be shown true in the context. This happens when insufficient resources are brought to bear on ~c[hyp'] at the time we try to relieve it. A sometimes desirable alternative behavior is for the system to assume ~c[hyp'], apply the rule, and to generate explicitly a special case to show that ~c[hyp'] is true in the context. This is called ``forcing'' ~c[hyp]. It can be arranged by restating the rule so that the offending hypothesis, ~c[hyp], is embedded in a call of ~c[force], as in ~c[(force hyp)]. By using the ~c[:]~ilc[corollary] field of the ~ilc[rule-classes] entry, a hypothesis can be forced without changing the statement of the theorem from which the rule is derived. Technically, ~c[force] is just a function of one argument that returns that argument. It is generally ~il[enable]d and hence evaporates during simplification. But its presence among the hypotheses of a conditional rule causes case splitting to occur if the hypothesis cannot be conventionally relieved. Since a forced hypothesis must be provable whenever the rule is otherwise applicable, forcing should be used only on hypotheses that are expected always to be true. A particularly common situation in which some hypotheses should be forced is in ``most general'' ~il[type-prescription] lemmas. If a single lemma describes the ``expected'' type of a function, for all ``expected'' arguments, then it is probably a good idea to force the hypotheses of the lemma. Thus, every time a term involving the function arises, the term will be given the expected type and its arguments will be required to be of the expected type. In applying this advice it might be wise to avoid forcing those hypotheses that are in fact just type predicates on the arguments, since the routine that applies ~il[type-prescription] lemmas has fairly thorough knowledge of the types of all terms. ~c[Force] can have the additional benefit of causing the ACL2 typing mechanism to interact with the ACL2 rewriter to establish the hypotheses of ~il[type-prescription] rules. To understand this remark, think of the ACL2 type reasoning system as a rather primitive rule-based theorem prover for questions about Common Lisp types, e.g., ``does this expression produce a ~ilc[consp]?'' ``does this expression produce some kind of ACL2 number, e.g., an ~ilc[integerp], a ~ilc[rationalp], or a ~ilc[complex-rationalp]?'' etc. It is driven by ~il[type-prescription] rules. To relieve the hypotheses of such rules, the type system recursively invokes itself. This can be done for any hypothesis, whether it is ``type-like'' or not, since any proposition, ~c[p], can be phrased as the type-like question ``does ~c[p] produce an object of type ~c[nil]?'' However, as you might expect, the type system is not very good at establishing hypotheses that are not type-like, unless they happen to be assumed explicitly in the context in which the question is posed, e.g., ``If ~c[p] produces a ~ilc[consp] then does ~c[p] produce ~c[nil]?'' If type reasoning alone is insufficient to prove some instance of a hypothesis, then the instance will not be proved by the type system and a ~il[type-prescription] rule with that hypothesis will be inapplicable in that case. But by embedding such hypotheses in ~c[force] expressions you can effectively cause the type system to ``punt'' them to the rest of the theorem prover. Of course, as already noted, this should only be done on hypotheses that are ``always true.'' In particular, if rewriting is required to establish some hypothesis of a ~il[type-prescription] rule, then the rule will be found inapplicable because the hypothesis will not be established by type reasoning alone. The ACL2 rewriter uses the type reasoning system as a subsystem. It is therefore possible that the type system will force a hypothesis that the rewriter could establish. Before a forced hypothesis is reported out of the rewriter, we try to establish it by rewriting. This makes the following surprising behavior possible: A ~il[type-prescription] rule fails to apply because some true hypothesis is not being relieved. The user changes the rule so as to ~st[force] the hypothesis. The system then applies the rule but reports no forcing. How can this happen? The type system ``punted'' the forced hypothesis to the rewriter, which established it. Finally, we should mention that the rewriter is never willing to force when there is an ~ilc[if] term present in the goal being simplified. Since ~ilc[and] terms and ~ilc[or] terms are merely abbreviations for ~ilc[if] terms, they also prevent forcing. Note that ~ilc[if] terms are ultimately eliminated using the ordinary flow of the proof (but ~pl[set-case-split-limitations]), allowing ~c[force] ultimately to function as intended. Moreover, forcing can be disabled, as described above; also ~pl[disable-forcing].~/" ; We define this function in :logic mode on the first pass so that it gets a ; nume. See the comment in check-built-in-constants. (declare (xargs :mode :logic :guard t)) x) ; See the comment in check-built-in-constants. ;; RAG - As promised by the comment above, this number had to be ;; changed to get ACL2 to compile. The number "104" is magical. I ;; figured it out by compiling ACL2, getting the error message that ;; said *force-xnume* should be "104" but wasn't, and then changed the ;; definition here. The comment in check-built-in-constants explains ;; why we need to play this (apparently silly) game. ;; RAG - After adding the non-standard predicates, this number grew to 110. (defconst *force-xnume* (let ((x 129)) #+:non-standard-analysis (+ x 12) #-:non-standard-analysis x)) (defun immediate-force-modep () ":Doc-Section Miscellaneous when executable counterpart is ~il[enable]d, ~il[force]d hypotheses are attacked immediately~/ Also ~pl[disable-immediate-force-modep] and ~pl[enable-immediate-force-modep]. This function symbol is defined simply to provide a ~il[rune] which can be ~il[enable]d and ~il[disable]d. Enabling ~bv[] (:executable-counterpart immediate-force-modep) ~ev[] causes ACL2 to attack ~il[force]d hypotheses immediately instead of delaying them to the next forcing round. ~bv[] Example Hints :in-theory (disable (:executable-counterpart immediate-force-modep)) ; delay forced hyps to forcing round :in-theory (enable (:executable-counterpart immediate-force-modep)) ; split on forced hyps immediately~/ ~ev[] ~l[force] for background information. When a ~ilc[force]d hypothesis cannot be established a record is made of that fact and the proof continues. When the proof succeeds a ``forcing round'' is undertaken in which the system attempts to prove each of the ~il[force]d hypotheses explicitly. However, if the ~il[rune] ~c[(:executable-counterpart immediate-force-modep)] is ~il[enable]d at the time the hypothesis is ~il[force]d, then ACL2 does not delay the attempt to prove that hypothesis but undertakes the attempt more or less immediately." ; We make this function :common-lisp-compliant so that it gets a nume on pass 1 ; of initialization. See the comment in check-built-in-constants. (declare (xargs :mode :logic :guard t)) "See :DOC immediate-force-modep.") ; See the comment in check-built-in-constants. ;; RAG - The value of "107" was modified as suggested during the ;; compilation of ACL2. It's magic. See the comment in ;; check-built-in-constants to find out more. ;; RAG - After adding the non-standard predicates, this changed to 113. (defconst *immediate-force-modep-xnume* (+ *force-xnume* 3)) (defun case-split (x) ":Doc-Section Miscellaneous like force but immediately splits the top-level goal on the hypothesis~/ ~c[Case-split] is an variant of ~ilc[force], which has similar special treatment in hypotheses of rules for the same ~il[rule-classes] as for ~c[force] (~pl[force]). This treatment takes place for rule classes ~c[:]~ilc[rewrite], ~c[:]~ilc[linear], ~c[:]~ilc[type-prescription], ~c[:]~ilc[definition], ~c[:]~ilc[meta] (actually in that case, the result of evaluating the hypothesis metafunction call), and ~c[:]~ilc[forward-chaining]. When a hypothesis of a conditional rule (of one of the classes listed above) has the form ~c[(case-split hyp)] it is logically equivalent to ~c[hyp]. However it affects the application of the rule generated as follows: if ACL2 attempts to apply the rule but cannot establish that the required instance of ~c[hyp] holds in the current context, it considers the hypothesis true anyhow, but (assuming all hypotheses are seen to be true and the rule is applied) creates a subgoal in which that instance of ~c[hyp] is assumed false. (There are exceptions, noted below.)~/ For example, given the rule ~bv[] (defthm p1->p2 (implies (case-split (p1 x)) (p2 x))) ~ev[] then an attempt to prove ~bv[] (implies (p3 x) (p2 (car x))) ~ev[] can give rise to a single subgoal: ~bv[] (IMPLIES (AND (NOT (P1 (CAR X))) (P3 X)) (P2 (CAR X))). ~ev[] Unlike ~ilc[force], ~c[case-split] does not delay the ``false case'' to a forcing round but tackles it more or less immediately. The special ``split'' treatment of ~c[case-split] can be disabled by disabling forcing: ~pl[force] for a discussion of disabling forcing, and also ~pl[disable-forcing]. Finally, we should mention that the rewriter is never willing to split when there is an ~ilc[if] term present in the goal being simplified. Since ~ilc[and] terms and ~ilc[or] terms are merely abbreviations for ~ilc[if] terms, they also prevent splitting. Note that ~ilc[if] terms are ultimately eliminated using the ordinary flow of the proof (but ~pl[set-case-split-limitations]), so ~c[case-split] will ultimately function as intended. When in the proof checker, ~c[case-split] behaves like ~c[force].~/" ; We define this function in :logic mode on the first pass so that it gets a ; nume. See the comment in check-built-in-constants. (declare (xargs :mode :logic :guard t)) x) (in-theory (disable (:executable-counterpart immediate-force-modep))) (defmacro disable-forcing nil ":Doc-Section Miscellaneous to disallow ~ilc[force]d ~ilc[case-split]s~/ ~bv[] General Form: ACL2 !>:disable-forcing ; disallow forced case splits ~ev[] ~l[force] and ~pl[case-split] for a discussion of forced case splits, which are inhibited by this command.~/ ~c[Disable-forcing] is actually a macro that ~il[disable]s the executable counterpart of the function symbol ~c[force]; ~pl[force]. When you want to use ~il[hints] to turn off forced case splits, use a form such as one of the following (these are equivalent). ~bv[] :in-theory (disable (:executable-counterpart force)) :in-theory (disable (force)) ~ev[] " '(in-theory (disable (:executable-counterpart force)))) (defmacro enable-forcing nil ":Doc-Section Miscellaneous to allow ~ilc[force]d ~ilc[case split]s~/ ~bv[] General Form: ACL2 !>:enable-forcing ; allowed forced case splits ~ev[] ~l[force] and ~pl[case-split] for a discussion of ~il[force]d case splits, which are turned back on by this command. (~l[disable-forcing] for how to turn them off.)~/ ~c[Enable-forcing] is actually a macro that ~il[enable]s the executable counterpart of the function symbol ~c[force]; ~pl[force]. When you want to use ~il[hints] to turn on forced case splits, use a form such as one of the following (these are equivalent). ~bv[] :in-theory (enable (:executable-counterpart force)) :in-theory (enable (force)) ~ev[] " '(in-theory (enable (:executable-counterpart force)))) (defmacro disable-immediate-force-modep () ":Doc-Section Miscellaneous ~il[force]d hypotheses are not attacked immediately~/ ~bv[] General Form: ACL2 !>:disable-immediate-force-modep ~ev[] This event causes ACL2 to delay ~il[force]d hypotheses to the next forcing round, rather than attacking them immediately. ~l[immediate-force-modep]. Or for more basic information, first ~pl[force] for a discussion of ~il[force]d case splits.~/ Disable-immediate-force-modep is a macro that ~il[disable]s the executable counterpart of the function symbol ~ilc[immediate-force-modep]. When you want to ~il[disable] this mode in ~il[hints], use a form such as one of the following (these are equivalent). ~bv[] :in-theory (disable (:executable-counterpart immediate-force-modep)) :in-theory (disable (immediate-force-modep)) ~ev[] " '(in-theory (disable (:executable-counterpart immediate-force-modep)))) (defmacro enable-immediate-force-modep () ":Doc-Section Miscellaneous ~il[force]d hypotheses are attacked immediately~/ ~bv[] General Form: ACL2 !>:enable-immediate-force-modep ~ev[] This event causes ACL2 to attack ~il[force]d hypotheses immediately instead of delaying them to the next forcing round. ~l[immediate-force-modep]. Or for more basic information, first ~pl[force] for a discussion of ~il[force]d case splits.~/ Enable-immediate-force-modep is a macro that ~il[enable]s the executable counterpart of the function symbol ~ilc[immediate-force-modep]. When you want to ~il[enable] this mode in ~il[hints], use a form such as one of the following (these are equivalent). ~bv[] :in-theory (enable (:executable-counterpart immediate-force-modep)) :in-theory (enable (immediate-force-modep)) ~ev[] " '(in-theory (enable (:executable-counterpart immediate-force-modep)))) (defun synp (vars form term) ; Top-level calls of this function in the hypothesis of a linear or ; rewrite rule are given special treatment when relieving the rule's ; hypotheses. (When the rule class gives such special treatment, it ; is an error to use synp in other than at the top-level.) The ; special treatment is as follows. Term is evaluated, binding state ; to the live state and mfc to the current metafunction context, as ; with meta rules. The result of this evaluation should be either t, ; nil, or an alist binding variables to terms, else we get a hard ; error. Moreover, if we get an alist then either (1) vars should be ; t, representing the set of all possible vars, and none of the keys ; in the alist should already be bound; or else (2) vars should be of ; the form (var1 ... vark), the keys of alist should all be among the ; vari, and none of vari should already be bound (actually this is ; checked when the rule is submitted) -- otherwise we get a hard ; error. ; As of Version_2.7 there are two macros that expand into calls to synp: ; (syntaxp form) ==> ; `(synp (quote nil) (quote (syntaxp ,form)) (quote (and ,form t))) ; (bind-free form &optional (vars 't)) ==> ; (if vars ; `(synp (quote ,vars) (quote (bind-free ,form ,vars)) (quote ,form)) ; `(synp (quote t) (quote (bind-free ,form)) (quote ,form)))) ; Warning: This function must be defined to always return t in order ; for our treatment of it (in particular, in translate) to be sound. ; The special treatment referred to above happens within relieve-hyp. (declare (xargs :mode :logic :guard t) (ignore vars form term)) t) (defmacro syntaxp (form) (declare (xargs :guard t)) ":Doc-Section Miscellaneous attach a heuristic filter on a rule~/ A calls of ~c[syntaxp] in the hypothesis of a ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], or ~c[:]~ilc[linear] rule is treated specially, as described below. Similar treatment is given to the evaluation of a ~c[:]~ilc[meta] rule's hypothesis function call. For example, consider the ~c[:]~ilc[rewrite] rule created from the following formula. ~bv[] Example: (IMPLIES (SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM)))) (EQUAL (LXD X) (LXD (NORM X)))). ~ev[] The ~c[syntaxp] hypothesis in this rule will allow the rule to be applied to ~c[(lxd (trn a b))] but will not allow it to be applied to ~c[(lxd (norm a))].~/ ~bv[] General Form: (SYNTAXP test) ~ev[] ~c[Syntaxp] always returns ~c[t] and so may be added as a vacuous hypothesis. However, when relieving the hypothesis, the test ``inside'' the ~c[syntaxp] form is actually treated as a meta-level proposition about the proposed instantiation of the rule's variables and that proposition must evaluate to true (non-~c[nil]) to ``establish'' the ~c[syntaxp] hypothesis. Note that the test of a ~c[syntaxp] hypothesis does not, in general, deal with the meaning or semantics or values of the terms, but rather with their syntactic forms. In the example above, the ~c[syntaxp] hypothesis allows the rule to be applied to every target of the form ~c[(lxd u)], provided ~c[u] is not of the form ~c[(norm v)]. Observe that without this syntactic restriction the rule above could loop, producing a sequence of increasingly complex targets ~c[(lxd a)], ~c[(lxd (norm a))], ~c[(lxd (norm (norm a)))], etc. An intuitive reading of the rule might be ``~c[norm] the argument of ~c[lxd] unless it has already been ~c[norm]ed.'' Note also that a ~c[syntaxp] hypothesis deals with the syntactic form used internally by ACL2, rather than that seen by the user. In some cases these are the same, but there can be subtle differences with which the writer of a ~c[syntaxp] hypothesis must be aware. You can use ~c[:]~ilc[trans] to display this internal representation. There are two types of ~c[syntaxp] hypotheses. The simpler type may be a hypothesis of a ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], or ~c[:]~ilc[linear] rule provided ~c[test] contains at least one variable but no free variables (~pl[free-variables]). In particular, ~c[test] may not use ~il[stobj]s; any stobj name will be treated as an ordinary variable. The case of ~c[:]~ilc[meta] rules is similar to the above, except that it applies to the result of applying the hypothesis metafunction. (Later below we will describe the second type, an ~em[extended] ~c[syntaxp] hypothesis, which may use ~ilc[state].) We illustrate the use of simple ~c[syntaxp] hypotheses by slightly elaborating the example given above. Consider a ~c[:]~ilc[rewrite] rule: ~bv[] (IMPLIES (AND (RATIONALP X) (SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM))))) (EQUAL (LXD X) (LXD (NORM X)))) ~ev[] How is this rule applied to ~c[(lxd (trn a b))]? First, we form a substitution that instantiates the left-hand side of the conclusion of the rule so that it is identical to the target term. In the present case, the substitution replaces ~c[x] with ~c[(trn a b)]. ~bv[] (LXD X) ==> (LXD (trn a b)). ~ev[] Then we backchain to establish the hypotheses, in order. Ordinarily this means that we instantiate each hypothesis with our substitution and then attempt to rewrite the resulting instance to true. Thus, in order to relieve the first hypothesis above, we rewrite ~bv[] (RATIONALP (trn a b)). ~ev[] If this rewrites to true, we continue. Of course, many users are aware of some exceptions to this general description of the way we relieve hypotheses. For example, if a hypothesis contains a ``free-variable'' ~-[] one not bound by the current substitution ~-[] we attempt to extend the substitution by searching for an instance of the hypothesis among known truths. ~l[free-variables]. ~ilc[Force]d hypotheses are another exception to the general rule of how hypotheses are relieved. Hypotheses marked with ~c[syntaxp], as in ~c[(syntaxp test)], are also exceptions. We instantiate such a hypothesis; but instead of rewriting the instantiated instance, we evaluate the instantiated ~c[test]. More precisely, we evaluate ~c[test] in an environment in which its variable symbols are bound to the quotations of the terms to which those variables are bound in the instantiating substitution. So in the case in point, we (in essence) evaluate ~bv[] (NOT (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))). ~ev[] This clearly evaluates to ~c[t]. When a ~c[syntaxp] test evaluates to true, we consider the ~c[syntaxp] hypothesis to have been established; this is sound because logically ~c[(syntaxp test)] is ~c[t] regardless of ~c[test]. If the test evaluates to ~c[nil] (or fails to evaluate because of ~il[guard] violations) we act as though we cannot establish the hypothesis and abandon the attempt to apply the rule; it is always sound to give up. The acute reader will have noticed something odd about the form ~bv[] (NOT (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))). ~ev[] When relieving the first hypothesis, ~c[(RATIONALP X)], we substituted ~c[(trn a b)] for ~c[X]; but when relieving the second hypothesis, ~c[(SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM))))], we substituted the quotation of ~c[(trn a b)] for ~c[X]. Why the difference? Remember that in the first hypothesis we are talking about the value of ~c[(trn a b)] ~-[] is it rational ~-[] while in the second one we are talking about its syntactic form. Remember also that Lisp, and hence ACL2, evaluates the arguments to a function before applying the function to the resulting values. Thus, we are asking ``Is the list ~c[(trn a b)] a ~ilc[consp] and if so, is its ~ilc[car] the symbol ~c[NORM]?'' The ~c[quote]s on both ~c[(trn a b)] and ~c[NORM] are therefore necessary. One can verify this by defining ~c[trn] to be, say ~ilc[cons], and then evaluating forms such as ~bv[] (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM)) (AND (CONSP (trn a b)) (EQ (CAR (trn a b)) NORM)) (AND (CONSP (trn 'a 'b)) (EQ (CAR (trn 'a 'b)) NORM)) (AND (CONSP '(trn a b)) (EQ '(CAR (trn a b)) ''NORM)) ~ev[] at the top-level ACL2 prompt. ~l[syntaxp-examples] for more examples of the use of ~c[syntaxp]. An extended ~c[syntaxp] hypothesis is similar to the simple type described above, but it uses two additional variables, ~c[mfc] and ~c[state], which must not be bound by the left hand side or an earlier hypothesis of the rule. They must be the last two variables mentioned by ~c[form]; first ~c[mfc], then ~c[state]. These two variables give access to the functions ~c[mfc-]xxx; ~pl[extended-metafunctions]. As described there, ~c[mfc] is bound to the so-called metafunction-context and ~c[state] to ACL2's ~ilc[state]. ~l[syntaxp-examples] for an example of the use of these extended ~c[syntaxp] hypotheses. We conclude with an example illustrating an error that may occur if you forget that a ~c[syntaxp] hypothesis will be evaluated in an environment where variables are bound to syntactic terms, not to values. Consider the following ~il[stobj] introduction (~pl[defstobj]). ~bv[] (defstobj st (fld1 :type (signed-byte 3) :initially 0) fld2) ~ev[] The following ~c[syntaxp] hypothesis is ill-formed for evaluation. Indeed, ACL2 causes an error because it anticipates that when trying to relieve the ~c[syntaxp] hypothesis of this rule, ACL2 would be evaluating ~c[(fld1 st)] where ~c[st] is bound to a term, not to an actual ~c[stobj] as required by the function ~c[fld1]. The error message is intended to explain this problem. ~bv[] ACL2 !>(defthm bad (implies (syntaxp (quotep (fld1 st))) (equal (stp st) (and (true-listp st) (equal (len st) 2) (fld1p (car st)))))) ACL2 Error in ( DEFTHM BAD ...): The form (QUOTEP (FLD1 ST)), from a SYNTAXP hypothesis, is not suitable for evaluation in an environment where its variables are bound to terms. See :DOC SYNTAXP. Here is further explanation: The form ST is being used, as an argument to a call of FLD1, where the single-threaded object of that name is required. But in the current context, the only declared stobj name is STATE. Note: this error occurred in the context (FLD1 ST). Summary Form: ( DEFTHM BAD ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ACL2 Error in ( DEFTHM BAD ...): See :DOC failure. ******** FAILED ******** ACL2 !> ~ev[] Presumably the intention was to rewrite the term ~c[(stp st)] when the ~c[fld1] component of ~c[st] is seen to be an explicit constant. As explained elsewhere (~pl[free-variables]), we can obtain the result of rewriting ~c[(fld1 st)] by binding a fresh variable to that term using ~c[EQUAL], as follows. ~bv[] (defthm good (implies (and (equal f (fld1 st)) (syntaxp (quotep f))) (equal (stp st) (and (true-listp st) (equal (len st) 2) (fld1p (car st)))))) ~ev[] The event above is admitted by ACL2. We can see it in action by disabling the definition of ~c[stp] so that only the rule above, ~c[good], is available for reasoning about ~c[stp]. ~bv[] (in-theory (disable stp)) ~ev[] Then the proof fails for the following, because the ~c[syntaxp] hypothesis of the rule, ~c[good], fails: ~c[(quotep f)] evaluates to ~c[nil] when ~c[f] is bound to the term ~c[(fld1 st)]. ~bv[] (thm (stp st)) ~ev[] However, the proof succeeds for the next form, as we explain below. ~bv[] (thm (stp (list 3 rest))) ~ev[] Consider what happens in that case when rule ~c[good] is applied to the term ~c[(stp (list 3 rest))]. (~l[free-variables] for relevant background.) The first hypothesis of ~c[good] binds the variable ~c[f] to the result of rewriting ~c[(fld1 st)], where ~c[st] is bound to the (internal form of) the term ~c[(list 3 rest)] ~-[] and that result is clearly the term, ~c['3]. Then the ~c[syntaxp] hypothesis is successfully relieved, because the evaluation of ~c[(quotep f)] returns ~c[t] in the environment that binds ~c[f] to ~c['3]." `(synp (quote nil) (quote (syntaxp ,form)) (quote (and ,form t)))) (deflabel syntaxp-examples :doc ":Doc-Section Syntaxp examples pertaining to syntaxp hypotheses~/ ~l[syntaxp] for a basic discussion of the use of ~c[syntaxp] to control rewriting.~/ A common syntactic restriction is ~bv[] (SYNTAXP (AND (CONSP X) (EQ (CAR X) 'QUOTE))) ~ev[] or, equivalently, ~bv[] (SYNTAXP (QUOTEP X)). ~ev[] A rule with such a hypothesis can be applied only if ~c[x] is bound to a specific constant. Thus, if ~c[x] is ~c[23] (which is actually represented internally as ~c[(quote 23)]), the test evaluates to ~c[t]; but if ~c[x] prints as ~c[(+ 11 12)] then the test evaluates to ~c[nil] (because ~c[(car x)] is the symbol ~ilc[binary-+]). We see the use of this restriction in the rule ~bv[] (implies (and (syntaxp (quotep c)) (syntaxp (quotep d))) (equal (+ c d x) (+ (+ c d) x))). ~ev[] If ~c[c] and ~c[d] are constants, then the ~ilc[executable-counterpart] of ~ilc[binary-+] will evaluate the sum of ~c[c] and ~c[d]. For instance, under the influence of this rule ~bv[] (+ 11 12 foo) ~ev[] rewrites to ~bv[] (+ (+ 11 12) foo) ~ev[] which in turn rewrites to ~c[(+ 23 foo)]. Without the syntactic restriction, this rule would loop with the built-in rules ~c[ASSOCIATIVITY-OF-+] or ~c[COMMUTATIVITY-OF-+]. We here recommend that the reader try the affects of entering expressions such as the following at the top level ACL2 prompt. ~bv[] (+ 11 23) (+ '11 23) (+ '11 '23) (+ ''11 ''23) :trans (+ 11 23) :trans (+ '11 23) :trans (+ ''11 23) :trans (+ c d x) :trans (+ (+ c d) x) ~ev[] We also recommend that the reader verify our claim above about looping by trying the affect of each of the following rules individually. ~bv[] (defthm good (implies (and (syntaxp (quotep c)) (syntaxp (quotep d))) (equal (+ c d x) (+ (+ c d) x)))) (defthm bad (implies (and (acl2-numberp c) (acl2-numberp d)) (equal (+ c d x) (+ (+ c d) x)))) ~ev[] on (the false) theorems: ~bv[] (thm (equal (+ 11 12 x) y)) (thm (implies (and (acl2-numberp c) (acl2-numberp d) (acl2-numberp x)) (equal (+ c d x) y))). ~ev[] One can use ~c[:]~ilc[brr], perhaps in conjunction with ~ilc[cw-gstack], to investigate any looping. Here is a simple example showing the value of rule ~c[good] above. Without ~c[good], the ~c[thm] form below fails. ~bv[] (defstub foo (x) t) (thm (equal (foo (+ 3 4 x)) (foo (+ 7 x)))) ~ev[] The next three examples further explore the use of ~c[quote] in ~ilc[syntaxp] hypotheses. We continue the examples of ~ilc[syntaxp] hypotheses with a rule from community book ~c[books/finite-set-theory/set-theory.lisp]. We will not discuss here the meaning of this rule, but it is necessary to point out that ~c[(ur-elementp nil)] is true in this book. ~bv[] (defthm scons-nil (implies (and (syntaxp (not (equal a ''nil))) (ur-elementp a)) (= (scons e a) (scons e nil)))). ~ev[] Here also, ~ilc[syntaxp] is used to prevent looping. Without the restriction, ~c[(scons e nil)] would be rewritten to itself since ~c[(ur-elementp nil)] is true.~nl[] Question: Why the use of two quotes in ~c[''nil]?~nl[] Hints: ~c[Nil] is a constant just as 23 is. Try ~c[:trans (cons a nil)], ~c[:trans (cons 'a 'nil)], and ~c[:trans (cons ''a ''nil)]. Also, don't forget that the arguments to a function are evaluated before the function is applied. The next two rules move negative constants to the other side of an inequality. ~bv[] (defthm |(< (+ (- c) x) y)| (implies (and (syntaxp (quotep c)) (syntaxp (< (cadr c) 0)) (acl2-numberp y)) (equal (< (+ c x) y) (< (fix x) (+ (- c) y))))) (defthm |(< y (+ (- c) x))| (implies (and (syntaxp (quotep c)) (syntaxp (< (cadr c) 0)) (acl2-numberp y)) (equal (< y (+ c x)) (< (+ (- c) y) (fix x))))) ~ev[] Questions: What would happen if ~c[(< (cadr c) '0)] were used? What about ~c[(< (cadr c) ''0)]? One can also use ~c[syntaxp] to restrict the application of a rule to a particular set of variable bindings as in the following taken from community book ~c[books/ihs/quotient-remainder-lemmas.lisp]. ~bv[] (encapsulate () (local (defthm floor-+-crock (implies (and (real/rationalp x) (real/rationalp y) (real/rationalp z) (syntaxp (and (eq x 'x) (eq y 'y) (eq z 'z)))) (equal (floor (+ x y) z) (floor (+ (+ (mod x z) (mod y z)) (* (+ (floor x z) (floor y z)) z)) z))))) (defthm floor-+ (implies (and (force (real/rationalp x)) (force (real/rationalp y)) (force (real/rationalp z)) (force (not (equal z 0)))) (equal (floor (+ x y) z) (+ (floor (+ (mod x z) (mod y z)) z) (+ (floor x z) (floor y z)))))) ) ~ev[] We recommend the use of ~c[:]~c[brr] to investigate the use of ~c[floor-+-crock]. Another useful restriction is defined by ~bv[] (defun rewriting-goal-literal (x mfc state) ;; Are we rewriting a top-level goal literal, rather than rewriting ;; to establish a hypothesis from a rewrite (or other) rule? (declare (ignore x state)) (null (access metafunction-context mfc :ancestors))). ~ev[] We use this restriction in the rule ~bv[] (defthm |(< (* x y) 0)| (implies (and (syntaxp (rewriting-goal-literal x mfc state)) (rationalp x) (rationalp y)) (equal (< (* x y) 0) (cond ((equal x 0) nil) ((equal y 0) nil) ((< x 0) (< 0 y)) ((< 0 x) (< y 0)))))) ~ev[] which has been found to be useful, but which also leads to excessive thrashing in the linear arithmetic package if used indiscriminately. ~l[extended-metafunctions] for information on the use of ~c[mfc] and ~c[metafunction-context]. ~/") (defmacro bind-free (form &optional (vars)) (declare (xargs :guard (or (eq vars nil) (eq vars t) (and (symbol-listp vars) (not (member-eq t vars)) (not (member-eq nil vars)))))) ":Doc-Section Miscellaneous to bind free variables of a rewrite, definition, or linear rule~/ ~bv[] Examples: (IMPLIES (AND (RATIONALP LHS) (RATIONALP RHS) (BIND-FREE (FIND-MATCH-IN-PLUS-NESTS LHS RHS) (X))) (EQUAL (EQUAL LHS RHS) (EQUAL (+ (- X) LHS) (+ (- X) RHS)))) (IMPLIES (AND (BIND-FREE (FIND-RATIONAL-MATCH-IN-TIMES-NESTS LHS RHS MFC STATE) (X)) (RATIONALP X) (CASE-SPLIT (NOT (EQUAL X 0)))) (EQUAL (< LHS RHS) (IF (< 0 X) (< (* (/ X) LHS) (* (/ X) RHS)) (< (* (/ X) RHS) (* (/ X) LHS))))) ~ev[]~/ General Forms: ~bv[] (BIND-FREE term var-list) (BIND-FREE term t) (BIND-FREE term) ~ev[] A rule which uses a ~c[bind-free] hypothesis has similarities to both a rule which uses a ~ilc[syntaxp] hypothesis and to a ~c[:]~ilc[meta] rule. ~c[Bind-free] is like ~ilc[syntaxp], in that it logically always returns ~c[t] but may affect the application of a ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], or ~c[:]~ilc[linear] rule when it is called at the top-level of a hypothesis. It is like a ~c[:]~ilc[meta] rule, in that it allows the user to perform transformations of terms under progammatic control. Note that a ~c[bind-free] hypothesis does not, in general, deal with the meaning or semantics or values of the terms, but rather with their syntactic forms. Before attempting to write a rule which uses ~c[bind-free], the user should be familiar with ~ilc[syntaxp] and the internal form that ACL2 uses for terms. This internal form is similar to what the user sees, but there are subtle and important differences. ~ilc[Trans] can be used to view this internal form. Just as for a ~ilc[syntaxp] hypothesis, there are two basic types of ~c[bind-free] hypotheses. The simpler type of ~c[bind-free] hypothesis may be used as the nth hypothesis in a ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], or ~c[:]~ilc[linear] rule whose ~c[:]~ilc[corollary] is ~c[(implies (and hyp1 ... hypn ... hypk) (equiv lhs rhs))] provided ~c[term] is a term, ~c[term] contains at least one variable, and every variable occuring freely in ~c[term] occurs freely in ~c[lhs] or in some ~c[hypi], ~c[i (EQUAL (+ (- (EXPT A N)) 3 (EXPT A N) (FOO A C)) (+ (- (EXPT A N)) (BAR B) (EXPT A N))). ~ev[] Question: What is the internal form of this result?~nl[] Hint: Use ~c[:]~ilc[trans]. When this rule fires, it adds the negation of a common term to both sides of the equality by selecting a binding for the otherwise-free variable ~c[x], under programmatic control. Note that other mechanisms such as the binding of ~il[free-variables] may also extend the substitution alist. Just as for a ~ilc[syntaxp] test, a ~c[bind-free] form signals failure by returning ~c[nil]. However, while a ~ilc[syntaxp] test signals success by returning true, a ~c[bind-free] form signals success by returning an alist which is used to extend the current substitution alist. Because of this use of the alist, there are several restrictions on it ~-[] in particular the alist must only bind variables, these variables must not be already bound by the substitution alist, and the variables must be bound to ACL2 terms. If ~c[term] returns an alist and the alist meets these restrictions, we append the alist to the substitution alist and use the result as the new current substitution alist. This new current substitution alist is then used when we attempt to relieve the next hypothesis or, if there are no more, instantiate the right hand side of the rule. There is also a second, optional, ~c[var-list] argument to a ~c[bind-free] hypothesis. If provided, it must be either ~c[t] or a list of variables. If it is not provided, it defaults to ~c[t]. If it is a list of variables, this second argument is used to place a further restriction on the possible values of the alist to be returned by ~c[term]: any variables bound in the alist must be present in the list of variables. We strongly recommend the use of this list of variables, as it allows some consistency checks to be performed at the time of the rule's admittance which are not possible otherwise. An extended ~c[bind-free] hypothesis is similar to the simple type described above, but it uses two additional variables, ~c[mfc] and ~c[state], which must not be bound by the left hand side or an earlier hypothesis of the rule. They must be the last two variables mentioned by ~c[term]: first ~c[mfc], then ~c[state]. These two variables give access to the functions ~c[mfc-]xxx; ~pl[extended-metafunctions]. As described there, ~c[mfc] is bound to the so-called metafunction-context and ~c[state] to ACL2's ~ilc[state]. ~l[bind-free-examples] for examples of the use of these extended ~c[bind-free] hypotheses. ~st[SECTION]: Returning a list of alists. As promised above, we conclude with a discussion of the case that evaluation of the ~c[bind-free] term produces a list of alists, ~c[x], rather than a single alist. In this case each member ~c[b] of ~c[x] is considered in turn, starting with the first and proceeding through the list. Each such ~c[b] is handled exactly as discussed above, as though it were the result of evaluating the ~c[bind-free] term. Thus, each ~c[b] extends the current variable binding alist, and all remaining hypotheses are then relieved, as though ~c[b] had been the value obtained by evaluating the ~c[bind-free] term. As soon as one such ~c[b] leads to successful relieving of all remaining hypotheses, the process of relieving hypotheses concludes, so no further members of ~c[x] are considered. We illustrate with a simple pedagogical example. First introduce functions ~c[p1] and ~c[p2] such that a rewrite rule specifies that ~c[p2] implies ~c[p1], but with a free variable. ~bv[] (defstub p1 (x) t) (defstub p2 (x y) t) (defaxiom p2-implies-p1 (implies (p2 x y) (p1 x))) ~ev[] If we add the following axiom, then ~c[(p1 x)] follows logically for all ~c[x]. ~bv[] (defaxiom p2-instance (p2 v (cons v 4))) ~ev[] Unfortunately, evaluation of ~c[(thm (p1 a))] fails, because ACL2 fails to bind the free variable ~c[y] in order to apply the rule ~c[p2-instance]. Let's define a function that produces a list of alists, each binding the variable ~c[y]. Of course, we know that only the middle one below is necessary in this simple example. In more complex examples, one might use heuristics to construct such a list of alists. ~bv[] (defun my-alists (x) (list (list (cons 'y (fcons-term* 'cons x ''3))) (list (cons 'y (fcons-term* 'cons x ''4))) (list (cons 'y (fcons-term* 'cons x ''5))))) ~ev[] The following rewrite rule uses ~c[bind-free] to return a list of candidate alists binding ~c[y]. ~bv[] (defthm p2-implies-p1-better (implies (and (bind-free (my-alists x) (y)) ; the second argument, (y), is optional (p2 x y)) (p1 x))) ~ev[] Now the proof succeeds for ~c[(thm (p1 a))]. Why? When ACL2 applies the ~c[rewrite] rule ~c[p2-implies-p1-better], it evaluates ~c[my-alists], as we can see from the following ~il[trace], to bind ~c[y] in three different alists. ~bv[] ACL2 !>(thm (p1 a)) 1> (ACL2_*1*_ACL2::MY-ALISTS A) <1 (ACL2_*1*_ACL2::MY-ALISTS (((Y CONS A '3)) ((Y CONS A '4)) ((Y CONS A '5)))) Q.E.D. ~ev[] The first alist, binding ~c[y] to ~c[(cons a '3)], fails to allow the hypothesis ~c[(p2 x y)] to be proved. But the next binding of ~c[y], to ~c[(cons a '4)], succeeds: then the current binding alist is ~c[((x . a) (y . (cons a '4)))], for which the hypothesis ~c[(p2 x y)] rewrites to true using the rewrite rule ~c[p2-instance].~/" (if vars `(synp (quote ,vars) (quote (bind-free ,form ,vars)) (quote ,form)) `(synp (quote t) (quote (bind-free ,form)) (quote ,form)))) (deflabel bind-free-examples :doc ":Doc-Section Bind-free examples pertaining to ~ilc[bind-free] hypotheses~/ ~l[bind-free] for a basic discussion of the use of ~c[bind-free] to control rewriting. Note that the examples below all illustrate the common case in which a ~c[bind-free] hypothesis generates a binding alist. ~l[bind-free], in particular the final section, for a discussion of the case that instead a list of binding alists is generated.~/ We give examples of the use of ~ilc[bind-free] hypotheses from the perspective of a user interested in reasoning about arithmetic, but it should be clear that ~ilc[bind-free] can be used for many other purposes also. EXAMPLE 1: Cancel a common factor. ~bv[] (defun bind-divisor (a b) ; If a and b are polynomials with a common factor c, we return a ; binding for x. We could imagine writing get-factor to compute the ; gcd, or simply to return a single non-invertible factor. (let ((c (get-factor a b))) (and c (list (cons 'x c))))) (defthm cancel-factor ;; We use case-split here to ensure that, once we have selected ;; a binding for x, the rest of the hypotheses will be relieved. (implies (and (acl2-numberp a) (acl2-numberp b) (bind-free (bind-divisor a b) (x)) (case-split (not (equal x 0))) (case-split (acl2-numberp x))) (iff (equal a b) (equal (/ a x) (/ b x))))) ~ev[] EXAMPLE 2: Pull integer summand out of floor. Note: This example has an ~em[extended] ~ilc[bind-free] hypothesis, which uses the term ~c[(find-int-in-sum sum mfc state)]. ~bv[] (defun fl (x) ;; This function is defined, and used, in the IHS books. (floor x 1)) (defun int-binding (term mfc state) ;; The call to mfc-ts returns the encoded type of term. ; ;; Thus, we are asking if term is known by type reasoning to ; ;; be an integer. ; (declare (xargs :stobjs (state) :mode :program)) (if (ts-subsetp (mfc-ts term mfc state) *ts-integer*) (list (cons 'int term)) nil)) (defun find-int-in-sum (sum mfc state) (declare (xargs :stobjs (state) :mode :program)) (if (and (nvariablep sum) (not (fquotep sum)) (eq (ffn-symb sum) 'binary-+)) (or (int-binding (fargn sum 1) mfc state) (find-int-in-sum (fargn sum 2) mfc state)) (int-binding sum mfc state))) ; Some additional work is required to prove the following. So for ; purposes of illustration, we wrap skip-proofs around the defthm. (skip-proofs (defthm cancel-fl-int ;; The use of case-split is probably not needed, since we should ;; know that int is an integer by the way we selected it. But this ;; is safer. (implies (and (acl2-numberp sum) (bind-free (find-int-in-sum sum mfc state) (int)) (case-split (integerp int))) (equal (fl sum) (+ int (fl (- sum int))))) :rule-classes ((:rewrite :match-free :all))) ) ; Arithmetic libraries will have this sort of lemma. (defthm hack (equal (+ (- x) x y) (fix y))) (in-theory (disable fl)) (thm (implies (and (integerp x) (acl2-numberp y)) (equal (fl (+ x y)) (+ x (fl y))))) ~ev[] EXAMPLE 3: Simplify terms such as (equal (+ a (* a b)) 0) ~bv[] (defun factors (product) ;; We return a list of all the factors of product. We do not ;; require that product actually be a product. (if (eq (fn-symb product) 'BINARY-*) (cons (fargn product 1) (factors (fargn product 2))) (list product))) (defun make-product (factors) ;; Factors is assumed to be a list of ACL2 terms. We return an ;; ACL2 term which is the product of all the ellements of the ;; list factors. (cond ((atom factors) ''1) ((null (cdr factors)) (car factors)) ((null (cddr factors)) (list 'BINARY-* (car factors) (cadr factors))) (t (list 'BINARY-* (car factors) (make-product (cdr factors)))))) (defun quotient (common-factors sum) ;; Common-factors is a list of ACL2 terms. Sum is an ACL2 term each ;; of whose addends have common-factors as factors. We return ;; (/ sum (make-product common-factors)). (if (eq (fn-symb sum) 'BINARY-+) (let ((first (make-product (set-difference-equal (factors (fargn sum 1)) common-factors)))) (list 'BINARY-+ first (quotient common-factors (fargn sum 2)))) (make-product (set-difference-equal (factors sum) common-factors)))) (defun intersection-equal (x y) (cond ((endp x) nil) ((member-equal (car x) y) (cons (car x) (intersection-equal (cdr x) y))) (t (intersection-equal (cdr x) y)))) (defun common-factors (factors sum) ;; Factors is a list of the factors common to all of the addends ;; examined so far. On entry, factors is a list of the factors in ;; the first addend of the original sum, and sum is the rest of the ;; addends. We sweep through sum, trying to find a set of factors ;; common to all the addends of sum. (declare (xargs :measure (acl2-count sum))) (cond ((null factors) nil) ((eq (fn-symb sum) 'BINARY-+) (common-factors (intersection-equal factors (factors (fargn sum 1))) (fargn sum 2))) (t (intersection-equal factors (factors sum))))) (defun simplify-terms-such-as-a+ab-rel-0-fn (sum) ;; If we can find a set of factors common to all the addends of sum, ;; we return an alist binding common to the product of these common ;; factors and binding quotient to (/ sum common). (if (eq (fn-symb sum) 'BINARY-+) (let ((common-factors (common-factors (factors (fargn sum 1)) (fargn sum 2)))) (if common-factors (let ((common (make-product common-factors)) (quotient (quotient common-factors sum))) (list (cons 'common common) (cons 'quotient quotient))) nil)) nil)) (defthm simplify-terms-such-as-a+ab-=-0 (implies (and (bind-free (simplify-terms-such-as-a+ab-rel-0-fn sum) (common quotient)) (case-split (acl2-numberp common)) (case-split (acl2-numberp quotient)) (case-split (equal sum (* common quotient)))) (equal (equal sum 0) (or (equal common 0) (equal quotient 0))))) (thm (equal (equal (+ u (* u v)) 0) (or (equal u 0) (equal v -1)))) ~ev[]") (defun extra-info (x y) (declare (ignore x y) (xargs :guard t)) t) (in-theory (disable extra-info (extra-info) (:type-prescription extra-info))) (defconst *extra-info-fn* ; If this symbol changes, then change *acl2-exports* and the documentation for ; xargs and verify-guards accordingly. 'extra-info) ; We deflabel Rule-Classes here, so we can refer to it in the doc string for ; tau-system. We define tau-system (the noop fn whose rune controls the ; whether the tau database is used during proofs) in axioms.lisp because we ; build in the nume of its executable counterpart as a constant (e.g., as we do ; with FORCE) and do not want constants additions to the sources to require ; changing that nume (as would happen if tau-system were defined in ; rewrite.lisp where rule-classes was originally defined). (deflabel rule-classes :doc ":Doc-Section Rule-Classes adding rules to the database~/ ~bv[] Example Form (from community book finite-set-theory/total-ordering.lisp): (defthm <<-trichotomy (implies (and (ordinaryp x) (ordinaryp y)) (or (<< x y) (equal x y) (<< y x))) :rule-classes ((:rewrite :corollary (implies (and (ordinaryp x) (ordinaryp y) (not (<< x y)) (not (equal x y))) (<< y x))))) General Form: a true list of rule class objects as defined below Special Cases: a symbol abbreviating a single rule class object ~ev[] When ~ilc[defthm] is used to prove a named theorem, rules may be derived from the proved formula and stored in the database. The user specifies which kinds of rules are to be built, by providing a list of rule class ~i[names] or, more generally, rule class ~i[objects], which name the kind of rule to build and optionally specify varioius attributes of the desired rule. The rule class names are ~c[:]~ilc[REWRITE], ~c[:]~ilc[BUILT-IN-CLAUSE], ~c[:]~ilc[CLAUSE-PROCESSOR], ~c[:]~ilc[COMPOUND-RECOGNIZER], ~c[:]~ilc[CONGRUENCE], ~c[:]~ilc[DEFINITION], ~c[:]~ilc[ELIM], ~c[:]~ilc[EQUIVALENCE], ~c[:]~ilc[FORWARD-CHAINING], ~c[:]~ilc[GENERALIZE], ~c[:]~ilc[INDUCTION], ~c[:]~ilc[LINEAR], ~c[:]~ilc[META], ~c[:]~ilc[REFINEMENT], ~c[:]~ilc[TAU-SYSTEM], ~c[:]~ilc[TYPE-PRESCRIPTION], ~c[:]~ilc[TYPE-SET-INVERTER], and ~c[:]~ilc[WELL-FOUNDED-RELATION]. Some classes ~i[require] the user-specification of certain class-specific attributes. Each class of rule affects the theorem prover's behavior in a different way, as discussed in the corresponding documentation topic. In this topic we discuss the various attributes that may be attached to rule classes. A rule class object is either one of the ~c[:class] keywords or else is a list of the form shown below. Those fields marked with ``(!)'' are required when the ~c[:class] is as indicated. ~bv[] (:class :COROLLARY term :TRIGGER-FNS (fn1 ... fnk) ; provided :class = :META (!) :TRIGGER-TERMS (t1 ... tk) ; provided :class = :FORWARD-CHAINING ; or :class = :LINEAR :TYPE-SET n ; provided :class = :TYPE-SET-INVERTER :TYPED-TERM term ; provided :class = :TYPE-PRESCRIPTION :CLIQUE (fn1 ... fnk) ; provided :class = :DEFINITION :CONTROLLER-ALIST alist ; provided :class = :DEFINITION :INSTALL-BODY directive ; provided :class = :DEFINITION :LOOP-STOPPER alist ; provided :class = :REWRITE :PATTERN term ; provided :class = :INDUCTION (!) :CONDITION term ; provided :class = :INDUCTION :SCHEME term ; provided :class = :INDUCTION (!) :MATCH-FREE all-or-once ; provided :class = :REWRITE or :class = :LINEAR or :class = :FORWARD-CHAINING :BACKCHAIN-LIMIT-LST limit ; provided :class = :REWRITE or :class = :META or :class = :LINEAR or :class = :TYPE-PRESCRIPTION :HINTS hints ; provided instrs = nil :INSTRUCTIONS instrs ; provided hints = nil :OTF-FLG flg) ~ev[] When rule class objects are provided by the user, most of the fields are optional and their values are computed in a context sensitive way. When a ~c[:class] keyword is used as a rule class object, all relevant fields are determined contextually. Each rule class object in ~c[:rule-classes] causes one or more rules to be added to the database. The ~c[:class] keywords are documented individually under the following names. Note that when one of these names is used as a ~c[:class], it is expected to be in the keyword package (i.e., the names below should be preceded by a colon but the ACL2 ~il[documentation] facilities do not permit us to use keywords below). ~/ See also ~ilc[force], ~il[case-split], ~ilc[syntaxp], and ~ilc[bind-free] for ``pragmas'' one can wrap around individual hypotheses of certain classes of rules to affect how the hypothesis is relieved. Before we get into the discussion of rule classes, let us return to an important point. In spite of the large variety of rule classes available, at present we recommend that new ACL2 users rely almost exclusively on (conditional) rewrite rules. A reasonable but slightly bolder approach is to use ~c[:]~ilc[type-prescription] and ~c[:]~ilc[forward-chaining] rules for ``type-theoretic'' rules, especially ones whose top-level function symbol is a common one like ~ilc[true-listp] or ~ilc[consp]; ~pl[type-prescription] and ~pl[forward-chaining]. However, the rest of the rule classes are really not intended for widespread use, but rather are mainly for experts. We expect that we will write more about the question of which kind of rule to use. For now: when in doubt, use a ~c[:]~ilc[rewrite] rule. ~c[:Rule-classes] is an optional keyword argument of the ~ilc[defthm] (and ~ilc[defaxiom]) event. In the following, let ~c[name] be the name of the event and let ~c[thm] be the formula to be proved or added as an axiom. If ~c[:rule-classes] is not specified in a ~ilc[defthm] (or ~ilc[defaxiom]) event, it is as though what was specified was to make one or more ~c[:]~ilc[rewrite] rules, i.e., as though ~c[:rule-classes] ~c[((:rewrite))] had been used. Use ~c[:rule-classes] ~c[nil] to specify that no rules are to be generated. If ~c[:rule-classes] class is specified, where class is a non-~c[nil] symbol, it is as though ~c[:rule-classes] ~c[((class))] had been used. Thus, ~c[:rule-classes] ~c[:]~ilc[forward-chaining] is equivalent to ~c[:rule-classes] ~c[((:forward-chaining))]. We therefore now consider ~c[:rule-classes] as a true list. If any element of that list is a keyword, replace it by the singleton list containing that keyword. Thus, ~c[:rule-classes] ~c[(:rewrite :elim)] is the same as ~c[:rule-classes] ~c[((:rewrite) (:elim))]. Each element of the expanded value of ~c[:rule-classes] must be a true list whose ~ilc[car] is one of the rule class keyword tokens listed above, e.g., ~c[:]~ilc[rewrite], ~c[:]~ilc[elim], etc., and whose ~ilc[cdr] is a ``keyword alist'' alternately listing keywords and values. The keywords in this alist must be taken from those shown below. They may be listed in any order and most may be omitted, as specified below.~bq[] ~c[:]~ilc[Corollary] ~-[] its value, ~c[term], must be a term. If omitted, this field defaults to ~c[thm]. The ~c[:]~ilc[corollary] of a rule class object is the formula actually used to justify the rule created and thus determines the form of the rule. Nqthm provided no similar capability: each rule was determined by ~c[thm], the theorem or axiom added. ACL2 permits ~c[thm] to be stated ``elegantly'' and then allows the ~c[:]~ilc[corollary] of a rule class object to specify how that elegant statement is to be interpreted as a rule. For the rule class object to be well-formed, its (defaulted) ~c[:]~ilc[corollary], ~c[term], must follow from ~c[thm]. Unless ~c[term] follows trivially from ~c[thm] using little more than propositional logic, the formula ~c[(implies thm term)] is submitted to the theorem prover and the proof attempt must be successful. During that proof attempt the values of ~c[:]~ilc[hints], ~c[:]~ilc[instructions], and ~c[:]~ilc[otf-flg], as provided in the rule class object, are provided as arguments to the prover. Such auxiliary proofs give the sort of output that one expects from the prover. However, as noted above, corollaries that follow trivially are not submitted to the prover; thus, such corollaries cause no prover output. Note that before ~c[term] is stored, all calls of macros in it are expanded away. ~l[trans]. ~c[:]~ilc[Hints], ~c[:]~ilc[instructions], ~c[:]~ilc[otf-flg] ~-[] the values of these fields must satisfy the same restrictions placed on the fields of the same names in ~ilc[defthm]. These values are passed to the recursive call of the prover used to establish that the ~c[:]~ilc[corollary] of the rule class object follows from the theorem or axiom ~c[thm]. ~c[:]~ilc[Type-set] ~-[] this field may be supplied only if the ~c[:class] is ~c[:]~ilc[type-set-inverter]. When provided, the value must be a type-set, an integer in a certain range. If not provided, an attempt is made to compute it from the corollary. ~l[type-set-inverter]. ~c[:Typed-term] ~-[] this field may be supplied only if the ~c[:class] is ~c[:]~ilc[type-prescription]. When provided, the value is the term for which the ~c[:]~ilc[corollary] is a type-prescription lemma. If no ~c[:typed-term] is provided in a ~c[:]~ilc[type-prescription] rule class object, we try to compute heuristically an acceptable term. ~l[type-prescription]. ~c[:Trigger-terms] ~-[] this field may be supplied only if the ~c[:class] is ~c[:]~ilc[forward-chaining] or ~c[:]~ilc[linear]. When provided, the value is a list of terms, each of which is to trigger the attempted application of the rule. If no ~c[:trigger-terms] is provided, we attempt to compute heuristically an appropriate set of triggers. ~l[forward-chaining] or ~pl[linear]. ~c[:Trigger-fns] ~-[] this field must (and may only) be supplied if the ~c[:class] is ~c[:]~ilc[meta]. Its value must be a list of function symbols (except that a macro alias can stand in for a function symbol; ~pl[add-macro-alias]). Terms with these symbols trigger the application of the rule. ~l[meta]. ~c[:Clique] and ~c[:controller-alist] ~-[] these two fields may only be supplied if the ~c[:class] is ~c[:]~ilc[definition]. If they are omitted, then ACL2 will attempt to guess them. Suppose the ~c[:]~ilc[corollary] of the rule is ~c[(implies hyp (equiv (fn a1 ... an) body))]. The value of the ~c[:clique] field should be a true list of function symbols, and if non-~c[nil] must include ~c[fn]. These symbols are all the members of the mutually recursive clique containing this definition of ~c[fn]. That is, a call of any function in ~c[:clique] is considered a ``recursive call'' for purposes of the expansion heuristics. The value of the ~c[:controller-alist] field should be an alist that maps each function symbol in the ~c[:clique] to a list of ~c[t]'s and ~c[nil]'s of length equal to the arity of the function. For example, if ~c[:clique] consists of just two symbols, ~c[fn1] and ~c[fn2], of arities ~c[2] and ~c[3] respectively, then ~c[((fn1 t nil) (fn2 nil t t))] is a legal value of ~c[:controller-alist]. The value associated with a function symbol in this alist is a ``mask'' specifying which argument slots of the function ``control'' the recursion for heuristic purposes. Sloppy choice of ~c[:clique] or ~c[:controller-alist] can result in infinite expansion and stack overflow. ~c[:Install-body] ~-[] this field may only be supplied if the ~c[:class] is ~c[:]~ilc[definition]. Its value must be ~c[t], ~c[nil], or the default, ~c[:normalize]. A value of ~c[t] or ~c[:normalize] will cause ACL2 to install this rule as the new body of the function being ``defined'' (~c[fn] in the paragraph just above); hence this definition will be installed for future ~c[:expand] ~il[hints]. Furthermore, if this field is omitted or the value is ~c[:normalize], then this definition will be simplified using the so-called ``normalization'' procedure that is used when processing definitions made with ~ilc[defun]. You must explicitly specify ~c[:install-body nil] in the following cases: ~c[fn] (as above) is a member of the value of constant ~c[*definition-minimal-theory*], the arguments are not a list of distinct variables, ~c[equiv] (as above) is not ~ilc[equal], or there are free variables in the hypotheses or right-hand side (~pl[free-variables]). However, supplying ~c[:install-body nil] will not affect the rewriter's application of the ~c[:definition] rule, other than to avoid using the rule to apply ~c[:expand] hints. If a definition rule equates ~c[(f a1 ... ak)] with ~c[body] but there are hypotheses, ~c[hyps], then ~c[:expand] ~il[hints] will replace terms ~c[(f term1 ... termk)] by corresponding terms ~c[(if hyps body (hide (f term1 ... termk)))]. ~c[:]~ilc[Loop-stopper] ~-[] this field may only be supplied if the class is ~c[:]~ilc[rewrite]. Its value must be a list of entries each consisting of two variables followed by a (possibly empty) list of functions, for example ~c[((x y binary-+) (u v foo bar))]. It will be used to restrict application of rewrite rules by requiring that the list of instances of the second variables must be ``smaller'' than the list of instances of the first variables in a sense related to the corresponding functions listed; ~pl[loop-stopper]. The list as a whole is allowed to be ~c[nil], indicating that no such restriction shall be made. Note that any such entry that contains a variable not being instantiated, i.e., not occurring on the left side of the rewrite rule, will be ignored. However, for simplicity we merely require that every variable mentioned should appear somewhere in the corresponding ~c[:]~ilc[corollary] formula. ~c[:Pattern], ~c[:Condition], ~c[:Scheme] ~-[] the first and last of these fields must (and may only) be supplied if the class is ~c[:]~ilc[induction]. ~c[:Condition] is optional but may only be supplied if the class is ~c[:]~ilc[induction]. The values must all be terms and indicate, respectively, the pattern to which a new induction scheme is to be attached, the condition under which the suggestion is to be made, and a term which suggests the new scheme. ~l[induction]. ~c[:Match-free] ~-[] this field must be ~c[:all] or ~c[:once] and may be supplied only if the ~c[:class] is either ~c[:]~ilc[rewrite], ~c[:]~ilc[linear], or ~c[:]~ilc[forward-chaining]. (This field is not implemented for other rule classes, including the ~c[:]~ilc[type-prescription] rule class.) ~l[free-variables] for a description of this field. Note: Although this field is intended to be used for controlling retries of matching free variables in hypotheses, it is legal to supply it even if there are no such free variables. This can simplify the automated generation of rules, but note that when ~c[:match-free] is supplied, the warning otherwise provided for the presence of free variables in hypotheses will be suppressed. ~c[:Backchain-limit-lst] ~-[] this field may be supplied only if the ~c[:class] is either ~c[:]~ilc[rewrite], ~c[:]~ilc[meta], ~c[:]~ilc[linear], or ~c[:]~ilc[type-prescription]. It is further required either only one rule is generated from the formula or, at least, every such rule has the same list of hypotheses. The value for ~c[:backchain-limit-lst] must be ~c[nil]; a non-negative integer; or, except in the case of ~c[:]~ilc[meta] rules, a true list each element of which is either ~c[nil] or a non-negative integer. If it is a list, its length must be equal to the number of hypotheses of the rule and each item in the list is the ``backchain limit'' associated with the corresponding hypothesis. If ~c[backchain-limit-lst] is a non-negative integer, it is defaulted to a list of the appropriate number of repetitions of that integer. The backchain limit of a hypothesis is used to limit the effort that ACL2 will expend when relieving the hypothesis. If it is ~c[NIL], no new limits are imposed; if it is an integer, the hypothesis will be limited to backchaining at most that many times. Note that backchaining may be further limited by a global ~c[backchain-limit]; ~pl[backchain-limit] for details. For different ways to reign in the rewriter, ~pl[rewrite-stack-limit] and ~pl[set-prover-step-limit]. Jared Davis has pointed out that you can set the ~c[:backchain-limit-lst] to 0 to avoid any attempt to relieve ~ilc[force]d hypotheses, which can lead to a significant speed-up in some cases. ~eq[]Once ~c[thm] has been proved (in the case of ~ilc[defthm]) and each rule class object has been checked for well-formedness (which might require additional proofs), we consider each rule class object in turn to generate and add rules. Let ~c[:class] be the class keyword token of the ~c[i]th class object (counting from left to right). Generate the ~il[rune] ~c[(:class name . x)], where ~c[x] is ~c[nil] if there is only one class and otherwise ~c[x] is ~c[i]. Then, from the ~c[:]~ilc[corollary] of that object, generate one or more rules, each of which has the name ~c[(:class name . x)]. See the ~c[:]~ilc[doc] entry for each rule class to see how formulas determine rules. Note that it is in principle possible for several rules to share the same name; it happens whenever a ~c[:]~ilc[corollary] determines more than one rule. This in fact only occurs for ~c[:]~ilc[rewrite], ~c[:]~ilc[linear], and ~c[:]~ilc[forward-chaining] class rules and only then if the ~c[:]~ilc[corollary] is essentially a conjunction. (See the documentation for ~il[rewrite], ~il[linear], or ~il[forward-chaining] for details.)~/") (defun tau-system (x) ":Doc-Section Rule-Classes make a rule for the ACL2 ``type checker''~/ This documentation topic describes the syntactic form of ``tau-system'' rules; these rules extend ACL2's ``type checker.'' For an introduction to the tau system, ~pl[introduction-to-the-tau-system]. There happens to be a ~i[function] named ~c[tau-system], defined as the identity function. Its only role is to provide the rune ~c[(:EXECUTABLE-COUNTERPART TAU-SYSTEM)], which is used to enable and disable the tau system. Otherwise the function ~c[tau-system] has no purpose and we recommend that you avoid using it so you are free to enable and disable the tau system. When in the default (``greedy'') mode (see ~ilc[set-tau-auto-mode]), every ~ilc[defun] and every ~c[:corollary] (see ~c[:]~ilc[rule-classes]) of every ~ilc[defthm] stored as a rule ~i[of any] ~c[:rule-class] is inspected to determine if it is of one of the forms below. Rules of these forms are added to the tau database, even if they are not labeled as ~c[:tau-system] rules, e.g., a ~c[:]~ilc[rewrite] rule might contribute to the tau database! To add a rule to the tau database without adding any other kind of rule, tag it with ~c[:]~ilc[rule-classes] ~c[:tau-system]. If a theorem has ~c[:]~ilc[rule-classes] ~c[nil], it is not considered for the tau database. ~bv[] General Forms: ~i[Boolean]: (booleanp (p v)) ~i[Eval]: (p 'const) or (p *const*) ~i[Simple]: (implies (p v) (q v)) ~i[Conjunctive]: (implies (and (p1 v) ... (pk v)) (q v)), ; Here k must exceed 1. ~i[Signature Form 1]: (implies (and (p1 x1) (p2 x2) ...) (q (fn x1 x2 ...))) ~i[Signature Form 2]: (implies (and (p1 x1) (p2 x2) ...) (q (mv-nth 'n (fn x1 x2 ...)))) ~i[Bounder Form 1 (or Form 2)]: (implies (and (tau-intervalp i1) ... (or (equal (tau-interval-dom i1) 'dom1-1) ...) ... (in-tau-intervalp x1 i1) ...) (and (tau-intervalp (bounder-fn i1 ...)) (in-tau-intervalp ~i[target] (bounder-fn i1 ...)))) where ~i[target] is (fn x1 ... y1 ...) in ~i[Form 1], and (mv-nth 'n (fn x1 ... y1 ...)) in ~i[Form 2] ~i[Big Switch]: (equal (fn . formals) body) ~i[MV-NTH Synonym]: (equal (nth-alt x y) (mv-nth x y)) or (equal (mv-nth x y) (nth-alt x y)) ~ev[] The symbols ~c[p], ~c[q], ~c[p1], etc., denote monadic (one-argument) Boolean-valued function symbols, or equalities in which one argument is constant, arithmetic comparisons in which one argument is a rational or integer constant, or the logical negations of such terms. By ``equalities'' we allow ~ilc[EQUAL], ~ilc[EQ], ~ilc[EQL], and ~ilc[=]. By ``arithmetic comparison'' we mean ~ilc[<], ~ilc[<=], ~ilc[>=], or ~ilc[>]. Any of these tau predicates may appear negated. The notation ~c[(p v)] above might stand for any one of: ~bv[] (INTEGERP X) (EQUAL V 'MONDAY) (<= I 16) (NOT (EQUAL X 'SUNDAY)) ~ev[] The different rule forms above affect different aspects of the tau system. We discuss each form in more detail below.~/ The documentation below is written as though the tau system is in auto mode! To insure that the only rules added to the tau system are those explicitly assigned to ~c[:rule-class] ~c[:tau-system], you should use ~ilc[set-tau-auto-mode] to select manual mode. ~bv[] General Form: ~i[Boolean]: (booleanp (p v)) ~ev[] Here ~c[p] must be a function symbol and ~c[v] must be a variable. Such a ~c[:tau-system] rule adds ~c[p] to the list of tau predicates. If ~c[p] was recognized as Boolean when it was defined, there is no need to state this rule. This form is needed if you define a monadic Boolean function in such a way that the system does not recognize that it is Boolean. ~bv[] General Form: ~i[Eval]: (p 'const) or (p *const*) ~ev[] Here ~c[p] must be a function symbol. In addition, recall that these general tau predicate forms may appear negated. So the form above includes such theorems as ~c[(NOT (GOOD-STATEP *INITIAL-STATE*))]. A theorem of this form thus records whether a named predicate is true or false on the given constant. Generally, when the tau system must determine whether an enabled tau predicate is true or false on a constant, it simply evaluates the predicate on the constant. This can be impossible or very inefficient if ~c[p] is not defined but constrained, or if ~c[p] is defined in a hard-to-compute way (e.g., ~c[(defun p (x) (evenp (ack x x)))] where ~c[ack] is the Ackermann function), or perhaps if the constant is very large. By proving a ~c[:tau-system] rule of Eval form, you cause the tau system to note the value of the predicate on the constant and henceforth to look it up instead of evaluating the definition. A difficulty, however, is determining that a slow down is due to the evaluation of tau predicates and not some other reason. The first step is determining that tau is slowing the proof down. See ~ilc[time-tracker-tau] for an explanation of ~c[TIME-TRACKER-NOTE]s output during some proofs involving tau reasoning. These notes can alert you to the fact that significant amounts of time are being spent in the tau system. ~ilc[Time-tracker-tau] gives some ways of determining whether tau predicate evaluation is involved. (If worse comes to worst, consider the following hack: In the ACL2 source file ~c[tau.lisp], immediately after the definition of the system function ~c[ev-fncall-w-tau-recog], there is a comment which contains some raw Lisp code that can be used to investigate whether tau's use of evaluation on constants is causing a problem.) However, once a recognizer and the constants on which it is being evaluated are identified, the tau system can be sped up by proving Eval rules to pre-compute and store the values of the recognizer on those constants. Alternatively, at the possible loss of some completeness in the tau system, the executable counterpart of the recognizer can be disabled. ~bv[] General Form: ~i[Simple]: (implies (p v) (q v)) ~ev[] Here ~c[v] must be a variable symbol. This rule builds-in the information that anything satisfying ~c[p] must also satisfy ~c[q], i.e., the ``type'' ~c[q] includes the ``type'' ~c[p]. Recall that the forms may be negated. Most of the time, ~c[p] and ~c[q] will be predicate symbols but it is possible they will be equalities- or inequalities-with-constants. Examples of Simple rules include the following, which are in fact built-in: ~bv[] (implies (natp x) (integerp x)) (implies (integerp x) (rationalp x)) (implies (integerp x) (not (true-listp x))) (implies (natp x) (not (< x 0))) (implies (symbol-alistp x) (alistp x)) ~ev[] Because the tau system records the transitive closure of the Simple rules, any time a term is known to satisfy ~c[natp] it is also known to satisfy ~c[integerp] and ~c[rationalp], and known not to satisfy ~c[true-listp], and known to be non-negative. ~bv[] General Form: ~i[Conjunctive]: (implies (and (p1 v) ... (pk v)) (q v)), ; Here k must exceed 1. ~ev[] The ~c[pi] and ~c[q] may be any tau predicates or their negations, ~c[v] must be a variable symbol, and ~c[i] must exceed 1 or else this is a Simple rule. An obvious operational interpretation of this rule is that if an object is known to satisfy all of the ~c[pi], then it is known to satisfy ~c[q]. However, the actual interpretation is more general. For example, if an object is known to satisfy all but one of the ~c[pi] and is known not to satisfy ~c[q], then the object is known not to satisfy the ``missing'' ~c[pi]. For example, the following Conjunctive rule allows tau to conclude that if weekday ~c[D] is not ~c[MON], ~c[TUE], ~c[THU] or ~c[FRI], then it is ~c[WED]: ~bv[] (implies (and (weekdayp d) (not (eq d 'MON)) (not (eq d 'TUE)) (not (eq d 'WED)) (not (eq d 'THU))) (eq d 'FRI)) ~ev[] The tau database is not closed under conjunctive rules; they are applied dynamically. ~bv[] General Form: ~i[Signature Form 1]: (implies (and (p1 x1) (p2 x2) ... (pn xn) dep-hyp) (q (fn x1 x2 ... xn))) ~ev[] The ~c[pi] and ~c[q] may be any tau predicates or their negations, ~c[fn] must be a function symbol of arity ~c[n], the ~c[xi] must be distinct variable symbols and ~c[dep-hyp] may be any term, provided it is not of the ~c[(pi xi)] shape and the only the variables in it are the ~c[xi]. The Signature form actually allows multiple tau predicates to be applied to each variable, e.g., x1 might be required to be both an ~c[INTEGERP] and ~c[EVENP]. The Signature form allows there to be multiple hypotheses classified as ~c[dep-hyp]s, i.e., not fitting any of the previous shapes, and they are implicitly just conjoined. The name ``dep-hyp'' is an abbreviation of ``dependent hypothesis'' and stems from the fact they often express relations between several of the function's inputs rather than type-like constraints on individual inputs. A Signature rule informs tau that the function ~c[fn] returns an object satisfying ~c[q] provided that the arguments satisfy the respective ~c[pi] and provided that ~c[dep-hyp] occurs in the current context. Note: to be precise, dependent hypotheses are relieved only by applying ACL2's most primitive form of reasoning, ~il[type-set]. In particular, tau reasoning is not used to establish dependent hypotheses. The presence of a ~c[dep-hyp] in a signature rule may severely restrict its applicability. We discuss this after showing a few mundane examples. An example Signature rule is ~bv[] (implies (and (integer-listp x) (integer-listp y)) (integer-listp (append x y))) ~ev[] Of course, a function may have multiple signatures: ~bv[] (implies (and (symbol-listp x) (symbol-listp y)) (symbol-listp (append x y))) ~ev[] Here is a Signature rule for the function ~c[pairlis$]: ~bv[] (implies (and (symbol-listp x) (integer-listp y)) (symbol-alistp (pairlis$ x y))) ~ev[] The tau system can consequently check this theorem by composing the last two rules shown and exploiting Simple rule stating that symbol-alists are also alists: ~bv[] (thm (implies (and (symbol-listp a) (symbol-listp b) (integer-listp y)) (alistp (pairlis$ (append a b) y)))) ~ev[] Since ~c[a] and ~c[b] are known to be lists of symbols and a signature for ~c[append] is that it preserves that predicate, the first argument to the ~c[pairlis$] expression is known to be a list of symbols. This means the Signature rule for ~c[pairlis$] tells us the result is a ~c[symbol-alistp], but the previously mentioned Simple rule, ~c[(implies (symbol-alistp x) (alistp x))], tells us the result is also an ~c[alistp]. When a Signature rule has an ~c[dep-hyp], that hypothesis is not an expression in the tau system. Tau is not used to check that hypothesis. Instead, tau uses the more primitive ~il[type-set] mechanism of ACL2. Here is an example of a Signature rule with a ~c[dep-hyp]: ~bv[] (implies (and (natp n) (integer-listp a) (< n (len a))) (integerp (nth n a))) ~ev[] Note that the last hypothesis is a dependent hypothesis: it is not a tau predicate but a relationship between ~c[n] and ~c[a]. It is relieved by ~il[type-set]. If one is trying to compute the signature of an ~c[(nth n a)] expression in a context in which ~c[(< n (len a))] is explicitly assumed, then this mechanism would establish the dependent hypothesis. But one can easily imagine an almost identical context where, say ~c[(< n (len (rev a)))] is explicitly assumed. In that context, the Signature rule would not be fired because ~ilc[type-set] cannot establish ~c[(< n (len a))] from ~c[(< n (len (rev a)))], even though it would be easily proved by rewriting using the theorem ~c[(equal (len (rev a)) (len a))]. Note also that if this signature could be phrased in a way that eliminates the dependency between ~c[n] and ~c[a] it would be more effective. For example, here is a related Signature rule without a dependent hypothesis: ~bv[] (implies (and (natp n) (register-filep a) (< n 16)) (integerp (nth n a))) ~ev[] In this theorem we require only that ~c[n] be less than 16, which is a tau predicate and hence just an additional tau constraint on ~c[n]. ~bv[] General Form: ~i[Signature Form 2]: (implies (and (p1 x1) (p2 x2) ... (pn xn) dep-hyp) (q (mv-nth 'n (fn x1 x2 ... xn)))) ~ev[] This form of signature rule is just like form 1 except that it is useful for functions that return multiple-values and allows us to ``type-check'' their individual outputs. ~bv[] General Form: ~i[Bounder Forms 1 and 2]: (implies (and (tau-intervalp i1) ... (or (equal (tau-interval-dom i1) 'dom1-1) ...) ... (in-tau-intervalp x1 i1) ...) (and (tau-intervalp (bounder-fn i1 ...)) (in-tau-intervalp ~i[target] (bounder-fn i1 ...)))) ~ev[] where ~i[target] is either ~c[(fn x1 ... y1 ...)] in ~i[Form 1] or ~c[(mv-nth 'n (fn x1 ... y1 ...))] in ~i[Form 2]. This form is for advanced users only and the schema given above is just a reminder of the general shape. A ``bounder'' for a given function symbol, ~c[fn], is a function symbol ~c[bounder-fn] that computes an interval containing ~c[(fn x1 ... y1 ...)] (or its ~c[n]th component in the case of Form 2 rules) from the intervals containing certain of the arguments of ~c[fn]. The correctness theorem for a bounder function informs the tau system that bounds for ~c[fn] are computed by ~c[bounder-fn] and sets up the correspondence between the relevant arguments, ~c[xi], of ~c[fn] and the intervals containing those arguments, ~c[ii] to which ~c[bounder-fn] is applied. When the tau system computes the tau for a call of ~c[fn], it computes the tau of the relevant arguments and applies the bounder to the intervals of those tau. This provides a domain and upper and/or lower bounds for the value of the term. The tau system then further augments that with signature rules. ~l[bounders] for details on intervals, bounders, and bounder correctness theorems. ~bv[] General Form: ~i[Big Switch]: (equal (fn . formals) body) ~ev[] In the Big Switch form, ~c[fn] must be a function symbol, ~c[formals] must be a list of distinct variable symbols, and ~c[body] must be a ``big switch'' term, i.e., one that case splits on tau predicates about a single variable and produces a term not involving that variable. An example of a Big Switch rule is ~bv[] (equal (conditional-type x y) (if (consp x) (consp y) (integerp y))) ~ev[] The idea is that the tau system can treat calls of ~c[conditional-type] as a tau-predicate after determining the tau of an argument. Since equality-to-constants are tau predicates, a more common example of a Big Switch rule is ~bv[] (equal (dtypep x expr) (case x (STMT (stmt-typep expr)) (EXPR (expr-typep expr)) (MODULE (module-typep expr)) (otherwise nil))) ~ev[] This is because ~c[(case x (STMT ...) ...)] macroexpands in ACL2 to ~c[(if (eql x 'STMT) ... ...)] and ~c[(eql x 'STMT)] is a tau predicate about ~c[x]. Big Switch rules are recognized when a function is defined (if tau is in automatic mode). They generally do not have to be proved explicitly, though they might be when mutual recursion is involved. Only the first detected Big Switch rule about a function ~c[fn] is recognized. ~bv[] General Form: ~i[MV-NTH Synonym]: (equal (nth-alt x y) (mv-nth x y)) or (equal (mv-nth x y) (nth-alt x y)) ~ev[] Rules of this form just tell the tau system that the user-defined function ~c[nth-alt] is synonymous with the ACL2 primitive function ~c[mv-nth]. Because ACL2's rewriter gives special handling to ~c[mv-nth], users sometimes define their own versions of that function so they can disable them and control rewriting better. By revealing to the tau system that such a synonym has been introduced you allow Signature rules of Form 2 to be used.~/" (declare (xargs :mode :logic :guard t)) x) ; Essay on the Status of the Tau System During and After Bootstrapping ; Think of there being two ``status bits'' associated with the tau system: (a) ; whether it is enabled or disabled and (b) whether it is automatically making ; :tau-system rules from non-:tau-system rules. These two bits are independent. ; Bit (a) may be inspected by (enabled-numep *tau-system-xnume* (ens state)) ; Bit (b) may be inspected by (table acl2-defaults-table :tau-auto-modep) ; To boot, we must think about two things: how we want these bits set DURING ; bootstrap and how we want them set (for the user) AFTER bootstrap. Our ; current choices are: ; During Bootstrapping: ; (1.a) tau is disabled -- unavailable for use in boot-strap proofs, and ; (1.b) tau is in manual mode -- make no :tau-system rules except those so tagged ; We don't actually have any reason for (1.a). The bootstrap process works ; fine either way, as of this writing (Aug, 2011) when the tau system was first ; integrated into ACL2. But we feel (1.b) is important: it is convenient if <------ ???? tau to do ; the tau database contains the rules laid down during the bootstrap process, ; e.g., the tau signatures of the primitives so that if the user immediately ; selects automatic mode for the session, the tau database is up to date as of ; that selection. ; After Bootstrapping: ; (2.a) tau is disabled -- not available for use in proofs, BUT ; (2.b) tau is in automatic mode -- makes :tau-system rules out of <---- ??? actually in manual mode ; non-:tau-system rules ; We feel that after booting, (2.a) is important because of backwards ; compatibility during book certification: we don't want goals eliminated by ; tau, causing subgoals to be renumbered. We feel that (2.b) is important in the ; long run: we'd like tau to be fully automatic and robust in big proof ; efforts, so we are trying to stress it by collecting tau rules even during ; book certification. In addition, we want the user who turns on the tau ; system to find that it knows as much as possible. ; Our post-bootstrap selections for these two bits affects the regression ; suite. If the tau system is enabled by default, then some adjustments must ; be made in the regression suite books! We have successfully recertified the ; regression suite with tau enabled, but only after making certain changes ; described in Essay on Tau-Clause -- Using Tau to Prove or Mangle Clauses. ; If tau is enabled by default, the regression slows down by about ; real slowdown: 5.3% ; user slowdown: 5.8% ; sys slowdown: 12.3% ; as measured with time make -j 3 regression-fresh on a Macbook Pro 2.66 GHz ; Intel Core i7 with 8 GB 1067 MHz DDR3 running Clozure Common Lisp Version ; 1.6-dev-r14316M-trunk (DarwinX8632). ; How do we achieve these settings? The following constant defines all four ; settings. To rebuild the system with different settings, just redefine this ; constant. It is not (always) possible to adjust these settings during boot ; by set-tau-auto-mode events, for example, because the acl2-defaults-table may ; not exist. (defconst *tau-status-boot-strap-settings* '((t . t) . (t . t))) ; See Warning below! ; '((t . t) . (nil . t))) ; ((1.a . 1.b) . (2.a . 2.b)) ; Thus, ; (1.a) = (caar *tau-status-boot-strap-settings*) ; tau system on/off during boot ; (1.b) = (cdar *tau-status-boot-strap-settings*) ; tau auto mode during boot ; (2.a) = (cadr *tau-status-boot-strap-settings*) ; tau system on/off after boot ; (2.b) = (cddr *tau-status-boot-strap-settings*) ; tau auto mode after boot ; Warning: If you change these defaults, be sure to change the documentation ; topics tau-system and introduction-to-the-tau-system and set-tau-auto-mode ; and probably tau-status, where we are likely to say that the default setting ; the user sees is tau-system on, auto mode on. (in-theory (if (caar *tau-status-boot-strap-settings*) (enable (:executable-counterpart tau-system)) (disable (:executable-counterpart tau-system)))) (defconst *tau-system-xnume* (+ *force-xnume* 12)) ; These constants record the tau indices of the arithmetic predicates. (defconst *tau-acl2-numberp-pair* '(0 . ACL2-NUMBERP)) (defconst *tau-integerp-pair* #+non-standard-analysis '(5 . INTEGERP) #-non-standard-analysis '(4 . INTEGERP)) (defconst *tau-rationalp-pair* #+non-standard-analysis '(6 . RATIONALP) #-non-standard-analysis '(5 . RATIONALP)) (defconst *tau-natp-pair* #+non-standard-analysis '(20 . NATP) #-non-standard-analysis '(17 . NATP)) (defconst *tau-posp-pair* #+non-standard-analysis '(21 . POSP) #-non-standard-analysis '(18 . POSP)) (defconst *tau-minusp-pair* #+non-standard-analysis '(29 . MINUSP) #-non-standard-analysis '(26 . MINUSP)) (defconst *tau-booleanp-pair* #+(and (not non-standard-analysis) acl2-par) '(103 . BOOLEANP) #+(and (not non-standard-analysis) (not acl2-par)) '(102 . BOOLEANP) #+(and non-standard-analysis (not acl2-par)) '(105 . BOOLEANP) #+(and non-standard-analysis acl2-par) '(106 . BOOLEANP) ) ; Note: The constants declared above are checked for accuracy after bootstrap ; by check-built-in-constants in interface-raw.lisp. ; The following axiom can be proved. John Cowles has proved some of these and ; we have proved others in our efforts to verify the guards in our code. ; Eventually we will replace some of these axioms by theorems. But not now ; because things are too fluid. ;; RAG - This axiom was strengthened to include the reals. Amusingly, ;; it was also weakened, since it leaves open the possibility that for ;; rational x, x*x is irrational. Luckily, the type-system knows this ;; isn't the case, so hopefully we have not weakened ACL2. (defaxiom nonnegative-product ; Note that in (* x x), x might be complex. So, we do not want to force the ; hypothesis below. (implies (real/rationalp x) (and (real/rationalp (* x x)) (<= 0 (* x x)))) ; We need the :type-prescription rule class below. Without it, ACL2 cannot ; prove (implies (rationalp x) (<= 0 (* x x))); primitive type-set reasoning ; will not notice that both arguments of * are identical. :rule-classes ((:type-prescription :typed-term (* x x)))) ; (add-schema Induction Schema ; (and (implies (not (integerp x)) (p x)) ; (p 0) ; (implies (and (integerp x) ; (< 0 x) ; (p (- x 1))) ; (p x)) ; (implies (and (integerp x) ; (< x 0) ; (p (+ x 1))) ; (p x))) ; (p x)) ; (defaxiom Integer-0 (integerp 0) :rule-classes nil) (defaxiom Integer-1 (integerp 1) :rule-classes nil) (defaxiom Integer-step (implies (integerp x) (and (integerp (+ x 1)) (integerp (+ x -1)))) :rule-classes nil) (defaxiom Lowest-Terms (implies (and (integerp n) (rationalp x) (integerp r) (integerp q) (< 0 n) (equal (numerator x) (* n r)) (equal (denominator x) (* n q))) (equal n 1)) :rule-classes nil) ; The following predicates are disjoint and these facts are all built into type-set: ; (((acl2-numberp x) ; (complex-rationalp x) ; ((rationalp x) ; ((integerp x) (< 0 x) (equal x 0) (< x 0)) ; ((not (integerp x)) (< 0 x) (< x 0)))) ; ((consp x) (proper-consp x) (improper-consp x)) ; ((symbolp x) (equal x nil) (equal x T) (not (or (equal x T) ; (equal x NIL)))) ; (stringp x) ; (characterp x) ; (other-kinds-of-objects)) ; Here we prove some rules that the tau system uses to manage primitive type-sets. ; The rules for natp, posp, and minusp are messy because those concepts are not ; simply predicates on the signs but also (sometimes) on INTEGERP. (defthm basic-tau-rules (and (implies (natp v) (not (minusp v))) (implies (natp v) (integerp v)) (implies (posp v) (natp v)) (implies (minusp v) (acl2-numberp v)) (implies (integerp v) (rationalp v)) (implies (rationalp v) (not (complex-rationalp v))) (implies (rationalp v) (not (characterp v))) (implies (rationalp v) (not (stringp v))) (implies (rationalp v) (not (consp v))) (implies (rationalp v) (not (symbolp v))) (implies (complex-rationalp v) (not (characterp v))) (implies (complex-rationalp v) (not (stringp v))) (implies (complex-rationalp v) (not (consp v))) (implies (complex-rationalp v) (not (symbolp v))) (implies (characterp v) (not (stringp v))) (implies (characterp v) (not (consp v))) (implies (characterp v) (not (symbolp v))) (implies (stringp v) (not (consp v))) (implies (stringp v) (not (symbolp v))) (implies (consp v) (not (symbolp v))) ; We catch Boolean type-prescriptions and convert them to tau signature rules. ; The first lemma below links booleanp to symbolp and thus to the other recogs. ; The next two deal with special cases: boolean functionse that do not have ; type-prescriptions because we have special functions for computing their ; type-sets. (implies (booleanp v) (symbolp v)) (booleanp (equal x y)) (booleanp (< x y)) ) :rule-classes :tau-system) ; ; For each of the primitives we have the axiom that when their guards ; ; are unhappy, the result is given by apply. This is what permits us ; ; to replace unguarded terms by apply's. E.g., ; ; (defaxiom +-guard ; (implies (or (not (rationalp x)) ; (not (rationalp y))) ; (equal (+ x y) ; (apply '+ (list x y))))) (defaxiom car-cdr-elim (implies (consp x) (equal (cons (car x) (cdr x)) x)) :rule-classes :elim) (defaxiom car-cons (equal (car (cons x y)) x)) (defaxiom cdr-cons (equal (cdr (cons x y)) y)) (defaxiom cons-equal (equal (equal (cons x1 y1) (cons x2 y2)) (and (equal x1 x2) (equal y1 y2)))) ; Induction Schema: (and (implies (not (consp x)) (p x)) ; (implies (and (consp x) (p (car x)) (p (cdr x))) ; (p x))) ; ---------------------------------------------- ; (p x) ; ; (defaxiom booleanp-characterp (booleanp (characterp x)) :rule-classes nil) (defaxiom characterp-page (characterp #\Page) :rule-classes nil) (defaxiom characterp-tab (characterp #\Tab) :rule-classes nil) (defaxiom characterp-rubout (characterp #\Rubout) :rule-classes nil) ; No-duplicatesp (defun no-duplicatesp-eq-exec (l) (declare (xargs :guard (symbol-listp l))) (cond ((endp l) t) ((member-eq (car l) (cdr l)) nil) (t (no-duplicatesp-eq-exec (cdr l))))) (defun no-duplicatesp-eql-exec (l) (declare (xargs :guard (eqlable-listp l))) (cond ((endp l) t) ((member (car l) (cdr l)) nil) (t (no-duplicatesp-eql-exec (cdr l))))) (defun no-duplicatesp-equal (l) (declare (xargs :guard (true-listp l))) (cond ((endp l) t) ((member-equal (car l) (cdr l)) nil) (t (no-duplicatesp-equal (cdr l))))) (defmacro no-duplicatesp-eq (x) `(no-duplicatesp ,x :test 'eq)) (defthm no-duplicatesp-eq-exec-is-no-duplicatesp-equal (equal (no-duplicatesp-eq-exec x) (no-duplicatesp-equal x))) (defthm no-duplicatesp-eql-exec-is-no-duplicatesp-equal (equal (no-duplicatesp-eql-exec x) (no-duplicatesp-equal x))) (defmacro no-duplicatesp (x &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins check for duplicates in a list~/ ~bv[] General Forms: (no-duplicatesp x) (no-duplicatesp x :test 'eql) ; same as above (eql as equality test) (no-duplicatesp x :test 'eq) ; same, but eq is equality test (no-duplicatesp x :test 'equal) ; same, but equal is equality test ~ev[] ~c[(no-duplicatesp lst)] is true if and only if no member of ~c[lst] occurs twice in ~c[lst]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing elements of ~c[lst].~/ The ~il[guard] for a call of ~c[no-duplicatesp] depends on the test. In all cases, the argument must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then the argument must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then the argument must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[no-duplicatesp] and its variants: ~bq[] ~c[(no-duplicatesp-eq x lst)] is equivalent to ~c[(no-duplicatesp x lst :test 'eq)]; ~c[(no-duplicatesp-equal x lst)] is equivalent to ~c[(no-duplicatesp x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[no-duplicatesp-equal].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x)) :logic (no-duplicatesp-equal x) :exec (no-duplicatesp-eq-exec x))) ((equal test ''eql) `(let-mbe ((x ,x)) :logic (no-duplicatesp-equal x) :exec (no-duplicatesp-eql-exec x))) (t ; (equal test 'equal) `(no-duplicatesp-equal ,x)))) ; The following is used in stobj-let. (defun chk-no-duplicatesp (lst) (declare (xargs :guard (and (eqlable-listp lst) (no-duplicatesp lst))) (ignore lst)) nil) ; Rassoc (defun r-eqlable-alistp (x) ; For guard to rassoc-eql-exec. ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of pairs whose ~ilc[cdr]s are suitable for ~ilc[eql]~/ The predicate ~c[r-eqlable-alistp] tests whether its argument is a ~ilc[true-listp] of ~ilc[consp] objects whose ~ilc[cdr]s all satisfy ~ilc[eqlablep]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (consp (car x)) (eqlablep (cdr (car x))) (r-eqlable-alistp (cdr x)))))) (defun r-symbol-alistp (x) ; For guard to rassoc-eq-exec. ":Doc-Section ACL2::ACL2-built-ins recognizer for association lists with symbols as values~/ ~c[(R-symbol-alistp x)] is true if and only if ~c[x] is a list of pairs of the form ~c[(cons key val)] where ~c[val] is a ~ilc[symbolp]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (consp (car x)) (symbolp (cdr (car x))) (r-symbol-alistp (cdr x)))))) (defun rassoc-eq-exec (x alist) (declare (xargs :guard (if (symbolp x) (alistp alist) (r-symbol-alistp alist)))) (cond ((endp alist) nil) ((eq x (cdr (car alist))) (car alist)) (t (rassoc-eq-exec x (cdr alist))))) (defun rassoc-eql-exec (x alist) (declare (xargs :guard (if (eqlablep x) (alistp alist) (r-eqlable-alistp alist)))) (cond ((endp alist) nil) ((eql x (cdr (car alist))) (car alist)) (t (rassoc-eql-exec x (cdr alist))))) (defun rassoc-equal (x alist) (declare (xargs :guard (alistp alist))) #-acl2-loop-only ; Jared Davis found efficiencies in using native assoc (rassoc x alist :test #'equal) #+acl2-loop-only (cond ((endp alist) nil) ((equal x (cdr (car alist))) (car alist)) (t (rassoc-equal x (cdr alist))))) (defmacro rassoc-eq (x alist) `(rassoc ,x ,alist :test 'eq)) (defthm rassoc-eq-exec-is-rassoc-equal (equal (rassoc-eq-exec x alist) (rassoc-equal x alist))) (defthm rassoc-eql-exec-is-rassoc-equal (equal (rassoc-eql-exec x alist) (rassoc-equal x alist))) #+acl2-loop-only (defmacro rassoc (x alist &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins look up value in association list~/ ~bv[] General Forms: (rassoc x alist) (rassoc x alist :test 'eql) ; same as above (eql as equality test) (rassoc x alist :test 'eq) ; same, but eq is equality test (rassoc x alist :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Rassoc x alist)] is the first member of ~c[alist] whose ~ilc[cdr] is ~c[x], or ~c[nil] if no such member exists. ~c[(rassoc x alist)] is thus similar to ~c[(assoc x alist)], the difference being that it looks for the first pair in the given alist whose ~ilc[cdr], rather than ~ilc[car], is ~ilc[eql] to ~c[x]. ~l[assoc]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with the ~ilc[cdr]s of successive elements of ~c[lst].~/ The ~il[guard] for a call of ~c[rassoc] depends on the test. In all cases, the second argument must satisfy ~ilc[alistp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[r-eqlable-alistp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[r-symbol-alistp]. ~l[equality-variants] for a discussion of the relation between ~c[rassoc] and its variants: ~bq[] ~c[(rassoc-eq x lst)] is equivalent to ~c[(rassoc x lst :test 'eq)]; ~c[(rassoc-equal x lst)] is equivalent to ~c[(rassoc x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[rassoc-equal]. ~c[Rassoc] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (alist ,alist)) :logic (rassoc-equal x alist) :exec (rassoc-eq-exec x alist))) ((equal test ''eql) `(let-mbe ((x ,x) (alist ,alist)) :logic (rassoc-equal x alist) :exec (rassoc-eql-exec x alist))) (t ; (equal test 'equal) `(rassoc-equal ,x ,alist)))) (defconst *standard-chars* '(#\Newline #\Space #\! #\" #\# #\$ #\% #\& #\' #\( #\) #\* #\+ #\, #\- #\. #\/ #\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\: #\; #\< #\= #\> #\? #\@ #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z #\[ #\\ #\] #\^ #\_ #\` #\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\{ #\| #\} #\~)) #+acl2-loop-only (defun standard-char-p (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for standard characters~/ ~c[(Standard-char-p x)] is true if and only if ~c[x] is a ``standard'' character, i.e., a member of the list ~c[*standard-chars*]. This list includes ~c[#\\Newline] and ~c[#\\Space] ~il[characters], as well as the usual punctuation and alphanumeric ~il[characters].~/ ~c[Standard-char-p] has a ~il[guard] requiring its argument to be a character. ~c[Standard-char-p] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" ; The following guard is required by p. 234 of CLtL. (declare (xargs :guard (characterp x))) (if (member x *standard-chars*) t nil)) (defun standard-char-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of standard characters~/ ~c[(standard-char-listp x)] is true if and only if ~c[x] is a null-terminated list all of whose members are standard ~il[characters]. ~l[standard-char-p].~/ ~c[Standard-char-listp] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (cond ((consp l) (and (characterp (car l)) (standard-char-p (car l)) (standard-char-listp (cdr l)))) (t (equal l nil)))) (defun character-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of characters~/ The predicate ~c[character-listp] tests whether its argument is a true list of ~il[characters]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom l) (equal l nil)) (t (and (characterp (car l)) (character-listp (cdr l)))))) (defthm character-listp-forward-to-eqlable-listp (implies (character-listp x) (eqlable-listp x)) :rule-classes :forward-chaining) (defthm standard-char-listp-forward-to-character-listp (implies (standard-char-listp x) (character-listp x)) :rule-classes :forward-chaining) (defaxiom coerce-inverse-1 (implies (character-listp x) (equal (coerce (coerce x 'string) 'list) x))) ; A "historical document" regarding standard characters: ; ; To: Kaufmann ; Subject: over strong axiom ; FCC: ~moore/old-mail ; --text follows this line-- ; Axioms.lisp currently contains ; ; (defaxiom coerce-inverse-2 ; (implies (stringp x) ; (equal (coerce (coerce x 'list) 'string) x))) ; ; But the guard for coerce (when the second argument is 'string) requires the first ; argument to be a standard-char-listp. Thus, unless we know that (coerce x 'list) ; returns a standard-char-listp when (stringp x), the guard on the outer coerce is ; violated. ; ; If we are really serious that ACL2 strings may contain nonstandard chars, then ; this axiom is too strong. I will leave this note in axioms.lisp and just go ; on. But when the guard question is settled I would like to return to this and ; make explicit our occasional implicit assumption that strings are composed of ; standard chars. ; ; J (defaxiom coerce-inverse-2 (implies (stringp x) (equal (coerce (coerce x 'list) 'string) x))) ; Once upon a time, Moore (working alone) added the following axiom. ; (defaxiom standard-char-listp-coerce ; (implies (stringp str) ; (standard-char-listp (coerce str 'list)))) (defaxiom character-listp-coerce (character-listp (coerce str 'list)) :rule-classes (:rewrite (:forward-chaining :trigger-terms ((coerce str 'list))))) ; In AKCL the nonstandard character #\Page prints as ^L and may be included in ; strings, as in "^L". Now if you try to type that string in ACL2, you get an ; error. And ACL2 does not let you use coerce to produce the string, e.g., ; with (coerce (list #\Page) 'string), because the guard for coerce is ; violated. So here we have a situation in which no ACL2 function in LP will ; ever see a nonstandard char in a string, but CLTL permits it. However, we ; consider the axiom to be appropriate, because ACL2 strings contain only ; standard characters. (in-theory (disable standard-char-listp standard-char-p)) ; (defthm standard-char-listp-coerce-forward-chaining ; ; ; If (stringp str) is in the context, we want to make a "note" that ; ; (coerce str 'list) is a standard-char-listp in case this fact is ; ; needed during later backchaining. We see no need to forward chain ; ; from (standard-char-listp (coerce str 'list)), however; the rewrite ; ; rule generated here should suffice for relieving any such hypothesis. ; ; (implies (stringp str) ; (standard-char-listp (coerce str 'list))) ; :rule-classes ((:forward-chaining :trigger-terms ; ((coerce str 'list))))) #+acl2-loop-only (defun string (x) ":Doc-Section ACL2::ACL2-built-ins ~il[coerce] to a string~/ ~c[(String x)] ~il[coerce]s ~c[x] to a string. If ~c[x] is already a string, then it is returned unchanged; if ~c[x] is a symbol, then its ~ilc[symbol-name] is returned; and if ~c[x] is a character, the corresponding one-character string is returned.~/ The ~il[guard] for ~c[string] requires its argument to be a string, a symbol, or a character. ~c[String] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard ; NOTE: When we finally get hold of a definitive Common Lisp ; reference, let's clarify the statement near the bottom of p. 466 of ; CLtL2, which says: "Presumably converting a character to a string ; always works according to this vote." But we'll plunge ahead as ; follows, in part because we want to remain compliant with CLtL1, ; which isn't as complete as one might wish regarding which characters ; can go into strings. (or (stringp x) (symbolp x) (characterp x)))) (cond ((stringp x) x) ((symbolp x) (symbol-name x)) (t (coerce (list x) 'string)))) #+acl2-loop-only (defun alpha-char-p (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for alphabetic characters~/ ~c[(Alpha-char-p x)] is true if and only if ~c[x] is a alphabetic character, i.e., one of the ~il[characters] ~c[#\\a], ~c[#\\b], ..., ~c[#\\z], ~c[#\\A], ~c[#\\B], ..., ~c[#\\Z].~/ The ~il[guard] for ~c[alpha-char-p] requires its argument to be a character. ~c[Alpha-char-p] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" ; The following guard is required by p. 235 of CLtL. (declare (xargs :guard (characterp x))) (and (member x '(#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z)) t)) #+acl2-loop-only (defun upper-case-p (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for upper case characters~/ ~c[(Upper-case-p x)] is true if and only if ~c[x] is an upper case character, i.e., a member of the list ~c[#\\A], ~c[#\\B], ..., ~c[#\\Z].~/ The ~il[guard] for ~c[upper-case-p] requires its argument to be a standard character (~pl[standard-char-p]). ~c[Upper-case-p] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" ; The guard characterp is required by p. 235 of CLtL. However, In Allegro 6.0 ; we see characters other than standard characters that are treated as upper ; case, such as (code-char (+ 128 65)). So we strengthen that guard. (declare (xargs :guard (and (characterp x) (standard-char-p x)))) (and (member x '(#\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z)) t)) #+acl2-loop-only (defun lower-case-p (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for lower case characters~/ ~c[(Lower-case-p x)] is true if and only if ~c[x] is a lower case character, i.e., a member of the list ~c[#\\A], ~c[#\\B], ..., ~c[#\\Z].~/ The ~il[guard] for ~c[lower-case-p] requires its argument to be a standard character (~pl[standard-char-p]). ~c[Lower-case-p] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" ; The guard characterp is required by p. 235 of CLtL. However, In Allegro 6.0 ; we see characters other than standard characters that are treated as upper ; case, such as (code-char (+ 128 65)). So we strengthen that guard. (declare (xargs :guard (and (characterp x) (standard-char-p x)))) (and (member x '(#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z)) t)) #+acl2-loop-only (defun char-upcase (x) ":Doc-Section ACL2::ACL2-built-ins turn lower-case ~il[characters] into upper-case ~il[characters]~/ ~c[(Char-upcase x)] is equal to ~c[#\\A] when ~c[x] is ~c[#\\a], ~c[#\\B] when ~c[x] is ~c[#\\b], ..., and ~c[#\\Z] when ~c[x] is ~c[#\\z], and is ~c[x] for any other character.~/ The ~il[guard] for ~c[char-upcase] requires its argument to be a standard character (~pl[standard-char-p]). ~c[Char-upcase] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" ; The guard characterp is required by p. 231 of CLtL. However, In Allegro 6.0 ; we see characters other than standard characters that are treated as upper ; case, such as (code-char (+ 128 65)). So we strengthen that guard. (declare (xargs :guard (and (characterp x) (standard-char-p x)))) (let ((pair (assoc x '((#\a . #\A) (#\b . #\B) (#\c . #\C) (#\d . #\D) (#\e . #\E) (#\f . #\F) (#\g . #\G) (#\h . #\H) (#\i . #\I) (#\j . #\J) (#\k . #\K) (#\l . #\L) (#\m . #\M) (#\n . #\N) (#\o . #\O) (#\p . #\P) (#\q . #\Q) (#\r . #\R) (#\s . #\S) (#\t . #\T) (#\u . #\U) (#\v . #\V) (#\w . #\W) (#\x . #\X) (#\y . #\Y) (#\z . #\Z))))) (cond (pair (cdr pair)) ((characterp x) x) (t (code-char 0))))) #+acl2-loop-only (defun char-downcase (x) ":Doc-Section ACL2::ACL2-built-ins turn upper-case ~il[characters] into lower-case ~il[characters]~/ ~c[(Char-downcase x)] is equal to ~c[#\\a] when ~c[x] is ~c[#\\A], ~c[#\\b] when ~c[x] is ~c[#\\B], ..., and ~c[#\\z] when ~c[x] is ~c[#\\Z], and is ~c[x] for any other character.~/ The ~il[guard] for ~c[char-downcase] requires its argument to be a standard character (~pl[standard-char-p]). ~c[Char-downcase] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" ; The guard characterp is required by p. 231 of CLtL. However, In Allegro 6.0 ; we see characters other than standard characters that are treated as upper ; case, such as (code-char (+ 128 65)). So we strengthen that guard. (declare (xargs :guard (and (characterp x) (standard-char-p x)))) (let ((pair (assoc x '((#\A . #\a) (#\B . #\b) (#\C . #\c) (#\D . #\d) (#\E . #\e) (#\F . #\f) (#\G . #\g) (#\H . #\h) (#\I . #\i) (#\J . #\j) (#\K . #\k) (#\L . #\l) (#\M . #\m) (#\N . #\n) (#\O . #\o) (#\P . #\p) (#\Q . #\q) (#\R . #\r) (#\S . #\s) (#\T . #\t) (#\U . #\u) (#\V . #\v) (#\W . #\w) (#\X . #\x) (#\Y . #\y) (#\Z . #\z))))) (cond (pair (cdr pair)) ((characterp x) x) (t (code-char 0))))) (defthm lower-case-p-char-downcase (implies (and (upper-case-p x) (characterp x)) (lower-case-p (char-downcase x)))) (defthm upper-case-p-char-upcase (implies (and (lower-case-p x) (characterp x)) (upper-case-p (char-upcase x)))) (defthm lower-case-p-forward-to-alpha-char-p (implies (and (lower-case-p x) (characterp x)) (alpha-char-p x)) :rule-classes :forward-chaining) (defthm upper-case-p-forward-to-alpha-char-p (implies (and (upper-case-p x) (characterp x)) (alpha-char-p x)) :rule-classes :forward-chaining) (defthm alpha-char-p-forward-to-characterp (implies (alpha-char-p x) (characterp x)) :rule-classes :forward-chaining) (defthm characterp-char-downcase (characterp (char-downcase x)) :rule-classes :type-prescription) (defthm characterp-char-upcase (characterp (char-upcase x)) :rule-classes :type-prescription) ; We disable the following functions in order to protect people from getting ; burned by their explosive definitions. (in-theory (disable alpha-char-p upper-case-p lower-case-p char-upcase char-downcase)) (defun string-downcase1 (l) (declare (xargs :guard (standard-char-listp l) :guard-hints (("Goal" :in-theory (enable standard-char-listp))))) (if (atom l) nil (cons (char-downcase (car l)) (string-downcase1 (cdr l))))) (defthm character-listp-string-downcase-1 (character-listp (string-downcase1 x))) #+acl2-loop-only (defun string-downcase (x) ":Doc-Section ACL2::ACL2-built-ins in a given string, turn upper-case ~il[characters] into lower-case~/ For a string ~c[x], ~c[(string-downcase x)] is the result of applying ~ilc[char-downcase] to each character in ~c[x].~/ The ~il[guard] for ~c[string-downcase] requires its argument to be a string containing only standard characters. ~c[String-downcase] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp x) (standard-char-listp (coerce x 'list))))) ; As with other functions, e.g., reverse, the guards on this function ; can't currently be proved because the outer coerce below requires ; its argument to be made of standard characters. We don't know that ; the string x is made of standard characters. (coerce (string-downcase1 (coerce x 'list)) 'string)) (defun string-upcase1 (l) (declare (xargs :guard (standard-char-listp l) :guard-hints (("Goal" :in-theory (enable standard-char-listp))))) (if (atom l) nil (cons (char-upcase (car l)) (string-upcase1 (cdr l))))) (defthm character-listp-string-upcase1-1 (character-listp (string-upcase1 x))) #+acl2-loop-only (defun string-upcase (x) ":Doc-Section ACL2::ACL2-built-ins in a given string, turn lower-case ~il[characters] into upper-case~/ For a string ~c[x], ~c[(string-upcase x)] is the result of applying ~ilc[char-upcase] to each character in ~c[x].~/ The ~il[guard] for ~c[string-upcase] requires its argument to be a string containing only standard characters. ~c[String-upcase] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp x) (standard-char-listp (coerce x 'list))))) (coerce (string-upcase1 (coerce x 'list)) 'string)) (defun our-digit-char-p (ch radix) (declare (xargs :guard (and (characterp ch) (integerp radix) (<= 2 radix) (<= radix 36)))) (let ((l (assoc ch '((#\0 . 0) (#\1 . 1) (#\2 . 2) (#\3 . 3) (#\4 . 4) (#\5 . 5) (#\6 . 6) (#\7 . 7) (#\8 . 8) (#\9 . 9) (#\a . 10) (#\b . 11) (#\c . 12) (#\d . 13) (#\e . 14) (#\f . 15) (#\g . 16) (#\h . 17) (#\i . 18) (#\j . 19) (#\k . 20) (#\l . 21) (#\m . 22) (#\n . 23) (#\o . 24) (#\p . 25) (#\q . 26) (#\r . 27) (#\s . 28) (#\t . 29) (#\u . 30) (#\v . 31) (#\w . 32) (#\x . 33) (#\y . 34) (#\z . 35) (#\A . 10) (#\B . 11) (#\C . 12) (#\D . 13) (#\E . 14) (#\F . 15) (#\G . 16) (#\H . 17) (#\I . 18) (#\J . 19) (#\K . 20) (#\L . 21) (#\M . 22) (#\N . 23) (#\O . 24) (#\P . 25) (#\Q . 26) (#\R . 27) (#\S . 28) (#\T . 29) (#\U . 30) (#\V . 31) (#\W . 32) (#\X . 33) (#\Y . 34) (#\Z . 35))))) (cond ((and l (< (cdr l) radix)) (cdr l)) (t nil)))) #+acl2-loop-only (defmacro digit-char-p (ch &optional (radix '10)) ":Doc-Section ACL2::ACL2-built-ins the number, if any, corresponding to a given character~/ ~c[(digit-char-p ch)] is the integer corresponding to the character ~c[ch] in base ~c[10]. For example, ~c[(digit-char-p #\\3)] is equal to the integer ~c[3]. More generally, an optional second argument specifies the radix (default ~c[10], as indicated above).~/ The ~il[guard] for ~c[digit-char-p] (more precisely, for the function ~c[our-digit-char-p] that calls of this macro expand to) requires its second argument to be an integer between 2 and 36, inclusive, and its first argument to be a character. ~c[Digit-char-p] is a Common Lisp function, though it is implemented in the ACL2 logic as an ACL2 macro. See any Common Lisp documentation for more information.~/" `(our-digit-char-p ,ch ,radix)) #+acl2-loop-only (defun char-equal (x y) ":Doc-Section ACL2::ACL2-built-ins character equality without regard to case~/ For ~il[characters] ~c[x] and ~c[y], ~c[(char-equal x y)] is true if and only if ~c[x] and ~c[y] are the same except perhaps for their case.~/ The ~il[guard] on ~c[char-equal] requires that its arguments are both standard ~il[characters] (~pl[standard-char-p]). ~c[Char-equal] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (characterp x) (standard-char-p x) (characterp y) (standard-char-p y)))) (eql (char-downcase x) (char-downcase y))) (defun atom-listp (lst) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of ~il[atom]s~/ The predicate ~c[atom-listp] tests whether its argument is a ~ilc[true-listp] of ~il[atom]s, i.e., of non-conses.~/ Also ~pl[good-atom-listp]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (cond ((atom lst) (eq lst nil)) (t (and (atom (car lst)) (atom-listp (cdr lst)))))) (defthm atom-listp-forward-to-true-listp (implies (atom-listp x) (true-listp x)) :rule-classes :forward-chaining) (defthm eqlable-listp-forward-to-atom-listp (implies (eqlable-listp x) (atom-listp x)) :rule-classes :forward-chaining) (defun good-atom-listp (lst) ; Keep this in sync with bad-atom. ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of ``good'' ~il[atom]s~/ The predicate ~c[good-atom-listp] tests whether its argument is a ~ilc[true-listp] of ``good'' ~il[atom]s, i.e., where each element is a number, a symbol, a character, or a string.~/ Also ~pl[atom-listp]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (cond ((atom lst) (eq lst nil)) (t (and (or (acl2-numberp (car lst)) (symbolp (car lst)) (characterp (car lst)) (stringp (car lst))) (good-atom-listp (cdr lst)))))) (defthm good-atom-listp-forward-to-atom-listp (implies (good-atom-listp x) (atom-listp x)) :rule-classes :forward-chaining) (defthm characterp-nth (implies (and (character-listp x) (<= 0 i) (< i (len x))) (characterp (nth i x)))) (defun ifix (x) ":Doc-Section ACL2::ACL2-built-ins coerce to an integer~/ ~c[Ifix] simply returns any integer argument unchanged, returning ~c[0] on a non-integer argument. Also ~pl[nfix], ~pl[rfix], ~pl[realfix] and ~pl[fix] for analogous functions that coerce to a natural number, a rational number, a real, and a number, respectively.~/ ~c[Ifix] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (if (integerp x) x 0)) (defun rfix (x) ":Doc-Section ACL2::ACL2-built-ins coerce to a rational number~/ ~c[Rfix] simply returns any rational number argument unchanged, returning ~c[0] on a non-rational argument. Also ~pl[nfix], ~pl[ifix], ~pl[realfix], and ~pl[fix] for analogous functions that coerce to a natural number, an integer, a real, and a number, respectively.~/ ~c[Rfix] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (if (rationalp x) x 0)) ;; RAG - I added "realfix" to coerce numbers into reals. I would have ;; liked to use "rfix" for it, but "rfix" was taken for the ;; rationals. "ifix" as in "irrational-fix" would be a misnomer, ;; since it's the identity functions for rationals as well as ;; irrationals. In desperation, we called it realfix, even though ;; that makes it more awkward to use than the other "fix" functions. ; Since the next function, realfix, is referred to by other :doc topics, do not ; make it conditional upon #+:non-standard-analysis. (defun realfix (x) ":Doc-Section ACL2::ACL2-built-ins coerce to a real number~/ ~c[Realfix] simply returns any real number argument unchanged, returning ~c[0] on a non-real argument. Also ~pl[nfix], ~pl[ifix], ~pl[rfix], and ~pl[fix] for analogous functions that coerce to a natural number, an integer, a rational, and a number, respectively.~/ ~c[Realfix] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (if (real/rationalp x) x 0)) (defun nfix (x) ":Doc-Section ACL2::ACL2-built-ins coerce to a natural number~/ ~c[Nfix] simply returns any natural number argument unchanged, returning ~c[0] on an argument that is not a natural number. Also ~pl[ifix], ~pl[rfix], ~pl[realfix], and ~pl[fix] for analogous functions that coerce to an integer, a rational number, a real, and a number, respectively.~/ ~c[Nfix] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (if (and (integerp x) (>= x 0)) x 0)) (defun string-equal1 (str1 str2 i maximum) (declare (xargs :guard (and (stringp str1) (standard-char-listp (coerce str1 'list)) (stringp str2) (standard-char-listp (coerce str2 'list)) (integerp i) (integerp maximum) (<= maximum (length str1)) (<= maximum (length str2)) (<= 0 i) (<= i maximum)) ; We make this function :program until we know enough about o-p ; to prove its termination. :mode :program)) (let ((i (nfix i))) (cond ((>= i (ifix maximum)) t) (t (and (char-equal (char str1 i) (char str2 i)) (string-equal1 str1 str2 (+ 1 i) maximum)))))) #+acl2-loop-only (defun string-equal (str1 str2) ":Doc-Section ACL2::ACL2-built-ins string equality without regard to case~/ For strings ~c[str1] and ~c[str2], ~c[(string-equal str1 str2)] is true if and only ~c[str1] and ~c[str2] are the same except perhaps for the cases of their ~il[characters].~/ The ~il[guard] on ~c[string-equal] requires that its arguments are strings consisting of standard characters (~pl[standard-char-listp]). ~c[String-equal] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp str1) (standard-char-listp (coerce str1 'list)) (stringp str2) (standard-char-listp (coerce str2 'list))) :mode :program)) (let ((len1 (length str1))) (and (= len1 (length str2)) (string-equal1 str1 str2 0 len1)))) (defun standard-string-alistp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for association lists with standard strings as keys~/ ~c[(Standard-string-alistp x)] is true if and only if ~c[x] is a list of pairs of the form ~c[(cons key val)] where ~c[key] is a string all of whose characters are standard (~pl[standard-char-p]).~/ ~c[Standard-string-alistp] has a ~il[guard] of ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (consp (car x)) (stringp (car (car x))) (standard-char-listp (coerce (car (car x)) 'list)) (standard-string-alistp (cdr x)))))) (defthm standard-string-alistp-forward-to-alistp (implies (standard-string-alistp x) (alistp x)) :rule-classes :forward-chaining) (defun assoc-string-equal (str alist) ":Doc-Section ACL2::ACL2-built-ins look up key, a string, in association list~/ ~c[(Assoc-string-equal x alist)] is similar to ~ilc[assoc-equal]. However, for string ~c[x] and alist ~c[alist], the comparison of ~c[x] with successive keys in ~c[alist] is done using ~ilc[string-equal] rather than ~ilc[equal].~/ The ~il[guard] for ~c[assoc-string-equal] requires that ~c[x] is a string and ~c[alist] is an alist. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp str) (standard-char-listp (coerce str 'list)) (standard-string-alistp alist)) :mode :program)) (cond ((endp alist) nil) ((string-equal str (car (car alist))) (car alist)) (t (assoc-string-equal str (cdr alist))))) ; Ordinal stuff. It seems more or less impossible to get o (x y) ":Doc-Section ACL2::ACL2-built-ins the greater-than relation for the ordinals~/~/ ~c[O>] is a macro and ~c[(o> x y)] expands to ~c[(o< y x)]. ~l[o<]." `(o< ,y ,x)) (defmacro o<= (x y) ":Doc-Section ACL2::ACL2-built-ins the less-than-or-equal relation for the ordinals~/~/ ~c[o<=] is a macro and ~c[(o<= x y)] expands to ~c[(not (o< y x))]. ~l[o<]." `(not (o< ,y ,x))) (defmacro o>= (x y) ":Doc-Section ACL2::ACL2-built-ins the greater-than-or-equal relation for the ordinals~/~/ ~c[O>=] is a macro and ~c[(o>= x y)] expands to ~c[(not (o< x y))]. ~l[o<]." `(not (o< ,x ,y))) (defun o-p (x) ":Doc-Section ACL2::ACL2-built-ins a recognizer for the ordinals up to epsilon-0~/ Using the nonnegative integers and lists we can represent the ordinals up to ~c[epsilon-0]. The ordinal representation used in ACL2 has changed as of Version_2.8 from that of Nqthm-1992, courtesy of Pete Manolios and Daron Vroon; additional discussion may be found in ``Ordinal Arithmetic in ACL2'', proceedings of ACL2 Workshop 2003, ~url[http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/]. Previously, ACL2's notion of ordinal was very similar to the development given in ``New Version of the Consistency Proof for Elementary Number Theory'' in The Collected Papers of Gerhard Gentzen, ed. M.E. Szabo, North-Holland Publishing Company, Amsterdam, 1969, pp 132-213.~/ The following essay is intended to provide intuition about ordinals. The truth, of course, lies simply in the ACL2 definitions of ~c[o-p] and ~ilc[o<]. Very intuitively, think of each non-zero natural number as by being denoted by a series of the appropriate number of strokes, i.e., ~bv[] 0 0 1 | 2 || 3 ||| 4 |||| ... ... ~ev[] Then ``~c[omega],'' here written as ~c[w], is the ordinal that might be written as ~bv[] w |||||..., ~ev[] i.e., an infinite number of strokes. Addition here is just concatenation. Observe that adding one to the front of ~c[w] in the picture above produces ~c[w] again, which gives rise to a standard definition of ~c[w]: ~c[w] is the least ordinal such that adding another stroke at the beginning does not change the ordinal. We denote by ~c[w+w] or ~c[w*2] the ``~c[doubly infinite]'' sequence that we might write as follows. ~bv[] w*2 |||||... |||||... ~ev[] One way to think of ~c[w*2] is that it is obtained by replacing each stroke in ~c[2] ~c[(||)] by ~c[w]. Thus, one can imagine ~c[w*3], ~c[w*4], etc., which leads ultimately to the idea of ``~c[w*w],'' the ordinal obtained by replacing each stroke in ~c[w] by ~c[w]. This is also written as ``~c[omega] squared'' or ~c[w^2], or: ~bv[] 2 w |||||... |||||... |||||... |||||... |||||... ... ~ev[] We can analogously construct ~c[w^3] by replacing each stroke in ~c[w] by ~c[w^2] (which, it turns out, is the same as replacing each stroke in ~c[w^2] by ~c[w]). That is, we can construct ~c[w^3] as ~c[w] copies of ~c[w^2], ~bv[] 3 2 2 2 2 w w ... w ... w ... w ... ... ~ev[] Then we can construct ~c[w^4] as ~c[w] copies of ~c[w^3], ~c[w^5] as ~c[w] copies of ~c[w^4], etc., ultimately suggesting ~c[w^w]. We can then stack ~c[omega]s, i.e., ~c[(w^w)^w] etc. Consider the ``limit'' of all of those stacks, which we might display as follows. ~bv[] . . . w w w w w ~ev[] That is epsilon-0. Below we begin listing some ordinals up to ~c[epsilon-0]; the reader can fill in the gaps at his or her leisure. We show in the left column the conventional notation, using ~c[w] as ``~c[omega],'' and in the right column the ACL2 object representing the corresponding ordinal. ~bv[] ordinal ACL2 representation 0 0 1 1 2 2 3 3 ... ... w '((1 . 1) . 0) w+1 '((1 . 1) . 1) w+2 '((1 . 1) . 2) ... ... w*2 '((1 . 2) . 0) (w*2)+1 '((1 . 2) . 1) ... ... w*3 '((1 . 3) . 0) (w*3)+1 '((1 . 3) . 1) ... ... 2 w '((2 . 1) . 0) ... ... 2 w +w*4+3 '((2 . 1) (1 . 4) . 3) ... ... 3 w '((3 . 1) . 0) ... ... w w '((((1 . 1) . 0) . 1) . 0) ... ... w 99 w +w +w4+3 '((((1 . 1) . 0) . 1) (99 . 1) (1 . 4) . 3) ... ... 2 w w '((((2 . 1) . 0) . 1) . 0) ... ... w w w '((((((1 . 1) . 0) . 1) . 0) . 1) . 0) ... ... ~ev[] Observe that the sequence of ~c[o-p]s starts with the natural numbers (which are recognized by ~ilc[natp]). This is convenient because it means that if a term, such as a measure expression for justifying a recursive function (~pl[o<]) must produce an ~c[o-p], it suffices for it to produce a natural number. The ordinals listed above are listed in ascending order. This is the ordering tested by ~ilc[o<]. The ``~c[epsilon-0] ordinals'' of ACL2 are recognized by the recursively defined function ~c[o-p]. The base case of the recursion tells us that natural numbers are ~c[epsilon-0] ordinals. Otherwise, an ~c[epsilon-0] ordinal is a list of ~ilc[cons] pairs whose final ~ilc[cdr] is a natural number, ~c[((a1 . x1) (a2 . x2) ... (an . xn) . p)]. This corresponds to the ordinal ~c[(w^a1)x1 + (w^a2)x2 + ... + (w^an)xn + p]. Each ~c[ai] is an ordinal in the ACL2 representation that is not equal to 0. The sequence of the ~c[ai]'s is strictly decreasing (as defined by ~ilc[o<]). Each ~c[xi] is a positive integer (as recognized by ~ilc[posp]). Note that infinite ordinals should generally be created using the ordinal constructor, ~ilc[make-ord], rather than ~ilc[cons]. The functions ~ilc[o-first-expt], ~ilc[o-first-coeff], and ~ilc[o-rst] are ordinals destructors. Finally, the function ~ilc[o-finp] and the macro ~ilc[o-infp] tell whether an ordinal is finite or infinite, respectively. The function ~ilc[o<] compares two ~c[epsilon-0] ordinals, ~c[x] and ~c[y]. If both are integers, ~c[(o< x y)] is just ~c[x a2 > ... > an > 0] such that ~c[alpha > a1] and ~c[alpha = w^(a1)x1 + w^(a2)x2 + ... + w^(an)xn + p]. We call ~c[a1] the ``first exponent'', ~c[x1] the ``first coefficient'', and the remainder ~c[(w^(a2)x2 + ... + w^(an)xn + p)] the ``rest'' of alpha. ~c[(Make-ord fe fco rst)] corresponds to the ordinal ~c[(w^fe)fco + rst]. Thus the first infinite ordinal, ~c[w] (~c[omega]), is constructed by ~bv[] (make-ord 1 1 0) ~ev[] and, for example, the ordinal ~c[(w^2)5 + w2 + 7] is constructed by: ~bv[] (make-ord 2 5 (make-ord 1 2 7)) . ~ev[] The reason ~c[make-ord] is used rather than ~ilc[cons] is that it allows us to reason more abstractly about the ordinals, without having to worry about the underlying representation. To see the ACL2 definition of this function, ~pl[pf]." (declare (xargs :guard (and (posp fco) (o-p fe) (o-p rst)))) (cons (cons fe fco) rst)) (defun list*-macro (lst) (declare (xargs :guard (and (true-listp lst) (consp lst)))) (if (endp (cdr lst)) (car lst) (cons 'cons (cons (car lst) (cons (list*-macro (cdr lst)) nil))))) #+acl2-loop-only (defmacro list* (&rest args) ":Doc-Section ACL2::ACL2-built-ins build a list~/ ~c[List*] is the Common Lisp macro for building a list of objects from given elements and a tail. For example, ~c[(list* 5 6 '(7 8 9))] equals the list ~c['(5 6 7 8 9)]. Also ~pl[list].~/ ~c[List*] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (consp args))) (list*-macro args)) #-acl2-loop-only (progn (defmacro throw-without-attach (ignored-attachment fn formals) `(throw-raw-ev-fncall (list* 'ev-fncall-null-body-er ,ignored-attachment ',fn (print-list-without-stobj-arrays (list ,@formals))))) (defvar *aokp* ; We set *aokp* to t simply so that we can use attachments in raw Lisp. It ; will be bound suitably inside the ACL2 loop by calls of raw-ev-fncall. t) (defmacro aokp () '*aokp*) #+hons (defvar *attached-fn-called* nil) #+hons (defmacro update-attached-fn-called (fn) `(when (null *attached-fn-called*) (setq *attached-fn-called* ,fn))) (defmacro throw-or-attach (fn formals &optional *1*-p) ; Warning: this macro assumes that (attachment-symbol fn) is special and, more ; important, bound. So it is probably best to lay down calls of of this macro ; using throw-or-attach-call. (let ((at-fn (attachment-symbol fn)) (at-fn-var (gensym))) ; It is tempting to insert the form (eval `(defvar ,at-fn nil)) here. But that ; would only be evaluated at compile time. When loading a compiled file on ; behalf of including a book, this eval call would no longer be around; it ; would instead have been executed during compilation. The Warning above is ; intended to guarantee that at-fn has already been both declared special and ; bound. `(let ((,at-fn-var ,at-fn)) ; to look up special var value only once (cond ((and ,at-fn-var (aokp)) #+hons (update-attached-fn-called ',fn) (funcall ,(if *1*-p `(*1*-symbol ,at-fn-var) at-fn-var) ,@formals)) (t (throw-without-attach ,at-fn ,fn ,formals)))))) ) (defun throw-or-attach-call (fn formals) ; A call of throw-or-attach assumes that the attachment-symbol is special and, ; more importantly, bound. So we ensure that property here. ; It's a bit subtle why this approach works. Indeed, consider the following ; example. Suppose the book foo.lisp has the just following two forms. ; (in-package "ACL2") ; (encapsulate ((foo (x) t)) (local (defun foo (x) x))) ; Now certify the book, with (certify-book "foo"), and then in a new session: ; :q ; (load "foo") ; (boundp (attachment-symbol 'foo)) ; Then boundp call returns nil. If instead we do this in a new session ; (include-book "foo") ; :q ; (boundp (attachment-symbol 'foo)) ; then the boundp call returns t. This is not surprising, since we can see by ; tracing throw-or-attach-call that it is being called, thus defining the ; attachment-symbol. ; There might thus seem to be the following possibility of errors due to ; unbound attachment-symbols. Suppose that foo were called before its ; attachment-symbol is defined by evaluation of the above encapsulate form in ; the loop, say, during the early load of the compiled file for foo.lisp on ; behalf of include-book. Then an error would occur, because the ; attachment-symbol for foo would not yet be defined. However, the only way we ; can imagine this case occurring for a certified book is if foo gets an ; attachment before it is called (else the book wouldn't have been ; certifiable). Yet in raw Lisp, defattach calls defparameter for the ; attachment-symbol for every function receiving an attachment, thus avoiding ; the possibility of this proposed problem of unbound attachment-symbols. (declare (xargs :guard t)) #-acl2-loop-only (eval `(defvar ,(attachment-symbol fn) nil)) (list 'throw-or-attach fn formals)) (defun null-body-er (fn formals maybe-attach) (declare (xargs :guard t)) (if maybe-attach (throw-or-attach-call fn formals) (list 'throw-without-attach nil fn formals))) ; CLTL2 and the ANSI standard have made the main Lisp package name be ; COMMON-LISP rather than the older LISP. Before Version_2.6 we ; handled this discrepancy in a way that could be said to be unsound. ; For example, one could prove (equal (symbol-package-name 'car) ; "LISP") in an ACL2 built on top of GCL, then prove (equal ; (symbol-package-name 'car) "COMMON-LISP")) in an ACL2 built on top ; of Allegro CL. Thus, one could certify a book with the former ; theorem in a GCL-based ACL2, then include that book in an ; Allegro-based ACL2 and prove NIL. Our solution is to make the ; "LISP" package look like "COMMON-LISP" from the perspective of ACL2, ; for example: (symbol-package-name 'car) = "COMMON-LISP". ; Warning: If you change the following, change the corresponding line in the ; defparameter for *ever-known-package-alist* above, consider changing ; symbol-package-name, and perhaps adjust the check for "LISP" in defpkg-fn. (defconst *main-lisp-package-name* ; Keep this in sync with *main-lisp-package-name-raw*. "COMMON-LISP") ; Warning: If you add primitive packages to this list, be sure to add ; the defaxioms that would be done by defpkg. For example, below you ; will find a defaxiom for ACL2-INPUT-CHANNEL-PACKAGE and any new ; package should have an analogous axiom added. Each of the primitive ; packages below has such an axiom explicitly added in axioms.lisp ; (except for the main lisp package name, whose import list is ; essentially unknown). ; Warning: Keep the initial value of the following constant identical to ; that of the raw lisp defparameter *ever-known-package-alist* above. (defconst *initial-known-package-alist* (list (make-package-entry :name "ACL2-INPUT-CHANNEL" :imports nil) (make-package-entry :name "ACL2-OUTPUT-CHANNEL" :imports nil) (make-package-entry :name "ACL2" :imports *common-lisp-symbols-from-main-lisp-package*) (make-package-entry :name *main-lisp-package-name* ; From a logical perspective, ACL2 pretends that no symbols are imported into ; the main Lisp package, "COMMON-LISP". This perspective is implemented by ; bad-lisp-objectp, as described in a comment there about maintaining the ; Invariant on Symbols in the Common Lisp Package. In short, every good ACL2 ; symbol not in a package known to ACL2 must be imported into the main Lisp ; package and must have "COMMON-LISP" as its *initial-lisp-symbol-mark* ; property. :imports nil) (make-package-entry :name "KEYWORD" :imports nil))) (defaxiom stringp-symbol-package-name (stringp (symbol-package-name x)) :rule-classes :type-prescription) (defaxiom symbolp-intern-in-package-of-symbol (symbolp (intern-in-package-of-symbol x y)) :rule-classes :type-prescription) (defaxiom symbolp-pkg-witness (symbolp (pkg-witness x)) :rule-classes :type-prescription) #-acl2-loop-only (defparameter *ld-level* ; This parameter will always be equal to the number of recursive calls of LD ; and/or WORMHOLE we are in. Since each pushes a new frame on ; *acl2-unwind-protect-stack* the value of *ld-level* should always be the ; length of the stack. But *ld-level* is maintained as a special, i.e., it is ; always bound when we enter LD while the stack is a global. An abort may ; possibly rip us out of a call of LD, causing *ld-level* to decrease but not ; affecting the stack. It is this violation of the "invariant" between the two ; that indicates that the stack must be unwound some (to cleanup after an ; aborted inferior). ; Parallelism blemish: This variable is let-bound in ld-fn (and hence by ; wormhole). Perhaps this could present a problem. For example, we wonder ; about the case where waterfall-parallelism is enabled and a parent thread ; gets confused about the value of *ld-level* (or (@ ld-level)) when changed by ; the child thread. For a second example, we can imagine (and we may have ; seen) a case in which there are two threads doing rewriting, and one does a ; throw (say, because time has expired), which puts the two threads temporarily ; out of sync in their values of *ld-level*. Wormholes involve calls of ld and ; hence also give us concern. As of this writing we know of no cases where any ; such problems exist, and there is at least one case, the definition of ; mt-future, where we explicitly provide bindings to arrange that a child ; thread receives its *ld-level* and (@ ld-level) from its parent (not from ; some spurious global values). Mt-future also has an assertion to check that ; we keep *ld-level* and (@ ld-level) in sync with each other. 0) ; For an explanation of the next defvar, see the comment in ; hard-error, below. #-acl2-loop-only (defvar *hard-error-returns-nilp* nil) #-acl2-loop-only (defun-one-output throw-raw-ev-fncall (val) ; This function just throws to raw-ev-fncall (or causes an ; interface-er if there is no raw-ev-fncall). The coding below ; actually assumes that we are in a raw-ev-fncall if *ld-level* > 0. ; This assumption may not be entirely true. If we have a bug in our ; LD code, e.g., in printing the prompt, we could throw to a ; nonexistent tag. We might get the GCL ; Error: The tag RAW-EV-FNCALL is undefined. (cond ((or (= *ld-level* 0) (raw-mode-p *the-live-state*)) (interface-er "~@0" (ev-fncall-msg val (w *the-live-state*) (user-stobj-alist *the-live-state*)))) (t (throw 'raw-ev-fncall val)))) (defun hard-error (ctx str alist) ; Logically, this function just returns nil. The implementation ; usually signals a hard error, which is sound since it is akin to ; running out of stack or some other resource problem. ; But if this function is called as part of a proof, e.g., ; (thm (equal (car (cons (hard-error 'ctx "Test" nil) y)) nil)) ; we do not want to cause an error! (Note: the simpler example ; (thm (equal (hard-error 'ctx "Test" nil) nil)) can be proved ; without any special handling of the executable counterpart of ; hard-error, because we know its type-set is *ts-nil*. So to cause ; an error, you have to have the hard-error term used in a place ; where type-reasoning alone won't do the job.) ; Sometimes hard-error is used in the guard of a function, e.g., ; illegal. Generally evaluating that guard is to signal an error. ; But if guard-checking-on is nil, then we want to cause no error and ; just let the guard return nil. We evaluate the guard even when ; guard-checking-on is nil (though not for user-defined functions when ; it is :none) so we know whether to call the raw Lisp version or the ; ACL2_*1*_ACL2 version of a function. ; Logically speaking the two behaviors of hard-error, nil or error, ; are indistinguishable. So we can choose which behavior we want ; without soundness concerns. Therefore, we have a raw Lisp special ; variable, named *hard-error-returns-nilp*, and if it is true, we ; return nil. It is up to the environment to somehow set that special ; variable. ; In ev-fncall we provide the argument hard-error-returns-nilp which ; is used as the binding of *hard-error-returns-nil* when we invoke ; the raw code. This also infects ev and the other functions in the ; ev-fncall clique, namely ev-lst and ev-acl2-unwind-protect. It is ; up to the user of ev-fncall to specify which behavior is desired. ; Generally speaking, that argument of ev-fncall is set to t in those ; calls of ev-fncall that are from within the theorem prover and on ; terms from the conjecture being proved. Secondly, (up to ; Version_2.5) in oneify-cltl-code and oneify-cltl-code, when we ; generated the ACL2_*1*_ACL2 code for a function, we laid down a ; binding for *hard-error-returns-nil*. That binding is in effect ; just when we evaluate the guard of the function. The binding is t ; if either it was already (meaning somebody above us has asked for ; hard-error to be treated this way) or if guard checking is turned ; off. ; See the comment after ILLEGAL (below) for a discussion of an ; earlier, inadequate handling of these issues. ":Doc-Section ACL2::ACL2-built-ins print an error message and stop execution~/ ~c[(Hard-error ctx str alist)] causes evaluation to halt with a short message using the ``context'' ~c[ctx]. An error message is first printed using the string ~c[str] and alist ~c[alist] that are of the same kind as expected by ~ilc[fmt]. ~l[fmt]. Also ~pl[er] for a macro that provides a unified way of signaling errors.~/ ~c[Hard-error] has a guard of ~c[t]. Also ~pl[illegal] for a similar capability which however has a guard of ~c[nil] that supports static checking using ~ilc[guard] verification, rather than using dynamic (run-time) checking. This distinction is illustrated elsewhere: ~pl[prog2$] for examples. Semantically, ~c[hard-error] ignores its arguments and always returns ~c[nil]. But if a call ~c[(hard-error ctx str alist)] is encountered during evaluation, then the string ~c[str] is printed using the association list ~c[alist] (as in ~ilc[fmt]), after which evaluation halts immediately. Here is a trivial, contrived example. ~bv[] ACL2 !>(cons 3 (hard-error 'my-context \"Printing 4: ~~n0\" (list (cons #\\0 4)))) HARD ACL2 ERROR in MY-CONTEXT: Printing 4: four ACL2 Error in TOP-LEVEL: Evaluation aborted. ACL2 !> ~ev[]~/" (declare (xargs :guard t)) #-acl2-loop-only (cond ((not *hard-error-returns-nilp*) ; We are going to ``cause an error.'' We print an error message with error-fms ; even though we do not have state. To do that, we must bind *wormholep* to ; nil so we don't try to push undo information (or, in the case of error-fms, ; cause an error for illegal state changes). If error-fms could evaluate arbitrary ; forms, e.g., to make legal state changes while in wormholes, then this would be ; a BAD IDEA. But error-fms only prints stuff that was created earlier (and passed ; in via alist). (cond ((fboundp 'acl2::error-fms) ;;; Print a msg (let ((*standard-output* *error-output*) ;;; one way ... (*wormholep* nil) (fn 'acl2::error-fms)) (funcall fn t ctx str alist *the-live-state*))) (t (print (list ctx str alist) *error-output*))) ;;; or another. ; Once upon a time hard-error took a throw-flg argument and did the ; following throw-raw-ev-fncall only if the throw-flg was t. Otherwise, ; it signalled an interface-er. Note that in either case it behaved like ; an error -- interface-er's are rougher because they do not leave you in ; the ACL2 command loop. I think this aspect of the old code was a vestige ; of the pre-*ld-level* days when we didn't know if we could throw or not. (throw-raw-ev-fncall 'illegal))) #+acl2-loop-only (declare (ignore ctx str alist)) nil) (defun illegal (ctx str alist) ":Doc-Section ACL2::ACL2-built-ins print an error message and stop execution~/ ~c[(Illegal ctx str alist)] causes evaluation to halt with a short message using the ``context'' ~c[ctx]. An error message is first printed using the string ~c[str] and alist ~c[alist] that are of the same kind as expected by ~ilc[fmt]. ~l[fmt], and ~pl[prog2$] for an example of how to use a related function, ~ilc[hard-error] (~pl[hard-error]). Also ~pl[er] for a macro that provides a unified way of signaling errors.~/ The difference between ~c[illegal] and ~ilc[hard-error] is that the former has a guard of ~c[nil] while the latter has a ~ilc[guard] of ~c[t]. Thus, you may want to use ~c[illegal] rather than ~c[hard-error] when you intend to do ~ilc[guard] verification at some point, and you expect the guard to guarantee that the ~c[illegal] call is never executed. ~l[prog2$] for an example.~/" ; We would like to use this function in :common-lisp-compliant function ; definitions, but prove that it's never called. Thus we have to make this ; function :common-lisp-compliant, and its guard is then nil. (declare (xargs :guard (hard-error ctx str alist))) (hard-error ctx str alist)) ; Note on Inadequate Handling of Illegal. ; Once upon a time (pre-Version 2.4) we had hard-error take an additional ; argument and the programmer used that argument to indicate whether the ; function was to cause an error or return nil. When hard-error was used ; in the :guard of ILLEGAL it was called so as not to cause an error (if ; guard checking was off) and when it was called in the body of ILLEGAL it ; was programmed to cause an error. However, the Rockwell folks, using ; LETs in support of stobjs, discovered that we caused hard errors on ; some guard verifications. Here is a simple example distilled from theirs: ; (defun foo (i) ; (declare (xargs :guard (integerp i))) ; (+ 1 ; (car ; (let ((j i)) ; (declare (type integer j)) ; (cons j nil))))) ; This function caused a hard error during guard verification. The ; troublesome guard conjecture is: ; (IMPLIES ; (INTEGERP I) ; (ACL2-NUMBERP ; (CAR (LET ((J I)) ; (PROG2$ (IF (INTEGERP J) ; T ; (ILLEGAL 'VERIFY-GUARDS ; "Some TYPE declaration is violated." ; NIL)) ; (LIST J)))))) ; The problem was that we eval'd the ILLEGAL during the course of trying ; to prove this. A similar challenge is the above mentioned ; (thm (equal (car (cons (hard-error 'ctx "Test" nil) y)) nil)) ; We leave this note simply in case the current handling of ; hard errors is found still to be inadequate. #+acl2-loop-only (defmacro intern (x y) (declare (xargs :guard (member-equal y (cons *main-lisp-package-name* '("ACL2" *main-lisp-package-name* "ACL2-INPUT-CHANNEL" "ACL2-OUTPUT-CHANNEL" "KEYWORD"))))) ":Doc-Section ACL2::ACL2-built-ins create a new symbol in a given package~/ ~c[(intern symbol-name symbol-package-name)] returns a symbol with the given ~ilc[symbol-name] and the given ~ilc[symbol-package-name]. We restrict Common Lisp's ~c[intern] so that the second argument is either the symbol *main-lisp-package-name*, the value of that constant, or is one of \"ACL2\", \"ACL2-INPUT-CHANNEL\", \"ACL2-OUTPUT-CHANNEL\", or \"KEYWORD\". To avoid that restriction, ~pl[intern$].~/ In ACL2 ~c[intern] is actually implemented as a macro that expands to a call of a similar function whose second argument is a symbol. Invoke ~c[:pe intern] to see the definition, or ~pl[intern-in-package-of-symbol]. To see why is ~c[intern] so restricted consider ~c[(intern \"X\" \"P\")]. In particular, is it a symbol and if so, what is its ~ilc[symbol-package-name]? One is tempted to say ``yes, it is a symbol in the package ~c[\"P\"].'' But if package ~c[\"P\"] has not yet been defined, that would be premature because the imports to the package are unknown. For example, if ~c[\"P\"] were introduced with ~bv[] (defpkg \"P\" '(LISP::X)) ~ev[] then in Common Lisp ~c[(symbol-package-name (intern \"X\" \"P\"))] returns ~C[\"LISP\"]. The obvious restriction on ~c[intern] is that its second argument be the name of a package known to ACL2. We cannot express such a restriction (except, for example, by limiting it to those packages known at some fixed time, as we do). Instead, we provide ~ilc[intern-in-package-of-symbol] which requires a ``witness symbol'' for the package instead of the package. The witness symbol is any symbol (expressible in ACL2) and uniquely specifies a package necessarily known to ACL2." (list 'intern-in-package-of-symbol x (cond ((equal y "ACL2") ''rewrite) ((equal y "ACL2-INPUT-CHANNEL") ''acl2-input-channel::a-random-symbol-for-intern) ((equal y "ACL2-OUTPUT-CHANNEL") ''acl2-output-channel::a-random-symbol-for-intern) ((equal y "KEYWORD") ':a-random-symbol-for-intern) ((or (equal y *main-lisp-package-name*) (eq y '*main-lisp-package-name*)) ''car) (t (illegal 'intern "The guard for INTERN is out of sync with its ~ definition.~%Consider adding a case for a second ~ argument of ~x0." (list (cons #\0 y))))))) (defmacro intern$ (x y) ":Doc-Section ACL2::ACL2-built-ins create a new symbol in a given package~/ ~c[Intern$] is a macro that behaves the same as the macro ~ilc[intern], except for weakening the restriction to a fixed set of package names so that any package name other than ~c[\"\"] is legal. ~l[intern]. Note that if you evaluate a call ~c[(intern$ x y)] for which there is no package with name ~c[y] that is known to ACL2, you will get an error.~/ ~c[(Intern$ x y)] expands to: ~bv[] (intern-in-package-of-symbol x (pkg-witness y)) ~ev[] ~l[intern-in-package-of-symbol] and ~pl[pkg-witness].~/" `(intern-in-package-of-symbol ,x (pkg-witness ,y))) #+acl2-loop-only (defun keywordp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for keywords~/ ~c[(Keywordp x)] is true if and only if ~c[x] is a keyword, i.e., a symbol in the \"KEYWORD\" package. Such symbols are typically printed using a colon (:) followed by the ~ilc[symbol-name] of the symbol.~/ ~c[Keywordp] has a ~il[guard] of ~c[t]. ~c[Keywordp] is a Common Lisp function. See any Common Lisp documentation for more information. The following log may be illuminating. ~bv[] ACL2 !>(intern \"ABC\" \"KEYWORD\") :ABC ACL2 !>(symbol-name ':ABC) \"ABC\" ACL2 !>(symbol-package-name ':ABC) \"KEYWORD\" ACL2 !> ~ev[] To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (and (symbolp x) (equal (symbol-package-name x) "KEYWORD"))) (defthm keywordp-forward-to-symbolp (implies (keywordp x) (symbolp x)) :rule-classes :forward-chaining) (defaxiom intern-in-package-of-symbol-symbol-name ; This axiom assumes that "" is not the name of any package, but is instead ; used as a default value when symbol-package-name is applied to a non-symbol. ; So, the hypotheses below imply (symbolp y). See also the lemma ; symbol-package-name-of-symbol-is-not-empty-string, below. See also ; chk-acceptable-defpkg for a related comment, in which a proof of nil is shown ; using this axiom when "" is not disallowed as a package name. (implies (and (symbolp x) (equal (symbol-package-name x) (symbol-package-name y))) (equal (intern-in-package-of-symbol (symbol-name x) y) x))) (defthm symbol-package-name-of-symbol-is-not-empty-string ; This rule became necessary for the proof of lemma nice->simple-inverse in ; community book books/workshops/2003/sumners/support/n2n.lisp, after axiom ; symbol-package-name-pkg-witness-name (below) was modified after Version_3.0.1 ; by adding the condition (not (equal pkg-name "")). We make it a ; :forward-chaining rule in order to avoid hanging a rewrite rule on 'equal. (implies (symbolp x) (not (equal (symbol-package-name x) ""))) :hints (("Goal" :use ((:instance intern-in-package-of-symbol-symbol-name (x x) (y 3))) :in-theory (disable intern-in-package-of-symbol-symbol-name))) :rule-classes ((:forward-chaining :trigger-terms ((symbol-package-name x))))) (defconst *pkg-witness-name* "ACL2-PKG-WITNESS") (defaxiom symbol-name-pkg-witness (equal (symbol-name (pkg-witness pkg-name)) *pkg-witness-name*)) (defaxiom symbol-package-name-pkg-witness-name (equal (symbol-package-name (pkg-witness pkg-name)) (if (and (stringp pkg-name) (not (equal pkg-name ""))) pkg-name ; See the comment in intern-in-package-of-symbol-symbol-name for why we do not ; use "" below. We avoid questions about names of built-in Lisp and keyword ; packages by using our own package name. "ACL2"))) ; Member-symbol-name is used in defpkg axioms. We keep it disabled in order to ; avoid stack overflows, for example in the proof of theorem ; symbol-listp-raw-acl2-exports in file community book ; books/misc/check-acl2-exports.lisp. (defun member-symbol-name (str l) (declare (xargs :guard (symbol-listp l))) (cond ((endp l) nil) ((equal str (symbol-name (car l))) l) (t (member-symbol-name str (cdr l))))) ; Defund is not yet available here: (in-theory (disable member-symbol-name)) (defthm symbol-equality ; This formula is provable using intern-in-package-of-symbol-symbol-name. (implies (and (symbolp s1) (symbolp s2) (equal (symbol-name s1) (symbol-name s2)) (equal (symbol-package-name s1) (symbol-package-name s2))) (equal s1 s2)) :rule-classes nil :hints (("Goal" :in-theory (disable intern-in-package-of-symbol-symbol-name) :use ((:instance intern-in-package-of-symbol-symbol-name (x s1) (y s2)) (:instance intern-in-package-of-symbol-symbol-name (x s2) (y s2)))))) (defaxiom symbol-name-intern-in-package-of-symbol (implies (and (stringp s) (symbolp any-symbol)) (equal (symbol-name (intern-in-package-of-symbol s any-symbol)) s))) (defaxiom symbol-package-name-intern-in-package-of-symbol (implies (and (stringp x) (symbolp y) (not (member-symbol-name x (pkg-imports (symbol-package-name y))))) (equal (symbol-package-name (intern-in-package-of-symbol x y)) (symbol-package-name y)))) (defaxiom intern-in-package-of-symbol-is-identity (implies (and (stringp x) (symbolp y) (member-symbol-name x (pkg-imports (symbol-package-name y)))) (equal (intern-in-package-of-symbol x y) (car (member-symbol-name x (pkg-imports (symbol-package-name y))))))) (defaxiom symbol-listp-pkg-imports (symbol-listp (pkg-imports pkg)) :rule-classes ((:forward-chaining :trigger-terms ((pkg-imports pkg))))) (defaxiom no-duplicatesp-eq-pkg-imports (no-duplicatesp-eq (pkg-imports pkg)) :rule-classes :rewrite) (defaxiom completion-of-pkg-imports (equal (pkg-imports x) (if (stringp x) (pkg-imports x) nil)) :rule-classes nil) ; These axioms are just the ones that would be added by defpkg had the packages ; in question been introduced that way. ; Warning: If the forms of these axioms are changed, you should ; probably visit the same change to the rules added by defpkg. (defaxiom acl2-input-channel-package (equal (pkg-imports "ACL2-INPUT-CHANNEL") nil)) (defaxiom acl2-output-channel-package (equal (pkg-imports "ACL2-OUTPUT-CHANNEL") nil)) (defaxiom acl2-package (equal (pkg-imports "ACL2") *common-lisp-symbols-from-main-lisp-package*)) (defaxiom keyword-package (equal (pkg-imports "KEYWORD") nil)) ; The following two axioms are probably silly. But at least they may provide ; steps towards building up the ACL2 objects constructively from a few ; primitives. (defaxiom string-is-not-circular (equal 'string (intern-in-package-of-symbol (coerce (cons #\S (cons #\T (cons #\R (cons #\I (cons #\N (cons #\G 0)))))) (cons #\S (cons #\T (cons #\R (cons #\I (cons #\N (cons #\G 0))))))) (intern-in-package-of-symbol 0 0))) :rule-classes nil) (defaxiom nil-is-not-circular (equal nil (intern-in-package-of-symbol (coerce (cons #\N (cons #\I (cons #\L 0))) 'string) 'string)) :rule-classes nil) ; Essay on Symbols and Packages ; A symbol may be viewed as a pair consisting of two strings: its symbol-name ; and its symbol-package-name, where the symbol-package-name is not "". (A ; comment in intern-in-package-of-symbol-symbol-name discusses why we disallow ; "".) However, some such pairs are not symbols because of the import ; structure (represented in world global 'known-package-alist). For example, ; the "ACL2" package imports a symbol with symbol-name "CAR" from the ; "COMMON-LISP" package, so the symbol-package-name of ACL2::CAR is ; "COMMON-LISP". Thus there is no symbol with a symbol-name of "CAR" and a ; symbol-package-name of "ACL2". ; The package system has one additional requirement: No package is allowed to ; import any symbol named *pkg-witness-name* from any other package. The ; function pkg-witness returns a symbol with that name; moreover, the ; symbol-package-name of (pkg-witness p) is p if p is a string other than "", ; else is "ACL2". ; Logically, we imagine that a package exists for every string (serving as the ; symbol-package-name of its symbols) except "". Of course, at any given time ; only finite many packages have been specified (either being built-in, or ; specified with defpkg); and, ACL2 will prohibit explicit specification of ; packages for certain strings, such as "ACL2_INVISIBLE". ; Finally, we specify that the symbol-name and symbol-package-name of any ; non-symbol are "". #-acl2-loop-only (defun-one-output intern-in-package-of-symbol (str sym) ; In general we require that intern be given an explicit string constant ; that names a package known at translate time. This avoids the run-time ; check that the package is known -- which would require passing state down ; to intern everywhere. However, we would like a more general intern ; mechanism and hence define the following, which is admitted by special ; decree in translate. The beauty of this use of intern is that the user ; supplies a symbol which establishes the existence of the desired package. (declare (type string str) (type symbol sym)) (let* ((mark (get sym *initial-lisp-symbol-mark*)) (pkg (if mark *main-lisp-package* (symbol-package sym)))) (multiple-value-bind (ans status) (intern str pkg) (declare (ignore status)) ; We next guarantee that if sym is an ACL2 object then so is ans. We assume ; that every import of a symbol into a package known to ACL2 is via defpkg, ; except perhaps for imports into the "COMMON-LISP" package. So unless sym ; resides in the "COMMON-LISP" package (whether natively or not), the ; symbol-package of sym is one of those known to ACL2. Thus, the only case of ; concern is the case that sym resides in the "COMMON-LISP" package. Since sym ; is an ACL2 object, then by the Invariant on Symbols in the Common Lisp ; Package (see bad-lisp-objectp), its symbol-package is *main-lisp-package* or ; else its *initial-lisp-symbol-mark* property is "COMMON-LISP". So we set the ; *initial-lisp-symbol-mark* for ans in each of these sub-cases, which ; preserves the above invariant. (when (and (eq pkg *main-lisp-package*) (not (get ans *initial-lisp-symbol-mark*))) (setf (get ans *initial-lisp-symbol-mark*) *main-lisp-package-name-raw*)) ans))) (defdoc pkg-imports ":Doc-Section ACL2::ACL2-built-ins list of symbols imported into a given package~/ Completion Axiom (~c[completion-of-pkg-imports]): ~bv[] (equal (pkg-imports x) (if (stringp x) (pkg-imports x) nil)) ~ev[]~/ ~il[Guard] for ~c[(pkg-imports x)]: ~bv[] (stringp x) ~ev[] ~c[(Pkg-imports pkg)] returns a duplicate-free list of all symbols imported into ~c[pkg], which should be the name of a package known to ACL2. For example, suppose ~c[\"MY-PKG\"] was created by ~bv[] (defpkg \"MY-PKG\" '(ACL2::ABC LISP::CAR)). ~ev[] Then ~c[(pkg-imports \"MY-PKG\")] equals the list ~c[(ACL2::ABC LISP::CAR)]. If ~c[pkg] is not a string, then ~c[(pkg-imports pkg)] is ~c[nil]. If ~c[pkg] is a string but not the name of a package known to ACL2, then the value of the form ~c[(pkg-imports pkg)] is unspecified, and it evaluation will fail to yield a value. By ``the symbols imported into ~c[pkg]'' we mean the symbols imported into ~c[pkg] by the ~ilc[defpkg] event that introduced ~c[pkg]. There are no imports for built-in packages except for the ~c[\"ACL2\"] package, which imports the symbols in the list value of the constant ~c[*common-lisp-symbols-from-main-lisp-package*]. In particular, this is the case for the ~c[\"COMMON-LISP\"] package. Users familiar with Common Lisp may find this surprising, since in actual Common Lisp implementations it is often the case that many symbols in that package are imported from other packages. However, ACL2 treats all symbols in the constant ~c[*common-lisp-symbols-from-main-lisp-package*] as having a ~ilc[symbol-package-name] of ~c[\"COMMON-LISP\"], as though they were not imported. ACL2 admits a symbol imported into in the ~c[\"COMMON-LISP\"] package only if it belongs to that list: any attempt to read any other symbol imported into the ~c[\"COMMON-LISP\"] package, or to produce such a symbol with ~ilc[intern$] or ~ilc[intern-in-package-of-symbol], will cause an error. The following axioms formalize properties of ~c[pkg-imports] discussed above (use ~c[:]~ilc[pe] to view them). ~bv[] symbol-package-name-intern-in-package-of-symbol intern-in-package-of-symbol-is-identity symbol-listp-pkg-imports no-duplicatesp-pkg-imports completion-of-pkg-imports ~ev[]") #-acl2-loop-only (defun-one-output pkg-imports (pkg) (declare (type string pkg)) (let ((entry (find-non-hidden-package-entry pkg (known-package-alist *the-live-state*)))) (cond (entry (package-entry-imports entry)) (t (throw-raw-ev-fncall (list 'pkg-imports-er pkg)))))) (defdoc pkg-witness ":Doc-Section ACL2::ACL2-built-ins return a specific symbol in the indicated package~/ For any string ~c[pkg] that names a package currently known to ACL2, ~c[(pkg-witness pkg)] is a symbol in that package whose ~ilc[symbol-name] is the value of constant ~c[*pkg-witness-name*]. Logically, this is the case even if the package is not currently known to ACL2. However, if ~c[pkg-witness] is called on a string that is not the name of a package known to ACL2, a hard Lisp error will result.~/ ~c[(Pkg-witness pkg)] has a guard of ~c[(and (stringp pkg) (not (equal pkg \"\")))]. If ~c[pkg] is not a string, then ~c[(pkg-witness pkg)] is equal to ~c[(pkg-witness \"ACL2\")]~/") #-acl2-loop-only (defun-one-output pkg-witness (pkg) (declare (type string pkg)) (cond ((find-non-hidden-package-entry pkg (known-package-alist *the-live-state*)) (let ((ans (intern *pkg-witness-name* pkg))) ; See comment in intern-in-package-of-symbol for an explanation of this trick. ans)) (t ; We use error rather than illegal, because we want to throw an error even when ; *hard-error-returns-nilp* is true. (error "The argument supplied to PKG-WITNESS, ~s, is not the name of ~ a package currently known to ACL2." pkg)))) ; UTILITIES - definitions of the rest of applicative Common Lisp. (defun binary-append (x y) ":Doc-Section ACL2::ACL2-built-ins ~il[concatenate] two lists~/ This binary function implements ~ilc[append], which is a macro in ACL2. ~l[append]~/ The ~il[guard] for ~c[binary-append] requires the first argument to be a ~ilc[true-listp]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (true-listp x))) (cond ((endp x) y) (t (cons (car x) (binary-append (cdr x) y))))) #+acl2-loop-only (defmacro append (&rest rst) ":Doc-Section ACL2::ACL2-built-ins ~il[concatenate] zero or more lists~/ ~c[Append], which takes zero or more arguments, expects all the arguments except perhaps the last to be true (null-terminated) lists. It returns the result of concatenating all the elements of all the given lists into a single list. Actually, in ACL2 ~c[append] is a macro that expands into calls of the binary function ~ilc[binary-append] if there are at least two arguments; if there is just one argument then the expansion is that argument; and finally, ~c[(append)] expands to ~c[nil].~/ ~c[Append] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (cond ((null rst) nil) ((null (cdr rst)) (car rst)) (t (xxxjoin 'binary-append rst)))) (defthm true-listp-append ; This rule has the effect of making the system automatically realize that (rev ; x) is a true-list, for example, where: ; (defun rev (x) ; (if (endp x) ; nil ; (append (rev (cdr x)) ; (list (car x))))) ; That in turn means that when it generalizes (rev x) to z it adds (true-listp ; z). ; That in turn means it can prove ; (defthm rev-append ; (equal (rev (append a b)) ; (append (rev b) (rev a)))) ; ; automatically, doing several generalizations and inductions. (implies (true-listp b) (true-listp (append a b))) :rule-classes :type-prescription) ; The following lemma originally appeared to be useful for accepting the ; definition of make-input-channel. Then it became useful for accepting the ; definition of string-append, though that's changed a bit. (defthm standard-char-listp-append (implies (true-listp x) (equal (standard-char-listp (append x y)) (and (standard-char-listp x) (standard-char-listp y)))) :hints (("Goal" :in-theory (enable standard-char-listp)))) (defthm character-listp-append (implies (true-listp x) (equal (character-listp (append x y)) (and (character-listp x) (character-listp y))))) (defthm append-to-nil (implies (true-listp x) (equal (append x nil) x))) #+acl2-loop-only (defmacro concatenate (result-type &rest sequences) ":Doc-Section ACL2::ACL2-built-ins concatenate lists or strings together~/ ~bv[] Examples: (concatenate 'string \"ab\" \"cd\" \"ef\") ; equals \"abcdef\" (concatenate 'string \"ab\") ; equals \"ab\" (concatenate 'list '(a b) '(c d) '(e f)) ; equals '(a b c d e f) (concatenate 'list) ; equals nil~/ General Form: (concatenate result-type x1 x2 ... xn) ~ev[] where ~c[n >= 0] and either: ~c[result-type] is ~c[']~ilc[string] and each ~c[xi] is a string; or ~c[result-type] is ~c[']~ilc[list] and each ~c[xi] is a true list. ~c[Concatenate] simply concatenates its arguments to form the result string or list. Also ~pl[append] and ~pl[string-append]. (The latter immediately generates a call to ~c[concatenate] when applied to strings.) Note: We do *not* try to comply with the Lisp language's insistence that ~c[concatenate] copies its arguments. Not only are we in an applicative setting, where this issue shouldn't matter for the logic, but also we do not actually modify the underlying lisp implementation of ~c[concatenate]; we merely provide a definition for it. ~c[Concatenate] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (member-equal result-type '('string 'list)))) (cond ((equal result-type ''string) (cond ((and sequences (cdr sequences) (null (cddr sequences))) ; Here we optimize for a common case, but more importantly, we avoid expanding ; to a call of string-append-lst for the call of concatenate in the definition ; of string-append. (list 'string-append (car sequences) (cadr sequences))) (t (list 'string-append-lst (cons 'list sequences))))) ((endp sequences) nil) (t ; Consider the call (concatenate 'list .... '(a . b)). At one time we tested ; for (endp (cdr sequences)) here, returning (car sequences) in that case. And ; otherwise, we returned (cons 'append sequences). However, these are both ; errors, because the last member of sequences might be a non-true-listp, in ; which case append signals no guard violation but Common Lisp breaks. (cons 'append (append sequences (list nil)))))) (defun string-append (str1 str2) ":Doc-Section ACL2::ACL2-built-ins ~il[concatenate] two strings~/ ~c[String-append] takes two arguments, which are both strings (if the ~il[guard] is to be met), and returns a string obtained by concatenating together the ~il[characters] in the first string followed by those in the second. Also ~pl[concatenate], noting that the macro call ~bv[] (concatenate 'string str1 str2). ~ev[] expands to the call ~bv[] (string-append str1 str2). ~ev[] To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard (and (stringp str1) (stringp str2)))) (mbe :logic (coerce (append (coerce str1 'list) (coerce str2 'list)) 'string) :exec ; This code may seem circular, since string-append calls the concatenate macro, ; which expands here into a call of string-append. However, the :exec case is ; only called if we are executing the raw Lisp code for string-append, in which ; case we will be executing the raw Lisp code for concatenate, which of course ; does not call the ACL2 function string-append. (concatenate 'string str1 str2))) (defun string-listp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of strings~/ The predicate ~c[string-listp] tests whether its argument is a ~ilc[true-listp] of strings. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (stringp (car x)) (string-listp (cdr x)))))) (defun string-append-lst (x) (declare (xargs :guard (string-listp x))) (cond ((endp x) "") (t (string-append (car x) (string-append-lst (cdr x)))))) ; We make 1+ and 1- macros in order to head off the potentially common error of ; using these as nonrecursive functions on left-hand sides of rewrite rules. #+acl2-loop-only (defmacro 1+ (x) ":Doc-Section ACL2::ACL2-built-ins increment by 1~/ ~c[(1+ x)] is the same as ~c[(+ 1 x)]. ~l[+].~/ ~c[1+] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (list '+ 1 x)) #+acl2-loop-only (defmacro 1- (x) ":Doc-Section ACL2::ACL2-built-ins decrement by 1~/ ~c[(1- x)] is the same as ~c[(- x 1)]. ~l[-].~/ ~c[1-] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (list '- x 1)) ; Remove (defun remove-eq-exec (x l) (declare (xargs :guard (if (symbolp x) (true-listp l) (symbol-listp l)))) (cond ((endp l) nil) ((eq x (car l)) (remove-eq-exec x (cdr l))) (t (cons (car l) (remove-eq-exec x (cdr l)))))) (defun remove-eql-exec (x l) (declare (xargs :guard (if (eqlablep x) (true-listp l) (eqlable-listp l)))) (cond ((endp l) nil) ((eql x (car l)) (remove-eql-exec x (cdr l))) (t (cons (car l) (remove-eql-exec x (cdr l)))))) (defun remove-equal (x l) (declare (xargs :guard (true-listp l))) #-acl2-loop-only ; for assoc-eq, Jared Davis found native assoc efficient (remove x l :test #'equal) #+acl2-loop-only (cond ((endp l) nil) ((equal x (car l)) (remove-equal x (cdr l))) (t (cons (car l) (remove-equal x (cdr l)))))) (defmacro remove-eq (x lst) `(remove ,x ,lst :test 'eq)) (defthm remove-eq-exec-is-remove-equal (equal (remove-eq-exec x l) (remove-equal x l))) (defthm remove-eql-exec-is-remove-equal (equal (remove-eql-exec x l) (remove-equal x l))) #+acl2-loop-only (defmacro remove (x l &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins remove all occurrences~/ ~bv[] General Forms: (remove x lst) (remove x lst :test 'eql) ; same as above (eql as equality test) (remove x lst :test 'eq) ; same, but eq is equality test (remove x lst :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Remove x lst)] is equal to ~c[lst] if ~c[x] is not a member of ~c[lst], else is the result of removing all occurrences of ~c[x] from ~c[lst]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with successive elements of ~c[lst]. Also ~pl[remove1].~/ The ~il[guard] for a call of ~c[remove] depends on the test. In all cases, the second argument must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[remove] and its variants: ~bq[] ~c[(remove-eq x lst)] is equivalent to ~c[(remove x lst :test 'eq)]; ~c[(remove-equal x lst)] is equivalent to ~c[(remove x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[remove-equal]. ~c[Remove] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (l ,l)) :logic (remove-equal x l) :exec (remove-eq-exec x l))) ((equal test ''eql) `(let-mbe ((x ,x) (l ,l)) :logic (remove-equal x l) :exec (remove-eql-exec x l))) (t ; (equal test 'equal) `(remove-equal ,x ,l)))) ; Remove1 (defun remove1-eq-exec (x l) (declare (xargs :guard (if (symbolp x) (true-listp l) (symbol-listp l)))) (cond ((endp l) nil) ((eq x (car l)) (cdr l)) (t (cons (car l) (remove1-eq-exec x (cdr l)))))) (defun remove1-eql-exec (x l) (declare (xargs :guard (if (eqlablep x) (true-listp l) (eqlable-listp l)))) (cond ((endp l) nil) ((eql x (car l)) (cdr l)) (t (cons (car l) (remove1-eql-exec x (cdr l)))))) (defun remove1-equal (x l) (declare (xargs :guard (true-listp l))) (cond ((endp l) nil) ((equal x (car l)) (cdr l)) (t (cons (car l) (remove1-equal x (cdr l)))))) (defmacro remove1-eq (x lst) `(remove1 ,x ,lst :test 'eq)) (defthm remove1-eq-exec-is-remove1-equal (equal (remove1-eq-exec x l) (remove1-equal x l))) (defthm remove1-eql-exec-is-remove1-equal (equal (remove1-eql-exec x l) (remove1-equal x l))) (defmacro remove1 (x l &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins remove first occurrences, testing using ~ilc[eql]~/ ~bv[] General Forms: (remove1 x lst) (remove1 x lst :test 'eql) ; same as above (eql as equality test) (remove1 x lst :test 'eq) ; same, but eq is equality test (remove1 x lst :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Remove1 x lst)] is equal to ~c[lst] if ~c[x] is not a member of ~c[lst], else is the result of removing the first occurrences of ~c[x] from ~c[lst]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with successive elements of ~c[lst]. Also ~pl[remove].~/ The ~il[guard] for a call of ~c[remove1] depends on the test. In all cases, the second argument must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[remove1] and its variants: ~bq[] ~c[(remove1-eq x lst)] is equivalent to ~c[(remove1 x lst :test 'eq)]; ~c[(remove1-equal x lst)] is equivalent to ~c[(remove1 x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[remove1-equal].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (l ,l)) :logic (remove1-equal x l) :exec (remove1-eq-exec x l))) ((equal test ''eql) `(let-mbe ((x ,x) (l ,l)) :logic (remove1-equal x l) :exec (remove1-eql-exec x l))) (t ; (equal test 'equal) `(remove1-equal ,x ,l)))) (deflabel pairlis :doc ":Doc-Section ACL2::ACL2-built-ins ~l[pairlis$]~/ The Common Lisp language allows its ~c[pairlis] function to construct an alist in any order! So we have to define our own version: ~l[pairlis$].~/~/") (defun pairlis$ (x y) ; CLTL allows its pairlis to construct an alist in any order! So we ; have to give this function a different name. ":Doc-Section ACL2::ACL2-built-ins zipper together two lists~/ The Common Lisp language allows its ~ilc[pairlis] function to construct an alist in any order! So we have to define our own version, ~c[pairlis$]. It returns the list of pairs obtained by ~ilc[cons]ing together successive respective members of the given lists until the first list runs out. (Hence in particular, if the second argument is ~c[nil] then each element of the first argument is paired with ~c[nil].)~/ The ~il[guard] for ~c[pairlis$] requires that its arguments are true lists. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (true-listp x) (true-listp y)))) (cond ((endp x) nil) (t (cons (cons (car x) (car y)) (pairlis$ (cdr x) (cdr y)))))) ; Remove-duplicates (defun remove-duplicates-eq-exec (l) (declare (xargs :guard (symbol-listp l))) (cond ((endp l) nil) ((member-eq (car l) (cdr l)) (remove-duplicates-eq-exec (cdr l))) (t (cons (car l) (remove-duplicates-eq-exec (cdr l)))))) (defun remove-duplicates-eql-exec (l) (declare (xargs :guard (eqlable-listp l))) (cond ((endp l) nil) ((member (car l) (cdr l)) (remove-duplicates-eql-exec (cdr l))) (t (cons (car l) (remove-duplicates-eql-exec (cdr l)))))) (defun remove-duplicates-equal (l) (declare (xargs :guard (true-listp l))) (cond ((endp l) nil) ((member-equal (car l) (cdr l)) (remove-duplicates-equal (cdr l))) (t (cons (car l) (remove-duplicates-equal (cdr l)))))) (defmacro remove-duplicates-eq (x) `(remove-duplicates ,x :test 'eq)) (defthm remove-duplicates-eq-exec-is-remove-duplicates-equal (equal (remove-duplicates-eq-exec x) (remove-duplicates-equal x))) (defthm remove-duplicates-eql-exec-is-remove-duplicates-equal (equal (remove-duplicates-eql-exec x) (remove-duplicates-equal x))) (defmacro remove-duplicates-logic (x) `(let ((x ,x)) (if (stringp x) (coerce (remove-duplicates-equal (coerce x 'list)) 'string) (remove-duplicates-equal x)))) #+acl2-loop-only (defmacro remove-duplicates (x &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins remove duplicates from a string or a list~/ ~bv[] General Forms: (remove-duplicates x) (remove-duplicates x :test 'eql) ; same as above (eql as equality test) (remove-duplicates x :test 'eq) ; same, but eq is equality test (remove-duplicates x :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Remove-duplicates x)] returns the result of deleting duplicate elements from the beginning of the list or string ~c[x]. For example, ~c[(remove-duplicates '(1 2 3 2 4))] is equal to ~c['(1 3 2 4)]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with successive elements of ~c[lst].~/ The ~il[guard] for a call of ~c[remove-duplicates] depends on the test. In all cases, the argument must satisfy ~ilc[stringp] or ~ilc[true-listp]. If the test is ~ilc[eql], then the argument must satisfy either ~ilc[stringp] or ~ilc[eqlable-listp]. If the test is ~ilc[eq], then the argument must satisfy ~ilc[symbol-listp]. The relation between ~c[remove-duplicates] and its variants is related to the usual pattern for equality variants; ~pl[equality-variants]. However, the possibility of a string argument changes the usual pattern a bit. As one might expect: ~bq[] ~c[(remove-duplicates-eq lst)] is equivalent to ~c[(remove-duplicates lst :test 'eq)]. ~eq[] However, ~c[remove-duplicates-equal] is defined without consideration of strings, for backward compatibility with versions of ACL2 through Version_4.2. The macro ~c[remove-duplicates-logic] has been introduced to model the behavior of ~c[remove-duplicates] even on strings; use ~c[:]~ilc[pe] if you wish to see its definition. So we can say the following. ~bq[] ~c[(remove-duplicates-logic lst)] is equivalent to ~c[(remove-duplicates lst :test 'equal)]; and ~c[(remove-duplicates-logic lst)] is equal to ~c[(remove-duplicates-equal lst)] when ~c[lst] is not a string. ~eq[] In particular, when the argument is not a string, reasoning about any of these primitives reduces to reasoning about the function ~c[remove-duplicates-equal]. ~c[Remove-duplicates] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x)) :logic (remove-duplicates-logic x) :exec (remove-duplicates-eq-exec x))) ((equal test ''eql) `(let-mbe ((x ,x)) :logic (remove-duplicates-logic x) :exec (if (stringp x) (coerce (remove-duplicates-eql-exec (coerce x 'list)) 'string) (remove-duplicates-eql-exec x)))) (t ; (equal test 'equal) `(remove-duplicates-logic ,x)))) (defthm character-listp-remove-duplicates (implies (character-listp x) (character-listp (remove-duplicates x)))) ; We now define the first five documentation sections: Events, ; Documentation, History, Other, and Miscellaneous. These ; are defined here simply so we can use them freely throughout. The ; first four are advertised in :help. (deflabel events :doc ":Doc-Section Events functions that extend the logic~/~/ Any extension of the syntax of ACL2 (i.e., the definition of a new constant or macro), the axioms (i.e., the definition of a function), or the rule database (i.e., the proof of a theorem), constitutes a logical ``event.'' Events change the ACL2 logical world (~pl[world]). Indeed, the only way to change the ACL2 ~il[world] is via the successful evaluation of an event function. Every time the ~il[world] is changed by an event, a landmark is left on the ~il[world] and it is thus possible to identify the ~il[world] ``as of'' the evaluation of a given event. An event may introduce new logical names. Some events introduce no new names (e.g., ~ilc[verify-guards]), some introduce exactly one (e.g., ~ilc[defmacro] and ~ilc[defthm]), and some may introduce many (e.g., ~ilc[encapsulate] ). ACL2 typically completes processing of an event by printing a summary. Unless proofs are skipped (~pl[ld-skip-proofsp]) or summary output is inhibited (~pl[set-inhibit-output-lst]), information about the proof attempt (if any) is printed that includes a list of rules used, a summary of warnings, and the number of ``prover steps'' (if any; ~pl[with-prover-step-limit]). A breakdown of the time used is also printed, which by default is runtime (cpu time), but can be changed to realtime (wall clock time); ~pl[get-internal-time]. ~l[embedded-event-form] for a discussion of events permitted in ~il[books].~/") (deflabel documentation :doc ":Doc-Section Documentation functions that display documentation~/ This section explains the ACL2 online documentation system. Thus, most of it assumes that you are typing at the terminal, inside an ACL2 session. If you are reading this description in another setting (for example, in a web browser, in Emacs info, or on paper), simply ignore the parts of this description that involve typing at the terminal. ACL2 users are welcome to contribute additional documentation. See the web page ~url[http://www.cs.utexas.edu/users/moore/acl2/contrib/]. For an introduction to the ACL2 online documentation system, type ~c[:]~ilc[more] below. Whenever the documentation system concludes with ``(type :more for more, :more! for the rest)'' you may type ~c[:]~ilc[more] to see the next block of documentation. Topics related to documentation are documented individually:~/ To view the documentation in a web browser, open a browser to file ~c[doc/HTML/acl2-doc.html] under your ACL2 source directory, or just go to the ACL2 home page at ~url[http://www.cs.utexas.edu/users/moore/acl2/]. Alternatively, follow a link on the ACL2 home page to a manual, known as the xdoc manual, which incorporates (but rearranges) the ACL2 documentation as well as documentation from many ACL2 community books. You can build a local copy of that manual; see for example the section ``BUILDING THE XDOC MANUAL'' in the community books ~c[Makefile] for instructions. To use Emacs Info (inside Emacs), first load distributed file ~c[emacs/emacs-acl2.el] (perhaps inside your ~c[.emacs] file) and then execute ~c[meta-x acl2-info]. In order to see true links to external web pages, you may find the following addition to your ~c[.emacs] file to be helpful. ~bv[] ; For emacs-version 22 or (presumably) later, you can probably set ; arrange that in Emacs Info, URLs become links, in the sense that ; if you hit ~c[] while standing on a URL, then you will be ; taken to that location in a web browser. If this does not happen ; automatically, then evaluating the `setq' form below might work ; if you have firefox. If that does not work, then you can probably ; figure out what to do as follows. First type ; control-h v browse-url-browser-function ; and then from the resulting help page, ; hit on the link ``customize'' in: ; ``You can customize this variable'' ; and then follow instructions. (setq browse-url-browser-function (quote browse-url-firefox)) ~ev[] There is a print version of the documentation, though we recommend using one of the other methods (web, Emacs Info, or online) to browse it. If you really want the print version, you can find it here: ~url[http://www.cs.utexas.edu/users/moore/publications/acl2-book.ps.gz]. Below we focus on how to access the online documentation, but some of the discussion is relevant to other formats. The ACL2 online documentation feature allows you to see extensive documentation on many ACL2 functions and ideas. You may use the documentation facilities to document your own ACL2 functions and theorems. If there is some name you wish to know more about, then type ~bv[] ACL2 !>:doc name ~ev[] in the top-level loop. If the name is documented, a brief blurb will be printed. If the name is not documented, but is ``similar'' to some documented names, they will be listed. Otherwise, ~c[nil] is returned. Every name that is documented contains a one-line description, a few notes, and some details. ~c[:]~ilc[Doc] will print the one-liner and the notes. When ~c[:]~ilc[doc] has finished it stops with the message ``(type :more for more, :more! for the rest)'' to remind you that details are available. If you then type ~bv[] ACL2 !>:more ~ev[] a block of the continued text will be printed, again concluding with ``(type :more for more, :more! for the rest)'' if the text continues further, or concluding with ``~c[*-]'' if the text has been exhausted. By continuing to type ~c[:]~ilc[more] until exhausting the text you can read successive blocks. Alternatively, you can type ~c[:]~ilc[more!] to get all the remaining blocks. If you want to get the details and don't want to see the elementary stuff typed by ~c[:]~ilc[doc] name, type: ~bv[] ACL2 !>:MORE-DOC name ~ev[] We have documented not just function names but names of certain important ideas too. For example, ~pl[rewrite] and ~pl[meta] to learn about ~c[:]~ilc[rewrite] rules and ~c[:]~ilc[meta] rules, respectively. ~l[hints] to learn about the structure of the ~c[:]~ilc[hints] argument to the prover. The ~ilc[deflabel] event (~pl[deflabel]) is a way to introduce a logical name for no reason other than to attach documentation to it; also ~pl[defdoc]. How do you know what names are documented? There is a documentation database which is querried with the ~c[:]~ilc[docs] command. The documentation database is divided into sections. The sections are listed by ~bv[] ACL2 !>:docs * ~ev[] Each section has a name, ~c[sect], and by typing ~bv[] ACL2 !>:docs sect ~ev[] or equivalently ~bv[] ACL2 !>:doc sect ~ev[] you will get an enumeration of the topics within that section. Those topics can be further explored by using ~c[:]~ilc[doc] (and ~c[:]~ilc[more]) on them. In fact the section name itself is just a documented name. ~c[:]~ilc[more] generally gives an informal overview of the general subject of the section. ~bv[] ACL2 !>:docs ** ~ev[] will list all documented topics, by section. This fills several pages but might be a good place to start. If you want documentation on some topic, but none of our names or brief descriptions seem to deal with that topic, you can invoke a command to search the text in the database for a given string. This is like the GNU Emacs ``~ilc[apropos]'' command. ~bv[] ACL2 !>:docs \"functional inst\" ~ev[] will list every documented topic whose ~c[:]~ilc[doc] or ~c[:]~ilc[more-doc] text includes the substring ~c[\"functional inst\"], where case and the exact number of spaces are irrelevant. If you want documentation on an ACL2 function or macro and the documentation database does not contain any entries for it, there are still several alternatives. ~bv[] ACL2 !>:args fn ~ev[] will print the arguments and some other relevant information about the named function or macro. This information is all gleaned from the definition (not from the documentation database) and hence this is a definitive way to determine if ~c[fn] is defined as a function or macro. You might also want to type: ~bv[] ACL2 !>:pc fn ~ev[] which will print the ~il[command] which introduced ~c[fn]. You should ~pl[command-descriptor] for details on the kinds of input you can give the ~c[:]~ilc[pc] command. The entire ACL2 documentation database is user extensible. That is, if you document your function definitions or theorems, then that documentation is made available via the database and its query commands. The implementation of our online documentation system makes use of Common Lisp's ``documentation strings.'' While Common Lisp permits a documentation string to be attached to any defined concept, Common Lisp assigns no interpretation to these strings. ACL2 attaches special significance to documentation strings that begin with the characters ``~c[:Doc-Section]''. When such a documentation string is seen, it is stored in the database and may be displayed via ~c[:]~ilc[doc], ~c[:]~ilc[more], ~c[:]~ilc[docs], etc. Such documentation strings must follow rigid syntactic rules to permit their processing by our commands. These are spelled out elsewhere; ~pl[doc-string]. A description of the structure of the documentation database may also be found; ~pl[doc-string]. Finally: To build the HTML documentation, proceed with the following sequence of steps. ~bq[] 1. In the ~c[doc/] subdirectory of the ACL2 distribution, start ACL2 and then evaluate ~c[(certify-book \"write-acl2-html\")]. 2. Exit ACL2 and start it up again (or, evaluate ~c[:]~ilc[u]). 3. Include the documented ~il[books] within your ACL2 loop using ~ilc[include-book]. 4. Evaluate ~c[(include-book \"../doc/write-acl2-html\" :dir :system)]. 5. Call macro ~c[write-html-file], following the instructions at the end of distributed file ~c[doc/write-acl2-html.lisp]. ~eq[]~/") (deflabel history :doc ":Doc-Section History functions that display or change history~/~/ ACL2 keeps track of the ~il[command]s that you have executed that have extended the logic or the rule database, as by the definition of macros, functions, etc. Using the facilities in this section you can review the sequence of ~il[command]s executed so far. For example, you can ask to see the most recently executed ~il[command], or the ~il[command] ~c[10] before that, or the ~il[command] that introduced a given function symbol. You can also undo back through some previous ~il[command], restoring the logical ~il[world] to what it was before the given ~il[command]. The annotations printed in the margin in response to some of these commands (such as `P', `L', and `V') are explained in the documentation for ~c[:]~ilc[pc]. Several technical terms are used in the documentation of the history ~il[command]s. You must understand these terms to use the ~il[command]s. These terms are documented via ~c[:]~ilc[doc] entries of their own. ~l[command], ~pl[events], ~pl[command-descriptor], and ~pl[logical-name].~/") #+acl2-loop-only (defmacro first (x) ":Doc-Section ACL2::ACL2-built-ins first member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'car x)) #+acl2-loop-only (defmacro second (x) ":Doc-Section ACL2::ACL2-built-ins second member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'cadr x)) #+acl2-loop-only (defmacro third (x) ":Doc-Section ACL2::ACL2-built-ins third member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'caddr x)) #+acl2-loop-only (defmacro fourth (x) ":Doc-Section ACL2::ACL2-built-ins fourth member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'cadddr x)) #+acl2-loop-only (defmacro fifth (x) ":Doc-Section ACL2::ACL2-built-ins fifth member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cddddr x))) #+acl2-loop-only (defmacro sixth (x) ":Doc-Section ACL2::ACL2-built-ins sixth member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'cadr (list 'cddddr x))) #+acl2-loop-only (defmacro seventh (x) ":Doc-Section ACL2::ACL2-built-ins seventh member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'caddr (list 'cddddr x))) #+acl2-loop-only (defmacro eighth (x) ":Doc-Section ACL2::ACL2-built-ins eighth member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'cadddr (list 'cddddr x))) #+acl2-loop-only (defmacro ninth (x) ":Doc-Section ACL2::ACL2-built-ins ninth member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'car (list 'cddddr (list 'cddddr x)))) #+acl2-loop-only (defmacro tenth (x) ":Doc-Section ACL2::ACL2-built-ins tenth member of the list~/ See any Common Lisp documentation for details.~/~/" (list 'cadr (list 'cddddr (list 'cddddr x)))) #+acl2-loop-only (defmacro rest (x) ":Doc-Section ACL2::ACL2-built-ins rest (~ilc[cdr]) of the list~/ In the logic, ~c[rest] is just a macro for ~ilc[cdr].~/ ~c[Rest] is a Common Lisp function. See any Common Lisp documentation for more information.~/" (list 'cdr x)) #+acl2-loop-only (defun identity (x) (declare (xargs :guard t)) ":Doc-Section ACL2::ACL2-built-ins the identity function~/ ~c[(Identity x)] equals ~c[x]; what else can we say?~/ ~c[Identity] is a Common Lisp function. See any Common Lisp documentation for more information.~/" x) #+acl2-loop-only (defun revappend (x y) ":Doc-Section ACL2::ACL2-built-ins concatentate the ~il[reverse] of one list to another~/ ~c[(Revappend x y)] ~il[concatenate]s the ~il[reverse] of the list ~c[x] to ~c[y], which is also typically a list.~/ The following theorem characterizes this English description. ~bv[] (equal (revappend x y) (append (reverse x) y)) ~ev[] Hint: This lemma follows immediately from the definition of ~ilc[reverse] and the following lemma. ~bv[] (defthm revappend-append (equal (append (revappend x y) z) (revappend x (append y z)))) ~ev[] The ~il[guard] for ~c[(revappend x y)] requires that ~c[x] is a true list. ~c[Revappend] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (true-listp x))) (if (endp x) y (revappend (cdr x) (cons (car x) y)))) (defthm character-listp-revappend (implies (true-listp x) (equal (character-listp (revappend x y)) (and (character-listp x) (character-listp y)))) ; In some versions of ACL2, the following :induct hint hasn't been necessary. :hints (("Goal" :induct (revappend x y)))) #+acl2-loop-only (defun reverse (x) ":Doc-Section ACL2::ACL2-built-ins reverse a list or string~/ ~c[(Reverse x)] is the result of reversing the order of the elements of the list or string ~c[x].~/ The ~il[guard] for ~c[reverse] requires that its argument is a true list or a string. ~c[Reverse] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (or (true-listp x) (stringp x)))) (cond ((stringp x) (coerce (revappend (coerce x 'list) nil) 'string)) (t (revappend x nil)))) (defdoc switches-parameters-and-modes ":Doc-Section switches-parameters-and-modes a variety of ways to modify the ACL2 environment~/ The beginning user might pay special attention to documentation for ~ilc[logic] and ~ilc[program]. Other topics in this section can be read as one gains familiarity with ACL2.~/~/") (defconst *valid-output-names* '(error warning warning! observation prove proof-checker event expansion summary proof-tree)) ; Set-difference$ (defun set-difference-eq-exec (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (or (symbol-listp l1) (symbol-listp l2))))) (cond ((endp l1) nil) ((member-eq (car l1) l2) (set-difference-eq-exec (cdr l1) l2)) (t (cons (car l1) (set-difference-eq-exec (cdr l1) l2))))) (defun set-difference-eql-exec (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (or (eqlable-listp l1) (eqlable-listp l2))))) (cond ((endp l1) nil) ((member (car l1) l2) (set-difference-eql-exec (cdr l1) l2)) (t (cons (car l1) (set-difference-eql-exec (cdr l1) l2))))) (defun set-difference-equal (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2)))) (cond ((endp l1) nil) ((member-equal (car l1) l2) (set-difference-equal (cdr l1) l2)) (t (cons (car l1) (set-difference-equal (cdr l1) l2))))) (defmacro set-difference-eq (l1 l2) `(set-difference$ ,l1 ,l2 :test 'eq)) (defthm set-difference-eq-exec-is-set-difference-equal (equal (set-difference-eq-exec l1 l2) (set-difference-equal l1 l2))) (defthm set-difference-eql-exec-is-set-difference-equal (equal (set-difference-eql-exec l1 l2) (set-difference-equal l1 l2))) (defmacro set-difference$ (l1 l2 &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins elements of one list that are not elements of another~/ ~bv[] General Forms: (set-difference$ l1 l2) (set-difference$ l1 l2 :test 'eql) ; same as above (eql as equality test) (set-difference$ l1 l2 :test 'eq) ; same, but eq is equality test (set-difference$ l1 l2 :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Set-difference$ l1 l2)] equals a list that contains the ~ilc[member]s of ~c[l1] that are not ~ilc[member]s of ~c[l2]. More precisely, the resulting list is the same as one gets by deleting the members of ~c[l2] from ~c[l1], leaving the remaining elements in the same order as in ~c[l1]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing members of the two lists.~/ The ~il[guard] for a call of ~c[set-difference$] depends on the test. In all cases, both arguments must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then one of the arguments must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then one of the arguments must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[set-difference$] and its variants: ~bq[] ~c[(set-difference-eq l1 l2)] is equivalent to ~c[(set-difference$ l1 l2 :test 'eq)]; ~c[(set-difference-equal l1 l2)] is equivalent to ~c[(set-difference$ l1 l2 :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[set-difference-equal]. ~c[Set-difference$] is similar to the Common Lisp primitive ~c[set-difference]. However, Common Lisp does not specify the order of elements in the result of a call of ~c[set-difference].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((l1 ,l1) (l2 ,l2)) :logic (set-difference-equal l1 l2) :exec (set-difference-eq-exec l1 l2))) ((equal test ''eql) `(let-mbe ((l1 ,l1) (l2 ,l2)) :logic (set-difference-equal l1 l2) :exec (set-difference-eql-exec l1 l2))) (t ; (equal test 'equal) `(set-difference-equal ,l1 ,l2)))) #+acl2-loop-only (defun listp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for (not necessarily proper) lists~/ ~c[(listp x)] is true when ~c[x] is either a ~ilc[cons] pair or is ~c[nil].~/ ~c[Listp] has no ~il[guard], i.e., its ~il[guard] is ~c[t]. ~c[Listp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard t)) (or (consp x) (equal x nil))) (defconst *summary-types* '(header form rules hint-events warnings time steps value splitter-rules)) (defun with-output-fn (ctx args off on gag-mode off-on-p gag-p stack summary summary-p) (declare (xargs :mode :program :guard (true-listp args))) (cond ((endp args) nil) ((keywordp (car args)) (let ((illegal-value-string "~x0 is not a legal value for a call of with-output, but has been ~ supplied for keyword ~x1. See :DOC with-output.")) (cond ((consp (cdr args)) (cond ((eq (car args) :gag-mode) (cond ((member-eq (cadr args) '(t :goals nil)) ; keep this list in sync with set-gag-mode (with-output-fn ctx (cddr args) off on (cadr args) off-on-p t stack summary summary-p)) (t (illegal ctx illegal-value-string (list (cons #\0 (cadr args)) (cons #\1 :gag-mode)))))) ((eq (car args) :stack) (cond (stack (illegal ctx "The keyword :STACK may only be supplied once in a call ~ of ~x0." (list (cons #\0 'with-output)))) ((member-eq (cadr args) '(:push :pop)) (with-output-fn ctx (cddr args) off on gag-mode off-on-p gag-p (cadr args) summary summary-p)) (t (illegal ctx illegal-value-string (list (cons #\0 (cadr args)) (cons #\1 :stack)))))) ((eq (car args) :summary) (cond (summary-p (illegal ctx "The keyword :SUMMARY may only be supplied once in ~ a call of ~x0." (list (cons #\0 'with-output)))) ((not (or (eq (cadr args) :all) (and (symbol-listp (cadr args)) (subsetp-eq (cadr args) *summary-types*)))) (illegal ctx "In a call of ~x0, the value of keyword :SUMMARY ~ must either be :ALL or a true-list contained in ~ the list ~x1." (list (cons #\0 'with-output) (cons #\1 *summary-types*)))) (t (with-output-fn ctx (cddr args) off on gag-mode off-on-p gag-p stack (cadr args) t)))) ((not (member-eq (car args) '(:on :off))) (illegal ctx "~x0 is not a legal keyword for a call of with-output. ~ See :DOC with-output." (list (cons #\0 (car args))))) (t (let ((syms (cond ((eq (cadr args) :all) :all) ((symbol-listp (cadr args)) (cadr args)) ((symbolp (cadr args)) (list (cadr args)))))) (cond (syms (cond ((eq (car args) :on) (and (null on) (with-output-fn ctx (cddr args) off (if (eq syms :all) :all syms) gag-mode t gag-p stack summary summary-p))) (t ; (eq (car args) :off) (and (null off) (with-output-fn ctx (cddr args) (if (eq syms :all) :all syms) on gag-mode t gag-p stack summary summary-p))))) (t (illegal ctx illegal-value-string (list (cons #\0 (cadr args)) (cons #\1 (car args)))))))))) (t (illegal ctx "A with-output form has terminated with a keyword, ~x0. ~ This is illegal. See :DOC with-output." (list (cons #\0 (car args)))))))) ((cdr args) (illegal ctx "Illegal with-output form. See :DOC with-output." nil)) ((not (or (eq off :all) (subsetp-eq off *valid-output-names*))) (illegal ctx "The :off argument to with-output-fn must either be :all or a ~ subset of the list ~X01, but ~x2 contains ~&3." (list (cons #\0 *valid-output-names*) (cons #\1 nil) (cons #\2 off) (cons #\3 (set-difference-eq off *valid-output-names*))))) ((not (or (eq on :all) (subsetp-eq on *valid-output-names*))) (illegal ctx "The :on argument to with-output-fn must either be :all or a ~ subset of the list ~X01, but ~x2 contains ~&3." (list (cons #\0 *valid-output-names*) (cons #\1 nil) (cons #\2 on) (cons #\3 (set-difference-eq on *valid-output-names*))))) (t `(state-global-let* (,@ (and gag-p `((gag-mode (f-get-global 'gag-mode state) set-gag-mode-fn))) ,@ (and (or off-on-p (eq stack :pop)) '((inhibit-output-lst (f-get-global 'inhibit-output-lst state)))) ,@ (and stack '((inhibit-output-lst-stack (f-get-global 'inhibit-output-lst-stack state)))) ,@ (and summary-p `((inhibited-summary-types ,(if (eq summary :all) nil (list 'quote (set-difference-eq *summary-types* summary))))))) (er-progn ,@(and gag-p `((pprogn (set-gag-mode ,gag-mode) (value nil)))) ,@(and stack `((pprogn ,(if (eq stack :pop) '(pop-inhibit-output-lst-stack state) '(push-inhibit-output-lst-stack state)) (value nil)))) ,@(and off-on-p `((set-inhibit-output-lst ,(cond ((eq on :all) (if (eq off :all) '*valid-output-names* `(quote ,off))) ((eq off :all) `(set-difference-eq *valid-output-names* ',on)) (t `(union-eq ',off (set-difference-eq (f-get-global 'inhibit-output-lst state) ',on))))))) ,(car args)))))) #+acl2-loop-only (defun last (l) ":Doc-Section ACL2::ACL2-built-ins the last ~ilc[cons] (not element) of a list~/ ~c[(Last l)] is the last ~ilc[cons] of a list. Here are examples. ~bv[] ACL2 !>(last '(a b . c)) (B . C) ACL2 !>(last '(a b c)) (C) ~ev[] ~/ ~c[(Last l)] has a ~il[guard] of ~c[(listp l)]; thus, ~c[l] need not be a ~ilc[true-listp]. ~c[Last] is a Common Lisp function. See any Common Lisp documentation for more information. Unlike Common Lisp, we do not allow an optional second argument for ~c[last]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (listp l))) (if (atom (cdr l)) l (last (cdr l)))) (defun first-n-ac (i l ac) (declare (type (integer 0 *) i) (xargs :guard (and (true-listp l) (true-listp ac)))) (cond ((zp i) (reverse ac)) (t (first-n-ac (1- i) (cdr l) (cons (car l) ac))))) (defun take (n l) ":Doc-Section ACL2::ACL2-built-ins initial segment of a list~/ For any natural number ~c[n] not exceeding the length of ~c[l], ~c[(take n l)] collects the first ~c[n] elements of the list ~c[l].~/ The following is a theorem (though it takes some effort, including lemmas, to get ACL2 to prove it): ~bv[] (equal (length (take n l)) (nfix n)) ~ev[] If ~c[n] is an integer greater than the length of ~c[l], then ~c[take] pads the list with the appropriate number of ~c[nil] elements. Thus, the following is also a theorem. ~bv[] (implies (and (integerp n) (true-listp l) (<= (length l) n)) (equal (take n l) (append l (make-list (- n (length l)))))) ~ev[] For related functions, ~pl[nthcdr] and ~pl[butlast]. The ~il[guard] for ~c[(take n l)] is that ~c[n] is a nonnegative integer and ~c[l] is a true list. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp n) (not (< n 0)) (true-listp l)))) #-acl2-loop-only (when (<= n most-positive-fixnum) (return-from take (loop for i fixnum from 1 to n ; Warning: Do not use "as x in l collect x" on the next line. Sol Swords ; disovered that at least in CCL, the looping stops in that case when l is ; empty. collect (pop l)))) (first-n-ac n l nil)) #+acl2-loop-only (defun butlast (lst n) ":Doc-Section ACL2::ACL2-built-ins all but a final segment of a list~/ ~c[(Butlast l n)] is the list obtained by removing the last ~c[n] elements from the true list ~c[l]. The following is a theorem (though it takes some effort, including lemmas, to get ACL2 to prove it). ~bv[] (implies (and (integerp n) (<= 0 n) (true-listp l)) (equal (length (butlast l n)) (if (< n (length l)) (- (length l) n) 0))) ~ev[] For related functions, ~pl[take] and ~pl[nthcdr].~/ The ~il[guard] for ~c[(butlast l n)] requires that ~c[n] is a nonnegative integer and ~c[lst] is a true list. ~c[Butlast] is a Common Lisp function. See any Common Lisp documentation for more information. Note: In Common Lisp the second argument of ~c[butlast] is optional, but in ACL2 it is required. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (true-listp lst) (integerp n) (<= 0 n)))) (let ((lng (len lst)) (n (nfix n))) (if (<= lng n) nil (take (- lng n) lst)))) #-acl2-loop-only (defmacro with-output (&rest args) (car (last args))) #+acl2-loop-only (defmacro with-output (&rest args) ":Doc-Section switches-parameters-and-modes suppressing or turning on specified output for an event~/ ~bv[] Examples: ; Turn off all output during evaluation of the indicated thm form. (with-output :off :all :gag-mode nil (thm (equal (app (app x y) z) (app x (app y z))))) ; Prove the indicated theorem with the event summary turned off and ; using the :goals setting for gag-mode. (with-output :off summary :gag-mode :goals (defthm app-assoc (equal (app (app x y) z) (app x (app y z))))) ; Same effect as just above: (with-output :on summary :summary nil :gag-mode :goals (defthm app-assoc (equal (app (app x y) z) (app x (app y z))))) ; Turn on only the indicated parts of the summary. (with-output :on summary :summary (time rules) :gag-mode :goals ; use gag-mode, with goal names printed (defthm app-assoc (equal (app (app x y) z) (app x (app y z))))) ; Same as specifying :off :all, but showing all output types: (with-output :off (error warning warning! observation prove proof-checker event expansion summary proof-tree) :gag-mode nil (thm (equal (app (app x y) z) (app x (app y z))))) ; Same as above, but :stack :push says to save the current ; inhibit-output-lst, which can be restored in a subsidiary with-output call ; that specifies :stack :pop. (with-output :stack :push :off :all :gag-mode nil (thm (equal (app (app x y) z) (app x (app y z)))))~/ General Form: (with-output :key1 val1 ... :keyk valk form) ~ev[] where each ~c[:keyi] is either ~c[:off], ~c[:on], ~c[:stack], ~c[:summary], or ~c[:gag-mode]; ~c[form] evaluates to an error triple (~pl[error-triples]); and ~c[vali] is as follows. If ~c[:keyi] is ~c[:off] or ~c[:on], then ~c[vali] can be ~c[:all], and otherwise is a symbol or non-empty list of symbols representing output types that can be inhibited; ~pl[set-inhibit-output-lst]. If ~c[:keyi] is ~c[:gag-mode], then ~c[vali] is one of the legal values for ~c[:]~ilc[set-gag-mode]. If ~c[:keyi] is ~c[:summary], then ~c[vali] is either ~c[:all] or a true-list of symbols each of which belongs to the list ~c[*summary-types*]. Otherwise ~c[:keyi] is ~c[:stack], in which case ~c[:vali] is ~c[:push] or ~c[:pop]; for now assume that ~c[:stack] is not specified (we'll return to it below). The result of evaluating the General Form above is to evaluate ~c[form], but in an environment where output occurs as follows. If ~c[:on :all] is specified, then every output type is turned on except as inhibited by ~c[:off]; else if ~c[:off :all] is specified, then every output type is inhibited except as specified by ~c[:on]; and otherwise, the currently-inhibited output types are reduced as specified by ~c[:on] and then extended as specified by ~c[:off]. But if ~c[:gag-mode] is specified, then before modifying how output is inhibited, ~ilc[gag-mode] is set for the evaluation of ~c[form] as specified by the value of ~c[:gag-mode]; ~pl[set-gag-mode]. If ~c[summary] is among the output types that are turned on (not inhibited), then if ~c[:summary] is specified, the only parts of the summary to be printed will be those specified by the value of ~c[:summary]. The correspondence should be clear, except perhaps that ~c[header] refers to the line containing only the word ~c[Summary], and ~c[value] refers to the value of the form printed during evaluation of sequences of events as for ~ilc[progn] and ~ilc[encapsulate]. Note that the handling of the ~c[:stack] argument pays no attention to the ~c[:summary] argument. Note: When the scope of ~c[with-output] is exited, then all modifications are undone, reverting ~c[gag-mode] and the state of output inhibition to those which were present before the ~c[with-output] call was entered. The ~c[:stack] keyword's effect is illustrated by the following example, where ``~c[(encapsulate nil)]'' may replaced by ``~c[(progn]'' without any change to the output that is printed. ~bv[] (with-output :stack :push :off :all (encapsulate () (defun f1 (x) x) (with-output :stack :pop (defun f2 (x) x)) (defun f3 (x) x) (with-output :stack :pop :off warning (in-theory nil)) (defun f4 (x) x))) ~ev[] The outer ~c[with-output] call saves the current output settings (as may have been modified by earlier calls of ~ilc[set-inhibit-output-lst]), by pushing them onto a stack, and then turns off all output. Each inner ~c[with-output] call temporarily pops that stack, restoring the starting output settings, until it completes and undoes the effects of that pop. Unless ~c[event] output was inhibited at the top level (~pl[set-inhibit-output-lst]), the following output is shown: ~bv[] Since F2 is non-recursive, its admission is trivial. We observe that the type of F2 is described by the theorem (EQUAL (F2 X) X). ~ev[] And then, if ~c[summary] output was not inhibited at the top level, we get the rest of this output: ~bv[] Summary Form: ( DEFUN F2 ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( IN-THEORY NIL) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ~ev[] Note that the use of ~c[:off warning] supresses a ~c[\"Theory\"] warning for the ~c[(in-theory nil)] event, and that in no case will output be printed for definitions of ~c[f1], ~c[f3], or ~c[f4], or for the ~ilc[encapsulate] event itself. The following more detailed explanation of ~c[:stack] is intended only for advanced users. After ~c[:gag-mode] is handled (if present) but before ~c[:on] or ~c[:off] is handled, the value of ~c[:stack] is handled as follows. If the value is ~c[:push], then ~il[state] global ~c[inhibit-output-lst-stack] is modified by pushing the value of ~il[state] global ~c[inhibit-output-lst] onto the value of ~il[state] global ~c[inhibit-output-lst-stack], which is ~c[nil] at the top level. If the value is ~c[:pop], then ~il[state] global ~c[inhibit-output-lst-stack] is modified only if non-~c[nil], in which case its top element is popped and becomes the value of of ~il[state] global ~c[inhibit-output-lst]. Warning: ~c[With-output] has no effect in raw Lisp, and hence is disallowed in function bodies. However, you can probably get the effect you want as illustrated below, where ~c[
] must return an error triple ~c[(mv erp val state)]; ~pl[ld] and ~pl[error-triples]. ~bv[] Examples avoiding with-output, for use in function definitions: ; Inhibit all output: (state-global-let* ((inhibit-output-lst *valid-output-names*)) ) ; Inhibit all warning output: (state-global-let* ((inhibit-output-lst (union-eq (f-get-global 'inhibit-output-lst state) '(warning warning!)))) ) ~ev[] Note that ~c[with-output] is allowed in books. ~l[embedded-event-form]." (let ((val (with-output-fn 'with-output args nil nil nil nil nil nil nil nil))) (or val (illegal 'with-output "Macroexpansion of ~q0 failed." (list (cons #\0 (cons 'with-output args))))))) ; Mutual Recursion ; We are about to need mutual recursion for the first time in axioms.lisp. ; We now define the mutual-recursion macro for the logic. (defun mutual-recursion-guardp (rst) (declare (xargs :guard t)) (cond ((atom rst) (equal rst nil)) (t (and (consp (car rst)) (true-listp (car rst)) (true-listp (caddr (car rst))) ; formals (member-eq (car (car rst)) '(defun defund defun-nx defund-nx)) (mutual-recursion-guardp (cdr rst)))))) (defun collect-cadrs-when-car-eq (x alist) (declare (xargs :guard (assoc-eq-equal-alistp alist))) (cond ((endp alist) nil) ((eq x (car (car alist))) (cons (cadr (car alist)) (collect-cadrs-when-car-eq x (cdr alist)))) (t (collect-cadrs-when-car-eq x (cdr alist))))) (defmacro value (x) ; Keep in sync with value@par. `(mv nil ,x state)) (defun value-triple-fn (form on-skip-proofs check) (declare (xargs :guard t)) `(cond ((and ,(not on-skip-proofs) (f-get-global 'ld-skip-proofsp state)) (value :skipped)) (t ,(let ((form `(let ((check ,check)) (cond (check (cond ((check-vars-not-free (check) ,form) :passed) ((tilde-@p check) (er hard 'value-triple "Assertion failed:~%~@0~|" check)) (t (er hard 'value-triple "Assertion failed on form:~%~x0~|" ',form)))) (t ,form))))) `(state-global-let* ((safe-mode (not (global-val 'boot-strap-flg (w state))))) (value ,form)))))) #+acl2-loop-only (defmacro value-triple (form &key on-skip-proofs check) ":Doc-Section Events compute a value, optionally checking that it is not ~c[nil]~/ ~bv[] Examples: (value-triple (+ 3 4)) (value-triple (cw \"hi\") :on-skip-proofs t) (value-triple (@ ld-pre-eval-print)) (value-triple (@ ld-pre-eval-print) :check t)~/ General Form: (value-triple form :on-skip-proofs sp ; optional; nil by default :check chk ; optional; nil by default ) ~ev[] ~c[Value-triple] provides a convenient way to evaluate a form in an event context, including ~ilc[progn] and ~ilc[encapsulate] and in ~il[books]; ~pl[events]. The form should evaluate to a single, non-~il[stobj] value. Calls of ~c[value-triple] are generally skipped when proofs are being skipped, in particular when ACL2 is performing the second pass through the ~il[events] of an ~ilc[encapsulate] form or during an ~ilc[include-book], or indeed any time ~ilc[ld-skip-proofsp] is non-~c[nil]. If you want the call evaluated during those times as well, use a non-~c[nil] value for ~c[:on-skip-proofs]. Note that the argument to ~c[:on-skip-proofs] is not evaluated. If you expect the form to evaluate to a non-~c[nil] value and you want an error to occur when that is not the case, you can use ~c[:check t]. More generally, the argument of ~c[:check] can be a form that evaluates to a single, non-~il[stobj] value. If this value is not ~c[nil], then the aforementioned test is made (that the given form is not ~c[nil]). If an error occurs and the value of ~c[:check] is a string or indeed any ``message'' suitable for printing by ~ilc[fmt] when supplied as a value for tilde-directive ~c[~~@], then that string or message is printed." (value-triple-fn form on-skip-proofs check)) (defmacro assert-event (form &key on-skip-proofs msg) ":Doc-Section Events assert that a given form returns a non-~c[nil] value~/ ~bv[] Examples: (assert-event (equal (+ 3 4) 7)) (assert-event (equal (+ 3 4) 7) :msg (msg \"Error: ~~x0\" 'equal-check)) (assert-event (equal (+ 3 4) 7) :on-skip-proofs t)~/ General Forms: (assert-event form) (assert-event form :on-skip-proofs t) ~ev[] ~c[Assert-event] takes a ground form, i.e., one with no free variables; ~ilc[stobj]s are allowed but only a single non-~ilc[stobj] value can be returned. The form is then evaluated and if the result is ~c[nil], then a so-called hard error (~pl[er]) results. This evaluation is however not done if proofs are being skipped, as during ~ilc[include-book] (also ~pl[skip-proofs] and ~pl[ld-skip-proofsp]), unless ~c[:on-skip-proofs t] is supplied. Normally, if an ~c[assert-event] call fails then a generic failure message is printed, showing the offending form. However, if keyword argument ~c[:msg] is supplied, then the failure message is printed as with ~ilc[fmt] argument ~c[~~@0]; ~pl[fmt]. In particular, ~c[:msg] is typically a string or a call ~c[(msg str arg-0 arg-1 ... arg-k)], where ~c[str] is a string and each ~c[arg-i] is the value to be associated with ~c[#\\i] upon formatted printing (as with ~ilc[fmt]) of the string ~c[str]. This form may be put into a book to be certified (~pl[books]), because ~c[assert-event] is a macro whose calls expand to calls of ~c[value-triple] (~pl[embedded-event-form]). When certifying a book, guard-checking is off, as though ~c[(set-guard-checking nil)] has been evaluated; ~pl[set-guard-checking]. That, together with a ``safe mode,'' guarantees that ~c[assert-event] forms are evaluated in the logic without guard violations while certifying a book.~/" (declare (xargs :guard (booleanp on-skip-proofs))) `(value-triple ,form :on-skip-proofs ,on-skip-proofs :check ,(or msg t))) (defun xd-name (event-type name) (declare (xargs :guard (member-eq event-type '(defund defthmd)))) (cond ((eq event-type 'defund) (list :defund name)) ((eq event-type 'defthmd) (list :defthmd name)) (t (illegal 'xd-name "Unexpected event-type for xd-name, ~x0" (list (cons #\0 event-type)))))) (defun defund-name-list (defuns acc) (declare (xargs :guard (and (mutual-recursion-guardp defuns) (true-listp acc)))) (cond ((endp defuns) (reverse acc)) (t (defund-name-list (cdr defuns) (cons (if (eq (caar defuns) 'defund) (xd-name 'defund (cadar defuns)) (cadar defuns)) acc))))) ; Begin support for defun-nx. (defun throw-nonexec-error (fn actuals) (declare (xargs :guard ; An appropriate guard would seem to be the following. ; (if (keywordp fn) ; (eq fn :non-exec) ; (and (symbolp fn) ; (true-listp actuals))) ; However, we want to be sure that the raw Lisp code is evaluated even if ; guard-checking has been set to :none. A simple fix is to replace the actuals ; if they are ill-formed, and that is what we do. t :verify-guards nil) #+acl2-loop-only (ignore fn actuals)) #-acl2-loop-only (progn (throw-raw-ev-fncall (list* 'ev-fncall-null-body-er ; The following nil means that we never blame non-executability on aokp. Note ; that defproxy is not relevant here, since that macro generates a call of ; install-event-defuns, which calls intro-udf-lst2, which calls null-body-er ; to lay down a call of throw-or-attach. So in the defproxy case, ; throw-nonexec-error doesn't get called! nil fn (if (eq fn :non-exec) actuals (print-list-without-stobj-arrays (if (true-listp actuals) actuals (error "Unexpected case: Ill-formed actuals for ~ throw-nonexec-error!")))))) ; Just in case throw-raw-ev-fncall doesn't throw -- though it always should. (error "This error is caused by what should be dead code!")) nil) (defun defun-nx-fn (form disabledp) (declare (xargs :guard (and (true-listp form) (true-listp (caddr form))) :verify-guards nil)) (let ((name (cadr form)) (formals (caddr form)) (rest (cdddr form)) (defunx (if disabledp 'defund 'defun))) `(,defunx ,name ,formals (declare (xargs :non-executable t :mode :logic)) ,@(butlast rest 1) (prog2$ (throw-nonexec-error ',name (list ,@formals)) ,@(last rest))))) (defmacro defun-nx (&whole form &rest rest) ":Doc-Section acl2::Events define a non-executable function symbol~/ ~bv[] Example: (set-state-ok t) (defun-nx foo (x state) (mv-let (a b c) (cons x state) (list a b c b a))) ; Note ``ill-formed'' call of foo just below. (defun bar (state y) (foo state y)) ~ev[] The macro ~c[defun-nx] introduces definitions using the ~ilc[defun] macro, always in ~c[:]~ilc[logic] mode, such that the calls of the resulting function cannot be evaluated. Such a definition is admitted without enforcing syntactic restrictions for executability, in particular for single-threadedness (~pl[stobj]) and multiple-values passing (~pl[mv] and ~pl[mv-let]). After such a definition is admitted, the usual syntactic rules for ~ilc[state] and user-defined ~il[stobj]s are relaxed for calls of the function it defines. Also ~pl[non-exec] for a way to designate subterms of function bodies, or subterms of code to be executed at the top level, as non-executable. The syntax of ~c[defun-nx] is identical to that of ~ilc[defun]. A form ~bv[] (defun-nx name (x1 ... xk) ... body) ~ev[] expands to the following form. ~bv[] (defun name (x1 ... xk) (declare (xargs :non-executable t :mode :logic)) ... (prog2$ (throw-nonexec-error 'name (list x1 ... xk)) body)) ~ev[] Note that because of the insertion of the above call of ~c[throw-nonexec-error], no formal is ignored when using ~c[defun-nx].~/ During proofs, the error is silent; it is ``caught'' by the proof mechanism and generally results in the introduction of a call of ~ilc[hide] during a proof. If an error message is produced by evaluating a call of the function on a list of arguments that includes ~c[state] or user-defined ~ilc[stobj]s, these arguments will be shown as symbols such as ~c[||] in the error message. In the case of a user-defined stobj bound by ~ilc[with-local-stobj] or ~ilc[stobj-let], the symbol printed will include the suffix ~c[{instance}], for example, ~c[|{instance}|]. It is harmless to include ~c[:non-executable t] in your own ~ilc[xargs] ~ilc[declare] form; ~c[defun-nx] will still lay down its own such declaration, but ACL2 can tolerate the duplication. Note that ~c[defund-nx] is also available. It has an effect identical to that of ~c[defun-nx] except that as with ~ilc[defund], it leaves the function disabled. If you use guards (~pl[guard]), please be aware that even though syntactic restrictions are relaxed for ~c[defun-nx], guard verification proceeds exactly as for ~ilc[defun]. If you want ACL2 to skip a form for purposes of generating guard proof obligations, use the macro ~ilc[non-exec], which generates a call of ~c[throw-nonexec-error] that differs somewhat from the one displayed above. ~l[non-exec]. ~l[defun] for documentation of ~c[defun]." (declare (xargs :guard (and (true-listp form) (true-listp (caddr form)))) (ignore rest)) (defun-nx-fn form nil)) (defmacro defund-nx (&whole form &rest rest) (declare (xargs :guard (and (true-listp form) (true-listp (caddr form)))) (ignore rest)) (defun-nx-fn form t)) (defun update-mutual-recursion-for-defun-nx-1 (defs) (declare (xargs :guard (mutual-recursion-guardp defs) :verify-guards nil)) (cond ((endp defs) nil) ((eq (caar defs) 'defun-nx) (cons (defun-nx-fn (car defs) nil) (update-mutual-recursion-for-defun-nx-1 (cdr defs)))) ((eq (caar defs) 'defund-nx) (cons (defun-nx-fn (car defs) t) (update-mutual-recursion-for-defun-nx-1 (cdr defs)))) (t (cons (car defs) (update-mutual-recursion-for-defun-nx-1 (cdr defs)))))) (defun update-mutual-recursion-for-defun-nx (defs) (declare (xargs :guard (mutual-recursion-guardp defs) :verify-guards nil)) (cond ((or (assoc-eq 'defun-nx defs) (assoc-eq 'defund-nx defs)) (update-mutual-recursion-for-defun-nx-1 defs)) (t defs))) #+acl2-loop-only (defmacro mutual-recursion (&whole event-form &rest rst) ":Doc-Section Events define some mutually recursive functions~/ ~bv[] Example: (mutual-recursion (defun evenlp (x) (if (consp x) (oddlp (cdr x)) t)) (defun oddlp (x) (if (consp x) (evenlp (cdr x)) nil)))~/ General Form: (mutual-recursion def1 ... defn) where each ~c[defi] is a call of ~ilc[defun], ~ilc[defund], ~ilc[defun-nx], or ~c[defund-nx]. ~ev[] When mutually recursive functions are introduced it is necessary to do the termination analysis on the entire clique of definitions. Each ~ilc[defun] form specifies its own measure, either with the ~c[:measure] keyword ~c[xarg] (~pl[xargs]) or by default to ~ilc[acl2-count]. When a function in the clique calls a function in the clique, the measure of the callee's actuals must be smaller than the measure of the caller's formals ~-[] just as in the case of a simply recursive function. But with mutual recursion, the callee's actuals are measured as specified by the callee's ~ilc[defun] while the caller's formals are measured as specified by the caller's ~ilc[defun]. These two measures may be different but must be comparable in the sense that ~ilc[o<] decreases through calls. If you want to specify ~c[:]~ilc[hints] or ~c[:guard-hints] (~pl[xargs]), you can put them in the ~ilc[xargs] declaration of any of the ~ilc[defun] forms, as the ~c[:]~ilc[hints] from each form will be appended together, as will the ~ilc[guard-hints] from each form. You may find it helpful to use a lexicographic order, the idea being to have a measure that returns a list of two arguments, where the first takes priority over the second. Here is an example. ~bv[] (include-book \"ordinals/lexicographic-ordering\" :dir :system) (encapsulate () (set-well-founded-relation l<) ; will be treated as LOCAL (mutual-recursion (defun foo (x) (declare (xargs :measure (list (acl2-count x) 1))) (bar x)) (defun bar (y) (declare (xargs :measure (list (acl2-count y) 0))) (if (zp y) y (foo (1- y)))))) ~ev[] The ~ilc[guard] analysis must also be done for all of the functions at the same time. If any one of the ~ilc[defun]s specifies the ~c[:]~ilc[verify-guards] ~c[xarg] to be ~c[nil], then ~il[guard] verification is omitted for all of the functions. Similarly, if any one of the ~ilc[defun]s specifies the ~c[:non-executable] ~c[xarg] to be ~c[t], or if any of the definitions uses ~ilc[defun-nx] or ~c[defund-nx], then every one of the definitions will be treated as though it specifies a ~c[:non-executable] ~c[xarg] of ~c[t]. Technical Note: Each ~c[defi] above must be a call of ~ilc[defun], ~ilc[defund], ~ilc[defun-nx], or ~c[defund-nx]. In particular, it is not permitted for a ~c[defi] to be an arbitrary form that macroexpands into a ~ilc[defun] form. This is because ~c[mutual-recursion] is itself a macro, and since macroexpansion occurs from the outside in, at the time ~c[(mutual-recursion def1 ... defk)] is expanded the ~c[defi] have not yet been macroexpanded. Suppose you have defined your own ~ilc[defun]-like macro and wish to use it in a ~c[mutual-recursion] expression. Well, you can't. (!) But you can define your own version of ~c[mutual-recursion] that allows your ~ilc[defun]-like form. Here is an example. Suppose you define ~bv[] (defmacro my-defun (&rest args) (my-defun-fn args)) ~ev[] where ~c[my-defun-fn] takes the arguments of the ~c[my-defun] form and produces from them a ~ilc[defun] form. As noted above, you are not allowed to write ~c[(mutual-recursion (my-defun ...) ...)]. But you can define the macro ~c[my-mutual-recursion] so that ~bv[] (my-mutual-recursion (my-defun ...) ... (my-defun ...)) ~ev[] expands into ~c[(mutual-recursion (defun ...) ... (defun ...))] by applying ~c[my-defun-fn] to each of the arguments of ~c[my-mutual-recursion]. ~bv[] (defun my-mutual-recursion-fn (lst) (declare (xargs :guard (alistp lst))) ; Each element of lst must be a consp (whose car, we assume, is always ; MY-DEFUN). We apply my-defun-fn to the arguments of each element and ; collect the resulting list of DEFUNs. (cond ((atom lst) nil) (t (cons (my-defun-fn (cdr (car lst))) (my-mutual-recursion-fn (cdr lst)))))) (defmacro my-mutual-recursion (&rest lst) ; Each element of lst must be a consp (whose car, we assume, is always ; MY-DEFUN). We obtain the DEFUN corresponding to each and list them ; all inside a MUTUAL-RECURSION form. (declare (xargs :guard (alistp lst))) (cons 'mutual-recursion (my-mutual-recursion-fn lst))). ~ev[]~/ :cited-by Programming" (declare (xargs :guard (mutual-recursion-guardp rst))) (let ((rst (update-mutual-recursion-for-defun-nx rst))) (let ((form (list 'defuns-fn (list 'quote (strip-cdrs rst)) 'state (list 'quote event-form) #+:non-standard-analysis ; std-p nil))) (cond ((assoc-eq 'defund rst) (list 'er-progn form (list 'with-output :off 'summary (list 'in-theory (cons 'disable (collect-cadrs-when-car-eq 'defund rst)))) (list 'value-triple (list 'quote (defund-name-list rst nil))))) (t form))))) ; Now we define the weak notion of term that guards metafunctions. (mutual-recursion (defun pseudo-termp (x) ":Doc-Section ACL2::ACL2-built-ins a predicate for recognizing term-like s-expressions~/ ~bv[] Example Forms: (pseudo-termp '(car (cons x 'nil))) ; has value t (pseudo-termp '(car x y z)) ; also has value t! (pseudo-termp '(delta (h x))) ; has value t (pseudo-termp '(delta (h x) . 7)) ; has value nil (not a true-listp) (pseudo-termp '((lambda (x) (car x)) b)) ; has value t (pseudo-termp '(if x y 123)) ; has value nil (123 is not quoted) (pseudo-termp '(if x y '123)) ; has value t ~ev[] If ~c[x] is the quotation of a term, then ~c[(pseudo-termp x)] is ~c[t]. However, if ~c[x] is not the quotation of a term it is not necessarily the case that ~c[(pseudo-termp x)] is ~c[nil].~/ ~l[term] for a discussion of the various meanings of the word ``term'' in ACL2. In its most strict sense, a term is either a legal variable symbol, a quoted constant, or the application of an ~c[n]-ary function symbol or closed ~c[lambda]-expression to ~c[n] terms. By ``legal variable symbol'' we exclude constant symbols, such as ~c[t], ~c[nil], and ~c[*ts-rational*]. By ``quoted constants'' we include ~c['t] (aka ~c[(quote t)]), ~c['nil], ~c['31], etc., and exclude constant names such as ~c[t], ~c[nil] and ~c[*ts-rational*], unquoted constants such as ~c[31] or ~c[1/2], and ill-formed ~c[quote] expressions such as ~c[(quote 3 4)]. By ``closed lambda expression'' we exclude expressions, such as ~c[(lambda (x) (cons x y))], containing free variables in their bodies. Terms typed by the user are translated into strict terms for internal use in ACL2. The predicate ~c[termp] checks this strict sense of ``term'' with respect to a given ACL2 logical world; ~l[world]. Many ACL2 functions, such as the rewriter, require certain of their arguments to satisfy ~c[termp]. However, as of this writing, ~c[termp] is in ~c[:]~ilc[program] mode and thus cannot be used effectively in conjectures to be proved. Furthermore, if regarded simply from the perspective of an effective ~il[guard] for a term-processing function, ~c[termp] checks many irrelevant things. (Does it really matter that the variable symbols encountered never start and end with an asterisk?) For these reasons, we have introduced the notion of a ``pseudo-term'' and embodied it in the predicate ~c[pseudo-termp], which is easier to check, does not require the logical ~il[world] as input, has ~c[:]~ilc[logic] mode, and is often perfectly suitable as a ~il[guard] on term-processing functions. A ~c[pseudo-termp] is either a symbol, a true list of length 2 beginning with the word ~c[quote], the application of an ~c[n]-ary pseudo-~c[lambda] expression to a true list of ~c[n] pseudo-terms, or the application of a symbol to a true list of ~c[n] ~c[pseudo-termp]s. By an ``~c[n]-ary pseudo-~c[lambda] expression'' we mean an expression of the form ~c[(lambda (v1 ... vn) pterm)], where the ~c[vi] are symbols (but not necessarily distinct legal variable symbols) and ~c[pterm] is a ~c[pseudo-termp]. Metafunctions may use ~c[pseudo-termp] as a ~il[guard]." (declare (xargs :guard t :mode :logic)) (cond ((atom x) (symbolp x)) ((eq (car x) 'quote) (and (consp (cdr x)) (null (cdr (cdr x))))) ((not (true-listp x)) nil) ((not (pseudo-term-listp (cdr x))) nil) (t (or (symbolp (car x)) ; For most function applications we do not check that the number of ; arguments matches the number of formals. However, for lambda ; applications we do make that check. The reason is that the ; constraint on an evaluator dealing with lambda applications must use ; pairlis$ to pair the formals with the actuals and pairlis$ insists on ; the checks below. (and (true-listp (car x)) (equal (length (car x)) 3) (eq (car (car x)) 'lambda) (symbol-listp (cadr (car x))) (pseudo-termp (caddr (car x))) (equal (length (cadr (car x))) (length (cdr x)))))))) (defun pseudo-term-listp (lst) (declare (xargs :guard t)) (cond ((atom lst) (equal lst nil)) (t (and (pseudo-termp (car lst)) (pseudo-term-listp (cdr lst)))))) ) (defthm pseudo-term-listp-forward-to-true-listp (implies (pseudo-term-listp x) (true-listp x)) :rule-classes :forward-chaining) ; For the encapsulate of too-many-ifs-post-rewrite (encapsulate () (table acl2-defaults-table :defun-mode :logic) (verify-guards pseudo-termp)) (defun pseudo-term-list-listp (l) (declare (xargs :guard t)) (if (atom l) (equal l nil) (and (pseudo-term-listp (car l)) (pseudo-term-list-listp (cdr l))))) (verify-guards pseudo-term-list-listp) ; Add-to-set (defun add-to-set-eq-exec (x lst) (declare (xargs :guard (if (symbolp x) (true-listp lst) (symbol-listp lst)))) (cond ((member-eq x lst) lst) (t (cons x lst)))) (defun add-to-set-eql-exec (x lst) (declare (xargs :guard (if (eqlablep x) (true-listp lst) (eqlable-listp lst)))) (cond ((member x lst) lst) (t (cons x lst)))) (defun add-to-set-equal (x l) (declare (xargs :guard (true-listp l))) ; Warning: This function is used by include-book-fn to add a ; certification tuple to the include-book-alist. We exploit the fact ; that if the tuple, x, isn't already in the list, l, then this ; function adds it at the front! So don't change this function ; without recoding include-book-fn. (cond ((member-equal x l) l) (t (cons x l)))) (defmacro add-to-set-eq (x lst) `(add-to-set ,x ,lst :test 'eq)) ; Added for backward compatibility (add-to-set-eql was present through ; Version_4.2): (defmacro add-to-set-eql (x lst) `(add-to-set ,x ,lst :test 'eql)) (defthm add-to-set-eq-exec-is-add-to-set-equal (equal (add-to-set-eq-exec x lst) (add-to-set-equal x lst))) (defthm add-to-set-eql-exec-is-add-to-set-equal (equal (add-to-set-eql-exec x lst) (add-to-set-equal x lst))) ; Disable non-recursive functions to assist in discharging mbe guard proof ; obligations. (in-theory (disable add-to-set-eq-exec add-to-set-eql-exec)) (defmacro add-to-set (x lst &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins add a symbol to a list~/ ~bv[] General Forms: (add-to-set x lst) (add-to-set x lst :test 'eql) ; same as above (eql as equality test) (add-to-set x lst :test 'eq) ; same, but eq is equality test (add-to-set x lst :test 'equal) ; same, but equal is equality test ~ev[] For a symbol ~c[x] and an object ~c[lst], ~c[(add-to-set-eq x lst)] is the result of ~ilc[cons]ing ~c[x] on to the front of ~c[lst], unless ~c[x] is already a ~ilc[member] of ~c[lst], in which case the result is ~c[lst]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with successive elements of ~c[lst].~/ The ~il[guard] for a call of ~c[add-to-set] depends on the test. In all cases, the second argument must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[add-to-set] and its variants: ~bq[] ~c[(add-to-set-eq x lst)] is equivalent to ~c[(add-to-set x lst :test 'eq)]; ~c[(add-to-set-equal x lst)] is equivalent to ~c[(add-to-set x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[add-to-set-equal].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (lst ,lst)) :logic (add-to-set-equal x lst) :exec (add-to-set-eq-exec x lst))) ((equal test ''eql) `(let-mbe ((x ,x) (lst ,lst)) :logic (add-to-set-equal x lst) :exec (add-to-set-eql-exec x lst))) (t ; (equal test 'equal) `(add-to-set-equal ,x ,lst)))) (defmacro variablep (x) (list 'atom x)) (defmacro nvariablep (x) (list 'consp x)) (defmacro fquotep (x) (list 'eq ''quote (list 'car x))) (defun quotep (x) (declare (xargs :guard t)) (and (consp x) (eq (car x) 'quote))) (defconst *t* (quote (quote t))) (defconst *nil* (quote (quote nil))) (defconst *0* (quote (quote 0))) (defconst *1* (quote (quote 1))) (defconst *-1* (quote (quote -1))) (defun kwote (x) ":Doc-Section ACL2::ACL2-built-ins quote an arbitrary object~/ For any object ~c[x], ~c[(kwote x)] returns the two-element list whose elements are the symbol ~c[quote] and the given ~c[x], respectively. The guard of ~c[(kwote x)] is ~c[t]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (mbe :logic ; Theorem ev-lambda-clause-correct in community book ; books/centaur/misc/evaluator-metatheorems.lisp goes out to lunch if we use ; the :exec term below as the definition. So we keep the :logic definition ; simple. (list 'quote x) :exec ; save conses (cond ((eq x nil) *nil*) ((eq x t) *t*) ((eql x 0) *0*) ((eql x 1) *1*) ((eql x -1) *-1*) (t (list 'quote x))))) (defun kwote-lst (lst) ":Doc-Section ACL2::ACL2-built-ins quote an arbitrary true list of objects~/ The function ~c[kwote-lst] applies the function ~c[kwote] to each element of a given list. The guard of ~c[(kwote-lst lst)] is ~c[(true-listp lst)]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) (t (cons (kwote (car lst)) (kwote-lst (cdr lst)))))) (defmacro unquote (x) (list 'cadr x)) (defmacro ffn-symb (x) (list 'car x)) (defun fn-symb (x) (declare (xargs :guard t)) (if (and (nvariablep x) (not (fquotep x))) (car x) nil)) (defmacro fargs (x) (list 'cdr x)) (mutual-recursion (defun all-vars1 (term ans) (declare (xargs :guard (and (pseudo-termp term) (symbol-listp ans)) :mode :program)) (cond ((variablep term) (add-to-set-eq term ans)) ((fquotep term) ans) (t (all-vars1-lst (fargs term) ans)))) (defun all-vars1-lst (lst ans) (declare (xargs :guard (and (pseudo-term-listp lst) (symbol-listp ans)) :mode :program)) (cond ((endp lst) ans) (t (all-vars1-lst (cdr lst) (all-vars1 (car lst) ans))))) ) (verify-termination-boot-strap (all-vars1 (declare (xargs :mode :logic :verify-guards nil))) (all-vars1-lst (declare (xargs :mode :logic)))) (defun all-vars (term) ; This function collects the variables in term in reverse print order of ; first occurrence. E.g., all-vars of '(f (g a b) c) is '(c b a). ; This ordering is exploited by, at least, loop-stopper and bad-synp-hyp. (declare (xargs :guard (pseudo-termp term) :verify-guards nil)) (all-vars1 term nil)) ; Progn. ; The definition of er-progn-fn below exposes a deficiency in ACL2 not ; present in full Common Lisp, namely ACL2's inability to generate a ; really ``new'' variable the way one can in a Common Lisp macro via ; gensym. One would like to be sure that in binding the two variables ; er-progn-not-to-be-used-elsewhere-erp ; er-progn-not-to-be-used-elsewhere-val that they were not used ; anywhere in the subsequent macro expansion of lst. If one had the ; macro expansion of lst at hand, one could manufacture a variable ; that was not free in the expansion with genvars, and that would do. ; As a less than elegant rememdy to the situation, we introduce below ; the macro check-vars-not-free, which takes two arguments, the first ; a not-to-be-evaluated list of variable names and the second an ; expression. We arrange to return the translation of the expression ; provided none of the variables occur freely in it. Otherwise, an error ; is caused. The situation is subtle because we cannot even obtain ; the free vars in an expression until it has been translated. For ; example, (value x) has the free var STATE in it, thanks to the macro ; expansion of value. But a macro can't call translate because macros ; can't get their hands on state. ; In an earlier version of this we built check-vars-not-free into ; translate itself. We defined it with a defmacro that expanded to ; its second arg, but translate did not actually look at the macro ; (raw lisp did) and instead implemented the semantics described ; above. Of course, if no error was caused the semantics agreed with ; the treatment and if an error was caused, all bets are off anyway. ; The trouble with that approach was that it worked fine as long as ; check-vars-not-free was the only such example we had of needing to ; look at the translated form of something in a macro. Unfortunately, ; others came along. So we invented the more general ; translate-and-test and now use it to define check-vars-not-free. (defmacro translate-and-test (test-fn form) ; Test-fn should be a LAMBDA expression (or function or macro symbol) ; of one non-STATE argument, and form is an arbitrary form. Logically ; we ignore test-fn and return form. However, an error is caused by ; TRANSLATE if the translation of form is not "approved" by test-fn. ; By "approved" we mean that when (test-fn 'term) is evaluated, where ; term is the translation of form, (a) the evaluation completes ; without an error and (b) the result is T. Otherwise, the result is ; treated as an error msg and displayed. (Actually, test-fn's answer ; is treated as an error msg if it is a stringp or a consp. Any other ; result, e.g., T or NIL (!), is treated as "approved.") If test-fn ; approves then the result of translation is the translation of form. ; For example, ; (translate-and-test ; (lambda (term) ; (or (subsetp (all-vars term) '(x y z)) ; (msg "~x0 uses variables other than x, y, and z." ; term))) ; ) ; is just the translation of provided that translation ; only involves the free vars x, y, and z; otherwise an error is ; caused. By generating calls of this macro other macros can ; ensure that the s they generate satisfy certain tests ; after those s are translated. ; This macro is actually implemented in translate. It can't be ; implemented here because translate isn't defined yet. However the ; semantics is consistent with the definition below, namely, it just ; expands to its second argument (which is, of course, translated). ; It is just that sometimes errors are caused. ; There are two tempting generalizations of this function. The first ; is that test-fn should be passed STATE so that it can make more ; "semantic" checks on the translation of form and perhaps so that it ; can signal the error itself. There is, as far as I know, ; nothing wrong with this generalization except that it is hard to ; implement. In order for TRANSLATE to determine whether test-fn ; approves of the term it must ev an expression. If that expression ; involved STATE then translated must pass in its STATE in that ; position. This requires coercing the state to an object, an act ; which is done with some trepidation in trans-eval and which could, ; presumably, be allowed earlier in translate. ; The second tempting generalization is that test-fn should have the ; power to massage the translation and return a new form which should, ; in turn, be translated. For example, then one could imagine, say, a ; macro that would permit a form to be turned into the quoted constant ; listing the variables that occur freely in the translated form. If ; the first generalization above has been carried out, then this would ; permit the translation of a form to be state dependent, which is ; illegal. But this second generalization is problematic anyway. In ; particular, what is the raw lisp counterpart of the generalized ; macro? Note that in its current incarnation, the raw lisp ; counterpart of translate-and-test is the same as its logical ; meaning: it just expands to its second arg. But if the desired ; expansion is computed from the translation of its second arg, then ; raw lisp would have to translate that argument. But we can't do ; that for a variety of reasons: (a) CLTL macros shouldn't be state ; dependent, (b) we can't call translate during compilation because in ; general the ACL2 world isn't present, etc. (declare (ignore test-fn)) form) ; Intersectp (defun intersectp-eq-exec (x y) (declare (xargs :guard (and (true-listp x) (true-listp y) (or (symbol-listp x) (symbol-listp y))))) (cond ((endp x) nil) ((member-eq (car x) y) t) (t (intersectp-eq-exec (cdr x) y)))) (defun intersectp-eql-exec (x y) (declare (xargs :guard (and (true-listp x) (true-listp y) (or (eqlable-listp x) (eqlable-listp y))))) (cond ((endp x) nil) ((member (car x) y) t) (t (intersectp-eql-exec (cdr x) y)))) (defun intersectp-equal (x y) (declare (xargs :guard (and (true-listp x) (true-listp y)))) (cond ((endp x) nil) ((member-equal (car x) y) t) (t (intersectp-equal (cdr x) y)))) (defmacro intersectp-eq (x y) `(intersectp ,x ,y :test 'eq)) (defthm intersectp-eq-exec-is-intersectp-equal (equal (intersectp-eq-exec x y) (intersectp-equal x y))) (defthm intersectp-eql-exec-is-intersectp-equal (equal (intersectp-eql-exec x y) (intersectp-equal x y))) (defmacro intersectp (x y &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins test whether two lists intersect~/ ~bv[] General Forms: (set-difference$ l1 l2) (set-difference$ l1 l2 :test 'eql) ; same as above (eql as equality test) (set-difference$ l1 l2 :test 'eq) ; same, but eq is equality test (set-difference$ l1 l2 :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Intersectp l1 l2)] returns ~c[t] if ~c[l1] and ~c[l2] have a ~ilc[member] in common, else it returns ~c[nil]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing members of the two lists.~/ The ~il[guard] for a call of ~c[intersectp] depends on the test. In all cases, both arguments must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then one of the arguments must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then one of the arguments must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[intersectp] and its variants: ~bq[] ~c[(intersectp-eq x lst)] is equivalent to ~c[(intersectp x lst :test 'eq)]; ~c[(intersectp-equal x lst)] is equivalent to ~c[(intersectp x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[intersectp-equal].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (y ,y)) :logic (intersectp-equal x y) :exec (intersectp-eq-exec x y))) ((equal test ''eql) `(let-mbe ((x ,x) (y ,y)) :logic (intersectp-equal x y) :exec (intersectp-eql-exec x y))) (t ; (equal test 'equal) `(intersectp-equal ,x ,y)))) (defun make-fmt-bindings (chars forms) (declare (xargs :guard (and (true-listp chars) (true-listp forms) (<= (length forms) (length chars))))) (cond ((endp forms) nil) (t (list 'cons (list 'cons (car chars) (car forms)) (make-fmt-bindings (cdr chars) (cdr forms)))))) (defmacro msg (str &rest args) ; Fmt is defined much later. But we need msg now because several of our macros ; generate calls of msg and thus msg must be a function when terms using those ; macros are translated. ":Doc-Section ACL2::ACL2-built-ins construct a ``message'' suitable for the ~c[~~@] directive of ~ilc[fmt]~/ ~l[fmt] for background on formatted printing with ACL2. We document ~c[msg] precisely below, but first, we give an informal introduction and illustrate with an example. Suppose you are writing a program that is to do some printing. Typically, you will either pass the ACL2 state around (~pl[programming-with-state]) and use formatted printing functions that take the state as an argument (~pl[fmt])), or else you might avoid using state by calling the utility, ~ilc[cw], to do you printing. Alternatively, you might print error messages upon encountering illegal situations; ~pl[er]. But there are times where instead of printing immediately, you may prefer to pass messages around, for example to accumulate them before printing a final message. In such cases, it may be desirable to construct ``message'' objects to pass around. For example, consider the following pair of little programs. The first either performs a computation or prints an error, and the second calls the first on two inputs. ~bv[] (defun invert1 (x) (if (consp x) (cons (cdr x) (car x)) (prog2$ (cw \"ERROR: ~~x0 expected a cons, but was given ~~x1.~~|\" 'invert1 x) nil))) (defun invert2 (x1 x2) (list (invert1 x1) (invert1 x2))) ~ev[] For example: ~bv[] ACL2 !>(invert1 '(3 . 4)) (4 . 3) ACL2 !>(invert1 'a) ERROR: INVERT1 expected a cons, but was given A. NIL ACL2 !>(invert2 '(3 . 4) '(5 . 6)) ((4 . 3) (6 . 5)) ACL2 !>(invert2 'a 'b) ERROR: INVERT1 expected a cons, but was given A. ERROR: INVERT1 expected a cons, but was given B. (NIL NIL) ACL2 !> ~ev[] Notice that when there are errors, there is no attempt to explain that these are due to a call of ~c[invert2]. That could be fixed, of course, by arranging for ~c[invert2] to print its own error; but for complicated programs it can be awkward to coordinate printing from many sources. So let's try a different approach. This time, the first function returns two results. The first result is an ``error indicator'' ~-[] either a message object or ~c[nil] ~-[] while the second is the computed value (only of interest when the first result is ~c[nil]). Then the higher-level function can print a single error message that includes the error message(s) from the lower-level function, as shown below. ~bv[] (defun invert1a (x) (if (consp x) (mv nil (cons (cdr x) (car x))) (mv (msg \"ERROR: ~~x0 expected a cons, but was given ~~x1.~~|\" 'invert1a x) nil))) (defun invert2a (x1 x2) (mv-let (erp1 y1) (invert1a x1) (mv-let (erp2 y2) (invert1a x2) (if erp1 (if erp2 (cw \"~~x0 failed with two errors:~~| ~~@1 ~~@2\" 'invert2a erp1 erp2) (cw \"~~x0 failed with one error:~~| ~~@1\" 'invert2a erp1)) (if erp2 (cw \"~~x0 failed with one error:~~| ~~@1\" 'invert2a erp2) (list y1 y2)))))) ~ev[] For example: ~bv[] ACL2 !>(invert2a '(3 . 4) '(5 . 6)) ((4 . 3) (6 . 5)) ACL2 !>(invert2a '(3 . 4) 'b) INVERT2A failed with one error: ERROR: INVERT1A expected a cons, but was given B. NIL ACL2 !>(invert2a 'a 'b) INVERT2A failed with two errors: ERROR: INVERT1A expected a cons, but was given A. ERROR: INVERT1A expected a cons, but was given B. NIL ACL2 !> ~ev[] If you study the example above, you might well understand ~c[msg]. But we conclude with precise documentation.~/ ~bv[] General Form: (msg str arg1 ... argk) ~ev[] where ~c[str] is a string and ~c[k] is at most 9. This macro returns a pair suitable for giving to the ~c[fmt] directive ~c[~~@]. Thus, suppose that ~c[#\\c] is bound to the value of ~c[(msg str arg1 ... argk)], where ~c[c] is a character and ~c[k] is at most 9. Then the ~c[fmt] directive ~c[~~@c] will print out the string, ~c[str], in the context of the alist in which the successive ~c[fmt] variables ~c[#\\0], ~c[#\\1], ..., ~c[#\\k] are bound to the successive elements of ~c[(arg1 ... argk)].~/" (declare (xargs :guard (<= (length args) 10))) `(cons ,str ,(make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) args))) (defun check-vars-not-free-test (vars term) (declare (xargs :guard (and (symbol-listp vars) (pseudo-termp term)) :verify-guards nil)) (or (not (intersectp-eq vars (all-vars term))) (msg "It is forbidden to use ~v0 in ~x1." vars term))) (defmacro check-vars-not-free (vars form) ; A typical use of this macro is (check-vars-not-free (my-erp my-val) ...) ; which just expands to the translation of ... provided my-erp and my-val do ; not occur freely in it. ; We wrap the body of the lambda into a simple function call, because ; translate11 calls ev-w on it and we want to avoid having lots of ev-rec ; calls, especially since intersectp-eq expands to an mbe call. (declare (xargs :guard (symbol-listp vars))) `(translate-and-test (lambda (term) (check-vars-not-free-test ',vars term)) ,form)) (defun er-progn-fn (lst) ; Keep in sync with er-progn-fn@par. (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((endp (cdr lst)) (car lst)) (t (list 'mv-let '(er-progn-not-to-be-used-elsewhere-erp er-progn-not-to-be-used-elsewhere-val state) (car lst) ; Avoid possible warning after optimized compilation: '(declare (ignorable er-progn-not-to-be-used-elsewhere-val)) (list 'if 'er-progn-not-to-be-used-elsewhere-erp '(mv er-progn-not-to-be-used-elsewhere-erp er-progn-not-to-be-used-elsewhere-val state) (list 'check-vars-not-free '(er-progn-not-to-be-used-elsewhere-erp er-progn-not-to-be-used-elsewhere-val) (er-progn-fn (cdr lst)))))))) (defmacro er-progn (&rest lst) ; Keep in sync with er-progn@par. ":Doc-Section ACL2::ACL2-built-ins perform a sequence of state-changing ``error triples''~/ ~bv[] Example: (er-progn (check-good-foo-p (f-get-global 'my-foo state) state) (value (* (f-get-global 'my-foo state) (f-get-global 'bar state)))) ~ev[] This sequencing primitive is only useful when programming with ~il[state], something that very few users will probably want to do. ~l[state].~/ ~c[Er-progn] is used much the way that ~ilc[progn] is used in Common Lisp, except that it expects each form within it to evaluate to an ``error triple'' of the form ~c[(mv erp val state)]; ~pl[error-triples]. The first such form, if any, that evaluates to such a triple where ~c[erp] is not ~c[nil] yields the error triple returned by the ~c[er-progn]. If there is no such form, then the last form returns the value of the ~c[er-progn] form. ~bv[] General Form: (er-progn ... ) ~ev[] where each ~c[] is an expression that evaluates to an error triple (~pl[programming-with-state]). The above form is essentially equivalent to the following (``essentially'' because in fact, care is taken to avoid variable capture). ~bv[] (mv-let (erp val state) (cond (erp (mv erp val state)) (t (mv-let (erp val state) (cond (erp (mv erp val state)) (t ... (mv-let (erp val state) (cond (erp (mv erp val state)) (t ))))))))) ~ev[]~/" (declare (xargs :guard (and (true-listp lst) lst))) (er-progn-fn lst)) #+acl2-par (defun er-progn-fn@par (lst) ; Keep in sync with er-progn-fn. (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((endp (cdr lst)) (car lst)) (t (list 'mv-let '(er-progn-not-to-be-used-elsewhere-erp er-progn-not-to-be-used-elsewhere-val) (car lst) ; Avoid possible warning after optimized compilation: '(declare (ignorable er-progn-not-to-be-used-elsewhere-val)) (list 'if 'er-progn-not-to-be-used-elsewhere-erp '(mv er-progn-not-to-be-used-elsewhere-erp er-progn-not-to-be-used-elsewhere-val) (list 'check-vars-not-free '(er-progn-not-to-be-used-elsewhere-erp er-progn-not-to-be-used-elsewhere-val) (er-progn-fn@par (cdr lst)))))))) #+acl2-par (defmacro er-progn@par (&rest lst) ; Keep in sync with er-progn. ":Doc-Section ACL2::ACL2-built-ins State-free version of ~ilc[er-progn].~/ ~/~/" (declare (xargs :guard (and (true-listp lst) lst))) (er-progn-fn@par lst)) (defun legal-case-clausesp (tl) (declare (xargs :guard t)) (cond ((atom tl) (eq tl nil)) ((and (consp (car tl)) (or (eqlablep (car (car tl))) (eqlable-listp (car (car tl)))) (consp (cdr (car tl))) (null (cdr (cdr (car tl)))) (if (or (eq t (car (car tl))) (eq 'otherwise (car (car tl)))) (null (cdr tl)) t)) (legal-case-clausesp (cdr tl))) (t nil))) (defun case-test (x pat) (declare (xargs :guard t)) (cond ((atom pat) (list 'eql x (list 'quote pat))) (t (list 'member x (list 'quote pat))))) (defun case-list (x l) (declare (xargs :guard (legal-case-clausesp l))) (cond ((endp l) nil) ((or (eq t (car (car l))) (eq 'otherwise (car (car l)))) (list (list 't (car (cdr (car l)))))) ((null (car (car l))) (case-list x (cdr l))) (t (cons (list (case-test x (car (car l))) (car (cdr (car l)))) (case-list x (cdr l)))))) (defun case-list-check (l) (declare (xargs :guard (legal-case-clausesp l))) (cond ((endp l) nil) ((or (eq t (car (car l))) (eq 'otherwise (car (car l)))) (list (list 't (list 'check-vars-not-free '(case-do-not-use-elsewhere) (car (cdr (car l))))))) ((null (car (car l))) (case-list-check (cdr l))) (t (cons (list (case-test 'case-do-not-use-elsewhere (car (car l))) (list 'check-vars-not-free '(case-do-not-use-elsewhere) (car (cdr (car l))))) (case-list-check (cdr l)))))) #+acl2-loop-only (defmacro case (&rest l) ":Doc-Section ACL2::ACL2-built-ins conditional based on if-then-else using ~ilc[eql]~/ ~bv[] Example Form: (case typ ((:character foo) (open file-name :direction :output)) (bar (open-for-bar file-name)) (otherwise (my-error \"Illegal.\"))) ~ev[] is the same as ~bv[] (cond ((member typ '(:character foo)) (open file-name :direction :output)) ((eql typ 'bar) (open-for-bar file-name)) (t (my-error \"Illegal.\"))) ~ev[] which in turn is the same as ~bv[] (if (member typ '(:character foo)) (open file-name :direction :output) (if (eql typ 'bar) (open-for-bar file-name) (my-error \"Illegal.\")))~/ ~ev[] Notice the quotations that appear in the example above: ~c['(:character foo)] and ~c['bar]. ~bv[] General Forms: (case expr (x1 val-1) ... (xk val-k) (otherwise val-k+1)) (case expr (x1 val-1) ... (xk val-k) (t val-k+1)) (case expr (x1 val-1) ... (xk val-k)) ~ev[] where each ~c[xi] is either ~ilc[eqlablep] or a true list of ~ilc[eqlablep] objects. The final ~c[otherwise] or ~c[t] case is optional. ~c[Case] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (and (consp l) (legal-case-clausesp (cdr l))))) (cond ((atom (car l)) (cons 'cond (case-list (car l) (cdr l)))) (t `(let ((case-do-not-use-elsewhere ,(car l))) (cond ,@(case-list-check (cdr l))))))) ; Position-ac (defun position-ac-eq-exec (item lst acc) (declare (xargs :guard (and (true-listp lst) (or (symbolp item) (symbol-listp lst)) (acl2-numberp acc)))) (cond ((endp lst) nil) ((eq item (car lst)) acc) (t (position-ac-eq-exec item (cdr lst) (1+ acc))))) (defun position-ac-eql-exec (item lst acc) (declare (xargs :guard (and (true-listp lst) (or (eqlablep item) (eqlable-listp lst)) (acl2-numberp acc)))) (cond ((endp lst) nil) ((eql item (car lst)) acc) (t (position-ac-eql-exec item (cdr lst) (1+ acc))))) (defun position-equal-ac (item lst acc) ; This function should perhaps be called position-ac-equal, but we name it ; position-equal-ac since that has been its name historically before the new ; handling of member etc. after Version_4.2. (declare (xargs :guard (and (true-listp lst) (acl2-numberp acc)))) (cond ((endp lst) nil) ((equal item (car lst)) acc) (t (position-equal-ac item (cdr lst) (1+ acc))))) (defmacro position-ac-equal (item lst acc) ; See comment about naming in position-equal-ac. `(position-equal-ac ,item ,lst ,acc)) (defmacro position-eq-ac (item lst acc) ; This macro may be oddly named; see the comment about naming in ; position-equal-ac. We also define position-ac-eq, which may be a more ; appropriate name. `(position-ac ,item ,lst ,acc :test 'eq)) (defmacro position-ac-eq (item lst acc) `(position-ac ,item ,lst ,acc :test 'eq)) (defthm position-ac-eq-exec-is-position-equal-ac (equal (position-ac-eq-exec item lst acc) (position-equal-ac item lst acc))) (defthm position-ac-eql-exec-is-position-equal-ac (equal (position-ac-eql-exec item lst acc) (position-equal-ac item lst acc))) (defmacro position-ac (item lst acc &key (test ''eql)) (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((item ,item) (lst ,lst) (acc ,acc)) :logic (position-equal-ac item lst) :exec (position-ac-eq-exec item lst))) ((equal test ''eql) `(let-mbe ((item ,item) (lst ,lst) (acc ,acc)) :logic (position-equal-ac item lst acc) :exec (position-ac-eql-exec item lst acc))) (t ; (equal test 'equal) `(position-equal-ac ,item ,lst)))) ; Position (defun position-eq-exec (item lst) (declare (xargs :guard (and (true-listp lst) (or (symbolp item) (symbol-listp lst))))) (position-ac-eq-exec item lst 0)) (defun position-eql-exec (item lst) (declare (xargs :guard (or (stringp lst) (and (true-listp lst) (or (eqlablep item) (eqlable-listp lst)))))) (if (stringp lst) (position-ac item (coerce lst 'list) 0) (position-ac item lst 0))) (defun position-equal (item lst) (declare (xargs :guard (or (stringp lst) (true-listp lst)))) #-acl2-loop-only ; for assoc-eq, Jared Davis found native assoc efficient (position item lst :test #'equal) #+acl2-loop-only (if (stringp lst) (position-ac item (coerce lst 'list) 0) (position-equal-ac item lst 0))) (defmacro position-eq (item lst) `(position ,item ,lst :test 'eq)) (defthm position-eq-exec-is-position-equal (implies (not (stringp lst)) (equal (position-eq-exec item lst) (position-equal item lst)))) (defthm position-eql-exec-is-position-equal (equal (position-eql-exec item lst) (position-equal item lst))) #+acl2-loop-only (defmacro position (x seq &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins position of an item in a string or a list~/ ~bv[] General Forms: (position x seq) (position x seq :test 'eql) ; same as above (eql as equality test) (position x seq :test 'eq) ; same, but eq is equality test (position x seq :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Position x seq)] is the least index (zero-based) of the element ~c[x] in the string or list ~c[seq], if ~c[x] is an element of ~c[seq]. Otherwise ~c[(position x seq)] is ~c[nil]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing ~c[x] with items of ~c[seq].~/ The ~il[guard] for a call of ~c[position] depends on the test. In all cases, the second argument must satisfy ~ilc[stringp] or ~ilc[true-listp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[position] and its variants: ~bq[] ~c[(position-eq x seq)] is equivalent to ~c[(position x seq :test 'eq)]; ~c[(position-equal x seq)] is equivalent to ~c[(position x seq :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[position-equal]. ~c[Position] is defined by Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (seq ,seq)) :logic (position-equal x seq) :exec (position-eq-exec x seq))) ((equal test ''eql) `(let-mbe ((x ,x) (seq ,seq)) :logic (position-equal x seq) :exec (position-eql-exec x seq))) (t ; (equal test 'equal) `(position-equal ,x ,seq)))) (defun nonnegative-integer-quotient (i j) ":Doc-Section ACL2::ACL2-built-ins natural number division function~/ ~bv[] Example Forms: (nonnegative-integer-quotient 14 3) ; equals 4 (nonnegative-integer-quotient 15 3) ; equals 5 ~ev[] ~c[(nonnegative-integer-quotient i j)] returns the integer quotient of the integers ~c[i] and (non-zero) ~c[j], i.e., the largest ~c[k] such that ~c[(* j k)] is less than or equal to ~c[i]. Also ~pl[floor], ~pl[ceiling] and ~pl[truncate], which are derived from this function and apply to rational numbers.~/ The ~il[guard] of ~c[(nonnegative-integer-quotient i j)] requires that ~c[i] is a nonnegative integer and ~c[j] is a positive integer. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (not (< i 0)) (integerp j) (< 0 j)))) #-acl2-loop-only ; See community book books/misc/misc2/misc.lisp for justification. (values (floor i j)) #+acl2-loop-only (if (or (= (nfix j) 0) (< (ifix i) j)) 0 (+ 1 (nonnegative-integer-quotient (- i j) j)))) ; Next we develop let* in the logic. (defun true-list-listp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for true (proper) lists of true lists~/ ~c[True-list-listp] is the function that checks whether its argument is a list that ends in, or equals, ~c[nil], and furthermore, all of its elements have that property. Also ~pl[true-listp]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (true-listp (car x)) (true-list-listp (cdr x)))))) (defthm true-list-listp-forward-to-true-listp (implies (true-list-listp x) (true-listp x)) :rule-classes :forward-chaining) (defun legal-let*-p (bindings ignore-vars ignored-seen top-form) ; We check that no variable declared ignored or ignorable is bound twice. We ; also check that all ignored-vars are bound. We could leave it to translate ; to check the resulting LET form instead, but we prefer to do the check here, ; both in order to clarify the problem for the user (the blame will be put on ; the LET* form) and because we are not sure of the Common Lisp treatment of ; such a LET* and could thus be in unknown territory were we ever to relax the ; corresponding restriction on LET. ; Ignored-seen should be nil at the top level. (declare (xargs :guard (and top-form ; to avoid irrelevance (symbol-alistp bindings) (symbol-listp ignore-vars) (symbol-listp ignored-seen)))) (cond ((endp bindings) (or (eq ignore-vars nil) (hard-error 'let* "All variables declared IGNOREd or IGNORABLE in a ~ LET* form must be bound, but ~&0 ~#0~[is~/are~] not ~ bound in the form ~x1." (list (cons #\0 ignore-vars) (cons #\1 top-form))))) ((member-eq (caar bindings) ignored-seen) (hard-error 'let* "A variable bound more than once in a LET* form may not ~ be declared IGNOREd or IGNORABLE, but the variable ~x0 ~ is bound more than once in form ~x1 and yet is so ~ declared." (list (cons #\0 (caar bindings)) (cons #\1 top-form)))) ((member-eq (caar bindings) ignore-vars) (legal-let*-p (cdr bindings) (remove (caar bindings) ignore-vars) (cons (caar bindings) ignored-seen) top-form)) (t (legal-let*-p (cdr bindings) ignore-vars ignored-seen top-form)))) (defun well-formed-type-decls-p (decls vars) ; Decls is a true list of declarations (type tp var1 ... vark). We check that ; each vari is bound in vars. (declare (xargs :guard (and (true-list-listp decls) (symbol-listp vars)))) (cond ((endp decls) t) ((subsetp-eq (cddr (car decls)) vars) (well-formed-type-decls-p (cdr decls) vars)) (t nil))) (defun symbol-list-listp (x) (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (symbol-listp (car x)) (symbol-list-listp (cdr x)))))) (defun get-type-decls (var type-decls) (declare (xargs :guard (and (symbolp var) (true-list-listp type-decls) (alistp type-decls) (symbol-list-listp (strip-cdrs type-decls))))) (cond ((endp type-decls) nil) ((member-eq var (cdr (car type-decls))) (cons (list 'type (car (car type-decls)) var) (get-type-decls var (cdr type-decls)))) (t (get-type-decls var (cdr type-decls))))) (defun let*-macro (bindings ignore-vars ignorable-vars type-decls body) (declare (xargs :guard (and (symbol-alistp bindings) (symbol-listp ignore-vars) (symbol-listp ignorable-vars) (true-list-listp type-decls) (alistp type-decls) (symbol-list-listp (strip-cdrs type-decls))))) (cond ((endp bindings) (prog2$ (or (null ignore-vars) (hard-error 'let*-macro "Implementation error: Ignored variables ~x0 ~ must be bound in superior LET* form!" ignore-vars)) (prog2$ (or (null ignorable-vars) (hard-error 'let*-macro "Implementation error: Ignorable ~ variables ~x0 must be bound in ~ superior LET* form!" ignorable-vars)) body))) (t ; (consp bindings) (cons 'let (cons (list (car bindings)) (let ((rest (let*-macro (cdr bindings) (remove (caar bindings) ignore-vars) (remove (caar bindings) ignorable-vars) type-decls body))) (append (and (member-eq (caar bindings) ignore-vars) (list (list 'declare (list 'ignore (caar bindings))))) (and (member-eq (caar bindings) ignorable-vars) (list (list 'declare (list 'ignorable (caar bindings))))) (let ((var-type-decls (get-type-decls (caar bindings) type-decls))) (and var-type-decls (list (cons 'declare var-type-decls)))) (list rest)))))))) (defun collect-cdrs-when-car-eq (x alist) (declare (xargs :guard (and (symbolp x) (true-list-listp alist)))) (cond ((endp alist) nil) ((eq x (car (car alist))) (append (cdr (car alist)) (collect-cdrs-when-car-eq x (cdr alist)))) (t (collect-cdrs-when-car-eq x (cdr alist))))) (defun append-lst (lst) (declare (xargs :guard (true-list-listp lst))) (cond ((endp lst) nil) (t (append (car lst) (append-lst (cdr lst)))))) (defun restrict-alist (keys alist) ; Returns the subsequence of alist whose cars are among keys (without any ; reordering). (declare (xargs :guard (and (symbol-listp keys) (alistp alist)))) (cond ((endp alist) nil) ((member-eq (caar alist) keys) (cons (car alist) (restrict-alist keys (cdr alist)))) (t (restrict-alist keys (cdr alist))))) #+acl2-loop-only (defmacro let* (&whole form bindings &rest decl-body) ":Doc-Section ACL2::ACL2-built-ins binding of lexically scoped (local) variables~/ ~bv[] Example LET* Forms: (let* ((x (* x x)) (y (* 2 x))) (list x y)) (let* ((x (* x x)) (y (* 2 x)) (x (* x y)) (a (* x x))) (declare (ignore a)) (list x y)) ~ev[] If the forms above are executed in an environment in which ~c[x] has the value ~c[-2], then the respective results are ~c['(4 8)] and ~c['(32 8)]. ~l[let] for a discussion of both ~ilc[let] and ~c[let*], or read on for a briefer discussion.~/ The difference between ~ilc[let] and ~c[let*] is that the former binds its local variables in parallel while the latter binds them sequentially. Thus, in ~c[let*], the term evaluated to produce the local value of one of the locally bound variables is permitted to reference any locally bound variable occurring earlier in the binding list and the value so obtained is the newly computed local value of that variable. ~l[let]. In ACL2 the only ~ilc[declare] forms allowed for a ~c[let*] form are ~c[ignore], ~c[ignorable], and ~c[type]. ~l[declare]. Moreover, no variable declared ~c[ignore]d or ~c[ignorable] may be bound more than once. A variable with a type declaration may be bound more than once, in which case the type declaration is treated by ACL2 as applying to each binding occurrence of that variable. It seems unclear from the Common Lisp spec whether the underlying Lisp implementation is expected to apply such a declaration to more than one binding occurrence, however, so performance in such cases may depend on the underlying Lisp. ~c[Let*] is a Common Lisp macro. See any Common Lisp documentation for more information.~/" (declare (xargs :guard ; We do not check that the variables declared ignored are not free in the body, ; nor do we check that variables bound in bindings that are used in the body ; are not declared ignored. Those properties will be checked for the expanded ; LET form, as appropriate. (and (symbol-alistp bindings) (true-listp decl-body) decl-body (let ((declare-forms (butlast decl-body 1))) (and (alistp declare-forms) (subsetp-eq (strip-cars declare-forms) '(declare)) (let ((decls (append-lst (strip-cdrs declare-forms)))) (let ((ign-decls (restrict-alist '(ignore ignorable) decls)) (type-decls (restrict-alist '(type) decls))) (and (symbol-alistp decls) (symbol-list-listp ign-decls) (subsetp-eq (strip-cars decls) '(ignore ignorable type)) (well-formed-type-decls-p type-decls (strip-cars bindings)) (legal-let*-p bindings (append-lst (strip-cdrs ign-decls)) nil form))))))))) (declare (ignore form)) (let ((decls (append-lst (strip-cdrs (butlast decl-body 1)))) (body (car (last decl-body)))) (let ((ignore-vars (collect-cdrs-when-car-eq 'ignore decls)) (ignorable-vars (collect-cdrs-when-car-eq 'ignorable decls)) (type-decls (strip-cdrs (restrict-alist '(type) decls)))) (let*-macro bindings ignore-vars ignorable-vars type-decls body)))) #+acl2-loop-only (defmacro progn (&rest r) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section ACL2::Events evaluate some ~il[events]~/ ~bv[] Example Form: (progn (defun foo (x) x) (defmacro my-defun (&rest args) (cons 'defun args)) (my-defun bar (x) (foo x))) General form: (progn event1 event2 ... eventk) ~ev[] where ~c[k] >= 0 and each ~c[eventi] is a legal embedded event form (~pl[embedded-event-form]). These events are evaluated in sequence. A utility is provided to assist in debugging failures of such execution; ~pl[redo-flat]. NOTE: If the ~c[eventi] above are not all legal embedded event forms (~pl[embedded-event-form]), consider using ~ilc[er-progn] or (with great care!) ~ilc[progn!] instead. For a related event form that does allow introduction of ~il[constraint]s and ~ilc[local] ~il[events], ~pl[encapsulate]. ACL2 does not allow the use of ~c[progn] in definitions. Instead, the macro ~ilc[er-progn] can be used for sequencing ~il[state]-oriented operations; ~pl[er-progn] and ~pl[state]. If you are using single-threaded objects (~pl[stobj]) you may wish to define a version of ~ilc[er-progn] that cascades the object through successive changes. ACL2's ~ilc[pprogn] is the ~c[state] analogue of such a macro. If your goal is simply to execute a sequence of top-level forms, for example a sequence of definitions, consider using ~c[ld] instead; ~pl[ld].~/~/" ; Like defun, defmacro, and in-package, progn does not have quite the same ; semantics as the Common Lisp function. This is useful only for sequences at ; the top level. It permits us to handle things like type sets and records. (list 'progn-fn (list 'quote r) 'state)) #+(and :non-standard-analysis (not acl2-loop-only)) (defun floor1 (x) ; See "RAG" comment in the definition of floor for an explanation of why we ; need this function. (floor x 1)) #+acl2-loop-only (progn (defdoc real ":Doc-Section ACL2::Real ACL2(r) support for real numbers~/ ACL2 supports rational numbers but not real numbers. However, starting with Version 2.5, a variant of ACL2 called ``ACL2(r)'' supports the real numbers by way of non-standard analysis. ACL2(r) was conceived and first implemented by Ruben Gamboa in his Ph.D. dissertation work, supervised by Bob Boyer with active participation by Matt Kaufmann. ACL2(r) has the same source files as ACL2. After you download ACL2, you can build ACL2(r) by executing the following command on the command line in your acl2-sources directory, replacing ~c[] with a path to your Lisp executable: ~bv[] make large-acl2r LISP= ~ev[] This will create an executable in your acl2-sources directory named ~c[saved_acl2r]. Note that if you download community books as tarfiles, then you should be sure to download the `nonstd' books, from ~url[http://acl2-books.googlecode.com/files/nonstd-6.3.tar.gz]. Then certify them from your acl2-sources directory, shown here as ~c[]: ~bv[] make regression-nonstd ACL2=/saved_acl2r ~ev[] To check that you are running ACL2(r), see if the prompt includes the string ``~c[(r)]'', e.g.: ~bv[] ACL2(r) !> ~ev[] Or, look at ~c[(@ acl2-version)] and see if ``~c[(r)]'' is a substring. In ACL2 (as opposed to ACL2(r)), when we say ``real'' we mean ``rational.''~/ Caution: ACL2(r) should be considered experimental: although we (Kaufmann and Moore) have carefully completed Gamboa's integration of the reals into the ACL2 source code, our primary concern has been to ensure unchanged behavior when ACL2 is compiled in the default manner, i.e., without the non-standard extensions. As for every release of ACL2, at the time of a release we are unaware of soundness bugs in ACL2 or ACL2(r). There is only limited documentation on the non-standard features of ACL2(r). We hope to provide more documentation for such features in future releases. Please feel free to query the authors if you are interested in learning more about ACL2(r). Gamboa's dissertation may also be helpful.~/") (defun floor (i j) ;; RAG - This function had to be modified in a major way. It was ;; originally defined only for rationals, and it used the fact that ;; the floor of "p/q" could be found by repeatedly subtracting "q" ;; from "p" (roughly speaking). This same trick, sadly, does not work ;; for the reals. Instead, we need something similar to the ;; archimedean axiom. Our version thereof is the _undefined_ function ;; "floor1", which takes a single argument and returns an integer ;; equal to it or smaller to it by no more than 1. Using this ;; function, we can define the more general floor function offered ;; below. ":Doc-Section ACL2::ACL2-built-ins division returning an integer by truncating toward negative infinity~/ ~bv[] Example Forms: ACL2 !>(floor 14 3) 4 ACL2 !>(floor -14 3) -5 ACL2 !>(floor 14 -3) -5 ACL2 !>(floor -14 -3) 4 ACL2 !>(floor -15 -3) 5 ~ev[] ~c[(Floor i j)] returns the result of taking the quotient of ~c[i] and ~c[j] and returning the greatest integer not exceeding that quotient. For example, the quotient of ~c[-14] by ~c[3] is ~c[-4 2/3], and the largest integer not exceeding that rational number is ~c[-5].~/ The ~il[guard] for ~c[(floor i j)] requires that ~c[i] and ~c[j] are rational (~il[real], in ACL2(r)) numbers and ~c[j] is non-zero. ~c[Floor] is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 ~c[floor] function returns only a single value, To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp i) (real/rationalp j) (not (eql j 0))))) #+:non-standard-analysis (let ((q (* i (/ j)))) (cond ((integerp q) q) ((rationalp q) (if (>= q 0) (nonnegative-integer-quotient (numerator q) (denominator q)) (+ (- (nonnegative-integer-quotient (- (numerator q)) (denominator q))) -1))) (t (floor1 q)))) #-:non-standard-analysis (let* ((q (* i (/ j))) (n (numerator q)) (d (denominator q))) (cond ((= d 1) n) ((>= n 0) (nonnegative-integer-quotient n d)) (t (+ (- (nonnegative-integer-quotient (- n) d)) -1)))) ) ;; RAG - This function was also modified to fit in the reals. It's ;; also defined in terms of the _undefined_ function floor1 (which ;; corresponds to the usual unary floor function). (defun ceiling (i j) ":Doc-Section ACL2::ACL2-built-ins division returning an integer by truncating toward positive infinity~/ ~bv[] Example Forms: ACL2 !>(ceiling 14 3) 5 ACL2 !>(ceiling -14 3) -4 ACL2 !>(ceiling 14 -3) -4 ACL2 !>(ceiling -14 -3) 5 ACL2 !>(ceiling -15 -3) 5 ~ev[] ~c[(Ceiling i j)] is the result of taking the quotient of ~c[i] and ~c[j] and returning the smallest integer that is at least as great as that quotient. For example, the quotient of ~c[-14] by ~c[3] is ~c[-4 2/3], and the smallest integer at least that great is ~c[-4].~/ The ~il[guard] for ~c[(ceiling i j)] requires that ~c[i] and ~c[j] are rational (~il[real], in ACL2(r)) numbers and ~c[j] is non-zero. ~c[Ceiling] is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 ~c[ceiling] function returns only a single value, To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp i) (real/rationalp j) (not (eql j 0))))) #+:non-standard-analysis (let ((q (* i (/ j)))) (cond ((integerp q) q) ((rationalp q) (if (>= q 0) (+ (nonnegative-integer-quotient (numerator q) (denominator q)) 1) (- (nonnegative-integer-quotient (- (numerator q)) (denominator q))))) ((realp q) (1+ (floor1 q))) (t 0))) #-:non-standard-analysis (let* ((q (* i (/ j))) (n (numerator q)) (d (denominator q))) (cond ((= d 1) n) ((>= n 0) (+ (nonnegative-integer-quotient n d) 1)) (t (- (nonnegative-integer-quotient (- n) d))))) ) ;; RAG - Another function modified to fit in the reals, using floor1. (defun truncate (i j) ":Doc-Section ACL2::ACL2-built-ins division returning an integer by truncating toward 0~/ ~bv[] Example Forms: ACL2 !>(truncate 14 3) 4 ACL2 !>(truncate -14 3) -4 ACL2 !>(truncate 14 -3) -4 ACL2 !>(truncate -14 -3) 4 ACL2 !>(truncate -15 -3) 5 ACL2 !>(truncate 10/4 3/4) 3 ~ev[] ~c[(Truncate i j)] is the result of taking the quotient of ~c[i] and ~c[j] and dropping the fraction. For example, the quotient of ~c[-14] by ~c[3] is ~c[-4 2/3], so dropping the fraction ~c[2/3], we obtain a result for ~c[(truncate -14 3)] of ~c[-4].~/ The ~il[guard] for ~c[(truncate i j)] requires that ~c[i] and ~c[j] are rational (~il[real], in ACL2(r)) numbers and ~c[j] is non-zero. ~c[Truncate] is a Common Lisp function. However, note that unlike Common Lisp, the ACL2 ~c[truncate] function returns only a single value, Also ~pl[nonnegative-integer-quotient], which is appropriate for integers and may simplify reasoning, unless a suitable arithmetic library is loaded, but be less efficient for evaluation on concrete objects. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp i) (real/rationalp j) (not (eql j 0))))) #+:non-standard-analysis (let ((q (* i (/ j)))) (cond ((integerp q) q) ((rationalp q) (if (>= q 0) (nonnegative-integer-quotient (numerator q) (denominator q)) (- (nonnegative-integer-quotient (- (numerator q)) (denominator q))))) (t (if (>= q 0) (floor1 q) (- (floor1 (- q))))))) #-:non-standard-analysis (let* ((q (* i (/ j))) (n (numerator q)) (d (denominator q))) (cond ((= d 1) n) ((>= n 0) (nonnegative-integer-quotient n d)) (t (- (nonnegative-integer-quotient (- n) d))))) ) ;; RAG - Another function modified to fit in the reals, using floor1. (defun round (i j) ":Doc-Section ACL2::ACL2-built-ins division returning an integer by rounding off~/ ~bv[] Example Forms: ACL2 !>(round 14 3) 5 ACL2 !>(round -14 3) -5 ACL2 !>(round 14 -3) -5 ACL2 !>(round -14 -3) 5 ACL2 !>(round 13 3) 4 ACL2 !>(round -13 3) -4 ACL2 !>(round 13 -3) -4 ACL2 !>(round -13 -3) 4 ACL2 !>(round -15 -3) 5 ACL2 !>(round 15 -2) -8 ~ev[] ~c[(Round i j)] is the result of taking the quotient of ~c[i] and ~c[j] and rounding off to the nearest integer. When the quotient is exactly halfway between consecutive integers, it rounds to the even one.~/ The ~il[guard] for ~c[(round i j)] requires that ~c[i] and ~c[j] are rational (~il[real], in ACL2(r)) numbers and ~c[j] is non-zero. ~c[Round] is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 ~c[round] function returns only a single value, To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp i) (real/rationalp j) (not (eql j 0))))) (let ((q (* i (/ j)))) (cond ((integerp q) q) ((>= q 0) (let* ((fl (floor q 1)) (remainder (- q fl))) (cond ((> remainder 1/2) (+ fl 1)) ((< remainder 1/2) fl) (t (cond ((integerp (* fl (/ 2))) fl) (t (+ fl 1))))))) (t (let* ((cl (ceiling q 1)) (remainder (- q cl))) (cond ((< (- 1/2) remainder) cl) ((> (- 1/2) remainder) (+ cl -1)) (t (cond ((integerp (* cl (/ 2))) cl) (t (+ cl -1))))))))) ) ;; RAG - I only had to modify the guards here to allow the reals, ;; since this function is defined in terms of the previous ones. (defun mod (x y) ":Doc-Section ACL2::ACL2-built-ins remainder using ~ilc[floor]~/ ~bv[] ACL2 !>(mod 14 3) 2 ACL2 !>(mod -14 3) 1 ACL2 !>(mod 14 -3) -1 ACL2 !>(mod -14 -3) -2 ACL2 !>(mod -15 -3) 0 ACL2 !> ~ev[] ~c[(Mod i j)] is that number ~c[k] that ~c[(* j (floor i j))] added to ~c[k] equals ~c[i].~/ The ~il[guard] for ~c[(mod i j)] requires that ~c[i] and ~c[j] are rational (~il[real], in ACL2(r)) numbers and ~c[j] is non-zero. ~c[Mod] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp x) (real/rationalp y) (not (eql y 0))))) (- x (* (floor x y) y))) (defun rem (x y) ":Doc-Section ACL2::ACL2-built-ins remainder using ~ilc[truncate]~/ ~bv[] ACL2 !>(rem 14 3) 2 ACL2 !>(rem -14 3) -2 ACL2 !>(rem 14 -3) 2 ACL2 !>(rem -14 -3) -2 ACL2 !>(rem -15 -3) 0 ACL2 !> ~ev[] ~c[(Rem i j)] is that number ~c[k] for which ~c[(* j (truncate i j))] added to ~c[k] equals ~c[i].~/ The ~il[guard] for ~c[(rem i j)] requires that ~c[i] and ~c[j] are rational (~il[real], in ACL2(r)) numbers and ~c[j] is non-zero. ~c[Rem] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp x) (real/rationalp y) (not (eql y 0))))) (- x (* (truncate x y) y))) (defun evenp (x) ":Doc-Section ACL2::ACL2-built-ins test whether an integer is even~/ ~c[(evenp x)] is true if and only if the integer ~c[x] is even. Actually, in the ACL2 logic ~c[(evenp x)] is defined to be true when ~c[x/2] is an integer.~/ The ~il[guard] for ~c[evenp] requires its argument to be an integer. ~c[Evenp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (integerp x))) (integerp (* x (/ 2)))) (defun oddp (x) ":Doc-Section ACL2::ACL2-built-ins test whether an integer is odd~/ ~c[(oddp x)] is true if and only if ~c[x] is odd, i.e., not even in the sense of ~ilc[evenp].~/ The ~il[guard] for ~c[oddp] requires its argument to be an integer. ~c[Oddp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (integerp x))) (not (evenp x))) (defun zerop (x) (declare (xargs :mode :logic :guard (acl2-numberp x))) ":Doc-Section ACL2::ACL2-built-ins test an acl2-number against 0~/ ~c[(zerop x)] is ~c[t] if ~c[x] is ~c[0] and is ~c[nil] otherwise. Thus, it is logically equivalent to ~c[(equal x 0)].~/ ~c[(Zerop x)] has a ~il[guard] requiring ~c[x] to be numeric and can be expected to execute more efficiently than ~c[(equal x 0)] in properly ~il[guard]ed compiled code. In recursions down the natural numbers, ~c[(zp x)] is preferred over ~c[(zerop x)] because the former coerces ~c[x] to a natural and allows the termination proof. In recursions through the integers, ~c[(zip x)] is preferred. ~l[zero-test-idioms]. ~c[Zerop] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (eql x 0)) ;; RAG - Only the guard changed here. (defun plusp (x) ":Doc-Section ACL2::ACL2-built-ins test whether a number is positive~/ ~c[(Plusp x)] is true if and only if ~c[x > 0].~/ The ~il[guard] of ~c[plusp] requires its argument to be a rational (~il[real], in ACL2(r)) number. ~c[Plusp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard (real/rationalp x))) (> x 0)) ;; RAG - Only the guard changed here. (defun minusp (x) ":Doc-Section ACL2::ACL2-built-ins test whether a number is negative~/ ~c[(Minusp x)] is true if and only if ~c[x < 0].~/ The ~il[guard] of ~c[minusp] requires its argument to be a rational (~il[real], in ACL2(r)) number. ~c[Minusp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :mode :logic :guard (real/rationalp x))) (< x 0)) ;; RAG - Only the guard changed here. (defun min (x y) ":Doc-Section ACL2::ACL2-built-ins the smaller of two numbers~/ ~c[(Min x y)] is the smaller of the numbers ~c[x] and ~c[y].~/ The ~il[guard] for ~c[min] requires its arguments to be rational (~il[real], in ACL2(r)) numbers. ~c[Min] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp x) (real/rationalp y)))) (if (< x y) x y)) ;; RAG - Only the guard changed here. (defun max (x y) ":Doc-Section ACL2::ACL2-built-ins the larger of two numbers~/ ~c[(Max x y)] is the larger of the numbers ~c[x] and ~c[y].~/ The ~il[guard] for ~c[max] requires its arguments to be rational (~il[real], in ACL2(r)) numbers. ~c[Max] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (real/rationalp x) (real/rationalp y)))) (if (> x y) x y)) ;; RAG - Only the guard changed here. The docstring below says that ;; abs must not be used on complex arguments, since that could result ;; in a non-ACL2 object. (defun abs (x) ":Doc-Section ACL2::ACL2-built-ins the absolute value of a real number~/ ~c[(Abs x)] is ~c[-x] if ~c[x] is negative and is ~c[x] otherwise.~/ The ~il[guard] for ~c[abs] requires its argument to be a rational (~il[real], in ACL2(r)) number. ~c[Abs] is a Common Lisp function. See any Common Lisp documentation for more information. From ``Common Lisp the Language'' page 205, we must not allow complex ~c[x] as an argument to ~c[abs] in ACL2, because if we did we would have to return a number that might be a floating point number and hence not an ACL2 object. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (real/rationalp x))) (if (minusp x) (- x) x)) (defun signum (x) ":Doc-Section ACL2::ACL2-built-ins indicator for positive, negative, or zero~/ ~c[(Signum x)] is ~c[0] if ~c[x] is ~c[0], ~c[-1] if ~c[x] is negative, and is ~c[1] otherwise.~/ The ~il[guard] for ~c[signum] requires its argument to be rational (~il[real], in ACL2(r)) number. ~c[Signum] is a Common Lisp function. See any Common Lisp documentation for more information. From ``Common Lisp the Language'' page 206, we see a definition of ~c[signum] in terms of ~ilc[abs]. As explained elsewhere (~pl[abs]), the ~il[guard] for ~ilc[abs] requires its argument to be a rational (~il[real], in ACL2(r)) number; hence, we make the same restriction for ~c[signum]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (real/rationalp x))) ; On CLTL p. 206 one sees the definition ; (if (zerop x) x (* x (/ (abs x)))). ; However, that suffers because it looks to type-set like it returns ; an arbitrary rational when in fact it returns -1, 0, or 1. So we ; give a more explicit definition. See the doc string in abs for a ; justification for disallowing complex arguments. (if (zerop x) 0 (if (minusp x) -1 +1))) (defun lognot (i) ":Doc-Section ACL2::ACL2-built-ins bitwise not of a two's complement number~/ ~c[(lognot i)] is the two's complement bitwise ~c[`not'] of the integer ~c[i].~/ ~c[Lognot] is actually defined by coercing its argument to an integer (~pl[ifix]), negating the result, and then subtracting ~c[1]. The ~il[guard] for ~c[lognot] requires its argument to be an integer. ~c[Lognot] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (integerp i))) (+ (- (ifix i)) -1)) ; This function is introduced now because we need it in the admission of ; logand. The admission of o-p could be moved up to right ; after the introduction of the "and" macro. ) (defthm standard-char-p-nth (implies (and (standard-char-listp chars) (<= 0 i) (< i (len chars))) (standard-char-p (nth i chars))) :hints (("Goal" :in-theory (enable standard-char-listp)))) (verify-termination-boot-strap (string-equal1 (declare (xargs :measure (nfix (- maximum (nfix i))))))) (verify-termination-boot-strap string-equal) (verify-termination-boot-strap assoc-string-equal) (verify-termination-boot-strap xxxjoin) (deflabel proof-of-well-foundedness :doc ":Doc-Section Miscellaneous a proof that ~ilc[o<] is well-founded on ~ilc[o-p]s~/ The soundness of ACL2 rests in part on the well-foundedness of ~ilc[o<] on ~ilc[o-p]s. This can be taken as obvious if one is willing to grant that those concepts are simply encodings of the standard mathematical notions of the ordinals below ~c[epsilon-0] and its natural ordering relation. But it is possible to prove that ~ilc[o<] is well-founded on ~ilc[o-p]s without having to assert any connection to the ordinals and that is what we do here. The community book ~c[books/ordinals/proof-of-well-foundedness] carries out the proof outlined below in ACL2, using only that the natural numbers are well-founded.~/ Before outlining the above mentioned proof, we note that in the analogous documentation page of ACL2 Version_2.7, there is a proof of the well-foundedness of ~c[e0-ord-<] on ~c[e0-ordinalp]s, the less-than relation and recognizer for the old ordinals (that is, for the ordinals appearing in ACL2 up through that version). Manolios and Vroon have given a proof in ACL2 Version_2.7 that the current ordinals (based on ~ilc[o<] and ~ilc[o-p]) are order-isomorphic to the old ordinals (based on ~c[e0-ord-<] and ~c[e0-ordinalp]). Their proof establishes that switching from the old ordinals to the current ordinals preserves the soundness of ACL2. For details see their paper: ~bf[] Manolios, Panagiotis & Vroon, Daron. Ordinal arithmetic in ACL2. Kaufmann, Matt, & Moore, J Strother (eds). Fourth International Workshop on the ACL2 Theorem Prover and Its Applications (ACL2-2003), July, 2003. See ~url[http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/]. ~ef[] We now give an outline of the above mentioned proof of well-foundedness. We first observe three facts about ~ilc[o<] on ordinals that have been proved by ACL2 using only structural induction on lists. These theorems can be proved by hand. ~bv[] (defthm transitivity-of-o< (implies (and (o< x y) (o< y z)) (o< x z)) :rule-classes nil) (defthm non-circularity-of-o< (implies (o< x y) (not (o< y x))) :rule-classes nil) (defthm trichotomy-of-o< (implies (and (o-p x) (o-p y)) (or (equal x y) (o< x y) (o< y x))) :rule-classes nil) ~ev[] These three properties establish that ~ilc[o<] orders the ~ilc[o-p]s. To put such a statement in the most standard mathematical nomenclature, we can define the macro: ~bv[] (defmacro o<= (x y) `(not (o< ,y ,x))) ~ev[] and then establish that ~c[o<=] is a relation that is a simple, complete (i.e., total) order on ordinals by the following three lemmas, which have been proved: ~bv[] (defthm antisymmetry-of-o<= (implies (and (o-p x) (o-p y) (o<= x y) (o<= y x)) (equal x y)) :rule-classes nil :hints ((\"Goal\" :use non-circularity-of-o<))) (defthm transitivity-of-o<= (implies (and (o-p x) (o-p y) (o<= x y) (o<= y z)) (o<= x z)) :rule-classes nil :hints ((\"Goal\" :use transitivity-of-o<))) (defthm trichotomy-of-o<= (implies (and (o-p x) (o-p y)) (or (o<= x y) (o<= y x))) :rule-classes nil :hints ((\"Goal\" :use trichotomy-of-o<))) ~ev[] Crucially important to the proof of the well-foundedness of ~ilc[o<] on ~ilc[o-p]s is the concept of ordinal-depth, abbreviated ~c[od]: ~bv[] (defun od (l) (if (o-finp l) 0 (1+ (od (o-first-expt l))))) ~ev[] If the ~c[od] of an ~ilc[o-p] ~c[x] is smaller than that of an ~ilc[o-p] ~c[y], then ~c[x] is ~ilc[o<] ~c[y]: ~bv[] (defun od-1 (x y) (if (o-finp x) (list x y) (od-1 (o-first-expt x) (o-first-expt y)))) (defthm od-implies-ordlessp (implies (and (o-p x) (< (od x) (od y))) (o< x y)) :hints ((\"Goal\" :induct (od-1 x y)))) ~ev[] Remark. A consequence of this lemma is the fact that if ~c[s = s(1)], ~c[s(2)], ... is an infinite, ~ilc[o<] descending sequence of ~ilc[o-p]s, then ~c[od(s(1))], ~c[od(s(2))], ... is a ``weakly'' descending sequence of non-negative integers: ~c[od(s(i))] is greater than or equal to ~c[od(s(i+1))]. ~em[Lemma Main.] For each non-negative integer ~c[n], ~ilc[o<] well-orders the set of ~ilc[o-p]s with ~c[od] less than or equal to ~c[n] . ~bv[] Base Case. n = 0. The o-ps with 0 od are the non-negative integers. On the non-negative integers, o< is the same as <. Induction Step. n > 0. We assume that o< well-orders the o-ps with od less than n. If o< does not well-order the o-ps with od less than or equal to n, consider, D, the set of infinite, o< descending sequences of o-ps of od less than or equal to n. The first element of a sequence in D has od n. Therefore, the o-first-expt of the first element of a sequence in D has od n-1. Since o<, by IH, well-orders the o-ps with od less than n, the set of o-first-expts of first elements of the sequences in D has a minimal element, which we denote by B and which has od of n-1. Let k be the minimum integer such that for some infinite, o< descending sequence s of o-ps with od less than or equal to n, the first element of s has an o-first-expt of B and an o-first-coeff of k. Notice that k is positive. Having fixed B and k, let s = s(1), s(2), ... be an infinite, o< descending sequence of o-ps with od less than or equal to n such that s(1) has a o-first-expt of B and an o-first-coeff of k. We show that each s(i) has a o-first-expt of B and an o-first-coeff of k. For suppose that s(j) is the first member of s either with o-first-expt B and o-first-coeff m (m neq k) or with o-first-expt B' and o-first-coeff B' (B' neq B). If (o-first-expt s(j)) = B', then B' has od n-1 (otherwise, by IH, s would not be infinite) and B' is o< B, contradicting the minimality of B. If 0 < m < k, then the fact that the sequence beginning at s(j) is infinitely descending contradicts the minimality of k. If m > k, then s(j) is greater than its predecessor; but this contradicts the fact that s is descending. Thus, by the definition of o<, for s to be a decreasing sequence of o-ps, (o-rst s(1)), (o-rst s(2)), ... must be a decreasing sequence. We end by showing this cannot be the case. Let t = t(1), t(2), ... be an infinite sequence of o-ps such that t(i) = (o-rst s(i)). Then t is infinitely descending. Furthermore, t(1) begins with an o-p B' that is o< B. Since t is in D, t(1) has od n, therefore, B' has od n-1. But this contradicts the minimality of B. Q.E.D. ~ev[] Theorem. ~ilc[o<] well-orders the ~ilc[o-p]s. Proof. Every infinite,~c[ o<] descending sequence of ~ilc[o-p]s has the property that each member has ~c[od] less than or equal to the ~c[od], ~c[n], of the first member of the sequence. This contradicts Lemma Main. Q.E.D.") #+acl2-loop-only (progn (defun expt (r i) ":Doc-Section ACL2::ACL2-built-ins exponential function~/ ~c[(Expt r i)] is the result of raising the number ~c[r] to the integer power ~c[i].~/ The ~il[guard] for ~c[(expt r i)] is that ~c[r] is a number and ~c[i] is an integer, and furthermore, if ~c[r] is ~c[0] then ~c[i] is nonnegative. When the type requirements of the ~il[guard] aren't met, ~c[(expt r i)] first coerces ~c[r] to a number and ~c[i] to an integer. ~c[Expt] is a Common Lisp function. See any Common Lisp documentation for more information. Note that ~c[r] can be a complex number; this is consistent with Common lisp. To see the ACL2 definition of this function, ~pl[pf].~/" ; CLtL2 (page 300) allows us to include complex rational arguments. (declare (xargs :guard (and (acl2-numberp r) (integerp i) (not (and (eql r 0) (< i 0)))) :measure (abs (ifix i)))) (cond ((zip i) 1) ((= (fix r) 0) 0) ((> i 0) (* r (expt r (+ i -1)))) (t (* (/ r) (expt r (+ i +1)))))) (defun logcount (x) ":Doc-Section ACL2::ACL2-built-ins number of ``on'' bits in a two's complement number~/ ~c[(Logcount x)] is the number of ``on'' bits in the two's complement representation of ~c[x].~/ ~c[(Logcount x)] has a ~il[guard] of ~c[(integerp x)]. ~c[Logcount] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (integerp x))) (cond ((zip x) 0) ((< x 0) (logcount (lognot x))) ((evenp x) (logcount (nonnegative-integer-quotient x 2))) (t (1+ (logcount (nonnegative-integer-quotient x 2)))))) (defun nthcdr (n l) ":Doc-Section ACL2::ACL2-built-ins final segment of a list~/ ~c[(Nthcdr n l)] removes the first ~c[n] elements from the list ~c[l].~/ The following is a theorem. ~bv[] (implies (and (integerp n) (<= 0 n) (true-listp l)) (equal (length (nthcdr n l)) (if (<= n (length l)) (- (length l) n) 0))) ~ev[] For related functions, ~pl[take] and ~pl[butlast]. The ~il[guard] of ~c[(nthcdr n l)] requires that ~c[n] is a nonnegative integer and ~c[l] is a true list. ~c[Nthcdr] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp n) (<= 0 n) (true-listp l)))) (if (zp n) l (nthcdr (+ n -1) (cdr l)))) (defun logbitp (i j) ":Doc-Section ACL2::ACL2-built-ins the ~c[i]th bit of an integer~/ For a nonnegative integer ~c[i] and an integer ~c[j], ~c[(logbitp i j)] is a Boolean, which is ~c[t] if and only if the value of the ~c[i]th bit is ~c[1] in the two's complement representation of ~c[j].~/ ~c[(Logbitp i j)] has a ~il[guard] that ~c[i] is a nonnegative integer and ~c[j] is an integer. ~c[Logbitp] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp j) (integerp i) (>= i 0)) :mode :program)) (oddp (floor (ifix j) (expt 2 (nfix i))))) (defun ash (i c) ":Doc-Section ACL2::ACL2-built-ins arithmetic shift operation~/ ~c[(ash i c)] is the result of taking the two's complement representation of the integer ~c[i] and shifting it by ~c[c] bits: shifting left and padding with ~c[c] ~c[0] bits if ~c[c] is positive, shifting right and dropping ~c[(abs c)] bits if ~c[c] is negative, and simply returning ~c[i] if ~c[c] is ~c[0].~/ The ~il[guard] for ~c[ash] requires that its arguments are integers. ~c[Ash] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp c)) :mode :program)) (floor (* (ifix i) (expt 2 c)) 1)) ) ; John Cowles first suggested a version of the following lemma for rationals. (defthm expt-type-prescription-non-zero-base (implies (and (acl2-numberp r) (not (equal r 0))) (not (equal (expt r i) 0))) :rule-classes :type-prescription) ;; RAG - I added the following lemma, similar to the rational case. #+:non-standard-analysis (defthm realp-expt-type-prescription (implies (realp r) (realp (expt r i))) :rule-classes :type-prescription) (defthm rationalp-expt-type-prescription (implies (rationalp r) (rationalp (expt r i))) :rule-classes :type-prescription) (verify-termination-boot-strap logbitp) (verify-termination-boot-strap ash) (deflabel characters :doc ":Doc-Section ACL2::ACL2-built-ins characters in ACL2~/ ACL2 accepts 256 distinct characters, which are the characters obtained by applying the function ~ilc[code-char] to each integer from ~c[0] to ~c[255]. Among these, Common Lisp designates certain ones as ~em[standard characters], namely those of the form ~c[(code-char n)] where ~c[n] is from ~c[33] to ~c[126], together with ~c[#\\Newline] and ~c[#\\Space]. The actual standard characters may be viewed by evaluating the ~ilc[defconst] ~c[*standard-chars*].~/ To be more precise, Common Lisp does not specify the precise relationship between ~ilc[code-char] and the standard characters. However, we check that the underlying Common Lisp implementation uses a particular relationship that extends the usual ASCII coding of characters. We also check that Space, Tab, Newline, Page, and Rubout correspond to characters with respective ~ilc[char-code]s ~t[32], ~t[9], ~t[10], ~t[12], and ~t[127]. ~ilc[Code-char] has an inverse, ~ilc[char-code]. Thus, when ~ilc[char-code] is applied to an ACL2 character, ~c[c], it returns a number ~c[n] between ~c[0] and ~c[255] inclusive such that ~c[(code-char n)] = ~c[c]. The preceding paragraph implies that there is only one ACL2 character with a given character code. CLTL allows for ``attributes'' for characters, which could allow distinct characters with the same code, but ACL2 does not allow this. ~em[The Character Reader] ACL2 supports the `~c[#\\]' notation for characters provided by Common Lisp, with some restrictions. First of all, for every character ~c[c], the notation ~bv[] #\\c ~ev[] may be used to denote the character object ~c[c]. That is, the user may type in this notation and ACL2 will read it as denoting the character object ~c[c]. In this case, the character immediately following ~c[c] must be one of the following ``terminating characters'': a Tab, a Newline, a Page character, a space, or one of the characters: ~bv[] \" ' ( ) ; ` , ~ev[] Other than the notation above, ACL2 accepts alternate notation for five characters. ~bv[] #\\Space #\\Tab #\\Newline #\\Page #\\Rubout ~ev[] Again, in each of these cases the next character must be from among the set of ``terminating characters'' described in the single-character case. Our implementation is consistent with IS0-8859, even though we don't provide ~c[#\\] syntax for entering characters other than that described above. Finally, we note that it is our intention that any object printed by ACL2's top-level-loop may be read back into ACL2. Please notify the implementors if you find a counterexample to this claim.~/") (defaxiom char-code-linear ; The other properties that we might be tempted to state here, ; (integerp (char-code x)) and (<= 0 (char-code x)), are taken care of by ; type-set-char-code. (< (char-code x) 256) :rule-classes :linear) (defaxiom code-char-type (characterp (code-char n)) :rule-classes :type-prescription) (defaxiom code-char-char-code-is-identity (implies (force (characterp c)) (equal (code-char (char-code c)) c))) (defaxiom char-code-code-char-is-identity (implies (and (force (integerp n)) (force (<= 0 n)) (force (< n 256))) (equal (char-code (code-char n)) n))) #+acl2-loop-only (defun char< (x y) ":Doc-Section ACL2::ACL2-built-ins less-than test for ~il[characters]~/ ~c[(char< x y)] is true if and only if the character code of ~c[x] is less than that of ~c[y]. ~l[char-code].~/ The ~il[guard] for ~c[char<] specifies that its arguments are ~il[characters]. ~c[Char<] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (characterp x) (characterp y)))) (< (char-code x) (char-code y))) #+acl2-loop-only (defun char> (x y) ":Doc-Section ACL2::ACL2-built-ins greater-than test for ~il[characters]~/ ~c[(char> x y)] is true if and only if the character code of ~c[x] is greater than that of ~c[y]. ~l[char-code].~/ The ~il[guard] for ~c[char>] specifies that its arguments are ~il[characters]. ~c[Char>] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (characterp x) (characterp y)))) (> (char-code x) (char-code y))) #+acl2-loop-only (defun char<= (x y) ":Doc-Section ACL2::ACL2-built-ins less-than-or-equal test for ~il[characters]~/ ~c[(char<= x y)] is true if and only if the character code of ~c[x] is less than or equal to that of ~c[y]. ~l[char-code].~/ The ~il[guard] for ~c[char<=] specifies that its arguments are ~il[characters]. ~c[Char<=] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (characterp x) (characterp y)))) (<= (char-code x) (char-code y))) #+acl2-loop-only (defun char>= (x y) ":Doc-Section ACL2::ACL2-built-ins greater-than-or-equal test for ~il[characters]~/ ~c[(char>= x y)] is true if and only if the character code of ~c[x] is greater than or equal to that of ~c[y]. ~l[char-code].~/ The ~il[guard] for ~c[char>=] specifies that its arguments are ~il[characters]. ~c[Char>=] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (characterp x) (characterp y)))) (>= (char-code x) (char-code y))) (defun string<-l (l1 l2 i) (declare (xargs :guard (and (character-listp l1) (character-listp l2) (integerp i)))) (cond ((endp l1) (cond ((endp l2) nil) (t i))) ((endp l2) nil) ((eql (car l1) (car l2)) (string<-l (cdr l1) (cdr l2) (+ i 1))) ((char< (car l1) (car l2)) i) (t nil))) #+acl2-loop-only (defun string< (str1 str2) ":Doc-Section ACL2::ACL2-built-ins less-than test for strings~/ ~c[(String< str1 str2)] is non-~c[nil] if and only if the string ~c[str1] precedes the string ~c[str2] lexicographically, where character inequalities are tested using ~ilc[char<]. When non-~c[nil], ~c[(string< str1 str2)] is the first position (zero-based) at which the strings differ. Here are some examples. ~bv[] ACL2 !>(string< \"abcd\" \"abu\") 2 ACL2 !>(string< \"abcd\" \"Abu\") NIL ACL2 !>(string< \"abc\" \"abcde\") 3 ACL2 !>(string< \"abcde\" \"abc\") NIL ~ev[] ~/ The ~il[guard] for ~c[string<] specifies that its arguments are strings. ~c[String<] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp str1) (stringp str2)))) (string<-l (coerce str1 'list) (coerce str2 'list) 0)) #+acl2-loop-only (defun string> (str1 str2) ":Doc-Section ACL2::ACL2-built-ins greater-than test for strings~/ ~c[(String> str1 str2)] is non-~c[nil] if and only if ~c[str2] precedes ~c[str1] lexicographically. When non-~c[nil], ~c[(string> str1 str2)] is the first position (zero-based) at which the strings differ. ~l[string<].~/ ~c[String>] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp str1) (stringp str2)))) (string< str2 str1)) #+acl2-loop-only (defun string<= (str1 str2) ":Doc-Section ACL2::ACL2-built-ins less-than-or-equal test for strings~/ ~c[(String<= str1 str2)] is non-~c[nil] if and only if the string ~c[str1] precedes the string ~c[str2] lexicographically or the strings are equal. When non-~c[nil], ~c[(string<= str1 str2)] is the first position (zero-based) at which the strings differ, if they differ, and otherwise is their common length. ~l[string<].~/ The ~il[guard] for ~c[string<=] specifies that its arguments are strings. ~c[String<=] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp str1) (stringp str2)))) (if (equal str1 str2) (length str1) (string< str1 str2))) #+acl2-loop-only (defun string>= (str1 str2) ":Doc-Section ACL2::ACL2-built-ins less-than-or-equal test for strings~/ ~c[(String>= str1 str2)] is non-~c[nil] if and only if the string ~c[str2] precedes the string ~c[str1] lexicographically or the strings are equal. When non-~c[nil], ~c[(string>= str1 str2)] is the first position (zero-based) at which the strings differ, if they differ, and otherwise is their common length. ~l[string>].~/ The ~il[guard] for ~c[string>=] specifies that its arguments are strings. ~c[String>=] is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (stringp str1) (stringp str2)))) (if (equal str1 str2) (length str1) (string> str1 str2))) (defun symbol-< (x y) ":Doc-Section ACL2::ACL2-built-ins less-than test for symbols~/ ~c[(symbol-< x y)] is non-~c[nil] if and only if either the ~ilc[symbol-name] of the symbol ~c[x] lexicographially precedes the ~ilc[symbol-name] of the symbol ~c[y] (in the sense of ~ilc[string<]) or else the ~ilc[symbol-name]s are equal and the ~ilc[symbol-package-name] of ~c[x] lexicographically precedes that of ~c[y] (in the same sense). So for example, ~c[(symbol-< 'abcd 'abce)] and ~c[(symbol-< 'acl2::abcd 'foo::abce)] are true.~/ The ~il[guard] for ~c[symbol] specifies that its arguments are symbols. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (symbolp x) (symbolp y)))) (let ((x1 (symbol-name x)) (y1 (symbol-name y))) (or (string< x1 y1) (and (equal x1 y1) (string< (symbol-package-name x) (symbol-package-name y)))))) (defthm string<-l-irreflexive (not (string<-l x x i))) (defthm string<-irreflexive (not (string< s s))) (defun substitute-ac (new old seq acc) (declare (xargs :guard (and (true-listp acc) (true-listp seq) (or (eqlablep old) (eqlable-listp seq))))) (cond ((endp seq) (reverse acc)) ((eql old (car seq)) (substitute-ac new old (cdr seq) (cons new acc))) (t (substitute-ac new old (cdr seq) (cons (car seq) acc))))) #+acl2-loop-only (defun substitute (new old seq) ":Doc-Section ACL2::ACL2-built-ins substitute into a string or a list, using ~ilc[eql] as test~/ ~c[(Substitute new old seq)] is the result of replacing each occurrence of ~c[old] in ~c[seq], which is a list or a string, with ~c[new].~/ The guard for ~c[substitute] requires that either ~c[seq] is a string and ~c[new] is a character, or else: ~c[seq] is a ~ilc[true-listp] such that either all of its members are ~ilc[eqlablep] or ~c[old] is ~c[eqlablep]. ~c[Substitute] is a Common Lisp function. See any Common Lisp documentation for more information. Since ACL2 functions cannot take keyword arguments (though macros can), the test used in ~c[substitute] is ~c[eql]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (or (and (stringp seq) (characterp new)) (and (true-listp seq) (or (eqlablep old) (eqlable-listp seq)))) ; Wait for state-global-let* to be defined, so that we can provide a ; local lemma. :verify-guards nil)) (if (stringp seq) (coerce (substitute-ac new old (coerce seq 'list) nil) 'string) (substitute-ac new old seq nil))) #+acl2-loop-only (defun sublis (alist tree) ":Doc-Section ACL2::ACL2-built-ins substitute an alist into a tree~/ ~c[(Sublis alist tree)] is obtained by replacing every leaf of ~c[tree] with the result of looking that leaf up in the association list ~c[alist]. However, a leaf is left unchanged if it is not found as a key in ~c[alist].~/ Leaves are looked up using the function ~ilc[assoc]. The ~il[guard] for ~c[(sublis alist tree)] requires ~c[(eqlable-alistp alist)]. This ~il[guard] ensures that the ~il[guard] for ~ilc[assoc] will be met for each lookup generated by ~c[sublis]. ~c[Sublis] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (eqlable-alistp alist))) (cond ((atom tree) (let ((pair (assoc tree alist))) (cond (pair (cdr pair)) (t tree)))) (t (cons (sublis alist (car tree)) (sublis alist (cdr tree)))))) #+acl2-loop-only (defun subst (new old tree) ":Doc-Section ACL2::ACL2-built-ins a single substitution into a tree~/ ~c[(Subst new old tree)] is obtained by substituting ~c[new] for every occurence of ~c[old] in the given tree.~/ Equality to ~c[old] is determined using the function ~ilc[eql]. The ~il[guard] for ~c[(subst new old tree)] requires ~c[(eqlablep old)], which ensures that the ~il[guard] for ~ilc[eql] will be met for each comparison generated by ~c[subst]. ~c[Subst] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (eqlablep old))) (cond ((eql old tree) new) ((atom tree) tree) (t (cons (subst new old (car tree)) (subst new old (cdr tree)))))) (defmacro pprogn (&rest lst) ; Keep in sync with pprogn@par. ":Doc-Section ACL2::ACL2-built-ins evaluate a sequence of forms that return ~il[state]~/ ~bv[] Example Form: (pprogn (cond ((or (equal (car l) #\\) (equal (car l) slash-char)) (princ$ #\\ channel state)) (t state)) (princ$ (car l) channel state) (mv (cdr l) state)) ~ev[] The convention for ~c[pprogn] usage is to give it a non-empty sequence of forms, each of which (except possibly for the last) returns state (~pl[state]) as its only value. The ~il[state] returned by each but the last is passed on to the next. The value or values of the last form are returned as the value of the ~c[pprogn]. If you are using single-threaded objects you may wish to define an analogue of this function for your own ~il[stobj].~/ General Form: ~bv[] (PPROGN form1 form2 ... formk result-form) ~ev[] This general form is equivalent, via macro expansion, to: ~bv[] (LET ((STATE form1)) (LET ((STATE form2)) ... (LET ((STATE formk)) result-form))) ~ev[] ~/" (declare (xargs :guard (and lst (true-listp lst)))) (cond ((endp (cdr lst)) (car lst)) #-acl2-loop-only ; The next case avoids compiler warnings from (pprogn .... (progn! ...)). Note ; that progn! in raw Lisp binds state to *the-live-state*, and hence shadows ; superior bindings of state. We are tempted to check that the last form ; starts with progn!, but of course it could be a macro call that expands to a ; call of progn!, so we make no such check. ((endp (cddr lst)) (list 'let (list (list 'STATE (car lst))) '(DECLARE (IGNORABLE STATE)) (cadr lst))) (t (list 'let (list (list 'STATE (car lst))) (cons 'pprogn (cdr lst)))))) (defmacro progn$ (&rest rst) ":Doc-Section ACL2::ACL2-built-ins execute a sequence of forms and return the value of the last one~/ This macro expands to a corresponding nest of calls of ~c[prog2$]; ~pl[prog2$]. The examples below show how this works: the first case below is typical, but we conclude with two special cases. ~bv[] ACL2 !>:trans1 (progn$ (f1 x) (f2 x) (f3 x)) (PROG2$ (F1 X) (PROG2$ (F2 X) (F3 X))) ACL2 !>:trans1 (progn$ (f1 x) (f2 x)) (PROG2$ (F1 X) (F2 X)) ACL2 !>:trans1 (progn$ (f1 x)) (F1 X) ACL2 !>:trans1 (progn$) NIL ACL2 !> ~ev[]~/~/" (cond ((null rst) nil) ((null (cdr rst)) (car rst)) (t (xxxjoin 'prog2$ rst)))) #+acl2-par (defmacro pprogn@par (&rest rst) ; Keep in sync with pprogn. `(progn$ ,@rst)) ; The Unwind-Protect Essay ; We wish to define an ACL2 macro form: ; (acl2-unwind-protect "expl" body cleanup1 cleanup2) ; with the following logical semantics ; (mv-let (erp val state) ; ,body ; (cond (erp (pprogn ,cleanup1 (mv erp val state))) ; (t (pprogn ,cleanup2 (mv erp val state))))) ; The idea is that it returns the 3 results of evaluating body except before ; propagating those results upwards it runs one of the two cleanup forms, ; depending on whether the body signalled an error. The cleanup forms return ; state. In typical use the cleanup forms restore the values of state global ; variables that were "temporarily" set by body. [Note that the "expl" ; is a string and it is always ignored. Its only use is to tag the elements ; of the stacks in the frames of *acl2-unwind-protect-stack* so that debugging ; is easier. None of our code actually looks at it.] ; In addition, we want acl2-unwind-protect to handle aborts caused by the user ; during the processing of body and we want ev to handle acl2-unwind-protect ; "properly" in a sense discussed later. ; We deal first with the notion of the "proper" way to handle aborts. Because ; of the way acl2-unwind-protect is used, namely to "restore" a "temporarily" ; smashed state, aborts during body should not prevent the execution of the ; cleanup code. Intuitively, the compiled form of an acl2-unwind-protect ; ought to involve a Common Lisp unwind-protect. In fact, it does not, for ; reasons developed below. But it is easier to think about the correctness of ; our implementation if we start by thinking in terms of using a raw lisp ; unwind-protect in the macroexpansion of each acl2-unwind-protect. ; The (imagined) unwind-protect is almost always irrelevant because "errors" ; signalled by body are in fact not Lisp errors. But should the user cause an ; abort during body, the unwind-protect will ensure that cleanup1 is executed. ; This is a logically arbitrary choice; we might have said cleanup2 is ; executed. By "ensure" we mean not only will the Lisp unwind-protect fire ; the cleanup code even though body was aborted; we mean that the cleanup code ; will be executed without possibility of abort. Now there is no way to ; disable interrupts in CLTL. But if we make sufficient assumptions about the ; cleanup forms then we can effectively disable interrupts by executing each ; cleanup form repeatedly until it is executed once without being aborted. We ; might define "idempotency" to be just the necessary property: the repeated ; (possibly partial) execution of the form, followed by a complete execution ; of the form, produces the same state as a single complete execution. For ; example, (f-put-global 'foo 'old-val state) is idempotent but (f-put-global ; 'foo (1- (get-global 'foo state)) state) is not. Cleanup1 should be idempotent ; to ensure that our implementation of unwind protect in the face of aborts is ; correct with respect to the (non-logical) semantics we have described. ; Furthermore, it bears pointing out that cleanup1 might be called upon to undo ; the work of a "partial" execution of cleanup2! This happens if the body ; completes normally and without signalling an error, cleanup2 is undertaken, ; and then the user aborts. So the rule is that if an abort occurs during an ; acl2-unwind-protect, cleanup1 is executed without interrupts. ; What, pray, gives us the freedom to give arbitrary semantics to ; acl2-unwind-protect in the face of an abort? We regard an abort as akin to ; unplugging the machine and plugging it back in. One should be thankful for ; any reasonable behavior and not quibble over whether it is the "logical" one ; or whether one ought to enforce informal rules like idempotency. Thus, we ; are not terribly sympathetic to arguments that this operational model is ; inconsistent with ACL2 semantics when the user types "Abort!" or doesn't ; understand unenforced assumptions about his cleanup code. All logical bets ; are off the moment the user types "Abort!". This model has the saving grace ; that we can implement it and that it can be used within the ACL2 system code ; to implement what we need during abort recovery. The operational model of ; an abort is that the machine finds the innermost acl2-unwind-protect, rips ; out of the execution of its body (or its cleanup code), executes the ; cleanup1 code with all aborts disabled and then propagates the abort upward. ; Now unfortunately this operational model cannot be implemented ; entirely locally in the compilation of an acl2-unwind-protect. ; Operationally, (acl2-unwind-protect "expl" body cleanup1 ; cleanup2) sort of feels like: ; (unwind-protect ,body ; (cond ( ,cleanup1 ) ; ( ,cleanup1 ) ; (t ,cleanup2 ))) ; where we do whatever we have to do to detect aborts and to pass aborts up in ; some cases and triples up in others. This can all be done with a suitable ; local nest of let, catch, unwind-protect, tests, and throw. But there is a ; problem: if the user is typing "Abort!" then what is to prevent him from ; doing it during the cleanup forms? Nothing. So in fact the sketched use of ; unwind-protect doesn't guarantee that the cleanup forms are executed fully. ; We have been unable to find a way to guarantee via locally produced compiled ; code that even idempotent cleanup forms are executed without interruption. ; Therefore, we take a step back and claim that at the top of the system is ; the ACL2 command interpreter. It will have an unwind-protect in it (quite ; probably the only unwind-protect in the whole system) and it will guarantee ; to execute all the cleanup forms before it prompts the user for the next ; expression to evaluate. An abort there will rip us out of the command ; interpreter. We shall arrange for re-entering it to execute the cleanup ; forms before prompting. If we imagine, again, that each acl2-unwind-protect ; is compiled into an unwind-protect, then since the aborts are passed up and ; the cleanup forms are each executed in turn as we ascend back to the top, ; the cleanup forms are just stacked. It suffices then for ; acl2-unwind-protect to push the relevant cleanup form (always form 1) on ; this stack before executing body and for the top-level to pop these forms ; and evaluate them one at a time before prompting for the next input. ; Actually, we must push the cleanup form and the current variable bindings in ; order to be able to evaluate the form "out of context." ; The stack in question is called *acl2-unwind-protect-stack*. It is really a ; stack of "frames". Each frame on the stack corresponds to a call of the ; general-purpose ACL2 read-eval-print loop. By so organizing it we can ensure ; that each call of the read-eval-print loop manages its own unwind protection ; (in the normal case) while also insuring that the stack is global and visible ; to all. This allows each level to clean up after aborted inferiors what ; failed to clean up after themselves. If however we abort during the last ; cleanup form, we will find ourselves in raw Lisp. See the comment about this ; case in ld-fn. ; One final observation is in order. It could be that there is no command ; interpreter because we are running an ACL2 application in raw lisp. In that ; case, "Abort!" means the machine was unplugged and all bets are off anyway. #-acl2-loop-only (defparameter *acl2-unwind-protect-stack* nil) #-acl2-loop-only (defmacro push-car (item place ctx) (let ((g (gensym))) `(let ((,g ,place)) (if (consp ,g) (push ,item (car ,g)) (if *lp-ever-entered-p* (illegal ,ctx "Apparently you have tried to execute a form in raw Lisp ~ that is only intended to be executed inside the ACL2 ~ loop. You should probably abort (e.g., :Q in akcl or ~ gcl, :A in LispWorks, :POP in Allegro), then type (LP) ~ and try again. If this explanation seems incorrect, ~ then please contact the implementors of ACL2." nil) (illegal ,ctx "Please enter the ACL2 loop by typing (LP) ." nil)))))) (defmacro acl2-unwind-protect (expl body cleanup1 cleanup2) ; Note: If the names used for the erp and val results are changed in the #+ ; code, then change them in the #- code also. We use the same names (rather ; than using gensym) just because we know they are acceptable if translate ; approves the check-vars-not-free. ; Note: Keep this function in sync with translated-acl2-unwind-protectp4. That ; function not only knows the precise form of the expression generated below ; but even knows the variable names used! #+acl2-loop-only (declare (ignore expl)) #+acl2-loop-only `(mv-let (acl2-unwind-protect-erp acl2-unwind-protect-val state) (check-vars-not-free (acl2-unwind-protect-erp acl2-unwind-protect-val) ,body) (cond (acl2-unwind-protect-erp (pprogn (check-vars-not-free (acl2-unwind-protect-erp acl2-unwind-protect-val) ,cleanup1) (mv acl2-unwind-protect-erp acl2-unwind-protect-val state))) (t (pprogn (check-vars-not-free (acl2-unwind-protect-erp acl2-unwind-protect-val) ,cleanup2) (mv acl2-unwind-protect-erp acl2-unwind-protect-val state))))) ; The raw code is very similar. But it starts out by pushing onto the undo ; stack the name of the cleanup function and the values of the arguments. Note ; however that we do this only if the state is the live state. That is the ; only state that matters after an abort. Suppose unwind protected code is ; modifying some state object other than the live one (e.g., we are computing ; some explicit value during a proof). Suppose an abort occurs. Consider the ; operational model described: we rip out of the computation, execute the ; cleanup code for the nearest unwind protect, and then pass the abort upwards, ; continuing until we get to the top level. No state besides the live one is ; relevant because no value is returned from an aborted computation. The fake ; state cleaned up at each stop on the way up is just wasted time. So we don't ; push the cleanup code for fake states. If body concludes without an abort we ; execute the appropriate cleanup form and then we pop the undo stack (if we ; pushed something). Note that it is possible that body completes without ; error, cleanup2 is started (and begins smashing state) and then (perhaps even ; after the completion of cleanup2 but before the pop) an abort rips us out, ; causing cleanup1 to be executed after cleanup2. Idempotency is not enough to ; say. #-acl2-loop-only `(let ((temp (and (live-state-p state) ; We have seen warnings from LispWorks 4.2.7 of this form that appear to be ; related to the present binding, but we do not yet know how to eliminate them: ; ; Eliminating a test of a variable with a declared type : TEMP [type CONS] (cons ,expl (function (lambda nil ,cleanup1)))))) ; FUNCTION captures the binding environment in which cleanup1 would ; have been executed. So by applying the resulting function to no ; arguments we evaluate cleanup1 in the current environment. We save ; this cons in temp so we can recognize it below. If we're not ; operating on the live state, temp is nil. (cond (temp (push-car temp *acl2-unwind-protect-stack* 'acl2-unwind-protect))) (mv-let (acl2-unwind-protect-erp acl2-unwind-protect-val state) ,body ; Roughly speaking, we should execute cleanup1 or cleanup2, as ; appropriate based on acl2-unwind-protect-erp, and then pop the ; stack. (Indeed, we used to do this.) However, it is possible that ; the execution of body pushed more forms on the stack and they ; haven't been cleaned off yet because of hard errors. Therefore, we ; first restore the stack to just after the pushing of temp, if we ; pushed temp. (cond (temp (acl2-unwind -1 temp))) (cond (acl2-unwind-protect-erp (pprogn ,cleanup1 (cond (temp (pop (car *acl2-unwind-protect-stack*)) state) (t state)) (mv acl2-unwind-protect-erp acl2-unwind-protect-val state))) (t (pprogn ,cleanup2 (cond (temp (pop (car *acl2-unwind-protect-stack*)) state) (t state)) (mv acl2-unwind-protect-erp acl2-unwind-protect-val state))))))) #-acl2-loop-only (defun-one-output acl2-unwind (n flg) ; flg = nil, pop until length of stack is n. Do not mess with new top-most ; frame. ; flg = t, pop until the length of the stack is n and there is ; at most one form in the top-most frame. This configures the stack ; the way it was when frame n was first built. ; (consp flg), pop until the top-most form in the top frame is eq to ; flg. We do not execute that form. Note that n is irrelevant in ; this case. ; In all cases, no form is removed from the stack until the form has been ; executed. Thus, an interruption in this process will leave the still-undone ; cleanup forms on the stack for continued processing. ; There is a very odd aspect to this function: the value of each cleanup form ; is simply discarded! What is going on? To think about this it is clarifying ; first to consider the case of cleanup in the absence of aborts, i.e., to ; think about the logical semantics of unwind protection. Consider then ; (acl2-unwind-protect "expl" body cleanup1 cleanup2). Call the initial STATE st. ; Suppose body computes normally but returns (mv t nil st'). That is, body ; signals an error and returns a modified state (e.g., that has the error ; message printed to it). Then cleanup1 is executed on st' to produce st'' ; and then the error triple (mv t nil st'') is propagated upwards. Note that ; unlike all the other variables in the cleanup form, the STATE used by ; cleanup1 is the post-body value of the variable, not the pre-body value. ; Now reflect on our abort processing. Before body is executed we captured the ; binding environment in which cleanup1 would have been executed, except that ; that environment contains the pre-body value for STATE. If an abort occurs ; during body we evaluate the cleanup function on those saved values. ; Technically we should replace the value of STATE by the post-body state, st', ; produced by body before the abort. Technically we should then pass upward to ; the next cleanup form the state, st'', produced by the just executed cleanup ; form. ; What prevents us from having to do this is the fact that we are always ; cleaning up the live state and only the live state. The slot holding STATE ; in the environment captured by FUNCTION contains *the-live-state*, which is ; both the pre-body and post-body value of STATE. The result of the cleanup ; form is guaranteed to be *the-live-state*. And so it only looks like we are ; ignoring the values of the cleanup forms! (cond ((cond ((eq flg nil) (= (length *acl2-unwind-protect-stack*) n)) ((eq flg t) (and (= (length *acl2-unwind-protect-stack*) n) (or (null (car *acl2-unwind-protect-stack*)) (null (cdr (car *acl2-unwind-protect-stack*)))))) (t (eq flg (car (car *acl2-unwind-protect-stack*))))) nil) ((null (car *acl2-unwind-protect-stack*)) (pop *acl2-unwind-protect-stack*) (acl2-unwind n flg)) (t (let ((*wormholep* nil)) ; We bind *wormholep* to nil so that we do not try to store undo forms ; for the state changes we are about to make. (apply (cdr (car (car *acl2-unwind-protect-stack*))) ; The presence of expl requires us to take the cdr! nil)) (pop (car *acl2-unwind-protect-stack*)) (acl2-unwind n flg)))) ; The above function, acl2-unwind, will be called in the command interpreter ; before any command is read from the user. Thus, by the time a user command ; is executed we are guaranteed that all cleanup forms from the previous ; command have been completed, regardless of how often it and its cleanup forms ; were interrupted. This completes our consideration of user-caused aborts ; during the execution of ACL2 source or compiled code by the Common Lisp ; system. Now we turn to the even more complicated (!) business of the ; "correct" execution acl2-unwind-protect by ACL2's own EV. ; The code for EV is presented several files from here. But we discuss ; the design issues here while the previous discussion is still fresh. ; By way of foreshadowing, ev is an interpreter for the logic. ; The first problem is that when EV sees an acl2-unwind-protect it doesn't see ; an acl2-unwind-protect at all. It sees the translation of the macro ; expansion. To make matters worse, there are two translations of an MV-LET ; expression: one if the expression occurs inside a function definition (or is ; otherwise deemed "executable") and another if it does not. The functions ; translated-acl2-unwind-protectp and translated-acl2-unwind-protectp4 ; recognize and return the relevant parts of a translated acl2-unwind-protect. ; We can't define them here because they use case-match, which isn't yet ; defined. ; So imagine that EV encounters a translated acl2-unwind-protect form, say ; (acl2-unwind-protect "expl" body cleanup1 cleanup2). Of course, if the ; evaluation is error and abort free, then it is done correctly. If an abort ; occurs we are free (by the unplugging argument) to do whatever we want. But ; what should EV do if there is some kind of an evaluation error in body? For ; example, suppose body calls an undefined function or violates some guard. A ; simple concrete question is "what should EV return on ; (acl2-unwind-protect "expl" ; (mv nil (car 0) state) ; (f-put-global 'foo 'error state) ; (f-put-global 'foo 'no-error state))?" ; For what it is worth, our answer to this concrete question is: ; (mv t "guard violation msg for car" (f-put-global 'foo 'error state)). ; To discuss this, we have to tip-toe carefully around a variety of "errors." ; Let us review EV's functionality. ; EV returns (mv erp val latches), where val is the value of the given ; form when erp is nil. If the form returns a multiple value, then val ; is the corresponding list. Note well: if form returns an error ; triple, then the error flag of that triple is the car of val, not ; erp. If erp is t, then some sort of "evaluation error" occurred ; (such as a udf, ubv or guard violation) and val is an error message. ; Latches is an alist that contains the current values of all stobjs, ; including one for 'state. We distinguish "evaluation errors" (erp = ; t) from the "programmed errors" that may be signaled by some bodies. ; A programmed error is signaled by val being a list of the form ; (t nil state), except that the actual state is to be found in the final ; value of the latches, not in val. ; It is useful to draw an analogy between Common Lisp execution of ; ACL2 source code and the EV interpretation of such code. In that ; analogy, EV's "evaluation errors" correspond to "aborts" and "hard ; errors," while EV's "programmed errors" correspond to "soft errors." ; It is this analogy that guides us in the design of EV. What does EV ; do if an evaluation error occurs during body? Consider the analogy: ; if Common Lisp gets a hard error during the evaluation of body, it ; evaluates cleanup1 and then passes the hard error up. Therefore, if ; EV gets an evaluation error during the evaluation of body, it ; evaluates cleanup1 and then passes the evaluation error up. In ; particular, if the attempt to eval body produces (mv t "msg" ; latches') then EV returns (mv t "msg" latches''), where latches'' is ; obtained by evaluating cleanup1 with STATE bound to latches'. This is ; analogous to what Common Lisp does for the live state. EV can do it ; for any state (live or otherwise) because it is tracking explicitly ; "the last returned state" during the computation, while Common Lisp ; is not. Furthermore, Common Lisp need not pass non-live states up ; since it is only the cleaned up live state that matters -- no other ; value is returned from aborted computations. But EV may be called ; by ACL2 code that makes use of the last state returned during the ; computation. ; If we could stop here the situation would be pretty neat. But there ; is more. EV must deal with a third kind of error: true aborts. We ; have just spoken of evaluation errors (i.e., guard violations and ; other errors detected by EV during evaluation) and of programmed ; errors signaled by the code EV is evaluating. But what if the user ; types "Abort?" Certainly neither EV nor its caller "catches" the ; abort: we just rip our way up through the unwind protects. But if ; EV was being used to modify the live state in an unwind protected ; way, those cleanup forms must be evaluated. This is just another ; way of saying that EV's interpretation of acl2-unwind-protect must ; be phrased in terms of acl2-unwind-protect just so that the live ; state is cleaned up after aborts. We can't actually do that because ; acl2-unwind-protect is too structured and insists that we deal with ; (mv erp val state) triples when EV is dealing with (mv erp (mv erp ; val state) latches) triples. But we use the same raw mechanism of ; the *acl2-unwind-protect-stack*. ; Now the question arises, "what gives us the right to design EV by ; analogy?" The spec for EV is that it returns the correct value when ; it reports no error (returned erp = nil). When an evaluation error ; is reported then all bets are off, i.e., the plug was pulled, and we ; can pretty much return the latches we want, as long as it, indeed, ; contains the final values of all the stobjs. ; This completes the unwind-protect essay. There are some additional comments ; in the code for EV. ; It is IMPERATIVE that the following macro, when-logic, is ONLY used when its ; second argument is a form that evaluates to an error triple. Keep this ; function in sync with boot-translate. (defmacro when-logic (str x) (list 'if '(eq (default-defun-mode-from-state state) :program) (list 'skip-when-logic (list 'quote str) 'state) x)) ; --------------------------------------------------------------------------- ; The *initial-event-defmacros* Discussion ; Lasciate ogni speranza, voi ch' entrate ; The following sequence of defmacros is critically important during ; boot strapping because they define the macros we have been using all ; this time! In fact, this very sequence of forms (minus those not ; marked by the Warning message seen repeatedly below) appears ; elsewhere in this system as a quoted list of constants, ; *initial-event-defmacros*. ; We'll present the defmacros first and then explain the rules for ; adding to or changing them. See also the discussion at ; *initial-event-defmacros*. #+acl2-loop-only (defmacro in-package (str) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section ACL2::Other select current package~/ ~bv[] Example: (in-package \"MY-PKG\")~/ General Form: (in-package str) ~ev[] where ~c[str] is a string that names an existing ACL2 package, i.e., one of the initial packages such as ~c[\"KEYWORD\"] or ~c[\"ACL2\"] or a package introduced with ~ilc[defpkg]. For a complete list of the known packages created with ~ilc[defpkg], evaluate ~bv[] (strip-cars (known-package-alist state)). ~ev[] ~l[defpkg]. An ACL2 book (~pl[books]) must contain a single ~c[in-package] form, which must be the first form in that book." ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'in-package-fn (list 'quote str) 'state)) #+acl2-loop-only (defmacro defpkg (&whole event-form name form &optional doc book-path hidden-p) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ; Keep this in sync with get-cmds-from-portcullis1, make-hidden-defpkg, ; equal-modulo-hidden-defpkgs, and (of course) the #-acl2-loop-only definition ; of defpkg. ":Doc-Section Events define a new symbol package~/ ~bv[] Example: (defpkg \"MY-PKG\" (union-eq *acl2-exports* *common-lisp-symbols-from-main-lisp-package*))~/ General Form: (defpkg \"name\" term doc-string) ~ev[] where ~c[\"name\"] is a non-empty string consisting of standard characters (~pl[standard-char-p]), none of which is lower case, that names the package to be created; ~c[term] is a variable-free expression that evaluates to a list of symbols, where no two distinct symbols in the list may have the same ~ilc[symbol-name], to be imported into the newly created package; and ~ilc[doc-string] is an optional ~il[documentation] string; ~pl[doc-string]. The name of the new package must be ``new'': the host lisp must not contain any package of that name. There are two exceptions to this newness rule, discussed at the end of this documentation. (There is actually an additional argument, book-path, that is used for error reporting but has no logical content. Users should generally ignore this argument, as well as the rest of this sentence: a book-path will be specified for ~ilc[defpkg] events added by ACL2 to the ~il[portcullis] of a book's ~il[certificate]; ~pl[hidden-death-package].) ~c[Defpkg] forms can be entered at the top-level of the ACL2 ~il[command] loop. They should not occur in ~il[books] (~pl[certify-book]). After a successful ~c[defpkg] it is possible to ``intern'' a string into the package using ~ilc[intern-in-package-of-symbol]. The result is a symbol that is in the indicated package, provided the imports allow it. For example, suppose ~c['my-pkg::abc] is a symbol whose ~ilc[symbol-package-name] is ~c[\"MY-PKG\"]. Suppose further that the imports specified in the ~c[defpkg] for ~c[\"MY-PKG\"] do not include a symbol whose ~ilc[symbol-name] is ~c[\"XYZ\"]. Then ~bv[] (intern-in-package-of-symbol \"XYZ\" 'my-pkg::abc) ~ev[] returns a symbol whose ~ilc[symbol-name] is ~c[\"XYZ\"] and whose ~ilc[symbol-package-name] is ~c[\"MY-PKG\"]. On the other hand, if the imports to the ~c[defpkg] does include a symbol with the name ~c[\"XYZ\"], say in the package ~c[\"LISP\"], then ~bv[] (intern-in-package-of-symbol \"XYZ\" 'my-pkg::abc) ~ev[] returns that symbol (which is uniquely determined by the restriction on the imports list above). ~l[intern-in-package-of-symbol]. Upon admission of a ~c[defpkg] event, the function ~c[pkg-imports] is extended to compute a list of all symbols imported into the given package, without duplicates. ~c[Defpkg] is the only means by which an ACL2 user can create a new package or specify what it imports. That is, ACL2 does not support the Common Lisp functions ~c[make-package] or ~c[import]. Currently, ACL2 does not support exporting at all. The Common Lisp function ~ilc[intern] is weakly supported by ACL2; ~pl[intern]. A more general form of that function is also provided: ~pl[intern$]. We now explain the two exceptions to the newness rule for package names. The careful experimenter will note that if a package is created with a ~c[defpkg] that is subsequently undone, the host lisp system will contain the created package even after the undo. Because ACL2 hangs onto ~il[world]s after they have been undone, e.g., to implement ~c[:]~ilc[oops] but, more importantly, to implement error recovery, we cannot actually destroy a package upon undoing it. Thus, the first exception to the newness rule is that ~c[name] is allowed to be the name of an existing package if that package was created by an undone ~c[defpkg] and the newly proposed set of imports is identical to the old one. ~l[package-reincarnation-import-restrictions]. This exception does not violate the spirit of the newness rule, since one is disinclined to believe in the existence of undone packages. The second exception is that ~c[name] is allowed to be the name of an existing package if the package was created by a ~c[defpkg] with identical set of imports. That is, it is permissible to execute ``redundant'' ~c[defpkg] ~il[command]s. The redundancy test is based on the values of the two import forms (comparing them after sorting and removing duplicates), not on the forms themselves. Finally, we explain why we require the package name to contain standard characters, none of which is lower case. We have seen at least one implementation that handled lower-case package names incorrectly. Since we see no need for lower-case characters in package names, which can lead to confusion anyhow (note for example that ~c[foo::bar] is a symbol whose ~ilc[symbol-package-name] is ~c[\"FOO\"], not ~c[\"foo\"]), we simply disallow them. Since the notion of ``lower case'' is only well-specified in Common Lisp for standard characters, we restrict to these. NOTE: Also ~pl[managing-acl2-packages] for contributed documentation on managing ACL2 packages.~/ :cited-by Programming" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defpkg-fn (list 'quote name) (list 'quote form) 'state (list 'quote doc) (list 'quote book-path) (list 'quote hidden-p) (list 'quote event-form))) (defdoc managing-acl2-packages ":Doc-Section defpkg user-contributed documentation on packages~/ Jared Davis has contributed documentation on managing ACL2 packages. See ~url[http://www.cs.utexas.edu/users/moore/acl2/contrib/managing-acl2-packages.html].~/~/") (deflabel hidden-defpkg :doc ":Doc-Section defpkg handling defpkg events that are local~/ ~l[hidden-death-package]~/~/") (deflabel hidden-death-package :doc ":Doc-Section defpkg handling ~ilc[defpkg] ~il[events] that are ~ilc[local]~/ This documentation topic explains a little bit about certain errors users may see when attempting to evaluate a ~ilc[defpkg] event. In brief, if you see an error that refers you to this topic, you are probably trying to admit a ~ilc[defpkg] event, and you should change the name of the package to be introduced by that event. Recall that ~c[defpkg] events introduce axioms, for example as follows. ~bv[] ACL2 !>(defpkg \"PKG0\" '(a b)) Summary Form: ( DEFPKG \"PKG0\" ...) Rules: NIL Warnings: None Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) \"PKG0\" ACL2 !>:pr! \"PKG0\" Rune: (:REWRITE PKG0-PACKAGE) Status: Enabled Lhs: (SYMBOL-PACKAGE-NAME (INTERN-IN-PACKAGE-OF-SYMBOL X Y)) Rhs: \"PKG0\" Hyps: (AND (STRINGP X) (NOT (MEMBER-SYMBOL-NAME X '(A B))) (SYMBOLP Y) (EQUAL (SYMBOL-PACKAGE-NAME Y) \"PKG0\")) Equiv: EQUAL Backchain-limit-lst: NIL Subclass: BACKCHAIN Loop-stopper: NIL ACL2 !> ~ev[] Now, a ~ilc[defpkg] event may be executed underneath an ~ilc[encapsulate] or ~ilc[include-book] form that is marked ~ilc[local]. In that case, traces of the added axiom will disappear after the surrounding ~ilc[encapsulate] or ~ilc[include-book] form is admitted. This can cause inconsistencies. (You can take our word for it, or you can look at the example shown in the ``Essay on Hidden Packages'' in source file ~c[axioms.lisp].) In order to prevent unsoundness, then, ACL2 maintains the following invariant. Let us say that a ~c[defpkg] event is ``hidden'' if it is in support of the current logical ~il[world] but is not present in that world as an event, because it is ~ilc[local] as indicated above. We maintain the invariant that all ~ilc[defpkg] ~il[events], even if ``hidden'', are tracked under-the-hood in the current logical ~il[world]. Sometimes this property causes ~ilc[defpkg] events to be written to the ~il[portcullis] of a book's ~il[certificate] (~pl[books]). At any rate, if you then try to define the package in a manner inconsistent with the earlier such definition, that is, with a different imports list, you will see an error because of the above-mentioned tracking. (By the way, this topic's name comes from Holly Bell, who heard \"hidden death package\" instead of \"hidden defpkg\". The description seemed to fit. Thanks, Holly!)~/~/") #+acl2-loop-only (defmacro defun (&whole event-form &rest def) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section acl2::Events define a function symbol~/ ~bv[] Examples: (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y)) (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (zp n) 1 (* n (fact (1- n)))))~/ General Form: (defun fn (var1 ... varn) doc-string dcl ... dcl body), ~ev[] where ~c[fn] is the symbol you wish to define and is a new symbolic name (~pl[name]), ~c[(var1 ... varn)] is its list of formal parameters (~pl[name]), and ~c[body] is its body. The definitional axiom is logically admissible provided certain restrictions are met. These are sketched below. Note that ACL2 does not support the use of ~c[lambda-list] keywords (such as ~c[&optional]) in the formals list of functions. We do support some such keywords in macros and often you can achieve the desired syntax by defining a macro in addition to the general version of your function. ~l[defmacro]. The ~il[documentation] string, ~ilc[doc-string], is optional; for a description of its form, ~pl[doc-string]. The ~em[declarations] (~pl[declare]), ~c[dcl], are also optional. If more than one ~c[dcl] form appears, they are effectively grouped together as one. Perhaps the most commonly used ACL2 specific declaration is of the form ~c[(declare (xargs :guard g :measure m))]. This declaration in the ~c[defun] of some function ~c[fn] has the effect of making the ``~il[guard]'' for ~c[fn] be the term ~c[g] and the ``measure'' be the term ~c[m]. The notion of ``measure'' is crucial to ACL2's definitional principle. The notion of ``guard'' is not, and is discussed elsewhere; ~pl[verify-guards] and ~pl[set-verify-guards-eagerness]. Note that the ~c[:measure] is ignored in ~c[:]~ilc[program] mode; ~pl[defun-mode]. We now briefly discuss the ACL2 definitional principle, using the following definition form which is offered as a more or less generic example. ~bv[] (defun fn (x y) (declare (xargs :guard (g x y) :measure (m x y))) (if (test x y) (stop x y) (step (fn (d x) y)))) ~ev[] Note that in our generic example, ~c[fn] has just two arguments, ~c[x] and ~c[y], the ~il[guard] and measure terms involve both of them, and the body is a simple case split on ~c[(test x y)] leading to a ``non-recursive'' branch, ~c[(stop x y)], and a ``recursive'' branch. In the recursive branch, ~c[fn] is called after ``decrementing'' ~c[x] to ~c[(d x)] and some step function is applied to the result. Of course, this generic example is quite specific in form but is intended to illustrate the more general case. Provided this definition is admissible under the logic, as outlined below, it adds the following axiom to the logic. ~bv[] Defining Axiom: (fn x y) = (if (test x y) (stop x y) (step (fn (d x) y))) ~ev[] Note that the ~il[guard] of ~c[fn] has no bearing on this logical axiom. This defining axiom is actually implemented in the ACL2 system by a ~c[:]~ilc[definition] rule, namely ~bv[] (equal (fn x y) (if (test a b) (stop a b) (step (fn (d a) b)))). ~ev[] ~l[definition] for a discussion of how definition rules are applied. Roughly speaking, the rule causes certain instances of ~c[(fn x y)] to be replaced by the corresponding instances of the body above. This is called ``opening up'' ~c[(fn x y)]. The instances of ~c[(fn x y)] opened are chosen primarily by heuristics which determine that the recursive calls of ~c[fn] in the opened body (after simplification) are more desirable than the unopened call of ~c[fn]. This discussion has assumed that the definition of ~c[fn] was admissible. Exactly what does that mean? First, ~c[fn] must be a previously unaxiomatized function symbol (however, ~pl[ld-redefinition-action]). Second, the formal parameters must be distinct variable names. Third, the ~il[guard], measure, and body should all be terms and should mention no free variables except the formal parameters. Thus, for example, body may not contain references to ``global'' or ``special'' variables; ACL2 constants or additional formals should be used instead. The final conditions on admissibility concern the termination of the recursion. Roughly put, all applications of ~c[fn] must terminate. In particular, there must exist a binary relation, ~c[rel], and some unary predicate ~c[mp] such that ~c[rel] is well-founded on objects satisfying ~c[mp], the measure term ~c[m] must always produce something satisfying ~c[mp], and the measure term must decrease according to ~c[rel] in each recursive call, under the hypothesis that all the tests governing the call are satisfied. By the meaning of well-foundedness, we know there are no infinitely descending chains of successively ~c[rel]-smaller ~c[mp]-objects. Thus, the recursion must terminate. The only primitive well-founded relation in ACL2 is ~ilc[o<] (~pl[o<]), which is known to be well-founded on the ~ilc[o-p]s (~pl[o-p]). For the proof of well-foundedness, ~pl[proof-of-well-foundedness]. However it is possible to add new well-founded relations. For details, ~pl[well-founded-relation]. We discuss later how to specify which well-founded relation is selected by ~c[defun] and in the present discussion we assume, without loss of generality, that it is ~ilc[o<] on the ~ilc[o-p]s. For example, for our generic definition of ~c[fn] above, with measure term ~c[(m x y)], two theorems must be proved. The first establishes that ~c[m] produces an ordinal: ~bv[] (o-p (m x y)). ~ev[] The second shows that ~c[m] decreases in the (only) recursive call of ~c[fn]: ~bv[] (implies (not (test x y)) (o< (m (d x) y) (m x y))). ~ev[] Observe that in the latter formula we must show that the ``~c[m]-size'' of ~c[(d x)] and ~c[y] is ``smaller than'' the ~c[m]-size of ~c[x] and ~c[y], provided the test, ~c[(test x y)], in the body fails, thus leading to the recursive call ~c[(fn (d x) y)]. ~l[o<] for a discussion of this notion of ``smaller than.'' It should be noted that the most commonly used ordinals are the natural numbers and that on natural numbers, ~ilc[o<] is just the familiar ``less than'' relation (~ilc[<]). Thus, it is very common to use a measure ~c[m] that returns a nonnegative integer, for then ~c[(o-p (m x y))] becomes a simple conjecture about the type of ~c[m] and the second formula above becomes a conjecture about the less-than relationship of nonnegative integer arithmetic. The most commonly used measure function is ~ilc[acl2-count], which computes a nonnegative integer size for all ACL2 objects. ~l[acl2-count]. Probably the most common recursive scheme in Lisp ~il[programming] is when some formal is supposed to be a list and in the recursive call it is replaced by its ~ilc[cdr]. For example, ~c[(test x y)] might be simply ~c[(atom x)] and ~c[(d x)] might be ~c[(cdr x)]. In that case, ~c[(acl2-count x)] is a suitable measure because the ~ilc[acl2-count] of a ~ilc[cons] is strictly larger than the ~ilc[acl2-count]s of its ~ilc[car] and ~ilc[cdr]. Thus, ``recursion by ~ilc[car]'' and ``recursion by ~ilc[cdr]'' are trivially admitted if ~ilc[acl2-count] is used as the measure and the definition protects every recursive call by a test insuring that the decremented argument is a ~ilc[consp]. Similarly, ``recursion by ~ilc[1-]'' in which a positive integer formal is decremented by one in recursion, is also trivially admissible. ~l[built-in-clause] to extend the class of trivially admissible recursive schemes. We now turn to the question of which well-founded relation ~c[defun] uses. It should first be observed that ~c[defun] must actually select both a relation (e.g., ~ilc[o<]) and a domain predicate (e.g., ~ilc[o-p]) on which that relation is known to be well-founded. But, as noted elsewhere (~pl[well-founded-relation]), every known well-founded relation has a unique domain predicate associated with it and so it suffices to identify simply the relation here. The ~ilc[xargs] field of a ~ilc[declare] permits the explicit specification of any known well-founded relation with the keyword ~c[:]~ilc[well-founded-relation]. An example is given below. If the ~ilc[xargs] for a ~c[defun] specifies a well-founded relation, that relation and its associated domain predicate are used in generating the termination conditions for the definition. If no ~c[:]~ilc[well-founded-relation] is specified, ~c[defun] uses the ~c[:]~ilc[well-founded-relation] specified in the ~ilc[acl2-defaults-table]. ~l[set-well-founded-relation] to see how to set the default well-founded relation (and, implicitly, its domain predicate). The initial default well-founded relation is ~ilc[o<] (with domain predicate ~ilc[o-p]). This completes the brief sketch of the ACL2 definitional principle. Optionally, ~pl[ruler-extenders] for a more detailed discussion of the termination analysis and resulting proof obligations for admissibility, as well as a discussion of the relation to how ACL2 stores induction schemes. On very rare occasions ACL2 will seem to \"hang\" when processing a definition, especially if there are many subexpressions of the body whose function symbol is ~ilc[if] (or which macroexpand to such an expression). In those cases you may wish to supply the following to ~ilc[xargs]: ~c[:normalize nil]. This is an advanced feature that turns off ACL2's usual propagation upward of ~c[if] tests. The following example illustrates all of the available declarations and most hint keywords, but is completely nonsensical. For documentation, ~pl[xargs] and ~pl[hints]. ~bv[] (defun example (x y z a b c i j) (declare (ignore a b c) (type integer i j) (xargs :guard (symbolp x) :measure (- i j) :ruler-extenders :basic :well-founded-relation my-wfr :hints ((\"Goal\" :do-not-induct t :do-not '(generalize fertilize) :expand ((assoc x a) (member y z)) :restrict ((<-trans ((x x) (y (foo x))))) :hands-off (length binary-append) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))))) :guard-hints ((\"Subgoal *1/3'\" :use ((:instance assoc-of-append (x a) (y b) (z c))))) :mode :logic :normalize nil :verify-guards nil :non-executable t :otf-flg t)) (example-body x y z i j)) ~ev[]~/ :cited-by Programming" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defun-fn (list 'quote def) 'state (list 'quote event-form) #+:non-standard-analysis ; std-p nil)) #+(and acl2-loop-only :non-standard-analysis) (defmacro defun-std (&whole event-form &rest def) ":Doc-Section acl2::Events define a function symbol~/~/ ~l[defun] for details. (More documentation on features related to non-standard analysis may be available in the future.)" (list 'defun-fn (list 'quote def) 'state (list 'quote event-form) t)) #+acl2-loop-only (defmacro defuns (&whole event-form &rest def-lst) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section Miscellaneous an alternative to ~ilc[mutual-recursion]~/ ~bv[] Example: (DEFUNS (evenlp (x) (if (consp x) (oddlp (cdr x)) t)) (oddlp (x) (if (consp x) (evenlp (cdr x)) nil)))~/ General Form: (DEFUNS defuns-tuple1 ... defuns-tuplen) ~ev[] is equivalent to ~bv[] (MUTUAL-RECURSION (DEFUN . defuns-tuple1) ... (DEFUN . defuns-tuplen)) ~ev[] In fact, ~c[defuns] is the more primitive of the two and ~ilc[mutual-recursion] is just a macro that expands to a call of ~ilc[defun] after stripping off the ~ilc[defun] at the ~ilc[car] of each argument to ~ilc[mutual-recursion]. We provide and use ~ilc[mutual-recursion] rather than ~c[defuns] because by leaving the ~ilc[defun]s in place, ~ilc[mutual-recursion] forms can be processed by the Emacs ~c[tags] program. ~l[mutual-recursion]." ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defuns-fn (list 'quote def-lst) 'state (list 'quote event-form) #+:non-standard-analysis ; std-p nil)) #+(and acl2-loop-only :non-standard-analysis) (defmacro defuns-std (&whole event-form &rest def-lst) ":Doc-Section Miscellaneous an alternative to ~ilc[mutual-recursion]~/~/ ~l[defuns] for details. (More documentation on features related to non-standard analysis may be available in the future.)" (list 'defuns-fn (list 'quote def-lst) 'state (list 'quote event-form) t)) (defmacro verify-termination (&rest lst) ":Doc-Section Events convert a function from :program mode to :logic mode~/ ~bv[] Example: (verify-termination fact)~/ General Forms: (verify-termination fn dcl ... dcl) (verify-termination (fn1 dcl ... dcl) (fn2 dcl ... dcl) ...) ~ev[] where ~c[fn] and the ~c[fni] are function symbols having ~c[:]~ilc[program] mode (~pl[defun-mode]) and all of the ~c[dcl]s are either ~ilc[declare] forms or ~il[documentation] strings. The first form above is an abbreviation for ~bv[] (verify-termination (fn dcl ... dcl)) ~ev[] so we limit our discussion to the second form. Each of the ~c[fni] must be in the same clique of mutually recursively defined functions, but not every function in the clique need be among the ~c[fni]. ~c[Verify-termination] attempts to establish the admissibility of the ~c[fni]. ~c[Verify-termination] retrieves their definitions, creates modified definitions using the ~c[dcl]s supplied above, and resubmits these definitions. You could avoid using ~c[verify-termination] by typing the new definitions yourself. So in that sense, ~c[verify-termination] adds no new functionality. But if you have prototyped your system in ~c[:]~ilc[program] mode and tested it, you can use ~c[verify-termination] to resubmit your definitions and change their ~il[defun-mode]s to ~c[:]~ilc[logic], addings ~il[hints] without having to retype or recopy the code. The ~ilc[defun] ~il[command] executed by ~c[verify-termination] is obtained by retrieving the ~ilc[defun] (or ~ilc[mutual-recursion]) ~il[command] that introduced the clique in question and then possibly modifying each definition as follows. Consider a function, ~c[fn], in the clique. If ~c[fn] is not among the ~c[fni] above, its definition is left unmodified other than to add ~c[(declare (xargs :mode :logic))]. Otherwise, ~c[fn] is some ~c[fni] and we modify its definition by inserting into it the corresponding ~c[dcl]s listed with ~c[fni] in the arguments to ~c[verify-termination], as well as ~c[(declare (xargs :mode :logic))]. In addition, we throw out from the old declarations in ~c[fn] the ~c[:mode] specification and anything that is specified in the new ~c[dcl]s. For example, suppose that ~c[fact] was introduced with: ~bv[] (defun fact (n) (declare (type integer n) (xargs :mode :program)) (if (zp n) 1 (* n (fact (1- n))))). ~ev[] Suppose later we do ~c[(verify-termination fact)]. Then the following definition is submitted. ~bv[] (defun fact (n) (declare (type integer n)) (if (zp n) 1 (* n (fact (1- n))))). ~ev[] Observe that this is the same definition as the original one, except the old specification of the ~c[:mode] has been deleted so that the ~il[defun-mode] now defaults to ~c[:]~ilc[logic]. Although the termination proof succeeds, ACL2 also tries to verify the ~il[guard], because we have (implicitly) provided a ~il[guard], namely ~c[(integerp n)], for this function. (~l[guard] for a general discussion of guards, and ~pl[type-spec] for a discussion of how type declarations are used in guards.) Unfortunately, the ~il[guard] verification fails, because the subterm ~c[(zp n)] requires that ~c[n] be nonnegative, as can be seen by invoking ~c[:args zp]. (For a discussion of termination issues relating to recursion on the naturals, ~pl[zero-test-idioms].) So we might be tempted to submit the following: ~bv[] (verify-termination fact (declare (xargs :guard (and (integerp n) (<= 0 n))))). ~ev[] However, this is considered a changing of the guard (from ~c[(integerp n)]), which is illegal. If we instead change the guard in the earlier ~c[defun] after undoing that earlier definition with ~c[:]~ilc[ubt]~c[ fact], then ~c[(verify-termination fact)] will succeed. ~st[Remark on system functions.] There may be times when you want to apply ~c[verify-termination] (and also, perhaps, ~ilc[verify-guards]) to functions that are predefined in ACL2. It may be necessary in such cases to modify the system code first. See Part II of ~url[http://www.cs.utexas.edu/users/moore/acl2/open-architecture/] for a discussion of the process for contributing updates to the system code and ~il[books] with such ~c[verify-termination] or ~ilc[verify-guards] ~il[events], perhaps resulting in more system functions being built-in as ~il[guard]-verified. To see which built-in functions have already received such treatment, see community books directory ~c[books/system/]; or, evaluate the constant ~c[*system-verify-guards-alist*], each of whose entries associates the name of a community book with a list of functions whose guard-verification is proved by including that book. See the above URL for more details. Note that if ~c[fn1] is already in ~c[:]~ilc[logic] mode, then the ~c[verify-termination] call has no effect. It is generally considered to be redundant, in the sense that it returns without error; but if the ~c[fn1] is a constrained function (i.e., introduced in the signature of an ~ilc[encapsulate], or by ~ilc[defchoose]), then an error occurs. This error is intended to highlight unintended uses of ~c[verify-termination]; but if you do not want to see an error in this case, you can write and use your own macro in place of ~c[verify-termination]. The following explanation of the implementation of ~c[verify-termination] may help with such a task. We conclude with a discussion of the use of ~ilc[make-event] to implement ~c[verify-termination]. This discussion can be skipped; we include it only for those who want to create variants of ~c[verify-termination], or who are interested in seeing an application of ~ilc[make-event]. Consider the following proof of ~c[nil], which succeeded up through Version_3.4 of ACL2. ~bv[] (encapsulate () (defun foo (x y) (declare (xargs :mode :program)) (if (or (zp x) (zp y)) (list x y) (foo (1+ x) (1- y)))) (local (defun foo (x y) (declare (xargs :measure (acl2-count y))) (if (or (zp x) (zp y)) (list x y) (foo (1+ x) (1- y))))) (verify-termination foo)) (defthm bad-lemma (zp x) :hints ((\"Goal\" :induct (foo x 1))) :rule-classes nil) ~ev[] How did this work? In the first pass of the ~ilc[encapsulate], the second ~ilc[defun] of ~c[foo] promoted ~c[foo] from ~c[:program] to ~c[:logic] mode, with ~c[y] as the unique measured variable. The following call to ~c[verify-termination] was then redundant. However, on the second pass of the ~ilc[encapsulate], the second (~ilc[local]) definition of ~c[foo] was skipped, and the ~c[verify-termination] event then used the first definition of ~c[foo] to guess the measure, based (as with all guesses of measures) on a purely syntactic criterion. ACL2 incorrectly chose ~c[(acl2-count x)] as the measure, installing ~c[x] as the unique measured variable, which in turn led to an unsound induction scheme subsequently used to prove ~c[nil] (lemma ~c[bad-lemma], above) Now, ~c[verify-termination] is a macro whose calls expand to ~ilc[make-event] calls. So in the first pass above, the ~c[verify-termination] call generated a ~c[defun] event identical to the ~ilc[local] ~ilc[defun] of ~c[foo], which was correctly identified as redundant. That expansion was recorded, and on the second pass of the ~ilc[encapsulate], the expansion was recalled and used in place of the ~c[verify-termination] call (that is how ~ilc[make-event] works). So instead of a measure being guessed for the ~c[verify-termination] call on the second pass, the same measure was used as was used on the first pass, and a sound induction scheme was stored. The attempt to prove ~c[nil] (lemma ~c[bad-lemma]) then failed." `(make-event (verify-termination-fn ',lst state))) #+acl2-loop-only (defmacro verify-termination-boot-strap (&whole event-form &rest lst) ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'verify-termination-boot-strap-fn (list 'quote lst) 'state (list 'quote event-form))) #+acl2-loop-only (defmacro verify-guards (&whole event-form name &key hints otf-flg guard-debug doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Note: If you change the default for guard-debug, then consider changing it in ; chk-acceptable-defuns as well, and fix the "Otherwise" message about ; :guard-debug in prove-guard-clauses. ":Doc-Section Events verify the ~il[guard]s of a function~/ ~l[guard] for a general discussion of guards. Before discussing the ~c[verify-guards] event, we first discuss guard verification, which can take place at definition time or, later, using ~c[verify-guards]. Typically, guard verification takes place at definition time if a guard (or type, or ~il[stobjs]) has been supplied explicitly unless ~c[:verify-guards nil] has been specified; ~pl[defun] and ~pl[xargs], and ~pl[set-verify-guards-eagerness] for how to change this default. The point of guard verification is to ensure that during evaluation of an expression without free variables, no guard violation takes place. Technical note: the first argument of ~c[verify-guards] must be a function symbol or the name of a ~ilc[defthm] or ~ilc[defaxiom] event, not a macro-alias for a function symbol (~pl[macro-aliases-table]). ~l[verify-guards+] for a utility that does not have this restriction. Guard verification is intended to guarantee that for any call of a given function, if its ~il[guard] holds for that call then the ~il[guard] will hold for every function call in the body of that function. Moreover, in order to avoid guard violations during evaluation of the function's guard itself, guard verification also is intended to guarantee that the guards are satisfied for all calls in the guard itself. Consider the following simple example. ~bv[] (defun f (x) (declare (xargs :guard (and (consp x) (integerp (car x))))) (if (rationalp (cdr x)) (+ (car x) (cdr x)) 17)) ~ev[] If you evaluate ~c[(f t)], for example, in the top-level loop, you will (by default) get a guard error. The point of guard verification is to guarantee the absence of guard errors, and we start by using this example to illustrate the proof obligations that guarantee such absence. The body of the above definition has the following function calls, where the first is the entire body. ~bv[] (if (rationalp (cdr x)) (< (car x) (cdr x)) 17) (rationalp (cdr x)) ; the test of the top-level IF call (cdr x) ; from (rationalp (cdr x)) (< (car x) (cdr x)) ; the true branch of the top-level IF call (car x) ; from (< (car x) (cdr x)) (cdr x) ; from (< (car x) (cdr x)) ~ev[] We thus see potentially six conditions to prove, one for each call. The guards of the function symbols of those calls are ~c[t] for ~ilc[if] and ~ilc[rationalp], ~c[(or (consp x) (equal x nil))] for both ~c[(car x)] and ~c[(cdr x)], and finally that both arguments are rationals for ~c[<]. Moreover, we can take advantage of ``contextual assumptions'': the ~c[if]-test conditions and the top-level ~c[:guard]. Thus, for ~c[verify-guards] the proof obligation from the body of ~c[f] is as follows. ~bv[] (implies (and (consp x) (integerp (car x))) ; from the :guard (and t ; from the top-level IF call t ; from (rationalp (cdr x)) (or (consp x) (equal x nil)) ; from the first (cdr x) (implies (rationalp (cdr x)) ; IF-test for calls in the true branch (and (or (consp x) (equal x nil)) ; from (car x) (or (consp x) (equal x nil)) ; from the second (cdr x) (and (rationalp (car x)) (rationalp (cdr x))) ; from the < call )))) ~ev[] But the ~c[:guard] itself generates a similar sort of proof obligation. Note that the guard ~c[(and (consp x) (integerp (car x)))] is really an abbreviation (i.e. via the macro ~ilc[AND]) for the term ~c[(if (consp x) (integerp (car x)) nil)]. The guard proof obligation for the guard itself is thus as follows. ~bv[] (and t ; from (consp x) (implies (consp x) (and t ; from (integerp (car x)) ; (consp x) ; from (car x) ; ))) ~ev[] All of the above proof obligations are indeed theorems, and guard verification succeeds for the above definition of ~c[f]. The example above illustrates the general procedure for generating the guard proof obligation. Each function call is considered in the body or guard of the function, and it is required that the guard is met for that call, under certain ``contextual assumptions'', which are as follows. In the case of the body of the named function, it is assumed that the guard holds for that function on its formal parameters. And in both cases ~-[] the body of the named function and also its guard ~-[] the governing tests from superior calls of ~ilc[IF] are also assumed. As mentioned above, if the guard on a function is not ~c[t], then guard verification requires not only consideration of the body under the assumption that the guard is true, but also consideration of the guard itself. Thus, for example, guard verification fails in the following example, even though there are no proof obligations arising from the body, because the guard itself can cause a guard violation when evaluated for an arbitrary value of ~c[x]: ~bv[] (defun foo (x) (declare (xargs :guard (car x))) x) ~ev[] We turn now to the ~c[verify-guards] event as a way of verifying the ~il[guard]s for a function or theorem. ~bv[] Examples: (verify-guards flatten) (verify-guards flatten :hints ((\"Goal\" :use (:instance assoc-of-app))) :otf-flg t :guard-debug t ; default = nil :doc \"string\")~/ General Form: (verify-guards name :hints hints :otf-flg otf-flg :guard-debug t ; typically t, but any value is legal :doc doc-string) ~ev[] In the General Form above, ~c[name] is the name of a ~c[:]~ilc[logic] function (~pl[defun-mode]) or of a theorem or axiom. In the most common case ~c[name] is the name of a function that has not yet had its ~il[guard]s verified, each subroutine of which has had its ~il[guard]s verified. The values ~ilc[hints], ~ilc[otf-flg], and ~ilc[guard-debug] are as described in the corresponding ~il[documentation] entries; and ~ilc[doc-string], if supplied, is a string ~st[not] beginning with ``~c[:Doc-Section]''. The four keyword arguments above are all optional. To admit this event, the conjunction of the guard proof obligations must be proved. If that proof is successful, ~c[name] is considered to have had its ~il[guard]s verified. ~l[verify-guards-formula] for a utility that lets you view the formula to be proved by ~c[verify-guards], but without creating an event. If ~c[name] is one of several functions in a mutually recursive clique, ~c[verify-guards] will attempt to verify the ~il[guard]s of all of the functions. If ~c[name] is a theorem or axiom name, ~c[verify-guards] verifies the guards of the associated formula. When a theorem has had its guards verified then you know that the theorem will evaluate to non-~c[nil] in all Common Lisps, without causing a runtime error (other than possibly a resource error). In particular, you know that the theorem's validity does not depend upon ACL2's arbitrary completion of the domains of partial Common Lisp functions. For example, if ~c[app] is defined as ~bv[] (defun app (x y) (declare (xargs :guard (true-listp x))) (if (endp x) y (cons (car x) (app (cdr x) y)))) ~ev[] then we can verify the guards of ~c[app] and we can prove the theorem: ~bv[] (defthm assoc-of-app (equal (app (app a b) c) (app a (app b c)))) ~ev[] However, if you go into almost any Common Lisp in which ~c[app] is defined as shown and evaluate ~bv[] (equal (app (app 1 2) 3) (app 1 (app 2 3))) ~ev[] we get an error or, perhaps, something worse like ~c[nil]! How can this happen since the formula is an instance of a theorem? It is supposed to be true! It happens because the theorem exploits the fact that ACL2 has completed the domains of the partially defined Common Lisp functions like ~ilc[car] and ~ilc[cdr], defining them to be ~c[nil] on all non-conses. The formula above violates the guards on ~c[app]. It is therefore ``unreasonable'' to expect it to be valid in Common Lisp. But the following formula is valid in Common Lisp: ~bv[] (if (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c))) t) ~ev[] That is, no matter what the values of ~c[a], ~c[b] and ~c[c] the formula above evaluates to ~c[t] in all Common Lisps (unless the Lisp engine runs out of memory or stack computing it). Furthermore the above formula is a theorem: ~bv[] (defthm guarded-assoc-of-app (if (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c))) t)) ~ev[] This formula, ~c[guarded-assoc-of-app], is very easy to prove from ~c[assoc-of-app]. So why prove it? The interesting thing about ~c[guarded-assoc-of-app] is that we can verify the guards of the formula. That is, ~c[(verify-guards guarded-assoc-of-app)] succeeds. Note that it has to prove that if ~c[a] and ~c[b] are true lists then so is ~c[(app a b)] to establish that the guard on the outermost ~c[app] on the left is satisfied. By verifying the guards of the theorem we know it will evaluate to true in all Common Lisps. Put another way, we know that the validity of the formula does not depend on ACL2's completion of the partial functions or that the formula is ``well-typed.'' One last complication: The careful reader might have thought we could state ~c[guarded-assoc-of-app] as ~bv[] (implies (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c)))) ~ev[] rather than using the ~c[if] form of the theorem. We cannot! The reason is technical: ~ilc[implies] is defined as a function in ACL2. When it is called, both arguments are evaluated and then the obvious truth table is checked. That is, ~c[implies] is not ``lazy.'' Hence, when we write the guarded theorem in the ~c[implies] form we have to prove the guards on the conclusion without knowing that the hypothesis is true. It would have been better had we defined ~c[implies] as a macro that expanded to the ~c[if] form, making it lazy. But we did not and after we introduced guards we did not want to make such a basic change. Recall however that ~c[verify-guards] is almost always used to verify the guards on a function definition rather than a theorem. We now return to that discussion. Because ~c[name] is not uniquely associated with the ~c[verify-guards] event (it necessarily names a previously defined function) the ~il[documentation] string, ~ilc[doc-string], is not stored in the ~il[documentation] database. Thus, we actually prohibit ~ilc[doc-string] from having the form of an ACL2 ~il[documentation] string; ~pl[doc-string]. ~c[Verify-guards] must often be used when the value of a recursive call of a defined function is given as an argument to a subroutine that is ~il[guard]ed. An example of such a situation is given below. Suppose ~c[app] (read ``append'') has a ~il[guard] requiring its first argument to be a ~ilc[true-listp]. Consider ~bv[] (defun rev (x) (declare (xargs :guard (true-listp x))) (cond ((endp x) nil) (t (app (rev (cdr x)) (list (car x)))))) ~ev[] Observe that the value of a recursive call of ~c[rev] is being passed into a ~il[guard]ed subroutine, ~c[app]. In order to verify the ~il[guard]s of this definition we must show that ~c[(rev (cdr x))] produces a ~ilc[true-listp], since that is what the ~il[guard] of ~c[app] requires. How do we know that ~c[(rev (cdr x))] is a ~ilc[true-listp]? The most elegant argument is a two-step one, appealing to the following two lemmas: (1) When ~c[x] is a ~ilc[true-listp], ~c[(cdr x)] is a ~ilc[true-listp]. (2) When ~c[z] is a ~ilc[true-listp], ~c[(rev z)] is a ~ilc[true-listp]. But the second lemma is a generalized property of ~c[rev], the function we are defining. This property could not be stated before ~c[rev] is defined and so is not known to the theorem prover when ~c[rev] is defined. Therefore, we might break the admission of ~c[rev] into three steps: define ~c[rev] without addressing its ~il[guard] verification, prove some general properties about ~c[rev], and then verify the ~il[guard]s. This can be done as follows: ~bv[] (defun rev (x) (declare (xargs :guard (true-listp x) :verify-guards nil)) ; Note this additional xarg. (cond ((endp x) nil) (t (app (rev (cdr x)) (list (car x)))))) (defthm true-listp-rev (implies (true-listp x2) (true-listp (rev x2)))) (verify-guards rev) ~ev[] The ACL2 system can actually admit the original definition of ~c[rev], verifying the ~il[guard]s as part of the ~ilc[defun] event. The reason is that, in this particular case, the system's heuristics just happen to hit upon the lemma ~c[true-listp-rev]. But in many more complicated functions it is necessary for the user to formulate the inductively provable properties before ~il[guard] verification is attempted. ~st[Remark on computation of guard conjectures and evaluation]. When ACL2 computes the ~il[guard] conjecture for the body of a function, it evaluates any ground subexpressions (those with no free variables), for calls of functions whose ~c[:]~ilc[executable-counterpart] ~il[rune]s are ~ilc[enable]d. Note that here, ``enabled'' refers to the current global ~il[theory], not to any ~c[:]~ilc[hints] given to the guard verification process; after all, the guard conjecture is computed even before its initial goal is produced. Also note that this evaluation is done in an environment as though ~c[:set-guard-checking :all] had been executed, so that we can trust that this evaluation takes place without guard violations; ~pl[set-guard-checking]. If you want to verify the ~il[guard]s on functions that are built into ACL2, you will first need to put them into ~c[:]~ilc[logic] mode. ~l[verify-termination], specifically the ``Remark on system functions'' in that ~il[documentation]." ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'verify-guards-fn (list 'quote name) 'state (list 'quote hints) (list 'quote otf-flg) (list 'quote guard-debug) (list 'quote doc) (list 'quote event-form))) (defmacro verify-guards+ (name &rest rest) ; We considered renaming verify-guards as verify-guards-basic, and then ; defining verify-guards on top of verify-guards-basic just as we now define ; verify-guards+ on top of verify-guards. But that could be complicated to ; carry out during the boot-strap, and it could be challenging to present a ; nice view to the user, simulataneously promoting the fiction that ; verify-guards is a primitive while giving accurate feedback. So we are ; leaving verify-guards as the primitive, but improving it to point to ; verify-guards+ when there is a macro alias. ; The example in the documentation below doesn't immediately yield a proof of ; nil, but perhaps mbe could be used for that (we haven't tried). At any rate, ; violation of the intent of guard verification is bad enough. ":Doc-Section Events verify the ~il[guard]s of a function~/ We assume familiarity with ~il[guard] verification; ~pl[verify-guards]. Unlike ~c[verify-guards], the macro call ~c[(verify-guards+ mac ...)] will verify guards for a function, ~c[fn], such that the macro ~c[mac] is associated with the function symbol ~c[fn] in ~ilc[macro-aliases-table] (also ~pl[add-macro-alias]). For example, if you define a macro ~c[app] and list append function ~c[binary-app], and you associate macro ~c[app] with function symbol ~c[binary-app] in ~ilc[macro-aliases-table], then evaluation of the form ~c[(verify-guard+ app)] will have the effect of evaluating ~c[(verify-guards fn)]. Note that in this setting, evaluation of ~c[(verify-guard app)] would cause an error, because ~c[app] is a macro and ~c[verify-guards], unlike ~c[verify-guards+], cannot handle macros.~/ The rest of this ~il[documentation] topic discusses why we do not simply arrange that ~c[verify-guards] be permitted to take a macro alias. The following example shows a soundness issue in doing so. ~bv[] (encapsulate () (defun f1 (x) (declare (xargs :guard (consp x) :verify-guards nil)) (car x)) (defun f2 (x) (declare (xargs :guard t :verify-guards nil)) (cdr x)) (defmacro mac (x) x) (add-macro-alias mac f2) ; silly macro alias ; (local (add-macro-alias mac f1)) ; alternate silly macro alias ; (verify-guards mac)) ~ev[] If we were to allow macro aliases in ~ilc[verify-guards], this event would be admitted, because on the first pass we are verifying guards of ~c[f1]. But after the ~ilc[encapsulate] form completes evaluation, it would appear that ~c[f2] is guard-verified. That could of course cause a raw Lisp error. The enhanced functionality provided by ~c[verify-guards+] does not have the above problem, because it takes advantage of ~ilc[make-event] to avoid taking advantage of the contradictory results produced by the two calls of ~c[add-macro-alias]. ~l[make-event]. If the specific example above is modified by replacing ~c[verify-guards] with ~c[verify-guards+], then the first pass through the ~ilc[encapsulate] form will expand the form ~c[(verify-guards+ mac)] to ~c[(verify-guards f1)]. That same expansion will be used for the ~c[verify-guards+] call during the second pass through the ~c[encapsulate] form, which is evaluated successfully and leaves us in a ~il[world] where ~c[f1] is guard-verified and ~c[f2] is not.~/" `(make-event (let* ((name ',name) (rest ',rest) (fn (deref-macro-name name (macro-aliases (w state))))) (pprogn (observation 'verify-guards+ "Attempting to verify guards for ~x0." fn) (value (list* 'verify-guards fn rest)))) :expansion? (verify-guards ,name ,@rest))) (defdoc defpun ":Doc-Section acl2::Events define a tail-recursive function symbol~/~/ ~c[Defpun] is a macro developed by Pete Manolios and J Moore that allows tail-recursive definitions. It is defined in community book ~c[books/misc/defpun.lisp], so to use it, execute the following event. ~bv[] (include-book \"misc/defpun\" :dir :system) ~ev[] Details of defpun are provided by Manolios and Moore in the ``Partial Functions in ACL2'' published with the ACL2 2000 workshop; see ~url[http://www.cs.utexas.edu/users/moore/acl2/workshop-2000/]. Also see ~url[http://www.cs.utexas.edu/users/moore/publications/defpun/index.html]. A variant, ~c[defp], has been developed by Matt Kaufmann to allow more general forms of tail recursion. If ~c[defpun] doesn't work for you, try ~c[defp] by first executing the following event. ~bv[] (include-book \"misc/defp\" :dir :system) ~ev[] Sandip Ray has contributed a variant of ~c[defpun], ~c[defpun-exec], that supports executability. See community book ~c[books/defexec/defpun-exec/defpun-exec.lisp]: ~bv[] (include-book \"defexec/defpun-exec/defpun-exec\" :dir :system) ~ev[] He has also contributed community book ~c[books/misc/misc2/defpun-exec-domain-example.lisp], for functions that are uniquely defined in a particular domain.") #+acl2-loop-only (defmacro defmacro (&whole event-form &rest mdef) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section acl2::Events define a macro~/ ~bv[] Example Defmacros: (defmacro xor (x y) (list 'if x (list 'not y) y)) (defmacro git (sym key) (list 'getprop sym key nil '(quote current-acl2-world) '(w state))) (defmacro one-of (x &rest rst) (declare (xargs :guard (symbol-listp rst))) (cond ((null rst) nil) (t (list 'or (list 'eq x (list 'quote (car rst))) (list* 'one-of x (cdr rst)))))) Example Expansions: term macroexpansion (xor a b) (if a (not b) b) (xor a (foo b)) (if a (not (foo b)) (foo b)) (git 'car 'lemmas) (getprop 'car 'lemmas nil 'current-acl2-world (w state)) (one-of x a b c) (or (eq x 'a) (or (eq x 'b) (or (eq x 'c) nil))) (one-of x 1 2 3) ill-formed (guard violation)~/ General Form: (defmacro name macro-args doc-string dcl ... dcl body) ~ev[] where ~c[name] is a new symbolic name (~pl[name]), ~c[macro-args] specifies the formal parameters of the macro, and ~c[body] is a term. The formal parameters can be specified in a much more general way than is allowed by ACL2 ~ilc[defun] ~il[events]; ~pl[macro-args] for a description of keyword (~c[&key]) and optional (~c[&optional]) parameters as well as other so-called ``lambda-list keywords'', ~c[&rest] and ~c[&whole]. ~ilc[Doc-string] is an optional ~il[documentation] string; ~pl[doc-string]. Each ~c[dcl] is an optional declaration (~pl[declare]) except that the only ~ilc[xargs] keyword permitted by ~c[defmacro] is ~c[:]~ilc[guard]. For compute-intensive applications see the community book ~c[misc/defmac.lisp], which can speed up macroexpansion by introducing an auxiliary ~c[defun]. For more information, evaluate the form ~c[(include-book \"misc/defmac\" :dir :system)] and then evaluate ~c[:doc defmac]. Macroexpansion occurs when a form is read in, i.e., before the evaluation or proof of that form is undertaken. To experiment with macroexpansion, ~pl[trans]. When a form whose ~ilc[car] is ~c[name] arises as the form is read in, the arguments are bound as described in CLTL pp. 60 and 145, the ~il[guard] is checked, and then the ~c[body] is evaluated. The result is used in place of the original form. In ACL2, macros do not have access to the ACL2 state ~ilc[state]. (If ~ilc[state] or any user-defined stobj (~pl[stobj]) is a macro argument, it is treated as an ordinary variable, bound at macro-expansion time to a piece of syntax.) This is in part a reflection of CLTL, p. 143, ``More generally, an implementation of Common Lisp has great latitude in deciding exactly when to expand macro calls with a program. ... Macros should be written in such a way as to depend as little as possible on the execution environment to produce a correct expansion.'' In ACL2, the product of macroexpansion is independent of the current environment and is determined entirely by the macro body and the functions and constants it references. It is possible, however, to define macros that produce expansions that refer to ~ilc[state] or other single-threaded objects (~pl[stobj]) or variables not among the macro's arguments. See the ~c[git] example above. For a related utility that does have access to the ACL2 ~il[state], ~pl[make-event].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defmacro-fn (list 'quote mdef) 'state (list 'quote event-form))) #+acl2-loop-only (defmacro defconst (&whole event-form name form &optional doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section acl2::Events define a constant~/ ~bv[] Examples: (defconst *digits* '(0 1 2 3 4 5 6 7 8 9)) (defconst *n-digits* (the unsigned-byte (length *digits*)))~/ General Form: (defconst name term doc-string) ~ev[] where ~c[name] is a symbol beginning and ending with the character ~c[*], ~c[term] is a variable-free term that is evaluated to determine the value of the constant, and ~ilc[doc-string] is an optional ~il[documentation] string (~pl[doc-string]). When a constant symbol is used as a ~il[term], ACL2 replaces it by its value; ~pl[term]. Note that ~c[defconst] uses a ``safe mode'' to evaluate its form, in order to avoids soundness issues but with an efficiency penalty (perhaps increasing the evaluation time by several hundred percent). If efficiency is a concern, or if for some reason you need the form to be evaluated without safe mode (e.g., you are an advanced system hacker using trust tags to traffic in raw Lisp code), consider using the macro ~c[defconst-fast] instead, defined in community book ~c[books/make-event/defconst-fast.lisp], for example: ~bv[] (defconst-fast *x* (expensive-fn ...)) ~ev[] A more general utility may be found in community book ~c[books/tools/defconsts.lisp]. Also ~pl[using-tables-efficiently] for an analogous issue with ~ilc[table] events. It may be of interest to note that ~c[defconst] is implemented at the lisp level using ~c[defparameter], as opposed to ~c[defconstant]. (Implementation note: this is important for proper support of undoing and redefinition.) We close with a technical remark, perhaps of interest only to users of ACL2(h), the experimental extension of ACL2 that supports hash cons, function memoization, and hash-table-based ``fast alists''; ~pl[hons-and-memoization]. For an event of the form ~c[(defconst *C* (quote OBJ))], i.e., ~c[(defconst *C* 'OBJ)], then the value associated with ~c[*C*] is ~c[OBJ]; that is, the value of ~c[*C*] is ~ilc[eq] to the actual object ~c[OBJ] occurring in the ~c[defconst] form. So for example, if ~ilc[make-event] is used to generate such a ~c[defconst] event, as it is in the two books mentioned above, and ~c[OBJ] is a fast alist (using ACL2(h)), then the value of ~c[*C*] is a fast alist. This guarantee disappears if the term in the ~c[defconst] form is not a quoted object, i.e., if it is not of the form ~c[(quote OBJ)].~/ :cited-by Programming" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defconst-fn (list 'quote name) (list 'quote form) 'state (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defmacro defthm (&whole event-form name term &key (rule-classes '(:REWRITE)) instructions hints otf-flg doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section Events prove and name a theorem~/ ~bv[] Examples: (defthm assoc-of-app (equal (app (app a b) c) (app a (app b c)))) ~ev[] The following nonsensical example illustrates all the optional arguments but is illegal because not all combinations are permitted. ~l[hints] for a complete list of ~il[hints]. ~bv[] (defthm main (implies (hyps x y z) (concl x y z)) :rule-classes (:REWRITE :GENERALIZE) :instructions (induct prove promote (dive 1) x (dive 2) = top (drop 2) prove) :hints ((\"Goal\" :do-not '(generalize fertilize) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))))) :otf-flg t :doc \"#0[one-liner/example/details]\")~/ General Form: (defthm name term :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :doc doc-string) ~ev[] where ~c[name] is a new symbolic name (~pl[name]), ~c[term] is a term alleged to be a theorem, and ~ilc[rule-classes], ~ilc[instructions], ~ilc[hints], ~ilc[otf-flg] and ~ilc[doc-string] are as described in their respective ~il[documentation]. The five keyword arguments above are all optional, however you may not supply both ~c[:]~ilc[instructions] and ~c[:]~ilc[hints], since one drives the proof checker and the other drives the theorem prover. If ~c[:]~ilc[rule-classes] is not specified, the list ~c[(:rewrite)] is used; if you wish the theorem to generate no rules, specify ~c[:]~ilc[rule-classes] ~c[nil]. When ACL2 processes a ~c[defthm] event, it first tries to prove the ~il[term] using the indicated hints (~pl[hints]) or ~il[instructions] (~pl[proof-checker]). If it is successful, it stores the rules described by the rule-classes (~pl[rule-classes]), proving the necessary corollaries.~/" (list 'defthm-fn (list 'quote name) (list 'quote term) 'state (list 'quote rule-classes) (list 'quote instructions) (list 'quote hints) (list 'quote otf-flg) (list 'quote doc) (list 'quote event-form) #+:non-standard-analysis ; std-p nil)) #+acl2-loop-only (defmacro defthmd (&whole event-form name term &rest rst) ":Doc-Section acl2::Events prove and name a theorem and then disable it~/~/ Use ~c[defthmd] instead of ~ilc[defthm] when you want to disable a theorem immediately after proving it. This macro has been provided for users who prefer working in a mode where theorems are only enabled when explicitly directed by ~c[:]~ilc[in-theory]. Specifically, the form ~bv[] (defthmd NAME TERM ...) ~ev[] expands to: ~bv[] (progn (defthmd NAME TERM ...) (with-output :off summary (in-theory (disable NAME))) (value-triple '(:defthmd NAME))). ~ev[] Note that ~c[defthmd] commands are never redundant (~pl[redundant-events]). Even if the ~c[defthm] event is redundant, then the ~ilc[in-theory] event will still be executed. The summary for the ~ilc[in-theory] event is suppressed. ~l[defthm] for documentation of ~c[defthm]." (declare (xargs :guard t) (ignore term rst)) (list 'progn (cons 'defthm (cdr event-form)) (list 'with-output :off 'summary (list 'in-theory (list 'disable name))) (list 'value-triple (list 'quote (xd-name 'defthmd name)) :on-skip-proofs t))) #+(and acl2-loop-only :non-standard-analysis) (defmacro defthm-std (&whole event-form name term &key (rule-classes '(:REWRITE)) instructions hints otf-flg doc) ":Doc-Section Events prove and name a theorem~/~/ ~l[defthm] for details. (More documentation on features related to non-standard analysis may be available in the future.)" (list 'defthm-fn (list 'quote name) (list 'quote term) 'state (list 'quote rule-classes) (list 'quote instructions) (list 'quote hints) (list 'quote otf-flg) (list 'quote doc) (list 'quote event-form) t)) #+acl2-loop-only (defmacro defaxiom (&whole event-form name term &key (rule-classes '(:REWRITE)) doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section Events add an axiom~/ WARNING: We strongly recommend that you not add axioms. If at all possible you should use ~ilc[defun] or ~ilc[mutual-recursion] to define new concepts recursively or use ~ilc[encapsulate] to constrain them constructively. If your goal is to defer a proof by using a top-down style, consider using ~ilc[skip-proofs]; see the discussion on ``Top-Down Proof'' in Section B.1.2 of ``Computer-Aided Reasoning: An Approach.'' Adding new axioms frequently renders the logic inconsistent. ~bv[] Example: (defaxiom sbar (equal t nil) :rule-classes nil :doc \":Doc-Section ...\")~/ General Form: (defaxiom name term :rule-classes rule-classes :doc doc-string) ~ev[] where ~c[name] is a new symbolic name (~pl[name]), ~c[term] is a term intended to be a new axiom, and ~ilc[rule-classes] and ~ilc[doc-string] are as described in the corresponding ~il[documentation] topics . The two keyword arguments are optional. If ~c[:]~ilc[rule-classes] is not supplied, the list ~c[(:rewrite)] is used; if you wish the axiom to generate no rules, specify ~c[:]~ilc[rule-classes] ~c[nil].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defaxiom-fn (list 'quote name) (list 'quote term) 'state (list 'quote rule-classes) (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defmacro deflabel (&whole event-form name &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events build a landmark and/or add a ~il[documentation] topic~/ ~bv[] Examples: (deflabel interp-section :doc \":Doc-Section ...\")~/ General Form: (deflabel name :doc doc-string) ~ev[] where ~c[name] is a new symbolic name (~pl[name]) and ~ilc[doc-string] is an optional ~il[documentation] string (~pl[doc-string]). This event adds the ~il[documentation] string for symbol ~c[name] to the ~c[:]~ilc[doc] database. By virtue of the fact that ~c[deflabel] is an event, it also marks the current ~il[history] with the ~c[name]. Thus, even undocumented labels are convenient as landmarks in a proof development. For example, you may wish to undo back through some label or compute a theory expression (~pl[theories]) in terms of some labels. ~c[Deflabel] ~il[events] are never considered redundant. ~l[redundant-events]. ~l[defdoc] for a means of attaching a ~il[documentation] string to a name without marking the current ~il[history] with that name.~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'deflabel-fn (list 'quote name) 'state (list 'quote doc) (list 'quote event-form))) (deflabel theories :doc ":Doc-Section Theories sets of ~il[rune]s to ~il[enable]/~il[disable] in concert~/ ~bv[] Example: '((:definition app) ; or (:d app) (:executable-counterpart app) (:i app) rv (rv) assoc-of-app) ~ev[] See:~/ A theory is a list of ``runic designators'' as described below. Each runic designator denotes a set of ``runes'' (~pl[rune]) and by unioning together the runes denoted by each member of a theory we define the set of runes corresponding to a theory. Theories are used to control which rules are ``~il[enable]d,'' i.e., available for automatic application by the theorem prover. There is always a ``current'' theory. A rule is ~il[enable]d precisely if its ~il[rune] is an element of the set of ~il[rune]s corresponding to the current theory. At the top-level, the current theory is the theory selected by the most recent ~ilc[in-theory] event, extended with the rule names introduced since then. Inside the theorem prover, the ~c[:]~ilc[in-theory] hint (~pl[hints]) can be used to select a particular theory as current during the proof attempt for a particular goal. Theories are generally constructed by ``theory expressions.'' Formally, a theory expression is any term, containing at most the single free variable ~ilc[world], that when evaluated with ~ilc[world] bound to the current ACL2 world (~pl[world]) produces a theory. ACL2 provides various functions for the convenient construction and manipulation of theories. These are called ``theory functions''(~pl[theory-functions]). For example, the theory function ~ilc[union-theories] takes two theories and produces their union. The theory function ~ilc[universal-theory] returns the theory containing all known rule names as of the introduction of a given logical name. But a theory expression can contain constants, e.g., ~bv[] '(len (len) (:rewrite car-cons) car-cdr-elim) ~ev[] and user-defined functions. The only important criterion is that a theory expression mention no variable freely except ~ilc[world] and evaluate to a theory. More often than not, theory expressions typed by the user do not mention the variable ~ilc[world]. This is because user-typed theory expressions are generally composed of applications of ACL2's theory functions. These ``functions'' are actually macros that expand into terms in which ~ilc[world] is used freely and appropriately. Thus, the technical definition of ``theory expression'' should not mislead you into thinking that interestng theory expressions must mention ~ilc[world]; they probably do and you just didn't know it! One aspect of this arrangement is that theory expressions cannot generally be evaluated at the top-level of ACL2, because ~ilc[world] is not bound. To see the value of a theory expression, ~c[expr], at the top-level, type ~bv[] ACL2 !>(LET ((WORLD (W STATE))) expr). ~ev[] However, because the built-in theories are quite long, you may be sorry you printed the value of a theory expression! A theory is a true list of runic designators and to each theory there corresponds a set of ~il[rune]s, obtained by unioning together the sets of ~il[rune]s denoted by each runic designator. For example, the theory constant ~bv[] '(len (len) (:e nth) (:rewrite car-cons) car-cdr-elim) ~ev[] corresponds to the set of ~il[rune]s ~bv[] {(:definition len) (:induction len) (:executable-counterpart len) (:executable-counterpart nth) (:elim car-cdr-elim) (:rewrite car-cons)} . ~ev[] Observe that the theory contains five elements but its runic correspondent contains six. That is because runic designators can denote sets of several ~il[rune]s, as is the case for the first designator, ~c[len]. If the above theory were selected as current then the six rules named in its runic counterpart would be ~il[enable]d and all other rules would be ~il[disable]d. We now precisely define the runic designators and the set of ~il[rune]s denoted by each. When we refer below to the ``macro-aliases dereference of'' a symbol, ~c[symb], we mean the (function) symbol corresponding ~c[symb] in the macro-aliases-table if there is such a symbol, else ~c[symb] itself; ~pl[macro-aliases-table]. For example, the macro-aliases dereference of ~ilc[append] is ~ilc[binary-append], and the macro-aliases dereference of ~ilc[nth] is ~c[nth].~bq[] o A ~il[rune] is a runic designator and denotes the singleton set containing that rune. o Suppose that ~c[symb] is a symbol and ~c[symb'] is the macro-aliases dereference of ~c[symb], where ~c[symb'] is a function symbol introduced with a ~ilc[defun] (or ~ilc[defuns]) event. Then ~c[symb] is a runic designator and denotes the set containing the runes ~c[(:definition symb')] and ~c[(:induction symb')], omitting the latter if no such ~il[induction] rune exists (presumably because the definition of ~c[symb'] is not singly recursive). o Suppose that ~c[symb] is a symbol and ~c[symb'] is the macro-aliases dereference of ~c[symb], where ~c[symb'] is a function symbol introduced with a ~ilc[defun] (or ~ilc[defuns]) event. Then ~c[(symb)] is a runic designator and denotes the singleton set containing the rune ~c[(:executable-counterpart symb')]. o If ~c[symb] is the name of a ~ilc[defthm] (or ~ilc[defaxiom]) event that introduced at least one rule, then ~c[symb] is a runic designator and denotes the set of the names of all rules introduced by the named event. o If ~c[str] is the string naming some ~ilc[defpkg] event and ~c[symb] is the symbol returned by ~c[(intern str \"ACL2\")], then ~c[symb] is a runic designator and denotes the singleton set containing ~c[(:rewrite symb)], which is the name of the rule stating the conditions under which the ~ilc[symbol-package-name] of ~c[(intern x str)] is ~c[str]. o If ~c[symb] is the name of a ~ilc[deftheory] event, then ~c[symb] is a runic designator and denotes the runic theory corresponding to ~c[symb]. o Finally, suppose that ~c[symb] is a symbol and ~c[symb'] is the macro-aliases dereference of ~c[symb]. Then ~c[(:KWD symb . rest)] is a runic designator if ~c[(:KWD' symb' . rest)] is a ~il[rune], where ~c[:KWD] is one of ~c[:d], ~c[:e], ~c[:i], or ~c[:t], and correspondingly ~c[:KWD'] is ~c[:definition], ~c[:executable-counterpart], ~c[:induction], or ~c[:type-prescription], respectively. In this case, ~c[(:KWD symb . rest)] denotes the runic theory corresponding to the rune ~c[(:KWD' symb' . rest)]. ~eq[]Note that including a function name, e.g., ~ilc[len], in the current theory ~il[enable]s that function but does not ~il[enable] the executable counterpart. Similarly, including ~c[(len)] or ~c[(:e len)] ~il[enable]s the executable counterpart but not the symbolic definition. And including the name of a proved lemma ~il[enable]s all of the rules added by the event. Of course, one can include explicitly the ~il[rune]s naming the rules in question and so can avoid entirely the use of non-runic elements in theories. Because a ~il[rune] is a runic designator denoting the set containing that ~il[rune], a list of ~il[rune]s is a theory and denotes itself. We call such theories ``runic theories.'' To every theory there corresponds a runic theory obtained by unioning together the sets denoted by each designator in the theory. When a theory is selected as ``current'' it is actually its runic correspondent that is effectively used. That is, a ~il[rune] is ~il[enable]d iff it is a member of the runic correspondent of the current theory. The value of a theory defined with ~ilc[deftheory] is the runic correspondent of the theory computed by the defining theory expression. The theory manipulation functions, e.g., ~ilc[union-theories], actually convert their theory arguments to their runic correspondents before performing the required set operation. The manipulation functions always return runic theories. Thus, it is sometimes convenient to think of (non-runic) theories as merely abbreviations for their runic correspondents, abbreviations which are ``expanded'' at the first opportunity by theory manipulation functions and the ``theory consumer'' functions such as ~ilc[in-theory] and ~ilc[deftheory].~/") #+acl2-loop-only (defmacro deftheory (&whole event-form name expr &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events define a theory (to ~il[enable] or ~il[disable] a set of rules)~/ ~bv[] Example: (deftheory interp-theory (set-difference-theories (universal-theory :here) (universal-theory 'interp-section)))~/ General Form: (deftheory name term :doc doc-string) ~ev[] where ~c[name] is a new symbolic name (~pl[name]), ~c[term] is a term that when evaluated will produce a theory (~pl[theories]), and ~ilc[doc-string] is an optional ~il[documentation] string (~pl[doc-string]). Except for the variable ~ilc[world], ~c[term] must contain no free variables. ~c[Term] is evaluated with ~ilc[world] bound to the current world (~pl[world]) and the resulting theory is then converted to a ~em[runic theory] (~pl[theories]) and associated with ~c[name]. Henceforth, this runic theory is returned as the value of the theory expression ~c[(theory name)]. The value returned is the length of the resulting theory. For example, in the following, the theory associated with ~c['FOO] has 54 ~il[rune]s: ~bv[] ACL2 !>(deftheory foo (union-theories '(binary-append) (theory 'minimal-theory))) Summary Form: ( DEFTHEORY FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) 54 ACL2 !> ~ev[] Note that the theory being defined depends on the context. For example, consider the following (contrived) example book. ~bv[] (in-package \"ACL2\") (defund foo (x) (consp x)) ; defund disables foo (local (in-theory (enable foo))) (deftheory my-theory (current-theory :here)) (in-theory (disable foo)) (defthm foo-property (implies (consp x) (foo x)) :hints ((\"Goal\" :in-theory (enable my-theory)))) ~ev[] At the time ~c[foo-property] is proved admissible during book certification (~pl[certify-book]), the ~ilc[local] ~ilc[in-theory] event has previously been evaluated, so the ~il[definition] of ~c[foo] is ~il[enable]d. Thus, the ~c[:in-theory] hint on ~c[foo-property] will ~il[enable] ~c[foo], and the theorem proves. HOWEVER, when the book is later included (~pl[include-book]), the ~ilc[local] event is skipped, so the definition of ~c[foo] is ~il[disable]d at the time the ~il[theory] ~c[my-theory] is defined. Hence, unlike the case for the admissibility pass of the book's certification, that theory does not include the definition of ~c[foo] when the book is included. There is, however, a way to ensure that a ~il[theory] defined in a book is the same at ~ilc[include-book] time as it was during the admissibility pass of the book's certification; ~pl[deftheory-static].~/ :cited-by Theories" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'deftheory-fn (list 'quote name) (list 'quote expr) 'state (list 'quote doc) (list 'quote event-form))) (defmacro deftheory-static (name theory) ":Doc-Section Events define a `static' theory (to ~il[enable] or ~il[disable] a set of rules)~/ This macro provides a variant of ~ilc[deftheory], such that the resulting theory is the same at ~ilc[include-book] time as it was at ~ilc[certify-book] time. We assume that the reader is familiar with ~il[theories]; ~pl[deftheory]. We begin here by illustrating how ~c[deftheory-static] differs from ~ilc[deftheory]. Suppose for example that the following events are the first two events in a book, where that book is certified in the initial ACL2 ~il[world] (~pl[ground-zero]). ~bv[] (deftheory my-theory (current-theory :here)) (deftheory-static my-static-theory (current-theory :here)) ~ev[] Now suppose we include that book after executing the following event. ~bv[] (in-theory (disable car-cons)) ~ev[] Suppose that later we execute ~c[(in-theory (theory 'my-theory))]. Then the rule ~c[car-cons] will be disabled, because it was disabled at the time the expression ~c[(current-theory :here)] was evaluated when processing the ~c[deftheory] of ~c[my-theory] while including the book. However, if we execute ~c[(in-theory (theory 'my-static-theory))], then the rule ~c[car-cons] will be enabled, because the value of the theory ~c[my-static-theory] was saved at the time the book was certified.~/ ~bv[] General Form: (deftheory-static name term :doc doc-string) ~ev[] The arguments are handled the same as for ~ilc[deftheory]. Thus, ~c[name] is a new symbolic name (~pl[name]), ~c[term] is a term that when evaluated will produce a theory (~pl[theories]), and ~ilc[doc-string] is an optional ~il[documentation] string (~pl[doc-string]). Except for the variable ~ilc[world], ~c[term] must contain no free variables. ~c[Term] is evaluated with ~ilc[world] bound to the current world (~pl[world]) and the resulting theory is then converted to a ~em[runic theory] (~pl[theories]) and associated with ~c[name]. Henceforth, this runic theory is returned as the value of the theory expression ~c[(theory name)]. As for ~ilc[deftheory], the value returned is the length of the resulting theory. We conclude with an optional discussion about the implementation of ~c[deftheory-static], for those familiar with ~ilc[make-event]. The following macroexpansion of the ~c[deftheory-static] form above shows how this works (~pl[trans1]). ~bv[] ACL2 !>:trans1 (deftheory-static my-static-theory (current-theory :here)) (MAKE-EVENT (LET ((WORLD (W STATE))) (LIST 'DEFTHEORY 'MY-STATIC-THEORY (LIST 'QUOTE (CURRENT-THEORY :HERE))))) ACL2 !> ~ev[] The idea is that upon evaluation of this ~c[make-event] form, the first step is to evaluate the indicated ~ilc[LET] expression to obtain a form ~c[(deftheory my-theory '(...))], where ``~c[(...)]'' is a list of all ~il[rune]s in current theory. If this form is in a book being certified, then the resulting ~c[deftheory] form is stored in the book's certificate, and is used when the book is included later.~/ :cited-by Theories" `(make-event (let ((world (w state))) (list 'deftheory ',name (list 'quote ,theory))))) #+acl2-loop-only (defmacro defstobj (&whole event-form name &rest args) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations (other than those ; that are always skipped), remove it from the list of exceptions in ; install-event just below its "Comment on irrelevance of skip-proofs". ":Doc-Section Events define a new single-threaded object ~/ Note: Novices are advised to avoid ~c[defstobj], perhaps instead using community books ~c[books/cutil/defaggregate.lisp] or ~c[books/data-structures/structures.lisp]. At the least, consider using ~c[(]~ilc[set-verify-guards-eagerness]~c[ 0)] to avoid ~il[guard] verification. On the other hand, after you learn to use ~c[defstobj], ~pl[defabsstobj] for another way to introduce single-threaded objects. ~bv[] Example: (defconst *mem-size* 10) ; for use of *mem-size* just below (defstobj st (reg :type (array (unsigned-byte 31) (8)) :initially 0) (p-c :type (unsigned-byte 31) :initially 555) halt ; = (halt :type t :initially nil) (mem :type (array (unsigned-byte 31) (*mem-size*)) :initially 0 :resizable t)) General Form: (defstobj name (field1 :type type1 :initially val1 :resizable b1) ... (fieldk :type typek :initially valk :resizable bk) :renaming alist :doc doc-string :inline flg :congruent-to old-stobj-name) ~ev[] where ~c[name] is a new symbol, each ~c[fieldi] is a symbol, each ~c[typei] is either a type-indicator (a ~ilc[type-spec] or ~il[stobj] name) or of the form ~c[(ARRAY type-indicator max)], each ~c[vali] is an object satisfying ~c[typei], and each ~c[bi] is ~c[t] or ~c[nil]. Each pair ~c[:initially vali] and ~c[:resizable bi] may be omitted; more on this below. The ~c[:renaming alist] argument is optional and allows the user to override the default function names introduced by this event. The ~ilc[doc-string] is also optional. The ~c[:inline flg] Boolean argument is also optional and declares to ACL2 that the generated access and update functions for the stobj should be implemented as macros under the hood (which has the effect of inlining the function calls). The optional ~c[:congruent-to old-stobj-name] argument specifies an existing stobj with exactly the same structure, and is discussed below. We describe further restrictions on the ~c[fieldi], ~c[typei], ~c[vali], and on ~c[alist] below. We recommend that you read about single-threaded objects (stobjs) in ACL2 before proceeding; ~pl[stobj]. The effect of this event is to introduce a new single-threaded object (i.e., a ``~il[stobj]''), named ~c[name], and the associated recognizers, creator, accessors, updaters, constants, and, for fields of ~c[ARRAY] type, length and resize functions.~/ ~em[The Single-Threaded Object Introduced] The ~c[defstobj] event effectively introduces a new global variable, named ~c[name], which has as its initial logical value a list of ~c[k] elements, where ~c[k] is the number of ``field descriptors'' provided. The elements are listed in the same order in which the field descriptors appear. If the ~c[:type] of a field is ~c[(ARRAY type-indicator (max))] then ~c[max] is a non-negative integer or a symbol introduced by ~ilc[defconst]) whose value is a non-negative integer, and the corresponding element of the stobj is initially of length specified by ~c[max]. Whether the value ~c[:type] is of the form ~c[(ARRAY type-indicator (max))] or, otherwise, just ~c[type-indicator], then ~c[type-indicator] is typically a type-spec; ~pl[type-spec]. However, ~c[type-indicator] can also be the name of a stobj that was previously introduced (by ~c[defstobj] or ~ilc[defabsstobj]). We ignore this ``nested stobj'' case below; ~pl[nested-stobjs] for a discussion of stobjs within stobjs. The keyword value ~c[:initially val] specifies the initial value of a field, except for the case of a ~c[:type] ~c[(ARRAY type-indicator (max))], in which case ~c[val] is the initial value of the corresponding array. Note that the actual representation of the stobj in the underlying Lisp may be quite different; ~pl[stobj-example-2]. For the moment we focus entirely on the logical aspects of the object. In addition, the ~c[defstobj] event introduces functions for recognizing and creating the stobj and for recognizing, accessing, and updating its fields. For fields of ~c[ARRAY] type, length and resize functions are also introduced. Constants are introduced that correspond to the accessor functions. ~em[Restrictions on the Field Descriptions in Defstobj] Each field descriptor is of the form: ~bv[] (fieldi :TYPE typei :INITIALLY vali) ~ev[] Note that the type and initial value are given in ``keyword argument'' format and may be given in either order. The ~c[typei] and ~c[vali] ``arguments'' are not evaluated. If omitted, the type defaults to ~c[t] (unrestricted) and the initial value defaults to ~c[nil]. Each ~c[typei] must be either a ~ilc[type-spec] or else a list of the form ~c[(ARRAY type-spec (max))]. (Again, we are ignoring the case of nested stobjs, discussed elsewhere; ~pl[nested-stobjs].) The latter forms are said to be ``array types.'' Examples of legal ~c[typei] are: ~bv[] (INTEGER 0 31) (SIGNED-BYTE 31) (ARRAY (SIGNED-BYTE 31) (16)) (ARRAY (SIGNED-BYTE 31) (*c*)) ; where *c* has a non-negative integer value ~ev[] The ~c[typei] describes the objects which are expected to occupy the given field. Those objects in ~c[fieldi] should satisfy ~c[typei]. We are more precise below about what we mean by ``expected.'' We first present the restrictions on ~c[typei] and ~c[vali]. Non-Array Types When ~c[typei] is a ~ilc[type-spec] it restricts the contents, ~c[x], of ~c[fieldi] according to the ``meaning'' formula given in the table for ~ilc[type-spec]. For example, the first ~c[typei] above restricts the field to be an integer between 0 and 31, inclusive. The second restricts the field to be an integer between -2^30 and (2^30)-1, inclusive. The initial value, ~c[vali], of a field description may be any ACL2 object but must satisfy ~c[typei]. Note that ~c[vali] is not a form to be evaluated but an object. A form that evaluates to ~c[vali] could be written ~c['vali], but ~c[defstobj] does not expect you to write the quote mark. For example, the field description ~bv[] (days-off :initially (saturday sunday)) ~ev[] describes a field named ~c[days-off] whose initial value is the list consisting of the two symbols ~c[SATURDAY] and ~c[SUNDAY]. In particular, the initial value is NOT obtained by applying the function ~c[saturday] to the variable ~c[sunday]! Had we written ~bv[] (days-off :initially '(saturday sunday)) ~ev[] it would be equivalent to writing ~bv[] (days-off :initially (quote (saturday sunday))) ~ev[] which would initialize the field to a list of length two, whose first element is the symbol ~c[quote] and whose second element is a list containing the symbols ~c[saturday] and ~c[sunday]. Array Types When ~c[typei] is of the form ~c[(ARRAY type-spec (max))], the field is supposed to be a list of items, initially of length specified by ~c[max], each of which satisfies the indicated ~c[type-spec]. ~c[Max] must be a non-negative integer or a defined constant evaluating to a non-negative integer. Thus, each of ~bv[] (ARRAY (SIGNED-BYTE 31) (16)) (ARRAY (SIGNED-BYTE 31) (*c*)) ; given previous event (defconst *c* 16) ~ev[] restricts the field to be a list of integers, initially of length 16, where each integer in the list is a ~c[(SIGNED-BYTE 31)]. We sometimes call such a list an ``array'' (because it is represented as an array in the underlying Common Lisp). The elements of an array field are indexed by position, starting at 0. Thus, the maximum legal index of an array field one less than is specified by ~c[max]. Note that the value of ~c[max] must be less than the Common Lisp constant ~c[array-dimension-limit], and also (though this presumably follows) less than the Common Lisp constant ~c[array-total-size-limit]. Note also that the ~c[ARRAY] type requires that the ~c[max] be enclosed in parentheses. This makes ACL2's notation consistent with the Common Lisp convention of describing the (multi-)dimensionality of arrays. But ACL2 currently supports only single dimensional arrays in stobjs. For array fields, the initial value ~c[vali] must be an object satisfying the ~ilc[type-spec] of the ~c[ARRAY] description. The initial value of the field is a list of ~c[max] repetitions of ~c[vali]. Array fields can be ``resized,'' that is, their lengths can be changed, if ~c[:resizable t] is supplied as shown in the example and General Form above. The new length must satisfy the same restriction as does ~c[max], as described above. Each array field in a ~c[defstobj] event gives rise to a length function, which gives the length of the field, and a resize function, which modifies the length of the field if ~c[:resizable t] was supplied with the field when the ~c[defstobj] was introduced and otherwise causes an error. If ~c[:resizable t] was supplied and the resize function specifies a new length ~c[k], then: if ~c[k] is less than the existing array length, the array is shortened simply by dropping elements with index at least ~c[k]; otherwise, the array is extended to length ~c[k] by mapping the new indices to the initial value (supplied by ~c[:initially], else default ~c[nil]). Array resizing is relatively slow, so we recommend using it somewhat sparingly. ~em[The Default Function Names] To recap, in ~bv[] (defstobj name (field1 :type type1 :initially val1) ... (fieldk :type typek :initially valk) :renaming alist :doc doc-string :inline inline-flag) ~ev[] ~c[name] must be a new symbol, each ~c[fieldi] must be a symbol, each ~c[typei] must be a ~ilc[type-spec] or ~c[(ARRAY type-spec (max))], and each ~c[vali] must be an object satisfying ~c[typei]. Roughly speaking, for each ~c[fieldi], a ~c[defstobj] introduces a recognizer function, an accessor function, and an updater function. The accessor function, for example, takes the stobj and returns the indicated component; the updater takes a new component value and the stobj and return a new stobj with the component replaced by the new value. But that summary is inaccurate for array fields. The accessor function for an array field does not take the stobj and return the indicated component array, which is a list of length specified by ~c[max]. Instead, it takes an additional index argument and returns the indicated element of the array component. Similarly, the updater function for an array field takes an index, a new value, and the stobj, and returns a new stobj with the indicated element replaced by the new value. These functions ~-[] the recognizer, accessor, and updater, and also length and resize functions in the case of array fields ~-[] have ``default names.'' The default names depend on the field name, ~c[fieldi], and on whether the field is an array field or not. For clarity, suppose ~c[fieldi] is named ~c[c]. The default names are shown below in calls, which also indicate the arities of the functions. In the expressions, we use ~c[x] as the object to be recognized by field recognizers, ~c[i] as an array index, ~c[v] as the ``new value'' to be installed by an updater, and ~c[name] as the single-threaded object. ~bv[] non-array field array field recognizer (cP x) (cP x) accessor (c name) (cI i name) updater (UPDATE-c v name) (UPDATE-cI i v name) length (c-LENGTH name) resize (RESIZE-c k name) ~ev[] Finally, a recognizer and a creator for the entire single-threaded object are introduced. The creator returns the initial stobj, but may only be used in limited contexts; ~pl[with-local-stobj]. If the single-threaded object is named ~c[name], then the default names and arities are as shown below. ~bv[] top recognizer (nameP x) creator (CREATE-name) ~ev[] For example, the event ~bv[] (DEFSTOBJ $S (X :TYPE INTEGER :INITIALLY 0) (A :TYPE (ARRAY (INTEGER 0 9) (3)) :INITIALLY 9)) ~ev[] introduces a stobj named ~c[$S]. The stobj has two fields, ~c[X] and ~c[A]. The ~c[A] field is an array. The ~c[X] field contains an integer and is initially 0. The ~c[A] field contains a list of integers, each between 0 and 9, inclusively. Initially, each of the three elements of the ~c[A] field is 9. This event introduces the following sequence of definitions: ~bv[] (DEFUN XP (X) ...) ; recognizer for X field (DEFUN AP (X) ...) ; recognizer of A field (DEFUN $SP ($S) ...) ; top-level recognizer for stobj $S (DEFUN CREATE-$S () ...) ; creator for stobj $S (DEFUN X ($S) ...) ; accessor for X field (DEFUN UPDATE-X (V $S) ...) ; updater for X field (DEFUN A-LENGTH ($S) ...) ; length of A field (DEFUN RESIZE-A (K $S) ...) ; resizer for A field (DEFUN AI (I $S) ...) ; accessor for A field at index I (DEFUN UPDATE-AI (I V $S) ...) ; updater for A field at index I ~ev[] ~em[Avoiding the Default Function Names] If you do not like the default names listed above you may use the optional ~c[:renaming] alist to substitute names of your own choosing. Each element of ~c[alist] should be of the form ~c[(fn1 fn2)], where ~c[fn1] is a default name and ~c[fn2] is your choice for that name. For example ~bv[] (DEFSTOBJ $S (X :TYPE INTEGER :INITIALLY 0) (A :TYPE (ARRAY (INTEGER 0 9) (3)) :INITIALLY 9) :renaming ((X XACCESSOR) (CREATE-$S MAKE$S))) ~ev[] introduces the following definitions ~bv[] (DEFUN XP (X) ...) ; recognizer for X field (DEFUN AP (X) ...) ; recognizer of A field (DEFUN $SP ($S) ...) ; top-level recognizer for stobj $S (DEFUN MAKE$S () ...) ; creator for stobj $S (DEFUN XACCESSOR ($S) ...) ; accessor for X field (DEFUN UPDATE-X (V $S) ...) ; updater for X field (DEFUN A-LENGTH ($S) ...) ; length of A field (DEFUN RESIZE-A (K $S) ...) ; resizer for A field (DEFUN AI (I $S) ...) ; accessor for A field at index I (DEFUN UPDATE-AI (I V $S) ...) ; updater for A field at index I ~ev[] Note that even though the renaming alist substitutes ``~c[XACCESSOR]'' for ``~c[X]'' the updater for the ~c[X] field is still called ``~c[UPDATE-X].'' That is because the renaming is applied to the default function names, not to the field descriptors in the event. Use of the ~c[:renaming] alist may be necessary to avoid name clashes between the default names and and pre-existing function symbols. ~em[Constants] ~c[Defstobj] events also introduce constant definitions (~pl[defconst]). One constant is introduced for each accessor function by prefixing and suffixing a `~c[*]' character on the function name. The value of that constant is the position of the field being accessed. For example, if the accessor functions are ~c[a], ~c[b], and ~c[c], in that order, then the following constant definitions are introduced. ~bv[] (defconst *a* 0) (defconst *b* 1) (defconst *c* 2) ~ev[] These constants are used for certain calls of ~ilc[nth] and ~ilc[update-nth] that are displayed to the user in proof output. For example, for stobj ~c[st] with accessor functions ~c[a], ~c[b], and ~c[c], in that order, the term ~c[(nth '2 st)] would be printed during a proof as ~c[(nth *c* st)]. Also ~pl[term], in particular the discussion there of untranslated terms, and ~pl[nth-aliases-table]. ~em[Inspecting the Effects of a Defstobj] Because the stobj functions are introduced as ``sub-events'' of the ~c[defstobj] the history commands ~c[:]~ilc[pe] and ~c[:]~ilc[pc] will not print the definitions of these functions but will print the superior ~c[defstobj] event. To see the definitions of these functions use the history command ~c[:]~ilc[pcb!]. To see an s-expression containing the definitions what constitute the raw Lisp implementation of the event, evaluate the form ~bv[] (nth 4 (global-val 'cltl-command (w state))) ~ev[] ~em[immediately after] the ~c[defstobj] event has been processed. A ~c[defstobj] is considered redundant only if the name, field descriptors, renaming alist, and inline flag are identical to a previously executed ~c[defstobj]. Note that a redundant ~c[defstobj] does not reset the ~il[stobj] fields to their initial values. ~em[Inlining and Performance] The ~c[:inline] keyword argument controls whether or not accessor, updater, and length functions are inlined (as macros under the hood, in raw Lisp). If ~c[:inline t] is provided then these are inlined; otherwise they are not. The advantage of inlining is potentially better performance; there have been contrived examples, doing essentially nothing except accessing and updating array fields, where inlining reduced the time by a factor of 10 or more; and inlining has sped up realistic examples by a factor of at least 2. Inlining may get within a factor of 2 of C execution times for such contrived examples, and within a few percent of C execution times on realistic examples. A drawback to inlining is that redefinition may not work as expected, much as redefinition may not work as expected for macros: defined functions that call a macro, or inlined stobj function, will not see a subsequent redefinition of the macro or inlined function. Another drawback to inlining is that because inlined functions are implemented as macros in raw Lisp, tracing (~pl[trace$]) will not show their calls. These drawbacks are avoided by default, but the user who is not concerned about them is advised to specify ~c[:inline t]. ~em[Specifying Congruent Stobjs] Two stobjs are may be considered to be ``congruent'' if they have the same structure, that is, their ~c[defstobj] events are identical when ignoring field names. In particular, every stobj is congruent to itself. In order to tell ACL2 that a new stobj ~c[st2] is indeed to be considered as congruent to an existing stobj ~c[st1], the ~c[defstobj] event introducing ~c[st2] is given the keyword argument ~c[:congruent-to st1]. Congruence is an equivalence relation: when you specify a new stobj to be congruent to an old one, you are also specifying that the new stobj is congruent to all other stobjs that are congruent to the old one. Thus, continuing the example above, if you specify that ~c[st3] is ~c[:congruent-to st2], then ~c[st1], ~c[st2], and ~c[st3] will all be congruent to each other. When two stobjs are congruent, ACL2 allows you to substitute one for another in a function call. Any number of stobjs may be replaced with congruent stobjs in the call, provided no two get replaced with the same stobj. The return values are correspondingly modified: if stobj ~c[st1] is replaced by ~c[st2] at an argument position, and if ~c[st1] is returned in the output ~il[signature] of the function, then ~c[st2] is returned in place of ~c[st1]. The following example illustrates congruent stobjs. For more examples of how to take advantage of congruent stobjs, and also of how to misuse them, see community book ~c[books/misc/congruent-stobjs-test.lisp]. ~bv[] (defstobj st1 fld1) (defstobj st2 fld2 :congruent-to st1) (defstobj st3 fld3 :congruent-to st2) ; equivalently, :congruent-to st1 (defun f (st1 st2 st3) (declare (xargs :stobjs (st1 st2 st3))) (list (fld2 st1) (fld3 st2) (fld1 st3))) (update-fld1 1 st1) (update-fld1 2 st2) ; notice use of update-fld1 on st2 (update-fld1 3 st3) ; notice use of update-fld1 on st3 (assert-event (equal (f st3 st2 st1) '(3 2 1))) ~ev[] The following example shows an error that occurs when stobj arguments are repeated, i.e., at least two stobj arguments (in this case, three) get replaced by the same stobj. ~bv[] ACL2 !>(f st1 st1 st1) ACL2 Error in TOP-LEVEL: The form ST1 is being used, as an argument to a call of F, where the single-threaded object ST2 was expected, even though these are congruent stobjs. See :DOC defstobj, in particular the discussion of congruent stobjs. Note: this error occurred in the context (F ST1 ST1 ST1). ACL2 !> ~ev[]~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defstobj-fn (list 'quote name) (list 'quote args) 'state (list 'quote event-form))) #+acl2-loop-only (defmacro in-theory (&whole event-form expr &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events designate ``current'' theory (enabling its rules)~/ ~bv[] Example: (in-theory (set-difference-theories (universal-theory :here) '(flatten (:executable-counterpart flatten))))~/ General Form: (in-theory term :doc doc-string) ~ev[] where ~c[term] is a term that when evaluated will produce a theory (~pl[theories]), and ~ilc[doc-string] is an optional ~il[documentation] string not beginning with ``~c[:Doc-Section] ...''. Because no unique name is associated with an ~c[in-theory] event, there is no way we can store the ~il[documentation] string ~ilc[doc-string] in our ~il[documentation] database. Hence, we actually prohibit ~ilc[doc-string] from having the form of an ACL2 ~il[documentation] string; ~pl[doc-string]. Except for the variable ~ilc[world], ~c[term] must contain no free variables. ~c[Term] is evaluated with the variable ~ilc[world] bound to the current ~il[world] to obtain a theory and the corresponding runic theory (~pl[theories]) is then made the current theory. Thus, immediately after the ~c[in-theory], a rule is ~il[enable]d iff its rule name is a member of the runic interpretation (~pl[theories]) of some member of the value of ~c[term]. ~l[theory-functions] for a list of the commonly used theory manipulation functions. Note that it is often useful to surround ~c[in-theory] ~il[events] with ~c[local], that is, to use ~c[(local (in-theory ...))]. This use of ~ilc[local] in ~ilc[encapsulate] events and ~il[books] will prevent the effect of this theory change from being exported outside the context of that ~c[encapsulate] or book. Also ~pl[hints] for a discussion of the ~c[:in-theory] hint, including some explanation of the important point that an ~c[:in-theory] hint will always be evaluated relative to the current ACL2 logical ~il[world], not relative to the theory of a previous goal. ~ilc[In-theory] returns an error triple (~pl[error-triples]). When the return is without error, the value is of the form ~c[(mv nil (:NUMBER-OF-ENABLED-RUNES k) state)] where ~c[k] is the length of the new current theory. This value of ~c[k] is what is printed when an ~c[in-theory] event evaluates without error at the top level.~/ :cited-by Theories" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'in-theory-fn (list 'quote expr) 'state (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defmacro in-arithmetic-theory (&whole event-form expr &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events designate theory for some rewriting done for non-linear arithmetic~/ We assume familiarity with ~il[theories]; in particular, ~pl[in-theory] for the normal way to set the current theory. Here, we discuss an analogous event that pertains only to non-linear arithmetic (~pl[non-linear-arithmetic]). ~bv[] Example: (in-arithmetic-theory '(lemma1 lemma2))~/ General Form: (in-arithmetic-theory term :doc doc-string) ~ev[] where ~c[term] is a term that when evaluated will produce a theory (~pl[theories]), and ~ilc[doc-string] is an optional ~il[documentation] string not beginning with ``~c[:Doc-Section] ...''. Except for the variable ~ilc[world], ~c[term] must contain no free variables. ~c[Term] is evaluated with the variable ~ilc[world] bound to the current ~il[world] to obtain a theory and the corresponding runic theory (~pl[theories]) is then used by non-linear arithmetic (~pl[non-linear-arithmetic]). Warning: If ~c[term] involves macros such as ~ilc[ENABLE] and ~ilc[DISABLE] you will probably not get what you expect! Those macros are defined relative to the ~ilc[CURRENT-THEORY]. But in this context you might wish they were defined in terms of the ``~c[CURRENT-ARITHMETIC-THEORY]'' which is not actually a defined function. We do not anticipate that users will repeatedly modify the arithmetic theory. We expect ~c[term] most often to be a constant list of runes and so have not provided ``arithmetic theory manipulation functions'' analogous to ~ilc[CURRENT-THEORY] and ~ilc[ENABLE]. Because no unique name is associated with an ~c[in-arithmetic-theory] event, there is no way we can store the ~il[documentation] string ~ilc[doc-string] in our il[documentation] database. Hence, we actually prohibit ~ilc[doc-string] from having the form of an ACL2 ~il[documentation] string; ~pl[doc-string]. ~l[non-linear-arithmetic].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'in-arithmetic-theory-fn (list 'quote expr) 'state (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defmacro regenerate-tau-database (&whole event-form &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events regenerate the tau database relative to the current enabled theory~/ ~bv[] Example: (regenerate-tau-database)~/ General Form: (regenerate-tau-database :doc doc-string) ~ev[] where ~ilc[doc-string] is an optional ~il[documentation] string not beginning with ``~c[:Doc-Section] ...''. Because no unique name is associated with a ~c[regenerate-tau-database] event, there is no way we can store the ~il[documentation] string ~ilc[doc-string] in our il[documentation] database. Hence, we actually prohibit ~ilc[doc-string] from having the form of an ACL2 ~il[documentation] string; ~pl[doc-string]. The tau database is regenerated by scanning the current logical world and re-processing every rule-generating event in it relative to the current enabled theory and current tau auto mode settings. ~l[introduction-to-the-tau-system] for background details. This command was intended to allow the user to remove a fact from the tau database, by regenerating the database without the fact. But as the following discussion highlights, ~c[regenerate-tau-database] does not really solve the problem. We regard it as a placeholder for a more sophisticated mechanism. However, we have trouble understanding why a user might wish to remove a fact from the database and are awaiting further user experiences before designing the more sophisticated mechanism. Suppose, for example, that you wanted to remove a signature rule provided by some rule with name ~i[rune]. You could disable ~i[rune] and regenerate the database. We discuss why you might ~-[] or might not ~-[] want to do this later. But suppose you did it. Unfortunately, the database you get will not be just like the one you started with minus the signature rule. The reason is that the database you started with was generated incrementally and the current theory might have evolved. To take a simple example, your starting database might have included a rule that has been disabled since it was first added. Thus, the part of your starting database built before the disabling was constructed with the rule enabled and the part built afterwards has the rule disabled. You are unlikely to get the same database whether you enable or disable that rule now. You might hope that the avoidance of ~c[in-theory] events would eliminate the problem but it does not because even the ~ilc[ground-zero] theory is constructed incrementally from the ``pre-history'' commands used to boot up ACL2. Those pre-history commands include some global ~c[in-theory] commands. Since every session starts from the ~c[ground-zero] state, the starting database is already ``infected'' with global ~c[in-theory] commands. The reason we hope that it will not be necessary to remove tau facts is that the tau system is designed merely to be fast and benign (see ~i[Design Philosophy] in ~il[introduction-to-the-tau-system]). The tau system's coverage should grows monotonically with the addition of rules. According to this understanding of tau, adding a signature rule, for example, may allow tau to prove some additional goals but will not prevent it from proving goals it could prove previously. If this is understanding of tau is accurate, we see no fundamental reason to support the removal of a fact. This, of course, ignores the possibility that the user wishes to explore alternative proof strategies or measure performance. We welcome user observations and experience on this issue.~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'regenerate-tau-database-fn 'state (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defmacro push-untouchable (&whole event-form name fn-p &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section switches-parameters-and-modes add name or list of names to the list of untouchable symbols~/ ~bv[] Examples: (push-untouchable my-var nil) (push-untouchable set-mem t)~/ General Form: (push-untouchable name{s} fn-p :doc doc-string) ~ev[] where ~c[name{s}] is a non-~c[nil] symbol or a non-~c[nil] true list of symbols, ~c[fn-p] is any value (but generally ~c[nil] or ~c[t]), and ~ilc[doc-string] is an optional ~il[documentation] string not beginning with ``~c[:Doc-Section] ...''. If ~c[name{s}] is a symbol it is treated as the singleton list containing that symbol. The effect of this event is to union the given symbols into the list of ``untouchable variables'' in the current world if ~c[fn-p] is ~c[nil], else to union the symbols into the list of ``untouchable functions''. This event is redundant if every symbol listed is already a member of the appropriate untouchables list (variables or functions). When a symbol is on the untouchables list it is syntactically illegal for any event to call a function or macro of that name, if ~c[fn-p] is non-~c[nil], or to change the value of a state global variable of that name, if ~c[fn-p] is ~c[nil]. Thus, the effect of pushing a function symbol, ~c[name], onto untouchables is to prevent any future event from using that symbol as a function or macro, or as a state global variable (according to ~c[fn-p]). This is generally done to ``fence off'' some primitive function symbol from ``users'' after the developer has used the symbol freely in the development of some higher level mechanism. Also ~pl[remove-untouchable].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (declare (xargs :guard (and name (or (symbolp name) (symbol-listp name)) (booleanp fn-p)))) (list 'push-untouchable-fn (list 'quote name) (list 'quote fn-p) 'state (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defmacro remove-untouchable (&whole event-form name fn-p &key doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section switches-parameters-and-modes remove names from lists of untouchable symbols~/ ~bv[] Example Forms: (remove-untouchable my-var nil) ; then state global my-var is not untouchable (remove-untouchable set-mem t) ; then function set-mem is not untouchable ~ev[] Also ~pl[push-untouchable]. This documentation topic is directed at those who build systems on top of ACL2. We first describe a means for removing restrictions related to so-called ``untouchables'': functions (or macros) that cannot be called, or state global variables that cannot be modified or unbound, without intervention that requires an active trust tag (~pl[defttag]). Then we describe the ~c[remove-untouchable] event. We begin by discussing untouchable state global variables ~c[temp-touchable-vars] and ~c[temp-touchable-fns], which initially have value ~c[nil]. These can often be used in place of ~c[remove-untouchable]. When the value is ~c[t], no variable (respectively, no function or macro) is treated as untouchable, regardless of the set of initial untouchables or the ~c[remove-untouchable] or ~ilc[push-untouchable] ~il[events] that have been admitted. Otherwise the value of each of these two variables is a ~ilc[symbol-listp], and no member of this list is treated as an untouchable variable (in the case of ~c[temp-touchable-vars]) or as an untouchable function or macro (in the case of ~c[temp-touchable-fns]). These two state global variables can be set by ~c[set-temp-touchable-vars] and ~c[set-temp-touchable-fns], respectively, provided there is an active trust tag (~pl[defttag]). Here is an illustrative example. This macro executes the indicated forms in a context where there are no untouchable variables, but requires an active trust tag when invoked. ~bv[] (defmacro with-all-touchable (&rest forms) `(progn! :state-global-bindings ((temp-touchable-vars t set-temp-touchable-vars)) (progn! ,@forms))) ~ev[] An equivalent version, which however is not recommended since ~ilc[state-global-let*] may have surprising behavior in raw Lisp, is as follows. ~bv[] (defmacro with-all-touchable (&rest forms) `(progn! (state-global-let* ((temp-touchable-vars t set-temp-touchable-vars)) (progn! ,@forms)))) ~ev[] Finally, the value ~c[t] for ~c[temp-touchable-vars] removes the requirement that built-in state globals cannot be made unbound (with ~c[makunbound-global]).~/ We now turn to the ~c[remove-untouchable] event, in case the approach above is for some reason not adequate. This event is illegal by default, since it can be used to provide access to ACL2 internal functions and data structures that are intentionally made untouchable for the user. If you want to call it, you must first create an active trust tag; ~pl[defttag]. ~bv[] General Form: (remove-untouchable name{s} fn-p :doc doc-string) ~ev[] where ~c[name{s}] is a non-~c[nil] symbol or a non-~c[nil] true list of symbols, ~c[fn-p] is any value (but generally ~c[nil] or ~c[t]), and ~ilc[doc-string] is an optional ~il[documentation] string not beginning with ``~c[:Doc-Section] ...''. If ~c[name{s}] is a symbol it is treated as the singleton list containing that symbol. The effect of this event is to remove the given symbols from the list of ``untouchable variables'' in the current world if ~c[fn-p] is ~c[nil], else to remove the symbols into the list of ``untouchable functions''. This event is redundant if no symbol listed is a member of the appropriate untouchables list (variables or functions).~/" (declare (xargs :guard (and name (or (symbolp name) (symbol-listp name)) (booleanp fn-p)))) `(cond ((not (ttag (w state))) (er soft 'remove-untouchable "It is illegal to execute remove-untouchable when there is no ~ active ttag; see :DOC defttag.")) (t ,(list 'remove-untouchable-fn (list 'quote name) (list 'quote fn-p) 'state (list 'quote doc) (list 'quote event-form))))) #+acl2-loop-only (defmacro set-body (&whole event-form fn name-or-rune) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events set the definition body~/ ~bv[] Examples: (set-body foo (:definition foo)) ; restore original definition of foo (set-body foo foo) ; same as just above (set-body foo my-foo-def) ; use my-foo-def for the body of foo (set-body foo (:definition my-foo-def)) ; same as just above ~ev[] Rules of class ~c[:]~ilc[definition] can install a new definition body, used for example by ~c[:expand] ~il[hints]. ~l[definition] and also ~pl[hints] for a detailed discussion of the ~c[:install-body] fields of ~c[:]~ilc[definition] rules and their role in ~c[:expand] hints. There may be several such definitions, but by default, the latest one is used by ~c[:expand] hints. Although the ~c[:with] keyword may be used in ~c[:expand] hints to override this behavior locally (~pl[hints]), it may be convenient to install a definition for expansion other than the latest one ~-[] for example, the original definition. ~c[Set-body] may be used for this purpose. ~bv[] General Form: (set-body function-symbol rule-name) ~ev[] where ~c[rule-name] is either a ~c[:definition] ~il[rune] or is a function symbol, ~c[sym], which represents the rune ~c[(:definition sym)]. You can view all definitions available for expansion; ~pl[show-bodies].~/~/" `(set-body-fn ',fn ',name-or-rune state ',event-form)) #+acl2-loop-only (defmacro table (&whole event-form name &rest args) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events user-managed tables~/ ~bv[] Examples: (table tests 1 '(...)) ; set contents of tests[1] to '(...) (table tests 25) ; get contents of tests[25] (table tests) ; return table tests as an alist (table tests nil nil :clear) ; clear table tests (table tests nil '((foo . 7)) :clear) ; set table tests to ((foo . 7)) (table tests nil nil :guard) ; fetch the table guard (table tests nil nil :guard term) ; set the table guard~/ General Form: (table table-name key-term value-term op term) ~ev[] where ~c[table-name] is a symbol that is the name of a (possibly new) table, ~c[key-term] and ~c[value-term], if present, are arbitrary terms involving (at most) the single variable ~ilc[world], ~c[op], if present, is one of the table operations below, and ~c[term], if present, is a term. ~c[Table] returns an ACL2 ``error triple'' (~pl[error-triples]). The effect of ~c[table] on ~ilc[state] depends on ~c[op] and how many arguments are presented. Some invocations actually have no effect on the ACL2 ~il[world] and hence an invocation of ~c[table] is not always an ``event''. We explain below, after giving some background information. ~b[Important Note:] The ~c[table] forms above are calls of a macro that expand to involve the special variable ~ilc[state]. This will prevent you from accessing a table from within a hint or theory where you do not have the ~ilc[state] variable. However, the form ~bv[] (table-alist 'tests world) ~ev[] returns the alist representation of the table named ~c[test] in the given world. Often you have access to ~c[world]. The ACL2 system provides ``tables'' by which the user can associate one object with another. Tables are in essence just conventional association lists ~-[] lists of pairs ~-[] but the ACL2 environment provides a means of storing these lists in the ``ACL2 world'' of the current ~ilc[state]. The ACL2 user could accomplish the same ends by using ACL2 ``global variables;'' however, limitations on global variable names are imposed to ensure ACL2's soundness. By convention, no table is important to ACL2's soundness, even though some features of the system use tables, and the user is invited to make free use of tables. Because tables are stored in the ACL2 ~il[world] they are restored by ~ilc[include-book] and undone by ~c[:]~ilc[ubt]. Many users of Nqthm requested a facility by which user data could be saved in Nqthm ``lib files'' and tables are ACL2's answer to that request. Abstractly, each table is an association list mapping ``keys'' to ``values.'' In addition, each table has a ``~c[:guard],'' which is a term that must be true of any key and value used. By setting the ~c[:guard] on a table you may enforce an invariant on the objects in the table, e.g., that all keys are positive integers and all values are symbols. Each table has a ``name,'' which must be a symbol. Given a table name, the following operations can be performed on the table. ~c[:put] ~-[] associate a value with a key (possibly changing the value currently associated with that key). ~c[:get] ~-[] retrieve the value associated with a key (or nil if no value has been associated with that key). ~c[:alist] ~-[] return an alist showing all keys and non-nil values in the table. ~c[:clear] ~-[] clear the table (so that every value is nil), or if val is supplied then set table to that value (which must be an alist). ~c[:guard] ~-[] fetch or set the :guard of the table. When the operations above suggest that the table or its ~c[:guard] are modified, what is actually meant is that the current ~il[state] is redefined so that in it, the affected table name has the appropriate properties. in such cases, the ~c[table] form is an event (~pl[events]). In the ~c[:put] case, if the key is already in the table and associated with the proposed value, then the ~c[table] event is redundant (~pl[redundant-events]). ~c[Table] forms are commonly typed by the user while interacting with the system. ~c[:Put] and ~c[:get] forms are especially common. Therefore, we have adopted a positional syntax that is intended to be convenient for most applications. Essentially, some operations admit a ``short form'' of invocation. ~bv[] (table name key-term value-term :put) ; long form (table name key-term value-term) ; short form ~ev[] evaluates the key- and value-terms, obtaining two objects that we call ~c[key] and ~c[value], checks that the ~c[key] and ~c[value] satisfy the ~c[:guard] on the named table and then ``modifies'' the named table so that the value associated with ~c[key] is ~c[value]. When used like this, ~c[table] is actually an event in the sense that it changes the ACL2 ~il[world]. In general, the forms evaluated to obtain the ~c[key] and ~c[value] may involve the variable ~ilc[world], which is bound to the then-current ~il[world] during the evaluation of the forms. However, in the special case that the table in question is named ~ilc[acl2-defaults-table], the ~c[key] and ~c[value] terms may not contain any variables. Essentially, the keys and values used in ~il[events] setting the ~ilc[acl2-defaults-table] must be explicitly given constants. ~l[acl2-defaults-table]. ~bv[] (table name key-term nil :get) ; long form (table name key-term) ; short form ~ev[] evaluates the key-term (see note below), obtaining an object, ~c[key], and returns the value associated with ~c[key] in the named table (or, ~c[nil] if there is no value associated with ~c[key]). When used like this, ~c[table] is not an event; the value is simply returned. ~bv[] (table name nil nil :alist) ; long form (table name) ; short form ~ev[] returns an alist representing the named table; for every key in the table with a non-~c[nil] associated value, the alist pairs the key and its value. The order in which the keys are presented is unspecified. When used like this, ~c[table] is not an event; the alist is simply returned. ~bv[] (table name nil val :clear) ~ev[] sets the named table to the alist ~c[val], making the checks that ~c[:put] makes for each key and value of ~c[val]. When used like this, ~c[table] is an event because it changes the ACL2 ~il[world]. ~bv[] (table name nil nil :guard) ~ev[] returns the translated form of the guard of the named table. ~bv[] (table name nil nil :guard term) ~ev[] Provided the named table is empty and has not yet been assigned a ~c[:guard] and ~c[term] (which is not evaluated) is a term that mentions at most the variables ~c[key], ~c[val] and ~ilc[world], this event sets the ~c[:guard] of the named table to ~c[term]. Whenever a subsequent ~c[:put] occurs, ~c[term] will be evaluated with ~c[key] bound to the key argument of the ~c[:put], ~c[val] bound to the ~c[val] argument of the ~c[:put], and ~ilc[world] bound to the then current ~il[world]. An error will be caused by the ~c[:put] if the result of the evaluation is ~c[nil]. Note that it is not allowed to change the ~c[:guard] on a table once it has been explicitly set. Before the ~c[:guard] is explicitly set, it is effectively just ~c[t]. After it is set it can be changed only by undoing the event that set it. The purpose of this restriction is to prevent the user from changing the ~c[:guards] on tables provided by other people or the system. The intuition behind the ~c[:guard] mechanism on tables is to enforce invariants on the keys and values in a table, so that the values, say, can be used without run-time checking. But if the ~c[:guard] of a table is sensitive to the ACL2 ~il[world], it may be possible to cause some value in the table to cease satisfying the ~c[:guard] without doing any operations on the table. Consider for example the ~c[:guard] ``no value in this table is the name of an event.'' As described, that is enforced each time a value is stored. Thus, ~c['bang] can be ~c[:put] in the table provided there is no event named ~c[bang]. But once it is in the table, there is nothing to prevent the user from defining ~c[bang] as a function, causing the table to contain a value that could not be ~c[:put] there anymore. Observe that not all state-sensitive ~c[:guard]s suffer this problem. The ~c[:guard] ``every value is an event name'' remains invariant, courtesy of the fact that undoing back through an event name in the table would necessarily undo the ~c[:put] of the name into the table. ~c[Table] was designed primarily for convenient top-level use. Tables are not especially efficient. Each table is represented by an alist stored on the property list of the table name. ~c[:Get] is just a ~c[getprop] and ~ilc[assoc-equal]. ~c[:Put] does a ~c[getprop] to the get the table alist, a ~c[put-assoc-equal] to record the new association, and a ~c[putprop] to store the new table alist ~-[] plus the overhead associated with ~c[:guard]s and undoable ~il[events], and checking (for redundancy) if the key is already bound to its proposed value. Note that there are never duplicate keys in the resulting ~c[alist]; in particular, when the operation ~c[:clear] is used to install new ~c[alist], duplicate keys are removed from that alist. A table name may be any symbol whatsoever. Symbols already in use as function or theorem names, for example, may be used as table names. Symbols in use only as table names may be defined with ~ilc[defun], etc. Because there are no restrictions on the user's choice of table names, table names are not included among the logical names. Thus, ~c[:pe name] will never display a table event (for a logical name other than ~c[:here]). Either ~c[:pe name] will display a ``normal'' event such as ~c[(defun name ...)] or ~c[(defthm name ...)] or else ~c[:pe name] will cause an error indicating that ~c[name] is not a logical name. This happens even if ~c[name] is in use as a table name. Similarly, we do not permit table names to have ~il[documentation] strings, since the same name might already have a ~il[documentation] string. If you want to associate a ~il[documentation] string with a table name that is being used no other way, define the name as a label and use the ~c[:]~ilc[doc] feature of ~ilc[deflabel] (~pl[deflabel]); also ~pl[defdoc].~/" ; At one time the table macro expanded to several different forms, ; depending on whether it was really expected to affect world. That ; was abandoned when it was actually included in the source files ; because of the important invariant that these defmacros be ; translatable by boot-translate. ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'table-fn (list 'quote name) (list 'quote args) 'state (list 'quote event-form))) #+acl2-loop-only (defmacro encapsulate (&whole event-form signatures &rest cmd-lst) ; Warning: See the Important Boot-Strapping Invariants before modifying! ":Doc-Section Events hide some ~il[events] and/or constrain some functions~/ ~c[Encapsulate] provides a way to execute a sequence of ~il[events] and then hide some of the resulting effects. There are two kinds of encapsulations: ``trivial'' and ``non-trivial''. We discuss these briefly before providing detailed ~il[documentation]. A trivial encapsulation is an event of the following form. ~bv[] (encapsulate () ; nil here indicates \"trivial\" ... ) ~ev[] We use the term ``sub-events'' to refer to ~c[] through ~c[]. Each sub-event ~c[] may be ``~il[local]'', that is, of the form ~c[(local )]; the other sub-events are called ``non-local''. When this ~c[encapsulate] form is submitted to ACL2, it is processed in two passes. On the first pass, each sub-event is processed in sequence; admission of the ~c[encapsulate] fails if any ~c[] fails to be admitted. Then a second pass is made after rolling back the logical ~il[world] to what it was just before executing the ~c[encapsulate] form. In the second pass, only the non-~il[local] forms ~c[] are evaluated, again in order, and proofs are skipped. For example, the following trivial encapsulation exports a single event, ~c[member-equal-reverse]. The lemma ~c[member-revappend] is used (as a ~il[rewrite] rule) to prove ~c[member-equal-reverse] on the first pass, but since ~c[member-revappend] is ~il[local], it is ignored on the second (final) pass. ~bv[] (encapsulate () (local (defthm member-revappend (iff (member-equal a (revappend x y)) (or (member-equal a x) (member-equal a y))) :hints ((\"Goal\" :induct (revappend x y))))) (defthm member-equal-reverse (iff (member-equal a (reverse x)) (member-equal a x)))) ~ev[] Of course, one might prefer to prove these ~il[events] at the top level, rather than within an encapsulation; but the point here is to illustrate that you can have ~il[local] ~il[events] that do not become part of the logical ~il[world]. (Such a capability is also provided at the level of ~il[books]; in particular, ~pl[include-book].) On the other hand, non-trivial encapsulations provide a way to introduce axioms about new function symbols, without introducing inconsistency and without introducing complete definitions. The following example illustrates how that works. ~bv[] (encapsulate ; The following list has a single signature, introducing a function foo of ; one argument that returns one value. (The list is non-empty, so we call ; this a \"non-trivial\" encapsulation.) ( ((foo *) => *) ) ; Introduce a ``witness'' (example) for foo, marked as local so that ; it is not exported: (local (defun foo (x) x)) ; Introduce a non-local property to be exported: (defthm foo-preserves-consp (implies (consp x) (consp (foo x)))) ) ~ev[] The form above introduces a new function symbol, ~c[foo], with the indicated property and no definition. In fact, the output from ACL2 concludes as follows. ~bv[] The following constraint is associated with the function FOO: (IMPLIES (CONSP X) (CONSP (FOO X))) ~ev[] To understand this example, we consider how non-trivial encapsulations are processed. The same two passes are made as for trivial encapsulations, and the (~il[local]) definition of ~c[foo] is ignored on the second pass, and hence does not appear in the resulting ACL2 logical ~il[world]. But before the second pass, each ~il[signature] is stored in the ~il[world]. Thus, when the theorem ~c[foo-preserves-consp] is encountered in the second pass, ~c[foo] is a known function symbol with the indicated signature. We turn now to more complete documentation. But discussion of redundancy for ~c[encapsulate] events may be found elsewhere; ~pl[redundant-encapsulate]. ~bv[] Other Examples: (encapsulate (((an-element *) => *)) ; The list of signatures above could also be written ; ((an-element (lst) t)) (local (defun an-element (lst) (if (consp lst) (car lst) nil))) (local (defthm member-equal-car (implies (and lst (true-listp lst)) (member-equal (car lst) lst)))) (defthm thm1 (implies (null lst) (null (an-element lst)))) (defthm thm2 (implies (and (true-listp lst) (not (null lst))) (member-equal (an-element lst) lst)))) (encapsulate () ; empty signature: no constrained functions indicated (local (defthm hack (implies (and (syntaxp (quotep x)) (syntaxp (quotep y))) (equal (+ x y z) (+ (+ x y) z))))) (defthm nthcdr-add1-conditional (implies (not (zp (1+ n))) (equal (nthcdr (1+ n) x) (nthcdr n (cdr x))))))~/ General Form: (encapsulate (signature ... signature) ev1 ... evn) ~ev[] where each ~ilc[signature] is a well-formed signature, each ~c[signature] describes a different function symbol, and each ~c[evi] is an embedded event form (~l[embedded-event-form]). Also ~pl[signature], in particular for a discussion of how a signature can assign a ~il[guard] to a function symbol. There must be at least one ~c[evi]. The ~c[evi] inside ~ilc[local] special forms are called ``local'' ~il[events] below. ~il[Events] that are not ~ilc[local] are sometimes said to be ``exported'' by the encapsulation. We make the further restriction that no ~ilc[defaxiom] event may be introduced in the scope of an ~c[encapsulate] (not even by ~c[encapsulate] or ~ilc[include-book] events that are among the ~c[evi]). Furthermore, no non-~ilc[local] ~ilc[include-book] event is permitted in the scope of any ~c[encapsulate] with a non-empty list of signatures. To be well-formed, an ~c[encapsulate] event must have the properties that each event in the body (including the ~ilc[local] ones) can be successfully executed in sequence and that in the resulting theory, each function mentioned among the ~il[signature]s was introduced via a ~ilc[local] event and has the ~il[signature] listed. (A utility is provided to assist in debugging failures of such execution; ~pl[redo-flat].) In addition, the body may contain no ``local incompatibilities'' which, roughly stated, means that the ~il[events] that are not ~ilc[local] must not syntactically require symbols defined by ~ilc[local] ~ilc[events], except for the functions listed in the ~il[signature]s. ~l[local-incompatibility]. Finally, no non-~ilc[local] recursive definition in the body may involve in its suggested induction scheme any function symbol listed among the ~il[signature]s. ~l[subversive-recursions]. Observe that if the ~il[signature]s list is empty, the resulting ``trivial'' ~c[encapsulate] may still be useful for deriving theorems to be exported whose proofs require lemmas you prefer to hide (i.e., made ~ilc[local]). Whether trivial or not (i.e., whether the signature is empty or not), ~c[encapsulate] exports the results of evaluating its non-~ilc[local] ~il[events], but its ~ilc[local] ~il[events] are ignored for the resulting logical ~il[world]. The result of a non-trivial ~c[encapsulate] event is an extension of the logic in which, roughly speaking, the functions listed in the ~il[signature]s are constrained to have the ~il[signature]s listed and to satisfy the non-~ilc[local] theorems proved about them. In fact, other functions introduced in the ~c[encapsulate] event may be considered to have ``~il[constraint]s'' as well. (~l[constraint] for details, which are only relevant to functional instantiation.) Since the ~il[constraint]s were all theorems in the ``ephemeral'' or ``local'' theory, we are assured that the extension produced by ~c[encapsulate] is sound. In essence, the ~ilc[local] definitions of the constrained functions are just ``witness functions'' that establish the consistency of the ~il[constraint]s. Because those definitions are ~ilc[local], they are not present in the theory produced by encapsulation. After a non-trivial ~c[encapsulate] event is admitted, theorems about the constrained function symbols may then be proved ~-[] theorems whose proofs necessarily employ only the ~il[constraint]s. Thus, those theorems may be later functionally instantiated, as with the ~c[:functional-instance] lemma instance (~pl[lemma-instance]), to derive analogous theorems about different functions, provided the constraints (~pl[constraint]) can be proved about the new functions. The ~il[default-defun-mode] for the first event in an encapsulation is the default ~il[defun-mode] ``outside'' the encapsulation. But since ~il[events] changing the ~il[defun-mode] are permitted within the body of an ~c[encapsulate], the default ~il[defun-mode] may be changed. However, ~il[defun-mode] changes occurring within the body of the ~c[encapsulate] are not exported. In particular, the ~ilc[acl2-defaults-table] after an ~c[encapsulate] is always the same as it was before the ~c[encapsulate], even though the ~c[encapsulate] body might contain ~il[defun-mode] changing ~il[events], ~c[:]~ilc[program] and ~c[:]~ilc[logic]. ~l[defun-mode]. More generally, after execution of an ~c[encapsulate] event, the value of ~ilc[acl2-defaults-table] is restored to what it was immediately before that event was executed. ~l[acl2-defaults-table]. We make some remarks on ~il[guard]s and evaluation. Calls of functions introduced in the ~il[signature]s list cannot be evaluated in the ACL2 read-eval-print loop. ~l[defattach] for a way to overcome this limitation. Moreover, any ~c[:]~ilc[guard] supplied in the signature is automatically associated in the ~il[world] with its corresponding function symbol, with no requirement other than that the guard is a legal term all of whose function symbols are in ~c[:]~ilc[logic] mode with their ~il[guard]s verified. In particular, there need not be any relationship between a guard in a signature and the guard in a ~c[local] witness function. Finally, note that for functions introduced non-~il[local]ly inside a non-trivial ~c[encapsulate] event, ~il[guard] verification is illegal unless ACL2 determines that the proof obligations hold outside the ~ilc[encapsulate] event as well. ~bv[] (encapsulate ((f (x) t)) (local (defun f (x) (declare (xargs :guard t)) (consp x))) ;; ERROR! (defun g (x) (declare (xargs :guard (f x))) (car x))) ~ev[] The order of the ~il[events] in the vicinity of an ~c[encapsulate] is confusing. We discuss it in some detail here because when logical names are being used with theory functions to compute sets of rules, it is sometimes important to know the order in which ~il[events] were executed. (~l[logical-name] and ~pl[theory-functions].) What, for example, is the set of function names extant in the middle of an encapsulation? If the most recent event is ~c[previous] and then you execute an ~c[encapsulate] constraining ~c[an-element] with two non-~ilc[local] ~il[events] in its body, ~c[thm1] and ~c[thm2], then the order of the ~il[events] after the encapsulation is (reading chronologically forward): ~c[previous], ~c[thm1], ~c[thm2], ~c[an-element] (the ~c[encapsulate] itself). Actually, between ~c[previous] and ~c[thm1] certain extensions were made to the ~il[world] by the superior ~c[encapsulate], to permit ~c[an-element] to be used as a function symbol in ~c[thm1]. Remark for ACL2(r) (~pl[real]). For ACL2(r), ~ilc[encapsulate] can be used to introduce classical and non-classical functions, as determined by the signatures; ~pl[signature]. Those marked as classical (respectively non-classical) must have classical (respectively, non-classical) ~ilc[local] witness functions. A related requirement applies to functional instantiation; ~pl[lemma-instance].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'encapsulate-fn (list 'quote signatures) (list 'quote cmd-lst) 'state (list 'quote event-form))) (defdoc redundant-encapsulate ":Doc-Section encapsulate redundancy of ~ilc[encapsulate] ~il[events]~/ For this ~il[documentation] topic we assume familiarity with ~c[encapsulate] events and the notion of redundancy for ~il[events]; ~pl[encapsulate] and ~pl[redundant-events]. The typical way for an ~c[encapsulate] event to be redundant is when a syntactically identical ~c[encapsulate] has already been executed under the same ~ilc[default-defun-mode], ~ilc[default-ruler-extenders], and ~ilc[default-verify-guards-eagerness]. But more generally, the ~c[encapsulate] events need not be syntactically identical; for example, it suffices that they agree when the contents of ~ilc[local] sub-events are ignored. The precise criterion for redundancy is given below, but let us first look at a consequence of the point just made about ignoring the contents of ~ilc[local] sub-events. Consider the following sequence of two events. ~bv[] (encapsulate () (defun f (x) x) (local (defthm f-identity (equal (f x) x)))) (encapsulate () (defun f (x) x) (local (defthm false-claim (equal (f x) (not x))))) ~ev[] You might be surprised to learn that the second is actually redundant, even though ~c[false-claim] is clearly not a theorem; indeed, its negation is a theorem! The following two points may soften the blow. First, this behavior is as specified above (and is specified more precisely below): the contents of ~il[local] events are ignored when checking redundancy of ~ilc[encapsulate] events. Second, this behavior is sound, because the logical meaning of an ~ilc[encapsulate] event is taken from the events that it exports, which do not include events that are ~il[local] to the ~c[encapsulate] event. Some users, however, want to use ~ilc[encapsulate] events for testing in a way that is thwarted by this ignoring of ~il[local] sub-events. Consider the following sort of example. ~bv[] (encapsulate () (local (defthm test1 ...))) (encapsulate () (local (defthm test2 ...))) ~ev[] Since the contents of local events are ignored when checking redundancy of an ~c[encapsulate] event, the second form just above is indeed redundant, presumably not as expected by whomever wrote these two tests. A solution is to add distinct non-local forms, for example as follows. ~bv[] (encapsulate () (value-triple \"test1\") (local (defthm test1 ...))) (encapsulate () (value-triple \"test2\") (local (defthm test2 ...))) ~ev[] A different solution is to use ~ilc[make-event] for testing, as follows. ~bv[] (make-event (er-progn (defthm test1 ...) (value '(value-triple nil)))) (make-event (er-progn (defthm test2 ...) (value '(value-triple nil)))) ~ev[] Also see community books ~c[misc/eval.lisp], ~c[make-event/eval-check.lisp], and ~c[make-event/eval-tests.lisp] for more ways to test in books. The precise criterion for redundancy of ~ilc[encapsulate] ~il[events] is that the existing and proposed ~c[encapsulate] events contain the same signatures and the same number of top-level events ~-[] let ~c[k] be that number ~-[] and for each ~c[i < k], the ~c[i]th top-level events ~c[E1] and ~c[E2] from the earlier and current ~c[encapsulate]s have one of the following properties. o ~c[E1] and ~c[E2] are equal; or o ~c[E1] is of the form ~c[(record-expansion E2 ...)]; or else o ~c[E1] and ~c[E2] are equal after replacing each ~ilc[local] sub-event by ~c[(local (value-triple :elided))], where a sub-event of an event ~c[E] is either ~c[E] itself, or a sub-event of a constituent event of ~c[E] in the case that ~c[E] is a call of ~ilc[with-output], ~ilc[with-prover-time-limit], ~ilc[with-prover-step-limit], ~c[record-expansion], ~ilc[time$], ~ilc[progn], ~ilc[progn!], or ~c[encapsulate] itself.~/~/") (defconst *load-compiled-file-values* '(t nil :warn :default :comp)) #+acl2-loop-only (defmacro include-book (&whole event-form user-book-name &key ; Warning: If you change the defaults below, be sure to change the ; construction of event-form in include-book-fn! (load-compiled-file ':default) (uncertified-okp 't) (defaxioms-okp 't) (skip-proofs-okp 't) (ttags ':default) dir doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! (declare (xargs :guard (member-eq load-compiled-file *load-compiled-file-values*))) ":Doc-Section Events load the ~il[events] in a file~/ ~bv[] Examples: (include-book \"my-arith\") (include-book \"/home/smith/my-arith\") (include-book \"/../../my-arith\") General Form: (include-book file :load-compiled-file action :uncertified-okp t/nil ; [default t] :defaxioms-okp t/nil ; [default t] :skip-proofs-okp t/nil ; [default t] :ttags ttags ; [default nil] :dir directory :doc doc-string) ~ev[] where ~c[file] is a book name. ~l[books] for general information, ~pl[book-name] for information about book names, and ~pl[pathname] for information about file names. ~c[Action] is one of ~c[t], ~c[nil], ~c[:default], ~c[:warn], or ~c[:comp]; these values are explained below, and the default is ~c[:default]. The three ~c[-okp] keyword arguments, which default to ~c[t], determine whether errors or warnings are generated under certain conditions explained below; when the argument is ~c[t], warnings are generated. The ~c[dir] argument, if supplied, is a keyword that represents an absolute pathname for a directory (~pl[pathname]), to be used instead of the current book directory (~pl[cbd]) for resolving the given ~c[file] argument to an absolute pathname. In particular, by default ~c[:dir :system] resolves ~c[file] using the ~c[books/] directory of your ACL2 installation, unless your ACL2 executable was built somewhere other than where it currently resides; please see the ``Books Directory'' below. To define other keywords that can be used for ~c[dir], ~pl[add-include-book-dir]. ~c[Doc-string] is an optional ~il[documentation] string; ~pl[doc-string]. If the book has no ~ilc[certificate], if its ~ilc[certificate] is invalid or if the certificate was produced by a different ~il[version] of ACL2, a warning is printed and the book is included anyway; ~pl[certificate]. This can lead to serious errors, perhaps mitigated by the presence of a ~c[.port] file from an earlier certification; ~pl[uncertified-books]. If the portcullis of the ~il[certificate] (~pl[portcullis]) cannot be raised in the host logical ~il[world], an error is caused and no change occurs to the logic. Otherwise, the non-~ilc[local] ~il[events] in file are assumed. Then the ~il[keep] of the ~il[certificate] is checked to ensure that the correct files were read; ~pl[keep]. A warning is printed if uncertified ~il[books] were included. Even if no warning is printed, ~c[include-book] places a burden on you; ~pl[certificate]. If you use ~il[guard]s, please note ~c[include-book] is executed as though ~c[(set-guard-checking nil)] has been evaluated; ~Pl[set-guard-checking]. If you want guards checked, please ~pl[ld] and/or ~pl[rebuild]. The value of ~c[:load-compiled-file] controls whether a compiled file for the given ~c[file] is loaded by ~c[include-book]. Note that this keyword applies only to the given ~c[file], not to any included sub-books. In order to skip loading all compiled files, for the given ~c[file] as well as all included sub-books ~-[] for example, to avoid Lisp errors such as ``Wrong FASL version'' ~-[] use ~c[(set-compiler-enabled nil state)]; ~pl[compilation]. Otherwise, if keyword argument ~c[:load-compiled-file] is missing or its value is the keyword ~c[:default], then it is treated as ~c[:warn]. If its value is ~c[nil], no attempt is made to load the compiled file for the book provided. In order to load the compiled file, it must be more recent than the book's ~il[certificate] (except in raw mode, where it must be more recent than the book itself; ~pl[set-raw-mode]). For non-~c[nil] values of ~c[:load-compiled-file] that do not result in a loaded compiled file, ACL2 proceeds as follows. Note that a load of a compiled file or expansion file aborts partway through whenever an ~ilc[include-book] form is encountered for which no suitable compiled or expansion file can be loaded. For much more detail, ~pl[book-compiled-file]. ~bq[] ~c[t]: Cause an error if the compiled file is not loaded. This same requirement is imposed on every ~ilc[include-book] form evaluated during the course of evaluation of the present ~c[include-book] form, except that for those subsidiary ~c[include-book]s that do not themselves specify ~c[:load-compiled-file t], it suffices to load the expansion file (~pl[book-compiled-file]). ~c[:warn]: An attempt is made to load the compiled file, and a warning is printed if that load fails to run to completion. ~c[:comp]: A compiled file is loaded as with value ~c[t], except that if a suitable ``expansion file'' exists but the compiled file does not, then the compiled file is first created. ~l[book-compiled-file].~eq[] The three ~c[-okp] arguments, ~c[:uncertified-okp], ~c[defaxioms-okp], and ~c[skip-proofs-okp], determine the system's behavior when the book or any subbook is found to be uncertified, when the book or any subbook is found to contain ~ilc[defaxiom] events, and when the book or any subbook is found to contain ~ilc[skip-proofs] events, respectively. All three default to ~c[t], which means it is ``ok'' for the condition to arise. In this case, a warning is printed but the processing to load the book is allowed to proceed. When one of these arguments is ~c[nil] and the corresponding condition arises, an error is signaled and processing is aborted. ~st[Exception]: ~c[:uncertified-okp] is ignored if the ~c[include-book] is being performed on behalf of a ~ilc[certify-book]. The keyword argument ~c[:ttags] may normally be omitted. A few constructs, used for example if you are building your own system based on ACL2, may require it. ~l[defttag] for an explanation of this argument. ~c[Include-book] is similar in spirit to ~ilc[encapsulate] in that it is a single event that ``contains'' other ~il[events], in this case the ~il[events] listed in the file named. ~c[Include-book] processes the non-~ilc[local] event forms in the file, assuming that each is admissible. ~ilc[Local] ~il[events] in the file are ignored. You may use ~c[include-book] to load several ~il[books], creating the logical ~il[world] that contains the definitions and theorems of all of them. If any non-~ilc[local] event of the book attempts to define a ~il[name] that has already been defined ~-[] and the book's definition is not syntactically identical to the existing definition ~-[] the attempt to include the book fails, an error message is printed, and no change to the logical ~il[world] occurs. ~l[redundant-events] for the details. When a book is included, the default ~il[defun-mode] (~pl[default-defun-mode]) for the first event is always ~c[:]~ilc[logic]. That is, the default ~il[defun-mode] ``outside'' the book ~-[] in the environment in which ~c[include-book] was called ~-[] is irrelevant to the book. ~il[Events] that change the ~il[defun-mode] are permitted within a book (provided they are not in ~ilc[local] forms). However, such changes within a book are not exported, i.e., at the conclusion of an ~c[include-book], the ``outside'' default ~il[defun-mode] is always the same as it was before the ~c[include-book]. Unlike every other event in ACL2, ~c[include-book] puts a burden on you. Used improperly, ~c[include-book] can be unsound in the sense that it can create an inconsistent extension of a consistent logical ~il[world]. A certification mechanism is available to help you carry this burden ~-[] but it must be understood up front that even certification is no guarantee against inconsistency here. The fundamental problem is one of file system security. ~l[certificate] for a discussion of the security issues. At the beginning of execution of an ~c[include-book] form, even before executing ~il[portcullis] ~il[command]s, the value of ~ilc[acl2-defaults-table] is restored to the value it had at startup. After execution of an ~c[include-book] form, the value of ~ilc[acl2-defaults-table] is restored to what it was immediately before that ~c[include-book] form was executed. ~l[acl2-defaults-table]. ~b[Books Directory.] We refer to the ``books directory'' of an executable image as the full pathname string of the directory associated with ~c[include-book] keyword option ~c[:dir :system] for that image. By default, it is the ~c[books/] subdirectory of the directory where the sources reside and the executable image is thus built (except for ACL2(r) ~-[] ~pl[real] ~-[], where it is ~c[books/nonstd/]). If those books reside elsewhere, the environment variable ~c[ACL2_SYSTEM_BOOKS] can be set to the ~c[books/] directory under which they reside (a Unix-style pathname, typically ending in ~c[books/] or ~c[books], is permissible). In most cases, your ACL2 executable is a small script in which you can set this environment variable just above the line on which the actual ACL2 image is invoked, for example: ~bv[] export ACL2_SYSTEM_BOOKS ACL2_SYSTEM_BOOKS=/home/acl2/4-0/acl2-sources/books ~ev[] If you follow suggestions in the installation instructions, these books will be the ACL2 community books; ~pl[community-books]. This concludes the guided tour through ~il[books]. ~l[set-compile-fns] for a subtle point about the interaction between ~c[include-book] and on-the-fly ~il[compilation]. ~l[certify-book] for a discussion of how to certify a book.~/ :cited-by Programming" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'include-book-fn (list 'quote user-book-name) 'state (list 'quote load-compiled-file) (list 'quote :none) (list 'quote uncertified-okp) (list 'quote defaxioms-okp) (list 'quote skip-proofs-okp) (list 'quote ttags) (list 'quote doc) (list 'quote dir) (list 'quote event-form))) #+acl2-loop-only (defmacro make-event (&whole event-form form &key expansion? check-expansion on-behalf-of) ; Essay on Make-event ; This essay incorporates by reference :doc make-event and :doc ; make-event-details. That is, one should start by reading those documentation ; topics. This is a place to add details that seem of interest only to the ; implementors, not to ACL2 users. ; When we lay down a command landmark for a command for which expansion has ; taken place, we need to record that expansion somehow for subsequent calls of ; certify-book, in order to recover portcullis commands. Thus, ; add-command-landmark and make-command-tuple have an argument for the ; expansion (which could be nil, indicating that no expansion took place). ; We use record-expansion (as described in :doc make-event-details) in order to ; support redundancy of encapsulate, as implemented by redundant-encapsulatep ; and its subroutines. Here is a summary of the issue. Consider: (encapsulate ; ((foo (x) t)) ... (make-event )). We have several goals. ; + Be able to execute this form a second time and have it be redundant. ; + If this form is redundant yet in a book, it cannot cause a new expansion ; result for the make-event or the encapsulate, and include-book has to do ; the right thing even, if possible, in raw mode. ; + We want to store a proper expansion of an encapsulate. ; + We want to recognize redundancy without having to execute the encapsulate. ; + If an encapsulate form is redundant then its stored version is identical ; to the stored version of the earlier form for which it is redundant. ; The last of these properties is important because otherwise unsoundness could ; result! Suppose for example that a book bar.lisp contains (local ; (include-book "foo")), where foo.lisp contains an encapsulate that causes a ; later encapsulate in bar.lisp to be redundant. What should we know at the ; point we see the later encapsulate? We should know that the event logically ; represented by the encapsulate is the same as the one logically represented ; by the earlier encapsulate, so we certainly do not want to re-do its ; expansion at include-book time. Thus, when an encapsulate is redundant, we ; store the expanded version of the earlier encapsulate as the expansion of the ; current unexpanded encapsulate, unless the two are identical. But how do we ; expand a non-redundant encapsulate? We expand it by replacing every ; sub-event ev by (record-expansion ev exp), when ev has an expansion exp. ; Then, we recognize a subsequent encapsulate as redundant with this one if ; their signatures are equal and each of the subsequent encapsulate's events, ; ev2, is either the same as the corresponding event ev1 of the old encapsulate ; or else ev1 is of the form (record-expansion ev2 ...). ; We elide local forms arising from make-event expansions when writing to book ; certificates, in order to save space. See elide-locals. ; Note that when :puff (specifically puff-command-block) is applied to an ; include-book form, it uses the expansion-alist from the book's certificate if ; there is an up-to-date certificate. ":Doc-Section Events evaluate (expand) a given form and then evaluate the result~/ ~c[Make-event] is a utility for generating ~il[events]. It provides a capability not offered by Lisp macros (~pl[defmacro]), as it allows access to the ACL2 ~ilc[state] and logical ~il[world]. In essence, the expression ~c[(make-event form)] replaces itself with the result of evaluating ~c[form], say, ~c[ev], as though one had submitted ~c[ev] instead of the ~c[make-event] call. For example, ~c[(make-event (quote (defun f (x) x)))] is equivalent to the event ~c[(defun f (x) x)]. We break this documentation into the following sections. ~st[Introduction]~nl[] ~st[Detailed Documentation]~nl[] ~st[Error Reporting]~nl[] ~st[Restriction to Event Contexts]~nl[] ~st[Examples Illustrating How to Access State]~nl[] ~st[Advanced Expansion Control] We begin with an informal introduction, which focuses on examples and introduces the key notion of ``expansion phase''. ~st[Introduction] ~c[Make-event] is particularly useful for those who program using the ACL2 ~ilc[state]; ~pl[programming-with-state]. That is because the evaluation of ~c[form] may read and even modify the ACL2 ~ilc[state]. Suppose for example that we want to define a constant ~c[*world-length*], that is the length of the current ACL2 ~il[world]. A ~c[make-event] form can accomplish this task, as follows. ~bv[] ACL2 !>(length (w state)) 98883 ACL2 !>(make-event (list 'defconst '*world-length* (length (w state)))) Summary Form: ( DEFCONST *WORLD-LENGTH* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (LIST ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) *WORLD-LENGTH* ACL2 !>*world-length* 98883 ACL2 !>(length (w state)) 98890 ACL2 !> ~ev[] How did this work? First, evaluation of the form ~c[(list 'defconst '*world-length* (length (w state)))] returned the event form ~c[(defconst *world-length* 98883)]. Then that event form was automatically submitted to ACL2. Of course, that changed the ACL2 logical ~il[world], which is why the final value of ~c[(length (w state))] is greater than its initial value. The example above illustrates how the evaluation of a ~c[make-event] call takes place in two phases. The first phase evaluates the argument of the call, in this case ~c[(list 'defconst '*world-length* (length (w state)))], to compute an event form, in this case ~c[(defconst *world-length* 98883)]. We call this evaluation the ``expansion'' phase. Then the resulting event form is evaluated, which in this case defines the constant ~c[*world-length*]. Now suppose we would like to introduce such a ~ilc[defconst] form any time we like. It is common practice to define macros to automate such tasks. Now we might be tempted simply to make the following definition. ~bv[] ; WRONG! (defmacro define-world-length-constant (name state) (list 'defconst name (length (w state)))) ~ev[] But ACL2 rejects such a definition, because a macro cannot take the ACL2 state as a parameter; instead, the formal parameter to this macro named ~c[\"STATE\"] merely represents an ordinary object. You can try to experiment with other such direct methods to define such a macro, but they won't work. Instead, however, you can use the approach illustrated by the ~c[make-event] example above to define the desired macro, as follows. ~bv[] (defmacro define-world-length-constant (name) `(make-event (list 'defconst ',name (length (w state))))) ~ev[] Here are example uses of this macro. ~bv[] ACL2 !>(define-world-length-constant *foo*) Summary Form: ( DEFCONST *FOO* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (LIST ...)) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) *FOO* ACL2 !>*foo* 98891 ACL2 !>:pe *foo* 2:x(DEFINE-WORLD-LENGTH-CONSTANT *FOO*) \ > (DEFCONST *FOO* 98891) ACL2 !>(length (w state)) 98897 ACL2 !>(define-world-length-constant *bar*) Summary Form: ( DEFCONST *BAR* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (LIST ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) *BAR* ACL2 !>*bar* 98897 ACL2 !>:pe *bar* 3:x(DEFINE-WORLD-LENGTH-CONSTANT *BAR*) \ > (DEFCONST *BAR* 98897) ACL2 !>(length (w state)) 98903 ACL2 !> ~ev[] Finally, we note that the expansion phase can be used for computation that has side effects, generally by modifying state. Here is a modification of the above example that does not change the world at all, but instead saves the length of the world in a state global. ~bv[] (make-event (pprogn (f-put-global 'my-world-length (length (w state)) state) (value '(value-triple nil)))) ~ev[] Notice that this time, the value returned by the expansion phase is not an event form, but rather, is an error triple (~pl[error-triples]) whose value component is an event form, namely, the event form ~c[(value-triple nil)]. Evaluation of that event form does not change the ACL2 world (~pl[value-triple]). Thus, the sole purpose of the ~c[make-event] call above is to change the ~il[state] by associating the length of the current logical world with the state global named ~c['my-world-length]. After evaluating this form, ~c[(@ my-world-length)] provides the length of the ACL2 world, as illustrated by the following transcript. ~bv[] ACL2 !>:pbt 0 0:x(EXIT-BOOT-STRAP-MODE) ACL2 !>(length (w state)) 98883 ACL2 !>(make-event (pprogn (f-put-global 'my-world-length (length (w state)) state) (value '(value-triple nil)))) Summary Form: ( MAKE-EVENT (PPROGN ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) NIL ACL2 !>(length (w state)) 98883 ACL2 !>:pbt 0 0:x(EXIT-BOOT-STRAP-MODE) ACL2 !> ~ev[] When ~c[make-event] is invoked by a book, it is expanded during book certification but not, by default, when the book is included. So for the example ~c[(define-world-length-constant *foo*)] given above, if that form is in a book, then the value of ~c[*foo*] will be the length of the world at the time this form was invoked during book certification, regardless of world length at ~ilc[include-book] time. (The expansion is recorded in the book's ~il[certificate], and re-used.) To overcome this default, you can specified keyword value ~c[:CHECK-EXPANSION t]. This will cause an error if the expansion is different, but it can be useful for side effects. For example, if you insert the following form in a book, then the length of the world will be printed when the form is encountered, whether during ~ilc[certify-book] or during ~ilc[include-book]. ~bv[] (make-event (pprogn (fms \"Length of current world: ~~x0~~|\" (list (cons #\\0 (length (w state)))) *standard-co* state nil) (value '(value-triple nil))) :check-expansion t) ~ev[]~/ ~st[Detailed Documentation] ~bv[] Examples: ; Trivial example: evaluate (quote (defun foo (x) x)) to obtain ; (defun foo (x) x), which is then evaluated. (make-event (quote (defun foo (x) x))) ; Evaluate (generate-form state) to obtain (mv nil val state), and ; then evaluate val. (Generate-form is not specified here, but ; imagine for example that it explores the state and then generates ; some desired definition or theorem.) (make-event (generate-form state)) ; As above, but make sure that if this form is in a book, then when ; we include the book, the evaluation of (generate-form state) ; should return the same value as it did when the book was ; certified. (make-event (generate-form state) :CHECK-EXPANSION t) ; As above (where the :CHECK-EXPANSION value can be included or ; not), where if there is an error during expansion, then the error ; message will explain that expansion was on behalf of the indicated ; object, typically specified as the first argument. (make-event (generate-form state) :ON-BEHALF-OF (generate-form state)) General Form: (make-event form :CHECK-EXPANSION chk :ON-BEHALF-OF obj :EXPANSION? form) ~ev[] where ~c[chk] is ~c[nil] (the default), ~c[t], or the intended ``expansion result'' from the evaluation of ~c[form] (as explained below); and if supplied, ~c[obj] is an arbitrary ACL2 object, used only in reporting errors in expansion, i.e., in the evaluation of form. The ~c[:EXPANSION?] keyword is discussed in the final section, on Advanced Expansion Control. We strongly recommend that you browse some ~c[.lisp] files in the community books directory ~c[books/make-event/]. You may even find it helpful, in order to understand ~c[make-event], to do so before continuing to read this documentation. You may also find it useful to browse community book ~c[books/misc/eval.lisp], which contains definitions of macros ~c[must-succeed] and ~c[must-fail] that are useful for testing and are used in many books in the ~c[books/make-event/] directory, especially ~c[eval-tests.lisp]. Another example, ~c[books/make-event/defrule.lisp], shows how to use macros whose calls expand to ~c[make-event] forms, which in turn can generate ~il[events]. For more examples, see file ~c[books/make-event/Readme.lsp]. Other than the examples, the explanations here should suffice for most users. If you want explanations of subtler details, ~pl[make-event-details]. Note that ~c[make-event] may only be used at the ``top level'' or where an event is expected. See the section ``Restriction to Event Contexts'', below. ~c[Make-event] is related to Lisp macroexpansion in the sense that its argument is evaluated to obtain an expansion result, which is evaluated again. Let us elaborate on each of these notions in turn: ``is evaluated,'' ``expansion result'', and ``evaluated again.'' The final section, on Advanced Expansion Control, will generalize these processes in a way that we ignore for now.~bq[] ``is evaluated'' ~-[] The argument can be any expression, which is evaluated as would be any expression submitted to ACL2's top level loop. Thus, ~ilc[state] and user-defined ~ilc[stobj]s may appear in the form supplied to ~c[make-event]. Henceforth, we will refer to this evaluation as ``expansion.'' Expansion is actually done in a way that restores ACL2's built-in ~ilc[state] global variables, including the logical ~il[world], to their pre-expansion values (with a few exceptions ~-[] ~pl[make-event-details] ~-[] and where we note that changes to user-defined ~ilc[state] global variables (~pl[assign]) are preserved). So, for example, events might be evaluated during expansion, but they will disappear from the logical ~il[world] after expansion returns its result. Moreover, proofs are enabled by default at the start of expansion (~pl[ld-skip-proofsp]) if keyword ~c[:CHECK-EXPANSION] is supplied and has a non-~c[nil] value. ``expansion result'' ~-[] The above expansion may result in an ordinary (non-~ilc[state], non-~ilc[stobj]) value, which we call the ``expansion result.'' Or, expansion may result in a multiple value of the form ~c[(mv erp val state)], or, more generally, ~c[(mv erp val state stobj-1 ... stobj-k)] where each ~c[stobj-i] is a ~il[stobj]; then the expansion result is ~c[val] unless ~c[erp] is not ~c[nil], in which case there is no expansion result, and the original ~c[make-event] evaluates to a soft error. In either case (single or multiple value), either ~c[val] is an embedded event form (~pl[embedded-event-form]), or else the original ~c[make-event] evaluates to a soft error, printed as described under ``Error Reporting'' below. ``evaluated again'' ~-[] the expansion result is evaluated in place of the original ~c[make-event]. ~eq[]The expansion process can invoke subsidiary calls of ~c[make-event], and the expansion result can (perhaps after macroexpansion) be a call of ~c[make-event]. It can be useful to track all these ~c[make-event] calls. The ~il[state] global variable ~c[make-event-debug] may be set to a non-~c[nil] value, for example ~c[(assign make-event-debug t)], in order to see a trace of the expansion process, where a level is displayed (as in ``~c[3>]'') to indicate the depth of subsidiary expansions. Expansion of a ~c[make-event] call will yield an event that replaces the original ~c[make-event] call. In particular, if you put a ~c[make-event] form in a book, then in essence it is replaced by its expansion result, created during the proof pass of the ~ilc[certify-book] process. We now elaborate on this idea of keeping the original expansion. A ~c[make-event] call generates a ``~c[make-event] replacement'' that may be stored by the system. In the simplest case, this replacement is the expansion result. When a book is certified, these replacements are stored in a book's certificate (technically, in the ~c[:EXPANSION-ALIST] field). Thus, although the book is not textually altered during certification, one may imagine a ``book expansion'' corresponding to the original book, in which events are substituted by replacements that were generated during the proof phase of certification. A subsequent ~ilc[include-book] will then include the book expansion corresponding to the indicated book. When a book is compiled during ~ilc[certify-book], it is actually the corresponding book expansion, stored as a temporary file, that is compiled instead. That temporary file is deleted after compilation unless one first evaluates the form ~c[(assign keep-tmp-files t)]. Note however that all of the original forms must still be legal ~il[events]; ~pl[embedded-event-form]. So for example, if the first event in a book is ~c[(local (defmacro my-id (x) x))], and is followed by ~c[(my-id (make-event ...))], the final ``~c[include-book]'' pass of ~ilc[certify-book] will fail because ~c[my-id] is not defined when the ~c[my-id] call is encountered. A ~c[make-event] replacement might not be the expansion when either of the keyword arguments ~c[:CHECK-EXPANSION] or ~c[:EXPANSION?] is supplied. We deal with the latter in the final section, on Advanced Expansion Control. If ~c[:CHECK-EXPANSION t] is supplied and the expansion is ~c[exp], then the replacement is obtained from the original ~c[make-event] call, by substituting ~c[exp] for ~c[t] as the value of keyword ~c[:CHECK-EXPANSION]. Such a ~c[make-event] call ~-[] during the second pass of an ~ilc[encapsulate] or during event processing on behalf of ~ilc[include-book] ~-[] will do the expansion again and check that the expansion result is equal to the original expansion result, ~c[exp]. In the unusual case that you know the expected expansion result, ~c[res], you can specify ~c[:CHECK-EXPANSION res] in the first place, so that the check is also done during the initial evaluation of the ~c[make-event] form. IMPORTANT BUT OBSCURE DETAIL: That expansion check is only done when processing events, not during a preliminary load of a book's compiled file. The following paragraph elaborates. (Here are details on the point made just above, for those who use the ~c[:CHECK-EXPANSION] argument to perform side-effects on the ~il[state]. When you include a book, ACL2 generally loads a compiled file before processing the events in the book; ~pl[book-compiled-file]. While it is true that a non-~c[nil] ~c[:CHECK-EXPANSION] argument causes ~ilc[include-book] to perform expansion of the ~c[make-event] form during event processing it does ~em[not] perform expansion when the compiled file (or expansion file; again, ~pl[book-compiled-file]) is loaded.) ACL2 performs the following space-saving optimization: when the expansion result is a ~ilc[local] event, then the ~c[make-event] replacement is ~c[(local (value-triple :ELIDED))]. The notion of ``expansion'' and ``replacement'' extend to the case that a call of ~c[make-event] is found in the course of macroexpansion. The following example illustrates this point. ~bv[] (encapsulate () (defmacro my-mac () '(make-event '(defun foo (x) x))) (my-mac)) :pe :here ~ev[] The above call of ~ilc[pe] shows that the form ~c[(my-mac)] has a ~c[make-event] expansion (and replacement) of ~c[(DEFUN FOO (X) X)]: ~bv[] (ENCAPSULATE NIL (DEFMACRO MY-MAC NIL '(MAKE-EVENT '(DEFUN FOO (X) X))) (RECORD-EXPANSION (MY-MAC) (DEFUN FOO (X) X))) ~ev[] ~st[Error Reporting] Suppose that expansion produces a soft error as described above. That is, suppose that the argument of a ~c[make-event] call evaluates to a multiple value ~c[(mv erp val state ...)] where ~c[erp] is not ~c[nil]. If ~c[erp] is a string, then that string is printed in the error message. If ~c[erp] is a ~ilc[cons] pair whose ~ilc[car] is a string, then the error prints ~c[\"~~@0\"] with ~c[#\\0] bound to that ~c[cons] pair; ~pl[fmt]. Any other non-~c[nil] value of ~c[erp] causes a generic error message to be printed. ~st[Restriction to Event Contexts] A ~c[make-event] call must occur either at the top level, or during ~c[make-event] expansion, or as an argument of an event constructor. We explain in more detail below. This restriction is imposed to enable ACL2 to track expansions produced by ~c[make-event]. The following examples illustrate this restriction. ~bv[] ; Legal: (progn (with-output :on summary (make-event '(defun foo (x) x)))) ; Illegal: (mv-let (erp val state) (make-event '(defun foo (x) x)) (mv erp val state)) ~ev[] More precisely: a ~c[make-event] call that is not itself evaluated during ~c[make-event] expansion is subject to the following requirement. After macroexpansion has taken place, such a ~c[make-event] call must be in an ``event context'', defined recursively as follows. (All but the first two cases below correspond to similar cases for constructing events; ~pl[embedded-event-form].) ~bq[] o A form submitted at the top level, or more generally, supplied to a call of ~ilc[ld], is in an event context. o A form occurring at the top level of a book is in an event context. o If ~c[(]~ilc[LOCAL]~c[ x1)] is in an event context, then so is ~c[x1]. o If ~c[(]~ilc[SKIP-PROOFS]~c[ x1)] is in an event context, then so is ~c[x1]. o If ~c[(]~ilc[MAKE-EVENT]~c[ x ...)] is in an event context and its expansion ~c[x1] is an embedded event form, then ~c[x1] is in an event context. o If ~c[(]~ilc[WITH-OUTPUT]~c[ ... x1)], ~c[(]~ilc[WITH-PROVER-STEP-LIMIT]~c[ ... x1 ...)], or ~c[(]~ilc[WITH-PROVER-TIME-LIMIT]~c[ ... x1)] is in an event context, then so is ~c[x1]. o For any call of ~ilc[PROGN] or ~ilc[PROGN!], each of its arguments is in an event context. o For any call of ~ilc[ENCAPSULATE], each of its arguments except the first (the signature list) is in an event context. o If ~c[(RECORD-EXPANSION x1 x2)] is in an event context, then ~c[x1] and ~c[x2] are in event contexts. Note: ~c[record-expansion] is intended for use only by the implementation, which imposes the additional restriction that ~c[x1] and its subsidiary ~c[make-event] calls (if any) must specify a ~c[:CHECK-EXPANSION] argument that is a ~il[consp]. ~eq[] Low-level remark, for system implementors. There is the one exception to the above restriction: a single ~ilc[state-global-let*] form immediately under a ~c[progn!] call. For example: ~bv[] (progn! (state-global-let* (make-event ...))) ~ev[] However, the following form may be preferable (~pl[progn!]): ~bv[] (progn! :STATE-GLOBAL-BINDINGS (make-event ...)) ~ev[] Also ~pl[remove-untouchable] for an interesting use of this exception. ~st[Examples Illustrating How to Access State] You can modify the ACL2 ~il[state] by doing your state-changing computation during the expansion phase, before expansion returns the event that is submitted. Here are some examples. First consider the following. Notice that expansion modifies state global ~c[my-global] during ~c[make-event] expansion, and then expansion returns a ~ilc[defun] event to be evaluated. ~bv[] (make-event (er-progn (assign my-global (length (w state))) (value '(defun foo (x) (cons x x))))) ~ev[] Then we get: ~bv[] ACL2 !>(@ my-global) 72271 ACL2 !>:pe foo L 1:x(MAKE-EVENT (ER-PROGN # #)) \ >L (DEFUN FOO (X) (CONS X X)) ACL2 !> ~ev[] Here's a slightly fancier example, where the computation affects the ~ilc[defun]. In a new session, execute: ~bv[] (make-event (er-progn (assign my-global (length (w state))) (value `(defun foo (x) (cons x ,(@ my-global)))))) ~ev[] Then: ~bv[] ACL2 !>(@ my-global) 72271 ACL2 !>:pe foo L 1:x(MAKE-EVENT (ER-PROGN # #)) \ >L (DEFUN FOO (X) (CONS X 72271)) ACL2 !> ~ev[] Note that ACL2 ~il[table] ~il[events] may avoid the need to use ~il[state] globals. For example, instead of the example above, consider this example in a new session. ~bv[] (make-event (let ((world-len (length (w state)))) `(progn (table my-table :STORED-WORLD-LENGTH ,world-len) (defun foo (x) (cons x ,world-len))))) ~ev[] Then: ~bv[] ACL2 !>(table my-table) ((:STORED-WORLD-LENGTH . 72271)) ACL2 !>:pe foo 1:x(MAKE-EVENT (LET # #)) \ >L (DEFUN FOO (X) (CONS X 72271)) ACL2 !> ~ev[] By the way, most built-in ~il[state] globals revert after expansion. But your own global (like ~c[my-global] above) can be set during expansion, and the new value will persist. ~st[Advanced Expansion Control] We conclude this ~il[documentation] section by discussing three kinds of additional control over ~c[make-event] expansion. These are all illustrated in community book ~c[books/make-event/make-event-keywords-or-exp.lisp]. The discussion below is split into the following three parts. (1) The value produced by expansion may have the form ~c[(:DO-PROOFS exp)], which specifies ~c[exp] as the expansion result, to be evaluated without skipping proofs even when including a book. (2) The value produced by expansion may have the form ~c[(:OR exp-1 ... exp-k)], which specifies that the first form ~c[exp-i] to evaluate without error is the expansion result. (3) The keyword argument ~c[:EXPANSION?] can serve to eliminate the storing of ~c[make-event] replacements, as described above for the ``book expansion'' of a book. We now elaborate on each of these. (1) ~c[:DO-PROOFS] ``call'' produced by expansion. We have discussed the expansion result produced by the expansion phase of evaluating a ~c[make-event] call. However, if the expansion phase produces an expression of the form ~c[(:DO-PROOFS exp)], then the expansion result is actually ~c[exp]. The ~c[:DO-PROOFS] wrapper indicates that even if proofs are currently being skipped (~pl[ld-skip-proofsp]), then evaluation of ~c[exp] should take place with proofs not skipped. For example, proofs will be performed when evaluating the ~c[make-event] expansion, namely the indicated ~c[defthm] event, in the following example. ~bv[] (set-ld-skip-proofsp t state) (make-event '(:DO-PROOFS (defthm app-assoc (equal (append (append x y) z) (append x y z))))) ~ev[] Note that such use of ~c[:DO-PROOFS] causes proofs to be performed when evaluating the expansion while including an uncertified book. But when including a certified book, then unless ~c[:CHECK-EXPANSION] is supplied a non-~c[nil] value, the ~c[make-event] replacement will just be the expansion, which does not include the ~c[:DO-PROOFS] wrapper and hence will be evaluated with proofs skipped. (2) ~c[:OR] ``call'' produced by expansion. There may be times where you want to try different expansions. For example, the community book ~c[books/make-event/proof-by-arith.lisp] attempts to admit a given event, which we'll denote ~c[EV], by trying events of the following form as ~c[BOOK] varies over different community books. ~bv[] (encapsulate () (local (include-book BOOK :DIR :SYSTEM)) EV) ~ev[] A naive implementation of this macro would evaluate all such ~ilc[encapsulate] events until one succeeds, and then return that successful event as the expansion. Then that event would need to be evaluated again! With some hacking one could avoid that re-evaluation by using ~ilc[skip-proofs], but that won't work if you are trying to create a certified book without skipped proofs. Instead, the implementation creates an expansion of the form ~c[(:OR ev-1 ev-2 ... ev-k)], where the list ~c[(ev-1 ev-2 ... ev-k)] enumerates the generated encapsulate events. In general, for this ``disjunctive case'' of a result from expansion, each ~c[ev-i] is evaluated in sequence, and the first that succeeds without error is considered to be the expansion result ~-[] and a repeat evaluation is avoided. If evaluation of each ~c[ev-i] results in an error, then so does the ~c[make-event] call. This special use of ~c[:OR] in a value produced by expansion is only supported at the top level. That is, the result can be ~c[(:OR ev-1 ev-2 ... ev-k)] but then each ~c[ev-i] must be a legal expansion result, without such further use of ~c[:OR] ~-[] except, ~c[ev-i] may be ~c[(:DO-PROOFS ev-i')], where ~c[ev-i'] then would serve as the expansion rather than ~c[ev-i]. (3) The ~c[:EXPANSION?] keyword argument. If keyword argument ~c[:EXPANSION?] has a non~c[nil] value, then the ~c[:CHECK-EXPANSION] keyword must be omitted or have value ~c[nil] or ~c[t], hence not a cons pair. The idea of the ~c[:EXPANSION?] keyword is to give you a way to avoid storing expansion results in a book's ~il[certificate]. Roughly speaking, when the expansion result matches the value of ~c[:EXPANSION?], then no expansion result is stored for the event by book certification; then when the book is later included, the value of ~c[:EXPANSION?] is used as the expansion, thus bypassing the expansion phase. One could say that the event is its own make-event replacement, but it is more accurate to say that there is no make-event replacement at all, since nothing is stored in the certificate for this event. Below, we elaborate on make-event replacements when ~c[:EXPANSION] is used and also discuss other properties of this keyword. We modify the notion of ``expansion result'' for ~c[make-event] forms to comprehend the use of the ~c[:EXPANSION?] keyword. For that purpose, let's consider a call of ~c[make-event] to be ``reducible'' if it has an ~c[:EXPANSION?] keyword with non-~c[nil] value, ~c[exp], and its ~c[:CHECK-EXPANSION] keyword is missing or has value ~c[nil], in which case the ``reduction'' of this ~c[make-event] call is defined to be ~c[exp]. The expansion result as originally defined is modified by the following ``recursive reduction'' process: recur through the original expansion, passing through calls of ~ilc[local], ~ilc[skip-proofs], ~ilc[with-output], ~ilc[with-prover-step-limit], and ~ilc[with-prover-time-limit], and replacing (recursively) any reducible call of ~c[make-event] by its reduction. Furthermore, we refer to two forms as ``reduction equivalent'' if their recursive reductions are equal. Note that the recursive reduction process does not pass through ~ilc[progn] or ~ilc[encapsulate], but that process is applied to the computation of expansions for their subsidiary ~ilc[make-event] calls. To explain further the effect of ~c[:EXPANSION? exp], we split into the following two cases. o Case 1: Evaluation is not taking place when including a book or evaluating the second pass of an ~ilc[encapsulate] event; more precisely, the value of ~c[(ld-skip-proofsp state)] is not the symbol ~c[INCLUDE-BOOK]. There are two subcases. ~bq[] - Case 1a: The expansion result is not reduction-equivalent to ~c[exp]. Then the ~c[make-event] call is processed as though the ~c[:EXPANSION?] keyword had been omitted. - Case 2a: The expansion result is reduction-equivalent to ~c[exp]. Then there is no ~c[make-event] replacement for this call of ~c[make-event]; no replacement will be put into the ~il[certificate] file for a book containing this ~c[make-event] call. When that book is subsequently included, the original form will be evaluated in the manner described in the next case.~eq[] o Case 2: Evaluation is taking place when including a book or evaluating the second pass of an ~ilc[encapsulate] event; more precisely, the value of ~c[(ld-skip-proofsp state)] is the symbol ~c[INCLUDE-BOOK]. Then the expansion is ~c[exp]. The expansion phase is skipped unless ~c[:CHECK-EXPANSION] is ~c[t]. The ~c[:EXPANSION?] keyword can be particularly useful in concert with the disjunctive (``~c[:OR]'') case (2) discussed above. Suppose that expansion produces a value as discussed in (2) above, ~c[(:OR exp-1 ... exp-k)]. If one of these expressions ~c[exp-i] is more likely than the others to be the expansion, then you may wish to specify ~c[:EXPANSION? exp-i], as this will avoid storing a ~c[make-event] replacement in that common case. This could be useful if the expressions are large, to avoid enlarging the ~il[certificate] file for a book containing the ~c[make-event] call. It is legal to specify both ~c[:EXPANSION? exp] and ~c[:CHECK-EXPANSION t]. When either ~c[(ld-skip-proofsp state)] is the symbol ~c[INCLUDE-BOOK], or evaluation is taking place in raw Lisp, then this combination is treated the same as if ~c[:EXPANSION?] is omitted and the value of ~c[:CHECK-EXPANSION] is ~c[exp]. Otherwise, this combination is treated the same as ~c[:CHECK-EXPANSION t], modified to accommodate the effect of ~c[:EXPANSION?] as discussed above: if the expansion is indeed the value of ~c[:EXPANSION?], then no ~c[make-event] replacement is generated." (declare (xargs :guard t)) ; Keep this in sync with the -acl2-loop-only definition. `(make-event-fn ',form ',expansion? ',check-expansion ',on-behalf-of ',event-form state)) (defdoc make-event-details ":Doc-Section Make-event details on ~ilc[make-event] expansion~/ The normal user of ~c[make-event] can probably ignore this section, but we include it for completeness. We assume that the reader has read and understood the basic documentation for ~c[make-event] (~pl[make-event]), but we begin below with a summary of expansion.~/ ~st[Introduction] Here is a summary of how we handle expansion involving ~c[make-event] forms. ~c[(make-event form :check-expansion nil)] This shows the ~c[:check-expansion] default of ~c[nil], and is typical user input. We compute the expansion ~c[exp] of ~c[form], which is the expansion of the original ~c[make-event] expression and is evaluated in place of that expression. ~c[(make-event form :check-expansion t)] The user presumably wants it checked that the expansion doesn't change in the future, in particular during ~ilc[include-book]. If the expansion of ~c[form] is ~c[exp], then we will evaluate ~c[exp] to obtain the value as before, but this time we record that the expansion of the original ~c[make-event] expression is ~c[(make-event form :check-expansion exp)] rather than simply ~c[exp]. ~c[(make-event form :check-expansion exp) ; exp a cons] This is generated for the case that ~c[:check-expansion] is ~c[t], as explained above. Evaluation is handled as described in that above case, except here we check that the expansion result is the given ~c[exp]. (Actually, the user is also allowed supply such a form.) The original ~c[make-event] expression does not undergo any expansion (intuitively, it expands to itself). Now let us take a look at how we expand ~ilc[progn] forms (~ilc[encapsulate] is handled similarly). ~c[(progn ... (make-event form :check-expansion nil) ...)] The expansion is obtained by replacing the ~c[make-event] form as follows. Let ~c[exp] be the expansion of ~c[form], Then replace the above ~c[make-event] form, which we denote as ~c[F], by ~c[(record-expansion F exp)]. Here, ~c[record-expansion] is a macro that returns its second argument. ~c[(progn ... (make-event form :check-expansion t) ...)] The expansion is of the form ~c[(record-expansion F exp)] as in the ~c[nil] case above, except that this time ~c[exp] is ~c[(make-event form :check-expansion exp')], where ~c[exp'] is the expansion of ~c[form]. ~c[(progn ... (make-event form :check-expansion exp) ...) ; exp a cons] No expansion takes place unless expansion takes place for at least one of the other subforms of the ~c[progn], in which case each such form ~c[F] is replaced by ~c[(record-expansion F exp)] where ~c[exp] is the expansion of ~c[F]. ~st[Detailed semantics] In our explanation of the semantics of ~c[make-event], we assume familiarity with the notion of ``embedded event form'' (~pl[embedded-event-form]). Let's say that the ``actual embedded event form'' corresponding to a given form is the underlying call of an ACL2 event: that is, ~ilc[LOCAL]s are dropped when ~c[ld-skip-proofsp] is ~c['include-book], and macros are expanded away, thus leaving us with a ~ilc[progn], a ~ilc[make-event], or an event form (possibly ~ilc[encapsulate]), any of which might have surrounding ~ilc[local], ~ilc[skip-proofs], or ~ilc[with-output] calls. Thus, such an actual embedded event form can be viewed as having the form ~c[(rebuild-expansion wrappers base-form)] where ~c[base-form] is a ~c[progn], a ~c[make-event], or an event form (possibly ~c[encapsulate]), and ~c[wrappers] are (as in ACL2 source function ~c[destructure-expansion]) the result of successively removing the event form from the result of macroexpansion, leaving a sequence of ~c[(local)], ~c[(skip-proofs)], and ~c[(with-output ...)] forms. In this case we say that the form ``destructures into'' the indicated ~c[wrappers] and ~c[base-form], and that it can be ``rebuilt from'' those ~c[wrappers] and ~c[base-form]. Elsewhere we define the notion of the ``expansion result'' from an evaluation (~pl[make-event]), and we mention that when expansion concludes, the ACL2 logical ~il[world] and most of the ~c[state] are restored to their pre-expansion values. Specifically, after evaluation of the argument of ~c[make-event] (even if it is aborted), the ACL2 logical world is restored to its pre-evaluation value, as are all state global variables in the list ~c[*protected-system-state-globals*]. Thus, assignments to user-defined state globals (~pl[assign]) do persist after expansion, since they are not in that list. We recursively define the combination of evaluation and expansion of an embedded event form, as follows. We also simultaneously define the notion of ``expansion takes place,'' which is assumed to propagate upward (in a sense that will be obvious), such that if no expansion takes place, then the expansion of the given form is considered to be itself. It is useful to keep in mind a goal that we will consider later: Every ~c[make-event] subterm of an expansion result has a ~c[:check-expansion] field that is a ~ilc[consp], where for this purpose ~c[make-event] is viewed as a macro that returns its ~c[:check-expansion] field. (Implementation note: The latest expansion of a ~ilc[make-event], ~ilc[progn], ~ilc[progn!], or ~ilc[encapsulate] is stored in state global ~c['last-make-event-expansion], except that if no expansion has taken place for that form then ~c['last-make-event-expansion] has value ~c[nil].)~bq[] If the given form is not an embedded event form, then simply cause a soft error, ~c[(mv erp val state)] where ~c[erp] is not ~c[nil]. Otherwise: If the evaluation of the given form does not take place (presumably because ~ilc[local] events are being skipped), then no expansion takes place. Otherwise: Let ~c[x] be the actual embedded event form corresponding to the given form, which destructures into wrappers ~c[W] and base-form ~c[B]. Then the original form is evaluated by evaluating ~c[x], and its expansion is as follows. If ~c[B] is ~c[(make-event form :check-expansion val)], then expansion takes place if and only if ~c[val] is not a ~c[consp] and no error occurs, as now described. Let ~c[R] be the expansion result from protected evaluation of ~c[form], if there is no error. ~c[R] must be an embedded event form, or it is an error. Then evaluate/expand ~c[R], where if ~c[val] is not ~c[nil] then state global ~c['ld-skip-proofsp] is initialized to ~c[nil]. (This initialization is important so that subsequent expansions are checked in a corresponding environment, i.e., where proofs are turned on in both the original and subsquent environments.) It is an error if this evaluation causes an error. Otherwise, the evaluation yields a value, which is the result of evaluation of the original ~c[make-event] expression, as well as an expansion, ~c[E_R]. Let ~c[E] be rebuilt from ~c[W] and ~c[E_R]. The expansion of the original form is ~c[E] if ~c[val] is ~c[nil], and otherwise is the result of replacing the original form's ~c[:check-expansion] field with ~c[E], with the added requirement that if ~c[val] is not ~c[t] (thus, a ~c[consp]) then ~c[E] must equal ~c[val] or else we cause an error. If ~c[B] is either ~c[(progn form1 form2 ...)] or ~c[(encapsulate sigs form1 form2 ...)], then after evaluating ~c[B], the expansion of the original form is the result of rebuilding from ~c[B], with wrappers ~c[W], after replacing each ~c[formi] in ~c[B] for which expansion takes place by ~c[(record-expansion formi formi')], where ~c[formi'] is the expansion of ~c[formi]. Note that these expansions are determined as the ~c[formi] are evaluated in sequence (where in the case of ~c[encapsulate], this determination occurs only during the first pass). Except, if no expansion takes place for any ~c[formi], then the expansion of the original form is itself. Otherwise, the expansion of the original form is itself. ~eq[]Similarly to the ~ilc[progn] and ~ilc[encapsulate] cases above, book certification causes a book to be replaced by its so-called ``book expansion.'' There, each event ~c[ev] for which expansion took place during the proof pass of certification ~-[] say, producing ~c[ev'] ~-[] is replaced by ~c[(record-expansion ev ev')]. Implementation Note. The book expansion is actually implemented by way of the ~c[:expansion-alist] field of its ~il[certificate], which associates 0-based positions of top-level forms in the book (not including the initial ~ilc[in-package] form) with their expansions. Thus, the book's source file is not overwritten; rather, the certificate's expansion-alist is applied when the book is included or compiled. End of Implementation Note. It is straightforward by computational induction to see that for any expansion of an embedded event form, every ~c[make-event] sub-event has a ~ilc[consp] ~c[:check-expansion] field. Here, by ``sub-event'' we mean to expand macros; and we also mean to traverse ~c[progn] and ~c[encapsulate] forms as well as ~c[:check-expansion] fields of ~c[make-event] forms. Thus, we will only see ~c[make-event] forms with ~c[consp] ~c[:check-expansion] fields in the course of ~c[include-book] forms, the second pass of ~c[encapsulate] forms, and raw Lisp. This fact guarantees that an event form will always be treated as its original expansion. ~st[Notes on ttags] ~l[defttag] for documentation of the notion of ``trust tag'' (``ttag''). We note here that even if an event ~c[(defttag tag-name)] for non-~c[nil] ~c[tag-name] is admitted only during the expansion phase of a ~ilc[make-event] form, then such expansion will nevertheless still cause ~c[tag-name] to be recorded in the logical ~il[world] (assuming that the ~c[make-event] form is admitted). So in order to certify such a book, a suitable ~c[:ttags] argument must be supplied; ~pl[certify-book]. ACL2 does provide a way to avoid the need for ~c[:ttags] arguments in such cases. The idea is to certify a book twice, where the results of ~c[make-event] expansion are saved from the first call of ~ilc[certify-book] and provided to the second call. ~l[set-write-acl2x]. Finally, we discuss a very unusual case where certification does not involve trust tags but a subsequent ~ilc[include-book] does involve trust tags: a ~c[make-event] call specifying ~c[:check-expansion t], whose expansion generates a ~ilc[defttag] event during ~ilc[include-book] but not ~ilc[certify-book]. Consider the following book. ~bv[] (in-package \"ACL2\") (make-event (er-progn (if (@ skip-notify-on-defttag) ; non-nil when including a certified book (pprogn (fms \"Value of (@ skip-notify-on-defttag): ~~x0~~|\" (list (cons #\0 (@ skip-notify-on-defttag))) *standard-co* state nil) (encapsulate () (defttag :foo) (value-triple \"Imagine something bad here!\"))) (value nil)) (value '(value-triple :some-value))) :check-expansion t) ~ev[] This book certifies successfully without the need for a ~c[:ttags] argument for ~ilc[certify-book]. Indeed, the above book's ~il[certificate] does not specify ~c[:foo] as a trust tag associated with the book, because no ~c[defttag] event was executed during book certification. Unfortunately, if we try to include this book without specifying a value of ~c[:ttags] that allows ~c[:foo], book inclusion will cause executing of the above ~ilc[defttag]. If that inclusion happens in the context of certifying some superior book and the appropriate ~c[:ttags] arguments have not been provided, that certification will fail.~/") (defdoc using-tables-efficiently ":Doc-Section Table Notes on how to use tables efficiently~/ (Thanks to Jared Davis for contributing this ~il[documentation] topic, to which we have made only minor modifications.) Suppose your book contains ~ilc[table] ~il[events], or macros that expand into ~c[table] events, of the following form: ~bv[] (table my-table 'my-field ) ~ev[] Then ~c[] will be evaluated ~em[twice] during ~ilc[certify-book] and ~em[again] every time you include the book with ~ilc[include-book]. In some cases this overhead can be avoided using ~ilc[make-event]. See also community book ~c[books/make-event/defconst-fast.lisp] for an analogous trick involving ~ilc[defconst].~/ As an example, suppose we want to store numbers in a table only if they satisfy some computationally expensive predicate. We'll introduce a new book, ~c[number-table.lisp], and create a table to store these numbers: ~bv[] (table number-table 'data nil) ~ev[] Instead of implementing a ``computationally expensive predicate,'' we'll write a function that just prints a message when it is called and accepts even numbers: ~bv[] (defun expensive-computation (n) (prog2$ (cw \"Expensive computation on ~~x0.~~%\" n) (evenp n))) ~ev[] Now we'll implement a macro, ~c[add-number], which will add its argument to the table only if it satisfies the expensive predicate: ~bv[] (defmacro add-number (n) `(table number-table 'data (let ((current-data (cdr (assoc-eq 'data (table-alist 'number-table world))))) (if (expensive-computation ,n) (cons ,n current-data) current-data)))) ~ev[] Finally, we'll call ~c[add-number] a few times to finish the book. ~bv[] (add-number 1) (add-number 2) (add-number 3) ~ev[] When we now invoke ~c[(certify-book \"number-table\")], we see the expensive predicate being called twice for each number: once in Step 2, the main pass, then again in Step 3, the admissibility check. Worse, the computation is performed again for each number when we use ~ilc[include-book] to load ~c[number-table], e.g., ~bv[] ACL2 !>(include-book \"number-table\") Expensive computation on 1. Expensive computation on 2. Expensive computation on 3. ~ev[] To avoid these repeated executions, we can pull the test out of the ~c[table] event using ~ilc[make-event]. Here's an alternate implementation of ~c[add-number] that won't repeat the computation: ~bv[] (defmacro add-number (n) `(make-event (if (expensive-computation ,n) '(table number-table 'data (cons ,n (cdr (assoc 'data (table-alist 'number-table world))))) '(value-triple :expensive-computation-failed)))) ~ev[] When we recertify ~c[number-table.lisp], we'll see the expensive computation is still called once for each number in Step 2, but is no longer called during Step 3. Similarly, the ~ilc[include-book] no longer shows any calls of the expensive computation.~/ :cite make-event") (defmacro record-expansion (x y) ; This funny macro simply returns its second argument. However, we use it in ; the implementation to replace a given embedded event form x by its make-event ; expansion y, while retaining the information that y came from expanding x. (declare (ignore x)) y) ; Essay on Soundness Threats ; Several of ACL2's rich set of features have the potential to compromise ; soundness unless we take suitable care, including: ; * defaxiom ; * hidden defpkg events (known-package-alist) ; * skip-proofs (skip-proofs and set-ld-skip-proofsp) ; * illegal certification world: uncertified books, non-events (including ; redefinition), trust tags (defttag) ; * acl2-defaults-table ; * local [not yet explained here, but there's lots we could say -- see release ; notes for related soundness bugs!] ; Here we briefly discuss these soundness threats and how we deal with them, ; pointing to other essays for further details. Many of these issues are ; caused by LOCAL, which can introduce axioms that ultimately disappear. ; To see the potential problem with defaxiom, imagine an event such as ; (encapsulate () (local (defaxiom temp )) (defthm foo )). ; Such an event would leave us in an ACL2 logical world for which is ; stored under the name foo as through it were a logical consequence of the ; axioms in that logical world, which presumably it is not. Our solution is to ; disallow defaxiom events in the scope of LOCAL. This is a bit tricky since ; the LOCAL may not be lexically apparent, as when a defaxiom occurs inside a ; book that is locally included. We therefore track LOCAL by binding state ; global variable 'in-local-flg to t (see the #+acl2-loop-only definition of ; LOCAL). ; The "hidden defpkg" problem is discussed in the Essay on Hidden Packages and ; is briefly summarized in :doc topic hidden-death-package. The basic problem ; is that a defpkg event introduces axioms, yet it may be introduced ; temporarily through a local include-book. The problem is thus similar to the ; defaxiom problem discussed just above, and a solution would be to disallow ; defpkg events in the scope of LOCAL. But that solution would be harsh: For ; example, community book books/arithmetic/top.lisp defines packages and yet we ; would like to be able to include this book locally when proving arithmetic ; facts. Our solution is to store all packages, even such "hidden" packages, ; in a world global 'known-package-alist. We are careful to track such ; packages during the first pass (proof pass) of encapsulate and certify-book. ; In the case of certify-book, we write out such defpkg events to the ; portcullis of the certificate so that they are not hidden when executing a ; subsequent corresponding include-book. ; The Essay on Skip-proofs describes our handling of skip-proofs in some ; detail, but here is a summary. We want to claim correctness for a system of ; books that is validated using certify-book without any keyword parameters. ; We thus want to require a non-nil value of keyword parameter :skip-proofs-okp ; for any book that depends on a skip-proofs event, whether that dependency is ; in the book's certification world, is in the book itself, or is ; (hereditarily) in an included book. We thus maintain a world global ; 'skip-proofs-seen with value t whenever the world depends on a skip-proofs, ; as explained in the above essay. ; Certification worlds are checked for legality by ; chk-acceptable-certify-book1, which collects uncertified books (using ; collect-uncertified-books) from the existing include-book-alist, checks if ; any redefinition was done, and (if not doing the Pcertify or Convert step of ; provisional certification) checks that pcert-books is empty. We of course ; miss uncertified locally-included books this way, but the only relevance of ; such books is whether they employed skip-proofs, ttags, or defaxioms, and ; this information is ultimately stored in the certificate of a parent book ; that is non-locally included in the certification world. We track locally ; included provisionally certified books under encapsulates, but as with ; uncertified books, we are not concerned about any locally included ; provisionally certified book under a certified book. ; The acl2-defaults-table stores the default defun-mode, and hence can affect ; soundness. However, chk-acceptable-certify-book1 checks that the default ; defun mode is logic at certification time, and we take various measures to ; avoid other potential pitfalls (probably identifiable by tags-searches ; through the source code for acl2-defaults-table and for default-defun-mode). ; When additional, tricky soundness threats are identified, it would be good to ; describe them here, along with how we deal with them. ; End of Essay on Soundness Threats ; Essay on Skip-proofs ; The skip-proofs event allows a modular, top-down style of proof. Skip-proofs ; differs from defaxiom: skip-proofs is intended for use when proof obligations ; are believed to be theorems but it is convenient to defer their proofs, while ; defaxiom is to be used for extending the first-order theory. Therefore, ; while we disallow local defaxiom events (which really do not make sense; are ; we extending the theory or not?), it does make sense to allow local ; skip-proofs events. Indeed, if we were to disallow local skip-proofs events ; then we would be ruling out the top-down, modular style of proof outlined in ; Kaufmann's article in the case studies book. ; But we then must track skip-proofs events in support of our correctness ; story. Our claim is that when a certified book has an empty portcullis and ; all of :SKIPPED-PROOFSP, :AXIOMSP, and :TTAGS are NIL in its certificate, ; then it is sound to extend a history by including such a book without error. ; In Version_2.5 we did such tracking using world global include-book-alist. ; That tracking proved inadequate, however. Consider the following books "top" ; and "sub". ; ; book "top" ; (in-package "ACL2") ; (encapsulate ; () ; (local (include-book "sub")) ; (defthm bad nil ; :rule-classes nil)) ; ; book "sub" ; (in-package "ACL2") ; (skip-proofs ; (defthm bad nil ; :rule-classes nil)) ; In Version_2.5, if you certify these books in the initial logical world and ; then (include-book "top"), then you will not see a "Skip-proofs" warning when ; you do the include-book, because the value of :SKIPPED-PROOFSP in the ; cert-annotations of the certificate of "foo" is nil. ; Version_2.6 through Version_3.4 more carefully tracked include-books for the ; presence of supporting skip-proofs events, including skip-proofs that are ; local inside an encapsulate, using a state global, 'include-book-alist-state. ; When constructing a book's certificate, the value of ; 'include-book-alist-state was bound to nil initially and then updated by ; include-book, and its final value was used to create the post-alist of the ; certificate. (We do not have to worry about analogous handling of :AXIOMSP ; because defaxioms are never allowed in a local context.) ; But that approach entailed, at certification time, looking in certificates of ; already-included books for skip-proofs information. This was inefficient for ; very large certificates such as those found in the work at Centaur ; Technology. So starting after Version_3.4 we are adopting a different ; approach. We no longer have state global 'skipped-proofsp. Instead, we ; focus only on maintaining world global 'skip-proofs-seen, consulting ; 'ld-skip-proofsp when we call install-event. ; We maintain the invariant that skip-proofs-seen is a form evaluated with ; proofs skipped in support of the construction of the current ACL2 logical ; world, if such exists (otherwise skip-proofs-seen is nil). This "form" can ; be (:include-book full-book-name) if full-book-name logically supports the ; current ACL2 world (perhaps locally) and contains a skip-proofs form. When ; we install an event, we set world global 'skip-proofs-seen (if it is not ; already set) if the event is evaluated with a non-nil value of state global ; 'ld-skip-proofsp, unless we are inside an include-book or the second pass of ; an encapsulate. (Note that the certificate of a book already carries the ; information of whether skip-proofs was invoked during cerification, and we ; use that information when including a book.) We may also avoid setting ; 'skip-proofs-seen if the event has no logical content, for example, a ; deflabel event. However, we avoid updating 'skip-proofs-seen in the cases of ; encapsulate and include-book, since they manage this global themselves, as ; follows. Encapsulate checks the value of 'skip-proofs-seen after its first ; pass and installs that value at the end of its second pass. Include-book ; sets 'skip-proofs-seen based on its certificate (its so-called cert-obj), ; which provides skip-proofs information at the top level and also in its ; post-alist (which is set based on world global include-book-alist-all). Note ; that certify-book does not set skip-proofs-seen in the resulting world, but ; since certify-book is not a valid embedded event form for a certification ; world, that is not a problem. ; Up through Version_3.4, we updated world globals 'skip-proofs-seen and ; 'redef-seen in maybe-add-command-landmark intead of as indicated above (in ; particular, instead of using install-event). But with progn!, this is ; misguided -- these should be updated at the event level, not the command ; level -- as the following example shows. ; (progn! (set-ld-redefinition-action '(:doit . :overwrite) state) ; (defun foo (x) (cons x x)) ; (set-ld-redefinition-action nil state)) ; Of course, this isn't exactly a soundness bug, since one needs an active ; trust tag in order to evaluate progn!. Nevertheless, we would like to avoid ; such a simple way to prove nil whenever there is any active trust tag! ; Finally, we note a related problem with Version_2.5 that was fixed in ; Version_2.6. Suppose that foo.lisp and bar.lisp both have this unique ; form after (in-package "ACL2"): ; (defthm bad nil ; :rule-classes nil) ; Now suppose we do this in a fresh session: ; (encapsulate () ; (local (include-book "foo")) ; (defthm bad nil ; :rule-classes nil)) ; Then (certify-book "bar" 1) succeeded in Version_2.5, and in subsequent ; sessions, if we evaluated (include-book "bar"), that succeeded without ; warning or error. ; End of Essay on Skip-proofs #+acl2-loop-only (defmacro skip-proofs (x) ":Doc-Section Other skip proofs for a given form ~-[] a quick way to introduce unsoundness~/ ~bv[] Example Form: (skip-proofs (defun foo (x) (if (atom x) nil (cons (car x) (foo (reverse (cdr x))))))) General Form: (skip-proofs form) ~ev[] where ~c[form] is processed as usual except that the proof obligations usually generated are merely assumed. Normally ~c[form] is an event; ~pl[events]. If you want to put ~c[skip-proofs] around more than one event, consider the following (~pl[progn]): ~c[(skip-proofs (progn event1 event2 ... eventk))]. WARNING: ~c[Skip-proofs] allows inconsistent ~il[events] to be admitted to the logic. Use it at your own risk!~/ Sometimes in the development of a formal model or proof it is convenient to skip the proofs required by a given event. By embedding the event in a ~c[skip-proofs] form, you can avoid the proof burdens generated by the event, at the risk of introducing unsoundness. Below we list four illustrative situations in which you might find ~c[skip-proofs] useful. 1. The termination argument for a proposed function definition is complicated. You presume you could admit it, but are not sure that your definition has the desired properties. By embedding the ~ilc[defun] event in a ~c[skip-proofs] you can ``admit'' the function and experiment with theorems about it before undoing (~pl[ubt]) and then paying the price of its admission. Note however that you might still have to supply a measure. The set of formals used in some valid measure, known as the ``measured subset'' of the set of formals, is used by ACL2's induction heuristics and therefore needs to be suitably specified. You may wish to specify the special measure of ~c[(:? v1 ... vk)], where ~c[(v1 ... vk)] enumerates the measured subset. 2. You intend eventually to verify the ~il[guard]s for a definition but do not want to take the time now to pursue that. By embedding the ~ilc[verify-guards] event in a ~c[skip-proofs] you can get the system to behave as though the ~il[guard]s were verified. 3. You are repeatedly recertifying a book while making many experimental changes. A certain ~ilc[defthm] in the book takes a very long time to prove and you believe the proof is not affected by the changes you are making. By embedding the ~ilc[defthm] event in a ~c[skip-proofs] you allow the theorem to be assumed without proof during the experimental recertifications. 4. You are constructing a proof top-down and wish to defer the proof of a ~ilc[defthm] until you are convinced of its utility. You can embed the ~c[defthm] in a ~c[skip-proofs]. Of course, you may find later (when you attempt prove the theorem) that the proposed ~c[defthm] is not a theorem. Unsoundness or Lisp errors may result if the presumptions underlying a use of ~c[skip-proofs] are incorrect. Therefore, ~c[skip-proofs] must be considered a dangerous (though useful) tool in system development. Roughly speaking, a ~ilc[defthm] embedded in a ~c[skip-proofs] is essentially a ~ilc[defaxiom], except that it is not noted as an axiom for the purposes of functional instantiation (~pl[lemma-instance]). But a skipped ~ilc[defun] is much more subtle since not only is the definitional equation being assumed but so are formulas relating to termination and type. The situation is also difficult to characterize if the ~c[skip-proofs] ~il[events] are within the scope of an ~ilc[encapsulate] in which constrained functions are being introduced. In such contexts no clear logical story is maintained; in particular, constraints aren't properly tracked for definitions. A proof script involving ~c[skip-proofs] should be regarded as work-in-progress, not as a completed proof with some unproved assumptions. A ~c[skip-proofs] event represents a promise by the author to admit the given event without further axioms. In other words, ~c[skip-proofs] should only be used when the belief is that the proof obligations are indeed theorems in the existing ACL2 logical ~il[world]. ACL2 allows the certification of ~il[books] containing ~c[skip-proofs] ~il[events] by providing the keyword argument ~c[:skip-proofs-okp t] to the ~ilc[certify-book] command. This is contrary to the spirit of certified ~il[books], since one is supposedly assured by a valid ~il[certificate] that a book has been ``blessed.'' But certification, too, takes the view of ~c[skip-proofs] as ``work-in-progress'' and so allows the author of the book to promise to finish. When such ~il[books] are certified, a warning to the author is printed, reminding him or her of the incurred obligation. When ~il[books] containing ~c[skip-proofs] are included into a session, a warning to the user is printed, reminding the user that the book is in fact incomplete and possibly inconsistent. This warning is in fact an error if ~c[:skip-proofs-okp] is ~c[nil] in the ~ilc[include-book] form; ~pl[include-book]. We conclude with a technical note. ~c[Skip-proofs] works by binding the ~ilc[ld] special ~ilc[ld-skip-proofsp] to ~c[t] unless it is already bound to a non-~c[nil] value; ~pl[ld-skip-proofsp].~/" `(state-global-let* ((ld-skip-proofsp (or (f-get-global 'ld-skip-proofsp state) t)) (inside-skip-proofs ; See the comment inside install-event for a discussion of the use of this ; binding. t)) ,x)) #+acl2-loop-only (defmacro local (x) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Keep this in sync with chk-embedded-event-form: if we skip the check on x ; there, we should skip evaluation of x here. ":Doc-Section Events hiding an event in an encapsulation or book~/ ~bv[] Examples: (local (defthm hack1 (implies (and (acl2-numberp x) (acl2-numberp y) (equal (* x y) 1)) (equal y (/ x))))) (local (defun double-naturals-induction (a b) (cond ((and (integerp a) (integerp b) (< 0 a) (< 0 b)) (double-naturals-induction (1- a) (1- b))) (t (list a b)))))~/ General Form: (local ev) ~ev[] where ~c[ev] is an event form. If the current default ~il[defun-mode] (~pl[default-defun-mode]) is ~c[:]~ilc[logic] and ~ilc[ld-skip-proofsp] is ~c[nil] or ~c[t], then ~c[(local ev)] is equivalent to ~c[ev]. But if the current default ~il[defun-mode] is ~c[:]~ilc[program] or if ~ilc[ld-skip-proofsp] is ~c[']~ilc[include-book], then ~c[(local ev)] is a ~c[no-op]. Thus, if such forms are in the event list of an ~ilc[encapsulate] event or in a book, they are processed when the encapsulation or book is checked for admissibility in ~c[:]~ilc[logic] mode but are skipped when extending the host ~il[world]. Such ~il[events] are thus considered ``local'' to the verification of the encapsulation or book. The non-local ~il[events] are the ones ``exported'' by the encapsulation or book. ~l[encapsulate] for a thorough discussion. Also ~pl[local-incompatibility] for a discussion of a commonly encountered problem with such event hiding: you can't make an event local if its presence is required to make sense of a non-local one. Note that ~il[events] that change the default ~il[defun-mode], and in fact any ~il[events] that set the ~ilc[acl2-defaults-table], are disallowed inside the scope of ~c[local]. ~l[embedded-event-form]." ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'if '(equal (ld-skip-proofsp state) 'include-book) '(mv nil nil state) (list 'if '(equal (ld-skip-proofsp state) 'initialize-acl2) '(mv nil nil state) (list 'state-global-let* '((in-local-flg t)) (list 'when-logic "LOCAL" x))))) #+acl2-loop-only (defmacro defchoose (&whole event-form &rest def) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events define a Skolem (witnessing) function~/ ~bv[] Examples: (defchoose choose-x-for-p1-and-p2 (x) (y z) (and (p1 x y z) (p2 x y z))) (defchoose choose-x-for-p1-and-p2 x (y z) ; equivalent to the above (and (p1 x y z) (p2 x y z))) ; The following is as above, but strengthens the axiom added to pick a sort ; of canonical witness, as described below. (defchoose choose-x-for-p1-and-p2 x (y z) (and (p1 x y z) (p2 x y z)) :strengthen t) (defchoose choose-x-and-y-for-p1-and-p2 (x y) (z) (and (p1 x y z) (p2 x y z)))~/ General Form: (defchoose fn (bound-var1 ... bound-varn) (free-var1 ... free-vark) body :doc doc-string :strengthen b), ~ev[] where ~c[fn] is the symbol you wish to define and is a new symbolic name (~pl[name]), ~c[(bound-var1 ... bound-varn)] is a list of distinct `bound' variables (see below), ~c[(free-var1 ... free-vark)] is the list of formal parameters of ~c[fn] and is disjoint from the bound variables, and ~c[body] is a term. The use of ~c[lambda-list] keywords (such as ~c[&optional]) is not allowed. The ~il[documentation] string argument, ~c[:doc doc-string], is optional; for a description of the form of ~c[doc-string] ~pl[doc-string]. The ~c[:strengthen] keyword argument is optional; if supplied, it must be ~c[t] or ~c[nil]. The system treats ~c[fn] very much as though it were declared in the ~il[signature] of an ~ilc[encapsulate] event, with a single axiom exported as described below. If you supply a ~c[:use] hint (~pl[hints]), ~c[:use fn], it will refer to that axiom. No rule (of class ~c[:]~ilc[rewrite] or otherwise; ~pl[rule-classes]) is created for ~c[fn]. ~c[Defchoose] is only executed in ~il[defun-mode] ~c[:]~ilc[logic]; ~pl[defun-mode]. Also ~pl[defun-sk]. In the most common case, where there is only one bound variable, it is permissible to omit the enclosing parentheses on that variable. The effect is the same whether or not those parentheses are omitted. We describe this case first, where there is only one bound variable, and then address the other case. Both cases are discussed assuming ~c[:strengthen] is ~c[nil], which is the default. We deal with the case ~c[:strengthen t] at the end. The effect of the form ~bv[] (defchoose fn bound-var (free-var1 ... free-vark) body) ~ev[] is to introduce a new function symbol, ~c[fn], with formal parameters ~c[(free-var1 ... free-vark)]. Now consider the following axiom, which states that ~c[fn] picks a value of ~c[bound-var] so that the body will be true, if such a value exists: ~bv[] (1) (implies body (let ((bound-var (fn free-var1 ... free-vark))) body)) ~ev[] This axiom is ``clearly conservative'' under the conditions expressed above: the function ~c[fn] simply picks out a ``witnessing'' value of ~c[bound-var] if there is one. For a rigorous statement and proof of this conservativity claim, ~pl[conservativity-of-defchoose]. Next consider the case that there is more than one bound variable, i.e., there is more than one bound-var in the following. ~bv[] (defchoose fn (bound-var1 ... bound-varn) (free-var1 ... free-vark) body) ~ev[] Then ~c[fn] returns a multiple value with ~c[n] components, and formula (1) above is expressed using ~ilc[mv-let] as follows: ~bv[] (implies body (mv-let (bound-var1 ... bound-varn) (fn free-var1 ... free-vark) body)) ~ev[] We now discuss the case that ~c[:strengthen t] is supplied. For simplicity we return to our simplest case, with ~c[defchoose] applied to function ~c[fn], a single free variable ~c[y], and a single bound variable ~c[bound-var]. The idea is that if we pick the ``smallest'' witnessing ~c[bound-var] for two different free variables ~c[y] and ~c[y1], then either those two witnesses are the same, or else one is less than the other, in which case the smaller one is a witness for its free variable but not for the other. (See comments in source function ~c[defchoose-constraint-extra] for more details.) Below, ~c[body1] is the result of replacing ~c[y] by ~c[y1] in ~c[body]. ~bv[] (2) (or (equal (fn y) (fn y1)) (let ((bound-var (fn y))) (and body (not body1))) (let ((bound-var (fn y1))) (and body1 (not body)))) ~ev[] An important application of this additional axiom is to be able to define a ``fixing'' function that picks a canonical representative of each equivalence class, for a given equivalence relation. The following events illustrate this point. ~bv[] (encapsulate ((equiv (x y) t)) (local (defun equiv (x y) (equal x y))) (defequiv equiv)) (defchoose efix (x) (y) (equiv x y) :strengthen t) (defthm equiv-implies-equal-efix-1 (implies (equiv y y1) (equal (efix y) (efix y1))) :hints ((\"Goal\" :use efix)) :rule-classes (:congruence)) (defthm efix-fixes (equiv (efix x) x) :hints ((\"Goal\" :use ((:instance efix (y x)))))) ~ev[] If there is more than one bound variable, then (2) is modified in complete analogy to (1) to use ~ilc[mv-let] in place of ~ilc[let]. Comment for logicians: As we point out in the documentation for ~ilc[defun-sk], ~c[defchoose] is ``appropriate,'' by which we mean that it is conservative, even in the presence of ~c[epsilon-0] induction. For a proof, ~l[conservativity-of-defchoose].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defchoose-fn (list 'quote def) 'state (list 'quote event-form))) (deflabel conservativity-of-defchoose :doc ":Doc-Section defchoose proof of conservativity of ~ilc[defchoose]~/ This documentation topic provides underlying theory. It is of theoretical interest only; it has no relationship to the effective use of ACL2.~/ The argument below for the conservativity of ~il[defchoose] replaces the terse and somewhat misleading reference to a forcing argument in Appendix B of the paper by ACL2 authors Kaufmann and Moore, ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203). Our basic idea is to to take a (countable) first-order structure for ACL2, M, together with a function symbol, f, introduced by ~il[defchoose], and find a way to expand M with an interpretation of f (without changing the universe of M) so that e0-induction continues to hold in the expansion. A remark at the end of this documentation topic shows why care is necessary. A concept called ``forcing'', originally introduced by Paul Cohen for set theory, has long since been adapted by logicians (in a simplified form) to model theory. This simplified model-theoretic forcing provides the means for making our careful expansion. The forcing argument presented below is intended to be completely self-contained for those familiar with basic first-order logic and ACL2. No background in forcing (model-theoretic or otherwise) is expected, though we do expect a rudimentary background in first-order logic and familiarity with the following. Preliminaries. We write s[p<-p0] to denote the result of extending or modifying the assignment s by binding p to p0. Now let A be a subset of the universe U of a first-order structure M. A is said to be ``first-order definable with parameters'' in M if for some formula phi, variable x, and assignment s binding the free variables of phi except perhaps for x, A = {a \\in U: M |= phi[s[x<-a]]. Note that we are writing ``\\in'' to denote set membership. Finally, we indicate the end of a proof (or of a theorem statement, when the proof is omitted) with the symbol ``-|''. We gratefully acknowledge very helpful feedback from John Cowles, who found several errors in a draft of this note and suggested the exercises. We also thank Ruben Gamboa for helpful feedback, and we thank Jim Schmerl for an observation that led us directly to this proof in the first place. We are given a consistent first-order theory T, extending the ACL2 ground-zero theory, that satisfies the e0-induction scheme. We wish to show that the extension of T by the following arbitrary defchoose event is conservative, where g is a new function symbol. ~bv[] (defchoose g ) ~ev[] Note that by ``the extension of T'' here we mean the extension of T by not only the new defchoose axiom displayed just below, but also the addition of e0-induction axioms for formulas in the language with the new defchoose function symbol, g. ~bv[] -> (LET = g() in ) ~ev[] By definition of conservativity, since proofs are finite, it clearly suffices to consider an arbitrary finite subset of T. Then by the completeness, soundness, and downward Lowenheim-Skolem theorems of first-order logic, it suffices to show that an arbitrary countable model of T can be expanded (i.e., by interpreting the new symbol g without changing the universe of the model) to a model of the corresponding defchoose axiom above, in which all e0-induction axioms hold in the language of that model. Below, we will carry out a so-called ~em[forcing] construction, which allows us to expand any countable model M of T to a model M[G] that satisfies e0-induction and also satisfies the above axiom generated from the above defchoose event. The ideas in this argument are standard in model theory; no novelty is claimed here. Fix a countable model M of a theory T that satisfies e0-induction and extends the ACL2 ground-zero theory. Also fix the above defchoose axiom, where g is not in the language of T. We start by defining a partial order P as follows. Let Nb and Nf be the lengths of and , respectively. P consists of all fn in M such that the following formula is true in M. Roughly speaking, it says that fn is a finite function witnessing the above requirement for g. ~bv[] alistp(fn) & no-duplicatesp-equal(strip-cars(fn)) & (forall , . (member-equal(cons(,), fn) -> (length() = Nb & length() = Nf & ((exists . ) -> )))) ~ev[] P is ordered by subset, i.e., we say that p2 ~em[extends] p1 if p1 is a subset (not necessarily proper) of p2 (more precisely, M |= subsetp-equal(p1,p2)). Remark. The original argument in Appendix B of the aforementioned paper can essentially be salvaged, as we now show. The key observation is that the particular choice of P is nearly irrelevant for the argument that follows below. In particular, we can instead define P to consist of finite one-one functions with domain contained in the set of natural numbers. More precisely, consider the following definitions. ~bv[] (defun function-p (fn) (declare (xargs :guard t)) (and (alistp fn) (no-duplicatesp-equal (strip-cars fn)))) (defun nat-listp (l) (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (natp (car l)) (nat-listp (cdr l)))))) (defun nat-function-p (x) (and (function-p x) (nat-listp (strip-cars x)))) ~ev[] and define inverse as follows. ~bv[] (defun inverse (fn) (declare (xargs :guard (alistp fn))) (if (endp fn) nil (cons (cons (cdar fn) (caar fn)) (inverse (cdr fn))))) ~ev[] Then P may instead be defined to consist of those fn for which nat-function-p(fn) & function-p(inverse(fn)). With this alternate definition of P, the argument below then goes through virtually unchanged, and we get an expansion M[G] of M in which there is a definable enumeration of the universe. The conservativity of defchoose then follows easily because the function being introduced can be defined explicitly using that enumeration (namely, always pick the least witness in the sense of the enumeration). End of Remark. Next we present the relevant forcing concepts from model theory. A ~em[dense] subset of P is a subset D of P such that for every p \\in P, there is d \\in D such that d extends p. A subset G of P is ~em[generic] with respect to a collection Ds of dense subsets of P, also written ``G is Ds-generic,'' if G is closed under subset (if p2 \\in G and p2 extends p1 then p1 \\in G), G is pairwise compatible (the union-equal of any two elements of G is in G), and every set in Ds has non-empty intersection with G. For p \\in P, we say that a subset D of P is ~em[dense beyond] p if for all p1 extending p there exists p2 extending p1 such that p2 \\in D. This notion makes sense even for D not a subset of P if we treat elements of D not in P as nil. Proposition 1. For any partial order P and countable collection Ds of dense subsets of P, there is a Ds-generic subset of P. Proof. Let Ds = {D0,D1,D2,...}. Define a sequence such that for all i, p_i \\in Di and p_(i+1) extends p_i. Let G = {p \\in P: for some i, pi extends p}. Then G is Ds-generic. -| Note that P is first-order definable (with parameters) in M. Let Df be the set of dense subsets of P that are first-order definable (with parameters) in M. A standard argument shows there are only countably many first-order definitions with parameters in a countable model M ~-[] for example, we can Goedel number all terms and then all formulas ~-[] hence, Df is countable. By Proposition 1, let G be Df-generic. Notice that for any list x of length Nb in M, the set of elements f of P for which x is in the domain of f is dense and first-order definable. We may thus define a function g0 as follows: g0(x_1,...,x_Nb) = y if there is some element of G containing the pair ((x_1 ... x_Nb) . y). It is easy to see that g0 is a total function on M. Let L be the language of T and let L[g] be the union of L with a set containing a single new function symbol, g. Let M[G] be the expansion of M to L[g] obtained by interpreting g to be g0 (see also Proposition 5 below). So now we have fixed M, P, Df, G, and g0, where G is Df-generic. Proposition 2. Let Df be the set of dense subsets of P that are first-order definable (with parameters) in M. Suppose that p \\in G and D \\in Df. Then for some q \\in G extending p, q \\in D. Proof. Let D0 be the set of p' \\in D that either extend p or have no extension in D that extends p. We leave it as a straightforward exercise to show that D0 is dense, and D0 is clearly first-order definable (with parameters) in M. So by genericity of G, we may pick q \\in D0 such that q \\in G. Thus q \\in D. By definition of generic, some extension q1 of both p and q belongs to G. Pick q2 \\in D extending q1; thus q has an extension in D that extends p (namely, q2), so by definition of D0, q extends p. -| Definition of forcing. Let phi(x1,...,xk) be a first-order formula in L[g] and let p \\in P. We define a formula of L, denoted ``p ||- phi'' (``p forces phi''), by recursion on phi (in the metatheory) as follows. (Here, we view ``or'' and ``forall'' as abbreviations.) ~bq[] If phi is atomic, then let phi'(A) be the result of replacing, inside-out, each subterm of the form g(x_1,...,x_Nb) with the term (cdr (assoc-equal (list x_1 ... x_Nb) A)), where A is neither p nor a variable occurring in phi. Then p ||- phi is defined as follows: ``The set {A \\in P: A extends p and phi'(A)} is dense beyond p''. That is, p ||- phi is the following formula: ~bv[] (forall p1 \\in P extending p) (exists p2 \\in P extending p1) phi'(p2). ~ev[] p ||- ~~phi is: (forall p' \\in P extending p) ~~(p' ||- phi) p ||- phi_1 & phi_2 is: (p ||- phi_1) & (p ||- phi_2) p ||- (exists x) phi is: (exists x) (p ||- phi) ~eq[] We will need the following definition later. Definition. p ||-w phi (p ~em[weakly forces] phi) is an abbreviation for p ||- ~~~~phi. The following exercises were suggested by John Cowles as a means for gaining familiarity with the definition of forcing. Exercise 1. Consider the formula (phi_1 OR phi_2) as an abbreviation for ~~(~~phi_1 & ~~phi_2), Show that p ||- (phi_1 OR phi_2) is equivalent to the following. ~bv[] (forall p' \\in P extending p) (exists p'' \\in P extending p') ((p'' ||- phi_1) OR (p'' ||- phi_2)) ~ev[] Exercise 2. Consider the formula (forall x)phi as an abbreviation for ~~(exists x)~~phi, Show that p ||- (forall x)phi is equivalent to the following. ~bv[] (forall x) (forall p1 \\in P extending p) (exists p2 \\in P extending p1) (p2 ||- phi). ~ev[] Exercise 3. Prove that p ||-w phi is equivalent to the following. ~bv[] (forall p' \\in P extending p) (exists p'' \\in P extending p') (p'' ||- phi). ~ev[] Exercise 4. Let phi be a formula of L[g]. Prove: M |= (p ||- phi)[s[p<-p0]] implies M |= (p ||-w phi)[s[p<-p0]]. Exercise 5. Let phi be a formula of L[g]. Prove: M |= (p ||- ~~phi)[s[p<-p0]] iff M |= (p ||-w ~~phi)[s[p<-p0]]. [End of exercises.] The definition of forcing stipulates how to view ``p ||- phi(x1,...,xk)'' as a new formula theta(p,x1,...,xk). That is, ``||-'' transforms formulas, so for any first-order formula phi, ``p ||- phi'' is just another first-order formula. That observation shows that a formula such as ((p ||- phi) OR (p ||- ~~phi)) is really just another first-order formula. The following proposition thus follows easily. Proposition 3. For any formula phi of L[g], {p0: M |= ((p ||- phi) OR (p ||- ~~phi))[s[p<-p0]]]} is a dense subset of P, which (since it is first-order definable with parameters in M) intersects G. -| The following proposition is easily proved by a structural induction on phi, and is left to the reader. Proposition 4. Let phi be a formula of L[g]. Suppose ~c[p0 \in P], ~c[p1 \in P],~nl[] M |= (p ||- phi)[s[p<-p0]] and p1 extends p0. Then~nl[] M |= (p ||- phi)[s[p<-p1]]. -| We will also need the following. Proposition 5. The following is dense for any finite set S of Nb-tuples: {p \\in P: for some \\in S, (list x_1 ... x_Nb) \\in strip-cars(p)}. Thus, the function g0 is a total function. -| The next lemma tells us that the sentences true in M[G] are those that are forced by an element of G. Truth Lemma. Let phi be a formula in L[g], let s be an assignment to the free variables of phi, and let p be a variable not in the domain of s. Then M[G] |= phi[s] iff for some p0 \\in G, M |= (p ||- phi)[s[p<-p0]]. Proof. The proof is by induction on the structure of phi. First suppose phi is atomic. Let D* be the set of elements p0 \\in P such that every assoc-equal evaluation from the definition of forcing phi returns a pair when A is bound to p0. (Intuitively, this means that p0 is a sufficiently large approximation from any G containing p0 to make sense of phi in M[G].) We make the following claim. ~bv[] (*) For all p0 \\in G such that p0 \\in D*, M[G] |= phi[s] iff M |= (p ||- phi)[s[p<-p0]]. ~ev[] To prove the claim, fix p0 in both G and D*, and recall the function g0 constructed from G in the definition of M[G]. Suppose that t_1, ..., t_Nb are terms and g(t_1, ..., t_Nb) is a subterm of phi. Then s assigns a value in M to each of the t_i. Let a_i be the value assigned by s to t_i. Then g0(a_1, ..., a_Nb) = (cdr (assoc-equal (list a_1 ... a_Nb) p0)), as the assoc-equal is a pair (since p0 \\in D*) and has the indicated value (because p0 \\in G). It follows by the definition of formula phi' in the definition of forcing: ~bv[] M[G] |= phi[s] iff M |= phi'(p)[s[p<-p0]] ~ev[] Moreover, because p0 \\in D* it is clear that this holds if p0 is replaced by an arbitrary extension of p0. Then (*) easily follows. By Proposition 5, D* is dense, so there is some p0 in the intersection of D* and G. The forward direction of the conclusion then follows by (*). The reverse direction is clear from (*) by application of Proposition 2 to D* and Proposition 4. Next, suppose M[G] |= ~~phi[x]. Then it is not the case that M[G] |= phi, so by the inductive hypothesis, there is no p0 \\in G for which M |= (p ||- phi)[s[p<-p0]]. By Proposition 3, there is p0 \\in G for which M |= (p ||- ~~phi)[s[p<-p0]]. For the other direction, suppose it is not the case that M[G] |= ~~phi[s]. So M[G] |= phi[s], and by the inductive hypothesis, there is p0 \\in G for which M |= (p ||- phi)[s[p<-p0]]. It follows that there is no p1 \\in G for which M |= (p ||- ~~phi)[s[p<-p1]], since from such p1 we can find a common extension p2 of p0 and p1 (since G is generic), and since p2 extends p0 then by Proposition 4, M |= (p ||- phi)[s[p<-p2]], contradicting (by definition of forcing) M |= (p ||- ~~phi)[s[p<-p1]] since p2 extends p1. The case (phi_1 & phi_2) follows easily from the inductive hypothesis. For the forward direction, apply Proposition 4 and the observation that by genericity, if p0 \\in G and p1 \\in G then p0 and p1 they have a common extension in G. Finally, the case (exists x) phi follows trivially from the inductive hypothesis. -| Truth Lemma Corollary. The Truth Lemma holds with ||-w replacing ||-. Proof. This is clear by applying the Truth Lemma to ~~~~phi. -| Here is our main theorem. Recall that all first-order theories in our ACL2 context satisfy the e0-induction scheme. Theorem. M[G] satisfies e0-induction. Proof. We consider an arbitrary instance of e0-induction in L[g], stated using a strict well-founded relation <| and a formula phi. We write phi(y) to indicate that y may be among the free variables of phi, and phi(y<-x) to denote the result of substituting x for y in phi. ~bv[] theta(y): (forall y) [((forall x <| y) phi(y<-x)) -> phi(y)] -> (forall y) phi(y) ~ev[] Our goal is to prove that theta holds in M[G]. Below, we abuse notation by leaving assignments implicit and by writing ``p ||- phi(y0)'' to signify that the formula (p ||- phi(y)) is true in M under the extension of the explicit assignment that binds y to y0. We believe that the intended meaning will be clear. Consider the following set D. ~bv[] D = {p \\in P: either p ||-w phi(y0) for all y0, or else for some y0, p ||- ~~phi(y0) and for all y1 <| y0 p ||-w phi(y1)}. ~ev[] The set D is clearly first-order definable (with parameters) in M. We claim that D is a dense subset of P. For suppose p0 \\in P; we find p1 \\in D extending p0, as follows. If p0 ||-w phi(y0) for all y0, then we may take p1 to be p0. Otherwise, by definition of ||-w and ||-, there is some y0 such that for some extension p0' of p0, p0' ||- ~~phi(y0). Pick a <|-minimal such y0, and correspondingly pick p1 so that p1 extends p0 and p1 ||- ~~phi(y0). In order to show that p1 \\in D, it remains to show that for all y1 <| y0, p1 ||-w phi(y1), i.e., there is no q extending p1 such that q ||- ~~phi(y1). This is indeed the case since otherwise q and y1 would contradict the <|-minimality of y0. Applying the genericity of G and just-proved density of D, pick p0 \\in G such that p0 \\in D. If p0 ||-w phi(y0) for all y0, then by the Truth Lemma Corollary, M[G] |= phi(y0) for all y0, and thus M[G] |= theta. Otherwise, since p0 \\in D we may choose y0 such that p0 ||- ~~phi(y0) and for all y1 <| y0, p0 ||-w phi(y1). By the Truth Lemma and its corollary, since p0 \\in G we have: ~bv[] (1) M[G] |= ~~phi(y0). (2) For all y1 <| y0, M[G] |= phi(y1). ~ev[] It follows that the antecedent of theta is false in M[G], as witnessed by y = y0; thus M[G] |= theta. -| Remark. We close by returning, as promised above, to the question of why so much care is necessary in constructing an expansion of M. We assume familiarity here with the notion of a ``non-standard'' natural number of M, i.e., one that is greater than the interpretation of any term that has the form (+ 1 1 1 ... 1). Here is a very simple example that illustrates the need for some care. Consider the following event, which introduces a function foo with the following property: for all x, if natp(x) then natp(foo(x)). ~bv[] (defchoose foo (y) (x) (implies (natp x) (natp y))) ~ev[] Certainly we can build a model of the above property from a model M of the ground-zero theory, by interpreting foo so that for all x for which M satisfies natp(x), foo(x) is also a natp in M. But suppose we start with a non-standard model M of the ground-zero theory, and we happen to define foo(x) to be 1 for all non-standard natural numbers x and 0 for all other x. The resulting expansion of M will not satisfy the e0-induction scheme or even the ordinary natural number induction scheme: foo(0)=0 holds in that expansion as does the implication foo(n)=0 => foo(n+1)=0 for every natural number n of M, standard or not; and yet foo(k)=0 fails for every non-standard natural number k of M.") #+acl2-loop-only (defmacro defattach (&whole event-form &rest args) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; See the Essay on Defattach. ; Developer note. A substantial test suite is stored at this UT CS file: ; /projects/acl2/devel-misc/books-devel/examples/defattach/test.lisp ":Doc-Section Events execute constrained functions using corresponding attached functions~/ This ~il[documentation] topic is organized into the following sections: ~st[Introductory example.]~nl[] ~st[Syntax and semantics of defattach.]~nl[] ~st[Three primary uses of defattach.]~nl[] ~st[Miscellaneous remarks, with discussion of possible user errors.] Please ~pl[encapsulate] if you intend to use ~c[defattach] but are not already familiar with the use of ~c[encapsulate] to introduce constrained functions. See community book ~c[books/misc/defattach-example.lisp] for a small example. it illustrates how ~c[defattach] may be used to build something like ``higher-order'' programs, in which constrained functions may be refined to different executable functions. More uses of ~c[defattach] may be found in the ACL2 source code, specifically, file ~c[boot-strap-pass-2.lisp]. The argument ~c[:skip-checks t] enables easy experimentation with ~c[defattach], by permitting use of ~c[:]~ilc[program] mode functions and the skipping of semantic checks. Also permitted is ~c[:skip-checks nil] (the default) and ~c[:skip-checks :cycles], which turns off only the update of the extended ancestor relation (see below) and hence the check for cycles in this relation; see below. We do not make any logical claims when the value of ~c[:skip-checks] is non-~c[nil]; indeed, a trust tag is required in this case (~pl[defttag]). Remark for those who use the experimental HONS extension (~pl[hons-and-memoization]): the interaction of memoization and attachments is not tracked for attachments introduced with a non-~c[nil] value of ~c[:skip-checks]. For more discussion of ~c[:skip-checks t], ~pl[defproxy]; we do not discuss ~c[:skip-checks] further, here. ~st[Introductory example.] We begin with a short log illustrating the use of ~c[defattach]. Notice that after evaluating the event ~c[(defattach f g)], a call of the constrained function ~c[f] is evaluated by instead calling ~c[g] on the arguments. ~bv[] ACL2 !>(encapsulate ((f (x) t :guard (true-listp x))) (local (defun f (x) x)) (defthm f-property (implies (consp x) (consp (f x))))) [... output omitted ...] T ACL2 !>(defun g (x) (declare (xargs :guard (or (consp x) (null x)))) (cons 17 (car x))) [... output omitted ...] G ACL2 !>(f '(3 4)) ; undefined function error ACL2 Error in TOP-LEVEL: ACL2 cannot ev the call of undefined function F on argument list: ((3 4)) To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !>(defattach f g) [... output omitted ...] :ATTACHMENTS-RECORDED ACL2 !>(f '(3 4)) ; f is evaluated using g (17 . 3) ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f '(3 4)) ; f is evaluated using g 1> (ACL2_*1*_ACL2::F (3 4)) 2> (ACL2_*1*_ACL2::G (3 4)) 3> (G (3 4)) <3 (G (17 . 3)) <2 (ACL2_*1*_ACL2::G (17 . 3)) <1 (ACL2_*1*_ACL2::F (17 . 3)) (17 . 3) ACL2 !>(defattach f nil) ; unattach f (remove its attachment) [... output omitted ...] :ATTACHMENTS-RECORDED ACL2 !>(f '(3 4)) ; undefined function error once again 1> (ACL2_*1*_ACL2::F (3 4)) ACL2 Error in TOP-LEVEL: ACL2 cannot ev the call of undefined function F on argument list: ((3 4)) To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !> ~ev[] ~st[Syntax and semantics of defattach.] The log above shows that the event ~c[(defattach f g)] allows ~c[g] to be used for evaluating calls of ~c[f]. From a logical perspective, the evaluation takes place in the addition to the current session of an ``attachment equation'' axiom (universally quantified over all ~c[x]) for each ~c[defattach] event: ~bv[] (equal (f x) (g x)) ;;; attachment equation axiom for (defattach f g) ~ev[] Below we explain ~c[defattach] in some detail. But it is important to keep in mind that evaluation with the attachment equations takes place in an extension of the logical theory of the session. ACL2 guarantees that this so-called ``evaluation theory'' remains consistent, assuming the absence of ~ilc[defaxiom] ~il[events] from the user. This guarantee is a consequence of a more general guarantee: an ACL2 logical ~il[world] exists in which (loosely speaking) the attachment equation for ~c[(defattach f g)], as ~c[(defun f (...) (g ...))], takes the place of the original defining event for ~c[f], for each ~c[defattach] event. This more general guarantee holds even if there are ~ilc[defaxiom] events, though as explained below, no function symbol that syntactically supports a ~c[defaxiom] formula is allowed to get an attachment. A deeper discussion of the logical issues is available (but not intended to be read by most users) in a long comment in the ACL2 source code labeled ``Essay on Defattach.'' ~bv[] Example Forms: (defattach f g) ; call g in place of calling constrained function f (defattach (f g)) ; same as just above (defattach (f g :hints ((\"Goal\" :in-theory (enable foo))))) ; equivalent to first form above, except with hints for the ; proof that the guard of f implies the guard of g (defattach (f g :hints ((\"Goal\" :in-theory (enable foo))) :otf-flg t)) ; as above, except with an :otf-flg of t for the proof that ; the guard of f implies the guard of g (defattach (f g) :hints ((\"Goal\" :use my-thm))) ; equivalent to first form above, except with hints for the ; proof that the constraints on f hold for g (defattach (f g) :hints ((\"Goal\" :use my-thm)) :otf-flg t) ; as above, except with an :otf-flg of t for the proof that ; the constraints on f hold for g (defattach (f g) (h j)) ; Attach g to f and attach j to h (defattach (f g :attach nil) (h j)) ; Same as just above, including the same proof obligations, ; except for one difference: because of :attach nil, calls ; of f will not be evaluated, i.e., there will be no ; executable attachment of g to f (defattach (f nil) (h j)) ; Attach j to h and unattach f (defattach (f g :hints ((\"Goal\" :in-theory (enable foo)))) (h j :hints ((\"Goal\" :in-theory (enable bar)))) :hints ((\"Goal\" :use my-thm))) ; Attach g to f and attach j to h, with hints: ; - For proving that the guard of f implies the guard of g, ; enable foo; ; - For proving that the guard of h implies the guard of j, ; enable bar; and ; - For proving that the constraints on f and h hold for ; g and j (respectively), use theorem my-thm. (defattach f nil) ; remove the attachment of f, if any (e.g., g above) (defattach (f nil)) ; same as just above~/ General Forms: (defattach f g) ; single attach or, if g is nil, unattach (defattach (f1 g1 :kwd val ...) ... (fk gk :kwd' val' ...) :kwd'' val'' ...) ~ev[] where each indicated keyword-value pair is optional and each keyword is one of ~c[:ATTACH], ~c[:HINTS], ~c[:OTF-FLG], or ~c[:INSTRUCTIONS]. The value of each ~c[:ATTACH] keyword is either ~c[t] or ~c[nil], with default ~c[t] except that the value of ~c[:ATTACH] at the ``top level,'' after each entry ~c[(fi gi ...)], is the default for each ~c[:ATTACH] keyword supplied in such an entry. We discuss the ~c[:ATTACH] keyword later in this ~il[documentation] topic. The associated values for the other keywords have the usual meanings for the proof obligations described below: the guard proof obligation for keywords within each ~c[(fi gi ...)] entry, and the constraint proof obligation for keywords at the top level. No keyword may occur twice in the same context, i.e., within the same ~c[(fi gi ...)] entry or at the top level; and ~c[:INSTRUCTIONS] may not occur in the same context with ~c[:HINTS] or ~c[:OTF-FLG]. The first General Form above is simply an abbreviation for the form ~c[(defattach (f g))], which is an instance of the second General Form above. For the second General Form we say that ~c[gi] is ``attached to'' ~c[fi] (by the ~c[defattach] event) if ~c[gi] is not ~c[nil], and otherwise we say that ~c[fi] is ``unattached'' (by the ~c[defattach] event). It is also convenient to refer to ~c[] as an ``attachment pair'' (of the event) if ~c[gi] is not ~c[nil]. We may refer to the set of ~c[fi] as the ``attachment nest'' of each ~c[fi]. We start with a brief introduction to the first General Form in the case that ~c[g] is not ~c[nil]. This form arranges that during evaluation, with exceptions noted below, every call of the constrained function symbol ~c[f] will in essence be replaced by a call of the function symbol ~c[g] on the same arguments. We may then refer to ~c[g] as the ``attachment of'' ~c[f], or say that ``~c[g] is attached to ~c[f].'' Notable exceptions, where we do not use attachments during evaluation, are for macroexpansion, evaluation of ~ilc[defconst] and ~ilc[defpkg] terms, evaluation during ~ilc[table] events, some ~il[stobj] operations including all ~il[stobj updates], and especially evaluation of ground terms (terms without free variables) during proofs. However, even for these cases we allow the use of attachments in the first argument of ~ilc[prog2$] and, more generally, the next-to-last (i.e., second) argument of ~ilc[return-last] when its first argument is not of the form ~c['m] for some macro, ~c[m]. To see why attachments are disallowed during evaluation of ground terms during proofs (except for the ~ilc[prog2$] and ~ilc[return-last] cases mentioned above), consider the following example. ~bv[] (defstub f (x) t) (defun g (x) (+ 3 x)) (defattach f g) ~ev[] If the form ~c[(f 2)] is submitted at the ACL2 prompt, the result will be ~c[5] because the attachment ~c[g] of ~c[f] is called on the argument, ~c[2]. However, during a proof the term ~c[(f 2)] will not be simplified to ~c[5], since that would be unsound, as there are no axioms about ~c[f] that would justify such a simplification. For the case that ~c[g] is ~c[nil] in the first General Form above, the result is the removal of the existing attachment to ~c[f], if any. After this removal, calls of ~c[f] will once again cause errors saying that ``ACL2 cannot ev the call of undefined function ~c[f] ...''. In this case not only is the previous attachment to ~c[f] removed; moreover, for every function symbol ~c[f'] in the attachment nest of ~c[f] in the ~c[defattach] event that introduced the existing attachment to ~c[f], then ~c[f'] is unattached. (An example near the end of this ~il[documentation] topic shows why this unattachment needs to be done.) Such removal takes place before the current ~c[defattach] is processed, but is restored if the new event fails to be admitted. We focus henceforth on the second General Form. There must be at least one attachment, i.e., ~c[i] must be at least 1. All keywords are optional; their role is described below. The ~c[fi] must be distinct constrained function symbols, that is, function symbols all introduced in ~il[signature]s of ~ilc[encapsulate] ~il[events] (or macros such as ~ilc[defstub] that generate ~ilc[encapsulate] events). Each non-~c[nil] ~c[gi] is a ~c[:]~ilc[logic]-mode function symbol that has had its guards verified, with the same ~il[signature] as ~c[fi] (though formal parameters for ~c[fi] and ~c[gi] may have different names). (Note: The macro ~c[defattach!], defined in community book ~c[books/misc/defattach-bang], avoids this restriction.) This event generates proof obligations and an ordering check, both described below. The effect of this event is first to remove any existing attachments for all the function symbols ~c[fi], as described above for the first General Form, and then to attach each ~c[gi] to ~c[fi]. Proof obligations must be checked before making attachments. For this discussion we assume that each ~c[gi] is non-~c[nil] (otherwise first remove all attachment pairs ~c[] for which ~c[gi] is nil). Let ~c[s] be the functional substitution mapping each ~c[fi] to ~c[gi]. For any term ~c[u], we write ~c[u\\s] for the result of applying ~c[s] to ~c[u]; that is, ~c[u\\s] is the ``functional instance'' obtained by replacing each ~c[fi] by ~c[gi] in ~c[u]. Let ~c[G_fi] and ~c[G_gi] be the guards of ~c[fi] and ~c[gi], respectively. Let ~c[G_fi'] be the result of replacing each formal of ~c[fi] by the corresponding formal of ~c[gi] in ~c[G_fi]. ACL2 first proves, for each ~c[i] (in order), the formula ~c[(implies G_fi' G_gi)\\s]. If this sequence of proofs succeeds, then the remaining formula to prove is the functional instance ~c[C\\s] of the conjunction ~c[C] of the constraints on the symbols ~c[fi]; ~pl[constraint]. This last proof obligation is thus similar to the one generated by functional instantiation (~pl[constraint]). As with functional instantiation, ACL2 stores the fact that such proofs have been done so that they are avoided in future events (~pl[lemma-instance]). Thus, you will likely avoid some proofs with the sequence ~bv[] (defattach f g) (defattach f nil) (defattach f g) (defattach f nil) ... ~ev[] rather than the sequence: ~bv[] (defattach f g) :u (defattach f g) :u ... ~ev[] It remains to describe an ordering check. We begin with the following motivating example. ~bv[] (defstub f (x) t) ; constrained function with no constraints (defun g (x) (declare (xargs :guard t)) (not (f x))) (defattach f g) ; ILLEGAL! ~ev[] Were the above ~c[defattach] event to succeed, the evaluation theory (discussed above) would be inconsistent: ~c[(f x)] equals ~c[(g x)] by the new attachment equation, which in turn equals ~c[(not (f x))] by definition of ~c[g]. The evaluation would therefore be meaningless. Also, from a practical perspective, there would be an infinite loop resulting from any call of ~c[f]. We consider a function symbol ~c[g] to be an ``extended immediate ancestor of'' a function symbol ~c[f] if either of the following two criteria is met: (a) ~c[g] occurs in the formula that introduces ~c[f] (i.e., definition body or constraint) and ~c[g] is introduced by an event different from (earlier than) the event introducing ~c[f]; or (b) ~c[g] is attached to ~c[f]. For a proposed ~c[defattach] event, we check that this relation has no cycles, where for condition (b) we include all attachment pairs that would result, including those remaining from earlier ~c[defattach] events. Of course, a special case is that no function symbol may be attached to itself. Similarly, no function symbol may be attached to any of its ``siblings'' ~-[] function symbols introduced by the same event ~-[] as siblings are considered equivalent for purposes of the acyclicity check. ~st[Three primary uses of defattach.]~nl[] We anticipate three uses of ~c[defattach]: (1) Constrained function execution (2) Sound modification of the ACL2 system (3) Program refinement We discuss these in turn. (1) The example at the beginning of this ~il[documentation] illustrates constrained function execution. (2) ACL2 is written essentially in itself. Thus, there is an opportunity to attaching to system functions. For example, encapsulated function ~c[too-many-ifs-post-rewrite], in the ACL2 source code, receives an attachment of ~c[too-many-ifs-post-rewrite-builtin], which implements a heuristic used in the rewriter. To find all such examples, search the source code for the string `-builtin'. Over time, we expect to continue replacing ACL2 source code in a similar manner. We invite the ACL2 community to assist in this ``open architecture'' enterprise; feel free to email the ACL2 implementors if you are interested in such activity. (3) Recall that for an attachment pair ~c[], a proof obligation is (speaking informally) that ~c[g] satisfies the constraint on ~c[f]. Yet more informally speaking, ~c[g] is ``more defined'' than ~c[f]; we can think of ~c[g] as ``refining'' ~c[f]. With these informal notions as motivation, we can view defattach as providing refinement though the following formal observation: the evaluation theory extends the theory of the ACL2 session, specifically by the addition of all attachment equations. For the logic-inclined, it may be useful to think model-theoretically: The class of models of the evaluation theory is non-empty but is a subset of the class of models of the current session theory. ~st[Miscellaneous remarks, with discussion of possible user errors.] We conclude with remarks on some details. A ~c[defattach] event is never redundant (~pl[redundant-events]); in that sense it is analogous to ~ilc[in-theory]. As mentioned above, the use of attachments is disabled for evaluation of ground terms during proofs. However, attachments can be used on code during the proof process, essentially when the ``program refinement'' is on theorem prover code rather than on functions we are reasoning about. The attachment to ~c[too-many-ifs-post-rewrite] described above provides one example of such attachments. Meta functions and clause-processor functions can also have attachments, with the restriction that no common ancestor with the evaluator can have an attachment; ~pl[evaluator-restrictions]. For an attachment pair ~c[], evaluation of ~c[f] never consults the ~il[guard] of ~c[f]. Rather, control passes to ~c[g], whose guard is checked if necessary. The proof obligation related to guards, as described above, guarantees that any legal call of ~c[f] is also a legal call of ~c[g]. Thus for guard-verified code that results in calls of ~c[f] in raw Lisp, it is sound to replace these calls with corresponding calls of ~c[g]. ~c[Defattach] events are illegal inside any ~ilc[encapsulate] event with a non-empty ~il[signature] unless they are ~il[local] to the ~ilc[encapsulate]. We next discuss a restriction based on a notion of a function symbol syntactically supporting an event. Function symbol ~c[f] is ~em[ancestral] in event ~c[E] if either ~c[f] occurs in ~c[E], or (recursively) ~c[f] occurs in an event ~c[E'] that introduces some function symbol ~c[g] that is ancestral in ~c[E]. We require that no function symbol ancestral in the formula of a ~ilc[defaxiom] event may have an attachment. Theoretical reasons are discussed in comments in the ACL2 source code, but here we give a little example showing the need for some such restriction: without it, we show how to prove ~c[nil]! ~bv[] (defn g1 () 1) (defn g2 () 2) (defstub f1 () t) (defstub f2 () t) (defund p (x) (declare (ignore x)) t) (defevaluator evl evl-list ((p x))) (defaxiom f1-is-f2 (equal (f1) (f2))) (defun meta-fn (x) (cond ((equal (f1) (f2)) x) (t *nil*))) (defthm bad-meta-rule (equal (evl x a) (evl (meta-fn x) a)) :rule-classes ((:meta :trigger-fns (p)))) (defattach f1 g1) (defattach f2 g2) (defthm contradiction nil :hints ((\"Goal\" :use ((:instance (:theorem (not (p x))) (x t))))) :rule-classes nil) ~ev[] To see all attachments: ~c[(all-attachments (w state))]. (Note that attachments introduced with a non-~c[nil] value of ~c[:skip-checks] will be omitted from this list.) Next we discuss the ~c[:ATTACH] keyword. There is rarely if ever a reason to specify ~c[:ATTACH T], but the following (admittedly contrived) example shows why it may be necessary to specify ~c[:ATTACH NIL]. First we introduce three new function symbols. ~bv[] (defstub f (x) t) (defun g (x) (f x)) (encapsulate ((h (x) t)) (local (defun h (x) (g x))) (defthm h-prop (equal (h x) (g x)))) ~ev[] Now suppose we want to attach the function ~ilc[acl2-numberp] to both ~c[f] and ~c[h]. ~bv[] (defattach (f acl2-numberp) (h acl2-numberp)) ~ev[] Such an attempt fails, because the following constraint is generated but is not a theorem: ~c[(EQUAL (ACL2-NUMBERP X) (G X))]. Clearly we also need to attach to ~c[g] as well. ~bv[] (defattach (f acl2-numberp) (h acl2-numberp) (g acl2-numberp)) ~ev[] But this fails for a different reason, as explained by the error message: ~bv[] ACL2 Error in ( DEFATTACH (F ACL2-NUMBERP) ...): It is illegal to attach to function symbol G, because it was introduced with DEFUN. See :DOC defattach. ~ev[] That is: logically, we need to attach ~c[acl2-numberp] to ~c[g], but we cannot actually attach to ~c[g] because it was introduced with ~ilc[defun], not with ~ilc[encapsulate]. So we specify ~c[:ATTACH NIL] for the attachment to ~c[g], saying that no actual attachment should be made to the code for ~c[g], even though for logical purposes we should consider that ~c[g] has been given the indicated attachment. ~bv[] (defattach (f acl2-numberp) (h acl2-numberp) (g acl2-numberp :attach nil)) ~ev[] Finally, we can check that ~c[f], ~c[g], and ~c[h] execute as expected. ~bv[] ACL2 !>(assert-event (and (f 3) (not (f t)) (g 3) (not (g t)) (h 3) (not (h t)))) :PASSED ACL2 !> ~ev[] We conclude with an example promised above, showing why it is necessary in general to unattach all function symbols in an existing attachment nest when unattaching any one of those function symbols. Consider the following example. ~bv[] (defstub f1 () t) (encapsulate ((f2 () t)) (local (defun f2 () (f1))) (defthm f2=f1 (equal (f2) (f1)))) (encapsulate ((f3 () t)) (local (defun f3 () (f1))) (defthm f3=f1 (equal (f3) (f1)))) (defun four () (declare (xargs :guard t)) 4) (defun five () (declare (xargs :guard t)) 5) (defattach (f1 four) (f2 four)) (defattach (f1 five) (f3 five)) ~ev[] The second ~c[defattach] replaces erases the existing attachment pair ~c[] before installing the new attachment pairs ~c[] and ~c[]. After the second defattach, both ~c[(f1)] and ~c[(f3)] evaluate to 5. Now suppose that the attachment pair ~c[] were not erased. Then we would have ~c[(f1)] evaluating to 5 and ~c[(f2)] evaluating to 4, contradicting the constraint ~c[f2=f1]. The evaluation theory would thus be inconsistent, and at a more concrete level, the user might well be surprised by evaluation results if the code were written with the assumption specified in the constraint ~c[f2=f1].~/" (list 'defattach-fn (list 'quote args) 'state (list 'quote event-form))) ; Now we define defattach in raw Lisp. #-acl2-loop-only (progn (defun attachment-symbol (x) ; Here we assume that the only use of the symbol-value of *1*f is to indicate ; that this value is the function attached to f. (*1*-symbol x)) (defun set-attachment-symbol-form (fn val) `(defparameter ,(attachment-symbol fn) ',val)) (defmacro defattach (&rest args) (cond ((symbolp (car args)) (set-attachment-symbol-form (car args) (cadr args))) (t (let (ans) (dolist (arg args) (cond ((keywordp arg) (return)) (t (push (set-attachment-symbol-form (car arg) (cond ((let ((tail (assoc-keyword :attach (cddr arg)))) (and tail (null (cadr tail)))) nil) (t (cadr arg)))) ans)))) (cons 'progn ans))))) ) ; Note: Important Boot-Strapping Invariants ; If any of the above forms are modified, be sure to change the ; setting of *initial-event-defmacros* as described there. Each of ; the defmacros above (except those excused) is of a rigid form ; recognized by the function primordial-event-macro-and-fn. For ; example, there are no declarations and the bodies used above are ; simple enough to be translatable by boot-translate before the world ; is created. ; More subtly, except for local, each macro generates a call of a ; corresponding -fn function on some actuals computed from the macros ; args: THE FORMALS OF THE -fn FUNCTIONS CAN BE DETERMINED BY LOOKING ; AT THE ACTUALS! For example, we can see that the 'formals for ; 'in-theory-fn, whenever it gets defined, will be '(expr state doc ; event-form). The function primordial-event-macro-and-fn1 computes ; the formals from the actuals. Don't change the expressions above, ; don't even change the formals to the defmacros, and don't change the ; formals of the -fns unless you understand this! ; End of *initial-event-defmacros* discussion. ; GETPROP - an efficient applicative property list replacement. ; We provide here a property list facility with applicative ; semantics. The two primitive operations are putprop and ; getprop. A ``world-alist'' is a list of ``triples'' of the ; form (symbol key . val). Putprop conses triples on to a given ; world-alist. Getprop take a symbol and key and looks for the ; first member of the given world-alist with the given symbol and ; key, returning the corresponding val, or a default if no such ; triple is found. ; In the ``usual case'', the cost of a getprop will be no more than ; the cost of a couple of get's in Common Lisp, rather than a search ; linear in the length of the given world-alist. The efficiency is ; based upon the strange ``world-name'' extra argument of getprop. ; Formally, world-name is to be regarded as a parameter of getprop ; that is simply ignored. Practically speaking, getprop uses this ; hint to check whether the given world-alist is in fact currently and ; validly represented by a set of properties on property lists. To do ; this, getprop checks that as the 'acl2-world-pair property of the ; given world-name, there is a pair whose car is (eq) the given ; world-alist. If this is the case, then the cdr of the pair, say ; world-key, is a gensymed symbol. The world-key property of any ; given symbol, symb, is an alist containing exactly those pairs (key ; . val) such that (symb key . val) is in world-alist. That is, to ; find the key property of symb it is sufficient to assoc-eq for key ; up the alist obtained by (get symb world-key). ; For a more thorough description of the issues concerning ; installation of worlds, see the discussion in interface-raw.lisp, ; under the section heading EXTENDING AND RETRACTING PROPERTY LIST ; WORLDS. ; To use getprop and putprop effectively, one must think clearly in ; terms of the usual order of Lisp evaluation. Getprop is only fast ; on worlds that have been ``installed'' as by extend-world or ; retract-world. (deflabel worldp) ; reserving this symbol for later use (defun plist-worldp (alist) (declare (xargs :guard t)) ; The following shortcut speeds up this function's execution. It seems ; slightly risky: if we can somehow get the installed world to be eq to a world ; in a theorem (say, by honsing both), and if that world does not actually ; satisfy the logical definition of plist-worldp, then we could prove nil. ; Initially we included community book books/centaur/doc, creating a world of ; length 359,153 (in a post-4.3 development version), and it took about 1/50 ; second to do this check without the above shortcut; so performance didn't ; seem too critical an issue here. However, the regression slowed down ; significantly without the shortcut. Here are statistics from HONS ; regressions using identical books, on the same unloaded machine. ; With shortcut: ; 15634.000u 1057.650s 53:22.39 521.2% 0+0k 352216+1367056io 1789pf+0w ; Without shortcut: ; 16414.440u 1048.600s 57:20.82 507.5% 0+0k 354128+1367184io 1696pf+0w ; So we have decided to keep the shortcut, since we really do expect this ; simple property to hold of any ACL2 world. #-acl2-loop-only (cond ((eq alist (w *the-live-state*)) (return-from plist-worldp t))) (cond ((atom alist) (eq alist nil)) (t (and (consp (car alist)) (symbolp (car (car alist))) (consp (cdr (car alist))) (symbolp (cadr (car alist))) (plist-worldp (cdr alist)))))) (defthm plist-worldp-forward-to-assoc-eq-equal-alistp (implies (plist-worldp x) (assoc-eq-equal-alistp x)) :rule-classes :forward-chaining) (defdoc getprop ":Doc-Section ACL2::ACL2-built-ins access fast property lists~/ ~bv[] General form: (getprop symb key default world-name world-alist) ~ev[] See community book ~c[books/misc/getprop.lisp] for an example that illustrates the use of ACL2 utilities ~ilc[getprop] and ~c[putprop] to take advantage of under-the-hood Lisp (hashed) property lists. To see the ACL2 definition of this function, ~pl[pf].~/~/") (defun putprop (symb key value world-alist) ":Doc-Section ACL2::ACL2-built-ins update fast property lists~/ ~bv[] General form: (putprop symbol key value world-alist) ~ev[] See community book ~c[books/misc/getprop.lisp] for an example that illustrates the use of ACL2 utilities ~ilc[getprop] and ~c[putprop] to take advantage of under-the-hood Lisp (hashed) property lists. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard (and (symbolp symb) (symbolp key) (plist-worldp world-alist)))) (cons (cons symb (cons key value)) world-alist)) ; Occasionally you will find comments of the form: ; On Metering ; Occasionally in this code you will see forms protected by ; #+acl2-metering. If you (push :acl2-metering *features*) and then ; recompile the affected forms, you will get some additional printing ; that indicates random performance meters we have found useful. ; The following two definitions support a particularly common style of ; metering we do. Suppose you have a typical tail recursive fn for ; exploring a big list ; (defun scan (lst) ; (cond (test ; finish) ; (t ; (scan (cdr lst))))) ; We often meter it with: ; (defun scan (lst) ; (cond (test ; #+acl2-metering (meter-maid 'scan 100) ; finish) ; (t ; #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) ; (scan (cdr lst))))) ; Where (meter-maid 'scan 100) tests meter-maid-cnt against 100 and if ; it is bigger prints a msg about 'scan. In any case, meter-maid ; resets cnt to 0. This style of metering is not very elegant because ; meter-maid-cnt ought to be initialized cleanly to 0 "at the top" and ; protected against error aborts (i.e., by binding it). But to do ; that we'd have to recode many of our tail recursive functions so ; they had preludes and lets. With our meter-maid style, we can just ; insert the metering text into the existing text and preserve the ; tail recursion and lack of initialization. Not often in metered ; runs do we abort (leaving meter-maid-cnt artificially high) and that ; results (at worst) in a spurious report on the next metered call. #-acl2-loop-only (defparameter meter-maid-cnt 0) #-acl2-loop-only (defun meter-maid (fn maximum &optional arg1 arg2 cnt) (cond ((> (or cnt meter-maid-cnt) maximum) (cond (arg2 (format t "~%Meter: ~s on ~s and ~s used ~s cycles.~%" fn arg1 arg2 (or cnt meter-maid-cnt))) (arg1 (format t "~%Meter: ~s on ~s used ~s cycles.~%" fn arg1 (or cnt meter-maid-cnt))) (t (format t "~%Meter: ~s used ~s cycles.~%" fn (or cnt meter-maid-cnt)))))) (setq meter-maid-cnt 0)) ; If we ever find this value stored under a property, then getprop acts as ; though no value was found. Thus, this value had better never be stored as a ; "legitimate" value of the property. To belabor this point: we have here a ; fundamental difference between our getprop and Lisp's get. (defconst *acl2-property-unbound* :acl2-property-unbound) (defun getprop-default (symb key default) (declare (xargs :guard t)) (prog2$ (and (consp default) (eq (car default) :error) (consp (cdr default)) (stringp (cadr default)) (null (cddr default)) (hard-error 'getprop "No property was found under symbol ~x0 for key ~x1. ~@2" (list (cons #\0 symb) (cons #\1 key) (cons #\2 (cadr default))))) default)) #-acl2-loop-only (defun-one-output sgetprop1 (symb key default world-alist inst-world-alist inst-gensym) (do ((tl world-alist (cdr tl))) ((null tl) (getprop-default symb key default)) (cond ((eq tl inst-world-alist) (return-from sgetprop1 (let ((temp (assoc-eq key (get symb inst-gensym)))) (cond (temp (cond ((cdr temp) (let ((ans (car (cdr temp)))) (if (eq ans *acl2-property-unbound*) (getprop-default symb key default) ans))) (t (getprop-default symb key default)))) (t (getprop-default symb key default)))))) ((and (eq symb (caar tl)) (eq key (cadar tl))) (return-from sgetprop1 (let ((ans (cddar tl))) (if (eq ans *acl2-property-unbound*) (getprop-default symb key default) ans))))))) ; The following code, not generally loaded, is used to augment fgetprop to ; determine the frequency with which we access properties. See the ; fgetprop-stats comment in fgetprop for a description of how to use ; this code. ; (defvar fgetprop-stats nil) ; ; (defvar analyzed-fgetprop-stats nil) ; ; (compile ; (defun update-fgetprop-stats (sym key) ; (let* ((sym-entry (assoc sym fgetprop-stats :test #'eq)) ; (key-entry (assoc key (cdr sym-entry) :test #'eq))) ; (cond (key-entry (setf (cdr key-entry) (1+ (cdr key-entry)))) ; (sym-entry (setf (cdr sym-entry) (cons (cons key 1) (cdr sym-entry)))) ; (t (setq fgetprop-stats ; (cons (cons sym (list (cons key 1))) fgetprop-stats))))))) ; ; (compile ; (defun analyze-fgetprop-stats nil ; (format t "Properties accessed and access counts:~%") ; (loop ; for x in (sort (let ((prop-alist nil)) ; (loop ; for pair in fgetprop-stats ; do ; (loop ; for x in (cdr pair) ; do ; (let ((temp (assoc (car x) prop-alist :test #'eq))) ; (cond (temp (setf (cdr temp) (+ (cdr temp) (cdr x)))) ; (t (setq prop-alist ; (cons (cons (car x) (cdr x)) ; prop-alist))))))) ; prop-alist) ; #'(lambda (x y) (> (cdr x) (cdr y)))) ; do ; (format t "~A~50T~9D~%" (car x) (cdr x))) ; (terpri t) ; (setq analyzed-fgetprop-stats ; (sort ; (loop ; for pair in fgetprop-stats ; collect ; (let* ((other-cutoff 1) ; (others ; (loop ; for x in (cdr pair) when (<= (cdr x) other-cutoff) ; sum (cdr x)))) ; (list* (car pair) ; (loop for x in (cdr pair) sum (cdr x)) ; (let ((temp ; (sort (loop ; for x in (cdr pair) ; when ; (or (= others 0) ; (= others other-cutoff) ;i.e., just 1 other ; (> (cdr x) other-cutoff)) ; collect x) ; #'(lambda (x y)(> (cdr x) (cdr y)))))) ; (if (> others other-cutoff) ; (append temp ; (list (cons "all other" others))) ; temp))))) ; #'(lambda (x y) (> (cadr x) (cadr y))))) ; (format t "Analyzed fgetprop-stats~%") ; (loop ; for trip in analyzed-fgetprop-stats ; do ; (format t "~S~45T~9D~%" (car trip) (cadr trip)) ; (loop ; for pair in (cddr trip) ; do ; (format t " ~A~50T~9D~%" (car pair) (cdr pair)))) ; t)) ; Note: In versions before V2.2 the following defvar was in ; interface-raw.lisp. But it is used earlier than that in the ; initialization process. (defun fgetprop (symb key default world-alist) ; This is getprop's meaning when we know the world name is 'current-acl2-world. ; The invariant maintained for the 'current-acl2-world is the same as that ; maintained for other world names with the additional fact that the installed ; alist itself is the value of the state global variable 'current-acl2-world, ; whose raw lisp counterpart is ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD, and the ; gensym under which the property alist is stored for each symbol is also kept ; in the raw lisp global *current-acl2-world-key*. Put another way, (get ; 'current-acl2-world 'acl2-world-pair) returns a pair equal to (cons ; ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD *current-acl2-world-key*). (declare (xargs :guard (and (symbolp symb) (symbolp key) (plist-worldp world-alist)))) #+acl2-loop-only (cond ((endp world-alist) default) ((and (eq symb (caar world-alist)) (eq key (cadar world-alist))) (let ((ans (cddar world-alist))) (if (eq ans *acl2-property-unbound*) default ans))) (t (fgetprop symb key default (cdr world-alist)))) ; The following two lines are commented out. They collect the fgetprop-stats. ; Those stats will tell you, for a given run of the system, which properties ; are accessed, the frequency with which they are accessed, and a breakdown by ; symbol of all the properties accessed. If you wish to collect the ; fgetprop-stats, then load the code above into raw lisp, remove the two ; semi-colons below, reload this defun of fgetprop, and run some experiments. ; Then use (analyze-fgetprop-stats) to print out the results. It is generally ; advisable to compile all the defuns just loaded. ; #-acl2-loop-only ; (update-fgetprop-stats symb key) #-acl2-loop-only (cond ((eq world-alist (symbol-value 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD)) (let ((temp (assoc-eq key (get symb *current-acl2-world-key*)))) (cond (temp (cond ((cdr temp) (let ((ans (car (cdr temp)))) (if (eq ans *acl2-property-unbound*) (getprop-default symb key default) ans))) (t (getprop-default symb key default)))) (t (getprop-default symb key default))))) (t (sgetprop1 symb key default world-alist (symbol-value 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD) *current-acl2-world-key*)))) (defun sgetprop (symb key default world-name world-alist) ; This is getprop's meaning when we don't know the world-name. (declare (xargs :guard (and (symbolp symb) (symbolp key) (symbolp world-name) (plist-worldp world-alist)))) ; Note that if default has the form '(:error string) where string is a ; stringp, then in raw Lisp we execute a hard error with context ; 'getprop and string string. Otherwise (and logically in any case), ; default is what we return when there is no key property of symb. #+acl2-loop-only (cond ((endp world-alist) default) ((and (eq symb (caar world-alist)) (eq key (cadar world-alist))) (let ((ans (cddar world-alist))) (if (eq ans *acl2-property-unbound*) default ans))) (t (sgetprop symb key default world-name (cdr world-alist)))) #-acl2-loop-only (let ((pair (get world-name 'acl2-world-pair))) (cond (pair (sgetprop1 symb key default world-alist (car pair) (cdr pair))) (t (do ((tl world-alist (cdr tl))) ((null tl) (getprop-default symb key default)) (cond ((and (eq symb (caar tl)) (eq key (cadar tl))) (return-from sgetprop (let ((ans (cddar tl))) (if (eq ans *acl2-property-unbound*) (getprop-default symb key default) ans)))))))))) (defun ordered-symbol-alistp (x) ; An ordered-symbol-alist is an alist whose keys are symbols which are ; in the symbol-< order. (declare (xargs :guard t)) (cond ((atom x) (null x)) ((atom (car x)) nil) (t (and (symbolp (caar x)) (or (atom (cdr x)) (and (consp (cadr x)) (symbolp (caadr x)) (symbol-< (caar x) (caadr x)))) (ordered-symbol-alistp (cdr x)))))) (in-theory (disable symbol-<)) (defthm ordered-symbol-alistp-forward-to-symbol-alistp (implies (ordered-symbol-alistp x) (symbol-alistp x)) :rule-classes :forward-chaining) (defun add-pair (key value l) (declare (xargs :guard (and (symbolp key) (ordered-symbol-alistp l)))) (cond ((endp l) (list (cons key value))) ((eq key (caar l)) (cons (cons key value) (cdr l))) ((symbol-< key (caar l)) (cons (cons key value) l)) (t (cons (car l) (add-pair key value (cdr l)))))) ; Delete-assoc (defun delete-assoc-eq-exec (key alist) (declare (xargs :guard (if (symbolp key) (alistp alist) (symbol-alistp alist)))) (cond ((endp alist) nil) ((eq key (caar alist)) (cdr alist)) (t (cons (car alist) (delete-assoc-eq-exec key (cdr alist)))))) (defun delete-assoc-eql-exec (key alist) (declare (xargs :guard (if (eqlablep key) (alistp alist) (eqlable-alistp alist)))) (cond ((endp alist) nil) ((eql key (caar alist)) (cdr alist)) (t (cons (car alist) (delete-assoc-eql-exec key (cdr alist)))))) (defun delete-assoc-equal (key alist) (declare (xargs :guard (alistp alist))) (cond ((endp alist) nil) ((equal key (caar alist)) (cdr alist)) (t (cons (car alist) (delete-assoc-equal key (cdr alist)))))) (defmacro delete-assoc-eq (key lst) `(delete-assoc ,key ,lst :test 'eq)) (defthm delete-assoc-eq-exec-is-delete-assoc-equal (equal (delete-assoc-eq-exec key lst) (delete-assoc-equal key lst))) (defthm delete-assoc-eql-exec-is-delete-assoc-equal (equal (delete-assoc-eql-exec key lst) (delete-assoc-equal key lst))) (defmacro delete-assoc (key alist &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins remove the first pair from an association list for a given key~/ ~bv[] General Forms: (delete-assoc key alist) (delete-assoc key alist :test 'eql) ; same as above (eql as equality test) (delete-assoc key alist :test 'eq) ; same, but eq is equality test (delete-assoc key alist :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Delete-assoc key alist)] returns an alist that is the same as the list ~c[alist], except that the first pair in ~c[alist] with a ~ilc[car] of ~c[key] is deleted, if there is one; otherwise ~c[alist] is returned. Note that the order of the elements of ~c[alist] is unchanged (though one may be deleted).~/ The ~il[guard] for a call of ~c[delete-assoc] depends on the test. In all cases, the second argument must satisfy ~ilc[alistp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the second argument must satisfy ~ilc[eqlable-alistp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the second argument must satisfy ~ilc[symbol-alistp]. ~l[equality-variants] for a discussion of the relation between ~c[delete-assoc] and its variants: ~bq[] ~c[(delete-assoc-eq key alist)] is equivalent to ~c[(delete-assoc key alist :test 'eq)]; ~c[(delete-assoc-equal key alist)] is equivalent to ~c[(delete-assoc key alist :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[delete-assoc-equal].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((key ,key) (alist ,alist)) :logic (delete-assoc-equal key alist) :exec (delete-assoc-eq-exec key alist))) ((equal test ''eql) `(let-mbe ((key ,key) (alist ,alist)) :logic (delete-assoc-equal key alist) :exec (delete-assoc-eql-exec key alist))) (t ; (equal test 'equal) `(delete-assoc-equal ,key ,alist)))) (defun getprops1 (alist) ; Each element of alist is of the form (key val1 ... valk), i.e., key is bound ; to a stack of vali's. We transform each element to (key . val1), i.e., each ; key is bound to the top-most vali. An empty stack or a top value of ; *acl2-property-unbound* means there is no binding for key. (declare (xargs :guard (true-list-listp alist))) (cond ((endp alist) nil) ((or (null (cdar alist)) (eq (car (cdar alist)) *acl2-property-unbound*)) (getprops1 (cdr alist))) (t (cons (cons (caar alist) (cadar alist)) (getprops1 (cdr alist)))))) (defun getprops (symb world-name world-alist) ; returns all of the properties of symb in world-alist, as a list of ; key-value pairs, sorted according to ordered-symbol-alistp. We ; respect the *acl2-property-unbound* convention. (declare (xargs :guard (and (symbolp symb) (symbolp world-name) (plist-worldp world-alist)) :mode :program)) #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (cond #-acl2-loop-only ((eq world-alist (car (get world-name 'acl2-world-pair))) #+acl2-metering (meter-maid 'getprops 100 symb) (sort (getprops1 (get symb (cdr (get world-name 'acl2-world-pair)))) #'(lambda (x y) (symbol-< (car x) (car y))))) ((endp world-alist) #+acl2-metering (meter-maid 'getprops 100 symb) nil) ((eq symb (caar world-alist)) (let ((alist (getprops symb world-name (cdr world-alist)))) (if (eq (cddar world-alist) *acl2-property-unbound*) (if (assoc-eq (cadar world-alist) alist) (delete-assoc-eq (cadar world-alist) alist) alist) (add-pair (cadar world-alist) (cddar world-alist) alist)))) (t (getprops symb world-name (cdr world-alist))))) (verify-termination-boot-strap getprops (declare (xargs :mode :logic :verify-guards nil))) ; We don't verify the guards for getprops until we have LOCAL, which really ; means, until LOCAL has STATE-GLOBAL-LET*. ; We disable the following function in order to protect people from getting ; burned by string<-l. (in-theory (disable string<)) (defthm equal-char-code (implies (and (characterp x) (characterp y)) (implies (equal (char-code x) (char-code y)) (equal x y))) :rule-classes nil :hints (("Goal" :use ((:instance code-char-char-code-is-identity (c x)) (:instance code-char-char-code-is-identity (c y)))))) (defun has-propsp1 (alist exceptions known-unbound) ; This function is only called from raw lisp code in has-propsp. Alist is the ; alist of ACL2 properties stored on the property list of some symbol. As ; such, each element of alist is of the form (prop val1 val2 ... valk) where ; val1 is the most recently stored value of the property prop for that symbol. ; We here check that each val1 is *acl2-property-unbound* (unless prop is among ; exceptions or known-unbound). (declare (xargs :guard (and (assoc-eq-equal-alistp alist) (true-listp exceptions) (true-listp known-unbound)))) (cond ((endp alist) nil) ((or (null (cdar alist)) (eq (cadar alist) *acl2-property-unbound*) (member-eq (caar alist) exceptions) (member-eq (caar alist) known-unbound)) (has-propsp1 (cdr alist) exceptions known-unbound)) (t t))) (defun has-propsp (symb exceptions world-name world-alist known-unbound) ; We return t iff symb has properties other than those listed in exceptions. (declare (xargs :guard (and (symbolp symb) (symbolp world-name) (plist-worldp world-alist) (true-listp exceptions) (true-listp known-unbound)))) #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (cond #-acl2-loop-only ((eq world-alist (car (get world-name 'acl2-world-pair))) #+acl2-metering (meter-maid 'has-propsp 100 symb) (has-propsp1 (get symb (cdr (get world-name 'acl2-world-pair))) exceptions known-unbound)) ((endp world-alist) #+acl2-metering (meter-maid 'has-propsp 100 symb) nil) ((or (not (eq symb (caar world-alist))) (member-eq (cadar world-alist) exceptions) (member-eq (cadar world-alist) known-unbound)) (has-propsp symb exceptions world-name (cdr world-alist) known-unbound)) ((eq (cddar world-alist) *acl2-property-unbound*) (has-propsp symb exceptions world-name (cdr world-alist) (cons (cadar world-alist) known-unbound))) (t t))) (defun extend-world (name wrld) ; Logically speaking, this function is a no-op that returns wrld. ; Practically speaking, it changes the Lisp property list ; state so that future getprops on name and wrld will be fast. ; However, wrld must be an extension of the current world installed ; under name, or else a hard error occurs. Finally, if name is ; 'current-acl2-world, then no changes are made, since we do not want ; the user to smash our world. #+acl2-loop-only (declare (xargs :guard t) (ignore name)) #+acl2-loop-only wrld #-acl2-loop-only (cond ((eq name 'current-acl2-world) wrld) (t (extend-world1 name wrld)))) (defun retract-world (name wrld) ; Logically speaking, this function is a no-op that returns wrld. ; Practically speaking, it changes the Lisp property list ; state so that future getprops on name and wrld will be fast. ; However, wrld must be a retraction of the current world installed ; under name, or else a hard error occurs. Finally, if name is ; 'current-acl2-world, then no changes are made, since we do not want ; the user to smash our world. #+acl2-loop-only (declare (xargs :guard t) (ignore name)) #+acl2-loop-only wrld #-acl2-loop-only (cond ((eq name 'current-acl2-world) wrld) (t (retract-world1 name wrld)))) (defun global-val (var wrld) ; If you are tempted to access a global variable value with getprop ; directly, so you can specify your own default value, it suggests ; that you have not initialized the global variable. See the ; discussion in primordial-world-globals. Follow the discipline of ; always initializing and always accessing with global-val. (declare (xargs :guard (and (symbolp var) (plist-worldp wrld)))) (getprop var 'global-value '(:error "GLOBAL-VAL didn't find a value. Initialize this ~ symbol in PRIMORDIAL-WORLD-GLOBALS.") 'current-acl2-world wrld)) ; Declarations. (defun function-symbolp (sym wrld) ; Sym must be a symbolp. We return t if sym is a function symbol and ; nil otherwise. We exploit the fact that every function symbol has a ; formals property. Of course, the property may be NIL so when we ; seek it we default to t so we can detect the absence of the ; property. Of course, if someone were to putprop 'formals t we would ; therefore claim the symbol weren't a function-symbolp. This fact is ; exploited when we prepare the world for the redefinition of a ; symbol. If for some reason you change the default, you must change ; it there too. It would be a good idea to search for 'formals t. (declare (xargs :guard (and (symbolp sym) (plist-worldp wrld)))) (not (eq (getprop sym 'formals t 'current-acl2-world wrld) t))) ; We define translate-declaration-to-guard and accompanying functions in ; program mode, including the-fn, simply so that they take up a little less ; space in the image by avoiding the need to store 'def-bodies and ; 'unnormalized-body properties. (defun translate-declaration-to-guard/integer (lo var hi) (declare (xargs :guard t :mode :program)) (let ((lower-bound (cond ((integerp lo) lo) ((eq lo '*) '*) ((and (consp lo) (integerp (car lo)) (null (cdr lo))) (1+ (car lo))) (t nil))) (upper-bound (cond ((integerp hi) hi) ((eq hi '*) '*) ((and (consp hi) (integerp (car hi)) (null (cdr hi))) (1- (car hi))) (t nil)))) (cond ((and upper-bound lower-bound) (cond ((eq lower-bound '*) (cond ((eq upper-bound '*) (list 'integerp var)) (t (list 'and (list 'integerp var) (list '<= var upper-bound))))) (t (cond ((eq upper-bound '*) (list 'and (list 'integerp var) (list '<= lower-bound var))) (t ; It is tempting to use integer-range-p below. However, integer-range-p was ; introduced in Version_2.7 in support of signed-byte-p and unsigned-byte-p, ; whose definitions were kept similar to those that had been in the ihs library ; for some time. Hence, integer-range-p is defined in terms of a strict < ; comparison to the upper integer, which does not fit well with our current ; needs. (It feels wrong to use (< var (1+ upper-bound)), even though not ; unsound.) (list 'and (list 'integerp var) (list '<= lower-bound var) (list '<= var upper-bound))))))) (t nil)))) (defun weak-satisfies-type-spec-p (x) (declare (xargs :guard t)) (and (consp x) (eq (car x) 'satisfies) (true-listp x) (equal (length x) 2) (symbolp (cadr x)))) ;; RAG - I added entries for 'real and 'complex. Guards with 'complex ;; have CHANGED SEMANTICS! Yikes! Before, the moniker 'complex had ;; the semantics of complex-rationalp. Now, it has the semantics of ;; complexp. I added a new declaration, 'complex-rational, to stand ;; for the old semantics of 'complex. (defun translate-declaration-to-guard1 (x var wrld) ; Wrld is either an ACL2 logical world or a symbol; see ; translate-declaration-to-guard. (declare (xargs :guard (or (symbolp wrld) (plist-worldp wrld)) :mode :program)) (cond ((or (eq x 'integer) (eq x 'signed-byte)) (list 'integerp var)) ((and (consp x) (eq (car x) 'integer) (true-listp x) (equal (length x) 3)) (translate-declaration-to-guard/integer (cadr x) var (caddr x))) ((eq x 'rational) (list 'rationalp var)) ((eq x 'real) (list 'real/rationalp var)) ((eq x 'complex) (list 'complex/complex-rationalp var)) ((and (consp x) (eq (car x) 'rational) (true-listp x) (equal (length x) 3)) (let ((lower-bound (cond ((rationalp (cadr x)) (cadr x)) ((eq (cadr x) '*) '*) ((and (consp (cadr x)) (rationalp (car (cadr x))) (null (cdr (cadr x)))) (list (car (cadr x)))) (t nil))) (upper-bound (cond ((rationalp (caddr x)) (caddr x)) ((eq (caddr x) '*) '*) ((and (consp (caddr x)) (rationalp (car (caddr x))) (null (cdr (caddr x)))) (list (car (caddr x)))) (t nil)))) (cond ((and upper-bound lower-bound) (cond ((eq lower-bound '*) (cond ((eq upper-bound '*) (list 'rationalp var)) (t (list 'and (list 'rationalp var) (cond ((consp upper-bound) (list '< var (car upper-bound))) (t (list '<= var upper-bound))))))) (t (cond ((eq upper-bound '*) (list 'and (list 'rationalp var) (cond ((consp lower-bound) (list '< (car lower-bound) var)) (t (list '<= lower-bound var))))) (t (list 'and (list 'rationalp var) (cond ((consp lower-bound) (list '< (car lower-bound) var)) (t (list '<= lower-bound var))) (cond ((consp upper-bound) (list '> (car upper-bound) var)) (t (list '<= var upper-bound))))))))) (t nil)))) ((and (consp x) (eq (car x) 'real) (true-listp x) (equal (length x) 3)) (let ((lower-bound (cond ((real/rationalp (cadr x)) (cadr x)) ((eq (cadr x) '*) '*) ((and (consp (cadr x)) (real/rationalp (car (cadr x))) (null (cdr (cadr x)))) (list (car (cadr x)))) (t nil))) (upper-bound (cond ((real/rationalp (caddr x)) (caddr x)) ((eq (caddr x) '*) '*) ((and (consp (caddr x)) (real/rationalp (car (caddr x))) (null (cdr (caddr x)))) (list (car (caddr x)))) (t nil)))) (cond ((and upper-bound lower-bound) (cond ((eq lower-bound '*) (cond ((eq upper-bound '*) (list 'real/rationalp var)) (t (list 'and (list 'real/rationalp var) (cond ((consp upper-bound) (list '< var (car upper-bound))) (t (list '<= var upper-bound))))))) (t (cond ((eq upper-bound '*) (list 'and (list 'real/rationalp var) (cond ((consp lower-bound) (list '< (car lower-bound) var)) (t (list '<= lower-bound var))))) (t (list 'and (list 'real/rationalp var) (cond ((consp lower-bound) (list '< (car lower-bound) var)) (t (list '<= lower-bound var))) (cond ((consp upper-bound) (list '> (car upper-bound) var)) (t (list '<= var upper-bound))))))))) (t nil)))) ((eq x 'bit) (list 'or (list 'equal var 1) (list 'equal var 0))) ((and (consp x) (eq (car x) 'mod) (true-listp x) (equal (length x) 2) (integerp (cadr x))) (translate-declaration-to-guard/integer 0 var (1- (cadr x)))) ((and (consp x) (eq (car x) 'signed-byte) (true-listp x) (equal (length x) 2) (integerp (cadr x)) (> (cadr x) 0)) (list 'signed-byte-p (cadr x) var)) ((eq x 'unsigned-byte) (translate-declaration-to-guard/integer 0 var '*)) ((and (consp x) (eq (car x) 'unsigned-byte) (true-listp x) (equal (length x) 2) (integerp (cadr x)) (> (cadr x) 0)) (list 'unsigned-byte-p (cadr x) var)) ((eq x 'atom) (list 'atom var)) ((eq x 'character) (list 'characterp var)) ((eq x 'cons) (list 'consp var)) ((eq x 'list) (list 'listp var)) ((eq x 'nil) ; We return a translated nil here instead of just nil so as not to ; look like we're saying "This is an unrecognized declaration." ''nil) ((eq x 'null) (list 'eq var nil)) ((eq x 'ratio) (list 'and (list 'rationalp var) (list 'not (list 'integerp var)))) ((eq x 'standard-char) (list 'standard-charp var)) ((eq x 'string) (list 'stringp var)) ((and (consp x) (eq (car x) 'string) (true-listp x) (equal (length x) 2) (integerp (cadr x)) (>= (cadr x) 0)) (list 'and (list 'stringp var) (list 'equal (list 'length var) (cadr x)))) ((eq x 'symbol) (list 'symbolp var)) ((eq x 't) t) ((and (weak-satisfies-type-spec-p x) (or (symbolp wrld) (eql (length (getprop (cadr x) 'formals nil 'current-acl2-world wrld)) 1))) (list (cadr x) var)) ((and (consp x) (eq (car x) 'member) (eqlable-listp (cdr x))) (list 'member var (list 'quote (cdr x)))) (t nil))) (mutual-recursion ;; RAG - This was modified to change the moniker 'complex to use ;; complexp instead of complex-rationalp. (defun translate-declaration-to-guard (x var wrld) ; This function is typically called on the sort of x you might write in a TYPE ; declaration, e.g., (DECLARE (TYPE x var1 ... varn)). Thus, x might be ; something like '(or symbol cons (integer 0 128)) meaning that var is either a ; symbolp, a consp, or an integer in the given range. X is taken as a ; declaration about the variable symbol var and is converted into an ; UNTRANSLATED term about var, except that we return nil if x is seen not to be ; a valid type-spec for ACL2. ; Wrld is an ACL2 logical world or a symbol (typically, nil), the difference ; being that a symbol indicates that we should do a weaker check. This extra ; argument was added after Version_3.0 when Dave Greve pointed out that Common ; Lisp only allows the type-spec (satisfies pred) when pred is a unary function ; symbol, not a macro. Thus, a non-symbol wrld can only strengthen this ; function, i.e., causing it to return nil in more cases. (declare (xargs :guard (or (symbolp wrld) (plist-worldp wrld)) :mode :program ; See the comment above translate-declaration-to-guard/integer. ; :measure (acl2-count x) )) (cond ((atom x) (translate-declaration-to-guard1 x var wrld)) ((eq (car x) 'not) (cond ((and (true-listp x) (equal (length x) 2)) (let ((term (translate-declaration-to-guard (cadr x) var wrld))) (and term (list 'not term)))) (t nil))) ((eq (car x) 'and) (cond ((true-listp x) (cond ((null (cdr x)) t) (t (let ((args (translate-declaration-to-guard-lst (cdr x) var wrld))) (cond (args (cons 'and args)) (t nil)))))) (t nil))) ((eq (car x) 'or) (cond ((true-listp x) (cond ((null (cdr x)) ''nil) (t (let ((args (translate-declaration-to-guard-lst (cdr x) var wrld))) (cond (args (cons 'or args)) (t nil)))))) (t nil))) ((eq (car x) 'complex) (cond ((and (consp (cdr x)) (null (cddr x))) (let ((r (translate-declaration-to-guard (cadr x) (list 'realpart var) wrld)) (i (translate-declaration-to-guard (cadr x) (list 'imagpart var) wrld))) (cond ((and r i) (list 'and (list 'complex/complex-rationalp var) r i)) (t nil)))) (t nil))) (t (translate-declaration-to-guard1 x var wrld)))) (defun translate-declaration-to-guard-lst (l var wrld) ; Wrld is an ACL2 logical world or a symbol; see ; translate-declaration-to-guard. (declare (xargs ; :measure (acl2-count l) :guard (and (true-listp l) (consp l) (or (null wrld) (plist-worldp wrld))) :mode :program)) (and (consp l) (let ((frst (translate-declaration-to-guard (car l) var wrld))) (cond ((null frst) nil) ((endp (cdr l)) (list frst)) (t (let ((rst (translate-declaration-to-guard-lst (cdr l) var wrld))) (cond ((null rst) nil) (t (cons frst rst))))))))) ) (deflabel declare ; Warning: Keep this in sync with acceptable-dcls-alist. :doc ":Doc-Section ACL2::Programming declarations~/ ~bv[] Examples: (declare (ignore x y z)) (declare (ignorable x y z) (type integer i j k) (type (satisfies integerp) m1 m2)) (declare (xargs :guard (and (integerp i) (<= 0 i)) :guard-hints ((\"Goal\" :use (:instance lemma3 (x (+ i j)))))))~/ General Form: (declare d1 ... dn) where, in ACL2, each di is of one of the following forms: (ignore v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment. These variables must not occur free in the scope of the declaration. (ignorable v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment. These variables need not occur free in the scope of the declaration. This declaration can be useful for inhibiting compiler warnings. (type type-spec v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment and type-spec is a type specifier (as described in the documentation for ~il[type-spec]). (xargs :key1 val1 ... :keyn valn) -- where the legal values of the keyi's and their respective vali's are described in the documentation for ~il[xargs]. Xargs declarations are only allowed at the top level of definitions (defun and defmacro, as shown below). (optimize ...) -- for example, ~c[(optimize (safety 3))]. This is allowed only at the top level of ~ilc[defun] forms. See any Common Lisp documentation for more information. ~ev[] Declarations in ACL2 may occur only where ~c[dcl] occurs below: ~bv[] (DEFUN name args doc-string dcl ... dcl body) (DEFMACRO name args doc-string dcl ... dcl body) (LET ((v1 t1) ...) dcl ... dcl body) (MV-LET (v1 ...) term dcl ... dcl body) (FLET ((name args dcl ... dcl body) ...)) ~ev[] Of course, if a form macroexpands into one of these (as ~ilc[let*] expands into nested ~ilc[let]s and our ~c[er-let*] expands into nested ~ilc[mv-let]s) then declarations are permitted as handled by the macros involved. ~c[Declare] is defined in Common Lisp. See any Common Lisp documentation for more information.~/") (deflabel type-spec :doc ":Doc-Section declare type specifiers in declarations~/ ~bv[] Examples: The symbol INTEGER in (declare (type INTEGER i j k)) is a type-spec. Other type-specs supported by ACL2 include RATIONAL, COMPLEX, (INTEGER 0 127), (RATIONAL 1 *), CHARACTER, and ATOM. ~terminal[Type :more for a complete listing.] ~ev[]~/ The type-specs and their meanings (when applied to the variable ~c[x] as in ~c[(declare (type type-spec x))] are given below. ~bv[] type-spec meaning (AND type1 ... typek) (AND (p1 X) ... (pk X)) where (pj x) is the meaning for type-spec typej ATOM (ATOM X) BIT (OR (EQUAL X 1) (EQUAL X 0)) CHARACTER (CHARACTERP X) COMPLEX (AND (COMPLEX-RATIONALP X) (RATIONALP (REALPART X)) (RATIONALP (IMAGPART X))) (COMPLEX RATIONAL) same as COMPLEX, above (COMPLEX type) (AND (COMPLEX-RATIONALP X) (p (REALPART X)) (p (IMAGPART X))) where (p x) is the meaning for type-spec type CONS (CONSP X) INTEGER (INTEGERP X) (INTEGER i j) (AND (INTEGERP X) ; See notes below (<= i X) (<= X j)) (MEMBER x1 ... xn) (MEMBER X '(x1 ... xn)) (MOD i) same as (INTEGER 0 i-1) NIL NIL (NOT type) (NOT (p X)) where (p x) is the meaning for type-spec type NULL (EQ X NIL) (OR type1 ... typek) (OR (p1 X) ... (pk X)) where (pj x) is the meaning for type-spec typej RATIO (AND (RATIONALP X) (NOT (INTEGERP X))) RATIONAL (RATIONALP X) (RATIONAL i j) (AND (RATIONALP X) ; See notes below (<= i X) (<= X j)) REAL (RATIONALP X) ; (REALP X) in ACL2(r) (REAL i j) (AND (RATIONALP X) ; See notes below (<= i X) (<= X j)) (SATISFIES pred) (pred X) ; Lisp requires a unary function, not a macro SIGNED-BYTE (INTEGERP X) (SIGNED-BYTE i) same as (INTEGER k m) where k=-2^(i-1), m=2^(i-1)-1 STANDARD-CHAR (STANDARD-CHARP X) STRING (STRINGP X) (STRING max) (AND (STRINGP X) (EQUAL (LENGTH X) max)) SYMBOL (SYMBOLP X) T T UNSIGNED-BYTE same as (INTEGER 0 *) (UNSIGNED-BYTE i) same as (INTEGER 0 (2^i)-1) ~ev[] ~em[Notes:] In general, ~c[(integer i j)] means ~bv[] (AND (INTEGERP X) (<= i X) (<= X j)). ~ev[] But if ~c[i] is the symbol ~c[*], the first inequality is omitted. If ~c[j] is the symbol ~c[*], the second inequality is omitted. If instead of being an integer, the second element of the type specification is a list containing an integer, ~c[(i)], then the first inequality is made strict. An analogous remark holds for the ~c[(j)] case. The ~c[RATIONAL] and ~c[REAL] type specifiers are similarly generalized.~/") (defun the-check (guard x y) (declare (xargs :guard (or guard (hard-error nil "The object ~xa does not satisfy the ~ declaration ~xb." (list (cons #\a y) (cons #\b x)))))) (declare (ignore x guard)) y) (defun the-fn (x y) (declare (xargs :guard (translate-declaration-to-guard x 'var nil) ; As noted above the definition of translate-declaration-to-guard/integer, we ; are trying to save a little space in the image. :mode :program)) (let ((guard (translate-declaration-to-guard x 'var nil))) ; Observe that we translate the type expression, x, wrt the variable var and ; then bind var to y below. It is logically equivalent to translate wrt to y ; instead and then generate the if-expression below instead of the let. Why do ; we do that? Because y (or var) is liable to occur many times in the guard ; and if y is a huge expression we blow ourselves away there. A good example ; of this comes up if one translates the expression (the-type-set xxx). When ; we translated the declaration wrt to 'xxx we got an expression in which 'xxx ; occurred five times (using a version of this function present through ; Version_6.1). By generating the let below, it occurs only once. ; Comment from Version_6.1 and before, probably still mostly relevant today, ; although (the-error type val) has been supplanted using the-check. ; We have tried an experiment in which we treat the (symbolp y) case ; specially: translate wrt to y and just lay down the if-expression (if guard ; y (the-error 'x y)). The system was able to do an :init, so this did not ; blow us out of the water -- as we know it does if you so treat all y's. ; But this IF-expressions in the guard are therefore turned loose in the ; surrounding term and contribute to the explosion of normalized bodies. So ; we have backtracked to this, which has the advantage of keeping the ; normalized sizes just linearly bigger. (cond ((null guard) (illegal nil "Illegal-type." (list (cons #\0 x)))) (t `(let ((var ,y)) ; The following declaration allows a check at translate time that any part ; (satisfies pred) of x is such that pred is a unary function symbol in the ; current world. An optimization in dcl-guardian guarantees that this ; declaration won't generate any proof obligations. ; WARNING: Do not change the form of this declaration without visiting the ; corresponding code for the-fn in chk-dcl-lst and dcl-guardian. (declare (type (or t ,x) var)) (the-check ,guard ',x var)))))) #+acl2-loop-only (defmacro the (x y) ":Doc-Section ACL2::ACL2-built-ins run-time type check~/ ~c[(The typ val)] checks that ~c[val] satisfies the type specification ~c[typ] (~pl[type-spec]). An error is caused if the check fails, and otherwise, ~c[val] is the value of this expression. Here are some examples. ~bv[] (the integer 3) ; returns 3 (the (integer 0 6) 3) ; returns 3 (the (integer 0 6) 7) ; causes an error (see below for exception) ~ev[] ~l[type-spec] for a discussion of the legal type specifications. There is an exception to the rule that failure of the type-check causes an error: there is no error when ~il[guard]-checking has been turned off with ~c[:set-guard-checking :NONE] or ~c[(with-guard-checking :NONE ...)]. ~l[set-guard-checking] and ~pl[with-guard-checking].~/ The following remark is for those who verify guards for their functions (~pl[guard] and ~pl[verify-guards]). We remark that a call of ~c[(the TYPE EXPR)] in the body of a function definition generates a guard proof obligation that the type, ~c[TYPE], holds for the value of the expression, ~c[EXPR]. Consider the following example. ~bv[] (defun f (x) (declare (xargs :guard (p1 x))) (if (p2 x) (the integer x) 17)) ~ev[] The ~il[guard] proof obligation generated for the ~c[THE] expression above is as follows. ~bv[] (implies (and (p1 x) (p2 x)) (let ((var x)) (integerp var))) ~ev[] ~c[THE] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (declare (xargs :guard (translate-declaration-to-guard x 'var nil))) (the-fn x y)) ; THEORY PROTO-PRIMITIVES ; Thus far it has been impossible to use the :in-theory hint in ; defthm and defun -- unless one wants to quote a theory -- because ; there are no primitives for getting all the names in the world. ; We here define the necessary basic functions, just so we can ; conveniently disable. See the extended discussion of theories ; in "other-events.lisp" where deftheory is defined. ; ARRAYS - efficient applicative arrays. ; We provide functions for accessing and updating both one and two ; dimensional arrays, with applicative semantics, but good access time ; to the most recently updated copy and usually constant update time. ; We first describe the one dimensional array data type. From the ; formal point of view, an array is simply an alist, i.e. a list of ; pairs. With one exception, the key (i.e., the car) of each pair is ; a nonnegative integer. However each array must have (at least) one ; pair whose car is :header and whose cdr is a keyword list, whose ; keys include :dimensions, :maximum-length, and :default. Thus, for ; example, the list '((1 . 2) (:header :dimensions (3) :maximum-length ; 7 :default a) (0 . 6)) represents the sequence #s(6 2 7). In the ; case of a one dimensional array, the dimension is a list of length ; one which is a nonnegative integer one greater than the maximum ; permitted index. (Other keywords, e.g. :purpose, for ; identification, are permitted and ignored.) Formally speakign, to ; find the value of a non-negative integer key in such an alist, we ; search the alist (with the function aref1) for the first pair whose ; car matches the key. If such a pair is found, then aref1 returns ; the cdr of the pair; otherwise aref1 returns the value associated ; with the :default key. It is illegal to give aref1 an an index ; equal to or greater than the car of the value associated with the ; :dimensions key. In the normal case, updating happens by simply ; consing a new pair on to the alist with the function aset1. ; However, when the list resulting from such a cons has length greater ; than the value associated with the :maximum-length key, the alist is ; ``compressed'' back to an alist of minimal length, but with the same ; aref1 search semantics. ; For efficiency, the user is asked to call the array functions with ; an additional argument, a symbol, called the ``name'' of the given ; array. From the point of view of the formal semantics, the name ; argument is simply and completely ignored. However, as with the ; implementation of property lists described above, the name provides ; a hint about where to find a ``real'' Common Lisp array that may ; currently represent the given alist, in which case an array access ; can go quite quickly because the real array may be accessed ; directly. ; A further requirement for fast access is that the user initially ; alert the implementation to the desire to make fast accesses by ; calling the function compress1 on the array (and the desired name). ; compress1 then associates with the alist (under the name) a ``real'' ; array. Compress1 returns a list that begins with the header and has ; its other elements in key-ascending order unless otherwise indicated ; by the hearder, with aref1-irrelevant pairs deleted. If the alist ; is already in this normal form, then no consing is done. If there ; is already an array associated with the given name, and if it ; happens to have the desired length, then no array allocation is done ; but instead that array is ``stolen''. ; In the usual case, whenever an array is updated (with aset1), the ; ``real'' array which acts as its shadow and supports efficient ; access, is set to support the ``new'' array, and no longer supports ; the ``old'' array. Thus one must, for efficiency's sake, be ; extremely conscious of the usual order of Common Lisp evaluation. ; For two dimensional arrays, the value of the key :dimensions should ; be a list of two positive integers and the aset2 and aref2 function ; take two indices. ; The following constant was originally introduced in order to ; "require that array indices fit into 32 bits so that some compilers ; can lay down faster code. In the case of two dimensional arrays, we ; require that the product of legal indices fit into 32 bits." In ; fact, we now make stronger requirements based on the ; array-total-size-limit and array-dimension-limit of the underlying ; Common Lisp implementation, as enforced by make-array$, whose ; definition follows shortly after this. (defconst *maximum-positive-32-bit-integer* (1- (expt 2 31))) #-acl2-loop-only (defconst *our-array-total-size-limit* ; GCL 2.3.8 has a bug that defines array-total-size-limit to be a symbol, ; 'ARRAY-DIMENSION-LIMIT. (Presumably the intention was to define ; array-total-size-limit to be the value of that symbol.) So we define our own ; version of array-total-size-limit. (if (eql array-total-size-limit 'ARRAY-DIMENSION-LIMIT) array-dimension-limit array-total-size-limit)) #-acl2-loop-only (defun-one-output chk-make-array$ (dimensions form) (or (let* ((dimensions (if (integerp dimensions) (list dimensions) dimensions))) (and (true-listp dimensions) (do ((tl dimensions (cdr tl))) ((null tl) t) (let ((dim (car dimensions))) (or (and (integerp dim) (<= 0 dim) (< dim array-dimension-limit)) (return nil)))) (< (let ((prod 1)) (do ((tl dimensions (cdr tl))) ((null tl)) (setq prod (* prod (car dimensions)))) prod) *our-array-total-size-limit*))) (illegal 'make-array$ "The dimensions of an array must obey restrictions of ~ the underlying Common Lisp: each must be a ~ non-negative integer less than the value of ~ array-dimension-limit (here, ~x0) and their product ~ must be less than the value of array-total-size-limit ~ (here, ~x1). The call ~x2, which has dimensions ~x3, ~ is thus illegal." (list (cons #\0 array-dimension-limit) (cons #\1 array-total-size-limit) (cons #\2 form) (cons #\3 dimensions))))) #-acl2-loop-only (defmacro make-array$ (&whole form dimensions &rest args) ; Common Lisp implementations are supposed to have limits on the dimensions of ; arrays: array-dimension-limit is a strict bound on each dimension, and ; array-total-size-limit is a strict bound on the product of the dimensions. ; But, we do not want to rely on the implementation to signal an error in such ; cases (as opposed to returning garbage or corrupting the image), let alone ; provide a useful error message. So we provide this function for creation of ; arrays. ; In case we find the following information useful later, here is a summary of ; the above constants in various 32-bit lisps, observed many years ago as of ; the time you are reading this comment. ; Lisp array-dimension-limit array-total-size-limit ; --------------- --------------------- ---------------------- ; CLISP 2.30 16777216 [2^24] 16777216 [2^24] ; CMUCL 18e 536870911 [2^29-1] 536870911 [2^29-1] ; SBCL 0.0 536870911 [2^29-1] 536870911 [2^29-1] ; GCL 2.5.0 2147483647 [2^31-1] 2147483647 [2^31-1] ; LISPWORKS 4.2.7 8388607 [2^23-1] 2096896 [2^21-256] ; Allegro CL 6.2 16777216 [2^24] 16777216 [2^24] ; MCL 4.2 16777216 [2^24] 16777216 [2^24] ; OpenMCL Version (Beta: Darwin) 0.13.6 (CCL): ; 16777216 [2^24] 16777216 [2^24] ; We go through some effort to find violations at compile time, partly for ; efficiency but mostly in order to provide compile-time feedback when there is ; a problem. (declare (ignore args)) (cond ((integerp dimensions) (prog2$ (chk-make-array$ dimensions (kwote form)) `(make-array ,@(cdr form)))) ((and (true-listp dimensions) ; (quote dims) (equal (length dimensions) 2) (eq (car dimensions) 'quote)) (prog2$ (chk-make-array$ (cadr dimensions) (kwote form)) `(make-array ,@(cdr form)))) (t `(prog2$ (chk-make-array$ ,dimensions ',form) (make-array ,@(cdr form)))))) ; For 1 and 2 dimensional arrays, there may be a property, 'acl2-array, stored ; under a symbol name. If so, this property has is a list of length four, ; (object actual-array to-go-array header), where object is an alist; ; actual-array, is the current ``real'' array associated with object under ; name; to-go-array is an array of length one whose content is the number of ; additional conses that may be added before compresses is required; and header ; is the first pair beginning with :header in object. (To-go-array is kept as ; an array rather than as a mere integer in order to avoid number boxing.) ; We use a one-slot cache for efficiency; see the Essay on Array Caching. #-acl2-loop-only (progn ; Essay on Array Caching ; We use the following approach, developed by Jared Davis and Sol Swords, to ; speed up ACL2 Arrays by avoiding (get name 'acl2-array) in the common case ; that you are reading/writing from the same array. We basically just add a ; one-slot cache, stored in the special *acl2-array-cache*. This is a ; performance win (on CCL, at least) because getting a property seems to be ; more expensive than getting a special. We could try this on other Lisps too, ; e.g., with these loops: ; ; (defparameter *foo* 1) ; (time ; (loop for i fixnum from 1 to 100000000 do (consp *foo*))) ; 0.07 secs ; (time ; (loop for i fixnum from 1 to 100000000 do (get 'consp 'sally))) ; 1.39 secs ; ; Our approach is simply to use macros in place of direct access to property ; lists, as follows. ; ; (get name 'acl2-array) --> (get-acl2-array-property name) ; (setf (get name 'acl2-array) prop) --> (set-acl2-array-property name prop) ; Finally, we inline aref1 and aref2. To see why, consider the following ; timing results. In each case, we started with ACL2 Version_4.3 built on CCL. ; The four results are based on two dimensions: either loading a patch file or ; not that implements our one-slot cache, and either inlining aref1 or not. ; The test run was the one contributed by Jared Davis and Sol Swords that is ; exhibited in a comment in set-acl2-array-property. ; 16.1 ; no patch ; 8.9 ; patch but no inline ; 11.6 ; no patch, but inline ; 4.3 ; patch and inline ; #+ACL2-PAR note: Unsurpisingly, when we add the semi-necessary locking to the ; array caching scheme (alternatively, we could investigate using a ; compare-and-swap-based mechanism like atomic increments), we experience a ; very large slow down. In Rager's experiment, it was about 40x slower. This ; is a terrible performance penalty, so in #+ACL2-PAR, we do not use array ; caching. (defparameter *acl2-array-cache* ; This special is always the same cons, but its car and cdr may be ; destructively modified. Its value always has the form (name . prop), where ; name is a symbol and prop is either nil or (get name 'acl2-array). (cons nil nil)) (defmacro set-acl2-array-property (name prop) ; Use this macro instead of (setf (get name 'acl2-array) prop). We update the ; 'acl2-array property of name, and install (name . prop) into the array cache. ; See the Essay on Array Caching. ; We are tempted to handle name as we handle prop, by let-binding name below. ; However, by using ,name directly we have reduced the time from 5.0 seconds to ; 4.3 seconds in the following test from Jared Davis and Sol Swords. ; (defun count-down (n) ; (if (zp n) ; nil ; (cons (- n 1) ; (count-down (- n 1))))) ; ; (defconst *test-array* ; (compress1 '*test-array* ; (cons (list :HEADER ; :DIMENSIONS (list 100) ; :MAXIMUM-LENGTH (+ 100 1) ; :DEFAULT 0 ; :NAME '*test-array*) ; (pairlis$ (count-down 100) ; (make-list 100))))) ; ; (let ((arr *test-array*)) ; (time (loop for i fixnum from 1 to 1000000000 do ; (aref1 '*test-array* arr 10)))) ; Therefore, we use ,name directly but add the following compile-time check to ; ensure that ,name refers to the given formal parameter rather than to the ; let-bound prop or cache. (when (or (not (symbolp name)) (eq name 'prop) (eq name '*acl2-array-cache*)) (error "Bad call, ~s: See set-acl2-array-property" `(set-acl2-array-property ,name ,prop))) #-acl2-par `(let ((prop ,prop) (cache *acl2-array-cache*)) (setf (cdr cache) nil) ; Invalidate the cache in case of interrupts. (setf (get ,name 'acl2-array) prop) (setf (car cache) ,name) (setf (cdr cache) prop)) #+acl2-par `(setf (get ,name 'acl2-array) ,prop)) (defmacro get-acl2-array-property (name) ; Use this macro instead of (get name 'acl2-array). We get the 'acl2-array ; property for name from the cache if possible, or from the property list if it ; is not cached. On a cache miss, we update the cache so that it points to the ; newly accessed array. See the Essay on Array Caching. ; See set-acl2-array-property for an explanation of the following compile-time ; check. (when (or (not (symbolp name)) (eq name 'prop) (eq name '*acl2-array-cache*)) (error "Bad call, ~s: See set-acl2-array-property" `(get-acl2-array-property ,name))) #-acl2-par `(let ((cache *acl2-array-cache*)) (or (and (eq ,name (car cache)) (cdr cache)) (let ((prop (get ,name 'acl2-array))) (setf (cdr cache) nil) ; Invalidate the cache in case of interrupts. (setf (car cache) ,name) (setf (cdr cache) prop)))) #+acl2-par `(get ,name 'acl2-array)) ) (defun bounded-integer-alistp (l n) ; Check that l is a true-list of pairs, (n . x), where each n is ; either :header or a nonnegative integer less than n. (declare (xargs :guard t)) (cond ((atom l) (null l)) (t (and (consp (car l)) (let ((key (caar l))) (and (or (eq key :header) (and (integerp key) (integerp n) (>= key 0) (< key n))) (bounded-integer-alistp (cdr l) n))))))) (defthm bounded-integer-alistp-forward-to-eqlable-alistp (implies (bounded-integer-alistp x n) (eqlable-alistp x)) :rule-classes :forward-chaining) (defun keyword-value-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for true lists whose even-position elements are keywords~/ ~c[(keyword-value-listp l)] is true if and only if ~c[l] is a list of even length of the form ~c[(k1 a1 k2 a2 ... kn an)], where each ~c[ki] is a keyword. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom l) (null l)) (t (and (keywordp (car l)) (consp (cdr l)) (keyword-value-listp (cddr l)))))) (defthm keyword-value-listp-forward-to-true-listp (implies (keyword-value-listp x) (true-listp x)) :rule-classes :forward-chaining) (defun assoc-keyword (key l) ":Doc-Section ACL2::ACL2-built-ins look up key in a ~ilc[keyword-value-listp]~/ If ~c[l] is a list of even length of the form ~c[(k1 a1 k2 a2 ... kn an)], where each ~c[ki] is a keyword, then ~c[(assoc-keyword key l)] is the first tail of ~c[l] starting with ~c[key] if key is some ~c[ki], and is ~c[nil] otherwise.~/ The ~il[guard] for ~c[(assoc-keyword key l)] is ~c[(keyword-value-listp l)]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (keyword-value-listp l))) (cond ((endp l) nil) ((eq key (car l)) l) (t (assoc-keyword key (cddr l))))) ; The following seems useful, though at this point its use isn't clear. (defthm keyword-value-listp-assoc-keyword (implies (keyword-value-listp l) (keyword-value-listp (assoc-keyword key l))) :rule-classes ((:forward-chaining :trigger-terms ((assoc-keyword key l))))) (defthm consp-assoc-equal ; This type-prescription rule (formerly two rules, consp-assoc-eq and ; consp-assoc) may have been partly responsible for a 2.5% real-time regression ; slowdown (3.2% user time) after implementing equality variants, after ; Version_4.2. In particular, it contributed to a significant slowdown in ; example4 of examples.lisp in community book ; books/workshops/2000/moore-manolios/partial-functions/tjvm.lisp. So, we are ; disabling it by default, later below. ; We include a corresponding :forward-chaining rule, which seems much less ; expensive, but still allows the event aref1 to be admitted. (implies (alistp l) (or (consp (assoc-equal name l)) (equal (assoc-equal name l) nil))) :rule-classes (:type-prescription (:forward-chaining :trigger-terms ((assoc-equal name l))))) #+acl2-loop-only (defmacro f-get-global (x st) ":Doc-Section ACL2::ACL2-built-ins get the value of a global variable in ~ilc[state]~/ ~bv[] Examples: (+ (f-get-global 'y state) 1) (f-put-global 'a (aset1 'ascii-map-array (f-get-global 'a state) 66 'Upper-case-B) state)~/ General Form: (f-get-global 'symbol state) ~ev[] where ~c[symbol] is any symbol to which you have ~ilc[assign]ed a global value. The macro ~ilc[@] is closely related to ~c[f-get-global]: ~c[(@ var)] macroexpands to ~c[(f-get-global 'var state)]. The macro ~ilc[f-get-global] makes it convenient to set the value of a symbol. The ~c[:]~ilc[ubt] operation has no effect on the ~c[global-table] of ~ilc[state]. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.~/" (list 'get-global x st)) #-acl2-loop-only (progn ; With f-get-global and set-difference-eq defined, we are ready to define ; raw Lisp support for defpkg-raw. (defun our-import (syms pkg) ; We have seen a case in which Allegro CL 8.0 spent about 20% of the time in ; IMPORT, on an include-book (with lots of nested include-books, and 20 defpkg ; forms executed altogether). That time was reduced to near 0 by using the ; present function, OUR-IMPORT, in place of IMPORT, presumably because ; (according to the profiler) calls to EXCL::INTERNAL-STRING= were avoided, ; probably in favor of hashing. We saw no significant change in time in GCL, ; however, so we exclude GCL and any non-ANSI (hence maybe no LOOP) Common Lisp ; from this enhancement. It might be worthwhile to consider other Common Lisp ; implementations besides Allegro CL and GCL. Perhaps Allegro CL will speed up ; its handling of IMPORT in future implementations (we have sent email to Franz ; Inc. about this), in which case we might consider deleting this function. #+(and (not gcl) cltl2) (loop for sym in syms do (import (or sym (list sym)) pkg)) #-(and (not gcl) cltl2) (import syms pkg)) (defvar *defpkg-virgins* nil) (defun check-proposed-imports (name package-entry proposed-imports) (cond ((equal proposed-imports (package-entry-imports package-entry)) ; The package has already been built in Common Lisp and the imports are ; identical. There is nothing for us to do. nil) (t ; The package has already been built in Common Lisp but with the wrong imports. ; There is nothing we can do. We do not want to unintern any symbols in it ; because we may thus render bad some saved logical worlds. See :DOC ; package-reincarnation-import-restrictions. In addition, see the Lisp comment ; that is part of that deflabel (but which is not actually part of the ; ACL2 documentation). (let* ((old-book-path (reverse (unrelativize-book-path (package-entry-book-path package-entry) (f-get-global 'system-books-dir *the-live-state*)))) (current-book-path (reverse (append (strip-cars (symbol-value 'acl2::*load-compiled-stack*)) (global-val 'include-book-path (w *the-live-state*))))) (old-imports (package-entry-imports package-entry)) (proposed-not-old (set-difference-eq proposed-imports old-imports)) (old-not-proposed (set-difference-eq old-imports proposed-imports)) (current-package (f-get-global 'current-package *the-live-state*))) (interface-er "~%We cannot reincarnate the package ~x0 because it was previously ~ defined with a different list of imported symbols.~|~%The previous ~ definition was made ~#1~[at the top level.~|~/in the portcullis of ~ the last of the book at the end of the following sequence of included ~ books, which starts with the top-most book at the front of the list ~ and works down to the book that defined the package.~|~% ~ ~F2~|~]~%The proposed definition is being made ~#3~[at the top ~ level.~|~/in the portcullis of the last of the book at the end of the ~ following sequence of included books, which starts with the top-most ~ book at the front of the list and works down to the book that is ~ trying to define the package.~|~% ~F4~|~]~%~#5~[The previous ~ definition imported the following list of symbols that are not ~ imports of the proposed definition, and is shown with respect to ~ current package ~x9:~|~% ~x6.~|~%~/~]~#7~[The proposed definition ~ imports the following list of symbols not imported by the previous ~ definition, and is shown with respect to current package ~x9:~|~% ~ ~x8.~|~%~/~]See :DOC package-reincarnation-import-restrictions." name (if old-book-path 1 0) old-book-path (if current-book-path 1 0) current-book-path (if old-not-proposed 0 1) old-not-proposed (if proposed-not-old 0 1) proposed-not-old current-package ))))) (defun-one-output defpkg-raw1 (name imports book-path event-form) (let ((package-entry (find-package-entry name *ever-known-package-alist*)) (pkg (find-package name)) (global-name (concatenate 'string acl2::*global-package-prefix* name)) (*1*-name (concatenate 'string acl2::*1*-package-prefix* name)) (proposed-imports (sort-symbol-listp imports))) (assert pkg) ; see defpkg-raw ; We bind proposed-imports to the value of the imports argument. We do not ; want to evaluate it more than once below. We DO reference, and hence ; evaluate, name more than once below. But name must be an explicit string ; constant. (cond (package-entry ; There is nothing for us to do other than to do a check. (check-proposed-imports name package-entry proposed-imports) name) ((not (member-equal name *defpkg-virgins*)) ; The package has been built in this Common Lisp but not by defpkg-raw1. It ; may be new because of the defpackage form in defpkg-raw, in which case it is ; an element of *defpkg-virgins*. Otherwise, it was defined in Common Lisp ; outside ACL2, and we should cause an error. (error "~%It is illegal to defpkg ~s because a package of that name ~ already exists in this lisp.~%" name)) (t (assert (not (assoc-equal name *package-alist*))) (let* ((incomplete-p t) (saved-ever-known-package-alist *ever-known-package-alist*) (wrld (w *the-live-state*)) (not-boot-strap (not (getprop 'boot-strap-flg 'global-value nil 'current-acl2-world wrld)))) (setq *defpkg-virgins* (remove1-equal name *defpkg-virgins*)) (unwind-protect (progn (setq *ever-known-package-alist* (cons (make-package-entry :name name :imports proposed-imports :book-path ; We store a suitable path for use by check-proposed-imports. (and not-boot-strap (append book-path (strip-cars (symbol-value 'acl2::*load-compiled-stack*)) (getprop 'include-book-path 'global-value nil 'current-acl2-world wrld))) :defpkg-event-form event-form) *ever-known-package-alist*)) (when proposed-imports ; Without the qualifier above, clisp imports nil if proposed-imports = nil. (our-import proposed-imports (find-package name))) ; So at this point we have set the package's imports appropriately. We now ; handle the dual packages in which the state globals and executable ; counterparts of symbols from pkg will reside. We do not reinitialize these ; hidden variables if we are recovering from an error or booting. (cond ((and (not *in-recover-world-flg*) not-boot-strap) (cond ((find-package global-name) (do-symbols (sym (find-package global-name)) (makunbound sym))) (t (make-package global-name :use nil))) (cond ((find-package *1*-name) nil) (t (make-package *1*-name :use nil))))) (setq incomplete-p nil) name) (when incomplete-p (setq *ever-known-package-alist* saved-ever-known-package-alist) (do-symbols (sym pkg) (unintern sym)) (delete-package (find-package name))))))))) (defun package-has-no-imports (name) (let ((pkg (find-package name))) (do-symbols (sym pkg) (when (not (eq (symbol-package sym) pkg)) (return-from package-has-no-imports nil)))) t) #-acl2-loop-only (defmacro maybe-make-package (name) ; When we moved to Version_4.3, with LispWorks once again a supported host ; Lisp, we modified the macro maybe-introduce-empty-pkg-1 to avoid the use of ; defpackage; see the comment in that macro. Unfortunately, the new approach ; didn't work for CMUCL (at least, for version 19e). The following example ; shows why; even with an eval-when form specifying :compile-toplevel, the ; compiled code seems to skip the underlying package-creation form, as shown ; below. Therefore we revert to the use of defpackage for CMUCL, which appears ; not to cause problems. ; % cat pkg-bug-cmucl.lisp ; ; (in-package "CL-USER") ; ; (eval-when (:load-toplevel :execute :compile-toplevel) ; (cond ((not (find-package "MYPKG")) ; (print "*** About to make package ***") ; (terpri) ; (make-package "MYPKG" :use nil)))) ; ; (defparameter *foo* 'mypkg::x) ; % /projects/acl2/lisps/cmucl-19e-linux/bin/cmucl ; CMU Common Lisp 19e (19E), running on kindness ; With core: /v/filer4b/v11q001/acl2/lisps/cmucl-19e-linux/lib/cmucl/lib/lisp.core ; Dumped on: Thu, 2008-05-01 11:56:07-05:00 on usrtc3142 ; See for support information. ; Loaded subsystems: ; Python 1.1, target Intel x86 ; CLOS based on Gerd's PCL 2004/04/14 03:32:47 ; * (load "pkg-bug-cmucl.lisp") ; ; ; Loading #P"/v/filer4b/v41q001/kaufmann/temp/pkg-bug-cmucl.lisp". ; ; "*** About to make package ***" ; T ; * (compile-file "pkg-bug-cmucl.lisp") ; ; ; Python version 1.1, VM version Intel x86 on 04 JUL 11 09:57:13 am. ; ; Compiling: /v/filer4b/v41q001/kaufmann/temp/pkg-bug-cmucl.lisp 04 JUL 11 09:56:24 am ; ; ; Byte Compiling Top-Level Form: ; ; ; pkg-bug-cmucl.x86f written. ; ; Compilation finished in 0:00:00. ; ; #P"/v/filer4b/v41q001/kaufmann/temp/pkg-bug-cmucl.x86f" ; NIL ; NIL ; * (quit) ; % /projects/acl2/lisps/cmucl-19e-linux/bin/cmucl ; CMU Common Lisp 19e (19E), running on kindness ; With core: /v/filer4b/v11q001/acl2/lisps/cmucl-19e-linux/lib/cmucl/lib/lisp.core ; Dumped on: Thu, 2008-05-01 11:56:07-05:00 on usrtc3142 ; See for support information. ; Loaded subsystems: ; Python 1.1, target Intel x86 ; CLOS based on Gerd's PCL 2004/04/14 03:32:47 ; * (load "pkg-bug-cmucl.x86f") ; ; ; Loading #P"/v/filer4b/v41q001/kaufmann/temp/pkg-bug-cmucl.x86f". ; ; ; Error in function LISP::FOP-PACKAGE: The package "MYPKG" does not exist. ; [Condition of type SIMPLE-ERROR] ; ; Restarts: ; 0: [CONTINUE] Return NIL from load of "pkg-bug-cmucl.x86f". ; 1: [ABORT ] Return to Top-Level. ; ; Debug (type H for help) ; ; (LISP::FOP-PACKAGE) ; Source: Error finding source: ; Error in function DEBUG::GET-FILE-TOP-LEVEL-FORM: Source file no longer exists: ; target:code/load.lisp. ; 0] #-cmu `(when (not (find-package ,name)) (make-package ,name :use nil)) #+cmu `(defpackage ,name (:use))) (defmacro maybe-introduce-empty-pkg-1 (name) ; It appears that GCL, requires a user::defpackage (non-ANSI case) or ; defpackage (ANSI case; this may be the same as user::defpackage) form near ; the top of a file in order to read the corresponding compiled file. For ; example, an error occurred upon attempting to load the community books file ; books/data-structures/defalist.o after certifying the corresponding book ; using GCL, because the form (MAYBE-INTRODUCE-EMPTY-PKG-1 "U") near the top of ; the file was insufficient to allow reading a symbol in the "U" package ; occurring later in the corresponding source file. ; On the other hand, the CL HyperSpec does not pin down the effect of ; defpackage when a package already exists. Indeed, the defpackage approach ; that we use for GCL does not work for LispWorks 6.0. ; So, we have quite different definitions of this macro for GCL and LispWorks. ; All other Lisps we have encountered seem happy with the approach we have ; adopted for Lispworks, so we adopt that approach for them, too. #-gcl `(eval-when #+cltl2 (:load-toplevel :execute :compile-toplevel) #-cltl2 (load eval compile) ; though probably #-gcl implies #+cltl2 (progn (maybe-make-package ,name) (maybe-make-package ,(concatenate 'string acl2::*global-package-prefix* name)) (maybe-make-package ,(concatenate 'string acl2::*1*-package-prefix* name)))) #+gcl (let ((defp #+cltl2 'defpackage #-cltl2 'user::defpackage)) `(progn (,defp ,name (:use)) (,defp ,(concatenate 'string acl2::*global-package-prefix* name) (:use)) (,defp ,(concatenate 'string acl2::*1*-package-prefix* name) (:use))))) (defmacro maybe-introduce-empty-pkg-2 (name) `(when (and (not (member ,name *defpkg-virgins* :test 'equal)) (not (assoc ,name *ever-known-package-alist* :test 'equal)) (package-has-no-imports ,name)) (push ,name *defpkg-virgins*))) (defmacro defpkg-raw (name imports book-path event-form) ; Defpkg checks that name is a string. Event-form is a cons. So we don't need ; to worry about capture below. `(let ((package-entry (find-package-entry ,name *ever-known-package-alist*)) (*safe-mode-verified-p* t)) (cond ((and package-entry (let ((old-event-form (package-entry-defpkg-event-form package-entry))) (and (equal (cadr old-event-form) (cadr ,event-form)) (equal (caddr old-event-form) (caddr ,event-form))))) ; This shorcut is potentially a big concern! We are checking that the name and ; term of the defpkg form agrees with an old defpkg form. But these two forms ; may have been evaluated in different worlds! Nevertheless, for now we trust ; that they really are equivalent, for efficiency's sake. Defpkg-fn will call ; chk-acceptable-defpkg, which will call ; chk-package-reincarnation-import-restrictions, and if there is a discrepancy ; between the current and old package, we'll find out then. ,name) (t (maybe-introduce-empty-pkg-1 ,name) (maybe-introduce-empty-pkg-2 ,name) (defpkg-raw1 ,name ,imports ,book-path ,event-form))))) ) #-acl2-loop-only (defun-one-output slow-array-warning (fn nm) (let ((action (f-get-global 'slow-array-action *the-live-state*))) (when action (format *error-output* "~%~%**********************************************************~%~ Slow Array Access! A call of ~a on an array named~%~ ~a is being executed slowly. See :DOC slow-array-warning.~%~ **********************************************************~%~%" fn nm) (when (not (eq action :warning)) (format *error-output* "To avoid the following break and get only the above warning:~%~s~%" '(assign slow-array-action :warning)) (break$))))) (deflabel arrays :doc ":Doc-Section ACL2::Programming an introduction to ACL2 arrays~/ Below we begin a detailed presentation of ACL2 arrays. ACL2's single-threaded objects (~pl[stobj]) provide a similar functionality that is generally more efficient when there are updates (writes), but is also more restrictive. ~/ ~l[arrays-example] for a brief introduction illustrating the use of ACL2 arrays. ACL2 provides relatively efficient 1- and 2-dimensional arrays. Arrays are awkward to provide efficiently in an applicative language because the programmer rightly expects to be able to ``modify'' an array object with the effect of changing the behavior of the element accessing function on that object. This, of course, does not make any sense in an applicative setting. The element accessing function is, after all, a function, and its behavior on a given object is immutable. To ``modify'' an array object in an applicative setting we must actually produce a new array object. Arranging for this to be done efficiently is a challenge to the implementors of the language. In addition, the programmer accustomed to the von Neumann view of arrays must learn how to use immutable applicative arrays efficiently. In this note we explain 1-dimensional arrays. In particular, we explain briefly how to create, access, and ``modify'' them, how they are implemented, and how to program with them. 2-dimensional arrays are dealt with by analogy. ~em[The Logical Description of ACL2 Arrays] An ACL2 1-dimensional array is an object that associates arbitrary objects with certain integers, called ``indices.'' Every array has a dimension, ~c[dim], which is a positive integer. The indices of an array are the consecutive integers from ~c[0] through ~c[dim-1]. To obtain the object associated with the index ~c[i] in an array ~c[a], one uses ~c[(aref1 name a i)]. ~c[Name] is a symbol that is irrelevant to the semantics of ~ilc[aref1] but affects the speed with which it computes. We will talk more about array ``names'' later. To produce a new array object that is like ~c[a] but which associates ~c[val] with index ~c[i], one uses ~c[(aset1 name a i val)]. An ACL2 1-dimensional array is actually an alist. There is no special ACL2 function for creating arrays; they are generally built with the standard list processing functions ~ilc[list] and ~ilc[cons]. However, there is a special ACL2 function, called ~ilc[compress1], for speeding up access to the elements of such an alist. We discuss ~ilc[compress1] later. One element of the alist must be the ``header'' of the array. The ~il[header] of a 1-dimensional array with dimension ~c[dim] is of the form: ~bv[] (:HEADER :DIMENSIONS (dim) :MAXIMUM-LENGTH max :DEFAULT obj ; optional :NAME name ; optional :ORDER order ; optional values are < (the default), >, or :none/nil ). ~ev[] ~c[Obj] may be any object and is called the ``default value'' of the array. ~ilc[Max] must be an integer greater than ~c[dim]. ~c[Name] must be a symbol. The ~c[:]~ilc[default] and ~c[:name] entries are optional; if ~c[:]~ilc[default] is omitted, the default value is ~c[nil]. The function ~ilc[header], when given a name and a 1- or 2-dimensional array, returns the ~il[header] of the array. The functions ~ilc[dimensions], ~ilc[maximum-length], and ~ilc[default] are similar and return the corresponding fields of the ~il[header] of the array. The role of the ~c[:]~ilc[dimensions] field is obvious: it specifies the legal indices into the array. The roles played by the ~c[:]~ilc[maximum-length] and ~c[:]~ilc[default] fields are described below. Aside from the ~il[header], the other elements of the alist must each be of the form ~c[(i . val)], where ~c[i] is an integer and ~c[0 <= i < dim], and ~c[val] is an arbitrary object. The ~c[:order] field of the header is ignored for 2-dimensional arrays. For 1-dimensional arrays, it specifies the order of keys (~c[i], above) when the array is compressed as with ~ilc[compress1], as described below. An ~c[:order] of ~c[:none] or ~c[nil] specifies no reordering of the alist by ~ilc[compress1], and an order of ~c[>] specifies reordering by ~ilc[compress1] so that keys are in descending order. Otherwise, the alist is reordered by ~ilc[compress1] so that keys are in ascending order. ~c[(Aref1 name a i)] is ~il[guard]ed so that ~c[name] must be a symbol, ~c[a] must be an array and ~c[i] must be an index into ~c[a]. The value of ~c[(aref1 name a i)] is either ~c[(cdr (assoc i a))] or else is the default value of ~c[a], depending on whether there is a pair in ~c[a] whose ~ilc[car] is ~c[i]. Note that ~c[name] is irrelevant to the value of an ~ilc[aref1] expression. You might ~c[:pe aref1] to see how simple the definition is. ~c[(Aset1 name a i val)] is ~il[guard]ed analogously to the ~ilc[aref1] expression. The value of the ~ilc[aset1] expression is essentially ~c[(cons (cons i val) a)]. Again, ~c[name] is irrelevant. Note ~c[(aset1 name a i val)] is an array, ~c[a'], with the property that ~c[(aref1 name a' i)] is ~c[val] and, except for index ~c[i], all other indices into ~c[a'] produce the same value as in ~c[a]. Note also that if ~c[a] is viewed as an alist (which it is) the pair ``binding'' ~c[i] to its old value is in ~c[a'] but ``covered up'' by the new pair. Thus, the length of an array grows by one when ~ilc[aset1] is done. Because ~ilc[aset1] covers old values with new ones, an array produced by a sequence of ~ilc[aset1] calls may have many irrelevant pairs in it. The function ~ilc[compress1] can remove these irrelevant pairs. Thus, ~c[(compress1 name a)] returns an array that is equivalent (vis-a-vis ~ilc[aref1]) to ~c[a] but which may be shorter. For technical reasons, the alist returned by ~ilc[compress1] may also list the pairs in a different order than listed in ~c[a]. To prevent arrays from growing excessively long due to repeated ~ilc[aset1] operations, ~ilc[aset1] actually calls ~ilc[compress1] on the new alist whenever the length of the new alist exceeds the ~c[:]~ilc[maximum-length] entry, ~ilc[max], in the ~il[header] of the array. See the definition of ~ilc[aset1] (for example by using ~c[:]~ilc[pe]). This is primarily just a mechanism for freeing up ~ilc[cons] space consumed while doing ~ilc[aset1] operations. Note however that this ~ilc[compress1] call is replaced by a hard error if the header specifies an ~c[:order] of ~c[:none] or ~c[nil]. This completes the logical description of 1-dimensional arrays. 2-dimensional arrays are analogous. The ~c[:]~ilc[dimensions] entry of the ~il[header] of a 2-dimensional array should be ~c[(dim1 dim2)]. A pair of indices, ~c[i] and ~c[j], is legal iff ~c[0 <= i < dim1] and ~c[0 <= j < dim2]. The ~c[:]~ilc[maximum-length] must be greater than ~c[dim1*dim2]. ~ilc[Aref2], ~ilc[aset2], and ~ilc[compress2] are like their counterparts but take an additional ~c[index] argument. Finally, the pairs in a 2-dimensional array are of the form ~c[((i . j) . val)]. ~em[The Implementation of ACL2 Arrays] Very informally speaking, the function ~ilc[compress1] ``creates'' an ACL2 array that provides fast access, while the function ~ilc[aref1] ``maintains'' fast access. We now describe this informal idea more carefully. ~ilc[Aref1] is essentially ~ilc[assoc]. If ~ilc[aref1] were implemented naively the time taken to access an array element would be linear in the dimension of the array and the number of ``assignments'' to it (the number of ~ilc[aset1] calls done to create the array from the initial alist). This is intolerable; arrays are ``supposed'' to provide constant-time access and change. The apparently irrelevant names associated with ACL2 arrays allow us to provide constant-time access and change when arrays are used in ``conventional'' ways. The implementation of arrays makes it clear what we mean by ``conventional.'' Recall that array names are symbols. Behind the scenes, ACL2 associates two objects with each ACL2 array name. The first object is called the ``semantic value'' of the name and is an alist. The second object is called the ``raw lisp array'' and is a Common Lisp array. When ~c[(compress1 name alist)] builds a new alist, ~c[a'], it sets the semantic value of ~c[name] to that new alist. Furthermore, it creates a Common Lisp array and writes into it all of the index/value pairs of ~c[a'], initializing unassigned indices with the default value. This array becomes the raw lisp array of ~c[name]. ~ilc[Compress1] then returns ~c[a'], the semantic value, as its result, as required by the definition of ~ilc[compress1]. When ~c[(aref1 name a i)] is invoked, ~ilc[aref1] first determines whether the semantic value of ~c[name] is ~c[a] (i.e., is ~ilc[eq] to the alist ~c[a]). If so, ~ilc[aref1] can determine the ~c[i]th element of ~c[a] by invoking Common Lisp's ~c[aref] function on the raw lisp array associated with name. Note that no linear search of the alist ~c[a] is required; the operation is done in constant time and involves retrieval of two global variables, an ~ilc[eq] test and ~c[jump], and a raw lisp array access. In fact, an ACL2 array access of this sort is about 5 times slower than a C array access. On the other hand, if ~c[name] has no semantic value or if it is different from ~c[a], then ~ilc[aref1] determines the answer by linear search of ~c[a] as suggested by the ~c[assoc-like] definition of ~ilc[aref1]. Thus, ~ilc[aref1] always returns the axiomatically specified result. It returns in constant time if the array being accessed is the current semantic value of the name used. The ramifications of this are discussed after we deal with ~ilc[aset1]. When ~c[(aset1 name a i val)] is invoked, ~ilc[aset1] does two ~ilc[cons]es to create the new array. Call that array ~c[a']. It will be returned as the answer. (In this discussion we ignore the case in which ~ilc[aset1] does a ~ilc[compress1].) However, before returning, ~ilc[aset1] determines if ~c[name]'s semantic value is ~c[a]. If so, it makes the new semantic value of ~c[name] be ~c[a'] and it smashes the raw lisp array of ~c[name] with ~c[val] at index ~c[i], before returning ~c[a'] as the result. Thus, after doing an ~ilc[aset1] and obtaining a new semantic value ~c[a'], all ~ilc[aref1]s on that new array will be fast. Any ~ilc[aref1]s on the old semantic value, ~c[a], will be slow. To understand the performance implications of this design, consider the chronological sequence in which ACL2 (Common Lisp) evaluates expressions: basically inner-most first, left-to-right, call-by-value. An array use, such as ~c[(aref1 name a i)], is ``fast'' (constant-time) if the alist supplied, ~c[a], is the value returned by the most recently executed ~ilc[compress1] or ~ilc[aset1] on the name supplied. In the functional expression of ``conventional'' array processing, all uses of an array are fast. The ~c[:name] field of the ~il[header] of an array is completely irrelevant. Our convention is to store in that field the symbol we mean to use as the name of the raw lisp array. But no ACL2 function inspects ~c[:name] and its primary value is that it allows the user, by inspecting the semantic value of the array ~-[] the alist ~-[] to recall the name of the raw array that probably holds that value. We say ``probably'' since there is no enforcement that the alist was compressed under the name in the ~il[header] or that all ~c[aset]s used that name. Such enforcement would be inefficient. ~em[Some Programming Examples] In the following examples we will use ACL2 ``global variables'' to hold several arrays. ~l[@], and ~pl[assign]. Let the ~ilc[state] global variable ~c[a] be the 1-dimensional compressed array of dimension ~c[5] constructed below. ~bv[] ACL2 !>(assign a (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero)))) ~ev[] Then ~c[(aref1 'demo (@ a) 0)] is ~c[zero] and ~c[(aref1 'demo (@ a) 1)] is ~c[uninitialized]. Now execute ~bv[] ACL2 !>(assign b (aset1 'demo (@ a) 1 'one)) ~ev[] Then ~c[(aref1 'demo (@ b) 0)] is ~c[zero] and ~c[(aref1 'demo (@ b) 1)] is ~c[one]. All of the ~ilc[aref1]s done so far have been ``fast.'' Note that we now have two array objects, one in the global variable ~c[a] and one in the global variable ~c[b]. ~c[B] was obtained by assigning to ~c[a]. That assignment does not affect the alist ~c[a] because this is an applicative language. Thus, ~c[(aref1 'demo (@ a) 1)] must ~st[still] be ~c[uninitialized]. And if you execute that expression in ACL2 you will see that indeed it is. However, a rather ugly comment is printed, namely that this array access is ``slow.'' The reason it is slow is that the raw lisp array associated with the name ~c[demo] is the array we are calling ~c[b]. To access the elements of ~c[a], ~ilc[aref1] must now do a linear search. Any reference to ~c[a] as an array is now ``unconventional;'' in a conventional language like Ada or Common Lisp it would simply be impossible to refer to the value of the array before the assignment that produced our ~c[b]. Now let us define a function that counts how many times a given object, ~c[x], occurs in an array. For simplicity, we will pass in the name and highest index of the array: ~bv[] ACL2 !>(defun cnt (name a i x) (declare (xargs :guard (and (array1p name a) (integerp i) (>= i -1) (< i (car (dimensions name a)))) :mode :logic :measure (nfix (+ 1 i)))) (cond ((zp (1+ i)) 0) ; return 0 if i is at most -1 ((equal x (aref1 name a i)) (1+ (cnt name a (1- i) x))) (t (cnt name a (1- i) x)))) ~ev[] To determine how many times ~c[zero] appears in ~c[(@ b)] we can execute: ~bv[] ACL2 !>(cnt 'demo (@ b) 4 'zero) ~ev[] The answer is ~c[1]. How many times does ~c[uninitialized] appear in ~c[(@ b)]? ~bv[] ACL2 !>(cnt 'demo (@ b) 4 'uninitialized) ~ev[] The answer is ~c[3], because positions ~c[2], ~c[3] and ~c[4] of the array contain that default value. Now imagine that we want to assign ~c['two] to index ~c[2] and then count how many times the 2nd element of the array occurs in the array. This specification is actually ambiguous. In assigning to ~c[b] we produce a new array, which we might call ~c[c]. Do we mean to count the occurrences in ~c[c] of the 2nd element of ~c[b] or the 2nd element of ~c[c]? That is, do we count the occurrences of ~c[uninitialized] or the occurrences of ~c[two]? If we mean the former the correct answer is ~c[2] (positions ~c[3] and ~c[4] are ~c[uninitialized] in ~c[c]); if we mean the latter, the correct answer is ~c[1] (there is only one occurrence of ~c[two] in ~c[c]). Below are ACL2 renderings of the two meanings, which we call ~c[[former~]] and ~c[[latter~]]. (Warning: Our description of these examples, and of an example ~c[[fast former~]] that follows, assumes that only one of these three examples is actually executed; for example, they are not executed in sequence. See ``A Word of Warning'' below for more about this issue.) ~bv[] (cnt 'demo (aset1 'demo (@ b) 2 'two) 4 (aref1 'demo (@ b) 2)) ; [former] (let ((c (aset1 'demo (@ b) 2 'two))) ; [latter] (cnt 'demo c 4 (aref1 'demo c 2))) ~ev[] Note that in ~c[[former~]] we create ~c[c] in the second argument of the call to ~c[cnt] (although we do not give it a name) and then refer to ~c[b] in the fourth argument. This is unconventional because the second reference to ~c[b] in ~c[[former~]] is no longer the semantic value of ~c[demo]. While ACL2 computes the correct answer, namely ~c[2], the execution of the ~ilc[aref1] expression in ~c[[former~]] is done slowly. A conventional rendering with the same meaning is ~bv[] (let ((x (aref1 'demo (@ b) 2))) ; [fast former] (cnt 'demo (aset1 'demo (@ b) 2 'two) 4 x)) ~ev[] which fetches the 2nd element of ~c[b] before creating ~c[c] by assignment. It is important to understand that ~c[[former~]] and ~c[[fast former~]] mean exactly the same thing: both count the number of occurrences of ~c[uninitialized] in ~c[c]. Both are legal ACL2 and both compute the same answer, ~c[2]. Indeed, we can symbolically transform ~c[[fast former~]] into ~c[[former~]] merely by substituting the binding of ~c[x] for ~c[x] in the body of the ~ilc[let]. But ~c[[fast former~]] can be evaluated faster than ~c[[former~]] because all of the references to ~c[demo] use the then-current semantic value of ~c[demo], which is ~c[b] in the first line and ~c[c] throughout the execution of the ~c[cnt] in the second line. ~c[[Fast former~]] is the preferred form, both because of its execution speed and its clarity. If you were writing in a conventional language you would have to write something like ~c[[fast former~]] because there is no way to refer to the 2nd element of the old value of ~c[b] after smashing ~c[b] unless it had been saved first. We turn now to ~c[[latter~]]. It is both clear and efficient. It creates ~c[c] by assignment to ~c[b] and then it fetches the 2nd element of ~c[c], ~c[two], and proceeds to count the number of occurrences in ~c[c]. The answer is ~c[1]. ~c[[Latter~]] is a good example of typical ACL2 array manipulation: after the assignment to ~c[b] that creates ~c[c], ~c[c] is used throughout. It takes a while to get used to this because most of us have grown accustomed to the peculiar semantics of arrays in conventional languages. For example, in raw lisp we might have written something like the following, treating ~c[b] as a ``global variable'': ~bv[] (cnt 'demo (aset 'demo b 2 'two) 4 (aref 'demo b 2)) ~ev[] which sort of resembles ~c[[former~]] but actually has the semantics of ~c[[latter~]] because the ~c[b] from which ~c[aref] fetches the 2nd element is not the same ~c[b] used in the ~c[aset]! The array ~c[b] is destroyed by the ~c[aset] and ~c[b] henceforth refers to the array produced by the ~c[aset], as written more clearly in ~c[[latter~]]. A Word of Warning: Users must exercise care when experimenting with ~c[[former~]], ~c[[latter~]] and ~c[[fast former~]]. Suppose you have just created ~c[b] with the assignment shown above, ~bv[] ACL2 !>(assign b (aset1 'demo (@ a) 1 'one)) ~ev[] If you then evaluate ~c[[former~]] in ACL2 it will complain that the ~ilc[aref1] is slow and compute the answer, as discussed. Then suppose you evaluate ~c[[latter~]] in ACL2. From our discussion you might expect it to execute fast ~-[] i.e., issue no complaint. But in fact you will find that it complains repeatedly. The problem is that the evaluation of ~c[[former~]] changed the semantic value of ~c[demo] so that it is no longer ~c[b]. To try the experiment correctly you must make ~c[b] be the semantic value of ~c[demo] again before the next example is evaluated. One way to do that is to execute ~bv[] ACL2 !>(assign b (compress1 'demo (@ b))) ~ev[] before each expression. Because of issues like this it is often hard to experiment with ACL2 arrays at the top-level. We find it easier to write functions that use arrays correctly and efficiently than to so use them interactively. This last assignment also illustrates a very common use of ~ilc[compress1]. While it was introduced as a means of removing irrelevant pairs from an array built up by repeated assignments, it is actually most useful as a way of insuring fast access to the elements of an array. Many array processing tasks can be divided into two parts. During the first part the array is built. During the second part the array is used extensively but not modified. If your ~il[programming] task can be so divided, it might be appropriate to construct the array entirely with list processing, thereby saving the cost of maintaining the semantic value of the name while few references are being made. Once the alist has stabilized, it might be worthwhile to treat it as an array by calling ~ilc[compress1], thereby gaining constant time access to it. ACL2's theorem prover uses this technique in connection with its implementation of the notion of whether a ~il[rune] is ~il[disable]d or not. Associated with every ~il[rune] is a unique integer ~c[index], called its ``nume.'' When each rule is stored, the corresponding nume is stored as a component of the rule. ~il[Theories] are lists of ~il[rune]s and membership in the ``current theory'' indicates that the corresponding rule is ~il[enable]d. But these lists are very long and membership is a linear-time operation. So just before a proof begins we map the list of ~il[rune]s in the current theory into an alist that pairs the corresponding numes with ~c[t]. Then we compress this alist into an array. Thus, given a rule we can obtain its nume (because it is a component) and then determine in constant time whether it is ~il[enable]d. The array is never modified during the proof, i.e., ~ilc[aset1] is never used in this example. From the logical perspective this code looks quite odd: we have replaced a linear-time membership test with an apparently linear-time ~ilc[assoc] after going to the trouble of mapping from a list of ~il[rune]s to an alist of numes. But because the alist of numes is an array, the ``apparently linear-time ~ilc[assoc]'' is more apparent than real; the operation is constant-time.~/ :cited-by Programming") (deflabel arrays-example :doc ; The transcript below was generated essentially after executing the following ; two forms: ; (set-fmt-soft-right-margin 55 state) ; (set-fmt-hard-right-margin 68 state) ":Doc-Section Arrays an example illustrating ACL2 arrays~/ The example below illustrates the use of ACL2 arrays. It is not, of course, a substitute for the detailed explanations provided elsewhere (~pl[arrays], including subtopics).~/ ~bv[] ACL2 !>(defun defarray (name size initial-element) (compress1 name (cons (list :HEADER :DIMENSIONS (list size) :MAXIMUM-LENGTH (1+ size) :DEFAULT initial-element :NAME name) nil))) Since DEFARRAY is non-recursive, its admission is trivial. We observe that the type of DEFARRAY is described by the theorem (AND (CONSP (DEFARRAY NAME SIZE INITIAL-ELEMENT)) (TRUE-LISTP (DEFARRAY NAME SIZE INITIAL-ELEMENT))). We used the :type-prescription rule COMPRESS1. Summary Form: ( DEFUN DEFARRAY ...) Rules: ((:TYPE-PRESCRIPTION COMPRESS1)) Warnings: None Time: 0.02 seconds (prove: 0.00, print: 0.02, other: 0.00) DEFARRAY ACL2 !>(assign my-ar (defarray 'a1 5 17)) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1)) ACL2 !>(aref1 'a1 (@ my-ar) 3) 17 ACL2 !>(aref1 'a1 (@ my-ar) 8) ACL2 Error in TOP-LEVEL: The guard for the function symbol AREF1, which is (AND (ARRAY1P NAME L) (INTEGERP N) (>= N 0) (< N (CAR (DIMENSIONS NAME L)))), is violated by the arguments in the call (AREF1 'A1 '(#) 8). ACL2 !>(assign my-ar (aset1 'a1 (@ my-ar) 3 'xxx)) ((3 . XXX) (:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1)) ACL2 !>(aref1 'a1 (@ my-ar) 3) XXX ACL2 !>(aset1 'a1 (@ my-ar) 3 'yyy) ; BAD: (@ my-ar) now points to ; an old copy of the array! ((3 . YYY) (3 . XXX) (:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1)) ACL2 !>(aref1 'a1 (@ my-ar) 3) ; Because of \"BAD\" above, the array ; access is done using assoc rather ; than Lisp aref, hence is slower; ; but the answer is still correct, ; reflecting the value in (@ my-ar), ; which was not changed above. ********************************************************** Slow Array Access! A call of AREF1 on an array named A1 is being executed slowly. See :DOC slow-array-warning ********************************************************** XXX ACL2 !> ~ev[]") (deflabel slow-array-warning :doc ":Doc-Section Arrays a warning or error issued when ~il[arrays] are used inefficiently~/ If you use ACL2 ~il[arrays] you may sometimes see a ~st[slow array] warning. We explain below what that warning means and some likely ``mistakes'' it may signify. First, we note that you can control whether or not you get a warning and, if so, whether or not a break (error from which you can continue; ~pl[break$]) ensues: ~bv[] (assign slow-array-action :warning) ; warn on slow array access (default) (assign slow-array-action :break) ; warn as above, and then call break$ (assign slow-array-action nil) ; do not warn or break on slow array access ~ev[] If you are using ACL2 arrays, then you probably care about performance, in which case it is probably best to avoid the ~c[nil] setting. Below we assume the default behavior: a warning, but no break.~/ The discussion in the documentation for ~il[arrays] defines what we mean by the semantic value of a name. As noted there, behind the scenes ACL2 maintains the invariant that with some names there is associated a pair consisting of an ACL2 array ~c[alist], called the semantic value of the name, and an equivalent raw lisp array. Access to ACL2 array elements, as in ~c[(aref1 name alist i)], is executed in constant time when the array alist is the semantic value of the name, because we can just use the corresponding raw lisp array to obtain the answer. ~ilc[Aset1] and ~ilc[compress1] modify the raw lisp array appropriately to maintain the invariant. If ~ilc[aref1] is called on a name and alist, and the alist is not the then-current semantic value of the name, the correct result is computed but it requires linear time because the alist must be searched. When this happens, ~ilc[aref1] prints a ~st[slow array] warning message to the comment window. ~ilc[Aset1] behaves similarly because the array it returns will cause the ~st[slow array] warning every time it is used. From the purely logical perspective there is nothing ``wrong'' about such use of ~il[arrays] and it may be spurious to print a warning message. But because ~il[arrays] are generally used to achieve efficiency, the ~st[slow array] warning often means the user's intentions are not being realized. Sometimes merely performance expectations are not met; but the message may mean that the functional behavior of the program is different than intended. Here are some ``mistakes'' that might cause this behavior. In the following we suppose the message was printed by ~ilc[aset1] about an array named ~c[name]. Suppose the alist supplied ~ilc[aset1] is ~c[alist]. (1) ~ilc[Compress1] was never called on ~c[name] and ~c[alist]. That is, perhaps you created an alist that is an ~ilc[array1p] and then proceeded to access it with ~ilc[aref1] but never gave ACL2 the chance to create a raw lisp array for it. After creating an alist that is intended for use as an array, you must do ~c[(compress1 name alist)] and pass the resulting ~c[alist'] as the array. (2) ~c[Name] is misspelled. Perhaps the array was compressed under the name ~c['delta-1] but accessed under ~c['delta1]? (3) An ~ilc[aset1] was done to modify ~c[alist], producing a new array, ~c[alist'], but you subsequently used ~c[alist] as an array. Inspect all ~c[(aset1 name ...)] occurrences and make sure that the alist modified is never used subsequently (either in that function or any other). It is good practice to adopt the following syntactic style. Suppose the alist you are manipulating is the value of the local variable ~c[alist]. Suppose at some point in a function definition you wish to modify ~c[alist] with ~ilc[aset1]. Then write ~bv[] (let ((alist (aset1 name alist i val))) ...) ~ev[] and make sure that the subsequent function body is entirely within the scope of the ~ilc[let]. Any uses of ~c[alist] subsequently will refer to the new alist and it is impossible to refer to the old alist. Note that if you write ~bv[] (foo (let ((alist (aset1 name alist i val))) ...) ; arg 1 (bar alist)) ; arg 2 ~ev[] you have broken the rules, because in ~c[arg 1] you have modified ~c[alist] but in ~c[arg 2] you refer to the old value. An appropriate rewriting is to lift the ~ilc[let] out: ~bv[] (let ((alist (aset1 name alist alist i val))) (foo ... ; arg 1 (bar alist))) ; arg 2 ~ev[] Of course, this may not mean the same thing. (4) A function which takes ~c[alist] as an argument and modifies it with ~ilc[aset1] fails to return the modified version. This is really the same as (3) above, but focuses on function interfaces. If a function takes an array ~c[alist] as an argument and the function uses ~ilc[aset1] (or a subfunction uses ~ilc[aset1], etc.), then the function probably ``ought'' to return the result produced by ~ilc[aset1]. The reasoning is as follows. If the array is passed into the function, then the caller is holding the array. After the function modifies it, the caller's version of the array is obsolete. If the caller is going to make further use of the array, it must obtain the latest version, i.e., that produced by the function.") (defun array1p (name l) ":Doc-Section Arrays recognize a 1-dimensional array~/ ~bv[] Example Form: (array1p 'delta1 a)~/ General Form: (array1p name alist) ~ev[] where ~c[name] and ~c[alist] are arbitrary objects. This function returns ~c[t] if ~c[alist] is a 1-dimensional ACL2 array. Otherwise it returns ~c[nil]. The function operates in constant time if ~c[alist] is the semantic value of ~c[name]. ~l[arrays]." (declare (xargs :guard t)) #-acl2-loop-only (cond ((symbolp name) (let ((prop (get-acl2-array-property name))) (cond ((and prop (eq l (car prop))) (return-from array1p (= 1 (array-rank (cadr prop))))))))) ; Note: This function does not use the header, dimensions, and maximum-length ; functions, but obtains their results through duplication of code. The reason ; is that we want those functions to have array1p or array2p as guards, so they ; can't be introduced before array1p. The reason we want this function in ; their guards, even though it is overly strong, is as follows. Users who use ; aref1 guard their functions with arrayp1 and then start proving theorems. ; The theorems talk about dimensions, etc. If dimensions, etc., are guarded ; with weaker things (like keyword-value-listp) then you find yourself either ; having to open up array1p or forward chain from it. But array1p is fairly ; hideous. So we intend to keep it disabled and regard it as the atomic test ; that it is ok to use array processing functions. (and (symbolp name) (alistp l) (let ((header-keyword-list (cdr (assoc-eq :header l)))) (and (keyword-value-listp header-keyword-list) (let ((dimensions (cadr (assoc-keyword :dimensions header-keyword-list))) (maximum-length (cadr (assoc-keyword :maximum-length header-keyword-list)))) (and (true-listp dimensions) (equal (length dimensions) 1) (integerp (car dimensions)) (integerp maximum-length) (< 0 (car dimensions)) (< (car dimensions) maximum-length) (<= maximum-length *maximum-positive-32-bit-integer*) (bounded-integer-alistp l (car dimensions)))))))) (defthm array1p-forward (implies (array1p name l) (and (symbolp name) (alistp l) (keyword-value-listp (cdr (assoc-eq :header l))) (true-listp (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (equal (length (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) 1) (integerp (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (integerp (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l))))) (< 0 (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (< (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l))))) (<= (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l)))) *maximum-positive-32-bit-integer*) (bounded-integer-alistp l (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))))) :rule-classes :forward-chaining) (defthm array1p-linear (implies (array1p name l) (and (< 0 (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (< (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l))))) (<= (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l)))) *maximum-positive-32-bit-integer*))) :rule-classes ((:linear :match-free :all))) (defun bounded-integer-alistp2 (l i j) (declare (xargs :guard t)) (cond ((atom l) (null l)) (t (and (consp (car l)) (let ((key (caar l))) (and (or (eq key :header) (and (consp key) (let ((i1 (car key)) (j1 (cdr key))) (and (integerp i1) (integerp j1) (integerp i) (integerp j) (>= i1 0) (< i1 i) (>= j1 0) (< j1 j))))))) (bounded-integer-alistp2 (cdr l) i j))))) (defun assoc2 (i j l) (declare (xargs :guard (and (integerp i) (integerp j)))) (if (atom l) nil (if (and (consp (car l)) (consp (caar l)) (eql i (caaar l)) (eql j (cdaar l))) (car l) (assoc2 i j (cdr l))))) (defun array2p (name l) ":Doc-Section Arrays recognize a 2-dimensional array~/ ~bv[] Example Form: (array2p 'delta1 a)~/ General Form: (array2p name alist) ~ev[] where ~c[name] and ~c[alist] are arbitrary objects. This function returns ~c[t] if ~c[alist] is a 2-dimensional ACL2 array. Otherwise it returns ~c[nil]. The function operates in constant time if ~c[alist] is the semantic value of ~c[name]. ~l[arrays]." (declare (xargs :guard t)) #-acl2-loop-only (cond ((symbolp name) (let ((prop (get-acl2-array-property name))) (cond ((and prop (eq l (car prop)) (return-from array2p (= 2 (array-rank (cadr prop)))))))))) (and (symbolp name) (alistp l) (let ((header-keyword-list (cdr (assoc-eq :header l)))) (and (keyword-value-listp header-keyword-list) (let ((dimensions (cadr (assoc-keyword :dimensions header-keyword-list))) (maximum-length (cadr (assoc-keyword :maximum-length header-keyword-list)))) (and (true-listp dimensions) (equal (length dimensions) 2) (let ((d1 (car dimensions)) (d2 (cadr dimensions))) (and (integerp d1) (integerp d2) (integerp maximum-length) (< 0 d1) (< 0 d2) (< (* d1 d2) maximum-length) (<= maximum-length *maximum-positive-32-bit-integer*) (bounded-integer-alistp2 l d1 d2))))))))) (defthm array2p-forward (implies (array2p name l) (and (symbolp name) (alistp l) (keyword-value-listp (cdr (assoc-eq :header l))) (true-listp (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (equal (length (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) 2) (integerp (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (integerp (cadr (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (integerp (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l))))) (< 0 (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (< 0 (cadr (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (< (* (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (cadr (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l))))) (<= (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l)))) *maximum-positive-32-bit-integer*) (bounded-integer-alistp2 l (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (cadr (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))))) :rule-classes :forward-chaining) (defthm array2p-linear (implies (array2p name l) (and (< 0 (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (< 0 (cadr (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (< (* (car (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (cadr (cadr (assoc-keyword :dimensions (cdr (assoc-eq :header l)))))) (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l))))) (<= (cadr (assoc-keyword :maximum-length (cdr (assoc-eq :header l)))) *maximum-positive-32-bit-integer*))) :rule-classes ((:linear :match-free :all))) ; (in-theory (disable array1p array2p)) (defun header (name l) (declare (xargs :guard (or (array1p name l) (array2p name l)))) ":Doc-Section Arrays return the header of a 1- or 2-dimensional array~/ ~bv[] Example Form: (header 'delta1 a)~/ General Form: (header name alist) ~ev[] where ~c[name] is arbitrary and ~c[alist] is a 1- or 2-dimensional array. This function returns the header of the array ~c[alist]. The function operates in virtually constant time if ~c[alist] is the semantic value of ~c[name]. ~l[arrays]." #+acl2-loop-only (prog2$ name ;to avoid warning in *1* function definition (assoc-eq :header l)) ; In the usual case, this function will take constant time regardless ; of where the header is in the alist. This makes the related ; functions for getting the fields of the header fast, too. #-acl2-loop-only (let ((prop (get-acl2-array-property name))) (cond ((and prop (eq l (car prop))) (cadddr prop)) (t (assoc-eq :header l))))) (defun dimensions (name l) ":Doc-Section Arrays return the ~c[:dimensions] from the ~il[header] of a 1- or 2-dimensional array~/ ~bv[] Example Form: (dimensions 'delta1 a)~/ General Form: (dimensions name alist) ~ev[] where ~c[name] is arbitrary and ~c[alist] is a 1- or 2-dimensional array. This function returns the dimensions list of the array ~c[alist]. That list will either be of the form ~c[(dim1)] or ~c[(dim1 dim2)], depending on whether ~c[alist] is a 1- or 2-dimensional array. ~c[Dim1] and ~c[dim2] will be integers and each exceed by 1 the maximum legal corresponding index. Thus, if ~c[dimensions] returns, say, ~c['(100)] for an array ~c[a] named ~c['delta1], then ~c[(aref1 'delta1 a 99)] is legal but ~c[(aref1 'delta1 a 100)] violates the ~il[guard]s on ~ilc[aref1]. ~c[Dimensions] operates in virtually constant time if ~c[alist] is the semantic value of ~c[name]. ~l[arrays]." (declare (xargs :guard (or (array1p name l) (array2p name l)))) (cadr (assoc-keyword :dimensions (cdr (header name l))))) (defun maximum-length (name l) ":Doc-Section Arrays return the ~c[:maximum-length] from the ~il[header] of an array~/ ~bv[] Example Form: (maximum-length 'delta1 a)~/ General Form: (maximum-length name alist) ~ev[] where ~c[name] is an arbitrary object and ~c[alist] is a 1- or 2-dimensional array. This function returns the contents of the ~c[:maximum-length] field of the ~il[header] of ~c[alist]. Whenever an ~ilc[aset1] or ~ilc[aset2] would cause the length of the alist to exceed its maximum length, a ~ilc[compress1] or ~ilc[compress2] is done automatically to remove irrelevant pairs from the array. ~c[Maximum-length] operates in virtually constant time if ~c[alist] is the semantic value of ~c[name]. ~l[arrays]." (declare (xargs :guard (or (array1p name l) (array2p name l)))) (cadr (assoc-keyword :maximum-length (cdr (header name l))))) (defun default (name l) ":Doc-Section Arrays return the ~c[:default] from the ~il[header] of a 1- or 2-dimensional array~/ ~bv[] Example Form: (default 'delta1 a)~/ General Form: (default name alist) ~ev[] where ~c[name] is an arbitrary object and ~c[alist] is a 1- or 2-dimensional array. This function returns the contents of the ~c[:default] field of the ~il[header] of ~c[alist]. When ~ilc[aref1] or ~ilc[aref2] is used to obtain a value for an index (or index pair) not bound in ~c[alist], the default value is returned instead. Thus, the array ~c[alist] may be thought of as having been initialized with the default value. ~c[default] operates in virtually constant time if ~c[alist] is the semantic value of ~c[name]. ~l[arrays]." (declare (xargs :guard (or (array1p name l) (array2p name l)))) (cadr (assoc-keyword :default (cdr (header name l))))) ; Parallelism wart: once upon a time we locked all array operations. Since ; then, two improvements have been made to ACL2: (1) the ; enabled-array-structure now uses unique names based on the current subgoal ; and (2) the array implementation itself was improved to be "more" thread-safe ; (you can compare the implementation of aset1 and other related functions in ; ACL2 3.6.1 and ACL2 4.0 to see the change). However, we suspect that ; that arrays are not thread-safe, as we have acknowledged in :DOC ; unsupported-waterfall-parallelism-features. ; ; Rager thinks that we stopped locking the array operations because the prover ; incurred significant overhead (if he can recall correctly, it was about a 40% ; increase in time required to certify a semi-expensive book) with locking ; enabled. He thinks that the change to enabled arrays, named (1) above, could ; have eliminated most of this overhead. However, further investigation is ; called for. ; For now, we do not lock any array operations, but we leave the dead code as ; hints to ourselves that we may need to do so. When this wart is addressed, ; this dead code (which can be found by searching for *acl2-par-arrays-lock*) ; should either be uncommented and modified, or it should be removed. ; #+(and acl2-par (not acl2-loop-only)) ; (deflock *acl2-par-arrays-lock*) (defun aref1 (name l n) ":Doc-Section Arrays access the elements of a 1-dimensional array~/ ~bv[] Example Form: (aref1 'delta1 a (+ i k))~/ General Form: (aref1 name alist index) ~ev[] where ~c[name] is a symbol, ~c[alist] is a 1-dimensional array and ~c[index] is a legal index into ~c[alist]. This function returns the value associated with ~c[index] in ~c[alist], or else the default value of the array. ~l[arrays] for details. This function executes in virtually constant time if ~c[alist] is in fact the ``semantic value'' associated with ~c[name] (~pl[arrays]). When it is not, ~c[aref1] must do a linear search through ~c[alist]. In that case the correct answer is returned but a ~st[slow array] comment is printed to the comment window. ~l[slow-array-warning]." #+acl2-loop-only (declare (xargs :guard (and (array1p name l) (integerp n) (>= n 0) (< n (car (dimensions name l)))))) #+acl2-loop-only (let ((x (and (not (eq n :header)) (assoc n l)))) (cond ((null x) (default name l)) (t (cdr x)))) ; We are entitled to make the following declaration because of the ; guard. #-acl2-loop-only (declare (type (unsigned-byte 31) n)) #-acl2-loop-only ; See comment above (for #+acl2-par) about *acl2-par-arrays-lock*: ; (with-lock ; *acl2-par-arrays-lock* (let ((prop (get-acl2-array-property name))) (cond ((eq l (car prop)) (svref (the simple-vector (car (cdr prop))) n)) (t (slow-array-warning 'aref1 name) (let ((x (assoc n l))) ; n is a number, hence not :header (cond ((null x) (default name l)) (t (cdr x)))))))) (defun compress11 (name l i n default) (declare (xargs :guard (and (array1p name l) (integerp i) (integerp n) (<= i n)) :measure (nfix (- n i)))) (cond ((zp (- n i)) nil) (t (let ((pair (assoc i l))) (cond ((or (null pair) (equal (cdr pair) default)) (compress11 name l (+ i 1) n default)) (t (cons pair (compress11 name l (+ i 1) n default)))))))) #-acl2-loop-only (defconstant *invisible-array-mark* 'acl2_invisible::|An Invisible Array Mark|) (defun array-order (header) (declare (xargs :guard (and (consp header) (keyword-value-listp (cdr header))))) (let ((orderp (assoc-keyword :order (cdr header)))) (cond ((and orderp (or (eq (cadr orderp) nil) (eq (cadr orderp) :none))) nil) ((and orderp (eq (cadr orderp) '>)) '>) (t ; default '<)))) (defun compress1 (name l) ":Doc-Section Arrays remove irrelevant pairs from a 1-dimensional array~/ ~bv[] Example Form: (compress1 'delta1 a)~/ General Form: (compress1 name alist) ~ev[] where ~c[name] is a symbol and ~c[alist] is a 1-dimensional array, generally named ~c[name]. ~l[arrays] for details. Logically speaking, this function removes irrelevant pairs from ~c[alist], possibly shortening it. The function returns a new array, ~c[alist'], with the same ~ilc[header] (including name and dimension) as ~c[alist], that, under ~ilc[aref1], is everywhere equal to ~c[alist]. That is, ~c[(aref1 name alist' i)] is ~c[(aref1 name alist i)], for all legal indices ~c[i]. ~c[Alist'] may be shorter than ~c[alist] and the non-irrelevant pairs may occur in a different order than in ~c[alist]. Practically speaking, this function plays an important role in the efficient implementation of ~ilc[aref1]. In addition to creating the new array, ~c[alist'], ~c[compress1] makes that array the ``semantic value'' of ~c[name] and allocates a raw lisp array to ~c[name]. For each legal index, ~c[i], that raw lisp array contains ~c[(aref1 name alist' i)] in slot ~c[i]. Thus, subsequent ~ilc[aref1] operations can be executed in virtually constant time provided they are given ~c[name] and the ~c[alist'] returned by the most recently executed ~c[compress1] or ~ilc[aset1] on ~c[name]. ~l[arrays]. In general, ~c[compress1] returns an alist whose ~ilc[cdr] is an association list whose keys are nonnegative integers in ascending order. However, if the ~ilc[header] specifies an ~c[:order] of ~c[>] then the keys will occur in descending order, and if the ~c[:order] is ~c[:none] or ~c[nil] then the keys will not be sorted, i.e., ~c[compress1] is logically the identity function (though it still attaches an array under the hood). Note however that a ~ilc[compress1] call is replaced by a hard error if the header specifies an ~c[:order] of ~c[:none] or ~c[nil] and the array's length exceeds the ~ilc[maximum-length] field of its ~ilc[header]." ; The uses of (the (unsigned-byte 31) ...) below rely on the array1p guard, ; which for example guarantees that the dimension is bounded by ; *maximum-positive-32-bit-integer* and that each array index (i.e., each car) ; is less than the dimension. These declarations probably only assist ; efficiency in GCL, but that may be the Lisp that benefits most from such ; fixnum declarations, anyhow. #+acl2-loop-only (declare (xargs :guard (array1p name l))) #+acl2-loop-only (case (array-order (header name l)) (< (cons (header name l) (compress11 name l 0 (car (dimensions name l)) (default name l)))) (> (cons (header name l) (reverse (compress11 name l 0 (car (dimensions name l)) (default name l))))) (t (prog2$ (and (> (length l) (maximum-length name l)) (hard-error 'compress1 "Attempted to compress a one-dimensional array named ~ ~x0 whose header specifies :ORDER ~x1 and whose ~ length, ~x2, exceeds its maximum-length, ~x3." (list (cons #\0 name) (cons #\1 nil) (cons #\2 (length l)) (cons #\3 (maximum-length name l))))) l))) #-acl2-loop-only ; See comment above (for #+acl2-par) about *acl2-par-arrays-lock*: ; (with-lock ; *acl2-par-arrays-lock* (let* ((old (get-acl2-array-property name)) (header (header name l)) (length (car (cadr (assoc-keyword :dimensions (cdr header))))) (maximum-length (cadr (assoc-keyword :maximum-length (cdr header)))) (default (cadr (assoc-keyword :default (cdr header)))) (order (array-order header)) old-car ar in-order) (when (and (null order) (> (length l) maximum-length)) (hard-error 'compress1 "Attempted to compress a one-dimensional array named ~x0 ~ whose header specifies :ORDER ~x1 and whose length, ~x2, ~ exceeds its maximum-length, ~x3." (list (cons #\0 name) (cons #\1 nil) (cons #\2 (length l)) (cons #\3 (maximum-length name l))))) ; Get an array that is all filled with the special mark *invisible-array-mark*. (cond ((and old (= 1 (array-rank (cadr old))) (= (length (cadr old)) length)) (setq old-car (car old)) (setf (car old) *invisible-array-mark*) (setq ar (cadr old)) (do ((i (1- length) (1- i))) ((< i 0)) (declare (type (signed-byte 32) i)) (setf (svref ar i) *invisible-array-mark*))) (t (setq ar (make-array$ length :initial-element *invisible-array-mark*)))) ; Store the value of each pair under its key (unless it is covered by ; an earlier pair with the same key). (do ((tl l (cdr tl))) ((null tl)) (let ((index (caar tl))) (cond ((eq index :header) nil) ((eq *invisible-array-mark* (svref ar index)) (setf (svref ar index) (cdar tl)))))) ; Determine whether l is already is in normal form (header first, ; strictly ascending keys, no default values, no extra header.) (setq in-order t) (when order (cond ((eq (caar l) :header) (do ((tl (cdr l) (cdr tl))) (nil) (cond ((or (eq (caar tl) :header) (eq (car (cadr tl)) :header)) (setq in-order nil) (return nil)) ((equal (cdr (car tl)) default) (setq in-order nil) (return nil)) ((null (cdr tl)) (return nil)) ((if (eq order '>) (<= (the (unsigned-byte 31) (caar tl)) (the (unsigned-byte 31) (car (cadr tl)))) (>= (the (unsigned-byte 31) (caar tl)) (the (unsigned-byte 31) (car (cadr tl))))) (setq in-order nil) (return nil))))) (t (setq in-order nil)))) (let ((num 1) x max-ar) (declare (type (unsigned-byte 31) num)) ; In one pass, set x to the value to be returned, put defaults into the array ; where the invisible mark still sits, and calculate the length of x. (cond (in-order (do ((i (1- length) (1- i))) ((< i 0)) (declare (type (signed-byte 32) i)) (let ((val (svref ar i))) (cond ((eq *invisible-array-mark* val) (setf (svref ar i) default)) (t (setq num (the (unsigned-byte 31) (1+ num))))))) (setq x l)) ((eq order '>) (do ((i 0 (1+ i))) ((int= i length)) (declare (type (unsigned-byte 31) i)) (let ((val (svref ar i))) (cond ((eq *invisible-array-mark* val) (setf (svref ar i) default)) ((equal val default) nil) (t (push (cons i val) x) (setq num (the (unsigned-byte 31) (1+ num))))))) (setq x (cons header x))) (t (do ((i (1- length) (1- i))) ((< i 0)) (declare (type (signed-byte 32) i)) (let ((val (svref ar i))) (cond ((eq *invisible-array-mark* val) (setf (svref ar i) default)) ((equal val default) nil) (t (push (cons i val) x) (setq num (the (unsigned-byte 31) (1+ num))))))) (setq x (cons header x)))) (cond (old (setq max-ar (caddr old)) (setf (aref (the (array (unsigned-byte 31) (*)) max-ar) 0) (the (unsigned-byte 31) (- maximum-length num)))) (t (setq max-ar (make-array$ 1 :initial-contents (list (- maximum-length num)) :element-type '(unsigned-byte 31))))) (cond (old (setf (cadr old) ar) (setf (cadddr old) header) ; We re-use the old value if it is equal to the new value. The example ; regarding compress1 in :doc note-2-7-other shows why we need to do this. In ; case that is not enough of a reason, here is a comment from Version_2.6 code, ; which is once again the code in Version_2.8. (Version_2.7 had a bug from an ; ill-advised attempt to deal with a problem with slow array warnings reported ; in :doc note-2-7-bug-fixes.) ; If the old car is equal to x, then we put the old pointer back into the ; car of the 'acl2-array property rather than the new pointer. ; This has the good effect of preserving the validity of any old ; copies of the array. It is clear the code below is correct, since ; we are putting down an equal structure in place of a newly consed up ; one. But why go out of our way? Why not just (setf (car old) x)? ; In fact, once upon a time, that is what we did. But it bit us when ; we tried to prove theorems in a post-:init world. ; When ACL2 is loaded the Common Lisp global constant ; *type-set-binary-+-table* is defined by (defconst & (compress2 ...)). ; It is set to some list, here called ptr1, built by compress2 (which ; contains code analogous to that we are documenting here in ; compress1). When ptr1 is built it is stored as the car of the ; 'acl2-array property of the array name 'type-set-binary-+-table, because at ; the time ACL2 is loaded, there is no old 'acl2-array property on ; that name. Suppose we then :init, loading the ACL2 source code into ; the current ACL2 world. That will execute the same defconst, in ; the acl2-loop-only setting. Compress2 is called and will build a ; new structure, ptr2 (called x in this code). Upon finishing, it ; will (according to the code here) find that ptr2 is equal to ptr1 ; and will put ptr1 into the car of the 'acl2-array property of ; 'type-set-binary-+-table. It will return ptr1. That will become the value ; of the 'const getprop of '*type-set-binary-+-table* in the ; current-acl2-world. When that world is installed, we will note that ; a non-virgin name, *type-set-binary-+-table*, is being defconst'd and so ; we will DO NOTHING, leaving ptr1 as the value of the Common Lisp ; global contant *type-set-binary-+-table*. So, because of the code below, ; all logical copies of this array are represented by ptr1. ; In the old days, compress2 put ptr2 into the car of the 'acl2-array ; property of 'type-set-binary-+-table. It returned ptr2, which thus became ; the value of the 'const getprop of '*type-set-binary-+-table*. When ; that world was installed, we noted that a non-virgin name was being ; defconst'd and we DID NOTHING, leaving ptr1 as the value of the ; global constant *type-set-binary-+-table*. Subsequent references to ; *type-set-binary-+-table* in our type-set code, e.g., as occurred when one ; tried to prove theorems about + after an :init, provoked the ; slow-array-warning. ; The following historical comment no longer applies to ; 'global-enabled-stucture, but it is still relevant to ; 'global-arithmetic-enabled-structure. ; This preservation (eq) of the old array is also crucial to the way ; recompress-global-enabled-structure works. That function extracts ; the :theory-array from the current global-enabled-structure -- said ; theory-array having been produced by a past call of compress1 and ; hence guaranteed to be sorted etc. It calls compress1 on it, which ; side-effects the underlying von Neumann array but returns the very ; same (eq) structure. We then discard that structure, having only ; wanted the side effect! Before we exploited this, we had to cons up ; a new global-enabled-structure and rebind 'global-enabled-stucture ; in the world. This had the bad effect of sometimes putting more ; than one binding of that variable. (setf (car old) (cond ((equal old-car x) old-car) (t x))) (car old)) (t (set-acl2-array-property name (list x ar max-ar header)) x))))) (defthm array1p-cons (implies (and (< n (caadr (assoc-keyword :dimensions (cdr (assoc-eq :header l))))) (not (< n 0)) (integerp n) (array1p name l)) (array1p name (cons (cons n val) l))) :hints (("Goal" :in-theory (enable array1p)))) (defun aset1 (name l n val) ":Doc-Section Arrays set the elements of a 1-dimensional array~/ ~bv[] Example Form: (aset1 'delta1 a (+ i k) 27)~/ General Form: (aset1 name alist index val) ~ev[] where ~c[name] is a symbol, ~c[alist] is a 1-dimensional array named ~c[name], ~c[index] is a legal index into ~c[alist], and ~c[val] is an arbitrary object. ~l[arrays] for details. Roughly speaking this function ``modifies'' ~c[alist] so that the value associated with ~c[index] is ~c[val]. More precisely, it returns a new array, ~c[alist'], of the same name and dimension as ~c[alist] that, under ~ilc[aref1], is everywhere equal to ~c[alist] except at ~c[index] where the result is ~c[val]. That is, ~c[(aref1 name alist' i)] is ~c[(aref1 name alist i)] for all legal indices ~c[i] except ~c[index], where ~c[(aref1 name alist' i)] is ~c[val]. In order to ``modify'' ~c[alist], ~c[aset1] ~ilc[cons]es a new pair onto the front. If the length of the resulting alist exceeds the ~c[:]~ilc[maximum-length] entry in the array ~il[header], ~c[aset1] compresses the array as with ~ilc[compress1]. It is generally expected that the ``semantic value'' of ~c[name] will be ~c[alist] (~pl[arrays]). This function operates in virtually constant time whether this condition is true or not (unless the ~ilc[compress1] operation is required). But the value returned by this function cannot be used efficiently by subsequent ~c[aset1] operations unless ~c[alist] is the semantic value of ~c[name] when ~c[aset1] is executed. Thus, if the condition is not true, ~c[aset1] prints a ~st[slow array] warning to the comment window. ~l[slow-array-warning]." #+acl2-loop-only (declare (xargs :guard (and (array1p name l) (integerp n) (>= n 0) (< n (car (dimensions name l)))))) #+acl2-loop-only (let ((l (cons (cons n val) l))) (cond ((> (length l) (maximum-length name l)) (compress1 name l)) (t l))) #-acl2-loop-only (declare (type (unsigned-byte 31) n)) #-acl2-loop-only ; See comment above (for #+acl2-par) about *acl2-par-arrays-lock*: ; (with-lock ; *acl2-par-arrays-lock* (let ((prop (get-acl2-array-property name))) (cond ((eq l (car prop)) (let* ((ar (cadr prop)) (to-go (aref (the (array (unsigned-byte 31) (*)) (caddr prop)) 0))) (declare (type (unsigned-byte 31) to-go) (simple-vector ar)) (cond ((eql (the (unsigned-byte 31) to-go) 0) (setf (car prop) *invisible-array-mark*) (setf (aref ar n) val) (let* ((header (cadddr prop)) (order (array-order header)) (length (car (cadr (assoc-keyword :dimensions (cdr header))))) (maximum-length (cadr (assoc-keyword :maximum-length (cdr header)))) (default (cadr (assoc-keyword :default (cdr header)))) (x nil) (num 1)) (declare (type (unsigned-byte 31) num length)) (declare (type (unsigned-byte 31) maximum-length)) (cond ((null order) ; Cause same error as in the logic. (return-from aset1 (compress1 name (cons (cons n val) l)))) ((eq order '>) (do ((i 0 (1+ i))) ((int= i length)) (declare (type (unsigned-byte 31) i)) (let ((val (svref ar (the (unsigned-byte 31) i)))) (cond ((equal val default) nil) (t (push (cons i val) x) (setq num (the (unsigned-byte 31) (1+ num)))))))) (t (do ((i (1- length) (1- i))) ((< i 0)) (declare (type (signed-byte 32) i)) (let ((val (svref ar (the (signed-byte 32) i)))) (cond ((equal val default) nil) (t (push (cons i val) x) (setq num (the (unsigned-byte 31) (1+ num))))))))) (setq x (cons header x)) (setf (aref (the (array (unsigned-byte 31) (*)) (caddr prop)) 0) (the (unsigned-byte 31) (- maximum-length num))) (setf (car prop) x) x)) (t (let ((x (cons (cons n val) l))) (setf (car prop) *invisible-array-mark*) (setf (svref (the simple-vector ar) n) val) (setf (aref (the (array (unsigned-byte 31) (*)) (caddr prop)) 0) (the (unsigned-byte 31) (1- to-go))) (setf (car prop) x) x))))) (t (let ((l (cons (cons n val) l))) (slow-array-warning 'aset1 name) (cond ((> (length l) (maximum-length name l)) (compress1 name l)) (t l))))))) (defun aref2 (name l i j) ":Doc-Section Arrays access the elements of a 2-dimensional array~/ ~bv[] Example Form: (aref2 'delta1 a i j)~/ General Form: (aref2 name alist i j) ~ev[] where ~c[name] is a symbol, ~c[alist] is a 2-dimensional array and ~c[i] and ~c[j] are legal indices into ~c[alist]. This function returns the value associated with ~c[(i . j)] in ~c[alist], or else the default value of the array. ~l[arrays] for details. This function executes in virtually constant time if ~c[alist] is in fact the ``semantic value'' associated with ~c[name] (~pl[arrays]). When it is not, ~c[aref2] must do a linear search through ~c[alist]. In that case the correct answer is returned but a ~st[slow array] comment is printed to the comment window. ~l[slow-array-warning]." #+acl2-loop-only (declare (xargs :guard (and (array2p name l) (integerp i) (>= i 0) (< i (car (dimensions name l))) (integerp j) (>= j 0) (< j (cadr (dimensions name l)))))) #+acl2-loop-only (let ((x (assoc2 i j l))) (cond ((null x) (default name l)) (t (cdr x)))) #-acl2-loop-only (declare (type (unsigned-byte 31) i j)) #-acl2-loop-only (let ((prop (get-acl2-array-property name))) (cond ((eq l (car prop)) (aref (the (array * (* *)) (car (cdr prop))) i j)) (t (slow-array-warning 'aref2 name) (let ((x (assoc2 i j l))) (cond ((null x) (default name l)) (t (cdr x)))))))) (defun compress211 (name l i x j default) (declare (xargs :guard (and (array2p name l) (integerp x) (integerp i) (integerp j) (<= x j)) :measure (nfix (- j x)))) (cond ((zp (- j x)) nil) (t (let ((pair (assoc2 i x l))) (cond ((or (null pair) (equal (cdr pair) default)) (compress211 name l i (+ 1 x) j default)) (t (cons pair (compress211 name l i (+ 1 x) j default)))))))) (defun compress21 (name l n i j default) (declare (xargs :guard (and (array2p name l) (integerp n) (integerp i) (integerp j) (<= n i) (<= 0 j)) :measure (nfix (- i n)))) (cond ((zp (- i n)) nil) (t (append (compress211 name l n 0 j default) (compress21 name l (+ n 1) i j default))))) (defun compress2 (name l) ":Doc-Section Arrays remove irrelevant pairs from a 2-dimensional array~/ ~bv[] Example Form: (compress2 'delta1 a)~/ General Form: (compress2 name alist) ~ev[] where ~c[name] is a symbol and ~c[alist] is a 2-dimensional array, generally named ~c[name]. ~l[arrays] for details. Logically speaking, this function removes irrelevant pairs from ~c[alist], possibly shortening it. The function returns a new array, ~c[alist'], with the same ~ilc[header] (including name and dimension) as ~c[alist], that, under ~ilc[aref2], is everywhere equal to ~c[alist]. That is, ~c[(aref2 name alist' i j)] is ~c[(aref2 name alist i j)], for all legal indices ~c[i] and ~c[j]. ~c[Alist'] may be shorter than ~c[alist] and the non-irrelevant pairs may occur in a different order in ~c[alist'] than in ~c[alist]. Practically speaking, this function plays an important role in the efficient implementation of ~ilc[aref2]. In addition to creating the new array, ~c[alist'], ~c[compress2] makes that array the ``semantic value'' of ~c[name] and allocates a raw lisp array to ~c[name]. For all legal indices, ~c[i] and ~c[j], that raw lisp array contains ~c[(aref2 name alist' i j)] in slot ~c[i],~c[j]. Thus, subsequent ~ilc[aref2] operations can be executed in virtually constant time provided they are given ~c[name] and the ~c[alist'] returned by the most recently executed ~c[compress2] or ~ilc[aset2] on ~c[name]. ~l[arrays]." #+acl2-loop-only ; The uses of (the (unsigned-byte 31) ...) below rely on the array2p ; guard, which for example guarantees that each dimension is bounded ; by *maximum-positive-32-bit-integer* and that array indices are ; therefore less than *maximum-positive-32-bit-integer*. These ; declarations probably only assist efficiency in GCL, but that may be ; the Lisp that benefits most from such fixnum declarations, anyhow. (declare (xargs :guard (array2p name l))) #+acl2-loop-only (cons (header name l) (compress21 name l 0 (car (dimensions name l)) (cadr (dimensions name l)) (default name l))) #-acl2-loop-only (let* ((old (get-acl2-array-property name)) (header (header name l)) (dimension1 (car (cadr (assoc-keyword :dimensions (cdr header))))) (dimension2 (cadr (cadr (assoc-keyword :dimensions (cdr header))))) (maximum-length (cadr (assoc-keyword :maximum-length (cdr header)))) (default (cadr (assoc-keyword :default (cdr header)))) old-car ar in-order) ; Get an array that is filled with the special mark *invisible-array-mark*. (cond ((and old (= 2 (array-rank (cadr old))) (and (= dimension1 (array-dimension (cadr old) 0)) (= dimension2 (array-dimension (cadr old) 1)))) (setq old-car (car old)) (setf (car old) *invisible-array-mark*) (setq ar (cadr old)) (let ((ar ar)) (declare (type (array * (* *)) ar)) (do ((i (1- dimension1) (1- i))) ((< i 0)) (declare (type fixnum i)) (do ((j (1- dimension2) (1- j))) ((< j 0)) (declare (type fixnum j)) (setf (aref ar i j) *invisible-array-mark*))))) (t (setq ar (make-array$ (list dimension1 dimension2) :initial-element *invisible-array-mark*)))) (let ((ar ar)) (declare (type (array * (* *)) ar)) ; Store the value of each pair under its key (unless it is covered by ; an earlier pair with the same key). (do ((tl l (cdr tl))) ((null tl)) (let ((index (caar tl))) (cond ((eq index :header) nil) ((eq *invisible-array-mark* (aref ar (the fixnum (car index)) (the fixnum (cdr index)))) (setf (aref ar (the fixnum (car index)) (the fixnum (cdr index))) (cdar tl)))))) ; Determine whether l is already in normal form (header first, ; strictly ascending keys, no default values, n extra header.) (setq in-order t) (cond ((eq (caar l) :header) (do ((tl (cdr l) (cdr tl))) (nil) (cond ((or (eq (caar tl) :header) (eq (car (cadr tl)) :header)) (setq in-order nil) (return nil)) ((equal (cdr (car tl)) default) (setq in-order nil) (return nil)) ((null (cdr tl)) (return nil)) ((or (> (the (unsigned-byte 31) (caaar tl)) (the (unsigned-byte 31) (caaadr tl))) (and (= (the (unsigned-byte 31) (caaar tl)) (the (unsigned-byte 31) (caaadr tl))) (> (the (unsigned-byte 31) (cdaar tl)) (the (unsigned-byte 31) (cdaadr tl))))) (setq in-order nil) (return nil))))) (t (setq in-order nil))) (let ((x nil) (num 1) max-ar) (declare (type (unsigned-byte 31) num)) ; In one pass, set x to the value to be returned, put defaults into the array ; where the invisible mark still sits, and calculate the length of x. (cond (in-order (do ((i (1- dimension1) (1- i))) ((< i 0)) (declare (type fixnum i)) (do ((j (1- dimension2) (1- j))) ((< j 0)) (declare (type fixnum j)) (let ((val (aref ar i j))) (cond ((eq *invisible-array-mark* val) (setf (aref ar i j) default)) (t (setq num (the (unsigned-byte 31) (1+ num)))))))) (setq x l)) (t (do ((i (1- dimension1) (1- i))) ((< i 0)) (declare (type fixnum i)) (do ((j (1- dimension2) (1- j))) ((< j 0)) (declare (type fixnum j)) (let ((val (aref ar i j))) (cond ((eq *invisible-array-mark* val) (setf (aref ar i j) default)) ((equal val default) nil) (t (push (cons (cons i j) val) x) (setq num (the (unsigned-byte 31) (1+ num)))))))) (setq x (cons header x)))) (cond (old (setq max-ar (caddr old)) (setf (aref (the (array (unsigned-byte 31) (*)) max-ar) 0) (the (unsigned-byte 31) (- maximum-length num)))) (t (setq max-ar (make-array$ 1 :initial-contents (list (- maximum-length num)) :element-type '(unsigned-byte 31))))) (cond (old (setf (cadr old) ar) (setf (cadddr old) header) (setf (car old) (cond ((equal old-car x) old-car) (t x))) (car old)) (t (set-acl2-array-property name (list x ar max-ar header)) x)))))) (defthm array2p-cons (implies (and (< j (cadr (dimensions name l))) (not (< j 0)) (integerp j) (< i (car (dimensions name l))) (not (< i 0)) (integerp i) (array2p name l)) (array2p name (cons (cons (cons i j) val) l))) :hints (("Goal" :in-theory (enable array2p)))) (defun aset2 (name l i j val) ":Doc-Section Arrays set the elements of a 2-dimensional array~/ ~bv[] Example Form: (aset2 'delta1 a i j 27)~/ General Form: (aset2 name alist i j val) ~ev[] where ~c[name] is a symbol, ~c[alist] is a 2-dimensional array named ~c[name], ~c[i] and ~c[j] are legal indices into ~c[alist], and ~c[val] is an arbitrary object. ~l[arrays] for details. Roughly speaking this function ``modifies'' ~c[alist] so that the value associated with ~c[(i . j)] is ~c[val]. More precisely, it returns a new array, ~c[alist'], of the same name and dimension as ~c[alist] that, under ~ilc[aref2], is everywhere equal to ~c[alist] except at ~c[(i . j)] where the result is ~c[val]. That is, ~c[(aref2 name alist' x y)] is ~c[(aref2 name alist x y)] for all legal indices ~c[x] ~c[y] except ~c[i] and ~c[j] where ~c[(aref2 name alist' i j)] is ~c[val]. In order to ``modify'' ~c[alist], ~c[aset2] ~ilc[cons]es a new pair onto the front. If the length of the resulting ~c[alist] exceeds the ~c[:]~ilc[maximum-length] entry in the array ~il[header], ~c[aset2] compresses the array as with ~ilc[compress2]. It is generally expected that the ``semantic value'' of ~c[name] will be ~c[alist] (~pl[arrays]). This function operates in virtually constant time whether this condition is true or not (unless the ~ilc[compress2] operation is required). But the value returned by this function cannot be used efficiently by subsequent ~c[aset2] operations unless ~c[alist] is the semantic value of ~c[name] when ~c[aset2] is executed. Thus, if the condition is not true, ~c[aset2] prints a ~st[slow array] warning to the comment window. ~l[slow-array-warning]." #+acl2-loop-only (declare (xargs :guard (and (array2p name l) (integerp i) (>= i 0) (< i (car (dimensions name l))) (integerp j) (>= j 0) (< j (cadr (dimensions name l)))))) #+acl2-loop-only (let ((l (cons (cons (cons i j) val) l))) (cond ((> (length l) (maximum-length name l)) (compress2 name l)) (t l))) #-acl2-loop-only (declare (type (unsigned-byte 31) i j)) #-acl2-loop-only (let ((prop (get-acl2-array-property name))) (cond ((eq l (car prop)) (let* ((ar (car (cdr prop))) (to-go (aref (the (array (unsigned-byte 31) (*)) (caddr prop)) 0))) (declare (type (unsigned-byte 31) to-go)) (declare (type (array * (* *)) ar)) (cond ((eql (the (unsigned-byte 31) to-go) 0) (setf (car prop) *invisible-array-mark*) (setf (aref ar i j) val) (let* ((header (cadddr prop)) (d1 (car (cadr (assoc-keyword :dimensions (cdr header))))) (d2 (cadr (cadr (assoc-keyword :dimensions (cdr header))))) (maximum-length (cadr (assoc-keyword :maximum-length (cdr header)))) (default (cadr (assoc-keyword :default (cdr header)))) (x nil) (num 1)) (declare (type (unsigned-byte 31) num d1 d2 maximum-length)) (do ((i (1- d1) (1- i))) ((< i 0)) (declare (type fixnum i)) (do ((j (1- d2) (1- j))) ((< j 0)) (declare (type fixnum j)) (let ((val (aref ar (the fixnum i) (the fixnum j)))) (cond ((equal val default) nil) (t (push (cons (cons i j) val) x) (setq num (the (unsigned-byte 31) (1+ num)))))))) (setq x (cons header x)) (setf (aref (the (array (unsigned-byte 31) (*)) (caddr prop)) 0) (the (unsigned-byte 31) (- maximum-length num))) (setf (car prop) x) x)) (t (let ((x (cons (cons (cons i j) val) l))) (setf (car prop) *invisible-array-mark*) (setf (aref ar i j) val) (setf (aref (the (array (unsigned-byte 31) (*)) (caddr prop)) 0) (the (unsigned-byte 31) (1- to-go))) (setf (car prop) x) x))))) (t (let ((l (cons (cons (cons i j) val) l))) (slow-array-warning 'aset2 name) (cond ((> (length l) (maximum-length name l)) (compress2 name l)) (t l))))))) (defun flush-compress (name) ":Doc-Section Arrays flush the under-the-hood array for the given name~/ ~bv[] Example Form: (flush-compress 'my-array)~/ General Form: (flush-compress name) ~ev[] where ~c[name] is a symbol. Recall that ~c[(compress1 nm alist)] associates an under-the-hood raw Lisp one-dimensional array of name ~c[nm] with the given association list, ~c[alist], while ~c[(compress2 nm alist)] is the analogous function for two-dimensional arrays; ~pl[compress1] and ~pl[compress2]. The only purpose of ~c[flush-compress], which always returns ~c[nil], is to remove the association of any under-the-hood array with the given name, thus eliminating slow array accesses (~pl[slow-array-warning]). It is not necessary if the return values of ~ilc[compress1] and ~ilc[compress2] are always used as the ``current'' copy of the named array, and thus ~c[flush-compress] should rarely, if ever, be needed in user applications. Nevertheless, we provide the following contrived example to show how ~c[flush-compress] can be used to good effect. Comments have been added to this log to provide explanation. ~bv[] ACL2 !>(assign a (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero) (1 . one)))) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ACL2 !>(aref1 'demo (@ a) 0) ZERO ; As expected, the above evaluation did not cause a slow array warning. Now ; we associate a different under-the-hood array with the name 'demo. ACL2 !>(compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero))) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO)) ; The following array access produces a slow array warning because (@ a) is ; no longer associated under-the-hood with the array name 'demo. ACL2 !>(aref1 'demo (@ a) 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ; Now we associate under-the-hood, with array name 'demo, an alist equal to ; (@ a). ACL2 !>(compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero) (1 . one))) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ; The following array access is still slow, because the under-the-hood array ; is merely associated with a copy of (@ a), not with the actual object ; (@ a). ACL2 !>(aref1 'demo (@ a) 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ; So we might try to fix the problem by recompressing. But this doesn't ; work. It would work, by the way, if we re-assign a: ; (assign a (compress1 'demo (@ a))). That is why we usually will not need ; flush-compress. ACL2 !>(compress1 'demo (@ a)) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ACL2 !>(aref1 'demo (@ a) 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ; Finally, we eliminate the warning by calling flush-compress before we call ; compress1. The call of flush-compress removes any under-the-hood ; association of an array with the name 'demo. Then the subsequent call of ; compress1 associates the object (@ a) with that name. (Technical point: ; compress1 always associates the indicated name with the value that it ; returns. in this case, what compress1 returns is (@ a), because (@ a) is ; already, logically speaking, a compressed array1p (starts with a :header ; and the natural number keys are ordered). ACL2 !>(flush-compress 'demo) NIL ACL2 !>(compress1 'demo (@ a)) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ACL2 !>(aref1 'demo (@ a) 0) ZERO ACL2 !> ~ev[]" (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore name)) #+acl2-loop-only nil #-acl2-loop-only (set-acl2-array-property name nil)) ; MULTIPLE VALUE returns, done our way, not Common Lisp's way. ; We implement an efficient mechanism for returning a multiple value, ; with an applicative semantics. Formally, the macro mv is just the ; same as ``list''; one can use it to return a list of arbitrary ; objects. However, the translator for ACL2 checks that mv is in fact ; only used to return values to mv-let, a special form of let which ; picks out the members of a list but does not hold on to the cdrs of ; the list. Because mv-let does not hold on to cdrs, we are able to ; implement mv so that the list is never actually consed up. Instead, ; the elements of the list are passed to mv-let in global locations. ; *number-of-return-values* may be increased (but not reduced) to be ; as high as required to increase the allowed number of ACL2 return ; values. However, if it is increased, the entire ACL2 system must be ; recompiled. Currently, the first 10 locations are handled specially ; in releases of AKCL past 206. #-(or acl2-loop-only acl2-mv-as-values) (progn (defparameter *return-values* (let (ans) (do ((i *number-of-return-values* (1- i))) ((= i 0)) (push (intern (format nil "*return-value-~a*" i)) ans)) ans)) (defmacro declare-return-values () (cons 'progn (declare-return-values1))) (defun declare-return-values1 () (mapcar #'(lambda (v) `(defvar ,v)) *return-values*)) (eval-when #-cltl2 (load eval compile) #+cltl2 (:load-toplevel :execute :compile-toplevel) (declare-return-values)) (defun in-akcl-with-mv-set-and-ref () (member :akcl-set-mv *features*)) (defconstant *akcl-mv-ref-and-set-inclusive-upper-bound* 9) (defmacro special-location (i) (cond ((or (not (integerp i)) (< i 1)) (acl2::interface-er "Macro calls of special-location must have an explicit ~ positive integer argument, which is not the case with ~x0." i)) ((> i *number-of-return-values*) (acl2::interface-er "Not enough built-in return values.")) (t (nth (1- i) *return-values*)))) (defmacro set-mv (i v) (cond ((or (not (integerp i)) (< i 1)) (interface-er "The first argument to a macro call of set-mv must be ~ an explicit positive integer, but that is not the case ~ with ~A." i)) #+akcl ((and (in-akcl-with-mv-set-and-ref) (<= i *akcl-mv-ref-and-set-inclusive-upper-bound*)) `(system::set-mv ,i ,v)) (t `(setf (special-location ,i) ,v)))) (defmacro mv-ref (i) (cond ((or (not (integerp i)) (< i 1)) (interface-er "The argument to macro calls of mv-ref must be an ~ explicit positive integer, but that is not the case with ~x0." i)) #+akcl ((and (in-akcl-with-mv-set-and-ref) (<= i *akcl-mv-ref-and-set-inclusive-upper-bound*)) `(system::mv-ref ,i)) (t `(special-location ,i)))) (defun mv-refs-fn (i) (let (ans) (do ((k i (1- k))) ((= k 0)) (push `(mv-ref ,k) ans)) ans)) (defmacro mv-refs (i) (cond ((and (natp i) (< i *number-of-return-values*)) ; optimization (cons 'list (mv-refs-fn i))) (t `(case ,i ,@(let (ans) (do ((j *number-of-return-values* (1- j))) ((= j 0)) (push `(,j (list ,@(mv-refs-fn j))) ans)) ans) (otherwise (interface-er "Not enough return values.")))))) ) (defun cdrn (x i) (declare (xargs :guard (and (integerp i) (<= 0 i)))) (cond ((zp i) x) (t (cdrn (list 'cdr x) (- i 1))))) (defun mv-nth (n l) ":Doc-Section ACL2::ACL2-built-ins the mv-nth element (zero-based) of a list~/ ~c[(Mv-nth n l)] is the ~c[n]th element of ~c[l], zero-based. If ~c[n] is greater than or equal to the length of ~c[l], then ~c[mv-nth] returns ~c[nil].~/ ~c[(Mv-nth n l)] has a ~il[guard] that ~c[n] is a non-negative integer. ~c[Mv-nth] is equivalent to the Common Lisp function ~ilc[nth] (although without the guard condition that the list is a ~ilc[true-listp]), but is used by ACL2 to access the nth value returned by a multiply valued expression. For example, the following are logically equivalent: ~bv[] (mv-let (erp val state) (read-object ch state) (value (list erp val))) ~ev[] and ~bv[] (let ((erp (mv-nth 0 (read-object ch state))) (val (mv-nth 1 (read-object ch state))) (state (mv-nth 2 (read-object ch state)))) (value (list erp val))) ~ev[] To see the ACL2 definition of ~c[mv-nth], ~pl[pf]. If ~c[EXPR] is an expression that is multiply valued, then the form ~c[(mv-nth n EXPR)] is illegal both in definitions and in forms submitted directly to the ACL2 loop. Indeed, ~c[EXPR] cannot be passed as an argument to any function (~c[mv-nth] or otherwise) in such an evaluation context. The reason is that ACL2 code compiled for execution does not actually create a list for multiple value return; for example, the ~c[read-object] call above logically returns a list of length 3, but when evaluated, it instead stores its three returned values without constructing a list. In such cases you can use ~c[mv-nth] to access the corresponding list by using ~c[mv-list], writing ~c[(mv-nth n (mv-list k EXPR))] for suitable ~c[k], where ~c[mv-list] converts a multiple value result into the corresponding list; ~pl[mv-list].~/" (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (atom l) nil (if (zp n) (car l) (mv-nth (- n 1) (cdr l))))) (defun make-mv-nths (args call i) (declare (xargs :guard (and (true-listp args) (integerp i)))) (cond ((endp args) nil) (t (cons (list (car args) (list 'mv-nth i call)) (make-mv-nths (cdr args) call (+ i 1)))))) #-(or acl2-loop-only acl2-mv-as-values) (defun mv-bindings (lst) ; Gensym a var for every element of lst except the last and pair ; that var with its element in a doublet. Return the list of doublets. (cond ((null (cdr lst)) nil) (t (cons (list (gensym) (car lst)) (mv-bindings (cdr lst)))))) #-(or acl2-loop-only acl2-mv-as-values) (defun mv-set-mvs (bindings i) (cond ((null bindings) nil) (t (cons `(set-mv ,i ,(caar bindings)) (mv-set-mvs (cdr bindings) (1+ i)))))) (defmacro mv (&rest l) ":Doc-Section ACL2::ACL2-built-ins returning a multiple value~/ ~c[Mv] is the mechanism provided by ACL2 for returning two or more values. Logically, ~c[(mv x1 x2 ... xn)] is the same as ~c[(list x1 x2 ... xn)], a list of the indicated values. However, ACL2 avoids the cost of building this list structure, with the cost that ~c[mv] may only be used in a certain style in definitions: if a function ever returns using ~c[mv] (either directly, or by calling another function that returns a multiple value), then this function must always return the same number of values. For more explanation of the multiple value mechanism, ~pl[mv-let]. Also ~pl[mv-list] for a way to convert a multiple value into an ordinary list.~/ ACL2 does not support the Common Lisp construct ~c[values], whose logical meaning seems difficult to characterize. ~c[Mv] is the ACL2 analogue of that construct.~/" (declare (xargs :guard (>= (length l) 2))) #+acl2-loop-only (cons 'list l) #+(and (not acl2-loop-only) acl2-mv-as-values) (return-from mv (cons 'values l)) #+(and (not acl2-loop-only) (not acl2-mv-as-values)) ; In an earlier version of the mv macro, we had a terrible bug. ; (mv a b ... z) expanded to ; (LET ((#:G1 a)) ; (SET-MV 1 b) ; ... ; (SET-MV k z) ; (SETQ *MOST-RECENT-MULTIPLICITY* 3) ; #:G1) ; Note that if the evaluation of z uses a multiple value then it overwrites the ; earlier SET-MV. Now this expansion is safe if there are only two values ; because the only SET-MV is done after the second value is computed. If there ; are three or more value forms, then this expansion is also safe if all but ; the first two are atomic. For example, (mv & & (killer)) is unsafe because ; (killer) may overwrite the SET-MV, but (mv & & STATE) is safe because the ; evaluation of an atomic form is guaranteed not to overwrite SET-MV settings. ; In general, all forms after the second must be atomic for the above expansion ; to be used. ; Suppose we are using GCL. In some cases we can avoid boxing fixnums that are ; the first value returned, by making the following two optimizations. First, ; we insert a declaration when we see (mv (the type expr) ...) where type is ; contained in the set of fixnums. Our second optimization is for the case ; of (mv v ...) where v is an atom, when we avoid let-binding v. To see why ; this second optimization is helpful, consider the following definition. ; (defun foo (x y) ; (declare (type (signed-byte 30) x)) ; (the-mv 2 ; (signed-byte 30) ; (mv x (cons y y)))) ; If we submit this definition to ACL2, the proclaim-form mechanism arranges ; for the following declaim form to be evaluated. ; (DECLAIM (FTYPE (FUNCTION ((SIGNED-BYTE 30) T) ; (VALUES (SIGNED-BYTE 30))) ; FOO)) ; Now let us exit the ACL2 loop and then, in raw Lisp, call disassemble on the ; above defun. Without our second optimization there is boxing: a call of ; CMPmake_fixnum in the output of disassemble. That happens because (mv x ; (cons y y)) macroexpands to something like this: ; (LET ((#:G5579 X)) (SET-MV 1 (CONS Y Y)) #:G5579) ; With the second optimization, however, we get this macroexpansion instead: ; (LET () (SET-MV 1 (CONS Y Y)) X) ; GCL can see that the fixnum declaration for x applies at the occurrence ; above, but fails (as of this writing, using GCL 2.6.8) to recognize that the ; above gensym is a fixnum. (cond ((atom-listp (cddr l)) ; We use the old expansion because it is safe and more efficient. (let* ((v (if (atom (car l)) (car l) (gensym))) (bindings (if (atom (car l)) nil `((,v ,(car l)))))) `(let ,bindings ; See comment above regarding boxing fixnums. ,@(and (consp (car l)) (let ((output (macroexpand-till (car l) 'the))) (cond ((and (consp output) (eq 'the (car output))) `((declare (type ,(cadr output) ,v)))) (t nil)))) ,@(let (ans) (do ((tl (cdr l) (cdr tl)) (i 1 (1+ i))) ((null tl)) (push `(set-mv ,i ,(car tl)) ans)) (nreverse ans)) ,v))) (t ; We expand (mv a b ... y z) to ; (LET ((#:G1 a) ; (#:G2 b) ; ... ; (#:Gk y)) ; (SET-MV k z) ; (SET-MV 1 #:G2) ; ... ; (SET-MV k-1 #:Gk) ; #:G1) (let* ((cdr-bindings (mv-bindings (cdr l))) (v (if (atom (car l)) (car l) (gensym))) (bindings (if (atom (car l)) cdr-bindings (cons (list v (car l)) cdr-bindings)))) `(let ,bindings ; See comment above regarding boxing fixnums. ,@(and (consp (car l)) (let ((output (macroexpand-till (car l) 'the))) (cond ((and (consp output) (eq 'the (car output))) `((declare (type ,(cadr output) ,v)))) (t nil)))) (set-mv ,(1- (length l)) ,(car (last l))) ,@(mv-set-mvs cdr-bindings 1) ,v))))) (defmacro mv? (&rest l) ; Why not simply extend mv and mv-let to handle single values? The reason is ; that there seem to be problems with defining (mv x) to be (list x) and other ; problems with defining (mv x) to be x. ; To see potential problems with defining (mv x) = (list x), consider this ; form: ; (mv-let (x) ; (f y) ; (g x y)) ; We presumably want it to expand as follows. ; (let ((x (f y))) ; (g x y)) ; But suppose (f y) is defined to be (mv (h y)). Then the above mv-let would ; instead have to expand to something like this: ; (let ((x (mv-nth 0 (f y)))) ; or, car instead of (mv-nth 0 ...) ; (g x y)) ; So in order to extend mv and mv-let to handle single values, we'd need to ; look carefully at the rather subtle mv and mv-nth code. It seems quite ; possible that some show-stopping reason would emerge why this approach can't ; work out, or if it does then it might be easy to make mistakes in the ; implementation. Note that we'd need to consider both the cases of ; #+acl2-mv-as-values and #acl2-mv-as-values. ; In a way it seems more natural anyhow that (mv x) is just x, since we don't ; wrap single-valued returns into a list. But that would ruin our simple story ; that mv is logically just list, instead giving us: ; (mv x) = x ; (mv x1 x2 ...) = (list x1 x2 ...) ; Thus it seems safest, and potentially less confusing to users, to introduce ; mv? and mv?-let to be used in cases that single-valued returns are to be ; allowed (presumably in generated code). ":Doc-Section ACL2::ACL2-built-ins return one or more values~/ ~c[Mv?] is designed to work with ~c[mv?-let], generalizing how ~ilc[mv] works with ~ilc[mv-let] by allowing the binding of a single variable. For example, the following form is legal. ~bv[] (mv?-let (y) (mv? (f x)) (declare (type integer y)) (g x y z)) ~ev[] The expression above is equivalent to the following expression, because ~c[(mv? form)] expands to ~c[form] for any expression, ~c[form]. ~bv[] (let ((y (f x))) (declare (type integer y)) (g x y z)) ~ev[] Logically, ~c[(mv? x)] is the same as ~c[x], while ~c[(mv? v1 v2 ...)] is the same as ~c[(list v1 v2 ...)]. Also ~pl[mv] and ~pl[mv?-let].~/~/" (declare (xargs :guard l)) (cond ((null (cdr l)) (car l)) (t `(mv ,@l)))) (defmacro mv-let (&rest rst) ; Warning: If the final logical form of a translated mv-let is ; changed, be sure to reconsider translated-acl2-unwind-protectp. ":Doc-Section ACL2::ACL2-built-ins calling multi-valued ACL2 functions~/ ~bv[] Example Form: (mv-let (x y z) ; local variables (mv 1 2 3) ; multi-valued expression (declare (ignore y)) ; optional declarations (cons x z)) ; body ~ev[] The form above binds the three ``local variables,'' ~c[x], ~c[y], and ~c[z], to the three results returned by the multi-valued expression and then evaluates the body. The result is ~c['(1 . 3)]. The second local, ~c[y], is ~il[declare]d ~c[ignore]d. The multi-valued expression can be any ACL2 expression that returns ~c[k] results, where ~c[k] is the number of local variables listed. Often however it is simply the application of a ~c[k]-valued function. ~c[Mv-let] is the standard way to invoke a multi-valued function when the caller must manipulate the vector of results returned.~/ ~bv[] General Form: (mv-let (var1 ... vark) term body) or (mv-let (var1 ... vark) term (declare ...) ... (declare ...) body) ~ev[] where the ~c[vari] are distinct variables, ~c[term] is a term that returns ~c[k] results and mentions only variables bound in the environment containing the ~c[mv-let] expression, and ~c[body] is a term mentioning only the ~c[vari] and variables bound in the environment containing the ~c[mv-let]. Each ~c[vari] must occur in ~c[body] unless it is ~il[declare]d ~c[ignore]d or ~c[ignorable] in one of the optional ~ilc[declare] forms, unless this requirement is turned off; ~pl[set-ignore-ok]. The value of the ~c[mv-let] term is the result of evaluating ~c[body] in an environment in which the ~c[vari] are bound, in order, to the ~c[k] results obtained by evaluating ~c[term] in the environment containing the ~c[mv-let]. Here is an extended example that illustrates both the definition of a multi-valued function and the use of ~c[mv-let] to call it. Consider a simple binary tree whose interior nodes are ~ilc[cons]es and whose leaves are non-~ilc[cons]es. Suppose we often need to know the number, ~c[n], of interior nodes of such a tree; the list, ~c[syms], of symbols that occur as leaves; and the list, ~c[ints], of integers that occur as leaves. (Observe that there may be leaves that are neither symbols nor integers.) Using a multi-valued function we can collect all three results in one pass. Here is the first of two definitions of the desired function. This definition is ``primitive recursive'' in that it has only one argument and that argument is reduced in size on every recursion. ~bv[] (defun count-and-collect (x) ; We return three results, (mv n syms ints) as described above. (cond ((atom x) ; X is a leaf. Thus, there are 0 interior nodes, and depending on ; whether x is a symbol, an integer, or something else, we return ; the list containing x in as the appropriate result. (cond ((symbolp x) (mv 0 (list x) nil)) ((integerp x)(mv 0 nil (list x))) (t (mv 0 nil nil)))) (t ; X is an interior node. First we process the car, binding n1, syms1, and ; ints1 to the answers. (mv-let (n1 syms1 ints1) (count-and-collect (car x)) ; Next we process the cdr, binding n2, syms2, and ints2. (mv-let (n2 syms2 ints2) (count-and-collect (car x)) ; Finally, we compute the answer for x from those obtained for its car ; and cdr, remembering to increment the node count by one for x itself. (mv (1+ (+ n1 n2)) (append syms1 syms2) (append ints1 ints2))))))) ~ev[] This use of a multiple value to ``do several things at once'' is very common in ACL2. However, the function above is inefficient because it ~il[append]s ~c[syms1] to ~c[syms2] and ~c[ints1] to ~c[ints2], copying the list structures of ~c[syms1] and ~c[ints1] in the process. By adding ``accumulators'' to the function, we can make the code more efficient. ~bv[] (defun count-and-collect1 (x n syms ints) (cond ((atom x) (cond ((symbolp x) (mv n (cons x syms) ints)) ((integerp x) (mv n syms (cons x ints))) (t (mv n syms ints)))) (t (mv-let (n2 syms2 ints2) (count-and-collect1 (cdr x) (1+ n) syms ints) (count-and-collect1 (car x) n2 syms2 ints2))))) ~ev[] We claim that ~c[(count-and-collect x)] returns the same triple of results as ~c[(count-and-collect1 x 0 nil nil)]. The reader is urged to study this claim until convinced that it is true and that the latter method of computing the results is more efficient. One might try proving the theorem ~bv[] (defthm count-and-collect-theorem (equal (count-and-collect1 x 0 nil nil) (count-and-collect x))). ~ev[] Hint: the inductive proof requires attacking a more general theorem. ACL2 does not support the Common Lisp construct ~c[multiple-value-bind], whose logical meaning seems difficult to characterize. ~c[Mv-let] is the ACL2 analogue of that construct. Also ~pl[mv] and ~pl[mv-list].~/" (declare (xargs :guard (and (>= (length rst) 3) (true-listp (car rst)) (>= (length (car rst)) 2)))) #+acl2-loop-only (list* 'let (make-mv-nths (car rst) (list 'mv-list (length (car rst)) (cadr rst)) 0) (cddr rst)) #+(and (not acl2-loop-only) acl2-mv-as-values) (return-from mv-let (cons 'multiple-value-bind rst)) #+(and (not acl2-loop-only) (not acl2-mv-as-values)) (cond ((> (length (car rst)) (+ 1 *number-of-return-values*)) (interface-er "Need more *return-values*. Increase ~ *number-of-return-values* and recompile ACL2.")) (t `(let ((,(car (car rst)) ,(cadr rst)) (,(cadr (car rst)) (mv-ref 1)) ,@(let (ans) (do ((tl (cddr (car rst)) (cdr tl)) (i 2 (1+ i))) ((null tl)) (push (list (car tl) `(mv-ref ,i)) ans)) (nreverse ans))) ,@ (cddr rst))))) (defmacro mv?-let (vars form &rest rst) ; See the comment in mv? for reasons why we do not simply extend mv-let to ; handle single values. ":Doc-Section ACL2::ACL2-built-ins calling possibly multi-valued ACL2 functions~/ ~c[Mv?-let] is a macro that extends the macro ~ilc[mv-let] by allowing a single variable to be bound. Thus, the syntax is the same as that of ~ilc[mv-let] except that ~c[mv?-let] is permitted to bind a single variable to a form that produces a single value. The macros ~c[mv?-let] and ~ilc[mv?] are provided to facilitate the writing of macros that traffic in expressions that could return one or more (multiple) values. For example, the form ~bv[] (mv?-let (y) (f x) (declare (type integer y)) (g x y z)) ~ev[] is equivalent to the following form. ~bv[] (let ((y (f x))) (declare (type integer y)) (g x y z)) ~ev[]~/ Calls of ~c[mv?-let] and of ~ilc[mv-let] are equivalent when the first argument contains at least two variables; ~pl[mv-let] for documentation. In the case of binding a single variable, the general form is ~bv[] (mv?-let (var) term (declare ...) ... (declare ...) body) ~ev[] and this is equivalent to the following form (~pl[let]). ~bv[] (let ((var term)) (declare ...) ... (declare ...) body) ~ev[] Also ~pl[mv?].~/" (declare (xargs :guard (and (true-listp vars) vars))) (cond ((null (cdr vars)) `(let ((,(car vars) ,form)) ,@rst)) (t `(mv-let ,vars ,form ,@rst)))) #+acl2-loop-only (defun mv-list (input-arity x) ":Doc-Section ACL2::ACL2-built-ins converting multiple-valued result to a single-valued list~/ ~bv[] Example Forms: ; Returns the list (3 4): (mv-list 2 (mv 3 4)) ; Returns a list containing the three values returned by var-fn-count: (mv-list 3 (var-fn-count '(cons (binary-+ x y) z) nil))~/ General form: (mv-list n term) ~ev[] Logically, ~c[(mv-list n term)] is just ~c[term]; that is, in the logic ~c[mv-list] simply returns its second argument. However, the evaluation of a call of ~c[mv-list] on explicit values always results in a single value, which is a (null-terminated) list. For evaluation, the term ~c[n] above (the first argument to an ~c[mv-list] call) must ``essentially'' (see below) be an integer not less than 2, where that integer is the number of values returned by the evaluation of ~c[term] (the second argument to that ~c[mv-list] call). We say ``essentially'' above because it suffices that the translation of ~c[n] to a term (~pl[trans]) be of the form ~c[(quote k)], where ~c[k] is an integer greater than 1. So for example, if ~c[term] above returns three values, then ~c[n] can be the expression ~c[3], or ~c[(quote 3)], or even ~c[(mac 3)] if ~c[mac] is a macro defined by ~c[(defmacro mac (x) x)]. But ~c[n] cannot be ~c[(+ 1 2)], because even though that expression evaluates to ~c[3], nevertheless it translates to ~c[(binary-+ '1 '2)], not to ~c[(quote 3)]. ~c[Mv-list] is the ACL2 analogue of the Common Lisp construct ~c[multiple-value-list]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t :mode :logic) (ignore input-arity)) x) #+(and (not acl2-loop-only) acl2-mv-as-values) (defmacro mv-list (input-arity x) (declare (ignore input-arity)) `(multiple-value-list ,x)) #+(and (not acl2-loop-only) (not acl2-mv-as-values)) (defmacro mv-list (input-arity x) `(cons ,x (mv-refs (1- ,input-arity)))) (deflabel state :doc ":Doc-Section ACL2::Programming the von Neumannesque ACL2 state object~/ Note: If you are interested in programming with state, ~pl[programming-with-state] after reading the information below.~/ The ACL2 state object is used extensively in programming the ACL2 system, and has been used in other ACL2 programs as well. However, most users, especially those interested in specification and verification (as opposed to programming ~i[per se]), need not be aware of the role of the state object in ACL2, and will not write functions that use it explicitly. We say more about this point at the end of this documentation topic. The ACL2 state object is an example of a single-threaded object or ~il[stobj]. ACL2 allows the user to define new single-threaded objects. Generally, ACL2 may need to access the ACL2 state but should not (cannot) change it except via a certain set of approved functions such as ~ilc[defun] and ~ilc[defthm]. If you need a state-like object to which you have complete rights, you may want a ~il[stobj]. Key to the idea of our ~c[state] is the notion of single-threadedness. For an explanation, ~pl[stobj]. The upshot of it is that ~c[state] is a variable symbol with severe restrictions on its use, so that it can be passed into only certain functions in certain slots, and must be returned by those functions that ``modify'' it. Henceforth, we do not discuss single-threaded objects in general (which the user can introduce with ~ilc[defstobj] and ~ilc[defabsstobj]) but one in particular, namely ACL2's ~c[state] object. The ~i[global table] is perhaps the most visible portion of the state object. Using the interface functions ~c[@] and ~c[assign], a user may bind global variables to the results of function evaluations (much as an Nqthm user exploits the Nqthm utility ~c[r-loop]). ~l[@], and ~pl[assign]. ACL2 supports several facilities of a truly von Neumannesque state machine character, including file ~il[io] and global variables. Logically speaking, the state is a true list of the 14 components described below. There is a ``current'' state object at the top-level of the ACL2 ~il[command] loop. This object is understood to be the value of what would otherwise be the free variable ~c[state] appearing in top-level input. When any ~il[command] returns a state object as one of its values, that object becomes the new current state. But ACL2 provides von Neumann style speed for state operations by maintaining only one physical (as opposed to logical) state object. Operations on the state are in fact destructive. This implementation does not violate the applicative semantics because we enforce certain draconian syntactic rules regarding the use of state objects. For example, one cannot ``hold on'' to an old state, access the components of a state arbitrarily, or ``modify'' a state object without passing it on to subsequent state-sensitive functions. Every routine that uses the state facilities (e.g. does ~il[io], or calls a routine that does ~il[io]), must be passed a ``state object.'' And a routine must return a state object if the routine modifies the state in any way. Rigid syntactic rules governing the use of state objects are enforced by the function ~c[translate], through which all ACL2 user input first passes. State objects can only be ``held'' in the formal parameter ~c[state], never in any other formal parameter and never in any structure (excepting a multiple-value return list field which is always a state object). State objects can only be accessed with the primitives we specifically permit. Thus, for example, one cannot ask, in code to be executed, for the length of ~c[state] or the ~ilc[car] of ~c[state]. In the statement and proof of theorems, there are no syntactic rules prohibiting arbitrary treatment of state objects. Logically speaking, a state object is a true list whose members are as follows:~bq[] ~c[Open-input-channels], an alist with keys that are symbols in package ~c[\"ACL2-INPUT-CHANNEL\"]. The value (~ilc[cdr]) of each pair has the form ~c[((:header type file-name open-time) . elements)], where ~c[type] is one of ~c[:character], ~c[:byte], or ~c[:object] and ~c[elements] is a list of things of the corresponding ~c[type], i.e. characters, integers of type ~c[(mod 255)], or lisp objects in our theory. ~c[File-name] is a string. ~c[Open-time] is an integer. ~l[io]. ~c[Open-output-channels], an alist with keys that are symbols in package ~c[\"ACL2-OUTPUT-CHANNEL\"]. The value of a pair has the form ~c[((:header type file-name open-time) . current-contents)]. ~l[io]. ~c[Global-table], an alist associating symbols (to be used as ``global variables'') with values. ~l[@], and ~pl[assign]. ~c[T-stack], a list of arbitrary objects accessed and changed by the functions ~c[aref-t-stack] and ~c[aset-t-stack]. ~c[32-bit-integer-stack], a list of arbitrary 32-bit-integers accessed and changed by the functions ~c[aref-32-bit-integer-stack] and ~c[aset-32-bit-integer-stack]. ~c[Big-clock-entry], an integer, that is used logically to bound the amount of effort spent to evaluate a quoted form. ~c[Idates], a list of dates and times, used to implement the function ~c[print-current-idate], which prints the date and time. ~c[Acl2-oracle], a list of objects, used for example to implement the functions that let ACL2 report how much time was used, but inaccessible to the user. Also ~pl[with-prover-time-limit]. ~c[File-clock], an integer that is increased on every file opening and closing, and on each call of ~ilc[sys-call], and is used to maintain the consistency of the ~ilc[io] primitives. ~c[Readable-files], an alist whose keys have the form ~c[(string type time)], where ~ilc[string] is a file name and ~c[time] is an integer. The value associated with such a key is a list of characters, bytes, or objects, according to ~c[type]. The ~c[time] field is used in the following way: when it comes time to open a file for input, we will only look for a file of the specified name and ~c[type] whose time field is that of ~c[file-clock]. This permits us to have a ``probe-file'' aspect to ~c[open-file]: one can ask for a file, find it does not exist, but come back later and find that it does now exist. ~c[Written-files], an alist whose keys have the form ~c[(string type time1 time2)], where ~ilc[string] is a file name, ~c[type] is one of ~c[:character], ~c[:byte] or ~c[:object], and ~c[time1] and ~c[time2] are integers. ~c[Time1] and ~c[time2] correspond to the ~c[file-clock] time at which the channel for the file was opened and closed. This field is write-only; the only operation that affects this field is ~c[close-output-channel], which ~ilc[cons]es a new entry on the front. ~c[Read-files], a list of the form ~c[(string type time1 time2)], where ~ilc[string] is a file name and ~c[time1] and ~c[time2] were the times at which the file was opened for reading and closed. This field is write only. ~c[Writeable-files], an alist whose keys have the form ~c[(string type time)]. To open a file for output, we require that the name, type, and time be on this list. ~c[List-all-package-names-lst], a list of ~c[true-listps]. Roughly speaking, the ~ilc[car] of this list is the list of all package names known to this Common Lisp right now and the ~ilc[cdr] of this list is the value of this ~c[state] variable after you look at its ~ilc[car]. The function, ~c[list-all-package-names], which takes the state as an argument, returns the ~ilc[car] and ~ilc[cdr]s the list (returning a new state too). This essentially gives ACL2 access to what is provided by CLTL's ~c[list-all-packages]. ~ilc[Defpkg] uses this feature to ensure that the about-to-be-created package is new in this lisp. Thus, for example, in ~c[akcl] it is impossible to create the package ~c[\"COMPILER\"] with ~ilc[defpkg] because it is on the list, while in Lucid that package name is not initially on the list. ~c[User-stobj-alist], an alist which associates user-defined single-threaded objects (~pl[stobj]) with their values. ~eq[] We recommend avoiding the use of the state object when writing ACL2 code intended to be used as a formal model of some system, for several reasons. First, the state object is complicated and contains many components that are oriented toward implementation and are likely to be irrelevant to the model in question. Second, there is currently not much support for reasoning about ACL2 functions that manipulate the state object, beyond their logical definitions. Third, the documentation about state is not as complete as one might wish. User-defined single-threaded objects offer the speed of ~c[state] while giving the user complete access to all the fields. ~l[stobj]. Again, if you are interested in programming with state ~pl[programming-with-state].~/ :cited-by Other") (defdoc programming-with-state ":Doc-Section state programming using the von Neumannesque ACL2 ~il[state] object~/ This ~il[documentation] section introduces some common techniques for programming using the ACL2 state object. A prerequisite is thus a basic understanding of that object; ~pl[state]. We hope this section is useful, and we invite suggestions for improvements and additions. A supplement to this section is the ACL2 source code, which uses most (and probably all) of the techniques discussed here. That code is thus a source of many examples, which can serve as ``templates'' to guide one's own programming with state. Recall that ``ACL2'' stands for ``A Computational Logic for Applicative Common Lisp''. In particular, the language is applicative: there are no global variables or side effects. For many purposes this does not feel restrictive; for example, an ACL2 user who is programming in raw Lisp may well be more comfortable coding a factorial function applicatively, using recursion, rather than using iteration with repeated assignment to the same variable. However, there are situations that call for reading or modifying the system state, such as performing input and output, signalling errors, saving information from one computation for use in a later one, or reading and updating system-level or environmental data. This section provides an introductory guide for writing functions that traffic in state. We emphasize that this guide is intended as an introduction; more complete documentation may often be found by following links to documentation of individual utilities, and again, more examples may be found by searching the ACL2 source code for uses of the functions and macros mentioned below. The rest of this section is organized as follows. ~bf[] ~sc[Enabling programming with state] ~sc[State globals and the ACL2 logical world] ~sc[A remark on guards] ~sc[Errors and error triples] ~sc[Sequential programming] ~sc[Binding variables using error triples] ~sc[Binding state global variables] ~sc[Input and output] ~sc[Timings] ~sc[Environment and system] ~sc[Remarks on events and LD] ~sc[Advanced topics] ~ef[]~/ ~sc[Enabling programming with state] In order to submit a definition that takes ~ilc[state] as a formal parameter, you must either declare ~c[state] as a ~c[:]~ilc[stobj] (~pl[xargs]) or first evaluate the following form at the top level: ~c[(set-state-ok t)]. Consider for example the following trivial definition. ~bv[] (defun foo (state) (mv 3 state)) ~ev[] If you submit the above definition in a fresh ACL2 session, you will get this error message. ~bv[] ACL2 Error in ( DEFUN FOO ...): The variable symbol STATE should not be used as a formal parameter of a defined function unless you are aware of its unusual status and the restrictions enforced on its use. See :DOC set-state-ok. ~ev[] If first you evaluate ~c[(set-state-ok t)], you can admit the above definition. Alternatively, you can declare ~c[state] as a ~c[:]~ilc[stobj], as follows. ~bv[] (defun foo (state) (declare (xargs :stobjs state)) (mv 3 state)) ~ev[] A difference in the two approaches is that for the latter, a ~il[guard] proof obligation is generated by default. See the section below entitled ``A remark on guards''. ~sc[State globals and the ACL2 logical world] Recall (~pl[state]) that one of the fields of the ACL2 state object is the global-table, which logically is an alist associating symbols, known as ``state globals'' or ``state global variables'', with values. But no such alist actually exists in the implementation. Instead, ACL2 provides utilities for reading state globals ~-[] ~pl[@] and ~pl[f-get-global] ~-[] and utilities for writing them ~-[] ~pl[assign] and ~pl[f-put-global]. The following log shows how they work; further explanation follows below. ~bv[] ACL2 !>(assign my-var (+ 3 4)) 7 ACL2 !>(@ my-var) 7 ACL2 !>(f-put-global 'my-var (+ 1 5) state) ACL2 !>(f-get-global 'my-var state) 6 ACL2 !> ~ev[] Note that the first result is indented by one space. This is ACL2's way to indicate that the ~ilc[assign] expression returned an ``error triple'' and that no error was signalled. We discuss error triples in more detail below; also ~pl[error-triples]. As illustrated above, the output signatures of the utilities for assigning to state globals differ from each other as follows: ~ilc[f-put-global] returns ~c[state], but ~ilc[assign] returns an error triple ~c[(mv nil val state)] where ~c[val] is the value assigned to the state global. The output signatures of the utilities for reading, ~c[@] and ~c[f-get-global], are identical. In fact, the form ~c[(f-get-global 'my-var state)] is the single-step macroexpansion of the form ~c[(@ my-var)], as can be confirmed using ~ilc[trans1]. ~bv[] ACL2 !>:trans1 (@ my-var) (F-GET-GLOBAL 'MY-VAR STATE) ACL2 !> ~ev[] State globals are useful for conveying persistent state information. Consider for example the utility ~ilc[set-inhibit-output-lst]. The form ~c[(set-inhibit-output-lst '(prove proof-tree))] is approximately equivalent to (assign inhibit-output-lst '(prove proof-tree)). We say ``approximately'' because ~c[set-inhibit-output-lst] additionally does some error checking to insure that all the tokens in the new list are legal. When deciding whether to print output, the ACL2 system reads the value of state global variable ~c[inhibit-output-lst]. A particularly useful state global is ~c[current-acl2-world], whose value is the ACL2 logical ~il[world]. Because the ACL2 world is commonly accessed in applications that use the ACL2 state, ACL2 provides a function that returns the world: ~c[(w state) = (f-get-global 'current-acl2-world state)]. While it is common to read the world, only functions ~c[set-w] and ~c[set-w!] are available to write the world, but these are untouchable and these should generally be avoided except by system implementors (pl[remove-untouchable]). ~sc[A remark on guards] For a function definition (~pl[defun]), if ~c[state] is specified as a ~il[stobj] as with the form ~c[(declare (xargs :stobjs state))], then the ~il[guard] for that function is considered to include the condition ~c[(state-p state)]. By default, ~il[guard] verification will then be performed. We can illustrate this point by modifying the example above as follows, to read the value of state global ~c[gag-mode]. ~bv[] (defun foo (state) (declare (xargs :stobjs state)) (f-get-global 'gag-mode state)) ~ev[] If you try this in a fresh ACL2 session, the proof will fail with the following key checkpoint, which says that the state global ~c[gag-mode] is bound in the global-table of the state. ~bv[] (IMPLIES (STATE-P1 STATE) (ASSOC-EQUAL 'GAG-MODE (NTH 2 STATE))) ~ev[] How can we deal with this proof failure? One way is simply to ignore the issue by defining the function in ~c[:]~ilc[program] mode, as follows. ~bv[] (defun foo (state) (declare (xargs :stobjs state :mode :program)) (f-get-global 'gag-mode state)) ~ev[] Perhaps a better way is to strengthen the guard to assert that the indicated state global is bound, as follows. ~bv[] (defun foo (state) (declare (xargs :guard (boundp-global 'gag-mode state) :stobjs state)) (f-get-global 'gag-mode state)) ~ev[] Also ~pl[guard-miscellany] for a discussion of how guards are generated from ~ilc[xargs] fields of ~il[declare] forms, specifically, for keywords ~c[:guard] and ~c[:stobjs]. ~sc[Errors and error triples] When evaluation returns three values, where the first two are ordinary objects and the third is the ACL2 state, the result may be called an ``error triple''. (Whether it is treated as an error triple depends on the programmer.) Error triples are often denoted ~c[(mv erp val state)], and common ACL2 programming idioms treat ~c[erp] as a flag indicating whether an error is being signalled and ~c[val] as the ``value'' computed. Also ~pl[error-triples]. Even ACL2 users who are not programmers encounter error triples, because these are the values returned by evaluation of ACL2 ~il[events]. Consider the following log, where the only user input is the ~c[defun] form following the ~il[prompt]. ~bv[] ACL2 !>(defun foo (x) x) Since FOO is non-recursive, its admission is trivial. We observe that the type of FOO is described by the theorem (EQUAL (FOO X) X). Summary Form: ( DEFUN FOO ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !> ~ev[] All output above results from explicit calls of output functions, except for the next-to-last line, which contains ~c[FOO]. Notice the single-space indentation preceding ~c[FOO]. That space indicates that in fact, the value returned by evaluation of the ~c[defun] form is the error triple whose error flag is ~c[nil] and whose computed value is ~c[FOO]. By default, ACL2 prints any error triple ~c[(mv nil val state)] by inserting a space before printing ~c[val]. You can change the default by setting state global ~ilc[ld-post-eval-print] to ~c[t]; notice how the same result is printed below. ~bv[] ACL2 !>:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 !>(set-ld-post-eval-print t state) (NIL T ) ACL2 !>(defun foo (x) x) Since FOO is non-recursive, its admission is trivial. We observe that the type of FOO is described by the theorem (EQUAL (FOO X) X). Summary Form: ( DEFUN FOO ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) (NIL FOO ) ACL2 !> ~ev[] The way error triples are printed by ~c[ld] is controlled not only by state global ~c[ld-post-eval-print], but also by state global ~c[ld-error-triples]. These are examples of ``ld specials''; ~pl[ld], ~pl[ld-post-eval-print], and ~pl[ld-error-triples]. It is so common to produce an error triple whose first (error flag) component is ~c[nil] that ACL2 provides a handy macro, ~c[value], for this purpose. Thus, ~c[(value )] is equivalent to ~c[(mv nil state)]. Also ~pl[value-triple] for a similar construct that is a legal event form. We turn now to the topic of errors. The macro ~ilc[ER] ``causes'' an error, but there are really two quite different kinds of errors: ``soft'' and ``hard'' errors. We use the term ``soft error'' to refer to a form that returns an error triple ~c[(mv erp val state)] for which ~c[erp] is non-~c[nil]. Soft errors do not interrupt the normal flow of evaluation: the error triple is returned to the caller which interprets the ~c[erp] flag and ~c[val] as directed by the programmer. Macros discussed below make it convenient to think about soft errors as short-circuiting the computation. Hard errors, on the other hand, do actually rip control away from the current evaluation and return it to the top-level loop. Logically speaking, expressions that cause hard errors return ~c[nil] in the error case, but the ~c[nil] is never seen in actual evaluation because control does not return to the caller. Note that the function ~ilc[abort!], which you can invoke by typing ~c[:]~ilc[a!], always returns to the top level. Note that ACL2 can prove that ~c[(abort!)] returns ~c[nil] but that this cannot be confirmed by computation. ~bv[] ACL2 !>(thm (equal (abort!) nil)) Q.E.D. Summary Form: ( THM ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL) (:TYPE-PRESCRIPTION ABORT!)) Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !>(equal (abort!) nil) Abort to ACL2 top-level ... ACL2 !> ~ev[] (What actually happens with a hard error, including non-default cases, is a bit subtle; most readers will probably want to skip this paragraph. The read-eval-print loop implemented by ~ilc[ld] is implemented by a call of the ACL2 evaluator function, ~c[trans-eval], on each input form. If a hard error occurs during evaluation of an input form, its ~c[trans-eval] call will return with a soft error. ~ilc[Ld], in turn handles that soft error appropriately; ~pl[ld-error-action].) The most common way to signal errors is the macro ~ilc[er], which prints a formatted error message and returns a soft or hard error as specified by the call. Note however that soft errors are signalled using ~c[:]~ilc[program] mode functions. Since the output signatures of soft and hard errors are different ~-[] hard errors ``return'' a single value while soft errors return a triple ~-[] mixing them in an expression requires embedding the hard error form in (an irrelevant) triple, as illustrated below. All branches of the expression must produce an error triple if any branch does. ~bv[] ACL2 !>(defun chk-find-or-abort (e x state) (declare (xargs :mode :program)) (if (endp x) (value ; Note use of VALUE! (er hard 'chk-find-or-abort \"Did not find ~~x0!\" e)) (if (not (integerp (car x))) (er soft 'chk-find-or-abort \"Non-integer, ~~x0, in list!\" (car x)) (if (eql (car x) e) (value x) (chk-find-or-abort e (cdr x) state))))) Summary Form: ( DEFUN CHK-FIND-OR-ABORT ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) CHK-FIND-OR-ABORT ACL2 !>(chk-find-or-abort 3 '(1 2 3 4 5) state) (3 4 5) ACL2 !>(chk-find-or-abort 3 '(1 A 3 4 5) state) ACL2 Error in CHK-FIND-OR-ABORT: Non-integer, A, in list! ACL2 !>(chk-find-or-abort 3 '(1 2 4 5) state) HARD ACL2 ERROR in CHK-FIND-OR-ABORT: Did not find 3! ... ACL2 !> ~ev[] ~l[er] for further discussion of errors. For some other individual topics related to errors ~pl[assert$], ~pl[break-on-error], ~pl[error1], ~pl[hard-error], ~pl[illegal], and ~pl[ld-error-triples]. In the next section we discuss soft errors further, in the context of programming. ~sc[Sequential programming] This section describes handy ways to modify state in steps, using macros that implement a sequence of ~ilc[let] or ~ilc[mv-let] bindings. For example, suppose you want to assign the values 1 and 2 to two state globals ~c[one-var] and ~c[two-var], respectively. Because of ACL2's syntactic restrictions on ~ilc[state], it is not legal simply to write ~c[(f-put-global 'two-var 2 (f-put-global 'one-var 1 state))]. However, ~ilc[let] comes to the rescue as follows. ~bv[] (let ((state (f-put-global 'one-var 1 state))) (let ((state (f-put-global 'two-var 2 state))) state)) ~ev[] It is so common to bind state successively in such a manner that ACL2 provides a macro, ~ilc[pprogn], for this purpose. Thus, an equivalent solution to the problem above is ~bv[] (pprogn (f-put-global 'one-var 1 state) (f-put-global 'two-var 2 state) state) ~ev[] or, more simply, as follows. ~bv[] (pprogn (f-put-global 'one-var 1 state) (f-put-global 'two-var 2 state)) ~ev[] ~l[pprogn]. Note that the last form is allowed to return multiple values; the only requirement on the last form is that its value include ~c[state]. It is also common to update the state using a sequence of forms such that each returns an error triple, where the intention is for evaluation to short-circuit immediately if a soft error is encountered. Suppose ~c[] and ~c[] are expressions that return error triples, where the ~c[state] components of the error triples might be updated, and one wishes to evaluate ~c[] and then ~c[], returning the (multiple) values returned by ~c[] unless the error triple returned by ~c[] is a soft error, in which case that error triple is returned. One can of course do so as follows. ~bv[] (mv-let (erp val state) (cond (erp (mv erp val state)) (t ))) ~ev[] But ACL2 provides a handy macro, ~ilc[er-progn], for this purpose. The following code is equivalent to the code just above. ~bv[] (er-progn ) ~ev[] ~l[er-progn] for more details. Note that unlike ~ilc[pprogn], the return ~il[signature] for the last expression must be the same as that of the others: an error triple. Let's consider how to use ~c[pprogn] and ~c[er-progn] together. In the following example ~c[f1] and ~c[f2] both return ~c[state], while each of ~c[g1] and ~c[g2] returns an error triple. The following code modifies state by executing these in the order ~c[f1], ~c[g1], ~c[f2], and finally ~c[g2], returning ~c[(mv nil val state)] where ~c[val] is the value component of the error triple returned by ~c[g2] ~-[] except we return a soft error if ~c[g1] or ~c[g2] returns a soft error. ~bv[] (pprogn (f1 x state) (er-progn (g1 x state) (pprogn (f2 x state) (g2 x state)))) ~ev[] Finally, consider the ~il[events] ~ilc[progn] and ~ilc[progn!]. These have similar behavior to that of ~ilc[er-progn]. However, ~ilc[progn] and ~ilc[progn!] may only be used in event contexts, for example at the top level or immediately underneath a call of ~ilc[encapsulate] or ~ilc[progn], while ~ilc[er-progn] has no such restriction. So when writing code, use ~c[er-progn] rather than ~ilc[progn] or ~ilc[progn!]. In particular, the body of a ~ilc[defun] must not have any calls of ~c[progn] (or of ~c[progn!] either), and the same restriction holds for any code to be executed, such as the body of a ~ilc[make-event] form. ~sc[Binding variables using error triples] In this section we discuss the macro ~c[er-let*], which is a variant of the special form, ~ilc[let*], that is useful when programming with state. The macro ~c[er-let*] is useful when binding variables to the value components of error triples. It is actually quite similar to ~c[er-progn], described above, except that ~c[er-let*] binds variables. First consider the following example. ~bv[] (er-let* ((x1 (f1 state)) (x2 (f2 x1 state))) (value (cons x1 x2))) ~ev[] The code just above is essentially equivalent to writing the following. ~bv[] (mv-let (erp x1 state) (f1 state) (cond (erp (mv erp x1 state)) (t (mv-let (erp x2 state) (f2 x1 state) (cond (erp (mv erp x2 state)) (t (value (cons x1 x2)))))))) ~ev[] As suggested by the example above, ~c[er-let*] has the same syntax as ~c[let*], except that declarations are not supported. (But note that ~c[ignore] declarations are not needed; all variables that are bound are also used, at least in the error case. Consider replacing ~c[(cons x1 x2)] by ~c[nil] in the example displayed immediately above, and note that ~c[x1] and ~c[x2] are still used.) However, unlike ~c[let*], ~c[er-let*] requires that for each binding ~c[(var expr)], the expression ~c[expr] must evaluate to an error triple and, moreover, it requires that the second argument (the ``body'') of ~c[er-let*] must evaluate to an error triple. If one of the variable expressions (e.g., the ~c[f1] and ~c[f2] calls above) signals an error, its error triple is returned as the value of the ~c[er-let*]. Of course, soft errors can be ``caught'' by using ~ilc[mv-let] instead of ~c[er-let*] and simply ignoring the error flag or, more generally, by returning a non-erroneous error triple even if the error flag was on. ~sc[Binding state global variables] In this section we introduce a utility, ~ilc[state-global-let*], that is an analogue of ~c[let*] for state global variables. Consider the following example. ~bv[] (state-global-let* ((inhibit-output-lst (add-to-set-eq 'summary (@ inhibit-output-lst)))) (thm (equal x x))) ~ev[] This form binds state global variable ~c[inhibit-output-lst] to the result of adding the symbol, ~c[summary], to the current value of that state global. Thus (~pl[set-inhibit-output-lst]), the usual summary is not printed when evaluating this call of ~ilc[thm]. ~l[state-global-let*] for more complete ~il[documentation]. ~sc[Input and output] In ACL2, most input and output involves the ACL2 state. ~l[io]. ~sc[Timings] For how to obtain the time elapsed since the start of the ACL2 session, ~pl[read-run-time]. For a utility for saving times into the ACL2 state and for printing those saved times, see the community book ~c[misc/save-time.lisp]. To time an evaluation (though this really isn't about state), ~pl[time$]. ~sc[Environment and system] Next, we mention briefly some ways in which ACL2 interacts with its environment using the ACL2 state. For how to read and write environment variables, ~pl[getenv$] and ~pl[setenv$]. For how to run a command in the host operating system, ~pl[sys-call]. ~sc[Remarks on events and LD] In general, undefined or surprising behavior may occur when using ACL2 ~il[events] or calling ~il[ld] in your programs. In some cases ACL2 enforces restrictions against these uses. We strongly discourage using ~ilc[ld] in programs, as it has been designed to be called only at the top level of a read-eval-print loop. There is also a restriction on contexts in which ~ilc[make-event] may be called: it may only be called in a context where an event is expected, such as the top level, in a book, or as an argument of ~ilc[encapsulate] or ~ilc[progn]. The reason is that ACL2 does very subtle and careful tracking of ~ilc[make-event] expansions; and it is only able to do this in event contexts, where it is able to carry out such tracking accurately. ~sc[Advanced topics] ACL2 provides the function ~c[trans-eval] to evaluate an arbitrary form (after translating it to a ~il[term], i.e., into internal form). For more information, we refer to reader to comments in the definition of ~c[trans-eval] in the ACL2 source code. There are also many examples of its use in the ACL2 sources. For a function that provides the true absolute filename, with soft links resolved, ~pl[canonical-pathname]. For a function that returns a check-sum on the characters in a channel, ~pl[check-sum]. To obtain a random number, ~pl[random$]. If you are programming in raw-mode (~pl[set-raw-mode]) or in raw Lisp, use the variable ~c[*the-live-state*] in place of the variable ~c[state]. We invite suggestions for additional advanced topics.~/") (defdoc error-triples ":Doc-Section state a common ACL2 programming idiom~/ When evaluation returns three values, where the first two are ordinary (non-~il[stobj]) objects and the third is the ACL2 ~il[state], the result may be called an ``error triple''. If an error triple is ~c[(mv erp val state)], we think of ~c[erp] as an error flag and ~c[val] as the returned value. By default, if the result of evaluating a top-level form is an error triple ~c[(mv erp val state)], then that result is not printed if ~c[erp] is non-~c[nil] or if ~c[val] is the keyword ~c[:INVISIBLE], and otherwise ~c[val] is printed with a preceding space. For example: ~bv[] ACL2 !>(+ 3 4) ; ordinary value 7 ACL2 !>(mv nil (+ 3 4) state) ; error triple, error component of nil 7 ACL2 !>(mv t (+ 3 4) state) ; error triple, non-nil error component ACL2 !>(mv nil :invisible state) ; special case for :INVISIBLE ACL2 !> ~ev[] ~l[programming-with-state] for a discussion of error triples and how to program with them. Also ~pl[ld-error-triples] and ~pl[ld] for a discussion of the value ~c[:COMMAND-CONVENTIONS] for keyword ~c[:LD-POST-EVAL-PRINT].~/~/") (defun update-nth (key val l) ":Doc-Section ACL2::ACL2-built-ins modify a list by putting the given value at the given position~/ ~c[(Update-nth key val l)] returns a list that is the same as the list ~c[l], except that the value at the ~c[0]-based position ~c[key] (a natural number) is ~c[val].~/ If ~c[key] is an integer at least as large as the length of ~c[l], then ~c[l] will be padded with the appropriate number of ~c[nil] elements, as illustrated by the following example. ~bv[] ACL2 !>(update-nth 8 'z '(a b c d e)) (A B C D E NIL NIL NIL Z) ~ev[] We have the following theorem. ~bv[] (implies (and (true-listp l) (integerp key) (<= 0 key)) (equal (length (update-nth key val l)) (if (< key (length l)) (length l) (+ 1 key)))) ~ev[] The ~il[guard] of ~c[update-nth] requires that its first (position) argument is a natural number and its last (list) argument is a true list. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (true-listp l)) (type (integer 0 *) key)) (cond ((zp key) (cons val (cdr l))) (t (cons (car l) (update-nth (1- key) val (cdr l)))))) ; Rockwell Addition: (defun update-nth-array (j key val l) (declare (xargs :guard (and (integerp j) (integerp key) (<= 0 j) (<= 0 key) (true-listp l) (true-listp (nth j l))))) (update-nth j (update-nth key val (nth j l)) l)) ; The following defmacro forms may speed up 32-bit-integerp a little. (defmacro maximum-positive-32-bit-integer () *maximum-positive-32-bit-integer*) (defmacro maximum-positive-32-bit-integer-minus-1 () (+ (- *maximum-positive-32-bit-integer*) -1)) (defun 32-bit-integerp (x) (declare (xargs :guard t)) (and (integerp x) (<= x (maximum-positive-32-bit-integer)) (>= x (maximum-positive-32-bit-integer-minus-1)))) (defthm 32-bit-integerp-forward-to-integerp (implies (32-bit-integerp x) (integerp x)) :rule-classes :forward-chaining) (defun acl2-number-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of numbers~/ The predicate ~c[acl2-number-listp] tests whether its argument is a true list of numbers. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (acl2-numberp (car l)) (acl2-number-listp (cdr l)))))) (defthm acl2-number-listp-forward-to-true-listp (implies (acl2-number-listp x) (true-listp x)) :rule-classes :forward-chaining) (defun rational-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of rational numbers~/ The predicate ~c[rational-listp] tests whether its argument is a true list of rational numbers. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (rationalp (car l)) (rational-listp (cdr l)))))) (defthm rational-listp-forward-to-acl2-number-listp (implies (rational-listp x) (acl2-number-listp x)) :rule-classes :forward-chaining) ;; RAG - This function is analogous to rational-listp. #+:non-standard-analysis (defun real-listp (l) (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (realp (car l)) (real-listp (cdr l)))))) (defdoc real-listp ":Doc-Section ACL2::Real ACL2(r) recognizer for a true list of real numbers~/ The predicate ~c[real-listp] tests whether its argument is a true list of real numbers. This predicate is only defined in ACL2(r) (~pl[real]).~/~/") ;; RAG - Standard forward chaining theorem about -listp. #+:non-standard-analysis (defthm real-listp-forward-to-acl2-number-listp (implies (real-listp x) (acl2-number-listp x)) :rule-classes :forward-chaining) (defun integer-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of integers~/ The predicate ~c[integer-listp] tests whether its argument is a true list of integers. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (integerp (car l)) (integer-listp (cdr l)))))) (defthm integer-listp-forward-to-rational-listp (implies (integer-listp x) (rational-listp x)) :rule-classes :forward-chaining) (defun nat-listp (l) ":Doc-Section ACL2::ACL2-built-ins recognizer for a true list of natural numbers~/ The predicate ~c[nat-listp] tests whether its argument is a true list of natural numbers. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (natp (car l)) (nat-listp (cdr l)))))) (defthm nat-listp-forward-to-integer-listp (implies (nat-listp x) (integer-listp x)) :rule-classes :forward-chaining) ;; RAG - Analogous to the forward rule from integers to rationals. #+:non-standard-analysis (defthm rational-listp-forward-to-real-listp (implies (rational-listp x) (real-listp x)) :rule-classes :forward-chaining) (defun 32-bit-integer-listp (l) (declare (xargs :guard t)) (cond ((atom l) (equal l nil)) (t (and (32-bit-integerp (car l)) (32-bit-integer-listp (cdr l)))))) (defthm 32-bit-integer-listp-forward-to-integer-listp (implies (32-bit-integer-listp x) (integer-listp x)) :rule-classes :forward-chaining) ; Observe that even though we are defining the primitive accessors and ; updaters for states, we do not use the formal parameter STATE as an ; argument. This is discussed in STATE-STATE below. (defun open-input-channels (st) (declare (xargs :guard (true-listp st))) (nth 0 st)) (defun update-open-input-channels (x st) (declare (xargs :guard (true-listp st))) (update-nth 0 x st)) (defun open-output-channels (st) (declare (xargs :guard (true-listp st))) (nth 1 st)) (defun update-open-output-channels (x st) (declare (xargs :guard (true-listp st))) (update-nth 1 x st)) (defun global-table (st) (declare (xargs :guard (true-listp st))) (nth 2 st)) (defun update-global-table (x st) (declare (xargs :guard (true-listp st))) (update-nth 2 x st)) (defun t-stack (st) (declare (xargs :guard (true-listp st))) (nth 3 st)) (defun update-t-stack (x st) (declare (xargs :guard (true-listp st))) (update-nth 3 x st)) (defun 32-bit-integer-stack (st) (declare (xargs :guard (true-listp st))) (nth 4 st)) (defun update-32-bit-integer-stack (x st) (declare (xargs :guard (true-listp st))) (update-nth 4 x st)) (defun big-clock-entry (st) (declare (xargs :guard (true-listp st))) (nth 5 st)) (defun update-big-clock-entry (x st) (declare (xargs :guard (true-listp st))) (update-nth 5 x st)) (defun idates (st) (declare (xargs :guard (true-listp st))) (nth 6 st)) (defun update-idates (x st) (declare (xargs :guard (true-listp st))) (update-nth 6 x st)) (defun acl2-oracle (st) (declare (xargs :guard (true-listp st))) (nth 7 st)) (defun update-acl2-oracle (x st) (declare (xargs :guard (true-listp st))) (update-nth 7 x st)) (defun file-clock (st) (declare (xargs :guard (true-listp st))) (nth 8 st)) (defun update-file-clock (x st) (declare (xargs :guard (true-listp st))) (update-nth 8 x st)) (defun readable-files (st) (declare (xargs :guard (true-listp st))) (nth 9 st)) (defun written-files (st) (declare (xargs :guard (true-listp st))) (nth 10 st)) (defun update-written-files (x st) (declare (xargs :guard (true-listp st))) (update-nth 10 x st)) (defun read-files (st) (declare (xargs :guard (true-listp st))) (nth 11 st)) (defun update-read-files (x st) (declare (xargs :guard (true-listp st))) (update-nth 11 x st)) (defun writeable-files (st) (declare (xargs :guard (true-listp st))) (nth 12 st)) (defun list-all-package-names-lst (st) (declare (xargs :guard (true-listp st))) (nth 13 st)) (defun update-list-all-package-names-lst (x st) (declare (xargs :guard (true-listp st))) (update-nth 13 x st)) ; We use the name ``user-stobj-alist1'' below so that we can reserve the ; name ``user-stobj-alist'' for the same function but which is known to ; take STATE as its argument. See the discussion of STATE-STATE. (defun user-stobj-alist1 (st) (declare (xargs :guard (true-listp st))) (nth 14 st)) (defun update-user-stobj-alist1 (x st) (declare (xargs :guard (true-listp st))) (update-nth 14 x st)) #-acl2-mv-as-values (defconst *initial-raw-arity-alist* ; The list below is used for printing raw mode results. It should include any ; functions that we know have arity 1 (in the sense of mv) but are not in ; *common-lisp-symbols-from-main-lisp-package*. ; The symbol :last means that the number of values returned by the call is the ; number of values returned by the last argument. '((er-progn . :last) (eval-when . :last) ; needed? (let . :last) (let* . :last) (make-event . 3) (mv-let . :last) (prog2$ . :last) (progn . :last) (the . :last) ; needed? (time . :last) (trace . 1) (untrace . 1) (set-raw-mode-on . 3) (set-raw-mode-off . 3) (mv-list . 1) (return-last . :last))) (defconst *initial-checkpoint-processors* ; This constant is used in the implementation of proof-trees. ; We have removed preprocess-clause and simplify-clause because they are ; clearly not checkpoint processors; settled-down-clause, because it shouldn't ; come up anyhow; and :forcing-round, which should not be included unless ; special provision is made for forcing rounds that do not start with this ; marker. Note that :induct is not a real processor, but rather will be a ; marker pointing to the start of the inductive proof of a pushed goal (in ; particular, to the induction scheme). '(eliminate-destructors-clause fertilize-clause generalize-clause eliminate-irrelevance-clause push-clause :induct)) (defconst *primitive-program-fns-with-raw-code* ; This is the list of :program mode functions generated by ; fns-different-wrt-acl2-loop-only in acl2-check.lisp. We have added comments ; to give a sense of why these functions have #-acl2-loop-only code. ; Functions in this list should be executed only in raw Lisp, hence perhaps not ; in safe-mode. See the case of 'program-only-er in ev-fncall-msg. ; This list is used in defining state global 'program-fns-with-raw-code. If ; errors are caused by attempting to call some of these functions in safe-mode, ; consider adding such functions to the list *oneify-primitives*. '(relieve-hyp-synp ; *deep-gstack* apply-abbrevs-to-lambda-stack1 ; *nth-update-tracingp* nth-update-rewriter ; *nth-update-tracingp* ev-w-lst ; *the-live-state* simplify-clause1 ; dmr-flush ev-rec-acl2-unwind-protect ; *acl2-unwind-protect-stack* allocate-fixnum-range ; *the-live-state* trace$-fn-general ; trace ev-fncall! ; apply open-trace-file-fn ; *trace-output* set-trace-evisc-tuple ; *trace-evisc-tuple* ev-fncall-w ; *the-live-state* ev-rec ; wormhole-eval setup-simplify-clause-pot-lst1 ; dmr-flush save-exec-fn ; save-exec-raw, etc. cw-gstack-fn ; *deep-gstack* recompress-global-enabled-structure ; get-acl2-array-property ev-w ; *the-live-state* verbose-pstack ; *verbose-pstk* user-stobj-alist-safe ; chk-user-stobj-alist comp-fn ; compile-uncompiled-defuns fmt-ppr ; print-infix get-memo ; *nu-memos* acl2-raw-eval ; eval pstack-fn ; *pstk-stack* dmr-start-fn ; dmr-start-fn-raw memo-exit ; *nu-memos* memo-key1 ; *nu-memos* ev-fncall-meta ; *metafunction-context* ld-loop ; *ld-level* print-summary ; dmr-flush ev ; *ev-shortcut-okp* ev-lst ; *ev-shortcut-okp* allegro-allocate-slowly-fn ; sys:gsgc-parameter certify-book-fn ; si::sgc-on translate11-flet-alist1 ; special-form-or-op-p include-book-fn1 include-book-fn set-w ; retract-world1, extend-world1, ... prove-loop ; *deep-gstack* chk-virgin ; chk-virgin2 w-of-any-state ; live-state-p lambda-abstract ; *lambda-abstractp* ld-fn-body ; reset-parallelism-variables, *first-entry-to-ld-fn-body-flg* untranslate ; *the-live-state* longest-common-tail-length-rec ; eq compile-function ; compile untranslate-lst ; *the-live-state* ev-synp ; *metafunction-context* add-polys ; *add-polys-counter* dmr-stop-fn ; dmr-stop-fn-raw ld-print-results ; print-infix apply-abbrevs-to-lambda-stack ; *nth-update-tracingp* flpr ; print-flat-infix close-trace-file-fn ; *trace-output* ev-fncall-rec ; raw-ev-fncall ev-fncall ; live-state-p ld-fn0 ; *acl2-unwind-protect-stack*, etc. ld-fn ; unwind-protect write-expansion-file ; compile-uncompiled-*1*-defuns latch-stobjs1 ; eq chk-package-reincarnation-import-restrictions ; [-restrictions2 version] untrace$-fn1 ; eval bdd-top ; (GCL only) si::sgc-on defstobj-field-fns-raw-defs ; call to memoize-flush when #+hons expansion-alist-pkg-names times-mod-m31 ; gcl has raw code iprint-ar-aref1 prove ; #+write-arithmetic-goals make-event-fn oops-warning checkpoint-world ubt-prehistory-fn get-declaim-list pathname-unix-to-os hcomp-build-from-portcullis defconst-val push-warning-frame pop-warning-frame push-warning initialize-accumulated-warnings ev-rec-return-last chk-return-last-entry fchecksum-atom step-limit-error1 waterfall1-lst@par ; for #+acl2-par waterfall1-wrapper@par-before ; for #+acl2-par waterfall1-wrapper@par-after ; for #+acl2-par increment-waterfall-parallelism-counter ; for #+acl2-par flush-waterfall-parallelism-hashtables ; for #+acl2-par clear-current-waterfall-parallelism-ht ; for #+acl2-par setup-waterfall-parallelism-ht-for-name ; for #+acl2-par set-waterfall-parallelism-fn ; for #+acl2-par combined with +hons fix-stobj-array-type set-gc-threshold$-fn certify-book-finish-complete chk-absstobj-invariants get-stobj-creator )) (defconst *primitive-logic-fns-with-raw-code* ; This is the list of :logic mode functions generated by ; fns-different-wrt-acl2-loop-only. We have commented on those functions whose ; #-acl2-loop-only code has side effects. (Side effects are presumably the ; only issue, since functionally the #-acl2-loop-only code had better implement ; the logic code!) We use lower-case when we can live with the ; #+acl2-loop-only code and upper case when we can't. '(mod-expt ; (GCL only) si::powm header search-fn state-p1 ; LIVE-STATE-P aref2 ; aref, slow-array-warning aref1 ; aref, slow-array-warning fgetprop ; EQ, GET, ... getenv$ ; GETENV$-RAW wormhole-eval ; *WORMHOLE-STATUS-ALIST* wormhole1 ; *WORMHOLEP*, ... get-wormhole-status ; *WORMHOLE-STATUS-ALIST* aset2 ; [seems like we can live with logic code] sgetprop ; SGETPROP1 setenv$ ; SI::SETENV ... getprops ; EQ, GET, ... compress1 ; [seems like we can live with logic code] time-limit5-reached-p ; THROW fmt-to-comment-window ; *THE-LIVE-STATE* len ; len1 cpu-core-count ; CORE-COUNT-RAW nonnegative-integer-quotient ; floor check-print-base ; PRIN1-TO-STRING retract-world ; RETRACT-WORLD1 aset1 ; [seems like we can live with logic code] array1p ; get [seems like we can live with logic code] boole$ ; boole array2p ; [seems like we can live with logic code] strip-cdrs ; strip-cdrs1 compress2 ; [seems like we can live with logic code] strip-cars ; strip-cars1 plist-worldp ; *the-live-state* (huge performance penalty?) wormhole-p ; *WORMHOLEP* may-need-slashes-fn ;*suspiciously-first-numeric-array* ... fmt-to-comment-window! ; *THE-LIVE-STATE* has-propsp ; EQ, GET, ... hard-error ; *HARD-ERROR-RETURNS-NILP*, FUNCALL, ... abort! p! ; THROW flush-compress ; SETF [may be critical for correctness] alphorder ; [bad atoms] extend-world ; EXTEND-WORLD1 default-total-parallelism-work-limit ; for #+acl2-par (raw Lisp error) ; The following have arguments of state-state, and hence some may not be of ; concern since presumably users cannot define these redundantly anyhow. But ; we go ahead and include them, just to be safe. user-stobj-alist read-acl2-oracle read-acl2-oracle@par update-user-stobj-alist decrement-big-clock put-global close-input-channel makunbound-global open-input-channel open-input-channel-p1 boundp-global1 global-table-cars1 extend-t-stack list-all-package-names close-output-channel write-byte$ shrink-t-stack aset-32-bit-integer-stack get-global 32-bit-integer-stack-length1 extend-32-bit-integer-stack aset-t-stack aref-t-stack read-char$ aref-32-bit-integer-stack open-output-channel open-output-channel-p1 princ$ read-object big-clock-negative-p peek-char$ shrink-32-bit-integer-stack read-run-time read-byte$ read-idate t-stack-length1 print-object$-ser get-output-stream-string$-fn mv-list return-last ; The following were discovered after we included functions defined in ; #+acl2-loop-only whose definitions are missing (or defined with ; defun-one-output) in #-acl-loop-only. ZPF IDENTITY ENDP NTHCDR LAST REVAPPEND NULL BUTLAST STRING NOT MOD PLUSP ATOM LISTP ZP FLOOR CEILING TRUNCATE ROUND REM REMOVE REMOVE-DUPLICATES LOGBITP ASH LOGCOUNT SIGNUM INTEGER-LENGTH EXPT SUBSTITUTE ZEROP MINUSP ODDP EVENP = /= MAX MIN CONJUGATE LOGANDC1 LOGANDC2 LOGNAND LOGNOR LOGNOT LOGORC1 LOGORC2 LOGTEST ABS STRING-EQUAL STRING< STRING> STRING<= STRING>= STRING-UPCASE STRING-DOWNCASE KEYWORDP EQ EQL CHAR SUBST SUBLIS ACONS NTH SUBSEQ LENGTH REVERSE ZIP STANDARD-CHAR-P ALPHA-CHAR-P UPPER-CASE-P LOWER-CASE-P CHAR< CHAR> CHAR<= CHAR>= CHAR-EQUAL CHAR-UPCASE CHAR-DOWNCASE AND-LIST OR-LIST ; relevant for #+acl2-par ; Might as well add additional ones below: random$ throw-nonexec-error gc$-fn set-compiler-enabled good-bye-fn ; exit-lisp remove-eq remove-equal take file-write-date$ print-call-history set-debugger-enable-fn ; lisp::*break-enable* and *debugger-hook* break$ ; break prin1$ prin1-with-slashes member-equal assoc-equal subsetp-equal no-duplicatesp-equal rassoc-equal remove-equal position-equal maybe-finish-output$ ; Found for hons after fixing note-fns-in-form just before release v4-2. FAST-ALIST-LEN HONS-COPY-PERSISTENT HONS-SUMMARY HONS-CLEAR HONS-WASH HONS-SHRINK-ALIST HONS-EQUAL-LITE CLEAR-HASH-TABLES NUMBER-SUBTREES FAST-ALIST-SUMMARY HONS-ACONS! CLEAR-MEMOIZE-TABLES HONS-COPY HONS-ACONS CLEAR-MEMOIZE-TABLE FAST-ALIST-FREE HONS-EQUAL HONS-RESIZE-FN HONS-GET HONS HONS-SHRINK-ALIST! MEMOIZE-SUMMARY CLEAR-MEMOIZE-STATISTICS make-fast-alist serialize-read-fn serialize-write-fn read-object-suppress assign-lock throw-or-attach-call oracle-apply oracle-apply-raw time-tracker-fn gc-verbose-fn set-absstobj-debug-fn sys-call-status ; *last-sys-call-status* sys-call ; system-call sys-call+ ; system-call+ canonical-pathname ; under dependent clause-processor ; mfc functions mfc-ancestors ; *metafunction-context* mfc-clause ; *metafunction-context* mfc-rdepth ; *metafunction-context* mfc-type-alist ; *metafunction-context* mfc-unify-subst ; *metafunction-context* mfc-world ; *metafunction-context* mfc-ap-fn ; under dependent clause-processor mfc-relieve-hyp-fn ; under dependent clause-processor mfc-relieve-hyp-ttree ; under dependent clause-processor mfc-rw+-fn ; under dependent clause-processor mfc-rw+-ttree ; under dependent clause-processor mfc-rw-fn ; under dependent clause-processor mfc-rw-ttree ; under dependent clause-processor mfc-ts-fn ; under dependent clause-processor mfc-ts-ttree ; under dependent clause-processor magic-ev-fncall ; under dependent clause-processor never-memoize-fn ; The following are introduced into the logic by an encapsulate, but have raw ; Lisp definitions. big-n zp-big-n decrement-big-n ; The following are introduced into the logic with encapsulates, but have their ; raw Lisp definitions provided by defproxy. ancestors-check oncep-tp print-clause-id-okp too-many-ifs-post-rewrite too-many-ifs-pre-rewrite )) (defconst *primitive-macros-with-raw-code* ; This list is generated by fns-different-wrt-acl2-loop-only. '(theory-invariant set-let*-abstractionp defaxiom set-bogus-mutual-recursion-ok set-ruler-extenders delete-include-book-dir certify-book progn! f-put-global push-untouchable set-backchain-limit set-default-hints! set-rw-cache-state! set-override-hints-macro deftheory pstk verify-guards defchoose set-default-backchain-limit set-state-ok set-ignore-ok set-non-linearp set-tau-auto-mode with-output set-compile-fns add-include-book-dir clear-pstk add-custom-keyword-hint initial-gstack acl2-unwind-protect set-well-founded-relation catch-time-limit5 catch-time-limit5@par defuns add-default-hints! local encapsulate remove-default-hints! include-book pprogn set-enforce-redundancy set-ignore-doc-string-error logic er deflabel mv-let program value-triple set-body comp set-bogus-defun-hints-ok dmr-stop defpkg set-measure-function set-inhibit-warnings! defthm mv f-big-clock-negative-p reset-prehistory mutual-recursion set-rewrite-stack-limit set-prover-step-limit add-match-free-override set-match-free-default the-mv table in-arithmetic-theory regenerate-tau-database set-case-split-limitations set-irrelevant-formals-ok remove-untouchable in-theory with-output-forced dmr-start rewrite-entry skip-proofs f-boundp-global make-event set-verify-guards-eagerness wormhole verify-termination-boot-strap start-proof-tree f-decrement-big-clock defabsstobj defstobj defund defttag defdoc push-gframe defthmd f-get-global set-nu-rewriter-mode ; Most of the following were discovered after we included macros defined in ; #+acl2-loop-only whose definitions are missing in #-acl-loop-only. CAAR CADR CDAR CDDR CAAAR CAADR CADAR CADDR CDAAR CDADR CDDAR CDDDR CAAAAR CAAADR CAADAR CAADDR CADAAR CADADR CADDAR CADDDR CDAAAR CDAADR CDADAR CDADDR CDDAAR CDDADR CDDDAR CDDDDR REST MAKE-LIST LIST OR AND * LOGIOR LOGXOR LOGAND SEARCH LOGEQV CONCATENATE LET* DEFUN THE > <= >= + - / 1+ 1- PROGN DEFMACRO COND CASE LIST* APPEND DEFCONST IN-PACKAGE INTERN FIRST SECOND THIRD FOURTH FIFTH SIXTH SEVENTH EIGHTH NINTH TENTH DIGIT-CHAR-P UNMEMOIZE MEMOIZE ; for #+hons DEFUNS-STD DEFTHM-STD DEFUN-STD ; for #+:non-standard-analysis POR PAND PLET PARGS ; for #+acl2-par SPEC-MV-LET ; for #+acl2-par ; The following were included after Version_3.4 as ACL2 continued to evolve. trace! with-live-state with-output-object-channel-sharing with-hcomp-bindings with-hcomp-ht-bindings redef+ redef- bind-acl2-time-limit defattach defproxy count member assoc subsetp no-duplicatesp rassoc remove remove-duplicates position catch-step-limit step-limit-error waterfall-print-clause-id@par ; for #+acl2-par deflock ; for #+acl2-par f-put-global@par ; for #+acl2-par set-waterfall-parallelism with-prover-step-limit waterfall1-wrapper@par ; for #+acl2-par with-waterfall-parallelism-timings ; for #+acl2-par with-parallelism-hazard-warnings ; for #+acl2-par warn-about-parallelism-hazard ; for #+acl2-par with-ensured-parallelism-finishing ; for #+acl2-par state-global-let* ; raw Lisp version for efficiency with-reckless-readtable with-lock catch-throw-to-local-top-level with-fast-alist-raw with-stolen-alist-raw fast-alist-free-on-exit-raw stobj-let add-ld-keyword-alias! set-ld-keyword-aliases! )) (defmacro with-live-state (form) ; Occasionally macros will generate uses of STATE, which is fine in the ACL2 ; loop but can cause compiler warnings in raw Lisp. For example, in v3-4 with ; CCL one found: ; ? [RAW LISP] (trace$) ; ;Compiler warnings : ; ; In an anonymous lambda form: Undeclared free variable STATE ; NIL ; NIL ; ACL2_INVISIBLE::|The Live State Itself| ; ? [RAW LISP] ; The present macro is provided in order to avoid this problem: in raw Lisp ; (with-live-state form) binds state to *the-live-state*. This way, we avoid ; the above compiler warning. ; Unfortunately, our initial solution was unsound. The following book ; certifies in Versions 3.5 and 4.3, and probably all versions inbetween. ; (in-package "ACL2") ; ; (defun foo (state) ; (declare (xargs :stobjs state)) ; (with-live-state state)) ; ; (defthm thm1 ; (equal (caddr (foo (build-state))) ; nil) ; :rule-classes nil) ; ; (defthm thm2 ; (consp (caddr (build-state))) ; :rule-classes nil) ; ; (defthm contradiction ; nil ; :hints (("Goal" ; :use (thm1 thm2) ; :in-theory (disable build-state (build-state)))) ; :rule-classes nil) ; The problem was that state was bound to *the-live-state* for evaluation ; during a proof, where lexically state had a different binding that should ; have ruled. This macro's conde included the check (eq (symbol-value 'state) ; *the-live-state*), which unfortunately was no check at all: it was already ; true because symbol-value returns the global value, and is not affected by a ; superior lexical binding of state. ; Our initial solution defined this macro to be the identity within the usual ; ACL2 loop, as determined by (> *ld-level* 0). But compile-file is called ; when certifying a book, so state remained free in that place, generating a ; compiler warning or (on occasion with CCL) an error. ; So we have decided to keep the existing implementation, in which this macro ; always binds state to *the-live-state* in raw Lisp, but to make this macro ; untouchable. Thus, users can call it freely in raw Lisp or raw-mode, where ; they simply need to understand its spec. But they will never be able to ; exploit it to prove nil (without a trust tag or entering raw Lisp). ; We could avoid making this macro untouchable if we had a way to query the ; lexical environment to see if state is lexically bound. If so, the macro ; call would expand to the identity; if not, it would bind state to ; *the-live-state*. But we found no way in Common Lisp to do that. ":Doc-Section ACL2::ACL2-built-ins allow a reference to ~c[state] in raw Lisp~/ The macro ~c[with-live-state] is an advanced feature that very few users will need (basically, only system hackers). Indeed, it is untouchable; ~pl[remove-untouchable] for how to enable calling ~c[with-live-state] in the ACL2 loop.~/ ~bv[] Example Form: (with-live-state (assign y 3)) General form: (with-live-state form) ~ev[] where form is an arbitrary form with a free reference to the variable ~ilc[state]. Logically, ~c[(with-live-state FORM)] macroexpands to ~c[FORM]. However, in raw Lisp it expands to: ~bv[] (let ((state *the-live-state*)) FORM) ~ev[] If a form that mentions the variable ~ilc[state] might be executed in raw Lisp ~-[] that is, either outside the ACL2 loop or in raw mode (~pl[set-raw-mode]) ~-[] then the surrounding the form with ~c[with-live-state] as shown above can avoid potential warnings or (much less likely) errors. Note however that if ~c[state] is lexically bound to a state other than the usual ``live'' state, surprising behavior may occur when evaluating a call of ~c[with-live-state] in raw Lisp or raw mode (either directly by evaluation or at compile time), because ~c[with-live-state] will override that lexical binding of ~ilc[state] by a lexical binding of ~c[state] to the usual ``live'' state.~/" #+acl2-loop-only form #-acl2-loop-only `(let ((state *the-live-state*)) ,form)) (defun init-iprint-ar (hard-bound enabledp) ; We return an iprint-ar with the given hard-bound. ; As stated in the Essay on Iprinting, we maintain the invariants that the ; dimension of state global 'iprint-ar exceeds the hard bound and that the ; maximum-length of the 'iprint-ar is always at least four times its dimension. ; Therefore, we need to avoid :order nil so that compress can shrink the ; array. ; We write the array ar as we do below so that (equal (compress1 'iprint-ar ar) ; ar) is T. That probably is not important, but it may come in handy at some ; point to know that compress1 is the identity on this array. ; WARNING: Consider carefully comments in rollover-iprint-ar and ; disable-iprint-ar before changing :ORDER. (declare (xargs :guard (natp hard-bound))) (let* ((dim (1+ hard-bound))) `((:HEADER :DIMENSIONS (,dim) :MAXIMUM-LENGTH ,(* 4 dim) :DEFAULT nil :NAME iprint-ar :ORDER :none) (0 . ,(if enabledp 0 (list 0)))))) ; The default bounds for iprinting are deliberately rather high, in order to ; minimize the chance that novice users attempt to read stale #@i# values. ; We assume that for those who use ACL2 with large objects, for whom iprinting ; causes a space problem because of these large bounds, will know to reset the ; bounds using set-iprint. (defconst *iprint-soft-bound-default* 1000) (defconst *iprint-hard-bound-default* 10000) (defdoc parallelism ":Doc-Section Parallelism experimental extension for parallel execution and proofs~/ This documentation topic relates to an experimental extension of ACL2, ACL2(p), created initially by David L. Rager. ~l[compiling-acl2p] for how to build an executable image that supports parallel execution. Also see community books directory ~c[books/parallel/] for examples. For a completely different sort of parallelism, at the system level, ~pl[provisional-certification].~/ IMPORTANT NOTE. We hope and expect that every evaluation result is correctly computed by ACL2(p), and that every formula proved using ACL2(p) is a theorem of the ACL2 logic (and in fact is provable using ACL2). However, we do not guarantee these properties. Since ACL2(p) is intended to be an aid in efficient evaluation and proof development, we focus less on ironclad soundness and more on providing an efficient and working implementation. Nevertheless, if you encounter a case where ACL2(p) computes an incorrect result, or produces a proof where ACL2 fails to do so (and this failure is not discussed in ~il[unsupported-waterfall-parallelism-features]), please notify the implementors. The ACL2 source code provides experimental parallel execution and proof capabilities. For example, one of ACL2's strengths lies in its ability to simulate industrial models efficiently, and it can also decrease the time required for proofs about such models both by making use of parallel evaluation and by dispatching proof subgoals in parallel. While we aim to support Clozure Common Lisp (CCL), Steel Bank Common Lisp (SBCL), and Lispworks, SBCL and Lispworks both currently sometimes experience problems when evaluating the ACL2 proof process (the ``waterfall'') in parallel. Therefore, CCL is the recommend Lisp for anyone that wants to use parallelism and isn't working on fixing those problems.~/") (defdoc parallel-programming ":Doc-Section ACL2::Parallelism parallel programming in ACL2(p)~/ Here we document support for parallel programming in ACL2(p), an experimental extension of ACL2; also ~pl[parallelism].~/ One of ACL2's strengths lies in its ability to execute industrial models efficiently. The ACL2 source code provides an experimental parallel execution capability that can increase the speed of explicit evaluation, including simulator runs using such models, and it can also decrease the time required for proofs that make heavy use of the evaluation of ground terms. The parallelism primitives are ~ilc[plet], ~ilc[pargs], ~ilc[pand], ~ilc[por], and ~ilc[spec-mv-let]. ~ilc[Pand] and ~ilc[por] terminate early when an argument is found to evaluate to ~c[nil] or non-~c[nil], respectively, thus potentially improving on the efficiency of lazy evaluation. ~ilc[Spec-mv-let] is a modification of ~ilc[mv-let] that supports speculative and parallel execution. Of the above five parallelism primitives, all but ~ilc[spec-mv-let] allow for limiting parallel execution (spawning of so-called ``threads'') depending on resource availability. Specifically, the primitives allow specification of a size condition to control the ~il[granularity] under which threads are allowed to spawn. You can use such ~il[granularity] declarations in recursively-defined functions to implement data-dependent parallelism in their execution. We recommend that in order to learn to use the parallelism primitives, you begin by reading examples: ~pl[parallelism-tutorial]. That section will direct you to further documentation topics. In addition to providing parallel programming primitives, ACL2(p) also provides the ability to execute the main ACL2 proof process in parallel. ~l[set-waterfall-parallelism] for further details.~/") (defdoc parallel-proof ; Parallelism blemish: write a few introductory words to "advertise" parallel ; proof in ACL2(p), perhaps by way of a very simple example. ":Doc-Section ACL2::Parallelism parallel proof in ACL2(p)~/ Here we document support for parallel proof in ACL2(p), an experimental extension of ACL2; also ~pl[parallelism], and for parallel programming in particular, ~pl[parallel-programming].~/~/") (defun default-total-parallelism-work-limit () ; The number of pieces of work in the system, *total-work-count* and ; *total-future-count* (depending upon whether one is using the ; plet/pargs/pand/por system or the spec-mv-let system [which is based upon ; futures]), must be less than the ACL2 global total-parallelism-work-limit in ; order to enable creation of new pieces of work or futures. (However, if ; total-parallelism-work-limit were set to 50, we could go from 49 to 69 pieces ; of work when encountering a pand; just not from 50 to 52.) ; Why limit the amount of work in the system? :Doc parallelism-how-to ; (subtopic "Another Granularity Issue Related to Thread Limitations") provides ; an example showing how cdr recursion can rapidly create threads. That ; example shows that if there is no limit on the amount of work we may create, ; then eventually, many successive cdrs starting at the top will correspond to ; waiting threads. If we do not limit the amount of work that can be created, ; this can exhaust the supply of Lisp threads available to process the elements ; of the list. ":Doc-Section Parallel-proof for ACL2(p): returns the default value for global ~c[total-parallelism-work-limit]~/ ~l[set-total-parallelism-work-limit].~/~/" (declare (xargs :guard t)) (let ((val ; Warning: It is possible, in principle to create (+ val ; *max-idle-thread-count*) threads. You'll receive either a hard Lisp error, ; segfault, or complete machine crash if your Lisp cannot create that many ; threads. ; We picked a new value of 400 on September 2011 to support Robert Krug's proof ; that took ~9000 seconds in serial mode. Initially, when ; default-total-parallelism-work-limit returned 50, the parallelized proof only ; saw an improvement to ~2200 seconds, but after changing the return value to ; 400, the parallelized proof now takes ~1300 seconds. ; After doing even more tests, we determined that a limit of 400 is still too ; low (another one of Robert's proofs showed us this). So, now that we have ; the use-case for setting this to the largest number that we think the ; underlying runtime system will support, we do exactly that. As of Jan 26, ; 2012, we think a safe enough limit is 4,000. So, we use that number. As ; multi-threading becomes more prevalent and the underlying runtime systems ; increase their support for massive numbers of threads, we may wish to ; continue to increase this number. Note, however, that since we would also ; like to support older systems, perhaps increasing this number is infeasible, ; since the default should support all systems. ; On April 6, 2012, Rager reworked the way that we use spec-mv-let in the ; waterfall. As such, the limit on the total amount of parallelism work ; allowed in the system now has a different consequence (in terms of the number ; of threads required to process futures). As such, the limit was increased ; from 4,000 to 8,000 on April 11, 2012. 8000)) #+(and acl2-par (not acl2-loop-only)) (let ((bound (* 4 *core-count*))) (when (< val bound) ; Since this check is cheap and not performed while we're doing proofs, we ; leave it. That being said, we do not realistically expect to receive this ; error for a very long time, because it will be a very long time until the ; number of CPU cores is within a factor of 4 of 10,000. David Rager actually ; found this check useful once upon a time (back when the limit was 50), ; because he was testing ACL2(p) on one of the IBM 64-core machines and forgot ; that this limit needed to be increased. (error "The value returned by function ~ default-total-parallelism-work-limit needs to be at ~%least ~ ~s, i.e., at least four times the *core-count*. ~%Please ~ redefine function default-total-parallelism-work-limit so ~ that it ~%is not ~s." bound val))) val)) (defconst *fmt-soft-right-margin-default* 65) (defconst *fmt-hard-right-margin-default* 77) (defconst *initial-global-table* ; Warning: Keep this list in alphabetic order as per ordered-symbol-alistp. It ; must satisfy the predicate ordered-symbol-alistp if build-state is to build a ; state-p. ; When you add a new state global to this table, consider whether to modify ; *protected-system-state-globals*. ; Note that check-state-globals-initialized insists that all state globals that ; are bound by the build are bound in this alist or in ; *initial-ld-special-bindings*. `((abbrev-evisc-tuple . :default) (accumulated-ttree . nil) ; just what succeeded; tracking the rest is hard (acl2-raw-mode-p . nil) (acl2-sources-dir . nil) ; set by initialize-state-globals (acl2-version . ; Keep this value in sync with the value assigned to ; acl2::*copy-of-acl2-version* in file acl2.lisp. ; The reason MCL needs special treatment is that (char-code #\Newline) = 13 in ; MCL, not 10. See also :DOC version. ; ACL2 Version 6.3 ; We put the version number on the line above just to remind ourselves to bump ; the value of state global 'acl2-version, which gets printed out with the ; check-sum info. ; Leave this here. It is read when loading acl2.lisp. This constant should be ; a string containing at least one `.'. The function save-acl2-in-akcl in ; akcl-init.lisp suggests that the user see :doc notexxx, where xxx is the ; substring appearing after the first `.'. ; We have occasion to write fixed version numbers in this code, that is, ; version numbers that are not supposed to be touched when we do ``version ; bump.'' The problem is that version bump tends to replace the standard ; version string with an incremented one, so we need a way to make references ; to versions in some non-standard form. In Lisp comments we tend to write ; these with an underscore instead of a space before the number. Thus, `ACL2 ; Version_2.5' is a fixed reference to that version. In :DOC strings we tend ; to write ACL2 Version 2.5. Note the two spaces. This is cool because HTML ; etc removes the redundant spaces so the output of this string is perfect. ; Unfortunately, if you use the double space convention in Lisp comments the ; double space is collapsed by ctrl-meta-q when comments are formatted. They ; are also collapsed by `fill-format-string', so one has to be careful when ; reformatting :DOC comments. ,(concatenate 'string "ACL2 Version 6.3" #+non-standard-analysis "(r)" #+(and mcl (not ccl)) "(mcl)")) (acl2p-checkpoints-for-summary . nil) (axiomsp . nil) (bddnotes . nil) (certify-book-info . ; Certify-book-info is non-nil when certifying a book, in which case it is a ; certify-book-info record. nil) (check-sum-weirdness . nil) (checkpoint-forced-goals . nil) ; default in :doc (checkpoint-processors . ; avoid unbound var error with collect-checkpoints ,*initial-checkpoint-processors*) (checkpoint-summary-limit . (nil . 3)) (compiled-file-extension . nil) ; set by initialize-state-globals (compiler-enabled . nil) ; Lisp-specific; set by initialize-state-globals (connected-book-directory . nil) ; set-cbd couldn't have put this! (current-acl2-world . nil) (current-package . "ACL2") (debug-pspv . ; This variable is used with #+acl2-par for printing information when certain ; modifications are made to the pspv in the waterfall. David Rager informs us ; in December 2011 that he hasn't used this variable in some time, but that it ; still works as far as he knows. It should be harmless to remove it if there ; is a reason to do so, but of course there would be fallout from doing so ; (e.g., argument lists of various functions that take a debug-pspv argument ; would need to change). nil) (debugger-enable . nil) ; keep in sync with :doc set-debugger-enable (defaxioms-okp-cert . t) ; t when not inside certify-book (deferred-ttag-notes . :not-deferred) (deferred-ttag-notes-saved . nil) (dmrp . nil) (doc-char-subst-table . nil) (doc-fmt-alist . nil) (evisc-hitp-without-iprint . nil) (eviscerate-hide-terms . nil) (fmt-hard-right-margin . ,*fmt-hard-right-margin-default*) (fmt-soft-right-margin . ,*fmt-soft-right-margin-default*) (gag-mode . nil) ; set in lp (gag-mode-evisc-tuple . nil) (gag-state . nil) (gag-state-saved . nil) ; saved when gag-state is set to nil (get-internal-time-as-realtime . nil) ; seems harmless to change (global-enabled-structure . nil) ; initialized in enter-boot-strap-mode (gstackp . nil) (guard-checking-on . t) (host-lisp . nil) (illegal-to-certify-message . nil) (in-local-flg . nil) (in-prove-flg . nil) (in-verify-flg . nil) (infixp . nil) ; See the Essay on Infix below (inhibit-output-lst . (summary)) ; Without this setting, initialize-acl2 ; will print a summary for each event. ; Exit-boot-strap-mode sets this list ; to nil. (inhibit-output-lst-stack . nil) (inhibited-summary-types . nil) (inside-skip-proofs . nil) (iprint-ar . ,(init-iprint-ar *iprint-hard-bound-default* nil)) (iprint-hard-bound . ,*iprint-hard-bound-default*) (iprint-soft-bound . ,*iprint-soft-bound-default*) (keep-tmp-files . nil) (last-make-event-expansion . nil) (last-prover-steps . nil) (last-step-limit . -1) ; any number should be OK (ld-level . 0) (ld-okp . :default) ; see :DOC calling-ld-in-bad-contexts (ld-redefinition-action . nil) (ld-skip-proofsp . nil) (logic-fns-with-raw-code . ,*primitive-logic-fns-with-raw-code*) (macros-with-raw-code . ,*primitive-macros-with-raw-code*) (main-timer . 0) (make-event-debug . nil) (make-event-debug-depth . 0) (match-free-error . nil) ; if t, modify :doc for set-match-free-error (modifying-include-book-dir-alist . nil) (more-doc-max-lines . 45) (more-doc-min-lines . 35) (more-doc-state . nil) (parallel-execution-enabled . nil) (parallelism-hazards-action . nil) ; nil or :error, else treated as :warn (pc-erp . nil) (pc-output . nil) (pc-print-macroexpansion-flg . nil) (pc-print-prompt-and-instr-flg . t) (pc-prompt . "->: ") (pc-prompt-depth-prefix . "#") (pc-ss-alist . nil) (pc-val . nil) (ppr-flat-right-margin . 40) (print-base . 10) (print-case . :upcase) (print-circle . nil) (print-circle-files . t) ; set to nil for #+gcl in LP (print-clause-ids . nil) (print-doc-start-column . 15) (print-escape . t) (print-length . nil) (print-level . nil) (print-lines . nil) (print-pretty . nil) ; default in Common Lisp is implementation dependent (print-radix . nil) (print-readably . nil) (print-right-margin . nil) (program-fns-with-raw-code . ,*primitive-program-fns-with-raw-code*) (prompt-function . default-print-prompt) (prompt-memo . nil) (proof-tree . nil) (proof-tree-buffer-width . ,*fmt-soft-right-margin-default*) (proof-tree-ctx . nil) (proof-tree-indent . "| ") (proof-tree-start-printed . nil) (proofs-co . acl2-output-channel::standard-character-output-0) (raw-arity-alist . nil) (raw-include-book-dir-alist . :ignore) (raw-proof-format . nil) (redo-flat-fail . nil) (redo-flat-succ . nil) (redundant-with-raw-code-okp . nil) (retrace-p . nil) (safe-mode . nil) (save-expansion-file . nil) (saved-output-p . nil) (saved-output-reversed . nil) (saved-output-token-lst . nil) (serialize-character . nil) (serialize-character-system . nil) ; set for #+hons in LP (show-custom-keyword-hint-expansion . nil) (skip-notify-on-defttag . nil) (skip-proofs-by-system . nil) (skip-proofs-okp-cert . t) ; t when not inside certify-book (slow-array-action . :break) ; set to :warning in exit-boot-strap-mode (splitter-output . t) (standard-co . acl2-output-channel::standard-character-output-0) (standard-oi . acl2-output-channel::standard-object-input-0) (step-limit-record . nil) (system-books-dir . nil) ; set in enter-boot-strap-mode and perhaps lp (temp-touchable-fns . nil) (temp-touchable-vars . nil) (term-evisc-tuple . :default) (timer-alist . nil) (tmp-dir . nil) ; set by lp; user-settable but not much advertised. (total-parallelism-work-limit ; for #+acl2-par . ,(default-total-parallelism-work-limit)) (total-parallelism-work-limit-error . t) ; for #+acl2-par (trace-co . acl2-output-channel::standard-character-output-0) (trace-level . 0) (trace-specs . nil) (triple-print-prefix . " ") (ttags-allowed . :all) (undone-worlds-kill-ring . (nil nil nil)) ; By making the above list of nils be of length n you can arrange for ACL2 to ; save n worlds for undoing undos. If n is 0, no undoing of undos is possible. ; If n is 1, the last undo can be undone. (user-home-dir . nil) ; set first time entering lp (verbose-theory-warning . t) (walkabout-alist . nil) (waterfall-parallelism . nil) ; for #+acl2-par (waterfall-parallelism-timing-threshold . 10000) ; #+acl2-par -- microsec limit for resource-and-timing-based mode (waterfall-printing . :full) ; for #+acl2-par (waterfall-printing-when-finished . nil) ; for #+acl2-par (window-interface-postlude . "#>\\>#<\\\\>") (window-interface-prelude . "~%#<\\\\>#<\\<~sw") (window-interfacep . nil) (wormhole-name . nil) (wormhole-status . nil) (write-acl2x . nil) (write-for-read . nil) (writes-okp . t))) #+acl2-loop-only ; not during compilation (value ; avoid value-triple, as state-global-let* is not yet defined in pass 1 (or (ordered-symbol-alistp *initial-global-table*) (illegal 'top-level "*initial-global-table* is not an ordered-symbol-alistp!" nil))) ; Essay on Infix ; ACL2 has a hook for providing a different syntax. We call this different ; syntax "infix" but it could be anything. If the state global variable ; infixp is nil, ACL2 only supports CLTL syntax. If infixp is non-nil ; then infix syntax may be used, depending on the context and the value of ; infixp. ; First, what is the "infix" syntax supported? The answer is "a really stupid ; one." In the built-in infix syntax, a well-formed expression is a dollar ; sign followed by a CLTL s-expression. E.g., if infixp is t one must ; write $ (car (cdr '(a b c))) instead of just (car (cdr '(a b c))). If ; infixp is t, the prover prints formulas by preceding them with a dollar ; sign. This stupid syntax allows the ACL2 developers to test the infix ; hooks without having to invent and implement an new syntax. Such testing ; has helped us identify places where, for example, we printed or read in ; one syntax when the other was expected by the user. ; However, we anticipate that users will redefine the infix primitives so as to ; implement interesting alternative syntax. This note explains the primitives ; which must be redefined. But first we discuss the values of the state ; global variable infixp. ; In addition to nil, infixp can be :in, :out or t (meaning both). As noted, ; if infixp is nil, we use Common Lisp s-expression syntax. If infixp is ; non-nil the syntax used depends on both infixp and on the context. On ; printing, we use infix if infixp is t or :out. On reading from the terminal, ; we expect infix if infixp is :in or t. When reading from files (as in ; include-book) with infixp non-nil, we peek at the file and if it begins with ; (IN-PACKAGE "... ; optionally preceded by Lisp-style comments and whitespace, we use lisp ; syntax, otherwise infix. The check is made with the raw Lisp function ; lisp-book-syntaxp. ; By allowing the :in and :out settings we allow users to type one and see the ; other. We think this might help some users learn the other syntax. ; The following variable and functions, mostly defined in raw Lisp should be ; redefined to implement an alternative infix syntax. ; ; (defparameter *parse* ...) ; (defun parse-infix-from-terminal (eof) ...) ; (defun print-infix (x termp width rpc col file eviscp) ...) ; (defun print-flat-infix (x termp file eviscp) ...) ; (defun flatsize-infix (x termp j max state eviscp) ...) ; We document each of these when we define them for the silly $ syntax. (defun all-boundp (alist1 alist2) (declare (xargs :guard (and (eqlable-alistp alist1) (eqlable-alistp alist2)))) (cond ((endp alist1) t) ((assoc (caar alist1) alist2) (all-boundp (cdr alist1) alist2)) (t nil))) (defun known-package-alistp (x) ; Keep this in sync with make-package-entry. (declare (xargs :guard t)) (cond ((atom x) (null x)) (t (and (true-listp (car x)) ; "final cdr" of book-path is a true-listp (stringp (car (car x))) ; name (symbol-listp (cadr (car x))) ; imports (known-package-alistp (cdr x)))))) (defthm known-package-alistp-forward-to-true-list-listp-and-alistp (implies (known-package-alistp x) (and (true-list-listp x) (alistp x))) :rule-classes :forward-chaining) (defun timer-alistp (x) ; A timer-alistp is an alist binding symbols to lists of rationals. (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) ((and (consp (car x)) (symbolp (caar x)) (rational-listp (cdar x))) (timer-alistp (cdr x))) (t nil))) (defthm timer-alistp-forward-to-true-list-listp-and-symbol-alistp (implies (timer-alistp x) (and (true-list-listp x) (symbol-alistp x))) :rule-classes :forward-chaining) (defun typed-io-listp (l typ) (declare (xargs :guard t)) (cond ((atom l) (equal l nil)) (t (and (case typ (:character (characterp (car l))) (:byte (and (integerp (car l)) (<= 0 (car l)) (< (car l) 256))) (:object t) (otherwise nil)) (typed-io-listp (cdr l) typ))))) (defthm typed-io-listp-forward-to-true-listp (implies (typed-io-listp x typ) (true-listp x)) :rule-classes :forward-chaining) (defconst *file-types* '(:character :byte :object)) (defun open-channel1 (l) (declare (xargs :guard t)) (and (true-listp l) (consp l) (let ((header (car l))) (and (true-listp header) (equal (length header) 4) (eq (car header) :header) (member-eq (cadr header) *file-types*) (stringp (caddr header)) (integerp (cadddr header)) (typed-io-listp (cdr l) (cadr header)))))) (defthm open-channel1-forward-to-true-listp-and-consp (implies (open-channel1 x) (and (true-listp x) (consp x))) :rule-classes :forward-chaining) (defun open-channel-listp (l) ; The following guard seems reasonable (and is certainly necessary, or at least ; some guard is) since open-channels-p will tell us that we're looking at an ; ordered-symbol-alistp. (declare (xargs :guard (alistp l))) (if (endp l) t (and (open-channel1 (cdr (car l))) (open-channel-listp (cdr l))))) (defun open-channels-p (x) (declare (xargs :guard t)) (and (ordered-symbol-alistp x) (open-channel-listp x))) (defthm open-channels-p-forward (implies (open-channels-p x) (and (ordered-symbol-alistp x) (true-list-listp x))) :rule-classes :forward-chaining) (defun file-clock-p (x) (declare (xargs :guard t)) (natp x)) (defthm file-clock-p-forward-to-integerp (implies (file-clock-p x) (natp x)) :rule-classes :forward-chaining) (defun readable-file (x) (declare (xargs :guard t)) (and (true-listp x) (consp x) (let ((key (car x))) (and (true-listp key) (equal (length key) 3) (stringp (car key)) (member (cadr key) *file-types*) (integerp (caddr key)) (typed-io-listp (cdr x) (cadr key)))))) (defthm readable-file-forward-to-true-listp-and-consp (implies (readable-file x) (and (true-listp x) (consp x))) :rule-classes :forward-chaining) (defun readable-files-listp (x) (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (readable-file (car x)) (readable-files-listp (cdr x)))))) (defthm readable-files-listp-forward-to-true-list-listp-and-alistp (implies (readable-files-listp x) (and (true-list-listp x) (alistp x))) :rule-classes :forward-chaining) (defun readable-files-p (x) (declare (xargs :guard t)) (readable-files-listp x)) (defthm readable-files-p-forward-to-readable-files-listp (implies (readable-files-p x) (readable-files-listp x)) :rule-classes :forward-chaining) (defun written-file (x) (declare (xargs :guard t)) (and (true-listp x) (consp x) (let ((key (car x))) (and (true-listp key) (equal (length key) 4) (stringp (car key)) (integerp (caddr key)) (integerp (cadddr key)) (member (cadr key) *file-types*) (typed-io-listp (cdr x) (cadr key)))))) (defthm written-file-forward-to-true-listp-and-consp (implies (written-file x) (and (true-listp x) (consp x))) :rule-classes :forward-chaining) (defun written-file-listp (x) (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (written-file (car x)) (written-file-listp (cdr x)))))) (defthm written-file-listp-forward-to-true-list-listp-and-alistp (implies (written-file-listp x) (and (true-list-listp x) (alistp x))) :rule-classes :forward-chaining) (defun written-files-p (x) (declare (xargs :guard t)) (written-file-listp x)) (defthm written-files-p-forward-to-written-file-listp (implies (written-files-p x) (written-file-listp x)) :rule-classes :forward-chaining) (defun read-file-listp1 (x) (declare (xargs :guard t)) (and (true-listp x) (equal (length x) 4) (stringp (car x)) (member (cadr x) *file-types*) (integerp (caddr x)) (integerp (cadddr x)))) (defthm read-file-listp1-forward-to-true-listp-and-consp (implies (read-file-listp1 x) (and (true-listp x) (consp x))) :rule-classes :forward-chaining) (defun read-file-listp (x) (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (read-file-listp1 (car x)) (read-file-listp (cdr x)))))) (defthm read-file-listp-forward-to-true-list-listp (implies (read-file-listp x) (true-list-listp x)) :rule-classes :forward-chaining) (defun read-files-p (x) (declare (xargs :guard t)) (read-file-listp x)) (defthm read-files-p-forward-to-read-file-listp (implies (read-files-p x) (read-file-listp x)) :rule-classes :forward-chaining) (defun writable-file-listp1 (x) (declare (xargs :guard t)) (and (true-listp x) (equal (length x) 3) (stringp (car x)) (member (cadr x) *file-types*) (integerp (caddr x)))) (defthm writable-file-listp1-forward-to-true-listp-and-consp (implies (writable-file-listp1 x) (and (true-listp x) (consp x))) :rule-classes :forward-chaining) (defun writable-file-listp (x) (declare (xargs :guard t)) (cond ((atom x) (equal x nil)) (t (and (writable-file-listp1 (car x)) (writable-file-listp (cdr x)))))) (defthm writable-file-listp-forward-to-true-list-listp (implies (writable-file-listp x) (true-list-listp x)) :rule-classes :forward-chaining) (defun writeable-files-p (x) (declare (xargs :guard t)) (writable-file-listp x)) (defthm writeable-files-p-forward-to-writable-file-listp (implies (writeable-files-p x) (writable-file-listp x)) :rule-classes :forward-chaining) (defun state-p1 (x) (declare (xargs :guard t)) #-acl2-loop-only (cond ((live-state-p x) (return-from state-p1 t))) (and (true-listp x) (equal (length x) 15) (open-channels-p (open-input-channels x)) (open-channels-p (open-output-channels x)) (ordered-symbol-alistp (global-table x)) (all-boundp *initial-global-table* (global-table x)) (plist-worldp (cdr (assoc 'current-acl2-world (global-table x)))) (symbol-alistp (getprop 'acl2-defaults-table 'table-alist nil 'current-acl2-world (cdr (assoc 'current-acl2-world (global-table x))))) (timer-alistp (cdr (assoc 'timer-alist (global-table x)))) (known-package-alistp (getprop 'known-package-alist 'global-value nil 'current-acl2-world (cdr (assoc 'current-acl2-world (global-table x))))) (true-listp (t-stack x)) (32-bit-integer-listp (32-bit-integer-stack x)) (integerp (big-clock-entry x)) (integer-listp (idates x)) (true-listp (acl2-oracle x)) (file-clock-p (file-clock x)) (readable-files-p (readable-files x)) (written-files-p (written-files x)) (read-files-p (read-files x)) (writeable-files-p (writeable-files x)) (true-list-listp (list-all-package-names-lst x)) (symbol-alistp (user-stobj-alist1 x)))) (defthm state-p1-forward (implies (state-p1 x) (and (true-listp x) (equal (length x) 15) (open-channels-p (nth 0 x)) (open-channels-p (nth 1 x)) (ordered-symbol-alistp (nth 2 x)) (all-boundp *initial-global-table* (nth 2 x)) (plist-worldp (cdr (assoc 'current-acl2-world (nth 2 x)))) (symbol-alistp (getprop 'acl2-defaults-table 'table-alist nil 'current-acl2-world (cdr (assoc 'current-acl2-world (nth 2 x))))) (timer-alistp (cdr (assoc 'timer-alist (nth 2 x)))) (known-package-alistp (getprop 'known-package-alist 'global-value nil 'current-acl2-world (cdr (assoc 'current-acl2-world (nth 2 x))))) (true-listp (nth 3 x)) (32-bit-integer-listp (nth 4 x)) (integerp (nth 5 x)) (integer-listp (nth 6 x)) (true-listp (nth 7 x)) (file-clock-p (nth 8 x)) (readable-files-p (nth 9 x)) (written-files-p (nth 10 x)) (read-files-p (nth 11 x)) (writeable-files-p (nth 12 x)) (true-list-listp (nth 13 x)) (symbol-alistp (nth 14 x)))) :rule-classes :forward-chaining ;; The hints can speed us up from over 40 seconds to less than 2. :hints (("Goal" :in-theory (disable nth length open-channels-p ordered-symbol-alistp all-boundp plist-worldp assoc timer-alistp known-package-alistp true-listp 32-bit-integer-listp integer-listp rational-listp file-clock-p readable-files-p written-files-p read-files-p writeable-files-p true-list-listp symbol-alistp)))) (defun state-p (state-state) (declare (xargs :guard t)) (state-p1 state-state)) ; Let us use state-p1 in our theorem-proving. (in-theory (disable state-p1)) ; The following could conceivably be useful before rewriting a literal ; containing state-p. (defthm state-p-implies-and-forward-to-state-p1 (implies (state-p state-state) (state-p1 state-state)) :rule-classes (:forward-chaining :rewrite)) ; On STATE-STATE ; No one should imagine calling any of the state accessors or updaters ; in executable code. These fields are all ``magic'' in some sense, ; in that they don't actually exist -- or, to put it more accurately, ; we do not represent them concretely as the ACL2 objects we alleged ; them to be in the axioms. In some cases, we might have gone to the ; trouble of supporting these things, at considerable cost, e.g. ; keeping a giant list of all characters printed this year or code to ; recover the logical value of written-files (which shows the times at ; which channels to files were opened and closed) from the actual file ; system. In other cases, such as big-clock-entry, the cost of ; support would have been intuitively equivalent to infinite: no ACL2. ; The user should be grateful that he can even indirectly access these ; fields at all in executable code, and should expect to study the ; means of access with excruciating pain and care. Although the ; fields of states may be THOUGHT of as ordinary logical objects (e.g. ; in theorems), the severe restriction on runtime access to them is ; the PRICE ONE PAYS for (a) high efficiency and (b) real-time ; effects. ; How do we prevent the user from applying, say, written-files, to the ; live state? Well, that is pretty subtle. We simply make the formal ; parameter to written-files be ST rather than STATE. Translate ; enforces the rule that a function may receive STATE only in a slot ; whose STOBJS-IN flag is STATE. And, with only one exception, the ; STOBJS-IN setting is always calculated by noting which formal is ; called STATE. So by giving written-files ST and never resetting its ; STOBJS-IN, we prevent it from being fed the live state (or any ; state) in code (such as defuns and top-level commands) where we are ; checking the use of state. (In theorems, anything goes.) As noted, ; this is the price one pays. ; So what is the exception to the rule that (the STATE flag in) ; STOBJS-IN is determined by STATE's position? The exception is ; managed by super-defun-wart and is intimately tied up with the use ; of STATE-STATE. The problem is that even though we don't permit ; written-files to be called by the user, we wish to support some ; functions (like close-output-channel) which do take state as an ; argument, which may be called by the user and which -- logically ; speaking -- are defined in terms of written-files. ; So consider close-output-channel. We would like to make its second ; parameter be STATE. But it must pass that parameter down to ; written-files in the logical code that defines close-output-channel. ; If that happened, we would get a translate error upon trying to ; define close-output-channel, because we would be passing STATE into ; a place (namely ST) where no state was allowed. So we use ; STATE-STATE instead. But while that lets close-output-channel be ; defined, it doesn't let the user apply it to state. However, after ; the definitional principle has translated the body and during the ; course of its storage of the many properties of the newly defined ; function, it calls super-defun-wart which asks "is this one of the ; special functions I was warned about?" If so, it sets STOBJS-IN and ; STOBJS-OUT for the function properly. A fixed number of functions ; are so built into super-defun-wart, which knows the location of the ; state-like argument and value for each of them. Once ; super-defun-wart has done its job, state must be supplied to ; close-output-channel, where expected. ; "But," you ask, "if state is supplied doesn't it find its way down ; to written-files and then cause trouble because written files isn't ; expecting the live state?" Yes, it would cause trouble if it ever ; got there, but it doesn't. Because for each of the functions that ; use STATE-STATE and are known to super-defun-wart, we provide raw ; lisp code to do the real work. That is, there are two definitions ; of close-output-channel. One, the logical one, is read in ; #+acl2-loop-only mode and presents the prissy logical definition in ; terms of written-files. This definition gets processed during our ; system initialization and generates the usual properties about a ; defined function that allow us to do theorem proving about the ; function. The other, in #-acl2-loop-only, is raw Lisp that knows ; how to close a channel when its given one in the live state. ; So the convention is that those functions (all defined in ; axioms.lisp) which (a) the user is permitted to call with real ; states but which (b) can only be logically defined in terms of calls ; to the primitive state accessors and updaters are (i) defined with ; STATE-STATE as a formal parameter, (ii) have their property list ; smashed appropriately for STOBJS-IN and STOBJS-OUT right after ; their admission, to reflect their true state character, and (iii) ; are operationally defined with raw lisp at some level between the ; defun and the use of the primitive state accessors and updaters. ; We need the following theorem to make sure that we cannot introduce ; via build-state something that fails to be a state. (defmacro build-state (&key open-input-channels open-output-channels global-table t-stack 32-bit-integer-stack (big-clock '4000000) idates acl2-oracle (file-clock '1) readable-files written-files read-files writeable-files list-all-package-names-lst user-stobj-alist) (list 'build-state1 (list 'quote open-input-channels) (list 'quote open-output-channels) (list 'quote (or global-table *initial-global-table*)) (list 'quote t-stack) (list 'quote 32-bit-integer-stack) (list 'quote big-clock) (list 'quote idates) (list 'quote acl2-oracle) (list 'quote file-clock) (list 'quote readable-files) (list 'quote written-files) (list 'quote read-files) (list 'quote writeable-files) (list 'quote list-all-package-names-lst) (list 'quote user-stobj-alist))) (defconst *default-state* (list nil nil *initial-global-table* nil nil 4000000 nil nil 1 nil nil nil nil nil nil)) (defun build-state1 (open-input-channels open-output-channels global-table t-stack 32-bit-integer-stack big-clock idates acl2-oracle file-clock readable-files written-files read-files writeable-files list-all-package-names-lst user-stobj-alist) (declare (xargs :guard (state-p1 (list open-input-channels open-output-channels global-table t-stack 32-bit-integer-stack big-clock idates acl2-oracle file-clock readable-files written-files read-files writeable-files list-all-package-names-lst user-stobj-alist)))) ; The purpose of this function is to provide a means for constructing ; a state other than the live state. (let ((s (list open-input-channels open-output-channels global-table t-stack 32-bit-integer-stack big-clock idates acl2-oracle file-clock readable-files written-files read-files writeable-files list-all-package-names-lst user-stobj-alist))) (cond ((state-p1 s) s) (t *default-state*)))) ; Although the two following functions are only identity functions ; from the logical point of view, in the von Neumann machinery ; implementation they are potentially dangerous and should not ; be used anywhere besides trans-eval. (defun coerce-state-to-object (x) (declare (xargs :guard t)) x) (defun coerce-object-to-state (x) (declare (xargs :guard t)) x) (verify-termination-boot-strap create-state) ; GLOBALS #-acl2-loop-only (defun-one-output strip-numeric-postfix (sym) (coerce (reverse (do ((x (reverse (coerce (symbol-name sym) 'list)) (cdr x))) ((or (null x) (eq (car x) #\-)) (cdr x)))) 'string)) (defun global-table-cars1 (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from global-table-cars1 (let (ans) (dolist (package-entry (global-val 'known-package-alist (w *the-live-state*))) (do-symbols (sym (find-package (concatenate 'string *global-package-prefix* (package-entry-name package-entry)))) (cond ((boundp sym) (push (intern (symbol-name sym) (package-entry-name package-entry)) ans))))) (sort ans (function symbol-<)))))) (strip-cars (global-table state-state))) (defun global-table-cars (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (state-p1 state-state))) (global-table-cars1 state-state)) (defun boundp-global1 (x state-state) (declare (xargs :guard (and (symbolp x) (state-p1 state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from boundp-global1 (boundp (global-symbol x))))) (cond ((assoc x (global-table state-state)) t) (t nil))) (defun boundp-global (x state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (symbolp x) (state-p1 state-state)))) (boundp-global1 x state-state)) (defmacro f-boundp-global (x st) #-acl2-loop-only (cond ((and (consp x) (eq 'quote (car x)) (symbolp (cadr x)) (null (cddr x))) (let ((s (gensym))) `(let ((,s ,st)) (declare (special ,(global-symbol (cadr x)))) (cond ((eq ,s *the-live-state*) (boundp ',(global-symbol (cadr x)))) (t (boundp-global ,x ,s)))))) (t `(boundp-global ,x ,st))) #+acl2-loop-only (list 'boundp-global x st)) (defun makunbound-global (x state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; This function is not very fast because it calls global-symbol. A ; faster version could easily be created. (declare (xargs :guard (and (symbolp x) (state-p1 state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (cond ((boundp-global1 x state-state) ; If the variable is not bound, then the makunbound below doesn't do ; anything and we don't have to save undo information. (Furthermore, ; there is nothing to save.) (push-wormhole-undo-formi 'put-global x (get-global x state-state)))))) (makunbound (global-symbol x)) (return-from makunbound-global *the-live-state*))) (update-global-table (delete-assoc-eq x (global-table state-state)) state-state)) #+acl2-loop-only (defun get-global (x state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; Keep this in sync with the #+acl2-loop-only definition of get-global (which ; uses qfuncall). (declare (xargs :guard (and (symbolp x) (state-p1 state-state) (boundp-global1 x state-state)))) (cdr (assoc x (global-table state-state)))) (defun put-global (key value state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (symbolp key) (state-p1 state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (cond ((boundp-global1 key state-state) (push-wormhole-undo-formi 'put-global key (get-global key state-state))) (t (push-wormhole-undo-formi 'makunbound-global key nil))))) (setf (symbol-value (global-symbol key)) value) (return-from put-global state-state))) (update-global-table (add-pair key value (global-table state-state)) state-state)) (defmacro f-put-global (key value st) ":Doc-Section ACL2::ACL2-built-ins assign to a global variable in ~ilc[state]~/ ~bv[] Examples: (f-put-global 'x (expt 2 10) state) (f-put-global 'a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B) state)~/ General Form: (f-put-global (quote symbol) term state) ~ev[] where ~c[symbol] is any symbol (with certain enforced exclusions to avoid overwriting ACL2 system ``globals'') and ~c[term] is any ACL2 term that could be evaluated at the top-level. ~c[F-put-global] evaluates the term, stores the result as the value of the given symbol in the ~c[global-table] of ~ilc[state], and returns the new ~c[state]. (Note: the actual implementation of the storage of this value is much more efficient than this discussion of the logic might suggest.) The macro ~ilc[assign] is closely related to ~c[f-put-global]: ~c[(assign var val)] macroexpands to ~c[(f-put-global 'var val state)]. The macros ~ilc[@] and ~ilc[f-get-global] give convenient access to the value of such globals. The ~c[:]~ilc[ubt] operation has no effect on the ~c[global-table] of ~ilc[state]. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.~/" #-acl2-loop-only (cond ((and (consp key) (eq 'quote (car key)) (symbolp (cadr key)) (null (cddr key))) (let ((v (gensym)) (s (gensym))) `(let ((,v ,value) (,s ,st)) (cond ((live-state-p ,s) (cond (*wormholep* (cond ((boundp-global1 ,key ,s) (push-wormhole-undo-formi 'put-global ,key (get-global ,key ,s))) (t (push-wormhole-undo-formi 'makunbound-global ,key nil))))) (let () (declare (special ,(global-symbol (cadr key)))) ,@(when (eq (cadr key) 'acl2-raw-mode-p) `((observe-raw-mode-setting ,v ,s))) (setq ,(global-symbol (cadr key)) ,v) ,s)) (t (put-global ,key ,v ,s)))))) (t `(put-global ,key ,value ,st))) #+acl2-loop-only (list 'put-global key value st)) #+acl2-par (defmacro f-put-global@par (key value st) ; WARNING: Every use of this macro deserves an explanation that addresses the ; following concern! This macro is used to modify the live ACL2 state, without ; passing state back! This is particularly dangerous if we are calling ; f-put-global@par in two threads that are executing concurrently, since the ; second use will override the first. (declare (ignorable key value st)) #+acl2-loop-only nil #-acl2-loop-only `(progn (f-put-global ,key ,value ,st) nil)) ; We now define state-global-let*, which lets us "bind" state ; globals. (defconst *initial-ld-special-bindings* ; This alist is used by initialize-acl2 to set the initial values of the LD ; specials. It is assumed by reset-ld-specials that the first three are the ; channels. `((standard-oi . ,*standard-oi*) (standard-co . ,*standard-co*) (proofs-co . ,*standard-co*) (ld-skip-proofsp . nil) (ld-redefinition-action . nil) (ld-prompt . t) (ld-missing-input-ok . nil) (ld-pre-eval-filter . :all) (ld-pre-eval-print . nil) (ld-post-eval-print . :command-conventions) (ld-evisc-tuple . nil) (ld-error-triples . t) (ld-error-action . :continue) (ld-query-control-alist . nil) (ld-verbose . "~sv. Level ~Fl. Cbd ~xc.~|System books ~ directory ~xb.~|Type :help for help.~%Type (good-bye) to ~ quit completely out of ACL2.~|~%"))) (defun always-boundp-global (x) (declare (xargs :guard (symbolp x))) (or (assoc-eq x *initial-global-table*) (assoc-eq x *initial-ld-special-bindings*))) (defun state-global-let*-bindings-p (lst) ; This function returns t iff lst is a true-list and each element is ; a doublet of the form (symbolp anything) or a triplet of the form (symbolp ; anything symbolp). (declare (xargs :guard t)) (cond ((atom lst) (eq lst nil)) (t (and (consp (car lst)) (symbolp (caar lst)) (consp (cdar lst)) (or (null (cddar lst)) (and (consp (cddar lst)) (symbolp (car (cddar lst))) (null (cdr (cddar lst))))) (state-global-let*-bindings-p (cdr lst)))))) (defun state-global-let*-get-globals (bindings) ; This function is used to generate code for the macroexpansion of ; state-global-let*. Roughly speaking, it returns a list, lst, of f-get-global ; forms that fetch the values of the variables we are about to smash. The ; expansion of state-global-let* will start with (LET ((temp (LIST ,@lst))) ; ...) and we will use the value of temp to restore the globals after the ; execution of the body. ; Now there is a subtlety. Some of the vars we are to "bind" might NOT be ; already bound in state. So we don't want to call f-get-global on them until ; we know they are bound, and for those that are not, "restoring" their old ; values means making them unbound again. So a careful specification of the ; value of temp (i.e., the value of (LIST ,@lst) where lst is what we are ; producing here) is that it is a list in 1:1 correspondence with the vars ; bound in bindings such that the element corresponding to the var x is nil if ; x is unbound in the pre-body state and is otherwise a singleton list ; containing the value of x in the pre-body state. (declare (xargs :guard (state-global-let*-bindings-p bindings))) (cond ((endp bindings) nil) (t (cons (if (always-boundp-global (caar bindings)) `(list (f-get-global ',(caar bindings) state)) `(if (f-boundp-global ',(caar bindings) state) (list (f-get-global ',(caar bindings) state)) nil)) (state-global-let*-get-globals (cdr bindings)))))) (defun state-global-let*-put-globals (bindings) ; This function is used to generate code for the macroexpansion of ; state-global-let*. It generates a list of f-put-globals that will set the ; bound variables in bindings to their desired local values, except that ; ``setters'' are used instead where provided (see the discussion of setters in ; state-global-let*). We insist that those initialization forms not mention ; the temporary variable state-global-let* uses to hang onto the restoration ; values. (declare (xargs :guard (state-global-let*-bindings-p bindings))) (cond ((endp bindings) nil) (t (cons (let ((val-form `(check-vars-not-free (state-global-let*-cleanup-lst) ,(cadar bindings)))) (cond ((cddr (car bindings)) `(if (f-boundp-global ',(caar bindings) state) (,(caddr (car bindings)) ; setter ,val-form state) (prog2$ (er hard 'state-global-let* "It is illegal to bind an unbound variable ~ in state-global-let*, in this case, ~x0, ~ when a setter function is supplied." ',(caar bindings)) state))) (t `(f-put-global ',(caar bindings) ,val-form state)))) (state-global-let*-put-globals (cdr bindings)))))) (defun state-global-let*-cleanup (bindings index) ; This function is used to generate code for the macroexpansion of ; state-global-let*. We generate a list of forms that when executed will ; restore the "bound" variables to their original values, using the list of ; restoration values. Recall that each restoration value is either a nil, ; indicating the variable was unbound, or a singleton listing the original ; value. We are generating that code. Index is the number of CDRs to be taken ; of the restoration values list that begins with the value for the first ; variable in bindings. It is initially 0, to represent the temporary variable ; used by state-global-let*, and is incremented by 1 on each call so that the ; restoration values list is symbolically CDRd ever time we recurse here. ; Note: Once upon a time we used a recursive function to do the cleanup. It ; essentially swept through the names of the state globals as it swept through ; the list of their initial values and did an f-put-global on each (here ; ignoring the unbound variable problem). That was dangerous because it ; violated the rules that f-put-global was only called on a quoted var. Those ; rules allow translate to enforce untouchables. To get away with it, we had ; to exempt that function from translate's restrictions on f-put-global. We ; thought we could regain security by then putting that function name on ; untouchables. But since calls to that function were laid down in macros, it ; can't be untouchable if the user is to use the macros. So we did it this ; way, which makes each f-put-global explicit and needs no special treatment. ; Finally, note that we use setters in place of f-put-global, when they are ; provided; see the discussion of setters in state-global-let*. (declare (xargs :guard (and (state-global-let*-bindings-p bindings) (natp index)))) (let ((cdr-expr 'state-global-let*-cleanup-lst)) (cond ((endp bindings) nil) (t (cons (cond ((cddr (car bindings)) `(,(caddr (car bindings)) (car (nth ,index ,cdr-expr)) state)) ((always-boundp-global (caar bindings)) `(f-put-global ',(caar bindings) (car (nth ,index ,cdr-expr)) state)) (t `(if (nth ,index ,cdr-expr) (f-put-global ',(caar bindings) (car (nth ,index ,cdr-expr)) state) (makunbound-global ',(caar bindings) state)))) (state-global-let*-cleanup (cdr bindings) (1+ index))))))) #+(and acl2-par (not acl2-loop-only)) (defparameter *possible-parallelism-hazards* ; If *possible-parallelism-hazards* is non-nil and state global ; 'parallelism-hazards-action is non-nil, then any operation known to cause ; problems in a parallel environment will print a warning (and maybe cause an ; error). For example, we know that calling state-global-let* in any ; environment where parallel execution is enabled could cause problems. See ; the use of with-parallelism-hazard-warnings inside waterfall and the use of ; warn-about-parallelism-hazard inside state-global-let* for how we warn the ; user of such potential pitfalls. ; Note that the ACL2 developer is not anticipated to set and clear this ; variable with a call like "setf" -- this should probably be done by using ; with-parallelism-hazard-warnings. ; Here is a simple example that demonstrates their use: ; (set-state-ok t) ; (skip-proofs ; (defun foo (state) ; (declare (xargs :guard t)) ; (state-global-let* ; ((x 3)) ; (value (f-get-global 'x state))))) ; (skip-proofs ; (defun bar (state) ; (declare (xargs :guard t)) ; (with-parallelism-hazard-warnings ; (foo state)))) ; (set-waterfall-parallelism :full) ; (bar state) ; prints the warning ; See also the comment in warn-about-parallelism-hazard for a detailed ; specification of how this all works. nil) (defmacro with-parallelism-hazard-warnings (body) ; See the comment in warn-about-parallelism-hazard. #+(and acl2-par (not acl2-loop-only)) `(let ((*possible-parallelism-hazards* t)) ,body) #-(and acl2-par (not acl2-loop-only)) body) (defmacro warn-about-parallelism-hazard (call body) ; This macro can cause a warning or error if raw Lisp global ; *possible-parallelism-hazards* is bound to t or :error, respectively. Such ; binding takes place with a call of with-parallelism-hazard-warnings. This ; macro is essentially a no-op when not in the scope of such a call, since ; *possible-parallelism-hazards* is nil by default. ; It is the programmer's responsibility to wrap this macro around any code (or ; callers that lead to such code) that can result in any "bad" behavior due to ; executing that code in a multi-threaded setting. For example, we call this ; macro in state-global-let*, which we know can be unsafe to execute in ; parallel with other state-modifying code. And that's a good thing, since for ; example state-global-let* is called by wormhole printing, which is invoked by ; the code (io? prove t ...) in waterfall-msg when parallelism is enabled. ; Recall the first paragraph above. Thus, state-global-let* does not cause any ; such warning or error by default, which is why in a #+acl2-par build, there ; is a call of with-parallelism-hazard-warnings in waterfall. #-(and acl2-par (not acl2-loop-only)) (declare (ignore call)) #+(and acl2-par (not acl2-loop-only)) `(progn (when (and *possible-parallelism-hazards* (f-get-global 'waterfall-parallelism state) (f-get-global 'parallelism-hazards-action *the-live-state*)) ; If a user sends an "offending call" as requested in the email below, consider ; adding a parallelism wart in the definition of the function being called, ; documenting that a user has actually encountered an execution of ACL2(p) that ; ran a function that we have indentified as not thread-safe (via ; warn-about-parallelism-hazard) inside a context that we have identified as ; eligible for parallel execution (via with-parallelism-hazard-warnings). (Or ; better yet, make a fix.) See the comments at the top of this function for ; more explanation. (format t "~%WARNING: A macro or function has been called that is not~%~ thread-safe. Please email this message, including the~%~ offending call and call history just below, to the ACL2 ~%~ implementors.~%") (let ((*print-length* 10) (*print-level* 10)) (pprint ',call) (print-call-history)) (format t "~%~%To disable the above warning, issue the form:~%~%~ ~s~%~%" '(f-put-global 'parallelism-hazards-action nil state)) (when (eq (f-get-global 'parallelism-hazards-action *the-live-state*) :error) (error "Encountered above parallelism hazard"))) ,body) #-(and acl2-par (not acl2-loop-only)) body) (defmacro with-ensured-parallelism-finishing (form) #+(or acl2-loop-only (not acl2-par)) form #-(or acl2-loop-only (not acl2-par)) `(our-multiple-value-prog1 ,form (loop while (futures-still-in-flight) as i from 1 do (progn (when (eql (mod i 10) 5) (cw "Waiting for all proof threads to finish~%")) (sleep 0.1))))) (defmacro state-global-let* (bindings body) ; NOTE: In April 2010 we discussed the possibility that we could simplify the ; raw-Lisp code for state-global-let* to avoid acl2-unwind-protect, in favor of ; let*-binding the state globals under the hood so that clean-up is done ; automatically by Lisp; after all, state globals are special variables. We ; see no reason why this can't work, but we prefer not to mess with this very ; stable code unless/until there is a reason. (Note that we however do not ; have in mind any potential change to the logic code for state-global-let*.) ; See state-free-global-let* for such a variant that is appropriate to use when ; state is not available. ":Doc-Section ACL2::ACL2-built-ins bind ~il[state] global variables~/ ~l[programming-with-state] for requisite background on programming with the ACL2 ~il[state]. ~bv[] Example Forms: (state-global-let* ((inhibit-output-lst *valid-output-names*)) (thm (equal x x))) (state-global-let* ((fmt-hard-right-margin 1000 set-fmt-hard-right-margin) (fmt-soft-right-margin 1000 set-fmt-soft-right-margin)) (mini-proveall))~/ General Form: (state-global-let* ((var1 form1) ; or (var1 form1 set-var1) ... (vark formk) ; or (vark formk set-vark) ) body) ~ev[] where: each ~c[vari] is a variable; each ~c[formi] is an expression whose value is a single ordinary object (i.e. not multiple values, and not ~il[state] or any other ~il[stobj]); ~c[set-vari], if supplied, is a function with ~il[signature] ~c[((set-vari * state) => state)]; and ~c[body] is an expression that evaluates to an error triple (~pl[error-triples]). Each ~c[formi] is evaluated in order, starting with ~c[form1], and with each such binding the state global variable ~c[vari] is bound to the value of ~c[formi], sequentially in the style of ~ilc[let*]. More precisely, then meaning of this form is to set (in order) the global values of the indicated ~il[state] global variables ~c[vari] to the values of ~c[formi] using ~ilc[f-put-global], execute ~c[body], restore the ~c[vari] to their previous values (but see the discussion of setters below), and return the triple produced by body (with its state as modified by the restoration). The restoration is guaranteed even in the face of aborts. The ``bound'' variables may initially be unbound in state and restoration means to make them unbound again. Still referring to the General Form above, let ~c[old-vali] be the value of state global variable ~c[vari] at the time ~c[vari] is about to be assigned the value of ~c[formi]. If ~c[set-vari] is not supplied, then as suggested above, the following form is evaluated at the conclusion of the evaluation of the ~c[state-global-let*] form, whether or not an error has occurred: ~c[(f-put-global 'vari 'old-vali state)]. However, if ~c[set-vari] is supplied, then instead the form evaluated will be ~c[(set-vari 'old-vali state)]. This capability is particularly useful if ~c[vari] is untouchable (~pl[push-untouchable]), since the above call of ~ilc[f-put-global] is illegal. Note that the scope of the bindings of a ~c[state-global-let*] form is the body of that form. This may seem obvious, but to drive the point home, let's consider the following example (~pl[set-print-base] and ~pl[set-print-radix]). ~bv[] ACL2 !>(state-global-let* ((print-base 16 set-print-base) (print-radix t set-print-radix)) (mv nil 10 state)) 10 ACL2 !> ~ev[] Why wasn't the result printed as ~c[#xA]? The reason is that the result was printed after evaluation of the entire form had completed. If you want to see ~c[#xA], do the printing in the scope of the bindings, for example as follows. ~bv[] ACL2 !>(state-global-let* ((print-base 16 set-print-base) (print-radix t set-print-radix)) (pprogn (fms \"~~x0~~%\" (list (cons #\0 10)) *standard-co* state nil) (mv nil 10 state))) #xA 10 ACL2 !> ~ev[]~/" (declare (xargs :guard (and (state-global-let*-bindings-p bindings) (no-duplicatesp-equal (strip-cars bindings))))) `(warn-about-parallelism-hazard ; We call warn-about-parallelism-hazard, because use of this macro in a ; parallel environment is potentially dangerous. It might work, because maybe ; no variables are rebound that are changed inside the waterfall, but we, the ; developers, want to know about any such rebinding. '(state-global-let* ,bindings ,body) (let ((state-global-let*-cleanup-lst (list ,@(state-global-let*-get-globals bindings)))) ,@(and (null bindings) '((declare (ignore state-global-let*-cleanup-lst)))) (acl2-unwind-protect "state-global-let*" (pprogn ,@(state-global-let*-put-globals bindings) (check-vars-not-free (state-global-let*-cleanup-lst) ,body)) (pprogn ,@(state-global-let*-cleanup bindings 0) state) (pprogn ,@(state-global-let*-cleanup bindings 0) state))))) #-acl2-loop-only (defmacro state-free-global-let* (bindings body) ; This raw Lisp macro is a variant of state-global-let* that should be used ; only when state is *not* lexically available, or at least not a formal ; parameter of the enclosing function or not something we care about tracking ; (because we are in raw Lisp). It is used to bind state globals that may have ; raw-Lisp side effects. If state were available this sort of binding could be ; inappropriate, since one could observe a change in state globals under the ; state-free-global-let* that was not justified by the logic. ; State-free-global-let* provides a nice alternative to state-global-let* when ; we want to avoid involving the acl2-unwind-protect mechanism, for example ; during parallel evaluation. ; Comment for #+acl2-par: When using state-free-global-let* inside functions ; that might execute in parallel (for example, functions that occur inside the ; waterfall), consider modifying macro mt-future to cause child threads to ; inherit these variables' values from their parent threads. See how we ; handled safe-mode and gc-on in macro mt-future for examples of how to cause ; such inheritance to occur. (cond ((null bindings) body) (t (let (bs syms) (dolist (binding bindings) (let ((sym (global-symbol (car binding)))) (push (list sym (cadr binding)) bs) (push sym syms))) `(let* ,(nreverse bs) (declare (special ,@(nreverse syms))) ,body))))) ; With state-global-let* defined, we may now define a few more primitives and ; finish some unfinished business. ; We start by introducing functions that support type declarations. We had to ; delay these because we use local in our proof, and local uses ; state-global-let*. Bootstrapping is tough. We could presumably do this ; earlier in the file and defer guard verification (which is why we need ; local), but since types are involved with guards, that seems dicey -- so we ; just wait till here. (defun integer-range-p (lower upper x) ; Notice the strict inequality for upper. This function was introduced in ; Version_2.7 in support of signed-byte-p and unsigned-byte-p, whose ; definitions were kept similar to those that had been in the ihs library for ; some time. (declare (xargs :guard (and (integerp lower) (integerp upper)))) (and (integerp x) (<= lower x) (< x upper))) (local (defthm natp-expt (implies (and (integerp base) (integerp n) (<= 0 n)) (integerp (expt base n))) :rule-classes :type-prescription)) ; For the definitions of signed-byte-p and unsigned-byte-p, we were tempted to ; put (integerp n) and (< 0 n) [or for unsigned-byte-p, (<= 0 n)] in the ; guards. However, instead we follow the approach already used for some time ; in community book books/ihs/logops-definitions.lisp, namely to include these ; as conjuncts in the bodies of the definitions, an approach that seems at ; least as reasonable. (defun signed-byte-p (bits x) ":Doc-Section ACL2::ACL2-built-ins recognizer for signed integers that fit in a specified bit width~/ ~c[(Signed-byte-p bits x)] is ~c[T] when ~c[bits] is a positive integer and ~c[x] is a signed integer whose 2's complement representation fits in a bit-width of ~c[bits], i.e., ~c[-2^(bits-1) <= x < 2^(bits-1)].~/ Note that a ~il[type-spec] of ~c[(signed-byte i)] for a variable ~c[x] in a function's ~ilc[declare] form translates to a ~il[guard] condition of ~c[(signed-byte-p i x)]. The ~il[guard] for ~c[signed-byte-p] is ~c[T]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (and (integerp bits) (< 0 bits) (integer-range-p (- (expt 2 (1- bits))) (expt 2 (1- bits)) x))) (defun unsigned-byte-p (bits x) ":Doc-Section ACL2::ACL2-built-ins recognizer for natural numbers that fit in a specified bit width~/ ~c[(Unsigned-byte-p bits x)] is ~c[T] when ~c[bits] is a positive integer and ~c[x] is a nonnegative integer that fits into a bit-width of ~c[bits], i.e., ~c[0 <= x < 2^bits].~/ Note that a ~il[type-spec] of ~c[(unsigned-byte i)] for a variable ~c[x] in a function's ~ilc[declare] form translates to a ~il[guard] condition of ~c[(unsigned-byte-p i x)]. The ~il[guard] for ~c[unsigned-byte-p] is ~c[T]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (and (integerp bits) (<= 0 bits) (integer-range-p 0 (expt 2 bits) x))) ; The following rules help built-in-clausep to succeed when guards are ; generated from type declarations. (defthm integer-range-p-forward (implies (and (integer-range-p lower (1+ upper-1) x) (integerp upper-1)) (and (integerp x) (<= lower x) (<= x upper-1))) :rule-classes :forward-chaining) (defthm signed-byte-p-forward-to-integerp (implies (signed-byte-p n x) (integerp x)) :rule-classes :forward-chaining) (defthm unsigned-byte-p-forward-to-nonnegative-integerp (implies (unsigned-byte-p n x) (and (integerp x) (<= 0 x))) :rule-classes :forward-chaining) ; The logic-only definition of zpf needs to come after expt and integer-range-p. (defmacro the-fixnum (n) (list 'the '(signed-byte 30) n)) #-acl2-loop-only (defun-one-output zpf (x) (declare (type (unsigned-byte 29) x)) (eql (the-fixnum x) 0)) #+acl2-loop-only (defun zpf (x) (declare (type (unsigned-byte 29) x)) ":Doc-Section ACL2::ACL2-built-ins testing a nonnegative fixnum against 0~/ ~c[Zpf] is exactly the same as ~ilc[zp], except that ~c[zpf] is intended for, and faster for, fixnum arguments. Its guard is specified with a type declaration, ~c[(type (unsigned-byte 29) x)]. (~l[declare] and ~pl[type-spec].) Also ~pl[zp]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (if (integerp x) (<= x 0) t)) ; We continue by proving the guards on substitute, all-vars1 and all-vars. (local (defthm character-listp-substitute-ac (implies (and (characterp new) (character-listp x) (character-listp acc)) (character-listp (substitute-ac new old x acc))))) (verify-guards substitute) (local (encapsulate () ; We wish to prove symbol-listp-all-vars1, below, so that we can verify the ; guards on all-vars1. But it is in a mutually recursive clique. Our strategy ; is simple: (1) define the flagged version of the clique, (2) prove that it is ; equal to the given pair of official functions, (3) prove that it has the ; desired property and (4) then obtain the desired property of the official ; function by instantiation of the theorem proved in step 3, using the theorem ; proved in step 2 to rewrite the flagged flagged calls in that instance to the ; official ones. ; Note: It would probably be better to make all-vars1/all-vars1-lst local, ; since it's really not of any interest outside the guard verification of ; all-vars1. However, since we are passing through this file more than once, ; that does not seem to be an option. (local (defun all-vars1/all-vars1-lst (flg lst ans) (if (eq flg 'all-vars1) (cond ((variablep lst) (add-to-set-eq lst ans)) ((fquotep lst) ans) (t (all-vars1/all-vars1-lst 'all-vars-lst1 (cdr lst) ans))) (cond ((endp lst) ans) (t (all-vars1/all-vars1-lst 'all-vars-lst1 (cdr lst) (all-vars1/all-vars1-lst 'all-vars1 (car lst) ans))))))) (local (defthm step-1-lemma (equal (all-vars1/all-vars1-lst flg lst ans) (if (equal flg 'all-vars1) (all-vars1 lst ans) (all-vars1-lst lst ans))))) (local (defthm step-2-lemma (implies (and (symbol-listp ans) (if (equal flg 'all-vars1) (pseudo-termp lst) (pseudo-term-listp lst))) (symbol-listp (all-vars1/all-vars1-lst flg lst ans))))) (defthm symbol-listp-all-vars1 (implies (and (symbol-listp ans) (pseudo-termp lst)) (symbol-listp (all-vars1 lst ans))) :hints (("Goal" :use (:instance step-2-lemma (flg 'all-vars1))))))) (verify-guards all-vars1) (verify-guards all-vars) (local (defthm symbol-listp-implies-true-listp (implies (symbol-listp x) (true-listp x)))) (verify-guards check-vars-not-free-test) ; Next, we verify the guards of getprops, which we delayed for the same ; reasons. (encapsulate () (defthm string<-l-asymmetric (implies (and (eqlable-listp x1) (eqlable-listp x2) (integerp i) (string<-l x1 x2 i)) (not (string<-l x2 x1 i))) :hints (("Goal" :in-theory (disable member)))) (defthm symbol-<-asymmetric (implies (and (symbolp sym1) (symbolp sym2) (symbol-< sym1 sym2)) (not (symbol-< sym2 sym1))) :hints (("Goal" :in-theory (set-difference-theories (enable string< symbol-<) '(string<-l))))) (defthm string<-l-transitive (implies (and (string<-l x y i) (string<-l y z j) (integerp i) (integerp j) (integerp k) (character-listp x) (character-listp y) (character-listp z)) (string<-l x z k)) :rule-classes ((:rewrite :match-free :all)) :hints (("Goal" :induct t :in-theory (disable member)))) (in-theory (disable string<-l)) (defthm symbol-<-transitive (implies (and (symbol-< x y) (symbol-< y z) (symbolp x) (symbolp y) (symbolp z)) (symbol-< x z)) :rule-classes ((:rewrite :match-free :all)) :hints (("Goal" :in-theory (enable symbol-< string<)))) (local (defthm equal-char-code-rewrite (implies (and (characterp x) (characterp y)) (implies (equal (char-code x) (char-code y)) (equal (equal x y) t))) :hints (("Goal" :use equal-char-code)))) (defthm string<-l-trichotomy (implies (and (not (string<-l x y i)) (integerp i) (integerp j) (character-listp x) (character-listp y)) (iff (string<-l y x j) (not (equal x y)))) :rule-classes ((:rewrite :match-free :all)) :hints (("Goal" :in-theory (set-difference-theories (enable string<-l) '(member)) :induct t))) (local (defthm equal-coerce (implies (and (stringp x) (stringp y)) (equal (equal (coerce x 'list) (coerce y 'list)) (equal x y))) :hints (("Goal" :use ((:instance coerce-inverse-2 (x x)) (:instance coerce-inverse-2 (x y))) :in-theory (disable coerce-inverse-2))))) (local (defthm symbol-equality-rewrite (implies (and (symbolp s1) (symbolp s2) (equal (symbol-name s1) (symbol-name s2)) (equal (symbol-package-name s1) (symbol-package-name s2))) (equal (equal s1 s2) t)) :hints (("Goal" :use symbol-equality)))) (defthm symbol-<-trichotomy (implies (and (symbolp x) (symbolp y) (not (symbol-< x y))) (iff (symbol-< y x) (not (equal x y)))) :hints (("Goal" :in-theory (enable symbol-< string<)))) (defthm ordered-symbol-alistp-delete-assoc-eq (implies (ordered-symbol-alistp l) (ordered-symbol-alistp (delete-assoc-eq key l)))) (defthm symbol-<-irreflexive (implies (symbolp x) (not (symbol-< x x))) :hints (("Goal" :use ((:instance symbol-<-asymmetric (sym1 x) (sym2 x))) :in-theory (disable symbol-<-asymmetric)))) (defthm ordered-symbol-alistp-add-pair (implies (and (ordered-symbol-alistp gs) (symbolp w5)) (ordered-symbol-alistp (add-pair w5 w6 gs)))) (defthm ordered-symbol-alistp-getprops (implies (and (plist-worldp w) (symbolp world-name) (symbolp key)) (ordered-symbol-alistp (getprops key world-name w))) :hints (("Goal" :in-theory (enable symbol-<)))) (local (defthm ordered-symbol-alistp-implies-symbol-alistp (implies (ordered-symbol-alistp x) (symbol-alistp x)))) (local (defthm symbol-alistp-implies-alistp (implies (symbol-alistp x) (alistp x)))) (verify-guards getprops) ) ; Functions such as logand require significant arithmetic to prove. Therefore ; part of the proofs for their "warming" will be deferred. ; Bishop Brock has contributed the lemma justify-integer-floor-recursion that ; follows. Although he has proved this lemma as part of a larger proof effort, ; we are not yet in a hurry to isolate its proof just now. (local (skip-proofs (defthm justify-integer-floor-recursion ; To use this, be sure to disable acl2-count and floor. If you leave ; acl2-count enabled, then prove a version of this appropriate to that setting. (implies (and (integerp i) (integerp j) (not (equal i 0)) (not (equal i -1)) (> j 1)) (< (acl2-count (floor i j)) (acl2-count i))) :rule-classes :linear))) #+acl2-loop-only (defmacro logand (&rest args) ":Doc-Section ACL2::ACL2-built-ins bitwise logical `and' of zero or more integers~/ When integers are viewed in their two's complement representation, ~c[logand] returns their bitwise logical `and'. In ACL2 ~c[logand] is a macro that expands into calls of the binary function ~c[binary-logand], except that ~c[(logand)] expands to ~c[-1] and ~c[(logand x)] expands to ~c[x].~/ The ~il[guard] for ~c[binary-logand] requires its arguments to be integers. ~c[Logand] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (cond ((null args) -1) ((null (cdr args)) (car args)) (t (xxxjoin 'binary-logand args)))) #+acl2-loop-only (defmacro logeqv (&rest args) ":Doc-Section ACL2::ACL2-built-ins bitwise logical equivalence of zero or more integers~/ When integers are viewed in their two's complement representation, ~c[logeqv] returns their bitwise logical equivalence. In ACL2 ~c[logeqv] is a macro that expands into calls of the binary function ~c[binary-logeqv], except that ~c[(logeqv)] expands to ~c[-1] and ~c[(logeqv x)] expands to ~c[x].~/ The ~il[guard] for ~c[binary-logeqv] requires its arguments to be integers. ~c[Logeqv] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (cond ((null args) -1) ((null (cdr args)) (car args)) (t (xxxjoin 'binary-logeqv args)))) #+acl2-loop-only (defmacro logior (&rest args) ":Doc-Section ACL2::ACL2-built-ins bitwise logical inclusive or of zero or more integers~/ When integers are viewed in their two's complement representation, ~c[logior] returns their bitwise logical inclusive or. In ACL2 ~c[logior] is a macro that expands into calls of the binary function ~c[binary-logior], except that ~c[(logior)] expands to ~c[0] and ~c[(logior x)] expands to ~c[x].~/ The ~il[guard] for ~c[binary-logior] requires its arguments to be integers. ~c[Logior] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (cond ((null args) 0) ((null (cdr args)) (car args)) (t (xxxjoin 'binary-logior args)))) #+acl2-loop-only (defmacro logxor (&rest args) ":Doc-Section ACL2::ACL2-built-ins bitwise logical exclusive or of zero or more integers~/ When integers are viewed in their two's complement representation, ~c[logxor] returns their bitwise logical exclusive or. In ACL2 ~c[logxor] is a macro that expands into calls of the binary function ~c[binary-logxor], except that ~c[(logxor)] expands to ~c[0] and ~c[(logxor x)] expands to ~c[x].~/ The ~il[guard] for ~c[binary-logxor] requires its arguments to be integers. ~c[Logxor] is defined in Common Lisp. See any Common Lisp documentation for more information.~/" (cond ((null args) 0) ((null (cdr args)) (car args)) (t (xxxjoin 'binary-logxor args)))) #+acl2-loop-only (defun integer-length (i) ; Bishop Brock contributed the following definition. We believe it to be ; equivalent to one on p. 361 of CLtL2: ; (log2 (if (< x 0) (- x) (1+ x))). ":Doc-Section ACL2::ACL2-built-ins number of bits in two's complement integer representation~/ For non-negative integers, ~c[(integer-length i)] is the minimum number of bits needed to represent the integer. Any integer can be represented as a signed two's complement field with a minimum of ~c[(+ (integer-length i) 1)] bits.~/ The ~il[guard] for ~c[integer-length] requires its argument to be an integer. ~c[Integer-length] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (integerp i) :hints (("Goal" :in-theory (disable acl2-count floor))))) (if (zip i) 0 (if (= i -1) 0 (+ 1 (integer-length (floor i 2)))))) (defun binary-logand (i j) (declare (xargs :guard (and (integerp i) (integerp j)) :hints (("Goal" :in-theory (disable acl2-count floor))))) (cond ((zip i) 0) ((zip j) 0) ((eql i -1) j) ((eql j -1) i) (t (let ((x (* 2 (logand (floor i 2) (floor j 2))))) (+ x (cond ((evenp i) 0) ((evenp j) 0) (t 1))))))) #+acl2-loop-only (defun lognand (i j) ":Doc-Section ACL2::ACL2-built-ins bitwise logical `nand' of two integers~/ When integers are viewed in their two's complement representation, ~c[lognand] returns their bitwise logical `nand'.~/ The ~il[guard] for ~c[lognand] requires its arguments to be integers. ~c[Lognand] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp j)))) (lognot (logand i j))) (defun binary-logior (i j) (declare (xargs :guard (and (integerp i) (integerp j)))) (lognot (logand (lognot i) (lognot j)))) #+acl2-loop-only (defun logorc1 (i j) ":Doc-Section ACL2::ACL2-built-ins bitwise logical inclusive or of two ints, complementing the first~/ When integers are viewed in their two's complement representation, ~c[logorc1] returns the bitwise logical inclusive or of the second with the bitwise logical `not' of the first.~/ The ~il[guard] for ~c[logorc1] requires its arguments to be integers. ~c[Logorc1] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp j)))) (logior (lognot i) j)) #+acl2-loop-only (defun logorc2 (i j) ":Doc-Section ACL2::ACL2-built-ins bitwise logical inclusive or of two ints, complementing the second~/ When integers are viewed in their two's complement representation, ~c[logorc2] returns the bitwise logical inclusive or of the first with the bitwise logical `not' of the second.~/ The ~il[guard] for ~c[logorc2] requires its arguments to be integers. ~c[Logorc2] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp j)))) (logior i (lognot j))) #+acl2-loop-only (defun logandc1 (i j) ":Doc-Section ACL2::ACL2-built-ins bitwise logical `and' of two ints, complementing the first~/ When integers are viewed in their two's complement representation, ~c[logandc1] returns the bitwise logical `and' of the second with the bitwise logical `not' of the first.~/ The ~il[guard] for ~c[logandc1] requires its arguments to be integers. ~c[Logandc1] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp j)))) (logand (lognot i) j)) #+acl2-loop-only (defun logandc2 (i j) ":Doc-Section ACL2::ACL2-built-ins bitwise logical `and' of two ints, complementing the second~/ When integers are viewed in their two's complement representation, ~c[logandc2] returns the bitwise logical `and' of the first with the bitwise logical `not' of the second.~/ The ~il[guard] for ~c[logandc2] requires its arguments to be integers. ~c[Logandc2] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp j)))) (logand i (lognot j))) (defun binary-logeqv (i j) (declare (xargs :guard (and (integerp i) (integerp j)))) (logand (logorc1 i j) (logorc1 j i))) (defun binary-logxor (i j) (declare (xargs :guard (and (integerp i) (integerp j)))) (lognot (logeqv i j))) #+acl2-loop-only (defun lognor (i j) ":Doc-Section ACL2::ACL2-built-ins bitwise logical `nor' of two integers~/ When integers are viewed in their two's complement representation, ~c[lognor] returns the bitwise logical `nor' of the first with the second.~/ The ~il[guard] for ~c[lognor] requires its arguments to be integers. ~c[Lognor] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp i) (integerp j)))) (lognot (logior i j))) #+acl2-loop-only (defun logtest (x y) ; p. 360 of CLtL2 ":Doc-Section ACL2::ACL2-built-ins test if two integers share a `1' bit~/ When integers ~c[x] and ~c[y] are viewed in their two's complement representation, ~c[(logtest x y)] is true if and only if there is some position for which both ~c[x] and ~c[y] have a `1' bit in that position.~/ The ~il[guard] for ~c[logtest] requires its arguments to be integers. ~c[Logtest] is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp x) (integerp y)))) (not (zerop (logand x y)))) ; Warning: Keep the following defconst forms in sync with *boole-array*. (defconst *BOOLE-1* 0) (defconst *BOOLE-2* 1) (defconst *BOOLE-AND* 2) (defconst *BOOLE-ANDC1* 3) (defconst *BOOLE-ANDC2* 4) (defconst *BOOLE-C1* 5) (defconst *BOOLE-C2* 6) (defconst *BOOLE-CLR* 7) (defconst *BOOLE-EQV* 8) (defconst *BOOLE-IOR* 9) (defconst *BOOLE-NAND* 10) (defconst *BOOLE-NOR* 11) (defconst *BOOLE-ORC1* 12) (defconst *BOOLE-ORC2* 13) (defconst *BOOLE-SET* 14) (defconst *BOOLE-XOR* 15) (defun boole$ (op i1 i2) ":Doc-Section ACL2::ACL2-built-ins perform a bit-wise logical operation on 2 two's complement integers~/ When integers ~c[x] and ~c[y] are viewed in their two's complement representation, ~c[(boole$ op x y)] returns the result of applying the bit-wise logical operation specified by ~c[op]. The following table is adapted from documentation for analogous Common Lisp function ~c[boole] in the Common Lisp HyperSpec (~url[http://www.lisp.org/HyperSpec/Body/fun_boole.html#boole]). Note that the values of ~c[op] for ~c[boole$] are ACL2 constants, rather than corresponding values of ~c[op] for the Common Lisp function ~c[boole]. ~bv[] op result ----------- --------- *boole-1* x *boole-2* y *boole-andc1* and complement of x with y *boole-andc2* and x with complement of y *boole-and* and *boole-c1* complement of x *boole-c2* complement of y *boole-clr* the constant 0 (all zero bits) *boole-eqv* equivalence (exclusive nor) *boole-ior* inclusive or *boole-nand* not-and *boole-nor* not-or *boole-orc1* or complement of x with y *boole-orc2* or x with complement of y *boole-set* the constant -1 (all one bits) *boole-xor* exclusive or ~ev[]~/ The guard of ~c[boole$] specifies that ~c[op] is the value of one of the constants above and that ~c[x] and ~c[y] are integers. See any Common Lisp documentation for analogous information about Common Lisp function ~c[boole]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (type (integer 0 15) op) (type integer i1 i2)) #-acl2-loop-only (boole (aref *boole-array* op) i1 i2) #+acl2-loop-only (cond ((eql op *BOOLE-1*) i1) ((eql op *BOOLE-2*) i2) ((eql op *BOOLE-AND*) (logand i1 i2)) ((eql op *BOOLE-ANDC1*) (logandc1 i1 i2)) ((eql op *BOOLE-ANDC2*) (logandc2 i1 i2)) ((eql op *BOOLE-C1*) (lognot i1)) ((eql op *BOOLE-C2*) (lognot i2)) ((eql op *BOOLE-CLR*) 0) ((eql op *BOOLE-EQV*) (logeqv i1 i2)) ((eql op *BOOLE-IOR*) (logior i1 i2)) ((eql op *BOOLE-NAND*) (lognand i1 i2)) ((eql op *BOOLE-NOR*) (lognor i1 i2)) ((eql op *BOOLE-ORC1*) (logorc1 i1 i2)) ((eql op *BOOLE-ORC2*) (logorc2 i1 i2)) ((eql op *BOOLE-SET*) 1) ((eql op *BOOLE-XOR*) (logxor i1 i2)) (t 0) ; added so that we get an integer type for integer i1 and i2 )) ; PRINTING and READING (deflabel io :doc ":Doc-Section IO input/output facilities in ACL2~/ ~bv[] Example: (mv-let (channel state) (open-input-channel \"foo.lisp\" :object state) (mv-let (eofp obj state) (read-object channel state) (. . (let ((state (close-input-channel channel state))) (mv final-ans state))..))) ~ev[] Also ~pl[file-reading-example]. For advanced ways to control printing, ~pl[print-control]. For a discussion of formatted printing, ~pl[fmt]. To control ACL2 abbreviation (``evisceration'') of objects before printing them, ~pl[set-evisc-tuple], ~pl[without-evisc], and ~pl[set-iprint]. To redirect output to a file, ~pl[output-to-file].~/ ACL2 supports input and output facilities equivalent to a subset of those found in Common Lisp. ACL2 does not support random access to files or bidirectional streams. In Common Lisp, input and output are to or from objects of type ~c[stream]. In ACL2, input and output are to or from objects called ``channels,'' which are actually symbols. Although a channel is a symbol, one may think of it intuitively as corresponding to a Common Lisp stream. Channels are in one of two ACL2 packages, ~c[\"ACL2-INPUT-CHANNEL\"] and ~c[\"ACL2-OUTPUT-CHANNEL\"]. When one ``opens'' a file one gets back a channel whose ~ilc[symbol-name] is the file name passed to ``open,'' postfixed with ~c[-n], where ~c[n] is a counter that is incremented every time an open or close occurs. There are three channels which are open from the beginning and which cannot be closed: ~bv[] acl2-input-channel::standard-character-input-0 acl2-input-channel::standard-object-input-0 acl2-input-channel::standard-character-output-0 ~ev[] All three of these are really Common Lisp's ~c[*standard-input*] or ~c[*standard-output*], appropriately. For convenience, three global variables are bound to these rather tedious channel names: ~bv[] *standard-ci* *standard-oi* *standard-co* ~ev[] Common Lisp permits one to open a stream for several different kinds of ~c[io], e.g. character or byte. ACL2 permits an additional type called ``object''. In ACL2 an ``io-type'' is a keyword, either ~c[:character], ~c[:byte], or ~c[:object]. When one opens a file, one specifies a type, which determines the kind of io operations that can be done on the channel returned. The types ~c[:character] and ~c[:byte] are familiar. Type ~c[:object] is an abstraction not found in Common Lisp. An ~c[:object] file is a file of Lisp objects. One uses ~c[read-object] to read from ~c[:object] files and ~c[print-object$] (or ~c[print-object$-ser]) to print to ~c[:object] files. (The reading and printing are really done with the Common Lisp ~c[read] and ~c[print] functions. For those familiar with ~c[read], we note that the ~c[recursive-p] argument is ~c[nil].) The function ~c[read-object-suppress] is logically the same as ~c[read-object] except that ~c[read-object-suppress] throws away the second returned value, i.e. the value that would normally be read, simply returning ~c[(mv eof state)]; under the hood, ~c[read-object-suppress] avoids errors, for example those caused by encountering symbols in packages unknown to ACL2. File-names are strings. ACL2 does not support the Common Lisp type ~ilc[pathname]. However, for the ~c[file-name] argument of the output-related functions listed below, ACL2 supports a special value, ~c[:STRING]. For this value, the channel connects (by way of a Common Lisp output string stream) to a string rather than to a file: as characters are written to the channel they can be retrieved by using ~c[get-output-stream-string$]. Here are the names, formals and output descriptions of the ACL2 io functions. ~bv[] Input Functions: (open-input-channel (file-name io-type state) (mv channel state)) (open-input-channel-p (channel io-type state) boolean) (close-input-channel (channel state) state) (read-char$ (channel state) (mv char/nil state)) ; nil for EOF (peek-char$ (channel state) boolean) (read-byte$ (channel state) (mv byte/nil state)) ; nil for EOF (read-object (channel state) (mv eof-read-flg obj-read state)) (read-object-suppress (channel state) (mv eof-read-flg state)) Output Functions: (open-output-channel (file-name io-type state) (mv channel state)) (open-output-channel! (file-name io-type state) (mv channel state)) (open-output-channel-p (channel io-type state) boolean) (close-output-channel (channel state) state) (princ$ (obj channel state) state) (write-byte$ (byte channel state) state) (print-object$ (obj channel state) state) (print-object$-ser (obj serialize-character channel state) state) (fms (string alist channel state evisc-tuple) state) (fms! (string alist channel state evisc-tuple) state) (fmt (string alist channel state evisc-tuple) (mv col state)) (fmt! (string alist channel state evisc-tuple) (mv col state)) (fmt1 (string alist col channel state evisc-tuple) (mv col state)) (fmt1! (string alist col channel state evisc-tuple) (mv col state)) (cw (string arg0 arg1 ... argn) nil) (get-output-stream-string$ (channel state &optional (close-p 't) (ctx ''get-output-stream-string$)) (mv erp string state)) ~ev[] The ``formatting'' functions are particularly useful; ~pl[fmt] and ~pl[cw]. In particular, ~ilc[cw] prints to a ``comment window'' and does not involve the ACL2 ~ilc[state], so many may find it easier to use than ~ilc[fmt] and its variants. The functions ~ilc[fms!], ~ilc[fmt!], and ~ilc[fmt1!] are the same as their respective functions without the ``~c[!],'' except that the ``~c[!]'' functions are guaranteed to print forms that can be read back in (at a slight readability cost). When one enters ACL2 with ~c[(lp)], input and output are taken from ~ilc[*standard-oi*] to ~ilc[*standard-co*]. Because these are synonyms for ~c[*standard-input*] and ~c[*standard-output*], one can drive ACL2 io off of arbitrary Common Lisp streams, bound to ~c[*standard-input*] and ~c[*standard-output*] before entry to ACL2. The macro ~c[get-output-stream-string$] returns the string accumulated into the given channel. By default, a call of this macro closes the supplied output channel. However, a third argument is optional (default ~c[t]), and if it evaluates to ~c[nil] then the channel remains open. The fourth argument is an optional context, which generally evaluates to a symbol, for error reporting. The following example illustrates. ~bv[] ACL2 !> (mv-let (channel state) (open-output-channel :string :object state) (pprogn (print-object$-ser 17 nil channel state) (print-object$-ser '(a b (c d)) nil channel state) (er-let* ((str1 (get-output-stream-string$ channel state nil))) ; keep the channel open (pprogn (print-object$-ser 23 nil channel state) (print-object$-ser '((e f)) nil channel state) (er-let* ; close the channel ((str2 (get-output-stream-string$ channel state))) (value (cons str1 str2))))))) (\" 17 (A B (C D))\" . \" 23 ((E F))\") ACL2 !> ~ev[] Also ~pl[printing-to-strings] for a discussion of formatted printing functions such as ~c[fmt-to-string] that do not take a channel or ~ilc[state] argument and return a string. By default, symbols are printed in upper case when vertical bars are not required, as specified by Common Lisp. ~l[set-print-case] for how to get ACL2 to print symbols in lower case. By default, numbers are printed in radix 10 (base 10). ~l[set-print-base] for how to get ACL2 to print numbers in radix 2, 8, or 16. To see the ~il[guard] of an IO function, or indeed any function, ~pl[args] or call the function ~c[guard]; but some built-in functions (including some IO functions) will print the result using the variable ~c[STATE-STATE]. While that is logically correct, if you want to execute the guard then you should replace that variable by ~c[STATE] and also replace each built-in function symbol of the form ~c[xxx-p1] by corresponding function symbol ~c[xxx-p]. Consider the following example. ~bv[] ACL2 !>:args princ$ Function PRINC$ Formals: (X CHANNEL STATE-STATE) Signature: (PRINC$ * * STATE) => STATE Guard: (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P1 STATE-STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P1 CHANNEL :CHARACTER STATE-STATE)) Guards Verified: T Defun-Mode: :logic Type: (CONSP (PRINC$ X CHANNEL STATE-STATE)) Documentation available via :DOC PRINC$ ACL2 !>(untranslate (guard 'princ$ nil (w state)) t (w state)) (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P1 STATE-STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P1 CHANNEL :CHARACTER STATE-STATE)) ACL2 !> ~ev[] If you want to execute the guard for ~ilc[princ$], then according to the suggestion above, you should consider the guard for ~c[(princ$ x channel state)] to be as follows. ~bv[] (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P CHANNEL :CHARACTER STATE)) ~ev[] For example, we can check the guard for a given value and channel as follows. ~bv[] ACL2 !>(let ((x 3) (channel *standard-co*)) (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P CHANNEL :CHARACTER STATE))) T ACL2 !> ~ev[] Comment for advanced users: Function ~ilc[open-output-channel!] is identical as a function to ~c[open-output-channel], except that the former may be called even during ~ilc[make-event] expansion and ~ilc[clause-processor] ~il[hints], but requires that there is an active trust tag (~pl[defttag]). Finally, we note that the community book ~c[books/misc/file-io.lisp] contains useful file io functions whose definitions illustrate some of the features described above.~/") (defdoc output-to-file ":Doc-Section IO redirecting output to a file~/ For a general discussion of ACL2 input/output and of the ACL2 read-eval-print loop, ~pl[io] and ~pl[ld] (respectively). Here we use an example to illustrate how to use some of the options provided by ~c[ld] to redirect ACL2 output to a file, other than the printing of the prompt (which continues to go to the terminal). There are two ~c[ld] specials that control output from the ~c[ld] command: ~ilc[proofs-co] for proof output and ~ilc[standard-co] for other output. The following example shows how to use these to redirect output to a file ~c[\"tmp.out\"]. The following command opens a character output channel to to the file ~c[\"tmp.out\"] and redirects proof output to that channel, i.e., to file ~c[\"tmp.out\"]. ~bv[] (mv-let (chan state) (open-output-channel \"tmp.out\" :character state) (set-proofs-co chan state)) ~ev[] Next, we redirect standard output to that same channel. ~bv[] (set-standard-co (proofs-co state) state) ~ev[] Now we can load an input file, in this case file ~c[\"tmp.lisp\"], and output will be redirected to file ~c[\"tmp.out\"]. (The use of ~c[:ld-pre-eval-print t] is optional; ~pl[ld].) ~bv[] (ld \"tmp.lisp\" :ld-pre-eval-print t) ~ev[] Having completed our load operation, we restore both proof output and standard output to the terminal, as follows. ~bv[] (set-standard-co *standard-co* state) (close-output-channel (proofs-co state) state) (set-proofs-co *standard-co* state) ~ev[] The following variant of the above example shows how to redirect output as above except without changing the global settings of the two ~ilc[ld] specials, ~ilc[proofs-co] and ~ilc[standard-co]. This approach uses a notion of ``global variables'' stored in the ACL2 ~il[state]; ~pl[assign] and ~pl[@]. ~bv[] (mv-let (chan state) (open-output-channel \"tmp.out\" :character state) (assign tmp-channel chan)) (ld \"tmp.lisp\" :ld-pre-eval-print t :proofs-co (@ tmp-channel) :standard-co (@ tmp-channel)) (close-output-channel (@ tmp-channel) state) ~ev[]~/~/") (defdoc *standard-co* ":Doc-Section IO the ACL2 analogue of CLTL's ~c[*standard-output*]~/ The value of the ACL2 constant ~c[*standard-co*] is an open character output channel that is synonymous to Common Lisp's ~c[*standard-output*].~/ ACL2 character output to ~c[*standard-co*] will go to the stream named by Common Lisp's ~c[*standard-output*]. That is, by changing the setting of ~c[*standard-output*] in raw Common Lisp you can change the actual destination of ACL2 output on the channel named by ~c[*standard-co*]. Observe that this happens without changing the logical value of ~c[*standard-co*] (which is some channel symbol). Changing the setting of ~c[*standard-output*] in raw Common Lisp essentially just changes the map that relates ACL2 to the physical world of terminals, files, etc. To see the value of this observation, consider the following. Suppose you write an ACL2 function which does character output to the constant channel ~c[*standard-co*]. During testing you see that the output actually goes to your terminal. Can you use the function to output to a file? Yes, if you are willing to do a little work in raw Common Lisp: open a stream to the file in question, set ~c[*standard-output*] to that stream, call your ACL2 function, and then close the stream and restore ~c[*standard-output*] to its nominal value. Similar observations can be made about the two ACL2 input channels, ~ilc[*standard-oi*] and ~ilc[*standard-ci*], which are analogues of ~c[*standard-input*]. Another reason you might have for wanting to change the actual streams associated with ~ilc[*standard-oi*] and ~c[*standard-co*] is to drive the ACL2 top-level loop, ~ilc[ld], on alternative input and output streams. This end can be accomplished easily within ACL2 by either calling ~ilc[ld] on the desired channels or file names or by resetting the ACL2 ~ilc[state] global variables ~c[']~ilc[standard-oi] and ~c[']~ilc[standard-co] which are used by ~ilc[ld]. ~l[standard-oi] and ~pl[standard-co].") (defdoc *standard-oi* ":Doc-Section IO an ACL2 object-based analogue of CLTL's ~c[*standard-input*]~/ The value of the ACL2 constant ~c[*standard-oi*] is an open object input channel that is synonymous to Common Lisp's ~c[*standard-input*].~/ ACL2 object input from ~c[*standard-oi*] is actually obtained by reading from the stream named by Common Lisp's ~c[*standard-input*]. That is, by changing the setting of ~c[*standard-input*] in raw Common Lisp you can change the source from which ACL2 reads on the channel ~c[*standard-oi*]. ~l[*standard-co*].") (defdoc *standard-ci* ":Doc-Section IO an ACL2 character-based analogue of CLTL's ~c[*standard-input*]~/ The value of the ACL2 constant ~c[*standard-ci*] is an open character input channel that is synonymous to Common Lisp's ~c[*standard-input*].~/ ACL2 character input from ~c[*standard-ci*] is actually obtained by reading ~il[characters] from the stream named by Common Lisp's ~c[*standard-input*]. That is, by changing the setting of ~c[*standard-input*] in raw Common Lisp you can change the source from which ACL2 reads on the channel ~c[*standard-ci*]. ~l[*standard-co*].") (defdoc print-control ":Doc-Section IO advanced controls of ACL2 printing~/ ~l[IO] for a summary of printing in ACL2. Here we document some advanced ways to control what is printed by ACL2's primary printing routines. ~l[set-print-base], ~pl[set-print-radix], and ~pl[set-print-case] for discussions of the most common ways to control what is printed. Indeed, these are the only ways to control the behavior of ~ilc[princ$] and ~c[prin1$]. The rest of this topic is for advanced users of ACL2. We refer to Common Lisp behavior, as described in any good Common Lisp documentation. ~st[Print-control variables]. ~ilc[Set-print-base], ~ilc[set-print-radix], and ~ilc[set-print-case] assign to corresponding so-called ``~il[state] global variables'' ~c['print-base], ~c['print-radix], and ~c['print-case], which can be accessed using the expressions ~c[(@ print-base)], ~c[(@ print-radix)], and ~c[(@ print-case)], respectively; ~pl[assign]. Here is a table showing all print-control variables, their setters, and their defaults. ~bv[] print-base set-print-base 10 print-case set-print-case :upcase print-circle set-print-circle nil [but see remark on print-circle-files, below] print-escape set-print-escape t print-length set-print-length nil print-level set-print-level nil print-lines set-print-lines nil print-pretty set-print-pretty nil print-radix set-print-radix nil print-readably set-print-readably nil print-right-margin set-print-right-margin nil ~ev[] Each ACL2 print-control variable ~c[print-xxx] can correspond in function to Common Lisp variable ~c[*PRINT-XXX*]. Specifically, the evaluation of forms ~c[(set-print-base t)], ~c[(set-print-radix t)], and ~c[(set-print-case t)] affects ACL2 printing functions in much the same way that setting to ~c[t] Common Lisp variables ~c[*PRINT-BASE*], ~c[*PRINT-RADIX*], and ~c[*PRINT-CASE*], respectively, affects Common Lisp printing. The same is true for ~c[print-escape], except that this does not affect ~ilc[princ$] or ~c[prin1$], which correspond to Common Lisp functions ~c[princ] and ~c[prin1]: ~c[princ] treats ~c[*PRINT-ESCAPE*] as ~c[nil] while ~c[prin1] treats ~c[*PRINT-ESCAPE*] as ~c[t]. Moreover, all print-control variables not mentioned in this paragraph are set to their defaults in ~ilc[princ$] and ~c[prin1$], as indicated by ACL2 constant ~c[*print-control-defaults*], except that ~c[print-readably] is set to ~c[nil] in ~c[princ$]. ~ilc[Fmt] and its related functions are sensitive to state globals ~c['print-base], ~c['print-radix], ~c['print-case], ~c['print-escape], and ~c['print-readably], in analogy with Common Lisp functions that don't fix ~c[*PRINT-ESCAPE*] or ~c[*PRINT-READABLY*]. But the ~ilc[fmt] functions do not respect settings of other print-control variables; for example, they act as though ~c['print-circle] is ~c[nil]. Since ACL2 output is produced using the same underlying print routines as the ~ilc[fmt] functions, it also is insensitive to all print-control variables except for the five above. To control the print-level and print-length used for producing ACL2 output, ~pl[set-evisc-tuple]. ~il[Print-object$] is sensitive to all of the print-control variables. Remark on ~c[print-circle-files]: ACL2 typically binds ~c['print-circle] to ~c[t] before writing ~il[certificate] files, or auxiliary files that are compiled when ~ilc[make-event] forms are present in a book, or files in support of ~c[:]~ilc[comp] commands. This binding allows for structure sharing that can keep these files from growing large. However, this behavior is defeated in GCL (Gnu Common Lisp), because of the small number of indices ~c[n] available by default (1024) for the ~c[#n=] reader macro. For the files described above, what actually happens is that ~c['print-circle] is bound to the value of ~c['print-circle-files], which by default is ~c[t] unless the underlying Lisp is GCL, in which case it is set to ~c[nil]. ~l[assign] for how to set ~il[state] globals such as ~c['print-circle-files]. For example, if you build GCL with a larger number of ~c[#n=] indices available, you may wish to restore the ~c[print-circle] behavior for ~il[certificate] files by following these instructions from Camm Maguire: ~bq[] This can trivially be revised to any larger constant by editing the following line of read.d and recompiling: ~c[#ifndef SHARP_EQ_CONTEXT_SIZE]~nl[] ~c[#define SHARP_EQ_CONTEXT_SIZE 500]~nl[] #endif~eq[] End of Remark. Evaluate ~c[(reset-print-control)] to restore all print-control variables to their original settings, as stored in constant ~c[*print-control-defaults*]. (Remark for those using ACL2 built on Gnu Common Lisp (GCL) versions that are non-ANSI, which as of October 2008 is all GCL versions recommended for ACL2: Note that Common Lisp variables ~c[*PRINT-LINES*], ~c[*PRINT-MISER-WIDTH*], ~c[*PRINT-READABLY*], ~c[*PRINT-PPRINT-DISPATCH*], and ~c[*PRINT-RIGHT-MARGIN*] do not have any effect for such GCL versions.)~/~/") (defdoc character-encoding ; Without the setting of custom:*default-file-encoding* for clisp in ; acl2.lisp, the build breaks with the following string (note the accented "i" ; in Martin, below): ; Francisco J. Martn Mateos ; With that setting, we do not need an explicit :external-format argument for ; the call of with-open-file in acl2-check.lisp that opens a stream for ; "acl2-characters". ; Because of the comment above, save an Emacs buffer connected to this file ; after setting the necessary buffer-local variable as follows. ; (setq save-buffer-coding-system 'iso-8859-1) ":Doc-Section IO how bytes are parsed into characters~/ When the Common Lisp reader comes across bytes in a file or at the terminal, they are parsed into characters. The simplest case is when each byte that is read is a standard character (~pl[standard-char-p]). It is actually quite common that each byte that is read corresponds to a single character. The parsing of bytes into characters is based on a ~em[character encoding], that is, a mapping that associates one or more bytes with each legal character. In order to help guarantee the portability of files (including ~il[books]), ACL2 installs a common character encoding for reading files, often known as iso-8859-1 or latin-1. For some host Lisps this character encoding is also used for reading from the terminal; but, sadly, this may not hold for all host Lisps, and may not even be possible for some of them. The use of the above encoding could in principle cause problems if one's editor produces files using an encoding other than iso-8859-1, at least if one uses non-standard characters. In particular, the default Emacs buffer encoding may be utf-8. If your file has non-standard characters, then in Emacs you can evaluate the form ~bv[] (setq save-buffer-coding-system 'iso-8859-1) ~ev[] before saving the buffer into a file. This will happen automatically for users who load distributed file ~c[emacs/emacs-acl2.el] into their Emacs sessions. For an example of character encodings in action, see the community book ~c[books/misc/character-encoding-test.lisp].~/~/") (defun set-forms-from-bindings (bindings) (declare (xargs :guard (and (symbol-alistp bindings) (true-list-listp bindings)))) (cond ((endp bindings) nil) (t (cons `(,(intern$ (concatenate 'string "SET-" (symbol-name (caar bindings))) "ACL2") ,(cadar bindings) state) (set-forms-from-bindings (cdr bindings)))))) (defconst *print-control-defaults* `((print-base ',(cdr (assoc-eq 'print-base *initial-global-table*)) set-print-base) (print-case ',(cdr (assoc-eq 'print-case *initial-global-table*)) set-print-case) (print-circle ',(cdr (assoc-eq 'print-circle *initial-global-table*)) set-print-circle) (print-escape ',(cdr (assoc-eq 'print-escape *initial-global-table*)) set-print-escape) (print-length ',(cdr (assoc-eq 'print-length *initial-global-table*)) set-print-length) (print-level ',(cdr (assoc-eq 'print-level *initial-global-table*)) set-print-level) (print-lines ',(cdr (assoc-eq 'print-lines *initial-global-table*)) set-print-lines) (print-pretty ',(cdr (assoc-eq 'print-pretty *initial-global-table*)) set-print-pretty) (print-radix ',(cdr (assoc-eq 'print-radix *initial-global-table*)) set-print-radix) (print-readably ',(cdr (assoc-eq 'print-readably *initial-global-table*)) set-print-readably) (print-right-margin ',(cdr (assoc-eq 'print-right-margin *initial-global-table*)) set-print-right-margin))) (defun alist-difference-eq (alist1 alist2) ; We return the elements of alist1 whose keys don't exist in the domain of ; alist2. (declare (xargs :guard (and (alistp alist1) (alistp alist2) (or (symbol-alistp alist1) (symbol-alistp alist2))))) (if (endp alist1) nil (if (assoc-eq (caar alist1) alist2) (alist-difference-eq (cdr alist1) alist2) (cons (car alist1) (alist-difference-eq (cdr alist1) alist2))))) (defmacro with-print-defaults (bindings form) `(state-global-let* ,(append bindings (cons '(serialize-character (f-get-global 'serialize-character-system state)) (alist-difference-eq *print-control-defaults* bindings))) ,form)) (defmacro reset-print-control () (cons 'pprogn (set-forms-from-bindings *print-control-defaults*))) (defun digit-to-char (n) ":Doc-Section ACL2::ACL2-built-ins map a digit to a character~/ ~bv[] Example: ACL2 !>(digit-to-char 8) #\\8 ~ev[] For an integer ~c[n] from 0 to 15, ~c[(digit-to-char n)] is the character corresponding to ~c[n] in hex notation, using uppercase letters for digits exceeding 9. If ~c[n] is in the appropriate range, that result is of course also the binary, octal, and decimal digit.~/ The ~il[guard] for ~c[digit-to-char] requires its argument to be an integer between 0 and 15, inclusive. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp n) (<= 0 n) (<= n 15)))) (case n (1 #\1) (2 #\2) (3 #\3) (4 #\4) (5 #\5) (6 #\6) (7 #\7) (8 #\8) (9 #\9) (10 #\A) (11 #\B) (12 #\C) (13 #\D) (14 #\E) (15 #\F) (otherwise #\0))) (defun print-base-p (print-base) ; Warning: Keep this in sync with check-print-base. (declare (xargs :guard t)) (member print-base '(2 8 10 16))) (defun explode-nonnegative-integer (n print-base ans) ":Doc-Section ACL2::ACL2-built-ins the list of ~il[characters] in the radix-r form of a number~/ ~bv[] Examples: ACL2 !>(explode-nonnegative-integer 925 10 nil) (#\9 #\2 #\5) ACL2 !>(explode-nonnegative-integer 325 16 nil) (#\3 #\9 #\D) ~ev[] For a non-negative integer ~c[n], ~c[(explode-nonnegative-integer n r nil)] is the list of ~il[characters] in the radix-~c[r] (base-~c[r]) representation of ~c[n].~/ The ~il[guard] for ~c[explode-nonnegative-integer] requires the first argument to be a nonnegative integer and second argument to be a valid radix for ACL2 (2, 8, 10, or 16). To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (integerp n) (>= n 0) (print-base-p print-base)) :mode :program)) (cond ((or (zp n) (not (print-base-p print-base))) (cond ((null ans) ; We could use endp instead of null above, but what's the point? Ans could be ; other than a true-listp for reasons other than that it's a non-nil atom, so ; why treat this case specially? '(#\0)) (t ans))) (t (explode-nonnegative-integer (floor n print-base) print-base (cons (digit-to-char (mod n print-base)) ans))))) (verify-termination-boot-strap explode-nonnegative-integer (declare (xargs :mode :logic :verify-guards nil :hints (("Goal" :in-theory (disable acl2-count floor)))))) (defthm true-listp-explode-nonnegative-integer ; This was made non-local in order to support the verify-termination-boot-strap ; for chars-for-tilde-@-clause-id-phrase/periods in file ; boot-strap-pass-2.lisp. (implies (true-listp ans) (true-listp (explode-nonnegative-integer n print-base ans))) :rule-classes :type-prescription) (local (skip-proofs (defthm mod-n-linear (implies (and (not (< n 0)) (integerp n) (print-base-p print-base)) (and (not (< (mod n print-base) 0)) (not (< (1- print-base) (mod n print-base))))) :rule-classes :linear))) (local (defthm integerp-mod (implies (and (integerp n) (< 0 n) (print-base-p print-base)) (integerp (mod n print-base))) :rule-classes :type-prescription)) (verify-guards explode-nonnegative-integer :hints (("Goal" :in-theory (disable mod)))) (defun explode-atom (x print-base) ; This function prints as though the print-radix is nil. For a version that ; uses the print-radix, see explode-atom+. (declare (xargs :guard (and (or (acl2-numberp x) (characterp x) (stringp x) (symbolp x)) (print-base-p print-base)) :mode :program)) (cond ((rationalp x) (cond ((integerp x) (cond ((< x 0) (cons #\- (explode-nonnegative-integer (- x) print-base nil))) (t (explode-nonnegative-integer x print-base nil)))) (t (append (explode-atom (numerator x) print-base) (cons #\/ (explode-nonnegative-integer (denominator x) print-base nil)))))) ((complex-rationalp x) (list* #\# #\C #\( (append (explode-atom (realpart x) print-base) (cons #\Space (append (explode-atom (imagpart x) print-base) '(#\))))))) ((characterp x) (list x)) ((stringp x) (coerce x 'list)) #+:non-standard-analysis ((acl2-numberp x) ; This case should never arise! (coerce "SOME IRRATIONAL OR COMPLEX IRRATIONAL NUMBER" 'list)) (t (coerce (symbol-name x) 'list)))) (verify-termination-boot-strap ; and guards explode-atom (declare (xargs :mode :logic))) (defun explode-atom+ (x print-base print-radix) (declare (xargs :guard (and (or (acl2-numberp x) (characterp x) (stringp x) (symbolp x)) (print-base-p print-base)) :mode :program)) (cond ((null print-radix) (explode-atom x print-base)) ((rationalp x) (cond ((eql print-base 10) (cond ((integerp x) (append (explode-atom x 10) '(#\.))) (t (append '(#\# #\1 #\0 #\r) (explode-atom x 10))))) (t `(#\# ,(case print-base (2 #\b) (8 #\o) (otherwise #\x)) ,@(explode-atom x print-base))))) ((complex-rationalp x) (list* #\# #\C #\( (append (explode-atom+ (realpart x) print-base print-radix) (cons #\Space (append (explode-atom+ (imagpart x) print-base print-radix) '(#\))))))) (t (explode-atom x print-base)))) (verify-termination-boot-strap ; and guards explode-atom+ (declare (xargs :mode :logic))) (defthm true-list-listp-forward-to-true-listp-assoc-equal ; This theorem (formerly two theorems ; true-list-listp-forward-to-true-listp-assoc-eq and ; true-list-listp-forward-to-true-listp-assoc-equal) may have been partly ; responsible for a 2.5% real-time regression slowdown (3.2% user time) after ; implementing equality variants, after Version_4.2. In particular, as a ; :type-prescription rule contributed to a significant slowdown in example4 of ; examples.lisp in community book ; books/workshops/2000/moore-manolios/partial-functions/tjvm.lisp. So we are ; disabling the type-prescription rule by default, later below, but adding the ; :forward-chaining rule (which is necessary for admitting event file-measure ; in community book books/unicode/file-measure.lisp). (implies (true-list-listp l) (true-listp (assoc-equal key l))) :rule-classes (:type-prescription (:forward-chaining :trigger-terms ((assoc-equal key l))))) (defthm true-listp-cadr-assoc-eq-for-open-channels-p ; As with rule consp-assoc-equal this rule is now potentially expensive because ; of equality variants. We disable it later, below. (implies (open-channels-p alist) (true-listp (cadr (assoc-eq key alist)))) :rule-classes ((:forward-chaining :trigger-terms ((cadr (assoc-eq key alist)))))) ; It is important to disable nth in order for the rule state-p1-forward to ; work. (local (in-theory (disable nth open-channels-p))) (defun open-input-channel-p1 (channel typ state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state) (member-eq typ *file-types*)))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from open-input-channel-p1 (and (get channel *open-input-channel-key*) (eq (get channel *open-input-channel-type-key*) typ))))) (let ((pair (assoc-eq channel (open-input-channels state-state)))) (and pair (eq (cadr (car (cdr pair))) typ)))) (defun open-output-channel-p1 (channel typ state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state) (member-eq typ *file-types*)))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from open-output-channel-p1 (and (get channel *open-output-channel-key*) (eq (get channel *open-output-channel-type-key*) typ))))) (let ((pair (assoc-eq channel (open-output-channels state-state)))) (and pair (eq (cadr (car (cdr pair))) typ)))) (defun open-input-channel-p (channel typ state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state) (member-eq typ *file-types*)))) (open-input-channel-p1 channel typ state-state)) (defun open-output-channel-p (channel typ state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state) (member-eq typ *file-types*)))) (open-output-channel-p1 channel typ state-state)) (defun open-output-channel-any-p1 (channel state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state)))) (or (open-output-channel-p1 channel :character state-state) (open-output-channel-p1 channel :byte state-state) (open-output-channel-p1 channel :object state-state))) (defun open-output-channel-any-p (channel state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state)))) (open-output-channel-any-p1 channel state-state)) (defun open-input-channel-any-p1 (channel state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state)))) (or (open-input-channel-p1 channel :character state-state) (open-input-channel-p1 channel :byte state-state) (open-input-channel-p1 channel :object state-state))) (defun open-input-channel-any-p (channel state-state) (declare (xargs :guard (and (symbolp channel) (state-p1 state-state)))) (open-input-channel-any-p1 channel state-state)) (defmacro print-case () '(f-get-global 'print-case state)) ; (defmacro acl2-print-case (&optional (st 'state)) ; (declare (ignore st)) ; `(er soft 'acl2-print-case ; "Macro ~x0 has been replaced by macro ~x1." ; 'acl2-print-case 'print-case)) (defmacro acl2-print-case (&optional (st 'state)) `(print-case ,st)) (defun set-print-case (case state) ":Doc-Section IO control whether symbols are printed in upper case or in lower case~/ By default, symbols are printed in upper case when vertical bars are not required, as specified by Common Lisp. As with Common Lisp, ACL2 supports printing in a \"downcase\" mode, where symbols are printed in lower case. Many printing functions (some details below) print characters in lower case for a symbol when the ACL2 ~il[state] global variable ~c[print-case] has value ~c[:downcase] and vertical bars are not necessary for printing that symbol. (Thus, this state global functions in complete analogy to the Common Lisp global ~c[*print-case*].) The value ~c[print-case] is returned by ~c[(print-case)], and may be set using the function ~c[set-print-case] as follows. ~bv[] (set-print-case :upcase state) ; Default printing (set-print-case :downcase state) ; Print symbols in lower case when ; vertical bars are not required ~ev[] The ACL2 user can expect that the ~c[:downcase] setting will have an effect for formatted output (~pl[fmt] and ~pl[fms]) when the directives are ~c[~~p], ~c[~~P], ~c[~~q], or ~c[~~Q], for built-in functions ~c[princ$] and ~c[prin1$], and the ~c[ppr] family of functions, and ~em[not] for built-in function ~c[print-object$]. For other printing functions, the effect of ~c[:downcase] is unspecified.~/ Also ~pl[print-control] for other user-settable print controls.~/" (declare (xargs :guard (and (or (eq case :upcase) (eq case :downcase)) (state-p state)))) (prog2$ (or (eq case :upcase) (eq case :downcase) (illegal 'set-print-case "The value ~x0 is illegal as an ACL2 print-case, which ~ must be :UPCASE or :DOWNCASE." (list (cons #\0 case)))) (f-put-global 'print-case case state))) (defmacro set-acl2-print-case (case) (declare (ignore case)) '(er soft 'set-acl2-print-case "Macro ~x0 has been replaced by function ~x1." 'set-acl2-print-case 'set-print-case)) (defmacro print-base (&optional (st 'state)) `(f-get-global 'print-base ,st)) (defmacro acl2-print-base (&optional (st 'state)) `(print-base ,st)) (defmacro print-radix (&optional (st 'state)) `(f-get-global 'print-radix ,st)) (defmacro acl2-print-radix (&optional (st 'state)) `(print-radix ,st)) (defun check-print-base (print-base ctx) ; Warning: Keep this in sync with print-base-p, and keep the format warning ; below in sync with princ$. (declare (xargs :guard t)) (if (print-base-p print-base) nil (hard-error ctx "The value ~x0 is illegal as a print-base, which must be 2, ~ 8, 10, or 16" (list (cons #\0 print-base)))) #+(and (not acl2-loop-only) allegro) (when (> print-base 10) (format t "NOTE: Printing of numbers in Allegro CL may be a bit slow. Allegro ~%~ CL's function PRINC prints alphabetic digits in lower case, unlike ~%~ other Lisps we have seen. While Allegro CL is compliant with the ~%~ Common Lisp spec in this regard, we have represented printing in the ~%~ logic in a manner consistent with those other Lisps, and hence ~%~ Allegro CL's PRINC violates our axioms. Therefore, ACL2 built on ~%~ Allegro CL prints radix-16 numbers without using the underlying ~%~ lisp's PRINC function.~%")) #+(and (not acl2-loop-only) (not allegro)) (when (int= print-base 16) (let ((*print-base* 16) (*print-radix* nil)) (or (equal (prin1-to-string 10) "A") ; If we get here, simply include the underlying Lisp as we handle allegro in ; the raw Lisp code for princ$. (illegal 'check-print-base "ERROR: This Common Lisp does not print in radix 16 using ~ upper-case alphabetic hex digits: for example, it prints ~ ~x0 instead of ~x1. Such printing is consistent with the ~ Common Lisp spec but is not reflected in ACL2's axioms ~ about printing (function digit-to-char, in support of ~ functions princ$ and prin1$), which in turn reflect the ~ behavior of the majority of Common Lisp implementations of ~ which we are aware. If the underlying Common Lisp's ~ implementors can make a patch available to remedy this ~ situation, please let the ACL2 implementors know and we ~ will incorporate a patch for that Common Lisp. In the ~ meantime, we do not see any way that this situation can ~ cause any unsoundness, so here is a workaround that you ~ can use at your own (minimal) risk. In raw Lisp, execute ~ the following form:~|~%~x2~|" (list (cons #\0 (prin1-to-string 10)) (cons #\1 "A") (cons #\2 '(defun check-print-base (print-base ctx) (declare (ignore print-base ctx)) nil)))))) nil) #-acl2-loop-only nil) (defun set-print-base (base state) ":Doc-Section IO control radix in which numbers are printed~/ By default, integers and ratios are printed in base 10. ACL2 also supports printing in radix 2, 8, or 16 by calling set-print-base with the desired radix (base). ~bv[] (set-print-base 10 state) ; Default printing (set-print-base 16 state) ; Print integers and ratios in hex ~ev[]~/ Here is a sample log. ~bv[] ACL2 !>(list 25 25/3) (25 25/3) ACL2 !>(set-print-base 16 state) ACL2 !>(list 25 25/3) (19 19/3) ACL2 !> ~ev[] ~l[set-print-radix] for how to print the radix, for example, printing the decimal number 25 in print-base 16 as ``~c[#x25]'' rather than ``~c[25]''. Also ~pl[print-control] for other user-settable print controls. Note: ACL2 ~il[events] and some other top-level commands (for example, ~ilc[thm], ~ilc[verify], and history commands such as ~c[:]~c[pe] and ~c[:]~c[pbt]) set the print base to 10 during their evaluation. So ~ilc[set-print-base] has no effect while these forms are being processed.~/" (declare (xargs :guard (and (print-base-p base) (state-p state)))) (prog2$ (check-print-base base 'set-print-base) (f-put-global 'print-base base state))) (defmacro set-acl2-print-base (base) (declare (ignore base)) '(er soft 'set-acl2-print-base "Macro ~x0 has been replaced by function ~x1." 'set-acl2-print-base 'set-print-base)) (defun set-print-circle (x state) (declare (xargs :guard (state-p state))) (f-put-global 'print-circle x state)) (defun set-print-escape (x state) (declare (xargs :guard (state-p state))) (f-put-global 'print-escape x state)) (defun set-print-pretty (x state) (declare (xargs :guard (state-p state))) (f-put-global 'print-pretty x state)) (defun set-print-radix (x state) ":Doc-Section IO control printing of the radix for numbers~/ ~l[set-print-base] for background on how the print base affects the printing of numbers. ~c[set-print-radix] affects whether a radix indicated when a number is printed. The radix is not indicated by default, or after evaluating ~c[(set-print-radix nil state)]. But if ~c[set-print-radix] is called with a first argument that evaluates to a non~c[nil] value ~-[] for example, ~c[(set-print-radix t state)] ~-[] then the radix is shown when printing. (This behavior is consistent with the handling of Common Lisp global ~c[*print-radix*].) The following log illustrates how this works. ~bv[] ACL2 !>(list 25 25/3) (25 25/3) ACL2 !>(set-print-base 16 state) ACL2 !>(list 25 25/3) (19 19/3) ACL2 !>(set-print-radix t state) ACL2 !>(list 25 25/3) (#x19 #x19/3) ACL2 !>(set-print-base 10 state) ACL2 !>(list 25 25/3) (25. #10r25/3) ACL2 !>(set-print-radix nil state) ACL2 !>(list 25 25/3) (25 25/3) ACL2 !> ~ev[] ~/~/" (declare (xargs :guard (state-p state))) (f-put-global 'print-radix x state)) (defun set-print-readably (x state) (declare (xargs :guard (state-p state))) (f-put-global 'print-readably x state)) (defun check-null-or-natp (n fn) (declare (xargs :guard t)) (or (null n) (natp n) (hard-error fn "The argument of ~x0 must be ~x1 or a positive integer, but ~ ~x2 is neither." (list (cons #\0 fn) (cons #\1 nil) (cons #\2 n))))) (defun set-print-length (n state) (declare (xargs :guard (and (or (null n) (natp n)) (state-p state)))) (prog2$ (check-null-or-natp n 'set-print-length) (f-put-global 'print-length n state))) (defun set-print-level (n state) (declare (xargs :guard (and (or (null n) (natp n)) (state-p state)))) (prog2$ (check-null-or-natp n 'set-print-level) (f-put-global 'print-level n state))) (defun set-print-lines (n state) (declare (xargs :guard (and (or (null n) (natp n)) (state-p state)))) (prog2$ (check-null-or-natp n 'set-print-lines) (f-put-global 'print-lines n state))) (defun set-print-right-margin (n state) (declare (xargs :guard (and (or (null n) (natp n)) (state-p state)))) (prog2$ (check-null-or-natp n 'set-print-right-margin) (f-put-global 'print-right-margin n state))) #-acl2-loop-only (defmacro get-input-stream-from-channel (channel) (list 'get channel (list 'quote *open-input-channel-key*) (list 'quote *non-existent-stream*))) #-acl2-loop-only (defmacro get-output-stream-from-channel (channel) (list 'get channel (list 'quote *open-output-channel-key*) (list 'quote *non-existent-stream*))) #-acl2-loop-only (defmacro with-print-controls (default bindings &rest body) ; Warning; If you bind *print-base* to value pb (in bindings), then you should ; strongly consider binding *print-radix* to t if pb exceeds 10 and to nil ; otherwise. (when (not (member-eq default '(:defaults :current))) (error "The first argument of with-print-controls must be :DEFAULT ~ or :CURRENT.")) (let ((raw-print-vars-alist '((*print-base* print-base . (f-get-global 'print-base state)) (*print-case* print-case . (f-get-global 'print-case state)) (*print-circle* print-circle . (f-get-global 'print-circle state)) (*print-escape* print-escape . (f-get-global 'print-escape state)) (*print-length* print-length . (f-get-global 'print-length state)) (*print-level* print-level . (f-get-global 'print-level state)) #+cltl2 (*print-lines* print-lines . (f-get-global 'print-lines state)) #+cltl2 (*print-miser-width* nil . nil) (*print-pretty* print-pretty . (f-get-global 'print-pretty state)) (*print-radix* print-radix . (f-get-global 'print-radix state)) (*print-readably* print-readably . (f-get-global 'print-readably state)) ; At one time we did something with *print-pprint-dispatch* for #+cltl2. But ; as of May 2013, ANSI GCL does not comprehend this variable. So we skip it ; here. In fact we skip it for all host Lisps, assuming that users who mess ; with *print-pprint-dispatch* in raw Lisp take responsibility for knowing what ; they're doing! ; #+cltl2 ; (*print-pprint-dispatch* nil . nil) #+cltl2 (*print-right-margin* print-right-margin . (f-get-global 'print-right-margin state))))) (when (not (and (alistp bindings) (let ((vars (strip-cars bindings))) (and (subsetp-eq vars (strip-cars raw-print-vars-alist)) (no-duplicatesp vars))))) (error "With-print-controls has illegal bindings:~% ~s" bindings)) `(let ((state *the-live-state*)) (let ((*read-base* 10) ; just to be safe (*readtable* *acl2-readtable*) #+cltl2 (*read-eval* nil) ; to print without using #. (*package* (find-package-fast (current-package state))) ,@bindings) (let ,(loop for triple in raw-print-vars-alist when (not (assoc-eq (car triple) bindings)) collect (let ((lisp-var (car triple)) (acl2-var (cadr triple))) (list lisp-var (cond ((and acl2-var (eq default :defaults)) (cadr (assoc-eq acl2-var *print-control-defaults*))) (t (cddr triple)))))) ,@body))))) ; ?? (v. 1.8) I'm not going to look at many, or any, of the skip-proofs ; events on this pass. (skip-proofs (defun princ$ (x channel state-state) ":Doc-Section ACL2::ACL2-built-ins print an atom~/ Use ~c[princ$] to do basic printing of atoms (i.e., other than ~c[cons] pairs). In particular, ~c[princ$] prints a string without the surrounding double-quotes and without escaping double-quote characters within the string. Note that ~c[princ$] is sensitive to the print-base, print-radix, and print-case; ~pl[set-print-base], ~pl[set-print-radix], and ~pl[set-print-case]. ~c[Princ$] returns ~ilc[state]. ~bv[] Examples: ACL2 !>(princ$ \"Howdy ho\" (standard-co state) state) Howdy ho ACL2 !>(pprogn (princ$ \"Howdy ho\" (standard-co state) state) (newline (standard-co state) state)) Howdy ho ACL2 !>(princ$ \"ab\\\"cd\" *standard-co* state) ab\"cd ACL2 !> ACL2 !>(princ$ 17 *standard-co* state) 17 ACL2 !>(set-print-base 16 state) ACL2 !>(princ$ 17 *standard-co* state) 11 ACL2 !>(set-print-radix t state) ACL2 !>(princ$ 17 *standard-co* state) #x11 ACL2 !>(princ$ 'xyz *standard-co* state) XYZ ACL2 !>(set-print-case :downcase state) ACL2 !>(princ$ 'xyz *standard-co* state) xyz ACL2 !> ~ev[]~/ The ~il[guard] for ~c[(princ$ x channel state)] is essentially as follows; ~pl[io] for an explanation of guards of certain built-in functions that take ~il[state], such as ~c[princ$]. ~bv[] (and (or (acl2-numberp x) (characterp x) (stringp x) (symbolp x)) (state-p1 state-state) (symbolp channel) (open-output-channel-p1 channel :character state-state)) ~ev[] ~l[fmt] for more sophisticated printing routines, and ~pl[IO] for general information about input and output.~/ :cited-by IO" ; Wart: We use state-state instead of state because of a bootstrap problem. ; The ACL2 princ$ does not handle conses because we are unsure what ; the specification of the real Common Lisp princ is concerning the ; insertion of spaces and newlines into the resulting text. (declare (xargs :guard (and (or (acl2-numberp x) (characterp x) (stringp x) (symbolp x)) (state-p1 state-state) (symbolp channel) (open-output-channel-p1 channel :character state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond ((and *wormholep* (not (eq channel *standard-co*))) ; If the live state is protected, then we allow output only to the ; *standard-co* channel. This is a little unexpected. The intuitive ; arrangement would be to allow output only to a channel whose actual ; stream was pouring into the wormhole window. Unfortunately, we do not ; know a good way to determine the ultimate stream to which a synonym ; stream is directed and hence cannot implement the intuitive ; arrangement. Instead we must assume that if *the-live-state- ; protected* is non-nil, then the standard channels have all been ; directed to acceptable streams and that doing i/o on them will not ; affect the streams to which they are normally directed. (wormhole-er 'princ$ (list x channel)))) (let ((stream (get-output-stream-from-channel channel))) (cond ((stringp x) ; We get a potentially significant efficiency boost by using write-string when ; x is a string. A few experiments suggest that write-string may be slightly ; more efficient than write-sequence (which isn't available in non-ANSI GCL ; anyhow), which in turn may be much more efficient than princ. It appears ; that the various print-controls don't affect the printing of strings, except ; for *print-escape* and *print-readably*; and the binding of *print-escape* to ; nil by princ seems to give the behavior of write-string, which is specified ; simply to print the characters of the string. (write-string x stream)) (t (with-print-controls ; We use :defaults here, binding only *print-escape* and *print-readably* (to ; avoid |..| on symbols), to ensure that raw Lisp agrees with the logical ; definition. :defaults ((*print-escape* nil) (*print-readably* nil) ; unnecessary if we keep current default (*print-base* (f-get-global 'print-base state)) (*print-radix* (f-get-global 'print-radix state)) (*print-case* (f-get-global 'print-case state))) #+allegro ; See the format call in check-print-base for why we take this extra effort for ; Allegro (in short, to print digit characters in upper case). (princ (cond ((and (rationalp x) (> *print-base* 10)) (coerce (explode-atom+ x *print-base* *print-radix*) 'string)) (t x)) stream) #-allegro (princ x stream)))) (cond ((eql x #\Newline) (force-output stream))) (return-from princ$ *the-live-state*)))) (let ((entry (cdr (assoc-eq channel (open-output-channels state-state))))) (update-open-output-channels (add-pair channel (cons (car entry) (revappend (if (and (symbolp x) ; The form (cdr (assoc-eq ...)) below is closely related to a call of ; print-case where state is replaced by state-state. However, the problem ; explained in the essay "On STATE-STATE" hits us here. That is, print-case ; generates a call of get-global, which, by the time this form is processed in ; the logic during boot-strap, expects state as an argument. We do not have ; state available here. We could modify print-case to take an optional ; argument and supply state-state for that argument, but that would not work ; either because get-global expects state. (eq (cdr (assoc-eq 'print-case (global-table state-state))) :downcase)) (coerce (string-downcase (symbol-name x)) 'list) (explode-atom+ x (cdr (assoc-eq 'print-base (global-table state-state))) (cdr (assoc-eq 'print-radix (global-table state-state))))) (cdr entry))) (open-output-channels state-state)) state-state))) ) (defun write-byte$ (x channel state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (integerp x) (>= x 0) (< x 256) (state-p1 state-state) (symbolp channel) (open-output-channel-p1 channel :byte state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond ((and *wormholep* (not (eq channel *standard-co*))) (wormhole-er 'write-byte$ (list x channel)))) (let ((stream (get-output-stream-from-channel channel))) (write-byte x stream) (return-from write-byte$ *the-live-state*)))) (let ((entry (cdr (assoc-eq channel (open-output-channels state-state))))) (update-open-output-channels (add-pair channel (cons (car entry) (cons x (cdr entry))) (open-output-channels state-state)) state-state))) #-acl2-loop-only (defvar *print-circle-stream* nil) (defmacro er (severity context str &rest str-args) ; Keep in sync with er@par. (declare (xargs :guard (and (true-listp str-args) (member-symbol-name (symbol-name severity) '(hard hard? hard! hard?! soft very-soft)) (<= (length str-args) 10)))) ; Note: We used to require (stringp str) but then we started writing such forms ; as (er soft ctx msg x y z), where msg was bound to the error message str ; (because the same string was used many times). ; The special form (er hard "..." &...) expands into a call of illegal on "..." ; and an alist built from &.... Since illegal has a guard of nil, the attempt ; to prove the correctness of a fn producing a hard error will require proving ; that the error can never occur. At runtime, illegal causes a CLTL error. ; The form (er soft ctx "..." &...) expands into a call of error1 on ctx, "..." ; and an alist built from &.... At runtime error1 builds an error object and ; returns it. Thus, soft errors are not errors at all in the CLTL sense and ; any function calling one which might cause an error ought to handle it. ; Just to make it easier to debug our code, we have arranged for the er macro ; to actually produce a prog2 form in which the second arg is as described ; above but the preceding one is an fmt statement which will actually print the ; error str and alist. Thus, we can see when soft errors occur, whether or not ; the calling program handles them appropriately. ; We do not advertise the hard! or very-soft severities, at least not yet. The ; implementation uses the former to force a hard error even in contexts where ; we would normally return nil. ":Doc-Section ACL2::ACL2-built-ins print an error message and ``cause an error''~/ ~l[fmt] for a general discussion of formatted printing in ACL2. All calls of ~c[er] print formatted strings, just as is done by ~ilc[fmt]. ~bv[] Example Forms: (er hard 'top-level \"Illegal inputs, ~~x0 and ~~x1.\" a b) (er hard? 'top-level \"Illegal inputs, ~~x0 and ~~x1.\" a b) (er soft 'top-level \"Illegal inputs, ~~x0 and ~~x1.\" a b) ~ev[] The examples above all print an error message to standard output saying that ~c[a] and ~c[b] are illegal inputs. However, the first two abort evaluation after printing an error message (while logically returning ~c[nil], though in ordinary evaluation the return value is never seen); while the third returns ~c[(mv t nil state)] after printing an error message. The result in the third case can be interpreted as an ``error'' when programming with the ACL2 ~ilc[state], something most ACL2 users will probably not want to do unless they are building systems of some sort; ~pl[programming-with-state]. If state is not available in the current context then you will probably want to use ~c[(er hard ...)] or ~c[(er hard? ...)] to cause an error; for example, if you are returning two values, you may write ~c[(mv (er hard ...) nil)]. The difference between the ~c[hard] and ~c[hard?] forms is one of guards. Use ~c[hard] if you want the call to generate a (clearly impossible) guard proof obligation of (essentially) ~c[NIL]. But use ~c[hard?] if you want to be able to call this function in guard-verified code, since the call generates a (trivially satisfied) guard proof obligation of ~c[T]. ~c[Er] is a macro, and the above three examples expand to calls of ACL2 functions, as shown below. ~l[illegal], ~pl[hard-error], and ~pl[error1]. The first two have guards of (essentially) ~c[NIL] and ~c[T], respectively, while ~ilc[error1] is in ~c[:]~ilc[program] mode.~/ ~bv[] General forms: (er hard ctx fmt-string arg1 arg2 ... argk) ==> {macroexpands, in essence, to:} (ILLEGAL CTX FMT-STRING (LIST (CONS #\\0 ARG1) (CONS #\\1 ARG2) ... (CONS #\\k ARGk))) (er hard? ctx fmt-string arg1 arg2 ... argk) ==> {macroexpands, in essence, to:} (HARD-ERROR CTX FMT-STRING (LIST (CONS #\\0 ARG1) (CONS #\\1 ARG2) ... (CONS #\\k ARGk))) (er soft ctx fmt-string arg1 arg2 ... argk) ==> {macroexpands, in essence, to:} (ERROR1 CTX FMT-STRING (LIST (CONS #\\0 ARG1) (CONS #\\1 ARG2) ... (CONS #\\k ARGk))) ~ev[]~/" (let ((alist (make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) str-args)) (severity-name (symbol-name severity))) (cond ((equal severity-name "SOFT") (list 'error1 context str alist 'state)) ((equal severity-name "VERY-SOFT") (list 'error1-safe context str alist 'state)) ((equal severity-name "HARD?") (list 'hard-error context str alist)) ((equal severity-name "HARD") (list 'illegal context str alist)) ((equal severity-name "HARD!") #+acl2-loop-only (list 'illegal context str alist) #-acl2-loop-only `(let ((*hard-error-returns-nilp* nil)) (illegal ,context ,str ,alist))) ((equal severity-name "HARD?!") #+acl2-loop-only (list 'hard-error context str alist) #-acl2-loop-only `(let ((*hard-error-returns-nilp* nil)) (hard-error ,context ,str ,alist))) (t ; The final case should never happen. (illegal 'top-level "Illegal severity, ~x0; macroexpansion of ER failed!" (list (cons #\0 severity))))))) #+acl2-par (defmacro er@par (severity context str &rest str-args) ; Keep in sync with er. (declare (xargs :guard (and (true-listp str-args) (member-symbol-name (symbol-name severity) '(hard hard? hard! soft very-soft)) (<= (length str-args) 10)))) (let ((alist (make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) str-args)) (severity-name (symbol-name severity))) (cond ((equal severity-name "SOFT") (list 'error1@par context str alist 'state)) (t ; The final case should never happen. (illegal 'top-level "Illegal severity, ~x0; macroexpansion of ER@PAR failed!" (list (cons #\0 severity))))))) (defun get-serialize-character (state) (declare (xargs :guard (and (state-p state) (boundp-global 'serialize-character state)))) (f-get-global 'serialize-character state)) (defun w (state) (declare (xargs :guard (state-p state) ; We have moved the definition of w up to here, so that we can call it from ; hons-enabledp, which is called from set-serialize-character, which we prefer ; to define before print-object$. We have verified its guards successfully ; later in this file, where w was previously defined. So rather fight that ; battle here, we verify guards at the location of its original definition. :verify-guards nil)) (f-get-global 'current-acl2-world state)) (defun hons-enabledp (state) (declare (xargs :verify-guards nil ; wait for w :guard (state-p state))) (global-val 'hons-enabled (w state))) (defun set-serialize-character (c state) (declare (xargs :verify-guards nil ; wait for hons-enabledp :guard (and (state-p state) (or (null c) (and (hons-enabledp state) (member c '(#\Y #\Z))))))) (cond ((or (null c) (and (hons-enabledp state) (member c '(#\Y #\Z)))) (f-put-global 'serialize-character c state)) (t ; presumably guard-checking is off (prog2$ (cond ((not (hons-enabledp state)) ; and note that c is not nil (er hard 'set-serialize-character "It is currently only legal to call ~x0 with a non-nil first ~ argument in a hons-enabled version of ACL2. If this ~ presents a problem, feel free to contact the ACL2 ~ implementors." 'set-serialize-character)) (t (er hard 'set-serialize-character "The first argument of a call of ~x0 must be ~v1. The ~ argument ~x2 is thus illegal." 'set-serialize-character '(nil #\Y #\Z) c))) state)))) (defun print-object$-ser (x serialize-character channel state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; This function is a version of print-object$ that allows specification of the ; serialize-character, which can be nil (the normal case for #-hons), #\Y, or ; #\Z (the normal case for #+hons). However, we currently treat this as nil in ; the #-hons version. ; See print-object$ for additional comments. (declare (ignorable serialize-character) ; only used when #+hons (xargs :guard (and (state-p1 state-state) (member serialize-character '(nil #\Y #\Z)) (symbolp channel) (open-output-channel-p1 channel :object state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* ; There is no standard object output channel and hence this channel is ; directed to some unknown user-specified sink and we can't touch it. (wormhole-er 'print-object$ (list x channel)))) (let ((stream (get-output-stream-from-channel channel))) (declare (special acl2_global_acl2::current-package)) ; Note: If you change the following bindings, consider changing the ; corresponding bindings in print-object$. (with-print-controls :current ((*print-circle* (and *print-circle-stream* (f-get-global 'print-circle state-state)))) (terpri stream) (or #+hons (cond (serialize-character (write-char #\# stream) (write-char serialize-character stream) (ser-encode-to-stream x stream) t)) (prin1 x stream)) (force-output stream))) (return-from print-object$-ser *the-live-state*))) (let ((entry (cdr (assoc-eq channel (open-output-channels state-state))))) (update-open-output-channels (add-pair channel (cons (car entry) (cons x (cdr entry))) (open-output-channels state-state)) state-state))) (defthm all-boundp-preserves-assoc-equal (implies (and (all-boundp tbl1 tbl2) (assoc-equal x tbl1)) (assoc-equal x tbl2)) :rule-classes nil) (local (defthm all-boundp-initial-global-table (implies (and (state-p1 state) (assoc-eq x *initial-global-table*)) (assoc x (nth 2 state))) :hints (("Goal" :use ((:instance all-boundp-preserves-assoc-equal (tbl1 *initial-global-table*) (tbl2 (nth 2 state)))) :in-theory (disable all-boundp))))) (defun print-object$ (x channel state) ; WARNING: In the HONS version, be sure to use with-output-object-channel-sharing ; rather than calling open-output-channel directly, so that ; *print-circle-stream* is initialized. ; We believe that if in a single Common Lisp session, one prints an object and ; then reads it back in with print-object$ and read-object, one will get back ; an equal object under the assumptions that (a) the package structure has not ; changed between the print and the read and (b) that *package* has the same ; binding. On a toothbrush, all calls of defpackage will occur before any ; read-objecting or print-object$ing, so the package structure will be the ; same. It is up to the user to set current-package back to what it was at ; print time if he hopes to read back in the same object. ; Warning: For soundness, we need to avoid using iprinting when writing to ; certificate files. We do all such writing with print-object$, so we rely on ; print-object$ not to use iprinting. (declare (xargs :guard (and (state-p state) ; We might want to modify state-p (actually, state-p1) so that the following ; conjunct is not needed. (member (get-serialize-character state) '(nil #\Y #\Z)) (symbolp channel) (open-output-channel-p channel :object state)))) (print-object$-ser x (get-serialize-character state) channel state)) ; We start the file-clock at one to avoid any possible confusion with ; the wired in standard-input/output channels, whose names end with ; "-0". #-acl2-loop-only (defparameter *file-clock* 1) (skip-proofs (defun make-input-channel (file-name clock) (declare (xargs :guard (and (rationalp clock) (standard-char-listp (explode-atom clock 10)) (stringp file-name) (standard-char-listp (coerce file-name 'list))))) (intern (coerce (append (coerce file-name 'list) (cons '#\- (explode-atom clock 10))) 'string) "ACL2-INPUT-CHANNEL")) ) (skip-proofs (defun make-output-channel (file-name clock) (declare (xargs :guard (and (rationalp clock) (standard-char-listp (explode-atom clock 10)) (or (eq file-name :string) (and (stringp file-name) (standard-char-listp (coerce file-name 'list))))))) (intern (coerce (cond ((eq file-name :string) (explode-atom clock 10)) (t (append (coerce file-name 'list) (cons '#\- (explode-atom clock 10))))) 'string) "ACL2-OUTPUT-CHANNEL")) ) ; We here set up the property list of the three channels that are open ; at the beginning. The order of the setfs and the superfluous call ; of symbol-name are to arrange, in AKCL, for the stream component to ; be first on the property list. #-acl2-loop-only (defun-one-output setup-standard-io () (symbol-name 'acl2-input-channel::standard-object-input-0) (setf (get 'acl2-input-channel::standard-object-input-0 *open-input-channel-type-key*) :object) (setf (get 'acl2-input-channel::standard-object-input-0 ; Here, and twice below, we use *standard-input* rather than ; (make-synonym-stream '*standard-input*) because Allegro doesn't ; seem to print to such a synonym stream. Perhaps it's relevant ; that (interactive-stream-p (make-synonym-stream '*standard-input*)) ; evaluates to nil in Allegro, but ; (interactive-stream-p *standard-input*) evaluates to t. *open-input-channel-key*) *standard-input*) (symbol-name 'acl2-input-channel::standard-character-input-0) (setf (get 'acl2-input-channel::standard-character-input-0 *open-input-channel-type-key*) :character) (setf (get 'acl2-input-channel::standard-character-input-0 *open-input-channel-key*) *standard-input*) (symbol-name 'acl2-output-channel::standard-character-output-0) (setf (get 'acl2-output-channel::standard-character-output-0 *open-output-channel-type-key*) :character) (setf (get 'acl2-output-channel::standard-character-output-0 *open-output-channel-key*) *standard-output*)) #-acl2-loop-only (eval-when #-cltl2 (load eval compile) #+cltl2 (:load-toplevel :execute :compile-toplevel) (setup-standard-io)) #-acl2-loop-only (defun-one-output lisp-book-syntaxp1 (s stream) ; See the parent function. This is a tail-recursive finite state acceptor. ; Our state s is one of: ; 0 - scanning spaces, tabs and newlines, ; semi - scanning thru the next newline (we saw a ; on this line) ; n>0 - (positive integer) scanning to the balancing bar hash sign. ; (hash . s) - just saw a hash sign in state s: if next char is ; a vertical bar, we've entered a new comment level. ; The s here is either 0 or n>0, i.e., we were in a ; state where hash bar opens a comment. ; (bar . s) - just saw a vertical bar in state s: if next char is hash ; we've exited a comment level. The s here is always an n>0, ; i.e., we were in a state where bar hash closes a comment. ; charlist - we insist that the n next chars in the file be the n chars ; in charlist; we return t if so and nil if not. ; list-of-charlist - we insist that the next char be one of the keys in ; this alist and that subsequent chars be as in corresponding ; value. (let ((char1 (read-char stream nil nil))) (cond ((null char1) nil) ((eq s 'semi) (cond ((eql char1 #\Newline) (lisp-book-syntaxp1 0 stream)) (t (lisp-book-syntaxp1 'semi stream)))) ((integerp s) (cond ((= s 0) (cond ((member char1 '(#\Space #\Tab #\Newline)) (lisp-book-syntaxp1 0 stream)) ((eql char1 #\;) (lisp-book-syntaxp1 'semi stream)) ((eql char1 #\#) (lisp-book-syntaxp1 '(hash . 0) stream)) ((eql char1 #\() (lisp-book-syntaxp1 '((#\I #\N #\- #\P #\A #\C #\K #\A #\G #\E #\Space #\") (#\L #\I #\S #\P #\: . ( (#\I #\N #\- #\P #\A #\C #\K #\A #\G #\E #\Space #\") (#\: #\I #\N #\- #\P #\A #\C #\K #\A #\G #\E #\Space #\"))) (#\A #\C #\L #\2 #\: #\: #\I #\N #\- #\P #\A #\C #\K #\A #\G #\E #\Space #\")) stream)) (t nil))) ((eql char1 #\#) (lisp-book-syntaxp1 (cons 'hash s) stream)) ((eql char1 #\|) (lisp-book-syntaxp1 (cons 'bar s) stream)) (t (lisp-book-syntaxp1 s stream)))) ((null s) t) ((eq (car s) 'hash) (cond ((eql char1 #\|) (lisp-book-syntaxp1 (1+ (cdr s)) stream)) ((= (cdr s) 0) #\#) ((eql char1 #\#) (lisp-book-syntaxp1 s stream)) (t (lisp-book-syntaxp1 (cdr s) stream)))) ((eq (car s) 'bar) (cond ((eql char1 #\#) (lisp-book-syntaxp1 (1- (cdr s)) stream)) ((eql char1 #\|) (lisp-book-syntaxp1 s stream)) (t (lisp-book-syntaxp1 (cdr s) stream)))) ((characterp (car s)) (cond ((eql (char-upcase char1) (car s)) (lisp-book-syntaxp1 (cdr s) stream)) (t nil))) (t ; (car s) is a list of alternative character states (let ((temp (assoc (char-upcase char1) s))) (cond ((null temp) nil) (t (lisp-book-syntaxp1 (cdr temp) stream)))))))) #-acl2-loop-only (defun-one-output lisp-book-syntaxp (file) ; We determine whether file is likely to be an ACL2 book in lisp syntax. In ; particular, we determine whether file starts with an optional Lisp comment ; followed by (IN-PACKAGE ".... The comment may be any number of lines; ; (possibly empty) whitespace, semi-colon style comments and nested #|...|# ; comments are recognized as "comments" here. We further allow the IN-PACKAGE ; to be written in any case and we allow the optional package designators: ; LISP:, LISP::, and ACL2::. We insist that there be no space between the ; open-parenthesis and the IN-PACKAGE symbol. Finally, after the IN-PACKAGE, ; we insist that there be exactly one space followed by a string quote followed ; by at least one more character in the file. If these conditions are met we ; return t; otherwise we return nil. (cond ((null (f-get-global 'infixp *the-live-state*)) t) (t (let ((stream (safe-open file :direction :input :if-does-not-exist nil))) (if stream (unwind-protect (lisp-book-syntaxp1 0 stream) (close stream)) nil))))) #-acl2-loop-only (defparameter *parser* nil) ; If *parser* is non-nil then it should be set to a string that names a Unix ; command that parses a file. Suppose *parser* is set to "infixparse". Then ; we will use the Unix command ; % infixparse < foo.lisp > foo.lisp.mirror ; to generate from "foo.lisp" a file of s-expressions "foo.lisp.mirror". The ; unix command should return error code 3 if the parse fails. Otherwise, the ; parse is assumed to have worked. #-acl2-loop-only (defun-one-output parse-infix-file (infile outfile) ; This function is only used with the silly $ infix syntax. It is the analogue ; of the *parse* Unix command that transforms a $ infix file to its ; s-expression image. Rather than make it be a Unix command and pay the ; complexity and performance cost of firing off another process, we just ; implement it it directly in this image for the $ syntax. (with-open-file (file1 infile :direction :input) (with-open-file (file2 outfile :direction :output) (prog ((form nil) (eof (cons nil nil))) loop (setq form (read file1 nil eof)) (cond ((eq form eof) (return nil)) ((eq form '$) (setq form (read file1 nil eof)) (cond ((eq form eof) (error "Bad $ infix syntax in ~s. Ended with a $." (namestring file1))) (t (print form file2)))) (t (error "Bad $ infix syntax in file ~s. Missing $ before ~ s-expr ending at position ~a." (namestring file1) (file-position file1)))) (go loop))))) (skip-proofs (defun open-input-channel (file-name typ state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; Here, file-name is an ACL2 file name (i.e., with Unix-style syntax). ; It is possible to get an error when opening an output file. We consider that ; a resource error for purposes of the story. Note that starting after ; Version_6.1, an error is unlikely except for non-ANSI GCL because of our use ; of safe-open. (declare (xargs :guard (and (stringp file-name) (member-eq typ *file-types*) (state-p1 state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'open-input-channel (list file-name typ)))) (return-from open-input-channel (progn (setq *file-clock* (1+ *file-clock*)) ; We do two different opens here because the default :element-type is ; different in CLTL and CLTL2. (let ((os-file-name (pathname-unix-to-os file-name *the-live-state*))) ; Protect against the sort of behavior Bob Boyer has pointed out for GCL, as ; the following kills all processes: (cond ((and (not (equal os-file-name "")) (eql (char os-file-name 0) #\|)) (error "It is illegal in ACL2 to open a filename whose ~%~ first character is |, as this may permit dangerous ~%~ behavior. For example, in GCL the following kills ~%~ all processes:~%~%~s~%" '(open "|kill -9 -1")))) (let ((stream (case typ ((:character :object) (safe-open os-file-name :direction :input :if-does-not-exist nil)) (:byte (safe-open os-file-name :direction :input :element-type '(unsigned-byte 8) :if-does-not-exist nil)) (otherwise (interface-er "Illegal input-type ~x0." typ))))) (cond ((null stream) (mv nil *the-live-state*)) #+akcl ((and (eq typ :object) (not (lisp-book-syntaxp os-file-name))) ; Note that lisp-book-syntaxp returns t unless state global 'infixp is t. So ; ignore the code below unless you're thinking about the infix case! (let* ((mirror-file-name (concatenate 'string (namestring stream) ".mirror")) (er-code (cond (*parser* (si::system (format nil "~s < ~s > ~s" *parser* (namestring stream) mirror-file-name))) (t (parse-infix-file file-name mirror-file-name) 0)))) (cond ((not (equal er-code 3)) (let ((channel (make-input-channel mirror-file-name *file-clock*)) (mirror-stream (open mirror-file-name :direction :input))) (symbol-name channel) (setf (get channel *open-input-channel-type-key*) typ) (setf (get channel *open-input-channel-key*) mirror-stream) (mv channel *the-live-state*))) (t (mv nil *the-live-state*))))) (t (let ((channel (make-input-channel file-name *file-clock*))) (symbol-name channel) (setf (get channel *open-input-channel-type-key*) typ) (setf (get channel *open-input-channel-key*) stream) (mv channel *the-live-state*)))))))))) (let ((state-state (update-file-clock (1+ (file-clock state-state)) state-state))) (let ((pair (assoc-equal (list file-name typ (file-clock state-state)) (readable-files state-state)))) (cond (pair (let ((channel (make-input-channel file-name (file-clock state-state)))) (mv channel (update-open-input-channels (add-pair channel (cons (list :header typ file-name (file-clock state-state)) (cdr pair)) (open-input-channels state-state)) state-state)))) (t (mv nil state-state)))))) ) (defthm nth-update-nth (equal (nth m (update-nth n val l)) (if (equal (nfix m) (nfix n)) val (nth m l))) :hints (("Goal" :in-theory (enable nth)))) (defthm true-listp-update-nth (implies (true-listp l) (true-listp (update-nth key val l))) :rule-classes :type-prescription) (local (defthm nth-zp (implies (and (syntaxp (not (equal n ''0))) (zp n)) (equal (nth n x) (nth 0 x))) :hints (("Goal" :expand ((nth n x) (nth 0 x)))))) (defthm nth-update-nth-array (equal (nth m (update-nth-array n i val l)) (if (equal (nfix m) (nfix n)) (update-nth i val (nth m l)) (nth m l)))) (defun close-input-channel (channel state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (not (member-eq channel '(acl2-input-channel::standard-character-input-0 acl2-input-channel::standard-object-input-0))) (state-p1 state-state) (symbolp channel) (open-input-channel-any-p1 channel state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'close-input-channel (list channel)))) (return-from close-input-channel (progn (setq *file-clock* (1+ *file-clock*)) (let ((stream (get channel *open-input-channel-key*))) (remprop channel *open-input-channel-key*) (remprop channel *open-input-channel-type-key*) (close stream)) *the-live-state*)))) (let ((state-state (update-file-clock (1+ (file-clock state-state)) state-state))) (let ((header-entries (cdr (car (cdr (assoc-eq channel (open-input-channels state-state))))))) (let ((state-state (update-read-files (cons (list (cadr header-entries) ; file-name (car header-entries) ; type (caddr header-entries) ; open-time (file-clock state-state)) ; close-time (read-files state-state)) state-state))) (let ((state-state (update-open-input-channels (delete-assoc-eq channel (open-input-channels state-state)) state-state))) state-state))))) (skip-proofs (defun open-output-channel (file-name typ state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; Here, file-name is an ACL2 file name (i.e., with Unix-style syntax). ; It is possible to get an error when opening an output file. We consider that ; a resource error for purposes of the story. Note that starting after ; Version_6.1, an error is unlikely except for non-ANSI GCL because of our use ; of safe-open. (declare (xargs :guard (and (or (stringp file-name) (eq file-name :string)) (member-eq typ *file-types*) (state-p1 state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond ((eq file-name :string)) (*wormholep* (wormhole-er 'open-output-channel (list file-name typ))) ((and (not (f-get-global 'writes-okp state-state)) ; Sol Swords observed that calling open-output-channel! outside the ACL2 loop ; causes an error (which is due to its use of state-global-let*). But it's ; really not necessary to protect against bad file access in raw Lisp, because ; it's impossible! So we eliminate the check on writes-okp if the ld-level is ; 0, i.e., if we are outside the ACL2 loop. (not (eql 0 (f-get-global 'ld-level state-state)))) (mv (hard-error 'open-output-channel "It is illegal to call open-output-channel in ~ contexts that can appear in books, such as ~ make-event expansion and clause-processor ~ hint evaluation. The attempt to open an ~ output channel to file ~x0 has thus failed. ~ Consider using open-output-channel! instead, ~ which is legal if there is an active trust ~ tag; see :DOC defttag." (list (cons #\0 file-name))) state-state))) (return-from open-output-channel (progn (setq *file-clock* (1+ *file-clock*)) (let* ((os-file-name (and (not (eq file-name :string)) (pathname-unix-to-os file-name *the-live-state*))) (stream (case typ ((:character :object) (cond ((eq file-name :string) (make-string-output-stream)) (t (safe-open os-file-name :direction :output :if-exists :supersede ; In ACL2(p) using CCL, we have seen an error caused when standard-co was ; connected to a file. Specifically, waterfall-print-clause-id@par was ; printing to standard-co -- i.e., to that file -- and CCL complained because ; the default is for a file stream to be private to the thread that created it. #+(and acl2-par ccl) :sharing #+(and acl2-par ccl) :lock)))) (:byte (cond ((eq file-name :string) (make-string-output-stream :element-type '(unsigned-byte 8))) (t (safe-open os-file-name :direction :output :if-exists :supersede :element-type '(unsigned-byte 8) #+(and acl2-par ccl) :sharing #+(and acl2-par ccl) :lock)))) (otherwise (interface-er "Illegal output-type ~x0." typ))))) (cond ((null stream) (mv nil *the-live-state*)) (t (let ((channel (make-output-channel file-name *file-clock*))) (symbol-name channel) (setf (get channel *open-output-channel-type-key*) typ) (setf (get channel *open-output-channel-key*) stream) (mv channel *the-live-state*))))))))) (let ((state-state (update-file-clock (1+ (file-clock state-state)) state-state))) (cond ((member-equal (list file-name typ (file-clock state-state)) (writeable-files state-state)) (let ((channel (make-output-channel file-name (file-clock state-state)))) (mv channel (update-open-output-channels (add-pair channel (cons (list :header typ file-name (file-clock state-state)) nil) (open-output-channels state-state)) state-state)))) (t (mv nil state-state))))) ) (skip-proofs (defun open-output-channel! (file-name typ state) ":Doc-Section io when trust tags are needed to open output channels~/ Use this function in place of ~c[open-output-channel] if you want to open a channel for output at times this would otherwise be prohibited, for example during ~ilc[make-event] expansion and ~ilc[clause-processor] ~il[hints]. If this functionality doesn't quite seem like what you need, take a look at the definition of ~c[open-output-channel!] in axioms.lisp, specifically the binding of ~ilc[state] global variable ~c[writes-okp]. The following example, taken from community book ~c[books/hons-archive/hons-archive.lisp], illustrates the latter approach. ~bv[] (defmacro har-zip! (x filename &key sortp) \"See :doc hons-archive\" `(mv-let (erp val state) (progn! :state-global-bindings ((temp-touchable-vars t set-temp-touchable-vars)) (state-global-let* ((writes-okp t)) (let ((state (har-zip-fn ,x ,filename ,sortp state))) (mv nil nil state)))) (declare (ignore erp val)) state)) ~ev[] The book below illustrates the soundness loophole plugged in ACL2 Version_3.2 related to file writes during book certification.~/ ~bv[] ; The following example is adapted (with only very slight changes) ; from one written by Peter Dillinger. It illustrates the prohibition ; against writing files enforced by with-output-channel during book ; certification (more specifically, during make-event expansion). ; This book certifies in ACL2 Version_3.1 before the fix discussed in the ; paragraph about it being ``possible to write files during book ; certification'' in :DOC NOTE-3-2. The fix was actually made to ACL2 ; function open-output-channel. ; After the fix, in order for certification to succeed one needs to do ; two things. First, in raw lisp: ; (push :after-writes-okp-fix *features*) ; Second, certify with this command: ; (certify-book \"writes-okp\" 0 nil :ttags (:writes-okp)) (in-package \"ACL2\") (local (defun write-objects-to-channel (obj-lst chan state) (declare (xargs :mode :program :stobjs state :guard (true-listp obj-lst))) (if (consp obj-lst) (pprogn (print-object$ (car obj-lst) chan state) (write-objects-to-channel (cdr obj-lst) chan state) state) state))) #+after-writes-okp-fix (defttag :writes-okp) (local (defun write-objects-to-file (obj-lst filename state) (declare (xargs :mode :program :stobjs state :guard (and (stringp filename) (true-listp obj-lst)))) (mv-let (chan state) #-after-writes-okp-fix (open-output-channel filename :object state) #+after-writes-okp-fix (open-output-channel! filename :object state) (if chan (pprogn (write-objects-to-channel obj-lst chan state) (close-output-channel chan state) (value :done)) (er soft 'write-object-to-file \"Could not open for writing: ~~x0\" filename))))) (local (defconst *nil.lisp* '((in-package \"ACL2\") (defthm bad nil :rule-classes nil)))) (local (defconst *nil.cert* '((IN-PACKAGE \"ACL2\") \"ACL2 Version 3.1\" :BEGIN-PORTCULLIS-CMDS :END-PORTCULLIS-CMDS NIL ((\"/home/peterd/test/nil.lisp\" \"nil\" \"nil\" ((:SKIPPED-PROOFSP) (:AXIOMSP) (:TTAGS)) . 134094174)) 62589544 ))) (local (make-event (er-progn (write-objects-to-file *nil.lisp* \"nil.lisp\" state) (write-objects-to-file *nil.cert* \"nil.cert\" state) (value '(value-triple :invisible))))) (local (include-book \"nil\" :load-compiled-file nil)) (defthm bad nil :rule-classes nil) ~ev[]" (declare (xargs :guard (and (stringp file-name) (member-eq typ *file-types*) (state-p state)))) (cond ((eql 0 (f-get-global 'ld-level state)) ; See the comment about this case in open-output-channel. (open-output-channel file-name typ state)) (t (mv-let (erp chan state) (state-global-let* ((writes-okp t)) (mv-let (chan state) (open-output-channel file-name typ state) (value chan))) (declare (ignore erp)) (mv chan state))))) ) (defmacro assert$ (test form) ":Doc-Section ACL2::ACL2-built-ins cause a hard error if the given test is false~/ ~bv[] General Form: (assert$ test form) ~ev[] where ~c[test] returns a single value and ~c[form] is arbitrary. Semantically, this call of ~c[assert$] is equivalent to ~c[form]. However, it causes a hard error if the value of ~c[test] is ~c[nil]. That hard error invokes the function ~ilc[illegal], which has a ~il[guard] that is equal to ~c[nil]; so if you use ~c[assert$] in code for which you verify guards, then a proof obligation will be that the occurrence of ~c[test] is never ~c[nil].~/~/" `(prog2$ (or ,test (er hard 'assert$ "Assertion failed:~%~x0" '(assert$ ,test ,form))) ,form)) (defun fmt-to-comment-window (str alist col evisc-tuple) ; WARNING: Keep this in sync with fmt-to-comment-window!. ; Logically, this is the constant function returning nil. However, it ; has a side-effect on the "comment window" which is imagined to be a ; separate window on the user's screen that cannot possibly be ; confused with the normal ACL2 display of the files in STATE. Using ; this function it is possible for an ACL2 expression to cause ; characters to appear in the comment window. Nothing whatsoever can ; be proved about these characters. If you want to prove something ; about ACL2 output, it must be directed to the channels and files in ; STATE. ":Doc-Section ACL2::ACL2-built-ins print to the comment window~/ ~l[cw] for an introduction to the comment window and the usual way to print it. Function ~c[fmt-to-comment-window] is identical to ~c[fmt1] (~pl[fmt]), except that the channel is ~ilc[*standard-co*] and the ACL2 ~ilc[state] is neither an input nor an output. An analogous function, ~c[fmt-to-comment-window!], prints with ~ilc[fmt!] instead of ~ilc[fmt], in order to avoid insertion of backslash (\\) characters for margins; also ~pl[cw!]. Note that even if you change the value of ~ilc[ld] special ~c[standard-co] (~pl[standard-co]), ~c[fmt-to-comment-window] will print to ~ilc[*standard-co*], which is the original value of ~ilc[standard-co].~/ ~bv[] General Form: (fmt-to-comment-window fmt-string alist col evisc-tuple) ~ev[] where these arguments are as desribed for ~ilc[fmt1]; ~pl[fmt].~/" (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore str alist col evisc-tuple)) #+acl2-loop-only nil ; Note: One might wish to bind *wormholep* to nil around this fmt1 expression, ; to avoid provoking an error if this fn is called while *wormholep* is t. ; However, the fact that we're printing to *standard-co* accomplishes the ; same thing. See the comment on synonym streams in princ$. #-acl2-loop-only (progn (fmt1 str alist col *standard-co* *the-live-state* evisc-tuple) nil)) (defun fmt-to-comment-window! (str alist col evisc-tuple) ; WARNING: Keep this in sync with fmt-to-comment-window. (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore str alist col evisc-tuple)) #+acl2-loop-only nil #-acl2-loop-only (progn (fmt1! str alist col *standard-co* *the-live-state* evisc-tuple) nil)) (defun pairlis2 (x y) ; Like pairlis$ except is controlled by y rather than x. (declare (xargs :guard (and (true-listp x) (true-listp y)))) (cond ((endp y) nil) (t (cons (cons (car x) (car y)) (pairlis2 (cdr x) (cdr y)))))) (defmacro cw (str &rest args) ; WARNING: Keep this in sync with cw!. ; A typical call of this macro is: ; (cw "The goal is ~p0 and the alist is ~x1.~%" ; (untranslate term t nil) ; unify-subst) ; Logically, this expression is equivalent to nil. However, it has ; the effect of first printing to the comment window the fmt string ; as indicated. It uses fmt-to-comment-window above, and passes it the ; column 0 and evisc-tuple nil, after assembling the appropriate ; alist binding the fmt vars #\0 through #\9. If you want ; (a) more than 10 vars, ; (b) vars other than the digit chars, ; (c) a different column, or ; (d) a different evisc-tuple, ; then call fmt-to-comment-window instead. ; Typically, calls of cw are embedded in prog2$ forms, ; e.g., ; (prog2$ (cw ...) ; (mv a b c)) ; which has the side-effect of printing to the comment window and ; logically returning (mv a b c). ":Doc-Section ACL2::ACL2-built-ins print to the comment window~/ Example: ~bv[] (cw \"The goal is ~~p0 and the alist is ~~x1.~~%\" (untranslate term t nil) unify-subst) ~ev[] Logically, this expression is equivalent to ~c[nil]. However, it has the effect of first printing to the so-called ``comment window'' the ~ilc[fmt] string as indicated. Thus, ~c[cw] is like ~c[fmt] (~pl[fmt]) except in three important ways. First, it is a macro whose calls expand to calls of a ~c[:]~ilc[logic] mode function. Second, it neither takes nor returns the ACL2 ~ilc[state]; logically ~c[cw] simply returns ~c[nil], although it prints to a ~em[comment window] that just happens to share the terminal screen with the standard character output ~ilc[*standard-co*]. Third, its ~c[fmt] args are positional references, so that for example ~bv[] (cw \"Answers: ~~p0 and ~~p1\" ans1 ans2) ~ev[] prints in the same manner as: ~bv[] (fmt \"Answers: ~~p0 and ~~p1\" (list (cons #\\0 ans1) (cons #\\1 ans2)) *standard-co* state nil) ~ev[] Typically, calls of ~c[cw] are embedded in ~ilc[prog2$] forms, e.g., ~bv[] (prog2$ (cw ...) (mv a b c)) ~ev[] which has the side-effect of printing to the comment window and logically returning ~c[(mv a b c)].~/ ~bv[] General Form: (cw fmt-string arg1 arg2 ... argn) ~ev[] where n is between 0 and 9 (inclusive). The macro uses ~ilc[fmt-to-comment-window], passing it the column ~c[0] and ~il[evisc-tuple] ~c[nil], after assembling the appropriate alist binding the ~ilc[fmt] vars #\\0 through #\\9; ~pl[fmt]. If you want ~bf[] (a) more than 10 vars, (b) vars other than the digit chars, (c) a different column, or (d) a different evisc-tuple, ~ef[] then call ~ilc[fmt-to-comment-window] instead. Also ~pl[cw!], which is useful if you want to be able to read the printed forms back in. Finally, we discuss another way to create formatted output that also avoids the need to pass in the ACL2 ~ilc[state]. The idea is to use wormholes; ~pl[wormhole]. Below is a function you can write, along with some calls, providing an illustration of this approach. ~bv[] (defun my-fmt-to-comment-window (str alist) (wormhole 'my-fmt-to-comment-window '(lambda (whs) whs) (list str alist) '(pprogn (fms (car (@ wormhole-input)) (cadr (@ wormhole-input)) *standard-co* state nil) (value :q)) :ld-verbose nil :ld-error-action :return ; harmless return on error :ld-prompt nil)) ; A non-erroneous call: (my-fmt-to-comment-window \"Here is ~~x0 for your inspection~~%\" (list (cons #\\0 'foo))) ; An error inside the fmt string (unbound fmt var); note that even ; with the error, the wormhole is exited. (my-fmt-to-comment-window \"Here is ~~x1 for your inspection~~%\" (list (cons #\\0 'foo))) ; A guard violation in the binding; note that even with the error, ; the wormhole is exited. (my-fmt-to-comment-window \"Here is ~~x0 for your inspection~~%\" (list (cons #\\0 (car 'foo)))) ~ev[]~/" `(fmt-to-comment-window ,str (pairlis2 '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (list ,@args)) 0 nil)) (defmacro cw! (str &rest args) ; WARNING: Keep this in sync with cw. ":Doc-Section ACL2::ACL2-built-ins print to the comment window~/ This is the same as ~ilc[cw], except that ~ilc[cw] inserts backslash (\\) characters when forced to print past the right margin, in order to make the output a bit clearer in that case. Use ~c[cw!] instead if you want to be able to read the forms back in.~/~/" `(fmt-to-comment-window! ,str (pairlis2 '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (list ,@args)) 0 nil)) (defun subseq-list (lst start end) (declare (xargs :guard (and (true-listp lst) (integerp start) (integerp end) (<= 0 start) (<= start end)) :mode :program)) (take (- end start) (nthcdr start lst))) #+acl2-loop-only (defun subseq (seq start end) ":Doc-Section ACL2::ACL2-built-ins subsequence of a string or list~/ For any natural numbers ~c[start] and ~c[end], where ~c[start] ~c[<=] ~c[end] ~c[<=] ~c[(length seq)], ~c[(subseq seq start end)] is the subsequence of ~c[seq] from index ~c[start] up to, but not including, index ~c[end]. ~c[End] may be ~c[nil], which which case it is treated as though it is ~c[(length seq)], i.e., we obtain the subsequence of ~c[seq] from index ~c[start] all the way to the end.~/ The ~il[guard] for ~c[(subseq seq start end)] is that ~c[seq] is a true list or a string, ~c[start] and ~c[end] are integers (except, ~c[end] may be ~c[nil], in which case it is treated as ~c[(length seq)] for the rest of this discussion), and ~c[0] ~c[<=] ~c[start] ~c[<=] ~c[end] ~c[<=] ~c[(length seq)]. ~c[Subseq] is a Common Lisp function. See any Common Lisp documentation for more information. Note: In Common Lisp the third argument of ~c[subseq] is optional, but in ACL2 it is required, though it may be ~c[nil] as explained above. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (or (true-listp seq) (stringp seq)) (integerp start) (<= 0 start) (or (null end) (and (integerp end) (<= end (length seq)))) (<= start (or end (length seq)))) :mode :program)) (if (stringp seq) (coerce (subseq-list (coerce seq 'list) start (or end (length seq))) 'string) (subseq-list seq start (or end (length seq))))) (defun lock-symbol-name-p (lock-symbol) (declare (xargs :guard t)) (and (symbolp lock-symbol) (let* ((name (symbol-name lock-symbol)) (len (length name))) (and (> len 2) (eql (char name 0) #\*) (eql (char name (1- len)) #\*))))) (defun assign-lock (key) (declare (xargs :guard (lock-symbol-name-p key))) #-(and (not acl2-loop-only) acl2-par) (declare (ignore key)) #+(and (not acl2-loop-only) acl2-par) (cond ((boundp key) (when (not (lockp (symbol-value key))) (error "Raw Lisp variable ~s is already bound to a value ~ that~%does not satisfy lockp." key))) (t (proclaim (list 'special key)) (setf (symbol-value key) (make-lock (symbol-name key))))) t) (table lock-table nil nil :guard (and (lock-symbol-name-p key) (assign-lock key))) #+(or acl2-loop-only (not acl2-par)) (defmacro with-lock (bound-symbol &rest forms) (declare (xargs :guard (lock-symbol-name-p bound-symbol))) `(translate-and-test (lambda (x) (prog2$ x ; x is not otherwise used (or (consp (assoc-eq ',bound-symbol (table-alist 'lock-table world))) (msg "The variable ~x0 has not been defined as a lock." ',bound-symbol)))) (progn$ ,@forms))) (defmacro deflock (lock-symbol) ; Deflock puts lock-symbol into the lock-table, and also defines a macro ; WITH-lock-symbol that is really just progn$. However, if #+acl2-par holds, ; then deflock also defines a ; Deflock defines what some Lisps call a "recursive lock", namely a lock that ; can be grabbed more than once by the same thread, but such that if a thread ; outside the owner tries to grab it, that thread will block. In addition to ; defining a lock, this macro also defines a macro that uses the lock to ; provide mutual-exclusion for a given list of operations. This macro has the ; name with-, where is the given ; lock-symbol without the leading and trailing * characters. ; Note that if lock-symbol is already bound, then deflock will not re-bind ; lock-symbol. (declare (xargs :guard (lock-symbol-name-p lock-symbol))) (let* ((name (symbol-name lock-symbol)) (macro-symbol (intern (concatenate 'string "WITH-" (subseq name 1 (1- (length name)))) "ACL2"))) `(progn (table lock-table ',lock-symbol t) ; The table event above calls make-lock when #+acl2-par, via assign-lock from ; the table guard of lock. However, table events are no-ops in raw Lisp, so we ; include the following form as well. #+(and acl2-par (not acl2-loop-only)) (defvar ,lock-symbol (make-lock (symbol-name ',lock-symbol))) (defmacro ,macro-symbol (&rest args) (list* 'with-lock ',lock-symbol args))))) (deflock ; Keep in sync with :DOC topic with-output-lock. *output-lock*) (skip-proofs ; as with open-output-channel (defun get-output-stream-string$-fn (channel state-state) (declare (xargs :guard (and (state-p1 state-state) (symbolp channel) (open-output-channel-any-p1 channel state-state)))) #-acl2-loop-only (when (live-state-p state-state) (let ((stream (get-output-stream-from-channel channel))) (when *wormholep* (wormhole-er 'get-output-stream-string$-fn (list channel))) (return-from get-output-stream-string$-fn (cond #-(and gcl (not cltl2)) ((not (typep stream 'string-stream)) (mv t nil state-state)) #+(and gcl (not cltl2)) ((or (not (typep stream 'stream)) (si::stream-name stream)) ; stream to a file ; As of this writing, we do not have confirmation from the gcl-devel list that ; si::stream-name really does return nil if and only if the stream is to a ; string rather than to a file. But we believe that to be the case. (mv t nil state-state)) (t (mv nil (get-output-stream-string stream) state-state)))))) #+acl2-loop-only (let* ((entry (cdr (assoc-eq channel (open-output-channels state-state)))) (header (assert$ (consp entry) (car entry))) (file-name (assert$ (and (true-listp header) (eql (length header) 4)) (nth 2 header)))) (cond ((eq file-name :string) (mv nil (coerce (reverse (cdr entry)) 'string) (update-open-output-channels (add-pair channel (cons header nil) (open-output-channels state-state)) state-state))) (t (mv t nil state-state))))) ) (defmacro get-output-stream-string$ (channel state-state &optional (close-p 't) (ctx ''get-output-stream-string$)) (declare (xargs :guard ; but *the-live-state* is OK in raw Lisp (eq state-state 'state)) (ignorable state-state)) `(let ((chan ,channel) (ctx ,ctx)) (mv-let (erp s state) (get-output-stream-string$-fn chan state) (cond (erp (er soft ctx "Symbol ~x0 is not associated with a string ~ output channel." chan)) (t ,(cond (close-p '(pprogn (close-output-channel chan state) (value s))) (t '(value s)))))))) (defun close-output-channel (channel state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (not (eq channel 'acl2-output-channel::standard-character-output-0)) (state-p1 state-state) (symbolp channel) (open-output-channel-any-p1 channel state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (when (eq channel (f-get-global 'standard-co state-state)) ; First, we cause a hard error if the channel is the value of state global ; 'standard-co. Comments below say more about this, but for now we point out ; that even though we cause an error, we won't get the error from term ; evaluation during proofs, because state-state will not be the live state. (mv (cond ((eq channel *standard-co*) ; This case might seem impossible because it would be a guard violation. But ; if a :program mode function call leads to the present call of ; close-output-channel, then the guard need not hold, so we make sure to cause ; an error here. (mv (er hard! 'close-output-channel "It is illegal to call close-output-channel on ~ *standard-co*."))) (t ; In Version_6.1 and probably before, we have seen an infinite loop occur ; when attempting to close standard-co. (state-free-global-let* ((standard-co *standard-co*)) (er hard! 'close-output-channel "It is illegal to call close-output-channel on ~ standard-co. Consider instead evaluating the ~ following form:~|~%~X01." '(let ((ch (standard-co state))) (er-progn (set-standard-co *standard-co* state) (pprogn (close-output-channel ch state) (value t)))) nil)))) state-state)) (cond (*wormholep* (wormhole-er 'close-output-channel (list channel)))) #+allegro ; April 2009: It seems that the last half of this month or so, occasionally ; there have been regression failures during inclusion of books that were ; apparently already certified. Those may all have been with Allegro CL. In ; particular, on 4/29/09 there were two successive regression failes as ; community book books/rtl/rel8/support/lib2.delta1/reps.lisp tried to include ; "bits" in that same directory. We saw a web page claiming an issue in old ; versions of Allegro CL for which finish-output didn't do the job, and ; force-output perhaps did. So we add a call here of force-output for Allegro. (force-output (get-output-stream-from-channel channel)) (finish-output (get-output-stream-from-channel channel)) ; At one time we called sync here, as shown below, for CCL. But Daron Vroon ; reported problems with (ccl:external-call "sync") on a PowerPC platform where ; "_sync" was expected instead. It seems best not to try to include code that ; is this low-level unless it is really necessary, because of the unknown ; diversity of future platforms that might require further maintenance; so ; we are commenting this out. ; #+ccl ; Bob Boyer suggestion ; (when (ccl-at-least-1-3-p) ; (ccl:external-call "sync")) (return-from close-output-channel (progn (setq *file-clock* (1+ *file-clock*)) (let ((str (get channel *open-output-channel-key*))) (remprop channel *open-output-channel-key*) (remprop channel *open-output-channel-type-key*) (close str)) *the-live-state*)))) (let ((state-state (update-file-clock (1+ (file-clock state-state)) state-state))) (let* ((pair (assoc-eq channel (open-output-channels state-state))) (header-entries (cdr (car (cdr pair))))) (let ((state-state (update-written-files (cons (cons (list (cadr header-entries) ; file-name (car header-entries) ; type (caddr header-entries) ; open-time (file-clock state-state)) ; close-time (cdr (cdr pair))) ; stuff written (written-files state-state)) state-state))) (let ((state-state (update-open-output-channels (delete-assoc-eq channel (open-output-channels state-state)) state-state))) state-state))))) (defun maybe-finish-output$ (channel state) ; Allegro 6.0 needs explicit calls of finish-output in order to flush to ; standard output when *print-pretty* is nil. SBCL 1.0 and 1.0.2 have ; exhibited this requirement during a redef query, for example: ; (defun foooooooooooooooooooooooooooo (x) x) ; :redef ; (defun foooooooooooooooooooooooooooo (x) (+ 1 x)) (declare (xargs :guard (and (symbolp channel) (state-p state) (open-output-channel-any-p channel state))) (ignorable channel state)) #+(and (not acl2-loop-only) (or sbcl allegro)) (finish-output (get-output-stream-from-channel channel)) nil) #-acl2-loop-only (defmacro legal-acl2-character-p (x) ; This predicate guarantees that two ACL2 characters with the same char-code ; are identical (eql). In fact, a legal character is an 8-bit character that ; is ``canonical,'' in the sense that it's the character returned by code-char ; on its character code. (let ((ch (gensym))) `(let* ((,ch ,x) (code (char-code ,ch))) (and (integerp code) (<= 0 code) (< code 256) (eql (the character ,ch) (the character (code-char code))))))) (defun read-char$ (channel state-state) ; read-char$ differs from read-char in several ways. It returns an ; mv-list of two values, the second being state. There are no eof ; args. Rather, nil is returned instead of character if there is no ; more input. ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (state-p1 state-state) (symbolp channel) (open-input-channel-p1 channel :character state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond ((and *wormholep* (not (eq channel *standard-ci*))) (wormhole-er 'read-char$ (list channel)))) (return-from read-char$ (let ((ch (read-char (get-input-stream-from-channel channel) nil nil))) (cond ((and ch (not (legal-acl2-character-p ch))) (interface-er "Illegal character read: ~x0 with code ~x1." ch (char-code ch))) (t (mv ch *the-live-state*))))))) (let ((entry (cdr (assoc-eq channel (open-input-channels state-state))))) (mv (car (cdr entry)) (update-open-input-channels (add-pair channel (cons (car entry) (cdr (cdr entry))) (open-input-channels state-state)) state-state)))) (defun peek-char$ (channel state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (state-p1 state-state) (symbolp channel) (open-input-channel-p1 channel :character state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond ((and *wormholep* (not (eq channel *standard-ci*))) (wormhole-er 'peek-char$ (list channel)))) (return-from peek-char$ (let ((ch (peek-char nil (get-input-stream-from-channel channel) nil nil))) (cond ((and ch (not (legal-acl2-character-p ch))) (interface-er "Illegal character peeked at: ~x0 with code ~x1." ch (char-code ch))) (t ch)))))) (let ((entry (cdr (assoc-eq channel (open-input-channels state-state))))) (car (cdr entry)))) (defun read-byte$ (channel state-state) ; read-byte$ differs from read-byte in several ways. It returns an ; mv-list of two values, the second being state. There are no eof ; args. Rather, nil is returned instead if there is no more input. ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (state-p1 state-state) (symbolp channel) (open-input-channel-p1 channel :byte state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'read-byte$ (list channel)))) (return-from read-byte$ (mv (read-byte (get-input-stream-from-channel channel) nil nil) *the-live-state*)))) (let ((entry (cdr (assoc-eq channel (open-input-channels state-state))))) (mv (car (cdr entry)) (update-open-input-channels (add-pair channel (cons (car entry) (cdr (cdr entry))) (open-input-channels state-state)) state-state)))) #-acl2-loop-only (defun-one-output parse-infix-from-terminal (eof) ; Eof is an arbitrary lisp object. If the terminal input is empty, return eof. ; Otherwise, parse one well-formed expression from terminal input and return the ; corresponding s-expr. If the file is exhausted before the parse finishes or ; if the parse is unsuccessful, cause a hard lisp error. ; In the current hackish implementation, the infix language is just a dollar ; sign followed by the s-expr. (let (dollar sexpr) (setq dollar (read *terminal-io* nil eof)) (cond ((eq dollar eof) eof) ((eq dollar '$) ; The following read could cause an error if the user types bad lisp syntax. (setq sexpr (read *terminal-io* nil eof)) (cond ((eq sexpr eof) (error "Ill-formed infix input. File ended on a $")) (t sexpr))) (t (error "Ill-formed infix input. You were supposed to type a $ ~ followed by an s-expression and you typed ~s instead." dollar))))) #-acl2-loop-only (defparameter *acl2-read-suppress* nil) (defun read-object (channel state-state) ; Read-object is somewhat like read. It returns an mv-list of three ; values: the first is a flag that is true iff the read happened at ; eof, the second is the object read (or nil if eof), and the third is ; state. ; Note that read-object establishes a new context for #n= reader macros, as it ; calls read (or hons-read) with a recursive-p argument of nil. ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (state-p1 state-state) (symbolp channel) (open-input-channel-p1 channel :object state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond ((and *wormholep* (not (eq channel *standard-oi*))) (wormhole-er 'read-object (list channel)))) (return-from read-object (let* ((read-object-eof ; Suggestion from Bob Boyer: By using dynamic-extent [see declaration below], ; we make the cons more 'secret' or 'new'. (Added August 2009: the ; dynamic-extent declaration below is commented out, with explanation. We are ; comfortable continuing to use a let-bound local here, since the extra cons ; seems trivial.) (cons nil nil)) (*package* (find-package (current-package *the-live-state*))) (*readtable* *acl2-readtable*) #+cltl2 (*read-eval* t) (*read-suppress* *acl2-read-suppress*) (*read-base* 10) #+gcl (si:*notify-gbc* ; no gbc messages while typing (if (or (eq channel *standard-oi*) (eq channel *standard-ci*)) nil si:*notify-gbc*)) (infixp (f-get-global 'infixp state-state)) (stream (get-input-stream-from-channel channel)) (obj (cond ((and (or (eq infixp t) (eq infixp :in)) (eq stream (get-input-stream-from-channel *standard-ci*))) (let ((obj (parse-infix-from-terminal read-object-eof))) (cond ((eq obj read-object-eof) read-object-eof) (t (chk-bad-lisp-object obj) obj)))) #+(and mcl (not ccl)) ((eq channel *standard-oi*) (ccl::toplevel-read)) ; (Comment for #+hons.) In the case of #+hons, we formerly called a function ; hons-read here when (f-get-global 'hons-read-p *the-live-state*) was true. ; That had the unfortunate behavior of hons-copying every object, which can be ; too expensive for large, unhonsed structures. This problem has been fixed ; with the addition of source files serialize[-raw].lisp, contributed by Jared ; Davis. (t (read stream nil read-object-eof nil))))) ; The following dynamic-extent declaration looks fine. But there have been ; spurious ill-formed certificate and checksum problems with Allegro CL for a ; few months (as of Aug. 2009) and I am suspicious that this could be the cause ; (in which case we have hit an Allegro CL compiler bug, if I'm correct about ; this declaration being fine). The efficiency improvement given by this ; declaration seems rather trivial, so I'll comment it out and see what ; happens. ; #+cltl2 ; (declare (dynamic-extent read-object-eof)) (cond ((eq obj read-object-eof) (mv t nil state-state)) (t (or (raw-mode-p state-state) (chk-bad-lisp-object obj)) (mv nil obj state-state))))))) (let ((entry (cdr (assoc-eq channel (open-input-channels state-state))))) (cond ((cdr entry) (mv nil (car (cdr entry)) (update-open-input-channels (add-pair channel (cons (car entry) (cdr (cdr entry))) (open-input-channels state-state)) state-state))) (t (mv t nil state-state))))) (defun read-object-suppress (channel state) ; Logically this function is the same as read-object except that it throws away ; the second returned value, i.e. the "real" value, simply returning (mv eof ; state). However, under the hood it uses Lisp special *read-suppress* to ; avoid errors in reading the next value, for example errors caused by ; encountering symbols in packages unknown to ACL2. (declare (xargs :guard (and (state-p state) (symbolp channel) (open-input-channel-p channel :object state)))) (let (#-acl2-loop-only (*acl2-read-suppress* t)) (mv-let (eof val state) (read-object channel state) (declare (ignore val)) (mv eof state)))) (defconst *suspiciously-first-numeric-chars* ; This constant is inlined in the definition of ; *suspiciously-first-numeric-array*. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\+ #\- #\. #\^ #\_)) (defconst *suspiciously-first-hex-chars* ; This constant is inlined in the definition of ; *suspiciously-first-hex-array*. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\A #\B #\C #\D #\E #\F #\a #\b #\c #\d #\e #\f #\+ #\- #\. #\^ #\_)) (defconst *base-10-chars* ; This constant is inlined in the definition of ; *base-10-array*. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) (defconst *hex-chars* ; This constant is inlined in the definition of ; *hex-array*. '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9 #\A #\B #\C #\D #\E #\F #\a #\b #\c #\d #\e #\f)) (defconst *letter-chars* ; This constant is inlined in the definition of ; *letter-array*. '(#\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z #\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z)) (defconst *slashable-chars* ; This constant is inlined in the definition of *slashable-array*. '(#\Newline #\Page #\Space #\" #\# #\' #\( #\) #\, #\: #\; #\\ #\` #\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\|)) (defun some-slashable (l) (declare (xargs :guard (character-listp l))) (cond ((endp l) nil) ((member (car l) *slashable-chars*) t) (t (some-slashable (cdr l))))) (skip-proofs (defun prin1-with-slashes1 (l slash-char channel state) (declare (xargs :guard (and (character-listp l) (characterp slash-char) (state-p state) (symbolp channel) (open-output-channel-p channel :character state)))) (cond ((endp l) state) (t (pprogn (cond ((or (equal (car l) #\\) (equal (car l) slash-char)) (princ$ #\\ channel state)) (t state)) (princ$ (car l) channel state) (prin1-with-slashes1 (cdr l) slash-char channel state))))) ) (skip-proofs (defun prin1-with-slashes (s slash-char channel state) (declare (xargs :guard (and (stringp s) (characterp slash-char) (state-p state) (symbolp channel) (open-output-channel-p channel :character state)))) #-acl2-loop-only (cond ((live-state-p state) ; We don't check *wormholep* here because it is checked in ; princ$ which is called first on each branch below. (let ((n (length (the string s)))) (declare (type fixnum n)) (do ((i 0 (1+ i))) ((= i n)) (declare (type fixnum i)) (let ((ch (aref (the string s) i))) (cond ((or (eql ch #\\) (eql ch slash-char)) (progn (princ$ #\\ channel state) (princ$ ch channel state))) (t (princ$ ch channel state)))))) (return-from prin1-with-slashes state))) (prin1-with-slashes1 (coerce s 'list) slash-char channel state)) ) (defmacro suspiciously-first-numeric-chars (print-base) `(if (eql ,print-base 16) *suspiciously-first-hex-chars* *suspiciously-first-numeric-chars*)) (defmacro numeric-chars (print-base) `(if (eql ,print-base 16) *hex-chars* *base-10-chars*)) (defun may-need-slashes1 (lst flg potnum-chars) ; See may-need-slashes. Here we check that lst (a symbol-name) consists ; entirely of digits, signs (+ or -), ratio markers (/), decimal points (.), ; extension characters (^ or _), except that it can also have letters provided ; there are no two consecutive letters. We could check only for upper-case ; letters, since lower-case letters are already handled (see some-slashable and ; *slashable-array* in may-need-slashes). But we might as well check for all ; letters, just to play it safe. ; Flg is t if the immediately preceding character was a letter, else nil. (declare (xargs :guard (and (character-listp lst) (true-listp potnum-chars)))) (cond ((endp lst) t) ((member (car lst) potnum-chars) (may-need-slashes1 (cdr lst) nil potnum-chars)) ((member (car lst) *letter-chars*) (cond (flg nil) (t (may-need-slashes1 (cdr lst) t potnum-chars)))) (t nil))) #-acl2-loop-only (defmacro potential-numberp (s0 n0 print-base) ; We assume that s is a non-empty string of length n. We return t if s ; represents a potential number for the given ACL2 print-base. (See ; may-need-slashes-fn for a discussion of potential numbers.) ; Warning: Keep this in sync with the corresponding #+acl2-loop-only code in ; may-need-slashes-fn. (let ((ar+ (gensym)) (ar (gensym)) (s (gensym)) (n (gensym))) `(let ((,ar+ (suspiciously-first-numeric-array ,print-base)) (,ar (numeric-array ,print-base)) (,s ,s0) (,n ,n0)) (declare (type fixnum ,n)) (and ; Either the first character is a digit or: the first character is a sign, ; decimal point, or extension character, and some other character is a digit. (let ((ch (the fixnum (char-code (aref (the string ,s) 0))))) (declare (type fixnum ch)) (or (svref ,ar ch) (and (svref ,ar+ ch) (do ((i 1 (1+ i))) ((= i ,n) nil) (declare (type fixnum i)) (when (svref ,ar (the fixnum (char-code (aref (the string ,s) i)))) (return t)))))) ; The string does not end with a sign. (not (member (aref (the string ,s) (the fixnum (1- ,n))) '(#\+ #\-))) ; The strong consists entirely of digits, signs, ratio markers, decimal points, ; extension characters, and number markers (i.e. letters, but no two in a ; row). The logic code for this is may-need-slashes1. (let ((prev-letter-p nil)) (do ((i 0 (1+ i))) ((= i ,n) t) (declare (type fixnum i)) (let ((ch (char-code (aref (the string ,s) i)))) (declare (type fixnum ch)) (cond ((or (svref ,ar+ ch) (int= ch *char-code-slash*)) (setq prev-letter-p nil)) ((svref *letter-array* ch) (cond (prev-letter-p (return nil)) (t (setq prev-letter-p t)))) (t (return nil)))))))))) (local ; needed for may-need-slashes-fn; could consider making this non-local (defthm character-listp-cdr (implies (character-listp x) (character-listp (cdr x))) :rule-classes :forward-chaining)) (defun may-need-slashes-fn (x print-base) ; We determine if the string x, a symbol name or symbol-package name, should be ; printed using |..|. The main ideas are to escape lower-case characters and ; to avoid the possibility of reading the printed result back in as a number ; instead of a symbol. ; More precisely: This function should return true if x represents a potential ; number, and ideally only if that is the case (in order to avoid needless use ; of |..|). The notion of "potential number" is discussed below. We perhaps ; escape more than necessary if print-base is 2, 4, or 8; the Common Lisp spec ; may not be clear on this, and anyhow it's simplest to be conservative and ; treat those bases as we treat base 10. ; The following four paragraphs from from Section 22.1.2 of CLtL2 ("Common Lisp ; the Language", 2nd Edition, by Guy L. Steele, Jr.) explains why we give ; separate consideration to the symbol-package-name and symbol-name. ; If there is a single package marker, and it occurs at the beginning of the ; token, then the token is interpreted as a keyword, that is, a symbol in ; the keyword package. The part of the token after the package marker must ; not have the syntax of a number. ; If there is a single package marker not at the beginning or end of the ; token, then it divides the token into two parts. The first part specifies ; a package; the second part is the name of an external symbol available in ; that package. Neither of the two parts may have the syntax of a number. ; If there are two adjacent package markers not at the beginning or end of ; the token, then they divide the token into two parts. The first part ; specifies a package; the second part is the name of a symbol within that ; package (possibly an internal symbol). Neither of the two parts may have ; the syntax of a number. ; X3J13 voted in March 1988 (COLON-NUMBER) to clarify that, in the ; situations described in the preceding three paragraphs, the restriction on ; the syntax of the parts should be strengthened: none of the parts may have ; the syntax of even a potential number. Tokens such as :3600, :1/2, and ; editor:3.14159 were already ruled out; this clarification further declares ; that such tokens as :2^ 3, compiler:1.7J, and Christmas:12/25/83 are also ; in error and therefore should not be used in portable ; programs. Implementations may differ in their treatment of such ; package-marked potential numbers. ; The following paragraph from a copy of the ANSI standard provides general ; guidance for printing symbols. We keep things simple by doing our escaping ; using |..|. ; When printing a symbol, the printer inserts enough single escape and/or ; multiple escape characters (backslashes and/or vertical-bars) so that if ; read were called with the same *readtable* and with *read-base* bound to ; the current output base, it would return the same symbol (if it is not ; apparently uninterned) or an uninterned symbol with the same print name ; (otherwise). ; ; For example, if the value of *print-base* were 16 when printing the symbol ; face, it would have to be printed as \FACE or \Face or |FACE|, because the ; token face would be read as a hexadecimal number (decimal value 64206) if ; the value of *read-base* were 16. ; ; Now, ACL2 never sets the read-base to 10, and indeed it only allows setting ; of its own print-base (i.e., state global 'print-base rather than Lisp ; variable *print-base*). Nevertheless we take a conservative interpretation ; of the paragraph immediately above: if the ACL2 print-base is 16, then we ; print a symbol as though it may be read back in base 16, which could happen ; if the user submits the result to raw Lisp. ; ; Back to the same CLtL2 section as above, we find the following syntax for ; numbers. ; Table 22-2: Actual Syntax of Numbers ; ; number ::= integer | ratio | floating-point-number ; integer ::= [sign] {digit}+ [decimal-point] ; ratio ::= [sign] {digit}+ / {digit}+ ; floating-point-number ::= [sign] {digit}* decimal-point {digit}+ [exponent] ; | [sign] {digit}+ [decimal-point {digit}*] exponent ; sign ::= + | - ; decimal-point ::= . ; digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 ; exponent ::= exponent-marker [sign] {digit}+ ; exponent-marker ::= e | s | f | d | l | E | S | F | D | L ; But instead of escaping strings that represent numbers, we escape strings ; that represent potential numbers. Quoting again from that same section of ; CLtL2: ; ; To allow for extensions to the syntax of numbers, a syntax for ; potential numbers is defined in Common Lisp that is more general ; than the actual syntax for numbers. Any token that is not a ; potential number and does not consist entirely of dots will always ; be taken to be a symbol, now and in the future; programs may rely on ; this fact. Any token that is a potential number but does not fit the ; actual number syntax defined below is a reserved token and has an ; implementation-dependent interpretation; an implementation may ; signal an error, quietly treat the token as a symbol, or take some ; other action. Programmers should avoid the use of such reserved ; tokens. (A symbol whose name looks like a reserved token can always ; be written using one or more escape characters.) ; ; ... ; ; A token is a potential number if it satisfies the following requirements: ; ; * It consists entirely of digits, signs (+ or -), ratio markers ; (/), decimal points (.), extension characters (^ or _), and ; number markers. (A number marker is a letter. Whether a letter ; may be treated as a number marker depends on context, but no ; letter that is adjacent to another letter may ever be treated ; as a number marker. Floating-point exponent markers are ; instances of number markers.) ; ; * It contains at least one digit. (Letters may be considered to ; be digits, depending on the value of *read-base*, but only in ; tokens containing no decimal points.) ; ; * It begins with a digit, sign, decimal point, or extension character. ; ; * It does not end with a sign. ; Below are examples. ; (defconst *a* ; '( ; ; Treat symbol package and name separately. Numeric strings need escaping. ; :|3| :|3G| :|33| |ACL2-PC|::|3| ; pkg is numeric except single letters ; ; :|3| :|3G| :|33| ACL2-PC::|3| ; ; ; None of the following strings gives a potential number in base 10: "no letter ; ; that is adjacent to another letter may ever be treated as a number marker". ; ; All these strings represent numbers in base 16. ; |ABC| |3BC| |+3BC| |-3BC| ; ;16 |ABC| |3BC| |+3BC| |-3BC| ; ;10 ABC 3BC +3BC -3BC ; ; ; Allegro gets this wrong, but ACL2 gets it right: potential number! ; |_345| ; ; |_345| ; SBCL 1.0.19, LispWorks 4.4.6, CMU CL 19e, CLISP 2.41, GCL 2.6.7 ; ; _345 ; [wrong] Allegro 8.0, CCL 1.2 ; ; ; Also not potential numbers, even in base 16: the first because of the decimal ; ; point (for base 16), the second because of the underscore, and the third ; ; because of consecutive letters that are not digits even in base 16. ; |A/B+.C| |3A3GG3| ; ; A/B+.C 3A3GG3 ; ; ; Potential number because letters are not consecutive. ; |3A3G3| ; ; |3A3G3| ; ; ; Not potential numbers: must begin with a digit, sign, decimal point, or ; ; extension character, and cannot end with a sign. ; |/12| |12+| |12C-| ; ; /12 12+ 12C- ; ; ; Must contain at least one digit. ; |+A| ; ;16 |+A| ; ;10 +A ; )) ; ; (defconst *b* ; ; ; This example is from CLtL2 with the following explanation given there: ; ; ; As examples, the following tokens are potential numbers, but they are not ; ; actually numbers as defined below, and so are reserved tokens. (They do ; ; indicate some interesting possibilities for future extensions.) So all ; ; should have verticle bars. ; ; '(|1B5000| ; oddly, GCL skips the vertical bars for this first one ; |777777Q| |1.7J| |-3/4+6.7J| |12/25/83| |27^19| |3^4/5| |6//7| |3.1.2.6| ; |^-43^| |3.141_592_653_589_793_238_4| |-3.7+2.6I-6.17J+19.6K|)) ; ; (defconst *c* ; ; ; This example is from CLtL2 with the following explanation given there: ; ; ; The following tokens are not potential numbers but are always treated as ; ; symbols: ; ; '(|/| |/5| |+| |1+| |1-| |FOO+| |AB.CD| |_| |^| |^/-|)) ; ; (defconst *d* ; ; ; From CLtL2, we see that we need |..| for each of the following in base 16 but ; ; for none of them in base 10. ; ; ; This example is from CLtL2 with the following explanation given there: ; ; ; The following tokens are potential numbers if the value of *read-base* is 16 ; ; (an abnormal situation), but they are always treated as symbols if the value ; ; of *read-base* is 10 (the usual value): ; ; '(|BAD-FACE| |25-DEC-83| |A/B| |FAD_CAFE| |F^|)) ; ; ; Now try check the answers: ; ; (set-print-base 16) ; (list *a* *b* *c* *d*) ; (set-print-base 10) ; (list *a* *b* *c* *d*) (declare (type string x)) #+acl2-loop-only (let* ((l (coerce x 'list)) (print-base ; Treat the base as 10 instead of 16 if there is a decimal point, as per the ; definition of potential number. (if (and (eql print-base 16) (member #\. l)) 10 print-base)) (numeric-chars (numeric-chars print-base)) (suspiciously-first-numeric-chars (suspiciously-first-numeric-chars print-base))) (or (null l) ; Keep the following conjunction in sync with potential-numberp. (and (or (member (car l) numeric-chars) (and (member (car l) suspiciously-first-numeric-chars) (intersectp (cdr l) numeric-chars))) (not (member (car (last l)) '(#\+ #\-))) (may-need-slashes1 (cdr l) nil (cons #\/ suspiciously-first-numeric-chars))) (some-slashable l))) #-acl2-loop-only (let ((len (length (the string x)))) (declare (type fixnum len)) ; fixnum by Section 15.1.1.2 of CL Hyperspec (when (eql print-base 16) (do ((i 0 (1+ i))) ((= i len) nil) (declare (type fixnum i)) (let ((ch (aref (the string x) i))) (declare (type character ch)) (cond ((eql ch #\.) (setq print-base 10) (return)))))) (or (int= len 0) (potential-numberp x len print-base) (do ((i 0 (1+ i))) ((= i len) nil) (declare (type fixnum i)) (let ((ch (char-code (aref (the string x) i)))) (declare (type fixnum ch)) (cond ((svref *slashable-array* ch) (return t)))))))) (defmacro may-need-slashes (x &optional (print-base '10)) ; This macro is deprecated; see needs-slashes instead. For backward ; compatibility (e.g., in community book books/misc/hons-help.lisp), the ; print-base is optional. For our own convenience, we allow that argument to ; be t in the normal case, where we take the print-base from the state. `(may-need-slashes-fn ,x ,print-base)) (defun needs-slashes (x state) (declare (xargs :guard (and (stringp x) (state-p state) (boundp-global 'print-escape state) (boundp-global 'print-readably state) (boundp-global 'print-base state)))) (and (or (f-get-global 'print-escape state) (f-get-global 'print-readably state)) (may-need-slashes-fn x (print-base)))) ; T-STACK #-acl2-loop-only (progn (defparameter *t-stack* (make-array$ 5)) (defparameter *t-stack-length* 0) ) (defun t-stack-length1 (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from t-stack-length1 *t-stack-length*))) (length (t-stack state-state))) (defun t-stack-length (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (state-p1 state-state))) (t-stack-length1 state-state)) (defun make-list-ac (n val ac) (declare (xargs :guard (and (integerp n) (>= n 0)))) (cond ((zp n) ac) (t (make-list-ac (1- n) val (cons val ac))))) #+acl2-loop-only (defmacro make-list (size &key initial-element) ":Doc-Section ACL2::ACL2-built-ins make a list of a given size~/ For a nonnegative integer ~c[size], ~c[(Make-list size)] is a list of elements of length ~c[size], each of which is initialized to the ~c[:initial-element] (which defaults to ~c[nil]).~/ ~c[Make-list] is a macro in ACL2, defined in terms of a tail recursive function ~c[make-list-ac] whose ~il[guard] requires ~c[size] to be a nonnegative integer. ~c[Make-list] is a Common Lisp function. See any Common Lisp documentation for more information.~/" `(make-list-ac ,size ,initial-element nil)) (defun extend-t-stack (n val state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (type (integer (0) *) n) (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'extend-t-stack (list n val)))) (let ((new-length (+ *t-stack-length* n))) (cond ((> new-length (length (the simple-vector *t-stack*))) (let ((new-length new-length)) (declare (type fixnum new-length)) (let ((new-array (make-array$ (* 2 new-length)))) (declare (simple-vector new-array)) (do ((i (1- *t-stack-length*) (1- i))) ((< i 0)) (declare (type fixnum i)) (setf (svref new-array i) (svref *t-stack* i))) (setq *t-stack* new-array))))) (let ((new-length new-length)) (declare (type fixnum new-length)) (do ((i *t-stack-length* (1+ i))) ((= i new-length)) (declare (type fixnum i)) (setf (svref *t-stack* i) val)) (setq *t-stack-length* new-length))) (return-from extend-t-stack state-state))) (update-t-stack (append (t-stack state-state) (make-list-ac n val nil)) state-state)) (encapsulate () (local (defthm true-listp-nthcdr (implies (true-listp lst) (true-listp (nthcdr n lst))) :rule-classes :type-prescription)) (verify-termination-boot-strap subseq-list) (local (defthm character-listp-first-n-ac (implies (and (character-listp x) (character-listp y) (<= n (length x))) (character-listp (first-n-ac n x y))))) (local (defthm len-nthcdr (implies (and (integerp n) (<= 0 n) (<= n (len x))) (equal (len (nthcdr n x)) (- (len x) n))))) (local (defthm character-listp-nthcdr (implies (character-listp x) (character-listp (nthcdr n x))))) (verify-termination-boot-strap subseq)) ; The following constants and the next two functions, pathname-os-to-unix and ; pathname-unix-to-os, support the use of Unix-style filenames in ACL2 as ; described in the Essay on Pathnames in interface-raw.lisp. ; The following constants represent our decision to use Unix-style pathnames ; within ACL2. See the Essay on Pathnames in interface-raw.lisp. (defconst *directory-separator* #\/) (defconst *directory-separator-string* (string *directory-separator*)) (defmacro os-er (os fnname) `(illegal ,fnname "The case where (os (w state)) is ~x0 has not been handled by the ~ ACL2 implementors for the function ~x1. Please inform them of this ~ problem." (list (cons #\0 ,os) (cons #\1 ,fnname)))) (defun os (wrld) (declare (xargs :guard (plist-worldp wrld))) (global-val 'operating-system wrld)) (local (in-theory (enable boundp-global1))) (verify-guards w) (verify-guards hons-enabledp) (verify-guards set-serialize-character) (defun mswindows-drive1 (filename) (declare (xargs :mode :program)) (let ((pos-colon (position #\: filename)) (pos-sep (position *directory-separator* filename))) (cond (pos-colon (cond ((eql pos-sep (1+ pos-colon)) ; In Windows, it appears that the value returned by truename can start with ; (for example) "C:/" or "c:/" depending on whether "c" is capitalized in the ; input to truename. Indeed, quoting ; http://msdn.microsoft.com/en-us/library/windows/desktop/aa365247(v=vs.85).aspx: ; Volume designators (drive letters) are similarly case-insensitive. For ; example, "D:\" and "d:\" refer to the same volume. ; So we take responsibility for canonicalizing, here. (string-upcase (subseq filename 0 pos-sep))) (t (illegal 'mswindows-drive1 "Implementation error: Unable to ~ compute mswindows-drive for ~ cbd:~%~x0~%(Implementor should see ~ function mswindows-drive)," (list (cons #\0 filename)))))) (t nil)))) (defun mswindows-drive (filename state) ; We get the drive from filename if possible, else from cbd. (declare (xargs :mode :program)) (or (and (eq (os (w state)) :mswindows) (or (and filename (mswindows-drive1 filename)) (let ((cbd (f-get-global 'connected-book-directory state))) (cond (cbd (mswindows-drive1 cbd)) (t (illegal 'mswindows-drive "Implementation error: Cbd is nil when ~ attempting to set mswindows-drive." nil)))))) "")) (defun pathname-os-to-unix (str os state) ; This function takes a pathname string in the host OS syntax and converts it ; to Unix syntax. (declare (xargs :mode :program)) (if (equal str "") str (case os (:unix str) ((:apple :mswindows) (let ((sep (if (eq os :apple) #\: #\\))) (let* ((relative-p-apple ; relevant only for apple (eql (char str 0) sep)) (str0 (substitute *directory-separator* sep (cond ((eq os :mswindows) str) (relative-p-apple (subseq str 1 (length str))) (t str))))) (cond ((and (eq os :apple) (not relative-p-apple)) (string-append *directory-separator-string* str0)) ((and (eq os :mswindows) (eql (char str0 0) *directory-separator*)) ; Warning: Do not append the drive if there is already a drive present. We ; rely on this in LP, where we initialize state global 'system-books-dir ; using unix-full-pathname (which calls pathname-os-to-unix) based on ; environment variable ACL2_SYSTEM_BOOKS, which might already have a drive that ; differs from that of the user. (string-append (mswindows-drive nil state) str0)) (t str0))))) (otherwise (os-er os 'pathname-os-to-unix))))) #+(and (not acl2-loop-only) ccl) (defun ccl-at-least-1-3-p () (and (boundp 'ccl::*openmcl-major-version*) (boundp 'ccl::*openmcl-minor-version*) (if (eql (symbol-value 'ccl::*openmcl-major-version*) 1) (> (symbol-value 'ccl::*openmcl-minor-version*) 2) (> (symbol-value 'ccl::*openmcl-major-version*) 1)))) (defun pathname-unix-to-os (str state) ; This function takes a Unix-style pathname string and converts it to a ; filename in the host OS. In the case of :mswindows, the "Unix-style" ; filename may or may not start with the drive, but the result definitely does. (declare (xargs :mode :program)) #+(and (not acl2-loop-only) ccl mswindows) ; We believe that CCL 1.2 traffics in Unix-style pathnames, so it would be a ; mistake to convert them to use #\\, because then (for example) probe-file may ; fail. However, we will allow Windows-style pathnames for CCL Versions 1.3 ; and beyond, based on the following quote from ; http://trac.clozure.com/ccl/wiki/WindowsNotes (4/30/09): ; Windows pathnames can use either forward-slash or backward-slash characters ; as directory separators. As of the 1.3 release, CCL should handle ; namestrings which use either forward- or backward-slashes; some prereleases ; and release-candidates generally had difficulty with backslashes. (when (not (ccl-at-least-1-3-p)) (return-from pathname-unix-to-os str)) (if (equal str "") str (let ((os (os (w state)))) (case os (:unix str) ((:apple :mswindows) (let ((sep (if (eq os :apple) #\: #\\))) (if (position sep str) (illegal 'pathname-unix-to-os "Unable to convert pathname ~p0 for OS ~p1 because ~ character ~p2 occurs in that pathname string at position ~p3." (list (cons #\0 str) (cons #\1 os) (cons #\2 sep) (cons #\3 (position sep str)))) (let* ((sep-is-first (eql (char str 0) *directory-separator*)) (str0 (substitute sep *directory-separator* (if (and (eq os :apple) sep-is-first) (subseq str 1 (length str)) str)))) (if (eq os :apple) (if sep-is-first str0 (string-append ":" str0)) (if sep-is-first (string-append (mswindows-drive nil state) str0) str0)))))) (otherwise (os-er os 'pathname-unix-to-os)))))) (defun shrink-t-stack (n state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (type (integer 0 *) n) (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'shrink-t-stack (list n)))) (let ((old *t-stack-length*) (new (max 0 (- *t-stack-length* n)))) (declare (type fixnum old new)) (setq *t-stack-length* new) (do ((i new (1+ i))) ((= i old)) (declare (type fixnum i)) (setf (svref *t-stack* i) nil))) (return-from shrink-t-stack *the-live-state*))) (update-t-stack (first-n-ac (max 0 (- (length (t-stack state-state)) n)) (t-stack state-state) nil) state-state)) (defun aref-t-stack (i state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (declare (type fixnum i)) (declare (xargs :guard (and (integerp i) (>= i 0) (state-p1 state-state) (< i (t-stack-length1 state-state))))) (cond #-acl2-loop-only ((live-state-p state-state) (svref *t-stack* (the fixnum i))) (t (nth i (t-stack state-state))))) (defun aset-t-stack (i val state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (declare (type fixnum i)) (declare (xargs :guard (and (integerp i) (>= i 0) (state-p1 state-state) (< i (t-stack-length1 state-state))))) (cond #-acl2-loop-only ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'aset-t-stack (list i val)))) (setf (svref *t-stack* (the fixnum i)) val) state-state) (t (update-t-stack (update-nth i val (t-stack state-state)) state-state)))) ; 32-bit-integer-stack #-acl2-loop-only (progn (defparameter *32-bit-integer-stack* (make-array$ 5 :element-type '(signed-byte 32))) (defparameter *32-bit-integer-stack-length* 0) ) (defun 32-bit-integer-stack-length1 (state-state) (declare (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from 32-bit-integer-stack-length1 *32-bit-integer-stack-length*))) (length (32-bit-integer-stack state-state))) (defun 32-bit-integer-stack-length (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (state-p1 state-state))) (32-bit-integer-stack-length1 state-state)) (defun extend-32-bit-integer-stack (n val state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (xargs :guard (and (32-bit-integerp val) (integerp n) (> n 0) (state-p1 state-state)))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'extend-32-bit-integer-stack (list n val)))) (let ((new-length (+ *32-bit-integer-stack-length* n))) (cond ((> new-length (length (the (array (signed-byte 32) (*)) *32-bit-integer-stack*))) (let ((new-length new-length)) (declare (type fixnum new-length)) (let ((new-array (make-array$ (* 2 new-length) :element-type '(signed-byte 32)))) (declare (type (array (signed-byte 32) (*)) new-array)) (do ((i (1- *32-bit-integer-stack-length*) (1- i))) ((< i 0)) (declare (type fixnum i)) (setf (aref (the (array (signed-byte 32) (*)) new-array) i) (aref (the (array (signed-byte 32) (*)) *32-bit-integer-stack*) i))) (setq *32-bit-integer-stack* new-array))))) (let ((new-length new-length)) (declare (type fixnum new-length)) (do ((i *32-bit-integer-stack-length* (1+ i))) ((= i new-length)) (declare (type fixnum i)) (setf (aref (the (array (signed-byte 32) (*)) *32-bit-integer-stack*) i) val)) (setq *32-bit-integer-stack-length* new-length))) (return-from extend-32-bit-integer-stack state-state))) (update-32-bit-integer-stack (append (32-bit-integer-stack state-state) (make-list-ac n val nil)) state-state)) (defun shrink-32-bit-integer-stack (n state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. (declare (type (integer 0 *) n) (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'shrink-32-bit-integer-stack (list n)))) (let ((old *32-bit-integer-stack-length*) (new (max 0 (- *32-bit-integer-stack-length* n)))) (declare (type fixnum old new)) (setq *32-bit-integer-stack-length* new) (do ((i new (1+ i))) ((= i old)) (declare (type fixnum i)) (setf (aref (the (array (signed-byte 32) (*)) *32-bit-integer-stack*) i) 0))) (return-from shrink-32-bit-integer-stack state-state))) (update-32-bit-integer-stack (first-n-ac (max 0 (- (length (32-bit-integer-stack state-state)) n)) (32-bit-integer-stack state-state) nil) state-state)) (defun aref-32-bit-integer-stack (i state-state) #-acl2-loop-only (declare (type fixnum i)) (declare (xargs :guard (and (integerp i) (>= i 0) (state-p1 state-state) (< i (32-bit-integer-stack-length1 state-state))))) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (the (signed-byte 32) (cond ((live-state-p state-state) (the (signed-byte 32) (aref (the (array (signed-byte 32) (*)) *32-bit-integer-stack*) (the fixnum i)))) (t (nth i (32-bit-integer-stack state-state))))) #+acl2-loop-only (nth i (32-bit-integer-stack state-state))) (defun aset-32-bit-integer-stack (i val state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (declare (type fixnum i)) (declare (type (signed-byte 32) val)) (declare (xargs :guard (and (integerp i) (>= i 0) (state-p1 state-state) (< i (32-bit-integer-stack-length1 state-state)) (32-bit-integerp val)))) (cond #-acl2-loop-only ((live-state-p state-state) (cond (*wormholep* (wormhole-er 'aset-32-bit-integer-stack (list i val)))) (setf (aref (the (array (signed-byte 32) (*)) *32-bit-integer-stack*) (the fixnum i)) (the (signed-byte 32) val)) state-state) (t (update-32-bit-integer-stack (update-nth i val (32-bit-integer-stack state-state)) state-state)))) (defmacro f-big-clock-negative-p (st) #-acl2-loop-only (let ((s (gensym))) `(let ((,s ,st)) (cond ((live-state-p ,s) nil) (t (big-clock-negative-p ,s))))) #+acl2-loop-only (list 'big-clock-negative-p st)) (defmacro f-decrement-big-clock (st) #-acl2-loop-only (let ((s (gensym))) `(let ((,s ,st)) (cond ((live-state-p ,s) ; Because there is no way to get the big-clock-entry for ; *the-live-state* we do not have to prevent the field from changing ; when *wormholep* is true. *the-live-state*) (t (decrement-big-clock ,s))))) #+acl2-loop-only (list 'decrement-big-clock st)) ; ??? (v. 1.8) I think it would be simpler to check for "zero-ness" rather ; than negativity, using zp. For now I won't touch the following or ; related functions. (defun big-clock-negative-p (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; big-clock-negative-p plays a crucial role in the termination of ev, ; translate1, and rewrite. The justification for big-clock-negative-p ; never returning t when given *the-live-state* be found in a comment ; on ld, where it is explained that a (constructive) existential ; quantifier is used in semantics of a top-level interaction with ld. ; Any ld interaction that completes will have called ; big-clock-decrement at most a finite number of times. The number of ; these calls will provide an appropriate value for the ; big-clock-entry for that interaction. (declare (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from big-clock-negative-p nil))) (< (big-clock-entry state-state) 0)) (defun decrement-big-clock (state-state) ; Wart: We use state-state instead of state because of a bootstrap problem. ; decrement-big-clock is the one function which is permitted to ; violate the rule that any function which is passed a state and ; modifies it must return it. A function that is passed state may ; pass one down the result of apply decrement-big-clock to the given ; state. decrement-big-clock is exempted from the requirement because ; there are means internal or external to ACL2 for perceiving the ; current big-clock value. (declare (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) ; Because there is no way to get the big-clock-entry for ; *the-live-state* we do not have to prevent the field from changing ; when *wormholep* is true. (return-from decrement-big-clock *the-live-state*))) (update-big-clock-entry (1- (big-clock-entry state-state)) state-state)) (defun list-all-package-names (state-state) (declare (xargs :guard (state-p1 state-state))) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (cond ((live-state-p state-state) (return-from list-all-package-names (mv (mapcar (function package-name) (list-all-packages)) state-state)))) (mv (car (list-all-package-names-lst state-state)) (update-list-all-package-names-lst (cdr (list-all-package-names-lst state-state)) state-state))) (defun user-stobj-alist (state-state) (declare (xargs :guard (state-p1 state-state))) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (cond ((live-state-p state-state) (return-from user-stobj-alist *user-stobj-alist*))) (user-stobj-alist1 state-state)) (defun update-user-stobj-alist (x state-state) (declare (xargs :guard (and (symbol-alistp x) (state-p1 state-state)))) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (cond ((live-state-p state-state) (setq *user-stobj-alist* x) (return-from update-user-stobj-alist *the-live-state*))) (update-user-stobj-alist1 x state-state)) (defun power-eval (l b) (declare (xargs :guard (and (rationalp b) (rational-listp l)))) (if (endp l) 0 (+ (car l) (* b (power-eval (cdr l) b))))) #-acl2-loop-only (defun-one-output idate () (power-eval (let (ans) (do ((i 1 (1+ i)) (tl (multiple-value-list (get-decoded-time)) (cdr tl))) ((> i 6) (reverse ans)) (push (cond ((= i 6) (- (car tl) 1900)) (t (car tl))) ans)) (reverse ans)) 100)) (defun read-idate (state-state) (declare (xargs :guard (state-p1 state-state))) ; Wart: We use state-state instead of state because of a bootstrap problem. #-acl2-loop-only (cond ((live-state-p state-state) ; Because there is no way for the user to know what the idates of the original ; state were, there is no way to tell whether we changed them. So we permit ; read-idate to work even when *wormholep* is non-nil. (return-from read-idate (mv (idate) state-state)))) (mv (cond ((null (idates state-state)) 0) (t (car (idates state-state)))) (update-idates (cdr (idates state-state)) state-state))) #-acl2-loop-only (defun get-internal-time () (if (f-get-global 'get-internal-time-as-realtime *the-live-state*) (get-internal-real-time) (get-internal-run-time))) (defdoc get-internal-time ":Doc-Section Miscellaneous runtime vs. realtime in ACL2 timings~/ The ACL2 system provides utilities that deal with elapsed time. The most visible of these is in the time summaries printed when completing evaluation of ~il[events]. For others, ~pl[with-prover-time-limit], ~pl[read-run-time], ~pl[time-tracker], ~pl[time-tracker-tau], and ~pl[pstack]. By default, these utilities all use an underlying notion of run time provided by the host Common Lisp implementation: specifically, Common Lisp function ~c[get-internal-run-time]. However, Common Lisp also provides function ~c[get-internal-run-time], which returns the real time (wall clock time). While the latter is specified to measure elapsed time, the former is left to the implementation, which might well only measure time spent in the Lisp process. Consider the following example, which is a bit arcane but basically sleeps for 2 seconds. ~bv[] (defttag t) ; to allow sys-call (make-event (prog2$ (sys-call \"sleep\" '(\"2\")) (value '(value-triple nil)))) ~ev[] A typical time summary might be as follows, drastically under-reporting the elapsed time. ~bv[] Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) ~ev[] However, you can instruct ACL2 to switch to using elapsed time (run time), in summaries and elsewhere, by evaluating the following form. ~bv[] (assign get-internal-time-as-realtime t) ~ev[] To return to using runtime: ~bv[] (assign get-internal-time-as-realtime nil) ~ev[] While the above example is rather silly, the issue becomes significant in time summaries for proofs that call out to external tools (~pl[sys-call] and ~pl[clause-processor]). Note that a function ~c[get-internal-time] is defined in raw Lisp but is not available inside the ACL2 loop. However, the expression ~c[(read-run-time state)] provides an interface to this function that is available inside the ACL2 loop; ~pl[read-run-time]. We are open to changing the default to elapsed wall-clock time (realtime), and may do so in future ACL2 releases.~/~/") (defun read-run-time (state-state) ":Doc-Section ACL2::ACL2-built-ins read elapsed runtime~/ By default, ~c[(read-run-time state)] returns ~c[(mv runtime state)], where runtime is the elapsed runtime in seconds since the start of the current ACL2 session and ~c[state] is the resulting ACL2 ~il[state]. But ~c[read-run-time] can be made to return elapsed realtime (wall clock time) instead; ~pl[get-internal-time].~/ The logical definition probably won't concern many users, but for completeness, we say a word about it here. That definition uses the function ~c[read-acl2-oracle], which modifies state by popping the value to return from its acl2-oracle field.~/" (declare (xargs :guard (state-p1 state-state))) ; Wart: We use state-state instead of state because of a bootstrap problem. ; See also read-acl2-oracle. #-acl2-loop-only (cond ((live-state-p state-state) ; Because there is no way for the user to know the acl2-oracle of the original ; state, there is no way to tell whether we changed it. So we permit ; read-run-time to work even when *wormholep* is non-nil. (return-from read-run-time (mv (/ (get-internal-time) internal-time-units-per-second) state-state)))) (mv (cond ((or (null (acl2-oracle state-state)) (not (rationalp (car (acl2-oracle state-state))))) 0) (t (car (acl2-oracle state-state)))) (update-acl2-oracle (cdr (acl2-oracle state-state)) state-state))) #-acl2-loop-only (defparameter *next-acl2-oracle-value* nil) (defun read-acl2-oracle (state-state) ; Keep in sync with #+acl2-par read-acl2-oracle@par. (declare (xargs :guard (state-p1 state-state))) ; Wart: We use state-state instead of state because of a bootstrap problem. ; See also read-run-time. #-acl2-loop-only (cond ((live-state-p state-state) (return-from read-acl2-oracle (let ((val *next-acl2-oracle-value*)) (setq *next-acl2-oracle-value* nil) (mv nil val state-state))))) (mv (null (acl2-oracle state-state)) (car (acl2-oracle state-state)) (update-acl2-oracle (cdr (acl2-oracle state-state)) state-state))) #+acl2-par (defun read-acl2-oracle@par (state-state) ; Keep in sync with read-acl2-oracle. ; Note that this function may make it possible to evaluate (equal X X) and ; return nil, for a suitable term X. Specifically, it may be the case that the ; term (equal (read-acl2-oracle@par state) (read-acl2-oracle@par state)) can ; evaluate to nil. More likely, something like ; (equal (read-acl2-oracle@par state) ; (prog2$ (read-acl2-oracle@par state))) ; could evaluate to nil, if sets *next-acl2-oracle-value* under the ; hood. However, we are willing to live with such low-likelihood risks in ; ACL2(p). (declare (xargs :guard (state-p1 state-state))) #-acl2-loop-only (cond ((live-state-p state-state) (return-from read-acl2-oracle@par (let ((val *next-acl2-oracle-value*)) (setq *next-acl2-oracle-value* nil) (mv nil val state-state))))) (mv (null (acl2-oracle state-state)) (car (acl2-oracle state-state)))) #-acl2-par (defun read-acl2-oracle@par (state-state) ; We have included read-acl2-oracle@par in *super-defun-wart-table*, in support ; of ACL2(p). But in order for ACL2(p) and ACL2 to be logically compatible, a ; defconst should have the same value in #+acl2-par as in #-acl2-par; so ; read-acl2-oracle@par is in *super-defun-wart-table* for #-acl2-par too, not ; just #+acl2-par. ; Because of that, if the function read-acl2-oracle@par were only defined in ; #+acl2-par, then a normal ACL2 user could define read-acl2-oracle@par and ; take advantage of such special treatment, which we can imagine is ; problematic. Rather than think hard about whether we can get away with that, ; we eliminate such a user option by defining this function in #-acl2-par. (declare (xargs :guard (state-p1 state-state)) (ignore state-state)) (mv (er hard? 'read-acl2-oracle@par "The function symbol ~x0 is reserved but may not be executed." 'read-acl2-oracle@par) nil)) (defun getenv$ (str state) ":Doc-Section ACL2::ACL2-built-ins read an environment variable~/ ~c[(Getenv$ str state)], where ~c[str] is a string, reads the value of environment variable ~c[str], returning a value of ~c[nil] if none is found or if the read fails. The logical story is that ~c[getenv$] reads its value from the ~c[oracle] field of the ACL2 ~ilc[state]. The return value is thus a triple of the form ~c[(mv erp val state)], where ~c[erp] will always be ~c[nil] in practice, and logically, ~c[val] is the top of the acl2-oracle field of the state and the returned state has the updated (popped) acl2-oracle. ~bv[] Example: (getenv$ \"PWD\" state) ==> (mv nil \"/u/joe/work\" state) ~ev[] Also ~pl[setenv$].~/~/" (declare (xargs :stobjs state :guard (stringp str))) #+acl2-loop-only (declare (ignore str)) #-acl2-loop-only (when (live-state-p state) (return-from getenv$ (value (and (stringp str) (getenv$-raw str))))) (read-acl2-oracle state)) (defun setenv$ (str val) ":Doc-Section ACL2::ACL2-built-ins set an environment variable~/ ~c[(Setenv$ str val)], where ~c[str] and ~c[val] are strings, sets the environment variable ~c[str] to have value ~c[val], for subsequent read by ~c[getenv$] (~pl[getenv$]), and returns ~c[nil]. Or, if this operation is not implemented for the host Common Lisp, an error will occur. ~bv[] Example: (setenv$ \"FOO\" \"BAR\") ~ev[]~/ It may be surprising that ~c[setenv$] returns ~c[nil]; indeed, it neither takes nor returns the ACL2 ~il[state]. The reason is that ~ilc[getenv$] takes responsibility for trafficking in ~il[state]; it is defined in the logic using the function ~c[read-acl2-oracle], which (again, in the logic) does modify state, by popping an entry from its acl2-oracle field. ~il[getenv$].~/" (declare (xargs :guard (and (stringp str) (stringp val)))) #+acl2-loop-only (declare (ignore str val)) #-acl2-loop-only (when (and (stringp str) (stringp val)) (or #+cmu (and (boundp ext::*environment-list*) (let* ((key (intern str :keyword)) (pair (cdr (assoc-eq key ext::*environment-list*)))) (cond (pair (setf (cdr pair) val)) (t (push (cons key val) ext::*environment-list*))))) ; #+sbcl ; The following is the best we could come up with for SBCL, but it ; didn't work. ; (nconc (posix-environ) (list (format nil "~a=~a" str val))) #+allegro (setf (sys::getenv str) val) #+clisp (setf (ext::getenv str) val) #+(or gcl allegro lispworks ccl sbcl clisp) (let ((fn #+gcl 'si::setenv #+lispworks 'hcl::setenv #+ccl 'ccl::setenv)) (and (fboundp fn) (funcall fn str val))) (error "Setenv$ is not available for this host Common Lisp. ~%~ If you know a way to provide this functionality for ~%~ this host Common Lisp, please contact the ACL2 ~%~ implementors."))) nil) (defun random$ (limit state) ":Doc-Section ACL2::ACL2-built-ins obtain a random value~/ ~bv[] Example: (random$ 10 state) ==> (mv 7 ) ~ev[] ~c[(Random$ limit state)], where ~c[limit] is a positive integer, returns a random non-negative integer together with a new ~ilc[state]. Logically, it simply returns the first element of a list that is a field of the ACL2 ~ilc[state], called the ~c[acl2-oracle], together with the new state resulting from removing that element from that list. (Except, if that element is not in range as specified above, then 0 is returned.) However, ~c[random$] actually invokes a Common Lisp function to choose the integer returned. Quoting from the Common Lisp HyperSpec(TM), ~url[http://www.lispworks.com/documentation/HyperSpec/Front]: ``An approximately uniform choice distribution is used... each of the possible results occurs with (approximate) probability 1/limit.'' Consider enabling rules ~c[natp-random$] and ~c[random$-linear] if you want to reason about ~c[random$].~/~/" (declare (type (integer 1 *) limit) (xargs :stobjs state)) #-acl2-loop-only (when (live-state-p state) (return-from random$ (mv (random limit) state))) (mv-let (erp val state) (read-acl2-oracle state) (mv (cond ((and (null erp) (natp val) (< val limit)) val) (t 0)) state))) (defthm natp-random$ (natp (car (random$ n state))) :rule-classes :type-prescription) (defthm random$-linear (and (<= 0 (car (random$ n state))) (implies (posp n) (< (car (random$ n state)) n))) :rule-classes :linear) (in-theory (disable random$ ; We keep the following rules disabled because it seems sad to pay the ; potential performance penalty (as they are hung on car) given how rarely they ; are likely to be used. natp-random$ random$-linear)) ; System calls #-acl2-loop-only (defvar *last-sys-call-status* 0) (defun sys-call (command-string args) ":Doc-Section ACL2::ACL2-built-ins make a system call to the host operating system~/ ~bv[] Example Forms: (sys-call \"cp\" '(\"foo.lisp\" \"foo-copied.lisp\")) (prog2$ (sys-call \"cp\" '(\"foo.lisp\" \"foo-copied.lisp\")) (sys-call-status state)) ~ev[] The first argument of ~c[sys-call] is a command for the host operating system, and the second argument is a list of strings that are the arguments for that command. In GCL and perhaps some other lisps, you can put the arguments with the command; but this is not the case, for example, in Allegro CL running on Linux. For a related utility, ~pl[sys-call+]. The use of ~ilc[prog2$] above is optional, but illustrates a typical sort of use when one wishes to get the return status. ~l[sys-call-status].~/ ~bv[] General Form: (sys-call cmd args) ~ev[] This function logically returns ~c[nil]. However, it makes the indicated call to the host operating system, as described above, using a function supplied ``under the hood'' by the underlying Lisp system. On occasions where one wishes to obtain the numeric status returned by the host operating system (or more precisely, by the Lisp function under the hood that passes the system call to the host operating system), one may do so; ~pl[sys-call-status]. The status value is the value returned by that Lisp function, which may well be the same numeric value returned by the host operating system for the underlying system call. Note that ~c[sys-call] does not touch the ACL2 ~ilc[state]; however, ~ilc[sys-call-status] updates the ~c[acl2-oracle] field of the ~c[state]. Be careful if you use ~c[sys-call]! It can be used for example to overwrite files, or worse! We view a use of ~c[sys-call] as a call to the operating system that is made outside ACL2. The following example from Bob Boyer shows how to use ~c[sys-call] to execute, in effect, arbitrary Lisp forms. ACL2 provides a ``trust tag'' mechanism that requires execution of a ~ilc[defttag] form before you can use ~c[sys-call]; ~pl[defttag]. (Note: The setting of the raw Lisp variable ~c[*features*] below is just to illustrate that any such mischief is possible. Normally ~c[*features*] is a list with more than a few elements.) ~bv[] % cat foo print *0x85d2064=0x838E920 detach q % acl2 ... boilerplate deleted ACL2 !>(sys-call \"gdb -p $PPID -w < foo >& /dev/null \" nil) NIL ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ACL2>*features* (:AKCL-SET-MV) ACL2> ~ev[] Finally, we make a comment about output redirection, which also applies to other related features that one may expect of a shell. ~c[Sys-call] does not directly support output redirection. If you want to run a program, ~c[P], and redirect its output, one option is to create a wrapper script, ~c[W] to call instead. Thus ~c[W] might be a shell script containing the line: ~bv[] P $* >& foo.out ~ev[] For a different, more direct solution, ~pl[sys-call+]." (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore command-string args)) #-acl2-loop-only (let ((rslt (system-call command-string args))) (progn (setq *last-sys-call-status* rslt) nil)) #+acl2-loop-only nil) (defun sys-call-status (state) ":Doc-Section ACL2::ACL2-built-ins exit status from the preceding system call~/ This function returns two values, ~c[(mv status state)]. The first is the status resulting from the most recent invocation of function ~c[sys-call]; ~pl[sys-call]. The second is the ACL2 ~ilc[state] object, which is also the input to this function.~/ The function ~ilc[sys-call] makes a system call to the host operating system using a function supplied ``under the hood'' by the underlying Lisp system. The status value is the value returned by that Lisp function, which may well be the numeric value returned by the host operating system for the underlying system call. For more information, ~pl[sys-call].~/" (declare (xargs :stobjs state)) #-acl2-loop-only (when (live-state-p state) (return-from sys-call-status (mv *last-sys-call-status* state))) (mv-let (erp val state) (read-acl2-oracle state) (declare (ignore erp)) (mv val state))) #-acl2-loop-only (defun read-file-by-lines (file &optional delete-after-reading) (let ((acc nil) (eof '(nil)) missing-newline-p) (with-open-file (s file :direction :input) (loop (multiple-value-bind (line temp) (read-line s nil eof) (cond ((eq line eof) (return acc)) (t (setq missing-newline-p temp) (setq acc (if acc (concatenate 'string acc (string #\Newline) line) line))))))) (when delete-after-reading (delete-file file)) (if missing-newline-p acc (concatenate 'string acc (string #\Newline))))) #-acl2-loop-only (defun system-call+ (string arguments) ; Warning: Keep this in sync with system-call. (let* (exit-code ; assigned below #+(or gcl clisp) (tmp-file (format nil "~a/tmp~s" (or (f-get-global 'tmp-dir *the-live-state*) "/tmp") (getpid$))) no-error (output-string (our-ignore-errors (prog1 #+gcl ; does wildcard expansion (progn (setq exit-code (si::system (let ((result string)) (dolist (x arguments) (setq result (concatenate 'string result " " x))) (concatenate 'string result " > " tmp-file)))) (read-file-by-lines tmp-file t)) #+lispworks ; does wildcard expansion (see comment below) (with-output-to-string (s) (setq exit-code (system::call-system-showing-output ; It was tempting to use (cons string arguments). This would cause the given ; command, string, to be applied to the given arguments, without involving the ; shell. But then a command such as "ls" would not work; one would have to ; provide a string such as "/bin/ls". So instead of using a list here, we use ; a string, which according to the LispWorks manual will invoke the shell, ; which will find commands (presumably including built-ins and also using the ; user's path). (let ((result string)) (dolist (x arguments) (setq result (concatenate 'string result " " x))) result) :output-stream s :prefix "" :show-cmd nil :kill-process-on-abort t)) #+windows ; process is returned above, not exit code (setq exit-code nil)) #+allegro ; does wildcard expansion (multiple-value-bind (stdout-lines stderr-lines exit-status) (excl.osi::command-output (let ((result string)) (dolist (x arguments) (setq result (concatenate 'string result " " x))) result)) (declare (ignore stderr-lines)) (setq exit-code exit-status) (let ((acc nil)) (loop for line in stdout-lines do (setq acc (if acc (concatenate 'string acc (string #\Newline) line) line))) acc)) #+cmu (with-output-to-string (s) (setq exit-code (let (temp) (if (ignore-errors (progn (setq temp (ext:process-exit-code (common-lisp-user::run-program string arguments :output s))) 1)) temp 1)))) #+sbcl (with-output-to-string (s) (setq exit-code (let (temp) (if (ignore-errors (progn (setq temp (sb-ext:process-exit-code (sb-ext:run-program string arguments :output s :search t))) 1)) temp 1)))) #+clisp (progn (setq exit-code (or (ext:run-program string :arguments arguments :output tmp-file) 0)) (read-file-by-lines tmp-file t)) #+ccl (with-output-to-string (s) (setq exit-code (let* ((proc (ccl::run-program string arguments :output s :wait t)) (status (multiple-value-list (ccl::external-process-status proc)))) (if (not (and (consp status) (eq (car status) :EXITED) (consp (cdr status)) (integerp (cadr status)))) 1 ; just some non-zero exit code here (cadr status))))) #-(or gcl lispworks allegro cmu sbcl clisp ccl) (declare (ignore string arguments)) #-(or gcl lispworks allegro cmu sbcl clisp ccl) (error "SYSTEM-CALL is not yet defined in this Lisp.") (setq no-error t))))) (values (cond ((integerp exit-code) exit-code) ((null exit-code) (if no-error 0 1)) (t (format t "WARNING: System-call produced non-integer, ~ non-nil exit code:~%~a~%" exit-code) 0)) (if (stringp output-string) output-string "")))) (encapsulate () ; Before Version_2.9.3, len-update-nth had the form of the local lemma below. ; It turns out that an easy way to prove the improved version below, ; contributed by Jared Davis, is to prove the old version first as a lemma: (local (defthm len-update-nth-lemma (implies (< (nfix n) (len x)) (equal (len (update-nth n val x)) (len x))))) (defthm len-update-nth (equal (len (update-nth n val x)) (max (1+ (nfix n)) (len x))))) (defthm update-acl2-oracle-preserves-state-p1 (implies (and (state-p1 state) (true-listp x)) (state-p1 (update-acl2-oracle x state))) :hints (("Goal" :in-theory (enable state-p1)))) (in-theory (disable update-acl2-oracle)) (defun sys-call+ (command-string args state) ":Doc-Section ACL2::ACL2-built-ins make a system call to the host OS, returning status and output~/ ~bv[] Example Form: ; The following returns (mv nil s state), where s is the standard output ; from the command: ls -l ./ (sys-call+ \"ls\" '(\"-l\" \"./\") state) General Form: (sys-call+ cmd args state) ~ev[] where ~c[cmd] is a command to the host operating system and ~c[args] is a list of strings. Also ~pl[sys-call]; but there are two differences between ~ilc[sys-call] and ~c[sys-call+]. First, the latter takes an extra argument, ~c[state]. Second, while ~c[sys-call] returns ~c[nil], ~c[sys-call+] returns three values: a so-called error triple (~pl[error-triples]), ~c[(mv erp val state)]. While execution returns values as described just below, further below we explain the logical return values. In the following, please keep in mind that the exact behavior depends on the platform; the description is only a guide. For example, on some platforms ~c[erp] might always be ~c[nil], even if in the error case, and ~c[val] might or might not include error output. (For details, look at the ACL2 source code for function ~c[system-call+], whose output is converted by replacing an ~c[erp] of ~c[nil] by 0.) ~bq[] ~c[Erp] is either ~c[nil] or a non-zero integer. Normally, ~c[nil] indicates that the command ran without error, and otherwise ~c[erp] is the exit status. ~c[Val] is a string, typically the output generated by the call of ~c[cmd]. ~c[State] is an ACL2 ~il[state].~eq[] While the description above pertains to the values returned by executing ~c[sys-call+], the logical values are as follows. For a discussion of the ~c[acl2-oracle] field of an ACL2 state, ~pl[state]. ~bq[] ~c[Erp] is the first element of the ~c[acl2-oracle] of the input state if that element is a nonzero integer, and otherwise is ~c[nil]. ~c[Val] is the second element of the ~c[acl2-oracle] of the input state if it is a string, else the empty string, ~c[\"\"]. ~c[State] is the result of popping the ~c[acl2-oracle] field twice from the input state.~eq[] Note that unlike ~ilc[sys-call], a call of ~ilc[sys-call+] has no effect on subsequent calls of ~ilc[sys-call-status]. As is the case for ~c[sys-call], a trust tag is required to call ~c[sys-call+]. For discussion of this and more, ~pl[sys-call].~/~/" (declare (xargs :stobjs state)) #+acl2-loop-only (declare (ignore command-string args)) #+acl2-loop-only (mv-let (erp1 erp state) (read-acl2-oracle state) (declare (ignore erp1)) (mv-let (erp2 val state) (read-acl2-oracle state) (declare (ignore erp2)) (mv (and (integerp erp) (not (eql 0 erp)) erp) (if (stringp val) val "") state))) #-acl2-loop-only (multiple-value-bind (status rslt) (system-call+ command-string args) (mv (if (eql status 0) nil status) rslt state))) ; End of system calls ; Time: idate, run-time, and timers. ; Time is a very nonapplicative thing. What is it doing in an ; applicative programming language and verification system? Formally, ; read time and cpu time are simply components of state which are ; lists of numbers about which we say nothing, not even that they are ; ascending. In actual practice, the numbers that we provide ; correspond to the universal time and the cpu time at the moment that ; read-idate and read-run-time are called. ; We provide a mechanism for the user to report real time and to keep ; track of and report cpu time, but we do not let the user do anything ; with times except print them, so as to keep computations entirely ; deterministic for read-book. We prohibit the user from accessing ; the internal timing subroutines and state variables by putting them ; on untouchables. (If we ever implement a file system, then of ; course the nondeterminism of read-book will be shattered because a ; user could check what sort of io was being generated.) ; The user can print the current date in a format we call the idate by ; calling (print-current-idate channel state). ; To keep track of the cpu time used in a way we find congenial, we ; implement a facility called timers. A ``timer'' is a symbolp with ; an associated value in the timer-alistp called the 'timer-alist, ; stored in the global table of state. Typically the value of a timer ; is a list of rationals, treated as a stack. One may have many such ; timers. As of this writing, the ACL2 system itself has three: ; 'prove-time, 'print-time, and 'other-time, and we use a singleton stack ; 'total-time, as a temporary to sum the times on the other stacks. ; To clean the slate, i.e. to get ready to start a new set of timings, ; one could invoke (set-timer 'prove-time '(0) state), (set-timer ; 'print-time '(0) state), etc., and finally (main-timer state). The ; set-timer function set the values of the timers each to a stack ; containing a single 0. The call of main-timer can be thought of as ; starting the clock running. What it actually does is store the ; current cpu-time-used figure in a secret place to be used later. ; Now, after some computing one could invoke (increment-timer ; 'prove-time state), which would attribute all of the cpu time used ; since cleaning the slate to the top-most element on the 'prove-time ; timer. That is, increment-timer takes the time used since the ; ``clock was started'' and adds it to the number on the top of the ; given timer stack. Increment-timer also restarts the clock. One ; could later execute (increment-timer 'print-time state), which would ; attribute all of the cpu time used since the previous call of ; increment-timer to 'print-time. And so forth. At an appropriate ; time, one could then call (print-timer 'print-time channel state) and ; (print-timer 'prove-time time), which would print the top-most ; values of the timers. Finally, one could either pop the timer ; stacks, exposing accumulated time in that category for some superior ; computation, or pop the stacks but add the popped time into the ; newly exposed accumulated time (charging the superior with the time ; used by the inferior), or simply reset the stacks as by set-timer. ; Time is maintained as a rational. We print time in seconds, accurate ; to two decimal places. We just print the number, without leading or ; trailing spaces or even the word ``seconds''. (local (defthm rational-listp-cdr (implies (rational-listp x) (rational-listp (cdr x))))) (defthm read-run-time-preserves-state-p1 (implies (state-p1 state) (state-p1 (nth 1 (read-run-time state)))) :rule-classes ((:forward-chaining :trigger-terms ((nth 1 (read-run-time state))))) :hints (("Goal" :in-theory (enable nth)))) (defthm read-acl2-oracle-preserves-state-p1 (implies (state-p1 state) (state-p1 (nth 2 (read-acl2-oracle state)))) :rule-classes ((:forward-chaining :trigger-terms ((nth 2 (read-acl2-oracle state))))) :hints (("Goal" :in-theory (enable nth)))) (in-theory (disable read-acl2-oracle)) (local (defthm rational-listp-implies-rationalp-car (implies (and (rational-listp x) x) (rationalp (car x))))) (defthm nth-0-read-run-time-type-prescription (implies (state-p1 state) (rationalp (nth 0 (read-run-time state)))) :hints (("Goal" :in-theory (enable nth))) :rule-classes ((:type-prescription :typed-term (nth 0 (read-run-time state))))) (in-theory (disable read-run-time)) ; Here we prefer not to develop a base of rules about mv-nth. So, we prove ; that it is the same as nth, and get on with the proofs. (local (defthm mv-nth-is-nth (equal (mv-nth n x) (nth n x)) :hints (("Goal" :in-theory (enable nth))))) (defun main-timer (state) (declare (xargs :guard (state-p state))) (mv-let (current-time state) (read-run-time state) (let ((old-value (cond ((and (f-boundp-global 'main-timer state) (rationalp (f-get-global 'main-timer state))) (f-get-global 'main-timer state)) (t 0)))) (let ((state (f-put-global 'main-timer current-time state))) (mv (- current-time old-value) state))))) ; Put-assoc (defun put-assoc-eq-exec (name val alist) (declare (xargs :guard (if (symbolp name) (alistp alist) (symbol-alistp alist)))) ; The function trans-eval exploits the fact that the order of the keys ; is unchanged. (cond ((endp alist) (list (cons name val))) ((eq name (caar alist)) (cons (cons name val) (cdr alist))) (t (cons (car alist) (put-assoc-eq-exec name val (cdr alist)))))) (defun put-assoc-eql-exec (name val alist) (declare (xargs :guard (if (eqlablep name) (alistp alist) (eqlable-alistp alist)))) ; The function trans-eval exploits the fact that the order of the keys ; is unchanged. (cond ((endp alist) (list (cons name val))) ((eql name (caar alist)) (cons (cons name val) (cdr alist))) (t (cons (car alist) (put-assoc-eql-exec name val (cdr alist)))))) (defun put-assoc-equal (name val alist) (declare (xargs :guard (alistp alist))) (cond ((endp alist) (list (cons name val))) ((equal name (caar alist)) (cons (cons name val) (cdr alist))) (t (cons (car alist) (put-assoc-equal name val (cdr alist)))))) (defmacro put-assoc-eq (name val alist) `(put-assoc ,name ,val ,alist :test 'eq)) ; Added for backward compatibility (add-to-set-eql was present through ; Version_4.2): (defmacro put-assoc-eql (name val alist) `(put-assoc ,name ,val ,alist :test 'eql)) (defthm put-assoc-eq-exec-is-put-assoc-equal (equal (put-assoc-eq-exec name val alist) (put-assoc-equal name val alist))) (defthm put-assoc-eql-exec-is-put-assoc-equal (equal (put-assoc-eql-exec name val alist) (put-assoc-equal name val alist))) (defmacro put-assoc (name val alist &key (test ''eql)) ":Doc-Section ACL2::ACL2-built-ins modify an association list by associating a value with a key~/ ~bv[] General Forms: (put-assoc name val alist) (put-assoc name val alist :test 'eql) ; same as above (eql as equality test) (put-assoc name val alist :test 'eq) ; same, but eq is equality test (put-assoc name val alist :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Put-assoc name val alist)] returns an alist that is the same as the list ~c[alist], except that the first pair in ~c[alist] with a ~ilc[car] of ~c[name] is replaced by ~c[(cons name val)], if there is one. If there is no such pair, then ~c[(cons name val)] is added at the end. Note that the order of the keys occurring in ~c[alist] is unchanged (though a new key may be added).~/ The ~il[guard] for a call of ~c[put-assoc] depends on the test. In all cases, the last argument must satisfy ~ilc[alistp]. If the test is ~ilc[eql], then either the first argument must be suitable for ~ilc[eql] (~pl[eqlablep]) or the last argument must satisfy ~ilc[eqlable-alistp]. If the test is ~ilc[eq], then either the first argument must be a symbol or the last argument must satisfy ~ilc[symbol-alistp]. ~l[equality-variants] for a discussion of the relation between ~c[put-assoc] and its variants: ~bq[] ~c[(put-assoc-eq name val alist)] is equivalent to ~c[(put-assoc name val alist :test 'eq)]; ~c[(put-assoc-equal name val alist)] is equivalent to ~c[(put-assoc name val alist :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[put-assoc-equal].~/" (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((name ,name) (val ,val) (alist ,alist)) :logic (put-assoc-equal name val alist) :exec (put-assoc-eq-exec name val alist))) ((equal test ''eql) `(let-mbe ((name ,name) (val ,val) (alist ,alist)) :logic (put-assoc-equal name val alist) :exec (put-assoc-eql-exec name val alist))) (t ; (equal test 'equal) `(put-assoc-equal ,name ,val ,alist)))) (local (defthm timer-alist-bound-in-state-p1 (implies (state-p1 s) (boundp-global1 'timer-alist s)) :hints (("Goal" :in-theory (enable state-p1))))) (local (in-theory (disable boundp-global1))) (local (defthm timer-alist-bound-in-state-p (implies (state-p s) (boundp-global1 'timer-alist s)))) (defun set-timer (name val state) (declare (xargs :guard (and (symbolp name) (rational-listp val) (state-p state)))) (f-put-global 'timer-alist (put-assoc-eq name val (f-get-global 'timer-alist state)) state)) (defun get-timer (name state) (declare (xargs :guard (and (symbolp name) (state-p state)))) (cdr (assoc-eq name (f-get-global 'timer-alist state)))) (local (defthm timer-alistp-implies-rational-listp-assoc-eq (implies (and (symbolp name) (timer-alistp alist)) (rational-listp (cdr (assoc-eq name alist)))))) (defun push-timer (name val state) (declare (xargs :guard (and (symbolp name) (rationalp val) (state-p state)))) (set-timer name (cons val (get-timer name state)) state)) ; The following four rules were not necessary until we added complex numbers. ; However, the first one is now crucial for acceptance of pop-timer. (defthm rationalp-+ (implies (and (rationalp x) (rationalp y)) (rationalp (+ x y)))) ; ;??? The rewrite rule above is troubling. I have spent some time thinking ; about how to eliminate it. Here is an essay on the subject. ; ; Rationalp-+, above, is needed in the guard proof for pop-timer, below. Why? ; ; Why do we need to make this a :rewrite rule? Why can't type-set establish ; (rationalp (+ x y)) whenever this rule would have applied? The reason, ; obviously, is that the hypotheses can't be established by type-set and must be ; established by rewrite. Since type-set doesn't call rewrite, we have to ; program enough of type-set in the rewriter to get the rewriter to act like ; type-set. That is what this lemma does (and that is why it is offensive to ; us). ; ; Why can't type-set establish the (rationalp x) and (rationalp y) hypotheses ; above? Here is the :rewrite rule we need: ; ; (defthm rational-listp-implies-rationalp-car ; (implies (and (rational-listp x) ; x) ; (rationalp (car x)))) ; ; Note that this lemma is "type-like" in the conclusion but not (very) type-like ; in the hypotheses. I mean, (rational-listp x) is not a "type recognizer" ; (except in a good type system, and we haven't got one of those!). The presence ; of this lemma in axioms.lisp should have alerted us to the possible need ; later for a lemma duplicating type-like reasoning in the rewriter. ; ; Here is a simple example of a theorem we can prove using rationalp-+ that we ; cannot prove (directly) without it. I introduce an undefined function so that ; I can state the theorem in a way that does not allow a car-cdr-elim. ; ; (defstub foo (x) t) ; ; (thm (implies (and (rational-listp (foo x)) (foo x)) ; (rationalp (+ 1 (car (foo x))))) ; ; :hints (("Goal" :in-theory (disable rationalp-+))) ; ) ; ; If rationalp-+ is enabled, this proof succeeds, because rewrite does our type ; reasoning for us (via rationalp-+) and uses rational-listp-implies- ; rationalp-car to get the hypothesis that (car (foo x)) is rational. If ; rationalp-+ is disabled, the proof fails because type-set doesn't know that ; (car (foo x)) is rational. ; ; In the actual application (in pop-timer below) no rational-listp hypothesis ; is present. Here is the actual goal ; ; (IMPLIES ; (AND (CONSP (CDDR (ASSOC-EQ NAME ; (CDR (ASSOC 'TIMER-ALIST (NTH 2 STATE)))))) ; (CONSP (CDR (ASSOC-EQ NAME ; (CDR (ASSOC 'TIMER-ALIST (NTH 2 STATE)))))) ; (STATE-P1 STATE) ; (SYMBOLP NAME) ; FLG) ; (RATIONALP (+ (CADR (ASSOC-EQ NAME ; (CDR (ASSOC 'TIMER-ALIST (NTH 2 STATE))))) ; (CADDR (ASSOC-EQ NAME ; (CDR (ASSOC 'TIMER-ALIST ; (NTH 2 STATE)))))))) ; ; If we insist on deleting rationalp-+ as a :rewrite rule we are obliged to ; add certain other rules as either :type-prescriptions or :forward-chaining ; rules. Going the :type-prescription route we could add ; ; (defthm rational-listp-implies-rationalp-car ; (implies (and (rational-listp x) x) ; (rationalp (car x))) ; :rule-classes :type-prescription) ; ; to get the first inkling of how to establish that the two arguments above ; are rational. But we must be able to establish the hypotheses of that rule ; within type-set, so we need ; ; (defthm timer-alistp-implies-rational-listp-assoc-eq ; (implies (and (symbolp name) ; (timer-alistp alist)) ; (rational-listp (cdr (assoc-eq name alist)))) ; :rule-classes :type-prescription) ; ; (defthm rational-listp-cdr ; (implies (rational-listp x) ; (rational-listp (cdr x))) ; :rule-classes :type-prescription) ; ; All three of these rules are currently :rewrite rules, so this would just shift ; rules from the rewriter to type-set. I don't know whether this is a good idea. ; But the methodology is fairly clear, namely: make sure that all concepts used ; in :type-prescription rules are specified with :type-prescription (and/or ; :forward-chaining) rules, not :rewrite rules. (defthm rationalp-* (implies (and (rationalp x) (rationalp y)) (rationalp (* x y)))) (defthm rationalp-unary-- (implies (rationalp x) (rationalp (- x)))) (defthm rationalp-unary-/ (implies (rationalp x) (rationalp (/ x)))) ; Here we add realp versions of the four rules above, as suggested by Jun ; Sawada. As he points out, these rules can be necessary in order to get ; proofs about real/rationalp that succeed in ACL2 also to succeed in ACL2(r). #+:non-standard-analysis (defthm realp-+ (implies (and (realp x) (realp y)) (realp (+ x y)))) #+:non-standard-analysis (defthm realp-* (implies (and (realp x) (realp y)) (realp (* x y)))) #+:non-standard-analysis (defthm realp-unary-- (implies (realp x) (realp (- x)))) #+:non-standard-analysis (defthm realp-unary-/ (implies (realp x) (realp (/ x)))) ; We seem to need the following in V1.8 because we have eliminated bctra. (defthm rationalp-implies-acl2-numberp (implies (rationalp x) (acl2-numberp x))) (defun pop-timer (name flg state) ; If flg is nil we discard the popped value. If flg is t we ; add the popped value into the exposed value. (declare (xargs :guard (and (symbolp name) (state-p state) (consp (get-timer name state)) (or (null flg) (consp (cdr (get-timer name state))))))) (let ((timer (get-timer name state))) (set-timer name (if flg (cons (+ (car timer) (cadr timer)) (cddr timer)) (cdr timer)) state))) (defun add-timers (name1 name2 state) (declare (xargs :guard (and (symbolp name1) (symbolp name2) (state-p state) (consp (get-timer name1 state)) (consp (get-timer name2 state))))) (let ((timer1 (get-timer name1 state)) (timer2 (get-timer name2 state))) (set-timer name1 (cons (+ (car timer1) (car timer2)) (cdr timer1)) state))) ; Here are lemmas for opening up nth on explicitly given conses. (defthm nth-0-cons (equal (nth 0 (cons a l)) a) :hints (("Goal" :in-theory (enable nth)))) (local (defthm plus-minus-1-1 (implies (acl2-numberp x) (equal (+ -1 1 x) x)))) (defthm nth-add1 (implies (and (integerp n) (>= n 0)) (equal (nth (+ 1 n) (cons a l)) (nth n l))) :hints (("Goal" :expand (nth (+ 1 n) (cons a l))))) (defthm main-timer-type-prescription (implies (state-p1 state) (and (consp (main-timer state)) (true-listp (main-timer state)))) :rule-classes :type-prescription) (defthm ordered-symbol-alistp-add-pair-forward (implies (and (symbolp key) (ordered-symbol-alistp l)) (ordered-symbol-alistp (add-pair key value l))) :rule-classes ((:forward-chaining :trigger-terms ((add-pair key value l))))) (defthm assoc-add-pair (implies (and (symbolp sym2) (ordered-symbol-alistp alist)) (equal (assoc sym1 (add-pair sym2 val alist)) (if (equal sym1 sym2) (cons sym1 val) (assoc sym1 alist))))) (defthm add-pair-preserves-all-boundp (implies (and (eqlable-alistp alist1) (ordered-symbol-alistp alist2) (all-boundp alist1 alist2) (symbolp sym)) (all-boundp alist1 (add-pair sym val alist2)))) (defthm state-p1-update-main-timer (implies (state-p1 state) (state-p1 (update-nth 2 (add-pair 'main-timer val (nth 2 state)) state))) :hints (("Goal" :in-theory (set-difference-theories (enable state-p1 global-table) '(true-listp ordered-symbol-alistp assoc sgetprop integer-listp rational-listp true-list-listp open-channels-p all-boundp plist-worldp timer-alistp known-package-alistp 32-bit-integer-listp file-clock-p readable-files-p written-files-p read-files-p writeable-files-p)))) :rule-classes ((:forward-chaining :trigger-terms ((update-nth 2 (add-pair 'main-timer val (nth 2 state)) state))))) (defun increment-timer (name state) ; A note about the integration of #+acl2-par code: ; Why not use defun@par to define increment-timer@par, using ; serial-first-form-parallel-second-form? If we do that, then we have to wait ; until after defun@par is defined, near the end of this file. But at that ; point, guard verification fails. However, guard verification succeeds here, ; not only during the normal boot-strap when proofs are skipped, but also when ; we do proofs (as with "make proofs"). After a few minutes of investigation, ; we have decided to leave well enough alone. (declare (xargs :guard (and (symbolp name) (state-p state) (consp (get-timer name state))))) (let ((timer (get-timer name state))) (mv-let (epsilon state) (main-timer state) (set-timer name (cons (+ (car timer) epsilon) (cdr timer)) state)))) (skip-proofs (defun print-rational-as-decimal (x channel state) (declare (xargs :guard (and (rationalp x) (state-p state) (equal (print-base) 10) (open-output-channel-p channel :character state)))) (let ((x00 (round (* 100 (abs x)) 1))) (pprogn (cond ((< x 0) (princ$ "-" channel state)) (t state)) (cond ((> x00 99) (princ$ (floor (/ x00 100) 1) channel state)) (t (princ$ "0" channel state))) (princ$ "." channel state) (let ((r (rem x00 100))) (cond ((< r 10) (pprogn (princ$ "0" channel state) (princ$ r channel state))) (t (princ$ r channel state))))))) ) (skip-proofs (defun print-timer (name channel state) (declare (xargs :guard (and (symbolp name) (state-p state) (open-output-channel-p channel :character state) (consp (get-timer name state))))) (print-rational-as-decimal (car (get-timer name state)) channel state)) ) (defun known-package-alist (state) ; We avoid using global-val below because this function is called during ; retract-world1 under set-w under enter-boot-strap-mode, before ; primordial-world-globals is called. (declare (xargs :guard (state-p state))) (getprop 'known-package-alist 'global-value nil 'current-acl2-world (w state))) ; Prin1 (skip-proofs (defun prin1$ (x channel state) ; prin1$ differs from prin1 in several ways. The second arg is state, not ; a stream. prin1$ returns the modified state, not x. (declare (xargs :guard (and (or (acl2-numberp x) (characterp x) (stringp x) (symbolp x)) (state-p state) (open-output-channel-p channel :character state)))) #-acl2-loop-only (cond ((live-state-p state) (cond ((and *wormholep* (not (eq channel *standard-co*))) (wormhole-er 'prin1$ (list x channel)))) (let ((stream (get-output-stream-from-channel channel))) (declare (special acl2_global_acl2::current-package)) (with-print-controls ; We use :defaults here, binding only *print-escape* (to put |..| on symbols ; where necessary), to ensure that raw Lisp agrees with the logical definition. ; Actually we need not bind *print-escape* explicitly here, since the default ; for print-escape, taken from *print-control-defaults* (from ; *initial-global-table*), is t. But we bind it anyhow in case we ever change ; its value in *initial-global-table*. :defaults ((*print-escape* t) (*print-base* (f-get-global 'print-base state)) (*print-radix* (f-get-global 'print-radix state)) (*print-case* (f-get-global 'print-case state))) (cond ((acl2-numberp x) (princ #+allegro ; See the comment about a similar case in princ$. (cond ((and (acl2-numberp x) (> *print-base* 10)) (coerce (explode-atom+ x *print-base* *print-radix*) 'string)) (t x)) #-allegro x stream)) ((characterp x) (princ "#\\" stream) (princ (case x ; Keep the following in sync with the function acl2-read-character-string. (#\Newline "Newline") (#\Space "Space") (#\Page "Page") (#\Tab "Tab") (#\Rubout "Rubout") (otherwise x)) stream)) ((stringp x) (princ #\" stream) (let ((n (length (the string x)))) (declare (type fixnum n)) (block check (do ((i 0 (1+ i))) ((= i n)) (declare (type fixnum i)) (let ((ch (char-code (aref (the string x) i)))) (declare (type fixnum ch)) (cond ((or (= ch *char-code-backslash*) (= ch *char-code-double-gritch*)) (prin1-with-slashes x #\" channel state) (return-from check nil))))) (princ x stream))) (princ #\" stream)) ((symbolp x) (cond ((keywordp x) (princ #\: stream)) ((or (equal (symbol-package-name x) (f-get-global 'current-package state)) (member-eq x (package-entry-imports (find-package-entry (f-get-global 'current-package state) (known-package-alist state))))) state) (t (let ((p (symbol-package-name x))) (cond ((needs-slashes p state) (princ "|" stream) (prin1-with-slashes p #\| channel state) (princ "|" stream)) ((eq *print-case* :downcase) (princ (string-downcase p) stream)) (t (princ p stream))) (princ "::" stream)))) (cond ((needs-slashes (symbol-name x) state) (princ #\| stream) (prin1-with-slashes (symbol-name x) #\| channel state) (princ #\| stream)) (t (princ x stream)))) (t (error "Prin1$ called on an illegal object ~a~%~%." x))) (return-from prin1$ state))))) (cond ((acl2-numberp x) (princ$ x channel state)) ((characterp x) (pprogn (princ$ "#\\" channel state) (princ$ (case x (#\Newline "Newline") (#\Space "Space") (#\Page "Page") (#\Tab "Tab") (#\Rubout "Rubout") (otherwise x)) channel state))) ((stringp x) (let ((l (coerce x 'list))) (pprogn (princ$ #\" channel state) (cond ((or (member #\\ l) (member #\" l)) (prin1-with-slashes x #\" channel state)) (t (princ$ x channel state))) (princ$ #\" channel state)))) (t (pprogn (cond ((keywordp x) (princ$ #\: channel state)) ((or (equal (symbol-package-name x) (f-get-global 'current-package state)) (member-eq x (package-entry-imports (find-package-entry (f-get-global 'current-package state) (known-package-alist state))))) state) (t (let ((p (symbol-package-name x))) (pprogn (cond ((needs-slashes p state) (pprogn (princ$ #\| channel state) (prin1-with-slashes p #\| channel state) (princ$ #\| channel state))) ((eq (print-case) :downcase) (princ$ (string-downcase p) channel state)) (t (princ$ p channel state))) (princ$ "::" channel state))))) (cond ((needs-slashes (symbol-name x) state) (pprogn (princ$ #\| channel state) (prin1-with-slashes (symbol-name x) #\| channel state) (princ$ #\| channel state))) (t (princ$ x channel state))))))) ) ; UNTOUCHABLES ; The ``untouchables'' mechanism of ACL2, we believe, gives ACL2 a ; modest form of write-protection which can be used to preserve ; integrity in the presence of arbitrary ACL2 user acts. If a symbol ; s is a member of the global-val of 'untouchable-fns or ; 'untouchable-vars in a world, then translate will cause an error if ; one attempts to define a function or macro (or to directly execute ; code) that would either (for 'untouchable-vars) set or make unbound ; a global variable with name s or (for 'untouchable-fns) call a ; function or macro named s. The general idea is to have a ``sacred'' ; variable, e.g. current-acl2-world, or function, e.g., set-w, which ; the user cannot directly use it has been placed on untouchables. ; Instead, to alter that variable or use that function, the user is ; required to invoke other functions that were defined before the ; symbol was made untouchable. Of course, the implementor must take ; great care to make sure that all methods of access to the resource ; are identified and that all but the authorized ones are on ; untouchables. We do not attempt to enforce any sort of read ; protection for state globals; untouchables is entirely oriented ; towards write protection. Read protection could not be perfectly ; enforced in any case since by calling translate one could at least ; find out what was on untouchables. (local (in-theory (enable boundp-global1))) (defun current-package (state) (declare (xargs :guard (state-p state))) ":Doc-Section Miscellaneous the package used for reading and printing~/ ~c[Current-package] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(current-package state)] and the updater is ~c[(set-current-package val state)], or more conventionally, ~c[(in-package val)]. The value of ~c[current-package] is actually the string that names the package. (Common Lisp's ``package'' objects do not exist in ACL2.) The current package must be known to ACL2, i.e., it must be one of the initial packages or a package defined with ~ilc[defpkg] by the user.~/ When printing symbols, the package prefix is displayed if it is not the ~c[current-package] and may be optionally displayed otherwise. Thus, if ~c[current-package] is ~c[\"ACL2\"] then the symbol ~c['ACL2::SYMB] may be printed as ~c[SYMB] or ~c[ACL2::SYMB], while ~c['MY-PKG::SYMB] must be printed as ~c[MY-PKG::SYMB]. But if ~c[current-package] is ~c[\"MY-PKG\"] then the former symbol must be printed as ~c[ACL2::SYMB] while the latter may be printed as ~c[SYMB]. In Common Lisp, ~c[current-package] also affects how objects are read from character streams. Roughly speaking, read and print are inverses if the ~c[current-package] is fixed, so reading from a stream produced by printing an object must produce an equal object. In ACL2, the situation is more complicated because we never read objects from character streams, we only read them from object ``streams'' (channels). Logically speaking, the objects in such a channel are fixed regardless of the setting of ~c[current-package]. However, our host file systems do not support the idea of Lisp object files and instead only support character files. So when you open an object input channel to a given (character file) we must somehow convert it to a list of ACL2 objects. This is done by a ~i[deus ex machina] (``a person or thing that appears or is introduced suddenly and unexpectedly and provides a contrived solution to an apparently insoluble difficulty,'' Webster's Ninth New Collegiate Dictionary). Roughly speaking, the ~i[deus ex machina] determines what sequence of calls to ~c[read-object] will occur in the future and what the ~c[current-package] will be during each of those calls, and then produces a channel containing the sequence of objects produced by an analogous sequence of Common Lisp reads with ~c[*current-package*] bound appropriately for each. A simple rule suffices to make sane file ~il[io] possible: before you read an object from an object channel to a file created by printing to a character channel, make sure the ~c[current-package] at read-time is the same as it was at print-time." (f-get-global 'current-package state)) (defthm state-p1-update-nth-2-world (implies (and (state-p1 state) (plist-worldp wrld) (known-package-alistp (getprop 'known-package-alist 'global-value nil 'current-acl2-world wrld)) (symbol-alistp (getprop 'acl2-defaults-table 'table-alist nil 'current-acl2-world wrld))) (state-p1 (update-nth 2 (add-pair 'current-acl2-world wrld (nth 2 state)) state))) :hints (("Goal" :in-theory (set-difference-theories (enable state-p1) '(global-val true-listp ordered-symbol-alistp assoc sgetprop integer-listp rational-listp true-list-listp open-channels-p all-boundp plist-worldp timer-alistp known-package-alistp 32-bit-integer-listp file-clock-p readable-files-p written-files-p read-files-p writeable-files-p))))) (defconst *initial-untouchable-fns* ; During development we sometimes want to execute (lp!), :redef+, and then (ld ; "patch.lisp"), where patch.lisp modifies some untouchable state globals or ; calls some untouchable functions or macros. It is therefore handy on ; occasion to replace the current untouchables with nil. This can be done by ; executing the following form: ; (progn ; (setf (cadr (assoc 'global-value (get 'untouchable-fns ; *current-acl2-world-key*))) ; nil) ; (setf (cadr (assoc 'global-value (get 'untouchable-vars ; *current-acl2-world-key*))) ; nil)) '(coerce-state-to-object coerce-object-to-state create-state user-stobj-alist user-stobj-alist-safe f-put-ld-specials ; We need to put ev (and the like) on untouchables because otherwise we can ; access untouchables! To see this, execute (defun foo (x) x), then outside ; the ACL2 loop, execute: ; (setf (cadr (assoc 'global-value ; (get 'untouchables *current-acl2-world-key*))) ; (cons 'foo ; (cadr (assoc 'global-value ; (get 'untouchables *current-acl2-world-key*))))) ; Then (unfortunately) you can evaluate (ev '(foo x) '((x . 3)) state nil nil ; t) without error. ev-fncall ev ev-lst ev-fncall! ev-fncall-rec ev-rec ev-rec-lst ev-rec-acl2-unwind-protect ev-w ev-w-lst install-event set-w set-w! cloaked-set-w! ; read-idate - used by write-acl2-html, so can't be untouchable? update-user-stobj-alist big-n decrement-big-n zp-big-n protected-eval ; must be in context of revert-world-on-error set-site-evisc-tuple set-evisc-tuple-lst set-evisc-tuple-fn1 set-iprint-ar checkpoint-world let-beta-reduce f-put-global@par ; for #+acl2-par (modifies state under the hood) with-live-state ; see comment in that macro stobj-evisceration-alist ; returns bad object trace-evisceration-alist ; returns bad object oracle-apply-raw ; We briefly included maybe-install-acl2-defaults-table, but that defeated the ; ability to call :puff. It now seems unnecessary to include ; maybe-install-acl2-defaults-table, since its body is something one can call ; directly. (And there seems to be no problem with doing so; otherwise, we ; need to prevent that, not merely to make maybe-install-acl2-defaults-table ; untouchable!) )) (defconst *initial-untouchable-vars* '(temp-touchable-vars temp-touchable-fns system-books-dir user-home-dir acl2-version certify-book-info connected-book-directory ; Although in-local-flg should probably be untouchable, currently that is ; problematic because the macro LOCAL expands into a form that touches ; in-local-flg. ; in-local-flg ; Since in-prove-flg need not be untouchable (currently it is only used by ; break-on-error), we omit it from this list. It is used by community book ; misc/bash.lisp. axiomsp current-acl2-world undone-worlds-kill-ring timer-alist main-timer wormhole-name proof-tree ; proof-tree-ctx - used in community book books/cli-misc/expander.lisp fmt-soft-right-margin fmt-hard-right-margin ; We would like to make the following three untouchable, to avoid ; getting a raw Lisp error in this sort of situation: ; (f-put-global 'inhibit-output-lst '(a . b) state) ; (defun foo (x) x) ; But this will take some work so we wait.... ; inhibit-output-lst ; inhibit-output-lst-stack ; inhibited-summary-types in-verify-flg mswindows-drive ;;; could be conditional on #+mswindows acl2-raw-mode-p defaxioms-okp-cert skip-proofs-okp-cert ttags-allowed skip-notify-on-defttag last-make-event-expansion make-event-debug-depth ppr-flat-right-margin ; The following should perhaps be untouchable, as they need to remain in sync. ; But they don't affect soundness, so if a user wants to mess with them, we ; don't really need to stop that. Note that we bind gag-state in ; with-ctx-summarized, via save-event-state-globals, so if we want to make that ; variable untouchable then we need to eliminate the call of ; with-ctx-summarized from the definition of the macro theory-invariant. ; gag-mode ; gag-state ; gag-state-saved checkpoint-summary-limit ; ld specials and such: ; ld-skip-proofsp ;;; used in macro skip-proofs; treat bogus values as t ld-redefinition-action current-package standard-oi standard-co proofs-co ld-prompt ld-missing-input-ok ld-pre-eval-filter ld-pre-eval-print ld-post-eval-print ld-evisc-tuple ld-error-triples ld-error-action ld-query-control-alist ld-verbose writes-okp program-fns-with-raw-code logic-fns-with-raw-code macros-with-raw-code dmrp trace-level ; can change under the hood without logic explanation trace-specs retrace-p parallel-execution-enabled total-parallelism-work-limit ; for #+acl2p-par total-parallelism-work-limit-error ; for #+acl2p-par waterfall-parallelism ; for #+acl2p-par waterfall-printing ; for #+acl2p-par redundant-with-raw-code-okp ; print control variables print-base ; must satisfy print-base-p print-case ; :upcase or :downcase (could also support :capitalize) ; print-circle ; generalized boolean ; print-circle-files ; generalized boolean ; print-escape ; generalized boolean print-length ; nil or non-negative integer print-level ; nil or non-negative integer print-lines ; nil or non-negative integer ; print-pretty ; generalized boolean ; print-radix ; generalized boolean ; print-readably ; generalized boolean print-right-margin ; nil or non-negative integer iprint-ar iprint-hard-bound iprint-soft-bound ; ld-evisc-tuple ; already mentioned above term-evisc-tuple abbrev-evisc-tuple gag-mode-evisc-tuple serialize-character serialize-character-system ; others skip-proofs-by-system host-lisp compiler-enabled compiled-file-extension modifying-include-book-dir-alist raw-include-book-dir-alist deferred-ttag-notes deferred-ttag-notes-saved pc-assign illegal-to-certify-message acl2-sources-dir last-prover-steps ; being conservative here; perhaps could omit )) ; There are a variety of state global variables, 'ld-skip-proofsp among them, ; that are "bound" by LD in the sense that their values are protected by ; pushing them upon entrance to LD and popping them upon exit. These globals ; are called the "LD specials". For each LD special there are accessor and ; updater functions. The updaters enforce our invariants on the values of the ; globals. We now define the accessor for the LD special ld-skip-proofsp. We ; delay the introduction of the updater until we have some error handling ; functions. (defun ld-skip-proofsp (state) (declare (xargs :guard (state-p state))) ":Doc-Section Miscellaneous how carefully ACL2 processes your ~il[command]s~/ ~bv[] Examples: ACL2 !>(set-ld-skip-proofsp t state) T ACL2 !s>(set-ld-skip-proofsp nil state) NIL ACL2 !>(set-ld-skip-proofsp 'include-book state) INCLUDE-BOOK ACL2 !s> ~ev[]~/ A global variable in the ACL2 ~ilc[state], called ~c['ld-skip-proofsp], determines the thoroughness with which ACL2 processes your ~il[command]s. This variable may take on one of three values: ~c[t], ~c[nil] or ~c[']~ilc[include-book]. When ~c[ld-skip-proofsp] is non-~c[nil], the system assumes that which ought to be proved and is thus unsound. The form ~c[(set-ld-skip-proofsp flg state)] is the general-purpose way of setting ~c[ld-skip-proofsp]. This global variable is an ``~ilc[ld] special,'' which is to say, you may call ~ilc[ld] in such a way as to ``bind'' this variable for the dynamic extent of the ~ilc[ld]. When ~c[ld-skip-proofsp] is non-~c[nil], the default ~il[prompt] displays the character ~c[s]. Thus, the ~il[prompt] ~bv[] ACL2 !s> ~ev[] means that the default ~il[defun-mode] is ~c[:]~ilc[logic] (otherwise the character ~c[p], for ~c[:]~ilc[program], would also be printed; ~pl[default-print-prompt]) but ``proofs are being skipped.'' Observe that there are two legal non-~c[nil] values, ~c[t] and ~c[']~ilc[include-book]. When ~c[ld-skip-proofsp] is ~c[t], ACL2 skips all proof obligations but otherwise performs all other required analysis of input ~il[events]. When ~c[ld-skip-proofsp] is ~c[']~ilc[include-book], ACL2 skips not only proof obligations but all analysis except that required to compute the effect of successfully executed ~il[events]. To explain the distinction, let us consider one particular event, say a ~ilc[defun]. Very roughly speaking, a ~ilc[defun] event normally involves a check of the syntactic well-formedness of the submitted definition, the generation and proof of the termination conditions, and the computation and storage of various rules such as a ~c[:]~ilc[definition] rule and some ~c[:]~ilc[type-prescription] rules. By ``normally'' above we mean when ~c[ld-skip-proofsp] is ~c[nil]. How does a ~ilc[defun] behave when ~c[ld-skip-proofsp] is non-~c[nil]? If ~c[ld-skip-proofsp] is ~c[t], then ~ilc[defun] performs the syntactic well-formedness checks and computes and stores the various rules, but it does not actually carry out the termination proofs. If ~c[ld-skip-proofsp] is ~c[']~ilc[include-book], ~ilc[defun] does not do the syntactic well-formedness check nor does it carry out the termination proof. Instead, it merely computes and stores the rules under the assumption that the checks and proofs would all succeed. Observe that a setting of ~c[']~ilc[include-book] is ``stronger'' than a setting of ~c[t] in the sense that ~c[']~ilc[include-book] causes ~ilc[defun] to assume even more about the admissibility of the event than ~c[t] does. As one might infer from the choice of name, the ~ilc[include-book] event sets ~c[ld-skip-proofsp] to ~c[']~ilc[include-book] when processing the ~il[events] in a book being loaded. Thus, ~ilc[include-book] does the miminal work necessary to carry out the effects of every event in the book. The syntactic checks and proof obligations were, presumably, successfully carried out when the book was certified. A non-~c[nil] value for ~c[ld-skip-proofsp] also affects the system's output messages. Event summaries (the paragraphs that begin ``Summary'' and display the event forms, rules used, etc.) are not printed when ~c[ld-skip-proofsp] is non-~c[nil]. Warnings and observations are printed when ~c[ld-skip-proofsp] is ~c[t] but are not printed when it is ~c[']~ilc[include-book]. Intuitively, ~c[ld-skip-proofsp] ~c[t] means skip just the proofs and otherwise do all the work normally required for an event; while ~c[ld-skip-proofsp] ~c[']~ilc[include-book] is ``stronger'' and means do as little as possible to process ~il[events]. In accordance with this intuition, ~ilc[local] ~il[events] are processed when ~c[ld-skip-proofsp] is ~c[t] but are skipped when ~c[ld-skip-proofsp] is ~c[']~ilc[include-book]. The ACL2 system itself uses only two settings, ~c[nil] and ~c[']~ilc[include-book], the latter being used only when executing the ~il[events] inside of a book being included. The ~c[ld-skip-proofsp] setting of ~c[t] is provided as a convenience to the user. For example, suppose one has a file of ~il[events]. By loading it with ~ilc[ld] with ~c[ld-skip-proofsp] set to ~c[t], the ~il[events] can all be checked for syntactic correctness and assumed without proof. This is a convenient way to recover a state lost by a system crash or to experiment with a modification of an ~il[events] file. The foregoing discussion is actually based on a lie. ~c[ld-skip-proofsp] is allowed two other values, ~c['initialize-acl2] and ~c['include-book-with-locals]. The first causes behavior similar to ~c[t] but skips ~ilc[local] ~il[events] and avoids some error checks that would otherwise prevent ACL2 from properly booting. The second is identical to ~c[']~ilc[include-book] but also executes ~ilc[local] ~il[events]. These additional values are not intended for use by the user, but no barriers to their use have been erected. We close by reminding the user that ACL2 is potentially unsound if ~c[ld-skip-proofsp] is ever set by the user. We provide access to it simply to allow experimentation and rapid reconstruction of lost or modified logical ~il[world]s." (f-get-global 'ld-skip-proofsp state)) #-acl2-loop-only (save-def (defun-one-output bad-lisp-objectp (x) ; This routine does a root and branch exploration of x and guarantees that x is ; composed entirely of complex rationals, rationals, 8-bit characters that are ; "canonical" in the sense that they are the result of applying code-char to ; their character code, strings of such characters, symbols made from such ; strings (and "interned" in a package known to ACL2) and conses of the ; foregoing. ; We return nil or non-nil. If nil, then x is a legal ACL2 object. If we ; return non-nil, then x is a bad object and the answer is a message, msg, such ; that (fmt "~@0" (list (cons #\0 msg)) ...) will explain why. ; All of our ACL2 code other than this routine assumes that we are manipulating ; non-bad objects, except for symbols in the invisible package, e.g. state and ; the invisible array mark. We make these restrictions for portability's sake. ; If a Lisp expression is a theorem on a Symbolics machine we want it to be a ; theorem on a Sun. Thus, we can't permit such constants as #\Circle-Plus. We ; also assume (and check in chk-suitability-of-this-common-lisp) that all of ; the characters mentioned above are distinct. (cond ((consp x) (or (bad-lisp-objectp (car x)) (bad-lisp-objectp (cdr x)))) ((integerp x) ; CLTL2 says, p. 39, ``X3J13 voted in January 1989 <76> to specify that the ; types of fixnum and bignum do in fact form an exhaustive partition of the ; type integer; more precisely, they voted to specify that the type bignum is ; by definition equivalent to (and integer (not fixnum)). I interpret this to ; mean that implementators (sic) could still experiment with such extensions as ; adding explicit representations of infinity, but such infinities would ; necessarily be of type bignum'' ; The axioms of ACL2 would certainly not hold for experimental infinite ; bignums. But we know of no way to test for an infinite integer. So up ; through Version_3.6.1, we repeatedly took the square root to check that we ; get to a fixnum (which would include 0): ; (do ((i 0 (1+ i)) ; (y (abs x) (isqrt y))) ; (nil) ; (cond ((typep y 'fixnum) (return nil)) ; ((> i 200) ; (return (cons "We suspect that ~x0 is an infinite ~ ; integer, which we cannot handle in ACL2." ; (list (cons #\0 x))))))) ; However, the CL HyperSpec glossary, ; http://www.lispworks.com/documentation/HyperSpec/Body/26_glo_i.htm#integer, ; defines integers to be "mathematical integers": ; integer n. an object of type integer, which represents a mathematical ; integer. ; The CL HyperSpec also makes that point in ; http://www.lispworks.com/documentation/HyperSpec/Body/t_intege.htm#integer: ; System Class INTEGER ; Class Precedence List: ; ; integer, rational, real, number, t ; ; Description: ; ; An integer is a mathematical integer. There is no limit on the ; magnitude of an integer. ; Therefore, we no longer check for bad integers. But if we really need some ; such check, perhaps the following would be at least as robust as the check ; above and much more efficient: ; (typep (logcount x) 'fixnum) ; Note that nonstandard integers integeres (like (H)) are not an issue ; because all Common Lisp integers are "real" integers, hence standard. nil) ((symbolp x) (cond ((eq x nil) nil) ; seems like useful special case for true lists ((bad-lisp-objectp (symbol-name x))) (t (let ((pkg (symbol-package x))) (cond ((null pkg) (cons "Uninterned symbols such as the one CLTL displays as ~ ~s0 are not allowed in ACL2." (list (cons #\0 (format nil "~s" x))))) ((not (eq x (intern (symbol-name x) pkg))) (cons "The symbol ~x0 fails to satisfy the property that it ~ be eq to the result of interning its symbol-name in ~ its symbol package. Such a symbol is illegal in ACL2." (list (cons #\0 (format nil "~s" x))))) ((or (eq pkg *main-lisp-package*) (get x *initial-lisp-symbol-mark*)) nil) ((let ((entry (find-package-entry (package-name pkg) (known-package-alist *the-live-state*)))) ; We maintain the following Invariant on Symbols in the Common Lisp Package: If ; a symbol arising in ACL2 evaluation or state resides in *main-lisp-package*, ; then either its symbol-package is *main-lisp-package* or else its ; *initial-lisp-symbol-mark* property is "COMMON-LISP". This invariant ; supports the notion that in the ACL2 logic, there are no symbols imported ; into the "COMMON-LISP" package: that is, the symbol-package-name of a symbol ; residing in the "COMMON-LISP" package is necessarily "COMMON-LISP". See the ; axiom common-lisp-package, and see the (raw Lisp) definition of ; symbol-package-name. ; With the above comment in mind, consider the possibility of allowing here the ; sub-case (eq x (intern (symbol-name x) *main-lisp-package*)). Now, the ; implementation of symbol-package-name is based on package-name for symbols ; whose *initial-lisp-symbol-mark* is not set; so if we allow such a sub-case, ; then the computed symbol-package-name would be wrong on symbols such as ; SYSTEM::ALLOCATE (in GCL) or CLOS::CLASS-DIRECT-DEFAULT-INITARGS (in CLISP), ; which are imported into the "COMMON-LISP" package but do not belong to the ; list *common-lisp-symbols-from-main-lisp-package*. One solution may seem to ; be to include code here, in this sub-case, that sets the ; *initial-lisp-symbol-mark* property on such a symbol; but that is not ; acceptable because include-book bypasses bad-lisp-objectp (see ; chk-bad-lisp-object). Our remaining option is to change the implementation ; of symbol-package-name to comprehend symbols like the two above, say by ; looking up the name of the symbol-package in find-non-hidden-package-entry ; and then doing the above eq test when the package name is not found. But ; this lookup could produce undesirable performance degradation for ; symbol-package-name. So instead, we will consider symbols like the two above ; to be bad Lisp objects, with the assumption that it is rare to encounter such ; a symbol, i.e.: a symbol violating the above Invariant on Symbols in the ; Common Lisp Package. (and (or (null entry) (package-entry-hidden-p entry)) (cons "The symbol CLTL displays as ~s0 is not in any of the ~ packages known to ACL2.~@1" (list (cons #\0 (format nil "~s" x)) (cons #\1 (cond ((or (null entry) (null (package-entry-book-path entry))) "") (t (msg " This package was defined under a ~ locally included book. Thus, some ~ include-book was local in the following ~ sequence of included books, from top-most ~ book down to the book whose portcullis ~ defines this package (with a defpkg ~ event).~|~% ~F0" (reverse (unrelativize-book-path (package-entry-book-path entry) (f-get-global 'system-books-dir *the-live-state*)))))))))))) (t nil)))))) ((stringp x) (cond ((not (simple-string-p x)) (cons "The strings of ACL2 must be simple strings, but ~x0 is not ~ simple." (list (cons #\0 x)))) (t (do ((i 0 (1+ i))) ((= i (length x))) (declare (type fixnum i)) (let ((ch (char (the string x) i))) (cond ((legal-acl2-character-p ch) nil) (t (let ((code (char-code ch))) (cond ((not (< code 256)) (return (cons "The strings of ACL2 may contain only ~ characters whose char-code does not ~ exceed 255. The object CLTL displays ~ as ~s0 has char-code ~x1 and hence is ~ not one of those." (list (cons #\0 (coerce (list ch) 'string)) (cons #\1 (char-code ch)))))) ((eql (the character ch) (the character (code-char code))) ; We allow the canonical character with code less than 256 in a string, even ; the character #\Null (for example) or any such character that may not be a ; legal-acl2-character-p, because in a string (unlike as a character object) ; the character will be printed in a way that can be read back in, not using a ; print name that may not be standard across all Lisps. nil) (t (return (cons "ACL2 strings may contain only ~ characters without attributes. The ~ character with char-code ~x0 that CLTL ~ displays as ~s1 is not the same as the ~ character that is the value of ~x2." (list (cons #\0 code) (cons #\1 (coerce (list ch) 'string)) (cons #\2 `(code-char ,code))))))))))))))) ((characterp x) (cond ((legal-acl2-character-p x) nil) (t ; Keep this code in sync with legal-acl2-character-p. (cons "The only legal ACL2 characters are those recognized by ~ the function legal-acl2-character-p. The character ~ with ~x0 = ~x1 that CLTL displays as ~s2 is not one of ~ those." (list (cons #\0 'char-code) (cons #\1 (char-code x)) (cons #\2 (coerce (list x) 'string))))))) ((typep x 'ratio) (or (bad-lisp-objectp (numerator x)) (bad-lisp-objectp (denominator x)))) ((typep x '(complex rational)) (or (bad-lisp-objectp (realpart x)) (bad-lisp-objectp (imagpart x)))) (t (cons "ACL2 permits only objects constructed from rationals, complex ~ rationals, legal ACL2 characters, simple strings of these ~ characters, symbols constructed from such strings and interned in ~ the ACL2 packages, and cons trees of such objects. The object ~ CLTL displays as ~s0 is thus illegal in ACL2." (list (cons #\0 (format nil "~s" x))))))) ) #-acl2-loop-only (defun-one-output chk-bad-lisp-object (x) ; We avoid the check when including a book, for efficiency. In one experiment ; on a large book we found a 2.8% time savings by redefining this function ; simply to return nil. (when (not (or *inside-include-book-fn* ; We avoid the bad-lisp-objectp check during the Convert procedure of ; provisional certification, in part because it is not necessary but, more ; important, to avoid errors due to hidden defpkg events. Without the check on ; cert-op below, we get such an error with the following example from Sol ; Swords. ;;; event.lisp ; (in-package "FOO") ; (defmacro acl2::my-event () ; '(make-event '(defun asdf () nil))) ;;; top.lisp ; (in-package "ACL2") ; (include-book "event") ; (my-event) ;;; Do these commands: ; ; In one session: ; (defpkg "FOO" *acl2-exports*) ; (certify-book "event" ?) ; ; Then in another session: ; (certify-book "top" ? t :pcert :create) ; ; Then in yet another session: ; (set-debugger-enable :bt) ; optional ; (certify-book "top" ? t :pcert :convert) (eq (cert-op *the-live-state*) :convert-pcert))) (let ((msg (bad-lisp-objectp x))) (cond (msg (interface-er "~@0" msg)) (t nil))))) (defmacro assign (x y) ":Doc-Section ACL2::ACL2-built-ins assign to a global variable in ~ilc[state]~/ ~bv[] Examples: (assign x (expt 2 10)) (assign a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B))~/ General Form: (assign symbol term) ~ev[] where ~c[symbol] is any symbol (with certain enforced exclusions to avoid overwriting ACL2 system ``globals'') and ~c[term] is any ACL2 term that could be evaluated at the top-level. ~c[Assign] evaluates the term, stores the result as the value of the given symbol in the ~c[global-table] of ~ilc[state], and returns the result. (Note: the actual implementation of the storage of this value is much more efficient than this discussion of the logic might suggest.) ~c[Assign] is a macro that effectively expands to the more complicated but understandable: ~bv[] (pprogn (f-put-global 'symbol term state) (mv nil (f-get-global 'symbol state) state)). ~ev[] The macro ~c[f-put-global] is closely related to ~ilc[assign]: ~c[(assign var val)] macroexpands to ~c[(f-put-global 'var val state)]. The macro ~ilc[@] gives convenient access to the value of such globals. The ~c[:]~ilc[ubt] operation has no effect on the ~c[global-table] of ~ilc[state]. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.~/" (declare (type symbol x)) `(pprogn (f-put-global ',x ,y state) (mv nil (f-get-global ',x state) state))) (defmacro @ (x) ":Doc-Section ACL2::ACL2-built-ins get the value of a global variable in ~ilc[state]~/ ~bv[] Examples: (+ (@ y) 1) (assign a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B))~/ General Form: (@ symbol) ~ev[] where ~c[symbol] is any symbol to which you have ~ilc[assign]ed a global value. This macro expands into ~c[(f-get-global 'symbol state)], which retrieves the stored value of the symbol. The macro ~c[f-get-global] is closely related to ~ilc[@]: ~c[(@ var)] macroexpands to ~c[(f-get-global 'var state)]. The macro ~ilc[assign] makes it convenient to set the value of a symbol. The ~c[:]~ilc[ubt] operation has no effect on the ~c[global-table] of ~ilc[state]. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.~/" (declare (type symbol x)) `(f-get-global ',x state)) ; We have found it useful, especially for proclaiming of FMT functions, to have ; a version `the2s' of the macro `the', for the multiple value case. Note that ; the value returned in raw lisp by (mv x y ...) is x (unless feature ; acl2-mv-as-values is set), so for example, we can avoid boxing the fixnum x ; by suitable declarations and proclamations. (defun make-var-lst1 (root sym n acc) (declare (xargs :guard (and (symbolp sym) (character-listp root) (integerp n) (<= 0 n)) :mode :program)) (cond ((zp n) acc) (t (make-var-lst1 root sym (1- n) (cons (intern-in-package-of-symbol (coerce (append root (explode-nonnegative-integer (1- n) 10 nil)) 'string) sym) acc))))) (encapsulate () (local (defthm character-listp-explode-nonnegative-integer (implies (character-listp ans) (character-listp (explode-nonnegative-integer n 10 ans))))) (verify-termination-boot-strap make-var-lst1)) (defun make-var-lst (sym n) (declare (xargs :guard (and (symbolp sym) (integerp n) (<= 0 n)))) (make-var-lst1 (coerce (symbol-name sym) 'list) sym n nil)) ; Union$ (defun union-eq-exec (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (or (symbol-listp l1) (symbol-listp l2))))) (cond ((endp l1) l2) ((member-eq (car l1) l2) (union-eq-exec (cdr l1) l2)) (t (cons (car l1) (union-eq-exec (cdr l1) l2))))) (defun union-eql-exec (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (or (eqlable-listp l1) (eqlable-listp l2))))) (cond ((endp l1) l2) ((member (car l1) l2) (union-eql-exec (cdr l1) l2)) (t (cons (car l1) (union-eql-exec (cdr l1) l2))))) (defun union-equal (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2)))) (cond ((endp l1) l2) ((member-equal (car l1) l2) (union-equal (cdr l1) l2)) (t (cons (car l1) (union-equal (cdr l1) l2))))) (defmacro union-eq (&rest lst) `(union$ ,@lst :test 'eq)) (defthm union-eq-exec-is-union-equal (equal (union-eq-exec l1 l2) (union-equal l1 l2))) (defthm union-eql-exec-is-union-equal (equal (union-eql-exec l1 l2) (union-equal l1 l2))) (defun parse-args-and-test (x tests default ctx form name) ; We use this function in union$ and intersection$ to remove optional keyword ; argument :TEST test from the given argument list, x. The result is (mv args ; test), where either x ends in :TEST test and args is the list of values ; preceding :TEST, or else args is x and test is default. ; Tests is the list of legal tests, typically '('eq 'eql 'equal). Default is ; the test to use by default, typically ''eql. Ctx, form, and name are used ; for error reporting. (declare (xargs :guard (and (true-listp x) (true-listp tests) (symbolp name)))) (let* ((len (length x)) (len-2 (- len 2)) (kwd/val (cond ((<= 2 len) (let ((kwd (nth len-2 x))) (cond ((keywordp kwd) (cond ((eq kwd :TEST) (nthcdr len-2 x)) (t (hard-error ctx "If a keyword is supplied in the ~ next-to-last argument of ~x0, that ~ keyword must be :TEST. The keyword ~x1 ~ is thus illegal in the call ~x2." (list (cons #\0 name) (cons #\1 kwd) (cons #\2 form)))))) (t nil)))) (t nil)))) (mv (cond (kwd/val (let ((test (car (last x)))) (cond ((not (member-equal test tests)) (hard-error ctx "The :TEST argument for ~x0 must be one of ~&1. The ~ form ~x2 is thus illegal. See :DOC ~s3." (list (cons #\0 name) (cons #\1 tests) (cons #\2 form) (cons #\3 (symbol-name name))))) (t test)))) (t default)) (cond (kwd/val (butlast x 2)) (t x))))) (defmacro union$ (&whole form &rest x) ":Doc-Section ACL2::ACL2-built-ins elements of one list that are not elements of another~/ ~bv[] General Forms: (union$ l1 l2 ... lk) (union$ l1 l2 ... lk :test 'eql) ; same as above (union$ l1 l2 ... lk :test 'eq) ; same, but eq is equality test (union$ l1 l2 ... lk :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Union$ x y)] equals a list that contains both the members of ~c[x] and the members of ~c[y]. More precisely, the resulting list is the same as one would get by first deleting the members of ~c[y] from ~c[x], and then concatenating the result to the front of ~c[y]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing members of the two lists. ~c[Union$] need not take exactly two arguments: ~c[(union$)] is ~c[nil], ~c[(union$ x)] is ~c[x], ~c[(union$ x y z ... :test test)] is ~c[(union$ x (union$ y z ... :test test) :test test)], and if ~c[:TEST] is not supplied, then ~c[(union$ x y z ...)] is ~c[(union$ x (union$ y z ...))]. For the discussion below we restrict ourselves, then, to the cases ~c[(union$ x y)] and ~c[(union$ x y :test test)].~/ The ~il[guard] for a call of ~c[union$] (in the two cases just above) depends on the test. In all cases, both arguments must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then one of the arguments must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then one of the arguments must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[union$] and its variants: ~bq[] ~c[(union-eq x lst)] is equivalent to ~c[(union$ x lst :test 'eq)]; ~c[(union-equal x lst)] is equivalent to ~c[(union$ x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[union-equal]. Note that ~c[union-eq] can take any number of arguments, in analogy to ~c[union$]; indeed, ~c[(union-eq ...)] expands to ~c[(union$ ... :test 'eq)]. However, ~c[union-equal] is a function, not a macro, and takes exactly two arguments. ~c[Union$] is similar to the Common Lisp primitive ~c[union]. However, Common Lisp does not specify the order of elements in the result of a call of ~c[union].~/" (mv-let (test args) (parse-args-and-test x '('eq 'eql 'equal) ''eql 'union$ form 'union$) (cond ((null args) nil) ((null (cdr args)) (car args)) (t (let* ((vars (make-var-lst 'x (length args))) (bindings (pairlis$ vars (pairlis$ args nil)))) (cond ((equal test ''eq) `(let-mbe ,bindings :logic ,(xxxjoin 'union-equal vars) :exec ,(xxxjoin 'union-eq-exec vars))) ((equal test ''eql) `(let-mbe ,bindings :logic ,(xxxjoin 'union-equal vars) :exec ,(xxxjoin 'union-eql-exec vars))) (t ; (equal test 'equal) (xxxjoin 'union-equal args)))))))) (defun subst-for-nth-arg (new n args) (declare (xargs :mode :program)) ; This substitutes the term new for the nth argument in the argument ; list args (0 based). (cond ((int= n 0) (cons new (cdr args))) (t (cons (car args) (subst-for-nth-arg new (1- n) (cdr args)))))) #+acl2-loop-only (defmacro the-mv (args type body &optional state-pos) ; A typical use of this macro is ; (the-mv 3 (signed-byte 30) 2) ; which expands to ; (MV-LET (X0 X1 STATE) ; ; (MV (THE (SIGNED-BYTE 30) X0) X1 STATE)) ; A more flexible use is ; (the-mv (v stobj1 state w) (signed-byte 30) ) ; which expands to ; (MV-LET (V STOBJ1 STATE W) ; ; (MV (THE (SIGNED-BYTE 30) V) STOBJ1 STATE W)) ; This macro may be used when body returns n>1 things via mv, where n=args if ; args is an integer and otherwise args is a true list of variables and n is ; the length of args. The macro effectively declares that the first (0th) ; value returned is of the indicated type. Finally, if n is an integer and the ; STATE is present in the return vector, you must specify where (0-based). ; The optional state-pos argument is the zero-based position of 'state in the ; argument list, if args is a number. Otherwise state-pos is irrelevant. (declare (xargs :guard (and (or (and (integerp args) (< 1 args)) (and (symbol-listp args) (cdr args))) (or (null state-pos) (and (integerp state-pos) (<= 0 state-pos) (< state-pos args)))))) (let ((mv-vars (if (integerp args) (if state-pos (subst-for-nth-arg 'state state-pos (make-var-lst 'x args)) (make-var-lst 'x args)) args))) (list 'mv-let mv-vars body (cons 'mv (cons (list 'the type (car mv-vars)) (cdr mv-vars)))))) #-acl2-loop-only (defmacro the-mv (vars type body &optional state-pos) (declare (ignore #-acl2-mv-as-values vars state-pos)) #+acl2-mv-as-values (list 'the `(values ,type ,@(make-list (if (integerp vars) (1- vars) (length (cdr vars))) :initial-element t)) body) #-acl2-mv-as-values (list 'the type body)) (defmacro the2s (x y) (list 'the-mv 2 x y 1)) (deflabel bibliography :doc ":Doc-Section Miscellaneous reports about ACL2~/ For a list of notes and reports about ACL2, see ~url[http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html].~/~/") ; Here we implement acl2-defaults-table, which is used for handling the default ; defun-mode and other defaults. ; WARNING: If you add a new key to acl-defaults-table, and hence a new ; set- function for smashing the acl2-defaults-table at that key, then ; be sure to add that set- function to the list in ; chk-embedded-event-form! E.g., when we added the ; :irrelevant-formals-ok key we also defined set-irrelevant-formals-ok ; and then added it to the list in chk-embedded-event-form. Also add ; similarly to (deflabel acl2-defaults-table ...) and to ; primitive-event-macros. (defun non-free-var-runes (runes free-var-runes-once free-var-runes-all acc) (declare (xargs :guard (and (true-listp runes) (true-listp free-var-runes-once) (true-listp free-var-runes-all)))) (if (endp runes) acc (non-free-var-runes (cdr runes) free-var-runes-once free-var-runes-all (if (or (member-equal (car runes) free-var-runes-once) (member-equal (car runes) free-var-runes-all)) acc (cons (car runes) acc))))) (defun free-var-runes (flg wrld) (declare (xargs :guard (plist-worldp wrld))) (cond ((eq flg :once) (global-val 'free-var-runes-once wrld)) (t ; (eq flg :all) (global-val 'free-var-runes-all wrld)))) (defthm natp-position-ac ; for admission of absolute-pathname-string-p (implies (and (integerp acc) (<= 0 acc)) (or (equal (position-ac item lst acc) nil) (and (integerp (position-ac item lst acc)) (<= 0 (position-ac item lst acc))))) :rule-classes :type-prescription) (defun absolute-pathname-string-p (str directoryp os) ; Str is a Unix-style pathname. However, on Windows, Unix-style absolute ; pathnames may start with a prefix such as "c:"; see mswindows-drive. ; Directoryp is non-nil when we require str to represent a directory in ACL2 ; with Unix-style syntax, returning nil otherwise. ; Function expand-tilde-to-user-home-dir should already have been applied ; before testing str with this function. (declare (xargs :guard (stringp str))) (let ((len (length str))) (and (< 0 len) (cond ((and (eq os :mswindows) ; hence os is not nil (let ((pos-colon (position #\: str)) (pos-sep (position *directory-separator* str))) (and pos-colon (eql pos-sep (1+ pos-colon)))) t)) ((eql (char str 0) *directory-separator*) t) (t ; possible hard error for ~ or ~/... (and (eql (char str 0) #\~) ; Note that a leading character of `~' need not get special treatment by ; Windows. See also expand-tilde-to-user-home-dir. (not (eq os :mswindows)) (prog2$ (and (or (eql 1 len) (eql (char str 1) *directory-separator*)) (hard-error 'absolute-pathname-string-p "Implementation error: Forgot ~ to apply ~ expand-tilde-to-user-home-dir ~ before calling ~ absolute-pathname-string-p. ~ Please contact the ACL2 ~ implementors." nil)) t)))) (if directoryp (eql (char str (1- len)) *directory-separator*) t)))) (defun include-book-dir-alistp (x os) (declare (xargs :guard t)) (cond ((atom x) (null x)) (t (and (consp (car x)) (keywordp (caar x)) (stringp (cdar x)) (absolute-pathname-string-p (cdar x) t os) (include-book-dir-alistp (cdr x) os))))) (defun illegal-ruler-extenders-values (x wrld) (declare (xargs :guard (and (symbol-listp x) (plist-worldp wrld)))) (cond ((endp x) nil) ((or (eq (car x) :lambdas) (function-symbolp (car x) wrld)) (illegal-ruler-extenders-values (cdr x) wrld)) (t (cons (car x) (illegal-ruler-extenders-values (cdr x) wrld))))) ; Intersection$ (defun intersection-eq-exec (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (or (symbol-listp l1) (symbol-listp l2))))) (cond ((endp l1) nil) ((member-eq (car l1) l2) (cons (car l1) (intersection-eq-exec (cdr l1) l2))) (t (intersection-eq-exec (cdr l1) l2)))) (defun intersection-eql-exec (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (or (eqlable-listp l1) (eqlable-listp l2))))) (cond ((endp l1) nil) ((member (car l1) l2) (cons (car l1) (intersection-eql-exec (cdr l1) l2))) (t (intersection-eql-exec (cdr l1) l2)))) (defun intersection-equal (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2)))) (cond ((endp l1) nil) ((member-equal (car l1) l2) (cons (car l1) (intersection-equal (cdr l1) l2))) (t (intersection-equal (cdr l1) l2)))) (defmacro intersection-eq (&rest lst) `(intersection$ ,@lst :test 'eq)) (defthm intersection-eq-exec-is-intersection-equal (equal (intersection-eq-exec l1 l2) (intersection-equal l1 l2))) (defthm intersection-eql-exec-is-intersection-equal (equal (intersection-eql-exec l1 l2) (intersection-equal l1 l2))) (defmacro intersection$ (&whole form &rest x) ":Doc-Section ACL2::ACL2-built-ins elements of one list that are not elements of another~/ ~bv[] General Forms: (intersection$ l1 l2 ... lk) (intersection$ l1 l2 ... lk :test 'eql) ; same as above (intersection$ l1 l2 ... lk :test 'eq) ; same, but eq is equality test (intersection$ l1 l2 ... lk :test 'equal) ; same, but equal is equality test ~ev[] ~c[(Intersection$ x y)] equals a list that contains the ~c[member]s of ~c[x] that are also ~c[member]s of ~c[y]. More precisely, the resulting list is the result of deleting from ~c[x] those members that that are not members of ~c[y]. The optional keyword, ~c[:TEST], has no effect logically, but provides the test (default ~ilc[eql]) used for comparing members of the two lists. ~c[Intersection$] need not take exactly two arguments, though it must take at least one argument: ~c[(intersection$ x)] is ~c[x], ~c[(intersection$ x y z ... :test test)] is ~c[(intersection$ x (intersection$ y z ... :test test) :test test)], and if ~c[:TEST] is not supplied, then ~c[(intersection$ x y z ...)] is ~c[(intersection$ x (intersection$ y z ...))]. For the discussion below we restrict ourselves, then, to the cases ~c[(intersection$ x y)] and ~c[(intersection$ x y :test test)].~/ The ~il[guard] for a call of ~c[intersection$] (in the two cases just above) depends on the test. In all cases, both arguments must satisfy ~ilc[true-listp]. If the test is ~ilc[eql], then one of the arguments must satisfy ~ilc[eqlable-listp]. If the test is ~ilc[eq], then one of the arguments must satisfy ~ilc[symbol-listp]. ~l[equality-variants] for a discussion of the relation between ~c[intersection$] and its variants: ~bq[] ~c[(intersection-eq x lst)] is equivalent to ~c[(intersection$ x lst :test 'eq)]; ~c[(intersection-equal x lst)] is equivalent to ~c[(intersection$ x lst :test 'equal)]. ~eq[] In particular, reasoning about any of these primitives reduces to reasoning about the function ~c[intersection-equal]. Note that ~c[intersection-eq] can take any positive number of arguments, in analogy to ~c[intersection$]; indeed, ~c[(intersection-eq ...)] expands to ~c[(intersection$ ... :test 'eq)]. However, ~c[intersection-equal] is a function, not a macro, and takes exactly two arguments. ~c[Intersection$] is similar to the Common Lisp primitive ~c[intersection]. However, Common Lisp does not specify the order of elements in the result of a call of ~c[intersection].~/" (mv-let (test args) (parse-args-and-test x '('eq 'eql 'equal) ''eql 'intersection$ form 'intersection$) (cond ((null args) (er hard 'intersection$ "Intersection$ requires at least one list argument. The call ~x0 is ~ thus illegal." form)) ((null (cdr args)) (car args)) (t (let* ((vars (make-var-lst 'x (length args))) (bindings (pairlis$ vars (pairlis$ args nil)))) (cond ((equal test ''eq) `(let-mbe ,bindings :logic ,(xxxjoin 'intersection-equal vars) :exec ,(xxxjoin 'intersection-eq-exec vars))) ((equal test ''eql) `(let-mbe ,bindings :logic ,(xxxjoin 'intersection-equal vars) :exec ,(xxxjoin 'intersection-eql-exec vars))) (t ; (equal test 'equal) `(xxxjoin 'intersection-equal ,args)))))))) (defun table-alist (name wrld) ; Return the named table as an alist. (declare (xargs :guard (and (symbolp name) (plist-worldp wrld)))) (getprop name 'table-alist nil 'current-acl2-world wrld)) (defun ruler-extenders-msg-aux (vals return-last-table) ; We return the intersection of vals with the symbols in the cdr of ; return-last-table. (declare (xargs :guard (and (symbol-listp vals) (symbol-alistp return-last-table)))) (cond ((endp return-last-table) nil) (t (let* ((first-cdr (cdar return-last-table)) (sym (if (consp first-cdr) (car first-cdr) first-cdr))) (cond ((member-eq sym vals) (cons sym (ruler-extenders-msg-aux vals (cdr return-last-table)))) (t (ruler-extenders-msg-aux vals (cdr return-last-table)))))))) (defun ruler-extenders-msg (x wrld) ; This message, if not nil, is passed to chk-ruler-extenders. (declare (xargs :guard (and (plist-worldp wrld) (symbol-alistp (fgetprop 'return-last-table 'table-alist nil wrld))))) (cond ((member-eq x '(:ALL :BASIC :LAMBDAS)) nil) ((and (consp x) (eq (car x) 'quote)) (msg "~x0 has a superfluous QUOTE, which you may wish to remove" x)) ((not (symbol-listp x)) (msg "~x0 is not a true list of symbols" x)) (t (let* ((vals (illegal-ruler-extenders-values x wrld)) (suspects (ruler-extenders-msg-aux vals (table-alist 'return-last-table wrld)))) (cond (vals (msg "~&0 ~#0~[is not a~/are not~] legal ruler-extenders ~ value~#0~[~/s~].~@1" vals (cond (suspects (msg " Note in particular that ~&0 ~#0~[is a ~ macro~/are macros~] that may expand to ~ calls of ~x1, which you may want to ~ specify instead." suspects 'return-last)) (t "")))) (t nil)))))) (defmacro chk-ruler-extenders (x soft ctx wrld) (let ((err-str "The proposed ruler-extenders is illegal because ~@0.")) `(let ((ctx ,ctx) (err-str ,err-str) (msg (ruler-extenders-msg ,x ,wrld))) (cond (msg ,(cond ((eq soft 'soft) `(er soft ctx err-str msg)) (t `(illegal ctx err-str (list (cons #\0 msg)))))) (t ,(cond ((eq soft 'soft) '(value t)) (t t))))))) (defmacro fixnum-bound () ; most-positive-fixnum in Allegro CL and many others (1- (expt 2 29))) (defconst *default-step-limit* ; The defevaluator event near the top of community book ; books/meta/meta-plus-equal.lisp, submitted at the top level without any ; preceding events, takes over 40,000 steps. Set the following to 40000 in ; order to make that event quickly exceed the default limit. (fixnum-bound)) (table acl2-defaults-table nil nil ; Warning: If you add a new key to this table, there will probably be a ; change you should make to a list in chk-embedded-event-form. (Search there ; for add-include-book-dir, and consider keeping that list alphabetical, just ; for convenience.) ; Developer suggestion: The following form provides an example of how to add a ; new key to the table guard, in this case, ; (setf (cadr (assoc-eq 'table-guard ; (get 'acl2-defaults-table *current-acl2-world-key*))) ; `(if (eq key ':new-key) ; (if (eq val 't) 't (symbol-listp val)) ; ,(cadr (assoc-eq 'table-guard ; (get 'acl2-defaults-table ; *current-acl2-world-key*))))) :guard (cond ((eq key :defun-mode) (member-eq val '(:logic :program))) ((eq key :verify-guards-eagerness) (member val '(0 1 2))) ((eq key :enforce-redundancy) (member-eq val '(t nil :warn))) ((eq key :ignore-doc-string-error) (member-eq val '(t nil :warn))) ((eq key :compile-fns) (member-eq val '(t nil))) ((eq key :measure-function) (and (symbolp val) (function-symbolp val world) ; The length expression below is just (arity val world) but we don't have arity ; yet. (= (length (getprop val 'formals t 'current-acl2-world world)) 1))) ((eq key :well-founded-relation) (and (symbolp val) (assoc-eq val (global-val 'well-founded-relation-alist world)))) ((eq key :bogus-defun-hints-ok) (member-eq val '(t nil :warn))) ((eq key :bogus-mutual-recursion-ok) (member-eq val '(t nil :warn))) ((eq key :irrelevant-formals-ok) (member-eq val '(t nil :warn))) ((eq key :ignore-ok) (member-eq val '(t nil :warn))) ((eq key :bdd-constructors) ; We could insist that the symbols are function symbols by using ; (all-function-symbolps val world), ; but perhaps one wants to set the bdd-constructors even before defining the ; functions. (symbol-listp val)) ((eq key :ttag) (or (null val) (and (keywordp val) (not (equal (symbol-name val) "NIL"))))) ((eq key :state-ok) (member-eq val '(t nil))) ; Rockwell Addition: See the doc string associated with ; set-let*-abstractionp. ((eq key :let*-abstractionp) (member-eq val '(t nil))) ; Rockwell Addition: See the doc string associated with ; set-nu-rewriter-mode. ((eq key :nu-rewriter-mode) (member-eq val '(nil t :literals))) ((eq key :backchain-limit) (and (true-listp val) (equal (length val) 2) (or (null (car val)) (natp (car val))) (or (null (cadr val)) (natp (cadr val))))) ((eq key :step-limit) (and (natp val) (<= val *default-step-limit*))) ((eq key :default-backchain-limit) (and (true-listp val) (equal (length val) 2) (or (null (car val)) (natp (car val))) (or (null (cadr val)) (natp (cadr val))))) ((eq key :rewrite-stack-limit) (unsigned-byte-p 29 val)) ((eq key :case-split-limitations) ; In set-case-split-limitations we permit val to be nil and default that ; to (nil nil). (and (true-listp val) (equal (length val) 2) (or (null (car val)) (natp (car val))) (or (null (cadr val)) (natp (cadr val))))) ((eq key :match-free-default) (member-eq val '(:once :all nil))) ((eq key :match-free-override) (or (eq val :clear) (null (non-free-var-runes val (free-var-runes :once world) (free-var-runes :all world) nil)))) ((eq key :match-free-override-nume) (integerp val)) ((eq key :non-linearp) (booleanp val)) ((eq key :tau-auto-modep) (booleanp val)) ((eq key :include-book-dir-alist) (and (include-book-dir-alistp val (os world)) (null (assoc-eq :SYSTEM val)))) ((eq key :ruler-extenders) (or (eq val :all) (chk-ruler-extenders val hard 'acl2-defaults-table world))) #+hons ((eq key :memoize-ideal-okp) (or (eq val :warn) (booleanp val))) (t nil))) (deflabel acl2-defaults-table :doc ":Doc-Section Other a ~il[table] specifying certain defaults, e.g., the default ~il[defun-mode]~/ ~bv[] Example Forms: (table acl2-defaults-table :defun-mode) ; current default defun-mode (table acl2-defaults-table :defun-mode :program) ; set default defun-mode to :program ~ev[]~/ ~l[table] for a discussion of tables in general. The legal keys for this ~il[table] are shown below. They may be accessed and changed via the general mechanisms provided by ~il[table]s. However, there are often more convenient ways to access and/or change the defaults. (See also the note below.) ~bv[] :defun-mode ~ev[] the default ~il[defun-mode], which must be ~c[:]~ilc[program] or ~c[:]~ilc[logic]. ~l[defun-mode] for a general discussion of ~il[defun-mode]s. The ~c[:]~ilc[defun-mode] key may be conveniently set by keyword commands naming the new ~il[defun-mode], ~c[:]~ilc[program] and ~c[:]~ilc[logic]. ~l[program] and ~pl[logic]. ~bv[] :enforce-redundancy ~ev[] if ~c[t], cause ACL2 to insist that most events are redundant (~pl[redundant-events]); if ~c[:warn], cause a warning instead of an error for such non-redundant events; else, ~c[nil]. ~l[set-enforce-redundancy]. ~bv[] :ignore-doc-string-error ~ev[] if ~c[t], cause ACL2 to ignore ill-formed ~il[documentation] strings rather than causing an error; if ~c[:warn], cause a warning instead of an error in such cases; else, ~c[nil] (the default). ~l[set-ignore-doc-string-error]. ~bv[] :verify-guards-eagerness ~ev[] an integer between 0 and 2 indicating how eager the system is to verify the ~il[guard]s of a ~il[defun] event. ~l[set-verify-guards-eagerness]. ~bv[] :compile-fns ~ev[] When this key's value is ~c[t], functions are compiled when they are ~ilc[defun]'d; otherwise, the value is ~c[nil]. (Except, this key's value is ignored when explicit compilation is suppressed; ~pl[compilation].) To set the flag, ~pl[set-compile-fns]. ~bv[] :measure-function ~ev[] the default measure function used by ~ilc[defun] when no ~c[:measure] is supplied in ~ilc[xargs]. The default measure function must be a function symbol of one argument. Let ~c[mfn] be the default measure function and suppose no ~c[:measure] is supplied with some recursive function definition. Then ~ilc[defun] finds the first formal, ~c[var], that is tested along every branch and changed in each recursive call. The system then ``guesses'' that ~c[(mfn var)] is the ~c[:measure] for that ~ilc[defun]. ~bv[] :well-founded-relation ~ev[] the default well-founded relation used by ~ilc[defun] when no ~c[:]~ilc[well-founded-relation] is supplied in ~ilc[xargs]. The default well-founded relation must be a function symbol, ~c[rel], of two arguments about which a ~c[:]~ilc[well-founded-relation] rule has been proved. ~l[well-founded-relation]. ~bv[] :bogus-defun-hints-ok ~ev[] When this key's value is ~c[t], ACL2 allows ~c[:hints] for nonrecursive function definitions. Otherwise, the value is the ~c[nil] (the default) or ~c[:warn] (which makes the check but merely warns when the check fails). ~l[set-bogus-defun-hints-ok]. ~bv[] :bogus-mutual-recursion-ok ~ev[] When this key's value is ~c[t], ACL2 skips the check that every function in a ~ilc[mutual-recursion] (or ~ilc[defuns]) ``clique'' calls at least one other function in that ``clique.'' Otherwise, the value is ~c[nil] (the default) or ~c[:warn] (which makes the check but merely warns when the check fails). ~l[set-bogus-mutual-recursion-ok]. ~bv[] :irrelevant-formals-ok ~ev[] When this key's value is ~c[t], the check for irrelevant formals is bypassed; otherwise, the value is the keyword ~c[nil] (the default) or ~c[:warn] (which makes the check but merely warns when the check fails). ~l[irrelevant-formals] and ~pl[set-irrelevant-formals-ok]. ~bv[] :ignore-ok ~ev[] When this key's value is ~c[t], the check for ignored variables is bypassed; otherwise, the value is the keyword ~c[nil] (the default) or ~c[:warn] (which makes the check but merely warns when the check fails). ~l[set-ignore-ok]. ~bv[] :bdd-constructors ~ev[] This key's value is a list of function symbols used to define the notion of ``BDD normal form.'' ~l[bdd-algorithm] and ~pl[hints]. ~bv[] :ttag ~ev[] This key's value, when non-~c[nil], allows certain operations that extend the trusted code base beyond what is provided by ACL2. ~l[defttag]. ~l[defttag]. ~bv[] :state-ok ~ev[] This key's value is either ~c[t] or ~c[nil] and indicates whether the user is aware of the syntactic restrictions on the variable symbol ~c[STATE]. ~l[set-state-ok]. ~bv[] :backchain-limit ~ev[] This key's value is a list of two ``numbers.'' Either ``number'' may optionally be ~c[nil], which is treated like positive infinity. The numbers control backchaining through hypotheses during type-set reasoning and rewriting. ~l[backchain-limit]. ~bv[] :default-backchain-limit ~ev[] This key's value is a list of two ``numbers.'' Either ``number'' may optionally be ~c[nil], which is treated like positive infinity. The numbers are used respectively to set the backchain limit of a rule if one has not been specified. ~l[backchain-limit]. ~bv[] :step-limit ~ev[] This key's value is either ~c[nil] or a natural number not exceeding the value of ~c[*default-step-limit*]. If the value is ~c[nil] or the value of ~c[*default-step-limit*], there is no limit on the number of ``steps'' that ACL2 counts during a proof: currently, the number of top-level rewriting calls. Otherwise, the value is the maximum number of such calls allowed during evaluation of any event. ~l[set-prover-step-limit]. ~bv[] :rewrite-stack-limit ~ev[] This key's value is a nonnegative integer less than ~c[(expt 2 28)]. It is used to limit the depth of calls of ACL2 rewriter functions. ~l[rewrite-stack-limit]. ~bv[] :let*-abstractionp ~ev[] This key affects how the system displays subgoals. The value is either ~c[t] or ~c[nil]. When t, let* expressions are introduced before printing to eliminate common subexpressions. The actual goal being worked on is unchanged. ~bv[] :nu-rewriter-mode ~ev[] This key's value is ~c[nil], ~c[t], or ~c[:literals]. When the value is non-~c[nil], the rewriter gives special treatment to expressions and functions defined in terms of ~ilc[nth] and ~ilc[update-nth]. See ~ilc[set-nu-rewriter-mode]. ~bv[] :case-split-limitations ~ev[] This key's value is a list of two ``numbers.'' Either ``number'' may optionally be ~c[nil], which is treated like positive infinity. The numbers control how the system handles case splits in the simplifier. ~l[set-case-split-limitations]. ~bv[] :include-book-dir-alist ~ev[] This key's value is used by ~ilc[include-book]'s ~c[:DIR] argument to associate a directory with a keyword. An exception is the keyword ~c[:SYSTEM] for the ~c[books/] directory; ~pl[include-book], in particular the section on ``Books Directory.'' ~bv[] :match-free-default ~ev[] This key's value is either ~c[:all], ~c[:once], or ~c[nil]. ~l[set-match-free-default]. ~bv[] :match-free-override ~ev[] This key's value is a list of runes. ~l[add-match-free-override]. ~bv[] :match-free-override-nume ~ev[] This key's value is an integer used in the implementation of ~il[add-match-free-override], so that only existing runes are affected by that event. ~bv[] :non-linearp ~ev[] This key's value is either ~c[t] or ~c[nil] and indicates whether the user wishes ACL2 to extend the linear arithmetic decision procedure to include non-linear reasoning. ~l[non-linear-arithmetic]. ~bv[] :tau-auto-modep ~ev[] This key's value is either ~c[t] or ~c[nil] and indicates whether the user wishes ACL2 to look for opportunities to create ~c[:]~ilc[tau-system] rules from all suitable ~c[defun]s and from all suitable ~c[defthm]s (with non-~c[nil] ~c[:]~ilc[rule-classes]). ~l[set-tau-auto-mode]. ~bv[] :ruler-extenders ~ev[] This key's value may be a list of symbols, indicating those function symbols that are not to block the collection of rulers; ~pl[defun]. Otherwise the value is ~c[:all] to indicate all function symbols, i.e., so that no function symbol blocks the collection of rulers. If a list is specified (rather than ~c[:all]), then it may contain the keyword ~c[:lambdas], which has the special role of specifying all ~c[lambda] applications. No other keyword is permitted in the list. ~l[ruler-extenders]. ~bv[] :memoize-ideal-okp ~ev[] This key is only legal in an experimental ~ilc[hons] version (~pl[hons-and-memoization]). Its value must be either ~c[t], ~c[nil], or ~c[:warn]. If the value is ~c[nil] or not present, then it is illegal by default to ~il[memoize] a ~c[:]~ilc[logic] mode function that has not been ~il[guard]-verified (~pl[verify-guards]), sometimes called an ``ideal-mode'' function. This illegality is the default because such calls of such functions in the ACL2 loop are generally evaluated in the logic (using so-called ``executable counterpart'' definitions), rather than directly by executing calls of the corresponding (memoized) raw Lisp function. However, such a raw Lisp call can be made when the function is called by a ~c[:]~ilc[program] mode function, so we allow you to override the default behavior by associating the value ~c[t] or ~c[:warn] with the key ~c[:memoize-ideal-okp], where with ~c[:warn] you get a suitable warning. Note that you can also allow memoization of ideal-mode functions by supplying argument ~c[:ideal-okp] to your memoization event (~pl[memoize]), in which case the value of ~c[:memoize-ideal-okp] in the ~c[acl2-defaults-table] is irrelevant. Note: Unlike all other ~il[table]s, ~c[acl2-defaults-table] can affect the soundness of the system. The ~il[table] mechanism therefore enforces on it a restriction not imposed on other ~il[table]s: when ~ilc[table] is used to update the ~c[acl2-defaults-table], the key and value must be variable-free forms. Thus, while ~bv[] (table acl2-defaults-table :defun-mode :program), (table acl2-defaults-table :defun-mode ':program), and (table acl2-defaults-table :defun-mode (compute-mode *my-data*)) ~ev[] are all examples of legal ~il[events] (assuming ~c[compute-mode] is a function of one non-~ilc[state] argument that produces a ~il[defun-mode] as its single value), ~bv[] (table acl2-defaults-table :defun-mode (compute-mode (w state))) ~ev[] is not legal because the value form is ~ilc[state]-sensitive. Consider for example the following three ~il[events] which one might make into the text of a book. ~bv[] (in-package \"ACL2\") (table acl2-defaults-table :defun-mode (if (ld-skip-proofsp state) :logic :program)) (defun crash-and-burn (x) (car x)) ~ev[] The second event is illegal because its value form is ~ilc[state]-sensitive. If it were not illegal, then it would set the ~c[:]~ilc[defun-mode] to ~c[:]~ilc[program] when the book was being certified but would set the ~il[defun-mode] to ~c[:]~ilc[logic] when the book was being loaded by ~ilc[include-book]. That is because during certification, ~ilc[ld-skip-proofsp] is ~c[nil] (proof obligations are generated and proved), but during book inclusion ~ilc[ld-skip-proofsp] is non-~c[nil] (those obligations are assumed to have been satisfied.) Thus, the above book, when loaded, would create a function in ~c[:]~ilc[logic] mode that does not actually meet the conditions for such status. For similar reasons, ~ilc[table] ~il[events] affecting ~c[acl2-defaults-table] are illegal within the scope of ~ilc[local] forms. That is, the text ~bv[] (in-package \"ACL2\") (local (table acl2-defaults-table :defun-mode :program)) (defun crash-and-burn (x) (car x)) ~ev[] is illegal because ~c[acl2-defaults-table] is changed locally. If this text were acceptable as a book, then when the book was certified, ~c[crash-and-burn] would be processed in ~c[:]~ilc[program] mode, but when the certified book was included later, ~c[crash-and-burn] would have ~c[:]~ilc[logic] mode because the ~ilc[local] event would be skipped. The text ~bv[] (in-package \"ACL2\") (program) ;which is (table acl2-defaults-table :defun-mode :program) (defun crash-and-burn (x) (car x)) ~ev[] is acceptable and defines ~c[crash-and-burn] in ~c[:]~ilc[program] mode, both during certification and subsequent inclusion. We conclude with an important observation about the relation between ~c[acl2-defaults-table] and ~ilc[include-book], ~ilc[certify-book], and ~ilc[encapsulate]. Including or certifying a book never has an effect on the ~c[acl2-defaults-table], nor does executing an ~ilc[encapsulate] event; we always restore the value of this ~il[table] as a final act. (Also ~pl[include-book], ~pl[encapsulate], and ~pl[certify-book].) That is, no matter how a book fiddles with the ~c[acl2-defaults-table], its value immediately after including that book is the same as immediately before including that book. If you want to set the ~c[acl2-defaults-table] in a way that persists, you need to do so using ~il[command]s that are not inside ~il[books]. It may be useful to set your favorite defaults in your ~ilc[acl2-customization] file; ~pl[acl2-customization].") #+acl2-loop-only (defmacro set-enforce-redundancy (x) ":Doc-Section switches-parameters-and-modes require most events to be redundant~/ ~bv[] General Forms: (set-enforce-redundancy nil) ; do not require redundancy (default) (set-enforce-redundancy t) ; most events (see below) must be redundant (set-enforce-redundancy :warn) ; warn for most non-redundant events ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~bv[] General Form: (set-enforce-redundancy flag) ~ev[] where ~c[flag] is ~c[nil], ~c[t], or ~c[:warn], as indicated above. This macro is essentially equivalent to ~bv[] (table acl2-defaults-table :enforce-redundancy flag) ~ev[] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. However, unlike the above simple call of the ~ilc[table] event function (~pl[table]), no output results from a ~c[set-enforce-redundancy] event. ~c[Set-enforce-redundancy] may be thought of as an event that merely sets a flag as indicated above, which determines whether most ~il[events], including ~ilc[defun] and ~ilc[defthm] events, are allowed to be redundant; ~pl[redundant-events]. The exceptions are ~ilc[deflabel], ~ilc[defpkg], ~ilc[encapsulate], ~ilc[include-book], ~ilc[push-untouchable], ~ilc[remove-untouchable], ~ilc[set-body], and ~ilc[table] ~il[events]. Any other type of non-redundant event will cause an error if ~c[flag] is ~c[t] and a warning if ~c[flag] is ~c[nil], ~em[except] in the course of carrying out an ~ilc[include-book] form. Note that because ~ilc[table] ~il[events] that set the ~ilc[acl2-defaults-table] are implicitly ~ilc[local], ~c[set-enforce-redundancy] events are ignored when including books. However, the presence of the event ~c[(set-enforce-redundancy t)] in a book guarantees that its subsequent definitions and theorems are redundant. This can be a useful property to maintain in library development, as we now describe. An example of the use of this form can be found in the community ~il[books] under directory ~c[books/rtl/rel4/]. The intention in that directory has been to put all the gory details in subdirectories ~c[support/] and ~c[arithmetic/], so that the books in subdirectory ~c[lib/] contain only the ``exported'' definitions and theorems. This approach is useful for human readability. Moreover, suppose we want to prove new theorems in ~c[lib/]. Typically we wish to prove the new theorems using the existing books in ~c[lib/]; however, our methodology demands that the proofs go into books in ~c[support/]. If every theorem in ~c[lib/] is redundant, then we can ~em[develop] the proofs in ~c[lib/] but then when we are done, ~em[move] each book with such proofs into ~c[support/] as follows. In any such book, we first replace ~ilc[include-book] forms referring to books in ~c[lib/] by ~ilc[include-book] forms referring to corresponding books in ~c[support/] and/or ~c[arithmetic/]. Then, we add suitable ~ilc[in-theory] events to get us back into the original ~c[lib/] proof environment. The default behavior of the system is as though the ~c[:enforce-redundancy] value is ~c[nil]. The current behavior can be ascertained by evaluating the following form. ~bv[] (cdr (assoc-eq :enforce-redundancy (table-alist 'acl2-defaults-table wrld))) ~ev[]" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :enforce-redundancy ,x) (table acl2-defaults-table :enforce-redundancy)))) #-acl2-loop-only (defmacro set-enforce-redundancy (x) (declare (ignore x)) nil) #+acl2-loop-only (defmacro set-ignore-doc-string-error (x) ":Doc-Section switches-parameters-and-modes allow ill-formed ~il[documentation] strings~/ ~bv[] General Forms: (set-ignore-doc-string-error nil) ; :doc strings must be well-formed (set-ignore-doc-string-error t) ; ill-formed :doc strings are ignored (set-ignore-doc-string-error :warn) ; ill-formed :doc strings are ignored ; except for causing a warning ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~bv[] General Form: (set-ignore-doc-string-error flag) ~ev[] where ~c[flag] is ~c[nil], ~c[t], or ~c[:warn], as indicated above. This macro is essentially equivalent to ~bv[] (table acl2-defaults-table :ignore-doc-string-error flag) ~ev[] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. However, unlike the above simple call of the ~ilc[table] event function (~pl[table]), no output results from a ~c[set-ignore-doc-string-error] event. Note that since ~ilc[defdoc] ~il[events] have the sole purpose of installing ~il[documentation] strings, these require well-formed documentation strings even after executing a call of ~c[ignore-doc-string-error]. The default behavior of the system is as though the ~c[:ignore-doc-string-error] value is ~c[nil]. The current behavior can be ascertained by evaluating the following form. ~bv[] (ignore-doc-string-error (w state)) ~ev[]" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :ignore-doc-string-error ,x) (table acl2-defaults-table :ignore-doc-string-error)))) #-acl2-loop-only (defmacro set-ignore-doc-string-error (x) (declare (ignore x)) nil) (defmacro default-verify-guards-eagerness-from-table (alist) `(or (cdr (assoc-eq :verify-guards-eagerness ,alist)) 1)) (defun default-verify-guards-eagerness (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (default-verify-guards-eagerness-from-table (table-alist 'acl2-defaults-table wrld))) #+acl2-loop-only (defmacro set-verify-guards-eagerness (x) ":Doc-Section switches-parameters-and-modes the eagerness with which ~il[guard] verification is tried.~/ ~bv[] Example Forms: try guard verification? (set-verify-guards-eagerness 0) ; no, unless :verify-guards t (set-verify-guards-eagerness 1) ; yes if a guard or type is supplied (set-verify-guards-eagerness 2) ; yes, unless :verify-guards nil ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~bv[] General Form: (set-verify-guards-eagerness n) ~ev[] where ~c[n] is a variable-free term that evaluates to ~c[0], ~c[1], or ~c[2]. This macro is essentially equivalent to ~bv[] (table acl2-defaults-table :verify-guards-eagerness n) ~ev[] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. However, unlike the above simple call of the ~ilc[table] event function (~pl[table]), no output results from a ~c[set-verify-guards-eagerness] event. ~c[Set-verify-guards-eagerness] may be thought of as an event that merely sets a flag to ~c[0], ~c[1], or ~c[2]. The flag is used by certain ~ilc[defun] ~il[events] to determine whether ~il[guard] verification is tried. The flag is irrelevant to those ~ilc[defun] ~il[events] in ~c[:]~ilc[program] mode and to those ~ilc[defun] ~il[events] in which an explicit ~c[:]~ilc[verify-guards] setting is provided among the ~ilc[xargs]. In the former case, ~il[guard] verification is not done because it can only be done when logical functions are being defined. In the latter case, the explicit ~c[:]~ilc[verify-guards] setting determines whether ~il[guard] verification is tried. So consider a ~c[:]~ilc[logic] mode ~ilc[defun] in which no ~c[:]~ilc[verify-guards] setting is provided. Is ~il[guard] verification tried? The answer depends on the eagerness setting as follows. If the eagerness is ~c[0], ~il[guard] verification is not tried. If the eagerness is ~c[1], it is tried if and only if a guard is explicitly specified in the ~ilc[defun], in the following sense: there is an ~c[xargs] keyword ~c[:guard] or ~c[:stobjs] or a ~ilc[type] declaration. If the eagerness is ~c[2], ~il[guard] verification is tried. The default behavior of the system is as though the ~c[:verify-guards-eagerness] is ~c[1]. The current behavior can be ascertained by evaluating the form ~c[(default-verify-guards-eagerness (w state))]." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :verify-guards-eagerness ,x) (table acl2-defaults-table :verify-guards-eagerness)))) #-acl2-loop-only (defmacro set-verify-guards-eagerness (x) (declare (ignore x)) nil) (defun default-compile-fns (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (cdr (assoc-eq :compile-fns (table-alist 'acl2-defaults-table wrld)))) #+acl2-loop-only (defmacro set-compile-fns (x) ":Doc-Section switches-parameters-and-modes have each function compiled as you go along.~/ ~bv[] Example Forms: (set-compile-fns t) ; new functions compiled after DEFUN (set-compile-fns nil) ; new functions not compiled after DEFUN ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Also ~pl[comp], because it may be more efficient in some Common Lisps to compile many functions at once rather than to compile each one as you go along.~/ ~bv[] General Form: (set-compile-fns term) ~ev[] where ~c[term] is a variable-free term that evaluates to ~c[t] or ~c[nil]. This macro is equivalent to ~bv[] (table acl2-defaults-table :compile-fns term) ~ev[] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. However, unlike the above simple call of the ~ilc[table] event function (~pl[table]), no output results from a ~c[set-compile-fns] event. ~c[Set-compile-fns] may be thought of as an event that merely sets a flag to ~c[t] or ~c[nil]. The flag's effect is felt when functions are defined, as with ~ilc[defun]. If the flag is ~c[t], functions are automatically compiled after they are defined, as are their executable counterparts (~pl[executable-counterpart]). Otherwise, functions are not automatically compiled. Exception: The flag has no effect when explicit compilation is suppressed; ~pl[compilation]. Because ~c[set-compile-fns] is an event, the old value of the flag is restored when a ~c[set-compile-fns] event is undone. Even when ~c[:set-compile-fns t] has been executed, functions are not individually compiled when processing an ~ilc[include-book] event. If you wish to include a book of compiled functions, we suggest that you first certify it with the ~il[compilation] flag set (~pl[certify-book]) or else compile the book by supplying the appropriate ~c[load-compiled-file] argument to ~ilc[include-book]. More generally, ~il[compilation] via ~c[set-compile-fns] is suppressed when the ~il[state] global variable ~ilc[ld-skip-proofsp] has value ~c[']~ilc[include-book].~/ :cited-by Programming" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :compile-fns ,x) (table acl2-defaults-table :compile-fns)))) #-acl2-loop-only (defmacro set-compile-fns (x) (declare (ignore x)) nil) (defun set-compiler-enabled (val state) ; We disallow the modification of 'compiler-enabled while inside include-book ; or certify-book, simply because it's too strange to contemplate; we think of ; 'compiler-enabled as a global property affecting defaults for certify-book ; and include-book. (declare (xargs :guard (and (member-eq val '(t nil :books)) (boundp-global 'certify-book-info state)) :stobjs state)) #-acl2-loop-only (when *inside-include-book-fn* (let ((str "It is illegal to call set-compiler-enabled inside include-book.")) (illegal 'set-compiler-enabled str nil) (error str) ; in surprising case that illegal doesn't cause an error )) (cond ((f-get-global 'certify-book-info state) (prog2$ (hard-error 'set-compiler-enabled "It is illegal to call set-compiler-enabled ~ inside certify-book." nil) state)) (t (f-put-global 'compiler-enabled val state)))) (defun default-measure-function (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (or (cdr (assoc-eq :measure-function (table-alist 'acl2-defaults-table wrld))) 'acl2-count)) #+acl2-loop-only (defmacro set-measure-function (name) ":Doc-Section switches-parameters-and-modes set the default measure function symbol~/ ~bv[] Examples: (set-measure-function nqthm::count) ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~bv[] General Form: (set-measure-function name) ~ev[] where ~c[name] is a function symbol of one argument. This macro is equivalent to ~c[(table acl2-defaults-table :measure-function 'name)], and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. Although this is thus an event (~pl[table]), nevertheless no output results from a ~c[set-measure-function] event. This event sets the default measure function to ~c[name]. Subsequently, if a recursively defined function is submitted to ~ilc[defun] with no explicitly given ~c[:measure] argument, ~ilc[defun] ``guesses'' the measure ~c[(name var)], where ~c[name] is the then current default measure function and ~c[var] is the first formal found to be tested along every branch and changed in every recursive call. Note that if ~c[(table acl2-defaults-table :measure-function 'name)] has its default value of ~c[nil], then the default measure function is ~ilc[acl2-count].~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :measure-function ',name) (table acl2-defaults-table :measure-function)))) #-acl2-loop-only (defmacro set-measure-function (name) (declare (ignore name)) nil) (defun default-well-founded-relation (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (or (cdr (assoc-eq :well-founded-relation (table-alist 'acl2-defaults-table wrld))) 'o<)) #+acl2-loop-only (defmacro set-well-founded-relation (rel) ":Doc-Section switches-parameters-and-modes set the default well-founded relation~/ ~bv[] Examples: (set-well-founded-relation lex2) ~ev[] provided ~c[lex2] has been proved to be a well-founded relation (~pl[well-founded-relation]). Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~bv[] General Form: (set-well-founded-relation rel) ~ev[] where ~c[rel] has been proved to be a well-founded relation on objects satisfying some predicate, ~c[mp]; ~pl[well-founded-relation]. This macro is equivalent to ~c[(table acl2-defaults-table :well-founded-relation 'rel)], and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. This event sets the default well-founded relation to be that imposed on ~c[mp]-measures by the relation ~c[rel]. Subsequently, if a recursively defined function is submitted to ~ilc[defun] with no explicitly given ~c[:]~ilc[well-founded-relation] argument, ~ilc[defun] uses the default relation, ~c[rel], and the associated domain predicate ~c[mp] used in its well-foundedness theorem. That is, the termination conditions generated will require proving that the measure used by the ~ilc[defun] is an ~c[mp]-measure and that in every recursive call the measure of the arguments decreases according to ~c[rel].~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :well-founded-relation ',rel) (table acl2-defaults-table :well-founded-relation)))) #-acl2-loop-only (defmacro set-well-founded-relation (rel) (declare (ignore rel)) nil) ; Another default is the defun-mode. (defmacro default-defun-mode-from-table (alist) `(let ((val (cdr (assoc-eq :defun-mode ,alist)))) (if (member-eq val '(:logic :program)) ; from table guard val ; We set the default-defun-mode to :program when val is NIL, which is ; the case for boot-strapping. :program))) (defun default-defun-mode (wrld) ":Doc-Section Miscellaneous the default ~il[defun-mode] of ~ilc[defun]'d functions~/ When a ~ilc[defun] is processed and no ~c[:mode] ~c[xarg] is supplied, the function ~c[default-defun-mode] is used. To find the default ~il[defun-mode] of the current ACL2 ~il[world], type ~c[(default-defun-mode (w state))]. ~l[defun-mode] for a discussion of ~il[defun-mode]s. To change the default ~il[defun-mode] of the ACL2 ~il[world], type one of the keywords ~c[:]~ilc[program] or ~c[:]~ilc[logic].~/ The default ACL2 ~il[prompt] displays the current default ~il[defun-mode] by showing the character ~c[p] for ~c[:]~ilc[program] mode, and omitting it for ~c[:]~ilc[logic] mode; ~pl[default-print-prompt]. The default ~il[defun-mode] may be changed using the keyword ~il[command]s ~c[:]~ilc[program] and ~c[:]~ilc[logic], which are equivalent to the ~il[command]s ~c[(program)] and ~c[(logic)]. Each of these names is documented separately: ~pl[program] and ~pl[logic]. The default ~il[defun-mode] is stored in the ~il[table] ~ilc[acl2-defaults-table] and hence may also be changed by a ~ilc[table] ~il[command]. ~l[table] and also ~pl[acl2-defaults-table]. Both mode-changing ~il[command]s are ~il[events]. While ~il[events] that change the default ~il[defun-mode] are permitted within an ~ilc[encapsulate] or the text of a book, their effects are ~ilc[local] in scope to the duration of the encapsulation or inclusion. For example, if the default ~il[defun-mode] is ~c[:]~ilc[logic] and a book is included that contains the event ~c[(program)], then subsequent ~il[events] within the book are processed with the default ~il[defun-mode] ~c[:]~ilc[program]; but when the ~ilc[include-book] event completes, the default ~il[defun-mode] will still be ~c[:]~ilc[logic]. ~il[Command]s that change the default ~il[defun-mode] are not permitted inside ~ilc[local] forms.~/" (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (default-defun-mode-from-table (table-alist 'acl2-defaults-table wrld))) ; The following is used in the definition of when-logic, in order to provide ; something limited to put on the chk-new-name-lst of the primordial world. (defun default-defun-mode-from-state (state) (declare (xargs :guard (state-p state))) (default-defun-mode (w state))) #+acl2-loop-only (defmacro logic nil ":Doc-Section switches-parameters-and-modes to set the default ~il[defun-mode] to ~c[:logic]~/ ~bv[] Example: ACL2 p!>:logic ACL2 !> ~ev[] Typing the keyword ~c[:logic] sets the default ~il[defun-mode] to ~c[:logic]. Functions defined in ~c[:logic] mode are logically defined. ~l[defun-mode]. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~l[defun-mode] for a discussion of the ~il[defun-mode]s available and what their effects on the logic are. ~l[default-defun-mode] for a discussion of how the default ~il[defun-mode] is used. This event is equivalent to ~c[(table acl2-defaults-table :defun-mode :logic)], and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs. ~l[acl2-defaults-table]. Recall that the top-level form ~c[:logic] is equivalent to ~c[(logic)]; ~pl[keyword-commands]. Thus, to change the default ~il[defun-mode] to ~c[:logic] in a book, use ~c[(logic)], which is an embedded event form, rather than ~c[:logic], which is not a legal form for ~il[books]. ~l[embedded-event-form]." '(state-global-let* ((inhibit-output-lst (list* 'summary (@ inhibit-output-lst)))) (er-progn (table acl2-defaults-table :defun-mode :logic) (value :invisible)))) #-acl2-loop-only (defmacro logic () nil) #+acl2-loop-only (defmacro program nil ":Doc-Section switches-parameters-and-modes to set the default ~il[defun-mode] to ~c[:]~ilc[program]~/ ~bv[] Example: ACL2 !>:program ACL2 p!> ~ev[] Typing the keyword ~c[:program] sets the default ~il[defun-mode] to ~c[:program]. Functions defined in ~c[:program] mode are logically undefined but can be executed on constants outside of deductive contexts. ~l[defun-mode]. Calls of the following macros are ignored (skipped) when in ~c[:program] mode. ~bv[] local verify-guards verify-termination defaxiom defthm deftheory in-theory in-arithmetic-theory regenerate-tau-database theory-invariant defchoose ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/ ~l[defun-mode] for a discussion of the ~il[defun-mode]s available and what their effects on the logic are. ~l[default-defun-mode] for a discussion of how the default ~il[defun-mode] is used. This event is equivalent to ~c[(table acl2-defaults-table :defun-mode :program)], and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs. ~l[acl2-defaults-table]. Recall that the top-level form ~c[:program] is equivalent to ~c[(program)]; ~pl[keyword-commands]. Thus, to change the default ~il[defun-mode] to ~c[:program] in a book, use ~c[(program)], which is an embedded event form, rather than ~c[:program], which is not a legal form for ~il[books]. ~l[embedded-event-form]." '(state-global-let* ((inhibit-output-lst (list* 'summary (@ inhibit-output-lst)))) (er-progn (table acl2-defaults-table :defun-mode :program) (value :invisible)))) #-acl2-loop-only (defmacro program () nil) (defun invisible-fns-table (wrld) ":Doc-Section switches-parameters-and-modes functions that are invisible to the ~il[loop-stopper] algorithm~/ ~bv[] Examples: ACL2 !>(invisible-fns-table (w state)) ((binary-+ unary--) (binary-* unary-/) (unary-- unary--) (unary-/ unary-/)) ~ev[] Among other things, the setting above has the effect of making ~ilc[unary--] ``invisible'' for the purposes of applying permutative ~c[:]~ilc[rewrite] rules to ~ilc[binary-+] trees. Also ~pl[add-invisible-fns] and ~pl[remove-invisible-fns], which manage macro aliases (~pl[macro-aliases-table]), as well as ~pl[set-invisible-fns-table]. ~l[table] for a general discussion of tables.~/ The ``invisible functions ~il[table]'' is an alist with elements of the following form, where ~c[fn] is a function symbol and the ~c[ufni] are unary function symbols in the current ACL2 ~il[world], and ~c[k] is at least 1. ~bv[] (fn ufn1 ufn2 ... ufnk) ~ev[] This ~il[table] thus associates with certain function symbols, e.g., ~c[fn] above, a set of unary functions, e.g., the ~c[ufni] above. The ~c[ufni] associated with ~c[fn] in the invisible functions table are said to be ``invisible with respect to ~c[fn].'' If ~c[fn] is not the ~ilc[car] of any pair in the ~c[alist], then no function is invisible for it. Thus for example, setting the invisible functions alist to ~c[nil] completely eliminates the consideration of invisibility. The notion of invisibility is involved in the use of the ~c[:]~ilc[loop-stopper] field of ~c[:]~ilc[rewrite] rules to prevent the indefinite application of permutative rewrite rules. Roughly speaking, if rewrite rules are being used to permute ~c[arg] and (ufni arg) inside of a nest of ~c[fn] calls, and ~c[ufni] is invisible with respect to ~c[fn], then ~c[arg] and ~c[(ufni arg)] are considered to have the same ``weight'' and will be permuted so as to end up as adjacent tips in the ~c[fn] nest. ~l[loop-stopper].~/" (declare (xargs :guard (plist-worldp wrld))) (table-alist 'invisible-fns-table wrld)) (defmacro set-invisible-fns-table (alist) ":Doc-Section switches-parameters-and-modes set the invisible functions table~/ ~bv[] Examples: (set-invisible-fns-table ((binary-+ unary--) (binary-* unary-/) (unary-- unary--) (unary-/ unary-/))) (set-invisible-fns-table t) ; restore original invisible-fns-table ~ev[] Among other things, the setting above has the effect of making ~ilc[unary--] ``invisible'' for the purposes of applying permutative ~c[:]~ilc[rewrite] rules to ~ilc[binary-+] trees. Thus, ~c[arg] and ~c[(unary-- arg)] will be given the same weight and will be permuted so as to be adjacent. The form ~c[(invisible-fns-table (w state))] returns the current value of the invisible functions table. Also ~pl[add-invisible-fns] and ~pl[remove-invisible-fns] for events that add to and remove from the invisible functions table, while accounting for macro aliases (~pl[macro-aliases-table]).~/ ~bv[] General Form: (set-invisible-fns-table alist) ~ev[] where ~c[alist] is either ~c[t] or a true list of pairs, each element of which is of the form ~c[(fn ufn1 ... ufnk)], where ~c[fn] is a function symbol and each ~c[ufni] is a unary function symbol. When alist is ~c[t], the initial value of this table is used in its place. Modulo the replacement of ~c[alist] by the default setting when ~c[alist] is ~c[t], this macro is equivalent to ~bv[] (table invisible-fns-table nil 'alist :clear) ~ev[] which is also an event (~pl[table]). Note that ~c[set-invisible-fns-table] does not evaluate its argument. However, you can call ~ilc[table] directly for that purpose. For example, ~bv[] (set-invisible-fns-table ((binary-+ unary--) (binary-* unary-/) (unary-- unary--) (unary-/ unary-/))) ~ev[] ie equivalent to the following; ~pl[table]. ~bv[] (table invisible-fns-table nil (quote ((binary-+ unary--) (binary-* unary-/) (unary-- unary--) (unary-/ unary-/))) :clear) ~ev[] ~l[invisible-fns-table] for a description of the invisible functions table.~/" `(table invisible-fns-table nil ',(cond ((eq alist t) ; We provide the alist = t setting mainly so the user can always ; obtain the initial setting. But we also use it ourselves in a call ; of (set-invisible-fns-table t) below that initialize the table. '((binary-+ unary--) (binary-* unary-/) (unary-- unary--) (unary-/ unary-/))) (t alist)) :clear)) (defun unary-function-symbol-listp (lst wrld) (declare (xargs :guard (plist-worldp wrld))) (cond ((atom lst) (null lst)) (t (and (symbolp (car lst)) ; The length expression below is roughly arity, which could have been used ; instead except that it is not defined yet in axioms.lisp. Note that since ; (length nil) = 1, this works even when we have do not have a ; function-symbolp. Actually we avoid length in order to ease the ; guard verification process at this point. ; (= (length formals) 1)... (let ((formals (getprop (car lst) 'formals nil 'current-acl2-world wrld))) (and (consp formals) (null (cdr formals)))) (unary-function-symbol-listp (cdr lst) wrld))))) (defun invisible-fns-entryp (key val wrld) (declare (xargs :guard (plist-worldp wrld))) (and (symbolp key) (function-symbolp key wrld) (unary-function-symbol-listp val wrld))) (table invisible-fns-table nil nil :guard (invisible-fns-entryp key val world)) (set-invisible-fns-table t) (defmacro add-invisible-fns (top-fn &rest unary-fns) ":Doc-Section switches-parameters-and-modes make some unary functions invisible to the ~il[loop-stopper] algorithm~/ ~bv[] Examples: (add-invisible-fns binary-+ unary-- foo) (add-invisible-fns + unary-- foo) ~ev[] Each of the ~il[events] above makes unary functions ~ilc[unary--] and ~c[foo] ``invisible'' for the purposes of applying permutative ~c[:]~ilc[rewrite] rules to ~ilc[binary-+] trees. Thus, ~c[arg] and ~c[(unary-- arg)] will be given the same weight and will be permuted so as to be adjacent.~/ ~bv[] General Form: (add-invisible-fns top-fn unary-fn1 ... unary-fnk) ~ev[] where ~c[top-fn] is a function symbol and the ~c[unary-fni] are unary function symbols, or more generally, these are all macro aliases for function symbols (~pl[macro-aliases-table]). For more information ~pl[invisible-fns-table]. Also ~pl[set-invisible-fns-table], which explains how to set the entire table in a single event, and ~pl[remove-invisible-fns].~/" `(table invisible-fns-table nil (let* ((tbl (table-alist 'invisible-fns-table world)) (macro-aliases (macro-aliases world)) (top-fn (deref-macro-name ',top-fn macro-aliases)) (old-entry (assoc-eq top-fn tbl)) (unary-fns (deref-macro-name-lst ',unary-fns macro-aliases))) (if (not (subsetp-eq unary-fns (cdr old-entry))) (put-assoc-eq top-fn (union-eq unary-fns (cdr old-entry)) tbl) (prog2$ (cw "~%NOTE: Add-invisible-fns did not change the ~ invisible-fns-table. Consider using :u or :ubt to ~ undo this event.~%") tbl))) :clear)) (defmacro remove-invisible-fns (top-fn &rest unary-fns) ":Doc-Section switches-parameters-and-modes make some unary functions no longer invisible~/ ~bv[] Examples: (remove-invisible-fns (binary-+ unary-- foo) (remove-invisible-fns (+ unary-- foo) ~ev[] The setting above has makes unary functions ~ilc[unary--] and ~c[foo] no longer ``invisible'' for the purposes of applying permutative ~c[:]~ilc[rewrite] rules to ~ilc[binary-+] trees.~/ ~bv[] General Form: (remove-invisible-fns top-fn unary-fn1 ... unary-fnk) ~ev[] where ~c[top-fn] is a function symbol and the ~c[unary-fni] are unary function symbols, or more generally, these are all macro aliases for function symbols (~pl[macro-aliases-table]). ~l[add-invisible-fns] and also ~pl[invisible-fns-table] and ~pl[set-invisible-fns-table].~/" `(table invisible-fns-table nil (let* ((tbl (table-alist 'invisible-fns-table world)) (macro-aliases (macro-aliases world)) (top-fn (deref-macro-name ',top-fn macro-aliases)) (old-entry (assoc-eq top-fn tbl)) (unary-fns (deref-macro-name-lst ',unary-fns macro-aliases))) (if (intersectp-eq unary-fns (cdr old-entry)) (let ((diff (set-difference-eq (cdr old-entry) unary-fns))) (if diff (put-assoc-eq top-fn diff tbl) (delete-assoc-eq top-fn tbl))) (prog2$ (cw "~%NOTE: Remove-invisible-fns did not change the ~ invisible-fns-table. Consider using :u or :ubt to ~ undo this event.~%") tbl))) :clear)) ; The following two definitions are included to help users transition from ; Version_2.6 to Version_2.7 (where [set-]invisible-fns-alist was replaced by ; [set-]invisible-fns-table). (defmacro set-invisible-fns-alist (alist) (declare (ignore alist)) '(er hard 'set-invisible-fns-alist "Set-invisible-fns-alist has been replaced by set-invisible-fns-table. ~ See :DOC invisible-fns-table. Also see :DOC add-invisible-fns and see ~ :DOC remove-invisible-fns.")) (defmacro invisible-fns-alist (wrld) (declare (ignore wrld)) '(er hard 'invisible-fns-alist "Invisible-fns-alist has been replaced by invisible-fns-table. Please ~ see :DOC invisible-fns-table.")) #+acl2-loop-only (defmacro set-bogus-defun-hints-ok (x) ":Doc-Section switches-parameters-and-modes allow unnecessary ``mutual recursion'' ~/ ~bv[] General Forms: (set-bogus-defun-hints-ok t) (set-bogus-defun-hints-ok nil) (set-bogus-defun-hints-ok :warn) ~ev[] By default, ACL2 causes an error when the keyword ~c[:]~ilc[hints] is supplied in an ~ilc[xargs] ~il[declare] form for a definition (~pl[defun]). This behavior can be defeated with ~c[(set-bogus-defun-hints-ok t)], or if you still want to see a warning in such cases, ~c[(set-bogus-defun-hints-ok :warn)].~/~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :bogus-defun-hints-ok ,x) (table acl2-defaults-table :bogus-defun-hints-ok)))) #-acl2-loop-only (defmacro set-bogus-defun-hints-ok (x) (declare (ignore x)) nil) #+acl2-loop-only (defmacro set-bogus-mutual-recursion-ok (x) ":Doc-Section switches-parameters-and-modes allow unnecessary ``mutual recursion'' ~/ ~bv[] Examples: (set-bogus-mutual-recursion-ok t) (set-bogus-mutual-recursion-ok nil) (set-bogus-mutual-recursion-ok :warn) ~ev[] By default, ACL2 checks that when a ``clique'' of more than one function is defined simultaneously (using ~ilc[mutual-recursion] or ~ilc[defuns]), then every body calls at least one of the functions in the ``clique.'' Below, we refer to definitional events that fail this check as ``bogus'' mutual recursions. The check is important because ACL2 does not store induction schemes for functions defined with other functions in a ~ilc[mutual-recursion] or ~ilc[defuns] event. Thus, ACL2 may have difficulty proving theorems by induction that involve such functions. Moreover, the check can call attention to bugs, since users generally intend that their mutual recursions are not bogus. Nevertheless, there are times when it is advantageous to allow bogus mutual recursions, for example when they are generated mechanically, even at the expense of losing stored induction schemes. The first example above allows bogus mutual recursion. The second example disallows bogus mutual recursion; this is the default. The third example allows bogus mutual recursion, but prints an appropriate warning. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table].~/ ~bv[] General Form: (set-bogus-mutual-recursion-ok flg) ~ev[] where ~c[flg] is either ~c[t], ~c[nil], or ~c[:warn].~/ :cited-by Programming" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :bogus-mutual-recursion-ok ,x) (table acl2-defaults-table :bogus-mutual-recursion-ok)))) #-acl2-loop-only (defmacro set-bogus-mutual-recursion-ok (x) (declare (ignore x)) nil) (defdoc ruler-extenders ":Doc-Section switches-parameters-and-modes control for ACL2's termination and induction analyses~/ ~st[Introduction] Consider the following recursive definition, which returns a list of threes of length one more than the length of ~c[x]. ~bv[] (defun f (x) (cons 3 (if (consp x) (f (cdr x)) nil))) ~ev[] One might expect ACL2's termination analysis to admit this function, since we know that ~c[(cdr x)] is ``smaller'' than ~c[x] if ~c[(consp x)] is true. (By default, ACL2's notion of ``smaller'' is ordinary natural-number ~c[<], and the argument ~c[x] is measured by applying function ~c[acl2-count] to ~c[x].) However, that termination analysis does not consider ~ilc[IF] tests, like ~c[(consp x)] above, when they occur under calls of functions other than ~c[IF], such as ~c[CONS] in the case above. One way to overcome this problem is to ``lift'' the ~c[IF] test to the top level, as follows. ~bv[] (defun f (x) (if (consp x) (cons 3 (f (cdr x))) (cons 3 nil))) ~ev[] But another way to overcome the problem is to tell ACL2 to extend its termination (and induction) analysis through calls of ~c[cons], as follows. ~bv[] (defun f (x) (declare (xargs :ruler-extenders (cons))) (cons 3 (if (consp x) (f (cdr x)) nil))) ~ev[] You may even wish to provide value ~c[:all] instead of an explicit list of ruler-extenders, so that no function call blocks the termination analysis: ~bv[] (defun f (x) (declare (xargs :ruler-extenders :all)) (cons 3 (if (consp x) (f (cdr x)) nil))) ~ev[] Alternatively, you can omit the ~c[XARGS] ~c[:RULER-EXTENDERS] form, instead modifying the global default set of ruler-extenders: ~bv[] (set-ruler-extenders :all) ; or, for example: (set-ruler-extenders '(cons return-last)) ~ev[] You can call the function ~ilc[default-ruler-extenders] as follows to see the current global default set of ruler-extenders: ~bv[] (default-ruler-extenders (w state)) ~ev[] We conclude this introduction by considering the handling of ~c[LET] expressions by termination analysis. Consider the following example. ~bv[] (defun fact (n) (the (integer 1 *) (if (posp n) (* n (fact (1- n))) 1))) ~ev[] ACL2 treats the call of ~ilc[THE] in the body of this definition as follows. ~bv[] (let ((var (if (posp n) (* n (fact (1- n))) 1))) (if (and (integerp var) (<= 1 var)) var )) ~ev[] A ~ilc[LET] expression, in turn, is treated as a ~ilc[LAMBDA] application: ~bv[] ((lambda (var) (if (if (integerp var) (not (< var 1)) nil) var )) (if (posp n) (* n (fact (1- n))) 1)) ~ev[] Notice that the ~ilc[posp] test, which governs the recursive call of ~c[fact], is inside an argument of a function application, namely the application of the ~c[LAMBDA] expression. So by default, ACL2 will not consider this ~ilc[posp] test in its termination analysis. The keyword ~c[:LAMBDAS] in the list of ruler-extenders denotes all calls of lambda expressions, much as the inclusion of ~c[CONS] in the ruler-extenders denotes all calls of ~c[CONS]. The following definition is thus accepted by ACL2. ~bv[] (defun fact (n) (declare (xargs :ruler-extenders (:lambdas))) (the (integer 1 *) (if (posp n) (* n (fact (1- n))) 1))) ~ev[] As a convenience, ACL2 allows the symbol ~c[:lambdas] in place of ~c[(:lambdas)], and in fact the former will also include the default ruler-extenders: ~ilc[RETURN-LAST] (which comes from macroexpansion of calls of ~ilc[PROG2$], ~ilc[EC-CALL], and others) and ~ilc[MV-LIST]. IMPORTANT REMARKS. (1) Notice that the argument to ~c[set-ruler-extenders] is evaluated, but the argument to ~c[:RULER-EXTENDERS] in ~c[XARGS] is not evaluated. (2) Do not put macro names in your list of ruler-extenders. For example, if you intend that ~c[+] should not block the termination analysis, in analogy to ~c[cons] in the example above, then the list of ruler-extenders should include ~c[binary-+], not ~c[+]. Of course, if you use ~c[:all] then this is not an issue, but see the next remark. (3) Also please note that by taking advantage of the ruler-extenders, you may be complicating the induction scheme stored for the function, whose computation takes similar advantage of the additional ~c[IF] structure that you are specifying. Below we describe the notion of ruler-extenders in detail, as well as how to set its default using ~c[set-ruler-extenders]. ~st[Details] We begin by discussing how to set the ruler-extenders by using the macro ~c[set-ruler-extenders]; below we will discuss the use of keyword ~c[:ruler-extenders] in ~ilc[XARGS] ~ilc[declare] forms. ~bv[] Examples: (set-ruler-extenders :basic) ; return to default (set-ruler-extenders *basic-ruler-extenders*) ; same as immediately above (set-ruler-extenders :all) ; every governing IF test rules a recursive call (set-ruler-extenders :lambdas) ; LET does not block termination analysis (set-ruler-extenders (cons :lambdas *basic-ruler-extenders*)) ; same as immediately above (set-ruler-extenders '(f g)) ; termination analysis goes past calls of f, g General Form: (set-ruler-extenders val) ~ev[] where ~c[val] evaluates to one of ~c[:basic], ~c[:all], ~c[:lambdas], or a true list of symbols containing no keyword other than, optionally, ~c[:lambdas].~/ When a recursive definition is submitted to ACL2 (in ~c[:]~ilc[logic] mode), the recursion must be proved to terminate; ~pl[defun]. More precisely, ACL2 explores the ~ilc[IF] structure of the body of the definition to accumulate the tests that ``rule'' any given recursive call. The following example reviews how this works. Suppose that ~c[f] has already been defined. ~bv[] (defun g (x y) (declare (xargs :measure (+ (acl2-count x) (acl2-count y)))) (if (consp x) (g (cdr x) y) (if (consp y) (f (g x (cdr y))) (f (list x y))))) ~ev[] ACL2 makes the following response to this proposed definition. Notice that the ~c[:measure] proposed above must be proved to be an ACL2 ordinal ~-[] that is, to satisfy ~c[O-P] ~-[] and that the arguments to each recursive call must be smaller (in the sense of that measure and ~c[O<], which here reduces to the ordinary ~c[<] relation) than the formals under the assumption of the ruling ~c[IF] tests. The first ~c[IMPLIES] term below thus corresponds to the recursive call ~c[(g (cdr x) y)], while the second corresponds to the recursive call ~c[(g x (cdr y))]. ~bv[] For the admission of G we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (+ (ACL2-COUNT X) (ACL2-COUNT Y)). The non-trivial part of the measure conjecture is Goal (AND (O-P (+ (ACL2-COUNT X) (ACL2-COUNT Y))) (IMPLIES (CONSP X) (O< (+ (ACL2-COUNT (CDR X)) (ACL2-COUNT Y)) (+ (ACL2-COUNT X) (ACL2-COUNT Y)))) (IMPLIES (AND (NOT (CONSP X)) (CONSP Y)) (O< (+ (ACL2-COUNT X) (ACL2-COUNT (CDR Y))) (+ (ACL2-COUNT X) (ACL2-COUNT Y))))). ~ev[] Now consider the following alternate version of the above definition. ~bv[] (defun g (x y) (declare (xargs :measure (+ (acl2-count x) (acl2-count y)))) (if (consp x) (g (cdr x) y) (f (if (consp y) (g x (cdr y)) (list x y))))) ~ev[] The first test, ~c[(consp x)], still rules the first recursive call, ~c[(g (cdr x) y)]. And the negation of that test, namely ~c[(not (consp x))], still rules the second recursive call ~c[(g x (cdr y))]. But the call of ~c[f] blocks the top-down exploration of the ~c[IF] structure of the body of ~c[g], so ~c[(consp y)] does not rule that second recursive call, which (again) is ~c[(g x (cdr y))]. As a result, ACL2 fails to admit the above definition. ~c[Set-ruler-extenders] is provided to overcome the sort of blocking described above. Suppose for example that the following event is submitted: ~bv[] (set-ruler-extenders '(f)) ~ev[] Then the alternate definition of ~c[g] above is admissible, because the call of ~c[f] no longer blocks the top-down exploration of the ~c[IF] structure of the body of ~c[g]: that is, ~c[(consp y)] becomes a ruler of the recursive call ~c[(g x (cdr y))]. In this case, we say that ~c[f] is a ``ruler-extender''. The same result obtains if we first submit ~bv[] (set-ruler-extenders :all) ~ev[] as this removes all function calls as blockers of the top-down analysis. In other words, with ~c[:all] it is the case that for every recursive call, every test argument of a superior call of ~c[IF] contributes a ruler of that recursive call. ACL2 handles ~ilc[LET] (and ~ilc[LET*]) expressions by translating them to ~c[LAMBDA] expressions (~pl[term]). The next examples illustrates termination analysis involving such expressions. First consider the following (admittedly inefficient) definition. ~bv[] (defun fact (n) (let ((k (if (natp n) n 0))) (if (equal k 0) 1 (* k (fact (+ -1 k)))))) ~ev[] ACL2 translates the body of this definition to a ~c[LAMBDA] application, essentially: ~bv[] ((lambda (k) (if (equal k 0) 1 (* k (fact (+ -1 k))))) (if (natp n) n 0)) ~ev[] As with the application of any function other than ~c[IF], the top-down termination analysis does not dive into arguments: the ~c[LAMBDA] blocks the continuation of the analysis into its argument. But here, the argument of the ~c[LAMBDA] is ~c[(if (natp n) n 0)], which has no recursive calls to consider anyhow. What is more interesting: ACL2 does continue its termination analysis into the body of the ~c[LAMBDA], in an environment binding the ~c[LAMBDA] formals to its actuals. In this case, the termination analysis thus continues into the term ~bv[] (if (equal k 0) 1 (* k (fact (+ -1 k)))) ~ev[] in the environment that binds ~c[k] to the term ~c[(if (natp n) n 0)]. Thus, the proof obligation is successfully discharged, as reported by ACL2: ~bv[] For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT N). The non-trivial part of the measure conjecture is Goal (IMPLIES (NOT (EQUAL (IF (NATP N) N 0) 0)) (O< (ACL2-COUNT (+ -1 (IF (NATP N) N 0))) (ACL2-COUNT N))). ..... Q.E.D. That completes the proof of the measure theorem for FACT. ~ev[] But now consider the following definition, in which the recursion takes place inside the argument of the ~c[LAMBDA] rather than inside the ~c[LAMBDA] body. ~bv[] (defun app (x y) (let ((result (if (endp x) y (cons (car x) (app (cdr x) y))))) (if (our-test result) result 0))) ~ev[] Writing the body in ~c[LAMBDA] notation: ~bv[] ((lambda (result) (if (our-test result) result 0)) (if (endp x) y (cons (car x) (app (cdr x) y)))) ~ev[] By default, the ~c[LAMBDA] call blocks the top-down termination analysis from proceeding into the term ~c[(if (endp x) ...)]. To solve this, one can submit the event: ~bv[] (set-ruler-extenders :lambdas) ~ev[] The above definition of ~c[app] is then admitted by ACL2, because the termination analysis is no longer blocked by the ~c[LAMBDA] call. The example just above illustrates that the heuristically-chosen measure is suitably sensitive to the ruler-extenders. Specifically: that measure is the application of ~c[acl2-count] to the first formal parameter of the function that is tested along every branch of the relevant ~c[IF] structure (as determined by the rulers) and occurs as a proper subterm at the same argument position in every recursive call. The heuristics for choosing the controller-alist for a ~ilc[definition] rule are similarly sensitive to the ruler-extenders (~pl[definition]). The remarks above for ~ilc[defun] ~il[events] are equally applicable when a definition sits inside a ~ilc[mutual-recursion] event, except of course that in this case, a ``recursive call'' is a call of any function being defined by that ~ilc[mutual-recursion] event. Rules of class ~c[:]~ilc[definition] are sensitive to ~c[set-ruler-extenders] in analogy to the case of ~c[defun] ~il[events]. This macro generates a call ~c[(table acl2-defaults-table :ruler-extenders val)] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs. ~l[acl2-defaults-table]. The current list of ruler-extenders may be obtained as ~bv[] (cdr (assoc-eq :ruler-extenders (table-alist 'acl2-defaults-table (w state)))) ~ev[] or more conveniently, as: ~bv[] (default-ruler-extenders (w state)) ~ev[] Note that evaluation of ~c[(set-ruler-extenders lst)], where ~c[lst] evaluates to a list, does not necessarily include the default ruler-extenders ~-[] i.e., those included for the argument, ~c[:basic] ~-[] which are the elements of the list constant ~c[*basic-ruler-extenders*], namely ~ilc[return-last] and ~ilc[mv-list]. You may, of course, include these explicitly in your list argument. We conclude our discussion by noting that the set of ruler-extenders can affect the induction scheme that is stored with a recursive definition. The community book ~c[books/misc/misc2/ruler-extenders-tests.lisp] explains how induction schemes are derived in this case. Consider the following example. ~bv[] (defun tree-of-nils-p (x) (if (consp x) (and (tree-of-nils-p (car x)) (tree-of-nils-p (cdr x))) (null x))) ~ev[] The above definition generates the following induction scheme. Note that ~c[(and u v)] expands to ~c[(if u v nil)], which explains why the term ~c[(tree-of-nils-p (car x))] rules the recursive call ~c[(tree-of-nils-p (cdr x))], resulting in the hypothesis ~c[(tree-of-nils-p (car x))] in the final conjunct below. ~bv[] (AND (IMPLIES (NOT (CONSP X)) (:P X)) (IMPLIES (AND (CONSP X) (NOT (TREE-OF-NILS-P (CAR X))) (:P (CAR X))) (:P X)) (IMPLIES (AND (CONSP X) (TREE-OF-NILS-P (CAR X)) (:P (CAR X)) (:P (CDR X))) (:P X))) ~ev[] Now consider the following variant of the above definition, in which a call of the function ~c[identity] blocks the termination analysis. ~bv[] (defun tree-of-nils-p (x) (if (consp x) (identity (and (tree-of-nils-p (car x)) (tree-of-nils-p (cdr x)))) (null x))) ~ev[] This time the induction scheme is as follows, since only the top-level ~c[IF] test contributes rulers to the termination analysis. ~bv[] (AND (IMPLIES (NOT (CONSP X)) (:P X)) (IMPLIES (AND (CONSP X) (:P (CAR X)) (:P (CDR X))) (:P X))) ~ev[] But now suppose we first designate ~c[identity] as a ruler-extender. ~bv[] (set-ruler-extenders '(identity)) ~ev[] Then the induction scheme generated for the both of the above variants of ~c[tree-of-nils-p] is the one shown for the first variant, which is reasonable because both definitions now produce essentially the same termination analysis.~/") #+acl2-loop-only (defmacro set-ruler-extenders (x) ; It seems a bit sad to evaluate x twice, but that seems kind of unavoidable if ; we are to use a table event to set the acl2-defaults-table, since WORLD is ; not available for the expression of that event. `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (er-progn (chk-ruler-extenders ,x soft 'set-ruler-extenders (w state)) (progn (table acl2-defaults-table :ruler-extenders (let ((x0 ,x)) (case x0 ; If keywords other than :ALL, :BASIC, and :LAMBDAS are supported, then also ; change get-ruler-extenders1. (:all :all) (:lambdas (cons :lambdas *basic-ruler-extenders*)) (:basic *basic-ruler-extenders*) (otherwise x0)))) (table acl2-defaults-table :ruler-extenders))))) #-acl2-loop-only (defmacro set-ruler-extenders (x) (declare (ignore x)) nil) #+acl2-loop-only (defmacro set-irrelevant-formals-ok (x) ":Doc-Section switches-parameters-and-modes allow irrelevant formals in definitions~/ ~bv[] Examples: (set-irrelevant-formals-ok t) (set-irrelevant-formals-ok nil) (set-irrelevant-formals-ok :warn) ~ev[] The first example above allows irrelevant formals in definitions; ~pl[irrelevant-formals]. The second example disallows irrelevant formals; this is the default. The third example allows irrelevant formals, but prints an appropriate warning. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table].~/ ~bv[] General Form: (set-irrelevant-formals-ok flg) ~ev[] where ~c[flg] is either ~c[t], ~c[nil], or ~c[:warn].~/ :cited-by Programming" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :irrelevant-formals-ok ,x) (table acl2-defaults-table :irrelevant-formals-ok)))) #-acl2-loop-only (defmacro set-irrelevant-formals-ok (x) (declare (ignore x)) nil) #+acl2-loop-only (defmacro set-ignore-ok (x) ":Doc-Section switches-parameters-and-modes allow unused formals and locals without an ~c[ignore] or ~c[ignorable] declaration~/ ~bv[] Examples: (set-ignore-ok t) (set-ignore-ok nil) (set-ignore-ok :warn) ~ev[] The first example above allows unused formals and locals, i.e., variables that would normally have to be ~il[declare]d ~c[ignore]d or ~c[ignorable]. The second example disallows unused formals and locals; this is the default. The third example allows them, but prints an appropriate warning. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table].~/ ~bv[] General Form: (set-ignore-ok flg) ~ev[] where ~c[flg] is either ~c[t], ~c[nil], or ~c[:warn]. One might find this event useful when one is generating function definitions by an automated procedure, when that procedure does not take care to make sure that all formals are actually used in the definitions that it generates. Note: Defun will continue to report irrelevant formals even if ~c[:set-ignore-ok] has been set to ~c[t], unless you also use ~ilc[set-irrelevant-formals-ok] to instruct it otherwise.~/ :cited-by Programming" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :ignore-ok ,x) (table acl2-defaults-table :ignore-ok)))) #-acl2-loop-only (defmacro set-ignore-ok (x) (declare (ignore x)) nil) #-acl2-loop-only (defmacro set-inhibit-warnings! (&rest x) (declare (ignore x)) nil) (table inhibit-warnings-table nil nil :guard (stringp key)) #+acl2-loop-only (defmacro set-inhibit-warnings! (&rest lst) ":Doc-Section switches-parameters-and-modes control warnings non-~ilc[local]ly~/ Please ~pl[set-inhibit-warnings], which is the same as ~c[set-inhibit-warnings!] except that the latter is not ~ilc[local] to the ~ilc[encapsulate] or the book in which it occurs. Probably ~il[set-inhibit-warnings] is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing ~ilc[encapsulate] or book.~/~/" (declare (xargs :guard (string-listp lst))) `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table inhibit-warnings-table nil ',(pairlis$ lst nil) :clear) (value-triple ',lst)))) (defmacro set-inhibit-warnings (&rest lst) ":Doc-Section switches-parameters-and-modes control warnings~/ ~bv[] Examples: (set-inhibit-warnings \"theory\" \"use\") ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs; ~pl[set-inhibit-warnings!] for a corresponding non-~ilc[local] event. Indeed, ~c[(set-inhibit-warnings ...)] is equivalent to ~c[(local (set-inhibit-warnings! ...))].~/ ~bv[] General Form: (set-inhibit-warnings string1 string2 ...) ~ev[] where each string is considered without regard to case. This macro is equivalent to ~c[(local (table inhibit-warnings-table nil 'lst :clear))], where ~c[lst] is the list of strings supplied. This macro is an event (~pl[table]), but no output results from a ~c[set-inhibit-warnings] event. ACL2 prints warnings that may, from time to time, seem excessive to experienced users. Each warning is ``labeled'' with a string identifying the type of warning. Consider for example ~bv[] ACL2 Warning [Use] in ( THM ...): It is unusual to :USE .... ~ev[] Here, the label is \"Use\". The argument list for ~c[set-inhibit-warnings] is a list of such labels, each of which is a string. Any warning is suppressed if its label is a member of this list, where case is ignored, . Thus, for example, the warning above will not be printed after a call of ~c[set-inhibit-warnings] that contains the string, ~c[\"Use\"] (or any string that is ~ilc[string-equal] to ~c[\"Use\"], such as ~c[\"use\"] or ~c[\"USE\"]). In summary: the effect of this event is to suppress any warning whose label is a member of the given argument list, where case is ignored. The list of currently inhibited warnings is the list of keys in the ~il[table] named ~c[inhibit-warnings-table]. (The values in the table are irrelevant.) One way to get that value is to get the result from evaluating the following form: ~c[(table-alist 'inhibit-warnings-table (w state))]. Of course, if warnings are inhibited overall ~-[] ~pl[set-inhibit-output-lst] ~-[] then this value is entirely irrelevant." `(local (set-inhibit-warnings! ,@lst))) (defmacro set-inhibit-output-lst (lst) ; In spite of the documentation for this macro, 'warning and 'warning! are ; handled completely independently by the ACL2 warning mechanism, which looks ; for 'warning or 'warning! in the value of state global 'inhibit-output-lst. ; Set-inhibit-output-lst adds 'warning to this state global whenever it adds ; 'warning. If the user sets inhibit-output-lst directly using f-put-global or ; assign, then including 'warning! will not automatically include 'warning. ":Doc-Section switches-parameters-and-modes control output~/ ~bv[] Examples: (set-inhibit-output-lst '(warning)) (set-inhibit-output-lst '(proof-tree prove proof-checker)) (set-inhibit-output-lst *valid-output-names*) ; inhibit all prover output :set-inhibit-output-lst (proof-tree prove)~/ General Form: (set-inhibit-output-lst lst) ~ev[] where ~c[lst] is a form (which may mention ~ilc[state]) that evaluates to a list of names, each of which is the name of one of the following ``kinds'' of output produced by ACL2. ~bv[] error error messages warning warnings other than those related to soundness warning! warnings (of all degrees of importance) observation observations prove commentary produced by the theorem prover proof-checker commentary produced by the proof-checker event non-proof commentary produced by events such as defun and encapsulate expansion commentary produced by make-event expansion summary the summary at the successful conclusion of an event proof-tree proof-tree output ~ev[] It is possible to inhibit each kind of output by putting the corresponding name into ~c[lst]. For example, if ~c['warning] is included in (the value of) ~c[lst], then no warnings are printed except those related to soundness, e.g., the inclusion of an uncertified book. Note that ~il[proof-tree] output is affected by ~c[set-inhibit-output-lst]; ~pl[proof-tree]. ~l[with-output] for a variant of this utility that can be used in ~il[books]. Also ~pl[set-inhibit-warnings] for how to inhibit individual warning types and ~pl[set-inhibited-summary-types] for how to inhibit individual parts of the summary. Printing of events on behalf of ~ilc[certify-book] and ~ilc[encapsulate] is inhibited when both ~c['event] and ~c['prove] belong to ~c[lst]. Otherwise, printing of events is controlled by the ~ilc[ld] special ~ilc[ld-pre-eval-print]. ~em[Note for advanced users.] By including ~c[warning!] in ~c[lst], you are automatically including ~c[warning] as well: all warnings will be inhibited. This is not the case if you modify value of state global variable ~c['inhibit-output-lst] directly (with ~ilc[assign] or ~c[f-put-global]); then, if you include ~c[warning!] but not ~c[warning], then warnings not related to soundness will still be printed (which is probably not what was intended)." `(let ((ctx 'set-inhibit-output-lst)) (er-let* ((lst (chk-inhibit-output-lst ,lst ctx state))) (pprogn (f-put-global 'inhibit-output-lst lst state) (value lst))))) (defmacro set-inhibited-summary-types (lst) ":Doc-Section switches-parameters-and-modes control which parts of the summary are printed~/ ~bv[] Example: (set-inhibited-summary-types '(rules time)) ~ev[] Note: This is not an event. Rather, it changes the ~il[state], in analogy to ~ilc[set-inhibit-output-lst].~/ ~bv[] General Form: (set-inhibited-summary-types form) ~ev[] where form evaluates to a true-list of symbols, each of which is among the values of the constant ~c[*summary-types*], i.e.: ~c[header], ~c[form], ~c[rules], ~c[hint-events] ~c[warnings], ~c[time], ~c[steps], ~c[value], and ~c[splitter-rules]. Each specified type inhibits printing of the corresponding portion of the summaries printed at the conclusions of ~il[events], where ~c[header] refers to an initial newline followed by the line containing just the word ~c[Summary]. Note the distinction between ~c[rules] and ~c[hint-events]. ~c[Rules] provides a record of automatic rule usage by the prover, while ~c[hint-events] shows the names of events given to ~c[:USE] or ~c[:BY] ~il[hints], as well as ~il[clause-processor] functions given to ~c[:CLAUSE-PROCESSOR] hints that have an effect on the proof. Also ~pl[set-inhibit-output-lst]. Note that ~c[set-inhibited-summary-types] has no effect when ~c[summary] is one of the types inhibited by ~il[set-inhibit-output-lst], because in that case none of the summary will be printed. To control summary types for a single event, ~pl[with-output]." `(let ((lst ,lst) (ctx 'set-inhibited-summary-types)) (cond ((not (true-listp lst)) (er soft ctx "The argument to set-inhibited-summary-types must evaluate ~ to a true-listp, unlike ~x0." lst)) ((not (subsetp-eq lst *summary-types*)) (er soft ctx "The argument to set-inhibited-summary-types must evaluate ~ to a subset of the list ~X01, but ~x2 contains ~&3." *summary-types* nil lst (set-difference-eq lst *summary-types*))) (t (pprogn (f-put-global 'inhibited-summary-types lst state) (value lst)))))) #+acl2-loop-only (defmacro set-state-ok (x) ":Doc-Section switches-parameters-and-modes allow the use of STATE as a formal parameter~/ Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. In brief: The variable symbol ~ilc[STATE] has an unusual status in ACL2. In order to use it, you either need to issue ~c[:set-state-ok t], as we explain below, or you need to declare it to be a ~il[stobj], as explained elsewhere (~pl[declare-stobjs]). Now we explain in more detail. Because the variable symbol ~ilc[STATE] denotes the ``current ACL2 state,'' ACL2 treats the symbol very restrictively when it occurs as a formal parameter of a defined function. The novice user, who is unlikely to be aware of the special status of that symbol, is likely to be confused when error messages about ~c[STATE] are printed in response to the innocent choice of that symbol as a formal variable. Therefore the top-level ACL2 loop can operate in a mode in which ~ilc[STATE] is simply disallowed as a formal parameter.~/ For a discussion of ~c[STATE], ~l[state] and ~pl[stobj]. Roughly speaking, at the top-level, the ``current ACL2 state'' is denoted by the variable symbol ~c[STATE]. Only the current state may be passed into a function expecting a state as an argument. Furthermore, the name of the formal parameter into which the current state is passed must be ~c[STATE] and nothing but the current state may be passed into a formal of that name. Therefore, only certain access and change functions can use that formal ~-[] namely those with a ~c[STATE] formal ~-[] and if any such function produces a new state it becomes the ``current state'' and must be passed along in the ~c[STATE] position thereafter. Thus, ACL2 requires that the state be single-threaded. This, in turn, allows us to represent only one state at a time and to produce new states from it destructively in a von Neumaneque fashion. The syntactic restrictions on the variable ~c[STATE] are enforced by the translate mechanism (~pl[trans] and ~pl[term]) when terms are read. To prevent the novice user from seeing messages prohibiting certain uses of the variable symbol ~C[STATE] ACL2 has a mode in which it simply disallows the use of that symbol as a formal parameter. Use of the symbol causes a simple error message. The system is initially in that mode. To get out of that mode, execute: ~bv[] :set-state-ok t ;;; or, (set-state-ok t) ~ev[] It is not recommended that you do this until you have read the documentation of ~ilc[STATE]. To enter the mode in which use of ~c[state] is prohibited as a formal parameter, do: ~bv[] :set-state-ok nil ~ev[] The mode is stored in the defaults table, ~l[acl2-defaults-table]. Thus, the mode may be set ~ilc[local]ly in books.~/ :cited-by programming " `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :state-ok ,x) (table acl2-defaults-table :state-ok)))) #-acl2-loop-only (defmacro set-state-ok (x) (declare (ignore x)) nil) ; Rockwell Addition: This is the standard litany of definitions supporting ; a new acl2-defaults-table entry. The doc string explains what it is all ; about. #+acl2-loop-only (defmacro set-let*-abstractionp (x) ":Doc-Section switches-parameters-and-modes to shorten many prettyprinted clauses~/ Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table]. When this flag is set to ~c[t], subterms that occur more than once in a clause are abstracted away with ~ilc[let*], generally shortening the displayed size of the clauses. This flag only affects how clauses are printed. It does not change what terms the theorem prover manipulates.~/ ~bv[] :set-let*-abstractionp t ;;; or, (set-let*-abstractionp t) ~ev[] will cause the prettyprinter to do ``let* abstraction'' on clauses before they are printed. The algorithm finds the maximal multiply-occuring subterm and extracts it, binding it to some new variable and replacing its occurrences by that variable. This produces a ~c[let*] form. This process is iterated until no subterm occurs more than once. This process generally takes a little time, but less time than to print large clauses. The process can greatly reduce the amount of text produced by the prover. THIS ONLY AFFECTS HOW THE CLAUSES ARE PRINTED! The unabstracted clauses are manipulated by the theorem prover. ~bv[] :set-let*-abstractionp nil ~ev[] restores normal clause printing. The mode is stored in the defaults table, ~l[acl2-defaults-table]. Thus, the mode may be set locally in books." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :let*-abstractionp ,x) (table acl2-defaults-table :let*-abstractionp)))) #-acl2-loop-only (defmacro set-let*-abstractionp (x) (declare (ignore x)) nil) (defmacro set-let*-abstraction (x) ; Usually the names of our set utilities do not end in "p". We leave ; set-let*-abstractionp for backward compatibility, but we add this version for ; consistency. `(set-let*-abstractionp ,x)) (defun let*-abstractionp (state) ; This function returns either nil or else a non-nil symbol in the current ; package. (declare (xargs :mode :program)) (and (cdr (assoc-eq :let*-abstractionp (table-alist 'acl2-defaults-table (w state)))) (pkg-witness (current-package state)))) ; WARNING: If you change the value of *initial-backchain-limit*, be sure ; to change the reference to it in (deflabel backchain-limit ...) and ; (defmacro set-backchain-limit ...). (defconst *initial-backchain-limit* '(nil nil)) (defconst *initial-default-backchain-limit* '(nil nil)) #+acl2-loop-only (defmacro set-backchain-limit (limit) ":Doc-Section switches-parameters-and-modes sets the backchain-limit used by the type-set and rewriting mechanisms~/ Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table]. This event sets the global ~ilc[backchain-limit] used by the ACL2 type-set and rewriting mechanisms. Its value may be a cons whose car and cdr are each either ~c[nil] or a non-negative integer. Its value ~c[x] may also be ~c[nil] or a non-negative integer, which is treated as a cons whose car and cdr are both ~c[x]. The car is used to limit backchaining used by the ACL2 type-set mechanism, while the cdr is used to limit backchaining used by the rewriting mechanism. ~l[backchain-limit] for details about how backchain-limits are used. Rewrite backchain limits may also be installed at the level of hints; ~pl[hints] for a discussion of ~c[:backchain-limit-rw].~/ ~bv[] :set-backchain-limit nil ; do not impose any additional limits :set-backchain-limit 0 ; allow only type-set reasoning for rewriting ; hypotheses :set-backchain-limit 500 ; allow backchaining to a depth of no more ; than 500 for rewriting hypotheses (set-backchain-limit 500) ; same as above :set-backchain-limit (500 500) ; same as above (set-backchain-limit '(500 500)) ; same as above (set-backchain-limit '(3 500)) ; allow type-set backchaining to a depth of no more ; than 3 and rewriter backchaining to a depth of no ; more than 500 ~ev[] The default limit is ~c[(nil nil)]." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :backchain-limit (let ((limit ,limit)) (if (atom limit) (list limit limit) limit))) (table acl2-defaults-table :backchain-limit)))) #-acl2-loop-only (defmacro set-backchain-limit (limit) (declare (ignore limit)) nil) (defun backchain-limit (wrld flg) ":Doc-Section Miscellaneous limiting the effort expended on relieving hypotheses~/ Before ACL2 can apply a rule with hypotheses, it must establish that the hypotheses are true. (We ignore the relaxing of this requirement afforded by ~ilc[case-split]s and ~ilc[force]d hypotheses.) ACL2 typically establishes each hypothesis by backchaining ~-[] instantiating the hypothesis and then rewriting it recursively. Here we describe how ACL2 allows the user to limit backchaining. At the end, below, we describe the function ~ilc[backchain-limit].~/ Each hypothesis of a ~ilc[rewrite], ~ilc[meta], ~ilc[linear], or ~ilc[type-prescription] rule is assigned a backchain-limit when the rule is stored. By default, this limit is ~c[nil], denoting infinity (no limit). However, the value used for the default may be set to a non-negative integer (or to ~c[nil]) by the user; ~pl[set-default-backchain-limit]. The default is overridden when a ~c[:backchain-limit-lst] is supplied explicitly with the rule; ~pl[rule-classes]. The number of recursive applications of backchaining starting with the hypothesis of a rule is limited to the backchain-limit associated with that hypothesis. Moreover, the user may set global backchain-limits that limit the total backchaining depth. ~l[set-backchain-limit]. One limit is for the use of ~ilc[rewrite], ~ilc[meta], and ~ilc[linear] rules, while the other limit is for so-called ``~il[type-set] reasoning'', which uses rules of class ~ilc[type-prescription] rules. The two limits operate independently. Below, we discuss the first kind of backchain limits, i.e., for other than ~ilc[type-prescription] rules, except as otherwise indicated; but the mechanism for those rules is similar. Below we lay out the precise sense in which a global backchain-limit interacts with the backchain-limits of individual rules in order to limit backchaining. But first we note that when further backchaining is disallowed, ACL2 can still prove a hypothesis in a given context by using that contextual information. In fact, ~il[type-set] reasoning may be used (except that a weaker version of it is used in the second case above, i.e., where we are already doing type-set reasoning). Thus, the relieving of hypotheses may be limited to the use of contextual information (without backchaining, i.e., without recursively rewriting hypotheses) by executing ~c[:set-backchain-limit 0]. Recall that there are two sorts of backchain limits: those applied to hypotheses of individual rules, as assigned by their ~c[:]~ilc[rule-classes] or else taken from the default (~pl[set-default-backchain-limit]); and the global limit, initially ~c[nil] (no limit) but settable with ~c[:]~ilc[set-backchain-limit]. Here is how these two types of limits interact to limit backchaining, i.e., recursive rewriting of hypotheses. ACL2 maintains a current backchain limit, which is the limit on the depth of recursive calls to the rewriter, as well as a current backchain depth, which is initially 0 and is incremented each time ACL2 backchains (and is decremented when a backchain completes). When ACL2 begins to rewrite a literal (crudely, one of the ``top-level'' terms of the goal currently being worked on), it sets the current backchain-limit to the global value, which is initially ~c[nil] but can be set using ~c[:]~ilc[set-backchain-limit]. When ACL2 is preparing to relieve a hypothesis by backchaining (hence, after it has already tried type-set reasoning), it first makes sure that the current backchain limit is greater than the current backchain depth. If not, then it refuses to relieve that hypothesis. Otherwise, it increments the current backchain depth and calculates a new current backchain-limit by taking the minimum of two values: the existing current backchain-limit, and the sum of the current backchain depth and the backchain-limit associated with the hypothesis. Thus, ACL2 only modifies the current backchain-limit if it is necessary to decrease that limit in order to respect the backchain limit associated with the hypothesis. We illustrate with the following examples. ~bv[] ; We stub out some functions so that we can reason about them. (defstub p0 (x) t) (defstub p1 (x) t) (defstub p2 (x) t) (defstub p3 (x) t) ; Initially, the default-backchain-limit is nil, or infinite. (defaxiom p2-implies-p1-limitless (implies (p2 x) (p1 x))) ; The following rule will have a backchain-limit of 0. (defaxiom p1-implies-p0-limit-0 (implies (p1 x) (p0 x)) :rule-classes ((:rewrite :backchain-limit-lst 0))) ; We have (p2 x) ==> (p1 x) ==> (p0 x). We wish to establish that ; (p2 x) ==> (p0 x). Normally, this would be no problem, but here ; we fail because ACL2 cannot establish (p0 x) by type-set reasoning ; alone. (thm (implies (p2 x) (p0 x))) ; We set the default-backchain-limit (for rewriting) to 1. :set-default-backchain-limit 1 ; The following is more powerful than p1-implies-p0-limit-0 ; because it can use rewrite rules to establish (p1 x). (defaxiom p1-implies-p0-limit-1 (implies (p1 x) (p0 x))) ; This theorem will succeed: (thm (implies (p2 x) (p0 x))) ; We return the default-backchain-limit to its initial value. :set-default-backchain-limit nil ; Here is our last axiom. (defaxiom p3-implies-p2-limitless (implies (p3 x) (p2 x))) ; We now have (p3 x) ==> (p2 x) ==> (p1 x) ==> (p0 x). However the ; rule p1-implies-p0-limit-1 has a backchain-limit of 1; hence we ; are not allowed to backchain far enough back to use ; p3-implies-p2-limitless. We therefore lose. (defthm will-fail (implies (p3 x) (p0 x))) ~ev[] Finally, we remark that to see the current global backchain-limits, issue the following commands. ~bv[] (backchain-limit wrld :ts) ; backchain limit for type-set reasoning (backchain-limit wrld :rewrite) ; backchain limit for rewriting ~ev[]" (declare (xargs :guard (and (member-eq flg '(:ts :rewrite)) (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld)) (true-listp (assoc-eq :backchain-limit (table-alist 'acl2-defaults-table wrld)))))) (let ((entry (or (cdr (assoc-eq :backchain-limit (table-alist 'acl2-defaults-table wrld))) *initial-backchain-limit*))) (if (eq flg :ts) (car entry) (cadr entry)))) #+acl2-loop-only (defmacro set-default-backchain-limit (limit) ":Doc-Section switches-parameters-and-modes sets the default backchain-limit used when admitting a rule~/ Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table]. This event sets the default ~ilc[backchain-limit] used when a new ~ilc[rewrite], ~ilc[linear], ~ilc[meta], or ~ilc[type-prescription] rule is admitted. Its value may be a two-element list whose elements are each either ~c[nil] or a non-negative integer. Its value ~c[x] may also be ~c[nil] or a non-negative integer, which is treated as the two element list ~c[(x x)]. The first element of the list is used to limit backchaining for a rule of class ~ilc[type-prescription] while the second element is used to limit backchaining for the other three classes of rules mentioned above. ~l[backchain-limit] for details about how backchain-limits are used. The examples below assume that a new rule doesn't itself specify a value for ~c[:backchain-limit-lst].~/ ~bv[] :set-default-backchain-limit nil ; do not impose backchain limits for the ; rule :set-default-backchain-limit 0 ; allow only type-set reasoning for ; relieving a new rule's hypotheses :set-default-backchain-limit 500 ; allow backchaining through a new rewrite, ; linear, or meta rule's hypotheses to a ; depth of no more than 500 (set-default-backchain-limit 500) ; same as above :set-default-backchain-limit (nil 500) ; same as above (set-default-backchain-limit '(nil 500)) ; same as above (set-default-backchain-limit '(3 500)) ; for a new :type-prescription rule, allow ; type-set backchaining to a depth ; of no more than 3; for a new ; rule of class :rewrite, :linear, ; or :meta, allow backchaining to ; a depth of no more than 50 (set-default-backchain-limit '(nil 500)) ; do not limit backchaining for a ; new :type-prescription rule; for ; a new rule of class :rewrite, ; :linear, or :meta, allow ; backchaining to a depth of no ; more than 50 ~ev[] The initial default backchain-limit is ~c[nil]." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :default-backchain-limit (let ((limit ,limit)) (if (atom limit) (list limit limit) limit))) (table acl2-defaults-table :default-backchain-limit)))) #-acl2-loop-only (defmacro set-default-backchain-limit (limit) (declare (ignore limit)) nil) (defun default-backchain-limit (wrld flg) ":Doc-Section Miscellaneous specifying the backchain limit for a rule~/ ~l[backchain-limit].~/ The initial value is ~c[(nil nil)]. To inspect the current value (as explained elsewhere; ~pl[backchain-limit]): ~bv[] (default-backchain-limit wrld :ts) ; for type-set reasoning (default-backchain-limit wrld :rewrite) ; for rewriting ~ev[]" (declare (xargs :guard (and (member-eq flg '(:ts :rewrite)) (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld)) (true-listp (assoc-eq :default-backchain-limit (table-alist 'acl2-defaults-table wrld)))))) (let ((entry (or (cdr (assoc-eq :default-backchain-limit (table-alist 'acl2-defaults-table wrld))) *initial-default-backchain-limit*))) (if (eq flg :ts) (car entry) (cadr entry)))) ; Essay on Step-limits ; We assume familiarity with step-limits at the user level; see :DOC ; set-prover-step-limit and see :DOC with-prover-step-limit. ; Step-limits are managed through the following three global data structures. ; - (f-get-global 'last-step-limit state) ; This value records the current step-limit (updated from time to time, but not ; constantly within the rewriter). In a compound event, this decreases as ; events are executed, except for those within a call of with-prover-step-limit ; whose flag is t (see :DOC with-prover-step-limit). ; - (table acl2-defaults-table :step-limit) ; The table value supplies a starting step-limit for top-level calls that are ; not in the scope of with-prover-step-limit, hence not in the scope of ; with-ctx-summarized (which calls save-event-state-globals, which calls ; with-prover-step-limit with argument :START). ; - (f-get-global 'step-limit-record state) ; This global is bound whenever entering the scope of with-prover-step-limit. ; It stores information about the step-limit being established for that scope, ; including the starting value to use for state global 'last-step-limit. That ; value is the current value of that state global, unless a call of ; set-prover-step-limit has set a different limit in the same context. ; We may write more if that becomes necessary, but we hope that the summary ; above provides sufficient orientation to make sense of the implementation. ; NOTE: If you change the implementation of step-limits, be sure to LD and ; also certify community book books/misc/misc2/step-limits.lisp. ; When writing a recursive function that uses step-limits, for which you are ; willing to have a return type of (mv step-limit erp val state): ; * give it a step-limit arg; ; * pass that along, for example with sl-let if that is convenient; ; * decrement the step-limit when you deem that a "step" has been taken; ; * call the top-level entry with the step-limit arg set to a fixnum limit that ; you prefer, for example with (initial-step-limit wrld state) or ; *default-step-limit* ; * wrap the top-level call in a catch-step-limit as illustrated in ; prove-loop1 ; See also catch-step-limit for more about how step-limits are managed. (defun step-limit-from-table (wrld) ; We return the top-level prover step-limit, with of course can be overridden ; by calls of with-prover-step-limit. (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld)) (let ((val (cdr (assoc-eq :step-limit (table-alist 'acl2-defaults-table wrld))))) (or (null val) (and (natp val) (<= val *default-step-limit*))))))) (or (cdr (assoc-eq :step-limit (table-alist 'acl2-defaults-table wrld))) *default-step-limit*)) #-acl2-loop-only (defparameter *step-limit-error-p* ; The value of this special variable is nil when not in the scope of ; catch-step-limit. When in such a scope, the value is t unless a throw has ; occurred to tag 'step-limit-tag, in which case the value is 'error. nil) #+acl2-loop-only (defmacro set-prover-step-limit (limit) ; See the Essay on Step-limits. ":Doc-Section switches-parameters-and-modes sets the step-limit used by the ACL2 prover~/ This event provides a way to limit the number of so-called ``prover steps'' permitted for an event. ~l[with-prover-step-limit] for a way to specify the limit on prover steps for a single event, rather than globally. For a related utility based on time instead of prover steps, ~pl[with-prover-time-limit]. For examples of how step limits work, see the community book ~c[books/misc/misc2/step-limits.lisp]. For a utility that returns an indicator of the number of prover steps most recently taken, ~pl[last-prover-steps]. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table]. ~bv[] Example Forms: (set-prover-step-limit *default-step-limit*) ; no limit on prover steps (set-prover-step-limit nil) ; abbreviation for the form just above (set-prover-step-limit 10000) ; allow at most 10,000 prover steps per event~/ General Form: (set-prover-step-limit expr) ~ev[] where ~c[expr] evaluates either to ~c[nil] or else to a natural number not exceeding the value of ~c[*default-step-limit*]. If that value is ~c[nil] or the value of ~c[*default-step-limit*], then no default limit is placed on the number of prover ``steps'' (see below) during processing of an event. Otherwise, that value is the maximum number of prover steps permitted before an error occurs. This event specifies the limit on the number of ``steps'' counted by the ACL2 prover during processing of an event. Currently, a step is counted for each call of the system functions ~c[rewrite] and ~c[expand-abbreviations]. However, the steps counted may change in future releases of ACL2, so users would probably be well served by avoiding the assumption that only the above two calls are counted as prover steps. Depending on the computer you are using, you may have less than a half-hour of time before the number of prover steps exceeds the maximum step-limit, which is one less than the value of ~c[*default-step-limit*]. Note however the exception stated above: if the ``limit'' is ~c[nil] or is the value of ~c[*default-step-limit*], then no limit is imposed. There is at best a loose connection between the counting of steps and ~ilc[with-prover-time-limit]. In particular, for a call of ~c[mfc-rw] or any ~c[mfc-] function (~pl[extended-metafunctions]), the steps taken during that call are forgotten when returning from that call. The limit is relevant for every event, as well as for calls of ~ilc[thm] and ~ilc[certify-book] ~-[] and more generally, to any form that creates a ``summary context'' to print the usual event summary. The limit is also put in force when entering the ~il[proof-checker]. A call of ~c[set-prover-step-limit] applies to each subsequent form unless the call of ~c[set-prover-step-limit] is within a summary context, in which case its effect disappears when exiting that summary context. The limit applies to each event, not just ``atomic'' events. Consider the following example. ~bv[] (set-prover-step-limit 500) (encapsulate () (defthm lemma-1 ; takes 380 steps (equal (append (append x y) z) (append x y z)) :rule-classes nil) (defthm lemma-2 ; would take 319 steps (equal (len (append x y)) (+ (len x) (len y))) :rule-classes nil)) ~ev[] The first ~ilc[defthm] event, ~c[lemma-1] takes 380 steps (as of this writing), as shown in the summary: ~bv[] Prover steps counted: 380 LEMMA-1 ~ev[] The second ~ilc[defthm] event, ~c[lemma-2], takes 319 steps (as of this writing) when evaluated at the top level. However, in the context above, 380 steps of the available 500 steps (from the ~c[set-prover-step-limit] event above) have already been taken under the above ~ilc[encapsulate] event. Thus, when the number of steps would exceed 120, the proof of ~c[lemma-2] is aborted: ~bv[] ACL2 Error in STEP-LIMIT: The prover step-limit, which is 120 in the current context, has been exceeded. See :DOC set-prover-step-limit. ~ev[] The summary for ~c[lemma-2] reflects that situation: ~bv[] Prover steps counted: More than 120 ~ev[] The summary for the ~ilc[encapsulate] event then indicates that the available steps for that event have also been exceeded: ~bv[] Prover steps counted: More than 500 ~ev[] The discussion above applies to any event that contains other events, hence applies similarly to ~ilc[progn] events. For those who use ~ilc[make-event], we note that prover steps in the expansion phase similarly contribute to the total number of steps counted. For example, suppose that the limit is 500 prover steps as above, and you submit ~c[(make-event EXPR)], where 300 prover steps take place during evaluation of ~c[EXPR], producing event ~c[EV]. Then evaluation of ~c[EV] will cause an error if it takes more than 200 prover steps. This observation actually can be used to count prover steps for sequences of forms that are not all legal ~ilc[events] (~pl[embedded-event-form]), such as calls of ~ilc[thm]. For example, a small built-in ACL2 test suite that includes ~ilc[thm] forms can be run by evaluating the form ~c[(mini-proveall)], and the steps can be counted as shown below. (Here we assume a fresh ACL2 session; an error would occur if first, we evaluate the event ~c[(set-prover-step-limit 500)] displayed above.) ~bv[] ACL2 !>(make-event (er-progn (mini-proveall) (value '(value-triple nil)))) [[... output omitted here ...]] Summary Form: ( MAKE-EVENT (ER-PROGN ...)) Rules: NIL Warnings: Double-rewrite, Equiv, Subsume and Non-rec Time: 0.38 seconds (prove: 0.04, print: 0.29, other: 0.05) Prover steps counted: 41090 NIL ACL2 !> ~ev[]~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (pprogn (let ((rec (f-get-global 'step-limit-record state)) (limit (or ,limit *default-step-limit*))) (cond ((and rec ; We check here that limit is legal, even though this is also checked by the ; table event below. Otherwise, we can get a raw Lisp error from, for example: ; (progn (set-prover-step-limit '(a b))) (natp limit) (<= limit *default-step-limit*)) (f-put-global 'step-limit-record (change step-limit-record rec :sub-limit limit :strictp (or (< limit *default-step-limit*) (access step-limit-record rec :strictp))) state)) (t state))) (progn (table acl2-defaults-table :step-limit (or ,limit *default-step-limit*)) (table acl2-defaults-table :step-limit))))) #-acl2-loop-only (defmacro set-prover-step-limit (limit) (declare (ignore limit)) nil) #+(and (not acl2-loop-only) acl2-rewrite-meter) ; for stats on rewriter depth (progn ; Here we provide a mechanism for checking the maximum stack depth attained by ; the rewrite nest, while at the same time turning off the rewrite-stack depth ; limit check. ; When we do a make certify-books or make regression after compiling with ; acl2-rewrite-meter in *features*, we will create a file foo.rstats for every ; book foo being certified. We can then collect all those stats into a single ; file by executing the following Unix command, where DIR is the acl2-sources ; directory: ; find DIR/books -name '*.rstats' -exec cat {} \; > rewrite-depth-stats.lisp (defparameter *rewrite-depth-max* 0) ; records max depth per event (defparameter *rewrite-depth-alist* nil) ; records max depth per book ) ; We might as well include code here for analyzing the resulting file ; rewrite-depth-stats.lisp (see comment above). We comment out this code since ; it will not be used very often. ; (include-book "books/misc/file-io") ; ; (defun collect-rstats-1 (filename alist acc) ; ; ; Elements of alist are of the form (event-name . n). We extend acc by an ; ; alist with corresponding elements (but no specified order) of the form ; ; ((filename . event-name) . n). ; ; (if (endp alist) ; acc ; (collect-rstats-1 filename ; (cdr alist) ; (cons (cons (cons filename (caar alist)) ; (cdar alist)) ; acc)))) ; ; (defun collect-rstats-2 (alist acc) ; ; ; Elements of alist are of the form (filename . alist2), where alist2 is an ; ; alist with elements of the form (event-name . n). ; ; (if (endp alist) ; acc ; (collect-rstats-2 (cdr alist) ; (collect-rstats-1 (caar alist) (cdar alist) acc)))) ; ; (defun collect-rstats (infile outfile state) ; ; ; Each object in infile as the form (filename . alist), where alist has ; ; elements of the form (event-name . n), where n is the rewrite stack depth ; ; required for event-name. We write out outfile, which contains a single form ; ; whose elements are of the form ((filename . event-name) . n). the cdr of ; ; each object in infile, as well as the object in the resulting outfile, are ; ; alists sorted by cdr (heaviest entry first). ; ; (declare (xargs :stobjs state :mode :program)) ; (er-let* ((forms (read-list infile 'collect-rstats state))) ; (write-list (merge-sort-cdr-> (collect-rstats-2 forms nil)) ; outfile 'collect-rstats state))) (defconst *default-rewrite-stack-limit* ; A proof at AMD has needed a value of at least 774, because of a subterm in ; hypothesis position of the form (member x '(255 254 253 ... 2 1 0)). But the ; entire regression suite (as of 1/8/03, during development of v2-8) only ; needed a value of at least 186 (one more than the 185 reported using ; collect-rstats). The example with :do-not in :doc rewrite-stack-limit ; caused a stack overflow in GCL with (set-rewrite-stack-limit 4350) but not ; with (set-rewrite-stack-limit 4300). Even 15000 didn't cause a stack ; overflow without the :do-not hint. 1000) #+acl2-loop-only (defmacro set-rewrite-stack-limit (limit) ":Doc-Section switches-parameters-and-modes Sets the rewrite stack depth used by the rewriter~/ Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. ~bv[] Example Forms: (set-rewrite-stack-limit 30) ; set to small limit :set-rewrite-stack-limit 30 ; same as above (set-rewrite-stack-limit *default-rewrite-stack-limit*) ; the default (set-rewrite-stack-limit (1- (expt 2 28))) ; maximum legal limit :set-rewrite-stack-limit nil ; same as above -- essentially, no limit ~ev[] This event sets the maximum stack depth for calls of certain functions that implement the ACL2 rewriter; ~pl[rewrite-stack-limit]. It must be a non-negative integer less than 2^28. A call ~c[(set-rewrite-stack-limit limit)] is equivalent to: ~bv[] (table acl2-defaults-table :rewrite-stack-limit limit). ~ev[] The use of ~ilc[acl2-defaults-table] ensures that this event's effect is implicitly ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs.~/ For a different but somewhat related concept, ~pl[backchain-limit]." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :rewrite-stack-limit ,(if (or (null limit) (equal limit (kwote nil))) (1- (expt 2 28)) limit)) (table acl2-defaults-table :rewrite-stack-limit)))) #-acl2-loop-only (defmacro set-rewrite-stack-limit (limit) (declare (ignore limit)) nil) (defun rewrite-stack-limit (wrld) ":Doc-Section Miscellaneous limiting the stack depth of the ACL2 rewriter~/ ACL2 users can create rules of class ~c[:]~ilc[rewrite] (~pl[rule-classes]) that have the potential of causing an infinite loop in the ACL2 rewriter. This could lead to Lisp stack overflows and even segmentation faults. For this reason, the depth of certain calls of functions in the ACL2 rewriter is limited by default using the value of the form ~c[(rewrite-stack-limit (w state))]. To see the limit in action, execute the following forms. ~bv[] (defthm app-assoc-1 (equal (append (append x y) z) (append x y z))) (defthm app-assoc-2 (equal (append x y z) (append (append x y) z))) (thm (equal (append a b c) xxx) ; try without these hints to see a slightly different error message :hints ((\"Goal\" :do-not '(preprocess)))) ~ev[] The ensuing error message shows a stack of one greater than the value of ~c[(rewrite-stack-limit (w state))], which by default is the value of the constant ~c[*default-rewrite-stack-limit*]. The error message also explains how to use ~c[:]~ilc[brr]~c[ t] and ~c[(]~ilc[cw-gstack]~c[)] to find looping rewrite rules. This limit can be changed; ~pl[set-rewrite-stack-limit].~/ For a related limit, ~pl[backchain-limit].~/" (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) #+(and (not acl2-loop-only) acl2-rewrite-meter) (prog2$ wrld 0) ; setting this to 0 initializes rdepth to 0 for rewrite calls #-(and (not acl2-loop-only) acl2-rewrite-meter) (or (cdr (assoc-eq :rewrite-stack-limit (table-alist 'acl2-defaults-table wrld))) *default-rewrite-stack-limit*)) #+acl2-loop-only (defmacro set-nu-rewriter-mode (x) ":Doc-Section switches-parameters-and-modes to turn on and off the nu-rewriter~/ Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. This event sets a flag that controls whether the ACL2 rewriter uses the special-purpose ~c[nth]/~c[update-nth] rewriter (nu-rewriter). The flag may have one of three values: ~c[nil], ~c[t], or ~c[:literals].~/ ~bv[] :set-nu-rewriter-mode nil ; do not use nu-rewriter :set-nu-rewriter-mode t ; use nu-rewriter in rewriting :set-nu-rewriter-mode :literals ; use nu-rewriter in rewriting after ; a pre-pass through every literal (set-nu-rewriter-mode :literals) ; same as above ~ev[] The value ~c[nil] prevents the use of the nu-rewriter. The other two values allow the use of the nu-rewriter. When the flag is non-~c[nil] and the rewriter encounters a term that ``begins with an ~c[nth]'', the nu-rewriter is applied. By ``begins with an ~c[nth]'' here we mean either the term is an application of ~c[nth] or is an application of some nonrecursive function or ~c[lambda] expression whose body contains an expression that begins with an ~c[nth]. Note that the use of the nu-rewriter here described above is driven by the rewriter, i.e., the nu-rewriter is applied only to terms visited by the rewriter in its inside-out sweep. When the flag is set to ~c[:literals] the system makes a pre-pass through every goal clause and applies the nu-rewriter to every subterm. The rewriter is then used on the output of that pre-pass. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table]. We expect to write more documentation as we gain experience with the nu-rewriter." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :nu-rewriter-mode ,x) (table acl2-defaults-table :nu-rewriter-mode)))) #-acl2-loop-only (defmacro set-nu-rewriter-mode (x) (declare (ignore x)) nil) (defun nu-rewriter-mode (wrld) (declare (xargs :mode :program)) (cdr (assoc-eq :nu-rewriter-mode (table-alist 'acl2-defaults-table wrld)))) ; Through Version_2.9.4, we set the nu-rewriter mode by default as follows: ; (set-nu-rewriter-mode nil) ; But nil is the default anyhow, and we prefer to keep the acl2-defaults-table ; clean so that its initial value agrees with the value in ; chk-raise-portcullis1. This isn't essentially, but for example it avoids ; laying down extra table forms when we :puff. ; Terminology: case-split-limitations refers to a list of two ; "numbers" (either of which might be nil meaning infinity), sr-limit ; is the name of the first number, and case-limit is the name of the ; second. To see how sr-limit is used, see clausify. To see how ; case-limit is used, see the Essay on Case Limit and also ; rewrite-clause. We allow the user only to set the ; case-split-limitations, not the numbers individually. (defun case-split-limitations (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) ":Doc-Section Miscellaneous a list of two ``numbers'' limiting the number of cases produced at once~/ ~bv[] Examples: ACL2 !>(case-split-limitations (w state)) (500 100) ~ev[] With the setting above, ~c[clausify] will not try subsumption/replacement if more than 500 clauses are involved. Furthermore, the simplifier, as it sweeps over a clause, will inhibit further case splits when it has accumulated 100 subgoals. This inhibition is implemented by continuing to rewrite subsequent literals but not splitting out their cases. This can produce literals containing ~c[IF]s.~/ ~l[set-case-split-limitations] for a more general discussion." (cdr (assoc-eq :case-split-limitations (table-alist 'acl2-defaults-table wrld)))) ; Warning: The function tilde-@-case-split-limitations-phrase builds in the ; fact that the car of case-split-limitations is the sr-limit and cadr is the ; case-limit. Rewrite-clause makes a similar assumption. So don't be fooled ; into thinking you can just change the structure here! (defmacro sr-limit (wrld) `(car (case-split-limitations ,wrld))) (defmacro case-limit (wrld) `(cadr (case-split-limitations ,wrld))) #+acl2-loop-only (defmacro set-case-split-limitations (lst) ":Doc-Section switches-parameters-and-modes set the case-split-limitations~/ ~bv[] Examples: (set-case-split-limitations '(500 100)) (set-case-split-limitations 'nil) (set-case-split-limitations '(500 nil)) ~ev[] The first of these prevents ~c[clausify] from trying the subsumption/replacement (see below) loop if more than 500 clauses are involved. It also discourages the clause simplifier from splitting into more than 100 cases at once. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. Moreover, its effect is to set the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form containing it; ~pl[acl2-defaults-table]. ~l[hints] for discussion of a related hint, ~c[:case-split-limitations]. Also ~pl[splitter] for information about reports on rules that may be responsible for case splits.~/ ~bv[] General Form: (set-case-split-limitations lst) ~ev[] where ~c[lst] is either ~c[nil] (denoting a list of two ~c[nil]s), or a list of length two, each element of which is either ~c[nil] or a natural number. When ~c[nil] is used as an element, it is treated as positive infinity. The default setting is ~c[(500 100)]. The first number specifies the maximum number of clauses that may participate in the ``subsumption/replacement'' loop. Because of the expensive nature of that loop (which compares every clause to every other in a way that is quadratic in the number of literals in the clauses), when the number of clauses exceeds about 1000, the system tends to ``go into a black hole,'' printing nothing and not even doing many garbage collections (because the subsumption/replacement loop does not create new clauses so much as it just tries to delete old ones). The initial setting is lower than the threshold at which we see noticeably bad performance, so you probably will not see this behavior unless you have raised or disabled the limit. Why raise the subsumption/replacement limit? The subsumption/replacement loop cleans up the set of subgoals produced by the simplifier. For example, if one subgoal is ~bv[] (implies (and p q r) s) [1] ~ev[] and another is ~bv[] (implies (and p (not q) r) s) [2] ~ev[] then the subsumption/replacement loop would produce the single subgoal ~bv[] (implies (and p r) s) [3] ~ev[] instead. This cleanup process is simply skipped when the number of subgoals exceeds the subsumption/replacement limit. But each subgoal must nonetheless be proved. The proofs of [1] and [2] are likely to duplicate much work, which is only done once in proving [3]. So with a low limit, you may find the system quickly produces a set of subgoals but then takes a long time to prove that set. With a higher limit, you may find the set of subgoals to be ``cleaner'' and faster to prove. Why lower the subsumption/replacement limit? If you see the system go into a ``black hole'' of the sort described above (no output, and few garbage collections), it could due to the subsumption/replacement loop working on a large set of large subgoals. You might temporarily lower the limit so that it begins to print the uncleaned set of subgoals. Perhaps by looking at the output you will realize that some function can be disabled so as to prevent the case explosion. Then raise or disable the limit again! The second number in the case-split-limitations specifies how many case splits the simplifier will allow before it begins to shut down case splitting. In normal operation, when a literal rewrites to a nest of ~c[IF]s, the system simplifies all subsequent literals in all the contexts generated by walking through the nest in all possible ways. This can also cause the system to ``go into a black hole'' printing nothing except garbage collection messages. This ``black hole'' behavior is different from than mentioned above because space is typically being consumed at a prodigious rate, since the system is rewriting the literals over and over in many different contexts. As the simplifier sweeps across the clause, it keeps track of the number of cases that have been generated. When that number exceeds the second number in case-split-limitations, the simplifier stops rewriting literals. The remaining, unrewritten, literals are copied over into the output clauses. ~c[IF]s in those literals are split out, but the literals themselves are not rewritten. Each output clause is then attacked again, by subsequent simplification, and eventually the unrewritten literals in the tail of the clause will be rewritten because the earlier literals will stabilize and stop producing case splits. The default setting of 100 is fairly low. We have seen successful proofs in which thousands of subgoals were created by a simplification. By setting the second number to small values, you can force the system to case split slowly. For example, a setting of 5 will cause it to generate ``about 5'' subgoals per simplification. You can read about how the simplifier works in the book Computer-Aided Reasoning: An Approach (Kaufmann, Manolios, Moore); also ~pl[introduction-to-the-theorem-prover] for a detailed tutorial on using the ACL2 prover. If you think about it, you will see that with a low case limit, the initial literals of a goal are repeatedly simplified, because each time a subgoal is simplified we start at the left-most subterm. So when case splitting prevents the later subterms from being fully split out, we revisit the earlier terms before getting to the later ones. This can be good. Perhaps it takes several rounds of rewriting before the earlier terms are in normal form and then the later terms rewrite quickly. But it could happen that the earlier terms are expensive to rewrite and do not change, making the strategy of delayed case splits less efficient. It is up to you. Sometimes the simplifier produces more clauses than you might expect, even with case-split-limitations in effect. As noted above, once the limit has been exceeded, the simplifier does not rewrite subsequent literals. But ~c[IF]s in those literals are split out nonetheless. Furthermore, the enforcement of the limit is -- as described above -- all or nothing: if the limit allows us to rewrite a literal then we rewrite the literal fully, without regard for limitations, and get as many cases as ``naturally'' are produced. It quite often happens that a single literal, when expanded, may grossly exceed the specified limits. If the second ``number'' is ~c[nil] and the simplifier is going to produce more than 1000 clauses, a ``helpful little message'' to this effect is printed out. This output is printed to the system's ``comment window'' not the standard proofs output.~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :case-split-limitations (let ((lst ,lst)) (cond ((eq lst nil) '(nil nil)) (t lst)))) (table acl2-defaults-table :case-split-limitations)))) #-acl2-loop-only (defmacro set-case-split-limitations (lst) (declare (ignore lst)) nil) ; Up through Version_2.9.4 we set case split limitations as follows: ; (set-case-split-limitations *default-case-split-limitations*) ; But as explained in the comment above for set-nu-rewriter-mode, we prefer to ; start with an acl2-defaults-table that agrees with the one in ; chk-raise-portcullis1. So we instead we set the initial acl2-defaults-table ; as follows, in end-prehistoric-world. (defconst *initial-acl2-defaults-table* `((:DEFUN-MODE . :LOGIC) (:INCLUDE-BOOK-DIR-ALIST . NIL) (:CASE-SPLIT-LIMITATIONS . (500 100)) (:TAU-AUTO-MODEP . ,(cddr *tau-status-boot-strap-settings*)))) ; (2.b) (defun untrans-table (wrld) ":Doc-Section switches-parameters-and-modes associates a function symbol with a macro for printing user-level terms~/ ~bv[] Examples: ACL2 !>(untrans-table (w state)) ((BINARY-+ + . T) (BINARY-* * . T) (BINARY-APPEND APPEND . T) (BINARY-LOGAND LOGAND . T) (BINARY-LOGIOR LOGIOR . T) (BINARY-LOGXOR LOGXOR . T) (BINARY-LOGEQV LOGEQV . T) (BINARY-POR POR . T) (BINARY-PAND PAND . T)) ~ev[] ~l[table] for a general discussion of tables.~/ ~l[add-macro-fn] for a more general discussion of this ~il[table] and for a way to associate a macro name with a function name in theory events." (declare (xargs :guard (plist-worldp wrld))) (table-alist 'untrans-table wrld)) (table untrans-table nil '((binary-+ + . t) (binary-* * . t) (binary-append append . t) (binary-logand logand . t) (binary-logior logior . t) (binary-logxor logxor . t) (binary-logeqv logeqv . t) (binary-por por . t) (binary-pand pand . t)) :clear) (defmacro add-macro-fn (macro macro-fn &optional right-associate-p) ":Doc-Section switches-parameters-and-modes associate a function name with a macro name~/ ~bv[] Examples: (add-macro-fn append binary-append) (add-macro-fn append binary-append t) ~ev[] These examples each associate the function symbol ~ilc[binary-append] with the macro name ~ilc[append]. As a result, theory functions will understand that ~c[append] refers to ~c[binary-append] ~-[] ~pl[add-macro-alias] ~-[] and moreover, proof output will be printed using ~c[append] rather than ~c[binary-append]. In the first case, ~c[(append x (append y z))] is printed rather than ~c[(append x y z)]. In the second case, right-associated arguments are printed flat: ~c[(append x y z)]. Such right-association is considered only for binary function symbols; otherwise the optional third argument is ignored.~/ ~bv[] General Forms: (add-macro-fn macro-name function-name) (add-macro-fn macro-name function-name nil) ; same as abov (add-macro-fn macro-name function-name t) ~ev[] This is a convenient way to add an entry to ~ilc[macro-aliases-table] and at the same time extend the ~ilc[untrans-table]. As suggested by the example above, calls of a function in this table will be printed as corresponding calls of macros, with right-associated arguments printed flat in the case of a binary function symbol if the optional third argument is t. In that case, for a binary function symbol ~c[fn] associated with macro name ~c[mac], then a call ~c[(fn arg1 (fn arg2 (... (fn argk arg))))] will be displayed to the user as though the ``term'' were ~c[(mac arg1 arg2 ... argk arg)]. For a call ~c[(f a1 ... ak)] of a function symbol that is not binary, or the optional argument is not supplied as ~c[t], then the effect is simply to replace ~c[f] by the corresponding macro symbol. ~l[add-macro-alias], which is invoked on the first two arguments. Also ~pl[remove-macro-alias], ~pl[untrans-table], and ~pl[remove-macro-fn].~/" `(progn (add-macro-alias ,macro ,macro-fn) (table untrans-table ',macro-fn '(,macro . ,right-associate-p)))) (defmacro add-binop (macro macro-fn) ":Doc-Section switches-parameters-and-modes associate a function name with a macro name~/ The form ~c[(add-binop macro macro-fn)] is an abbreviation for the form ~c[(add-macro-fn macro macro-fn t)]. ~l[add-macro-fn].~/~/" `(add-macro-fn ,macro ,macro-fn t)) (defmacro remove-macro-fn (macro-fn) ":Doc-Section switches-parameters-and-modes remove the association of a function name with a macro name~/ ~bv[] Example: (remove-macro-fn binary-append)~/ General Form: (remove-macro-fn macro-fn) ~ev[] ~l[add-macro-fn] for a discussion of how to associate a macro name with a function name. This form sets ~ilc[untrans-table] to the result of deleting the association of a macro name with the given binary function name. If the function name has no such association, then this form still generates an event, but the event has no real effect.~/" `(table untrans-table nil (let ((tbl (table-alist 'untrans-table world))) (if (assoc-eq ',macro-fn tbl) (delete-assoc-eq-exec ',macro-fn tbl) (prog2$ (cw "~%NOTE: the name ~x0 did not appear as a key in ~ untrans-table. Consider using :u or :ubt to ~ undo this event, which is harmless but does not ~ change untrans-table.~%" ',macro-fn) tbl))) :clear)) (defmacro remove-binop (macro-fn) ":Doc-Section switches-parameters-and-modes remove the association of a function name with a macro name~/ The form ~c[(remove-binop macro-fn)] is an abbreviation for the form ~c[(remove-macro-fn macro-fn)]. ~l[remove-macro-fn].~/~/" `(remove-macro-fn ,macro-fn)) ; Begin implementation of tables allowing user control of :once and :all for ; the :match-free behavior of rewrite, linear, and forward-chaining rules. (defun match-free-default (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (cdr (assoc-eq :match-free-default (table-alist 'acl2-defaults-table wrld)))) #+acl2-loop-only (defmacro set-match-free-default (x) ":Doc-Section switches-parameters-and-modes provide default for ~c[:match-free] in future rules~/ ~bv[] General Forms: (set-match-free-default :once) (set-match-free-default :all) (set-match-free-default nil) ~ev[] Note: This utility does not apply to ~il[type-prescription] rules; for a related topic pertinent to such rules, ~pl[free-variables-type-prescription]. As described elsewhere (~pl[free-variables]), a ~il[rewrite], ~il[linear], or ~il[forward-chaining] rule may have free variables in its hypotheses, and ACL2 can be directed either to try all bindings (``~c[:all]'') or just the first (``~c[:once]'') when relieving that hypothesis, as a basis for relieving subsequent hypotheses. This directing of ~c[:all] or ~c[:once] is generally provided by specifying either ~c[:match-free :once] or ~c[:match-free :all] in the ~c[:]~ilc[rule-classes] of the rule. If neither of these is specified, then the most recent ~c[set-match-free-default] is used by ACL2 to fill in this missing ~c[:match-free] field. ~l[rule-classes]. Except: If the last ~c[set-match-free-default] specifies ~c[nil], then ACL2 reverts to the behavior it had at start-up, as described in Remarks (2) and (3) below. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It uses the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs.~/ Remarks. (1) The use of ~c[set-match-free-default] has no effect on existing rules. In order to change the behavior of existing rules with respect to free-variable matching, ~pl[add-match-free-override]. (2) If you submit a ~il[rewrite], ~il[linear], or ~il[forward-chaining] rule with a free variable in a hypothesis, and no default setting was previously specified with ~c[set-match-free-default] or the default setting is ~c[nil], and the rule is not within a book being processed with ~ilc[include-book], ~ilc[certify-book], or ~ilc[rebuild], then a warning or error is caused. In order to make this an error instead of a warning, ~pl[set-match-free-error]. (3) If you submit a ~il[rewrite], ~il[linear], or ~il[forward-chaining] rule with a free variable in a hypothesis, and no default setting has been previously specified with ~c[set-match-free-default] or the default setting is ~c[nil], and no error is caused (see (2) above), then the default ~c[:all] is used.~/" ; :cited-by free-variables add-match-free-override set-match-free-error `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :match-free-default ,x) (table acl2-defaults-table :match-free-default)))) #-acl2-loop-only (defmacro set-match-free-default (x) (declare (ignore x)) nil) (defmacro set-match-free-error (x) ":Doc-Section switches-parameters-and-modes control error vs. warning when ~c[:match-free] is missing~/ ~bv[] Legal Forms: (set-match-free-error nil) :set-match-free-error nil (set-match-free-error t) :set-match-free-error t ~ev[] As described elsewhere (~pl[free-variables]), when a ~il[rewrite], ~il[linear], or ~il[forward-chaining] rule has free variables in its hypotheses, the user can specify whether to try all bindings (``~c[:all]'') or just the first (``~c[:once]'') when relieving its hypotheses, as a basis for relieving subsequent hypotheses. This direction of ~c[:all] or ~c[:once] is generally provided by specifying either ~i[:match-free :once] or ~i[:match-free :all] in the ~c[:]~ilc[rule-classes] of the rule. But suppose that neither of these is specified for such a rule. (Note: ~c[set-match-free-error] is not relevant for ~il[type-prescription] rules.) Also suppose that ~c[set-match-free-default] has not specified a default of ~c[:once] or ~c[:all] (~pl[set-match-free-default]). In this case a warning will occur except when in the context of ~ilc[include-book]. If you prefer to see an error in such cases, except in the context of ~ilc[certify-book], execute ~c[(set-match-free-error t)]. If there is no error, then a default of ~c[:all] is used.~/ Note: This is ~sc[not] an event! Instead, ~c[set-match-free-error] sets the state global ~c['match-free-error] (~pl[state] and ~pl[assign]). Thus, this form cannot be put into a book. If you are tempted to put it into a book, consider the fact that it really isn't needed there, since the absence of ~c[:match-free] does not cause an error in the context of ~ilc[certify-book] or ~ilc[include-book]. If you still feel the need for such a form, consider using ~c[set-match-free-default] to provide a default, at least within the scope of the current book (if any); ~pl[set-match-free-default].~/" ; :cited-by free-variables add-match-free-override set-match-free-default (declare (xargs :guard (booleanp x))) `(f-put-global 'match-free-error ,x state)) (defun match-free-override (wrld) ; We return either :clear or else a cons, whose car indicates the minimum nume ; to which the override will not apply, and whose cdr is the list of runes in ; the :match-free-override field of the acl2-defaults-table. (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (let ((pair (assoc-eq :match-free-override (table-alist 'acl2-defaults-table wrld)))) (if (or (null pair) (eq (cdr pair) :clear)) :clear (cons (cdr (assoc-eq :match-free-override-nume (table-alist 'acl2-defaults-table wrld))) (cdr pair))))) #+acl2-loop-only (defmacro add-match-free-override (flg &rest runes) ":Doc-Section switches-parameters-and-modes set ~c[:match-free] value to ~c[:once] or ~c[:all] in existing rules~/ ~bv[] Example Forms: (add-match-free-override :once t) ; Try only the first binding of free variables when relieving hypotheses ; of any rule of class :rewrite, :linear, or :forward-chaining. (add-match-free-override :all (:rewrite foo) (:rewrite bar)) ; For rewrite rules foo and bar, try all bindings of free variables when ; relieving hypotheses. (add-match-free-override :clear) ; Restore :match-free to what was originally stored for each rule (either ; :all or :once). ~ev[] As described elsewhere (~pl[free-variables]), a ~il[rewrite], ~il[linear], or ~il[forward-chaining] rule may have free variables in its hypotheses, and ACL2 can be directed either to try all bindings (``~c[:all]'') or just the first (``~c[:once]'') when relieving a hypothesis, as a basis for relieving subsequent hypotheses. This direction is generally provided by specifying either ~c[:match-free :once] or ~c[:match-free :all] in the ~c[:]~ilc[rule-classes] of the rule, or by using the most recent ~ilc[set-match-free-default] event. Also ~pl[rule-classes]. However, if a proof is going slowly, you may want to modify the behavior of some such rules so that they use only the first match for free variables in a hypothesis when relieving subsequent hypotheses, rather than backtracking and trying additional matches as necessary. (But note: ~c[add-match-free-override] is not relevant for ~il[type-prescription] rules.) The event ~c[(add-match-free-override :once t)] has that effect. Or at the other extreme, perhaps you want to specify all rules as ~c[:all] rules except for a some specific exceptions. Then you can execute ~c[(add-match-free-override :all t)] followed by, say, ~c[(add-match-free-override :once (:rewrite foo) (:linear bar))].~/ ~bv[] General Forms: (add-match-free-override :clear) (add-match-free-override flg t) (add-match-free-override flg rune1 rune2 ... runek) ~ev[] where ~c[flg] is ~c[:once] or ~c[:all] and the ~c[runei] are ~ilc[rune]s. If ~c[:clear] is specified then all rules will have the ~c[:all]/~c[:once] behavior from when they were first stored. The second general form causes all ~il[rewrite] ~il[linear], and ~il[forward-chaining] rules to have the behavior specified by ~c[flg] (~c[:all] or ~c[:once]). Finally, the last of these, where runes are specified, is additive in the sense that only the indicated rules are affected; all others keep the behavior they had just before this event was executed (possible because of earlier ~c[add-match-free-override] events). At the conclusion of this event, ACL2 prints out the list of all ~c[:]~ilc[linear], ~c[:]~ilc[rewrite], and ~c[:]~ilc[forward-chaining] runes whose rules contain free variables in hypotheses that are to be bound ~c[:once], except that if there are no overrides (value ~c[:clear] was used), then ~c[:clear] is printed. This event only affects rules that exist at the time it is executed. Future rules are not affected by the override. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It uses the ~ilc[acl2-defaults-table], and hence its effect is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs. ~em[Remarks] Lists of the ~c[:]~ilc[rewrite], ~c[:]~ilc[linear], and ~c[:]~ilc[forward-chaining] ~il[rune]s whose behavior was originally ~c[:once] or ~c[:all] are returned by the following forms, respectively. ~bv[] (free-var-runes :once (w state)) (free-var-runes :all (w state)) ~ev[] The form ~bv[] (match-free-override (w state)) ~ev[] evaluates to a pair, whose ~ilc[car] is a number used by ACL2 to determine whether a ~il[rune] is sufficiently old to be affected by the override, and whose ~ilc[cdr] is the list of ~il[rune]s whose behavior is specified as ~c[:once] by ~c[add-match-free-override]; except, if no runes have been overridden, then the keyword ~c[:clear] is returned.~/" ; :cited-by free-variables set-match-free-default set-match-free-error `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) ,(cond ((eq flg :clear) (cond ((null runes) '(progn (table acl2-defaults-table :match-free-override :clear) (table acl2-defaults-table :match-free-override))) (t `(er soft 'add-match-free-override "When the first argument of add-match-free-override is :clear, it ~ must be the only argument.")))) ((not (member-eq flg '(:all :once))) `(er soft 'add-match-free-override "The first argument of add-match-free-override must be :clear, ~ :all, or :once, but it is: ~x0." ',flg)) (t `(let ((runes ',runes)) (cond ((and (not (equal runes '(t))) (non-free-var-runes runes (free-var-runes :once (w state)) (free-var-runes :all (w state)) nil)) (er soft 'add-match-free-override "Unless add-match-free-override is given a single argument of ~ T, its arguments must be :rewrite, :linear, or ~ :forward-chaining runes in the current ACL2 world with free ~ variables in their hypotheses. The following argument~#0~[ ~ is~/s are~] thus illegal: ~&0." (non-free-var-runes runes (free-var-runes :once (w state)) (free-var-runes :all (w state)) nil))) (t (er-progn ,(cond ((and (equal runes '(t)) (eq flg :all)) '(er-progn (let ((next-nume (get-next-nume (w state)))) (table-fn 'acl2-defaults-table (list :match-free-override-nume (list 'quote next-nume)) state (list 'table 'acl2-defaults-table ':match-free-override-nume (list 'quote next-nume)))) (table acl2-defaults-table :match-free-override nil))) (t `(let* ((wrld (w state)) (old-table-val (match-free-override wrld)) (old-once-runes (cond ((equal runes '(t)) (union-equal (free-var-runes :all wrld) (free-var-runes :once wrld))) ((eq old-table-val :clear) (free-var-runes :once wrld)) (t (cdr old-table-val)))) (new-once-runes ,(cond ((equal runes '(t)) ; and (eq flg :once) 'old-once-runes) ((eq flg :once) `(union-equal ',runes old-once-runes)) (t `(set-difference-equal old-once-runes ',runes)))) (next-nume (get-next-nume wrld))) (er-progn (table-fn 'acl2-defaults-table (list :match-free-override-nume (list 'quote next-nume)) state (list 'table 'acl2-defaults-table ':match-free-override-nume (list 'quote next-nume))) (table-fn 'acl2-defaults-table (list :match-free-override (list 'quote new-once-runes)) state (list 'table 'acl2-defaults-table ':match-free-override (list 'quote new-once-runes))))))) (value (let ((val (match-free-override (w state)))) (if (eq val :clear) :clear (cdr val)))))))))))) #-acl2-loop-only (defmacro add-match-free-override (flg &rest runes) (declare (ignore flg runes)) nil) (defmacro add-include-book-dir (keyword dir) ":Doc-Section switches-parameters-and-modes link keyword for ~c[:dir] argument of ~ilc[ld] and ~ilc[include-book]~/ ~bv[] Example Form: (add-include-book-dir :smith \"/u/smith/\") ; For (include-book \"foo\" :dir :smith), prepend \"/u/smith/\" to \"foo\".~/ General Form: (add-include-book-dir kwd dir) ~ev[] where ~c[kwd] is a ~ilc[keywordp] and ~c[dir] is the ~il[pathname] of a directory. (If the final '~c[/]' is missing, ACL2 will add it for you.) The effect of this event is to modify the meaning of the ~c[:dir] keyword argument of ~ilc[include-book] as indicated by the examples above, and similarly for ~ilc[ld], namely by associating the indicated directory with the indicated keyword for purposes of the ~c[:dir] argument. By the ``indicated directory'' we mean, in the case that the pathname is a relative pathname, the directory relative to the current connected book directory; ~pl[cbd]. ~l[delete-include-book-dir] for how to undo this effect. A keyword that is already associated with a directory string by an existing invocation of ~c[add-include-book-dir] cannot be associated with a different directory string. If that is your intention, first apply ~ilc[delete-include-book-dir] to that keyword; ~pl[delete-include-book-dir]. If however the new directory string is identical with the old, then the call of ~c[add-include-book-dir] will be redundant (~pl[redundant-events]). The keyword ~c[:system] can never be redefined. It will always point to the absolute pathname of the system books directory, which by default is immediately under the directory where the ACL2 executable was originally built (~pl[include-book], in particular the discussion there of ``books directory''). This macro generates (in essence) a call ~c[(table acl2-defaults-table :include-book-dir-alist ...)] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs. ~l[acl2-defaults-table]. Even if you invoke ~c[add-include-book-dir] before certifying a book, so that this event is among the book's ~il[portcullis] commands rather than in the book itself, nevertheless that ~c[add-include-book-dir] event will not be visible after the book is included. (Note: The above behavior is generally preserved in raw-mode (~pl[set-raw-mode]),though by means other than a table.)~/" `(add-include-book-dir-fn ,keyword ,dir ; We use state in the loop but the live state outside it. This could be a ; problem if we could define a function that can take a non-live state as an ; argument; see the bug through Version_4.3 explained in a comment in ; with-live-state. However, we prevent that problem by putting ; add-include-book-dir in a suitable list in the definition of translate11. #+acl2-loop-only state #-acl2-loop-only *the-live-state*)) (defmacro delete-include-book-dir (keyword) ":Doc-Section switches-parameters-and-modes unlink keyword for ~c[:dir] argument of ~ilc[ld] and ~ilc[include-book]~/ ~bv[] Example Forms: (delete-include-book-dir :smith) ; Remove association of directory with :smith for include-book.~/ General Form: (delete-include-book-dir kwd) ~ev[] where ~c[kwd] is a ~ilc[keywordp]. The effect of this event is to modify the meaning of the ~c[:dir] keyword argument of ~ilc[include-book] and ~ilc[ld] as indicated by the examples above, namely by removing association of any directory with the indicated keyword for purposes of the ~ilc[include-book] (and ~ilc[ld]) ~c[:dir] argument. Normally one would instead use ~ilc[add-include-book-dir] to associate a new directory with that keyword; ~pl[add-include-book-dir]. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. This macro is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[add-include-book-dir] for a discussion of this aspect of both macros.~/" `(delete-include-book-dir-fn ,keyword ; We use state in the loop but the live state outside it. This could be a ; problem if we could define a function that can take a non-live state as an ; argument; see the bug through Version_4.3 explained in a comment in ; with-live-state. However, we prevent that problem by putting ; delete-include-book-dir in a suitable list in the definition of translate11. #+acl2-loop-only state #-acl2-loop-only *the-live-state*)) ; Begin implementation of tables controlling non-linear arithmetic. (defconst *non-linear-rounds-value* 3) (defun non-linearp (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (let ((temp (assoc-eq :non-linearp (table-alist 'acl2-defaults-table wrld)))) (if temp (cdr temp) nil))) #+acl2-loop-only (defmacro set-non-linearp (toggle) ":Doc-Section switches-parameters-and-modes to turn on or off non-linear arithmetic reasoning~/ ~bv[] Examples: (set-non-linearp t) (set-non-linearp nil) ~ev[]~/ ~l[non-linear-arithmetic]. This event is equivalent to ~c[(table acl2-defaults-table :non-linearp )], and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. The initial value is ~c[nil]." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :non-linearp ,toggle) (table acl2-defaults-table :non-linearp)))) #-acl2-loop-only (defmacro set-non-linearp (toggle) (declare (ignore toggle)) nil) (defmacro set-non-linear (toggle) ; Usually the names of our set utilities do not end in "p". We leave ; set-non-linearp for backward compatibility, but we add this version for ; consistency. `(set-non-linearp ,toggle)) (defun tau-auto-modep (wrld) ; See the Essay on the Status of the Tau System During and After Bootstrapping ; for further details. ; The tau system either makes :tau-system rules out of non-:tau-system rules on ; the fly or it does not. It does if auto mode is t; it doesn't if auto mode ; is nil. ; The auto mode is stored in the acl2-defaults-table. The default auto mode ; when bootstrapping is completed, i.e., choice (2.b) of the essay cited above, ; is t, by virtue of the setting of *initial-acl2-defaults-table*. However, ; that constant is loaded into the acl2-defaults-table only at the very end of ; the bootstrap process, in end-prehistoric-world. So how do we implement ; (1.b), the status of tau-auto-modep during bootstrapping? Answer: here. ; Note: Once we tried to adjust the (1.b) decision by inserting a ; (set-tau-auto-mode ...) event into the boot strap sequence. But that doesn't ; work because you can't insert it early enough, since many events are ; processed before the acl2-defaults-table even exists. ; Note: if the user clears the acl2-defaults-table, then the auto mode is just ; returns to its default value as specified by ; *tau-status-boot-strap-settings*, not to (cdr nil). (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (let ((temp (assoc-eq :tau-auto-modep (table-alist 'acl2-defaults-table wrld)))) (cond ((null temp) (if (global-val 'boot-strap-flg wrld) (cdar *tau-status-boot-strap-settings*) ; (1.b) tau auto mode during boot strap nil)) (t (cdr temp))))) #+acl2-loop-only (defmacro set-tau-auto-mode (toggle) ":Doc-Section switches-parameters-and-modes turn on or off automatic (``greedy'') generation of ~c[:tau-system] rules~/ ~bv[] Examples: (set-tau-auto-mode t) ; select automatic (``greedy'') mode (set-tau-auto-mode nil) ; select manual mode ~ev[]~/ This event is equivalent to ~c[(table acl2-defaults-table :tau-auto-modep )], and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. ~l[introduction-to-the-tau-system] for background details. The tau system gathers rules for its database in one of two ways: greedily or only at the explicit command of the user. ``Greedy'' mode is officially called ``automatic mode'' and is the default. The other mode is called ``manual mode.'' In automatic mode, all rules processed by ACL2 are also considered for inclusion in the tau database: if the ~c[:corollary] of some proved theorem happens to fit into one of the forms described in ~c[:]~ilc[tau-system], that rule is quietly added to the tau database ~i[regardless of what] ~c[:]~ilc[rule-classes] the user named for the ~c[:corollary]. Of course, such rules are also stored in the ways named by the user. See the ~i[Design Philosophy] section of ~il[introduction-to-the-tau-system] for a discussion of why the tau system is greedy by default. More details are given on automatic mode after we explain manual mode. To more tightly control the rules available to the tau system, the user may select manual mode by executing ~c[(set-tau-auto-mode nil)]. In manual mode, the only events that create ~c[:tau-system] rules are ~c[defthm] events explicitly specifying the ~c[:]~ilc[tau-system] rule class in the ~c[:]~ilc[rule-classes] argument. Of course, for a ~c[:tau-system] rule to be created from a proved formula (or its specified ~c[:corollary]), the formula must be of the appropriate shape (syntactic form). ~l[tau-system]. In manual mode, if the ~c[:tau-system] rule class is specified but the formula is not of an appropriate form an error is signalled. (Note: even in manual mode, monadic functions that are recognized as Boolean are classified as tau predicates; but no rules are created for them.) Returning to our discussion of automatic mode, a ~c[:]~ilc[tau-system] rule may be created by any of the events below, provided the definition or formula proved is of an appropriate shape: (1) ~c[defun] events introducing ``big switch'' or ``~c[mv-nth] synonyms,'' (2) ~c[defun] events creating type-prescription rules that may be also represented as ``signature rules'' of form 1, and (3) any ~c[defthm] event with a non-~c[nil] ~c[:rule-classes] argument if no ~c[:tau-system] rule is among the rule classes and the formula proved is in the shape of any ~c[tau-system] rule. Of course, events such as ~ilc[defstobj] and ~ilc[defevaluator] may also add rules to the tau database when they execute the ~ilc[defun] and ~ilc[defthm] events implicit in their descriptions. ~l[tau-system] for a description of the various shapes mentioned above. Note that any rule (of any rule class) created when the tau system is in manual mode is also created in automatic mode. For example, if an event would create a ~c[:DEFINITION], ~c[:TYPE-PRESCRIPTION], ~c[FORWARD-CHAINING], or ~c[:REWRITE] rule when the tau system is in manual mode, then the event will create that same rule when the tau system is in automatic mode. Automatic mode just means that some additional ~c[:tau-system] rules may be created. Of course, if a ~c[defthm] event explicitly specifies a ~c[:tau-system] rule class, then even if the tau system is in automatic mode, that tau rule is created from the proved formula (or the specified ~c[:corollary]) or else an error is caused. But if the tau system is in automatic mode and a ~c[defthm] event doesn't explicitly specify a ~c[:tau-system] rule class, then the system quietly checks each specified ~c[:corollary] ~-[] or, in the absence of any ~c[:corollary], it checks the proved formula ~-[] for whether it can be stored as a tau rule. If so, then the system stores a tau rule, in addition to storing the specified rule. Of course, no error is signalled if a proved formula of some non-~c[:tau-system] rule class fails to be of an appropriate shape for the tau system. In automatic mode, if the ~c[:rule-classes] specified for ~c[defthm] included several corollaries and any one of them is of class ~c[:tau-system] then the only tau system rules created are those explicitly classed as ~c[:tau-system] rules. For example, suppose a ~c[defthm] has one ~c[:corollary] stored as a ~c[:rewrite] rule and another ~c[:corollary] stored as a ~c[:tau-system] rule. But suppose the ~c[:rewrite] rule happens to also to fit the form of a ~c[:tau-system] rule. Is it added to the tau database or not? The answer is no. If you have taken the trouble to specify ~c[:tau-system] corollaries for an event, then those corollaries are the only ones stored as tau sytem rules from that event. Note that had both corollaries been classed as ~c[:rewrite] rules (and been of acceptable ~c[:tau-system] form) both would have also been made ~c[:tau-system] rules. This also allows you be in automatic mode and state a ~c[:rewrite] or other non-~c[:tau-system] rule and prevent it from being also made a tau system rule: just add a frivolous ~c[:tau-system] ~c[:corollary] like ~c[(booleanp (integerp x))]. Recall that the use of tau rules is controlled by the rune ~c[(:EXECUTABLE-COUNTERPART TAU-SYSTEM)]. When that rune is disabled, no tau rules are ~i[used] in proofs. However, the tau system continues to collect tau rules if the system is in automatic mode. Thus, if and when the tau system is re-enabled, rules automatically generated while the tau system was disabled will be used as usual by the tau system. Finally, note that ~c[defthm] events with ~c[:rule-classes] ~c[nil] do not create ~c[:tau-system] rules even if the formula proved is of an appropriate shape, regardless of whether the tau system is in automatic or manual mode. The macro ~ilc[tau-status] provides a convenient way to enable/disable the ~c[:]~ilc[executable-counterpart] of ~c[tau-system] and/or to switch between manual and automatic modes. It may also be used to determine the current settings of those two flags." `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :tau-auto-modep ,toggle) (table acl2-defaults-table :tau-auto-modep)))) #-acl2-loop-only (defmacro set-tau-auto-mode (toggle) (declare (ignore toggle)) nil) #+acl2-loop-only (defmacro defttag (tag-name &key doc) ":Doc-Section Events introduce a trust tag (ttag)~/ ~st[Introduction]. This event is intended for advanced users who, in essence, want to build extensions of ACL2. The typical intended use is to create ~il[books] that extend the functionality of ACL2 in ways not allowed without a so-called ``active trust tag''. A trust tag thus represents a contract: The writer of such a book is guaranteeing that the book extends ACL2 in a ``correct'' way as defined by the writer of the book. The writer of the book will often have a small section of the book in the scope of an active trust tag that can be inspected by potential users of that book: ~bv[] (defttag :some-ttag) ; install :some-ttag as an active trust tag (defttag nil) ; remove active trust tag ~ev[] Why might trust tags be needed? The evaluation of certain functions can introduce bugs and even unsoundness, but can be useful in restricted ways that avoid such issues. For example, ~ilc[sys-call] can be used in an unsafe way, for example to overwrite files, or worse; ~pl[sys-call] for a frightening example from Bob Boyer. The following example shows that the function ~ilc[sys-call] is restricted by default, but can be called after installing an active trust tag. ~bv[] ACL2 !>(sys-call \"pwd\" nil) ACL2 Error in TOP-LEVEL: The SYS-CALL function cannot be called unless a trust tag is in effect. See :DOC defttag. ACL2 !>(defttag t) ; Install :T as an active trust tag. TTAG NOTE: Adding ttag :T from the top level loop. T ACL2 !>(sys-call \"pwd\" nil) ; print the current directory and return NIL /u/kaufmann NIL ACL2 !>(defttag nil) ; Remove the active trust tag (using value NIL). NIL ACL2 !>(sys-call \"pwd\" nil) ; Now we get the error again: ACL2 Error in TOP-LEVEL: The SYS-CALL function cannot be called unless a trust tag is in effect. See :DOC defttag. ACL2 !> ~ev[] Of course, using ~ilc[sys-call] with the Linux command ~c[pwd] is not likely to cause any soundness problems! So suppose we want to create a function that prints the working directory. We might put the following ~il[events] into a book that is to be certified. ~bv[] (in-package \"ACL2\") (defttag :pwd-ttag) (defun print-working-dir () (declare (xargs :mode :program)) (sys-call \"pwd\" nil)) (defttag nil) ; optional (books end with this implicitly) ~ev[] We can certify this book with a specification that ~c[:pwd-ttag] is a legal trust tag: ~bv[] (certify-book \"pwd\" 0 t :ttags (:pwd-ttag)) ~ev[] One can now use this book by executing ~ilc[include-book] with keyword parameter ~c[:ttags (:pwd-ttag)] and then calling function ~c[print-working-dir]: ~bv[] (include-book \"pwd\" :ttags (:pwd-ttag)) (print-working-dir) ; working directory is printed to terminal ~ev[]~/ ~st[Detailed documentation.] ~bv[] General Forms: (defttag tag-name) (defttag tag-name :doc doc-string) ~ev[] where ~c[tag-name] is a symbol. The ~c[:doc doc-string] argument is optional; if supplied, then it must be a valid ~il[documentation] string (~pl[doc-string]), and the ~c[defttag] call will generate a corresponding ~ilc[defdoc] event for ~c[tag-name]. (For the rest of this discussion we ignore the ~c[:doc] argument.) Note however that (other than the ~c[:doc] argument), if ~c[tag-name] is not ~c[nil] then it is converted to a ``corresponding ~il[keyword]'': a symbol in the ~c[\"KEYWORD\"] package with the same ~ilc[symbol-name] as ~c[tag-name]. Thus, for example, ~c[(defttag foo)] is equivalent to ~c[(defttag :foo)]. Moreover, a non-~c[nil] symbol with a ~ilc[symbol-name] of ~c[\"NIL\"] is illegal for trust tags; thus, for example, ~c[(defttag :nil)] is illegal. This event introduces or removes a so-called active trust tag (or ``ttag'', pronounced ``tee tag''). An active ttag is a ~il[keyword] symbol that is associated with potentially unsafe evaluation. For example, calls of ~ilc[sys-call] are illegal unless there is an active trust tag. An active trust tag can be installed using a ~c[defttag] event. If one introduces an active ttag and then writes definitions that calls ~ilc[sys-call], presumably in a defensibly ``safe'' way, then responsibility for those calls is attributed to that ttag. This attribution (or blame!) is at the level of ~il[books]; a book's ~il[certificate] contains a list of ttags that are active in that book, or in a book that is included (possibly ~il[local]ly), or in a book included in a book that is included (either inclusion being potentially ~il[local]), and so on. We explain all this in more detail below. ~c[(Defttag :tag-name)] is essentially equivalent to ~bv[] (table acl2-defaults-table :ttag :tag-name) ~ev[] and hence is ~ilc[local] to any ~il[books] and ~ilc[encapsulate] ~il[events] in which it occurs; ~pl[acl2-defaults-table]. We say more about the scope of ~c[defttag] forms below. Note: This is an event! It does not print the usual event summary but nevertheless executes the above ~ilc[table] event and hence changes the ACL2 logical ~il[world], and is so recorded. Although no event summary is printed, it is important to note that the ``TTAG NOTE'', discussed below, is always printed for a non-nil ~c[:tag-name] (unless deferred; ~pl[set-deferred-ttag-notes]). ~st[Active ttags.] Suppose ~c[tag-name] is a non-~c[nil] symbol. Then ~c[(defttag :tag-name)] sets ~c[:tag-name] to be the (unique) ``active ttag.'' There must be an active ttag in order for there to be any mention of certain function and macro symbols, including ~ilc[sys-call]; evaluate the form ~c[(strip-cars *ttag-fns-and-macros*)] to see the full list of such symbols. On the other hand, ~c[(defttag nil)] removes the active ttag, if any; there is then no active ttag. The scope of a ~c[defttag] form in a book being certified or included is limited to subsequent forms in the same book before the next ~c[defttag] (if any) in that book. Similarly, if a ~c[defttag] form is evaluated in the top-level loop, then its effect is limited to subsequent forms in the top-level loop before the next ~c[defttag] in the top-level loop (if any). Moreoever, ~ilc[certify-book] is illegal when a ttag is active; of course, in such a circumstance one can execute ~c[(defttag nil)] in order to allow book certification. ~st[Ttag notes and the ``certifier.''] When a ~c[defttag] is executed with an argument other than ~c[nil], output is printed, starting on a fresh line with: ~c[TTAG NOTE]. For example: ~bv[] ACL2 !>(defttag :foo) TTAG NOTE: Adding ttag :FOO from the top level loop. :FOO ACL2 !> ~ev[] If the ~c[defttag] occurs in an included book, the message looks like this. ~bv[] TTAG NOTE (for included book): Adding ttag :FOO from file /u/smith/acl2/my-book.lisp. ~ev[] The ``~c[TTAG NOTE]'' message is always printed on a single line. The intention is that one can search the standard output for all such notes in order to find all ~i[defttag] events. In a sense, ~i[defttag] events can allow you to define your own system on top of ACL2 (for example, ~pl[progn!]). So in order for someone else (who we might call the ``certifier'') to be confident that your collection of ~il[books] is meaningful, that certifier should certify all the user-supplied books from scratch and check either that no ~c[:ttags] were supplied to ~ilc[certify-book], or else look for every ~c[TTAG NOTE] in the standard output in order to locate all ~c[defttag] ~il[events] with non-~c[nil] tag name. In this way, the certifier can in principle decide whether to be satisfied that those ~c[defttag] events did not allow inappropriate forms in the user-supplied books. In order to eliminate much of the output from ~c[TTAG NOTE]s, ~pl[set-deferred-ttag-notes]. Note however that the resulting security is somewhat less; therefore, a ~c[TTAG NOTE] is printed when invoking ~c[set-deferred-ttag-notes] to defer printing of ttag notes. ~st[Allowed ttags when certifying and including books.] A ~c[defttag] form may not be evaluated unless its argument is a so-called ``allowed'' ttag. All ttags are allowed in the interactive top-level loop. However, during ~ilc[certify-book] and ~ilc[include-book], the set of allowed ttags is restricted according to the ~c[:ttags] keyword argument. If this argument is omitted then no ttag is allowed, so a ~c[defttag] call will fail during book certification or inclusion in this case. This restriction applies even to ~c[defttag] forms already evaluated in the so-called certification ~il[world] at the time ~ilc[certify-book] is called. But note that ~c[(defttag nil)] is always legal. A ~c[:ttags] argument of ~ilc[certify-book] and ~ilc[include-book] can have value ~c[:all], indicating that every ttag is allowed, i.e., no restriction is being placed on the arguments, just as in the interactive top-level loop. In the case of ~c[include-book], an omitted ~c[:ttags] argument or an argument of ~c[:default] is treated as ~c[:all], except that warnings will occur when the book's ~il[certificate] includes ttags; but for ~c[certify-book], an omitted ~c[ttags] argument is treated as ~c[nil]. Otherwise, if the ~c[:ttags] argument is supplied but not ~c[:all], then its value is a true list of ttag specifications, each having one of the following forms, where ~c[sym] is a non-~c[nil] symbol which is treated as the corresponding ~il[keyword]. ~bq[] (1) ~c[:sym] (2) ~c[(:sym)] (3) ~c[(:sym x1 x2 ... xk)], where k > 0 and each ~c[xi] is a string, except that one ~c[xi] may be ~c[nil].~eq[] In Case (1), ~c[(defttag :sym)] is allowed to occur in at most one book or else in the top-level loop (i.e., the certification world for a book under certification or a book being included). Case (2) allows ~c[(defttag :sym)] to occur in an unlimited number of books. For case (3) the ~c[xi] specify where ~c[(defttag :sym)] may occur, as follows. The case that ~c[xi] is ~c[nil] refers to the top-level loop, while all other ~c[xi] are filenames, where the ~c[\".lisp\"] extension is optional and relative pathnames are considered to be relative to the connected book directory (~pl[cbd]). Note that the restrictions on ~c[(defttag :sym)] apply equally to any equivalent for based on the notion of ``corresponding keyword'' discussed above, e.g., ~c[(defttag acl2::sym)]. An error message, as shown below, illustrates how ACL2 enforcess the notion of allowed ttags. Suppose that you call ~ilc[certify-book] with argument ~c[:ttags (:foo)], where you have already executed ~c[(defttag :foo)] in the certification world (i.e., before calling ~ilc[certify-book]). Then ACL2 immediately associates the ttag ~c[:foo] with ~c[nil], where again, ~c[nil] refers to the top-level loop. If ACL2 then encounters ~c[(defttag foo)] inside that book, you will get the following error (using the full book name for the book, as shown): ~bv[] ACL2 Error in ( TABLE ACL2-DEFAULTS-TABLE ...): The ttag :FOO associated with file /u/smith/work/my-book.lisp is not among the set of ttags permitted in the current context, specified as follows: ((:FOO NIL)). See :DOC defttag. ~ev[] In general the structure displayed by the error message, which is ~c[((:FOO NIL))] in this case, represents the currently allowed ttags with elements as discussed in (1) through (3) above. In this case, that list's unique element is ~c[(:FOO NIL)], meaning that ttag ~c[:FOO] is only allowed at the top level (as represented by ~c[NIL]). ~st[Associating ttags with books and with the top-level loop.] When a book is certified, each form ~c[(defttag tag)] that is encountered for non-~c[nil] ~c[tag] in that book or an included book is recorded in the generated ~il[certificate], which associates the keyword corresponding to ~c[tag] with the ~il[full-book-name] of the book containing that ~c[deftag]. If such a ~c[defttag] form is encountered outside a book, hence in the ~il[portcullis] of the book being certified or one of its included books, then that keyword is associated with ~c[nil] in the generated ~il[certificate]. Note that the notion of ``included book'' here applies to the recursive notion of a book either included directly in the book being certified or else included in such a book, where we account even for ~il[local]ly included books. For examples of ways to take advantage of ttags, see community book ~c[books/hacking/hacker.lisp] and ~pl[ttags-seen], ~pl[progn!], ~pl[remove-untouchable], ~pl[set-raw-mode], and ~pl[sys-call]." (declare (xargs :guard (symbolp tag-name))) `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table acl2-defaults-table :ttag ',(and tag-name (intern (symbol-name tag-name) "KEYWORD"))) ,@(cond (doc `((defdoc ,tag-name ,doc))) (t nil)) (table acl2-defaults-table :ttag)))) #-acl2-loop-only (defmacro defttag (&rest args) (declare (ignore args)) nil) (defun ttag (wrld) ; This function returns nil if there is no active ttag. (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (cdr (assoc-eq :ttag (table-alist 'acl2-defaults-table wrld)))) ; We here document some Common Lisp functions. The primitives are near ; the end of this file. (defdoc complex-rationalp ":Doc-Section ACL2::ACL2-built-ins recognizes complex rational numbers~/ ~bv[] Examples: (complex-rationalp 3) ; nil, as 3 is rational, not complex rational (complex-rationalp #c(3 0)) ; nil, since #c(3 0) is the same as 3 (complex-rationalp t) ; nil (complex-rationalp #c(3 1)) ; t, as #c(3 1) is the complex number 3 + i ~ev[]~/ ~l[complex] for more about complex rationals in ACL2.") (deflabel let :doc ":Doc-Section ACL2::ACL2-built-ins binding of lexically scoped (local) variables~/ ~bv[] Example LET Form: (let ((x (* x x)) (y (* 2 x))) (list x y)) ~ev[] If the form above is executed in an environment in which ~c[x] has the value ~c[-2], then the result is ~c['(4 -4)].~/ ~c[Let] expressions bind variables so that their ``local'' values, the values they have when the ``body'' of the ~c[let] is evaluated, are possibly different than their ``global'' values, the values they have in the context in which the ~c[let] expression appears. In the ~c[let] expression above, the local variables bound by the ~c[let] are ~c[x] and ~c[y]. They are locally bound to the values delivered by the two forms ~c[(* x x)] and ~c[(* 2 x)], respectively, that appear in the ``bindings'' of the ~c[let]. The body of the ~c[let] is ~c[(list x y)]. Suppose that the ~c[let] expression above occurs in a context in which ~c[x] has the value ~c[-2]. (The global value of ~c[y] is irrelevant to this example.) For example, one might imagine that the ~c[let] form above occurs as the body of some function, ~c[fn], with the formal parameter ~c[x] and we are evaluating ~c[(fn -2)]. To evaluate the ~c[let] above in a context in which ~c[x] is ~c[-2], we first evaluate the two forms specifying the local values of the variables. Thus, ~c[(* x x)] is evaluated and produces ~c[4] (because ~c[x] is ~c[-2]) and ~c[(* 2 x)] is evaluated and produces ~c[-4] (because ~c[x] is ~c[-2]). Then ~c[x] and ~c[y] are bound to these values and the body of the ~c[let] is evaluated. Thus, when the body, ~c[(list x y)] is evaluated, ~c[x] is ~c[4] and ~c[y] is ~c[-4]. Thus, the body produces ~c['(4 -4)]. Note that the binding of ~c[y], which is written after the binding of ~c[x] and which mentions ~c[x], nevertheless uses the global value of ~c[x], not the new local value. That is, the local variables of the ~c[let] are bound ``in parallel'' rather than ``sequentially.'' In contrast, if the ~bv[] Example LET* Form: (let* ((x (* x x)) (y (* 2 x))) (list x y)) ~ev[] is evaluated when the global value of ~c[x] is ~c[-2], then the result is ~c['(4 8)], because the local value of ~c[y] is computed after ~c[x] has been bound to ~c[4]. ~ilc[Let*] binds its local variables ``sequentially.'' ~bv[] General LET Forms: (let ((var1 term1) ... (varn termn)) body) and (let ((var1 term1) ... (varn termn)) (declare ...) ... (declare ...) body) ~ev[] where the ~c[vari] are distinct variables, the ~c[termi] are terms involving only variables bound in the environment containing the ~c[let], and ~c[body] is a term involving only the ~c[vari] plus the variables bound in the environment containing the ~c[let]. Each ~c[vari] must be used in ~c[body] or else ~il[declare]d ignored. A ~c[let] form is evaluated by first evaluating each of the ~c[termi], obtaining for each a ~c[vali]. Then, each ~c[vari] is bound to the corresponding ~c[vali] and ~c[body] is evaluated. Actually, ~c[let] forms are just abbreviations for certain uses of ~c[lambda] notation. In particular ~bv[] (let ((var1 term1) ... (varn termn)) (declare ...) body) ~ev[] is equivalent to ~bv[] ((lambda (var1 ... varn) (declare ...) body) term1 ... termn). ~ev[] ~ilc[Let*] forms are used when it is desired to bind the ~c[vari] sequentially, i.e., when the local values of preceding ~c[varj] are to be used in the computation of the local value for ~c[vari]. ~bv[] General LET* Forms: (let* ((var1 term1) ... (varn termn)) body) and (let* ((var1 term1) ... (varn termn)) (declare (ignore x1 ... xm)) body) ~ev[] where the ~c[vari] are variables (not necessarily distinct), the ~c[termi] are terms involving only variables bound in the environment containing the ~ilc[let*] and those ~c[varj] such that ~c[j:trans (flet ((f (x) (cons x x)) (g (x y) (+ x y))) (declare (inline f)) (f (g 3 4))) ((LAMBDA (X) (CONS X X)) ((LAMBDA (X Y) (BINARY-+ X Y)) '3 '4)) => * ACL2 !> ~ev[] ~c[Flet] is part of Common Lisp. See any Common Lisp documentation for more information. We conclude by pointing out an important aspect of ~c[flet] shared by ACL2 and Common Lisp: The binding is lexical, not dynamic. That is, the ~c[flet] binding of a function symbol only applies to calls of that function symbol in the body of the ~c[flet], not other calls made in the course of evaluation. Consider the following example. Suppose we define: ~bv[] (defun f (x) x) (defun g (x) x) (defun h (x) (flet ((f (x) (cons x x))) (g x))) ~ev[] Then evaluation of ~c[(h 3)] results in ~c[3], not in the ~c[cons] pair ~c[(3 . 3)], because the ~c[flet] binding of ~c[f] only applies to calls of ~c[f] that appear in the body of that ~c[flet]. In this case, only ~c[g] is called in the body of that ~c[flet].~/") #-acl2-loop-only (defun-one-output what-is-the-global-state () ; This function is for cosmetics only and is not called by ; anything else. It tells you what you are implicitly passing ; in at the global-table field when you run with *the-live-state*. (list (list :open-input-channels (let (ans) (do-symbols (sym (find-package "ACL2-INPUT-CHANNEL")) (cond ((and (get sym *open-input-channel-key*) (get sym *open-input-channel-type-key*)) (push (cons sym (list (get sym *open-input-channel-type-key*) (strip-numeric-postfix sym))) ans)))) (sort ans (function (lambda (x y) (symbol-< (car x) (car y))))))) (list :open-output-channels (let (ans) (do-symbols (sym (find-package "ACL2-OUTPUT-CHANNEL")) (cond ((and (get sym *open-output-channel-key*) (get sym *open-output-channel-type-key*)) (push (cons sym (list (get sym *open-output-channel-type-key*) (strip-numeric-postfix sym))) ans)))) (sort ans (function (lambda (x y) (symbol-< (car x) (car y))))))) (list :global-table (global-table-cars *the-live-state*)) (list :t-stack (let (ans) (do ((i (1- *t-stack-length*) (1- i))) ((< i 0)) (push (aref-t-stack i *the-live-state*) ans)) ans)) (list :32-bit-integer-stack (let (ans) (do ((i (1- *32-bit-integer-stack-length*) (1- i))) ((< i 0)) (push (aref-32-bit-integer-stack i *the-live-state*) ans)) ans)) (list :big-clock '?) (list :idates '?) (list :acl2-oracle '?) (list :file-clock *file-clock*) (list :readable-files '?) (list :written-files '?) (list :read-files '?) (list :writeable-files '?) (list :list-all-package-names-lst '?))) ; Here we implement the macro-aliases table. ; Since books do not set the acl2-defaults-table (see the end of the :doc for ; that topic), we don't use the acl2-defaults-table to hold the macro-aliases ; information. Otherwise, one would not be able to export associations of ; functions with new macros outside a book, which seems unfortunate. Note that ; since macro-aliases are only used for theories, which do not affect the ; soundness of the system, it's perfectly OK to export such information. Put ; another way: we already allow the two passes of encapsulate to yield ; different values of theory expressions, so it's silly to start worrying now ; about the dependency of theory information on macro alias information. (deflabel macro-aliases-table :doc ":Doc-Section switches-parameters-and-modes a ~il[table] used to associate function names with macro names~/ ~bv[] Example: (table macro-aliases-table 'append 'binary-append) ~ev[] This example associates the function symbol ~ilc[binary-append] with the macro name ~ilc[append]. As a result, the name ~ilc[append] may be used as a runic designator (~pl[theories]) by the various theory functions. Thus, for example, it will be legal to write ~bv[] (in-theory (disable append)) ~ev[] as an abbreviation for ~bv[] (in-theory (disable binary-append)) ~ev[] which in turn really abbreviates ~bv[] (in-theory (set-difference-theories (current-theory :here) '(binary-append)))~/ General Form: (table macro-aliases-table 'macro-name 'function-name) ~ev[] or very generally ~bv[] (table macro-aliases-table macro-name-form function-name-form) ~ev[] where ~c[macro-name-form] and ~c[function-name-form] evaluate, respectively, to a macro name and a symbol in the current ACL2 ~il[world]. ~l[table] for a general discussion of tables and the ~c[table] event used to manipulate tables. Note that ~c[function-name-form] (above) does not need to evaluate to a function symbol, but only to a symbol. As a result, one can introduce the alias before defining a recursive function, as follows. ~bv[] (table macro-aliases-table 'mac 'fn) (defun fn (x) (if (consp x) (mac (cdr x)) x)) ~ev[] Although this is obviously contrived example, this flexibility can be useful to macro writers; see for example the definition of ACL2 system macro ~ilc[defun-inline]. The ~ilc[table] ~ilc[macro-aliases-table] is an alist that associates macro symbols with function symbols, so that macro names may be used as runic designators (~pl[theories]). For a convenient way to add entries to this ~il[table], ~pl[add-macro-alias]. To remove entries from the ~il[table] with ease, ~pl[remove-macro-alias]. This ~il[table] is used by the theory functions; ~pl[theories]. For example, in order that ~c[(disable append)] be interpreted as ~c[(disable binary-append)], it is necessary that the example form above has been executed. In fact, this ~il[table] does indeed associate many of the macros provided by the ACL2 system, including ~ilc[append], with function symbols. Loosely speaking, it only does so when the macro is ``essentially the same thing as'' a corresponding function; for example, ~c[(append x y)] and ~c[(binary-append x y)] represent the same term, for any expressions ~c[x] and ~c[y].~/") (table macro-aliases-table nil nil :guard (and (symbolp key) (not (eq (getprop key 'macro-args t 'current-acl2-world world) t)) (symbolp val) ; We no longer (as of August 2012) require that val be a function symbol, so ; that we can support recursive definition with defun-inline. It would be nice ; to use the following code as a replacement. However, ; chk-all-but-new-name-cmp is not defined at this point, and we don't think ; it's worth the trouble to fight this boot-strapping battle. If we decide ; later to strengthen the guard this, then we will need to update :doc ; macro-aliases-table to require that the value is a function symbol, not just ; a symbol. ; (mv-let (erp val) ; (chk-all-but-new-name-cmp ; val ; "guard for macro-aliases-table" ; 'function ; world) ; (declare (ignore val)) ; (null erp))) )) (table macro-aliases-table nil '((+ . binary-+) (* . binary-*) (digit-char-p . our-digit-char-p) (intern . intern-in-package-of-symbol) (append . binary-append) (logand . binary-logand) (logior . binary-logior) (logxor . binary-logxor) (logeqv . binary-logeqv) (variablep . atom) (ffn-symb . car) (fargs . cdr) (first . car) (rest . cdr) (build-state . build-state1) (f-boundp-global . boundp-global) (f-get-global . get-global) (f-put-global . put-global) (f-big-clock-negative-p . big-clock-negative-p) (f-decrement-big-clock . decrement-big-clock)) :clear) (defun macro-aliases (wrld) (declare (xargs :guard (plist-worldp wrld))) (table-alist 'macro-aliases-table wrld)) (defmacro add-macro-alias (macro-name fn-name) ":Doc-Section switches-parameters-and-modes associate a function name with a macro name~/ ~bv[] Example: (add-macro-alias append binary-append) ~ev[] This example associates the function symbol ~ilc[binary-append] with the macro name ~ilc[append]. As a result, the name ~ilc[append] may be used as a runic designator (~pl[theories]) by the various theory functions. ~l[macro-aliases-table] for more details. Also ~pl[add-macro-fn] for an extension of this utility that also affects printing.~/ ~bv[] General Form: (add-macro-alias macro-name function-name) ~ev[] This is a convenient way to add an entry to ~ilc[macro-aliases-table]. ~l[macro-aliases-table] and also ~pl[remove-macro-alias].~/" `(table macro-aliases-table ',macro-name ',fn-name)) (add-macro-alias real/rationalp #+:non-standard-analysis realp #-:non-standard-analysis rationalp) (add-macro-alias member-eq member-equal) (add-macro-alias member member-equal) (add-macro-alias assoc-eq assoc-equal) (add-macro-alias assoc assoc-equal) (add-macro-alias subsetp-eq subsetp-equal) (add-macro-alias subsetp subsetp-equal) (add-macro-alias no-duplicatesp-eq no-duplicatesp-equal) (add-macro-alias no-duplicatesp no-duplicatesp-equal) (add-macro-alias rassoc-eq rassoc-equal) (add-macro-alias rassoc rassoc-equal) (add-macro-alias remove-eq remove-equal) (add-macro-alias remove remove-equal) (add-macro-alias remove1-eq remove1-equal) (add-macro-alias remove1 remove1-equal) (add-macro-alias remove-duplicates-eq remove-duplicates-equal) (add-macro-alias remove-duplicates remove-duplicates-equal) (add-macro-alias position-ac-eq position-equal-ac) (add-macro-alias position-eq-ac position-equal-ac) (add-macro-alias position-ac position-equal-ac) (add-macro-alias position-eq position-equal) (add-macro-alias position position-equal) (add-macro-alias set-difference-eq set-difference-equal) (add-macro-alias set-difference$ set-difference-equal) (add-macro-alias add-to-set-eq add-to-set-equal) (add-macro-alias add-to-set-eql add-to-set-equal) ; for pre-v4-3 compatibility (add-macro-alias add-to-set add-to-set-equal) (add-macro-alias intersectp-eq intersectp-equal) (add-macro-alias intersectp intersectp-equal) (add-macro-alias put-assoc-eq put-assoc-equal) (add-macro-alias put-assoc-eql put-assoc-equal) ; for pre-v4-3 compatibility (add-macro-alias put-assoc put-assoc-equal) (add-macro-alias delete-assoc-eq delete-assoc-equal) (add-macro-alias delete-assoc delete-assoc-equal) (add-macro-alias union-eq union-equal) (add-macro-alias union$ union-equal) (add-macro-alias intersection-eq intersection-equal) (add-macro-alias intersection$ intersection-equal) (defmacro remove-macro-alias (macro-name) ":Doc-Section switches-parameters-and-modes remove the association of a function name with a macro name~/ ~bv[] Example: (remove-macro-alias append)~/ General Form: (remove-macro-alias macro-name) ~ev[] ~l[macro-aliases-table] for a discussion of macro aliases; also ~pl[add-macro-alias]. This form sets ~ilc[macro-aliases-table] to the result of deleting the key ~c[macro-name] from that ~il[table]. If the name does not occur in the ~il[table], then this form still generates an event, but the event has no real effect.~/" `(table macro-aliases-table nil (let ((tbl (table-alist 'macro-aliases-table world))) (if (assoc-eq ',macro-name tbl) (delete-assoc-eq-exec ',macro-name tbl) (prog2$ (cw "~%NOTE: the name ~x0 did not appear as a key in ~ macro-aliases-table. Consider using :u or :ubt to ~ undo this event, which is harmless but does not ~ change macro-aliases-table.~%" ',macro-name) tbl))) :clear)) ; Here we implement the nth-aliases table. This is quite analogous to the ; macro-aliases table; see the comment above for a discussion of why we do not ; use the acl2-defaults-table here. (deflabel nth-aliases-table :doc ":Doc-Section switches-parameters-and-modes a ~il[table] used to associate names for nth/update-nth printing~/ ~bv[] Example: (table nth-aliases-table 'st0 'st) ~ev[] This example associates the symbol ~c[st0] with the symbol ~c[st]. As a result, when the theorem prover prints terms of the form ~c[(nth n st0)] or ~c[(update-nth n val st0)], where ~c[st] is a ~il[stobj] whose ~c[n]th accessor function is ~c[f-n], then it will print ~c[n] as ~c[*f-n*].~/ ~bv[] General Form: (table nth-aliases-table 'alias-name 'name) ~ev[] This event causes ~c[alias-name] to be treated like ~c[name] for purposes of the printing of terms that are calls of ~c[nth] and ~c[update-nth]. (Note however that ~c[name] is not recursively looked up in this table.) Both must be symbols other than ~ilc[state]. ~l[term], in particular the discussion there of untranslated terms. For a convenient way to add entries to this ~il[table], ~pl[add-nth-alias]. To remove entries from the ~il[table] with ease, ~pl[remove-nth-alias].~/") (table nth-aliases-table nil nil :guard (and (symbolp key) (not (eq key 'state)) (eq (getprop key 'accessor-names t 'current-acl2-world world) t) (symbolp val) (not (eq val 'state)))) (table nth-aliases-table nil nil :clear) (defun nth-aliases (wrld) (declare (xargs :guard (plist-worldp wrld))) (table-alist 'nth-aliases-table wrld)) (defmacro add-nth-alias (alias-name name) ":Doc-Section switches-parameters-and-modes associate one symbol with another for printing of ~ilc[nth]/~ilc[update-nth] terms~/ ~bv[] Example: (add-nth-alias st0 st) ~ev[] This example associates the symbol ~c[st0] with the symbol ~c[st] for purposes of printing certain terms of the form ~c[(nth n st0)] and ~c[(update-nth n val st0)].~/ ~bv[] General Form: (add-nth-alias alias-name name) ~ev[] This is a convenient way to add an entry to ~ilc[nth-aliases-table]. ~l[nth-aliases-table] and also ~pl[remove-nth-alias].~/" `(table nth-aliases-table ',alias-name ',name)) (defmacro remove-nth-alias (alias-name) ":Doc-Section switches-parameters-and-modes remove a symbol alias for printing of ~ilc[nth]/~ilc[update-nth] terms~/ ~bv[] Example: (remove-nth-alias append)~/ General Form: (remove-nth-alias alias-name) ~ev[] ~l[nth-aliases-table] for further discussion; also ~pl[add-nth-alias]. This form sets ~ilc[nth-aliases-table] to the result of deleting the key ~c[alias-name] from that ~il[table]. If the name does not occur in the ~il[table], then this form still generates an event, but the event has no real effect.~/" `(table nth-aliases-table nil (let ((tbl (table-alist 'nth-aliases-table world))) (if (assoc-eq ',alias-name tbl) (delete-assoc-eq-exec ',alias-name tbl) (prog2$ (cw "~%NOTE: the name ~x0 did not appear as a key in ~ nth-aliases-table. Consider using :u or :ubt to ~ undo this event, which is harmless but does not ~ change nth-aliases-table.~%" ',alias-name) tbl))) :clear)) ; Here we implement the default-hints table. This is quite analogous to the ; macro-aliases table; see the comment above for a discussion of why we do not ; use the acl2-defaults-table here. In this case that decision is perhaps a ; little less clear; in fact, we used the acl2-defaults-table for this purpose ; before Version_2.9. But Jared Davis pointed out that his sets books could be ; more useful if the setting of default-hints could be visible outside a book. (deflabel default-hints-table :doc ":Doc-Section switches-parameters-and-modes a ~il[table] used to provide ~il[hints] for proofs~/ Please ~pl[set-default-hints], ~pl[add-default-hints], and ~pl[remove-default-hints] for how to use this table. For completeness, we mention here that under the hood, these events all update the ~c[default-hints-table] by updating its key, ~c[t], for example as follows. ~bv[] (table default-hints-table t '((computed-hint-1 clause) (computed-hint-2 clause stable-under-simplificationp))) ~ev[]~/ The use of default hints is explained elsewhere; ~pl[set-default-hints]. Advanced users only: ~pl[override-hints] for an advanced variant of default hints.") (defun default-hints (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'default-hints-table wrld))))) ":Doc-Section Miscellaneous a list of hints added to every proof attempt~/ ~bv[] Examples: ACL2 !>(default-hints (w state)) ((computed-hint-1 clause) (computed-hint-2 clause stable-under-simplificationp)) ~ev[] The value returned by this function is added to the right of the ~c[:]~ilc[hints] argument of every ~ilc[defthm] and ~ilc[thm] command, and to hints provided to ~ilc[defun]s as well (~c[:hints], ~c[:guard-hints], and (for ACL2(r)) ~c[:std-hints]).~/ ~l[set-default-hints] for a more general discussion. Advanced users only: ~pl[override-hints] for an advanced variant of default hints that are not superseded by ~c[:]~ilc[hints] arguments." (cdr (assoc-eq t (table-alist 'default-hints-table wrld)))) (defmacro set-default-hints (lst) ":Doc-Section switches-parameters-and-modes set the default hints~/ ~bv[] Examples: (set-default-hints '((computed-hint-1 clause) (computed-hint-2 clause stable-under-simplificationp))) (set-default-hints nil) ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs; ~pl[set-default-hints!] for a corresponding non-~ilc[local] event.~/ ~bv[] General Form: (set-default-hints lst) ~ev[] where ~c[lst] is a list. Generally speaking, the elements of ~c[lst] should be suitable for use as ~ilc[computed-hints]. Whenever a ~ilc[defthm] or ~ilc[thm] command is executed, the default hints are appended to the right of any explicitly provided ~c[:]~ilc[hints] in the command. The same applies to ~ilc[defun]s as well (~c[:hints], ~c[:guard-hints], and (for ACL2(r)) ~c[:std-hints]). The hints are then translated and processed just as though they had been explicitly included. Technically, we do not put restrictions on ~c[lst], beyond that it is a true list. It would be legal to execute ~bv[] (set-default-hints '((\"Goal\" :use lemma23))) ~ev[] with the effect that the given hint is added to subsequent hints supplied explicitly. An explicit \"Goal\" hint would, however, take priority, as suggested by the mention above of ``appended to the right.'' Note that ~c[set-default-hints] sets the default hints as specified. To add to or remove from the current default, ~pl[add-default-hints] and ~pl[remove-default-hints]. To see the current default hints, ~pl[default-hints]. Finally, note that the effects of ~c[set-default-hints], ~ilc[add-default-hints], and ~ilc[remove-default-hints] are ~ilc[local] to the book in which they appear. Thus, users who include a book with such forms will not have their default hints affected by such forms. In order to export the effect of setting the default hints, use ~ilc[set-default-hints!], ~ilc[add-default-hints!], or ~ilc[remove-default-hints!]. For a related feature, which however is only for advanced system builders, ~pl[override-hints].~/" `(local (set-default-hints! ,lst))) #+acl2-loop-only (defmacro set-default-hints! (lst) ":Doc-Section switches-parameters-and-modes set the default hints non-~ilc[local]ly~/ Please ~pl[set-default-hints], which is the same as ~c[set-default-hints!] except that the latter is not ~ilc[local] to the ~ilc[encapsulate] or the book in which it occurs. Probably ~il[set-default-hints] is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing ~ilc[encapsulate] or book.~/~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table default-hints-table t ,lst) (table default-hints-table t)))) #-acl2-loop-only (defmacro set-default-hints! (lst) (declare (ignore lst)) nil) (defmacro add-default-hints (lst &key at-end) ":Doc-Section switches-parameters-and-modes add to the default hints~/ ~bv[] Examples: (add-default-hints '((computed-hint-1 clause) (computed-hint-2 clause stable-under-simplificationp))) (add-default-hints '((computed-hint-3 id clause world)) :at-end t) ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs (~pl[add-default-hints!] for a corresponding non-~ilc[local] event).~/ ~bv[] General Forms: (add-default-hints lst) (add-default-hints lst :at-end flg) ~ev[] where ~c[lst] is a list. Generally speaking, the elements of ~c[lst] should be suitable for use as ~ilc[computed-hints]. This event is completely analogous to ~ilc[set-default-hints], the difference being that ~c[add-default-hints] appends the indicated hints to the front of the list of default hints, so that they are tried first ~-[] or, if ~c[flg] is supplied and evaluates to other than ~c[nil], at the end of the list, so that they are tried last ~-[] rather than ~st[replacing] the default hints with the indicated hints. Each new hint is thus considered after each existing hints when both are applied to the same goal. Also ~l[set-default-hints], ~pl[remove-default-hints], and ~pl[default-hints]. Finally, note that the effects of ~c[set-default-hints], ~ilc[add-default-hints], and ~ilc[remove-default-hints] are ~ilc[local] to the book in which they appear. Thus, users who include a book with such forms will not have their default hints affected by such forms. In order to export the effect of setting the default hints, use ~ilc[set-default-hints!], ~ilc[add-default-hints!], or ~ilc[remove-default-hints!]. For a related feature, which however is only for advanced system builders, ~pl[override-hints].~/" `(local (add-default-hints! ,lst :at-end ,at-end))) #+acl2-loop-only (defmacro add-default-hints! (lst &key at-end) ":Doc-Section switches-parameters-and-modes add to the default hints non-~ilc[local]ly~/ Please ~pl[add-default-hints], which is the same as ~c[add-default-hints!] except that the latter is not ~ilc[local] to the ~ilc[encapsulate] or the book in which it occurs. Probably ~il[add-default-hints] is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing ~ilc[encapsulate] or book.~/~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table default-hints-table t (if ,at-end (append (default-hints world) ,lst) (append ,lst (default-hints world)))) (table default-hints-table t)))) #-acl2-loop-only (defmacro add-default-hints! (lst) (declare (ignore lst)) nil) (defmacro remove-default-hints (lst) ":Doc-Section switches-parameters-and-modes remove from the default hints~/ ~bv[] Examples: (remove-default-hints '((computed-hint-1 clause) (computed-hint-2 clause stable-under-simplificationp))) ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs (~pl[remove-default-hints!] for a corresponding non-~ilc[local] event).~/ ~bv[] General Form: (remove-default-hints lst) ~ev[] where ~c[lst] is a list. Generally speaking, the elements of ~c[lst] should be suitable for use as ~ilc[computed-hints]. Also ~pl[add-default-hints]. If some elements of the given list do not belong to the existing default hints, they will simply be ignored by this event. Also ~l[set-default-hints], ~pl[add-default-hints], and ~pl[default-hints]. Finally, note that the effects of ~c[set-default-hints], ~ilc[add-default-hints], and ~ilc[remove-default-hints] are ~ilc[local] to the book in which they appear. Thus, users who include a book with such forms will not have their default hints affected by such forms. In order to export the effect of setting the default hints, use ~ilc[set-default-hints!], ~ilc[add-default-hints!], or ~ilc[remove-default-hints!].~/" `(local (remove-default-hints! ,lst))) #+acl2-loop-only (defmacro remove-default-hints! (lst) ":Doc-Section switches-parameters-and-modes remove from the default hints non-~ilc[local]ly~/ Please ~pl[remove-default-hints], which is the same as ~c[remove-default-hints!] except that the latter is not ~ilc[local] to the ~ilc[encapsulate] or the book in which it occurs. Probably ~il[remove-default-hints] is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing ~ilc[encapsulate] or book.~/~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table default-hints-table t (set-difference-equal (default-hints world) ,lst)) (table default-hints-table t)))) #-acl2-loop-only (defmacro remove-default-hints! (lst) (declare (ignore lst)) nil) #+acl2-loop-only (defmacro set-override-hints-macro (lst at-end ctx) `(state-global-let* ((inhibit-output-lst (list* 'summary (@ inhibit-output-lst)))) (set-override-hints-fn ,lst ,at-end ,ctx (w state) state))) #-acl2-loop-only (defmacro set-override-hints-macro (&rest args) (declare (ignore args)) nil) (defmacro add-override-hints! (lst &key at-end) ":Doc-Section switches-parameters-and-modes add non-~il[local]ly to the ~il[override-hints]~/ ~c[Add-override-hints!] is the same as ~ilc[add-override-hints], except that the former is not ~il[local] to ~il[books] or ~ilc[encapsulate] ~il[events] in which it occurs. ~l[add-override-hints]; also ~pl[set-override-hints].~/~/" (declare (xargs :guard (booleanp at-end))) `(set-override-hints-macro ,lst ,at-end 'add-override-hints!)) (defmacro add-override-hints (lst &key at-end) ":Doc-Section switches-parameters-and-modes add to the ~il[override-hints]~/ ~l[override-hints] for a discussion of override-hints. Here we describe how to extend the list of override-hints. Note that the effects of ~c[add-override-hints] ~il[events] are ~il[local] to the ~il[books] or ~c[encapsulate] ~il[events] in which they reside; ~pl[add-override-hints!] to avoid that restriction. Also ~pl[set-override-hints] to set a new list of override-hints to it, ignoring the present list rather than adding to it. ~bv[] General Forms: (add-override-hints form) (add-override-hints form :at-end t) (add-override-hints form :at-end nil) ; default for :at-end ~ev[] where ~c[form] evaluates to a list of computed hint forms. The effect of this event is to extend the current list of ~il[override-hints] by appending the result of that evaluation. The default is to append the evaluation result to the front of the current list of override-hints, but if ~c[:at-end t] is specified, then the evaluation result is appended to the end of the current list.~/~/" (declare (xargs :guard (booleanp at-end))) `(local (set-override-hints-macro ,lst ,at-end 'add-override-hints))) (defmacro set-override-hints! (lst) ":Doc-Section switches-parameters-and-modes set the ~il[override-hints] non-~il[local]ly~/ ~c[Set-override-hints!] is the same as ~ilc[set-override-hints], except that the former is not ~il[local] to ~il[books] or ~ilc[encapsulate] ~il[events] in which it occurs. ~l[set-override-hints]; also ~pl[add-override-hints].~/~/" `(set-override-hints-macro ,lst :clear 'set-override-hints!)) (defmacro set-override-hints (lst) ":Doc-Section switches-parameters-and-modes set the ~il[override-hints]~/ ~l[override-hints] for a discussion of override-hints. Here we describe how to set them. Note that the effects of ~c[set-override-hints] ~il[events] are ~il[local] to the ~il[books] or ~c[encapsulate] ~il[events] in which they reside; ~pl[set-override-hints!] to avoid that restriction. Also ~pl[add-override-hints] to add to the list of override-hints, rather than setting a new list and ignoring the present list. ~bv[] General Form: (set-override-hints form) ~ev[] where ~c[form] evaluates to a list of computed hint forms. The effect of this event is to set the list of ~il[override-hints] to the result of that evaluation.~/~/" `(local (set-override-hints-macro ,lst :clear 'set-override-hints))) (defmacro remove-override-hints! (lst) ":Doc-Section switches-parameters-and-modes delete non-~il[local]ly from the list of ~il[override-hints]~/ ~c[Remove-override-hints!] is the same as ~ilc[remove-override-hints], except that the former is not ~il[local] to ~il[books] or ~ilc[encapsulate] ~il[events] in which it occurs. ~l[remove-override-hints]; also ~pl[add-override-hints] and ~pl[set-override-hints].~/~/" `(set-override-hints-macro ,lst :remove 'remove-override-hints!)) (defmacro remove-override-hints (lst) ":Doc-Section switches-parameters-and-modes delete from the list of ~il[override-hints]~/ ~l[override-hints] for a discussion of override-hints. Here we describe how to delete from the list of override-hints. Note that the effects of ~c[remove-override-hints] ~il[events] are ~il[local] to the ~il[books] or ~c[encapsulate] ~il[events] in which they reside; ~pl[remove-override-hints!] to avoid that restriction. Also ~pl[add-override-hints] and ~pl[set-override-hints]. ~bv[] General Form: (remove-override-hints form) ~ev[] where ~c[form] should evaluate to a list of computed hint forms. The effect of this event is to set the list of ~il[override-hints] to the result of deleting each element of the evaluation result from the ~il[override-hints], if that element indeed belongs to the override-hints; no check is made that these elements are actually elements of the existing override-hints.~/~/" `(local (set-override-hints-macro ,lst :remove 'remove-override-hints))) (defmacro set-rw-cache-state (val) ; Essay on Rw-cache ; Introduction ; We cache failed attempts to relieve hypotheses. The basic idea is that ; whenever a hypothesis rewrites to other than true, we store that fact so that ; the rewrite rule is not tried again with the same unify-subst. The failure ; information is stored in tag-trees. Two kinds of failures are stored: those ; for which the unify-subst includes at least one variable bound from an ; earlier free-variable hypothesis (the "free-failure" cases), and the rest ; (the "normal-failure" cases). The free-failure case is stored in a tree ; structure with normal-failures at the leaves; see the definition of record ; rw-cache-entry. Normal-failures are recognized by ; rw-cacheable-failure-reason, which is an attachable function. When cached ; failures are found, they can be ignored if the user attaches to ; relieve-hyp-failure-entry-skip-p. ; When relieve-hyps is called, it looks in the tag-tree for a relevant failure. ; If a normal-failure record is found, then the attempt can quickly fail. If a ; free-failure record is found, then it is passed along through the process of ; relieving the hypotheses, so that after variables are bound by a hypothesis, ; this record can be consulted on subsequent hypotheses to abort rewriting. ; New failure information is recorded upon exit from relieve-hyps; in the ; free-failure case, the information to be recorded was accumulated during the ; process of relieving hypotheses. ; Rw-cache-states: *legal-rw-cache-states* = (t nil :disabled :atom) ; In a preliminary implementation we tried a scheme in which the rw-cache ; persisted through successive literals of a clause. However, we encountered ; dozens of failures in the regression suite, some of them probably because the ; tail-biting heuristic was causing failures whose caching wasn't suitable for ; other literals. Such a scheme, which also allows the rw-cache to persist to ; a single child, is represented by rw-cache-state t. When a clause reaches ; stable-under-simplificationp without any appropriate computed hint, if the ; state is t then it transitions to :disabled so that a pass is made through ; simplify-clause without interference from the rw-cache. (See for example the ; end of waterfall-step-cleanup.) Some failures with rw-cache-state t ; disappear if the rw-cache-state begins at :disabled, so that some preliminary ; simplification occurs before any failure caching. ; But even starting with :disabled, we have seen regression failures. ; Therefore our default rw-cache-state is :atom, which creates a fresh rw-cache ; for each literal of a clause; see rewrite-atm. An advantage of :atom is that ; we do not transition to a disabled state. That transition for rw-cache-state ; t is responsible for larger numbers reported in event summaries for "Prover ; steps counted" in the mini-proveall, presumably because an extra pass must be ; made through the simplifier sometime before going into induction even though ; that rarely helps (probably, never in the mini-proveall). ; Overview of some terminology, data structures, and algorithms ; We store relieve-hyps failures in tag-trees. As we discuss below, there are ; two tags associated with this failure information: 'rw-cache-any-tag and ; 'rw-cache-nil-tag. Each tag is associated with what we also call an ; "rw-cache". Sometimes we refer abstractly the values of both tags as the ; "rw-cache"; we expect that the context will resolve any possible confusion ; between the value of a tag and the entire cache (from both tags). Each tag's ; value is what we call a "psorted symbol-alist": a true list that may have at ; most one occurrence of t, where each non-t element is a cons pair whose car ; is a symbol, and where the tail past the occurrence of t (if any) is sorted ; by car. In general, the notion of "psorted" can be applied to any kind of ; true-list that has a natural notion of "sort" associated with it: then a ; psorted list is one that has at most one occurrence of t as a member, such ; that (cdr (member-equal t s)) is sorted. Indeed, we use a second kind of ; psorted list, which we call an "rw-cache-list": the elements (other than t) ; are rw-cache-entry records, and the sort relation is lexorder. By using ; psorted lists, we defer the cost of sorting until merge-time, where sorting ; is important to avoid quadratic blow-up; the use of t as a marker allows us ; to avoid re-sorting the same list. ; We maintain the invariant that the information in the "nil" cache is also in ; the "any" cache. The "nil" cache is thus more restrictive: it only stores ; cases in which the failure is suitable for a stronger context. It gets its ; name because one such case is when a hypothesis rewrites to nil. But we also ; store syntaxp and bind-free hypotheses that fail (except, we never store such ; failures when extended metafunctions are involved, because of their high ; level of dependence on context beyond the unify-subst). Thus, the "nil" ; cache is preserved when we pass to a branch of an IF term; the "any" cache is ; however replaced in that case by the "nil" cache (which preserves the above ; invariant). On the other hand, when we pop up out of an IF branch, we throw ; away any accumulation into the "nil" cache but we merge the new "any" cache ; into the old "any" cache. See rw-cache-enter-context and ; rw-cache-exit-context. ; The following definitions and trace$ forms can be evaluated in order to do ; some checking of the above invariant during subsequent proofs (e.g., when ; followed by :mini-proveall). ; (defun rw-tagged-objects-subsetp (alist1 alist2) ; (declare (xargs :mode :program)) ; (cond ((endp alist1) t) ; (t (and (or (eq (car alist1) t) ; (subsetp-equal (cdar alist1) ; (cdr (assoc-rw-cache (caar alist1) ; alist2)))) ; (rw-tagged-objects-subsetp (cdr alist1) alist2))))) ; ; (defun chk-rw-cache-inv (ttree string) ; (declare (xargs :mode :program)) ; (or (rw-tagged-objects-subsetp (tagged-objects 'rw-cache-nil-tag ttree) ; (tagged-objects 'rw-cache-any-tag ttree)) ; (prog2$ (cw string) ; (break$)))) ; ; (trace$ (relieve-hyps ; :entry (chk-rw-cache-inv ttree "Relieve-hyps entry~%") ; :exit (chk-rw-cache-inv (car (last values)) "Relieve-hyps exit~%") ; :evisc-tuple :no-print)) ; (trace$ (rewrite ; :entry (chk-rw-cache-inv ttree "Rewrite entry~%") ; :exit (chk-rw-cache-inv (car (last values)) "Rewrite exit~%") ; :evisc-tuple :no-print)) ; (trace$ (rewrite-fncall ; :entry (chk-rw-cache-inv ttree "Rewrite-fncall entry~%") ; :exit (chk-rw-cache-inv (car (last values)) "Rewrite-fncall exit~%") ; :evisc-tuple :no-print)) ; Our rw-cache-entry records store a unify-subst rather than an instance of a ; rule's left-hand side. One advantage is that the unify-subst may be smaller, ; because of repeated occurrences of a variable on the left-hand side. Another ; advantage is that in the normal-failure case, we restrict the unify-subst to ; the variables occurring in the failed hypothesis; see the call of ; restrict-alist-to-all-vars in note-relieve-hyp-failure. This clearly permits ; more hits in the rw-cache, and of course it may result in less time being ; spent in equality checking (see the comment in restrict-alist-to-all-vars ; about the order being unchanged by restriction). ; Here we record some thoughts on a preliminary implementation, in which we ; kept the "nil" and "any" caches disjoint, rather than including the "nil" ; cache in the "any" cache. ; With that preliminary implementation, we accumulated both the "nil" and ; "any" caches into the "any" cache when popping out of an IF context. We ; experimented a bit with instead ignoring the "nil" cache, even though we ; could lose some cache hits. We saw two potential benefits for such a ; change. For one, it would save the cost of doing the union operation that ; would be required. For another, it would give us a chance to record a hit ; outside that IF context as a bona fide "nil" entry, which is preserved when ; diving into future IF contexts or (for rw-cache-state t) into a unique ; subgoal. Ultimately, though, experiments pointed us to continuing our ; popping of "nil" entries into the "any" cache. ; Finally, we list some possible improvements that could be considered. ; Consider sorting in the free-failure case (see ; combine-free-failure-alists). ; Remove assert$ in split-psorted-list1 (which checks that t doesn't occur ; twice in a list). ; For free-failure case, consider optimizing to avoid checking for equality ; against a suitable tail of unify-subst that know must be equal; see for ; example rw-cache-list-lookup and replace-free-rw-cache-entry1. ; For free-failure case, consider doing a tighter job of assigning the ; failure-reason to a unify-subst. For example, if hypothesis 2 binds free ; variable y and hypothesis 5 binds free variable z, and hypothesis 6 is (foo ; y) and its rewrite fails, then associate the failure with the binding of y ; at hypothesis 2. And in that same scenario, if hypothesis 6 is instead ; (foo x), where x is bound on the left-hand side of the rule, then create a ; normal-failure reason instead of a free-failure reason. If we make any ; such change, then revisit the comments in (defrec rw-cache-entry ...). ; In restrict-alist-to-all-vars, as noted in a comment there, ; we could do a better job of restricting the unify-subst in the case of ; at least one binding hypothesis. ; In accumulate-rw-cache1, consider eliminating a COND branch that can ; require an equality test to save a few conses, as noted in a comment ; there. ; Modify accumulate-rw-cache to be more efficient, by taking advantage of the ; invariant that the "nil" cache is contained in the "any" cache. ; Consider saving a few conses in rw-cache-exit-context by avoiding ; modification of the nil cache if the old and new nil caches are equal, ; indeed, eq. Maybe a new primitive that tests with eq, but has a guard that ; the true and false branches are equal, would help. (Maybe this would ; somehow be implemented using return-last.) It is not sufficient to check ; the lengths of the caches, or even of their elements, because with ; free-vars one can make an extension without changing these lengths. ; Perhaps modify restore-rw-cache-any-tag to extend old "any" cache with the ; new "nil" cache, instead of throwing away new "nil" entries entirely. See ; restore-rw-cache-any-tag. ; Extend debug handling to free case in relieve-hyps, and/or explain in :doc ; (or at least comments) how this works. ; Perhaps we could keep around the "nil" cache longer than we currently do. ; Consider changing functions in the rewrite nest that deal with linear ; arithmetic, such as add-linear-lemma, to use the rw-cache of the input ; ttree rather than ignoring it, and to return a ttree with an extension of ; that rw-cache. A related idea is to take more advantage in such functions ; of rw-caches in intermediate ttrees, such as rw-caches in ttrees of ; irrelevant-pot-lst values in rewrite-with-linear. [The two of us discussed ; this idea. I think we decided that although we can't rule out the value of ; the above, maybe it's not too important. Note that when the pot-lst ; contributes to the proof, the cache entries will then work their way into ; the main tag-tree.] There may be other opportunities to accumulate into ; rw-caches, for example inside simplify-clause1 by passing input ttree0 into ; pts-to-ttree-lst, under the call of setup-simplify-clause-pot-lst. ":Doc-Section switches-parameters-and-modes set the default rw-cache-state~/ The ACL2 rewriter uses a data structure, called the rw-cache (rewriter cache), to save failed attempts to apply conditional ~il[rewrite] rules. The regression suite has taken approximately 11% less time with this mechanism. The rw-cache is active by default but this event allows it to be turned off or modified. Note that this event is ~il[local] to its context (from ~ilc[encapsulate] or ~ilc[include-book]). For a non-local version, use ~il[set-rw-cache-state!]. ~bv[] Example forms: (set-rw-cache-state :atom) ; default: rw-cache cleared for each literal ; (i.e., hypothesis or conclusion of a goal) (set-rw-cache-state nil) ; rw-cache is inactive (set-rw-cache-state t) ; rw-cache persists beyond each literal (set-rw-cache-state :disabled) ; rw-cache is inactive, but the rw-cache-state ; transitions to state t after ; simplification takes place~/ General Form: (set-rw-cache-state val) ~ev[] where ~c[val] evaluates to one of the four values shown in ``Example forms'' above. The default is ~c[:atom], which enables the rw-cache but clears it before rewriting a hypothesis or conclusion of any goal. The value ~c[t] is provides more aggresive use of the rw-cache, basically preserving the rw-cache when there is a single subgoal. The value ~c[:disabled] is the same as ~c[t], except that the rw-cache is initially inactive and only becomes active when some simplification has taken place. We have seen a few cases where value ~c[t] will make a proof fail but ~c[:disabled] does not. The following example illustrates the rw-cache in action. You will see a break during evaluation of the ~ilc[thm] form. Type ~c[:eval] and you will see a failed rewriting attempt. Type ~c[:go] to continue, and at the next break type ~c[:eval] again. This time you will see the same failed rewriting attempt, but this time labeled with a notation saying that the failure was cached earlier, which indicates that this time the rewriter did not even attempt to prove the hypothesis of the ~il[rewrite] rule ~c[f1->f2]. ~bv[] (defstub f1 (x) t) (defstub f2 (x) t) (defaxiom f1->f2 (implies (f1 x) (equal (f2 x) t))) :brr t :monitor (:rewrite f1->f2) t (thm (equal (car (f2 a)) (cdr (f2 a)))) ~ev[] Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded. It is ~ilc[local] to the book or ~ilc[encapsulate] form in which it occurs (~pl[set-rw-cache-state!] for a corresponding non-~ilc[local] event). We also note that rw-cache-state changes may also be caused at the subgoal level; ~pl[hints]. We welcome you to experiment with different rw-cache states. If the more aggressive values of ~c[t] and ~c[:disabled] cause proofs to fail, then you can revert to the default of ~c[:atom] or even turn off the rw-cache using ~c[(set-rw-cache-state nil)]. We don't expect users to need a deep knowledge of the rw-cache in order to do such experiments, but readers interested in details of the rw-cache implementation are invited to read the ``Essay on Rw-cache'' in the ACL2 source code.~/" `(local (set-rw-cache-state! ,val))) #+acl2-loop-only (defmacro set-rw-cache-state! (val) ":Doc-Section switches-parameters-and-modes set the default rw-cache-state non-~ilc[local]ly~/ Please ~pl[set-rw-cache-state], which is the same as ~c[set-rw-cache-state!] except that the latter is not ~ilc[local] to the ~ilc[encapsulate] or the book in which it occurs.~/~/" `(state-global-let* ((inhibit-output-lst (list* 'event 'summary (@ inhibit-output-lst)))) (progn (table rw-cache-state-table t ,val) (table rw-cache-state-table t)))) #-acl2-loop-only (defmacro set-rw-cache-state! (val) (declare (ignore val)) nil) (defconst *legal-rw-cache-states* '(t nil :disabled :atom)) (table rw-cache-state-table nil nil :guard (case key ((t) (member-eq val *legal-rw-cache-states*)) (t nil))) (defun fix-true-list (x) ":Doc-Section ACL2::ACL2-built-ins coerce to a true list~/ ~c[Fix-true-list] is the identity function on ~ilc[true-listp] objects. It converts every list to a true list by dropping the final ~ilc[cdr], and it converts every ~il[atom] to ~c[nil]. To see the ACL2 definition of this function, ~pl[pf].~/~/" (declare (xargs :guard t)) (if (consp x) (cons (car x) (fix-true-list (cdr x))) nil)) (defthm pairlis$-fix-true-list (equal (pairlis$ x (fix-true-list y)) (pairlis$ x y))) (defun boolean-listp (lst) ; We define this in axioms.lisp so that we can use this function in theorems ; whose proof uses BDDs. (declare (xargs :guard t)) (cond ((atom lst) (eq lst nil)) (t (and (or (eq (car lst) t) (eq (car lst) nil)) (boolean-listp (cdr lst)))))) (defthm boolean-listp-cons ; This rule is important for simplifying the trivial boolean-listp hypothesis ; of a goal that is given to the OBDD package. (equal (boolean-listp (cons x y)) (and (booleanp x) (boolean-listp y)))) (defthm boolean-listp-forward ; We expect this rule to be crucial in many circumstances where a :BDD hint is ; given. (implies (boolean-listp (cons a lst)) (and (booleanp a) (boolean-listp lst))) :rule-classes :forward-chaining) (defthm boolean-listp-forward-to-symbol-listp ; We expect this rule, in combination with symbol-listp-forward-to-true-listp, ; to be crucial in many circumstances where a :BDD hint is given. (implies (boolean-listp x) (symbol-listp x)) :rule-classes :forward-chaining) ; Here we record axioms pertaining to the values returned by primitives on ; inputs violating their guards. These all have :rule-classes nil, and should ; be kept in sync with the defun-*1* definitions in interface-raw.lisp, as ; well as with the documentation that follows them. ; In some of these cases we prove rewrite rules that default "wrong" arguments. ; We think this will help linear arithmetic, among other things, without ; significantly slowing down the rewriter. We'll see. (defaxiom completion-of-+ (equal (+ x y) (if (acl2-numberp x) (if (acl2-numberp y) (+ x y) x) (if (acl2-numberp y) y 0))) :rule-classes nil) (defthm default-+-1 (implies (not (acl2-numberp x)) (equal (+ x y) (fix y))) :hints (("Goal" :use completion-of-+))) (defthm default-+-2 (implies (not (acl2-numberp y)) (equal (+ x y) (fix x))) :hints (("Goal" :use completion-of-+))) (defaxiom completion-of-* (equal (* x y) (if (acl2-numberp x) (if (acl2-numberp y) (* x y) 0) 0)) :rule-classes nil) (defthm default-*-1 (implies (not (acl2-numberp x)) (equal (* x y) 0))) (defthm default-*-2 (implies (not (acl2-numberp y)) (equal (* x y) 0))) (defaxiom completion-of-unary-minus (equal (- x) (if (acl2-numberp x) (- x) 0)) :rule-classes nil) (defthm default-unary-minus (implies (not (acl2-numberp x)) (equal (- x) 0))) (defaxiom completion-of-unary-/ (equal (/ x) (if (and (acl2-numberp x) (not (equal x 0))) (/ x) 0)) :rule-classes nil) (defthm default-unary-/ (implies (or (not (acl2-numberp x)) (equal x 0)) (equal (/ x) 0))) ;; RAG - This axiom was strengthened to include the reals. (defaxiom completion-of-< (equal (< x y) (if (and (real/rationalp x) (real/rationalp y)) (< x y) (let ((x1 (if (acl2-numberp x) x 0)) (y1 (if (acl2-numberp y) y 0))) (or (< (realpart x1) (realpart y1)) (and (equal (realpart x1) (realpart y1)) (< (imagpart x1) (imagpart y1))))))) :rule-classes nil) (defthm default-<-1 (implies (not (acl2-numberp x)) (equal (< x y) (< 0 y))) :hints (("Goal" :use (completion-of-< (:instance completion-of-< (x 0)))))) (defthm default-<-2 (implies (not (acl2-numberp y)) (equal (< x y) (< x 0))) :hints (("Goal" :use (completion-of-< (:instance completion-of-< (y 0)))))) (defaxiom completion-of-car (equal (car x) (cond ((consp x) (car x)) (t nil))) :rule-classes nil) (defthm default-car (implies (not (consp x)) (equal (car x) nil))) (defaxiom completion-of-cdr (equal (cdr x) (cond ((consp x) (cdr x)) (t nil))) :rule-classes nil) (defthm default-cdr (implies (not (consp x)) (equal (cdr x) nil))) (defthm cons-car-cdr (equal (cons (car x) (cdr x)) (if (consp x) x (cons nil nil)))) (defaxiom completion-of-char-code (equal (char-code x) (if (characterp x) (char-code x) 0)) :rule-classes nil) (defthm default-char-code (implies (not (characterp x)) (equal (char-code x) 0)) :hints (("Goal" :use completion-of-char-code))) (defaxiom completion-of-code-char (equal (code-char x) (if (and (integerp x) (>= x 0) (< x 256)) (code-char x) (code-char 0))) :rule-classes nil) ; Omitted for now; maybe slows down the rewriter too much. ; ; (defthm default-code-char ; (implies (not (and (integerp x) ; (>= x 0) ; (< x 256))) ; (equal (code-char x) ; (code-char 0))) ; :hints (("Goal" :use completion-of-code-char))) ;; RAG - This axiom was strengthened to include the reals. (defaxiom completion-of-complex (equal (complex x y) (complex (if (real/rationalp x) x 0) (if (real/rationalp y) y 0))) :rule-classes nil) ;; RAG - This axiom was weakened to include the reals. (defthm default-complex-1 (implies (not (real/rationalp x)) (equal (complex x y) (complex 0 y))) :hints (("Goal" :use completion-of-complex))) ;; RAG - This axiom was weakened to include the reals. (defthm default-complex-2 (implies (not (real/rationalp y)) (equal (complex x y) (if (real/rationalp x) x 0))) :hints (("Goal" :use ((:instance completion-of-complex) (:instance complex-definition (y 0)))))) ;; RAG - This axiom was modified to include the reals. (defthm complex-0 (equal (complex x 0) #+:non-standard-analysis (realfix x) #-:non-standard-analysis (rfix x)) :hints (("Goal" :use ((:instance complex-definition (y 0)))))) (defthm add-def-complex (equal (+ x y) (complex (+ (realpart x) (realpart y)) (+ (imagpart x) (imagpart y)))) :hints (("Goal" :use ((:instance complex-definition (x (+ (realpart x) (realpart y))) (y (+ (imagpart x) (imagpart y)))) (:instance complex-definition (x (realpart x)) (y (imagpart x))) (:instance complex-definition (x (realpart y)) (y (imagpart y)))))) :rule-classes nil) (defthm realpart-+ (equal (realpart (+ x y)) (+ (realpart x) (realpart y))) :hints (("Goal" :use add-def-complex))) (defthm imagpart-+ (equal (imagpart (+ x y)) (+ (imagpart x) (imagpart y))) :hints (("Goal" :use add-def-complex))) (defaxiom completion-of-coerce (equal (coerce x y) (cond ((equal y 'list) (if (stringp x) (coerce x 'list) nil)) (t (coerce (make-character-list x) 'string)))) :rule-classes nil) (defthm default-coerce-1 (implies (not (stringp x)) (equal (coerce x 'list) nil)) :hints (("Goal" :use (:instance completion-of-coerce (y 'list))))) (defthm make-character-list-make-character-list (equal (make-character-list (make-character-list x)) (make-character-list x))) (defthm default-coerce-2 (implies (and (syntaxp (not (equal y ''string))) (not (equal y 'list))) (equal (coerce x y) (coerce x 'string))) :hints (("Goal" :use ((:instance completion-of-coerce) (:instance completion-of-coerce (x x) (y 'string)))))) ; This next one is weaker than it could be. If x is not a true list of ; characters it is coerced to one with make-character-list. We deal with only ; the simplest case where x is some atom. (defthm default-coerce-3 (implies (not (consp x)) (equal (coerce x 'string) "")) :hints (("Goal" :use (:instance completion-of-coerce (y 'string))))) (defaxiom completion-of-denominator (equal (denominator x) (if (rationalp x) (denominator x) 1)) :rule-classes nil) (defthm default-denominator (implies (not (rationalp x)) (equal (denominator x) 1)) :hints (("Goal" :use completion-of-denominator))) ;; RAG - The following axioms give the rules for working with the ;; undefined predicate floor1. We start with the completion axiom, ;; which says floor1 is only useful for real numbers. #+:non-standard-analysis (defaxiom completion-of-floor1 (equal (floor1 x) (if (realp x) (floor1 x) 0)) :rule-classes nil) ;; RAG - The second axiom about floor1 is that it returns 0 for any ;; invalid argument. #+:non-standard-analysis (defthm default-floor1 (implies (not (realp x)) (equal (floor1 x) 0))) ;; RAG - We also know that floor1 is the identity function for the integers. #+:non-standard-analysis (defaxiom floor1-integer-x (implies (integerp x) (equal (floor1 x) x))) ;; RAG - And, we know that the floor1 of x is no larger than x itself. #+:non-standard-analysis (defaxiom floor1-x-<=-x (implies (realp x) (<= (floor1 x) x)) :rule-classes :linear) ;; RAG - Finally, we know that the floor1 of x is larger than x-1. #+:non-standard-analysis (defaxiom x-<-add1-floor1-x (implies (realp x) (< x (1+ (floor1 x)))) :rule-classes :linear) ;; RAG - This theorem is useful for proving the value of floor1 is a ;; specific value. It is probably only useful when instantiated ;; manually, so we do not make it a rewrite rule. #+:non-standard-analysis (defthm floor1-value (implies (and (realp x) (integerp fx) (<= fx x) (< x (1+ fx))) (equal (floor1 x) fx)) :rule-classes nil) (defaxiom completion-of-imagpart (equal (imagpart x) (if (acl2-numberp x) (imagpart x) 0)) :rule-classes nil) (defthm default-imagpart (implies (not (acl2-numberp x)) (equal (imagpart x) 0))) (defaxiom completion-of-intern-in-package-of-symbol (equal (intern-in-package-of-symbol x y) (if (and (stringp x) (symbolp y)) ; We avoid calling INTERN here, which might otherwise lead to a guard ; violation. It's certainly OK to lay down the original call at this point! (intern-in-package-of-symbol x y) nil)) :rule-classes nil) ; (defthm default-intern-in-package-of-symbol ; (implies (not (and (stringp x) ; (symbolp y))) ; (equal (intern-in-package-of-symbol x y) ; nil)) ; :hints (("Goal" :use completion-of-intern-in-package-of-symbol))) (defaxiom completion-of-numerator (equal (numerator x) (if (rationalp x) (numerator x) 0)) :rule-classes nil) (defthm default-numerator (implies (not (rationalp x)) (equal (numerator x) 0))) (defaxiom completion-of-realpart (equal (realpart x) (if (acl2-numberp x) (realpart x) 0)) :rule-classes nil) (defthm default-realpart (implies (not (acl2-numberp x)) (equal (realpart x) 0))) (defaxiom completion-of-symbol-name (equal (symbol-name x) (if (symbolp x) (symbol-name x) "")) :rule-classes nil) (defthm default-symbol-name (implies (not (symbolp x)) (equal (symbol-name x) "")) :hints (("Goal" :use completion-of-symbol-name))) (defaxiom completion-of-symbol-package-name (equal (symbol-package-name x) (if (symbolp x) (symbol-package-name x) "")) :rule-classes nil) (defthm default-symbol-package-name (implies (not (symbolp x)) (equal (symbol-package-name x) "")) :hints (("Goal" :use completion-of-symbol-package-name))) ;; RAG - Here, I put in the basic theory that we will use for ;; non-standard analysis. (defdoc i-small ":Doc-Section ACL2::Real ACL2(r) recognizer for infinitesimal numbers~/ ~c[(I-small x)] is true if and only if ~c[x] is an infinitesimal number (possibly 0). This predicate is only defined in ACL2(r) (~pl[real]).~/~/") (defdoc i-close ":Doc-Section ACL2::Real ACL2(r) test for whether two numbers are infinitesimally close~/ ~c[(I-close x y)] is true if and only if ~c[x-y] is an infinitesimal number. This predicate is only defined in ACL2(r) (~pl[real]).~/~/") (defdoc i-large ":Doc-Section ACL2::Real ACL2(r) recognizer for infinitely large numbers~/ ~c[(I-large x)] is true if and only if ~c[x] is non-zero and ~c[1/x] is an infinitesimal number. This predicate is only defined in ACL2(r) (~pl[real]).~/~/") (defdoc i-limited ":Doc-Section ACL2::Real ACL2(r) recognizer for limited numbers~/ ~c[(I-limited x)] is true if and only if ~c[x] is a number that is not infinitely large. This predicate is only defined in ACL2(r) (~pl[real]).~/~/") (defdoc standardp ":Doc-Section ACL2::Real ACL2(r) recognizer for standard objects~/ ~c[(Standardp x)] is true if and only if ~c[x] is a ``standard'' object. This notion of ``standard'' comes from non-standard analysis and is discussed in Ruben Gamboa's dissertation. In brief, all the familiar objects are standard: e.g., the familiar real numbers are standard, but non-zero infinitesimals are not standard, and the familiar integers are standard, but not those that exceed every integer that you can express in the usual way (1, 2, 3, and so on). Similarly, the familiar lists are standard, but not so a list that contains a large number of integers, where ``large'' means more than the standard integers. The set of standard numbers is closed under the usual arithmetic operations, hence the sum of a standard number and a non-zero infinitesimal is not standard, though it is what is called ``limited'' (~pl[i-limited]). This predicate is only defined in ACL2(r) (~pl[real]).~/~/") (defdoc standard-part ":Doc-Section ACL2::Real ACL2(r) function mapping limited numbers to standard numbers~/ ~c[(Standard-part x)] is, for a given ~ilc[i-limited] number ~c[x], the unique real number infinitesimally close (~pl[i-close]) to ~c[x]. This function is only defined in ACL2(r) (~pl[real]).~/~/") #+:non-standard-analysis (progn (defun i-small (x) (declare (xargs :guard t)) (and (acl2-numberp x) (equal (standard-part x) 0))) (defun i-close (x y) (declare (xargs :guard t)) (and (acl2-numberp x) (acl2-numberp y) (i-small (- x y)))) (defun i-large (x) (declare (xargs :guard t)) (and (acl2-numberp x) (not (equal x 0)) (i-small (/ x)))) (defmacro i-limited (x) `(and (acl2-numberp ,x) (not (i-large ,x)))) ; The first axiom is crucial in the theory. We establish that there ; is at least one non-standard number, namely (i-large-integer). (defaxiom i-large-integer-is-large (i-large (i-large-integer))) ; Now, we have some axioms about standardp. Standardp ; behaves reasonably with respect to the arithmetic operators. ; RAGTODO: Some of these are theorems now, and should be introduced ; as theorems instead of axioms. (defaxiom standardp-plus (implies (and (standardp x) (standardp y)) (standardp (+ x y)))) (defaxiom standardp-uminus (equal (standardp (- x)) (standardp (fix x)))) (defaxiom standardp-times (implies (and (standardp x) (standardp y)) (standardp (* x y)))) (defaxiom standardp-udivide (equal (standardp (/ x)) (standardp (fix x)))) (defaxiom standardp-complex (equal (standardp (complex x y)) (and (standardp (realfix x)) (standardp (realfix y))))) ; The following should not be needed; in fact, when attempting to interpret ; this terms as a rewrite rule, ACL2(r) will complain because (cons-term ; 'standardp ''1) is *t*. (defaxiom standardp-one (standardp 1) :rule-classes nil) ;; Now, we have some theorems (axioms?) about standard-part. (defaxiom standard-part-of-standardp (implies (and (acl2-numberp x) (standardp x)) (equal (standard-part x) x))) (defaxiom standardp-standard-part (implies (i-limited x) (standardp (standard-part x)))) (defaxiom standard-part-of-reals-is-idempotent (implies (realp x) (equal (standard-part (standard-part x)) (standard-part x)))) (defaxiom standard-part-of-complex (equal (standard-part (complex x y)) (complex (standard-part x) (standard-part y)))) ;; We consider the arithmetic operators now. (defaxiom standard-part-of-plus (equal (standard-part (+ x y)) (+ (standard-part (fix x)) (standard-part (fix y))))) (defaxiom standard-part-of-uminus (equal (standard-part (- x)) (- (standard-part (fix x))))) (defaxiom standard-part-of-times (implies (and (i-limited x) (i-limited y)) (equal (standard-part (* x y)) (* (standard-part x) (standard-part y))))) (defaxiom standard-part-of-udivide (implies (and (i-limited x) (not (i-small x))) (equal (standard-part (/ x)) (/ (standard-part x))))) (defaxiom standard-part-<= (implies (and (realp x) (realp y) (<= x y)) (<= (standard-part x) (standard-part y)))) (defaxiom small-are-limited (implies (i-small x) (i-limited x)) :rule-classes (:forward-chaining :rewrite)) (in-theory (disable (:rewrite small-are-limited))) (defaxiom standards-are-limited (implies (and (acl2-numberp x) (standardp x)) (i-limited x)) :rule-classes (:forward-chaining :rewrite)) (defthm standard-constants-are-limited (implies (and (syntaxp (and (consp x) (eq (car x) 'quote))) (acl2-numberp x) (standardp x)) (i-limited x))) (in-theory (disable (:rewrite standards-are-limited))) (defaxiom limited-integers-are-standard (implies (and (i-limited x) (integerp x)) (standardp x)) :rule-classes (:forward-chaining :rewrite)) (in-theory (disable (:rewrite limited-integers-are-standard))) (defaxiom standard+small->i-limited (implies (and (standardp x) (i-small eps)) (i-limited (+ x eps)))) (in-theory (disable standard+small->i-limited)) ) (defdoc acl2-numberp ":Doc-Section ACL2::ACL2-built-ins recognizer for numbers~/ ~c[(acl2-numberp x)] is true if and only if ~c[x] is a number, i.e., a rational or complex rational number.~/~/") (defdoc + ":Doc-Section ACL2::ACL2-built-ins addition macro~/ ~c[+] is really a macro that expands to calls of the function ~ilc[binary-+]. So for example ~bv[] (+ x y 4 z) ~ev[] represents the same term as ~bv[] (binary-+ x (binary-+ y (binary-+ 4 z))). ~ev[] ~l[binary-+].~/~/") (defdoc binary-+ ":Doc-Section ACL2::ACL2-built-ins addition function~/ Completion Axiom (~c[completion-of-+]): ~bv[] (equal (binary-+ x y) (if (acl2-numberp x) (if (acl2-numberp y) (binary-+ x y) x) (if (acl2-numberp y) y 0))) ~ev[]~/ ~il[Guard] for ~c[(binary-+ x y)]: ~bv[] (and (acl2-numberp x) (acl2-numberp y)) ~ev[] Notice that like all arithmetic functions, ~c[binary-+] treats non-numeric inputs as ~c[0]. Calls of the macro ~ilc[+] expand to calls of ~c[binary-+]; ~pl[+].") (defdoc binary-* ":Doc-Section ACL2::ACL2-built-ins multiplication function~/ Completion Axiom (~c[completion-of-*]): ~bv[] (equal (binary-* x y) (if (acl2-numberp x) (if (acl2-numberp y) (binary-* x y) 0) 0)) ~ev[]~/ ~il[Guard] for ~c[(binary-* x y)]: ~bv[] (and (acl2-numberp x) (acl2-numberp y)) ~ev[] Notice that like all arithmetic functions, ~c[binary-*] treats non-numeric inputs as ~c[0]. Calls of the macro ~ilc[*] expand to calls of ~c[binary-*]; ~pl[*].") (defdoc - ":Doc-Section ACL2::ACL2-built-ins macro for subtraction and negation~/ ~l[binary-+] for addition and ~pl[unary--] for negation.~/ Note that ~c[-] represents subtraction as follows: ~bv[] (- x y) ~ev[] represents the same term as ~bv[] (+ x (- y)) ~ev[] which is really ~bv[] (binary-+ x (unary-- y)). ~ev[] Also note that ~c[-] represents arithmetic negation as follows: ~bv[] (- x) ~ev[] expands to ~bv[] (unary-- x). ~ev[] ") (defdoc unary-- ":Doc-Section ACL2::ACL2-built-ins arithmetic negation function~/ Completion Axiom (~c[completion-of-unary-minus]): ~bv[] (equal (unary-- x) (if (acl2-numberp x) (unary-- x) 0)) ~ev[]~/ ~il[Guard] for ~c[(unary-- x)]: ~bv[] (acl2-numberp x) ~ev[] Notice that like all arithmetic functions, ~c[unary--] treats non-numeric inputs as ~c[0]. Calls of the macro ~ilc[-] on one argument expand to calls of ~c[unary--]; ~pl[-].") (defdoc unary-/ ":Doc-Section ACL2::ACL2-built-ins reciprocal function~/ Completion Axiom (~c[completion-of-unary-/]): ~bv[] (equal (unary-/ x) (if (and (acl2-numberp x) (not (equal x 0))) (unary-/ x) 0)) ~ev[]~/ ~il[Guard] for ~c[(unary-/ x)]: ~bv[] (and (acl2-numberp x) (not (equal x 0))) ~ev[] Notice that like all arithmetic functions, ~c[unary-/] treats non-numeric inputs as ~c[0]. Calls of the macro ~ilc[/] on one argument expand to calls of ~c[unary-/]; ~pl[/].") (defdoc < ":Doc-Section ACL2::ACL2-built-ins less-than~/ Completion Axiom (~c[completion-of-<]): ~bv[] (equal (< x y) (if (and (rationalp x) (rationalp y)) (< x y) (let ((x1 (if (acl2-numberp x) x 0)) (y1 (if (acl2-numberp y) y 0))) (or (< (realpart x1) (realpart y1)) (and (equal (realpart x1) (realpart y1)) (< (imagpart x1) (imagpart y1))))))) ~ev[]~/ ~il[Guard] for ~c[(< x y)]: ~bv[] (and (rationalp x) (rationalp y)) ~ev[] Notice that like all arithmetic functions, ~c[<] treats non-numeric inputs as ~c[0]. This function has the usual meaning on the rational numbers, but is extended to the complex rational numbers using the lexicographic order: first the real parts are compared, and if they are equal, then the imaginary parts are compared.") (defdoc car ":Doc-Section ACL2::ACL2-built-ins returns the first element of a non-empty list, else ~c[nil]~/ Completion Axiom (~c[completion-of-car]): ~bv[] (equal (car x) (cond ((consp x) (car x)) (t nil))) ~ev[]~/ ~il[Guard]: ~bv[] (or (consp x) (equal x nil)) ~ev[] Notice that in the ACL2 logic, ~c[car] returns ~c[nil] for every ~il[atom].") (defdoc cdr ":Doc-Section ACL2::ACL2-built-ins returns the second element of a ~ilc[cons] pair, else ~c[nil]~/ Completion Axiom (~c[completion-of-cdr]): ~bv[] (equal (cdr x) (cond ((consp x) (cdr x)) (t nil))) ~ev[]~/ ~il[Guard]: ~bv[] (or (consp x) (equal x nil)) ~ev[] Notice that in the ACL2 logic, ~c[cdr] returns ~c[nil] for every ~il[atom].") (defdoc char-code ":Doc-Section ACL2::ACL2-built-ins the numeric code for a given character~/ Completion Axiom (~c[completion-of-char-code]): ~bv[] (equal (char-code x) (if (characterp x) (char-code x) 0)) ~ev[]~/ ~il[Guard] for ~c[(char-code x)]: ~bv[] (characterp x) ~ev[] This function maps all non-characters to ~c[0].") (defdoc characterp ":Doc-Section ACL2::ACL2-built-ins recognizer for ~il[characters]~/ ~c[(characterp x)] is true if and only if ~c[x] is a character.~/~/") (defdoc code-char ":Doc-Section ACL2::ACL2-built-ins the character corresponding to a given numeric code~/ Completion Axiom (~c[completion-of-code-char]): ~bv[] (equal (code-char x) (if (and (integerp x) (>= x 0) (< x 256)) (code-char x) (code-char 0))) ~ev[]~/ ~il[Guard] for ~c[(code-char x)]: ~bv[] (and (integerp x) (>= x 0) (< x 256)) ~ev[] ACL2 supports 8-bit ~il[characters]. Inputs not between ~c[0] and ~c[255] are treated as ~c[0].") (defdoc complex ":Doc-Section ACL2::ACL2-built-ins create an ACL2 number~/ ~bv[] Examples: (complex x 3) ; x + 3i, where i is the principal square root of -1 (complex x y) ; x + yi (complex x 0) ; same as x, for rational numbers x~/ ~ev[] The function ~c[complex] takes two rational number arguments and returns an ACL2 number. This number will be of type ~c[(complex rational)] [as defined in the Common Lisp language], except that if the second argument is zero, then ~c[complex] returns its first argument. The function ~ilc[complex-rationalp] is a recognizer for complex rational numbers, i.e. for ACL2 numbers that are not rational numbers. The reader macro ~c[#C] (which is the same as ~c[#c]) provides a convenient way for typing in complex numbers. For explicit rational numbers ~c[x] and ~c[y], ~c[#C(x y)] is read to the same value as ~c[(complex x y)]. The functions ~ilc[realpart] and ~ilc[imagpart] return the real and imaginary parts (respectively) of a complex (possibly rational) number. So for example, ~c[(realpart #C(3 4)) = 3], ~c[(imagpart #C(3 4)) = 4], ~c[(realpart 3/4) = 3/4], and ~c[(imagpart 3/4) = 0]. The following built-in axiom may be useful for reasoning about complex numbers. ~bv[] (defaxiom complex-definition (implies (and (real/rationalp x) (real/rationalp y)) (equal (complex x y) (+ x (* #c(0 1) y)))) :rule-classes nil) ~ev[] A completion axiom that shows what ~c[complex] returns on arguments violating its ~il[guard] (which says that both arguments are rational numbers) is the following, named ~c[completion-of-complex]. ~bv[] (equal (complex x y) (complex (if (rationalp x) x 0) (if (rationalp y) y 0))) ~ev[] ") (defdoc cons ":Doc-Section ACL2::ACL2-built-ins pair and list constructor~/ ~c[(cons x y)] is a pair whose first component is ~c[x] and second component is ~c[y]. If ~c[y] is a list, then ~c[(cons x y)] is a list that has an addtional element ~c[x] on the front.~/~/") (defdoc consp ":Doc-Section ACL2::ACL2-built-ins recognizer for ~il[cons] pairs~/ ~c[(consp x)] is true if and only if ~c[x] is a ~il[cons] pair.~/~/") (defdoc coerce ; Jared Davis has written a faster version of coercing a character list to a ; string, which is displayed just below. But we have decided not to try to ; meddle with the underlying Lisp implementation of coerce (though on 2/27/09 ; Bob Boyer temporarily added a patch from Gary Byers to hons-raw.lisp to speed ; this up for CCL). Jared adds (6/30/09) that CCL now handles coerce ; efficiently, both to strings and to lists. ; (defun my-coerce (chars) ; (let* ((length (the integer (length (the list chars)))) ; (str (the vector (make-string (the integer length)))) ; (i (the integer 0))) ; (loop for char in chars ; do ; (setf (aref (the vector str) (the integer i)) ; (the character char)) ; (incf (the integer i))) ; str)) ":Doc-Section ACL2::ACL2-built-ins coerce a character list to a string and a string to a list~/ Completion Axiom (~c[completion-of-coerce]): ~bv[] (equal (coerce x y) (cond ((equal y 'list) (if (stringp x) (coerce x 'list) nil)) (t (coerce (make-character-list x) 'string)))) ~ev[]~/ ~il[Guard] for ~c[(coerce x y)]: ~bv[] (if (equal y 'list) (stringp x) (if (equal y 'string) (character-listp x) nil)) ~ev[] Also see community book ~c[books/misc/fast-coerce.lisp], contributed by Jared Davis, for a version of ~c[coerce] that may be faster for Common Lisp implementations other than CCL 1.3 or later, if the second argument is ~c['list] (for coercing a string to a list). ~/") (defdoc denominator ":Doc-Section ACL2::ACL2-built-ins divisor of a ratio in lowest terms~/ Completion Axiom (~c[completion-of-denominator]): ~bv[] (equal (denominator x) (if (rationalp x) (denominator x) 1)) ~ev[]~/ ~il[Guard] for ~c[(denominator x)]: ~bv[] (rationalp x) ~ev[] ~/") (defdoc equal ":Doc-Section ACL2::ACL2-built-ins true equality~/ ~c[(equal x y)] is equal to ~c[t] or ~c[nil], according to whether or not ~c[x] and ~c[y] are the same value.~/ For a discussion of the various idioms for testing against 0, ~l[zero-test-idioms].~/") (defdoc if ":Doc-Section ACL2::ACL2-built-ins if-then-else function~/ ~c[(if x y z)] is equal to ~c[y] if ~c[x] is any value other than ~c[nil], and is equal to ~c[z] if ~c[x] is ~c[nil].~/ Only one of ~c[y], ~c[z] is evaluated when ~c[(if x y z)] is evaluated. ~c[If] has a ~il[guard] of ~c[t]. ~c[If] is part of Common Lisp. See any Common Lisp documentation for more information.~/") (defdoc imagpart ":Doc-Section ACL2::ACL2-built-ins imaginary part of a complex number~/ Completion Axiom (~c[completion-of-imagpart]): ~bv[] (equal (imagpart x) (if (acl2-numberp x) (imagpart x) 0)) ~ev[]~/ ~il[Guard] for ~c[(imagpart x)]: ~bv[] (acl2-numberp x) ~ev[] ~/") (defdoc integerp ":Doc-Section ACL2::ACL2-built-ins recognizer for whole numbers~/ ~c[(integerp x)] is true if and only if ~c[x] is an integer.~/~/") (defdoc intern-in-package-of-symbol ":Doc-Section ACL2::ACL2-built-ins create a symbol with a given name~/ Completion Axiom (~c[completion-of-intern-in-package-of-symbol]): ~bv[] (equal (intern-in-package-of-symbol x y) (if (and (stringp x) (symbolp y)) (intern-in-package-of-symbol x y) nil)) ~ev[]~/ ~il[Guard] for ~c[(intern-in-package-of-symbol x y)]: ~bv[] (and (stringp x) (symbolp y)) ~ev[] Intuitively, ~c[(intern-in-package-of-symbol x y)] creates a symbol with ~ilc[symbol-name] ~c[x] ~il[intern]ed in the package containing ~c[y]. More precisely, suppose ~c[x] is a string, ~c[y] is a symbol with ~ilc[symbol-package-name] pkg and that the ~ilc[defpkg] event creating pkg had the list of symbols imports as the value of its second argument. Then ~c[(intern-in-package-of-symbol x y)] returns a symbol, ans, the ~ilc[symbol-name] of ans is ~c[x], and the ~ilc[symbol-package-name] of ans is pkg, unless ~c[x] is the ~ilc[symbol-name] of some member of imports with ~ilc[symbol-package-name] ipkg, in which case the ~ilc[symbol-package-name] of ans is ipkg. Because ~ilc[defpkg] requires that there be no duplications among the ~ilc[symbol-name]s of the imports, ~c[intern-in-package-of-symbol] is uniquely defined. For example, suppose ~c[\"MY-PKG\"] was created by ~bv[] (defpkg \"MY-PKG\" '(ACL2::ABC LISP::CAR)). ~ev[] Let ~c[w] be ~c['my-pkg::witness]. Observe that ~bv[] (symbolp w) is t ; w is a symbol (symbol-name w) is \"WITNESS\" ; w's name is \"WITNESS\" (symbol-package-name w) is \"MY-PKG\" ; w is in the package \"MY-PKG\" ~ev[] The construction of ~c[w] illustrates one way to obtain a symbol in a given package: write it down as a constant using the double-colon notation. But another way to obtain a symbol in a given package is to create it with ~c[intern-in-package-of-symbol]. ~bv[] (intern-in-package-of-symbol \"XYZ\" w) is MY-PKG::XYZ (intern-in-package-of-symbol \"ABC\" w) is ACL2::ABC (intern-in-package-of-symbol \"CAR\" w) is LISP::CAR (intern-in-package-of-symbol \"car\" w) is MY-PKG::|car| ~ev[]") (defdoc numerator ":Doc-Section ACL2::ACL2-built-ins dividend of a ratio in lowest terms~/ Completion Axiom (~c[completion-of-numerator]): ~bv[] (equal (numerator x) (if (rationalp x) (numerator x) 0)) ~ev[]~/ ~il[Guard] for ~c[(numerator x)]: ~bv[] (rationalp x) ~ev[] ~/") (defdoc rationalp ":Doc-Section ACL2::ACL2-built-ins recognizer for rational numbers (ratios and integers)~/ ~c[(rationalp x)] is true if and only if ~c[x] is an rational number.~/~/") (defdoc realpart ":Doc-Section ACL2::ACL2-built-ins real part of a complex number~/ Completion Axiom (~c[completion-of-realpart]): ~bv[] (equal (realpart x) (if (acl2-numberp x) (realpart x) 0)) ~ev[]~/ ~il[Guard] for ~c[(realpart x)]: ~bv[] (acl2-numberp x) ~ev[] ~/") (defdoc stringp ":Doc-Section ACL2::ACL2-built-ins recognizer for strings~/ ~c[(stringp x)] is true if and only if ~c[x] is a string.~/~/") (defdoc symbol-name ":Doc-Section ACL2::ACL2-built-ins the name of a symbol (a string)~/ Completion Axiom (~c[completion-of-symbol-name]): ~bv[] (equal (symbol-name x) (if (symbolp x) (symbol-name x) \"\")) ~ev[]~/ ~il[Guard] for ~c[(symbol-name x)]: ~bv[] (symbolp x) ~ev[] ~/") (defdoc symbol-package-name ":Doc-Section ACL2::ACL2-built-ins the name of the package of a symbol (a string)~/ WARNING: While ~c[symbol-package-name] behaves properly on all ACL2 objects, it may give surprising results when called in raw Lisp. For more details ~pl[pkg-imports], in particular the discussion there of the ~c[\"COMMON-LISP\"] package. Completion Axiom (~c[completion-of-symbol-package-name]): ~bv[] (equal (symbol-package-name x) (if (symbolp x) (symbol-package-name x) \"\")) ~ev[]~/ ~il[Guard] for ~c[(symbol-package-name x)]: ~bv[] (symbolp x) ~ev[] Note: ~c[Symbol-package-name] may diverge from the name of the symbol's package in raw Lisp, in the case that this package is the main Lisp package. For example, in GCL ~c[(symbol-package-name 'car)] evaluates to \"COMMON-LISP\" even though the actual package name for the symbol, ~c[car], is \"LISP\".~/") (defdoc symbolp ":Doc-Section ACL2::ACL2-built-ins recognizer for symbols~/ ~c[(symbolp x)] is true if and only if ~c[x] is a symbol.~/~/") (defdoc quote ":Doc-Section ACL2::ACL2-built-ins create a constant~/ The form ~c[(quote x)] evaluates to ~c[x]. See any Common Lisp documentation.~/~/") (defun double-rewrite (x) (declare (xargs :guard t)) ":Doc-Section Miscellaneous cause a term to be rewritten twice~/ Logically, ~c[double-rewrite] is the ~ilc[identity] function: ~c[(double-rewrite x)] is equal to ~c[x]. However, the ACL2 rewriter treats calls of ~c[double-rewrite] in the following special manner. When it encounters a term ~c[(double-rewrite u)], it first rewrites ~c[u] in the current context, and then the rewriter rewrites the result. Such double-rewriting is rarely necessary, but it can be useful when rewriting under non-trivial equivalence relations (~pl[equivalence]). The following example will illustrate the issue. ~bv[] ; Define an equivalence relation. (defun my-equiv (x y) (equal x y)) (defequiv my-equiv) ; Define a unary function whose argument is preserved by my-equiv. (defun foo (x) (declare (ignore x)) t) (defcong my-equiv equal (foo x) 1) ; Define some other unary functions. (defun g (x) x) (defun h1 (x) x) (defun h2 (x) x) ; Prove some lemmas and then disable the functions above. (defthm lemma-1 (my-equiv (h1 x) (h2 x))) (defthm lemma-2 (foo (h2 x))) (defthm lemma-3 (implies (foo x) (equal (g x) x))) (in-theory (union-theories (theory 'minimal-theory) '(lemma-1 lemma-2 lemma-3 my-equiv-implies-equal-foo-1))) ; Attempt to prove a simple theorem that follows ``obviously'' from the ; events above. (thm (equal (g (h1 a)) (h1 a))) ~ev[] We might expect the proof of this final ~c[thm] to succeed by the following reasoning. It is immediate from ~c[lemma-3] provided we can establish ~c[(foo (h1 a))]. By the ~c[defcong] event above, we know that ~c[(foo (h1 a))] equals ~c[(foo (h2 a))] provided ~c[(my-equiv (h1 a) (h2 a))]; but this is immediate from ~c[lemma-1]. And finally, ~c[(foo (h2 a))] is true by ~c[lemma-2]. Unfortunately, the proof fails. But fortunately, ACL2 gives the following useful warning when ~c[lemma-3] is submitted: ~bv[] ACL2 Warning [Double-rewrite] in ( DEFTHM LEMMA-3 ...): In the :REWRITE rule generated from LEMMA-3, equivalence relation MY-EQUIV is maintained at one problematic occurrence of variable X in hypothesis (FOO X), but not at any binding occurrence of X. Consider replacing that occurrence of X in this hypothesis with (DOUBLE-REWRITE X). See :doc double- rewrite for more information on this issue. ~ev[] We can follow the warning's advice by changing ~c[lemma-3] to the following. ~bv[] (defthm lemma-3 (implies (foo (double-rewrite x)) (equal (g x) x))) ~ev[] With this change, the proof succeeds for the final ~c[thm] above. In practice, it should suffice for users to follow the advice given in the ``~c[Double-rewrite]'' warnings, by adding calls of ~c[double-rewrite] around certain variable occurrences. But this can cause inefficiency in large proof efforts. For that reason, and for completeness, it seems prudent to explain more carefully what is going on; and that is what we do for the remainder of this ~il[documentation] topic. Optionally, also see the paper ``Double Rewriting for Equivalential Reasoning in ACL2'' by Matt Kaufmann and J Strother Moore, in the proceedings of the 2006 ACL2 Workshop (paper is published in ACM Digital Library, ~url[http://portal.acm.org/toc.cfm?id=1217975]).~/ ~st[Suggesting congruence rules.] Sometimes the best way to respond to a ``~c[Double-rewrite]'' warning may be to prove a congruence rule. Consider for example this rule. ~bv[] (defthm insert-sort-is-id (perm (insert-sort x) x)) ~ev[] Assuming that ~c[perm] has been identified as an ~il[equivalence] relation (~pl[defequiv]), we will get the following warning. ~bv[] ACL2 Warning [Double-rewrite] in ( DEFTHM INSERT-SORT-IS-ID ...): In a :REWRITE rule generated from INSERT-SORT-IS-ID, equivalence relation PERM is maintained at one problematic occurrence of variable X in the right-hand side, but not at any binding occurrence of X. Consider replacing that occurrence of X in the right-hand side with (DOUBLE-REWRITE X). See :doc double-rewrite for more information on this issue. ~ev[] The problem is that the second occurrence of ~c[x] (the right-hand side of the rule ~c[insert-sort-is-id]) is in a context where ~c[perm] is to be maintained, yet in this example, the argument ~c[x] of ~c[insert-sort] on the left-hand side of that rule is in a context where ~c[perm] will not be maintained. This can lead one to consider the possibility that ~c[perm] could be maintained in that left-hand side occurrence of ~c[x], and if so, to prove the following congruence rule. ~bv[] (defcong perm perm (insert-sort x) 1) ~ev[] This will eliminate the above warning for ~c[insert-sort-is-id]. More important, this ~ilc[defcong] event would probably be useful, since it would allow rewrite rules with equivalence relation ~c[perm] to operate on the first argument of any call of ~c[insert-sort] whose context calls for maintaining ~c[perm]. ~st[Details on double-rewrite.] The reader who wants these details may first wish to ~pl[equivalence] for relevant review. The ACL2 rewriter takes a number of contextual arguments, including the generated equivalence relation being maintained (~pl[congruence]) and an association list that maps variables to terms. We call the latter alist the ~c[unify-subst] because it is produced by unifying (actually matching) a pattern against a current term; let us explain this point by returning to the example above. Consider what happens when the rewriter is given the top-level goal of the ~c[thm] above. ~bv[] (equal (g (h1 a)) (h1 a)) ~ev[] This rewrite is performed with the empty alist (~c[unify-subst]), and is begun by rewriting the first argument (in that same empty ~c[unify-subst]): ~bv[] (g (h1 a)) ~ev[] Note that the only equivalence relation being maintained at this point is ~c[equal]. Now, the rewriter notices that the left-hand side of ~c[lemma-3], which is ~c[(g x)], matches ~c[(g (h1 a))]. The rewriter thus creates a ~c[unify-subst] binding ~c[x] to ~c[(h1 a)]: ~c[((x . (h1 a)))]. It now attempts to rewrite the hypothesis of ~c[lemma-3] to ~c[t] under this ~c[unify-subst]. Consider what happens now if the hypothesis of ~c[lemma-3] is ~c[(foo x)]. To rewrite this hypothesis under a ~c[unify-subst] of ~c[((x . (h1 a)))], it will first rewrite ~c[x] under this ~c[unify-subst]. The key observation here is that this rewrite takes place simply by returning the value of ~c[x] in the ~c[unify-subst], namely ~c[(h1 a)]. No further rewriting is done! The efficiency of the ACL2 rewriter depends on such caching of previous rewriting results. But suppose that, instead, the hypothesis of ~c[lemma-3] is ~c[(foo (double-rewrite x))]. As before, the rewriter dives to the first argument of this call of ~c[foo]. But this time the rewriter sees the call ~c[(double-rewrite x)], which it handles as follows. First, ~c[x] is rewritten as before, yielding ~c[(h1 a)]. But now, because of the call of ~c[double-rewrite], the rewriter takes ~c[(h1 a)] and rewrites it under the empty ~c[unify-subst]. What's more, because of the ~c[defcong] event above, this rewrite takes place in a context where it suffices to maintain the equivalence relation ~c[my-equiv]. This allows for the application of ~c[lemma-1], hence ~c[(h1 a)] is rewritten (under ~c[unify-subst] = ~c[nil]) to ~c[(h2 a)]. Popping back up, the rewriter will now rewrite the call of ~c[foo] to ~c[t] using ~c[lemma-2]. The example above explains how the rewriter treats calls of ~c[double-rewrite], but it may leave the unfortunate impression that the user needs to consider each ~c[:]~ilc[rewrite] or ~c[:]~ilc[linear] rule carefully, just in case a call of ~c[double-rewrite] may be appropriate. Fortunately, ACL2 provides a ``[Double-rewrite]'' warning to inform the user of just this sort of situation. If you don't see this warning when you submit a (~c[:]~ilc[rewrite] or ~c[:]~ilc[linear]) rule, then the issue described here shouldn't come up for that rule. Such warnings may appear for hypotheses or right-hand side of a ~c[:]~ilc[rewrite] rule, and for hypotheses or full conclusion (as opposed to just the trigger term) of a ~c[:]~ilc[linear] rule. If you do see a ``[Double-rewrite]'' warning, then should you add the indicated call(s) of ~c[double-rewrite]? At the time of writing this ~il[documentation], the answer is not clear. Early experiments with double rewriting suggested that it may be too expensive to call ~c[double-rewrite] in every instance where a warning indicates that there could be an advantage to doing so. And at the time of this writing, the ACL2 regression suite has about 1900 such warnings (but note that books were developed before ~c[double-rewrite] or the ``[Double-rewrite]'' warning were implemented), which suggests that one can often do fine just ignoring such warnings. However, it seems advisable to go ahead and add the calls of ~c[double-rewrite] indicated by the warnings unless you run across efficiency problems caused by doing so. Of course, if you decide to ignore all such warnings you can execute the event:~nl[] ~c[(]~ilc[set-inhibit-warnings]~c[ \"Double-rewrite\")]. Finally, we note that it is generally not necessary to call ~c[double-rewrite] in order to get its effect in the following case, where the discussion above might have led one to consider a call of ~c[double-rewrite]: a hypothesis is a variable, or more generally, we are considering a variable occurrence that is a branch of the top-level ~c[IF] structure of a hypothesis. The automatic handling of this case, by a form of double rewriting, was instituted in ACL2 Version_2.9 and remains in place with the introduction of ~c[double-rewrite]. Here is a simple illustrative example. Notice that ~c[foo-holds] applies to prove the final ~ilc[thm] below, even without a call of ~c[double-rewrite] in the hypothesis of ~c[foo-holds], and that there is no ``[Double-rewrite]'' warning when submitting ~c[foo-holds]. ~bv[] (encapsulate (((foo *) => *) ((bar *) => *)) (local (defun foo (x) (declare (ignore x)) t)) (local (defun bar (x) (declare (ignore x)) t)) (defthm foo-holds (implies x (equal (foo x) t))) (defthm bar-holds-propositionally (iff (bar x) t))) (thm (foo (bar y))) ~ev[]~/" x) #-acl2-loop-only (progn ; The following variables implement time limits. Only bind-acl2-time-limit ; should bind *acl2-time-limit*, as described in a comment in ; bind-acl2-time-limit, where one may find a a discussion of how these ; variables are handled. (defparameter *acl2-time-limit* nil) (defparameter *acl2-time-limit-boundp* nil) ) (defun chk-with-prover-time-limit-arg (time) (declare (xargs :guard t)) (or (let ((time (if (and (consp time) (null (cdr time))) (car time) time))) (and (rationalp time) (< 0 time) time)) (hard-error 'with-prover-time-limit "The first argument to ~x0 must evaluate to a non-negative ~ rational number or a list containing such a number, but ~ such an argument has evaluated to ~x1." (list (cons #\0 'with-prover-time-limit) (cons #\1 time))))) #-acl2-loop-only (defmacro with-prover-time-limit1-raw (time form) ; This macro does not check that time is of a suitable form (see :doc ; with-prover-time-limit). However, with-prover-time-limit lays down a call of ; chk-with-prover-time-limit-arg, which is called before return-last passes ; control to the present macro. (let ((time-limit-var (gensym))) `(let* ((,time-limit-var ,time) (temp (+ (get-internal-time) (* internal-time-units-per-second (if (consp ,time-limit-var) (car ,time-limit-var) ,time-limit-var)))) (*acl2-time-limit* (if (or (consp ,time-limit-var) (null *acl2-time-limit*)) temp (min temp *acl2-time-limit*)))) ,form))) (defmacro with-prover-time-limit1 (time form) `(return-last 'with-prover-time-limit1-raw ,time ,form)) (defmacro with-prover-time-limit (time form) ":Doc-Section Other limit the time for proofs~/ ~bv[] Examples: ; Limit (mini-proveall) to about 1/4 second: (with-prover-time-limit 1/4 (mini-proveall)) ; Limit (mini-proveall) to about 1/4 second, even if surrounding call of ; with-prover-time-limit provides for a more restrictive bound: (with-prover-time-limit '(1/4) (mini-proveall)) ; Limit the indicated theorem to about 1/50 second, and if the proof does not ; complete or it fails, then put down a label instead. (mv-let (erp val state) (with-prover-time-limit 1/50 (thm (equal (append (append x x) x) (append x x x)))) (if erp (deflabel foo :doc \"Attempt failed.\") (value (list :succeeded-with val))))~/ General Form: (with-prover-time-limit time form) ~ev[] where ~c[time] evaluates to a positive rational number or to a list containing such, and ~c[form] is arbitrary. Logically, ~c[(with-prover-time-limit time form)] is equivalent to ~c[form]. However, if the time for evaluation of ~c[form] exceeds the value specified by ~c[time], and if ACL2 notices this fact during a proof, then that proof will abort, for example like this: ~bv[] ACL2 Error in ( DEFTHM PERM-REFLEXIVE ...): Out of time in the rewriter. ~ev[] If there is already a surrounding call of ~c[with-prover-time-limit] that has set up an expiration time, the inner ~c[with-prover-time-limit] call is not allowed to push that time further into the future unless the inner time is specified as a list containing a rational, rather than as a rational. Note that by default, the time used is runtime (cpu time); to switch to realtime (elapsed time), ~pl[get-internal-time]. For a related utility based on prover steps instead of time, ~pl[with-prover-step-limit]; also ~pl[set-prover-step-limit]. Those utilities have the advantage of having platform-independent behavior, unlike time limits, which of course are generally less restrictive for faster processors. But note that the prover steps counted need not correspond closely to prover time. Although ~c[with-prover-time-limit] behaves like an ACL2 function in the sense that it evaluates both its arguments, it is however actually a macro that behaves as follows. (1) The value of its first (time limit) argument affects the evaluation of its second argument (by causing an error during that evaluation if the time for completion is insufficient). (2) The second argument can return multiple values (~pl[mv]), which are then returned by the call of ~c[with-prover-time-limit]. (3) Thus, there is not a fixed number of values returned by ~c[with-prover-time-limit]. If you find that the time limit appears to be implemented too loosely, it may be because the prover only checks the time elapsed at certain points during the proof process, for example at entry to the rewriter. For example, if you write your own ~ilc[clause-processor] that does an expensive computation, the time is unlikely to be checked during its execution. If however you find the time limit seems to be ignored even during ordinary prover operation, you are encouraged to email an example to the ACL2 implementors with instructions on how to observe the undesirable behavior. This information can perhaps be used to improve ACL2 by the insertion of more checks for expiration of the time limit. The rest of this documentation topic explains the rather subtle logical story, and is not necessary for understanding how to use ~c[with-prover-time-limit]. The ACL2 ~ilc[state] object logically contains a field called the ~c[acl2-oracle], which is an arbitrary true list of objects. This field can be read by a function called ~c[read-acl2-oracle], which however is untouchable (~pl[push-untouchable]), meaning that it is cannot be called by ACL2 users. The ~c[acl2-oracle] field is thus ``secret''. Our claim is that any ACL2 session makes sense for ~st[some] value of ~c[acl2-oracle] in the initial ~c[state] for that session. Logically, ~c[with-prover-time-limit] is a no-op, just returning its second value. But under the hood, it provides a ``hint'' for the ~c[acl2-oracle], so that (logically speaking) when its first element (~ilc[car]) is consulted by ACL2's prover to see if the time limit has expired, it gets the ``right'' answer (specifically, either nil if all is well or else a message to print if the time limit has expired). Logically, the ~c[acl2-oracle] is then ~ilc[cdr]'ed ~-[] that is, its first element is popped off ~-[] so that future results from ~c[read-acl2-oracle] are independent of the one just obtained.~/" `(with-prover-time-limit1 (chk-with-prover-time-limit-arg ,time) ,form)) #-acl2-loop-only (defparameter *time-limit-tags* nil) (defmacro catch-time-limit5 (form) ; Keep in sync with catch-time-limit5@par. `(mv-let (step-limit x1 x2 x3 x4 ; values that cannot be stobjs state) #+acl2-loop-only ,form ; so, except for state, form does not return a stobj #-acl2-loop-only (progn (setq *next-acl2-oracle-value* nil) (catch 'time-limit5-tag (let ((*time-limit-tags* (add-to-set-eq 'time-limit5-tag *time-limit-tags*))) ,form))) (pprogn (f-put-global 'last-step-limit step-limit state) (mv-let (nullp temp state) (read-acl2-oracle state) ; clears *next-acl2-oracle-value* (declare (ignore nullp)) (cond (temp (mv step-limit temp nil nil nil nil state)) (t (mv step-limit nil x1 x2 x3 x4 state))))))) #+acl2-par (defmacro catch-time-limit5@par (form) ; Keep in sync with catch-time-limit5. `(mv-let (step-limit x1 x2 x3 x4) ; values that cannot be stobjs #+acl2-loop-only ,form ; so, form returns neither a stobj nor state #-acl2-loop-only (progn ; Parallelism blemish: there is a rare race condition related to ; *next-acl2-oracle-value*. Specifically, a thread might set the value of ; *next-acl2-oracle-value*, throw the 'time-limit5-tag, and the value of ; *next-acl2-oracle-value* wouldn't be read until after that tag was caught. ; In the meantime, maybe another thread would have cleared ; *next-acl2-oracle-value*, and the needed value would be lost. (setq *next-acl2-oracle-value* nil) (catch 'time-limit5-tag (let ((*time-limit-tags* (add-to-set-eq 'time-limit5-tag *time-limit-tags*))) ,form))) (pprogn@par ; Parallelism no-fix: we haven't analyzed the code to determine whether the ; following call of (f-put-global@par 'last-step-limit ...) will be overridden ; by another similar call performed by a concurrent thread. But we can live ; with that because step-limits do not affect soundness. (f-put-global@par 'last-step-limit step-limit state) (mv-let (nullp temp) (read-acl2-oracle@par state);clears *next-acl2-oracle-value* (declare (ignore nullp)) (cond (temp (mv step-limit temp nil nil nil nil)) (t (mv step-limit nil x1 x2 x3 x4))))))) (defun time-limit5-reached-p (msg) ; Where should we call this function? We want to strike a balance between ; calling it often enough that we get reasonably tight results for ; with-prover-time-limit, yet calling it rarely enough so that we don't slow ; down the prover, in particular from calls of (get-internal-time). ; As of this writing we call this function in add-poly, ; quick-and-dirty-subsumption-replacement-step, subsumption-replacement-loop, ; rewrite, subsumes, and expand-abbreviations. Here are some results for run ; times in Allegro CL with output inhibited. For (verify-guards read-utf8-fast ; ...) in community book books/unicode/read-utf8.lisp, total cpu time went from ; 353.70 to 436.89 seconds when wrapped as (with-prover-time-limit 5000 ; (verify-guards read-utf8-fast ...)). That's about 24%. On the other hand, ; (with-prover-time-limit 5000 (mini-proveall)) had total cpu times of 720, ; 750, and 680 while (mini-proveall) had times of 710, 660, and 600, which is ; (very roughly) a 9% drop. ; At one time, including the time at which the above statistics were gathered, ; we also called this function in ev-fncall, ev, ev-lst, and ev-fncall-w (and ; at this writing we also see ev-w-lst and ev-w). But we found an infinite ; loop with ev, as documented there. (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore msg)) #-acl2-loop-only (when (and *acl2-time-limit* ; The following test isn't currently necessary, strictly speaking. But it's a ; cheap test so we include it for robustness, in case for example someone calls ; rewrite not in the scope of catch-time-limit5. (member-eq 'time-limit5-tag *time-limit-tags*) (< *acl2-time-limit* (get-internal-time))) (setq *next-acl2-oracle-value* (if (eql *acl2-time-limit* 0) "Aborting due to an interrupt." msg)) (throw 'time-limit5-tag (mv (f-get-global 'last-step-limit *the-live-state*) nil nil nil nil *the-live-state*))) nil) (defmacro catch-step-limit (form) ; Form should evaluate to a result of the form (mv step-limit erp val state). ; Wrap this macro around any form for which you want an error to occur if the ; step-limit transitions from 0 to -1. Search for occurrences of ; *step-limit-error-p* for details of how this works. #+acl2-loop-only `(mv-let (step-limit erp val state) ,form (mv-let (erp2 val2 state) (read-acl2-oracle state) (cond ((and (null erp2) (natp val2)) (mv val2 t nil state)) (t (mv step-limit erp val state))))) #-acl2-loop-only `(let ((*step-limit-error-p* t)) (assert$ (eq state *the-live-state*) (let ((sl/erp/val (catch 'step-limit-tag (mv-let (step-limit erp val ignored-state) ,form (declare (ignore ignored-state)) (list* step-limit erp val))))) (cond ((eq *step-limit-error-p* 'error) (mv -1 t nil state)) (t (mv (car sl/erp/val) (cadr sl/erp/val) (cddr sl/erp/val) state))))))) (defconst *guard-checking-values* '(t nil :nowarn :all :none)) (defun chk-with-guard-checking-arg (val) (declare (xargs :guard t)) (cond ((member-eq val *guard-checking-values*) val) (t (hard-error 'with-guard-checking "The first argument to ~x0 must evaluate to one of ~ ~v1. But such an argument has evaluated to ~x2." (list (cons #\0 'with-guard-checking) (cons #\1 *guard-checking-values*) (cons #\2 val)))))) #-acl2-loop-only (defmacro with-guard-checking1-raw (val form) ; This macro does not check that val is a member of *guard-checking-values*. ; However, with-guard-checking lays down a call of chk-with-guard-checking-arg, ; which is called before return-last passes control to the present macro. (let ((v (global-symbol 'guard-checking-on))) `(let ((,v ,val)) (declare (special ,v)) ,form))) (defmacro with-guard-checking1 (val form) `(return-last 'with-guard-checking1-raw ,val ,form)) (defmacro with-guard-checking (val form) ":Doc-Section switches-parameters-and-modes suppressing or enable guard-checking for a form~/ ~bv[] Example: ; Turn off all guard-checking for the indicated calls of append and car: (with-guard-checking :none (car (append 3 4)))~/ General Form: (with-guard-checking val form) ~ev[] where ~c[val] evaluates to a legal guard-checking value (~pl[set-guard-checking], or evaluate ~c[*guard-checking-values*] to see the list of such values), and ~c[form] is a form to be evaluated as though we had first executed ~c[(set-guard-checking val)]. Of course, this gaurd-checking setting is active only during evaluation of ~c[form], and is ignored once evaluation passes into raw Lisp functions (~pl[guards-and-evaluation])." (declare (xargs :guard t)) `(with-guard-checking1 (chk-with-guard-checking-arg ,val) ,form)) (defun abort! () ":Doc-Section Miscellaneous to return to the top-level of ACL2's command loop~/ This is an alias for ~c[a!]; ~pl[a!]. For a related feature that only pops up one level, ~pl[p!].~/~/" (declare (xargs :guard t)) #-acl2-loop-only (throw 'local-top-level :abort) nil) (defmacro a! () ":Doc-Section Miscellaneous to return to the top-level of ACL2's command loop~/ When ~c[(a!)] is evaluated inside of ACL2's command loop, the current computation is aborted and control returns to the top of the command loop, exactly as though the user had interrupted and aborted the current computation. (Note: Versions of ACL2 up to Version_3.4 provided `~c[#.]' for this purpose, but no longer; ~pl[sharp-dot-reader].) If you are at an ACL2 prompt (as opposed to a raw Lisp break), then you may type ~c[:a!] in place of ~c[(a!)]; ~pl[keyword-commands]. For a related feature that only pops up one level, ~pl[p!].~/ Logically speaking, ~c[(a!) = nil]. But imagine that it is defined in such a way that it causes a stack overflow or other resource exhaustion when called." (declare (xargs :guard t)) '(abort!)) (defun p! () ":Doc-Section Miscellaneous to pop up (at least) one level of ACL2's command loop~/ Logically speaking, ~c[(p!) = nil]. If you are already at the top level of the ACL2 command loop, rather than being in a subsidiary call of ~ilc[ld], then the keyword then a call of ~c[(p!)] returns ~c[nil] and has no other effect. Otherwise, ~c[(p!)] is evaluated inside a call of ~ilc[ld] that was made inside ACL2's command loop. In that case, the current computation is aborted and treating as causing an error, and control returns to the superior call of ~c[ld]. Here is a more detailed description of the effect of ~c[(p!)] when not at the top level of the ACL2 command loop. The current call of ~c[LD] is treated as though ~c[ld-error-action] is ~c[:RETURN!] (the default) and hence immediately returns control to the superior call of ~ilc[ld]. If all calls of ~ilc[ld] were made with the default ~c[ld-error-action] of ~c[:RETURN!], then all superior calls of ~c[ld] will then complete until you are back at top level of the ACL2 loop. For more information, ~pl[ld-error-action]. If you are at an ACL2 prompt (as opposed to a raw Lisp break), then you may type ~c[:p!] in place of ~c[(p!)]; ~pl[keyword-commands].~/~/" (declare (xargs :guard t)) #-acl2-loop-only (throw 'local-top-level :pop) nil) (in-theory (disable abort! (:executable-counterpart abort!) p! (:executable-counterpart p!) ; We could disable (:executable-counterpart hide) earlier, but this is a ; convenient place to do it. (:executable-counterpart hide))) #-acl2-loop-only (defparameter *wormhole-status-alist* nil) #-acl2-loop-only (defparameter *inhibit-wormhole-activityp* nil) (defun wormhole1 (name input form ld-specials) ; Here is the world's fanciest no-op. ; We need a guard to force guard verification to happen. This way, a ; call of wormhole1 will definitely invoke the -acl2-loop-only code ; below, not the logical version. (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore name input form ld-specials)) #+acl2-loop-only nil #-acl2-loop-only (cond (*inhibit-wormhole-activityp* nil) ((let ((temp (cdr (assoc-equal name *wormhole-status-alist*)))) ; Note: Below we inline wormhole-entry-code, to be defined later. (and (consp temp) (eq (car temp) :SKIP))) nil) (t (let ((*wormholep* t) (state *the-live-state*) (*wormhole-cleanup-form* ; WARNING: The own-cons and the progn form constructed below must be NEW! ; See note below. (let ((own-cons (cons nil nil))) (list 'progn `(cond ((car (quote ,own-cons)) (error "Attempt to execute *wormhole-cleanup-form* ~ twice!")) (t (setq *wormhole-status-alist* (put-assoc-equal ',name (f-get-global 'wormhole-status *the-live-state*) *wormhole-status-alist*)))) `(fix-trace ',(f-get-global 'trace-specs *the-live-state*)) `(setf (car (quote ,own-cons)) t) 'state)))) ; Note: What's going on above? The cleanup form's spine is new conses because ; we smash them, inserting new formi's between the cond and the setf. When ; the setf is executed it sets a flag owned by this particular form. When ; that flag is set, this form cannot be executed again. Instead it causes an ; error. I am afraid that this form might be executed repeatedly by ; interrupted interrupt processing. One might think that would be ok. But ; inspection of the value of this form reveals that it is not unusual for it ; to contain (MAKUNBOUND-GLOBAL 'WORMHOLE-STATUS *THE-LIVE-STATE*) near the ; bottom and that, in turn, would cause the f-get-global reference to ; wormhole-status in the cond to go astray (with or without an error message). ; So rather than take random luck on whether an error message is printed or an ; ``unbound value'' is returned as a value, we force an error message that ; will cause us to come back here. The likely scenarios are that the cleanup ; form got executed twice because of repeated, rapid ctrl-c inputs or that it ; got executed once by Lisp's unwind-protect and later by our acl2-unwind or ; the eval below. (cond ((null name) (return-from wormhole1 nil))) (push-car (cons "Post-hoc unwind-protect for wormhole" ; Robert Krug tells us that CCL complained before we introduced function ; below. We use a non-special lexical variable to capture the current value of ; *wormhole-cleanup-form* (as we formerly did) as we push the function onto the ; stack. (let ((acl-non-special-var *wormhole-cleanup-form*)) (function (lambda nil (eval acl-non-special-var))))) *acl2-unwind-protect-stack* 'wormhole1) ; The f-put-globals about to be performed will be done undoably. (f-put-global 'wormhole-name name state) (f-put-global 'wormhole-input input state) (f-put-global 'wormhole-status (cdr (assoc-equal name *wormhole-status-alist*)) state) (ld-fn (append `((standard-oi . (,form . ,*standard-oi*)) (standard-co . ,*standard-co*) (proofs-co . ,*standard-co*)) ld-specials) state t) (eval *wormhole-cleanup-form*) (pop (car *acl2-unwind-protect-stack*)) nil)))) (defun wormhole-p (state) ":Doc-Section Miscellaneous predicate to determine if you are inside a ~ilc[wormhole]~/ ~l[wormhole] for a discussion of wormholes. ~c[(Wormhole-p state)] returns ~c[(mv nil t state)] when evaluated inside a wormhole, else ~c[(mv nil nil state)].~/~/" (declare (xargs :guard (state-p state))) #-acl2-loop-only (when (live-state-p state) (return-from wormhole-p (value *wormholep*))) (read-acl2-oracle state)) (defun duplicates (lst) (declare (xargs :guard (symbol-listp lst))) (cond ((endp lst) nil) ((member-eq (car lst) (cdr lst)) (add-to-set-eq (car lst) (duplicates (cdr lst)))) (t (duplicates (cdr lst))))) (defun evens (l) (declare (xargs :guard (true-listp l))) (cond ((endp l) nil) (t (cons (car l) (evens (cddr l)))))) (defun odds (l) (declare (xargs :guard (true-listp l))) (evens (cdr l))) (defun set-equalp-equal (lst1 lst2) (declare (xargs :guard (and (true-listp lst1) (true-listp lst2)))) (and (subsetp-equal lst1 lst2) (subsetp-equal lst2 lst1))) ; Essay on Metafunction Support, Part 1 ; (The second part of this essay is in ld.lisp.) ; Historical Note: Metafunctions have traditionally taken just one argument: ; the term to be simplified. In 1999, Robert Krug, working on arithmetic ; metafunctions, wished to call type-set from within a metafunction. This ; inspired the creation of what were called ``extended metafunctions'' in ; contrast to the ``vanilla metafunctions'' that had gone before. (Originally, ; we used the name ``tutti-frutti metafunctions'' but that seemed too silly.) ; In June, 1999, a patch supporting extended metafunctions in Version_2.4 was ; given to Robert for experimental purposes. He extended it and gave it back ; in July, 2000. It was integrated into Version_2.6 in July, 2000. ; Historical Note 2: Previous to Version_2.7 the functions below could only be ; used in the context of a metafunction. As per a suggestion by Eric Smith, ; and incorporating an implementation provided by Robert Krug, they can now be ; called from within a syntaxp or bind-free hypothesis. We refer to a function ; that appears in one of these three contexts as a meta-level function. ; However, we still continue to use the term metafunction context, even though ; this is somewhat inconsistent. ; We wish to allow the user to call certain theorem proving functions, like ; type-set and rewrite, from within meta-level functions, without defining ; those functions logically. We provide uninterpreted function symbols, e.g., ; mfc-ts and mfc-rw+, for this purpose and arrange for them to be type-set and ; rewrite within the context of a meta-level function's execution. ; Notes: ; 1. There are two kinds of functions with the prefix ``mfc-''. ; * ordinary defined :logic mode functions used to access parts of ; the ``metafunction context.'' Example: mfc-clause. ; * uninterpreted functions with execution-only-in-meta-level-functions ; semantics. Example: mfc-ts. ; The user may be unaware that these are two different classes of symbols. ; But the first is given explicit axioms and the second is not. ; 2. If a new function is added, functions of the first type are preferred ; because they are what they seem. Such functions are defined here in ; axioms.lisp. ; 3. Functions of the second type are introduced with unknown constraints from ; a define-trusted-clause-processor event, and are defined in raw ; Lisp using the defun-overrides mechanism. ; In the next four paragraphs, we typically refer only to metafunctions, but ; most of the below applies to meta-level functions generally. ; Originally, these uninterpreted functions were essentially defstubs, ; logically, and were only to be used to make heuristic choices between correct ; alternative transformations within the metafunction. That is, practically ; speaking, the metatheorem stating the correctness of a metafunction was ; proved in the absence of any axioms about mfc-tc and mfc-rw+. Now we have ; meta-extract-contextual-fact available for reasoning about these functions; ; see :DOC meta-extract. ; Metafunctions providing this additional capability are called extended ; metafunctions and can be recognized by having more than one argument. We ; still support vanilla flavored, one argument, metafunctions. ; It is necessary to pass ``type-set'' and ``rewrite'' (really, mfc-ts and ; mfc-rw+) additional arguments, arguments not available to vanilla ; metafunctions, like the type-alist, the simplify-clause-pot-lst, etc. To ; make this convenient, we will bundle these arguments up into a record ; structure called the metafunction-context (``mfc''). When an extended ; metafunction is called from within the rewriter, we will construct a suitable ; record and pass it into the metafunction in the appropriate argument ; position. We give the user functions, e.g., mfc-clause, to access parts of ; this structure; we could provide functions for every component but in fact ; only provide the ones Robert Krug has needed so far. But in general the user ; could access the context arbitrarily with cars and cdrs from within the ; metafunction and there is nothing we can do to hide its actual structure. ; Indeed, there is no reason to do so. The required metatheorem does not ; constrain that argument at all, so nothing but heuristic decisions can be ; made on the basis of what we actually pass in. ; The main use of the metafunction-context is to pass into mfc-ts and mfc-rw+ ; (and mfc-rw). We execute them only on a live STATE argument, so that ; execution results are explained by the implicit axioms on these functions; ; see the discussion of meta-extract-contextual-fact in the Essay on ; Correctness of Meta Reasoning. Before the introduction of ; meta-extract-contextual-fact, it was necessary to insist on a live state ; argument, for correctness. Now it may well be sufficient to insist only that ; the mfc argument is the raw-Lisp *metafunction-context*, which holds a ; suitable logical world used by these mfc-xx functions. But here is our ; thinking prior to the addition of meta-extract-contextual-fact. ; The live state cannot be a value in a theorem. So these functions are ; uninterpreted there. When a metafunction is called in the theorem prover, ; the live state is passed in, to be used to authorize the functions to ; execute. Thus, these uninterpreted functions must be provided a STATE ; argument even if they would not otherwise need it. Mfc-ts is an example of ; a function that has an otherwise unneeded STATE argument: type-set does not ; need state. ; How do we know that the context passed into the meta-level function will ; permit type-set and rewrite to execute without error? How do we know that ; such complicated components as the world, the type-alist, and ; simplify-clause-pot-lst are well-formed? One way would be to formalize ; guards on all the theorem prover's functions and require guard proofs on ; metafunctions. But the system is not ready for that yet. (We believe we ; know the guards for our functions, but we have never written them down ; formally.) ; To ensure that the metafunction context is well-formed (and also for the ; logical reason mentioned above, where we using implicit axioms on mfc-xx ; functions to justify meta-extract-contextual-fact hypotheses in meta rules), ; we refuse to execute unless the context is EQ to the one created by rewrite ; when it calls the meta-level function. Sensible errors are generated ; otherwise. When rewrite generates a context, it binds the Lisp special ; *metafunction-context* to the context, to permit this check. That special ; has value NIL outside meta-level functions. #-acl2-loop-only (defparameter *metafunction-context* nil) ; The ``term'' passed to the type-set and rewrite is checked explicitly to be ; well-formed with respect to the world passed in the context. This gives the ; meta-level function author the freedom to ask type-set questions about ; subterms of what the meta-level function was passed, or even questions about ; newly consed up terms. ; In this section we define the metafunction context accessors, i.e., :logic ; mode functions of the first type noted above. We are free to add more ; functions analogous to mfc-clause to recover components of the ; metafunction-context mfc. If you add more functions to the ; metafunction-context record, be sure to define them below, updating existing ; definitions as necessary due to layout changes for that record. ; First, we define some accessor functions that should really be defined by ; defrec, except that we don't want to go through the effort to move the ; definition of defrec to axioms.lisp. ; The present PROGN form is the result of executing the following forms in an ; ACL2 built without this form -- but be sure to replace the defrec form below ; with the corresponding defrec that appears later in the sources! (PROGN ; :set-raw-mode-on! ; (cons 'progn ; (er-let* ((form (trans1 '(defrec metafunction-context ...)))) ; (loop for x in (cdr (butlast form 2)) ; collect (er-let* ((y (trans1 x))) y)))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field RDEPTH| (RDEPTH) (LIST 'LET (LIST (LIST 'RDEPTH RDEPTH)) '(CAR RDEPTH))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field TYPE-ALIST| (TYPE-ALIST) (LIST 'LET (LIST (LIST 'TYPE-ALIST TYPE-ALIST)) '(CAR (CDR TYPE-ALIST)))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field OBJ| (OBJ) (LIST 'LET (LIST (LIST 'OBJ OBJ)) '(CAR (CDR (CDR OBJ))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field GENEQV| (GENEQV) (LIST 'LET (LIST (LIST 'GENEQV GENEQV)) '(CAR (CDR (CDR (CDR GENEQV)))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field WRLD| (WRLD) (LIST 'LET (LIST (LIST 'WRLD WRLD)) '(CAR (CDR (CDR (CDR (CDR WRLD))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field FNSTACK| (FNSTACK) (LIST 'LET (LIST (LIST 'FNSTACK FNSTACK)) '(CAR (CDR (CDR (CDR (CDR (CDR FNSTACK)))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field ANCESTORS| (ANCESTORS) (LIST 'LET (LIST (LIST 'ANCESTORS ANCESTORS)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR ANCESTORS))))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field BACKCHAIN-LIMIT| (BACKCHAIN-LIMIT) (LIST 'LET (LIST (LIST 'BACKCHAIN-LIMIT BACKCHAIN-LIMIT)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR (CDR BACKCHAIN-LIMIT)))))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field SIMPLIFY-CLAUSE-POT-LST| (SIMPLIFY-CLAUSE-POT-LST) (LIST 'LET (LIST (LIST 'SIMPLIFY-CLAUSE-POT-LST SIMPLIFY-CLAUSE-POT-LST)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR SIMPLIFY-CLAUSE-POT-LST))))))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field RCNST| (RCNST) (LIST 'LET (LIST (LIST 'RCNST RCNST)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR RCNST)))))))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field GSTACK| (GSTACK) (LIST 'LET (LIST (LIST 'GSTACK GSTACK)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR GSTACK))))))))))))) (DEFMACRO |Access METAFUNCTION-CONTEXT record field TTREE| (TTREE) (LIST 'LET (LIST (LIST 'TTREE TTREE)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR TTREE)))))))))))))) ; The present PROGN form is the result of executing the following forms in an ; ACL2 built without this form -- but be sure to replace the defrec form below ; with the corresponding defrec that appears later in the sources! (DEFMACRO |Access METAFUNCTION-CONTEXT record field UNIFY-SUBST| (UNIFY-SUBST) (LIST 'LET (LIST (LIST 'UNIFY-SUBST UNIFY-SUBST)) '(CAR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR (CDR UNIFY-SUBST)))))))))))))))) (DEFMACRO |Access REWRITE-CONSTANT record field CURRENT-CLAUSE| (CURRENT-CLAUSE) ; WARNING: This definition must be kept in sync with the defrec for ; rewrite-constant! ; This form comes from the definition of the :current-clause accessor of defrec ; rewrite-constant, by using trans1 to eliminate defabbrev in favor of defmacro. ; (access rewrite-constant ; (access metafunction-context mfc :rcnst) ; :current-clause) (LIST 'LET (LIST (LIST 'CURRENT-CLAUSE CURRENT-CLAUSE)) '(CDR (CAR (CDR (CDR (CDR (CDR CURRENT-CLAUSE)))))))) (defun record-error (name rec) (declare (xargs :guard t)) (er hard? 'record-error "An attempt was made to treat ~x0 as a record of type ~x1." rec name)) (defun record-accessor-function-name (name field) (declare (xargs :guard (and (symbolp name) (symbolp field)))) (intern-in-package-of-symbol (coerce (append (coerce "Access " 'list) (coerce (symbol-name name) 'list) (coerce " record field " 'list) (coerce (symbol-name field) 'list)) 'string) name)) (defmacro access (name rec field) (cond ((keywordp field) (list (record-accessor-function-name name field) rec)) (t (er hard 'record-error "Access was given a non-keyword as a field ~ specifier. The offending form was ~x0." (list 'access name rec field))))) (defun mfc-clause (mfc) (declare (xargs :guard t)) ; We protect the access below with a simple guard to make this function ; compliant. We return nil on the false branch, so in fact the acl2-loop-only ; body is equal to rhs. We then add a short-circuit in raw lisp that saves us ; from having to run the guard test in the vast majority of cases. It is ; assumed that *metafunction-context* is either NIL or a proper ; metafunction-context record. #-acl2-loop-only (cond ((eq mfc *metafunction-context*) (return-from mfc-clause (access rewrite-constant (access metafunction-context mfc :rcnst) :current-clause)))) ; Note: We check the pseudo-term-listp condition to ensure that ; pseudo-term-listp-mfc-clause (in axioms.lisp) is a theorem. (if (and (true-listp mfc) (true-listp (access metafunction-context mfc :rcnst)) ; The following case is unfortunate, but necessary for the guard proof. (consp (nth 4 (access metafunction-context mfc :rcnst))) (pseudo-term-listp (access rewrite-constant (access metafunction-context mfc :rcnst) :current-clause))) (access rewrite-constant (access metafunction-context mfc :rcnst) :current-clause) nil)) (defun mfc-rdepth (mfc) (declare (xargs :guard t)) #-acl2-loop-only (cond ((eq mfc *metafunction-context*) (return-from mfc-rdepth (access metafunction-context mfc :rdepth)))) (if (true-listp mfc) (access metafunction-context mfc :rdepth) nil)) (defun type-alist-entryp (x) ; (term ts . ttree) (declare (xargs :guard t)) (and (consp x) (pseudo-termp (car x)) (consp (cdr x)) (integerp (cadr x)) ; We check that (cadr x) is between *min-type-set* and *max-type-set*, which ; are checked by check-built-in-constants. (<= #-:non-standard-analysis -8192 #+:non-standard-analysis -65536 (cadr x)) (<= (cadr x) #-:non-standard-analysis 8191 #+:non-standard-analysis 65535))) (defun type-alistp (x) (declare (xargs :guard t)) (if (consp x) (and (type-alist-entryp (car x)) (type-alistp (cdr x))) (eq x nil))) (defun mfc-type-alist (mfc) (declare (xargs :guard t)) ; This function is analogous to mfc-clause, above. #-acl2-loop-only (cond ((eq mfc *metafunction-context*) (return-from mfc-type-alist (access metafunction-context mfc :type-alist)))) (if (and (true-listp mfc) (type-alistp (access metafunction-context mfc :type-alist))) (access metafunction-context mfc :type-alist) nil)) (defun mfc-ancestors (mfc) (declare (xargs :guard t)) ; This function is analogous to mfc-clause, above. #-acl2-loop-only (cond ((eq mfc *metafunction-context*) (return-from mfc-ancestors (access metafunction-context mfc :ancestors)))) (if (and (true-listp mfc) (true-listp (access metafunction-context mfc :ancestors))) (access metafunction-context mfc :ancestors) nil)) (defun mfc-unify-subst (mfc) (declare (xargs :guard t)) #-acl2-loop-only (cond ((eq mfc *metafunction-context*) (return-from mfc-unify-subst (access metafunction-context mfc :unify-subst)))) (if (true-listp mfc) (access metafunction-context mfc :unify-subst) nil)) (defun mfc-world (mfc) (declare (xargs :guard t)) #-acl2-loop-only (cond ((eq mfc *metafunction-context*) (return-from mfc-world (access metafunction-context mfc :wrld)))) (if (true-listp mfc) (access metafunction-context mfc :wrld) nil)) ; When verifying guards on meta-functions, the following two events are ; handy. (defthm pseudo-term-listp-mfc-clause (pseudo-term-listp (mfc-clause mfc))) (defthm type-alistp-mfc-type-alist (type-alistp (mfc-type-alist mfc))) ; If you add more of these mfc accessor functions, list them in the defrec ; for rewrite-constant. ; See ``Essay on Metafunction Support, Part 2'' for the definitions of the ; uninterpreted mfc functions. ; Essay on a Total Order of the ACL2 Universe ; Pete Manolios has suggested the inclusion a total order of the ACL2 universe. ; He has pointed out that such an order often makes reasoning simpler, in ; particular allowing for sorting of arbitrary lists, canonical forms for sets, ; and nice theorems about records (certain structures sorted by key) that do ; not have hypotheses about the keys. The lemma immediately preceding the ; theorem in Appendix B of the paper "Structured Theory Development for a ; Mechanized Logic" (Journal of Automated Reasoning, vol. 26, no. 2, (2001), ; 161-203) guarantees that it is conservative to add such an order, in fact an ; order isomorphic to ACL2's natural numbers. (That argument is flawed, but we ; fix it in documentation topic conservativity-of-defchoose. But see the ; relevant comment in the acl2-loop-only definition of defchoose for why an ; enumeration is problematic for ACL2(r).) ; Here we add the weakest axiom we can think of that gives a total order of the ; universe, by adding a predicate that orders the non-conses that are not of ; any of the types known to ACL2 (numbers, strings, characters, symbols). We ; then derive a total order from it, lexorder, which uses function alphorder to ; order atoms. These functions have been in ACL2 from perhaps the beginning, ; but starting with Version_2.6, they comprehend the notion of bad-atom -- ; atoms that satisfy bad-lisp-objectp -- in particular the primitive ordering ; bad-atom<=. The user is free to develop other total orders besides lexorder. ; We thank Pete Manolios for supplying a version of the events below and Rob ; Sumners for useful discussions and a modification of Pete's events. (defun bad-atom (x) ; Keep this in sync with good-atom-listp. (declare (xargs :guard t)) (not (or (consp x) (acl2-numberp x) (symbolp x) (characterp x) (stringp x)))) (defthm bad-atom-compound-recognizer (iff (bad-atom x) (not (or (consp x) (acl2-numberp x) (symbolp x) (characterp x) (stringp x)))) :rule-classes :compound-recognizer) (in-theory (disable bad-atom)) #-acl2-loop-only (defun-one-output bad-atom<= (x y) (error "We have called bad-atom<= on ~s and ~s, but bad-atom<= has no Common ~ Lisp definition." x y)) ; We now introduce the total ordering on bad-atoms. We keep most of the ; consequences local, because we are interested in exporting facts only about ; lexorder, which is a total order of the universe. (defaxiom booleanp-bad-atom<= (or (equal (bad-atom<= x y) t) (equal (bad-atom<= x y) nil)) :rule-classes :type-prescription) (defaxiom bad-atom<=-antisymmetric (implies (and (bad-atom x) (bad-atom y) (bad-atom<= x y) (bad-atom<= y x)) (equal x y)) :rule-classes nil) (defaxiom bad-atom<=-transitive (implies (and (bad-atom<= x y) (bad-atom<= y z) (bad-atom x) (bad-atom y) (bad-atom z)) (bad-atom<= x z)) :rule-classes ((:rewrite :match-free :all))) (defaxiom bad-atom<=-total (implies (and (bad-atom x) (bad-atom y)) (or (bad-atom<= x y) (bad-atom<= y x))) :rule-classes nil) ; Now we can introduce a total order on atoms followed by a total order on all ; ACL2 objects. (defun alphorder (x y) ":Doc-Section ACL2::ACL2-built-ins total order on atoms~/ ~c[Alphorder] is a non-strict total order, a ``less than or equal,'' on atoms. By ``non-strict total order'' we mean a function that always returns ~c[t] or ~c[nil] and satisfies the following properties.~bq[] o Antisymmetry: ~c[XrY & YrX -> X=Y] o Transitivity: ~c[XrY & YrZ -> XrZ] o Trichotomy: ~c[XrY v YrX] ~eq[]Also ~pl[lexorder], which extends ~c[alphorder] to all objects. ~c[(Alphorder x y)] has a guard of ~c[(and (atom x) (atom y))].~/ Within a single type: rationals are compared arithmetically, complex rationals are compared lexicographically, characters are compared via their char-codes, and strings and symbols are compared with alphabetic ordering. Across types, rationals come before complexes, complexes come before characters, characters before strings, and strings before symbols. We also allow for ``bad atoms,'' i.e., atoms that are not legal Lisp objects but make sense in the ACL2 logic; these come at the end, after symbols. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard (and (atom x) (atom y)))) (cond ((real/rationalp x) (cond ((real/rationalp y) (<= x y)) (t t))) ((real/rationalp y) nil) ((complex/complex-rationalp x) (cond ((complex/complex-rationalp y) (or (< (realpart x) (realpart y)) (and (= (realpart x) (realpart y)) (<= (imagpart x) (imagpart y))))) (t t))) ((complex/complex-rationalp y) nil) ((characterp x) (cond ((characterp y) (<= (char-code x) (char-code y))) (t t))) ((characterp y) nil) ((stringp x) (cond ((stringp y) (and (string<= x y) t)) (t t))) ((stringp y) nil) (t ; Since we only execute on good ACL2 objects, we know that x and y are ; symbols. However, the logic allows for other kinds of atoms as well, as ; recognized by the predicate bad-atom. The following shortcut avoids any ; potential overhead of accounting for bad atoms. #-acl2-loop-only ; We'd use (symbol-<= x y) if we had it. (not (symbol-< y x)) #+acl2-loop-only (cond ((symbolp x) (cond ((symbolp y) (not (symbol-< y x))) (t t))) ((symbolp y) nil) (t (bad-atom<= x y)))))) (defun lexorder (x y) ":Doc-Section ACL2::ACL2-built-ins total order on ACL2 objects~/ ~c[Lexorder] is a non-strict total order, a ``less than or equal,'' on ACL2 objects. Also ~pl[alphorder], the restriction of ~c[lexorder] to atoms; the notion of ``non-strict total order'' is defined there. ~c[Lexorder] has a guard of ~c[t].~/ For ~c[lexorder], an ~il[atom] and a ~il[cons] are ordered so that the ~il[atom] comes first, and two ~il[cons]es are ordered so that the one with the recursively smaller ~ilc[car] comes first, with the ~ilc[cdr]s being compared only if the ~ilc[car]s are equal. ~c[Lexorder] compares two atoms by using ~ilc[alphorder]. To see the ACL2 definition of this function, ~pl[pf].~/" (declare (xargs :guard t)) (cond ((atom x) (cond ((atom y) ; Historical Plaque: Here once was found the comment: ; From the VM one can conclude that ALPHORDER is a ; total ordering when restricted to ATOMs. ; attesting to the Interlisp ancestry of this theorem prover. (alphorder x y)) (t t))) ((atom y) nil) ((equal (car x) (car y)) (lexorder (cdr x) (cdr y))) (t (lexorder (car x) (car y))))) (local (defthm bad-atom<=-reflexive (implies (bad-atom x) (bad-atom<= x x)) :hints (("Goal" :by (:instance bad-atom<=-total (y x)))))) (local (defthm bad-atom<=-total-rewrite (implies (and (not (bad-atom<= y x)) (bad-atom x) (bad-atom y)) (bad-atom<= x y)) :hints (("Goal" :by (:instance bad-atom<=-total))) :rule-classes :forward-chaining)) (local (defthm equal-coerce (implies (and (stringp x) (stringp y)) (equal (equal (coerce x 'list) (coerce y 'list)) (equal x y))) :hints (("Goal" :use ((:instance coerce-inverse-2 (x x)) (:instance coerce-inverse-2 (x y))) :in-theory (disable coerce-inverse-2))))) (defthm alphorder-reflexive (implies (not (consp x)) (alphorder x x))) (local (defthm string<=-l-transitive-at-0 (implies (and (not (string<-l y x 0)) (not (string<-l z y 0)) (character-listp x) (character-listp y) (character-listp z)) (not (string<-l z x 0))) :rule-classes ((:rewrite :match-free :all)) :hints (("Goal" :use (:instance string<-l-transitive (i 0) (j 0) (k 0)))))) (defthm alphorder-transitive (implies (and (alphorder x y) (alphorder y z) (not (consp x)) (not (consp y)) (not (consp z))) (alphorder x z)) :rule-classes ((:rewrite :match-free :all)) :hints (("Goal" :in-theory (enable string< symbol-<)))) (defthm alphorder-anti-symmetric (implies (and (not (consp x)) (not (consp y)) (alphorder x y) (alphorder y x)) (equal x y)) :hints (("Goal" :in-theory (union-theories '(string< symbol-<) (disable code-char-char-code-is-identity)) :use ((:instance symbol-equality (s1 x) (s2 y)) (:instance bad-atom<=-antisymmetric) (:instance code-char-char-code-is-identity (c y)) (:instance code-char-char-code-is-identity (c x))))) :rule-classes ((:forward-chaining :corollary (implies (and (alphorder x y) (not (consp x)) (not (consp y))) (iff (alphorder y x) (equal x y))) :hints (("Goal" :in-theory (disable alphorder)))))) (defthm alphorder-total (implies (and (not (consp x)) (not (consp y))) (or (alphorder x y) (alphorder y x))) :hints (("Goal" :use (:instance bad-atom<=-total) :in-theory (enable string< symbol-<))) :rule-classes ((:forward-chaining :corollary (implies (and (not (alphorder x y)) (not (consp x)) (not (consp y))) (alphorder y x))))) (in-theory (disable alphorder)) (defthm lexorder-reflexive (lexorder x x)) (defthm lexorder-anti-symmetric (implies (and (lexorder x y) (lexorder y x)) (equal x y)) :rule-classes :forward-chaining) (defthm lexorder-transitive (implies (and (lexorder x y) (lexorder y z)) (lexorder x z)) :rule-classes ((:rewrite :match-free :all))) (defthm lexorder-total (or (lexorder x y) (lexorder y x)) :rule-classes ((:forward-chaining :corollary (implies (not (lexorder x y)) (lexorder y x))))) ; Although there is no known harm in leaving lexorder enabled, it seems likely ; that most reasoning about this function will only need the four properties ; proved above. (in-theory (disable lexorder)) ; We introduce merge-sort-lexorder, which is used in ; show-accumulated-persistence but may be generally useful. (defun merge-lexorder (l1 l2 acc) (declare (xargs :guard (and (true-listp l1) (true-listp l2) (true-listp acc)) :measure (+ (len l1) (len l2)))) (cond ((endp l1) (revappend acc l2)) ((endp l2) (revappend acc l1)) ((lexorder (car l1) (car l2)) (merge-lexorder (cdr l1) l2 (cons (car l1) acc))) (t (merge-lexorder l1 (cdr l2) (cons (car l2) acc))))) (local (defthm <=-len-evens (<= (len (evens l)) (len l)) :rule-classes :linear :hints (("Goal" :induct (evens l))))) (local (defthm <-len-evens (implies (consp (cdr l)) (< (len (evens l)) (len l))) :rule-classes :linear)) (defthm true-listp-merge-sort-lexorder (implies (and (true-listp l1) (true-listp l2)) (true-listp (merge-lexorder l1 l2 acc))) :rule-classes :type-prescription) (defun merge-sort-lexorder (l) (declare (xargs :guard (true-listp l) :measure (len l))) (cond ((endp (cdr l)) l) (t (merge-lexorder (merge-sort-lexorder (evens l)) (merge-sort-lexorder (odds l)) nil)))) ; We move if* to axioms.lisp, so that all :logic mode functions that come with ; the system will be defined in this file. We do not need this property for ; Version 2.5 or earlier, but we may need it later if we modify the way that ; we define *1* functions. ; Since if* is in :Doc-Section Bdd, we move the :doc for bdd here as well. (defdoc bdd ":Doc-Section Bdd ordered binary decision diagrams with rewriting~/ Ordered binary decision diagrams (OBDDs, often simply called BDDs) are a technique, originally published by Randy Bryant, for the efficient simplification of Boolean expressions. In ACL2 we combine this technique with rewriting to handle arbitrary ACL2 terms that can represent not only Boolean values, but non-Boolean values as well. In particular, we provide a setting for deciding equality of bit vectors (lists of Boolean values).~/ An introduction to BDDs for the automated reasoning community may be found in ``Introduction to the OBDD Algorithm for the ATP Community'' by J Moore, ~i[Journal of Automated Reasoning] (1994), pp. 33-45. (This paper also appears as Technical Report #84 from Computational Logic, Inc.) Further information about BDDs in ACL2 can be found in the subtopics of this ~il[documentation] section. In particular, ~pl[bdd-introduction] for a good starting place that provides a number of examples. ~l[hints] for a description of ~c[:bdd] hints. For quick reference, here is an example; but only the ~c[:vars] part of the hint is required, as explained in the documentation for ~il[hints]. The values shown are the defaults. ~bv[] (:vars nil :bdd-constructors (cons) :prove t :literal :all) ~ev[] We suggest that you next visit the documentation topic ~il[BDD-INTRODUCTION].") (defun if* (x y z) ":Doc-Section Bdd for conditional rewriting with BDDs~/ The function ~c[IF*] is defined to be ~ilc[IF], but it is used in a special way by ACL2's ~il[BDD] package.~/ As explained elsewhere (~pl[bdd-algorithm]), ACL2's ~il[BDD] algorithm gives special treatment to ~il[term]s of the form ~c[(IF* TEST TBR FBR)]. In such cases, the algorithm simplifies ~c[TEST] first, and the result of that simplification must be a constant (normally ~c[t] or ~c[nil], but any non-~c[nil] explicit value is treated like ~c[t] here). Otherwise, the algorithm aborts. Thus, ~c[IF*] may be used to implement a sort of conditional rewriting for ACL2's ~il[BDD] package, even though this package only nominally supports unconditional rewriting. The following contrived example should make this point clear. Suppose that we want to prove that ~c[(nthcdr (length x) (append x y))] is equal to ~c[y], but that we would be happy to prove this only for lists having length 4. We can state such a theorem as follows. ~bv[] (let ((x (list x0 x1 x2 x3))) (equal (nthcdr (length x) (append x y)) y)) ~ev[] If we want to prove this formula with a ~c[:]~ilc[BDD] hint, then we need to have appropriate rewrite rules around. First, note that ~c[LENGTH] is defined as follows (try ~c[:]~ilc[PE] ~ilc[LENGTH]): ~bv[] (length x) = (if (stringp x) (len (coerce x 'list)) (len x)) ~ev[] Since ~il[BDD]-based rewriting is merely very simple unconditional rewriting (~pl[bdd-algorithm]), we expect to have to prove a rule reducing ~ilc[STRINGP] of a ~ilc[CONS]: ~bv[] (defthm stringp-cons (equal (stringp (cons x y)) nil)) ~ev[] Now we need a rule to compute the ~c[LEN] of ~c[X], because the definition of ~c[LEN] is recursive and hence not used by the ~il[BDD] package. ~bv[] (defthm len-cons (equal (len (cons a x)) (1+ (len x)))) ~ev[] We imagine this rule simplifying ~c[(LEN (LIST X0 X1 X2 X3))] in terms of ~c[(LEN (LIST X1 X2 X3))], and so on, and then finally ~c[(LEN nil)] should be computed by execution (~pl[bdd-algorithm]). We also need to imagine simplifying ~c[(APPEND X Y)], where still ~c[X] is bound to ~c[(LIST X0 X1 X2 X3)]. The following two rules suffice for this purpose (but are needed, since ~ilc[APPEND], actually ~ilc[BINARY-APPEND], is recursive). ~bv[] (defthm append-cons (equal (append (cons a x) y) (cons a (append x y)))) (defthm append-nil (equal (append nil x) x)) ~ev[] Finally, we imagine needing to simplify calls of ~ilc[NTHCDR], where the first argument is a number (initially, the length of ~c[(LIST X0 X1 X2 X3)], which is 4). The second lemma below is the traditional way to accomplish that goal (when not using BDDs), by proving a conditional rewrite rule. (The first lemma is only proved in order to assist in the proof of the second lemma.) ~bv[] (defthm fold-constants-in-+ (implies (and (syntaxp (quotep x)) (syntaxp (quotep y))) (equal (+ x y z) (+ (+ x y) z)))) (defthm nthcdr-add1-conditional (implies (not (zp (1+ n))) (equal (nthcdr (1+ n) x) (nthcdr n (cdr x))))) ~ev[] The problem with this rule is that its hypothesis makes it a conditional ~il[rewrite] rule, and conditional rewrite rules are not used by the ~il[BDD] package. (~l[bdd-algorithm] for a discussion of ``BDD rules.'') (Note that the hypothesis cannot simply be removed; the resulting formula would be false for ~c[n = -1] and ~c[x = '(a)], for example.) We can solve this problem by using ~c[IF*], as follows; comments follow. ~bv[] (defthm nthcdr-add1 (equal (nthcdr (+ 1 n) x) (if* (zp (1+ n)) x (nthcdr n (cdr x))))) ~ev[] How is ~c[nthcdr-add1] applied by the ~il[BDD] package? Suppose that the ~il[BDD] computation encounters a ~il[term] of the form ~c[(NTHCDR (+ 1 N) X)]. Then the ~il[BDD] package will apply the ~il[rewrite] rule ~c[nthcdr-add1]. The first thing it will do when attempting to simplify the right hand side of that rule is to attempt to simplify the term ~c[(ZP (1+ N))]. If ~c[N] is an explicit number (which is the case in the scenario we envision), this test will reduce (assuming the executable counterparts of ~ilc[ZP] and ~ilc[BINARY-+] are ~il[enable]d) to ~c[t] or to ~c[nil]. In fact, the lemmas above (not including the lemma ~c[nthcdr-add1-conditional]) suffice to prove our goal: ~bv[] (thm (let ((x (list x0 x1 x2 x3))) (equal (nthcdr (length x) (append x y)) y)) :hints ((\"Goal\" :bdd (:vars nil)))) ~ev[] If we execute the following form that disables the definition and executable counterpart of the function ~ilc[ZP] ~bv[] (in-theory (disable zp (zp))) ~ev[] before attempting the proof of the theorem above, we can see more clearly the point of using ~c[IF*]. In this case, the prover makes the following report. ~bv[] ACL2 Error in ( THM ...): Unable to resolve test of IF* for term (IF* (ZP (+ 1 N)) X (NTHCDR N (CDR X))) under the bindings ((X (CONS X0 (CONS X1 (CONS X2 #)))) (N '3)) -- use SHOW-BDD to see a backtrace. ~ev[] If we follow the advice above, we can see rather clearly what happened. ~l[show-bdd]. ~bv[] ACL2 !>(show-bdd) BDD computation on Goal yielded 21 nodes. ============================== BDD computation was aborted on Goal, and hence there is no falsifying assignment that can be constructed. Here is a backtrace of calls, starting with the top-level call and ending with the one that led to the abort. See :DOC show-bdd. (LET ((X (LIST X0 X1 X2 X3))) (EQUAL (NTHCDR (LENGTH X) (APPEND X Y)) Y)) alist: ((Y Y) (X3 X3) (X2 X2) (X1 X1) (X0 X0)) (NTHCDR (LENGTH X) (APPEND X Y)) alist: ((X (LIST X0 X1 X2 X3)) (Y Y)) (IF* (ZP (+ 1 N)) X (NTHCDR N (CDR X))) alist: ((X (LIST* X0 X1 X2 X3 Y)) (N 3)) ACL2 !> ~ev[] Each of these term-alist pairs led to the next, and the test of the last one, namely ~c[(ZP (+ 1 N))] where ~c[N] is bound to ~c[3], was not simplified to ~c[t] or to ~c[nil]. What would have happened if we had used ~ilc[IF] in place of ~c[IF*] in the rule ~c[nthcdr-add1]? In that case, if ~c[ZP] and its executable counterpart were disabled then we would be put into an infinite loop! For, each time a term of the form ~c[(NTHCDR k V)] is encountered by the BDD package (where k is an explicit number), it will be rewritten in terms of ~c[(NTHCDR k-1 (CDR V))]. We would prefer that if for some reason the term ~c[(ZP (+ 1 N))] cannot be decided to be ~c[t] or to be ~c[nil], then the BDD computation should simply abort. Even if there were no infinite loop, this kind of use of ~c[IF*] is useful in order to provide feedback of the form shown above whenever the test of an ~c[IF] term fails to simplify to ~c[t] or to ~c[nil]." (declare (xargs :mode :logic :verify-guards t)) (if x y z)) (defun resize-list (lst n default-value) ; This function supports stobjs. The documentation is found later, since ; :Doc-Section stobj is not yet defined. (declare (xargs :guard t)) (if (and (integerp n) (> n 0)) (cons (if (atom lst) default-value (car lst)) (resize-list (if (atom lst) lst (cdr lst)) (1- n) default-value)) nil)) ; Define e/d, adapted with only minor changes from Bishop Brock's community ; book books/ihs/ihs-init.lisp. (deflabel theory-functions :doc ":Doc-Section Theories functions for obtaining or producing ~il[theories]~/ ~bv[] Example Calls of Theory Functions: (universal-theory :here) (union-theories th1 th2) (set-difference-theories th1 th2) ~ev[] The theory functions are documented individually:~/ The functions (actually, macros) mentioned above are convenient ways to produce ~il[theories]. (~l[theories].) Some, like ~ilc[universal-theory], take a logical name (~pl[logical-name]) as an argument and return the relevant theory as of the time that name was introduced. Others, like ~ilc[union-theories], take two ~il[theories] and produce a new one. ~l[redundant-events] for a caution about the use of logical names in theory expressions. Theory expressions are generally composed of applications of theory functions. Formally, theory expressions are expressions that involve, at most, the free variable ~ilc[world] and that when evaluated with ~ilc[world] bound to the current ACL2 world (~pl[world]) return ~il[theories]. The ``theory functions'' are actually macros that expand into forms that involve the free variable ~ilc[world]. Thus, for example ~c[(universal-theory :here)] actually expands to ~c[(universal-theory-fn :here world)] and when that form is evaluated with ~ilc[world] bound to the current ACL2 ~il[world], ~c[universal-theory-fn] scans the ACL2 property lists and computes the current universal theory. Because the theory functions all implicitly use ~ilc[world], the variable does not generally appear in anything the user types.~/") (defun e/d-fn (theory e/d-list enable-p) "Constructs the theory expression for the E/D macro." (declare (xargs :guard (and (true-list-listp e/d-list) (or (eq enable-p t) (eq enable-p nil))))) (cond ((atom e/d-list) theory) (enable-p (e/d-fn `(UNION-THEORIES ,theory ',(car e/d-list)) (cdr e/d-list) nil)) (t (e/d-fn `(SET-DIFFERENCE-THEORIES ,theory ',(car e/d-list)) (cdr e/d-list) t)))) (defmacro e/d (&rest theories) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories enable/disable rules~/ The macro ~c[e/d] creates theory expressions for use in ~ilc[in-theory] hints and events. It provides a convenient way to ~ilc[enable] and ~ilc[disable] simultaneously, without having to write arcane theory expressions. ~bv[] Examples: (e/d (lemma1 lemma2)) ; equivalent to (enable lemma1 lemma2) (e/d () (lemma)) ; equivalent to (disable lemma) (e/d (lemma1) (lemma2 lemma3)) ; Enable lemma1 then disable lemma2, lemma3. (e/d () (lemma1) (lemma2)) ; Disable lemma1 then enable lemma2.~/ General Form: (e/d enables-0 disables-0 ... enables-n disables-n) ~ev[] where each ~c[enables-i] and ~c[disables-i] is a list of runic designators; ~pl[theories], ~pl[enable], and ~pl[disable]. The ~c[e/d] macro takes any number of lists suitable for the ~ilc[enable] and ~ilc[disable] macros, and creates a theory that is equal to ~c[(current-theory :here)] after executing the following commands. (in-theory (enable . enables-0)) (in-theory (disable . disables-0)) [etc.] (in-theory (enable . enables-n)) (in-theory (disable . disables-n))~/ :cited-by theory-functions" (declare (xargs :guard (true-list-listp theories))) (cond ((atom theories) '(CURRENT-THEORY :HERE)) (t (e/d-fn '(CURRENT-THEORY :HERE) theories t)))) ; We avoid skipping proofs for the rest of initialization, so that we can do ; the verify-termination-boot-strap proofs below during the first pass. See ; the comment in the encapsulate that follows. Note that preceding in-theory ; events are skipped during pass 1 of the boot-strap, since we are only just ; now entering :logic mode and in-theory events are skipped in :program mode. #+acl2-loop-only (f-put-global 'ld-skip-proofsp nil state) ; (set-ld-skip-proofsp nil state) (encapsulate () (logic) ; We verify termination (and guards) for the following functions, in order that ; certain macroexpansions avoid stack overflows during boot-strapping or at ; least are sped up. (verify-termination-boot-strap alistp) (verify-termination-boot-strap symbol-alistp) (verify-termination-boot-strap true-listp) (verify-termination-boot-strap len) (verify-termination-boot-strap length) (verify-termination-boot-strap nth) (verify-termination-boot-strap char) (verify-termination-boot-strap eqlable-alistp) (verify-termination-boot-strap assoc-eql-exec) (verify-termination-boot-strap assoc-equal) (verify-termination-boot-strap sublis) (verify-termination-boot-strap nfix) (verify-termination-boot-strap ifix) (verify-termination-boot-strap integer-abs) ; for acl2-count (verify-termination-boot-strap acl2-count) ; for nonnegative-integer-quotient (verify-termination-boot-strap nonnegative-integer-quotient) (verify-termination-boot-strap floor) (verify-termination-boot-strap symbol-listp) ) (defun mod-expt (base exp mod) ":Doc-Section ACL2::ACL2-built-ins exponential function~/ ~c[(mod-expt r i m)] is the result of raising the number ~c[r] to the integer power ~c[i] and then taking the residue mod ~c[m]. That is, ~c[(mod-expt r i m)] is equal to ~c[(mod (expt r i) m)].~/ The ~il[guard] for ~c[(mod-expt r i m)] is that ~c[r] is a rational number and ~c[i] is an integer; if ~c[r] is ~c[0] then ~c[i] is nonnegative; and ~c[m] is a non-zero rational number. In some implementations (GCL Version 2.7.0 as of this writing), this function is highly optimized when ~c[r] and ~c[i] are natural numbers, not both zero, and ~c[m] is a positive integer. For other Lisp implementations, consider using function ~c[mod-expt-fast], defined in the community book ~c[arithmetic-3/floor-mod/mod-expt-fast.lisp], which should still provide significantly improved performance over this function. To see the ACL2 definition of this function, ~pl[pf].~/" ; This is just an optimized version of (mod (expt base exp) mod). (declare (xargs :guard (and (real/rationalp base) (integerp exp) (not (and (eql base 0) (< exp 0))) (real/rationalp mod) (not (eql mod 0))))) #+(and (not acl2-loop-only) gcl) (when (and (fboundp 'si::powm) (natp base) (natp exp) (not (and (eql base 0) (eql exp 0))) (posp mod)) ; The restrictions above can be weakened if justified by a clear spec for ; si::powm. Unfortunately, it's not evident whether any available version of ; GCL defines si::powm. (return-from mod-expt (si::powm base exp mod))) (mod (expt base exp) mod)) (defmacro fcons-term* (&rest x) ; ; Start experimental code mod, to check that calls of fcons-term are legitimate ; ; shortcuts in place of the corresponding known-correct calls of cons-term. ; #-acl2-loop-only ; `(let* ((fn-used-only-in-fcons-term* ,(car x)) ; (args-used-only-in-fcons-term* (list ,@(cdr x))) ; (result (cons fn-used-only-in-fcons-term* ; args-used-only-in-fcons-term*))) ; (assert$ (equal result (cons-term fn-used-only-in-fcons-term* ; args-used-only-in-fcons-term*)) ; result)) ; #+acl2-loop-only ; ; End experimental code mod. (cons 'list x)) (defun conjoin2 (t1 t2) ; This function returns a term representing the logical conjunction of ; t1 and t2. The term is IFF-equiv to (AND t1 t2). But, the term is ; not EQUAL to (AND t1 t2) because if t2 is *t* we return t1's value, ; while (AND t1 t2) would return *t* if t1's value were non-NIL. (declare (xargs :guard t)) (cond ((equal t1 *nil*) *nil*) ((equal t2 *nil*) *nil*) ((equal t1 *t*) t2) ((equal t2 *t*) t1) (t (fcons-term* 'if t1 t2 *nil*)))) (defun conjoin (l) (declare (xargs :guard (true-listp l))) (cond ((endp l) *t*) ((endp (cdr l)) (car l)) (t (conjoin2 (car l) (conjoin (cdr l)))))) (defun conjoin2-untranslated-terms (t1 t2) ; See conjoin2. This function has the analogous spec, but where t1 and t2 need ; not be translated. (declare (xargs :guard t)) (cond ((or (equal t1 *nil*) (eq t1 nil)) *nil*) ((or (equal t2 *nil*) (eq t2 nil)) *nil*) ((or (equal t1 *t*) (eq t1 t)) t2) ((or (equal t2 *t*) (eq t2 t)) t1) (t (fcons-term* 'if t1 t2 *nil*)))) (defun conjoin-untranslated-terms (l) ; This function is analogous to conjoin, but where t1 and t2 need not be ; translated. (declare (xargs :guard (true-listp l))) (cond ((endp l) *t*) ((endp (cdr l)) (car l)) (t (conjoin2-untranslated-terms (car l) (conjoin-untranslated-terms (cdr l)))))) (defun disjoin2 (t1 t2) ; We return a term IFF-equiv (but not EQUAL) to (OR t1 t2). For example, ; if t1 is 'A and t2 is 'T, then we return 'T but (OR t1 t2) is 'A. (declare (xargs :guard t)) (cond ((equal t1 *t*) *t*) ((equal t2 *t*) *t*) ((equal t1 *nil*) t2) ((equal t2 *nil*) t1) (t (fcons-term* 'if t1 *t* t2)))) (defun disjoin (lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) *nil*) ((endp (cdr lst)) (car lst)) (t (disjoin2 (car lst) (disjoin (cdr lst)))))) (defun disjoin-lst (clause-list) (declare (xargs :guard (true-list-listp clause-list))) (cond ((endp clause-list) nil) (t (cons (disjoin (car clause-list)) (disjoin-lst (cdr clause-list)))))) (defun conjoin-clauses (clause-list) (declare (xargs :guard (true-list-listp clause-list))) (conjoin (disjoin-lst clause-list))) (defconst *true-clause* (list *t*)) (defconst *false-clause* nil) (defun clauses-result (tuple) (declare (xargs :guard (true-listp tuple))) (cond ((car tuple) (list *false-clause*)) (t (cadr tuple)))) (defdoc sharp-dot-reader ":Doc-Section other read-time evaluation of constants~/ ~bv[] Example: ACL2 !>(defconst *a* '(a b c)) Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) *A* ACL2 !>(quote (1 2 #.*a* 3 4)) (1 2 (A B C) 3 4) ACL2 !> ~ev[]~/ The ACL2 reader supports the syntax ~c[#.*a*] where ~c[*a*] was defined by ~ilc[defconst]. In this case, the reader treats ~c[#.*a*] as though it were reading the value of ~c[*a*]. This feature can be useful in conjunction with the use of ~ilc[evisc-table] to abbreviate large constants, so that the abbreviation can be read back in; ~pl[evisc-table]. Remarks. (1) The ACL2 reader only supports `~c[#.]' as described above, unlike Common Lisp. Older versions (preceding 3.5) used `~c[#.]' to abort, but that functionality is now carried out by ~c[(a!)]; ~pl[a!]. For a related feature that only pops up one level, ~pl[p!]. (2) If you call ~ilc[certify-book] on a book that contains a form `~c[#.*foo*]', the ~c[*foo*] must already be defined in the ~il[world] in which you issue the ~c[certify-book] command. The reason is that ~c[certify-book] reads the entire book before evaluating its forms.") (defdoc sharp-comma-reader ":Doc-Section other DEPRECATED read-time evaluation of constants~/ The use of `~c[#,]' has been deprecated. Please use `~c[#.]' instead; ~pl[sharp-dot-reader].~/~/") (defdoc sharp-bang-reader ":Doc-Section other package prefix that is not restricted to symbols~/ ~bv[] Examples: ACL2 !>(defpkg \"FOO\" nil) Summary Form: ( DEFPKG \"FOO\" ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) \"FOO\" ACL2 !>'#!foo(a b) (FOO::A FOO::B) ACL2 !>'#!foo(a #!acl2 b) (FOO::A B) ACL2 !>'#!foo(#!acl2 a b) (A FOO::B) ACL2 !>'#!foo(#!\"ACL2\" a b) (A FOO::B) ACL2 !> ~ev[]~/ The ACL2 reader supports the syntax ~c[#!pkg-name expr] where ~c[pkg-name] is a string or symbol that names a package known to ACL2. As illustrated above, this syntax nests as one might expect. In the special case that ~c[expr] is a symbol, ~c[#!pkg-name expr] is equivalent to ~c[pkg-name::expr].") (defdoc sharp-u-reader ":Doc-Section other allow underscore characters in numbers~/ ~bv[] Example: ACL2 !>#ub1000_1000_1000_ 2184 ACL2 !>#b100010001000 2184 ACL2 !>#uo1_1 9 ACL2 !>#o11 9 ACL2 !>#u34_5 345 ACL2 !>#u345 345 ACL2 !>345 345 ACL2 !>#ux12_a 298 ACL2 !>#ux12a 298 ACL2 !>#u x12a 298 ACL2 !>#x12a 298 ACL2 !>#u123_456/7_919 123456/7919 ACL2 !> ~ev[]~/ The ACL2 reader supports the use of ~c[#ub], ~c[#uo], and ~c[#ux] where one would otherwise write ~c[#b], ~c[#o], and ~c[#x], respectively (for binary, octal, and hexadecimal numerals), but where underscore characters (`~c[_]') are allowed but ignored. Also supported is the prefix ~c[#u] in front of a an expression that is a decimal numeral except that underscore characteres are allowed but ignored. The precise specification of ~c[#u] is as follows. The Lisp reader reads one expression after the ~c[#u]. If the result is a number, then that number is returned by the reader. Otherwise the result must be a symbol whose name begins with one of the characters `~c[B]', `~c[O]', or `~c[X]', or else a decimal digit (one of the characters `~c[0], ~c[1], ..., ~c[9]'). All underscores are removed from the name of that symbol to obtain a string and in the first three cases only, a `~c[#]' character is prepended to that string. The resulting string is then handed to the Lisp reader in order to obtain the final result, which must be a number or else an error occurs.") (defdoc evisc-table ":Doc-Section events support for abbreviated output~/ The ~c[evisc-table] is an ACL2 table (~pl[table]) whose purpose is to modify the print representations of specified non-~c[nil] objects. When a key (some object) is associated with a string value, then that string is printed instead of that key (as an abbreviation). For example, the following log shows how to abbreviate the key ~c[(a b c)] with the token ~c[]. ~bv[] ACL2 !>(table evisc-table '(a b c) \"\") Summary Form: ( TABLE EVISC-TABLE ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) EVISC-TABLE ACL2 !>'(a b c) ACL2 !>'(4 5 a b c) (4 5 . ) ACL2 !> ~ev[]~/ Every value in this ~il[table] must be either a string or ~c[nil], where ~c[nil] eliminates any association of the key with an abbreviation. Continuing with the log above: ~bv[] ACL2 !>(table evisc-table '(a b c) nil) Summary Form: ( TABLE EVISC-TABLE ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) EVISC-TABLE ACL2 !>'(a b c) (A B C) ACL2 !>'(4 5 a b c) (4 5 A B C) ACL2 !> ~ev[] It can be particularly helpful to use this table to abbreviate a constant introduced by ~ilc[defconst] by prefixing the constant name with ~c[\"#,\"], as we now describe. Consider first the following example. ~bv[] (defconst *abc* '(1 2 3 4 5 6 7 8)) (table evisc-table *abc* (concatenate 'string \"#,\" (symbol-name '*abc*))) ~ev[] Then the constant ~c[*abc*] is printed as follows ~-[] very helpful if its associated structure is significantly larger than the 8-element list of numbers shown above! ~bv[] ACL2 !>*abc* #,*ABC* ACL2 !> ~ev[] What's more, the ACL2 reader will replace ~c[#,*C*], where ~c[*C*] is defined by ~ilc[defconst], by its value, regardless of ~c[evisc-table]; ~pl[sharp-dot-reader]. Continuing with the example above, we have: ~bv[] ACL2 !>(cdr (quote #,*ABC*)) (2 3 4 5 6 7 8) ACL2 !> ~ev[] Of course, more care needs to be taken if packages are involved (~pl[defpkg]), as we now illustrate. ~bv[] ACL2 !>(defpkg \"FOO\" nil) Summary Form: ( DEFPKG \"FOO\" ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) \"FOO\" ACL2 !>(defconst foo::*a* '(1 2 3)) Summary Form: ( DEFCONST FOO::*A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO::*A* ACL2 !>(table evisc-table foo::*a* \"#,foo::*a*\") Summary Form: ( TABLE EVISC-TABLE ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) EVISC-TABLE ACL2 !>foo::*a* #,foo::*a* ACL2 !>'#,foo::*a* #,foo::*a* ACL2 !>(cdr '#,foo::*a*) (2 3) ACL2 !> ~ev[] We conclude by an example showing some extra care that may be important to consider taking. We start with: ~bv[] (defconst |*BaR*| '(3 4)) ~ev[] Then the following works just fine; but try it without the extra code for the ~c[may-need-slashes] case and you'll see that the sharp-dot printing is missing. First: ~bv[] (table evisc-table |*BaR*| (let ((x (symbol-name '|*BaR*|))) (if (may-need-slashes x) (concatenate 'string \"#.|\" x \"|\") (concatenate 'string \"#.\" x)))) ~ev[] Then: ~bv[] ACL2 !>|*BaR*| #,|*BaR*| ACL2 !> ~ev[]") (table evisc-table nil nil :guard ; we don't want to abbreviate nil (and (not (null key)) (or (stringp val) (null val)))) ; Essay on the Design of Custom Keyword Hints ; A custom keyword hint is installed by adding a pair to the ; custom-keywords-table using one of the two forms: ; (add-custom-keyword-hint :keyi ugtermi) ; or ; (add-custom-keyword-hint :keyi ugtermi :checker uctermi) ; Restrictions are explained below, but both ugtermi and uctermi are ; untranslated terms and VAL is allowed as a free var in them. ; In the event that no :checker is supplied, uctermi defaults to (value t). ; Add-custom-keyword-hint translates the two terms, to get "generator" term ; gtermi and "checker" term ctermi, and then pairs :keyi with the doublet ; (ctermi gtermi) in the custom-keywords-table. The presence of such a pair ; makes :keyi a custom keyword hint. ; Every custom keyword hint, :keyi, thus has translated checker and generator ; terms and we call them ctermi and gtermi below. ; Here are the restrictions: ; (a) :keyi is not among the primitive hint keywords in *hint-keywords*. ; (b) ctermi is a term involving no free variables other than (VAL WORLD CTX ; STATE) whose output signature is (mv erp val STATE) to be interpreted as a ; standard ACL2 error triple. Note that ctermi can modify state arbitrarily. ; Its non-erroneous value is irrelevant. We are just giving it a chance to ; cause an error. ; (c) gtermi is a term involving no free variables other than (VAL ; KEYWORD-ALIST ID CLAUSE WORLD HIST PSPV CTX STATE), along with ; STABLE-UNDER-SIMPLIFICATIONP except in the context of :backtrack hints, in ; which case PROCESSOR and CLAUSE-LIST are the extra variables. Note that ; these variables, other than VAL, are those for the general case of a computed ; hint. The output signature of gtermi should be an error triple. Again, ; gtermi can modify state arbitrarily. The value component will be treated as ; a normal hint. ; How are custom keyword hints processed? ; Suppose :keyi is such a key, with checker term ctermi and generator term ; gtermi, and suppose the user writes a hint like: ; ("goal-spec" ... :keyi vali ...) ; At hint translate time, ctermi is evaluated, with VAL bound to vali. and ; WORLD, CTX, and STATE bound in the obvious way. The value is ignored! All ; that ctermi does is cause an error if it doesn't like val. The evaluation is ; conducted in a "protected" way that minimizes effects of ctermi on the state! ; Then, when the clause identified by goal-spec arises, the first custom ; keyword hint, :keyi, with (now translated) value vali and generator term ; gtermi is found. We first evaluate ctermi ``again'' on vali. Provided the ; non-erroneous exit is made, we then do a protected evaluation (as above) of ; gtermi with the following bindings: ; val: vali ; keyword-alist: (... :keyi vali ...) ; id: parsed form of goal-spec (see :DOC clause-identifier) ; clause, etc: as appropriate given clause and context ; The value of gtermi, say new-keyword-alist, is then used to replace the ; original hint ; ("goal-spec" ... :keyi vali ...) ; with: ; ("goal-spec" . new-keyword-alist) ; Note carefully: the value returned by a single custom keyword hint replaces ; the ENTIRE list of keywords and values in which it appears. Practically ; speaking, custom keywords should be sensitive to the input keyword-alist and ; return a modified version of it. This can easily be done by using the ; primitive function splice-keyword-alist or more sophisticated functions that ; attempt to merge new hints into old ones. ; Note that the new keyword-alist might contain more custom keyword hints and ; their checkers will not have been run. ; This process is repeated until there are no custom keywords in the list. ; This iteration is limited by a counter that is initially set to ; *custom-keyword-max-iterations*. ; When all custom keywords are eliminated, the hint is translated ; conventionally and applied to the subgoal. ; Thus, if the hint attached to a goal-spec contains any custom keyword, it ; cannot be fully translated until the goal arises. (Typically, custom hints, ; like computed hints, look at the clause itself.) For that reason, if a ; user-supplied hint, ; ("goal-spec" . keyword-alist) ; contains a custom keyword among the keys in the keyword-alist, we translate ; it to a full-fledged computed hint: ; (custom-keyword-hint-interpreter ; 'keyword-alist ; 'parsed-goal-spec ; ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX STATE) ; and further evaluation and translation happens when the subgoal arises. (We ; do eagerly evaluate custom keyword hints if their associated gtermi do not ; involve any of the dynamically determined variables, like CLAUSE.) ; Note that gtermi is free to add as many :NO-OP T entries as it wants to ; insure the result is non-empty, if that's a problem. (defconst *top-hint-keywords* ; WARNING: :use must precede :cases in this list, because ; of its use in the call of first-assoc-eq in apply-top-hints-clause. ; Specifically, if both :use and :cases are present in the hint-settings, then ; apply-top-hints-clause1 expects that call of first-assoc-eq to return the ; :use hint. See apply-top-hints-clause1. '(:use :cases :by :bdd :clause-processor :or)) (defconst *hint-keywords* ; This constant contains all the legal hint keywords as well as ; :computed-hints-replacement. (append *top-hint-keywords* '(:computed-hints-replacement :error :no-op :no-thanks :expand :case-split-limitations :restrict :do-not :do-not-induct :hands-off :in-theory :nonlinearp :backchain-limit-rw :reorder :backtrack :induct :rw-cache-state))) (table custom-keywords-table nil nil :guard ; Val must be of the form (uterm1 uterm2), where uterm1 and uterm2 are ; untranslated terms with certain syntactic properties, including being ; single-threaded in state and with output signatures (mv erp val state). But ; we cannot check that without access to state. So we actually don't check ; those key properties until we use them and we employ trans-eval at that ; point. ; #+ACL2-PAR note: it may be the case that, with waterfall parallelism enabled, ; both uterm1 and uterm2 must not return state. ; As a matter of interest, uterm1 is the untranslated generator term for the ; key and uterm2 is the untranslated checker term. (and (not (member-eq key *hint-keywords*)) (true-listp val) (equal (length val) 2))) #+acl2-loop-only (defmacro add-custom-keyword-hint (key uterm1 &key (checker '(value t))) ":Doc-Section Events add a new custom keyword hint~/ ~bv[] Examples: (add-custom-keyword-hint :my-hint (my-hint-fn val ...)) (add-custom-keyword-hint :my-hint (my-hint-fn val ...) :checker (my-hint-checker-fn val ...)) ~ev[] ~/ ~bv[] General Form: (add-custom-keyword-hint :key term1 :checker term2) ~ev[] where ~c[:key] is a ~ilc[keywordp] not among the primitive keyword hints listed in ~c[*hint-keywords*], the ~c[:checker] argument is optional, and ~c[term1] and (if supplied) ~c[term2] are terms with certain free-variable and signature restrictions described below. Henceforth, ~c[:key] is treated as a custom keyword hint, e.g., the user can employ ~c[:key] in hints to ~ilc[defthm], such as: ~bv[] (defthm name ... :hints ((\"Subgoal *1/1'\" ... :key val ...))). ~ev[] Custom keyword hints are complicated. To use them you must understand ~ilc[state], multiple values (e.g., ~ilc[mv] and ~ilc[mv-let]), ACL2's notion of error triples (~pl[programming-with-state]), how to generate ``soft'' errors with ~ilc[er], how to use ~ilc[fmt]-strings to control output, how to use computed hints (~pl[computed-hints]) and some aspects of ACL2's internal event processing. Furthermore, it is possible to implement a custom keyword hint that can make an event non-reproducible! So we recommend that these hints be developed by ACL2 experts. Basically the custom keyword feature allows the implementors and other experts to extend the hint facility without modifying the ACL2 sources. ~c[Term1] is called the ``generator'' term and ~c[term2] is called the ``checker'' term of the custom keyword hint ~c[:key]. Together they specify the semantics of the new custom keyword hint ~c[:key]. Roughly speaking, when a custom keyword hint is supplied by the user, as in ~bv[] (defthm name ... :hints ((\"Subgoal *1/1'\" ... :my-hint val ...))). ~ev[] the checker term is evaluated on ~c[val] to check that ~c[val] is of the expected shape. Provided ~c[val] passes the check, the generator term is used to compute a standard hint. Like computed hints, the generator of a custom keyword hint is allowed to inspect the actual clause on which it is being fired. Indeed, it is allowed to inspect the entire list of hints (standard and custom) supplied for that clause. Thus, in the most general case, a custom keyword hint is just a very special kind of computed hint. The generator, ~c[term1], must have no free variables other than: ~bv[] (val keyword-alist id clause world stable-under-simplificationp hist pspv ctx state). ~ev[] Moreover, either ~c[term1] must evaluate to a single non-~il[stobj] value, or else it must be single-threaded in ~c[state] and have the standard error-triple output signature, ~c[(mv * * state)]. The restrictions on the checker, ~c[term2], are that it be single-threaded in ~c[state], have the standard error-triple output signature, ~c[(mv * * state)], and have no free variables other than: ~bv[] (val world ctx state). ~ev[] For examples, see the community books directory ~c[books/hints/], in particular ~c[basic-tests.lisp]. To delete a previously added custom keyword hint, ~pl[remove-custom-keyword-hint]. The community book ~c[hints/merge-hint.lisp] can be useful in writing custom keyword hints. See the examples near the of the file. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/" `(add-custom-keyword-hint-fn ',key ',uterm1 ',checker state)) #-acl2-loop-only (defmacro add-custom-keyword-hint (&rest args) (declare (ignore args)) nil) (defmacro remove-custom-keyword-hint (keyword) ":Doc-Section Events remove a custom keyword hint~/ ~bv[] Example Forms: (remove-custom-keyword-hint :my-hint) ~/ General Form: (remove-custom-keyword-hint keyword) ~ev[] where ~c[keyword] is a ~ilc[keywordp]. For an explanation of how custom keyword hints are processed, ~pl[custom-keyword-hints]; also ~pl[add-custom-keyword-hint]. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical ~il[world] and is so recorded.~/" `(table custom-keywords-table nil (let ((tbl (table-alist 'custom-keywords-table world))) (if (assoc-eq ',keyword tbl) (delete-assoc-eq-exec ',keyword tbl) (prog2$ (cw "~%NOTE: the name ~x0 did not appear as a key in ~ custom-keywords-table. Consider using :u or :ubt to ~ undo this event, which is harmless but does not ~ change custom-keywords-table.~%" ',keyword) tbl))) :clear)) (defun splice-keyword-alist (key new-segment keyword-alist) (declare (xargs :guard (and (keywordp key) (keyword-value-listp keyword-alist) (true-listp new-segment)))) (cond ((endp keyword-alist) nil) ((eq key (car keyword-alist)) (append new-segment (cddr keyword-alist))) (t (cons (car keyword-alist) (cons (cadr keyword-alist) (splice-keyword-alist key new-segment (cddr keyword-alist))))))) (deflabel custom-keyword-hints :doc ":Doc-Section Miscellaneous user-defined hints~/ ~l[add-custom-keyword-hint] for a discussion of how advanced users can define their own hint keywords. For examples, see the community books directory ~c[books/hints/], in particular ~c[basic-tests.lisp].~/~/") (defmacro show-custom-keyword-hint-expansion (flg) ":Doc-Section custom-keyword-hints print out custom keyword hints when they are expanded~/ ~bv[] Examples: (show-custom-keyword-hint-expansion t) (show-custom-keyword-hint-expansion nil) ~ev[] ~/ ~bv[] General Form: (show-custom-keyword-hint-expansion flg) ~ev[] If the value of ~c[flg] is non-~c[nil], then when custom keyword hints are expanded, the system prints the results of each expansion. This is sometimes useful for debugging custom keyword hints and, from time to time, may be useful in understanding how a custom hint affects some proof attempt. The default setting is ~c[nil]. For an explanation of how custom keyword hints are processed, ~pl[custom-keyword-hints].~/" `(f-put-global 'show-custom-keyword-hint-expansion ,flg state)) ; Start implementation of search. (defun search-fn-guard (seq1 seq2 from-end test start1 start2 end1 end2 end1p end2p) (declare (xargs :guard t) (ignore from-end)) (and (cond ((not (member-eq test '(equal char-equal))) (er hard? 'search "For the macro ~x0, only the :test values ~x1 and ~x2 are ~ supported; ~x3 is not. If you need other tests supported, ~ please contact the ACL2 implementors." 'search 'equal 'char-equal test)) ((and (stringp seq1) (stringp seq2)) (or (eq test 'equal) (and (standard-char-listp (coerce seq1 'list)) (standard-char-listp (coerce seq2 'list))) (er hard? 'search "When ~x0 is called on two strings, they must both ~ consist of standard characters. However, this is not ~ the case for ~x1." 'search (if (standard-char-listp (coerce seq1 'list)) seq2 seq1)))) ((eq test 'char-equal) (er hard? 'search "For the macro ~x0, the :test ~x1 is only supported for ~ string arguments. If you need this test supported for ~ true lists, please contact the ACL2 implementors." 'search 'char-equal)) ((and (true-listp seq1) (true-listp seq2)) t) (t (er hard? 'search "The first two arguments of ~x0 must both evaluate to true ~ lists or must both evaluate to strings." 'search))) (let ((end1 (if end1p end1 (length seq1))) (end2 (if end2p end2 (length seq2)))) (and (natp start1) (natp start2) (natp end1) (natp end2) (<= start1 end1) (<= start2 end2) (<= end1 (length seq1)) (<= end2 (length seq2)))))) (defun search-from-start (seq1 seq2 start2 end2) (declare (xargs :measure (nfix (1+ (- end2 start2))) :guard (and (or (true-listp seq1) (stringp seq1)) (or (true-listp seq2) (stringp seq2)) (integerp start2) (<= 0 start2) (integerp end2) (<= end2 (length seq2)) (<= (+ start2 (length seq1)) end2)))) (let ((bound2 (+ start2 (length seq1)))) (cond ((or (not (integerp end2)) (not (integerp start2))) nil) ((equal seq1 (subseq seq2 start2 bound2)) start2) ((>= bound2 end2) nil) (t (search-from-start seq1 seq2 (1+ start2) end2))))) (defun search-from-end (seq1 seq2 start2 end2 acc) (declare (xargs :measure (nfix (1+ (- end2 start2))) :guard (and (or (true-listp seq1) (stringp seq1)) (or (true-listp seq2) (stringp seq2)) (integerp start2) (<= 0 start2) (integerp end2) (<= end2 (length seq2)) (<= (+ start2 (length seq1)) end2)))) (cond ((or (not (integerp end2)) (not (integerp start2))) nil) (t (let* ((bound2 (+ start2 (length seq1))) (matchp (equal seq1 (subseq seq2 start2 bound2))) (new-acc (if matchp start2 acc))) (cond ((>= bound2 end2) new-acc) (t (search-from-end seq1 seq2 (1+ start2) end2 new-acc))))))) ; The following lemmas are needed for guard verification of search-fn. (encapsulate () (local (defthm len-string-downcase1 (equal (len (string-downcase1 x)) (len x)))) (local (defthm stringp-subseq (implies (stringp str) (stringp (subseq str start end))))) (local (defthm standard-char-listp-nthcdr (implies (standard-char-listp x) (standard-char-listp (nthcdr n x))) :hints (("Goal" :in-theory (enable standard-char-listp))))) (local (defthm standard-char-listp-revappend (implies (and (standard-char-listp x) (standard-char-listp ac)) (standard-char-listp (revappend x ac))) :hints (("Goal" :in-theory (enable standard-char-listp))))) (local (defthm standard-char-listp-first-n-ac (implies (and (standard-char-listp x) (standard-char-listp ac) (<= n (len x))) (standard-char-listp (first-n-ac n x ac))) :hints (("Goal" :in-theory (enable standard-char-listp))))) (local (defthm character-listp-first-n-ac (implies (and (character-listp x) (character-listp ac) (<= n (len x))) (character-listp (first-n-ac n x ac))))) (local (defthm character-listp-nthcdr (implies (character-listp x) (character-listp (nthcdr n x))))) (local (defthm nthcdr-nil (equal (nthcdr n nil) nil))) (local (defthm len-nthcdr (equal (len (nthcdr n x)) (nfix (- (len x) (nfix n)))))) (local (defthm subseq-preserves-standard-char-listp (implies (and (stringp seq) (natp start) (natp end) (<= start end) (<= end (length seq)) (standard-char-listp (coerce seq 'list))) (standard-char-listp (coerce (subseq seq start end) 'list))))) (local (defthm true-listp-revappend (equal (true-listp (revappend x y)) (true-listp y)))) (local (defthm true-listp-first-n-ac (implies (and (true-listp acc) (true-listp lst)) (true-listp (first-n-ac n lst acc))))) (local (defthm true-listp-nthcdr (implies (true-listp x) (true-listp (nthcdr n x))))) (local (defthm true-listp-subseq (implies (true-listp seq) (true-listp (subseq seq start end))) :rule-classes (:rewrite :type-prescription))) (local (defthm len-revappend (equal (len (revappend x y)) (+ (len x) (len y))))) (local (defthm len-first-n-ac (implies (true-listp ac) (equal (len (first-n-ac n lst ac)) (+ (nfix n) (len ac)))))) (local (defthm len-subseq (implies (and (true-listp seq) (natp start) (natp end) (<= start end)) (equal (len (subseq seq start end)) (- end start))))) (local (defthm len-subseq-string (implies (and (stringp seq) (natp start) (natp end) (<= start end) (<= end (len (coerce seq 'list)))) (equal (len (coerce (subseq seq start end) 'list)) (- end start))) :hints (("Goal" :in-theory (enable subseq))))) (defun search-fn (seq1 seq2 from-end test start1 start2 end1 end2 end1p end2p) (declare (xargs :guard (search-fn-guard seq1 seq2 from-end test start1 start2 end1 end2 end1p end2p) :guard-hints (("Goal" :in-theory (disable subseq))))) #-acl2-loop-only ; only called when the guard is true (if (or end1p end2p) (search seq1 seq2 :from-end from-end :test test :start1 start1 :start2 start2 :end1 (if end1p end1 (length seq1)) :end2 (if end2p end2 (length seq2))) (search seq1 seq2 :from-end from-end :test test :start1 start1 :start2 start2)) #+acl2-loop-only (let* ((end1 (if end1p end1 (length seq1))) (end2 (if end2p end2 (length seq2))) (seq1 (subseq seq1 start1 end1))) (mv-let (seq1 seq2) (cond ((eq test 'char-equal) ; hence, both are strings, by the guard (mv (string-downcase seq1) (string-downcase seq2))) (t (mv seq1 seq2))) (and (<= (- end1 start1) (- end2 start2)) (cond (from-end (search-from-end seq1 seq2 start2 end2 nil)) (t (search-from-start seq1 seq2 start2 end2))))))) ) #+acl2-loop-only (defmacro search (seq1 seq2 &key from-end (test ''equal) (start1 '0) (start2 '0) (end1 'nil end1p) (end2 'nil end2p)) ":Doc-Section ACL2::ACL2-built-ins search for a string or list in another string or list~/ ~bv[] Example Forms: (search \"cd\" \"Cdabcdefcde\") ; = 4, index of first match (search \"cd\" \"Cdabcdefcde\" :test 'equal) ; same as above (search \"cd\" \"Cdabcdefcde\" :from-end t) ; = 8, index of last match (search \"cd\" \"Cdabcdefcde\" :start1 1) ; = 1 (search \"cd\" \"Cdabcdefcde\" :start2 5) ; = 8 (search \"cd\" \"Cdabcdefcde\" :test 'char-equal) ; = 0 (case-insensitive) (search \"ac\" \"Cdabcdefcde\") ; = nil (search '(a b) '(9 8 a b 7 6)) ; = 2~/ General Form: (search seq1 seq2 &key from-end test start1 start2 end1 end2) ~ev[] ~c[Search] indicates whether one string or list occurs as a (contiguous) subsequence of another string or list, respectively. It returns ~c[nil] if no such match is found; otherwise it returns the (zero-based) index of the first match by default, but a non-~c[nil] value of keyword argument ~c[:from-end] causes it to return the last match. The ~c[:test] is ~c[equal] by default. The other legal value for ~c[:test] is ~c[char-equal], which can be given only for two strings, in which case the match is case-insensitive. Finally, values of ~c[:start1] and ~c[:end1] for the first sequence, and of ~c[:start2] and ~c[:end2] for the second sequence, bound the portion of the respective sequence used for deciding on a match, though the index returned is always an index into the second sequence as a whole. The ~il[guard] for calls of ~c[search] is given by a function, ~c[search-fn-guard], which has the following requirements.~bq[] o The two arguments much both satisfy ~ilc[true-listp] or else must both be strings, which must consist of standard characters (~pl[standard-char-p]) if the ~c[:test] is ~ilc[char-equal]. o The ~c[:test] must evaluate to one of the symbols ~ilc[equal] or ~ilc[char-equal], where the latter is only allowed if the (first) two arguments are strings. o The values of ~c[:start1], ~c[:start2], ~c[:end1], and ~c[:end2] must all be natural numbers, where if omitted they default to 0, 0, the length ~c[len1] of the first argument, and the length ~c[len2] of the second argument, respectively. o If ~c[start1] is the value of ~c[:start1], defaulting as described just above, and similarly for the other start and end keywords and for lengths ~c[len1] and ~c[len2] as described just above, then: ~c[start1 <= end1 <= len1] and ~c[start2 <= end2 <= len2]. ~eq[]~c[Search] is a Common Lisp function (actually, a macro in ACL2). See any Common Lisp documentation for more information.~/" `(search-fn ,seq1 ,seq2 ,from-end ,test ,start1 ,start2 ,end1 ,end2 ,end1p ,end2p)) (defthm eqlablep-nth (implies (eqlable-listp x) (eqlablep (nth n x))) :hints (("Goal" :in-theory (enable nth)))) (defun count-stringp (item x start end) (declare (xargs :guard (and (stringp x) (natp start) (natp end) (<= end (length x))) :measure (nfix (- (1+ end) start)))) (cond ((or (not (integerp start)) (not (integerp end)) (<= end start)) 0) ((eql item (char x start)) (1+ (count-stringp item x (1+ start) end))) (t (count-stringp item x (1+ start) end)))) (defun count-listp (item x end) (declare (xargs :guard (and (true-listp x) (natp end)))) (cond ((or (endp x) (zp end)) 0) ((equal item (car x)) (1+ (count-listp item (cdr x) (1- end)))) (t (count-listp item (cdr x) (1- end))))) (encapsulate () (local (defthm true-listp-nthcdr (implies (true-listp x) (true-listp (nthcdr n x))))) (defun count-fn (item sequence start end) (declare (xargs :guard (and (if (true-listp sequence) t (stringp sequence)) (natp start) (or (null end) (and (natp end) (<= end (length sequence))))))) (let ((end (or end (length sequence)))) (cond ((<= end start) 0) ((stringp sequence) (count-stringp item sequence start end)) (t (count-listp item (nthcdr start sequence) (- end start))))))) #+acl2-loop-only (defmacro count (item sequence &key (start '0) end) ":Doc-Section ACL2::ACL2-built-ins count the number of occurrences of an item in a string or true-list~/ ~bv[] Example Forms: (count #\\D \"DabcDefcDe\") ; = 3 (count #\\D \"DabcDefcDe\" :start 1) ; = 2 (count #\\D \"DabcDefcDe\" :start 1 :end 5) ; = 1 (count #\\D \"DabcDefcDe\" :start 1 :end 4) ; = 0 (count #\\z \"DabcDefcDe\") ; = 0 (count '(a b) '(17 (a b) 23 (a b) (c d))) ; = 2 General Form: (count item sequence &key start end) ~ev[] ~c[(Count item sequence)] returns the number of times ~c[item] occurs in ~c[sequence]. The ~il[guard] for calls of ~c[count] (which is actually a macro in ACL2) specifies that ~c[sequence] is a string or a true-list, and that ~c[start], which defaults to 0, and ~c[end], which defaults to the length of ~c[sequence], are valid indices into sequence. See any Common Lisp documentation for more information about ~c[count], which is a Common Lisp utility. At this time ACL2 does not support keyword arguments for ~c[count] other than ~c[:start] and ~c[:end]; we may add support for the ~c[:from-end] keyword upon request.~/~/" `(count-fn ,item ,sequence ,start ,end)) ; Skipped on first (:program mode) pass: (verify-termination-boot-strap cpu-core-count) ; We need for sharp-atsign-alist to be compiled before it is called in ; *sharp-atsign-ar*, file basis.lisp. So we put its definition here, along ; with its callee make-sharp-atsign. A nice exercise is to put these functions ; in :logic mode. (defun make-sharp-atsign (i) (declare (xargs :guard (natp i) :mode :program)) (concatenate 'string "#@" (coerce (explode-nonnegative-integer i 10 nil) 'string) "#")) (defun sharp-atsign-alist (i acc) (declare (xargs :guard (natp i) :mode :program)) (cond ((zp i) acc) (t (sharp-atsign-alist (1- i) (acons i (make-sharp-atsign i) acc))))) ; Essay on the Implementation of Time$ ; It is tempting to define (time$ x ...) to be a macro expanding to x in the ; logic. But then translate will eliminate time$; yet some version of time$ ; needs to be a function, so that it is still around for ev-rec to see. If it ; weren't for ev-rec, time$ could be a macro as long as it were left alone by ; oneify, i.e., on the list *macros-for-nonexpansion-in-raw-lisp*. ; So, we need some way to represent time$ as a function in the logic. On the ; other hand, we cannot define time$ as a function in raw Lisp, because then ; its arguments will be evaluated before there is any opportunity to set things ; up to get timing information. ; Consider also the issue of keyword arguments. We want time$ to take keyword ; arguments, but on the other hand, we do not allow functions with keyword ; arguments. So again we see that time$ needs to be a macro. ; Thus, we define time$ to be a macro that expands to a corresponding call of ; time$1, which in turn expands to a call (return-last 'time$1-raw & &). ; Return-last is a function in the logic but is a macro in raw Lisp. Since ; return-last is a function in the logic, it does not take keyword arguments; ; for convenience we define a macro our-time to be the keyword version of the ; raw Lisp macro time$1-raw. ; The following examples make a nice little test suite. Run each form and ; observe whether the output is consistent with the comments attached to the ; form. ; (defun f (n) ; (declare (xargs :guard (natp n) :verify-guards nil)) ; (make-list n)) ; (time$ (length (f 100))) ; times an ev-rec call ; (time$ (length (f 100)) :mintime 0) ; same as above ; (time$ (length (f 100)) :mintime nil) ; native time output ; (defun g (x native-p) ; (declare (xargs :guard (natp x) :verify-guards nil)) ; (if native-p ; (len (time$ (f x) :mintime nil)) ; (len (time$ (f x))))) ; (g 100 nil) ; time a *1*f call ; (g 100 t) ; time a *1*f call ; (verify-guards f) ; (g 100 nil) ; still times a *1*f call, since g's guards aren't verified ; (g 100 t) ; still times a *1*f call, since g's guards aren't verified ; (verify-guards g) ; (g 100 nil) ; times a call of f ; (g 100 t) ; times a call of f ; ; Check unnormalized and normalized bodies: ; (assert-event (equal (body 'g nil (w state)) ; '(IF NATIVE-P ; (LEN (RETURN-LAST ; 'TIME$1-RAW ; (CONS 'NIL ; (CONS 'NIL ; (CONS 'NIL ; (CONS 'NIL ; (CONS 'NIL 'NIL))))) ; (F X))) ; (LEN (RETURN-LAST ; 'TIME$1-RAW ; (CONS '0 ; (CONS 'NIL ; (CONS 'NIL ; (CONS 'NIL ; (CONS 'NIL 'NIL))))) ; (F X)))))) ; (assert-event (equal (body 'g t (w state)) ; '(LEN (F X)))) ; (time$ 3 :mintime nil) ; prints verbose, native timing message ; (time$ 3 :minalloc 0) ; prints usual timing message ; (time$ 3 :mintime 0 :real-mintime 0) ; error ; (time$ 3 :mintime 0 :run-mintime 0) ; prints usual timing message ; (time$ 3 :real-mintime 1) ; no timing output ; (time$ 3 :run-mintime 1) ; no timing output ; (time$ 3 :minalloc 10000) ; no timing output if :minalloc is supported ; (time$ (length (f 100)) ; prints "Howdy" ; :msg "Howdy~%") ; (let ((bar (+ 3 4))) ; (time$ (length (f 100000)) ; prints indicated timing message ; :msg "The execution of ~xf took ~st seconds (in real time; ~sc sec. ~ ; run time), and allocated ~sa bytes. In an unrelated note, bar ~ ; currently has the value ~x0.~%" ; :args (list bar))) ; (defun h (x real-min run-min alloc msg args) ; (declare (xargs :guard (natp x))) ; (len (time$ (f x) ; :mintime real-min ; :run-mintime run-min ; :minalloc alloc ; :msg msg ; :args args))) ; (h 1000000 nil nil nil nil nil) ; native time msg ; (h 1000000 0 nil nil nil nil) ; usual time msg ; (h 1000000 nil nil nil ; custom time msg, as indicated ; "The execution of ~xf took ~st seconds (in real time; ~sc sec. run time), ~ ; and allocated ~sa bytes. In an unrelated note, bar currently has the ~ ; value ~x0.~%" ; (list (+ 4 5))) ; End of Essay on the Implementation of Time$ #-acl2-loop-only (defmacro time$1-raw (val x) (let ((val-var (gensym)) (real-mintime-var (gensym)) (run-mintime-var (gensym)) (minalloc-var (gensym)) (msg-var (gensym)) (args-var (gensym))) `(let* ((,val-var ,val) (,real-mintime-var (pop ,val-var)) (,run-mintime-var (pop ,val-var)) (,minalloc-var (pop ,val-var)) (,msg-var (pop ,val-var)) (,args-var (pop ,val-var))) (our-time ,x :real-mintime ,real-mintime-var :run-mintime ,run-mintime-var :minalloc ,minalloc-var :msg ,msg-var :args ,args-var)))) (defmacro time$1 (val form) `(return-last 'time$1-raw ,val ,form)) (defmacro time$ (x &key (mintime '0 mintime-p) (real-mintime 'nil real-mintime-p) run-mintime minalloc msg args) ":Doc-Section ACL2::ACL2-built-ins time an evaluation~/ Semantically, ~c[(time$ x ...)] equals ~c[x]. However, its evaluation may write timing output to the trace output (which is usually the terminal), as explained further below. ~bv[] Examples: ; Basic examples: (time$ (foo 3 4)) (time$ (mini-proveall)) (defun bar (x) (time$ (f x))) ; Custom examples, which use a custom timing message rather than a built-in ; message from Lisp: ; Report only if real time is at least 1/2 second (two equivalent forms). (time$ (foo) :mintime 1/2) (time$ (foo) :real-mintime 1/2) ; Report only if allocation is at least 1000 bytes (and if the Lisp supports ; :minalloc). (time$ (foo) :minalloc 1000) ; Report only if real time is at least 1/2 second and (if the Lisp supports ; :minalloc) allocation is at least 931 bytes. (time$ (foo) :real-mintime 1/2 :minalloc 931) ; Print \"Hello Moon, Goodbye World\" instead of any timing data. (time$ (foo) :msg \"Hello ~~s0, ~~s1 World.\" :args (list \"Moon\" \"Goodbye\")) ; Print default custom timing message (same as omitting :mintime 0): (time$ (foo) :mintime 0) ; Print supplied custom timing message. (let ((bar ...)) (time$ (foo) :msg \"The execution of ~~xf took ~~st seconds of real ~~ time and ~~sc seconds of run time (cpu time), and ~~ allocated ~~sa bytes. In an unrelated note, bar ~~ currently has the value ~~x0.~~%\" :args (list bar)))~/ General Forms: (time$ form) (time$ form ; arguments below are optional :real-mintime :run-mintime :minalloc :msg :args ) ; Note: :real-mintime can be replaced by :mintime ~ev[] where ~c[form] is processed as usual except that the host Common Lisp times its evaluation. The simplest form is ~c[(time$ x)], which will call the ~c[time] utility in the underlying Lisp, and will print a small default message. If you want to see a message printed by the host Lisp, use ~c[(time$ x :mintime nil)] instead, which may provide detailed, implementation-specific data such as the amounts of time spent in user and system mode, the gc time, the number of page faults encountered, and so on. Of you can create a custom message, configured using the ~c[:msg] and ~c[:args] parameters. ~c[Time$] can also be made to report timing information only conditionally: the ~c[:real-mintime] (or equivalently, ~c[:mintime]), ~c[:run-mintime], and ~c[:minalloc] arguments can be used to avoid reporting timing information for computations that take a small amount of time (perhaps as might be expected in ordinary cases), but to draw the user's attention to computations that take longer or allocate more memory than expected. We next document the keyword arguments in some detail. ~bq[] Keyword arguments ~c[:real-mintime] (or ~c[:mintime]) and ~c[:run-mintime] can be used to specify a minimum time threshold for time reporting. That is, no timing information will be printed if the execution of ~c[form] takes less than the specified number of seconds of real (total) time or run (cpu) time, respectively. Note that rational numbers like 1/2 may be used to specify a fractional amount of seconds. It is an error to specify both ~c[:real-mintime] and its synonym, ~c[:mintime]. Keyword argument ~c[:minalloc] is not supported on all Lisps. When it is not supported, it is ignored. But on supported Lisps, ~c[:minalloc] can be used to specify a minimum memory allocation threshold. If ~c[form] results in fewer than this many bytes being allocated, then no timing information will be reported. Keyword argument ~c[:msg], when provided, should be a string accepted by the ~c[fmt] family of functions (~pl[fmt]), and it may refer to the elements of ~c[:args] by their positions, just as for ~c[cw] (~pl[cw]).~eq[] The following directives allow you to report timing information using the ~c[:msg] string. The examples at the top of this documentation topic illustrate the use of these directives. ~bq[] ~c[~~xf] ~-[] the form that was executed ~c[~~sa] ~-[] the amount of memory allocated, in bytes (in supported Lisps) ~c[~~st] ~-[] the real time taken, in seconds ~c[~~sc] ~-[] the run time (cpu time) taken, in seconds ~eq[] We turn now to an example that illustrates how ~c[time$] can be called in function bodies. Consider the following definition of the Fibonacci function, followed by the definition of a function that times ~c[k] calls of this function. ~bv[] (defun fib (n) (if (zp n) 1 (if (= n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (defun time-fib (k) (if (zp k) nil (prog2$ (time$ (fib k) :mintime 1/2 :msg \"(fib ~~x0) took ~~st seconds, ~~sa bytes allocated.~~%\" :args (list k)) (time-fib (1- k))))) ~ev[] The following log shows a sample execution of the function defined just above. ~bv[] ACL2 !>(time-fib 36) (fib 36) took 3.19 seconds, 1280 bytes allocated. (fib 35) took 1.97 seconds, 1280 bytes allocated. (fib 34) took 1.21 seconds, 1280 bytes allocated. (fib 33) took 0.75 seconds, 1280 bytes allocated. NIL ACL2 !> ~ev[] Notes: (1) Common Lisp specifies that the ~c[time] utility prints to ``trace output'', and ~c[time$] follows this convention. Thus, if you have opened a ~il[trace] file (~pl[open-trace-file]), then you can expect to find the ~c[time$] output there. (2) Unless the ~c[:msg] argument is supplied, an explicit call of ~c[time$] in the top-level loop will show that the form being timed is a call of the ACL2 evaluator function ~c[ev-rec]. This is normal; the curious are invited, at their own risk, to ~pl[return-last] for an explanation.~/ :cited-by ACL2::Programming :cited-by other" (declare (xargs :guard t)) (cond ((and real-mintime-p mintime-p) (er hard 'time$ "It is illegal for a ~x0 form to specify both :real-mintime and ~ :mintime." 'time$)) (t (let ((real-mintime (or real-mintime mintime))) `(time$1 (list ,real-mintime ,run-mintime ,minalloc ,msg ,args) ,x))))) #-acl2-loop-only (progn (defmacro our-multiple-value-prog1 (form &rest other-forms) ; WARNING: If other-forms causes any calls to mv, then use protect-mv so that ; when #-acl2-mv-as-values, the multiple values returned by evaluation of form ; are those returned by the call of our-multiple-value-prog1. `(#+acl2-mv-as-values multiple-value-prog1 #-acl2-mv-as-values prog1 ,form ,@other-forms)) (eval `(mv ,@(make-list *number-of-return-values* :initial-element 0))) #-acl2-mv-as-values (defconst *mv-vars* (let ((ans nil)) (dotimes (i (1- *number-of-return-values*)) (push (gensym) ans)) ans)) #-acl2-mv-as-values (defconst *mv-var-values* (mv-refs-fn (1- *number-of-return-values*))) #-acl2-mv-as-values (defconst *mv-extra-var* (gensym)) (defun protect-mv (form &optional multiplicity) ; We assume here that form is evaluated only for side effect and that we don't ; care what is returned by protect-mv. All we care about is that form is ; evaluated and that all values stored by mv will be restored after the ; evaluation of form. #+acl2-mv-as-values (declare (ignore multiplicity)) #-acl2-mv-as-values (when (and multiplicity (not (and (integerp multiplicity) (< 0 multiplicity)))) (error "PROTECT-MV must be called with an explicit multiplicity, when ~ supplied, unlike ~s" multiplicity)) `(progn #+acl2-mv-as-values ,form #-acl2-mv-as-values ,(cond ((eql multiplicity 1) form) ((eql multiplicity 2) `(let ((,(car *mv-vars*) ,(car *mv-var-values*))) ,form (mv 0 ,(car *mv-vars*)))) (t (mv-let (mv-vars mv-var-values) (cond (multiplicity (mv (nreverse (let ((ans nil) (tail *mv-vars*)) (dotimes (i (1- multiplicity)) (push (car tail) ans) (setq tail (cdr tail))) ans)) (mv-refs-fn (1- multiplicity)))) (t (mv *mv-vars* *mv-var-values*))) `(mv-let ,(cons *mv-extra-var* mv-vars) (mv 0 ,@mv-var-values) (declare (ignore ,*mv-extra-var*)) (progn ,form (mv 0 ,@mv-vars)))))) nil)) ) #-acl2-loop-only (defmacro our-time (x &key real-mintime run-mintime minalloc msg args) (let ((g-real-mintime (gensym)) (g-run-mintime (gensym)) (g-minalloc (gensym)) (g-msg (gensym)) (g-args (gensym)) (g-start-real-time (gensym)) (g-start-run-time (gensym)) #+ccl (g-start-alloc (gensym))) `(let ((,g-real-mintime ,real-mintime) (,g-run-mintime ,run-mintime) (,g-minalloc ,minalloc) (,g-msg ,msg) (,g-args ,args)) (cond ((not (or ,g-real-mintime ,g-run-mintime ,g-minalloc ,g-msg ,g-args)) #+(or allegro clisp) ; For Allegro and CLISP, the time utilities are such that it can be useful to ; print a newline before printing a top-level result. Note that we can use ; prog1 for these Lisps today (Sept. 2009), but we consider the possibility of ; #+acl2-mv-as-values for these lisps in the future. (our-multiple-value-prog1 (time ,x) (when (eq *trace-output* *terminal-io*) (newline *standard-co* *the-live-state*))) #-(or allegro clisp) (time ,x)) ((and ,g-real-mintime (not (rationalp ,g-real-mintime))) (interface-er "Illegal call of ~x0: :real-mintime must be nil or a rational, but ~ ~x1 is neither." 'time$ ,g-real-mintime)) ((and ,g-run-mintime (not (rationalp ,g-run-mintime))) (interface-er "Illegal call of ~x0: :run-mintime must be nil or a rational, but ~ ~x1 is neither." 'time$ ,g-run-mintime)) ((and ,g-minalloc (not (rationalp ,g-minalloc))) (interface-er "Illegal call of ~x0: :alloc must be nil or a rational, but ~x1 is ~ neither." 'time$ ,g-minalloc)) ((and ,g-msg (not (stringp ,g-msg))) (interface-er "Illegal call of ~x0: :msg must be nil or a string, but ~x1 is ~ neither." 'time$ ,g-msg)) ((not (true-listp ,g-args)) (interface-er "Illegal call of ~x0: :args must be a true list, but ~x1 is not." 'time$ ,g-args)) (t (let* ((,g-start-real-time (get-internal-real-time)) (,g-start-run-time (get-internal-run-time)) #+ccl (,g-start-alloc (CCL::total-bytes-allocated))) (our-multiple-value-prog1 ,x ,(protect-mv `(let* ((end-run-time (get-internal-run-time)) (end-real-time (get-internal-real-time)) (real-elapsed (/ (- end-real-time ,g-start-real-time) internal-time-units-per-second)) (run-elapsed (/ (- end-run-time ,g-start-run-time) internal-time-units-per-second)) (real-elapsed-str (format nil "~,2F" real-elapsed)) (run-elapsed-str (format nil "~,2F" run-elapsed)) #+ccl (allocated (- (ccl::total-bytes-allocated) ,g-start-alloc))) (when (not (or (and ,g-real-mintime (< real-elapsed ,g-real-mintime)) (and ,g-run-mintime (< run-elapsed ,g-run-mintime)) #+ccl (and ,g-minalloc (< allocated ,g-minalloc)))) (let* ((alist (list* (cons #\t real-elapsed-str) (cons #\c run-elapsed-str) (cons #\a #+ccl (format nil "~:D" allocated) #-ccl "[unknown]") (cons #\f ',x) (cons #\e (evisc-tuple 3 2 (world-evisceration-alist *the-live-state* nil) nil)) (and ,g-msg (pairlis$ '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) ,g-args)))) (,g-msg (or ,g-msg #+ccl "; ~Xfe took ~|; ~st seconds realtime, ~ ~sc seconds runtime~|; (~sa bytes ~ allocated).~%" #-ccl "; ~Xfe took~|; ~st seconds realtime, ~ ~sc seconds runtime.~%"))) (fmt-to-comment-window ,g-msg alist 0 (abbrev-evisc-tuple *the-live-state*))))))))))))) (encapsulate () (local (defthm true-listp-revappend (equal (true-listp (revappend x y)) (true-listp y)))) (local (defthm true-listp-first-n-ac (implies (and (true-listp acc) (true-listp lst)) (true-listp (first-n-ac n lst acc))))) (verify-guards throw-nonexec-error) (verify-guards defun-nx-fn) (verify-guards update-mutual-recursion-for-defun-nx-1) (verify-guards update-mutual-recursion-for-defun-nx) ) ; For some reason, MCL didn't like it when there was a single definition of ; gc$-fn with acl2-loop-only directives in the body. So we define the two ; versions separately. #-acl2-loop-only (defun-one-output gc$-fn (args) ; Warning: Keep this in sync with :doc gc$. ; We will add some checks on the arguments as a courtesy, but really, it is up ; to the user to pass in the right arguments. #+allegro (apply `excl:gc args) #+ccl (apply 'ccl::gc args) ; no args as per Gary Byers 12/08 #+clisp (apply 'ext:gc args) #+cmu (apply 'system::gc args) #+gcl (if (eql (length args) 1) (apply 'si::gbc args) (er hard 'gc$ "In GCL, gc$ requires exactly one argument, typically T.")) #+lispworks (apply 'hcl::gc-generation (or args (list #+lispworks-64bit 7 #-lispworks-64bit 3))) #+sbcl (apply 'sb-ext:gc args) #-(or allegro gcl clisp cmu sbcl ccl lispworks) (illegal 'gc$ "GC$ is not supported in this Common Lisp." nil) nil) #+acl2-loop-only (defun gc$-fn (args) (declare (ignore args) (xargs :guard t)) nil) (defmacro gc$ (&rest args) ":Doc-Section Miscellaneous invoke the garbage collector~/ This function is provided so that the user can call the garbage collector of the host Lisp from inside the ACL2 loop. Specifically, a call of ~c[gc$] is translated into a call of a function below on the same arguments. ~bv[] Allegro CL: excl:gc CCL ccl::gc CLISP ext:gc CMU Common Lisp system::gc GCL si::gbc LispWorks hcl::gc-generation [default argument list: (7) for 64-bit OS, else (3)] SBCL sb-ext:gc ~ev[] The arguments, if any, are as documented in the underlying Common Lisp. It is up to the user to pass in the right arguments, although we may do some rudimentary checks. Also ~pl[gc-verbose]. Evaluation of a call of this macro always returns ~c[nil].~/~/" `(gc$-fn ',args)) #-acl2-loop-only (defun-one-output gc-verbose-fn (arg) ; For a related function, see gc$-fn. (let ((arg (and arg t))) ; coerce to Boolean (declare (ignorable arg)) #+ccl (ccl::gc-verbose arg arg) #+cmu (setq ext:*gc-verbose* arg) #+gcl (si:*notify-gbc* arg) #-(or ccl cmu gcl) (format t "GC-VERBOSE is not supported in this Common Lisp.~%Contact the ~ ACL2 developers if you would like to help add such support.") nil)) #+acl2-loop-only (defun gc-verbose-fn (arg) (declare (ignore arg) (xargs :guard t)) nil) (defmacro gc-verbose (arg) ":Doc-Section Miscellaneous control printing from the garbage collector~/ ~bv[] General Form: (gc-verbose arg) ~ev[] Garbage collection (gc) is performed by every Lisp implementation; ~pl[gc$]. However, different ACL2 builds might see more or fewer messages. This macro is intended to provide an interface for controlling the verbosity, which is off if the argument evaluates to ~c[nil] and otherwise is on. The above functionality is only supported for the following host Common Lisp implementations: CCL, CMUCL, and GCL. Otherwise, the only effect of this macro is to print a notice that it is not supported. You are welcome to contact the ACL2 developers if you would like to help in adding such support for another host Common Lisp. Evaluation of a call of this macro always returns ~c[nil].~/~/" `(gc-verbose-fn ,arg)) (defun get-wormhole-status (name state) ":Doc-Section Miscellaneous make a wormhole's status visible outside the wormhole~/ General Form: (get-wormhole-status name state) ~c[Name] should be the name of a wormhole (~pl[wormhole]). This function returns an error triple (~pl[error-triples]) of the form ~c[(mv nil s state)], where ~c[s] is the status of the named wormhole. The status is obtained by reading the oracle in the ACL2 ~ilc[state].~/ This function makes the status of a wormhole visible outside the wormhole. But since this function takes ~ilc[state] and modifies it, the function may only be used in contexts in which you may change ~ilc[state]. Otherwise, the wormhole status may stay in the wormhole. See ~ilc[wormhole-eval] and ~ilc[wormhole].~/" #+acl2-loop-only (declare (xargs :guard (state-p state)) (ignore name)) #-acl2-loop-only (when (live-state-p state) (return-from get-wormhole-status (value (cdr (assoc-equal name *wormhole-status-alist*))))) (read-acl2-oracle state)) (defun file-write-date$ (file state) (declare (xargs :guard (stringp file) :stobjs state)) #+acl2-loop-only (declare (ignore file)) #+(not acl2-loop-only) (when (live-state-p state) (return-from file-write-date$ (mv (our-ignore-errors (file-write-date file)) state))) (mv-let (erp val state) (read-acl2-oracle state) (mv (and (null erp) (posp val) val) state))) ; Next: debugger control (defun debugger-enable (state) (declare (xargs :guard (and (state-p state) (boundp-global 'debugger-enable state)))) (f-get-global 'debugger-enable state)) (defun break$ () ; This function gets around a bug in Allegro CL (at least in Versions 7.0 and ; 8.0), as admitted by Franz support, and in and CMU CL. These Lisps pay ; attention to *debugger-hook* even when (break) is invoked, but they ; shouldn't. ; Keep this in sync with break-on-error-fn. ":Doc-Section ACL2::ACL2-built-ins cause an immediate Lisp break~/ ACL2 users are generally advised to avoid breaking into raw Lisp. Advanced users may, on occasion, see the need to do so. Evaluating ~c[(break$)] will have that effect. (Exception: ~c[break$] is disabled after evaluation of ~c[(set-debugger-enable :never)]; ~pl[set-debugger-enable].) ~c[Break$] returns ~c[nil].~/~/ :cited-by other" (declare (xargs :guard t)) #-acl2-loop-only (and (not (eq (debugger-enable *the-live-state*) :never)) #+(and gcl (not cltl2)) (break) #-(and gcl (not cltl2)) (let ((*debugger-hook* nil) #+ccl ; useful for CCL revision 12090 and beyond (ccl::*break-hook* nil)) #+ccl ; for CCL revisions before 12090 (declare (ignorable ccl::*break-hook*)) (break))) nil) #-acl2-loop-only (defvar *ccl-print-call-history-count* ; This variable is only used by CCL, but we define it for all Lisps so that ; this name is equally unavailable as a name for defconst in all host Lisps. ; The user is welcome to change this in raw Lisp. Perhaps we should advertise ; it and use a state global. We have attempted to choose a value sufficiently ; large to get well into the stack, but not so large as to swamp the system. ; Even with the default for CCL (as of mid-2013) of -Z 2M, the stack without ; this restriction could be much larger. For example, in the ACL2 loop we ; made the definition ; (defun foo (x) (if (atom x) nil (cons (car x) (foo (cdr x))))) ; and then ran (foo (make-list 1000000)), and after 65713 abbreviated stack ; frames CCL just hung. But with this restriction, it took less than 6 seconds ; to evaluate the following in raw Lisp, including printing the stack to the ; terminal (presumably it would be much faster to print to a file): ; (time$ (ignore-errors (ld '((foo (make-list 1000000)))))) 10000) (defun print-call-history () ; We welcome suggestions from users or Lisp-specific experts for how to improve ; this function, which is intended to give a brief but useful look at the debug ; stack. (declare (xargs :guard t)) #-acl2-loop-only (when (global-val 'boot-strap-flg (w *the-live-state*)) ; We don't know why SBCL 1.0.37 hung during guard verification of ; maybe-print-call-history during the boot-strap. But we sidestep that issue ; here. (return-from print-call-history nil)) #+(and ccl (not acl2-loop-only)) (when (fboundp 'ccl::print-call-history) ; See CCL file lib/backtrace.lisp for more options (eval '(ccl::print-call-history :detailed-p nil :count *ccl-print-call-history-count*))) ; It seems awkward to deal with GCL, both because of differences in debugger ; handling and because we haven't found documentation on how to get a ; backtrace. For example, (system::ihs-backtrace) seems to give a much smaller ; answer when it's invoked during (our-abort) than when it is invoked directly ; in the debugger. ; #+(and gcl (not acl2-loop-only)) ; (when (fboundp 'system::ihs-backtrace) ; (eval '(system::ihs-backtrace))) #+(and allegro (not acl2-loop-only)) (when (fboundp 'tpl::do-command) (eval '(tpl:do-command "zoom" :from-read-eval-print-loop nil :count t :all t))) #+(and sbcl (not acl2-loop-only)) (when (fboundp 'sb-debug::backtrace) (eval '(sb-debug::backtrace))) #+(and cmucl (not acl2-loop-only)) (when (fboundp 'debug::backtrace) (eval '(debug::backtrace))) #+(and clisp (not acl2-loop-only)) (when (fboundp 'system::print-backtrace) (eval '(catch 'system::debug (system::print-backtrace)))) #+(and lispworks (not acl2-loop-only)) (when (fboundp 'dbg::output-backtrace) (eval '(dbg::output-backtrace :verbose))) nil) (defun debugger-enabledp (state) (declare (xargs :guard (and (state-p state) (boundp-global 'debugger-enable state)))) (let ((val (f-get-global 'debugger-enable state))) (and (member-eq val '(t :break :break-bt :bt-break)) t))) (defun maybe-print-call-history (state) (declare (xargs :guard (and (state-p state) (boundp-global 'debugger-enable state)))) (and (member-eq (f-get-global 'debugger-enable state) '(:bt :break-bt :bt-break)) (print-call-history))) (defmacro with-reckless-readtable (form) ; This macro creates a context in which reading takes place without usual ; checks that #n# is only used after #n= and without the usual restrictions on ; characters (specifically, *old-character-reader* is used rather than the ACL2 ; character reader, #'acl2-character-reader). See *reckless-acl2-readtable*. #+acl2-loop-only form #-acl2-loop-only `(let ((*readtable* *reckless-acl2-readtable*) ; Since print-object$ binds *readtable* to *acl2-readtable*, we bind the latter ; here: (*acl2-readtable* *reckless-acl2-readtable*)) ,form)) (defmacro set-debugger-enable (val) ; WARNING: Keep this documentation in sync with the initial setting of ; 'debugger-enable in *initial-global-table* and with our-abort. ":Doc-Section switches-parameters-and-modes control whether Lisp errors and breaks invoke the Lisp debugger~/ ~bv[] Forms (see below for explanations and GCL exceptions): (set-debugger-enable t) ; enable breaks into the raw Lisp debugger (set-debugger-enable :break) ; same as above :set-debugger-enable t ; same as above (set-debugger-enable :break-bt) ; as above, but print a backtrace first (set-debugger-enable :bt-break) ; as above, but print a backtrace first (set-debugger-enable :bt) ; print a backtrace but do not enter debugger (set-debugger-enable :never) ; disable all breaks into the debugger (set-debugger-enable nil) ; disable debugger except when calling break$ ~ev[] ~em[Introduction.] Suppose we define ~c[foo] in ~c[:]~ilc[program] mode to take the ~ilc[car] of its argument. This can cause a raw Lisp error. ACL2 will then return control to its top-level loop unless you enable the Lisp debugger, as shown below (except: the error message can take quite a different form in non-ANSI GCL). ~bv[] ACL2 !>(defun foo (x) (declare (xargs :mode :program)) (car x)) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo 3) *********************************************** ************ ABORTING from raw Lisp *********** Error: Attempt to take the car of 3 which is not listp. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !>(SET-DEBUGGER-ENABLE T) ACL2 !>(foo 3) Error: Attempt to take the car of 3 which is not listp. [condition type: TYPE-ERROR] Restart actions (select using :continue): 0: Abort entirely from this (lisp) process. [Current process: Initial Lisp Listener] [1] ACL2(1): [RAW LISP] ~ev[]~/ ~em[Details.] ACL2 usage is intended to take place inside the ACL2 read-eval-print loop (~pl[lp]). Indeed, in most Lisp implementations ACL2 comes up inside that loop, as evidenced by the prompt: ~bv[] ACL2 !> ~ev[] However, one can occasionally hit a raw Lisp error. Here is the above example again, this time for a GCL implementation, which unfortunately gives a slightly less aesthetic report. ~bv[] ACL2 !>(foo 3) Error: 3 is not of type LIST. Fast links are on: do (si::use-fast-links nil) for debugging Error signalled by CAR. Backtrace: funcall > system:top-level > lisp:lambda-closure > lp > acl2_*1*_acl2::foo > foo > car > system:universal-error-handler > system::break-level-for-acl2 > let* > UNLESS ACL2 !> ~ev[] Here, the user has defined ~c[foo] in ~c[:]~ilc[program] mode, with an implicit ~il[guard] of ~c[t]. The ACL2 evaluator therefore called the Lisp evaluator, which expected ~c[nil] or a ~ilc[consp] argument to ~ilc[car]. By default, ACL2 will return to its top-level loop (at the same level of ~ilc[LD]) when there is a raw Lisp error, as though a call of ~ilc[ER] with flag ~c[HARD] has been evaluated. If instead you want to enter the raw Lisp debugger in such cases, evaluate the following form. ~bv[] (set-debugger-enable t) ~ev[] You can subsequently return to the default behavior with: ~bv[] (set-debugger-enable nil) ~ev[] Either way, you can enter the Lisp debugger from within the ACL2 loop by evaluating ~c[(]~ilc[break$]~c[)]. If you want ~c[break$] disabled, then evaluate the following, which disables entry to the Lisp debugger not only for Lisp errors but also when executing ~c[(break$)]. ~bv[] (set-debugger-enable :never) ~ev[] The discussion above also applies to interrupts (from ~c[Control-C]) in some, but not all, host Common Lisps. It remains to discuss options ~c[:break], ~c[:bt], ~c[:break-bt], and ~c[:bt-break]. Option ~c[:break] is synonymous with option ~c[t], while option ~c[:bt] prints a backtrace. Options ~c[:break-bt] and ~c[:bt-break] are equivalent, and each has the combined effect of ~c[:bt] and ~c[:break]: a backtrace is printed and then the debugger is entered. Note that ~c[set-debugger-enable] applies not only to raw Lisp errors, but also to ACL2 errors: those affected by ~ilc[break-on-error]. However, for ACL2 errors, entering the debugger is controlled only by ~c[break-on-error], not by ~c[set-debugger-enable]. For ACL2 errors encountered after evaluating ~c[(break-on-error t)], the ~c[set-debugger-enable] values of ~c[:bt], ~c[:break-bt], and ~c[:bt-break] will result in the same effect: in many host LIsps, this effect will be to cause a backtrace to be printed. Remark for Common Lisp hackers (except for the case that the host Lisp is non-ANSI GCL). You can customize the form of the backtrace printed by entering raw Lisp (with ~c[:q]) and then redefining function ~c[print-call-history], whose definition immediately precedes that of ~c[break-on-error] in ACL2 source file ~c[ld.lisp]. Of course, all bets are off when defining any function in raw Lisp, but as a practical matter you are probably fine as long as your books are ultimately certified with an unmodified copy of ACL2. If you come up with improvements to ~c[print-call-history], please pass them along to the ACL2 implementors." `(set-debugger-enable-fn ,val state)) (defun set-debugger-enable-fn (val state) (declare (xargs :guard (and (state-p state) (member-eq val '(t nil :never :break :bt :break-bt :bt-break))) :guard-hints (("Goal" :in-theory (enable state-p1))))) #+(and (not acl2-loop-only) (and gcl (not cltl2))) (when (live-state-p state) (setq lisp::*break-enable* (debugger-enabledp state))) (pprogn (f-put-global 'debugger-enable val state) (if (consp (f-get-global 'dmrp state)) ; Then user invoked this function, so avoid having a later stop-dmr change the ; value of 'debugger-enable. (f-put-global 'dmrp t state) state))) ; See comment in true-listp-cadr-assoc-eq-for-open-channels-p. (in-theory (disable true-listp-cadr-assoc-eq-for-open-channels-p)) ; See comment in consp-assoc-equal. (in-theory (disable (:type-prescription consp-assoc-equal))) ; See comment in true-list-listp-forward-to-true-listp-assoc-equal. (in-theory (disable (:type-prescription true-list-listp-forward-to-true-listp-assoc-equal))) ; The definitions that follow provide support for the experimental parallelism ; extension, ACL2(p), of ACL2. Also see the Essay on Parallelism, Parallelism ; Warts, Parallelism Blemishes, Parallelism No-fixes, and Parallelism Hazards. (defun add-@par-suffix (symbol) (declare (xargs :guard (symbolp symbol))) (intern (string-append (symbol-name symbol) "@PAR") "ACL2")) (defun generate-@par-mappings (symbols) (declare (xargs :guard (symbol-listp symbols))) (cond ((endp symbols) nil) (t (cons (cons (add-@par-suffix (car symbols)) (car symbols)) (generate-@par-mappings (cdr symbols)))))) ; Parallelism blemish: consider adding a doc topic explaining that if a user ; finds the #+acl2-par version of an "@par" function to be useful, that they ; should contact the authors of ACL2. The authors should then create a version ; of the desired "@par" function, perhaps suffixing it with "@ns" (for "no ; state"). And then the "@par" function could simply call the "@ns" version. ; A good example candidate for this is simple-translate-and-eval@par, which ; could be used inside Sol Swords's GL system to produce computed hints that ; don't modify state. (defconst *@par-mappings* ; For each symbol SYM in the quoted list below, the #-acl2-par call below of ; define-@par-macros will automatically define a macro SYM@par that expands to ; the corresponding call of SYM. For #+acl2-par, however, SYM@par must be ; defined explicitly. For example, in #-acl2-par, waterfall1-lst@par is ; automatically defined to call waterfall1-lst, but in #+acl2-par we explicitly ; define waterfall1-lst@par. ; Next we consider the role played by the list below in expanding calls of the ; macro defun@par. In #-acl2-par, there actually is no role: a call of ; defun@par simply expands to a call of defun on the same arguments, i.e., ; defun@par is simply replaced by defun. ; Consider then the #+acl2-par case for a call (defun@par FN . rest). This ; call expands to a progn of two defuns, which we refer to as the "parallel" ; (or "@par") and "serial" (or "non-@par") versions of (the definition on) FN. ; For the parallel version we obtain (defun FN@par . rest). For the serial ; version we obtain (defun FN . rest'), where rest' is the result of replacing ; SYM@par by SYM in rest for each symbol SYM in the list below. Consider for ; example the definition (defun@par waterfall-step formals body); note that we ; are still considering only the #+acl2-par case. This call expands to a progn ; of parallel and serial versions. The parallel version is (defun ; waterfall-step@par formals body), i.e., with no change to the body of the ; given defun@par. The serial version is of the form (defun waterfall-step ; formals body'), where for example the call of waterfall-step1@par in body is ; replaced by a corresponding call of waterfall-step1 in body'. ; Suppose that F is a function that has both a parallel definition (defining ; F@par) and serial definition (defining F), such that F@par is called in the ; body of (defun@par G ...). Then it is useful to include F in the list below. ; To see why, consider the #-acl2-par expansion of (defun@par G ...), which ; still has a call of F@par. By including F in the list below, we ensure that ; F@par is automatically defined as a macro that replaces F@par by F. ; Note that this list does not contain all symbols defined with an @par ; counterpart. For example, the symbol mutual-recursion is omitted from this ; list, and mutual-recursion@par must be defined explicitly in both #+acl2-par ; and #-acl2-par. This works because mutual-recursion@par does not need to be ; called from inside any functions defined with defun@par. ; Also, sometimes we need to create a non-@par version of a macro that is the ; identity macro, just so that we can have an @par version that does something ; important for the parallel case inside a call of defun@par. ; Waterfall1-wrapper is an example of such a macro (and it may be the only ; example). Since waterfall1-wrapper@par is called within functions defined ; with defun@par, waterfall1-wrapper must be included in this list, as ; explained above. ; This list is split into two groups: (1) symbols that have an explicit ; #+acl2-par definition for the parallel (@par) version, and (2) symbols for ; which defun@par is used for defining both the symbol and its @par version. ; Group (1) is further divided into (1a) utilities that are "primitive" in ; nature and (1b) higher-level functions and macros. (generate-@par-mappings '( ; Group 1a (see above): catch-time-limit5 cmp-and-value-to-error-quadruple cmp-to-error-triple er er-let* er-progn error-fms error-in-parallelism-mode error1 f-put-global io? io?-prove mv mv-let parallel-only pprogn serial-first-form-parallel-second-form serial-only sl-let state-mac value warning$ ; Group 1b (see above): add-custom-keyword-hint eval-clause-processor eval-theory-expr formal-value-triple increment-timer simple-translate-and-eval translate-in-theory-hint waterfall-print-clause-id waterfall-print-clause-id-fmt1-call waterfall-update-gag-state waterfall1-lst waterfall1-wrapper xtrans-eval ; Group 2 (see above): accumulate-ttree-and-step-limit-into-state add-custom-keyword-hint-fn apply-override-hint apply-override-hint1 apply-override-hints apply-reorder-hint apply-top-hints-clause check-translated-override-hint chk-arglist chk-do-not-expr-value chk-equal-arities chk-equiv-classicalp chk-theory-expr-value chk-theory-expr-value1 chk-theory-invariant chk-theory-invariant1 custom-keyword-hint-interpreter custom-keyword-hint-interpreter1 eval-and-translate-hint-expression find-applicable-hint-settings find-applicable-hint-settings1 gag-state-exiting-cl-id load-hint-settings-into-pspv load-hint-settings-into-rcnst load-theory-into-enabled-structure maybe-warn-about-theory maybe-warn-about-theory-from-rcnsts maybe-warn-about-theory-simple maybe-warn-for-use-hint pair-cl-id-with-hint-setting process-backtrack-hint push-clause put-cl-id-of-custom-keyword-hint-in-computed-hint-form record-gag-state thanks-for-the-hint translate translate1 translate-backchain-limit-rw-hint translate-backtrack-hint translate-bdd-hint translate-bdd-hint1 translate-by-hint translate-case-split-limitations-hint translate-cases-hint translate-clause-processor-hint translate-custom-keyword-hint translate-do-not-hint translate-do-not-induct-hint translate-error-hint translate-expand-hint translate-expand-hint1 translate-expand-term translate-expand-term1 translate-functional-substitution translate-hands-off-hint translate-hands-off-hint1 translate-hint translate-hints translate-hints1 translate-hints2 translate-hints+1 translate-hint-expression translate-hint-expressions translate-hint-settings translate-induct-hint translate-lmi translate-lmi/functional-instance translate-lmi/instance translate-no-op-hint translate-no-thanks-hint translate-nonlinearp-hint translate-or-hint translate-reorder-hint translate-restrict-hint translate-rw-cache-state-hint translate-simple-or-error-triple translate-substitution translate-substitution-lst translate-term-lst translate-use-hint translate-use-hint1 translate-x-hint-value warn-on-duplicate-hint-goal-specs waterfall-msg waterfall-print-clause waterfall-step waterfall-step1 waterfall-step-cleanup waterfall0 waterfall0-or-hit waterfall0-with-hint-settings waterfall1))) (defun make-identity-for-@par-mappings (mappings) ; Although this is only used for #-acl2-par, we define it unconditionally so ; that its rune is available in both ACL2 and ACL2(p). Robert Krug used ; arithmetic-5, which employs deftheory-static, and hence was bitten when this ; rune was missing. (declare (xargs :guard (alistp mappings))) (cond ((endp mappings) nil) (t (cons `(defmacro ,(caar mappings) (&rest rst) (cons ',(cdar mappings) rst)) (make-identity-for-@par-mappings (cdr mappings)))))) #-acl2-par (defmacro define-@par-macros () ; This macro defines the #-acl2-par version of the @par functions and macros. `(progn ,@(make-identity-for-@par-mappings *@par-mappings*))) #-acl2-par (define-@par-macros) ; To find places where we issue definitions both without the "@par" suffix and ; with the "@par" suffix, one can run the following. (For example, there might ; be a defun@par of foo, but there might instead be both a defun of foo and a ; defun of foo@par. The first line below can catch either of these.) ; grep "@par" *.lisp | grep "defun " ; grep "@par" *.lisp | grep "defmacro " (defun replace-defun@par-with-defun (forms) (declare (xargs :guard (alistp forms))) (cond ((endp forms) nil) ((eq (caar forms) 'defun@par) (cons (cons 'defun (cdar forms)) (replace-defun@par-with-defun (cdr forms)))) (t (cons (car forms) (replace-defun@par-with-defun (cdr forms)))))) #-acl2-par (defmacro mutual-recursion@par (&rest forms) `(mutual-recursion ,@(replace-defun@par-with-defun forms))) #+acl2-par (defun defun@par-fn (name parallel-version rst) (declare (xargs :guard (and (symbolp name) (booleanp parallel-version) (true-listp rst)))) (let ((serial-function-symbol (intern (symbol-name name) "ACL2")) (parallel-function-symbol (intern (string-append (symbol-name name) "@PAR") "ACL2")) (serial-definition-args (sublis *@par-mappings* rst)) (parallel-definition-args rst)) (if parallel-version `(defun ,parallel-function-symbol ,@parallel-definition-args) `(defun ,serial-function-symbol ,@serial-definition-args)))) #+acl2-par (defun mutual-recursion@par-guardp (rst) (declare (xargs :guard t)) (cond ((atom rst) (equal rst nil)) (t (and (consp (car rst)) (true-listp (car rst)) (true-listp (caddr (car rst))) ; formals (symbolp (cadar rst)) (member-eq (car (car rst)) '(defun defund defun-nx defund-nx defun@par)) (mutual-recursion@par-guardp (cdr rst)))))) #+acl2-par (defun mutual-recursion@par-fn (forms serial-and-par) (declare (xargs :guard (and (mutual-recursion@par-guardp forms) (booleanp serial-and-par)))) (cond ((endp forms) nil) ((equal (caar forms) 'defun@par) (let* ((curr (car forms)) (name (cadr curr)) (rst (cddr curr))) (cond (serial-and-par (cons (defun@par-fn name t rst) (cons (defun@par-fn name nil rst) (mutual-recursion@par-fn (cdr forms) serial-and-par)))) (t (cons (defun@par-fn name nil rst) (mutual-recursion@par-fn (cdr forms) serial-and-par)))))) (t (cons (car forms) (mutual-recursion@par-fn (cdr forms) serial-and-par))))) #+acl2-par (defmacro mutual-recursion@par (&rest forms) (declare (xargs :guard (mutual-recursion@par-guardp forms))) `(mutual-recursion ,@(mutual-recursion@par-fn forms t))) (defmacro defun@par (name &rest args) ; See *@par-mappings* for a discussion of this macro. In brief: for ; #-acl2-par, defun@par is just defun. But for #+acl2-par, defun@par defines ; two functions, a "parallel" and a "serial" version. The serial version ; defines the given symbol, but the parallel version defines a corresponding ; symbol with suffix "@PAR". #+acl2-par `(progn ,(defun@par-fn name t args) ,(defun@par-fn name nil args)) #-acl2-par `(defun ,name ,@args)) (defmacro serial-first-form-parallel-second-form (x y) ; Keep in sync with serial-first-form-parallel-second-form@par. (declare (ignore y)) x) #+acl2-par (defmacro serial-first-form-parallel-second-form@par (x y) ; Keep in sync with serial-first-form-parallel-second-form. (declare (ignore x)) y) (defmacro serial-only (x) ; Keep in sync with serial-only@par. x) #+acl2-par (defmacro serial-only@par (x) ; Keep in sync with serial-only. (declare (ignore x)) nil) (defmacro parallel-only (x) ; Keep in sync with parallel-only@par. (declare (ignore x)) nil) #+acl2-par (defmacro parallel-only@par (x) ; Keep in sync with parallel-only. x) #+acl2-par (defmacro mv@par (&rest rst) (declare (xargs :guard ; sanity check (member-eq 'state rst))) `(mv? ,@(remove1-eq 'state rst))) #+acl2-par (defmacro value@par (val) ; Keep in sync with value. `(mv nil ,val)) (defmacro state-mac () ; Keep in sync with state-mac@par. 'state) #+acl2-par (defmacro state-mac@par () ; Keep in sync with state-mac. nil) #+acl2-par (defmacro mv-let@par (vars call &rest rst) (declare (xargs :guard ; sanity check (member-eq 'state vars))) `(mv?-let ,(remove1-eq 'state vars) ,call ,@rst)) #+acl2-par (defmacro warning$@par (&rest rst) ; We do not simply just call warning$-cw, because we actually have state ; available when we use warning$@par. `(let ((state-vars (default-state-vars t)) (wrld (w state))) (warning$-cw1 ,@rst))) (defmacro error-in-parallelism-mode (fake-return-value form) (declare (ignore fake-return-value)) form) #+acl2-par (defmacro error-in-parallelism-mode@par (return-value form) ; We avoid even trying to evaluate form, instead returning a hard error with a ; useful message. Return-value must have the same output signature as that of ; form. ; Any form enwrapped with error-in-parallelism-mode@par is essentially ; disabled. To restore the code to its original form, just remove the wrapper ; error-in-parallelism-mode@par. `(prog2$ (er hard 'error-in-parallelism-mode@par "There has been an attempt to evaluate a form that is disallowed in ~ the parallelized evaluation of the waterfall. See :doc ~ set-waterfall-parallelism for how to disable such parallel ~ evaluation. Please let the ACL2 authors know if you see this ~ message, as our intent is that its occurence should be rare. The ~ offending form is: ~x0" ',form) ,return-value)) #+acl2-par (defun increment-timer@par (name state) (declare (xargs :guard t) (ignore name state)) (state-mac@par)) ; These constants are needed both in parallel.lisp and boot-strap-pass-2.lisp, ; so we define them here. (defconst *waterfall-printing-values* '(:full :limited :very-limited)) (defconst *waterfall-parallelism-values* '(nil t :full :top-level :resource-based :resource-and-timing-based :pseudo-parallel)) ; This is needed in both boot-strap-pass-2.lisp and parallel.lisp, so we put it ; here. (defun symbol-constant-fn (prefix sym) (declare (xargs :guard (and (symbolp prefix) (symbolp sym)))) (intern (concatenate 'string (symbol-name prefix) "-" (symbol-name sym)) "ACL2")) ; Oracle-funcall, oracle-apply, and oracle-apply-ttag: (defun stobjs-in (fn w) ; Fn must be a function symbol, not a lambda expression and not an ; undefined symbol. See the Essay on STOBJS-IN and STOBJS-OUT. (declare (xargs :guard (and (symbolp fn) (plist-worldp w)))) (if (eq fn 'cons) ; We call this function on cons so often we optimize it. '(nil nil) (getprop fn 'stobjs-in nil 'current-acl2-world w))) (defmacro oracle-funcall (fn &rest args) ":Doc-Section ACL2::ACL2-built-ins call a function argument on the remaining arguments~/ ~c[Oracle-funcall] evaluates its first argument to produce an ACL2 function symbol, and then applies that function symbol to the values of the rest of the arguments. The return value is of the form ~c[(mv call-result state)]. ~bv[] Examples: (oracle-funcall 'cons 3 4) ==> (mv '(3 . 4) ) (oracle-funcall (car '(floor foo bar)) (+ 6 7) 5) ==> (mv 2 ) ~ev[] ~c[Oracle-funcall] is a macro; each of its calls macroexpands to a call of the related utility ~c[oracle-apply] that takes the ACL2 ~ilc[state] as an argument, as follows: ~bv[] (oracle-funcall fn x1 x2 .. xk) ~ev[] macroexpands to ~bv[] (oracle-apply fn (list x1 x2 .. xk) state) ~ev[] Note that calls of ~c[oracle-funcall] and ~c[oracle-apply] return two values: the result of the function application, and a modified ~ilc[state]. ~l[oracle-apply] for details, including information about ~il[guard]s.~/~/" `(oracle-apply ,fn (list ,@args) state)) (defun all-nils (lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) t) (t (and (eq (car lst) nil) (all-nils (cdr lst)))))) (defun oracle-apply-guard (fn args state) (declare (xargs :stobjs state)) (and (symbolp fn) (not (eq fn 'return-last)) (true-listp args) (let* ((wrld (w state)) (formals (getprop fn 'formals t 'current-acl2-world wrld)) (stobjs-in (stobjs-in fn wrld))) (and (not (eq formals t)) (eql (len formals) (len args)) (true-listp stobjs-in) ; needed for guard of all-nils (all-nils stobjs-in))))) (defun oracle-apply (fn args state) ; The use of an oracle is important for the logical story. For example, we can ; imagine the following sort of situation without an oracle. ; (encapsulate ; () ; (local (defun f (x) ; 1)) ; (defthm prop-1 ; (equal (oracle-funcall 'f) 1) ; :rule-classes nil)) ; ; (encapsulate ; () ; (local (defun f () ; 2)) ; (defthm prop-2 ; (equal (oracle-funcall 'f) 2) ; :rule-classes nil)) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use (prop-1 prop-2)))) ":Doc-Section ACL2::ACL2-built-ins call a function argument on the given list of arguments~/ ~c[Oracle-apply] evaluates its first argument to produce an ACL2 function symbol, ~c[FN], and then applies ~c[FN] to the value of the second argument, which should be a true list whose length is the number of inputs for ~c[FN]. The return value is of the form ~c[(mv call-result state)]. ~bv[] Examples: (oracle-apply 'cons '(3 4) state) = (mv '(3 . 4) ) (oracle-apply (car '(floor foo)) (list (+ 6 7) 5) state) = (mv 2 ) ~ev[] Also ~pl[oracle-funcall] for a related utility. Note that calls of ~c[oracle-funcall] and ~c[oracle-apply] return two values: the result of the function application, and a modified ~ilc[state]. ~c[Oracle-apply] is defined in ~c[:]~ilc[logic] mode, and in fact is ~il[guard]-verified. However, you will not be able to prove much about this function, because it is defined in the logic using the ~c[acl2-oracle] field of the ACL2 ~il[state]. The behavior described above ~-[] i.e., making a function call ~-[] takes place when the third argument is the ACL2 ~ilc[state], so during proofs (when that can never happen), a term ~c[(oracle-apply 'fn '...)] will not simplify using a call of ~c[fn]. The guard for ~c[(oracle-apply fn args state)] is the term ~c[(oracle-apply-guard fn args state)], which says the following: ~c[fn] and ~c[args] must satisfy ~ilc[symbolp] and ~ilc[true-listp], respectively; ~c[fn] must be a known function symbol other than ~ilc[return-last] that is not untouchable (~pl[push-untouchable]) and has no ~il[stobj] arguments (not even ~ilc[state]); and the ~il[length] of ~c[args] must equal the arity of ~c[fn] (~pl[signature]). The requirement that ~c[fn] be a known function symbol may be a bit onerous for guard verification, but this is easily overcome by using ec-call, for example as follows. ~bv[] (defun f (x state) (declare (xargs :stobjs state)) (ec-call (oracle-apply 'car (list x) state))) ~ev[] This use of ~ilc[ec-call] will, however, cause the ~il[guard] of ~c[oracle-apply] to be checked at runtime. If the ~il[guard] for ~c[oracle-apply] fails to hold but there is no guard violation because guard-checking is suppressed (~pl[set-guard-checking]), then the value returned is computed using its logical definition ~-[] which, as mentioned above, uses the ACL2 oracle ~-[] and hence the value computed is unpredictable (indeed, the function argument will not actually be called). The value returned by ~c[oracle-apply] is always a single value obtained by calling the executable counterpart of its function argument, as we now explain. Consider a form ~c[(oracle-apply fn args state)] that evaluates to ~c[(mv VAL state')], where ~c[fn] evaluates to the function symbol ~c[F]. If ~c[F] returns multiple values, then ~c[VAL] is the first value computed by the call of ~c[F] on the value of ~c[args]. More precisely, ~c[oracle-apply] actually invokes the executable counterpart of ~c[F]; thus, if ~c[args] is the expression ~c[(list x1 ... xk)], then ~c[VAL] is the same as (first) value returned by evaluating ~c[(ec-call (F x1 x2 ... xk))]. ~l[ec-call]. (Remark. If you identify a need for a version of ~c[oracle-apply] to return multiple values, we can perhaps provide such a utility; feel free to contact the ACL2 implementors to request it.) A subtlety is that the evaluation takes place in so-called ``safe mode'', which avoids raw Lisp errors due to calls of ~c[:]~ilc[program] mode functions. The use of safe mode is unlikely to be noticed if the value of the first argument of ~c[oracle-apply] is a ~c[:]~ilc[logic] mode function symbol. However, for ~c[:program] mode functions with side effects due to special raw Lisp code, as may be the case for built-in functions or for custom functions defined with active trust tags (~pl[defttag]), use of the following function may be preferable: ~l[oracle-apply-raw] for a much less restrictive version of ~c[oracle-apply], which avoids safe mode and (for example) can apply a function that has a definition in the host Lisp but not in the ACL2 ~il[world].~/~/" (declare (xargs :stobjs state :guard (oracle-apply-guard fn args state))) #-acl2-loop-only (when (live-state-p state) (return-from oracle-apply (mv (state-free-global-let* ((safe-mode t)) (apply (*1*-symbol fn) args)) state))) #+acl2-loop-only (mv-let (erp val state) (read-acl2-oracle state) (declare (ignore erp)) ; We arrange for the result to depend logically on fn and args. This is ; probably not important to do, but it seems potentially weird for the result ; ot have nothing to do with fn or with args. (mv (and (true-listp val) (eq (car val) fn) (equal (cadr val) args) (caddr val)) state))) (defun oracle-apply-raw (fn args state) ":Doc-Section ACL2::ACL2-built-ins call a function argument on the given list of arguments, no restrictions~/ ~l[oracle-apply], as we assume familiarity with that function. ~c[Oracle-apply-raw] is a variant of ~c[oracle-apply] that is untouchable, and hence requires a trust tag to remove the untouchability (~pl[defttag] and ~pl[remove-untouchable]). Unlike ~c[oracle-apply], ~c[oracle-apply-raw] simply calls the raw Lisp function ~c[funcall] to compute the result, without restriction: the specified ~c[:]~ilc[guard] is ~c[t], the function itself is applied (not its executable counterpart), there is no restriction for untouchable functions or ~ilc[return-last], and safe mode is not used. Thus, in general, ~c[oracle-apply-raw] can be dangerous to use: any manner of error can occur! As is the case for ~ilc[oracle-apply], the function symbol ~ilc[oracle-apply-raw] is defined in ~c[:]~ilc[logic] mode and is ~il[guard]-verified. ~c[Oracle-apply-raw] is logically defined to be ~ilc[oracle-apply]; more precisely: ~bv[] (oracle-apply-raw fn args state) = {logical definition} (ec-call (oracle-apply fn args state)) ~ev[]~/~/" (declare (xargs :stobjs state :guard t)) #-acl2-loop-only (when (live-state-p state) (return-from oracle-apply-raw (mv (funcall fn args) state))) #+acl2-loop-only (ec-call (oracle-apply fn args state))) (defun time-tracker-fn (tag kwd kwdp times interval min-time msg) ; Do not conditionalize this function on #-acl2-par, even though its only ; intended use is on behalf of the #-acl2-par definition of time-tracker, ; because otherwise theories computed for ACL2 and ACL2(p) may differ, for ; example when including community books under arithmetic-5/. (declare (xargs :guard t)) (cond ((and (booleanp tag) kwdp) (er hard? 'time-tracker "It is illegal to call ~x0 with a Boolean tag and more than one ~ argument. See :DOC time-tracker." 'time-tracker)) ((booleanp tag) #-acl2-loop-only (setf (symbol-value '*time-tracker-disabled-p*) ; setq gives compiler warning (not tag)) nil) #-acl2-loop-only ((symbol-value '*time-tracker-disabled-p*) nil) ((not (symbolp tag)) (er hard? 'time-tracker "Illegal first argument for ~x0 (should be a symbol): ~x1. See :DOC ~ time-tracker." 'time-tracker)) ((and (not (booleanp tag)) (not (member-eq kwd '(:init :end :print? :stop :start)))) (er hard? 'time-tracker "Illegal second argument for ~x0: ~x1. See :DOC time-tracker." 'time-tracker kwd)) ((or (and times (not (eq kwd :init))) (and interval (not (eq kwd :init))) (and min-time (not (eq kwd :print?))) (and msg (not (or (eq kwd :init) (eq kwd :print?))))) (er hard? 'time-tracker "Illegal call of ~x0: a non-nil keyword argument of ~x1 is illegal ~ for a second argument of ~x2. See :DOC time-tracker." 'time-tracker (cond ((and times (not (eq kwd :init))) :times) ((and interval (not (eq kwd :init))) :interval) ((and min-time (not (eq kwd :print?))) :min-time) (t :msg)) kwd)) (t #-acl2-loop-only (case kwd (:init (tt-init tag times interval msg)) (:end (tt-end tag)) (:print? (tt-print? tag min-time msg)) (:stop (tt-stop tag)) (:start (tt-start tag))) nil))) #-acl2-par (defmacro time-tracker (tag &optional (kwd 'nil kwdp) &key times interval min-time msg) `(time-tracker-fn ,tag ,kwd ,kwdp ,times ,interval ,min-time ,msg)) #+acl2-par (defmacro time-tracker (&rest args) (declare (ignore args)) nil) (defdoc time-tracker ; This documentation is separated from the defmacro for time-tracker because ; that defmacro has two definitions, one for #-acl2-par and one for ; #+acl2-par. We need this :doc topic present in both kinds of builds, because ; of the :cite of it in :doc trace. ":Doc-Section programming display time spent during specified evaluation~/ The ~c[time-tracker] macro is a utility for displaying time spent during specified evaluation. In general, the user provides this specification. However, ACL2 itself uses this utility for tracking uses of its ~il[tau-system] reasoning utility (~pl[time-tracker-tau]). We discuss that use as an example before discussing the general form for calls of ~c[time-tracker]. Note that by default, the time being tracked is runtime (cpu time); to switch to realtime (elapsed time), ~pl[get-internal-time]. Remark for ACL2(p) users (~pl[parallelism]): ~c[time-tracker] is merely a no-op in ACL2(p). During the development of the ~il[tau-system], we were concerned about the possibility that it would slow down proofs without any indication of how one might avoid the problem. We wanted a utility that would alert the user in such situations. However, the tau-system code does not return ~il[state], so we could not track time spent in the state. We developed the ~c[time-tracker] utility to track time and print messages, and we did it in a general way so that others can use it in their own code. Here is an example of such a message that could be printed during a proof. ~bv[] TIME-TRACKER-NOTE [:TAU]: Elapsed runtime in tau is 2.58 secs; see :DOC time-tracker-tau. ~ev[] And here is an example of such a message that could be printed at the end of the proof. ~bv[] TIME-TRACKER-NOTE [:TAU]: For the proof above, the total time spent in the tau system was 20.29 seconds. See :DOC time-tracker-tau. ~ev[] The ~c[time-tracker] utility tracks computation time spent on behalf of a user-specified ``tag''. In the case of the tau-system, we chose the tag, ~c[:tau]. The first argument of ~c[time-tracker] is the tag, which in our running example is always ~c[:tau]. Note that although all arguments of ~c[time-tracker] are evaluated, the first argument is typically a keyword and the second is always a keyword, and such arguments evaluate to themselves. An ACL2 function invoked at the start of a proof includes approximately the following code. ~bv[] (progn$ (time-tracker :tau :end) (time-tracker :tau :init :times '(1 2 3 4 5) :interval 5 :msg \"Elapsed runtime in tau is ~~st secs; see :DOC ~~ time-tracker-tau.~~|~~%\") ...) ~ev[] The first ~c[time-tracker] call (above) ends any existing time-tracking for tag ~c[:tau]. One might have expected it be put into code managing the proof summary, but we decided not to rely on that code being executed, say, in case of an interrupt. When a given tag is not already being time-tracked, then ~c[:end] is a no-op (rather than an error). The second ~c[time-tracker] call (above) initiates time-tracking for the tag, ~c[:tau]. Moreover, it specifies the effect of the ~c[:print?] keyword. Consider the following abbreviated definition from the ACL2 source code. ~bv[] (defun tau-clausep-lst-rec (clauses ens wrld ans ttree state calist) (cond ((endp clauses) (mv (revappend ans nil) ttree calist)) (t (mv-let (flg1 ttree1 calist) (tau-clausep (car clauses) ens wrld state calist) (prog2$ (time-tracker :tau :print?) (tau-clausep-lst-rec (cdr clauses) ...)))))) ~ev[] Notice that ~c[(time-tracker :tau :print?)] is executed immediately after ~c[tau-clausep] is called. The idea is to check whether the total time elapsed inside the tau-system justifies printing a message to the user. The specification of ~c[:times '(1 2 3 4 5)] in the ~c[:init] form above says that a message should be printed after 1 second, after 2 seconds, ..., and after 5 seconds. Thereafter, the specification of ~c[:interval 5] in the ~c[:init] form above says that each time we print, at least 5 additional seconds should have been tracked before ~c[(time-tracker :tau :print?)] prints again. Finally, the ~c[:msg] keyword above specifies just what should be printed. If it is omitted, then a reasonable default message is printed (as discussed below), but in this case we wanted to print a custom message. The ~c[:msg] string above is what is printed using formatted printing (~pl[fmt]), where the character ~c[#\\t] is bound to a string giving a decimal representation with two decimal points of the time tracked so far for tag ~c[:tau]. (As our general description below points out, ~c[:msg] can also be a ``message'' list rather than a string.) But when is time actually tracked for ~c[:tau]? Consider the following definition from the ACL2 source code. ~bv[] (defun tau-clausep-lst (clauses ens wrld ans ttree state calist) (prog2$ (time-tracker :tau :start) (mv-let (clauses ttree calist) (tau-clausep-lst-rec clauses ens wrld ans ttree state calist) (prog2$ (time-tracker :tau :stop) (mv clauses ttree calist))))) ~ev[] The two calls of ~c[time-tracker] above first start, and then stop, time-tracking for the tag, ~c[:tau]. Thus, time is tracked during evaluation of the call of ~c[tau-clausep-lst-rec], which is the function (discussed above) that does the ~il[tau-system]'s work. Finally, as noted earlier above, ACL2 may print a time-tracking message for tag ~c[:tau] at the end of a proof. The ACL2 function ~c[print-summary] contains essentially the following code. ~bv[] (time-tracker :tau :print? :min-time 1 :msg \"For the proof above, the total runtime ~~ spent in the tau system was ~~st seconds. ~~ See :DOC time-tracker-tau.~~|~~%\") ~ev[] The use of ~c[:min-time] says that we are to ignore the ~c[:times] and ~c[:interval] established by the ~c[:init] call described above, and instead, print a message if and only if at least 1 second (since 1 is the value of keyword ~c[:min-time]) has been tracked for tag ~c[:tau]. Formatted printing (~pl[fmt]) is used for the value of ~c[:msg], where the character ~c[#\\t] is bound to a decimal string representation of the time in seconds, as described above. The example above covers all legal values for the second argument of ~c[time-tracker] and discusses all the additional legal keyword arguments. We conclude with a precise discussion of all arguments. Note that all arguments are evaluated; thus when we refer to an argument, we are discussing the value of that argument. All times discussed are runtimes, i.e., cpu times, unless that default is changed; ~pl[get-internal-time]. ~bv[] General forms: (time-tracker t) ; enable time-tracking (default) (time-tracker nil) ; disable time-tracking (time-tracker tag ; a symbol other than t or nil option ; :init, :end, :start, :stop, or :print? ;; keyword arguments: :times ; non-nil if and only if option is :init :interval ; may only be non-nil with :init option :min-time ; may only be non-nil with :print? option :msg ; may only be non-nil with :init and :print? options ~ev[] Time-tracking is enabled by default. If the first argument is ~c[t] or ~c[nil], then no other arguments are permitted and time-tracking is enabled or disabled, respectively. When time-tracking is disabled, nothing below takes place. Thus we assume time-tracking is enabled for the remainder of this discussion. We also assume below that the first argument is neither ~c[t] nor ~c[nil]. We introduce some basic notions about time-tracking. A given tag, such as ~c[:tau] in the example above, might or might not currently be ``tracked'': ~c[:init] causes the specified tag to be tracked, while ~c[:end] causes the specified tag not to be tracked. If the tag is being tracked, the tag might or might not be ``active'': ~c[:start] causes the tag to be in an active state, whie ~c[:stop] causes the tag not to be active. Note that only tracked tags can be in an active or inactive state. For a tag that is being tracked, the ``accumulated time'' is the total time spent in an active state since the time that the tag most recently started being tracked, and the ``checkpoint list'' is a non-empty list of rational numbers specifying when printing may take place, as described below. We now consider each legal value for the second argument, or ``option'', for a call of ~c[time-tracker] on a given tag. ~c[:Init] specifies that the tag is to be tracked. It also establishes defaults for the operation of ~c[:print?], as described below, using the ~c[:times], ~c[:interval], and ~c[:msg] keywords. Of these three, only ~c[:times] is required, and its value must be a non-empty list of rational numbers specifying the initial checkpoint list for the tag. It is an error to specify ~c[:init] if the tag is already being tracked. (So if you don't care whether or not the tag is already being tracked and you want to initiate tracking for that tag, use ~c[:end] first.) ~c[:End] specifies that if the tag is being tracked, then it should nstop being tracked. If the tag is not being tracked, then ~c[:end] has no effect. ~c[:Start] specifies that the tag is to be active. It is an error to specify ~c[:start] if the tag is not being tracked or is already active. ~c[:Stop] specifies that the tag is to stop being active. It is an error to specify ~c[:stop] if the tag is not being tracked or is not active. ~c[:Print?] specifies that if the tag is being tracked (not necessarily active), then a message should be printed if a suitable condition is met. The nature of that message and that condition depend on the keyword options supplied with ~c[:print?] as well as those supplied with the ~c[:init] option that most recently initiated tracking. ~c[:Print?] has no effect if the tag is not being tracked, except that if certain keyword values are checked if supplied with ~c[:print?]: ~c[:min-time] must be a rational number or ~c[nil], and ~c[:msg] must be either a string, a true-list whose ~c[car] is a string, or ~c[nil]. The remainder of this documentation describes the ~c[:print?] option in detail under the assumption that the tag is being tracked: first, giving the conditions under which it causes a message to be printed, and second, explaining what is printed. When ~c[:print?] is supplied a non-~c[nil] value of ~c[:min-time], that value must be a rational number, in which case a message is printed if the accumulated time for the tag is at least that value. Otherwise a message is printed if the accumulated time is greater than or equal to the ~c[car] of the checkpoint list for the tag. In that case, the tracking state for the tag is updated in the following two ways. First, the checkpoint list is scanned from the front and as long as the accumulated time is greater than or equal to the ~c[car] of the remaining checkpoint list, that ~c[car] is popped off the checkpoint list. Second, if the checkpoint list has been completely emptied and a non-~c[nil] ~c[:interval] was supplied when tracking was most recently initiated with the ~c[:init] option, then the checkpoint list is set to contain a single element, namely the sum of the accumulated time with that value of ~c[:interval]. Finally, suppose the above criteria are met, so that ~c[:print?] results in printing of a message. We describe below the message, ~c[msg], that is printed. Here is how it is printed (~pl[fmt]), where ~c[seconds-as-decimal-string] is a string denoting the number of seconds of accumulated time for the tag, with two digits after the decimal point. ~bv[] (fms \"TIME-TRACKER-NOTE [~~x0]: ~~@1~~|\" (list (cons #\0 tag) (cons #\1 msg) (cons #\t seconds-as-decimal-string)) (proofs-co state) state nil) ~ev[] The value of ~c[msg] is the value of the ~c[:msg] keyword supplied with ~c[:print?], if non-~c[nil]; else, the value of ~c[:msg] supplied when most recently initialization with the ~c[:init] option, if non-~c[nil]; and otherwise, the string ~c[\"~~st s\"] (the final ``s'' abbreviating the word ``seconds''). It is convenient to supply ~c[:msg] as a call ~c[(msg str arg-0 arg-1 ... arg-k)], where ~c[str] is a string and each ~c[arg-i] is the value to be associated with ~c[#\\i] upon formatted printing (as with ~ilc[fmt]) of the string ~c[str].~/~/") (defdoc time-tracker-tau ":Doc-Section miscellaneous messages about expensive use of the ~il[tau-system]~/ This ~il[documentation] topic explains messages printing by the theorem prover about the ~il[tau-system], as follows. During a proof you may see a message such as the following. ~bv[] TIME-TRACKER-NOTE [:TAU]: Elapsed runtime in tau is 4.95 secs; see :DOC time-tracker-tau. ~ev[] Just below a proof summary you may see a message such as the following. ~bv[] TIME-TRACKER-NOTE [:TAU]: For the proof above, the total runtime spent in the tau system was 30.01 seconds. See :DOC time-tracker-tau. ~ev[] Both of these messages are intended to let you know that certain prover heuristics (~pl[tau-system]) may be slowing proofs down more than they are helping. If you are satisfied with the prover's performance, you may ignore these messages or even turn them off by disabling time-tracking entirely (~pl[time-tracker]). Otherwise, here are some actions that you can take to solve such a performance problem. The simplest solution is to disable the tau-system, in either of the following equivalent ways. ~bv[] (in-theory (disable (:executable-counterpart tau-system))) (in-theory (disable (tau-system))) ~ev[] But if you want to leave the tau-system enabled, you could investigate the possibility is that the tau-system is causing an expensive ~c[:]~ilc[logic]-mode function to be executed. You can diagnose that problem by observing the rewriter ~-[] ~pl[dmr] ~-[] or by interrupting the system and getting a backtrace (~pl[set-debugger-enable]). A solution in that case is to disable the executable-counterpart of that function, for example in either of these equivalent ways. ~bv[] (in-theory (disable (:executable-counterpart foo))) (in-theory (disable (foo))) ~ev[] As a result, the tau-system will be weakened, but perhaps only negligibly. In either case above, you may prefer to provide ~c[:]~ilc[in-theory] hints rather than ~c[:in-theory] ~il[events]; ~pl[hints]. A more sophisticated solution is to record values of the ~c[:]~ilc[logic]-mode function in question, so that the tau-system will look up the necessary values rather than calling the function, whether or not the executable-counterpart of that function is enabled. Here is an example of a lemma that can provide such a solution. ~l[tau-system]. ~bv[] (defthm lemma (and (foo 0) (foo 17) (foo t) (not (foo '(a b c)))) :rule-classes :tau-system) ~ev[]~/~/") #-acl2-loop-only (defg *inside-absstobj-update* #(0)) (defun set-absstobj-debug-fn (val always) (declare (xargs :guard t)) #+acl2-loop-only (declare (ignore always)) #-acl2-loop-only (let ((temp (svref *inside-absstobj-update* 0))) (cond ((or (null temp) (eql temp 0) (and always (or (ttag (w *the-live-state*)) (er hard 'set-absstobj-debug "It is illegal to supply a non-nil value for ~ keyword :always, for set-absstobj-debug, unless ~ there is an active trust tag.")))) (setf (aref *inside-absstobj-update* 0) (cond ((eq val :reset) (if (natp temp) 0 nil)) (val nil) (t 0)))) (t (er hard 'set-absstobj-debug "It is illegal to call set-absstobj-debug in a context where ~ an abstract stobj invariance violation has already occurred ~ but not yet been processed. You can overcome this ~ restriction either by waiting for the top-level prompt, or ~ by evaluating the following form: ~x0." `(set-abbstobj-debug ,(if (member-eq val '(nil :reset)) nil t) :always t))))) val) (defmacro set-absstobj-debug (val &key (event-p 't) always on-skip-proofs) ; Here is a book that was certifiable in ACL2 Version_5.0, obtained from Sol ; Swords (shown here with only very trivial changes). It explains why we need ; the :protect keyword for defabsstobj, as explained in :doc note-6-0. ; Community book books/misc/defabsstobj-example-4.lisp is based on this ; example, but focuses on invariance violation and avoids the work Sol did to ; get a proof of nil. ; (in-package "ACL2") ; ; (defstobj const-stobj$c (const-fld$c :type bit :initially 0)) ; ; (defstub stop () nil) ; ; ;; Logically preserves the field value as 0, but actually leaves it as 1 ; (defun change-fld$c (const-stobj$c) ; (declare (xargs :stobjs const-stobj$c)) ; (let ((const-stobj$c (update-const-fld$c 1 const-stobj$c))) ; (prog2$ (stop) ; (update-const-fld$c 0 const-stobj$c)))) ; ; (defun get-fld$c (const-stobj$c) ; (declare (xargs :stobjs const-stobj$c)) ; (const-fld$c const-stobj$c)) ; ; (defun const-stobj$ap (const-stobj$a) ; (declare (xargs :guard t)) ; (equal const-stobj$a 0)) ; ; (defun change-fld$a (const-stobj$a) ; (declare (xargs :guard t) ; (ignore const-stobj$a)) ; 0) ; ; ;; Logically returns 0, exec version returns the field value which should ; ;; always be 0... ; (defun get-fld$a (const-stobj$a) ; (declare (xargs :guard t) ; (ignore const-stobj$a)) ; 0) ; ; (defun create-const-stobj$a () ; (declare (xargs :guard t)) ; 0) ; ; (defun-nx const-stobj-corr (const-stobj$c const-stobj$a) ; (and (equal const-stobj$a 0) (equal const-stobj$c '(0)))) ; ; (in-theory (disable (const-stobj-corr) ; (change-fld$c))) ; ; (DEFTHM CREATE-CONST-STOBJ{CORRESPONDENCE} ; (CONST-STOBJ-CORR (CREATE-CONST-STOBJ$C) ; (CREATE-CONST-STOBJ$A)) ; :RULE-CLASSES NIL) ; ; (DEFTHM CREATE-CONST-STOBJ{PRESERVED} ; (CONST-STOBJ$AP (CREATE-CONST-STOBJ$A)) ; :RULE-CLASSES NIL) ; ; (DEFTHM GET-FLD{CORRESPONDENCE} ; (IMPLIES (CONST-STOBJ-CORR CONST-STOBJ$C CONST-STOBJ) ; (EQUAL (GET-FLD$C CONST-STOBJ$C) ; (GET-FLD$A CONST-STOBJ))) ; :RULE-CLASSES NIL) ; ; (DEFTHM CHANGE-FLD{CORRESPONDENCE} ; (IMPLIES (CONST-STOBJ-CORR CONST-STOBJ$C CONST-STOBJ) ; (CONST-STOBJ-CORR (CHANGE-FLD$C CONST-STOBJ$C) ; (CHANGE-FLD$A CONST-STOBJ))) ; :RULE-CLASSES NIL) ; ; (DEFTHM CHANGE-FLD{PRESERVED} ; (IMPLIES (CONST-STOBJ$AP CONST-STOBJ) ; (CONST-STOBJ$AP (CHANGE-FLD$A CONST-STOBJ))) ; :RULE-CLASSES NIL) ; ; (defabsstobj const-stobj ; :concrete const-stobj$c ; :recognizer (const-stobjp :logic const-stobj$ap :exec const-stobj$cp) ; :creator (create-const-stobj :logic create-const-stobj$a :exec ; create-const-stobj$c) ; :corr-fn const-stobj-corr ; :exports ((get-fld :logic get-fld$a :exec get-fld$c) ; (change-fld :logic change-fld$a :exec change-fld$c ; ;; new ; ;; :protect t ; ))) ; ; ;; Causes an error and leaves the stobj in an inconsistent state (field ; ;; is 1) ; (make-event ; (mv-let ; (erp val state) ; (trans-eval '(change-fld const-stobj) 'top state t) ; (declare (ignore erp val)) ; (value '(value-triple nil)))) ; ; (defevaluator my-ev my-ev-lst ((if a b c))) ; ; (defun my-clause-proc (clause hint const-stobj) ; (declare (xargs :stobjs const-stobj ; :guard t) ; (ignore hint)) ; (if (= 0 (get-fld const-stobj)) ;; always true by defn. of get-fld ; (mv nil (list clause)) ; (mv nil nil))) ;; unsound if this branch is taken ; ; (defthm my-clause-proc-correct ; (implies (and (pseudo-term-listp clause) ; (alistp a) ; (my-ev (conjoin-clauses ; (clauses-result ; (my-clause-proc clause hint const-stobj))) ; a)) ; (my-ev (disjoin clause) a)) ; :rule-classes :clause-processor) ; ; (defthm foo nil :hints (("goal" :clause-processor ; (my-clause-proc clause nil const-stobj))) ; :rule-classes nil) ":Doc-Section switches-parameters-and-modes obtain debugging information upon atomicity violation for an abstract stobj~/ This ~il[documentation] topic assumes familiarity with abstract stobjs. ~l[defabsstobj]. Below we explain what is meant by an error message such as the following. ~bv[] ACL2 Error in CHK-ABSSTOBJ-INVARIANTS: Possible invariance violation for an abstract stobj! See :DOC set-absstobj-debug, and PROCEED AT YOUR OWN RISK. ~ev[] The use of ~c[(set-absstobj-debug t)] will make this error message more informative, as follows, at the cost of slower execution ~-[] but in practice, the slowdown may be negligible (more on that below). ~bv[] ACL2 Error in CHK-ABSSTOBJ-INVARIANTS: Possible invariance violation for an abstract stobj! See :DOC set-absstobj-debug, and PROCEED AT YOUR OWN RISK. Evaluation was aborted under a call of abstract stobj export UPDATE-FLD-NIL-BAD. ~ev[] You may be best off starting a new ACL2 session if you see one of the errors above. But you can continue at your own risk. With a trust tag (~pl[defttag]), you can even fool ACL2 into thinking nothing is wrong, and perhaps you can fix up the abstract stobj so that indeed, nothing really is wrong. See the community book ~c[books/misc/defabsstobj-example-4.lisp] for how to do that. That book also documents the ~c[:always] keyword and a special value for the first argument, ~c[:RESET]. ~bv[] Examples: (set-absstobj-debug t) ; obtain extra debug info, as above (set-absstobj-debug t :event-p t) ; same as above (set-absstobj-debug t :on-skip-proofs t) ; as above, but even in include-book (set-absstobj-debug t :event-p nil) ; returns one value, not error triple (set-absstobj-debug nil) ; avoid extra debug info (default)~/ General Form: (set-absstobj-debug val :event-p event-p ; default t :always always ; default nil :on-skip-proofs on-skip-proofs ; default nil ) ~ev[] where the keyword arguments are optional with defaults as indicated above, and all supplied arguments are evaluated except for ~c[on-skip-proofsp], which must be Boolean (if supplied). Keyword arguments are discussed at the end of this topic. Recall (~pl[defabsstobj]) that for any exported function whose ~c[:EXEC] function might (according to ACL2's heuristics) modify the concrete stobj non-atomically, one must specify ~c[:PROTECT t]. This results in extra code generated for the exported function, which provides a check that atomicity was not actually violated by a call of the exported function. The extra code might slow down execution, but perhaps only negligibly in typical cases. If you can tolerate a bit extra slow-down, then evaluate the form ~c[(set-absstobj-debug t)]. Subsequent such errors will provide additional information, as in the example displayed earlier in this documentation topic. Finally we document the keyword arguments, other than ~c[:ALWAYS], which is discussed in a book as mentioned above. When the value of ~c[:EVENT-P] is true, which it is by default, the call of ~c[set-absstobj-debug] will expand to an event. That event is a call of ~ilc[value-triple]. In that case, ~c[:ON-SKIP-PROOFS] is passed to that call so that ~c[set-absstobj-debug] has an effect even when proofs are being skipped, as during ~ilc[include-book]. That behavior is the default; that is, ~c[:ON-SKIP-PROOFS] is ~c[nil] by default. Also ~pl[value-triple]. The value of keyword ~c[:ON-SKIP-PROOFS] must always be either ~c[t] or ~c[nil], but other than that, it is ignored when ~c[EVENT-P] is ~c[nil].~/" (declare (xargs :guard ; We provide this guard as a courtesy: since on-skip-proofs is not evaluated, a ; non-nil form that evaluates to nil (such as 'nil) would otherwise be passed ; without evaluation and hence treated as being true. (booleanp on-skip-proofs))) (let ((form `(set-absstobj-debug-fn ,val ,always))) (cond (event-p `(value-triple ,form :on-skip-proofs ,on-skip-proofs)) (t form)))) ; The following functions are defined in logic mode because they will be ; used in tau bounder correctness theorems. We basically define two functions, ; intervalp and in-intervalp, but we also define various subroutines needed to ; make those functions manageable. In tau.lisp we define the record structure: ; (defrec tau-interval (domain (lo-rel . lo) . (hi-rel . hi)) t) ; and this is precisely the structure recognized by intervalp and given meaning ; by in-intervalp. We therefore achieve the goal that the user can prove ; theorems about bounder functions defined in terms of the concepts named here ; and we can run those functions on the actual tau-intervals constructed by the ; tau system. (Of course, those actual intervals could have been constructed ; and accessed by these functions rather than the more efficient record ; expressions, but efficiency matters.) ; In the guard below, we know when both x and y are non-nil then (at least) one ; is a rational. Under that guard, the body below is actually equivalent to the ; more elegant: ; (if (or (null x) ; (null y)) ; t ; (if rel (< x y) (<= x y))) ; except the body is guard-verifiable while the elegant one is not, since the ; guard for < (and <=) requires that both arguments be rationals. This is ; proved by the thm following the definition. (defun = result (i-max)) -1) (t (the (signed-byte 28) result))))))))) (defmacro imin (x y) `(the (signed-byte 28) (let ((x ,x) (y ,y)) (declare (type (signed-byte 28) x y)) (cond ((< x 0) (cond ((< y 0) -1) (t y))) ((< y 0) x) (t (the (signed-byte 28) (min x y))))))) (defun big-test (x y z) (declare (type (signed-byte 28) x y z)) (imin (i+ x y) (i+ y (imin x z)))) ~ev[] ~/") ; Essay on Wormholes ; Once upon a time (Version 3.6 and earlier) the wormhole function had a ; pseudo-flg argument which allowed the user a quick way to determine whether ; it was appropriate to incur the expense of going into the wormhole. The idea ; was that the form could have one a free var in it, wormhole-output, and that ; when it was evaluated in raw Lisp that variable was bound to the last value ; returned by the wormhole. Since wormhole always returned nil anyway, this ; screwy semantics didn't matter. However, it was implemented in such a way ; that a poorly constructed pseudo-flg could survive guard verification and yet ; cause a hard error at runtime because during guard verification ; wormhole-output was bound to NIL but in actual evaluation it was entirely ; under the control of the wormhole forms. ; To fix this we have introduced wormhole-eval. It takes two important ; arguments, the name of the wormhole and a lambda expression. Both must be ; quoted. The lambda may have at most one argument but the body may contain ; any variables available in the environment of the wormhole-eval call. (A ; third argument to wormhole-eval is an arbitrary form that uses all the free ; vars of the lambda, thus insuring that translate will cause an error if the ; lambda uses variables unavailble in the context.) The body of the lambda ; must be a single-valued, non-state, non-stobj term. ; The idea is that the lambda expression is applied to the last value of the ; wormhole output and its value is assigned as the last value of the wormhole ; output. Wormhole-eval always returns nil. Translation of a wormhole-eval ; call enforces these restrictions. Furthermore, it translates the body of the ; lambda (even though the lambda is quoted). This is irrelevant since the ; wormhole-eval returns nil regardless of the lambda expression supplied. ; Similarly, translation computes an appropriate third argument to use all the ; free vars, so the user may just write nil there and a suitable form is ; inserted by translate. ; We arrange for wormhole-eval to be a macro in raw lisp that really does what ; is said above. ; To make it bullet-proof, when we generate guard clauses we go inside the ; lambda, generating a new variable symbol to use in place of the lambda formal ; denoting the last value of the wormhole output. Thus, if guard clauses can be ; verified, it doesn't matter what the wormhole actually returns as its value. ; Ev-rec, the interpreter for terms, treats wormhole-eval specially in the ; expected way, as does oneify. Thus, both interpreted and compiled calls of ; wormhole-eval are handled, and guard violations are handled politely. ; Now, how does this allow us to fix the wormhole pseudo-flg problem? ; The hidden global variable in Lisp used to record the status of the various ; wormholes is called *wormhole-status-alist*. The entry in this alist for ; a particular wormhole will be called the wormhole's ``status.'' The lambda ; expression in wormhole-eval maps the wormhole's status to a new status. ; The status of a wormhole is supposed to be a cons whose car is either :ENTER ; or :SKIP. However, in the absence of verifying the guards on the code inside ; wormholes and in light of the fact that users can set the status by ; manipulating wormhole-status in the wormhole it is hard to insure that the ; status is always as supposed. So we code rather defensively. ; When the ``function'' wormhole is called it may or may not actually enter a ; wormhole. ``Entering'' the wormhole means invoking the form on the given ; input, inside a side-effects undoing call of ld. That, in turn, involves ; setting up the ld specials and then reading, translating, and evaluating ; forms. Upon exit, cleanup must be done. So entering is expensive. ; Whether it enters the wormhole or not depends on the wormhole's status, and ; in particular it depends on what we call the wormhole's ``entry code'' ; computed from the status as follows. ; If the wormhole's status statisfies wormhole-statusp then the situation is ; simple: wormhole enters the wormhole if the status is :ENTER and doesn't if ; the status is :SKIP. But we compute the entry code defensively: the entry ; code is :SKIP if and only if the wormhole's status is a cons whose car is ; :SKIP. Otherwise, the entry code is :ENTER. ; If we enter the wormhole, we take the wormhole input argument and stuff it ; into (@ wormhole-input), allowing the user to see it inside the ld code. We ; take the wormhole status and stuff it into (@ wormhole-status), allowing the ; user to see it and probably change it with (assign wormhole-status...). When ; we exit ld, we take (@ wormhole-status) and put it back into the hidden ; *wormhole-status-alist*. ; One subtlety arises: How to make wormholes re-entrant... The problem is that ; sometimes the current status is in the hidden alist and other times it is in ; (@ wormhole-status). So when we try to enter a new wormhole from within a ; wormhole -- which always happens by calling wormhole-eval -- the first thing ; we do is stuff the current (@ wormhole-status) into the hidden ; *wormhole-status-alist*. This means that the lambda expression for the new ; entrance is applied, it is applied to the ``most recent'' value of the status ; of that particular wormhole. The natural undoing of wormhole effects ; implements the restoration of (@ wormhole-status) upon exit from the ; recursive wormhole. ; If we wanted to convert our system code to logic mode we would want to verify ; the guards of the lambda bodies and the wormhole-status after ld. See the ; comment in push-accp. Here is a proposal for how to do that. First, insist ; that wormhole names are symbols. Indeed, they must be one argument, ; guard-verified Boolean functions. The guard for a call of wormhole-eval on a ; wormhole named foo should include the conjunct (foo nil) to insure that the ; initial value of the status is acceptable. The guard on the body of (lambda ; (whs) body) should be extended to include the hypothesis that (foo whs) is ; true and that (foo whs) --> (foo body) is true. We should then change ; wormhole so that if it calls ld it tests foo at runtime after the ld returns ; so we know that the final status satisfies foo. If we do this we can safely ; assume that every status seen by a lambda body in wormhole-eval will satisfy ; the foo invariant. (defun wormhole-statusp (whs) ":Doc-Section Miscellaneous predicate recognizing well-formed wormhole status object~/ ~bv[] General Form: (wormhole-statusp whs) ~ev[] ~l[wormhole]. This predicate is useful in guards for wormholes. It checks whether ~c[whs] is either ~c[nil] or a cons whose car is ~c[:ENTER] or ~c[:SKIP].~/~/" (declare (xargs :mode :logic :guard t)) (or (equal whs nil) (and (consp whs) (or (eq (car whs) :ENTER) (eq (car whs) :SKIP))))) (defun wormhole-entry-code (whs) ; Keep this function in sync with the inline code in wormhole1. ":Doc-Section Miscellaneous determines the wormhole entry code from a wormhole status object~/ ~bv[] General Form: (wormhole-entry-code whs) ~ev[] ~l[wormhole]. Returns ~c[:ENTER] or ~c[:SKIP] given a well-formed wormhole status ~c[whs]. If ~c[whs] is ~c[nil] or not well-formed, the entry code is ~c[:ENTER].~/~/" (declare (xargs :mode :logic :guard t)) (if (and (consp whs) (eq (car whs) :SKIP)) :SKIP :ENTER)) (defun wormhole-data (whs) ":Doc-Section Miscellaneous determines the wormhole data object from a wormhole status object~/ ~bv[] General Form: (wormhole-data whs) ~ev[] ~l[wormhole]. Returns the wormhole data from a well-formed wormhole status ~c[whs]. If ~c[whs] is ~c[nil] or not well-formed, the data is ~c[nil].~/~/" (declare (xargs :mode :logic :guard t)) (if (consp whs) (cdr whs) nil)) (defun set-wormhole-entry-code (whs code) ":Doc-Section Miscellaneous sets the wormhole entry code in a wormhole status object~/ ~bv[] General Form: (set-wormhole-entry-code whs code) ~ev[] ~l[wormhole]. ~c[Whs] should be a well-formed wormhole status and ~c[code] should be ~c[:ENTER] or ~c[:SKIP]. This function returns a new status with the specified entry code but the same data as ~c[whs]. It avoids unnecessary consing if the entry code for ~c[whs] is already set to ~c[code]. This function does not affect state or a wormhole's hidden status. It just returns a (possibly) new status object suitable as the value of the ~c[lambda] expressions in ~ilc[wormhole-eval] and ~ilc[wormhole].~/~/" (declare (xargs :mode :logic :guard (or (eq code :ENTER) (eq code :SKIP)))) (if (consp whs) (if (eq (car whs) code) whs (cons code (cdr whs))) (if (eq code :enter) whs (cons :skip whs)))) (defun set-wormhole-data (whs data) ":Doc-Section Miscellaneous sets the wormhole data object in a wormhole status object~/ ~bv[] General Form: (set-wormhole-data whs data) ~ev[] ~l[wormhole]. ~c[Whs] should be a well-formed wormhole status; ~c[data] is arbitrary. This function returns a new status with the same entry code as ~c[whs] but with the new ~c[data]. It avoids unnecessary consing if the data for ~c[whs] is already set to ~c[data]. This function does not affect state or a wormhole's hidden status. It just returns a (possibly) new status object suitable as the value of the ~c[lambda] expressions in ~ilc[wormhole-eval] and ~ilc[wormhole].~/~/" (declare (xargs :mode :logic :guard t)) (if (consp whs) (if (equal (cdr whs) data) whs (cons (car whs) data)) (cons :enter data))) (defun make-wormhole-status (old-status new-code new-data) ":Doc-Section Miscellaneous creates a wormhole status object from given status, entry code, and data~/ ~bv[] General Form: (make-wormhole-status whs code data) ~ev[] ~l[wormhole]. ~c[Whs] should be a well-formed wormhole status, ~c[code] should be ~c[:ENTER] or ~c[:SKIP], and ~c[data] is arbitrary. This function returns a new status with the specified entry code and data, reusing ~c[whs] if it is appropriate.~/~/" (declare (xargs :mode :logic :guard (or (eq new-code :ENTER) (eq new-code :SKIP)))) (if (consp old-status) (if (and (eq new-code (car old-status)) (equal new-data (cdr old-status))) old-status (cons new-code new-data)) (cons new-code new-data))) ; (defthm wormhole-status-guarantees ; (if (or (eq code :enter) ; (eq code :skip)) ; (and (implies (wormhole-statusp whs) ; (wormhole-statusp (set-wormhole-entry-code whs code))) ; (implies (wormhole-statusp whs) ; (wormhole-statusp (set-wormhole-data whs data))) ; (equal (wormhole-entry-code (set-wormhole-entry-code whs code)) ; code) ; (equal (wormhole-data (set-wormhole-data whs data)) ; data) ; (implies (wormhole-statusp whs) ; (equal (wormhole-data (set-wormhole-entry-code whs code)) ; (wormhole-data whs))) ; (implies (wormhole-statusp whs) ; (equal (wormhole-entry-code ; (set-wormhole-data whs data)) ; (wormhole-entry-code whs))) ; (implies (wormhole-statusp whs) ; (wormhole-statusp (make-wormhole-status whs code data))) ; (equal (wormhole-entry-code (make-wormhole-status whs code data)) ; code) ; (equal (wormhole-data (make-wormhole-status whs code data)) ; data)) ; t) ; :rule-classes nil) ; ; (verify-guards wormhole-status-guarantees) ; In particular, given a legal code, set-wormhole-entry-code preserves ; wormhole-statusp and always returns an object with the given entry code ; (whether the status was well-formed or not). Furthermore, the guards on ; these functions are verified. Thus, they can be called safely even if the ; user has messed up our wormhole status. Of course, if the user has messed up ; the status, there is no guarantee about what happens inside the wormhole. (defun tree-occur-eq (x y) ; Does symbol x occur in the cons tree y? (declare (xargs :guard (symbolp x))) (cond ((consp y) (or (tree-occur-eq x (car y)) (tree-occur-eq x (cdr y)))) (t (eq x y)))) #+acl2-loop-only (defun wormhole-eval (qname qlambda free-vars) ; A typical call of this function is ; (wormhole-eval 'my-wormhole ; '(lambda (output) (p x y output)) ; (list x y)) ; And the pragmatic semantics is that the lambda expression is applied to the ; last output of the wormhole my-wormhole, the result of of the application is ; stuffed back in as the last output, and the function logically returns nil. ; Note that free vars in the lambda must listed. This is so that the free vars ; of this wormhole-eval expression consists of the free vars of the lambda, ; even though the lambda appears quoted. Translate automatically replaces the ; lambda expression constant by the translated version of that same constant, ; and it replaces the supposed list of free vars by the actual free vars. So ; in fact the user calling wormhole-eval can just put nil in the free-vars arg ; and let translate fill it in. Translate can mangle the arguments of ; wormhole-eval because it always returns nil, regardless of its arguments. ; The guard is declared below to be t but actually we compute the guard for the ; body of the quoted lambda, with some fiddling about the bound variable. ":Doc-Section Miscellaneous state-saving without state ~-[] a short-cut to a parallel universe~/ ~bv[] Example Form: (wormhole-eval 'demo '(lambda (whs) (set-wormhole-data whs (cons (cons name info) (wormhole-data whs)))) (prog2$ info name)) General Form: (wormhole-eval name lambda varterm) ~ev[] where ~c[name] must be a quoted wormhole name and ~c[lambda] must be a quoted ~c[lambda]-expression. The ~c[lambda]-expression must have at most one formal parameter but the body of the ~c[lambda]-expression may contain other variables. Note that in the example form given above, the ~c[lambda] has one formal, ~c[whs], and uses ~c[name] and ~c[info] freely. Note that the ~c[lambda] is quoted. The third argument of ~c[wormhole-eval], ~c[varterm], is an arbitrary term that should mention all of the free variables in the ~c[lambda]-expression. That term establishes your ``right'' to refer to those free variables in the environment in which the ~c[wormhole-eval] expression occurs. The value of ~c[varterm] is irrelevant and if you provide ~c[nil] ACL2 will automatically provide a suitable term, namely a ~c[prog2$] form like the one shown in the example above. Aside: Exception for ACL2(p) (~pl[parallelism]) to the irrelevance of ~c[varterm]. By default, calls of ~c[wormhole-eval] employ a lock, ~c[*wormhole-lock*]. To avoid such a lock, include the symbol ~c[:NO-WORMHOLE-LOCK] in ~c[varterm]; for example, you might replace a last argument of ~c[nil] in ~c[wormhole-eval] by ~c[:NO-WORMHOLE-LOCK]. End of Aside. ~l[wormhole] for a full explanation of wormholes. Most relevant here is that every wormhole has a name and a status. The status is generally a cons pair whose ~c[car] is the keyword ~c[:ENTER] or the keyword ~c[:SKIP] and whose ~c[cdr] is an arbitrary object used to store information from one wormhole call to the next. Here is a succinct summary of ~c[wormhole-eval]. If the ~c[lambda]-expression has a local variable, ~c[wormhole-eval] applies the ~c[lambda]-expression to the wormhole status of the named wormhole and remembers the value as the new wormhole status. If the ~c[lambda] has no formal parameter, the ~c[lambda] is applied to no arguments and the value is the new status. ~c[Wormhole-eval] returns ~c[nil]. Thus, the formal parameter of the ~c[lambda]-expression, if provided, denotes the wormhole's hidden status information; the value of the ~c[lambda] is the new status and is hidden away. The guard of a ~c[wormhole-eval] call is the guard of the body of the ~c[lambda]-expression, with a fresh variable symbol used in place of the formal so that no assumptions are possible about the hidden wormhole status. If the guard of a ~c[wormhole-eval] is verified, the call is macroexpanded inline to the evaluation of the body in a suitable environment. Thus, it can be a very fast way to access and change hidden state information, but the results must remain hidden. To do arbitrary computations on the hidden state (i.e., to access the ACL2 ~ilc[state] or logical ~il[world] or to interact with the user) ~pl[wormhole]. Functions that are probably useful in the body of the ~ilc[lambda] or the guard of a function using ~c[wormhole-eval] include the following: ~ilc[wormhole-statusp], ~ilc[wormhole-entry-code], ~ilc[wormhole-data], ~ilc[set-wormhole-entry-code], ~ilc[set-wormhole-data], and ~ilc[make-wormhole-status]. ~l[wormhole] for a series of example uses of ~c[wormhole-eval] and ~c[wormhole]. For a behind-the-scenes description of how wormholes work, ~l[wormhole-implementation].~/~/" (declare (xargs :mode :logic :guard t) (ignore qname qlambda free-vars)) nil) (deflock *wormhole-lock*) #-acl2-loop-only (defmacro wormhole-eval (qname qlambda free-vars) (declare (xargs :guard t)) ; All calls of wormhole-eval that have survived translation are of a special ; form. Qname is a quoted object (used as the name of a wormhole), and qlambda ; is of one of the two forms: ; (i) (quote (lambda (whs) body)), or ; (ii) (quote (lambda () body)) ; where whs (``wormhole status'') is a legal variable symbol, body is a fully ; translated term that may involve whs and other variables which returns one ; result. We furthermore know that the free vars in the lambda are the free ; vars of the term free-vars, which is typically just a list-expression of ; variable names supplied by translate. Finally, we know that whs appears as ; the lambda formal iff it is used in body. ; Wormholes may have arbitrary objects for names, so qname is not necessarily a ; quoted symbol. This may be the first entry into the wormhole of that name, ; in which case the most recent output of the wormhole is understood to be nil. ; Logically this function always returns nil. Actually, it applies the lambda ; expression to either (i) ``the most recent output'' of the named wormhole or ; (ii) no arguments, appropriately, and stores the result as the most recent ; output, and then returns nil. (let* ((whs (if (car (cadr (cadr qlambda))) (car (cadr (cadr qlambda))) ; Case (i) (gensym))) ; Case (ii) (val (gensym)) (form ; The code we lay down is the same in both cases, because we use the variable whs to ; store the old value of the status to see whether it has changed. But we have ; to generate a name if one isn't supplied. `(progn (cond (*wormholep* (setq *wormhole-status-alist* (put-assoc-equal (f-get-global 'wormhole-name *the-live-state*) (f-get-global 'wormhole-status *the-live-state*) *wormhole-status-alist*)))) (let* ((*wormholep* t) (,whs (cdr (assoc-equal ,qname *wormhole-status-alist*))) (,val ,(caddr (cadr qlambda)))) (or (equal ,whs ,val) (setq *wormhole-status-alist* (put-assoc-equal ,qname ,val *wormhole-status-alist*))) nil)))) (cond ((tree-occur-eq :no-wormhole-lock free-vars) form) (t `(with-wormhole-lock ,form))))) (defmacro wormhole (name entry-lambda input form &key (current-package 'same current-packagep) (ld-skip-proofsp 'same ld-skip-proofspp) (ld-redefinition-action 'save ld-redefinition-actionp) (ld-prompt ''wormhole-prompt) (ld-missing-input-ok 'same ld-missing-input-okp) (ld-pre-eval-filter 'same ld-pre-eval-filterp) (ld-pre-eval-print 'same ld-pre-eval-printp) (ld-post-eval-print 'same ld-post-eval-printp) (ld-evisc-tuple 'same ld-evisc-tuplep) (ld-error-triples 'same ld-error-triplesp) (ld-error-action 'same ld-error-actionp) (ld-query-control-alist 'same ld-query-control-alistp) (ld-verbose 'same ld-verbosep)) ":Doc-Section Miscellaneous ~ilc[ld] without ~ilc[state] ~-[] a short-cut to a parallel universe~/ ~bv[] Example Form: ; The following form enters a recursive read-eval-print loop on a ; copy of the current state, allowing you to interact with that loop. ; Note that the form does not mention the ACL2 state variable! ; Evaluate the form below. Inside the resulting loop, define some function, ; e.g., with ~c[(defun foo (x) x)]. Then exit with ~c[:q] and observe, ; e.g., with ~c[:pe foo], that the external state did not change. (wormhole 'foo '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) nil '(list 'hello 'there)) ~ev[] ~/ ~bv[] General Form: (wormhole name entry-lambda input form :current-package ... ; known package name :ld-skip-proofsp ... ; nil, t or 'include-book :ld-redefinition-action ; nil or '(:a . :b) :ld-prompt ... ; nil, t, or some prompt printer fn :ld-missing-input-ok ... ; nil, t, :warn, or warning message :ld-pre-eval-filter ... ; :all, :query, or some new name :ld-pre-eval-print ... ; nil, t, or :never :ld-post-eval-print ... ; nil, t, or :command-conventions :ld-evisc-tuple ... ; nil or '(alist level length hiding-cars) :ld-error-triples ... ; nil or t :ld-error-action ... ; :return!, :return, :continue, or :error :ld-query-control-alist ; alist supplying default responses :ld-verbose ...) ; nil or t ~ev[] The keyword arguments above are exactly those of ~ilc[ld] (~pl[ld]) except that three of ~ilc[ld]'s keyword arguments are missing: the three that specify the channels ~ilc[standard-oi], ~ilc[standard-co] and ~ilc[proofs-co], which default in ~c[wormhole] to ACL2's comment window. There are two ways to create and enter a wormhole: ~c[wormhole] as described here and the simpler ~ilc[wormhole-eval]. We recommend you read this full account of wormholes before using ~c[wormhole-eval]. Ignoring the use of ~c[entry-lambda], ~c[wormhole] manufactures a named ``wormhole ~il[state]'' and calls the general-purpose ACL2 read-eval-print loop ~ilc[ld] on it. However, when ~c[ld] exits, the wormhole evaporates and the function ~c[wormhole] returns ~c[nil]. The manufactured state is like the ``current'' ACL2 ~il[state] except for two things. First, some information from the last wormhole state of this name is transferred into the new state; this allows a wormhole to maintain some state from one call to the next. Second, some information from the wormhole call itself is transferred into the new state; this allows the wormhole to be sensitive to context. These two changes to the current state are reflected in the settings ~c[(@ wormhole-status)] and ~c[(@ wormhole-input)] discussed in detail below. Note that ~c[wormhole] may be called from environments in which ~ilc[state] is not bound. It is still applicative because it always returns ~c[nil]. There are some restrictions about what can be done inside a wormhole. As you may imagine, we really do not ``copy the current state'' but rather just keep track of how we modified it and undo those modifications upon exit. An error is signalled if you try to modify state in an unsupported way. For this same reason, wormholes do not allow updating of any user-defined single-threaded objects. ~l[stobj]. One example wormhole is the implementation of the ACL2 ~ilc[accumulated-persistence] facility for tracking the frequency with which rules are tried. To implement this feature directly the theorem prover would have to take the tracking data as an argument and pass it around so that updates could be accumulated. This would greatly clutter the code. Instead, the tracking data is maintained in a wormhole. The theorem prover enters the wormhole to update the data as rules are tried. When you request a display of the data, ~ilc[show-accumulated-persistence] enters the wormhole and prints the data. But the data is never available outside that wormhole. The ACL2 system uses a second wormhole to implement the ~ilc[brr] facility, allowing the user to interact with the rewriter as rules are applied. We now specify the arguments and behavior of ~c[wormhole]. The ~c[name] argument must be a quoted constant and is typically a symbol. It will be the ``name'' of the wormhole. A wormhole of that name will be created the first time either ~c[wormhole] or ~ilc[wormhole-eval] is called. Every wormhole name has a ``status.'' The status of a wormhole is stored outside of ACL2; it is inaccessible to the ACL2 user except when in the named wormhole. But the status of a wormhole may be set by the user from within the wormhole. Upon the first call of ~c[wormhole] or ~c[wormhole-eval] on a name, the status of that name is ~c[nil]. But in general you should arrange for the status to be a cons. The status is set by the quoted ~c[lambda] every time ~c[wormhole] is called; but it may also be set in the ~c[form] argument (the first form evaluated in the interactive loop) by assigning to the state global variable ~c[wormhole-status], as with ~bv[] (assign wormhole-status ...) ~ev[] or even by the user interacting with the loop if you do not exit the loop with the first form. The ~c[car] of the cons should be either ~c[:ENTER] or ~c[:SKIP] and is called the wormhole's ``entry code.'' The entry code of ~c[nil] or an unexpectedly shaped status is ~c[:ENTER]. The ~c[cdr] of the cons is arbitrary data maintained by you. When ~c[wormhole] is invoked, the status of the specified name is incorporated into the manufactured wormhole state. In particular, inside the wormhole, the status is the value of the state global variable ~c[wormhole-status]. That is, inside the wormhole, the status may be accessed by ~c[(@ wormhole-status)] and set by ~c[(assign wormhole-status ...)], ~c[f-get-global] and ~c[f-put-global]. When ~c[ld] exits -- typically because the form ~c[:q] was read by ~c[ld] -- the then-current value of wormhole-status is hidden away so that it can be restored when this wormhole is entered again. The rest of the wormhole state is lost. This allows a sequence of entries and exits to a wormhole to maintain some history in the status and this information can be manipulated by ACL2 functions executing inside the wormhole. The second argument to ~c[wormhole] must be a quoted lambda expression. We explain it later. The third argument, ~c[input], may be any term. The value of the term is passed into the manufactured wormhole state, allowing you to pass in information about the calling context. Inside the wormhole, the ~c[input] is available via ~c[(@ wormhole-input)]. It could be reassigned via ~c[(assign wormhole-input ...)], but there is no reason to do that. The fourth argument, ~c[form], may be any term; when ~ilc[ld] is called on the manufactured wormhole state, the first form evaluated by ~c[ld] will be the value of ~c[form]. Note that ~c[form] will be translated by ~c[ld]. Errors, including guard violations, in the translation or execution of that first form will leave you in the interactive loop of the wormhole state. When used properly, the first form allows you to greet your user before reading the first interactive command or simply to do whatever computation you want to do inside the wormhole and exit silently. We give examples below. Manufacturing a wormhole state is relatively expensive; in addition, the forms executed by ~c[ld] must be read, translated, and interpreted as with any user type-in. The ~c[entry-lambda] offers a way to avoid this or, at least, to decide whether to incur that expense. Before the wormhole state is manufactured and entered, the ~c[entry-lambda] is applied to the current wormhole status with ~ilc[wormhole-eval]. That ~c[lambda] application must produce a new wormhole status, which is stored as the wormhole's status. The entry code for the new status determines whether ~c[wormhole] actually manufactures a wormhole state and calls ~c[ld]. If the entry code for that new status is ~c[:ENTER] the wormhole state is manufactured and entered; otherwise, the new status is simply saved as the most recent status but the wormhole state is not manufactured or entered. Note therefore that the ~c[entry-lambda] may be used to perform two functions: (a) to determine if it is really necessary to manufacture a state and (b) to update the data in the wormhole status as a function of the old status without invoking ~c[ld]. The ~c[entry-lambda] must be a quoted lambda expression of at most one argument. Thus, the argument must be either ~bv[] '(lambda (whs) ) ~ev[] or ~bv[] '(lambda () ) ~ev[] Note the quote. If a formal, e.g., ~c[whs], is provided, it must be used as a variable in the ~c[lambda] body. The ~c[lambda]-expression may contain free variables, that is, the body may mention variables other than the ~c[lambda] formal. These free variables are understood in the caller's environment. These conventions allow us to compile the ~c[entry-lambda] application very efficiently when the guard has been verified. The guard on a call of ~c[wormhole] is the conjunction of the guards on the arguments conjoined with the guard on the body of the ~c[entry-lambda]. ~l[wormhole-eval] for a discussion of the guard on the ~c[lambda]-expression. The functions ~ilc[wormhole-statusp], ~ilc[wormhole-entry-code], ~ilc[wormhole-data], ~ilc[set-wormhole-entry-code], ~ilc[set-wormhole-data], and ~ilc[make-wormhole-status] may be useful in manipulating entry codes and data in the ~c[entry-lambda]. Note that you access and manipulate the wormhole's status in two different ways depending on whether you're ``outside'' of the wormhole applying the quoted ~c[lambda] or ``inside'' the read-eval-print loop of the wormhole. OUTSIDE (~c[wormhole-eval]): access via the value of the ~c[lambda] formal and set by returning the new status as the value of the ~c[lambda] body. INSIDE (~c[ld] phase of ~c[wormhole]): access via ~c[(@ wormhole-status)], and set via ~c[(assign wormhole-status ...)]. Pragmatic Advice on Designing a Wormhole: Suppose you are using wormholes to implement some extra-logical utility. You must contemplate how you will use your wormhole's status to store hidden information. You might be tempted to exploit the entry code as part of the status. For example, you may think of ~c[:ENTER] as indicating that your utility is ``turned on'' and ~c[:SKIP] as indicating that your utility is ``turned off.'' We advise against such a design. We recommend you base your decisions on the wormhole data. We recommend that you set but not read the wormhole entry code to signal whether you wish to enter a full-fledged wormhole. To use the entry code as a flag overloads it and invites confusion when your facility is ``turned off'' but you have to enter the wormhole for some reason. For a behind-the-scenes description of how wormholes work, ~l[wormhole-implementation]. Here are some sample situations handled by ~c[wormhole-eval] and ~c[wormhole]. Let the wormhole in question be named ~c[DEMO]. Initially its status is ~c[NIL]. The functions below all maintain the convention that the status is either ~c[nil] or of the form ~c[(:key . lst)], where ~c[:key] is either ~c[:SKIP] or ~c[:ENTER] and ~c[lst] is a true-list of arbitrary objects. But since there is no way to prevent the user from entering the ~c[DEMO] wormhole interactively and doing something to the status, this convention cannot be enforced. Thus, the functions below do what we say they do, e.g., remember all the values of ~c[x] ever seen, only if they're the only functions messing with the ~c[DEMO] status. On the other hand, the guards of all the functions below can be verified. We have explicitly declared that the guards on the functions below are to be verified, to confirm that they can be. Guard verification is optional but wormholes (and ~c[wormhole-eval] in particular) are more efficient when guards have been verified. All of the functions defined below return ~c[nil]. The examples below build on each other. If you really want to understand wormholes we recommend that you evaluate each of the forms below, in the order they are discussed. ~b[Q.] How do I create a wormhole that prints its status to the comment window? ~bv[] (defun demo-status () (declare (xargs :verify-guards t)) (wormhole-eval 'demo '(lambda (whs) (prog2$ (cw \"DEMO status:~~%~~x0~~%\" whs) whs)) nil)) ~ev[] Note above that after printing the status to the comment window we return the new (unchanged) status ~c[whs]. Had we just written the call of ~c[cw], which returns ~c[nil], the function would print the status and then set it to ~c[nil]! ~b[Q.] How do I use a wormhole to collect every symbol, ~c[x], passed to the function? ~bv[] (defun demo-collect (x) (declare (xargs :verify-guards t)) (wormhole-eval 'demo '(lambda (whs) (make-wormhole-status whs (wormhole-entry-code whs) (if (symbolp x) (cons x (wormhole-data whs)) (wormhole-data whs)))) nil)) ~ev[] We could have also defined this function this way: ~bv[] (defun demo-collect (x) (declare (xargs :verify-guards t)) (if (symbolp x) (wormhole-eval 'demo '(lambda (whs) (set-wormhole-data whs (cons x (wormhole-data whs)))) nil) nil)) ~ev[] Both versions always return ~c[nil] and both versions collect into the wormhole data field just the symbols ~c[x] upon which ~c[demo-collect] is called. ~b[Q.] How do I use ~c[demo-collect]? Below is a function that maps over a list and computes its length. But it has been annotated with a call to ~c[demo-collect] on every element. ~bv[] (defun my-len (lst) (if (endp lst) 0 (+ 1 (prog2$ (demo-collect (car lst)) (my-len (cdr lst)))))) ~ev[] Thus, for example: ~bv[] ACL2 !>(my-len '(4 temp car \"Hi\" rfix)) 5 ACL2 !>(demo-status) DEMO status: (:ENTER RFIX CAR TEMP) NIL ACL2 !> ~ev[] ~b[Q.] How do I set the entry code to ~c[:ENTER] or ~c[:SKIP] according to whether ~c[name] is a ~c[member-equal] of the list of things seen so far? Note that we cannot check this condition outside the wormhole, because it depends on the list of things collected so far. We make the decision inside the ~c[lambda]-expression. Note that we explicitly check that the guard of ~c[member-equal] is satisfied by the current wormhole status, since we cannot rely on the invariant that no other function interferes with the status of the ~c[DEMO] wormhole. In the case that the status is ``unexpected'' we act like the status is ~c[nil] and set it to ~c[(:SKIP . NIL)]. ~bv[] (defun demo-set-entry-code (name) (declare (xargs :verify-guards t)) (wormhole-eval 'demo '(lambda (whs) (if (true-listp (wormhole-data whs)) (set-wormhole-entry-code whs (if (member-equal name (wormhole-data whs)) :ENTER :SKIP)) '(:SKIP . NIL))) nil)) ~ev[] Thus ~bv[] ACL2 !>(demo-set-entry-code 'monday) NIL ACL2 !>(demo-status) DEMO status: (:SKIP RFIX CAR TEMP) NIL ACL2 !>(demo-set-entry-code 'rfix) NIL ACL2 !>(demo-status) DEMO status: (:ENTER RFIX CAR TEMP) NIL ACL2 !> ~ev[] ~b[Q.] Suppose I want to collect every symbol and then, if the symbol has an ~c[ABSOLUTE-EVENT-NUMBER] property in the ACL2 logical world, print the defining event with ~c[:pe] and then enter an interactive loop; but if the symbol does not have an ~c[ABSOLUTE-EVENT-NUMBER], don't print anything and don't enter an interactive loop. Here it is not important to know what ~c[ABSOLUTE-EVENT-NUMBER] is; this example just shows that we can use a wormhole to access the ACL2 logical world, even in a function that does not take the state as an argument. In the code below, we use ~c[wormhole] instead of ~c[wormhole-eval], because we might have to access the logical world and enter an interactive loop. But for efficiency we do as much as we can inside the entry ~c[lambda], where we can check whether ~c[x] is symbol and collect it into the data field of the wormhole status. Note that if we collect ~c[x], we also set the entry code to ~c[:ENTER]. If we don't collect ~c[x], we set the entry code to ~c[:SKIP]. ~bv[] (defun collect-symbols-and-print-events (x) (declare (xargs :guard t)) (wormhole 'demo '(lambda (whs) (if (symbolp x) (make-wormhole-status whs :ENTER (cons x (wormhole-data whs))) (set-wormhole-entry-code whs :SKIP))) ; The wormhole will not get past here is unless the entry code is ; :ENTER. If we get past here, we manufacture a state, put ; x into ~c[(@ wormhole-input)] and call ld in such a way that the ; first form executed is the quoted if-expression below. x '(if (getprop (@ wormhole-input) 'absolute-event-number nil 'CURRENT-ACL2-WORLD (w state)) (er-progn (mv-let (col state) (fmt \"~~%Entering a wormhole on the event name ~~x0~~%\" (list (cons #\\0 (@ wormhole-input))) *standard-co* state nil) (declare (ignore col)) (value nil)) (pe (@ wormhole-input)) (set-ld-prompt 'wormhole-prompt state) (value :invisible)) (value :q)) :ld-verbose nil :ld-prompt nil)) ~ev[] The ``first form'' (the ~c[if]) asks whether the ~c[wormhole-input] (i.e., ~c[x]) has an ~c[ABSOLUTE-EVENT-NUMBER] property. If so, it enters an ~ilc[er-progn] to perform a sequence of commands, each of which returns an ACL2 error triple (~pl[programming-with-state]). The first form uses ~ilc[fmt] to print a greeting. Since ~c[fmt] returns ~c[(mv col state)] and we must return an error triple, we embed the ~c[fmt] term in an ~c[(mv-let (col state) ... (value nil))]. The macro ~c[value] takes an object and returns a ``normal return'' error triple. The second form in the ~c[er-progn] uses the ACL2 history macro ~c[pe] (~pl[pe]) to print the defining event for a name. The third form sets the prompt of this read-eval-print loop to the standard function for printing the wormhole prompt. We silenced the printing of the prompt when we called ~c[ld], thanks to the ~c[:ld-prompt nil] keyword option. More on this below. The fourth form returns the error triple value ~c[:invisible] as the value of the first form. This prevents ~c[ld] from printing the value of the first form. Since we have not exited ~c[ld], that function just continues by reading the next form from the comment window. The user perceives this as entering a read-eval-print loop. We continue in the loop until the user types ~c[:q]. On the other branch of the ~c[if], if the symbol has no ~c[ABSOLUTE-EVENT-NUMBER] property, we execute the form ~c[(value :q)], which is the programming equivalent of typing ~c[:q]. That causes the ~c[ld] to exit. The ~c[ld] special variables set in the call to ~c[wormhole] and further manipulated inside the first form to ~c[ld] may require explanation. By setting ~c[:]~ilc[ld-verbose] to ~c[nil], we prevent ~c[ld] from printing the familiar ACL2 banner when ~c[ld] is called. If ~c[:ld-verbose nil] is deleted, then you would see something like ~bv[] ACL2 Version 4.0. Level 2. ... Type (good-bye) to quit completely out of ACL2. ~ev[] before the first form is read and evaluated. By setting ~c[:]~ilc[ld-prompt] to ~c[nil] we prevent ~c[ld] from printing the prompt before reading and evaluating the first form. As this example shows, to use full-blown wormholes you must understand the protocol for using wormhole status to control whether a wormhole state is manufactured for ~c[ld] and you must also understand programming with ~ilc[state] and the effects of the various ~ilc[ld] ``special variables.'' From the discussion above we see that wormholes can be used to create formatted output without passing in the ACL2 ~ilc[state]. For examples ~pl[cw], in particular the discussion at the end of that documentation topic.~/" `(with-wormhole-lock (prog2$ (wormhole-eval ,name ,entry-lambda ; It is probably harmless to allow a second lock under the one above, but there ; is no need, so we avoid it. :no-wormhole-lock) (wormhole1 ,name ,input ,form (list ,@(append (if current-packagep (list `(cons 'current-package ,current-package)) nil) (if ld-skip-proofspp (list `(cons 'ld-skip-proofsp ,ld-skip-proofsp)) nil) (if ld-redefinition-actionp (list `(cons 'ld-redefinition-action ,ld-redefinition-action)) nil) (list `(cons 'ld-prompt ,ld-prompt)) (if ld-missing-input-okp (list `(cons 'ld-missing-input-ok ,ld-missing-input-ok)) nil) (if ld-pre-eval-filterp (list `(cons 'ld-pre-eval-filter ,ld-pre-eval-filter)) nil) (if ld-pre-eval-printp (list `(cons 'ld-pre-eval-print ,ld-pre-eval-print)) nil) (if ld-post-eval-printp (list `(cons 'ld-post-eval-print ,ld-post-eval-print)) nil) (if ld-evisc-tuplep (list `(cons 'ld-evisc-tuple ,ld-evisc-tuple)) nil) (if ld-error-triplesp (list `(cons 'ld-error-triples ,ld-error-triples)) nil) (if ld-error-actionp (list `(cons 'ld-error-action ,ld-error-action)) nil) (if ld-query-control-alistp (list `(cons 'ld-query-control-alist ,ld-query-control-alist)) nil) (if ld-verbosep (list `(cons 'ld-verbose ,ld-verbose)) nil))))))) (deflabel wormhole-implementation :doc ":Doc-Section Miscellaneous notes on how wormholes are implemented~/ What happens when you call ~ilc[wormhole]? Recall that a typical call of the function looks like this: ~bv[] (wormhole 'name '(lambda (whs) ...) input form :ld-verbose ... ...) ~ev[] A brief recap of the advertised semantics for ~c[wormhole] establishes our terminology: When the above ~c[wormhole] is evaluated, the ~c[lambda]-expression is applied to the wormhole's status and the result is stored as the new status. Then, if the entry-code of the new status is ~c[:ENTER], ~ilc[ld] is invoked on a copy of the ``current state'' with the specified ~c[ld-] ``special variables;'' output is directed to the comment window. In that copy of the state, the state-global variable ~c[wormhole-input] is set to the value of ~c[input] and the state-global variable ~c[wormhole-status] is set to the (new) status computed by the ~c[lambda]-expression. Thus, inside the wormhole, ~c[(@ wormhole-input)] returns the list of inputs, ~c[(@ wormhole-status)] returns the current status, and ~c[(assign wormhole-status ...)] sets the wormhole's status. The first form executed by the ~c[ld] is the value of ~c[form] and unless that form returns ~c[(value :q)], causing the ~c[ld] to quit, the ~c[ld] proceeds to take subsequent input from the comment window. Upon exiting from ~c[ld], the wormhole state ``evaporates.'' The wormhole's status upon exit is remembered and restored the next time the wormhole is entered. Here is what really happens. Each wormhole's status is recorded in an alist stored in a Common Lisp global variable named ~c[*wormhole-status-alist*]. This variable is not part of the ACL2 state. If you exit the ACL2 loop with ~c[:q] you can inspect the value of ~c[*wormhole-status-alist*]. When the ~c[lambda]-expression is evaluated it is applied to the value associated with ~c[name] in the alist and the result is stored back into that alist. This step is performed by ~ilc[wormhole-eval]. To make things more efficient, ~c[wormhole-eval] is just a macro that expands into a ~c[let] that binds the ~c[lambda] formal to the current status and whose body is the ~c[lambda] body. Guard clauses are generated from the body, with one exception: the ~c[lambda] formal is replaced by a new variable so that no prior assumptions are available about the value of the the wormhole status. If the newly computed status has an entry code of ~c[:ENTER] ~ilc[ld] will be invoked. But we don't really copy state, of course. Instead we will invoke ~c[ld] on the live state, which is always available in the von Neumann world in which ACL2 is implemented. To give the illusion of copying state, we will undo changes to the state upon exiting. To support this, we do two things just before invoking ~c[ld]: we bind a Common Lisp special variable is to ~c[t] to record that ACL2 is in a wormhole, and we initialize an accumulator that will be used to record state changes made while in the wormhole. Then ~c[ld] is invoked, with first argument, ~c[standard-oi], being set to ~c[(cons form *standard-oi*)]. According to the standard semantics of ~c[ld], this reads and evaluates ~c[form] and then the forms in the specified channel. The standard channels are directed to and from the terminal, which is the physical realization of the comment window. All state modifying functions of ACL2 are sensitive to the special variable that indicates that evaluation is in a wormhole. Some ACL2 state-modifying functions (e.g., those that modify the file system like ~ilc[write-byte$]) are made to cause an error if invoked inside a wormhole on a file other than the terminal. Others, like ~c[f-put-global] (the function behind such features as ~c[assign] and maintenance of the ACL2 logical world by such events as ~ilc[defun] and ~ilc[defthm]) are made to record the old value of the state component being changed; these records are kept in the accumulator initialized above. Upon exit from ~c[ld] for any reason, the final value of ~c[(@ wormhole-status)] is stored in ~c[*wormhole-status-alist*] and then the accumulator is used to ``undo'' all the state changes. ~c[Wormhole] always returns ~c[nil].~/~/") (defun global-set (var val wrld) (declare (xargs :guard (and (symbolp var) (plist-worldp wrld)))) (putprop var 'global-value val wrld)) (defun defabbrev1 (lst) (declare (xargs :guard (true-listp lst))) (cond ((null lst) nil) (t (cons (list 'list (list 'quote (car lst)) (car lst)) (defabbrev1 (cdr lst)))))) (defun legal-variable-or-constant-namep (name) ; This function checks the syntax of variable or constant name ; symbols. In all cases, name must be a symbol that is not in the ; keyword package or among *common-lisp-specials-and-constants* ; (except t and nil), or in the main Lisp package but outside ; *common-lisp-symbols-from-main-lisp-package*, and that does not ; start with an ampersand. The function returns 'constant, 'variable, ; or nil. ; WARNING: T and nil are legal-variable-or-constant-nameps ; because we want to allow their use as constants. ; We now allow some variables (but still no constants) from the main Lisp ; package. See *common-lisp-specials-and-constants*. The following two note ; explains why we have been cautious here. ; Historical Note ; This package restriction prohibits using some very common names as ; variables or constants, e.g., MAX and REST. Why do we do this? The ; reason is that there are a few such symbols, such as ; LAMBDA-LIST-KEYWORDS, which if bound or set could cause real ; trouble. Rather than attempt to identify all of the specials of ; CLTL that are prohibited as ACL2 variables, we just prohibit them ; all. One might be reminded of Alexander cutting the Gordian Knot. ; We could spend a lot of time unravelling complex questions about ; specials in CLTL or we can get on with it. When ACL2 prevents you ; from using REST as an argument, you should see the severed end of a ; once tangled rope. ; For example, akcl and lucid (and others perhaps) allow you to define ; (defun foo (boole-c2) boole-c2) but then (foo 3) causes an error. ; Note that boole-c2 is recognized as special (by ; system::proclaimed-special-p) in lucid, but not in akcl (by ; si::specialp); in fact it's a constant in both. Ugh. ; End of Historical Note. (and (symbolp name) (cond ((or (eq name t) (eq name nil)) 'constant) (t (let ((p (symbol-package-name name))) (and (not (equal p "KEYWORD")) (let ((s (symbol-name name))) (cond ((and (not (= (length s) 0)) (eql (char s 0) #\*) (eql (char s (1- (length s))) #\*)) (if (equal p *main-lisp-package-name*) nil 'constant)) ((and (not (= (length s) 0)) (eql (char s 0) #\&)) nil) ((equal p *main-lisp-package-name*) (and (not (member-eq name *common-lisp-specials-and-constants*)) (member-eq name *common-lisp-symbols-from-main-lisp-package*) 'variable)) (t 'variable))))))))) (defun legal-constantp1 (name) ; This function should correctly distinguish between variables and ; constants for symbols that are known to satisfy ; legal-variable-or-constant-namep. Thus, if name satisfies this ; predicate then it cannot be a variable. (declare (xargs :guard (symbolp name))) (or (eq name t) (eq name nil) (let ((s (symbol-name name))) (and (not (= (length s) 0)) (eql (char s 0) #\*) (eql (char s (1- (length s))) #\*))))) (defun tilde-@-illegal-variable-or-constant-name-phrase (name) ; Assume that legal-variable-or-constant-namep has failed on name. ; We return a phrase that when printed with ~@0 will complete the ; sentence "Variable names must ...". Observe that the sentence ; could be "Constant names must ...". (cond ((not (symbolp name)) "be symbols") ((keywordp name) "not be in the KEYWORD package") ((and (legal-constantp1 name) (equal (symbol-package-name name) *main-lisp-package-name*)) (cons "not be in the main Lisp package, ~x0" (list (cons #\0 *main-lisp-package-name*)))) ((and (> (length (symbol-name name)) 0) (eql (char (symbol-name name) 0) #\&)) "not start with ampersands") ((and (not (legal-constantp1 name)) (member-eq name *common-lisp-specials-and-constants*)) "not be among certain symbols from the main Lisp package, namely, the ~ value of the list *common-lisp-specials-and-constants*") ((and (not (legal-constantp1 name)) (equal (symbol-package-name name) *main-lisp-package-name*) (not (member-eq name *common-lisp-symbols-from-main-lisp-package*))) "either not be in the main Lisp package, or else must be among the ~ imports into ACL2 from that package, namely, the list ~ *common-lisp-symbols-from-main-lisp-package*") (t "be approved by LEGAL-VARIABLE-OR-CONSTANT-NAMEP and this ~ one wasn't, even though it passes all the checks known to ~ the diagnostic function ~ TILDE-@-ILLEGAL-VARIABLE-OR-CONSTANT-NAME-PHRASE"))) (defun legal-constantp (name) ; A name may be declared as a constant if it has the syntax of a ; variable or constant (see legal-variable-or-constant-namep) and ; starts and ends with a *. ; WARNING: Do not confuse this function with defined-constant. (eq (legal-variable-or-constant-namep name) 'constant)) (defun defined-constant (name w) ; Name is a defined-constant if it has been declared with defconst. ; If name is a defined-constant then we can show that it satisfies ; legal-constantp, because when a name is declared as a constant we ; insist that it satisfy the syntactic check. But there are ; legal-constantps that aren't defined-constants, e.g., any symbol ; that could be (but hasn't yet been) declared as a constant. We ; check, below, that name is a symbolp just to guard the getprop. ; This function returns the quoted term that is the value of name, if ; name is a constant. That result is always non-nil (it may be (quote ; nil) of course). (and (symbolp name) (getprop name 'const nil 'current-acl2-world w))) (defun legal-variablep (name) ; Name may be used as a variable if it has the syntax of a variable ; (see legal-variable-or-constant-namep) and does not have the syntax of ; a constant, i.e., does not start and end with a *. (eq (legal-variable-or-constant-namep name) 'variable)) (defun genvar1 (pkg-witness char-lst avoid-lst cnt) ; This function generates a symbol in the same package as the symbol ; pkg-witness that is guaranteed to be a legal-variablep and not in avoid-lst. ; We form a symbol by concatenating char-lst and the decimal representation of ; the natural number cnt. Observe the guard below. Since guards are not ; checked in :program code, the user must ensure upon calling this ; function that pkg-witness is a symbol in some package other than the main ; lisp package or the keyword package and that char-lst is a list of characters ; not beginning with * or &. Given that guard, there must exist a sufficiently ; large cnt to make our generated symbol be in the package of pkg-witness (a ; finite number of generated symbols might have been interned in one of the ; non-variable packages). (declare (xargs :guard (and (let ((p (symbol-package-name pkg-witness))) (and (not (equal p "KEYWORD")) (not (equal p *main-lisp-package-name*)))) (consp char-lst) (not (eql (car char-lst) #\*)) (not (eql (car char-lst) #\&))))) (let ((sym (intern-in-package-of-symbol (coerce (append char-lst (explode-nonnegative-integer cnt 10 nil)) 'string) pkg-witness))) (cond ((or (member sym avoid-lst) ; The following call of legal-variablep could soundly be replaced by ; legal-variable-or-constant-namep, given the guard above, but we keep it ; as is for robustness. (not (legal-variablep sym))) (genvar1 pkg-witness char-lst avoid-lst (1+ cnt))) (t sym)))) (defun genvar (pkg-witness prefix n avoid-lst) ; This is THE function that ACL2 uses to generate new variable names. ; Prefix is a string and n is either nil or a natural number. Together we ; call prefix and n the "root" of the variable we generate. ; We generate from prefix a legal variable symbol in the same package as ; pkg-witness that does not occur in avoid-lst. If n is nil, we first try the ; symbol with symbol-name prefix first and otherwise suffix prefix with ; increasingly large naturals (starting from 0) to find a suitable variable. ; If n is non-nil it had better be a natural and we immediately begin trying ; suffixes from there. Since no legal variable begins with #\* or #\&, we tack ; a #\V on the front of our prefix if prefix starts with one of those chars. ; If prefix is empty, we use "V". ; Note: This system will eventually contain a lot of code to generate ; "suggestive" variable names. However, we make the convention that ; in the end every variable name generated is generated by this ; function. Thus, all other code associated with variable name ; generation is heuristic if this one is correct. (let* ((pkg-witness (cond ((let ((p (symbol-package-name pkg-witness))) (or (equal p "KEYWORD") (equal p *main-lisp-package-name*))) ; If pkg-witness is in an inappropriate package, we default it to the ; "ACL2" package. 'genvar) (t pkg-witness))) (sym (if (null n) (intern-in-package-of-symbol prefix pkg-witness) nil)) (cnt (if n n 0))) (cond ((and (null n) (legal-variablep sym) (not (member sym avoid-lst))) sym) (t (let ((prefix (coerce prefix 'list))) (cond ((null prefix) (genvar1 pkg-witness '(#\V) avoid-lst cnt)) ((and (consp prefix) (or (eql (car prefix) #\*) (eql (car prefix) #\&))) (genvar1 pkg-witness (cons #\V prefix) avoid-lst cnt)) (t (genvar1 pkg-witness prefix avoid-lst cnt)))))))) (defun packn1 (lst) (declare (xargs :guard (good-atom-listp lst))) (cond ((endp lst) nil) (t (append (explode-atom (car lst) 10) (packn1 (cdr lst)))))) (defun packn (lst) (declare (xargs :guard (good-atom-listp lst))) (let ((ans ; See comment in intern-in-package-of-symbol for an explanation of this trick. (intern (coerce (packn1 lst) 'string) "ACL2"))) ans)) (defun packn-pos (lst witness) (declare (xargs :guard (and (good-atom-listp lst) (symbolp witness)))) (intern-in-package-of-symbol (coerce (packn1 lst) 'string) witness)) (defun pack2 (n1 n2) (packn (list n1 n2))) (defun gen-formals-from-pretty-flags1 (pretty-flags i avoid) (cond ((endp pretty-flags) nil) ((eq (car pretty-flags) '*) (let ((xi (pack2 'x i))) (cond ((member-eq xi avoid) (let ((new-var (genvar 'genvar ;;; ACL2 package "GENSYM" 1 avoid))) (cons new-var (gen-formals-from-pretty-flags1 (cdr pretty-flags) (+ i 1) (cons new-var avoid))))) (t (cons xi (gen-formals-from-pretty-flags1 (cdr pretty-flags) (+ i 1) avoid)))))) (t (cons (car pretty-flags) (gen-formals-from-pretty-flags1 (cdr pretty-flags) (+ i 1) avoid))))) (defun gen-formals-from-pretty-flags (pretty-flags) ; Given a list of prettyified stobj flags, e.g., '(* * $S * STATE) we ; generate a proposed list of formals, e.g., '(X1 X2 $S X4 STATE). We ; guarantee that the result is a list of symbols as long as ; pretty-flags. Furthermore, a non-* in pretty-flags is preserved in ; the same slot in the output. Furthermore, the symbol generated for ; each * in pretty-flags is unique and not among the symbols in ; pretty-flags. Finally, STATE is not among the symbols we generate. (gen-formals-from-pretty-flags1 pretty-flags 1 pretty-flags)) (defun defstub-body (output) ; This strange little function is used to turn an output signature ; spec (in either the old or new style) into a term. It never causes ; an error, even if output is ill-formed! What it returns in that ; case is irrelevant. If output is well-formed, i.e., is one of: ; output result ; * nil ; x x ; state state ; (mv * state *) (mv nil state nil) ; (mv x state y) (mv x state y) ; it replaces the *'s by nil and otherwise doesn't do anything. (cond ((atom output) (cond ((equal output '*) nil) (t output))) ((equal (car output) '*) (cons nil (defstub-body (cdr output)))) (t (cons (car output) (defstub-body (cdr output)))))) (defun collect-non-x (x lst) ; This function preserves possible duplications of non-x elements in lst. ; We use this fact when we check the legality of signatures. (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((equal (car lst) x) (collect-non-x x (cdr lst))) (t (cons (car lst) (collect-non-x x (cdr lst)))))) #+acl2-loop-only (defmacro defproxy (name args-sig arrow body-sig) ":Doc-Section acl2::Events define a non-executable ~c[:]~ilc[program]-mode function for attachment~/ This event is provided for those who want to experiment with ~ilc[defattach] using ~c[:]~ilc[program] mode functions, and without proof obligations or constraints on cycles in the extended ancestors graph; ~pl[defattach]. If you merely want to define a stub or a non-executable function, ~pl[defstub] or ~pl[defun-nx], respectively. See community book ~c[books/misc/defproxy-test.lisp] for an extended (but simple) example. ~bv[] Example Forms: (defproxy subr1 (* *) => *) (defproxy add-hash (* * hashtable) => (mv * hashtable))~/ General Form: (defproxy name args-sig => output-sig) ~ev[] where ~c[name] is a new function symbol and ~c[(name . args-sig) => output-sig)] is a signature; ~pl[signature]. The macro ~c[defproxy] provides a convenient way to introduce a ``proxy'': a ~c[:program] mode function that can be given attachments for execution (~pl[defattach]), assuming that there is an active trust tag (~pl[defttag]). Thus, a ~c[defproxy] calls expands to a ~ilc[defun] form with the following ~ilc[xargs] ~ilc[declare] form: ~c[:non-executable :program]. Note that ~ilc[verify-termination] is not permitted for such a function. However, it is permitted to put the proxy function into ~c[:]~ilc[logic] mode by use of an ~ilc[encapsulate] event; indeed, this is the way to ``upgrade'' an attachment so that the normal checks are performed and no trust tag is necessary. In order to take advantage of a ~ilc[defproxy] form, one provides a subsequent ~c[defattach] form to attach an executable function to the ~c[defproxy]-introduced function. When ~c[:skip-checks t] is provided in a ~ilc[defattach] form, the usual checks for ~c[defattach] ~il[events] are skipped, including proof obligations and the check that the extended ancestor relation has no cycles (~pl[defattach]). There must be an active trust tag (~pl[defttag]) in order to use ~c[:skip-checks t]. In that case the use of ~c[:skip-checks t] is permitted; but note that its use is in fact required if a ~c[:]~ilc[program] mode function is involved, and even if a ~c[:]~ilc[logic] mode function is involved that has not been ~il[guard]-verified. The following log shows a simple use of defproxy. ~bv[] ACL2 !>(defproxy foo-stub (*) => *) Summary Form: ( DEFUN FOO-STUB ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FOO-STUB ACL2 !>(foo-stub '(3 4 5)) ACL2 Error in TOP-LEVEL: ACL2 cannot ev the call of undefined function FOO-STUB on argument list: ((3 4 5)) To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !>(defun foo-impl (x) (declare (xargs :mode :program :guard (or (consp x) (eq x nil)))) (car x)) Summary Form: ( DEFUN FOO-IMPL ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO-IMPL ACL2 !>(defttag t) TTAG NOTE: Adding ttag :T from the top level loop. T ACL2 !>(defattach (foo-stub foo-impl) :skip-checks t) Summary Form: ( DEFATTACH (FOO-STUB FOO-IMPL) ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :ATTACHMENTS-RECORDED ACL2 !>(foo-stub '(3 4 5)) 3 ACL2 !> ~ev[] One can replace this attachment with one that uses ~c[:]~ilc[logic] mode functions and does not skip checks. The idea is to reintroduce the proxy function using an ~ilc[encapsulate] form, which does not require redefinition (~pl[ld-redefinition-action]) to be enabled, and either to put the attachment into ~c[:]~ilc[logic] mode with the ~il[guard] verified, as we do in the example below, or else to attach to a different ~il[guard]-verified ~c[:]~ilc[logic] mode function. ~bv[] ACL2 !>(defattach (foo-stub nil) :skip-checks t) ; remove attachment Summary Form: ( DEFATTACH (FOO-STUB NIL) ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :ATTACHMENTS-RECORDED ACL2 !>(encapsulate ((foo-stub (x) t :guard (true-listp x))) (local (defun foo-stub (x) (cdr x))) (defthm foo-stub-reduces-acl2-count (implies (consp x) (< (acl2-count (foo-stub x)) (acl2-count x))))) [[ ... output omitted here ... ]] The following constraint is associated with the function FOO-STUB: (IMPLIES (CONSP X) (< (ACL2-COUNT (FOO-STUB X)) (ACL2-COUNT X))) Summary Form: ( ENCAPSULATE ((FOO-STUB ...) ...) ...) Rules: NIL Warnings: Non-rec Time: 0.02 seconds (prove: 0.01, print: 0.00, other: 0.01) T ACL2 !>(verify-termination foo-impl) Since FOO-IMPL is non-recursive, its admission is trivial. We could deduce no constraints on the type of FOO-IMPL. Computing the guard conjecture for FOO-IMPL.... The guard conjecture for FOO-IMPL is trivial to prove. FOO-IMPL is compliant with Common Lisp. Summary Form: ( DEFUN FOO-IMPL ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (VERIFY-TERMINATION-FN ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FOO-IMPL ACL2 !>(defttag nil) ; optional NIL ACL2 !>(defattach (foo-stub foo-impl)) The guard proof obligation is (IMPLIES (TRUE-LISTP X) (OR (CONSP X) (EQ X NIL))). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. This concludes the guard proof. We now prove that the attachment satisfies the required constraint. The goal to prove is (IMPLIES (CONSP X) (< (ACL2-COUNT (FOO-IMPL X)) (ACL2-COUNT X))). [[ ... output omitted here ... ]] Q.E.D. Summary Form: ( DEFATTACH (FOO-STUB FOO-IMPL)) Rules: ((:DEFINITION ACL2-COUNT) (:DEFINITION FOO-IMPL) (:ELIM CAR-CDR-ELIM) (:FAKE-RUNE-FOR-LINEAR NIL) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:REWRITE CAR-CONS) (:REWRITE CDR-CONS) (:TYPE-PRESCRIPTION ACL2-COUNT)) Time: 0.02 seconds (prove: 0.01, print: 0.01, other: 0.00) :ATTACHMENTS-RECORDED ACL2 !> ~ev[] We close with some remarks on the checking of ~il[guard]s in the case that ~ilc[defattach] has been called with keyword argument ~c[:skip-checks t]. We illustrate with examples, where we assume an attachment pair ~c[(f . g)] created by an event ~c[(defattach ... (f g) ... :skip-checks t ...)]. A good model for the treatment of ~c[:skip-checks t] is dependent on whether ~c[f] was introduced with ~c[defproxy] or with ~ilc[encapsulate]: for ~c[defproxy], the normal guard-related checks are treated as skipped, while for ~ilc[encapsulate], they are assumed to hold. First suppose that ~c[f] was introduced using ~c[defproxy], and consider the following example. ~bv[] (defproxy f (*) => *) (defun g (x) (car x)) ; not guard-verified; implicit guard of t is too weak (defttag t) ; trust tag needed for :skip-checks t (defattach (f g) :skip-checks t) ~ev[] If we try to evaluate the form ~c[(f 3)] in ACL2, then the top-level so-called ``executable counterpart'' (i.e., the logically-defined funcction, also known as the ``*1*'' function) of ~c[f] is invoked. It calls the executable counterpart of ~c[g], which calls the executable counterpart of ~ilc[car], which in turn checks the ~il[guard] of ~ilc[car] and causes a guard violation error (unless we first turn off guard-checking; ~pl[set-guard-checking]). ~bv[] ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f 3) 1> (ACL2_*1*_ACL2::F 3) 2> (ACL2_*1*_ACL2::G 3) ACL2 Error in TOP-LEVEL: The guard for the function call (CAR X), which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CAR 3). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> ~ev[] Little changes if we modify the example above by strengtheing the guard of ~c[g]. ~bv[] (defproxy f (*) => *) (defun g (x) (declare (xargs :guard (consp x))) (car x)) (defttag t) ; trust tag needed for :skip-checks t (defattach (f g) :skip-checks t) ~ev[] The result of evaluating ~c[(f 3)] is as before, except that this time the guard violation occurs at the time that ~c[g] is called. ~bv[] ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f 3) 1> (ACL2_*1*_ACL2::F 3) 2> (ACL2_*1*_ACL2::G 3) ACL2 Error in TOP-LEVEL: The guard for the function call (G X), which is (CONSP X), is violated by the arguments in the call (G 3). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set- guard-checking for information about suppressing this check with (set- guard-checking :none), as recommended for new users. ACL2 !> ~ev[] Now consider a slight variation of the example just above, in which ~c[f] is introduced using ~ilc[encapsulate] instead of using ~c[defproxy]. ~bv[] (encapsulate ( ((f *) => *) ) (local (defun f (x) x))) (defun g (x) (declare (xargs :guard (consp x))) (car x)) (defttag t) ; trust tag needed for :skip-checks t (defattach (f g) :skip-checks t) ~ev[] Since ~c[f] was introduced by ~ilc[encapsulate] instead of by ~c[defproxy], ACL2 assumes that the usual guard properties hold. In particular, it assumes that (informally speaking) the guard of ~c[f] implies the guard of ~c[g]; ~pl[defattach] for details. So in this case, ACL2 proceeds under that assumption even though it's actually false, and the result is a raw Lisp error. ~bv[] ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f 3) 1> (ACL2_*1*_ACL2::F 3) 2> (G 3) *********************************************** ************ ABORTING from raw Lisp *********** Error: Attempt to take the car of 3 which is not listp. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !> ~ev[] If you replace ~c[g] by its definition in the first example of this series, i.e. with a guard (implicitly) of ~c[t], you will see the same error, this time because the ~ilc[defattach] event assumed that ~c[g] was guard-verified." (cond ((not (and (symbol-listp args-sig) (symbolp arrow) (equal (symbol-name arrow) "=>"))) (er hard 'defproxy "Defproxy must be of the form (proxy name args-sig => body-sig), ~ where args-sig is a true-list of symbols. See :DOC defproxy.")) (t (let ((formals (gen-formals-from-pretty-flags args-sig)) (body (defstub-body body-sig)) (stobjs (collect-non-x '* args-sig))) `(defun ,name ,formals (declare (xargs :non-executable :program :mode :program ,@(and stobjs `(:stobjs ,stobjs))) (ignorable ,@formals)) ; The form of the body below is dictated by function throw-nonexec-error-p. ; Notice that we do not pass the formals to throw-nonexec-error as we do in ; defun-nx-fn, because if the formals contain a stobj then we would violate ; stobj restrictions, which are checked for non-executable :program mode ; functions. (prog2$ (throw-nonexec-error ',name nil) ,body)))))) #-acl2-loop-only (defmacro defproxy (name args-sig arrow body-sig) ; Note that a defproxy redefined using encapsulate can generate a warning in ; CLISP (see comment about CLISP in with-redefinition-suppressed), because ; indeed there are two definitions being made for the same name. However, the ; definition generated for a function by encapsulate depends only on the ; function's signature, up to renaming of formals; see the #-acl2-loop-only ; definition of encapsulate. So this redefinition is as benign as the ; redefinition that occurs in raw Lisp with a redundant defun. `(defstub ,name ,args-sig ,arrow ,body-sig)) ; We now use encapsulate to implement defstub. It is handy to do so here, ; rather than in other-events.lisp, since the raw Lisp definition of defproxy ; uses defstub. (defun defstub-ignores (formals body) ; The test below is sufficient to ensure that the set-difference-equal ; used to compute the ignored vars will not cause an error. We return ; a true list. The formals and body will be checked thoroughly by the ; encapsulate, provided we generate it! Provided they check out, the ; result returned is the list of ignored formals. (if (and (symbol-listp formals) (or (symbolp body) (and (consp body) (symbol-listp (cdr body))))) (set-difference-equal formals (if (symbolp body) (list body) (cdr body))) nil)) ; The following function is used to implement a slighly generalized ; form of macro args, namely one in which we can provide an arbitrary ; number of ordinary arguments terminated by an arbitrary number of ; keyword argument pairs. (defun partition-rest-and-keyword-args1 (x) (cond ((endp x) (mv nil nil)) ((keywordp (car x)) (mv nil x)) (t (mv-let (rest keypart) (partition-rest-and-keyword-args1 (cdr x)) (mv (cons (car x) rest) keypart))))) (defun partition-rest-and-keyword-args2 (keypart keys alist) ; We return t if keypart is ill-formed as noted below. Otherwise, we ; return ((:keyn . vn) ... (:key1 . v1)). (cond ((endp keypart) alist) ((and (keywordp (car keypart)) (consp (cdr keypart)) (not (assoc-eq (car keypart) alist)) (member (car keypart) keys)) (partition-rest-and-keyword-args2 (cddr keypart) keys (cons (cons (car keypart) (cadr keypart)) alist))) (t t))) (defun partition-rest-and-keyword-args (x keys) ; X is assumed to be a list of the form (a1 ... an :key1 v1 ... :keyk ; vk), where no ai is a keyword. We return (mv erp rest alist), where ; erp is t iff the keyword section of x is ill-formed. When erp is ; nil, rest is '(a1 ... an) and alist is '((:key1 . v1) ... (:keyk ; . vk)). ; The keyword section is ill-formed if it contains a non-keyword in an ; even numbered element, if it binds the same keyword more than once, ; or if it binds a keyword other than those listed in keys. (mv-let (rest keypart) (partition-rest-and-keyword-args1 x) (let ((alist (partition-rest-and-keyword-args2 keypart keys nil))) (cond ((eq alist t) (mv t nil nil)) (t (mv nil rest alist)))))) (defmacro defstub (name &rest rst) ":Doc-Section Events stub-out a function symbol~/ ~bv[] Examples: ACL2 !>(defstub subr1 (* * state) => (mv * state)) ACL2 !>(defstub add-hash (* * hashtable) => hashtable)~/ General Forms: (defstub name args-sig => output-sig) (defstub name args-sig => output-sig :doc doc-string) ~ev[] ~c[Name] is a new function symbol and ~c[(name . args-sig) => output-sig)] is a ~il[signature]. If the optional ~ilc[doc-string] is supplied it should be a documentation string. See also the ``Old Style'' heading below. ~c[Defstub] macro expands into an ~ilc[encapsulate] event (~pl[encapsulate]). Thus, no axioms are available about ~c[name] but it may be used wherever a function of the given signature is permitted. Exception: if ~c[output-sig] is of the form ~c[(mv ...)], then a ~c[:]~ilc[type-prescription] rule is introduced stating that ~c[name] returns a value satisfying ~ilc[true-listp]. Old Style: ~bv[] Old Style General Form: (defstub name formals output) (defstub name formals output :doc doc-string) ~ev[] where ~c[name] is a new function symbol, ~c[formals] is its list of formal parameters, and ~c[output] is either a symbol (indicating that the function returns one result) or a term of the form ~c[(mv s1 ... sn)], where each ~c[si] is a symbol (indicating that the function returns ~c[n] results). Whether and where the symbol ~ilc[state] occurs in ~c[formals] and ~c[output] indicates how the function handles ~il[state]. It should be the case that ~c[(name formals output)] is in fact a signature (~pl[signature]). Note that with the old style notation it is impossible to stub-out a function that uses any single-threaded object other than state. The old style is preserved for compatibility with earlier versions of ACL2." (mv-let (erp args key-alist) (partition-rest-and-keyword-args rst '(:doc)) (cond ((or erp (not (or (equal (length args) 2) (and (equal (length args) 3) (symbol-listp (car args)) (symbolp (cadr args)) (equal (symbol-name (cadr args)) "=>"))))) `(er soft 'defstub "Defstub must be of the form (defstub name formals ~ body) or (defstub name args-sig => body-sig), where ~ args-sig is a true-list of symbols. Both ~ forms permit an optional, final :DOC doc-string ~ argument. See :DOC defstub.")) (t (let ((doc (cdr (assoc-eq :doc key-alist)))) (cond ((equal (length args) 2) ; Old style (let* ((formals (car args)) (body (cadr args)) (ignores (defstub-ignores formals body))) `(encapsulate ((,name ,formals ,body)) (logic) (local (defun ,name ,formals (declare (ignore ,@ignores)) ,body)) ,@(and (consp body) (eq (car body) 'mv) `((defthm ,(packn-pos (list "TRUE-LISTP-" name) name) (true-listp (,name ,@formals)) :rule-classes :type-prescription))) ,@(if doc `((defdoc ,name ,doc)) nil)))) (t (let* ((args-sig (car args)) (body-sig (caddr args)) (formals (gen-formals-from-pretty-flags args-sig)) (body (defstub-body body-sig)) (ignores (defstub-ignores formals body)) (stobjs (collect-non-x '* args-sig))) `(encapsulate (((,name ,@args-sig) => ,body-sig)) (logic) (local (defun ,name ,formals (declare (ignore ,@ignores) (xargs :stobjs ,stobjs)) ,body)) ,@(and (consp body-sig) (eq (car body-sig) 'mv) `((defthm ,(packn-pos (list "TRUE-LISTP-" name) name) (true-listp (,name ,@formals)) :rule-classes :type-prescription))) ,@(if doc `((defdoc ,name ,doc)) nil)))))))))) (defun lambda-keywordp (x) (and (symbolp x) (eql 1 (string<= "&" (symbol-name x))))) (defun arglistp1 (lst) ; Every element of lst is a legal-variablep. (cond ((atom lst) (null lst)) (t (and (legal-variablep (car lst)) (arglistp1 (cdr lst)))))) (defun arglistp (lst) (and (arglistp1 lst) (no-duplicatesp lst))) (defun find-first-bad-arg (args) ; This function is only called when args is known to be a non-arglistp ; that is a true list. It returns the first bad argument and a string ; that completes the phrase "... violates the rules because it ...". (declare (xargs :guard (and (true-listp args) (not (arglistp args))))) (cond ;;((null args) (mv nil nil)) -- can't happen, given the guard! ((not (symbolp (car args))) (mv (car args) "is not a symbol")) ((legal-constantp1 (car args)) (mv (car args) "has the syntax of a constant")) ((lambda-keywordp (car args)) (mv (car args) "is a lambda keyword")) ((keywordp (car args)) (mv (car args) "is in the KEYWORD package")) ((member-eq (car args) *common-lisp-specials-and-constants*) (mv (car args) "belongs to the list *common-lisp-specials-and-constants* ~ of symbols from the main Lisp package")) ((member-eq (car args) (cdr args)) (mv (car args) "occurs more than once in the list")) ((and (equal (symbol-package-name (car args)) *main-lisp-package-name*) (not (member-eq (car args) *common-lisp-symbols-from-main-lisp-package*))) (mv (car args) "belongs to the main Lisp package but not to the list ~ *common-lisp-symbols-from-main-lisp-package*")) (t (find-first-bad-arg (cdr args))))) (defun process-defabbrev-declares (decls) (cond ((endp decls) ()) ; Here we do a cheap check that the declare form is illegal. It is tempting to ; use collect-declarations, but it take state. Anyhow, there is no soundness ; issue; the user will just be a bit surprised when the error shows up later as ; the macro defined by the defabbrev is applied. ((not (and (consp (car decls)) (eq (caar decls) 'DECLARE) (true-list-listp (cdar decls)) (subsetp-eq (strip-cars (cdar decls)) '(IGNORE IGNORABLE TYPE)))) (er hard 'process-defabbrev-declares "In a DEFABBREV form, each expression after the argument list ~ but before the body must be of the form (DECLARE decl1 .. ~ declk), where each dcli is of the form (IGNORE ..), (IGNORABE ~ ..), or (TYPE ..). The form ~x0 is thus illegal." (car decls))) (t (cons (kwote (car decls)) (process-defabbrev-declares (cdr decls)))))) (defmacro defabbrev (fn args &rest body) ":Doc-Section Events a convenient form of macro definition for simple expansions~/ ~bv[] Examples: (defabbrev snoc (x y) (append y (list x))) (defabbrev sq (x) (declare (type (signed-byte 8) x)) (* x x)) General Form: (defabbrev name (v1 ... vn) doc-string decl1 ... declk body) ~ev[] where ~c[name] is a new function symbol, the ~c[vi] are distinct variable symbols, and ~c[body] is a term. The ~c[decli], if supplied, should be legal ~c[declare] forms; ~pl[declare]. ~c[Doc-string] is an optional ~il[documentation] string; ~pl[doc-string]. Roughly speaking, the ~c[defabbrev] event is akin to defining ~c[f] so that ~c[(f v1 ... vn) = body]. But rather than do this by adding a new axiom, ~c[defabbrev] defines ~c[f] to be a macro so that ~c[(f a1 ... an)] expands to ~c[body], with the ``formals,'' ~c[vi], replaced by the ``actuals,'' ~c[ai].~/ For example, if ~c[snoc] is defined as shown in the first example above, then ~c[(snoc (+ i j) temp)] is just an abbreviation for ~bv[] (append temp (list (+ i j))). ~ev[] In order to generate efficiently executable Lisp code, the macro that ~c[defabbrev] introduces uses a ~ilc[let] to bind the ``formals'' to the ``actuals.'' Consider the second example above. Logically speaking, ~c[(sq (ack i j))] is an abbreviation for ~c[(* (ack i j) (ack i j))]. But in fact the macro for ~c[sq] introduced by ~c[defabbrev] actually arranges for ~c[(sq (ack i j))] to expand to: ~bv[] (let ((x (ack i j))) (* x x)) ~ev[] which executes more efficiently than ~c[(* (ack i j) (ack i j))]. In the theorem prover, the ~c[let] above expands to ~bv[] ((lambda (x) (* x x)) (ack i j)) ~ev[] and thence to ~c[(* (ack i j) (ack i j))]. It is important to note that the term in ~c[body] should not contain a call of ~c[name] ~-[] i.e., ~c[defabbrev] should not be used in place of ~c[defun] when the function is recursive. ACL2 will not complain when the ~c[defabbrev] form is processed, but instead ACL2 will more than likely go into an infinite loop during macroexpansion of any form that has a call of ~c[name]. It is also important to note that the parameters of any call of a macro defined by defabbrev will, as is the case for the parameters of a function call, be evaluated before the body is evaluated, since this is the evaluation order of ~ilc[let]. This may lead to some errors or unexpected inefficiencies during evaluation if the body contains any conditionally evaluted forms like ~c[cond], ~c[case], or ~c[if]. Consider the following example. ~bv[] (defabbrev foo (x y) (if (test x) (bar y) nil)) ~ev[] Notice a typical one-step expansion of a call of ~c[foo] (~pl[trans1]): ~bv[] ACL2 !>:trans1 (foo expr1 expr2) (LET ((X EXPR1) (Y EXPR2)) (IF (TEST X) (BAR Y) NIL)) ACL2 !> ~ev[] Now imagine that ~c[expr2] is a complicated expression whose evaluation is intended only when the predicate ~c[test] holds of ~c[expr1]. The expansion above suggests that ~c[expr2] will always be evaluated by the call ~c[(foo expr1 expr2)], which may be inefficient (since perhaps we only need that value when ~c[test] is true of ~c[expr1]). The evaluation of ~c[expr2] may even cause an error, for example in ~c[:]~ilc[program] mode if the expression ~c[expr2] has been constructed in a manner that could cause a guard violation unless ~c[test] holds of ~c[expr1]." (cond ((null body) (er hard (cons 'defabbrev fn) "The body of this DEFABBREV form is missing.")) ((not (true-listp args)) (er hard (cons 'defabbrev fn) "The formal parameter list for a DEFABBREV must be a true list. The ~ argument list ~x0 is thus illegal." args)) ((not (arglistp args)) (mv-let (culprit explan) (find-first-bad-arg args) (er hard (cons 'defabbrev fn) "The formal parameter list for a DEFABBREV must be a ~ list of distinct variables, but ~x0 does not meet these ~ conditions. The element ~x1 ~@2." args culprit explan))) (t (mv-let (doc-string-list body) (if (and (stringp (car body)) (cdr body)) (mv (list (car body)) (cdr body)) (mv nil body)) (cond ((null body) (er hard (cons 'defabbrev fn) "This DEFABBREV form has a doc string but no ~ body.")) ((and (consp (car (last body))) (eq (caar (last body)) 'declare)) (er hard (cons 'defabbrev fn) "The body of this DEFABBREV form is a DECLARE ~ form, namely ~x0. This is illegal and probably ~ is not what was intended." (car (last body)))) (t `(defmacro ,fn ,args ,@doc-string-list (list 'let (list ,@(defabbrev1 args)) ,@(process-defabbrev-declares (butlast body 1)) ',(car (last body)))))))))) ;; RAG - I changed the primitive guard for the < function, and the ;; complex function. Added the functions complexp, realp, and floor1. ;; RAG - I subsequently changed this to add the non-standard functions ;; standardp, standard-part and i-large-integer. I had some ;; questions as to whether these functions should appear on this list ;; or not. After considering carefully, I decided that was the right ;; course of action. In addition to adding them to the list below, I ;; also add them to *non-standard-primitives* which is a special list ;; of non-standard primitives. Functions in this list are considered ;; to be constrained. Moreover, they are given the value t for the ;; property 'unsafe-induction so that recursion and induction are ;; turned off for terms built from these functions. (defconst *primitive-formals-and-guards* ; Keep this in sync with ev-fncall-rec-logical and type-set-primitive, and with ; the documentation and "-completion" axioms of the primitives. Also be sure ; to define a *1* function for each function in this list that is not a member ; of *oneify-primitives*. ; WARNING: for each primitive below, primordial-world puts a 'stobjs-in that is ; a list of nils of the same length as its formals, and a 'stobjs-out of ; '(nil). Revisit that code if you add a primitive that involves stobjs! ; WARNING: Just below you will find another list, *primitive-monadic-booleans* ; that lists the function names from this list that are monadic booleans. The ; names must appear in the same order as here! '((acl2-numberp (x) 't) (bad-atom<= (x y) (if (bad-atom x) (bad-atom y) 'nil)) (binary-* (x y) (if (acl2-numberp x) (acl2-numberp y) 'nil)) (binary-+ (x y) (if (acl2-numberp x) (acl2-numberp y) 'nil)) (unary-- (x) (acl2-numberp x)) (unary-/ (x) (if (acl2-numberp x) (not (equal x '0)) 'nil)) (< (x y) ; We avoid the temptation to use real/rationalp below, since it is a macro. (if #+:non-standard-analysis (realp x) #-:non-standard-analysis (rationalp x) #+:non-standard-analysis (realp y) #-:non-standard-analysis (rationalp y) 'nil)) (car (x) (if (consp x) 't (equal x 'nil))) (cdr (x) (if (consp x) 't (equal x 'nil))) (char-code (x) (characterp x)) (characterp (x) 't) (code-char (x) (if (integerp x) (if (< x '0) 'nil (< x '256)) 'nil)) (complex (x y) (if #+:non-standard-analysis (realp x) #-:non-standard-analysis (rationalp x) #+:non-standard-analysis (realp y) #-:non-standard-analysis (rationalp y) 'nil)) (complex-rationalp (x) 't) #+:non-standard-analysis (complexp (x) 't) (coerce (x y) (if (equal y 'list) (stringp x) (if (equal y 'string) (character-listp x) 'nil))) (cons (x y) 't) (consp (x) 't) (denominator (x) (rationalp x)) (equal (x y) 't) #+:non-standard-analysis (floor1 (x) (realp x)) (if (x y z) 't) (imagpart (x) (acl2-numberp x)) (integerp (x) 't) (intern-in-package-of-symbol (str sym) (if (stringp str) (symbolp sym) 'nil)) (numerator (x) (rationalp x)) (pkg-imports (pkg) (stringp pkg)) (pkg-witness (pkg) (if (stringp pkg) (not (equal pkg '"")) 'nil)) (rationalp (x) 't) #+:non-standard-analysis (realp (x) 't) (realpart (x) (acl2-numberp x)) (stringp (x) 't) (symbol-name (x) (symbolp x)) (symbol-package-name (x) (symbolp x)) (symbolp (x) 't) #+:non-standard-analysis (standardp (x) 't) #+:non-standard-analysis (standard-part (x) ; If (x) is changed here, change cons-term1-cases. (acl2-numberp x)) #+:non-standard-analysis (i-large-integer () 't))) (defconst *primitive-monadic-booleans* ; This is the list of primitive monadic boolean function symbols. Each ; function must be listed in *primitive-formals-and-guards* and they should ; appear in the same order. (The reason order matters is simply to make it ; easier to check at the end of boot-strap that we have included all the ; monadic booleans.) '(acl2-numberp characterp complex-rationalp #+:non-standard-analysis complexp consp integerp rationalp #+:non-standard-analysis realp stringp symbolp #+:non-standard-analysis standardp)) (defun equal-x-constant (x const) ; x is an arbitrary term, const is a quoted constant, e.g., a list of ; the form (QUOTE guts). We return a term equivalent to (equal x ; const). (let ((guts (cadr const))) (cond ((symbolp guts) (list 'eq x const)) ((or (acl2-numberp guts) (characterp guts)) (list 'eql x guts)) ((stringp guts) (list 'equal x guts)) (t (list 'equal x const))))) (defun match-tests-and-bindings (x pat tests bindings) ; We return two results. The first is a list of tests, in reverse ; order, that determine whether x matches the structure pat. We ; describe the language of pat below. The tests are accumulated onto ; tests, which should be nil initially. The second result is an alist ; containing entries of the form (sym expr), suitable for use as the ; bindings in the let we generate if the tests are satisfied. The ; bindings required by pat are accumulated onto bindings and thus are ; reverse order, although their order is actually irrelevant. ; For example, the pattern ; ('equal ('car ('cons u v)) u) ; matches only first order instances of (EQUAL (CAR (CONS u v)) u). ; The pattern ; ('equal (ev (simp x) a) (ev x a)) ; matches only second order instances of (EQUAL (ev (simp x) a) (ev x a)), ; i.e., ev, simp, x, and a are all bound in the match. ; In general, the match requires that the cons structure of x be isomorphic ; to that of pat, down to the atoms in pat. Symbols in the pat denote ; variables that match anything and get bound to the structure matched. ; Occurrences of a symbol after the first match only structures equal to ; the binding. Non-symbolp atoms match themselves. ; There are some exceptions to the general scheme described above. A ; cons structure starting with QUOTE matches only itself. The symbols ; nil and t, and all symbols whose symbol-name starts with #\* match ; only structures equal to their values. (These symbols cannot be ; legally bound in ACL2 anyway, so this exceptional treatment does not ; restrict us further.) Any symbol starting with #\! matches only the ; value of the symbol whose name is obtained by dropping the #\!. ; This is a way of referring to already bound variables in the ; pattern. Finally, the symbol & matches anything and causes no ; binding. (cond ((symbolp pat) (cond ((or (eq pat t) (eq pat nil)) (mv (cons (list 'eq x pat) tests) bindings)) ((and (> (length (symbol-name pat)) 0) (eql #\* (char (symbol-name pat) 0))) (mv (cons (list 'equal x pat) tests) bindings)) ((and (> (length (symbol-name pat)) 0) (eql #\! (char (symbol-name pat) 0))) (mv (cons (list 'equal x (intern (coerce (cdr (coerce (symbol-name pat) 'list)) 'string) "ACL2")) tests) bindings)) ((eq pat '&) (mv tests bindings)) (t (let ((binding (assoc-eq pat bindings))) (cond ((null binding) (mv tests (cons (list pat x) bindings))) (t (mv (cons (list 'equal x (cadr binding)) tests) bindings))))))) ((atom pat) (mv (cons (equal-x-constant x (list 'quote pat)) tests) bindings)) ((eq (car pat) 'quote) (mv (cons (equal-x-constant x pat) tests) bindings)) (t (mv-let (tests1 bindings1) (match-tests-and-bindings (list 'car x) (car pat) (cons (list 'consp x) tests) bindings) (match-tests-and-bindings (list 'cdr x) (cdr pat) tests1 bindings1))))) (defun match-clause (x pat forms) (mv-let (tests bindings) (match-tests-and-bindings x pat nil nil) (list (if (null tests) t (cons 'and (reverse tests))) (cons 'let (cons (reverse bindings) forms))))) (defun match-clause-list (x clauses) (cond ((consp clauses) (if (eq (caar clauses) '&) (list (match-clause x (caar clauses) (cdar clauses))) (cons (match-clause x (caar clauses) (cdar clauses)) (match-clause-list x (cdr clauses))))) (t '((t nil))))) (defmacro case-match (&rest args) (declare (xargs :guard (and (consp args) (symbolp (car args)) (alistp (cdr args)) (null (cdr (member-equal (assoc-eq '& (cdr args)) (cdr args))))))) ":Doc-Section ACL2::ACL2-built-ins pattern matching or destructuring~/ ~bv[] General Form: (case-match x (pat1 dcl1 body1) ... (patk dclk bodyk)) ~ev[] where ~c[x] is a variable symbol, the ~c[pati] are structural patterns as described below, the ~c[dcli] are optional ~ilc[declare] forms and the ~c[bodyi] are terms. Return the value(s) of the ~c[bodyi] corresponding to the first ~c[pati] matching ~c[x], or ~c[nil] if none matches. Pattern Language:~nl[] With the few special exceptions described below, matching requires that the ~ilc[cons] structure of ~c[x] be isomorphic to that of the pattern, down to the ~il[atom]s in the pattern. Non-symbol ~il[atom]s in the pattern match only themselves. Symbols in the pattern denote variables which match anything and which are bound by a successful match to the corresponding substructure of ~c[x]. Variables that occur more than once must match the same (~ilc[EQUAL]) structure in every occurrence. ~bv[] Exceptions: & Matches anything and is not bound. Repeated occurrences of & in a pattern may match different structures. nil, t, *sym* These symbols cannot be bound and match only their global values. !sym where sym is a symbol that is already bound in the context of the case-match, matches only the current binding of sym. 'obj Matches only itself. ~ev[] Some examples are shown below.~/ Below we show some sample patterns and examples of things they match and do not match. ~bv[] pattern matches non-matches (x y y) (ABC 3 3) (ABC 3 4) ; 3 is not 4 (fn x . rst) (P (A I) B C) (ABC) ; NIL is not (x . rst) (J (A I)) ; rst matches nil ('fn (g x) 3) (FN (H 4) 3) (GN (G X) 3) ; 'fn matches only itself (& t & !x) ((A) T (B) (C)) ; provided x is '(C) ~ev[] Consider the two binary trees that contain three leaves. They might be described as ~c[(x . (y . z))] and ~c[((x . y) . z)], where ~c[x], ~c[y], and ~c[z] are atomic. Suppose we wished to recognize those trees. The following ~c[case-match] would do: ~bv[] (case-match tree ((x . (y . z)) (and (atom x) (atom y) (atom z))) (((x . y) . z) (and (atom x) (atom y) (atom z)))) ~ev[] Suppose we wished to recognize such trees where all three tips are identical. Suppose further we wish to return the tip if the tree is one of those recognized ones and to return the number ~c[7] otherwise. ~bv[] (case-match tree ((x . (x . x)) (if (atom x) x 7)) (((x . x) . x) (if (atom x) x 7)) (& 7)) ~ev[] Note that ~c[case-match] returns ~c[nil] if no ~c[pati] matches. Thus if we must return ~c[7] in that case, we have to add as the final pattern the ~c[&], which always matches anything." (cons 'cond (match-clause-list (car args) (cdr args)))) #+:non-standard-analysis (defconst *non-standard-primitives* '(standardp standard-part i-large-integer)) (defun cons-term1-cases (alist) ; Initially, alist is *primitive-formals-and-guards*. (cond ((endp alist) nil) ((member-eq (caar alist) '(if ; IF is handled directly in cons-term1-body. bad-atom<= pkg-imports pkg-witness)) (cons-term1-cases (cdr alist))) (t (cons (let* ((trip (car alist)) (fn (car trip)) (formals (cadr trip)) (guard (caddr trip))) (list fn (cond #+:non-standard-analysis ((eq fn 'i-large-integer) nil) ; fall through in cons-term1-body #+:non-standard-analysis ((eq fn 'standardp) '(kwote t)) #+:non-standard-analysis ((eq fn 'standard-part) (assert$ (eq (car formals) 'x) `(and ,guard ; a term in variable x (kwote ,@formals)))) ((equal guard *t*) `(kwote (,fn ,@formals))) ((or (equal formals '(x)) (equal formals '(x y))) `(and ,guard (kwote (,fn ,@formals)))) (t (case-match formals ((f1) `(let ((,f1 x)) (and ,guard (kwote (,fn ,@formals))))) ((f1 f2) `(let ((,f1 x) (,f2 y)) (and ,guard (kwote (,fn ,@formals))))) (& (er hard! 'cons-term1-cases "Unexpected formals, ~x0" formals))))))) (cons-term1-cases (cdr alist)))))) (defconst *cons-term1-alist* (cons-term1-cases *primitive-formals-and-guards*)) (defmacro cons-term1-body () `(let ((x (unquote (car args))) (y (unquote (cadr args)))) (or (case fn ,@*cons-term1-alist* (if (kwote (if x y (unquote (caddr args))))) (not (kwote (not x)))) (cons fn args)))) (defun quote-listp (l) (declare (xargs :guard (true-listp l))) (cond ((null l) t) (t (and (quotep (car l)) (quote-listp (cdr l)))))) (defun cons-term1 (fn args) (declare (xargs :guard (and (pseudo-term-listp args) (quote-listp args)))) (cons-term1-body)) (defun cons-term (fn args) (declare (xargs :guard (pseudo-term-listp args))) (cond ((quote-listp args) (cons-term1 fn args)) (t (cons fn args)))) (defmacro cons-term* (fn &rest args) `(cons-term ,fn (list ,@args))) (defmacro mcons-term (fn args) ; The "m" in "mcons-term" is for "maybe fast". Some calls of this macro can ; probably be replaced with fcons-term. `(cons-term ,fn ,args)) (defmacro mcons-term* (fn &rest args) ; The "m" in "mcons-term*" is for "maybe fast". Some of calls of this macro ; can probably be replaced with fcons-term*. `(cons-term* ,fn ,@args)) (defmacro fcons-term (fn args) ; ; Start experimental code mod, to check that calls of fcons-term are legitimate ; ; shortcuts in place of the corresponding known-correct calls of cons-term. ; #-acl2-loop-only ; `(let* ((fn-used-only-in-fcons-term ,fn) ; (args-used-only-in-fcons-term ,args) ; (result (cons fn-used-only-in-fcons-term ; args-used-only-in-fcons-term))) ; (assert$ (equal result (cons-term fn-used-only-in-fcons-term ; args-used-only-in-fcons-term)) ; result)) ; #+acl2-loop-only ; ; End experimental code mod. (list 'cons fn args)) (defun fargn1 (x n) (declare (xargs :guard (and (integerp n) (> n 0)))) (cond ((eql n 1) (list 'cdr x)) (t (list 'cdr (fargn1 x (- n 1)))))) (defmacro fargn (x n) (list 'car (fargn1 x n))) (defun cdr-nest (n v) (cond ((equal n 0) v) (t (fargn1 v n)))) (defun all-but-last (l) (cond ((endp l) nil) ((endp (cdr l)) nil) (t (cons (car l) (all-but-last (cdr l)))))) ; Essay on Evisceration ; We have designed the pretty printer so that it can print an ; "eviscerated" object, that is, an object that has had certain ; substructures removed. We discuss the prettyprinter in the Essay on ; the ACL2 Prettyprinter. The pretty printer has a flag, eviscp, ; which indicates whether the object has been eviscerated or not. If ; not, then the full object is printed as it stands. If so, then ; certain substructures of it are given special interpretation by the ; printer. In particular, when the printer encounters a cons of the ; form (:evisceration-mark . x) then x is a string and the cons is ; printed by printing the characters in x (without the double ; gritches). ; object pretty printed output ; (:evisceration-mark . "#") # ; (:evisceration-mark . "...") ... ; (:evisceration-mark . "") ; (:evisceration-mark . ":EVISCERATION-MARK") :EVISCERATION-MARK ; So suppose you have some object and you want to print it, implementing ; the CLTL conventions for *print-level* and *print-length*. Then you ; must first scan it, inserting :evisceration-mark forms where ; appropriate. But what if it contains some occurrences of ; :evisceration-mark? Then you must use evisceration mechanism to print ; them correctly! Once you have properly eviscerated the object, you can ; call the prettyprinter on it, telling it that the object has been ; eviscerated. If, on the other hand, you don't want to eviscerate it, ; then you needn't sweep it to protect the native :evisceration-marks: ; just call the prettyprinter with the eviscp flag off. (defconst *evisceration-mark* :evisceration-mark) ; Note: It is important that the evisceration-mark be a keyword. ; One reason is that (:evisceration-mark . ":EVISCERATION-MARK") ; couldn't be used to print a non-keyword because the package might ; need to be printed. Another is that we exploit the fact that no ; event name nor any formal is *evisceration-mark*. See ; print-ldd-full-or-sketch. Furthermore, if the particular keyword ; chosen is changed, alter *anti-evisceration-mark* below! (defconst *evisceration-hash-mark* (cons *evisceration-mark* "#")) (defconst *evisceration-ellipsis-mark* (cons *evisceration-mark* "...")) (defconst *evisceration-world-mark* (cons *evisceration-mark* "")) (defconst *evisceration-state-mark* (cons *evisceration-mark* "")) (defconst *evisceration-error-triple-marks* (list nil nil *evisceration-state-mark*)) (defconst *evisceration-hiding-mark* (cons *evisceration-mark* "")) (defconst *anti-evisceration-mark* (cons *evisceration-mark* ":EVISCERATION-MARK")) (defmacro evisceratedp (eviscp x) ; Warning: The value of x should be a consp. `(and ,eviscp (eq (car ,x) *evisceration-mark*))) ; Essay on Iprinting ; Through Version_3.4, when ACL2 eviscerated a form using a print-level or ; print-length from an evisc-tuple, the resulting # and ... made it impossible ; to read the form back in. We have implemented "iprinting" (think ; "interactive printing") to deal with this problem. Our implementation uses ; an "iprint array", or "iprint-ar" for short, as described below. Now, when ; iprinting is enabled, then instead of # or ... we will see #@i# for i = 1, 2, ; etc. See :doc set-iprint for more information at the user level. In brief, ; the idea is to maintain a state global 'iprint-ar whose value is an ACL2 ; array that associates each such i with its hidden value. (This use of #@i# ; allows us also to think of "iprinting" as standing for "index printing" or "i ; printing".) ; We implement this idea by modifying the recursive subroutines of eviscerate ; to accumulate each association of a positive i with its hidden value. When ; fmt (or fms, etc.) is called, eviscerate-top or eviscerate-stobjs-top will be ; called in order to update the existing 'iprint-ar with those new ; associations. ; We use index 0 to store the most recent i for which #@i# has been printed, ; assuming iprinting is enabled, or else (list i) if iprinting is disabled. We ; call such i the last-index, and it is initially 0. Note that state global ; 'iprint-ar is thus always bound to an installed ACL2 array. ; We have to face a fundamental question: Do we use acons or aset1 as we ; encounter a new form to assign to some #@i# during those recursive ; subroutines? The latter is dangerous in case we interrupt before installing ; the result in the state global. So it's tempting to use acons -- but it ; could be inefficient to compress the iprint-ar on each top-level call. So ; instead we use acons to build up a new alist from scratch. Then at the ; top level, we apply aset1 for each entry if we can do so without needing to ; ``rollover'', i.e., set the last-index back to 0; otherwise we call compress1 ; rather than making a series of aset1 calls. With luck this final step will ; be fast and unlikely to be interrupted from the time the first aset1 or ; compress1 is applied until the state global 'iprint-ar is updated. ; Let's also comment on why we have a soft and a hard bound (as described in ; :doc set-iprint). In general we allow indices to increase between successive ; top-level invocations, so that the user can read back in any forms that were ; printed. But the soft bound keeps forces a rollover at the top level of LD ; when the last-index exceeds that bound, so that we don't hold on to a ; potentially unbounded amount of space for the objects in the iprint-ar. The ; hard bound (which generally exceeds the soft bound) steps in if the ; last-index exceeds it after pretty-printing a single form. Thus, if there ; are large objects and very long runs between successive top-level forms, ; space can be reclaimed. The hard bound is therefore probably less likely to ; be of use. ; We maintain the invariant that the dimension of state global 'iprint-ar ; exceeds the hard bound. Thus, when we update the 'iprint-ar in the normal ; case that the hard bound is not exceeded, then the dimension will not be ; exceeded either; that is, every update will be with an index that is in ; bounds. In order to maintain this invariant, the hard bound is untouchable, ; and its setter function compresses the global iprint-ar with a new dimension ; that exceeds the specified hard bound. Therefore the hard bound must be a ; number, not nil. Notice that with this invariant, we can avoid compressing ; twice when we roll over upon exceeding the hard or soft bound: we first reset ; the last-index to 0 and then do the compression, rather than compressing once ; for the increased dimension and once for the rollover. ; We also maintain the invariant that the maximum-length of the 'iprint-ar is ; always at least four times its dimension. See the comment about this in ; rollover-iprint-ar. ; It is tempting to cause an error when the user submits a form containing some ; #@j# and #@k# such that j <= last-index < k. In such a case, k is from ; before the rollover and j is from after the rollover, so these couldn't have ; been stored during a prettyprint of the same form. But we avoid considering ; this restriction because the user might want to read a list of forms that ; include some prettyprinted before the last rollover and others printed after ; the last rollover. At any time, the reader is happy with #@j# for any index ; j <= last-index and also any j below the maximum index before the last ; rollover (initially 0). ; We need to be sure that the global iprint-ar is installed as an ACL2 array, in ; order to avoid slow-array-warnings. See the comment in ; push-wormhole-undo-formi for how we deal with this issue in the presence of ; wormholes. ; End of Essay on Iprinting (defconst *sharp-atsign-ar* ; see get-sharp-atsign (let ((dim (1+ *iprint-hard-bound-default*))) (compress1 'sharp-atsign-ar (cons `(:HEADER :DIMENSIONS (,dim) :MAXIMUM-LENGTH ,(1+ dim) ; no duplicates expected :NAME sharp-atsign-ar) (sharp-atsign-alist *iprint-hard-bound-default* nil))))) (defun get-sharp-atsign (i) ; If i is below the hard bound, then we get the string #@i# from a fixed array, ; so that we don't have to keep consing up that string. (declare (xargs :guard (posp i))) (cond ((<= i *iprint-hard-bound-default*) (aref1 'sharp-atsign-ar *sharp-atsign-ar* i)) (t (make-sharp-atsign i)))) (defun update-iprint-alist (iprint-alist val) ; We are doing iprinting. Iprint-alist is either a positive integer, ; representing the last-index but no accumulated iprint-alist, or else is a ; non-empty alist of entries (i . val_i). See the Essay on Iprinting. (cond ((consp iprint-alist) (let ((i (1+ (caar iprint-alist)))) (acons i val iprint-alist))) (t ; iprint-alist is a natp (acons (1+ iprint-alist) val nil)))) ; We now define the most elementary eviscerator, the one that implements ; *print-level* and *print-length*. In this same pass we also arrange to ; hide any object in alist, where alist pairs objects with their ; evisceration strings -- or if not a string, with the appropriate ; evisceration pair. (mutual-recursion (defun eviscerate1 (x v max-v max-n alist evisc-table hiding-cars iprint-alist) ; Iprint-alist is either a symbol, indicating that we are not doing iprinting; a ; positive integer, representing the last-index but no accumulated iprint-alist; ; or an accumulated alist of entries (i . val_i). See the Essay on Iprinting. ; Note that if iprint-alist is a symbol, then it is nil if no evisceration has ; been done based on print-length or print-level, else t. (let ((temp (or (hons-assoc-equal x alist) (hons-assoc-equal x evisc-table)))) (cond ((cdr temp) (mv (cond ((stringp (cdr temp)) (cons *evisceration-mark* (cdr temp))) (t (cdr temp))) iprint-alist)) ((atom x) (mv (cond ((eq x *evisceration-mark*) *anti-evisceration-mark*) (t x)) iprint-alist)) ((= v max-v) (cond ((symbolp iprint-alist) (mv *evisceration-hash-mark* t)) (t (let ((iprint-alist (update-iprint-alist iprint-alist x))) (mv (cons *evisceration-mark* (get-sharp-atsign (caar iprint-alist))) iprint-alist))))) ((member-eq (car x) hiding-cars) (mv *evisceration-hiding-mark* iprint-alist)) (t (eviscerate1-lst x (1+ v) 0 max-v max-n alist evisc-table hiding-cars iprint-alist))))) (defun eviscerate1-lst (lst v n max-v max-n alist evisc-table hiding-cars iprint-alist) (let ((temp (or (hons-assoc-equal lst alist) (hons-assoc-equal lst evisc-table)))) (cond ((cdr temp) (mv (cond ((stringp (cdr temp)) (cons *evisceration-mark* (cdr temp))) (t (cdr temp))) iprint-alist)) ((atom lst) (mv (cond ((eq lst *evisceration-mark*) *anti-evisceration-mark*) (t lst)) iprint-alist)) ((= n max-n) (cond ((symbolp iprint-alist) (mv (list *evisceration-ellipsis-mark*) t)) (t (let ((iprint-alist (update-iprint-alist iprint-alist lst))) (mv (cons *evisceration-mark* (get-sharp-atsign (caar iprint-alist))) iprint-alist))))) (t (mv-let (first iprint-alist) (eviscerate1 (car lst) v max-v max-n alist evisc-table hiding-cars iprint-alist) (mv-let (rest iprint-alist) (eviscerate1-lst (cdr lst) v (1+ n) max-v max-n alist evisc-table hiding-cars iprint-alist) (mv (cons first rest) iprint-alist))))))) ) (mutual-recursion (defun eviscerate1p (x alist evisc-table hiding-cars) ; This function returns t iff (eviscerate1 x 0 -1 -1 alist evisc-table hidep) ; returns something other than x. That is, iff the evisceration of x either ; uses alist, evisc-table, hiding or the *anti-evisceration-mark* (assuming ; that print-level and print-length never max out). (let ((temp (or (hons-assoc-equal x alist) (hons-assoc-equal x evisc-table)))) (cond ((cdr temp) t) ((atom x) (cond ((eq x *evisceration-mark*) t) (t nil))) ((member-eq (car x) hiding-cars) t) (t (eviscerate1p-lst x alist evisc-table hiding-cars))))) (defun eviscerate1p-lst (lst alist evisc-table hiding-cars) (let ((temp (or (hons-assoc-equal lst alist) (hons-assoc-equal lst evisc-table)))) (cond ((cdr temp) t) ((atom lst) (cond ((eq lst *evisceration-mark*) t) (t nil))) (t (or (eviscerate1p (car lst) alist evisc-table hiding-cars) (eviscerate1p-lst (cdr lst) alist evisc-table hiding-cars)))))) ) (defun eviscerate (x print-level print-length alist evisc-table hiding-cars iprint-alist) ; See also eviscerate-top, which takes iprint-ar from the state and installs a ; new iprint-ar in the state, and update-iprint-alist, which describes the role ; of a non-symbol iprint-alist as per the Essay on Iprinting. ; Print-level and print-length should either be non-negative integers or nil. ; Alist and evisc-table are alists pairing arbitrary objects to strings or ; other objects. Hiding-cars is a list of symbols. Any x that starts with one ; of these symbols is printed as . If alist or evisc-table pairs an ; object with a string, the string is printed in place of the object. If alist ; or evisc-table pairs an object with anything else, x, then x is substituted ; for the the object and is treated as eviscerated. In general, alist will ; come from an evisceration tuple and evisc-table will be the value of the ; 'evisc-table table in the current ACL2 world. We give priority to the former ; because the user may want to override the evisc-table, for example using ~P ; in a call of fmt. ; This function copies the structure x and replaces certain deep substructures ; with evisceration marks. The determination of which substructures to so ; abbreviate is based on the same algorithm used to define *print-level* and ; *print-length* in CLTL, with the additional identification of all occurrences ; of any object in alist or evisc-table. ; For example, if x is '(if (member x y) (+ (car x) 3) '(foo . b)) and ; print-level is 2 and print-length is 3 then the output is: ; (IF (MEMBER X Y) ; (+ (*evisceration-mark* . "#") 3) ; (*evisceration-mark* . "...")) ; See pg 373 of CLTL. ; Of course we are supposed to print this as: ; (IF (MEMBER X Y) (+ # 3) ...) ; We consider a couple of special cases to reduce unnecessary consing ; of eviscerated values. (cond ((and (null print-level) (null print-length)) ; Warning: Observe that even if alist is nil, x might contain the ; *evisceration-mark* or hiding expressions and hence have a ; non-trivial evisceration (cond ((eviscerate1p x alist evisc-table hiding-cars) (eviscerate1 x 0 -1 -1 alist evisc-table hiding-cars iprint-alist)) (t (mv x iprint-alist)))) (t (eviscerate1 x 0 (or print-level -1) (or print-length -1) alist evisc-table hiding-cars iprint-alist)))) (defun eviscerate-simple (x print-level print-length alist evisc-table hiding-cars) ; This wrapper for eviscerate avoids the need to pass back multiple values when ; the iprint-alist is nil and we don't care if evisceration has occurred. (mv-let (result null-iprint-alist) (eviscerate x print-level print-length alist evisc-table hiding-cars nil) (assert$ (symbolp null-iprint-alist) result))) (defun aset1-lst (name alist ar) (declare (xargs :guard (eqlable-alistp alist))) ; really nat-alistp (cond ((endp alist) ar) (t (aset1-lst name (cdr alist) (aset1 name ar (caar alist) (cdar alist)))))) ; Next we define accessors for iprint arrays. (defun iprint-hard-bound (state) (f-get-global 'iprint-hard-bound state)) (defun iprint-soft-bound (state) (f-get-global 'iprint-soft-bound state)) (defun iprint-last-index* (iprint-ar) (declare (xargs :guard (array1p 'iprint-ar iprint-ar))) (let ((x (aref1 'iprint-ar iprint-ar 0))) (if (consp x) ; iprinting is disabled (car x) x))) (defun iprint-last-index (state) (iprint-last-index* (f-get-global 'iprint-ar state))) (defun iprint-ar-illegal-index (index state) (declare (xargs :guard (and (natp index) (state-p state)))) (or (zp index) (let* ((iprint-ar (f-get-global 'iprint-ar state)) (bound (default 'iprint-ar iprint-ar))) (if (null bound) (> index (iprint-last-index* iprint-ar)) (> index bound))))) (defun iprint-enabledp (state) (natp (aref1 'iprint-ar (f-get-global 'iprint-ar state) 0))) (defun iprint-ar-aref1 (index state) ; We do not try to determine if the index is appropriate, other than to avoid a ; guard violation on the aref1 call. See the Essay on Iprinting. (declare (xargs :guard (and (posp index) (state-p state)))) (let ((iprint-ar (f-get-global 'iprint-ar state))) ;; PAPER: ; We use a raw Lisp error since otherwise we get an error such as "Can't throw ; to tag RAW-EV-FNCALL". #-acl2-loop-only (cond ((>= index (car (dimensions 'iprint-ar iprint-ar))) ; The following error probably never occurs, since we have already done a ; bounds check with iprint-ar-illegal-index. (error "Out of range index for iprinting: ~s.~%See :DOC set-iprint." index))) (aref1 'iprint-ar iprint-ar index))) (defun collect-posp-indices-to-header (ar acc) ; Accumulates the reverse of ar onto acc, skipping entries with index 0 and ; stopping just before the :header. (cond ((endp ar) (er hard 'collect-posp-indices-to-header "Implementation error: Failed to find :HEADER as expected!")) ((eq (caar ar) :HEADER) acc) (t (collect-posp-indices-to-header (cdr ar) (if (eql (caar ar) 0) acc (cons (car ar) acc)))))) (defun rollover-iprint-ar (iprint-alist last-index state) ; We assume that iprinting is enabled. Install a new iprint-ar, whose last ; index before rollover is intended to be last-index and whose alist is ; intended to extend state global 'iprint-ar, as the new (and compressed) value ; of state global 'iprint-ar. (let* ((old-iprint-ar (f-get-global 'iprint-ar state)) (new-dim ; Clearly last-index exceeds the iprint-hard-bound, as required by one of our ; invariants (see the Essay on Iprinting), if we are rolling over because ; last-index exceeds that hard bound. But we can also call rollover-iprint-ar ; when exceeding the soft bound, which may be smaller than the hard bound (it ; probably is smaller, typically). The taking of this max is cheap so we ; always do it, so that rollover-iprint-ar will always preserve the above ; invariant. ; To illustrate the above point, evaluate the following forms in a fresh ACL2 ; session and see the error if we bind new-dim to (1+ last-index). ; (set-ld-evisc-tuple (evisc-tuple 2 3 nil nil) state) ; (set-iprint t :soft-bound 2 :hard-bound 7) ; '((a b c d e) (a b c d e) (a b c d e)) ; '((a b c d e) (a b c d e) (a b c d e) (a b c d e) (a b c d e)) (1+ (max (iprint-hard-bound state) last-index))) (new-max-len ; A multiplier of 4 allows us to maintain the invariant that the maximum-length ; is always at least four times the dimension. This guarantees that the ; 'iprint-ar alist never reaches the maximum-length because it never reaches ; 4*d, where d is the dimension, as this alist has at most: ; - up to d-2 values for index >= 1 since the latest rollover; ; - up to d-2 values for index >= 1 before the latest rollover; ; - at most two headers (the 2nd is just before a new compression at rollover) ; - no two successive bindings of index 0 ; So without considering index 0, the maximum is (d-2 + d-2 + 2) = 2d-1. Now ; for the bindings of index 0, double that and add one to get 4d-1. ; Thus, since the dimension never decreases (except when we reinitialize), we ; are assured that our use of aset1-lst in update-iprint-ar will never cause a ; recompression. See also corresponding comments in disable-iprint-ar and ; enable-iprint-ar. (* 4 new-dim)) (new-header (prog2$ (or (<= new-max-len *maximum-positive-32-bit-integer*) (er hard 'rollover-iprint-ar "Attempted to expand iprint-ar to a maximum-length of ~x0, ~ exceeding *maximum-positive-32-bit-integer*, which is ~x1." new-max-len *maximum-positive-32-bit-integer*)) `(:HEADER :DIMENSIONS (,new-dim) :MAXIMUM-LENGTH ,new-max-len :DEFAULT ,last-index :NAME iprint-ar :ORDER :none))) (new-iprint-ar (compress1 'iprint-ar (cons new-header (acons 0 0 (collect-posp-indices-to-header old-iprint-ar ; If we change the :order to < from :none, then we need to reverse iprint-alist ; just below. But first read the comment in disable-iprint-ar to see why we ; changing the :order from :none requires some thought. iprint-alist)))))) (f-put-global 'iprint-ar new-iprint-ar state))) (defun update-iprint-ar (iprint-alist state) ; We assume that iprinting is enabled. Iprint-alist is known to be a consp. ; We update state global 'iprint-ar by updating iprint-ar with the pairs in ; iprint-alist. (let ((last-index (caar iprint-alist))) (cond ((> last-index (iprint-hard-bound state)) (rollover-iprint-ar iprint-alist last-index state)) (t (f-put-global 'iprint-ar ; We know last-index <= (iprint-hard-bound state), and it is an invariant that ; this hard bound is less than the dimension of (@ iprint-ar). See the ; discussion of this invariant in the Essay on Iprinting. So last-index is ; less than that dimension, hence we can update with aset1 without encountering ; out-of-bounds indices. (aset1-lst 'iprint-ar (acons 0 last-index iprint-alist) (f-get-global 'iprint-ar state)) state))))) (defun eviscerate-top (x print-level print-length alist evisc-table hiding-cars state) ; We take iprint-ar from the state and then install a new iprint-ar in the state, ; in addition to returning the evisceration of x. See eviscerate and the Essay ; on Iprinting for more details. (mv-let (result iprint-alist) (eviscerate x print-level print-length alist evisc-table hiding-cars (and (iprint-enabledp state) (iprint-last-index state))) (let ((state (cond ((eq iprint-alist t) (f-put-global 'evisc-hitp-without-iprint t state)) ((atom iprint-alist) state) (t (update-iprint-ar iprint-alist state))))) (mv result state)))) (defun world-evisceration-alist (state alist) (let ((wrld (w state))) (cond ((null wrld) ; loading during the build alist) (t (cons (cons wrld *evisceration-world-mark*) alist))))) (defun stobj-print-name (name) (coerce (cons #\< (append (string-downcase1 (coerce (symbol-name name) 'list)) '(#\>))) 'string)) (defun evisceration-stobj-mark (name inputp) ; NAME is a stobj name. We return an evisceration mark that prints as ; ``''. We make a special case out of STATE. (cond (inputp name) ((eq name 'STATE) *evisceration-state-mark*) (t (cons *evisceration-mark* (stobj-print-name name))))) (defun evisceration-stobj-marks1 (stobjs-flags inputp) ; See the comment in eviscerate-stobjs, below. (cond ((null stobjs-flags) nil) ((car stobjs-flags) (cons (evisceration-stobj-mark (car stobjs-flags) inputp) (evisceration-stobj-marks1 (cdr stobjs-flags) inputp))) (t (cons nil (evisceration-stobj-marks1 (cdr stobjs-flags) inputp))))) (defconst *error-triple-sig* '(nil nil state)) (defconst *cmp-sig* '(nil nil)) (defun evisceration-stobj-marks (stobjs-flags inputp) (cond ((equal stobjs-flags *error-triple-sig*) (if inputp *error-triple-sig* *evisceration-error-triple-marks*)) ((equal stobjs-flags '(nil)) '(nil)) (t (evisceration-stobj-marks1 stobjs-flags inputp)))) (defun eviscerate-stobjs1 (estobjs-out lst print-level print-length alist evisc-table hiding-cars iprint-alist) (cond ((null estobjs-out) (mv nil iprint-alist)) ((car estobjs-out) (mv-let (rest iprint-alist) (eviscerate-stobjs1 (cdr estobjs-out) (cdr lst) print-level print-length alist evisc-table hiding-cars iprint-alist) (mv (cons (car estobjs-out) rest) iprint-alist))) (t (mv-let (first iprint-alist) (eviscerate (car lst) print-level print-length alist evisc-table hiding-cars iprint-alist) (mv-let (rest iprint-alist) (eviscerate-stobjs1 (cdr estobjs-out) (cdr lst) print-level print-length alist evisc-table hiding-cars iprint-alist) (mv (cons first rest) iprint-alist)))))) (defun eviscerate-stobjs (estobjs-out lst print-level print-length alist evisc-table hiding-cars iprint-alist) ; See also eviscerate-stobjs-top, which takes iprint-ar from the state and ; installs a new iprint-ar in the state. ; Warning: Right now, we abbreviate all stobjs with the convention. I ; have toyed with the idea of allowing the user to specify how a stobj is to be ; abbreviated on output. This is awkward. See the Essay on Abbreviating Live ; Stobjs below. ; We wish to eviscerate lst with the given print-level, etc., but respecting ; stobjs that we may find in lst. Estobjs-out describes the shape of lst as a ; multiple value vector: if estobjs-out is of length 1, then lst is the single ; result; otherwise, lst is a list of as many elements as estobjs-out is long. ; The non-nil elements of stobjs name the stobjs in lst -- EXCEPT that unlike ; an ordinary ``stobjs-out'', the elements of estobjs-out are evisceration ; marks we are to ``print!'' For example corresponding to the stobjs-out ; setting of '(NIL $MY-STOBJ NIL STATE) is the estobjs-out ; '(NIL ; (:EVISCERATION-MARK . "<$my-stobj>") ; NIL ; (:EVISCERATION-MARK . "")) ; Here, we assume *evisceration-mark* is :EVISCERATION-MARK. (cond ((null estobjs-out) ; Lst is either a single non-stobj output or a list of n non-stobj outputs. We ; eviscerate it without regard for stobjs. (eviscerate lst print-level print-length alist evisc-table hiding-cars iprint-alist)) ((null (cdr estobjs-out)) ; Lst is a single output, which is either a stobj or not depending on whether ; (car stobjs-out) is non-nil. (cond ((car estobjs-out) (mv (car estobjs-out) iprint-alist)) (t (eviscerate lst print-level print-length alist evisc-table hiding-cars iprint-alist)))) (t (eviscerate-stobjs1 estobjs-out lst print-level print-length alist evisc-table hiding-cars iprint-alist)))) (defun eviscerate-stobjs-top (estobjs-out lst print-level print-length alist evisc-table hiding-cars state) ; See eviscerate-stobjs. (mv-let (result iprint-alist) (eviscerate-stobjs estobjs-out lst print-level print-length alist evisc-table hiding-cars (and (iprint-enabledp state) (iprint-last-index state))) (let ((state (cond ((eq iprint-alist t) (f-put-global 'evisc-hitp-without-iprint t state)) ((atom iprint-alist) state) (t (update-iprint-ar iprint-alist state))))) (mv result state)))) ; Essay on Abbreviating Live Stobjs ; Right now the live state is abbreviated as when it is printed, and ; the user's live stobj $s is abbreviated as <$s>. It would be cool if the ; user could specify how he or she wants a stobj displayed, e.g., by selecting ; key components for printing or by providing a function which maps the stobj ; to some non-stobj ``stand-in'' or eviscerated object for printing. ; I have given this matter several hours' thought and abandoned it for the ; moment. I am not convinced it is worth the trouble. It IS a lot of trouble. ; We eviscerate stobjs in the read-eval-print loop. (Through Version_4.3, we ; also eviscerated stobjs in a very low-level place: ev-fncall-msg (and its ; more pervasive friend, ev-fncall-guard-er), used to print stobjs involved in ; calls of functions on args that violate a guard.) ; Every stobj must have some ``stand-in transformer'' function, fn. We will ; typically be holding a stobj name, e.g., $S, and a live value, val, e.g., ; (#(777) #(1 2 3 ...)), and wish to obtain some ACL2 object to print in place ; of the value. This value is obtained by applying fn to val. The two main ; issues I see are ; (a) where do we find fn? The candidate places are state, world, and val ; itself. But we do not have state available in the low-level code. ; (b) how do we apply fn to val? The obvious thing is to call trans-eval or do ; an ev-fncall. Again, we need state. Furthermore, depending on how we do it, ; we have to fight a syntactic battle of ``casting'' an arbitrary object, val, ; to a stobj of type name, to apply a function which has a STOBJS-IN of (name). ; A more important problem is the one of order-of-definition. Which is defined ; first: how to eviscerate a stobj or how to evaluate a form? Stobj ; evisceration calls evaluation to apply fn, but evaluation calls stobj ; evisceration to report guard errors. ; Is user-specified stobj abbreviation really worth the trouble? ; One idea that presents itself is that val ``knows how to abbreviate itself.'' ; I think this is akin to the idea of having a :program mode function, say ; stobj-standin, which syntactically takes a non-stobj and returns a non-stobj. ; Actually, stobj-standin would be called on val. It is clear that I could ; define this function in raw lisp: look in *the-live-state* to determine how ; to abbreviate val and then just do it. But what would be the logical ; definition of it? We could leave it undefined, or defined to be an undefined ; function. Until we admit the whole ACL2 system :logically, we could even ; define it in the logic to be t even though it really returned something else, ; since as a :program its logical definition is irrelevant. But at the moment ; I don't think ACL2 has a precedent for such a function and I don't think ; user-specified stobj abbreviation is justification enough for doing it. ; End of Essay on Abbreviating Live Stobjs ; Now we lay down some macros that help with the efficiency of the FMT ; functions, by making it easy to declare various formals and function values ; to be fixnums. See the Essay on Fixnum Declarations. (defmacro mv-letc (vars form body) `(mv-let ,vars ,form (declare (type (signed-byte 30) col)) ,body)) (defmacro er-hard-val (val &rest args) ; Use (er-hard-val val ctx str ...) instead of (er hard? ctx str ...) ; when there is an expectation on the return type, which should be the ; type of val. Compilation with the cmulisp compiler produces many ; warnings if we do not use some such device. `(prog2$ (er hard? ,@args) ,val)) (defmacro the-fixnum! (n ctx) ; See also the-half-fixnum!. (let ((upper-bound (fixnum-bound))) (declare (type (signed-byte 30) upper-bound)) (let ((lower-bound (- (1+ upper-bound)))) (declare (type (signed-byte 30) lower-bound)) `(the-fixnum (let ((n ,n)) (if (and (<= n ,upper-bound) (>= n ,lower-bound)) n (er-hard-val 0 ,ctx "The object ~x0 is not a fixnum ~ (precisely: not a (signed-byte 30))." n))))))) (defmacro the-half-fixnum! (n ctx) ; Same as the-fixnum!, but leaves some room. (let ((upper-bound (floor (fixnum-bound) 2))) ; (1- (expt 2 28)) (declare (type (signed-byte 29) upper-bound)) (let ((lower-bound (- (1+ upper-bound)))) (declare (type (signed-byte 29) lower-bound)) `(the-fixnum (let ((n ,n)) (if (and (<= n ,upper-bound) (>= n ,lower-bound)) n (er-hard-val 0 ,ctx "The object ~x0 is not a `half-fixnum' ~ (precisely: not a (signed-byte 29))." n))))))) (defmacro the-unsigned-byte! (bits n ctx) `(the (unsigned-byte ,bits) (let ((n ,n) (bits ,bits)) (if (unsigned-byte-p bits n) n (er-hard-val 0 ,ctx "The object ~x0 is not an (unsigned-byte ~x1)." n bits))))) (defmacro the-string! (s ctx) `(if (stringp ,s) (the string ,s) (er-hard-val "" ,ctx "Not a string: ~s0." ,s))) (defun xxxjoin-fixnum (fn args root) ; This is rather like xxxjoin, but we wrap the-fixnum around all ; arguments. (declare (xargs :guard (true-listp args))) (if (cdr args) (list 'the-fixnum (list fn (list 'the-fixnum (car args)) (xxxjoin-fixnum fn (cdr args) root))) (if args ; one arg (list 'the-fixnum (car args)) root))) (defmacro +f (&rest args) (xxxjoin-fixnum '+ args 0)) (defmacro -f (arg1 &optional arg2) (if arg2 `(the-fixnum (- (the-fixnum ,arg1) (the-fixnum ,arg2))) `(the-fixnum (- (the-fixnum ,arg1))))) (defmacro 1-f (x) (list 'the-fixnum (list '1- (list 'the-fixnum x)))) (defmacro 1+f (x) (list 'the-fixnum (list '1+ (list 'the-fixnum x)))) (defmacro charf (s i) (list 'the 'character (list 'char s i))) (defmacro *f (&rest args) (xxxjoin-fixnum '* args 1)) ; Essay on the ACL2 Prettyprinter ; The ACL2 prettyprinter is a two pass, linear time, exact prettyprinter. By ; "exact" we mean that if it has a page of width w and a big enough form, it ; will guarantee to use all the columns, i.e., the widest line will end in ; column w. The algorithm dates from about 1971 -- virtually the same code was ; in the earliest Edinburgh Pure Lisp Theorem Prover. This approach to ; prettyprinting was invented by Bob Boyer; see ; http://www.cs.utexas.edu/~boyer/pretty-print.pdf. Most prettyprinters are ; quadratic and inexact. ; The secret to this method is to make two linear passes, ppr1 and ppr2. The ; first pass builds a data structure, called a ``ppr tuple,'' that tells the ; second pass how to print. ; Some additional general principles of our prettyprinter are ; (i) Print flat whenever possible. ; (ii) However, don't print flat argument lists of length over 40; they're ; too hard to parse. (But this can be overridden by state global ; ppr-flat-right-margin.) ; (iii) Atoms and eviscerated things (which print like atoms, e.g., `') ; may be printed on a single line. ; (iv) But parenthesized expressions should not be printed on a line with any ; other argument (unless the whole form fits on the line). Thus we may ; produce: ; `(foo (bar a) b c d)' ; and ; `(foo a b ; c d)' ; But we never produce ; `(foo (bar a) b ; c d)' ; preferring instead ; `(foo (bar a) ; b c d)' ; It is our belief that parenthesized expressions are hard to parse and ; after doing so the eye tends to miss little atoms (like b above) ; hiding in their shadows. ; To play with ppr we recommend executing this form: ; (ppr2 (ppr1 x (print-base) (print-radix) 30 0 state t) ; 0 *standard-co* state t) ; This will prettyprint x on a page of width 30, assuming that printing starts ; in column 0. To see the ppr tuple that drives the printer, just evaluate the ; inner ppr1 form, ; (ppr1 x (print-base) (print-radix) 30 0 state nil). ; The following test macro is handy. A typical call of the macro is ; (test 15 (foo (bar x) (mum :key1 val1 :key2 :val2))) ; Note that x is not evaluated. If you want to evaluate x and ppr the value, ; use ; (testfn 10 ; (eviscerate-simple `(foo (bar x) ; (mum :key1 :val1 :key2 :val2) ; ',(w state)) ; nil nil ; print-level and print-length ; (world-evisceration-alist state nil) ; nil ; nil) ; state) ; Note that x may be eviscerated, i.e., eviscerated objects in x are printed in ; their short form, not literally. ; (defun testfn (d x state) ; (declare (xargs :mode :program :stobjs (state))) ; (let ((tuple (ppr1 x (print-base) (print-radix) d 0 state t))) ; (pprogn ; (fms "~%Tuple: ~x0~%Output:~%" (list (cons #\0 tuple)) ; *standard-co* state nil) ; (ppr2 tuple 0 *standard-co* state t) ; (fms "~%" nil *standard-co* state nil)))) ; ; (defmacro test (d x) ; Ppr tuples record enough information about the widths of various forms so ; that it can be computed without having to recompute any part of it and so ; that the second pass can print without having to count characters. ; A ppr tuple has the form (token n . z). In the display below, the variables ; ti represent ppr tuples and the variables xi represent objects to be printed ; directly. Any xi could an eviscerated object, a list whose car is the ; evisceration mark. ; (FLAT n x1 ... xk) - Print the xi, separated by spaces, all on one ; line. The total width of output will be n. ; Note that k >= 1. Note also that such a FLAT ; represents k objects. A special case is (FLAT ; n x1), which represents one object. We make ; this observation because sometimes (in ; cons-ppr1) we `just know' that k=1 and the ; reason is: we know the FLAT we're holding ; represents a single object. ; (FLAT n x1... . xk)- Print the xi, separated by spaces, with xk ; separated by `. ', all on one line. Here xk ; is at atom or an eviscerated object. ; (FLAT n . xk) - Here, xk is an atom (or an eviscerated object). ; Print a dot, a space, and xk. The width will ; be n. Note that this FLAT does not actually ; represent an object. That is, no Lisp object ; prints as `. xk'. ; Note: All three forms of FLAT are really just (FLAT n . x) where x is a ; possibly improper list and the elements of x (and its final cdr) are printed, ; separated appropriately by spaces or dot. ; (MATCHED-KEYWORD n x1) ; - Exactly like (FLAT n x1), i.e., prints x1, ; but by virtue of being different from FLAT ; no other xi's are ever added. In this tuple, ; x1 is always a keyword and it will appear on ; a line by itself. Its associated value will ; appear below it in the column because we tried ; to put them on the same line but we did not have ; room. ; (DOT 1) - Print a dot. ; (QUOTE n . t1) - Print a single-quote followed by pretty- ; printing the ppr tuple t1. ; (WIDE n t1 t2 ...) - Here, t1 is a FLAT tuple of width j. We ; print an open paren, the contents of t1, a ; space, and then we prettyprint each of the ; remaining ti in a column. When we're done, we ; print a close paren. The width of the longest ; line we will print is n. ; (i n t1 ...) - We print an open paren, prettyprint t1, then ; do a newline. Then we prettyprint the ; remaining ti in the column that is i to the ; right of the paren. We conclude with a close ; paren. The width of the longest line we will ; print is n. We call this an `indent tuple'. ; (KEYPAIR n t1 . t2)- Here, t1 is a FLAT tuple of width j. We print ; t1, a space, and then prettyprint t2. The ; length of the longest line we will print is n. ; The sentences "The length of the longest line we will print is n." ; bears explanation. Consider ; (FOO (BAR X) ; (MUMBLE Y) ; Z) ;|<- 15 chars ->| ; 123456789012345 ; The length of the longest line, n, is 15. That is, the length of the longest ; line counts the spaces from the start of the printing. In the case of a ; KEYPAIR tuple: ; :KEY (FOO ; (BAR X) ; Y) ;|<- 13 ->| ; we count the spaces from the beginning of the keyword. That is, we consider ; the whole block of text. ; Below we print test-term in two different widths, and display the ppr tuple ; that drives each of the two printings. ; (assign test-term ; '(FFF (GGG (HHH (QUOTE (A . B)))) ; (III YYY ZZZ))) ; ; ; (ppr2 (ppr1 (@ test-term) (print-base) (print-radix) 30 0 state nil) 0 ; *standard-co* state nil) ; ; => ; (FFF (GGG (HHH '(A . B))) (WIDE 25 (FLAT 3 FFF) ; (III YYY ZZZ)) (FLAT 20 (GGG (HHH '(A . B)))) ; (FLAT 14 (III YYY ZZZ))) ; <- 25 ->| ; ; (ppr2 (ppr1 (@ test-term) (print-base) (print-radix) 20 0 state nil) 0 ; *standard-co* state nil) ; ; => ; (FFF (1 20 (FLAT 3 FFF) ; (GGG (4 19 (FLAT 3 GGG) ; (HHH '(A . B))) (FLAT 15 (HHH '(A . B)))) ; (III YYY ZZZ)) (FLAT 14 (III YYY ZZZ))) ; ; <- 20 ->| ; The function cons-ppr1, below, is the first interesting function in the nest. ; We want to build a tuple to print a given list form, like a function call. ; We basically get the tuple for the car and a list of tuples for the cdr and ; then use cons-ppr1 to combine them. The resulting list of tuples will be ; embedded in either a WIDE or an indent tuple. Thus, this list of tuples we ; will create describes a column of forms. The number of items in that column ; is not necessarily the same as the number of arguments of the function call. ; For example, the term (f a b c) might be prettyprinted as ; (f a ; b c) ; where b and c are printed flat on a single line. Thus, the three arguments ; of f end up being described by a list of two tuples, one for a and another ; for b and c. ; To form lists of tuples we just use cons-ppr1 to combine the tuples we get ; for each element. ; Let x and lst be, respectively, a ppr tuple for an element and a list of ; tuples for list of elements. Think of lst as describing a column of forms. ; Either x can become another item that column, or else x can be incorporated ; into the first item in that column. For example, suppose x will print as X ; and lst will print as a column containing y1, y2, etc. Then we have this ; choice for printing x and lst: ; lengthened column lengthened first row ; x x y1 ; y1 y2 ; ... ... ; We get the `lengthened column' behavior if we just cons x onto lst. We get ; the `lengthened row' behavior if we merge the tuples for x and y1. But we ; only merge if they both print flat. ; Essay on the Printing of Dotted Pairs and ; It is instructive to realize that we print a dotted pair as though it were a ; list of length 3 and the dot was just a normal argument. ; In the little table below I show, for various values of d, two things: the ; characters output by ; (ppr2 (ppr1 `(xx . yy) (print-base) (print-radix) d 0 state nil) ; 0 *standard-co* state nil) ; and the ppr tuple produced by the ppr1 call. ; ; d output ppr tuple ; |<- 9 ->| ; 9 (XX . YY) (FLAT 9 (XX . YY)) ; 8 (XX (3 8 (FLAT 2 XX) (FLAT 5 . YY)) ; . YY) ; 7 (XX (2 7 (FLAT 2 XX) (FLAT 5 . YY)) ; . YY) ; 6 (XX (1 6 (FLAT 2 XX) (FLAT 5 . YY)) ; . YY) ; 5 (XX (2 5 (FLAT 2 XX) (DOT 1) (FLAT 3 YY)) ; . ; YY) ; 4 (XX (1 4 (FLAT 2 XX) (DOT 1) (FLAT 3 YY)) ; . ; YY) ; The fact that the dot is not necessarily connected to (on the same line as) ; the atom following it is the reason we have the (DOT 1) tuple. We have to ; represent the dot so that its placement is first class. So when we're ; assembling the tuple for a list, we cdr down the list using cons-ppr1 to put ; together the tuple for the car with the tuple for the cdr. If we reach a ; non-nil cdr, atm, we call cons-ppr1 on the dot tuple and the tuple ; representing the atm. Depending on the width we have, this may produce (FLAT ; n . atm) which attaches the dot to the atm, or ((DOT 1) (FLAT n atm)) which ; leaves the dot on a line by itself. ; We want keywords to appear on new lines. That means if the first element of ; lst is a keyword, don't merge (unless x is one too). ; BUG ; ACL2 p!>(let ((x '(foo bigggggggggggggggg . :littlllllllllllllle))) ; (ppr2 (ppr1 x (print-base) (print-radix) 40 0 state nil) ; 0 *standard-co* state nil)) ; (x = (DOT 1) ; lst = ((FLAT 21 :LITTLLLLLLLLLLLLLLE)) ; val = ((FLAT 23 . :LITTLLLLLLLLLLLLLLE))) ; ; HARD ACL2 ERROR in CONS-PPR1: I thought I could force it! (defmacro ppr-flat-right-margin () '(f-get-global 'ppr-flat-right-margin state)) (defun set-ppr-flat-right-margin (val state) (if (posp val) (f-put-global 'ppr-flat-right-margin val state) (prog2$ (illegal 'set-ppr-flat-right-margin "Set-ppr-flat-right-margin takes a positive integer ~ argument, unlike ~x0." (list (cons #\0 val))) state))) ; Note: In the function below, column is NOT a number! Often in this code, ; ``col'' is used to represent the position of the character column into which ; we are printing. But ``column'' is a list of ppr tuples. (defun keyword-param-valuep (tuple eviscp) ; We return t iff tuple represents a single object that could plausibly be the ; value of a keyword parameter. The (or i ii iii iv) below checks that tuple ; represents a single object, either by being (i) a FLAT tuple listing exactly ; one object (ii) a QUOTE tuple, (iii) a WIDE tuple, or (iv) an indent tuple. ; The only other kinds of tuples are KEYPAIR tuples, FLAT tuples representing ; dotted objects `. atm', FLAT tuples representing several objects `a b c', and ; MATCHED-KEYWORD tuples representing keywords whose associated values are on ; the next line. These wouldn't be provided as the value of a keyword ; argument. (or (and (eq (car tuple) 'flat) (not (or (atom (cddr tuple)) ; tuple is `. atm' (evisceratedp eviscp (cddr tuple)))) (null (cdr (cddr tuple)))) (eq (car tuple) 'quote) (eq (car tuple) 'wide) (integerp (car tuple)))) (defun cons-ppr1 (x column width ppr-flat-right-margin eviscp) ; Here, x is a ppr tuple representing either a dot or a single object and ; column is a list of tuples corresponding to a list of objects (possibly a ; list of length greater than that of column). Intuitively, column will print ; as a column of objects and we want to add x to that column, either by ; extending the top row or adding a new row. In the most typical case, x might ; be (FLAT 3 ABC) and column is ((FLAT 7 DEF GHI) (...)). Thus our choices ; would be to produce ; lengthened column lengthened first row ; ABC ABC DEF GHI ; DEF GHI (...) ; (...) ; It is also here that we deal specially with keywords. If x is ; (FLAT 3 :ABC) and column is ((...) (...)) then we have the choice: ; lengthened column lengthened first row ; :ABC :ABC (...) ; (...) (...) ; (...) ; The default behavior is always to lengthen the column, which is just to cons ; x onto column. (cond ((and (eq (car x) 'flat) ; Note: Since x represents a dot or an object, we know that it is not of the ; form (FLAT n . atm). Thus, (cddr x) is a list of length 1 containing a ; single (possibly eviscerated) object, x1. If that object is an atom (or ; prints like one) we'll consider merging it with whatever else is on the first ; row. (or (atom (car (cddr x))) (evisceratedp eviscp (car (cddr x)))) (consp column)) (let ((x1 (car (cddr x))) (row1 (car column))) ; We know x represents the atom x1 (actually, x1 may be an eviscerated object, ; but if so it prints flat like an atom, e.g., `'). Furthermore, we ; know column is non-empty and so has a first element, e.g., row1. (cond ((keywordp x1) ; So x1 is a keyword. Are we looking at a keypair? We are if row1 represents ; a single value. By a ``single value'' we mean a single object that can be ; taken as the value of the keyword x1. If row1 represents a sequence of more ; than one object, e.g., (FLAT 5 a b c), then we are not in a keypair situation ; because keyword argument lists must be keyword/value pairs all the way down ; and we form these columns bottom up, so if b were a keyword in the proper ; context, we would have paired it with c as keypair, not merged it, or we ; would have put it in a MATCHED-KEYWORD, indicating that its associated value ; is below it in the column. If row1 does not represent a single value we act ; just like x1 had not been a keyword, i.e., we try to merge it with row1. ; This will shut down subsequent attempts to create keypairs above us. (cond ((and (keyword-param-valuep row1 eviscp) (or (null (cdr column)) (eq (car (cadr column)) 'keypair) (eq (car (cadr column)) 'matched-keyword))) ; So x1 is a keyword, row1 represents a keyword parameter value, and ; the rest of the column represents keyword/value pairs. The last ; test is made by just checking the item on the column below row1. It ; would only be a keyword/value pair if the whole column consisted of ; those. We consider making a keypair of width n = width of key, plus ; space, plus width of widest line in row1. Note that we don't mind ; this running over the standard 40 character max line length because ; it is so iconic. (let ((n (+ (cadr x) (+ 1 (cadr row1))))) (cond ((<= n width) (cons (cons 'keypair (cons n (cons x row1))) (cdr column))) ; Otherwise, we put x on a newline and leave the column as it was. Note that ; we convert x from a FLAT to a MATCHED-KEYWORD, so insure that it stays on a ; line by itself and to keyword/value pairs encountered above us in the ; bottom-up processing to be paired with KEYPAIR. (t (cons (cons 'MATCHED-KEYWORD (cdr x)) column))))) ; In this case, we are not in the context of a keyword/value argument even ; though x is a keyword. So we act just like x is not a keyword and see ; whether we can merge it with row1. We merge only if row1 is FLAT already and ; the width of the merged row is acceptable. Even if row1 prints as `. atm' we ; will merge, giving rise to such displays as ; (foo a b c ; d e f . atm) ((eq (car row1) 'flat) (let ((n (+ (cadr x) (+ 1 (cadr row1))))) (cond ((and (<= n ppr-flat-right-margin) (<= n width)) (cons (cons 'flat (cons n (cons x1 (cddr row1)))) (cdr column))) (t (cons x column))))) (t (cons x column)))) ; In this case, x1 is not a keyword. But it is known to print in atom-like ; way, e.g., `ABC' or `'. So we try a simple merge following the same ; scheme as above. ((eq (car row1) 'flat) (let ((n (+ (cadr x) (+ 1 (cadr row1))))) (cond ((and (<= n ppr-flat-right-margin) (<= n width)) (cons (cons 'flat (cons n (cons x1 (cddr row1)))) (cdr column))) (t (cons x column))))) (t (cons x column))))) ((and (eq (car x) 'dot) (consp column)) (let ((row1 (car column))) (cond ((eq (car row1) 'flat) ; In this case we know (car (cddr row1)) is an atom (or an eviscerated object) ; and it becomes the cddr of the car of the answer, which puts the dot on the ; same line as the terminal cdr. (let ((n (+ (cadr x) (+ 1 (cadr row1))))) (cond ((and (<= n ppr-flat-right-margin) (<= n width)) (cons (cons 'flat (cons n (car (cddr row1)))) (cdr column))) (t (cons x column))))) (t (cons x column))))) ; In this case, x1 does not print flat. So we add a new row. (t (cons x column)))) (defun flsz-integer (x print-base acc) (declare (type (unsigned-byte 5) print-base) (type (signed-byte 30) acc) (xargs :guard (print-base-p print-base))) (the-fixnum (cond ((< x 0) (flsz-integer (- x) print-base (1+f acc))) ((< x print-base) (1+f acc)) (t (flsz-integer (truncate x print-base) print-base (1+f acc)))))) (defun flsz-atom (x print-base print-radix acc state) (declare (type (unsigned-byte 5) print-base) (type (signed-byte 30) acc)) (the-fixnum (cond ((> acc (the (signed-byte 30) 100000)) ; In order to make it very simple to guarantee that flsz and flsz-atom return ; fixnums, we ensure that acc is small enough below. We could certainly ; provide a much more generous bound, but 100,000 seems safe at the moment! 100000) ((integerp x) (flsz-integer x print-base (cond ((null print-radix) acc) ((int= print-base 10) ; `.' suffix (+f 1 acc)) (t ; #b, #o, or #x prefix (+f 2 acc))))) ((symbolp x) ; For symbols we add together the length of the "package part" and the symbol ; name part. We include the colons in the package part. (+f (cond ((keywordp x) (1+f acc)) ((or (equal (symbol-package-name x) (f-get-global 'current-package state)) (member-eq x (package-entry-imports (find-package-entry (f-get-global 'current-package state) (known-package-alist state))))) acc) (t (let ((p (symbol-package-name x))) (cond ((needs-slashes p state) (+f 4 acc (the-half-fixnum! (length p) 'flsz-atom))) (t (+f 2 acc (the-half-fixnum! (length p) 'flsz-atom))))))) (let ((s (symbol-name x))) (cond ((needs-slashes s state) (+f 2 (the-half-fixnum! (length s) 'flsz-atom))) (t (+f (the-half-fixnum! (length s) 'flsz-atom))))))) ((rationalp x) (flsz-integer (numerator x) print-base (flsz-integer (denominator x) print-base (cond ((null print-radix) (+f 1 acc)) ((int= print-base 10) ; #10r prefix (+f 5 acc)) (t ; #b, #o, or #x prefix (+f 3 acc)))))) ((complex-rationalp x) (flsz-atom (realpart x) print-base print-radix (flsz-atom (imagpart x) print-base print-radix acc state) state)) ((stringp x) (+f 2 acc (the-half-fixnum! (length x) 'flsz-atom))) ((characterp x) (+f acc (cond ((eql x #\Newline) 9) ((eql x #\Rubout) 8) ((eql x #\Space) 7) ((eql x #\Page) 6) ((eql x #\Tab) 5) (t 3)))) (t 0)))) (defun flsz1 (x print-base print-radix j maximum state eviscp) ; Actually, maximum should be of type (signed-byte 29). (declare (type (unsigned-byte 5) print-base) (type (signed-byte 30) j maximum)) (the-fixnum (cond ((> j maximum) j) ((atom x) (flsz-atom x print-base print-radix j state)) ((evisceratedp eviscp x) (+f j (the-half-fixnum! (length (cdr x)) 'flsz))) ((atom (cdr x)) (cond ((null (cdr x)) (flsz1 (car x) print-base print-radix (+f 2 j) maximum state eviscp)) (t (flsz1 (cdr x) print-base print-radix (flsz1 (car x) print-base print-radix (+f 5 j) maximum state eviscp) maximum state eviscp)))) ((and (eq (car x) 'quote) (consp (cdr x)) (null (cddr x))) (flsz1 (cadr x) print-base print-radix (+f 1 j) maximum state eviscp)) (t (flsz1 (cdr x) print-base print-radix (flsz1 (car x) print-base print-radix (+f 1 j) maximum state eviscp) maximum state eviscp))))) (defun output-in-infixp (state) (let ((infixp (f-get-global 'infixp state))) (or (eq infixp t) (eq infixp :out)))) (defun flatsize-infix (x print-base print-radix termp j max state eviscp) ; Suppose that printing x flat in infix notation causes k characters to come ; out. Then we return j+k. All answers greater than max are equivalent. ; If you think of j as the column into which you start printing flat, then this ; returns the column you'll print into after printing x. If that column ; exceeds max, which is the right margin, then it doesn't matter by how far it ; exceeds max. ; In our $ infix notation, flat output has two extra chars in it, the $ and ; space. But note that we use infix output only if infixp is t or :out. (declare (ignore termp)) (+ 2 (flsz1 x print-base print-radix j max state eviscp))) (defun flsz (x termp j maximum state eviscp) (cond ((output-in-infixp state) (flatsize-infix x (print-base) (print-radix) termp j maximum state eviscp)) (t (flsz1 x (print-base) (print-radix) j maximum state eviscp)))) (defun max-width (lst maximum) (cond ((null lst) maximum) ((> (cadr (car lst)) maximum) (max-width (cdr lst) (cadr (car lst)))) (t (max-width (cdr lst) maximum)))) (mutual-recursion (defun ppr1 (x print-base print-radix width rpc state eviscp) ; We create a ppr tuple for x, i.e., a list structure that tells us how to ; prettyprint x, in a column of the given width. Rpc stands for `right paren ; count' and is the number of right parens that will follow the printed version ; of x. For example, in printing the x in (f (g (h x)) u) there will always be ; 2 right parens after it. So we cannot let x use the entire available width, ; only the width-2. Rpc would be 2. Eviscp indicates whether we are to think ; of evisc marks as printing as atom-like strings or whether they're just ; themselves as data. (declare (type (signed-byte 30) print-base width rpc)) (let ((sz (flsz1 x print-base print-radix rpc width state eviscp))) (declare (type (signed-byte 30) sz)) (cond ((or (atom x) (evisceratedp eviscp x) (and (<= sz width) (<= sz (ppr-flat-right-margin)))) (cons 'flat (cons sz (list x)))) ((and (eq (car x) 'quote) (consp (cdr x)) (null (cddr x))) (let* ((x1 (ppr1 (cadr x) print-base print-radix (+f width -1) rpc state eviscp))) (cons 'quote (cons (+ 1 (cadr x1)) x1)))) (t (let* ((x1 (ppr1 (car x) print-base print-radix (+f width -1) (the-fixnum (if (null (cdr x)) (+ rpc 1) 0)) state eviscp)) ; If the fn is a symbol (or eviscerated, which we treat as a symbol), then the ; hd-sz is the length of the symbol. Else, hd-sz is nil. Think of (null ; hd-sz) as meaning "fn is a lambda expession". (hd-sz (cond ((or (atom (car x)) (evisceratedp eviscp (car x))) (cadr x1)) (t nil))) ; When printing the cdr of x, give each argument the full width (minus 1 for ; the minimal amount of indenting). Note that x2 contains the ppr tuples for ; the car and the cdr. (x2 (cons x1 (ppr1-lst (cdr x) print-base print-radix (+f width -1) (+f rpc 1) state eviscp))) ; If the fn is a symbol, then we get the maximum width of any single argument. ; Otherwise, we get the maximum width of the fn and its arguments. (maximum (cond (hd-sz (max-width (cdr x2) -1)) (t (max-width x2 -1))))) (cond ((null hd-sz) ; If the fn is lambda, we indent the args by 1 and report the width of the ; whole to be one more than the maximum computed above. (cons 1 (cons (+ 1 maximum) x2))) ((<= (+ hd-sz (+ 2 maximum)) width) ; We can print WIDE if we have room for an open paren, the fn, a space, and the ; widest argument. (cons 'wide (cons (+ hd-sz (+ 2 maximum)) x2))) ((< maximum width) ; If the maximum is less than the width, we can do exact indenting of the ; arguments to make the widest argument come out on the right margin. This ; exactness property is one of the things that makes this algorithm produce ; such beautiful output: we get the largest possible indentation, which makes ; it easy to identify peer arguments. How much do we indent? width-maximum ; will guarantee that the widest argument ends on the right margin. However, ; we believe that it is more pleasing if argument columns occur at regular ; indents. So we limit our indenting to 5 and just give up the white space ; over on the right margin. Note that we compute the width of the whole term ; accordingly. (cons (min 5 (+ width (- maximum))) (cons (+ maximum (min 5 (+ width (- maximum)))) x2))) ; If maximum is not less than width, we indent by 1. (t (cons 1 (cons (+ 1 maximum) x2))))))))) ; The next function computes a ppr tuple for each element of lst. Typically ; these are all arguments to a function. But of course, we prettyprint ; arbitrary constants and so have to handle the case that the list is not a ; true-list. ; If you haven't read about cons-ppr1, above, do so now. (defun ppr1-lst (lst print-base print-radix width rpc state eviscp) (declare (type (signed-byte 30) print-base width rpc)) (cond ((atom lst) ; If the list is empty and null, then nothing is printed (besides the parens ; which are being accounted for otherwise). If the list is terminated by some ; non-nil atom, we will print a dot and the atom. We do that by merging a dot ; tuple into the flat for the atom, if there's room on the line, using ; cons-ppr1. Where this merged flat will go, i.e., will it be indented under ; the car as happens in the Essay on the Printing of Dotted Pairs, is the ; concern of ppr1-lst, not the cons-ppr1. The cons-ppr1 below just produces a ; merged flat containing the dot, if the width permits. (cond ((null lst) nil) (t (cons-ppr1 '(dot 1) (list (ppr1 lst print-base print-radix width rpc state eviscp)) width (ppr-flat-right-margin) eviscp)))) ; The case for an eviscerated terminal cdr is handled the same way. ((evisceratedp eviscp lst) (cons-ppr1 '(dot 1) (list (ppr1 lst print-base print-radix width rpc state eviscp)) width (ppr-flat-right-margin) eviscp)) ; If the list is a true singleton, we just use ppr1 and we pass it the rpc that ; was passed in because this last item will be followed by that many parens on ; the same line. ((null (cdr lst)) (list (ppr1 (car lst) print-base print-radix width rpc state eviscp))) ; Otherwise, we know that the car is followed by more elements. So its rpc is ; 0. (t (cons-ppr1 (ppr1 (car lst) print-base print-radix width 0 state eviscp) (ppr1-lst (cdr lst) print-base print-radix width rpc state eviscp) width (ppr-flat-right-margin) eviscp)))) ) (defun newline (channel state) (princ$ #\Newline channel state)) (defun fmt-hard-right-margin (state) (the-fixnum (f-get-global 'fmt-hard-right-margin state))) (defun fmt-soft-right-margin (state) (the-fixnum (f-get-global 'fmt-soft-right-margin state))) (defun set-fmt-hard-right-margin (n state) ":Doc-Section IO set the right margin for formatted output~/ In this documentation topic we discuss setting of both a ``soft'' and a ``hard'' right margin. ~bv[] Example Forms: (set-fmt-soft-right-margin 55 state) ; set soft right margin to 55 (set-fmt-hard-right-margin 68 state) ; set hard right margin to 68 ~ev[] ~ilc[Fmt] and related functions insert linebreaks when lines get too long. A linebreak is inserted at an aesthetically appropriate point once the column exceeds the value of ~c[(@ fmt-soft-right-margin)]. If however the column exceeds the value of ~c[(@ fmt-hard-right-margin)], then a linebreak is soon inserted. Such a ``hard'' linebreak follows the insertion of a backslash (~c[\\]) character unless ~ilc[fmt!], ~ilc[fms!], or ~ilc[fmt1!] is used, or state global ~c[write-for-read] is true.~/~/" (cond ((and (integerp n) (< 0 n)) (f-put-global 'fmt-hard-right-margin (the-half-fixnum! n 'set-fmt-hard-right-margin) state)) (t (let ((err (er hard 'set-fmt-hard-right-margin "The fmt-hard-right-margin must be a positive ~ integer, but ~x0 is not." n))) (declare (ignore err)) state)))) (defun set-fmt-soft-right-margin (n state) ":Doc-Section IO set the soft right margin for formatted output~/ ~l[set-fmt-hard-right-margin] for a discussion of the soft and hard right margin for formatted output.~/~/" (cond ((and (integerp n) (< 0 n)) (f-put-global 'fmt-soft-right-margin (the-half-fixnum! n 'set-fmt-soft-right-margin) state)) (t (let ((err (er hard 'set-fmt-soft-right-margin "The fmt-soft-right-margin must be a positive ~ integer, but ~x0 is not." n))) (declare (ignore err)) state)))) (defun write-for-read (state) (f-get-global 'write-for-read state)) (defun spaces1 (n col hard-right-margin channel state) (declare (type (signed-byte 30) n col hard-right-margin)) (cond ((<= n 0) state) ((> col hard-right-margin) (pprogn (if (write-for-read state) state (princ$ #\\ channel state)) (newline channel state) (spaces1 n 0 hard-right-margin channel state))) (t (pprogn (princ$ #\Space channel state) (spaces1 (1-f n) (1+f col) hard-right-margin channel state))))) ; The use of *acl2-built-in-spaces-array* to circumvent the call to spaces1 ; under spaces has saved about 25% in GCL and a little more than 50% in ; Allegro. (defun make-spaces-array-rec (n acc) (if (zp n) (cons (cons 0 "") acc) (make-spaces-array-rec (1- n) (cons (cons n (coerce (make-list n :initial-element #\Space) 'string)) acc)))) (defun make-spaces-array (n) (compress1 'acl2-built-in-spaces-array (cons `(:HEADER :DIMENSIONS (,(1+ n)) :MAXIMUM-LENGTH ,(+ 2 n) :DEFAULT nil ; should be ignored :NAME acl2-built-in-spaces-array) (make-spaces-array-rec n nil)))) (defconst *acl2-built-in-spaces-array* ; Keep the 200 below in sync with the code in spaces. (make-spaces-array 200)) (defun spaces (n col channel state) (declare (type (signed-byte 30) n col)) (let ((hard-right-margin (fmt-hard-right-margin state)) (result-col (+f n col))) (declare (type (signed-byte 30) hard-right-margin result-col)) (if (and (<= result-col hard-right-margin) ; Keep the 200 below in sync with the code in *acl2-built-in-spaces-array*. (<= n 200)) ;; actually (1+ hard-right-margin) would do (princ$ (aref1 'acl2-built-in-spaces-array *acl2-built-in-spaces-array* n) channel state) (spaces1 (the-fixnum! n 'spaces) (the-fixnum col) hard-right-margin channel state)))) (mutual-recursion (defun flpr1 (x channel state eviscp) (cond ((atom x) (prin1$ x channel state)) ((evisceratedp eviscp x) (princ$ (cdr x) channel state)) ((and (eq (car x) 'quote) (consp (cdr x)) (null (cddr x))) (pprogn (princ$ #\' channel state) (flpr1 (cadr x) channel state eviscp))) (t (pprogn (princ$ #\( channel state) (flpr11 x channel state eviscp))))) (defun flpr11 (x channel state eviscp) (pprogn (flpr1 (car x) channel state eviscp) (cond ((null (cdr x)) (princ$ #\) channel state)) ((or (atom (cdr x)) (evisceratedp eviscp (cdr x))) (pprogn (princ$ " . " channel state) (flpr1 (cdr x) channel state eviscp) (princ$ #\) channel state))) (t (pprogn (princ$ #\Space channel state) (flpr11 (cdr x) channel state eviscp)))))) ) #-acl2-loop-only (defun-one-output print-flat-infix (x termp file eviscp) ; Print x flat (without terpri's) in infix notation to the open output ; stream file. Give special treatment to :evisceration-mark iff ; eviscp. We only call this function if flatsize-infix assures us ; that x will fit on the line. See the Essay on Evisceration in this ; file to details on that subject. (declare (ignore termp eviscp)) (let ((*print-case* :downcase) (*print-pretty* nil)) (princ "$ " file) (prin1 x file))) (defun flpr (x termp channel state eviscp) #+acl2-loop-only (declare (ignore termp)) #-acl2-loop-only (cond ((and (live-state-p state) (output-in-infixp state)) (print-flat-infix x termp (get-output-stream-from-channel channel) eviscp) (return-from flpr *the-live-state*))) (flpr1 x channel state eviscp)) (defun ppr2-flat (x channel state eviscp) ; We print the elements of x, separated by spaces. If x is a non-nil atom, we ; print a dot and then x. (cond ((null x) state) ((or (atom x) (evisceratedp eviscp x)) (pprogn (princ$ #\. channel state) (princ$ #\Space channel state) (flpr1 x channel state eviscp))) (t (pprogn (flpr1 (car x) channel state eviscp) (cond ((cdr x) (pprogn (princ$ #\Space channel state) (ppr2-flat (cdr x) channel state eviscp))) (t state)))))) (mutual-recursion (defun ppr2-column (lst loc col channel state eviscp) ; We print the elements of lst in a column. The column number is col and we ; assume the print head is currently in column loc, loc <= col. Thus, to ; indent to col we print col-loc spaces. After every element of lst but the ; last, we print a newline. (cond ((null lst) state) (t (pprogn (spaces (+ col (- loc)) loc channel state) (ppr2 (car lst) col channel state eviscp) (cond ((null (cdr lst)) state) (t (pprogn (newline channel state) (ppr2-column (cdr lst) 0 col channel state eviscp)))))))) (defun ppr2 (x col channel state eviscp) ; We interpret the ppr tuple x. (case (car x) (flat (ppr2-flat (cddr x) channel state eviscp)) (matched-keyword (ppr2-flat (cddr x) channel state eviscp)) ; just like flat! (dot (princ$ #\. channel state)) (quote (pprogn (princ$ #\' channel state) (ppr2 (cddr x) (+ 1 col) channel state eviscp))) (keypair (pprogn (ppr2-flat (cddr (car (cddr x))) channel state eviscp) (princ$ #\Space channel state) (ppr2 (cdr (cddr x)) (+ col (+ 1 (cadr (car (cddr x))))) channel state eviscp))) (wide (pprogn (princ$ #\( channel state) (ppr2-flat (cddr (car (cddr x))) channel state eviscp) (ppr2-column (cdr (cddr x)) (+ col (+ 1 (cadr (car (cddr x))))) (+ col (+ 2 (cadr (car (cddr x))))) channel state eviscp) (princ$ #\) channel state))) (otherwise (pprogn (princ$ #\( channel state) (ppr2 (car (cddr x)) (+ col (car x)) channel state eviscp) (cond ((cdr (cddr x)) (pprogn (newline channel state) (ppr2-column (cdr (cddr x)) 0 (+ col (car x)) channel state eviscp) (princ$ #\) channel state))) (t (princ$ #\) channel state))))))) ) ; We used to set *fmt-ppr-indentation* below to 5, but it the indentation was ; sometimes odd because when printing a list, some elements could be indented ; and others not. At any rate, it should be less than the ; fmt-hard-right-margin in order to preserve the invariant that fmt0 is called ; on columns that do not exceed this value. (defconst *fmt-ppr-indentation* 0) (defun ppr (x col channel state eviscp) ; If eviscp is nil, then we pretty print x as given. Otherwise, x has been ; eviscerated and we give special importance to the *evisceration-mark*. NOTE ; WELL: This function does not eviscerate -- it assumes the evisceration has ; been done if needed. (declare (type (signed-byte 30) col)) (let ((fmt-hard-right-margin (fmt-hard-right-margin state))) (declare (type (signed-byte 30) fmt-hard-right-margin)) (cond ((< col fmt-hard-right-margin) (ppr2 (ppr1 x (print-base) (print-radix) (+f fmt-hard-right-margin (-f col)) 0 state eviscp) col channel state eviscp)) (t (let ((er (er hard 'ppr "The `col' argument to ppr must be less than value ~ of the state global variable ~ fmt-hard-right-margin, but ~x0 is not less than ~ ~x1." col fmt-hard-right-margin))) (declare (ignore er)) state))))) (defun scan-past-whitespace (s i maximum) (declare (type (signed-byte 30) i maximum) (type string s)) (the-fixnum (cond ((< i maximum) (cond ((member (charf s i) '(#\Space #\Tab #\Newline)) (scan-past-whitespace s (+f i 1) maximum)) (t i))) (t maximum)))) (defun zero-one-or-more (x) (let ((n (cond ((integerp x) x) (t (length x))))) (case n (0 0) (1 1) (otherwise 2)))) (defun find-alternative-skip (s i maximum) ; This function finds the first character after a list of alternatives. i is ; the value of find-alternative-stop, i.e., it points to the ~ in the ~/ or ~] ; that closed the alternative used. ; Suppose s is "~#7~[ab~/cd~/ef~]acl2". ; 01234567890123456789 ; If i is 11, the answer is 17. ; (declare (type (signed-byte 30) i maximum) (type string s)) (the-fixnum (cond ((< i maximum) (let ((char-s-i (charf s i))) (declare (type character char-s-i)) (case char-s-i (#\~ (let ((char-s-1+i (charf s (1+f i)))) (declare (type character char-s-1+i)) (case char-s-1+i (#\] (+f 2 i)) (#\[ (find-alternative-skip s (find-alternative-skip s (+f 2 i) maximum) maximum)) (otherwise (find-alternative-skip s (+f 2 i) maximum))))) (otherwise (find-alternative-skip s (+f 1 i) maximum))))) (t (er-hard-val 0 'find-alternative-skip "Illegal Fmt Syntax - While looking for the terminating ~ bracket of a tilde alternative directive in the string ~ below we ran off the end of the string.~|~%~x0" s))))) (defun find-alternative-start1 (x s i maximum) (declare (type (signed-byte 30) x i maximum) (type string s)) (the-fixnum (cond ((= x 0) i) ((< i maximum) (let ((char-s-i (charf s i))) (declare (type character char-s-i)) (case char-s-i (#\~ (let ((char-s-1+-i (charf s (1+f i)))) (declare (type character char-s-1+-i)) (case char-s-1+-i (#\/ (find-alternative-start1 (1-f x) s (+f 2 i) maximum)) (#\] (er-hard-val 0 'find-alternative-start1 "Illegal Fmt Syntax -- The tilde directive ~ terminating at position ~x0 of the string below ~ does not have enough alternative clauses. When ~ the terminal bracket was reached we still needed ~ ~#1~[~/1 more alternative~/~x2 more ~ alternatives~].~|~%~x3" i (zero-one-or-more x) x s)) (#\[ (find-alternative-start1 x s (find-alternative-skip s (+f 2 i) maximum) maximum)) (otherwise (find-alternative-start1 x s (+f 2 i) maximum))))) (otherwise (find-alternative-start1 x s (+f 1 i) maximum))))) (t (er-hard-val 0 'find-alternative-start1 "Illegal Fmt Syntax -- While searching for the appropriate ~ alternative clause of a tilde alternative directive in the ~ string below, we ran off the end of the string.~|~%~x0" s))))) (defun fmt-char (s i j maximum err-flg) (declare (type (signed-byte 30) i maximum) ; We only increment i by a small amount, j. (type (integer 0 100) j) (type string s)) (the character (cond ((< (+f i j) maximum) (charf s (+f i j))) (t (prog2$ ; return an arbitrary character (cond (err-flg (er hard 'fmt-char "Illegal Fmt Syntax. The tilde directive at ~ location ~x0 in the fmt string below requires that ~ we look at the character ~x1 further down in the ~ string. But the string terminates at location ~ ~x2.~|~%~x3" i j maximum s)) (t nil)) #\a))))) (defun find-alternative-start (x s i maximum) ; This function returns the index of the first character in the xth ; alternative, assuming i points to the ~ that begins the alternative ; directive. If x is not an integer, we assume it is a non-empty ; list. If its length is 1, pick the 0th alternative. Otherwise, ; pick the 1st. This means we can test on a list to get a "plural" test. ; Suppose s is "~#7~[ab~/cd~/ef~]acl2". The indices into s are ; 01234567890123456789 ; This function is supposed to be called with i=0. Suppose register ; 7 contains a 1. That's the value of x. This function will return ; 9, the index of the beginning of alternative x. (declare (type (signed-byte 30) i maximum) (type string s)) (the-fixnum (let ((x (cond ((integerp x) (the-fixnum! x 'find-alternative-start)) ((and (consp x) (atom (cdr x))) 0) (t 1)))) (declare (type (signed-byte 30) x)) (cond ((not (and (eql (the character (fmt-char s i 3 maximum t)) #\~) (eql (the character (fmt-char s i 4 maximum t)) #\[))) (er-hard-val 0 'find-alternative-start "Illegal Fmt Syntax: The tilde directive at ~x0 in the ~ fmt string below must be followed immediately by ~~[. ~ ~|~%~x1" i s)) (t (find-alternative-start1 x s (+f i 5) maximum)))))) (defun find-alternative-stop (s i maximum) ; This function finds the end of the alternative into which i is ; pointing. i is usually the first character of the current alternative. ; The answer points to the ~ in the ~/ or ~] closing the alternative. ; Suppose s is "~#7~[ab~/cd~/ef~]acl2". ; 01234567890123456789 ; and i is 9. Then the answer is 11. (declare (type (signed-byte 30) i maximum) (type string s)) (the-fixnum (cond ((< i maximum) (let ((char-s-i (charf s i))) (declare (type character char-s-i)) (case char-s-i (#\~ (let ((char-s-1+i (charf s (1+f i)))) (declare (type character char-s-1+i)) (case char-s-1+i (#\/ i) (#\[ (find-alternative-stop s (find-alternative-skip s (+f 2 i) maximum) maximum)) (#\] i) (otherwise (find-alternative-stop s (+f 2 i) maximum))))) (otherwise (find-alternative-stop s (+f 1 i) maximum))))) (t (er-hard-val 0 'find-alternative-stop "Illegal Fmt Syntax -- While looking for the terminating ~ slash of a tilde alternative directive alternative clause ~ in the string below we ran off the end of the string. ~ ~|~%~x0" s))))) (defun punctp (c) (if (member c '(#\. #\, #\: #\; #\? #\! #\) #\])) c nil)) (defun fmt-tilde-s1 (s i maximum col channel state) (declare (type (signed-byte 30) i maximum col) (type string s)) (the2s (signed-byte 30) (cond ((not (< i maximum)) (mv col state)) ((and (> col (fmt-hard-right-margin state)) (not (write-for-read state))) (pprogn (princ$ #\\ channel state) (newline channel state) (fmt-tilde-s1 s i maximum 0 channel state))) (t (let ((c (charf s i)) (fmt-soft-right-margin (fmt-soft-right-margin state))) (declare (type character c) (type (signed-byte 30) fmt-soft-right-margin)) (cond ((and (> col fmt-soft-right-margin) (not (write-for-read state)) (eql c #\Space)) (pprogn (newline channel state) (fmt-tilde-s1 s (scan-past-whitespace s (+f i 1) maximum) maximum 0 channel state))) ((and (> col fmt-soft-right-margin) (not (write-for-read state)) (or (eql c #\-) (eql c #\_)) (not (int= (1+f i) maximum))) ; If we are beyond the soft right margin and we are about to print a ; hyphen or underscore and it is not the last character in the string, ; then print it and do a terpri. If it is the last character, as it ; is in say, the function name "1-", then we don't do the terpri and ; hope there is a better place to break soon. The motivating example ; for this was in seeing a list of function names get printed in a way ; that produced a comma as the first character of the newline, e.g., ; "... EQL, 1+, 1- ; , ZEROP and PLUSP." (pprogn (princ$ c channel state) (if (eql c #\-) state (princ$ #\- channel state)) (newline channel state) (fmt-tilde-s1 s (scan-past-whitespace s (+f i 1) maximum) maximum 0 channel state))) (t (pprogn (princ$ c channel state) (fmt-tilde-s1 s (1+f i) maximum (1+f col) channel state))))))))) (defun fmt-var (s alist i maximum) (declare (type (signed-byte 30) i maximum) (type string s)) (let ((x (assoc (the character (fmt-char s i 2 maximum t)) alist))) (cond (x (cdr x)) (t (er hard 'fmt-var "Unbound Fmt Variable. The tilde directive at location ~x0 ~ in the fmt string below uses the variable ~x1. But ~ this variable is not bound in the association list, ~ ~x2, supplied with the fmt string.~|~%~x3" i (char s (+f i 2)) alist s))))) (defun splat-atom (x print-base print-radix indent col channel state) (let* ((sz (flsz-atom x print-base print-radix 0 state)) (too-bigp (> (+ col sz) (fmt-hard-right-margin state)))) (pprogn (if too-bigp (pprogn (newline channel state) (spaces indent 0 channel state)) state) (prin1$ x channel state) (mv (if too-bigp (+ indent sz) (+ col sz)) state)))) ; Splat, below, prints out an arbitrary ACL2 object flat, introducing ; the single-gritch notation for quote and breaking lines between lexemes ; to avoid going over the hard right margin. It indents all but the first ; line by indent spaces. (mutual-recursion (defun splat (x print-base print-radix indent col channel state) (cond ((atom x) (splat-atom x print-base print-radix indent col channel state)) ((and (eq (car x) 'quote) (consp (cdr x)) (null (cddr x))) (pprogn (princ$ #\' channel state) (splat (cadr x) print-base print-radix indent (1+ col) channel state))) (t (pprogn (princ$ #\( channel state) (splat1 x print-base print-radix indent (1+ col) channel state))))) (defun splat1 (x print-base print-radix indent col channel state) (mv-let (col state) (splat (car x) print-base print-radix indent col channel state) (cond ((null (cdr x)) (pprogn (princ$ #\) channel state) (mv (1+ col) state))) ((atom (cdr x)) (cond ((> (+ 3 col) (fmt-hard-right-margin state)) (pprogn (newline channel state) (spaces indent 0 channel state) (princ$ ". " channel state) (mv-let (col state) (splat (cdr x) print-base print-radix indent (+ indent 2) channel state) (pprogn (princ$ #\) channel state) (mv (1+ col) state))))) (t (pprogn (princ$ " . " channel state) (mv-let (col state) (splat (cdr x) print-base print-radix indent (+ 3 col) channel state) (pprogn (princ$ #\) channel state) (mv (1+ col) state))))))) (t (pprogn (princ$ #\Space channel state) (splat1 (cdr x) print-base print-radix indent (1+ col) channel state)))))) ) (defun number-of-digits (n print-base print-radix) ; We compute the width of the field necessary to express the integer n ; in the given print-base. We assume minus signs are printed but plus ; signs are not. Thus, if n is -123 we return 4, if n is 123 we ; return 3. (cond ((< n 0) (1+ (number-of-digits (abs n) print-base print-radix))) ((< n print-base) (cond ((null print-radix) 1) ((int= print-base 10) ; `.' suffix 2) (t ; #b, #o, or #x prefix 3))) (t (1+ (number-of-digits (floor n print-base) print-base print-radix))))) (defun left-pad-with-blanks (n width col channel state) ; Print the integer n right-justified in a field of width width. ; We return the final column (assuming we started in col) and state. (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (let ((d (the-half-fixnum! (number-of-digits n (print-base) (print-radix)) 'left-pad-with-blanks))) (declare (type (signed-byte 30) d)) (cond ((>= d width) (pprogn (prin1$ n channel state) (mv (+ col d) state))) (t (pprogn (spaces (- width d) col channel state) (prin1$ n channel state) (mv (the-fixnum! (+ col width) 'left-pad-with-blanks) state))))))) (defmacro maybe-newline (body) ; This macro is used in fmt0 to force a newline only when absolutely ; necessary. It knows the locals of fmt0, in particular, col, ; channel, and state. We wrap this macro around code that is about to ; print a character at col. Once upon a time we just started fmt0 ; with a newline if we were past the hard right margin, but that ; produced occasional lines that ended naturally at the hard right ; margin and then had a backslash inserted in anticipation of the 0 ; characters to follow. It was impossible to tell if more characters ; follow because there may be tilde commands between where you are and ; the end of the line, and they may or may not print things. `(mv-letc (col state) (cond ((and (> col (fmt-hard-right-margin state)) (not (write-for-read state))) (pprogn (princ$ #\\ channel state) (newline channel state) (mv 0 state))) (t (mv col state))) ,body)) ; To support the convention that er, fmt, and even individual fmt ; commands such as ~X can control their own evisceration parameters, ; we now introduce the idea of an evisceration tuple, or evisc-tuple. (defun evisc-tuple (print-level print-length alist hiding-cars) ; See :doc set-evisc-tuple for a lot of information about evisc-tuples. Also ; see the Essay on Iprinting for a related topic. ; This is really just a record constructor, but we haven't got defrec ; yet so we do it by hand. See set-evisc-tuple. ; We sometimes write out constant evisc tuples! However they are commented ; nearby with (evisc-tuple ...). ; The primitive consumers of evisc tuples all call eviscerate-top or ; eviscerate-stobjs-top. ; car cadr caddr cadddr ":Doc-Section IO control suppression of details when printing~/ ACL2 output is generally printed in full. However, ACL2 can be directed to abbreviate, or ``eviscerate'', objects before printing them. To ``eviscerate'' an object we replace certain substructures within it by strings that are printed in their stead. Such replacement is made relative to a so-called ``evisc-tuple'', which has four components: ~c[(evisc-tuple print-level print-length alist hiding-cars)] is the same as the value of ~c[(list alist print-level print-length hiding-cars)], and the components are used as follows (with priority order as discussed below). The alist component is used to replace any substructure occurring as a key by the corresponding string. The print-level and print-length are analogous to Common Lisp variables ~c[*print-level*] and ~c[*print-length*], respectively, and cause replacement of substructures deeper than print-level by `~c[#]' and those longer than print-length by `~c[...]'. Finally, any ~ilc[consp] ~c[x] that starts with one of the symbols in ~c[hiding-cars] is printed as ~c[]. The following example illustrates the use of an evisc-tuple that limits the print-level to 3 ~-[] only three descents into list structures are permitted before replacing a subexpression by `~c[#]' ~-[] and limits the print-length to 4 ~-[] only the first four elements of any list structure will be printed before replacing its tail by `~c[...]'. ~bv[] ACL2 !>(fms \"~~x0~~%\" (list (cons #\\0 '((a b ((c d)) e f g) u v w x y))) *standard-co* state (evisc-tuple 3 4 nil nil)) ((A B (#) E ...) U V W ...) ACL2 !> ~ev[] Notice that it is impossible to read the printed value back into ACL2, since there is no way for the ACL2 reader to interpret `~c[#]' or `~c[...]'. To solve this problem, ~pl[set-iprint]. In the above example we pass an evisc-tuple explicitly to a printing function, in this case, ~ilc[fms] (~pl[fmt]). But ACL2 also does its own printing, for example during a proof attempt. There are global evisc-tuples that control ACL2's printing; ~pl[set-evisc-tuple] and ~pl[without-evisc].~/~/" (list alist print-level print-length hiding-cars)) (defun standard-evisc-tuplep (x) (or (null x) (and (true-listp x) (= (length x) 4) (alistp (car x)) (or (null (cadr x)) (integerp (cadr x))) (or (null (caddr x)) (integerp (caddr x))) (symbol-listp (cadddr x))))) (defun abbrev-evisc-tuple (state) ; As of January 2009 the abbrev-evisc-tuple is used in error, warning$, ; observation, pstack, break-on-error, and miscellany such as running commands ; where little output is desired, say for :ubt or rebuild. We don't put this ; complete of a specification into the documentation, however, in case later we ; tweak the set of uses of the abbrev-evisc-tuple. This comment should ; similarly not be viewed as definitive if it is long after January 2009. (let ((evisc-tuple (f-get-global 'abbrev-evisc-tuple state))) (cond ((eq evisc-tuple :default) (cons (world-evisceration-alist state nil) '(5 7 nil))) (t evisc-tuple)))) (defmacro gag-mode () ":Doc-Section Miscellaneous verbosity of proof output~/ Please ~pl[set-gag-mode] for an explanation of gag-mode, which can take any of the following values: ~bv[] (gag-mode) ; generally evaluates to t, nil, or :goals ~ev[]~/~/" '(f-get-global 'gag-mode state)) (defun default-evisc-tuple (state) (prog2$ (cw "NOTE: default-evisc-tuple has been deprecated. Please use ~ abbrev-evisc-tuple instead. If you are seeing this message ~ then you are probably using the acl2-books google repository; ~ please email Matt Kaufmann to find out how to eliminate this ~ message.~|~%") (abbrev-evisc-tuple state))) (defun term-evisc-tuple (flg state) ; This evisceration tuple is used when we are printing terms or lists of terms. ; If state global 'term-evisc-tuple has value other than :default, then we ; return that value. Otherwise: ; We don't hide the world or state because they aren't (usually) found in ; terms. This saves us a little time. If the global value of ; 'eviscerate-hide-terms is t, we print (HIDE ...) as . Otherwise not. ; Flg controls whether we actually eviscerate on the basis of structural depth ; and length. If flg is t we do. The choice of the print-length 4 is ; motivated by the idea of being able to print IF as (IF # # #) rather than (IF ; # # ...). Print-level 3 lets us print a clause as ((NOT (PRIMEP #)) ...) ; rather than ((NOT #) ...). (let ((evisc-tuple (f-get-global 'term-evisc-tuple state))) (cond ((not (eq evisc-tuple :default)) evisc-tuple) ((f-get-global 'eviscerate-hide-terms state) (cond (flg ;;; (evisc-tuple 3 4 nil '(hide)) '(nil 3 4 (hide))) (t ;;; (evisc-tuple nil nil nil '(hide)) '(nil nil nil (hide))))) (flg ;;; (evisc-tuple 3 4 nil nil) '(nil 3 4 nil)) (t nil)))) (defun gag-mode-evisc-tuple (state) (cond ((gag-mode) (let ((val (f-get-global 'gag-mode-evisc-tuple state))) (if (eq val :DEFAULT) nil val))) (t (term-evisc-tuple nil state)))) (defun ld-evisc-tuple (state) ":Doc-Section Miscellaneous determines whether ~ilc[ld] suppresses details when printing~/ ~c[Ld-evisc-tuple] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-evisc-tuple state)] and an updater is ~c[(set-ld-evisc-tuple val state)], although the use of ~ilc[set-evisc-tuple] is preferred for updating. ~c[Ld-evisc-tuple] must be either ~c[nil], which is its initial value, or a legal evisc-tuple: ~pl[set-evisc-tuple].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-evisc-tuple] is one of them. ~ilc[Ld] may print the forms it is evaluating and/or the results of evaluation. Depending on the value of ~c[ld-evisc-tuple] ~ilc[ld] may ``eviscerate'' objects before printing them. ~l[set-evisc-tuple] for a discussion of evisceration and of how other evisc-tuples affect the printing of error messages and warnings, as well as other output not from ~c[ld]." (let ((evisc-tuple (f-get-global 'ld-evisc-tuple state))) (assert$ (not (eq evisc-tuple :default)) ; only abbrev, term evisc-tuples evisc-tuple))) (deflabel eviscerate-hide-terms :doc ":Doc-Section IO to print ~c[(hide ...)] as ~c[]~/ ~bv[] Example: (assign eviscerate-hide-terms t) (assign eviscerate-hide-terms nil) ~ev[]~/ ~c[Eviscerate-hide-terms] is a ~ilc[state] global variable whose value is either ~c[t] or ~c[nil]. The variable affects how terms are displayed by default (but not if you have set the term-evisc-tuple to other than its default; ~pl[set-evisc-tuple]). If ~c[t], terms of the form ~c[(hide ...)] are printed as ~c[]. Otherwise, they are printed normally.") #-acl2-loop-only (defun-one-output print-infix (x termp width rpc col file eviscp) ; X is an s-expression denoting a term (if termp = t) or an evg (if ; termp = nil). File is an open output file. Prettyprint x in infix ; notation to file. If eviscp is t then we are to give special treatment to ; the :evisceration-mark; otherwise not. ; This hook is modeled after the ACL2 pretty-printer, which has the following ; additional features. These features need not be implemented in the infix ; prettyprinter. The printer is assumed to be in column col, where col=0 means ; it is on the left margin. We are supposed to print our first character in ; that column. We are supposed to print in a field of width width. That is, ; the largest column into which we might print is col+width-2. Finally, assume ; that on the last line of the output somebody is going to write rpc additional ; characters and arrange for this not to overflow the col+width-2 limit. Rpc ; is used when, for example, we plan to print some punctuation, like a comma, ; after a form and want to ensure that we can do it without overflowing the ; right margin. (One might think that the desired effect could be obtained by ; setting width smaller, but that is wrong because it narrows the whole field ; and we only want to guarantee space on the last line.) Here is an example. ; Use ctrl-x = in emacs to see what columns things are in. The semi-colons are ; in column 0. Pretend they are all spaces, as they would be if the printing ; had been done by fmt-ppr. ; (foobar ; (here is a long arg) ; a) ; Here, col = 2, width = 23, and rpc = 19! ; Infix Hack: ; We simply print out $ followed by the expression. We print the ; expression in lower-case. (declare (ignore termp width rpc col eviscp)) (let ((*print-case* :downcase) (*print-pretty* t)) (princ "$ " file) (prin1 x file))) (defun fmt-ppr (x termp width rpc col channel state eviscp) (declare (type (signed-byte 30) col)) #+acl2-loop-only (declare (ignore termp)) #-acl2-loop-only (cond ((and (live-state-p state) (output-in-infixp state)) (print-infix x termp width rpc col (get-output-stream-from-channel channel) eviscp) (return-from fmt-ppr *the-live-state*))) (ppr2 (ppr1 x (print-base) (print-radix) width rpc state eviscp) col channel state eviscp)) (mutual-recursion (defun fmt0* (str0 str1 str2 str3 lst alist col channel state evisc-tuple) ; This odd function prints out the members of lst. If the list has no ; elements, str0 is used. If the list has 1 element, str1 is used ; with #\* bound to the element. If the list has two elements, str2 ; is used with #\* bound to the first element and then str1 is used ; with #\* bound to the second. If the list has more than two ; elements, str3 is used with #\* bound successively to each element ; until there are only two left. The function is used in the ; implementation of ~&, ~v, and ~*. (declare (type (signed-byte 30) col) (type string str0 str1 str2 str3)) (the2s (signed-byte 30) (cond ((null lst) (fmt0 str0 alist 0 (the-fixnum! (length str0) 'fmt0*) col channel state evisc-tuple)) ((null (cdr lst)) (fmt0 str1 (cons (cons #\* (car lst)) alist) 0 (the-fixnum! (length str1) 'fmt0*) col channel state evisc-tuple)) ((null (cddr lst)) (mv-letc (col state) (fmt0 str2 (cons (cons #\* (car lst)) alist) 0 (the-fixnum! (length str2) 'fmt0*) col channel state evisc-tuple) (fmt0* str0 str1 str2 str3 (cdr lst) alist col channel state evisc-tuple))) (t (mv-letc (col state) (fmt0 str3 (cons (cons #\* (car lst)) alist) 0 (the-fixnum! (length str3) 'fmt0*) col channel state evisc-tuple) (fmt0* str0 str1 str2 str3 (cdr lst) alist col channel state evisc-tuple)))))) (defun fmt0&v (flg lst punct col channel state evisc-tuple) (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (case flg (& (case punct (#\. (fmt0* "" "~x*." "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (#\, (fmt0* "" "~x*," "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (#\: (fmt0* "" "~x*:" "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (#\; (fmt0* "" "~x*;" "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (#\! (fmt0* "" "~x*!" "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (#\) (fmt0* "" "~x*)" "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (#\? (fmt0* "" "~x*?" "~x* and " "~x*, " lst nil col channel state evisc-tuple)) (otherwise (fmt0* "" "~x*" "~x* and " "~x*, " lst nil col channel state evisc-tuple)))) (otherwise (case punct (#\. (fmt0* "" "~x*." "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (#\, (fmt0* "" "~x*," "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (#\: (fmt0* "" "~x*:" "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (#\; (fmt0* "" "~x*;" "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (#\! (fmt0* "" "~x*!" "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (#\) (fmt0* "" "~x*)" "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (#\? (fmt0* "" "~x*?" "~x* or " "~x*, " lst nil col channel state evisc-tuple)) (otherwise (fmt0* "" "~x*" "~x* or " "~x*, " lst nil col channel state evisc-tuple))))))) (defun spell-number (n cap col channel state evisc-tuple) ; If n is an integerp we spell out the name of the cardinal number n ; (for a few cases) or else we just print the decimal representation ; of n. E.g., n=4 makes us spell "four". If n is a consp then we ; assume its car is an integer and we spell the corresponding ordinal ; number, e.g., n= '(4 . th) makes us spell "fourth". We capitalize ; the word if cap is t. (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (let ((str (cond ((integerp n) (cond ((int= n 0) (if cap "Zero" "zero")) ((int= n 1) (if cap "One" "one")) ((int= n 2) (if cap "Two" "two")) ((int= n 3) (if cap "Three" "three")) ((int= n 4) (if cap "Four" "four")) ((int= n 5) (if cap "Five" "five")) ((int= n 6) (if cap "Six" "six")) ((int= n 7) (if cap "Seven" "seven")) ((int= n 8) (if cap "Eight" "eight")) ((int= n 9) (if cap "Nine" "nine")) ((int= n 10) (if cap "Ten" "ten")) ((int= n 11) (if cap "Eleven" "eleven")) ((int= n 12) (if cap "Twelve" "twelve")) ((int= n 13) (if cap "Thirteen" "thirteen")) (t "~x0"))) ((and (consp n) (<= 0 (car n)) (<= (car n) 13)) (cond ((int= (car n) 0) (if cap "Zeroth" "zeroth")) ((int= (car n) 1) (if cap "First" "first")) ((int= (car n) 2) (if cap "Second" "second")) ((int= (car n) 3) (if cap "Third" "third")) ((int= (car n) 4) (if cap "Fourth" "fourth")) ((int= (car n) 5) (if cap "Fifth" "fifth")) ((int= (car n) 6) (if cap "Sixth" "sixth")) ((int= (car n) 7) (if cap "Seventh" "seventh")) ((int= (car n) 8) (if cap "Eighth" "eighth")) ((int= (car n) 9) (if cap "Ninth" "ninth")) ((int= (car n) 10) (if cap "Tenth" "tenth")) ((int= (car n) 11) (if cap "Eleventh" "eleventh")) ((int= (car n) 12) (if cap "Twelfth" "twelfth")) (t (if cap "Thirteenth" "thirteenth")))) (t (let ((d (mod (abs (car n)) 10))) ; We print -11th, -12th, -13th, ... -20th, -21st, -22nd, etc., though ; what business anyone has using negative ordinals I can't imagine. (cond ((or (int= d 0) (> d 3) (int= (car n) -11) (int= (car n) -12) (int= (car n) -13)) "~x0th") ((int= d 1) "~x0st") ((int= d 2) "~x0nd") (t "~x0rd"))))))) (fmt0 (the-string! str 'spell-number) (cond ((integerp n) (cond ((and (<= 0 n) (<= n 13)) nil) (t (list (cons #\0 n))))) (t (cond ((and (<= 0 (car n)) (<= (car n) 13)) nil) (t (list (cons #\0 (car n))))))) 0 (the-fixnum! (length str) 'spell-number) col channel state evisc-tuple)))) (defun fmt-tilde-s (s col channel state) ; If s is a symbol or a string, we print it out, breaking on hyphens but not ; being fooled by fmt directives inside it. We also allow s to be a number ; (not sure why this was ever allowed, but we continue to support it). We ; return the new col and state. (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (cond ((acl2-numberp s) (pprogn (prin1$ s channel state) (mv (flsz-atom s (print-base) (print-radix) col state) state))) ((stringp s) (fmt-tilde-s1 s 0 (the-fixnum! (length s) 'fmt-tilde-s) col channel state)) (t (let ((str (symbol-name s))) (cond ((keywordp s) (cond ((needs-slashes str state) (splat-atom s (print-base) (print-radix) 0 col channel state)) (t (fmt0 ":~s0" (list (cons #\0 str)) 0 4 col channel state nil)))) ((or (equal (symbol-package-name s) (f-get-global 'current-package state)) (member-eq s (package-entry-imports (find-package-entry (f-get-global 'current-package state) (known-package-alist state))))) (cond ((needs-slashes str state) (splat-atom s (print-base) (print-radix) 0 col channel state)) (t (fmt-tilde-s1 str 0 (the-fixnum! (length str) 'fmt-tilde-s) col channel state)))) (t (let ((p (symbol-package-name s))) (cond ((or (needs-slashes p state) (needs-slashes str state)) (splat-atom s (print-base) (print-radix) 0 col channel state)) (t (fmt0 "~s0::~-~s1" (list (cons #\0 p) (cons #\1 str)) 0 10 col channel state nil))))))))))) (defun fmt0 (s alist i maximum col channel state evisc-tuple) (declare (type (signed-byte 30) i maximum col) (type string s)) ; WARNING: If you add new tilde-directives update :DOC fmt and the ; copies in :DOC fmt1 and :DOC fms. (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (cond ((>= i maximum) (mv (the (signed-byte 30) col) state)) (t (let ((c (charf s i))) (declare (type character c)) (cond ((eql c #\~) (let ((fmc (the character (fmt-char s i 1 maximum t)))) (declare (type character fmc)) (case fmc ((#\p #\q #\P #\Q #\x #\y #\X #\Y) ; The only difference between pqPQ and xyXY is that the former can cause infix ; printing. (But see the comment below about "hyphenate" for how we can cause ; the latter to enable hyphenation.) However, as of this writing (Jan. 2009) ; it is far from clear that infix printing still works; so we consider it to be ; deprecated. Infix printing assumes the term has already been untranslated. ; The difference between the lowercase directives and the uppercase ones is ; that the uppercase ones take two fmt-vars, e.g., ~X01, and use the contents ; of the second one as the evisceration value. Otherwise the uppercase ; directives behave as their lowercase counterparts. ; On symbols, ~x and ~y are alike and just print starting in col. On non- ; symbols they both prettyprint. But ~y starts printing in col while ~x may do ; a terpri and indent first. ~x concludes with a terpri if it put out a terpri ; before printing. ~y always concludes with a terpri on non-symbols, so you ; know where you end up. (maybe-newline (let* ((caps (or (eql fmc #\P) (eql fmc #\Q) (eql fmc #\X) (eql fmc #\Y))) (px (or (eql fmc #\p) (eql fmc #\P) (eql fmc #\x) (eql fmc #\X))) (qy (not px)) (pq (or (eql fmc #\p) (eql fmc #\P) (eql fmc #\q) (eql fmc #\Q))) (local-evisc-tuple (cond (caps (fmt-var s alist (1+f i) maximum)) (t evisc-tuple))) (evisc-table (table-alist 'evisc-table (w state))) (eviscp (or local-evisc-tuple evisc-table))) (mv-let (x state) (cond (eviscp (eviscerate-top (fmt-var s alist i maximum) (cadr local-evisc-tuple) ;;; print-level (caddr local-evisc-tuple) ;;; print-length (car local-evisc-tuple) ;;; alist evisc-table (cadddr local-evisc-tuple) ;;; hiding-cars state)) (t (mv (fmt-var s alist i maximum) state))) ; Through Version_3.4, ACL2 could hyphenate rule names during proof commentary ; because of the following COND branch in the case of ~x/~y/~X/~Y (though ; fmt-symbol-name has since been renamed as fmt-tilde-s). We have decided to ; opt instead for uniform treatment of ~x/~y/~X/~Y and ~p/~q/~P/~Q, modulo ; potential support for infix printing for the latter group (which we may ; eliminate in the future). By avoiding hyphenation we make it easier for a ; user to grab a rule name from the output, though now one might want to do ; some hyphenation by hand when preparing proof output for publication. ; ((and (or (symbolp x) ; (acl2-numberp x)) ; (member-eq fmc '(#\x #\y #\X #\Y))) ; (mv-letc (col state) ; (fmt-tilde-s x col channel state) ; (fmt0 s alist ; (+f i (if (or (eql fmc #\X) ; (eql fmc #\Y)) ; 4 ; 3)) ; maximum col channel state evisc-tuple))) (let ((fmt-hard-right-margin (fmt-hard-right-margin state))) (declare (type (signed-byte 30) fmt-hard-right-margin)) (let ((sz (flsz x pq col fmt-hard-right-margin state eviscp))) (declare (type (signed-byte 30) sz)) (cond ((and px (> col (the-fixnum *fmt-ppr-indentation*)) (>= sz fmt-hard-right-margin) (not (>= (flsz x pq (the-fixnum *fmt-ppr-indentation*) fmt-hard-right-margin state eviscp) fmt-hard-right-margin))) (pprogn (newline channel state) (spaces1 (the-fixnum *fmt-ppr-indentation*) 0 fmt-hard-right-margin channel state) (fmt0 s alist i maximum (the-fixnum *fmt-ppr-indentation*) channel state evisc-tuple))) ((or qy (>= sz fmt-hard-right-margin)) (pprogn (cond (qy state) ((= col 0) state) (t (newline channel state))) (if qy state (spaces1 (the-fixnum *fmt-ppr-indentation*) 0 fmt-hard-right-margin channel state)) (let ((c (fmt-char s i (the-fixnum (if caps 4 3)) maximum nil))) (cond ((punctp c) (pprogn (fmt-ppr x pq (+f fmt-hard-right-margin (-f (if qy col *fmt-ppr-indentation*))) 1 (the-fixnum (if qy col *fmt-ppr-indentation*)) channel state eviscp) (princ$ c channel state) (newline channel state) (fmt0 s alist (scan-past-whitespace s (+f i (if caps 5 4)) maximum) maximum 0 channel state evisc-tuple))) (t (pprogn (fmt-ppr x pq (+f fmt-hard-right-margin (-f (if qy col *fmt-ppr-indentation*))) 0 (the-fixnum (if qy col *fmt-ppr-indentation*)) channel state eviscp) (newline channel state) (fmt0 s alist (scan-past-whitespace s (+f i (if caps 4 3)) maximum) maximum 0 channel state evisc-tuple))))))) (t (pprogn (flpr x pq channel state eviscp) (fmt0 s alist (+f i (if caps 4 3)) maximum sz channel state evisc-tuple)))))))))) (#\@ (let ((s1 (fmt-var s alist i maximum))) (mv-letc (col state) (cond ((stringp s1) (fmt0 s1 alist 0 (the-fixnum! (length s1) 'fmt0) col channel state evisc-tuple)) ((consp s1) (fmt0 (car s1) (append (cdr s1) alist) 0 (the-fixnum! (length (car s1)) 'fmt0) col channel state evisc-tuple)) (t (mv (er-hard-val 0 'fmt0 "Illegal Fmt Syntax. The ~ tilde-@ directive at position ~ ~x0 of the string below is ~ illegal because its variable ~ evaluated to ~x1, which is ~ neither a string nor a ~ list.~|~%~x2" i s1 s) state))) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple)))) (#\# (let ((n (find-alternative-start (fmt-var s alist i maximum) s i maximum))) (declare (type (signed-byte 30) n)) (let ((m (find-alternative-stop s n maximum))) (declare (type (signed-byte 30) m)) (let ((o (find-alternative-skip s m maximum))) (declare (type (signed-byte 30) o)) (mv-letc (col state) (fmt0 s alist (the-fixnum n) (the-fixnum m) col channel state evisc-tuple) (fmt0 s alist (the-fixnum o) maximum col channel state evisc-tuple)))))) (#\* (let ((x (fmt-var s alist i maximum))) (mv-letc (col state) (fmt0* (car x) (cadr x) (caddr x) (cadddr x) (car (cddddr x)) (append (cdr (cddddr x)) alist) col channel state evisc-tuple) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple)))) (#\& (let ((i+3 (+f i 3))) (declare (type (signed-byte 30) i+3)) (mv-letc (col state) (fmt0&v '& (fmt-var s alist i maximum) (punctp (and (< i+3 maximum) (char s i+3))) col channel state evisc-tuple) (fmt0 s alist (the-fixnum (cond ((punctp (and (< i+3 maximum) (char s i+3))) (+f i 4)) (t i+3))) maximum col channel state evisc-tuple)))) (#\v (let ((i+3 (+f i 3))) (declare (type (signed-byte 30) i+3)) (mv-letc (col state) (fmt0&v 'v (fmt-var s alist i maximum) (punctp (and (< i+3 maximum) (char s i+3))) col channel state evisc-tuple) (fmt0 s alist (the-fixnum (cond ((punctp (and (< i+3 maximum) (char s i+3))) (+f i 4)) (t i+3))) maximum col channel state evisc-tuple)))) (#\n (maybe-newline (mv-letc (col state) (spell-number (fmt-var s alist i maximum) nil col channel state evisc-tuple) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple)))) (#\N (maybe-newline (mv-letc (col state) (spell-number (fmt-var s alist i maximum) t col channel state evisc-tuple) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple)))) (#\t (maybe-newline (let ((goal-col (fmt-var s alist i maximum)) (fmt-hard-right-margin (fmt-hard-right-margin state))) (declare (type (signed-byte 30) goal-col fmt-hard-right-margin)) (pprogn (cond ((> goal-col fmt-hard-right-margin) (let ((er (er hard 'fmt0 "It is illegal to tab past the ~ value of (@ ~ fmt-hard-right-margin), ~x0, and ~ hence the directive ~~t~s1 to tab ~ to column ~x2 is illegal. See ~ :DOC set-fmt-hard-right-margin." fmt-hard-right-margin (string (fmt-char s i 2 maximum t)) goal-col))) (declare (ignore er)) state)) ((>= col goal-col) (pprogn (newline channel state) (spaces1 (the-fixnum goal-col) 0 fmt-hard-right-margin channel state))) (t (spaces1 (-f goal-col col) col fmt-hard-right-margin channel state))) (fmt0 s alist (+f i 3) maximum (the-fixnum goal-col) channel state evisc-tuple))))) (#\c (maybe-newline (let ((pair (fmt-var s alist i maximum))) (cond ((and (consp pair) (integerp (car pair)) (integerp (cdr pair)) (>= (cdr pair) 0)) (mv-letc (col state) (left-pad-with-blanks (car pair) (cdr pair) col channel state) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple))) (t (mv (er-hard-val 0 'fmt0 "Illegal Fmt Syntax. The tilde-c ~ directive at position ~x0 of the string ~ below is illegal because its variable ~ evaluated to ~x1, which is not of the ~ form (n . width), where n and width are ~ integers and width is ~ nonnegative.~|~%~x2" i pair s) state)))))) ((#\f #\F) (maybe-newline (mv-letc (col state) (splat (fmt-var s alist i maximum) (print-base) (print-radix) (if (eql fmc #\F) (1+f col) 0) col channel state) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple)))) (#\s (maybe-newline (mv-letc (col state) (fmt-tilde-s (fmt-var s alist i maximum) col channel state) (fmt0 s alist (+f i 3) maximum col channel state evisc-tuple)))) (#\Space (let ((fmt-hard-right-margin (fmt-hard-right-margin state))) (declare (type (signed-byte 30) fmt-hard-right-margin)) (pprogn (cond ((> col fmt-hard-right-margin) (newline channel state)) (t state)) (princ$ #\Space channel state) (fmt0 s alist (+f i 2) maximum (cond ((> col fmt-hard-right-margin) 1) (t (1+f col))) channel state evisc-tuple)))) (#\_ (maybe-newline (let ((fmt-hard-right-margin (fmt-hard-right-margin state))) (declare (type (signed-byte 30) fmt-hard-right-margin)) (let ((n (the-half-fixnum! (fmt-var s alist i maximum) 'fmt0))) (declare (type (signed-byte 30) n)) (let ((new-col (+f col n))) (declare (type (signed-byte 30) new-col)) (pprogn (spaces n col channel state) (cond ((> new-col fmt-hard-right-margin) (newline channel state)) (t state)) (fmt0 s alist (+f i 3) maximum (the-fixnum (cond ((> new-col fmt-hard-right-margin) 0) (t new-col))) channel state evisc-tuple))))))) (#\Newline (fmt0 s alist (scan-past-whitespace s (+f i 2) maximum) maximum col channel state evisc-tuple)) (#\| (pprogn (if (int= col 0) state (newline channel state)) (fmt0 s alist (+f i 2) maximum 0 channel state evisc-tuple))) (#\% (pprogn (newline channel state) (fmt0 s alist (+f i 2) maximum 0 channel state evisc-tuple))) (#\~ (maybe-newline (pprogn (princ$ #\~ channel state) (fmt0 s alist (+f i 2) maximum (1+f col) channel state evisc-tuple)))) (#\- (cond ((> col (fmt-soft-right-margin state)) (pprogn (princ$ #\- channel state) (newline channel state) (fmt0 s alist (scan-past-whitespace s (+f i 2) maximum) maximum 0 channel state evisc-tuple))) (t (fmt0 s alist (+f i 2) maximum col channel state evisc-tuple)))) (otherwise (let ((x (er hard 'fmt0 "Illegal Fmt Syntax. The tilde ~ directive at position ~x0 of the ~ string below is unrecognized.~|~%~x1" i s))) (declare (ignore x)) (mv 0 state)))))) ((and (> col (fmt-soft-right-margin state)) (eql c #\Space)) (pprogn (newline channel state) (fmt0 s alist (scan-past-whitespace s (+f i 1) maximum) maximum 0 channel state evisc-tuple))) ((and (>= col (fmt-soft-right-margin state)) (eql c #\-)) (pprogn (princ$ c channel state) (newline channel state) (fmt0 s alist (scan-past-whitespace s (+f i 1) maximum) maximum 0 channel state evisc-tuple))) ; ((and (eql c #\Space) ; I cut out this code in response to Kaufmann's complaint 38. The idea is ; *not* to ignore spaces after ~% directives. I've left the code here to ; remind me of what I used to do, in case I see output that is malformed. ; (int= col 0)) ; (fmt0 s alist (+f i 1) maximum 0 channel state evisc-tuple)) (t (maybe-newline (pprogn (princ$ c channel state) (fmt0 s alist (+f i 1) maximum (if (eql c #\Newline) 0 (+f col 1)) channel state evisc-tuple)))))))))) ) (defun tilde-*-&v-strings (flg lst punct) ; This function returns an object that when bound to #\0 will cause ; ~*0 to print a conjunction (flg='&) or disjunction (flg='v) of the ; strings in lst, followed by punctuation punct, which must be #\. or ; #\,. ; WARNING: This displayed strings are not equal to the strings in lst ; because whitespace may be inserted! ; ~& doesn't print a list of short strings very well because the first ; group is printed flat across the line, then when the line gets too ; long, the next string is indented and followed by a newline, which ; allows another bunch to be printed flat. This function prints them ; with ~s which actually breaks the strings up internally in a way ; that does not preserve their equality. "history-management.lisp" ; might have a newline inserted after the hyphen. (case flg (& (case punct (#\. (list "" "\"~s*\"." "\"~s*\" and " "\"~s*\", " lst)) (#\, (list "" "\"~s*\"," "\"~s*\" and " "\"~s*\", " lst)) (#\: (list "" "\"~s*\":" "\"~s*\" and " "\"~s*\", " lst)) (#\; (list "" "\"~s*\";" "\"~s*\" and " "\"~s*\", " lst)) (#\! (list "" "\"~s*\"!" "\"~s*\" and " "\"~s*\", " lst)) (#\) (list "" "\"~s*\")" "\"~s*\" and " "\"~s*\", " lst)) (#\? (list "" "\"~s*\"?" "\"~s*\" and " "\"~s*\", " lst)) (otherwise (list "" "\"~s*\"" "\"~s*\" and " "\"~s*\", " lst)))) (otherwise (case punct (#\. (list "" "\"~s*\"." "\"~s*\" or " "\"~s*\", " lst)) (#\, (list "" "\"~s*\"," "\"~s*\" or " "\"~s*\", " lst)) (#\: (list "" "\"~s*\":" "\"~s*\" or " "\"~s*\", " lst)) (#\; (list "" "\"~s*\";" "\"~s*\" or " "\"~s*\", " lst)) (#\! (list "" "\"~s*\"!" "\"~s*\" or " "\"~s*\", " lst)) (#\) (list "" "\"~s*\")" "\"~s*\" or " "\"~s*\", " lst)) (#\? (list "" "\"~s*\"?" "\"~s*\" or " "\"~s*\", " lst)) (otherwise (list "" "\"~s*\"" "\"~s*\" or " "\"~s*\", " lst)))))) (defun fmt1 (str alist col channel state evisc-tuple) ; WARNING: The master copy of the tilde-directives list is in :DOC fmt. ":Doc-Section ACL2::ACL2-built-ins ~c[:(str alist col co-channel state evisc) => (mv col state)]~/ ~l[fmt] for further explanation, including documentation of the tilde-directives.~/~/" (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (mv-let (col state) (fmt0 (the-string! str 'fmt1) alist 0 (the-fixnum! (length str) 'fmt1) (the-fixnum! col 'fmt1) channel state evisc-tuple) (declare (type (signed-byte 30) col)) (prog2$ (and (eq channel *standard-co*) (maybe-finish-output$ *standard-co* state)) (mv col state))))) (defun fmt (str alist channel state evisc-tuple) ; WARNING: IF you change the list of tilde-directives, change the copy of it in ; the :DOC for fmt1 and fms. ; For a discussion of our style of pretty-printing, see ; http://www.cs.utexas.edu/~boyer/pretty-print.pdf. ":Doc-Section ACL2::ACL2-built-ins formatted printing~/ ACL2 provides the functions ~c[fmt], ~ilc[fmt1], and ~ilc[fms] as substitutes for Common Lisp's ~c[format] function. Also ~pl[fmt!], ~pl[fmt1!], and ~pl[fms!] for versions of these functions that write forms to files in a manner that allows them to be read, by avoiding using backslash (~c[\\]) to break long lines. There are also analogues of these functions that return a string without taking ~ilc[state] as an argument; ~pl[printing-to-strings]. All three print a given string under an alist pairing character objects with values, interpreting certain ``tilde-directives'' in the string. ~c[Channel] must be a character output channel (e.g., ~ilc[*standard-co*]). ~bv[] General Forms: result (fms string alist channel state evisc-tuple) ; state (fmt string alist channel state evisc-tuple) ; (mv col state) (fmt1 string alist column channel state evisc-tuple) ; (mv col state) ~ev[] ~ilc[Fms] and ~c[fmt] print an initial newline to put ~c[channel] in column ~c[0]; ~ilc[Fmt1] requires the current column as input. Columns are numbered from ~c[0]. The current column is the column into which the next character will be printed. (Thus, the current column number is also the number of ~il[characters] printed since the last newline.) The ~c[col] returned by ~c[fmt] and ~ilc[fmt1] is the current column at the conclusion of the formatting. ~c[Evisc-tuple] must be either ~c[nil] (meaning no abbreviations are used when objects are printed) or an ``evisceration tuple''; ~pl[evisc-tuple]. We list the tilde-directives below. The notation is explained after the chart. ~bv[] ~~xx pretty print vx (maybe after printing a newline) ~~yx pretty print vx starting in current column; end with newline ~~Xxy like ~~xx but use vy as the evisceration tuple ~~Yxy like ~~yx but use vy as the evisceration tuple ~~@x if vx is a string, \"str\", recursively format \"str\" if vx is (\"str\" . a), recursively format \"str\" under a+ ~~#x~~[...~~/...~~/ ... ~~/...~~] cases on vx ^ ^ ... ^ if 0<=vx<=k, choose vxth alternative 0 1 ... k if vx is a list of length 1, case 0; else 1 ~~*x iterator: vx must be of the form (\"str0\" \"str1\" \"str2\" \"str3\" lst . a); if lst is initially empty, format \"str0\" under a+; otherwise, bind #\\* successively to the elements of lst and then recursively format \"stri\" under a+, where i=1 if there is one element left to process, i=2 if there are two left, and i=3 otherwise. ~~&x print elements of vx with ~~x, separated by commas and a final ``and'' ~~vx print elements of vx with ~~x, separated by commas and a final ``or'' ~~nx if vx is a small positive integer, print it as a word, e.g., seven; if vx is a singleton containing a small positive integer, print the corresponding ordinal as a word, e.g., seventh ~~Nx like ~~nx but the word is capitalized, e.g., Seven or Seventh ~~tx tab out to column vx; newline first if at or past column vx ~~cx vx is (n . w), print integer n right justified in field of width w ~~fx print object vx flat over as many lines as necessary ~~Fx same as ~~f, except that subsequent lines are indented to start one character to the right of the first character printed ~~sx if vx is a symbol, print vx, breaking on hyphens (unless the symbol would normally be printed with surrounding vertical bar characters (|), in which case print as with ~~fx); if vx is a string, print the characters in it, breaking on hyphens; else vx is a number, to be printed using the current print-base and print-radix ~~ tilde space: print a space ~~_x print vx spaces ~~ tilde newline: skip following whitespace ~~% output a newline ~~| output a newline unless already on left margin ~~~~ print a tilde ~~- if close to rightmargin, output a hyphen and newline; else skip this char ~ev[] If ~c[x] is a character, then ~c[vx] is the value of ~c[#\\x] under the current alist. Consider for example the discussion above for ~c[~~y], ``~c[~~yx pretty print vx]'', applied to the following expression: ~c[(fmt \"HELLO ~~y7\" (list (cons #\\7 'world)) *standard-co* state nil)]. Then in this example: ~c[#\\x] is 7; and ~c[vx] is the value of character ~c[#\\7] under the given alist, which is the symbol, ~c[WORLD]. Thus, ACL2 will print ~c[HELLO WORLD]. When we say ``format ~c[str] under ~c[a+]'' we mean: process the given string under an alist obtained by appending ~c[a] to the current alist. The following example illustrates how this works. ~bv[] ACL2 !>(fms \"~~@0\" (list (cons #\\0 (cons \"~~x0 ~~@1\" (list (cons #\\0 'abc)))) (cons #\\1 \"-- and now: ~~x0 again~~%\")) *standard-co* state nil) ABC -- and now: ABC again ACL2 !> ~ev[] Note: ~c[~~p], ~c[~~q], ~c[~~P], and ~c[~~Q] are also currently supported, but are deprecated. These are respectively the same as ~c[~~x], ~c[~~y], ~c[~~X], and ~c[~~Y], except that their arguments are expected to be terms, preferably untranslated (user-level) terms, that could be printed using infix notation in certain environments. Infix printing is not currently supported but may be if there is sufficient need for it.~/ ACL2's formatting functions print to the indicated channel, keeping track of which column they are in. ~ilc[Fmt1] can be used if the caller knows which column the channel is in (i.e., how many ~il[characters] have been printed since the last newline). Otherwise, ~c[fmt] or ~ilc[fms] must be used, both of which output a newline so as to establish the column position at ~c[0]. Unlike Common Lisp's ~c[format] routine, ~c[fmt] and its relatives break the output into lines so that, by default, an attempt is made to avoid printing past column ~c[77] (the value of constant ~c[*fmt-hard-right-margin-default*]). ~l[set-fmt-hard-right-margin] for a discussion of how linebreaks are inserted and how to change the relevant default settings. The formatting functions scan the string from left to right, printing each successive character unless it is a tilde ~c[(~~)]. Upon encountering tildes the formatters take action determined by the character or ~il[characters] immediately following the tilde. The typical tilde-directive is a group of three successive ~il[characters] from the string being printed. For example, ~c[~~x0] is a 3 character ~c[tilde-directive]. The first character in a tilde-directive is always the tilde character itself. The next character is called the ``command'' character. The character after that is usually taken as the name of a ``format variable'' that is bound in the alist under which the string is being printed. Format variables are, by necessity, ~il[characters]. The objects actually printed by a tilde-directive are the objects obtained by looking up the command's format variables in the alist. Typical format variable names are ~c[0], ~c[1], ~c[2], ..., ~c[9], ~c[a], ~c[b], ~c[c], etc., and if a tilde-directive uses the format variable ~c[0], as in ~c[~~x0], then the character ~c[#\\0] must be bound in the alist. Some tilde commands take no arguments and others take more than one, so some directives are of length two and others are longer. It should be noted that this use of ~il[characters] in the string to denote arguments is another break from Common Lisp's ~c[format] routine. In Common Lisp, the directives refer implicitly to the ``next item to be printed.'' But in ACL2 the directives name each item explicitly with our format variables. The following text contains examples that can be evaluated. To make this process easier, we use a macro which is defined as part of ACL2 just for this ~il[documentation]. The macro is named ~c[fmx] and it takes up to eleven arguments, the first of which is a format string, ~c[str], and the others of which are taken as the values of format variables. The variables used are ~c[#\\0] through ~c[#\\9]. The macro constructs an appropriate alist, ~c[a], and then evaluates ~c[(fmt str a *standard-co* state nil)]. Thus, ~bv[] (fmx \"Here is v0, ~~x0, and here is v1, ~~x1.\" (cons 'value 0) (cons 'value 1)) ~ev[] is just an abbreviation for ~bv[] (fmt \"Here is v0, ~~x0, and here is v1, ~~x1.\" (list (cons #\\0 (cons 'value 0)) (cons #\\1 (cons 'value 1))) *standard-co* state nil) ~ev[] which returns ~c[(mv 53 state)] after printing the line ~bv[] Here is v0, (VALUE . 0), and here is v1, (VALUE . 1). ~ev[] We now devote special attention to three of the tilde-directives whose use is non-obvious. ~em[The Case Statement] ~c[~~#x] is essentially a ``case statement'' in the language of ~c[fmt]. The proper form of the statement is ~bv[] ~~#x~~[case-0~~/case-1~~/ ... ~~/case-k~~], ~ev[] where each of the ~c[case-i] is a format string. In the most common use, the variable ~c[x] has an integer value, ~c[vx], between ~c[0] and ~c[k], inclusive. The effect of formatting the directive is to format ~c[case-vx]. For example ~bv[] (fmx \"Go ~~#0~~[North~~/East~~/South~~/West~~].~~%\" 1) ~ev[] will print ``Go East.'' followed by a newline and will return ~c[(mv 0 state)], while if you change the ~c[1] above to ~c[3] (the maximum legal value), it will print ``Go West.'' In order to make it easier to print such phrases as ``there are seven cases'' requiring agreement between subject and verb based on the number of elements of a list, the case statement allows its variable to take a list as its value and selects ~c[case-0] if the list has length ~c[1] and ~c[case-1] otherwise. ~bv[] (let ((cases '(a b c))) (fmx \"There ~~#0~~[is ~~n1 case~~/are ~~n1 cases~~].\" cases (length cases))) ~ev[] will print ``There are three cases.'' but if you change the ~c['(a b c)] above simply to ~c['(a)] it will print ``There is one case.'' and if you change it to ~c[nil] it will print ``There are zero cases.'' ~em[Indirection] Roughly speaking, ~c[~~@] will act as though the value of its argument is a format string and splice it into the current string at the current position. It is often used when the phrase to be printed must be computed. For example, ~bv[] (let ((ev 'DEFUN)) (fmx \"~~x0 is an event~~@1.\" 'foo (if (member-eq ev '(defun defstub encapsulate)) \" that may introduce a function symbol\" \"\"))) ~ev[] will print ``~c[foo] is an event that may introduce a function symbol,'' but if the value of ~c[ev] is changed from ~c[']~ilc[defun] to ~c[']~ilc[defthm], it prints ``~c[foo] is an event.'' The ~c[~~@] directive ``splices'' in the computed phrase (which might be empty). Of course, this particular example could be done with the case statement ~bv[] ~~#1~~[~~/ that may introduce a function symbol~~] ~ev[] where the value of ~c[#\\1] is appropriately computed to be ~c[0] or ~c[1]. If the argument to ~c[~~@] is a pair, it is taken to be a format string ~ilc[cons]ed onto an alist, i.e., ~c[(\"str\" . a)], and the alist, ~c[a], is used to extend the current one before ~c[\"str\"] is recursively processed. This feature of ~c[fmt] can be used to pass around ``phrases'' that contain computed contextual information in ~c[a]. The most typical use is as ``error messages.'' For example, suppose you are writing a function which does not have access to ~ilc[state] and so cannot print an error message. It may nevertheless be necessary for it to signal an error to its caller, say by returning two results, the first of which is interpreted as an error message if non-~c[nil]. Our convention is to use a ~c[~~@] pair to represent such messages. For example, the error value might be produced by the code: ~bv[] (cons \"Error: The instruction ~~x0 is illegal when the stack is ~~x1.~~%\" (list (cons #\\0 (current-instruction st)) (cons #\\1 (i-stack st)))) ~ev[] If the ~c[current-instruction] and ~c[i-stack] (whatever they are) are ~c['(popi 3)] and ~c['(a b)] when the ~ilc[cons] above is evaluated, then it produces ~bv[] '(\"Error: The instruction ~~x0 is illegal when the stack is ~~x1.~~%\" (#\\0 POPI 3) (#\\1 A B)) ~ev[] and if this pair is made the value of the ~c[fmt] variable ~c[0], then ~c[~~@0] will print ~bv[] Error: The instruction (POPI 3) is illegal when the stack is (A B). ~ev[] For example, evaluate ~bv[] (let ((pair '(\"Error: The instruction ~~x0 is illegal when the stack is ~~x1.~~%\" (#\\0 POPI 3) (#\\1 A B)))) (fmx \"~~@0\" pair)). ~ev[] Thus, even though the function that produced the ``error'' could not print it, it could specify exactly what error message and data are to be printed. This example raises another issue. Sometimes it is desirable to break lines in your format strings so as to make your source code more attractive. That is the purpose of the ~c[tilde-newline] directive. The following code produces exactly the same output as described above. ~bv[] (let ((pair '(\"Error: The instruction ~~x0 ~~ is illegal when the stack is ~~ ~~x1.~~%\" (#\\0 POPI 3) (#\\1 A B)))) (fmx \"~~@0\" pair)). ~ev[] Finally, observe that when ~c[~~@0] extends the current alist, ~c[alist], with the one, ~c[a], in its argument, the bindings from ~c[a] are added to the front of ~c[alist], overriding the current values of any shared variables. This ensures that the variable values seen by the recursively processed string, ~c[\"str\"], are those from ~c[a], but if ~c[\"str\"] uses variables not bound in ~c[a], their values are as specified in the original alist. Intuitively, variables bound in ~c[a] are local to the processing of ~c[(\"str\" . a)] but ~c[\"str\"] may use ``global variables.'' The example above illustrates this because when the ~c[~~@0] is processed, ~c[#\\0] is bound to the error message pair. But when the ~c[~~x0] in the error string is processed, ~c[#\\0] is bound to the illegal instruction. ~em[Iteration] The ~c[~~*] directive is used to process each element of a list. For example, ~bv[] (let ((lst '(a b c d e f g h))) ; a true-list whose elements we exhibit (fmx \"~~*0\" `(\"Whoa!\" ; what to print if there's nothing to print \"~~x*!\" ; how to print the last element \"~~x* and \" ; how to print the 2nd to last element \"~~x*, \" ; how to print all other elements ,lst))) ; the list of elements to print ~ev[] will print ``~c[A, B, C, D, E, F, G and H!]''. Try this example with other true list values of ~c[lst], such as ~c['(a b)], ~c['(a)], and ~c[nil]. The tilde-directives ~c[~~&0] and ~c[~~v0], which take a true list argument and display its elements separated by commas and a final ``and'' or ``or,'' are implemented in terms of the more general ~c[~~*]. The ~c[~~*] directive allows the 5-tuple to specify in its final ~ilc[cdr] an alist with which to extend the current one before processing the individual elements. We often use ~c[~~*] to print a series of phrases, separated by suitable punctuation, whitespace and noise words. In such use, the ~c[~~*] handles the separation of the phrases and each phrase is generally printed by ~c[~~@]. Here is a complex example. In the ~ilc[let*], below, we bind phrases to a list of ~c[~~@] pairs and then we create a ~c[~~*] 5-tuple to print out the conjunction of the phrases with a parenthetical ``finally!'' if the series is longer than 3. ~bv[] (let* ((phrases (list (list \"simplifying with the replacement rules ~~&0\" (cons #\\0 '(rewrite-rule1 rewrite-rule2 rewrite-rule3))) (list \"destructor elimination using ~~x0\" (cons #\\0 'elim-rule)) (list \"generalizing the terms ~~&0\" (cons #\\0 '((rev x) (app u v)))) (list \"inducting on ~~x0\" (cons #\\0 'I)))) (5-tuple (list \"magic\" ; no phrases \"~~@*\" ; last phrase \"~~@*, and~~#f~~[~~/ (finally!)~~] \" ; second to last phrase \"~~@*, \" ; other phrases phrases ; the phrases themselves (cons #\\f (if (>(length phrases) 3) 1 0))))) ;print ``finally''? (fmx \"We did it by ~~*0.\" 5-tuple)) ~ev[] This ~ilc[let*] prints ~bv[] We did it by simplifying with the replacement rules REWRITE-RULE1, REWRITE-RULE2 and REWRITE-RULE3, destructor elimination using ELIM- RULE, generalizing the terms (REV X) and (APP U V), and (finally!) inducting on I. ~ev[] You might wish to try evaluating the ~ilc[let*] after removing elements of phrases. Most of the output produced by ACL2 is produced via ~c[fmt] statements. Thus, inspection of the source code will yield many examples. A complicated example is the code that explains the simplifier's work. See ~c[:]~ilc[pc] ~c[simplify-clause-msg1]. An ad hoc example is provided by the function ~c[fmt-doc-example], which takes two arguments: an arbitrary true list and ~ilc[state]. To see how ~c[fmt-doc-example] works, ~c[:]~ilc[pe] ~c[fmt-doc-example]. ~bv[] (fmt-doc-example '(a b c d e f g h i j k l m n o p) state) ~ev[] will produce the output ~bv[] Here is a true list: (A B C D E F G H I J K L M N O P). It has 16 elements, the third of which is C. We could print each element in square brackets: ([A], [B], [C], [D], [E], [F], [G], [H], [I], [J], [K], [L], [M], [N], [almost there: O], [the end: P]). And if we wished to itemize them into column 15 we could do it like this 0123456789012345 0 (zeroth) A 1 (first) B 2 (second) C 3 (third) D 4 (fourth) E 5 (fifth) F 6 (sixth) G 7 (seventh) H 8 (eighth) I 9 (ninth) J 10 (tenth) K 11 (eleventh) L 12 (twelfth) M 13 (thirteenth) N 14 (14th) O 15 (15th) P End of example. ~ev[] and return ~c[(mv 15 state)]. Finally, we should remind the reader that ~c[fmt] and its subfunctions, most importantly ~c[fmt0], are written entirely in ACL2. We make this comment for two reasons. First, it illustrates the fact that quite low level code can be efficiently written in the language. Second, it means that as a last resort for documentation purposes you can read the source code without changing languages." (the2s (signed-byte 30) (pprogn (newline channel state) (fmt1 str alist 0 channel state evisc-tuple)))) (defun fms (str alist channel state evisc-tuple) ; WARNING: The master copy of the tilde-directives list is in :DOC fmt. ":Doc-Section ACL2::ACL2-built-ins ~c[:(str alist co-channel state evisc) => state]~/ ~l[fmt] for further explanation, including documentation of the tilde-directives.~/~/" (pprogn (newline channel state) (mv-let (col state) (fmt1 str alist 0 channel state evisc-tuple) (declare (ignore col)) state))) (defun fmt1! (str alist col channel state evisc-tuple) ; WARNING: The master copy of the tilde-directives list is in :DOC fmt. ":Doc-Section ACL2::ACL2-built-ins ~c[:(str alist col channel state evisc) => (mv col state)]~/ This function is nearly identical to ~c[fmt1]; ~pl[fmt1]. The only difference is that ~c[fmt1] may insert backslash (\\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use ~c[fmt1!] instead if you want to be able to read the forms back in.~/~/" (mv-let (erp col state) (state-global-let* ((write-for-read t)) (mv-let (col state) (fmt1 str alist col channel state evisc-tuple) (mv nil col state))) (declare (ignore erp)) (mv col state))) (defun fmt! (str alist channel state evisc-tuple) ; WARNING: The master copy of the tilde-directives list is in :DOC fmt. ":Doc-Section ACL2::ACL2-built-ins ~c[:(str alist co-channel state evisc) => state]~/ This function is nearly identical to ~c[fmt]; ~pl[fmt]. The only difference is that ~c[fmt] may insert backslash (\\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use ~c[fmt!] instead if you want to be able to read the forms back in.~/~/" (mv-let (erp col state) (state-global-let* ((write-for-read t)) (mv-let (col state) (fmt str alist channel state evisc-tuple) (mv nil col state))) (declare (ignore erp)) (mv col state))) (defun fms! (str alist channel state evisc-tuple) ; WARNING: The master copy of the tilde-directives list is in :DOC fmt. ":Doc-Section ACL2::ACL2-built-ins ~c[:(str alist co-channel state evisc) => state]~/ This function is nearly identical to ~c[fms]; ~pl[fms]. The only difference is that ~c[fms] may insert backslash (\\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use ~c[fms!] instead if you want to be able to read the forms back in.~/~/" (mv-let (erp val state) (state-global-let* ((write-for-read t)) (pprogn (fms str alist channel state evisc-tuple) (mv nil nil state))) (declare (ignore erp val)) state)) (defmacro fmx (str &rest args) (declare (xargs :guard (<= (length args) 10))) `(fmt ,str ,(make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) args) *standard-co* state nil)) (defun fmt-doc-example1 (lst i) (cond ((null lst) nil) (t (cons (cons "~c0 (~n1)~tc~y2~|" (list (cons #\0 (cons i 5)) (cons #\1 (list i)) (cons #\2 (car lst)))) (fmt-doc-example1 (cdr lst) (1+ i)))))) (defun fmt-doc-example (x state) (fmt "Here is a true list: ~x0. It has ~#1~[no elements~/a single ~ element~/~n2 elements~], ~@3~%~%We could print each element in square ~ brackets:~%(~*4). And if we wished to itemize them into column 15 we ~ could do it like this~%0123456789012345~%~*5End of example." (list (cons #\0 x) (cons #\1 (cond ((null x) 0) ((null (cdr x)) 1)(t 2))) (cons #\2 (length x)) (cons #\3 (cond ((< (length x) 3) "and so we can't print the third one!") (t (cons "the third of which is ~x0." (list (cons #\0 (caddr x))))))) (cons #\4 (list "[empty]" "[the end: ~y*]" "[almost there: ~y*], " "[~y*], " x)) (cons #\5 (list* "" "~@*" "~@*" "~@*" (fmt-doc-example1 x 0) (list (cons #\c 15))))) *standard-co* state nil)) (defun fmt-abbrev1 (str alist col channel state suffix-msg) (pprogn (f-put-global 'evisc-hitp-without-iprint nil state) (mv-let (col state) (fmt1 str alist col channel state (abbrev-evisc-tuple state)) (fmt1 "~@0~@1" (list (cons #\0 (cond ((f-get-global 'evisc-hitp-without-iprint state) (assert$ (not (iprint-enabledp state)) "~|(See :DOC set-iprint to be able to see ~ elided values in this message.)")) (t ""))) (cons #\1 suffix-msg)) col channel state nil)))) (defun fmt-abbrev (str alist col channel state suffix-msg) (mv-let (col state) (fmt-abbrev1 str alist col channel state suffix-msg) (declare (ignore col)) state)) (defconst *fmt-ctx-spacers* '(defun #+:non-standard-analysis defun-std mutual-recursion defuns defthm #+:non-standard-analysis defthm-std defaxiom defconst defstobj defabsstobj defpkg deflabel defdoc deftheory defchoose verify-guards verify-termination defmacro in-theory in-arithmetic-theory regenerate-tau-database push-untouchable remove-untouchable reset-prehistory set-body table encapsulate include-book)) (defun fmt-ctx (ctx col channel state) ; We print the context in which an error has occurred. If infix printing is ; being used (infixp = t or :out) then ctx is just the event form itself and we ; print it with evisceration. Otherwise, we are more efficient in our choice ; of ctx and we interpret it according to its type, to make it convenient to ; construct the more common contexts. If ctx is nil, we print nothing. If ctx ; is a symbol, we print it from #\0 via "~x0". If ctx is a pair whose car is a ; symbol, we print its car and cdr from #\0 and #\1 respectively with "(~x0 ~x1 ; ...)". Otherwise, we print it from #\0 with "~@0". ; We print no other words, spaces or punctuation. We return the new ; col and state. (declare (type (signed-byte 30) col)) ; The following bit of raw-Lisp code can be useful when observing ; "ACL2 Error in T:". ; #-acl2-loop-only ; (when (eq ctx t) (break)) (the2s (signed-byte 30) (cond ((output-in-infixp state) (fmt1 "~p0" (list (cons #\0 ctx)) col channel state (evisc-tuple 1 2 nil nil))) ((null ctx) (mv col state)) ((symbolp ctx) (fmt1 "~x0" (list (cons #\0 ctx)) col channel state nil)) ((and (consp ctx) (symbolp (car ctx))) (fmt1 "(~@0~x1 ~x2 ...)" (list (cons #\0 (if (member-eq (car ctx) *fmt-ctx-spacers*) " " "")) (cons #\1 (car ctx)) (cons #\2 (cdr ctx))) col channel state nil)) (t (fmt-abbrev1 "~@0" (list (cons #\0 ctx)) col channel state ""))))) (defun fmt-in-ctx (ctx col channel state) ; We print the phrase " in ctx: ", if ctx is non-nil, and return ; the new col and state. (declare (type (signed-byte 30) col)) (the2s (signed-byte 30) (cond ((null ctx) (fmt1 ": " nil col channel state nil)) (t (mv-let (col state) (fmt1 " in " nil col channel state nil) (mv-let (col state) (fmt-ctx ctx col channel state) (fmt1 ": " nil col channel state nil))))))) (defun error-fms-channel (hardp ctx str alist channel state) ; This function prints the "ACL2 Error" banner and ctx, then the ; user's str and alist, and then two carriage returns. It returns state. ; Historical Note about ACL2 ; Once upon a time we accomplished all this with something like: "ACL2 ; Error (in ~xc): ~@s~%~%" and it bound #\c and #\s to ctx and str in ; alist. That suffers from the fact that it may overwrite the user's ; bindings of #\c and #\s -- unlikely if this error call was generated ; by our er macro. We rewrote the function this way simply so we ; would not have to remember that some variables are special. (mv-let (col state) (fmt1 (if hardp "~%HARD ACL2 ERROR" "~%ACL2 Error") nil 0 channel state nil) (mv-let (col state) (fmt-in-ctx ctx col channel state) (fmt-abbrev str alist col channel state "")))) (defun error-fms (hardp ctx str alist state) ; See error-fms-channel. Here we also print extra newlines. ; Keep in sync with error-fms-cw. (with-output-lock (let ((chan (f-get-global 'standard-co state))) (pprogn (newline chan state) (error-fms-channel hardp ctx str alist chan state) (newline chan state) (newline chan state))))) #-acl2-loop-only (defvar *accumulated-warnings* nil) (defun push-warning-frame (state) #-acl2-loop-only (setq *accumulated-warnings* (cons nil *accumulated-warnings*)) state) (defun absorb-frame (lst stk) (if (consp stk) (cons (union-equal lst (car stk)) (cdr stk)) stk)) (defun pop-warning-frame (accum-p state) ; When a "compound" event has a "sub-event" that generates warnings, we want ; the warning strings from the sub-event's summary to appear in the parent ; event's summary. Accum-p should be nil if and only if the sub-event whose ; warning frame we are popping had its warnings suppressed. ; Starting after Version_4.1, we use the ACL2 oracle to explain warning frames. ; Previously we kept these frames with a state global variable, ; 'accumulated-warnings, rather than in the raw lisp variable, ; *accumulated-warnings*. But then we introduced warning$-cw1 to support the ; definitions of translate1-cmp and translate-cmp, which do not modify the ACL2 ; state. Since warning$-cw1 uses a wormhole, the warning frames based on a ; state global variable were unavailable when printing warning summaries. #+acl2-loop-only (declare (ignore accum-p)) #+acl2-loop-only (mv-let (erp val state) (read-acl2-oracle state) (declare (ignore erp)) (mv val state)) #-acl2-loop-only (let ((stk *accumulated-warnings*)) (cond ((consp stk) (progn (setq *accumulated-warnings* (if accum-p (absorb-frame (car stk) (cdr stk)) (cdr stk))) (mv (car stk) state))) (t (mv (er hard 'pop-warning-frame "The 'accumulated-warnings stack is empty.") state))))) (defun push-warning (summary state) #+acl2-loop-only (declare (ignore summary)) #-acl2-loop-only (when (consp *accumulated-warnings*) ; We used to cause an error, shown below, if the above test fails. But ; WARNINGs are increasingly used by non-events, such as :trans and (thm ...) ; and rather than protect them all with push-warning-frame/pop-warning-frame we ; are just adopting the policy of not pushing warnings if the stack isn't set ; up for them. Here is the old code. ; (prog2$ (er hard 'push-warning ; "The 'accumulated-warnings stack is empty but we were ~ ; asked to add ~x0 to the top frame." ; summary) ; state) (setq *accumulated-warnings* (cons (add-to-set-equal summary (car *accumulated-warnings*)) (cdr *accumulated-warnings*)))) state) (defun member-string-equal (str lst) (cond ((endp lst) nil) (t (or (string-equal str (car lst)) (member-string-equal str (cdr lst)))))) (defabbrev flambda-applicationp (term) ; Term is assumed to be nvariablep. (consp (car term))) (defabbrev lambda-applicationp (term) (and (consp term) (flambda-applicationp term))) (defabbrev flambdap (fn) ; Fn is assumed to be the fn-symb of some term. (consp fn)) (defabbrev lambda-formals (x) (cadr x)) (defabbrev lambda-body (x) (caddr x)) (defabbrev make-lambda (args body) (list 'lambda args body)) (defabbrev make-let (bindings body) (list 'let bindings body)) (defun doubleton-list-p (x) (cond ((atom x) (equal x nil)) (t (and (true-listp (car x)) (eql (length (car x)) 2) (doubleton-list-p (cdr x)))))) (defmacro er-let* (alist body) ; This macro introduces the variable er-let-star-use-nowhere-else. ; The user who uses that variable in his forms is likely to be ; disappointed by the fact that we rebind it. ; Keep in sync with er-let*@par. (declare (xargs :guard (and (doubleton-list-p alist) (symbol-alistp alist)))) (cond ((null alist) (list 'check-vars-not-free '(er-let-star-use-nowhere-else) body)) (t (list 'mv-let (list 'er-let-star-use-nowhere-else (caar alist) 'state) (cadar alist) (list 'cond (list 'er-let-star-use-nowhere-else (list 'mv 'er-let-star-use-nowhere-else (caar alist) 'state)) (list t (list 'er-let* (cdr alist) body))))))) #+acl2-par (defmacro er-let*@par (alist body) ; Keep in sync with er-let*. ; This macro introduces the variable er-let-star-use-nowhere-else. ; The user who uses that variable in his forms is likely to be ; disappointed by the fact that we rebind it. (declare (xargs :guard (and (doubleton-list-p alist) (symbol-alistp alist)))) (cond ((null alist) (list 'check-vars-not-free '(er-let-star-use-nowhere-else) body)) (t (list 'mv-let (list 'er-let-star-use-nowhere-else (caar alist)) (cadar alist) (list 'cond (list 'er-let-star-use-nowhere-else (list 'mv 'er-let-star-use-nowhere-else (caar alist))) (list t (list 'er-let*@par (cdr alist) body))))))) (defmacro match (x pat) (list 'case-match x (list pat t))) (defmacro match! (x pat) (list 'or (list 'case-match x (list pat '(value nil))) (list 'er 'soft nil "The form ~x0 was supposed to match the pattern ~x1." x (kwote pat)))) (defun def-basic-type-sets1 (lst i) (declare (xargs :guard (and (integerp i) (true-listp lst)))) (cond ((null lst) nil) (t (cons (list 'defconst (car lst) (list 'the-type-set (expt 2 i))) (def-basic-type-sets1 (cdr lst) (+ i 1)))))) (defmacro def-basic-type-sets (&rest lst) (let ((n (length lst))) `(progn (defconst *actual-primitive-types* ',lst) (defconst *min-type-set* (- (expt 2 ,n))) (defconst *max-type-set* (- (expt 2 ,n) 1)) (defmacro the-type-set (x) ; Warning: Keep this definition in sync with the type declaration in ; ts-subsetp0 and ts-subsetp. `(the (integer ,*min-type-set* ,*max-type-set*) ,x)) ,@(def-basic-type-sets1 lst 0)))) (defun list-of-the-type-set (x) (cond ((consp x) (cons (list 'the-type-set (car x)) (list-of-the-type-set (cdr x)))) (t nil))) (defmacro ts= (a b) (list '= (list 'the-type-set a) (list 'the-type-set b))) ; We'll create fancier versions of ts-complement0, ts-union0, and ; ts-intersection0 once we have defined the basic type sets. (defmacro ts-complement0 (x) (list 'the-type-set (list 'lognot (list 'the-type-set x)))) (defmacro ts-complementp (x) (list 'minusp x)) (defun ts-union0-fn (x) (list 'the-type-set (cond ((null x) '*ts-empty*) ((null (cdr x)) (car x)) (t (xxxjoin 'logior (list-of-the-type-set x)))))) (defmacro ts-union0 (&rest x) (declare (xargs :guard (true-listp x))) (ts-union0-fn x)) (defmacro ts-intersection0 (&rest x) (list 'the-type-set (cons 'logand (list-of-the-type-set x)))) (defmacro ts-disjointp (&rest x) (list 'ts= (cons 'ts-intersection x) '*ts-empty*)) (defmacro ts-intersectp (&rest x) (list 'not (list 'ts= (cons 'ts-intersection x) '*ts-empty*))) ; We do not define ts-subsetp0, both because we don't need it and because if we ; do define it, we will be tempted to add the declaration found in ts-subsetp, ; yet we have not yet defined *min-type-set* or *max-type-set*. (defun ts-builder-case-listp (x) ; A legal ts-builder case list is a list of the form ; ((key1 val1 ...) (key2 val2 ...) ... (keyk valk ...)) ; where none of the keys is 'otherwise or 't except possibly keyk and ; every key is a symbolp if keyk is 'otherwise or 't. ; This function returns t, nil, or 'otherwise. A non-nil value means ; that x is a legal ts-builder case list. If it returns 'otherwise, ; it means keyk is an 'otherwise or a 't clause. That aspect of the ; function is not used outside of its definition, but it is used in ; the definition below. ; If keyk is an 'otherwise or 't then each of the other keys will ; occur twice in the expanded form of the ts-builder expression and ; hence those keys must all be symbols. (cond ((atom x) (eq x nil)) ((and (consp (car x)) (true-listp (car x)) (not (null (cdr (car x))))) (cond ((or (eq t (car (car x))) (eq 'otherwise (car (car x)))) (cond ((null (cdr x)) 'otherwise) (t nil))) (t (let ((ans (ts-builder-case-listp (cdr x)))) (cond ((eq ans 'otherwise) (cond ((symbolp (car (car x))) 'otherwise) (t nil))) (t ans)))))) (t nil))) (defun ts-builder-macro1 (x case-lst seen) (declare (xargs :guard (and (symbolp x) (ts-builder-case-listp case-lst)))) (cond ((null case-lst) nil) ((or (eq (caar case-lst) t) (eq (caar case-lst) 'otherwise)) (sublis (list (cons 'x x) (cons 'seen seen) (cons 'ts2 (cadr (car case-lst)))) '((cond ((ts-intersectp x (ts-complement0 (ts-union0 . seen))) ts2) (t *ts-empty*))))) (t (cons (sublis (list (cons 'x x) (cons 'ts1 (caar case-lst)) (cons 'ts2 (cadr (car case-lst)))) '(cond ((ts-intersectp x ts1) ts2) (t *ts-empty*))) (ts-builder-macro1 x (cdr case-lst) (cons (caar case-lst) seen)))))) (defun ts-builder-macro (x case-lst) (declare (xargs :guard (and (symbolp x) (ts-builder-case-listp case-lst)))) (cons 'ts-union (ts-builder-macro1 x case-lst nil))) (defmacro ts-builder (&rest args) ; (declare (xargs :guard (and (consp args) ; (symbolp (car args)) ; (ts-builder-case-listp (cdr args))))) (ts-builder-macro (car args) (cdr args))) (defabbrev strip-not (term) ; A typical use of this macro is: ; (mv-let (not-flg atm) (strip-not term) ; ...body...) ; which has the effect of binding not-flg to T and atm to x if term ; is of the form (NOT x) and binding not-flg to NIL and atm to term ; otherwise. (cond ((and (nvariablep term) ; (nquotep term) (eq (ffn-symb term) 'not)) (mv t (fargn term 1))) (t (mv nil term)))) ; The ACL2 Record Facilities ; Our record facility gives us the ability to declare "new" types of ; structures which are represented as lists. If desired the lists ; are tagged with the name of the new record type. Otherwise they are ; not tagged and are called "cheap" records. ; The expression (DEFREC SHIP (X . Y) NIL) declares SHIP to ; be a tagged (non-cheap) record of two components X and Y. An ; example concrete SHIP is '(SHIP 2 . 4). Note that cheapness refers ; only to whether the record is tagged and whether the tag is tested ; upon access and change, not whether the final cdr is used. ; To make a ship: (MAKE SHIP :X x :Y y) or (MAKE SHIP :Y y :X x). ; To access the Xth component of the ship object obj: (ACCESS SHIP obj :X). ; To change the Xth component to val: (CHANGE SHIP obj :X val). ; Note the use of keywords in these forms. ; It is possible to change several fields at once, e.g., ; (CHANGE SHIP obj :X val-x :Y val-y). In general, to cons up a changed ; record one only does the conses necessary. ; The implementation of records is as follows. DEFREC expands ; into a collection of macro definitions for certain generated function ; symbols. In the example above we define the macros: ; |Make SHIP record| ; |Access SHIP record field X| ; |Access SHIP record field Y| ; |Change SHIP record fields| ; The macro expression (MAKE SHIP ...) expands to a call of the first ; function. (ACCESS SHIP ... :X) expands to a call of the second. ; (CHANGE SHIP obj :X val-x :Y val-y) expands to ; (|Change SHIP record fields| obj :X val-x :Y val-y). ; The five new symbols above are defined as macros that further expand ; into raw CAR/CDR nests if the record is cheap and a similar nest ; that first checks the type of the record otherwise. ; In using the record facility I have sometimes pondered which fields I should ; allocate where to maximize access speed. Other times I have just laid them ; out in an arbitrary fashion. In any case, the following functions might be ; useful if you are wondering how to lay out a record. That is, grab the ; following progn and execute it in the full ACL2 system. (It cannot be ; executed at this point in basis.lisp because it uses functions defined ; elsewhere; it is here only to be easy to find when looking up the comments ; about records.) Note that it changes the default-defun-mode to :program. Then ; invoke :sbt n, where n is an integer. ; For example ; ACL2 g>:sbt 5 ; The Binary Trees with Five Tips ; 2.400 ((2 . 2) 2 3 . 3) ; 2.600 (1 (3 . 3) 3 . 3) ; 2.800 (1 2 3 4 . 4) ; Sbt will print out all of the interesting binary trees with the ; given number of tips. The integer appearing at a tip is the number ; of car/cdrs necessary to access that field of a cheap record laid ; out as shown. That is also the number of conses required to change ; that single field. The decimal number in the left column is the ; average number of car/cdrs required to access a field, assuming all ; fields are accessed equally often. The number of trees generated ; grows exponentially with n. Roughly 100 trees are printed for size ; 10. Beware! ; The function (analyze-tree x state) is also helpful. E.g., ; ACL2 g>(analyze-tree '((type-alist . term) cl-ids rewrittenp ; force-flg . rune-or-non-rune) ; state) ; Shape: ((2 . 2) 2 3 4 . 4) ; Field Depths: ; ((TYPE-ALIST . 2) ; (TERM . 2) ; (CL-IDS . 2) ; (REWRITTENP . 3) ; (FORCE-FLG . 4) ; (RUNE-OR-NON-RUNE . 4)) ; Avg Depth: 2.833 ; (progn ; (program) ; (defun bump-binary-tree (tree) ; (cond ((atom tree) (1+ tree)) ; (t (cons (bump-binary-tree (car tree)) ; (bump-binary-tree (cdr tree)))))) ; ; (defun cons-binary-trees (t1 t2) ; (cons (bump-binary-tree t1) (bump-binary-tree t2))) ; ; (defun combine-binary-trees1 (t1 lst2 ans) ; (cond ((null lst2) ans) ; (t (combine-binary-trees1 t1 (cdr lst2) ; (cons (cons-binary-trees t1 (car lst2)) ; ans))))) ; ; (defun combine-binary-trees (lst1 lst2 ans) ; (cond ; ((null lst1) ans) ; (t (combine-binary-trees (cdr lst1) ; lst2 ; (combine-binary-trees1 (car lst1) lst2 ans))))) ; ; (mutual-recursion ; ; (defun all-binary-trees1 (i n) ; (cond ((= i 0) nil) ; (t (revappend (combine-binary-trees (all-binary-trees i) ; (all-binary-trees (- n i)) ; nil) ; (all-binary-trees1 (1- i) n))))) ; ; (defun all-binary-trees (n) ; (cond ((= n 1) (list 0)) ; (t (all-binary-trees1 (floor n 2) n)))) ; ) ; ; (defun total-access-time-binary-tree (x) ; (cond ((atom x) x) ; (t (+ (total-access-time-binary-tree (car x)) ; (total-access-time-binary-tree (cdr x)))))) ; ; (defun total-access-time-binary-tree-lst (lst) ; ; ; Pairs each tree in lst with its total-access-time. ; ; (cond ((null lst) nil) ; (t (cons (cons (total-access-time-binary-tree (car lst)) ; (car lst)) ; (total-access-time-binary-tree-lst (cdr lst)))))) ; ; (defun show-binary-trees1 (n lst state) ; (cond ((null lst) state) ; (t (let* ((tat (floor (* (caar lst) 1000) n)) ; (d0 (floor tat 1000)) ; (d1 (- (floor tat 100) (* d0 10))) ; (d2 (- (floor tat 10) (+ (* d0 100) (* d1 10)))) ; (d3 (- tat (+ (* d0 1000) (* d1 100) (* d2 10))))) ; ; (pprogn ; (mv-let (col state) ; (fmt1 "~x0.~x1~x2~x3 ~x4~%" ; (list (cons #\0 d0) ; (cons #\1 d1) ; (cons #\2 d2) ; (cons #\3 d3) ; (cons #\4 (cdar lst))) ; 0 ; *standard-co* state nil) ; (declare (ignore col)) ; state) ; (show-binary-trees1 n (cdr lst) state)))))) ; ; (defun show-binary-trees (n state) ; (let ((lst (reverse ; (merge-sort-car-> ; (total-access-time-binary-tree-lst ; (all-binary-trees n)))))) ; (pprogn ; (fms "The Binary Trees with ~N0 Tips~%" ; (list (cons #\0 n)) ; *standard-co* state nil) ; (show-binary-trees1 n lst state)))) ; ; (defun analyze-tree1 (x i) ; (cond ((atom x) i) ; (t (cons (analyze-tree1 (car x) (1+ i)) ; (analyze-tree1 (cdr x) (1+ i)))))) ; ; (defun analyze-tree2 (x i) ; (cond ((atom x) (list (cons x i))) ; (t (append (analyze-tree2 (car x) (1+ i)) ; (analyze-tree2 (cdr x) (1+ i)))))) ; ; (defun analyze-tree3 (x) ; (cond ((atom x) 1) ; (t (+ (analyze-tree3 (car x)) (analyze-tree3 (cdr x)))))) ; ; (defun analyze-tree (x state) ; (let* ((binary-tree (analyze-tree1 x 0)) ; (alist (analyze-tree2 x 0)) ; (n (analyze-tree3 x)) ; (k (total-access-time-binary-tree binary-tree))) ; (let* ((tat (floor (* k 1000) n)) ; (d0 (floor tat 1000)) ; (d1 (- (floor tat 100) (* d0 10))) ; (d2 (- (floor tat 10) (+ (* d0 100) (* d1 10)))) ; (d3 (- tat (+ (* d0 1000) (* d1 100) (* d2 10))))) ; (pprogn ; (fms "Shape: ~x0~%Field Depths: ~x1~%Avg Depth: ~x2.~x3~x4~x5~%" ; (list (cons #\0 binary-tree) ; (cons #\1 alist) ; (cons #\2 d0) ; (cons #\3 d1) ; (cons #\4 d2) ; (cons #\5 d3)) ; *standard-co* state nil) ; (value :invisible))))) ; ; (defmacro sbt (n) `(pprogn (show-binary-trees ,n state) (value :invisible)))) ; (defun record-maker-function-name (name) (intern-in-package-of-symbol (coerce (append (coerce "Make " 'list) (coerce (symbol-name name) 'list) (coerce " record" 'list)) 'string) name)) ; Record-accessor-function-name is now in axioms.lisp. (defun record-changer-function-name (name) (intern-in-package-of-symbol (coerce (append (coerce "Change " 'list) (coerce (symbol-name name) 'list) (coerce " record fields" 'list)) 'string) name)) (defmacro make (&rest args) (cond ((keyword-value-listp (cdr args)) (cons (record-maker-function-name (car args)) (cdr args))) (t (er hard 'record-error "Make was given a non-keyword as a field specifier. ~ The offending form is ~x0." (cons 'make args))))) ; Access is now in axioms.lisp. (defmacro change (&rest args) (cond ((keyword-value-listp (cddr args)) (cons (record-changer-function-name (car args)) (cdr args))) (t (er hard 'record-error "Change was given a non-keyword as a field specifier. ~ The offending form is ~x0." (cons 'change args))))) (defun make-record-car-cdrs1 (lst var) (cond ((null lst) var) (t (list (car lst) (make-record-car-cdrs1 (cdr lst) var))))) (defun make-record-car-cdrs (field-layout car-cdr-lst) (cond ((atom field-layout) (cond ((null field-layout) nil) (t (list (make-record-car-cdrs1 car-cdr-lst field-layout))))) (t (append (make-record-car-cdrs (car field-layout) (cons 'car car-cdr-lst)) (make-record-car-cdrs (cdr field-layout) (cons 'cdr car-cdr-lst)))))) (defun make-record-accessors (name field-lst car-cdrs cheap) (cond ((null field-lst) nil) (t (cons (cond (cheap (list 'defabbrev (record-accessor-function-name name (car field-lst)) (list (car field-lst)) (car car-cdrs))) (t (list 'defabbrev (record-accessor-function-name name (car field-lst)) (list (car field-lst)) (sublis (list (cons 'name name) (cons 'x (car field-lst)) (cons 'z (car car-cdrs))) '(prog2$ (or (and (consp x) (eq (car x) (quote name))) (record-error (quote name) x)) z))))) (make-record-accessors name (cdr field-lst) (cdr car-cdrs) cheap))))) (defun symbol-name-tree-occur (sym sym-tree) ; Sym is a symbol -- in fact, a keyword in proper usage -- and ; sym-tree is a tree of symbols. We ask whether a symbol with ; the same symbol-name as key occurs in sym-tree. If so, we return ; that symbol. Otherwise we return nil. (cond ((symbolp sym-tree) (cond ((equal (symbol-name sym) (symbol-name sym-tree)) sym-tree) (t nil))) ((atom sym-tree) nil) (t (or (symbol-name-tree-occur sym (car sym-tree)) (symbol-name-tree-occur sym (cdr sym-tree)))))) (defun some-symbol-name-tree-occur (syms sym-tree) (cond ((null syms) nil) ((symbol-name-tree-occur (car syms) sym-tree) t) (t (some-symbol-name-tree-occur (cdr syms) sym-tree)))) (defun make-record-changer-cons (fields field-layout x) ; Fields is the list of keyword field specifiers that are being ; changed. Field-layout is the user's layout of the record. X is the ; name of the variable holding the instance of the record. (cond ((not (some-symbol-name-tree-occur fields field-layout)) x) ((atom field-layout) field-layout) (t (list 'cons (make-record-changer-cons fields (car field-layout) (list 'car x)) (make-record-changer-cons fields (cdr field-layout) (list 'cdr x)))))) (defun make-record-changer-let-bindings (field-layout lst) ; Field-layout is the symbol tree provided by the user describing the ; layout of the fields. Lst is the keyword/value list in a change ; form. We want to bind each field name to the corresponding value. ; The only reason we take field-layout as an argument is that we ; don't know from :key which package 'key is in. (cond ((null lst) nil) (t (let ((var (symbol-name-tree-occur (car lst) field-layout))) (cond ((null var) (er hard 'record-error "A make or change form has used ~x0 as though ~ it were a legal field specifier in a record ~ with the layout ~x1." (car lst) field-layout)) (t (cons (list var (cadr lst)) (make-record-changer-let-bindings field-layout (cddr lst))))))))) (defun make-record-changer-let (name field-layout cheap rec lst) (cond (cheap (list 'let (cons (list 'record-changer-not-to-be-used-elsewhere rec) (make-record-changer-let-bindings field-layout lst)) (make-record-changer-cons (evens lst) field-layout 'record-changer-not-to-be-used-elsewhere))) (t (list 'let (cons (list 'record-changer-not-to-be-used-elsewhere rec) (make-record-changer-let-bindings field-layout lst)) (sublis (list (cons 'name name) (cons 'cons-nest (make-record-changer-cons (evens lst) field-layout '(cdr record-changer-not-to-be-used-elsewhere)))) '(prog2$ (or (and (consp record-changer-not-to-be-used-elsewhere) (eq (car record-changer-not-to-be-used-elsewhere) (quote name))) (record-error (quote name) record-changer-not-to-be-used-elsewhere)) (cons (quote name) cons-nest))))))) (defun make-record-changer (name field-layout cheap) (list 'defmacro (record-changer-function-name name) '(&rest args) (list 'make-record-changer-let (kwote name) (kwote field-layout) cheap '(car args) '(cdr args)))) (defun make-record-maker-cons (fields field-layout) ; Fields is the list of keyword field specifiers being initialized in ; a record. Field-layout is the user's specification of the layout. ; We lay down a cons tree isomorphic to field-layout whose tips are ; either the corresponding tip of field-layout or nil according to ; whether the keyword corresponding to the field-layout tip is in fields. (cond ((atom field-layout) (cond ((some-symbol-name-tree-occur fields field-layout) ; The above call is a little strange isn't it? Field-layout is an ; atom, a symbol really, and here we are asking whether any element of ; fields symbol-name-tree-occurs in it. We're really just exploiting ; some-symbol-name-tree-occur to walk down fields for us taking the ; symbol-name of each element and seeing if it occurs in (i.e., in ; this case, is) the symbol name of field-layout. field-layout) (t nil))) (t (list 'cons (make-record-maker-cons fields (car field-layout)) (make-record-maker-cons fields (cdr field-layout)))))) (defun make-record-maker-let (name field-layout cheap lst) (cond (cheap (list 'let (make-record-changer-let-bindings field-layout lst) (make-record-maker-cons (evens lst) field-layout))) (t (list 'let (make-record-changer-let-bindings field-layout lst) (list 'cons (kwote name) (make-record-maker-cons (evens lst) field-layout)))))) (defun make-record-maker (name field-layout cheap) (list 'defmacro (record-maker-function-name name) '(&rest args) (list 'make-record-maker-let (kwote name) (kwote field-layout) cheap 'args))) (defun make-record-field-lst (field-layout) (cond ((atom field-layout) (cond ((null field-layout) nil) (t (list field-layout)))) (t (append (make-record-field-lst (car field-layout)) (make-record-field-lst (cdr field-layout)))))) (defun record-maker-recognizer-name (name) ; We use the "WEAK-" prefix in order to avoid name clashes with stronger ; recognizers that one may wish to define. (declare (xargs :guard (symbolp name))) (intern-in-package-of-symbol (concatenate 'string "WEAK-" (symbol-name name) "-P") name)) (defun make-record-recognizer-body (field-layout) (declare (xargs :guard t)) (cond ((consp field-layout) (cond ((consp (car field-layout)) (cond ((consp (cdr field-layout)) `(and (consp x) (let ((x (car x))) ,(make-record-recognizer-body (car field-layout))) (let ((x (cdr x))) ,(make-record-recognizer-body (cdr field-layout))))) (t `(and (consp x) (let ((x (car x))) ,(make-record-recognizer-body (car field-layout))))))) ((consp (cdr field-layout)) `(and (consp x) (let ((x (cdr x))) ,(make-record-recognizer-body (cdr field-layout))))) (t '(consp x)))) (t t))) (defun make-record-recognizer (name field-layout cheap recog-name) `(defun ,recog-name (x) (declare (xargs :mode :logic :guard t)) ,(cond (cheap (make-record-recognizer-body field-layout)) (t `(and (consp x) (eq (car x) ',name) (let ((x (cdr x))) ,(make-record-recognizer-body field-layout))))))) (defun record-macros (name field-layout cheap recog-name) (declare (xargs :guard (or recog-name (symbolp name)))) (let ((recog-name (or recog-name (record-maker-recognizer-name name)))) (cons 'progn (append (make-record-accessors name (make-record-field-lst field-layout) (make-record-car-cdrs field-layout (if cheap nil '(cdr))) cheap) (list (make-record-changer name field-layout cheap) (make-record-maker name field-layout cheap) (make-record-recognizer name field-layout cheap recog-name)))))) ; WARNING: If you change the layout of records, you must change ; certain functions that build them in. Generally, these functions ; are defined before defrec was defined, but need to access ; components. See the warning associated with defrec rewrite-constant ; for a list of one group of such functions. You might also search ; for occurrences of the word defrec prior to this definition of it. (defmacro defrec (name field-lst cheap &optional recog-name) ; A recognizer with guard t has is defined using recog-name, if supplied; else, ; by default, its name for (defrec foo ...) is the symbol WEAK-FOO-P, in the ; same package as foo. (record-macros name field-lst cheap recog-name)) (defabbrev equalityp (term) ; Note that the fquotep below is commented out. This function violates ; our standard rules on the use of ffn-symb but is ok since we are looking ; for 'equal and not for 'quote or any constructor that might be hidden ; inside a quoted term. (and (nvariablep term) ; (not (fquotep term)) (eq (ffn-symb term) 'equal))) (defabbrev inequalityp (term) ; Note that the fquotep below is commented out. This function violates ; our standard rules on the use of ffn-symb but is ok since we are looking ; for 'equal and not for 'quote or any constructor that might be hidden ; inside a quoted term. (and (nvariablep term) ; (not (fquotep term)) (eq (ffn-symb term) '<))) (defabbrev consityp (term) ; Consityp is to cons what equalityp is equal: it recognizes terms ; that are non-evg cons expressions. (and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'cons))) (defun power-rep (n b) (if (< n b) (list n) (cons (rem n b) (power-rep (floor n b) b)))) (defun decode-idate (n) (let ((tuple (power-rep n 100))) (cond ((< (len tuple) 6) (er hard 'decode-idate "Idates are supposed to decode to a list of at least length six ~ but ~x0 decoded to ~x1." n tuple)) ((equal (len tuple) 6) tuple) (t ; In this case, tuple is (secs mins hrs day month yr1 yr2 ...) where 0 ; <= yri < 100 and (yr1 yr2 ...) represents a big number, yr, in base ; 100. Yr is the number of years since 1900. (let ((secs (nth 0 tuple)) (mins (nth 1 tuple)) (hrs (nth 2 tuple)) (day (nth 3 tuple)) (mo (nth 4 tuple)) (yr (power-eval (cdr (cddddr tuple)) 100))) (list secs mins hrs day mo yr)))))) (defun pcd2 (n channel state) (declare (xargs :guard (integerp n))) (cond ((< n 10) (pprogn (princ$ "0" channel state) (princ$ n channel state))) (t (princ$ n channel state)))) (defun print-idate (n channel state) (let* ((x (decode-idate n)) (sec (car x)) (minimum (cadr x)) (hrs (caddr x)) (day (cadddr x)) (mo (car (cddddr x))) (yr (cadr (cddddr x)))) ; yr = years since 1900. It is possible ; that yr > 99! (pprogn (princ$ (nth (1- mo) '(|January| |February| |March| |April| |May| |June| |July| |August| |September| |October| |November| |December|)) channel state) (princ$ #\Space channel state) (princ$ day channel state) (princ$ '|,| channel state) (princ$ #\Space channel state) (princ$ (+ 1900 yr) channel state) (princ$ " " channel state) (pcd2 hrs channel state) (princ$ '|:| channel state) (pcd2 minimum channel state) (princ$ '|:| channel state) (pcd2 sec channel state) state))) (defun print-current-idate (channel state) (mv-let (d state) (read-idate state) (print-idate d channel state))) ; Essay on Inhibited Output and the Illusion of Windows ; The "io" in io?, below, stands for "inhibit output". Roughly speaking, it ; takes an unevaluated symbolic token denoting a "kind" of output, an output ; shape involving STATE, and a form with the indicated output signature. ; If the "kind" of output is currently inhibited, it returns all nils and the ; current state, e.g., (mv nil state nil) in the case where the output ; shape is something like (mv x state y). If the kind of output is not ; inhibited, the form is evaluated and its value is returned. ; If form always returned an error triple, this could be said as: ; `(cond ((member-eq ',token (f-get-global 'inhibit-output-lst state)) ; (value nil)) ; (t ,form)) ; This whole macro is just a simple way to do optionally inhibited output. ; The introduction of an emacs window-based interface, led us to put a little ; more functionality into this macro. Each kind of output has a window ; associated with it. If the kind of output is uninhibited, the io? macro ; sends to *standard-co* certain auxiliary output which causes the ; *standard-co* output by form to be shipped to the designated window. ; The association of windows is accomplished via the constant ; *window-descriptions* below which contains elements of the form (token str ; clear cursor-at-top pop-up), where token is a "kind" of output, str ; identifies the associated window, and the remaining components specify ; options for how output to the window is handled by default. The io? macro ; provides keyword arguments for overriding these defaults. If :clear t is ; specified, the window is cleared before the text is written into it, ; otherwise the text is appended to the end. If :cursor-at-top t is specified, ; the cursor is left at the top of the inserted text, otherwise it is left at ; the bottom of the inserted text. If :pop-up t is specified, the window is ; raised to the top of the desktop, otherwise the window remains where it was. ; We have purposely avoided trying to suggest that windows are objects in ACL2. ; We have no way to create them or manage them. We merely ship a sequence of ; characters to *standard-co* and let the host do whatever it does with them. ; Extending ACL2 with some window abstraction is a desirable thing to do. I ; would like to be able to manipulate windows as ACL2 objects. But that is ; beyond the scope of the current work whose aim is merely to provide a more ; modern interface to ACL2 without doing too much violence to ACL2's ; applicative nature or to its claim to be Common Lisp. Those two constraints ; make the introduction of true window objects truly interesting. ; Finally io? allows for the entire io process to be illusory. This occurs if ; the commentp argument is t. In this case, the io? form is logically ; equivalent to NIL. The actual output is performed after opening a wormhole ; to state. (defconst *window-descriptions* ; str clr top pop '((proof-tree "0" t t nil) (rewrite-state "1" t nil nil) (frame "2" t t t) (error "3" t t t) (warning! "3" t t t) (warning "3" t t t) (observation "3" t t t) (prove "4" nil nil nil) (event "4" nil nil nil) (expansion "4" nil nil nil) (summary "4" nil nil nil) (chronology "5" t nil nil) (proof-checker "6" nil nil nil) (temporary "t" t t t) (query "q" t t t))) (defun io?-nil-output (lst default-bindings) (cond ((null lst) nil) (t (cons (cond ((eq (car lst) 'state) 'state) ((cadr (assoc-eq (car lst) default-bindings))) (t nil)) (io?-nil-output (cdr lst) default-bindings))))) (defmacro check-exact-free-vars (ctx vars form) ; A typical use of this macro is (check-free-vars io? vars form) which just ; expands to the translation of form provided all vars occurring freely in form ; are among vars and vice-versa. The first argument is the name of the calling ; routine, which is used in error reporting. (declare (xargs :guard (symbol-listp vars))) `(translate-and-test (lambda (term) (let ((vars ',vars) (all-vars (all-vars term))) (cond ((not (subsetp-eq all-vars vars)) (msg "Free vars problem with ~x0: Variable~#1~[~/s~] ~&1 ~ occur~#1~[s~/~] in ~x2 even though not declared." ',ctx (set-difference-eq all-vars vars) term)) ((not (subsetp-eq vars all-vars)) (msg "Free vars problem with ~x0: Variable~#1~[~/s~] ~&1 ~ ~#1~[does~/do~] not occur in ~x2 even though declared." ',ctx (set-difference-eq vars all-vars) term)) (t t)))) ,form)) (defun formal-bindings (vars) ; For example, if vars is (ab cd) then return the object ; ((list (quote ab) (list 'quote ab)) (list (quote cd) (list 'quote cd))). (if (endp vars) nil (cons (list 'list (list 'quote (car vars)) (list 'list ''quote (car vars))) (formal-bindings (cdr vars))))) (defrec io-record ; WARNING: We rely on the shape of this record in io-record-forms. ; Note: As of Version_3.4 we do not use any io-marker other than :ctx. Earlier ; versions might not have made any real use of those either, writing but not ; reading them. (io-marker . form) t) (defmacro io-record-forms (io-records) ; WARNING: If you change this macro, consider changing (defrec io-record ...) ; too. `(strip-cdrs ,io-records)) (defun push-io-record (io-marker form state) (f-put-global 'saved-output-reversed (cons (make io-record :io-marker io-marker :form form) (f-get-global 'saved-output-reversed state)) state)) (defun saved-output-token-p (token state) (and (f-get-global 'saved-output-p state) (or (eq (f-get-global 'saved-output-token-lst state) :all) (member-eq token (f-get-global 'saved-output-token-lst state))))) (defun io?-wormhole-bindings (i vars) (declare (xargs :guard (and (true-listp vars) (natp i)))) (cond ((endp vars) nil) (t (cons (list (car vars) `(nth ,i (@ wormhole-input))) (io?-wormhole-bindings (1+ i) (cdr vars)))))) (defmacro io? (token commentp shape vars body &key (clear 'nil clear-argp) (cursor-at-top 'nil cursor-at-top-argp) (pop-up 'nil pop-up-argp) (default-bindings 'nil) (chk-translatable 't)) ; Typical use (io? error nil (mv col state) (x y) (fmt ...)), meaning execute ; the fmt statement unless 'error is on 'inhibit-output-lst. The mv expression ; is the shape of the output produced by the fmt expression, and the list (x y) ; for vars indicates the variables other than state that occur free in that ; expression. See the comment above, and see the Essay on Saved-output for a ; comment that gives a convenient macro for obtaining the free variables other ; than state that occur free in body. ; Default-bindings is a list of doublets (symbol value). It is used in order ; to supply a non-nil return value for other than state when io is suppressed. ; For example, fmt returns col and state, as suggested by the third (shape) ; argument below. Without the :default-bindings, this form would evaluate to ; (mv nil state) if event IO is inhibited. But there are fixnum declarations ; that require the first return value of fmt to be an integer, and we can ; specify the result in the inhibited case to be (mv 0 state) with the ; following :default-bindings: ; (io? event nil (mv col state) nil (fmt ...) :default-bindings ((col 0))) ; The values in :default-bindings are evaluated, so it would be equivalent to ; replace 0 with (- 4 4), for example. ; Keep argument list in sync with io?@par. ; Chk-translatable is only used when commentp is not nil, to check at translate ; time that the body passes translation relative to the given shape. ; (Otherwise such a check is only made when the wormhole call below is actually ; evaluated.) (declare (xargs :guard (and (symbolp token) (symbol-listp vars) (no-duplicatesp vars) (not (member-eq 'state vars)) (assoc-eq token *window-descriptions*)))) (let* ((associated-window (assoc-eq token *window-descriptions*)) (expansion `(let* ((io?-output-inhibitedp (member-eq ',token (f-get-global 'inhibit-output-lst state))) (io?-alist (and (not io?-output-inhibitedp) (list (cons #\w ,(cadr associated-window)) (cons #\c ,(if clear-argp clear (caddr associated-window))) (cons #\t ,(if cursor-at-top-argp cursor-at-top (cadddr associated-window))) (cons #\p ,(if pop-up-argp pop-up (car (cddddr associated-window)))) ; Peter Dillinger requested the following binding, so that he could specify a ; window prelude string that distinguishes between, for example, "prove", ; "event", and "summary" output, which with the default string would all just ; show up as window 4. (cons #\k ,(symbol-name token)))))) (pprogn (if (or io?-output-inhibitedp (null (f-get-global 'window-interfacep state))) state (mv-let (io?-col state) (fmt1! (f-get-global 'window-interface-prelude state) io?-alist 0 *standard-co* state nil) (declare (ignore io?-col)) state)) ,(let ((body `(check-vars-not-free (io?-output-inhibitedp io?-alist) (check-exact-free-vars io? (state ,@vars) ,body))) (nil-output (if (eq shape 'state) 'state (cons 'mv (io?-nil-output (cdr shape) default-bindings)))) (postlude `(mv-let (io?-col state) (if (or io?-output-inhibitedp (null (f-get-global 'window-interfacep state))) (mv 0 state) (fmt1! (f-get-global 'window-interface-postlude state) io?-alist 0 *standard-co* state nil)) (declare (ignore io?-col)) (check-vars-not-free (io?-output-inhibitedp io?-alist io?-col) ,shape)))) (let ((body (if commentp `(let ,(io?-wormhole-bindings 0 vars) ,body) body))) (cond ((eq shape 'state) `(pprogn (if io?-output-inhibitedp state ,body) ,postlude)) (t `(mv-let ,(cdr shape) (if io?-output-inhibitedp ,nil-output ,body) ,postlude))))))))) (cond (commentp (let ((form (cond ((eq shape 'state) `(pprogn ,expansion (value :q))) (t `(mv-let ,(cdr shape) ,expansion (declare (ignore ,@(remove1-eq 'state (cdr shape)))) (value :q)))))) `(prog2$ ,(if chk-translatable `(chk-translatable ,body ,shape) nil) (wormhole 'comment-window-io '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) (list ,@vars) ',form :ld-error-action :return! :ld-verbose nil :ld-pre-eval-print nil :ld-prompt nil)))) (t `(pprogn (cond ((saved-output-token-p ',token state) (push-io-record nil ; io-marker (list 'let (list ,@(formal-bindings vars)) ',expansion) state)) (t state)) ,expansion))))) #+acl2-par (defmacro io?@par (token commentp &rest rst) ; This macro is the same as io?, except that it provides the extra property ; that the commentp flag is overridden to use comment-window printing. ; Keep the argument list in sync with io?. ; Parallelism blemish: surround the io? call below with a suitable lock. Once ; this is done, remove any redundant locks around io?@par calls. (declare (ignore commentp)) `(io? ,token t ,@rst)) (defmacro io?-prove (vars body &rest keyword-args) ; Keep in sync with io?-prove-cw. `(io? prove nil state ,vars (if (gag-mode) state ,body) ,@keyword-args)) (defmacro io?-gag (vars body &rest keyword-args) `(io? prove nil state ,vars (if (gag-mode) ,body state) ,@keyword-args)) (defun output-ignored-p (token state) (and (not (saved-output-token-p token state)) (member-eq token (f-get-global 'inhibit-output-lst state)))) (defun error1 (ctx str alist state) ; Warning: Keep this in sync with error1-safe and error1@par. ":Doc-Section ACL2::ACL2-built-ins print an error message and cause a ``soft error''~/ ~c[(Error1 ctx str alist)] returns ~c[(mv t nil state)]. An error message is first printed using the the ``context'' ~c[ctx], as well as the string ~c[str] and alist ~c[alist] that are of the same kind as expected by ~ilc[fmt]. ~l[fmt]. ~c[Error1] can be interpreted as causing an ``error'' when programming with the ACL2 ~ilc[state], something most ACL2 users will probably not want to do; ~pl[ld-error-triples] and ~pl[er-progn]. In order to cause errors with ~c[:]~ilc[logic] mode functions, ~pl[hard-error] and ~pl[illegal]. Better yet, ~pl[er] for a macro that provides a unified way of signaling errors.~/ As mentioned above, ~c[error1] always returns ~c[(mv t nil state)]. But if a call ~c[(error1 ctx str alist)] is encountered during evaluation, then the string ~c[str] is first printed using the association list ~c[alist] (as in ~ilc[fmt]). Here is a trivial, contrived example. ~bv[] ACL2 !>(error1 'my-context \"Printing 4: ~~n0\" (list (cons #\\0 4)) state) ACL2 Error in MY-CONTEXT: Printing 4: four ACL2 !> ~ev[]~/" (pprogn (io? error nil state (alist str ctx) (error-fms nil ctx str alist state)) (mv t nil state))) #+acl2-par (defun error1@par (ctx str alist state) ; Keep in sync with error1. We accept state so that calls to error1 and ; error1@par look the same. (declare (ignore state)) (prog2$ (io? error t state (alist str ctx) (error-fms nil ctx str alist state) :chk-translatable nil) (mv@par t nil state))) (defun error1-safe (ctx str alist state) ; Warning: Keep this in sync with error1. ; Note: One can rely on this returning a value component of nil. (pprogn (io? error nil state (alist str ctx) (error-fms nil ctx str alist state)) (mv nil nil state))) (defconst *uninhibited-warning-summaries* '("Uncertified" "Provisionally certified" "Skip-proofs" "Defaxioms" "Ttags" ; The above are included because of soundness. But "Compiled file", below, is ; included so that we can see it even when inside include-book, since messages ; printed by missing-compiled-book may assume that such warnings are not ; inhibited. "Compiled file")) (defun warning-off-p1 (summary wrld ld-skip-proofsp) ; This function is used by warning$ to determine whether a given warning should ; be printed. See also warning-disabled-p, which we can use to avoid needless ; computation on behalf of disabled warnings. (or (and summary (assoc-string-equal summary (table-alist 'inhibit-warnings-table wrld))) ; The above is sufficient to turn off (warning$ "string" ...). But even when ; the above condition isn't met, we turn off all warnings -- with the exception ; of those related to soundness -- while including a book. (and (or (eq ld-skip-proofsp 'include-book) (eq ld-skip-proofsp 'include-book-with-locals) (eq ld-skip-proofsp 'initialize-acl2)) (not (and summary (member-string-equal summary *uninhibited-warning-summaries*)))))) (defun warning-off-p (summary state) (warning-off-p1 summary (w state) (ld-skip-proofsp state))) (defrec state-vars ; Warning: Keep this in sync with default-state-vars. ((hons-enabled safe-mode . temp-touchable-vars) . (guard-checking-on ld-skip-proofsp temp-touchable-fns . parallel-execution-enabled)) nil) (defmacro default-state-vars (state-p &key (safe-mode 'nil safe-mode-p) (temp-touchable-vars 'nil temp-touchable-vars-p) (guard-checking-on 't guard-checking-on-p) (ld-skip-proofsp 'nil ld-skip-proofsp-p) (temp-touchable-fns 'nil temp-touchable-fns-p) (parallel-execution-enabled 'nil parallel-execution-enabled-p)) ; Warning: Keep this in sync with defrec state-vars. ; State-p is t to indicate that we use the current values of the relevant state ; globals. Otherwise we use the specified defaults, which are supplied above ; for convenience but can be changed there (i.e., in this code) if better ; default values are found. The value :hons for state-p is treated like nil, ; except that state-var hons-enabled is t rather than nil. (cond ((eq state-p t) `(make state-vars :hons-enabled (hons-enabledp state) :safe-mode ,(if safe-mode-p safe-mode '(f-get-global 'safe-mode state)) :temp-touchable-vars ,(if temp-touchable-vars-p temp-touchable-vars '(f-get-global 'temp-touchable-vars state)) :guard-checking-on ,(if guard-checking-on-p guard-checking-on '(f-get-global 'guard-checking-on state)) :ld-skip-proofsp ,(if ld-skip-proofsp-p ld-skip-proofsp '(f-get-global 'ld-skip-proofsp state)) :temp-touchable-fns ,(if temp-touchable-fns-p temp-touchable-fns '(f-get-global 'temp-touchable-fns state)) :parallel-execution-enabled ,(if parallel-execution-enabled-p parallel-execution-enabled '(f-get-global 'parallel-execution-enabled state)))) (t ; state-p is not t `(make state-vars :hons-enabled ,(eq state-p :hons) :safe-mode ,safe-mode :temp-touchable-vars ,temp-touchable-vars :guard-checking-on ,guard-checking-on :ld-skip-proofsp ,ld-skip-proofsp :temp-touchable-fns ,temp-touchable-fns :parallel-execution-enabled ,parallel-execution-enabled)))) (defun warning1-body (ctx summary str alist state) (let ((channel (f-get-global 'proofs-co state))) (pprogn (if summary (push-warning summary state) state) (mv-let (col state) (fmt "ACL2 Warning~#0~[~/ [~s1]~]" (list (cons #\0 (if summary 1 0)) (cons #\1 summary)) channel state nil) (mv-let (col state) (fmt-in-ctx ctx col channel state) (fmt-abbrev str alist col channel state "~%~%")))))) (defmacro warning1-form (commentp) ; See warning1. `(mv-let (check-warning-off summary) (cond ((consp summary) (mv nil (car summary))) (t (mv t summary))) (cond ((and check-warning-off ,(if commentp '(warning-off-p1 summary wrld (access state-vars state-vars :ld-skip-proofsp)) '(warning-off-p summary state))) ,(if commentp nil 'state)) ; Note: There are two io? expressions below. They are just alike except ; that the first uses the token WARNING! and the other uses WARNING. Keep ; them that way! ((and summary (member-string-equal summary *uninhibited-warning-summaries*)) (io? WARNING! ,commentp state (summary ctx alist str) (warning1-body ctx summary str alist state) :chk-translatable nil)) (t (io? WARNING ,commentp state (summary ctx alist str) (warning1-body ctx summary str alist state) :chk-translatable nil))))) (defun warning1 (ctx summary str alist state) ; This function prints the "ACL2 Warning" banner and ctx, then the ; user's summary, str and alist, and then two carriage returns. (warning1-form nil)) (defmacro warning$ (&rest args) ; A typical use of this macro might be: ; (warning$ ctx "Loops" "The :REWRITE rule ~x0 loops forever." name) or ; (warning$ ctx nil "The :REWRITE rule ~x0 loops forever." name). ; If the second argument is wrapped in a one-element list, as in ; (warning$ ctx ("Loops") "The :REWRITE rule ~x0 loops forever." name), ; then that argument is quoted, and no check will be made for whether the ; warning is disabled, presumably because we are in a context where we know the ; warning is enabled. (list 'warning1 (car args) ; We seem to have seen a GCL 2.6.7 compiler bug, laying down bogus calls of ; load-time-value, when replacing (consp (cadr args)) with (and (consp (cadr ; args)) (stringp (car (cadr args)))). But it seems fine to have the semantics ; of warning$ be that conses are quoted in the second argument position. (if (consp (cadr args)) (kwote (cadr args)) (cadr args)) (caddr args) (make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (cdddr args)) 'state)) (defmacro warning-disabled-p (summary) ; We can use this function to avoid needless computation on behalf of disabled ; warnings. (declare (xargs :guard (stringp summary))) (let ((tp (if (member-equal summary *uninhibited-warning-summaries*) 'warning! 'warning))) `(or (output-ignored-p ',tp state) (warning-off-p ,summary state)))) (defmacro observation1-body (commentp) `(io? observation ,commentp state (str alist ctx abbrev-p) (let ((channel (f-get-global 'proofs-co state))) (mv-let (col state) (fmt "ACL2 Observation" nil channel state nil) (mv-let (col state) (fmt-in-ctx ctx col channel state) (cond (abbrev-p (fmt-abbrev str alist col channel state "~|")) ((null abbrev-p) (mv-let (col state) (fmt1 str alist col channel state nil) (declare (ignore col)) (newline channel state))) (t (prog2$ (er hard 'observation1 "The abbrev-p (fourth) argument of ~ observation1 must be t or nil, so the ~ value ~x0 is illegal." abbrev-p) state)))))) :chk-translatable nil)) (defun observation1 (ctx str alist abbrev-p state) ; This function prints the "ACL2 Observation" banner and ctx, then the ; user's str and alist, and then a carriage return. (observation1-body nil)) (defun observation1-cw (ctx str alist abbrev-p) (observation1-body t)) (defmacro observation (&rest args) ; A typical use of this macro might be: ; (observation ctx "5 :REWRITE rules are being stored under name ~x0." name). ":Doc-Section ACL2::ACL2-built-ins print an observation~/ Here is a typical application of ~c[observation]. ~bv[] ACL2 !>(let ((ctx 'top-level) (name 'foo)) (observation ctx \"Skipping processing of name ~~x0.\" name)) ACL2 Observation in TOP-LEVEL: Skipping processing of name FOO. ACL2 !> ~ev[] ~c[Observation] prints an initial ``~c[ACL2 Observation...: ]'', and then prints the indicated message using formatted printing (~pl[fmt]). Notice in the example above that evaluation of a call of ~c[observation] returns ~ilc[state]. Indeed, ~c[observation] is actually a macro whose expansion takes and returns the ACL2 ~ilc[state]. A similar utility, ~c[observation-cw], is available that does not take or return ~c[state]; rather, it returns ~c[nil] as the suffix ``~c[cw]'' suggests that a ``comment window'' is the target of this printing, rather than the state. For example: ~bv[] ACL2 !>(let ((ctx 'top-level) (name 'foo)) (observation-cw ctx \"Skipping processing of name ~~x0.\" name)) ACL2 Observation in TOP-LEVEL: Skipping processing of name FOO. NIL ACL2 !> ~ev[] ~c[Observation-cw] takes exactly the same arguments as ~c[observation], but ~c[observation-cw] does its printing in a so-called ``wormhole''; ~pl[wormhole].~/ ~bv[] General Forms: (observation ctx fmt-string fmt-arg1 fmt-arg2 ... fmt-argk) (observation-cw ctx fmt-string fmt-arg1 fmt-arg2 ... fmt-argk) ~ev[] where ~c[ctx] generally evaluates to a symbol (but see below), and ~c[fmt-string] together with the ~c[fmt-argi] are suitable for passing to ~ilc[fmt]. Output begins and ends with a newline. Recall from the example above that the output from a call of ~c[observation] (or ~c[observation-cw]) begins with ``~c[ACL2 Observation]'' and additional characters ending in ``~c[: ]'', for example ``~c[ in TOP-LEVEL: ]'', followed by formatted output produced from ~c[fmt-string] with the given ~c[fmt-argi]. The characters printed immediately following the string ``~c[ACL2 Observation]'' depend on the value of ~c[ctx]. If ~c[ctx] is ~c[nil], nothing is printed. If ~c[ctx] is a non-~c[nil] symbol, it is printed using ~ilc[fmt] directive ~c[~~x]. If ~c[ctx] is a ~ilc[cons] pair whose ~ilc[car] is a symbol, formatted printing is applied to the string \"(~~x0 ~~x1 ...)\", where ~c[#\\0] and ~c[#\\1] are bound respectively to that car and cdr. Otherwise, ~c[ctx] is printed using ~ilc[fmt] directive ~c[~~@]. We next discuss situations in which printing is inhibited for ~c[observation] and ~c[observation-cw]. No printing is done when ~c[observation] is among the inhibited output types; ~pl[set-inhibit-output-lst]. Moreover, no printing is done by ~c[observation] during ~ilc[include-book]. If you want to avoid printing from ~c[observation-cw] during ~ilc[include-book], then you need to manage that yourself." `(cond ((or (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals) (eq (ld-skip-proofsp state) 'initialize-acl2)) state) (t (observation1 ,(car args) ,(cadr args) ,(make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (cddr args)) t state)))) (defmacro observation-cw (&rest args) ; See observation. In #-acl2-par, this macro uses wormholes to avoid modifying ; state, and prints even when including books. In #+acl2-par, to avoid ; wormholes, which are known not to be thread-safe, we simply call cw. ; See observation. This macro uses wormholes to avoid accessing state, and ; prints even when including books. ; We considered using the @par naming scheme to define this macro in ; #+acl2-par, but the name would then have "@par" in it, which could jar users. #-acl2-par `(observation1-cw ,(car args) ,(cadr args) ,(make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (cddr args)) t) #+acl2-par ; Parallelism blemish: consider using *the-live-state* to disable ; observation-cw, i.e., to avoid the cw call below, when observations are ; turned off. But note that if we have such #-acl2-loop-only code, users might ; be surprised when their own use of observation-cw doesn't benefit from such ; restrictions. `(cw ,(cadr args) ,@(cddr args))) (defun skip-when-logic (str state) (pprogn (observation 'top-level "~s0 events are skipped when the default-defun-mode is ~x1." str (default-defun-mode-from-state state)) (mv nil nil state))) (defun chk-inhibit-output-lst (lst ctx state) (cond ((not (true-listp lst)) (er soft ctx "The argument to set-inhibit-output-lst must evaluate to a ~ true-listp, unlike ~x0." lst)) ((not (subsetp-eq lst *valid-output-names*)) (er soft ctx "The argument to set-inhibit-output-lst must evaluate to a ~ subset of the list ~X01, but ~x2 contains ~&3." *valid-output-names* nil lst (set-difference-eq lst *valid-output-names*))) (t (let ((lst (if (member-eq 'warning! lst) (add-to-set-eq 'warning lst) lst))) (pprogn (cond ((and (member-eq 'prove lst) (not (member-eq 'proof-tree lst)) (member-eq 'proof-tree (f-get-global 'inhibit-output-lst state))) (warning$ ctx nil "The printing of proof-trees is being ~ enabled while the printing of proofs ~ is being disabled. You may want to ~ execute :STOP-PROOF-TREE in order to ~ inhibit proof-trees as well.")) (t state)) (value lst)))))) ; With er defined, we may now define chk-ld-skip-proofsp. (defconst *ld-special-error* "~x1 is an illegal value for the state global variable ~x0. See ~ :DOC ~x0.") (defun chk-ld-skip-proofsp (val ctx state) (declare (xargs :mode :program)) (cond ((member-eq val '(t nil include-book initialize-acl2 include-book-with-locals)) (value nil)) (t (er soft ctx *ld-special-error* 'ld-skip-proofsp val)))) (defun set-ld-skip-proofsp (val state) (declare (xargs :mode :program)) (er-progn (chk-ld-skip-proofsp val 'set-ld-skip-proofsp state) (pprogn (f-put-global 'ld-skip-proofsp val state) (value val)))) (defmacro set-ld-skip-proofs (val state) ; Usually the names of our set utilities do not end in "p". We leave ; set-ld-skip-proofsp for backward compatibility, but we add this version ; for consistency. (declare (ignore state)) ; avoid a stobj problem `(set-ld-skip-proofsp ,val state)) (defun set-write-acl2x (val state) ":Doc-Section switches-parameters-and-modes cause ~ilc[certify-book] to write out a ~c[.acl2x] file~/ ~bv[] Example Forms: (set-write-acl2x nil state) (set-write-acl2x t state) (set-write-acl2x '(nil) state) ; same as just above, but allow inclusion of ; uncertified books during certify-book (set-write-acl2x '(t) state) (set-write-acl2x '(include-book-with-locals) state) ~ev[] ~bv[] General Form: (set-write-acl2x val state) ~ev[] where ~c[val] evaluates to ~c[t], ~c[nil], or a one-element list whose element is a legal value for the global ~c['ld-skip-proofsp]; ~pl[ld-skip-proofsp]. The value returned is an error triple, which in the non-error case is ~c[(mv nil v state)], where ~c[v] is the value of ~c[val] and ~c[state] is the result of updating the input ~il[state] by assigning state global ~c['write-acl2x] the value ~c[v]. The command ~c[(set-write-acl2x val state)] assigns the value of ~c[val] to the ~il[state] global variable ~c['write-acl2x], affecting whether or not ~ilc[certify-book] writes out a file with extension ~c[acl2x], called a ``~c[.acl2x] file'' and pronounced ``dot-acl2x file''. Such a file is read or written by ~ilc[certify-book] when it is supplied with keyword argument ~c[:acl2x t]. By default, such a call of ~c[certify-book] reads a ~c[.acl2x] file; but if the value of state global variable ~c['write-acl2x] is not ~c[nil], then ~c[certify-book] writes a ~c[.acl2x] file (in which case it is illegal to specify a non-~c[nil] value for ~ilc[certify-book] keyword argument ~c[:pcert]). Consider for example ~c[(certify-book \"foo\" 0 nil :acl2x t)]. By default, this command reads file ~c[foo.acl2x], which supplies replacements for some forms in ~c[foo.lisp], as described later below. But if the value of state global ~c['write-acl2x] is not ~c[nil], then instead, this ~c[certify-book] command writes such a file ~c[foo.acl2x]. Before we discuss the function of ~c[.acl2x] files, we first explain more about how a non-~c[nil] value of ~il[state] global ~c['write-acl2x] affects the behavior of a command ~c[(certify-book ... :acl2x t ...)]. A significant effect on the behavior is that after processing events in the given book, ACL2 writes out a ~c[.acl2x] file and then returns, skipping the other subsequent actions typically performed by ~ilc[certify-book]: a ~il[local-incompatibility] check, writing of a ~il[certificate] file, and possibly ~il[compilation]. Another effect is that proofs may be skipped when processing ~il[events] assuming that the the ~c[certify-book] command does not explicitly specify ~c[:skip-proofs-okp nil], as we now explain. A non-~c[nil] value of ~c['write-acl2x] should either be ~c[t] or a one-element list ~c[(x)], where ~c[x] is a legal value for the ~il[state] global ~c['ld-skip-proofsp] (~pl[ld-skip-proofsp]). In both cases, ~c[certify-book] will process ~il[events] to write out a ~c[.acl2x] file as described above. But in the latter (list) case, event processing will take place according to the value of ~c[x]: in particular, proofs will be skipped when ~c[x] is not ~c[nil], and if moreover ~c[x] is the symbol ~c[include-book-with-locals], then only one pass will be made through each ~ilc[encapsulate] form. A third effect of a non-~c[nil] value of ~c['write-acl2x], which is restricted to the list case, is that ~il[include-book] events encountered during event processing are allowed to succeed on uncertified books, something that is prohibited during most calls of ~ilc[certify-book]. When ~ilc[certify-book] is used to write out a ~c[.acl2x] file, there is typically a subsequent run of ~ilc[certify-book] that reads that file. Consider how this can work with a book ~c[foo.lisp]. In the first call of ~c[certify-book], a file ~c[foo.acl2x] is written that contains all ~ilc[make-event] expansions, but ~c[foo.cert] is not written. In the second call of ~c[certify-book], no ~ilc[make-event] expansion typically takes place, because ~c[foo.acl2x] supplies the expansions. The command ~c[(set-write-acl2x t state)] should be evaluated before the first certification (though another legal non-~c[nil] value may be used in place of ~c[t]), setting the value of ~ilc[state] global ~c['write-acl2x] to ~c[t], to enable writing of ~c[foo.acl2x]; and the command ~c[(set-write-acl2x nil state)] may be evaluated before the second run (though this is not necessary in a fresh ACL2 session) in order to complete the certification (writing out ~c[foo.cert]) using ~c[foo.acl2x] to supply the ~ilc[make-event] expansions. When ~ilc[Certify-book] is supplied with keyword argument ~c[:acl2x t] it will read or write the book's ~c[.acl2x] file; when supplied with ~c[:acl2x nil], it will not read or write that ~c[.acl2x] file. The value of ~c[:acl2x] is ~c[nil] by default. The interaction of ~ilc[certify-book] with the corresponding ~c[.acl2x] file is as follows. ~bf[] o If ~c[:acl2x] is ~c[t], then: - If ~c[set-write-acl2x] has been (most recently) called with a value of ~c[t] for its first argument, then ACL2 writes the corresponding ~c[.acl2x] file. - If ~c[set-write-acl2x] has been (most recently) called with a value of ~c[nil] for its first argument, or not called at all, then ACL2 insists on a corresponding ~c[.acl2x] file that is at least as recent as the corresponding ~c[.lisp] file, causing an error otherwise. o If ~c[:acl2x] is ~c[nil], then: - If ~c[set-write-acl2x] has been (most recently) called with a value ~c[t] for its first argument, or if argument ~c[:ttagsx] is supplied, then an error occurs. - If the ~c[.acl2x] file exists, then regardless of whether or how ~c[set-write-acl2x] has been called, ACL2 ignores the ~c[.acl2x] file but issues a warning about it. ~ef[] Suppose you use the two-runs approach: first write a ~c[.acl2x] file, then certify using (reading) that ~c[.acl2x] file. Then with scripts such as makefiles, then you may wish to provide a single ~ilc[certify-book] command to use for both runs. For that purpose, ~ilc[certify-book] supports the keyword argument ~c[:ttagsx]. If this argument is supplied and ~c[write-acl2x] is true, then this argument is treated as the ~c[:ttags] argument, overriding a ~c[:ttags] argument if present. That is, for the two runs, ~c[:ttagsx] may be used to specify the trust tags used in the first certification while ~c[:ttags] specifies the trust tags, if any (else ~c[:ttags] may be omitted), used in the second certification. Note: If the argument ~c[:ttagsx] is not supplied, then its value defaults to the (explicit or default) value of the ~c[:ttags] argument. The built-in ACL2 Makefile support automatically generates suitable dependencies if you create a ~c[.acl2] file with a ~ilc[certify-book] call matching the following regular expression, case-independent: ~bv[] (certify-book[^;]*:acl2x t ~ev[] For an example ~c[.acl2] file with a ~c[certify-book] call matching the above pattern, see community books file ~c[books/make-event/double-cert-test-1.acl2]. Note that ~ilc[include-book] is generally not affected by ~c[set-write-acl2x], other than through the indirect effect on ~ilc[certify-book]. More precisely: All expansions are stored in the ~il[certificate] file, so when ~ilc[include-book] is applied to a certified book, the ~c[.acl2x] file is not consulted.~/ An example of how to put this all together may be found in community book ~c[books/make-event/double-cert-test-1.lisp]. There, we see the following form. ~bv[] (make-event (progn (defttag :my-ttag) (progn! (let ((val (sys-call \"pwd\" nil))) (value (list 'defun 'foo () val)))))) ~ev[] Imagine that in place of the binding computed using ~ilc[sys-call], which by the way requires a trust tag, is some computation of your choice (such as reading forms from a file) that is used to construct your own event, in place of the ~ilc[defun] event constructed above. The ~c[Makefile] in that directory contains the following added dependency, so that file ~c[double-cert-test-1.acl2x] will be created: ~bv[] double-cert-test-1.cert: double-cert-test-1.acl2x ~ev[] There is also the file ~c[double-cert-test-1.acl2] in that directory, which contains a single form as follows. ~bv[] (certify-book \"double-cert-test-1\" ? t :ttagsx :all :ttags nil) ~ev[] Thus, a call of `make' first creates file ~c[double-cert-test-1.acl2x], which uses the above ~c[:ttagsx] argument in order to support the use of ~ilc[defttag] during ~ilc[make-event] expansion. Then, `make' goes on to cause a second certification in which no trust tags are involved. As a result, the parent book ~c[double-cert-test.lisp] is ultimately certified without requiring any trust tags. The discussion above is probably sufficient for most users of the two-run approach it describes. We conclude with further details for those who want more information. Those who wish to see a yet lower-level explanation of how all this works are invited to read the comment in the ACL2 source code entitled ``Essay on .acl2x Files (Double Certification). Consider the ~c[.acl2x] file produced by the first run as described above. It contains a single expression, which is an association list whose keys are all positive integers, which occur in increasing order. When the ~c[.acl2x] file is present and at least as recent as the corresponding ~c[.lisp] file, then for a subsequent ~ilc[certify-book] with argument ~c[:acl2x t] and the (default) value of ~c[nil] for ~il[state] global ~c['write-acl2x], that association list will be applied to the top-level events in the book, as follows. Suppose the entry ~c[(n . ev)] belongs to the association list in the ~c[.acl2x] file. Then ~c[n] is a positive integer, and the ~c[n]th top-level event in the book ~-[] where the ~c[0]th event is the initial ~ilc[in-package] form ~-[] will be replaced by ~c[ev]. In practice, ~c[ev] is the ~ilc[make-event] expansion created during certification for the ~c[nth] top-level event in the book; and this will always be the case if the ~c[.acl2x] file is created by ~ilc[certify-book] after execution of the form ~c[(set-write-acl2x t state)]. However, you are welcome to associate indices manually with any ~il[events] you wish into the alist stored in the ~c[.acl2x] file. Note: Also see the community book ~c[make-event/acl2x-help.lisp] for a useful utility that can be used to skip proofs during the writing of ~c[.acl2x] files.~/" (declare (xargs :guard (state-p state))) (er-progn (cond ((member-eq val '(t nil)) (value nil)) ((and (consp val) (null (cdr val))) (chk-ld-skip-proofsp (car val) 'set-write-acl2x state)) (t (er soft 'set-write-acl2x "Illegal value for set-write-acl2x, ~x0. See :DOC ~ set-write-acl2x." val))) (pprogn (f-put-global 'write-acl2x val state) (value val)))) ; CHECK SUMS ; We begin by developing code to compute checksums for files, culminating in ; function check-sum. (Later we will consider checksums for objects.) ; We can choose any two nonnegative integers for the following two ; constants and still have a check-sum algorithm, provided, (a) that ; (< (* 127 *check-length-exclusive-maximum*) *check-sum-exclusive-maximum*) ; and provided (b) that (* 2 *check-sum-exclusive-maximum*) is of type ; (signed-byte 32). The first condition assures that the intermediate ; sum we obtain by adding to a running check-sum the product of a ; character code with the current location can be reduced modulo ; *check-sum-exclusive-maximum* by subtracting *check-sum-exclusive-maximum*. ; Choosing primes, as we do, may help avoid some loss of information ; due to cancellation. Choosing primes that are smaller may lead to ; check sums with less information. (defconst *check-sum-exclusive-maximum* 268435399 "268435399 is the first prime below 2^28. We use integers modulo this number as check sums.") (defconst *check-length-exclusive-maximum* 2097143 "2097143 is the first prime below 2^21. We use integers modulo this number as indices into the stream we are check summing.") ; We actually return check-sums which are in (mod ; *check-sum-exclusive-maximum*). (defconst *-check-sum-exclusive-maximum* (- *check-sum-exclusive-maximum*)) (defconst *1-check-length-exclusive-maximum* (1- *check-length-exclusive-maximum*)) (defun ascii-code! (x) (let ((y (char-code x))) (cond ((or (= y 0) (= y 128)) 1) ((< 127 y) (- y 128)) (t y)))) (defun check-sum1 (sum len channel state) (declare (type (signed-byte 32) sum len)) (let ((len (cond ((= len 0) *1-check-length-exclusive-maximum*) (t (the (signed-byte 32) (1- len)))))) (declare (type (signed-byte 32) len)) (mv-let (x state) (read-char$ channel state) (cond ((not (characterp x)) (mv sum state)) (t (let ((inc (ascii-code! x))) (declare (type (unsigned-byte 7) inc)) (cond ((and (= inc 0) (not (eql x #\Tab))) (mv x state)) (t (let ((inc (the (unsigned-byte 7) (cond ((= inc 0) 9) (t inc))))) (declare (type (unsigned-byte 7) inc)) (let ((sum (+ sum (the (signed-byte 32) (* inc len))))) (declare (type (signed-byte 32) sum)) (check-sum1 (cond ((>= sum *check-sum-exclusive-maximum*) (the (signed-byte 32) (+ sum *-check-sum-exclusive-maximum*))) (t sum)) len channel state))))))))))) (defun check-sum (channel state) ; This function returns a check-sum on the characters in a stream. ; This function also checks that every character read is either ; #\Newline, #\Tab, or #\Space, or a printing Ascii character. If the ; first value returned is a character, that character was not legal. ; Otherwise, the first value returned is an integer, the check-sum. ":Doc-Section ACL2::ACL2-built-ins assigning ``often unique'' integers to files and objects~/ A ``check sum'' is an integer in some fixed range computed from the printed representation of an object, e.g., the sum, modulo ~c[2**32], of the ascii codes of all the ~il[characters] in the printed representation.~/ Ideally, you would like the check sum of an object to be uniquely associated with that object, like a fingerprint. It could then be used as a convenient way to recognize the object in the future: you could remember the check sum (which is relatively small) and when an object is presented to you and alleged to be the special one you could compute its check sum and see if indeed it was. Alas, there are many more objects than check sums (after all, each check sum is an object, and then there's ~c[t]). So you try to design a check sum algorithm that maps similar looking objects far apart, in the hopes that corruptions and counterfeits ~-[] which appear to be similar to the object ~-[] have different check sums. Nevertheless, the best you can do is a many-to-one map. If an object with a different check sum is presented, you can be positive it is not the special object. But if an object with the same check sum is presented, you have no grounds for positive identification. The basic check sum algorithm in ACL2 is called ~c[check-sum-obj], which computes the check sum of an ACL2 object. Roughly speaking, we scan the print representation of the object and, for each character encountered, we multiply the ascii code of the character times its position in the stream (modulo a certain prime) and then add (modulo a certain prime) that into the running sum. This is inaccurate in many senses (for example, we don't always use the ascii code and we see numbers as though they were printed in base 127) but indicates the basic idea. ACL2 uses check sums to increase security in the ~il[books] mechanism; ~pl[certificate]." (check-sum1 0 *1-check-length-exclusive-maximum* channel state)) ; We now develop code for computing checksums of objects. There are two ; separate algorithms, culminating respectively in functions old-check-sum-obj ; and fchecksum-obj. The first development was used up through ACL2 ; Version_3.4, which uses an algorithm similar to that of our file-based ; function, check-sum. However, the #+hons version of ACL2 was being used on ; large cons trees with significant subtree sharing. These "galactic" trees ; could have relatively few distinct cons cells but a huge naive node count. ; It was thus desirable to memoize the computation of checksums, which was ; impossible using the existing algorithm because it modified state. ; The second development was contributed by Jared Davis (and is now maintained ; by the ACL2 developers, who are responsible for any errors). It is amenable ; to memoization and, indeed, fchecksum-obj is memoized in the #+hons version ; of ACL2. We say more after developing the code for the first algorithm, ; culminating in function check-sum-obj1. ; We turn now to the first development (which is no longer used in ACL2). (defun check-sum-inc (n state) (declare (type (unsigned-byte 7) n)) (let ((top (32-bit-integer-stack-length state))) (declare (type (signed-byte 32) top)) (let ((sum-loc (the (signed-byte 32) (+ top -1))) (len-loc (the (signed-byte 32) (+ top -2)))) (declare (type (signed-byte 32) sum-loc len-loc)) (let ((sum (aref-32-bit-integer-stack sum-loc state))) (declare (type (signed-byte 32) sum)) (let ((len (aref-32-bit-integer-stack len-loc state))) (declare (type (signed-byte 32) len)) (let ((len (cond ((= 0 len) *1-check-length-exclusive-maximum*) (t (the (signed-byte 32) (+ len -1)))))) (declare (type (signed-byte 32) len)) (let ((state (aset-32-bit-integer-stack len-loc len state))) (let ((new-sum (the (signed-byte 32) (+ sum (the (signed-byte 32) (* n len)))))) (declare (type (signed-byte 32) new-sum)) (let ((new-sum (cond ((>= new-sum *check-sum-exclusive-maximum*) (the (signed-byte 32) (+ new-sum *-check-sum-exclusive-maximum*))) (t new-sum)))) (declare (type (signed-byte 32) new-sum)) (aset-32-bit-integer-stack sum-loc new-sum state)))))))))) (defun check-sum-natural (n state) (declare (type unsigned-byte n)) (cond ((<= n 127) (check-sum-inc (the (unsigned-byte 7) n) state)) (t (pprogn (check-sum-inc (the (unsigned-byte 7) (rem n 127)) state) (check-sum-natural (truncate n 127) state))))) (defun check-sum-string1 (str i len state) (declare (type string str)) (declare (type (signed-byte 32) i len)) (cond ((= i len) state) (t (let ((chr (char str i))) (declare (type character chr)) (let ((code (ascii-code! chr))) (declare (type (unsigned-byte 7) code)) (cond ((> code 127) (f-put-global 'check-sum-weirdness (cons str i) state)) (t (pprogn (check-sum-inc code state) (check-sum-string1 str (the (signed-byte 32) (1+ i)) len state))))))))) (defun check-sum-string2 (str i len state) ; This function serves the same purpose as check-sum-string1 except ; that no assumption is made that i or len fit into 32 bits. It ; seems unlikely that this function will ever be called, since it ; seems unlikely that any Lisp will support strings of length 2 billion ; or more, but who knows. (declare (type string str)) (cond ((= i len) state) (t (let ((chr (char str i))) (let ((code (ascii-code! chr))) (cond ((> code 127) (f-put-global 'check-sum-weirdness (cons str i) state)) (t (pprogn (check-sum-inc code state) (check-sum-string2 str (1+ i) len state))))))))) (defun check-sum-string (str state) (let ((len (the integer (length (the string str))))) (cond ((32-bit-integerp len) (check-sum-string1 str 0 (the (signed-byte 32) len) state)) (t (check-sum-string2 str 0 len state))))) (defun check-sum-obj1 (obj state) (cond ((symbolp obj) (pprogn (check-sum-inc 1 state) (check-sum-string (symbol-name obj) state))) ((stringp obj) (pprogn (check-sum-inc 2 state) (check-sum-string obj state))) ((rationalp obj) (cond ((integerp obj) (cond ((< obj 0) (pprogn (check-sum-inc 3 state) (check-sum-natural (- obj) state))) (t (pprogn (check-sum-inc 4 state) (check-sum-natural obj state))))) (t (let ((n (numerator obj))) (pprogn (check-sum-inc 5 state) (check-sum-natural (if (< n 0) (1- (- n)) n) state) (check-sum-natural (denominator obj) state)))))) ((consp obj) (pprogn (check-sum-inc 6 state) (check-sum-obj1 (car obj) state) (cond ((atom (cdr obj)) (cond ((cdr obj) (pprogn (check-sum-inc 7 state) (check-sum-obj1 (cdr obj) state))) (t (check-sum-inc 8 state)))) (t (check-sum-obj1 (cdr obj) state))))) ((characterp obj) (pprogn (check-sum-inc 9 state) (let ((n (ascii-code! obj))) (cond ((< n 128) (check-sum-inc (ascii-code! obj) state)) (t (f-put-global 'check-sum-weirdness obj state)))))) ((complex-rationalp obj) (pprogn (check-sum-inc 14 state) (check-sum-obj1 (realpart obj) state) (check-sum-obj1 (imagpart obj) state))) (t (f-put-global 'check-sum-weirdness obj state)))) (defun old-check-sum-obj (obj state) ; This function became obsolete after Version_3.4 but we include it in case ; there are situations where it becomes useful again. It is the culmination of ; our first development of checksums for objects (as discussed above). ; We return a check-sum on obj, using an algorithm similar to that of ; check-sum. We return a non-integer as the first value if (and only if) the ; obj is not composed entirely of conses, symbols, strings, rationals, complex ; rationals, and characters. If the first value is not an integer, it is one of ; the offending objects encoutered. ; We typically use this function to compute check sums of cert-obj records and ; of objects of the form (cons expansion-alist ev-lst) where ev-lst is the list ; of forms in a book, including the initial in-package, and expansion-alist ; comes from make-event expansion. (pprogn (extend-32-bit-integer-stack 2 0 state) (let ((top (32-bit-integer-stack-length state))) (let ((sum-loc (+ top -1)) (len-loc (+ top -2))) (pprogn (aset-32-bit-integer-stack sum-loc 0 state) (aset-32-bit-integer-stack len-loc *1-check-length-exclusive-maximum* state) (f-put-global 'check-sum-weirdness nil state) (check-sum-obj1 obj state) (let ((ans (aref-32-bit-integer-stack sum-loc state))) (pprogn (shrink-32-bit-integer-stack 2 state) (let ((x (f-get-global 'check-sum-weirdness state))) (cond (x (pprogn (f-put-global 'check-sum-weirdness nil state) (mv x state))) (t (mv ans state))))))))))) ; We now develop code for the second checksum algorithm, contributed by Jared ; Davis (now maintained by the ACL2 developers, who are responsible for any ; errors). See also the long comment after check-sum-obj, below. ; Our initial attempts however were a problem for GCL, which boxes fixnums ; unless one is careful. A regression took about 44 or 45 minutes instead of ; 35 or 36 minutes, which is really significant considering that (probably) ; only the checksum code was changed, and one would expect checksums to take a ; trivial fraction of time during a regression. Therefore, we developed code ; to avoid boxing fixnums in GCL during a common operation: multiplication mod ; M31 = #x7fffffff. The code below is developed only for defining that ; operation, times-mod-m31; so we could conditionalize with #+gcl all ; definitions below up to times-mod-m31. We believe that the following is a ; theorem, but we have not proved it (nor even admitted the relevant functions ; into :logic mode): ; (implies (and (natp x) (< x #x7fffffff) ; (natp y) (< y #x7fffffff)) ; (equal (times-mod-m31 x y) ; (rem (* x y) #x7fffffff))) ; We considered using our fancy times-mod-m31 and its subfunctions for other ; than GCL. The time loss for ACL2h built on CCL 1.2 (actually ; 1.2-r10991M-trunk) on DarwinX8664 was only about 3.2%, which seems worth the ; cost in order to avoid having Lisp-specific code. However, regression runs ; with ACL2 built on Allegro CL exhibited intermittent checksumming errors. We ; wonder about a possible compiler bug, since neither heavy addition of checks, ; nor running with safety 3 (both ACL2h on CCL and ACL2 on Allegro CL) showed ; any inappropriate type declarations in the code below, and there were no ; checksumming problems exhibited with CCL, GCL, or SBCL. Moreover, Allegro CL ; showed significant slow down with the fancy times-mod-m31, not surprisingly ; since Allegro CL supports fixnums of less than 32 bits. Therefore, we ; decided to use a much simpler times-mod-m31 for all Lisps except GCL. (defun plus-mod-m31 (u v) ; Add u and v mod M31 = #x7fffffff. (declare (type (signed-byte 32) u v)) (the (signed-byte 32) (let ((u (min u v)) (v (max u v))) (declare (type (signed-byte 32) u v)) (cond ((< u #x40000000) ; 2^30 (cond ((< v #x40000000) ; 2^30 (the (signed-byte 32) (+ u v))) (t (let ((part (+ (the (signed-byte 32) (logand v #x3FFFFFFF)) ; v - 2^30 u))) (declare (type (signed-byte 32) part)) (cond ((< part #x3FFFFFFF) (the (signed-byte 32) (logior part #x40000000))) ((eql part #x3FFFFFFF) 0) (t ; part + 2^30 = part' + 2^31 (the (signed-byte 32) (1+ (the (signed-byte 32) (logxor part #x40000000)))))))))) (t (the (signed-byte 32) (- #x7FFFFFFF (the (signed-byte 32) (+ (the (signed-byte 32) (- #x7FFFFFFF u)) (the (signed-byte 32) (- #x7FFFFFFF v))))))))))) (defun double-mod-m31 (x) ; This is an optimization of (plus-mod-m31 x x). (declare (type (signed-byte 32) x)) (the (signed-byte 32) (cond ((< x #x40000000) ; 2^30 (the (signed-byte 32) (ash x 1))) (t (the (signed-byte 32) (- #x7FFFFFFF (the (signed-byte 32) (ash (the (signed-byte 32) (- #x7FFFFFFF x)) 1)))))))) (defun times-expt-2-16-mod-m31 (x) ; Given x < M31 = #x7fffffff, we compute 2^16*x mod M31. The idea is to view x ; as the concatenation of 15-bit chunk H (high) to 16-bit chunk L (low), so ; that reasoning mod M31, 2^16*x = 2^32*H + 2^16*L = 2*H + 2^16*L. Note that ; if L has its high (15th) bit set, then writing L# for the result of masking ; out that bit, we have [mod M31] 2^16*L = 2^16(2^15 + L#) = 2^31 + 2^16 * L#. ; = 1 + 2^16 * L#. ; We can test this function in CCL, in raw Lisp, as follows. (It may be too ; slow to do this in GCL since some intermediate results might not be fixnums.) ; It took us about 3.5 minutes (late 2008). ; (defun test () ; (loop for i from 0 to #x7ffffffe ; when (not (eql (times-expt-2-16-mod-m31 i) ; (mod (* #x10000 i) #x7fffffff))) ; do (return i))) ; (test) (declare (type (signed-byte 32) x)) (the (signed-byte 32) (let ((hi (ash x -16)) (lo (logand x #x0000ffff))) (declare (type (signed-byte 32) hi lo)) (cond ((eql 0 (the (signed-byte 32) (logand lo #x8000))) ; logbitp in GCL seems to box! (the (signed-byte 32) (plus-mod-m31 (double-mod-m31 hi) (the (signed-byte 32) (ash lo 16))))) (t (the (signed-byte 32) (plus-mod-m31 (double-mod-m31 hi) (the (signed-byte 32) (logior #x1 (the (signed-byte 32) (ash (the (signed-byte 32) (logand lo #x7fff)) 16))))))))))) #+(and (not gcl) (not acl2-loop-only)) (declaim (inline times-mod-m31)) (defun times-mod-m31 (u v) ; Note that u or v (or both) can be #x7fffffff, not just less than that number; ; this code will still give the correct result, 0. ; See the comment above about "using our fancy times-mod-m31" for GCL only. (declare (type (signed-byte 32) u v)) (the (signed-byte 32) #+(or (not gcl) acl2-loop-only) (rem (the (signed-byte 64) (* u v)) #x7fffffff) #+(and gcl (not acl2-loop-only)) ; We want to avoid boxing, where we have 32-bit fixnums u and v. We compute as ; follows: ; u * v ; = (2^16 u-hi + u-lo) * (2^16 v-hi + v-lo) ; = 2^32 u-hi v-hi + 2^16 u-hi v-lo + 2^16 u-lo v-hi + u-lo v-lo ; = [mod M31 = #x7fffffff] ; 2 u-hi v-hi + 2^16(u-hi*v-lo + u-lo*v-hi) + u-lo*v-lo ; Now u-hi and v-hi are less than 2^15, while u-lo and v-lo are less than ; 2^16. So we need to be careful with the term u-lo*v-lo. (let ((u-hi (ash u -16)) (u-lo (logand u #x0000ffff)) (v-hi (ash v -16)) (v-lo (logand v #x0000ffff))) (declare (type (signed-byte 32) u-hi u-lo v-hi v-lo)) (let ((term1 (double-mod-m31 (the (signed-byte 32) (* u-hi v-hi)))) (term2 (times-expt-2-16-mod-m31 (plus-mod-m31 (the (signed-byte 32) (* u-hi v-lo)) (the (signed-byte 32) (* u-lo v-hi))))) (term3 (cond ((or (eql (the (signed-byte 32) (logand u-lo #x8000)) 0) (eql (the (signed-byte 32) (logand v-lo #x8000)) 0)) (the (signed-byte 32) (* u-lo v-lo))) (t ; Let H = 2^15, and let u0 and v0 be the results of masking out the high bits ; of u-lo and v-lo, respectively. So: ; u-lo * v-lo ; = (H + u0) * (H + v0) ; = H^2 + H*(u0 + v0) + u0*v0 (let ((u0 (logand u #x7fff)) (v0 (logand v #x7fff))) (declare (type (signed-byte 32) u0 v0)) (plus-mod-m31 #x40000000 ; 2^30 (plus-mod-m31 (the (signed-byte 32) (* #x8000 ; 2^15 (the (signed-byte 32) (+ u0 v0)))) (the (signed-byte 32) (* u0 v0))))))))) (declare (type (signed-byte 32) term1 term2 term3)) (plus-mod-m31 term1 (plus-mod-m31 term2 term3)))))) ; Now we can include (our latest version of) Jared's code. (defun fchecksum-natural-aux (n ans) ; A "functional" checksum for natural numbers. ; ; N is the natural number we want to checksum. ; ANS is the answer we have accumulated so far. ; ; Let M31 be 2^31 - 1. This happens to be the largest representable 32-bit ; signed number using 2's complement arithmetic. It is also a Mersenne prime. ; Furthermore, let P1 be 392894102, which is a nice, large primitive root of ; M31. From number theory, we can construct a basic pseudorandom number ; generator as follows: ; ; rnd0 = seed ; rnd1 = (rnd0 * P1) mod M31 ; rnd2 = (rnd1 * P1) mod M31 ; ... ; ; And our numbers will not repeat until 2^31 - 1. In fact, such a generator ; is found in the community book "misc/random." ; ; Our checksum algorithm uses this idea in a slightly different way. Given a ; 31-bit natural number, K, think of (K * P1) mod M31 as a way to "shuffle" the ; bits of K around in a fairly random manner. Then, to checksum a (potentially ; large) integer n, we break n up into 31-bit chunks, call them K1, K2, ..., ; Km. We then compute (Ki * P1) mod M31 for each i, and xor the results all ; together to compute a new, 31-bit checksum. ; A couple of other notes. ; ; - M31 may be written as #x7FFFFFFF. ; ; - We recur using (ash n -31), but this computes the same thing as (truncate ; n (expt 2 31)). ; ; - We split n into Ki by using (logand n #x7FFFFFFF), which is the same as ; (rem n (expt 2 31)). (declare (type (integer 0 *) n)) (declare (type (signed-byte 32) ans)) (the (signed-byte 32) (if (eql n 0) ans (fchecksum-natural-aux (the (integer 0 *) (ash n -31)) (the (signed-byte 32) (logxor ans (the (signed-byte 32) (times-mod-m31 (logand n #x7FFFFFFF) 392894102)))))))) (defun fchecksum-natural (n) (declare (type (integer 0 *) n)) (the (signed-byte 32) (fchecksum-natural-aux n 28371987))) (defun fchecksum-string1 (str i len ans) ; A "functional" checksum for strings. ; ; This is similar to the case for natural numbers. ; ; We consider the string in 31-bit pieces; each character in the string has, ; associated with it, an 8-bit character code, so we can combine four of these ; codes together to create a 32 bit chunk. We then simply drop the highest ; resulting bit (which should typically not matter because the character codes ; above 127 are so rarely used). The remaining 31-bits are be treated just as ; the 31-bit chunks of integers are, but the only twist is that we will use a ; different primitive root so that we come up with different numbers. In ; particular, we will use 506249751. ; WARNING: Keep this in sync with fchecksum-string2. (declare (type string str)) (declare (type (signed-byte 32) i len ans)) (the (signed-byte 32) (if (>= i len) ans (let* ((c0 (logand #x7F (the (signed-byte 32) (char-code (the character (char str i)))))) (i (+ i 1)) (c1 (if (>= i len) 0 (char-code (the character (char str i))))) (i (+ i 1)) (c2 (if (>= i len) 0 (char-code (the character (char str i))))) (i (+ i 1)) (c3 (if (>= i len) 0 (char-code (the character (char str i))))) (bits ; GCL 2.6.7 does needless boxing when we call logior on the four arguments, ; even when each of them is of the form (the (signed-byte 32) xxx). So the ; code is a bit ugly below. (logior (the (signed-byte 32) (ash c0 24)) (the (signed-byte 32) (logior (the (signed-byte 32) (ash c1 16)) (the (signed-byte 32) (logior (the (signed-byte 32) (ash c2 8)) (the (signed-byte 32) c3)))))))) (declare (type (signed-byte 32) c0 i c1 c2 c3 bits)) (fchecksum-string1 str i len (the (signed-byte 32) (logxor ans (the (signed-byte 32) (times-mod-m31 bits 506249751))))))))) (defun fchecksum-string2 (str i len ans) ; Same as above, but we don't assume i, len are (signed-byte 32)'s. ; WARNING: Keep this in sync with fchecksum-string1. (declare (type string str)) (declare (type (signed-byte 32) ans)) (declare (type (integer 0 *) i len)) (the (signed-byte 32) (if (>= i len) ans (let* ((c0 (logand #x7F (the (signed-byte 32) (char-code (the character (char str i)))))) (i (+ i 1)) (c1 (if (>= i len) 0 (char-code (the character (char str i))))) (i (+ i 1)) (c2 (if (>= i len) 0 (char-code (the character (char str i))))) (i (+ i 1)) (c3 (if (>= i len) 0 (char-code (the character (char str i))))) (bits ; see comment in fchecksum-string1 about ugly code below (logior (the (signed-byte 32) (ash c0 24)) (the (signed-byte 32) (logior (the (signed-byte 32) (ash c1 16)) (the (signed-byte 32) (logior (the (signed-byte 32) (ash c2 8)) (the (signed-byte 32) c3)))))))) (declare (type (signed-byte 32) c0 c1 c2 c3 bits) (type (integer 0 *) i)) (fchecksum-string2 str i len (the (signed-byte 32) (logxor ans (the (signed-byte 32) (times-mod-m31 bits 506249751))))))))) (defun fchecksum-string (str) (declare (type string str)) (the (signed-byte 32) (let ((length (length str))) (declare (type (integer 0 *) length)) (cond ((< length 2147483647) ; so (+ 1 length) is (signed-byte 32) (fchecksum-string1 str 0 length ; We scramble the length in order to get a seed. This number is just another ; primitive root. (times-mod-m31 (the (signed-byte 32) (+ 1 length)) 718273893))) (t (fchecksum-string2 str 0 length ; As above, but WARNING: Do not use times-mod-m31 here, because length need not ; be a fixnum. (rem (the integer (* (+ 1 length) 718273893)) #x7FFFFFFF))))))) #-(or acl2-loop-only hons) (defvar *fchecksum-symbol-memo* nil) (defun fchecksum-atom (x) ; X is any atom. We compute a "functional checksum" of X. ; ; This is pretty straightforward. For naturals and strings, we just call the ; functions we've developed above. Otherwise, the object is composed out of ; naturals and strings. We compute the component-checksums, then "scramble" ; them by multiplying with another primitive root. Since it is easy to find ; primitive roots, it is easy to scramble in many different ways based on the ; different types we are looking at. (the (signed-byte 32) (cond ((natp x) (fchecksum-natural x)) ((integerp x) ; It's not a natural, so it's negative. We compute the code for the absolute ; value, then scramble it with yet another primitive root. (let ((abs-code (fchecksum-natural (- x)))) (declare (type (signed-byte 32) abs-code)) (times-mod-m31 abs-code 283748912))) ((symbolp x) (cond #-(or hons acl2-loop-only) ((and *fchecksum-symbol-memo* (gethash x *fchecksum-symbol-memo*))) (t (let* ((pkg-code (fchecksum-string (symbol-package-name x))) (sym-code (fchecksum-string (symbol-name x))) (pkg-code-scramble ; We scramble the bits of pkg-code so that it matters that they are in order. ; To do this, we multiply by another primitive root and mod out by M31. (times-mod-m31 pkg-code 938187814))) (declare (type (signed-byte 32) pkg-code sym-code pkg-code-scramble)) (cond #-(or hons acl2-loop-only) (*fchecksum-symbol-memo* (setf (gethash x *fchecksum-symbol-memo*) (logxor pkg-code-scramble sym-code))) (t (logxor pkg-code-scramble sym-code))))))) ((stringp x) (fchecksum-string x)) ((characterp x) ; just scramble using another primitive root (times-mod-m31 (char-code x) 619823821)) ((rationalp x) (let* ((num-code (fchecksum-atom (numerator x))) (den-code (fchecksum-natural (denominator x))) (num-scramble (times-mod-m31 num-code 111298397)) (den-scramble (times-mod-m31 den-code 391892127))) (declare (type (signed-byte 32) num-code den-code num-scramble den-scramble)) (logxor num-scramble den-scramble))) ((complex-rationalp x) (let* ((imag-code (fchecksum-atom (imagpart x))) (real-code (fchecksum-atom (realpart x))) (imag-scramble (times-mod-m31 imag-code 18783723)) (real-scramble (times-mod-m31 real-code 981827319))) (declare (type (signed-byte 32) imag-code real-code imag-scramble real-scramble)) (logxor imag-scramble real-scramble))) (t (prog2$ (er hard 'fchecksum-atom "Bad atom, ~x0" x) 0))))) (defun fchecksum-obj (x) ; Finally, we just use the same idea to scramble cars and cdrs on conses. To ; make this efficient on structure-shared objects, it ought to be memoized. We ; do this explicitly in memoize-raw.lisp (for ACL2h). ; Warning: With #+hons, there could be performance problems if this is put into ; :logic mode without verifying guards. That is because fchecksum-obj is ; memoized by running acl2h-init, and for memoization, we expect the raw Lisp ; function to be executed, but :ideal mode functions are run without ever ; slipping into raw Lisp. ; Note that we could make this partially tail-recursive by accumulating from ; the cdr, but this would ruin memoization. If we find performance problems ; with non-hons versions, we could consider having two versions of ; fchecksum-obj, and passing state into check-sum-obj to decide which one to ; call depending on whether or not fchecksum-obj is memoized. (declare (xargs :guard t)) (the (signed-byte 32) (if (atom x) (fchecksum-atom x) (let* ((car-code (fchecksum-obj (car x))) (cdr-code (fchecksum-obj (cdr x))) (car-scramble (times-mod-m31 car-code 627718124)) (cdr-scramble (times-mod-m31 cdr-code 278917287))) (declare (type (signed-byte 32) car-code cdr-code car-scramble cdr-scramble)) (logxor car-scramble cdr-scramble))))) #-acl2-loop-only (declaim (notinline check-sum-obj)) ; see comment below for old code (defun check-sum-obj (obj) (declare (xargs :guard t)) (fchecksum-obj obj)) ; ; To use old check-sum-obj code, but then add check-sum-obj to ; ; *PRIMITIVE-PROGRAM-FNS-WITH-RAW-CODE* if doing this for a build: ; (defun check-sum-obj (obj) ; #-acl2-loop-only ; (return-from check-sum-obj ; (mv-let (val state) ; (old-check-sum-obj obj *the-live-state*) ; (declare (ignore state)) ; val)) ; #+acl2-loop-only ; (declare (ignore obj)) ; (er hard 'check-sum-obj "ran *1* code for check-sum-obj")) ; Here are some examples. ; ; (fchecksum-obj 0) ; (fchecksum-obj 19) ; (fchecksum-obj 1892) ; (fchecksum-obj "foo") ; (fchecksum-obj "bfdkja") ; (fchecksum-obj #\a) ; (fchecksum-obj "a") ; (fchecksum-obj #\b) ; (fchecksum-obj #\c) ; (fchecksum-obj 189) ; (fchecksum-obj -189) ; (fchecksum-obj -19189) ; (fchecksum-obj -19283/188901) ; (fchecksum-obj 19283/188901) ; (fchecksum-obj 19283/2) ; (fchecksum-obj 2/19283) ; (fchecksum-obj 19283) ; (fchecksum-obj #c(19283 198)) ; (fchecksum-obj #c(198 19283)) ; (fchecksum-obj #c(-19283/1238 198)) ; ; (fchecksum-obj 3) ; (fchecksum-obj '(3 . nil)) ; (fchecksum-obj '(nil . 3)) ; ; (fchecksum-obj nil) ; (fchecksum-obj '(nil)) ; (fchecksum-obj '(nil nil)) ; (fchecksum-obj '(nil nil nil)) ; (fchecksum-obj '(nil nil nil nil)) ; ; ; And here are some additional comments. If you want to generate more ; ; primitive roots, or verify that the ones we have picked are primitive roots, ; ; try this: ; ; (include-book "arithmetic-3/floor-mod/mod-expt-fast" :dir :system) ; (include-book "make-event/assert" :dir :system) ; ; ; Here we establish that the factors of M31-1 are 2, 3, 7, 11, 31, 151, and ; ; 331. ; ; (assert! (equal (- #x7FFFFFFF 1) ; (* 2 3 3 7 11 31 151 331))) ; ; ;; And so the following is sufficient to establish that n is a primitive ; ;; root. ; ; (defund primitive-root-p (n) ; (let* ((m31 #x7FFFFFFF) ; (m31-1 (- m31 1))) ; (and (not (equal (mod-expt-fast n (/ m31-1 2) m31) 1)) ; (not (equal (mod-expt-fast n (/ m31-1 3) m31) 1)) ; (not (equal (mod-expt-fast n (/ m31-1 7) m31) 1)) ; (not (equal (mod-expt-fast n (/ m31-1 11) m31) 1)) ; (not (equal (mod-expt-fast n (/ m31-1 31) m31) 1)) ; (not (equal (mod-expt-fast n (/ m31-1 151) m31) 1)) ; (not (equal (mod-expt-fast n (/ m31-1 331) m31) 1))))) ; ; ; And here are some primitive roots that we found. There are lots of ; ; them. If you want a new one, just pick a number and start incrementing ; ; or decrementing until it says T. ; ; (primitive-root-p 506249751) ; (primitive-root-p 392894102) ; (primitive-root-p 938187814) ; (primitive-root-p 718273893) ; (primitive-root-p 619823821) ; (primitive-root-p 283748912) ; (primitive-root-p 111298397) ; (primitive-root-p 391892127) ; (primitive-root-p 18783723) ; (primitive-root-p 981827319) ; ; (primitive-root-p 627718124) ; (primitive-root-p 278917287) ; ; ; At one point I [Jared] used this function to analyze different ; ; implementations of fchecksum-natural. You might find it useful if you want ; ; to write an alternate implementation. You want to produce a fast routine ; ; that doesn't have many collisions. ; ; (defun analyze-fchecksum-natural (n) ; (let (table ones twos more) ; ;; Table is a mapping from sums to the number of times they are hit. ; (setq table (make-hash-table)) ; (loop for i from 1 to n do ; (let ((sum (fchecksum-natural i))) ; (setf (gethash sum table) ; (+ 1 (nfix (gethash sum table)))))) ; ;; Now we will walk the table and see how many sums are hit once, ; ;; twice, or more often than that. ; (setq ones 0) ; (setq twos 0) ; (setq more 0) ; (maphash (lambda (key val) ; (declare (ignore key)) ; (cond ((= val 1) (incf ones val)) ; ((= val 2) (incf twos val)) ; (t (incf more val)))) ; table) ; (format t "~a~%" (list ones twos more)) ; (format t "Unique mappings: ~5,2F%~%" ; (* 100 (/ (coerce ones 'float) n))) ; (format t "2-ary collisions: ~5,2F%~%" ; (* 100 (/ (coerce twos 'float) n))) ; (format t "3+-ary collisions: ~5,2F%~%" ; (* 100 (/ (coerce more 'float) n))))) ; ; (analyze-fchecksum-natural 1000) ; (analyze-fchecksum-natural 10000) ; (analyze-fchecksum-natural 100000) ; (analyze-fchecksum-natural 1000000) ; (analyze-fchecksum-natural 10000000) ; End of checksum code. (defun read-file-iterate (channel acc state) (mv-let (eof obj state) (read-object channel state) (cond (eof (mv (reverse acc) state)) (t (read-file-iterate channel (cons obj acc) state))))) (defun read-file (name state) (mv-let (channel state) (open-input-channel name :object state) (cond (channel (mv-let (ans state) (read-file-iterate channel nil state) (pprogn (close-input-channel channel state) (mv nil ans state)))) (t (er soft 'read-file "No file found ~x0." name))))) (defun formals (fn w) (declare (xargs :guard (and (symbolp fn) (plist-worldp w)))) (cond ((flambdap fn) (lambda-formals fn)) (t (let ((temp (getprop fn 'formals t 'current-acl2-world w))) (cond ((eq temp t) (er hard? 'formals "Every function symbol is supposed to have a ~ 'FORMALS property but ~x0 does not!" fn)) (t temp)))))) (defun arity (fn w) (cond ((flambdap fn) (length (lambda-formals fn))) (t (let ((temp (getprop fn 'formals t 'current-acl2-world w))) (cond ((eq temp t) nil) (t (length temp))))))) (defun stobjs-out (fn w) ; Warning: keep this in sync with get-stobjs-out-for-declare-form. ; See the Essay on STOBJS-IN and STOBJS-OUT. (cond ((eq fn 'cons) ; We call this function on cons so often we optimize it. '(nil)) ((member-eq fn '(if return-last)) (er hard! 'stobjs-out "Implementation error: Attempted to find stobjs-out for ~x0." fn)) (t (getprop fn 'stobjs-out '(nil) 'current-acl2-world w)))) ; With stobjs-out defined, we can define user-defined-functions-table. (deflabel user-defined-functions-table :doc ":Doc-Section switches-parameters-and-modes an advanced ~il[table] used to replace certain system functions~/ ~bv[] Examples: (table user-defined-functions-table 'untranslate-preprocess 'my-preprocess) (table user-defined-functions-table 'untranslate 'my-untranslate) ~ev[] This feature should perhaps only be used by advanced users who have a thorough understanding of the system functions being replaced. There are currently two ways a user can affect the way ACL2 prints terms. The first example associates the user-defined function symbol ~c[my-preprocess] with ~c[untranslate-preprocess]. As a result, when ACL2 prints a term, say during a proof, using its so-called ``untranslate'' process the first thing it does is to call ~c[my-preprocess] on two arguments: that term and the current ACL2 logical ~il[world]. If the call produces a non-~c[nil] result, then that result is passed to the untranslate process. The second example associates the user-defined function symbol ~c[my-untranslate] with the built-in function symbol ~c[untranslate]. As a result, the code for ~c[my-untranslate] will be run whenever the untranslate process is run. The formals of the two functions must agree and must not contain any ~il[stobj] names. Note that these overrides fail to occur upon guard violations and some other evaluation errors.~/ The ~c[untranslate-preprocess] approach may suffice for most cases in which a user wants to modify the way output is produced by the theorem prover. We present an example immediately below, but see community book ~c[books/misc/untranslate-patterns.lisp] for a more elaborate example. If the ~c[untranslate-preprocess] approach does not seem sufficient for your purposes, you are invited to look at community book ~c[books/misc/rtl-untranslate.lisp] for an example of user-defined ~c[untranslate] (i.e., following the second example displayed above). Suppose you have a large constant that you would prefer not to see in proofs. For example, you may have submitted the following definition (but imagine a much larger constant, say, a list of length 1,000,000). ~bv[] (defconst *a* '(a b c d)) ~ev[] If you submit the following (silly) theorem ~bv[] (thm (equal (cons x *a*) (car (cons yyy zzz)))) ~ev[] then you will see the following output: ~bv[] (EQUAL (CONS X '(A B C D)) YYY). ~ev[] If ~c[*a*] had represented a much larger structure, we would wish we could see the following instead. ~bv[] (EQUAL (CONS X *A*) YYY) ~ev[] That can be accomplished as follows. First we make the following definition. ~bv[] (defun my-preprocess (term wrld) (declare (ignore wrld)) (if (equal term (list 'quote *a*)) '*a* nil)) ~ev[] Now we submit the following ~ilc[table] event. ~bv[] (table user-defined-functions-table 'untranslate-preprocess 'my-preprocess) ~ev[] This will install ~c[my-preprocess] as a preprocessor before the normal untranslation routine is applied to printing a term. When the untranslation routine encounters the constant ~c[(QUOTE (A B C D))], it will replace it with ~c[*a*], and the usual untranlation routine will print this as ~c[*A*].~/") (defconst *user-defined-functions-table-keys* ; Although it would be very odd to add return-last to this list, we state here ; explicitly that it is illegal to do so, because user-defined-functions-table ; has a :guard that relies on this in order to avoid applying stobjs-out to ; return-last. '(untranslate untranslate-lst untranslate-preprocess)) (table user-defined-functions-table nil nil :guard (and (member-eq key *user-defined-functions-table-keys*) (symbolp val) (not (eq (getprop val 'formals t 'current-acl2-world world) t)) (all-nils (stobjs-out val world)))) (defrec def-body ; Use the 'recursivep property, not this :recursivep field, when referring to ; the original definition, as is necessary for verify-guards, ; verify-termination, and handling of *1* functions. ((nume hyp ; nil if there are no hypotheses . concl) . (recursivep formals rune . controller-alist)) t) (defun latest-body (fncall hyp concl) (if hyp (fcons-term* 'if hyp concl (fcons-term* 'hide fncall)) concl)) (defun def-body (fn wrld) (car (getprop fn 'def-bodies nil 'current-acl2-world wrld))) (defun body (fn normalp w) ; The safe way to call this function is with normalp = nil, which yields the ; actual original body of fn. The normalized body is provably equal to the ; unnormalized body, but that is not a strong enough property in some cases. ; Consider for example the following definition: (defun foo () (car 3)). Then ; (body 'foo nil (w state)) is (CAR '3), so guard verification for foo will ; fail, as it should. But (body 'foo t (w state)) is 'NIL, so we had better ; scan the unnormalized body when generating the guard conjecture rather than ; the normalized body. Functional instantiation may also be problematic if ; constraints are gathered using the normalized body, although we do not yet ; have an example showing that this is critical. ; WARNING: If normalp is non-nil, then we are getting the most recent body ; installed by a :definition rule with non-nil :install-body value. Be careful ; that this is really what is desired; and if so, be aware that we are not ; returning the corresponding def-body rune. (cond ((flambdap fn) (lambda-body fn)) (normalp (let ((def-body (def-body fn w))) (latest-body (fcons-term fn (access def-body def-body :formals)) (access def-body def-body :hyp) (access def-body def-body :concl)))) (t (getprop fn 'unnormalized-body nil 'current-acl2-world w)))) (defun symbol-class (sym wrld) ; The symbol-class of a symbol is one of three keywords: ; :program - not defined within the logic ; :ideal - defined in the logic but not known to be CL compliant ; :common-lisp-compliant - defined in the logic and known to be compliant with ; Common Lisp ; Convention: We never print the symbol-classes to the user. We would prefer ; the user not to think about these classes per se. It encourages a certain ; confusion, we think, because users want everything to be ; common-lisp-compliant and start thinking of it as a mode, sort of like "super ; :logic" or something. So we are keeping these names to ourselves by not ; using them in error messages and documentation. Typically used English ; phrases are such and such is "compliant with Common Lisp" or "is not known to ; be compliant with Common Lisp." ; Historical Note: :Program function symbols were once called "red", :ideal ; symbols were once called "blue", and :common-lisp-compliant symbols were once ; called "gold." ; Before we describe the storage scheme, let us make a few observations. ; First, most function symbols have the :program symbol-class, because until ; ACL2 is admitted into the logic, the overwhelming majority of the function ; symbols will be system functions. Second, all :logic function symbols have ; symbol-class :ideal or :common-lisp-compliant. Third, this function, ; symbol-class, is most often applied to :logic function symbols, because most ; often we use it to sweep through the function symbols in a term before ; verify-guards. Finally, theorem names are very rarely of interest here but ; they are always either :ideal or (very rarely) :common-lisp-compliant. ; Therefore, our storage scheme is that every :logic function will have a ; symbol-class property that is either :ideal or :common-lisp-compliant. We ; will not store a symbol-class property for :program but just rely on the ; absence of the property (and the fact that the symbol is recognized as a ; function symbol) to default its symbol-class to :program. Thus, system ; functions take no space but are slow to answer. Finally, theorems will ; generally have no stored symbol-class (so it will default to :ideal for them) ; but when it is stored it will be :common-lisp-compliant. ; Note that the defun-mode of a symbol is actually determined by looking at its ; symbol-class. We only store the symbol-class. That is more often the ; property we need to look at. But we believe it is simpler for the user to ; think in terms of :mode and :verify-guards. (declare (xargs :guard (and (symbolp sym) (plist-worldp wrld)))) (or (getprop sym 'symbol-class nil 'current-acl2-world wrld) (if (getprop sym 'theorem nil 'current-acl2-world wrld) :ideal :program))) (defmacro fdefun-mode (fn wrld) ; Fn must be a symbol and a function-symbol of wrld. `(if (eq (symbol-class ,fn ,wrld) :program) :program :logic)) (defmacro programp (fn wrld) `(eq (symbol-class ,fn ,wrld) :program)) (defmacro logicalp (fn wrld) `(not (eq (symbol-class ,fn ,wrld) :program))) (mutual-recursion (defun program-termp (term wrld) (cond ((variablep term) nil) ((fquotep term) nil) ((flambdap (ffn-symb term)) (or (program-termp (lambda-body (ffn-symb term)) wrld) (program-term-listp (fargs term) wrld))) ((programp (ffn-symb term) wrld) t) (t (program-term-listp (fargs term) wrld)))) (defun program-term-listp (lst wrld) (cond ((null lst) nil) (t (or (program-termp (car lst) wrld) (program-term-listp (cdr lst) wrld))))) ) (defdoc common-lisp ":Doc-Section Miscellaneous relation to Common Lisp, including deviations from the spec~/ ACL2 is a logic, a theorem prover, and a programming language based on Common Lisp. A connection with Common Lisp is established with guards (~pl[guard]). However, here we document potential deviations from Common Lisp semantics even in the presence of verified guards. Our view is that these deviations are extremely unlikely to manifest; indeed, as of this writing we are unaware of any cases in which these issues arise in practice. However, we feel obligated to acknowledge their possibility, which could result in surprises during evaluation or even proof.~/ The Common Lisp spec allows certain predicates to return what it calls ``generalized Booleans,'' which are really arbitrary values that are to be viewed as either ~c[nil] or non-~c[nil]. However, in ACL2 these functions are assumed to return ~c[nil] or ~c[t]. For details,~pl[generalized-booleans]. The execution of forms with ~c[:]~ilc[program] mode functions can result in calls of functions on arguments that do not satisfy their ~il[guard]s. In practice, this simply causes hard Lisp errors. But in principle one could imagine a damaged Lisp image that operates incorrectly. ~l[defun-mode-caveat]. The Common Lisp spec, specifically Section 3.2.2.3 of the Common Lisp HyperSpec, allows for undefined results when a function is ``multiply defined'' in a compiled file. ACL2 allows redundant ~ilc[defun]s in a book, and in general ~il[books] are compiled by ~c[certify-book] (but ~pl[certify-book] and ~pl[compilation] for how to control such compilation). Moreover, ACL2 provides a redefinition capability (~pl[ld-redefinition-action] and ~pl[redef]), and the above section also allows for undefined results when a function is defined in a compiled file and then redefined, presumably (for example) because of inlining.") (deflabel defun-mode-caveat :doc ":Doc-Section Common-lisp potential soundness issue for functions with ~il[defun-mode] ~c[:]~ilc[program]~/ Technically speaking, in the current implementation, the execution of functions having ~il[defun-mode] ~c[:]~ilc[program] may damage the ACL2 system in a way that renders it unsound. In practice, we have never seen this happen; so, the explanations below can be viewed as extremely paranoid. Nevertheless, here we document this concern, even if it should be taken with more than a grain of salt. ~l[defun-mode] for a discussion of ~il[defun-mode]s. That discussion describes an imagined implementation that is slightly different from this one. This note explains that the current implementation is open to unsoundness. For discussion of a different soundness issue that is also related to function execution, ~pl[generalized-booleans].~/ The execution of a function having ~il[defun-mode] ~c[:]~ilc[program] may violate Common Lisp ~il[guard]s on the subroutines used. (This may be true even for calls of a function on arguments that satisfy its ~il[guard], because ACL2 has not verified that its ~il[guard] is sufficient to protect its subroutines.) When a ~il[guard] is violated at runtime all bets are off. That is, no guarantees are made either about the answer being ``right'' or about the continued rationality of the ACL2 system itself. For example, suppose you make the following ~ilc[defun]: ~bv[] (defun crash (i) (declare (xargs :mode :program :guard (integerp i))) (car i)) ~ev[] Note that the declared guard does not in fact adequately protect the subroutines in the body of ~c[crash]; indeed, satisfying the guard to ~c[crash] will guarantee that the ~ilc[car] expression is in violation of its guard. Because this function is admitted in ~c[:]~ilc[program]-mode, no checks are made concerning the suitability of the guard. Furthermore, in the current ACL2 implementation, ~c[crash] is executed directly in Common Lisp. Thus if you call ~c[crash] on an argument satisfying its guard you will cause an erroneous computation to take place. ~bv[] ACL2 !>(crash 7) Error: Caught fatal error [memory may be damaged] ... ~ev[] There is no telling how much damage is done by this errant computation. In some lisps your ACL2 job may actually crash back to the operating system. In other lisps you may be able to recover from the ``hard error'' and resume ACL2 in a damaged but apparently functional image. THUS, HAVING A FUNCTION WITH ~IL[DEFUN-MODE] ~c[:]~ilc[PROGRAM] IN YOUR SYSTEM ABSOLVES US, THE ACL2 IMPLEMENTORS, FROM RESPONSIBILITY FOR THE SOUNDNESS OF OUR SYSTEM. Furthermore ACL2 DOES NOT YET PROVIDE ANY MEANS OF REGAINING ASSURANCES OF SOUNDNESS AFTER THE INTRODUCTION OF A FUNCTION IN ~c[:]~ilc[PROGRAM] MODE, EVEN IF IT IS ULTIMATELY CONVERTED TO ~c[:]~ilc[LOGIC] MODE (since its execution could have damaged the system in a way that makes it possible to verify its termination and ~il[guard]s unsoundly). Finally, THE VAST MAJORITY OF ACL2 SYSTEM CODE IS IN ~c[:]~ilc[PROGRAM] MODE AND SO ALL BETS ARE OFF FROM BEFORE YOU START! This hopeless state of current affairs will change, we think. We think we have defined our functions ``correctly'' in the sense that they can be converted, without ``essential'' modification, to ~c[:]~ilc[logic] mode. We think it very unlikely that a mis-guarded function in ~c[:]~ilc[program] mode (whether ours or yours) will cause unsoundness without some sort of hard lisp error accompanying it. We think that ultimately we can make it possible to execute your functions (interpretively) without risk to the system, even when some have ~c[:]~ilc[program] mode. In that imagined implementation, code using functions having ~c[:]~ilc[program] mode would run more slowly, but safely. These functions could be introduced into the logic ex post facto, whereupon the code's execution would speed up because Common Lisp would be allowed to execute it directly. We therefore ask that you simply pretend that this is that imagined implementation, introduce functions in ~c[:]~ilc[program] mode, use them as convenient and perhaps ultimately introduce some of them in ~c[:]~ilc[logic] mode and prove their properties. If you use the system this way we can develop (or dismiss) this style of formal system development. BUT BE ON THE LOOKOUT FOR SCREWUPS DUE TO DAMAGE CAUSED BY THE EXECUTION OF YOUR FUNCTIONS HAVING ~c[:]~ilc[PROGRAM] MODE!") (deflabel generalized-booleans :doc ":Doc-Section Common-lisp potential soundness issues related to ACL2 predicates~/ The discussion below outlines a potential source of unsoundness in ACL2. Although to our knowledge there is no existing Lisp implementation in which this problem can arise, nevertheless we feel compelled to explain this situation. Consider for example the question: What is the value of ~c[(equal 3 3)]? According to the ACL2 axioms, it's ~c[t]. And as far as we know, based on considerable testing, the value is ~c[t] in every Common Lisp implementation. However, according the Common Lisp draft proposed ANSI standard, any non-~c[nil] value could result. Thus for example, ~c[(equal 3 3)] is allowed by the standard to be ~c[17].~/ The Common Lisp standard specifies (or soon will) that a number of Common Lisp functions that one might expect to return Boolean values may, in fact, return arbitrary values. Examples of such functions are ~ilc[equal], ~ilc[rationalp], and ~ilc[<]; a much more complete list is given below. Indeed, we dare to say that every Common Lisp function that one may believe returns only Booleans is, nevertheless, not specified by the standard to have that property, with the exceptions of the functions ~c[not] and ~c[null]. The standard refers to such arbitrary values as ``generalized Booleans,'' but in fact this terminology makes no restriction on those values. Rather, it merely specifies that they are to be viewed, in an informal sense, as being either ~c[nil] or not ~c[nil]. This situation is problematic for ACL2, which axiomatizes these functions to return Booleans. The problem is that because (for efficiency and simplicity) ACL2 makes very direct use of compiled Common Lisp functions to support the execution of its functions, there is in principle a potential for unsoundness due to these ``generalized Booleans.'' For example, ACL2's ~ilc[equal] function is defined to be Common Lisp's ~c[equal]. Hence if in Common Lisp the form ~c[(equal 3 3)] were to evaluate to 17, then in ACL2 we could prove (using the ~c[:]~ilc[executable-counterpart] of ~ilc[equal]) ~c[(equal (equal 3 3) 17)]. However, ACL2 can also prove ~c[(equal (equal x x) t)], and these two terms together are contradictory, since they imply (instantiating ~c[x] in the second term by ~c[3]) that ~c[(equal 3 3)] is both equal to ~c[17] and to ~c[t]. To make matters worse, the standard allows ~c[(equal 3 3)] to evaluate to ~em[different] non-~c[nil] values every time. That is: ~c[equal] need not even be a function! Fortunately, no existing Lisp implementation takes advantage of the flexibility to have (most of) its predicates return generalized Booleans, as far as we know. We may implement appropriate safeguards in future releases of ACL2, in analogy to (indeed, probably extending) the existing safeguards by way of guards (~pl[guard]). For now, we'll sleep a bit better knowing that we have been up-front about this potential problem. The following list of functions contains all those that are defined in Common Lisp to return generalized Booleans but are assumed by ACL2 to return Booleans. In addition, the functions ~ilc[acl2-numberp] and ~ilc[complex-rationalp] are directly defined in terms of respective Common Lisp functions ~c[numberp] and ~c[complexp]. ~bv[] /= < = alpha-char-p atom char-equal char< char<= char> char>= characterp consp digit-char-p endp eq eql equal evenp integerp keywordp listp logbitp logtest lower-case-p minusp oddp plusp rationalp standard-char-p string-equal string< string<= string> string>= stringp subsetp symbolp upper-case-p zerop ~ev[] ") (defun defun-mode (name wrld) ":Doc-Section Miscellaneous determines whether a function definition is a logical act~/ Two ``~il[defun-mode]s'' are supported, ~c[:]~ilc[program] and ~c[:]~ilc[logic]. Roughly speaking, ~c[:]~ilc[program] mode allows you to prototype a function for execution without any proof burdens, while ~c[:]~ilc[logic] mode allows you to add a new definitional axiom to the logic. The system comes up in ~c[:]~ilc[logic] mode. Execution of functions whose ~il[defun-mode] is ~c[:]~ilc[program] may render ACL2 unsound! ~l[defun-mode-caveat]. Note that calls of ~ilc[local] and of many ~il[events] are skipped in ~c[:program] mode; ~pl[program].~/ When you define a function in the ACL2 logic, that function can be run on concrete data. But it is also possible to reason deductively about the function because each definition extends the underlying logic with a definitional axiom. To ensure that the logic is sound after the addition of this axiom, certain restrictions have to be met, namely that the recursion terminates. This can be quite challenging. Because ACL2 is a ~il[programming] language, you often may wish simply to program in ACL2. For example, you may wish to define your system and test it, without any logical burden. Or, you may wish to define ``utility'' functions ~-[] functions that are executed to help manage the task of building your system but functions whose logical properties are of no immediate concern. Such functions might be used to generate test data or help interpret the results of tests. They might create files or explore the ACL2 database. The termination arguments for such functions are an unnecessary burden provided no axioms about the functions are ever used in deductions. Thus, ACL2 introduces the idea of the ``~il[defun-mode]'' of a function. The ~c[:mode] keyword of ~ilc[defun]'s ~ilc[declare] ~c[xarg] allows you to specify the ~il[defun-mode] of a given definition. If no ~c[:mode] keyword is supplied, the default ~il[defun-mode] is used; ~pl[default-defun-mode]. There are two ~il[defun-mode]s, each of which is written as a keyword: ~c[:]~ilc[program] ~-[] logically undefined but executable outside deductive contexts. ~c[:]~ilc[logic] ~-[] axiomatically defined as per the ACL2 definitional principle. It is possible to change the ~il[defun-mode] of a function from ~c[:]~ilc[program] to ~c[:]~ilc[logic]. We discuss this below. We think of functions having ~c[:]~ilc[program] mode as ``dangerous'' functions, while functions having ~c[:]~ilc[logic] mode are ``safe.'' The only requirement enforced on ~c[:]~ilc[program] mode functions is the syntactic one: each definition must be well-formed ACL2. Naively speaking, if a ~c[:]~ilc[program] mode function fails to terminate then no harm is done because no axiom is added (so inconsistency is avoided) and some invocations of the function may simply never return. This simplistic justification of ~c[:]~ilc[program] mode execution is faulty because it ignores the damage that might be caused by ``mis-guarded'' functions. ~l[defun-mode-caveat]. We therefore implicitly describe an imagined implementation of ~il[defun-mode]s that is safe and, we think, effective. But please ~pl[defun-mode-caveat]. The default ~il[defun-mode] is ~c[:]~ilc[logic]. This means that when you ~ilc[defun] a function the system will try to prove termination. If you wish to introduce a function of a different ~il[defun-mode] use the ~c[:mode] ~ilc[xargs] keyword. Below we show ~c[fact] introduced as a function in ~c[:]~ilc[program] mode. ~bv[] (defun fact (n) (declare (xargs :mode :program)) (if (or (not (integerp n)) (= n 0)) 1 (* n (fact (1- n))))) ~ev[] No axiom is added to the logic as a result of this definition. By introducing ~c[fact] in ~c[:]~ilc[program] mode we avoid the burden of a termination proof, while still having the option of executing the function. For example, you can type ~bv[] ACL2 !>(fact 3) ~ev[] and get the answer ~c[6]. If you type ~c[(fact -1)] you will get a hard lisp error due to ``infinite recursion.'' However, the ACL2 theorem prover knows no axioms about ~c[fact]. In particular, if the term ~c[(fact 3)] arises in a proof, the theorem prover is unable to deduce that it is ~c[6]. From the perspective of the theorem prover it is as though ~c[fact] were an undefined function symbol of arity ~c[1]. Thus, modulo certain important issues (~pl[defun-mode-caveat]), the introduction of this function in ~c[:]~ilc[program] mode does not imperil the soundness of the system ~-[] despite the fact that the termination argument for ~c[fact] was omitted ~-[] because nothing of interest can be proved about ~c[fact]. Indeed, we do not allow ~c[fact] to be used in logical contexts such as conjectures submitted for proof. It is possible to convert a function from ~c[:]~ilc[program] mode to ~c[:]~ilc[logic] mode at the cost of proving that it is admissible. This can be done by invoking ~bv[] (verify-termination fact) ~ev[] which is equivalent to submitting the ~ilc[defun] of ~c[fact], again, but in ~c[:]~ilc[logic] mode. ~bv[] (defun fact (n) (declare (xargs :mode :logic)) (if (or (not (integerp n)) (= n 0)) 1 (* n (fact (1- n))))) ~ev[] This particular event will fail because the termination argument requires that ~c[n] be nonnegative. A repaired ~ilc[defun], for example with ~ilc[=] replaced by ~ilc[<=], will succeed, and an axiom about ~c[fact] will henceforth be available. Technically, ~ilc[verify-termination] submits a redefinition of the ~c[:]~ilc[program] mode function. This is permitted, even when ~ilc[ld-redefinition-action] is ~c[nil], because the new definition is identical to the old (except for its ~c[:mode] and, possibly, other non-logical properties). ~l[guard] for a discussion of how to restrict the execution of functions. ~il[Guard]s may be ``verified'' for functions in ~c[:]~ilc[logic] mode; ~pl[verify-guards]." ; Only function symbols have defun-modes. For all other kinds of names ; e.g., package names and macro names, the "defun-mode" is nil. ; Implementation Note: We do not store the defun-mode of a symbol on the ; property list of the symbol. We compute the defun-mode from the symbol-class. (cond ((and (symbolp name) (function-symbolp name wrld)) (fdefun-mode name wrld)) (t nil))) ; Rockwell Addition: Consider the guard conjectures for a stobj-using ; function. Every accessor and updater application will generate the ; obligation to prove (stp st), where stp is the recognizer for the ; stobj st. But this is guaranteed to be true for bodies that have ; been translated as defuns, because of the syntactic restrictions on ; stobjs. So in this code we are concerned with optimizing these ; stobj recognizer expressions away, by replacing them with T. (defun get-stobj-recognizer (stobj wrld) ; If stobj is a stobj name, return the name of its recognizer; else nil. The ; value of the 'stobj property is always (*the-live-var* recognizer creator ; ...), for all user defined stobj names. The value is '(*the-live-state*) for ; STATE and is nil for all other names. (cond ((eq stobj 'state) 'state-p) (t (cadr (getprop stobj 'stobj nil 'current-acl2-world wrld))))) (defun stobj-recognizer-terms (known-stobjs wrld) ; Given a list of stobjs, return the list of recognizer applications. ; E.g., given (STATE MY-ST) we return ((STATE-P STATE) (MY-STP MY-ST)). (cond ((null known-stobjs) nil) (t (cons (fcons-term* (get-stobj-recognizer (car known-stobjs) wrld) (car known-stobjs)) (stobj-recognizer-terms (cdr known-stobjs) wrld))))) (defun mcons-term-smart (fn args) ; The following function is guaranteed to create a term provably equal to (cons ; fn args). If we find other optimizations to make here, we should feel free ; to do so. (if (and (eq fn 'if) (equal (car args) *t*)) (cadr args) (cons-term fn args))) (mutual-recursion (defun optimize-stobj-recognizers1 (known-stobjs recog-terms term) (cond ((variablep term) term) ((fquotep term) term) ((flambda-applicationp term) ; We optimize the stobj recognizers in the body of the lambda. We do ; not have to watch out of variable name changes, since if a stobj ; name is passed into a lambda it is passed into a local of the same ; name. We need not optimize the body if no stobj name is used as a ; formal. But we have to optimize the args in either case. (let ((formals (lambda-formals (ffn-symb term))) (body (lambda-body (ffn-symb term)))) (cond ((intersectp-eq known-stobjs formals) (fcons-term (make-lambda formals (optimize-stobj-recognizers1 known-stobjs recog-terms body)) (optimize-stobj-recognizers1-lst known-stobjs recog-terms (fargs term)))) (t (fcons-term (ffn-symb term) (optimize-stobj-recognizers1-lst known-stobjs recog-terms (fargs term))))))) ((and (null (cdr (fargs term))) (member-equal term recog-terms)) ; If the term is a recognizer call, e.g., (MY-STP MY-ST), we replace ; it by T. The first conjunct above is just a quick test: If the term ; has 2 or more args, then don't bother to do the member-equal. If ; the term has 1 or 0 (!) args we do. We won't find it if it has 0 ; args. *t*) (t (mcons-term-smart (ffn-symb term) (optimize-stobj-recognizers1-lst known-stobjs recog-terms (fargs term)))))) (defun optimize-stobj-recognizers1-lst (known-stobjs recog-terms lst) (cond ((endp lst) nil) (t (cons (optimize-stobj-recognizers1 known-stobjs recog-terms (car lst)) (optimize-stobj-recognizers1-lst known-stobjs recog-terms (cdr lst))))))) (defun optimize-stobj-recognizers (known-stobjs term wrld) ; Term is a term. We scan it and find every call of the form (st-p ; st) where st is a member of known-stobjs and st-p is the stobj ; recognizer function for st. We replace each such call by T. The ; idea is that we have simplified term under the assumption that each ; (st-p st) is non-nil. This simplification preserves equivalence ; with term PROVIDED all stobj recognizers are Boolean valued! (cond ((null known-stobjs) term) (t (optimize-stobj-recognizers1 known-stobjs (stobj-recognizer-terms known-stobjs wrld) term)))) ; Rockwell Addition: The new flag, stobj-optp, determines whether the ; returned guard has had all the stobj recognizers optimized away. Of ; course, whether you should call this with stobj-optp t or nil ; depends on the expression you're exploring: if it has been suitably ; translated, you can use t, else you must use nil. Every call of ; guard (and all the functions that call those) has been changed to ; pass down this flag. I won't mark every such place, but they'll ; show up in the compare-windows. (defun guard (fn stobj-optp w) ; This function is just the standard way to obtain the guard of fn in ; world w. ; If stobj-optp is t, we optimize the returned term, simplifying it ; under the assumption that every stobj recognizer in it is true. If ; fn traffics in stobjs, then it was translated under the stobj ; syntactic restrictions. Let st be a known stobj for fn (i.e., ; mentioned in its stobjs-in) and let st-p be the corresponding ; recognizer. This function should only be called with stobj-optp = t ; if you know (st-p st) to be true in the context of that call. ; The documentation string below addresses the general notion of ; guards in ACL2, rather than explaining this function. ":Doc-Section Miscellaneous restricting the domain of a function~/ The ACL2 system provides a mechanism for restricting a function to a particular domain. Such restrictions are called ``guards.'' A definition's guard has no effect on the logical axiom added when that definition is accepted by ACL2, and novices are often best served by avoiding guards. However, guards can be useful as a specification device or for increasing execution efficiency. To make such a restriction, use the ~c[:guard] keyword (~pl[xargs]) or, especially if you want the host Lisp compiler to use this information, use ~c[type] declarations (~pl[declare]; also ~pl[xargs] for a discussion of the ~c[split-types] keyword). The general issue of guards and guard verification is discussed in the topics listed below.~/ To begin further discussion of guards, ~pl[guard-introduction]. That section directs the reader to further sections for more details. To see just a summary of some ~il[command]s related to guards, ~pl[guard-quick-reference]. For a discussion of the use of proof to verify the absence of guard violations, ~pl[verify-guards].~/ :cite verify-guards :cite set-verify-guards-eagerness" (cond ((flambdap fn) *t*) ((or (not stobj-optp) (all-nils (stobjs-in fn w)) ) (getprop fn 'guard *t* 'current-acl2-world w)) (t ; If we have been told to optimize the stobj recognizers (stobj-optp = ; t) and there are stobjs among the arguments of fn, then fn was ; translated with the stobj syntactic restrictions enforced for those ; names. That means we can optimize the guard of the function ; appropriately. (optimize-stobj-recognizers (collect-non-x 'nil (stobjs-in fn w)) (or (getprop fn 'guard *t* 'current-acl2-world w) ; Once upon a time we found a guard of nil, and it took awhile to track down ; the source of the ensuing error. (illegal 'guard "Found a nil guard for ~x0." (list (cons #\0 fn)))) w)))) (defdoc extra-info ":Doc-Section Guard generate markers to indicate sources of guard proof obligations~/ ~l[guard-debug] for a discussion of this function, which is useful for debugging failures during ~il[guard] verification.~/~/") (defdoc guard-debug ":Doc-Section Guard generate markers to indicate sources of guard proof obligations~/ ACL2 guard verification (~pl[guard]) is often best avoided by beginning users of ACL2. When guard verification is employed, it can generate numerous goals, some of which may not be theorems if the definition being processed has bugs. It can be difficult to find such bugs. This ~il[documentation] topic explains a capability provided by ACL2 to help find such bugs. We begin with the following example. Although it is contrived and a bit simplistic, it illustrates how the guard-debug utility works. ~bv[] (defun length-repeat (x) (length (append x x))) (verify-guards length-repeat :guard-debug t) ~ev[] The output produces two top-level key checkpoints, as follows. ~bv[] Subgoal 2 (IMPLIES (EXTRA-INFO '(:GUARD (:BODY LENGTH-REPEAT)) '(APPEND X X)) (TRUE-LISTP X)) Subgoal 1 (IMPLIES (AND (EXTRA-INFO '(:GUARD (:BODY LENGTH-REPEAT)) '(LENGTH (APPEND X X))) (NOT (TRUE-LISTP (APPEND X X)))) (STRINGP (APPEND X X))) ~ev[] The upper subgoal (numbered 2) says that the body of the definition of ~c[length-repeat] contains a call ~c[(APPEND X X)], which is the source of the goal. In this case, that makes sense: the ~il[guard] for a call ~c[(append arg1 arg2)] is ~c[(true-listp arg1)], which in this case is ~c[(TRUE-LISTP X)]. The lower subgoal (numbered 1) says that the same definition contains the call ~c[(LENGTH (APPEND X X))], which generates the proof obligation that if ~c[(APPEND X X)] does not satisfy ~c[true-listp], then it must satisfy ~c[stringp]. That proof obligation comes directly from the ~il[guard] for ~ilc[length]. Of course, the example above is a bit obvious. But for large definitional bodies such information can be very helpful. Note that guard-debug can be specified not only in ~ilc[verify-guards] events but also in ~ilc[xargs] ~ilc[declare] forms of ~ilc[defun] events. We now describe the guard-debug utility in some detail. ~c[(Extra-info x y)] always returns ~c[t] by definition. However, if ~il[guard] verification takes place with a non-~c[nil] setting of ~c[guard-debug] (see below), then the goals generated for guard verification can include hypotheses that are calls of ~c[extra-info]. Typically, such a hypothesis is of the following form: ~bv[] (extra-info '(:guard (:body F)) '(G ARG1 ... ARGk)) ~ev[] The above form indicates that the goal in which it occurs was generated to verify that the ~il[guard] of the function ~c[F] is satisfied by the arguments ~c[ARG1] through ~c[ARGk], where the term ~c[(G ARG1 ... ARGk)] occurs in the body of the function ~c[F] whose guard verification is in progress. If however the above call of ~c[G] occurs in the guard of ~c[F] instead of the body of ~c[F], then ~c[:body] is replaced by ~c[:guard] above: ~bv[] (extra-info '(:guard (:guard F)) '(G ARG1 ... ARGk)) ~ev[] If the same proof obligation (goal clause) arises from more than one occurrence of the same call, then a single goal will be generated, which has several ~c[extra-info] hypotheses added to show the multiple sources of that proof obligation. All rules (~pl[rune]) associated with ~c[extra-info] are ~il[disable]d by default, so that hypotheses of the form described above are not simplified to ~c[t]. These hypotheses are intended to ride along for free: you can generally expect the proof to have the same structure whether or not the ~c[:guard-debug] option is supplied, though on rare occasions the proofs may diverge. It remains to explain the notion of a ~c[guard-debug] setting of ~c[t], that is, to explain how to obtain the additional hypotheses described above. If guards are being verified during processing of a ~ilc[defun] event (whether or not inside a call of ~ilc[mutual-recursion]), then one specifies ~c[:guard-debug t] in an ~ilc[xargs] declaration of a ~ilc[declare] form; ~pl[xargs]. If however the guard verification is on behalf of a ~ilc[verify-guards] call, whether for a definition or a theorem, then one specifies the keyword argument ~c[:guard-debug t].~/ Also ~pl[print-gv] for a utility for debugging guard violations, in contrast to the above guard-debug mechanism, which is for debugging failed proofs arising from guard verification.~/") (deflabel guard-quick-reference :doc ":Doc-Section Guard brief summary of guard checking and guard verification~/ For a careful introduction to guards, ~pl[guard].~/ ~b[I. GUARD CHECKING DURING EXECUTION] ~em[Effect] Guards on definitions are checked at execution time (except for guards on subsidiary calls of recursive or mutually recursive functions). ~em[When does it happen] By default, guards are checked for all forms submitted at the top level. ~em[To disable]~nl[] ~c[:set-guard-checking nil] ; skip raw Lisp if there is a guard violation ~c[:set-guard-checking :none] ; skip guard checking entirely ~em[To (re-)enable]~nl[] ~c[:set-guard-checking t] ~l[set-guard-checking] for more options. ~b[II. GUARD VERIFICATION] ~em[Effect] A proof is attempted of the obligations arising from the guards of subsidiary functions in a ~ilc[defun], ~ilc[defthm], or ~ilc[defaxiom] event. In the case of a ~c[defun], the guard itself is also verified (under an implicit guard of ~c[t]). ~em[When does it happen] Only names of defined functions, ~ilc[defthm]s, and ~ilc[defaxiom]s are subject to guard verification. Guard verification may occur when functions are defined (using ~ilc[defun]), but it requires an explicit call of ~ilc[verify-guards] in order to verify guards for ~ilc[defthm]s and ~ilc[defaxiom]s. Constrained functions (~pl[encapsulate]) may not have their guards verified. ~c[(verify-guards foo ...)]~nl[] causes guard verification for the ~ilc[defun], ~ilc[defthm], or ~ilc[defaxiom] named by ~c[foo], if it has not already been successfully done. The default ~il[defun-mode] (~pl[default-defun-mode]) must be ~c[:]~ilc[logic], or else this event is ignored. ~c[(defun foo ...)]~nl[] causes guard verification of ~c[foo] if and only if the following two conditions are both met. First, foo is processed in ~c[:]~ilc[logic] mode (either by setting mode ~c[:]~ilc[logic] globally, or by including ~c[:mode :logic] in the ~ilc[xargs] declaration). Second, at least one of the following sub-conditions is met. Also ~pl[xargs], and ~pl[set-verify-guards-eagerness] for how to change this behavior. ~bq[] 1. The ~ilc[xargs] declaration specifies a ~c[:]~ilc[guard]. 2. There is at least one ~c[type] declaration (~pl[declare]). 3. The ~ilc[xargs] declaration specifies ~c[:]~ilc[stobj]~c[s]. 4. The ~ilc[xargs] declaration specifies ~c[:]~ilc[verify-guards] ~c[t]. ~eq[] ~c[(verify-termination foo ...)]~nl[] causes guard verification of ~c[foo] if ~c[foo] is a function currently defined in ~c[:]~ilc[program] mode and the criteria for guard verification of a ~ilc[defun] form are met, as discussed above. The default ~il[defun-mode] (~pl[default-defun-mode]) must be ~c[:]~ilc[logic], or else this event is ignored.") (deflabel guard-introduction :doc ":Doc-Section Guard introduction to ~il[guard]s in ACL2~/ Most users can probably profit by avoiding dealing with guards most of the time. If they seem to get in the way, they can be ``turned off'' using the command ~c[:]~ilc[set-guard-checking] ~c[nil]; for more about this, ~pl[set-guard-checking]. For more about guards in general, ~pl[guard].~/ The guard on a function symbol is a formula about the formals of the function. To see the guard on a function, use the keyword command ~c[:]~ilc[args]. ~l[args]. To specify the guard on a function at ~c[defun-time], use the ~c[:]~ilc[guard] ~c[xarg] (~l[xargs]) or ~c[type] declarations (~pl[declare]). The latter may be preferable if you want the host Lisp compiler to use this information. Guards can be seen as having either of two roles: (a) they are a specification device allowing you to characterize the kinds of inputs a function ``should'' have, or (b) they are an efficiency device allowing logically defined functions to be executed directly in Common Lisp. Briefly: If the guards of a function definition are ``verified'' (~pl[verify-guards]), then the evaluation of a call of that function on arguments satisfying its guard will have the following property: ~bq[] All subsequent function calls during that evaluation will be on arguments satisfying the guard of the called function. ~eq[] The consequence of this fact for (a) is that your specification function is well-formed, in the sense that the values returned by this function on appropriate arguments only depend on the restrictions of the called functions to their intended domains. The consequence of this fact for (b) is that in the ACL2 system, when a function whose guards have been verified is called on arguments that satisfy its guard, then the raw lisp function defined by this function's ~ilc[defun] event is used to evaluate the call. Note however that even when the user-supplied ~ilc[defun] is not used, ACL2 uses a corresponding ``executable counterpart'' that generally performs, we expect, nearly as well as the raw lisp function. ~l[comp] to see how ~il[compilation] can speed up both kinds of execution. Let us turn next to the issue of the relationship between guards and evaluation. ~l[guards-and-evaluation].") (deflabel guards-and-evaluation :doc ":Doc-Section Guard the relationship between guards and evaluation~/ The guard has no effect on the logical axiom added by the definition of a function. It does, however, have consequences for how calls of that function are evaluated in ACL2. We begin by explaining those consequences, when ACL2 is in its default ``mode,'' i.e., as originally brought up. In subsequent discussion we'll consider other ways that guards can interact with evaluation. For more about guards in general, ~pl[guard]. For in-depth discussion of the interaction between the ~il[defun-mode] and guard checking, ~pl[set-guard-checking], ~pl[guard-evaluation-table], ~pl[guard-evaluation-examples-script], and ~pl[guard-evaluation-examples-log]. Also ~pl[generalized-booleans] for discussion about a subtle issue in the evaluation of certain Common Lisp functions.~/ ~em[Guards and evaluation I: the default] Consider the following very simple definition. ~bv[] (defun foo (x) (cons 1 (cdr x))) ~ev[] First consider how raw Common Lisp behaves when evaluating calls of this function. To evaluate ~c[(foo x)] for some expression ~c[x], first ~c[x] is evaluated to some value ~c[v], and then ~c[(cons 1 (cdr x))] is evaluated with ~c[x] bound to ~c[v]. For example, if ~c[v] is ~c[(cons 'a 3)], then Common Lisp computes ~c[(cons 1 3)]. But if (for example) ~c[v] is a number, e.g., ~c[7], then there is no way to predict what Common Lisp might do. Some implementations would cause ``sensible'' errors, others might return nonsense, still others might crash the host machine. The results tend toward the catastrophic if the call of ~c[foo] in question is in compiled code. Now by default, ACL2 evaluates calls of ~c[foo] exactly as Common Lisp does, except that it uses guards to check the ``legality'' of each function call. So for example, since ~c[(cdr x)] has a guard of ~c[(or (consp x) (equal x nil))], the call ~c[(foo 7)] would cause a ``guard violation,'' as illustrated below. ~bv[] ACL2 !>(foo 7) ACL2 Error in TOP-LEVEL: The guard for the function symbol CDR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CDR 7). ACL2 !> ~ev[] Thus, the relation between evaluation in ACL2 and evaluation in Common Lisp is that the two produce the very same results, provided there is no guard violation. ~em[Guards and evaluation II:] ~c[:]~ilc[set-guard-checking]. The ACL2 logic is a logic of total functions. That is, every application of a function defined has a completely specified result. See the ~il[documentation] for each individual primitive for the specification of what it returns when its guard is violated; for example, ~pl[cdr]. The presence of guards thus introduces a choice in the sense of evaluation. When you type a form for evaluation do you mean for guards to be checked or not? Put another way, do you mean for the form to be evaluated in Common Lisp (if possible) or in the ACL2 logic? Note: If Common Lisp delivers an answer, it will be the same as in the logic, but it might be erroneous to execute the form in Common Lisp. For example, the ACL2 logic definition of ~ilc[cdr] implies that the ~ilc[cdr] of an ~il[atom] is ~c[nil]; ~pl[cdr]. So: should ~c[(cdr 7)] cause a guard violation error or return ~c[nil]? The top-level ACL2 loop has a variable which controls which sense of execution is provided. By default, ``guard checking'' is on, by which we mean that guards are checked at runtime, in the sense already described. To allow execution to proceed in the logic when there is a guard violation, do ~c[:]~ilc[set-guard-checking]~c[ nil]; or evaluate ~c[:]~ilc[set-guard-checking]~c[ :none] to skip guard checking entirely. To turn ``guard checking'' back on, execute the top-level form ~c[:]~ilc[set-guard-checking]~c[ t]. The status of guard checking reflected in the ~il[prompt]; guard-checking is ``on'' when the ~il[prompt] contains an exclamation mark (also ~pl[default-print-prompt]). For example, ~bv[] ACL2 !> ~ev[] means guard checking is on and ~bv[] ACL2 > ~ev[] means guard checking is off. The exclamation mark can be thought of as ``barring'' certain computations. The absence of the mark suggests the absence of error messages or unbarred access to the logical axioms. Thus, for example ~bv[] ACL2 !>(car 'abc) ~ev[] will signal an error, while ~bv[] ACL2 >(car 'abc) ~ev[] will return ~c[nil]. To return to our previous example: with guard checking off, ~c[(foo 7)] evaluates to ~c[(cons 1 nil)]. Also ~pl[set-guard-checking]. ~em[Guards and evaluation III: guard verification] Consider the defininition of ~c[foo] given above, but modified so that a reasonable guard of ~c[(consp x)] is specified, as shown below. ~bv[] (defun foo (x) (declare (xargs :guard (consp x))) (cons 1 (cdr x))) ~ev[] We say ``reasonable guard'' above because if ~c[x] is such that ~c[(consp x)] holds, then the call of ~ilc[cdr] in the evaluation of ~c[(foo x)] will not cause a guard violation. Thus, it ``should'' be legal to evaluate ~c[(foo x)], for any such ~c[x], simply by evaluating this form in raw Common Lisp. The ~ilc[verify-guards] event has been provided for this purpose. Details may be found elsewhere; ~pl[verify-guards]. Briefly, for any defined function ~c[fn], the event ~c[(verify-guards fn)] attempts to check the condition discussed above, that whenever ~c[fn] is called on arguments that satisfy its guard, the evaluation of this call will proceed without any guard violation. (Moreover, the guard itself should be evaluable without any guard violation.) If this check is successful, then future calls of this sort will be evaluated in raw Common Lisp. Returning to our example above, the ~c[(verify-guards foo)] will succeed because the guard ~c[(consp x)] of ~c[foo] implies the guard generated from the call ~c[(cdr x)] in the body of the definition, namely, ~c[(or (consp x) (equal x nil))] (~pl[cdr]). Then the evaluation of ~c[(foo (cons 'a 3))] will take place in raw Common Lisp, because ~c[(cons 'a 3)] satisfies the guard of ~c[foo]. This ability to dive into raw Common Lisp hinges on the proof that the guards you attach to your own functions are sufficient to ensure that the guards encountered in the body are satisfied. This is called ``guard verification.'' Once a function has had its guards verified, then ACL2 can evaluate the function somewhat faster (but see ``Guards and evaluation V: efficiency issues'' below). Perhaps more importantly, ACL2 can also guarantee that the function will be evaluated correctly by any implementation of Common Lisp (provided the guard of the function is satisfied on the input). That is, if you have verified the guards of a system of functions and you have determined that they work as you wish in your host ACL2 (perhaps by proving it, perhaps by testing), then they will work identically in any Common Lisp. There is a subtlety to our treatment of evaluation of calls of functions whose guards have been verified. If the function's guard is not satisfied by such a call, then no further attempt is made to evaluate any call of that function in raw lisp during the course of evaluation of that call. This is obvious if guard checking is on, because an error is signalled the first time its guard is violated; but in fact it is also true when guard checking is off. ~l[guard-example] for an example. ~em[Guards and evaluation IV: functions having :program mode] Strictly speaking, functions in ~c[:]~ilc[program] mode (~pl[defun-mode]) do not have definitions in the ACL2 logic. So what does it mean to evaluate calls of such functions in ACL2? In general we treat ~c[:]~ilc[program] functions much as we treat ~c[:]~ilc[logic] functions whose guards have been verified, except that when no error occurs then the corresponding raw Lisp function is always called. (We say ``in general'' because there are exceptions, discussed in the ``Aside'' just below.) Note that when the guard of a function in ~c[:]~ilc[logic] mode is violated, there is still a value that the ACL2 logic proves is equal to the given call. But the same cannot be said for a function in ~c[:]~ilc[program] mode. Nevertheless, for the sake of convenience we go ahead and evaluate the corresponding raw Lisp function except in the situation where the guard is violated and guard-checking is on, aside from the following: ~st[Aside]. There are exceptions to the use of raw Lisp, discussed just above, to evaluate calls of ~c[:]~ilc[program] mode functions. The primary one is that after ~c[:]~ilc[set-guard-checking]~c[ :none], evaluation of user-defined ~c[:]~ilc[program] mode function calls is done in the ACL2 logic, not in raw Lisp. The more obscure exception is that during expansion of macros and ~ilc[make-event] forms, and during evaluation of ~ilc[defconst] forms, ACL2 enters a ``safe mode'' in which this escape to raw Lisp is prevented. The following example illustrates how the user can experiment directly with safe mode, though it is preferred to use ~c[:]~ilc[set-guard-checking]~c[ :none] if you are happy to skip all guard checking and evaluate forms in the logic. ~bv[] ACL2 !>(defun foo (x) (declare (xargs :mode :program :guard t)) (car x)) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo 3) Error: Attempt to take the car of 3 which is not listp. [condition type: SIMPLE-ERROR] Restart actions (select using :continue): 0: Return to Top Level (an \"abort\" restart). 1: Abort entirely from this process. [1] ACL2(2): :pop ACL2 !>(assign safe-mode t) T ACL2 !>(foo 3) ACL2 Error in TOP-LEVEL: The guard for the function symbol CAR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CAR 3). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>(assign safe-mode nil) NIL ACL2 !>(foo 3) Error: Attempt to take the car of 3 which is not listp. [condition type: SIMPLE-ERROR] Restart actions (select using :continue): 0: Return to Top Level (an \"abort\" restart). 1: Abort entirely from this process. [1] ACL2(2): ~ev[] The other exception occurs after ~ilc[set-guard-checking] can be called with a value of ~c[:all]; ~pl[set-guard-checking]. ~st[End of aside.] Thus, as with ~c[:]~ilc[logic] functions: when a guard has been satisfied on a call of a function with ~c[:]~ilc[program] mode, no subsidiary guard checking will be done. Notice that by treating functions in ~c[:]~ilc[program] mode like functions whose guards have been verified, we are using raw lisp to compute their values when their guards are met. We do not check guards any further once raw lisp is invoked. This can lead to hard lisp errors if the guards are not appropriate, as illustrated below. ~bv[] ACL2 >:program ACL2 p>(defun foo (x) (declare (xargs :guard t)) (cons 1 (cdr x))) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.02 seconds (prove: 0.00, print: 0.00, proof tree: 0.00, other: 0.02) FOO ACL2 p>(foo 3) Error: 3 is not of type LIST. Fast links are on: do (use-fast-links nil) for debugging Error signalled by CDR. Broken at COND. Type :H for Help. ACL2>> ~ev[] ~l[defun-mode-caveat]. However, here is a way to get ACL2 to do run-time guard checking for user-defined ~c[:]~ilc[program] mode functions. With this method, ACL2 will evaluate calls of user-defined ~c[:program] mode functions in a manner that follows their ACL2 definitions. Simply execute the following in the ACL2 loop to put ACL2 into a ``safe mode.'' ~bv[] (f-put-global 'safe-mode t state) ~ev[] Let us revisit the example above, using safe mode. Notice that the guard of ~ilc[cdr] is now being checked, because the executable counterpart of ~c[foo] is being called even though the ~il[guard] is ~c[t]. ~bv[] ACL2 !>(f-put-global 'safe-mode t state) ACL2 !>:program ACL2 p!>(defun foo (x) (declare (xargs :guard t)) (cons 1 (cdr x))) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 p!>(foo 3) ACL2 Error in TOP-LEVEL: The guard for the function symbol CDR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CDR 3). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 p!> ~ev[] If we go back into ``unsafe'' mode, then we once again see a raw Lisp error, as we now illustrate. ~bv[] ACL2 p!>(f-put-global 'safe-mode nil state) ACL2 p!>(foo 3) Error: 3 is not of type LIST. Fast links are on: do (si::use-fast-links nil) for debugging Error signalled by CDR. Broken at COND. Type :H for Help. ACL2>> ~ev[] ~em[Guards and evaluation V: efficiency issues] We have seen that by verifying the guards for a ~c[:]~ilc[logic] function, we arrange that raw lisp is used for evaluation of calls of such functions when the arguments satisfy its guard. This has several apparent advantages over the checking of guards as we go. First, the savings is magnified as your system of functions gets deeper: the guard is checked upon the top-level entry to your system and then raw Common Lisp does all the computing. Second, if the raw Common Lisp is compiled (~pl[compilation]), enormous speed-ups are possible. Third, if your Common Lisp or its compiler does such optimizations as ~c[tail-recursion] removal, raw Common Lisp may be able to compute your functions on input much ``bigger'' than ACL2 can. The first of these advantages is quite important if you have complicated guards. However, the other two advantages are probably not very important, as we now explain. When a function is defined in ~c[:]~ilc[logic] mode, its ~ilc[defun] is executed in raw Common Lisp. (We might call this the ``primary'' raw lisp definition of the function.) However, a corresponding ``logic definition'' is also executed. The ``logic definition'' is a ~ilc[defun] in raw lisp that checks guards at runtime and escapes to the primary raw lisp definition if the guard holds of the arguments and the function has already had its guards verified. Otherwise the logic definition executes the body of the function by calling the logic definitions of each subroutine. Now it is true that ~il[compilation] generally speeds up execution enormously. However, the ~c[:]~ilc[comp] command (~pl[comp]) compiles both of the raw lisp definitions associated with a ~c[:]~ilc[logic] function. Also, we have attempted to arrange that for every tail recursion removal done on the actual ~ilc[defun], a corresponding tail recursion removal is done on the ``logic definition.'' We believe that in most cases, the logic definition executes almost as fast as the primary raw lisp definition, at least if the evaluation of the guards is fast. So, the main advantage of guard verification is probably that it lets you know that the function may be executed safely in raw lisp, returning the value predicted by the ACL2 logic, whenever its arguments satisfy its guard. We envision the development of systems of applicative lisp functions that have been developed and reasoned about using ACL2 but which are intended for evaluation in raw Common Lisp (perhaps with only a small ``core'' of ACL2 loaded), so this advantage of guard verification is important. Nevertheless, guard verification might be important for optimal efficiency when the functions make use of type declarations. For example, at this writing, the GCL implementation of Common Lisp can often take great advantage of ~ilc[declare] forms that assign small integer types to formal parameters. Note that while type declarations contributed to the guard by default, they can be proved from the guard instead; ~pl[xargs] for a discussion of the ~c[:SPLIT-TYPES] keyword. To continue the discussion of guards, ~pl[guards-for-specification] to read about the use of guards as a specification device.") (deflabel guards-for-specification :doc ":Doc-Section Guard guards as a specification device~/ A use of guard verification that has nothing to do with efficiency is as a way to gain confidence in specifications. This use has the feel of ``types'' in many traditional ~il[programming] languages, though guards allow much greater expressiveness than most systems of types (and unfortunately, as a result they are not syntactically checkable). For more discussion of guards in general, ~pl[guard].~/ Suppose you have written a collection of function definitions that are intended to specify the behavior of some system. Perhaps certain functions are only intended to be called on certain sorts of inputs, so you attach guards to those functions in order to ``enforce'' that requirement. And then, you verify the guards for all those functions. Then what have you gained, other than somewhat increased efficiency of execution (as explained above), which quite possibly isn't your main concern? You have gained the confidence that when evaluating any call of a (specification) function whose arguments satisfy that function's guard, all subsequent function calls during the course of evaluation will have this same property, that the arguments satisfy the guard of the calling function. In logical terms, we can say that the equality of the original call with the returned value is provable from weakened versions of the definitions, where each definitional axiom is replaced by an implication whose antecedent is the requirement that the arguments satisfy the guard and whose consequent is the original axiom. For example, ~bv[] (defun foo (x) (declare (xargs :guard (consp x))) (cons 1 (cdr x))) ~ev[] originally generates the axiom ~bv[] (equal (foo x) (cons 1 (cdr x))) ~ev[] but in fact, when evaluation involves no guard violation then the following weaker axiom suffices in the justification of the evaluation. ~bv[] (implies (consp x) (equal (foo x) (cons 1 (cdr x)))) ~ev[] If you are following links to read this documentation as a hypertext style document, then please ~pl[guard-miscellany]. This concludes our discussion of guards with miscellaneous remarks, and also contains pointers to related topics.") (deflabel guard-miscellany :doc ":Doc-Section Guard miscellaneous remarks about guards~/ The discussion of guards concludes here with a few miscellaneous remarks. (Presumably you found this documentation by following a link; ~pl[guards-for-specification].) For further information related to guards other than what you find under ``~il[guard],'' see any of the following documentation topics: ~il[guard-example], ~ilc[set-verify-guards-eagerness], ~ilc[set-guard-checking], ~ilc[verify-guards], and (for a discussion of keyword ~c[:SPLIT-TYPES]) ~ilc[xargs].~/ ~ilc[Defun] can be made to try to verify the guards on a function. This is controlled by the ``~il[defun-mode]'' of the ~ilc[defun]; ~pl[defun-mode]. The ~il[defun-mode] is either as specified with the ~c[:mode] ~c[xarg] of the ~ilc[defun] or else defaults to the default ~il[defun-mode]. ~l[default-defun-mode]. If the ~il[defun-mode] of the ~ilc[defun] is ~c[:]~ilc[logic] and either a ~il[guard] is specified explicitly or ~c[:]~ilc[verify-guards] ~c[t] is specified in the ~ilc[xargs], then we attempt to verify the guards of the function. Otherwise we do not. (But ~pl[set-verify-guards-eagerness] for how to modify this behavior.) It is sometimes impossible for the system to verify the guards of a recursive function at definition time. For example, the guard conjectures might require the invention and proof of some inductively derived property of the function (as often happens when the value of a recursive call is fed to a guarded subroutine). So sometimes it is necessary to define the function using ~c[:verify-guards nil] then to state and prove key theorems about the function, and only then have the system attempt guard verification. Post-~ilc[defun] guard verification is achieved via the event ~ilc[verify-guards]. ~l[verify-guards]. It should be emphasized that guard verification affects only two things: how fast ACL2 can evaluate the function and whether the function is executed correctly by raw Common Lisp, without guard violations. Since ACL2 does not use the raw Common Lisp definition of a function to evaluate its calls unless that function's guards have been verified, the latter effect is felt only if you run functions in raw Common Lisp rather than via ACL2's command loop. Guard verification does not otherwise affect the theorem prover or the semantics of a definition. If you are not planning on running your function on ``big'' inputs and you don't care if your function runs correctly in raw Common Lisp (e.g., you have formalized some abstract mathematical property and just happened to use ACL2 as your language), there is no need to suffer through guard verification. Often users start by not doing guard verification and address that problem later. Sometimes you are driven to it, even in mathematical projects, because you find that you want to run your functions particularly fast or in raw Common Lisp. If ~ilc[certify-book] is used to compile a file, and the file contains functions with unverified guard conjectures, then you will be warned that the compiled file cannot be loaded into raw Common Lisp with the expectation that the functions will run correctly. This is just the same point we have been making: ACL2 and Common Lisp agree only on the restricted domains specified by our guards. When guards are violated, Common Lisp can do anything. When you call a compiled function on arguments violating its guards, the chances are only increased that Common Lisp will go berserk, because compiled functions generally check fewer things at runtime and tend to be more fragile than interpreted ones. Finally, we note that ACL2 collects up ~il[guard]s from ~ilc[declare] forms in order of appearance. So for example, the ~ilc[declare] form ~bv[] (declare (xargs :guard (foo x)) (type string x) ~ev[] will generate the guard ~c[(and (foo x) (stringp x))], while the form ~bv[] (declare (type string x) (xargs :guard (foo x)) ~ev[] will generate the guard ~c[(and (stringp x) (foo x))]. The only exception to this rule is the case that ~c[:guard] and ~c[:stobjs] are specified in the same ~ilc[xargs] form, in which case the ~c[:stobjs] form will be treated as through it comes before the ~c[:guard] form.") (deflabel guard-evaluation-examples-script :doc ":Doc-Section Guard a script to show combinations of ~il[defun-mode]s and ~il[guard]-checking~/ Below is a script that illustrates the combination of ~il[defun-mode]s ~-[] ~c[:]~ilc[program] mode, ~c[:]~ilc[logic] mode without ~il[guard]s verified, and ~c[:]~ilc[logic] mode with ~il[guard]s verified ~-[] with values from ~ilc[set-guard-checking] ~-[] ~c[t] (the default), ~c[:all], ~c[:none], and ~c[nil]. (It does not illustrate the value ~c[:nowarn], which is the same as ~c[t] except for inhibiting a warning.) The script also illustrates cases where the guard is not, or is, ~c[t]. ~l[guard-evaluation-examples-log] for result of running this script. Before presenting the script below, we give some instructions in case you want to run it yourself. ~l[set-guard-checking] for discussion of the interaction between ~il[defun-mode]s and ~il[guard]-checking that is illustrated by this script. Also ~pl[guard-evaluation-table] for a succinct table, with associated discussion, that covers in detail the interactions illustrated here. The script mentions the running of ``Tracing Code''. The code is the following sequence of commands. ~bv[] (trace$ fact) :set-guard-checking t (fact 2) (fact t) :set-guard-checking :all (fact 2) (fact t) :set-guard-checking :none (fact 2) (fact t) :set-guard-checking nil (fact 2) (fact t) ~ev[] If you want to run the script yourself, you may find it handy to use the following Emacs keyboard macro for running the tracing code in 2-window mode, with the cursor in the window with the script and ACL2 running in the other window. ~bv[] (fset 'step-guard-script [?\C-a ?\C- ?\C-e ?\M-w ?\C-a ?\C-n ?\C-x ?o ?\M-> ?\C-y return ?\C-x ?o]) ; Put it on a key (if you have defined the indicated keymap by using ; emacs/emacs-acl2.el): (define-key ctl-t-keymap \"r\" 'step-guard-script) ~ev[] The script follows.~/ ~bv[] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Program mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun fact (x) (declare (xargs :guard (integerp x) :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. It shows execution in raw Lisp in the t and nil ; cases of :set-guard-checking, but not in the :all or :none cases. We get a ; guard violation for argument t in the case :set-guard-checking t. :u (defun fact (x) (declare (xargs :guard t :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. It should give the same results as above, ; except that we no longer get a guard violation in the case ; :set-guard-checking t. :u ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Logic mode, guard other than t ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun fact (x) (declare (xargs :guard (integerp x) :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. It should give guard violations for (fact t) ; with guard-checking set to t or :all. It should never run in raw Lisp, ; because we have not verified guards. In the t case, we can get a warning ; about avoiding the guard check on recursive calls, but only if we do not ; trace the function, fact. (verify-guards fact) ; Run the Tracing Code here. The results should be as described just above, ; except that now we go into raw Lisp for (fact 2) with guard-checking other ; than :none. :u :u ; The following definition is the same as above, except that guards are ; verified. (defun fact (x) (declare (xargs :guard (integerp x) :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. We should get the same traces as in the ; immediately preceding case, since guards had been verified in both cases. :u ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Logic mode, guard t ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun fact (x) (declare (xargs :guard t :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. We should never go in to raw Lisp, because ; guards have not been verified. We will see the same traces for (fact 2) as ; with the (integerp x) guard above with :verify-guards nil specified, except ; that even without tracing, there is no warning for :set-guard-checking t ; about recursive calls. And, there are no guard violations for (fact t), of ; course, since posp (necessarily, if we are to verify guards) has a guard of ; t. (verify-guards fact) ; Run the Tracing Code here. You shouldn't see any surprises. Note however ; that if we back up to the start (using :u :u) and then define fact as just ; above but without :verify-guards nil, then the :none setting will allow us ; to go into raw Lisp: although :none generally avoids execution of raw Lisp ; counterparts, it allows this when the guard is T and guards have been ; verified. ~ev[]~/") (deflabel guard-evaluation-examples-log :doc ":Doc-Section Guard log showing combinations of ~il[defun-mode]s and ~il[guard]-checking~/ ~l[guard-evaluation-examples-script] for a script that shows the interaction of ~il[defun-mode]s with the value set by ~ilc[set-guard-checking]. Here, we present a log resulting from running this script.~/ ~l[set-guard-checking] for discussion of the interaction between ~il[defun-mode]s and ~il[guard]-checking that is illustrated by this script. Also ~pl[guard-evaluation-table] for a succinct table, with associated discussion, that covers in detail the interactions illustrated here. ~bv[] ACL2 !>(defun fact (x) (declare (xargs :guard (integerp x) :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) Summary Form: ( DEFUN FACT ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 !>(trace$ fact) ((FACT)) ACL2 !>:set-guard-checking t Guard-checking-on already has value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the :program function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the :program function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard t :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) Summary Form: ( DEFUN FACT ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard (integerp x) :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). The non-trivial part of the measure conjecture is [[output omitted here]] Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) [[Comment added to the log: Normally you would get a message about guard-checking being inhibited on recursive calls. However, when a function is traced the guard-checking is done on recursive calls unless the guards have been verified (see :DOC verify-guards). ]] 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) [[Comment added to the log: In spite of the warning above, guards are checked here on self-recursive calls, because the function is traced. ]] 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >(verify-guards fact) Computing the guard conjecture for FACT.... The guard conjecture for FACT is trivial to prove, given the :compound- recognizer rule POSP-COMPOUND-RECOGNIZER, primitive type reasoning and the :type-prescription rule FACT. FACT is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS FACT) Rules: ((:COMPOUND-RECOGNIZER POSP-COMPOUND-RECOGNIZER) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:TYPE-PRESCRIPTION FACT)) Warnings: None Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u L 1:x(DEFUN FACT (X) ...) ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard (integerp x) :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). The non-trivial part of the measure conjecture is [[output omitted here]] Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard t :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). The non-trivial part of the measure conjecture is [[output omitted here]] Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >(verify-guards fact) Computing the guard conjecture for FACT.... The guard conjecture for FACT is trivial to prove, given the :compound- recognizer rule POSP-COMPOUND-RECOGNIZER and the :type-prescription rule FACT. FACT is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS FACT) Rules: ((:COMPOUND-RECOGNIZER POSP-COMPOUND-RECOGNIZER) (:TYPE-PRESCRIPTION FACT)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT [[Note added to log: No need to trace fact again after verify-guards.]] ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 > ~ev[]~/") (deflabel guard-evaluation-table :doc ":Doc-Section Guard a table that shows combinations of ~il[defun-mode]s and ~il[guard]-checking~/ ~l[set-guard-checking] for an introduction to the topic discussed here. Also ~pl[guard] for a general discussion of guards, and ~pl[guard-evaluation-examples-script] for a script that illustrates combinations presented below. Note: The default setting for guard-checking (that is, the initial value for ~c[(@ guard-checking-on)]) is ~c[T]. The table below illustrates the interaction of the ~il[defun-mode] with the value supplied to ~ilc[set-guard-checking]. The first row considers functions defined in ~c[:]~ilc[program] mode; the other two consider functions defined in ~c[:]~ilc[logic] mode. The columns correspond to four values of state global ~c['guard-checking-on], as supplied to ~ilc[set-guard-checking]. (A fifth value, ~c[:nowarn], is similar to ~c[t] but suppresses warnings encountered with ~c[t] (as explained in those warning messages), and is not considered here.) During proofs, ~c['guard-checking-on] is set to ~c[nil] regardless of how this variable has been set in the top-level loop. Below this table, we make some comments about its entries, ordered by row and then by column. For example, when we refer to ``b2'' we are discussing the execution of a ~c[:]~ilc[logic] mode function whose guards have not been verified, after having executed ~c[:]~ilc[set-guard-checking]~c[ :all]. ~bv[] guard-checking-on: (1)t (2):all (3):none (4)nil (a) :program a1 a2 a3 a4 (b) guards not verified b1 b2 b3 b4 (c) guards verified c1 c2 c3 c4 ~ev[] a1. Check the ~il[guard] upon entry, then use the raw Lisp code if the guard checks (else cause an error). This is a common setting when one wants a little guard checking but also wants the efficiency of raw Lisp. But note that you can get raw Lisp errors. For example, if you make the definition ~c[(defun foo (x) (car x))] in ~c[:]~ilc[program] mode and execute ~c[:]~ilc[set-guard-checking]~c[ t], and then execute ~c[(foo 3)], you will likely get an error from the call ~c[(car 3)] made in raw Lisp. a2. For built-in (predefined) functions, see a1 instead. Otherwise:~nl[] Check the ~il[guard], without exception. Thus, we never run the raw Lisp code in this case. This can be useful when testing ~c[:]~ilc[program] mode functions, but you may want to run ~c[:]~ilc[comp]~c[ t] or at least ~c[:]~ilc[comp]~c[ :exec] in this case, so that the execution is done using compiled code. a3. For built-in (predefined) functions, see a4 instead. Otherwise:~nl[] Do not check the ~il[guard]. For ~c[:]~ilc[program] mode functions, we never run the raw Lisp code in this case; so if you care about efficiency, see the comment in a2 above about ~c[:]~ilc[comp]. This combination is useful if you are using ACL2 as a programming language and do not want to prove theorems about your functions or suffer ~il[guard] violations. In this case, you can forget about any connection between ACL2 and Common Lisp. a4. Run the raw Lisp code without checking ~il[guard]s at all. Thus, for ~c[:]~ilc[program] mode functions, the ~c[nil] setting is often preferable to the ~c[:none] setting because you get the efficiency of raw Lisp execution. However, with ~c[nil] you can therefore get hard Lisp errors as in a1 above. b1. Guards are checked at the top-level, though not at self-recursive calls. We never run the raw Lisp code in this case; guards would need to be verified first. b2. Unlike the ~c[t] setting, guards are checked even on self-recursive calls. But like the ~c[t] setting, we do not run the raw Lisp code. Use this setting if you want guards checked on each recursive call in spite of the cost of doing so. b3, b4. Execution avoids the raw Lisp code and never checks guards. The ~c[nil] and ~c[:none] settings behave the same in this case (i.e., for ~c[:]~ilc[logic] mode functions whose guards have not been verified), except that recursive calls are never inlined for ~c[:none] and tracing (~pl[trace]) will show recursive calls for ~c[:none] but not for ~c[nil]. c1, c2. Guards are checked. If the checks pass, evaluation takes place using the raw Lisp code. If the checks fail, we get a guard violation. Either way, we do not execute ``in the logic''; we only execute using the raw Lisp code. Note that ~c[t] and ~c[:all] behave the same in this case, (i.e. for ~c[:]~ilc[logic] mode functions whose ~il[guard]s have been verified). c3, c4. For the ~c[:none] and ~c[nil] settings, ~c[:]~ilc[logic] mode functions whose guards have been verified will never cause guard violations. However, with ~c[nil] and for built-in functions in ~c[:logic] mode, guards are still checked: if the check succeeds, then evaluation is done using the raw Lisp code, and if not, it is done by the ``logic'' code, including self-recursive calls (though unlike the ~c[t] case, we will not see a warning about this). But with ~c[:none] for user-defined functions, no guard checking is done, and the only time the raw Lisp code will be executed is when the guard is ~c[t] and guards are verified at the time the executable counterpart of the function is defined (i.e., when the function is admitted unless it is later defined again and compiled using ~c[:]~ilc[comp]). Thus, if you use ~c[:none] and you want a function ~c[(foo x)] with guard ~c[(g x)] to execute using raw Lisp code, you can write a ``wrapper''function with a guard of ~c[t]: ~bv[] (defun foo-wrap (x) (declare (xargs :guard t)) (if (g x) (foo x) 'do-not-case)) ~ev[] If you want the speed of executing raw Lisp code and you have non-trivial guards on functions that you want to call at the top-level, use ~c[nil] rather than ~c[:none]. ~/~/") (defun guard-lst (fns stobj-optp w) (cond ((null fns) nil) (t (cons (guard (car fns) stobj-optp w) (guard-lst (cdr fns) stobj-optp w))))) (defmacro equivalence-relationp (fn w) ; See the Essay on Equivalence, Refinements, and Congruence-based ; Rewriting. ; (Note: At the moment, the fact that fn is an equivalence relation is ; encoded merely by existence of a non-nil 'coarsenings property. No ; :equivalence rune explaining why fn is an equivalence relation is to ; be found there -- though such a rune does exist and is indeed found ; among the 'congruences of fn itself. We do not track the use of ; equivalence relations, we just use them anonymously. It would be ; good to track them and report them. When we do that, read the Note ; on Tracking Equivalence Runes in subst-type-alist1.) `(let ((fn ,fn)) ; While both equal and iff have non-nil coarsenings properties, we make ; special cases of them here because they are common and we wish to avoid ; the getprop. (or (eq fn 'equal) (eq fn 'iff) (and (not (flambdap fn)) (getprop fn 'coarsenings nil 'current-acl2-world ,w))))) (defun >=-len (x n) (declare (xargs :guard (and (integerp n) (<= 0 n)))) (if (= n 0) t (if (atom x) nil (>=-len (cdr x) (1- n))))) (defun all->=-len (lst n) (declare (xargs :guard (and (integerp n) (<= 0 n)))) (if (atom lst) (eq lst nil) (and (>=-len (car lst) n) (all->=-len (cdr lst) n)))) (defun strip-cadrs (x) (declare (xargs :guard (all->=-len x 2))) (cond ((null x) nil) (t (cons (cadar x) (strip-cadrs (cdr x)))))) ; Rockwell Addition: Just moved from other-events.lisp (defun strip-cddrs (x) (declare (xargs :guard (all->=-len x 2))) (cond ((null x) nil) (t (cons (cddar x) (strip-cddrs (cdr x)))))) (defun global-set-lst (alist wrld) (cond ((null alist) wrld) (t (global-set-lst (cdr alist) (global-set (caar alist) (cadar alist) wrld))))) (defmacro cons-term1-body-mv2 () `(let ((x (unquote (car args))) (y (unquote (cadr args)))) (let ((evg (case fn ,@*cons-term1-alist* (if (kwote (if x y (unquote (caddr args))))) (not (kwote (not x)))))) (cond (evg (mv t evg)) (t (mv nil form)))))) (defun cons-term1-mv2 (fn args form) (declare (xargs :guard (and (pseudo-term-listp args) (quote-listp args)))) (cons-term1-body-mv2)) (mutual-recursion (defun sublis-var1 (alist form) (declare (xargs :guard (and (symbol-alistp alist) (pseudo-term-listp (strip-cdrs alist)) (pseudo-termp form)))) (cond ((variablep form) (let ((a (assoc-eq form alist))) (cond (a (mv (not (eq form (cdr a))) (cdr a))) (t (mv nil form))))) ((fquotep form) (mv nil form)) (t (mv-let (changedp lst) (sublis-var1-lst alist (fargs form)) (let ((fn (ffn-symb form))) (cond (changedp (mv t (cons-term fn lst))) ((and (symbolp fn) ; optimization (quote-listp lst)) (cons-term1-mv2 fn lst form)) (t (mv nil form)))))))) (defun sublis-var1-lst (alist l) (declare (xargs :guard (and (symbol-alistp alist) (pseudo-term-listp (strip-cdrs alist)) (pseudo-term-listp l)))) (cond ((endp l) (mv nil l)) (t (mv-let (changedp1 term) (sublis-var1 alist (car l)) (mv-let (changedp2 lst) (sublis-var1-lst alist (cdr l)) (cond ((or changedp1 changedp2) (mv t (cons term lst))) (t (mv nil l)))))))) ) (defun sublis-var (alist form) ; Call this function with alist = nil to put form into quote-normal form so ; that for example if form is (cons '1 '2) then '(1 . 2) is returned. The ; following two comments come from the nqthm version of this function. ; In REWRITE-WITH-LEMMAS we use this function with the nil alist ; to put form into quote normal form. Do not optimize this ; function for the nil alist. ; This is the only function in the theorem prover that we ; sometimes call with a "term" that is not in quote normal form. ; However, even this function requires that form be at least a ; pseudo-termp. ; We rely on quote-normal form for the return value, for example in calls of ; sublis-var in rewrite-with-lemma and in apply-top-hints-clause1. (declare (xargs :guard (and (symbol-alistp alist) (pseudo-term-listp (strip-cdrs alist)) (pseudo-termp form)))) (mv-let (changedp val) (sublis-var1 alist form) (declare (ignore changedp)) val)) (defun sublis-var-lst (alist l) (declare (xargs :guard (and (symbol-alistp alist) (pseudo-term-listp (strip-cdrs alist)) (pseudo-term-listp l)))) (mv-let (changedp val) (sublis-var1-lst alist l) (declare (ignore changedp)) val)) (defun subcor-var1 (vars terms var) (declare (xargs :guard (and (symbol-listp vars) (pseudo-term-listp terms) (equal (length vars) (length terms)) (variablep var)))) (cond ((endp vars) var) ((eq var (car vars)) (car terms)) (t (subcor-var1 (cdr vars) (cdr terms) var)))) (mutual-recursion (defun subcor-var (vars terms form) ; "Subcor" stands for "substitute corresponding elements". Vars and terms are ; in 1:1 correspondence, and we substitute terms for corresponding vars into ; form. This function was called sub-pair-var in nqthm. (declare (xargs :guard (and (symbol-listp vars) (pseudo-term-listp terms) (equal (length vars) (length terms)) (pseudo-termp form)))) (cond ((variablep form) (subcor-var1 vars terms form)) ((fquotep form) form) (t (cons-term (ffn-symb form) (subcor-var-lst vars terms (fargs form)))))) (defun subcor-var-lst (vars terms forms) (declare (xargs :guard (and (symbol-listp vars) (pseudo-term-listp terms) (equal (length vars) (length terms)) (pseudo-term-listp forms)))) (cond ((endp forms) nil) (t (cons (subcor-var vars terms (car forms)) (subcor-var-lst vars terms (cdr forms)))))) ) ; We now develop the code to take a translated term and "untranslate" ; it into something more pleasant to read. (defun car-cdr-nest1 (term ad-lst n) (cond ((or (int= n 4) (variablep term) (fquotep term) (and (not (eq (ffn-symb term) 'car)) (not (eq (ffn-symb term) 'cdr)))) (mv ad-lst term)) (t (car-cdr-nest1 (fargn term 1) (cons (if (eq (ffn-symb term) 'car) #\A #\D) ad-lst) (1+ n))))) (defun car-cdr-nest (term) (cond ((variablep term) (mv nil term)) ((fquotep term) (mv nil term)) ((or (eq (ffn-symb term) 'car) (eq (ffn-symb term) 'cdr)) (mv-let (ad-lst guts) (car-cdr-nest1 (fargn term 1) nil 1) (cond (ad-lst (mv (intern (coerce (cons #\C (cons (if (eq (ffn-symb term) 'car) #\A #\D) (revappend ad-lst '(#\R)))) 'string) "ACL2") guts)) (t (mv nil term))))) (t (mv nil nil)))) (defun collect-non-trivial-bindings (vars vals) (cond ((null vars) nil) ((eq (car vars) (car vals)) (collect-non-trivial-bindings (cdr vars) (cdr vals))) (t (cons (list (car vars) (car vals)) (collect-non-trivial-bindings (cdr vars) (cdr vals)))))) (defun untranslate-and (p q iff-flg) ; The following theorem illustrates the various cases: ; (thm (and (equal (and t q) q) ; (iff (and p t) p) ; (equal (and p (and q1 q2)) (and p q1 q2)))) ; Warning: Keep this in sync with and-addr. (cond ((eq p t) q) ((and iff-flg (eq q t)) p) ((and (consp q) (eq (car q) 'and)) (cons 'and (cons p (cdr q)))) (t (list 'and p q)))) (defun untranslate-or (p q) ; The following theorem illustrates the various cases: ; (thm (equal (or p (or q1 q2)) (or p q1 q2)))) (cond ((and (consp q) (eq (car q) 'or)) (cons 'or (cons p (cdr q)))) (t (list 'or p q)))) (defun case-length (key term) ; Key is either nil or a variablep symbol. Term is a term. We are ; imagining printing term as a case on key. How long is the case ; statement? Note that every term can be printed as (case key ; (otherwise term)) -- a case of length 1. If key is nil we choose it ; towards extending the case-length. (case-match term (('if ('equal key1 ('quote val)) & y) (cond ((and (if (null key) (variablep key1) (eq key key1)) (eqlablep val)) (1+ (case-length key1 y))) (t 1))) (('if ('eql key1 ('quote val)) & y) (cond ((and (if (null key) (variablep key1) (eq key key1)) (eqlablep val)) (1+ (case-length key1 y))) (t 1))) (('if ('member key1 ('quote val)) & y) (cond ((and (if (null key) (variablep key1) (eq key key1)) (eqlable-listp val)) (1+ (case-length key1 y))) (t 1))) (& 1))) ; And we do a similar thing for cond... (defun cond-length (term) (case-match term (('if & & z) (1+ (cond-length z))) (& 1))) ; In general the following list should be set to contain all the boot-strap ; functions that have boolean type set. (defconst *untranslate-boolean-primitives* '(equal)) (defun right-associated-args (fn term) ; Fn is a function symbol of two arguments. Term is a call of fn. ; E.g., fn might be 'BINARY-+ and term might be '(BINARY-+ A (BINARY-+ ; B C)). We return the list of arguments in the right-associated fn ; nest, e.g., '(A B C). (let ((arg2 (fargn term 2))) (cond ((and (nvariablep arg2) (not (fquotep arg2)) (eq fn (ffn-symb arg2))) (cons (fargn term 1) (right-associated-args fn arg2))) (t (fargs term))))) (defun dumb-negate-lit (term) (declare (xargs :guard (pseudo-termp term))) (cond ((variablep term) (fcons-term* 'not term)) ((fquotep term) (cond ((equal term *nil*) *t*) (t *nil*))) ((eq (ffn-symb term) 'not) (fargn term 1)) ((and (eq (ffn-symb term) 'equal) (or (equal (fargn term 2) *nil*) (equal (fargn term 1) *nil*))) (if (equal (fargn term 2) *nil*) (fargn term 1) (fargn term 2))) (t (fcons-term* 'not term)))) (defun dumb-negate-lit-lst (lst) (cond ((endp lst) nil) (t (cons (dumb-negate-lit (car lst)) (dumb-negate-lit-lst (cdr lst)))))) (mutual-recursion (defun term-stobjs-out-alist (vars args alist wrld) (if (endp vars) nil (let ((st (term-stobjs-out (car args) alist wrld)) (rest (term-stobjs-out-alist (cdr vars) (cdr args) alist wrld))) (if (and st (symbolp st)) (acons (car vars) st rest) rest)))) (defun term-stobjs-out (term alist wrld) ; Warning: This function currently has heuristic application only. We need to ; think harder about it if we are to rely on it for soundness. (cond ((variablep term) (or (cdr (assoc term alist)) (and (getprop term 'stobj nil 'current-acl2-world wrld) term))) ((fquotep term) nil) ((eq (ffn-symb term) 'return-last) (term-stobjs-out (car (last (fargs term))) alist wrld)) (t (let ((fn (ffn-symb term))) (cond ((member-eq fn '(nth mv-nth)) (let* ((arg1 (fargn term 1)) (n (and (quotep arg1) (cadr arg1)))) (and (integerp n) (<= 0 n) (let ((term-stobjs-out (term-stobjs-out (fargn term 2) alist wrld))) (and (consp term-stobjs-out) (nth n term-stobjs-out)))))) ((eq fn 'update-nth) (term-stobjs-out (fargn term 3) alist wrld)) ((flambdap fn) ; (fn args) = ((lambda vars body) args) (let ((vars (lambda-formals fn)) (body (lambda-body fn))) (term-stobjs-out body (term-stobjs-out-alist vars (fargs term) alist wrld) wrld))) ((eq fn 'if) (or (term-stobjs-out (fargn term 2) alist wrld) (term-stobjs-out (fargn term 3) alist wrld))) (t (let ((lst (stobjs-out fn wrld))) (cond ((and (consp lst) (null (cdr lst))) (car lst)) (t lst))))))))) ) (defun accessor-root (n term wrld) ; When term is a stobj name, say st, ac is the accessor function for st defined ; to return (nth n st), then untranslate maps (nth n st) to (nth *ac* st). ; The truth is that the 'accessor-names property of st is used to carry this ; out. Update-nth gets similar consideration. ; But what about (nth 0 (run st n)), where run returns a stobj st? Presumably ; we would like to print that as (nth *b* (run st n)) where b is the 0th field ; accessor function for st. We would also like to handle terms such as (nth 1 ; (mv-nth 3 (run st n))). These more general cases are likely to be important ; to making stobj proofs palatable. There is yet another consideration, which ; is that during proofs, the user may use variable names other than stobj names ; to refer to stobjs. For example, there may be a theorem of the form ; (... st st0 ...), which could generate a term (nth n st0) during a proof that ; the user would prefer to see printed as (nth *b* st0). ; The present function returns the field name to be returned in place of n when ; untranslating (nth n term) or (update-nth n val term). Wrld is, of course, ; an ACL2 world. (let ((st (term-stobjs-out term (table-alist 'nth-aliases-table wrld) wrld))) (and st (symbolp st) (let ((accessor-names (getprop st 'accessor-names nil 'current-acl2-world wrld))) (and accessor-names (< n (car (dimensions st accessor-names))) (aref1 st accessor-names n)))))) ; We define progn! here so that it is available before its call in redef+. But ; first we define observe-raw-mode-setting, a call of which is laid down by the ; use of f-put-global on 'acl2-raw-mode-p in the definition of progn!. #-acl2-loop-only (defvar *load-compiled-stack* nil) #-acl2-loop-only (defun observe-raw-mode-setting (v state) ; We are about to set state global 'acl2-raw-mode-p to v. We go through some ; lengths to maintain 'raw-include-book-dir-alist here and warn when the value ; of this variable is discarded as we leave raw mode. We are thus violating ; the semantics of put-global, by setting 'raw-include-book-dir-alist when only ; 'acl2-raw-mode-p is to be set -- but all bets are off when using raw mode, so ; this violation is tolerable. (let ((old-raw-mode (f-get-global 'acl2-raw-mode-p state)) (old-raw-include-book-dir-alist (f-get-global 'raw-include-book-dir-alist state)) (ctx 'observe-raw-mode-setting)) (cond ((or (iff v old-raw-mode) ; If we are executing a raw-Lisp include-book on behalf of include-book-fn, ; then a change in the status of raw mode is not important, as we will continue ; to maintain and use the value of state global 'raw-include-book-dir-alist as ; the value for include-book-dir-alist (see the function include-book-dir). ; This state global is bound by state-global-let* in load-compiled-book, which ; in turn is called by include-book under include-book-fn. *load-compiled-stack*) state) ((iff (eq old-raw-include-book-dir-alist :ignore) old-raw-mode) ; Clearly the two arguments of iff can't both be non-nil, since the value of ; 'raw-include-book-dir-alist is never nil in raw-mode. Can they both be nil? ; Assuming old-raw-mode is nil, then since (iff v old-raw-mode) is false, we ; are about to go into raw mode. Also, since we are not in the previous case, ; we are not currently under include-book-fn. But since we are currently not ; in raw mode and not under include-book-fn, we expect ; old-raw-include-book-dir-alist to be :ignore, as per the Essay on ; Include-book-dir-alist: "We maintain the invariant that :ignore is the value ; [of 'include-book-dir-alist] except when in raw-mode or during evaluation of ; include-book-fn." (prog2$ (er hard! ctx "Implementation error: Transitioning from ~x0 = ~x1 ~ and yet the value of state global variable ~x2 is ~ ~x3! Implementors should see the comment just above ~ this message in observe-raw-mode-setting." 'acl2-raw-mode-p old-raw-mode 'raw-include-book-dir-alist old-raw-include-book-dir-alist) state)) (t (let ((old-table-include-book-dir-alist (cdr (assoc-eq :include-book-dir-alist (table-alist 'acl2-defaults-table (w state)))))) (pprogn (cond ((and old-raw-mode ; The warning below is probably irrelevant for a context such that ; acl2-defaults-table will ultimately be discarded, because even without ; raw-mode we will be discarding include-book-dir-alist changes. (not (acl2-defaults-table-local-ctx-p state)) (not (equal old-raw-include-book-dir-alist old-table-include-book-dir-alist))) (warning$ ctx "Raw-mode" "The set of legal values for the :DIR argument of ~ include-book and ld appears to have changed when ~ ~x0 or ~x1 was executed in raw-mode. Changes ~ are being discarded as we exit raw-mode." 'add-include-book-dir 'delete-include-book-dir)) (t state)) (f-put-global 'raw-include-book-dir-alist (cond (old-raw-mode ; We are leaving raw-mode and are not under include-book-fn. :ignore) (t old-table-include-book-dir-alist)) state))))))) #+acl2-loop-only (defmacro progn! (&rest r) ":Doc-Section Events evaluate some forms, not necessarily ~il[events]~/ ~st[WARNING!] This event is intended for advanced users who, in essence, want to build extensions of ACL2. See ~pl[defttag], in particular, the ``WARNING'' there, and see the warning about ~il[stobj]s at the end of this documentation topic. ~c[Progn!] can be used like ~ilc[progn], even in ~il[books]. But unlike ~ilc[progn], ~c[progn!] does not require its constituent forms to be ~il[events] (~pl[embedded-event-form]), except that the first form cannot be a symbol unless it is ~c[:state-global-bindings] (advanced feature, described below). However, ~pl[make-event] for a ``Restriction to the Top Level'' that still applies under a call of ~c[progn!]. Because ~c[progn!] allows non-events, it differs from ~c[progn] in another important respect: ~c[progn!] is illegal unless there is an active ttag; ~pl[defttag]. See community book ~c[books/hacking/hacker.lisp] for two macros, ~c[with-raw-mode] and ~c[with-redef-allowed], each defined in terms of ~c[progn!], that allow arbitrary forms in contexts that would normally require legal embedded event forms.~/ Given a form ~c[(progn! form1 form2 ... formk)], ACL2 will evaluate each ~c[formi] in turn (for i from 1 to k). If a form returns more than one value (~pl[mv]) where the first value returned is not ~c[nil], then no later form will be evaluated and the result returned by the ~c[progn!] call will be ~c[(mv erp val state)] for some non-~c[nil] value ~c[erp], signifying an error (~pl[ld-error-triples]). Otherwise the evaluation is considered to have succeeded, and will continue with later forms. The value returned by a call of ~c[progn!] with no such error is of the form ~c[(mv nil v state)], where ~c[v] depends on the last form as follows. If the last form evaluates to a single value, then ~c[v] is that value, except if the value is a ~il[stobj], say ~c[ST], then ~c[v] is the symbol ~c[REPLACED-ST]. Otherwise the last form evaluates to some ~c[(mv nil x ...)], and ~c[v] is ~c[x] unless after the final form's evaluation we are in raw-mode (~pl[set-raw-mode]), in which case the ~c[progn!] call returns ~c[nil] (so that ACL2 can at least print the result ~-[] imagine Lisp returning a pathname object from a ~c[load] call, for example). The normal undoing mechanism does not generally apply to forms within a ~c[progn!] that are not legal ACL2 ~il[events] (~pl[embedded-event-form]). In particular, note that a non-~ilc[local] call of ~c[progn!] in an ~ilc[encapsulate] event will generally be evaluated twice: once on each pass. This fact is worth keeping in mind if you are using ~c[progn!] to change the state of the system; ask yourself if it is acceptable to apply that state-changing operation more than once. Please note that ~c[progn!] may differ from ~ilc[progn] in the following sense: definitions within a call of ~c[progn!] might not be compiled. For example, consider the following book. ~bv[] (in-package \"ACL2\") (defttag :test) (progn (defun f1 (x) x)) (progn! (defun f2 (x) x)) ~ev[] If the underlying Lisp is GCL 2.6.7, then after including this certified book (where the default certification took place, creating a compiled file), then ~c[f1] is a compiled function but ~c[f2] is not. For other Lisps supported by ACL2, both ~c[f1] and ~c[f2] are compiled, though we are not sure that every function under every call of ~c[progn!] would similarly be compiled. We now describe, for system hackers only, a sophisticated extension of ~c[progn!] not mentioned above: support for keyword argument ~c[:state-global-bindings]. If the first argument of ~c[progn!] is this keyword, then the second argument is treated as a list of bindings as expected by ACl2 system function ~ilc[state-global-let*]. Thus, in the ACL2 loop, ~bv[] (progn! :state-global-bindings bindings form1 form2 ... formk) ~ev[] is treated as follows: ~bv[] (progn! (state-global-let* bindings (progn! form1 form2 ... formk))) ~ev[] However, in raw Lisp the former is just: ~bv[] (progn form1 form2 ... formk) ~ev[] Thus, one should use the ~c[:state-global-bindings] argument with care, since the behavior in the ACL2 loop can differ from that in Common Lisp. The intention is that one bind only ~il[state] global variables that are relevant to evaluation of the forms within the ACL2 loop and are harmlessly ignored for evaluation of those forms in raw Lisp. Here is a typical sort of example, as ~il[state] global ~c[ld-redefinition-action] is not relevant to the evaluation of ~ilc[defun] in raw Lisp. ~bv[] (progn! (remove-untouchable 'ld-redefinition-action nil) (progn! :state-global-bindings ((ld-redefinition-action '(:doit . :overwrite))) (defun foo (x) (cons x x))) (push-untouchable 'ld-redefinition-action nil)) ~ev[] Finally, we point out a pitfall of ~c[progn!] related to ~il[stobj]s. The following book can cause a hard Lisp error, depending on the host Common Lisp, when certified with a non-~c[nil] value for ~c[compile-flg] (~pl[certify-book]). ~bv[] (in-package \"ACL2\") (defstobj st fld) (defttag :my-ttag) (progn! (update-fld 3 st)) ~ev[] The problem is that the ~il[stobj] variable ~c[st] is not known to raw Lisp. The compilation problem disappears if the last form above is replaced with the following two forms. ~bv[] (include-book \"hacking/hacker\" :dir :system) (with-raw-mode (update-fld 3 *the-live-st*)) ~ev[] ~/" (declare (xargs :guard (or (not (symbolp (car r))) (eq (car r) :state-global-bindings)))) (cond ((and (consp r) (eq (car r) :state-global-bindings)) `(state-global-let* ,(cadr r) (progn!-fn ',(cddr r) ',(cadr r) state))) (t `(progn!-fn ',r nil state)))) #-acl2-loop-only (defmacro progn! (&rest r) (let ((sym (gensym))) `(let ((state *the-live-state*) (,sym (f-get-global 'acl2-raw-mode-p *the-live-state*))) (declare (ignorable state)) ,@(cond ((eq (car r) :state-global-bindings) (cddr r)) (t r)) ; Notice that we don't need to use state-global-let* to protect against the ; possibility that the resetting of acl2-raw-mode-p never gets executed below. ; There are two reasons. First, ACL2's unwind protection mechanism doesn't ; work except inside the ACL2 loop, and although it may be that we always ; execute progn! forms from (ultimately) inside the ACL2 loop, it is preferable ; not to rely on that assumption. The other reason is that we assume that ; there are no errors during the execution of r in raw Lisp, since presumably ; the progn! form was already admitted in the loop. There are flaws in this ; assumption, of course: the user may abort or may be submitting the progn! in ; raw mode (in which case progn!-fn was not executed first). So we may want to ; revisit the resetting of acl2-raw-mode-p, but in that case we need to ; consider whether we need our solution to work outside the ACL2 loop, and if ; so, then whether it actually does work. (f-put-global 'acl2-raw-mode-p ,sym state) (value nil)))) ; The LD Specials ; The function LD will "bind" some state globals in the sense that it will ; smash their global values and then restore the old values upon completion. ; These state globals are called "LD specials". The LD read-eval-print loop ; will reference these globals. The user is permitted to set these globals ; with commands executed in LD -- with the understanding that the values are ; lost when LD is exited and the pop occurs. ; To make it easy to reference them and to ensure that they are set to legal ; values, we will define access and update functions for them. We define the ; functions here rather than in ld.lisp so that we may use them freely in our ; code. (defun ld-redefinition-action (state) ":Doc-Section Miscellaneous to allow redefinition without undoing~/ ~c[Ld-redefinition-action] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-redefinition-action state)] and the updater is ~c[(set-ld-redefinition-action val state)]. ~st[WARNING!] If ~c[ld-redefinition-action] is non-~c[nil] then ACL2 is liable to be made unsafe or unsound either by ill-considered definitions, or because redefining a macro or inlined function called in the body of a function, ~c[g], may not cause the new definition to be called by ~c[g]. The keyword command ~c[:]~ilc[redef] will set ~c[ld-redefinition-action] to a convenient setting allowing unsound redefinition. See below.~/ When ~c[ld-redefinition-action] is ~c[nil], redefinition is prohibited. In that case, an error message is printed upon any attempt to introduce a name that is already in use. There is one exception to this rule. It is permitted to redefine a function symbol in ~c[:]~ilc[program] mode to be a function symbol in ~c[:]~ilc[logic] mode provided the formals and body remain the same. This is the standard way a function ``comes into'' logical existence. Throughout the rest of this discussion we exclude from our meaning of ``redefinition'' the case in which a function in ~c[:]~ilc[program] mode is identically redefined in ~c[:]~ilc[logic] mode. At one time, ACL2 freely permitted the ~il[signature]-preserving redefinition of ~c[:]~ilc[program] mode functions but it no longer does. ~l[redefining-programs]. When ~c[ld-redefinition-action] is non-~c[nil], you are allowed to redefine a name that is already in use. ~st[The system may be rendered unsound] by such an act. It is important to understand how dangerous redefinition is. Suppose ~c[fn] is a function symbol that is called from within some other function, say ~c[g]. Suppose ~c[fn] is redefined so that its arity changes. Then the definition of ~c[g] is rendered syntactically ill-formed by the redefinition. This can be devastating since the entire ACL2 system assumes that terms in its database are well-formed. For example, if ACL2 executes ~c[g] by running the corresponding function in raw Common Lisp the redefinition of ~c[fn] may cause raw lisp to break in irreparable ways. As Lisp programmers we live with this all the time by following the simple rule: after changing the syntax of a function don't run any function that calls it via its old syntax. This rule also works in the context of the evaluation of ACL2 functions, but it is harder to follow in the context of ACL2 deductions, since it is hard to know whether the database contains a path leading the theorem prover from facts about one function to facts about another. Finally, of course, even if the database is still syntactically well-formed there is no assurance that all the rules stored in it are valid. For example, theorems proved about ~c[g] survive the redefinition of ~c[fn] but may have crucially depended on the properties of the old ~c[fn]. In summary, we repeat the warning: ~st[all bets are off if you set] ~c[ld-redefinition-action] to ~st[non]-~c[nil]. ACL2 provides some enforcement of the concern above, by disabling ~ilc[certify-book] if any ~il[world]-changing ~il[events] exist in the certification ~il[world] that were executed with a non-~c[nil] value of ~c['ld-redefinition-action]. (This value is checked at the end of each top-level command, but the value does not change during evaluation of embedded event forms; ~pl[embedded-event-form].) If at any point in a session you wish to see the list of all names that have been redefined, ~pl[redefined-names]. That said, we'll give you enough rope to hang yourself. When ~c[ld-redefinition-action] is non-~c[nil], it must be a pair, ~c[(a . b)]. The value of ~c[a] determines how the system interacts with you when a redefinition is submitted. The value of ~c[b] allows you to specify how the property list of the redefined name is to be ``renewed'' before the redefinition. There are several dimensions to the space of possibilities controlled by part a: Do you want to be queried each time you redefine a name, so you can confirm your intention? (We sometimes make typing mistakes or simply forget we have used that name already.) Do you want to see a warning stating that the name has been redefined? Do you want ACL2 system functions given special protection from possible redefinition? Below are the choices for part a:~bq[] ~c[:query] ~-[] every attempt to redefine a name will produce a query. The query will allow you to abort the redefinition or proceed. It will will also allow you to specify the part ~c[b] for this redefinition. ~c[:Query] is the recommended setting for users who wish to dabble in redefinition. ~c[:warn] ~-[] any user-defined function may be redefined but a post-redefinition warning is printed. The attempt to redefine a system name produces a query. If you are prototyping and testing a big system in ACL2 this is probably the desired setting for part ~c[a]. ~c[:doit] ~-[] any user-defined function may be redefined silently (without query or warning) but when an attempt is made to redefine a system function, a query is made. This setting is recommended when you start making massive changes to your prototyped system (and tire of even the warning messages issued by ~c[:warn]). ~eq[]In support of our own ACL2 systems ~il[programming] there are two other settings. We suggest ordinary users not use them.~bq[] ~c[:warn!] ~-[] every attempt to redefine a name produces a warning but no query. Since ACL2 system functions can be redefined this way, this setting should be used by the only-slightly-less-than supremely confident ACL2 system hacker. ~c[:doit!] ~-[] this setting allows any name to be redefined silently (without query or warnings). ACL2 system functions are fair game. This setting is reserved for the supremely confident ACL2 system hacker. (Actually, this setting is used when we are loading massively modified versions of the ACL2 source files.) ~eq[]Part ~c[b] of ~c[ld-redefinition-action] tells the system how to ``renew'' the property list of the name being redefined. There are two choices:~bq[] ~c[:erase] ~-[] erase all properties stored under the name, or ~c[:overwrite] ~-[] preserve existing properties and let the redefining overwrite them. ~eq[]It should be stressed that neither of these ~c[b] settings is guaranteed to result in an entirely satisfactory state of affairs after the redefinition. Roughly speaking, ~c[:erase] returns the property list of the name to the state it was in when the name was first introduced. Lemmas, type information, etc., stored under that name are lost. Is that what you wanted? Sometimes it is, as when the old definition is ``completely wrong.'' But other times the old definition was ``almost right'' in the sense that some of the work done with it is still (intended to be) valid. In that case, ~c[:overwrite] might be the correct ~c[b] setting. For example if ~c[fn] was a function and is being re-~ilc[defun]'d with the same ~il[signature], then the properties stored by the new ~ilc[defun] should overwrite those stored by the old ~ilc[defun] but the properties stored by ~ilc[defthm]s will be preserved. In addition, neither setting will cause ACL2 to erase properties stored under other symbols! Thus, if ~c[FOO] names a rewrite rule which rewrites a term beginning with the function symbol ~c[BAR] and you then redefine ~c[FOO] to rewrite a term beginning with the function symbol ~c[BAZ], then the old version of ~c[FOO] is still available (because the rule itself was added to the rewrite rules for ~c[BAR], whose property list was not cleared by redefining ~c[FOO]). The ~c[b] setting is only used as the default action when no query is made. If you choose a setting for part a that produces a query then you will have the opportunity, for each redefinition, to specify whether the property list is to be erased or overwritten. The keyword command ~c[:]~ilc[redef] sets ~c[ld-redefinition-action] to the pair ~c[(:query . :overwrite)]. Since the resulting query will give you the chance to specify ~c[:erase] instead of ~c[:overwrite], this setting is quite convenient. But when you are engaged in heavy-duty prototyping, you may wish to use a setting such as ~c[:warn] or even ~c[:doit]. For that you will have to invoke a form such as: ~bv[] (set-ld-redefinition-action '(:doit . :overwrite) state) . ~ev[] ~ilc[Encapsulate] causes somewhat odd interaction with the user if redefinition occurs within the encapsulation because the ~il[encapsulate]d event list is processed several times. For example, if the redefinition action causes a query and a non-local definition is actually a redefinition, then the query will be posed twice, once during each pass. C'est la vie. Finally, it should be stressed again that redefinition is dangerous because not all of the rules about a name are stored on the property list of the name. Thus, redefinition can render ill-formed terms stored elsewhere in the database or can preserve now-invalid rules. ~l[redundant-events], in particular the section ``Note About Unfortunate Redundancies,'' for more discussion of potential pitfalls of redefinition." (f-get-global 'ld-redefinition-action state)) (deflabel redefining-programs :doc ":Doc-Section ACL2::Programming an explanation of why we restrict redefinitions~/ ACL2 does not in general allow the redefinition of functions because logical inconsistency can result: previously stored theorems can be rendered invalid if the axioms defining the functions involved are changed. However, to permit prototyping of both ~c[:]~ilc[program] and ~c[:]~ilc[logic] mode systems, ACL2 permits redefinition if the user has accepted logical responsibility for the consequences by setting ~ilc[ld-redefinition-action] to an appropriate non-~c[nil] value. The refusal of ACL2 to support the unrestricted redefinition of ~c[:]~ilc[program] mode functions may appear somewhat capricious. After all, what are the logical consequences of changing a definition if no axioms are involved?~/ Three important points should be made before we discuss redefinition further. The first is that ACL2 does support redefinition (of both ~c[:]~ilc[program] and ~c[:]~ilc[logic] functions) when ~ilc[ld-redefinition-action] is non-~c[nil]. The second is that a ``redefinition'' that does not change the mode, formals, guards, type declarations, stobjs, or body of a function is considered redundant and is permitted even when ~ilc[ld-redefinition-action] is ~c[nil]. We recognize and permit redundant definitions because it is not uncommon for two distinct ~il[books] to share identical function definitions. When determining whether the body of a function is changed by a proposed redefinition, we actually compare the untranslated versions of the two bodies. ~l[term]. For example, redundancy is not recognized if the old body is ~c[(list a b)] and the new body is ~c[(cons a (cons b nil))]. We use the untranslated bodies because of the difficulty of translating the new body in the presence of the old syntactic information, given the possibility that the redefinition might attempt to change the ~il[signature] of the function, i.e., the number of formals, the number of results, or the position of single-threaded objects in either. The third important point is that a ``redefinition'' that preserves the formals, guard, types, stobjs, and body but changes the mode from ~c[:]~ilc[program] to ~c[:]~ilc[logic] is permitted even when ~ilc[ld-redefinition-action] is ~c[nil]. That is what ~ilc[verify-termination] does. This note addresses the temptation to allow redefinition of ~c[:]~ilc[program] functions in situations other than the three described above. Therefore, suppose ~ilc[ld-redefinition-action] is ~c[nil] and consider the cases. Case 1. Suppose the new definition attempts to change the formals or more generally the ~il[signature] of the function. Accepting such a redefinition would render ill-formed other ~c[:]~ilc[program] functions which call the redefined function. Subsequent attempts to evaluate those callers could arbitrarily damage the Common Lisp image. Thus, redefinition of ~c[:]~ilc[program] functions under these circumstances requires the user's active approval, as would be sought with ~ilc[ld-redefinition-action] ~c['(:query . :overwrite)]. Case 2. Suppose the new definition attempts to change the body (even though it preserves the ~il[signature]). At one time we believed this was acceptable and ACL2 supported the quiet redefinition of ~c[:]~ilc[program] mode functions in this circumstance. However, because such functions can be used in macros and redundancy checking is based on untranslated bodies, this turns out to be unsound! (Aside: Perhaps this is not an issue if the function takes ~ilc[state] or a user-defined ~il[stobj] argument; but we do not further consider this distinction.) Such redefinition is therefore now prohibited. We illustrate such an unsoundness below. Let ~c[foo-thm1.lisp] be a book with the following contents. ~bv[] (in-package \"ACL2\") (defun p1 (x) (declare (xargs :mode :program)) (list 'if x 't 'nil)) (defmacro p (x) (p1 x)) (defun foo (x) (p x)) (defthm foo-thm1 (iff (foo x) x) :rule-classes nil) ~ev[] Note that the macro form ~c[(p x)] translates to ~c[(if x t nil)]. The ~c[:]~ilc[program] function ~c[p1] is used to generate this translation. The function ~c[foo] is defined so that ~c[(foo x)] is ~c[(p x)] and a theorem about ~c[foo] is proved, namely, that ~c[(foo x)] is true iff ~c[x] is true. Now let ~c[foo-thm2.lisp] be a book with the following contents. ~bv[] (in-package \"ACL2\") (defun p1 (x) (declare (xargs :mode :program)) (list 'if x 'nil 't)) (defmacro p (x) (p1 x)) (defun foo (x) (p x)) (defthm foo-thm2 (iff (foo x) (not x)) :rule-classes nil) ~ev[] In this book, the ~c[:]~ilc[program] function ~c[p1] is defined so that ~c[(p x)] means just the negation of what it meant in the first book, namely, ~c[(if x nil t)]. The function ~c[foo] is defined identically ~-[] more precisely, the ~i[untranslated] body of ~c[foo] is identical in the two ~il[books], but because of the difference between the two versions of the ~c[:]~ilc[program] function ~c[p1] the axioms defining the two ~c[foo]s are different. In the second book we prove the theorem that ~c[(foo x)] is true iff ~c[x] is nil. Now consider what would happen if the ~il[signature]-preserving redefinition of ~c[:]~ilc[program] functions were permitted and these two ~il[books] were included. When the second book is included the redefinition of ~c[p1] would be permitted since the ~il[signature] is preserved and ~c[p1] is just a ~c[:]~ilc[program]. But then when the redefinition of ~c[foo] is processed it would be considered redundant and thus be permitted. The result would be a logic in which it was possible to prove that ~c[(foo x)] is equivalent to both ~c[x] and ~c[(not x)]. In particular, the following sequence leads to a proof of nil: ~bv[] (include-book \"foo-thm1\") (include-book \"foo-thm2\") (thm nil :hints ((\"Goal\" :use (foo-thm1 foo-thm2)))) ~ev[] It might be possible to loosen the restrictions on the redefinition of ~c[:]~ilc[program] functions by allowing ~il[signature]-preserving redefinition of ~c[:]~ilc[program] functions not involved in macro definitions. Alternatively, we could implement definition redundancy checking based on the translated bodies of functions (though that is quite problematic). Barring those two changes, we believe it is necessary simply to impose the same restrictions on the redefinition of ~c[:]~ilc[program] mode functions as we do on ~c[:]~ilc[logic] mode functions.") (defun chk-ld-redefinition-action (val ctx state) (cond ((or (null val) (and (consp val) (member-eq (car val) '(:query :warn :doit :warn! :doit!)) (member-eq (cdr val) '(:erase :overwrite)))) (value nil)) (t (er soft ctx *ld-special-error* 'ld-redefinition-action val)))) (defun set-ld-redefinition-action (val state) (er-progn (chk-ld-redefinition-action val 'set-ld-redefinition-action state) (pprogn (f-put-global 'ld-redefinition-action val state) (value val)))) (defmacro redef nil ":Doc-Section Miscellaneous a common way to set ~il[ld-redefinition-action]~/ ~bv[] Example and General Form: ACL2 !>:redef ~ev[] This command sets ~ilc[ld-redefinition-action] to ~c['(:query . :overwrite)].~/ This command allows redefinition of functions and other ~il[events] without undoing, but with a query that requires the user to acknowledge that the redefinition is intentional. To avoid that query, ~pl[redef!] or for more options, ~pl[ld-redefinition-action]." '(set-ld-redefinition-action '(:query . :overwrite) state)) (defmacro redef! nil ":Doc-Section Miscellaneous a common way to set ~il[ld-redefinition-action]~/ ~bv[] Example and General Form: ACL2 !>:redef! ~ev[] This command sets ~ilc[ld-redefinition-action] to ~c['(:warn! . :overwrite)].~/ This command allows redefinition of functions and other ~il[events] without undoing, but with a warning. The command ~c[:]~ilc[redef] is similar, but queries the user first before doing the redefinition. For more related options, ~pl[ld-redefinition-action]." '(set-ld-redefinition-action '(:warn! . :overwrite) state)) (defmacro redef+ nil ; WARNING: Keep this in sync with redef-. ":Doc-Section Miscellaneous system hacker's redefinition ~il[command]~/ ~bv[] Example and General Form: ACL2 !>:redef+ ACL2 p!> ~ev[] This ~il[command] is intended only for system hackers, not typical users. It sets ~ilc[ld-redefinition-action] to ~c['(:warn! . :overwrite)], sets the default ~il[defun-mode] to ~c[:]~ilc[program], and invokes ~ilc[set-state-ok] with value ~c[t]. It also introduces ~c[(defttag :redef+)], so that redefinition of system functions will be permitted; ~pl[defttag]. Finally, it removes as untouchable (~pl[push-untouchable]) all variables and functions.~/ WARNING: If the form ~c[(redef+)] is used in a book, then including the book can leave you in a state in which dangerous actions are allowed, specifically: redefinition, and access to functions and variables normally prohibited because they are untouchable. To avoid this problem, insert the form ~c[(]~ilc[redef-]~c[)] into your book after ~c[(redef+)]. To see the code for ~c[redef+], evaluate ~c[:trans1 (redef+)]. This ~il[command] is intended for those who are modifying ACL2 source code definitions. Thus, note that even system functions can be redefined with a mere warning. Be careful!" #-acl2-loop-only nil #+acl2-loop-only `(with-output :off (summary event) (progn (defttag :redef+) (progn! (set-ld-redefinition-action '(:warn! . :overwrite) state) (program) (set-temp-touchable-vars t state) (set-temp-touchable-fns t state) (f-put-global 'redundant-with-raw-code-okp t state) (set-state-ok t))))) (defmacro redef- nil ; WARNING: Keep this in sync with redef+. ":Doc-Section Miscellaneous turn off system hacker's redefinition ~il[command]~/ ~bv[] Example and General Form: ACL2 !>:redef- ACL2 p!> ~ev[] This macro, for system hackers only, is a convenient way to reverse the effects of ~c[:redef+]. ~l[redef+]. We do not guarantee that ~c[:redef-] will restore everything one might expect to its state before the earlier ~c[:redef+]. To see exactly what ~c[:redef-] does, look at its code, for example by evaluating ~c[:trans1 (redef-)]~/~/" #-acl2-loop-only nil #+acl2-loop-only `(with-output :off (summary event) (progn (redef+) ; to allow forms below (progn! (f-put-global 'redundant-with-raw-code-okp nil state) (set-temp-touchable-vars nil state) (set-temp-touchable-fns nil state) (defttag nil) (logic) (set-ld-redefinition-action nil state) (set-state-ok nil))))) (defun chk-current-package (val ctx state) (cond ((find-non-hidden-package-entry val (known-package-alist state)) (value nil)) (t (er soft ctx *ld-special-error* 'current-package val)))) (defun set-current-package (val state) ; This function is equivalent to in-package-fn except for the ; error message generated. (er-progn (chk-current-package val 'set-current-package state) (pprogn (f-put-global 'current-package val state) (value val)))) (defun standard-oi (state) ":Doc-Section ACL2::ACL2-built-ins the standard object input ``channel''~/ ~c[Standard-oi] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(standard-oi state)] and the updater is ~c[(set-standard-oi val state)]. ~c[Standard-oi] must be an open object input channel, a true list of objects, or a list of objects whose last ~ilc[cdr] is an open object input channel. It is from this source that ~ilc[ld] takes the input forms to process. When ~ilc[ld] is called, if the value specified for ~c[standard-oi] is a string or a list of objects whose last ~ilc[cdr] is a string, then ~ilc[ld] treats the string as a file name and opens an object input channel from that file, where the connected book directory (~pl[cbd]) is used to resolve relative pathnames. The channel opened by ~ilc[ld] is closed by ~ilc[ld] upon termination.~/ ``Standard-oi'' stands for ``standard object input.'' The read-eval-print loop in ~ilc[ld] reads the objects in ~c[standard-oi] and treats them as forms to be evaluated. The initial value of ~c[standard-oi] is the same as the value of ~ilc[*standard-oi*] (~pl[*standard-oi*])." (f-get-global 'standard-oi state)) (defun read-standard-oi (state) ; We let LD take a true-listp as the "input file" and so we here implement ; the generalized version of (read-object (standard-oi state) state). (let ((standard-oi (standard-oi state))) (cond ((consp standard-oi) (let ((state (f-put-global 'standard-oi (cdr standard-oi) state))) (mv nil (car standard-oi) state))) ((null standard-oi) (mv t nil state)) (t (read-object standard-oi state))))) (defun chk-standard-oi (val ctx state) (cond ((and (symbolp val) (open-input-channel-p val :object state)) (value nil)) ((true-listp val) (value nil)) ((and (consp val) (symbolp (cdr (last val))) (open-input-channel-p (cdr (last val)) :object state)) (value nil)) (t (er soft ctx *ld-special-error* 'standard-oi val)))) (defun set-standard-oi (val state) (er-progn (chk-standard-oi val 'set-standard-oi state) (pprogn (f-put-global 'standard-oi val state) (value val)))) (defun standard-co (state) ":Doc-Section ACL2::ACL2-built-ins the character output channel to which ~ilc[ld] prints~/ ~c[Standard-co] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(standard-co state)] and the updater is ~c[(set-standard-co val state)]. ~c[Standard-co] must be an open character output channel. It is to this channel that ~ilc[ld] prints the ~il[prompt], the form to be evaluated, and the results. The event ~il[command]s such as ~ilc[defun], ~ilc[defthm], etc., which print extensive commentary do not print to ~c[standard-co] but rather to a different channel, ~ilc[proofs-co], so that you may redirect this commentary while still interacting via ~c[standard-co]. ~l[proofs-co].~/ ``Standard-co'' stands for ``standard character output.'' The initial value of ~c[standard-co] is the same as the value of ~ilc[*standard-co*] (~pl[*standard-co*])." (f-get-global 'standard-co state)) (defun chk-standard-co (val ctx state) (cond ((and (symbolp val) (open-output-channel-p val :character state)) (value nil)) (t (er soft ctx *ld-special-error* 'standard-co val)))) (defun set-standard-co (val state) (er-progn (chk-standard-co val 'set-standard-co state) (pprogn (f-put-global 'standard-co val state) (value val)))) (defun proofs-co (state) ":Doc-Section ACL2::ACL2-built-ins the proofs character output channel~/ ~c[Proofs-co] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(proofs-co state)] and the updater is ~c[(set-proofs-co val state)]. ~c[Proofs-co] must be an open character output channel. It is to this channel that ~ilc[defun], ~ilc[defthm], and the other event ~il[command]s print their commentary.~/ ``Proofs-co'' stands for ``proofs character output.'' The initial value of ~c[proofs-co] is the same as the value of ~ilc[*standard-co*] (~pl[*standard-co*])." (f-get-global 'proofs-co state)) (defun chk-proofs-co (val ctx state) (cond ((and (symbolp val) (open-output-channel-p val :character state)) (value nil)) (t (er soft ctx *ld-special-error* 'proofs-co val)))) (defun set-proofs-co (val state) (er-progn (chk-proofs-co val 'set-proofs-co state) (pprogn (f-put-global 'proofs-co val state) (value val)))) (deflabel prompt :doc ":Doc-Section Miscellaneous the prompt printed by ~ilc[ld]~/~/ The prompt printed by ACL2 conveys information about various ``modes.'' ~l[default-print-prompt] and ~pl[ld-prompt] for details. The prompt during raw Lisp breaks is, with most Common Lisp implementations, adjusted by ACL2 to include the string ~c[\"[RAW LISP]\"], in order to reminder users not to submit ACL2 forms there; ~pl[breaks]. For Lisps that seem to use the same code for printing prompts at the top-level as in ~il[breaks], the top-level prompt is similarly adjusted. For Lisps with the above prompt adjustment, The following forms may be executed in raw Lisp (i.e., after typing ~c[:q]). ~bv[] (install-new-raw-prompt) ; install prompt with [RAW LISP] as described above (install-old-raw-prompt) ; revert to original prompt from host Common Lisp ~ev[]") (defun ld-prompt (state) ":Doc-Section Miscellaneous determines the ~il[prompt] printed by ~ilc[ld]~/ ~c[Ld-prompt] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-prompt state)] and the updater is ~c[(set-ld-prompt val state)]. ~c[Ld-prompt] must be either ~c[nil], ~c[t], or a function symbol that, when given an open output character channel and ~il[state], prints the desired ~il[prompt] to the channel and returns two values: the number of ~il[characters] printed and the ~il[state]. The initial value of ~c[ld-prompt] is ~c[t].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-prompt] is one of them. ~c[Ld-prompt] determines whether ~ilc[ld] prints a ~il[prompt] before reading the next form from ~ilc[standard-oi]. If ~c[ld-prompt] is ~c[nil], ~ilc[ld] prints no ~il[prompt]. If ~c[ld-prompt] is ~c[t], the default ~il[prompt] printer is used, which displays information that includes the current package, default ~il[defun-mode], ~il[guard] checking status (on or off), and ~ilc[ld-skip-proofsp]; ~pl[default-print-prompt]. If ~c[ld-prompt] is neither ~c[nil] nor ~c[t], then it should be a function name, ~c[fn], such that ~c[(fn channel state)] will print the desired ~il[prompt] to ~c[channel] in ~ilc[state] and return ~c[(mv col state)], where ~c[col] is the number of ~il[characters] output (on the last line output). You may define your own ~il[prompt] printing function. If you supply an inappropriate ~il[prompt] function, i.e., one that causes an error or does not return the correct number and type of results, the following odd ~il[prompt] will be printed instead: ~bv[] Bad Prompt See :DOC ld-prompt> ~ev[] which will lead you to this message. You should either call ~ilc[ld] appropriately next time or assign an appropriate value to ~c[ld-prompt]." (f-get-global 'ld-prompt state)) (defun chk-ld-prompt (val ctx state) (cond ((or (null val) (eq val t) (let ((wrld (w state))) (and (symbolp val) (equal (arity val wrld) 2) (equal (stobjs-in val wrld) '(nil state)) (equal (stobjs-out val wrld) '(nil state))))) (value nil)) (t (er soft ctx *ld-special-error* 'ld-prompt val)))) (defun set-ld-prompt (val state) (er-progn (chk-ld-prompt val 'set-ld-prompt state) (pprogn (f-put-global 'ld-prompt val state) (value val)))) (defun ld-keyword-aliases (state) ":Doc-Section switches-parameters-and-modes abbreviation of some keyword commands~/ ~bv[] Examples: (set-ld-keyword-aliases '((:q 0 q-fn) (:e 0 exit-acl2-macro)) state) (ld-keyword-aliases state) ; current value of the ld-keyword-aliases table ~ev[] ~c[Ld-keyword-aliases] is the name of a ACL2 table (~pl[table]) and also the name of a function of ~c[state] that returns the value of this table. That value must be an alist, each element of which is of the form ~c[(:keyword n fn)], where ~c[:keyword] is a keyword, ~c[n] is a nonnegative integer, and ~c[fn] is a function symbol of arity ~c[n], a macro symbol, or a ~c[lambda] expression of arity ~c[n]. When ~c[keyword] is typed as an ~ilc[ld] command, ~c[n] more forms are read, ~c[x1, ..., xn], and the form ~c[(fn 'x1 ... 'xn)] is then evaluated. The initial value of the ~c[ld-keyword-aliases] ~il[table] is ~c[nil]. ACL2 provides functions to modify the ~c[ld-keyword-aliases] table, as follows. ~bq[] ~c[(Set-ld-keyword-aliases val state)]: sets the table to ~c[val], which must be a legal alist as described above. This is an event that may go into a book (~pl[events]), but its effect will be ~il[local] to that book. ~c[Set-ld-keyword-aliases!] is the same as ~c[set-ld-keyword-aliases], except that its effect is not ~il[local]. Indeed, the form ~c[(set-ld-keyword-aliases val state)] is equivalent to the form ~c[(local (set-ld-keyword-aliases! val state)]. ~c[(Add-ld-keyword-alias key val state)]: modifies the table by binding the keyword ~c[key] to ~c[val], which must be a legal value as described above. This is an event that may go into a book (~pl[events]), but its effect will be ~il[local] to that book. ~c[Add-ld-keyword-alias!] is the same as ~c[add-ld-keyword-alias], except that its effect is not ~il[local]. Indeed, the form ~c[(add-ld-keyword-alias key val state)] is equivalent to the form ~c[(local (add-ld-keyword-alias! key val state)]. ~eq[] Consider the first example above: ~bv[] (set-ld-keyword-aliases '((:q 0 q-fn) (:e 0 exit-acl2-macro)) state) ~ev[] With this event, ~c[:]~ilc[q] is redefined to have the effect of executing ~c[(q-fn)], so for example if you have defined ~c[q-fn] with ~bv[] (defmacro q-fn () '(er soft 'q \"You un-bound :q and now we have a soft error.\")) ~ev[] then ~c[:]~ilc[q] will cause an error, and if you have defined ~bv[] (defmacro exit-acl2-macro () '(exit-ld state)) ~ev[] then ~c[:e] will cause the effect (it so happens) that ~c[:]~ilc[q] normally has. If you prefer ~c[:e] to ~c[:]~ilc[q] for exiting the ACL2 loop, you might even want to put such definitions of ~c[q-fn] and ~c[exit-acl2-macro] together with the ~c[set-ld-keyword-aliases] form above into your ~c[\"acl2-customization.lsp\"] file; ~pl[acl2-customization].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-keyword-aliases] is one of them. ~c[Ld-keyword-aliases] affects how keyword commands are parsed. Generally speaking, ~ilc[ld]'s command interpreter reads ``~c[:fn x1 ... xn]'' as ``~c[(fn 'x1 ... 'xn)]'' when ~c[:fn] is a keyword and ~c[fn] is the name of an ~c[n]-ary function; ~pl[keyword-commands]. But this parse is overridden, as described above, for the keywords bound in the ~c[ld-keyword-aliases] ~il[table]." (table-alist 'ld-keyword-aliases (w state))) (defun ld-keyword-aliasesp (key val wrld) (and (keywordp key) (true-listp val) (int= (length val) 2) (let ((n (car val)) (fn (cadr val))) (and (natp n) (cond ((and (symbolp fn) (function-symbolp fn wrld)) (equal (arity fn wrld) n)) ((and (symbolp fn) (getprop fn 'macro-body nil 'current-acl2-world wrld)) t) (t (and (true-listp fn) (>= (length fn) 3) (<= (length fn) 4) (eq (car fn) 'lambda) (arglistp (cadr fn)) (int= (length (cadr fn)) n)))))))) (table ld-keyword-aliases nil nil :guard (ld-keyword-aliasesp key val world)) #+acl2-loop-only (defmacro add-ld-keyword-alias! (key val) `(state-global-let* ((inhibit-output-lst (list* 'summary 'event (@ inhibit-output-lst)))) (progn (table ld-keyword-aliases ,key ,val) (table ld-keyword-aliases)))) #-acl2-loop-only (defmacro add-ld-keyword-alias! (key val) (declare (ignore key val)) nil) (defmacro add-ld-keyword-alias (key val) `(local (add-ld-keyword-alias! ,key ,val))) #+acl2-loop-only (defmacro set-ld-keyword-aliases! (alist) `(state-global-let* ((inhibit-output-lst (list* 'summary 'event (@ inhibit-output-lst)))) (progn (table ld-keyword-aliases nil ',alist :clear) (table ld-keyword-aliases)))) #-acl2-loop-only (defmacro set-ld-keyword-aliases! (alist) (declare (ignore alist)) nil) (defmacro set-ld-keyword-aliases (alist &optional state) ; We add state (optionally) just for backwards compatibility through ; Version_6.2. We might eliminate it after Version_6.3. (declare (ignore state)) `(local (set-ld-keyword-aliases! ,alist))) (defun ld-missing-input-ok (state) ":Doc-Section Miscellaneous determines which forms ~ilc[ld] evaluates~/ ~c[ld-missing-input-ok] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-missing-input-ok state)] and the updater is ~c[(set-ld-missing-input-ok val state)]. ~c[ld-missing-input-ok] must be either ~c[nil], ~c[t], or ~c[:warn]. The initial value of ~c[ld-missing-input-ok] is ~c[nil]. The general-purpose ACL2 read-eval-print loop, ~ilc[ld], is controlled by various flags that control its behavior, and ~c[ld-missing-input-ok] is one of them. In brief, the first argument of ~c[ld] can indicate a file from which to read input. If the file does not exist, it is an error by default, but ~c[ld] becomes essentially a no-op if ~c[t] or ~c[:warn] is supplied for ~c[:ld-missing-input-ok], where ~c[:warn] prints a warning. Also ~pl[ld].~/~/" (f-get-global 'ld-missing-input-ok state)) (defun msgp (x) (declare (xargs :guard t)) (or (stringp x) (and (true-listp x) (stringp (car x))))) (defun chk-ld-missing-input-ok (val ctx state) (cond ((or (member-eq val '(t nil :warn)) (msgp val) ; admittedly, a weak check ) (value nil)) (t (er soft ctx *ld-special-error* 'ld-missing-input-ok val)))) (defun set-ld-missing-input-ok (val state) (er-progn (chk-ld-missing-input-ok val 'set-ld-missing-input-ok state) (pprogn (f-put-global 'ld-missing-input-ok val state) (value val)))) (defun ld-pre-eval-filter (state) ":Doc-Section Miscellaneous determines which forms ~ilc[ld] evaluates~/ ~c[Ld-pre-eval-filter] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-pre-eval-filter state)] and the updater is ~c[(set-ld-pre-eval-filter val state)]. ~c[Ld-pre-eval-filter] must be either ~c[:all], ~c[:query], or a new name that could be defined (e.g., by ~ilc[defun] or ~ilc[defconst]). The initial value of ~c[ld-pre-eval-filter] is ~c[:all].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-pre-eval-filter] is one of them. If the filter is ~c[:all], then every form read is evaluated. If the filter is ~c[:query], then after a form is read it is printed to ~ilc[standard-co] and the user is asked if the form is to be evaluated or skipped. If the filter is a new name, then all forms are evaluated until that name is introduced, at which time ~ilc[ld] terminates normally. The ~c[:all] filter is, of course, the normal one. ~c[:Query] is useful if you want to replay selected the ~il[command]s in some file. The new name filter is used if you wish to replay all the ~il[command]s in a file up through the introduction of the given one." (f-get-global 'ld-pre-eval-filter state)) (defun new-namep (name wrld) ; We determine if name has properties on world wrld. Once upon a time ; this was equivalent to just (not (assoc-eq name wrld)). However, we ; have decided to ignore certain properties: ; * 'global-value - names with this property are just global variables ; in our code; we permit the user to define functions ; with those names. ; * 'table-alist - names with this property are being used as tables ; * 'table-guard - names with this property are being used as tables ; WARNING: If this list of properties is changed, change renew-name/erase. ; Additionally, if name has a non-nil 'redefined property name is treated as ; new if all of its other properties are as set by renew-name/erase or ; renew-name/overwrite, as appropriate. The 'redefined property is set by ; renew-name to be (renewal-mode . old-sig) where renewal-mode is :erase, ; :overwrite, or :reclassifying-overwrite. (let ((redefined (getprop name 'redefined nil 'current-acl2-world wrld))) (cond ((and (consp redefined) (eq (car redefined) :erase)) ; If we erased the properties of name and they are still erased, then we ; will find no non-nil properties except for those left by ; renew-name/erase and renew-name. (not (has-propsp name '(REDEFINED GLOBAL-VALUE TABLE-ALIST TABLE-GUARD) 'current-acl2-world wrld nil))) ((and (consp redefined) (or (eq (car redefined) :overwrite) (eq (car redefined) :reclassifying-overwrite))) ; We make a check analogous to that for erasure, allowing arbitrary non-nil ; values on all the properties untouched by renew-name/overwrite and insisting ; that all the properties erased by that function are still gone. Technically ; we should confirm that the lemmas property has been cleansed of all ; introductory rules, but in fact we allow it to have an arbitrary non-nil ; value. This is correct because if 'formals is gone then we cleansed 'lemmas ; and nothing could have been put back there since name is not yet a function ; symbol again. (not (has-propsp name '(REDEFINED LEMMAS GLOBAL-VALUE LABEL LINEAR-LEMMAS FORWARD-CHAINING-RULES ELIMINATE-DESTRUCTORS-RULE COARSENINGS CONGRUENCES INDUCTION-RULES THEOREM UNTRANSLATED-THEOREM CLASSES CONST THEORY TABLE-GUARD TABLE-ALIST MACRO-BODY MACRO-ARGS PREDEFINED TAU-PAIR POS-IMPLICANTS NEG-IMPLICANTS UNEVALABLE-BUT-KNOWN SIGNATURE-RULES-FORM-1 SIGNATURE-RULES-FORM-2 BIG-SWITCH TAU-BOUNDERS-FORM-1 TAU-BOUNDERS-FORM-2 ) 'current-acl2-world wrld nil))) (t (not (has-propsp name '(GLOBAL-VALUE TABLE-ALIST TABLE-GUARD) 'current-acl2-world wrld nil)))))) (defun chk-ld-pre-eval-filter (val ctx state) (cond ((or (member-eq val '(:all :query)) (and (symbolp val) (not (keywordp val)) (not (equal (symbol-package-name val) *main-lisp-package-name*)) (new-namep val (w state)))) (value nil)) (t (er soft ctx *ld-special-error* 'ld-pre-eval-filter val)))) (defun set-ld-pre-eval-filter (val state) (er-progn (chk-ld-pre-eval-filter val 'set-ld-pre-eval-filter state) (pprogn (f-put-global 'ld-pre-eval-filter val state) (value val)))) (defun ld-pre-eval-print (state) ":Doc-Section Miscellaneous determines whether ~ilc[ld] prints the forms to be ~c[eval]'d~/ ~c[Ld-pre-eval-print] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-pre-eval-print state)] and the updater is ~c[(set-ld-pre-eval-print val state)]. ~c[Ld-pre-eval-print] must be either ~c[t], ~c[nil], or ~c[:never]. The initial value of ~c[ld-pre-eval-print] is ~c[nil].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-pre-eval-print] is one of them. If this global variable is ~c[t], then before evaluating the form just read from ~ilc[standard-oi], ~ilc[ld] prints the form to ~ilc[standard-co]. If the variable is ~c[nil], no such printing occurs. The ~c[t] option is useful if you are reading from a file of ~il[command]s and wish to assemble a complete script of the session in ~ilc[standard-co]. The value ~c[:never] of ~c[ld-pre-eval-print] is rarely used. During the evaluation of ~ilc[encapsulate] and of ~ilc[certify-book] forms, subsidiary events are normally printed, even if ~c[ld-pre-eval-print] is ~c[nil]. Thus for example, when the user submits an ~ilc[encapsulate] form, all subsidiary events are generally printed even in the default situation where ~c[ld-pre-eval-print] is ~c[nil]. But occasionally one may want to suppress such printing. In that case, ~c[ld-pre-eval-print] should be set to ~c[:never]. As described elsewhere (~pl[set-inhibit-output-lst]), another way to suppress such printing is to execute ~c[(set-inhibit-output-lst lst)] where ~c[lst] evaluates to a list including ~c['prove] and ~c['event]." (f-get-global 'ld-pre-eval-print state)) (defun chk-ld-pre-eval-print (val ctx state) (cond ((member-eq val '(nil t :never)) (value nil)) (t (er soft ctx *ld-special-error* 'ld-pre-eval-print val)))) (defun set-ld-pre-eval-print (val state) (er-progn (chk-ld-pre-eval-print val 'set-ld-pre-eval-print state) (pprogn (f-put-global 'ld-pre-eval-print val state) (value val)))) (defun ld-post-eval-print (state) ":Doc-Section Miscellaneous determines whether and how ~ilc[ld] prints the result of evaluation~/ ~c[Ld-post-eval-print] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-post-eval-print state)] and the updater is ~c[(set-ld-post-eval-print val state)]. ~c[Ld-post-eval-print] must be either ~c[t], ~c[nil], or ~c[:command-conventions]. The initial value of ~c[ld-post-eval-print] is ~c[:command-conventions].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-post-eval-print] is one of them. If this global variable is ~c[t], ~ilc[ld] prints the result. In the case of a form that produces a multiple value, ~ilc[ld] prints the list containing all the values (which, logically speaking, is what the form returned). If ~c[ld-post-eval-print] is ~c[nil], ~ilc[ld] does not print the values. This is most useful when ~ilc[ld] is used to load a previously processed file. Finally, if ~c[ld-post-eval-print] is ~c[:command-conventions] and also ~c[ld-error-triples] is ~c[t], then ~ilc[ld] prints the result but treats ``error triples'' specially. An ``error triple'' (~pl[error-triples]) is a result, ~c[(mv erp val state)], that consists of two ordinary values and ~ilc[state]. Many ACL2 functions use such triples to signal errors. The convention is that if ~c[erp] (the first value) is ~c[nil], then the function is returning ~c[val] (the second value) as its conventional single result and possibly side-effecting ~il[state] (as with some output). If ~c[erp] is ~c[t], then an error has been caused, ~c[val] is irrelevant and the error message has been printed in the returned ~il[state]. Example ACL2 functions that follow this convention include ~ilc[defun] and ~ilc[in-package]. If such ``error producing'' functions are evaluated while ~c[ld-post-eval-print] is set to ~c[t], then you would see them producing lists of length 3. This is disconcerting to users accustomed to Common Lisp (where these functions produce single results but sometimes cause errors or side-effect ~il[state]). For more information about error triples, ~pl[programming-with-state]. When ~c[ld-post-eval-print] is ~c[:command-conventions] and a form produces an error triple ~c[(mv erp val state)] as its value, ~ilc[ld] prints nothing if ~c[erp] is non-~c[nil] and otherwise ~ilc[ld] prints just ~c[val]. Because it is a misrepresentation to suggest that just one result was returned, ~ilc[ld] prints the value of the global variable ~c['triple-print-prefix] before printing ~c[val]. ~c['triple-print-prefix] is initially ~c[\" \"], which means that when non-erroneous error triples are being abbreviated to ~c[val], ~c[val] appears one space off the left margin instead of on the margin. In addition, when ~c[ld-post-eval-print] is ~c[:command-conventions] and the value component of an error triple is the keyword ~c[:invisible] then ~ilc[ld] prints nothing. This is the way certain commands (e.g., ~c[:]~ilc[pc]) appear to return no value. By printing nothing when an error has been signalled, ~ilc[ld] makes it appear that the error (whose message has already appeared in ~il[state]) has ``thrown'' the computation back to load without returning a value. By printing just ~c[val] otherwise, we suppress the fact that ~il[state] has possibly been changed." (f-get-global 'ld-post-eval-print state)) (defun chk-ld-post-eval-print (val ctx state) (cond ((member-eq val '(nil t :command-conventions)) (value nil)) (t (er soft ctx *ld-special-error* 'ld-post-eval-print val)))) (defun set-ld-post-eval-print (val state) (er-progn (chk-ld-post-eval-print val 'set-ld-post-eval-print state) (pprogn (f-put-global 'ld-post-eval-print val state) (value val)))) (defun ld-error-triples (state) ":Doc-Section Miscellaneous determines whether a form caused an error during ~ilc[ld]~/ ~c[Ld-error-triples] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-error-triples state)] and the updater is ~c[(set-ld-error-triples val state)]. ~c[Ld-error-triples] must be either ~c[t] or ~c[nil]. The initial value of ~c[ld-error-triples] is ~c[t].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-error-triples] is one of them. If this variable has the value ~c[t] then when a form evaluates to an error triple ~c[(mv erp val state)] (~pl[error-triples]) such that ~c[erp] is non-~c[nil], then an error is deemed to have occurred. When an error occurs in evaluating a form, ~ilc[ld] rolls back the ACL2 ~il[world] to the configuration it had at the conclusion of the last error-free form. Then ~ilc[ld] takes the action determined by ~ilc[ld-error-action]." (f-get-global 'ld-error-triples state)) (defun chk-ld-error-triples (val ctx state) (cond ((member-eq val '(nil t)) (value nil)) (t (er soft ctx *ld-special-error* 'ld-error-triples val)))) (defun set-ld-error-triples (val state) (er-progn (chk-ld-error-triples val 'set-ld-error-triples state) (pprogn (f-put-global 'ld-error-triples val state) (value val)))) (defun ld-error-action (state) ":Doc-Section Miscellaneous determines ~ilc[ld]'s response to an error~/ ~c[Ld-error-action] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-error-action state)] and the updater is ~c[(set-ld-error-action val state)]. ~c[Ld-error-action] must be ~c[:continue], ~c[:return], ~c[:return!], or ~c[:error]. The initial value of ~c[ld-error-action] is ~c[:continue], which means that the top-level ACL2 ~il[command] loop will not exit when an error is caused by ~c[user-typein]. But the default value for ~c[ld-error-action] on calls of ~ilc[ld] is ~c[:return!].~/ The general-purpose ACL2 read-eval-print loop, ~ilc[ld], reads forms from ~ilc[standard-oi], evaluates them and prints the result to ~ilc[standard-co]. However, there are various flags that control ~ilc[ld]'s behavior and ~c[ld-error-action] is one of them. Suppose that ~ilc[ld-error-triples] is ~c[t] and a form evaluates to an error triple ~c[(mv erp val state)]; ~pl[error-triples]. If the ``error component'', ~c[erp], is non-~c[nil], then an error is said to have occurred. If an error occurs, ~ilc[ld]'s action depends on ~c[ld-error-action]. If it is ~c[:continue], ~ilc[ld] just continues processing the forms in ~ilc[standard-oi]. If it is ~c[:return] or ~c[:return!], ~ilc[ld] stops and returns as though it had emptied the channel. If it is ~c[:error], ~ilc[ld] stops and returns, signalling an error to its caller by returning an error triple with non-~c[nil] error component, and reverting the logical ~il[world] to its value just before that call of ~ilc[ld]. To see this effect of ~c[:ERROR] for ~c[ld-error-action], consider the following example. ~bv[] (ld '((defun f (x) x) (defun bad (x)) (defun g (x) x))) ~ev[] When the ~ilc[defun] of ~c[bad] fails (because its body is missing), evaluation of the ~c[ld] call stops; thus, the ~ilc[defun] of ~c[g] is not evaluated. The definition of ~c[f] will be removed from the logical ~il[world] before the call of ~c[ld] returns. However, by default each user call of ~c[ld] is made with a ~c[ld-error-action] of ~c[:RETURN!] (not ~c[:ERROR]). In the common case that all nested calls of ~c[ld] inside the ACL2 loop are made this way, an error will not roll back the logical ~il[world]. However, it will still halt evaluation of forms for the current call of ~c[ld] and any parent calls of ~c[ld] (other than the call made on behalf of ~c[lp] that entered the ACL2 loop in the first place), as though there were no more forms to evaluate. We have already discussed the behavior of ~c[ld] when an error occurs. But there is another case when ~c[ld] can stop processing more forms: when ~c[ld-error-action] is not ~c[:CONTINUE], ~ilc[ld-error-triples] is ~c[t], and evaluation of a form returns an error triple ~c[(mv nil val state)], where ~c[nil] is the error component and whose ``value component'', ~c[val] is a ~ilc[cons] pair whose ~ilc[car] is the symbol ~c[:STOP-LD]. Let ~c[val] be the pair ~c[(:STOP-LD . x)]. Then the call of ~c[ld] returns the error triple ~c[(mv nil (:STOP-LD n . x) state)], where ~c[n] is the value of ~ilc[state] global variable ~c['ld-level] at the time of termination. The following example illustrates how this works. ~bv[] (ld '((defun f1 (x) x) (ld '((defun f2 (x) x) (mv nil '(:STOP-LD my-error more-info) state) (defun g2 (x) x))) (defun g1 (x) x))) ~ev[] The outer call of ~c[ld] returns the value ~bv[] (:STOP-LD 2 3 MY-ERROR MORE-INFO) ~ev[] and leaves us in a world the includes definitions for ~c[f1] and ~c[f2], but no definition for ~c[g1] or ~c[g2] since neither of their two ~c[defun] forms was evaluated. The value of ~ilc[state] global ~c['ld-level] is incremented from 1 to 2 when the outer ~c[ld] is entered and then again to 3 when the inner ~c[ld] is entered. When the inner ~c[ld] escounters the error triple ~c[(mv nil (:STOP-LD my-error more-info) state)], it sees ~c[:STOP-LD] in the ~c[car] of the value component and pushes the current value of ~c['ld-level], 3, onto the ~ilc[cdr] of that value, to return the value triple ~c[(mv nil (:STOP-LD my-error 3 more-info) state)]. The outer of ~c[ld] then sees this value and returns ~c[(mv nil (:STOP-LD my-error 2 3 more-info) state)], since its current value of ~c['ld-level] is 2 after the inner ~c[ld] exits. That concludes our discussion of how these special ~c[:STOP-LD] values are handled; but how are they created? While they can be created directly by evaluation results as suggested in the example above, that is not the standard way. Rather, ~c[ld] returns an error triple ~c[(mv nil (:STOP-LD n) state)], where ~c[n] is the value of variable ~c[ld-level] at the time of termination, when the following conditions hold: an error occurs, ~c[ld-error-action] is ~c[RETURN!] (which is the default), and ~ilc[ld-error-triples] is ~c[t] (the default). The following example, which is a bit similar to the preceding one, illustrates both creation and handling of the special ~c[:STOP-LD] values. ~bv[] (ld '((defun f1 (x) x) (ld '((defun f2 (x) x) (ld '((defun f3 (x) x) (defun bad (x)) ; ERROR -- missing the body (defun g3 (x) x))) (defun g2 (x) x))) (defun g1 (x) x))) ~ev[] The result is that ~c[f1], ~c[f2], and ~c[f3] are defined, but none of ~c[g1], ~c[g2], or ~c[g3] is defined. Let's see why. The innermost call of ~ilc[ld] has a default ~c[:ld-error-action] of ~c[:RETURN!] (as do the other calls). So when the definition of ~c[bad] fails, then the innermost ~c[ld] returns ~c[(mv nil (:STOP-LD 4) state)]. The middle ~c[ld] sees this value, and since its ~c[:ld-error-action] is not ~c[:CONTINUE] (because it has the default value of ~c[:RETURN!]), it returns before considering the definition of ~c[g2], with value ~c[(mv nil (:STOP-LD 3 4) state)]. The topmost call of ~c[ld] similarly sees the ~c[:STOP-LD]; stops evaluation of forms, without defining ~c[g1]; and returns ~c[(mv nil (:STOP-LD 2 3 4) state)]." (f-get-global 'ld-error-action state)) (defun chk-ld-error-action (val ctx state) (cond ((member-eq val '(:continue :return :return! :error)) (value nil)) (t (er soft ctx *ld-special-error* 'ld-error-action val)))) (defun set-ld-error-action (val state) (er-progn (chk-ld-error-action val 'set-ld-error-action state) (pprogn (f-put-global 'ld-error-action val state) (value val)))) (defun ld-query-control-alist (state) ":Doc-Section Miscellaneous how to default answers to queries~/ ~c[Ld-query-control-alist] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-query-control-alist state)] and the updater is ~c[(set-ld-query-control-alist val state)]. Roughly speaking, ~c[ld-query-control-alist] is either ~c[nil] (meaning all queries should be interactive), ~c[t] (meaning all should default to the first accepted response), or an alist that pairs query ids to keyword responses. The alist may end in either ~c[t] or ~c[nil], indicating the default value for all ids not listed explicitly. Formally, the ~c[ld-query-control-alist] must satisfy ~c[ld-query-control-alistp]. The initial ~c[ld-query-control-alist] is ~c[nil], which means all queries are handled interactively.~/ When an ACL2 query is raised, a unique identifying symbol is printed in parentheses after the word ``Query''. This symbol, called the ``query id,'' can be used in conjunction with ~c[ld-query-control-alist] to prevent the query from being handled interactively. By ``handled interactively'' we mean that the query is printed to ~ilc[*standard-co*] and a response is read from ~ilc[*standard-oi*]. The alist can be used to obtain a ``default value'' for each query id. If this value is ~c[nil], then the query is handled interactively. In all other cases, the system handles the query without interaction (although text may be printed to ~ilc[standard-co] to make it appear that an interaction has occurred; see below). If the default value is ~c[t], the system acts as though the user responded to the query by typing the first response listed among the acceptable responses. If the default value is neither ~c[nil] nor ~c[t], then it must be a keyword and one of the acceptable responses. In that case, the system acts as though the user responded with the given keyword. Next, we discuss how the ~c[ld-query-control-alist] assigns a default value to each query id. It assigns each id the first value paired with the id in the alist, or, if no such pair appears in the alist, it assigns the final ~ilc[cdr] of the alist as the value. Thus, ~c[nil] assigns all ids ~c[nil]. ~c[T] assigns all ids ~c[t]. ~c['((:filter . nil) (:sysdef . :n) . t)] assigns ~c[nil] to the ~c[:filter] query, ~c[:n] to the ~c[:sysdef] query, and ~c[t] to all others. It remains only to discuss how the system prints text when the default value is not ~c[nil], i.e., when the query is handled without interaction. In fact, it is allowed to pair a query id with a singleton list containing a keyword, rather than a keyword, and this indicates that no printing is to be done. Thus for the example above, ~c[:sysdef] queries would be handled noninteractively, with printing done to simulate the interaction. But if we change the example so that ~c[:sysdef] is paired with ~c[(:n)], i.e., if ~c[ld-query-control-alist] is ~c['((:filter . nil) (:sysdef :n) . t)], then no such printing would take place for ~c[sysdef] queries. Instead, the default value of ~c[:n] would be assigned ``quietly''." (f-get-global 'ld-query-control-alist state)) (defun ld-query-control-alistp (val) (cond ((atom val) (or (eq val nil) (eq val t))) ((and (consp (car val)) (symbolp (caar val)) (or (eq (cdar val) nil) (eq (cdar val) t) (keywordp (cdar val)) (and (consp (cdar val)) (keywordp (cadar val)) (null (cddar val))))) (ld-query-control-alistp (cdr val))) (t nil))) (defun cdr-assoc-query-id (id alist) (cond ((atom alist) alist) ((eq id (caar alist)) (cdar alist)) (t (cdr-assoc-query-id id (cdr alist))))) (defun chk-ld-query-control-alist (val ctx state) (cond ((ld-query-control-alistp val) (value nil)) (t (er soft ctx *ld-special-error* 'ld-query-control-alist val)))) (defun set-ld-query-control-alist (val state) (er-progn (chk-ld-query-control-alist val 'set-ld-query-control-alist state) (pprogn (f-put-global 'ld-query-control-alist val state) (value val)))) (defun ld-verbose (state) ":Doc-Section Miscellaneous determines whether ~ilc[ld] prints ``ACL2 Loading ...''~/ ~c[Ld-verbose] is an ~ilc[ld] special (~pl[ld]). The accessor is ~c[(ld-verbose state)] and the updater is ~c[(set-ld-verbose val state)]. ~c[Ld-verbose] must be ~c[t], ~c[nil] or a string or ~ilc[consp] suitable for ~ilc[fmt] printing via the ~c[~~@] command. The initial value of ~c[ld-verbose] is a ~ilc[fmt] message that prints the ACL2 version number, ~ilc[ld] level and connected book directory.~/ Before processing the forms in ~ilc[standard-oi], ~ilc[ld] may print a header. The printing of this header is controlled by ~c[ld-verbose]. If ~c[ld-verbose] is ~c[nil], no header is printed. If it is ~c[t], ~ilc[ld] prints the message ~bv[] ACL2 loading ~ev[] where ~c[] is the input channel supplied to ~ilc[ld]. A similar message is printed when ~ilc[ld] completes. If ~c[ld-verbose] is neither ~c[t] nor ~c[nil] then it is presumably a header and is printed with the ~c[~~@] ~ilc[fmt] directive before ~ilc[ld] begins to read and process forms. In this case the ~c[~~@] ~ilc[fmt] directive is interpreted in an environment in which ~c[#\\v] is the ACL2 version string, ~c[#\\l] is the level of the current recursion in ~ilc[ld] and/or ~ilc[wormhole], and ~c[#\\c] is the connected book directory ~c[(cbd)]." (f-get-global 'ld-verbose state)) (defun chk-ld-verbose (val ctx state) (cond ((or (stringp val) (and (consp val) (stringp (car val)))) (value nil)) ((member-eq val '(nil t)) (value nil)) (t (er soft ctx *ld-special-error* 'ld-verbose val)))) (defun set-ld-verbose (val state) (er-progn (chk-ld-verbose val 'set-ld-verbose state) (pprogn (f-put-global 'ld-verbose val state) (value val)))) (defconst *nqthm-to-acl2-primitives* ; Keep this list in sync with documentation for nqthm-to-acl2. '((ADD1 1+) (ADD-TO-SET ADD-TO-SET-EQUAL ADD-TO-SET-EQ) (AND AND) (APPEND APPEND BINARY-APPEND) (APPLY-SUBR . "Doesn't correspond to anything in ACL2, really. See the documentation for DEFEVALUATOR and META.") (APPLY$ . "See the documentation for DEFEVALUATOR and META.") (ASSOC ASSOC-EQUAL ASSOC ASSOC-EQ) (BODY . "See the documentation for DEFEVALUATOR and META.") (CAR CAR) (CDR CDR) (CONS CONS) (COUNT ACL2-COUNT) (DIFFERENCE -) (EQUAL EQUAL EQ EQL =) (EVAL$ . "See the documentation for DEFEVALUATOR and META.") (FALSE . "Nqthm's F corresponds to the ACL2 symbol NIL.") (FALSEP NOT NULL) ;;(FIX) ;;(FIX-COST) ;;(FOR) (FORMALS . "See the documentation for DEFEVALUATOR and META.") (GEQ >=) (GREATERP >) (IDENTITY IDENTITY) (IF IF) (IFF IFF) (IMPLIES IMPLIES) (LEQ <=) (LESSP <) (LISTP CONSP) (LITATOM SYMBOLP) (MAX MAX) (MEMBER MEMBER-EQUAL MEMBER MEMBER-EQ) (MINUS - UNARY--) (NEGATIVEP MINUSP) (NEGATIVE-GUTS ABS) (NLISTP ATOM) (NOT NOT) (NUMBERP ACL2-NUMBERP INTEGERP RATIONALP) (OR OR) (ORDINALP O-P) (ORD-LESSP O<) (PACK . "See INTERN and COERCE.") (PAIRLIST PAIRLIS$) (PLUS + BINARY-+) ;;(QUANTIFIER-INITIAL-VALUE) ;;(QUANTIFIER-OPERATION) (QUOTIENT /) (REMAINDER REM MOD) (STRIP-CARS STRIP-CARS) (SUB1 1-) ;;(SUBRP) ;;(SUM-CDRS) (TIMES * BINARY-*) (TRUE . "The symbol T.") ;;(TRUEP) (UNION UNION-EQUAL UNION-EQ) (UNPACK . "See SYMBOL-NAME and COERCE.") (V&C$ . "See the documentation for DEFEVALUATOR and META.") (V&C-APPLY$ . "See the documentation for DEFEVALUATOR and META.") (ZERO . "The number 0.") (ZEROP ZEROP))) (defconst *nqthm-to-acl2-commands* ; Keep this list in sync with documentation for nqthm-to-acl2. '((ACCUMULATED-PERSISTENCE ACCUMULATED-PERSISTENCE) (ADD-AXIOM DEFAXIOM) (ADD-SHELL . "There is no shell principle in ACL2.") (AXIOM DEFAXIOM) (BACKQUOTE-SETTING . "Backquote is supported in ACL2, but not currently documented.") (BOOT-STRAP GROUND-ZERO) (BREAK-LEMMA MONITOR) (BREAK-REWRITE BREAK-REWRITE) (CH PBT . "See also :DOC history.") (CHRONOLOGY PBT . "See also :DOC history.") (COMMENT DEFLABEL) (COMPILE-UNCOMPILED-DEFNS COMP) (CONSTRAIN . "See :DOC encapsulate and :DOC local.") (DATA-BASE . "Perhaps the closest ACL2 analogue of DATA-BASE is PROPS. But see :DOC history for a collection of commands for querying the ACL2 database (``world''). Note that the notions of supporters and dependents are not supported in ACL2.") (DCL DEFSTUB) (DEFN DEFUN DEFMACRO) (DEFTHEORY DEFTHEORY) (DISABLE DISABLE) (DISABLE-THEORY . "See :DOC theories. The Nqthm command (DISABLE-THEORY FOO) corresponds roughly to the ACL2 command (in-theory (set-difference-theories (current-theory :here) (theory 'foo))).") (DO-EVENTS LD) (DO-FILE LD) (ELIM ELIM) (ENABLE ENABLE) (ENABLE-THEORY . "See :DOC theories. The Nqthm command (ENABLE-THEORY FOO) corresponds roughly to the ACL2 command (in-theory (union-theories (theory 'foo) (current-theory :here))).") (EVENTS-SINCE PBT) (FUNCTIONALLY-INSTANTIATE . "ACL2 provides a form of the :USE hint that corresponds roughly to the FUNCTIONALLY-INSTANTIATE event of Nqthm. See :DOC lemma-instance.") (GENERALIZE GENERALIZE) (HINTS HINTS) (LEMMA DEFTHM) (MAINTAIN-REWRITE-PATH BRR) (MAKE-LIB . "There is no direct analogue of Nqthm's notion of ``library.'' See :DOC books for a description of ACL2's mechanism for creating and saving collections of events.") (META META) (NAMES NAME) (NOTE-LIB INCLUDE-BOOK) (PPE PE) (PROVE THM) (PROVEALL . "See :DOC ld and :DOC certify-book. The latter corresponds to Nqthm's PROVE-FILE,which may be what you're interested in, really.") (PROVE-FILE CERTIFY-BOOK) (PROVE-FILE-OUT CERTIFY-BOOK) (PROVE-LEMMA DEFTHM . "See also :DOC hints.") (R-LOOP . "The top-level ACL2 loop is an evaluation loop as well, so no analogue of R-LOOP is necessary.") (REWRITE REWRITE) (RULE-CLASSES RULE-CLASSES) (SET-STATUS IN-THEORY) (SKIM-FILE LD-SKIP-PROOFSP) (TOGGLE IN-THEORY) (TOGGLE-DEFINED-FUNCTIONS EXECUTABLE-COUNTERPART-THEORY) (TRANSLATE TRANS TRANS1) (UBT UBT U) (UNBREAK-LEMMA UNMONITOR) (UNDO-BACK-THROUGH UBT) (UNDO-NAME . "See :DOC ubt. There is no way to undo names in ACL2 without undoing back through such names. However, see :DOC ld-skip-proofsp for information about how to quickly recover the state."))) (defun nqthm-to-acl2-fn (name state) (declare (xargs :guard (symbolp name))) (io? temporary nil (mv erp val state) (name) (let ((prims (cdr (assoc-eq name *nqthm-to-acl2-primitives*))) (comms (cdr (assoc-eq name *nqthm-to-acl2-commands*)))) (pprogn (cond (prims (let ((syms (fix-true-list prims)) (info (if (consp prims) (cdr (last prims)) prims))) (pprogn (if syms (fms "Related ACL2 primitives (use :PE or see documentation ~ to learn more): ~&0.~%" (list (cons #\0 syms)) *standard-co* state nil) state) (if info (pprogn (fms info (list (cons #\0 syms)) *standard-co* state nil) (newline *standard-co* state)) state)))) (t state)) (cond (comms (let ((syms (fix-true-list comms)) (info (if (consp comms) (cdr (last comms)) comms))) (pprogn (if syms (fms "Related ACL2 commands (use :PE or see documentation ~ to learn more): ~&0.~%" (list (cons #\0 syms)) *standard-co* state nil) state) (if info (pprogn (fms info (list (cons #\0 syms)) *standard-co* state nil) (newline *standard-co* state)) state)))) (t state)) (if (or prims comms) (value :invisible) (pprogn (fms "Sorry, but there seems to be no ACL2 notion corresponding ~ to the alleged Nqthm notion ~x0.~%" (list (cons #\0 name)) *standard-co* state nil) (value :invisible))))))) ; Here are functions that can be defined to print out the last part of the ; documentation string for nqthm-to-acl2, using (print-nqthm-to-acl2-doc ; state). ; (defun print-nqthm-to-acl2-doc1 (alist state) ; (cond ; ((null alist) state) ; (t (let* ((x (fix-true-list (cdar alist))) ; (s (if (atom (cdar alist)) ; (cdar alist) ; (cdr (last (cdar alist)))))) ; (mv-let ; (col state) ; (fmt1 " ~x0~t1--> " ; (list (cons #\0 (caar alist)) ; (cons #\1 16)) ; 0 *standard-co* state nil) ; (declare (ignore col)) ; (mv-let ; (col state) ; (fmt1 " ~&0" ; (list (cons #\0 x)) ; 0 *standard-co* state nil) ; (declare (ignore col)) ; (pprogn ; (if (or (null x) (null s)) ; state ; (fms "~t0" (list (cons #\0 21)) *standard-co* state nil)) ; (if s ; (mv-let ; (col state) ; (fmt1 "~@0~%" ; Here % was vertical bar, but emacs 19 has trouble... ; (list (cons #\0 s)) 0 *standard-co* state nil) ; (declare (ignore col)) ; state) ; (newline *standard-co* state)) ; (print-nqthm-to-acl2-doc1 (cdr alist) state)))))))) ; ; (defun print-nqthm-to-acl2-doc (state) ; (pprogn ; (princ$ " ~bv[]" *standard-co* state) ; (fms " Nqthm functions --> ACL2" ; nil *standard-co* state nil) ; (fms " ----------------------------------------~%" ; nil *standard-co* state nil) ; (print-nqthm-to-acl2-doc1 *nqthm-to-acl2-primitives* state) ; (fms " ========================================~%" ; nil *standard-co* state nil) ; (fms " Nqthm commands --> ACL2" ; nil *standard-co* state nil) ; (fms " ----------------------------------------~%" ; nil *standard-co* state nil) ; (print-nqthm-to-acl2-doc1 *nqthm-to-acl2-commands* state) ; (princ$ " ~ev[]" *standard-co* state) ; (newline *standard-co* state) ; (value :invisible))) (defmacro nqthm-to-acl2 (x) ; Keep documentation for this function in sync with *nqthm-to-acl2-primitives* ; and *nqthm-to-acl2-commands*. See comment above for how some of this ; documentation was generated. (declare (xargs :guard (and (true-listp x) (equal (length x) 2) (eq (car x) 'quote) (symbolp (cadr x))))) ":Doc-Section Documentation ACL2 analogues of Nqthm functions and commands~/ ~bv[] Example Forms: :nqthm-to-acl2 prove-lemma ; Display ACL2 topic(s) and/or print ; information corresponding to Nqthm ; PROVE-LEMMA command. (nqthm-to-acl2 'prove-lemma) ; Same as above.~/ General Form: (nqthm-to-acl2 name) ~ev[] where ~c[name] is a notion documented for Nqthm: either a function in the Nqthm logic, or a command. If there is corresponding information available for ACL2, it will be printed in response to this command. This information is not intended to be completely precise, but rather, is intended to help those familiar with Nqthm to make the transition to ACL2. We close with two tables that contain all the information used by this ~c[nqthm-to-acl2] command. The first shows the correspondence between functions in the Nqthm logic and corresponding ACL2 functions (when possible); the second is similar, but for commands rather than functions. ~bv[] Nqthm functions --> ACL2 ---------------------------------------- ADD1 --> 1+ ADD-TO-SET --> ADD-TO-SET-EQUAL and ADD-TO-SET-EQ AND --> AND APPEND --> APPEND and BINARY-APPEND APPLY-SUBR --> No correspondent, but see the documentation for DEFEVALUATOR and META. APPLY$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. ASSOC --> ASSOC-EQUAL, ASSOC and ASSOC-EQ BODY --> No correspondent, but see the documentation for DEFEVALUATOR and META. CAR --> CAR CDR --> CDR CONS --> CONS COUNT --> ACL2-COUNT DIFFERENCE --> - EQUAL --> EQUAL, EQ, EQL and = EVAL$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. FALSE --> Nqthm's F corresponds to the ACL2 symbol NIL. FALSEP --> NOT and NULL FORMALS --> No correspondent, but see the documentation for DEFEVALUATOR and META. GEQ --> >= GREATERP --> > IDENTITY --> IDENTITY IF --> IF IFF --> IFF IMPLIES --> IMPLIES LEQ --> <= LESSP --> < LISTP --> CONSP LITATOM --> SYMBOLP MAX --> MAX MEMBER --> MEMBER-EQUAL, MEMBER and MEMBER-EQ MINUS --> - and UNARY-- NEGATIVEP --> MINUSP NEGATIVE-GUTS --> ABS NLISTP --> ATOM NOT --> NOT NUMBERP --> ACL2-NUMBERP, INTEGERP and RATIONALP OR --> OR ORDINALP --> O-P ORD-LESSP --> O< PACK --> See intern and coerce. PAIRLIST --> PAIRLIS$ PLUS --> + and BINARY-+ QUOTIENT --> / REMAINDER --> REM and MOD STRIP-CARS --> STRIP-CARS SUB1 --> 1- TIMES --> * and BINARY-* TRUE --> The symbol T. UNION --> UNION-EQUAL and UNION-EQ UNPACK --> See symbol-name and coerce. V&C$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. V&C-APPLY$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. ZERO --> The number 0. ZEROP --> ZP ======================================== Nqthm commands --> ACL2 ---------------------------------------- ACCUMULATED-PERSISTENCE --> ACCUMULATED-PERSISTENCE ADD-AXIOM --> DEFAXIOM ADD-SHELL --> There is no shell principle in ACL2. AXIOM --> DEFAXIOM BACKQUOTE-SETTING --> Backquote is supported in ACL2, but not currently documented. BOOT-STRAP --> GROUND-ZERO BREAK-LEMMA --> MONITOR BREAK-REWRITE --> BREAK-REWRITE CH --> PBT See also :DOC history. CHRONOLOGY --> PBT See also :DOC history. COMMENT --> DEFLABEL COMPILE-UNCOMPILED-DEFNS --> COMP CONSTRAIN --> See :DOC encapsulate and :DOC local. DATA-BASE --> Perhaps the closest ACL2 analogue of DATA-BASE is PROPS. But see :DOC history for a collection of commands for querying the ACL2 database (``world''). Note that the notions of supporters and dependents are not supported in ACL2. DCL --> DEFSTUB DEFN --> DEFUN and DEFMACRO DEFTHEORY --> DEFTHEORY DISABLE --> DISABLE DISABLE-THEORY --> See :DOC theories. The Nqthm command (DISABLE-THEORY FOO) corresponds roughly to the ACL2 command (in-theory (set-difference-theories (current-theory :here) (theory 'foo))). DO-EVENTS --> LD DO-FILE --> LD ELIM --> ELIM ENABLE --> ENABLE ENABLE-THEORY --> See :DOC theories. The Nqthm command (ENABLE-THEORY FOO) corresponds roughly to the ACL2 command (in-theory (union-theories (theory 'foo) (current-theory :here))). EVENTS-SINCE --> PBT FUNCTIONALLY-INSTANTIATE --> ACL2 provides a form of the :USE hint that corresponds roughly to the FUNCTIONALLY-INSTANTIATE event of Nqthm. See :DOC lemma-instance. GENERALIZE --> GENERALIZE HINTS --> HINTS LEMMA --> DEFTHM MAINTAIN-REWRITE-PATH --> BRR MAKE-LIB --> There is no direct analogue of Nqthm's notion of ``library.'' See :DOC books for a description of ACL2's mechanism for creating and saving collections of events. META --> META NAMES --> NAME NOTE-LIB --> INCLUDE-BOOK PPE --> PE PROVE --> THM PROVEALL --> See :DOC ld and :DOC certify-book. The latter corresponds to Nqthm's PROVE-FILE,which may be what you're interested in,really. PROVE-FILE --> CERTIFY-BOOK PROVE-FILE-OUT --> CERTIFY-BOOK PROVE-LEMMA --> DEFTHM See also :DOC hints. R-LOOP --> The top-level ACL2 loop is an evaluation loop as well, so no analogue of R-LOOP is necessary. REWRITE --> REWRITE RULE-CLASSES --> RULE-CLASSES SET-STATUS --> IN-THEORY SKIM-FILE --> LD-SKIP-PROOFSP TOGGLE --> IN-THEORY TOGGLE-DEFINED-FUNCTIONS --> EXECUTABLE-COUNTERPART-THEORY TRANSLATE --> TRANS and TRANS1 UBT --> UBT and U UNBREAK-LEMMA --> UNMONITOR UNDO-BACK-THROUGH --> UBT UNDO-NAME --> See :DOC ubt. There is no way to undo names in ACL2 without undoing back through such names. However, see :DOC ld-skip-proofsp for information about how to quickly recover the state. ~ev[] " `(nqthm-to-acl2-fn ,x state)) #+(and gcl (not acl2-loop-only)) (progn (defvar *current-allocated-fixnum-lo* 0) (defvar *current-allocated-fixnum-hi* 0)) (defun allocate-fixnum-range (fixnum-lo fixnum-hi) ":Doc-Section ACL2::ACL2-built-ins set aside fixnums in GCL~/ ~c[(Allocate-fixnum-range fixnum-lo fixnum-hi)] causes Gnu Common Lisp (GCL) to create a persistent table for the integers between ~c[fixnum-lo] and ~c[fixnum-hi] (both bounds inclusive). This table is referenced first when any integer is boxed and the existing box in the table is used if the integer is in bounds. This can speed up GCL considerably by avoiding wasteful fixnum boxing. Here, ~c[fixnum-lo] and ~c[fixnum-hi] should be fixnums. On 32-bit machines it would be good for them to be of type ~c[(signed-byte 30)], with ~c[fixnum-lo <= fixnum-hi].~/ When this function is executed in a Lisp implementation other than GCL, it has no side effect. This function always returns ~c[nil]." (declare (xargs :guard (and (integerp fixnum-lo) (integerp fixnum-hi) (>= fixnum-hi fixnum-lo))) (type (signed-byte 30) fixnum-lo fixnum-hi)) ; This function is simply NIL in the logic but allocates a range of fixnums ; (from fixnum-lo to fixnum-hi) in GCL as a side effect (a side effect which ; should only affect the speed with which ACL2 computes a value, but not the ; value itself up to EQUALity). In GCL, there is a range of pre-allocated ; fixnums which are fixed to be -1024 to +1023. (let ((tmp (- fixnum-hi fixnum-lo))) (declare (ignore tmp)) #+(and gcl (not acl2-loop-only)) (cond ((or (> fixnum-hi *current-allocated-fixnum-hi*) (< fixnum-lo *current-allocated-fixnum-lo*)) (fms "NOTE: Allocating bigger fixnum table in GCL.~|" nil (standard-co *the-live-state*) *the-live-state* nil) (system::allocate-bigger-fixnum-range fixnum-lo (1+ fixnum-hi)) (setq *current-allocated-fixnum-lo* fixnum-lo) (setq *current-allocated-fixnum-hi* fixnum-hi)) (t (fms "No further fixnum allocation done:~| Previous fixnum table ~ encompasses desired allocation.~|" nil (standard-co *the-live-state*) *the-live-state* nil))) #+(and (not gcl) (not acl2-loop-only)) (fms "Fixnum allocation is only performed in GCL.~|" nil (standard-co *the-live-state*) *the-live-state* nil) nil)) ; It has been found useful to allocate new space very gradually in Allegro CL ; 6.1 for at least one unusually large job on a version of RedHat Linux (over ; 600MB without this caused GC error; with this call, the corresponding image ; size was cut by very roughly one third and there was no GC error). However, ; the problem seems to disappear in Allegro CL 6.2. So we won't advertise ; (document) this utility. #+allegro (defmacro allegro-allocate-slowly (&key (free-bytes-new-other '1024) (free-bytes-new-pages '1024) (free-percent-new '3) (expansion-free-percent-old '3) (expansion-free-percent-new '3)) `(allegro-allocate-slowly-fn ,free-bytes-new-other ,free-bytes-new-pages ,free-percent-new ,expansion-free-percent-old ,expansion-free-percent-new)) (defun allegro-allocate-slowly-fn (free-bytes-new-other free-bytes-new-pages free-percent-new expansion-free-percent-old expansion-free-percent-new) #-(and allegro (not acl2-loop-only)) (declare (ignore free-bytes-new-other free-bytes-new-pages free-percent-new expansion-free-percent-old expansion-free-percent-new)) #+(and allegro (not acl2-loop-only)) (progn (setf (sys:gsgc-parameter :free-bytes-new-other) free-bytes-new-other) (setf (sys:gsgc-parameter :free-bytes-new-pages) free-bytes-new-pages) (setf (sys:gsgc-parameter :free-percent-new) free-percent-new) (setf (sys:gsgc-parameter :expansion-free-percent-old) expansion-free-percent-old) (setf (sys:gsgc-parameter :expansion-free-percent-new) expansion-free-percent-new)) nil) ; All code for the pstack feature occurs immediately below. When a form is ; wrapped in (pstk form), form will be pushed onto *pstk-stack* during its ; evaluation. The stack can be evaluated (during a break or after an ; interrupted proof) by evaluating the form (pstack), and it is ; initialized at the beginning of each new proof attempt (in prove-loop, since ; that is the prover's entry point under both prove and pc-prove). #-acl2-loop-only (progn (defparameter *pstk-stack* nil) (defvar *verbose-pstk* nil) ; The following are only of interest when *verbose-pstk* is true. (defparameter *pstk-level* 1) (defparameter *pstk-start-time-stack* nil)) (defmacro clear-pstk () #+acl2-loop-only nil #-acl2-loop-only '(progn (setq *pstk-stack* nil) (setq *pstk-level* 1) (setq *pstk-start-time-stack* nil))) (defconst *pstk-vars* '(pstk-var-0 pstk-var-1 pstk-var-2 pstk-var-3 pstk-var-4 pstk-var-5 pstk-var-6 pstk-var-7 pstk-var-8 pstk-var-9 pstk-var-10 pstk-var-11 pstk-var-12)) (defun pstk-bindings-and-args (args vars) ; We return (mv bindings new-args fake-args). Here new-args is a symbol-listp ; and of the same length as args, where each element of args is either a symbol ; or is the value of the corresponding element of new-args in bindings. ; Fake-args is the same as new-args except that state has been replaced by ; . (cond ((endp args) (mv nil nil nil)) ((endp vars) (mv (er hard 'pstk-bindings-and-args "The ACL2 sources need *pstk-vars* to be extended.") nil nil)) (t (mv-let (bindings rest-args fake-args) (pstk-bindings-and-args (cdr args) (cdr vars)) (cond ((eq (car args) 'state) (mv bindings (cons (car args) rest-args) (cons '' rest-args))) ((symbolp (car args)) (mv bindings (cons (car args) rest-args) (cons (car args) fake-args))) (t (mv (cons (list (car vars) (car args)) bindings) (cons (car vars) rest-args) (cons (car vars) fake-args)))))))) (defmacro pstk (form) (declare (xargs :guard (consp form))) #+acl2-loop-only `(check-vars-not-free ,*pstk-vars* ,form) #-acl2-loop-only (mv-let (bindings args fake-args) (pstk-bindings-and-args (cdr form) *pstk-vars*) `(let ,bindings (setq *pstk-stack* (cons ,(list* 'list (kwote (car form)) fake-args) *pstk-stack*)) (dmr-flush) (when (and *verbose-pstk* (or (eq *verbose-pstk* t) (not (member-eq ',(car form) *verbose-pstk*)))) (setq *pstk-start-time-stack* (cons (get-internal-time) *pstk-start-time-stack*)) (format t "~V@TCP~D> ~S~%" (* 2 *pstk-level*) *pstk-level* ',(car form)) (setq *pstk-level* (1+ *pstk-level*))) (our-multiple-value-prog1 ,(cons (car form) args) ; Careful! We must be careful not to smash any mv-ref value in the forms ; below, in case form returns a multiple value. So, for example, we use format ; rather than fmt1. (when (and *verbose-pstk* (or (eq *verbose-pstk* t) (not (member-eq ',(car form) *verbose-pstk*)))) (setq *pstk-level* (1- *pstk-level*)) (format t "~V@TCP~D< ~S [~,2F seconds]~%" (* 2 *pstk-level*) *pstk-level* ',(car form) (/ (- (get-internal-time) (pop *pstk-start-time-stack*)) (float internal-time-units-per-second)))) (setq *pstk-stack* (cdr *pstk-stack*)) ,@(and (not (eq (car form) 'ev-fncall-meta)) ; overkill in that case '((dmr-flush))) ,@(and (eq (car form) 'rewrite-atm) '((setq *deep-gstack* nil))))))) (defun pstack-fn (allp state) #+acl2-loop-only (declare (ignore allp)) #-acl2-loop-only (cond ((and allp (not (eq allp :all))) (fmt-abbrev "~%~p0" (list (cons #\0 (if allp *pstk-stack* (strip-cars *pstk-stack*)))) 0 *standard-co* state "~|")) (t (fms "~p0~|" (list (cons #\0 (if allp *pstk-stack* (strip-cars *pstk-stack*)))) *standard-co* state (and allp ; (eq allp :all) (cons (world-evisceration-alist state nil) '(nil nil nil)))))) #-acl2-loop-only (if (assoc-eq 'preprocess-clause *pstk-stack*) (cw "NOTE: You may find the hint :DO-NOT '(PREPROCESS) helpful.~|")) (value :invisible)) (defmacro pstack (&optional allp) ":Doc-Section Other seeing what the prover is up to~/ ~bv[] General Forms: (pstack) ; inspect break (pstack t) ; inspect break, printing all calls in abbreviated form (pstack :all) ; as above, but only abbreviating the ACL2 world ~ev[] When the form ~c[(pstack)] is executed during a break from a proof, or at the end of a proof that the user has aborted, a ``process stack'' (or ``prover stack'') will be printed that gives some idea of what the theorem prover has been doing. Moreover, by evaluating ~c[(verbose-pstack t)] before starting a proof (~pl[verbose-pstack]) one can get trace-like information about prover functions, including time summaries, printed to the screen during a proof. This feature is currently quite raw and may be refined considerably as time goes on, based on user suggestions. For example, the usual control of printing given by ~il[set-inhibit-output-lst] is irrelevant for printing the pstack. The use of ~c[(pstack t)] or ~c[(pstack :all)] should only be used by those who are comfortable looking at functions in the ACL2 source code. Otherwise, simply use ~c[(pstack)].~/ Entries in the pstack include the following (listed here alphabetically, except for the first). ~c[preprocess-clause], ~c[simplify-clause], etc. (in general,~c[xxx-clause]): top-level processes in the prover ``waterfall'' ~c[clausify]: splitting a goal into subgoals ~c[ev-fncall]: evaluating a function on explicit arguments ~c[ev-fncall-meta]: evaluating a metafunction ~c[forward-chain]: building a context for the current goal using ~ilc[forward-chaining] rules ~c[induct]: finding an induction scheme ~c[pop-clause]: getting the next goal to prove by induction ~c[process-assumptions]: creating forcing rounds ~c[remove-built-in-clauses]: removing built-in clauses (~pl[built-in-clause]) ~c[process-equational-polys]: deducing interesting equations ~c[remove-trivial-equivalences]: removing trivial equalities (and equivalences) from the current goal ~c[rewrite-atm]: rewriting a top-level term in the current goal ~c[setup-simplify-clause-pot-lst]: building the linear arithmetic database for the current goal ~c[strip-branches], ~c[subsumption-replacement-loop]: subroutines of ~c[clausify] ~c[waterfall]: top-level proof control ~/" `(pstack-fn ,allp state)) (defun verbose-pstack (flg-or-list) ":Doc-Section Pstack seeing what the prover is up to (for advanced users)~/ ~bv[] General Forms: (verbose-pstack t) ; get trace-like information on prover during proofs (verbose-pstack '(fn1 fn2 ...)) ; as above, but omit calls of the indicated functions (verbose-pstack nil) ; turn off trace-like information on prover ~ev[] For example, (verbose-pstack '(ev-fncall)) will provide a trace of various prover functions during proofs, except for the function ~c[ev-fncall]. By evaluating ~c[(verbose-pstack t)] one can get trace-like information during subsequent proofs about prover functions, including time summaries, printed to the screen during a proof. To turn off this feature, evaluate ~c[(verbose-pstack nil)]. Also ~l[pstack].~/~/" (declare (xargs :guard (or (eq flg-or-list t) (eq flg-or-list nil) (symbol-listp flg-or-list)))) #+acl2-loop-only flg-or-list #-acl2-loop-only (setq *verbose-pstk* flg-or-list)) ; End of pstack code. ; The following two functions could go in axioms.lisp, but it seems not worth ; putting them in :logic mode so we might as well put them here. (defun pop-inhibit-output-lst-stack (state) (let ((stk (f-get-global 'inhibit-output-lst-stack state))) (cond ((null stk) state) (t (pprogn (f-put-global 'inhibit-output-lst (car stk) state) (f-put-global 'inhibit-output-lst-stack (cdr stk) state)))))) (defun push-inhibit-output-lst-stack (state) (f-put-global 'inhibit-output-lst-stack (cons (f-get-global 'inhibit-output-lst state) (f-get-global 'inhibit-output-lst-stack state)) state)) (defun set-gc-threshold$-fn (new-threshold verbose-p) ; This function is used to manage garbage collection in a way that is friendly ; to ACL2(p). As suggested by its name, it sets (in supported Lisps), to ; new-threshold, the number of bytes to be allocated before the next garbage ; collection. It may set other gc-related behavior as well. (declare (ignorable verbose-p)) (let ((ctx 'set-gc-threshold$)) (cond ((not (posp new-threshold)) (er hard ctx "The argument to set-gc-threshold$ must be a positive integer, so ~ the value ~x0 is illegal." new-threshold)) (t #-acl2-loop-only (progn #+ccl (ccl:set-lisp-heap-gc-threshold new-threshold) #+(and ccl acl2-par) (progn (cw "Disabling the CCL Ephemeral GC for ACL2(p)~%") (ccl:egc nil)) #+sbcl (setf (sb-ext:bytes-consed-between-gcs) (1- new-threshold)) #+(and lispworks lispworks-64bit) (progn (when (< new-threshold (expt 2 20)) (let ((state *the-live-state*)) ; Avoid warning$-cw, since this function is called by LP outside the loop. (warning$ 'set-gc-threshold$ nil "Ignoring argument to set-gc-threshold$, ~x0, because ~ it specifies a threshold of less than one megabyte. ~ Using default threshold of one megabyte."))) ; Calling set-gen-num-gc-threshold sets the GC threshold for the given ; generation of garbage. (system:set-gen-num-gc-threshold 0 (max (expt 2 10) (/ new-threshold (expt 2 10)))) (system:set-gen-num-gc-threshold 1 (max (expt 2 17) (/ new-threshold (expt 2 3)))) (system:set-gen-num-gc-threshold 2 (max (expt 2 18) (/ new-threshold (expt 2 2)))) ; This call to set-blocking-gen-num accomplishes two things: (1) It sets the ; third generation as the "final" generation -- nothing can be promoted to ; generation four or higher. (2) It sets the GC threshold for generation 3. (system:set-blocking-gen-num 3 :gc-threshold (max (expt 2 20) new-threshold))) #-(or ccl sbcl (and lispworks lispworks-64bit)) (when verbose-p (let ((state *the-live-state*)) ; Avoid warning$-cw, since this function is called by LP outside the loop. (warning$ 'set-gc-threshold$ nil "We have not yet implemented setting the garbage ~ collection threshold for this Lisp. Contact the ACL2 ~ implementors to request such an implementation.")))) t)))) (defmacro set-gc-threshold$ (new-threshold &optional (verbose-p 't)) ; See comments in set-gc-threshold$-fn. `(set-gc-threshold$-fn ,new-threshold ,verbose-p)) acl2-sources/bdd.lisp0000666002132200015000000050754212222115527014213 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; Table of contents. ; ; 0. PRELIMINARY MACROS ; I. INTRODUCTION AND DATA TYPES ; II. OP-ALIST ; III. HASH OPERATIONS ; IV. HASH OPERATIONS: QUOTEPS ; V. BDD RULES AND ONE-WAY UNIFIER ; VI. SOME INTERFACE UTILITIES ; VII. MAIN ALGORITHM ; VIII. TOP-LEVEL (INTERFACE) ROUTINES ; IX. COMPILING THIS FILE AND OTHER HELPFUL TIPS ; ; Mx-id-bound is currently 438619, perhaps too low. We could perhaps fix this ; by changing how we deal with close to 16 args in op-hash-index1, and by ; changing 131 in if-hash-index. (in-package "ACL2") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; 0. PRELIMINARY MACROS ;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro mvf (x &rest rest) ; We often wish to pass back a multiple value such that the first value is a ; fixnum. Efficiency is apparently improved in GCL when that fixnum is not ; "boxed," but instead is treated as a raw C integer. Currently ACL2 provides ; mechanisms for this, but they require that an appropriate THE expression ; surround such a value when it is the first value passed back by MV. (Note ; that there seems to be no way to keep GCL from boxing fixnums in other than ; the first argument position of MV.) `(mv (the-fixnum ,x) ,@rest)) (defmacro logandf (&rest args) (xxxjoin-fixnum 'logand args -1)) (defmacro logxorf (&rest args) (xxxjoin-fixnum 'logxor args 0)) (defmacro logiorf (&rest args) (xxxjoin-fixnum 'logior args 0)) (defmacro ashf (x y) (list 'the-fixnum (list 'ash (list 'the-fixnum x) (list 'the-fixnum y)))) (defmacro mx-id-bound () ; This bound on mx-id must be such that our calls of +f and *f involving mx-id ; produce fixnums. At this writing the most severe such test is in ; op-hash-index1; see the comment there. (1- (floor (fixnum-bound) 153))) (defmacro 1+mx-id (x) ; DILEMMA: Do we let this macro box (1+ x) or not, and if so, when? Here are ; some thoughts on the issue. ; Use this macro to increment mx-id,in order to guarantee that mx-id remains a ; fixnum. X is known to be a nonnegative fixnum; this macro checks that we ; keep it a fixnum by adding 1 to it. It actually checks even more, namely, ; that `(the-fixnum (let ((x ,x)) (declare (type (signed-byte 30) x)) ; Should we really include the declaration above? The main reason seems to be ; in order for the incrementing operation below to run fast, but in fact we ; have done several experiments and it seems that the current version of this ; code is optimal for performance. That's a bit surprising, since each mx-id ; gets consed into a list anyhow (a cst), and hence is boxed in GCL (which is ; the only list we are talking about here). So, there wouldn't appear to be ; any particular advantage in wrapping the-fixnum around this form. At any ; rate, the performance issues here seem to be quite minor. ; A typical use of this macro is of the form ; (let ((new-mx-id (1+mx-id mx-id))) ; (declare (type (signed-byte 30) new-mx-id)) ; (let ((new-cst (make-leaf-cst ; new-mx-id ; term ; nil))) ; (mvf new-mx-id ; ...))) ; Note that make-leaf-cst will box new-mx-id -- after all, it is consing ; new-mx-id into a list. The present approach delays this boxing until that ; time, so that we don't have to unbox new-mx-id in the mvf form above. The ; unboxed new-mx-id may actually never benefit from being unboxed, and in fact ; we may want to rework our entire bdd code so that mx-ids are always boxed. (cond ((< x ,(mx-id-bound)) (1+f x)) (t (the-fixnum ; the-fixnum call may help with proclaiming (er-hard-val 0 'bdd "Maximum id for bdds exceeded. Current maximum id is ~x0." x))))))) (defmacro bdd-error (mx-id fmt-string fmt-alist bad-cst ttree) ; Perhaps it would be more "natural" to define this macro to return ; `(mvf ,mx-id ,(cons fmt-string ,fmt-alist) ,bad-cst ,ttree) ; since then we can view the result as ; `(mvf ,mx-id , ,bad-cst ,call-stack ,ttree) ; However, we would like to have a very fast test for whether the tuple ; returned designates an error. The present approach allows us to test with ; stringp on the second value returned. We take advantage of that in the ; definition of bdd-mv-let. ; Note that the order of the first two values should not be changed: we ; declare mx-id to be a fixnum at some point, and we we want the second ; position to be tested by stringp to see if we have an "error" situation. ; Keep this in sync with bdd-mv-let. `(mvf ,mx-id ,fmt-string (cons ,fmt-alist ,bad-cst) ; The following nil is really an initial value of the bdd-call-stack that is ; ultimately to be placed in a bddnote. At the time of this writing, ; bdd-mv-let is the only place where we update this stack. nil ,ttree)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; I. INTRODUCTION AND DATA TYPES ;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; In this work we represent terms in what we call the cheap syntax. Such a ; "term" is called a "csterm". Bryant would call it a "node." ; We are interested in normalized IF expressions corresponding to ACL2 terms. ; If x is not itself an IF term, then (IF x y z) is represented by ; `(k ,x ,boolp ,y . ,z) ; where k is a natural number that is uniquely associated with and ; boolp is t if the term is known to be Boolean. The association between k and ; is arranged via a "hash table" discussed below. The objective is ; that two canonicalized IF expressions are equal (and therefore represent the ; same term) iff their unique identifiers (cars) are =. ; We also represent "leaf" ACL2 terms, which are generally IF-free, as csts of ; a slightly different sort; see below. (Note that these may have IFs in them ; because certain function symbols are "blocked" -- see bdd-constructors.) ; The list of "leaf" csts arising from variables in the input term, which we ; typically call leaf-cst-list, is passed around unchanged by various of our ; functions. We rely on the op-ht to find csts for other leaves, and to avoid ; re-consing up leaf csts. ; The shapes of csts are as follows. Note that two csts are equal iff their ; cars are =. ; Non-leaf: ; (unique-id tst boolp tbr . fbr) ; informally, represents (if tst tbr fbr) ; Leaf: ; (unique-id term boolp) ; where term is of one of the following forms: ; variable ; quotep ; application of function symbol other than IF to a list of csts ; WARNING: The definition of leafp below relies on the fact that leaf csts are ; exactly those whose final cdr is nil. Do not succomb to the temptation to ; add a new field as the final cdr without taking this into account. ; Note: It is tempting to replace the "term" in the last case by an ACL2 term, ; rather than an application of a function symbol to a list of csts. However, ; the list of csts has presumably already been consed up, so we save the ; re-consing, awaiting the final decoding to build the actual ACL2 term if ; necessary. ; Macros for accessing canonicalized IFs: (defmacro unique-id (x) `(the-fixnum (car ,x))) (defmacro tst (x) `(cadr ,x)) ;a cst, not a number; but beware since tst=trm ;and trm is a sort of term ; Note: We found that 95% of the time on one test was being spent inside ; cst-boolp, when we used to keep this information directly in leaf nodes only. (defmacro cst-boolp (x) `(caddr ,x)) (defmacro tbr (x) `(cadddr ,x)) (defmacro fbr (x) `(cddddr ,x)) (defmacro leafp (x) `(null (cdddr ,x))) (defmacro trm (x) `(cadr ,x)) (defun bdd-constructors (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld))))) (let ((pair (assoc-eq :bdd-constructors (table-alist 'acl2-defaults-table wrld)))) (if pair (cdr pair) '(cons)))) (defun make-leaf-cst (unique-id trm boolp) ; We write the definition this way, rather than simply as something like (list* ; unique-id trm boolp ), in order to avoid repeatedly consing up '(t . nil) and ; '(nil . nil). (if boolp (list* unique-id trm '(t)) (list* unique-id trm '(nil)))) (defun evg-fn-symb (x) ; This function takes the view that every explicit value can be constructed ; from 0. It returns nil on 0, but, in principle, returns an appropriate ; function symbol otherwise. At this point we choose not to support this idea ; in full, but instead we view cons as the only constructor. We leave the full ; code in place as a comment, in case we choose to support this idea in the ; future. ; (cond ((consp x) 'cons) ; ((symbolp x) 'intern-in-package-of-symbol) ; ((integerp x) ; (cond ((< x 0) 'unary--) ; ((< 0 x) 'binary-+) ; (t nil))) ; ((rationalp x) ; (if (equal (numerator x) 1) ; 'unary-/ ; 'binary-*)) ; ((complex-rationalp x) ; 'complex) ; ((stringp x) 'coerce) ; ((characterp x) 'char-code) ; (t (er hard 'fn-symb "Unexpected object, ~x0." ; x))) (cond ((consp x) 'cons) (t nil))) (defun bdd-constructor-trm-p (trm bdd-constructors) (and (consp trm) (if (fquotep trm) (member-eq (evg-fn-symb (cadr trm)) bdd-constructors) (member-eq (car trm) bdd-constructors)))) (defun evg-type (x) ; This function takes the view that every explicit value can be constructed ; from 0. It returns nil on 0, but, in principle, returns an appropriate ; function symbol otherwise. See also evg-fn-symb. (cond ((consp x) 'consp) ((symbolp x) 'symbol) ((integerp x) 'integer) ((rationalp x) 'rational) ((complex-rationalp x) 'complex-rational) ((stringp x) 'string) ((characterp x) 'character) (t (er hard 'fn-symb "Unexpected object, ~x0." x)))) (defun make-if-cst (unique-id tst tbr fbr bdd-constructors) ; The second value returned is always a new cst. The first value is non-nil ; when there is an "error", in which case that value is of the form (fmt-string ; . alist). (let* ((boolp (and (cst-boolp tbr) (cst-boolp fbr))) (new-cst (list* unique-id tst boolp tbr fbr))) (cond ((or (and (leafp tbr) (bdd-constructor-trm-p (trm tbr) bdd-constructors)) (and (leafp fbr) (bdd-constructor-trm-p (trm fbr) bdd-constructors))) (mv (msg "Attempted to create IF node during BDD processing with ~@0, ~ which would produce a non-BDD term (as defined in :DOC ~ bdd-algorithm). See :DOC show-bdd." (let ((true-fn (trm tbr)) (false-fn (trm fbr))) (cond ((and (leafp tbr) (bdd-constructor-trm-p (trm tbr) bdd-constructors)) (cond ((and (leafp fbr) (bdd-constructor-trm-p (trm fbr) bdd-constructors)) (msg "true branch with ~#0~[function symbol ~x1~/explicit ~ value of type ~x2~] and false branch with ~ ~#3~[function symbol ~x4~/explicit value of type ~ ~x5~]" (if (eq (car true-fn) 'quote) 1 0) (car true-fn) (and (eq (car true-fn) 'quote) (evg-type (cadr true-fn))) (if (eq (car false-fn) 'quote) 1 0) (car false-fn) (and (eq (car false-fn) 'quote) (evg-type (cadr false-fn))))) (t (msg "true branch with ~#0~[function symbol ~x1~/explicit ~ value of type ~x2~]" (if (eq (car true-fn) 'quote) 1 0) (car true-fn) (and (eq (car true-fn) 'quote) (evg-type (cadr true-fn))))))) (t (msg "false branch with ~#0~[function symbol ~x1~/explicit ~ value of type ~x2~]" (if (eq (car false-fn) 'quote) 1 0) (car false-fn) (and (eq (car false-fn) 'quote) (evg-type (cadr false-fn)))))))) new-cst)) (t (mv nil new-cst))))) ; We will always represent nil and t as described above. To make this work, we ; must set the initial mx-id to 2, so the next unique id generated is 3. ; It is nearly inconsequential which of t or nil has the smaller id, but we ; find it handy to give t the smaller id, as noted in a comment in the ; definition of combine-op-csts-comm. (defconst *cst-t* (make-leaf-cst 1 *t* t)) (defconst *cst-nil* (make-leaf-cst 2 *nil* t)) (defmacro cst= (cst1 cst2) `(= (unique-id ,cst1) (unique-id ,cst2))) (defmacro cst-tp (cst) `(= (unique-id ,cst) 1)) (defmacro cst-nilp (cst) `(= (unique-id ,cst) 2)) (defmacro cst-varp (cst) `(< 2 (unique-id ,cst))) (defun cst-nonnilp (cst) (and (leafp cst) (if (quotep (trm cst)) (not (cst-nilp cst)) (and (nvariablep (trm cst)) ; Consider other types here besides cons, e.g., that of numbers. We may want ; to pass in a list of functions that have been checked to have type-sets that ; are disjoint from *ts-nil* and variable-free. We would use a member-eq test ; below against such a list. This list of function symbols could be determined ; easily from the list of all function symbols in op-alist. (eq (ffn-symb (trm cst)) 'cons))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; II. OP-ALIST ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The goal of this section is to define functions op-alist and op-alist-info. ; See those definitions below for more details. Briefly, these functions ; respectively build and do lookup in a so-called op-alist, which is a list of ; entries that describe function symbols occurring in the term for which we ; want to build a bdd. (defun bool-mask1 (formals vars rune) ; Formals is the list of formals of a function symbol, and vars is a list of ; variables contained in formals such that every call of that function returns ; either t or nil, assuming that each var in vars is of boolean type. This ; function returns a list in one-one correspondence with formals (but see ; below), replacing a formal by t if it belongs to vars (thus indicating that ; this position's actual might be returned) and nil if not. Rune is a ; type-prescription record, used simply for the final cdr of the list returned ; (after all the t's and nil's have been listed as indicated above). (cond ((endp formals) rune) ((member-eq (car formals) vars) (cons t (bool-mask1 (cdr formals) vars rune))) (t (cons nil (bool-mask1 (cdr formals) vars rune))))) (defun boolean-term-var (x) ; X is a term. If x is of the form (booleanp v) or something "clearly" ; equivalent to it, return v. Otherwise return nil. (and (not (variablep x)) (not (fquotep x)) (cond ((and (eq (ffn-symb x) 'booleanp) (variablep (fargn x 1))) (fargn x 1)) ((eq (ffn-symb x) 'if) ; Check for translated version of (or (equal v t) (equal v nil)) or ; (or (equal v nil) (equal v t)). (let ((test (fargn x 1)) (tbr (fargn x 2)) (fbr (fargn x 3))) (and (not (variablep test)) (not (fquotep test)) (eq (ffn-symb test) 'equal) (let ((v (fargn test 1))) (and (variablep v) (let ((b (fargn test 2))) (and (or (equal b *t*) (equal b *nil*)) (let ((c (if (equal b *t*) *nil* *t*))) (if (and (equal test tbr) (equal fbr (fcons-term* 'equal v c))) v nil))))))))) (t nil)))) (defun boolean-hyps-vars (hyps) ; If hyps consists of terms of the form (booleanp v), or perhaps the ; equivalent, then we return a list indices of such v. (if (endp hyps) nil (let ((rst (boolean-hyps-vars (cdr hyps)))) (if (eq rst t) t (let ((v (boolean-term-var (car hyps)))) (if v (cons v rst) t)))))) (defun first-boolean-type-prescription (type-prescription-list ens formals) ; This function finds the most recent enabled type-prescription rule from the ; given list whose :basic-ts is boolean and :hyps are all of the form (booleanp ; v) or a "clearly" equivalent form, where the :term is of the form (fn ... v ; ...). It returns two values. The first is the :rune of the rule, which is ; non-nil if and only if such a rule is found. If the first value is non-nil, ; then the second value is a "mask" as described in the comment in bool-mask. (cond ((endp type-prescription-list) (mv nil nil)) ((and (enabled-numep (access type-prescription (car type-prescription-list) :nume) ens) (ts-subsetp (access type-prescription (car type-prescription-list) :basic-ts) *ts-boolean*)) (let* ((tp (car type-prescription-list)) (hyps (access type-prescription tp :hyps)) (vars (access type-prescription tp :vars))) (if hyps (let ((more-vars (boolean-hyps-vars hyps))) (if (or (eq more-vars t) (not (subsetp-eq more-vars formals))) (first-boolean-type-prescription (cdr type-prescription-list) ens formals) (mv (access type-prescription tp :rune) (union-eq vars more-vars)))) (mv (access type-prescription tp :rune) vars)))) (t (first-boolean-type-prescription (cdr type-prescription-list) ens formals)))) (defun recognizer-rune (fn recognizer-alist wrld ens) (cond ((endp recognizer-alist) nil) ((and (eq fn (access recognizer-tuple (car recognizer-alist) :fn)) (enabled-runep (access recognizer-tuple (car recognizer-alist) :rune) ens wrld)) (access recognizer-tuple (car recognizer-alist) :rune)) (t (recognizer-rune fn (cdr recognizer-alist) wrld ens)))) (defun bool-mask (fn recognizer-alist wrld ens) ; Returns a "mask" that is a suitable argument to bool-flg. Thus, this ; function returns either nil or else a mask of the form ; (list* b1 b2 ... bn rune) ; where rune is a type prescription rune and each bi is either t or nil. The ; function bool-flg will check that a given call of fn is boolean, returning ; rune if it can confirm this fact. A bi must be marked t if the conclusion ; that the call of fn is boolean requires a check that the formal corresponding ; to bi is boolean. ; We give special treatment not only to compound recognizers, but also to ; Boolean-valued primitives, since these will not generally have built-in ; type-prescription rules. (cond ((or (eq fn 'equal) (eq fn '<)) (list* nil nil *fake-rune-for-type-set*)) ((eq fn 'not) ; `Not' is so basic that we could probably skip this case, but we might as well ; handle it appropriately. (list* nil *fake-rune-for-type-set*)) (t (let ((rune (recognizer-rune fn recognizer-alist wrld ens)) (formals (formals fn wrld))) (if rune (bool-mask1 formals nil rune) (mv-let (rune vars) ; We only consider the most recent type prescription with Boolean base type. ; Some day we might consider somehow combining all such type prescription ; rules. (first-boolean-type-prescription (getprop fn 'type-prescriptions nil 'current-acl2-world wrld) ens formals) (and rune (bool-mask1 formals vars rune)))))))) (defun commutative-p1 (fn lemmas ens) ; Fn is assumed to have arity 2 in the current ACL2 world. (cond ((endp lemmas) nil) (t (if (and (member-eq (access rewrite-rule (car lemmas) :subclass) '(backchain abbreviation)) (equal (access rewrite-rule (car lemmas) :equiv) 'equal) (enabled-numep (access rewrite-rule (car lemmas) :nume) ens) (null (access rewrite-rule (car lemmas) :hyps)) (let ((lhs (access rewrite-rule (car lemmas) :lhs)) (rhs (access rewrite-rule (car lemmas) :rhs))) (and (or (eq (ffn-symb lhs) fn) (er hard 'commutative-p1 "We had thought we had a rewrite rule with :lhs ~ being a call of ~x0, but the :lhs is ~x1." fn lhs)) (nvariablep rhs) (not (fquotep rhs)) (eq (ffn-symb rhs) fn) (variablep (fargn lhs 1)) (variablep (fargn lhs 2)) (not (eq (fargn lhs 1) (fargn lhs 2))) (equal (fargn lhs 1) (fargn rhs 2)) (equal (fargn lhs 2) (fargn rhs 1))))) (access rewrite-rule (car lemmas) :rune) (commutative-p1 fn (cdr lemmas) ens))))) (defun find-equivalence-rune (fn rules) (cond ((endp rules) nil) ((eq (access congruence-rule (car rules) :equiv) fn) (let ((rune (access congruence-rule (car rules) :rune))) (if (eq (car rune) :equivalence) rune (find-equivalence-rune fn (cdr rules))))) (t (find-equivalence-rune fn (cdr rules))))) (defun equivalence-rune1 (fn congruences) ; For example, if fn is 'iff then congruences may contain: ; (EQUAL ((126 IFF :EQUIVALENCE IFF-IS-AN-EQUIVALENCE)) ; ((126 IFF :EQUIVALENCE IFF-IS-AN-EQUIVALENCE))) ; But the two singleton lists above can contain other members too. See the ; Essay on Equivalence, Refinements, and Congruence-based Rewriting. ; See add-equivalence-rule. (cond ((endp congruences) (er hard 'equivalence-rune "Failed to find an equivalence rune for function symbol ~x0." fn)) (t (let ((x (car congruences))) (case-match x (('equal rules1 rules2) (let ((rune (find-equivalence-rune fn rules1))) (if (and rune (equal rune (find-equivalence-rune fn rules2))) rune (equivalence-rune1 fn (cdr congruences))))) (& (equivalence-rune1 fn (cdr congruences)))))))) (defun equivalence-rune (fn wrld) (declare (xargs :guard (equivalence-relationp fn wrld))) (cond ((eq fn 'equal) (fn-rune-nume 'equal nil nil wrld)) (t (equivalence-rune1 fn (getprop fn 'congruences '(:error "See equivalence-rune.") 'current-acl2-world wrld))))) (defun commutative-p (fn ens wrld) ; Note that if the value is non-nil, it is a rune justifying the commutativity ; of the given function. (and (= (arity fn wrld) 2) (if (equivalence-relationp fn wrld) (equivalence-rune fn wrld) (commutative-p1 fn (getprop fn 'lemmas nil 'current-acl2-world wrld) ens)))) ; To memoize the various merging operations we will hash on the opcodes. ; Increasing each by a factor of 3 was intended to make it spread out a little ; more, but (at least this has been true at one time) it doesn't have much of ; an effect. (defun op-alist (fns acc i ens wrld) ; Build a list of entries (op opcode comm-p enabled-executable-counterpartp ; mask). The next index to use is i. Call this as in (op-alist (remove1-eq ; 'if (all-fnnames term)) nil 6 (ens state) (w state)). Keep this in sync with ; op-alist-info. ; Note that if comm-p is non-nil, it is a rune justifying the commutativity of ; the given function. Similarly, if enabled-executable-counterpartp is non-nil ; then it is an :executable-counterpart rune. (cond ((endp fns) acc) ((> i (mx-id-bound)) (er hard 'bdd "We are very surprised to see that, apparently, your problem for bdd ~ processing involves ~x0 function symbols! We cannot handle this many ~ function symbols." (/ i 3))) (t (op-alist (cdr fns) (cons (list* (car fns) i (commutative-p (car fns) ens wrld) (and (not (getprop (car fns) 'constrainedp nil 'current-acl2-world wrld)) (enabled-xfnp (car fns) ens wrld) (fn-rune-nume (car fns) nil t wrld)) (bool-mask (car fns) (global-val 'recognizer-alist wrld) wrld ens)) acc) (+ 3 i) ens wrld)))) (defun op-alist-info (fn op-alist) ; Returns (mv opcode comm-p enabled-exec-p mask). Keep this in sync with ; op-alist. (cond ((endp op-alist) (mv (er hard 'op-alist-info "Function not found: ~x0" fn) nil nil nil)) ((eq fn (caar op-alist)) (let ((info (cdar op-alist))) (mv (car info) (cadr info) (caddr info) (cdddr info)))) (t (op-alist-info fn (cdr op-alist))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; III. HASH OPERATIONS ;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro if-op-code () 3) (defmacro hash-size () ; Do not quote the body of this definition. We want it computed at ; definition/compile time! ; At one time we used a defconst here, but then we realized that we would ; (apparently, at least, in GCL) have to pay the price both of looking up the ; value of that variable and also unboxing it for fixnum operations. Although ; a little experimentation did not yield meaningful evidence that there is ; really an impact on performance, we proceed with a macro. ; WARNING: Do not increase this size too close to (fixnum-bound). See the ; warning in op-hash-index-evg. (1- (expt 2 15))) ; We have two hash arrays, 'if-ht for assigning unique-ids to csts, and 'op-ht ; for memo-izing the merge operators. In each case the array assigns "buckets" ; to indices. ; Buckets in an if-ht are lists of non-leaf csts. ; Buckets in an op-ht are lists of entries of the form (cst op . args), where ; args is a list of csts. The length of the list is the arity of op. ; Exception: op can be quote, in which case args is a list containing a single ; evg. (defmacro if-hash-index (x y z) ; Note that (+ 131 2 1) does not exceed 153. See the comment about mx-id-bound ; in op-hash-index1. There is probably nothing sacred about the choices of ; these three numbers 131, 2, and 1, although it seems good that they are ; relatively prime. `(logandf (+f (*f 131 (unique-id ,x)) (*f 2 (unique-id ,y)) (unique-id ,z)) (hash-size))) (defun op-hash-index1 (args i acc) ; There should be no more than 16 args before we "turn around", so that the ; multiplier on unique-ids is no more than (1+ (+ 2 3 ... 17)) = 153. (The ; `1+' is because in op-hash-index we add in the op-code as well. Op-codes are ; also bounded by mx-id-bound -- see op-alist -- as are of course unique-ids.) ; See the comment in mx-id-bound. ; If we want to increase the (mx-id-bound), we believe that we could start the ; "turnaround" here earlier. But we have not yet checked this claim carefully. (declare (type (signed-byte 30) i acc) (xargs :measure (acl2-count args))) (the-fixnum (cond ((endp args) (if (< acc 0) (- acc) acc)) ((or (= (the-fixnum i) 18) (= (the-fixnum i) -1)) (if (> acc 0) (op-hash-index1 args -17 acc) (op-hash-index1 args 2 acc))) (t (op-hash-index1 (cdr args) (1+f i) (+f acc (*f i (unique-id (car args))))))))) (defmacro op-hash-index (op-code args) `(logandf (+f ,op-code (op-hash-index1 ,args 2 1)) (hash-size))) (defmacro op-hash-index-2 (op-code arg1 arg2) ; This special case of op-hash-index is used for commutative operators in ; chk-memo-2. `(logandf (+f ,op-code (*f 2 (unique-id ,arg1)) (*f 3 (unique-id ,arg2))) (hash-size))) (defmacro op-hash-index-if (arg1 arg2 arg3) `(logandf (+f (if-op-code) (*f 2 (unique-id ,arg1)) (*f 3 (unique-id ,arg2)) (*f 4 (unique-id ,arg3))) (hash-size))) ; Having found the bucket associated with the hash-index, here is how ; we search it. (defun if-search-bucket (x y z lst) ; Here lst is a list of non-leaf csts. (cond ((null lst) nil) ((and (cst= x (tst (car lst))) (cst= y (tbr (car lst))) (cst= z (fbr (car lst)))) (car lst)) (t (if-search-bucket x y z (cdr lst))))) (defun cst=-lst (x y) (cond ((endp x) t) (t (and (cst= (car x) (car y)) (cst=-lst (cdr x) (cdr y)))))) (defmacro eq-op (x y) ; This test must change if we start allowing LAMBDAs as operators. `(eq ,x ,y)) (defun op-search-bucket (op args lst) ; Here op is a function symbol and lst is a tail of a bucket from an op-ht. ; Thus, lst is a list of elements of the form (cst op0 . args0), where args0 is ; a list of csts unless op0 is 'quote, which it is not if op0 is op. (cond ((null lst) nil) ((and (eq-op op (cadr (car lst))) (cst=-lst args (cddr (car lst)))) (car (car lst))) (t (op-search-bucket op args (cdr lst))))) (defun op-search-bucket-2 (op arg1 arg2 lst) ; This is a version of op-search-bucket for binary functions. This is merely ; an optimization we use for commutative operators, since we know that they are ; binary. We could of course use this for all binary operators, but the point ; here is that for commutative operators we delay consing up the commuted ; argument list until it is necessary. See combine-op-csts-comm. (cond ((null lst) nil) ((and (eq-op op (cadr (car lst))) (let ((args (cddr (car lst)))) (and (cst= arg1 (car args)) (cst= arg2 (cadr args))))) (car (car lst))) (t (op-search-bucket-2 op arg1 arg2 (cdr lst))))) (defun op-search-bucket-if (arg1 arg2 arg3 lst) ; This is a version of op-search-bucket that does not require us to cons up the ; arguments into a list, used in chk-memo-if. This is merely an optimization ; we use since IF is such a common operation. (cond ((null lst) nil) ((and (eq-op 'if (cadr (car lst))) (let ((args (cddr (car lst)))) (and (cst= arg1 (car args)) (cst= arg2 (cadr args)) (cst= arg3 (caddr args))))) (car (car lst))) (t (op-search-bucket-if arg1 arg2 arg3 (cdr lst))))) (defun chk-memo (op-code op args op-ht) ; If has an entry in op-ht, return 0 and the entry. ; Otherwise, return the hash index for (simply to avoid ; its recomputation) and NIL. Entries are of the form (result op . args). We ; return the hash index as the first value so that we can avoid boxing up a ; fixnum for it in GCL. (declare (type (signed-byte 30) op-code)) (the-mv 2 (signed-byte 30) (let ((n (op-hash-index op-code args))) (declare (type (signed-byte 30) n)) (let ((ans (op-search-bucket op args (aref1 'op-ht op-ht n)))) (cond (ans (mvf 0 ans)) (t (mvf n nil))))))) (defun chk-memo-2 (op-code op arg1 arg2 op-ht) ; This is merely an optimization of chk-memo for the case where the operator is ; binary, in particularly for commutative operators; see the comment in ; op-search-bucket-2. (declare (type (signed-byte 30) op-code)) (the-mv 2 (signed-byte 30) (let ((n (op-hash-index-2 op-code arg1 arg2))) (declare (type (signed-byte 30) n)) (let ((ans (op-search-bucket-2 op arg1 arg2 (aref1 'op-ht op-ht n)))) (cond (ans (mvf 0 ans)) (t (mvf n nil))))))) (defun chk-memo-if (arg1 arg2 arg3 op-ht) ; This is merely an optimization of chk-memo for the case where the operator is ; if, which is likely very common. Note that by treating this special case as ; we do, we avoid consing up the list of arguments in some cases; see ; combine-if-csts. (the-mv 2 (signed-byte 30) (let ((n (op-hash-index-if arg1 arg2 arg3))) (declare (type (signed-byte 30) n)) (let ((ans (op-search-bucket-if arg1 arg2 arg3 (aref1 'op-ht op-ht n)))) (cond (ans (mvf 0 ans)) (t (mvf n nil))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; IV. HASH OPERATIONS: QUOTEPS ;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defmacro half-hash-size () (floor (hash-size) 2)) (defmacro fourth-hash-size () (floor (hash-size) 4)) (defun op-hash-index-string (index acc string) (declare (type (signed-byte 30) index acc)) (the-fixnum (cond ((= index 0) acc) (t (let ((index (1- (the-fixnum index)))) (declare (type (signed-byte 30) index)) (op-hash-index-string index (logandf (hash-size) (+f acc (char-code (char string index)))) string)))))) (defun op-hash-index-evg (evg) (the-fixnum (cond ((integerp evg) (logandf (hash-size) evg)) ((rationalp evg) (logandf (hash-size) (+ (numerator evg) (* 17 (denominator evg))))) ((acl2-numberp evg) (logandf (hash-size) (+f (op-hash-index-evg (realpart evg)) (op-hash-index-evg (imagpart evg))))) ((characterp evg) (+f (fourth-hash-size) (char-code evg))) ((symbolp evg) (logandf (hash-size) ; WARNING: Here we assume that (* 19 (hash-size)) is a fixnum. We know it is ; because the hash index is at most (hash-size), which is well under ; (fixnum-bound). (*f 19 (op-hash-index-evg (symbol-name evg))))) ((stringp evg) (the-fixnum (op-hash-index-string (the-fixnum! (length evg) 'bdd) (half-hash-size) evg))) (t ;cons (logandf (hash-size) (+f (op-hash-index-evg (car evg)) (*f 2 (op-hash-index-evg (cdr evg))))))))) (defun op-search-bucket-quote (evg bucket) (cond ((null bucket) nil) ((and (eq-op 'quote (cadr (car bucket))) (equal evg (caddr (car bucket)))) (car (car bucket))) (t (op-search-bucket-quote evg (cdr bucket))))) (defun chk-memo-quotep (term op-ht) (the-mv 2 (signed-byte 30) (let ((n (op-hash-index-evg (cadr term)))) (declare (type (signed-byte 30) n)) (let ((ans (op-search-bucket-quote (cadr term) (aref1 'op-ht op-ht n)))) ; One might think that the calls of the-fixnum just below are not necessary, ; but in fact they do appear to produce better compiled code in GCL. (cond (ans (mvf 0 ans)) (t (mvf n nil))))))) (defun bdd-quotep (term op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (the-mv 3 (signed-byte 30) (cond ((equal term *t*) (mvf mx-id *cst-t* op-ht)) ((equal term *nil*) (mvf mx-id *cst-nil* op-ht)) (t (mv-let (hash-index ans) (chk-memo-quotep term op-ht) (declare (type (signed-byte 30) hash-index)) (cond (ans (mvf mx-id ans op-ht)) (t (let ((new-mx-id (1+mx-id mx-id))) (declare (type (signed-byte 30) new-mx-id)) (let ((new-cst (make-leaf-cst new-mx-id term nil))) (mvf new-mx-id new-cst (aset1 'op-ht op-ht hash-index (cons ; The following is the same as (list new-cst 'quote (cadr term)), but saves a ; cons. (cons new-cst term) (aref1 'op-ht op-ht hash-index))))))))))))) (defmacro bdd-quotep+ (term op-ht if-ht mx-id ttree) `(mv-let (mx-id cst op-ht) (bdd-quotep ,term ,op-ht ,mx-id) (declare (type (signed-byte 30) mx-id)) (mvf mx-id cst op-ht ,if-ht ,ttree))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; V. BDD RULES AND ONE-WAY UNIFIER ;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We could just use the rewrite-rule data structures already existing in the ; ACL2 world. However, we suspect that it is a good idea, in order to support ; computationally intensive bdd computations, to avoid having to look at the ; enabled structure or dig deep into a rewrite rule in order to find the ; various fields we need. In fact, we want to have the lhs available as ; quickly as possible, since that field is used the most. (defrec bdd-rule (lhs rhs . rune) t) (defun rewrite-rule-to-bdd-rule (lemma) (make bdd-rule :lhs (access rewrite-rule lemma :lhs) :rhs (access rewrite-rule lemma :rhs) :rune (access rewrite-rule lemma :rune))) (defun bdd-rules-alist1 (recp lemmas ens all-fns nondef-rules def-rules new-fns) ; This function returns lists of definitional and non-definitional bdd-rules ; corresponding to the lemmas of a given function symbol. The arguments are as ; follows. ; recp: True when the top-level function symbol for the lemmas is recursive ; lemmas: The rewrite-rule structures that we want to convert to bdd-rules ; ens: The current enabled structure ; all-fns: List of all function symbols already encountered in bdd rules built ; nondef-rules: Bdd-rules accumulated so far not from definition rules ; def-rules: Bdd-rules accumulated so far from definition rules ; new-fns: List of function symbols to be added to all-fns (an accumulator) ; At this point, we do not support backchaining: that is, we assume that each ; rule has :hyps field of NIL. We also do not allow free variables in the ; :rhs, and we require :lhs to be a function symbol call. We also require a ; null loop-stopper (:heuristic-info for subclass 'backchain), rather than ; attempting to control looping during the bdd computation. Perhaps some of ; these restrictions can be removed after some thought and additional ; implementation work. ; We require that the :rhs only contain function symbols that are known in the ; op-alist. In order to enforce this requirement, we simply pass back two ; values: a list of new function symbols to consider (i.e., ones not in ; all-fns that occur in :rhs fields) and the list of bdd-rules. ; As noted in a comment in bdd-rules-alist, the lists of lemmas returned by ; this function need to be reversed, because they have the oldest rules at the ; front. That could easily be changed, though the natural way to do this would ; presumably render this function non-tail recursive. At this point the issue ; seems sufficiently minor that we are satisfied to leave things this way. (cond ((endp lemmas) (mv new-fns nondef-rules def-rules)) (t (let ((subclass (access rewrite-rule (car lemmas) :subclass))) (cond ((and (eq (access rewrite-rule (car lemmas) :equiv) 'equal) (enabled-numep (access rewrite-rule (car lemmas) :nume) ens) (case subclass (definition (and (null recp) (null (access rewrite-rule (car lemmas) :hyps)) (subsetp-eq (all-vars (access rewrite-rule (car lemmas) :rhs)) (all-vars (access rewrite-rule (car lemmas) :lhs))))) (abbreviation (subsetp-eq (all-vars (access rewrite-rule (car lemmas) :rhs)) (all-vars (access rewrite-rule (car lemmas) :lhs)))) (backchain (and (null (access rewrite-rule (car lemmas) :hyps)) (null (access rewrite-rule (car lemmas) :heuristic-info)) (subsetp-eq (all-vars (access rewrite-rule (car lemmas) :rhs)) (all-vars (access rewrite-rule (car lemmas) :lhs))))) (otherwise nil))) (bdd-rules-alist1 recp (cdr lemmas) ens all-fns (if (eq subclass 'definition) nondef-rules (cons (rewrite-rule-to-bdd-rule (car lemmas)) nondef-rules)) (if (eq subclass 'definition) (cons (rewrite-rule-to-bdd-rule (car lemmas)) def-rules) def-rules) (union-eq (set-difference-eq (all-fnnames (access rewrite-rule (car lemmas) :rhs)) all-fns) new-fns))) (t (bdd-rules-alist1 recp (cdr lemmas) ens all-fns nondef-rules def-rules new-fns))))))) (defun extra-rules-for-bdds (fn wrld) ; We include certain trivial rewrite rules regardless of whether there are ; explicit rewrite rules that corrrespond to them. (cond ((eq fn 'equal) (list (make rewrite-rule :nume nil :hyps nil :equiv 'equal ; Rockwell Addition: I have totally stripped out all vestiges of the ; aborted attempt to implement :OUTSIDE-IN rewrite rules. I won't comment ; on subsequent differences of this sort. :subclass 'backchain :heuristic-info nil :backchain-limit-lst *initial-default-backchain-limit* :rune *fake-rune-for-anonymous-enabled-rule* :lhs (fcons-term* 'equal *nil* 'x) :var-info t :rhs (fcons-term* 'if 'x *nil* *t*)) (make rewrite-rule :nume nil :hyps nil :equiv 'equal :subclass 'backchain :heuristic-info nil :backchain-limit-lst *initial-default-backchain-limit* :rune *fake-rune-for-anonymous-enabled-rule* :lhs (fcons-term* 'equal 'x *nil*) :var-info t :rhs (fcons-term* 'if 'x *nil* *t*)))) ((equivalence-relationp fn wrld) ; We do not need to include reflexivity when fn is 'equal, because it is ; hardwired into the definition of combine-op-csts. (list (make rewrite-rule :nume nil :hyps nil :equiv 'equal :subclass 'abbreviation :heuristic-info nil :backchain-limit-lst *initial-default-backchain-limit* :rune (equivalence-rune fn wrld) :lhs (fcons-term* fn 'x 'x) :var-info t :rhs *t*))) ((eq fn 'mv-nth) (list (make rewrite-rule :nume nil :hyps nil :equiv 'equal :subclass 'backchain :heuristic-info nil :backchain-limit-lst *initial-default-backchain-limit* :rune (fn-rune-nume 'mv-nth nil nil wrld) :lhs (fcons-term* 'mv-nth 'n (fcons-term* 'cons 'x 'y)) :var-info t ; (if (zp n) x (mv-nth (- n 1) y)) :rhs (fcons-term* 'if (fcons-term* 'zp 'n) 'x (fcons-term* 'mv-nth (fcons-term* 'binary-+ 'n (kwote -1)) 'y))))) (t nil))) (defun bdd-rules-alist (fns all-fns bdd-rules-alist ens wrld) ; Call this with a list fns of function symbols that does not include 'if, and ; all-fns the result of adding 'if to that list. The parameter bdd-rules-alist ; is the accumulator, initially nil. ; WARNING: Be sure to modify this function to account for hypotheses if we ; implement conditional rewriting with BDDs. ; Invariant: fns is a subset of all-fns. This is important for not just ; termination, but in fact to guarantee that the same function (car fns) is ; never processed twice by bdd-rules-alist1. ; NOTE: Do we store entries when there are no rules, or not? Not. Suppose ; there are p elements of fns with a non-nil set of rules and q elements of fns ; with a nil set of rules. Then the average number of CDRs required for lookup ; (assuming each function symbol is looked up the same number of times) is ; roughly (p+q)/2 if we store entries for nil sets of rules; and if we don't, ; it's: [1/(p+q)]*(p*p/2 + q*p), which equals [p/2(p+q)]*(p + 2q). ; Now we can see that we're better off the second way, not storing nil entries: ; p+q >= [p/(p+q)]*(p + 2q) ? ; (p+q)^2 >= p^2 + 2pq ? ; q^2 >= 0 ! ; Yes, in fact the inequality is strict if q > 0. (cond ((endp fns) (mv all-fns bdd-rules-alist)) (t (mv-let (new-fns nondef-rules def-rules) (bdd-rules-alist1 (recursivep (car fns) wrld) (append (getprop (car fns) 'lemmas nil 'current-acl2-world wrld) (extra-rules-for-bdds (car fns) wrld)) ens (cons (car fns) all-fns) nil nil nil) (cond ((or def-rules nondef-rules) (bdd-rules-alist (append new-fns (cdr fns)) (append new-fns all-fns) (cons (cons (car fns) ; The calls of reverse below ensure that rules will be tried in the appropriate ; order, i.e., most recent ones first. See the comment in bdd-rules-alist1. (cons (reverse nondef-rules) (reverse def-rules))) bdd-rules-alist) ens wrld)) ; Otherwise do not store an entry for (car fns) in bdd-rules-alist, as argued ; in the comment above. (t (bdd-rules-alist (cdr fns) all-fns bdd-rules-alist ens wrld))))))) ; We now adapt ACL2's one-way-unifier for terms to the realms of csts. (defmacro one-way-unify1-cst-2 (mx-id p1 p2 cst1 cst2 alist op-ht) `(mv-let (mx-id ans alist1 op-ht) (one-way-unify1-cst ,mx-id ,p1 ,cst1 ,alist ,op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mv-let (mx-id ans alist2 op-ht) (one-way-unify1-cst mx-id ,p2 ,cst2 alist1 op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mvf mx-id t alist2 op-ht)) (t (mvf mx-id nil ,alist op-ht))))) (t (mvf mx-id nil ,alist op-ht))))) (defmacro one-way-unify1-cst-3 (mx-id p1 p2 p3 cst1 cst2 cst3 alist op-ht) `(mv-let (mx-id ans alist2 op-ht) (one-way-unify1-cst-2 ,mx-id ,p1 ,p2 ,cst1 ,cst2 ,alist ,op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mv-let (mx-id ans alist3 op-ht) (one-way-unify1-cst mx-id ,p3 ,cst3 alist2 op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mvf mx-id t alist3 op-ht)) (t (mvf mx-id nil ,alist op-ht))))) (t (mvf mx-id nil ,alist op-ht))))) (mutual-recursion ; The following functions are adapted from one-way-unify1 and the like. (defun one-way-unify1-cst (mx-id pat cst alist op-ht) (declare (type (signed-byte 30) mx-id)) (the-mv 4 (signed-byte 30) (cond ((variablep pat) (let ((pair (assoc-eq pat alist))) (cond (pair (cond ((cst= (cdr pair) cst) (mvf mx-id t alist op-ht)) (t (mvf mx-id nil alist op-ht)))) (t (mvf mx-id t (cons (cons pat cst) alist) op-ht))))) ((not (leafp cst)) (cond ((fquotep pat) (mvf mx-id nil alist op-ht)) ((eq (ffn-symb pat) 'if) (one-way-unify1-cst-3 mx-id (fargn pat 1) (fargn pat 2) (fargn pat 3) (tst cst) (tbr cst) (fbr cst) alist op-ht)) (t (mvf mx-id nil alist op-ht)))) (t (let ((term (trm cst))) (cond ((fquotep pat) (cond ((equal pat term) (mvf mx-id t alist op-ht)) (t (mvf mx-id nil alist op-ht)))) ((variablep term) (mvf mx-id nil alist op-ht)) ((fquotep term) ;term is not a term, but fquotep is ok here (cond ((acl2-numberp (cadr term)) (let ((ffn-symb (ffn-symb pat))) (case ffn-symb (binary-+ (cond ((quotep (fargn pat 1)) (mv-let (mx-id cst op-ht) (bdd-quotep (kwote (- (cadr term) (fix (cadr (fargn pat 1))))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (one-way-unify1-cst mx-id (fargn pat 2) cst alist op-ht))) ((quotep (fargn pat 2)) (mv-let (mx-id cst op-ht) (bdd-quotep (kwote (- (cadr term) (fix (cadr (fargn pat 2))))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (one-way-unify1-cst mx-id (fargn pat 1) cst alist op-ht))) (t (mvf mx-id nil alist op-ht)))) (binary-* (cond ((and (quotep (fargn pat 1)) (integerp (cadr (fargn pat 1))) (> (abs (cadr (fargn pat 1))) 1)) (mv-let (mx-id cst op-ht) (bdd-quotep (kwote (/ (cadr term) (cadr (fargn pat 1)))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (one-way-unify1-cst mx-id (fargn pat 2) cst alist op-ht))) ((and (quotep (fargn pat 2)) (integerp (cadr (fargn pat 2))) (> (abs (cadr (fargn pat 2))) 1)) (mv-let (mx-id cst op-ht) (bdd-quotep (kwote (/ (cadr term) (cadr (fargn pat 2)))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (one-way-unify1-cst mx-id (fargn pat 1) cst alist op-ht))) (t (mvf mx-id nil alist op-ht)))) (unary-- (cond ((>= (+ (realpart (cadr term)) (imagpart (cadr term))) 0) (mvf mx-id nil alist op-ht)) (t (mv-let (mx-id cst op-ht) (bdd-quotep (kwote (- (cadr term))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (one-way-unify1-cst mx-id (fargn pat 1) cst alist op-ht))))) (unary-/ (cond ((or (>= (* (cadr term) (conjugate (cadr term))) 1) (eql 0 (cadr term))) (mvf mx-id nil alist op-ht)) (t (mv-let (mx-id cst op-ht) (bdd-quotep (kwote (/ (cadr term))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (one-way-unify1-cst mx-id (fargn pat 1) cst alist op-ht))))) (otherwise (mvf mx-id nil alist op-ht))))) ; We try to avoid some complications by avoiding intern-in-package-of-symbol ; and coerce for now. We are not aware of any reason why they should present ; undue difficulties, however. ((consp (cadr term)) (cond ((eq (ffn-symb pat) 'cons) ; We have to be careful with alist below so we are a no change loser. (mv-let (mx-id cst1 op-ht) (bdd-quotep (kwote (car (cadr term))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (mv-let (mx-id ans alist1 op-ht) (one-way-unify1-cst mx-id (fargn pat 1) cst1 alist op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mv-let (mx-id cst2 op-ht) (bdd-quotep (kwote (cdr (cadr term))) op-ht mx-id) (declare (type (signed-byte 30) mx-id)) (mv-let (mx-id ans alist2 op-ht) (one-way-unify1-cst mx-id (fargn pat 2) cst2 alist1 op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mvf mx-id t alist2 op-ht)) (t (mvf mx-id nil alist op-ht)))))) (t (mvf mx-id nil alist op-ht)))))) (t (mvf mx-id nil alist op-ht)))) (t (mvf mx-id nil alist op-ht)))) ((eq (ffn-symb pat) (ffn-symb term)) ; Note: We do not allow lambda expressions at this point. If that changes, ; then we should consider that case too. (cond ((eq (ffn-symb pat) 'equal) (one-way-unify1-cst-equal mx-id (fargn pat 1) (fargn pat 2) (fargn term 1) (fargn term 2) alist op-ht)) (t (mv-let (mx-id ans alist1 op-ht) (one-way-unify1-cst-lst mx-id (fargs pat) (fargs term) alist op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mvf mx-id t alist1 op-ht)) (t (mvf mx-id nil alist op-ht))))))) (t (mvf mx-id nil alist op-ht)))))))) (defun one-way-unify1-cst-lst (mx-id pl cstl alist op-ht) ; This function is NOT a No Change Loser. (declare (type (signed-byte 30) mx-id)) (the-mv 4 (signed-byte 30) (cond ((null pl) (mvf mx-id t alist op-ht)) (t (mv-let (mx-id ans alist op-ht) (one-way-unify1-cst mx-id (car pl) (car cstl) alist op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (one-way-unify1-cst-lst mx-id (cdr pl) (cdr cstl) alist op-ht)) (t (mvf mx-id nil alist op-ht)))))))) (defun one-way-unify1-cst-equal (mx-id pat1 pat2 cst1 cst2 alist op-ht) (declare (type (signed-byte 30) mx-id)) (the-mv 4 (signed-byte 30) (mv-let (mx-id ans alist op-ht) (one-way-unify1-cst-2 mx-id pat1 pat2 cst1 cst2 alist op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mvf mx-id t alist op-ht)) (t (one-way-unify1-cst-2 mx-id pat2 pat1 cst1 cst2 alist op-ht)))))) ) (defun some-one-way-unify-cst-lst (cst-lst rules op-ht mx-id ttree) (declare (type (signed-byte 30) mx-id)) (the-mv 6 (signed-byte 30) (cond ((endp rules) (mvf mx-id nil nil nil op-ht ttree)) (t (mv-let (mx-id ans alist op-ht) (one-way-unify1-cst-lst mx-id (fargs (access bdd-rule (car rules) :lhs)) cst-lst nil op-ht) (declare (type (signed-byte 30) mx-id)) (cond (ans (mvf mx-id t (access bdd-rule (car rules) :rhs) alist op-ht (push-lemma (access bdd-rule (car rules) :rune) ttree))) (t (some-one-way-unify-cst-lst cst-lst (cdr rules) op-ht mx-id ttree)))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; VI. SOME INTERFACE UTILITIES ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We will ignore declaration opportunities in this section, especially for ; declaring mx-id to be a fixnum, because efficiency is a minor issue here. (defun leaf-cst-list (lst bool-vars acc mx-id) ; Here lst is a list of variables from the input term. Returns a list of leaf ; csts for those variables, i.e., elements of the form (unique-id variable ; bool), where if bool is t then variable is known to be Boolean. (cond ((endp lst) (mv mx-id acc)) (t (mv-let (mx-id acc) (cond ((assoc-eq (car lst) acc) (mv mx-id acc)) (t (let ((new-mx-id (1+mx-id mx-id))) (mv new-mx-id (cons (make-leaf-cst new-mx-id (car lst) (member-eq (car lst) bool-vars)) acc))))) (leaf-cst-list (cdr lst) bool-vars acc mx-id))))) (mutual-recursion (defun decode-cst (cst cst-array) ; This takes a cst and returns a term and an updated cst-array, whose nth entry ; is the decoding of the cst with unique id n. (let ((term (aref1 'cst-array cst-array (unique-id cst)))) (cond (term (mv term cst-array)) ((leafp cst) (cond ((or (variablep (trm cst)) (fquotep (trm cst))) (mv (trm cst) cst-array)) (t (mv-let (args cst-array) (decode-cst-lst (fargs (trm cst)) cst-array) (let ((x (cons-term (ffn-symb (trm cst)) args))) (mv x (aset1 'cst-array cst-array (unique-id cst) x))))))) (t (mv-let (tst cst-array) (decode-cst (tst cst) cst-array) (mv-let (tbr cst-array) (decode-cst (tbr cst) cst-array) (mv-let (fbr cst-array) (decode-cst (fbr cst) cst-array) (let ((x (mcons-term* 'if tst tbr fbr))) (mv x (aset1 'cst-array cst-array (unique-id cst) x)))))))))) (defun decode-cst-lst (cst-lst cst-array) (cond ((endp cst-lst) (mv nil cst-array)) (t (mv-let (first cst-array) (decode-cst (car cst-lst) cst-array) (mv-let (rest cst-array) (decode-cst-lst (cdr cst-lst) cst-array) (mv (cons first rest) cst-array)))))) ) (defun decode-cst-alist1 (alist cst-array) (cond ((endp alist) (mv nil cst-array)) (t (mv-let (term cst-array) (decode-cst (cdar alist) cst-array) (mv-let (rest cst-array) (decode-cst-alist1 (cdr alist) cst-array) (mv (cons (list (caar alist) term) rest) cst-array)))))) (defun decode-cst-alist (cst-alist cst-array) (mv-let (alist cst-array) (decode-cst-alist1 cst-alist cst-array) (declare (ignore cst-array)) alist)) (defun leaf-cst-list-array (mx-id) (let ((dim (1+ mx-id))) (compress1 'cst-array `((:header :dimensions (,dim) :maximum-length ,(+ 10 dim) :default nil))))) (defconst *some-non-nil-value* "Some non-nil value") (defun falsifying-assignment1 (cst acc cst-array) ; Returns a list of doublets (var bool) that provide an environment for ; falsifying the given cst. Also returns a new cst-array; we have to do that ; so that we always pass in the "current" cst-array, in order to avoid slow ; array access. (cond ((cst-nilp cst) (mv acc cst-array)) ((quotep (trm cst)) (mv (er hard 'falsifying-assignment1 "Tried to falsify ~x0!" (trm cst)) cst-array)) ((leafp cst) (mv-let (term cst-array) (decode-cst cst cst-array) (mv (cons (list term nil) acc) cst-array))) ((cst-nonnilp (tbr cst)) (mv-let (term cst-array) (decode-cst (tst cst) cst-array) (falsifying-assignment1 (fbr cst) (cons (list term nil) acc) cst-array))) (t (mv-let (term cst-array) (decode-cst (tst cst) cst-array) (falsifying-assignment1 (tbr cst) (cons (list term (if (cst-boolp (tst cst)) t *some-non-nil-value*)) acc) cst-array))))) (defun falsifying-assignment (cst mx-id) (mv-let (asst cst-array) (falsifying-assignment1 cst nil (leaf-cst-list-array mx-id)) (declare (ignore cst-array)) (reverse asst))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; VII. MAIN ALGORITHM ;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun make-if (mx-id n op args x y z op-ht if-ht bdd-constructors) ; This function returns either ; (mvf mx-id cst op-ht if-ht) ; or (culling this from an "erroneous" return of make-if-cst below) ; (mvf mx-id fmt-string fmt-alist bad-cst) ; Intuitively, this function makes a cst representing (IF x y z). But ; we know that this is the answer to the merge op(args) and we ; know that n is the hash index of . We know ; is not in the op-ht. We first look in the if-ht to ; see if (IF x y z) is there. If so, we return it. If not, we build ; an appropriate one, assigning the next unique id, which is (1+ ; mx-id), and add it to the if-ht. In any case, before returning, we ; store the returned cst as the answer for op(arg1,...) in op-ht. We ; thus have to return four results: the new mx-id, the cst, and the ; two hash arrays. (declare (type (signed-byte 30) n mx-id)) (the-mv 4 (signed-byte 30) (cond ((cst= y z) (mvf mx-id y ; The following aset1 was added after Moore's first presentation of this ; work. Its absence was discovered during a code-walk with Jim ; Bitner. The times improved slightly on most examples, except mul08 ; where we lost a few more seconds. The times shown in ; ~moore/text/pc-hacking.mss -- the most recent version of a talk on ; this work -- have been updated to show the performance of this ; version of the code. (aset1 'op-ht op-ht n (cons (list* y op args) (aref1 'op-ht op-ht n))) if-ht)) (t (let ((m (if-hash-index x y z))) (declare (type (signed-byte 30) m)) (let* ((bucket (aref1 'if-ht if-ht m)) (old-if (if-search-bucket x y z bucket))) (cond (old-if (mvf mx-id old-if (aset1 'op-ht op-ht n (cons (list* old-if op args) (aref1 'op-ht op-ht n))) if-ht)) ((and (cst-tp y) (cst-nilp z) (cst-boolp x)) (mvf mx-id x (aset1 'op-ht op-ht n (cons (list* x op args) (aref1 'op-ht op-ht n))) if-ht)) (t (let ((mx-id (1+mx-id mx-id))) (declare (type (signed-byte 30) mx-id)) (mv-let (erp new-if) (make-if-cst mx-id x y z bdd-constructors) (cond (erp (mvf mx-id (car erp) ;fmt-string (cdr erp) ;fmt-alist new-if ;bad cst )) (t (mvf mx-id new-if (aset1 'op-ht op-ht n (cons (list* new-if op args) (aref1 'op-ht op-ht n))) (aset1 'if-ht if-ht m (cons new-if bucket))))))))))))))) (defun make-if-no-memo (mx-id x y z op-ht if-ht bdd-constructors) ; Same as make-if, except that we do not change op-ht, and we assume that y and ; z are already known to be distinct. (declare (type (signed-byte 30) mx-id)) (the-mv 4 (signed-byte 30) (let ((m (if-hash-index x y z))) (declare (type (signed-byte 30) m)) (let* ((bucket (aref1 'if-ht if-ht m)) (old-if (if-search-bucket x y z bucket))) (cond (old-if (mvf mx-id old-if op-ht if-ht)) ((and (cst-tp y) (cst-nilp z) (cst-boolp x)) (mvf mx-id x op-ht if-ht)) (t (let ((mx-id (1+mx-id mx-id))) (declare (type (signed-byte 30) mx-id)) (mv-let (erp new-if) (make-if-cst mx-id x y z bdd-constructors) (cond (erp (mvf mx-id (car erp) ;fmt-string (cdr erp) ;fmt-alist new-if ;bad cst )) (t (mvf mx-id new-if op-ht (aset1 'if-ht if-ht m (cons new-if bucket))))))))))))) (defmacro split-var (cst) ; The variable to split on from cst. If cst is a leaf, then we only split on ; it if it is a cst-varp (i.e., not the representation of T or NIL) and is ; known to be Boolean. `(if (leafp ,cst) (if (and (cst-varp ,cst) (cst-boolp ,cst)) ,cst nil) (tst ,cst))) (defun min-var (acc args) ; Args is a list of csts. We return nil if there is no variable to split on. ; Otherwise, we return the leaf cst with the smallest unique-id. Call this ; with acc = nil. (declare (xargs :measure (acl2-count args))) (if (endp args) acc (let ((var (split-var (car args)))) (if (null var) (min-var acc (cdr args)) (min-var (cond ((null acc) var) ((< (unique-id var) (unique-id acc)) var) (t acc)) (cdr args)))))) (defun combine-op-csts1 (var-id args) ; Args is a list of csts, and var-id is the unique-id of a term that is not ; necessarily Boolean-valued. We return (mv true-branch-args ; false-branch-args), where under the assumption that var-id is the unique id ; of a term that is not (semantically) nil, args represents the same list of ; terms as true-branch-args; and under the assumption that var-id is the unique ; id of a term that (semantically) equals nil, args represents the same list of ; terms as false-branch-args. (declare (type (signed-byte 30) var-id)) (if (endp args) (mv nil nil) (mv-let (x y) (combine-op-csts1 var-id (cdr args)) (cond ((leafp (car args)) (if (and (= (the-fixnum var-id) (unique-id (car args))) ; Even though we are splitting on var-id, we need to know that it is the unique ; id of a boolean variable in order to simplify as shown below. Note that ; var-id need only be the unique-id of a Boolean cst when split-var returns it ; by virtue of its being a leaf; it could be non-Boolean if split-var ; encounters it as a test. (cst-boolp (car args))) (mv (cons *cst-t* x) (cons *cst-nil* y)) (mv (cons (car args) x) (cons (car args) y)))) (t (if (= (the-fixnum var-id) (unique-id (tst (car args)))) (mv (cons (tbr (car args)) x) (cons (fbr (car args)) y)) (mv (cons (car args) x) (cons (car args) y)))))))) (defun bool-flg (args mask) ; Checks that for each "bit" set in mask, the corresponding arg in args is ; known to be Boolean. In the case that mask is (typically) from a type ; prescription, this allows us to conclude, assuming that the given function ; symbol's base type is Boolean, then the application of that function to args ; is Boolean. ; If this function returns a non-nil value, then that value is a type ; prescription rune. (cond ((endp args) ; Then mask is a type prescription rune. mask) ((car mask) (and (cst-boolp (car args)) (bool-flg (cdr args) (cdr mask)))) (t (bool-flg (cdr args) (cdr mask))))) (defun some-bdd-constructorp (args bdd-constructors) (cond ((endp args) nil) (t (or (and (leafp (car args)) (bdd-constructor-trm-p (trm (car args)) bdd-constructors)) (some-bdd-constructorp (cdr args) bdd-constructors))))) (defun combine-op-csts-simple (hash-index op mask args op-ht if-ht mx-id bdd-constructors ttree) ; Make a new leaf-cst for (op . args). Note: this function returns an "error" ; in the sense described in the bdd nest if the call attempts to build a ; non-bdd-constructor node when some argument is a bdd-constructor. Pass in ; bdd-constructors = nil if no such attempt is possible; otherwise, we know ; that op is not a member of bdd-constructors. (declare (type (signed-byte 30) hash-index mx-id)) (the-mv 5 (signed-byte 30) (let ((new-mx-id (1+mx-id mx-id)) (rune (and mask ; If mask is non-nil, we guarantee that op corresponds to a function whose type ; is Boolean modulo that mask (for its type prescription). (bool-flg args mask)))) (declare (type (signed-byte 30) new-mx-id)) (let ((new-cst (make-leaf-cst new-mx-id (cons op args) rune))) (cond ((and bdd-constructors ; We presumably know that (not (member-eq op bdd-constructors)). (some-bdd-constructorp args bdd-constructors)) (bdd-error new-mx-id "Attempted to create ~x0 node during BDD processing with an argument ~ that is a call of ~#1~[a bdd-constructor~/CONS~], which would ~ produce a non-BDD term (as defined in :DOC bdd-algorithm). See ~ :DOC show-bdd." (list (cons #\0 op) (cons #\1 (if (equal bdd-constructors '(cons)) 1 0))) new-cst ttree)) (t (mvf new-mx-id new-cst (aset1 'op-ht op-ht hash-index (cons (list* new-cst op args) (aref1 'op-ht op-ht hash-index))) if-ht (if rune (push-lemma rune ttree) ttree)))))))) (defmacro bdd-mv-let (vars form body) ; The idea here is that we want to allow functions in the bdd nest to return ; multiple values of the sort returned by the macro bdd-error. ; Combine-if-csts+ gets special treatment. ; This macro should only be used when the first var has a fixnum value. We go ; even further by requiring that the first var be mx-id. Whenever we write ; (bdd-mv-let vars form body) ; we assume that body returns the same number of values as does form. ; Keep this in sync with bdd-error, as indicated in a comment below. The code ; below is the only place, as of this writing, where we update the ; bdd-call-stack. (declare (xargs :guard (and (true-listp vars) (eq (car vars) 'mx-id) (< 2 (length vars)) (consp form) (true-listp form) (member-eq (car form) '(combine-if-csts+ combine-op-csts combine-op-csts+ combine-op-csts-guts combine-op-csts-comm bdd bdd-alist bdd-list))))) `(mv-let ,vars ,form (declare (type (signed-byte 30) mx-id)) (if (stringp ,(cadr vars)) ,(cond ((eq (car form) 'bdd) ; Vars is of the form returned by bdd-error: ; (mv mx-id fmt-string (cons fmt-alist bad-cst) call-stack ttree). ; We want to push the current term onto the call-stack. (list 'mvf (car vars) (cadr vars) (caddr vars) (list 'cons ; Keep this in sync with the definition of bdd. Here, (cadr form) is really ; the first argument of bdd, which should be a term, and (caddr form) is the ; second argument, which should be an alist. The cons we generate here is the ; new value of the call-stack. (list 'cons (cadr form) (caddr form)) (cadddr vars)) (cadddr (cdr vars)))) (t ; Then vars represents an "error", and we want to return an error of the same ; sort. This sort will be different for combine-if-csts+ than for the other ; allowable functions (from the guard expresssion above), but no matter. (cons 'mvf vars))) ,body))) (defmacro combine-if-csts+ (cst1 cst2 cst3 op-ht if-ht mx-id bdd-constructors) `(cond ((cst-nilp ,cst1) (mvf ,mx-id ,cst3 ,op-ht ,if-ht)) ((cst-nonnilp ,cst1) (mvf ,mx-id ,cst2 ,op-ht ,if-ht)) (t (combine-if-csts ,cst1 ,cst2 ,cst3 ,op-ht ,if-ht ,mx-id ,bdd-constructors)))) (defun combine-if-csts1 (var-id args) ; This function is identical to combine-op-csts1, except that the op is ; assumed to be IF. (declare (type (signed-byte 30) var-id)) (mv-let (x y) (combine-op-csts1 var-id (cdr args)) (cond ((leafp (car args)) (if (= (the-fixnum var-id) (unique-id (car args))) (mv (cons *cst-t* x) (cons *cst-nil* y)) (mv (cons (car args) x) (cons (car args) y)))) (t (if (= (the-fixnum var-id) (unique-id (tst (car args)))) (mv (cons (tbr (car args)) x) (cons (fbr (car args)) y)) (mv (cons (car args) x) (cons (car args) y))))))) (defun combine-if-csts (test-cst true-cst false-cst op-ht if-ht mx-id bdd-constructors) ; Similarly to the bdd nest, this function returns either ; (mvf mx-id cst op-ht if-ht) ; or ; (mvf mx-id fmt-string (cons fmt-alist bad-cst) nil). ; We assume here that test-cst is not the cst of a quotep, and that the input ; csts are really all csts (not error strings). (declare (type (signed-byte 30) mx-id)) (the-mv 4 (signed-byte 30) (cond ((cst= true-cst false-cst) (mvf mx-id true-cst op-ht if-ht)) ((cst= test-cst false-cst) (combine-if-csts test-cst true-cst *cst-nil* op-ht if-ht mx-id bdd-constructors)) ((and (cst= test-cst true-cst) (cst-boolp true-cst)) (combine-if-csts test-cst *cst-t* false-cst op-ht if-ht mx-id bdd-constructors)) ((and (cst-nilp false-cst) (if (cst-tp true-cst) (cst-boolp test-cst) (cst= test-cst true-cst))) (mvf mx-id test-cst op-ht if-ht)) (t (let ((true-var (split-var true-cst)) (false-var (split-var false-cst))) (cond ((and (leafp test-cst) (or (null true-var) (< (unique-id test-cst) (unique-id true-var))) (or (null false-var) (< (unique-id test-cst) (unique-id false-var)))) ; Then the test is the appropriate variable to split on for building a bdd, so ; we proceed to build a bdd. Some test data suggests that it is more efficient ; to avoid op-ht memoization in this case; it makes sense anyhow that if-ht ; memoization would suffice here. After all, very little work would be done ; inbetween looking in the op-ht and looking in the if-ht. So, we neither ; consult nor use the op-ht when the test-cst is already in the right position. (make-if-no-memo mx-id test-cst true-cst false-cst op-ht if-ht bdd-constructors)) (t (mv-let (hash-index ans) (chk-memo-if test-cst true-cst false-cst op-ht) (declare (type (signed-byte 30) hash-index)) (cond (ans (mvf mx-id ans op-ht if-ht)) (t (let* ((args (list test-cst true-cst false-cst)) (min-var (min-var nil args))) ; Note that min-var is non-nil; otherwise split-var returns nil for each arg, ; and the previous case would apply. (mv-let (args1 args2) (combine-if-csts1 (unique-id min-var) args) (bdd-mv-let (mx-id cst1 op-ht if-ht) (combine-if-csts+ (car args1) (cadr args1) (caddr args1) op-ht if-ht mx-id bdd-constructors) (bdd-mv-let (mx-id cst2 op-ht if-ht) (combine-if-csts+ (car args2) (cadr args2) (caddr args2) op-ht if-ht mx-id bdd-constructors) (make-if mx-id hash-index 'if args min-var cst1 cst2 op-ht if-ht bdd-constructors))))))))))))))) (defun cst-list-to-evg-list (cst-lst) (cond ((endp cst-lst) nil) (t (cons (cadr (trm (car cst-lst))) (cst-list-to-evg-list (cdr cst-lst)))))) (defun cst-quote-listp (cst-lst) (cond ((endp cst-lst) t) ((and (leafp (car cst-lst)) (quotep (trm (car cst-lst)))) (cst-quote-listp (cdr cst-lst))) (t nil))) (defrec bddspv ; Bddspv stands for "BDD special variables", in analogy to pspv. We simply ; prefer not to pass around such long argument lists. In addition, we expect ; the code to be easier to modify this way; for example, the addition of ; bdd-constructors as a field in the bddspv avoids the need to massive ; modification of function calls. (op-alist bdd-rules-alist . bdd-constructors) t) (defun bdd-ev-fncall (mx-id hash-index op mask args op-ht if-ht bdd-constructors rune ttree state) (declare (type (signed-byte 30) hash-index mx-id)) (the-mv 5 (signed-byte 30) (mv-let (erp val latches) (ev-fncall op (cst-list-to-evg-list args) state nil nil nil) (declare (ignore latches)) (cond (erp ; How can this case happen? Ev-fncall can only "return an error" if there is a ; guard violation (not possible in this context) or a call of a constrained ; function (introduced locally in an encapsulate or introduced by defchoose). ; Although we have guaranteed that op is not constrained (see the code for ; op-alist), still the body of op could contain calls of constrained functions. (combine-op-csts-simple hash-index op mask args op-ht if-ht mx-id (and (not (member-eq op bdd-constructors)) ; See the comment in combine-op-csts-simple. The idea is that if op is in ; bdd-constructors, then we may suppress a certain check. bdd-constructors) ttree)) (t (bdd-quotep+ (list 'quote val) op-ht if-ht mx-id (push-lemma rune ttree))))))) (defmacro combine-op-csts+ (mx-id comm-p enabled-exec-p op-code op mask args op-ht if-ht op-bdd-rules ttree bddspv) ; In combine-op-csts-guts we want to call either combine-op-csts or ; combine-op-csts-comm, depending on the comm-p argument. It would be slightly ; more efficient if we simply had two versions of combine-op-csts-guts: one ; that calls combine-op-csts and one that calls combine-op-csts-comm. But the ; savings seems quite trivial, so we devise this macro to call the appropriate ; function. `(if ,comm-p (combine-op-csts-comm ,mx-id ,comm-p ,enabled-exec-p ,op-code ,op ,mask (car ,args) (cadr ,args) ,args ,op-ht ,if-ht ,op-bdd-rules ,ttree ,bddspv state) (combine-op-csts ,mx-id ,enabled-exec-p ,op-code ,op ,mask ,args ,op-ht ,if-ht ,op-bdd-rules ,ttree ,bddspv state))) (defun make-if-for-op (mx-id hash-index op args test-cst true-cst false-cst op-ht if-ht bdd-constructors) (declare (type (signed-byte 30) hash-index mx-id)) (the-mv 4 (signed-byte 30) (cond ((cst= true-cst false-cst) ; Keep this case in sync with make-if. (mvf mx-id true-cst (aset1 'op-ht op-ht hash-index (cons (list* true-cst op args) (aref1 'op-ht op-ht hash-index))) if-ht)) ((let ((true-split-var (split-var true-cst)) (false-split-var (split-var false-cst)) (test-id (unique-id test-cst))) (declare (type (signed-byte 30) test-id)) (and (or (null true-split-var) (< test-id (unique-id true-split-var))) (or (null false-split-var) (< test-id (unique-id false-split-var))))) (make-if mx-id hash-index op args test-cst true-cst false-cst op-ht if-ht bdd-constructors)) (t (bdd-mv-let (mx-id cst op-ht if-ht) (combine-if-csts+ test-cst true-cst false-cst op-ht if-ht mx-id bdd-constructors) (mvf mx-id cst (aset1 'op-ht op-ht hash-index (cons (list* cst op args) (aref1 'op-ht op-ht hash-index))) if-ht)))))) (mutual-recursion ; All functions in this nest return either ; (mvf mx-id cst op-ht if-ht ttree) ; or (as returned by bdd-error) ; (mvf mx-id fmt-string (fmt-alist . bad-cst) call-stack ttree) (defun combine-op-csts (mx-id enabled-exec-p op-code op mask args op-ht if-ht op-bdd-rules ttree bddspv state) ; Returns a cst for (op . args). For special treatment of the case where the ; operator is commutative, in order to avoid some consing, use ; combine-op-csts-comm. (declare (type (signed-byte 30) op-code mx-id)) (the-mv 5 (signed-byte 30) (mv-let (hash-index ans) (chk-memo op-code op args op-ht) (declare (type (signed-byte 30) hash-index)) (cond (ans (mvf mx-id ans op-ht if-ht ttree)) ((and enabled-exec-p (cst-quote-listp args)) (bdd-ev-fncall mx-id hash-index op mask args op-ht if-ht (access bddspv bddspv :bdd-constructors) enabled-exec-p ttree state)) ((and (eq op 'booleanp) (cst-boolp (car args))) (mvf mx-id *cst-t* op-ht if-ht (push-lemma (fn-rune-nume 'booleanp nil nil (w state)) ttree))) (t (combine-op-csts-guts mx-id nil hash-index enabled-exec-p op-code op mask args op-ht if-ht op-bdd-rules ttree bddspv state)))))) (defun combine-op-csts-comm (mx-id comm-p enabled-exec-p op-code op mask arg1 arg2 args op-ht if-ht op-bdd-rules ttree bddspv state) ; Returns a cst for (op arg1 arg2) where op is commutative and comm-p is a rune ; justifying commutativity of op. ; When args is non-nil, it is (list arg1 arg2). The idea is to avoid making a ; cons when possible. (declare (type (signed-byte 30) op-code mx-id)) (the-mv 5 (signed-byte 30) (cond ((and (eq op 'equal) (cst= arg1 arg2)) ; Alternatively, we could wait until after the chk-memo-2 test below. But in ; that case, we should make the appropriate entry in the op-ht so that we don't ; waste our time the next time the same call of 'equal arises, looking for an ; entry in op-ht that has not been (and will never be) put there. But we ; prefer to avoid the op-ht entirely in this trivial case, and also avoid the ; computations having to do with commutativity. ; Actually, a few experiments suggest that we should have left this branch ; where it was, jut before the next branch involving 'equal. But that makes no ; sense! Since the performance degradation seemed to be at most a couple of ; percent, we'll leave it this way for now. (mvf mx-id *cst-t* op-ht if-ht (push-lemma (fn-rune-nume 'equal nil nil (w state)) ttree))) (t (mv-let (arg1 arg2 args ttree) (cond ((and (quotep arg2) (not (quotep arg1))) (mv arg2 arg1 nil (push-lemma comm-p ttree))) ((< (unique-id arg2) (unique-id arg1)) (mv arg2 arg1 nil (push-lemma comm-p ttree))) (t (mv arg1 arg2 args ttree))) (mv-let (hash-index ans) (chk-memo-2 op-code op arg1 arg2 op-ht) (declare (type (signed-byte 30) hash-index)) (cond (ans (mvf mx-id ans op-ht if-ht ttree)) ((and (eq op 'equal) (cst-tp arg1) (cst-boolp arg2)) ; Note: We are tempted to worry about the term (equal 'nil 't), which would ; not get caught by this case and hence, we fret, could fall through to a call ; of bdd-ev-fncall (which may be rather slower than we wish). However, since ; the unique id is 1 for T and 2 for NIL, and we have already commuted the args ; if necessary, then there is nothing to worry about. (mvf mx-id arg2 op-ht if-ht (push-lemma (fn-rune-nume 'equal nil nil (w state)) ttree))) ((and enabled-exec-p (quotep (trm arg1)) (quotep (trm arg2))) (bdd-ev-fncall mx-id hash-index op mask (or args (list arg1 arg2)) op-ht if-ht (access bddspv bddspv :bdd-constructors) enabled-exec-p ttree state)) (t (combine-op-csts-guts mx-id comm-p hash-index enabled-exec-p op-code op mask ; It is tempting to avoid consing up the following list, just in case it will ; be torn apart again. However, this list is the one that is ultimately ; memoized, so we need it anyhow. (or args (list arg1 arg2)) op-ht if-ht op-bdd-rules ttree bddspv state))))))))) (defun combine-op-csts-guts (mx-id comm-p hash-index enabled-exec-p op-code op mask args op-ht if-ht op-bdd-rules ttree bddspv state) ; Note that op-bdd-rules is a pair of the form (bdd-lemmas . bdd-defs). These ; are all the bdd rules that rewrite calls of the function symbol op. (declare (type (signed-byte 30) op-code mx-id hash-index)) (the-mv 5 (signed-byte 30) (mv-let (mx-id ans rhs alist op-ht ttree) (some-one-way-unify-cst-lst args (car op-bdd-rules) op-ht mx-id ttree) (declare (type (signed-byte 30) mx-id)) (cond (ans (bdd-mv-let (mx-id cst op-ht if-ht ttree) (bdd rhs alist op-ht if-ht mx-id ttree bddspv state) ; We could consider avoiding the following memoization for the application of ; lemmas. The "be" benchmarks suggest mixed results. (mvf mx-id cst (aset1 'op-ht op-ht hash-index (cons (list* cst op args) (aref1 'op-ht op-ht hash-index))) if-ht ttree))) (t (let ((bdd-constructors (access bddspv bddspv :bdd-constructors))) (cond ((member-eq op bdd-constructors) ; Then build a leaf node. We do not distribute IF through calls of ; bdd-constructors. (combine-op-csts-simple hash-index op mask args op-ht if-ht mx-id nil ttree)) (t (mv-let (mx-id ans rhs alist op-ht ttree) (some-one-way-unify-cst-lst args (cdr op-bdd-rules) op-ht mx-id ttree) (declare (type (signed-byte 30) mx-id)) (cond (ans (bdd-mv-let (mx-id cst op-ht if-ht ttree) (bdd rhs alist op-ht if-ht mx-id ttree bddspv state) ; We could consider avoiding the following memoization for the application of ; definitions. The "be" benchmarks suggest mixed results. (mvf mx-id cst (aset1 'op-ht op-ht hash-index (cons (list* cst op args) (aref1 'op-ht op-ht hash-index))) if-ht ttree))) (t (let ((min-var (min-var nil args))) ; There is certainly a potential here for more case splitting than me might ; desire. For, notice that min-var could be non-nil even though all of the ; args are leaves, because split-var (called by min-var) is happy to return a ; leaf that is known to be Boolean (and not t or nil). However, our current ; model of how OBDDs will be used suggests that we rarely get to this part of ; the code anyhow, because operators not belonging to bdd-constructors will ; have been expanded away using rewrite rules or definitions. So, we see no ; need at this point to take pains to avoid case splitting. Instead, we prefer ; to err on the side of "completeness". (cond ((null min-var) (combine-op-csts-simple hash-index op mask args op-ht if-ht mx-id ; At this point we know that op is a not a member of bdd-constructors. So we ; must pass in bdd-constructors here rather than nil. See the comment in ; combine-op-csts-simple. bdd-constructors ttree)) (t (mv-let (args1 args2) (combine-op-csts1 (unique-id min-var) args) ; Collect args1 for the true branch and args2 for the false branch. For ; example, (foo x0 (if min-var x1 x2) (if min-var x3 x4)) yields ; (mv (list x0 x1 x3) (list x0 x2 x4)). More reifically: ; (combine-op-csts1 3 '((4 x0 t) ; (9 (3 y t) t (5 x1 t) . (6 x2 t)) ; (10 (3 y t) t (7 x3 t) . (8 x4 t)))) ; is equal to ; (mv ((4 X0 T) (5 X1 T) (7 X3 T)) ; ((4 X0 T) (6 X2 T) (8 X4 T))) (bdd-mv-let (mx-id cst1 op-ht if-ht ttree) (combine-op-csts+ mx-id comm-p enabled-exec-p op-code op mask args1 op-ht if-ht op-bdd-rules ttree bddspv) (bdd-mv-let (mx-id cst2 op-ht if-ht ttree) (combine-op-csts+ mx-id comm-p enabled-exec-p op-code op mask args2 op-ht if-ht op-bdd-rules ttree bddspv) (mv-let (mx-id ans op-ht if-ht) (make-if-for-op mx-id hash-index op args min-var cst1 cst2 op-ht if-ht bdd-constructors) (declare (type (signed-byte 30) mx-id)) (cond ((stringp ans) (bdd-error mx-id ans op-ht if-ht ttree)) (t (mvf mx-id ans op-ht if-ht ttree))))))))))))))))))))) (defun bdd (term alist op-ht if-ht mx-id ttree bddspv state) (declare (xargs :measure (acl2-count term) :guard (pseudo-termp term)) (type (signed-byte 30) mx-id)) (the-mv 5 (signed-byte 30) (cond ((variablep term) (mvf mx-id (or (cdr (assoc-eq term alist)) (er hard 'bdd "Didn't find variable ~x0!" term)) op-ht if-ht ttree)) ((fquotep term) (cond ((eq (cadr term) t) (mvf mx-id *cst-t* op-ht if-ht ttree)) ((eq (cadr term) nil) (mvf mx-id *cst-nil* op-ht if-ht ttree)) (t (bdd-quotep+ term op-ht if-ht mx-id ttree)))) ((or (eq (ffn-symb term) 'if) (eq (ffn-symb term) 'if*)) (bdd-mv-let (mx-id test-cst op-ht if-ht ttree) (bdd (fargn term 1) alist op-ht if-ht mx-id ; We will need to note the use of if* eventually, so for simplicity we do it ; now. (if (eq (ffn-symb term) 'if) ttree (push-lemma (fn-rune-nume 'if* nil nil (w state)) ttree)) bddspv state) ; Note that we don't simply call combine-if-csts+, because we want to avoid ; applying bdd to one of the branches if the test already decides the issue. (cond ((cst-nilp test-cst) (bdd (fargn term 3) alist op-ht if-ht mx-id ttree bddspv state)) ((cst-nonnilp test-cst) (bdd (fargn term 2) alist op-ht if-ht mx-id ttree bddspv state)) ((eq (ffn-symb term) 'if*) (bdd-error mx-id "Unable to resolve test of IF* for term~|~%~p0~|~%under the ~ bindings~|~%~x1~|~%-- use SHOW-BDD to see a backtrace." (list (cons #\0 (untranslate term nil (w state))) (cons #\1 (decode-cst-alist alist (leaf-cst-list-array mx-id)))) ; We need a cst next, though we don't care about it. *cst-t* ttree)) (t (bdd-mv-let (mx-id true-cst op-ht if-ht ttree) (bdd (fargn term 2) alist op-ht if-ht mx-id ttree bddspv state) (bdd-mv-let (mx-id false-cst op-ht if-ht ttree) (bdd (fargn term 3) alist op-ht if-ht mx-id ttree bddspv state) (mv-let (mx-id cst op-ht if-ht) (combine-if-csts test-cst true-cst false-cst op-ht if-ht mx-id (access bddspv bddspv :bdd-constructors)) (declare (type (signed-byte 30) mx-id)) (cond ((stringp cst) (bdd-error mx-id cst op-ht if-ht ttree)) (t (mvf mx-id cst op-ht if-ht ttree)))))))))) ((flambdap (ffn-symb term)) (bdd-mv-let (mx-id alist op-ht if-ht ttree) (bdd-alist (lambda-formals (ffn-symb term)) (fargs term) alist op-ht if-ht mx-id ttree bddspv state) (bdd (lambda-body (ffn-symb term)) alist op-ht if-ht mx-id ttree bddspv state))) (t (mv-let (opcode comm-p enabled-exec-p mask) (op-alist-info (ffn-symb term) (access bddspv bddspv :op-alist)) (declare (type (signed-byte 30) opcode)) (cond (comm-p (bdd-mv-let (mx-id arg1 op-ht if-ht ttree) (bdd (fargn term 1) alist op-ht if-ht mx-id ttree bddspv state) (bdd-mv-let (mx-id arg2 op-ht if-ht ttree) (bdd (fargn term 2) alist op-ht if-ht mx-id ttree bddspv state) (combine-op-csts-comm mx-id comm-p enabled-exec-p opcode (ffn-symb term) mask arg1 arg2 nil op-ht if-ht (cdr (assoc-eq (ffn-symb term) (access bddspv bddspv :bdd-rules-alist))) ttree bddspv state)))) (t (bdd-mv-let (mx-id lst op-ht if-ht ttree) (bdd-list (fargs term) alist op-ht if-ht mx-id ttree bddspv state) (combine-op-csts mx-id enabled-exec-p opcode (ffn-symb term) mask ; For a first cut I'll keep this simple. Later, we may want to avoid consing ; up lst in the first place if we're only going to mess with it. lst op-ht if-ht (cdr (assoc-eq (ffn-symb term) (access bddspv bddspv :bdd-rules-alist))) ttree bddspv state))))))))) (defun bdd-alist (formals actuals alist op-ht if-ht mx-id ttree bddspv state) (declare (type (signed-byte 30) mx-id)) (the-mv 5 (signed-byte 30) (cond ((endp formals) (mvf mx-id nil op-ht if-ht ttree)) (t (bdd-mv-let (mx-id bdd op-ht if-ht ttree) (bdd (car actuals) alist op-ht if-ht mx-id ttree bddspv state) (bdd-mv-let (mx-id rest-alist op-ht if-ht ttree) (bdd-alist (cdr formals) (cdr actuals) alist op-ht if-ht mx-id ttree bddspv state) (mvf mx-id (cons (cons (car formals) bdd) rest-alist) op-ht if-ht ttree))))))) (defun bdd-list (lst alist op-ht if-ht mx-id ttree bddspv state) (declare (type (signed-byte 30) mx-id)) (the-mv 5 (signed-byte 30) (cond ((endp lst) (mvf mx-id nil op-ht if-ht ttree)) (t (bdd-mv-let (mx-id bdd op-ht if-ht ttree) (bdd (car lst) alist op-ht if-ht mx-id ttree bddspv state) (bdd-mv-let (mx-id rest op-ht if-ht ttree) (bdd-list (cdr lst) alist op-ht if-ht mx-id ttree bddspv state) (mvf mx-id (cons bdd rest) op-ht if-ht ttree))))))) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; VIII. TOP-LEVEL (INTERFACE) ROUTINES ;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We will ignore declaration opportunities in this section, especially for ; declaring mx-id to be a fixnum, because efficiency is a minor issue at this ; level. ; See axioms.lisp for the definition of if*. (defun if-ht-max-length (state) (if (f-boundp-global 'if-ht-max-length state) (f-get-global 'if-ht-max-length state) (+ 100000 (hash-size)))) (defun op-ht-max-length (state) (if (f-boundp-global 'op-ht-max-length state) (f-get-global 'op-ht-max-length state) (+ 100000 (hash-size)))) (defun leaf-cst-list-to-alist (leaf-cst-list) ; Leaf-cst-list is a list of leaf csts of the form (unique-id var bool-flg). ; We return a corresponding alist in which each variable is paired with its ; cst. (cond ((endp leaf-cst-list) nil) (t (cons (cons (trm (car leaf-cst-list)) (car leaf-cst-list)) (leaf-cst-list-to-alist (cdr leaf-cst-list)))))) #+(and gcl (not acl2-loop-only)) (defvar *request-bigger-fixnum-table* (fboundp 'system::allocate-bigger-fixnum-range)) (defun bdd-top (term input-vars bool-vars bdd-constructors cl-id ens state) ; This function returns a bddnote, where if an "error" occurs then the cst is ; nil. This bddnote has an empty :term field. ; Input-vars should be the list of all variables, with the highest priority ; variables (those which will have the lowest unique-ids) listed first. At any ; rate, all variables in bool-vars are to be considered Boolean-valued. ; This function is allowed to assume that we are in a context where only ; propositional equivalence need be maintained. (let* ((fns (all-fnnames term)) (wrld (w state))) (mv-let (fns bdd-rules-alist) (bdd-rules-alist (remove1-eq 'if fns) (add-to-set-eq 'if fns) nil ens wrld) (let ((op-alist (op-alist fns nil 6 ens wrld)) (if-ht (compress1 'if-ht `((:header :dimensions (,(1+ (hash-size))) :maximum-length ,(if-ht-max-length state) :default nil)))) (op-ht (compress1 'op-ht `((:header :dimensions (,(1+ (hash-size))) :maximum-length ,(op-ht-max-length state) :default nil)))) (all-vars (let ((term-vars (reverse (all-vars term)))) ; So, term-vars has the variables in print order of first occurrence, a very ; unsatisfying but very simple heuristic. (cond ((not (symbol-listp input-vars)) (er hard 'bdd-top "The second argument of BDD-TOP must ~ be a list of variables, but ~x0 is ~ not." input-vars)) ((subsetp-eq term-vars input-vars) input-vars) (t (er hard 'bdd-top "The following variables are free ~ in the input term, ~x0, but not do ~ not occur in the specified input ~ variables, ~x1: ~x2." term input-vars (set-difference-eq term-vars input-vars))))))) #+(and (not acl2-loop-only) akcl) (cond ((and (fboundp 'si::sgc-on) (funcall 'si::sgc-on)) (fms "NOTE: Turning off SGC. If you wish to turn SGC ~ back on again, execute (SI::SGC-ON T) in raw Lisp.~|" nil (standard-co *the-live-state*) *the-live-state* nil) (funcall 'si::sgc-on nil))) #+(and gcl (not acl2-loop-only)) (cond (*request-bigger-fixnum-table* (allocate-fixnum-range 0 (hash-size)) (setq *request-bigger-fixnum-table* nil))) (mv-let (mx-id leaf-cst-list) (leaf-cst-list all-vars bool-vars nil (max (unique-id *cst-nil*) (unique-id *cst-t*))) (mv-let (mx-id cst op-ht if-ht ttree) (bdd term (leaf-cst-list-to-alist leaf-cst-list) op-ht if-ht mx-id nil (make bddspv :op-alist op-alist :bdd-rules-alist bdd-rules-alist :bdd-constructors bdd-constructors) state) (cond ((stringp cst) ; Then we actually have ; (mv mx-id fmt-string (cons fmt-alist bad-cst) call-stack ttree). (make bddnote :cl-id cl-id :goal-term term :mx-id mx-id :err-string cst :fmt-alist (car op-ht) :cst (cdr op-ht) :term nil :bdd-call-stack if-ht :ttree ttree)) (t (make bddnote :cl-id cl-id :goal-term term :mx-id mx-id :err-string nil :fmt-alist nil :cst cst :term nil :bdd-call-stack nil :ttree ttree))))))))) (defun get-bool-vars (vars type-alist ttree acc) (cond ((endp vars) (mv acc ttree)) (t (let ((entry ; We use the low-level function assoc-eq here so that it is clear we are not ; depending on the ACL2 world. (assoc-eq (car vars) type-alist))) (cond ((and entry (ts-subsetp (cadr entry) *ts-boolean*)) (get-bool-vars (cdr vars) type-alist (cons-tag-trees (cddr entry) ttree) (cons (car vars) acc))) (t (get-bool-vars (cdr vars) type-alist ttree acc))))))) (defun bdd-clause1 (hint-alist type-alist cl position ttree0 cl-id ens wrld state) ; Returns (mv hitp x y), where: ; if hitp is 'error then x is a msg and y is nil or a bddnote; ; if hitp is 'miss then x is nil and y is a bddnote; ; else hitp is 'hit, in which case x is a list of clauses and y is a ttree. (let* ((term (case position (:conc (mcons-term* 'if (car (last cl)) *t* *nil*)) (:all (mcons-term* 'if (disjoin cl) *t* *nil*)) (otherwise (let ((lit (nth position cl))) (case-match lit (('not x) (mcons-term* 'if x *t* *nil*)) (& (mcons-term* 'not lit))))))) (all-vars (all-vars term)) (vars-hint (cdr (assoc-eq :vars hint-alist))) (prove-hint (if (assoc-eq :prove hint-alist) (cdr (assoc-eq :prove hint-alist)) t)) (bdd-constructors-hint (if (assoc-eq :bdd-constructors hint-alist) (cdr (assoc-eq :bdd-constructors hint-alist)) (bdd-constructors wrld)))) (mv-let (bool-vars ttree1) (get-bool-vars all-vars type-alist ttree0 nil) (cond ((not (subsetp-eq (if (eq vars-hint t) all-vars vars-hint) bool-vars)) (let ((bad-vars (set-difference-eq (if (eq vars-hint t) all-vars vars-hint) bool-vars))) (mv 'error (msg "The following variable~#0~[ is~/s are~] not known to be ~ Boolean by trivial (type set) reasoning: ~&0. Perhaps you ~ need to add hypotheses to that effect. Alternatively, you ~ may want to prove :type-prescription rules (see :DOC ~ type-prescription) or :forward-chaining (see :DOC ~ forward-chaining) rules to help with the situation, or ~ perhaps to start with the hint ~x1." bad-vars (list :cases (if (consp (cdr bad-vars)) (list (cons 'and (pairlis$ (make-list (length bad-vars) :initial-element 'booleanp) (pairlis$ bad-vars nil)))) `((booleanp ,(car bad-vars)))))) nil))) (t (let* ((real-vars-hint (if (eq vars-hint t) nil vars-hint)) (bddnote (bdd-top term (append real-vars-hint (set-difference-eq (reverse all-vars) real-vars-hint)) bool-vars bdd-constructors-hint cl-id ens state)) (cst (access bddnote bddnote :cst)) (err-string (access bddnote bddnote :err-string)) (ttree (access bddnote bddnote :ttree))) (cond (err-string ; An error occurred; we aborted the bdd computation. (if prove-hint (mv 'error (cons (access bddnote bddnote :err-string) (access bddnote bddnote :fmt-alist)) bddnote) (mv 'miss nil bddnote))) ((cst-tp cst) (mv 'hit nil (add-to-tag-tree 'bddnote bddnote (cons-tag-trees ttree ttree1)))) (prove-hint (mv 'error (list "The :BDD hint for the current goal has ~ successfully simplified this goal, but has ~ failed to prove it. Consider using (SHOW-BDD) ~ to suggest a counterexample; see :DOC show-bdd.") bddnote)) (t (mv-let (new-term cst-array) (decode-cst cst (leaf-cst-list-array (access bddnote bddnote :mx-id))) (declare (ignore cst-array)) (let* ((bddnote (change bddnote bddnote :term new-term)) (ttree (add-to-tag-tree 'bddnote bddnote (cons-tag-trees ttree ttree1)))) (cond ((eq position :conc) (mv 'hit (list (add-literal new-term (butlast cl 1) t)) ttree)) ((eq position :all) (mv 'hit (list (add-literal new-term nil nil)) ttree)) (t ; hypothesis (mv 'hit (list (subst-for-nth-arg (dumb-negate-lit new-term) position cl)) ttree))))))))))))) (defmacro expand-and-or-simple+ (term bool fns-to-be-ignored-by-rewrite wrld ttree) ; Unlike expand-and-or-simple, the list of terms (second value) returned by ; this macro is always ``correct,'' and the hitp value is always non-nil. `(mv-let (hitp lst ttree1) (expand-and-or-simple ,term ,bool ,fns-to-be-ignored-by-rewrite ,wrld ,ttree) (cond (hitp (mv hitp lst ttree1)) (t (mv t (list ,term) ,ttree))))) (defun expand-and-or-simple (term bool fns-to-be-ignored-by-rewrite wrld ttree) ; See the comment in expand-clause. This is a simple version of expand-and-or ; that does not expand abbreviations or, in fact, use lemmas at all (just the ; definitions of NOT, IF, and IMPLIES). We expand the top-level fn symbol of ; term provided the expansion produces a conjunction -- when bool is nil -- or ; a disjunction -- when bool is t. We return three values: a hitp flag ; denoting success, the resulting list of terms (to be conjoined or disjoined ; to produce a term equivalent to term), and a new ttree. If the hitp flag is ; nil then we make no guarantees about the ``resulting list of terms,'' which ; in fact (for efficiency) is typically nil. ; Note that this function only guarantees propositional (iff) equivalence of ; term with the resulting conjunction or disjunction. (cond ((variablep term) (mv nil nil ttree)) ((fquotep term) (cond ((equal term *nil*) (if bool (mv t nil ttree) (mv nil nil ttree))) ((equal term *t*) (if bool (mv nil nil ttree) (mv t nil ttree))) (t (if bool (mv t (list *t*) ttree) (mv t nil ttree))))) ((member-equal (ffn-symb term) fns-to-be-ignored-by-rewrite) (mv nil nil ttree)) ((flambda-applicationp term) ; We don't use (and-orp (lambda-body (ffn-symb term)) bool) here because that ; approach ignores nested lambdas. (mv-let (hitp lst ttree0) (expand-and-or-simple (lambda-body (ffn-symb term)) bool fns-to-be-ignored-by-rewrite wrld ttree) (cond (hitp (mv hitp (subcor-var-lst (lambda-formals (ffn-symb term)) (fargs term) lst) ttree0)) (t (mv nil nil ttree))))) ((eq (ffn-symb term) 'not) (mv-let (hitp lst ttree0) (expand-and-or-simple (fargn term 1) (not bool) fns-to-be-ignored-by-rewrite wrld ttree) (cond (hitp (mv hitp (dumb-negate-lit-lst lst) (push-lemma (fn-rune-nume 'not nil nil wrld) ttree0))) (t (mv nil nil ttree))))) ((and (eq (ffn-symb term) 'implies) bool) (expand-and-or-simple (subcor-var (formals 'implies wrld) (fargs term) (body 'implies t wrld)) bool fns-to-be-ignored-by-rewrite wrld (push-lemma (fn-rune-nume 'implies nil nil wrld) ttree))) ((eq (ffn-symb term) 'if) (let ((t1 (fargn term 1)) (t2 (fargn term 2)) (t3 (fargn term 3))) (cond ((or (equal t1 *nil*) (equal t2 t3)) (expand-and-or-simple+ t3 bool fns-to-be-ignored-by-rewrite wrld ttree)) ((quotep t1) (expand-and-or-simple+ t2 bool fns-to-be-ignored-by-rewrite wrld ttree)) ((and (quotep t2) (quotep t3)) (cond ((equal t2 *nil*) ; We already know t2 is not t3, so we have t3 = *t* up to iff-equivalence, and ; hence we are looking at (not t1) up to iff-equivalence. (mv-let (hitp lst ttree) (expand-and-or-simple t1 (not bool) fns-to-be-ignored-by-rewrite wrld ttree) (mv t (if hitp (dumb-negate-lit-lst lst) (list (dumb-negate-lit t1))) ttree))) ((equal t3 *nil*) (expand-and-or-simple+ t1 bool fns-to-be-ignored-by-rewrite wrld ttree)) (t (expand-and-or-simple *t* bool fns-to-be-ignored-by-rewrite wrld ttree)))) ((and (quotep t3) (eq (not bool) (equal t3 *nil*))) ; We combine the cases (or (not t1) t2) [bool = t] and (and t1 t2) [bool = ; nil]. (mv-let (hitp lst1 ttree) (expand-and-or-simple+ t1 nil fns-to-be-ignored-by-rewrite wrld ttree) (declare (ignore hitp)) (mv-let (hitp lst2 ttree) (expand-and-or-simple+ t2 bool fns-to-be-ignored-by-rewrite wrld ttree) (declare (ignore hitp)) (if bool (mv t (union-equal (dumb-negate-lit-lst lst1) lst2) ttree) (mv t (union-equal lst1 lst2) ttree))))) ((and (quotep t2) (eq (not bool) (equal t2 *nil*))) ; We combine the cases (or t1 t3) [bool = t] and (and (not t1) t3) ; [bool = nil]. (mv-let (hitp lst1 ttree) (expand-and-or-simple+ t1 t fns-to-be-ignored-by-rewrite wrld ttree) (declare (ignore hitp)) (mv-let (hitp lst2 ttree) (expand-and-or-simple+ t3 bool fns-to-be-ignored-by-rewrite wrld ttree) (declare (ignore hitp)) (if bool (mv t (union-equal lst1 lst2) ttree) (mv t (union-equal (dumb-negate-lit-lst lst1) lst2) ttree))))) (t (mv nil nil ttree))))) (t (mv nil nil ttree)))) (defun expand-clause (cl fns-to-be-ignored-by-rewrite wrld ttree acc) ; A classic form for a bdd problem is something like the following. ; (let ((x (list x0 x1 ...)) ; (implies (boolean-listp x) ; ...) ; How do we let the bdd package know that x0, x1, ... are Boolean? It needs to ; know that x really is (list x0 x1 ...), and then it needs to forward-chain ; from (boolean-listp (list x0 x1 ...)) to the conjunction of (booleanp xi). ; However, the clause handed to bdd-clause may be a one-element clause with the ; literal displayed above, so here we "flatten" this literal into a clause that ; is more amenable to forward-chaining. (cond ((endp cl) (mv acc ttree)) (t (mv-let (hitp lst ttree) (expand-and-or-simple+ (car cl) t fns-to-be-ignored-by-rewrite wrld ttree) (declare (ignore hitp)) (expand-clause (cdr cl) fns-to-be-ignored-by-rewrite wrld ttree (union-equal lst acc)))))) (defun bdd-clause (bdd-hint cl-id top-clause pspv wrld state) ; This function is analogous to simplify-clause (except that bdd-hint is passed ; in here), and shares much code with simplify-clause1. It is separated out ; from apply-top-hints-clause for readability. We return 4 values, as required ; by apply-top-hints-clause. ; Unlike simplify-clause1, we do not call ok-to-force, but instead we do not ; force during forward-chaining. We may want to revisit that decision, but ; for now, we prefer to minimize forcing during bdd processing. (let ((rcnst (access prove-spec-var pspv :rewrite-constant)) (literal-hint (or (cdr (assoc-eq :literal bdd-hint)) :all))) (cond ((and (integerp literal-hint) ; Note that literal-hint is a 0-based index; see translate-bdd-hint1. We know ; that literal-hint is non-negative, translate-bdd-hint1 never returns a ; negative literal-hint. (not (< literal-hint (1- (length top-clause))))) (mv 'error (msg "There ~#0~[are no hypotheses~/is only one hypothesis~/are only ~ ~n0 hypotheses~] in this goal, but your :BDD hint suggested ~ that there would be at least ~x1 ~ ~#1~[~/hypothesis~/hypotheses]." (1- (length top-clause)) (1+ literal-hint)) nil pspv)) (t (mv-let (hitp current-clause current-clause-pts remove-trivial-equivalences-ttree) (remove-trivial-equivalences top-clause (enumerate-elements top-clause 0) t (access rewrite-constant rcnst :current-enabled-structure) wrld state nil nil nil) (declare (ignore hitp)) (let ((position (cond ((integerp literal-hint) (position literal-hint current-clause-pts)) (t literal-hint)))) (cond ((or (null position) (and (eq literal-hint :conc) (not (member (1- (length top-clause)) current-clause-pts)))) (mv 'error (msg "The attempt to use a :BDD hint for the goal named ~ \"~@0\" has failed. The problem is that the hint ~ specified that BDD processing was to be used on ~ the ~#1~[~n2 hypothesis~/conclusion~], which has ~ somehow disappeared. One possibility is that this ~ literal is an equivalence that has disappeared ~ after being used for substitution into the rest of ~ the goal. Another possibility is that this ~ literal has merged with another. We suspect, ~ therefore, that you would benefit by reconsidering ~ this :BDD hint, possibly attaching it to a ~ subsequent goal instead." (tilde-@-clause-id-phrase cl-id) (if (null position) 0 1) (if (null position) (1+ literal-hint) 0)) nil pspv)) (t (let ((ens (access rewrite-constant rcnst :current-enabled-structure))) (mv-let (expanded-clause ttree) (expand-clause current-clause (access rewrite-constant rcnst :fns-to-be-ignored-by-rewrite) wrld remove-trivial-equivalences-ttree nil) (mv-let (contradictionp type-alist fc-pair-lst) (forward-chain-top 'bdd-clause expanded-clause nil nil ; Let's not force t ; do-not-reconsiderp wrld ens (match-free-override wrld) state) (cond (contradictionp ; Note: When forward-chain returns with contradictionp = t, then the ; "fc-pair-lst" is really a ttree. We must add the remove-trivial- ; equivalences ttree to the ttree returned by forward-chain and we must ; remember our earlier case splits. (mv 'hit nil (cons-tag-trees ttree fc-pair-lst) pspv)) (t (mv-let (changedp clauses ttree) ; Ttree is either nil or a bddnote if changedp is 'miss or 'error. See ; waterfall-step. (bdd-clause1 bdd-hint type-alist current-clause position ttree cl-id ens wrld state) (mv changedp clauses ttree pspv))))))))))))))) ; See show-bdd and successive definitions for code used to inspect the ; results of using OBDDs. (deflabel obdd :doc ":Doc-Section Miscellaneous ordered binary decision diagrams with rewriting~/ ~l[bdd] for information on this topic.~/~/") (deflabel bdd-algorithm ; Note: the ``IF-lifting-for-IF loop'' described here is really ; combine-if-csts+. :doc ":Doc-Section Bdd summary of the BDD algorithm in ACL2~/ The BDD algorithm in ACL2 uses a combination of manipulation of ~c[IF] terms and unconditional rewriting. In this discussion we begin with some relevant mathematical theory. This is followed by a description of how ACL2 does BDDs, including concluding discussions of soundness, completeness, and efficiency. We recommend that you read the other documentation about BDDs in ACL2 before reading the rather technical material that follows. ~l[BDD].~/ Here is an outline of our presentation. Readers who want a user perspective, without undue mathematical theory, may wish to skip to Part (B), referring to Part (A) only on occasion if necessary. (A) ~st[Mathematical Considerations] ~bq[] (A1) BDD term order (A2) BDD-constructors and BDD terms, and their connection with aborting the BDD algorithm (A3) Canonical BDD terms (A4) A theorem stating the equivalence of provable and syntactic equality for canonical BDD terms ~eq[] (B) ~st[Algorithmic Considerations] ~bq[] (B1) BDD rules (rules used by the rewriting portion of the ACL2 BDD algorithm) (B2) Terms ``known to be Boolean'' (B3) An ``IF-lifting'' operation used by the algorithm, as well as an iterative version of that operation (B4) The ACL2 BDD algorithm (B5) Soundness and Completeness of the ACL2 BDD algorithm (B6) Efficiency considerations ~eq[] (A) ~st[Mathematical Considerations] (A1) ~em[BDD term order] Our BDD algorithm creates a total ``BDD term order'' on ACL2 terms, on the fly. We use this order in our discussions below of IF-lifting and of canonical BDD terms, and in the algorithm's use of commutativity. The particular order is unimportant, except that we guarantee (for purposes of commutative functions) that constants are smaller in this order than non-constants. (A2) ~em[BDD-constructors] (assumed to be ~c['(cons)]) and ~em[BDD terms] We take as given a list of function symbols that we call the ``BDD-constructors.'' By default, the only BDD-constructor is ~ilc[cons], although it is legal to specify any list of function symbols as the BDD-constructors, either by using the ~il[acl2-defaults-table] (~pl[acl2-defaults-table]) or by supplying a ~c[:BDD-CONSTRUCTORS] hint (~pl[hints]). Warning: this capability is largely untested and may produce undesirable results. Henceforth, except when explicitly stated to the contrary, we assume that BDD-constructors is ~c['(cons)]. Roughly speaking, a ~il[BDD] term is the sort of ~il[term] produced by our BDD algorithm, namely a tree with all ~ilc[cons] nodes lying above all non-~c[CONS] nodes. More formally, a ~il[term] is said to be a ~il[BDD] term if it contains ~st[no] subterm of either of the following forms, where ~c[f] is not ~c[CONS]. ~bv[] (f ... (CONS ...) ...) (f ... 'x ...) ; where (consp x) = t ~ev[] We will see that whenever the BDD algorithm attempts to create a ~il[term] that is not a ~il[BDD] term, it aborts instead. Thus, whenever the algorithm completes without aborting, it creates a ~il[BDD] term. (A3) ~em[Canonical BDD terms] We can strengthen the notion of ``BDD term'' to a notion of ``canonical BDD term'' by imposing the following additional requirements, for every subterm of the form ~c[(IF x y z)]: ~bq[] (a) ~c[x] is a variable, and it precedes (in the BDD term order) every variable occurring in ~c[y] or ~c[z]; (b) ~c[y] and ~c[z] are syntactically distinct; and, (c) it is not the case that ~c[y] is ~c[t] and ~c[z] is ~c[nil]. ~eq[] We claim that it follows easily from our description of the BDD algorithm that every term it creates is a canonical BDD term, assuming that the variables occurring in all such terms are treated by the algorithm as being Boolean (see (B2) below) and that the terms contain no function symbols other than ~c[IF] and ~c[CONS]. Thus, under those assumptions the following theorem shows that the BDD algorithm never creates distinct terms that are provably equal, a property that is useful for completeness and efficiency (as we explain in (B5) and (B6) below). (A4) ~em[Provably equal canonical BDD terms are identical] We believe that the following theorem and proof are routine extensions of a standard result and proof to terms that allow calls of ~c[CONS]. ~st[Theorem]. Suppose that ~c[t1] and ~c[t2] are canonical BDD terms that contain no function symbols other than ~c[IF] and ~c[CONS]. Also suppose that ~c[(EQUAL t1 t2)] is a theorem. Then ~c[t1] and ~c[t2] are syntactically identical. Proof of theorem: By induction on the total number of symbols occurring in these two terms. First suppose that at least one term is a variable; without loss of generality let it be ~c[t1]. We must prove that ~c[t2] is syntactically the same as ~c[t1]. Now it is clearly consistent that ~c[(EQUAL t1 t2)] is false if ~c[t2] is a call of ~c[CONS] (to see this, simply let ~c[t1] be an value that is not a ~c[CONSP]). Similarly, ~c[t2] cannot be a constant or a variable other than ~c[t1]. The remaining possibility to rule out is that ~c[t2] is of the form ~c[(IF t3 t4 t5)], since by assumption its function symbol must be ~c[IF] or ~c[CONS] and we have already handled the latter case. Since ~c[t2] is canonical, we know that ~c[t3] is a variable. Since ~c[(EQUAL t1 t2)] is provable, i.e., ~bv[] (EQUAL t1 (if t3 t4 t5)) ~ev[] is provable, it follows that we may substitute either ~c[t] or ~c[nil] for ~c[t3] into this equality to obtain two new provable equalities. First, suppose that ~c[t1] and ~c[t3] are distinct variables. Then these substitutions show that ~c[t1] is provably equal to both ~c[t4] and ~c[t5] (since ~c[t3] does not occur in ~c[t4] or ~c[t5] by property (a) above, as ~c[t2] is canonical), and hence ~c[t4] and ~c[t5] are provably equal to each other, which implies by the inductive hypothesis that they are the same term ~-[] and this contradicts the assumption that ~c[t2] is canonical (property (b)). Therefore ~c[t1] and ~c[t3] are the same variable, i.e., the equality displayed above is actually ~c[(EQUAL t1 (if t1 t4 t5))]. Substituting ~c[t] and then ~c[nil] for ~c[t1] into this provable equality lets us prove ~c[(EQUAL t t4)] and ~c[(EQUAL nil t5)], which by the inductive hypothesis implies that ~c[t4] is (syntactically) the term ~c[t] and ~c[t5] is ~c[nil]. That is, ~c[t2] is ~c[(IF t1 t nil)], which contradicts the assumption that ~c[t2] is canonical (property (c)). Next, suppose that at least one term is a call of ~c[IF]. Our first observation is that the other term is also a call of ~c[IF]. For if the other is a call of ~c[CONS], then they cannot be provably equal, because the former has no function symbols other than ~c[IF] and hence is Boolean when all its variables are assigned Boolean values. Also, if the other is a constant, then both branches of the ~c[IF] term are provably equal to that constant and hence these branches are syntactically identical by the inductive hypothesis, contradicting property (b). Hence, we may assume for this case that both terms are calls of ~c[IF]; let us write them as follows. ~bv[] t0: (IF t1 t2 t3) u0: (IF u1 u2 u3) ~ev[] Note that ~c[t1] and ~c[u1] are variables, by property (a) of canonical BDD terms. First we claim that ~c[t1] does not strictly precede ~c[u1] in the BDD term order. For suppose ~c[t1] does strictly precede ~c[u1]. Then property (a) of canonical BDD terms guarantees that ~c[t1] does not occur in ~c[u0]. Hence, an argument much like one used above shows that ~c[u0] is provably equal to both ~c[t2] (substituting ~c[t] for ~c[t1]) and ~c[t3] (substituting ~c[nil] for ~c[t1]), and hence ~c[t2] and ~c[t3] are provably equal. That implies that they are identical terms, by the inductive hypothesis, which then contradicts property (b) for ~c[t0]. Similarly, ~c[u1] does not strictly precede ~c[t1] in the BDD term order. Therefore, ~c[t1] and ~c[u1] are the same variable. By substituting ~c[t] for this variable we see that ~c[t2] and ~c[u2] are provably equal, and hence they are equal by the inductive hypothesis. Similarly, by substituting ~c[nil] for ~c[t1] (and ~c[u1]) we see that ~c[t3] and ~c[u3] are provably, hence syntactically, equal. We have covered all cases in which at least one term is a variable or at least one term is a call of ~c[IF]. If both terms are constants, then provable and syntactic equality are clearly equivalent. Finally, then, we may assume that one term is a call of ~c[CONS] and the other is a constant or a call of ~c[CONS]. The constant case is similar to the ~c[CONS] case if the constant is a ~c[CONSP], so we omit it; while if the constant is not a ~c[CONSP] then it is not provably equal to a call of ~c[CONS]; in fact it is provably ~em[not] equal! So, we are left with a final case, in which canonical BDD terms ~c[(CONS t1 t2)] and ~c[(CONS u1 u2)] are provably equal, and we want to show that ~c[t1] and ~c[u1] are syntactically equal as are ~c[t2] and ~c[u2]. These conclusions are easy consequences of the inductive hypothesis, since the ACL2 axiom ~c[CONS-EQUAL] (which you can inspect using ~c[:]~ilc[PE]) shows that equality of the given terms is equivalent to the conjunction of ~c[(EQUAL t1 t2)] and ~c[(EQUAL u1 u2)]. Q.E.D. (B) ~st[Algorithmic Considerations] (B1) ~em[BDD rules] A rule of class ~c[:]~ilc[rewrite] (~pl[rule-classes]) is said to be a ``~il[BDD] rewrite rule'' if and only if it satisfies the following criteria. (1) The rule is ~il[enable]d. (2) Its ~il[equivalence] relation is ~ilc[equal]. (3) It has no hypotheses. (4) Its ~c[:]~ilc[loop-stopper] field is ~c[nil], i.e., it is not a permutative rule. (5) All variables occurring in the rule occur in its left-hand side (i.e., there are no ``free variables''; ~pl[rewrite]). A rule of class ~c[:]~ilc[definition] (~pl[rule-classes]) is said to be a ``~il[BDD] definition rule'' if it satisfies all the criteria above (except (4), which does not apply), and moreover the top function symbol of the left-hand side was not recursively (or mutually recursively) defined. Technical point: Note that this additional criterion is independent of whether or not the indicated function symbol actually occurs in the right-hand side of the rule. Both BDD rewrite rules and BDD definition rules are said to be ``BDD rules.'' (B2) ~em[Terms ''known to be Boolean''] We apply the BDD algorithm in the context of a top-level goal to prove, namely, the goal at which the ~c[:BDD] hint is attached. As we run the BDD algorithm, we allow ourselves to say that a set of ~il[term]s is ``known to be Boolean'' if we can verify that the goal is provable from the assumption that at least one of the terms is not Boolean. Equivalently, we allow ourselves to say that a set of terms is ``known to be Boolean'' if we can verify that the original goal is provably equivalent to the assertion that if all terms in the set are Boolean, then the goal holds. The notion ``known to be Boolean'' is conservative in the sense that there are generally sets of terms for which the above equivalent criteria hold and yet the sets of terms are not noted as as being ``known to be Boolean.'' However, ACL2 uses a number of tricks, including ~il[type-set] reasoning and analysis of the structure of the top-level goal, to attempt to establish that a sufficiently inclusive set of terms is known to be Boolean. From a practical standpoint, the algorithm determines a set of terms known to be Boolean; we allow ourselves to say that each term in this set is ``known to be Boolean.'' The algorithm assumes that these terms are indeed Boolean, and can make use of that assumption. For example, if ~c[t1] is known to be Boolean then the algorithm simplifies ~c[(IF t1 t nil)] to ~c[t1]; see (iv) in the discussion immediately below. (B3) ~em[IF-lifting] and the ~em[IF-lifting-for-IF loop] Suppose that one has a ~il[term] of the form ~c[(f ... (IF test x y) ...)], where ~c[f] is a function symbol other than ~c[CONS]. Then we say that ``IF-lifting'' ~c[test] ``from'' this term produces the following term, which is provably equal to the given term. ~bv[] (if test (f ... x ...) ; resulting true branch (f ... y ...)) ; resulting false branch ~ev[] Here, we replace each argument of ~c[f] of the form ~c[(IF test .. ..)], for the same ~c[test], in the same way. In this case we say that ``IF-lifting applies to'' the given term, ``yielding the test'' ~c[test] and with the ``resulting two branches'' displayed above. Whenever we apply IF-lifting, we do so for the available ~c[test] that is least in the BDD term order (see (A1) above). We consider arguments ~c[v] of ~c[f] that are ``known to be Boolean'' (see above) to be replaced by ~c[(IF v t nil)] for the purposes of IF-lifting, i.e., before IF-lifting is applied. There is one special case, however, for IF-lifting. Suppose that the given term is of the form ~c[(IF v y z)] where ~c[v] is a variable and is the test to be lifted out (i.e., it is least in the BDD term order among the potential tests). Moroever, suppose that neither ~c[y] nor ~c[z] is of the form ~c[(IF v W1 W2)] for that same ~c[v]. Then IF-lifting does not apply to the given term. We may now describe the IF-lifting-for-IF loop, which applies to terms of the form ~c[(IF test tbr fbr)] where the algorithm has already produced ~c[test], ~c[tbr], and ~c[fbr]. First, if ~c[test] is ~c[nil] then we return ~c[fbr], while if ~c[test] is a non-~c[nil] constant or a call of ~c[CONS] then we return ~c[tbr]. Otherwise, we see if IF-lifting applies. If IF-lifting does not apply, then we return ~c[(IF test tbr fbr)]. Otherwise, we apply IF-lifting to obtain a term of the form ~c[(IF x y z)], by lifting out the appropriate test. Now we recursively apply the IF-lifting-for-IF loop to the term ~c[(IF x y z)], unless any of the following special cases apply. ~bq[] (i) If ~c[y] and ~c[z] are the same term, then return ~c[y]. (ii) Otherwise, if ~c[x] and ~c[z] are the same term, then replace ~c[z] by ~c[nil] before recursively applying IF-lifting-for-IF. (iii) Otherwise, if ~c[x] and ~c[y] are the same term and ~c[y] is known to be Boolean, then replace ~c[y] by ~c[t] before recursively applying IF-lifting-for-IF. (iv) If ~c[z] is ~c[nil] and either ~c[x] and ~c[y] are the same term or ~c[x] is ``known to be Boolean'' and ~c[y] is ~c[t], then return ~c[x]. ~eq[] NOTE: When a variable ~c[x] is known to be Boolean, it is easy to see that the form ~c[(IF x t nil)] is always reduced to ~c[x] by this algorithm. (B4) ~em[The ACL2 BDD algorithm] We are now ready to present the BDD algorithm for ACL2. It is given an ACL2 ~il[term], ~c[x], as well as an association list ~c[va] that maps variables to terms, including all variables occurring in ~c[x]. We maintain the invariant that whenever a variable is mapped by ~c[va] to a term, that term has already been constructed by the algorithm, except: initially ~c[va] maps every variable occurring in the top-level term to itself. The algorithm proceeds as follows. We implicitly ordain that whenever the BDD algorithm attempts to create a ~il[term] that is not a ~il[BDD] term (as defined above in (A2)), it aborts instead. Thus, whenever the algorithm completes without aborting, it creates a ~il[BDD] term. ~bq[] If ~c[x] is a variable, return the result of looking it up in ~c[va]. If ~c[x] is a constant, return ~c[x]. If ~c[x] is of the form ~c[(IF test tbr fbr)], then first run the algorithm on ~c[test] with the given ~c[va] to obtain ~c[test']. If ~c[test'] is ~c[nil], then return the result ~c[fbr'] of running the algorithm on ~c[fbr] with the given ~c[va]. If ~c[test'] is a constant other than ~c[nil], or is a call of ~c[CONS], then return the result ~c[tbr'] of running the algorithm on ~c[tbr] with the given ~c[va]. If ~c[tbr] is identical to ~c[fbr], return ~c[tbr]. Otherwise, return the result of applying the IF-lifting-for-IF loop (described above) to the term ~c[(IF test' tbr' fbr')]. If ~c[x] is of the form ~c[(IF* test tbr fbr)], then compute the result exactly as though ~ilc[IF] were used rather than ~ilc[IF*], except that if ~c[test'] is not a constant or a call of ~c[CONS] (see paragraph above), then abort the BDD computation. Informally, the tests of ~ilc[IF*] terms are expected to ``resolve.'' NOTE: This description shows how ~ilc[IF*] can be used to implement conditional rewriting in the BDD algorithm. If ~c[x] is a ~c[LAMBDA] expression ~c[((LAMBDA vars body) . args)] (which often corresponds to a ~ilc[LET] term; ~pl[let]), then first form an alist ~c[va'] by binding each ~c[v] in ~c[vars] to the result of running the algorithm on the corresponding member of ~c[args], with the current alist ~c[va]. Then, return the result of the algorithm on ~c[body] in the alist ~c[va']. Otherwise, ~c[x] is of the form ~c[(f x1 x2 ... xn)], where ~c[f] is a function symbol other than ~ilc[IF] or ~ilc[IF*]. In that case, let ~c[xi'] be the result of running the algorithm on ~c[xi], for ~c[i] from 1 to ~c[n], using the given alist ~c[va]. First there are a few special cases. If ~c[f] is ~ilc[EQUAL] then we return ~c[t] if ~c[x1'] is syntactically identical to ~c[x2'] (where this test is very fast; see (B6) below); we return ~c[x1'] if it is known to be Boolean and ~c[x2'] is ~c[t]; and similarly, we return ~c[x2'] if it is known to be Boolean and ~c[x1'] is ~c[t]. Next, if each ~c[xi'] is a constant and the ~c[:]~ilc[executable-counterpart] of ~c[f] is enabled, then the result is obtained by computation. Next, if ~c[f] is ~ilc[BOOLEANP] and ~c[x1'] is known to be Boolean, ~c[t] is returned. Otherwise, we proceed as follows, first possibly swapping the arguments if they are out of (the BDD term) order and if ~c[f] is known to be commutative (see below). If a BDD rewrite rule (as defined above) matches the term ~c[(f x1'... xn')], then the most recently stored such rule is applied. If there is no such match and ~c[f] is a BDD-constructor, then we return ~c[(f x1'... xn')]. Otherwise, if a BDD definition rule matches this term, then the most recently stored such rule (which will usually be the original definition for most users) is applied. If none of the above applies and neither does IF-lifting, then we return ~c[(f x1'... xn')]. Otherwise we apply IF-lifting to ~c[(f x1'... xn')] to obtain a term ~c[(IF test tbr fbr)]; but we aren't done yet. Rather, we run the BDD algorithm (using the same alist) on ~c[tbr] and ~c[fbr] to obtain terms ~c[tbr'] and ~c[fbr'], and we return ~c[(IF test tbr' fbr')] unless ~c[tbr'] is syntactically identical to ~c[fbr'], in which case we return ~c[tbr']. ~eq[] When is it the case that, as said above, ``~c[f] is known to be commutative''? This happens when an enabled rewrite rule is of the form ~c[(EQUAL (f X Y) (f Y X))]. Regarding swapping the arguments in that case: recall that we may assume very little about the BDD term order, essentially only that we swap the two arguments when the second is a constant and the first is not, for example, in ~c[(+ x 1)]. Other than that situation, one cannot expect to predict accurately when the arguments of commutative operators will be swapped. (B5) Soundness and Completeness of the ACL2 BDD algorithm Roughly speaking, ``soundness'' means that the BDD algorithm should give correct answers, and ``completeness'' means that it should be powerful enough to prove all true facts. Let us make the soundness claim a little more precise, and then we'll address completeness under suitable hypotheses. ~st[Claim] (Soundness). If the ACL2 BDD algorithm runs to completion on an input term ~c[t0], then it produces a result that is provably equal to ~c[t0]. We leave the proof of this claim to the reader. The basic idea is simply to check that each step of the algorithm preserves the meaning of the term under the bindings in the given alist. Let us start our discussion of completeness by recalling the theorem proved above in (A4). ~st[Theorem]. Suppose that ~c[t1] and ~c[t2] are canonical BDD terms that contain no function symbols other than ~c[IF] and ~c[CONS]. Also suppose that ~c[(EQUAL t1 t2)] is a theorem. Then ~c[t1] and ~c[t2] are syntactically identical. Below we show how this theorem implies the following completeness property of the ACL2 BDD algorithm. We continue to assume that ~c[CONS] is the only BDD-constructor. ~st[Claim] (Completeness). Suppose that ~c[t1] and ~c[t2] are provably equal terms, under the assumption that all their variables are known to be Boolean. Assume further that under this same assumption, top-level runs of the ACL2 BDD algorithm on these terms return terms that contain only the function symbols ~c[IF] and ~c[CONS]. Then the algorithm returns the same term for both ~c[t1] and ~c[t2], and the algorithm reduces ~c[(EQUAL t1 t2)] to ~c[t]. Why is this claim true? First, notice that the second part of the conclusion follows immediately from the first, by definition of the algorithm. Next, notice that the terms ~c[u1] and ~c[u2] obtained by running the algorithm on ~c[t1] and ~c[t2], respectively, are provably equal to ~c[t1] and ~c[t2], respectively, by the Soundness Claim. It follows that ~c[u1] and ~c[u2] are provably equal to each other. Since these terms contain no function symbols other than ~c[IF] or ~c[CONS], by hypothesis, the Claim now follows from the Theorem above together with the following lemma. ~st[Lemma]. Suppose that the result of running the ACL2 BDD algorithm on a top-level term ~c[t0] is a term ~c[u0] that contains only the function symbols ~c[IF] and ~c[CONS], where all variables of ~c[t0] are known to be Boolean. Then ~c[u0] is a canonical BDD term. Proof: left to the reader. Simply follow the definition of the algorithm, with a separate argument for the IF-lifting-for-IF loop. Finally, let us remark on the assumptions of the Completeness Claim above. The assumption that all variables are known to be Boolean is often true; in fact, the system uses the forward-chaining rule ~c[boolean-listp-forward] (you can see it using ~c[:]~ilc[pe]) to try to establish this assumption, if your theorem has a form such as the following. ~bv[] (let ((x (list x0 x1 ...)) (y (list y0 y1 ...))) (implies (and (boolean-listp x) (boolean-listp y)) ...)) ~ev[] Moreover, the ~c[:BDD] hint can be used to force the prover to abort if it cannot check that the indicated variables are known to be Boolean; ~pl[hints]. Finally, consider the effect in practice of the assumption that the terms resulting from application of the algorithm contain calls of ~c[IF] and ~c[CONS] only. Typical use of BDDs in ACL2 takes place in a theory (~pl[theories]) in which all relevant non-recursive function symbols are enabled and all recursive function symbols possess enabled BDD rewrite rules that tell them how open up. For example, such a rule may say how to expand on a given function call's argument that has the form ~c[(CONS a x)], while another may say how to expand when that argument is ~c[nil]). (See for example the rules ~c[append-cons] and ~c[append-nil] in the documentation for ~ilc[IF*].) We leave it to future work to formulate a theorem that guarantees that the BDD algorithm produces terms containing calls only of ~c[IF] and ~c[CONS] assuming a suitably ``complete'' collection of rewrite rules. (B6) ~em[Efficiency considerations] Following Bryant's algorithm, we use a graph representation of ~il[term]s created by the BDD algorithm's computation. This representation enjoys some important properties. ~bq[] (Time efficiency) The test for syntactic equality of BDD terms is very fast. (Space efficiency) Equal BDD data structures are stored identically in memory. ~eq[] ~em[Implementation note.] The representation actually uses a sort of hash table for BDD terms that is implemented as an ACL2 1-dimensional array. ~l[arrays]. In addition, we use a second such hash table to avoid recomputing the result of applying a function symbol to the result of running the algorithm on its arguments. We believe that these uses of hash tables are standard. They are also discussed in Moore's paper on BDDs; ~pl[bdd] for the reference.") (deflabel bdd-introduction :doc ":Doc-Section Bdd examples illustrating the use of BDDs in ACL2~/ ~l[bdd] for a brief introduction to BDDs in ACL2 and for pointers to other documentation on BDDs in ACL2. Here, we illustrate the use of BDDs in ACL2 by way of some examples. For a further example, ~pl[if*].~/ Let us begin with a really simple example. (We will explain the ~c[:bdd] hint ~c[(:vars nil)] below.) ~bv[] ACL2 !>(thm (equal (if a b c) (if (not a) c b)) :hints ((\"Goal\" :bdd (:vars nil)))) ; Prove with BDDs [Note: A hint was supplied for our processing of the goal above. Thanks!] But simplification with BDDs (7 nodes) reduces this to T, using the :definitions EQUAL and NOT. Q.E.D. Summary Form: ( THM ...) Rules: ((:DEFINITION EQUAL) (:DEFINITION NOT)) Warnings: None Time: 0.18 seconds (prove: 0.05, print: 0.02, other: 0.12) Proof succeeded. ACL2 !> ~ev[] The ~c[:bdd] hint ~c[(:vars nil)] indicates that BDDs are to be used on the indicated goal, and that any so-called ``variable ordering'' may be used: ACL2 may use a convenient order that is far from optimal. It is beyond the scope of the present documentation to address the issue of how the user may choose good variable orderings. Someday our implementation of BDDs may be improved to include heuristically-chosen variable orderings rather than rather random ones. Here is a more interesting example. ~bv[] (defun v-not (x) ; Complement every element of a list of Booleans. (if (consp x) (cons (not (car x)) (v-not (cdr x))) nil)) ; Now we prove a rewrite rule that explains how to open up v-not on ; a consp. (defthm v-not-cons (equal (v-not (cons x y)) (cons (not x) (v-not y)))) ; Finally, we prove for 7-bit lists that v-not is self-inverting. (thm (let ((x (list x0 x1 x2 x3 x4 x5 x6))) (implies (boolean-listp x) (equal (v-not (v-not x)) x))) :hints ((\"Goal\" :bdd ;; Note that this time we specify a variable order. (:vars (x0 x1 x2 x3 x4 x5 x6))))) ~ev[] It turns out that the variable order doesn't seem to matter in this example; using several orders we found that 30 nodes were created, and the proof time was about 1/10 of a second on a (somewhat enhanced) Sparc 2. The same proof took about a minute and a half without any ~c[:bdd] hint! This observation is a bit misleading perhaps, since the theorem for arbitrary ~c[x], ~bv[] (thm (implies (boolean-listp x) (equal (v-not (v-not x)) x))) ~ev[] only takes about 1.5 times as long as the ~c[:bdd] proof for 7 bits, above! Nevertheless, BDDs can be very useful in reducing proof time, especially when there is no regular structure to facilitate proof by induction, or when the induction scheme is so complicated to construct that significant user effort is required to get the proof by induction to go through. Finally, consider the preceding example, with a ~c[:bdd] hint of (say) ~c[(:vars nil)], but with the rewrite rule ~c[v-not-cons] above disabled. In that case, the proof fails, as we see below. That is because the BDD algorithm in ACL2 uses hypothesis-free ~c[:]~il[rewrite] rules, ~c[:]~ilc[executable-counterpart]~c[s], and nonrecursive definitions, but it does not use recursive definitions. Notice that when we issue the ~c[(show-bdd)] command, the system's response clearly shows that we need a rewrite rule for simplifying terms of the form ~c[(v-not (cons ...))]. ~bv[] ACL2 !>(thm (let ((x (list x0 x1 x2 x3 x4 x5 x6))) (implies (boolean-listp x) (equal (v-not (v-not x)) x))) :hints ((\"Goal\" :bdd (:vars nil) :in-theory (disable v-not-cons)))) [Note: A hint was supplied for our processing of the goal above. Thanks!] ACL2 Error in ( THM ...): Attempted to create V-NOT node during BDD processing with an argument that is a call of a bdd-constructor, which would produce a non-BDD term (as defined in :DOC bdd-algorithm). See :DOC show-bdd. Summary Form: ( THM ...) Rules: NIL Warnings: None Time: 0.58 seconds (prove: 0.13, print: 0.00, other: 0.45) ******** FAILED ******** See :DOC failure ******** FAILED ******** ACL2 !>(show-bdd) BDD computation on Goal yielded 17 nodes. ============================== BDD computation was aborted on Goal, and hence there is no falsifying assignment that can be constructed. Here is a backtrace of calls, starting with the top-level call and ending with the one that led to the abort. See :DOC show-bdd. (LET ((X (LIST X0 X1 X2 X3 X4 X5 ...))) (IMPLIES (BOOLEAN-LISTP X) (EQUAL (V-NOT (V-NOT X)) X))) alist: ((X6 X6) (X5 X5) (X4 X4) (X3 X3) (X2 X2) (X1 X1) (X0 X0)) (EQUAL (V-NOT (V-NOT X)) X) alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...))) (V-NOT (V-NOT X)) alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...))) (V-NOT X) alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...))) ACL2 !> ~ev[] The term that has caused the BDD algorithm to abort is thus ~c[(V-NOT X)], where ~c[X] has the value ~c[(LIST X0 X1 X2 X3 X4 X5 ...)], i.e., ~c[(CONS X0 (LIST X1 X2 X3 X4 X5 ...))]. Thus, we see the utility of introducing a rewrite rule to simplify terms of the form ~c[(V-NOT (CONS ...))]. The moral of this story is that if you get an error of the sort shown above, you may find it useful to execute the command ~c[(show-bdd)] and use the result as advice that suggests the left hand side of a rewrite rule. Here is another sort of failed proof. In this version we have omitted the hypothesis that the input is a bit vector. Below we use ~c[show-bdd] to see what went wrong, and use the resulting information to construct a counterexample. This failed proof corresponds to a slightly modified input theorem, in which ~c[x] is bound to the 4-element list ~c[(list x0 x1 x2 x3)]. ~bv[] ACL2 !>(thm (let ((x (list x0 x1 x2 x3))) (equal (v-not (v-not x)) x)) :hints ((\"Goal\" :bdd ;; This time we do not specify a variable order. (:vars nil)))) [Note: A hint was supplied for our processing of the goal above. Thanks!] ACL2 Error in ( THM ...): The :BDD hint for the current goal has successfully simplified this goal, but has failed to prove it. Consider using (SHOW-BDD) to suggest a counterexample; see :DOC show-bdd. Summary Form: ( THM ...) Rules: NIL Warnings: None Time: 0.18 seconds (prove: 0.07, print: 0.00, other: 0.12) ******** FAILED ******** See :DOC failure ******** FAILED ******** ACL2 !>(show-bdd) BDD computation on Goal yielded 73 nodes. ============================== Falsifying constraints: ((X0 \"Some non-nil value\") (X1 \"Some non-nil value\") (X2 \"Some non-nil value\") (X3 \"Some non-nil value\") ((EQUAL 'T X0) T) ((EQUAL 'T X1) T) ((EQUAL 'T X2) T) ((EQUAL 'T X3) NIL)) ============================== Term obtained from BDD computation on Goal: (IF X0 (IF X1 (IF X2 (IF X3 (IF # # #) (IF X3 # #)) (IF X2 'NIL (IF X3 # #))) (IF X1 'NIL (IF X2 (IF X3 # #) (IF X2 # #)))) (IF X0 'NIL (IF X1 (IF X2 (IF X3 # #) (IF X2 # #)) (IF X1 'NIL (IF X2 # #))))) ACL2 Query (:SHOW-BDD): Print the term in full? (N, Y, W or ?): n ; I've seen enough. The assignment shown above suggests ; (though not conclusively) that if we bind x3 to a non-nil ; value other than T, and bind x0, x1, and x2 to t, then we ; this may give us a counterexample. ACL2 !>(let ((x0 t) (x1 t) (x2 t) (x3 7)) (let ((x (list x0 x1 x2 x3))) ;; Let's use LIST instead of EQUAL to see how the two ;; lists differ. (list (v-not (v-not x)) x))) ((T T T T) (T T T 7)) ACL2 !> ~ev[] ~l[if*] for another example.") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;; IX. COMPILING THIS FILE AND OTHER HELPFUL TIPS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; In order to check for slow code, you can execute the following from ACL2 built ; on GCL, inside raw Lisp. ; ; (compile-file "bdd.lisp" :c-file t) ; ; Then, search the file bdd.c for make_fixnum and number_ for slow stuff. Note ; that you'll find a lot of these, but you only need to worry about them in the ; workhorse functions, and you don't need to worry about CMPmake_fixnum when it ; is used for an error or for a new mx-id. ; ; When you find one of these, search upward for `local entry' to see which ; function or macro you are in. Don't worry, for example, about commutative-p, ; which is a database kind of function rather than a workhorse function. ; ; You'll see things like the following (from local entry to BDD). The idea here ; is that we are boxing a fixnum and pushing it on a stack, but why? LnkLI253 ; appears to be a function call, which is found near the end of the file to ; correspond to leaf-cst-list-array. If we're still not clear on what's going ; on, we can look up 273 as well. When we do this, we find that we are probably ; in the part of the BDD code shown at the end, which is not a problem. ; ; V1570 = CMPmake_fixnum(V1549); ; V1571= (*(LnkLI253))(/* INLINE-ARGS */V1569,V1570); ; V1572= (*(LnkLI273))((V1525),/* INLINE-ARGS */V1571); ; ; .... ; ; static object LnkTLI273(va_alist)va_dcl{va_list ap;va_start(ap); ; return(object )call_proc(VV[273],&LnkLI273,2,ap);} /* DECODE-CST-ALIST */ ; ; static object LnkTLI253(va_alist)va_dcl{va_list ap;va_start(ap); ; return(object )call_proc(VV[253],&LnkLI253,2,ap);} /* LEAF-CST-LIST-ARRAY */ ; ; ; Source code from (defun bdd ...) [an earlier version]: ; ; (bdd-error ; mx-id ; "Unable to resolve test of IF* for term~|~%~p0~|~%under the ~ ; bindings~|~%~x1~|~%-- use SHOW-BDD to see a backtrace." ; (list (cons #\0 (untranslate term nil)) ; (cons #\1 ; (decode-cst-alist alist ; (leaf-cst-list-array ; (strip-cdrs alist) ; mx-id)))) ; ; ; We need a cst next, though we don't care about it. ; ; *cst-t* ; ttree) acl2-sources/boot-strap-pass-2.lisp0000664002132200015000000016231312222115527016646 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; This file, boot-strap-pass-2, is compiled and loaded; but it is only ; processed during the second pass of the boot-strap process, not the first. ; We introduce proper defattach events, i.e., without :skip-checks t. Here are ; some guiding principles for making system functions available for attachment ; by users. ; - The initial attachment is named by adding the suffix -builtin. For ; example, worse-than is a constrained function initially attached to ; worse-than-builtin. ; - Use the weakest logical specs we can (even if T), without getting ; distracted by names. For example, we do not specify a relationship between ; worse-than-or-equal and worse-than. ; - Only make functions attachable if they are used in our sources somewhere ; outside their definitions. So for example, we do not introduce ; worse-than-list as a constrained function, since its only use is in the ; mutual-recursion event that defines worse-than. ; We conclude by defining some theories, at the end so that they pick up the ; rest of this file. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Miscellaneous verify-termination and guard verification ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; cons-term and symbol-class -- at one point during development, used in ; fncall-term, but anyhow, generally useful to have in logic mode (verify-termination-boot-strap quote-listp) ; and guards (verify-termination-boot-strap cons-term1) ; and guards (verify-termination-boot-strap cons-term) ; and guards (verify-termination-boot-strap symbol-class) ; and guards ; observation1-cw (verify-termination-boot-strap observation1-cw) (verify-guards observation1-cw) ; packn1 and packn (verify-termination-boot-strap packn1) ; and guards (encapsulate () (local (defthm character-listp-explode-nonnegative-integer (implies (character-listp z) (character-listp (explode-nonnegative-integer x y z))) :rule-classes ((:forward-chaining :trigger-terms ((explode-nonnegative-integer x y z)))))) (local (defthm character-listp-explode-atom (character-listp (explode-atom x y)) :rule-classes ((:forward-chaining :trigger-terms ((explode-atom x y)))))) (verify-termination-boot-strap packn) ; and guards (verify-termination-boot-strap packn-pos) ; and guards ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Attachment: too-many-ifs-post-rewrite and too-many-ifs-pre-rewrite ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; #+acl2-loop-only ; The above readtime conditional avoids a CLISP warning, and lets the defproxy ; for print-clause-id-okp provide the raw Lisp definition. (encapsulate ((too-many-ifs-post-rewrite (args val) t :guard (and (pseudo-term-listp args) (pseudo-termp val)))) (local (defun too-many-ifs-post-rewrite (args val) (list args val)))) ; The following events are derived from the original version of community book ; books/system/too-many-ifs.lisp. But here we provide a proof that does not ; depend on books. Our approach was to take the proof in the above book, ; eliminate the unnecessary use of an arithmetic book, expand away all uses of ; macros and make-events, avoid use of (theory 'minimal-theory) since that ; theory didn't yet exist (where these events were originally placed), and ; apply some additional hand-editing in order (for example) to remove hints ; depending on the tools/flag community book. We have left original events ; from the book as comments. (encapsulate () (logic) ;;; (include-book "tools/flag" :dir :system) ; In the original book, but not needed for its certification: ; (include-book "arithmetic/top-with-meta" :dir :system) ; Comments like the following show events from the original book. ;;; (make-flag pseudo-termp-flg ;;; pseudo-termp ;;; :flag-var flg ;;; :flag-mapping ((pseudo-termp . term) ;;; (pseudo-term-listp . list)) ;;; :defthm-macro-name defthm-pseudo-termp ;;; :local t) (local (defun-nx pseudo-termp-flg (flg x lst) (declare (xargs :verify-guards nil :normalize nil :measure (case flg (term (acl2-count x)) (otherwise (acl2-count lst))))) (case flg (term (if (consp x) (cond ((equal (car x) 'quote) (and (consp (cdr x)) (equal (cddr x) nil))) ((true-listp x) (and (pseudo-termp-flg 'list nil (cdr x)) (cond ((symbolp (car x)) t) ((true-listp (car x)) (and (equal (length (car x)) 3) (equal (caar x) 'lambda) (symbol-listp (cadar x)) (pseudo-termp-flg 'term (caddar x) nil) (equal (length (cadar x)) (length (cdr x))))) (t nil)))) (t nil)) (symbolp x))) (otherwise (if (consp lst) (and (pseudo-termp-flg 'term (car lst) nil) (pseudo-termp-flg 'list nil (cdr lst))) (equal lst nil)))))) (local (defthm pseudo-termp-flg-equivalences (equal (pseudo-termp-flg flg x lst) (case flg (term (pseudo-termp x)) (otherwise (pseudo-term-listp lst)))) :hints (("goal" :induct (pseudo-termp-flg flg x lst))))) (local (in-theory (disable (:definition pseudo-termp-flg)))) ; Added here (not present or needed in the certified book): (verify-termination-boot-strap max) ; and guards (verify-termination-boot-strap var-counts1) ;;; (make-flag var-counts1-flg ;;; var-counts1 ;;; :flag-var flg ;;; :flag-mapping ((var-counts1 . term) ;;; (var-counts1-lst . list)) ;;; :defthm-macro-name defthm-var-counts1 ;;; :local t) (local (defun-nx var-counts1-flg (flg rhs arg lst acc) (declare (xargs :verify-guards nil :normalize nil :measure (case flg (term (acl2-count rhs)) (otherwise (acl2-count lst))) :hints nil :well-founded-relation o< :mode :logic) (ignorable rhs arg lst acc)) (case flg (term (cond ((equal arg rhs) (+ 1 acc)) ((consp rhs) (cond ((equal 'quote (car rhs)) acc) ((equal (car rhs) 'if) (max (var-counts1-flg 'term (caddr rhs) arg nil acc) (var-counts1-flg 'term (cadddr rhs) arg nil acc))) (t (var-counts1-flg 'list nil arg (cdr rhs) acc)))) (t acc))) (otherwise (if (consp lst) (var-counts1-flg 'list nil arg (cdr lst) (var-counts1-flg 'term (car lst) arg nil acc)) acc))))) (local (defthm var-counts1-flg-equivalences (equal (var-counts1-flg flg rhs arg lst acc) (case flg (term (var-counts1 arg rhs acc)) (otherwise (var-counts1-lst arg lst acc)))))) (local (in-theory (disable (:definition var-counts1-flg)))) ;;; (defthm-var-counts1 natp-var-counts1 ;;; (term ;;; (implies (natp acc) ;;; (natp (var-counts1 arg rhs acc))) ;;; :rule-classes :type-prescription) ;;; (list ;;; (implies (natp acc) ;;; (natp (var-counts1-lst arg lst acc))) ;;; :rule-classes :type-prescription) ;;; :hints (("Goal" :induct (var-counts1-flg flg rhs arg lst acc)))) (local (defthm natp-var-counts1 (case flg (term (implies (natp acc) (natp (var-counts1 arg rhs acc)))) (otherwise (implies (natp acc) (natp (var-counts1-lst arg lst acc))))) :hints (("Goal" :induct (var-counts1-flg flg rhs arg lst acc))) :rule-classes nil)) (local (defthm natp-var-counts1-term (implies (natp acc) (natp (var-counts1 arg rhs acc))) :hints (("Goal" ; :in-theory (theory 'minimal-theory) :use ((:instance natp-var-counts1 (flg 'term))))) :rule-classes :type-prescription)) (local (defthm natp-var-counts1-list (implies (natp acc) (natp (var-counts1-lst arg lst acc))) :hints (("Goal" ; :in-theory (theory 'minimal-theory) :use ((:instance natp-var-counts1 (flg 'list))))) :rule-classes :type-prescription)) (verify-guards var-counts1) (verify-termination-boot-strap var-counts) ; and guards ;;; Since the comment about var-counts says that var-counts returns a list of ;;; nats as long as lhs-args, I prove those facts, speculatively. ; Except, we reason instead about integer-listp. See the comment just above ; the commented-out definition of nat-listp in the source code (file ; rewrite.lisp). ; (verify-termination nat-listp) (local (defthm integer-listp-var-counts (integer-listp (var-counts lhs-args rhs)))) (local (defthm len-var-counts (equal (len (var-counts lhs-args rhs)) (len lhs-args)))) (verify-termination-boot-strap count-ifs) ; and guards ; Added here (not present or needed in the certified book): (verify-termination-boot-strap ifix) ; and guards ; Added here (not present or needed in the certified book): (verify-termination-boot-strap abs) ; and guards ; Added here (not present or needed in the certified book): (verify-termination-boot-strap expt) ; and guards ; Added here (not present or needed in the certified book): (local (defthm natp-expt (implies (and (integerp base) (integerp n) (<= 0 n)) (integerp (expt base n))) :rule-classes :type-prescription)) ; Added here (not present or needed in the certified book): (verify-termination-boot-strap signed-byte-p) ; and guards (verify-termination-boot-strap too-many-ifs0) ; and guards (verify-termination-boot-strap too-many-ifs-pre-rewrite-builtin) ; and guards (verify-termination-boot-strap occur-cnt-bounded) ;;; (make-flag occur-cnt-bounded-flg ;;; occur-cnt-bounded ;;; :flag-var flg ;;; :flag-mapping ((occur-cnt-bounded . term) ;;; (occur-cnt-bounded-lst . list)) ;;; :defthm-macro-name defthm-occur-cnt-bounded ;;; :local t) (local (defun-nx occur-cnt-bounded-flg (flg term2 term1 lst a m bound-m) (declare (xargs :verify-guards nil :normalize nil :measure (case flg (term (acl2-count term2)) (otherwise (acl2-count lst)))) (ignorable term2 term1 lst a m bound-m)) (case flg (term (cond ((equal term1 term2) (if (< bound-m a) -1 (+ a m))) ((consp term2) (if (equal 'quote (car term2)) a (occur-cnt-bounded-flg 'list nil term1 (cdr term2) a m bound-m))) (t a))) (otherwise (if (consp lst) (let ((new (occur-cnt-bounded-flg 'term (car lst) term1 nil a m bound-m))) (if (equal new -1) -1 (occur-cnt-bounded-flg 'list nil term1 (cdr lst) new m bound-m))) a))))) (local (defthm occur-cnt-bounded-flg-equivalences (equal (occur-cnt-bounded-flg flg term2 term1 lst a m bound-m) (case flg (term (occur-cnt-bounded term1 term2 a m bound-m)) (otherwise (occur-cnt-bounded-lst term1 lst a m bound-m)))))) (local (in-theory (disable (:definition occur-cnt-bounded-flg)))) ;;; (defthm-occur-cnt-bounded integerp-occur-cnt-bounded ;;; (term ;;; (implies (and (integerp a) ;;; (integerp m)) ;;; (integerp (occur-cnt-bounded term1 term2 a m bound-m))) ;;; :rule-classes :type-prescription) ;;; (list ;;; (implies (and (integerp a) ;;; (integerp m)) ;;; (integerp (occur-cnt-bounded-lst term1 lst a m bound-m))) ;;; :rule-classes :type-prescription) ;;; :hints (("Goal" :induct (occur-cnt-bounded-flg flg term2 term1 lst a m ;;; bound-m)))) (local (defthm integerp-occur-cnt-bounded (case flg (term (implies (and (integerp a) (integerp m)) (integerp (occur-cnt-bounded term1 term2 a m bound-m)))) (otherwise (implies (and (integerp a) (integerp m)) (integerp (occur-cnt-bounded-lst term1 lst a m bound-m))))) :rule-classes nil :hints (("Goal" :induct (occur-cnt-bounded-flg flg term2 term1 lst a m bound-m))))) (local (defthm integerp-occur-cnt-bounded-term (implies (and (integerp a) (integerp m)) (integerp (occur-cnt-bounded term1 term2 a m bound-m))) :rule-classes :type-prescription :hints (("goal" ; :in-theory (theory 'minimal-theory) :use ((:instance integerp-occur-cnt-bounded (flg 'term))))))) (local (defthm integerp-occur-cnt-bounded-list (implies (and (integerp a) (integerp m)) (integerp (occur-cnt-bounded-lst term1 lst a m bound-m))) :rule-classes :type-prescription :hints (("goal" ; :in-theory (theory 'minimal-theory) :use ((:instance integerp-occur-cnt-bounded (flg 'list))))))) ;;; (defthm-occur-cnt-bounded signed-byte-p-30-occur-cnt-bounded-flg ;;; (term ;;; (implies (and (force (signed-byte-p 30 a)) ;;; (signed-byte-p 30 m) ;;; (signed-byte-p 30 (+ bound-m m)) ;;; (force (<= 0 a)) ;;; (<= 0 m) ;;; (<= 0 bound-m) ;;; (<= a (+ bound-m m))) ;;; (and (<= -1 (occur-cnt-bounded term1 term2 a m bound-m)) ;;; (<= (occur-cnt-bounded term1 term2 a m bound-m) (+ bound-m m)))) ;;; :rule-classes :linear) ;;; (list ;;; (implies (and (force (signed-byte-p 30 a)) ;;; (signed-byte-p 30 m) ;;; (signed-byte-p 30 (+ bound-m m)) ;;; (force (<= 0 a)) ;;; (<= 0 m) ;;; (<= 0 bound-m) ;;; (<= a (+ bound-m m))) ;;; (and (<= -1 (occur-cnt-bounded-lst term1 lst a m bound-m)) ;;; (<= (occur-cnt-bounded-lst term1 lst a m bound-m) (+ bound-m m)))) ;;; :rule-classes :linear) ;;; :hints (("Goal" :induct (occur-cnt-bounded-flg flg term2 term1 lst a m ;;; bound-m)))) (local (defthm signed-byte-p-30-occur-cnt-bounded-flg (case flg (term (implies (and (force (signed-byte-p 30 a)) (signed-byte-p 30 m) (signed-byte-p 30 (+ bound-m m)) (force (<= 0 a)) (<= 0 m) (<= 0 bound-m) (<= a (+ bound-m m))) (and (<= -1 (occur-cnt-bounded term1 term2 a m bound-m)) (<= (occur-cnt-bounded term1 term2 a m bound-m) (+ bound-m m))))) (otherwise (implies (and (force (signed-byte-p 30 a)) (signed-byte-p 30 m) (signed-byte-p 30 (+ bound-m m)) (force (<= 0 a)) (<= 0 m) (<= 0 bound-m) (<= a (+ bound-m m))) (and (<= -1 (occur-cnt-bounded-lst term1 lst a m bound-m)) (<= (occur-cnt-bounded-lst term1 lst a m bound-m) (+ bound-m m)))))) :rule-classes nil :hints (("Goal" :induct (occur-cnt-bounded-flg flg term2 term1 lst a m bound-m))))) (local (defthm signed-byte-p-30-occur-cnt-bounded-flg-term (implies (and (force (signed-byte-p 30 a)) (signed-byte-p 30 m) (signed-byte-p 30 (+ bound-m m)) (force (<= 0 a)) (<= 0 m) (<= 0 bound-m) (<= a (+ bound-m m))) (and (<= -1 (occur-cnt-bounded term1 term2 a m bound-m)) (<= (occur-cnt-bounded term1 term2 a m bound-m) (+ bound-m m)))) :rule-classes :linear :hints (("Goal" ; :in-theory (theory 'minimal-theory) :use ((:instance signed-byte-p-30-occur-cnt-bounded-flg (flg 'term))))))) (local (defthm signed-byte-p-30-occur-cnt-bounded-flg-list (implies (and (force (signed-byte-p 30 a)) (signed-byte-p 30 m) (signed-byte-p 30 (+ bound-m m)) (force (<= 0 a)) (<= 0 m) (<= 0 bound-m) (<= a (+ bound-m m))) (and (<= -1 (occur-cnt-bounded-lst term1 lst a m bound-m)) (<= (occur-cnt-bounded-lst term1 lst a m bound-m) (+ bound-m m)))) :rule-classes :linear :hints (("Goal" ; :in-theory (theory 'minimal-theory) :use ((:instance signed-byte-p-30-occur-cnt-bounded-flg (flg 'list))))))) (verify-guards occur-cnt-bounded) (verify-termination-boot-strap too-many-ifs1) ; and guards (verify-termination-boot-strap too-many-ifs-post-rewrite-builtin) ; and guards ) (defattach too-many-ifs-post-rewrite too-many-ifs-post-rewrite-builtin) ; Complete too-many-ifs-pre-rewrite. #+acl2-loop-only ; The above readtime conditional avoids a CLISP warning, and lets the defproxy ; for print-clause-id-okp provide the raw Lisp definition. (encapsulate ((too-many-ifs-pre-rewrite (args counts) t :guard (and (pseudo-term-listp args) (integer-listp counts) (equal (len args) (len counts))))) (local (defun too-many-ifs-pre-rewrite (args counts) (list args counts)))) (defattach (too-many-ifs-pre-rewrite too-many-ifs-pre-rewrite-builtin)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Attachment: ancestors-check, worse-than, worse-than-or-equal ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (verify-termination-boot-strap pseudo-variantp) (verify-termination-boot-strap member-char-stringp) (verify-termination-boot-strap terminal-substringp1) (verify-termination-boot-strap terminal-substringp) (verify-termination-boot-strap evg-occur) (verify-termination-boot-strap min-fixnum) (verify-termination-boot-strap fn-count-evg-rec ; but not guards (declare (xargs :verify-guards nil))) (defthm fn-count-evg-rec-type-prescription (implies (natp acc) (natp (fn-count-evg-rec evg acc calls))) :rule-classes :type-prescription) (defthm fn-count-evg-rec-bound (< (fn-count-evg-rec evg acc calls) 536870912) ; (expt 2 29) :rule-classes :linear) (verify-guards fn-count-evg-rec) (verify-termination-boot-strap occur) (verify-termination-boot-strap worse-than-builtin) ; and worse-than-or-equal-builtin (verify-termination-boot-strap ancestor-listp) (verify-termination-boot-strap earlier-ancestor-biggerp) (verify-termination-boot-strap fn-count-1) ; but not guards (defthm fn-count-1-type (implies (and (integerp fn-count-acc) (integerp p-fn-count-acc)) (and (integerp (car (fn-count-1 flag term fn-count-acc p-fn-count-acc))) (integerp (mv-nth 0 (fn-count-1 flag term fn-count-acc p-fn-count-acc))) (integerp (mv-nth 1 (fn-count-1 flag term fn-count-acc p-fn-count-acc))) (integerp (nth 0 (fn-count-1 flag term fn-count-acc p-fn-count-acc))) (integerp (nth 1 (fn-count-1 flag term fn-count-acc p-fn-count-acc))))) :rule-classes ((:forward-chaining :trigger-terms ((fn-count-1 flag term fn-count-acc p-fn-count-acc))))) (verify-guards fn-count-1) (verify-termination-boot-strap var-fn-count-1) ; but not guards (defthm symbol-listp-cdr-assoc-equal (implies (symbol-list-listp x) (symbol-listp (cdr (assoc-equal key x))))) ; We state the following three rules in all forms that we think might be useful ; to those who want to reason about var-fn-count-1, for example if they are ; developing attachments to ancestors-check. (defthm integerp-nth-0-var-fn-count-1 (implies (integerp var-count-acc) (integerp (nth 0 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)))) :rule-classes ((:forward-chaining :trigger-terms ((var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)) :corollary (implies (integerp var-count-acc) (and (integerp (nth 0 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table))) (integerp (mv-nth 0 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table))) (integerp (car (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)))))))) (defthm integerp-nth-1-var-fn-count-1 (implies (integerp fn-count-acc) (integerp (nth 1 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)))) :rule-classes ((:forward-chaining :trigger-terms ((var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)) :corollary (implies (integerp fn-count-acc) (and (integerp (nth 1 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table))) (integerp (mv-nth 1 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)))))))) (defthm integerp-nth-2-var-fn-count-1 (implies (integerp p-fn-count-acc) (integerp (nth 2 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)))) :rule-classes ((:forward-chaining :trigger-terms ((var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)) :corollary (implies (integerp p-fn-count-acc) (and (integerp (nth 2 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table))) (integerp (mv-nth 2 (var-fn-count-1 flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table)))))))) (verify-guards var-fn-count-1) (verify-termination-boot-strap equal-mod-commuting) ; and guards (verify-termination-boot-strap ancestors-check1) (verify-termination-boot-strap ancestors-check-builtin) (defun member-equal-mod-commuting (x lst wrld) (declare (xargs :guard (and (pseudo-termp x) (pseudo-term-listp lst) (plist-worldp wrld)))) (cond ((endp lst) nil) ((equal-mod-commuting x (car lst) wrld) lst) (t (member-equal-mod-commuting x (cdr lst) wrld)))) ; In the following, terms (nth 0 ...) and (nth 1 ...) in the hints were ; originally (car ...) and (mv-nth 1 ...), respectively, but those didn't ; work. It would be good at some point to explore why not, given that the ; original versions worked outside the build. (defun strip-ancestor-literals (ancestors) (declare (xargs :guard (ancestor-listp ancestors))) (cond ((endp ancestors) nil) (t (cons (access ancestor (car ancestors) :lit) (strip-ancestor-literals (cdr ancestors)))))) (encapsulate () (local (defthm ancestors-check1-property (mv-let (on-ancestors assumed-true) (ancestors-check1 lit-atm lit var-cnt fn-cnt p-fn-cnt ancestors tokens) (implies (and on-ancestors assumed-true) (member-equal-mod-commuting lit (strip-ancestor-literals ancestors) nil))) :rule-classes nil)) (defthmd ancestors-check-builtin-property (mv-let (on-ancestors assumed-true) (ancestors-check-builtin lit ancestors tokens) (implies (and on-ancestors assumed-true) (member-equal-mod-commuting lit (strip-ancestor-literals ancestors) nil))) :hints (("Goal" :use ((:instance ancestors-check1-property (lit-atm lit) (var-cnt 0) (fn-cnt 0) (p-fn-cnt 0)) (:instance ancestors-check1-property (lit-atm lit) (var-cnt (nth 0 (var-fn-count-1 nil lit 0 0 0 nil nil))) (fn-cnt (nth 1 (var-fn-count-1 nil lit 0 0 0 nil nil))) (p-fn-cnt (nth 2 (var-fn-count-1 nil lit 0 0 0 nil nil)))) (:instance ancestors-check1-property (lit-atm (cadr lit)) (var-cnt (nth 0 (var-fn-count-1 nil (cadr lit) 0 0 0 nil nil))) (fn-cnt (nth 1 (var-fn-count-1 nil (cadr lit) 0 0 0 nil nil))) (p-fn-cnt (nth 2 (var-fn-count-1 nil (cadr lit) 0 0 0 nil nil))))))))) #+acl2-loop-only ; The above readtime conditional avoids a CLISP warning, and lets the defproxy ; for print-clause-id-okp provide the raw Lisp definition. (encapsulate ((ancestors-check (lit ancestors tokens) (mv t t) :guard (and (pseudo-termp lit) (ancestor-listp ancestors) (true-listp tokens)))) (local (defun ancestors-check (lit ancestors tokens) (ancestors-check-builtin lit ancestors tokens))) (defthmd ancestors-check-constraint (implies (and (pseudo-termp lit) (ancestor-listp ancestors) (true-listp tokens)) (mv-let (on-ancestors assumed-true) (ancestors-check lit ancestors tokens) (implies (and on-ancestors assumed-true) (member-equal-mod-commuting lit (strip-ancestor-literals ancestors) nil)))) :hints (("Goal" :use ancestors-check-builtin-property)))) (defattach (ancestors-check ancestors-check-builtin) :hints (("Goal" :by ancestors-check-builtin-property))) (defattach worse-than worse-than-builtin) (defattach worse-than-or-equal worse-than-or-equal-builtin) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Attachment: acl2x-expansion-alist ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (verify-termination-boot-strap hons-copy-with-state) ; and guards (verify-termination-boot-strap identity-with-state) ; and guards (defattach (acl2x-expansion-alist ; User-modifiable; see comment in the defstub introducing ; acl2x-expansion-alist. identity-with-state)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Attachments: rw-cache utilities ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (verify-termination-boot-strap rw-cache-debug-builtin) ; and guards (defattach rw-cache-debug rw-cache-debug-builtin) (verify-termination-boot-strap rw-cache-debug-action-builtin) ; and guards (defattach rw-cache-debug-action rw-cache-debug-action-builtin) (verify-termination-boot-strap rw-cacheable-failure-reason-builtin) ; and guards (defattach rw-cacheable-failure-reason rw-cacheable-failure-reason-builtin) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Attachments: print-clause-id-okp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (verify-termination-boot-strap all-digits-p) ; and guards (verify-termination-boot-strap ; and guards (d-pos-listp (declare (xargs :guard-hints (("Goal" :use ((:instance coerce-inverse-2 (x (symbol-name (car lst)))) (:instance character-listp-coerce (str (symbol-name (car lst))))) :expand ((len (coerce (symbol-name (car lst)) 'list))) :in-theory (disable coerce-inverse-2 character-listp-coerce))))))) (verify-termination-boot-strap pos-listp) (verify-guards pos-listp) (defthm d-pos-listp-forward-to-true-listp (implies (d-pos-listp x) (true-listp x)) :rule-classes :forward-chaining) (verify-termination-boot-strap clause-id-p) ; and guards #+acl2-loop-only ; The above readtime conditional avoids a CLISP warning, and lets the defproxy ; for print-clause-id-okp provide the raw Lisp definition. (encapsulate (((print-clause-id-okp *) => * :formals (cl-id) :guard (clause-id-p cl-id))) (local (defun print-clause-id-okp (x) x))) (verify-termination-boot-strap print-clause-id-okp-builtin) ; and guards (defattach print-clause-id-okp print-clause-id-okp-builtin) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Attachments: oncep-tp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We could avoid the forms below by replacing the earlier forms ; (defproxy oncep-tp (* *) => *) ; (defun oncep-tp-builtin ...) ; :guard t ; (defattach (oncep-tp oncep-tp-builtin) :skip-checks t) ; in place, by changing defproxy to defstub and removing :skip-checks t. ; However, the guard on once-tp would then be left with a guard of t, which ; might be stronger than we'd like. #+acl2-loop-only ; The above readtime conditional avoids a CLISP warning, and lets the defproxy ; for print-clause-id-okp provide the raw Lisp definition. (encapsulate (((oncep-tp * *) => * :formals (rune wrld) :guard (and (plist-worldp wrld) ; Although (runep rune wrld) is appropriate here, we don't want to fight the ; battle yet of putting runep into :logic mode. So we just lay down the ; syntactic part of its code, which should suffice for user-defined attachments ; to oncep-tp. (and (consp rune) (consp (cdr rune)) (symbolp (cadr rune)))))) (logic) (local (defun oncep-tp (rune wrld) (oncep-tp-builtin rune wrld)))) (defattach oncep-tp oncep-tp-builtin) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; verify-termination and guard verification: ; string-for-tilde-@-clause-id-phrase and some subsidiary functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; David Rager proved termination and guards for ; string-for-tilde-@-clause-id-phrase, with a proof that included community ; books unicode/explode-atom and unicode/explode-nonnegative-integer. Here, we ; rework that proof a bit to avoid those dependencies. Note that this proof ; depends on d-pos-listp, whose termination and guard verification are ; performed above. ; We proved true-listp-explode-nonnegative-integer here, but then found it was ; already proved locally in axioms.lisp. So we made that defthm non-local (and ; strengthened it to its current form). (verify-termination-boot-strap chars-for-tilde-@-clause-id-phrase/periods) (verify-termination-boot-strap chars-for-tilde-@-clause-id-phrase/primes) (defthm pos-listp-forward-to-integer-listp (implies (pos-listp x) (integer-listp x)) :rule-classes :forward-chaining) (verify-termination-boot-strap chars-for-tilde-@-clause-id-phrase) (defthm true-listp-chars-for-tilde-@-clause-id-phrase/periods (true-listp (chars-for-tilde-@-clause-id-phrase/periods lst)) :rule-classes :type-prescription) (defthm true-listp-explode-atom (true-listp (explode-atom n print-base)) :rule-classes :type-prescription) (encapsulate () ; The following local events create perfectly good rewrite rules, but we avoid ; the possibility of namespace clashes for existing books by making them local ; as we add them after Version_4.3. (local (defthm character-listp-explode-nonnegative-integer (implies (character-listp ans) (character-listp (explode-nonnegative-integer n print-base ans))))) (local (defthm character-listp-explode-atom (character-listp (explode-atom n print-base)) :hints ; need to disable this local lemma from axioms.lisp (("Goal" :in-theory (disable character-listp-cdr))))) (local (defthm character-listp-chars-for-tilde-@-clause-id-phrase/periods (character-listp (chars-for-tilde-@-clause-id-phrase/periods lst)))) (verify-termination-boot-strap string-for-tilde-@-clause-id-phrase)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; verify-termination and guard verification: ; strict-merge-symbol-<, strict-merge-sort-symbol-<, strict-symbol-<-sortedp, ; and sort-symbol-listp ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (verify-termination-boot-strap strict-merge-symbol-< (declare (xargs :measure (+ (len l1) (len l2))))) (encapsulate () (local (defthm len-strict-merge-symbol-< (<= (len (strict-merge-symbol-< l1 l2 acc)) (+ (len l1) (len l2) (len acc))) :rule-classes :linear)) (local (defthm len-evens (equal (len l) (+ (len (evens l)) (len (odds l)))) :rule-classes :linear)) (local (defthm symbol-listp-evens (implies (symbol-listp x) (symbol-listp (evens x))) :hints (("Goal" :induct (evens x))))) (local (defthm symbol-listp-odds (implies (symbol-listp x) (symbol-listp (odds x))))) (local (defthm symbol-listp-strict-merge-symbol-< (implies (and (symbol-listp l1) (symbol-listp l2) (symbol-listp acc)) (symbol-listp (strict-merge-symbol-< l1 l2 acc))))) (verify-termination-boot-strap strict-merge-sort-symbol-< (declare (xargs :measure (len l) :verify-guards nil))) (defthm symbol-listp-strict-merge-sort-symbol-< ; This lemma is non-local because it is needed for "make proofs", for ; guard-verification for new-verify-guards-fns1. (implies (symbol-listp x) (symbol-listp (strict-merge-sort-symbol-< x)))) (verify-guards strict-merge-sort-symbol-<) (verify-termination-boot-strap strict-symbol-<-sortedp) ; and guards (verify-termination-boot-strap sort-symbol-listp) ; and guards ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Theories ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deftheory definition-minimal-theory (definition-runes *definition-minimal-theory* nil world)) (deftheory executable-counterpart-minimal-theory (definition-runes *built-in-executable-counterparts* t world)) (deftheory minimal-theory ; Warning: The resulting value must be a runic-theoryp. See ; theory-fn-callp. ; Keep this definition in sync with translate-in-theory-hint. (union-theories (theory 'definition-minimal-theory) (union-theories ; Without the :executable-counterpart of force, the use of (theory ; 'minimal-theory) will produce the warning "Forcing has transitioned ; from enabled to disabled", at least if forcing is enabled (as is the ; default). '((:executable-counterpart force)) (theory 'executable-counterpart-minimal-theory))) :doc ":Doc-Section Theories a minimal theory to enable~/~/ This ~ilc[theory] (~pl[theories]) enables only a few built-in functions and executable counterparts. It can be useful when you want to formulate lemmas that rather immediately imply the theorem to be proved, by way of a ~c[:use] hint (~pl[hints]), for example as follows. ~bv[] :use (lemma-1 lemma-2 lemma-3) :in-theory (union-theories '(f1 f2) (theory 'minimal-theory)) ~ev[] In this example, we expect the current goal to follow from lemmas ~c[lemma-1], ~c[lemma-2], and ~c[lemma-3] together with rules ~c[f1] and ~c[f2] and some obvious facts about built-in functions (such as the ~il[definition] of ~ilc[implies] and the ~c[:]~ilc[executable-counterpart] of ~ilc[car]). The ~c[:]~ilc[in-theory] hint above is intended to speed up the proof by turning off all inessential rules.~/ :cited-by theory-functions") ; See the Essay on the Status of the Tau System During and After Bootstrapping ; in axioms.lisp where we discuss choices (1.a), (1.b), (2.a) and (2.b) ; related to the status of the tau system. Here is where we implement ; (2.a). (in-theory (if (cadr *tau-status-boot-strap-settings*) ; (2.a) (enable (:executable-counterpart tau-system)) (disable (:executable-counterpart tau-system)))) (deftheory ground-zero (current-theory :here) ; WARNING: Keep this near the end of *acl2-pass-2-files* in order for ; the ground-zero theory to be as expected. :doc ":Doc-Section Theories ~il[enable]d rules in the ~il[startup] theory~/ ACL2 concludes its initialization ~c[(boot-strapping)] procedure by defining the theory ~c[ground-zero]; ~pl[theories]. In fact, this theory is just the theory defined by ~c[(current-theory :here)] at the conclusion of initialization; ~pl[current-theory].~/ Note that by evaluating the event ~bv[] (in-theory (current-theory 'ground-zero)) ~ev[] you can restore the current theory to its value at the time you started up ACL2.~/ :cited-by theory-functions") ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; meta-extract support ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (verify-termination-boot-strap formals) ; and guards (verify-termination-boot-strap constraint-info) ; and guards (defund meta-extract-formula (name state) ; This function supports meta-extract-global-fact+. It needs to be executable ; and in :logic mode (hence, as required by the ACL2 build process, ; guard-verified), since it may be called by meta functions. ; While this function can be viewed as a version of formula, it applies only to ; symbols (not runes), it is in :logic mode, and there are a few other ; differences as well. The present function requires name to be a symbol and ; only returns a normalp=nil form of body. (Otherwise, in order to put body in ; :logic mode, body would need to be guard-verified, which would probably take ; considerable effort.) (declare (xargs :stobjs state :guard (symbolp name))) (let ((wrld (w state))) (or (getprop name 'theorem nil 'current-acl2-world wrld) (mv-let (flg prop) (constraint-info name wrld) (cond ((eq prop *unknown-constraints*) *t*) (flg (ec-call (conjoin prop))) (t prop)))))) (verify-termination-boot-strap type-set-quote) (verify-guards type-set-quote) (defun typespec-check (ts x) (declare (xargs :guard (integerp ts))) (if (bad-atom x) (< ts 0) ; bad-atom type intersects every complement type ; We would like to write ; (ts-subsetp (type-set-quote x) ts) ; here, but for that we need a stronger guard than (integerp ts), and we prefer ; to keep this simple. (not (eql 0 (logand (type-set-quote x) ts))))) (defun meta-extract-rw+-term (term alist equiv rhs state) ; This function supports the function meta-extract-contextual-fact. Neither of ; these functions is intended to be executed. ; Meta-extract-rw+-term creates (logically) a term claiming that term under ; alist is equiv to rhs, where equiv=nil represents 'equal and equiv=t ; represents 'iff. If equiv is not t, nil, or an equivalence relation, then ; *t* is returned. ; Note that this function does not support the use of a geneqv for the equiv ; argument. (declare (xargs :mode :program ; becomes :logic with system-verify-guards :stobjs state :guard (and (symbol-alistp alist) (pseudo-term-listp (strip-cdrs alist)) (pseudo-termp term)))) (non-exec (let ((lhs (sublis-var alist term))) (case equiv ((nil) `(equal ,lhs ,rhs)) ((t) `(iff ,lhs ,rhs)) (otherwise (if (symbolp equiv) (if (equivalence-relationp equiv (w state)) `(,equiv ,lhs ,rhs) ; else bad equivalence relation *t*) *t*)))))) (defun meta-extract-contextual-fact (obj mfc state) ; This function is not intended to be executed. ; This function may be called in the hypothesis of a meta rule, because we know ; it always produces a term that evaluates to non-nil under the mfc where the ; metafunction is called, using the specific alist A for which we're proving ; (evl x a) = (evl (metafn x) a). The terms it produces reflect the ; correctness of certain prover operations -- currently, accessing type-alist ; and typeset information, rewriting, and linear arithmetic. See the Essay on ; Correctness of Meta Reasoning. Note that these operations use the state for ; heuristic purposes, and get their logical information from the world stored ; in mfc (not in state). ; This function avoids forcing and does not return a tag-tree. (declare (xargs :mode :program ; becomes :logic with system-verify-guards :stobjs state)) (non-exec (case-match obj ((':typeset term . &) ; mfc-ts produces correct results `(typespec-check ',(mfc-ts term mfc state :forcep nil :ttreep nil) ,term)) ((':rw+ term alist obj equiv . &) ; result is equiv to term/alist. (meta-extract-rw+-term term alist equiv (mfc-rw+ term alist obj equiv mfc state :forcep nil :ttreep nil) state)) ((':rw term obj equiv . &) ; as for :rw+, with alist of nil (meta-extract-rw+-term term nil equiv (mfc-rw term obj equiv mfc state :forcep nil :ttreep nil) state)) ((':ap term . &) ; Can linear arithmetic can falsify term? (if (mfc-ap term mfc state :forcep nil) `(not ,term) *t*)) ((':relieve-hyp hyp alist rune target bkptr . &) ; hyp/alist proved? (if (mfc-relieve-hyp hyp alist rune target bkptr mfc state :forcep nil :ttreep nil) (sublis-var alist hyp) *t*)) (& *t*)))) (defun rewrite-rule-term (x) ; This function is not intended to be executed. It turns a rewrite-rule record ; into a term. (declare (xargs :guard t)) (non-exec (if (eq (access rewrite-rule x :subclass) 'meta) *t* `(implies ,(conjoin (access rewrite-rule x :hyps)) (,(access rewrite-rule x :equiv) ,(access rewrite-rule x :lhs) ,(access rewrite-rule x :rhs)))))) (defmacro meta-extract-global-fact (obj state) ; See meta-extract-global-fact+. `(meta-extract-global-fact+ ,obj ,state ,state)) (defun fncall-term (fn arglist state) (declare (xargs :stobjs state :guard (true-listp arglist))) (mv-let (erp val) (magic-ev-fncall fn arglist state t ; hard-error-returns-nilp nil ; aok ) (cond (erp *t*) (t (fcons-term* 'equal ; As suggested by Sol Swords, we use fcons-term below in order to avoid having ; to reason about the application of an evaluator to (cons-term fn ...). (fcons-term fn (kwote-lst arglist)) (kwote val)))))) (defun logically-equivalent-states (st1 st2) (declare (xargs :guard t)) (non-exec (equal (w st1) (w st2)))) (defun meta-extract-global-fact+ (obj st state) ; This function is not intended to be executed. ; This function may be called in the hypothesis of a meta rule, because we know ; it always produces a term that evaluates to non-nil for any alist. The terms ; it produces reflect the correctness of certain facts stored in the world. ; See the Essay on Correctness of Meta Reasoning. (declare (xargs :mode :program ; becomes :logic with system-verify-guards :stobjs state)) (non-exec (cond ((logically-equivalent-states st state) (case-match obj ((':formula name) (meta-extract-formula name st)) ((':lemma fn n) (let* ((lemmas (getprop fn 'lemmas nil 'current-acl2-world (w st))) (rule (nth n lemmas))) ; The use of rewrite-rule-term below relies on the fact that the 'LEMMAS ; property of a symbol in the ACL2 world is a list of rewrite-rule records that ; reflect known facts. (if (< (nfix n) (len lemmas)) (rewrite-rule-term rule) *t*))) ; Fn doesn't exist or n is too big. ((':fncall fn arglist) (non-exec ; avoid guard check (fncall-term fn arglist st))) (& *t*))) (t *t*)))) (add-macro-alias meta-extract-global-fact meta-extract-global-fact+) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Support for system-verify-guards ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This section supports a mechanism for users to extend the set of ; guard-verified functions. They do so in community books under books/system/, ; which are checked when building with feature :acl2-devel, for example ; building with `make' with ACL2_DEVEL=d. But normal builds will not set that ; feature, and will simply trust that functions marked in ; *system-verify-guards-alist* can be guard-verified. ; A flaw in our approach is that user-supplied guard verifications may depend ; on package axioms. Thus, we view such verifications as strong hints, rather ; than as ironclad guarantees that the functions can be guard-verified in ; definitional (or even conservative) extensions of the ground-zero theory. We ; consider this sufficient, as the event that some package axiom will cause ; such bogus marking as guard-verified seems much less likely than the event ; that our system has other serious bugs! (verify-termination-boot-strap safe-access-command-tuple-form) ; and guards (defun pair-fns-with-measured-subsets (fns wrld acc) (declare (xargs :guard (and (symbol-listp fns) (plist-worldp wrld) (true-listp acc)))) (cond ((endp fns) (reverse acc)) (t (pair-fns-with-measured-subsets (cdr fns) wrld (cons (let* ((fn (car fns)) (justification (getprop fn 'justification nil 'current-acl2-world wrld)) (ms (and (consp justification) ; for guard (consp (cdr justification)) ; for guard (access justification justification :subset)))) (cons fn ms)) acc))))) (defun new-verify-guards-fns1 (wrld installed-wrld acc) (declare (xargs :guard (and (plist-worldp wrld) (plist-worldp installed-wrld) (symbol-listp acc)))) (cond ((or (endp wrld) (and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value) (equal (safe-access-command-tuple-form (cddar wrld)) '(exit-boot-strap-mode)))) (pair-fns-with-measured-subsets (strict-merge-sort-symbol-< acc) installed-wrld nil)) ((and (eq (cadar wrld) 'symbol-class) (eq (cddar wrld) :COMMON-LISP-COMPLIANT) (getprop (caar wrld) 'predefined nil 'current-acl2-world installed-wrld)) (new-verify-guards-fns1 (cdr wrld) installed-wrld (cons (caar wrld) acc))) (t (new-verify-guards-fns1 (cdr wrld) installed-wrld acc)))) (defun new-verify-guards-fns (state) ; It is important for performance that this function be guard-verified, because ; it is called inside an assert-event form in chk-new-verified-guards, which ; causes evaluation to be in safe-mode and would cause evaluation of ; plist-worldp on behalf of guard-checking for new-verify-guards-fns1. (declare (xargs :stobjs state)) (let ((wrld (w state))) (new-verify-guards-fns1 wrld wrld nil))) (defconst *system-verify-guards-alist* ; Each cdr was produced by evaluating ; (new-verify-guards-fns state) ; after including the book indicated in the car in a build with feature ; :acl2-devel set (see discussion in the comment at the top of this section). ; For example, cdr of the entry for "system/top" is produced by evaluating: ; (include-book "system/top" :dir :system). ; The indicated books need to be certified using an ACL2 executable that was ; built with feature :acl2-devel set, but this takes about 2.5 minutes on a ; fast machine in Feb. 2013, as follows: ; make -j 8 regression ACL2_BOOK_DIRS=system ACL2=<:acl2-devel version> ; Each member of each cdr below is of the form (fn . measured-subset). ; Note that it is not necessary to do a full regression with an :acl2-devel ; executable; only the books in the keys of this alist need to be certified. '(("system/top" (ARGLISTP) (ARGLISTP1 LST) (CONS-TERM1-MV2) (DUMB-NEGATE-LIT) (FETCH-DCL-FIELD) (FETCH-DCL-FIELDS LST) (FETCH-DCL-FIELDS1 LST) (FETCH-DCL-FIELDS2 KWD-LIST) (FIND-FIRST-BAD-ARG ARGS) (LAMBDA-KEYWORDP) (LEGAL-CONSTANTP1) (LEGAL-VARIABLE-OR-CONSTANT-NAMEP) (LEGAL-VARIABLEP) (META-EXTRACT-CONTEXTUAL-FACT) (META-EXTRACT-GLOBAL-FACT+) (META-EXTRACT-RW+-TERM) (MISSING-FMT-ALIST-CHARS) (MISSING-FMT-ALIST-CHARS1 CHAR-TO-TILDE-S-STRING-ALIST) (PLAUSIBLE-DCLSP LST) (PLAUSIBLE-DCLSP1 LST) (STRIP-DCLS LST) (STRIP-DCLS1 LST) (STRIP-KEYWORD-LIST LST) (SUBCOR-VAR FORM) (SUBCOR-VAR-LST FORMS) (SUBCOR-VAR1 VARS) (SUBLIS-VAR) (SUBLIS-VAR-LST) (SUBLIS-VAR1 FORM) (SUBLIS-VAR1-LST L) (SUBST-EXPR) (SUBST-EXPR-ERROR) (SUBST-EXPR1 TERM) (SUBST-EXPR1-LST ARGS) (SUBST-VAR FORM) (SUBST-VAR-LST L)))) (defconst *len-system-verify-guards-alist* (length *system-verify-guards-alist*)) (defmacro chk-new-verified-guards (n) (cond ((or (not (natp n)) (> n *len-system-verify-guards-alist*)) `(er soft 'chk-new-verified-guards "The index ~x0 is not a valid index for *system-verify-guards-alist*." ',n)) ((eql n *len-system-verify-guards-alist*) '(value-triple :CHK-NEW-VERIFIED-GUARDS-COMPLETE)) (t (let* ((pair (nth n *system-verify-guards-alist*)) (user-book-name (car pair)) (fns (cdr pair))) `(progn (include-book ,user-book-name :DIR :SYSTEM :UNCERTIFIED-OKP nil :DEFAXIOMS-OKP nil :SKIP-PROOFS-OKP nil :TTAGS nil) (assert-event (equal ',fns (new-verify-guards-fns state)) :msg (msg "ERROR: The set of newly guard-verified functions ~ from the ACL2 community book ~x0 does not match the ~ expected set from the constant ~ *system-verify-guards-alist*.~|~%From the ~ book:~|~X13~|~%Expected from ~ *system-verify-guards-alist*:~|~X23~|" ',user-book-name (new-verify-guards-fns state) ',fns nil)) (value-triple :CHK-NEW-VERIFIED-GUARDS-SUCCESS)))))) (defun system-verify-guards-fn-1 (fns-alist acc) (declare (xargs :guard (symbol-alistp fns-alist))) (cond ((endp fns-alist) acc) (t (system-verify-guards-fn-1 (cdr fns-alist) (cons `(skip-proofs (verify-termination-boot-strap ; and guards ,(caar fns-alist) ,@(let ((ms (cdar fns-alist))) (and ms `((declare (xargs :measure (:? ,@ms)))))))) acc))))) (defun cons-absolute-event-numbers (fns-alist wrld acc) (declare (xargs :guard (and (symbol-alistp fns-alist) (plist-worldp wrld) (alistp acc)))) (cond ((endp fns-alist) acc) (t (cons-absolute-event-numbers (cdr fns-alist) wrld (acons (or (getprop (caar fns-alist) 'absolute-event-number nil 'current-acl2-world wrld) (er hard? 'cons-absolute-event-numbers "The 'absolute-event-number property is missing ~ for ~x0." (caar fns-alist))) (car fns-alist) acc))))) (defun sort->-absolute-event-number (fns-alist wrld) (declare (xargs :mode :program)) ; because of merge-sort-car-> (strip-cdrs (merge-sort-car-> (cons-absolute-event-numbers fns-alist wrld nil)))) (defun system-verify-guards-fn (alist wrld acc) (declare (xargs :mode :program)) ; because of sort->-absolute-event-number (cond ((endp alist) acc) (t (system-verify-guards-fn (cdr alist) wrld (system-verify-guards-fn-1 (sort->-absolute-event-number (cdar alist) wrld) acc))))) (defmacro system-verify-guards () `(make-event (let ((events (system-verify-guards-fn *system-verify-guards-alist* (w state) nil))) (list* 'encapsulate () '(set-verify-guards-eagerness 2) events)))) ; Normally we go ahead and trust *system-verify-guards-alist*, installing ; guard-verified functions with the following form. But when feature ; :acl2-devel is set, then we do not do so, as we instead intend to run ; (chk-new-verified-guards i) for each i less than the length of ; *system-verify-guards-alist*, in order to check that the effect of ; system-verify-guards is sound. This check is performed by using `make' with ; target devel-check, for example as follows, where denotes a full ; pathname for a build of ACL2 using feature :acl2-devel (see comments above ; for how to make such a build): ; (time nice make -j 8 regression-fresh devel-check ACL2=) #+(and acl2-loop-only ; Note that make-event can't be called here in raw Lisp. (not acl2-devel)) (system-verify-guards) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; End ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (deflabel ; WARNING: Put this at the end of the last file in ; *acl2-pass-2-files*. end-of-pass-2) acl2-sources/build-allegro-exe.cl0000664002132200015000000000641611605147054016406 0ustar kaufmannacl2; NOTE: An Allegro CL dynamic runtime license is needed in order for this to ; create an Allegro CL ACL2 application acl2.exe in the bin/ directory. ; See target allegro-app in GNUmakefile. #| Execute the following form from the ACL2 sources directory, setting the variable *acl2-application-distributed-books-dir* if necessary as shown below (probably not necessary). echo '(generate-application "acl2.exe" (or (sys::getenv "ACL2_BIN_DIR") "app/") (quote ("../build-image.cl")) :runtime :dynamic :include-compiler t)' \ | lisp >& generate-application.log& Then look at generate-application.log and see if it's OK, especially near the end. |# ; | ; The file our-develenv.cl will not be distributed with ACL2. It is obtained ; from a path such as /AllegroCL-7.0/acl70/develenv.cl, then ; commenting out those not allowed in runtime images, as suggested in the above ; file. (load "our-develenv.cl") (load "init.lisp") (in-package "ACL2") ; The sources had better already be compiled! The following form just informs ; ACL2 of that fact. (note-compile-ok) ; We are attempting to replicate the effects of this form from the Makefile: ; (acl2::save-acl2 (quote (acl2::initialize-acl2 (quote acl2::include-book) acl2::*acl2-pass-2-files* t)) "${PREFIXsaved_acl2}") ; From save-acl2 (load-acl2) (setq *saved-build-date* (saved-build-date-string)) ; It is legal to replace nil below by, e.g., "/u/acl2/v2-9/acl2-sources/books/". (defconstant *acl2-application-distributed-books-dir* nil) (initialize-acl2 (quote include-book) *acl2-pass-2-files* *acl2-application-distributed-books-dir*) ; Adapted from save-acl2-in-allegro (setq *saved-mode* (quote (initialize-acl2 (quote include-book) *acl2-pass-2-files* *acl2-application-distributed-books-dir*))) (tpl:setq-default *PACKAGE* (find-package "ACL2")) (setq EXCL:*RESTART-INIT-FUNCTION* 'acl2-default-restart) (load "allegro-acl2-trace.lisp") ; Copied exactly from acl2-init.lisp: (defvar *acl2-default-restart-complete* nil) ; Copied exactly from acl2-init.lisp: (defun acl2-default-restart () (if *acl2-default-restart-complete* (return-from acl2-default-restart nil)) (format t *saved-string* *copy-of-acl2-version* *saved-build-date* (cond (*saved-mode* (format nil "~% Initialized with ~a." *saved-mode*)) (t "")) (eval '(latest-release-note-string)) ; avoid possible warning ) (maybe-load-acl2-init) (in-package "ACL2") ; The following two lines follow the recommendation in Allegro CL's ; documentation file doc/delivery.htm. #+allegro (tpl:setq-default *package* (find-package "ACL2")) #+allegro (rplacd (assoc 'tpl::*saved-package* tpl:*default-lisp-listener-bindings*) 'common-lisp:*package*) #+allegro (lp) #+openmcl (eval '(lp)) ; using eval to avoid compiler warning ; See the comment in save-acl2-in-lispworks for why we need the following call. #+lispworks (mp:initialize-multiprocessing) ;;Lispworks 4.2.0 no longer recognizes the following: ;;#+lispworks (lw::extend-current-stack 1000) (setq *acl2-default-restart-complete* t) nil) ; Now optionally include some books. ;(load "/home/kaufmann/foo.fasl") acl2-sources/defpkgs.lisp0000664002132200015000000007156212222115527015101 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; This file, defpkgs.lisp, illustrates the idea that defpkg forms ; should be off in files all by themselves. We require defpkg forms ; to be in files all by themselves to support the compilation of ; files. Files with defpkg forms should only be loaded, never ; compiled. Of course, during the compilation of other files, it will ; be necessary for defpkg files to be loaded at the appropriate time. ; The idea of putting defpkg forms in separate files is in the spirit ; of the CLTL2 idea of putting DEFPACKAGE forms in separate files. By ; keeping package creation separate from compilation, one avoids many ; pitfalls and inconsistencies between Common Lisp implementations. (in-package "ACL2") (defconst *acl2-exports* ; This constant provides a handy list of symbols to export from the ACL2 ; package. It includes all documented constants, functions, and macros, except ; for those that we explicitly exclude, as checked by certifying community book ; books/misc/check-acl2-exports.lisp. See that book for the list of symbols, ; *acl2-exports-exclusions*, that we explicitly exclude from this list. ; We keep this list sorted, since that makes defpkg more efficient when users ; choose to import these symbols; it avoids having to sort them then. ; The following ``policy'' was used to determine this setting of *acl2-exports*. ; First, if the user wishes to program in ACL2, he or she will import ; *common-lisp-symbols-from-main-lisp-package* in addition to *acl2-exports*. ; So this list was set primarily with theorem proving in mind. ; Prior to ACL2 Version_2.4, the list was short. It contained 55 symbols. ; Before the release of ACL2 Version_2.5 we added symbols to the list. The ; symbols added were, in some cases, dependent on the :DOC topics as of 2.5. ; (a) all :logic mode functions ; (b) most of the symbols users had imported into packages in books/, ; (c) selected :program mode functions with :DOC topics, ; (d) all macros with :DOC topics, ; (e) selected constants with :DOC topics, ; (f) certain other constants ; (g) symbols used to write defuns and theorems, gathered by looking ; at the documentation for DECLARE, HINTS, RULE-CLASSES, MACROS ; This is still not very systematic, because there is a fundamental tension: if ; we make it conveniently large we import symbols the user might wish to ; define. (sort-symbol-listp (append *hons-primitives* ; even for non-hons version, for compatibility of the two '(TRACE* ; not defined by ACL2, but may well be defined in a book GRANULARITY ; for parallelism primitives ) '(& &ALLOW-OTHER-KEYS &AUX &BODY &KEY &OPTIONAL &REST &WHOLE * *ACL2-EXPORTS* *COMMON-LISP-SPECIALS-AND-CONSTANTS* *COMMON-LISP-SYMBOLS-FROM-MAIN-LISP-PACKAGE* *MAIN-LISP-PACKAGE-NAME* *STANDARD-CHARS* *STANDARD-CI* *STANDARD-CO* *STANDARD-OI* + - / /= 1+ 1- 32-BIT-INTEGER-LISTP 32-BIT-INTEGER-LISTP-FORWARD-TO-INTEGER-LISTP 32-BIT-INTEGER-STACK 32-BIT-INTEGER-STACK-LENGTH 32-BIT-INTEGER-STACK-LENGTH1 32-BIT-INTEGERP 32-BIT-INTEGERP-FORWARD-TO-INTEGERP < <-ON-OTHERS <= = > >= ?-FN @ A! ABORT! ABS ACCUMULATED-PERSISTENCE ACL2-COUNT ACL2-INPUT-CHANNEL-PACKAGE ACL2-NUMBER-LISTP ACL2-NUMBERP ACL2-ORACLE ACL2-OUTPUT-CHANNEL-PACKAGE ACL2-PACKAGE ACONS ACTIVE-RUNEP ADD-BINOP ADD-CUSTOM-KEYWORD-HINT ADD-DEFAULT-HINTS ADD-DEFAULT-HINTS! ADD-INCLUDE-BOOK-DIR ADD-INVISIBLE-FNS ADD-LD-KEYWORD-ALIAS ADD-LD-KEYWORD-ALIAS! ADD-MACRO-ALIAS ADD-MACRO-FN ADD-MATCH-FREE-OVERRIDE ADD-NTH-ALIAS ADD-OVERRIDE-HINTS ADD-OVERRIDE-HINTS! ADD-PAIR ADD-PAIR-PRESERVES-ALL-BOUNDP ADD-RAW-ARITY ADD-TIMERS ADD-TO-SET ADD-TO-SET-EQ ADD-TO-SET-EQL ADD-TO-SET-EQUAL ALISTP ALISTP-FORWARD-TO-TRUE-LISTP ALL-BOUNDP ALL-BOUNDP-PRESERVES-ASSOC ALL-VARS ALL-VARS1 ALL-VARS1-LST ALLOCATE-FIXNUM-RANGE ALPHA-CHAR-P ALPHA-CHAR-P-FORWARD-TO-CHARACTERP ALPHORDER AND AND-MACRO APPEND AREF-32-BIT-INTEGER-STACK AREF-T-STACK AREF1 AREF2 ARGS ARRAY1P ARRAY1P-CONS ARRAY1P-FORWARD ARRAY1P-LINEAR ARRAY2P ARRAY2P-CONS ARRAY2P-FORWARD ARRAY2P-LINEAR ASET-32-BIT-INTEGER-STACK ASET-T-STACK ASET1 ASET2 ASH ASSERT$ ASSERT-EVENT ASSIGN ASSOC ASSOC-ADD-PAIR ASSOC-EQ ASSOC-EQ-EQUAL ASSOC-EQ-EQUAL-ALISTP ASSOC-EQUAL ASSOC-KEYWORD ASSOC-STRING-EQUAL ASSOC2 ASSOCIATIVITY-OF-* ASSOCIATIVITY-OF-+ ASSUME ATOM ATOM-LISTP ATOM-LISTP-FORWARD-TO-TRUE-LISTP BACKCHAIN-LIMIT BIG-CLOCK-ENTRY BIG-CLOCK-NEGATIVE-P BINARY-* BINARY-+ BINARY-APPEND BIND-FREE BIT BOOLE$ BOOLEAN-LISTP BOOLEAN-LISTP-CONS BOOLEAN-LISTP-FORWARD BOOLEAN-LISTP-FORWARD-TO-SYMBOL-LISTP BOOLEANP BOOLEANP-CHARACTERP BOOLEANP-COMPOUND-RECOGNIZER BOUNDED-INTEGER-ALISTP BOUNDED-INTEGER-ALISTP-FORWARD-TO-EQLABLE-ALISTP BOUNDED-INTEGER-ALISTP2 BOUNDP-GLOBAL BOUNDP-GLOBAL1 BREAK$ BREAK-ON-ERROR BRR BRR@ BUILD-STATE1 BUTLAST CAAAAR CAAADR CAAAR CAADAR CAADDR CAADR CAAR CADAAR CADADR CADAR CADDAR CADDDR CADDR CADR CANONICAL-PATHNAME CAR CAR-CDR-ELIM CAR-CONS CASE CASE-LIST CASE-LIST-CHECK CASE-MATCH CASE-SPLIT CASE-SPLIT-LIMITATIONS CASE-TEST CBD CDAAAR CDAADR CDAAR CDADAR CDADDR CDADR CDAR CDDAAR CDDADR CDDAR CDDDAR CDDDDR CDDDR CDDR CDR CDR-CONS CDRN CEILING CERTIFY-BOOK CERTIFY-BOOK! CHAR CHAR-CODE CHAR-CODE-CODE-CHAR-IS-IDENTITY CHAR-CODE-LINEAR CHAR-DOWNCASE CHAR-EQUAL CHAR-UPCASE CHAR< CHAR<= CHAR> CHAR>= CHARACTER CHARACTER-ALISTP CHARACTER-LISTP CHARACTER-LISTP-APPEND CHARACTER-LISTP-COERCE CHARACTER-LISTP-FORWARD-TO-EQLABLE-LISTP CHARACTER-LISTP-REMOVE-DUPLICATES-EQL CHARACTER-LISTP-REVAPPEND CHARACTER-LISTP-STRING-DOWNCASE-1 CHARACTER-LISTP-STRING-UPCASE1-1 CHARACTERP CHARACTERP-CHAR-DOWNCASE CHARACTERP-CHAR-UPCASE CHARACTERP-NTH CHARACTERP-PAGE CHARACTERP-RUBOUT CHARACTERP-TAB CHECK-VARS-NOT-FREE CHECKPOINT-FORCED-GOALS CLAUSE CLOSE-INPUT-CHANNEL CLOSE-OUTPUT-CHANNEL CLOSE-TRACE-FILE CLOSURE CODE-CHAR CODE-CHAR-CHAR-CODE-IS-IDENTITY CODE-CHAR-TYPE COERCE COERCE-INVERSE-1 COERCE-INVERSE-2 COERCE-OBJECT-TO-STATE COERCE-STATE-TO-OBJECT COMMUNITY-BOOKS COMMUTATIVITY-OF-* COMMUTATIVITY-OF-+ COMP COMPLETION-OF-* COMPLETION-OF-+ COMPLETION-OF-< COMPLETION-OF-CAR COMPLETION-OF-CDR COMPLETION-OF-CHAR-CODE COMPLETION-OF-CODE-CHAR COMPLETION-OF-COERCE COMPLETION-OF-COMPLEX COMPLETION-OF-DENOMINATOR COMPLETION-OF-IMAGPART COMPLETION-OF-INTERN-IN-PACKAGE-OF-SYMBOL COMPLETION-OF-NUMERATOR COMPLETION-OF-REALPART COMPLETION-OF-SYMBOL-NAME COMPLETION-OF-SYMBOL-PACKAGE-NAME COMPLETION-OF-UNARY-/ COMPLETION-OF-UNARY-MINUS COMPLEX COMPLEX-0 COMPLEX-DEFINITION COMPLEX-EQUAL COMPLEX-IMPLIES1 COMPLEX-RATIONALP COMPLEX/COMPLEX-RATIONALP COMPRESS1 COMPRESS11 COMPRESS2 COMPRESS21 COMPRESS211 CONCATENATE COND COND-CLAUSESP COND-MACRO CONJUGATE CONS CONS-EQUAL CONSP CONSP-ASSOC-EQUAL COROLLARY CPU-CORE-COUNT CURRENT-PACKAGE CURRENT-THEORY CW CW! CW-GSTACK DECLARE DECREMENT-BIG-CLOCK DEFABBREV DEFABSSTOBJ DEFABSSTOBJ-MISSING-EVENTS DEFATTACH DEFAULT DEFAULT-*-1 DEFAULT-*-2 DEFAULT-+-1 DEFAULT-+-2 DEFAULT-<-1 DEFAULT-<-2 DEFAULT-BACKCHAIN-LIMIT DEFAULT-CAR DEFAULT-CDR DEFAULT-CHAR-CODE DEFAULT-COERCE-1 DEFAULT-COERCE-2 DEFAULT-COERCE-3 DEFAULT-COMPILE-FNS DEFAULT-COMPLEX-1 DEFAULT-COMPLEX-2 DEFAULT-DEFUN-MODE DEFAULT-DEFUN-MODE-FROM-STATE DEFAULT-DENOMINATOR DEFAULT-HINTS DEFAULT-IMAGPART DEFAULT-MEASURE-FUNCTION DEFAULT-NUMERATOR DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT DEFAULT-PRINT-PROMPT DEFAULT-REALPART DEFAULT-RULER-EXTENDERS DEFAULT-SYMBOL-NAME DEFAULT-SYMBOL-PACKAGE-NAME DEFAULT-UNARY-/ DEFAULT-UNARY-MINUS DEFAULT-VERIFY-GUARDS-EAGERNESS DEFAULT-WELL-FOUNDED-RELATION DEFAXIOM DEFCHOOSE DEFCONG DEFCONST DEFDOC DEFEQUIV DEFEVALUATOR DEFEXEC DEFINE-PC-ATOMIC-MACRO DEFINE-PC-HELP DEFINE-PC-MACRO DEFINE-PC-META DEFINE-TRUSTED-CLAUSE-PROCESSOR DEFLABEL DEFLOCK DEFMACRO DEFMACRO-LAST DEFN DEFND DEFPKG DEFPROXY DEFREFINEMENT DEFSTOBJ DEFSTUB DEFTHEORY DEFTHEORY-STATIC DEFTHM DEFTHMD DEFTTAG DEFUN DEFUN-INLINE DEFUN-NOTINLINE DEFUN-NX DEFUN-SK DEFUND DEFUND-INLINE DEFUND-NOTINLINE DEFUND-NX DEFUNS DELETE-ASSOC DELETE-ASSOC-EQ DELETE-ASSOC-EQUAL DELETE-INCLUDE-BOOK-DIR DENOMINATOR DIGIT-CHAR-P DIGIT-TO-CHAR DIMENSIONS DISABLE DISABLE-FORCING DISABLE-IMMEDIATE-FORCE-MODEP DISABLEDP DISASSEMBLE$ DISTRIBUTIVITY DMR-START DMR-STOP DOC DOC! DOCS DOUBLE-REWRITE DUPLICATES E/D E0-ORD-< E0-ORDINALP EC-CALL EIGHTH ELIMINATE-DESTRUCTORS ELIMINATE-IRRELEVANCE ENABLE ENABLE-FORCING ENABLE-IMMEDIATE-FORCE-MODEP ENCAPSULATE ENDP EQ EQL EQLABLE-ALISTP EQLABLE-ALISTP-FORWARD-TO-ALISTP EQLABLE-LISTP EQLABLE-LISTP-FORWARD-TO-ATOM-LISTP EQLABLEP EQLABLEP-RECOG EQUAL EQUAL-CHAR-CODE ER ER-PROGN ER-PROGN-FN ER-PROGN@PAR ER-PROGN-FN@PAR EVENP EVENS EVENT EVISC-TUPLE EXECUTABLE-COUNTERPART-THEORY EXIT EXPLODE-ATOM EXPLODE-NONNEGATIVE-INTEGER EXPT EXPT-TYPE-PRESCRIPTION-NON-ZERO-BASE EXTEND-32-BIT-INTEGER-STACK EXTEND-T-STACK EXTEND-WORLD EXTRA-INFO F-GET-GLOBAL F-PUT-GLOBAL FAST-ALIST-FREE-ON-EXIT FERTILIZE FC-REPORT FGETPROP FIFTH FILE-CLOCK FILE-CLOCK-P FILE-CLOCK-P-FORWARD-TO-INTEGERP FINALIZE-EVENT-USER FIRST FIRST-N-AC FIX FIX-TRUE-LIST FLET FLOOR FLUSH-COMPRESS FMS FMS! FMT FMT! FMT-TO-COMMENT-WINDOW FMT1 FMT1! FMS-TO-STRING FMS!-TO-STRING FMT-TO-STRING FMT!-TO-STRING FMT1-TO-STRING FMT1!-TO-STRING FNCALL-TERM FORCE FOURTH FUNCTION-SYMBOLP FUNCTION-THEORY GAG-MODE GC$ GC-VERBOSE GENERALIZE GET-COMMAND-SEQUENCE GET-GLOBAL GET-OUTPUT-STREAM-STRING$ GET-TIMER GET-WORMHOLE-STATUS GCS GETENV$ GETPROP GETPROP-DEFAULT GETPROPS GETPROPS1 GLOBAL-TABLE GLOBAL-TABLE-CARS GLOBAL-TABLE-CARS1 GLOBAL-VAL GOOD-ATOM-LISTP GOOD-BYE GROUND-ZERO GUARD GUARD-OBLIGATION HARD-ERROR HAS-PROPSP HAS-PROPSP1 HEADER HELP HIDE HONS-ASSOC-EQUAL HONS-COPY-PERSISTENT I-AM-HERE ID IDATES IDENTITY IF IF* IFF IFF-IMPLIES-EQUAL-IMPLIES-1 IFF-IMPLIES-EQUAL-IMPLIES-2 IFF-IMPLIES-EQUAL-NOT IFF-IS-AN-EQUIVALENCE IFIX IGNORE ILLEGAL IMAGPART IMAGPART-COMPLEX IMMEDIATE-FORCE-MODEP IMPLIES IMPROPER-CONSP IN-ARITHMETIC-THEORY IN-PACKAGE IN-TAU-INTERVALP IN-THEORY INCLUDE-BOOK INCOMPATIBLE INCREMENT-TIMER INDUCT INITIALIZE-EVENT-USER INT= INTEGER INTEGER-0 INTEGER-1 INTEGER-ABS INTEGER-IMPLIES-RATIONAL INTEGER-LENGTH INTEGER-LISTP INTEGER-LISTP-FORWARD-TO-RATIONAL-LISTP INTEGER-STEP INTEGERP INTERN INTERN$ INTERN-IN-PACKAGE-OF-SYMBOL INTERN-IN-PACKAGE-OF-SYMBOL-SYMBOL-NAME INTERSECTION$ INTERSECTION-EQ INTERSECTION-EQUAL INTERSECTION-THEORIES INTERSECTP INTERSECTP-EQ INTERSECTP-EQUAL INVERSE-OF-* INVERSE-OF-+ INVISIBLE-FNS-TABLE KEYWORD-PACKAGE KEYWORD-VALUE-LISTP KEYWORD-VALUE-LISTP-ASSOC-KEYWORD KEYWORD-VALUE-LISTP-FORWARD-TO-TRUE-LISTP KEYWORDP KEYWORDP-FORWARD-TO-SYMBOLP KNOWN-PACKAGE-ALIST KNOWN-PACKAGE-ALISTP KNOWN-PACKAGE-ALISTP-FORWARD-TO-TRUE-LIST-LISTP-AND-ALISTP KWOTE KWOTE-LST LAMBDA LAST LAST-PROVER-STEPS LD LD-ERROR-ACTION LD-ERROR-TRIPLES LD-EVISC-TUPLE LD-KEYWORD-ALIASES LD-MISSING-INPUT-OK LD-POST-EVAL-PRINT LD-PRE-EVAL-FILTER LD-PRE-EVAL-PRINT LD-PROMPT LD-QUERY-CONTROL-ALIST LD-REDEFINITION-ACTION LD-SKIP-PROOFSP LD-VERBOSE LEGAL-CASE-CLAUSESP LEN LEN-UPDATE-NTH LENGTH LET* LEXORDER LIST LIST* LIST*-MACRO LIST-ALL-PACKAGE-NAMES LIST-ALL-PACKAGE-NAMES-LST LIST-MACRO LISTP LOCAL LOGAND LOGANDC1 LOGANDC2 LOGBITP LOGCOUNT LOGEQV LOGIC LOGIOR LOGNAND LOGNOR LOGNOT LOGORC1 LOGORC2 LOGTEST LOGXOR LOWER-CASE-P LOWER-CASE-P-CHAR-DOWNCASE LOWER-CASE-P-FORWARD-TO-ALPHA-CHAR-P LOWEST-TERMS LP MACRO-ALIASES MACRO-ARGS MAIN-TIMER MAIN-TIMER-TYPE-PRESCRIPTION MAKE-CHARACTER-LIST MAKE-CHARACTER-LIST-MAKE-CHARACTER-LIST MAKE-EVENT MAKE-FAST-ALIST MAKE-FMT-BINDINGS MAKE-INPUT-CHANNEL MAKE-LIST MAKE-LIST-AC MAKE-MV-NTHS MAKE-ORD MAKE-OUTPUT-CHANNEL MAKE-TAU-INTERVAL MAKE-VAR-LST MAKE-VAR-LST1 MAKE-WORMHOLE-STATUS MAKUNBOUND-GLOBAL MAX MAXIMUM-LENGTH MAY-NEED-SLASHES MBE MBT MEMBER MEMBER-EQ MEMBER-EQUAL MEMBER-SYMBOL-NAME META-EXTRACT-CONTEXTUAL-FACT META-EXTRACT-FORMULA META-EXTRACT-GLOBAL-FACT META-EXTRACT-GLOBAL-FACT+ META-EXTRACT-RW+-TERM MFC MIN MINIMAL-THEORY MINUSP MOD MOD-EXPT MONITOR MONITORED-RUNES MORE MORE! MORE-DOC MSG MUST-BE-EQUAL MUTUAL-RECURSION MUTUAL-RECURSION-GUARDP MV MV? MV-LET MV?-LET MV-LIST MV-NTH NAT-LISTP NATP NEEDS-SLASHES NEVER-MEMOIZE NEWLINE NFIX NIL NIL-IS-NOT-CIRCULAR NINTH NO-DUPLICATESP NO-DUPLICATESP-EQ NO-DUPLICATESP-EQUAL NON-EXEC NONNEGATIVE-INTEGER-QUOTIENT NONNEGATIVE-PRODUCT NONZERO-IMAGPART NOT NQTHM-TO-ACL2 NTH NTH-0-CONS NTH-0-READ-RUN-TIME-TYPE-PRESCRIPTION NTH-ADD1 NTH-ALIASES NTH-UPDATE-NTH NTHCDR NULL NUMERATOR O-FINP O-FIRST-COEFF O-FIRST-EXPT O-INFP O-P O-RST O< O<= O> O>= OBSERVATION OBSERVATION-CW ODDP ODDS OK-IF OOPS OPEN-CHANNEL-LISTP OPEN-CHANNEL1 OPEN-CHANNEL1-FORWARD-TO-TRUE-LISTP-AND-CONSP OPEN-CHANNELS-P OPEN-CHANNELS-P-FORWARD OPEN-INPUT-CHANNEL OPEN-INPUT-CHANNEL-ANY-P OPEN-INPUT-CHANNEL-ANY-P1 OPEN-INPUT-CHANNEL-P OPEN-INPUT-CHANNEL-P1 OPEN-INPUT-CHANNELS OPEN-OUTPUT-CHANNEL OPEN-OUTPUT-CHANNEL! OPEN-OUTPUT-CHANNEL-ANY-P OPEN-OUTPUT-CHANNEL-ANY-P1 OPEN-OUTPUT-CHANNEL-P OPEN-OUTPUT-CHANNEL-P1 OPEN-OUTPUT-CHANNELS OPEN-TRACE-FILE OR OR-MACRO ORACLE-FUNCALL ORACLE-APPLY ORACLE-APPLY-RAW ORDERED-SYMBOL-ALISTP ORDERED-SYMBOL-ALISTP-ADD-PAIR ORDERED-SYMBOL-ALISTP-ADD-PAIR-FORWARD ORDERED-SYMBOL-ALISTP-FORWARD-TO-SYMBOL-ALISTP ORDERED-SYMBOL-ALISTP-GETPROPS ORDERED-SYMBOL-ALISTP-DELETE-ASSOC-EQ OTHERWISE OUR-DIGIT-CHAR-P OVERRIDE-HINTS P! PKG-IMPORTS PAIRLIS$ PAIRLIS2 PAND PARGS PBT PC PCB PCB! PCS PE PE! PEEK-CHAR$ PF PKG-WITNESS PL PL2 PLET PLIST-WORLDP PLIST-WORLDP-FORWARD-TO-ASSOC-EQ-EQUAL-ALISTP PLUSP POP-TIMER POR POSITION POSITION-AC POSITION-EQ POSITION-EQ-AC POSITION-EQUAL POSITION-EQUAL-AC POSITIVE POSP POWER-EVAL PPROGN PR PR! PREPROCESS PRIN1$ PRIN1-WITH-SLASHES PRIN1-WITH-SLASHES1 PRINC$ PRINT-GV PRINT-OBJECT$ PRINT-OBJECT$-SER PRINT-RATIONAL-AS-DECIMAL PRINT-TIMER PROFILE PROG2$ PROGN PROGN! PROGN$ PROGRAM PROOF-TREE PROOFS-CO PROPER-CONSP PROPS PROVE PSEUDO-TERM-LISTP PSEUDO-TERM-LISTP-FORWARD-TO-TRUE-LISTP PSEUDO-TERMP PSO PSO! PSOF PSOG PSTACK PUFF PUFF* PUSH-TIMER PUSH-UNTOUCHABLE PUT-ASSOC PUT-ASSOC-EQ PUT-ASSOC-EQL PUT-ASSOC-EQUAL PUT-GLOBAL PUTPROP QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP QUIT QUOTE QUOTEP R-SYMBOL-ALISTP R-EQLABLE-ALISTP RANDOM$ RASSOC RASSOC-EQ RASSOC-EQUAL RATIO RATIONAL RATIONAL-IMPLIES1 RATIONAL-IMPLIES2 RATIONAL-LISTP RATIONAL-LISTP-FORWARD-TO-TRUE-LISTP RATIONALP RATIONALP-* RATIONALP-+ RATIONALP-EXPT-TYPE-PRESCRIPTION RATIONALP-IMPLIES-ACL2-NUMBERP RATIONALP-UNARY-- RATIONALP-UNARY-/ READ-ACL2-ORACLE READ-ACL2-ORACLE-PRESERVES-STATE-P1 READ-BYTE$ READ-CHAR$ READ-FILE-LISTP READ-FILE-LISTP-FORWARD-TO-TRUE-LIST-LISTP READ-FILE-LISTP1 READ-FILE-LISTP1-FORWARD-TO-TRUE-LISTP-AND-CONSP READ-FILES READ-FILES-P READ-FILES-P-FORWARD-TO-READ-FILE-LISTP READ-IDATE READ-OBJECT READ-RUN-TIME READ-RUN-TIME-PRESERVES-STATE-P1 READABLE-FILE READABLE-FILE-FORWARD-TO-TRUE-LISTP-AND-CONSP READABLE-FILES READABLE-FILES-LISTP READABLE-FILES-LISTP-FORWARD-TO-TRUE-LIST-LISTP-AND-ALISTP READABLE-FILES-P READABLE-FILES-P-FORWARD-TO-READABLE-FILES-LISTP REAL/RATIONALP REALFIX REALPART REALPART-COMPLEX REALPART-IMAGPART-ELIM REBUILD REDEF REDEF! REDEF+ REDEF- REDO-FLAT REM REMOVE REGENERATE-TAU-DATABASE REMOVE-BINOP REMOVE-CUSTOM-KEYWORD-HINT REMOVE-DEFAULT-HINTS REMOVE-DEFAULT-HINTS! REMOVE-DUPLICATES REMOVE-DUPLICATES-EQ REMOVE-DUPLICATES-EQL REMOVE-DUPLICATES-EQUAL REMOVE-EQ REMOVE-EQUAL REMOVE-INVISIBLE-FNS REMOVE-MACRO-ALIAS REMOVE-MACRO-FN REMOVE-NTH-ALIAS REMOVE-OVERRIDE-HINTS REMOVE-OVERRIDE-HINTS! REMOVE-RAW-ARITY REMOVE-UNTOUCHABLE REMOVE1 REMOVE1-EQ REMOVE1-EQUAL RESET-FC-REPORTING RESET-KILL-RING RESET-LD-SPECIALS RESET-PREHISTORY RESET-PRINT-CONTROL RESIZE-LIST REST RESTORE-MEMOIZATION-SETTINGS RETRACT-WORLD RETRIEVE RETURN-LAST RETURN-LAST-TABLE REVAPPEND REVERSE REWRITE-STACK-LIMIT RFIX ROUND SATISFIES SAVE-AND-CLEAR-MEMOIZATION-SETTINGS SAVE-EXEC SEARCH SECOND SERIALIZE-READ SERIALIZE-WRITE SET-ABSSTOBJ-DEBUG SET-ACCUMULATED-PERSISTENCE SET-BACKCHAIN-LIMIT SET-BODY SET-BOGUS-DEFUN-HINTS-OK SET-BOGUS-MUTUAL-RECURSION-OK SET-CASE-SPLIT-LIMITATIONS SET-CBD SET-CHECKPOINT-SUMMARY-LIMIT SET-COMPILE-FNS SET-COMPILER-ENABLED SET-DEBUGGER-ENABLE SET-DEFAULT-BACKCHAIN-LIMIT SET-DEFAULT-HINTS SET-DEFAULT-HINTS! SET-DEFERRED-TTAG-NOTES SET-DIFFERENCE-EQ SET-DIFFERENCE-EQUAL SET-DIFFERENCE$ SET-DIFFERENCE-THEORIES SET-ENFORCE-REDUNDANCY SET-EQUALP-EQUAL SET-EVISC-TUPLE SET-FC-CRITERIA SET-FC-REPORT-ON-THE-FLY SET-FMT-HARD-RIGHT-MARGIN SET-FMT-SOFT-RIGHT-MARGIN SET-GAG-MODE SET-GUARD-CHECKING SET-IGNORE-DOC-STRING-ERROR SET-IGNORE-OK SET-INHIBIT-OUTPUT-LST SET-INHIBIT-WARNINGS SET-INHIBIT-WARNINGS! SET-INHIBITED-SUMMARY-TYPES SET-INVISIBLE-FNS-TABLE SET-IPRINT SET-IRRELEVANT-FORMALS-OK SET-LD-KEYWORD-ALIASES SET-LD-KEYWORD-ALIASES! SET-LD-REDEFINITION-ACTION SET-LD-SKIP-PROOFS SET-LD-SKIP-PROOFSP SET-LET*-ABSTRACTION SET-LET*-ABSTRACTIONP SET-MATCH-FREE-DEFAULT SET-MATCH-FREE-ERROR SET-MEASURE-FUNCTION SET-NON-LINEAR SET-NON-LINEARP SET-NU-REWRITER-MODE SET-OVERRIDE-HINTS SET-OVERRIDE-HINTS! SET-PARALLEL-EXECUTION SET-PRINT-BASE SET-PRINT-CASE SET-PRINT-CIRCLE SET-PRINT-CLAUSE-IDS SET-PRINT-ESCAPE SET-PRINT-LENGTH SET-PRINT-LEVEL SET-PRINT-LINES SET-PRINT-RADIX SET-PRINT-READABLY SET-PRINT-RIGHT-MARGIN SET-PROVER-STEP-LIMIT SET-RAW-MODE SET-RAW-MODE-ON! SET-RAW-PROOF-FORMAT SET-REWRITE-STACK-LIMIT SET-RULER-EXTENDERS SET-RW-CACHE-STATE SET-RW-CACHE-STATE! SET-SAVED-OUTPUT SET-SERIALIZE-CHARACTER SET-SPLITTER-OUTPUT SET-STATE-OK SET-TAU-AUTO-MODE SET-TIMER SET-TOTAL-PARALLELISM-WORK-LIMIT SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR SET-TRACE-EVISC-TUPLE SET-VERIFY-GUARDS-EAGERNESS SET-W SET-WELL-FOUNDED-RELATION SET-WATERFALL-PARALLELISM SET-WATERFALL-PARALLELISM-HACKS-ENABLED SET-WATERFALL-PARALLELISM-HACKS-ENABLED! SET-WATERFALL-PRINTING SET-WORMHOLE-DATA SET-WORMHOLE-ENTRY-CODE SET-WRITE-ACL2X SETENV$ SEVENTH SGETPROP SHOW-ACCUMULATED-PERSISTENCE SHOW-BDD SHOW-BODIES SHOW-CUSTOM-KEYWORD-HINT-EXPANSION SHOW-FC-CRITERIA SHRINK-32-BIT-INTEGER-STACK SHRINK-T-STACK SIGNED-BYTE SIGNED-BYTE-P SIGNUM SIMPLIFY SIXTH SKIP-PROOFS SOME-SLASHABLE SPEC-MV-LET SPLITTER-OUTPUT STABLE-UNDER-SIMPLIFICATIONP STANDARD-CHAR STANDARD-CHAR-LISTP STANDARD-CHAR-LISTP-APPEND STANDARD-CHAR-LISTP-FORWARD-TO-CHARACTER-LISTP STANDARD-CHAR-P STANDARD-CHAR-P-NTH STANDARD-CO STANDARD-OI STANDARD-STRING-ALISTP STANDARD-STRING-ALISTP-FORWARD-TO-ALISTP START-PROOF-TREE STATE STATE-GLOBAL-LET* STATE-GLOBAL-LET*-CLEANUP STATE-GLOBAL-LET*-GET-GLOBALS STATE-GLOBAL-LET*-PUT-GLOBALS STATE-P STATE-P-IMPLIES-AND-FORWARD-TO-STATE-P1 STATE-P1 STATE-P1-FORWARD STATE-P1-UPDATE-MAIN-TIMER STATE-P1-UPDATE-NTH-2-WORLD STEP-LIMIT STOBJ-LET STOP-PROOF-TREE STRING STRING-APPEND STRING-APPEND-LST STRING-DOWNCASE STRING-DOWNCASE1 STRING-EQUAL STRING-EQUAL1 STRING-IS-NOT-CIRCULAR STRING-LISTP STRING-UPCASE STRING-UPCASE1 STRING< STRING<-IRREFLEXIVE STRING<-L STRING<-L-ASYMMETRIC STRING<-L-IRREFLEXIVE STRING<-L-TRANSITIVE STRING<-L-TRICHOTOMY STRING<= STRING> STRING>= STRINGP STRINGP-SYMBOL-PACKAGE-NAME STRIP-CARS STRIP-CDRS SUBLIS SUBSEQ SUBSEQ-LIST SUBSETP SUBSETP-EQ SUBSETP-EQUAL SUBST SUBSTITUTE SUBSTITUTE-AC SUMMARY SYMBOL SYMBOL-< SYMBOL-<-ASYMMETRIC SYMBOL-<-IRREFLEXIVE SYMBOL-<-TRANSITIVE SYMBOL-<-TRICHOTOMY SYMBOL-ALISTP SYMBOL-ALISTP-FORWARD-TO-EQLABLE-ALISTP SYMBOL-DOUBLET-LISTP SYMBOL-EQUALITY SYMBOL-LISTP SYMBOL-LISTP-FORWARD-TO-TRUE-LISTP SYMBOL-NAME SYMBOL-NAME-INTERN-IN-PACKAGE-OF-SYMBOL SYMBOL-PACKAGE-NAME SYMBOLP SYMBOLP-INTERN-IN-PACKAGE-OF-SYMBOL SYNP SYNTAXP SYS-CALL SYS-CALL+ SYS-CALL-STATUS T T-STACK T-STACK-LENGTH T-STACK-LENGTH1 TABLE TABLE-ALIST TAKE TAU-DATA TAU-DATABASE TAU-INTERVAL-DOM TAU-INTERVAL-LO TAU-INTERVAL-LO-REL TAU-INTERVAL-HI TAU-INTERVAL-HI-REL TAU-INTERVALP TAU-STATUS TAU-SYSTEM TENTH TERM-ORDER THE THE-CHECK THE-FIXNUM THE-FIXNUM! THEORY THEORY-INVARIANT THIRD THM TIME$ TIME-TRACKER TIME-TRACKER-TAU TIMER-ALISTP TIMER-ALISTP-FORWARD-TO-TRUE-LIST-LISTP-AND-SYMBOL-ALISTP TOGGLE-PC-MACRO TOP-LEVEL TRACE! TRACE$ TRANS TRANS! TRANS1 TRICHOTOMY TRUE-LIST-LISTP TRUE-LIST-LISTP-FORWARD-TO-TRUE-LISTP TRUE-LIST-LISTP-FORWARD-TO-TRUE-LISTP-ASSOC-EQUAL TRUE-LISTP TRUE-LISTP-CADR-ASSOC-EQ-FOR-OPEN-CHANNELS-P TRUE-LISTP-UPDATE-NTH TRUNCATE TTAGS-SEEN TYPE TYPED-IO-LISTP TYPED-IO-LISTP-FORWARD-TO-TRUE-LISTP TYPESPEC-CHECK U UBT UBT! UBT-PREHISTORY UBU UBU! UNARY-- UNARY-/ UNARY-FUNCTION-SYMBOL-LISTP UNICITY-OF-0 UNICITY-OF-1 UNION$ UNION-EQ UNION-EQUAL UNION-THEORIES UNIVERSAL-THEORY UNMONITOR UNSAVE UNSIGNED-BYTE UNSIGNED-BYTE-P UNTRACE$ UNTRANS-TABLE UNTRANSLATE UPDATE-32-BIT-INTEGER-STACK UPDATE-ACL2-ORACLE UPDATE-ACL2-ORACLE-PRESERVES-STATE-P1 UPDATE-BIG-CLOCK-ENTRY UPDATE-FILE-CLOCK UPDATE-GLOBAL-TABLE UPDATE-IDATES UPDATE-LIST-ALL-PACKAGE-NAMES-LST UPDATE-NTH UPDATE-OPEN-INPUT-CHANNELS UPDATE-OPEN-OUTPUT-CHANNELS UPDATE-READ-FILES UPDATE-T-STACK UPDATE-USER-STOBJ-ALIST UPDATE-USER-STOBJ-ALIST1 UPDATE-WRITTEN-FILES UPPER-CASE-P UPPER-CASE-P-CHAR-UPCASE UPPER-CASE-P-FORWARD-TO-ALPHA-CHAR-P USER-STOBJ-ALIST USER-STOBJ-ALIST1 VALUE-TRIPLE VERBOSE-PSTACK VERIFY VERIFY-GUARDS VERIFY-GUARDS+ VERIFY-GUARDS-FORMULA VERIFY-TERMINATION W WALKABOUT WARNING! WATERFALL-PARALLELISM WATERFALL-PRINTING WET WITH-FAST-ALIST WITH-GUARD-CHECKING WITH-LIVE-STATE WITH-LOCAL-STATE WITH-LOCAL-STOBJ WITH-OUTPUT WITH-OUTPUT-LOCK WITH-PROVER-STEP-LIMIT WITH-PROVER-STEP-LIMIT! WITH-PROVER-TIME-LIMIT WITH-SERIALIZE-CHARACTER WITH-STOLEN-ALIST WITHOUT-EVISC WOF WORLD WORMHOLE WORMHOLE-DATA WORMHOLE-ENTRY-CODE WORMHOLE-EVAL WORMHOLE-P WORMHOLE-STATUSP WORMHOLE1 WRITABLE-FILE-LISTP WRITABLE-FILE-LISTP-FORWARD-TO-TRUE-LIST-LISTP WRITABLE-FILE-LISTP1 WRITABLE-FILE-LISTP1-FORWARD-TO-TRUE-LISTP-AND-CONSP WRITE-BYTE$ WRITEABLE-FILES WRITEABLE-FILES-P WRITEABLE-FILES-P-FORWARD-TO-WRITABLE-FILE-LISTP WRITTEN-FILE WRITTEN-FILE-FORWARD-TO-TRUE-LISTP-AND-CONSP WRITTEN-FILE-LISTP WRITTEN-FILE-LISTP-FORWARD-TO-TRUE-LIST-LISTP-AND-ALISTP WRITTEN-FILES WRITTEN-FILES-P WRITTEN-FILES-P-FORWARD-TO-WRITTEN-FILE-LISTP XARGS XOR XXXJOIN ZERO ZEROP ZIP ZP ZPF ; For ACL2(r): DEFTHM-STD DEFUN-STD DEFUNS-STD I-CLOSE I-LARGE I-LIMITED I-SMALL REAL-LISTP STANDARD-PART STANDARDP))) "This is the list of ACL2 symbols that the ordinary user is extremely likely to want to include in the import list of any package created because these symbols are the basic hooks for using ACL2. However, it is never necessary to do such importing: one can always use the acl2:: prefix." ) (defpkg "ACL2-USER" (union-eq *acl2-exports* *common-lisp-symbols-from-main-lisp-package*) ":Doc-Section ACL2::Programming a package the ACL2 user may prefer~/ This package imports the standard Common Lisp symbols that ACL2 supports and also a few symbols from package ~c[\"ACL2\"] that are commonly used when interacting with ACL2. You may prefer to select this as your current package so as to avoid colliding with ACL2 system names.~/ This package imports the symbols listed in ~c[*common-lisp-symbols-from-main-lisp-package*], which contains hundreds of CLTL function and macro names including those supported by ACL2 such as ~ilc[cons], ~ilc[car], and ~ilc[cdr]. It also imports the symbols in ~c[*acl2-exports*], which contains a few symbols that are frequently used while interacting with the ACL2 system, such as ~ilc[implies], ~ilc[defthm], and ~ilc[rewrite]. It imports nothing else. Thus, names such as ~ilc[alistp], ~ilc[member-equal], and ~ilc[type-set], which are defined in the ~c[\"ACL2\"] package are not present here. If you find yourself frequently colliding with names that are defined in ~c[\"ACL2\"] you might consider selecting ~c[\"ACL2-USER\"] as your current package (~pl[in-package]). If you select ~c[\"ACL2-USER\"] as the current package, you may then simply type ~ilc[member-equal] to refer to ~c[acl2-user::member-equal], which you may define as you see fit. Of course, should you desire to refer to the ~c[\"ACL2\"] version of ~ilc[member-equal], you will have to use the ~c[\"ACL2::\"] prefix, e.g., ~c[acl2::member-equal]. If, while using ~c[\"ACL2-USER\"] as the current package, you find that there are symbols from ~c[\"ACL2\"] that you wish we had imported into it (because they are frequently used in interaction), please bring those symbols to our attention. For example, should ~ilc[union-theories] and ~ilc[universal-theory] be imported? Except for stabilizing on the ``frequently used'' names from ~c[\"ACL2\"], we intend never to define a symbol whose ~ilc[symbol-package-name] is ~c[\"ACL2-USER\"].") acl2-sources/defthm.lisp0000664002132200015000000251301712222115527014723 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; This file contains the functions that check the acceptable forms for ; the various classes of rules, the functions that generate the rules ; from the forms, and finally the functions that actually do the adding. ; It also contains various history management and command facilities whose ; implementation is intertwined with the storage of rules, e.g., :pr and ; some monitoring stuff. ; The structure of the file is that we first define the checkers and ; generators for each class of rule. Each such section has a header ; like that shown below. When we finish all the individual classes ; we enter the final sections, headed ; Section: Handling a List of Classes ; Section: More History Management and Command Stuff ; Section: The DEFAXIOM Event ; Section: The DEFTHM Event ; Section: Some Convenient Abbreviations for Defthm ;--------------------------------------------------------------------------- ; Section: :REWRITE Rules ; In this section we develop the function chk-acceptable- ; rewrite-rule, which checks that all the :REWRITE rules generated ; from a term are legal. We then develop add-rewrite-rule which does ; the actual generation and addition of the rules to the world. (mutual-recursion (defun remove-lambdas (term) (if (or (variablep term) (fquotep term)) term (let ((args (remove-lambdas-lst (fargs term))) (fn (ffn-symb term))) (if (flambdap fn) (subcor-var (lambda-formals fn) args (remove-lambdas (lambda-body fn))) (cons-term fn args))))) (defun remove-lambdas-lst (termlist) (if termlist (cons (remove-lambdas (car termlist)) (remove-lambdas-lst (cdr termlist))) nil)) ) ; We use the following functions to determine the sense of the conclusion ; as a :REWRITE rule. (defun interpret-term-as-rewrite-rule2 (name hyps lhs rhs wrld) (cond ((equal lhs rhs) (msg "A :REWRITE rule generated from ~x0 is illegal because it rewrites the ~ term ~x1 to itself! This can happen even when you submit a rule whose ~ left and right sides appear to be different, in the case that those two ~ sides represent the same term (in particular, after macroexpansion). ~ See :DOC rewrite. You may wish to consider submitting a DEFTHM event ~ ending with :RULE-CLASSES NIL." name lhs)) ((or (variablep lhs) (fquotep lhs) (flambda-applicationp lhs) (eq (ffn-symb lhs) 'if)) (msg "A :REWRITE rule generated from ~x0 is illegal because it rewrites the ~ ~@1 ~x2. See :DOC rewrite." name (cond ((variablep lhs) "variable symbol") ((fquotep lhs) "quoted constant") ((flambda-applicationp lhs) "LET-expression") (t "IF-expression")) lhs)) (t (let ((bad-synp-hyp-msg (bad-synp-hyp-msg hyps (all-vars lhs) nil wrld))) (cond (bad-synp-hyp-msg (msg "A rewrite rule generated from ~x0 is illegal because ~@1" name bad-synp-hyp-msg)) (t nil)))))) (defun interpret-term-as-rewrite-rule1 (term equiv-okp ens wrld) ; Here we do the work described in interpret-term-as-rewrite-rule. If ; equiv-okp is nil, then no special treatment is given to equivalence relations ; other than equal, iff, and members of *equality-aliases*. (cond ((variablep term) (mv 'iff term *t* nil)) ((fquotep term) (mv 'iff term *t* nil)) ((member-eq (ffn-symb term) *equality-aliases*) (mv 'equal (fargn term 1) (fargn term 2) nil)) ((if equiv-okp (equivalence-relationp (ffn-symb term) wrld) (member-eq (ffn-symb term) '(equal iff))) (mv-let (equiv ttree) (cond ((eq (ffn-symb term) 'iff) (mv-let (ts ttree) (type-set (fargn term 1) nil nil nil ens wrld nil nil nil) (cond ((ts-subsetp ts *ts-boolean*) (mv-let (ts ttree) (type-set (fargn term 2) nil nil nil ens wrld ttree nil nil) (cond ((ts-subsetp ts *ts-boolean*) (mv 'equal ttree)) (t (mv 'iff nil))))) (t (mv 'iff nil))))) (t (mv (ffn-symb term) nil))) (mv equiv (fargn term 1) (fargn term 2) ttree))) ((eq (ffn-symb term) 'not) (mv 'equal (fargn term 1) *nil* nil)) (t (mv-let (ts ttree) (type-set term nil nil nil ens wrld nil nil nil) (cond ((ts-subsetp ts *ts-boolean*) (mv 'equal term *t* ttree)) (t (mv 'iff term *t* nil))))))) (defun interpret-term-as-rewrite-rule (name hyps term ens wrld) ; This function returns five values. The first can be a msg for printing an ; error message. Otherwise the first is nil, in which case the second is an ; equivalence relation, eqv; the next two are terms, lhs and rhs, such that ; (eqv lhs rhs) is propositionally equivalent to term; and the last is an ; 'assumption-free ttree justifying the claim. (let ((term (remove-lambdas term))) (mv-let (eqv lhs rhs ttree) (interpret-term-as-rewrite-rule1 term t ens wrld) (let ((msg (interpret-term-as-rewrite-rule2 name hyps lhs rhs wrld))) (cond (msg ; We try again, this time with equiv-okp = nil to avoid errors for a form such ; as the following. Its evaluation caused a hard Lisp error in Version_4.3 ; during the second pass of the encapsulate at the final defthm, and is based ; closely on an example sent to us by Jared Davis. ; (encapsulate ; () ; (defun my-equivp (x y) ; (equal (nfix x) (nfix y))) ; (local ; (defthm my-equivp-reflexive ; (my-equivp x x))) ; (defequiv my-equivp) ; (defthm my-equivp-reflexive ; (my-equivp x x))) (mv-let (eqv2 lhs2 rhs2 ttree2) (interpret-term-as-rewrite-rule1 term nil ens wrld) (cond ((interpret-term-as-rewrite-rule2 name hyps lhs2 rhs2 wrld) (mv msg eqv lhs rhs ttree)) (t (mv nil eqv2 lhs2 rhs2 ttree2))))) (t (mv nil eqv lhs rhs ttree))))))) ; We inspect the lhs and some hypotheses with the following function ; to determine if non-recursive defuns will present a problem to the ; user. (mutual-recursion (defun non-recursive-fnnames (term ens wrld) (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (add-to-set-equal (ffn-symb term) (non-recursive-fnnames-lst (fargs term) ens wrld))) ((let ((def-body (def-body (ffn-symb term) wrld))) (and def-body (enabled-numep (access def-body def-body :nume) ens) (not (access def-body def-body :recursivep)))) (add-to-set-eq (ffn-symb term) (non-recursive-fnnames-lst (fargs term) ens wrld))) (t (non-recursive-fnnames-lst (fargs term) ens wrld)))) (defun non-recursive-fnnames-lst (lst ens wrld) (cond ((null lst) nil) (t (union-equal (non-recursive-fnnames (car lst) ens wrld) (non-recursive-fnnames-lst (cdr lst) ens wrld))))) ) ; The list just constructed is odd because it may contain some lambda ; expressions posing as function symbols. We use the following function ; to transform those into let's just for printing purposes... (defun hide-lambdas1 (formals) ; CLTL uses # as the "too deep to show" symbol. But if we use it, we ; print vertical bars around it. Until we modify the printer to support ; some kind of hiding, we'll use Interlisp's ampersand. (cond ((null formals) nil) (t (cons (list (car formals) '&) (hide-lambdas1 (cdr formals)))))) (defun hide-lambdas (lst) (cond ((null lst) nil) (t (cons (if (flambdap (car lst)) (list 'let (hide-lambdas1 (lambda-formals (car lst))) (lambda-body (car lst))) (car lst)) (hide-lambdas (cdr lst)))))) ; Now we develop the stuff to determine if we have a permutative :REWRITE rule. (defun variantp (term1 term2) ; This function returns two values: A flag indicating whether the two ; terms are variants and the substitution which when applied to term1 ; yields term2. (mv-let (ans unify-subst) (one-way-unify term1 term2) (cond (ans (let ((range (strip-cdrs unify-subst))) (mv (and (symbol-listp range) (no-duplicatesp-equal range)) unify-subst))) (t (mv nil nil))))) (mutual-recursion (defun surrounding-fns1 (vars term fn acc) ; See surrounding-fns for the definition of the notions used below. ; Vars is a list of variables. Term is a term that occurs as an argument in ; some (here unknown) application of the function fn. Acc is either a list of ; function symbols or the special token 'has-lambda. Observe that if term is a ; var in vars, then fn surrounds some var in vars in whatever larger term ; contained the application of fn. ; If term is a var in vars, we collect fn into acc. If term is not a var, we ; collect into acc all the function symbols surrounding any element of vars. ; However, if we ever encounter a lambda application surrounding a var in vars ; (including fn), we set acc to the special token 'has-lambda, and collections ; cease thereafter. (cond ((variablep term) (cond ((member-eq term vars) (if (or (eq acc 'has-lambda) (not (symbolp fn))) 'has-lambda (add-to-set-eq fn acc))) (t acc))) ((fquotep term) acc) (t (surrounding-fns-lst vars (fargs term) (ffn-symb term) acc)))) (defun surrounding-fns-lst (vars term-list fn acc) (cond ((null term-list) acc) (t (surrounding-fns-lst vars (cdr term-list) fn (surrounding-fns1 vars (car term-list) fn acc))))) ) (defun surrounding-fns (vars term) ; This function returns the list of all functions fn surrounding, in term, any ; var in vars, except that if that list includes a lambda expression we return ; nil. ; We make this precise as follows. Let us say a function symbol or lambda ; expression, fn, ``surrounds'' a variable v in term if there is a subterm of ; term that is an application of fn and v is among the actuals of that ; application. Thus, in the term (fn (g x) (h (d x)) y), g and d both surround ; x and fn surrounds y. Note that h surrounds no variable. ; Consider the set, s, of all functions fn such that fn surrounds a variable ; var in term, where var is a member of the list of variables var. If s ; contains a lambda expression, we return nil; otherwise we return s. (cond ((or (variablep term) (fquotep term)) nil) (t (let ((ans (surrounding-fns-lst vars (fargs term) (ffn-symb term) nil))) (if (eq ans 'has-lambda) nil ans))))) (defun loop-stopper1 (alist vars lhs) (cond ((null alist) nil) ((member-eq (car (car alist)) (cdr (member-eq (cdr (car alist)) vars))) (cons (list* (caar alist) (cdar alist) (surrounding-fns (list (caar alist) (cdar alist)) lhs)) (loop-stopper1 (cdr alist) vars lhs))) (t (loop-stopper1 (cdr alist) vars lhs)))) (defun loop-stopper (lhs rhs) ; If lhs and rhs are variants, we return the "expansion" (see next ; paragraph) of the subset of the unifying substitution containing ; those pairs (x . y) in which a variable symbol (y) is being moved ; forward (to the position of x) in the print representation of the ; term. For example, suppose lhs is (foo x y z) and rhs is (foo y z ; x). Then both y and z are moved forward, so the loop-stopper is the ; "expansion" of '((y . z) (x . y)). This function exploits the fact ; that all-vars returns the set of variables listed in reverse ; print-order. ; In the paragraph above, the "expansion" of a substitution ((x1 . ; y1) ... (xn . yn)) is the list ((x1 y1 . fns-1) ... (xn yn . ; fns-n)), where fns-i is the list of function symbols of subterms of ; lhs that contain xi or yi (or both) as a top-level argument. ; Exception: If any such "function symbol" is a LAMBDA, then fns-i is ; nil. ; Note: John Cowles first suggested the idea that led to the idea of ; invisible function symbols as implemented here. Cowles observation ; was that it would be very useful if x and (- x) were moved into ; adjacency by permutative rules. His idea was to redefine term-order ; so that those two terms were of virtually equal weight. Our notion ; of invisible function symbols and the handling of loop-stopper is ; meant to address Cowles original concern without complicating ; term-order, which is used in places besides permutative rewriting. ":Doc-Section Miscellaneous limit application of permutative rewrite rules~/ ~l[rule-classes] for a discussion of the syntax of the ~c[:loop-stopper] field of ~c[:]~ilc[rewrite] rule-classes. Here we describe how that field is used, and also how that field is created when the user does not explicitly supply it. For example, the built-in ~c[:]~ilc[rewrite] rule ~c[commutativity-of-+], ~bv[] (implies (and (acl2-numberp x) (acl2-numberp y)) (equal (+ x y) (+ y x))), ~ev[] creates a rewrite rule with a loop-stopper of ~c[((x y binary-+))]. This means, very roughly, that the term corresponding to ~c[y] must be ``smaller'' than the term corresponding to ~c[x] in order for this rule to apply. However, the presence of ~ilc[binary-+] in the list means that certain functions that are ``invisible'' with respect to ~ilc[binary-+] (by default, ~ilc[unary--] is the only such function) are more or less ignored when making this ``smaller'' test. We are much more precise below.~/ Our explanation of loop-stopping is in four parts. First we discuss ACL2's notion of ``term order.'' Next, we bring in the notion of ``invisibility'', and use it together with term order to define orderings on terms that are used in the loop-stopping algorithm. Third, we describe that algorithm. These topics all assume that we have in hand the ~c[:loop-stopper] field of a given rewrite rule; the fourth and final topic describes how that field is calculated when it is not supplied by the user. ACL2 must sometimes decide which of two terms is syntactically simpler. It uses a total ordering on terms, called the ``term order.'' Under this ordering constants such as ~c['(a b c)] are simpler than terms containing variables such as ~c[x] and ~c[(+ 1 x)]. Terms containing variables are ordered according to how many occurrences of variables there are. Thus ~c[x] and ~c[(+ 1 x)] are both simpler than ~c[(cons x x)] and ~c[(+ x y)]. If variable counts do not decide the order, then the number of function applications are tried. Thus ~c[(cons x x)] is simpler than ~c[(+ x (+ 1 y))] because the latter has one more function application. Finally, if the number of function applications do not decide the order, a lexicographic ordering on Lisp objects is used. ~l[term-order] for details. When the loop-stopping algorithm is controlling the use of permutative ~c[:]~ilc[rewrite] rules it allows ~c[term1] to be moved leftward over ~c[term2] only if ~c[term1] is smaller, in a suitable sense. Note: The sense used in loop-stopping is ~st[not] the above explained term order but a more complicated ordering described below. The use of a total ordering stops rules like commutativity from looping indefinitely because it allows ~c[(+ b a)] to be permuted to ~c[(+ a b)] but not vice versa, assuming ~c[a] is smaller than ~c[b] in the ordering. Given a set of permutative rules that allows arbitrary permutations of the tips of a tree of function calls, this will normalize the tree so that the smallest argument is leftmost and the arguments ascend in the order toward the right. Thus, for example, if the same argument appears twice in the tree, as ~c[x] does in the ~ilc[binary-+] tree denoted by the term ~c[(+ a x b x)], then when the allowed permutations are done, all occurrences of the duplicated argument in the tree will be adjacent, e.g., the tree above will be normalized to ~c[(+ a b x x)]. Suppose the loop-stopping algorithm used term order, as noted above, and consider the ~ilc[binary-+] tree denoted by ~c[(+ x y (- x))]. The arguments here are in ascending term order already. Thus, no permutative rules are applied. But because we are inside a ~c[+-expression] it is very convenient if ~c[x] and ~c[(- x)] could be given virtually the same position in the ordering so that ~c[y] is not allowed to separate them. This would allow such rules as ~c[(+ i (- i) j) = j] to be applied. In support of this, the ordering used in the control of permutative rules allows certain unary functions, e.g., the unary minus function above, to be ``invisible'' with respect to certain ``surrounding'' functions, e.g., ~ilc[+] function above. Briefly, a unary function symbol ~c[fn1] is invisible with respect to a function symbol ~c[fn2] if ~c[fn2] belongs to the value of ~c[fn1] in ~ilc[invisible-fns-table]; also ~pl[set-invisible-fns-table], which explains its format and how it can be set by the user. Roughly speaking, ``invisible'' function symbols are ignored for the purposes of the term-order test. Consider the example above, ~c[(+ x y (- x))]. The translated version of this term is ~c[(binary-+ x (binary-+ y (unary-- x)))]. The initial ~ilc[invisible-fns-table] makes ~ilc[unary--] invisible with repect to ~ilc[binary-+]. The commutativity rule for ~ilc[binary-+] will attempt to swap ~c[y] and ~c[(unary-- x)] and the loop-stopping algorithm is called to approve or disapprove. If term order is used, the swap will be disapproved. But term order is not used. While the loop-stopping algorithm is permuting arguments inside a ~ilc[binary-+] expression, ~ilc[unary--] is invisible. Thus, insted of comparing ~c[y] with ~c[(unary-- x)], the loop-stopping algorithm compares ~c[y] with ~c[x], approving the swap because ~c[x] comes before ~c[y]. Here is a more precise specification of the total order used for loop-stopping with respect to a list, ~c[fns], of functions that are to be considered invisible. Let ~c[x] and ~c[y] be distinct terms; we specify when ``~c[x] is smaller than ~c[y] with respect to ~c[fns].'' If ~c[x] is the application of a unary function symbol that belongs to ~c[fns], replace ~c[x] by its argument. Repeat this process until the result is not the application of such a function; let us call the result ~c[x-guts]. Similarly obtain ~c[y-guts] from ~c[y]. Now if ~c[x-guts] is the same term as ~c[y-guts], then ~c[x] is smaller than ~c[y] in this order iff ~c[x] is smaller than ~c[y] in the standard term order. On the other hand, if ~c[x-guts] is different than ~c[y-guts], then ~c[x] is smaller than ~c[y] in this order iff ~c[x-guts] is smaller than ~c[y-guts] in the standard term order. Now we may describe the loop-stopping algorithm. Consider a rewrite rule with conclusion ~c[(equiv lhs rhs)] that applies to a term ~c[x] in a given context; ~pl[rewrite]. Suppose that this rewrite rule has a loop-stopper field (technically, the ~c[:heuristic-info] field) of ~c[((x1 y1 . fns-1) ... (xn yn . fns-n))]. (Note that this field can be observed by using the command ~c[:]~ilc[pr] with the name of the rule; ~pl[pr].) We describe when rewriting is permitted. The simplest case is when the loop-stopper list is ~c[nil] (i.e., ~c[n] is ~c[0]); in that case, rewriting is permitted. Otherwise, for each ~c[i] from 1 to ~c[n] let ~c[xi'] be the actual term corresponding to the variable ~c[xi] when ~c[lhs] is matched against the term to be rewritten, and similarly correspond ~c[yi'] with ~c[y]. If ~c[xi'] and ~c[yi'] are the same term for all ~c[i], then rewriting is not permitted. Otherwise, let ~c[k] be the least ~c[i] such that ~c[xi'] and ~c[yi'] are distinct. Let ~c[fns] be the list of all functions that are invisible with respect to every function in ~c[fns-k], if ~c[fns-k] is non-empty; otherwise, let ~c[fns] be ~c[nil]. Then rewriting is permitted if and only if ~c[yi'] is smaller than ~c[xi'] with respect to ~c[fns], in the sense defined in the preceding paragraph. It remains only to describe how the loop-stopper field is calculated for a rewrite rule when this field is not supplied by the user. (On the other hand, to see how the user may specify the ~c[:loop-stopper], ~pl[rule-classes].) Suppose the conclusion of the rule is of the form ~c[(equiv lhs rhs)]. First of all, if ~c[rhs] is not an instance of the left hand side by a substitution whose range is a list of distinct variables, then the loop-stopper field is ~c[nil]. Otherwise, consider all pairs ~c[(u . v)] from this substitution with the property that the first occurrence of ~c[v] appears in front of the first occurrence of ~c[u] in the print representation of ~c[rhs]. For each such ~c[u] and ~c[v], form a list ~c[fns] of all functions ~c[fn] in ~c[lhs] with the property that ~c[u] or ~c[v] (or both) appears as a top-level argument of a subterm of ~c[lhs] with function symbol ~c[fn]. Then the loop-stopper for this rewrite rule is a list of all lists ~c[(u v . fns)].~/" (mv-let (ans unify-subst) (variantp lhs rhs) (cond (ans (loop-stopper1 unify-subst (all-vars lhs) lhs)) (t nil)))) (defun remove-irrelevant-loop-stopper-pairs (pairs vars) ; Keep this in sync with irrelevant-loop-stopper-pairs. (if pairs (if (and (member-eq (caar pairs) vars) (member-eq (cadar pairs) vars)) ; Note that the use of loop-stopper1 by loop-stopper guarantees that ; machine-constructed loop-stoppers only contain pairs (u v . fns) for ; which u and v both occur in the lhs of the rewrite rule. Hence, it ; is reasonable to include the test above. (cons (car pairs) (remove-irrelevant-loop-stopper-pairs (cdr pairs) vars)) (remove-irrelevant-loop-stopper-pairs (cdr pairs) vars)) nil)) (defun put-match-free-value (match-free-value rune wrld) (cond ((eq match-free-value :all) (global-set 'free-var-runes-all (cons rune (global-val 'free-var-runes-all wrld)) wrld)) ((eq match-free-value :once) (global-set 'free-var-runes-once (cons rune (global-val 'free-var-runes-once wrld)) wrld)) ((null match-free-value) wrld) (t (er hard 'put-match-free-value "Internal ACL2 error (called put-match-free-value with ~ match-free-value equal to ~x0). Please contact the ACL2 implementors." match-free-value)))) (defun free-vars-in-hyps (hyps bound-vars wrld) ; Let hyps be a list of terms -- the hypotheses to some :REWRITE rule. ; Let bound-vars be a list of variables. We find all the variables that ; will be free-vars in hyps when each variable in bound-vars is bound. ; This would be just (set-difference-eq (all-vars1-lst hyps) bound-vars) ; were it not for the fact that relieve-hyps interprets the hypothesis ; (equal v term), where v is free and does not occur in term, as ; a "let v be term..." instead of as a genuine free variable to be found ; by search. ; Warning: Keep this function and free-vars-in-hyps-considering-bind-free ; in sync. (cond ((null hyps) nil) (t (mv-let (forcep flg) (binding-hyp-p (car hyps) (pairlis$ bound-vars bound-vars) wrld) ; The odd pairlis$ above just manufactures a substitution with bound-vars as ; bound vars so we can use free-varsp to answer the question, "does ; the rhs of the equality contain any free variables?" The range of ; the subsitution is irrelevant. If the conjunction above is true, then ; the current hyp is of the form (equiv v term) and v will be chosen ; by rewriting term. V is not a "free variable". (cond ((and flg (not forcep)) (free-vars-in-hyps (cdr hyps) (cons (fargn (car hyps) 1) bound-vars) wrld)) (t (let ((hyp-vars (all-vars (car hyps)))) (union-eq (set-difference-eq hyp-vars bound-vars) (free-vars-in-hyps (cdr hyps) (union-eq hyp-vars bound-vars) wrld))))))))) (defun free-vars-in-hyps-simple (hyps bound-vars) ; This is a simpler variant of free-vars-in-hyps that does not give special ; treatment to terms (equal variable term). (cond ((null hyps) nil) (t (let ((hyp-vars (all-vars (car hyps)))) (union-eq (set-difference-eq hyp-vars bound-vars) (free-vars-in-hyps-simple (cdr hyps) (union-eq hyp-vars bound-vars))))))) (defun free-vars-in-fc-hyps (triggers hyps concls) ; This function determines whether a rule has free variables, given the ; triggers, hyps and conclusions of the rule. (if (endp triggers) nil (let ((vars (all-vars (car triggers)))) (or (free-vars-in-hyps-simple hyps vars) (or (free-vars-in-hyps-simple concls vars) (free-vars-in-fc-hyps (cdr triggers) hyps concls)))))) (defun free-vars-in-hyps-considering-bind-free (hyps bound-vars wrld) ; This function is similar to the above free-vars-in-hyps. It ; differs in that it takes into account the effects of bind-free. ; Note that a bind-free hypothesis expands to a call to synp in ; which the first arg denotes the vars that are potentially bound ; by the hyp. This first arg will be either a quoted list of vars ; or 't which we interpret to mean all the otherwise free vars. ; Vars that are potentially bound by a bind-free hyp are not considered ; to be free vars for the purposes of this function. ; Note that a syntaxp hypothesis also expands to a call of synp, ; but that in this case the first arg is 'nil. ; Warning: Keep this function and free-vars-in-hyps in sync. (cond ((null hyps) nil) (t (mv-let (forcep flg) (binding-hyp-p (car hyps) (pairlis$ bound-vars bound-vars) wrld) ; The odd pairlis$ above just manufactures a substitution with bound-vars as ; bound vars so we can use free-varsp to answer the question, "does ; the rhs of the equality contain any free variables?" The range of ; the subsitution is irrelevant. If the conjunction above is true, then ; the current hyp is of the form (equiv v term) and v will be chosen ; by rewriting term. V is not a "free variable". (cond ((and flg (not forcep)) (free-vars-in-hyps-considering-bind-free (cdr hyps) (cons (fargn (car hyps) 1) bound-vars) wrld)) ((and (nvariablep (car hyps)) (not (fquotep (car hyps))) (eq (ffn-symb (car hyps)) 'synp) (not (equal (fargn (car hyps) 1) *nil*))) ; not syntaxp hyp (cond ((equal (fargn (car hyps) 1) *t*) ; All free variables are potentially bound. The user will presumably not want ; to see a warning in this case. nil) ((and (quotep (fargn (car hyps) 1)) (not (collect-non-legal-variableps (cadr (fargn (car hyps) 1))))) (free-vars-in-hyps-considering-bind-free (cdr hyps) (union-eq (cadr (fargn (car hyps) 1)) bound-vars) wrld)) (t (er hard 'free-vars-in-hyps-considering-bind-free "We thought the first argument of synp in this context ~ was either 'NIL, 'T, or else a quoted true list of ~ variables, but ~x0 is not!" (fargn (car hyps) 1))))) (t (let ((hyp-vars (all-vars (car hyps)))) (union-eq (set-difference-eq hyp-vars bound-vars) (free-vars-in-hyps-considering-bind-free (cdr hyps) (union-eq hyp-vars bound-vars) wrld))))))))) (defun all-vars-in-hyps (hyps) ; We return a list of all the vars mentioned in hyps or, if there is ; a synp hyp whose var-list is 't, we return t. (cond ((null hyps) nil) ((variablep (car hyps)) (add-to-set-eq (car hyps) (all-vars-in-hyps (cdr hyps)))) ((fquotep (car hyps)) (all-vars-in-hyps (cdr hyps))) ((eq (ffn-symb (car hyps)) 'synp) (cond ((equal (fargn (car hyps) 1) *nil*) (all-vars-in-hyps (cdr hyps))) ((equal (fargn (car hyps) 1) *t*) t) ((and (quotep (fargn (car hyps) 1)) (not (collect-non-legal-variableps (cadr (fargn (car hyps) 1))))) (union-eq (cadr (fargn (car hyps) 1)) (all-vars-in-hyps (cdr hyps)))) (t (er hard 'free-vars-in-hyps-considering-bind-free "We thought the first argument of synp in this context ~ was either 'NIL, 'T, or else a quoted true list of ~ variables, but ~x0 is not!" (fargn (car hyps) 1))))) (t (union-eq (all-vars (car hyps)) (all-vars-in-hyps (cdr hyps)))))) (defun match-free-value (match-free hyps pat wrld) (or match-free (and (free-vars-in-hyps hyps (all-vars pat) wrld) (or (match-free-default wrld) ; We presumably already caused an error if at this point we would find a value ; of t for state global match-free-error. :all)))) (defun match-free-fc-value (match-free hyps concls triggers wrld) ; This function, based on match-free-value, uses free-vars-in-fc-hyps to ; determine whether free-vars are present in a forward-chaining rule (if so it ; returns nil). If free-vars are not present then it uses the match-free value ; of the rule (given by the match-free arg) or the match-free default value of ; the world to determine the correct match-free value for this particular rule. (or match-free (and (free-vars-in-fc-hyps triggers hyps concls) (or (match-free-default wrld) :all)))) (defun rule-backchain-limit-lst (backchain-limit-lst hyps wrld flg) (cond (backchain-limit-lst (cadr backchain-limit-lst)) (t (let ((limit (default-backchain-limit wrld flg))) (and limit (make-list (length hyps) :initial-element limit)))))) (defun create-rewrite-rule (rune nume hyps equiv lhs rhs loop-stopper-lst backchain-limit-lst match-free-value wrld) ; This function creates a :REWRITE rule of subclass 'backchain or ; 'abbreviation from the basic ingredients, preprocessing the hyps and ; computing the loop-stopper. Equiv is an equivalence relation name. (let ((hyps (preprocess-hyps hyps)) (loop-stopper (if loop-stopper-lst (remove-irrelevant-loop-stopper-pairs (cadr loop-stopper-lst) (all-vars lhs)) (loop-stopper lhs rhs)))) (make rewrite-rule :rune rune :nume nume :hyps hyps :equiv equiv :lhs lhs :var-info (free-varsp lhs nil) :rhs rhs :subclass (cond ((and (null hyps) (null loop-stopper) (abbreviationp nil (all-vars-bag lhs nil) rhs)) 'abbreviation) (t 'backchain)) :heuristic-info loop-stopper ; If backchain-limit-lst is given, then it is a keyword-alist whose second ; element is a list of values of length (length hyps), and we use this value. ; Otherwise we use the default. This will be either nil -- used directly -- or ; an integer which we expand to a list of (length hyps) copies. :backchain-limit-lst (rule-backchain-limit-lst backchain-limit-lst hyps wrld :rewrite) :match-free match-free-value))) ; The next subsection of our code develops various checkers to help the ; user manage his collection of rules. (defun hyps-that-instantiate-free-vars (free-vars hyps) ; We determine the hyps in hyps that will be used to instantiate ; the free variables, free-vars, of some rule. Here, variables "bound" by ; calls of bind-free are not considered free in the case of rewrite and linear ; rules, so would not appear among free-vars in those cases. (cond ((null free-vars) nil) ((intersectp-eq free-vars (all-vars (car hyps))) (cons (car hyps) (hyps-that-instantiate-free-vars (set-difference-eq free-vars (all-vars (car hyps))) (cdr hyps)))) (t (hyps-that-instantiate-free-vars free-vars (cdr hyps))))) (mutual-recursion (defun maybe-one-way-unify (pat term alist) ; We return t if "it is possible" that pat matches term. More accurately, if ; we return nil, then (one-way-unify1 pat term alist) definitely fails. Thus, ; the answer t below is always safe. The answer nil means there is no ; substitution, s extending alist such that pat/s is term. (cond ((variablep pat) (let ((pair (assoc-eq pat alist))) (or (not pair) (eq pat (cdr pair))))) ((fquotep pat) (equal pat term)) ((variablep term) nil) ((fquotep term) t) ((equal (ffn-symb pat) (ffn-symb term)) (maybe-one-way-unify-lst (fargs pat) (fargs term) alist)) (t nil))) (defun maybe-one-way-unify-lst (pat-lst term-lst alist) (cond ((endp pat-lst) t) (t (and (maybe-one-way-unify (car pat-lst) (car term-lst) alist) (maybe-one-way-unify-lst (cdr pat-lst) (cdr term-lst) alist))))) ) (defun maybe-one-way-unify-with-some (pat term-lst alist) ; If we return nil, then there is no term in term-lst such that (one-way-unify ; pat term alist). If we return t, then pat might unify with some member. (cond ((endp term-lst) nil) ((maybe-one-way-unify pat (car term-lst) alist) t) (t (maybe-one-way-unify-with-some pat (cdr term-lst) alist)))) (defun maybe-subsumes (cl1 cl2 alist) ; We return t if it is possible that the instance of cl1 via alist subsumes ; cl2. More accurately, if we return nil then cl1 does not subsume cl2. ; Recall what it means for (subsumes cl1 cl2 alist) to return t: cl1/alist' is ; a subset of cl2, where alist' is an extension of alist. Observe that the ; subset check would fail if cl1 contained a literal (P X) and there is no ; literal beginning with P in cl2. More generally, suppose there is a literal ; of cl1 (e.g., (P X)) that unifies with no literal of cl2. Then cl1 could not ; possibly subsume cl2. ; For a discussion of the origin of this function, see subsumes-rewrite-rule. ; It was made more efficient after Version_3.0, by adding an alist argument to ; eliminate the possibility of subsumption in more cases. ; Note that this function does not give special treatment for literals ; satisfying extra-info-lit-p. We intend this function for use in checking ; subsumption of rewrite rules, and extra-info-lit-p has no special role for ; the rewriter. (cond ((null cl1) t) ((maybe-one-way-unify-with-some (car cl1) cl2 alist) (maybe-subsumes (cdr cl1) cl2 alist)) (t nil))) (defun subsumes-rewrite-rule (rule1 rule2 wrld) ; We answer the question: does rule1 subsume rule2? I.e., can rule1 ; (probably) be applied whenever rule2 can be? Since we don't check ; the loop-stoppers, the "probably" is warranted. There may be other ; reasons it is warranted. But this is just a heuristic check performed ; as a service to the user. ; One might ask why we do the maybe-subsumes. We do the subsumes ; check on the hyps of two rules with matching :lhs. In a hardware ; related file we were once confronted with a rule1 having :hyps ; ((BOOLEANP A0) (BOOLEANP B0) (BOOLEANP S0) (BOOLEANP C0_IN) ; (BOOLEANP A1) (BOOLEANP B1) (BOOLEANP S1) (BOOLEANP C1_IN) ; ... ; (S_REL A0 B0 C0_IN S0) ; ...) ; and a rule2 with :hyps ; ((BOOLEANP A0) (BOOLEANP B0) (BOOLEANP S0) ; (BOOLEANP A1) (BOOLEANP B1) (BOOLEANP S1) ; ...) ; The subsumes computation ran for over 30 minutes (and was eventually ; aborted). The problem is that the extra variables in rule1, e.g., ; C0_IN, were matchable in many different ways, e.g., C0_IN <- A0, ; C0_IN <- B0, etc., all of which must be tried in a subsumption ; check. But no matter how you get rid of (BOOLEANP C0_IN) by ; choosing C0_IN, you will eventually run into the S_REL hypothesis in ; rule1 which has no counterpart in rule2. Thus we installed the ; relatively quick maybe-subsumes check. The check scans the :hyps of ; the first rule and determines whether there is some hypothesis that ; cannot possibly be matched against the hyps of the other rule. (and (refinementp (access rewrite-rule rule1 :equiv) (access rewrite-rule rule2 :equiv) wrld) (mv-let (ans unify-subst) (one-way-unify (access rewrite-rule rule1 :lhs) (access rewrite-rule rule2 :lhs)) (and ans (maybe-subsumes (access rewrite-rule rule1 :hyps) (access rewrite-rule rule2 :hyps) unify-subst) (eq (subsumes *init-subsumes-count* (access rewrite-rule rule1 :hyps) (access rewrite-rule rule2 :hyps) unify-subst) t))))) (defun find-subsumed-rule-names (lst rule ens wrld) ; Lst is a list of rewrite-rules. Rule is a rewrite-rule. We return ; the names of those elements of lst that are subsumed by rule. We ; skip those rules in lst that are disabled in the global enabled structure ; and those that are META or DEFINITION rules. (cond ((null lst) nil) ((and (enabled-numep (access rewrite-rule (car lst) :nume) ens) (not (eq (access rewrite-rule (car lst) :subclass) 'meta)) (not (eq (access rewrite-rule (car lst) :subclass) 'definition)) (subsumes-rewrite-rule rule (car lst) wrld)) (cons (base-symbol (access rewrite-rule (car lst) :rune)) (find-subsumed-rule-names (cdr lst) rule ens wrld))) (t (find-subsumed-rule-names (cdr lst) rule ens wrld)))) (defun find-subsuming-rule-names (lst rule ens wrld) ; Lst is a list of rewrite-rules. Rule is a rewrite-rule. We return ; the names of those elements of lst that subsume rule. We skip those ; rules in lst that are disabled and that are META or DEFINITION rules. (cond ((null lst) nil) ((and (enabled-numep (access rewrite-rule (car lst) :nume) ens) (not (eq (access rewrite-rule (car lst) :subclass) 'meta)) (not (eq (access rewrite-rule (car lst) :subclass) 'definition)) (subsumes-rewrite-rule (car lst) rule wrld)) (cons (base-symbol (access rewrite-rule (car lst) :rune)) (find-subsuming-rule-names (cdr lst) rule ens wrld))) (t (find-subsuming-rule-names (cdr lst) rule ens wrld)))) (defun forced-hyps (lst) (cond ((null lst) nil) ((and (nvariablep (car lst)) (not (fquotep (car lst))) (or (eq (ffn-symb (car lst)) 'force) (eq (ffn-symb (car lst)) 'case-split))) (cons (car lst) (forced-hyps (cdr lst)))) (t (forced-hyps (cdr lst))))) (defun strip-top-level-nots-and-forces (hyps) (cond ((null hyps) nil) (t (mv-let (not-flg atm) (strip-not (if (and (nvariablep (car hyps)) (not (fquotep (car hyps))) (or (eq (ffn-symb (car hyps)) 'force) (eq (ffn-symb (car hyps)) 'case-split))) (fargn (car hyps) 1) (car hyps))) (declare (ignore not-flg)) (cons atm (strip-top-level-nots-and-forces (cdr hyps))))))) (defun free-variable-error? (token name ctx wrld state) (if (and (null (match-free-default wrld)) (f-get-global 'match-free-error state)) (er soft ctx "The warning above has caused this error in order to make it clear ~ that there are free variables in ~s0 of a ~x1 rule generated from ~ ~x2. This error can be suppressed for the rest of this ACL2 ~ session by submitting the following form:~|~%~x3~|~%However, you ~ are advised not to do so until you have read the documentation on ~ ``free variables'' (see :DOC free-variables) in order to understand ~ the issues. In particular, you can supply a :match-free value for ~ the :rewrite rule class (see :DOC rule-classes) or a default for ~ the book under development (see :DOC set-match-free-default)." (if (eq token :forward-chaining) "some trigger term" "the hypotheses") token name '(set-match-free-error nil)) (value nil))) (defun extend-geneqv-alist (var geneqv alist wrld) ; For each pair (x . y) in alist, x is a variable and y is a geneqv. The ; result extends alist by assocating variable var with geneqv, thus extending ; the generated equivalence relation already associated with var in alist. (put-assoc-eq var (union-geneqv geneqv (cdr (assoc-eq var alist)) wrld) alist)) (mutual-recursion (defun covered-geneqv-alist (term geneqv alist wrld) ; Extends alist, an accumulator, as follows. The result associates, with each ; variable bound in term, a geneqv representing the list of all equivalence ; relations that are sufficient to preserve at one or more free occurrences of ; that variable in term, in order to preserve the given geneqv at term. This ; function creates the initial bound-vars-alist for ; double-rewrite-opportunities; see also the comment there. ; Alist is an accumulator with entries of the form (variable . geneqv). (cond ((variablep term) (extend-geneqv-alist term geneqv alist wrld)) ((fquotep term) alist) (t (covered-geneqv-alist-lst (fargs term) (geneqv-lst (ffn-symb term) geneqv nil wrld) alist wrld)))) (defun covered-geneqv-alist-lst (termlist geneqv-lst alist wrld) (cond ((endp termlist) alist) (t (covered-geneqv-alist-lst (cdr termlist) (cdr geneqv-lst) (covered-geneqv-alist (car termlist) (car geneqv-lst) alist wrld) wrld)))) ) (defun uncovered-equivs (geneqv covered-geneqv wrld) (cond ((endp geneqv) nil) (t (let ((equiv (access congruence-rule (car geneqv) :equiv)) (rst (uncovered-equivs (cdr geneqv) covered-geneqv wrld))) (cond ((geneqv-refinementp equiv covered-geneqv wrld) rst) (t (cons equiv rst))))))) (mutual-recursion (defun uncovered-equivs-alist (term geneqv var-geneqv-alist var-geneqv-alist0 obj-not-? acc-equivs acc-counts wrld) ; Accumulator acc-equiv is an alist that associates variables with lists of ; equivalence relations, and accumulator acc-counts associates variables with ; natural numbers. We are given a term whose value is to be maintained with ; respect to the given geneqv, along with var-geneqv-alist, which associates ; variables with ilsts of equivalence relations. We return extensions of ; acc-equivs, acc-counts, and var-geneqv-alist as follows. ; Consider a bound (by var-geneqv-alist) variable occurrence in term. Its ; context is known to preserve certain equivalence relations; but some of these ; may be "uncovered", i.e., not among the ones associated with this variable in ; var-geneqv-alist. If that is the case, then add those "uncovered" ; equivalence relations to the list associated with this variable in ; acc-equivs, and increment the value of this variable in acc-counts by 1. ; However, we skip the above analysis for the case that geneqv is *geneqv-iff* ; and we are at the top level of the IF-structure of the top-level term (not ; including the tests). This function is used for creating warnings that ; suggest the use of double-rewrite, which however is generally not necessary ; in such situations; see rewrite-solidify-plus. ; For a free variable occurrence in term, we leave acc-equivs and acc-counts ; unchanged, and instead extend var-geneqv-alist by associating this variable ; with the geneqv for its context. Var-geneqv-alist0 is left unchanged by this ; process, for purposes of checking free-ness. (cond ((variablep term) (let ((binding (assoc-eq term var-geneqv-alist0))) (cond ((null binding) (mv acc-equivs acc-counts (extend-geneqv-alist term geneqv var-geneqv-alist wrld))) ((and obj-not-? (equal geneqv *geneqv-iff*)) ; The call of rewrite-solidify-plus in rewrite makes it unnecessary to warn ; when the objective is other than '? and the given geneqv is *geneqv-iff*. (mv acc-equivs acc-counts var-geneqv-alist)) (t (let* ((covered-geneqv (cdr binding)) (uncovered-equivs (uncovered-equivs geneqv covered-geneqv wrld))) (cond (uncovered-equivs (mv (put-assoc-eq term (union-eq uncovered-equivs (cdr (assoc-eq term acc-equivs))) acc-equivs) (put-assoc-eq term (1+ (or (cdr (assoc-eq term acc-counts)) 0)) acc-counts) var-geneqv-alist)) (t (mv acc-equivs acc-counts var-geneqv-alist)))))))) ((or (fquotep term) (eq (ffn-symb term) 'double-rewrite)) (mv acc-equivs acc-counts var-geneqv-alist)) ((and obj-not-? (eq (ffn-symb term) 'if)) (mv-let (acc-equivs acc-counts var-geneqv-alist) (uncovered-equivs-alist (fargn term 3) geneqv var-geneqv-alist var-geneqv-alist0 t acc-equivs acc-counts wrld) (mv-let (acc-equivs acc-counts var-geneqv-alist) (uncovered-equivs-alist (fargn term 2) geneqv var-geneqv-alist var-geneqv-alist0 t acc-equivs acc-counts wrld) (uncovered-equivs-alist (fargn term 1) *geneqv-iff* var-geneqv-alist var-geneqv-alist0 nil acc-equivs acc-counts wrld)))) (t (uncovered-equivs-alist-lst (fargs term) (geneqv-lst (ffn-symb term) geneqv nil wrld) var-geneqv-alist var-geneqv-alist0 acc-equivs acc-counts wrld)))) (defun uncovered-equivs-alist-lst (termlist geneqv-lst var-geneqv-alist var-geneqv-alist0 acc-equivs acc-counts wrld) (cond ((endp termlist) (mv acc-equivs acc-counts var-geneqv-alist)) (t (mv-let (acc-equivs acc-counts var-geneqv-alist) (uncovered-equivs-alist (car termlist) (car geneqv-lst) var-geneqv-alist var-geneqv-alist0 nil acc-equivs acc-counts wrld) (uncovered-equivs-alist-lst (cdr termlist) (cdr geneqv-lst) var-geneqv-alist var-geneqv-alist0 acc-equivs acc-counts wrld))))) ) (defun double-rewrite-opportunities (hyp-index hyps var-geneqv-alist final-term final-location final-geneqv wrld) ; We return an alist having entries (location var-equiv-alist ; . var-count-alist), where location is a string identifying a term (either the ; hyp-index_th member of the original hyps, or the final-term), var-equiv-alist ; associates variables of that term with their "uncovered equivs" as defined ; below, and var-count-alist associates variables of that term with the number ; of occurrences of a given variable that have at least one "uncovered" equiv. ; This function is called only for the purpose of producing a warning when ; there is a missed opportunity for a potentially useful call of ; double-rewrite. Consider a variable occurrence in hyps, the hypotheses of a ; rule, in a context where it is sufficient to preserve equiv. If that ; variable occurs in the left-hand side of a rewrite rule (or the max-term of a ; linear rule) in at least one context where it is sufficient to preserve ; equiv, that would give us confidence that the value associated with that ; occurrence (in the unifying substitution) had been fully rewritten with ; respect to equiv. But otherwise, we want to note this "uncovered" equiv for ; that variable in that hyp. ; We give similar treatment for the right-hand side of a rewrite rule and ; conclusion of a linear rule, using the parameters final-xxx. ; Var-geneqv-alist is an alist that binds variables to geneqvs. Initially, the ; keys are exactly the bound variables of the unifying substitution. Each key ; is associated with a geneqv that represents the equivalence relation ; generated by all equivalence relations known to be preserved for at least one ; variable occurrence in the pattern that was matched to give the unifying ; substitution (the left left-hand side of a rewrite rule or max-term of a ; linear rule). As we move through hyps, we may encounter a hypothesis (equal ; var term) or (equiv var (double-rewrite term)) that binds a variable, var, in ; which case we will extend var-geneqv-alist for var at that point. Note that ; we do not extend var-geneqv-alist for other free variables in hypotheses, ; because we do not know the equivalence relations that were maintained when ; creating the rewritten terms to which the free variables are bound. (cond ((endp hyps) (mv-let (var-equivs-alist var-counts var-geneqv-alist) (uncovered-equivs-alist final-term final-geneqv var-geneqv-alist var-geneqv-alist nil nil nil wrld) (declare (ignore var-geneqv-alist)) (if var-equivs-alist (list (list* final-location var-equivs-alist var-counts)) nil))) (t (mv-let (forcep bind-flg) (binding-hyp-p (car hyps) var-geneqv-alist wrld) (let ((hyp (if forcep (fargn (car hyps) 1) (car hyps)))) (cond (bind-flg (let* ((equiv (ffn-symb hyp)) (var (fargn hyp 1)) (term0 (fargn hyp 2)) (term (if (and (nvariablep term0) (not (fquotep term0)) (eq (ffn-symb term0) 'double-rewrite)) (fargn term0 1) term0)) (new-geneqv (cadr (geneqv-lst equiv *geneqv-iff* nil wrld)))) (double-rewrite-opportunities (1+ hyp-index) (cdr hyps) (covered-geneqv-alist term new-geneqv (assert$ (variablep var) (extend-geneqv-alist var new-geneqv var-geneqv-alist wrld)) wrld) final-term final-location final-geneqv wrld))) (t (mv-let (var-equivs-alist var-counts var-geneqv-alist) (uncovered-equivs-alist (car hyps) *geneqv-iff* var-geneqv-alist var-geneqv-alist t nil nil wrld) (let ((cdr-result (double-rewrite-opportunities (1+ hyp-index) (cdr hyps) var-geneqv-alist final-term final-location final-geneqv wrld))) (if var-equivs-alist (cons (list* (msg "the ~n0 hypothesis" (list hyp-index)) var-equivs-alist var-counts) cdr-result) cdr-result)))))))))) (defun show-double-rewrite-opportunities1 (location var-equivs-alist var-count-alist token name max-term-msg ctx state) ; This should only be called in a context where we know that double-rewrite ; warnings are enabled. Otherwise we lose efficiency, and anyhow warning$ is ; called below with ("Double-rewrite"). (cond ((endp var-equivs-alist) state) (t (pprogn (let* ((var (caar var-equivs-alist)) (count (let ((pair (assoc-eq var var-count-alist))) (assert$ pair (cdr pair))))) (warning$ ctx ("Double-rewrite") "In a ~x0 rule generated from ~x1~@2, ~ equivalence relation~#3~[ ~&3~ is~/s ~&3 ~ are~] maintained at ~n4 problematic ~ occurrence~#5~[~/s~] of variable ~x6 in ~@7, ~ but not at any binding occurrence of ~x6. ~ Consider replacing ~#5~[that ~ occurrence~/those ~n4 occurrences~] of ~x6 in ~ ~@7 with ~x8. See :doc double-rewrite for ~ more information on this issue." token name max-term-msg (cdar var-equivs-alist) count (if (eql count 1) 0 1) var location (list 'double-rewrite var))) (show-double-rewrite-opportunities1 location (cdr var-equivs-alist) var-count-alist token name max-term-msg ctx state))))) (defun show-double-rewrite-opportunities (hyp-var-equivs-counts-alist-pairs token name max-term-msg ctx state) ; Hyp-var-equivs-counts-alist-pairs is an alist as returned by ; double-rewrite-opportunities; see the comment there. Final-term, ; final-location, final-var-equivs-alist, and final-var-count-alist are the ; analog of one entry of that alist, but for the right-hand side of a rewrite ; rule or the conclusion of a linear rule. ; For efficiency, check warning-disabled-p before calling this function. (cond ((endp hyp-var-equivs-counts-alist-pairs) state) (t (pprogn (show-double-rewrite-opportunities1 (caar hyp-var-equivs-counts-alist-pairs) (cadar hyp-var-equivs-counts-alist-pairs) (cddar hyp-var-equivs-counts-alist-pairs) token name max-term-msg ctx state) (show-double-rewrite-opportunities (cdr hyp-var-equivs-counts-alist-pairs) token name max-term-msg ctx state))))) (defun irrelevant-loop-stopper-pairs (pairs vars) ; Keep this in sync with remove-irrelevant-loop-stopper-pairs. (if pairs (if (and (member-eq (caar pairs) vars) (member-eq (cadar pairs) vars)) (irrelevant-loop-stopper-pairs (cdr pairs) vars) (cons (car pairs) (irrelevant-loop-stopper-pairs (cdr pairs) vars))) nil)) (defun chk-rewrite-rule-warnings (name match-free loop-stopper rule ctx ens wrld state) (let* ((token (if (eq (access rewrite-rule rule :subclass) 'definition) :definition :rewrite)) (hyps (access rewrite-rule rule :hyps)) (lhs (access rewrite-rule rule :lhs)) (non-rec-fns-lhs (non-recursive-fnnames lhs ens wrld)) (lhs-vars (all-vars lhs)) (rhs-vars (all-vars (access rewrite-rule rule :rhs))) (free-vars (free-vars-in-hyps-considering-bind-free hyps lhs-vars wrld)) (inst-hyps (hyps-that-instantiate-free-vars free-vars hyps)) (non-rec-fns-inst-hyps (non-recursive-fnnames-lst (strip-top-level-nots-and-forces inst-hyps) ens wrld)) (subsume-check-enabled (not (warning-disabled-p "Subsume"))) (subsumed-rule-names (and subsume-check-enabled (find-subsumed-rule-names (getprop (ffn-symb lhs) 'lemmas nil 'current-acl2-world wrld) rule ens wrld))) (subsuming-rule-names (and subsume-check-enabled (find-subsuming-rule-names (getprop (ffn-symb lhs) 'lemmas nil 'current-acl2-world wrld) rule ens wrld))) (equiv (access rewrite-rule rule :equiv)) (double-rewrite-opportunities (and (not (warning-disabled-p "Double-rewrite")) (double-rewrite-opportunities 1 hyps (covered-geneqv-alist lhs (cadr (geneqv-lst equiv nil nil wrld)) ; geneqv nil wrld) (access rewrite-rule rule :rhs) "the right-hand side" (cadr (geneqv-lst (access rewrite-rule rule :equiv) nil nil wrld)) wrld)))) (pprogn (cond (double-rewrite-opportunities (show-double-rewrite-opportunities double-rewrite-opportunities token name "" ctx state)) (t state)) (cond (non-rec-fns-lhs (warning$ ctx "Non-rec" "A ~x0 rule generated from ~x1 will be ~ triggered only by terms containing the non-recursive ~ function symbol~#2~[ ~&2. Unless this function ~ is~/s ~&2. Unless these functions are~] disabled, ~ this rule is unlikely ever to be used." token name (hide-lambdas non-rec-fns-lhs))) (t state)) (er-progn (cond ((and free-vars (null match-free)) (pprogn (warning$ ctx "Free" "A ~x0 rule generated from ~x1 contains the free ~ variable~#2~[ ~&2. This variable~/s ~&2. These ~ variables~] will be chosen by searching for ~#3~[an ~ instance~/instances~] of ~*4 in the context of the term ~ being rewritten. This is generally a severe restriction ~ on the applicability of a ~x0 rule. See :DOC ~ free-variables." token name free-vars inst-hyps (tilde-*-untranslate-lst-phrase inst-hyps t wrld)) (free-variable-error? token name ctx wrld state))) (t (value nil))) (pprogn (cond ((and free-vars (forced-hyps inst-hyps)) (warning$ ctx "Free" "For the forced ~#0~[hypothesis~/hypotheses~], ~*1, used ~ to instantiate free variables we will search for ~#0~[an ~ instance of the argument~/instances of the arguments~] ~ rather than ~#0~[an instance~/instances~] of the FORCE or ~ CASE-SPLIT ~#0~[term itself~/terms themselves~]. If a ~ search fails for such a hypothesis, we will cause a case ~ split on the partially instantiated hypothesis. Note ~ that this case split will introduce a ``free variable'' ~ into the conjecture. While sound, this will establish a ~ goal almost certain to fail since the restriction ~ described by this apparently necessary hypothesis ~ constrains a variable not involved in the problem. To ~ highlight this oddity, we will rename the free variables ~ in such forced hypotheses by prefixing them with ~ ``UNBOUND-FREE-''. This is not guaranteed to generate a ~ new variable but at least it generates an unusual one. ~ If you see such a variable in a subsequent proof (and did ~ not introduce them yourself) you should consider the ~ possibility that the free variables of this rewrite rule ~ were forced into the conjecture." (if (null (cdr (forced-hyps inst-hyps))) 0 1) (tilde-*-untranslate-lst-phrase (forced-hyps inst-hyps) t wrld))) (t state)) (cond ((set-difference-eq rhs-vars lhs-vars) ; Usually the above will be nil. If not, the recomputation below is no big ; deal. (cond ((set-difference-eq rhs-vars (all-vars1-lst hyps lhs-vars)) (warning$ ctx "Free" "A ~x0 rule generated from ~x1 contains the the free ~ variable~#2~[~/s~] ~&2 on the right-hand side of the ~ rule, which ~#2~[is~/are~] not bound on the left-hand ~ side~#3~[~/ or in the hypothesis~/ or in any ~ hypothesis~]. This can cause new variables to be ~ introduced into the proof, which may surprise you." token name (set-difference-eq rhs-vars (all-vars1-lst hyps lhs-vars)) (zero-one-or-more hyps))) (t state))) (t state)) (cond (non-rec-fns-inst-hyps (warning$ ctx "Non-rec" "As noted, we will instantiate the free ~ variable~#0~[~/s~], ~&0, of a ~x1 rule generated from ~ ~x2, by searching for the ~#3~[hypothesis~/set of ~ hypotheses~] shown above. However, ~#3~[this hypothesis ~ mentions~/these hypotheses mention~] the function ~ symbol~#4~[ ~&4, which is~/s ~&4, which are~] defun'd ~ non-recursively. Unless disabled, ~#4~[this function ~ symbol is~/these function symbols are~] unlikely to occur ~ in the conjecture being proved and hence the search for ~ the required ~#3~[hypothesis~/hypotheses~] will likely ~ fail." free-vars token name inst-hyps (hide-lambdas non-rec-fns-inst-hyps))) (t state)) (cond (subsumed-rule-names (warning$ ctx ("Subsume") "A newly proposed ~x0 rule generated from ~x1 probably ~ subsumes the previously added :REWRITE rule~#2~[~/s~] ~ ~&2, in the sense that the new rule will now probably be ~ applied whenever the old rule~#2~[~/s~] would have been." token name subsumed-rule-names)) (t state)) (cond (subsuming-rule-names (warning$ ctx ("Subsume") "The previously added rule~#1~[~/s~] ~&1 subsume~#1~[s~/~] ~ a newly proposed ~x0 rule generated from ~x2, in the ~ sense that the old rule~#1~[ rewrites a more general ~ target~/s rewrite more general targets~]. Because the ~ new rule will be tried first, it may nonetheless find ~ application." token subsuming-rule-names name)) (t state)) (cond ((warning-disabled-p "Loop-Stopper") state) (t (let ((bad-pairs (irrelevant-loop-stopper-pairs loop-stopper lhs-vars))) (cond (bad-pairs (warning$ ctx ("Loop-Stopper") "When the ~x0 rule generated from ~x1 is created, ~ the ~#2~[entry~/entries~] ~&2 from the specified ~ :LOOP-STOPPER will be ignored because the two ~ specified variables do not both occur on the ~ left-hand side of the rule. See :DOC loop-stopper." token name bad-pairs)) (t state))))) (value nil)))))) (defun chk-acceptable-rewrite-rule2 (name match-free loop-stopper hyps concl ctx ens wrld state) ; This is the basic function for checking that (IMPLIES (AND . hyps) ; concl) generates a useful :REWRITE rule. If it does not, we cause an ; error. If it does, we may print some warnings regarding the rule ; generated. The superior functions, chk-acceptable-rewrite-rule1 ; and chk-acceptable-rewrite-rule just cycle down to this one after ; flattening the IMPLIES/AND structure of the user's input term. When ; successful, this function returns a ttree justifying the storage of ; the :REWRITE rule -- it sometimes depends on type-set information. (mv-let (msg eqv lhs rhs ttree) (interpret-term-as-rewrite-rule name hyps concl ens wrld) (cond (msg (er soft ctx "~@0" msg)) (t (let ((rewrite-rule (create-rewrite-rule *fake-rune-for-anonymous-enabled-rule* nil hyps eqv lhs rhs nil nil nil wrld))) ; The :REWRITE rule created above is used only for subsumption checking and ; then discarded. The rune, nume, loop-stopper-lst, and match-free used are ; irrelevant. The warning messages, if any, concerning subsumption report the ; name of the rule as name. (er-progn (chk-rewrite-rule-warnings name match-free loop-stopper rewrite-rule ctx ens wrld state) (value ttree))))))) (defun chk-acceptable-rewrite-rule1 (name match-free loop-stopper lst ctx ens wrld state) ; Each element of lst is a pair, (hyps . concl) and we check that each ; such pair, when interpreted as the term (implies (and . hyps) ; concl), generates a legal :REWRITE rule. We return the accumulated ; ttrees. (cond ((null lst) (value nil)) (t (er-let* ((ttree1 (chk-acceptable-rewrite-rule2 name match-free loop-stopper (caar lst) (cdar lst) ctx ens wrld state)) (ttree (chk-acceptable-rewrite-rule1 name match-free loop-stopper (cdr lst) ctx ens wrld state))) (value (cons-tag-trees ttree1 ttree)))))) (defun chk-acceptable-rewrite-rule (name match-free loop-stopper term ctx ens wrld state) ; We strip the conjuncts out of term and flatten those in the ; hypotheses of implications to obtain a list of implications, each of ; the form (IMPLIES (AND . hyps) concl), and each represented simply ; by a pair (hyps . concl). For each element of that list we then ; determine whether it generates a legal :REWRITE rule. See ; chk-acceptable-rewrite-rule2 for the guts of this test. We either ; cause an error or return successfully. We may print warning ; messages without causing an error. On successful returns the value ; is a ttree that justifies the storage of all the :REWRITE rules. (chk-acceptable-rewrite-rule1 name match-free loop-stopper (unprettyify (remove-guard-holders term)) ctx ens wrld state)) ; So now we work on actually generating and adding the rules. (defun add-rewrite-rule2 (rune nume hyps concl loop-stopper-lst backchain-limit-lst match-free ens wrld) ; This is the basic function for generating and adding a rule named ; rune from the formula (IMPLIES (AND . hyps) concl). (mv-let (msg eqv lhs rhs ttree) (interpret-term-as-rewrite-rule (base-symbol rune) hyps concl ens wrld) (declare (ignore ttree)) (cond (msg ; Msg is nil if we have called chk-acceptable-rewrite-rule for the ; corresponding rule under the same event that we are processing here. But ; suppose we are in the second pass of encapsulate or the local compatibility ; check of certify-book. Then that check may have been done in a different ; world than the one we have now. ; Even then, we typically expect that if interpret-term-as-rewrite-rule avoids ; returning an error, then it does so for every call made on the same arguments ; other than, perhaps, the world. Looking at the code for ; interpret-term-as-rewrite-rule2 and its callees, we see that it suffices to ; show that if interpret-term-as-rewrite-rule2 returns nil for lhs and rhs that ; are returned by a call of interpret-term-as-rewrite-rule1, then that call of ; interpret-term-as-rewrite-rule2 returns nil when the only input argument ; changes are the world and, for the latter call, equiv-okp = t. A ; counterexample would have to be a term of the form (equiv x y), where equiv ; is an equivalence relation in the first world passed to ; interpret-term-as-rewrite-rule1 but not in the second, where ; interpret-term-as-rewrite-rule2 returns nil for lhs = x and rhs = y but ; returns a non-nil msg for lhs = (equiv x y) and rhs = *t*. The only way that ; can happen is with the bad-synp-hyp-msg check in ; interpret-term-as-rewrite-rule2, as in the following example -- and it does ; indeed happen! But we think this hard error is so rare that it is ; tolerable. ; (encapsulate ; () ; (defun my-equivp (x y) ; (equal (nfix x) (nfix y))) ; (local (defequiv my-equivp)) ; (defthm foo ; (implies (and (bind-free (list (cons 'y x)) (y)) ; (equal y x)) ; (my-equivp (identity x) y)))) (er hard 'add-rewrite-rule2 "We believe that this error is occurring because the conclusion of a ~ proposed :REWRITE rule generated from ~x0 is of the form (equiv LHS ~ RHS), where equiv was a known equivalence relation when this rule ~ was originally processed, but that is no longer the case. As a ~ result, the rule is now treated as rewriting (equiv LHS RHS) to t, ~ and yet a BIND-FREE hypothesis is attempting to bind a variable in ~ RHS. Perhaps you can fix this problem by making equiv an ~ equivalence relation non-locally." (base-symbol rune))) (t (let* ((match-free-value (match-free-value match-free hyps lhs wrld)) (rewrite-rule (create-rewrite-rule rune nume hyps eqv lhs rhs loop-stopper-lst backchain-limit-lst match-free-value wrld)) (wrld1 (putprop (ffn-symb lhs) 'lemmas (cons rewrite-rule (getprop (ffn-symb lhs) 'lemmas nil 'current-acl2-world wrld)) wrld))) (put-match-free-value match-free-value rune wrld1)))))) (defun add-rewrite-rule1 (rune nume lst loop-stopper-lst backchain-limit-lst match-free ens wrld) ; Each element of lst is a pair, (hyps . concl). We generate and ; add to wrld a :REWRITE for each. (cond ((null lst) wrld) (t (add-rewrite-rule1 rune nume (cdr lst) loop-stopper-lst backchain-limit-lst match-free ens (add-rewrite-rule2 rune nume (caar lst) (cdar lst) loop-stopper-lst backchain-limit-lst match-free ens wrld))))) (defun add-rewrite-rule (rune nume loop-stopper-lst term backchain-limit-lst match-free ens wrld) ; This function might better be called "add-rewrite-rules" because we ; may get many :REWRITE rules from term. But we are true to our naming ; convention. "Consistency is the hobgoblin of small minds." Emerson? (add-rewrite-rule1 rune nume (unprettyify (remove-guard-holders term)) loop-stopper-lst backchain-limit-lst match-free ens wrld)) ;--------------------------------------------------------------------------- ; Section: :LINEAR Rules ; We now move on to :LINEAR class rules. (deflabel linear :doc ":Doc-Section Rule-Classes make some arithmetic inequality rules~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example: (defthm length-member-leq-length If inequality reasoning begins to (implies (and (eqlablep e) consider how (length (member a b)) (true-listp x)) compares to any other term, add to (<= (length (member e x)) the set of known inequalities the fact (length x))) that it is no larger than (length b), :rule-classes :linear) provided (eqlablep a) and (true-listp b) rewrite to t. General Form: (and ... (implies (and ...hi...) (implies (and ...hk...) (and ... (rel lhs rhs) ...))) ...) ~ev[] Note: One ~c[:linear] rule class object might create many linear arithmetic rules from the ~c[:]~ilc[corollary] formula. To create the rules, we first flatten the ~ilc[and] and ~ilc[implies] structure of the formula, transforming it into a conjunction of formulas, each of the form ~bv[] (implies (and h1 ... hn) (rel lhs rhs)) ~ev[] where no hypothesis is a conjunction and ~c[rel] is one of the inequality relations ~ilc[<], ~ilc[<=], ~ilc[=], ~ilc[>], or ~ilc[>=]. If necessary, the hypothesis of such a conjunct may be vacuous. We create a ~c[:linear] rule for each such conjunct, if possible, and otherwise cause an error.~/ Each rule has one or more ``trigger terms'' which may be specified by the user using the ~c[:trigger-terms] field of the rule class or which may be defaulted to values chosen by the system. We discuss the determination of trigger terms after discussing how linear rules are used. ~c[:Linear] rules are used by an arithmetic decision procedure during rewriting. ~l[linear-arithmetic] and ~pl[non-linear-arithmetic]. Here we assume that the reader is familiar with the material described in ~ilc[linear-arithmetic]. Recall that we eliminate the unknowns of an inequality in term-order, largest unknowns first. (~l[term-order].) In order to facilitate this strategy, we store the inequalities in ``linear pots''. For purposes of the present discussion, let us say that an inequality is ``about'' its largest unknown. Then, all of the inequalities about a particular unknown are stored in the same linear pot, and the pot is said to be ``labeled'' with that unknown. This storage layout groups all of the inequalities which are potential candidates for cancellation with each other into one place. It is also key to the efficient operation of ~c[:linear] rules. If the arithmetic decision procedure has stabilized and not yielded a contradiction, we scan through the list of linear pots examining each label as we go. If the trigger term of some ~c[:linear] rule can be instantiated to match the label, we so instantiate that rule and attempt to relieve the hypotheses with general-purpose rewriting. If we are successful, we add the rule's instantiated conclusion to our set of inequalities. This may let cancellation continue. Note: Problems may arise if you explicitly store a linear lemma under a trigger term that, when instantiated, is not the largest unknown in the instantiated concluding inequality. Suppose for example you store the linear rule ~c[(<= (fn i j) (/ i (* j j)))] under the trigger term ~c[(fn i j)]. Then when the system ``needs'' an inequality about ~c[(fn a b)], (i.e., because ~c[(fn a b)] is the label of some linear pot, and hence the largest unknown in some inequality), it will appeal to the rule and deduce ~c[(<= (fn a b) (/ a (* b b)))]. However, the largest unknown in this inequality is ~c[(/ a (* b b))] and hence it will be stored in a linear pot labeled with ~c[(/ a (* b b))]. The original, triggering inequality which is in a pot about ~c[(fn a b)] will therefore not be cancelled against the new one. It is generally best to specify as a trigger term one of the ``maximal'' terms of the polynomial, as described below. We now describe how the trigger terms are determined. Most of the time, the trigger terms are not specified by the user and are instead selected by the system. However, the user may specify the terms by including an explicit ~c[:trigger-terms] field in the rule class, e.g., ~bv[] General Form of a Linear Rule Class: (:LINEAR :COROLLARY formula :TRIGGER-TERMS (term1 ... termk)) ~ev[] Each ~c[termi] must be a term and must not be a variable, quoted constant, lambda application, ~c[let-expression] or ~c[if-expression]. In addition, each ~c[termi] must be such that if all the variables in the term are instantiated and then the hypotheses of the corollary formula are relieved (possibly instantiating additional free variables), then all the variables in the concluding inequality are instantiated. We generate a linear rule for each conjuctive branch through the corollary and store each rule under each of the specified triggers. Thus, if the corollary formula contains several conjuncts, the variable restrictions on the ~c[termi] must hold for each conjunct. If ~c[:trigger-terms] is omitted the system computes a set of trigger terms. Each conjunct of the corollary formula may be given a unique set of triggers depending on the variables that occur in the conjunct and the addends that occur in the concluding inequality. In particular, the trigger terms for a conjunct is the list of all ``maximal addends'' in the concluding inequality. The ``addends'' of ~c[(+ x y)] and ~c[(- x y)] are the union of the addends of ~c[x] and ~c[y]. The addends of ~c[(- x)] and ~c[(* n x)], where ~c[n] is a rational constant, is just ~c[{x}]. The addends of an inequality are the union of the addends of the left- and right-hand sides. The addends of any other term, ~c[x], is ~c[{x}]. A term is maximal for a conjunct ~c[(implies hyps concl)] of the corollary if (a) the term is a non-variable, non-quote, non-lambda application, non-~ilc[let] and non-~ilc[if] expression, (b) the term contains enough variables so that when they are instantiated and the hypotheses are relieved (which may bind some free variables; ~pl[free-variables]) then all the variables in ~c[concl] are instantiated, and (c) no other addend is always ``bigger'' than the term, in the technical sense described below. The technical notion referenced above depends on the notion of ~em[fn-count], the number of function symbols in a term, and ~em[pseudo-fn-count], which is essentially the number of function symbols implicit in a constant (~pl[term-order], specifically the discussion of ``pseudo-function application count'' at the end). We say ~c[term1] is always bigger than ~c[term2] if all instances of ~c[term1] have a larger fn-count (actually lexicographic order of fn-count and pseudo-fn-count) than the corresponding instances of ~c[term2]. This is equivalent to saying that the fn-count of ~c[term1] is larger than that of ~c[term2] (by ``fn-count'' here we mean the lexicographic order of fn-count and pseudo-fn-count) and the variable bag for ~c[term2] is a subbag of that for ~c[term1]. For example, ~c[(/ a (* b b))] is always bigger than ~c[(fn a b)] because the first has two function applications and ~c[{a b}] is a subbag of ~c[a b b], but ~c[(/ a (* b b))] is not always bigger than ~c[(fn a x)].") (defun expand-inequality-fncall1 (term) ; Term is a non-variable, non-quotep term. If it is a call of one of ; the primitive arithmetic relations, <, =, and /=, we return a ; nearly-equivalent term using not, equal, and < in place of that ; top-level call. Otherwise, we return term. We ignore the guards of ; arithmetic relations expanded! ; Warning: See the warning in expand-inequality-fncall below. It is ; crucial that if (fn a b) is expanded here then the guards necessary ; to justify that expansion are implied by the rationalp assumptions ; produced during the linearization of the expanded term. In ; particular, (rationalp a) and (rationalp b) ought to be sufficient ; to permit (fn a b) to expand to whatever we produce below. (let ((fn (ffn-symb term))) (case fn (< term) (= (mcons-term* 'equal (fargn term 1) (fargn term 2))) (/= (mcons-term* 'not (mcons-term* 'equal (fargn term 1) (fargn term 2)))) (otherwise term)))) (defun expand-inequality-fncall (term) ; If term is a (possibly negated) call of a primitive arithmetic ; relation, <, = and /=, we reexpress it in terms of ; not, equal, and < so that it can be linearized successfully. ; Otherwise, we return term. ; Warning: This function expands the definitions of the primitives ; above without considering their guards. This is unsound if the ; expanded form is used in place of the term. For example, (= x y) ; is here expanded to (equal x y), and in the case that the ; guards are violated the two terms are not equivalent. Do not call ; this function casually! ; What is the intended use of this function? Suppose the user has ; proved a theorem, (implies hyps (= a b)) and wants it stored as a ; :LINEAR rule. We instead store a rule concluding with (equal a b)! ; Note that the rule we store is not equivalent to the rule proved! ; We've ignored the acl2-numberp guards on =. Isn't that scary? Yes. ; But how do :LINEAR rules get used? Let max be one of the maximal ; terms of the rule we store and suppose we encounter a term, max', ; that is an instance of max. Then we will instantiate the stored ; conclusion (equal a b) with the substitution derived from max' to ; obtain (equal a' b') and then linearize that. The linearization of ; an equality insists that both arguments be known rational -- i.e. ; that their type-sets are a subset of *ts-rational*. Thus, in ; essence we are acting as though we had the theorem (implies (and ; (rationalp a) (rationalp b) hyps) (equal a b)) and use type-set to ; relieve the first two hyps. But this imagined theorem is an easy ; consequence of (implies hyps (= a b)) given that (rationalp a) and ; (rationalp b) let us reduce (= a b) to (equal a b). (mv-let (negativep atm) (strip-not term) (let ((atm (cond ((variablep atm) atm) ((fquotep atm) atm) (t (expand-inequality-fncall1 atm))))) (cond (negativep (dumb-negate-lit atm)) (t atm))))) ; Once we linearize the conclusion of a :LINEAR lemma, we extract all the ; linear variables (i.e., terms in the alist of the polys) and identify ; those that are "maximal." (defun all-vars-in-poly-lst (lst) ; Lst is a list of polynomials. We return the list of all linear variables ; used. (cond ((null lst) nil) (t (union-equal (strip-cars (access poly (car lst) :alist)) (all-vars-in-poly-lst (cdr lst)))))) ; Part of the notion of maximal is "always bigger", which we develop here. (defun subbagp-eq (bag1 bag2) (cond ((null bag1) t) ((null bag2) nil) ((member-eq (car bag1) bag2) (subbagp-eq (cdr bag1) (remove1-eq (car bag1) bag2))) (t nil))) (defun always-biggerp-data (term) ; See always-biggerp. (mv-let (fn-cnt p-fn-cnt) (fn-count term) (cons term (cons fn-cnt (cons p-fn-cnt (all-vars-bag term nil)))))) (defun always-biggerp-data-lst (lst) ; See always-biggerp. (cond ((null lst) nil) (t (cons (always-biggerp-data (car lst)) (always-biggerp-data-lst (cdr lst)))))) (defun always-biggerp (abd1 abd2) ; We say term1 is always bigger than term2 if all instances of term1 ; have a larger fn-count (actually lexicographic order of fn-count and ; pseudo-fn-count) than the corresponding instances of term2. This is ; equivalent to saying that the fn-count of term1 is larger than that ; of term2 (by "fn-count" here we mean the lexicographic order of ; fn-count and pseudo-fn-count) and the variable bag for term2 is a ; subbag of that for term1. ; Because we will be doing this check repeatedly across a list of terms ; we have converted the terms into "abd" (always bigger data) ; triples of the form (term fn-cnt . vars). Our two arguments are ; abd triples for term1 and term2. (and (or (> (cadr abd1) (cadr abd2)) (and (eql (cadr abd1) (cadr abd2)) (> (caddr abd1) (caddr abd2)))) (subbagp-eq (cdddr abd2) (cdddr abd1)))) ; That completes the notion of always-biggerp. We now complete the ; notion of "maximal term". It is probably best to read backwards from ; that defun. (defun no-element-always-biggerp (abd-lst abd) ; abd-lst is a list of always-biggerp-data triples. Abd is one such ; triple. If there is an element of the lst that is always bigger than ; abd, we return nil; else t. (cond ((null abd-lst) t) ((always-biggerp (car abd-lst) abd) nil) (t (no-element-always-biggerp (cdr abd-lst) abd)))) (defun maximal-terms1 (abd-lst abd-lst0 needed-vars) ; See maximal-terms. (cond ((null abd-lst) nil) ((and (nvariablep (car (car abd-lst))) (not (fquotep (car (car abd-lst)))) (not (flambda-applicationp (car (car abd-lst)))) (not (eq (ffn-symb (car (car abd-lst))) 'if)) (subsetp-eq needed-vars (cdddr (car abd-lst))) (no-element-always-biggerp abd-lst0 (car abd-lst))) (cons (car (car abd-lst)) (maximal-terms1 (cdr abd-lst) abd-lst0 needed-vars))) (t (maximal-terms1 (cdr abd-lst) abd-lst0 needed-vars)))) (defun maximal-terms (lst hyp-vars concl-vars) ; Lst is a list of terms. Hyp-vars and concl-vars are the variables ; occurring in the hypothesis and conclusion, respectively, of some ; lemma. We wish to return the subset of "maximal terms" in lst. ; These terms will be used as triggers to fire the :LINEAR rule built ; from (implies hyps concl). A term is maximal if it is not a ; variable, quote, lambda-application or IF, its variables plus those ; of the hyps include those of the conclusion (so there are no free ; vars in the conclusion after we match on the maximal term and ; relieve the hyps) and there is no other term in lst that is "always ; bigger." Intuitively, the idea behind "always bigger" is that the ; fn-count of one term is larger than that of the other, under all ; instantiations. ; The subroutine maximal-terms1 does most of the work. We convert the ; list of terms into an abd list, containing triples of the form (term ; fn-cnt . vars) for each term in lst. Then we pass maximal-terms1 ; two copies of this; the first it recurs down so as to visit one term ; at a time and the second it holds fixed to use to search for bigger ; terms. Finally, a condition equivalent to the variable restriction ; above is that each maximal term contain at least those variables in ; the conclusion which aren't in the hyps, and so we compute that set ; here to avoid more consing. (let ((abd-lst (always-biggerp-data-lst lst))) (maximal-terms1 abd-lst abd-lst (if (eq hyp-vars t) nil (set-difference-eq concl-vars hyp-vars))))) ; That finishes maximal-terms. Onward. ; We now develop the functions to support the friendly user interface. (defun collect-when-ffnnamesp (fns lst) ; Return the subset of lst consisting of those terms that mention any ; fn in fns. (cond ((null lst) nil) ((ffnnamesp fns (car lst)) (cons (car lst) (collect-when-ffnnamesp fns (cdr lst)))) (t (collect-when-ffnnamesp fns (cdr lst))))) (defun make-free-max-terms-msg1 (max-terms vars hyps) ; This function is used by make-free-max-terms-msg1 and is building a ; list of pairs of the form (str . alist'). Each such pair is ; suitable for giving to the ~@ fmt directive, which will print the ; string str under the alist obtained by appending alist' to the ; current alist. The idea here is simply to identify those max-terms ; that give rise to free-vars in the hyps and to comment upon them. (cond ((null max-terms) nil) ((subsetp-eq vars (all-vars (car max-terms))) (make-free-max-terms-msg1 (cdr max-terms) vars hyps)) (t (cons (cons "When ~xN is triggered by ~xT the variable~#V~[~/s~] ~&V ~ will be chosen by searching for ~#H~[an ~ instance~/instances~] of ~&H among the hypotheses of the ~ conjecture being rewritten. " (list (cons #\T (car max-terms)) (cons #\V (set-difference-eq vars (all-vars (car max-terms)))) (cons #\H (hyps-that-instantiate-free-vars (set-difference-eq vars (all-vars (car max-terms))) hyps)))) (make-free-max-terms-msg1 (cdr max-terms) vars hyps))))) (defun make-free-max-terms-msg (name max-terms vars hyps) ; We make a message suitable for giving to the ~* fmt directive that ; will print out a sequence of sentences of the form "When name is ; triggered by foo the variables u and v will be chosen by searching ; for the hypotheses h1 and h2. When ..." Vars is a list of the ; variables occurring in the hypotheses of the lemma named name. ; Hyps is the list of hyps. We always end with two spaces. (list* "" "~@*" "~@*" "~@*" (make-free-max-terms-msg1 max-terms vars hyps) (list (cons #\N name)))) (defun external-linearize (term ens wrld state) (linearize term t ;positivep nil ;type-alist ens (ok-to-force-ens ens) wrld ;wrld nil ;ttree state)) (defun bad-synp-hyp-msg-for-linear (max-terms hyps wrld) (if (null max-terms) (mv nil nil) (let ((bad-synp-hyp-msg (bad-synp-hyp-msg hyps (all-vars (car max-terms)) nil wrld))) (if bad-synp-hyp-msg (mv bad-synp-hyp-msg (car max-terms)) (bad-synp-hyp-msg-for-linear (cdr max-terms) hyps wrld))))) (defun show-double-rewrite-opportunities-linear (hyps max-terms final-term name ctx wrld state) (cond ((endp max-terms) state) (t (pprogn (show-double-rewrite-opportunities (double-rewrite-opportunities 1 hyps (covered-geneqv-alist (car max-terms) nil nil wrld) final-term "the conclusion" *geneqv-iff* ; final-geneqv wrld) :linear name (msg " for trigger term ~x0" (untranslate (car max-terms) nil wrld)) ctx state) (show-double-rewrite-opportunities-linear hyps (cdr max-terms) final-term name ctx wrld state))))) (defun chk-acceptable-linear-rule2 (name match-free trigger-terms hyps concl ctx ens wrld state) ; This is the basic function for checking that (implies (AND . hyps) ; concl) generates a useful :LINEAR rule. If it does not, we cause an ; error. If it does, we may print some warnings regarding the rule ; generated. The superior functions, chk-acceptable-linear-rule1 ; and chk-acceptable-linear-rule just cycle down to this one after ; flattening the IMPLIES/AND structure of the user's input term. ; The trigger-terms above are those supplied by the user in the rule class. If ; nil, we are to generate the trigger terms automatically, choosing all of the ; maximal terms. If provided, we know that each element of trigger-terms is a ; term that is a legal (if possibly silly) trigger for each rule. (let* ((xconcl (expand-inequality-fncall concl)) (lst (external-linearize xconcl ens wrld state))) (cond ((null lst) (er soft ctx "No :LINEAR rule can be generated from ~x0. See :DOC linear." name)) ((not (null (cdr lst))) (er soft ctx "No :LINEAR rule can be generated from ~x0 because the ~ linearization of its conclusion, which in normal form is ~p1, ~ produces a disjunction of polynomial inequalities. See :DOC ~ linear." name (untranslate xconcl t wrld))) (t (let* ((all-vars-hyps (all-vars-in-hyps hyps)) (potential-free-vars (free-vars-in-hyps-considering-bind-free hyps nil wrld)) (all-vars-in-poly-lst (all-vars-in-poly-lst (car lst))) (max-terms (or trigger-terms (maximal-terms all-vars-in-poly-lst all-vars-hyps (all-vars concl)))) (non-rec-fns (non-recursive-fnnames-lst max-terms ens wrld)) (bad-max-terms (collect-when-ffnnamesp non-rec-fns max-terms)) (free-max-terms-msg (make-free-max-terms-msg name max-terms potential-free-vars hyps))) (cond ((null max-terms) (cond ((null all-vars-in-poly-lst) (er soft ctx "No :LINEAR rule can be generated from ~x0 because ~ there are no ``maximal terms'' in the inequality ~ produced from its conclusion. In fact, the inequality ~ has simplified to one that has no variables." name)) (t (er soft ctx "No :LINEAR rule can be generated from ~x0 because ~ there are no ``maximal terms'' in the inequality ~ produced from its conclusion. The inequality produced ~ from its conclusion involves a linear polynomial in ~ the unknown~#1~[~/s~] ~&1. No unknown above has the ~ three properties of a maximal term (see :DOC linear). ~ What can you do? The most direct solution is to make ~ this a :REWRITE rule rather than a :LINEAR rule. Of ~ course, you then have to make sure your intended ~ application can suffer it being a :REWRITE rule! A ~ more challenging (and sometimes more rewarding) ~ alternative is to package up some of your functions ~ into a new non-recursive function (either in the ~ unknowns or the hypotheses) so as to create a maximal ~ term. Of course, if you do that, you have to arrange ~ to use that non-recursive function in the intended ~ applications of this rule." name all-vars-in-poly-lst)))) (t (mv-let (bad-synp-hyp-msg bad-max-term) (bad-synp-hyp-msg-for-linear max-terms hyps wrld) (cond (bad-synp-hyp-msg (er soft ctx "While checking the hypotheses of ~x0 and using ~ the trigger term ~x1, the following error message ~ was generated: ~% ~%~ ~@2" name bad-max-term bad-synp-hyp-msg)) (t (pprogn (if (warning-disabled-p "Double-rewrite") state (show-double-rewrite-opportunities-linear hyps max-terms concl name ctx wrld state)) (cond ((equal max-terms bad-max-terms) (warning$ ctx "Non-rec" "A :LINEAR rule generated from ~x0 will be ~ triggered only by terms containing the ~ non-recursive function symbol~#1~[ ~&1. Unless ~ this function is~/s ~&1. Unless these functions ~ are~] disabled, such triggering terms are ~ unlikely to arise and so ~x0 is unlikely to ever ~ be used." name (hide-lambdas non-rec-fns))) (bad-max-terms (warning$ ctx "Non-rec" "A :LINEAR rule generated from ~x0 will be ~ triggered by the terms ~&1. ~N2 of these terms, ~ namely ~&3, contain~#3~[s~/~] the non-recursive ~ function symbol~#4~[ ~&4. Unless this function ~ is~/s ~&4. Unless these functions are~] ~ disabled, ~x0 is unlikely to be triggered via ~ ~#3~[this term~/these terms~]." name max-terms (length bad-max-terms) bad-max-terms (hide-lambdas non-rec-fns))) (t state)) (cond ((and (car (cddddr free-max-terms-msg)) (null match-free)) (pprogn (warning$ ctx "Free" "A :LINEAR rule generated from ~x0 will be ~ triggered by the term~#1~[~/s~] ~&1. ~*2This is ~ generally a severe restriction on the ~ applicability of the :LINEAR rule." name max-terms free-max-terms-msg) (free-variable-error? :linear name ctx wrld state))) (t (value nil)))))))))))))) (defun chk-acceptable-linear-rule1 (name match-free trigger-terms lst ctx ens wrld state) ; Each element of lst is a pair, (hyps . concl) and we check that each ; such pair, when interpreted as the term (implies (and . hyps) ; concl), generates a legal :LINEAR rule. (cond ((null lst) (value nil)) (t (er-progn (chk-acceptable-linear-rule2 name match-free trigger-terms (caar lst) (cdar lst) ctx ens wrld state) (chk-acceptable-linear-rule1 name match-free trigger-terms (cdr lst) ctx ens wrld state))))) (defun chk-acceptable-linear-rule (name match-free trigger-terms term ctx ens wrld state) ; We strip the conjuncts out of term and flatten those in the ; hypotheses of implications to obtain a list of implications, each of ; the form (IMPLIES (AND . hyps) concl), and each represented simply ; by a pair (hyps . concl). For each element of that list we then ; determine whether it generates a legal :LINEAR rule. See ; chk-acceptable-linear-rule2 for the guts of this test. We either ; cause an error or return successfully. We may print warning ; messages without causing an error. (chk-acceptable-linear-rule1 name match-free trigger-terms (unprettyify (remove-guard-holders term)) ctx ens wrld state)) ; And now, to adding :LINEAR rules... (defun add-linear-rule3 (rune nume hyps concl max-terms backchain-limit-lst match-free put-match-free-done wrld) (cond ((null max-terms) wrld) (t (let* ((match-free-value (match-free-value match-free hyps (car max-terms) wrld)) (linear-rule (make linear-lemma :rune rune :nume nume :hyps hyps :concl concl :max-term (car max-terms) :backchain-limit-lst (rule-backchain-limit-lst backchain-limit-lst hyps wrld :rewrite) :match-free match-free-value)) (wrld1 (putprop (ffn-symb (access linear-lemma linear-rule :max-term)) 'linear-lemmas (cons linear-rule (getprop (ffn-symb (access linear-lemma linear-rule :max-term)) 'linear-lemmas nil 'current-acl2-world wrld)) wrld))) (add-linear-rule3 rune nume hyps concl (cdr max-terms) backchain-limit-lst match-free (or put-match-free-done match-free-value) (if put-match-free-done ; In this case we have already added this rune to the appropriate world global, ; so we do not want to do so again. wrld1 (put-match-free-value match-free-value rune wrld1))))))) (defun add-linear-rule2 (rune nume trigger-terms hyps concl backchain-limit-lst match-free ens wrld state) (let* ((concl (remove-guard-holders concl)) (xconcl (expand-inequality-fncall concl)) (lst (external-linearize xconcl ens wrld state)) (hyps (preprocess-hyps hyps)) (all-vars-hyps (all-vars-in-hyps hyps)) (max-terms (or trigger-terms (maximal-terms (all-vars-in-poly-lst (car lst)) all-vars-hyps (all-vars concl))))) (add-linear-rule3 rune nume hyps xconcl max-terms backchain-limit-lst match-free nil wrld))) (defun add-linear-rule1 (rune nume trigger-terms lst backchain-limit-lst match-free ens wrld state) (cond ((null lst) wrld) (t (add-linear-rule1 rune nume trigger-terms (cdr lst) backchain-limit-lst match-free ens (add-linear-rule2 rune nume trigger-terms (caar lst) (cdar lst) backchain-limit-lst match-free ens wrld state) state)))) (defun add-linear-rule (rune nume trigger-terms term backchain-limit-lst match-free ens wrld state) ; Sol Swords sent the following example on 10/12/09, which failed because of ; the modification after Version_3.6.1 to mv-let (to introduce mv-list in the ; expansion), until the call below of remove-guard-holders was added. ; (defun break-cons (x) ; (mv (car x) (cdr x))) ; (defthm break-cons-size-decr-0 ; (mv-let (car cdr) ; (break-cons x) ; (declare (ignore cdr)) ; (implies (consp x) ; (< (acl2-count car) (acl2-count x)))) ; :rule-classes :linear) ; (defthm break-cons-size-decr-1 ; (mv-let (car cdr) ; (break-cons x) ; (declare (ignore car)) ; (implies (consp x) ; (< (acl2-count cdr) (acl2-count x)))) ; :rule-classes :linear) ; (in-theory (disable break-cons acl2-count mv-nth)) ; (defun recur-over-break-cons (x) ; (if (atom x) ; (list x) ; (mv-let (car cdr) (break-cons x) ; (append (recur-over-break-cons car) ; (recur-over-break-cons cdr))))) (add-linear-rule1 rune nume trigger-terms (unprettyify (remove-guard-holders term)) backchain-limit-lst match-free ens wrld state)) ;--------------------------------------------------------------------------- ; Section: :WELL-FOUNDED-RELATION Rules (deflabel well-founded-relation :doc ":Doc-Section Rule-Classes show that a relation is well-founded on a set~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example: (defthm lex2p-is-well-founded-relation (and (implies (pairp x) (o-p (ordinate x))) (implies (and (pairp x) (pairp y) (lex2p x y)) (o< (ordinate x) (ordinate y)))) :rule-classes :well-founded-relation) ~ev[] The example above creates a ~c[:well-founded-relation] rule, where of course the functions ~c[pairp], ~c[lex2p], and ~c[ordinate] would have to be defined first. It establishes that ~c[lex2p] is a well-founded relation on ~c[pairp]s. We explain and give details below.~/ Exactly two general forms are recognized: ~bv[] General Forms (AND (IMPLIES (mp x) (O-P (fn x))) ; Property 1 (IMPLIES (AND (mp x) ; Property 2 (mp y) (rel x y)) (O< (fn x) (fn y)))), ~ev[] or ~bv[] (AND (O-P (fn x)) ; Property 1 (IMPLIES (rel x y) ; Property 2 (O< (fn x) (fn y)))) ~ev[] where ~c[mp], ~c[fn], and ~c[rel] are function symbols, ~c[x] and ~c[y] are distinct variable symbols, and no other ~c[:well-founded-relation] theorem about ~c[fn] has been proved. When the second general form is used, we act as though the first form were used with ~c[mp] being the function that ignores its argument and returns ~c[t]. The discussion below therefore considers only the first general form. Note: We are very rigid when checking that the submitted formula is of one of these forms. For example, in the first form, we insist that all the conjuncts appear in the order shown above. Thus, interchanging the two properties in the top-level conjunct or rearranging the hyptheses in the second conjunct both produce unrecognized forms. The requirement that each ~c[fn] be proved well-founded at most once ensures that for each well-founded relation, ~c[fn], there is a unique ~c[mp] that recognizes the domain on which ~c[rel] is well-founded. We impose this requirement simply so that ~c[rel] can be used as a short-hand when specifying the well-founded relations to be used in definitions; otherwise the specification would have to indicate which ~c[mp] was to be used. We also insist that the new ordering be embedded into the ordinals as handled by ~ilc[o-p] and ~ilc[o<] and not some into previously admitted user-defined well-founded set and relation. This restriction should pose no hardship. If ~c[mp] and ~c[rel] were previously shown to be well-founded via the embedding ~c[fn], and you know how to embed some new set and relation into ~c[mp] and ~c[rel], then by composing ~c[fn] with your new embedding and using the previously proved well-founded relation lemma you can embed the new set and relation into the ordinals. ~c[Mp] is a predicate that recognizes the objects that are supposedly ordered in a well-founded way by ~c[rel]. We call such an object an ``~c[mp]-measure'' or simply a ``measure'' when ~c[mp] is understood. Property 1 tells us that every measure can be mapped into an ACL2 ordinal. (~l[o-p].) This mapping is performed by ~c[fn]. Property 2 tells us that if the measure ~c[x] is smaller than the measure ~c[y] according to ~c[rel] then the image of ~c[x] under ~c[fn] is a smaller than that of ~c[y], according to the well-founded relation ~ilc[o<]. (~l[o<].) Thus, the general form of a ~c[:well-founded-relation] formula establishes that there exists a ~c[rel]-order preserving embedding (namely via ~c[fn]) of the ~c[mp]-measures into the ordinals. We can thus conclude that ~c[rel] is well-founded on ~c[mp]-measures. Such well-founded relations are used in the admissibility test for recursive functions, in particular, to show that the recursion terminates. To illustrate how such information may be used, consider a generic function definition ~bv[] (defun g (x) (if (test x) (g (step x)) (base x))). ~ev[] If ~c[rel] has been shown to be well-founded on ~c[mp]-measures, then ~c[g]'s termination can be ensured by finding a measure, ~c[(m x)], with the property that ~c[m] produces a measure: ~bv[] (mp (m x)), ; Defun-goal-1 ~ev[] and that the argument to ~c[g] gets smaller (when measured by ~c[m] and compared by ~c[rel]) in the recursion, ~bv[] (implies (test x) (rel (m (step x)) (m x))). ; Defun-goal-2 ~ev[] If ~c[rel] is selected as the ~c[:well-founded-relation] to be used in the definition of ~c[g], the definitional principal will generate and attempt to prove ~c[defun-goal-1] and ~c[defun-goal-2] to justify ~c[g]. We show later why these two goals are sufficient to establish the termination of ~c[g]. Observe that neither the ordinals nor the embedding, ~c[fn], of the ~c[mp]-measures into the ordinals is involved in the goals generated by the definitional principal. Suppose now that a ~c[:well-founded-relation] theorem has been proved for ~c[mp] and ~c[rel]. How can ~c[rel] be ``selected as the ~c[:well-founded-relation]'' by ~ilc[defun]? There are two ways. First, an ~ilc[xargs] keyword to the ~ilc[defun] event allows the specification of a ~c[:well-founded-relation]. Thus, the definition of ~c[g] above might be written ~bv[] (defun g (x) (declare (xargs :well-founded-relation (mp . rel))) (if (test x) (g (step x)) (base x))) ~ev[] Alternatively, ~c[rel] may be specified as the ~c[:default-well-founded-relation] in ~ilc[acl2-defaults-table] by executing the event ~bv[] (set-well-founded-relation rel). ~ev[] When a ~ilc[defun] event does not explicitly specify the relation in its ~ilc[xargs] the default relation is used. When ACL2 is initialized, the default relation is ~ilc[o<]. Finally, though it is probably obvious, we now show that ~c[defun-goal-1] and ~c[defun-goal-2] are sufficient to ensure the termination of ~c[g] provided ~c[property-1] and ~c[property-2] of ~c[mp] and ~c[rel] have been proved. To this end, assume we have proved ~c[defun-goal-1] and ~c[defun-goal-2] as well as ~c[property-1] and ~c[property-2] and we show how to admit ~c[g] under the primitive ACL2 definitional principal (i.e., using only the ordinals). In particular, consider the definition event ~bv[] (defun g (x) (declare (xargs :well-founded-relation o< :measure (fn (m x)))) (if (test x) (g (step x)) (base x))) ~ev[] Proof that ~c[g] is admissible: To admit the definition of ~c[g] above we must prove ~bv[] (o-p (fn (m x))) ; *1 ~ev[] and ~bv[] (implies (test x) ; *2 (o< (fn (m (step x))) (fn (m x)))). ~ev[] But *1 can be proved by instantiating ~c[property-1] to get ~bv[] (implies (mp (m x)) (o-p (fn (m x)))), ~ev[] and then relieving the hypothesis with ~c[defun-goal-1], ~c[(mp (m x))]. Similarly, *2 can be proved by instantiating ~c[property-2] to get ~bv[] (implies (and (mp (m (step x))) (mp (m x)) (rel (m (step x)) (m x))) (o< (fn (m (step x))) (fn (m x)))) ~ev[] and relieving the first two hypotheses by appealing to two instances of ~c[defun-goal-1], thus obtaining ~bv[] (implies (rel (m (step x)) (m x)) (o< (fn (m (step x))) (fn (m x)))). ~ev[] By chaining this together with ~c[defun-goal-2], ~bv[] (implies (test x) (rel (m (step x)) (m x))) ~ev[] we obtain *2. Q.E.D.") (defun destructure-well-founded-relation-rule (term) ; We check that term is the translation of one of the two forms ; described in :DOC well-founded-relation. We return two results, (mv ; mp rel). If mp is nil in the result, then term is not of the ; required form. If mp is t, then term is of the second general form ; (i.e., we act as though t were the function symbol for (lambda (x) ; t)). With that caveat, if the mp is non-nil then term establishes ; that rel is a well-founded relation on mp-measures. (case-match term (('IF ('IMPLIES (mp x) ('O-P (fn x))) ('IMPLIES ('IF (mp x) ('IF (mp y) (rel x y) ''NIL) ''NIL) ('O< (fn x) (fn y))) ''NIL) (cond ((and (symbolp mp) (variablep x) (symbolp fn) (variablep y) (not (eq x y)) (symbolp rel)) (mv mp rel)) (t (mv nil nil)))) (('IF ('O-P (fn x)) ('IMPLIES (rel x y) ('O< (fn x) (fn y))) ''NIL) (cond ((and (variablep x) (symbolp fn) (variablep y) (not (eq x y)) (symbolp rel)) (mv t rel)) (t (mv nil nil)))) (& (mv nil nil)))) (defun chk-acceptable-well-founded-relation-rule (name term ctx wrld state) (mv-let (mp rel) (destructure-well-founded-relation-rule term) (cond ((null mp) (er soft ctx "No :WELL-FOUNDED-RELATION rule can be generated for ~x0 ~ because it does not have either of the two general forms ~ described in :DOC well-founded-relation." name)) ((and (assoc-eq rel (global-val 'well-founded-relation-alist wrld)) (not (eq (cadr (assoc-eq rel (global-val 'well-founded-relation-alist wrld))) mp))) (er soft ctx "~x0 was shown in ~x1 to be well-founded~@2 We do not permit more ~ than one domain to be associated with a well-founded relation. To ~ proceed in this direction, you should define some new function ~ symbol to be ~x0 and state your well-foundedness in terms of the ~ new function." rel (cadr (cddr (assoc-eq rel (global-val 'well-founded-relation-alist wrld)))) (if (eq (cadr (assoc-eq rel (global-val 'well-founded-relation-alist wrld))) t) "." (msg " on objects satisfying ~x0." (cadr (assoc-eq rel (global-val 'well-founded-relation-alist wrld))))))) (t (value nil))))) (defun add-well-founded-relation-rule (rune nume term wrld) (declare (ignore nume)) (mv-let (mp rel) (destructure-well-founded-relation-rule term) (global-set 'well-founded-relation-alist (cons (cons rel (cons mp rune)) (global-val 'well-founded-relation-alist wrld)) wrld))) ;--------------------------------------------------------------------------- ; Section: :BUILT-IN-CLAUSE Rules (deflabel built-in-clause :doc ":Doc-Section Rule-Classes to build a clause into the simplifier~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example: (defthm acl2-count-abl (and (implies (and (true-listp x) (not (equal x nil))) (< (acl2-count (abl x)) (acl2-count x))) (implies (and (true-listp x) (not (equal nil x))) (< (acl2-count (abl x)) (acl2-count x)))) :rule-classes :built-in-clause) ~ev[] A ~c[:built-in-clause] rule can be built from any formula other than propositional tautologies. Roughly speaking, the system uses the list of built-in clauses as the first method of proof when attacking a new goal. Any goal that is subsumed by a built in clause is proved ``silently.''~/ ACL2 maintains a set of ``built-in'' clauses that are used to short-circuit certain theorem proving tasks. We discuss this at length below. When a theorem is given the rule class ~c[:built-in-clause] ACL2 flattens the ~ilc[implies] and ~ilc[and] structure of the ~c[:]~ilc[corollary] formula so as to obtain a set of formulas whose conjunction is equivalent to the given corollary. It then converts each of these to clausal form and adds each clause to the set of built-in clauses. The example above (regardless of the definition of ~c[abl]) will build in two clauses, ~bv[] {(not (true-listp x)) (equal x nil) (< (acl2-count (abl x)) (acl2-count x))} ~ev[] and ~bv[] {(not (true-listp x)) (equal nil x) (< (acl2-count (abl x)) (acl2-count x))}. ~ev[] We now give more background. Recall that a clause is a set of terms, implicitly representing the disjunction of the terms. Clause ~c[c1] is ``subsumed'' by clause ~c[c2] if some instance of ~c[c2] is a subset ~c[c1]. For example, let ~c[c1] be ~bv[] {(not (consp l)) (equal a (car l)) (< (acl2-count (cdr l)) (acl2-count l))}. ~ev[] Then ~c[c1] is subsumed by ~c[c2], shown below, ~bv[] {(not (consp x)) ; second term omitted here (< (acl2-count (cdr x)) (acl2-count x))} ~ev[] because we can instantiate ~c[x] in ~c[c2] with ~c[l] to obtain a subset of ~c[c1]. Observe that ~c[c1] is the clausal form of ~bv[] (implies (and (consp l) (not (equal a (car l)))) (< (acl2-count (cdr l)) (acl2-count l))), ~ev[] ~c[c2] is the clausal form of ~bv[] (implies (consp l) (< (acl2-count (cdr l)) (acl2-count l))) ~ev[] and the subsumption property just means that ~c[c1] follows trivially from ~c[c2] by instantiation. The set of built-in clauses is just a set of known theorems in clausal form. Any formula that is subsumed by a built-in clause is thus a theorem. If the set of built-in theorems is reasonably small, this little theorem prover is fast. ACL2 uses the ``built-in clause check'' in four places: (1) at the top of the iteration in the prover -- thus if a built-in clause is generated as a subgoal it will be recognized when that goal is considered, (2) within the simplifier so that no built-in clause is ever generated by simplification, (3) as a filter on the clauses generated to prove the termination of recursively ~ilc[defun]'d functions and (4) as a filter on the clauses generated to verify the guards of a function. The latter two uses are the ones that most often motivate an extension to the set of built-in clauses. Frequently a given formalization problem requires the definition of many functions which require virtually identical termination and/or guard proofs. These proofs can be short-circuited by extending the set of built-in clauses to contain the most general forms of the clauses generated by the definitional schemes in use. The attentive user might have noticed that there are some recursive schemes, e.g., recursion by ~ilc[cdr] after testing ~ilc[consp], that ACL2 just seems to ``know'' are ok, while for others it generates measure clauses to prove. Actually, it always generates measure clauses but then filters out any that pass the built-in clause check. When ACL2 is initialized, the clause justifying ~ilc[cdr] recursion after a ~ilc[consp] test is added to the set of built-in clauses. (That clause is ~c[c2] above.) Note that only a subsumption check is made; no rewriting or simplification is done. Thus, if we want the system to ``know'' that ~ilc[cdr] recursion is ok after a negative ~ilc[atom] test (which, by the definition of ~ilc[atom], is the same as a ~ilc[consp] test), we have to build in a second clause. The subsumption algorithm does not ``know'' about commutative functions. Thus, for predictability, we have built in commuted versions of each clause involving commutative functions. For example, we build in both ~bv[] {(not (integerp x)) (< 0 x) (= x 0) (< (acl2-count (+ -1 x)) (acl2-count x))} ~ev[] and the commuted version ~bv[] {(not (integerp x)) (< 0 x) (= 0 x) (< (acl2-count (+ -1 x)) (acl2-count x))} ~ev[] so that the user need not worry whether to write ~c[(= x 0)] or ~c[(= 0 x)] in definitions. ~c[:built-in-clause] rules added by the user can be enabled and disabled.") (defun chk-acceptable-built-in-clause-rule2 (name hyps concl ctx wrld state) ; This is the basic function for checking that (IMPLIES (AND . hyps) concl) ; generates a useful built-in clause rule. If it does not, we cause an error. ; The superior functions, chk-acceptable-built-in-clause-rule1 and ; chk-acceptable-built-in-clause-rule just cycle down to this one after ; flattening the IMPLIES/AND structure of the user's input term. (let* ((term (if (null hyps) concl (mcons-term* 'if (conjoin hyps) concl *t*))) (clauses (clausify term nil t (sr-limit wrld)))) (cond ((null clauses) (er soft ctx "~x0 is an illegal :built-in-clause rule because ~p1 clausifies ~ to nil, indicating that it is a propositional tautology. See ~ :DOC built-in-clause." name (untranslate (cond ((null hyps) concl) (t (mcons-term* 'implies (conjoin hyps) concl))) t wrld))) (t (value nil))))) (defun chk-acceptable-built-in-clause-rule1 (name lst ctx wrld state) ; Each element of lst is a pair, (hyps . concl) and we check that each such ; pair, when interpreted as the term (implies (and . hyps) concl), generates ; one or more clauses to be built-in. (cond ((null lst) (value nil)) (t (er-progn (chk-acceptable-built-in-clause-rule2 name (caar lst) (cdar lst) ctx wrld state) (chk-acceptable-built-in-clause-rule1 name (cdr lst) ctx wrld state))))) (defun chk-acceptable-built-in-clause-rule (name term ctx wrld state) ; We strip the conjuncts out of term and flatten those in the hypotheses of ; implications to obtain a list of implications, each of the form (IMPLIES (AND ; . hyps) concl), and each represented simply by a pair (hyps . concl). For ; each element of that list we then determine whether it generates one or more ; clauses. See chk-acceptable-built-in-clause-rule2 for the guts of this test. ; We either cause an error or return successfully. (chk-acceptable-built-in-clause-rule1 name (unprettyify term) ctx wrld state)) ; So now we work on actually generating and adding :BUILT-IN-CLAUSE rules. (mutual-recursion (defun fn-and-maximal-level-no (term wrld fn max) ; We explore term and return (mv fn max), where fn is an "explicit" function ; symbol of term, max is its get-level-no, and that level number is maximal in ; term. By an "explicit" function symbol of term we mean one not on ; *one-way-unify1-implicit-fns*. We return the initial fn and max unless some ; explicit symbol of term actually betters it. If you call this with fn=nil ; and max=-1 you will get back a legitimate function symbol if term contains at ; least one explicit symbol. Furthermore, it is always the maximal symbol ; occurring first in print-order. (cond ((variablep term) (mv fn max)) ((fquotep term) (mv fn max)) ((flambdap (ffn-symb term)) (mv-let (fn max) (fn-and-maximal-level-no (lambda-body (ffn-symb term)) wrld fn max) (fn-and-maximal-level-no-lst (fargs term) wrld fn max))) ((member-eq (ffn-symb term) *one-way-unify1-implicit-fns*) (fn-and-maximal-level-no-lst (fargs term) wrld fn max)) (t (let ((n (get-level-no (ffn-symb term) wrld))) (cond ((> n max) (fn-and-maximal-level-no-lst (fargs term) wrld (ffn-symb term) n)) (t (fn-and-maximal-level-no-lst (fargs term) wrld fn max))))))) (defun fn-and-maximal-level-no-lst (lst wrld fn max) (cond ((null lst) (mv fn max)) (t (mv-let (fn max) (fn-and-maximal-level-no (car lst) wrld fn max) (fn-and-maximal-level-no-lst (cdr lst) wrld fn max))))) ) (defun built-in-clause-discriminator-fn (cl wrld) (mv-let (fn max) (fn-and-maximal-level-no-lst cl wrld nil -1) (declare (ignore max)) fn)) (defun all-fnnames-subsumer (cl wrld) ; Let cl be a clause which is about to be built in. Cl subsumes another ; clause, cla, if under some instantiation of cl, cl', the literals of cl' are ; a subset of those of cla. Thus, a necessary condition for cl to subsume cla ; is that the function symbols of cl be a subset of those of cla. However, ; one-way-unify1 knows that (binary-+ '1 x) can be instantiated to be '7, by ; letting x be '6. Thus, if by "the function symbols" of a clause we mean ; those that explicitly occur, i.e., all-fnnames-lst, then, contrary to what ; was just said, it is possible for cl to subsume cla without the function ; symbols of cl being a subset of those of cla: let cl contain (binary-+ '1 x) ; where cla contains '7 and no mention of binary-+. So we here compute the ; list of function symbols of cl which must necessarily occur in cla. It is ; always sound to throw out symbols from the list returned here. In addition, ; we make sure that the "discriminator function symbol" of cl occur first in ; the list. That symbol will be used to classify this subsumer into a bucket ; in the built-in-clause list. (let ((syms (set-difference-eq (all-fnnames-lst cl) *one-way-unify1-implicit-fns*)) (discrim-fn (built-in-clause-discriminator-fn cl wrld))) (cond ((null discrim-fn) syms) (t (cons discrim-fn (remove1-eq discrim-fn syms)))))) (defun make-built-in-clause-rules1 (rune nume clauses wrld) ; We build a built-in-clause record for every element of clauses. We put the ; last literal of each clause first on the heuristic grounds that the last ; literal of a user-supplied clause is generally the most interesting and thus ; the one the subsumption check should look at first. ; Note: The :all-fnnames computed here has the property that the discriminator ; function symbol is the car and the remaining functions are in the cdr. When ; a built-in-clause record is stored into the built-in-clauses alist, the ; record is changed; the discriminator is stripped out, leaving the remaining ; fns as the :all-fnnames. (cond ((null clauses) nil) (t (let ((cl (cons (car (last (car clauses))) (butlast (car clauses) 1)))) (cons (make built-in-clause :rune rune :nume nume :clause cl :all-fnnames (all-fnnames-subsumer cl wrld)) (make-built-in-clause-rules1 rune nume (cdr clauses) wrld)))))) (defun chk-initial-built-in-clauses (lst wrld good-lst some-badp) ; This function sweeps down the list of initial built-in clause records and ; checks that the :all-fnnames of each is set properly given the current wrld. ; The standard top-level call of this function is (chk-initial-built-in-clauses ; *initial-built-in-clauses* wrld nil nil) where wrld is the world in which you ; wish to check the appropriateness of the initial setting. This function ; returns either nil, meaning that everything was ok, or a new copy of lst ; which is correct for the current wrld. (cond ((null lst) (cond (some-badp (reverse good-lst)) (t nil))) (t (let* ((clause (access built-in-clause (car lst) :clause)) (fnnames1 (access built-in-clause (car lst) :all-fnnames)) (fnnames2 (all-fnnames-subsumer clause wrld))) (chk-initial-built-in-clauses (cdr lst) wrld (cons `(make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause ',clause :all-fnnames ',fnnames2) good-lst) (or some-badp (not (equal fnnames1 fnnames2)))))))) (defun make-built-in-clause-rules (rune nume lst wrld) ; Each element of lst is a pair, (hyps . concl). We generate and collect the ; clauses for each such pair. (cond ((null lst) nil) (t (let* ((hyps (caar lst)) (concl (cdar lst)) (clauses (clausify (if (null hyps) concl (mcons-term* 'if (conjoin hyps) concl *t*)) nil t (sr-limit wrld)))) (append (make-built-in-clause-rules1 rune nume clauses wrld) (make-built-in-clause-rules rune nume (cdr lst) wrld)))))) (defun classify-and-store-built-in-clause-rules (lst pots wrld) ; Lst is a list of built-in-clause records. Each record contains an ; :all-fnnames field, which contains a (possibly empty) list of function ; symbols. The first symbol in the :all-fnnames list is the "discriminator ; function symbol" of the clause, the heaviest function symbol in the clause. ; Pots is an alist in which each entry pairs a symbol, fn, to a list of ; built-in-clause records; the list has the property that every clause in it ; has fn as its discriminator function symbol. We add each record in lst to ; the appropriate pot in pots. ; If a record has :all-fnnames nil then it is most likely a primitive built-in ; clause, i.e., a member of *initial-built-in-clauses*. The nil is a signal to ; this function to compute the appropriate :all-fnnames using the function ; all-fnnames-subsumer which is what we use when we build a built-in clause ; record for the user with make-built-in-clause-rules1. This is just a rugged ; way to let the list of implicit function symbols known to one-way-unify1 vary ; without invalidating our *initial-built-in-clauses* setting. ; But it is possible, perhaps, for a user-supplied built-in clause to contain ; no function symbols of the kind returned by all-fnnames-subsumer. For ; example, the user might prove 7 as a built-in clause. Perhaps a ; nonpathological example arises, but I haven't bothered to think of one. ; Instead, this is handled soundly, as follows. If the :all-fnnames is nil we ; act like it hasn't been computed yet (as above) and compute it. Then we ; consider the discriminator function symbol to the car of the resulting list, ; which might be nil. There is a special pot for the nil "discriminator ; function symbol". (cond ((null lst) pots) (t (let* ((bic (car lst)) (fns (or (access built-in-clause bic :all-fnnames) (all-fnnames-subsumer (access built-in-clause bic :clause) wrld))) (fn (car fns)) (pot (cdr (assoc-eq fn pots)))) (classify-and-store-built-in-clause-rules (cdr lst) (put-assoc-eq fn (cons (change built-in-clause bic :all-fnnames (cdr fns)) pot) pots) wrld))))) (defun add-built-in-clause-rule (rune nume term wrld) ; We strip the conjuncts out of term and flatten those in the hypotheses of ; implications to obtain a list of implications and then clausify each and ; store each clause as a :BUILT-IN-CLAUSE rule. We maintain the invariant ; that 'half-length-built-in-clauses is equal to the (floor n 2), where n ; is the length of 'built-in-clauses. (let ((rules (make-built-in-clause-rules rune nume (unprettyify term) wrld))) ; Every rule in rules is stored (somewhere) into built-in-clauses, so the ; number of clauses goes up by (length rules). Once we had a bug here: we ; incremented 'half-length-built-in-clauses by half the length of rules. That ; was pointless since we're dealing with integers here: rules is most often of ; length 1 and so we would increment by 0 and never accumulate all those 1/2's! (global-set 'half-length-built-in-clauses (floor (+ (length rules) (length (global-val 'built-in-clauses wrld))) 2) (global-set 'built-in-clauses (classify-and-store-built-in-clause-rules rules (global-val 'built-in-clauses wrld) wrld) wrld)))) ;--------------------------------------------------------------------------- ; Section: :COMPOUND-RECOGNIZER Rules (deflabel compound-recognizer :doc ":Doc-Section Rule-Classes make a rule used by the typing mechanism~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Examples: (defthm alistp-implies-true-listp-compound-recognizer (implies (alistp x) ; When (alistp x) is assumed true, add (true-listp x)) ; the additional hypothesis that x is :rule-classes :compound-recognizer) ; of primitive type true-listp.~/ (defthm natp-compound-recognizer ; See discussion below. (equal (natp x) (and (integerp x) (<= 0 x))) :rule-classes :compound-recognizer) ~ev[] Before presenting the General Forms, we start with a motivating example: the second ~ilc[defthm] form above, which provides a nice example of a ~c[:compound-recognizer] rule that is built into ACL2. To see how this rule might be useful, consider the following (admittedly very simple) ~il[events]. ~bv[] (defun triple (x) (* 3 x)) (defthm triple-preserves-integerp (implies (integerp x) (integerp (triple x)))) (in-theory (disable triple natp)) ~ev[] If the above ~c[:compound-recognizer] rule is disabled, then the following trivial theorem fails as shown; we explain below. ~bv[] (thm (implies (natp x) (integerp (triple x))) :hints ((\"Goal\" :in-theory (disable natp-compound-recognizer)))) ~ev[] The problem is that when ACL2 tries to rewrite the term ~c[(integerp (triple x))] using the ~c[:]~ilc[rewrite] rule ~c[triple-preserves-integerp], it needs to rewrite the hypothesis ~c[(integerp x)] to ~c[t], but instead what is known is ~c[(natp x)]. If we remove the hint, then the proof succeeds because the above ~c[:compound-recognizer] rule tells ACL2 that when assuming ~c[(natp x)] to be true, it should actually assume both ~c[(integerp x)] and ~c[(<= 0 x)] to be true. ~bv[] General Forms: (implies (fn x) concl) ; (1) (implies (not (fn x)) concl) ; (2) (and (implies (fn x) concl1) ; (3) (implies (not (fn x)) concl2)) (if (fn x) concl1 concl2) ; (4) (iff (fn x) concl) ; (5) (equal (fn x) concl) ; (6) ~ev[] where ~c[fn] is a Boolean valued function of one argument, ~c[x] is a variable symbol, and the system can deduce some restriction on the primitive type of ~c[x] from the assumption that ~c[concl] holds. The last restriction is vague but one way to understand it is to weaken it a little to ``and ~c[concl] is a non-tautological conjunction or disjunction of the primitive type recognizers listed below.'' The primitive ACL2 types and a suitable primitive recognizing expression for each are listed below. ~bv[] type suitable primitive recognizer zero (equal x 0) negative integers (and (integerp x) (< x 0)) positive integers (and (integerp x) (> x 0)) negative ratio (and (rationalp x) (not (integerp x)) (< x 0)) positive ratio (and (rationalp x) (not (integerp x)) (> x 0)) complex rational (complex-rationalp x) nil (equal x nil) t (equal x t) other symbols (and (symbolp x) (not (equal x nil)) (not (equal x t))) proper conses (and (consp x) (true-listp x)) improper conses (and (consp x) (not (true-listp x))) strings (stringp x) characters (characterp x) ~ev[] Thus, a suitable ~c[concl] to recognize the naturals would be ~c[(or (equal x 0) (and (integerp x) (> x 0)))] but it turns out that we also permit ~c[(and (integerp x) (>= x 0))]. Similarly, the true-lists could be specified by ~bv[] (or (equal x nil) (and (consp x) (true-listp x))) ~ev[] but we in fact allow ~c[(true-listp x)]. When time permits we will document more fully what is allowed or implement a macro that permits direct specification of the desired type in terms of the primitives. There are essentially four forms of ~c[:compound-recognizer] rules, as the forms labeled (3) and (4) above are equivalent, as are those labeled (5) and (6). We explain how such rules are used by considering the individual forms. Consider form (1), ~c[(implies (fn x) concl)]. The effect of such a rule is that when the rewriter assumes ~c[(fn x)] true, as it would while diving through ~c[(if (fn x) xxx ...)] to rewrite ~c[xxx], it restricts the type of ~c[x] as specified by ~c[concl]. For example, if ~c[concl] is the term ~c[(integerp x)], then when rewriting ~c[xxx], ~c[x] will be assumed to be an integer. However, when assuming ~c[(fn x)] false, as necessary in ~c[(if (fn x) ... xxx)], the rule permits no additional assumptions about the type of ~c[x]. For example, if ~c[fn] is ~c[primep], i.e., the predicate that recognizes prime numbers, then ~c[(implies (primep x) (and (integerp x) (>= x 0)))] is a compound recognizer rule of the first form. When ~c[(primep x)] is assumed true, the rewriter gains the additional information that ~c[x] is a natural number. When ~c[(primep x)] is assumed false, no additional information is gained ~-[] since ~c[x] may be a non-prime natural or may not even be a natural. Form (2) is the symmetric case, when assuming ~c[(fn x)] false permits type restrictions on ~c[x] but assuming ~c[(fn x)] true permits no such restrictions. For example, if we defined ~c[exprp] to be the recognizer for well-formed expressions for some language in which all symbols, numbers, character objects and strings were well-formed ~-[] e.g., the well-formedness rules only put restrictions on expressions represented by ~ilc[consp]s ~-[] then the theorem ~c[(implies (not (exprp x)) (consp x))] is a rule of the second form. Assuming ~c[(exprp x)] true tells us nothing about the type of ~c[x]; assuming it false tells us ~c[x] is a ~ilc[consp]. Forms (3) and (4), which are really equivalent, address themselves to the case where one type may be deduced from ~c[(fn x)] and a generally unrelated type may be deduced from its negation. If we modified the expression recognizer above so that character objects are illegal, then rules of the forms (3) and (4) are ~bv[] (and (implies (exprp x) (not (characterp x))) (implies (not (exprp x)) (or (consp x) (characterp x)))). (if (exprp x) (not (characterp x)) (or (consp x) (characterp x))) ~ev[] Finally, rules of forms (5) and (6) address the case where ~c[fn] recognizes all and only the objects whose type is described. In these cases, ~c[fn] is really just a new name for some ``compound recognizers.'' The classic example is ~c[(booleanp x)], which is just a handy combination of two primitive types: ~bv[] (iff (booleanp x) (or (equal x t) (equal x nil))). ~ev[] Often it is best to disable ~c[fn] after proving that it is a compound recognizer, since otherwise the term ~c[(fn x)] will be expanded and thus disappear. Every time you prove a new compound recognizer rule about ~c[fn] it overrides all previously proved compound recognizer rules about ~c[fn]. Thus, if you want to establish the type implied by ~c[(fn x)] and you want to establish the type implied by ~c[(not (fn x))], you must prove a compound recognizer rule of the third, fourth, fifth, or sixth forms. Proving a rule of the first form followed by one of the second only leaves the second fact in the database. Compound recognizer rules can be disabled with the effect that older rules about ~c[fn], if any, are exposed. If you prove more than one compound recognizer rule for a function, you may see a ~st[warning] message to the effect that the new rule is not as ``restrictive'' as the old. That is, the new rules do not give the rewriter strictly more type information than it already had. The new rule is stored anyway, overriding the old, if enabled. You may be playing subtle games with enabling or rewriting. But two other interpretations are more likely, we think. One is that you have forgotten about an earlier rule and should merely print it out to make sure it says what you intend, and then discard your new rule. The other is that you meant to give the system more information and the system has simply been unable to extract the intended type information from the term you placed in the conclusion of the new rule. Given our lack of specificity in saying how type information is extracted from rules, you can hardly blame yourself for this problem. Sorry. If you suspect you've been burned this way, you should rephrase the new rule in terms of the primitive recognizing expressions above and see if the warning is still given. It would also be helpful to let us see your example so we can consider it as we redesign this stuff. Compound recognizer rules are similar to ~c[:]~ilc[forward-chaining] rules in that the system deduces new information from the act of assuming something true or false. If a compound recognizer rule were stored as a forward chaining rule it would have essentially the same effect as described, when it has any effect at all. The important point is that ~c[:]~ilc[forward-chaining] rules, because of their more general and expensive form, are used ``at the top level'' of the simplification process: we forward chain from assumptions in the goal being proved. But compound recognizer rules are built in at the bottom-most level of the simplifier, where type reasoning is done. All that said, compound recognizer rules are a rather fancy, specialized mechanism. It may be more appropriate to create ~c[:]~ilc[forward-chaining] rules instead of ~c[:compound-recognizer] rules.") (defun destructure-compound-recognizer (term) ; If term is one of the forms of a compound recognizer lemma we return ; its parity (TRUE, FALSE, WEAK-BOTH or STRONG-BOTH), the recognizer ; fn, its variablep argument in this term, and the type description ; term. In the case of WEAK-BOTH the type description term is a pair ; -- not a term -- consisting of the true term and the false term. ; Otherwise we return four nils. (case-match term (('implies ('not (fn x)) concl) (cond ((and (variablep x) (symbolp fn)) (mv 'false fn x concl)) (t (mv nil nil nil nil)))) (('implies (fn x) concl) (cond ((and (variablep x) (symbolp fn)) (mv 'true fn x concl)) (t (mv nil nil nil nil)))) (('if ('implies (fn x) concl1) ('implies ('not (fn x)) concl2) ''nil) (cond ((and (variablep x) (symbolp fn)) (mv 'weak-both fn x (cons concl1 concl2))) (t (mv nil nil nil nil)))) (('if (fn x) concl1 concl2) (cond ((and (variablep x) (symbolp fn)) (mv 'weak-both fn x (cons concl1 concl2))) (t (mv nil nil nil nil)))) (('if ('implies ('not (fn x)) concl2) ('implies (fn x) concl1) ''nil) (cond ((and (variablep x) (symbolp fn)) (mv 'weak-both fn x (cons concl1 concl2))) (t (mv nil nil nil nil)))) (('if ('implies ('not (fn x)) concl2) ('implies (fn x) concl1) ''nil) (cond ((and (variablep x) (symbolp fn)) (mv 'weak-both fn x (cons concl1 concl2))) (t (mv nil nil nil nil)))) (('iff (fn x) concl) (cond ((and (variablep x) (symbolp fn)) (mv 'strong-both fn x concl)) (t (mv nil nil nil nil)))) (('equal (fn x) concl) (cond ((and (variablep x) (symbolp fn)) (mv 'strong-both fn x concl)) (t (mv nil nil nil nil)))) (& (mv nil nil nil nil)))) (defun make-recognizer-tuple (rune nume parity fn var term ens wrld) ; If parity is 'WEAK-BOTH then term is really (tterm . fterm). We ; create a recognizer-tuple from our arguments. Nume is stored in ; the :nume and may be nil. We return two results, the ; recognizer-tuple and the ttree justifying the type-set(s) in it. (case parity (true (mv-let (ts ttree) (type-set-implied-by-term var nil term ens wrld nil) (mv (make recognizer-tuple :rune rune :nume nume :fn fn :true-ts ts :false-ts *ts-unknown* :strongp nil) ttree))) (false (mv-let (ts ttree) (type-set-implied-by-term var nil term ens wrld nil) (mv (make recognizer-tuple :rune rune :nume nume :fn fn :true-ts *ts-unknown* :false-ts ts :strongp nil) ttree))) (weak-both (mv-let (tts ttree) (type-set-implied-by-term var nil (car term) ens wrld nil) (mv-let (fts ttree) (type-set-implied-by-term var nil (cdr term) ens wrld ttree) (mv (make recognizer-tuple :rune rune :nume nume :fn fn :true-ts tts :false-ts fts :strongp (ts= tts (ts-complement fts))) ttree)))) (otherwise ; Warning: We proved that (fn x) = term and one is tempted to build a ; :strongp = t rule. But since we do not guarantee that term is ; equivalent to the type-set we deduce from it, we cannot just get the ; type-set for term and complement it for the false branch. And we ; cannot guarantee to build a strong rule. Instead, we act more or ; less like we do for weak-both: we compute independent type sets from ; term and (not term) and just in the case that they are complementary ; do we build a strong rule. (mv-let (tts ttree) (type-set-implied-by-term var nil term ens wrld nil) (mv-let (fts ttree) (type-set-implied-by-term var t term ens wrld ttree) (mv (make recognizer-tuple :rune rune :nume nume :fn fn :true-ts tts :false-ts fts :strongp (ts= tts (ts-complement fts))) ttree)))))) (defun comment-on-new-recog-tuple1 (new-recog-tuple recognizer-alist ctx state) ; This function compares a newly proposed recognizer tuple to each of ; the tuples on the recognizer-alist, expecting that it will be more ; restrictive than each of the existing tuples with the same :fn. Let ; tts', fts', and strongp' be the obvious fields from the new tuple, ; and let tts, fts, and strongp be from an existing tuple. Let ts' <= ; ts here mean (ts-subsetp ts' ts) and let strongp' <= strongp be true ; if either strongp is nil or strongp' is t. Then we say the new ; tuple is ``more restrictive'' than the existing tuple iff ; (a) tts' <= tts & fts' <= fts & strongp' <= strongp, and ; (b) at least one of the three primed fields is different from its ; unprimed counterpart. ; For each old tuple that is at least as restrictive as the new tuple ; we print a warning. We never cause an error. However, we have ; coded the function and its caller so that if we someday choose to ; cause an error it will be properly handled. (Without more experience ; with compound recognizers we do not know what sort of checks would be ; most helpful.) (cond ((null recognizer-alist) (value nil)) ((eq (access recognizer-tuple new-recog-tuple :fn) (access recognizer-tuple (car recognizer-alist) :fn)) (cond ((and (ts-subsetp (access recognizer-tuple new-recog-tuple :true-ts) (access recognizer-tuple (car recognizer-alist) :true-ts)) (ts-subsetp (access recognizer-tuple new-recog-tuple :false-ts) (access recognizer-tuple (car recognizer-alist) :false-ts)) (or (access recognizer-tuple new-recog-tuple :strongp) (null (access recognizer-tuple (car recognizer-alist) :strongp))) (or (not (ts= (access recognizer-tuple new-recog-tuple :false-ts) (access recognizer-tuple (car recognizer-alist) :false-ts))) (not (ts= (access recognizer-tuple new-recog-tuple :true-ts) (access recognizer-tuple (car recognizer-alist) :true-ts))) (not (eq (access recognizer-tuple new-recog-tuple :strongp) (access recognizer-tuple (car recognizer-alist) :strongp))))) (comment-on-new-recog-tuple1 new-recog-tuple (cdr recognizer-alist) ctx state)) (t (pprogn (warning$ ctx ("Compound-rec") "The newly proposed compound recognizer rule ~x0 is not as ~ restrictive as the old rule ~x1. See :DOC ~ compound-recognizer." (base-symbol (access recognizer-tuple new-recog-tuple :rune)) (base-symbol (access recognizer-tuple (car recognizer-alist) :rune))) (comment-on-new-recog-tuple1 new-recog-tuple (cdr recognizer-alist) ctx state))))) (t (comment-on-new-recog-tuple1 new-recog-tuple (cdr recognizer-alist) ctx state)))) (defun comment-on-new-recog-tuple (new-recog-tuple ctx ens wrld state) ; This function prints out a warning advising the user of the type ; information to be extracted from a newly proposed compound ; recognizer. We also print out a description of the lemmas used to ; derive this information. We also compare the new recognizer tuple ; to any old tuples we have for the same function and print a suitable ; message should it be less ``restrictive.'' ; We never cause an error, but this function and its caller are coded ; so that if we someday choose to cause an error it will be properly ; handled. (Without more experience with compound recognizers we do ; not know what sort of checks would be most helpful.) (let ((pred (fcons-term* (access recognizer-tuple new-recog-tuple :fn) 'x))) (mv-let (tts-term ttree) (convert-type-set-to-term 'x (access recognizer-tuple new-recog-tuple :true-ts) ens wrld nil) (mv-let (fts-term ttree) (convert-type-set-to-term 'x (access recognizer-tuple new-recog-tuple :false-ts) ens wrld ttree) (let ((tts-term (untranslate tts-term t wrld)) (fts-term (untranslate fts-term t wrld))) (er-progn (if (and (ts= (access recognizer-tuple new-recog-tuple :true-ts) *ts-unknown*) (ts= (access recognizer-tuple new-recog-tuple :false-ts) *ts-unknown*)) (er soft ctx "When ~x0 is assumed true, ~x1 will allow us to deduce ~ nothing about the type of X. Also, when ~x0 is assumed ~ false, ~x1 will allow us to deduce nothing about the type of ~ X. Thus this is not a legal compound recognizer rule. See ~ doc :compound-recognizer if these observations surprise you." pred (base-symbol (access recognizer-tuple new-recog-tuple :rune))) (value nil)) (pprogn (observation ctx "When ~x0 is assumed true, ~x1 will allow us to deduce ~#2~[nothing ~ about the type of X.~/~p3.~] When ~x0 is assumed false, ~x1 will ~ allow us to deduce ~#4~[nothing about the type of X.~/~p5.~] Note ~ that ~x0 is~#6~[ not~/~] a strong compound recognizer, according ~ to this rule. See doc :compound-recognizer if these observations ~ surprise you. These particular expressions of the type ~ information are based on ~*7." pred (base-symbol (access recognizer-tuple new-recog-tuple :rune)) (if (eq tts-term t) 0 1) tts-term (if (eq fts-term t) 0 1) fts-term (if (access recognizer-tuple new-recog-tuple :strongp) 1 0) (tilde-*-simp-phrase ttree)) (if (warning-disabled-p "Compound-rec") (value nil) (comment-on-new-recog-tuple1 new-recog-tuple (global-val 'recognizer-alist wrld) ctx state))))))))) (defun chk-acceptable-compound-recognizer-rule (name term ctx ens wrld state) ; If we don't cause an error, we return an 'assumption-free ttree that ; justifies the type information extracted from term. (mv-let (parity fn var term1) (destructure-compound-recognizer term) (cond ((null parity) (er soft ctx "No :COMPOUND-RECOGNIZER rule can be generated from ~x0 ~ because it does not have the form described in :DOC ~ compound-recognizer." name)) (t (mv-let (ts ttree1) (type-set (mcons-term* fn var) nil nil nil ens wrld nil nil nil) (cond ((not (ts-subsetp ts *ts-boolean*)) ; To loosen the Boolean restriction, we must change assume-true-false ; so that when a compound recognizer is assumed true its type-set is ; not just set to *ts-t*. A comment at the defrec for ; recognizer-tuple also says that fn must be Boolean. It would be a ; good idea, before changing this, to inspect all code involved with ; recognizer-tuples. (er soft ctx "A function can be treated as a :COMPOUND-RECOGNIZER only ~ if it is Boolean valued. ~x0 is not known to be Boolean. ~ See :DOC compound-recognizer." fn)) (t ; Historical Note: We used to combine the new type information with ; the old. We do not do that anymore: we store exactly what the new ; rule tells us. The reason is so that we can maintain a 1:1 ; relationship between what we store and rule names, so that it is ; meaningful to disable a compound recognizer rule. (mv-let (recog-tuple ttree2) ; Note: Below we counterfeit a rune based on name, simply so that the ; recog-tuple we get back really looks like one. The actual rule ; created for term1 will have a different name (x will be specified). ; This tuple is only used for error reporting and we dig name out of ; its rune then. (make-recognizer-tuple `(:COMPOUND-RECOGNIZER ,name . x) nil parity fn var term1 ens wrld) (er-progn (comment-on-new-recog-tuple recog-tuple ctx ens wrld state) (value (cons-tag-trees ttree1 ttree2))))))))))) ; And to add :COMPOUND-RECOGNIZER rules... (defun add-compound-recognizer-rule (rune nume term ens wrld) ; We construct the recongizer-tuple corresponding to term and just add ; it onto the front of the current recognizer-alist. We used to merge ; it into the existing tuple for the same :fn, if one existed, but ; that makes disabling these rules complicated. When we retrieve ; tuples from the alist we look for the first enabled tuple about the ; :fn in question. So it is necessary to leave old tuples for :fn in ; place. (mv-let (parity fn var term1) (destructure-compound-recognizer term) (mv-let (recog-tuple ttree) (make-recognizer-tuple rune nume parity fn var term1 ens wrld) (declare (ignore ttree)) (global-set 'recognizer-alist (cons recog-tuple (global-val 'recognizer-alist wrld)) wrld)))) ;--------------------------------------------------------------------------- ; Section: :FORWARD-CHAINING Rules (deflabel forward-chaining :doc ":Doc-Section Rule-Classes make a rule to forward chain when a certain trigger arises~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Examples: (defthm p-and-r-forward ; When (p a) appears in a formula to be (implies (and (p x) (r x)) ; simplified, try to establish (p a) and (q (f x))) ; (r a) and, if successful, add (q (f a)) :rule-classes :forward-chaining) ; to the known assumptions. (defthm p-and-r-forward ; as above with most defaults filled in (implies (and (p x) (r x)) (q (f x))) :rule-classes ((:forward-chaining :trigger-terms ((p x)) :corollary (implies (and (p x) (r x)) (q (f x))) :match-free :all))) ~ev[] To specify the triggering terms provide a non-empty list of terms as the value of the ~c[:trigger-terms] field of the rule class object.~/ ~bv[] General Form: Any theorem, provided an acceptable triggering term exists. ~ev[] The structure of this documentation is as follows. First we give a brief overview of forward chaining and contrast it to backchaining (rewriting). Then we lay out the syntactic restrictions on ~c[:forward-chaining] rules. Then we give more details about the process and point to a tool to assist you in debugging your ~c[:forward-chaining] rules. ~em[Overview and When to Use Forward Chaining] Forward chaining is performed as part of the simplification process: before the goal is rewritten a ~em[context] is established. The context tells the theorem prover what may be assumed during rewriting, in particular, to establish hypotheses of rewrite rules. Forward chaining is used to extend the context before rewriting begins. For example, the ~c[:forward-chaining] rule ~c[(implies (p x) (p1 x))] would add ~c[(p1 A)] to the context, where ~c[A] is some term, if ~c[(p A)] is already in the context. Forward chaining and backchaining are duals. If a rewrite rule requires that ~c[(p1 A)] be established and ~c[(p A)] is known, it could be done either by making ~c[(implies (p x) (p1 x))] a ~c[:forward-chaining] rule or a ~c[:rewrite] rule. Which should you choose? As a rule of thumb, if a conclusion like ~c[(p1 A)] is expected to be widely needed, it is better to derive it via forward chaining because then it is available ``for free'' during the rewriting after paying the one-time cost of forward chaining. Alternatively, if ~c[(p1 A)] is a rather special hypothesis of key importance to only a few rewrite rules, it is best to derive it only when needed. Thus forward chaining is pro-active and backward chaining (rewriting) is reactive. ~em[Syntactic Restrictions] Forward chaining rules are generated from the corollary term (~pl[rule-classes]) as follows. First, every ~ilc[let] expression is expanded away (hence, so is every ~ilc[let*] and ~ilc[lambda] expression), as is every call of a so-called ``guard holder,'' ~ilc[mv-list] or ~ilc[return-last] (the latter resulting from macroexpansion of calls of ~ilc[prog2$], ~ilc[must-be-equal] or ~ilc[mbe]), ~ilc[ec-call], and a few others), or `~ilc[the]'. If the resulting term has the form ~c[(implies hyp concl)], then ~c[concl] is treated as a conjunction, with one forward chaining rule with hypothesis ~c[hyp] created for each conjunct. In the other case, where the corollary term is not an ~ilc[implies], we process it as we process the conclusion in the first case. Note that unlike rewrite rules, a nested implication is not folded into a single implication. Consider for example the following term. ~bv[] (implies (p1 x) (implies (p2 x) (p3 x))) ~ev[] Although this term is parsed for a rewrite rule as ~c[(implies (and (p1 x) (p2 x)) (p3 x))], that is not the case when this term is parsed for a forward-chaining rule, in which case ~c[(p1 x)] is treated as the hypothesis and ~c[(implies (p2 x) (p3 x))] is treated as the conclusion. The ~c[:trigger-terms] field of a ~c[:forward-chaining] rule class object should be a non-empty list of terms, if provided, and should have certain properties described below. If the ~c[:trigger-terms] field is not provided, it defaults to the singleton list containing the ``atom'' of the first hypothesis of the formula. (The atom of ~c[(not x)] is ~c[x]; the atom of any other term is the term itself.) If there are no hypotheses and no ~c[:trigger-terms] were provided, an error is caused. A triggering term is acceptable if it is not a variable, a quoted constant, a lambda application, a ~ilc[let]- (or ~ilc[let*]-) expression, or a ~ilc[not]-expression, and every variable symbol in the conclusion of the theorem either occurs in the hypotheses or occurs in the trigger. ~em[More Details about Forward Chaining] ~c[:Forward-chaining] rules are used by the simplifier ~em[before] it begins to rewrite the literals of the goal. (Forward chaining is thus carried out from scratch for each goal.) If any term in the goal is an instance of a trigger of some forward chaining rule, we try to establish the hypotheses of that forward chaining theorem (from the negation of the goal). To relieve a hypothesis we only use type reasoning, evaluation of ground terms, and presence among our known assumptions. We do not use rewriting. So-called free variables in hypotheses are treated specially; ~pl[free-variables]. If all hypotheses are relieved, and certain heuristics approve of the newly derived conclusion, we add the instantiated conclusion to our known assumptions. Since this might introduce new terms into the assumptions, forward chaining is repeated. Heuristic approval of each new addition is necessary to avoid infinite looping as would happen with the rule ~c[(implies (p x) (p (f x)))], which might otherwise forward chain from ~c[(p A)] to ~c[(p (f A))] to ~c[(p (f (f A)))], etc. ~em[Caution]. Forward chaining does not actually add terms to the goals displayed during proof attempts. Instead, it extends an associated ~em[context], called ``assumptions'' in the preceding paragraph, that ACL2 builds from the goal currently being proved. (For insiders: forward chaining extends the ~c[type-alist].) The context starts out with ``obvious'' consequences of the negation of the goal. For example, if the goal is ~bv[] (implies (and (p A) (q (f A))) (c A)) ~ev[] then the context notes that ~c[(p A)] and ~c[(q (f A))] are non-~c[nil] and ~c[(c A)] is ~c[nil]. Forward chaining is then used to expand the context. For example, if a forward chaining rule has ~c[(f x)] as a trigger term and has body ~c[(implies (p x) (r (f x)))], then the context is extended by binding ~c[(r (f A))] to non-~c[nil], provided the heuristics approve of this extension. Note however that since ~c[(r (f A))] is put into the context, not the goal, you will not see it in the goal formula. Furthermore, the assumption added to the context is just the instantiation of the conclusion of the rule, with no simplification or rewriting applied. Thus, for example, if it contains an enabled non-recursive function symbol it is unlikely ever to match a (rewritten) term arising during subsequent simplification of the goal. However, forward-chaining does support the linear arithmetic reasoning package. For example, suppose that forward-chaining puts ~c[(< (f x) (g x))] into the context. Then this inequality also goes into the linear arithmetic database, together with suitable instances of linear lemmas whose trigger term is a call of ~c[g]. ~l[linear]. Debugging ~c[:forward-chaining] rules can be difficult since their effects are not directly visible on the goal being simplified. Tools are available to help you discover what forward chaining has occurred ~pl[forward-chaining-reports].") (defun chk-triggers (name match-free hyps terms hyps-vars concls-vars ctx ens wrld state) ; Name is the name of a proposed forward chaining rule with hyps hyps ; and triggers terms. We verify that every trigger is a non-variable, ; non-quote, non-lambda, non-NOT application. We also print the ; free-variable warning messages. (cond ((null terms) (value nil)) ((or (variablep (car terms)) (fquotep (car terms)) (flambda-applicationp (car terms)) (eq (ffn-symb (car terms)) 'not)) (er soft ctx "It is illegal to use a variable, a quoted constant, the ~ application of a lambda-expression, a LET-expression, ~ or a NOT-expression as the trigger of a forward ~ chaining rule. Your proposed trigger, ~x0, violates ~ these restrictions. See :DOC forward-chaining." (car terms))) ((not (subsetp-eq concls-vars (all-vars1 (car terms) hyps-vars))) (er soft ctx "We cannot use ~x0 as a forward chaining rule triggered ~ by ~x1 because the variable~#2~[ ~&2 is~/s ~&2 are~] ~ used in the conclusion but not in the ~#3~[~/hypothesis ~ or the ~/hypotheses or the ~]trigger. See :DOC ~ forward-chaining." name (car terms) (set-difference-eq concls-vars (all-vars1 (car terms) hyps-vars)) (zero-one-or-more hyps))) (t (let* ((warn-non-rec (not (warning-disabled-p "Non-rec"))) (free-vars (free-vars-in-hyps hyps (all-vars (car terms)) wrld)) (inst-hyps (hyps-that-instantiate-free-vars free-vars hyps)) (forced-hyps (forced-hyps inst-hyps)) (non-rec-fns (and warn-non-rec (non-recursive-fnnames (car terms) ens wrld))) (non-rec-fns-inst-hyps (and warn-non-rec (non-recursive-fnnames-lst (strip-top-level-nots-and-forces inst-hyps) ens wrld)))) (er-progn (cond ((and free-vars (null match-free)) (pprogn (warning$ ctx "Free" "When the :FORWARD-CHAINING rule generated from ~x0 ~ is triggered by ~x1 it contains the free ~ variable~#2~[ ~&2. This variable~/s ~&2. These ~ variables~] will be chosen by searching for ~#3~[an ~ instance~/instances~] of ~&3 among the hypotheses of ~ the conjecture being rewritten. This is generally a ~ severe restriction on the applicability of the ~ forward chaining rule." name (car terms) free-vars inst-hyps) (free-variable-error? :forward-chaining name ctx wrld state))) (t (value nil))) (pprogn (cond ((and free-vars forced-hyps) (warning$ ctx "Free" "Forward chaining rule ~x0 has forced (or ~ case-split) ~#1~[hypothesis~/hypotheses~], ~*2, ~ which will be used to instantiate one or more free ~ variables. We will search for suitable ~ instantiations (of the term inside the FORCE or ~ CASE-SPLIT) among the known assumptions in the ~ context at the time we encounter ~#1~[the~/each~] ~ hypothesis. If no instances are found, we will ~ force or case split on the partially instantiated ~ ~#1~[hypothesis~/hypotheses~] instead of waiting ~ for future rounds of forward chaining which might ~ derive appropriate instances. Note that this will ~ introduce a ``free variable'' into the conjecture. ~ While sound, this will establish a goal almost ~ certain to fail since the restriction described by ~ this apparently necessary hypothesis constrains a ~ variable not involved in the problem. To highlight ~ this oddity, we will rename the free variables in ~ such forced hypotheses by prefixing them with ~ ``UNBOUND-FREE-''. This is not guaranteed to ~ generate a new variable but at least it generates ~ an unusual one. If you see such a variable in a ~ subsequent proof (and did not introduce them ~ yourself) you should consider the possibility that ~ the free variables of this forward chaining rule ~ were forced into the conjecture." name (if (null (cdr forced-hyps)) 0 1) (tilde-*-untranslate-lst-phrase forced-hyps t wrld))) (t state)) (cond (non-rec-fns (warning$ ctx ("Non-rec") "The term ~x0 contains the non-recursive function ~ symbol~#1~[ ~&1. Unless this function is~/s ~&1. ~ Unless these functions are~] disabled, ~x0 is ~ unlikely ever to occur as a trigger for ~x2." (car terms) (hide-lambdas non-rec-fns) name)) (t state)) (cond (non-rec-fns-inst-hyps (warning$ ctx ("Non-rec") "As noted, when triggered by ~x0, we will instantiate ~ the free variable~#1~[~/s~], ~&1, of the rule ~x2, ~ by searching for the ~#3~[hypothesis~/set of ~ hypotheses~] shown above. However, ~#3~[this ~ hypothesis mentions~/these hypotheses mention~] the ~ function symbol~#4~[ ~&4, which is~/s ~&4, which ~ are~] defun'd non-recursively. Unless disabled, ~ ~#4~[this function symbol is~/these function symbols ~ are~] unlikely to occur in the conjecture being ~ proved and hence the search for the required ~ ~#3~[hypothesis~/hypotheses~] will likely fail." (car terms) free-vars name inst-hyps (hide-lambdas non-rec-fns-inst-hyps))) (t state)) (chk-triggers match-free name hyps (cdr terms) hyps-vars concls-vars ctx ens wrld state))))))) (defun destructure-forward-chaining-term (term) ; We return two lists, hyps and concls, such that term is equivalent to ; (implies (and . hyps) (and . concls)). ; We have considered treating (IMPLIES a (IMPLIES b c)) as (IMPLIES (and a b) ; c) when we parse :forward-chaining rules. At the moment we do not, and hence ; such a :forward-chaing rule might put (IMPLIES b c) on the type-alist. The ; code for the ``improved'' parsing is in the comment just below. This would ; bring the parsing of :forward-chaining rules more into line with what we do ; for :rewrite rules. But an email from Dave Greve gave us the impression that ; he and others might intentionally put calls of IMPLIES on the type-alist. ; This is in the spirit of ``just do what the user said.'' We never ran a ; regression with the ``improved'' parsing so we don't know what effect it ; might have. But we decided to stick with the ``just do what the user said'' ; approach. ; (let ((term (remove-lambdas (remove-guard-holders term)))) ; (cond ((or (variablep term) ; (fquotep term) ; (not (eq (ffn-symb term) 'implies))) ; (mv nil (flatten-ands-in-lit term))) ; (t ; ; ; Term is of the form (implies arg1 arg2). We recursively ; ; destructure arg2 first, in case it is another (implies ...). ; ; (mv-let (hyps concls) ; (destructure-forward-chaining-term (fargn term 2)) ; (mv (append (flatten-ands-in-lit (fargn term 1)) ; hyps) ; concls))))) (let ((term (remove-lambdas (remove-guard-holders term)))) (cond ((or (variablep term) (fquotep term) (not (eq (ffn-symb term) 'implies))) (mv nil (flatten-ands-in-lit term))) (t (mv (flatten-ands-in-lit (fargn term 1)) (flatten-ands-in-lit (fargn term 2))))))) (defun chk-acceptable-forward-chaining-rule (name match-free trigger-terms term ctx ens wrld state) ; Acceptable forward chaining rules are of the form ; (IMPLIES (AND . hyps) ; (AND . concls)) ; We used to split term up with unprettyify as is done for REWRITE ; class rules. But that meant that we had to establish hyps ; once for each concl whenever the rule was triggered. (mv-let (hyps concls) (destructure-forward-chaining-term term) (let ((hyps-vars (all-vars1-lst hyps nil)) (concls-vars (all-vars1-lst concls nil))) (chk-triggers name match-free hyps trigger-terms hyps-vars concls-vars ctx ens wrld state)))) (defun putprop-forward-chaining-rules-lst (rune nume triggers hyps concls match-free wrld) (cond ((null triggers) (put-match-free-value match-free rune wrld)) (t (putprop-forward-chaining-rules-lst rune nume (cdr triggers) hyps concls match-free (putprop (ffn-symb (car triggers)) 'forward-chaining-rules (cons (make forward-chaining-rule :rune rune :nume nume :trigger (car triggers) :hyps hyps :concls concls :match-free match-free) (getprop (ffn-symb (car triggers)) 'forward-chaining-rules nil 'current-acl2-world wrld)) wrld))))) (defun add-forward-chaining-rule (rune nume trigger-terms term match-free wrld) (mv-let (hyps concls) (destructure-forward-chaining-term term) (putprop-forward-chaining-rules-lst rune nume trigger-terms hyps concls (match-free-fc-value match-free hyps concls trigger-terms wrld) wrld))) ;--------------------------------------------------------------------------- ; Section: :META Rules (deflabel meta :doc ":Doc-Section Rule-Classes make a ~c[:meta] rule (a hand-written simplifier)~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. Meta rules extend the ACL2 simplifier with hand-written code to transform certain terms to equivalent ones. To add a meta rule, the ~c[:]~ilc[corollary] formula must establish that the hand-written ``metafunction'' preserves the meaning of the transformed term. ~bv[] Examples: (defthm fn-correct-1 ; Modify the rewriter to use fn to (equal (evl x a) ; transform terms that are calls of (evl (fn x) a)) ; nth or of foo. :rule-classes ((:meta :trigger-fns (nth foo)))) (defthm fn-correct-2 ; As above, but this illustrates (implies (and (pseudo-termp x) ; that without loss of generality we (alistp a)) ; may restrict x to be shaped like a (equal (evl x a) ; term and a to be an alist. (evl (fn x) a))) :rule-classes ((:meta :trigger-fns (nth foo)))) (defthm fn-correct-3 ; As above (with or without the (implies (and (pseudo-termp x) ; hypotheses on x and a), with the (alistp a) ; additional restriction that the (evl (hyp-fn x) a)) ; meaning of (hyp-fn x) is true in (equal (evl x a) ; the current context. That is, the (evl (fn x) a))) ; applicability of the transformation :rule-classes ; may be dependent upon some computed ((:meta :trigger-fns (nth foo)))) ; hypotheses. ~ev[] While our intention is that the set of ACL2 documentation topics is self-contained, readers might find it useful to see the following paper for an introduction to meta reasoning in ACL2. ~bq[] W. A. Hunt, Jr., R. B. Krug, M. Kaufmann, J S. Moore and E. W. Smith, ``Meta Reasoning in ACL2.'' TPHOLs 2005, ed. J. Hurd and T. F. Melham, LNCS 3603, Springer-Verlag, Berlin, 2005, pp. 163-178.~eq[] A non-~c[nil] list of function symbols must be supplied as the value of the ~c[:trigger-fns] field in a ~c[:meta] rule class object (except that a macro alias can stand in for a function symbol; ~pl[add-macro-alias]).~/ ~bv[] General Forms: (implies (and (pseudo-termp x) ; this hyp is optional (alistp a) ; this hyp is optional (ev (hyp-fn x ...) a) ; this hyp is optional ; meta-extract hyps may also be included (see below) ) (equiv (ev x a) (ev (fn x ...) a))) ~ev[] where ~c[equiv] is a known ~il[equivalence] relation, ~c[x] and ~c[a] are distinct variable names, and ~c[ev] is an evaluator function (see below), and ~c[fn] is a function symbol, as is ~c[hyp-fn] when provided. The arguments to ~c[fn] and ~c[hyp-fn] should be identical. In the most common case, both take a single argument, ~c[x], which denotes the term to be simplified. If ~c[fn] and/or ~c[hyp-fn] are ~il[guard]ed, their ~il[guard]s should be trivially implied by ~ilc[pseudo-termp]. We say the theorem above is a ``metatheorem'' or ``metalemma'' and ~c[fn] is a ``metafunction'', and ~c[hyp-fn] is a ``hypothesis metafunction''. If ``~c[...]'' is empty, i.e., the metafunctions take just one argument, we say they are ``vanilla flavored.'' If ``~c[...]'' is non-empty, we say the metafunctions are ``extended.'' Extended metafunctions can access ~ilc[state] and context sensitive information to compute their results, within certain limits. We discuss vanilla metafunctions here and recommend a thorough understanding of them before proceeding (at which time ~pl[extended-metafunctions]). Additional hypotheses are supported, called ``meta-extract hypotheses''. These allow metafunctions to depend on the validity of certain terms extracted from the context or the logical ~il[world]. These hypotheses provide a relatively advanced form of metatheorem so we explain them elsewhere; ~pl[meta-extract]. One might think that metafunctions and (if supplied) hypothesis metafunctions must be executable: that is, not constrained (i.e., introduced in the ~il[signature] of ~ilc[encapsulate] ~il[events]), and not ~il[declare]d ~c[:]~ilc[non-executable]. After all, there is no point in installing a simplifier that cannot be run! However, such a restriction is not enforced, because one could introduce a metafunction using ~ilc[encapsulate] and then use ~ilc[defattach] to attach it to an executable function; ~pl[defattach]. We defer discussion of the case in which there is a hypothesis metafunction and for now address the case in which the other two hypotheses are present. In the discussion below, we refer to the argument, ~c[x], of ~c[fn] and ~c[hyp-fn] as a ``term.'' When these metafunctions are executed by the simplifier, they will be applied to (the quotations of) terms. But during the proof of the metatheorem itself, ~c[x] may not be the quotation of a term. If the ~ilc[pseudo-termp] hypothesis is omitted, ~c[x] may be any object. Even with the ~ilc[pseudo-termp] hypothesis, ~c[x] may merely ``look like a term'' but use non-function symbols or function symbols of incorrect arity. In any case, the metatheorem is stronger than necessary to allow us to apply the metafunctions to terms, as we do in the discussion below. We return later to the question of proving the metatheorem. Suppose the general form of the metatheorem above is proved with the ~ilc[pseudo-termp] and ~ilc[alistp] hypotheses. Then when the simplifier encounters a term, ~c[(h t1 ... tn)], that begins with a function symbol, ~c[h], listed in ~c[:trigger-fns], it applies the metafunction, ~c[fn], to the quotation of the term, i.e., it evaluates ~c[(fn '(h t1 ... tn))] to obtain some result, which can be written as ~c['val]. If ~c['val] is different from ~c['(h t1 ... tn)] and ~c[val] is a term, then ~c[(h t1 ... tn)] is replaced by ~c[val], which is then passed along for further rewriting. Because the metatheorem establishes the correctness of ~c[fn] for all terms (even non-terms!), there is no restriction on which function symbols are listed in the ~c[:trigger-fns]. Generally, of course, they should be the symbols that head up the terms simplified by the metafunction ~c[fn]. ~l[term-table] for how one obtains some assistance towards guaranteeing that ~c[val] is indeed a term. The ``evaluator'' function, ~c[ev], is a function that can evaluate a certain class of expressions, namely, all of those composed of variables, constants, and applications of a fixed, finite set of function symbols, ~c[g1], ..., ~c[gk]. Generally speaking, the set of function symbols handled by ~c[ev] is chosen to be exactly the function symbols recognized and manipulated by the metafunctions being introduced. For example, if ~c[fn] manipulates expressions in which ~c[']~ilc[equal] and ~c[']~ilc[binary-append] occur as function symbols, then ~c[ev] is generally specified to handle ~ilc[equal] and ~ilc[binary-append]. The actual requirements on ~c[ev] become clear when the metatheorem is proved. The standard way to introduce an evaluator is to use the ACL2 macro ~ilc[defevaluator], though this is not strictly necessary. ~l[defevaluator] if you want details. [Aside for the logic-minded.] Why are we justified in using metafunctions this way? Suppose ~c[(fn 'term1)] is ~c['term2]. What justifies replacing ~c[term1] by ~c[term2]? The first step is to assert that ~c[term1] is ~c[(ev 'term1 a)], where ~c[a] is an alist that maps ~c['var] to ~c[var], for each variable ~c[var] in ~c[term1]. This step is incorrect, because ~c['term1] may contain function symbols other than the ones, ~c[g1], ..., ~c[gk], that ~c[ev] knows how to handle. But we can grow ~c[ev] to a ``larger'' evaluator, ~c[ev*], an evaluator for all of the symbols that occur in ~c[term1] or ~c[term2]. We can prove that ~c[ev*] satisfies the ~il[constraint]s on ~c[ev], provided no ~ilc[defaxiom] events are adding constraints to ~c[ev] (or callers of ~c[ev], and recursively); ACL2 checks this additional property. Hence, the metatheorem holds for ~c[ev*] in place of ~c[ev], by functional instantiation. We can then carry out the proof of the ~il[equivalence] of ~c[term1] and ~c[term2] as follows: Fix ~c[a] to be an alist that maps the quotations of the variables of ~c[term1] and ~c[term2] to themselves. Then, ~bv[] term1 = (ev* 'term1 a) ; (1) by construction of ev* and a = (ev* (fn 'term1) a) ; (2) by the metatheorem for ev* = (ev* 'term2 a) ; (3) by evaluation of fn = term2 ; (4) by construction of ev* and a ~ev[] Note that in line (2) above, where we appeal to the (functional instantiation of the) metatheorem, we can relieve its (optional) ~ilc[pseudo-termp] and ~ilc[alistp] hypotheses by appealing to the facts that ~c[term1] is a term and ~c[a] is an alist by construction. [End of Aside for the logic-minded.] There are subtleties related to the notion of ``growing'' ~c[ev] to a ``larger'' evaluator, as mentioned in the paragraph just above. For corresponding restrictions on ~c[:meta] rules, ~pl[evaluator-restrictions]. Finally, we turn to the second case, in which there is a hypothesis metafunction. In that case, consider as before what happens when the simplifier encounters a term, ~c[(h t1 ... tn)], where ~c[h] is listed in ~c[:trigger-fns]. This time, after it applies ~c[fn] to ~c['(h t1 ... tn)] to obtain the quotation of some new term, ~c['val], it then applies the hypothesis metafunction, ~c[hyp-fn]. That is, it evaluates ~c[(hyp-fn '(h t1 ... tn))] to obtain some result, which can be written as ~c['hyp-val]. If ~c[hyp-val] is not in fact a term, the metafunction is not used. Provided ~c[hyp-val] is a term, the simplifier attempts to establish (by conventional backchaining) that this term is non-~c[nil] in the current context. If this attempt fails, then the meta rule is not applied. Otherwise, ~c[(h t1...tn)] is replaced by ~c[val] as in the previous case (where there was no hypothesis metafunction). Why is it justified to make this extension to the case of hypothesis metafunctions? First, note that the rule ~bv[] (implies (and (pseudo-termp x) (alistp a) (ev (hyp-fn x) a)) (equal (ev x a) (ev (fn x) a))) ~ev[] is logically equivalent to the rule ~bv[] (implies (and (pseudo-termp x) (alistp a)) (equal (ev x a) (ev (new-fn x) a))) ~ev[] where ~c[(new-fn x)] is defined to be ~c[(list 'if (hyp-fn x) (fn x) x)]. (If we're careful, we realize that this argument depends on making an extension of ~c[ev] to an evaluator ~c[ev*] that handles ~ilc[if] and the functions manipulated by ~c[hyp-fn].) If we write ~c['term] for the quotation of the present term, and if ~c[(hyp-fn 'term)] and ~c[(fn 'term)] are both terms, say ~c[hyp1] and ~c[term1], then by the previous argument we know it is sound to rewrite term to ~c[(if hyp1 term1 term)]. But since we have established in the current context that ~c[hyp1] is non-~c[nil], we may simplify ~c[(if hyp1 term1 term)] to ~c[term1], as desired. We now discuss the role of the ~ilc[pseudo-termp] hypothesis. ~c[(Pseudo-termp x)] checks that ~c[x] has the shape of a term. Roughly speaking, it ensures that ~c[x] is a symbol, a quoted constant, or a true list consisting of a ~c[lambda] expression or symbol followed by some pseudo-terms. Among the properties of terms not checked by ~ilc[pseudo-termp] are that variable symbols never begin with ampersand, ~c[lambda] expressions are closed, and function symbols are applied to the correct number of arguments. ~l[pseudo-termp]. There are two possible roles for ~ilc[pseudo-termp] in the development of a metatheorem: it may be used as the ~il[guard] of the metafunction and/or hypothesis metafunction and it may be used as a hypothesis of the metatheorem. Generally speaking, the ~ilc[pseudo-termp] hypothesis is included in a metatheorem only if it makes it easier to prove. The choice is yours. (An extreme example of this is when the metatheorem is invalid without the hypothesis!) We therefore address ourselves the question: should a metafunction have a ~ilc[pseudo-termp] ~il[guard]? A ~ilc[pseudo-termp] ~il[guard] for a metafunction, in connection with other considerations described below, improves the efficiency with which the metafunction is used by the simplifier. To make a metafunction maximally efficient you should (a) provide it with a ~ilc[pseudo-termp] ~il[guard] and exploit the ~il[guard] when possible in coding the body of the function (~pl[guards-and-evaluation], especially the section on efficiency issues), (b) verify the ~il[guard]s of the metafunction (~pl[verify-guards]), and (c) compile the metafunction (~pl[comp]). When these three steps have been taken the simplifier can evaluate ~c[(fn 'term1)] by running the compiled ``primary code'' (~pl[guards-and-evaluation]) for ~c[fn] directly in Common Lisp. (Note however that explicit compilation may be suppressed; ~pl[compilation].) Before discussing efficiency issues further, let us review for a moment the general case in which we wish to evaluate ~c[(fn 'obj)] for some ~c[:]~ilc[logic] function. We must first ask whether the ~il[guard]s of ~c[fn] have been verified. If not, we must evaluate ~c[fn] by executing its logic definition. This effectively checks the ~il[guard]s of every subroutine and so can be slow. If, on the other hand, the ~il[guard]s of ~c[fn] have been verified, then we can run the primary code for ~c[fn], provided ~c['obj] satisfies the ~il[guard] of ~c[fn]. So we must next evaluate the ~il[guard] of ~c[fn] on ~c['obj]. If the ~il[guard] is met, then we run the primary code for ~c[fn], otherwise we run the logic code. Now in the case of a metafunction for which the three steps above have been followed, we know the ~il[guard] is (implied by) ~ilc[pseudo-termp] and that it has been verified. Furthermore, we know without checking that the ~il[guard] is met (because ~c[term1] is a term and hence ~c['term1] is a ~ilc[pseudo-termp]). Hence, we can use the compiled primary code directly. We strongly recommend that you compile your metafunctions, as well as all their subroutines (unless explicit compilation is suppressed; ~pl[compilation]). Guard verification is also recommended. Finally, we present a very simple example of the use of ~c[:meta] rules, based on one provided by Robert Krug. This example illustrates a trick for avoiding undesired rewriting after applying a metafunction or any other form of rewriting. To elaborate: in general, the term ~c[t2] obtained by applying a metafunction to a term ~c[t1] is then handed immediately to the rewriter, which descends recursively through the arguments of function calls to rewrite ~c[t2] completely. But if ~c[t2] shares a lot of structure with ~c[t1], then it might not be worthwhile to rewrite ~c[t2] immediately. (A rewrite of ~c[t2] will occur anyhow the next time a goal is generated.) The trick involves avoiding this rewrite by wrapping ~c[t2] inside a call of ~ilc[hide], which in turn is inside a call of a user-defined ``unhiding'' function, ~c[unhide]. ~bv[] (defun unhide (x) (declare (xargs :guard t)) x) (defthm unhide-hide (equal (unhide (hide x)) x) :hints ((\"Goal\" :expand ((hide x))))) (in-theory (disable unhide)) (defun my-plus (x y) (+ x y)) (in-theory (disable my-plus)) (defevaluator evl evl-list ((my-plus x y) (binary-+ x y) (unhide x) (hide x))) (defun meta-fn (term) (declare (xargs :guard (pseudo-termp term))) (if (and (consp term) (equal (length term) 3) (equal (car term) 'my-plus)) `(UNHIDE (HIDE (BINARY-+ ,(cadr term) ,(caddr term)))) term)) (defthm my-meta-lemma (equal (evl term a) (evl (meta-fn term) a)) :hints ((\"Goal\" :in-theory (enable my-plus))) :rule-classes ((:meta :trigger-fns (my-plus)))) ~ev[] Notice that in the following (silly) conjecture, ACL2 initially does only does the simplification directed by the metafunction; a second goal is generated before the commuativity of addition can be applied. If the above calls of ~c[UNHIDE] and ~c[HIDE] had been stripped off, then ~c[Goal'] would have been the term printed in ~c[Goal''] below. ~bv[] ACL2 !>(thm (equal (my-plus b a) ccc)) This simplifies, using the :meta rule MY-META-LEMMA and the :rewrite rule UNHIDE-HIDE, to Goal' (EQUAL (+ B A) CCC). This simplifies, using the :rewrite rule COMMUTATIVITY-OF-+, to Goal'' (EQUAL (+ A B) CCC). ~ev[] The discussion above probably suffices to make good use of this ~c[(UNHIDE (HIDE ...))] trick. However, we invite the reader who wishes to understand the trick in depth to evaluate the following form before submitting the ~ilc[thm] form above. ~bv[] (trace$ (rewrite :entry (list (take 2 arglist)) :exit (list (car values))) (rewrite-with-lemma :entry (list (take 2 arglist)) :exit (take 2 values))) ~ev[] The following annotated subset of the trace output (which may appear a bit different depending on the underlying Common Lisp implementation) explains how the trick works. ~bv[] 2> (REWRITE ((MY-PLUS B A) NIL))> 3> (REWRITE-WITH-LEMMA ((MY-PLUS B A) (REWRITE-RULE (:META MY-META-LEMMA) 1822 NIL EQUAL META-FN NIL META NIL NIL)))> We apply the meta rule, then recursively rewrite the result, which is the (UNHIDE (HIDE ...)) term shown just below. 4> (REWRITE ((UNHIDE (HIDE (BINARY-+ B A))) ((A . A) (B . B))))> 5> (REWRITE ((HIDE (BINARY-+ B A)) ((A . A) (B . B))))> The HIDE protects its argument from being touched by the rewriter. <5 (REWRITE (HIDE (BINARY-+ B A)))> 5> (REWRITE-WITH-LEMMA ((UNHIDE (HIDE (BINARY-+ B A))) (REWRITE-RULE (:REWRITE UNHIDE-HIDE) 1806 NIL EQUAL (UNHIDE (HIDE X)) X ABBREVIATION NIL NIL)))> Now we apply UNHIDE-HIDE, then recursively rewrite its right-hand side in an environment where X is bound to (BINARY-+ B A). 6> (REWRITE (X ((X BINARY-+ B A))))> Notice that at this point X is cached, so REWRITE just returns (BINARY-+ B A). <6 (REWRITE (BINARY-+ B A))> <5 (REWRITE-WITH-LEMMA T (BINARY-+ B A))> <4 (REWRITE (BINARY-+ B A))> <3 (REWRITE-WITH-LEMMA T (BINARY-+ B A))> <2 (REWRITE (BINARY-+ B A))> ~ev[]") (deflabel evaluator-restrictions ; Here is Erik Reeber's modification of his proof of nil below, but for the ; development Version of ACL2 as of early March 2007, before the fix to ACL2 for ; this problem. [It contains a truly trivial edit we've made, not important.] ; ; (in-package "ACL2") ; ; (defun my-cancel (term) ; (case-match term ; (('f ('g)) ; *t*) ; (('f2 ('g2)) ; *t*) ; (& term))) ; ; (defun f2 (x) ; (not x)) ; ; (defun g2 () ; nil) ; ; (encapsulate ; ((f (x) t)) ; ; (local (defun f (x) (declare (ignore x)) t)) ; ; (defevaluator evl evl-list ; ((f x) ; (f2 x) ; (g2))) ; ; (defthm correctness-of-my-cancel ; (equal (evl x a) ; (evl (my-cancel x) a)) ; :rule-classes ((:meta :trigger-fns (f))))) ; ; (encapsulate ; () ; ; (local (defstub c () t)) ; ; (local (encapsulate ; () ; (local (defun g () (c))) ; (local (in-theory (disable g (g)))) ; (local (defthm f-g ; (equal (f (g)) t) ; :rule-classes nil)) ; (defthm f-c ; (equal (f (c)) t) ; :hints (("Goal" :use f-g ; :in-theory (e/d (g) (correctness-of-my-cancel)))) ; :rule-classes nil))) ; ; (defthm f-t ; (equal (f x) t) ; :hints (("Goal" :by (:functional-instance ; f-c ; (c (lambda () x))))) ; :rule-classes nil)) ; ; (defun g () ; nil) ; ; ; Below is the expansion of the following defevaluator, changed slightly as ; ; indicated by comments. ; ; (defevaluator evl2 evl2-list ((f x) (f2 x) (g) (g2))) ; ; (ENCAPSULATE ; (((EVL2 * *) => *) ; ((EVL2-LIST * *) => *)) ; (SET-INHIBIT-WARNINGS "theory") ; (LOCAL (IN-THEORY *DEFEVALUATOR-FORM-BASE-THEORY*)) ; (LOCAL ; (MUTUAL-RECURSION (DEFUN EVL2 (X A) ; (DECLARE (XARGS :VERIFY-GUARDS NIL ; :MEASURE (ACL2-COUNT X) ; :WELL-FOUNDED-RELATION O< ; :MODE :LOGIC)) ; (COND ((SYMBOLP X) (CDR (ASSOC-EQ X A))) ; ((ATOM X) NIL) ; ((EQ (CAR X) 'QUOTE) (CAR (CDR X))) ; ((CONSP (CAR X)) ; (EVL2 (CAR (CDR (CDR (CAR X)))) ; (PAIRLIS$ (CAR (CDR (CAR X))) ; (EVL2-LIST (CDR X) A)))) ; ((EQUAL (CAR X) 'F) ; changed f to f2 just below ; (F2 (EVL2 (CAR (CDR X)) A))) ; ((EQUAL (CAR X) 'F2) ; (F2 (EVL2 (CAR (CDR X)) A))) ; ((EQUAL (CAR X) 'G) (G)) ; ((EQUAL (CAR X) 'G2) (G2)) ; (T NIL))) ; (DEFUN EVL2-LIST (X-LST A) ; (DECLARE (XARGS :MEASURE (ACL2-COUNT X-LST) ; :WELL-FOUNDED-RELATION O<)) ; (COND ((ENDP X-LST) NIL) ; (T (CONS (EVL2 (CAR X-LST) A) ; (EVL2-LIST (CDR X-LST) A))))))) ; ; (DEFTHM EVL2-CONSTRAINT-1 ; (IMPLIES (SYMBOLP X) ; (EQUAL (EVL2 X A) ; (CDR (ASSOC-EQ X A))))) ; (DEFTHM EVL2-CONSTRAINT-2 ; (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'QUOTE)) ; (EQUAL (EVL2 X A) (CADR X)))) ; (DEFTHM EVL2-CONSTRAINT-3 ; (IMPLIES (AND (CONSP X) (CONSP (CAR X))) ; (EQUAL (EVL2 X A) ; (EVL2 (CADDAR X) ; (PAIRLIS$ (CADAR X) ; (EVL2-LIST (CDR X) A)))))) ; (DEFTHM EVL2-CONSTRAINT-4 ; (IMPLIES (NOT (CONSP X-LST)) ; (EQUAL (EVL2-LIST X-LST A) NIL))) ; (DEFTHM EVL2-CONSTRAINT-5 ; (IMPLIES (CONSP X-LST) ; (EQUAL (EVL2-LIST X-LST A) ; (CONS (EVL2 (CAR X-LST) A) ; (EVL2-LIST (CDR X-LST) A))))) ; (DEFTHM EVL2-CONSTRAINT-6 ; (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F)) ; (EQUAL (EVL2 X A) ; changed f to f2 just below ; (F2 (EVL2 (CADR X) A))))) ; (DEFTHM EVL2-CONSTRAINT-7 ; (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F2)) ; (EQUAL (EVL2 X A) ; (F2 (EVL2 (CADR X) A))))) ; (DEFTHM EVL2-CONSTRAINT-8 ; (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G)) ; (EQUAL (EVL2 X A) (G)))) ; (DEFTHM EVL2-CONSTRAINT-9 ; (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G2)) ; (EQUAL (EVL2 X A) (G2))))) ; ; (defthm f2-t ; (equal (f2 x) t) ; :hints (("Goal" :by (:functional-instance ; f-t ; (f f2) ; (evl evl2) ; (evl-list evl2-list))))) ; ; (defthm bug-implies-nil ; nil ; :hints (("Goal" :use ((:instance f2-t (x t))))) ; :rule-classes nil) :doc ":Doc-Section meta some restrictions on the use of evaluators in meta-level rules~/ Note: This topic, which explains some subtleties for evaluators, can probably be skipped by most readers. Rules of class ~c[:]~ilc[meta] and of class ~c[:]~ilc[clause-processor] are stated using so-called ``evaluator'' functions. Here we explain some restrictions related to evaluators. Below we refer primarily to ~c[:meta] rules, but the discussion applies equally to ~c[:clause-processor] rules. In a nutshell, we require that a rule's evaluator does not support other functions in the rule, and we require that the evaluator not be introduced under a non-trivial encapsulate. We also require that no function has an attachment (~pl[defattach]) that is both ancestral in the evaluator and also ancestral in the meta or clause-processor functions. We explain these restrictions in detail below.~/ An argument given elsewhere (~pl[meta], in particular ``Aside for the logic-minded'') explains that the correctness argument for applying metatheoretic simplifiers requires that one be able to ``grow'' an evaluator (~pl[defevaluator]) to handle all functions in the current ACL2 ~il[world]. Then we may, in essence, functionally instantiate the original evaluator to the new (``grown'') evaluator, provided that the new evaluator satisfies all of the axioms of the original. We therefore require that the evaluator function does not support the formula of any ~ilc[defaxiom] event. This notion of ``support'' (sometimes denoted ``is an ancestor of'') is defined recursively as follows: a function symbol supports a formula if either it occurs in that formula, or else it supports the definition or constraint for some function symbol that occurs in that formula. Moreover, we require that neither the evaluator function nor its list version support the definition or constraint for any other function symbol occurring in the proposed ~c[:meta] theorem. We also require that the evaluator does not support the formula of a ~c[:meta] rule's metafunction (nor, if there is one, hypothesis metafunction) or of a ~c[:clause-processor] rule's clause-processor function. This requirement, along with with the analogous requirement for ~ilc[defaxiom] ~il[events] stated above, are necessary in order to carry out the functional instantiation argument alluded to above, as follows (where the reader may find it useful to have some familiarity with the paper ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pages 161-203). By the usual conservativity argument, we know that the rule follows logically from the axiomatic events for its supporters. This remains true if we functionally instantiate the evaluator with one corresponding to all the functions symbols of the current session, since none of the definitions of supporters of defaxioms or metafunctions are hit by that functional substitution. Notice though that the argument above depends on knowing that the rule is not itself an axiom about the evaluator! Therefore, we also restrict evaluators so that they are not defined in the scope of a superior ~ilc[encapsulate] event with non-empty signature, in order to avoid an even more subtle problem. The aforementioned correctness argument depends on knowing that the rule is provable from the axioms on the evaluator and metafunction (and hypothesis metafunction, if any). The additional restriction avoids unsoundness! The following events, if allowed, produce a proof that ~c[(f x)] equals ~c[t] even though, as shown below, that does not follow logically from the axioms introduced. ~bv[] ; Introduce our metafunction. (defun my-cancel (term) (case-match term (('f ('g)) *t*) (& term))) ; Introduce our evaluator and prove our meta rule, but in the same ; encapsulate! (encapsulate ((f (x) t)) (local (defun f (x) (declare (ignore x)) t)) (defevaluator evl evl-list ((f x))) (defthm correctness-of-my-cancel (equal (evl x a) (evl (my-cancel x) a)) :rule-classes ((:meta :trigger-fns (f))))) ; Prove that (f x) = t. (encapsulate () (local (defstub c () t)) (local (encapsulate () (local (defun g () (c))) (local (in-theory (disable g (g)))) (local (defthm f-g (equal (f (g)) t) :rule-classes nil)) (defthm f-c (equal (f (c)) t) :hints ((\"Goal\" :use f-g :in-theory (e/d (g) (correctness-of-my-cancel)))) :rule-classes nil))) (defthm f-t (equal (f x) t) :hints ((\"Goal\" :by (:functional-instance f-c (c (lambda () x))))) :rule-classes nil)) ~ev[] To see that the term ~c[(equal (f x) t)] does not follow logically from the axiomatic ~il[events] above, consider following the above definition of ~c[my-cancel] with the following ~il[events] instead. ~bv[] ; (defun my-cancel (term) ...) as before, then: (defun f (x) (not x)) (defun g () nil) (defevaluator evl evl-list ((f x) (g))) ~ev[] These events imply the axiomatic events above, because we still have the definition of ~c[my-cancel], we have a stronger ~ilc[defevaluator] event, and we can now prove ~c[correctness-of-my-cancel] exactly as it is stated above. So, the rule ~c[f-t] is a logical consequence of the chronology of the current session. However, in the current session we can also prove the following rule, which contradicts ~c[f-t]. ~bv[] (defthm f-not-t (equal (f t) nil) :rule-classes nil) ~ev[] It follows that the current session logically yields a contradiction! Erik Reeber has taken the above example and modified it to prove ~c[nil] in ACL2 Version_3.1, as follows. ~bv[] (in-package \"ACL2\") (defun my-cancel (term) (case-match term (('f ('g)) *t*) (('f2 ('g2)) *t*) (& term))) (defun f2 (x) (not x)) (defun g2 () nil) (encapsulate ((f (x) t)) (local (defun f (x) (declare (ignore x)) t)) (defevaluator evl evl-list ((f x) (f2 x) (g2))) (defthm correctness-of-my-cancel (equal (evl x a) (evl (my-cancel x) a)) :rule-classes ((:meta :trigger-fns (f))))) (encapsulate () (local (defstub c () t)) (local (encapsulate () (local (defun g () (c))) (local (in-theory (disable g (g)))) (local (defthm f-g (equal (f (g)) t) :rule-classes nil)) (defthm f-c (equal (f (c)) t) :hints ((\"Goal\" :use f-g :in-theory (e/d (g) (correctness-of-my-cancel)))) :rule-classes nil))) (defthm f-t (equal (f x) t) :hints ((\"Goal\" :by (:functional-instance f-c (c (lambda () x))))) :rule-classes nil)) (defun g () nil) ; Below is the expansion of the following defevaluator, changed slightly as ; indicated by comments. ; (defevaluator evl2 evl2-list ((f x) (f2 x) (g) (g2))) (ENCAPSULATE (((EVL2 * *) => *) ((EVL2-LIST * *) => *)) (SET-INHIBIT-WARNINGS \"theory\") (LOCAL (IN-THEORY *DEFEVALUATOR-FORM-BASE-THEORY*)) (LOCAL (MUTUAL-RECURSION (DEFUN EVL2 (X A) (DECLARE (XARGS :VERIFY-GUARDS NIL :MEASURE (ACL2-COUNT X) :WELL-FOUNDED-RELATION O< :MODE :LOGIC)) (COND ((SYMBOLP X) (CDR (ASSOC-EQ X A))) ((ATOM X) NIL) ((EQ (CAR X) 'QUOTE) (CAR (CDR X))) ((CONSP (CAR X)) (EVL2 (CAR (CDR (CDR (CAR X)))) (PAIRLIS$ (CAR (CDR (CAR X))) (EVL2-LIST (CDR X) A)))) ((EQUAL (CAR X) 'F) ; changed f to f2 just below (F2 (EVL2 (CAR (CDR X)) A))) ((EQUAL (CAR X) 'F2) (F2 (EVL2 (CAR (CDR X)) A))) ((EQUAL (CAR X) 'G) (G)) ((EQUAL (CAR X) 'G2) (G2)) (T NIL))) (DEFUN EVL2-LIST (X-LST A) (DECLARE (XARGS :MEASURE (ACL2-COUNT X-LST) :WELL-FOUNDED-RELATION O<)) (COND ((ENDP X-LST) NIL) (T (CONS (EVL2 (CAR X-LST) A) (EVL2-LIST (CDR X-LST) A))))))) (DEFTHM EVL2-CONSTRAINT-1 (IMPLIES (SYMBOLP X) (EQUAL (EVL2 X A) (CDR (ASSOC-EQ X A))))) (DEFTHM EVL2-CONSTRAINT-2 (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'QUOTE)) (EQUAL (EVL2 X A) (CADR X)))) (DEFTHM EVL2-CONSTRAINT-3 (IMPLIES (AND (CONSP X) (CONSP (CAR X))) (EQUAL (EVL2 X A) (EVL2 (CADDAR X) (PAIRLIS$ (CADAR X) (EVL2-LIST (CDR X) A)))))) (DEFTHM EVL2-CONSTRAINT-4 (IMPLIES (NOT (CONSP X-LST)) (EQUAL (EVL2-LIST X-LST A) NIL))) (DEFTHM EVL2-CONSTRAINT-5 (IMPLIES (CONSP X-LST) (EQUAL (EVL2-LIST X-LST A) (CONS (EVL2 (CAR X-LST) A) (EVL2-LIST (CDR X-LST) A))))) (DEFTHM EVL2-CONSTRAINT-6 (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F)) (EQUAL (EVL2 X A) ; changed f to f2 just below (F2 (EVL2 (CADR X) A))))) (DEFTHM EVL2-CONSTRAINT-7 (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F2)) (EQUAL (EVL2 X A) (F2 (EVL2 (CADR X) A))))) (DEFTHM EVL2-CONSTRAINT-8 (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G)) (EQUAL (EVL2 X A) (G)))) (DEFTHM EVL2-CONSTRAINT-9 (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G2)) (EQUAL (EVL2 X A) (G2))))) (defthm f2-t (equal (f2 x) t) :hints ((\"Goal\" :by (:functional-instance f-t (f f2) (evl evl2) (evl-list evl2-list))))) (defthm bug-implies-nil nil :hints ((\"Goal\" :use ((:instance f2-t (x t))))) :rule-classes nil) ~ev[] Finally, we also require that no function has an attachment (~pl[defattach]) that is both ancestral in the evaluator and also ancestral in the meta or clause-processor functions. (If you don't use ~ilc[defattach] then you can ignore this condition.) Without this restriction, the following events prove ~c[nil]. ~bv[] (in-package \"ACL2\") (defstub f () t) (defevaluator evl evl-list ((f))) (defun my-meta-fn (x) (if (equal x '(f)) (list 'quote (f)) x)) (defthm my-meta-fn-correct (equal (evl x a) (evl (my-meta-fn x) a)) :rule-classes ((:meta :trigger-fns (f)))) (defun constant-nil () (declare (xargs :guard t)) nil) (defattach f constant-nil) (defthm f-is-nil ; proved using my-meta-fn-correct (equal (f) nil) :rule-classes nil) (defthm contradiction nil :hints ((\"Goal\" :use ((:functional-instance f-is-nil (f (lambda () t)))))) :rule-classes nil) ~ev[] To see why this restriction is sufficient, see a comment in the ACL2 source code entitled ``; Essay on Correctness of Meta Reasoning.''") (deflabel extended-metafunctions :doc ":Doc-Section Miscellaneous state and context sensitive metafunctions~/ ~bv[] General Form of an Extended :Meta theorem: (implies (and (pseudo-termp x) ; this hyp is optional (alistp a) ; this hyp is optional (ev (hyp-fn x mfc state) a) ; this hyp is optional ; meta-extract hyps may also be included (see below) ) (equiv (ev x a) (ev (fn x mfc state) a))) ~ev[] where the restrictions are as described in the ~il[documentation] for ~ilc[meta] where ~c[state] is literally the symbol ~c[STATE], and ~c[x], ~c[a], ~c[mfc], and ~c[state] are distinct variable symbols. A ~c[:meta] theorem of the above form installs ~c[fn] as a metatheoretic simplifier with hypothesis function ~c[hyp-fn], exactly as for vanilla metafunctions. The only difference is that when the metafunctions are applied, some contextual information is passed in via the ~c[mfc] argument and the ACL2 ~ilc[state] is made available. ~l[meta] for a discussion of vanilla flavored metafunctions. This documentation assumes you are familiar with the simpler situation, in particular, how to define a vanilla flavored metafunction, ~c[fn], and its associated hypothesis metafunction, ~c[hyp-fn], and how to state and prove metatheorems installing such functions. Defining extended metafunctions requires that you also be familiar with many ACL2 implementation details. This documentation is sketchy on these details; see the ACL2 source code or email the ~il[acl2-help] list if you need more help. Additional hypotheses are supported, called ``meta-extract hypotheses'', that allow metafunctions to depend on the validity of certain terms extracted from the context or the logical ~il[world]. These hypotheses provide an even more advanced form of metatheorem so we explain them elsewhere; ~pl[meta-extract].~/ The metafunction context, ~c[mfc], is a list containing many different data structures used by various internal ACL2 functions. We do not document the form of ~c[mfc]. Your extended metafunction should just take ~c[mfc] as its second formal and pass it into the functions mentioned below. The ACL2 ~c[state] is well-documented (~pl[state]). Below we present expressions below that can be useful in defining extended metafunctions. Some of these expressions involve keyword arguments, ~c[:forcep] and ~c[:ttree], which are optional and in most cases are fine to omit, and which we explain after we present the useful expressions. ~c[(mfc-clause mfc)]: returns the current goal, in clausal form. A clause is a list of ACL2 terms, implicitly denoting the disjunction of the listed terms. The clause returned by ~c[mfc-clause] is the clausal form of the translation (~pl[trans]) of the goal or subgoal on which the rewriter is working. When a metafunction calls ~c[mfc-clause], the term being rewritten by the metafunction either occurs somewhere in this clause or, perhaps more commonly, is being rewritten on behalf of some lemma to which the rewriter has backchained while trying to rewrite a term in the clause. ~c[(mfc-ancestors mfc)]: returns an alist whose keys are the negations of the backchaining hypotheses being pursued. In particular, ~c[(null (mfc-ancestors mfc))] will be true exactly when rewriting is on part of the current goal. Exception: An element of this alist whose key is of the form ~c[(:binding-hyp hyp unify-subst)] indicates that ~c[hyp] has been encountered as a hypothesis of the form ~c[(equal var term)] or ~c[(equiv var (double-rewrite-term))], in each case binding variable ~c[var] to the result of rewriting ~c[term] under ~c[unify-subst]. ~c[(mfc-rdepth mfc)]: returns the remaining stack depth for calls of the rewriter (by default, ~c[*default-rewrite-stack-limit*] at the top level; ~pl[rewrite-stack-limit]). When this is 0, no further calls of the rewriter can be made without error. ~c[(mfc-type-alist mfc)]: returns the type-alist governing the occurrence of the term, ~c[x], being rewritten by the metafunction. A type-alist is an association list, each element of which is of the form ~c[(term ts . ttree)]. Such an element means that the term ~c[term] has the ~il[type-set] ~c[ts]. The ~c[ttree] component is probably irrelevant here. All the terms in the type-alist are in translated form (~pl[trans]). The ~c[ts] are numbers denoting finite Boolean combinations of ACL2's primitive types (~pl[type-set]). The type-alist includes not only information gleaned from the conditions governing the term being rewritten but also that gleaned from forward chaining from the (negations of the) other literals in the clause returned by ~c[mfc-clause]. ~c[(mfc-unify-subst mfc)]: returns ~c[nil] except when evaluating a ~ilc[syntaxp] or ~ilc[bind-free] hypothesis, in which case, returns the unifying substitution present at the start of that evaluation. ~c[(mfc-world mfc)]: returns the ACL2 logical ~ilc[world]. ~c[(mfc-ts term mfc state :forcep forcep :ttreep ttreep)]: returns the ~c[type-set] of ~c[term] in the current context; ~pl[type-set]. ~c[(mfc-rw term obj equiv-info mfc state)]: returns the result of rewriting ~c[term] in the current context, ~c[mfc], with objective ~c[obj] and the equivalence relation described by ~c[equiv-info]. ~c[Obj] should be ~c[t], ~c[nil], or ~c[?], and describes your objective: try to show that ~c[term] is true, false, or anything. ~c[Equiv-info] is either ~c[nil], ~c[t], a function symbol ~c[fn] naming a known equivalence relation, or a list of congruence rules. ~c[Nil] means return a term that is ~c[equal] to ~c[term]. ~c[T] means return a term that is propositionally equivalent (i.e., in the ~c[iff] sense) to ~c[term], while ~c[fn] means return a term ~c[fn]-equivalent to ~c[term]. The final case, which is intended only for advanced users, allows the specification of generated equivalence relations, as supplied to the ~c[geneqv] argument of ~c[rewrite]. Generally, if you wish to establish that ~c[term] is true in the current context, use the idiom ~bv[] (equal (mfc-rw term t t mfc state) *t*) ~ev[] The constant ~c[*t*] is set to the internal form of the constant term ~c[t], i.e., ~c['t]. ~c[(mfc-rw+ term alist obj equiv-info mfc state)]: if ~c[alist] is ~c[nil] then this is equivalent to ~c[(mfc-rw term obj equiv-info mfc state)]. However, the former takes an argument, ~c[alist], that binds variables to terms, and returns the result of rewriting ~c[term] under that ~c[alist], where this rewriting is as described for ~c[mfc-rw] above. The function ~c[mfc-rw+] can be more efficient than ~c[mfc-rw], because the terms in the binding alist have generally already been rewritten, and it can be inefficient to rewrite them again. For example, consider a rewrite rule of the following form. ~bv[] (implies (and ... (syntaxp (... (mfc-rw `(bar ,x) ...) ...)) ...) (equal (... x ...) ...)) ~ev[] Here, ~c[x] is bound in the conclusion to the result of rewriting some term, say, ~c[tm]. Then the above call of ~c[mfc-rw] will rewrite ~c[tm], which is probably unnecessary. So a preferable form of the rule above may be as follows, so that ~c[tm] is not further rewritten by ~c[mfc-rw+]. ~bv[] (implies (and ... (syntaxp (... (mfc-rw+ '(bar v) `((v . ,x)) ...) ...)) ...) (equal (... x ...) ...)) ~ev[] However, you may find that the additional rewriting done by ~c[mfc-rw] is useful in some cases. ~c[(mfc-ap term mfc state)]: returns ~c[t] or ~c[nil] according to whether linear arithmetic can determine that ~c[term] is false. To the cognoscenti: returns the contradiction flag produced by linearizing ~c[term] and adding it to the ~c[linear-pot-lst]. ~c[(mfc-relieve-hyp hyp alist rune target bkptr mfc state)]: returns ~c[t] or ~c[nil] according to whether the indicated hypothesis term, ~c[hyp], can be relieved (proved) under the giving variable bindings, ~c[alist]. Note that this function returns ~c[nil] if ~c[hyp] has free variables (~pl[free-variables]). Here, ~c[hyp] is the hypothesis of the indicated ~ilc[rune] at (one-based) position ~c[bkptr], and ~c[target] is an instantiated term to which ~c[rune] is being applied. Note that no indication is returned for whether any assumptions have been generated by ~ilc[force] or ~ilc[case-split]. (If you need such a feature, feel free to request it of the ACL2 implementors.) We explain the ~c[:forcep] and ~c[:ttreep] keyword arguments provided in some expressions, as promised above. Their defaults are ~c[:same] and ~c[nil], respectively. A value of ~c[:same] for ~c[:forcep] means that forcing will be allowed if and only if it is allowed in the current rewriting environment; ~pl[force]. A value of ~c[t] or ~c[nil] for ~c[:forcep] overrides this default and allows or disallows forcing, respectively. By default these functions return a single value, ~c[val], but if ~c[:ttreep] is ~c[t] then they return ~c[(mv val ttree)], where ~c[ttree] is the tag-tree (~pl[ttree]) returned by the indicated operation, with an input tag-tree of ~c[nil]). During the execution of a metafunction by the theorem prover, the expressions above compute the results specified. Typically, you should imagine that there are no axioms about the ~c[mfc-] function symbols: treat them as uninterpreted function symbols. There is an advanced feature, meta-extract hypotheses, that can avoid this logical weakness in some cases when proving ~c[:]~ilc[meta] rules; ~pl[meta-extract]. But we assume for the rest of the present ~il[documentation] topic that you do not use meta-extract hypotheses. Thus, in the proof of the correctness of a metafunction, no information is available about the results of these functions: you should ~em[use these functions for heuristic purposes only]. For example, your metafunction may use these functions to decide whether to perform a given transformation, but the transformation must be sound regardless of the value that your metafunction returns. We illustrate this below. It is sometimes possible to use the hypothesis metafunction, ~c[hyp-fn], to generate a sufficient hypothesis to justify the transformation. The generated hypothesis might have already been ``proved'' internally by your use of ~c[mfc-ts] or ~c[mfc-rw], but the system will have to prove it ``officially'' by relieving it. We illustrate this below also. We conclude with a script that defines, verifies, and uses several extended metafunctions. This script is based on one provided by Robert Krug, who was instrumental in the development of this style of metafunction and whose help we gratefully acknowledge. ~bv[] ; Here is an example. I will define (foo i j) simply to be (+ i j). ; But I will keep its definition disabled so the theorem prover ; doesn't know that. Then I will define an extended metafunction ; that reduces (foo i (- i)) to 0 provided i has integer type and the ; expression (< 10 i) occurs as a hypothesis in the clause. ; Note that (foo i (- i)) is 0 in any case. (defun foo (i j) (+ i j)) (defevaluator eva eva-lst ((foo i j) (unary-- i) ; I won't need these two cases until the last example below, but I ; include them now. (if x y z) (integerp x))) (set-state-ok t) (defun metafn (x mfc state) (cond ((and (consp x) (equal (car x) 'foo) (equal (caddr x) (list 'unary-- (cadr x)))) ; So x is of the form (foo i (- i)). Now I want to check some other ; conditions. (cond ((and (ts-subsetp (mfc-ts (cadr x) mfc state) *ts-integer*) (member-equal `(NOT (< '10 ,(cadr x))) (mfc-clause mfc))) (quote (quote 0))) (t x))) (t x))) (defthm metafn-correct (equal (eva x a) (eva (metafn x mfc state) a)) :rule-classes ((:meta :trigger-fns (foo)))) (in-theory (disable foo)) ; The following will fail because the metafunction won't fire. ; We don't know enough about i. (thm (equal (foo i (- i)) 0)) ; Same here. (thm (implies (and (integerp i) (< 0 i)) (equal (foo i (- i)) 0))) ; But this will work. (thm (implies (and (integerp i) (< 10 i)) (equal (foo i (- i)) 0))) ; This won't, because the metafunction looks for (< 10 i) literally, ; not just something that implies it. (thm (implies (and (integerp i) (< 11 i)) (equal (foo i (- i)) 0))) ; Now I will undo the defun of metafn and repeat the exercise, but ; this time check the weaker condition that (< 10 i) is provable ; (by rewriting it) rather than explicitly present. (ubt 'metafn) (defun metafn (x mfc state) (cond ((and (consp x) (equal (car x) 'foo) (equal (caddr x) (list 'unary-- (cadr x)))) (cond ((and (ts-subsetp (mfc-ts (cadr x) mfc state) *ts-integer*) (equal (mfc-rw `(< '10 ,(cadr x)) t t mfc state) *t*)) ; The mfc-rw above rewrites (< 10 i) with objective t and iffp t. The ; objective means the theorem prover will try to establish it. The ; iffp means the theorem prover can rewrite maintaining propositional ; equivalence instead of strict equality. (quote (quote 0))) (t x))) (t x))) (defthm metafn-correct (equal (eva x a) (eva (metafn x mfc state) a)) :rule-classes ((:meta :trigger-fns (foo)))) (in-theory (disable foo)) ; Now it will prove both: (thm (implies (and (integerp i) (< 10 i)) (equal (foo i (- i)) 0))) (thm (implies (and (integerp i) (< 11 i)) (equal (foo i (- i)) 0))) ; Now I undo the defun of metafn and change the problem entirely. ; This time I will rewrite (integerp (foo i j)) to t. Note that ; this is true if i and j are integers. I can check this ; internally, but have to generate a hyp-fn to make it official. (ubt 'metafn) (defun metafn (x mfc state) (cond ((and (consp x) (equal (car x) 'integerp) (consp (cadr x)) (equal (car (cadr x)) 'foo)) ; So x is (integerp (foo i j)). Now check that i and j are ; ``probably'' integers. (cond ((and (ts-subsetp (mfc-ts (cadr (cadr x)) mfc state) *ts-integer*) (ts-subsetp (mfc-ts (caddr (cadr x)) mfc state) *ts-integer*)) *t*) (t x))) (t x))) ; To justify this transformation, I generate the appropriate hyps. (defun hyp-fn (x mfc state) (declare (ignore mfc state)) ; The hyp-fn is run only if the metafn produces an answer different ; from its input. Thus, we know at execution time that x is of the ; form (integerp (foo i j)) and we know that metafn rewrote ; (integerp i) and (integerp j) both to t. So we just produce their ; conjunction. Note that we must produce a translated term; we ; cannot use the macro AND and must quote constants! Sometimes you ; must do tests in the hyp-fn to figure out which case the metafn ; produced, but not in this example. `(if (integerp ,(cadr (cadr x))) (integerp ,(caddr (cadr x))) 'nil)) (defthm metafn-correct (implies (eva (hyp-fn x mfc state) a) (equal (eva x a) (eva (metafn x mfc state) a))) :rule-classes ((:meta :trigger-fns (integerp)))) (in-theory (disable foo)) ; This will not be proved. (thm (implies (and (rationalp x) (integerp i)) (integerp (foo i j)))) ; But this will be. (thm (implies (and (rationalp x) (integerp i) (integerp j)) (integerp (foo i j)))) ~ev[] ") (deflabel meta-extract :doc ":Doc-Section Miscellaneous meta reasoning using valid terms extracted from context or ~il[world]~/ For this advanced topic, we assume familiarity with metatheorems and metafunctions (~pl[meta]), as well as extended metafunctions (~pl[extended-metafunctions]). The capability described here ~-[] so-called ``meta-extract hypotheses'' for a ~c[:]~ilc[meta] or a ~c[:]~ilc[clause-processor] rule ~-[] provides an advanced form of meta-level reasoning that was initially designed largely by Sol Swords, who also provided a preliminary implementation. A meta rule or clause-processor rule may have so-called ``meta-extract'' hypotheses that take forms displayed below. Here ~c[evl] is the evaluator, ~c[obj] is an arbitrary term, ~c[mfc] is the metafunction context (which is a variable other than the symbol ~c[STATE] that represents the metafunction context; ~pl[extended-metafunctions]), ~c[state] is literally the symbol ~c[STATE], ~c[a] is the second argument of ~c[evl] in both arguments of the conclusion of the rule, and ~c[aa] is an arbitrary term. ~bv[] (evl (meta-extract-contextual-fact obj mfc state) a) (evl (meta-extract-global-fact obj state) aa)) ; equivalent to the next form (evl (meta-extract-global-fact+ obj state state) aa) (evl (meta-extract-global-fact+ obj st state) aa) ~ev[] The first form is only legal for ~c[:meta] rules for which the metafunction is an extended metafunction. The remaining forms are legal for both ~c[:meta] rules and ~c[:clause-processor] rules. Sol Swords has contributed a community book, ~c[clause-processors/meta-extract-user.lisp], that uses a Skolemization trick to allow one to use at most one ~c[meta-extract-global-fact+] hypothesis and at most one ~c[meta-extract-contextual-fact] hypothesis.~/ These additional hypotheses may be necessary in order to prove a proposed metatheorem or (for the second type of hypothesis above) clause-processor rule, in particular when the correctness of the metafunction depends on the correctness of utilities extracting formulas from the logical ~il[world] or (for the first type) facts from the metafunction context (mfc). After the rule is proved, however, the meta-extract hypotheses have no effect on how the rule is applied during a proof. An argument for correctness of using meta-extract hypotheses is given in the ACL2 source code within a comment entitled ``Essay on Correctness of Meta Reasoning''. In the documentation below, we focus primarily on ~c[:]~ilc[meta] rules, since the use of ~c[meta-extract-global-fact] hypotheses in ~c[:]~ilc[clause-processor] rules is entirely analogous. (At the end, though, we discuss the last of the four forms displayed above.) And for ~c[:meta] rules we focus not on the application of rules but, rather, on how the use of meta-extract hypotheses allow you to prove correctness of metafunctions that use facts from the logical ~il[world] or the metafunction context (mfc). Below we describe properties of ~c[meta-extract-contextual-fact] and ~c[meta-extract-global-fact], but only after we illustrate their utility with an example. But even before we present that example, we first give a sense of how to think about these functions by showing a theorem that one can prove about the first of them. If this snippet doesn't help your intuition, then just skip over it and start with the example. ~bv[] (defevaluator evl evl-list ((binary-+ x y) (typespec-check x y))) (thm (implies (not (bad-atom (cdr (assoc-equal 'x alist)))) (equal (evl (meta-extract-contextual-fact (list :typeset 'x) mfc state) alist) (not (equal 0 ; indicates non-empty intersection (logand (type-set-quote ; type-set of a constant (cdr (assoc-equal 'x alist))) (mfc-ts-fn 'x mfc state nil))))))) ~ev[] The following example comes from the community book, ~c[books/clause-processors/meta-extract-simple-test.lisp] (after it defines the evaluator), which presents very basic (and contrived) examples that nevertheless illustrate meta-extract hypotheses. ~bv[] (defthm plus-identity-2-meta (implies (and (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp) state) (list (cons 'u (nthmeta-ev (cadr (cadr term)) a)))) (nthmeta-ev (meta-extract-contextual-fact `(:typeset ,(caddr term)) mfc state) a)) (equal (nthmeta-ev term a) (nthmeta-ev (plus-identity-2-metafn term mfc state) a))) :rule-classes ((:meta :trigger-fns (binary-+)))) ~ev[] The two hypotheses illustratate the two basic kinds of meta-extract hypotheses: applications of the evaluator to a call of ~c[meta-extract-global-fact] and to a call of ~c[meta-extract-contextual-fact]. Here is the definition of the metafunction used in the above rule, slightly simplified here from what is found in the above book (but adequate for proving the two events that follow it in the above book). ~bv[] (defun plus-identity-2-metafn (term mfc state) (declare (xargs :stobjs state :verify-guards nil)) (case-match term (('binary-+ ('bar &) y) (cond ((equal (meta-extract-formula 'bar-posp state) '(POSP (BAR U))) (if (ts= (mfc-ts y mfc state :forcep nil) *ts-character*) (cadr term) term)) (t term))) (& term))) ~ev[] This metafunction returns its input term unchanged except in the case that the term is of the form ~c[(binary-+ (bar x) y)] and the following two conditions are met, in which case it returns ~c[(bar x)]. ~bv[] (1) (equal (meta-extract-formula 'bar-posp state) '(POSP (BAR U))) (2) (ts= (mfc-ts y mfc state :forcep nil) *ts-character*) ~ev[] So suppose that term is ~c[(list 'binary-+ (list 'bar x) y)]. We show how the meta-extract hypotheses together with (1) and (2) imply that the conclusion of the above ~c[:meta] rule holds. Here is that conclusion after a bit of simplification. ~bv[] (equal (nthmeta-ev (list 'binary-+ (list 'bar x) y) a) (nthmeta-ev (list 'bar x) a)) ~ev[] This equality simplifies as follows using the evaluator properties of ~c[nthmeta-ev]. ~bv[] (equal (binary-+ (bar (nthmeta-ev x a)) (nthmeta-ev y a)) (bar (nthmeta-ev x a))) ~ev[] Since a positive number plus a character is that number, it clearly suffices to show: ~bv[] (A) (posp (bar (nthmeta-ev x a))) (B) (characterp (nthmeta-ev y a)) ~ev[] It remains then to show that these follow from (1) and (2) together with the meta-extract hypotheses. First consider (A). We show that it is just a simplification of the first meta-extract hypothesis. ~bv[] (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp) state) (list (cons 'u (nthmeta-ev (cadr (cadr term)) a)))) = {by our assumption that term is (list 'binary-+ (list 'bar x) y)} (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp) state) (list (cons 'u (nthmeta-ev x a)))) = {by definition of meta-extract-global-fact, as discussed later} (nthmeta-ev (meta-extract-formula 'bar-posp state) (list (cons 'u (nthmeta-ev x a)))) = {by (1)} (nthmeta-ev '(posp (bar u)) (list (cons 'u (nthmeta-ev x a)))) = {by evaluator properties of nthmeta-ev} (posp (bar (nthmeta-ev x a))) ~ev[] Now consider (B). We show that it is just a simplification of the second meta-extract hypothesis. ~bv[] (nthmeta-ev (meta-extract-contextual-fact `(:typeset ,(caddr term)) mfc state) a) = {by our assumption that term is (list 'binary-+ (list 'bar x) y)} (nthmeta-ev (meta-extract-contextual-fact (list ':typeset y) mfc state) a) = {by definition of meta-extract-contextual-fact, as discussed later} (nthmeta-ev (list 'typespec-check (list 'quote (mfc-ts y mfc state :forcep nil)) y) a) = {by (2)} (nthmeta-ev (list 'typespec-check (list 'quote *ts-character*) y) a) = {by evaluator properties of nthmeta-ev} (typespec-check *ts-character* (nthmeta-ev y a)) = {by definition of typespec-check} (characterp (nthmeta-ev y a)) ~ev[] Note the use of ~c[:forcep nil] above. All of the ~c[mfc-xx] functions take a keyword argument ~c[:forcep]. Calls of ~c[mfc-xx] functions made on behalf of ~c[meta-extract-contextual-fact] always use ~c[:forcep nil], so in order to reason about these calls in your own metafunctions, you will want to use ~c[:forcep nil]. We have contemplated adding a utility like ~c[meta-extract-contextual-fact] that allows forcing but returns a tag-tree (~pl[ttree]), and may do so if there is demand for it. Finally, we document what is provided logically by calls of ~c[meta-extract-global-fact] and ~c[meta-extract-contextual-fact]. Of course, you are invited to look at the definitions of these function in the ACL2 source code, or by using ~c[:]~ilc[pe]. Note that both of these functions are non-executable (each of their bodies is inside a call of ~ilc[non-exec]); their purpose is purely logical, not for execution. The functions return ~c[*t*], i.e., ~c[(quote t)], in cases that they provide no information. First we consider the value of ~c[(meta-extract-global-fact obj state)] for various values of ~c[obj]. When we refer below to concepts like ``body'' and ``evaluation'', we refer to these with respect to the logical world of the input ~c[state]. ~bq[] Case ~c[obj] = ~c[(list :formula FN)]:~nl[] The value reduces to the value of ~c[(meta-extract-formula FN state)], which returns the ``formula'' of ~c[FN] in the following sense. If ~c[FN] is a function symbol with formals ~c[(X1 ... Xk)], then the formula is the ~il[constraint] on ~c[FN] if ~c[FN] is constrained or introduced by ~ilc[defchoose], and otherwise is ~c[(equal (FN X1 ... Xk) BODY)], where ~c[BODY] is the (unsimplified) body of the definition of ~c[FN]. Otherwise, if ~c[FN] is the name of a theorem, the formula is just what is stored for that theorem. Otherwise, the formula is ~c[*t*]. Case ~c[obj] = ~c[(list :lemma FN N)]:~nl[] Assume ~c[N] is a natural number; otherwise, treat ~c[N] as 0. If ~c[FN] is a function symbol with more than ~c[N] associated lemmas ~-[] ``associated'' in the sense of being either a ~c[:]~ilc[definition] rule for ~c[FN] or a ~c[:]~ilc[rewrite] rule for ~c[FN] whose left-hand side has a top function symbol of ~c[FN] ~-[] then the value is the ~c[N]th such lemma (with zero-based indexing). Otherwise the value is ~c[*t*]. Case ~c[obj] = ~c[(list :fncall FN ARGLIST)]:~nl[] Assume that ~c[FN] is a ~c[:]~ilc[logic]-mode function symbol and that ~c[ARGLIST] is a true list of values of the same length as list of formal parameters for ~c[FN] (i.e., as the input arity of ~c[FN]). Also assume that the application of ~c[FN] to actual parameter list ~c[ARGLIST] returns a result ~c[(mv nil x)]. Let ~c[(QARG1 ... QARGk)] be the result of quoting each element of ~c[ARGLIST], i.e., replacing each ~c[y] in ~c[ARGLIST] by the two-element list ~c[(quote y)]. Then the value is the term ~c[(equal (FN QARG1 ... QARGk) (quote x))]. For any other values of ~c[obj], the value is ~c[*t*].~eq[] Finally, the value of ~c[(meta-extract-contextual-fact obj mfc state)] is as follows for various values of ~c[obj]. Note a difference from the semantics of ~c[meta-extract-global-fact]: below, the relevant logical world is the one stored in the metafunction context, ~c[mfc], not in the input ~c[state]. ~bq[] Case ~c[obj] = (list :typeset TERM ...):~nl[] The value is the value of ~c[(typespec-check ts TERM)], where ~c[ts] is the value of ~c[(mfc-ts TERM mfc state :forcep nil :ttreep nil)], and where ~c[(typespec-check ts val)] is defined to be true when ~c[val] has type-set ~c[ts]. (Exception: If ~c[val] satisfies ~c[bad-atom] then ~c[typespec-check] is true when ~c[ts] is negative.) Case ~c[obj] = (list :rw+ TERM ALIST OBJ EQUIV ...):~nl[] We assume below that ~c[EQUIV] is a symbol that represents an equivalence relation, where ~c[nil] represents ~ilc[equal], ~c[t] represents ~ilc[iff], and otherwise ~c[EQUIV] represents itself (an equivalence relation in the current logical ~il[world]). For any other ~c[EQUIV] the value is ~c[*t*]. Now let ~c[rhs] be the value of ~c[(mfc-rw+ TERM ALIST OBJ EQUIV mfc state :forcep nil :ttreep nil)]. Then the value is the term ~c[(list 'equv (sublis-var ALIST TERM) rhs)], where equv is the equivalence relation represented by ~c[EQUIV], and ~c[sublis-var] is defined to substitute a variable-binding alist into a term. Case ~c[obj] = (list :rw TERM OBJ EQUIV ...):~nl[] The value is the same as above but for an ~c[ALIST] of ~c[nil], i.e., for the case that ~c[obj] is ~c[(list :rw+ TERM nil OBJ EQUIV ...)]. Case ~c[obj] = (list :ap TERM ...):~nl[] The value is ~c[(list 'not TERM)] if ~c[(mfc-ap TERM mfc state :forcep nil)] is true, else is ~c[*t*]. Case ~c[obj] = (list :relieve-hyp HYP ALIST RUNE TARGET BKPTR ...):~nl[] The value is ~c[(sublis-var alist hyp)] ~-[] see above for a discussion of ~c[sublis-var] ~-[] if the following is true. ~bv[] (mfc-relieve-hyp hyp alist rune target bkptr mfc state :forcep nil :ttreep nil) ~ev[] Otherwise the value is ~c[*t*]. If no case above applies, then the value is ~c[*t*].~eq[] We conclude by considering the fourth of the four forms above (and implicitly, its special case represented by the third form above): ~bv[] (evl (meta-extract-global-fact+ obj st state) aa) ~ev[] The discussion above is for the function ~c[meta-extract-global-fact+], but assumes that the logical ~il[world]s of ~c[st] and ~c[state] are equal; otherwise the value returned is ~c[*t*]. Of course, since a call of ~c[meta-extract-global-fact] expands to a corresponding call of ~c[meta-extract-global-fact+] in which the last two arguments are both ~c[state], that condition holds automatically for that case. But the ~c[state] mentioned in the meta-extract hypotheses of a ~il[meta] rule or ~il[clause-processor] rule is in essence an initial state. In the case of a clause-processor rule, the clause-processor function may modify that initial state (say, by printing or modifying some state globals) without changing its world, and then pass that modified state to ~c[fncall-term]. While ~c[fncall-term] may produce a different result for this modified state than for the initial state, both are valid: the state used for heuristic purposes, such as determining whether guard-checking may cause an error. A useful instance of the hypothesis displayed above will be one in which ~c[st] is that modified state.~/") (link-doc-to meta-extract-contextual-fact miscellaneous meta-extract) (link-doc-to meta-extract-global-fact miscellaneous meta-extract) (link-doc-to meta-extract-global-fact+ miscellaneous meta-extract) (link-doc-to meta-extract-formula miscellaneous meta-extract) (link-doc-to meta-extract-rw+-term miscellaneous meta-extract) (link-doc-to fncall-term miscellaneous meta-extract) (link-doc-to typespec-check miscellaneous meta-extract) (defun evaluator-clause/arglist (evfn formals x) ; See evaluator-clause. We return a list of the form ; '((evfn (cadr x) a) (evfn (caddr x) a) ...) containing ; as many elements as there are in formals. The evfn and ; x we use are as provided in our arguments, but the variable ; symbol A in our answer is built-in below. (cond ((null formals) nil) (t (cons (mcons-term* evfn (mcons-term* 'car x) 'a) (evaluator-clause/arglist evfn (cdr formals) (mcons-term* 'cdr x)))))) (defun evaluator-clause (evfn fn-args) ; Fn-args is of the form (fn v1 ... vn), a well-formed application of the ; function fn. We return a clause that expresses the theorem ; (implies (and (consp x) ; (equal (car x) 'fn)) ; (equal (evfn x a) ; (fn (evfn (cadr x) a) ; ... ; (evfn (cad...dr x) a)))) ; where evfn and fn are the function symbols provided. Note that the ; clause we return uses the variable symbols X and A. These symbols ; are built into this definition and that of evaluator-clause/arglist. (list '(not (consp x)) (fcons-term* 'not (fcons-term* 'equal '(car x) (kwote (car fn-args)))) (fcons-term* 'equal (fcons-term* evfn 'x 'a) (fcons-term (car fn-args) (evaluator-clause/arglist evfn (cdr fn-args) '(cdr x)))))) (defun evaluator-clauses1 (evfn fn-args-lst) (cond ((null fn-args-lst) nil) (t (cons (evaluator-clause evfn (car fn-args-lst)) (evaluator-clauses1 evfn (cdr fn-args-lst)))))) (defun evaluator-clauses (evfn evfn-lst fn-args-lst) ; We return the set of clauses that describe an evaluator, evfn, that ; knows about the function symbols listed in fn-args-lst. The ; mutually recursive function that evaluates a list of such terms is ; named evfn-lst. ; The clauses that describe an evaluator include an evaluator-clause ; (q.v.) for each fn in fn-args-lst plus clauses describing evfn when ; x is a variable symbol, a quoted object, and a lambda application, ; plus clauses describing evfn-lst on nil and on conses. ; Note: The function chk-evaluator exploits the fact that if evfn is ; an evaluator, then the constraint on it will contain at least 4 ; clauses. (One of the five fixed clauses below is about only ; evfn-lst and not about evfn and hence wouldn't be among the ; constraints of evfn.) If this changes, change chk-evaluatorp. ; The functions guess-fn-args-lst-for-evfn and guess-evfn-lst-for-evfn take the ; known constraints on an evfn and guess the evfn-lst and list of fns for which ; evfn might be an evaluator. These functions knows the structure of the ; clauses generated here, in particular, the structure of the clause describing ; evfn-lst on a cons and the structure of the evaluator-clause for a given fn. ; If these structures change, change these two functions. ; WARNING: Don't change the clauses below without reading the Notes ; above! (append (sublis (list (cons 'evfn evfn) (cons 'evfn-lst evfn-lst)) '(((not (consp x)) (not ; (syntaxp (not (equal a ''nil))) (synp 'nil '(syntaxp (not (equal a ''nil))) '(if (not (equal a ''nil)) 't 'nil))) (equal (car x) 'quote) (equal (evfn x a) (evfn (cons (car x) (kwote-lst (evfn-lst (cdr x) a))) 'nil))) ((not (symbolp x)) ; We considered replacing the right-hand side below simply by (cdr (assoc-equal ; x a)), i.e., without making a special case for x = nil. Our motivation was ; an observation from Sol Swords: there is a kind of mismatch between that ; special case for nil on the one hand, and the treating of nil as an ordinary ; variable by sublis-var. Indeed, he went through some effort to deal with ; this mismatch in his community book, ; books/clause-processors/sublis-var-meaning.lisp, using a hypothesis (not ; (assoc nil alist)) in some lemmas in that book. ; However, if we were to make that change, together with the corresponding ; change in the local witness for the evaluator in the symbolp case, then the ; preceding clause (above) would no longer be valid for our local witness. ; Consider for example the case that x is '(binary-+) and a is '((nil . 7)), ; and that evfn is the local witness and understands binary-+. Then the ; left-hand side above is 14 but the right-hand side is 0. A fix is to modify ; the preceding clause by replacing the final 'nil by a (and then dropping the ; syntaxp hypothesis above, and even making this a definition rule with ; :controller-alist mapping the evaluator to (t nil)). But that change would ; make invalid the lemma ev-commutes-car in community book ; books/tools/defevaluator-fast.lisp. It would also require changing some ; hints, for example replacing the :hints in event lemma0, community book ; books/clause-processors/bv-add.lisp, by (("Goal" :expand ((evl x1 env)))). ; Who knows how many books might be affected, including some user books not in ; the regression suite? So we have decided to leave well enough alone, at ; least for now. If later we learn of a reason to reconsider, we may do so. (equal (evfn x a) (if x (cdr (assoc-equal x a)) 'nil))) ((not (consp x)) (not (equal (car x) 'quote)) (equal (evfn x a) (car (cdr x)))) ((not (consp x)) (not (consp (car x))) (equal (evfn x a) (evfn (car (cdr (cdr (car x)))) (pairlis$ (car (cdr (car x))) (evfn-lst (cdr x) a))))) ((consp x-lst) (equal (evfn-lst x-lst a) 'nil)) ((not (consp x-lst)) (equal (evfn-lst x-lst a) (cons (evfn (car x-lst) a) (evfn-lst (cdr x-lst) a)))))) (evaluator-clauses1 evfn fn-args-lst))) ; The function above describes the constraints on an evaluator ; function. The user will define his own evfn and evfn-lst and prove ; the constraint formulas. Later, when evfn is used in an alleged ; :META theorem, we will verify that it is an evaluator by getting its ; constraint, digging the clauses out of it, and comparing them to the ; list above. But in our statement of the constraints we use car/cdr ; nests freely. The user is liable to use cadr nests (or first, ; second, third, etc., which expand to cadr nests). We therefore take ; time out from our development of evaluators and define the functions ; for normalizing the user's cadr nests to car/cdr nests. The ; following code feels really clunky. (defun cdrp (x term) ; We determine whether term is of the form (cdr (cdr ... (cdr x))), ; where there are 0 or more cdrs. (cond ((equal x term) t) ((variablep term) nil) ((fquotep term) nil) ((eq (ffn-symb term) 'cdr) (cdrp x (fargn term 1))) (t nil))) ; A source of confusion the user faces is that he may write ; (eq & 'fn) or (eq 'fn &) where we expect (equal & 'fn). So we ; normalize those too, at the top-level of a clause. We call it ; a term-lst rather than a clause for symmetry with the foregoing. (defun expand-eq-and-atom-term-lst (lst) ; This function scans the clause lst and replaces literals of the ; form (not (eq x 'sym)), (not (eq 'sym x)), and (not (equal 'sym x)) ; by (not (equal x 'sym)). It also replaces literals of the form ; (atom x) by (not (consp x)). (cond ((null lst) nil) (t (let ((rst (expand-eq-and-atom-term-lst (cdr lst))) (lit (car lst))) (case-match lit (('not ('eq x ('quote s))) (cond ((symbolp s) (cons (mcons-term* 'not (mcons-term* 'equal x (list 'quote s))) rst)) ((and (quotep x) (symbolp (cadr x))) (cons (mcons-term* 'not (mcons-term* 'equal (list 'quote s) x)) rst)) (t (cons lit rst)))) (('not ('eq ('quote s) x)) (cond ((symbolp s) (cons (mcons-term* 'not (mcons-term* 'equal x (list 'quote s))) rst)) (t (cons lit rst)))) (('not ('equal ('quote s) x)) (cond ((and (symbolp s) (not (and (quotep x) (symbolp (cadr x))))) (cons (mcons-term* 'not (mcons-term* 'equal x (list 'quote s))) rst)) (t (cons lit rst)))) (('atom x) (cons (mcons-term* 'not (mcons-term* 'consp x)) rst)) (& (cons lit rst))))))) ; And here, at long last, is the function that massages a user's ; alleged evaluator constraint clause so as to unfold all the cadrs ; and cadars of the pseudo-term in question. (defun normalize-alleged-evaluator-clause (clause) ; Supposing clause is an evaluator clause, we make the likely ; transformations to remove minor syntactic variants introduced by the ; user. In particular, we eliminate the uses of atom and eq. (expand-eq-and-atom-term-lst clause)) ; And here is how we massage the list of user clauses. (defun normalize-alleged-evaluator-clause-set (lst) (cond ((null lst) nil) (t (cons (normalize-alleged-evaluator-clause (car lst)) (normalize-alleged-evaluator-clause-set (cdr lst)))))) (defun shallow-clausify1 (lst) ; Lst is a list of pairs, each of the form (hyps . concl) as returned ; by unprettyify. We convert it to a list of clauses. (cond ((null lst) nil) (t (conjoin-clause-to-clause-set (add-literal (cdar lst) (dumb-negate-lit-lst (caar lst)) t) (shallow-clausify1 (cdr lst)))))) (defun shallow-clausify (term) ; We extract a set of clauses from term whose conjunction is is ; propositionally equivalent to term. This is like clausify except ; that we are very shallow and stupid. ; Note: Why on earth do we have this function? The intended use for ; this function is to clausify the constraint on an alleged evaluator ; function evfn. The idea is to convert the user's constraint to a ; set of clauses and compare that set to the canonical evaluator ; clauses. Why not just use clausify? If one of the functions ; interpretted by evfn is 'if then our full-blown clausify will break ; that clause apart into two unrecognizable pieces. (shallow-clausify1 (unprettyify term))) ; We next turn to guessing the evfn-lst and list of fns for which evfn ; is an evaluator. Our guesses key on the structure of the clauses ; that constrain evfn. (defun find-evfn-lst-in-clause (evfn cl) ; We are looking for the clause that specifies how evfn evaluates ; a lambda application. That clause will mention evfn-lst, the ; function that evaluates a list of terms. In particular, we scan ; cl looking for the literal ; (equal (evfn x a) ; (evfn (caddar x) ; (pairlis$ (cadar x) ; (evfn-lst (cdr x) a)))) ; except we know that the cadr nests are in car/cdr form if this is a ; good clause. If we find such a literal we use evfn-lst as our ; guess. Otherwise we return nil (cond ((null cl) nil) (t (let ((lit (car cl))) (case-match lit (('equal (!evfn x a) (!evfn ('car ('cdr ('cdr ('car x)))) ('pairlis$ ('car ('cdr ('car x))) (evfn-lst ('cdr x) a)))) (cond ((and (variablep x) (variablep a)) evfn-lst) (t (find-evfn-lst-in-clause evfn (cdr cl))))) (& (find-evfn-lst-in-clause evfn (cdr cl)))))))) (defun guess-evfn-lst-for-evfn (evfn cl-set) ; We look through cl-set for the clause that specifies how evfn ; evaluates lambda applications. That clause mentions evfn-lst and if ; we find it we return the evfn-lst mentioned. Otherwise nil. ; We insist that the clause be of length 3. (cond ((null cl-set) nil) ((and (int= (length (car cl-set)) 3) (find-evfn-lst-in-clause evfn (car cl-set)))) (t (guess-evfn-lst-for-evfn evfn (cdr cl-set))))) (defun find-fn-in-clause (cl wrld) (cond ((null cl) nil) (t (let ((lit (car cl))) (case-match lit (('not ('equal ('car x) ('quote fn))) (cond ((and (variablep x) (symbolp fn) (not (eq fn 'quote)) (function-symbolp fn wrld)) fn) (t (find-fn-in-clause (cdr cl) wrld)))) (& (find-fn-in-clause (cdr cl) wrld))))))) (defun guess-fn-args-lst-for-evfn (cl-set wrld) ; We return a list of ``fn-args'', terms of the form (fn v1 ... vn) where the ; vi are the formals of fn. The list contains a fn-arg for each function ; symbol fn such that some 3 literal clause in cl-set contains a literal of the ; form (not (equal (car x) 'fn)). (cond ((null cl-set) nil) (t (let ((fn (and (int= (length (car cl-set)) 3) (find-fn-in-clause (car cl-set) wrld)))) (cond (fn (cons (mcons-term fn (formals fn wrld)) (guess-fn-args-lst-for-evfn (cdr cl-set) wrld))) (t (guess-fn-args-lst-for-evfn (cdr cl-set) wrld))))))) (defun normalized-evaluator-cl-set (ev wrld) (normalize-alleged-evaluator-clause-set (shallow-clausify (mv-let (sym x) (constraint-info ev wrld) (assert$ (not (eq x *unknown-constraints*)) (cond (sym (conjoin x)) (t x))))))) (defun chk-evaluator (evfn wrld ctx state) ; Evfn must be a function symbol. We check that evfn is an evaluator ; function in wrld, or else we cause an error. To be an evaluator ; function evfn must be a function symbol and there must exist another ; symbol, evfn-lst, and a list of function symbols, fns, such that the ; constraints on evfn and evfn-lst are equivalent to the evaluator ; clauses for evfn, evfn-lst and fns. ; What do we mean by the constraints being "equivalent" to the evaluator ; clauses? We convert the two constraint formulas to sets of clauses ; with shallow-clausify. Then we expand the cadrs in the user's set. ; Then we do a bi-directional subsumption check on the evaluator clauses. ; By doing a subsumption check we permit the user to use any variable ; names he wishes and to order his clauses and the literals within his ; clauses any way he wishes. ; However, before we can do that we have to decide what evfn-lst and ; fns we will use. We guess, by inspecting the constraints of evfn. ; If our guess is wrong we'll just end up saying that evfn is not an ; evaluator fn. If our guess is right, we'll confirm it by the subsumption ; check. So the guessing method is technically unimportant. However, we ; believe it is complete: if there exist suitable evfn-lst and fns, ; we find them. (let ((cl-set0 (normalized-evaluator-cl-set evfn wrld)) (str "The symbol ~x0, playing the role of an evaluator in your alleged ~ theorem, does not pass the test for an evaluator. See :DOC meta ~ and :DOC defevaluator. The constraint on ~x0 is in fact ~p1. ~@2") ) (cond ((< (length cl-set0) 4) (er soft ctx str evfn (prettyify-clause-set cl-set0 nil wrld) "This constraint has fewer than four conjuncts.")) (t (let ((evfn-lst (guess-evfn-lst-for-evfn evfn cl-set0))) (cond ((null evfn-lst) (er soft ctx str evfn (prettyify-clause-set cl-set0 nil wrld) "We cannot find the formula describing how to ~ evaluate lambda applications.")) (t (let* ((fn-args-lst (guess-fn-args-lst-for-evfn cl-set0 wrld)) (cl-set1 (conjoin-clause-sets cl-set0 (normalized-evaluator-cl-set evfn-lst wrld))) (cl-set2 (remove-guard-holders-lst-lst (evaluator-clauses evfn evfn-lst fn-args-lst)))) (cond ((not (and (clause-set-subsumes nil cl-set1 cl-set2) (clause-set-subsumes nil cl-set2 cl-set1))) (er soft ctx "If ~x0 is an evaluator then it recognizes ~#1~[no ~ function symbols~/only the function symbol ~&2~/the ~ function symbols ~&2~] and its mutually recursive ~ counterpart for lists of terms must be ~x3. The ~ constraints on ~x0 and ~x3 must therefore be ~ ~P45.~|~%We would recognize ~x0 and ~x3 as evaluators ~ if the constraints on them subsumed and were subsumed ~ by the constraints above. But, in fact, the ~ constraints on ~x0 and ~x3 are ~P65 and the ~ bidirectional subsumption check fails. See :DOC ~ defevaluator." evfn (zero-one-or-more fn-args-lst) (strip-cars fn-args-lst) evfn-lst (prettyify-clause-set cl-set2 nil wrld) (term-evisc-tuple nil state) (prettyify-clause-set cl-set1 nil wrld))) (t (value nil))))))))))) ; To make it easier to introduce an evaluator, we define the following ; macro. (defun defevaluator-form/defthms (evfn prefix i clauses) (cond ((null clauses) nil) (t (cons (list* (if (eql i 0) 'defthmd 'defthm) (genvar evfn prefix i nil) (prettyify-clause (car clauses) nil nil) (and (eql i 0) `(:hints (("Goal" :expand ((:free (x) (hide x)) (,evfn x a)) :in-theory (disable (:executable-counterpart ,evfn))))))) (defevaluator-form/defthms evfn prefix (1+ i) (cdr clauses)))))) (defun defevaluator-form/fns-clauses (evfn fn-args-lst) ; We return a list of cond clauses, ; ( ; ((equal (car x) 'fn1) ; (fn1 (evfn (cadr x) a) ... (evfn (cad...dr x) a))) ; ((equal (car x) 'fn2) ; (fn2 (evfn (cadr x) a) ... (evfn (cad...dr x) a))) ; ... ; (t nil)) ; containing a clause for each fni in fns plus a final t clause. (cond ((null fn-args-lst) '((t nil))) (t (cons (list (list 'equal '(car x) (kwote (caar fn-args-lst))) (cons (caar fn-args-lst) (evaluator-clause/arglist evfn (cdar fn-args-lst) '(cdr x)))) (defevaluator-form/fns-clauses evfn (cdr fn-args-lst)))))) (defconst *defevaluator-form-base-theory* (append *definition-minimal-theory* '(car-cdr-elim car-cons cdr-cons o< o-finp o-first-expt o-first-coeff o-rst natp posp acl2-count alistp fix-true-list kwote kwote-lst pairlis$-fix-true-list (:type-prescription acl2-count)))) (defun defevaluator-form (evfn evfn-lst fn-args-lst) (let* ((clauses (evaluator-clauses evfn evfn-lst fn-args-lst)) (fns-clauses (defevaluator-form/fns-clauses evfn fn-args-lst)) (defthms (defevaluator-form/defthms evfn (symbol-name (pack2 evfn '-constraint-)) 0 clauses))) `(encapsulate (((,evfn * *) => *) ((,evfn-lst * *) => *)) (set-inhibit-warnings "theory") (local (in-theory *defevaluator-form-base-theory*)) ,@(sublis (list (cons 'evfn evfn) (cons 'evfn-lst evfn-lst) (cons 'fns-clauses fns-clauses) (cons 'defthms defthms)) '((local (mutual-recursion (defun-nx evfn (x a) (declare (xargs :verify-guards nil :measure (acl2-count x) :well-founded-relation o< :mode :logic)) (cond ((symbolp x) ; Before removing the conjunct of x below, see the long comment in ; evaluator-clauses about "without making a special case for x = nil". (and x (cdr (assoc-eq x a)))) ((atom x) nil) ((eq (car x) 'quote) (car (cdr x))) ((consp (car x)) (evfn (car (cdr (cdr (car x)))) (pairlis$ (car (cdr (car x))) (evfn-lst (cdr x) a)))) . fns-clauses)) (defun-nx evfn-lst (x-lst a) (declare (xargs :measure (acl2-count x-lst) :well-founded-relation o<)) (cond ((endp x-lst) nil) (t (cons (evfn (car x-lst) a) (evfn-lst (cdr x-lst) a))))))) (local (defthm eval-list-kwote-lst (equal (evfn-lst (kwote-lst args) a) (fix-true-list args)))) . defthms))))) (defun pairs-to-macro-alias-msgs (alist) (declare (xargs :guard (symbol-alistp alist))) (cond ((endp alist) nil) (t (cons (msg "~x0 is a macro alias for function ~x1" (caar alist) (cdar alist)) (pairs-to-macro-alias-msgs (cdr alist)))))) (defun defevaluator-check-msg (alist macro-aliases wrld bad macro-alist) (declare (xargs :guard (and (symbol-alistp alist) (symbol-alistp macro-aliases) (plist-worldp wrld) (symbol-listp bad) (symbol-alistp macro-alist)))) (cond ((endp alist) (cond ((or bad macro-alist) (msg "~@0~@1" (cond ((null bad) "") ((null (cdr bad)) (msg "The symbol ~x0 is not a function symbol in ~ the current ACL2 world." (car bad))) (t (msg "The symbols ~&0 are not function symbols in ~ the current ACL2 world." bad))) (cond ((null macro-alist) "") (t (msg " Note that ~*0." (list "" ; nothing to print "~@*" ; last element "~@*, and " ; 2nd to last element "~@*" ; all other elements (pairs-to-macro-alias-msgs macro-alist))))))) (t nil))) ((function-symbolp (caar alist) wrld) (defevaluator-check-msg (cdr alist) macro-aliases wrld bad macro-alist)) (t (defevaluator-check-msg (cdr alist) macro-aliases wrld (cons (caar alist) bad) (let ((entry (assoc-eq (caar alist) macro-aliases))) (cond (entry (cons entry macro-alist)) (t macro-alist))))))) (defun defevaluator-check (x evfn evfn-lst fn-args-lst ctx state) (declare (xargs :guard (and (state-p state) (symbol-alistp fn-args-lst) (symbol-alistp (fgetprop 'macro-aliases-table 'table-alist nil (w state)))))) (cond ((not (and (symbolp evfn) (symbolp evfn-lst) (symbol-list-listp fn-args-lst))) (er soft ctx "The form of a defevaluator event is (defevaluator evfn evfn-lst ~ fn-args-lst), where evfn and evfn-lst are symbols and ~ fn-args-lst is a true list of lists of symbols. However, ~x0 ~ does not have this form." x)) (t (let* ((wrld (w state)) (msg (defevaluator-check-msg fn-args-lst (macro-aliases wrld) wrld nil nil))) (cond (msg (er soft ctx "~@0" msg)) (t (value nil))))))) (defun defevaluator-check-form (x evfn evfn-lst fn-args-lst) (declare (xargs :guard t)) `(with-output :off error :stack :push (make-event (er-progn (with-output :stack :pop (defevaluator-check ',x ',evfn ',evfn-lst ',fn-args-lst '(defevaluator . ,evfn) state)) (value '(value-triple nil)))))) (defmacro defevaluator (&whole x evfn evfn-lst fn-args-lst &key skip-checks) ; Note: It might be nice to allow defevaluator to take a :DOC string, but that ; would require allowing encapsulate to take such a string! ":Doc-Section Events introduce an evaluator function~/ ~bv[] Example: (defevaluator evl evl-list ((length x) (member-equal x y))) ~ev[] ~l[meta].~/ ~bv[] General Form: (defevaluator ev ev-list ((g1 x1 ... xn_1) ... (gk x1 ... xn_k)) ~ev[] where ~c[ev] and ~c[ev]-list are new function symbols and ~c[g1], ..., ~c[gk] are old function symbols with the indicated number of formals, i.e., each ~c[gi] has ~c[n_i] formals. This function provides a convenient way to constrain ~c[ev] and ~c[ev-list] to be mutually-recursive evaluator functions for the symbols ~c[g1], ..., ~c[gk]. Roughly speaking, an evaluator function for a fixed, finite set of function symbols is a restriction of the universal evaluator to terms composed of variables, constants, lambda expressions, and applications of the given functions. However, evaluator functions are constrained rather than defined, so that the proof that a given metafunction is correct vis-a-vis a particular evaluator function can be lifted (by functional instantiation) to a proof that it is correct for any larger evaluator function. ~l[meta] for a discussion of metafunctions. ~c[Defevaluator] executes an ~ilc[encapsulate] after generating the appropriate ~ilc[defun] and ~ilc[defthm] events. Perhaps the easiest way to understand what ~c[defevaluator] does is to execute the keyword command ~bv[] :trans1 (defevaluator evl evl-list ((length x) (member-equal x y))) ~ev[] and inspect the output. This trick is also useful in the rare case that the event fails because a hint is needed. In that case, the output of ~c[:]~ilc[trans1] can be edited by adding hints, then submitted directly. Formally, ~c[ev] is said to be an ``evaluator function for ~c[g1], ..., ~c[gk], with mutually-recursive counterpart ~c[ev-list]'' iff ~c[ev] and ~c[ev-list] are constrained functions satisfying just the ~il[constraint]s discussed below. ~c[Ev] and ~c[ev-list] must satisfy ~il[constraint]s (0)-(4) and (k): ~bv[] (0) How to ev an arbitrary function application: (implies (and (consp x) (syntaxp (not (equal a ''nil))) (not (equal (car x) 'quote))) (equal (ev x a) (ev (cons (car x) (kwote-lst (ev-list (cdr x) a))) nil))) (1) How to ev a variable symbol: (implies (symbolp x) (equal (ev x a) (and x (cdr (assoc-equal x a))))) (2) How to ev a constant: (implies (and (consp x) (equal (car x) 'quote)) (equal (ev x a) (cadr x))) (3) How to ev a lambda application: (implies (and (consp x) (consp (car x))) (equal (ev x a) (ev (caddar x) (pairlis$ (cadar x) (ev-list (cdr x) a))))) (4) How to ev an argument list: (implies (consp x-lst) (equal (ev-list x-lst a) (cons (ev (car x-lst) a) (ev-list (cdr x-lst) a)))) (implies (not (consp x-lst)) (equal (ev-list x-lst a) nil)) (k) For each i from 1 to k, how to ev an application of gi, where gi is a function symbol of n arguments: (implies (and (consp x) (equal (car x) 'gi)) (equal (ev x a) (gi (ev x1 a) ... (ev xn a)))), where xi is the (cad...dr x) expression equivalent to (nth i x). ~ev[] ~c[Defevaluator] defines suitable witnesses for ~c[ev] and ~c[ev-list], proves the theorems about them, and constrains ~c[ev] and ~c[ev-list] appropriately. We expect ~c[defevaluator] to work without assistance from you, though the proofs do take some time and generate a lot of output. The proofs are done in the context of a fixed theory, namely the value of the constant ~c[*defevaluator-form-base-theory*]. (Aside: (3) above may seem surprising, since the bindings of ~c[a] are not included in the environment that is used to evaluate the lambda body, ~c[(caddar x)]. However, ACL2 lambda expressions are all ~em[closed]: in ~c[(lambda (v1 ... vn) body)], the only free variables in ~c[body] are among the ~c[vi]. ~l[term].)" ; This function executes an encapsulate that defines an evaluator ; evfn (with mutually recursive counterpart evfn-lst for lists of ; terms) that recognizes the functions in fns. (let ((form (defevaluator-form evfn evfn-lst fn-args-lst))) (cond (skip-checks form) (t `(progn ,(defevaluator-check-form x evfn evfn-lst fn-args-lst) ,form))))) (deflabel term-table :doc ":Doc-Section switches-parameters-and-modes a table used to validate meta rules~/ ~bv[] Example: (table term-table t '((binary-+ x y) '3 'nil (car x))) ~ev[]~/ ~l[table] for a general discussion of tables and the ~c[table] event used to manipulate tables. The ``~c[term-table]'' is used at the time a meta rule is checked for syntactic correctness. Each proposed metafunction is run on each term in this table, and the result in each case is checked to make sure that it is a ~c[termp] in the current world. In each case where this test fails, a warning is printed. Whenever a metafunction is run in support of the application of a meta rule, the result must be a term in the current world. When the result is not a term, a hard error arises. The ~c[term-table] is simply a means for providing feedback to the user at the time a meta rule is submitted, warning of the definite possibility that such a hard error will occur at some point in the future. The key used in ~c[term-table] is arbitrary. The top-most value is always the one that is used; it is the entire list of terms to be considered. Each must be a ~c[termp] in the current ACL2 world.~/") (table term-table nil nil :guard (term-listp val world)) (table term-table t '((binary-+ x y) (binary-* '0 y) (car x))) (defun remove-meta-extract-contextual-hyps (hyps ev mfc-symbol a) ; Return (mv hyps' flg), where hyps' is the result of removing suitable ; meta-extract-contextual-fact hypotheses from hyps and flg is true if and only ; if at least one such hypothesis was removed. Ev is the evaluator function ; symbol and mfc-symbol is either nil or the mfc from the conclusion of a rule ; of class :meta. See also remove-meta-extract-global-hyps for an ; corresponding function for global hypotheses. (cond ((atom hyps) (mv nil nil)) (t (let ((hyp (car hyps))) (mv-let (hs flg) (remove-meta-extract-contextual-hyps (cdr hyps) ev mfc-symbol a) (case-match hyp ((!ev ('meta-extract-contextual-fact & !mfc-symbol ; Note that meta-extract-contextual-fact calls mfc- functions, which get their ; world from the mfc, not the state (at least as of this writing, on ; 4/17/2013). Thus, we believe that meta-extract-contextual-fact is correct ; regardless of the state argument. This belief allows us to loosen the ; restriction that the state is 'state, and instead allow an arbitrary state ; here. But we keep the restriction that state is 'state; we may more ; carefullly consider relaxing it upon request. 'state) !a) (mv hs t)) (& (mv (cons hyp hs) flg)))))))) (defun remove-meta-extract-global-hyps (hyps ev) ; Return (mv hyps' flg), where hyps' is the result of removing suitable ; meta-extract-global-fact+ hypotheses from hyps and flg is true if and only if ; at least one such hypothesis was removed. Ev is the evaluator function ; symbol. See also remove-meta-extract-contextual-hyps for an analogous ; function. (declare (xargs :mode :program)) (cond ((atom hyps) (mv nil nil)) (t (let ((hyp (car hyps))) (mv-let (hs flg) (remove-meta-extract-global-hyps (cdr hyps) ev) (case-match hyp ((!ev ('meta-extract-global-fact+ & & 'state) &) (mv hs t)) (& (mv (cons hyp hs) flg)))))))) (defun meta-rule-hypothesis-functions (hyp ev x a mfc-symbol) ; Here hyp is the hypothesis of the proposed meta rule (or, *t* if ; there is none). We want to identify the hypothesis metafunction ; (see :DOC meta) of that rule. We return nil if the hyp is ; unacceptable, t if there is no extra hypothesis, and otherwise the ; hypothesis function. Note that we allow, but do not require, the ; hypotheses (pseudo-termp x) and (alistp a) to be among the ; hypotheses, in which case we delete them before returning the ; result. ; If mfc-symbol is non-nil, this is an extended metafunction and we ; insist that the hyp function be extended also. All extended ; functions take three arguments, the term, the context, and STATE, in ; that order. The value of mfc-symbol is the variable symbol used to ; denote the context. (let ((hyps (remove1-equal (fcons-term* 'pseudo-termp x) (remove1-equal (fcons-term* 'alistp a) (flatten-ands-in-lit hyp))))) (mv-let (hyps flg1) (if mfc-symbol (remove-meta-extract-contextual-hyps hyps ev mfc-symbol a) (mv hyps nil)) (mv-let (hyps flg2) (remove-meta-extract-global-hyps hyps ev) (let ((hyp3 (car hyps)) (extended-args (if mfc-symbol (cons mfc-symbol '(STATE)) nil))) (mv (cond ((null hyps) t) (t (and (null (cdr hyps)) (case-match hyp3 ((!ev (fn !x . !extended-args) !a) fn) (& nil))))) (append (and flg1 '(meta-extract-contextual-fact)) (and flg2 '(meta-extract-global-fact+))))))))) (defun meta-fn-args (term extendedp ens state) (cond (extendedp (let ((wrld (w state))) (list term (make metafunction-context :rdepth (rewrite-stack-limit wrld) :type-alist nil :obj '? :geneqv nil :wrld wrld :fnstack nil :ancestors nil :simplify-clause-pot-lst nil :rcnst (make-rcnst ens wrld :force-info t :top-clause (list term) :current-clause (list term)) :gstack nil :ttree nil :unify-subst nil) (coerce-state-to-object state)))) (t (list term)))) (defun chk-meta-function (metafn name trigger-fns extendedp term-list ctx ens state) ; If extendedp is nil we call metafn on only one term arg. Otherwise, we call ; it on args of the type: (term context state). We manufacture a trivial ; context. We don't care what non-nil value extendedp is. (cond ((null term-list) (value nil)) ((or (variablep (car term-list)) (fquotep (car term-list)) (flambda-applicationp (car term-list)) (not (member-eq (ffn-symb (car term-list)) trigger-fns))) (chk-meta-function metafn name trigger-fns extendedp (cdr term-list) ctx ens state)) (t (let ((args (meta-fn-args (car term-list) extendedp ens state))) (pprogn (cond ((warning-disabled-p "Meta") state) (t (mv-let (erp val latches) (ev-fncall-meta metafn args state) (declare (ignore latches)) (cond (erp ; We use warnings rather than errors when the checks fail, partly so ; that we can feel free to change the checks without changing what the ; prover will accept. Put differently, we don't want user-managed ; tables to affect what the prover is able to prove. (warning$ ctx ("Meta") "An error occurred upon running the metafunction ~ ~x0 on the term ~x1. This does not bode well ~ for the utility of this metafunction for the ~ meta rule ~x2. See :DOC term-table." metafn (car term-list) name)) ((termp val (w state)) state) (t (warning$ ctx ("Meta") "The value obtained upon running the ~ metafunction ~x0 on the term ~x1 is ~x2, which ~ is NOT a termp in the current ACL2 world. This ~ does not bode well for the utility of this ~ metafunction for the meta rule ~x3. See :DOC ~ term-table." metafn (car term-list) val name)))))) (chk-meta-function metafn name trigger-fns extendedp (cdr term-list) ctx ens state)))))) (defun ev-lst-from-ev (ev wrld) ; We expect already to have checked that ev has a known constraint (see assert$ ; call below). (guess-evfn-lst-for-evfn ev (normalized-evaluator-cl-set ev wrld))) (defun attached-fns (fns wrld) (cond ((endp fns) nil) (t (let ((prop (attachment-alist (car fns) wrld))) (cond ((or (null prop) (and (consp prop) (eq (car prop) :attachment-disallowed))) (attached-fns (cdr fns) wrld)) (t (cons (car fns) (attached-fns (cdr fns) wrld)))))))) (defun siblings (f wrld) (or (getprop f 'siblings nil 'current-acl2-world wrld) (getprop f 'recursivep nil 'current-acl2-world wrld) (list f))) (defun canonical-sibling (f wrld) (let ((sibs (getprop f 'siblings nil 'current-acl2-world wrld))) (cond (sibs (car sibs)) (t (let ((sibs (getprop f 'recursivep nil 'current-acl2-world wrld))) (cond (sibs (car sibs)) (t f))))))) (mutual-recursion (defun canonical-ffn-symbs (term wrld ans ignore-fns rlp) ; For a discussion of rlp, see the end of the Essay on Correctness of Meta ; Reasoning. (cond ((variablep term) ans) ((fquotep term) ans) ((and rlp (eq (ffn-symb term) 'return-last) (not (equal (fargn term 1) ''mbe1-raw))) (canonical-ffn-symbs (fargn term 3) wrld ans ignore-fns rlp)) (t (canonical-ffn-symbs-lst (fargs term) wrld (cond ((flambda-applicationp term) (canonical-ffn-symbs (lambda-body (ffn-symb term)) wrld ans ignore-fns rlp)) (t (let ((fn (canonical-sibling (ffn-symb term) wrld))) (cond ((member-eq fn ignore-fns) ans) (t (add-to-set-eq fn ans)))))) ignore-fns rlp)))) (defun canonical-ffn-symbs-lst (lst wrld ans ignore-fns rlp) (cond ((null lst) ans) (t (canonical-ffn-symbs-lst (cdr lst) wrld (canonical-ffn-symbs (car lst) wrld ans ignore-fns rlp) ignore-fns rlp)))) ) (defun collect-canonical-siblings (fns wrld ans ignore-fns) (cond ((endp fns) ans) (t (collect-canonical-siblings (cdr fns) wrld (let ((fn (canonical-sibling (car fns) wrld))) (cond ((or (member-eq fn ignore-fns) (member-eq fn ans)) ans) (t (cons fn ans)))) ignore-fns)))) (defun immediate-canonical-ancestors (fn wrld ignore-fns rlp) ; This function is analogous to immediate-instantiable-ancestors, but it ; traffics entirely in canonical functions and is not concerned with the notion ; of instantiablep. To see why guards are involved, see the reference to the ; Essay on Correctness of Meta Reasoning in the Essay on Defattach, which also ; explains special handling of return-last, performed here when rlp is true. (let ((guard-anc (canonical-ffn-symbs (guard fn nil wrld) wrld nil ignore-fns rlp))) (mv-let (name x) (constraint-info fn wrld) (cond ((eq x *unknown-constraints*) (let* ((cl-proc (getprop name 'constrainedp '(:error "See immediate-canonical-ancestors: ~ expected to find a 'constrainedp property ~ where we did not.") 'current-acl2-world wrld)) (supporters (unknown-constraint-supporters cl-proc wrld))) (collect-canonical-siblings supporters wrld guard-anc ignore-fns))) (name (canonical-ffn-symbs-lst x wrld guard-anc ignore-fns rlp)) (t (canonical-ffn-symbs x wrld guard-anc ignore-fns rlp)))))) (defun canonical-ancestors-rec (fns wrld ans rlp) ; See canonical-ancestors. Unlike that function, it includes fns in the ; result, and it assumes that all functions in fns are canonical. (cond ((null fns) ans) ((member-eq (car fns) ans) (canonical-ancestors-rec (cdr fns) wrld ans rlp)) (t (let* ((ans1 (cons (car fns) ans)) (imm (immediate-canonical-ancestors (car fns) wrld ans1 rlp)) (ans2 (canonical-ancestors-rec imm wrld ans1 rlp))) (canonical-ancestors-rec (cdr fns) wrld ans2 rlp))))) (defun canonical-ancestors (fn wrld rlp) ; This function is completely analogous to instantiable-ancestors, except that ; it takes a single function that is not included in the result, it traffics ; entirely in canonical functions, and it is not concerned with the notion of ; instantiablep. It assumes that fn is canonical. ; For a discussion of rlp, see the end of the Essay on Correctness of Meta ; Reasoning. (let* ((imm (immediate-canonical-ancestors fn wrld (list fn) rlp))) (canonical-ancestors-rec imm wrld nil rlp))) (defun canonical-ancestors-lst (fns wrld) ; Fns is a set of function symbols, not necessarily canonical. We return all ; canonical ancestors of fns. (canonical-ancestors-rec (collect-canonical-siblings fns wrld nil nil) wrld nil t)) (defun chk-evaluator-use-in-rule (name meta-fn hyp-fn extra-fns rule-type ev ctx wrld state) (er-progn (let ((temp (context-for-encapsulate-pass-2 (decode-logical-name ev wrld) (f-get-global 'in-local-flg state)))) (case temp (illegal (er soft ctx ; see comment in defaxiom-supporters "The proposed ~x0 rule, ~x1, is illegal because its evaluator ~ function symbol, ~x2, is defined in a superior non-trivial ~ encapsulate event (``non-trivial'' in the sense that it has a ~ non-empty signature). See :DOC evaluator-restrictions. In some ~ cases, a solution is to make the current ~x0 rule LOCAL, though ~ the alleged evaluator will probably not be available for future ~ :META or :CLAUSE-PROCESSOR rules." rule-type name ev)) (maybe (pprogn (warning$ ctx nil ; add a string here if someone wants to turn this off "The proposed ~x0 rule will ultimately need to be LOCAL in ~ its immediately surrounding encapsulate event, because ~ its evaluator is introduced in a superior non-trivial ~ encapsulate event. Even if this rule is LOCAL, the ~ alleged evaluator will probably not be available for ~ future :META or :CLAUSE-PROCESSOR rules. See :DOC ~ evaluator-restrictions." rule-type name ev) (value nil))) (otherwise (value nil)))) (mv-let (fn constraint) (constraint-info ev wrld) (declare (ignore fn)) (cond ((eq constraint *unknown-constraints*) (er soft ctx ; see comment in defaxiom-supporters "The proposed ~x0 rule, ~x1, is illegal because its evaluator ~ function symbol, ~x2, is constrained by the (unknown) theory of a ~ dependent clause-processor, ~x3. See :DOC clause-processor." rule-type name ev (getprop ev 'constrainedp '(:error "See chk-evaluator-use-in-rule: expected to find ~ a 'constrainedp property where we did not.") 'current-acl2-world wrld))) (t (let* ((ev-lst (ev-lst-from-ev ev wrld)) (ev-prop (getprop ev 'defaxiom-supporter nil 'current-acl2-world wrld)) (ev-lst-prop (getprop ev-lst 'defaxiom-supporter nil 'current-acl2-world wrld)) (ev-fns (list ev ev-lst)) (meta-fn-lst (if hyp-fn (list meta-fn hyp-fn) (list meta-fn))) (meta-anc (canonical-ancestors-lst meta-fn-lst wrld)) (extra-anc (canonical-ancestors-lst extra-fns wrld)) (ev-anc (canonical-ancestors-lst (list ev) wrld))) (cond ((and extra-fns (or (getprop ev 'predefined nil 'current-acl2-world wrld) (getprop ev-lst 'predefined nil 'current-acl2-world wrld))) ; See the comment below about this case in the comment in a case below, where ; we point out that extra-fns are defined in the boot-strap world. (er soft ctx "The proposed evaluator function, ~x0, was defined in the ~ boot-strap world. This is illegal when meta-extract hyotheses ~ are present, because for logical reasons our implementation ~ assumes that the evaluator is not ancestral in ~v1." (if (getprop ev 'predefined nil 'current-acl2-world wrld) ev ev-lst) '(meta-extract-contextual-fact meta-extract-global-fact+))) ((or ev-prop ev-lst-prop) (er soft ctx ; see comment in defaxiom-supporters "The proposed ~x0 rule, ~x1, is illegal because its evaluator ~ function symbol, ~x2, supports the formula of the defaxiom ~ event named ~x3. See :DOC evaluator-restrictions." rule-type name (if ev-prop ev ev-lst) (or ev-prop ev-lst-prop))) ((intersectp-eq ev-fns meta-anc) ; As explained in defaxiom-supporters, we might expect also to check here that ; ev and ev-lst are not ancestral in extra-fns. But extra-fns are defined in ; the boot-strap world while ev and ev-lst, as we check above, are not. ; It would be nice to improve the following error message by finding the ; particular function symbol in the meta or clause-processor rule for which ev ; is ancestral. (er soft ctx ; see comment in defaxiom-supporters "The proposed ~x0 rule, ~x1, is illegal because its ~ evaluator~#2~[~/ (list)~] function symbol, ~x3, supports the ~ definition of the rule's metafunction~#4~[~/s~], ~&4. See ~ :DOC evaluator-restrictions." rule-type name (if (member-eq ev meta-anc) 0 1) (if (member-eq ev meta-anc) ev ev-lst) meta-fn-lst)) (t ; We would like to be able to use attachments where possible. However, the ; example at the end of :doc evaluator-restrictions shows that this is unsound ; in general and is followed by other relevant remarks. (let ((bad-attached-fns-1 (attached-fns (intersection-eq ev-anc meta-anc) wrld)) (bad-attached-fns-2 ; Although we need bad-attached-fns-2 to be empty (see the Essay on Correctness ; of Meta Reasoning), we could at the very least store extra-anc in the world, ; based on both meta-extract-contextual-fact and meta-extract-global-fact+, so ; that we don't have to compute extra-anc every time. But that check is ; probably cheap, so we opt for simplicity. (attached-fns (intersection-eq extra-anc meta-anc) wrld))) (cond ((or bad-attached-fns-1 bad-attached-fns-2) (let ((msg "because the attached function~#0~[~/s~] ~&0 ~ ~#0~[is~/are~] ancestral in both the ~@1 and ~@2 ~ functions") (type-string (if (eq rule-type :meta) "meta" "clause-processor"))) (er soft ctx ; see comment in defaxiom-supporters "The proposed ~x0 rule, ~x1, is illegal ~@2~@3. See ~ :DOC evaluator-restrictions." rule-type name (msg msg (or bad-attached-fns-1 bad-attached-fns-2) (if bad-attached-fns-1 "evaluator" "meta-extract") type-string) (cond ((and bad-attached-fns-1 bad-attached-fns-2) (msg ", and because ~@0" (msg msg bad-attached-fns-2 "meta-extract" type-string))) (t ""))))) (t (value nil)))))))))))) (defun chk-rule-fn-guard (function-string rule-type fn ctx wrld state) ; At one time we insisted that fn not have a non-nil value for its 'constrained ; or 'non-executablep property. With the advent of defattach, a constrained ; function may however be a reasonable choice. Rather than do an elaborate ; check here on exactly what sort of constrained function might be attachable, ; we trust that the writer of :meta and :clause-processor rules knows better ; than to attach to functions that cannot be executed. (let ((guard (guard fn t wrld)) (pseudo-termp-predicate (case rule-type (:meta 'pseudo-termp) (:clause-processor 'pseudo-term-listp) (t (er hard 'chk-rule-fn-guard "Implementation error: unknown case in chk-rule-fn-guard. ~ Please contact the ACL2 implementors."))))) (cond ((or (equal guard *t*) (tautologyp (fcons-term* 'implies (fcons-term* pseudo-termp-predicate (car (formals fn wrld))) guard) wrld)) (value nil)) (t (er soft ctx "The ~s0 of a ~x1 rule must have a guard that obviously ~ holds whenever its first argument is known to be a ~x2 and ~ any stobj arguments are assumed to satisfy their stobj ~ predicates. However, the guard for ~x3 is ~p4. See :DOC ~ ~@5." function-string rule-type pseudo-termp-predicate fn (untranslate guard t wrld) (case rule-type (:meta "meta") (:clause-processor "clause-processor") (t (er hard 'chk-rule-fn-guard "Implementation error: unknown case in ~ chk-rule-fn-guard. Please contact the ACL2 ~ implementors.")))))))) (defun chk-acceptable-meta-rule (name trigger-fns term ctx ens wrld state) (if (member-eq 'IF trigger-fns) (er soft ctx "The function symbol IF is not an acceptable member of ~ :trigger-fns, because the ACL2 simplifier is not set up to apply ~ :meta rules to calls of IF.") (let ((str "No :META rule can be generated from ~x0 because ~p1 does not ~ have the form of a metatheorem. See :DOC meta.")) (mv-let (hyp eqv ev x a fn mfc-symbol) (case-match term (('implies hyp (eqv (ev x a) (ev (fn x) a))) (mv hyp eqv ev x a fn nil)) ((eqv (ev x a) (ev (fn x) a)) (mv *t* eqv ev x a fn nil)) (('implies hyp (eqv (ev x a) (ev (fn x mfc-symbol 'STATE) a))) (mv hyp eqv ev x a fn mfc-symbol)) ((eqv (ev x a) (ev (fn x mfc-symbol 'STATE) a)) (mv *t* eqv ev x a fn mfc-symbol)) (& (mv *t* nil nil nil nil nil nil))) (cond ((null eqv) (er soft ctx str name (untranslate term t wrld))) ((eq fn 'return-last) ; Ev-fncall-meta calls ev-fncall!. We could make an exception for return-last, ; calling ev-fncall instead, but for now we avoid that runtime overhead by ; excluding return-last. It's a bit difficult to imagine that anyone would ; use return-last as a metafunction anyhow. (er soft ctx "It is illegal to use ~x0 as a metafunction, as specified ~ by ~x1. See :DOC meta." 'return-last name)) ((not (and (not (flambdap eqv)) (equivalence-relationp eqv wrld) (variablep x) (variablep a) (not (eq x a)) (not (eq fn 'quote)) (not (flambdap fn)) (or (null mfc-symbol) (and (variablep mfc-symbol) (no-duplicatesp (list x a mfc-symbol 'STATE)))))) ; Note: Fn must be a symbol, not a lambda expression. That is because ; in rewrite-with-lemma, when we apply the metafunction, we use ev-fncall-meta. (er soft ctx str name (untranslate term t wrld))) ((not (member-equal (stobjs-in fn wrld) '((nil) (nil nil state)))) (er soft ctx "Metafunctions cannot take single-threaded object names ~ other than STATE as formal parameters. The function ~x0 ~ may therefore not be used as a metafunction." fn)) (t (er-progn (chk-rule-fn-guard "metafunction" :meta fn ctx wrld state) (mv-let (hyp-fn extra-fns) (meta-rule-hypothesis-functions hyp ev x a mfc-symbol) (let ((term-list (cdar (table-alist 'term-table (w state))))) (er-progn (cond ((null hyp-fn) (er soft ctx str name (untranslate term t wrld))) ((and (not (eq hyp-fn t)) (not (member-equal (stobjs-in hyp-fn wrld) '((nil) (nil nil state))))) ; It is tempting to avoid the check here that hyp-fn does not take ; stobjs in. After all, we have already checked this for fn, and fn ; and hyp-fn have the same actuals. But our defun warts allow certain ; functions to traffic in stobjs even though they do not use STATE (or ; another stobj name) as a formal. So, we play it safe and check. (er soft ctx "Hypothesis metafunctions cannot take single ~ threaded object names as formal parameters. The ~ function ~x0 may therefore not be used as a ~ hypothesis metafunction." hyp-fn)) ((not (eq hyp-fn t)) (er-progn (chk-evaluator-use-in-rule name fn hyp-fn extra-fns :meta ev ctx wrld state) (chk-rule-fn-guard "hypothesis function" :meta fn ctx wrld state))) (t (chk-evaluator-use-in-rule name fn nil extra-fns :meta ev ctx wrld state))) (chk-evaluator ev wrld ctx state) ; In the code below, mfc-symbol is used merely as a Boolean indicating ; that this is an extended metafunction. (chk-meta-function fn name trigger-fns mfc-symbol term-list ctx ens state) (if (eq hyp-fn t) (value nil) (chk-meta-function hyp-fn name trigger-fns mfc-symbol term-list ctx ens state)))))))))))) ; And to add a :META rule: (defun add-meta-rule1 (lst rule wrld) ; Fn is a function symbol, not a lambda expression. (cond ((null lst) wrld) (t (add-meta-rule1 (cdr lst) rule (putprop (car lst) 'lemmas (cons rule (getprop (car lst) 'lemmas nil 'current-acl2-world wrld)) wrld))))) (defun maybe-putprop-lst (symb-lst key val wrld) (cond ((endp symb-lst) wrld) (t (let ((symb (car symb-lst))) (maybe-putprop-lst (cdr symb-lst) key val (cond ((getprop symb key nil 'current-acl2-world wrld) wrld) (t (putprop symb key val wrld)))))))) (defun mark-attachment-disallowed2 (fns msg wrld) ; It might be that we only need to disallow attachments to constrained ; functions. However, our theory (Essay on Correctness of Meta Reasoning, as ; referenced in chk-evaluator-use-in-rule) doesn't address this possibility, so ; until someone complains we'll keep this simple and disallow attachments for ; each member of fns, whether or not its attachment is used in evaluation. (cond ((endp fns) wrld) (t (mark-attachment-disallowed2 (cdr fns) msg (let ((old-prop (getprop (car fns) 'attachment nil 'current-acl2-world wrld))) (cond ((and (consp old-prop) (eq (car old-prop) :attachment-disallowed)) wrld) (t (putprop (car fns) 'attachment (cons :attachment-disallowed msg) wrld)))))))) (defun mark-attachment-disallowed1 (canonical-fns msg wrld) (cond ((endp canonical-fns) wrld) (t (mark-attachment-disallowed1 (cdr canonical-fns) msg (mark-attachment-disallowed2 (siblings (car canonical-fns) wrld) msg wrld))))) (defun mark-attachment-disallowed (meta-fns ev msg wrld) ; We mark as unattachable all functions ancestral in both meta-fns and ev. We ; obtain that set of common ancestors by restricting first to canonical ; functions, and then taking all siblings (in mark-attachment-disallowed1) ; before marking (in mark-attachment-disallowed2). (mark-attachment-disallowed1 (intersection-eq (canonical-ancestors-lst meta-fns wrld) (canonical-ancestors-lst (list ev) wrld)) msg wrld)) (defun add-meta-rule (rune nume trigger-fns term backchain-limit wrld) (mv-let (hyp eqv ev x a fn mfc-symbol) (case-match term (('implies hyp (eqv (ev x a) (ev (fn x) a))) (mv hyp eqv ev x a fn nil)) ((eqv (ev x a) (ev (fn x) a)) (mv *t* eqv ev x a fn nil)) (('implies hyp (eqv (ev x a) (ev (fn x mfc-symbol 'STATE) a))) (mv hyp eqv ev x a fn mfc-symbol)) ((eqv (ev x a) (ev (fn x mfc-symbol 'STATE) a)) (mv *t* eqv ev x a fn mfc-symbol)) (& (mv *t* nil nil nil nil nil nil))) (mv-let (hyp-fn extra-fns) (meta-rule-hypothesis-functions hyp ev x a mfc-symbol) (declare (ignore extra-fns)) (cond ((or (null hyp-fn) (null eqv)) (er hard 'add-meta-rule "Add-meta-rule broke on args ~x0! It seems to be out of sync with ~ chk-acceptable-meta-rule." (list rune nume trigger-fns term))) (t (add-meta-rule1 trigger-fns (make rewrite-rule :rune rune :nume nume :hyps (if (eq hyp-fn t) nil hyp-fn) :equiv eqv :lhs fn :var-info nil ; unused :rhs (if mfc-symbol 'extended nil) :subclass 'meta :heuristic-info nil :backchain-limit-lst backchain-limit) (mark-attachment-disallowed (if (eq hyp-fn t) (list fn) (list hyp-fn fn)) ev (msg "it supports both evaluator and meta functions ~ used in :META rule ~x0" (base-symbol rune)) wrld))))))) ;--------------------------------------------------------------------------- ; Section: Destructor :ELIM Rules (deflabel elim :doc ":Doc-Section Rule-Classes make a destructor elimination rule~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. The following example of an ~c[:elim] rule is an important one, and is built into ACL2. ~bv[] (defaxiom car-cdr-elim (implies (consp x) (equal (cons (car x) (cdr x)) x)) :rule-classes :elim) ~ev[] The class of ~c[:elim] rules is fundamentally quite different from the more common class of ~c[:]~ilc[rewrite] rules. Briefly put, a ~c[:rewrite] rule replaces instances of its left-hand side with corresponding instances of its right-hand side. But an ~c[:elim] rule, on the other hand, has the effect of generalizing so-called ``destructor'' function applications to variables. In essence, applicability of a ~c[:rewrite] rule is based on matching its left-hand side, while applicability of an ~c[:elim] rule is based on the presence of at least one destructor term. For example, a conjecture about ~c[(car x)] and ~c[(cdr x)] can be replaced by a conjecture about new variables ~c[x1] and ~c[x2], as shown in the following example. (Run the command ~c[:mini-proveall] and search for ~c[CAR-CDR-ELIM] to see the full proof containing this excerpt.) ~bv[] Subgoal *1/1' (IMPLIES (AND (CONSP X) (TRUE-LISTP (REV (CDR X)))) (TRUE-LISTP (APP (REV (CDR X)) (LIST (CAR X))))). The destructor terms (CAR X) and (CDR X) can be eliminated by using CAR-CDR-ELIM to replace X by (CONS X1 X2), (CAR X) by X1 and (CDR X) by X2. This produces the following goal. Subgoal *1/1'' (IMPLIES (AND (CONSP (CONS X1 X2)) (TRUE-LISTP (REV X2))) (TRUE-LISTP (APP (REV X2) (LIST X1)))). This simplifies, using primitive type reasoning, to Subgoal *1/1''' (IMPLIES (TRUE-LISTP (REV X2)) (TRUE-LISTP (APP (REV X2) (LIST X1)))). ~ev[] The resulting conjecture is often simpler and hence more amenable to proof. The application of an ~c[:elim] rule thus replaces a variable by a term that contains applications of so-called ``destructor'' functions to that variable. The example above is typical: the variable ~c[x] is replaced by the term ~c[(cons (car x) (cdr x))], which applies a so-called ``constructor'' function, ~ilc[cons], to applications ~c[(car x)] and ~c[(cdr x)] of destructor functions ~ilc[car] and ~ilc[cdr] to that same variable, ~c[x]. But that is only part of the story. ACL2 then generalizes the destructor applications ~c[(car x)] and ~c[(cdr x)] to new variables ~c[x1] and ~c[x2], respectively, and ultimately the result is a simpler conjecture. More generally, the application of an ~c[:elim] rule replaces a variable by a term containing applications of destructors; there need not be a clear-cut notion of ``constructor.'' But the situation described above is typical, and we will focus on it, giving full details when we introduce the ``General Form'' below. Notice that the situation can be complicated a bit by a rule's hypotheses. For example, the replacement specified by the rule ~c[car-cdr-elim] (shown near the beginning of this discussion) is only valid if the variable being replaced is a cons structure. Thus, when ACL2 applies ~c[car-cdr-elim] to replace a variable ~c[v], it will split into two cases: one case in which ~c[(consp v)] is true, in which ~c[v] is replaced by ~c[(cons (car v) (cdr v))] and then ~c[(car v)] and ~c[(cdr v)] are generalized to new variables; and one case in which ~c[(consp v)] is false. In practice, ~c[(consp v)] is often provable, perhaps even literally present as a hypotheses; then of course there is no need to introduce the second case. That is why there is no such second case in the example above. You might find ~c[:elim] rules to be useful whenever you have in mind a data type that can be built up from its fields with a ``constructor'' function and whose fields can be accessed by corresponding ``destructor'' functions. So for example, if you have a ``house'' data structure that represents a house in terms of its address, price, and color, you might have a rule like the following. ~bv[] Example: (implies (house-p x) (equal (make-house (address x) (price x) (color x)) x)) ~ev[] The application of such a rule is entirely analogous to the application of the rule ~c[car-cdr-elim] discussed above. We discuss such rules and their application more carefully below.~/ ~bv[] General Form: (implies hyp (equiv lhs x)) ~ev[] where ~c[equiv] is a known equivalence relation (~pl[defequiv]); ~c[x] is a variable symbol; and ~c[lhs] contains one or more terms (called ``destructor terms'') of the form ~c[(fn v1 ... vn)], where ~c[fn] is a function symbol and the ~c[vi] are distinct variable symbols, ~c[v1], ..., ~c[vn] include all the variable symbols in the formula, no ~c[fn] occurs in ~c[lhs] in more than one destructor term, and all occurrences of ~c[x] in ~c[lhs] are inside destructor terms. To use an ~c[:elim] rule, the theorem prover waits until a conjecture has been maximally simplified. It then searches for an instance of some destructor term ~c[(fn v1 ... vn)] in the conjecture, where the instance for ~c[x] is some variable symbol, ~c[vi], and every occurrence of ~c[vi] outside the destructor terms is in an ~c[equiv]-hittable position. If such an instance is found, then the theorem prover instantiates the ~c[:elim] formula as indicated by the destructor term matched; splits the conjecture into two goals, according to whether the instantiated hypothesis, ~c[hyp], holds; and in the case that it does hold, generalizes all the instantiated destructor terms in the conjecture to new variables and then replaces ~c[vi] in the conjecture by the generalized instantiated ~c[lhs]. An occurrence of ~c[vi] is ``~c[equiv]-hittable'' if sufficient congruence rules (~pl[defcong]) have been proved to establish that the propositional value of the clause is not altered by replacing that occurrence of ~c[vi] by some ~c[equiv]-equivalent term. If an ~c[:elim] rule is not applied when you think it should have been, and the rule uses an equivalence relation, ~c[equiv], other than ~c[equal], it is most likely that there is an occurrence of the variable that is not ~c[equiv]-hittable. Easy occurrences to overlook are those in the governing hypotheses. If you see an unjustified occurrence of the variable, you must prove the appropriate congruence rule to allow the ~c[:elim] to fire. Further examples of how ACL2 ~c[:elim] rules are used may be found in the corresponding discussion of ``Elimation of Destructors'' for Nqthm, in Section 10.4 of A Computational Logic Handbook.") (mutual-recursion (defun destructors (term ans) ; Union-equal into ans all of the subterms of term of the form (fn v1 ; ... vn) where fn is a symbol and the vi are distinct variables. (cond ((or (variablep term) (fquotep term) (flambda-applicationp term)) ans) (t (destructors-lst (fargs term) (cond ((and (fargs term) (all-variablep (fargs term)) (no-duplicatesp-equal (fargs term))) (add-to-set-equal term ans)) (t ans)))))) (defun destructors-lst (lst ans) (cond ((null lst) ans) (t (destructors-lst (cdr lst) (destructors (car lst) ans))))) ) (defun strip-ffn-symbs (lst) (cond ((null lst) nil) (t (cons (ffn-symb (car lst)) (strip-ffn-symbs (cdr lst)))))) (defun chk-acceptable-elim-rule1 (name vars dests ctx wrld state) (cond ((null dests) (value nil)) ((not (subsetp-eq vars (fargs (car dests)))) (er soft ctx "~x0 is an unacceptable destructor elimination rule because ~ the destructor term ~x1 does not mention ~&2. See :DOC elim." name (car dests) (set-difference-eq vars (fargs (car dests))))) ((getprop (ffn-symb (car dests)) 'eliminate-destructors-rule nil 'current-acl2-world wrld) (er soft ctx "~x0 is an unacceptable destructor elimination rule because ~ we already have a destructor elimination rule for ~x1, ~ namely ~x2, and we do not support more than one elimination rule ~ for the same function symbol." name (ffn-symb (car dests)) (getprop (ffn-symb (car dests)) 'eliminate-destructors-rule nil 'current-acl2-world wrld))) (t (chk-acceptable-elim-rule1 name vars (cdr dests) ctx wrld state)))) (defun chk-acceptable-elim-rule (name term ctx wrld state) (let ((lst (unprettyify term))) (case-match lst (((& . (equiv lhs rhs))) (cond ((not (equivalence-relationp equiv wrld)) (er soft ctx "~x0 is an unacceptable destructor elimination rule ~ because ~x1 is not a known equivalence relation. See ~ :DOC elim." name equiv)) ((nvariablep rhs) (er soft ctx "~x0 is an unacceptable destructor elimination rule ~ because the right-hand side of its conclusion, ~x1, is ~ not a variable symbol. See :DOC elim." name rhs)) (t (let ((dests (destructors lhs nil))) (cond ((null dests) (er soft ctx "~x0 is an unacceptable destructor elimination rule ~ because the left-hand side of its conclusion, ~x1, ~ does not contain any terms of the form (fn v1 ... ~ vn), where fn is a function symbol and the vi are ~ all distinct variables. See :DOC elim." name lhs)) ((not (no-duplicatesp-equal (strip-ffn-symbs dests))) (er soft ctx "~x0 is an unacceptable destructor elimination rule ~ because the destructor terms, ~&1, include more than ~ one occurrence of the same function symbol. See :DOC ~ elim." name dests)) ((occur rhs (sublis-expr (pairlis-x2 dests *t*) lhs)) (er soft ctx "~x0 is an unacceptable destructor elimination rule ~ because the right-hand side of the conclusion, ~x1, ~ occurs in the left-hand side, ~x2, in places other ~ than the destructor term~#3~[~/s~] ~&3. See :DOC ~ elim." name rhs lhs dests)) (t (chk-acceptable-elim-rule1 name (all-vars term) dests ctx wrld state))))))) (& (er soft ctx "~x0 is an unacceptable destructor elimination rule because ~ its conclusion is not of the form (equiv lhs rhs). See ~ :DOC elim." name))))) ; and to add an :ELIM rule: (defun add-elim-rule1 (rune nume hyps equiv lhs rhs lst dests wrld) ; Lst is a tail of dests and contains the destructor terms for which we ; have not yet added a rule. For each destructor in lst we add an elim ; rule to wrld. (cond ((null lst) wrld) (t (let* ((dest (car lst)) (rule (make elim-rule :rune rune :nume nume :hyps hyps :equiv equiv :lhs lhs :rhs rhs :crucial-position (- (length (fargs dest)) (length (member-eq rhs (fargs dest)))) :destructor-term dest :destructor-terms dests))) (add-elim-rule1 rune nume hyps equiv lhs rhs (cdr lst) dests (putprop (ffn-symb dest) 'eliminate-destructors-rule rule wrld)))))) (defun add-elim-rule (rune nume term wrld) (let* ((lst (unprettyify term)) (hyps (caar lst)) (equiv (ffn-symb (cdar lst))) (lhs (fargn (cdar lst) 1)) (rhs (fargn (cdar lst) 2)) (dests (reverse (destructors lhs nil)))) (add-elim-rule1 rune nume hyps equiv lhs rhs dests dests wrld))) ;--------------------------------------------------------------------------- ; Section: :GENERALIZE Rules (deflabel generalize :doc ":Doc-Section Rule-Classes make a rule to restrict generalizations~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example: (defthm integer-listp-rev (implies (integer-listp x) (integer-listp (rev x))) :rule-classes :generalize)~/ General Form: any theorem ~ev[] To use a ~c[:generalize] rule, the system waits until it has decided to generalize some term, ~c[term], by replacing it with some new variable ~c[v]. If any ~c[:generalize] formula can be instantiated so that some non-variable subterm becomes ~c[term], then that instance of the formula is added as a hypothesis. Thus for the example above, if the term ~c[(rev x2)] is generalized to the variable ~c[rv] during a proof, then the following is added as a hypothesis when generalizing to a new goal. ~bv[] (implies (integer-listp x2) (integer-listp rv)) ~ev[] At the moment, the best description of how ACL2 ~c[:generalize] rules are used may be found in the discussion of ``Generalize Rules,'' page 248 of A Computational Logic Handbook, or ``Generalization,'' page 132 of ``Computer-Aided Reasoning: An Approach.'' Also ~pl[introduction-to-the-theorem-prover] for detailed tutorial on using ACL2 to prove theorems, which includes some discussion of generalization.") (defun chk-acceptable-generalize-rule (name term ctx wrld state) ; This function is really a no-op. It exists simply for regularity. (declare (ignore name term ctx wrld)) (value nil)) (defun add-generalize-rule (rune nume term wrld) (global-set 'generalize-rules (cons (make generalize-rule :rune rune :nume nume :formula term) (global-val 'generalize-rules wrld)) wrld)) ;--------------------------------------------------------------------------- ; Section: :TYPE-PRESCRIPTION Rules (deflabel type-prescription :doc ":Doc-Section Rule-Classes make a rule that specifies the type of a term~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Examples: (defthm integerp-foo ; Assumes that foo has been (integerp (foo x y)) ; defined; then, states that :rule-classes :type-prescription) ; (foo x y) is of type integer. (defthm characterp-nth-type-prescription ; (Nth n lst) is of type character (implies ; provided the hypotheses can be (and (character-listp lst) ; established by type reasoning. (<= 0 n) (< n (len lst))) (characterp (nth n lst))) :rule-classes :type-prescription) (defthm characterp-nth-type-prescription-alt ; equivalent to the above (implies (and (character-listp lst) (<= 0 n) (< n (len lst))) (characterp (nth n lst))) :rule-classes ((:type-prescription :typed-term (nth n lst)))) (defthm demodulize-type-for-quote-value ; (Demodulize a lst 'value ans) is (implies ; either a nonnegative integer or (and (atom a) ; of the same type as ans, provided (true-listp lst) ; the hyps can be established by type (member-equal a lst)) ; reasoning (or (and (integerp (demodulize a lst 'value ans)) (>= (demodulize a lst 'value ans) 0)) (equal (demodulize a lst 'value ans) ans))) :rule-classes :type-prescription) ~ev[] To specify the term whose type (~pl[type-set]) is described by the rule, provide that term as the value of the ~c[:typed-term] field of the rule class object.~/ ~bv[] General Form (after preprocessing; see below): (implies hyps (or type-restriction1-on-pat ... type-restrictionk-on-pat (equal pat var1) ... (equal pat varj))) ~ev[] where ~c[pat] is the application of some function symbol to some arguments, each ~c[type-restrictioni-on-pat] is a term involving ~c[pat] and containing no variables outside of the occurrences of ~c[pat], and each ~c[vari] is one of the variables of ~c[pat]. Generally speaking, the ~c[type-restriction] terms ought to be terms that inform us as to the type of ~c[pat]. Ideally, they should be ``primitive recognizing expressions'' about ~c[pat]; ~pl[compound-recognizer]. We describe preprocessing at the end of this topic. If the ~c[:typed-term] is not provided in the rule class object, it is computed heuristically by looking for a term in the conclusion whose type is being restricted. An error is caused if no such term is found. Roughly speaking, the effect of adding such a rule is to inform the ACL2 typing mechanism that ~c[pat] has the type described by the conclusion, when the hypotheses are true. In particular, the type of ~c[pat] is within the union of the types described by the several disjuncts. The ``type described by'' ~c[(equal pat vari)] is the type of ~c[vari]. More operationally, when asked to determine the type of a term that is an instance of ~c[pat], ACL2 will first attempt to establish the hypotheses. ~st[This is done by type reasoning alone, not rewriting!] However, if some hypothesis is a call of ~ilc[force], then forcing may occur, which may ultimately invoke the rewriter; ~pl[force] and ~pl[case-split]. So-called free variables in hypotheses are treated specially; ~pl[free-variables]. Provided the hypotheses are established by type reasoning, ACL2 then unions the types described by the ~c[type-restrictioni-on-pat] terms together with the types of those subexpressions of ~c[pat] identified by the ~c[vari]. The final type computed for a term is the intersection of the types implied by each applicable rule. Type prescription rules may be disabled. You can limit the recursive establishment of hypotheses of rules; ~pl[set-backchain-limit]. Because only type reasoning is used to establish the hypotheses of ~c[:type-prescription] rules, some care must be taken with the hypotheses. Suppose, for example, that the non-recursive function ~c[my-statep] is defined as ~bv[] (defun my-statep (x) (and (true-listp x) (equal (len x) 2))) ~ev[] and suppose ~c[(my-statep s)] occurs as a hypothesis of a ~c[:type-prescription] rule that is being considered for use in the proof attempt for a conjecture with the hypothesis ~c[(my-statep s)]. Since the hypothesis in the conjecture is rewritten, it will become the conjunction of ~c[(true-listp s)] and ~c[(equal (len s) 2)]. Those two terms will be assumed to have type ~c[t] in the context in which the ~c[:type-prescription] rule is tried. But type reasoning will be unable to deduce that ~c[(my-statep s)] has type ~c[t] in this context. Thus, either ~c[my-statep] should be disabled (~pl[disable]) during the proof attempt or else the occurrence of ~c[(my-statep s)] in the ~c[:type-prescription] rule should be replaced by the conjunction into which it rewrites. While this example makes it clear how non-recursive predicates can cause problems, non-recursive functions in general can cause problems. For example, if ~c[(mitigate x)] is defined to be ~c[(if (rationalp x) (1- x) x)] then the hypothesis ~c[(pred (mitigate s))] in the conjecture will rewrite, opening ~c[mitigate] and splitting the conjecture into two subgoals, one in which ~c[(rationalp s)] and ~c[(pred (1- x))] are assumed and the other in which ~c[(not (rationalp s))] and ~c[(pred x)] are assumed. But ~c[(pred (mitigate s))] will not be typed as ~c[t] in either of these contexts. The moral is: beware of non-recursive functions occuring in the hypotheses of ~c[:type-prescription] rules. Because of the freedom one has in forming the conclusion of a type-prescription, we have to use heuristics to recover the pattern, ~c[pat], whose type is being specified. In some cases our heuristics may not identify the intended term and the ~c[:type-prescription] rule will be rejected as illegal because the conclusion is not of the correct form. When this happens you may wish to specify the ~c[pat] directly. This may be done by using a suitable rule class token. In particular, when the token ~c[:type-prescription] is used it means ACL2 is to compute pat with its heuristics; otherwise the token should be of the form ~c[(:type-prescription :typed-term pat)], where ~c[pat] is the term whose type is being specified. The defun event may generate a ~c[:type-prescription] rule. Suppose ~c[fn] is the name of the function concerned. Then ~c[(:type-prescription fn)] is the rune given to the type-prescription, if any, generated for ~c[fn] by ~ilc[defun]. (The trivial rule, saying ~c[fn] has unknown type, is not stored, but ~ilc[defun] still allocates the rune and the corollary of this rune is known to be ~c[t].) We close with a discussion of how, before a term is parsed into a ~c[:type-prescription] rule, it is preprocessed. We describe this preprocessing in some detail below, but first consider the following (contrived) example. ~bv[] (defthm append-tp-example (let ((result (append x y))) (implies (nat-listp x) (implies (let ((second-hyp (integer-listp y))) second-hyp) (true-listp result)))) :rule-classes :type-prescription) ~ev[] This theorem is parsed into a type-prescription rule with the following hypotheses and conclusion. ~bv[] (nat-listp x) ; first hypothesis ((lambda (second-hyp) second-hyp) (integer-listp y)) ; second hypothesis (true-listp (binary-append x y)) ; conclusion ~ev[] Notice that the top-level ~ilc[LET] was expanded, i.e., ~c[(append x y)] was substituted for ~c[result] ~-[] more accurately, ~c[(binary-append x y)] was substituted for ~c[result], since ~ilc[append] is a macro that abbreviates ~ilc[binary-append]. Also notice that the two hypotheses were ``flattened'' in the sense that they were gathered up into a list. Finally, notice that the ~ilc[LET] in the second hypothesis was not expanded (it was merely translated to internal form, using ~c[LAMBDA]). If you actually submit the theorem above, you will get warnings, which you may choose to ignore; the application of ~c[type-prescription] rules is somewhat subtle, so if you use them then you may wish to experiment to see which forms work best for you. Here is the detail promised above, for parsing a term into a ~c[:type-prescription] rule. There are two steps. (1) ACL2 first translates the term, expanding all macros (~pl[trans]) and also expanding away calls of all so-called ``guard holders,'' ~ilc[mv-list] and ~ilc[return-last] (the latter resulting for example from calls of ~ilc[prog2$], ~ilc[mbe], or ~ilc[ec-call]), as well as expansions of the macro `~ilc[the]'. (2) Then the the translated term is traversed top-down, expanding away ~c[lambda]s (~ilc[let], ~ilc[let*], and ~ilc[mv-let] expressions) and flattening the ~ilc[IMPLIES] structure, until the conclusion is exposed; then the conclusion's ~c[lambda]s are also expanded away. The simplest way to understand (2) may be to look at the definition of ACL2 source function ~c[unprettyify-tp], which implements Step (2), say by evaluating ~c[:]~ilc[pe]~c[ unprettyify-tp].~/") (defun find-type-prescription-pat (term ens wrld) ; Suppose term is the translation of a legal type-prescription lemma ; conclusion, e.g., ; (or (rationalp (fn x x y)) ; (and (symbolp (fn x x y)) ; (not (equal (fn x x y) nil))) ; (consp (fn x x y)) ; (equal (fn x x y) y)). ; In general, term will be some IF expression giving type or equality ; information about some function application, e.g., (fn x x y) in the ; example above. This function attempts to identify the term whose ; type is described. The function is merely heuristic in that if it ; fails (returns nil) the user will have to tell us what term to use. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) nil) ((eq (ffn-symb term) 'if) (or (find-type-prescription-pat (fargn term 1) ens wrld) (find-type-prescription-pat (fargn term 2) ens wrld) (find-type-prescription-pat (fargn term 3) ens wrld))) ((eq (ffn-symb term) 'not) (find-type-prescription-pat (fargn term 1) ens wrld)) ((eq (ffn-symb term) '<) (if (quotep (fargn term 1)) (fargn term 2) (fargn term 1))) ((eq (ffn-symb term) 'equal) (cond ((or (variablep (fargn term 1)) (fquotep (fargn term 1))) (fargn term 2)) ((or (variablep (fargn term 2)) (fquotep (fargn term 2))) (fargn term 1)) (t nil))) ((most-recent-enabled-recog-tuple (ffn-symb term) (global-val 'recognizer-alist wrld) ens) (fargn term 1)) (t term))) (defun type-prescription-disjunctp (var term) ; Warning: Keep this function in sync with ; subst-nil-into-type-prescription-disjunct. ; Var is a variable and term is a term. Essentially we are answering ; the question, ``is term a legal disjunct in the conclusion of a ; type-prescription about pat'' for some term pat. However, by this ; time all occurrences of the candidate pat in the conclusion have ; been replaced by some new variable symbol and that symbol is var. ; Furthermore, we will have already checked that the resulting ; generalized concl contains no variables other than var and the ; variables occurring in pat. So what this function actually checks ; is that term is either (equal var other-var), (equal other-var var), ; or else is some arbitrary term whose all-vars is identically the ; singleton list containing var. ; If term is one of the two equality forms above, then we know ; other-var is a variable in pat and that term is one of the disjuncts ; that says ``pat sometimes returns this part of its input.'' If term ; is of the third form, then it might have come from a ; type-restriction on pat, e.g., (and (rationalp pat) (<= pat 0)) or ; (compound-recognizerp pat), or it might be some pretty arbitrary ; term. However, we at least know that it contains no variables at ; all outside the occurrences of pat and that means that we can trust ; type-set-implied-by-term to tell us what this term implies about ; pat. (cond ((variablep term) ; This could be a type-prescription disjunct in the generalized concl ; only if term is var, i.e., the original disjunct was equivalent to ; (not (equal pat 'nil)). (eq term var)) ((fquotep term) nil) ((flambda-applicationp term) nil) (t (or (and (eq (ffn-symb term) 'equal) (or (and (eq var (fargn term 1)) (variablep (fargn term 2)) (not (eq (fargn term 1) (fargn term 2)))) (and (eq var (fargn term 2)) (variablep (fargn term 1)) (not (eq (fargn term 2) (fargn term 1)))))) (equal (all-vars term) (list var)))))) (defun type-prescription-conclp (var concl) ; Warning: Keep this function in sync with ; subst-nil-into-type-prescription-concl. ; Var is a variable and concl is a term. We recognize those concl ; that are the macroexpansion of (or t1 ... tk) where every ti is a ; type-prescription-disjunctp about var. ; In the grand scheme of things, concl was obtained from the ; conclusion of an alleged :TYPE-PRESCRIPTION lemma about some term, ; pat, by replacing all occurrences of pat with some new variable, ; var. We also know that concl involves no variables other than var ; and those that occurred in pat. (cond ((variablep concl) (type-prescription-disjunctp var concl)) ((fquotep concl) nil) ((flambda-applicationp concl) nil) ((eq (ffn-symb concl) 'if) (cond ((equal (fargn concl 1) (fargn concl 2)) (and (type-prescription-disjunctp var (fargn concl 1)) (type-prescription-conclp var (fargn concl 3)))) (t (type-prescription-disjunctp var concl)))) (t (type-prescription-disjunctp var concl)))) (defun subst-nil-into-type-prescription-disjunct (var term) ; Warning: Keep this function in sync with type-prescription-disjunctp. ; We assume var and term are ok'd by type-prescription-disjunctp. ; If term is of the form (equal var other-var) or (equal other-var var) ; we replace it by nil, otherwise we leave it alone. (cond ((variablep term) term) ; The next two cases never happen, but we leave them in just to make ; sure we copy term modulo this substitution. ((fquotep term) term) ((flambda-applicationp term) term) ((and (eq (ffn-symb term) 'equal) (or (and (eq var (fargn term 1)) (variablep (fargn term 2)) (not (eq (fargn term 1) (fargn term 2)))) (and (eq var (fargn term 2)) (variablep (fargn term 1)) (not (eq (fargn term 2) (fargn term 1)))))) *nil*) (t term))) (defun subst-nil-into-type-prescription-concl (var concl) ; Warning: Keep this function in sync with type-prescription-conclp. ; We know that var and concl are ok'd by type-prescription-conclp. So ; concl is a disjunction of terms, some of which are of the form ; (equal var other-var). We replace each of those disjuncts in concl ; with nil so as to produce that part of concl that is a disjunct of ; type restrictions. That is, if our answer is basic-term and vars is ; the list of all the other-vars in concl, then concl is equivalent to ; basic-term disjoined with the equality between var and each variable ; in vars. (cond ((variablep concl) (subst-nil-into-type-prescription-disjunct var concl)) ; The next two cases never happen. ((fquotep concl) concl) ((flambda-applicationp concl) concl) ((eq (ffn-symb concl) 'if) (cond ((equal (fargn concl 1) (fargn concl 2)) (let ((temp (subst-nil-into-type-prescription-disjunct var (fargn concl 1)))) (fcons-term* 'if temp temp (subst-nil-into-type-prescription-concl var (fargn concl 3))))) (t (subst-nil-into-type-prescription-disjunct var concl)))) (t (subst-nil-into-type-prescription-disjunct var concl)))) (defun unprettyify-tp (term) ; This variant of unprettyify avoids giviing special treatment to conjunctions, ; and hence is suitable for parsing terms into type-prescription rules. Unlike ; unprettyify, it returns (mv hyps concl). (case-match term (('implies t1 t2) (mv-let (hyps concl) (unprettyify-tp t2) (mv (append? (flatten-ands-in-lit t1) hyps) concl))) ((('lambda vars body) . args) (unprettyify-tp (subcor-var vars args body))) (& (mv nil (remove-lambdas term))))) (defun destructure-type-prescription (name typed-term term ens wrld) ; Warning: Keep this in sync with the :BACKCHAIN-LIMIT-LST case of ; translate-rule-class-alist. ; Note: This function does more than "destructure" term into a ; :TYPE-PRESCRIPTION rule, it checks a lot of conditions too and ; computes type-sets. However, it doesn't actually cause errors -- ; note that state is not among its arguments -- but may return an ; error message suitable for printing with ~@. We return many ; results. The first is nil or an error message. The rest are ; relevant only if the first is nil and are described below. We code ; this way because the destructuring and checking are inextricably ; intertwined and when we destructure in order to add the rule, we do ; not have state around. ; We determine whether term is a suitable :TYPE-PRESCRIPTION lemma ; about the term typed-term. Term is suitable as a :TYPE- ; PRESCRIPTION lemma about typed-term if the conclusion of term, ; concl, is a disjunction of type-prescription disjuncts about ; typed-term. Each disjunct must either be an equality between ; typed-term and one of the variables occurring in typed-term, or else ; must be some term, such as (and (rationalp typed-term) (<= ; typed-term 0)) or (compound-recognizerp typed-term), that mentions ; typed-term and contains no variables outside those occurrences of ; typed-term. ; If term is unsuitable we return an error msg and nils. Otherwise we ; return nil and four more things: the list of hyps, a basic type ; set, a list of variables, and a ttree. In that case, term implies ; that when hyps are true, the type-set of typed-term is the union of the ; basic type-set together with the type-sets of the variables listed. ; The ttree records our dependencies on compound recognizers or other ; type-set lemmas in wrld. The ttree returned contains no 'assumption ; tags. (let ((term (remove-guard-holders term))) (mv-let (hyps concl) (unprettyify-tp term) (cond ((or (variablep typed-term) (fquotep typed-term) (flambda-applicationp typed-term)) (mv (msg "The :TYPED-TERM, ~x0, provided in the :TYPE-PRESCRIPTION ~ rule class for ~x1 is illegal because it is a variable, ~ constant, or lambda application. See :DOC type-prescription." typed-term name) nil nil nil nil nil)) ((dumb-occur-lst typed-term hyps) (mv (msg "The :TYPED-TERM, ~x0, of the proposed :TYPE-PRESCRIPTION ~ rule ~x1 occurs in the hypotheses of the rule. This would ~ cause ``infinite backchaining'' if we permitted ~x1 as a ~ :TYPE-PRESCRIPTION. (Don't feel reassured by this check: ~ infinite backchaining may occur anyway since it can be ~ caused by the combination of several rules.)" typed-term name) nil nil nil nil nil)) (t (let ((all-vars-typed-term (all-vars typed-term)) (all-vars-concl (all-vars concl))) (cond ((not (subsetp-eq all-vars-concl all-vars-typed-term)) (mv (msg "~x0 cannot be used as a :TYPE-PRESCRIPTION rule as ~ described by the given rule class because the ~ :TYPED-TERM, ~x1, does not contain the ~#2~[variable ~&2 ~ which is~/variables ~&2 which are~] mentioned in the ~ conclusion. See :DOC type-prescription." name typed-term (set-difference-eq all-vars-concl all-vars-typed-term)) nil nil nil nil nil)) (t (let* ((new-var (genvar (find-pkg-witness typed-term) "TYPED-TERM" nil all-vars-typed-term)) (concl1 (subst-expr new-var typed-term concl))) (cond ((not (type-prescription-conclp new-var concl1)) (mv (msg "~x0 is an illegal :TYPE-PRESCRIPTION lemma of the ~ class indicated because its conclusion is not a ~ disjunction of type restrictions about the ~ :TYPED-TERM ~x1. See :DOC type-prescription." name typed-term) nil nil nil nil nil)) (t (let ((vars (remove1-eq new-var (all-vars concl1))) (basic-term (subst-nil-into-type-prescription-concl new-var concl1))) ; Once upon a time, briefly, we got the type-set implied by (and hyps ; basic-term), thinking that we might need hyps to extract type ; information from basic-term. But the only var in basic-term is new ; so the hyps don't help much. The idea was to permit lemmas like ; (implies (rationalp x) (<= 0 (* x x))). Note that the guard for <= ; is satisfied only if we know that the product is rational, which we ; can deduce from the hyp. But when we try to process that lemma, the ; typed-term in generalized away, e.g., (implies (rationalp x) (<= 0 ; Z)). Thus, the hyps don't help: the only var in basic-term is ; new-var. You could conjoin hyps and concl1 and THEN generalize the ; typed-term to new-var, thereby linking the occurrences of typed-term ; in the hyps to those in the concl. But this is very unhelpful ; because it encourages the creation of lemmas that contain the ; typed-term in the hyps. That is bad because type-set then ; infinitely backchains. In the face of these difficulties, we have ; reverted back to the simplest treatment of type-prescription lemmas. (mv-let (ts ttree) (type-set-implied-by-term new-var nil basic-term ens wrld nil) (cond ((ts= ts *ts-unknown*) (mv (msg "~x0 is a useless :TYPE-PRESCRIPTION ~ lemma because we can deduce no type ~ restriction about its :TYPED-TERM ~ (below represented by ~x1) from the ~ generalized conclusion, ~p2. See :DOC ~ type-prescription." name new-var (untranslate concl1 t wrld)) nil nil nil nil nil)) ((not (assumption-free-ttreep ttree)) ; If type-set-implied-by-term requires that we force some assumptions, ; it is not clear what to do. For example, it is possible that the ; assumptions involve new-var, which makes no sense in the context of ; an application of this rule. My intuition tells me this error will ; never arise because for legal concls, basic-term is guard free. If ; there are :TYPE-PRESCRIPTION lemmas about the compound recognizers ; in it, they could have forced hyps. I think it unlikely, since the ; recognizers are Boolean. Well, I guess I could add a ; :TYPE-PRESCRIPTION lemma that said that under some forced hyp the ; compound-recognizer was actually t. In that case, the forced hyp ; would necessarily involve new-var, since that is the only argument ; to a compound recognizer. It would be interesting to see a living ; example of this situation. (mv (if (tagged-objectsp 'fc-derivation ttree) (er hard "Somehow an 'fc-derivation, ~x0, has ~ found its way into the ttree returned ~ by type-set-implied-by-term." (car (tagged-objects 'fc-derivation ttree))) (msg "~x0 is an illegal :TYPE-PRESCRIPTION ~ lemma because in determining the ~ type-set implied for its :TYPED-TERM, ~ ~x1, by its conclusion the ~ ~#2~[assumption ~&2 was~/assumptions ~ ~&2 were~] and our :TYPE-PRESCRIPTION ~ preprocessor, ~ CHK-ACCEPTABLE-TYPE-PRESCRIPTION-RULE, ~ does not know how to handle this ~ supposedly unusual situation. It would ~ be very helpful to report this error to ~ the authors." name typed-term (tagged-objects 'assumption ttree))) nil nil nil nil nil)) (t (mv nil hyps concl ts vars ttree)))))))))))))))) (defun add-type-prescription-rule (rune nume typed-term term backchain-limit-lst ens wrld quietp) (mv-let (erp hyps concl ts vars ttree) (destructure-type-prescription (base-symbol rune) typed-term term ens wrld) (declare (ignore concl ttree)) (cond (erp (cond (quietp ; We pass in the quietp flag when attempting to add a :type-prescription rule ; indirectly, as under a defequiv event. The following example causes the ; following code to be executed. Otherwise, we see an unfortunate error. (Or ; perhaps we really should see that error, since we will be unable to add the ; booleanp type prescription for the equivalence relation. However, then we ; will need to re-work community book ; books/workshops/2000/manolios/pipeline/pipeline/deterministic-systems/128/top/ma128-isa128.lisp.) ; (defun my-equal (x y) ; (equal x y)) ; ; (in-theory (disable ; (:type-prescription my-equal) ; (:COMPOUND-RECOGNIZER BOOLEANP-COMPOUND-RECOGNIZER))) ; ; (defequiv my-equal ; :hints (("Goal" :in-theory (enable booleanp)))) ; ; ; In v2-7 and presumably earlier, the above leads us to a type-prescription ; ; rule with a NIL :basic-ts field: ; ; ACL2 !>(car (getprop 'my-equal 'type-prescriptions t 'current-acl2-world (w state))) ; (NIL (1685 MY-EQUAL X Y) ; NIL ; (NIL :EQUIVALENCE MY-EQUAL-IS-AN-EQUIVALENCE) ; BOOLEANP (MY-EQUAL X Y)) ; ACL2 !> (prog2$ (cw "~%NOTE: ACL2 is unable to create a proposed ~ type-prescription rule from the term ~x0 for ~ :typed-term ~x1, so this proposed rule is not being ~ added.~|" term typed-term) wrld)) (t (er hard 'add-type-prescription-rule "Unable to process this :TYPE-PRESCRIPTION rule. A possible ~ explanation is that we are in the second pass of an ~ include-book or encapsulate, and although this rule was ~ legal in the first pass, it is not legal in the second pass. ~ For example, the rule may depend on a preceding ~ :COMPOUND-RECOGNIZER rule local to this encapsulate or ~ include-book. The usual error message for ~ :TYPE-PRESCRIPTION rules now follows.~|~%~@0" erp)))) (t (putprop (ffn-symb typed-term) 'type-prescriptions (cons (make type-prescription :rune rune :nume nume :term typed-term :hyps hyps :backchain-limit-lst (rule-backchain-limit-lst backchain-limit-lst hyps wrld :ts) :basic-ts ts :vars vars :corollary term) (getprop (ffn-symb typed-term) 'type-prescriptions nil 'current-acl2-world wrld)) wrld))))) (defun strong-compound-recognizer-p (fn recognizer-alist ens) (cond ((endp recognizer-alist) nil) ((let ((recog-tuple (car recognizer-alist))) (and (eq fn (access recognizer-tuple recog-tuple :fn)) (access recognizer-tuple recog-tuple :strongp) (enabled-numep (access recognizer-tuple recog-tuple :nume) ens))) t) (t (strong-compound-recognizer-p fn (cdr recognizer-alist) ens)))) (defun warned-non-rec-fns-for-tp (term recognizer-alist ens wrld) (cond ((or (variablep term) (fquotep term)) nil) ((flambdap (ffn-symb term)) (cons (ffn-symb term) (non-recursive-fnnames-lst (fargs term) ens wrld))) ((eq (ffn-symb term) 'if) ; Type-set and assume-true-false explore the top-level IF structure in such a ; way that NOT and strong compound recognizers aren't problems. (union-equal (warned-non-rec-fns-for-tp (fargn term 1) recognizer-alist ens wrld) (union-equal (warned-non-rec-fns-for-tp (fargn term 2) recognizer-alist ens wrld) (warned-non-rec-fns-for-tp (fargn term 3) recognizer-alist ens wrld)))) ((eq (ffn-symb term) 'not) (warned-non-rec-fns-for-tp (fargn term 1) recognizer-alist ens wrld)) ((strong-compound-recognizer-p (ffn-symb term) recognizer-alist ens) (non-recursive-fnnames-lst (fargs term) ens wrld)) (t (non-recursive-fnnames term ens wrld)))) (defun warned-non-rec-fns-tp-hyps1 (hyps recognizer-alist ens wrld acc) (cond ((endp hyps) acc) (t (warned-non-rec-fns-tp-hyps1 (cdr hyps) recognizer-alist ens wrld (let ((hyp (if (and (nvariablep (car hyps)) ; (not (fquotep (car hyps))) ; implied by: (member-eq (ffn-symb (car hyps)) '(force case-split))) (fargn (car hyps) 1) (car hyps)))) (cond (acc (union-equal (warned-non-rec-fns-for-tp hyp recognizer-alist ens wrld) acc)) (t (warned-non-rec-fns-for-tp hyp recognizer-alist ens wrld)))))))) (defun warned-non-rec-fns-tp-hyps (hyps ens wrld) (warned-non-rec-fns-tp-hyps1 hyps (global-val 'recognizer-alist wrld) ens wrld nil)) (defun chk-acceptable-type-prescription-rule (name typed-term term backchain-limit-lst ctx ens wrld state) ; Like all individual rule checkers, we either cause an error or ; return a ttree that records our dependencies on lemmas. (mv-let (erp hyps concl ts vars ttree) (destructure-type-prescription name typed-term term ens wrld) (declare (ignore ts)) (cond (erp (er soft ctx "~@0" erp)) (t (let* ((weakp ; We avoid calling weak-type-prescription-rulep if we are going to ignore the ; warning anyhow. Otherwise, we construct a temporary world. ; We check (null vars) because otherwise, the warning can be needlessly harsh. ; For example, try submitting these events in a fresh ACL2 session after ; removing the (null vars) check from this function. ; (defstub foo (x) x) ; (defaxiom foo-type-prescription ; (or (integerp (foo y)) ; (equal (foo y) y)) ; :rule-classes :type-prescription) ; Then the warning will be printed without the (null vars) check, even though ; the rule above is a perfectly good one. (and (null vars) (not (warning-disabled-p "Type prescription")) (let* ((nume (get-next-nume wrld)) (rune (list :type-prescription name)) (wrld2 (add-type-prescription-rule rune nume typed-term term backchain-limit-lst ens wrld nil))) (mv-let (ts ttree) (type-set term nil t nil ens wrld2 nil nil nil) (or (not (assumption-free-ttreep ttree)) (ts-intersectp ts *ts-nil*))))))) (pprogn (cond (weakp (warning$ ctx ("Type prescription") "The :type-prescription rule generated for ~x0 may be ~ weaker than you expect. Note that the conclusion of a ~ :type-prescription rule is stored as a numeric type ~ rather than a term. It so happens that~| ~p1~|is not ~ provable using type-set reasoning in the extension of ~ the current world by that rule. Because information ~ has been lost, this rule probably does not have the ~ strength that it appears to have.~@2" name (untranslate term t wrld) (if (ffnnamep '< concl) " The conclusion of this rule contains a call of ~ function symbol < (or a macro <=, >, or >=), so it ~ may be worth considering making a :linear rule; ~ see :DOC linear." ""))) (t state)) (let* ((warned-non-rec-fns (and (not (warning-disabled-p "Non-rec")) (warned-non-rec-fns-tp-hyps hyps ens wrld))) (warned-free-vars (and (not (warning-disabled-p "Free")) (free-vars-in-hyps hyps (all-vars typed-term) wrld))) (inst-hyps (and warned-free-vars ; optimization (hyps-that-instantiate-free-vars warned-free-vars hyps)))) (pprogn (cond (warned-non-rec-fns (warning$ ctx ("Non-rec") "The hypothesis of the :type-prescription rule ~ generated from ~x0 contains the non-recursive ~ function symbol~#1~[~/s~] ~&1. Since the ~ hypotheses of :type-prescription rules are relieved ~ by type reasoning alone (and not rewriting) ~ ~#1~[this function is~/these functions are~] liable ~ to make the rule inapplicable. See :DOC ~ type-prescription." name (hide-lambdas warned-non-rec-fns))) (t state)) (cond (warned-free-vars (warning$ ctx ("Free") "The :type-prescription rule generated from ~x0 ~ contains the free variable~#1~[ ~&1. This ~ variable~/s ~&1. These variables~] will be chosen ~ by searching for instances of ~&2 among the ~ hypotheses of conjectures being rewritten. This is ~ generally a severe restriction on the applicability ~ of the :type-prescription rule." name warned-free-vars inst-hyps)) (t state)) (cond ((and warned-free-vars (forced-hyps inst-hyps)) (warning$ ctx ("Free") "For the forced ~#0~[hypothesis~/hypotheses~], ~&1, ~ used to instantiate free variables we will search ~ for ~#0~[an instance of the argument~/instances of ~ the arguments~] rather than ~#0~[an ~ instance~/instances~] of the FORCE or CASE-SPLIT ~ ~#0~[term itself~/terms themselves~]. If a search ~ fails for such a hypothesis, we will cause a case ~ split on the partially instantiated hypothesis. ~ Note that this case split will introduce a ``free ~ variable'' into the conjecture. While sound, this ~ will establish a goal almost certain to fail since ~ the restriction described by this apparently ~ necessary hypothesis constrains a variable not ~ involved in the problem. To highlight this oddity, ~ we will rename the free variables in such forced ~ hypotheses by prefixing them with ~ ``UNBOUND-FREE-''. This is not guaranteed to ~ generate a new variable but at least it generates ~ an unusual one. If you see such a variable in a ~ subsequent proof (and did not introduce them ~ yourself) you should consider the possibility that ~ the free variables of this type-prescription rule ~ were forced into the conjecture." (if (null (cdr (forced-hyps inst-hyps))) 0 1) (forced-hyps inst-hyps))) (t state)) (value ttree))))))))) ;--------------------------------------------------------------------------- ; Section: :EQUIVALENCE Rules (deflabel equivalence :doc ":Doc-Section Rule-Classes mark a relation as an equivalence relation~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example: (defthm r-equal-is-an-equivalence ; assumes that r-equal has been defined (and (booleanp (r-equal x y)) (r-equal x x) (implies (r-equal x y) (r-equal y x)) (implies (and (r-equal x y) (r-equal y z)) (r-equal x z))) :rule-classes :equivalence) ~ev[] Also ~pl[defequiv].~/ ~bv[] General Form: (and (booleanp (equiv x y)) (equiv x x) (implies (equiv x y) (equiv y x)) (implies (and (equiv x y) (equiv y z)) (equiv x z))) ~ev[] except that the order of the conjuncts and terms and the choice of variable symbols is unimportant. The effect of such a rule is to identify ~c[equiv] as an equivalence relation. Note that only Boolean 2-place function symbols can be treated as equivalence relations. ~l[congruence] and ~pl[refinement] for closely related concepts. The macro form ~c[(defequiv equiv)] is an abbreviation for a ~ilc[defthm] of rule-class ~c[:equivalence] that establishes that ~c[equiv] is an equivalence relation. It generates the formula shown above. ~l[defequiv]. When ~c[equiv] is marked as an equivalence relation, its reflexivity, symmetry, and transitivity are built into the system in a deeper way than via ~c[:]~ilc[rewrite] rules. More importantly, after ~c[equiv] has been shown to be an equivalence relation, lemmas about ~c[equiv], e.g., ~bv[] (implies hyps (equiv lhs rhs)), ~ev[] when stored as ~c[:]~ilc[rewrite] rules, cause the system to rewrite certain occurrences of (instances of) ~c[lhs] to (instances of) ~c[rhs]. Roughly speaking, an occurrence of ~c[lhs] in the ~c[kth] argument of some ~c[fn]-expression, ~c[(fn ... lhs' ...)], can be rewritten to produce ~c[(fn ... rhs' ...)], provided the system ``knows'' that the value of ~c[fn] is unaffected by ~c[equiv]-substitution in the ~c[kth] argument. Such knowledge is communicated to the system via ``congruence lemmas.'' For example, suppose that ~c[r-equal] is known to be an equivalence relation. The ~c[:]~ilc[congruence] lemma ~bv[] (implies (r-equal s1 s2) (equal (fn s1 n) (fn s2 n))) ~ev[] informs the rewriter that, while rewriting the first argument of ~c[fn]-expressions, it is permitted to use ~c[r-equal] rewrite-rules. ~l[congruence] for details about ~c[:]~ilc[congruence] lemmas. Interestingly, congruence lemmas are automatically created when an equivalence relation is stored, saying that either of the equivalence relation's arguments may be replaced by an equivalent argument. That is, if the equivalence relation is ~c[fn], we store congruence rules that state the following fact: ~bv[] (implies (and (fn x1 y1) (fn x2 y2)) (iff (fn x1 x2) (fn y1 y2))) ~ev[] Another aspect of equivalence relations is that of ``refinement.'' We say ~c[equiv1] ``refines'' ~c[equiv2] iff ~c[(equiv1 x y)] implies ~c[(equiv2 x y)]. ~c[:]~ilc[refinement] rules permit you to establish such connections between your equivalence relations. The value of refinements is that if the system is trying to rewrite something while maintaining ~c[equiv2] it is permitted to use as a ~c[:]~ilc[rewrite] rule any refinement of ~c[equiv2]. Thus, if ~c[equiv1] is a refinement of ~c[equiv2] and there are ~c[equiv1] rewrite-rules available, they can be brought to bear while maintaining ~c[equiv2]. ~l[refinement]. The system initially has knowledge of two equivalence relations, equality, denoted by the symbol ~ilc[equal], and propositional equivalence, denoted by ~ilc[iff]. ~ilc[Equal] is known to be a refinement of all equivalence relations and to preserve equality across all arguments of all functions. Typically there are five steps involved in introducing and using a new equivalence relation, equiv.~bq[] (1) Define ~c[equiv], (2) prove the ~c[:equivalence] lemma about ~c[equiv], (3) prove the ~c[:]~ilc[congruence] lemmas that show where ~c[equiv] can be used to maintain known relations, (4) prove the ~c[:]~ilc[refinement] lemmas that relate ~c[equiv] to known relations other than equal, and (5) develop the theory of conditional ~c[:]~ilc[rewrite] rules that drive equiv rewriting. ~eq[]More will be written about this as we develop the techniques. For now, here is an example that shows how to make use of equivalence relations in rewriting. Among the theorems proved below is ~bv[] (defthm insert-sort-is-id (perm (insert-sort x) x)) ~ev[] Here ~c[perm] is defined as usual with ~c[delete] and is proved to be an equivalence relation and to be a congruence relation for ~ilc[cons] and ~ilc[member]. Then we prove the lemma ~bv[] (defthm insert-is-cons (perm (insert a x) (cons a x))) ~ev[] which you must think of as you would ~c[(insert a x) = (cons a x)]. Now prove ~c[(perm (insert-sort x) x)]. The base case is trivial. The induction step is ~bv[] (consp x) & (perm (insert-sort (cdr x)) (cdr x)) -> (perm (insert-sort x) x). ~ev[] Opening ~c[insert-sort] makes the conclusion be ~bv[] (perm (insert (car x) (insert-sort (cdr x))) x). ~ev[] Then apply the induction hypothesis (rewriting ~c[(insert-sort (cdr x))] to ~c[(cdr x)]), to make the conclusion be ~bv[] (perm (insert (car x) (cdr x)) x) ~ev[] Then apply ~c[insert-is-cons] to get ~c[(perm (cons (car x) (cdr x)) x)]. But we know that ~c[(cons (car x) (cdr x))] is ~c[x], so we get ~c[(perm x x)] which is trivial, since ~c[perm] is an equivalence relation. Here are the events. ~bv[] (encapsulate (((lt * *) => *)) (local (defun lt (x y) (declare (ignore x y)) nil)) (defthm lt-non-symmetric (implies (lt x y) (not (lt y x))))) (defun insert (x lst) (cond ((atom lst) (list x)) ((lt x (car lst)) (cons x lst)) (t (cons (car lst) (insert x (cdr lst)))))) (defun insert-sort (lst) (cond ((atom lst) nil) (t (insert (car lst) (insert-sort (cdr lst)))))) (defun del (x lst) (cond ((atom lst) nil) ((equal x (car lst)) (cdr lst)) (t (cons (car lst) (del x (cdr lst)))))) (defun mem (x lst) (cond ((atom lst) nil) ((equal x (car lst)) t) (t (mem x (cdr lst))))) (defun perm (lst1 lst2) (cond ((atom lst1) (atom lst2)) ((mem (car lst1) lst2) (perm (cdr lst1) (del (car lst1) lst2))) (t nil))) (defthm perm-reflexive (perm x x)) (defthm perm-cons (implies (mem a x) (equal (perm x (cons a y)) (perm (del a x) y))) :hints ((\"Goal\" :induct (perm x y)))) (defthm perm-symmetric (implies (perm x y) (perm y x))) (defthm mem-del (implies (mem a (del b x)) (mem a x))) (defthm perm-mem (implies (and (perm x y) (mem a x)) (mem a y))) (defthm mem-del2 (implies (and (mem a x) (not (equal a b))) (mem a (del b x)))) (defthm comm-del (equal (del a (del b x)) (del b (del a x)))) (defthm perm-del (implies (perm x y) (perm (del a x) (del a y)))) (defthm perm-transitive (implies (and (perm x y) (perm y z)) (perm x z))) (defequiv perm) (in-theory (disable perm perm-reflexive perm-symmetric perm-transitive)) (defcong perm perm (cons x y) 2) (defcong perm iff (mem x y) 2) (defthm atom-perm (implies (not (consp x)) (perm x nil)) :rule-classes :forward-chaining :hints ((\"Goal\" :in-theory (enable perm)))) (defthm insert-is-cons (perm (insert a x) (cons a x))) (defthm insert-sort-is-id (perm (insert-sort x) x)) (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y)) (defun rev (x) (if (consp x) (app (rev (cdr x)) (list (car x))) nil)) (defcong perm perm (app x y) 2) (defthm app-cons (perm (app a (cons b c)) (cons b (app a c)))) (defthm app-commutes (perm (app a b) (app b a))) (defcong perm perm (app x y) 1 :hints ((\"Goal\" :induct (app y x)))) (defthm rev-is-id (perm (rev x) x)) (defun == (x y) (if (consp x) (if (consp y) (and (equal (car x) (car y)) (== (cdr x) (cdr y))) nil) (not (consp y)))) (defthm ==-reflexive (== x x)) (defthm ==-symmetric (implies (== x y) (== y x))) (defequiv ==) (in-theory (disable ==-symmetric ==-reflexive)) (defcong == == (cons x y) 2) (defcong == iff (consp x) 1) (defcong == == (app x y) 2) (defcong == == (app x y) 1) (defthm rev-rev (== (rev (rev x)) x)) ~ev[]~/") ; For a rule to acceptable as an :EQUIVALENCE rule, it must state the ; Boolean-ness, reflexivity, symmetry, and transitivity of a 2-place ; function symbol. We make the user type in the desired formula and ; then check that he typed a suitable one. This way we can define a ; simple macro that generates a suitable defthm event (rather than ; have to produce a new event type with all the prove-level hint ; passing mechanism). To check that the formula is suitable we ; generate a cannonical formula and check that the given one subsumes ; it. To add an :EQUIVALENCE rule we add a 'coarsenings property to ; the function symbol and also set up an initial 'congruences property ; for it. ; Some of the simple functions below anticipate the day we allow n-ary ; equivalences (n>2) but don't be fooled into thinking we allow it ; today! (defun boolean-fn (fn) ; The name boolean is not usable for definitions in Allegro, because ; it's in the COMMON-LISP package. So, we'd better not use that name ; here. `(booleanp (,fn x y))) (defun reflexivity (fn) ; In this function we expect fn to have arity 2. `(,fn x x)) (defun symmetry (fn) ; This function expects fn to have arity 2. `(implies (,fn x y) (,fn y x))) (defun transitivity (fn) ; This function expects fn to have arity 2. `(implies (and (,fn x y) (,fn y z)) (,fn x z))) (defun equivalence-relation-condition (fn) ; This function expects fn to have arity 2. We generate a formula that states ; that fn is Boolean, reflexive, symmetric, and transitive. ; There are at least two reasons we require equivalence relations to be ; Boolean. One is to simplify assume-true-false. When we assume (fn x y) ; true, we pair it with *ts-t* rather than its full type-set take away ; *ts-nil*. The other is that from reflexivity and Boolean we get than fn is ; commutative and so can freely use (fn y x) for (fn x y). If we did not have ; the Boolean condition we would have to be more careful about, say, ; commutative unification. `(and ,(boolean-fn fn) ,(reflexivity fn) ,(symmetry fn) ,(transitivity fn))) (defun find-candidate-equivalence-relation (clauses) ; Clauses is a list of clauses. We look for one of the form ; ((fn x x)) and if we find it, we return fn; else nil. See ; chk-acceptable-equivalence-rule. (cond ((null clauses) nil) (t (let ((clause (car clauses))) (case-match clause (((fn x x)) (declare (ignore x)) fn) (& (find-candidate-equivalence-relation (cdr clauses)))))))) (defun collect-problematic-pre-equivalence-rule-names (lst) ; A problematic pre-equivalence rule about a soon-to-be-named ; equivalence relation equiv is one whose conclusion is (equiv lhs ; rhs), where lhs is not a variable or a quote. Such a rule could be ; stored as a :REWRITE rule for lhs after equiv is known to be an ; equivalence relation; but before that, such a rule is stored to ; rewrite (equiv lhs rhs) to T. Assuming lst is all the :REWRITE rules ; for equiv, we return the list of names of the problematic rules. (cond ((null lst) nil) ((and (eq (access rewrite-rule (car lst) :equiv) 'equal) (equal (access rewrite-rule (car lst) :rhs) *t*) (not (variablep (fargn (access rewrite-rule (car lst) :lhs) 1))) (not (quotep (fargn (access rewrite-rule (car lst) :lhs) 1)))) (cons (access rewrite-rule (car lst) :rune) (collect-problematic-pre-equivalence-rule-names (cdr lst)))) (t (collect-problematic-pre-equivalence-rule-names (cdr lst))))) (defun chk-acceptable-equivalence-rule (name term ctx wrld state) ; Term supposedly states that fn is boolean, reflexive, symmetric, and ; transitive. To check that, we generate our canonical statement of ; those four properties and then check that term subsumes it. We ; clausify both statements with shallow-clausify, which tears apart ; the IMPLIES and AND structure of the terms without messing up the ; IFs. ; The hard part is finding out the candidate fn. Consider the clausification ; of an acceptable term. The clauses are shown below (ignoring choice of clause order, ; literal order and variable names): ; ((booleanp (fn x y))) ; ((fn x x)) ; ((not (fn x y)) (fn y x)) ; ((not (fn x z)) ; (not (fn z y)) ; (fn x y)) ; So to find fn we will look for the reflexive clause. (let* ((act-clauses (shallow-clausify term)) (fn (find-candidate-equivalence-relation act-clauses))) (cond ((null fn) (er soft ctx "~x0 is an unacceptable :EQUIVALENCE lemma. Such a lemma ~ must state that a given 2-place function symbol is ~ Boolean, reflexive, symmetric, and transitive. We cannot ~ find the statement of reflexivity, which is the one we key ~ on to identify the name of the alleged equivalence ~ relation. Perhaps you have forgotten to include it. More ~ likely, perhaps your relation takes more than two ~ arguments. We do not support n-ary equivalence relations, ~ for n>2. Sorry." name)) (t (er-let* ((eqv-cond (translate (equivalence-relation-condition fn) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (let ((eqv-clauses (shallow-clausify eqv-cond))) ; In the first test below we open-code a call of equivalence-relationp, ; avoiding special treatment for iff since we want (defequiv iff) to succeed ; during initialization. (cond ((or (eq fn 'equal) (and (not (flambdap fn)) (getprop fn 'coarsenings nil 'current-acl2-world wrld))) (er soft ctx "~x0 is already known to be an equivalence relation." fn)) (t (let ((subsumes (clause-set-subsumes *init-subsumes-count* act-clauses eqv-clauses))) (cond ((eq subsumes t) (cond ((warning-disabled-p "Equiv") ; optimization (value nil)) (t (let ((lst (scrunch-eq (collect-problematic-pre-equivalence-rule-names (getprop fn 'lemmas nil 'current-acl2-world wrld))))) (cond (lst (pprogn (warning$ ctx ("Equiv") "Any lemma about ~p0, proved before ~x1 is ~ marked as an equivalence relation, is ~ stored so as to rewrite ~p0 to T. After ~ ~x1 is known to be an equivalence ~ relation, such a rule would rewrite the ~ left-hand side to the right-hand side, ~ preserving ~x1. You have previously ~ proved ~n2 possibly problematic ~ rule~#3~[~/s~] about ~x1, namely ~&3. ~ After ~x1 is marked as an equivalence ~ relation you should reconsider ~ ~#3~[this~/each~] problematic rule. If ~ the rule is merely in support of ~ establishing that ~x1 is an equivalence ~ relation, it may be appropriate to disable ~ it permanently hereafter. If the rule is ~ now intended to rewrite left to right, you ~ must prove the lemma again after ~x1 is ~ known to be an equivalence relation." (fcons-term fn '(x y)) fn (length lst) (strip-cadrs lst)) (value nil))) (t (value nil))))))) (t (er soft ctx (if subsumes ; (eq subsumes '?) ; Perhaps the user could come up with a case that puts us here, but that's ; pretty hard to imagine! So we use *init-subsumes-count* in the call of ; clause-set-subsumes above, so that we can complain if we get to this case. "This low-level implementation error is a complete ~ surprise, as the subsumption check returned '? ~ for the :EQUIVALENCE lemma ~x0 for funcction ~ symbol ~x1. This failure occurred when it was ~ checked that the equivalence-relation formula ~ subsumes the following canonical form: ~X23. ~ Please contact the ACL2 implementors." "~x0 is an unacceptable :EQUIVALENCE lemma for the ~ function symbol ~x1. To be acceptable the formula ~ being proved must state that ~x1 is Boolean, ~ reflexive, symmetric, and transitive. This is ~ checked by verifying that the formula subsumes the ~ following canonical form: ~x2. It does not.") name fn (prettyify-clause-set eqv-clauses nil wrld) nil)))))))))))) (defun add-equivalence-rule (rune nume term ens wrld) ; Term states that some function symbol fn is an equivalence relation. ; We recover from term the fn in question and add a 'coarsenings ; property for fn, stating that it is a coarsening of itself. This ; marks it as an equivalence relation. We also add it to the ; coarsenings of 'equal, which is the only other equivalence relation ; that we know is a refinement of this new one. The coarsenings of ; 'equal is thus the list of all known equivalence relations. The car of ; the 'coarsenings property for an equivalence relation fn is always ; eq to fn itself. However, subsequent relations are listed in ; arbitrary order. ; If fn is not "obviously" Boolean in the sense that type-set reports ; that it is Boolean, we store a type-prescription rule for it. This is ; usually unnecessary when fn is defined. But on the off chance that its ; Boolean nature was missed by DEFUN or -- more likely -- when fn is a ; constrained function that is undefined in this world, we often need ; this fact. ; We also add a 'congruences property for fn. See the essay on ; equivalence, refinements, and congruence-based rewriting. ; The property that we add states that the equality of two fn expressions ; is maintained by maintaining fn in both arguments. ; That is ; (implies (fn x1 x2) (equal (fn x1 y) (fn x2 y))) ; and ; (implies (fn y1 y2) (equal (fn x y1) (fn x y2))). ; We prove this below. ; Suppose fn is an arbitrary equivalence relation. ; (encapsulate (((fn * *) => *)) ; (local (defun fn (x y) (equal x y))) ; (defequiv fn)) ; We pick out from its properties just three that we care about, its ; Boolean nature, symmetry, and transitivity. We don't care that it ; is reflexive and the proofs below go through if you constrain fn ; just to have the three properties below. We made fn an equivalence ; relation simply so we could conclude with some :congruence lemmas ; about fn -- an act which causes an error if fn is not an equivalence ; relation. But the theorems proved about fn are true of any relation ; with the three properties below. ; (defthm fn-boolean (booleanp (fn x y)) ; :rule-classes :type-prescription ; :hints (("Goal" :use fn-is-an-equivalence))) ; ; (defthm fn-symm (implies (fn x y) (equal (fn y x) t)) ; :hints (("Goal" :use fn-is-an-equivalence))) ; ; (defthm fn-trans (implies (and (fn x y) (fn y z)) (equal (fn x z) t)) ; :hints (("Goal" :use fn-is-an-equivalence))) ; So now we observe the first of our two congruence properties: to ; maintain identity in fn expressions it is sufficient to maintain ; "fn-ity" in the first argument position. ; (defthm fn-congruence1 ; (implies (fn x1 x2) ; (equal (fn x1 y) (fn x2 y))) ; :rule-classes :congruence ; :hints (("Goal" :use (:instance ; (:theorem ; (implies (and (booleanp p) ; (booleanp q)) ; (equal (equal p q) (iff p q)))) ; (p (fn x1 y)) ; (q (fn x2 y)))) ; ("Subgoal 2.1" :use ((:instance fn-symm (x x1) (y x2))) ; :in-theory (disable fn-symm)))) ; And, to maintain identity in fn expressions it suffices to maintain ; "fn-ity" in the second argument position. ; (defthm fn-congruence2 ; (implies (fn y1 y2) ; (equal (fn x y1) (fn x y2))) ; :rule-classes :congruence ; :hints (("Goal" :use (:instance ; (:theorem ; (implies (and (booleanp p) ; (booleanp q)) ; (equal (equal p q) (iff p q)))) ; (p (fn x y1)) ; (q (fn x y2)))) ; ("Subgoal 2.1" :use ((:instance fn-symm (x y1) (y y2))) ; :in-theory (disable fn-symm)))) ; We do not store with the equivalence relation the name of the event ; that established that it is an equivalence relation. That means we ; can't report it in our dependencies or disable it. (let* ((act-clauses (shallow-clausify term)) (fn (find-candidate-equivalence-relation act-clauses))) (putprop fn 'coarsenings (list fn) (putprop 'equal 'coarsenings (append (getprop 'equal 'coarsenings nil 'current-acl2-world wrld) (list fn)) (putprop fn 'congruences (cons (list 'equal (list (make congruence-rule :rune rune :nume nume :equiv fn)) (list (make congruence-rule :rune rune :nume nume :equiv fn))) (getprop fn 'congruences nil 'current-acl2-world wrld)) (cond ((mv-let (ts ttree) (type-set (fcons-term* fn 'x 'y) nil nil nil ens wrld nil nil nil) (declare (ignore ttree)) (ts-subsetp ts *ts-boolean*)) wrld) (t (add-type-prescription-rule rune nume (fcons-term* fn 'x 'y) (fcons-term* 'booleanp (fcons-term* fn 'x 'y)) nil ; backchain-limit-lst ens wrld t)))))))) ;--------------------------------------------------------------------------- ; Section: :REFINEMENT Rules (deflabel refinement :doc ":Doc-Section Rule-Classes record that one equivalence relation refines another~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example: (defthm bag-equal-refines-set-equal (implies (bag-equal x y) (set-equal y x)) :rule-classes :refinement) ~ev[] Also ~pl[defrefinement].~/ ~bv[] General Form: (implies (equiv1 x y) (equiv2 x y)) ~ev[] ~c[Equiv1] and ~c[equiv2] must be known equivalence relations. The effect of such a rule is to record that ~c[equiv1] is a refinement of ~c[equiv2]. This means that ~c[equiv1] ~c[:]~ilc[rewrite] rules may be used while trying to maintain ~c[equiv2]. ~l[equivalence] for a general discussion of the issues. The macro form ~c[(defrefinement equiv1 equiv2)] is an abbreviation for a ~ilc[defthm] of rule-class ~c[:refinement] that establishes that ~c[equiv1] is a refinement of ~c[equiv2]. ~l[defrefinement]. Suppose we have the ~c[:]~ilc[rewrite] rule ~bv[] (bag-equal (append a b) (append b a)) ~ev[] which states that ~ilc[append] is commutative modulo bag-equality. Suppose further we have established that bag-equality refines set-equality. Then when we are simplifying ~ilc[append] expressions while maintaining set-equality we use ~ilc[append]'s commutativity property, even though it was proved for bag-equality. Equality is known to be a refinement of all equivalence relations. The transitive closure of the refinement relation is maintained, so if ~c[set-equality], say, is shown to be a refinement of some third sense of equivalence, then ~c[bag-equality] will automatially be known as a refinement of that third equivalence. ~c[:refinement] lemmas cannot be disabled. That is, once one equivalence relation has been shown to be a refinement of another, there is no way to prevent the system from using that information. Of course, individual ~c[:]~ilc[rewrite] rules can be disabled. More will be written about this as we develop the techniques.") (defun chk-acceptable-refinement-rule (name term ctx wrld state) (let ((str "~x0 does not have the form of a :REFINEMENT rule. See :DOC refinement.")) (case-match term (('implies (equiv1 x y) (equiv2 x y)) (cond ((and (equivalence-relationp equiv1 wrld) (equivalence-relationp equiv2 wrld) (variablep x) (variablep y) (not (eq x y))) (cond ((refinementp equiv1 equiv2 wrld) (er soft ctx "~x0 is already known to be a refinement of ~ ~x1. See :DOC refinement." equiv1 equiv2)) (t (value nil)))) (t (er soft ctx str name)))) (& (er soft ctx str name))))) ; As noted in the essay on equivalence, refinements, and ; congruence-based rewriting, we maintain our refinements database ; via the 'coarsenings property, for efficiency reasons explained in ; the essay. Thus, if equiv1 is a refinement of equiv2 then equiv2 is ; a coarsening of equiv1. We therefore wish to add equiv2 to the ; coarsening property of equiv1. However, as noted in the essay, the ; coarsening properties are kept closed under transitivity. So we need ; a transitive closure operation. ; Rather that try to implement this closure operation directly on the ; property-list world, where we would repeatedly extend the 'coarsenings ; properties of the affected equivs, we have decided on a more modular and ; elegant approach. We will simply collect all the coarsening properties ; into an alist, close that alist under the appropriate operation, and then ; go put the new coarsenings into the property list world. ; We start with the trivial operations of collecting and then ; redistributing all the coarsenings. (defun collect-coarsenings (wrld) ; Return an alist that pairs each equivalence relation in wrld with ; its current coarsenings. (let ((all-equivs (getprop 'equal 'coarsenings nil 'current-acl2-world wrld))) (pairlis$ all-equivs (getprop-x-lst all-equivs 'coarsenings wrld)))) (defun putprop-coarsenings (alist wrld) ; Alist pairs equiv relations with their new 'coarsenings property. ; Put each property, provided it is different from its current value ; in wrld. (cond ((null alist) wrld) ((equal (getprop (caar alist) 'coarsenings nil 'current-acl2-world wrld) (cdar alist)) (putprop-coarsenings (cdr alist) wrld)) (t (putprop (caar alist) 'coarsenings (cdar alist) (putprop-coarsenings (cdr alist) wrld))))) ; We now develop the world's least efficient transitive closure ; algorithm. Let alist be an alist pairing symbols to sets of ; symbols. By ``the value of a symbol'' in this context we mean the ; value assigned by the alist. We close the value sets under the ; operation of unioning into the set the value of any symbol already ; in the set. This operation eventually terminates since there are ; only a finite number of symbols involved. ; We do this in a very inefficient way. We literally just extend ; each value set by unioning into it the appropriate other sets and ; iterate that operation until there are no changes. If we ever have ; to operate with many equivalence relations enjoying many refinement ; relationships, we'll have to look at this code again. (defun union-values (lst alist) ; We form the union of the values of the members of lst under alist. (cond ((null lst) nil) (t (union-eq (cdr (assoc-eq (car lst) alist)) (union-values (cdr lst) alist))))) (defun extend-value-set (lst alist) ; We union into lst the value under alist of each element of lst. In ; an effort to preserve order we implement this in a slightly bizarre ; style. This concern about order is three-fold. First, it lets us ; code the termination check with an equality rather than a ; set-equality. Second, it ensures maintenance of the invariant that ; the car of the coarsenings property for an equiv is the equiv ; itself, e.g., see refinementp. Third, it means that 'coarsenings ; that don't get extended don't get changed and so don't get written ; back to the world. (append lst (set-difference-eq (union-values lst alist) lst))) (defun extend-each-value-set (alist1 alist2) ; we visit each value set in alist1 and extend it with the ; values specified by alist2. (cond ((null alist1) nil) (t (cons (cons (caar alist1) (extend-value-set (cdar alist1) alist2)) (extend-each-value-set (cdr alist1) alist2))))) (defun close-value-sets (alist) ; We extend each value set in alist, under alist, until alist doesn't ; change. Because we have taken care to preserve the order of things ; in extend-value-set we know that a value set doesn't change unless ; it has a new element. Thus, we can use equal rather than set-equal ; to check for our termination condition. But the real reason we care ; about order is so that the 'congruences properties eventually ; restored are usually unchanged. (let ((new-alist (extend-each-value-set alist alist))) (cond ((equal new-alist alist) alist) (t (close-value-sets new-alist))))) (defun add-refinement-rule (name nume term wrld) (declare (ignore name nume)) (let ((equiv1 (ffn-symb (fargn term 1))) (equiv2 (ffn-symb (fargn term 2)))) ; We collect all the 'coarsenings properties into an alist, add equiv2 ; to the end of the pot for equiv1, close that as discussed above, and ; then put the resulting 'coarsenings properties back into the world. (putprop-coarsenings (close-value-sets (put-assoc-eq equiv1 (append (getprop equiv1 'coarsenings nil 'current-acl2-world wrld) (list equiv2)) (collect-coarsenings wrld))) wrld))) ;--------------------------------------------------------------------------- ; Section: :CONGRUENCE Rules (deflabel congruence :doc ":Doc-Section Rule-Classes the relations to maintain while simplifying arguments~/ ~l[rule-classes] for a general discussion of rule classes and how they are used to build rules from formulas. An example ~c[:]~ilc[corollary] formula from which a ~c[:congruence] rule might be built is: ~bv[] Example: (defthm set-equal-implies-iff-memb-2 (implies (set-equal x y) (iff (memb e x) (memb e y))) :rule-classes :congruence) ~ev[] Also ~pl[defcong] and ~pl[equivalence].~/ ~bv[] General Form: (implies (equiv1 xk xk-equiv) (equiv2 (fn x1... xk ...xn) (fn x1... xk-equiv ...xn))) ~ev[] where ~c[equiv1] and ~c[equiv2] are known equivalence relations, ~c[fn] is an ~c[n-ary] function symbol and the ~c[xi] and ~c[xk-equiv] are all distinct variables. The effect of such a rule is to record that the ~c[equiv2]-equivalence of ~c[fn]-expressions can be maintained if, while rewriting the ~c[kth] argument position, ~c[equiv1]-equivalence is maintained. ~l[equivalence] for a general discussion of the issues. We say that ~c[equiv2], above, is the ``outside equivalence'' in the rule and ~c[equiv1] is the ``inside equivalence for the ~c[k]th argument'' The macro form ~c[(defcong equiv1 equiv2 (fn x1 ... x1) k)] is an abbreviation for a ~ilc[defthm] of rule-class ~c[:congruence] that attempts to establish that ~c[equiv2] is maintained by maintaining ~c[equiv1] in ~c[fn]'s ~c[k]th argument. The ~ilc[defcong] macro automatically generates the general formula shown above. ~l[defcong]. The ~c[memb] example above tells us that ~c[(memb e x)] is propositionally equivalent to ~c[(memb e y)], provided ~c[x] and ~c[y] are ~c[set-equal]. The outside equivalence is ~ilc[iff] and the inside equivalence for the second argument is ~c[set-equal]. If we see a ~c[memb] expression in a propositional context, e.g., as a literal of a clause or test of an ~ilc[if] (but not, for example, as an argument to ~ilc[cons]), we can rewrite its second argument maintaining ~c[set-equality]. For example, a rule stating the commutativity of ~ilc[append] (modulo set-equality) could be applied in this context. Since equality is a refinement of all equivalence relations, all equality rules are always available. ~l[refinement]. All known ~c[:congruence] rules about a given outside equivalence and ~c[fn] can be used independently. That is, consider two ~c[:congruence] rules with the same outside equivalence, ~c[equiv], and about the same function ~c[fn]. Suppose one says that ~c[equiv1] is the inside equivalence for the first argument and the other says ~c[equiv2] is the inside equivalence for the second argument. Then ~c[(fn a b)] is ~c[equiv] ~c[(fn a' b')] provided ~c[a] is ~c[equiv1] to ~c[a'] and ~c[b] is ~c[equiv2] to ~c[b']. This is an easy consequence of the transitivity of ~c[equiv]. It permits you to think independently about the inside equivalences. Furthermore, it is possible that more than one inside equivalence for a given argument slot will maintain a given outside equivalence. For example, ~c[(length a)] is equal to ~c[(length a')] if ~c[a] and ~c[a'] are related either by ~c[list-equal] or by ~ilc[string-equal]. You may prove two (or more) ~c[:congruence] rules for the same slot of a function. The result is that the system uses a new, ``generated'' equivalence relation for that slot with the result that rules of both (or all) kinds are available while rewriting. ~c[:Congruence] rules can be disabled. For example, if you have two different inside equivalences for a given argument position and you find that the ~c[:]~ilc[rewrite] rules for one are unexpectedly preventing the application of the desired rule, you can disable the rule that introduced the unwanted inside equivalence. ~em[Remark on Replacing IFF by EQUAL.] You may encounter a warning suggesting that a congruence rule ``can be strengthened by replacing the second equivalence relation, IFF, by EQUAL.'' Suppose for example that this warning occurs when you submit the following rule: ~bv[] (defcong equiv1 iff (fn x y) 2) ~ev[] which is shorthand for the following: ~bv[] (defthm equiv1-implies-iff-fn-2 (implies (equiv1 y y-equiv) (iff (fn x y) (fn x y-equiv))) :rule-classes (:congruence)) ~ev[] The warning is telling you that ACL2 was able to deduce that ~c[fn] always returns a Boolean, and hence a trivial but useful consequence is obtained by replacing ~ilc[iff] by ~ilc[equal] ~-[] ~bv[] (defcong equiv1 equal (fn x y) 2) ~ev[] ~-[] which is shorthand for the following: ~bv[] (defthm equiv1-implies-equal-fn-2 (implies (equiv1 y y-equiv) (equal (fn x y) (fn x y-equiv))) :rule-classes (:congruence)) ~ev[] If you have difficulty proving the latter directly, you can derive it from the former by giving a suitable hint, minimally as follows. ~bv[] (defcong equiv1 equal (fn x y) 2 :hints ((\"Goal\" :use equiv1-implies-iff-fn-2 :in-theory (union-theories '((:type-prescription fn)) (theory 'minimal-theory))))) ~ev[] By heeding this warning, you may avoid unnecessary ~ilc[double-rewrite] warnings later. We now explain why, but ~pl[double-rewrite] for relevant background material. For example, suppose you have proved the ``~c[iff]'' version of the congruence rule above, and later you submit the following rewrite rule. ~bv[] (defthm equal-list-perm (implies (equiv1 x y) (fn x y))) ~ev[] Since ~c[fn] is known to return a Boolean, ACL2 performs an optimization that stores this rule as though it were the following. ~bv[] (defthm equal-list-perm (implies (equiv1 x y) (equal (fn x y) t))) ~ev[] Thus, if ACL2's rewriter sees a term ~c[(fn a b)] in a context where the equivalence relation ~ilc[iff] is not being maintained, then it cannot use rule ~c[equiv1-implies-iff-fn-2], so it rewrites argument ~c[a] without the benefit of knowing that it suffices to maintain ~c[equiv1]; and then it caches the result. When ACL2 subsequently attempts to relieve the hypothesis ~c[(equiv1 x y)], it will rewrite ~c[x] simply by returning the rewritten value of ~c[a] from the result cache. This is unfortunate if ~c[a] could have been rewritten more completely under maintainance of the equivalence relation ~c[equiv1] ~-[] which is legal in the hypothesis since ~c[a] is an argument of ~c[equiv1], which is an ~il[equivalence] relation. The user who observes the warning from rule ~c[equiv1-implies-iff-fn-2], and replaces it with ~c[equiv1-implies-equal-fn-2], will avoid this unfortunate case.") (defun corresponding-args-eq-except (args1 args2 xk yk) ; Suppose args1 and args2 are two true lists of equal length, args1 ; contains distinct symbols, xk and yk are symbols and xk is an ; element of args1. Then we determine whether args2 is equal to args1 ; except at xk where args2 contains yk. (cond ((null args1) t) ((eq (car args1) xk) (and (eq (car args2) yk) (corresponding-args-eq-except (cdr args1) (cdr args2) xk yk))) (t (and (eq (car args1) (car args2)) (corresponding-args-eq-except (cdr args1) (cdr args2) xk yk))))) (defun interpret-term-as-congruence-rule (name term wrld) ; This function recognizes terms that are :CONGRUENCE lemmas. We ; return two results. The first is a flag that indicates whether or ; not the term is a :CONGRUENCE lemma. If the term is a congruence ; lemma, the second result is a 4-tuple, (fn equiv1 k equiv2), which ; means term states that ``equiv2 is preserved by equiv1 in the kth ; argument of fn.'' If the term is not a :CONGRUENCE rule, the second ; is a tilde-@ message explaining why. See the essay on equivalence, ; refinements, and congruence-based rewriting for details. ; :CONGRUENCE lemmas are of the form ; (implies (equiv1 xk yk) ; (equiv2 (fn x1 ... xk ... xn) (fn x1 ... yk ... xn))) ; where fn is a function symbol, all the xi and yk are distinct ; variables and equiv1 and the equiv2 are equivalence relations. ; Such a lemma is read as ``equiv2 is preserved by equiv1 in the kth ; argument of fn.'' ; We do not actually cause an error because this function is sometimes ; called when STATE is unavailable. We combine the recognition of the ; :CONGRUENCE lemma with the construction of the 4-tuple describing it ; because the two are so intermingled that it seemed dubious to ; separate them into two functions. (let ((pairs (unprettyify (remove-guard-holders term))) (hyp-msg "~x0 is an unacceptable :CONGRUENCE rule. The ~ single hypothesis of a :CONGRUENCE rule must be a ~ term of the form (equiv x y), where equiv has ~ been proved to be an equivalence relation and x ~ and y are distinct variable symbols. The ~ hypothesis of ~x0, ~x1, is not of this form.") (concl-msg "~x0 is an unacceptable :CONGRUENCE rule. The ~ conclusion of an acceptable rule must be of the ~ form (equiv (fn x1 ... xk ... xn) (fn x1 ... yk ~ ... xn)) where equiv has been proved to be an ~ equivalence relation, fn is a function symbol, ~ the xi are distinct variable symbols, xk is ~x1, ~ yk is ~x2, and ~x2 does not occur among the xi. ~ The conclusion of ~x0, ~x3, is not of this form.")) (cond ((and (int= (length pairs) 1) (int= (length (caar pairs)) 1)) (let ((hyp (caaar pairs)) (concl (cdar pairs))) (case-match hyp ((equiv1 xk yk) (cond ((and (variablep xk) (variablep yk) (equivalence-relationp equiv1 wrld)) (case-match concl ((equiv2 (fn . args1) (fn . args2)) (cond ((and (equivalence-relationp equiv2 wrld) (symbolp fn) (all-variablep args1) (no-duplicatesp-equal args1) (member-eq xk args1) (corresponding-args-eq-except args1 args2 xk yk)) (mv t (list fn equiv1 (1+ (- (length args1) (length (member-eq xk args1)))) equiv2))) (t (mv nil (msg concl-msg name xk yk concl))))) (& (mv nil (msg concl-msg name xk yk concl))))) (t (mv nil (msg hyp-msg name hyp))))) (& (mv nil (msg hyp-msg name hyp)))))) (t (mv nil (msg "~x0 is an unacceptable :CONGRUENCE rule. When ~ an acceptable :CONGRUENCE rule is ~ propositionally flattened, only one conjunct is ~ produced and it is of the form (implies (equiv1 ~ xk yk) (equiv2 (fn ... xk ...) (fn ... yk ~ ...))), where equiv1 and equiv2 are equivalence ~ relations. ~x0 is not of this form." name)))))) (defun some-congruence-rule-same (equiv rules) ; Return the first element of rules which has equiv as its :equiv field. (cond ((null rules) nil) ((eq equiv (access congruence-rule (car rules) :equiv)) (car rules)) (t (some-congruence-rule-same equiv (cdr rules))))) (defun some-congruence-rule-has-refinement (equiv rules wrld) ; Return the first element of rules which has equiv as a refinement of its ; :equiv field. (cond ((null rules) nil) ((refinementp equiv (access congruence-rule (car rules) :equiv) wrld) (car rules)) (t (some-congruence-rule-has-refinement equiv (cdr rules) wrld)))) (defun chk-acceptable-congruence-rule (name term ctx wrld state) ; We check that term is of the form ; (implies (equiv1 xk yk) ; (equiv2 (fn ... xk ...) (fn ... yk ...))) ; We print a warning message if we already know that equiv2 is ; preserved by equiv1 in the kth slot of fn. We are not so much ; watching out for the possibility that equiv1 literally occurs in the ; list in the kth slot -- though that could happen and the old rule be ; disabled so the user is unaware that it exists. We are more ; concerned about the possibility that equiv1 is some refinement of a ; relation already in the kth slot. (mv-let (flg x) (interpret-term-as-congruence-rule name term wrld) (cond ((not flg) (er soft ctx "~@0" x)) (t (let ((fn (car x)) (equiv1 (cadr x)) (k (caddr x)) (equiv2 (cadddr x))) (let ((temp (nth k (assoc-eq equiv2 (getprop fn 'congruences nil 'current-acl2-world wrld))))) (cond ((some-congruence-rule-same equiv1 temp) (er soft ctx "The previously added :CONGRUENCE lemma, ~x0, ~ establishes that ~x1 preserves ~x2 in the ~n3 slot ~ of ~x4. Thus, ~x5 is unnecessary." (base-symbol (access congruence-rule (some-congruence-rule-same equiv1 temp) :rune)) equiv1 equiv2 (cons k 'th) fn name)) ((some-congruence-rule-has-refinement equiv1 temp wrld) (er soft ctx "The previously added :CONGRUENCE lemma, ~x0, ~ establishes that ~x1 preserves ~x2 in the ~n3 slot ~ of ~x4. But we know that ~x5 is a refinement of ~ ~x1. Thus, ~x6 is unnecessary." (base-symbol (access congruence-rule (some-congruence-rule-has-refinement equiv1 temp wrld) :rune)) (access congruence-rule (some-congruence-rule-has-refinement equiv1 temp wrld) :equiv) equiv2 (cons k 'th) fn equiv1 name)) (t (pprogn (cond ((eq equiv1 'equal) (warning$ ctx "Equiv" "The :CONGRUENCE rule ~x0 will have no effect ~ on proofs because ACL2 already knows that ~ ~x1 refines every equivalence relation, ~ including ~x2." name 'equal equiv2)) ((and (eq equiv2 'iff) (mv-let (ts ttree) (type-set (cons-term fn (formals fn wrld)) nil nil nil (ens state) wrld nil nil nil) (declare (ignore ttree)) (ts-subsetp ts *ts-boolean*))) (warning$ ctx "Equiv" "The :CONGRUENCE rule ~x0 can be ~ strengthened by replacing the second ~ equivalence relation, ~x1, by ~x2. See ~ :DOC congruence, in particular (near the ~ end) the Remark on Replacing IFF by EQUAL." name 'iff 'equal)) (t state)) (value nil)))))))))) (defun putnth (val n lst) (declare (xargs :guard (and (true-listp lst) (integerp n) (<= 0 n)))) (cond ((int= n 0) (cons val (cdr lst))) (t (cons (car lst) (putnth val (1- n) (cdr lst)))))) (defun add-congruence-rule-to-congruence (rule k congruence) ; Congruence is an element of the 'congruence property of some n-ary ; function fn. As such, it is of the form (equiv geneqv1 ... geneqvk ; ... geneqvn), where equiv is some equivalence relation and each of ; the geneqvi is a list of congruence-rule records. We add rule to ; geneqvk. (putnth (cons rule (nth k congruence)) k congruence)) (defun add-congruence-rule (rune nume term wrld) ; Suppose term states that equiv2 is preserved by equiv1 in the kth ; argument of fn. Then we add a new :CONGRUENCE rule to the ; 'congruences property of fn recording this fact. The new rule is ; added as the first rule tried for the kth argument of fn while ; maintaining equiv2. In addition, the entry for equiv2 is moved to ; the front of the list of congruences for fn so that when we rewrite ; fn maintaining some equiv3, where equiv2 is a refinement of equiv3, ; we will try equiv2 first. This idea of moving the equiv2 entry to ; the front is not motivated by any example -- this code has not yet ; seen action -- it is just the first-cut design. (mv-let (flg x) (interpret-term-as-congruence-rule (base-symbol rune) term wrld) (declare (ignore flg)) (let ((fn (car x)) (equiv1 (cadr x)) (k (caddr x)) (equiv2 (cadddr x))) (let* ((temp (assoc-eq equiv2 (getprop fn 'congruences nil 'current-acl2-world wrld))) (equiv2-congruence (or temp (cons equiv2 (make-list-ac (arity fn wrld) nil nil)))) (rst (if temp (remove1-equal temp (getprop fn 'congruences nil 'current-acl2-world wrld)) (getprop fn 'congruences nil 'current-acl2-world wrld)))) (putprop fn 'congruences (cons (add-congruence-rule-to-congruence (make congruence-rule :rune rune :nume nume :equiv equiv1) k equiv2-congruence) rst) wrld))))) ;--------------------------------------------------------------------------- ; Section: :DEFINITION rules (deflabel definition :doc ":Doc-Section Rule-Classes make a rule that acts like a function definition~/ ~l[rule-classes] for a general discussion of rule classes and how they are used to build rules from formulas. An example ~c[:]~ilc[corollary] formula from which a ~c[:definition] rule might be built is: ~bv[] Examples: (defthm open-len-twice (implies (true-listp x) (equal (len x) (if (null x) 0 (if (null (cdr x)) 1 (+ 2 (len (cddr x))))))) :rule-classes :definition) ; Same as above, with :controller-alist made explicit: (defthm open-len-twice (implies (true-listp x) (equal (len x) (if (null x) 0 (if (null (cdr x)) 1 (+ 2 (len (cddr x))))))) :rule-classes ((:definition :controller-alist ((len t)))))~/ General Form: (implies hyp (equiv (fn a1 ... an) body)) ~ev[] where ~c[equiv] is an equivalence relation and ~c[fn] is a function symbol other than ~ilc[if], ~ilc[hide], ~ilc[force] or ~ilc[case-split]. Such rules allow ``alternative'' definitions of ~c[fn] to be proved as theorems but used as definitions. These rules are not true ``definitions'' in the sense that they (a) cannot introduce new function symbols and (b) do not have to be terminating recursion schemes. They are just conditional rewrite rules that are controlled the same way we control recursive definitions. We call these ``definition rules'' or ``generalized definitions''. Consider the general form above. Generalized definitions are stored among the ~c[:]~ilc[rewrite] rules for the function ``defined,'' ~c[fn] above, but the procedure for applying them is a little different. During rewriting, instances of ~c[(fn a1 ... an)] are replaced by corresponding instances of ~c[body] provided the ~c[hyp]s can be established as for a ~c[:]~ilc[rewrite] rule and the result of rewriting ~c[body] satisfies the criteria for function expansion. There are two primary criteria, either of which permits expansion. The first is that the ``recursive'' calls of ~c[fn] in the rewritten body have arguments that already occur in the goal conjecture. The second is that the ``controlling'' arguments to ~c[fn] are simpler in the rewritten body. The notions of ``recursive call'' and ``controllers'' are complicated by the provisions for mutually recursive definitions. Consider a ``clique'' of mutually recursive definitions. Then a ``recursive call'' is a call to any function defined in the clique and an argument is a ``controller'' if it is involved in the measure that decreases in all recursive calls. These notions are precisely defined by the definitional principle and do not necessarily make sense in the context of generalized definitional equations as implemented here. But because the heuristics governing the use of generalized definitions require these notions, it is generally up to the user to specify which calls in body are to be considered recursive and what the controlling arguments are. This information is specified in the ~c[:clique] and ~c[:controller-alist] fields of the ~c[:definition] rule class. The ~c[:clique] field is the list of function symbols to be considered recursive calls of ~c[fn]. In the case of a non-recursive definition, the ~c[:clique] field is empty; in a singly recursive definition, it should consist of the singleton list containing ~c[fn]; otherwise it should be a list of all of the functions in the mutually recursive clique with this definition of ~c[fn]. If the ~c[:clique] field is not provided it defaults to ~c[nil] if ~c[fn] does not occur as a function symbol in ~c[body] and it defaults to the singleton list containing ~c[fn] otherwise. Thus, ~c[:clique] must be supplied by the user only when the generalized definition rule is to be treated as one of several in a mutually recursive clique. The ~c[:controller-alist] is an alist that maps each function symbol in the ~c[:clique] to a mask specifying which arguments are considered controllers. The mask for a given member of the clique, ~c[fn], must be a list of ~c[t]'s and ~c[nil]'s of length equal to the arity of ~c[fn]. A ~c[t] should be in each argument position that is considered a ``controller'' of the recursion. For a function admitted under the principle of definition, an argument controls the recursion if it is one of the arguments measured in the termination argument for the function. But in generalized definition rules, the user is free to designate any subset of the arguments as controllers. Failure to choose wisely may result in the ``infinite expansion'' of definitional rules but cannot render ACL2 unsound since the rule being misused is a theorem. If the ~c[:controller-alist] is omitted it can sometimes be defaulted automatically by the system. If the ~c[:clique] is ~c[nil], the ~c[:controller-alist] defaults to ~c[nil]. If the ~c[:clique] is a singleton containing ~c[fn], the ~c[:controller-alist] defaults to the controller alist computed by ~c[(defun fn args body)]. (The user can obtain some control over this analysis by setting the default ruler-extenders; ~pl[ruler-extenders].) If the ~c[:clique] contains more than one function, the user must supply the ~c[:controller-alist] specifying the controllers for each function in the clique. This is necessary since the system cannot determine and thus cannot analyze the other definitional equations to be included in the clique. For example, suppose ~c[fn1] and ~c[fn2] have been defined one way and it is desired to make ``alternative'' mutually recursive definitions available to the rewriter. Then one would prove two theorems and store each as a ~c[:definition] rule. These two theorems would exhibit equations ``defining'' ~c[fn1] and ~c[fn2] in terms of each other. No provision is here made for exhibiting these two equations as a system of equations. One is proved and then the other. It just so happens that the user intends them to be treated as mutually recursive definitions. To achieve this end, both ~c[:definition] rules should specify the ~c[:clique] ~c[(fn1 fn2)] and should specify a suitable ~c[:controller-alist]. If, for example, the new definition of ~c[fn1] is controlled by its first argument and the new definition of ~c[fn2] is controlled by its second and third (and they each take three arguments) then a suitable ~c[:controller-alist] would be ~c[((fn1 t nil nil) (fn2 nil t t))]. The order of the pairs in the alist is unimportant, but there must be a pair for each function in the clique. Inappropriate heuristic advice via ~c[:clique] and ~c[:controller-alist] can cause ``infinite expansion'' of generalized definitions, but cannot render ACL2 unsound. Note that the actual definition of ~c[fn1] has the runic name ~c[(:definition fn1)]. The runic name of the alternative definition is ~c[(:definition lemma)], where ~c[lemma] is the name given to the event that created the generalized ~c[:definition] rule. This allows theories to switch between various ``definitions'' of the functions. By default, a ~c[:definition] rule establishes the so-called ``body'' of a function. The body is used by ~c[:expand] ~il[hints], and it is also used heuristically by the theorem prover's preprocessing (the initial simplification using ``simple'' rules that is controlled by the ~c[preprocess] symbol in ~c[:do-not] ~il[hints]), induction analysis, and the determination for when to warn about non-recursive functions in rules. The body is also used by some heuristics involving whether a function is recursively defined, and by the ~c[expand], ~c[x], and ~c[x-dumb] commands of the ~il[proof-checker]. ~l[rule-classes] for a discussion of the optional field ~c[:install-body] of ~c[:definition] rules, which controls whether a ~c[:definition] rule is used as described in the paragraph above. Note that even if ~c[:install-body nil] is supplied, the rewriter will still rewrite with the ~c[:definition] rule; in that case, ACL2 just won't install a new body for the top function symbol of the left-hand side of the rule, which for example affects the application of ~c[:expand] hints as described in the preceding paragraph. Also ~pl[set-body] and ~pl[show-bodies] for how to change the body of a function symbol. Note only that if you prove a definition rule for function ~c[foo], say, ~c[foo-new-def], you will need to refer to that definition as ~c[foo-new-def] or as ~c[(:DEFINITION foo-new-def)]. That is because a ~c[:definition] rule does not change the meaning of the symbol ~c[foo] for ~c[:use] ~il[hints], nor does it change the meaning of the symbol ~c[foo] in theory expressions; ~pl[theories], in particular the discussion there of runic designators. Similarly ~c[:]~ilc[pe] ~c[foo] and ~c[:]~ilc[pf] ~c[foo] will still show the original definition of ~c[foo]. The definitional principle, ~ilc[defun], actually adds ~c[:definition] rules. Thus the handling of generalized definitions is exactly the same as for ``real'' definitions because no distinction is made in the implementation. Suppose ~c[(fn x y)] is ~ilc[defun]'d to be ~c[body]. Note that ~ilc[defun] (or ~ilc[defuns] or ~ilc[mutual-recursion]) can compute the clique for ~c[fn] from the syntactic presentation and it can compute the controllers from the termination analysis. Provided the definition is admissible, ~ilc[defun] adds the ~c[:definition] rule ~c[(equal (fn x y) body)].~/") (defun chk-destructure-definition (name term ctx wrld state) (mv-let (hyps equiv fn args body ttree) (destructure-definition term nil nil wrld nil) (declare (ignore hyps equiv args body ttree)) (cond ((null fn) (er soft ctx "~x0 cannot be stored as a :DEFINITION rule ~ because the :COROLLARY formula, ~p1, is not of ~ the proper form. See :DOC definition." name (untranslate term t wrld))) (t (value nil))))) (defun chk-acceptable-definition-install-body (fn hyps equiv args body install-body install-body-supplied-p ctx state) (let ((install-body (if install-body-supplied-p install-body :NORMALIZE)) (er-preamble (msg "For a :DEFINITION rule with non-nil :INSTALL-BODY value~@0," (if install-body-supplied-p "" " (default :NORMALIZE)"))) (install-body-msg (if install-body-supplied-p "" (msg " Please add :INSTALL-BODY ~x0 to your :DEFINITION rule ~ class." nil)))) (cond ((null install-body) (value nil)) ((member-eq fn *definition-minimal-theory*) ; This restriction is to allow us to assume that calls of (body fn t wrld), ; which occur in several places in the source code, refer to the original ; normalized body of fn, which excuses us from tracking the corresponding rune. (er soft ctx "~@0 the function symbol being called on the left-hand side, ~x1, ~ must not be among the following built-in functions: ~&2.~@3 ~ Please contact the implementors if you feel that this is an ~ encumbrance to you." er-preamble fn *definition-minimal-theory* install-body-msg)) ((not (arglistp args)) (er soft ctx "~@0 the arguments on the left-hand side of the rule must be a list ~ of distinct variables, unlike ~x1.~@2 See :DOC definition." er-preamble args install-body-msg)) ((not (eq equiv 'equal)) (er soft ctx "~@0 the equivalence relation on the left-hand side of the rule ~ must be ~x1, unlike ~x2.~@3 See :DOC definition." er-preamble 'equal equiv install-body-msg)) ((free-varsp-member-lst hyps args) (er soft ctx "~@0 the hypotheses must not contain free variables that are not ~ among the variables on its left-hand side. The ~#1~[variable ~&1 ~ violates~/variables ~&1 violate~] this requirement.~@2 See :DOC ~ definition." er-preamble (set-difference-eq (all-vars1-lst hyps nil) args) install-body-msg)) ((free-varsp-member body args) (er soft ctx "~@0 the right-hand side of a :DEFINITION rule must not contain free ~ variables that are not among the variables on its left-hand side. ~ The ~#1~[variable ~&1 violates~/variables ~&1 violate~] this ~ requirement.~@2 See :DOC definition." er-preamble (set-difference-eq (all-vars body) args) install-body-msg)) (t (value nil))))) (defun chk-acceptable-definition-rule (name clique controller-alist install-body-tail term ctx ens wrld state) ; Term is a translated term that is the :COROLLARY of a :DEFINITION with the ; given :CLIQUE and :CONTROLLER-ALIST. We know the clique and alist are well ; formed. But to check that during rule class translation, we had to ; destructure term with chk-destructure-definition and it must have passed. ; The only things left to check are the various subsumption-type conditions on ; rewrite rules, as well as the :install-body argument, passed in as ; install-body-tail of the form (:install-body value ...) if :install-body was ; supplied by the user, and as nil otherwise. (mv-let (hyps equiv fn args body ttree) (destructure-definition term nil ens wrld nil) (cond ((eq fn 'hide) (er soft ctx "It is illegal to make a definition rule for ~x0, because of the ~ special role of this function in the ACL2 rewriter." 'hide)) (t (let ((rule (make rewrite-rule :rune *fake-rune-for-anonymous-enabled-rule* :nume nil :hyps (preprocess-hyps hyps) :equiv equiv :lhs (mcons-term fn args) :var-info (var-counts args body) :rhs body :subclass 'definition :heuristic-info (cons clique controller-alist) :backchain-limit-lst nil))) (er-progn (chk-rewrite-rule-warnings name nil ; match-free nil ; loop-stopper rule ctx ens wrld state) (chk-acceptable-definition-install-body fn hyps equiv args body (cadr install-body-tail) install-body-tail ctx state) (value ttree))))))) ; add-definition-rule was defined in defuns.lisp in order to implement ; defuns-fn0. ;--------------------------------------------------------------------------- ; Section: :INDUCTION rules (deflabel induction :doc ":Doc-Section Rule-Classes make a rule that suggests a certain induction~/ ~bv[] Example: (defthm recursion-by-sub2-induction-rule t :rule-classes ((:induction :pattern (* 1/2 i) :condition (and (integerp i) (>= i 0)) :scheme (recursion-by-sub2 i)))) ~ev[]~/ In ACL2, as in Nqthm, the functions in a conjecture ``suggest'' the inductions considered by the system. Because every recursive function must be admitted with a justification in terms of a measure that decreases in a well-founded way on a given set of ``controlling'' arguments, every recursive function suggests a dual induction scheme that ``unwinds'' the function from a given application. For example, since ~ilc[append] (actually ~ilc[binary-append], but we'll ignore the distinction here) decomposes its first argument by successive ~ilc[cdr]s as long as it is a non-~c[nil] true list, the induction scheme suggested by ~c[(append x y)] has a base case supposing ~c[x] to be either not a true list or to be ~c[nil] and then has an induction step in which the induction hypothesis is obtained by replacing ~c[x] by ~c[(cdr x)]. This substitution decreases the same measure used to justify the definition of ~ilc[append]. Observe that an induction scheme is suggested by a recursive function application only if the controlling actuals are distinct variables, a condition that is sufficient to ensure that the ``substitution'' used to create the induction hypothesis is indeed a substitution and that it drives down a certain measure. In particular, ~c[(append (foo x) y)] does not suggest an induction unwinding ~ilc[append] because the induction scheme suggested by ~c[(append x y)] requires that we substitute ~c[(cdr x)] for ~c[x] and we cannot do that if ~c[x] is not a variable symbol. Once ACL2 has collected together all the suggested induction schemes it massages them in various ways, combining some to simultaneously unwind certain cliques of functions and vetoing others because they ``flaw'' others. We do not further discuss the induction heuristics here; the interested reader should see Chapter XIV of A Computational Logic (Boyer and Moore, Academic Press, 1979) which represents a fairly complete description of the induction heuristics of ACL2. However, unlike Nqthm, ACL2 provides a means by which the user can elaborate the rules under which function applications suggest induction schemes. Such rules are called ~c[:induction] rules. The definitional principle automatically creates an ~c[:induction] rule, named ~c[(:induction fn)], for each admitted recursive function, ~c[fn]. It is this rule that links applications of ~c[fn] to the induction scheme it suggests. Disabling ~c[(:induction fn)] will prevent ~c[fn] from suggesting the induction scheme derived from its recursive definition. It is possible for the user to create additional ~c[:induction] rules by using the ~c[:induction] rule class in ~ilc[defthm]. Technically we are ``overloading'' ~ilc[defthm] by using it in the creation of ~c[:induction] rules because no theorem need be proved to set up the heuristic link represented by an ~c[:induction] rule. However, since ~ilc[defthm] is generally used to create rules and rule-class objects are generally used to specify the exact form of each rule, we maintain that convention and introduce the notion of an ~c[:induction] rule. An ~c[:induction] rule can be created from any lemma whatsoever. ~bv[] General Form of an :induction Lemma or Corollary: T General Form of an :induction rule-class: (:induction :pattern pat-term :condition cond-term :scheme scheme-term) ~ev[] where ~c[pat-term], ~c[cond-term], and ~c[scheme-term] are all terms, ~c[pat-term] is the application of a function symbol, ~c[fn], ~c[scheme-term] is the application of a function symbol, ~c[rec-fn], that suggests an induction, and, finally, every free variable of ~c[cond-term] and ~c[scheme-term] is a free variable of ~c[pat-term]. We actually check that ~c[rec-fn] is either recursively defined ~-[] so that it suggests the induction that is intrinsic to its recursion ~-[] or else that another ~c[:induction] rule has been proved linking a call of ~c[rec-fn] as the ~c[:pattern] to some scheme. The induction rule created is used as follows. When an instance of the ~c[:pattern] term occurs in a conjecture to be proved by induction and the corresponding instance of the ~c[:condition] term is known to be non-~c[nil] (by type reasoning alone), the corresponding instance of the ~c[:scheme] term is created and the rule ``suggests'' the induction, if any, suggested by that term. (Analysis of that term may further involve induction rules, though the applied rule is removed from consideration during that further analysis, in order to avoid looping.) If ~c[rec-fn] is recursive, then the suggestion is the one that unwinds that recursion. Consider, for example, the example given above, ~bv[] (:induction :pattern (* 1/2 i) :condition (and (integerp i) (>= i 0)) :scheme (recursion-by-sub2 i)). ~ev[] In this example, we imagine that ~c[recursion-by-sub2] is the function: ~bv[] (defun recursion-by-sub2 (i) (if (and (integerp i) (< 1 i)) (recursion-by-sub2 (- i 2)) t)) ~ev[] Observe that this function recursively decomposes its integer argument by subtracting ~c[2] from it repeatedly and stops when the argument is ~c[1] or less. The value of the function is irrelevant; it is its induction scheme that concerns us. The induction scheme suggested by ~c[(recursion-by-sub2 i)] is ~bv[] (and (implies (not (and (integerp i) (< 1 i))) ; base case (:p i)) (implies (and (and (integerp i) (< 1 i)) ; induction step (:p (- i 2))) (:p i))) ~ev[] We can think of the base case as covering two situations. The first is when ~c[i] is not an integer. The second is when the integer ~c[i] is ~c[0] or ~c[1]. In the base case we must prove ~c[(:p i)] without further help. The induction step deals with those integer ~c[i] greater than ~c[1], and inductively assumes the conjecture for ~c[i-2] while proving it for ~c[i]. Let us call this scheme ``induction on ~c[i] by twos.'' Suppose the above ~c[:induction] rule has been added. Then an occurrence of, say, ~c[(* 1/2 k)] in a conjecture to be proved by induction would suggest, via this rule, an induction on ~c[k] by twos, provided ~c[k] was known to be a nonnegative integer. This is because the induction rule's ~c[:pattern] is matched in the conjecture, its ~c[:condition] is satisfied, and the ~c[:scheme] suggested by the rule is that derived from ~c[(recursion-by-sub2 k)], which is induction on ~c[k] by twos. Similarly, the term ~c[(* 1/2 (length l))] would suggest no induction via this rule, even though the rule ``fires'' because it creates the ~c[:scheme] ~c[(recursion-by-sub2 (length l))] which suggests no inductions unwinding ~c[recursion-by-sub2] (since the controlling argument of ~c[recursion-by-sub2] in this ~c[:scheme] is not a variable symbol). Continuing this example one step further illustrates the utility of ~c[:induction] rules. We could define the function ~c[recursion-by-cddr] that suggests the induction scheme decomposing its ~ilc[consp] argument two ~ilc[cdr]s at a time. We could then add the ~c[:induction] rule linking ~c[(* 1/2 (length x))] to ~c[(recursion-by-cddr x)] and arrange for ~c[(* 1/2 (length l))] to suggest induction on ~c[l] by ~ilc[cddr]. Observe that ~c[:induction] rules require no proofs to be done. Such a rule is merely a heuristic link between the ~c[:pattern] term, which may occur in conjectures to be proved by induction, and the ~c[:scheme] term, from which an induction scheme may be derived. Hence, when an ~c[:induction] rule-class is specified in a ~ilc[defthm] event, the theorem proved is irrelevant. The easiest theorem to prove is, of course, ~c[t]. Thus, we suggest that when an ~c[:induction] rule is to be created, the following form be used: ~bv[] (defthm name T :rule-classes ((:induction :pattern pat-term :condition cond-term :scheme scheme-term))) ~ev[] The name of the rule created is ~c[(:induction name)]. When that rune is disabled the heuristic link between ~c[pat-term] and ~c[scheme-term] is broken.") (defun chk-acceptable-induction-rule (name term ctx wrld state) ; This function is really a no-op. It exists simply for regularity. (declare (ignore name term ctx wrld)) (value nil)) (defun add-induction-rule (rune nume pat-term cond-term scheme-term term wrld) (declare (ignore term)) (let ((fn (ffn-symb pat-term))) (putprop fn 'induction-rules (cons (make induction-rule :rune rune :nume nume :pattern pat-term :condition cond-term :scheme scheme-term) (getprop fn 'induction-rules nil 'current-acl2-world wrld)) wrld))) ;--------------------------------------------------------------------------- ; Section: :TYPE-SET-RECOGNIZER-TERM Rules (deflabel type-set-inverter :doc ":Doc-Section Rule-Classes exhibit a new decoding for an ACL2 type-set~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example Rule Class: (:type-set-inverter :corollary (equal (and (counting-number x) (not (equal x 0))) (and (integerp x) (< 0 x))) :type-set 2)~/ General Forms of Rule Class: :type-set-inverter, or (:type-set-inverter :type-set n) General Form of Theorem or Corollary: (EQUAL new-expr old-expr) ~ev[] where ~c[n] is a ~ilc[type-set] (~pl[type-set]) and ~c[old-expr] is the term containing ~c[x] as a free variable that ACL2 currently uses to recognize ~ilc[type-set] ~c[n]. For a given ~c[n], the exact form of ~c[old-expr] is generated by ~bv[] (convert-type-set-to-term 'x n (ens state) (w state) nil)]. ~ev[] If the ~c[:]~ilc[type-set] field of the rule-class is omitted, we attempt to compute it from the right-hand side, ~c[old-expr], of the corollary. That computation is done by ~c[type-set-implied-by-term] (~pl[type-set]). However, it is possible that the type-set we compute from ~c[lhs] does not have the required property that when inverted with ~c[convert-type-set-to-term] the result is ~c[lhs]. If you omit ~c[:]~ilc[type-set] and an error is caused because ~c[lhs] has the incorrect form, you should manually specify both ~c[:]~ilc[type-set] and the ~c[lhs] generated by ~c[convert-type-set-to-term]. The rule generated will henceforth make ~c[new-expr] be the term used by ACL2 to recognize type-set ~c[n]. If this rule is created by a ~ilc[defthm] event named ~c[name] then the rune of the rule is ~c[(:type-set-inverter name)] and by disabling that rune you can prevent its being used to decode type-sets. Type-sets are inverted when forced assumptions are turned into formulas to be proved. In their internal form, assumptions are essentially pairs consisting of a context and a goal term, which was forced. Abstractly a context is just a list of hypotheses which may be assumed while proving the goal term. But actually contexts are alists which pair terms with type-sets, encoding the current hypotheses. For example, if the original conjecture contained the hypothesis ~c[(integerp x)] then the context used while working on that conjecture will include the assignment to ~c[x] of the type-set ~c[*ts-integer*].") (defun chk-acceptable-type-set-inverter-rule (name ts term ctx ens wrld state) (let* ((vars (all-vars term))) (cond ((not (and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'equal) (equal vars '(X)) (equal (all-vars (fargn term 1)) (all-vars (fargn term 2))))) (er soft ctx "The :COROLLARY of a :TYPE-SET-INVERTER rule must be of the form ~ (equal old-expr new-expr), where new-expr and old-expr are each ~ terms containing the single free variable X. ~p0 is not of this ~ form, so ~x1 is an illegal :TYPE-SET-INVERTER rule. See :DOC ~ type-set-inverter." (untranslate term t wrld) name)) (t (mv-let (ts2 ttree) (cond ((null ts) (type-set-implied-by-term 'X nil (fargn term 2) ens wrld nil)) (t (mv ts nil))) (cond ((not (and (integerp ts2) (<= *min-type-set* ts2) (<= ts2 *max-type-set*))) ; It is believed neither of the following errors will ever occur. The hard ; error won't occur because type-set-implied-by-term always returns a type-set. ; The soft error won't occur because translate-rule-class-alist insists, when a ; :TYPE-SET is supplied, that the type-set be proper and causes this error ; there. (cond ((null ts) (mv t (er hard ctx "Type-set-implied-by-term returned ~x0 instead of a ~ type-set!" ts2) state)) (t (er soft ctx "The :TYPE-SET of a :TYPE-SET-INVERTER rule must be a ~ type-set, i.e., an integer n such that ~x0 <= n <= ~x1. ~ But ~x2 is not so ~x3 is an illegal :TYPE-SET-INVERTER ~ rule. See :DOC type-set-inverter." *min-type-set* *max-type-set* ts2 name)))) (t (mv-let (required-old-expr ttree) (convert-type-set-to-term 'X ts2 ens wrld ttree) (cond ((not (tautologyp (fcons-term* 'iff (fargn term 2) required-old-expr) wrld)) (er soft ctx "The right-hand side of the :COROLLARY of a :TYPE-SET-INVERTER ~ rule with :TYPE-SET ~x0 must be propositionally equivalent to ~ ~p1 but you have specified ~p2. Thus, ~x3 is an illegal ~ :TYPE-SET-INVERTER rule. See :doc type-set-inverter." ts2 (untranslate required-old-expr t wrld) (untranslate (fargn term 2) t wrld) name)) (t (value ttree))))))))))) (defun add-type-set-inverter-rule (rune nume ts term ens wrld) (mv-let (ts ttree) (cond ((null ts) (type-set-implied-by-term 'X nil (fargn term 2) ens wrld nil)) (t (mv ts nil))) (declare (ignore ttree)) (global-set 'type-set-inverter-rules (cons (make type-set-inverter-rule :nume nume :rune rune :ts ts :terms (flatten-ands-in-lit (fargn term 1))) (global-val 'type-set-inverter-rules wrld)) wrld))) ; -------------------------------------------------------------------------- ; Section: :TAU-SYSTEM rules ; The code for adding :tau-system rules is in a prior file, namely ; history-management, where it is used in install-event as part of ; tau-auto-modep. ;--------------------------------------------------------------------------- ; Section: :CLAUSE-PROCESSOR Rules (deflabel clause-processor :doc ":Doc-Section Rule-Classes make or apply a ~c[:clause-processor] rule (goal-level simplifier)~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. ~bv[] Example (which we'll return to, below): (defthm correctness-of-note-fact-clause-processor (implies (and (pseudo-term-listp cl) (alistp a) (evl0 (conjoin-clauses (note-fact-clause-processor cl term)) a)) (evl0 (disjoin cl) a)) :rule-classes :clause-processor) ~ev[] Also ~pl[define-trusted-clause-processor] for documentation of an analogous utility that does not require the clause-processor to be proved correct. But please read the present documentation before reading about that utility. Both utilities designate functions ``clause-processors''. Such functions must be executable ~-[] hence not constrained by virtue of being introduced in the ~il[signature] of an ~ilc[encapsulate] ~-[] and must respect ~il[stobj] and output arity restrictions. For example, something like ~c[(car (mv ...))] is illegal; also ~pl[signature]. We begin this documentation with an introduction, focusing on an example, and then conclude with details. You might find it most useful simply to look at the examples in community books directory ~c[books/clause-processors/]; see file ~c[Readme.lsp] in that directory. A ~c[:clause-processor] rule installs a simplifier at the level of goals, where a goal is represented as a ~em[clause]: a list of ~il[term]s that is implicitly viewed as a disjunction (the application of ~ilc[OR]). For example, if ACL2 prints a goal in the form ~c[(implies (and p q) r)], then the clause might be the one-element list containing the internal representation of this term ~-[] ~c[(implies (if p q 'nil) r)] ~-[] but more likely, the corresponding clause is ~c[((not p) (not q) r)]. Note that the members of a clause are ~em[translated] terms; ~pl[term]. For example, they do not contains calls of the macro ~c[AND], and constants are quoted. Note that clause-processor simplifiers are similar to metafunctions, and similar efficiency considerations apply. ~l[meta], in particular the discussion on how to ``make a metafunction maximally efficient.'' Unlike rules of class ~c[:]~ilc[meta], rules of class ~c[:clause-processor] must be applied by explicit ~c[:clause-processor] ~il[hints]; they are not applied automatically (unless by way of computed hints; ~pl[computed-hints]). But ~c[:clause-processor] rules can be useful in situations for which it is more convenient to code a simplifier that manipulates the entire goal clause rather than individual subterms of terms in the clause. We begin with a simple illustrative example: a clause-processor that assumes an alleged fact (named ~c[term] in the example) and creates a separate goal to prove that fact. We can extend the hypotheses of the current goal (named ~c[cl] in the example) with a term by adding the negation of that term to the clause (disjunctive) representation of that goal. So the following returns a list of two clauses: the result of adding ~c[term] as a hypothesis to the input clause, as just described, and a second clause consisting only of that term. This list of two clauses can be viewed as the conjunction of the first clause and the second clause (where again, each clause is viewed as a disjunction). ~bv[] (defun note-fact-clause-processor (cl term) (declare (xargs :guard t)) ; optional, for better efficiency (list (cons (list 'not term) cl) (list term))) ~ev[] As with ~c[:]~ilc[meta] rules, we need to introduce a suitable evaluator; ~pl[defevaluator] if you want details. Since we expect to reason about the function ~ilc[NOT], because of its role in ~c[note-fact-clause-processor] as defined above, we include ~c[NOT] in the set of functions known to this evaluator. We also include ~c[IF], as is often a good idea. ~bv[] (defevaluator evl0 evl0-list ((not x) (if x y z))) ~ev[] ACL2 can now prove the following theorem automatically. (This is the example displayed at the outset of this ~il[documentation] topic.) Of course, ~c[:clause-processor] rules about clause-processor functions less trivial than ~c[note-fact-clause-processor] may require lemmas to be proved first! The function ~c[disjoin] takes a clause and returns its disjunction (the result of applying ~ilc[OR] to its members), and ~c[conjoin-clauses] applies ~c[disjoin] to every element of a given list of clauses and then conjoins (applies ~c[AND]) to the corresponding list of resulting terms. ~bv[] (defthm correctness-of-note-fact-clause-processor (implies (and (pseudo-term-listp cl) (alistp a) (evl0 (conjoin-clauses (note-fact-clause-processor cl term)) a)) (evl0 (disjoin cl) a)) :rule-classes :clause-processor) ~ev[] Now let us submit a silly but illustrative example theorem to ACL2, to show how a corresponding ~c[:clause-processor] hint is applied. The hint says to apply the clause-processor function, ~c[note-fact-clause-processor], to the current goal clause and a ``user hint'' as the second argument of that function, in this case ~c[(equal a a)]. Thus, a specific variable, ~c[clause], is always bound to the current goal clause for the evaluation of the ~c[:clause-processor] hint, to produce a list of clauses. Since two subgoals are created below, we know that this list contained two clauses. Indeed, these are the clauses returned when ~c[note-fact-clause-processor] is applied to two arguments: the current clause, which is the one-element list ~c[((equal (car (cons x y)) x))], and the user hint, ~c[(equal a a)]. ~bv[] ACL2 !>(thm (equal (car (cons x y)) x) :hints ((\"Goal\" :clause-processor (note-fact-clause-processor clause '(equal a a))))) [Note: A hint was supplied for our processing of the goal above. Thanks!] We now apply the verified :CLAUSE-PROCESSOR function NOTE-FACT-CLAUSE- PROCESSOR to produce two new subgoals. Subgoal 2 (IMPLIES (EQUAL A A) (EQUAL (CAR (CONS X Y)) X)). But we reduce the conjecture to T, by the :executable-counterpart of IF and the simple :rewrite rule CAR-CONS. Subgoal 1 (EQUAL A A). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. Summary Form: ( THM ...) Rules: ((:EXECUTABLE-COUNTERPART IF) (:EXECUTABLE-COUNTERPART NOT) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:REWRITE CAR-CONS)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !> ~ev[]~/ That concludes our introduction to clause-processor rules and hints. We turn now to detailed documentation. The ~il[signature] of a clause-processor function, ~c[CL-PROC], must have one of the following forms. Here, each ~c[st_i] is a ~il[stobj] (possibly ~c[state]) while the other parameters and results are not stobjs (~pl[stobj]). Note that there need not be input stobjs in [3] ~-[] i.e., ~c[k] can be 0 ~-[] and even if there are, there need not be output stobjs. ~bv[] [1] ((CL-PROC cl) => cl-list) [2] ((CL-PROC cl hint) => cl-list) [3] ((CL-PROC cl hint st_1 ... st_k) => (mv erp cl-list st_i1 ... st_in)) ~ev[] In [3], we think of the first component of the result as an error flag. Indeed, a proof will instantly abort if that error flag is not ~c[nil]. We next discuss the legal forms of ~c[:clause-processor] rules, followed below by a discussion of ~c[:clause-processor] ~il[hints]. In the discussion below, we use lower-case names to represent specific symbols, for example ~c[implies], and we use upper-case names to represent more arbitrary pieces of syntax (which we will describe), for example, ~c[CL]. If a ~c[:]~ilc[rule-classes] specification includes ~c[:clause-processor], then the corresponding term must have the following form. (Additional ``meta-extract'' hypotheses, not shown or discussed below, may be included as desired in order to use facts from the logical ~ilc[world] to help prove the rule; ~pl[meta-extract] for explanation of this advanced feature.) ~bv[] (implies (and (pseudo-term-listp CL) (alistp A) (EVL (conjoin-clauses ) B)) (EVL (disjoin CL) A)) ~ev[] Here ~c[EVL] is a known evaluator; ~c[CL] and ~C[A] are distinct non-stobj variables; and ~c[] is an expression representing the clauses returned by the clause-processor function ~c[CL-PROC], whose form depends on the ~il[signature] of that function, as follows. Typically ~c[B] is ~c[A], but it can be any term (useful when generalization is occurring; see the example ``Test generalizing alist'' in community book ~c[books/clause-processors/basic-examples.lisp]). For cases [1] and [2] above, ~c[] is of the form ~c[(CL-PROC CL)] or ~c[(CL-PROC CL HINT)], respectively, where in the latter case ~c[HINT] is a non-stobj variable distinct from the variables ~c[CL] and ~c[A]. For case [3], ~c[] is of the form ~bv[] (clauses-result (CL-PROC CL HINT st_1 ... st_k)) ~ev[] where the ~c[st_i] are the specific stobj names mentioned in [3]. Logically, ~c[clauses-result] returns the ~ilc[cadr] if the ~ilc[car] is ~c[NIL], and otherwise (for the error case) returns a list containing the empty (false) clause. So in the non-error case, ~c[clauses-result] picks out the second result, denoted ~c[cl-list] in [3] above, and in the error case the implication above trivially holds. In the above theorem, we are asked to prove ~c[(EVL (disjoin CL) A)] assuming that the conjunction of all clauses produced by the clause processor evaluates to a non-~c[nil] value under some alist ~c[B]. In fact, we can choose ~c[B] so as to allow us to assume evaluations of the generated clauses over many different alists. This technique is discussed in the community book ~c[books/clause-processors/multi-env-trick.lisp], which introduces some macros that may be helpful in accomplishing proofs of this type. The clause-processor function, ~c[CL], must have a guard that ACL2 can trivially prove from the hypotheses that the first argument of ~c[CL] is known to be a ~c[pseudo-term-listp] and any ~il[stobj] arguments are assumed to satisfy their stobj predicates. Next we specify the legal forms for ~c[:clause-processor] ~il[hints]. These depend on the signature as described in [1] through [3] above. Below, as above, ~c[CL-PROC] is the clause-processor function, and references to ``~c[clause]'' refer to that exact variable (not, for example, to ~c[cl]). In each of the three cases, the forms shown for that case are equivalent; in particular, the ~c[:function] syntax is simply a convenience for the final form in each case. Signature [1], ~c[((cl-proc cl) => cl-list)]: ~bv[] :clause-processor CL-PROC :clause-processor (:function CL-PROC) :clause-processor (CL-PROC clause) ~ev[] or any term macroexpanding to ~c[(CL-PROC clause)]. Signature [2], ((cl-proc cl hint) => cl-list): ~bv[] :clause-processor (:function CL-PROC :hint HINT) :clause-processor (CL-PROC clause HINT) ~ev[] or any term macroexpanding to ~c[(CL-PROC clause HINT)], where ~c[HINT] is any term with at most ~c[CLAUSE] free. Signature [3], ((CL-PROC cl hint ...) => (mv erp cl-list ...)) ~bv[] :clause-processor (:function CL-PROC :hint HINT) :clause-processor (CL-PROC clause HINT st_1 ... st_k) ~ev[] or any term macroexpanding to ~c[(CL-PROC clause HINT st_1 ... st_k)], where ~c[HINT] is any term with at most ~c[CLAUSE] free. A ~c[:clause-processor] hint causes the proof to abort if the result returned by evaluating the suitable ~c[CL-PROC] call, as above, is not a list of clauses, i.e., a list of (translated) ~il[term] lists. The proof also aborts if in case [3] the first (~c[erp]) value returned is not ~c[nil], in which case ~c[erp] is used for printing an error message as follows: if it is a string, then that string is printed; but if it is a non-empty true list whose first element is a string, then it is printed as though by ~c[(fmt ~~@0 (list (cons #\\0 erp)) ...)] (~pl[fmt]). Otherwise, a non-~c[nil] ~c[erp] value causes a generic error message to be printed. If there is no error as above, but the ~c[CL-PROC] call returns clause list whose single element is equal to the input clause, then the hint is ignored since we are left with the goal with which we started. In that case, the other prover processes are then applied as usual. You can see all current ~c[:clause-processor] rules by issuing the following command: ~c[(print-clause-processor-rules)]. The following paper discusses ACL2 clause-processors at a high level suitable for a non-ACL2 audience:~bq[] M. Kaufmann, J S. Moore, S. Ray, and E. Reeber, ``Integrating External Deduction Tools with ACL2.'' ~em[Journal of Applied Logic] (Special Issue: Empirically Successful Computerized Reasoning), Volume 7, Issue 1, March 2009, pp. 3--25. Also published online (DOI ~c[10.1016/j.jal.2007.07.002]). Preliminary version in: Proceedings of the 6th International Workshop on the Implementation of Logics (IWIL 2006) (C. Benzmueller, B. Fischer, and G. Sutcliffe, editors), CEUR Workshop Proceedings Vol. 212, Phnom Penh, Cambodia, pp. 7-26, November 2006, ~url[http://ceur-ws.org/Vol-212/].~eq[]") (defun tilde-@-illegal-clause-processor-sig-msg (cl-proc stobjs-in stobjs-out) ; A clause-processor should have signature of the form ; (cl-proc cl) => cl-list ; or ; (cl-proc cl hint) => cl-list ; or ; (cl-proc cl hint st_1 ... st_k) => (erp cl-list st_i1 ... st_in) (cond ((null (cdr stobjs-out)) ; first two signatures (cond ((car stobjs-out) (msg "~x0 returns a single argument but it is a stobj" cl-proc)) ((or (equal stobjs-in '(nil)) (equal stobjs-in '(nil nil))) nil) (t (msg "~x0 returns a single argument, but doesn't take exactly one ~ or two arguments, both not stobjs" cl-proc)))) ((and ; the final (third) class of signatures above (null (car stobjs-in)) (cdr stobjs-in) (null (cadr stobjs-in)) (not (member-eq nil (cddr stobjs-in))) (null (car stobjs-out)) (cdr stobjs-out) (null (cadr stobjs-out)) (not (member-eq nil (cddr stobjs-out)))) nil) (t (msg "both the arguments and results of ~x0 in this case are expected to ~ contain stobjs in exactly all positions other than the first two" cl-proc)))) (defun destructure-clause-processor-rule (term) (case-match term (('implies hyp (ev ('disjoin clause) alist)) (mv-let (hyps meta-extract-flg) (remove-meta-extract-global-hyps (remove1-equal (fcons-term* 'pseudo-term-listp clause) (remove1-equal (fcons-term* 'alistp alist) (flatten-ands-in-lit hyp))) ev) (case-match hyps (((ev ('conjoin-clauses cl-result) &)) (case-match cl-result (('clauses-result (cl-proc !clause . rest-args)) (mv t cl-proc clause alist rest-args ev (cadr cl-result) meta-extract-flg)) ((cl-proc !clause . rest-args) (mv nil cl-proc clause alist rest-args ev cl-result meta-extract-flg)) (& (mv :error nil nil nil nil nil nil nil)))) (& (mv :error nil nil nil nil nil nil nil))))) (& (mv :error nil nil nil nil nil nil nil)))) (defun chk-acceptable-clause-processor-rule (name term ctx wrld state) ; Note that term has been translated (as it comes from a translated rule ; class), but not for execution. (let ((str "No :CLAUSE-PROCESSOR rule can be generated from ~x0 ~ because~|~%~p1~|~%does not have the necessary form: ~@2. See ~ :DOC clause-processor.")) (mv-let (clauses-result-call-p cl-proc clause alist rest-args ev cl-proc-call meta-extract-flg) (destructure-clause-processor-rule term) (cond ((eq clauses-result-call-p :error) (er soft ctx str name (untranslate term t wrld) "it fails to satisfy basic syntactic criteria")) ((not (and (symbolp cl-proc) (function-symbolp cl-proc wrld))) (er soft ctx str name (untranslate term t wrld) ; We may never see the following message, but it seems harmless to do this ; check. (msg "the symbol ~x0 is not a function symbol in the current world" cl-proc))) (t (mv-let (erp t-cl-proc-call bindings state) ; Here we catch the use of the wrong stobjs. Other checking is done below. (translate1 cl-proc-call :stobjs-out ; clause-processor call must be executable '((:stobjs-out . :stobjs-out)) t ctx wrld state) (declare (ignore bindings)) (cond (erp (er soft ctx str name (untranslate term t wrld) (msg "the clause-processor call is not in a form suitable ~ for evaluation (as may be indicated by an error ~ message above)"))) (t (assert$ ; If translation changes cl-proc-call, we want to know! (equal cl-proc-call t-cl-proc-call) (let* ((stobjs-in (stobjs-in cl-proc wrld)) (stobjs-out (stobjs-out cl-proc wrld))) (er-progn (cond ((if clauses-result-call-p ; expected: iff at least 2 args (equal stobjs-out '(nil)) (not (equal stobjs-out '(nil)))) (er soft ctx str name (untranslate term t wrld) (msg "~x0 returns ~#1~[only~/more than~] one value ~ and hence there should be ~#1~[no~/a~] call of ~ ~x2" cl-proc (if clauses-result-call-p 0 1) 'clauses-result))) (t (let ((msg (tilde-@-illegal-clause-processor-sig-msg cl-proc stobjs-in stobjs-out))) (cond (msg (er soft ctx str name (untranslate term t wrld) msg)) (t (value nil)))))) (let* ((user-hints-p (cdr stobjs-in)) (user-hints (cond (user-hints-p (car rest-args)) (t nil))) (stobjs-called (cond (user-hints-p (cdr rest-args)) (t rest-args))) (non-alist-vars (if user-hints (list* clause user-hints stobjs-called) (list* clause stobjs-called))) (vars (cons alist non-alist-vars)) (bad-vars (collect-non-legal-variableps vars))) (cond (bad-vars (er soft ctx str name (untranslate term t wrld) (msg "the clause-processor function must be ~ applied to a list of distinct variable and ~ stobj names, but ~&0 ~#0~[is~/are~] not" (untranslate-lst bad-vars nil wrld)))) ((not (no-duplicatesp vars)) (cond ((no-duplicatesp non-alist-vars) (er soft ctx str name (untranslate term t wrld) (msg "the proposed :clause-processor rule ~ uses ~x0 as its alist variable, but ~ this variable also occurs in the ~ argument list of the clause-processor ~ function, ~x1" alist cl-proc))) (t (er soft ctx str name (untranslate term t wrld) (msg "the clause-processor function must be ~ applied to a list of distinct ~ variable and stobj names, but the ~ list ~x0 contains duplicates" non-alist-vars))))) (t (value nil)))) (chk-evaluator-use-in-rule name cl-proc nil (and meta-extract-flg '(meta-extract-global-fact+)) :clause-processor ev ctx wrld state) (chk-rule-fn-guard "clause-processor" :clause-processor cl-proc ctx wrld state) (chk-evaluator ev wrld ctx state)))))))))))) (defun add-clause-processor-rule (name term wrld) ; Warning: Keep this in sync with chk-acceptable-clause-processor-rule. ; This function is non-standard, as the other add-x-rule functions traffic in ; runes and numes. If we ever decide to support automatic application of ; clause-processor rules, along with enabling and disabling, then we should ; modify this to fit into that common mold. For now, it seems misleading to ; deal with runes, since these rules cannot be enabled or disabled or applied ; automatically. (mv-let (clauses-result-call-p cl-proc clause alist rest-args ev cl-proc-call meta-extract-flg) (destructure-clause-processor-rule term) (declare (ignore clause alist rest-args cl-proc-call meta-extract-flg)) (assert$ (and (not (eq clauses-result-call-p :error)) (symbolp cl-proc) (function-symbolp cl-proc wrld)) (putprop cl-proc 'clause-processor t ; We keep a global list of clause-processor-rules, simply in order to be ; able to print them. But someone may find other uses for this list, in ; particular in order to code computed hints that look for applicable ; clause-processor rules. (global-set 'clause-processor-rules (acons name term (global-val 'clause-processor-rules wrld)) (mark-attachment-disallowed (list cl-proc) ev (msg "it supports both the evaluator and clause-processor ~ function used in :CLAUSE-PROCESSOR rule ~x0" name) wrld)))))) ; Finally, we develop code for trusted clause-processors. This has nothing to ; do with defthm, but it seems reasonable to place it immediately below code ; for verified clause-processors. (defun trusted-clause-processor-table-guard (key val wrld) ; There is not much point in checking whether the key is already designated as ; a clause-processor, because a redundant table event won't even result in such ; a check. We could at least do this check for the non-redundant case, but ; there isn't really any need: It's perfectly OK to redefine the supporters and ; property of being a dependent clause-processor, provided the rest of the ; checks pass. The user might be surprised in such cases, so the macro ; define-trusted-clause-processor causes an error if the proposed trusted ; clause-processor is already designated as such. ; At one time we insisted that key not have a non-nil value for its ; 'constrained or 'non-executablep property. With the advent of defattach, a ; constrained function may however be a reasonable choice. Rather than do an ; elaborate check here on exactly what sort of constrained function might be ; attachable (none, if it is a dependent clause-processor), we trust that the ; writer of :meta and :clause-processor rules knows better than to attach to ; functions that cannot be executed. (let ((er-msg "The proposed designation of a trusted clause-processor is ~ illegal because ~@0. See :DOC ~ define-trusted-clause-processor.") (ctx 'trusted-clause-processor-table-guard)) (cond ((not (or (ttag wrld) (global-val 'boot-strap-flg wrld))) (er hard ctx er-msg "there is not an active ttag (also see :DOC ttag)")) ((not (symbolp key)) (er hard ctx er-msg (msg "the clause-processor must be a symbol, unlike ~x0" key))) ((not (function-symbolp key wrld)) (er hard ctx er-msg (msg "the clause-processor must be a function symbol, unlike ~x0" key))) ((not (and (consp val) (all-function-symbolps (car val) wrld))) (cond ((not (symbol-listp (car val))) (er hard ctx er-msg "the indicated supporters list is not a true list of symbols")) (t (er hard ctx er-msg (msg "the indicated supporter~#0~[ ~&0 is not a function ~ symbol~/s ~&0 are not function symbols~] in the ~ current ACL2 world" (non-function-symbols (car val) wrld)))))) ((and (cdr val) (not (eql (length (non-trivial-encapsulate-ee-entries (global-val 'embedded-event-lst wrld))) 1))) (let ((ee-entries (non-trivial-encapsulate-ee-entries (global-val 'embedded-event-lst wrld)))) (cond ((null ee-entries) (er hard ctx er-msg "there is no promised encapsulate to associate with this ~ dependent clause-processor")) (t (er hard ctx er-msg (msg "there is not a unique encapsulate for the promised ~ encapsulate to associate with this dependent ~ clause-processor. In particular, an enclosing ~ encapsulate introduces function ~x0, while an encapsulate ~ superior to that introduces function ~x1" (caar (cadr (car ee-entries))) (caar (cadr (cadr ee-entries))))))))) (t (let ((failure-msg (tilde-@-illegal-clause-processor-sig-msg key (stobjs-in key wrld) (stobjs-out key wrld)))) (cond (failure-msg (er hard ctx er-msg (msg failure-msg key))) (t t))))))) (table trusted-clause-processor-table nil nil :guard (trusted-clause-processor-table-guard key val world)) (defmacro define-trusted-clause-processor (clause-processor supporters &key label ;;; optional, but required if doc is non-nil doc ;;; optional partial-theory ;;; optional ttag ;;; optional; nil is same as missing ) ; We could mention that unlike trusted clause-processors, no supporters need to ; be specified for a verified clause-processor, as such a rule is guaranteed to ; be a theorem even in if local events have been removed. But that probably ; would distract more than it would enlighten. ":Doc-Section Events define a trusted (unverified) goal-level simplifier~/ This ~il[documentation] assumes familiarity with ~c[:clause-processor] rules; ~pl[clause-processor]. Briefly put, a ~em[clause-processor] is a user-defined function that takes as input the ACL2 representation of a goal ~-[] a ~em[clause] ~-[] and returns a list of goals (i.e., a list of clauses). A ~c[:clause-processor] rule is a way to inform ACL2 that a clause-processor has been proved correct and now may be specified in ~c[:clause-processor] ~il[hints]. Here we describe a utility, ~c[define-trusted-clause-processor], that provides another way to inform ACL2 that a function is to be considered a clause-processor that can be specified in a ~c[:clause-processor] hint. You can find examples of correct and incorrect use of this utility in community book ~c[books/clause-processors/basic-examples]. Consider the simple example already presented for ~c[:clause-processor] rules (again, ~pl[clause-processor]), for a simple clause-processor named ~c[note-fact-clause-processor]. Instead of introducing an evaluator and proving a correctness theorem with ~c[:rule-classes :clause-processor], we can simply inform ACL2 that we trust the function ~c[note-fact-clause-processor] to serve as a clause-processor. ~bv[] (define-trusted-clause-processor note-fact-clause-processor nil :ttag my-ttag) ~ev[] A non-nil ~c[:ttag] argument generates a ~ilc[defttag] event in order to acknowledge the dependence of the ACL2 session on the (unproved) correctness of this clause-processor. That argument can be omitted if there is currently an active trust tag. ~l[defttag]. Because we are trusting this clause-processor, rather than having proved it correct, we refer to it as a ``trusted'' clause-processor to contrast with a proved-correct, or ``verified'', clause-processor. Now that the event displayed above has established ~c[note-fact-clause-processor] as a (trusted) clause-processor, we can use it in a ~c[:clause-processor] hint, for example as follows. Notice that the output is identical to that for the corresponding example presented for the verified case (~pl[clause-processor]), except that the word ``verified'' has been replaced by the word ``trusted''. ~bv[] ACL2 !>(thm (equal (car (cons x y)) x) :hints ((\"Goal\" :clause-processor (note-fact-clause-processor clause '(equal a a))))) [Note: A hint was supplied for our processing of the goal above. Thanks!] We now apply the trusted :CLAUSE-PROCESSOR function NOTE-FACT-CLAUSE- PROCESSOR to produce two new subgoals. Subgoal 2 (IMPLIES (EQUAL A A) (EQUAL (CAR (CONS X Y)) X)). But we reduce the conjecture to T, by the :executable-counterpart of IF and the simple :rewrite rule CAR-CONS. Subgoal 1 (EQUAL A A). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. Summary Form: ( THM ...) Rules: ((:EXECUTABLE-COUNTERPART IF) (:EXECUTABLE-COUNTERPART NOT) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:REWRITE CAR-CONS)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !> ~ev[] Indeed, if one runs this example first and subsequently verifies the clause-processor, one will see the word ``trusted'' change to ``verified''.~/ The general form is as follows. ~bv[] (define-trusted-clause-processor cl-proc ;;; clause-processor function supporters ;;; see below &key label ;;; optional, but required if doc is non-nil doc ;;; optional ttag ;;; discussed above partial-theory ;;; optional encapsulate event ) ~ev[] If a ~c[:label] ~c[LAB] is supplied, then a subsidiary ~ilc[deflabel] event will be generated with name ~c[LAB], which will enable you to to undo this ~c[define-trusted-clause-processor] event using: ~c[:]~ilc[ubt]~c[ LAB]. If you supply a ~c[:label] then you may supply a ~c[:doc] argument to use with that generated ~ilc[deflabel] event. We discussed the ~c[:ttag] argument above. The entire form is considered redundant (skipped) if it is identical to one already executed in the current ACL2 world; but if it is not redundant, then ~c[cl-proc] must not already have been similarly designated as a trusted clause-processor. Note that ~c[cl-proc] may be defined either in ~c[:program]-mode or ~c[:logic]-mode. The ~c[supporters] argument should be a true list of function symbols in the current ACL2 world. It is important that this list include user-defined functions whose definitions support the correctness of the clause-processor function. Otherwise, ~ilc[local] definitions of those missing supporters can render the use of this clause-processor unsound, as discussed in the paper referenced at the end of the ~il[clause-processor] documentation topic. Moreover, ACL2 assumes for dependent clause-processors (discussed below) that every function symbol constrained by the ``promised encapsulate'' of that event is either among those ~c[supporters] or ancestral in one of them (i.e. a supporter of a supporter, a supporter of one of those, etc.). ~st[Dependent clause-processors and promised encapsulates]: The ~c[:partial-theory] argument Suppose you want to introduce a clause-processor to reason about a complex hardware simulator that is implemented outside ACL2. Sawada and Reeber had just such a problem, as reported in their FMCAD 2006 paper. Indeed, they used ~ilc[sys-call] to implement a ~c[:]~ilc[program]-mode function in ACL2 that can invoke that simulator. In principle one could code the simulator directly in ACL2; but it would be a tremendous amount of work that has no practical purpose, given the interface to the external simulator. So: In what sense can we have a clause-processor that proves properties about a simulator when that simulator is not fully axiomatized in ACL2? Our answer, in a nutshell, is this: The above ~c[:partial-theory] argument provides a way to write merely some of the ~il[constraint]s on the external tool (or even no constraints at all), with the understanding that such constraints are present implicitly in a stronger ``promised'' ~c[encapsulate], for example by exporting the full definition. If a trusted clause-processor is introduced with a ~c[:partial-theory] argument, we call it a ``dependent'' clause-processor, because its correctness is dependent on the constraints implicitly introduced by the ~c[:partial-theory] ~c[encapsulate] form. The implicit constraints should logically imply the constraints actually introduced by the explicit ~c[encapsulate], but they should also be sufficient to justify every possible invocation of the clause-processor in a ~c[:clause-processor] hint. The user of a ~c[define-trusted-clause-processor] form is making a guarantee ~-[] or, is relying on a guarantee provided by the writer of that form ~-[] that in principle, there exists a so-called ``promised encapsulate'': an ~c[encapsulate] form with the same ~il[signature] as the ~c[:partial-theory] ~c[encapsulate] form associated with the trusted clause-processor, but whose constraints introduced are the aforementioned implicit constraints. There are several additional requirements on a ~c[:partial-theory] argument. First, it must be an ~ilc[encapsulate] event with non-empty ~il[signature]. Moreover, the functions introduced by that event must be exactly those specified in the signature, and no more. And further still, the ~c[define-trusted-clause-processor] form cannot be executed inside any ~ilc[encapsulate] form with non-empty ~il[signature]; we can think of this situation as attempting to associate more than one ~c[encapsulate] with the functions introduced in the inner ~c[encapsulate]. The ~c[:partial-theory] event will (in essence) be executed as part of the evaluation of the ~c[define-trusted-clause-processor] form. Again, a critical obligation rests on the user who provides a ~c[:partial-theory]: there must exist (in principle at least) a corresponding promised encapsulate form with the same ~il[signature] that could logically be admitted, whenever the above ~c[define-trusted-clause-processor] form is evaluated successfully, that justifies the designation of ~c[cl-proc] as a clause-processor. See also the paper mentioned above for more about promised encapsulates. A key consequence is that the ~il[constraint]s are unknown for the functions introduced in (the signature of) a ~c[:partial-theory] ~ilc[encapsulate] form. Thus, functional instantiation (~pl[functional-instantiation-example]) is disabled for function in the signature of a ~c[:partial-theory] form. ~st[A remark on the underlying implementation] You can see all of the current trusted clause-processors by issuing the command ~c[(table trusted-clause-processor-table)]. Those that are dependent clause-processors will be associated in the resulting association list with a pair whose ~c[car] is the list of supporters and whose ~c[cdr] is ~c[t], i.e., with ~c[(supporters . t)]; the others will be associated just with ~c[(supporters)]. Thus, ~c[define-trusted-clause-processor] is actually a macro that generates (among other things) a ~c[table] event for a table named ~c[trusted-clause-processor-table]; ~pl[table]. You are invited to use ~c[:]~ilc[trans1] to see expansions of calls of this macro. ~st[A technique for using raw Lisp to define a trusted clause-processor] The following code is intended to give an idea for how one might define the ``guts'' of a trusted clause-processor in raw Lisp. The idea is to stub out functions, such as ~c[acl2-my-prove below], that you want to define in raw Lisp; and then, load a raw Lisp file to overwrite any such function with the real code. But then we make any such overwritten function untouchable. (This last step is important because otherwise, one can prove ~c[nil] using a ~c[:functional-instance] ~c[:use] hint, by exploiting the fact that this function has executable code for which there is no corresponding definitional axiom.) ~bv[] (defstub acl2-my-prove (term hint) t) (program) (defttag :my-cl-proc) (progn ; We wrap everything here in a single progn, so that the entire form is ; atomic. That's important because we want the use of push-untouchable to ; prevent anything besides my-clause-processor from calling acl2-my-prove. (progn! (set-raw-mode-on state) (load \"my-hint-raw.lsp\") ; defines my-prove in raw Lisp (defun acl2-my-prove (term hint) (my-prove term hint))) (defun my-clause-processor (cl hint) (declare (xargs :guard (pseudo-term-listp cl) :mode :program)) (if (acl2-my-prove (disjoin cl) hint) (disjoin-clause-segments-to-clause (pairlis$ (hint-to-termlist hint) nil) cl) (prog2$ (cw \"~~|~~%NOTE: Unable to prove goal with ~~ my-clause-processor and indicated hint.~~|\") (list cl)))) (push-untouchable acl2-my-prove t) ) ~ev[]" (let* ((ctx 'define-trusted-clause-processor) (er-msg "The proposed use of define-trusted-clause-processor is ~ illegal because ~@0. See :DOC ~ define-trusted-clause-processor.") (assert-check `(assert-event (not (assoc-eq ',clause-processor (table-alist 'trusted-clause-processor-table (w state)))) :msg (msg "The function ~x0 is already indicated as a trusted ~ clause-processor." ',clause-processor) :on-skip-proofs t)) (ttag-extra (and ttag `((defttag ,ttag)))) (label-extra (and label (cond (doc `((deflabel ,label :doc ,doc))) (t `((deflabel ,label)))))) (extra (append ttag-extra label-extra))) (cond ((not (symbol-listp supporters)) (er hard ctx er-msg "the second (supporters) argument must be a true list of symbols")) ((not (symbolp clause-processor)) ; expansion will do stronger check (er hard ctx er-msg "the first argument must be a symbol (in fact, must be a defined ~ function symbol in the current ACL2 world)")) ((and doc (not label)) (er hard ctx er-msg "a non-nil :label argument is required when a non-nil :doc argument ~ is supplied")) (t (case-match partial-theory (nil `(encapsulate () ,assert-check ,@extra (table trusted-clause-processor-table ',clause-processor '(,supporters)))) (('encapsulate sigs . events) (cond ((atom sigs) (er hard ctx er-msg "the encapsulate event associated with :partial-theory has an ~ empty signature list")) ((atom events) (er hard ctx er-msg "the encapsulate event associated with :partial-theory has an ~ empty list of sub-events")) ((not (true-listp events)) (er hard ctx er-msg "the encapsulate event associated with :partial-theory has a ~ list of sub-events that is not a true-listp")) (t `(encapsulate ,sigs ,assert-check (logic) ; to avoid skipping local events ,@events ,@extra (table trusted-clause-processor-table ',clause-processor '(,supporters . t)))))) (& (er hard ctx er-msg "a supplied :partial-theory argument must be a call of ~ encapsulate"))))))) ;--------------------------------------------------------------------------- ; Section: Handling a List of Classes ; We start by translating the user-supplied list of rule-class tokens. ; Once upon a time we considered the idea of permitting rule classes, e.g., ; :FORWARD-CHAINING, to be abbreviated by arbitrary subsequences of their ; characters. We implemented the idea via "disambiguation alists." We have ; since scrapped the idea for user-level consistency: rule classes are only one ; source of long keywords. Do we permit the abbreviation of, say, :HINTS by ; :H? Do we permit the abbreviation of :RULE-CLASSES to :RC? Do we permit the ; abbreviation of the :PROPS keyword command of LP? There is a good argument ; that we ought to permit a powerful symbol-level abbreviation convention. ; Macros suffer by requiring parentheses. But since we don't have the time, ; now, to carry out the root-and-branch implementation of keyword ; disambiguation, we have scrapped the idea for now. We leave the following ; dead code in place. ; (defun char-subsequencep (x y) ; ; ; Determine whether x is a subsequence of y, e.g., '(#\B #\D) is a ; ; char-subsequencep of '(#\A #\B #\C #\D) but not of '(#\A #\D #\B). ; ; X and y must be true lists of characters. ; ; (cond ((null x) t) ; ((null y) nil) ; ((eql (car x) (car y)) ; (char-subsequencep (cdr x) (cdr y))) ; (t (char-subsequencep x (cdr y))))) ; ; (defun disambiguate1 (x alist) ; ; ; Alist should pair character lists with arbitrary values. We select those ; ; pairs whose key have x as a subsequence. ; ; (cond ((null alist) nil) ; ((char-subsequencep x (caar alist)) ; (cons (car alist) (disambiguate1 x (cdr alist)))) ; (t (disambiguate1 x (cdr alist))))) ; ; (defun make-disambiguation-alist (lst) ; ; ; This function is used to preprocess a true list of symbols into an ; ; alist suitable for disambiguate. For example, '(FOO BAR) is ; ; transformed into '(((#\F #\O #\O) . FOO) ((#\B #\A #\R) . BAR)). ; ; (cond ((null lst) nil) ; (t (cons (cons (coerce (symbol-name (car lst)) 'list) (car lst)) ; (make-disambiguation-alist (cdr lst)))))) ; ; (defun assoc-cdr (x alist) ; ; ; Like assoc-equal but uses the cdr of each pair in alist as the key. ; ; (cond ((null alist) nil) ; ((equal x (cdar alist)) (car alist)) ; (t (assoc-cdr x (cdr alist))))) ; ; (defun disambiguate (token alist ctx phrase state) ; ; ; This function disambiguates token wrt alist or else causes an error. ; ; Token must be a symbol and alist must be a ``disambiguation alist,'' ; ; an alist pairing lists of characters to symbols. For example, if ; ; token is :EM and alist includes the pair ((#\E #\L #\I #\M) . :ELIM) ; ; and no other pair whose key has EM as a subsequence, then no error ; ; is caused and :ELIM is returned as the value. If the token is a ; ; subsequence of no key or of more than one key, an error is caused. ; ; Phrase is a tilde-@ phrase that fills in the sentence: "The ; ; acceptable ~@1 are ..." so, for example, phrase might be "rule ; ; classes". ; ; ; We adopt the convention, for sanity, that if token is eq to the ; ; value component of some pair in alist, then its meaning is itself. ; ; This guarantees that if you spell a token out completely you get that ; ; token and no other; in particular, you don't get an ambiguity error ; ; just one key in the alist is a subsequence of another. ; ; (cond ; ((assoc-cdr token alist) (value token)) ; (t ; (let ((winners (disambiguate1 (coerce (symbol-name token) 'list) alist))) ; (cond ((null winners) ; (er soft ctx "The token ~x0 denotes none of the acceptable ~@1: ~&2." ; token ; phrase ; (strip-cdrs alist))) ; ((null (cdr winners)) ; (value (cdar winners))) ; (t (er soft ctx "The token ~x0 is ambiguously denotes the ~@1: ~&2." ; token ; phrase ; (strip-cdrs winners)))))))) ; ; (defun tilde-@-abbreviates-but-phrase (x y) ; ; ; We produce a tilde-@ phrase that prints as "x abbreviates y, but y" ; ; if x is different from y and that is just "y" otherwise. Both x and ; ; y are symbols. This is used to print such messages as ":RWT ; ; abbreviates :REWRITE, but :REWRITE cannot be used as a structured ; ; rule class." ; ; (cond ((eq x y) (msg "~x0" y)) ; (t (msg "~x0 abbreviates ~x1, but ~x1" x y)))) ; ; ; Thus ends the dead code devoted to disambiguation. ; ; Now we stub out the proof checker's sense of "instructions." (defun primitive-instructionp (instr state) (let* ((cmd (car (make-official-pc-instr instr))) (typ (pc-command-type cmd))) (and (member-eq typ '(primitive atomic-macro)) (acl2-system-namep (intern-in-package-of-symbol (symbol-name cmd) 'acl2-pc::induct) (w state))))) (defun non-primitive-instructions (instructions state) (cond ((endp instructions) nil) ((primitive-instructionp (car instructions) state) (non-primitive-instructions (cdr instructions) state)) (t (cons (car instructions) (non-primitive-instructions (cdr instructions) state))))) (defun chk-primitive-instruction-listp (instructions ctx state) (if (true-listp instructions) (value nil) (er soft ctx "An :instructions argument must be a ~ true-list and ~x0 is not." instructions))) (defun translate-instructions (name instructions ctx wrld state) (declare (ignore name wrld)) (if (eq instructions t) (value t) (er-progn (chk-primitive-instruction-listp instructions ctx state) (value instructions)))) (defun controller-alistp (clique alist wrld) ; Clique is a list of function symbols. Alist is an arbitrary object. ; We confirm that alist is an alist that maps each fn in clique to a ; mask of t's and nil's whose length is the arity of the corresponding ; fn. (cond ((atom alist) (cond ((null alist) (null clique)) (t nil))) ((and (consp (car alist)) (symbolp (caar alist)) (member-eq (caar alist) clique) (boolean-listp (cdar alist)) (= (length (cdar alist)) (arity (caar alist) wrld))) (controller-alistp (remove1-eq (caar alist) clique) (cdr alist) wrld)) (t nil))) (defun alist-to-keyword-alist (alist ans) ; Convert ((key1 . val1) ... (keyn . valn)) to a keyword alist, i.e., ; (keyn valn ... key1 val1). Note that we reverse the order of the ; "key pairs." (declare (xargs :guard (alistp alist))) (cond ((endp alist) ans) (t (alist-to-keyword-alist (cdr alist) (cons (caar alist) (cons (cdar alist) ans)))))) (defun loop-stopper-alist-p (x wrld) (cond ((consp x) (and (true-listp (car x)) (<= 2 (length (car x))) (legal-variablep (caar x)) (legal-variablep (cadar x)) (not (eq (caar x) (cadar x))) (all-function-symbolps (cddar x) wrld) (loop-stopper-alist-p (cdr x) wrld))) (t (eq x nil)))) (defun guess-controller-alist-for-definition-rule (names formals body ctx wrld state) ; Names is a singleton list containing a function name to be used as the clique ; for a :definition rule with the indicated formals and body. We guess a ; :controller-alist or cause an error. (let ((t-machine (termination-machine names body nil nil (default-ruler-extenders wrld)))) (er-let* ((m (guess-measure (car names) nil formals 0 t-machine ctx wrld state))) (value (list (cons (car names) (make-controller-pocket formals (all-vars m)))))))) (defun chk-legal-linear-trigger-terms1 (term lst name ctx state) (cond ((null lst) (value nil)) ((subsetp-eq (set-difference-eq (all-vars (cdar lst)) (all-vars1-lst (caar lst) nil)) (all-vars term)) (chk-legal-linear-trigger-terms1 term (cdr lst) name ctx state)) (t (er soft ctx "Each term in the :TRIGGER-TERMS of a :LINEAR rule should be a ~ legal trigger for the rule generated for each branch through ~ the corollary. But the the proposed trigger ~p0 for the ~ :LINEAR rule ~x1 is illegal for the branch ~p2 because it ~ contains insufficient variables. See :DOC linear." (untranslate term nil (w state)) name (untranslate (if (caar lst) (fcons-term* 'implies (conjoin (caar lst)) (cdar lst)) (cdar lst)) t (w state)))))) (defun chk-legal-linear-trigger-terms (terms lst name ctx state) ; When the user supplies some :TRIGGER-TERMS for a :LINEAR rule, we must check ; that each trigger is legal for every rule generated from the unprettified ; corollary. Here, terms is a true-list of terms proposed as triggers and lst ; is the unprettification of the corollary, i.e., a list of pairs of the form ; ((hyps1 . concl1) ... (hypsk . conclk)). To be legal, each term must be a ; non-variable, non-quote, non-lambda application, non-IF and must, for each ; (hypsi . concli) pair, contain sufficient variables so that the vars in hypsi ; plus those in the term include all the vars in concli. We check these ; conditions and return nil or cause an error. (cond ((null terms) (value nil)) ((and (nvariablep (car terms)) (not (fquotep (car terms))) (not (flambda-applicationp (car terms))) (not (eq (ffn-symb (car terms)) 'if))) (er-progn (chk-legal-linear-trigger-terms1 (car terms) lst name ctx state) (chk-legal-linear-trigger-terms (cdr terms) lst name ctx state))) (t (er soft ctx "The term ~p0 supplied as a :TRIGGER-TERM for the :LINEAR rule ~x1 ~ is illegal because it is either a variable, a quoted constant, a ~ lambda application (or LET-expression), or an IF-expression." (untranslate (car terms) nil (w state)) name)))) (defun backchain-limit-listp (lst) ; Recognizer for true-lists each of whose elements is either NIL or a ; non-negative integer. (cond ((atom lst) (equal lst nil)) ((or (null (car lst)) (natp (car lst))) (backchain-limit-listp (cdr lst))) (t nil))) (defun eliminate-macro-aliases (lst macro-aliases wrld) ; Returns (mv flg lst), where flg is nil if lst is unchanged, :error if there ; is an error (some element is neither a function symbol nor a macro aliases) ; -- in which case lst is a string giving a reason for the error after "but ; " -- else :changed if there is no error but at least one ; macro alias was found. (cond ((atom lst) (cond ((null lst) (mv nil nil)) (t (mv :error "does not end in nil")))) (t (mv-let (flg rst) (eliminate-macro-aliases (cdr lst) macro-aliases wrld) (cond ((eq flg :error) (mv :error rst)) (t (let* ((next (car lst)) (fn (deref-macro-name next macro-aliases))) (cond ((not (and (symbolp fn) (function-symbolp fn wrld))) (mv :error (msg "contains ~x0" next))) ((or (eq flg :changed) (not (eq next fn))) (mv :changed (cons fn rst))) (t (mv nil lst)))))))))) (defun translate-rule-class-alist (token alist seen corollary name x ctx ens wrld state) ; Alist is the untranslated alist of a rule-class with car token. ; Corollary is the translated value of the :COROLLARY entry in alist ; (which is guaranteed to be present). Seen is an alist of the keys ; seen so far and their translated values. It is in fact the reverse ; of the final answer. We translate the values in alist, making sure ; that no key is seen twice, that the keys seen are appropriate for ; the class named by token, and that all required keys (other than ; :COROLLARY) are present. The variable x is the object the user ; supplied to specify this class and is used only in error messages. ; Name is the name of the event for which this rule class is being ; translated and is used in the translation of :BY hints. ; WARNING: If you add new keywords, be sure to change the ; documentation under deflabel rule-classes. (cond ((null alist) (cond ((eq token :META) (cond ((not (assoc-eq :TRIGGER-FNS seen)) (er soft ctx "The :META rule class must specify :TRIGGER-FNS. ~ The rule class ~x0 is thus illegal. See :DOC meta." x)) (t (value (alist-to-keyword-alist seen nil))))) ((eq token :FORWARD-CHAINING) (cond ((not (assoc-eq :TRIGGER-TERMS seen)) (mv-let (hyps concls) (destructure-forward-chaining-term corollary) (declare (ignore concls)) (cond ((null hyps) (er soft ctx "When no :TRIGGER-TERMS component is ~ specified for a :FORWARD-CHAINING ~ rule class, the first hypothesis of ~ the conjecture is used as the only ~ trigger. But ~p0 has no hypotheses ~ and thus ~x1 is an illegal rule ~ class. See :DOC forward-chaining." (untranslate corollary t wrld) x)) (t (let* ((first-hyp (if (and (nvariablep (car hyps)) (not (fquotep (car hyps))) (or (eq (ffn-symb (car hyps)) 'force) (eq (ffn-symb (car hyps)) 'case-split))) (fargn (car hyps) 1) (car hyps))) (trigger-term (if (and (nvariablep first-hyp) (not (fquotep first-hyp)) (eq (ffn-symb first-hyp) 'not)) (fargn first-hyp 1) first-hyp))) (pprogn (observation ctx "The :TRIGGER-TERMS for the ~ :FORWARD-CHAINING rule ~x0 will ~ consist of the list containing ~p1." name (untranslate trigger-term nil wrld)) (value (alist-to-keyword-alist seen (list :TRIGGER-TERMS (list trigger-term)))))))))) (t (value (alist-to-keyword-alist seen nil))))) ((eq token :TYPE-PRESCRIPTION) (cond ((not (assoc-eq :TYPED-TERM seen)) (mv-let (hyps concl) (unprettyify-tp (remove-guard-holders corollary)) (declare (ignore hyps)) (let ((pat (cond ((and (not (variablep concl)) (not (fquotep concl)) (eq (ffn-symb concl) 'implies)) (find-type-prescription-pat (fargn concl 2) ens wrld)) (t (find-type-prescription-pat concl ens wrld))))) (cond ((null pat) (er soft ctx "When no :TYPED-TERM component is specified for a ~ :TYPE-PRESCRIPTION rule class, a suitable term is ~ selected heuristically from the conjecture. But ~ our heuristics identify no candidate term in ~p0. ~ Thus, ~x1 is an illegal rule class. See :DOC ~ type-prescription." (untranslate corollary t wrld) x)) (t (pprogn (if (ld-skip-proofsp state) state (observation ctx "Our heuristics choose ~p0 as the ~ :TYPED-TERM." (untranslate pat nil wrld))) (value (alist-to-keyword-alist seen (list :TYPED-TERM pat))))))))) (t (value (alist-to-keyword-alist seen nil))))) ((eq token :DEFINITION) (er-progn (chk-destructure-definition name corollary ctx wrld state) (mv-let (hyps equiv fn args body ttree) (destructure-definition corollary nil nil wrld nil) (declare (ignore hyps equiv ttree)) ; Rockwell Addition: In the old code, the recursivep property of a ; singly recursive function was the function name itself; the ; recursivep property of a function in a mutually-recursive clique was ; the list of all the fns in the clique. In order to speed up the ; check to determine if there is a recursive function on the fnstack, ; I decided to make the recursivep property of a recursive fn be ; a list of all the fns in its "clique" -- possibly the singleton ; list containing just the singly recursive function name. That way, ; if the fnstack contains a function name, I know it is non-recursive. ; In support of this change, I changed the processing of :definition ; rules. In the old code, the translated clique of a :definition was ; made atomic (i.e., the fn name itself) if the clique was a singleton. ; For sanity, I don't do that now: the translated clique is what ; you wrote. This change shows up several times in the window-compare ; because in the old code we had to change back and forth between ; (fn) and fn. (er-let* ((clique (value (cond ((assoc-eq :clique seen) (cdr (assoc-eq :clique seen))) ((ffnnamep fn body) (list fn)) (t nil)))) (controller-alist (cond ((assoc-eq :CONTROLLER-ALIST seen) (value (cdr (assoc-eq :CONTROLLER-ALIST seen)))) ((null clique) (value nil)) ((null (cdr clique)) (guess-controller-alist-for-definition-rule clique args body ctx wrld state)) (t (er soft ctx "We are unable to guess a :CONTROLLER-ALIST for a ~ :DEFINITION rule if the :CLIQUE contains more ~ than one function symbol. Therefore, you must ~ supply a :CONTROLLER-ALIST entry for ~x0." name))))) (cond ((controller-alistp clique controller-alist wrld) (value (alist-to-keyword-alist seen (append (if (assoc-eq :CLIQUE seen) nil (list :CLIQUE clique)) (if (assoc-eq :CONTROLLER-ALIST seen) nil (list :CONTROLLER-ALIST controller-alist)))))) (t (er soft ctx "The :CONTROLLER-ALIST of a :DEFINITION must be an alist ~ that maps each function symbol in the :CLIQUE to a list of ~ t's and nil's whose length is equal to the arity of the ~ function symbol. ~x0 is an inappropriate controller alist ~ for the :CLIQUE consisting of ~&1. See :DOC definition." controller-alist clique))))))) ((eq token :INDUCTION) (cond ((not (assoc-eq :PATTERN seen)) (er soft ctx "The :INDUCTION rule class requires the specification of a ~ :PATTERN term and ~x0 contains no such entry." x)) ((not (assoc-eq :SCHEME seen)) (er soft ctx "The :INDUCTION rule class requires the specification of a ~ :SCHEME term and ~x0 contains no such entry." x)) (t (let* ((pat-term (cdr (assoc-eq :pattern seen))) (cond-term (or (cdr (assoc-eq :condition seen)) *t*)) (scheme-term (cdr (assoc-eq :scheme seen))) (pat-vars (all-vars pat-term)) (cond-vars (all-vars cond-term)) (scheme-vars (all-vars scheme-term))) (cond ((not (subsetp-eq cond-vars pat-vars)) (er soft ctx "The variables occuring freely in the :CONDITION term ~ of an :INDUCTION rule class must be a subset of those ~ occuring freely in the :PATTERN term. But the ~ condition term ~x0 mentions ~&1, which ~#1~[does~/do~] ~ not occur in the pattern term ~x2. Thus the ~ :INDUCTION rule class specified for ~x3 is illegal." cond-term (set-difference-eq cond-vars pat-vars) pat-term name)) ((not (subsetp-eq scheme-vars pat-vars)) (er soft ctx "The variables occuring freely in the :SCHEME term ~ of an :INDUCTION rule class must be a subset of those ~ occuring freely in the :PATTERN term. But the ~ scheme term ~x0 mentions ~&1, which ~#1~[does~/do~] ~ not occur in the pattern term ~x2. Thus the ~ :INDUCTION rule class specified for ~x3 is illegal." scheme-term (set-difference-eq scheme-vars pat-vars) pat-term name)) ((assoc-eq :condition seen) (value (alist-to-keyword-alist seen nil))) (t (value (alist-to-keyword-alist seen (list :CONDITION *t*))))))))) (t (value (alist-to-keyword-alist seen nil))))) ((assoc-eq (car alist) seen) (er soft ctx "Rule classes may not contain duplicate keys, but ~x0 occurs ~ twice in ~x1. See :DOC rule-classes." (car alist) x)) (t (let ((assumep (or (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals) (eq (ld-skip-proofsp state) 'initialize-acl2)))) (er-let* ((val (case (car alist) (:COROLLARY (value corollary)) (:HINTS (cond ((assoc-eq :INSTRUCTIONS seen) (er soft ctx "It is illegal to supply both :INSTRUCTIONS ~ and :HINTS in a rule class. Hence, ~x0 is ~ illegal. See :DOC rule-classes." x)) (t (er-let* ((hints (if assumep (value nil) (translate-hints+ (cons "Corollary of " name) (cadr alist) (default-hints wrld) ctx wrld state)))) (value hints))))) (:INSTRUCTIONS (cond ((assoc-eq :HINTS seen) (er soft ctx "It is illegal to supply both :HINTS and :INSTRUCTIONS ~ in a rule class. Hence, ~x0 is illegal. See :DOC ~ rule-classes." x)) (t (er-let* ((instrs (if assumep (value nil) (translate-instructions (cons "Corollary of " name) (cadr alist) ctx wrld state)))) (value instrs))))) (:OTF-FLG (value (cadr alist))) (:TRIGGER-FNS (cond ((eq token :FORWARD-CHAINING) (er soft ctx "The :FORWARD-CHAINING rule class may specify ~ :TRIGGER-TERMS but may not specify :TRIGGER-FNS. ~ Thus, ~x0 is illegal. See :DOC forward-chaining and ~ :DOC meta." x)) ((not (eq token :META)) (er soft ctx ":TRIGGER-FNS can only be specified for :META rules. ~ Thus, ~x0 is illegal. See :DOC ~@1." x (symbol-name token))) ((atom (cadr alist)) (er soft ctx "The :TRIGGER-FNS component of a :META rule class must ~ be a non-empty true-list of function symbols. But ~x0 ~ is empty. See :DOC meta." (cadr alist))) ((eq (car (cadr alist)) 'quote) (er soft ctx "The :TRIGGER-FNS component of a :META rule class must ~ be a non-empty true-list of function symbols. You ~ specified ~x0 for this component, but the list is not ~ to be quoted.~@1 See :DOC meta." (cadr alist) (cond ((and (consp (cdr (cadr alist))) (symbol-listp (cadr (cadr alist))) (null (cddr (cadr alist)))) (msg " Perhaps you intended ~x0 instead." (cadr (cadr alist)))) (t "")))) (t (mv-let (flg lst) (eliminate-macro-aliases (cadr alist) (macro-aliases wrld) wrld) (cond ((eq flg :error) (er soft ctx "The :TRIGGER-FNS component of a :META ~ rule class must be a non-empty ~ true-list of function symbols, but ~ ~x0 ~@1. See :DOC meta." (cadr alist) lst)) (t (value lst))))))) (:TRIGGER-TERMS (cond ((eq token :META) (er soft ctx "The :META rule class may specify :TRIGGER-FNS but may ~ not specify :TRIGGER-TERMS. Thus, ~x0 is illegal. ~ See :DOC meta and :DOC forward-chaining." x)) ((not (true-listp (cadr alist))) (er soft ctx "The :TRIGGER-TERMS must be a list true list. Thus the ~ rule class ~x0 proposed for ~x1 is illegal." x name)) ((eq token :LINEAR) ; We allow but do not require :TRIGGER-TERMS to be provided for :LINEAR rules. ; The whole idea of :TRIGGER-TERMS specified at the rule-class level is a ; little jarring in the case of linear rules because we generate a linear rule ; for each unprettified branch through the COROLLARY of the rule class and the ; appropriate trigger terms for one branch may not be those for another. ; Nevertheless, when :TRIGGER-TERMS is provided, we store the rule for every ; branch under every given trigger. You get what you ask for. The moral is ; that if you are going to provide :TRIGGER-TERMS you would be well-advised to ; provide a corollary with only one branch. (er-let* ((terms (translate-term-lst (cadr alist) t t t ctx wrld state))) (cond ((null terms) (er soft ctx "For the :LINEAR rule ~x0 you specified an empty ~ list of :TRIGGER-TERMS. This is illegal. If you ~ wish to cause ACL2 to compute the trigger terms, ~ omit the :TRIGGER-TERMS field entirely. See :DOC ~ linear." name)) (t (let ((terms (remove-guard-holders-lst terms))) (er-progn (chk-legal-linear-trigger-terms terms (unprettyify (remove-guard-holders corollary)) name ctx state) (value terms))))))) ((eq token :FORWARD-CHAINING) (er-let* ((terms (translate-term-lst (cadr alist) t t t ctx wrld state))) (cond ((null terms) (er soft ctx ":FORWARD-CHAINING rules must have at least one ~ trigger. Your rule class, ~x0, specifies ~ none. See :DOC forward-chaining." x)) (t (value (remove-guard-holders-lst terms)))))) (t (er soft ctx ":TRIGGER-TERMS can only be specified for ~ :FORWARD-CHAINING and :LINEAR rules. Thus, ~x0 is ~ illegal. See :DOC ~@1." x (symbol-name token))))) (:TYPED-TERM (cond ((not (eq token :TYPE-PRESCRIPTION)) (er soft ctx "Only :TYPE-PRESCRIPTION rule classes are permitted to ~ have a :TYPED-TERM component. Thus, ~x0 is illegal. ~ See :DOC ~@1." x (symbol-name token))) (t (er-let* ((term (translate (cadr alist) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (value term))))) (:BACKCHAIN-LIMIT-LST (let ((hyps-concl-pairs ; We could call unprettyify in all cases below (not always with ; remove-guard-holders, though). But it seems more appropriate not to rely on ; unprettyify to handle the very specific legal forms of meta rules. ; Warning: Keep this in sync with destructure-type-prescription. (case token (:meta (case-match corollary (('implies hyp concl) (list (cons (list hyp) concl))) (& (list (cons nil corollary))))) (:type-prescription (mv-let (hyps concl) (unprettyify-tp (remove-guard-holders corollary)) (list (cons hyps concl)))) (otherwise (unprettyify (remove-guard-holders corollary)))))) (cond ((not (member-eq token '(:REWRITE :META :LINEAR :TYPE-PRESCRIPTION))) (er soft ctx "The rule-class ~@0 is not permitted to have a ~ :BACKCHAIN-LIMIT-LST component. Hence, ~x1 is ~ illegal. See :DOC ~@0." (symbol-name token) x)) ((not (equal (length (remove-duplicates-equal (strip-cars hyps-concl-pairs))) 1)) (er soft ctx "We do not allow you to specify the ~ :BACKCHAIN-LIMIT-LST when more than one rule is ~ produced from the corollary and at least two such ~ rules have different hypothesis lists. You should ~ split the corollary of ~x0 into parts and specify a ~ limit for each." x)) (t (let ((hyps (car (car hyps-concl-pairs)))) (cond ((null hyps) (er soft ctx "There are no hypotheses, so :BACKCHAIN-LIMIT-LST ~ makes no sense. See :DOC RULE-CLASSES.")) ((null (cadr alist)) (value nil)) ((and (integerp (cadr alist)) (<= 0 (cadr alist))) (cond ((eq token :META) (value (cadr alist))) (t (value (make-list (length hyps) :initial-element (cadr alist)))))) ((eq token :META) (er soft ctx "The legal values of :BACKCHAIN-LIMIT-LST for ~ rules of class :META are nil or a non-negative ~ integer. See :DOC RULE-CLASSES.")) ((and (backchain-limit-listp (cadr alist)) (eql (length (cadr alist)) (length hyps))) (value (cadr alist))) (t (er soft ctx "The legal values of :BACKCHAIN-LIMIT-LST are ~ nil, a non-negative integer, or a list of these ~ of the same length as the flattened hypotheses. ~ In this case the list of flattened hypotheses, ~ of length ~x0, is:~% ~x1.~%See :DOC ~ RULE-CLASSES." (length hyps) hyps)))))))) (:MATCH-FREE (cond ((not (member-eq token '(:REWRITE :LINEAR :FORWARD-CHAINING))) (er soft ctx "Only :REWRITE, :FORWARD-CHAINING, and :LINEAR rule ~ classes are permitted to have a :MATCH-FREE component. ~ Thus, ~x0 is illegal. See :DOC free-variables." x)) ((not (member-eq (cadr alist) '(:ALL :ONCE))) (er soft ctx "The legal values of :MATCH-FREE are :ALL and :ONCE. ~ Thus, ~x0 is illegal. See :DOC free-variables." x)) (t (value (cadr alist))))) (:CLIQUE (cond ((not (eq token :DEFINITION)) (er soft ctx "Only :DEFINITION rule classes are permitted to have a ~ :CLIQUE component. Thus, ~x0 is illegal. See :DOC ~ ~@1." x (symbol-name token))) (t (er-progn (chk-destructure-definition name corollary ctx wrld state) (mv-let (hyps equiv fn args body ttree) (destructure-definition corollary nil nil wrld nil) (declare (ignore hyps equiv args ttree)) (let ((clique (cond ((null (cadr alist)) nil) ((atom (cadr alist)) (list (cadr alist))) (t (cadr alist))))) (cond ((not (and (all-function-symbolps clique wrld) (no-duplicatesp-equal clique))) (mv-let (flg lst) (eliminate-macro-aliases (cadr alist) (macro-aliases wrld) wrld) (er soft ctx "The :CLIQUE of a :DEFINITION must be a ~ truelist of function symbols (containing ~ no duplications) or else a single ~ function symbol. ~x0 is neither.~@1 ~ See :DOC definition." (cadr alist) (cond ((eq flg :error) "") (t (msg " Note that it is illegal ~ to use ~v0 here, because ~ we require function ~ symbols, not merely macros ~ that are aliases for ~ function symbols (see :DOC ~ macro-aliases-table)." (set-difference-equal (cadr alist) lst))))))) ((and (ffnnamep fn body) (not (member-eq fn clique))) (er soft ctx "The :CLIQUE of a :DEFINITION must contain ~ the defined function, ~x0, if the body ~ calls the function. See :DOC definition." fn)) ((and clique (not (member-eq fn clique))) (er soft ctx "The :CLIQUE of a :DEFINITION, when ~ non-nil, must contain the function ~ defined. ~x0 does not contain ~x1. See ~ :DOC definition." (cadr alist) fn)) (t (value clique))))))))) (:CONTROLLER-ALIST (cond ((not (eq token :DEFINITION)) (er soft ctx "Only :DEFINITION rule classes are permitted to have a ~ :CONTROLLER-ALIST component. Thus, ~x0 is illegal. ~ See :DOC ~@1." x (symbol-name token))) (t ; Actually, the rules on a controller alist involve the clique in question. ; We don't necessarily know the clique yet. We wait until the end, when ; :CLIQUE will have been processed, to check that the following value is ok. (value (cadr alist))))) (:INSTALL-BODY (cond ((not (eq token :DEFINITION)) (er soft ctx "Only :DEFINITION rule classes are permitted to have an ~ :INSTALL-BODY component. Thus, ~x0 is illegal. ~ See :DOC ~@1." x (symbol-name token))) ((not (member-eq (cadr alist) '(t nil :NORMALIZE))) (er soft ctx "The :INSTALL-BODY component of a :DEFINITION rule ~ class must have one of the values ~v0. Thus, ~x1 is ~ illegal. See :DOC ~@2." '(t nil :NORMALIZE) (cadr alist) (symbol-name token))) (t (value (cadr alist))))) (:LOOP-STOPPER (cond ((not (eq token :REWRITE)) (er soft ctx "Only :REWRITE rule classes are permitted to have a ~ :LOOP-STOPPER component. Thus, ~x0 is illegal. See ~ :DOC rule-classes." x)) ((not (loop-stopper-alist-p (cadr alist) wrld)) (er soft ctx "The :LOOP-STOPPER for a rule class must be a list ~ whose elements have the form (variable1 variable2 . ~ fns), where variable1 and variable2 are distinct ~ variables and fns is a list of function symbols, but ~ ~x0 does not have this form. Thus, ~x1 is illegal. ~ See :DOC rule-classes." (cadr alist) x)) ((not (subsetp-eq (union-eq (strip-cars (cadr alist)) (strip-cadrs (cadr alist))) (all-vars corollary))) (let ((bad-vars (set-difference-eq (union-eq (strip-cars (cadr alist)) (strip-cadrs (cadr alist))) (all-vars corollary)))) (er soft ctx "The variables from elements (variable1 variable2 . ~ fns) of a :LOOP-STOPPER specified for a rule class ~ must all appear in the :COROLLARY theorem for that ~ rule class. However, the ~#0~[variables ~&1 ~ do~/variable ~&1 does~] not appear in ~p2. Thus, ~ ~x3 is illegal. See :DOC rule-classes." (if (cdr bad-vars) 0 1) bad-vars (untranslate corollary t wrld) x))) (t (value (cadr alist))))) (:PATTERN (cond ((not (eq token :INDUCTION)) (er soft ctx "Only :INDUCTION rule classes are permitted to have a ~ :PATTERN component. Thus, ~x0 is illegal. See :DOC ~ ~@1." x (symbol-name token))) (t (er-let* ((term (translate (cadr alist) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (cond ((or (variablep term) (fquotep term) (flambdap (ffn-symb term))) (er soft ctx "The :PATTERN term of an :INDUCTION rule class may ~ not be a variable symbol, constant, or the ~ application of a lambda expression. Thus ~x0 is ~ illegal. See :DOC induction." x)) (t (value term))))))) (:CONDITION (cond ((not (eq token :INDUCTION)) (er soft ctx "Only :INDUCTION rule classes are permitted to have a ~ :CONDITION component. Thus, ~x0 is illegal. See :DOC ~ ~@1." x (symbol-name token))) (t (er-let* ((term (translate (cadr alist) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (value term))))) (:SCHEME (cond ((not (eq token :INDUCTION)) (er soft ctx "Only :INDUCTION rule classes are permitted to have a ~ :SCHEME component. Thus, ~x0 is illegal. See :DOC ~ ~@1." x (symbol-name token))) (t (er-let* ((term (translate (cadr alist) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (cond ((or (variablep term) (fquotep term) (flambdap (ffn-symb term))) (er soft ctx "The :SCHEME term of an :INDUCTION rule class may ~ not be a variable symbol, constant, or the ~ application of a lambda expression. Thus ~x0 is ~ illegal. See :DOC induction." x)) ((not (or (getprop (ffn-symb term) 'induction-machine nil 'current-acl2-world wrld) (getprop (ffn-symb term) 'induction-rules nil 'current-acl2-world wrld))) (er soft ctx "The function symbol of the :SCHEME term of an ~ :INDUCTION rule class must, at least sometimes, ~ suggest an induction and ~x0 does not. See :DOC ~ induction." (ffn-symb term))) (t (value term))))))) (:TYPE-SET (cond ((not (eq token :TYPE-SET-INVERTER)) (er soft ctx "Only :TYPE-SET-INVERTER rule classes are permitted to ~ have a :TYPE-SET component. Thus ~x0 is illegal. See ~ :DOC ntype-set-inverter." x)) ((not (and (integerp (cadr alist)) (<= *min-type-set* (cadr alist)) (<= (cadr alist) *max-type-set*))) (er soft ctx "The :TYPE-SET of a :TYPE-SET-INVERTER rule must be a ~ type-set, i.e., an integer between ~x0 and ~x1, ~ inclusive. ~x2 is not a type-set. See :DOC type-set ~ and :DOC type-set-inverter." *min-type-set* *max-type-set* (cadr alist))) (t (value (cadr alist))))) (otherwise (er soft ctx "The key ~x0 is unrecognized as a rule class component. ~ See :DOC rule-classes." (car alist)))))) (translate-rule-class-alist token (cddr alist) (cons (cons (car alist) val) seen) corollary name x ctx ens wrld state)))))) (defun translate-rule-class1 (class tflg name x ctx ens wrld state) ; Class is a candidate rule class. We know it is of the form (:key ; :key1 val1 ... :keyn valn). We know that among the :keyi is ; :COROLLARY and that if tflg is on then the :COROLLARY value has ; already been translated. Make sure class is syntactically legal and ; translate all the vals in it. X is the user's original ; specification of this class and is used only in error messages. ; Name is the name of the event for which this class is being ; translated. ; The binding below exhibits all the rule tokens and identifies the ; special additional keywords allowed (or required) by them. All of ; the tokens allow the keywords :COROLLARY, :HINTS, :INSTRUCTIONS, and ; :OTF-FLG. ; Note: The "definitive" description of the fields in our rule classes is to be ; found in (deflabel rule-classes ...). It is hygenic to compare periodically ; the setting below to the form described there. (let ((rule-tokens '(:REWRITE :LINEAR ; :TRIGGER-TERMS (optional) :WELL-FOUNDED-RELATION :BUILT-IN-CLAUSE :COMPOUND-RECOGNIZER :ELIM :GENERALIZE :META ; :TRIGGER-FNS :FORWARD-CHAINING ; :TRIGGER-TERMS (optional) :EQUIVALENCE :REFINEMENT :CONGRUENCE :TYPE-PRESCRIPTION ; :TYPED-TERM (optional) :DEFINITION ; :CLIQUE and :CONTROLLER-ALIST :INDUCTION ; :PATTERN, :CONDITION (optional), ; and :SCHEME :TYPE-SET-INVERTER ; :TYPE-SET (optional) :CLAUSE-PROCESSOR :TAU-SYSTEM ))) (cond ((not (member-eq (car class) rule-tokens)) (er soft ctx "~x0 is not one of the known rule tokens, ~&1. See :DOC ~ rule-classes." (car class) rule-tokens)) (t (er-let* ((corollary (cond (tflg (value (cadr (assoc-keyword :corollary (cdr class))))) (t (translate (cadr (assoc-keyword :corollary (cdr class))) t t t ctx wrld state)))) ; known-stobjs = t (stobjs-out = t) (alist (translate-rule-class-alist (car class) (cdr class) nil corollary name x ctx ens wrld state))) (value (cons (car class) alist))))))) (defun reason-for-non-keyword-value-listp (x) (cond ((atom x) (cond ((null x) (er hard 'reason-for-non-keyword-value-listp "Uh oh, it was a keyword-value-listp after all!")) (t (msg "there is a non-nil final cdr of ~x0" x)))) ((not (keywordp (car x))) (msg "we found a non-keyword, ~x0, where a keyword was expected" (car x))) ((atom (cdr x)) (msg "the value corresponding to the final key of ~x0 was missing" (car x))) (t (reason-for-non-keyword-value-listp (cddr x))))) (defun translate-rule-class (name x thm ctx ens wrld state) ; X is an untranslated rule class. For example, x may be :REWRITE or ; (:META :TRIGGER-FNS (fn1 ... fnk)) or even (:REWRITE :COROLLARY ; (IMPLIES p q) :HINTS ...). We either translate x into a "fully ; elaborated" rule class or else cause an error. A fully elaborated ; rule class starts with one of the rule class keywords, token, ; followed by an alternating keyword/value list. Every fully ; elaborated class has a :COROLLARY component. In addition, every ; :META class has a :TRIGGER-FNS component, every :FORWARD-CHAINING ; class has a :TRIGGER-TERMS component, and every :TYPE-PRESCRIPTION ; has a :TYPED-TERM component. No keyword is bound twice in the list ; and the values associated with each keyword is syntactically correct ; in a local sense, e.g., alleged function symbols are really function ; symbols, alleged terms are translated terms, alleged hints are ; translated hints, etc. We do not make the non-local checks, such as ; that the :COROLLARY of a :TYPE-PRESCRIPTION rule actually prescribes ; the type of the :TYPED-TERM. Those checks are made by the ; individual acceptability checkers. (let ((er-string "The object ~x0 is not a rule class. Rule classes are either certain ~ keywords, e.g., :REWRITE, or lists of the form (:rule-token :key1 ~ val1 :key2 val2 ...), as in (:REWRITE :COROLLARY term :HINTS ...). ~ In your case, ~@1. See :DOC rule-classes.")) (cond ((or (keywordp x) (and (consp x) (keywordp (car x)) (keyword-value-listp (cdr x)))) (translate-rule-class1 (cond ((symbolp x) (list x :COROLLARY thm)) ((assoc-keyword :COROLLARY (cdr x)) x) (t `(,(car x) :COROLLARY ,thm ,@(cdr x)))) (or (symbolp x) (not (assoc-keyword :COROLLARY (cdr x)))) name x ctx ens wrld state)) ((not (consp x)) (er soft ctx er-string x "the rule class is a non-keyword atom")) ((not (keywordp (car x))) (er soft ctx er-string x "the rule class starts with the non-keyword ~x2" (car x))) (t (er soft ctx er-string x (reason-for-non-keyword-value-listp (cdr x))))))) (defun translate-rule-classes1 (name classes thm ctx ens wrld state) ; We make sure that classes is a true list of legal rule classes. We ; translate the terms that occur in the classes and return the ; translated list of classes, i.e., a list of fully elaborated rule ; classes. (cond ((atom classes) (cond ((null classes) (value nil)) (t (er soft ctx "The list of rule classes is supposed to a true ~ list, but your list ends in ~x0. See :DOC ~ rule-classes." classes)))) (t (er-let* ((class (translate-rule-class name (car classes) thm ctx ens wrld state)) (rst (translate-rule-classes1 name (cdr classes) thm ctx ens wrld state))) (value (cons class rst)))))) (defun translate-rule-classes (name classes thm ctx ens wrld state) ; We adopt the convention that if a non-nil atomic classes is provided ; it is understood as the singleton list containing that atom. Thus, ; one is permitted to write ; :rule-classes :elim ; and have it understood as ; :rule-classes (:elim). ; However, it is not possible to so abbreviate non-atomic classes. ; That is, one might expect to be able to write: ; :rule-classes (:TYPE-PRESCRIPTION :TYPED-TERM (foo a b)) ; but one would be disappointed if one did. Any non-atomic value for ; classes is treated as though it were a list of rule classes. The effect ; intended by the above example can only be achieved by writing ; :rule-classes ((:TYPE-PRESCRIPTION :TYPED-TERM (foo a b))). (translate-rule-classes1 name (cond ((null classes) nil) ((atom classes) (list classes)) (t classes)) thm ctx ens wrld state)) ; We now turn our attention to the function that checks that a given ; term generates acceptable rules in all of a specified set of ; classes. The basic function is the one below, that takes a class ; token and calls the appropriate acceptability checker. In all of ; the code below we can assume that "class" is one of the objects ; produced by translate-rule-class above and "classes" is a true list ; of such objects. (defun chk-acceptable-x-rule (name class ctx ens wrld state) ; We check that the :COROLLARY term of class can be used as a rule of ; the class specified. Class is a fully elaborated, translated rule ; class. This function is just a big switch. Each exit subroutine ; returns a ttree justifying the claim that class describes a rule. (let ((term (cadr (assoc-keyword :COROLLARY (cdr class))))) (case (car class) (:REWRITE (chk-acceptable-rewrite-rule name (cadr (assoc-keyword :MATCH-FREE (cdr class))) (cadr (assoc-keyword :LOOP-STOPPER (cdr class))) term ctx ens wrld state)) (:LINEAR (chk-acceptable-linear-rule name (cadr (assoc-keyword :MATCH-FREE (cdr class))) (cadr (assoc-keyword :TRIGGER-TERMS (cdr class))) term ctx ens wrld state)) (:WELL-FOUNDED-RELATION (chk-acceptable-well-founded-relation-rule name term ctx wrld state)) (:BUILT-IN-CLAUSE (chk-acceptable-built-in-clause-rule name term ctx wrld state)) (:COMPOUND-RECOGNIZER (chk-acceptable-compound-recognizer-rule name term ctx ens wrld state)) (:ELIM (chk-acceptable-elim-rule name term ctx wrld state)) (:GENERALIZE (chk-acceptable-generalize-rule name term ctx wrld state)) (:EQUIVALENCE (chk-acceptable-equivalence-rule name term ctx wrld state)) (:CONGRUENCE (chk-acceptable-congruence-rule name term ctx wrld state)) (:REFINEMENT (chk-acceptable-refinement-rule name term ctx wrld state)) (:META ; We already know that the :TRIGGER-FNS of a :META rule class are all function ; symbols. However, we need them in order to produce warning messages when ; metafunctions produce non-termps. See chk-acceptable-meta-rule. (chk-acceptable-meta-rule name (cadr (assoc-keyword :TRIGGER-FNS (cdr class))) term ctx ens wrld state)) (:CLAUSE-PROCESSOR (chk-acceptable-clause-processor-rule name term ctx wrld state)) (:FORWARD-CHAINING (chk-acceptable-forward-chaining-rule name (cadr (assoc-keyword :MATCH-FREE (cdr class))) (cadr (assoc-keyword :TRIGGER-TERMS (cdr class))) term ctx ens wrld state)) (:TYPE-PRESCRIPTION (chk-acceptable-type-prescription-rule name (cadr (assoc-keyword :TYPED-TERM (cdr class))) term (cadr (assoc-keyword :BACKCHAIN-LIMIT-LST (cdr class))) ctx ens wrld state)) (:DEFINITION (chk-acceptable-definition-rule name (cadr (assoc-keyword :CLIQUE (cdr class))) (cadr (assoc-keyword :CONTROLLER-ALIST (cdr class))) (assoc-keyword :INSTALL-BODY (cdr class)) term ctx ens wrld state)) (:INDUCTION (chk-acceptable-induction-rule name term ctx wrld state)) (:TYPE-SET-INVERTER (chk-acceptable-type-set-inverter-rule name (cadr (assoc-keyword :TYPE-SET (cdr class))) term ctx ens wrld state)) (:TAU-SYSTEM (chk-acceptable-tau-rule name term ctx wrld state)) (otherwise (value (er hard ctx "Unrecognized rule class token ~x0 in CHK-ACCEPTABLE-X-RULE." (car class))))))) (defun chk-acceptable-x-rules (name classes ctx ens wrld state) ; Classes has already been translated and hence is known to be a true ; list of fully elaborated rule classes. Each class has a :COROLLARY ; term and we check that the term can be used as a rule of the ; indicated class. We return a tag-tree supporting the claim. (cond ((null classes) (value nil)) (t (er-let* ((ttree1 (chk-acceptable-x-rule name (car classes) ctx ens wrld state)) (ttree (chk-acceptable-x-rules name (cdr classes) ctx ens wrld state))) (value (cons-tag-trees ttree1 ttree)))))) (defun collect-keys-eq (sym-list alist) (cond ((endp alist) nil) ((member-eq (caar alist) sym-list) (cons (car alist) (collect-keys-eq sym-list (cdr alist)))) (t (collect-keys-eq sym-list (cdr alist))))) ; So here is how you check that it is legal to add the rules from a ; thm term, named name, in all of the classes classes. (defun chk-acceptable-rules (name classes ctx ens wrld state) ; The classes have already been translated, so we do not need to worry ; about unrecognized classes. Each class contains a :COROLLARY which ; is a translated term. We check that the :COROLLARY term can be used ; as a rule of the class indicated. We either cause an error or ; return a ttree justifying whatever pre/post-processing is done to ; store the rules. If we are not doing proofs we skip the checks. (let ((classes (cond ((or (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals)) ; We avoid the check for :REWRITE rules, tolerating a rare hard error as a ; result. See the comment just above the hard error in add-rewrite-rule2. ; We need to check :meta and :clause-processor rules even when skipping proofs. ; Below is a slight modification of a proof of nil sent by Dave Greve and Jared ; Davis, which is no longer possible after this check (namely: meta-foo-rule ; fails). In this example, the :meta rule is not supported by an evaluator in ; the second pass through the encapsulate. The Essay on Correctness of Meta ; Reasoning makes it clear that we need the evaluator axioms to justify the ; application of a :meta or :clause-processor rule. ; (defun meta-foo (term) ; (if (and (consp term) ; (equal (car term) 'foo)) ; *nil* ; term)) ; ; (encapsulate ; (((evx * *) => *) ; ((evx-list * *) => *) ; ((foo *) => *)) ; ; (local ; (defun foo (x) ; (declare (ignore x)) ; nil)) ; ; (local ; (defevaluator evx evx-list ; ((foo x)))) ; ; (defthm meta-foo-rule ; (equal (evx term a) ; (evx (meta-foo term) a)) ; :rule-classes ((:meta :trigger-fns (foo))))) ; ; (defun goo (x) ; (declare (ignore x)) ; t) ; ; (defthm qed ; (not (goo x)) ; :hints (("goal" :use (:functional-instance (:theorem (not (foo x))) ; (foo goo)) ; :in-theory (disable ; goo ; (:type-prescription goo) ; (goo)))) ; :rule-classes nil) ; ; (defthm contradiction ; nil ; :hints (("goal" :use qed :in-theory (enable goo))) ; :rule-classes nil) (collect-keys-eq '(:meta :clause-processor) classes)) (t classes)))) (cond ((null classes) ; optimization (value nil)) (t (er-let* ((ttree1 (chk-acceptable-x-rules name classes ctx ens wrld state))) ; At one time we accumulated ttree1 into state. But that caused rules to be ; reported during a failed proof that are not actually used in the proof. It ; is better to let install-event take care of accumulating this ttree (as part ; of the final ttree) into state, so that users can see the relevant ; explanatory message, "The storage of ... depends upon ...". (er-progn (chk-assumption-free-ttree ttree1 ctx state) (value ttree1))))))) ; We now turn to actually adding the rules generated. The development is ; exactly analogous to the checking above. (defun add-x-rule (rune nume class ens wrld state) ; We add the rules of class class derived from term. ; WARNING: If this function is changed, change info-for-x-rules (and/or ; subsidiaries) and find-rules-of-rune2. ; WARNING: If you add a new type of rule record, update access-x-rule-rune. (let ((term (cadr (assoc-keyword :COROLLARY (cdr class))))) (case (car class) (:REWRITE (add-rewrite-rule rune nume (assoc-keyword :LOOP-STOPPER (cdr class)) term (assoc-keyword :BACKCHAIN-LIMIT-LST (cdr class)) (cadr (assoc-keyword :MATCH-FREE (cdr class))) ens wrld)) (:LINEAR (add-linear-rule rune nume (cadr (assoc-keyword :TRIGGER-TERMS (cdr class))) term (assoc-keyword :BACKCHAIN-LIMIT-LST (cdr class)) (cadr (assoc-keyword :MATCH-FREE (cdr class))) ens wrld state)) (:WELL-FOUNDED-RELATION (add-well-founded-relation-rule rune nume term wrld)) (:BUILT-IN-CLAUSE (add-built-in-clause-rule rune nume term wrld)) (:COMPOUND-RECOGNIZER (add-compound-recognizer-rule rune nume term ens wrld)) (:ELIM (add-elim-rule rune nume term wrld)) (:GENERALIZE (add-generalize-rule rune nume term wrld)) (:EQUIVALENCE (add-equivalence-rule rune nume term ens wrld)) (:REFINEMENT (add-refinement-rule rune nume term wrld)) (:CONGRUENCE (add-congruence-rule rune nume term wrld)) (:META (add-meta-rule rune nume (cadr (assoc-keyword :TRIGGER-FNS (cdr class))) term (cadr (assoc-keyword :BACKCHAIN-LIMIT-LST (cdr class))) wrld)) (:CLAUSE-PROCESSOR (add-clause-processor-rule (base-symbol rune) term wrld)) (:FORWARD-CHAINING (add-forward-chaining-rule rune nume (cadr (assoc-keyword :TRIGGER-TERMS (cdr class))) term (cadr (assoc-keyword :MATCH-FREE (cdr class))) wrld)) (:TYPE-PRESCRIPTION (add-type-prescription-rule rune nume (cadr (assoc-keyword :TYPED-TERM (cdr class))) term (cadr (assoc-keyword :BACKCHAIN-LIMIT-LST (cdr class))) ens wrld nil)) (:DEFINITION (add-definition-rule rune nume (cadr (assoc-keyword :CLIQUE (cdr class))) (cadr (assoc-keyword :CONTROLLER-ALIST (cdr class))) (let ((pair (assoc-keyword :INSTALL-BODY (cdr class)))) (if pair (cadr pair) :NORMALIZE)) term ens wrld)) (:INDUCTION (add-induction-rule rune nume (cadr (assoc-keyword :PATTERN (cdr class))) (cadr (assoc-keyword :CONDITION (cdr class))) (cadr (assoc-keyword :SCHEME (cdr class))) term wrld)) (:TYPE-SET-INVERTER (add-type-set-inverter-rule rune nume (cadr (assoc-keyword :TYPE-SET (cdr class))) term ens wrld)) (:TAU-SYSTEM ; One might think that :tau-system rules are added here, since every other rule ; class is handled here. But one would be wrong! Because of the automatic mode in ; the tau system and because of the facility for regenerating the tau database, ; :tau-system rules are added by the tau-visit code invoked most often from ; install-event. wrld) ; WARNING: If this function is changed, change info-for-x-rules (and/or ; subsidiaries) and find-rules-of-rune2. ; WARNING: If you add a new type of rule record, update access-x-rule-rune. (otherwise (er hard 'add-x-rule "Unrecognized rule class token ~x0 in ADD-X-RULE." (car class)))))) (defun add-rules1 (mapping-pairs classes ens wrld state) ; Mapping-pairs is in 1:1 correspondence with classes. Each mapping ; pair is of the form (nume . rune) and gives the nume and rune we are ; to use for the rule built according to the corresponding element of ; classes. Recall that each element of classes has a :COROLLARY component ; which is the term describing the rule. Thus, term (above) is actually ; not used to build any rule. (cond ((null classes) wrld) (t (add-rules1 (cdr mapping-pairs) (cdr classes) ens (add-x-rule (cdr (car mapping-pairs)) (car (car mapping-pairs)) (car classes) ens wrld state) state)))) (defun truncate-class-alist (alist term) ; Alist is the cdr of a fully elaborated rule class and hence is a ; keyword-alistp -- not a regular alist! As such it contains a :COROLLARY ; field and perhaps :HINTS and :INSTRUCTIONS. A truncated class is a fully ; elaborated class with the :HINTS and :INSTRUCTIONS fields thrown out. In ; addition, we throw out the :COROLLARY field if its value is term. (cond ((null alist) nil) ((or (eq (car alist) :HINTS) (eq (car alist) :INSTRUCTIONS) (and (eq (car alist) :COROLLARY) (equal (cadr alist) term))) (truncate-class-alist (cddr alist) term)) (t (cons (car alist) (cons (cadr alist) (truncate-class-alist (cddr alist) term)))))) (defun truncate-classes (classes term) ; This function generates the value we store under the ; 'truncated-classes property of an event whose 'theorem property is ; term. It seems sensible to us to store the fully elaborated rule ; classes for a name and term. For example, from them you can recover ; the exact logical expression of a given rule. But a fully ; elaborated rule class can be an exceedingly large object to display, ; e.g., with :PROPS, because its translated :HINTS fields may contain ; large theories. Thus, we "truncate" the elaborated classes, ; throwing away :HINTS, :INSTRUCTIONS, and perhaps (if it is identical ; to term, the 'theorem field of the event). (cond ((null classes) nil) (t (cons (cons (caar classes) (truncate-class-alist (cdar classes) term)) (truncate-classes (cdr classes) term))))) (defun make-runes1 (event-name classes runes) ; Event-name is a symbol and classes is a list of fully elaborated ; rule classes. Hence, each element of classes is a list that starts ; with a rule token keyword, e.g., :REWRITE, :META, etc. We make up a ; list of runes in 1:1 correspondence with classes. The general form ; of a name is (token event-name . i), where token is the keyword for ; the class and i enumerates how many occurrences we have already ; counted for that keyword. So for example, suppose event-name is FOO ; and classes contains, in order two :REWRITE classes and an :ELIM ; class, then we will name them (:REWRITE FOO . 1) (:REWRITE FOO . 2) ; (:ELIM FOO). Note the oddity: if there is just one rule with a ; given token, its i is nil; otherwise i is an integer that counts ; from 1. (cond ((null classes) (revappend runes nil)) (t (let* ((token (caar classes)) (temp (assoc-eq token runes))) (make-runes1 event-name (cdr classes) ; The new name we add is of the form (token event-name . i) where ; i is: 1, if we haven't seen token before but there is another occurrence ; of token in classes; nil, if we haven't seen token before and we won't ; see it again; and one more than the last i for token if we've seen ; token before. (cons (cons token (cons event-name (cond ((null temp) (cond ((assoc-eq token (cdr classes)) 1) (t nil))) (t (1+ (cddr temp)))))) runes)))))) (defun make-runes (event-name classes) ; Given an event name and the rule classes for the event we create the ; list of runes naming each rule. The list is in 1:1 correspondence ; with classes. (make-runes1 event-name classes nil)) (defun make-runic-mapping-pairs (nume runes) ; Given the nume to be assigned to the first rune in a list of runes, ; we return a list, in ascending order by nume, of the mapping pairs, ; each pair of the form (nume . rune), in 1:1 correspondence with ; runes. (cond ((null runes) (prog2$ (or (<= nume (fixnum-bound)) (max-nume-exceeded-error 'make-runic-mapping-pairs)) nil)) (t (cons (cons nume (car runes)) (make-runic-mapping-pairs (1+ nume) (cdr runes)))))) (defun add-rules (name classes term untranslated-term ens wrld state) ; Name is an event name. We store term under the 'theorem property ; for name. Under the 'truncated-classes for name we store the ; truncated, but otherwise fully elaborated, rule classes. Under the ; 'runic-mapping-pairs we store the alist mapping numes to runes, ; i.e., ((n1 . rune1) ... (nk . runek)), where the runes are in 1:1 ; correspondence with classes. The numes are consecutive integers ; uniquely associated with the corresponding runes. N1 is the least, ; Nk is the greatest, and thus Nk+1 is the next available nume in the ; world resulting from this addition. For more on runes and numes, ; see runep. See also the Essay on the Assignment of Runes and Numes ; by DEFUNS. (let ((runic-mapping-pairs (make-runic-mapping-pairs (get-next-nume wrld) (make-runes name classes)))) (putprop name 'runic-mapping-pairs runic-mapping-pairs (putprop name 'theorem term (putprop name 'untranslated-theorem untranslated-term (putprop name 'classes (truncate-classes classes term) (add-rules1 runic-mapping-pairs classes ens wrld state))))))) (defun redundant-theoremp (name term classes wrld) ; We know name is a symbol, but it may or may not be new. We return t ; if name is already defined as the name of the theorem term with the ; given rule-classes. If we are booting, no theorem is redundant. (and (equal term (getprop name 'theorem 0 'current-acl2-world wrld)) (equal (truncate-classes classes term) (getprop name 'classes 0 'current-acl2-world wrld)))) ; The next part develops the functions for proving that each alleged ; corollary in a rule class follows from the theorem proved. (defun non-tautological-classes (term classes) ; Term is a translated term (indeed, it known to be a theorem). ; Classes is a list of translated rule classes, each therefore having ; a :COROLLARY field. We'll say an element of classes is ; "tautological" if its :COROLLARY is implied by term, e.g., if ; (IMPLIES term corollary) is a theorem. Return that sublist of ; classes consisting of the non-tautological elements. (cond ((null classes) nil) ((let ((cor (cadr (assoc-keyword :COROLLARY (cdr (car classes)))))) (or (equal term cor) (if-tautologyp (mcons-term* 'if term cor *t*)))) (non-tautological-classes term (cdr classes))) (t (cons (car classes) (non-tautological-classes term (cdr classes)))))) (defun prove-corollaries1 (name term i n rule-classes ens wrld ctx state ttree) ; Term is a theorem just proved. Rule-classes is a list of translated ; rule classes and each is known to be non-tautological wrt term. We ; prove that term implies the :corollary of each rule class, or cause ; an error. We return the ttree accumulated from all the proofs. The ; two variables i and n are integers and used merely to control the ; output that enumerates where we are in the process: i is a 1-based ; counter indicating the position in the enumeration of the next rule ; class; n is the number of rule classes in all. (cond ((null rule-classes) (value ttree)) (t (let ((goal (fcons-term* 'implies term (cadr (assoc-keyword :COROLLARY (cdr (car rule-classes)))))) (otf-flg (cadr (assoc-keyword :OTF-FLG (cdr (car rule-classes))))) (hints (cadr (assoc-keyword :HINTS (cdr (car rule-classes))))) (instructions (cadr (assoc-keyword :INSTRUCTIONS (cdr (car rule-classes)))))) (er-let* ((hints (if hints (value hints) ; already translated, with default-hints (let ((default-hints (default-hints wrld))) (if default-hints ; not yet translated; no explicit hints (translate-hints (cons "Corollary of " name) default-hints ctx wrld state) (value nil)))))) (pprogn (io? event nil state (wrld goal n i) (fms "The~#0~[~/ first~/ second~/ next~/ last~] goal is ~p1.~%" (list (cons #\0 (cond ((and (= i 1) (= n 1)) 0) ((= i 1) 1) ((= i 2) 2) ((= i n) 4) (t 3))) (cons #\1 (untranslate goal t wrld))) (proofs-co state) state (term-evisc-tuple nil state))) (er-let* ((ttree1 (cond (instructions (proof-checker nil (untranslate goal t wrld) goal nil instructions wrld state)) (t (prove goal (make-pspv ens wrld :displayed-goal goal :otf-flg otf-flg) hints ens wrld ctx state))))) (prove-corollaries1 name term (1+ i) n (cdr rule-classes) ens wrld ctx state (cons-tag-trees ttree1 ttree))))))))) (defun prove-corollaries (name term rule-classes ens wrld ctx state) ; Rule-classes is a list of translated rule classes. The basic idea ; is to prove the :COROLLARY of every class in rule-classes. Like ; prove, we return an error triple; the non-erroneous value is a ttree ; signalling the successful proof of all the corollaries. (let* ((classes (non-tautological-classes term rule-classes)) (n (length classes))) (cond ((= n 0) (value nil)) (t (pprogn (io? event nil state (rule-classes n) (fms "~%We now consider~#2~[ the~/, in turn, the ~x0~]~#1~[~/ ~ non-trivial~] ~#2~[corollary~/corollaries~] claimed in the ~ specified rule ~#3~[class~/classes~].~%" (list (cons #\0 n) (cons #\1 (if (= (length rule-classes) 1) 0 1)) (cons #\2 (if (= n 1) 0 1)) (cons #\3 (if (= (length rule-classes) 1) 0 1))) (proofs-co state) state (term-evisc-tuple nil state))) (prove-corollaries1 name term 1 n classes ens wrld ctx state nil)))))) ;--------------------------------------------------------------------------- ; Section: More History Management and Command Stuff ; While we are at it, we here develop the code for printing out all the ; rules added by a particular event. (defun enabled-runep-string (rune ens wrld) (if (enabled-runep rune ens wrld) "Enabled" "Disabled")) (defun untranslate-hyps (hyps wrld) (cond ((null hyps) t) ((null (cdr hyps)) (untranslate (car hyps) t wrld)) (t (cons 'and (untranslate-lst hyps t wrld))))) (defun info-for-lemmas (lemmas numes ens wrld) (if (null lemmas) nil (let* ((rule (car lemmas)) (nume (access rewrite-rule rule :nume)) (rune (access rewrite-rule rule :rune)) (subclass (access rewrite-rule rule :subclass)) (lhs (access rewrite-rule rule :lhs)) (rhs (access rewrite-rule rule :rhs)) (hyps (access rewrite-rule rule :hyps)) (equiv (access rewrite-rule rule :equiv)) (backchain-limit-lst (access rewrite-rule rule :backchain-limit-lst)) (heuristic-info (access rewrite-rule rule :heuristic-info))) (if (or (eq numes t) (member nume numes)) (cons `((:rune ,rune :rewrite ,nume) (:enabled ,(and (enabled-runep rune ens wrld) t)) ,@(if (eq subclass 'meta) `((:meta-fn ,lhs) (:hyp-fn ,(or hyps :none) hyps)) `((:lhs ,(untranslate lhs nil wrld) lhs) (:rhs ,(untranslate rhs nil wrld) rhs) (:hyps ,(untranslate-hyps hyps wrld) hyps))) (:equiv ,equiv) (:backchain-limit-lst ,backchain-limit-lst) (:subclass ,subclass) ,@(cond ((eq subclass 'backchain) `((:loop-stopper ,heuristic-info))) ((eq subclass 'definition) `((:clique ,(car heuristic-info)) (:controller-alist ,(cdr heuristic-info)))) (t nil))) (info-for-lemmas (cdr lemmas) numes ens wrld)) (info-for-lemmas (cdr lemmas) numes ens wrld))))) (defun world-to-next-event (wrld) (cond ((null wrld) nil) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value)) nil) (t (cons (car wrld) (world-to-next-event (cdr wrld)))))) (defun assoc-eq-eq (x y alist) ; We look for a pair on alist of the form (x y . val) where we compare with x ; and y using eq. We return the pair or nil. (cond ((endp alist) nil) ((and (eq (car (car alist)) x) (eq (car (cdr (car alist))) y)) (car alist)) (t (assoc-eq-eq x y (cdr alist))))) (defun actual-props (props seen acc) ; Props is a list whose elements have the form (sym key . val), where val could ; be *acl2-property-unbound*. Seen is the list containing some (sym key . &) ; for each pair (sym key) that has already been seen. (cond ((null props) (reverse acc)) ((assoc-eq-eq (caar props) (cadar props) seen) (actual-props (cdr props) seen acc)) ((eq (cddr (car props)) *acl2-property-unbound*) (actual-props (cdr props) (cons (car props) seen) acc)) (t (actual-props (cdr props) (cons (car props) seen) (cons (car props) acc))))) (defun info-for-well-founded-relation-rules (rules) ; There is not record class corresponding to well-founded-relation rules. But ; the well-founded-relation-alist contains triples of the form (rel mp . rune) ; and we assume rules is a list of such triples. (if (null rules) nil (let* ((rule (car rules)) (rune (cddr rule))) (cons (list (list :rune rune :well-founded-relation) (list :domain-predicate (cadr rule)) (list :well-founded-relation (car rule))) (info-for-well-founded-relation-rules (cdr rules)))))) (defun info-for-built-in-clause-rules1 (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (nume (access built-in-clause rule :nume)) (rune (access built-in-clause rule :rune)) (clause (access built-in-clause rule :clause))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :built-in-clause nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :clause (prettyify-clause clause nil wrld) clause)) (info-for-built-in-clause-rules1 (cdr rules) numes ens wrld)) (info-for-built-in-clause-rules1 (cdr rules) numes ens wrld))))) (defun info-for-built-in-clause-rules (alist numes ens wrld) (if (null alist) nil (append (info-for-built-in-clause-rules1 (cdar alist) numes ens wrld) (info-for-built-in-clause-rules (cdr alist) numes ens wrld)))) (defun info-for-compound-recognizer-rules (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (nume (access recognizer-tuple rule :nume)) (rune (access recognizer-tuple rule :rune)) (true-ts (access recognizer-tuple rule :true-ts)) (false-ts (access recognizer-tuple rule :false-ts)) (strongp (access recognizer-tuple rule :strongp))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :compound-recognizer nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :fn (access recognizer-tuple rule :fn)) (list :true-ts (decode-type-set true-ts) true-ts) (list :false-ts (decode-type-set false-ts) false-ts) (list :strongp strongp)) (info-for-compound-recognizer-rules (cdr rules) numes ens wrld)) (info-for-compound-recognizer-rules (cdr rules) numes ens wrld))))) (defun info-for-generalize-rules (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (nume (access generalize-rule rule :nume)) (rune (access generalize-rule rule :rune)) (formula (access generalize-rule rule :formula))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :generalize nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :formula (untranslate formula t wrld) formula)) (info-for-generalize-rules (cdr rules) numes ens wrld)) (info-for-generalize-rules (cdr rules) numes ens wrld))))) (defun info-for-linear-lemmas (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (nume (access linear-lemma rule :nume)) (rune (access linear-lemma rule :rune)) (hyps (access linear-lemma rule :hyps)) (concl (access linear-lemma rule :concl)) (max-term (access linear-lemma rule :max-term)) (backchain-limit-lst (access linear-lemma rule :backchain-limit-lst))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :linear nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :hyps (untranslate-hyps hyps wrld) hyps) (list :concl (untranslate concl nil wrld) concl) (list :max-term (untranslate max-term nil wrld) max-term) (list :backchain-limit-lst backchain-limit-lst)) (info-for-linear-lemmas (cdr rules) numes ens wrld)) (info-for-linear-lemmas (cdr rules) numes ens wrld))))) (defun info-for-eliminate-destructors-rule (rule numes ens wrld) (let ((rune (access elim-rule rule :rune)) (nume (access elim-rule rule :nume)) (hyps (access elim-rule rule :hyps)) (lhs (access elim-rule rule :lhs)) (rhs (access elim-rule rule :rhs)) (destructor-term (access elim-rule rule :destructor-term)) (destructor-terms (access elim-rule rule :destructor-terms)) (crucial-position (access elim-rule rule :crucial-position))) (if (or (eq numes t) (member nume numes)) (list (list (list :rune rune :elim nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :hyps (untranslate-hyps hyps wrld) hyps) (list :lhs (untranslate lhs nil wrld) lhs) (list :rhs (untranslate rhs nil wrld) rhs) (list :destructor-term (untranslate destructor-term nil wrld) destructor-term) (list :destructor-terms (untranslate-lst destructor-terms nil wrld) destructor-terms) (list :crucial-position crucial-position))) nil))) (defun info-for-congruences (val numes ens wrld) ; val is of the form (equiv geneqv1 ... geneqvk ... geneqvn). ; This seems complicated so we'll punt for now. (declare (ignore val numes ens wrld)) nil) (defun info-for-coarsenings (val numes ens wrld) ; It is not obvious how to determine which coarsenings are really new, so we ; print nothing. (declare (ignore val numes ens wrld)) nil) (defun info-for-forward-chaining-rules (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (rune (access forward-chaining-rule rule :rune)) (nume (access forward-chaining-rule rule :nume)) (trigger (access forward-chaining-rule rule :trigger)) (hyps (access forward-chaining-rule rule :hyps)) (concls (access forward-chaining-rule rule :concls))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :forward-chaining nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :trigger (untranslate trigger nil wrld) trigger) (list :hyps (untranslate-hyps hyps wrld) hyps) (list :concls ; The :concls of a forward-chaining rule is really a implicit conjunction of ; all the conclusions you can draw. So we untranslate the list and put an ; AND on the front, which is just what untranslate-hyps does, oddly enough. (untranslate-hyps concls wrld) concls)) (info-for-forward-chaining-rules (cdr rules) numes ens wrld)) (info-for-forward-chaining-rules (cdr rules) numes ens wrld))))) (defun decode-type-set-lst (lst) (if lst (cons (decode-type-set (car lst)) (decode-type-set-lst (cdr lst))) nil)) (defun info-for-type-prescriptions (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (rune (access type-prescription rule :rune)) (nume (access type-prescription rule :nume)) (term (access type-prescription rule :term)) (hyps (access type-prescription rule :hyps)) (backchain-limit-lst (access type-prescription rule :backchain-limit-lst)) (basic-ts (access type-prescription rule :basic-ts)) (vars (access type-prescription rule :vars)) (corollary (access type-prescription rule :corollary))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :type-prescription nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :term (untranslate term nil wrld) term) (list :hyps (untranslate-hyps hyps wrld) hyps) (list :backchain-limit-lst backchain-limit-lst) (list :basic-ts (decode-type-set basic-ts) basic-ts) (list :vars vars) (list :corollary (untranslate corollary t wrld) corollary)) (info-for-type-prescriptions (cdr rules) numes ens wrld)) (info-for-type-prescriptions (cdr rules) numes ens wrld))))) (defun info-for-induction-rules (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (rune (access induction-rule rule :rune)) (nume (access induction-rule rule :nume)) (pattern (access induction-rule rule :pattern)) (condition (access induction-rule rule :condition)) (scheme (access induction-rule rule :scheme))) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :induction nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :pattern (untranslate pattern nil wrld) pattern) (list :condition (untranslate condition t wrld) condition) (list :scheme (untranslate scheme nil wrld) scheme)) (info-for-induction-rules (cdr rules) numes ens wrld)) (info-for-induction-rules (cdr rules) numes ens wrld))))) (defun info-for-type-set-inverter-rules (rules numes ens wrld) (if (null rules) nil (let* ((rule (car rules)) (rune (access type-set-inverter-rule rule :rune)) (nume (access type-set-inverter-rule rule :nume)) (type-set (access type-set-inverter-rule rule :ts)) (terms (access type-set-inverter-rule rule :terms)) ) (if (or (eq numes t) (member nume numes)) (cons (list (list :rune rune :type-set-inverter nume) (list :enabled (and (enabled-runep rune ens wrld) t)) (list :type-set type-set) (list :condition (untranslate-hyps terms wrld) terms)) (info-for-type-set-inverter-rules (cdr rules) numes ens wrld)) (info-for-type-set-inverter-rules (cdr rules) numes ens wrld))))) (defun info-for-x-rules (sym key val numes ens wrld) ; See add-x-rule for an enumeration of rule classes that generate the ; properties shown below. ; Warning: Keep this function in sync with find-rules-of-rune2. In that ; spirit, tau rules are completely invisible and so we return nil for ; any property affected by tau rules. ; Info functions inspect the various rules and turn them into alists of the ; form: ; (key . (value1 ... valueN)) ; When we print these alists with :pr, we only print "key: value1". This lets ; you store additional information in later values. For example, value1 might ; want to untranslate the term for prettier printing to the user, or decode the ; type-set, etc. Value2 can then include the original term or undecoded ; type-set, so that programs can use that value instead. (cond ((eq key 'global-value) (case sym (well-founded-relation-alist ; Avoid printing the built-in anonymous rule if that is all we have here. (if (consp (cdr val)) (info-for-well-founded-relation-rules val) nil)) (built-in-clauses (info-for-built-in-clause-rules val numes ens wrld)) (type-set-inverter-rules (info-for-type-set-inverter-rules val numes ens wrld)) (recognizer-alist (info-for-compound-recognizer-rules val numes ens wrld)) (generalize-rules (info-for-generalize-rules val numes ens wrld)) (otherwise nil))) (t (case key (lemmas (info-for-lemmas val numes ens wrld)) (linear-lemmas (info-for-linear-lemmas val numes ens wrld)) (eliminate-destructors-rule (info-for-eliminate-destructors-rule val numes ens wrld)) (congruences (info-for-congruences val numes ens wrld)) (coarsenings (info-for-coarsenings val numes ens wrld)) (forward-chaining-rules (info-for-forward-chaining-rules val numes ens wrld)) (type-prescriptions (info-for-type-prescriptions val numes ens wrld)) (induction-rules (info-for-induction-rules val numes ens wrld)) (otherwise nil))))) (defun info-for-rules (props numes ens wrld) (cond ((null props) nil) ((eq (cadar props) *acl2-property-unbound*) (info-for-rules (cdr props) numes ens wrld)) (t (append (info-for-x-rules (caar props) (cadar props) (cddar props) numes ens wrld) (info-for-rules (cdr props) numes ens wrld))))) (defun print-info-for-rules-entry (keys vals chan state) (if (not (consp keys)) state (mv-let (col state) (fmt1 "~s0:" (list (cons #\0 (let* ((name (symbol-name (car keys))) (lst (coerce name 'list))) (coerce (cons (car lst) (string-downcase1 (cdr lst))) 'string)))) 0 chan state nil) (mv-let (col state) (cond ((< col 14) (fmt1 "~t0~q1" (list (cons #\0 14) (cons #\1 (caar vals))) col chan state nil)) (t (fmt1 " ~q1" (list (cons #\0 14) (cons #\1 (caar vals))) col chan state nil))) (declare (ignore col)) (print-info-for-rules-entry (cdr keys) (cdr vals) chan state))))) (defun print-info-for-rules (info chan state) (if (not (consp info)) (value :invisible) (pprogn (newline chan state) (print-info-for-rules-entry (strip-cars (car info)) (strip-cdrs (car info)) chan state) (print-info-for-rules (cdr info) chan state)))) (defun pr-body (wrld-segment numes wrld state) (print-info-for-rules (info-for-rules (actual-props wrld-segment nil nil) numes (ens state) wrld) (standard-co state) state)) (defun pr-fn (name state) (cond ((and (symbolp name) (not (keywordp name))) (let* ((wrld (w state)) (name (deref-macro-name name (macro-aliases wrld))) (numes (strip-cars (getprop name 'runic-mapping-pairs nil 'current-acl2-world wrld))) (wrld-segment (world-to-next-event (cdr (decode-logical-name name wrld))))) (pr-body wrld-segment numes wrld state))) (t (er soft 'pr "The argument to PR must be a non-keyword symbol. Perhaps you ~ should use PR! instead.")))) (defun print-clause-processor-rules1 (alist wrld state) (if (null alist) (value :invisible) (let* ((pair (car alist)) (name (car pair)) (term (cdr pair))) (pprogn (fms "Rule ~x0:~|~P12~|" (list (cons #\0 name) (cons #\1 (untranslate term nil wrld)) (cons #\2 (term-evisc-tuple nil state))) (standard-co state) state nil) (print-clause-processor-rules1 (cdr alist) wrld state))))) (defmacro print-clause-processor-rules () '(let ((wrld (w state))) (print-clause-processor-rules1 (global-val 'clause-processor-rules wrld) wrld state))) (defun new-numes (world-segment) (cond ((null world-segment) nil) ((and (eq (cadr (car world-segment)) 'runic-mapping-pairs) (not (eq (cddr (car world-segment)) *acl2-property-unbound*))) (append (strip-cars (cddr (car world-segment))) (new-numes (cdr world-segment)))) (t (new-numes (cdr world-segment))))) (defun world-to-next-command (wrld ans) (cond ((null wrld) (reverse ans)) ((and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value)) (reverse ans)) (t (world-to-next-command (cdr wrld) (cons (car wrld) ans))))) (defun pr!-fn (cd state) ; We assume that the world starts with a command landmark. (let ((wrld (w state))) (er-let* ((wrld-tail (er-decode-cd cd wrld 'print-new-rules state))) (pr-body (world-to-next-command (cdr wrld-tail) nil) t wrld state)))) (defmacro pr (name) ":Doc-Section History print the rules stored by the event with the given name~/ ~bv[] Examples: :pr fn ; prints the rules from the definition of fn (including any ; :type-prescription rule and :definition rule) :pr assoc-append ; if assoc-append is a rewrite rule, prints that rule ~ev[]~/ Also ~pl[pr!], which is similar but works at the command level instead of at the event level, and ~pl[pl], which displays all rewrite rules for calls of ~c[fn], not just those introduced at definition time. ~c[Pr] takes one argument, a logical name, and prints the rules associated with it. In each case it prints the rune, the current enabled/disabled status, and other appropriate fields from the rule. It may be helpful to read the documentation for various kinds of rules in order to understand the information printed by this command. For example, the information printed for a linear rule might be: ~bv[] Rune: (:LINEAR ABC) Enabled: T Hyps: ((CONSP X)) Concl: (< (ACL2-COUNT (CAR X)) (ACL2-COUNT X)) Max-term: (ACL2-COUNT (CAR X)) Backchain-limit-lst: (3) ~ev[] The ~c[hyps] and ~c[concl] fields for this rule are fairly self-explanatory, but it is useful to ~pl[linear] to learn about maximal terms (which, as one might guess, are stored under ``Max-term''). Currently, this function does not print congruence rules or equivalence rules. The expert user might also wish to use ~ilc[find-rules-of-rune]. ~l[find-rules-of-rune].~/" (list 'pr-fn name 'state)) (defmacro pr! (cd) ":Doc-Section History print rules stored by the command with a given command descriptor~/ ~bv[] Examples: :pr! fn ; prints the rules from the definition of fn (including any ; :type-prescription rule and :definition rule), as well as all other ; rules created by the command that created by fn (which could be ; many rules if, for example, fn was defined by an include-book ; command). :pr! :max ; prints all the rules stored by the most recent command ~ev[]~/ Also ~pl[pr], which is similar but works at the event level instead of at the command level. ~ilc[Pr] takes one argument, a command descriptor, and prints the rules created by the corresponding event. In each case it prints the rune, the current enabled/disabled status, and other appropriate fields from the rule. ~l[pr] for further details.~/" (list 'pr!-fn cd 'state)) (defun disabledp-fn-lst (runic-mapping-pairs ens) (cond ((null runic-mapping-pairs) nil) ((enabled-numep (caar runic-mapping-pairs) ens) (disabledp-fn-lst (cdr runic-mapping-pairs) ens)) (t (cons (cdar runic-mapping-pairs) (disabledp-fn-lst (cdr runic-mapping-pairs) ens))))) (defun disabledp-fn (name ens wrld) (declare (xargs :guard t)) (cond ((symbolp name) (let ((name2 (if (symbolp name) (deref-macro-name name (macro-aliases wrld)) name))) (cond ((and (not (eq name2 :here)) name2 (logical-namep name2 wrld)) (disabledp-fn-lst (getprop name2 'runic-mapping-pairs nil 'current-acl2-world wrld) ens)) (t (er hard 'disabledp "Illegal call of disabledp on symbolp argument ~x0. ~ See :DOC disabledp." name))))) ((runep name wrld) (not (enabled-runep name ens wrld))) (t (er hard 'disabledp "Illegal call of disabledp on non-symbol, non-rune argument ~ ~x0. See :DOC disabledp." name)))) (defmacro disabledp (name) ":Doc-Section Miscellaneous determine whether a given name or rune is disabled~/ ~bv[] Examples: :disabledp foo ; returns a list of all disabled runes whose base ; symbol is foo (~pl[rune]) (disabledp 'foo) ; same as above (i.e., :disabledp foo) :disabledp (:rewrite bar . 1) ; returns t if the indicated rune is ; disabled, else nil (disabledp (:rewrite bar . 1)); same as immediately above ~ev[]~/ Also ~pl[pr], which gives much more information about the rules associated with a given event. ~c[Disabledp] takes one argument, an event name (~pl[events]) other than ~c[nil] or a ~il[rune]. In the former case it returns the list of disabled runes associated with that name, in the sense that the rune's ``base symbol'' is that name (~pl[rune]) or, if the event named is a ~ilc[defmacro] event, then the list of disabled runes associated with the function corresponding to that macro name, if any (~pl[macro-aliases-table]). In the latter case, where the argument is a ~il[rune], ~c[disabledp] returns ~c[t] if the rune is disabled, and ~c[nil] otherwise.~/" `(disabledp-fn ,name (ens state) (w state))) (defun access-x-rule-rune (x rule) ; Given a rule object, rule, of record type x, we return the :rune of rule. ; This is thus ``(access x rule :rune).'' ; Note: We include with every case the rule-class tokens that create this rule ; so that we can search for any such tokens and find this function when adding ; a new, similar, rule-class. ; There is no record object generated only by ;;; :refinement ; ;;; :tau-system (case x (recognizer-tuple ;;; :compound-recognizer (access recognizer-tuple rule :rune)) (type-prescription ;;; :type-prescription (access type-prescription rule :rune)) (congruence-rule ;;; :congruence ;;; :equivalence (access congruence-rule rule :rune)) (rewrite-rule ;;; :rewrite ;;; :meta ;;; :definition (access rewrite-rule rule :rune)) (well-founded-relation-rule ;;; :well-founded-relation ; No such record type, but we pretend! (cddr rule)) (linear-lemma ;;; :linear (access linear-lemma rule :rune)) (forward-chaining-rule ;;; :forward-chaining (access forward-chaining-rule rule :rune)) (built-in-clause ;;; :built-in-clause (access built-in-clause rule :rune)) (elim-rule ;;; :elim (access elim-rule rule :rune)) (generalize-rule ;;; :generalize (access generalize-rule rule :rune)) (induction-rule ;;; :induction (access induction-rule rule :rune)) (type-set-inverter-rule ;;; :type-set-inverter (access type-set-inverter-rule rule :rune)) (otherwise (er hard 'access-x-rule-rune "Unrecognized rule class, ~x0." x)))) (defun collect-x-rules-of-rune (x rune lst ans) ; Lst is a list of rules of type x. We collect all those elements of lst ; with :rune rune. (cond ((null lst) ans) ((equal rune (access-x-rule-rune x (car lst))) (collect-x-rules-of-rune x rune (cdr lst) (add-to-set-equal (car lst) ans))) (t (collect-x-rules-of-rune x rune (cdr lst) ans)))) (defun collect-congruence-rules-of-rune-in-geneqv-lst (geneqv-lst rune ans) ; A geneqv is a list of congruence rules. Geneqv-lst, above, is a list of ; geneqvs. We scan every congruence rule in geneqv-lst and collect those with ; the :rune rune. (cond ((null geneqv-lst) ans) (t (collect-congruence-rules-of-rune-in-geneqv-lst (cdr geneqv-lst) rune (collect-x-rules-of-rune 'congruence-rule rune (car geneqv-lst) ans))))) (defun collect-congruence-rules-of-rune (congruences rune ans) ; The 'congruences property of an n-ary function symbol is a list of tuples, ; each of which is of the form (equiv geneqv1 ... geneqvn), where each geneqvi ; is a list of congruence rules. Congruences is the 'congruences property of ; some function. We scan it and collect every congruence rule in it that has ; :rune rune. (cond ((null congruences) ans) (t (collect-congruence-rules-of-rune (cdr congruences) rune (collect-congruence-rules-of-rune-in-geneqv-lst (cdr (car congruences)) rune ans))))) (defun find-rules-of-rune2 (rune sym key val ans) ; (sym key . val) is a member of wrld. We collect all the rules in val with ; :rune rune. This function is patterned after info-for-x-rules. ; Wart: If key is 'eliminate-destructors-rule, then val is a single rule, not a ; list of rules. We handle this case specially below. ; Warning: Keep this function in sync with info-for-x-rules. In that spirit, ; note that tau rules never store runes and hence are completely ignored ; here, as in info-for-x-rules. (let ((token (car rune))) ; As an efficiency, we do not look for rune in places where it cannot occur. ; For example, if token is :elim then there is no point in searching through ; the 'lemmas properties. In general, each case below insists that token is of ; the appropriate class. Sometimes there are more than one. For example, the ; 'lemmas property may contain :rewrite, :definition, and :meta runes, all of ; which are stored as REWRITE-RULEs. (cond ((eq key 'global-value) (case sym (well-founded-relation-alist (if (eq token :well-founded-relation) (collect-x-rules-of-rune 'well-founded-relation-rule rune val ans) ans)) (built-in-clauses (if (eq token :built-in-clause) (collect-x-rules-of-rune 'built-in-clause rune val ans) ans)) (type-set-inverter-rules (if (eq token :type-set-inverter) (collect-x-rules-of-rune 'type-set-inverter-rule rune val ans) ans)) (recognizer-alist (if (eq token :compound-recognizer) (collect-x-rules-of-rune 'recognizer-tuple rune val ans) ans)) (generalize-rules (if (eq token :generalize) (collect-x-rules-of-rune 'generalize-rule rune val ans) ans)) (otherwise ans))) (t (case key (lemmas (if (member-eq token '(:rewrite :meta :definition)) (collect-x-rules-of-rune 'rewrite-rule rune val ans) ans)) (linear-lemmas (if (eq token :linear) (collect-x-rules-of-rune 'linear-lemma rune val ans) ans)) (eliminate-destructors-rule (if (eq token :elim) (collect-x-rules-of-rune 'elim-rule rune (list val) ans) ans)) (congruences (if (member-eq token '(:congruence :equivalence)) (collect-congruence-rules-of-rune val rune ans) ans)) (coarsenings ; :Refinement rules add to the 'coarsenings property. If equiv1 is a ; refinement of equiv2, then equiv2 is a coarsening of equiv1 and the lemma ; establishing that fact adds equiv2 to the 'coarsenings property of equiv1. ; There is no rule object corresponding to this fact. Hence, even if rune is ; the :refinement rune responsible for adding some equiv2 to this list, we ; won't find a rule object here by the name rune. ; Similar comments apply to :equivalence rules. They add to the 'coarsenings ; property but no rule object exists. It should be noted that some congruence ; rules are added by lemmas of class :equivalence and those rules are named by ; :equivalence runes and are found among the 'congruences properties. ans) (forward-chaining-rules (if (eq token :forward-chaining) (collect-x-rules-of-rune 'forward-chaining-rule rune val ans) ans)) (type-prescriptions (if (eq token :type-prescription) (collect-x-rules-of-rune 'type-prescription rune val ans) ans)) (induction-rules (if (eq token :induction) (collect-x-rules-of-rune 'induction-rule rune val ans) ans)) (otherwise ans)))))) (defun find-rules-of-rune1 (rune props ans) ; Props is a list of triples and can be considered a segment of some wrld. (It ; is not only because duplicates have been removed.) We visit every property ; and collect all the rules with :rune rune. (cond ((null props) ans) ((eq (cddar props) *acl2-property-unbound*) (find-rules-of-rune1 rune (cdr props) ans)) (t (find-rules-of-rune1 rune (cdr props) (find-rules-of-rune2 rune (caar props) (cadar props) (cddar props) ans))))) (defun find-rules-of-rune (rune wrld) ":Doc-Section Miscellaneous find the rules named rune~/ ~bv[] General Form: (find-rules-of-rune rune wrld) ~ev[]~/ This function finds all the rules in ~c[wrld] with ~c[:]~ilc[rune] rune. It returns a list of rules in their internal form (generally as described by the corresponding ~c[defrec]). Decyphering these rules is difficult since one cannot always look at a rule object and decide what kind of record it is without exploiting many system invariants (e.g., that ~c[:]~ilc[rewrite] runes only name rewrite-rules). At the moment this function returns ~c[nil] if the rune in question is a ~c[:]~ilc[refinement] rune, because there is no object representing ~c[:]~ilc[refinement] rules. (~c[:]~ilc[refinement] rules cause changes in the ~c['coarsenings] properties.) In addition, if the rune is an ~c[:]~ilc[equivalence] rune, then congruence rules with that rune will be returned ~-[] because ~c[:]~ilc[equivalence] lemmas generate some congruence rules ~-[] but the fact that a certain function is now known to be an equivalence relation is not represented by any rule object and so no such rule is returned. (The fact that the function is an equivalence relation is encoded entirely in its presence as a ~c['coarsening] of ~ilc[equal].)" ; Find all the rules in wrld with :rune rune. We do this by first obtaining ; that segment of wrld consisting of the properties stored by the event ; named by the base symbol of rune. Then we collect every rule mentioned ; in the segment, provided the rule has :rune rune. (declare (xargs :guard (and (plist-worldp wrld) (runep rune wrld)))) (let ((wrld-tail (decode-logical-name (base-symbol rune) wrld))) (find-rules-of-rune1 rune (actual-props (world-to-next-event (cdr wrld-tail)) nil nil) nil))) (defun collect-non-backchain-subclass (rules) ; Rules is a list of REWRITE-RULEs. We collect all those that are not ; of :subclass 'backchain. (cond ((null rules) nil) ((eq (access rewrite-rule (car rules) :subclass) 'backchain) (collect-non-backchain-subclass (cdr rules))) (t (cons (car rules) (collect-non-backchain-subclass (cdr rules)))))) (defun chk-acceptable-monitor (rune expr ctx state) ; We check that rune is a breakable rune and expr is a suitable ; conditional expression. We either cause an error or return ; the translation of expr. (cond ((not (runep rune (w state))) (er soft ctx "~x0 is not a rune." rune)) ((not (member-eq (car rune) '(:rewrite :definition))) (er soft ctx "Only :REWRITE and :DEFINITION runes may be monitored. We cannot ~ break ~x0." rune)) (t (er-let* ((term (translate-break-condition expr ctx state))) (cond ((eq (car rune) :rewrite) ; The checks below can be extremely expensive when dealing with a :definition ; rule for a function that is part of a large mutual recursion nest. We have ; seen the call of actual-props in find-rules-of-rune take over a minute for a ; function defined in a mutual-recursion nest of several thousand functions. ; So we restrict the check to :rewrite rules. (let* ((rules (find-rules-of-rune rune (w state))) (bad-rewrite-rules (collect-non-backchain-subclass rules))) ; Observe that we collect all the non-backchain rules but then claim to the ; user that they are all abbreviation rules. That is because we believe that ; there are only four subclasses of rewrite rules: backchain, abbreviation, ; definition, and meta and the latter two have runes beginning with the tokens ; :definition and :meta instead of :rewrite. (pprogn (cond ((null rules) (prog2$ (er hard ctx "Implementation error (please contact the ACL2 ~ implementors): Although ~x0 is a runep, ~ find-rules-of-rune fails to find any rules for it." rune) state)) ((equal (length bad-rewrite-rules) (length rules)) (warning$ ctx "Monitor" "The rune ~x0 only names ~#1~[a simple ~ abbreviation rule~/~n2 simple abbreviation ~ rules~]. Monitors can be installed on ~ abbreviation rules, but will not fire during ~ preprocessing, so you may want to supply the hint ~ :DO-NOT '(PREPROCESS); see :DOC hints. For an ~ explanation of what a simple abbreviation rule ~ is, see :DOC simple. Also, see :DOC monitor." rune bad-rewrite-rules (length bad-rewrite-rules))) (bad-rewrite-rules (warning$ ctx "Monitor" "Among the ~n0 rules named ~x1 ~#2~[is a simple ~ abbreviation rule~/are ~n3 simple abbreviation ~ rules~]. Such rules can be monitored, but will ~ not fire during preprocessing, so you may want to ~ supply the hint :DO-NOT '(PREPROCESS); see :DOC ~ hints, For an explanation of what a simple ~ abbreviation rule is, see :DOC simple. Also, see ~ :DOC monitor." (length rules) rune bad-rewrite-rules (length bad-rewrite-rules))) (t state)) (value term)))) (t (value term))))))) (defun chk-acceptable-monitors (lst ctx state) ; We check that lst is an acceptable value for the brr-global ; 'brr-monitored-runes. We return the translation of lst or cause an ; error. (cond ((null lst) (value nil)) ((not (and (consp (car lst)) (consp (cdr (car lst))) (null (cddr (car lst))))) (er soft ctx "Every element of brr-monitored-runes must be a doublet of the ~ form (rune term) and ~x0 is not." (car lst))) (t (er-let* ((term (chk-acceptable-monitor (car (car lst)) (cadr (car lst)) ctx state)) (rlst (chk-acceptable-monitors (cdr lst) ctx state))) (value (cons (list (car (car lst)) term) rlst)))))) (defun monitor1 (rune form ctx state) ; The list of monitored runes modified by this function is a brr-global. ; Thus, this function should only be evaluated within a wormhole. The macro ; monitor can be called in either a wormhole state or a normal state. (er-let* ((term (chk-acceptable-monitor rune form ctx state))) (prog2$ (or (f-get-global 'gstackp state) (cw "Note: Enable break-rewrite with :brr t.~%")) (pprogn (f-put-global 'brr-monitored-runes (put-assoc-equal rune (list term) (get-brr-global 'brr-monitored-runes state)) state) (value (get-brr-global 'brr-monitored-runes state)))))) (defun unmonitor1 (rune ctx state) (cond ((assoc-equal rune (get-brr-global 'brr-monitored-runes state)) (pprogn (f-put-global 'brr-monitored-runes (remove1-equal (assoc-equal rune (get-brr-global 'brr-monitored-runes state)) (get-brr-global 'brr-monitored-runes state)) state) (prog2$ (cond ((and (f-get-global 'gstackp state) (null (get-brr-global 'brr-monitored-runes state))) (cw "Note: No runes are being monitored. Disable break-rewrite ~ with :brr nil.~%")) (t nil)) (value (get-brr-global 'brr-monitored-runes state))))) (t (er soft ctx "~x0 is not monitored." rune)))) (defun monitor-fn (rune expr state) ; If we are not in a wormhole, get into one. Then we set brr-monitored-runes ; appropriately. We always print the final value of brr-monitored-runes to the ; comment window and we always return (value :invisible). (cond ((eq (f-get-global 'wormhole-name state) 'brr) (er-progn (monitor1 rune expr 'monitor state) (prog2$ (cw "~Y01~|" (get-brr-global 'brr-monitored-runes state) nil) (value :invisible)))) (t (prog2$ (brr-wormhole '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) nil `(er-progn (monitor1 ',rune ',expr 'monitor state) (prog2$ (cw "~Y01~|" (get-brr-global 'brr-monitored-runes state) nil) (value nil))) nil) (value :invisible))))) (defun unmonitor-fn (rune ctx state) (cond ((eq (f-get-global 'wormhole-name state) 'brr) (er-progn (cond ((eq rune :all) (pprogn (f-put-global 'brr-monitored-runes nil state) (value nil))) ((and (consp rune) (keywordp (car rune))) (unmonitor1 rune ctx state)) (t (er soft ctx "The only legal arguments to UNMONITOR are runes and :ALL, but ~x0 is neither. See :DOC unmonitor ~ for a more precise explanation of the requirements." rune))) (prog2$ (cw "~Y01~|" (get-brr-global 'brr-monitored-runes state) nil) (value :invisible)))) (t (prog2$ (brr-wormhole '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) nil `(er-progn (cond ((eq ',rune :all) (pprogn (f-put-global 'brr-monitored-runes nil state) (value nil))) ((and (consp ',rune) (keywordp (car ',rune))) (unmonitor1 ',rune ',ctx state)) (t (er soft ',ctx "The only legal arguments to UNMONITOR are runes ~ and :ALL, but ~x0 is neither. See :DOC ~ unmonitor for a more precise explanation of the ~ requirements." ',rune))) (prog2$ (cw "~Y01~|" (get-brr-global 'brr-monitored-runes state) nil) (value nil))) nil) (value :invisible))))) (defun monitored-runes-fn (state) (cond ((eq (f-get-global 'wormhole-name state) 'brr) (prog2$ (cw "~Y01~|" (get-brr-global 'brr-monitored-runes state) nil) (value :invisible))) (t (prog2$ (brr-wormhole '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) nil `(prog2$ (cw "~Y01~|" (get-brr-global 'brr-monitored-runes state) nil) (value nil)) nil) (value :invisible))))) (defun brr-fn (flg state) (cond (flg (pprogn (f-put-global 'gstackp t state) (prog2$ (cw "Use :a! to exit break-rewrite.~|See :DOC set-evisc-tuple to ~ control suppression of details when printing.~|~%The monitored ~ runes are:~%") (er-progn (monitored-runes-fn state) (value t))))) (t (pprogn (f-put-global 'gstackp nil state) (value nil))))) (defmacro brr (flg) ":Doc-Section Break-Rewrite to enable or disable the breaking of rewrite rules~/ ~bv[] Example: :brr t ; enable :brr nil ; disable~/ General Form: (brr flg) ~ev[] where ~c[flg] evaluates to ~c[t] or ~c[nil]. This function modifies ~ilc[state] so that the attempted application of certain rewrite rules are ``broken.'' ``~c[Brr]'' stands for ``break-rewrite'' and can be thought of as a mode with two settings. The normal mode is ``disabled.'' When ~c[brr] mode is ``enabled'' the ACL2 rewriter monitors the attempts to apply certain rules and advises the user of those attempts by entering an interactive wormhole break. From within this break the user can watch selected application attempts. ~l[break-rewrite]. The user can also interact with the system during ~c[brr] breaks via ~ilc[brr-commands]. The rules monitored are selected by using the ~ilc[monitor] and ~ilc[unmonitor] commands. It is possible to break a rune ``conditionally'' in the sense that an interactive break will occur only if a specified predicate is true of the environment at the time of the attempted application. ~l[monitor] and ~pl[unmonitor]. Even if a non-empty set of rules has been selected, no breaks will occur unless ~c[brr] mode is enabled. Thus, the first time in a session that you wish to monitor a rewrite rule, use ~c[:brr] ~c[t] to enable ~c[brr] mode. Thereafter you may select runes to be monitored with ~ilc[monitor] and ~ilc[unmonitor] with the effect that whenever monitored rules are tried (and their break conditions are met) an interactive break will occur. Be advised that when ~c[brr] mode is enabled the rewriter is somewhat slower than normal. Furthermore, that sluggishness persists even if no runes are monitored. You may regain normal performance ~-[] regardless of what runes are monitored ~-[] by disabling ~c[brr] mode with ~c[:brr] ~c[nil]. Why isn't ~c[brr] mode disabled automatically when no runes are monitored? More generally, why does ACL2 have ~c[brr] mode at all? Why not just test whether there are monitored runes? If you care about the answers, ~pl[why-brr]. BRR Mode and Console Interrupts: If the system is operating in ~c[brr] mode and you break into raw Lisp (as by causing a console interrupt or happening upon a signalled Lisp error; ~pl[breaks]), you can return to the ACL2 top-level, outside any ~c[brr] environment, by executing ~c[(]~ilc[abort!]~c[)]. Otherwise, the normal way to quit from such a break (for example ~c[:q] in GCL, ~c[:reset] in Allegro CL, and ~c[q] in CMU CL) will return to the innermost ACL2 read-eval-print loop, which may or may not be the top-level of your ACL2 session! In particular, if the break happens to occur while ACL2 is within the ~c[brr] environment (in which it is preparing to read ~ilc[brr-commands]), the abort will merely return to that ~c[brr] environment. Upon exiting that environment, normal theorem proving is continued (and the ~c[brr] environment may be entered again in response to subsequent monitored rule applications). Before returning to the ~c[brr] environment, ACL2 ``cleans up'' from the interrupted ~c[brr] processing. However, it is not possible (given the current implementation) to clean up perfectly. This may have two side-effects. First, the system may occasionally print the self-explanatory ``Cryptic BRR Message 1'' (or 2), informing you that the system has attempted to recover from an aborted ~c[brr] environment. Second, it is possible that subsequent ~c[brr] behavior in that proof will be erroneous because the cleanup was done incorrectly. The moral is that you should not trust what you learn from ~c[brr] if you have interrupted and aborted ~c[brr] processing during the proof. These issues do not affect the behavior or soundness of the theorem prover." `(brr-fn ,flg state)) (deflabel why-brr :doc ":Doc-Section Miscellaneous an explanation of why ACL2 has an explicit ~ilc[brr] mode~/ Why isn't ~ilc[brr] mode automatically disabled when there are no monitored runes? The reason is that the list of monitored runes is kept in a wormhole state.~/ ~l[wormhole] for more information on wormholes in general. But the fundamental property of the wormhole function is that it is a logical ~c[no-op], a constant function that does not take state as an argument. When entering a wormhole, arbitrary information can be passed in (including the external state). That information is used to construct a near copy of the external state and that ``wormhole state'' is the one with respect to which interactions occur during breaks. But no information is carried by ACL2 out of a wormhole ~-[] if that were allowed wormholes would not be logical no-ops. The only information carried out of a wormhole is in the user's head. ~ilc[Break-rewrite] interacts with the user in a wormhole state because the signature of the ACL2 rewrite function does not permit it to modify ~ilc[state]. Hence, only wormhole interaction is possible. (This has the additional desirable property that the correctness of the rewriter does not depend on what the user does during interactive breaks within it; indeed, it is logically impossible for the user to affect the course of ~ilc[rewrite].) Now consider the list of monitored runes. Is that kept in the external state as a normal state global or is it kept in the wormhole state? If it is in the external state then it can be inspected within the wormhole but not changed. This is unacceptable; it is common to change the ~il[monitor]ed rules as the proof attempt progresses, installing monitors when certain rules are about to be used in certain contexts. Thus, the list of monitored runes must be kept as a wormhole variable. Hence, its value cannot be determined outside the wormhole, where the proof attempt is ongoing. This raises another question: If the list of monitored runes is unknown to the rewriter operating on the external state, how does the rewriter know when to break? The answer is simple: it breaks every time, for every rune, if ~ilc[brr] mode is enabled. The wormhole is entered (silently), computations are done within the wormhole state to determine if the user wants to see the break, and if so, interactions begin. For unmonitored runes and runes with false break conditions, the silent wormhole entry is followed by a silent wormhole exit and the user perceives no break. Thus, the penalty for running with ~ilc[brr] mode enabled when there are no monitored runes is high: a wormhole is entered on every application of every rune and the user is simply unware of it. The user who has finally unmonitored all runes is therefore strongly advised to carry this information out of the wormhole and to do ~c[:]~ilc[brr] ~c[nil] in the external state when the next opportunity arises.") (defmacro brr@ (sym) ":Doc-Section Break-Rewrite to access context sensitive information within ~ilc[break-rewrite]~/ ~bv[] Example: (brr@ :target) ; the term being rewritten (brr@ :unify-subst) ; the unifying substitution~/ General Form: (brr@ :symbol) ~ev[] where ~c[:symbol] is one of the following keywords. Those marked with ~c[*] probably require an implementor's knowledge of the system to use effectively. They are supported but not well documented. More is said on this topic following the table. ~bv[] :symbol (brr@ :symbol) ------- --------------------- :target the term to be rewritten. This term is an instantiation of the left-hand side of the conclusion of the rewrite-rule being broken. This term is in translated form! Thus, if you are expecting (equal x nil) -- and your expectation is almost right -- you will see (equal x 'nil); similarly, instead of (cadr a) you will see (car (cdr a)). In translated forms, all constants are quoted (even nil, t, strings and numbers) and all macros are expanded. :unify-subst the substitution that, when applied to :target, produces the left-hand side of the rule being broken. This substitution is an alist pairing variable symbols to translated (!) terms. :wonp t or nil indicating whether the rune was successfully applied. (brr@ :wonp) returns nil if evaluated before :EVALing the rule. :rewritten-rhs the result of successfully applying the rule or else nil if (brr@ :wonp) is nil. The result of successfully applying the rule is always a translated (!) term and is never nil. :failure-reason some non-nil lisp object indicating why the rule was not applied or else nil. Before the rule is :EVALed, (brr@ :failure-reason) is nil. After :EVALing the rule, (brr@ :failure-reason) is nil if (brr@ :wonp) is t. Rather than document the various non-nil objects returned as the failure reason, we encourage you simply to evaluate (brr@ :failure-reason) in the contexts of interest. Alternatively, study the ACL2 function tilde-@- failure-reason-phrase. :lemma * the rewrite rule being broken. For example, (access rewrite-rule (brr@ :lemma) :lhs) will return the left-hand side of the conclusion of the rule. :type-alist * a display of the type-alist governing :target. Elements on the displayed list are of the form (term type), where term is a term and type describes information about term assumed to hold in the current context. The type-alist may be used to determine the current assumptions, e.g., whether A is a CONSP. :ancestors * a stack of frames indicating the backchain history of the current context. The theorem prover is in the process of trying to establish each hypothesis in this stack. Thus, the negation of each hypothesis can be assumed false. Each frame also records the rules on behalf of which this backchaining is being done and the weight (function symbol count) of the hypothesis. All three items are involved in the heuristic for preventing infinite backchaining. Exception: Some frames are ``binding hypotheses'' (equal var term) or (equiv var (double-rewrite term)) that bind variable var to the result of rewriting term. :gstack * the current goal stack. The gstack is maintained by rewrite and is the data structure printed as the current ``path.'' Thus, any information derivable from the :path brr command is derivable from gstack. For example, from gstack one might determine that the current term is the second hypothesis of a certain rewrite rule. ~ev[] In general ~c[brr@-expressions] are used in break conditions, the expressions that determine whether interactive breaks occur when ~il[monitor]ed ~il[rune]s are applied. ~l[monitor]. For example, you might want to break only those attempts in which one particular term is being rewritten or only those attempts in which the binding for the variable ~c[a] is known to be a ~ilc[consp]. Such conditions can be expressed using ACL2 system functions and the information provided by ~c[brr@]. Unfortunately, digging some of this information out of the internal data structures may be awkward or may, at least, require intimate knowledge of the system functions. But since conditional expressions may employ arbitrary functions and macros, we anticipate that a set of convenient primitives will gradually evolve within the ACL2 community. It is to encourage this evolution that ~c[brr@] provides access to the ~c[*]'d data." (declare (xargs :guard (member-eq sym '(:target :unify-subst :wonp :rewritten-rhs :failure-reason :lemma :type-alist :ancestors :gstack)))) (case sym (:target '(get-brr-local 'target state)) (:unify-subst '(get-brr-local 'unify-subst state)) (:wonp '(get-brr-local 'wonp state)) (:rewritten-rhs '(get-brr-local 'rewritten-rhs state)) (:failure-reason '(get-brr-local 'failure-reason state)) (:lemma '(get-brr-local 'lemma state)) (:type-alist '(get-brr-local 'type-alist state)) (:ancestors '(get-brr-local 'ancestors state)) (otherwise '(get-brr-global 'brr-gstack state)))) (defmacro monitor (rune expr) ":Doc-Section Break-Rewrite to monitor the attempted application of a rule name~/ ~bv[] Example: (monitor '(:rewrite assoc-of-app) 't) :monitor (:rewrite assoc-of-app) t :monitor (:definition app) (equal (brr@ :target) '(app c d))~/ General Form: (monitor rune term) ~ev[] where ~c[rune] is a ~il[rune] and ~c[term] is a term, called the ``break condition.'' ~c[Rune] must be either a ~c[:rewrite] ~il[rune] or a ~c[:definition] ~il[rune]. When a ~il[rune] is ~il[monitor]ed any attempt to apply it may result in an interactive break in an ACL2 ``~il[wormhole] ~il[state].'' There you will get a chance to see how the application proceeds. ~l[break-rewrite] for a description of the interactive loop entered. Whether an interactive break occurs depends on the value of the break condition expression associated with the ~il[monitor]ed ~il[rune]. NOTE: Some ~c[:rewrite] rules are considered ``simple abbreviations''; ~pl[simple]. These can be be monitored, but only at certain times during the proof. Monitoring is carried out by code inside the rewriter but abbreviation rules may be applied by a special purpose simplifier inside the so-called ~em[preprocess] phase of a proof. If you desire to monitor an abbreviation rule, a warning will be printed suggesting that you may want to supply the hint ~c[:DO-NOT '(PREPROCESS)]; ~pl[hints]. Without such a hint, an abbreviation rule can be applied during the preprocess phase of a proof, and no such application will cause an interactive break. To remove a ~il[rune] from the list of ~il[monitor]ed ~il[rune]s, use ~c[unmonitor]. To see which ~il[rune]s are ~il[monitor]ed and what their break conditions are, evaluate ~c[(monitored-runes)]. ~c[Monitor], ~c[unmonitor] and ~c[monitored-runes] are macros that expand into expressions involving ~c[state]. While these macros appear to return the list of ~il[monitor]ed ~il[rune]s this is an illusion. They all print ~il[monitor]ed ~il[rune] information to the comment window and then return error triples (~pl[error-triples]) instructing ~c[ld] to print nothing. It is impossible to return the list of ~il[monitor]ed ~il[rune]s because it exists only in the ~il[wormhole] ~il[state] with which you interact when a break occurs. This allows you to change the ~il[monitor]ed ~il[rune]s and their conditions during the course of a proof attempt without changing the ~il[state] in which the the proof is being constructed. Unconditional break points are obtained by using the break condition ~c[t]. We now discuss conditional break points. The break condition, ~c[expr], must be a term that contains no free variables other than ~c[state] and that returns a single non-~c[state] result. In fact, the result should be ~c[nil], ~c[t], or a true list of commands to be fed to the resulting interactive break. Whenever the system attempts to use the associated rule, ~c[expr] is evaluated in the ~il[wormhole] interaction ~il[state]. A break occurs only if the result of evaluating ~c[expr] is non-~c[nil]. If the result is a true list, that list is appended to the front of ~c[standard-oi] and hence is taken as the initial user commands issued to the interactive break. In order to develop effective break conditions it must be possible to access context sensitive information, i.e., information about the context in which the ~il[monitor]ed ~il[rune] is being tried. The ~c[brr@] macro may be used in break conditions to access such information as the term being rewritten and the current governing assumptions. This information is not stored in the proof ~il[state] but is transferred into the ~il[wormhole] ~il[state] when breaks occur. The macro form is ~c[(brr@ :sym)] where ~c[:sym] is one of several keyword symbols, including ~c[:target] (the term being rewritten), ~c[:unify-subst] (the substitution that instantiates the left-hand side of the conclusion of the rule so that it is the target term), and ~c[:type-alist] (the governing assumptions). ~l[brr@]. For example, ~bv[] ACL2 !>:monitor (:rewrite assoc-of-app) (equal (brr@ :target) '(app a (app b c))) ~ev[] will monitor ~c[(:rewrite assoc-of-app)] but will cause an interactive break only when the target term, the term being rewritten, is ~c[(app a (app b c))]. Because break conditions are evaluated in the interaction environment, the user developing a break condition for a given ~il[rune] can test candidate break conditions before installing them. For example, suppose an unconditional break has been installed on a ~il[rune], that an interactive break has occurred and that the user has determined both that this particular application is uninteresting and that many more such applications will likely occur. An appropriate response would be to develop an expression that recognizes such applications and returns ~c[nil]. Of course, the hard task is figuring out what makes the current application uninteresting. But once a candidate expression is developed, the user can evaluate it in the current context simply to confirm that it returns ~c[nil]. Recall that when a break condition returns a non-~c[nil] true list that list is appended to the front of ~c[standard-oi]. For example, ~bv[] ACL2 !>:monitor (:rewrite assoc-of-app) '(:go) ~ev[] will cause ~c[(:rewrite assoc-of-app)] to be ~il[monitor]ed and will make the break condition be ~c['(:go)]. This break condition always evaluates the non-~c[nil] true list ~c[(:go)]. Thus, an interactive break will occur every time ~c[(:rewrite assoc-of-app)] is tried. The break is fed the command ~c[:go]. Now the command ~c[:go] causes ~c[break-rewrite] to (a) evaluate the attempt to apply the lemma, (b) print the result of that attempt, and (c) exit from the interactive break and let the proof attempt continue. Thus, in effect, the above ~c[:monitor] merely ``traces'' the attempted applications of the ~il[rune] but never causes an interactive break requiring input from the user. It is possible to use this feature to cause a conditional break where the effective break condition is tested ~st[after] the lemma has been tried. For example: ~bv[] ACL2 !>:monitor (:rewrite lemma12) '(:unify-subst :eval$ nil :ok-if (or (not (brr@ :wonp)) (not (equal (brr@ :rewritten-rhs) '(foo a)))) :rewritten-rhs) ~ev[] causes the following behavior when ~c[(:rewrite lemma12)] is tried. A break always occurs, but it is fed the commands above. The first, ~c[:unify-subst], causes ~c[break-rewrite] to print out the unifying substitution. Then in response to ~c[:eval$] ~c[nil] the lemma is tried but with all ~il[rune]s temporarily ~il[unmonitor]ed. Thus no breaks will occur during the rewriting of the hypotheses of the lemma. When the attempt has been made, control returns to ~c[break-rewrite] (which will print the results of the attempt, i.e., whether the lemma was applied, if so what the result is, if not why it failed). The next command, the ~c[:ok-if] with its following expression, is a conditional exit command. It means exit ~c[break-rewrite] if either the attempt was unsuccessful, ~c[(not (brr@ :wonp))], or if the result of the rewrite is any term other than ~c[(foo a)]. If this condition is met, the break is exited and the remaining break commands are irrelevant. If this condition is not met then the next command, ~c[:rewritten-rhs], prints the result of the application (which in this contrived example is known to be ~c[(foo a)]). Finally, the list of supplied commands is exhausted but ~c[break-rewrite] expects more input. Therefore, it begins prompting the user for input. The end result, then, of the above ~c[:monitor] command is that the ~il[rune] in question is elaborately traced and interactive breaks occur whenever it rewrites its target to ~c[(foo a)]. We recognize that the above break condition is fairly arcane. We suspect that with experience we will develop some useful idioms. For example, it is straightforward now to define macros that monitor ~il[rune]s in the ways suggested by the following names: ~c[trace-rune], ~c[break-if-target-is], and ~c[break-if-result-is]. For example, the last could be defined as ~bv[] (defmacro break-if-result-is (rune term) `(monitor ',rune '(quote (:eval :ok-if (not (equal (brr@ :rewritten-rhs) ',term)))))) ~ev[] (Note however that the submitted term must be in translated form.) Since we don't have any experience with this kind of control on lemmas we thought it best to provide a general (if arcane) mechanism and hope that the ACL2 community will develop the special cases that we find most convenient." `(monitor-fn ,rune ,expr state)) (defmacro unmonitor (rune) ":Doc-Section Break-Rewrite to stop monitoring a rule name~/ ~bv[] Examples: (unmonitor '(:rewrite assoc-of-app)) :unmonitor (:rewrite assoc-of-app) :unmonitor :all~/ General Forms: (unmonitor rune) (unmonitor :all) ~ev[] Here, ~c[rune] is a ~il[rune] that is currently among those with break points installed. This function removes the break. Subtle point: Because you may want to unmonitor a ``~il[rune]'' that is no longer a ~il[rune] in the current ACL2 ~il[world], we don't actually check this about ~il[rune]. Instead, we simply check that ~il[rune] is a ~c[consp] beginning with a ~c[keywordp]. That way, you'll know you've made a mistake if you try to ~c[:unmonitor binary-append] instead of ~c[:unmonitor (:definition binary-append)], for example." `(unmonitor-fn ,rune 'unmonitor state)) (defmacro monitored-runes () ":Doc-Section Break-Rewrite print the ~il[monitor]ed ~il[rune]s and their break conditions~/ ~bv[] Example and General Form: :monitored-runes ~ev[]~/ This macro prints a list, each element of which is of the form ~c[(rune expr)], showing each ~il[monitor]ed ~il[rune] and its current break condition." `(monitored-runes-fn state)) (defun proceed-from-brkpt1 (action runes ctx state) ; Action may be ; silent - exit brr with no output except the closing parenthesis ; print - exit brr after printing results of attempted application ; break - do not exit brr ; Runes is allegedly either t or a list of runes to be used as brr-monitored-runes ; after pairing every rune with *t*. If it is t, it means use the same ; brr-monitored-runes. Otherwise, we check that they are all legal. If not, we ; warn and do not exit. We may wish someday to provide the capability of ; proceeding with conditions other than *t* on the various runes, but I haven't ; seen a nice design for that yet. (er-let* ((lst (if (eq runes t) (value nil) (chk-acceptable-monitors (pairlis-x2 runes (list *t*)) ctx state)))) (pprogn (put-brr-local 'saved-standard-oi (f-get-global 'standard-oi state) state) (put-brr-local 'saved-brr-monitored-runes (get-brr-global 'brr-monitored-runes state) state) (if (eq runes t) state (f-put-global 'brr-monitored-runes lst state)) (put-brr-local 'action action state) (exit-brr-wormhole state)))) (defun exit-brr (state) ; The assoc-eq on 'wonp below determines if we are in brkpt2 or brkpt1. (cond ((assoc-eq 'wonp (get-brr-global 'brr-alist state)) (prog2$ (cw "~F0)~%" (get-brr-local 'depth state)) (pprogn (pop-brr-stack-frame state) (exit-brr-wormhole state)))) (t (proceed-from-brkpt1 'silent t 'exit-brr state)))) (defun ok-if-fn (term state) (er-let* ((pair (simple-translate-and-eval term nil '(state) "The ok-if test" 'ok-if (w state) state t))) (cond ((cdr pair) (exit-brr state)) (t (value nil))))) (defmacro ok-if (term) ":Doc-Section Break-Rewrite conditional exit from ~c[break-rewrite]~/ ~bv[] Example Form: :ok-if (null (brr@ :wonp))~/ General Form: :ok-if expr ~ev[] where ~c[expr] is a term involving no free variables other than ~c[state] and returning one non-~c[state] result which is treated as Boolean. This form is intended to be executed from within ~c[break-rewrite] (~pl[break-rewrite]). Consider first the simple situation that the ~c[(ok-if term)] is a command read by ~c[break-rewrite]. Then, if the term is non-~c[nil], ~c[break-rewrite] exits and otherwise it does not. More generally, ~c[ok-if] returns an ACL2 error triple ~c[(mv erp val state)]. (~l[ld] or ~pl[programming-with-state] for more on error triples.) If any form being evaluated as a command by ~c[break-rewrite] returns the triple returned by ~c[(ok-if term)] then the effect of that form is to exit ~il[break-rewrite] if term is non-~c[nil]. Thus, one might define a function or macro that returns the value of ~c[ok-if] expressions on all outputs and thus create a convenient new way to exit ~c[break-rewrite]. The exit test, ~c[term], generally uses ~c[brr@] to access context sensitive information about the attempted rule application. ~l[brr@]. ~c[Ok-if] is useful inside of command sequences produced by break conditions. ~l[monitor]. ~c[:ok-if] is most useful after an ~c[:eval] command has caused ~c[break-rewrite] to try to apply the rule because in the resulting break environment ~c[expr] can access such things as whether the rule succeeded, if so, what term it produced, and if not, why. There is no need to use ~c[:ok-if] before ~c[:eval]ing the rule since the same effects could be achieved with the break condition on the rule itself. Perhaps we should replace this concept with ~c[:eval-and-break-if]? Time will tell." `(ok-if-fn ,term state)) ;--------------------------------------------------------------------------- ; Section: The DEFAXIOM Event (defun print-rule-storage-dependencies (name ttree state) (cond ((ld-skip-proofsp state) (value nil)) (t (pprogn (io? event nil state (name ttree) (let ((simp-phrase (tilde-*-simp-phrase ttree))) (cond ((nth 4 simp-phrase) (fms "The storage of ~x0 depends upon ~*1.~%" (list (cons #\0 name) (cons #\1 simp-phrase)) (proofs-co state) state nil)) (t state)))) (value nil))))) (defun defaxiom-supporters (tterm name ctx wrld state) ; Here we document requirements on disjointness of the sets of evaluator ; functions and defaxiom supporters. ; First, consider the following comment from relevant-constraints (which should ; be kept in sync with that comment), regarding functional instantiation of a ; theorem, thm, using a functional substitution, alist. ; The relevant theorems are the set of all terms, term, such that ; (a) term mentions some function symbol in the domain of alist, ; AND ; (b) either ; (i) term arises from a definition of or constraint on a function symbol ; ancestral either in thm or in some defaxiom, ; OR ; (ii) term is the body of a defaxiom. ; Then when we (conceptually at least) functionally instantiate a :meta or ; :clause-processor rule using a functional substitution of the form ((evl ; evl') (evl-list evl'-list)), we need to know that the above proof obligations ; are met. ; ACL2 insists (in function chk-evaluator-use-in-rule) that the evaluator of a ; proposed :meta or :clause-processor rule is not ancestral in any defaxiom or ; in the definition of, or constraint on, the rule's metafunctions, nor is the ; evaluator ancestral in meta-extract-global-fact+ and ; meta-extract-contextual-fact if they are used in the rule. Thus, when we ; imagine functionally instantiating the rule as discussed above, at the point ; of its application, the only relevant theorems for (i) above are the ; constraints on the evaluator, and there are no relevant theorems for (ii) ; above. We can use our usual computation of "ancestral", which does not ; explore below functions that are not instantiablep, since (presumably!) ; non-instantiablep functions are primitives in which no evaluator functions is ; ancestral. ; But there is a subtlety not fully addressed above. Consider the following ; case: a legitimate :meta (or :clause-processor) rule, with evaluator evl, is ; followed by a defaxiom event for which evl (or evl-list) is ancestral. Does ; this new defaxiom invalidate the existing rule? The answer is no, but the ; argument above doesn't quite explain why, so we elaborate here. Let C0 be ; the chronology in which the meta rule was proved and let C1 be the current ; chronology, which extends C0. Let C2 be the result of extending C0 with a ; defstub for every function symbol of C1 that is not in C0, except for the ; evaluator pair evl'/evl'-list, introduced at the end for all function symbols ; of C1. Then the argument applies to C2, so the desired functional instance ; is a theorem of C2. But the theory of C2 is a subtheory of C1, so the ; desired functional instance is a theorem of C1. (declare (ignore name ctx)) (let ((supporters (instantiable-ancestors (all-fnnames tterm) wrld nil))) (value supporters))) (defun defaxiom-fn (name term state rule-classes doc event-form) ; Important Note: Don't change the formals of this function without reading the ; *initial-event-defmacros* discussion in axioms.lisp. (when-logic "DEFAXIOM" (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defaxiom name)) (let ((wrld (w state)) (ens (ens state)) (event-form (or event-form (list* 'defaxiom name term (append (if (not (equal rule-classes '(:REWRITE))) (list :rule-classes rule-classes) nil) (if doc (list :doc doc) nil)))))) (er-progn (chk-all-but-new-name name ctx nil wrld state) (er-let* ((tterm (translate term t t t ctx wrld state)) ; known-stobjs = t (stobjs-out = t) (supporters (defaxiom-supporters tterm name ctx wrld state)) (classes (translate-rule-classes name rule-classes tterm ctx ens wrld state))) (cond ((redundant-theoremp name tterm classes wrld) (stop-redundant-event ctx state)) (t ; Next we implement Defaxiom Restriction for Defattach from The Essay on ; Defattach: no ancestor (according to the transitive closure of the ; immediate-supporter relation) of a defaxiom event has an attachment. Since ; this is all about logic, we remove guard-holders from term before doing this ; check. (let ((attached-fns (attached-fns (canonical-ancestors-lst (all-ffn-symbs (remove-guard-holders tterm) nil) wrld) wrld))) (cond (attached-fns (er soft ctx "The following function~#0~[ has an attachment, but is~/s ~ have attachments, but are~] ancestral in the proposed ~ axiom: ~&0. ~ See :DOC defattach." attached-fns)) (t (enforce-redundancy event-form ctx wrld (er-let* ((ttree1 (chk-acceptable-rules name classes ctx ens wrld state)) (wrld1 (chk-just-new-name name 'theorem nil ctx wrld state)) (doc-pair (translate-doc name doc ctx state)) (ttree3 (cond ((ld-skip-proofsp state) (value nil)) (t (prove-corollaries name tterm classes ens wrld1 ctx state))))) (let* ((wrld2 (update-doc-database name doc doc-pair (add-rules name classes tterm term ens wrld1 state))) (wrld3 (global-set 'nonconstructive-axiom-names (cons name (global-val 'nonconstructive-axiom-names wrld)) wrld2)) (wrld4 (maybe-putprop-lst supporters 'defaxiom-supporter name wrld3)) (ttree4 (cons-tag-trees ttree1 ttree3))) (pprogn (f-put-global 'axiomsp t state) (er-progn (chk-assumption-free-ttree ttree4 ctx state) (print-rule-storage-dependencies name ttree1 state) (install-event name event-form 'defaxiom name ttree4 nil :protect ctx wrld4 state))))))))))))))))) ;--------------------------------------------------------------------------- ; Section: The DEFTHM Event (defun warn-on-inappropriate-defun-mode (assumep event-form ctx state) (if (or assumep (eq (default-defun-mode (w state)) :logic)) state (warning$ ctx "Defun-Mode" "It is perhaps unusual to execute the event ~x0 in the ~ :program default-defun-mode when ld-skip-proofsp has not been ~ set to T." event-form))) ;; RAG - this trio of functions adds the hypothesis "(standardp x)" ;; for each variable x in the theorem. #+:non-standard-analysis (defun add-hyp-standardp-var-lst (vars) (if (consp vars) (cons (list 'standardp (car vars)) (add-hyp-standardp-var-lst (cdr vars))) nil)) #+:non-standard-analysis (defun strengthen-hyps-using-transfer-principle (hyps vars) ; Hyps is an untranslated expression. (cons 'and (append (add-hyp-standardp-var-lst vars) (if (and (consp hyps) (eq (car hyps) 'and)) (cdr hyps) (list hyps))))) #+:non-standard-analysis (defun weaken-using-transfer-principle (term) ; Term is an untranslated expression. (let ((vars (all-vars term))) (case-match term (('implies hyps ('standardp subterm)) (declare (ignore subterm)) (list 'implies hyps (cons 'and (add-hyp-standardp-var-lst vars)))) (('standardp subterm) (declare (ignore subterm)) (cons 'and (add-hyp-standardp-var-lst vars))) (('implies hyps concls) (list 'implies (strengthen-hyps-using-transfer-principle hyps vars) concls)) (& (list 'implies (cons 'and (add-hyp-standardp-var-lst vars)) term))))) #+:non-standard-analysis (defun remove-standardp-hyp (tterm) (if (and (consp tterm) (eq (car tterm) 'standardp) (variablep (car (cdr tterm)))) (list 'eq (car (cdr tterm)) (car (cdr tterm))) tterm)) #+:non-standard-analysis (defun remove-standardp-hyps (tterm) (if (and (consp tterm) (eq (car tterm) 'if) (equal (car (cdr (cdr (cdr tterm)))) (list 'quote nil))) (list 'if (remove-standardp-hyp (car (cdr tterm))) (remove-standardp-hyps (car (cdr (cdr tterm)))) (list 'quote nil)) (remove-standardp-hyp tterm))) #+:non-standard-analysis (defun remove-standardp-hyps-and-standardp-conclusion (tterm) (case-match tterm (('implies hyps ('standardp subterm)) (list 'implies (remove-standardp-hyps hyps) subterm)) (('standardp subterm) subterm) (& tterm))) #+:non-standard-analysis (defun chk-classical-term-or-standardp-of-classical-term (tterm term ctx wrld state) ; Tterm is the translation of term. (let* ((names (all-fnnames (remove-standardp-hyps-and-standardp-conclusion tterm))) (non-classical-fns (get-non-classical-fns-from-list names wrld nil))) (if (null non-classical-fns) (value nil) (er soft ctx "It is illegal to use DEFTHM-STD to prove non-classical ~ formulas. However, there has been an attempt to prove ~ the formula ~x0 using DEFTHM-STD, even though it ~ contains the non-classical function ~*1." term `("" "~x*" "~x* and " "~x*," ,non-classical-fns))))) #+(and acl2-par (not acl2-loop-only)) (defmacro with-waterfall-parallelism-timings (name form) `(unwind-protect-disable-interrupts-during-cleanup (progn (setup-waterfall-parallelism-ht-for-name ,name) (reset-future-queue-length-history) (setf *acl2p-starting-proof-time* (get-internal-real-time)) ,form) (clear-current-waterfall-parallelism-ht))) #-(and acl2-par (not acl2-loop-only)) (defmacro with-waterfall-parallelism-timings (name form) (declare (ignore name)) form) (defun defthm-fn1 (name term state rule-classes instructions hints otf-flg doc event-form #+:non-standard-analysis std-p) (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defthm name)) (let ((wrld (w state)) (ens (ens state)) (event-form (or event-form (list* 'defthm name term (append (if (not (equal rule-classes '(:REWRITE))) (list :rule-classes rule-classes) nil) (if instructions (list :instructions instructions) nil) (if hints (list :hints hints) nil) (if otf-flg (list :otf-flg otf-flg) nil) (if doc (list :doc doc) nil))))) (ld-skip-proofsp (ld-skip-proofsp state))) (pprogn (warn-on-inappropriate-defun-mode ld-skip-proofsp event-form ctx state) #+acl2-par (erase-acl2p-checkpoints-for-summary state) (with-waterfall-parallelism-timings name (er-progn (chk-all-but-new-name name ctx nil wrld state) (er-let* ((tterm0 (translate term t t t ctx wrld state)) ; known-stobjs = t (stobjs-out = t) (tterm #+:non-standard-analysis (if std-p (er-progn (chk-classical-term-or-standardp-of-classical-term tterm0 term ctx wrld state) (translate (weaken-using-transfer-principle term) t t t ctx wrld state)) (value tterm0)) #-:non-standard-analysis (value tterm0)) (classes ; (#+:non-standard-analysis) We compute rule classes with respect to the ; original (translated) term. The modified term is only relevant for proof. (translate-rule-classes name rule-classes tterm0 ctx ens wrld state))) (cond ((redundant-theoremp name tterm0 classes wrld) (stop-redundant-event ctx state)) (t (enforce-redundancy event-form ctx wrld (er-let* ((ttree1 (chk-acceptable-rules name classes ctx ens wrld state)) (wrld1 (chk-just-new-name name 'theorem nil ctx wrld state))) (er-let* ((instructions (if (or (eq ld-skip-proofsp 'include-book) (eq ld-skip-proofsp 'include-book-with-locals) (eq ld-skip-proofsp 'initialize-acl2)) (value nil) (translate-instructions name instructions ctx wrld1 state))) ; Observe that we do not translate the hints if ld-skip-proofsp is non-nil. ; Once upon a time we translated the hints unless ld-skip-proofsp was ; 'include-book, which meant we translated them if it was t, which meant we did ; it during initialize-acl2. That caused a failure due to the fact that ENABLE ; was not defined when it was first used in axioms.lisp. This choice is ; a little unsettling because it means (hints (if (or (eq ld-skip-proofsp 'include-book) (eq ld-skip-proofsp 'include-book-with-locals) (eq ld-skip-proofsp 'initialize-acl2)) (value nil) (translate-hints+ name hints ; If there are :instructions, then default hints are to be ignored; otherwise ; the error just below will prevent :instructions in the presence of default ; hints. (and (null instructions) (default-hints wrld1)) ctx wrld1 state))) (doc-pair (translate-doc name doc ctx state)) (ttree2 (cond (instructions (er-progn (cond (hints (er soft ctx "It is not permitted to ~ supply both :INSTRUCTIONS ~ and :HINTS to DEFTHM.")) (t (value nil))) #+:non-standard-analysis (if std-p ; How could this happen? Presumably the user created a defthm event using the ; proof-checker, and then absent-mindedly somehow suffixed "-std" on to the ; car, defthm, of that form. (er soft ctx ":INSTRUCTIONS are not supported for ~ defthm-std events.") (value nil)) (proof-checker name term tterm classes instructions wrld1 state))) (t (prove tterm (make-pspv ens wrld1 :displayed-goal term :otf-flg otf-flg) hints ens wrld1 ctx state)))) (ttree3 (cond (ld-skip-proofsp (value nil)) (t (prove-corollaries name tterm0 classes ens wrld1 ctx state))))) (let ((wrld2 (update-doc-database name doc doc-pair (add-rules name classes tterm0 term ens wrld1 state))) (ttree4 (cons-tag-trees ttree1 (cons-tag-trees ttree2 ttree3)))) (er-progn (chk-assumption-free-ttree ttree4 ctx state) (print-rule-storage-dependencies name ttree1 state) (install-event name event-form 'defthm name ttree4 nil :protect ctx wrld2 state))))))))))))))) (defun defthm-fn (name term state rule-classes instructions hints otf-flg doc event-form #+:non-standard-analysis std-p) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. (when-logic "DEFTHM" (defthm-fn1 name term state rule-classes instructions hints otf-flg doc event-form #+:non-standard-analysis std-p))) (defmacro thm (term &key hints otf-flg doc) ":Doc-Section Other prove a theorem~/ ~bv[] Example: (thm (equal (app (app a b) c) (app a (app b c)))) ~ev[] Also ~pl[defthm]. Unlike ~ilc[defthm], ~c[thm] does not create an event; it merely causes the theorem prover to attempt a proof.~/ ~bv[] General Form: (thm term :hints hints :otf-flg otf-flg :doc doc-string) ~ev[] where ~c[term] is a term alleged to be a theorem, and ~ilc[hints], ~ilc[otf-flg] and ~ilc[doc-string] are as described in the corresponding ~c[:]~ilc[doc] entries. The three keyword arguments above are all optional.~/" (list 'thm-fn (list 'quote term) 'state (list 'quote hints) (list 'quote otf-flg) (list 'quote doc))) (defun thm-fn (term state hints otf-flg doc) (er-progn (with-ctx-summarized (if (output-in-infixp state) (list* 'THM term (if (or hints otf-flg doc) '(irrelevant) nil)) "( THM ...)") (let ((wrld (w state)) (ens (ens state))) (er-let* ((hints (translate-hints+ 'thm hints (default-hints wrld) ctx wrld state)) (doc-pair (translate-doc nil doc ctx state))) (er-let* ((tterm (translate term t t t ctx wrld state)) ; known-stobjs = t (stobjs-out = t) (ttree (prove tterm (make-pspv ens wrld :displayed-goal term :otf-flg otf-flg) hints ens wrld ctx state))) (value nil))))) (pprogn (io? prove nil state nil (fms "Proof succeeded.~%" nil (proofs-co state) state nil)) (value :invisible)))) ; Note: During boot-strapping the thm macro is unavailable because it is ; not one of the *initial-event-defmacros*. ;--------------------------------------------------------------------------- ; Section: Some Convenient Abbreviations for Defthm (defun chk-extensible-rule-classes (rule-classes ctx state) (cond ((or (symbolp rule-classes) (true-listp rule-classes)) (value nil)) (t (er soft ctx "The :rule-classes argument to must be either ~ a symbol or a true-listp. Your rule-classes is ~x0." rule-classes)))) (defun extend-rule-classes (class rule-classes) (cond ((symbolp rule-classes) (cond ((null rule-classes) class) ((eq rule-classes class) rule-classes) (t (list class rule-classes)))) ((member-eq class rule-classes) rule-classes) (t (cons class rule-classes)))) (defun gen-new-name-in-package-of-symbol1 (char-lst cnt pkgsym wrld) ; This function generates a symbol in the same package as pkgsym that ; is guaranteed to be a new-namep in wrld. We form a symbol by ; concatenating char-lst and the decimal representation of the natural ; number cnt (separated by a hyphen). Clearly, for some sufficiently ; large cnt that symbol is a new name. (let ((sym (intern-in-package-of-symbol (coerce (append char-lst (cons #\- (explode-nonnegative-integer cnt 10 nil))) 'string) pkgsym))) (cond ((new-namep sym wrld) sym) (t (gen-new-name-in-package-of-symbol1 char-lst (1+ cnt) pkgsym wrld))))) (defun gen-new-name-in-package-of-symbol (sym pkgsym wrld) ; We generate a symbol, sym', in the same package as pkgsym, such that ; (new-namep sym' wrld). If sym itself will not do, we start trying ; the extension of sym with successive integers, e.g., sym-0, sym-1, ; sym-2, ... (let ((sym1 (if (equal (symbol-package-name sym) (symbol-package-name pkgsym)) sym (intern-in-package-of-symbol (symbol-name sym) pkgsym)))) (cond ((new-namep sym1 wrld) sym1) (t (gen-new-name-in-package-of-symbol1 (coerce (symbol-name sym) 'list) 0 pkgsym wrld))))) (defmacro defequiv (equiv &key (rule-classes '(:EQUIVALENCE)) instructions hints otf-flg event-name doc) ":Doc-Section Events prove that a function is an ~il[equivalence] relation~/ ~bv[] Example: (defequiv set-equal) is an abbreviation for (defthm set-equal-is-an-equivalence (and (booleanp (set-equal x y)) (set-equal x x) (implies (set-equal x y) (set-equal y x)) (implies (and (set-equal x y) (set-equal y z)) (set-equal x z))) :rule-classes (:equivalence)) ~ev[] ~l[equivalence].~/ ~bv[] General Form: (defequiv fn :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :event-name event-name :doc doc) ~ev[] where ~c[fn] is a function symbol of arity 2, ~c[event-name], if supplied, is a symbol, and all other arguments are as specified in the documentation for ~ilc[defthm]. The ~c[defequiv] macro expands into a call of ~c[defthm]. The name of the ~c[defthm] is ~c[fn-is-an-equivalence], unless ~c[event-name] is supplied, in which case ~c[event-name] is the name used. The term generated for the ~c[defthm] event states that ~c[fn] is Boolean, reflexive, symmetric, and transitive. The rule-class ~c[:equivalence] is added to the ~il[rule-classes] specified, if it is not already there. All other arguments to the generated ~c[defthm] form are as specified by the other keyword arguments above." `(defthm ,(or event-name (intern-in-package-of-symbol (coerce (packn1 (list equiv "-IS-AN-EQUIVALENCE")) 'string) equiv)) ,(equivalence-relation-condition equiv) :rule-classes ,(extend-rule-classes :equivalence rule-classes) ,@(if instructions (list :instructions instructions) nil) ,@(if hints (list :hints hints) nil) ,@(if otf-flg (list :otf-flg otf-flg) nil) ,@(if doc (list :doc doc) nil))) (defmacro defrefinement (equiv1 equiv2 &key (rule-classes '(:REFINEMENT)) instructions hints otf-flg event-name doc) ":Doc-Section Events prove that ~c[equiv1] refines ~c[equiv2]~/ ~bv[] Example: (defrefinement equiv1 equiv2) is an abbreviation for (defthm equiv1-refines-equiv2 (implies (equiv1 x y) (equiv2 x y)) :rule-classes (:refinement)) ~ev[] ~l[refinement].~/ ~bv[] General Form: (defrefinement equiv1 equiv2 :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :event-name event-name :doc doc) ~ev[] where ~c[equiv1] and ~c[equiv2] are known ~il[equivalence] relations, ~c[event-name], if supplied, is a symbol and all other arguments are as specified in the documentation for ~ilc[defthm]. The ~c[defrefinement] macro expands into a call of ~c[defthm]. The name supplied is ~c[equiv1-refines-equiv2], unless ~c[event-name] is supplied, in which case it is used as the name. The term supplied states that ~c[equiv1] refines ~c[equiv2]. The rule-class ~c[:refinement] is added to the ~c[rule-classes] specified, if it is not already there. All other arguments to the generated ~c[defthm] form are as specified by the other keyword arguments above." `(defthm ,(or event-name (intern-in-package-of-symbol (coerce (packn1 (list equiv1 "-REFINES-" equiv2)) 'string) equiv1)) (implies (,equiv1 x y) (,equiv2 x y)) :rule-classes ,(extend-rule-classes :REFINEMENT rule-classes) ,@(if instructions (list :instructions instructions) nil) ,@(if hints (list :hints hints) nil) ,@(if otf-flg (list :otf-flg otf-flg) nil) ,@(if doc (list :doc doc) nil))) (defmacro defcong (&whole x equiv1 equiv2 fn-args k &key (rule-classes '(:CONGRUENCE)) instructions hints otf-flg event-name doc) ":Doc-Section Events prove ~il[congruence] rule~/ ~bv[] ~c[Defcong] is used to prove that one ~il[equivalence] relation preserves another in a given argument position of a given function. Example: (defcong set-equal iff (memb x y) 2) is an abbreviation for (defthm set-equal-implies-iff-memb-2 (implies (set-equal y y-equiv) (iff (memb x y) (memb x y-equiv))) :rule-classes (:congruence)) ~ev[] ~l[congruence] and also ~pl[equivalence].~/ ~bv[] General Form: (defcong equiv1 equiv2 term k :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :event-name event-name :doc doc) ~ev[] where ~c[equiv1] and ~c[equiv2] are known ~il[equivalence] relations, ~c[term] is a call of a function ~c[fn] on the correct number of distinct variable arguments ~c[(fn x1 ... xn)], ~c[k] is a positive integer less than or equal to the arity of ~c[fn], and other arguments are as specified in the documentation for ~ilc[defthm]. The ~c[defcong] macro expands into a call of ~ilc[defthm]. The name of the ~ilc[defthm] event is ~c[equiv1-implies-equiv2-fn-k] unless an ~c[:event-name] keyword argument is supplied for the name. The term of the theorem is ~bv[] (implies (equiv1 xk yk) (equiv2 (fn x1... xk ...xn) (fn x1... yk ...xn))). ~ev[] The rule-class ~c[:]~ilc[congruence] is added to the ~ilc[rule-classes] specified, if it is not already there. All other arguments to the generated ~ilc[defthm] form are as specified by the keyword arguments above." (cond ((not (and (symbolp equiv1) (symbolp equiv2) (integerp k) (< 0 k) (symbol-listp fn-args) (no-duplicatesp-equal (cdr fn-args)) (< k (length fn-args)))) `(er soft 'defcong "The form of a defcong event is (defcong equiv1 equiv2 term k ...), ~ where equiv1 and equiv2 are symbols and k is a positive integer less ~ than the length of term, where term should be a call of a function ~ symbol on distinct variable arguments. However, ~x0 does not have ~ this form. See :DOC defcong." ',x)) (t (let ((sym (if (equal (symbol-package-name equiv1) *main-lisp-package-name*) (pkg-witness "ACL2") equiv1))) `(defthm ,(or event-name (intern-in-package-of-symbol (coerce (packn1 (list equiv1 "-IMPLIES-" equiv2 "-" (car fn-args) "-" k)) 'string) sym)) ,(let ((arg-k-equiv (intern-in-package-of-symbol (coerce (packn1 (list (nth k fn-args) '-equiv)) 'string) sym))) `(implies (,equiv1 ,(nth k fn-args) ,arg-k-equiv) (,equiv2 ,fn-args ,(putnth arg-k-equiv k fn-args)))) :rule-classes ,(extend-rule-classes :CONGRUENCE rule-classes) ,@(if instructions (list :instructions instructions) nil) ,@(if hints (list :hints hints) nil) ,@(if otf-flg (list :otf-flg otf-flg) nil) ,@(if doc (list :doc doc) nil)))))) acl2-sources/defuns.lisp0000664002132200015000000152711012222115527014736 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; Rockwell Addition: A major change is the provision of non-executable ; functions. These are typically functions that use stobjs but which ; are translated as though they were theorems rather than definitions. ; This is convenient (necessary?) for specifying some stobj ; properties. These functions will have executable counterparts that ; just throw. These functions will be marked with the property ; non-executablep. (defconst *mutual-recursion-ctx-string* "( MUTUAL-RECURSION ( DEFUN ~x0 ...) ...)") (defun translate-bodies1 (non-executablep names bodies bindings known-stobjs-lst ctx wrld state-vars) ; Non-executablep should be t or nil, to indicate whether or not the bodies are ; to be translated for execution. In the case of a function introduced by ; defproxy, non-executablep will be nil. (cond ((null bodies) (trans-value nil)) (t (mv-let (erp x bindings2) (translate1-cmp (car bodies) (if non-executablep t (car names)) (if non-executablep nil bindings) (car known-stobjs-lst) (if (and (consp ctx) (equal (car ctx) *mutual-recursion-ctx-string*)) (msg "( MUTUAL-RECURSION ... ( DEFUN ~x0 ...) ~ ...)" (car names)) ctx) wrld state-vars) (cond ((and erp (eq bindings2 :UNKNOWN-BINDINGS)) ; We try translating in some other order. This attempt isn't complete; for ; example, the following succeeds, but it fails if we switch the first two ; definitions. But it's cheap and better than nothing; without it, the ; unswitched version would fail, too. If this becomes an issue, consider the ; potentially quadratic algorithm of first finding one definition that ; translates successfully, then another, and so on, until all have been ; translated. ; (set-state-ok t) ; (set-bogus-mutual-recursion-ok t) ; (program) ; (mutual-recursion ; (defun f1 (state) ; (let ((state (f-put-global 'last-m 1 state))) ; (f2 state))) ; (defun f2 (state) ; (let ((state (f-put-global 'last-m 1 state))) ; (f3 state))) ; (defun f3 (state) ; state)) (trans-er-let* ((y (translate-bodies1 non-executablep (cdr names) (cdr bodies) bindings (cdr known-stobjs-lst) ctx wrld state-vars)) (x (translate1-cmp (car bodies) (if non-executablep t (car names)) (if non-executablep nil bindings) (car known-stobjs-lst) (if (and (consp ctx) (equal (car ctx) *mutual-recursion-ctx-string*)) (msg "( MUTUAL-RECURSION ... ( DEFUN ~x0 ...) ~ ...)" (car names)) ctx) wrld state-vars))) (trans-value (cons x y)))) (erp (mv erp x bindings2)) (t (let ((bindings bindings2)) (trans-er-let* ((y (translate-bodies1 non-executablep (cdr names) (cdr bodies) bindings (cdr known-stobjs-lst) ctx wrld state-vars))) (trans-value (cons x y)))))))))) (defun chk-non-executable-bodies (names arglists bodies non-executablep ctx state) ; Note that bodies are in translated form. (cond ((endp bodies) (value nil)) (t (let ((name (car names)) (body (car bodies)) (formals (car arglists))) ; The body should generally be a translated form of (prog2$ ; (throw-nonexec-error 'name (list . formals)) ...), as laid down by ; defun-nx-fn. But we make an exception for defproxy, i.e. (eq non-executablep ; :program), since it won't be true in that case and we don't care that it be ; true, as we have a program-mode function that does a throw. (cond ((throw-nonexec-error-p body (and (not (eq non-executablep :program)) name) formals) (chk-non-executable-bodies (cdr names) (cdr arglists) (cdr bodies) non-executablep ctx state)) (t (er soft ctx "The body of a defun that is marked :non-executable ~ (perhaps implicitly, by the use of defun-nx) must ~ be of the form (prog2$ (throw-nonexec-error ...) ~ ...)~@1. The definition of ~x0 is thus illegal. ~ See :DOC defun-nx." (car names) (if (eq non-executablep :program) "" " that is laid down by defun-nx")))))))) (defun translate-bodies (non-executablep names arglists bodies known-stobjs-lst ctx wrld state) ; Translate the bodies given and return a pair consisting of their translations ; and the final bindings from translate. Note that non-executable :program ; mode functions need to be analyzed for stobjs-out, because they are proxies ; (see :DOC defproxy) for encapsulated functions that may replace them later, ; and we need to guarantee to callers that those stobjs-out do not change with ; such replacements. (declare (xargs :guard (true-listp bodies))) (mv-let (erp lst bindings) (translate-bodies1 (eq non-executablep t) ; not :program names bodies (pairlis$ names names) known-stobjs-lst ctx wrld (default-state-vars t)) (er-progn (cond (erp ; erp is a ctx, lst is a msg (er soft erp "~@0" lst)) (non-executablep (chk-non-executable-bodies names arglists lst non-executablep ctx state)) (t (value nil))) (cond ((eq non-executablep t) (value (cons lst (pairlis-x2 names '(nil))))) (t (value (cons lst bindings))))))) ; The next section develops our check that mutual recursion is ; sensibly used. (defun chk-mutual-recursion-bad-names (lst names bodies) (cond ((null lst) nil) ((ffnnamesp names (car bodies)) (chk-mutual-recursion-bad-names (cdr lst) names (cdr bodies))) (t (cons (car lst) (chk-mutual-recursion-bad-names (cdr lst) names (cdr bodies)))))) (defconst *chk-mutual-recursion-string* "The definition~#0~[~/s~] of ~&1 ~#0~[does~/do~] not call any of ~ the other functions being defined via ~ mutual recursion. The theorem prover ~ will perform better if you define ~&1 ~ without the appearance of mutual recursion. See ~ :DOC set-bogus-mutual-recursion-ok to get ~ ACL2 to handle this situation differently.") (defun chk-mutual-recursion1 (names bodies warnp ctx state) (cond ((and warnp (warning-disabled-p "mutual-recursion")) (value nil)) (t (let ((bad (chk-mutual-recursion-bad-names names names bodies))) (cond ((null bad) (value nil)) (warnp (pprogn (warning$ ctx ("mutual-recursion") *chk-mutual-recursion-string* (if (consp (cdr bad)) 1 0) bad) (value nil))) (t (er soft ctx *chk-mutual-recursion-string* (if (consp (cdr bad)) 1 0) bad))))))) (defun chk-mutual-recursion (names bodies ctx state) ; We check that names has at least 1 element and that if it has ; more than one then every body calls at least one of the fns in ; names. The idea is to ensure that mutual recursion is used only ; when "necessary." This is not necessary for soundness but since ; mutually recursive fns are not handled as well as singly recursive ; ones, it is done as a service to the user. In addition, several ; error messages and other user-interface features exploit the presence ; of this check. (cond ((null names) (er soft ctx "It is illegal to use MUTUAL-RECURSION to define no functions.")) ((null (cdr names)) (value nil)) (t (let ((bogus-mutual-recursion-ok (cdr (assoc-eq :bogus-mutual-recursion-ok (table-alist 'acl2-defaults-table (w state)))))) (if (eq bogus-mutual-recursion-ok t) (value nil) (chk-mutual-recursion1 names bodies (eq bogus-mutual-recursion-ok :warn) ctx state)))))) ; We now develop put-induction-info. (mutual-recursion (defun ffnnamep-mod-mbe (fn term) ; We determine whether the function fn (possibly a lambda-expression) is used ; as a function in term', the result of expanding mbe calls (and equivalent ; calls) in term. Keep this in sync with the ffnnamep nest. Unlike ffnnamep, ; we assume here that fn is a symbolp. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (ffnnamep-mod-mbe fn (lambda-body (ffn-symb term))) (ffnnamep-mod-mbe-lst fn (fargs term)))) ((eq (ffn-symb term) fn) t) ((and (eq (ffn-symb term) 'return-last) (quotep (fargn term 1)) (eq (unquote (fargn term 1)) 'mbe1-raw)) (ffnnamep-mod-mbe fn (fargn term 3))) (t (ffnnamep-mod-mbe-lst fn (fargs term))))) (defun ffnnamep-mod-mbe-lst (fn l) (declare (xargs :guard (and (symbolp fn) (pseudo-term-listp l)))) (if (null l) nil (or (ffnnamep-mod-mbe fn (car l)) (ffnnamep-mod-mbe-lst fn (cdr l))))) ) ; Here is how we set the recursivep property. ; Rockwell Addition: The recursivep property has changed. Singly ; recursive fns now have the property (fn) instead of fn. (defun putprop-recursivep-lst (names bodies wrld) ; On the property list of each function symbol is stored the 'recursivep ; property. For nonrecursive functions, the value is implicitly nil but no ; value is stored (see comment below). Otherwise, the value is a true-list of ; fn names in the ``clique.'' Thus, for singly recursive functions, the value ; is a singleton list containing the function name. For mutually recursive ; functions the value is the list of every name in the clique. This function ; stores the property for each name and body in names and bodies. ; WARNING: We rely on the fact that this function puts the same names into the ; 'recursivep property of each member of the clique, in our handling of ; being-openedp. (cond ((int= (length names) 1) (cond ((ffnnamep-mod-mbe (car names) (car bodies)) (putprop (car names) 'recursivep names wrld)) (t ; Until we started using the 'def-bodies property to answer most questions ; about recursivep (see macro recursivep), it was a good idea to put a ; 'recursivep property of nil in order to avoid having getprop walk through an ; entire association list looking for 'recursivep. Now, this less-used ; property is just in the way. wrld))) (t (putprop-x-lst1 names 'recursivep names wrld)))) (defrec tests-and-call (tests call) nil) ; In nqthm this record was called TEST-AND-CASE and the second component was ; the arglist of a recursive call of the function being analyzed. Because of ; the presence of mutual recursion, we have renamed it tests-and-call and the ; second component is a "recursive" call (possibly mutually recursive). (mutual-recursion (defun all-calls (names term alist ans) ; Names is a list of defined function symbols. We accumulate into ans all ; terms u/alist such that for some f in names, u is a subterm of term that is a ; call of f. The algorithm just explores term looking for calls, and ; instantiate them as they are found. ; Our answer is in reverse print order (displaying lambda-applications ; as LETs). For example, if a, b and c are all calls of fns in names, ; then if term is (foo a ((lambda (x) c) b)), which would be printed ; as (foo a (let ((x b)) c)), the answer is (c b a). (cond ((variablep term) ans) ((fquotep term) ans) ((flambda-applicationp term) (all-calls names (lambda-body (ffn-symb term)) (pairlis$ (lambda-formals (ffn-symb term)) (sublis-var-lst alist (fargs term))) (all-calls-lst names (fargs term) alist ans))) (t (all-calls-lst names (fargs term) alist (cond ((member-eq (ffn-symb term) names) (add-to-set-equal (sublis-var alist term) ans)) (t ans)))))) (defun all-calls-lst (names lst alist ans) (cond ((null lst) ans) (t (all-calls-lst names (cdr lst) alist (all-calls names (car lst) alist ans))))) ) (defun all-calls-alist (names alist ans) ; This function processes an alist and computes all the calls of fns ; in names in the range of the alist and accumulates them onto ans. (cond ((null alist) ans) (t (all-calls-alist names (cdr alist) (all-calls names (cdar alist) nil ans))))) (defun termination-machine1 (tests calls ans) ; This function makes a tests-and-call with tests tests for every call ; in calls. It accumulates them onto ans so that if called initially ; with ans=nil the result is a list of tests-and-call in the reverse ; order of the calls. (cond ((null calls) ans) (t (termination-machine1 tests (cdr calls) (cons (make tests-and-call :tests tests :call (car calls)) ans))))) (mutual-recursion ; This clique is identical to the ffnnamesp/ffnnamesp-lst clique, except that ; here we assume that every element of fns is a symbol. (defun ffnnamesp-eq (fns term) (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (member-eq (ffn-symb term) fns) (ffnnamesp-eq fns (lambda-body (ffn-symb term))) (ffnnamesp-eq-lst fns (fargs term)))) ((member-eq (ffn-symb term) fns) t) (t (ffnnamesp-eq-lst fns (fargs term))))) (defun ffnnamesp-eq-lst (fns l) (if (null l) nil (or (ffnnamesp-eq fns (car l)) (ffnnamesp-eq-lst fns (cdr l))))) ) (defun member-eq-all (a lst) (or (eq lst :all) (member-eq a lst))) (mutual-recursion (defun termination-machine (names body alist tests ruler-extenders) ; This function builds a list of tests-and-call records for all calls in body ; of functions in names, but substituting alist into every term in the result. ; At the top level, body is the body of a function in names and alist is nil. ; Note that we don't need to know the function symbol to which the body ; belongs; all the functions in names are considered "recursive" calls. Names ; is a list of all the mutually recursive fns in the clique. Alist maps ; variables in body to actuals and is used in the exploration of lambda ; applications. ; For each recursive call in body a tests-and-call is returned whose tests are ; all the tests that "rule" the call and whose call is the call. If a rules b ; then a governs b but not vice versa. For example, in (if (g (if a b c)) d e) ; a governs b but does not rule b. The reason for taking this weaker notion of ; governance is that we can show that the tests-and-calls are together ; sufficient to imply the tests-and-calls generated by induction-machine. The ; notion of "rules" is extended by ruler-extenders; see :doc ; acl2-defaults-table and see :doc ruler-extenders. (cond ((or (variablep body) (fquotep body)) nil) ((flambda-applicationp body) (let ((lambda-body-result (termination-machine names (lambda-body (ffn-symb body)) (pairlis$ (lambda-formals (ffn-symb body)) (sublis-var-lst alist (fargs body))) tests ruler-extenders))) (cond ((member-eq-all :lambdas ruler-extenders) (union-equal (termination-machine-for-list names (fargs body) alist tests ruler-extenders) lambda-body-result)) (t (termination-machine1 (reverse tests) (all-calls-lst names (fargs body) alist nil) lambda-body-result))))) ((eq (ffn-symb body) 'if) (let* ((inst-test (sublis-var alist ; Since (remove-guard-holders x) is provably equal to x, the machine we ; generate using it below is equivalent to the machine generated without it. (remove-guard-holders (fargn body 1)))) (branch-result (append (termination-machine names (fargn body 2) alist (cons inst-test tests) ruler-extenders) (termination-machine names (fargn body 3) alist (cons (dumb-negate-lit inst-test) tests) ruler-extenders)))) (cond ((member-eq-all 'if ruler-extenders) (append (termination-machine names (fargn body 1) alist tests ruler-extenders) branch-result)) (t (termination-machine1 (reverse tests) (all-calls names (fargn body 1) alist nil) branch-result))))) ((and (eq (ffn-symb body) 'return-last) (quotep (fargn body 1)) (eq (unquote (fargn body 1)) 'mbe1-raw)) ; It is sound to treat return-last as a macro for logic purposes. We do so for ; (return-last 'mbe1-raw exec logic) both for induction and for termination. ; We could probably do this for any return-last call, but for legacy reasons ; (before introduction of return-last after v4-1) we restrict to 'mbe1-raw. (termination-machine names (fargn body 3) ; (return-last 'mbe1-raw exec logic) alist tests ruler-extenders)) ((member-eq-all (ffn-symb body) ruler-extenders) (let ((rec-call (termination-machine-for-list names (fargs body) alist tests ruler-extenders))) (if (member-eq (ffn-symb body) names) (cons (make tests-and-call :tests (reverse tests) :call (sublis-var alist body)) rec-call) rec-call))) (t (termination-machine1 (reverse tests) (all-calls names body alist nil) nil)))) (defun termination-machine-for-list (names bodies alist tests ruler-extenders) (cond ((endp bodies) nil) (t (append (termination-machine names (car bodies) alist tests ruler-extenders) (termination-machine-for-list names (cdr bodies) alist tests ruler-extenders))))) ) (defun termination-machines (names bodies ruler-extenders-lst) ; This function builds the termination machine for each function defined ; in names with the corresponding body in bodies. A list of machines ; is returned. (cond ((null bodies) nil) (t (cons (termination-machine names (car bodies) nil nil (car ruler-extenders-lst)) (termination-machines names (cdr bodies) (cdr ruler-extenders-lst)))))) ; We next develop the function that guesses measures when the user has ; not supplied them. (defun proper-dumb-occur-as-output (x y) ; We determine whether the term x properly occurs within the term y, insisting ; in addition that if y is an IF expression then x occurs properly within each ; of the two output branches. ; For example, X does not properly occur in X or Z. It does properly occur in ; (CDR X) and (APPEND X Y). It does properly occur in (IF a (CDR X) (CAR X)) ; but not in (IF a (CDR X) 0) or (IF a (CDR X) X). ; This function is used in always-tested-and-changedp to identify a formal to ; use as the measured formal in the justification of a recursive definition. ; We seek a formal that is tested on every branch and changed in every ; recursion. But if (IF a (CDR X) X) is the new value of X in some recursion, ; then it is not really changed, since if we distributed the IF out of the ; recursive call we would see a call in which X did not change. (cond ((equal x y) nil) ((variablep y) nil) ((fquotep y) nil) ((eq (ffn-symb y) 'if) (and (proper-dumb-occur-as-output x (fargn y 2)) (proper-dumb-occur-as-output x (fargn y 3)))) (t (dumb-occur-lst x (fargs y))))) (defun always-tested-and-changedp (var pos t-machine) ; Is var involved in every tests component of t-machine and changed ; and involved in every call, in the appropriate argument position? ; In some uses of this function, var may not be a variable symbol ; but an arbitrary term. (cond ((null t-machine) t) ((and (dumb-occur-lst var (access tests-and-call (car t-machine) :tests)) (let ((argn (nth pos (fargs (access tests-and-call (car t-machine) :call))))) ; If argn is nil then it means there was no enough args to get the one at pos. ; This can happen in a mutually recursive clique not all clique members have the ; same arity. (and argn (proper-dumb-occur-as-output var argn)))) (always-tested-and-changedp var pos (cdr t-machine))) (t nil))) (defun guess-measure (name defun-flg args pos t-machine ctx wrld state) ; T-machine is a termination machine, i.e., a lists of tests-and-call. Because ; of mutual recursion, we do not know that the call of a tests-and-call is a ; call of name; it may be a call of a sibling of name. We look for the first ; formal that is (a) somehow tested in every test and (b) somehow changed in ; every call. Upon finding such a var, v, we guess the measure (acl2-count v). ; But what does it mean to say that v is "changed in a call" if we are defining ; (foo x y v) and see a call of bar? We mean that v occurs in an argument to ; bar and is not equal to that argument. Thus, v is not changed in (bar x v) ; and is changed in (bar x (mumble v)). The difficulty here of course is that ; (mumble v) may not be being passed as the new value of v. But since this is ; just a heuristic guess intended to save the user the burden of typing ; (acl2-count x) a lot, it doesn't matter. ; If we fail to find a measure we cause an error. ; Pos is initially 0 and is the position in the formals list of the first ; variable listed in args. Defun-flg is t if we are guessing a measure on ; behalf of a function definition and nil if we are guessing on behalf of a ; :definition rule. It affects only the error message printed. (cond ((null args) (cond ((null t-machine) ; Presumably guess-measure was called here with args = NIL, for example if ; :set-bogus-mutual-recursion allowed it. We pick a silly measure that will ; work. If it doesn't work (hard to imagine), well then, we'll find out when ; we try to prove termination. (value (mcons-term* (default-measure-function wrld) *0*))) (t (er soft ctx "No ~#0~[:MEASURE~/:CONTROLLER-ALIST~] was supplied with the ~ ~#0~[definition of~/proposed :DEFINITION rule for~] ~x1. Our ~ heuristics for guessing one have not made any suggestions. ~ No argument of the function is tested along every branch of ~ the relevant IF structure and occurs as a proper subterm at ~ the same argument position in every recursive call. You must ~ specify a ~#0~[:MEASURE. See :DOC defun.~/:CONTROLLER-ALIST. ~ ~ See :DOC definition.~@2~] Also see :DOC ruler-extenders ~ for how to affect how much of the IF structure is explored by ~ our heuristics." (if defun-flg 0 1) name (cond (defun-flg "") (t " In some cases you may wish to use the :CONTROLLER-ALIST ~ from the original (or any previous) definition; this may ~ be seen by using :PR.")))))) ((always-tested-and-changedp (car args) pos t-machine) (value (mcons-term* (default-measure-function wrld) (car args)))) (t (guess-measure name defun-flg (cdr args) (1+ pos) t-machine ctx wrld state)))) (defun guess-measure-alist (names arglists measures t-machines ctx wrld state) ; We either cause an error or return an alist mapping the names in ; names to their measures (either user suggested or guessed). ; Warning: The returned alist, a, should have the property that (strip-cars a) ; is equal to names. We rely on that property in put-induction-info. (cond ((null names) (value nil)) ((equal (car measures) *no-measure*) (er-let* ((m (guess-measure (car names) t (car arglists) 0 (car t-machines) ctx wrld state))) (er-let* ((alist (guess-measure-alist (cdr names) (cdr arglists) (cdr measures) (cdr t-machines) ctx wrld state))) (value (cons (cons (car names) m) alist))))) (t (er-let* ((alist (guess-measure-alist (cdr names) (cdr arglists) (cdr measures) (cdr t-machines) ctx wrld state))) (value (cons (cons (car names) (car measures)) alist)))))) ; We now embark on the development of prove-termination, which must ; prove the justification theorems for each termination machine and ; the measures supplied/guessed. (defun remove-built-in-clauses (cl-set ens oncep-override wrld state ttree) ; We return two results. The first is a subset of cl-set obtained by deleting ; all built-in-clauseps and the second is the accumulated ttrees for the ; clauses we deleted. (cond ((null cl-set) (mv nil ttree)) (t (mv-let (built-in-clausep ttree1) (built-in-clausep ; We added defun-or-guard-verification as the caller arg of the call of ; built-in-clausep below. This addition is a little weird because there is no ; such function as defun-or-guard-verification; the caller argument is only ; used in trace reporting by forward-chaining. If we wanted to be more precise ; about who is responsible for this call, we'd have to change a bunch of ; functions because this function is called by clean-up-clause-set which is in ; turn called by prove-termination, guard-obligation-clauses, and ; verify-valid-std-usage (which is used in the non-standard defun-fn1). We ; just didn't think it mattered so much as to to warrant changing all those ; functions. 'defun-or-guard-verification (car cl-set) ens oncep-override wrld state) ; Ttree is known to be 'assumption free. (mv-let (new-set ttree) (remove-built-in-clauses (cdr cl-set) ens oncep-override wrld state (cons-tag-trees ttree1 ttree)) (cond (built-in-clausep (mv new-set ttree)) (t (mv (cons (car cl-set) new-set) ttree)))))))) (defun length-exceedsp (lst n) (cond ((null lst) nil) ((= n 0) t) (t (length-exceedsp (cdr lst) (1- n))))) (defun clean-up-clause-set (cl-set ens wrld ttree state) ; Warning: The set of clauses returned by this function only implies the input ; set. They are thought to be equivalent only if the input set contains no ; tautologies. See the caution in subsumption-replacement-loop. ; This function removes subsumed clauses from cl-set, does replacement (e.g., ; if the set includes the clauses {~q p} and {q p} replace them both with {p}), ; and removes built-in clauses. It returns two results, the cleaned up clause ; set and a ttree justifying the deletions and extending ttree. The returned ; ttree is 'assumption free (provided the incoming ttree is also) because all ; necessary splitting is done internally. ; Bishop Brock has pointed out that it is unclear what is the best order in ; which to do these two checks. Subsumption-replacement first and then ; built-in clauses? Or vice versa? We do a very trivial analysis here to ; order the two. Bishop is not to blame for this trivial analysis! ; Suppose there are n clauses in the initial cl-set. Suppose there are b ; built-in clauses. The cost of the subsumption-replacement loop is roughly ; n*n and that of the built-in check is n*b. Contrary to all common sense let ; us suppose that the subsumption-replacement loop eliminates redundant clauses ; at the rate, r, so that if we do the subsumption- replacement loop first at a ; cost of n*n we are left with n*r clauses. Note that the worst case for r is ; 1 and the smaller r is, the better; if r were 1/100 it would mean that we ; could expect subsumption-replacement to pare down a set of 1000 clauses to ; just 10. More commonly perhaps, r is just below 1, e.g., 99 out of 100 ; clauses are unaffected. To make the analysis possible, let's assume that ; built-in clauses crop up at the same rate! So, ; n^2 + bnr = cost of doing subsumption-replacement first = sub-first ; bn + (nr)^2 = cost of doing built-in clauses first = bic-first ; Observe that when r=1 the two costs are the same, as they should be. But ; generally, r can be expected to be slightly less than 1. ; Here is an example. Let n = 10, b = 100 and r = 99/100. In this example we ; have only a few clauses to consider but lots of built in clauses, and we have ; a realistically low expectation of hits. The cost of sub-first is 1090 but ; the cost of bic-first is 1098. So we should do sub-first. ; On the other hand, if n=100, b=20, and r=99/100 we see sub-first costs 11980 ; but bic-first costs 11801, so we should do built-in clauses first. This is a ; more common case. ; In general, we should do built-in clauses first when sub-first exceeds ; bic-first. ; n^2 + bnr >= bn + (nr)^2 = when we should do built-in clauses first ; Solving we get: ; n > b/(1+r). ; Indeed, if n=50 and b=100 and r=99/100 we see the costs of the two equal ; at 7450. (cond ((let ((sr-limit (sr-limit wrld))) (and sr-limit (> (length cl-set) sr-limit))) (pstk (remove-built-in-clauses cl-set ens (match-free-override wrld) wrld state (add-to-tag-tree 'sr-limit t ttree)))) ((length-exceedsp cl-set (global-val 'half-length-built-in-clauses wrld)) (mv-let (cl-set ttree) (pstk (remove-built-in-clauses cl-set ens (match-free-override wrld) wrld state ttree)) (mv (pstk (subsumption-replacement-loop (merge-sort-length cl-set) nil nil)) ttree))) (t (pstk (remove-built-in-clauses (pstk (subsumption-replacement-loop (merge-sort-length cl-set) nil nil)) ens (match-free-override wrld) wrld state ttree))))) (defun measure-clause-for-branch (name tc measure-alist rel wrld) ; Name is the name of some function, say f0, in a mutually recursive ; clique. Tc is a tests-and-call in the termination machine of f0 and hence ; contains some tests and a call of some function in the clique, say, ; f1. Measure-alist supplies the measures m0 and m1 for f0 and f1. ; Rel is the well-founded relation we are using. ; We assume that the 'formals for all the functions in the clique have ; already been stored in wrld. ; We create a set of clauses equivalent to ; tests -> (rel m1' m0), ; where m1' is m1 instantiated as indicated by the call of f1. (let* ((f0 name) (m0 (cdr (assoc-eq f0 measure-alist))) (tests (access tests-and-call tc :tests)) (call (access tests-and-call tc :call)) (f1 (ffn-symb call)) (m1-prime (subcor-var (formals f1 wrld) (fargs call) (cdr (assoc-eq f1 measure-alist)))) (concl (mcons-term* rel m1-prime m0))) (add-literal concl (dumb-negate-lit-lst tests) t))) (defun measure-clauses-for-fn1 (name t-machine measure-alist rel wrld) (cond ((null t-machine) nil) (t (conjoin-clause-to-clause-set (measure-clause-for-branch name (car t-machine) measure-alist rel wrld) (measure-clauses-for-fn1 name (cdr t-machine) measure-alist rel wrld))))) (defun measure-clauses-for-fn (name t-machine measure-alist mp rel wrld) ; We form all of the clauses that are required to be theorems for the admission ; of name with the given termination machine and measures. Mp is the "domain ; predicate" for the well-founded relation rel, or else mp is t meaning rel is ; well-founded on the universe. (For example, mp is o-p when rel is o<.) For ; the sake of illustration, suppose the defun of name is simply ; (defun name (x) ; (declare (xargs :guard (guard x))) ; (if (test x) (name (d x)) x)) ; Assume mp and rel are o-p and o<. Then we will create clauses equivalent ; to: ; (o-p (m x)) ; and ; (test x) -> (o< (m (d x)) (m x)). ; Observe that the guard of the function is irrelevant! ; We return a set of clauses which are implicitly conjoined. (cond ((eq mp t) (measure-clauses-for-fn1 name t-machine measure-alist rel wrld)) (t (conjoin-clause-to-clause-set (add-literal (mcons-term* mp (cdr (assoc-eq name measure-alist))) nil t) (measure-clauses-for-fn1 name t-machine measure-alist rel wrld))))) (defun measure-clauses-for-clique (names t-machines measure-alist mp rel wrld) ; We assume we can obtain from wrld the 'formals for each fn in names. (cond ((null names) nil) (t (conjoin-clause-sets (measure-clauses-for-fn (car names) (car t-machines) measure-alist mp rel wrld) (measure-clauses-for-clique (cdr names) (cdr t-machines) measure-alist mp rel wrld))))) (defun tilde-*-measure-phrase1 (alist wrld) (cond ((null alist) nil) (t (cons (msg (cond ((null (cdr alist)) "~p1 for ~x0.") (t "~p1 for ~x0")) (caar alist) (untranslate (cdar alist) nil wrld)) (tilde-*-measure-phrase1 (cdr alist) wrld))))) (defun tilde-*-measure-phrase (alist wrld) ; Let alist be an alist mapping function symbols, fni, to measure terms, mi. ; The fmt directive ~*0 will print the following, if #\0 is bound to ; the output of this fn: ; "m1 for fn1, m2 for fn2, ..., and mk for fnk." ; provided alist has two or more elements. If alist contains ; only one element, it will print just "m1." ; Note the final period at the end of the phrase! In an earlier version ; we did not add the period and saw a line-break between the ~x1 below ; and its final period. ; Thus, the following fmt directive will print a grammatically correct ; sentence ending with a period: "For the admission of ~&1 we will use ; the measure ~*0" (list* "" "~@*" "~@* and " "~@*, " (cond ((null (cdr alist)) (list (cons "~p1." (list (cons #\1 (untranslate (cdar alist) nil wrld)))))) (t (tilde-*-measure-phrase1 alist wrld))) nil)) (defun find-?-measure (measure-alist) (cond ((endp measure-alist) nil) ((let* ((entry (car measure-alist)) (measure (cdr entry))) (and (consp measure) (eq (car measure) :?) entry))) (t (find-?-measure (cdr measure-alist))))) (defun prove-termination (names t-machines measure-alist mp rel hints otf-flg bodies ctx ens wrld state ttree) ; Given a list of the functions introduced in a mutually recursive clique, ; their t-machines, the measure-alist for the clique, a domain predicate mp on ; which a certain relation, rel, is known to be well-founded, a list of hints ; (obtained by joining all the hints in the defuns), and a world in which we ; can find the 'formals of each function in the clique, we prove the theorems ; required by the definitional principle. In particular, we prove that each ; measure is an o-p and that in every tests-and-call in the t-machine of each ; function, the measure of the recursive calls is strictly less than that of ; the incoming arguments. If we fail, we cause an error. ; This function produces output describing the proofs. It should be the first ; output-producing function in the defun processing on every branch through ; defun. It always prints something and leaves you in a clean state ready to ; begin a new sentence, but may leave you in the middle of a line (i.e., col > ; 0). ; If we succeed we return two values, consed together as "the" value in this ; error/value/state producing function. The first value is the column produced ; by our output. The second value is a ttree in which we have accumulated all ; of the ttrees associated with each proof done. ; This function is specially coded so that if t-machines is nil then it is a ; signal that there is only one element of names and it is a non-recursive ; function. In that case, we short-circuit all of the proof machinery and ; simply do the associated output. We coded it this way to preserve the ; invariant that prove-termination is THE place the defun output is initiated. ; This function increments timers. Upon entry, any accumulated time is charged ; to 'other-time. The printing done herein is charged to 'print-time and the ; proving is charged to 'prove-time. (mv-let (cl-set cl-set-ttree) (cond ((and (not (ld-skip-proofsp state)) t-machines) (clean-up-clause-set (measure-clauses-for-clique names t-machines measure-alist mp rel wrld) ens wrld ttree state)) (t (mv nil ttree))) (cond ((and (not (ld-skip-proofsp state)) (find-?-measure measure-alist)) (let* ((entry (find-?-measure measure-alist)) (fn (car entry)) (measure (cdr entry))) (er soft ctx "A :measure of the form (:? v1 ... vk) is only legal when the ~ defun is redundant (see :DOC redundant-events) or when skipping ~ proofs (see :DOC ld-skip-proofsp). The :measure ~x0 supplied for ~ function symbol ~x1 is thus illegal." measure fn))) (t (er-let* ((cl-set-ttree (accumulate-ttree-and-step-limit-into-state cl-set-ttree :skip state))) (pprogn (increment-timer 'other-time state) (let ((displayed-goal (prettyify-clause-set cl-set (let*-abstractionp state) wrld)) (simp-phrase (tilde-*-simp-phrase cl-set-ttree))) (mv-let (col state) (cond ((ld-skip-proofsp state) (mv 0 state)) ((null t-machines) (io? event nil (mv col state) (names) (fmt "Since ~&0 is non-recursive, its admission is trivial." (list (cons #\0 names)) (proofs-co state) state nil) :default-bindings ((col 0)))) ((null cl-set) (io? event nil (mv col state) (measure-alist wrld rel names) (fmt "The admission of ~&0 ~#0~[is~/are~] trivial, using ~@1 ~ and the measure ~*2" (list (cons #\0 names) (cons #\1 (tilde-@-well-founded-relation-phrase rel wrld)) (cons #\2 (tilde-*-measure-phrase measure-alist wrld))) (proofs-co state) state (term-evisc-tuple nil state)) :default-bindings ((col 0)))) (t (io? event nil (mv col state) (cl-set-ttree displayed-goal simp-phrase measure-alist wrld rel names) (fmt "For the admission of ~&0 we will use ~@1 and the ~ measure ~*2 The non-trivial part of the measure ~ conjecture~#3~[~/, given ~*4,~] is~@5~%~%Goal~%~Q67." (list (cons #\0 names) (cons #\1 (tilde-@-well-founded-relation-phrase rel wrld)) (cons #\2 (tilde-*-measure-phrase measure-alist wrld)) (cons #\3 (if (nth 4 simp-phrase) 1 0)) (cons #\4 simp-phrase) (cons #\5 (if (tagged-objectsp 'sr-limit cl-set-ttree) " as follows (where the ~ subsumption/replacement limit ~ affected this analysis; see :DOC ~ case-split-limitations)." "")) (cons #\6 displayed-goal) (cons #\7 (term-evisc-tuple nil state))) (proofs-co state) state nil) :default-bindings ((col 0))))) (pprogn (increment-timer 'print-time state) (cond ((null cl-set) ; If the io? above did not print because 'event is inhibited, then col is nil. ; Just to keep ourselves sane, we will set it to 0. (value (cons (or col 0) cl-set-ttree))) (t (mv-let (erp ttree state) (prove (termify-clause-set cl-set) (make-pspv ens wrld :displayed-goal displayed-goal :otf-flg otf-flg) hints ens wrld ctx state) (cond (erp (let ((erp-msg (cond ((subsetp-eq '(summary error) (f-get-global 'inhibit-output-lst state)) ; This case is an optimization, in order to avoid the computations below, in ; particular of termination-machines. Note that erp-msg is potentially used in ; error output -- see the (er soft ...) form below -- and it is also ; potentially used in summary output, when print-summary passes to ; print-failure the first component of the error triple returned below. nil) (t (msg "The proof of the measure conjecture for ~&0 ~ has failed.~@1" names (if (equal t-machines (termination-machines names bodies (make-list (length names) :initial-element :all))) "" (msg "~|**NOTE**: The use of declaration ~ ~x0 would change the measure ~ conjecture, perhaps making it easier ~ to prove. See :DOC ruler-extenders." '(xargs :ruler-extenders :all)))))))) (mv-let (erp val state) (er soft ctx "~@0~|" erp-msg) (declare (ignore erp val)) (mv (msg "~@0 " erp-msg) nil state)))) (t (mv-let (col state) (io? event nil (mv col state) (names) (fmt "That completes the proof of the ~ measure theorem for ~&1. Thus, we ~ admit ~#1~[this function~/these ~ functions~] under the principle of ~ definition." (list (cons #\1 names)) (proofs-co state) state nil) :default-bindings ((col 0))) (pprogn (increment-timer 'print-time state) (value (cons (or col 0) (cons-tag-trees cl-set-ttree ttree))))))))))))))))))) ; When we succeed in proving termination, we will store the ; justification properties. (defun putprop-justification-lst (measure-alist subset-lst mp rel ruler-extenders-lst subversive-p wrld) ; Each function has a 'justification property. The value of the property ; is a justification record. (cond ((null measure-alist) wrld) (t (putprop-justification-lst (cdr measure-alist) (cdr subset-lst) mp rel ruler-extenders-lst subversive-p (putprop (caar measure-alist) 'justification (make justification :subset ; The following is equal to (all-vars (cdar measure-alist)), but since we ; already have it available, we use it rather than recomputing this all-vars ; call. (car subset-lst) :subversive-p subversive-p :mp mp :rel rel :measure (cdar measure-alist) :ruler-extenders (car ruler-extenders-lst)) wrld))))) (defun union-equal-to-end (x y) ; This is like union-equal, but where a common element is removed from the ; second argument instead of the first. (cond ((intersectp-equal x y) (append x (set-difference-equal y x))) (t (append x y)))) (defun cross-tests-and-calls3 (tacs tacs-lst) (cond ((endp tacs-lst) nil) (t (let ((tests1 (access tests-and-calls tacs :tests)) (tests2 (access tests-and-calls (car tacs-lst) :tests))) (cond ((some-element-member-complement-term tests1 tests2) (cross-tests-and-calls3 tacs (cdr tacs-lst))) (t (cons (make tests-and-calls :tests (union-equal-to-end tests1 tests2) :calls (union-equal (access tests-and-calls tacs :calls) (access tests-and-calls (car tacs-lst) :calls))) (cross-tests-and-calls3 tacs (cdr tacs-lst))))))))) (defun cross-tests-and-calls2 (tacs-lst1 tacs-lst2) ; See cross-tests-and-calls. (cond ((endp tacs-lst1) nil) (t (append (cross-tests-and-calls3 (car tacs-lst1) tacs-lst2) (cross-tests-and-calls2 (cdr tacs-lst1) tacs-lst2))))) (defun cross-tests-and-calls1 (tacs-lst-lst acc) ; See cross-tests-and-calls. (cond ((endp tacs-lst-lst) acc) (t (cross-tests-and-calls1 (cdr tacs-lst-lst) (cross-tests-and-calls2 (car tacs-lst-lst) acc))))) (defun sublis-tests-rev (test-alist acc) ; Each element of test-alist is a pair (test . alist) where test is a term and ; alist is either an alist or the atom :no-calls, which we treat as nil. Under ; that interpretation, we return the list of all test/alist, in reverse order ; from test-alist. (cond ((endp test-alist) acc) (t (sublis-tests-rev (cdr test-alist) (let* ((test (caar test-alist)) (alist (cdar test-alist)) (inst-test (cond ((or (eq alist :no-calls) (null alist)) test) (t (sublis-expr alist test))))) (cons inst-test acc)))))) (defun all-calls-test-alist (names test-alist acc) (cond ((endp test-alist) acc) (t (all-calls-test-alist names (cdr test-alist) (let* ((test (caar test-alist)) (alist (cdar test-alist))) (cond ((eq alist :no-calls) acc) (t (all-calls names test alist acc)))))))) (defun cross-tests-and-calls (names top-test-alist top-calls tacs-lst-lst) ; We are given a list, tacs-lst-lst, of lists of non-empty lists of ; tests-and-calls records. Each such record represents a list of tests ; together with a corresponding list of calls. Each way of selecting elements ; in the ith member of tacs-lst-lst can be viewed as a "path" ; through tacs-lst-lst (see also discussion of a matrix, below). We return a ; list containing a tests-and-calls record formed for each path: the tests, as ; the union of the tests of top-test-alist (viewed as a list of entries ; test/alist; see sublis-tests-rev) and the testsi; and the calls, as the union ; of the top-calls and the callsi. ; We can visualize the above discussion by forming a sort of matrix as follows. ; The columns (which need not all have the same length) are the elements of ; tacs-lst-lst; typically, for some call of a function in names, each column ; contains the tests-and-calls records formed from an argument of that call ; using induction-machine-for-fn1. A "path", as discussed above, is formed by ; picking one record from each column. The order of records in the result is ; probably not important, but it seems reasonable to give priority to the ; records from the first argument by starting with all paths containing the ; first record of the first argument; and so on. ; Note that the records are in the desired order for each list in tacs-lst-lst, ; but are in reverse order for top-test-alist, and also tacs-lst-lst is in ; reverse order, i.e., it corresponds to the arguments of some function call ; but in reverse argument order. ; For any tests-and-calls record: we view the tests as their conjunction, we ; view the calls as specifying substitutions, and we view the measure formula ; as the implication specifying that the substitutions cause an implicit ; measure to go down, assuming the tests. Logically, we want the resulting ; list of tests-and-calls records to have the following properties. ; - Coverage: The disjunction of the tests is provably equivalent to the ; conjunction of the tests in top-test-alist. ; - Disjointness: The conjunction of any two tests is provably equal to nil. ; - Measure: Each measure formula is provable. ; We assume that each list in tacs-lst-lst has the above three properties, but ; with top-test-alist being the empty list (that is, with conjunction of T). ; It's not clear as of this writing that Disjointness is necessary. The others ; are critical for justifying the induction schemes that will ultimately be ; generated from the tests-and-calls records. ; (One may imagine an alternate approach that avoids taking this sort of cross ; product, by constructing induction schemes with inductive hypotheses of the ; form (implies (and )). But ; then the current tests-and-calls data structure and corresponding heuristics ; would have to be revisited.) (let ((full-tacs-lst-lst (append tacs-lst-lst (list (list (make tests-and-calls :tests (sublis-tests-rev top-test-alist nil) :calls (all-calls-test-alist names top-test-alist top-calls))))))) (cross-tests-and-calls1 (cdr full-tacs-lst-lst) (car full-tacs-lst-lst)))) (mutual-recursion (defun induction-machine-for-fn1 (names body alist test-alist calls ruler-extenders merge-p) ; At the top level, this function builds a list of tests-and-calls for the ; given body of a function in names, a list of all the mutually recursive fns ; in a clique. Note that we don't need to know the function symbol to which ; the body belongs; all the functions in names are considered "recursive" ; calls. As we recur, we are considering body/alist, with accumulated tests ; consisting of test/a for test (test . a) in test-alist (but see :no-calls ; below, treated as the nil alist), and accumulated calls (already ; instantiated). ; To understand this algorithm, let us first consider the case that there are ; no lambda applications in body, which guarantees that alist will be empty on ; every recursive call, and ruler-extenders is nil. We explore body, ; accumulating into the list of tests (really, test-alist, but we defer ; discussion of the alist aspect) as we dive: for (if x y z), we accumulate x ; as we dive into y, and we accumulate the negation of x as we dive into z. ; When we hit a term u for which we are blocked from diving further (because we ; have encountered other than an if-term, or are diving into the first argument ; of an if-term), we collect up all the tests, reversing them to restore them ; to the order in which they were encountered from the top, and we collect up ; all calls of functions in names that are subterms of u or of any of the ; accumulated tests. From the termination analysis we know that assuming the ; collected tests, the arguments for each call are suitably smaller than the ; formals of the function symbol of that call, where of course, for a test only ; the tests superior to it are actually necessary. ; There is a subtle aspect to the handling of the tests in the above algorithm. ; To understand it, consider the following example. Suppose names is (f), p is ; a function symbol, 'if is in ruler-extenders, and body is: ; (if (consp x) ; (if (if (consp x) ; (p x) ; (p (f (cons x x))) ; x ; (f (cdr x))) ; x) ; Since 'if is in ruler-extenders, termination analysis succeeds because the ; tests leading to (f (cons x x)) are contradictory. But with the naive ; algorithm described above, when we encounter the term (f (cdr x)) we would ; create a tests-and-calls record that collects up the call (f (cons x x)); yet ; clearly (cons x x) is not smaller than the formal x under the default measure ; (acl2-count x), even assuming (consp x) and (not (p (f (cons x x)))). ; Thus it is unsound in general to collect all the calls of a ruling test when ; 'if is among the ruler-extenders. But we don't need to do so anyhow, because ; we will collect suitable calls from the first argument of an 'if test as we ; dive into it, relying on cross-tests-and-calls to incorporate those calls, as ; described below. We still have to note the test as we dive into the true and ; false branches of an IF call, but that test should not contribute any calls ; when the recursion bottoms out under one of those branches. ; A somewhat similar issue arises with lambda applications in the case that ; :lambdas is among the ruler-extenders. Consider the term ((lambda (x) (if ; )) ). With :lambdas among the ruler-extenders, we ; will be diving into , and not every call in may be assumed to be ; "smaller" than the formals as we are exploring the body of the lambda. So we ; need to collect up the combination of and an alist binding lambda ; formals to actuals (in this example, binding x to , or more generally, ; the instantiation of under the existing bindings). That way, when the ; recursion bottoms out we can collect calls explicitly in that test (unless ; 'if is in ruler-extenders, as already described), instantiating them with the ; associated alist. If we instead had collected up the instantiated test, we ; would also have collected all calls in above when bottoming out in the ; lambda body, and that would be a mistake (as discussed above, since not every ; call in arg is relevant). ; So when the recursion bottoms out, some tests should not contribute any ; calls, and some should be instantiated with a corresponding alist. As we ; collect a test when we recur into the true or false branch of an IF call, we ; thus actually collect a pair consisting of the test and a corresponding ; alist, signifying that for every recursive call c in the test, the actual ; parameter list for c/a is known to be "smaller" than the formals. If ; ruler-extenders is the default, then all calls of the instantiated test are ; known to be "smaller", so we pair the instantiated test with nil. But if 'if ; is in the ruler-extenders, then we do not want to collect any calls of the ; test -- as discussed above, cross-tests-and-calls will take care of them -- ; so we pair the instantiated test with the special indicator :no-calls. ; The merge-p argument concerns the question of whether exploration of a term ; (if test tbr fbr) should create two tests-and-calls records even if there are ; no recursive calls in tbr or fbr. For backward compatibility, the answer is ; "no" if we are exploring according to the conventional notion of "rulers". ; But now imagine a function body that has many calls of 'if deep under ; different arguments of some function call. If we create separate records as ; in the conventional case, the induction scheme may explode when we combine ; these cases with cross-tests-and-calls -- it will be as though we clausified ; even before starting the induction proof proper. But if we avoid such a ; priori case-splitting, then during the induction proof, it is conceivable ; that many of these potential separate cases could be dispatched with ; rewriting, thus avoiding so much case splitting. ; So if merge-p is true, then we avoid creating tests-and-calls records when ; both branches of an IF term have no recursive calls. We return (mv flag ; tests-and-calls-lst), where if merge-p is true, then flag is true exactly ; when a call of a function in names has been encountered. For backward ; compatibility, merge-p is false except when we the analysis has taken ; advantage of ruler-extenders. If merge-p is false, then the first returned ; value is irrelevant. ; Note: Perhaps some calls of reverse can be omitted, though that might ruin ; some regressions. Our main concern for replayability has probably been the ; order of the tests, not so much the order of the calls. (cond ((or (variablep body) (fquotep body) (and (not (flambda-applicationp body)) (not (eq (ffn-symb body) 'if)) (not (and (eq (ffn-symb body) 'return-last) (quotep (fargn body 1)) (eq (unquote (fargn body 1)) 'mbe1-raw))) (not (member-eq-all (ffn-symb body) ruler-extenders)))) (mv (and merge-p ; optimization (ffnnamesp names body)) (list (make tests-and-calls :tests (sublis-tests-rev test-alist nil) :calls (reverse (all-calls names body alist (all-calls-test-alist names (reverse test-alist) calls))))))) ((flambda-applicationp body) (cond ((member-eq-all :lambdas ruler-extenders) ; other case is easier to follow (mv-let (flg1 temp1) (induction-machine-for-fn1 names (lambda-body (ffn-symb body)) (pairlis$ (lambda-formals (ffn-symb body)) (sublis-var-lst alist (fargs body))) nil ; test-alist nil ; calls ruler-extenders ; The following example shows why we use merge-p = t when ruler-extenders ; includes :lambdas. ; (defun app (x y) ; ((lambda (result) ; (if (our-test result) ; result ; 0)) ; (if (endp x) ; y ; (cons (car x) ; (app (cdr x) y))))) ; If we do not use t, then we wind up crossing two base cases from the lambda ; body with two from the arguments, which seems like needless explosion. t) (mv-let (flg2 temp2) (induction-machine-for-fn1-lst names (fargs body) alist ruler-extenders nil ; acc t ; merge-p nil) ; flg (mv (or flg1 flg2) (cross-tests-and-calls names test-alist calls ; We cons the lambda-body's contribution to the front, since we want its tests ; to occur after those of the arguments to the lambda application (because the ; lambda body occurs lexically last in a LET form, so this will make the most ; sense to the user). Note that induction-machine-for-fn1-lst returns its ; result in reverse of the order of arguments. Thus, the following cons will ; be in the reverse order that is expected by cross-tests-and-calls. (cons temp1 temp2)))))) (t ; (not (member-eq-all :lambdas ruler-extenders)) ; We just go straight into the body of the lambda, with the appropriate alist. ; But we modify calls, so that every tests-and-calls we build will contain all ; of the calls occurring in the actuals to the lambda application. (mv-let (flg temp) (induction-machine-for-fn1 names (lambda-body (ffn-symb body)) (pairlis$ (lambda-formals (ffn-symb body)) (sublis-var-lst alist (fargs body))) test-alist (all-calls-lst names (fargs body) alist calls) ruler-extenders merge-p) (mv (and merge-p ; optimization (or flg (ffnnamesp-lst names (fargs body)))) temp))))) ((and (eq (ffn-symb body) 'return-last) (quotep (fargn body 1)) (eq (unquote (fargn body 1)) 'mbe1-raw)) ; See the comment in termination-machine about it being sound to treat ; return-last as a macro. (induction-machine-for-fn1 names (fargn body 3) alist test-alist calls ruler-extenders merge-p)) ((eq (ffn-symb body) 'if) (let ((test ; Since (remove-guard-holders x) is provably equal to x, the machine we ; generate using it below is equivalent to the machine generated without it. (remove-guard-holders (fargn body 1)))) (cond ((member-eq-all 'if ruler-extenders) ; other case is easier to follow (mv-let (tst-flg tst-result) (induction-machine-for-fn1 names (fargn body 1) ; keep guard-holders alist test-alist calls ruler-extenders t) (let ((inst-test (sublis-var alist test)) (merge-p (or merge-p ; If the test contains a recursive call then we prefer to merge when computing ; the induction machines for the true and false branches, to avoid possible ; explosion in cases. tst-flg))) (mv-let (tbr-flg tbr-result) (induction-machine-for-fn1 names (fargn body 2) alist (cons (cons inst-test :no-calls) nil) ; tst-result has the tests nil ; calls, already in tst-result ruler-extenders merge-p) (mv-let (fbr-flg fbr-result) (induction-machine-for-fn1 names (fargn body 3) alist (cons (cons (dumb-negate-lit inst-test) :no-calls) nil) ; tst-result has the tests nil ; calls, already in tst-result ruler-extenders merge-p) (cond ((and merge-p (not (or tbr-flg fbr-flg))) (mv tst-flg tst-result)) (t (mv (or tbr-flg fbr-flg tst-flg) (cross-tests-and-calls names nil ; top-test-alist nil ; calls are already in tst-result ; We put the branch contributions on the front, since their tests are to wind ; up at the end, in analogy to putting the lambda body on the front as ; described above. (list (append tbr-result fbr-result) tst-result)))))))))) (t ; (not (member-eq-all 'if ruler-extenders)) (mv-let (tbr-flg tbr-result) (induction-machine-for-fn1 names (fargn body 2) alist (cons (cons test alist) test-alist) calls ruler-extenders merge-p) (mv-let (fbr-flg fbr-result) (induction-machine-for-fn1 names (fargn body 3) alist (cons (cons (dumb-negate-lit test) alist) test-alist) calls ruler-extenders merge-p) (cond ((and merge-p (not (or tbr-flg fbr-flg))) (mv nil (list (make tests-and-calls :tests (sublis-tests-rev test-alist nil) :calls (all-calls names test alist (reverse (all-calls-test-alist names (reverse test-alist) calls))))))) (t (mv (or tbr-flg fbr-flg) (append tbr-result fbr-result)))))))))) (t ; (member-eq-all (ffn-symb body) ruler-extenders) and not lambda etc. (mv-let (merge-p args) ; The special cases just below could perhaps be nicely generalized to any call ; in which at most one argument contains calls of any name in names. We found ; that we needed to avoid merge-p=t on the recursive call in the prog2$ case ; (where no recursive call is in the first argument) when we introduced ; defun-nx after Version_3.6.1, since the resulting prog2$ broke community book ; books/tools/flag.lisp, specifically event (FLAG::make-flag flag-pseudo-termp ; ...), because the :normalize nil kept the prog2$ around and merge-p=t then ; changed the induction scheme. ; Warning: Do not be tempted to skip the call of cross-tests-and-calls in the ; special cases below for mv-list, prog2$ and arity 1. It is needed in order ; to handle :no-calls (used above). (cond ((and (eq (ffn-symb body) 'mv-list) (not (ffnnamesp names (fargn body 1)))) (mv merge-p (list (fargn body 2)))) ((and (eq (ffn-symb body) 'return-last) (quotep (fargn body 1)) (eq (unquote (fargn body 1)) 'progn) (not (ffnnamesp names (fargn body 2)))) (mv merge-p (list (fargn body 3)))) ((null (cdr (fargs body))) (mv merge-p (list (fargn body 1)))) (t (mv t (fargs body)))) (let* ((flg0 (member-eq (ffn-symb body) names)) (calls (if flg0 (cons (sublis-var alist body) calls) calls))) (mv-let (flg temp) (induction-machine-for-fn1-lst names args alist ruler-extenders nil ; acc merge-p nil) ; flg (mv (or flg0 flg) (cross-tests-and-calls names test-alist calls temp)))))))) (defun induction-machine-for-fn1-lst (names bodies alist ruler-extenders acc merge-p flg) ; The resulting list corresponds to bodies in reverse order. (cond ((endp bodies) (mv flg acc)) (t (mv-let (flg1 ans1) (induction-machine-for-fn1 names (car bodies) alist nil ; tests nil ; calls ruler-extenders merge-p) (induction-machine-for-fn1-lst names (cdr bodies) alist ruler-extenders (cons ans1 acc) merge-p (or flg1 flg)))))) ) ; We now develop the code for eliminating needless tests in tests-and-calls ; records, leading to function simplify-tests-and-calls-lst. See the comment ; there. Term-equated-to-constant appears earlier, because it is used in ; related function simplify-clause-for-term-equal-const-1. (defun term-equated-to-constant-in-termlist (lst) (cond ((endp lst) (mv nil nil)) (t (mv-let (var const) (term-equated-to-constant (car lst)) (cond (var (mv var const)) (t (term-equated-to-constant-in-termlist (cdr lst)))))))) (defun simplify-tests (var const tests) ; For a related function, see simplify-clause-for-term-equal-const-1. (cond ((endp tests) (mv nil nil)) (t (mv-let (changedp rest) (simplify-tests var const (cdr tests)) (mv-let (flg term) (strip-not (car tests)) (mv-let (var2 const2) (term-equated-to-constant term) (cond ((and flg (equal var var2) (not (equal const const2))) (mv t rest)) (changedp (mv t (cons (car tests) rest))) (t (mv nil tests))))))))) (defun simplify-tests-and-calls (tc) ; For an example of the utility of removing guard holders, note that lemma ; STEP2-PRESERVES-DL->NOT2 in community book ; books/workshops/2011/verbeek-schmaltz/sources/correctness.lisp has failed ; when we did not do so. (let* ((tests0 (remove-guard-holders-lst (access tests-and-calls tc :tests)))) (mv-let (var const) (term-equated-to-constant-in-termlist tests0) (let ((tests (cond (var (mv-let (changedp tests) (simplify-tests var const tests0) (declare (ignore changedp)) tests)) (t tests0)))) (cond ((null tests) nil) ; contradictory case (t (make tests-and-calls :tests tests :calls (remove-guard-holders-lst (access tests-and-calls tc :calls))))))))) (defun simplify-tests-and-calls-lst (tc-list) ; We eliminate needless tests (not (equal term (quote const))) that clutter the ; induction machine. To see this function in action: ; (skip-proofs (defun foo (x) ; (if (consp x) ; (case (car x) ; (0 (foo (nth 0 x))) ; (1 (foo (nth 1 x))) ; (2 (foo (nth 2 x))) ; (3 (foo (nth 3 x))) ; (otherwise (foo (cdr x)))) ; x))) ; (thm (equal (foo x) yyy)) (cond ((endp tc-list) nil) (t (cons (simplify-tests-and-calls (car tc-list)) (simplify-tests-and-calls-lst (cdr tc-list)))))) (defun induction-machine-for-fn (names body ruler-extenders) ; We build an induction machine for the function in names with the given body. ; We claim the soundness of the induction schema suggested by this machine is ; easily seen from the proof done by prove-termination. See ; termination-machine. ; Note: The induction machine built for a clique of more than 1 ; mutually recursive functions is probably unusable. We do not know ; how to do inductions on such functions now. (mv-let (flg ans) (induction-machine-for-fn1 names body nil ; alist nil ; tests nil ; calls ruler-extenders nil); merge-p (declare (ignore flg)) (simplify-tests-and-calls-lst ans))) (defun induction-machines (names bodies ruler-extenders-lst) ; This function builds the induction machine for each function defined ; in names with the corresponding body in bodies. A list of machines ; is returned. See termination-machine. ; Note: If names has more than one element we return nil because we do ; not know how to interpret the induction-machines that would be ; constructed from a non-trivial clique of mutually recursive ; functions. As a matter of fact, as of this writing, ; induction-machine-for-fn constructs the "natural" machine for ; mutually recursive functions, but there's no point in consing them ; up since we can't use them. So all that machinery is ; short-circuited here. (cond ((null (cdr names)) (list (induction-machine-for-fn names (car bodies) (car ruler-extenders-lst)))) (t nil))) (defun putprop-induction-machine-lst (names bodies ruler-extenders-lst subversive-p wrld) ; Note: If names has more than one element we do nothing. We only ; know how to interpret induction machines for singly recursive fns. (cond ((cdr names) wrld) (subversive-p wrld) (t (putprop (car names) 'induction-machine (car (induction-machines names bodies ruler-extenders-lst)) wrld)))) (defun quick-block-initial-settings (formals) (cond ((null formals) nil) (t (cons 'un-initialized (quick-block-initial-settings (cdr formals)))))) (defun quick-block-info1 (var term) (cond ((eq var term) 'unchanging) ((dumb-occur var term) 'self-reflexive) (t 'questionable))) (defun quick-block-info2 (setting info1) (case setting (questionable 'questionable) (un-initialized info1) (otherwise (cond ((eq setting info1) setting) (t 'questionable))))) (defun quick-block-settings (settings formals args) (cond ((null settings) nil) (t (cons (quick-block-info2 (car settings) (quick-block-info1 (car formals) (car args))) (quick-block-settings (cdr settings) (cdr formals) (cdr args)))))) (defun quick-block-down-t-machine (name settings formals t-machine) (cond ((null t-machine) settings) ((not (eq name (ffn-symb (access tests-and-call (car t-machine) :call)))) (er hard 'quick-block-down-t-machine "When you add induction on mutually recursive functions don't ~ forget about QUICK-BLOCK-INFO!")) (t (quick-block-down-t-machine name (quick-block-settings settings formals (fargs (access tests-and-call (car t-machine) :call))) formals (cdr t-machine))))) (defun quick-block-info (name formals t-machine) ; This function should be called a singly recursive function, name, and ; its termination machine. It should not be called on a function ; in a non-trivial mutually recursive clique because the we don't know ; how to analyze a call to a function other than name in the t-machine. ; We return a list in 1:1 correspondence with the formals of name. ; Each element of the list is either 'unchanging, 'self-reflexive, ; or 'questionable. The list is used to help quickly decide if a ; blocked formal can be tolerated in induction. (quick-block-down-t-machine name (quick-block-initial-settings formals) formals t-machine)) (defun putprop-quick-block-info-lst (names t-machines wrld) ; We do not know how to compute quick-block-info for non-trivial ; mutually-recursive cliques. We therefore don't do anything for ; those functions. If names is a list of length 1, we do the ; computation. We assume we can find the formals of the name in wrld. (cond ((null (cdr names)) (putprop (car names) 'quick-block-info (quick-block-info (car names) (formals (car names) wrld) (car t-machines)) wrld)) (t wrld))) (deflabel subversive-recursions :doc ":Doc-Section Miscellaneous why we restrict ~il[encapsulate]d recursive functions~/ Subtleties arise when one of the ``constrained'' functions, ~c[f], introduced in the ~il[signature] of an ~ilc[encapsulate] event, is involved in the termination argument for a non-local recursively defined function, ~c[g], in that ~c[encapsulate]. During the processing of the encapsulated events, ~c[f] is locally defined to be some witness function, ~c[f']. Properties of ~c[f'] are explicitly proved and exported from the encapsulate as the constraints on the undefined function ~c[f]. But if ~c[f] is used in a recursive ~c[g] defined within the encapsulate, then the termination proof for ~c[g] may use properties of ~c[f'] ~-[] the witness ~-[] that are not explicitly set forth in the constraints stated for ~c[f]. Such recursive ~c[g] are said be ``subversive'' because if naively treated they give rise to unsound induction schemes or (via functional instantiation) recurrence equations that are impossible to satisfy. We illustrate what could go wrong below. Subversive recursions are not banned outright. Instead, they are treated as part of the constraint. That is, in the case above, the definitional equation for ~c[g] becomes one of the constraints on ~c[f]. This is generally a severe restriction on future functional instantiations of ~c[f]. In addition, ACL2 removes from its knowledge of ~c[g] any suggestions about legal inductions to ``unwind'' its recursion. What should you do? Often, the simplest response is to move the offending recursive definition, e.g., ~c[g], out of the encapsulate. That is, introduce ~c[f] by constraint and then define ~c[g] as an ``independent'' event. You may need to constrain ``additional'' properties of ~c[f] in order to admit ~c[g], e.g., constrain it to reduce some ordinal measure. However, by separating the introduction of ~c[f] from the admission of ~c[g] you will clearly identify the necessary constraints on ~c[f], functional instantiations of ~c[f] will be simpler, and ~c[g] will be a useful function which suggests inductions to the theorem prover. Note that the functions introduced in the ~il[signature] should not even occur ancestrally in the termination proofs for non-local recursive functions in the encapsulate. That is, the constrained functions of an encapsulate should not be reachable in the dependency graph of the functions used in the termination arguments of recursive functions in encapsulate. If they are reachable, their definitions become part of the constraints.~/ The following event illustrates the problem posed by subversive recursions. ~bv[] (encapsulate (((f *) => *)) (local (defun f (x) (cdr x))) (defun g (x) (if (consp x) (not (g (f x))) t))) ~ev[] Suppose, contrary to how ACL2 works, that the encapsulate above were to introduce no constraints on ~c[f] on the bogus grounds that the only use of ~c[f] in the encapsulate is in an admissible function. We discuss the plausibility of this bogus argument in a moment. Then it would be possible to prove the theorem: ~bv[] (defthm f-not-identity (not (equal (f '(a . b)) '(a . b))) :rule-classes nil :hints ((\"Goal\" :use (:instance g (x '(a . b)))))) ~ev[] simply by observing that if ~c[(f '(a . b))] were ~c['(a . b)], then ~c[(g '(a . b))] would be ~c[(not (g '(a . b)))], which is impossible. But then we could functionally instantiate ~c[f-not-identity], replacing ~c[f] by the identity function, to prove ~c[nil]! This is bad. ~bv[] (defthm bad nil :rule-classes nil :hints ((\"Goal\" :use (:functional-instance f-not-identity (f identity))))) ~ev[] This sequence of events was legal in versions of ACL2 prior to Version 1.5. When we realized the problem we took steps to make it illegal. However, our steps were insufficient and it was possible to sneak in a subversive function (via mutual recursion) as late as Version 2.3. We now turn to the plausibility of the bogus argument above. Why might one even be tempted to think that the definition of ~c[g] above poses no constraint on ~c[f]? Here is a very similar encapsulate. ~bv[] (encapsulate (((f *) => *)) (local (defun f (x) (cdr x))) (defun map (x) (if (consp x) (cons (f x) (map (cdr x))) nil))) ~ev[] Here ~c[map] plays the role of ~c[g] above. Like ~c[g], ~c[map] calls the constrained function ~c[f]. But ~c[map] truly does not constrain ~c[f]. In particular, the definition of ~c[map] could be moved ``out'' of the encapsulate so that ~c[map] is introduced afterwards. The difference between ~c[map] and ~c[g] is that the constrained function plays no role in the termination argument for the one but does for the other. As a ``user-friendly'' gesture, ACL2 implicitly moves ~c[map]-like functions out of encapsulations; logically speaking, they are introduced after the encapsulation. This simplifies the constraint. When the constraint cannot be thus simplfied the user is advised, via the ``infected'' warning, to phrase the encapsulation in the simplest way possible. The lingering bug between Versions 1.5 and 2.3 mentioned above was due to our failure to detect the ~c[g]-like nature of some functions when they were defined in mutually recursively cliques with other functions. The singly recursive case was recognized. The bug arose because our detection ``algorithm'' was based on the ``suggested inductions'' left behind by successful definitions. We failed to recall that mutually-recursive definitions do not, as of this writing, make any suggestions about inductions and so did not leave any traces of their subversive natures. We conclude by elaborating on the criterion ``involved in the termination argument'' mentioned at the outset. Suppose that function ~c[f] is recursively defined in an ~ilc[encapsulate], where the body of ~c[f] has no ``ancestor'' among the functions introduced in the signature of that ~ilc[encapsulate], i.e.: the body contains no call of a signature function, no call of a function whose body calls a signature function, and so on. Then ACL2 treats ~c[f] as though it is defined in front of that ~c[encapsulate] form; so ~c[f] is not ~c[constrained] by the encapsulate, and its definition is hence certainly not subversive in the sense discussed above. But suppose to the contrary that some function introduced in the signature is an ancestor of the body of ~c[f]. Then the definition of ~c[f] is subversive if moreover, its termination proof obligation has an ancestor among the signature functions. Now, that proof obligation involves terms from the first argument of selected calls of ~c[IF], as well as recursive calls; for a detailed discussion, ~pl[ruler-extenders]. The important point here is that for the recursive calls, only the arguments in ``measured'' positions are relevant to the termination proof obligation. Consider the following example. ~bv[] (encapsulate (((h *) => *)) (local (defun h (n) n)) (defun f (i n) (if (zp i) n (f (1- i) (h n))))) ~ev[] ACL2 heuristically picks the measure ~c[(acl2-count i)] for the definition of ~c[f]; thus, ~c[i] is the only ``measured formal'' of ~c[f]. Since ~c[i] is the first formal of ~c[f], then for the recursive call of ~c[f], only the first argument contributes to the termination proof obligation: in this case, ~c[(1- i)] but not ~c[(h n)]. Thus, even though ~c[h] is a signature function, the definition of ~c[f] is not considered to be subversive; an induction scheme is thus stored for ~c[f]. (This restriction to measured formal positions of recursive calls, for determining subversive definitions, is new in Version_3.5 of ACL2.)~/") (deflabel subversive-inductions :doc ":Doc-Section Miscellaneous why we restrict ~il[encapsulate]d recursive functions~/ ~l[subversive-recursions]. ~/ ") (defmacro big-mutrec (names) ; All mutual recursion nests with more than the indicated number of defuns will ; be processed by installing intermediate worlds, for improved performance. We ; have seen an improvement of roughly two orders of magnitude in such a case. ; The value below is merely heuristic, chosen with very little testing; we ; should feel free to change it. `(> (length ,names) 20)) (defmacro update-w (condition new-w &optional retract-p) ; WARNING: This function installs a world, so it may be necessary to call it ; only in the (dynamic) context of revert-world-on-error. For example, its ; calls during definitional processing are all under the call of ; revert-world-on-error in defuns-fn. (let ((form `(pprogn ,(if retract-p '(set-w 'retraction wrld state) '(set-w 'extension wrld state)) (value wrld)))) ; We handling condition t separately, to avoid a compiler warning (at least in ; Allegro CL) that the final COND branch (t (value wrld)) is unreachable. (cond ((eq condition t) `(let ((wrld ,new-w)) ,form)) (t `(let ((wrld ,new-w)) (cond (,condition ,form) (t (value wrld)))))))) (defun get-sig-fns1 (ee-lst) (cond ((endp ee-lst) nil) (t (let ((ee-entry (car ee-lst))) (cond ((and (eq (car ee-entry) 'encapsulate) (cddr ee-entry)) ; pass-2 (append (get-sig-fns1 (cdr ee-lst)) ; usually nil (strip-cars (cadr ee-entry)))) (t (get-sig-fns1 (cdr ee-lst)))))))) (defun get-sig-fns (wrld) (get-sig-fns1 (global-val 'embedded-event-lst wrld))) (defun selected-all-fnnames-lst (formals subset actuals acc) (cond ((endp formals) acc) (t (selected-all-fnnames-lst (cdr formals) subset (cdr actuals) (if (member-eq (car formals) subset) (all-fnnames1 nil (car actuals) acc) acc))))) (defun subversivep (fns t-machine formals-and-subset-alist wrld) ; See subversive-cliquep for conditions (1) and (2). (cond ((endp t-machine) nil) (t (or ; Condition (1): (intersectp-eq fns (instantiable-ancestors (all-fnnames-lst (access tests-and-call (car t-machine) :tests)) wrld nil)) ; Condition (2): (let* ((call (access tests-and-call (car t-machine) :call)) (entry (assoc-eq (ffn-symb call) formals-and-subset-alist)) (formals (assert$ entry (cadr entry))) (subset (cddr entry)) (measured-call-args-ancestors (instantiable-ancestors (selected-all-fnnames-lst formals subset (fargs call) nil) wrld nil))) (intersectp-eq fns measured-call-args-ancestors)) ; Recur: (subversivep fns (cdr t-machine) formals-and-subset-alist wrld))))) (defun subversive-cliquep (fns t-machines formals-and-subset-alist wrld) ; Here, fns is a list of functions introduced in an encapsulate. If we are ; using the [Front] rule (from the Structured Theory paper) to move some ; functions forward, then fns is the list of ones that are NOT moved: they all ; use the signature functions somehow. T-machines is a list of termination ; machines for some clique of functions defined within the encapsulate. The ; clique is subversive if some function defined in the clique has a subversive ; t-machine. ; Intuitively, a t-machine is subversive if its admission depended on ; properties of the witnesses for signature functions. That is, the definition ; uses signature functions in a way that affects the termination argument. ; Technically a t-machine is subversive if some tests-and-call record in it has ; either of the following properties: ; (1) a test mentions a function in fns ; (2) an argument of a call in a measured position mentions a function in fns. ; Observe that if a clique is not subversive then every test and argument to ; every recursive call uses functions defined outside the encapsulate. If we ; are in a top-level encapsulate, then a non-subversive clique is a ``tight'' ; clique wrt the set S of functions in the initial world of the encapsulate, ; where ``tight'' is defined by the Structured Theory paper, i.e.: for every ; subterm u of a ruler or recursive call in the clique, all function symbols of ; u belong to S (but now we restrict to measured positions in recursive ; calls). (cond ((endp t-machines) nil) (t (or (subversivep fns (car t-machines) formals-and-subset-alist wrld) (subversive-cliquep fns (cdr t-machines) formals-and-subset-alist wrld))))) (defun prove-termination-non-recursive (names bodies mp rel hints otf-flg big-mutrec ctx ens wrld state) ; This function separates out code from put-induction-info. (er-progn (cond (hints (let ((bogus-defun-hints-ok (cdr (assoc-eq :bogus-defun-hints-ok (table-alist 'acl2-defaults-table (w state)))))) (cond ((eq bogus-defun-hints-ok :warn) (pprogn (warning$ ctx "Non-rec" "Since ~x0 is non-recursive your supplied :hints will be ~ ignored (as these are used only during termination ~ proofs). Perhaps either you meant to supply ~ :guard-hints or the body of the definition is incorrect." (car names)) (value nil))) (bogus-defun-hints-ok ; t (value nil)) (t ; bogus-defun-hints-ok = nil, the default (er soft ctx "Since ~x0 is non-recursive it is odd that you have supplied ~ :hints (which are used only during termination proofs). We ~ suspect something is amiss, e.g., you meant to supply ~ :guard-hints or the body of the definition is incorrect. To ~ avoid this error, see :DOC set-bogus-defun-hints-ok." (car names)))))) (t (value nil))) (er-let* ((wrld1 (update-w big-mutrec wrld)) (pair (prove-termination names nil nil mp rel nil otf-flg bodies ctx ens wrld1 state nil))) ; We know that pair is of the form (col . ttree), where col is the column ; the output state is in. (value (list (car pair) wrld1 (cdr pair)))))) (defun prove-termination-recursive (names arglists measures t-machines mp rel hints otf-flg bodies ctx ens wrld state) ; This function separates out code from put-induction-info. ; First we get the measures for each function. That may cause an error if we ; couldn't guess one for some function. (er-let* ((measure-alist (guess-measure-alist names arglists measures t-machines ctx wrld state)) (hints (if hints ; hints and default-hints already translated (value hints) (let ((default-hints (default-hints wrld))) (if default-hints ; not yet translated (translate-hints (cons "Measure Lemma for" (car names)) default-hints ctx wrld state) (value hints))))) (pair (prove-termination names t-machines measure-alist mp rel hints otf-flg bodies ctx ens wrld state nil))) ; Ok, we have managed to prove termination! Pair is a pair of the form (col . ; ttree), where col tells us what column the printer is in and ttree describes ; the proofs done. (value (list* (car pair) measure-alist (cdr pair))))) (defun put-induction-info-recursive (names arglists col ttree measure-alist t-machines ruler-extenders-lst bodies mp rel wrld state) ; This function separates out code from put-induction-info. ; We have proved termination. Col tells us what column the printer is in and ; ttree describes the proofs done. We now store the 'justification of each ; function, the induction machine for each function, and the quick-block-info. (let* ((subset-lst ; Below, we rely on the fact that this subset-lst corresponds, in order, to ; names. See the warnings comment in guess-measure-alist. (collect-all-vars (strip-cdrs measure-alist))) (sig-fns (get-sig-fns wrld)) (subversive-p (and sig-fns (subversive-cliquep sig-fns t-machines (pairlis$ names (pairlis$ arglists subset-lst)) wrld))) (wrld2 (putprop-induction-machine-lst names bodies ruler-extenders-lst subversive-p wrld)) (wrld3 (putprop-justification-lst measure-alist subset-lst mp rel ruler-extenders-lst subversive-p wrld2)) (wrld4 (putprop-quick-block-info-lst names t-machines wrld3))) ; We are done. We will return the final wrld and the ttree describing ; the proofs we did. (value (list* col wrld4 (push-lemma (cddr (assoc-eq rel (global-val 'well-founded-relation-alist wrld4))) ttree) subversive-p)))) (defun put-induction-info (names arglists measures ruler-extenders-lst bodies mp rel hints otf-flg big-mutrec ctx ens wrld state) ; WARNING: This function installs a world. That is safe at the time of this ; writing because this function is only called by defuns-fn0, which is only ; called by defuns-fn, where that call is protected by a revert-world-on-error. ; We are processing a clique of mutually recursive functions with the names, ; arglists, measures, ruler-extenders-lst, and bodies given. All of the above ; lists are in 1:1 correspondence. The hints is the result of appending ; together all of the hints provided. Mp and rel are the domain predicate and ; well-founded relation to be used. We attempt to prove the admissibility of ; the recursions. We cause an error if any proof fails. We put a lot of ; properties under the function symbols, namely: ; recursivep all fns in names ; justification all recursive fns in names ; induction-machine the singly recursive fn in name* ; quick-block-info the singly recursive fn in name* ; symbol-class :ideal all fns in names ; *If names consists of exactly one recursive fn, we store its ; induction-machine and its quick-block-info, otherwise we do not. ; If no error occurs, we return a triple consisting of the column the printer ; is in, the final value of wrld and a tag-tree documenting the proofs we did. ; Note: The function could be declared to return 5 values, but we would rather ; use the standard state and error primitives and so it returns 3 and lists ; together the three "real" answers. (let ((wrld1 (putprop-recursivep-lst names bodies wrld))) ; The put above stores a note on each function symbol as to whether it is ; recursive or not. An important question arises: have we inadventently ; assumed something axiomatically about inadmissible functions? We say no. ; None of the functions in question have bodies yet, so the simplifier doesn't ; care about properties such as 'recursivep. However, we make use of this ; property below to decide if we need to prove termination. (cond ((and (null (cdr names)) (null (getprop (car names) 'recursivep nil 'current-acl2-world wrld1))) ; If only one function is being defined and it is non-recursive, we can quit. ; But we have to store the symbol-class and we have to print out the admission ; message with prove-termination so the rest of our processing is uniform. (prove-termination-non-recursive names bodies mp rel hints otf-flg big-mutrec ctx ens wrld1 state)) (t ; Otherwise we first construct the termination machines for all the ; functions in the clique. (let ((t-machines (termination-machines names bodies ruler-extenders-lst))) ; Next we get the measures for each function. That may cause an error ; if we couldn't guess one for some function. (er-let* ((wrld1 (update-w ; Sol Swords sent an example in which a clause-processor failed during a ; termination proof. That problem goes away if we install the world, which we ; do by making the following binding. t ; formerly big-mutrec wrld1)) (triple (prove-termination-recursive names arglists measures t-machines mp rel hints otf-flg bodies ctx ens wrld1 state))) (let* ((col (car triple)) (measure-alist (cadr triple)) (ttree (cddr triple))) (put-induction-info-recursive names arglists col ttree measure-alist t-machines ruler-extenders-lst bodies mp rel wrld1 state)))))))) ; We next worry about storing the normalized bodies. (defun destructure-definition (term install-body ens wrld ttree) ; Term is a translated term that is the :corollary of a :definition rule. If ; install-body is non-nil then we intend to update the 'def-bodies ; property; and if moreover, install-body is :normalize, then we want to ; normalize the resulting new body. Ens is an enabled structure if ; install-body is :normalize; otherwise ens is ignored. ; We return (mv hyps equiv fn args body new-body ttree) or else nils if we fail ; to recognize the form of term. Hyps results flattening the hypothesis of ; term, when a call of implies, into a list of hypotheses. Failure can be ; detected by checking for (null fn) since nil is not a legal fn symbol. (mv-let (hyps equiv fn-args body) (case-match term (('implies hyp (equiv fn-args body)) (mv (flatten-ands-in-lit hyp) equiv fn-args body)) ((equiv fn-args body) (mv nil equiv fn-args body)) (& (mv nil nil nil nil))) (let ((equiv (if (member-eq equiv *equality-aliases*) 'equal equiv)) (fn (and (consp fn-args) (car fn-args)))) (cond ((and fn (symbolp fn) (not (member-eq fn ; Hide is disallowed in chk-acceptable-definition-rule. '(quote if))) (equivalence-relationp equiv wrld)) (mv-let (body ttree) (cond ((eq install-body :NORMALIZE) (normalize (remove-guard-holders body) nil ; iff-flg nil ; type-alist ens wrld ttree)) (t (mv body ttree))) (mv hyps equiv fn (cdr fn-args) body ttree))) (t (mv nil nil nil nil nil nil)))))) (defun member-rewrite-rule-rune (rune lst) ; Lst is a list of :rewrite rules. We determine whether there is a ; rule in lst with the :rune rune. (cond ((null lst) nil) ((equal rune (access rewrite-rule (car lst) :rune)) t) (t (member-rewrite-rule-rune rune (cdr lst))))) (defun replace-rewrite-rule-rune (rune rule lst) ; Lst is a list of :rewrite rules and one with :rune rune is among them. ; We replace that rule with rule. (cond ((null lst) nil) ((equal rune (access rewrite-rule (car lst) :rune)) (cons rule (cdr lst))) (t (cons (car lst) (replace-rewrite-rule-rune rune rule (cdr lst)))))) ; We massage the hyps with this function to speed rewrite up. (defun preprocess-hyp (hyp) ; In nqthm, this function also replaced (not (zerop x)) by ; ((numberp x) (not (equal x '0))). ; Lemma replace-consts-cp-correct1 in community book ; books/clause-processors/replace-defined-consts.lisp failed after we added ; calls of mv-list to the macroexpansion of mv-let calls in Version_4.0, which ; allowed lemma replace-const-corr-replace-const-alists-list to be applied: ; there was a free variable in the hypothesis that had no longer been matched ; when mv-list was introduced. So we have decided to add the calls of ; remove-guard-holders below to take care of such issues. (case-match hyp (('atom x) (list (mcons-term* 'not (mcons-term* 'consp (remove-guard-holders x))))) (& (list (remove-guard-holders hyp))))) (defun preprocess-hyps (hyps) (cond ((null hyps) nil) (t (append (preprocess-hyp (car hyps)) (preprocess-hyps (cdr hyps)))))) (defun add-definition-rule-with-ttree (rune nume clique controller-alist install-body term ens wrld ttree) ; We make a :rewrite rule of subtype 'definition (or 'abbreviation) ; and add it to the 'lemmas property of the appropriate fn. This ; function is defined the way it is (namely, taking term as an arg and ; destructuring it rather than just taking term in pieces) because it ; is also used as the function for adding a user-supplied :REWRITE ; rule of subclass :DEFINITION. (mv-let (hyps equiv fn args body ttree) (destructure-definition term install-body ens wrld ttree) (let* ((vars-bag (all-vars-bag-lst args nil)) (abbreviationp (and (null hyps) (null clique) ; Rockwell Addition: We have changed the notion of when a rule is an ; abbreviation. Our new concern is with stobjs and lambdas. ; If fn returns a stobj, we don't consider it an abbreviation unless ; it contains no lambdas. Thus, the updaters are abbreviations but ; lambda-nests built out of them are not. We once tried the idea of ; letting a lambda in a function body disqualify the function as an ; abbreviation, but that made FLOOR no longer an abbreviation and some ; of the fp proofs failed. So we made the question depend on stobjs ; for compatibility's sake. (abbreviationp (not (all-nils ; We call getprop rather than calling stobjs-out, because this code may run ; with fn = return-last, and the function stobjs-out causes an error in that ; case. We don't mind treating return-last as an ordinary function here. (getprop fn 'stobjs-out '(nil) 'current-acl2-world wrld))) vars-bag body))) (rule (make rewrite-rule :rune rune :nume nume :hyps (preprocess-hyps hyps) :equiv equiv :lhs (mcons-term fn args) :var-info (cond (abbreviationp (not (null vars-bag))) (t (var-counts args body))) :rhs body :subclass (cond (abbreviationp 'abbreviation) (t 'definition)) :heuristic-info (cond (abbreviationp nil) (t (cons clique controller-alist))) ; Backchain-limit-lst does not make much sense for definitions. :backchain-limit-lst nil))) (let ((wrld0 (if (eq fn 'hide) wrld (putprop fn 'lemmas (cons rule (getprop fn 'lemmas nil 'current-acl2-world wrld)) wrld)))) (cond (install-body (mv (putprop fn 'def-bodies (cons (make def-body :nume nume :hyp (and hyps (conjoin hyps)) :concl body :rune rune :formals args :recursivep clique :controller-alist controller-alist) (getprop fn 'def-bodies nil 'current-acl2-world wrld)) wrld0) ttree)) (t (mv wrld0 ttree))))))) (defun add-definition-rule (rune nume clique controller-alist install-body term ens wrld) (mv-let (wrld ttree) (add-definition-rule-with-ttree rune nume clique controller-alist install-body term ens wrld nil) (declare (ignore ttree)) wrld)) #+:non-standard-analysis (defun listof-standardp-macro (lst) ; If the guard for standardp is changed from t, consider changing ; the corresponding calls of mcons-term* to fcons-term*. (if (consp lst) (if (consp (cdr lst)) (mcons-term* 'if (mcons-term* 'standardp (car lst)) (listof-standardp-macro (cdr lst)) *nil*) (mcons-term* 'standardp (car lst))) *t*)) (defun putprop-body-lst (names arglists bodies normalizeps clique controller-alist #+:non-standard-analysis std-p ens wrld installed-wrld ttree) ; Rockwell Addition: A major change is the handling of PROG2$ and THE ; below. ; We store the body property for each name in names. It is set to the ; normalized body. Normalization expands some nonrecursive functions, namely ; those on *expandable-boot-strap-non-rec-fns*, which includes old favorites ; like EQ and ATOM. In addition, we eliminate all the RETURN-LASTs and THEs ; from the body. This can be seen as just an optimization of expanding nonrec ; fns. ; We add a definition rule equating the call of name with its normalized body. ; We store the unnormalized body under the property 'unnormalized-body. ; We return two results: the final wrld and a ttree justifying the ; normalization, which is an extension of the input ttree. ; Essay on the Normalization of Bodies ; We normalize the bodies of functions to speed up type-set and rewriting. But ; there are some subtle issues here. Let term be a term and let term' be its ; normalization. We will ignore iff-flg and type-alist here. First, we claim ; that term and term' are equivalent. Thus, if we are allowed to add the axiom ; (fn x) = term then we may add (fn x) = term' too. But while term and term' ; are equivalent they are not interchangeable from the perspective of defun ; processing. For example, as nqthm taught us, the measure conjectures ; generated from term' may be inadequate to justify the admission of a function ; whose body is term. A classic example is (fn x) = (if (fn x) t t), where the ; normalized body is just t. The Hisorical Plaque below contains a proof that ; if (fn x) = term' is admissible then there exists one and only one function ; satisfying (fn x) = term. Thus, while the latter definition may not actually ; be admissible it at least will not get us into trouble and in the end the ; issue vis-a-vis admissibility seems to be the technical one of exactly how we ; wish to define the Principle of Definition. ; Historical Plaque from Nqthm ; The following extensive comment used to guard the definition of ; DEFN0 in nqthm and is placed here partly as a nostalgic reminder of ; decades of work and partly because it has some good statistics in it ; that we might still want to look at. ; This function is FUNCALLed and therefore may not be made a MACRO. ; The list of comments on this function do not necessarily describe ; the code below. They have been left around in reverse chronology ; order to remind us of the various combinations of preprocessing ; we have tried. ; If we ever get blown out of the water while normalizing IFs in a ; large defn, read the following comment before abandoning ; normalization. ; 18 August 1982. Here we go again! At the time of this writing ; the preprocessing of defns is as follows, we compute the ; induction and type info on the translated body and store under ; sdefn the translated body. This seems to slow down the system a ; lot and we are going to change it so that we store under sdefn ; the result of expanding boot strap nonrec fns and normalizing ; IFs. As nearly as we can tell from the comments below, we have ; not previously tried this. According to the record, we have ; tried expanding all nonrec fns, and we have tried expanding boot ; strap fns and doing a little normalization. The data that ; suggests this will speed things up is as follows. Consider the ; first call of SIMPLIFY-CLAUSE in the proof of PRIME-LIST-TIMES ; -LIST. The first three literals are trivial but the fourth call ; of SIMPLIFY-CLAUSE1 is on (NOT (PRIME1 C (SUB1 C))). With SDEFNs ; not expanded and normalized -- i.e., under the processing as it ; was immediately before the current change -- there are 2478 calls ; of REWRITE and 273 calls of RELIEVE-HYPS for this literal. With ; all defns preprocessed as described here those counts drop to ; 1218 and 174. On a sample of four theorems, PRIME-LIST-TIMES- ; LIST, PRIME-LIST-PRIME-FACTORS, FALSIFY1-FALSIFIES, and ORDERED- ; SORT, the use of normalized and expanded sdefns saves us 16 ; percent of the conses over the use of untouched sdefns, reducing ; the cons counts for those theorems from 880K to 745K. It seems ; unlikely that this preprocessing will blow us out of the water on ; large defns. For the EV used in UNSOLV and for the 386L M with ; subroutine call this new preprocessing only marginally increases ; the size of the sdefn. It would be interesting to see a function ; that blows us out of the water. When one is found perhaps the ; right thing to do is to so preprocess small defns and leave big ; ones alone. ; 17 December 1981. Henceforth we will assume that the very body ; the user supplies (modulo translation) is the body that the ; theorem-prover uses to establish that there is one and only one ; function satisfying the definition equation by determining that ; the given body provides a method for computing just that ; function. This prohibits our "improving" the body of definitions ; such as (f x) = (if (f x) a a) to (f x) = a. ; 18 November 1981. We are sick of having to disable nonrec fns in ; order to get large fns processed, e.g., the interpreter for our ; 386L class. Thus, we have decided to adopt the policy of not ; touching the user's typein except to TRANSLATE! it. The ; induction and type analysis as well as the final SDEFN are based ; on the translated typein. ; Before settling with the preprocessing used below we tried ; several different combinations and did provealls. The main issue ; was whether we should normalize sdefns. Unfortunately, the ; incorporation of META0-LEMMAS was also being experimented with, ; and so we do not have a precise breakdown of who is responsible ; for what. However, below we give the total stats for three ; separate provealls. The first, called 1PROVEALL, contained ; exactly the code below -- except that the ADD-DCELL was given the ; SDEFN with all the fn names replaced by *1*Fns instead of a fancy ; TRANSLATE-TO-INTERLISP call. Here are the 1PROVEALL stats. ; Elapsed time = 9532.957, CPU time = 4513.88, GC time = 1423.261, ; IO time = 499.894, CONSes consumed = 6331517. ; We then incorporated META0-LEMMAS. Simultaneously, we tried ; running the RUN fns through DEFN and found that we exploded. The ; expansion of nonrec fns and the normalization of IFs before the ; induction analysis transformed functions of CONS-COUNT 300 to ; functions of CONS-COUNT exceeding 18K. We therefore decided to ; expand only BOOT-STRAP fns -- and not NORMALIZE-IFS for the ; purposes of induction analysis. After the induction and type ; analyses were done, we put down an SDEFN with some trivial IF ; simplification performed -- e.g., IF X Y Y => Y and IF bool T F ; => bool -- but not a NORMALIZE-IFs version. We then ran a ; proveall with CANCEL around as a META0-LEMMA. The result was ; about 20 percent slower than the 1PROVEALL and used 15 percent ; more CONSes. At first this was attributed to CANCEL. However, ; we then ran two simultaneous provealls, one with META0-LEMMAS set ; to NIL and one with it set to ((1CANCEL . CORRECTNESS-OF-CANCEL)). ; The result was that the version with CANCEL available used ; slightly fewer CONSes than the other one -- 7303311 to 7312505 ; That was surprising because the implementation of META0-LEMMAS ; uses no CONSes if no META0-LEMMAS are available, so the entire 15 ; percent more CONSes had to be attributed to the difference in the ; defn processing. This simultaneous run was interesting for two ; other reasons. The times -- while still 20 percent worse than ; 1PROVEALL -- were one half of one percent different, with CANCEL ; being the slower. That means having CANCEL around does not cost ; much at all -- and the figures are significant despite the slop ; in the operating system's timing due to thrashing because the two ; jobs really were running simultaneously. The second interesting ; fact is that CANCEL can be expected to save us a few CONSes ; rather than cost us. ; We therefore decided to return the DEFN0 processing to its ; original state. Only we did it in two steps. First, we put ; NORMALIZE-IFs into the pre-induction processing and into the ; final SDEFN processing. Here are the stats on the resulting ; proveall, which was called PROVEALL-WITH-NORM-AND-CANCEL but not ; saved. Elapsed time = 14594.01, CPU time = 5024.387, GC time = ; 1519.932, IO time = 593.625, CONSes consumed = 6762620. ; While an improvement, we were still 6 percent worse than ; 1PROVEALL on CONSes. But the only difference between 1PROVEALL ; and PROVEALL-WITH-NORM-AND-CANCEL -- if you discount CANCEL which ; we rightly believed was paying for itself -- was that in the ; former induction analyses and type prescriptions were being ; computed from fully expanded bodies while in the latter they were ; computed from only BOOT-STRAP-expanded bodies. We did not ; believe that would make a difference of over 400,000 CONSes, but ; had nothing else to believe. So we went to the current state, ; where we do the induction and type analyses on the fully expanded ; and normalized bodies -- bodies that blow us out of the water on ; some of the RUN fns. Here are the stats for ; PROVEALL-PROOFS.79101, which was the proveall for that version. ; Elapsed time = 21589.84, CPU time = 4870.231, GC time = 1512.813, ; IO time = 554.292, CONSes consumed= 6356282. ; Note that we are within 25K of the number of CONSes used by ; 1PROVEALL. But to TRANSLATE-TO-INTERLISP all of the defns in ; question costs 45K. So -- as expected -- CANCEL actually saved ; us a few CONSes by shortening proofs. It takes only 18 seconds ; to TRANSLATE-TO-INTERLISP the defns, so a similar argument does ; not explain why the latter proveall is 360 seconds slower than ; 1PROVEALL. But since the elapsed time is over twice as long, we ; believe it is fair to chalk that time up to the usual slop ; involved in measuring cpu time on a time sharing system. ; We now explain the formal justification of the processing we do ; on the body before testing it for admissibility. ; We do not work with the body that is typed in by the user but ; with an equivalent body' produced by normalization and the ; expansion of nonrecursive function calls in body. We now prove ; that if (under no assumptions about NAME except that it is a ; function symbol of the correct arity) (a) body is equivalent to ; body' and (b) (name . args) = body' is accepted under our ; principle of definition, then there exists exactly one function ; satisfying the original equation (name . args) = body. ; First observe that since the definition (name . args) = body' is ; accepted by our principle of definition, there exists a function ; satisfying that equation. But the accepted equation is ; equivalent to the equation (name . args) = body by the ; hypothesis that body is equivalent to body'. ; We prove that there is only one such function by induction. ; Assume that the definition (name . args) = body has been accepted ; under the principle of definition. Suppose that f is a new name ; and that (f . args) = bodyf, where bodyf results from replacing ; every use of name as a function symbol in body with f. It ; follows that (f . args) = bodyf', where bodyf' results from ; replacing every use of name as a function symbol in body' with f. ; We can now easily prove that (f . args) = (name . args) by ; induction according to the definition of name. Q.E.D. ; One might be tempted to think that if the defn with body' is ; accepted under the principle of definition then so would be the ; defn with body and that the use of body' was merely to make the ; implementation of the defn principle more powerful. This is not ; the case. For example ; (R X) = (IF (R X) T T) ; is not accepted by the definitional principle, but we would ; accept the body'-version (R X) = T, and by our proof, that ; function uniquely satisfies the equation the user typed in. ; One might be further tempted to think that if we changed ; normalize so that (IF X Y Y) = Y was not applied, then the two ; versions were inter-acceptable under the defn principle. This is ; not the case either. The function ; (F X) = (IF (IF (X.ne.0) (F X-1) F) (F X-1) T) ; is not accepted under the principle of defn. Consider its ; normalized body. (cond ((null names) (mv wrld ttree)) (t (let* ((fn (car names)) (args (car arglists)) (body (car bodies)) (normalizep (car normalizeps)) (rune (fn-rune-nume fn nil nil installed-wrld)) (nume (fn-rune-nume fn t nil installed-wrld))) (let* ((eqterm (fcons-term* 'equal (fcons-term fn args) body)) (term #+:non-standard-analysis (if (and std-p (consp args)) (fcons-term* 'implies (listof-standardp-macro args) eqterm) eqterm) #-:non-standard-analysis eqterm) #+:non-standard-analysis (wrld (if std-p (putprop fn 'constrainedp t wrld) wrld))) (mv-let (wrld ttree) (add-definition-rule-with-ttree rune nume clique controller-alist (if normalizep :NORMALIZE t) ; install-body term ens (putprop fn 'unnormalized-body body wrld) ttree) (putprop-body-lst (cdr names) (cdr arglists) (cdr bodies) (cdr normalizeps) clique controller-alist #+:non-standard-analysis std-p ens wrld installed-wrld ttree))))))) ; We now develop the facility for guessing the type-prescription of a defuned ; function. When guards were part of the logic, the first step was to guess ; the types implied by the guard. We no longer have to do that, but the ; utility written for it is used elsewhere and so we keep it here. ; Suppose you are trying to determine the type implied by term for some ; variable x. The key trick is to normalize the term and replace every true ; output by x and every nil output by a term with an empty type-set. Then take ; the type of that term. For example, if term is (if (if p q) r nil) then it ; normalizes to (if p (if q (if r t nil) nil) nil) and so produces the ; intermediate term (if p (if q (if r x e ) e ) e ), where x is the formal in ; whose type we are interested and e is a new variable assumed to be of empty ; type. (defun type-set-implied-by-term1 (term tvar fvar) ; Term is a normalized propositional term. Tvar and fvar are two variable ; symbols. We return a normalized term equivalent to (if term tvar fvar) ; except we drive tvar and fvar as deeply into term as possible. (cond ((variablep term) (fcons-term* 'if term tvar fvar)) ((fquotep term) (if (equal term *nil*) fvar tvar)) ((eq (ffn-symb term) 'if) (fcons-term* 'if (fargn term 1) (type-set-implied-by-term1 (fargn term 2) tvar fvar) (type-set-implied-by-term1 (fargn term 3) tvar fvar))) (t ; We handle all non-IF applications here, even lambda applications. ; Once upon a time we considered driving into the body of a lambda. ; But that introduces a free var in the body, namely fvar (or whatever ; the new variable symbol is) and there are no guarantees that type-set ; works on such a non-term. (fcons-term* 'if term tvar fvar)))) (defun type-set-implied-by-term (var not-flg term ens wrld ttree) ; Given a variable and a term, we determine a type set for the ; variable under the assumption that the term is non-nil. If not-flg ; is t, we negate term before using it. This function is not used in ; the guard processing but is needed in the compound-recognizer work. ; The ttree returned is 'assumption-free (provided the initial ttree ; is also). (let* ((new-var (genvar 'genvar "EMPTY" nil (all-vars term))) (type-alist (list (list* new-var *ts-empty* nil)))) (mv-let (normal-term ttree) (normalize term t nil ens wrld ttree) (type-set (type-set-implied-by-term1 normal-term (if not-flg new-var var) (if not-flg var new-var)) nil nil type-alist ens wrld ttree nil nil)))) (defun putprop-initial-type-prescriptions (names wrld) ; Suppose we have a clique of mutually recursive fns, names. Suppose ; that we can recover from wrld both the formals and body of each ; name in names. ; This function adds to the front of each 'type-prescriptions property ; of the names in names an initial, empty guess at its ; type-prescription. These initial rules are unsound and are only the ; starting point of our iterative guessing mechanism. Oddly, the ; :rune and :nume of each rule is the same! We use the ; *fake-rune-for-anonymous-enabled-rule* for the rune and the nume ; nil. We could create the proper runes and numes (indeed, we did at ; one time) but those runes then find their way into the ttrees of the ; various guesses (and not just the rune of the function being typed ; but also the runes of its clique-mates). By adopting this fake ; rune, we prevent that. ; The :term and :hyps we create for each rule are appropriate and survive into ; the final, accurate guess. But the :basic-ts and :vars fields are initially ; empty here and are filled out by the iteration. (cond ((null names) wrld) (t (let ((fn (car names))) (putprop-initial-type-prescriptions (cdr names) (putprop fn 'type-prescriptions (cons (make type-prescription :rune *fake-rune-for-anonymous-enabled-rule* :nume nil :term (mcons-term fn (formals fn wrld)) :hyps nil :backchain-limit-lst nil :basic-ts *ts-empty* :vars nil :corollary *t*) (getprop fn 'type-prescriptions nil 'current-acl2-world wrld)) wrld)))))) ; We now turn to the problem of iteratively guessing new ; type-prescriptions. The root of this guessing process is the ; computation of the type-set and formals returned by a term. (defun map-returned-formals-via-formals (formals pockets returned-formals) ; Formals is the formals list of a lambda expression, (lambda formals ; body). Pockets is a list in 1:1 correspondence with formals. Each ; pocket in pockets is a set of vars. Finally, returned-formals is a ; subset of formals. We return the set of vars obtained by unioning ; together the vars in those pockets corresponding to those in ; returned-formals. ; This odd little function is used to help determine the returned ; formals of a function defined in terms of a lambda-expression. ; Suppose foo is defined in terms of ((lambda formals body) arg1 ... ; argn) and we wish to determine the returned formals of that ; expression. We first determine the returned formals in each of the ; argi. That produces our pockets. Then we determine the returned ; formals of body -- note however that the formals returned by body ; are not the formals of foo but the formals of the lambda. The ; returned formals of body are our returned-formals. This function ; can then be used to convert the returned formals of body into ; returned formals of foo. (cond ((null formals) nil) ((member-eq (car formals) returned-formals) (union-eq (car pockets) (map-returned-formals-via-formals (cdr formals) (cdr pockets) returned-formals))) (t (map-returned-formals-via-formals (cdr formals) (cdr pockets) returned-formals)))) (defun map-type-sets-via-formals (formals ts-lst returned-formals) ; This is just like the function above except instead of dealing with ; a list of lists which are unioned together we deal with a list of ; type-sets which are ts-unioned. (cond ((null formals) *ts-empty*) ((member-eq (car formals) returned-formals) (ts-union (car ts-lst) (map-type-sets-via-formals (cdr formals) (cdr ts-lst) returned-formals))) (t (map-type-sets-via-formals (cdr formals) (cdr ts-lst) returned-formals)))) (defun vector-ts-union (ts-lst1 ts-lst2) ; Given two lists of type-sets of equal lengths we ts-union ; corresponding elements and return the resulting list. (cond ((null ts-lst1) nil) (t (cons (ts-union (car ts-lst1) (car ts-lst2)) (vector-ts-union (cdr ts-lst1) (cdr ts-lst2)))))) (defun map-cons-tag-trees (lst ttree) ; Cons-tag-tree every element of lst into ttree. (cond ((null lst) ttree) (t (map-cons-tag-trees (cdr lst) (cons-tag-trees (car lst) ttree))))) (defun type-set-and-returned-formals-with-rule1 (alist rule-vars type-alist ens wrld basic-ts vars-ts vars ttree) ; See type-set-with-rule1 for a slightly simpler version of this. ; Note: This function is really just a loop that finishes off the ; computation done by type-set-and-returned-formals-with-rule, below. ; It would be best not to try to understand this function until you ; have read that function and type-set-and-returned-formals. ; Alist maps variables in a type-prescription to terms. The context in which ; those terms occur is described by type-alist. Rule-vars is the list of :vars ; of the rule. ; The last four arguments are accumulators that will become four of the ; answers delivered by type-set-and-returned-formals-with-rule, i.e., ; a basic-ts, the type-set of a set of vars, the set of vars, and the ; justifying ttree. We assemble these four answers by sweeping over ; alist, considering each var and its image term. If the var is not ; in the rule-vars, we go on. If the var is in the rule-vars, then ; its image is a possible value of the term for which we are computing ; a type-set. If its image is a variable, we accumulate it and its ; type-set into vars and vars-ts. If its image is not a variable, we ; accumulate its type-set into basic-ts. ; The ttree returned is 'assumption-free (provided the initial ttree ; is also). (cond ((null alist) (mv basic-ts vars-ts vars type-alist ttree)) ((member-eq (caar alist) rule-vars) (mv-let (ts ttree) (type-set (cdar alist) nil nil type-alist ens wrld ttree nil nil) (let ((variablep-image (variablep (cdar alist)))) (type-set-and-returned-formals-with-rule1 (cdr alist) rule-vars type-alist ens wrld (if variablep-image basic-ts (ts-union ts basic-ts)) (if variablep-image (ts-union ts vars-ts) vars-ts) (if variablep-image (add-to-set-eq (cdar alist) vars) vars) ttree)))) (t (type-set-and-returned-formals-with-rule1 (cdr alist) rule-vars type-alist ens wrld basic-ts vars-ts vars ttree)))) (defun type-set-and-returned-formals-with-rule (tp term type-alist ens wrld ttree) ; This function is patterned after type-set-with-rule, which the ; reader might understand first. ; The ttree returned is 'assumption-free (provided the initial ttree ; and type-alist are also). (cond ((enabled-numep (access type-prescription tp :nume) ens) (mv-let (unify-ans unify-subst) (one-way-unify (access type-prescription tp :term) term) (cond (unify-ans (with-accumulated-persistence (access type-prescription tp :rune) (basic-ts vars-ts vars type-alist ttree) (not (and (ts= basic-ts *ts-unknown*) (ts= vars-ts *ts-empty*) (null vars))) (let* ((backchain-limit (backchain-limit wrld :ts)) (type-alist (extend-type-alist-with-bindings unify-subst nil nil type-alist nil ens wrld nil nil nil backchain-limit))) (mv-let (relieve-hyps-ans type-alist ttree) (type-set-relieve-hyps (access type-prescription tp :rune) term (access type-prescription tp :hyps) (access type-prescription tp :backchain-limit-lst) nil nil unify-subst type-alist nil ens wrld nil ttree nil nil backchain-limit 1) (cond (relieve-hyps-ans ; Subject to the conditions in ttree, we now know that the type set of term is ; either in :basic-ts or else that term is equal to the image under unify-subst ; of some var in the :vars of the rule. Our charter is to return five results: ; basic-ts, vars-ts, vars, type-alist and ttree. We do that with the ; subroutine below. It sweeps over the unify-subst, considering each vi and ; its image, ai. If ai is a variable, then it accumulates ai into the returned ; vars (which is initially nil below) and the type-set of ai into vars-ts ; (which is initially *ts-empty* below). If ai is not a variable, it ; accumulates the type-set of ai into basic-ts (which is initially :basic-ts ; below). (type-set-and-returned-formals-with-rule1 unify-subst (access type-prescription tp :vars) type-alist ens wrld (access type-prescription tp :basic-ts) *ts-empty* nil (push-lemma (access type-prescription tp :rune) ttree))) (t ; We could not establish the hyps of the rule. Thus, the rule tells us ; nothing about term. (mv *ts-unknown* *ts-empty* nil type-alist ttree))))))) (t ; The :term of the rule does not unify with our term. (mv *ts-unknown* *ts-empty* nil type-alist ttree))))) (t ; The rule is disabled. (mv *ts-unknown* *ts-empty* nil type-alist ttree)))) (defun type-set-and-returned-formals-with-rules (tp-lst term type-alist ens w ts vars-ts vars ttree) ; See type-set-with-rules for a simpler model of this function. We ; try to apply each type-prescription in tp-lst, "conjoining" the ; results into an accumulating type-set, ts, and vars (and its ; associated type-set, vars-ts). However, if a rule fails to change ; the accumulating answers, we ignore it. ; However, we cannot really conjoin two type-prescriptions and get a ; third. We do, however, deduce a valid conclusion. A rule ; essentially gives us a conclusion of the form (or basic-ts ; var-equations), where basic-ts is the proposition that the term is ; of one of several given types and var-equations is the proposition ; that the term is one of several given vars. Two rules therefore ; tell us (or basic-ts1 var-equations1) and (or basic-ts2 ; var-equations2). Both of these propositions are true. From them we ; deduce the truth ; (or (and basic-ts1 basic-ts2) ; (or var-equations1 var-equations2)). ; Note that we conjoin the basic type-sets but we disjoin the vars. The ; validity of this conclusion follows from the tautology ; (implies (and (or basic-ts1 var-equations1) ; (or basic-ts2 var-equations2)) ; (or (and basic-ts1 basic-ts2) ; (or var-equations1 var-equations2))). ; It would be nice if we could conjoin both sides, but that's not valid. ; Recall that we actually must also return the union of the type-sets ; of the returned vars. Since the "conjunction" of two rules leads us ; to union the vars together we just union their types together too. ; The ttree returned is 'assumption free provided the initial ttree and ; type-alist are also. (cond ((null tp-lst) (mv-let (ts1 ttree1) (type-set term nil nil type-alist ens w ttree nil nil) (let ((ts2 (ts-intersection ts1 ts))) (mv ts2 vars-ts vars (if (ts= ts2 ts) ttree ttree1))))) (t (mv-let (ts1 vars-ts1 vars1 type-alist1 ttree1) (type-set-and-returned-formals-with-rule (car tp-lst) term type-alist ens w ttree) (let* ((ts2 (ts-intersection ts1 ts)) (unchangedp (and (ts= ts2 ts) (equal type-alist type-alist1)))) ; If the type-set established by the new rule doesn't change (i.e., ; narrow) what we already know, we simply choose to ignore the new ; rule. If it does change, then ts2 is smaller and we have to union ; together what we know about the vars and report the bigger ttree. (type-set-and-returned-formals-with-rules (cdr tp-lst) term type-alist1 ens w ts2 (if unchangedp vars-ts (ts-union vars-ts1 vars-ts)) (if unchangedp vars (union-eq vars1 vars)) (if unchangedp ttree ttree1))))))) (mutual-recursion (defun type-set-and-returned-formals (term type-alist ens wrld ttree) ; Term is the if-normalized body of a defined function. The ; 'type-prescriptions property of that fn (and all of its peers in its mutually ; recursive clique) may or may not be nil. If non-nil, it may contain many ; enabled rules. (When guards were part of the logic, we computed the type-set ; of a newly defined function twice, once before and once after verifying its ; guards. So during the second pass, a valid rule was present.) Among the ; rules is one that is possibly unsound and represents our current guess at the ; type. We compute, from that guess, a "basic type-set" for term and a list of ; formals that might be returned by term. We also return the union of the ; type-sets of the returned formals and a ttree justifying all our work. An ; odd aspect of this ttree is that it will probably include the rune of the ; very rule we are trying to create, since its use in this process is ; essentially as an induction hypothesis. ; Terminology: Consider a term and a type-alist, and the basic ; type-set and returned formals as computed here. Let a "satisfying" ; instance of the term be an instance obtained by replacing each ; formal by an actual that has as its type-set a subtype of that of ; the corresponding formal under type-alist. Let the "returned ; actuals" of such an instance be the actuals corresponding to ; returned formals. We say the type set of such a satisfying instance ; of term is "described" by a basic type-set and some returned formals ; if the type-set of the instance is a subset of the union of the ; basic type-set and the type-sets of the returned actuals. Claim: ; The type-set of a satisfying instance of term is given by our ; answer. ; This function returns four results. The first is the basic type ; set computed. The third is the set of returned formals. The second ; one is the union of the type-sets of the returned formals. Thus, ; the type-set of the term can in fact be obtained by unioning together ; the first and second answers. However, top-level calls of this ; function are basically unconcerned with the second answer. The fourth ; answer is a ttree justifying all the type-set reasoning done so far, ; accumulated onto the initial ttree. ; We claim that if our computation produces the type-set and formals ; that the type-prescription alleges, then the type-prescription is a ; correct one. ; The function works by walking through the if structure of the body, ; using the normal assume-true-false to construct the governing ; type-alist for each output branch. Upon arriving at an output we ; compute the type set and returned formals for that branch. If the ; output is a quote or a call to an ACL2 primitive, we just use ; type-set. If the output is a call of a defun'd function, we ; interpret its type-prescription. ; The ttree returned is 'assumption-free provided the initial ttree ; and type-alist are also. ; Historical Plaque from Nqthm. ; In nqthm, the root of the guessing processing was DEFN-TYPE-SET, ; which was mutually recursive with DEFN-ASSUME-TRUE-FALSE. The ; following comment could be found at the entrance to the guessing ; process: ; ************************************************************* ; THIS FUNCTION WILL BE COMPLETELY UNSOUND IF TYPE-SET IS EVER ; REACHABLE FROM WITHIN IT. IN PARTICULAR, BOTH THE TYPE-ALIST AND ; THE TYPE-PRESCRIPTION FOR THE FN BEING PROCESSED ARE SET TO ONLY ; PARTIALLY ACCURATE VALUES AS THIS FN COMPUTES THE REAL TYPE-SET. ; ************************************************************* ; We now believe that this dreadful warning is an overstatement of the ; case. It is true that in nqthm the type-alist used in DEFN-TYPE-SET ; would cause trouble if it found its way into TYPE-SET, because it ; bound vars to "defn type-sets" (pairs of type-sets and variables) ; instead of to type-sets. But the fear of the inaccurate ; TYPE-PRESCRIPTIONs above is misplaced we think. We believe that if ; one guesses a type-prescription and then confirms that it accurately ; describes the function body, then the type-prescription is correct. ; Therefore, in ACL2, far from fencing type-set away from ; "defun-type-set" we use it explicitly. This has the wonderful ; advantage that we do not duplicate the type-set code (which is even ; worse in ACL2 than it was in nqthm). (cond ((variablep term) ; Term is a formal variable. We compute its type-set under ; type-alist. If it is completely unrestricted, then we will say that ; formal is sometimes returned. Otherwise, we will say that it is not ; returned. Once upon a time we always said it was returned. But the ; term (if (integerp x) (if (< x 0) (- x) x) 0) as occurs in ; integer-abs, then got the type-set "nonnegative integer or x" which ; meant that it effectively had the type-set unknown. ; Observe that the code below satisfies our Claim. If term' is a ; satisfying instance of this term, then we know that term' is in fact ; an actual being substituted for this formal. Since term' is ; satisfying, the type-set of that actual (i.e., term') is a subtype ; of ts, below. Thus, the type-set of term' is indeed described by ; our answer. (mv-let (ts ttree) (type-set term nil nil type-alist ens wrld ttree nil nil) (cond ((ts= ts *ts-unknown*) (mv *ts-empty* ts (list term) ttree)) (t (mv ts *ts-empty* nil ttree))))) ((fquotep term) ; Term is a constant. We return a basic type-set consisting of the ; type-set of term. Our Claim is true because the type-set of every ; instance of term is a subtype of the returned basic type-set is a ; subtype of the basic type-set. (mv-let (ts ttree) (type-set term nil nil type-alist ens wrld ttree nil nil) (mv ts *ts-empty* nil ttree))) ((flambda-applicationp term) ; Without loss of generality we address ourselves to a special case. ; Let term be ((lambda (...u...) body) ...arg...). Let the formals in ; term be x1, ..., xn. ; We compute a basic type-set, bts, some returned vars, vars, and the ; type-sets of the vars, vts, for a lambda application as follows. ; (1) For each argument, arg, obtain bts-arg, vts-arg, and vars-arg, ; which are the basic type-set, the variable type-set, and the ; returned variables with respect to the given type-alist. ; (2) Build a new type-alist, type-alist-body, by binding the formals ; of the lambda, (...u...), to the types of its arguments (...arg...). ; We know that the type of arg is the union of bts-arg and the types ; of those xi in vars-arg positions (which is to say, vts-arg). ; (3) Obtain bts-body, vts-body, and vars-body, by recursively ; processing body under type-alist-body. ; (4) Create the final bts by unioning bts-body and those of the ; bts-args in positions that are sometimes returned, as specified by ; vars-body. ; (5) Create the final vars by unioning together those of the ; vars-args in positions that are sometimes returned, as specified by ; vars-body. ; (6) Union together the types of the vars to create the final vts. ; We claim that the type-set of any instance of term that satisfies ; type-alist is described by the bts and vars computed above and that ; the vts computed above is the union of the the types of the vars ; computed. ; Now consider an instance, term', of term, in which the formals of ; term are mapped to some actuals and type-alist is satisfied. Then ; the type-set of each actual is a subtype of the type assigned each ; xi. Observe further that if term' is an instance of term satisfying ; type-alist then term' is ((lambda (...u...) body) ...arg'...), where ; arg' is an instance of arg satisfying type-alist. ; Thus, by induction, the type-set of arg' is a subtype of the union ; of bts-arg and the type-sets of those actuals in vars-arg positions. ; But the union of the type-sets of those actuals in vars-arg ; positions is a subtype of the union of the type-sets of the xi in ; vars-arg. Also observe that term' is equal, by lambda expansion, to ; body', where body' is the instance of body in which each u is ; replaced by the corresponding arg'. Note that body' is an instance ; of body satisfying type-alist-body: the type of arg' is a subtype of ; that assigned u in type-alist-body, because the type of arg' is a ; subtype of the union of bts-arg and the type-sets of the actuals in ; vars-arg positions, but the type assigned u in type-alist-body is ; the union of bts-arg and the type-sets of the xi in vars-arg. ; Therefore, by induction, we know that the type-set of body' is a ; subtype of bts-body and the type-sets of those arg' in vars-body ; positions. But the type-set of each arg' is a subtype of bts-arg ; unioned with the type-sets of the actuals in vars-arg positions. ; Therefore, when we union over the selected arg' we get a subtype of ; the union of the union of the selected bts-args and the union of the ; type-sets of the actuals in vars positions. By the associativity ; and commutativity of union, the bts and vars created in (4) and (5) ; are correct. (mv-let (bts-args vts-args vars-args ttree-args) (type-set-and-returned-formals-lst (fargs term) type-alist ens wrld) (mv-let (bts-body vts-body vars-body ttree) (type-set-and-returned-formals (lambda-body (ffn-symb term)) (zip-variable-type-alist (lambda-formals (ffn-symb term)) (pairlis$ (vector-ts-union bts-args vts-args) ttree-args)) ens wrld ttree) (declare (ignore vts-body)) (let* ((bts (ts-union bts-body (map-type-sets-via-formals (lambda-formals (ffn-symb term)) bts-args vars-body))) (vars (map-returned-formals-via-formals (lambda-formals (ffn-symb term)) vars-args vars-body)) (ts-and-ttree-lst (type-set-lst vars nil nil type-alist nil ens wrld nil nil (backchain-limit wrld :ts)))) ; Below we make unconventional use of map-type-sets-via-formals. ; Its first and third arguments are equal and thus every element of ; its second argument will be ts-unioned into the answer. This is ; just a hackish way to union together the type-sets of all the ; returned formals. (mv bts (map-type-sets-via-formals vars (strip-cars ts-and-ttree-lst) vars) vars (map-cons-tag-trees (strip-cdrs ts-and-ttree-lst) ttree)))))) ((eq (ffn-symb term) 'if) ; If by type-set reasoning we can see which way the test goes, we can ; clearly focus on that branch. So now we consider (if t1 t2 t3) where ; we don't know which way t1 will go. We compute the union of the ; respective components of the answers for t2 and t3. In general, the ; type-set of any instance of this if will be at most the union of the ; type-sets of the instances of t2 and t3. (In the instance, t1' might ; be decidable and a smaller type-set could be produced.) (mv-let (must-be-true must-be-false true-type-alist false-type-alist ts-ttree) (assume-true-false (fargn term 1) nil nil nil type-alist ens wrld nil nil nil) ; Observe that ts-ttree does not include ttree. If must-be-true and ; must-be-false are both nil, ts-ttree is nil and can thus be ignored. (cond (must-be-true (type-set-and-returned-formals (fargn term 2) true-type-alist ens wrld (cons-tag-trees ts-ttree ttree))) (must-be-false (type-set-and-returned-formals (fargn term 3) false-type-alist ens wrld (cons-tag-trees ts-ttree ttree))) (t (mv-let (basic-ts2 formals-ts2 formals2 ttree) (type-set-and-returned-formals (fargn term 2) true-type-alist ens wrld ttree) (mv-let (basic-ts3 formals-ts3 formals3 ttree) (type-set-and-returned-formals (fargn term 3) false-type-alist ens wrld ttree) (mv (ts-union basic-ts2 basic-ts3) (ts-union formals-ts2 formals-ts3) (union-eq formals2 formals3) ttree))))))) (t (let* ((fn (ffn-symb term)) (recog-tuple (most-recent-enabled-recog-tuple fn (global-val 'recognizer-alist wrld) ens))) (cond (recog-tuple (mv-let (ts ttree1) (type-set (fargn term 1) nil nil type-alist ens wrld ttree nil nil) (mv-let (ts ttree) (type-set-recognizer recog-tuple ts ttree1 ttree) (mv ts *ts-empty* nil ttree)))) (t (type-set-and-returned-formals-with-rules (getprop (ffn-symb term) 'type-prescriptions nil 'current-acl2-world wrld) term type-alist ens wrld *ts-unknown* *ts-empty* nil ttree))))))) (defun type-set-and-returned-formals-lst (lst type-alist ens wrld) (cond ((null lst) (mv nil nil nil nil)) (t (mv-let (basic-ts returned-formals-ts returned-formals ttree) (type-set-and-returned-formals (car lst) type-alist ens wrld nil) (mv-let (ans1 ans2 ans3 ans4) (type-set-and-returned-formals-lst (cdr lst) type-alist ens wrld) (mv (cons basic-ts ans1) (cons returned-formals-ts ans2) (cons returned-formals ans3) (cons ttree ans4))))))) ) (defun guess-type-prescription-for-fn-step (name body ens wrld ttree) ; This function takes one incremental step towards the type- prescription of ; name in wrld. Body is the normalized body of name. We assume that the ; current guess for a type-prescription for name is the car of the ; 'type-prescriptions property. That is, initialization has occurred and every ; iteration keeps the current guess at the front of the list. ; We get the type-set of and formals returned by body. We convert the two ; answers into a new type-prescription and replace the current car of the ; 'type-prescriptions property. ; We return the new world and an 'assumption-free ttree extending ttree. (let* ((ttree0 ttree) (old-type-prescriptions (getprop name 'type-prescriptions nil 'current-acl2-world wrld)) (tp (car old-type-prescriptions))) (mv-let (new-basic-type-set returned-vars-type-set new-returned-vars ttree) (type-set-and-returned-formals body nil ens wrld ttree) (declare (ignore returned-vars-type-set)) (cond ((ts= new-basic-type-set *ts-unknown*) ; Ultimately we will delete this rule. But at the moment we wish merely to ; avoid contaminating the ttree of the ongoing process by whatever we've ; done to derive this. (mv (putprop name 'type-prescriptions (cons (change type-prescription tp :basic-ts *ts-unknown* :vars nil) (cdr old-type-prescriptions)) wrld) ttree0)) (t (mv (putprop name 'type-prescriptions (cons (change type-prescription tp :basic-ts new-basic-type-set :vars new-returned-vars) (cdr old-type-prescriptions)) wrld) ttree)))))) (defconst *clique-step-install-interval* ; This interval represents how many type prescriptions are computed before ; installing the resulting intermediate world. The value below is merely ; heuristic, chosen with very little testing; we should feel free to change it. 30) (defun guess-and-putprop-type-prescription-lst-for-clique-step (names bodies ens wrld ttree interval state) ; Given a list of function names and their normalized bodies ; we take one incremental step toward the final type-prescription of ; each fn in the list. We return a world containing the new ; type-prescription for each fn and a ttree extending ttree. ; Note: During the initial coding of ACL2 the iteration to guess ; type-prescriptions was slightly different from what it is now. Back ; then we used wrld as the world in which we computed all the new ; type-prescriptions. We returned those new type-prescriptions to our ; caller who determined whether the iteration had repeated. If not, ; it installed the new type-prescriptions to generate a new wrld' and ; called us on that wrld'. ; It turns out that that iteration can loop indefinitely. Consider the ; mutually recursive nest of foo and bar where ; (defun foo (x) (if (consp x) (not (bar (cdr x))) t)) ; (defun bar (x) (if (consp x) (not (foo (cdr x))) nil)) ; Below are the successive type-prescriptions under the old scheme: ; iteration foo type bar type ; 0 {} {} ; 1 {T NIL} {NIL} ; 2 {T} {T NIL} ; 3 {T NIL} {NIL} ; ... ... ... ; Observe that the type of bar in round 1 is incomplete because it is ; based on the incomplete type of foo from round 0. This kind of ; incompleteness is supposed to be closed off by the iteration. ; Indeed, in round 2 bar has got its complete type-set. But the ; incompleteness has now been transferred to foo: the round 2 ; type-prescription for foo is based on the incomplete round 1 ; type-prescription of bar. Isn't this an elegant example? ; The new iteration computes the type-prescriptions in a strict linear ; order. So that the round 1 type-prescription of bar is based on the ; round 1 type-prescription of foo. (cond ((null names) (mv wrld ttree state)) (t (mv-let (erp val state) (update-w (int= interval 0) wrld) (declare (ignore erp val)) (mv-let (wrld ttree) (guess-type-prescription-for-fn-step (car names) (car bodies) ens wrld ttree) (guess-and-putprop-type-prescription-lst-for-clique-step (cdr names) (cdr bodies) ens wrld ttree (if (int= interval 0) *clique-step-install-interval* (1- interval)) state)))))) (defun cleanse-type-prescriptions (names type-prescriptions-lst def-nume rmp-cnt ens wrld installed-wrld ttree) ; Names is a clique of function symbols. Type-prescriptions-lst is in ; 1:1 correspondence with names and gives the value in wrld of the ; 'type-prescriptions property for each name. (We provide this just ; because our caller happens to be holding it.) This function should ; be called when we have completed the guessing process for the ; type-prescriptions for names. This function does two sanitary ; things: (a) it deletes the guessed rule if its :basic-ts is ; *ts-unknown*, and (b) in the case that the guessed ; rule is kept, it is given the rune and nume described by the Essay ; on the Assignment of Runes and Numes by DEFUNS. It is assumed that ; def-nume is the nume of (:DEFINITION fn), where fn is the car of ; names. We delete *ts-unknown* rules just to save type-set the ; trouble of relieving their hyps or skipping them. ; Rmp-cnt (which stands for "runic-mapping-pairs count") is the length of the ; 'runic-mapping-pairs entry for the functions in names (all of which have the ; same number of mapping pairs). We increment our def-nume by rmp-cnt on each ; iteration. ; This function knows that the defun runes for each name are laid out ; as follows, where i is def-nume: ; i (:definition name) ^ ; i+1 (:executable-counterpart name) ; i+2 (:type-prescription name) rmp-cnt=3 or 4 ; i+4 (:induction name) ; optional v ; Furthermore, we know that the nume of the :definition rune for the kth ; (0-based) name in names is def-nume+(k*rmp-cnt); that is, we assigned ; numes to the names in the same order as the names appear in names. (cond ((null names) (mv wrld ttree)) (t (let* ((fn (car names)) (lst (car type-prescriptions-lst)) (new-tp (car lst))) (mv-let (wrld ttree1) (cond ((ts= *ts-unknown* (access type-prescription new-tp :basic-ts)) (mv (putprop fn 'type-prescriptions (cdr lst) wrld) nil)) (t (mv-let (corollary ttree1) (convert-type-prescription-to-term new-tp ens ; We use the installed world (the one before cleansing started) for efficient ; handling of large mutual recursion nests. installed-wrld) (mv (putprop fn 'type-prescriptions (cons (change type-prescription new-tp :rune (list :type-prescription fn) :nume (+ 2 def-nume) :corollary corollary) (cdr lst)) wrld) ttree1)))) (cleanse-type-prescriptions (cdr names) (cdr type-prescriptions-lst) (+ rmp-cnt def-nume) rmp-cnt ens wrld installed-wrld (cons-tag-trees ttree1 ttree))))))) (defun guess-and-putprop-type-prescription-lst-for-clique (names bodies def-nume ens wrld ttree big-mutrec state) ; We assume that in wrld we find 'type-prescriptions for every fn in ; names. We compute new guesses at the type-prescriptions for each fn ; in names. If they are all the same as the currently stored ones we ; quit. Otherwise, we store the new guesses and iterate. Actually, ; when we quit, we cleanse the 'type-prescriptions as described above. ; We return the final wrld and a ttree extending ttree. Def-nume is ; the nume of (:DEFINITION fn), where fn is the first element of names ; and is used in the cleaning up to install the proper numes in the ; generated rules. (let ((old-type-prescriptions-lst (getprop-x-lst names 'type-prescriptions wrld))) (mv-let (wrld1 ttree state) (guess-and-putprop-type-prescription-lst-for-clique-step names bodies ens wrld ttree *clique-step-install-interval* state) (er-progn (update-w big-mutrec wrld1) (cond ((equal old-type-prescriptions-lst (getprop-x-lst names 'type-prescriptions wrld1)) (mv-let (wrld2 ttree) (cleanse-type-prescriptions names old-type-prescriptions-lst def-nume (length (getprop (car names) 'runic-mapping-pairs nil 'current-acl2-world wrld)) ens wrld wrld1 ttree) (er-progn ; Warning: Do not use set-w! here, because if we are in the middle of a ; top-level include-book, that will roll the world back to the start of that ; include-book. We have found that re-installing the world omits inclusion of ; the compiled files for subsidiary include-books (see description of bug fix ; in :doc note-2-9 (bug fixes)). (update-w big-mutrec wrld t) (update-w big-mutrec wrld2) (mv wrld2 ttree state)))) (t (guess-and-putprop-type-prescription-lst-for-clique names bodies def-nume ens wrld1 ttree big-mutrec state))))))) (defun get-normalized-bodies (names wrld) ; Return the normalized bodies for names in wrld. ; WARNING: We ignore the runes and hyps for the normalized bodies returned. So ; this function is probably only of interest when names are being introduced, ; where the 'def-bodies properties have been placed into wrld but no new ; :definition rules with non-nil :install-body fields have been proved for ; names. (cond ((endp names) nil) (t (cons (access def-body (def-body (car names) wrld) :concl) (get-normalized-bodies (cdr names) wrld))))) (defun putprop-type-prescription-lst (names subversive-p def-nume ens wrld ttree state) ; Names is a list of mutually recursive fns being introduced. We assume that ; for each fn in names we can obtain from wrld the 'formals and the normalized ; body (from 'def-bodies). Def-nume must be the nume assigned (:DEFINITION ; fn), where fn is the first element of names. See the Essay on the Assignment ; of Runes and Numes by DEFUNS. We compute type-prescriptions for each fn in ; names and store them. We return the new wrld and a ttree extending ttree ; justifying what we've done. ; This function knows that HIDE should not be given a ; 'type-prescriptions property. ; Historical Plaque for Versions Before 1.8 ; In 1.8 we "eliminated guards from the ACL2 logic." Prior to that guards were ; essentially hypotheses on the definitional equations. This complicated many ; things, including the guessing of type-prescriptions. After a function was ; known to be Common Lisp compliant we could recompute its type-prescription ; based on the fact that we knew that every subfunction in it would return its ; "expected" type. Here is a comment from that era, preserved for posterity. ; On Guards: In what way is the computed type-prescription influenced ; by the changing of the 'guards-checked property from nil to t? ; The key is illustrated by the following fact: type-set returns ; *ts-unknown* if called on (+ x y) with gc-flg nil but returns a ; subset of *ts-acl2-number* if called with gc-flg t. To put this into ; context, suppose that the guard for (fn x y) is (g x y) and that it ; is not known by type-set that (g x y) implies that both x and y are ; acl2-numberps. Suppose the body of fn is (+ x y). Then the initial ; type-prescription for fn, computed when the 'guards-checked property ; is nil, will have the basic-type-set *ts-unknown*. After the guards ; have been checked the basic type-set will be *ts-acl2-number*. (cond ((and (consp names) (eq (car names) 'hide) (null (cdr names))) (mv wrld ttree state)) (subversive-p ; We avoid storing a runic type-prescription rule for any subversive function, ; because our fixed-point algorithm assumes the the definition provably ; terminates, which may not be the case for subversive functions. ; Below is a series of two examples. The first is the simpler of the two, and ; shows the basic problem. It succeeds in Version_3.4. ; (encapsulate ; () ; ; (defun h (x) (declare (ignore x)) t) ; ; (in-theory (disable (:type-prescription h))) ; ; (local (in-theory (enable (:type-prescription h)))) ; ; (encapsulate (((f *) => *)) ; (local (defun f (x) (cdr x))) ; (defun g (x) ; (if (consp x) (g (f x)) (h x)))) ; ; (defthm atom-g ; (atom (g x)) ; :rule-classes nil) ; ) ; ; (defthm contradiction nil ; :hints (("Goal" :use ((:instance ; (:functional-instance ; atom-g ; (f identity) ; (g (lambda (x) ; (if (consp x) x t)))) ; (x '(a b)))))) ; :rule-classes nil) ; Our first solution was to erase type-prescription rules for subversive ; functions after the second pass through the encapsulate. While that dealt ; with the example above -- atom-g was no longer provable -- the problem was ; that the type-prescription rule can be used to normalize a non-subversive ; (indeed, non-recursive) definition later in the same encapsulate, before the ; type-prescription rule has been erased. The second example shows how that ; works: ; (encapsulate ; () ; ; (defun h (x) (declare (ignore x)) t) ; ; (in-theory (disable (:type-prescription h))) ; ; (local (in-theory (enable (:type-prescription h)))) ; ; (encapsulate (((f *) => *)) ; (local (defun f (x) (cdr x))) ; (defun g (x) ; (if (consp x) (g (f x)) (h x))) ; (defun k (x) ; (g x))) ; ; ; The following in-theory event is optional; it emphasizes that the problem is ; ; with the use of the bogus type-prescription for g in normalizing the body of ; ; k, not with the direct use of a type-prescription rule in subsequent ; ; proofs. ; (in-theory (disable (:type-prescription k) (:type-prescription g))) ; ; (defthm atom-k ; (atom (k x)) ; :rule-classes nil) ; ) ; ; (defthm contradiction nil ; :hints (("Goal" :use ((:instance ; (:functional-instance ; atom-k ; (f identity) ; (g (lambda (x) ; (if (consp x) x t))) ; (k (lambda (x) ; (if (consp x) x t)))) ; (x '(a b)))))) ; :rule-classes nil) (mv wrld ttree state)) (t (let ((bodies (get-normalized-bodies names wrld)) (big-mutrec (big-mutrec names))) (er-let* ((wrld1 (update-w big-mutrec (putprop-initial-type-prescriptions names wrld)))) (guess-and-putprop-type-prescription-lst-for-clique names bodies def-nume ens wrld1 ttree big-mutrec state)))))) ; So that finishes the type-prescription business. Now to level-no... (defun putprop-level-no-lst (names wrld) ; We compute the level-no properties for all the fns in names, assuming they ; have no such properties in wrld (i.e., we take advantage of the fact that ; when max-level-no sees a nil 'level-no it acts like it saw 0). Note that ; induction and rewriting do not use heuristics for 'level-no, so it seems ; reasonable not to recompute the 'level-no property when adding a :definition ; rule with non-nil :install-body value. We assume that we can get the ; 'recursivep and the 'def-bodies property of each fn in names from wrld. (cond ((null names) wrld) (t (let ((maximum (max-level-no (body (car names) t wrld) wrld))) (putprop-level-no-lst (cdr names) (putprop (car names) 'level-no (if (getprop (car names) 'recursivep nil 'current-acl2-world wrld) (1+ maximum) maximum) wrld)))))) ; Next we put the primitive-recursive-defun property (defun primitive-recursive-argp (var term wrld) ; Var is some formal of a recursively defined function. Term is the actual in ; the var position in a recursive call in the definition of the function. ; I.e., we are recursively replacing var by term in the definition. Is this ; recursion in the p.r. schema? Well, that is impossible to tell by just ; looking at the recursion, because we need to know that the tests governing ; the recursion are also in the scheme. In fact, we don't even check that; we ; just rely on the fact that the recursion was justified and so some governing ; test does the job. So, ignoring tests, what is a p.r. function? It is one ; in which every formal is replaced either by itself or by an application of a ; (nest of) primitive recursive destructors to itself. The primitive recursive ; destructors recognized here are all unary function symbols with level-no 0 ; (e.g., car, cdr, nqthm::sub1, etc) as well as terms of the form (+ & -n) and ; (+ -n &), where -n is negative. ; A consequence of this definition (before we turned 1+ into a macro) is that ; 1+ is a primitive recursive destructor! Thus, the classic example of a ; terminating function not in the classic p.r. scheme, ; (fn x y) = (if (< x y) (fn (1+ x) y) 0) ; is now in the "p.r." scheme. This is a crock! ; Where is this notion used? The detection that a function is "p.r." is made ; after its admittance during the defun principle. The appropriate flag is ; stored under the property 'primitive-recursive-defunp. This property is only ; used (as of this writing) by induction-complexity1, where we favor induction ; candidates suggested by non-"p.r." functions. Thus, the notion of "p.r." is ; entirely heuristic and only affects which inductions we choose. ; Why don't we define it correctly? That is, why don't we only recognize ; functions that recurse via car, cdr, etc.? The problem is the ; introduction of the "NQTHM" package, where we want NQTHM::SUB1 to be a p.r. ; destructor -- even in the defn of NQTHM::LESSP which must happen before we ; prove that NQTHM::SUB1 decreases according to NQTHM::LESSP. The only way to ; fix this, it seems, would be to provide a world global variable -- perhaps a ; new field in the acl2-defaults-table -- to specify which function symbols are ; to be considered p.r. destructors. We see nothing wrong with this solution, ; but it seems cumbersome at the moment. Thus, we adopted this hackish notion ; of "p.r." and will revisit the problem if and when we see counterexamples to ; the induction choices caused by this notion. (cond ((variablep term) (eq var term)) ((fquotep term) nil) (t (let ((fn (ffn-symb term))) (case fn (binary-+ (or (and (nvariablep (fargn term 1)) (fquotep (fargn term 1)) (rationalp (cadr (fargn term 1))) (< (cadr (fargn term 1)) 0) (primitive-recursive-argp var (fargn term 2) wrld)) (and (nvariablep (fargn term 2)) (fquotep (fargn term 2)) (rationalp (cadr (fargn term 2))) (< (cadr (fargn term 2)) 0) (primitive-recursive-argp var (fargn term 1) wrld)))) (otherwise (and (symbolp fn) (fargs term) (null (cdr (fargs term))) (= (get-level-no fn wrld) 0) (primitive-recursive-argp var (fargn term 1) wrld)))))))) (defun primitive-recursive-callp (formals args wrld) (cond ((null formals) t) (t (and (primitive-recursive-argp (car formals) (car args) wrld) (primitive-recursive-callp (cdr formals) (cdr args) wrld))))) (defun primitive-recursive-callsp (formals calls wrld) (cond ((null calls) t) (t (and (primitive-recursive-callp formals (fargs (car calls)) wrld) (primitive-recursive-callsp formals (cdr calls) wrld))))) (defun primitive-recursive-machinep (formals machine wrld) ; Machine is an induction machine for a singly recursive function with ; the given formals. We return t iff every recursive call in the ; machine has the property that every argument is either equal to the ; corresponding formal or else is a primitive recursive destructor ; nest around that formal. (cond ((null machine) t) (t (and (primitive-recursive-callsp formals (access tests-and-calls (car machine) :calls) wrld) (primitive-recursive-machinep formals (cdr machine) wrld))))) (defun putprop-primitive-recursive-defunp-lst (names wrld) ; The primitive-recursive-defun property of a function name indicates ; whether the function is defined in the primitive recursive schema -- ; or, to be precise, in a manner suggestive of the p.r. schema. We do ; not actually check for syntactic adherence to the rules and this ; property is of heuristic use only. See the comment in ; primitive-recursive-argp. ; We say a defun'd function is p.r. iff it is not recursive, or else it ; is singly recursive and every argument position of every recursive call ; is occupied by the corresponding formal or else a nest of primitive ; recursive destructors around the corresponding formal. ; Observe that our notion doesn't include any inspection of the tests ; governing the recursions and it doesn't include any check of the ; subfunctions used. E.g., the function that collects all the values of ; Ackerman's functions is p.r. if it recurses on cdr's. (cond ((null names) wrld) ((cdr names) wrld) ((primitive-recursive-machinep (formals (car names) wrld) (getprop (car names) 'induction-machine nil 'current-acl2-world wrld) wrld) (putprop (car names) 'primitive-recursive-defunp t wrld)) (t wrld))) ; Onward toward defuns... Now we generate the controller-alists. (defun make-controller-pocket (formals vars) ; Given the formals of a fn and a measured subset, vars, of formals, ; we generate a controller-pocket for it. A controller pocket is a ; list of t's and nil's in 1:1 correspondence with the formals, with ; t in the measured slots. (cond ((null formals) nil) (t (cons (if (member (car formals) vars) t nil) (make-controller-pocket (cdr formals) vars))))) (defun make-controller-alist1 (names wrld) ; Given a clique of recursive functions, we return the controller alist built ; for the 'justification. A controller alist is an alist that maps fns in the ; clique to controller pockets. The controller pockets describe the measured ; arguments in a justification. We assume that all the fns in the clique have ; been justified (else none would be justified). ; This function should not be called on a clique consisting of a single, ; non-recursive fn (because it has no justification). (cond ((null names) nil) (t (cons (cons (car names) (make-controller-pocket (formals (car names) wrld) (access justification (getprop (car names) 'justification '(:error "See MAKE-CONTROLLER-ALIST1.") 'current-acl2-world wrld) :subset))) (make-controller-alist1 (cdr names) wrld))))) (defun make-controller-alist (names wrld) ; We store a controller-alists property for every recursive fn in names. We ; assume we can get the 'formals and the 'justification for each fn from wrld. ; If there is a fn with no 'justification, it means the clique consists of a ; single non-recursive fn and we store no controller-alists. We generate one ; controller pocket for each fn in names. ; The controller-alist associates a fn in the clique to a controller pocket. A ; controller pocket is a list in 1:1 correspondence with the formals of the fn ; with a t in slots that are controllers. The controllers assigned for the fns ; in the clique by a given controller-alist were used jointly in the ; justification of the clique. (and (getprop (car names) 'justification nil 'current-acl2-world wrld) (make-controller-alist1 names wrld))) (defun max-nume-exceeded-error (ctx) (er hard ctx "ACL2 assumes that no nume exceeds ~x0. It is very surprising that ~ this bound is about to be exceeded. We are causing an error because ~ for efficiency, ACL2 assumes this bound is never exceeded. Please ~ contact the ACL2 implementors with a request that this assumption be ~ removed from enabled-numep." (fixnum-bound))) (defun putprop-defun-runic-mapping-pairs1 (names def-nume tp-flg ind-flg wrld) ; Names is a list of function symbols. For each fn in names we store some ; runic mapping pairs. We always create (:DEFINITION fn) and (:EXECUTABLE- ; COUNTERPART fn). If tp-flg is t, we create (:TYPE-PRESCRIPTION fn). If ; ind-flg is t we create (:INDUCTION fn). However, ind-flg is t only if tp-flg ; is t (that is, tp-flg = nil and ind-flg = t never arises). Thus, we may ; store 2 (tp-flg = nil; ind-flg = nil), 3 (tp-flg = t; ind-flg = nil), or 4 ; (tp-flg = t; ind-flg = t) runes. As of this writing, we never call this ; function with tp-flg nil but ind-flg t and the function is not prepared for ; that possibility. ; WARNING: Don't change the layout of the runic-mapping-pairs without ; considering all the places that talk about the Essay on the Assignment of ; Runes and Numes by DEFUNS. (cond ((null names) wrld) (t (putprop-defun-runic-mapping-pairs1 (cdr names) (+ 2 (if tp-flg 1 0) (if ind-flg 1 0) def-nume) tp-flg ind-flg (putprop (car names) 'runic-mapping-pairs (list* (cons def-nume (list :DEFINITION (car names))) (cons (+ 1 def-nume) (list :EXECUTABLE-COUNTERPART (car names))) (if tp-flg (list* (cons (+ 2 def-nume) (list :TYPE-PRESCRIPTION (car names))) (if ind-flg (list (cons (+ 3 def-nume) (list :INDUCTION (car names)))) nil)) nil)) wrld))))) (defun putprop-defun-runic-mapping-pairs (names tp-flg wrld) ; Essay on the Assignment of Runes and Numes by DEFUNS ; Names is a clique of mutually recursive function names. For each ; name in names we store a 'runic-mapping-pairs property. Each name ; gets either four (tp-flg = t) or two (tp-flg = nil) mapping pairs: ; ((n . (:definition name)) ; (n+1 . (:executable-counterpart name)) ; (n+2 . (:type-prescription name)) ; only if tp-flg ; (n+3 . (:induction name))) ; only if tp-flg and name is ; ; recursively defined ; where n is the next available nume. Important aspects to this ; include: ; * Fn-rune-nume knows where the :definition and :executable-counterpart ; runes are positioned. ; * Several functions (e.g. augment-runic-theory) exploit the fact ; that the mapping pairs are ordered ascending. ; * function-theory-fn1 knows that if the token of the first rune in ; the 'runic-mapping-pairs is not :DEFINITION then the base symbol ; is not a function symbol. ; * Get-next-nume implicitly exploits the fact that the numes are ; consecutive integers -- it adds the length of the list to ; the first nume to get the next available nume. ; * Cleanse-type-prescriptions knows that the same number of numes are ; consumed by each function in a DEFUNS. We have consistently used ; the formal parameter def-nume when we were enumerating numes for ; definitions. ; * Convert-theory-to-unordered-mapping-pairs1 knows that if the first rune in ; the list is a :definition rune, then the length of this list is 4 if and ; only if the list contains an :induction rune, in which case that rune is ; last in the list. ; In short, don't change the layout of this property unless you ; inspect every occurrence of 'runic-mapping-pairs in the system! ; (Even that won't find the def-nume uses.) Of special note is the ; fact that all non-constrained function symbols are presumed to have ; the same layout of 'runic-mapping-pairs as shown here. Constrained ; symbols have a nil 'runic-mapping-pairs property. ; We do not allocate the :type-prescription or :induction runes or their numes ; unless tp-flg is non-nil. This way we can use this same function to ; initialize the 'runic-mapping-pairs for primitives like car and cdr, without ; wasting runes and numes. We like reusing this function for that purpose ; because it isolates the place we create the 'runic-mapping-pairs for ; functions. (let ((next-nume (get-next-nume wrld))) (prog2$ (or (<= (the-fixnum next-nume) (- (the-fixnum (fixnum-bound)) (the-fixnum (* (the-fixnum 4) (the-fixnum (length names)))))) (max-nume-exceeded-error 'putprop-defun-runic-mapping-pairs)) (putprop-defun-runic-mapping-pairs1 names next-nume tp-flg (and tp-flg (getprop (car names) 'recursivep nil 'current-acl2-world wrld)) wrld)))) ; Before completing the implementation of defun we turn to the implementation ; of the verify-guards event. The idea is that one calls (verify-guards name) ; and we will generate the guard conditions for all the functions in the ; mutually recursive clique with name, prove them, and then exploit those ; proofs by resetting their symbol-classs. This process is optionally available ; as part of the defun event and hence we must define it before defun. ; While reading this code it is best to think of ourselves as having completed ; defun. Imagine a wrld in which a defun has just been done: the ; 'unnormalized-body is b, the unnormalized 'guard is g, the 'symbol-class is ; :ideal. The user then calls (verify-guards name) and we want to prove that ; every guard encountered in the mutually recursive clique containing name is ; satisfied. ; We have to collect every subroutine mentioned by any member of the clique and ; check that its guards have been checked. We cause an error if not. Once we ; have checked that all the subroutines have had their guards checked, we ; generate the guard clauses for the new functions. (defun eval-ground-subexpressions-lst-lst (lst-lst ens wrld state ttree) (cond ((null lst-lst) (mv nil nil ttree)) (t (mv-let (flg1 x ttree) (eval-ground-subexpressions-lst (car lst-lst) ens wrld state ttree) (mv-let (flg2 y ttree) (eval-ground-subexpressions-lst-lst (cdr lst-lst) ens wrld state ttree) (mv (or flg1 flg2) (if (or flg1 flg2) (cons x y) lst-lst) ttree)))))) (defun guard-clauses+ (term debug-info stobj-optp clause ens wrld state ttree) (mv-let (clause-lst0 ttree) (guard-clauses term debug-info stobj-optp clause wrld ttree) (mv-let (flg clause-lst ttree) (eval-ground-subexpressions-lst-lst clause-lst0 ens wrld state ttree) (declare (ignore flg)) (mv clause-lst ttree)))) (defun guard-clauses-for-body (hyp-segments body debug-info stobj-optp ens wrld state ttree) ; Hyp-segments is a list of clauses derived from the guard for body. We ; generate the guard clauses for the unguarded body, body, under each of the ; different hyp segments. We return a clause set and a ttree justifying all ; the simplification and extending ttree. ; Name is nil unless we are in a mutual-recursion, in which case it is the name ; of the function associated with the given body. (cond ((null hyp-segments) (mv nil ttree)) (t (mv-let (cl-set1 ttree) (guard-clauses+ body debug-info stobj-optp (car hyp-segments) ens wrld state ttree) (mv-let (cl-set2 ttree) (guard-clauses-for-body (cdr hyp-segments) body debug-info stobj-optp ens wrld state ttree) (mv (conjoin-clause-sets+ debug-info cl-set1 cl-set2) ttree)))))) (defun guard-clauses-for-fn (name debug-p ens wrld state ttree) ; Given a function name we generate the clauses that establish that ; all the guards in both the unnormalized guard and unnormalized body are ; satisfied. While processing the guard we assume nothing. But we ; generate the guards for the unnormalized body under each of the ; possible guard-hyp-segments derived from the assumption of the ; normalized 'guard. We return the resulting clause set and an extension ; of ttree justifying it. The resulting ttree is 'assumption-free, ; provided the initial ttree is also. ; Notice that in the two calls of guard below, used while computing ; the guard conjectures for the guard of name itself, we use stobj-opt ; = nil. (mv-let (cl-set1 ttree) (guard-clauses+ (guard name nil wrld) (and debug-p `(:guard (:guard ,name))) nil nil ens wrld state ttree) (mv-let (normal-guard ttree) (normalize (guard name nil wrld) t ; iff-flg nil ; type-alist ens wrld ttree) (mv-let (changedp body ttree) (eval-ground-subexpressions (getprop name 'unnormalized-body '(:error "See GUARD-CLAUSES-FOR-FN.") 'current-acl2-world wrld) ens wrld state ttree) (declare (ignore changedp)) (let ((hyp-segments ; Should we expand lambdas here? I say ``yes,'' but only to be ; conservative with old code. Perhaps we should change the t to nil? (clausify (dumb-negate-lit normal-guard) nil t (sr-limit wrld)))) (mv-let (cl-set2 ttree) (guard-clauses-for-body hyp-segments body (and debug-p `(:guard (:body ,name))) ; Observe that when we generate the guard clauses for the body we optimize ; the stobj recognizers away, provided the named function is executable. (not (eq (getprop name 'non-executablep nil 'current-acl2-world wrld) t)) ens wrld state ttree) (mv-let (type-clauses ttree) (guard-clauses-for-body hyp-segments (fcons-term* 'insist (getprop name 'split-types-term *t* 'current-acl2-world wrld)) (and debug-p `(:guard (:type ,name))) nil ; stobj-optp: no clear reason for setting this to t ens wrld state ttree) (let ((cl-set2 (if type-clauses ; optimization (conjoin-clause-sets+ debug-p type-clauses cl-set2) cl-set2))) (mv (conjoin-clause-sets+ debug-p cl-set1 cl-set2) ttree))))))))) (defun guard-clauses-for-clique (names debug-p ens wrld state ttree) ; Given a mutually recursive clique of fns we generate all of the ; guard conditions for every function in the clique and return that ; set of clauses and a ttree extending ttree and justifying its ; construction. The resulting ttree is 'assumption-free, provided the ; initial ttree is also. (cond ((null names) (mv nil ttree)) (t (mv-let (cl-set1 ttree) (guard-clauses-for-fn (car names) debug-p ens wrld state ttree) (mv-let (cl-set2 ttree) (guard-clauses-for-clique (cdr names) debug-p ens wrld state ttree) (mv (conjoin-clause-sets+ debug-p cl-set1 cl-set2) ttree)))))) ; That completes the generation of the guard clauses. We will prove ; them with prove. (defun print-verify-guards-msg (names col state) ; Note that names is either a singleton list containing a theorem name ; or is a mutually recursive clique of function names. ; This function increments timers. Upon entry, the accumulated time ; is charged to 'other-time. The time spent in this function is ; charged to 'print-time. (cond ((ld-skip-proofsp state) state) (t (pprogn (increment-timer 'other-time state) (mv-let (col state) (io? event nil (mv col state) (col names) (fmt1 "~&0 ~#0~[is~/are~] compliant with Common Lisp.~|" (list (cons #\0 names)) col (proofs-co state) state nil) :default-bindings ((col 0))) (declare (ignore col)) (increment-timer 'print-time state)))))) (defun collect-ideals (names wrld acc) (cond ((null names) acc) ((eq (symbol-class (car names) wrld) :ideal) (collect-ideals (cdr names) wrld (cons (car names) acc))) (t (collect-ideals (cdr names) wrld acc)))) (defun collect-non-ideals (names wrld) (cond ((null names) nil) ((eq (symbol-class (car names) wrld) :ideal) (collect-non-ideals (cdr names) wrld)) (t (cons (car names) (collect-non-ideals (cdr names) wrld))))) (defun collect-non-common-lisp-compliants (names wrld) (cond ((null names) nil) ((eq (symbol-class (car names) wrld) :common-lisp-compliant) (collect-non-common-lisp-compliants (cdr names) wrld)) (t (cons (car names) (collect-non-common-lisp-compliants (cdr names) wrld))))) (defun all-fnnames1-exec (flg x acc) ; Keep this in sync with all-fnnames1. (cond (flg ; x is a list of terms (cond ((null x) acc) (t (all-fnnames1-exec nil (car x) (all-fnnames1-exec t (cdr x) acc))))) ((variablep x) acc) ((fquotep x) acc) ((flambda-applicationp x) (all-fnnames1-exec nil (lambda-body (ffn-symb x)) (all-fnnames1-exec t (fargs x) acc))) ((eq (ffn-symb x) 'return-last) (cond ((equal (fargn x 1) '(quote mbe1-raw)) (all-fnnames1-exec nil (fargn x 2) acc)) ((and (equal (fargn x 1) '(quote ec-call1-raw)) (nvariablep (fargn x 3)) (not (fquotep (fargn x 3))) (not (flambdap (ffn-symb (fargn x 3))))) (all-fnnames1-exec t (fargs (fargn x 3)) acc)) (t (all-fnnames1-exec t (fargs x) (add-to-set-eq (ffn-symb x) acc))))) (t (all-fnnames1-exec t (fargs x) (add-to-set-eq (ffn-symb x) acc))))) (defmacro all-fnnames-exec (term) `(all-fnnames1-exec nil ,term nil)) (defun chk-common-lisp-compliant-subfunctions (names0 names terms wrld str ctx state) ; Assume we are defining (or have defined) names with bodies or guards of terms ; (1:1 correspondence). We wish to make the definitions ; :common-lisp-compliant. Then we insist that every function used in terms ; other than names0 be :common-lisp-compliant. Str is a string used in our ; error message and is "guard", "split-types expression", "body" or "auxiliary ; function". Note that this function is used by chk-acceptable-defuns and by ; chk-acceptable-verify-guards and chk-stobj-field-descriptor. In the first ; usage, names have not been defined yet; in the other two they have. So be ; careful about using wrld to get properties of names. (cond ((null names) (value nil)) (t (let ((bad (collect-non-common-lisp-compliants (set-difference-eq (all-fnnames-exec (car terms)) names0) wrld))) (cond (bad (er soft ctx "The ~@0 for ~x1 calls the function~#2~[ ~&2~/s ~&2~], the ~ guards of which have not yet been verified. See :DOC ~ verify-guards." str (car names) bad)) (t (chk-common-lisp-compliant-subfunctions names0 (cdr names) (cdr terms) wrld str ctx state))))))) (defun chk-acceptable-verify-guards-formula (name x ctx wrld state) (mv-let (erp term bindings state) (translate1 x :stobjs-out '((:stobjs-out . :stobjs-out)) t ; known-stobjs ctx wrld state) (declare (ignore bindings)) (cond ((and erp (null name)) (mv-let (erp val state) (state-global-let* ((inhibit-output-lst *valid-output-names*)) (mv-let (erp term bindings state) (translate1 x t nil t ctx wrld state) (declare (ignore bindings)) (mv erp term state))) (declare (ignore val)) (cond (erp ; translation for formulas fails, so rely on previous error (silent-error state)) (t (er soft ctx "The guards for the given formula cannot be verified it ~ has the wrong syntactic form for evaluation, perhaps ~ due to multiple-value or stobj restrictions. See :DOC ~ verify-guards."))))) (erp (er soft ctx "The guards for ~x0 cannot be verified because its formula ~ has the wrong syntactic form for evaluation, perhaps due to ~ multiple-value or stobj restrictions. See :DOC ~ verify-guards." (or name x))) ((collect-non-common-lisp-compliants (all-fnnames-exec term) wrld) (er soft ctx "The formula ~#0~[named ~x1~/~x1~] contains a call of the ~ function~#2~[ ~&2~/s ~&2~], the guards of which have not yet ~ been verified. See :DOC verify-guards." (if name 0 1) (or name x) (collect-non-common-lisp-compliants (all-fnnames-exec term) wrld))) (t (value (cons :term term)))))) (defun chk-acceptable-verify-guards (name ctx wrld state) ; We check that name is acceptable input for verify-guards. We return either ; the list of names in the clique of name (if name and every peer in the clique ; is :ideal and every subroutine of every peer is :common-lisp-compliant), the ; symbol 'redundant (if name and every peer is :common-lisp-compliant), or ; cause an error. ; One might wonder when two peers in a clique can have different symbol-classs, ; e.g., how is it possible (as implied above) for name to be :ideal but for one ; of its peers to be :common-lisp-compliant or :program? Redefinition. For ; example, the clique could have been admitted as :logic and then later one ; function in it redefined as :program. Because redefinition invalidates the ; system, we could do anything in this case. What we choose to do is to cause ; an error and say you can't verify the guards of any of the functions in the ; nest. (er-let* ((symbol-class (cond ((symbolp name) (value (symbol-class name wrld))) (t (er soft ctx "~x0 is not a symbol. See :DOC verify-guards." name))))) (cond ((eq symbol-class :common-lisp-compliant) (value 'redundant)) ((getprop name 'theorem nil 'current-acl2-world wrld) ; Theorems are of either symbol-class :ideal or :common-lisp-compliant. (er-progn (chk-acceptable-verify-guards-formula name (getprop name 'untranslated-theorem nil 'current-acl2-world wrld) ctx wrld state) (value (list name)))) ((function-symbolp name wrld) (case symbol-class (:program (er soft ctx "~x0 is :program. Only :logic functions can have their guards ~ verified. See :DOC verify-guards." name)) (:ideal (let* ((recp (getprop name 'recursivep nil 'current-acl2-world wrld)) (names (cond ((null recp) (list name)) (t recp))) (non-ideal-names (collect-non-ideals names wrld))) (cond (non-ideal-names (er soft ctx "One or more of the mutually-recursive peers of ~x0 ~ either was not defined in :logic mode or has already ~ had its guards verified. The offending function~#1~[ ~ is~/s are~] ~&1. We thus cannot verify the guards of ~ ~x0. This situation can arise only through ~ redefinition." name non-ideal-names)) (t (er-progn (chk-common-lisp-compliant-subfunctions names names (guard-lst names nil wrld) wrld "guard" ctx state) (chk-common-lisp-compliant-subfunctions names names (getprop-x-lst names 'unnormalized-body wrld) wrld "body" ctx state) (value names)))))) (otherwise ; the symbol-class :common-lisp-compliant is handled above (er soft ctx "Implementation error: Unexpected symbol-class, ~x0, for the ~ function symbol ~x1." symbol-class name)))) (t (let ((fn (deref-macro-name name (macro-aliases wrld)))) (er soft ctx "~x0 is not a theorem name or a function symbol in the current ~ ACL2 world. ~@1" name (cond ((eq fn name) "See :DOC verify-guards.") (t (msg "Note that ~x0 is a macro-alias for ~x1. ~ Consider calling verify-guards with argument ~x1 ~ instead, or use verify-guards+. See :DOC ~ verify-guards, see :DOC verify-guards+, and see ~ :DOC macro-aliases-table." name fn))))))))) (defun guard-obligation-clauses (x guard-debug ens wrld state) ; X is either a list of names corresponding to a defun, mutual-recursion nest, ; or defthm, or else of the form (:term . y) where y is a translated term. ; Returns a set of clauses justifying the guards for y in the latter case, else ; x, together with an assumption-free tag-tree justifying that set of clauses ; and the new state. (Do not view this as an error triple!) (mv-let (cl-set cl-set-ttree state) (cond ((and (consp x) (eq (car x) :term)) (mv-let (cl-set cl-set-ttree) (guard-clauses+ (cdr x) (and guard-debug :top-level) nil ;stobj-optp = nil nil ens wrld state nil) (mv cl-set cl-set-ttree state))) ((and (consp x) (null (cdr x)) (getprop (car x) 'theorem nil 'current-acl2-world wrld)) (mv-let (cl-set cl-set-ttree) (guard-clauses+ (getprop (car x) 'theorem nil 'current-acl2-world wrld) (and guard-debug (car x)) nil ;stobj-optp = nil nil ens wrld state nil) (mv cl-set cl-set-ttree state))) (t (mv-let (erp pair state) (state-global-let* ((guard-checking-on ; It is important to turn on guard-checking here. If we avoid this binding, ; then we can get a hard Lisp error as follows, because a call of ; eval-ground-subexpressions from guard-clauses-for-fn should have failed (due ; to a guard violation) but didn't. ; (set-guard-checking nil) ; (defun foo (x) ; (declare (xargs :guard (consp x))) ; (cons x (car 3))) ; (set-guard-checking t) ; (foo '(a b)) ; Exercise (not yet done): Modify the example by using a recursive definition ; so that we can verify guards if we bind guard-checking-on to anything other ; than :all here, and then get a hard Lisp error as above. :all)) (mv-let (cl-set cl-set-ttree) (guard-clauses-for-clique x (cond ((null guard-debug) nil) ((cdr x) 'mut-rec) (t t)) ens wrld state nil) (value (cons cl-set cl-set-ttree)))) (declare (ignore erp)) (mv (car pair) (cdr pair) state)))) ; Cl-set-ttree is 'assumption-free. (mv-let (cl-set cl-set-ttree) (clean-up-clause-set cl-set ens wrld cl-set-ttree state) ; Cl-set-ttree is still 'assumption-free. (mv cl-set cl-set-ttree state)))) (defun guard-obligation (x guard-debug ctx state) ":Doc-Section Other the guard proof obligation~/ ~l[verify-guards], and ~pl[guard] for a discussion of guards. Also ~pl[verify-guards-formula] for a utility provided for viewing the guard proof obligation, without proof. ~c[Guard-obligation] is a lower level function for use in system programs, not typically appropriate for most ACL2 users. If you simply want to see the guard proof obligations, ~pl[verify-guards-formula]. ~bv[] Example Form: (guard-obligation 'foo nil 'top-level state) (guard-obligation '(if (consp x) (foo (car x)) t) nil 'my-function state)~/ General Forms: (guard-obligation name guard-debug ctx state) (guard-obligation term guard-debug ctx state) ~ev[] where the first argument is either the name of a function or theorem or is a non-variable term that may be in untranslated form; ~c[guard-debug] is typically ~c[nil] but may be ~c[t] (~pl[guard-debug]); ~c[ctx] is a context (typically, a symbol used in error and warning messages); and ~ilc[state] references the ACL2 ~il[state]. If you want to obtain the formula but you don't care about the so-called ``tag tree'': ~bv[] (mv-let (erp val state) (guard-obligation x guard-debug 'top-level state) (if erp ( .. code for handling error case, e.g., name is undefined .. ) (let ((cl-set (cadr val))) ; to be proved for guard verification ( .. code using cl-set, which is a list of clauses, implicitly conjoined, each of which is viewed as a disjunction .. )))) ~ev[] The form ~c[(guard-obligation x guard-debug ctx state)] evaluates to a triple ~c[(mv erp val state)], where ~c[erp] is ~c[nil] unless there is an error, and ~ilc[state] is the ACL2 state. Suppose ~c[erp] is ~c[nil]. Then ~c[val] is the keyword ~c[:redundant] if the corresponding ~ilc[verify-guards] event would be redundant; ~pl[redundant-events]. Otherwise, ~c[val] is a tuple ~c[(list* names cl-set ttree)], where: ~c[names] is ~c[(cons :term xt)] if ~c[x] is not a variable, where ~c[xt] is the translated form of ~c[x]; and otherwise is a list containing ~c[x] along with, if ~c[x] is defined in a ~c[mutual-recursion], any other functions defined in the same ~ilc[mutual-recursion] nest; ~c[cl-set] is a list of lists of terms, viewed as a conjunction of clauses (each viewed (as a disjunction); and ~c[ttree] is an assumption-free tag-tree that justifies cl-set. (The notion of ``tag-tree'' may probably be ignored except for system developers.) ~c[Guard-obligation] is typically used for function names or non-variable terms, but as for ~ilc[verify-guards], it may also be applied to theorem names. See the source code for ~ilc[verify-guards-formula] for an example of how to use ~c[guard-obligation].~/" (let* ((wrld (w state)) (namep (and (symbolp x) (not (keywordp x)) (not (defined-constant x wrld))))) (er-let* ((y (cond (namep (chk-acceptable-verify-guards x ctx wrld state)) (t (chk-acceptable-verify-guards-formula nil x ctx wrld state))))) (cond ((and namep (eq y 'redundant)) (value :redundant)) (t (mv-let (cl-set cl-set-ttree state) (guard-obligation-clauses y guard-debug (ens state) wrld state) (value (list* y cl-set cl-set-ttree)))))))) (defun prove-guard-clauses-msg (names cl-set cl-set-ttree displayed-goal verify-guards-formula-p state) (let ((simp-phrase (tilde-*-simp-phrase cl-set-ttree))) (cond ((null cl-set) (fmt "The guard conjecture for ~#0~[~&1~/the given term~] is trivial to ~ prove~#2~[~/, given ~*3~].~@4" (list (cons #\0 (if names 0 1)) (cons #\1 names) (cons #\2 (if (nth 4 simp-phrase) 1 0)) (cons #\3 simp-phrase) (cons #\4 (if verify-guards-formula-p "~|" " "))) (proofs-co state) state nil)) (t (pprogn (fms "The non-trivial part of the guard conjecture for ~#0~[~&1~/the ~ given term~]~#2~[~/, given ~*3,~] is~%~%Goal~%~Q45." (list (cons #\0 (if names 0 1)) (cons #\1 names) (cons #\2 (if (nth 4 simp-phrase) 1 0)) (cons #\3 simp-phrase) (cons #\4 displayed-goal) (cons #\5 (or (term-evisc-tuple nil state) (and (gag-mode) (let ((tuple (gag-mode-evisc-tuple state))) (cond ((eq tuple t) (term-evisc-tuple t state)) (t tuple))))))) (proofs-co state) state nil) (mv 0 ; don't care state)))))) (defmacro verify-guards-formula (x &key guard-debug &allow-other-keys) ":Doc-Section Other view the guard proof obligation, without proving it~/ ~l[verify-guards] and ~pl[guard] for a discussion of guards. This utility is provided for viewing a guard proof obligation, without doing a proof. ~bv[] Example Forms: (verify-guards-formula foo) (verify-guards-formula foo :guard-debug t) (verify-guards-formula foo :otf-flg dont-care :xyz whatever) (verify-guards-formula (+ (foo x) (bar y)) :guard-debug t) ~ev[] ~c[Verify-guards-formula] allows all keywords, but only pays attention to ~c[:guard-debug], which has the same effect as in ~ilc[verify-guards] (~pl[guard-debug]). Apply ~c[verify-guards-formula] to a name just as you would use ~ilc[verify-guards], but when you only want to view the formula rather than creating an event. If the first argument is not a symbol, then it is treated as the body of a ~ilc[defthm] event for which you want the guard proof obligation. ~l[guard-obligation] if you want to obtain guard proof obligations for use in a program.~/~/" `(er-let* ((tuple (guard-obligation ',x ',guard-debug 'verify-guards-formula state))) (cond ((eq tuple :redundant) (value :redundant)) (t (let ((names (car tuple)) (displayed-goal (prettyify-clause-set (cadr tuple) (let*-abstractionp state) (w state))) (cl-set-ttree (cddr tuple))) (mv-let (col state) (prove-guard-clauses-msg (if (and (consp names) (eq (car names) :term)) nil names) (cadr tuple) cl-set-ttree displayed-goal t state) (declare (ignore col)) (value :invisible))))))) (defun prove-guard-clauses (names hints otf-flg guard-debug ctx ens wrld state) ; Names is either a clique of mutually recursive functions or else a singleton ; list containing a theorem name. We generate and attempt to prove the guard ; conjectures for the formulas in names. We generate suitable output ; explaining what we are doing. This is an error/value/state producing ; function that returns a pair of the form (col . ttree) when non-erroneous. ; Col is the column in which the printer is left. We always output something ; and we always leave the printer ready to start a new sentence. Ttree is a ; tag-tree describing the proof. ; This function increments timers. Upon entry, any accumulated time ; is charged to 'other-time. The printing done herein is charged ; to 'print-time and the proving is charged to 'prove-time. (cond ((ld-skip-proofsp state) (value '(0 . nil))) (t (mv-let (cl-set cl-set-ttree state) (pprogn (io? event nil state (names) (fms "Computing the guard conjecture for ~&0....~|" (list (cons #\0 names)) (proofs-co state) state nil)) (guard-obligation-clauses names guard-debug ens wrld state)) ; Cl-set-ttree is 'assumption-free. (pprogn (increment-timer 'other-time state) (let ((displayed-goal (prettyify-clause-set cl-set (let*-abstractionp state) wrld))) (mv-let (col state) (io? event nil (mv col state) (names cl-set cl-set-ttree displayed-goal) (prove-guard-clauses-msg names cl-set cl-set-ttree displayed-goal nil state) :default-bindings ((col 0))) (pprogn (increment-timer 'print-time state) (cond ((null cl-set) (value (cons col cl-set-ttree))) (t (mv-let (erp ttree state) (prove (termify-clause-set cl-set) (make-pspv ens wrld :displayed-goal displayed-goal :otf-flg otf-flg) hints ens wrld ctx state) (cond (erp (mv-let (erp1 val state) (er soft ctx "The proof of the guard conjecture for ~&0 has ~ failed. You may wish to avoid specifying a ~ guard, or to supply option :VERIFY-GUARDS ~x1 ~ with the :GUARD.~@2~|" names nil (if guard-debug "" " Otherwise, you may wish to specify ~ :GUARD-DEBUG T; see :DOC verify-guards.")) (declare (ignore erp1)) (mv (msg "The proof of the guard conjecture for ~&0 has ~ failed; see the discussion above about ~&1. " names (if guard-debug '(:VERIFY-GUARDS) '(:VERIFY-GUARDS :GUARD-DEBUG))) val state))) (t (mv-let (col state) (io? event nil (mv col state) (names) (fmt "That completes the proof of the ~ guard theorem for ~&0. " (list (cons #\0 names)) (proofs-co state) state nil) :default-bindings ((col 0))) (pprogn (increment-timer 'print-time state) (value (cons (or col 0) (cons-tag-trees cl-set-ttree ttree)))))))))))))))))) (defun verify-guards-fn1 (names hints otf-flg guard-debug ctx state) ; This function is called on a clique of mutually recursively defined ; fns whose guards have not yet been verified. Hints is a properly ; translated hints list. This is an error/value/state producing ; function. We cause an error if some subroutine of names has not yet ; had its guards checked or if we cannot prove the guards. Otherwise, ; the "value" is a pair of the form (wrld . ttree), where wrld results ; from storing symbol-class :common-lisp-compliant for each name and ; ttree is the ttree proving the guards. ; Note: In a series of conversations started around 13 Jun 94, with Bishop ; Brock, we came up with a new proposal for the form of guard conjectures. ; However, we have decided to delay the experiementation with this proposal ; until we evaluate the new logic of Version 1.8. But, the basic idea is this. ; Consider two functions, f and g, with guards a and b, respectively. Suppose ; (f (g x)) occurs in a context governed by q. Then the current guard ; conjectures are ; (1) q -> (b x) ; guard for g holds on x ; (2) q -> (a (g x)) ; guard for f holds on (g x) ; Note that (2) contains (g x) and we might need to know that x satisfies the ; guard for g here. Another way of putting it is that if we have to prove both ; (1) and (2) we might imagine forward chaining through (1) and reformulate (2) ; as (2') q & (b x) -> (a (g x)). ; Now in the days when guards were part of the logic, this was a pretty ; compelling idea because we couldn't get at the definition of (g x) in (2) ; without establisthing (b x) and thus formulation (2) forced us to prove ; (1) all over again during the proof of (2). But it is not clear whether ; we care now, because the smart user will define (g x) to "do the right thing" ; for any x and thus f will approve of (g x). So it is our expectation that ; this whole issue will fall by the wayside. It is our utter conviction of ; this that leads us to write this note. Just in case... ; ++++++++++++++++++++++++++++++ ; ; Date: Sun, 2 Oct 94 17:31:10 CDT ; From: kaufmann (Matt Kaufmann) ; To: moore ; Subject: proposal for handling generalized booleans ; ; Here's a pretty simple idea, I think, for handling generalized Booleans. For ; the rest of this message I'll assume that we are going to implement the ; about-to-be-proposed handling of guards. This proposal doesn't address ; functions like member, which could be thought of as returning generalized ; booleans but in fact are completely specified (when their guards are met). ; Rather, the problem we need to solve is that certain functions, including EQUAL ; and RATIONALP, only specify the propositional equivalence class of the value ; returned, and no more. I'll call these "problematic functions" for the rest of ; this note. ; ; The fundamental ideas of this proposal are as follows. ; ; ==================== ; ; (A) Problematic functions are completely a non-issue except for guard ; verification. The ACL2 logic specifies Boolean values for functions that are ; specified in dpANS to return generalized Booleans. ; ; (B) Guard verification will generate not only the current proof obligations, ; but also appropriate proof obligations to show that for all values returned by ; relevant problematic functions, only their propositional equivalence class ; matters. More on this later. ; ; (C) If a function is problematic, it had better only be used in propositional ; contexts when used in functions or theorems that are intended to be ; :common-lisp-compliant. For example, consider the following. ; ; (defun foo (x y z) ; (if x ; (equal y z) ; (cons y z))) ; ; This is problematic, and we will never be able to use it in a ; :common-lisp-compliant function or formula for other than its propositional ; value (unfortunately). ; ; ==================== ; ; Elaborating on (B) above: ; ; So for example, if we're verifying guards on ; ; (... (foo (rationalp x) ...) ...) ; ; then there will be a proof obligation to show that under the appropriate ; hypotheses (from governing IF tests), ; ; (implies (and a b) ; (equal (foo a ...) (foo b ...))) ; ; Notice that I've assumed that a and b are non-NIL. The other case, where a and ; b are both NIL, is trivial since in that case a and b are equal. ; ; Finally, most of the time no such proof obligation will be generated, because ; the context will make it clear that only the propositional equivalence class ; matters. In fact, for each function we'll store information that gives ; ``propositional arguments'' of the function: arguments for which we can be ; sure that only their propositional value matters. More on this below. ; ; ==================== ; ; Here are details. ; ; ==================== ; ; 1. Every function will have a ``propositional signature,'' which is a list of ; T's and NIL's. The CAR of this list is T when the function is problematic. ; The CDR of the list is in 1-1 correspondence with the function's formals (in ; the same order, of course), and indicates whether the formal's value only ; matters propositionally for the value of the function. ; ; For example, the function ; ; (defun bar (x y z) ; (if x ; (equal y z) ; (equal y nil))) ; ; has a propositional signature of (T T NIL NIL). The first T represents the ; fact that this function is problematic. The second T represents the fact that ; only the propositional equivalence class of X is used to compute the value of ; this function. The two NILs say that Y and Z may have their values used other ; than propositionally. ; ; An argument that corresponds to a value of T will be called a ``propositional ; argument'' henceforth. An OBSERVATION will be made any time a function is ; given a propositional signature that isn't composed entirely of NILs. ; ; (2) Propositional signatures will be assigned as follows, presumably hung on ; the 'propositional-signature property of the function. We intend to ensure ; that if a function is problematic, then the CAR of its propositional signature ; is T. The converse could fail, but it won't in practice. ; ; a. The primitives will have their values set using a fixed alist kept in sync ; with *primitive-formals-and-guards*, e.g.: ; ; (defconst *primitive-propositional-signatures* ; '((equal (t nil nil)) ; (cons (nil nil nil)) ; (rationalp (t nil)) ; ...)) ; ; In particular, IF has propositional signature (NIL T NIL NIL): although IF is ; not problematic, it is interesting to note that its first argument is a ; propositional argument. ; ; b. Defined functions will have their propositional signatures computed as ; follows. ; ; b1. The CAR is T if and only if some leaf of the IF-tree of the body is the ; call of a problematic function. For recursive functions, the function itself ; is considered not to be problematic for the purposes of this algorithm. ; ; b2. An argument, arg, corresponds to T (i.e., is a propositional argument in ; the sense defined above) if and only if for every subterm for which arg is an ; argument of a function call, arg is a propositional argument of that function. ; ; Actually, for recursive functions this algorithm is iterative, like the type ; prescription algorithm, in the sense that we start by assuming that every ; argument is propositional and iterate, continuing to cut down the set of ; propositional arguments until it stabilizes. ; ; Consider for example: ; ; (defun atom-listp (lst) ; (cond ((atom lst) (eq lst nil)) ; (t (and (atom (car lst)) ; (atom-listp (cdr lst)))))) ; ; Since EQ returns a generalized Boolean, ATOM-LISTP is problematic. Since ; the first argument of EQ is not propositional, ATOM-LISTP has propositional ; signature (T NIL). ; ; Note however that we may want to replace many such calls of EQ as follows, ; since dpANS says that NULL really does return a Boolean [I guess because it's ; sort of synonymous with NOT]: ; ; (defun atom-listp (lst) ; (cond ((atom lst) (null lst)) ; (t (and (atom (car lst)) ; (atom-listp (cdr lst)))))) ; ; Now this function is not problematic, even though one might be nervous because ; ATOM is, in fact, problematic. However, ATOM is in the test of an IF (because ; of how AND is defined). Nevertheless, the use of ATOM here is of issue, and ; this leads us to the next item. ; ; (3) Certain functions are worse than merely problematic, in that their value ; may not even be determined up to propositional equivalence class. Consider for ; example our old favorite: ; ; (defun bad (x) ; (equal (equal x x) (equal x x))) ; ; In this case, we can't really say anything at all about the value of BAD, ever. ; ; So, every function is checked that calls of problematic functions in its body ; only occur either at the top-level of its IF structure or in propositional ; argument positions. This check is done after the computation described in (2)b ; above. ; ; So, the second version of the definition of ATOM-LISTP above, ; ; (defun atom-listp (lst) ; (cond ((atom lst) (null lst)) ; (t (and (atom (car lst)) ; (atom-listp (cdr lst)))))) ; ; is OK in this sense, because both calls of ATOM occur in the first argument of ; an IF call, and the first argument of IF is propositional. ; ; Functions that fail this check are perfectly OK as :ideal functions; they just ; can't be :common-lisp-compliant. So perhaps they should generate a warning ; when submitted as :ideal, pointing out that they can never be ; :common-lisp-compliant. ; ; -- Matt (let ((wrld (w state)) (ens (ens state))) (er-let* ((pair (prove-guard-clauses names hints otf-flg guard-debug ctx ens wrld state))) ; Pair is of the form (col . ttree) (let* ((col (car pair)) (ttree1 (cdr pair)) (wrld1 (putprop-x-lst1 names 'symbol-class :common-lisp-compliant wrld))) (pprogn (print-verify-guards-msg names col state) (value (cons wrld1 ttree1))))))) (defun verify-guards-fn (name state hints otf-flg guard-debug doc event-form) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. (when-logic "VERIFY-GUARDS" (with-ctx-summarized (if (output-in-infixp state) event-form (cond ((and (null hints) (null otf-flg) (null doc)) (msg "( VERIFY-GUARDS ~x0)" name)) (t (cons 'verify-guards name)))) (let ((wrld (w state)) (event-form (or event-form (list* 'verify-guards name (append (if hints (list :hints hints) nil) (if otf-flg (list :otf-flg otf-flg) nil) (if doc (list :doc doc) nil))))) (assumep (or (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals) (eq (ld-skip-proofsp state) 'initialize-acl2)))) (er-let* ((names (chk-acceptable-verify-guards name ctx wrld state))) (cond ((eq names 'redundant) (stop-redundant-event ctx state)) (t (enforce-redundancy event-form ctx wrld (er-let* ((hints (if assumep (value nil) (translate-hints+ (cons "Guard Lemma for" name) hints (default-hints wrld) ctx wrld state))) (doc-pair (translate-doc nil doc ctx state)) ; Doc-pair is guaranteed to be nil because of the nil name supplied to ; translate-doc. (pair (verify-guards-fn1 names hints otf-flg guard-debug ctx state))) ; pair is of the form (wrld1 . ttree) (er-progn (chk-assumption-free-ttree (cdr pair) ctx state) (install-event name event-form 'verify-guards 0 (cdr pair) nil nil nil (car pair) state))))))))))) ; That completes the implementation of verify-guards. We now return ; to the development of defun itself. ; Here is the short-cut used when we are introducing :program functions. ; The super-defun-wart operations are not so much concerned with the ; :program defun-mode as with system functions that need special treatment. ; The wonderful super-defun-wart operations should not, in general, mess with ; the primitive state accessors and updaters. They have to do with a ; boot-strapping problem that is described in more detail in STATE-STATE in ; axioms.lisp. ; The following table has gives the proper STOBJS-IN and STOBJS-OUT ; settings for the indicated functions. ; Warning: If you ever change this table so that it talks about stobjs other ; than STATE, then reconsider oneify-cltl-code. These functions assume that if ; stobjs-in from this table is non-nil then special handling of STATE is ; required; or, at least, they did before Version_2.6. (defconst *super-defun-wart-table* ; fn stobjs-in stobjs-out '((COERCE-STATE-TO-OBJECT (STATE) (NIL)) (COERCE-OBJECT-TO-STATE (NIL) (STATE)) (USER-STOBJ-ALIST (STATE) (NIL)) (UPDATE-USER-STOBJ-ALIST (NIL STATE) (STATE)) (BIG-CLOCK-NEGATIVE-P (STATE) (NIL)) (DECREMENT-BIG-CLOCK (STATE) (STATE)) (STATE-P (STATE) (NIL)) (OPEN-INPUT-CHANNEL-P (NIL NIL STATE) (NIL)) (OPEN-OUTPUT-CHANNEL-P (NIL NIL STATE) (NIL)) (OPEN-INPUT-CHANNEL-ANY-P (NIL STATE) (NIL)) (OPEN-OUTPUT-CHANNEL-ANY-P (NIL STATE) (NIL)) (READ-CHAR$ (NIL STATE) (NIL STATE)) (PEEK-CHAR$ (NIL STATE) (NIL)) (READ-BYTE$ (NIL STATE) (NIL STATE)) (READ-OBJECT (NIL STATE) (NIL NIL STATE)) (READ-ACL2-ORACLE (STATE) (NIL NIL STATE)) (READ-ACL2-ORACLE@PAR (STATE) (NIL NIL)) (READ-RUN-TIME (STATE) (NIL STATE)) (READ-IDATE (STATE) (NIL STATE)) (LIST-ALL-PACKAGE-NAMES (STATE) (NIL STATE)) (PRINC$ (NIL NIL STATE) (STATE)) (WRITE-BYTE$ (NIL NIL STATE) (STATE)) (PRINT-OBJECT$-SER (NIL NIL NIL STATE) (STATE)) (GET-GLOBAL (NIL STATE) (NIL)) (BOUNDP-GLOBAL (NIL STATE) (NIL)) (MAKUNBOUND-GLOBAL (NIL STATE) (STATE)) (PUT-GLOBAL (NIL NIL STATE) (STATE)) (GLOBAL-TABLE-CARS (STATE) (NIL)) (T-STACK-LENGTH (STATE) (NIL)) (EXTEND-T-STACK (NIL NIL STATE) (STATE)) (SHRINK-T-STACK (NIL STATE) (STATE)) (AREF-T-STACK (NIL STATE) (NIL)) (ASET-T-STACK (NIL NIL STATE) (STATE)) (32-BIT-INTEGER-STACK-LENGTH (STATE) (NIL)) (EXTEND-32-BIT-INTEGER-STACK (NIL NIL STATE) (STATE)) (SHRINK-32-BIT-INTEGER-STACK (NIL STATE) (STATE)) (AREF-32-BIT-INTEGER-STACK (NIL STATE) (NIL)) (ASET-32-BIT-INTEGER-STACK (NIL NIL STATE) (STATE)) (OPEN-INPUT-CHANNEL (NIL NIL STATE) (NIL STATE)) (OPEN-OUTPUT-CHANNEL (NIL NIL STATE) (NIL STATE)) (GET-OUTPUT-STREAM-STRING$-FN (NIL STATE) (NIL NIL STATE)) (CLOSE-INPUT-CHANNEL (NIL STATE) (STATE)) (CLOSE-OUTPUT-CHANNEL (NIL STATE) (STATE)) (SYS-CALL-STATUS (STATE) (NIL STATE)))) (defun project-out-columns-i-and-j (i j table) (cond ((null table) nil) (t (cons (cons (nth i (car table)) (nth j (car table))) (project-out-columns-i-and-j i j (cdr table)))))) (defconst *super-defun-wart-binding-alist* (project-out-columns-i-and-j 0 2 *super-defun-wart-table*)) (defconst *super-defun-wart-stobjs-in-alist* (project-out-columns-i-and-j 0 1 *super-defun-wart-table*)) (defun super-defun-wart-bindings (bindings) (cond ((null bindings) nil) (t (cons (or (assoc-eq (caar bindings) *super-defun-wart-binding-alist*) (car bindings)) (super-defun-wart-bindings (cdr bindings)))))) (defun store-stobjs-ins (names stobjs-ins w) (cond ((null names) w) (t (store-stobjs-ins (cdr names) (cdr stobjs-ins) (putprop (car names) 'stobjs-in (car stobjs-ins) w))))) (defun store-super-defun-warts-stobjs-in (names wrld) ; Store the built-in stobjs-in values of the super defuns among names, if any. (cond ((null names) wrld) ((assoc-eq (car names) *super-defun-wart-stobjs-in-alist*) (store-super-defun-warts-stobjs-in (cdr names) (putprop (car names) 'stobjs-in (cdr (assoc-eq (car names) *super-defun-wart-stobjs-in-alist*)) wrld))) (t (store-super-defun-warts-stobjs-in (cdr names) wrld)))) (defun collect-old-nameps (names wrld) (cond ((null names) nil) ((new-namep (car names) wrld) (collect-old-nameps (cdr names) wrld)) (t (cons (car names) (collect-old-nameps (cdr names) wrld))))) (defun defuns-fn-short-cut (names docs pairs guards split-types-terms bodies non-executablep wrld state) ; This function is called by defuns-fn when the functions to be defined are ; :program. It short cuts the normal put-induction-info and other such ; analysis of defuns. The function essentially makes the named functions look ; like primitives in the sense that they can be used in formulas and they can ; be evaluated on explicit constants but no axioms or rules are available about ; them. In particular, we do not store 'def-bodies, type-prescriptions, or ; any of the recursion/induction properties normally associated with defuns and ; the prover will not execute them on explicit constants. ; We do take care of the documentation database. ; Like defuns-fn0, this function returns a pair consisting of the new world and ; a tag-tree recording the proofs that were done. (let* ((boot-strap-flg (global-val 'boot-strap-flg wrld)) (wrld0 (cond (non-executablep (putprop-x-lst1 names 'non-executablep non-executablep wrld)) (t wrld))) (wrld1 (if boot-strap-flg wrld0 (putprop-x-lst2 names 'unnormalized-body bodies wrld0))) (wrld2 (update-doc-database-lst names docs pairs (putprop-x-lst2-unless names 'guard guards *t* (putprop-x-lst2-unless names 'split-types-term split-types-terms *t* (putprop-x-lst1 names 'symbol-class :program wrld1)))))) (value (cons wrld2 nil)))) ; Now we develop the output for the defun event. (defun print-defun-msg/collect-type-prescriptions (names wrld) ; This function returns two lists, a list of names in names with ; trivial type-prescriptions (i.e., NIL 'type-prescriptions property) ; and an alist that pairs names in names with the term representing ; their (non-trivial) type prescriptions. (cond ((null names) (mv nil nil)) (t (mv-let (fns alist) (print-defun-msg/collect-type-prescriptions (cdr names) wrld) (let ((lst (getprop (car names) 'type-prescriptions nil 'current-acl2-world wrld))) (cond ((null lst) (mv (cons (car names) fns) alist)) (t (mv fns (cons (cons (car names) (untranslate (access type-prescription (car lst) :corollary) t wrld)) alist))))))))) (defun print-defun-msg/type-prescriptions1 (alist simp-phrase col state) ; See print-defun-msg/type-prescriptions. We print out a string of ; phrases explaining the alist produced above. We return the final ; col and state. This function used to be a tilde-* phrase, but ; you cannot get the punctuation after the ~xt commands. (cond ((null alist) (mv col state)) ((null (cdr alist)) (fmt1 "the type of ~xn is described by the theorem ~Pt0. ~#p~[~/We ~ used ~*s.~]~|" (list (cons #\n (caar alist)) (cons #\t (cdar alist)) (cons #\0 (term-evisc-tuple nil state)) (cons #\p (if (nth 4 simp-phrase) 1 0)) (cons #\s simp-phrase)) col (proofs-co state) state nil)) ((null (cddr alist)) (fmt1 "the type of ~xn is described by the theorem ~Pt0 ~ and the type of ~xm is described by the theorem ~Ps0.~|" (list (cons #\n (caar alist)) (cons #\t (cdar alist)) (cons #\0 (term-evisc-tuple nil state)) (cons #\m (caadr alist)) (cons #\s (cdadr alist))) col (proofs-co state) state nil)) (t (mv-let (col state) (fmt1 "the type of ~xn is described by the theorem ~Pt0, " (list (cons #\n (caar alist)) (cons #\t (cdar alist)) (cons #\0 (term-evisc-tuple nil state))) col (proofs-co state) state nil) (print-defun-msg/type-prescriptions1 (cdr alist) simp-phrase col state))))) (defun print-defun-msg/type-prescriptions (names ttree wrld col state) ; This function prints a description of each non-trivial ; type-prescription for the functions names. It assumes that at the ; time it is called, it is printing in col. It returns the final col, ; and the final state. (let ((simp-phrase (tilde-*-simp-phrase ttree))) (mv-let (fns alist) (print-defun-msg/collect-type-prescriptions names wrld) (cond ((null alist) (fmt1 " We could deduce no constraints on the type of ~#0~[~&0.~/any of ~ the functions in the clique.~]~#1~[~/ However, in normalizing the ~ definition~#0~[~/s~] we used ~*2.~]~%" (list (cons #\0 names) (cons #\1 (if (nth 4 simp-phrase) 1 0)) (cons #\2 simp-phrase)) col (proofs-co state) state nil)) (fns (mv-let (col state) (fmt1 " We could deduce no constraints on the type of ~#f~[~vf,~/any of ~ ~vf,~] but we do observe that " (list (cons #\f fns)) col (proofs-co state) state nil) (print-defun-msg/type-prescriptions1 alist simp-phrase col state))) (t (mv-let (col state) (fmt1 " We observe that " nil col (proofs-co state) state nil) (print-defun-msg/type-prescriptions1 alist simp-phrase col state))))))) (defun simple-signaturep (fn wrld) ; A simple signature is one in which no stobjs are involved and the ; output is a single value. (and (all-nils (stobjs-in fn wrld)) ; We call getprop rather than calling stobjs-out, because this code may run ; with fn = return-last, and the function stobjs-out causes an error in that ; case. We don't mind treating return-last as an ordinary function here. (null (cdr (getprop fn 'stobjs-out '(nil) 'current-acl2-world wrld))))) (defun all-simple-signaturesp (names wrld) (cond ((endp names) t) (t (and (simple-signaturep (car names) wrld) (all-simple-signaturesp (cdr names) wrld))))) (defun print-defun-msg/signatures1 (names wrld state) (cond ((endp names) state) ((not (simple-signaturep (car names) wrld)) (pprogn (fms "~x0 => ~x1." (list (cons #\0 (cons (car names) (prettyify-stobj-flags (stobjs-in (car names) wrld)))) (cons #\1 (prettyify-stobjs-out ; We call getprop rather than calling stobjs-out, because this code may run ; with fn = return-last, and the function stobjs-out causes an error in that ; case. We don't mind treating return-last as an ordinary function here. (getprop (car names) 'stobjs-out '(nil) 'current-acl2-world wrld)))) (proofs-co state) state nil) (print-defun-msg/signatures1 (cdr names) wrld state))) (t (print-defun-msg/signatures1 (cdr names) wrld state)))) (defun print-defun-msg/signatures (names wrld state) (cond ((all-simple-signaturesp names wrld) state) ((cdr names) (pprogn (fms "The Non-simple Signatures" nil (proofs-co state) state nil) (print-defun-msg/signatures1 names wrld state) (newline (proofs-co state) state))) (t (pprogn (print-defun-msg/signatures1 names wrld state) (newline (proofs-co state) state))))) (defun print-defun-msg (names ttree wrld col state) ; Once upon a time this function printed more than just the type ; prescription message. We've left the function here to handle that ; possibility in the future. This function returns the final state. ; This function increments timers. Upon entry, the accumulated time ; is charged to 'other-time. The time spent in this function is ; charged to 'print-time. (cond ((ld-skip-proofsp state) state) (t (io? event nil state (names ttree wrld col) (pprogn (increment-timer 'other-time state) (mv-let (erp ttree state) (accumulate-ttree-and-step-limit-into-state ttree :skip state) (declare (ignore erp)) (mv-let (col state) (print-defun-msg/type-prescriptions names ttree wrld col state) (declare (ignore col)) (pprogn (print-defun-msg/signatures names wrld state) (increment-timer 'print-time state))))))))) (defun get-ignores (lst) (cond ((null lst) nil) (t (cons (ignore-vars (fourth (car lst))) (get-ignores (cdr lst)))))) (defun get-ignorables (lst) (cond ((null lst) nil) (t (cons (ignorable-vars (fourth (car lst))) (get-ignorables (cdr lst)))))) (defun chk-all-stobj-names (lst msg ctx wrld state) ; Cause an error if any element of lst is not a legal stobj name in wrld. (cond ((endp lst) (value nil)) ((not (stobjp (car lst) t wrld)) (er soft ctx "Every name used as a stobj (whether declared explicitly via the ~ :STOBJ keyword argument or implicitly via *-notation) must have ~ been previously defined as a single-threaded object with ~ defstobj or defabsstobj. ~x0 is used as stobj name ~#1~[~/in ~ ~@1 ~]but has not been defined as a stobj." (car lst) msg)) (t (chk-all-stobj-names (cdr lst) msg ctx wrld state)))) (defun get-declared-stobj-names (edcls ctx wrld state) ; Each element of edcls is the cdr of a DECLARE form. We look for the ; ones of the form (XARGS ...) and find the first :stobjs keyword ; value in each such xargs. We know there is at most one :stobjs ; occurrence in each xargs by chk-dcl-lst. We union together all the ; values of that keyword, after checking that each value is legal. We ; return the list of declared stobj names or cause an error. ; Keep this in sync with get-declared-stobjs (which does not do any checking ; and returns a single value). (cond ((endp edcls) (value nil)) ((eq (caar edcls) 'xargs) (let* ((temp (assoc-keyword :stobjs (cdar edcls))) (lst (cond ((null temp) nil) ((null (cadr temp)) nil) ((atom (cadr temp)) (list (cadr temp))) (t (cadr temp))))) (cond (lst (cond ((not (symbol-listp lst)) (er soft ctx "The value specified for the :STOBJS xarg ~ must be a true list of symbols and ~x0 is ~ not." lst)) (t (er-progn (chk-all-stobj-names lst (msg "... :stobjs ~x0 ..." (cadr temp)) ctx wrld state) (er-let* ((rst (get-declared-stobj-names (cdr edcls) ctx wrld state))) (value (union-eq lst rst))))))) (t (get-declared-stobj-names (cdr edcls) ctx wrld state))))) (t (get-declared-stobj-names (cdr edcls) ctx wrld state)))) (defun get-stobjs-in-lst (lst ctx wrld state) ; Lst is a list of ``fives'' as computed in chk-acceptable-defuns. ; Each element is of the form (fn args "doc" edcls body). We know the ; args are legal arg lists, but nothing else. ; Unless we cause an error, we return a list in 1:1 correspondence ; with lst containing the STOBJS-IN flags for each fn. This involves ; three steps. First we recover from the edcls the declared :stobjs. ; We augment those with STATE, if STATE is in formals, which is always ; implicitly a stobj, if STATE is in the formals. We confirm that all ; the declared stobjs are indeed stobjs in wrld. Then we compute the ; stobj flags using the formals and the declared stobjs. (cond ((null lst) (value nil)) (t (let ((fn (first (car lst))) (formals (second (car lst)))) (er-let* ((dcl-stobj-names (get-declared-stobj-names (fourth (car lst)) ctx wrld state)) (dcl-stobj-namesx (cond ((and (member-eq 'state formals) (not (member-eq 'state dcl-stobj-names))) (er-progn (chk-state-ok ctx wrld state) (value (cons 'state dcl-stobj-names)))) (t (value dcl-stobj-names))))) (cond ((not (subsetp-eq dcl-stobj-namesx formals)) (er soft ctx "The stobj name~#0~[ ~&0 is~/s ~&0 are~] ~ declared but not among the formals of ~x1. ~ This generally indicates some kind of ~ typographical error and is illegal. Declare ~ only those stobj names listed in the formals. ~ The formals list of ~x1 is ~x2." (set-difference-equal dcl-stobj-namesx formals) fn formals)) (t (er-let* ((others (get-stobjs-in-lst (cdr lst) ctx wrld state))) ; Note: Wrld is irrelevant below because dcl-stobj-namesx is not T so ; we simply look for the formals that are in dcl-stobj-namesx. (value (cons (compute-stobj-flags formals dcl-stobj-namesx wrld) others)))))))))) (defun chk-stobjs-out-bound (names bindings ctx state) (cond ((null names) (value nil)) ((translate-unbound (car names) bindings) (er soft ctx "Translate failed to determine the output signature of ~ ~x0." (car names))) (t (chk-stobjs-out-bound (cdr names) bindings ctx state)))) (defun store-stobjs-out (names bindings w) (cond ((null names) w) (t (store-stobjs-out (cdr names) bindings (putprop (car names) 'stobjs-out (translate-deref (car names) bindings) w))))) (defun unparse-signature (x) ; Suppose x is an internal form signature, i.e., (fn formals stobjs-in ; stobjs-out). Then we return an external version of it, e.g., ((fn ; . stobjs-in) => (mv . stobjs-out)). This is only used in error ; reporting. (let* ((fn (car x)) (pretty-flags1 (prettyify-stobj-flags (caddr x))) (output (prettyify-stobjs-out (cadddr x)))) `((,fn ,@pretty-flags1) => ,output))) (defun chk-defun-mode (defun-mode ctx state) (cond ((eq defun-mode :program) (value nil)) ((eq defun-mode :logic) ; We do the check against the value of state global 'program-fns-with-raw-code ; in redefinition-renewal-mode, so that we do it only when reclassifying. (value nil)) (t (er soft ctx "The legal defun-modes are :program and :logic. ~x0 is ~ not a recognized defun-mode." defun-mode)))) (defun scan-to-cltl-command (wrld) ; Scan to the next binding of 'cltl-command or to the end of this event block. ; Return either nil or the global-value of cltl-command for this event. (cond ((null wrld) nil) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value)) nil) ((and (eq (caar wrld) 'cltl-command) (eq (cadar wrld) 'global-value)) (cddar wrld)) (t (scan-to-cltl-command (cdr wrld))))) (defconst *xargs-keywords* ; Keep this in sync with deflabel XARGS. '(:guard :guard-hints :guard-debug :hints :measure :ruler-extenders :mode :non-executable :normalize :otf-flg #+:non-standard-analysis :std-hints :stobjs :verify-guards :well-founded-relation :split-types)) (defun plausible-dclsp1 (lst) ; We determine whether lst is a plausible cdr for a DECLARE form. Ignoring the ; order of presentation and the number of occurrences of each element ; (including 0), we ensure that lst is of the form (... (TYPE ...) ... (IGNORE ; ...) ... (IGNORABLE ...) ... (XARGS ... :key val ...) ...) where the :keys ; are our xarg keys (members of *xargs-keywords*). (declare (xargs :guard t)) (cond ((atom lst) (null lst)) ((and (consp (car lst)) (true-listp (car lst)) (or (member-eq (caar lst) '(type ignore ignorable)) (and (eq (caar lst) 'xargs) (keyword-value-listp (cdar lst)) (subsetp-eq (evens (cdar lst)) *xargs-keywords*)))) (plausible-dclsp1 (cdr lst))) (t nil))) (defun plausible-dclsp (lst) ; We determine whether lst is a plausible thing to include between the formals ; and the body in a defun, e.g., a list of doc strings and DECLARE forms. We ; do not insist that the DECLARE forms are "perfectly legal" -- for example, we ; would approve (DECLARE (XARGS :measure m1 :measure m2)) -- but they are ; well-enough formed to permit us to walk through them with the fetch-from-dcls ; functions below. ; Note: This predicate is not actually used by defuns but is used by ; verify-termination in order to guard its exploration of the proposed dcls to ; merge them with the existing ones. After we define the predicate we define ; the exploration functions, which assume this fn as their guard. The ; exploration functions below are used in defuns, in particular, in the ; determination of whether a proposed defun is redundant. (declare (xargs :guard t)) (cond ((atom lst) (null lst)) ((stringp (car lst)) (plausible-dclsp (cdr lst))) ((and (consp (car lst)) (eq (caar lst) 'declare) (plausible-dclsp1 (cdar lst))) (plausible-dclsp (cdr lst))) (t nil))) ; The above function, plausible-dclsp, is the guard and the role model for the ; following functions which explore plausible-dcls and either collect all the ; "fields" used or delete certain fields. (defun dcl-fields1 (lst) (declare (xargs :guard (plausible-dclsp1 lst))) (cond ((endp lst) nil) ((member-eq (caar lst) '(type ignore ignorable)) (add-to-set-eq (caar lst) (dcl-fields1 (cdr lst)))) (t (union-eq (evens (cdar lst)) (dcl-fields1 (cdr lst)))))) (defun dcl-fields (lst) ; Lst satisfies plausible-dclsp, i.e., is the sort of thing you would find ; between the formals and the body of a DEFUN. We return a list of all the ; "field names" used in lst. Our answer is a subset of the list ; *xargs-keywords*. (declare (xargs :guard (plausible-dclsp lst))) (cond ((endp lst) nil) ((stringp (car lst)) (add-to-set-eq 'comment (dcl-fields (cdr lst)))) (t (union-eq (dcl-fields1 (cdar lst)) (dcl-fields (cdr lst)))))) (defun strip-keyword-list (fields lst) ; Lst is a keyword-value-listp, i.e., (:key1 val1 ...). We remove any key/val ; pair whose key is in fields. (declare (xargs :guard (and (symbol-listp fields) (keyword-value-listp lst)))) (cond ((endp lst) nil) ((member-eq (car lst) fields) (strip-keyword-list fields (cddr lst))) (t (cons (car lst) (cons (cadr lst) (strip-keyword-list fields (cddr lst))))))) (defun strip-dcls1 (fields lst) (declare (xargs :guard (and (symbol-listp fields) (plausible-dclsp1 lst)))) (cond ((endp lst) nil) ((member-eq (caar lst) '(type ignore ignorable)) (cond ((member-eq (caar lst) fields) (strip-dcls1 fields (cdr lst))) (t (cons (car lst) (strip-dcls1 fields (cdr lst)))))) (t (let ((temp (strip-keyword-list fields (cdar lst)))) (cond ((null temp) (strip-dcls1 fields (cdr lst))) (t (cons (cons 'xargs temp) (strip-dcls1 fields (cdr lst))))))))) (defun strip-dcls (fields lst) ; Lst satisfies plausible-dclsp. Fields is a list as returned by dcl-fields, ; i.e., a subset of the symbols in *xargs-keywords*. We copy lst deleting any ; part of it that specifies a value for one of the fields named. The result ; satisfies plausible-dclsp. (declare (xargs :guard (and (symbol-listp fields) (plausible-dclsp lst)))) (cond ((endp lst) nil) ((stringp (car lst)) (cond ((member-eq 'comment fields) (strip-dcls fields (cdr lst))) (t (cons (car lst) (strip-dcls fields (cdr lst)))))) (t (let ((temp (strip-dcls1 fields (cdar lst)))) (cond ((null temp) (strip-dcls fields (cdr lst))) (t (cons (cons 'declare temp) (strip-dcls fields (cdr lst))))))))) (defun fetch-dcl-fields2 (field-names kwd-list acc) (declare (xargs :guard (and (symbol-listp field-names) (keyword-value-listp kwd-list)))) (cond ((endp kwd-list) acc) (t (let ((acc (fetch-dcl-fields2 field-names (cddr kwd-list) acc))) (if (member-eq (car kwd-list) field-names) (cons (cadr kwd-list) acc) acc))))) (defun fetch-dcl-fields1 (field-names lst) (declare (xargs :guard (and (symbol-listp field-names) (plausible-dclsp1 lst)))) (cond ((endp lst) nil) ((member-eq (caar lst) '(type ignore ignorable)) (if (member-eq (caar lst) field-names) (cons (cdar lst) (fetch-dcl-fields1 field-names (cdr lst))) (fetch-dcl-fields1 field-names (cdr lst)))) (t (fetch-dcl-fields2 field-names (cdar lst) (fetch-dcl-fields1 field-names (cdr lst)))))) (defun fetch-dcl-fields (field-names lst) (declare (xargs :guard (and (symbol-listp field-names) (plausible-dclsp lst)))) (cond ((endp lst) nil) ((stringp (car lst)) (if (member-eq 'comment field-names) (cons (car lst) (fetch-dcl-fields field-names (cdr lst))) (fetch-dcl-fields field-names (cdr lst)))) (t (append (fetch-dcl-fields1 field-names (cdar lst)) (fetch-dcl-fields field-names (cdr lst)))))) (defun fetch-dcl-field (field-name lst) ; Lst satisfies plausible-dclsp, i.e., is the sort of thing you would find ; between the formals and the body of a DEFUN. Field-name is 'comment or one ; of the symbols in the list *xargs-keywords*. We return the list of the ; contents of all fields with that name. We assume we will find at most one ; specification per XARGS entry for a given keyword. ; For example, if field-name is :GUARD and there are two XARGS among the ; DECLAREs in lst, one with :GUARD g1 and the other with :GUARD g2 we return ; (g1 g2). Similarly, if field-name is TYPE and lst contains (DECLARE (TYPE ; INTEGER X Y)) then our output will be (... (INTEGER X Y) ...) where the ... ; are the other TYPE entries. (declare (xargs :guard (and (symbolp field-name) (plausible-dclsp lst)))) (fetch-dcl-fields (list field-name) lst)) (defun set-equalp-eq (lst1 lst2) (declare (xargs :guard (and (true-listp lst1) (true-listp lst2) (or (symbol-listp lst1) (symbol-listp lst2))))) (and (subsetp-eq lst1 lst2) (subsetp-eq lst2 lst1))) (defun non-identical-defp-chk-measures (name new-measures old-measures justification) (cond ((equal new-measures old-measures) nil) (t ; We could try harder, by translating the new measure and seeing if the set of ; free variables is the same as the old measured subset. But as Sandip Ray ; points out, it might be odd for the new "measure" to be allowed when in fact ; we have proved nothing about it! Also, the new measure would have to be ; translated in order to get its free variables, and we prefer not to pay that ; price (though perhaps it's quite minor). Bottom line: we see no reason for ; anyone to expect a definition to be redundant with an earlier one that has a ; different measure. (let ((old-measured-subset (assert$ justification ; Old-measured-subset is used only if chk-measure-p is true. In that case, if ; the existing definition is non-recursive then we treat the measured subset as ; nil. (access justification justification :subset)))) (cond ((and (consp new-measures) (null (cdr new-measures)) (let ((new-measure (car new-measures))) (or (equal new-measure (car old-measures)) (and (true-listp new-measure) (eq (car new-measure) :?) (arglistp (cdr new-measure)) (set-equalp-eq old-measured-subset (cdr new-measure)))))) nil) (old-measures (msg "the proposed and existing definitions for ~x0 differ on their ~ measures. The existing measure is ~x1. The new measure needs ~ to be specified explicitly with :measure (see :DOC xargs), ~ either to be identical to the existing measure or to be a call ~ of :? on the measured subset; for example, ~x2 will serve as ~ the new :measure." name (car old-measures) (cons :? old-measured-subset))) (t (msg "the existing definition for ~x0 does not have an explicitly ~ specified measure. Either remove the :measure declaration from ~ your proposed definition, or else specify a :measure that ~ applies :? to the existing measured subset, for example, ~x1." name (cons :? old-measured-subset)))))))) (defun non-identical-defp (def1 def2 chk-measure-p wrld) ; This predicate is used in recognizing redundant definitions. In our intended ; application, def2 will have been successfully processed and def1 is merely ; proposed, where def1 and def2 are each of the form (fn args ...dcls... body) ; and everything is untranslated. Two such tuples are "identical" if their ; fns, args, bodies, types, stobjs, guards, and (if chk-measure-p is true) ; measures are equal -- except that the new measure can be (:? v1 ... vk) if ; (v1 ... vk) is the measured subset for the old definition. We return nil if ; def1 is thus redundant with ("identical" to) def2. Otherwise we return a ; message suitable for printing using " Note that ~@k.". ; Note that def1 might actually be syntactically illegal, e.g., it might ; specify two different :measures. But it is possible that we will still ; recognize it as identical to def2 because the args and body are identical. ; Thus, the syntactic illegality of def1 might not be discovered if def1 is ; avoided because it is redundant. This happens already in redundancy checking ; in defthm: a defthm event is redundant if it introduces an identical theorem ; with the same name -- even if the :hints in the new defthm are ill-formed. ; The idea behind redundancy checking is to allow books to be loaded even if ; they share some events. The assumption is that def1 is in a book that got ; (or will get) processed by itself sometime and the ill-formedness will be ; detected there. That will change the check sum on the book and cause ; certification to lapse in the book that considered def1 redundant. ; Should we do any checks here related to the :subversive-p field of the ; justification for def2? The concern is that def2 (the old definition) is ; subversive but local, and def1 (the new definition) is not subversive and is ; non-local. But the notion of "subversive" is handled just as well in pass2 ; as in pass1, so ultimately def1 will be marked correctly on its ; subversiveness. (let* ((justification (and chk-measure-p ; optimization (getprop (car def2) 'justification nil 'current-acl2-world wrld))) (all-but-body1 (butlast (cddr def1) 1)) (ruler-extenders1-lst (fetch-dcl-field :ruler-extenders all-but-body1)) (ruler-extenders1 (if ruler-extenders1-lst (car ruler-extenders1-lst) (default-ruler-extenders wrld)))) (cond ((and justification (not (equal (access justification justification :ruler-extenders) ruler-extenders1))) (msg "the proposed and existing definitions for ~x0 differ on their ~ ruler-extenders (see :DOC ruler-extenders). The proposed value ~ of ruler-extenders is ~x1, while the value for the existing ~ definition of ~x0 is ~x2." (car def1) ruler-extenders1 (access justification justification :ruler-extenders))) ((equal def1 def2) ; optimization nil) ((not (eq (car def1) (car def2))) ; check same fn (can this fail?) (msg "the name of the new event, ~x0, differs from the name of the ~ corresponding existing event, ~x1." (car def1) (car def2))) ((not (equal (cadr def1) (cadr def2))) ; check same args (msg "the proposed argument list for ~x0, ~x1, differs from the ~ existing argument list, ~x2." (car def1) (cadr def1) (cadr def2))) ((not (equal (car (last def1)) (car (last def2)))) ; check same body (msg "the proposed body for ~x0,~|~%~p1,~|~%differs from the existing ~ body,~|~%~p2.~|~%" (car def1) (car (last def1)) (car (last def2)))) (t (let ((all-but-body2 (butlast (cddr def2) 1))) (cond ((not (equal (fetch-dcl-field :non-executable all-but-body1) (fetch-dcl-field :non-executable all-but-body2))) (msg "the proposed and existing definitions for ~x0 differ on their ~ :non-executable declarations." (car def1))) ((not (equal (fetch-dcl-field :stobjs all-but-body1) (fetch-dcl-field :stobjs all-but-body2))) ; We insist that the :STOBJS of the two definitions be identical. Vernon ; Austel pointed out the following bug. ; Define a :program mode function with a non-stobj argument. ; (defun stobjless-fn (stobj-to-be) ; (declare (xargs :mode :program)) ; stobj-to-be) ; Use it in the definition of another :program mode function. ; (defun my-callee-is-stobjless (x) ; (declare (xargs :mode :program)) ; (stobjless-fn x)) ; Then introduce a the argument name as a stobj: ; (defstobj stobj-to-be ; (a-field :type integer :initially 0)) ; And reclassify the first function into :logic mode. ; (defun stobjless-fn (stobj-to-be) ; (declare (xargs :stobjs stobj-to-be)) ; stobj-to-be) ; If you don't notice the different use of :stobjs then the :program ; mode function my-callee-is-stobjless [still] treats the original ; function as though its argument were NOT a stobj! For example, ; (my-callee-is-stobjless 3) is a well-formed :program mode term ; that treats 3 as a stobj. (msg "the proposed and existing definitions for ~x0 differ on their ~ :stobj declarations." (car def1))) ((not (equal (fetch-dcl-field 'type all-but-body1) (fetch-dcl-field 'type all-but-body2))) ; Once we removed the restriction that the type and :guard fields of the defs ; be equal. But imagine that we have a strong guard on foo in our current ACL2 ; session, but that we then include a book with a much weaker guard. (Horrors! ; What if the new guard is totally unrelated!?) If we didn't make the tests ; below, then presumably the guard on foo would be unchanged by this ; include-book. Suppose that in this book, we have verified guards for a ; function bar that calls foo. Then after including the book, it will look as ; though correctly guarded calls of bar always generate only correctly guarded ; calls of foo, but now that foo has a stronger guard than it did when the book ; was certified, this might not always be the case. (msg "the proposed and existing definitions for ~x0 differ on their ~ type declarations." (car def1))) ((let* ((guards1 (fetch-dcl-field :guard all-but-body1)) (guards1-trivial-p (or (null guards1) (equal guards1 '(t)))) (guards2 (fetch-dcl-field :guard all-but-body2)) (guards2-trivial-p (or (null guards2) (equal guards2 '(t))))) ; See the comment above on type and :guard fields. Here, we comprehend the ; fact that omission of a guard is equivalent to :guard t. Of course, it is ; also equivalent to :guard 't and even to :guard (not nil), but we see no need ; to be that generous with our notion of redundancy. (cond ((and guards1-trivial-p guards2-trivial-p) nil) ((not (equal guards1 guards2)) (msg "the proposed and existing definitions for ~x0 differ ~ on their :guard declarations." (car def1))) ; So now we know that the guards are equal and non-trivial. If the types are ; non-trivial too then we need to make sure that the combined order of guards ; and types for each definition are in agreement. The following example shows ; what can go wrong without that check. ; (encapsulate ; () ; (local (defun foo (x) ; (declare (xargs :guard (consp x))) ; (declare (xargs :guard (consp (car x)))) ; x)) ; (defun foo (x) ; (declare (xargs :guard (consp (car x)))) ; (declare (xargs :guard (consp x))) ; x)) ; ; (foo 3) ; hard raw Lisp error! ((not (equal (fetch-dcl-fields '(type :guard) all-but-body1) (fetch-dcl-fields '(type :guard) all-but-body2))) (msg "although the proposed and existing definitions for ~ ~x0 agree on the their type and :guard declarations, ~ they disagree on the combined orders of those ~ declarations."))))) ((let ((split-types1 (fetch-dcl-field :split-types all-but-body1)) (split-types2 (fetch-dcl-field :split-types all-but-body2))) (or (not (eq (all-nils split-types1) (all-nils split-types2))) ; Catch the case of illegal values in the proposed definition. (not (boolean-listp split-types1)) (and (member-eq nil split-types1) (member-eq t split-types1)))) (msg "the proposed and existing definitions for ~x0 differ on their ~ :split-types declarations." (car def1))) ((not chk-measure-p) nil) ((null justification) ; The old definition (def2) was non-recursive. Then since the names and bodies ; are identical (as checked above), the new definition (def1) is also ; non-recursive. In this case we don't care about the measures; see the ; comment above about "syntactically illegal". nil) (t (non-identical-defp-chk-measures (car def1) (fetch-dcl-field :measure all-but-body1) (fetch-dcl-field :measure all-but-body2) justification)))))))) (defun identical-defp (def1 def2 chk-measure-p wrld) ; This function is probably obsolete -- superseded by non-identical-defp -- but ; we leave it here for reference by comments. (not (non-identical-defp def1 def2 chk-measure-p wrld))) (defun redundant-or-reclassifying-defunp0 (defun-mode symbol-class ld-skip-proofsp chk-measure-p def wrld) ; See redundant-or-reclassifying-defunp. This function has the same behavior ; as that one, except in this one, if parameter chk-measure-p is nil, then ; measure checking is suppressed. (cond ((function-symbolp (car def) wrld) (let* ((wrld1 (decode-logical-name (car def) wrld)) (name (car def)) (val (scan-to-cltl-command (cdr wrld1))) (chk-measure-p (and chk-measure-p ; If we are skipping proofs, then we do not need to check the measure. Why ; not? One case is that we are explicitly skipping proofs (with skip-proofs, ; rebuild, set-ld-skip-proofsp, etc.; or, inclusion of an uncertified book), in ; which case all bets are off. Otherwise we are including a certified book, ; where the measured subset was proved correct. This observation satisfies our ; concern, which is that the current redundant definition will ultimately ; become the actual definition because the earlier one is local. (not ld-skip-proofsp) ; A successful redundancy check may require that the untranslated measure is ; identical to that of the earlier corresponding defun. Without such a check ; we can store incorrect induction information, as exhibited by the "soundness ; bug in the redundancy criterion for defun events" mentioned in :doc ; note-3-0-2. The following examples, which work with Version_3.0.1 but ; (fortunately) not afterwards, build on the aforementioned proof of nil given ; in :doc note-3-0-2, giving further weight to our insistence on the same ; measure if the mode isn't changing from :program to :logic. ; The following example involves redundancy only for :program mode functions. ; (encapsulate ; () ; ; (local (defun foo (x y) ; (declare (xargs :measure (acl2-count y) :mode :program)) ; (if (and (consp x) (consp y)) ; (foo (cons x x) (cdr y)) ; y))) ; ; (defun foo (x y) ; (declare (xargs :mode :program)) ; (if (and (consp x) (consp y)) ; (foo (cons x x) (cdr y)) ; y)) ; ; (verify-termination foo)) ; ; (defthm bad ; (atom x) ; :rule-classes nil ; :hints (("Goal" :induct (foo x '(3))))) ; ; (defthm contradiction ; nil ; :rule-classes nil ; :hints (("Goal" :use ((:instance bad (x '(7))))))) ; Note that even though we do not store induction schemes for mutual-recursion, ; the following variant of the first example shows that we still need to check ; measures in that case: ; (set-bogus-mutual-recursion-ok t) ; ease construction of example ; ; (encapsulate ; () ; (local (encapsulate ; () ; ; (local (mutual-recursion ; (defun bar (x) x) ; (defun foo (x y) ; (declare (xargs :measure (acl2-count y))) ; (if (and (consp x) (consp y)) ; (foo (cons x x) (cdr y)) ; y)))) ; ; (mutual-recursion ; (defun bar (x) x) ; (defun foo (x y) ; (if (and (consp x) (consp y)) ; (foo (cons x x) (cdr y)) ; y))))) ; (defun foo (x y) ; (if (and (consp x) (consp y)) ; (foo (cons x x) (cdr y)) ; y))) ; ; (defthm bad ; (atom x) ; :rule-classes nil ; :hints (("Goal" :induct (foo x '(3))))) ; ; (defthm contradiction ; nil ; :rule-classes nil ; :hints (("Goal" :use ((:instance bad (x '(7))))))) ; | ; After Version_3.4 we no longer concern ourselves with the measure in the case ; of :program mode functions, as we now explain. ; Since verify-termination is now just a macro for make-event, we may view the ; :measure of a :program mode function as nothing more than a hint for use by ; that make-event. So we need think only about definitions (defun, defuns). ; Note that the measure for a :logic mode definition will always come lexically ; from that definition. So for redundancy, soundness only requires that the ; measured subsets agree when the old and new definitions are both in :logic ; mode. We can even change the measure from an existing :program mode ; definition to produce a new :program mode definition, so as to provide a ; better hint for a later verify-termination call. ; One might think that we should do the measures check when the old definition ; is :logic and the new one is :program. But in that case, either the new one ; is redundant or ultimately in :program mode (if the first is local and the ; second is installed on a second pass). Either way, there is no concern: if ; the definition is installed, it will be in program mode and hence its measure ; presents no concern for soundness. (eq (cadr val) :logic) (eq defun-mode :logic)))) ; The 'cltl-command val for a defun is (defuns :defun-mode ignorep . def-lst) ; where :defun-mode is a keyword (rather than nil which means this was an ; encapsulate or was :non-executable). (cond ((null val) nil) ((and (consp val) (eq (car val) 'defuns) (keywordp (cadr val))) (cond ((non-identical-defp def (assoc-eq name (cdddr val)) chk-measure-p wrld)) ; Else, this cltl-command contains a member of def-lst that is identical to ; def. ((eq (cadr val) defun-mode) (cond ((and (eq symbol-class :common-lisp-compliant) (eq (symbol-class name wrld) :ideal)) ; The following produced a hard error in v2-7, because the second defun was ; declared redundant on the first pass and then installed as ; :common-lisp-compliant on the second pass: ; (encapsulate nil ; (local ; (defun foo (x) (declare (xargs :guard t :verify-guards nil)) (car x))) ; (defun foo (x) (declare (xargs :guard t)) (car x))) ; (thm (equal (foo 3) xxx)) ; The above example was derived from one sent by Jared Davis, who proved nil in ; an early version of v2-8 by exploiting this idea to trick ACL2 into ; considering guards verified for a function employing mbe. ; Here, we prevent such promotion of :ideal to :common-lisp-compliant. 'verify-guards) (t 'redundant))) ((and (eq (cadr val) :program) (eq defun-mode :logic)) 'reclassifying) (t ; We allow "redefinition" from :logic to :program mode by treating the latter ; as redundant. At one time we thought it should be disallowed because of an ; example like this: ; (encapsulate nil ; (local (defun foo (x) x)) ; (defun foo (x) (declare (xargs :mode :program)) x) ; redundant? ; (defthm foo-is-id (equal (foo x) x))) ; We clearly don't want to allow this encapsulation or analogous books. But ; this is prevented by pass 2 of the encapsulate (similarly, but at the book ; level, for certify-book), when ACL2 discovers that foo is now :program mode. ; We need to be careful to avoid similar traps elsewhere. ; It's important to allow such to be redundant in order to avoid the following ; problem, pointed out by Jared Davis. Imagine that one book defines a ; function in :logic mode, while another has an identical definition in ; :program mode followed by verify-termination. Also imagine that both books ; are independently certified. Now imagine, in a fresh session, including the ; first book and then the second. Inclusion of the second causes an error in ; Version_3.4 because of the "downgrade" from :logic mode to :program mode at ; the time the :program mode definition is encountered. ; Finally, note that we are relying on safe-mode! Imagine a book with a local ; :logic mode definition of f followed by a non-local :program mode definition ; of f, followed by a defconst that uses f. Also suppose that the guard of f ; is insufficient to verify its guards; to be specific, suppose (f x) is ; defined to be (car x) with a guard of t. If we call (f 3) in the defconst, ; there is a guard violation. In :logic mode that isn't a problem, because we ; are running *1* code. But in :program mode we could get a hard Lisp error. ; In fact, we won't in the case of defconst, because defconst forms are ; evaluated in safe mode. For a potentially related issue, see the comments in ; (deflabel note-2-9 ...) for an example of how we can get unsoundness, not ; merely a hard error, for the use of ill-guarded functions in defconst forms. 'redundant))) ((and (null (cadr val)) ; optimization (fetch-dcl-field :non-executable (butlast (cddr def) 1))) (cond ((let* ((event-tuple (cddr (car wrld1))) (event (if (symbolp (cadr event-tuple)) (cdr event-tuple) ; see make-event-tuple (cddr event-tuple)))) (non-identical-defp def (case (car event) (mutual-recursion (assoc-eq name (strip-cdrs (cdr event)))) (defuns (assoc-eq name (cdr event))) (otherwise (cdr event))) chk-measure-p wrld))) ((and (eq (symbol-class name wrld) :program) (eq defun-mode :logic)) 'reclassifying) (t ; We allow "redefinition" from :logic to :program mode by treating the latter ; as redundant. See the comment above on this topic. 'redundant))) (t nil)))) (t nil))) (defun redundant-or-reclassifying-defunp (defun-mode symbol-class ld-skip-proofsp def wrld) ; Def is a defuns tuple such as (fn args ...dcls... body) that has been ; submitted to defuns with mode defun-mode. We determine whether fn is already ; defined in wrld and has an "identical" definition (up to defun-mode). We ; return either nil, a message (cons pair suitable for printing with ~@), ; 'redundant, 'reclassifying, or 'verify-guards. 'Redundant is returned if ; there is an existing definition for fn that is identical-defp to def and has ; mode :program or defun-mode, except that in this case 'verify-guards is ; returned if the symbol-class was :ideal but this definition indicates ; promotion to :common-lisp-compliant. 'Reclassifying is returned if there is ; an existing definition for fn that is identical-defp to def but in mode ; :program while defun-mode is :logic. Otherwise nil or an explanatory ; message, suitable for printing using " Note that ~@0.", is returned. ; Functions further up the call tree will decide what to do with a result of ; 'verify-guards. But a perfectly reasonable action would be to cause an error ; suggesting the use of verify-guards instead of defun. (redundant-or-reclassifying-defunp0 defun-mode symbol-class ld-skip-proofsp t def wrld)) (defun redundant-or-reclassifying-defunsp10 (defun-mode symbol-class ld-skip-proofsp chk-measure-p def-lst wrld ans) ; See redundant-or-reclassifying-defunsp1. This function has the same behavior ; as that one, except in this one, if parameter chk-measure-p is nil, then ; measure checking is suppressed. (cond ((null def-lst) ans) (t (let ((x (redundant-or-reclassifying-defunp0 defun-mode symbol-class ld-skip-proofsp chk-measure-p (car def-lst) wrld))) (cond ((consp x) x) ; a message ((eq ans x) (redundant-or-reclassifying-defunsp10 defun-mode symbol-class ld-skip-proofsp chk-measure-p (cdr def-lst) wrld ans)) (t nil)))))) (defun redundant-or-reclassifying-defunsp1 (defun-mode symbol-class ld-skip-proofsp def-lst wrld ans) (redundant-or-reclassifying-defunsp10 defun-mode symbol-class ld-skip-proofsp t def-lst wrld ans)) (defun recover-defs-lst (fn wrld) ; Fn is a :program function symbol in wrld. Thus, it was introduced by defun. ; (Constrained and defchoose functions are :logic.) We return the defs-lst ; that introduced fn. We recover this from the cltl-command for fn. ; A special case is when fn is non-executable. We started allowing ; non-executable :program mode functions after Version_4.1, to provide an easy ; way to use defattach, especially during the boot-strap. We prohibit ; reclassifying such a function symbol into :logic mode, for at least the ; following reason: we store the true stobjs-out for non-executable :program ; mode functions, to match attachments that may be made; but we always store a ; stobjs-out of (nil) in the :logic mode case. We could perhaps allow ; reclassifying into :logic mode in cases where the stobjs-out is (nil) in the ; :program mode function, by recovering defuns from the event. But it seems ; most coherent simply to disallow the upgrade. We store a different value, ; :program, for the 'non-executablep property for :program mode functions than ; for :logic mode functions, where we store t. (let ((err-str "For technical reasons, we do not attempt to recover the ~ definition of a ~s0 function such as ~x1. It is surprising ~ actually that you are seeing this message; please contact ~ the ACL2 implementors unless you have called ~x2 yourself.") (ctx 'recover-defs-lst)) (cond ((getprop fn 'non-executablep nil 'current-acl2-world wrld) ; We shouldn't be seeing this message, as something between verify-termination ; and this lower-level function should be handling the non-executable case ; (which is disallowed for the reasons explained above, related to ; stobjs-out). (er hard ctx err-str "non-executable" fn 'recover-defs-lst)) (t (let ((val (scan-to-cltl-command (cdr (lookup-world-index 'event (getprop fn 'absolute-event-number '(:error "See ~ RECOVER-DEFS-LST.") 'current-acl2-world wrld) wrld))))) (cond ((and (consp val) (eq (car val) 'defuns)) ; Val is of the form (defuns defun-mode-flg ignorep def1 ... defn). If ; defun-mode-flg is non-nil then the parent event was (defuns def1 ... defn) ; and the defun-mode was defun-mode-flg. If defun-mode-flg is nil, the parent ; was an encapsulate, defchoose, or :non-executable, but none of these cases ; should occur since presumably we are only considering :program mode functions ; that are not non-executable. (cond ((cadr val) (cdddr val)) (t (er hard ctx err-str "non-executable or :LOGIC mode" fn 'recover-defs-lst)))) (t (er hard ctx "We failed to find the expected CLTL-COMMAND for the ~ introduction of ~x0." fn)))))))) (defun get-clique (fn wrld) ; Fn must be a function symbol. We return the list of mutually recursive fns ; in the clique containing fn, according to their original definitions. If fn ; is :program we have to look for the cltl-command and recover the clique from ; the defs-lst. Otherwise, we can use the 'recursivep property. (cond ((programp fn wrld) (let ((defs (recover-defs-lst fn wrld))) (strip-cars defs))) (t (let ((recp (getprop fn 'recursivep nil 'current-acl2-world wrld))) (cond ((null recp) (list fn)) (t recp)))))) (defun redundant-or-reclassifying-defunsp0 (defun-mode symbol-class ld-skip-proofsp chk-measure-p def-lst wrld) ; See redundant-or-reclassifying-defunsp. This function has the same behavior ; as that one, except in this one, if parameter chk-measure-p is nil, then ; measure checking is suppressed. (cond ((null def-lst) 'redundant) (t (let ((ans (redundant-or-reclassifying-defunp0 defun-mode symbol-class ld-skip-proofsp chk-measure-p (car def-lst) wrld))) (cond ((consp ans) ans) ; a message (t (let ((ans (redundant-or-reclassifying-defunsp10 defun-mode symbol-class ld-skip-proofsp chk-measure-p (cdr def-lst) wrld ans))) (cond ((eq ans 'redundant) (cond ((or (eq defun-mode :program) (let ((recp (getprop (caar def-lst) 'recursivep nil 'current-acl2-world wrld))) (if (and (consp recp) (consp (cdr recp))) (set-equalp-eq (strip-cars def-lst) recp) (null (cdr def-lst))))) ans) (t (msg "for :logic mode definitions to be ~ redundant, if one is defined with ~ mutual-recursion then both must be ~ defined in the same mutual-recursion.~|~%")))) ((and (eq ans 'reclassifying) (not (set-equalp-eq (strip-cars def-lst) (get-clique (caar def-lst) wrld)))) (msg "for reclassifying :program mode definitions ~ to :logic mode, an entire mutual-recursion ~ clique must be reclassified. In this case, ~ the mutual-recursion that defined ~x0 also ~ defined the following, not included in the ~ present event: ~&1.~|~%" (caar def-lst) (set-difference-eq (get-clique (caar def-lst) wrld) (strip-cars def-lst)))) (t ans))))))))) (defun get-unnormalized-bodies (names wrld) (cond ((endp names) nil) (t (cons (getprop (car names) 'unnormalized-body nil 'current-acl2-world wrld) (get-unnormalized-bodies (cdr names) wrld))))) (defun strip-last-elements (lst) (declare (xargs :guard (true-list-listp lst))) (cond ((endp lst) nil) (t (cons (car (last (car lst))) (strip-last-elements (cdr lst)))))) (defun redundant-or-reclassifying-defunsp (defun-mode symbol-class ld-skip-proofsp def-lst ctx wrld ld-redefinition-action fives non-executablep stobjs-in-lst default-state-vars) ; We return 'redundant if the functions in def-lst are already identically ; defined with :mode defun-mode and class symbol-class. We return ; 'verify-guards if they are al identically defined with :mode :logic and class ; :ideal, but this definition indicates promotion to :common-lisp-compliant. ; Finally, we return 'reclassifying if they are all identically defined in ; :mode :program and defun-mode is :logic. We return nil otherwise. ; We start to answer this question by independently considering each def in ; def-lst. We then add additional requirements pertaining to mutual-recursion. ; The first is for :logic mode definitions (but see the Historical Plaque ; below): if the old and new definition are in different mutual-recursion nests ; (or if one is in a mutual-recursion with other definitions and the other is ; not), then the new definition is not redundant. To see why we make this ; additional restriction, consider the following example. ; (encapsulate ; () ; (local ; (mutual-recursion ; (defun f (x y) ; (if (and (consp x) (consp y)) ; (f (cons 3 x) (cdr y)) ; (list x y))) ; (defun g (x y) ; (if (consp y) ; (f x (cdr y)) ; (list x y))))) ; ; (defun f (x y) ; ;;; possible IMPLICIT (bad) measure of (acl2-count x) ; (if (and (consp x) (consp y)) ; (f (cons 3 x) (cdr y)) ; (list x y)))) ; As the comment indicates, if ACL2 were to use the entire mutual-recursion to ; guess measures, then it might well guess a different measure (based on y) for ; the first definition of f than for the second (based on x), leaving us with ; an unsound induction scheme for f (based incorrectly on x). Although ACL2 ; does not guess measures that way as of this writing (shortly after the ; Version_3.4 release), still one can imagine future heuristic changes of this ; sort. A more "practical" reason for this restriction is that it seems to ; make the underlying theory significantly easier to work out. ; A second requirement is that we do not reclassify from :program mode to ; :logic mode for a proper subset of a mutual-recursion nest. This restriction ; may be overly conservative, but then again, we expect it to be rare that it ; would affect anyone. While we do not have a definitive reason for this ; restriction, consider for example induction schemes, which are stored for ; single recursion but not mutual-recursion. Although this issue may be fully ; handled by the restriction on redundancy described above, we see this as just ; one possible pitfall, so we prefer to maintain the invariant that all ; functions in a mutual-recursion nest have the same defun-mode. ; Note: Our redundancy check for definitions is based on the untranslated ; terms. This is different from, say, theorems, where we compare translated ; terms. The reason is that we do not store the translated versions of ; :program definitions and don't want to go to the cost of translating ; what we did store. We could, I suppose. We handle theorems the way we do ; because we store the translated theorem on the property list, so it is easy. ; Our main concern vis-a-vis redundancy is arranging for identical definitions ; not to blow us up when we are loading books that have copied definitions and ; I don't think translation will make an important difference to the utility of ; the feature. ; Note: There is a possible bug lurking here. If the host Common Lisp expands ; macros before storing the symbol-function, then we could recognize as ; "redundant" an identical defun that, if actually passed to the underlying ; Common Lisp, would result in the storage of a different symbol-function ; because of the earlier redefinition of some macro used in the "redundant" ; definition. This is not a soundness problem, since redefinition is involved. ; But it sure might annoy somebody who didn't notice that his redefinition ; wasn't processed. ; Historical Plaque: The following comment was in place before we restricted ; redundancy to insist on identical mutual-recursion nests. ; We answer this question by answering it independently for each def in ; def-lst. Thus, every def must be 'redundant or 'reclassifying as ; appropriate. This seems really weak because we do not insist that only one ; cltl-command tuple is involved. But (defuns def1 ... defn) just adds one ; axiom for each defi and the axiom is entirely determined by the defi. Thus, ; if we have executed a defuns that added the axiom for defi then it is the ; same axiom as would be added if we executed a different defuns that ; contained defi. Furthermore, a cltl-command of the form (defuns :defun-mode ; ignorep def1 ... defn) means (defuns def1 ... defn) was executed in this ; world with the indicated defun-mode. (let ((ans (redundant-or-reclassifying-defunsp0 defun-mode symbol-class ld-skip-proofsp t def-lst wrld))) (cond ((and ld-redefinition-action (member-eq ans '(redundant reclassifying verify-guards))) ; We do some extra checking, converting ans to nil, in order to consider there ; to be true redefinition (by returning nil) in cases where that seems possible ; -- in particular, because translated bodies have changed due to prior ; redefinition of macros or defconsts called in a new body. Our handling of ; this case isn't perfect, for example because it may reject reclassification ; when the order changes. But at least it forces some definitions to be ; considered as doing redefinition. Notice that this extra effort is only ; performed when redefinition is active, so as not to slow down the system in ; the normal case. If there has been no redefinition in the session, then we ; expect this extra checking to be unnecessary. (let ((names (strip-cars fives)) (bodies (get-bodies fives))) (mv-let (erp lst bindings) (translate-bodies1 (eq non-executablep t) ; not :program names bodies (pairlis$ names names) stobjs-in-lst ctx wrld default-state-vars) (declare (ignore bindings)) (cond (erp ans) ((eq (symbol-class (car names) wrld) :program) (let ((old-defs (recover-defs-lst (car names) wrld))) (and (equal names (strip-cars old-defs)) (mv-let (erp old-lst bindings) (translate-bodies1 ; The old non-executablep is nil; see recover-defs-lst. nil names (strip-last-elements old-defs) (pairlis$ names names) stobjs-in-lst ctx wrld default-state-vars) (declare (ignore bindings)) (cond ((and (null erp) (equal lst old-lst)) ans) (t nil)))))) ; Otherwise we expect to be dealing with :logic mode functions. ((equal lst (get-unnormalized-bodies names wrld)) ans) (t nil))))) (t ans)))) (defun collect-when-cadr-eq (sym lst) (cond ((null lst) nil) ((eq sym (cadr (car lst))) (cons (car lst) (collect-when-cadr-eq sym (cdr lst)))) (t (collect-when-cadr-eq sym (cdr lst))))) (defun all-programp (names wrld) ; Names is a list of function symbols. Return t iff every element of ; names is :program. (cond ((null names) t) (t (and (programp (car names) wrld) (all-programp (cdr names) wrld))))) ; Essay on the Identification of Irrelevant Formals ; A formal is irrelevant if its value does not affect the value of the ; function. Of course, ignored formals have this property, but we here address ; ourselves to the much more subtle problem of formals that are used only in ; irrelevant ways. For example, y in ; (defun foo (x y) (if (zerop x) 0 (foo (1- x) (cons x y)))) ; is irrelevant. Clearly, any formal mentioned outside of a recursive call is ; relevant -- provided that no previously introduced function has irrelevant ; arguments and no definition tests constants as in (if t x y). But a formal ; that is never used outside a recursive call may still be relevant, as ; illustrated by y in: ; (defun foo (x y) (if (< x 2) x (foo y 0))) ; Observe that (foo 3 1) = 1 and (foo 3 0) = 0; thus, y is relevant. (This ; function can be admitted with the measure (cond ((< x 2) 0) ((< y 2) 1) (t ; 2)).) ; Thus, we have to do a transitive closure computation based on which formals ; appear in which actuals of recursive calls. In the first pass we see that x, ; above, is relevant because it is used outside the recursion. In the next ; pass we see that y is relevant because it is passed into the x argument ; position of a recursive call. ; The whole thing is made somewhat more hairy by mutual recursion, though no ; new intellectual problems are raised. However, to cope with mutual recursion ; we stop talking about "formals" and start talking about "posns." A posn here ; is a natural number n that represents the nth formal for a function in the ; mutually recursive clique. We say a "posn is used" if the corresponding ; formal is used. ; A "recursive call" here means a call of any function in the clique. We ; generally use the variable clique-alist to mean an alist whose elements are ; each of the form (fn . posns). ; A second problem is raised by the presence of lambda expressions. We discuss ; them more below. ; Our algorithm iteratively computes the relevant posns of a clique by ; successively enlarging an initial guess. The initial guess consists of all ; the posns used outside of a recursive call, including the guard or measure or ; the lists of ignored or ignorable formals. Clearly, every posn so collected ; is relevant. We then iterate, sweeping into the set every posn used either ; outside recursion or in an actual used in a relevant posn. When this ; computation ceases to add any new posns we consider the uncollected posns to ; be irrelevant. ; For example, in (defun foo (x y) (if (zerop x) 0 (foo (1- x) (cons x y)))) we ; intially guess that x is relevant and y is not. The next iteration adds ; nothing, because y is not used in the x posn, so we are done. ; On the other hand, in (defun foo (x y) (if (< x 2) x (foo y 0))) we might ; once again guess that y is irrelevant. However, the second pass would note ; the occurrence of y in a relevant posn and would sweep it into the set. We ; conclude that there are no irrelevant posns in this definition. ; So far we have not discussed lambda expressions; they are unusual in this ; setting because they may hide recursive calls that we should analyze. We do ; not want to expand the lambdas away, for fear of combinatoric explosions. ; Instead, we expand the clique-alist, by adding, for each lambda-application a ; new entry that pairs that lambda expression with the appropriate terms. ; (That is, the "fn" of the new clique member is the lambda expression itself.) ; Thus, we actually use assoc-equal instead of assoc-eq when looking in ; clique-alist. (defun formal-position (var formals i) (cond ((null formals) i) ((eq var (car formals)) i) (t (formal-position var (cdr formals) (1+ i))))) (defun make-posns (formals vars) (cond ((null vars) nil) (t (cons (formal-position (car vars) formals 0) (make-posns formals (cdr vars)))))) (mutual-recursion (defun relevant-posns-term (fn formals term fns clique-alist posns) ; Term is a term occurring in the body of fn which has formals formals. We ; collect a posn into posns if it is used outside a recursive call (or in an ; already known relevant actual to a recursive call). See the Essay on the ; Identification of Irrelevant Formals. (cond ((variablep term) (add-to-set (formal-position term formals 0) posns)) ((fquotep term) posns) ((equal (ffn-symb term) fn) (relevant-posns-call fn formals (fargs term) 0 fns clique-alist :same posns)) ((member-equal (ffn-symb term) fns) (relevant-posns-call fn formals (fargs term) 0 fns clique-alist (cdr (assoc-equal (ffn-symb term) clique-alist)) posns)) (t (relevant-posns-term-lst fn formals (fargs term) fns clique-alist posns)))) (defun relevant-posns-term-lst (fn formals lst fns clique-alist posns) (cond ((null lst) posns) (t (relevant-posns-term-lst fn formals (cdr lst) fns clique-alist (relevant-posns-term fn formals (car lst) fns clique-alist posns))))) (defun relevant-posns-call (fn formals actuals i fns clique-alist called-fn-posns posns) ; See the Essay on the Identification of Irrelevant Formals. ; This function extends the set, posns, of posns for fn that are known to be ; relevant. It does so by analyzing the given (tail of the) actuals for a call ; of some function in the clique, which we denote as called-fn, where that call ; occurs in the body of fn (which has the given formals). Called-fn-posns is ; the set of posns for called-fn that are known to be relevant, except for the ; case that called-fn is fn, in which case called-fn-posns is :same. The ; formal i, which is initially 0, is the position in called-fn's argument ; list of the first element of actuals. We extend posns, the posns of fn known ; to be relevant, by seeing which posns are used in the actuals in the relevant ; posns of called-fn (i.e., called-fn-posns). (cond ((null actuals) posns) (t (relevant-posns-call fn formals (cdr actuals) (1+ i) fns clique-alist called-fn-posns (if (member i (if (eq called-fn-posns :same) posns ; might be extended through recursive calls called-fn-posns)) (relevant-posns-term fn formals (car actuals) fns clique-alist posns) posns))))) ) (defun relevant-posns-clique1 (fns arglists bodies all-fns ans) (cond ((null fns) ans) (t (relevant-posns-clique1 (cdr fns) (cdr arglists) ; nil, once we cdr down to the lambdas (cdr bodies) ; nil, once we cdr down to the lambdas all-fns (let* ((posns (cdr (assoc-equal (car fns) ans))) (new-posns (cond ((flambdap (car fns)) (relevant-posns-term (car fns) (lambda-formals (car fns)) (lambda-body (car fns)) all-fns ans posns)) (t (relevant-posns-term (car fns) (car arglists) (car bodies) all-fns ans posns))))) (cond ((equal posns new-posns) ; optimization ans) (t (put-assoc-equal (car fns) new-posns ans)))))))) (defun relevant-posns-clique-recur (fns arglists bodies clique-alist) (let ((clique-alist1 (relevant-posns-clique1 fns arglists bodies fns clique-alist))) (cond ((equal clique-alist1 clique-alist) clique-alist) (t (relevant-posns-clique-recur fns arglists bodies clique-alist1))))) (defun relevant-posns-clique-init (fns arglists guards split-types-terms measures ignores ignorables ans) ; We associate each function in fns, reversing the order in fns, with ; obviously-relevant formal positions. (cond ((null fns) ans) (t (relevant-posns-clique-init (cdr fns) (cdr arglists) (cdr guards) (cdr split-types-terms) (cdr measures) (cdr ignores) (cdr ignorables) (acons (car fns) (make-posns (car arglists) (all-vars1 (car guards) (all-vars1 (car split-types-terms) (all-vars1 (car measures) ; Ignored formals are considered not to be irrelevant. Should we do similarly ; for ignorable formals? ; - If yes (ignorables are not irrelevant), then we may miss some irrelevant ; formals. Of course, it is always OK to miss some irrelevant formals, but ; we would prefer not to miss them needlessly. ; - If no (ignorables are irrelevant), then we may report an ignorable variable ; as irrelevant, which might annoy the user even though it really is ; irrelevant, if "ignorable" not only means "could be ignored" but also means ; "could be irrelevant". ; We choose "yes". If the user has gone through the trouble to label a ; variable as irrelevant, then the chance that irrelevance is due to a typo are ; dwarfed by the chance that irrelevance is due to being an ignorable var. (union-eq (car ignorables) (car ignores)))))) ans))))) ; We now develop the code to generate the clique-alist for lambda expressions. (mutual-recursion (defun relevant-posns-lambdas (term ans) (cond ((or (variablep term) (fquotep term)) ans) ((flambdap (ffn-symb term)) (relevant-posns-lambdas (lambda-body (ffn-symb term)) (acons (ffn-symb term) nil (relevant-posns-lambdas-lst (fargs term) ans)))) (t (relevant-posns-lambdas-lst (fargs term) ans)))) (defun relevant-posns-lambdas-lst (termlist ans) (cond ((endp termlist) ans) (t (relevant-posns-lambdas-lst (cdr termlist) (relevant-posns-lambdas (car termlist) ans))))) ) (defun relevant-posns-merge (alist acc) (cond ((endp alist) acc) ((endp (cdr alist)) (cons (car alist) acc)) ((equal (car (car alist)) (car (cadr alist))) (relevant-posns-merge (acons (caar alist) (union$ (cdr (car alist)) (cdr (cadr alist))) (cddr alist)) acc)) (t (relevant-posns-merge (cdr alist) (cons (car alist) acc))))) (defun relevant-posns-lambdas-top (bodies) (let ((alist (merge-sort-lexorder (relevant-posns-lambdas-lst bodies nil)))) (relevant-posns-merge alist nil))) (defun relevant-posns-clique (fns arglists guards split-types-terms measures ignores ignorables bodies) ; We compute the relevant posns in an expanded clique alist (one in which the ; lambda expressions have been elevated to clique membership). The list of ; relevant posns includes the relevant lambda posns. We do it by iteratively ; enlarging an iniital clique-alist until it is closed. (let* ((clique-alist1 (relevant-posns-clique-init fns arglists guards split-types-terms measures ignores ignorables nil)) (clique-alist2 (relevant-posns-lambdas-top bodies))) (relevant-posns-clique-recur (append fns (strip-cars clique-alist2)) arglists bodies (revappend clique-alist1 clique-alist2)))) (defun irrelevant-non-lambda-slots-clique2 (fn formals i posns acc) (cond ((endp formals) acc) (t (irrelevant-non-lambda-slots-clique2 fn (cdr formals) (1+ i) posns (cond ((member i posns) acc) (t (cons (list* fn i (car formals)) acc))))))) (defun irrelevant-non-lambda-slots-clique1 (fns arglists clique-alist acc) (cond ((endp fns) (assert$ (or (null clique-alist) (flambdap (caar clique-alist))) acc)) (t (assert$ (eq (car fns) (caar clique-alist)) (irrelevant-non-lambda-slots-clique1 (cdr fns) (cdr arglists) (cdr clique-alist) (irrelevant-non-lambda-slots-clique2 (car fns) (car arglists) 0 (cdar clique-alist) acc)))))) (defun irrelevant-non-lambda-slots-clique (fns arglists guards split-types-terms measures ignores ignorables bodies) ; Let clique-alist be an expanded clique alist (one in which lambda expressions ; have been elevated to clique membership). Return all the irrelevant slots ; for the non-lambda members of the clique. ; A "slot" is a triple of the form (fn n . var), where fn is a function symbol, ; n is some nonnegative integer less than the arity of fn, and var is the nth ; formal of fn. If (fn n . var) is in the list returned by this function, then ; the nth formal of fn, namely var, is irrelevant to the value computed by fn. (let ((clique-alist (relevant-posns-clique fns arglists guards split-types-terms measures ignores ignorables bodies))) (irrelevant-non-lambda-slots-clique1 fns arglists clique-alist nil))) (defun tilde-*-irrelevant-formals-msg1 (slots) (cond ((null slots) nil) (t (cons (cons "~n0 formal of ~x1, ~x2," (list (cons #\0 (list (1+ (cadar slots)))) (cons #\1 (caar slots)) (cons #\2 (cddar slots)))) (tilde-*-irrelevant-formals-msg1 (cdr slots)))))) (defun tilde-*-irrelevant-formals-msg (slots) (list "" "~@*" "~@* and the " "~@* the " (tilde-*-irrelevant-formals-msg1 slots))) (defun chk-irrelevant-formals (fns arglists guards split-types-terms measures ignores ignorables bodies ctx state) (let ((irrelevant-formals-ok (cdr (assoc-eq :irrelevant-formals-ok (table-alist 'acl2-defaults-table (w state)))))) (cond ((or (eq irrelevant-formals-ok t) (and (eq irrelevant-formals-ok :warn) (warning-disabled-p "Irrelevant-formals"))) (value nil)) (t (let ((irrelevant-slots (irrelevant-non-lambda-slots-clique fns arglists guards split-types-terms measures ignores ignorables bodies))) (cond ((null irrelevant-slots) (value nil)) ((eq irrelevant-formals-ok :warn) (pprogn (warning$ ctx ("Irrelevant-formals") "The ~*0 ~#1~[is~/are~] irrelevant. See :DOC ~ irrelevant-formals." (tilde-*-irrelevant-formals-msg irrelevant-slots) (if (cdr irrelevant-slots) 1 0)) (value nil))) (t (er soft ctx "The ~*0 ~#1~[is~/are~] irrelevant. See :DOC ~ irrelevant-formals." (tilde-*-irrelevant-formals-msg irrelevant-slots) (if (cdr irrelevant-slots) 1 0))))))))) (deflabel irrelevant-formals :doc ":Doc-Section ACL2::Programming formals that are used but only insignificantly~/ Let ~c[fn] be a function of ~c[n] arguments. Let ~c[x] be the ~c[i]th formal of ~c[fn]. We say ~c[x] is ``irrelevant in ~c[fn]'' if ~c[x] does not occur in either the ~il[guard] or the measure for ~c[fn], and the value of ~c[(fn a1...ai...an)] is independent of ~c[ai].~/ The easiest way to define a function with an irrelevant formal is simply not to use the formal in the body of the function. Such formals are said to be ``ignored'' by Common Lisp and a special declaration is provided to allow ignored formals. ACL2 makes a distinction between ignored and irrelevant formals. Note however that if a variable is ~ilc[declare]d ~c[ignore]d or ~c[ignorable], then it will not be reported as irrelevant. An example of an irrelevant formal is ~c[x] in the definition of ~c[fact] below. ~bv[] (defun fact (i x) (declare (xargs :guard (and (integerp i) (<= 0 i)))) (if (zerop i) 1 (* i (fact (1- i) (cons i x))))). ~ev[] Observe that ~c[x] is only used in recursive calls of ~c[fact]; it never ``gets out'' into the result. ACL2 can detect some irrelevant formals by a closure analysis on how the formals are used. For example, if the ~c[i]th formal is only used in the ~c[i]th argument position of recursive calls, then it is irrelevant. This is how ~c[x] is used above. It is possible for a formal to appear only in recursive calls but still be relevant. For example, ~c[x] is ~st[not] irrelevant below, even though it only appears in the recursive call. ~bv[] (defun fn (i x) (if (zerop i) 0 (fn x (1- i)))) ~ev[] The key observation above is that while ~c[x] only appears in a recursive call, it appears in an argument position, namely ~c[i]'s, that is relevant. (The function above can be admitted with a ~c[:measure] of ~c[(+ (nfix i) (nfix x))].) Establishing that a formal is irrelevant, in the sense defined above, can be an arbitrarily hard problem because it requires theorem proving. For example, is ~c[x] irrelevant below? ~bv[] (defun test (i j k x) (if (p i j k) 0 x)) ~ev[] Note that the value of ~c[(test i j k x)] is independent of ~c[x] ~-[] thus making ~c[x] irrelevant ~-[] precisely if ~c[(p i j k)] is a theorem. ACL2's syntactic analysis of a definition does not guarantee to notice all irrelevant formals. We regard the presence of irrelevant formals as an indication that something is wrong with the definition. We cause an error on such definitions and suggest that you recode the definition so as to eliminate the irrelevant formals. If you must have an irrelevant formal, one way to ``trick'' ACL2 into accepting the definition, without slowing down the execution of your function, is to use the formal in an irrelevant way in the ~il[guard]. For example, to admit fact, above, with its irrelevant ~c[x] one might use ~bv[] (defun fact (i x) (declare (xargs :guard (and (integerp i) (<= 0 i) (equal x x)))) (if (zerop i) 0 (* i (fact (1- i) (cons i x))))) ~ev[] For those who really want to turn off this feature, we have provided a way to use the ~ilc[acl2-defaults-table] for this purpose; ~pl[set-irrelevant-formals-ok].") (defun chk-logic-subfunctions (names0 names terms wrld str ctx state) ; Assume we are defining names in terms of terms (1:1 correspondence). Assume ; also that the definitions are to be :logic. Then we insist that every ; function used in terms be :logic. Str is a string used in our error ; message and is either "guard", "split-types expression", or "body". (cond ((null names) (value nil)) (t (let ((bad (collect-programs (set-difference-eq (all-fnnames (car terms)) names0) wrld))) (cond (bad (er soft ctx "The ~@0 for ~x1 calls the :program function~#2~[ ~ ~&2~/s ~&2~]. We require that :logic definitions be ~ defined entirely in terms of :logically defined ~ functions. See :DOC defun-mode." str (car names) bad)) (t (chk-logic-subfunctions names0 (cdr names) (cdr terms) wrld str ctx state))))))) ;; RAG - This function strips out the functions which are ;; non-classical in a chk-acceptable-defuns "fives" structure. #+:non-standard-analysis (defun get-non-classical-fns-from-list (names wrld fns-sofar) (cond ((null names) fns-sofar) (t (let ((fns (if (or (not (symbolp (car names))) (classicalp (car names) wrld)) fns-sofar (cons (car names) fns-sofar)))) (get-non-classical-fns-from-list (cdr names) wrld fns))))) ;; RAG - This function takes in a list of terms and returns any ;; non-classical functions referenced in the terms. #+:non-standard-analysis (defmacro get-non-classical-fns (lst wrld) `(get-non-classical-fns-aux ,lst ,wrld nil)) #+:non-standard-analysis (defun get-non-classical-fns-aux (lst wrld fns-sofar) (cond ((null lst) fns-sofar) (t (get-non-classical-fns-aux (cdr lst) wrld (get-non-classical-fns-from-list (all-fnnames (car lst)) wrld fns-sofar))))) ;; RAG - this function checks that the measures used to accept the definition ;; are classical. Note, *no-measure* is a signal that the default measure is ;; being used (see get-measures1) -- and in that case, we know it's classical, ;; since it's just the acl2-count of some tuple consisting of variables in the ;; defun. #+:non-standard-analysis (defun strip-missing-measures (lst accum) (if (consp lst) (if (equal (car lst) *no-measure*) (strip-missing-measures (cdr lst) accum) (strip-missing-measures (cdr lst) (cons (car lst) accum))) accum)) #+:non-standard-analysis (defun chk-classical-measures (measures names ctx wrld state) (let ((non-classical-fns (get-non-classical-fns (strip-missing-measures measures nil) wrld))) (cond ((null non-classical-fns) (value nil)) (t (er soft ctx "It is illegal to use non-classical measures to justify a ~ recursive definition. However, there has been an ~ attempt to recursively define ~*0 using the ~ non-classical functions ~*1 in the measure." `("" "~x*," "~x* and " "~x*, " ,names) `("" "~x*," "~x* and " "~x*, " ,non-classical-fns)))))) ;; RAG - This function checks that non-classical functions only appear ;; on non-recursive functions. #+:non-standard-analysis (defun chk-no-recursive-non-classical (non-classical-fns names mp rel measures bodies ctx wrld state) (cond ((and (int= (length names) 1) (not (ffnnamep-mod-mbe (car names) (car bodies)))) ; Then there is definitely no recursion (see analogous computation in ; putprop-recursivep-lst). Note that with :bogus-mutual-recursion-ok, a clique ; of size greater than 1 might not actually have any recursion. But then it ; will be up to the user in this case to eliminate the appearance of possible ; recursion. (value nil)) ((not (null non-classical-fns)) (er soft ctx "It is illegal to use non-classical functions in a ~ recursive definition. However, there has been an ~ attempt to recursively define ~*0 using the ~ non-classical function ~*1." `("" "~x*," "~x* and " "~x*, " ,names) `("" "~x*," "~x* and " "~x*, " ,non-classical-fns))) ((not (and (classicalp mp wrld) (classicalp rel wrld))) (er soft ctx "It is illegal to use a non-classical function as a ~ well-ordering or well-ordered domain in a recursive ~ definition. However, there has been an ~ attempt to recursively define ~*0 using the ~ well-ordering function ~x* and domain ~x*." `("" "~x*," "~x* and " "~x*, " ,names) mp rel)) (t (chk-classical-measures measures names ctx wrld state)))) (defun union-collect-non-x (x lst) (cond ((endp lst) nil) (t (union-equal (collect-non-x x (car lst)) (union-collect-non-x x (cdr lst)))))) (defun translate-measures (terms ctx wrld state) ; WARNING: Keep this in sync with translate-term-lst. Here we allow (:? var1 ; ... vark), where the vari are distinct variables. (cond ((null terms) (value nil)) (t (er-let* ((term (cond ((and (consp (car terms)) (eq (car (car terms)) :?)) (cond ((arglistp (cdr (car terms))) (value (car terms))) (t (er soft ctx "A measure whose car is :? must be of the ~ form (:? v1 ... vk), where (v1 ... vk) is ~ a list of distinct variables. The measure ~ ~x0 is thus illegal." (car terms))))) (t (translate (car terms) ; One might use stobjs-out '(nil) below, if one felt uneasy about measures ; changing state. But we know no logical justification for this feeling, nor ; do we ever expect to execute the measures in Common Lisp. In fact we find it ; useful to be able to pass state into a measure even when its argument ; position isn't "state"; consider for example the function big-clock-entry. t ; stobjs-out t t ctx wrld state)))) (rst (translate-measures (cdr terms) ctx wrld state))) (value (cons term rst)))))) (defun redundant-predefined-error-msg (name) (let ((pkg-name (and (symbolp name) ; probably always true (symbol-package-name name)))) (msg "ACL2 is processing a redundant definition of the name ~x0, which is ~ ~#1~[already defined using special raw Lisp code~/predefined in the ~ ~x2 package~]. For technical reasons, we disallow non-LOCAL ~ redundant definitions in such cases; see :DOC redundant-events. ~ Consider wrapping this definition inside a call of LOCAL." name (if (equal pkg-name *main-lisp-package-name*) 1 0) *main-lisp-package-name*))) (defun chk-acceptable-defuns-redundancy (names ctx wrld state) ; The following comment is referenced in :doc redundant-events and in a comment ; in defmacro-fn. If it is removed or altered, consider modifying that ; documentation and comment (respectively). ; The definitions of names have tentatively been determined to be redundant. ; We cause an error if this is not allowed, else return (value 'redundant). ; Here we cause an error for non-local redundant built-in definitions. The ; reason is that some built-ins are defined using #-acl2-loop-only code. So ; consider what happens when such a built-in function has a definition ; occurring in the compiled file for a book. At include-book time, this new ; definition will be loaded from that compiled file, presumably without any ; #-acl2-loop-only. ; The following book certified in ACL2 Version_3.3 built on SBCL, where we have ; #+acl2-mv-as-values and also we load compiled files. In this case the ; problem was that while ACL2 defined prog2$ as a macro in #-acl2-loop-only, ; for proper multiple-value handling, nevertheless that definition was ; overridden by the compiled definition loaded by the compiled file associated ; with the book "prog2" (not shown here, but containing the redundant ; #+acl2-loop-only definition of prog2$). ; (in-package "ACL2") ; ; (include-book "prog2") ; redundant #+acl2-loop-only def. of prog2$ ; ; (defun foo (x) ; (prog2$ 3 (mv x x))) ; ; (defthm foo-fact ; (equal (foo 4) ; (list 4 4)) ; :rule-classes nil) ; ; (verify-guards foo) ; ; (defthm foo-fact-bogus ; (equal (foo 4) ; (list 4)) ; :rule-classes nil) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use (foo-fact foo-fact-bogus))) ; :rule-classes nil) ; After Version_4.1, prog2$ became just a macro whose calls expanded to forms ; (return-last 'progn ...). But the idea illustrated above is still relevant. ; We make this restriction for functions whose #+acl2-loop-only and ; #-acl2-loop-only definitions disagree. See ; fns-different-wrt-acl2-loop-only. ; By the way, it is important to include functions defined in #+acl2-loop-only ; that have no definition in #-acl2-loop-only. This becomes clear if you ; create a book with (in-package "ACL2") followed by the definition of LENGTH ; from axioms.lisp. In an Allegro CL build of ACL2 Version_3.3, you will get a ; raw Lisp error during the compilation phase when you apply certify-book to ; this book, complaining about redefining a function in the COMMON-LISP ; package. ; Note that we can avoid the restriction for local definitions, since those ; will be ignored in the compiled file. (cond ((and (not (f-get-global 'in-local-flg state)) (not (global-val 'boot-strap-flg (w state))) (not (f-get-global 'redundant-with-raw-code-okp state)) (let ((recp (getprop (car names) 'recursivep nil 'current-acl2-world wrld)) (bad-fns (if (eq (symbol-class (car names) wrld) :program) (f-get-global 'program-fns-with-raw-code state) (f-get-global 'logic-fns-with-raw-code state)))) (if recp (intersectp-eq recp bad-fns) (member-eq (car names) bad-fns)))) (er soft ctx "~@0" (redundant-predefined-error-msg (car names)))) (t (value 'redundant)))) (defun chk-acceptable-defuns-verify-guards-er (names ctx wrld state) ; The redundancy check during processing the definition(s) of names has ; returned 'verify-guards. We cause an error. If that proves to be too ; inconvenient for users, we could look into arranging for a call of ; verify-guards. (let ((include-book-path (global-val 'include-book-path wrld))) (mv-let (erp ev-wrld-and-cmd-wrld state) (state-global-let* ((inhibit-output-lst (cons 'error (f-get-global 'inhibit-output-lst state)))) ; Keep the following in sync with pe-fn. (let ((wrld (w state))) (er-let* ((ev-wrld (er-decode-logical-name (car names) wrld :pe state)) (cmd-wrld (superior-command-world ev-wrld wrld :pe state))) (value (cons ev-wrld cmd-wrld))))) (mv-let (erp1 val1 state) (er soft ctx "The definition of ~x0~#1~[~/ (along with the others in its ~ mutual-recursion clique)~]~@2 demands guard verification, ~ but there is already a corresponding existing definition ~ without its guard verified. ~@3Use verify-guards instead; ~ see :DOC verify-guards. ~#4~[Here is the existing ~ definition of ~x0:~/The existing definition of ~x0 appears ~ to precede this one in the same top-level command.~]" (car names) names (cond (include-book-path (cons " in the book ~xa" (list (cons #\a (car include-book-path))))) (t "")) (cond ((cddr include-book-path) (cons "Note: The above book is included under the ~ following sequence of included books from outside ~ to inside, i.e., top-level included book ~ first:~|~&b.~|" (list (cons #\b (reverse (cdr include-book-path)))))) ((cdr include-book-path) (cons "Note: The above book is included inside the book ~ ~xb. " (list (cons #\b (cadr include-book-path))))) (t "")) (if erp 1 0)) (pprogn (if erp state (pe-fn1 wrld (standard-co state) (car ev-wrld-and-cmd-wrld) (cdr ev-wrld-and-cmd-wrld) state)) (mv erp1 val1 state)))))) (defun chk-non-executablep (defun-mode non-executablep ctx state) ; We check that the value for keyword :non-executable is legal with respect to ; the given defun-mode. (cond ((eq non-executablep nil) (value nil)) ((eq defun-mode :logic) (cond ((eq non-executablep t) (value nil)) (t (er soft ctx "The :NON-EXECUTABLE flag for :LOGIC mode functions ~ must be ~x0 or ~x1, but ~x2 is neither." t nil non-executablep)))) (t ; (eq defun-mode :program) (cond ((eq non-executablep :program) (value nil)) (t (er soft ctx "The :NON-EXECUTABLE flag for :PROGRAM mode functions ~ must be ~x0 or ~x1, but ~x2 is neither." :program nil non-executablep)))))) (defun chk-acceptable-defuns0 (fives ctx wrld state) ; This helper function for chk-acceptable-defuns factors out some computation, ; as requested by Daron Vroon for ACL2s purposes. (er-let* ((stobjs-in-lst (get-stobjs-in-lst fives ctx wrld state)) (defun-mode (get-unambiguous-xargs-flg :MODE fives (default-defun-mode wrld) ctx state)) (non-executablep (get-unambiguous-xargs-flg :NON-EXECUTABLE fives nil ctx state)) (verify-guards (get-unambiguous-xargs-flg :VERIFY-GUARDS fives '(unspecified) ctx state))) (er-progn (chk-defun-mode defun-mode ctx state) (chk-non-executablep defun-mode non-executablep ctx state) (cond ((consp verify-guards) ; This means that the user did not specify a :verify-guards. We will default ; it appropriately. (value nil)) ((eq defun-mode :program) (if (eq verify-guards nil) (value nil) (er soft ctx "When the :MODE is :program, the only legal :VERIFY-GUARDS ~ setting is NIL. ~x0 is illegal." verify-guards))) ((or (eq verify-guards nil) (eq verify-guards t)) (value nil)) (t (er soft ctx "The legal :VERIFY-GUARD settings are NIL and T. ~x0 is ~ illegal." verify-guards))) (let* ((symbol-class (cond ((eq defun-mode :program) :program) ((consp verify-guards) (cond ((= (default-verify-guards-eagerness wrld) 0) :ideal) ((= (default-verify-guards-eagerness wrld) 1) (if (get-guardsp fives wrld) :common-lisp-compliant :ideal)) (t :common-lisp-compliant))) (verify-guards :common-lisp-compliant) (t :ideal)))) (value (list* stobjs-in-lst defun-mode non-executablep symbol-class)))))) (defun get-boolean-unambiguous-xargs-flg-lst (key lst default ctx state) (er-let* ((lst (get-unambiguous-xargs-flg-lst key lst default ctx state))) (cond ((boolean-listp lst) (value lst)) (t (er soft ctx "The value~#0~[ ~&0 is~/s ~&0 are~] illegal for XARGS key ~x1, as ~x2 and ~x3 are the only legal values for this key." lst key t nil))))) (defun chk-acceptable-defuns1 (names fives stobjs-in-lst defun-mode symbol-class rc non-executablep ctx wrld state #+:non-standard-analysis std-p) ; WARNING: This function installs a world, hence should only be called when ; protected by a revert-world-on-error (a condition that should be inherited ; when called by chk-acceptable-defuns). (let ((docs (get-docs fives)) (big-mutrec (big-mutrec names)) (arglists (strip-cadrs fives)) (default-hints (default-hints wrld)) (assumep (or (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals))) (reclassifying-all-programp (and (eq rc 'reclassifying) (all-programp names wrld)))) (er-let* ((wrld1 (chk-just-new-names names 'function rc ctx wrld state)) (doc-pairs (translate-doc-lst names docs ctx state)) (wrld2 (update-w big-mutrec (store-stobjs-ins names stobjs-in-lst (putprop-x-lst2 names 'formals arglists (putprop-x-lst1 names 'symbol-class symbol-class wrld1))))) (untranslated-measures ; If the defun-mode is :program, or equivalently, the symbol-class is :program, ; then we don't need the measures. But we do need "measures" that pass the ; tests below, such as the call of chk-free-and-ignored-vars-lsts. So, we ; simply pretend that no measures were supplied, which is clearly reasonable if ; we are defining the functions to have symbol-class :program. (get-measures symbol-class fives ctx state)) (measures (translate-measures untranslated-measures ctx wrld2 state)) (ruler-extenders-lst (get-ruler-extenders-lst symbol-class fives ctx state)) (rel (get-unambiguous-xargs-flg :WELL-FOUNDED-RELATION fives (default-well-founded-relation wrld2) ctx state)) (do-not-translate-hints (value (or assumep (eq (ld-skip-proofsp state) 'initialize-acl2)))) (hints (if (or do-not-translate-hints (eq defun-mode :program)) (value nil) (let ((hints (get-hints fives))) (if hints (translate-hints+ (cons "Measure Lemma for" (car names)) hints default-hints ctx wrld2 state) (value nil))))) (guard-hints (if (or do-not-translate-hints (eq defun-mode :program)) (value nil) ; We delay translating the guard-hints until after the definition is installed, ; so that for example the hint setting :in-theory (enable foo), where foo is ; being defined, won't cause an error. (value (append (get-guard-hints fives) default-hints)))) (std-hints #+:non-standard-analysis (cond ((and std-p (not assumep)) (translate-hints+ (cons "Std-p for" (car names)) (get-std-hints fives) default-hints ctx wrld2 state)) (t (value nil))) #-:non-standard-analysis (value nil)) (otf-flg (if do-not-translate-hints (value nil) (get-unambiguous-xargs-flg :OTF-FLG fives t ctx state))) (guard-debug (get-unambiguous-xargs-flg :GUARD-DEBUG fives ; Note: If you change the following default for guard-debug, then consider ; changing it in verify-guards as well, and fix the "Otherwise" message about ; :guard-debug in prove-guard-clauses. nil ; guard-debug default ctx state)) (split-types-lst (get-boolean-unambiguous-xargs-flg-lst :SPLIT-TYPES fives nil ctx state)) (normalizeps (get-boolean-unambiguous-xargs-flg-lst :NORMALIZE fives t ctx state))) (er-progn (cond ((not (and (symbolp rel) (assoc-eq rel (global-val 'well-founded-relation-alist wrld2)))) (er soft ctx "The :WELL-FOUNDED-RELATION specified by XARGS must be a symbol ~ which has previously been shown to be a well-founded relation. ~ ~x0 has not been. See :DOC well-founded-relation." rel)) (t (value nil))) (let ((mp (cadr (assoc-eq rel (global-val 'well-founded-relation-alist wrld2))))) (er-let* ((bodies-and-bindings (translate-bodies non-executablep ; t or :program names arglists (get-bodies fives) stobjs-in-lst ; see "slight abuse" comment below ctx wrld2 state))) (let* ((bodies (car bodies-and-bindings)) (bindings (super-defun-wart-bindings (cdr bodies-and-bindings))) #+:non-standard-analysis (non-classical-fns (get-non-classical-fns bodies wrld2))) (er-progn (if assumep (value nil) (er-progn (chk-stobjs-out-bound names bindings ctx state) #+:non-standard-analysis (chk-no-recursive-non-classical non-classical-fns names mp rel measures bodies ctx wrld2 state))) (let* ((wrld30 (store-super-defun-warts-stobjs-in names wrld2)) (wrld31 (store-stobjs-out names bindings wrld30)) (wrld3 #+:non-standard-analysis (if (or std-p (null non-classical-fns)) wrld31 (putprop-x-lst1 names 'classicalp nil wrld31)) #-:non-standard-analysis wrld31)) (er-let* ((guards (translate-term-lst (get-guards fives split-types-lst nil wrld2) ; Warning: Keep this call of translate-term-lst in sync with translation of a ; guard in chk-defabsstobj-guard. ; Stobjs-out: ; Each guard returns one, non-stobj result. This arg is used for each guard. ; By using stobjs-out '(nil) we enable the thorough checking of the use of ; state. Thus, the above call ensures that guards do not modify (or return) ; state. We are taking the conservative position because intuitively there is ; a confusion over the question of whether, when, and how often guards are run. ; By prohibiting them from modifying state we don't have to answer the ; questions about when they run. '(nil) ; Logic-modep: ; Since guards have nothing to do with the logic, and since they may ; legitimately have mode :program, we set logic-modep to nil here. This arg is ; used for each guard. nil ; Known-stobjs-lst: ; Here is a slight abuse. Translate-term-lst is expecting, in this ; argument, a list in 1:1 correspondence with its first argument, ; specifying the known-stobjs for the translation of corresponding ; terms. But we are supplying the stobjs-in for the term, not the ; known-stobjs. The former is a list of stobj flags and the latter is ; a list of stobj names, i.e., the list we supply may contain a NIL ; element where it should have no element at all. This is allowed by ; stobjsp. Technically we ought to map over the stobjs-in-lst and ; change each element to its collect-non-x nil. stobjs-in-lst ctx ; Note the use of wrld3 instead of wrld2. It is important that the proper ; stobjs-out be put on the new functions before we translate the guards! When ; we first allowed the functions being defined to be used in their guards (in ; v3-6), we introduced a soundness bug found by Sol Swords just after the ; release of v4-0, as follows. ; (defun foo (x) ; (declare (xargs :guard (or (consp x) ; (atom (foo '(a . b)))))) ; (mv (car x) ; (mbe :logic (consp x) ; :exec t))) ; ; (defthm bad ; nil ; :hints (("goal" :use ((:instance foo (x nil))))) ; :rule-classes nil) wrld3 state)) (split-types-terms (translate-term-lst (get-guards fives split-types-lst t wrld2) ; The arguments below are the same as those for the preceding call of ; translate-term-lst. '(nil) nil stobjs-in-lst ctx wrld3 state))) (er-progn (if (eq defun-mode :logic) ; Although translate checks for inappropriate calls of :program functions, ; translate11 and translate1 do not. (er-progn (chk-logic-subfunctions names names guards wrld3 "guard" ctx state) (chk-logic-subfunctions names names split-types-terms wrld3 "split-types expression" ctx state) (chk-logic-subfunctions names names bodies wrld3 "body" ctx state)) (value nil)) (if (eq symbol-class :common-lisp-compliant) (er-progn (chk-common-lisp-compliant-subfunctions names names guards wrld3 "guard" ctx state) (chk-common-lisp-compliant-subfunctions names names split-types-terms wrld3 "split-types expression" ctx state) (chk-common-lisp-compliant-subfunctions names names bodies wrld3 "body" ctx state)) (value nil)) (mv-let (erp val state) ; This mv-let is just an aside that lets us conditionally check a bunch of ; conditions we needn't do in assumep mode. (cond (assumep (mv nil nil state)) (t (let ((ignores (get-ignores fives)) (ignorables (get-ignorables fives))) (er-progn (chk-free-and-ignored-vars-lsts names arglists guards split-types-terms measures ignores ignorables bodies ctx state) (chk-irrelevant-formals names arglists guards split-types-terms measures ignores ignorables bodies ctx state) (chk-mutual-recursion names bodies ctx state))))) (cond (erp (mv erp val state)) (t (value (list 'chk-acceptable-defuns names arglists docs doc-pairs guards measures ruler-extenders-lst mp rel hints guard-hints std-hints ;nil for non-std otf-flg bodies symbol-class normalizeps reclassifying-all-programp wrld3 non-executablep guard-debug split-types-terms )))))))))))))))) (defun conditionally-memoized-fns (fns memoize-table) (declare (xargs :guard (and (symbol-listp fns) (alistp memoize-table)))) (cond ((endp fns) nil) (t (let ((alist (cdr (assoc-eq (car fns) memoize-table)))) (cond ((and alist ; optimization (let ((condition-fn (cdr (assoc-eq :condition-fn alist)))) (and condition-fn (not (eq condition-fn t))))) (cons (car fns) (conditionally-memoized-fns (cdr fns) memoize-table))) (t (conditionally-memoized-fns (cdr fns) memoize-table))))))) ;; RAG - I modified the function below to check for recursive ;; definitions using non-classical predicates. (defun chk-acceptable-defuns (lst ctx wrld state #+:non-standard-analysis std-p) ; WARNING: This function installs a world, hence should only be called when ; protected by a revert-world-on-error. ; Rockwell Addition: We now also return the non-executable flag. ; This function does all of the syntactic checking associated with defuns. It ; causes an error if it doesn't like what it sees. It returns the traditional ; 3 values of an error-producing, output-producing function. However, the ; "real" value of the function is a list of items extracted from lst during the ; checking. These items are: ; names - the names of the fns in the clique ; arglists - their formals ; docs - their documentation strings ; pairs - the (section-symbol . citations) pairs parsed from docs ; guards - their translated guards ; measures - their translated measure terms ; ruler-extenders-lst ; - their ruler-extenders ; mp - the domain predicate (e.g., o-p) for well-foundedness ; rel - the well-founded relation (e.g., o<) ; hints - their translated hints, to be used during the proofs of ; the measure conjectures, all flattened into a single list ; of hints of the form ((cl-id . settings) ...). ; guard-hints ; - like hints but to be used for the guard conjectures and ; untranslated ; std-hints (always returned, but only of interest when ; #+:non-standard-analysis) ; - like hints but to be used for the std-p conjectures ; otf-flg - t or nil, used as "Onward Thru the Fog" arg for prove ; bodies - their translated bodies ; symbol-class ; - :program, :ideal, or :common-lisp-compliant ; normalizeps ; - list of Booleans, used to determine for each fn in the clique ; whether its body is to be normalized ; reclassifyingp ; - t or nil, t if this is a reclassifying from :program ; with identical defs. ; wrld - a modified wrld in which the following properties ; may have been stored for each fn in names: ; 'formals, 'stobjs-in and 'stobjs-out ; non-executablep - t, :program, or nil according to whether these defuns ; are to be non-executable. Defuns with non-executable t may ; violate the translate conventions on stobjs. ; guard-debug ; - t or nil, used to add calls of EXTRA-INFO to guard conjectures ; split-types-terms ; - list of translated terms, each corresponding to type ; declarations made for a definition with XARGS keyword ; :SPLIT-TYPES T (er-let* ((fives (chk-defuns-tuples lst nil ctx wrld state)) ; Fives is a list in 1:1 correspondence with lst. Each element of ; fives is a 5-tuple of the form (name args doc edcls body). Consider the ; element of fives that corresponds to ; (name args (DECLARE ...) "Doc" (DECLARE ...) body) ; in lst. Then that element of fives is (name args "Doc" (...) body), ; where the ... is the cdrs of the DECLARE forms appended together. ; No translation has yet been applied to them. The newness of name ; has not been checked yet either, though we know it is all but new, ; i.e., is a symbol in the right package. We do know that the args ; are all legal. (names (value (strip-cars fives)))) (er-progn (chk-no-duplicate-defuns names ctx state) (chk-xargs-keywords fives *xargs-keywords* ctx state) (er-let* ((tuple (chk-acceptable-defuns0 fives ctx wrld state))) (let* ((stobjs-in-lst (car tuple)) (defun-mode (cadr tuple)) (non-executablep (caddr tuple)) (symbol-class (cdddr tuple)) (rc (redundant-or-reclassifying-defunsp defun-mode symbol-class (ld-skip-proofsp state) lst ctx wrld (ld-redefinition-action state) fives non-executablep stobjs-in-lst (default-state-vars t)))) (cond ((eq rc 'redundant) (chk-acceptable-defuns-redundancy names ctx wrld state)) ((eq rc 'verify-guards) ; We avoid needless complication by simply causing a polite error in this ; case. If that proves to be too inconvenient for users, we could look into ; arranging for a call of verify-guards here. (chk-acceptable-defuns-verify-guards-er names ctx wrld state)) #+hons ((and (eq rc 'reclassifying) (conditionally-memoized-fns names (table-alist 'memoize-table wrld))) ; We no longer recall exactly why we have this restriction. However, after ; discussing this with Sol Swords we think it's because we tolerate all sorts ; of guard violations when dealing with :program mode functions, but we expect ; guards to be handled properly with :logic mode functions, including the ; condition function. If we verify termination and guards for the memoized ; function but not the condition, that could present a problem. Quite possibly ; we can relax this check somewhat after thinking things through -- e.g., if ; the condition function is a guard-verified :logic mode function -- if there ; is demand for such an enhancement. (er soft ctx "It is illegal to verify termination (i.e., convert from ~ :program to :logic mode) for function~#0~[~/s~] ~&0, because ~ ~#0~[it is~/they are~] currently memoized with conditions; you ~ need to unmemoize ~#0~[it~/them~] first. See :DOC memoize." (conditionally-memoized-fns names (table-alist 'memoize-table wrld)))) (t (chk-acceptable-defuns1 names fives stobjs-in-lst defun-mode symbol-class rc non-executablep ctx wrld state #+:non-standard-analysis std-p)))))))) (deflabel XARGS ; Keep this in sync with *xargs-keywords*. :doc ":Doc-Section Miscellaneous extra arguments, for example to give ~il[hints] to ~ilc[defun]~/ Common Lisp's ~ilc[defun] function does not easily allow one to pass extra arguments such as ``~il[hints]''. ACL2 therefore supports a peculiar new declaration (~pl[declare]) designed explicitly for passing additional arguments to ~ilc[defun] via a keyword-like syntax. This declaration can also be used with ~ilc[defmacro], but only for ~c[xargs] keyword ~c[:GUARD]; so we restrict attention below to use of ~c[xargs] in ~ilc[defun] ~il[events]. The following declaration is nonsensical but does illustrate all of the ~c[xargs] keywords for ~ilc[defun] (which are the members of the list ~c[*xargs-keywords*]). ~bv[] (declare (xargs :guard (symbolp x) :guard-hints ((\"Goal\" :in-theory (theory batch1))) :guard-debug t :hints ((\"Goal\" :in-theory (theory batch1))) :measure (- i j) :ruler-extenders :basic :mode :logic :non-executable t :normalize nil :otf-flg t :stobjs ($s) :verify-guards t :split-types t :well-founded-relation my-wfr))~/ General Form: (xargs :key1 val1 ... :keyn valn) ~ev[] where the keywords and their respective values are as shown below. Note that once ``inside'' the xargs form, the ``extra arguments'' to ~ilc[defun] are passed exactly as though they were keyword arguments. ~c[:]~ilc[GUARD]~nl[] ~c[Value] is a term involving only the formals of the function being defined. The actual ~il[guard] used for the definition is the conjunction of all the ~il[guard]s and types (~pl[declare]) ~il[declare]d. ~c[:GUARD-HINTS]~nl[] ~c[Value]: hints (~pl[hints]), to be used during the ~il[guard] verification proofs as opposed to the termination proofs of the ~ilc[defun]. ~c[:GUARD-DEBUG]~nl[] ~c[Value]: ~c[nil] by default, else directs ACL2 to decorate each guard proof obligation with hypotheses indicating its sources. ~l[guard-debug]. ~c[:]~ilc[HINTS]~nl[] Value: hints (~pl[hints]), to be used during the termination proofs as opposed to the ~il[guard] verification proofs of the ~ilc[defun]. ~c[:MEASURE]~nl[] ~c[Value] is a term involving only the formals of the function being defined. This term is indicates what is getting smaller in the recursion. The well-founded relation with which successive measures are compared is ~ilc[o<]. Also allowed is a special case, ~c[(:? v1 ... vk)], where ~c[(v1 ... vk)] enumerates a subset of the formal parameters such that some valid measure involves only those formal parameters. However, this special case is only allowed for definitions that are redundant (~pl[redundant-events]) or are executed when skipping proofs (~pl[skip-proofs]). Another special case is ~c[:MEASURE nil], which is treated the same as if ~c[:MEASURE] is omitted. ~c[:RULER-EXTENDERS]~nl[] For recursive definitions (possibly mutually recursive), ~c[value] controls termination analysis and the resulting stored induction scheme. ~l[ruler-extenders] for a discussion of legal values and their effects. ~c[:MODE]~nl[] ~c[Value] is ~c[:]~ilc[program] or ~c[:]~ilc[logic], indicating the ~ilc[defun] mode of the function introduced. ~l[defun-mode]. If unspecified, the ~ilc[defun] mode defaults to the default ~ilc[defun] mode of the current ~il[world]. To convert a function from ~c[:]~ilc[program] mode to ~c[:]~ilc[logic] mode, ~pl[verify-termination]. ~c[:NON-EXECUTABLE]~nl[] ~c[Value] is normally ~c[t] or ~c[nil] (the default). Rather than stating ~c[:non-executable t] directly, which requires ~c[:]~ilc[logic] mode and that the definitional body has a certain form, we suggest using the macro ~c[defun-nx] or ~c[defund-nx]; ~pl[defun-nx]. A third value of ~c[:non-executable] for advanced users is ~c[:program], which is generated by expansion of ~c[defproxy] forms; ~pl[defproxy]. For another way to deal with non-executability, ~pl[non-exec]. ~c[:NORMALIZE]~nl[] Value is a flag telling ~ilc[defun] whether to propagate ~ilc[if] tests upward. Since the default is to do so, the value supplied is only of interest when it is ~c[nil]. (~l[defun]). ~c[:]~ilc[OTF-FLG]~nl[] Value is a flag indicating ``onward through the fog'' (~pl[otf-flg]). ~c[:STOBJS]~nl[] ~c[Value] is either a single ~ilc[stobj] name or a true list of stobj names (~pl[stobj] and ~pl[defstobj], and perhaps ~pl[defabsstobj]). Every stobj name among the formals of the function must be listed, if the corresponding actual is to be treated as a stobj. That is, if a function uses a stobj name as a formal parameter but the name is not declared among the ~c[:stobjs] then the corresponding argument is treated as ordinary. The only exception to this rule is ~ilc[state]: whether you include it or not, ~c[state] is always treated as a single-threaded object. This declaration has two effects. One is to enforce the syntactic restrictions on single-threaded objects. The other is to strengthen the ~ilc[guard] of the function being defined so that it includes conjuncts specifying that each declared single-threaded object argument satisfies the recognizer for the corresponding single-threaded object. ~c[:]~ilc[VERIFY-GUARDS]~nl[] ~c[Value] is ~c[t] or ~c[nil], indicating whether or not ~il[guard]s are to be verified upon completion of the termination proof. This flag should only be ~c[t] if the ~c[:mode] is unspecified but the default ~ilc[defun] mode is ~c[:]~ilc[logic], or else the ~c[:mode] is ~c[:]~ilc[logic]. ~c[:]~c[SPLIT-TYPES]~nl[] ~c[Value] is ~c[t] or ~c[nil], indicating whether or not ~il[type]s are to be proved from the ~il[guard]s. The default is ~c[nil], indicating that type declarations (~pl[declare]) contribute to the ~il[guard]s. If the value is ~c[t], then instead, the expressions corresponding to the type declarations (~pl[type-spec]) are conjoined into a ``split-type expression,'' and guard verification insists that this term is implied by the specified ~c[:guard]. Suppose for example that a definition has the following ~ilc[declare] form. ~bv[] (declare (xargs :guard (p x y) :split-types t) (type integer x) (type (satisfies good-bar-p) y)) ~ev[] Then for guard verification, ~c[(p x y)] is assumed, and in addition to the usual proof obligations derived from the body of the definition, guard verification requires a proof that ~c[(p x y)] implies both ~c[(integerp x)] and ~c[(good-bar-p y)]. See community book ~c[demos/split-types-examples.lisp] for small examples. ~c[:]~ilc[WELL-FOUNDED-RELATION]~nl[] ~c[Value] is a function symbol that is known to be a well-founded relation in the sense that a rule of class ~c[:]~ilc[well-founded-relation] has been proved about it. ~l[well-founded-relation].~/") (defmacro link-doc-to-keyword (name parent see) `(defdoc ,name ,(concatenate 'string ":Doc-Section " (symbol-name parent) " " (string-downcase (symbol-name see)) " keyword ~c[:" (symbol-name name) "]~/ ~l[" (string-downcase (symbol-name see)) "].~/~/"))) (defmacro link-doc-to (name parent see) `(defdoc ,name ,(concatenate 'string ":Doc-Section " (symbol-package-name parent) "::" (symbol-name parent) " ~l[" (string-downcase (symbol-name see)) "].~/~/~/"))) (link-doc-to-keyword guard-hints miscellaneous xargs) (link-doc-to-keyword measure miscellaneous xargs) (link-doc-to-keyword mode miscellaneous xargs) (link-doc-to-keyword non-executable miscellaneous xargs) (link-doc-to-keyword normalize miscellaneous xargs) (link-doc-to-keyword stobjs miscellaneous xargs) (link-doc-to-keyword do-not-induct miscellaneous hints) (link-doc-to-keyword do-not miscellaneous hints) (link-doc-to-keyword expand miscellaneous hints) (link-doc-to-keyword restrict miscellaneous hints) (link-doc-to-keyword hands-off miscellaneous hints) (link-doc-to-keyword induct miscellaneous hints) (link-doc-to-keyword use miscellaneous hints) (link-doc-to-keyword cases miscellaneous hints) (link-doc-to-keyword by miscellaneous hints) (link-doc-to-keyword nonlinearp miscellaneous hints) (link-doc-to-keyword backchain-limit-rw miscellaneous hints) (link-doc-to-keyword reorder miscellaneous hints) (link-doc-to-keyword no-thanks miscellaneous hints) (link-doc-to-keyword backtrack miscellaneous hints) (link-doc-to read-byte$ acl2-built-ins io) (link-doc-to open-input-channel acl2-built-ins io) (link-doc-to open-input-channel-p acl2-built-ins io) (link-doc-to close-input-channel acl2-built-ins io) (link-doc-to read-char$ acl2-built-ins io) (link-doc-to peek-char$ acl2-built-ins io) (link-doc-to read-object acl2-built-ins io) (link-doc-to open-output-channel acl2-built-ins io) (link-doc-to open-output-channel-p acl2-built-ins io) (link-doc-to close-output-channel acl2-built-ins io) (link-doc-to write-byte$ acl2-built-ins io) (link-doc-to print-object$ acl2-built-ins io) (link-doc-to get-output-stream-string$ acl2-built-ins io) (link-doc-to lambda miscellaneous term) (link-doc-to untranslate acl2-built-ins user-defined-functions-table) (link-doc-to set-ld-skip-proofsp switches-parameters-and-modes ld-skip-proofsp) (link-doc-to set-ld-keyword-aliases switches-parameters-and-modes ld-keyword-aliases) (link-doc-to set-ld-keyword-aliases! switches-parameters-and-modes ld-keyword-aliases) (link-doc-to add-ld-keyword-alias switches-parameters-and-modes ld-keyword-aliases) (link-doc-to add-ld-keyword-alias! switches-parameters-and-modes ld-keyword-aliases) (link-doc-to set-ld-redefinition-action switches-parameters-and-modes ld-redefinition-action) (link-doc-to set-ruler-extenders switches-parameters-and-modes ruler-extenders) (link-doc-to show-accumulated-persistence other accumulated-persistence) (link-doc-to set-accumulated-persistence other accumulated-persistence) (link-doc-to dynamically-monitor-rewrites other dmr) (link-doc-to keyword miscellaneous keywordp) (link-doc-to ignore programming declare) (link-doc-to ignorable programming declare) (link-doc-to type programming declare) (link-doc-to optimize programming declare) (link-doc-to reset-print-control io print-control) (link-doc-to set-print-circle io print-control) (link-doc-to set-print-escape io print-control) (link-doc-to set-print-length io print-control) (link-doc-to set-print-level io print-control) (link-doc-to set-print-lines io print-control) (link-doc-to set-print-radix io print-control) (link-doc-to set-print-readably io print-control) (link-doc-to set-print-right-margin io print-control) (link-doc-to iprint io set-iprint) (link-doc-to iprinting io set-iprint) (link-doc-to external-format io character-encoding) (link-doc-to acl2s miscellaneous acl2-sedan) (link-doc-to waterfall miscellaneous hints-and-the-waterfall) (link-doc-to trust-tag miscellaneous defttag) (link-doc-to verify-guards-eagerness switches-parameters-and-modes set-verify-guards-eagerness) (link-doc-to default-verify-guards-eagerness switches-parameters-and-modes set-verify-guards-eagerness) (link-doc-to set-compiler-enabled switches-parameters-and-modes compilation) (link-doc-to member-eq acl2-built-ins member) (link-doc-to member-equal acl2-built-ins member) (link-doc-to assoc-eq acl2-built-ins assoc) (link-doc-to assoc-equal acl2-built-ins assoc) (link-doc-to subsetp-eq acl2-built-ins subsetp) (link-doc-to subsetp-equal acl2-built-ins subsetp) (link-doc-to no-duplicatesp-eq acl2-built-ins no-duplicatesp) (link-doc-to no-duplicatesp-equal acl2-built-ins no-duplicatesp) (link-doc-to rassoc-eq acl2-built-ins rassoc) (link-doc-to rassoc-equal acl2-built-ins rassoc) (link-doc-to remove-eq acl2-built-ins remove) (link-doc-to remove-equal acl2-built-ins remove) (link-doc-to remove1-eq acl2-built-ins remove1) (link-doc-to remove1-equal acl2-built-ins remove1) (link-doc-to remove-duplicates-eq acl2-built-ins remove-duplicates) (link-doc-to remove-duplicates-equal acl2-built-ins remove-duplicates) (link-doc-to position-eq acl2-built-ins position) (link-doc-to position-equal acl2-built-ins position) (link-doc-to set-difference-eq acl2-built-ins set-difference$) (link-doc-to set-difference-equal acl2-built-ins set-difference$) (link-doc-to add-to-set-eq acl2-built-ins add-to-set) (link-doc-to add-to-set-eql acl2-built-ins add-to-set) ; pre-v4-3 compatibility (link-doc-to add-to-set-equal acl2-built-ins add-to-set) (link-doc-to intersectp-eq acl2-built-ins intersectp) (link-doc-to intersectp-equal acl2-built-ins intersectp) (link-doc-to put-assoc-eq acl2-built-ins put-assoc) (link-doc-to put-assoc-eql acl2-built-ins put-assoc) ; pre-v4-3 compatibility (link-doc-to put-assoc-equal acl2-built-ins put-assoc) (link-doc-to delete-assoc-eq acl2-built-ins delete-assoc) (link-doc-to delete-assoc-equal acl2-built-ins delete-assoc) (link-doc-to union-eq acl2-built-ins union$) (link-doc-to union-equal acl2-built-ins union$) (link-doc-to intersection-eq acl2-built-ins intersection$) (link-doc-to intersection-equal acl2-built-ins intersection$) (link-doc-to observation-cw acl2-built-ins observation) (link-doc-to set-serialize-character serialize with-serialize-character) (link-doc-to &allow-other-keys miscellaneous macro-args) (link-doc-to &body miscellaneous macro-args) (link-doc-to &key miscellaneous macro-args) (link-doc-to &optional miscellaneous macro-args) (link-doc-to &rest miscellaneous macro-args) (link-doc-to &whole miscellaneous macro-args) (link-doc-to gcs history get-command-sequence) (link-doc-to single-threaded-objects programming stobj) (link-doc-to if-intro miscellaneous splitter) ; (link-doc-to case-split miscellaneous splitter) ; no; :doc case-split exists (link-doc-to immed-forced miscellaneous splitter) (link-doc-to forced miscellaneous force) (link-doc-to print-summary-user switches-parameters-and-modes finalize-event-user) (link-doc-to apropos miscellaneous finding-documentation) #+:non-standard-analysis (defun build-valid-std-usage-clause (arglist body) (cond ((null arglist) (list (mcons-term* 'standardp body))) (t (cons (mcons-term* 'not (mcons-term* 'standardp (car arglist))) (build-valid-std-usage-clause (cdr arglist) body))))) #+:non-standard-analysis (defun verify-valid-std-usage (names arglists bodies hints otf-flg ttree0 ctx ens wrld state) (cond ((null (cdr names)) (let* ((name (car names)) (arglist (car arglists)) (body (car bodies))) (mv-let (cl-set cl-set-ttree) (clean-up-clause-set (list (build-valid-std-usage-clause arglist body)) ens wrld ttree0 state) (pprogn (increment-timer 'other-time state) (let ((displayed-goal (prettyify-clause-set cl-set (let*-abstractionp state) wrld))) (mv-let (col state) (io? event nil (mv col state) (cl-set displayed-goal name) (cond ((null cl-set) (fmt "~%The admission of ~x0 as a classical function ~ is trivial." (list (cons #\0 name)) (proofs-co state) state nil)) (t (fmt "~%The admission of ~x0 as a classical function ~ with non-classical body requires that it return ~ standard values for standard arguments. That ~ is, we must prove~%~%Goal~%~Q12." (list (cons #\0 name) (cons #\1 displayed-goal) (cons #\2 (term-evisc-tuple nil state))) (proofs-co state) state nil)))) (pprogn (increment-timer 'print-time state) (cond ((null cl-set) (value (cons col cl-set-ttree))) (t (mv-let (erp ttree state) (prove (termify-clause-set cl-set) (make-pspv ens wrld :displayed-goal displayed-goal :otf-flg otf-flg) hints ens wrld ctx state) (cond (erp (mv t nil state)) (t (mv-let (col state) (io? event nil (mv col state) (name) (fmt "That completes the proof that ~x0 ~ returns standard values for standard ~ arguments." (list (cons #\0 name)) (proofs-co state) state nil)) (pprogn (increment-timer 'print-time state) (value (cons col (cons-tag-trees cl-set-ttree ttree))))))))))))))))) (t (er soft ctx "It is not permitted to use MUTUAL-RECURSION to define non-standard ~ predicates. Use MUTUAL-RECURSION to define standard versions of ~ these predicates, then use DEFUN-STD to generalize them, if that's ~ what you mean.")))) (defun union-eq1-rev (x y) ; This is like (union-eq x y) but is tail recursive and ; reverses the order of the new elements. (cond ((endp x) y) ((member-eq (car x) y) (union-eq1-rev (cdr x) y)) (t (union-eq1-rev (cdr x) (cons (car x) y))))) (defun collect-hereditarily-constrained-fnnames (names wrld ans) (cond ((endp names) ans) (t (let ((name-fns (getprop (car names) 'hereditarily-constrained-fnnames nil 'current-acl2-world wrld))) (cond (name-fns (collect-hereditarily-constrained-fnnames (cdr names) wrld (union-eq1-rev name-fns ans))) (t (collect-hereditarily-constrained-fnnames (cdr names) wrld ans))))))) (defun putprop-hereditarily-constrained-fnnames-lst (names bodies wrld) ; Names is a non-empty list of defined function names and bodies is in ; 1:1 correspondence. We set the hereditarily-constrained-fnnames ; property of each name in names, by collecting all the function names ; appearing in the bodies and filtering for the hereditarily ; constrained ones. We also add each name in names to the world global ; defined-hereditarily-constrained-fns. ; A ``hereditarily constrained function'' is either a constrained ; function, e.g., one introduced with defchoose or encapsulate, or ; else a defun'd function whose definition involves a hereditarily ; constrained function. The value of the ; hereditarily-constrained-fnnames property of a function symbol, fn, ; is a list of all the hereditarily constrained functions involved ; (somehow) in the definition of fn. If the list is nil, the symbol ; is in no sense constrained, but is either a primitive, e.g., car, or ; an ordinary defun'd function. If the list is a singleton, then its ; only element must necessarily be the fn itself and we know therefore ; that fn is constrained. Otherwise, the list has at least two ; elements and that fn is a defined but hereditarily constrained ; function. For example, if h is constrained and map-h is defined in ; terms of h, then the property for h will be '(h) and that for map-h ; will be '(map-h h). Mutually recursive cliques will list all the ; fns in the clique. One cannot assume the car of the list is fn. (let ((fnnames (collect-hereditarily-constrained-fnnames (all-fnnames1 t bodies nil) wrld nil))) (cond (fnnames (global-set 'defined-hereditarily-constrained-fns (append names (global-val 'defined-hereditarily-constrained-fns wrld)) (putprop-x-lst1 names 'hereditarily-constrained-fnnames (append names fnnames) wrld))) (t wrld)))) (defun defuns-fn1 (tuple ens big-mutrec names arglists docs pairs guards guard-hints std-hints otf-flg guard-debug bodies symbol-class normalizeps split-types-terms non-executablep #+:non-standard-analysis std-p ctx state) ; See defuns-fn0. ; WARNING: This function installs a world. That is safe at the time of this ; writing because this function is only called by defuns-fn0, which is only ; called by defuns-fn, where that call is protected by a revert-world-on-error. #-:non-standard-analysis (declare (ignore std-hints)) (let ((col (car tuple)) (subversive-p (cdddr tuple))) (er-let* ((wrld1 (update-w big-mutrec (cadr tuple))) (wrld2 (update-w big-mutrec (putprop-defun-runic-mapping-pairs names t wrld1))) (wrld3 (update-w big-mutrec (putprop-x-lst2-unless names 'guard guards *t* wrld2))) (wrld4 (update-w big-mutrec (putprop-x-lst2-unless names 'split-types-term split-types-terms *t* wrld3))) ; Rockwell Addition: To save time, the nu-rewriter doesn't look at ; functions unless they contain nu-rewrite targets, as defined in ; rewrite.lisp. Here is where I store the property that says whether a ; function is a target. (wrld5 (update-w big-mutrec (cond ((eq (car names) 'NTH) (putprop 'nth 'nth-update-rewriter-targetp t wrld4)) ((getprop (car names) 'recursivep nil 'current-acl2-world wrld4) ; Nth-update-rewriter does not go into recursive functions. We could consider ; redoing this computation when installing a new definition rule, as well as ; the putprop below, but that's a heuristic decision that doesn't seem to be so ; important. wrld4) ((nth-update-rewriter-targetp (car bodies) wrld4) ; This precomputation of whether the body of the function is a ; potential target for nth-update-rewriter is insensitive to whether ; the functions being seen are disabled. If the function being ; defined calls a non-recursive function that uses NTH, then this ; function is marked as being a target. If later that subroutine is ; disabled, then nth-update-rewriter will not go into it and this ; function may no longer really be a potential target. But if we do ; not ``memoize'' the computation this way then it may be ; exponentially slow, going repeatedly into large bodies called ; more than one time in a function. (putprop (car names) 'nth-update-rewriter-targetp t wrld4)) (t wrld4)))) #+:non-standard-analysis (assumep (value (or (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals)))) #+:non-standard-analysis (col/ttree1 (if (and std-p (not assumep)) (verify-valid-std-usage names arglists bodies std-hints otf-flg (caddr tuple) ctx ens wrld5 state) (value (cons col (caddr tuple))))) #+:non-standard-analysis (col (value (car col/ttree1))) (ttree1 #+:non-standard-analysis (value (cdr col/ttree1)) #-:non-standard-analysis (value (caddr tuple)))) (mv-let (wrld6 ttree2) (putprop-body-lst names arglists bodies normalizeps (getprop (car names) 'recursivep nil 'current-acl2-world wrld5) (make-controller-alist names wrld5) #+:non-standard-analysis std-p ens wrld5 wrld5 nil) (er-progn (update-w big-mutrec wrld6) (mv-let (wrld7 ttree2 state) (putprop-type-prescription-lst names subversive-p (fn-rune-nume (car names) t nil wrld6) ens wrld6 ttree2 state) (er-progn (update-w big-mutrec wrld7) (er-let* ((wrld8 (update-w big-mutrec (putprop-level-no-lst names wrld7))) (wrld9 (update-w big-mutrec (putprop-primitive-recursive-defunp-lst names wrld8))) (wrld9a (update-w big-mutrec (putprop-hereditarily-constrained-fnnames-lst names bodies wrld9))) (wrld10 (update-w big-mutrec (update-doc-database-lst names docs pairs wrld9a))) (wrld11 (update-w big-mutrec (putprop-x-lst1 names 'congruences nil wrld10))) (wrld11a (update-w big-mutrec (putprop-x-lst1 names 'coarsenings nil wrld11))) (wrld11b (update-w big-mutrec (if non-executablep (putprop-x-lst1 names 'non-executablep non-executablep wrld11a) wrld11a)))) (let ((wrld12 #+:non-standard-analysis (if std-p (putprop-x-lst1 names 'unnormalized-body nil (putprop-x-lst1 names 'def-bodies nil wrld11b)) wrld11b) #-:non-standard-analysis wrld11b)) (pprogn (print-defun-msg names ttree2 wrld12 col state) (set-w 'extension wrld12 state) (cond ((eq symbol-class :common-lisp-compliant) (er-let* ((guard-hints (if guard-hints (translate-hints (cons "Guard for" (car names)) guard-hints ctx wrld12 state) (value nil))) (pair (verify-guards-fn1 names guard-hints otf-flg guard-debug ctx state))) ; Pair is of the form (wrld . ttree3) and we return a pair of the same ; form, but we must combine this ttree with the ones produced by the ; termination proofs and type-prescriptions. (value (cons (car pair) (cons-tag-trees ttree1 (cons-tag-trees ttree2 (cdr pair))))))) (t (value (cons wrld12 (cons-tag-trees ttree1 ttree2))))))))))))))) (defun defuns-fn0 (names arglists docs pairs guards measures ruler-extenders-lst mp rel hints guard-hints std-hints otf-flg guard-debug bodies symbol-class normalizeps split-types-terms non-executablep #+:non-standard-analysis std-p ctx wrld state) ; WARNING: This function installs a world. That is safe at the time of this ; writing because this function is only called by defuns-fn, where that call is ; protected by a revert-world-on-error. (cond ((eq symbol-class :program) (defuns-fn-short-cut names docs pairs guards split-types-terms bodies non-executablep wrld state)) (t (let ((ens (ens state)) (big-mutrec (big-mutrec names))) (er-let* ((tuple (put-induction-info names arglists measures ruler-extenders-lst bodies mp rel hints otf-flg big-mutrec ctx ens wrld state))) (defuns-fn1 tuple ens big-mutrec names arglists docs pairs guards guard-hints std-hints otf-flg guard-debug bodies symbol-class normalizeps split-types-terms non-executablep #+:non-standard-analysis std-p ctx state)))))) (defun strip-non-hidden-package-names (known-package-alist) (if (endp known-package-alist) nil (let ((package-entry (car known-package-alist))) (cond ((package-entry-hidden-p package-entry) (strip-non-hidden-package-names (cdr known-package-alist))) (t (cons (package-entry-name package-entry) (strip-non-hidden-package-names (cdr known-package-alist)))))))) (defun in-package-fn (str state) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. (cond ((not (stringp str)) (er soft 'in-package "The argument to IN-PACKAGE must be a string, but ~ ~x0 is not." str)) ((not (find-non-hidden-package-entry str (known-package-alist state))) (er soft 'in-package "The argument to IN-PACKAGE must be a known package ~ name, but ~x0 is not. The known packages are ~*1" str (tilde-*-&v-strings '& (strip-non-hidden-package-names (known-package-alist state)) #\.))) (t (let ((state (f-put-global 'current-package str state))) (value str))))) (defun defstobj-functionsp (names embedded-event-lst) ; This function determines whether all the names in names are being defined as ; part of a defstobj or defabsstobj event. If so, it returns the name of the ; stobj; otherwise, nil. ; Explanation of the context: Defstobj and defabsstobj use defun to define the ; recognizers, accessors and updaters. But these events must install their own ; versions of the raw lisp code for these functions, to take advantage of the ; single-threadedness of their use. So what happens when defstobj or ; defabsstobj executes (defun name ...), where name is say an updater? ; Defuns-fn is run on the singleton list '(name) and the axiomatic def of name. ; At the end of the normal processing, defuns-fn computes a CLTL-COMMAND for ; name. When this command is installed by add-trip, it sets the ; symbol-function of name to the given body. Add-trip also installs a *1*name ; definition by oneifying the given body. But in the case of a defstobj (or ; defabsstobj) function we do not want the first thing to happen: we will ; compute a special body for the name and install it with its own CLTL-COMMAND. ; So to handle defstobj and defabsstobj, defuns-fn tells add-trip not to set ; the symbol-function. This is done by setting the ignorep flag in the defun ; CLTL-COMMAND. So the question arises: how does defun know that the name it ; is defining is being introduced by defstobj or defabsstobj? This function ; answers that question. ; Note that *1*name should still be defined as the oneified axiomatic body, as ; with any defun. Before v2-9 we introduced the *1* function at defun time. ; (We still do so if the function is being reclassified with an identical body, ; from :program mode to :logic mode, since there is no need to redefine its ; symbol-function -- -- indeed its installed symbol-function might be ; hand-coded as part of these sources -- but add-trip must generate a *1* ; body.) Because stobj functions can be inlined as macros (via the :inline ; keyword of defstobj), we need to defer definition of the *1* function until ; after the raw Lisp def (which may be a macro) has been added. We failed to ; do this in v2-8, which caused an error in CCL as reported by John ; Matthews: ; (defstobj tiny-state ; (progc :type (unsigned-byte 10) :initially 0) ; :inline t) ; ; (update-progc 3 tiny-state) ; Note: At the moment, defstobj and defabsstobj do not introduce any mutually ; recursive functions. So every name is handled separately by defuns-fns. ; Hence, names, here, is always a singleton, though we do not exploit that. ; Also, embedded-event-lst is always a list ee-entries, each being a cons with ; the name of some superevent like ENCAPSULATE, INCLUDE-BOOK, or DEFSTOBJ ; (which is also used for DEFABSSTOBJ), in the car. The ee-entry for the most ; immediate superevent is the first on the list. At the moment, defstobj and ; defabsstobj do not use encapsulate or other structuring mechanisms. Thus, ; the defstobj ee-entry will be first on the list. But we look up the list, ; just in case. The ee-entry for a defstobj or defabsstobj is of the form ; (defstobj name names) where name is the name of the stobj and names is the ; list of recognizers, accessors and updaters and their helpers. (let ((temp (assoc-eq 'defstobj embedded-event-lst))) (cond ((and temp (subsetp-equal names (caddr temp))) (cadr temp)) (t nil)))) ; The following definition only supports non-standard analysis, but it seems ; reasonable to allow it in the standard version too. ; #+:non-standard-analysis (defun index-of-non-number (lst) (cond ((endp lst) nil) ((acl2-numberp (car lst)) (let ((temp (index-of-non-number (cdr lst)))) (and temp (1+ temp)))) (t 0))) #+:non-standard-analysis (defun non-std-error (fn index formals actuals) (er hard fn "Function ~x0 was called with the ~n1 formal parameter, ~x2, bound to ~ actual parameter ~x3, which is not a (standard) number. This is illegal, ~ because the arguments of a function defined with defun-std must all be ~ (standard) numbers." fn (list index) (nth index formals) (nth index actuals))) #+:non-standard-analysis (defun non-std-body (name formals body) ; The body below is a bit inefficient in the case that we get an error. ; However, we do not expect to get errors very often, and the alternative is to ; bind a variable that we have to check is not in formals. `(if (index-of-non-number (list ,@formals)) (non-std-error ',name (index-of-non-number ',formals) ',formals (list ,@formals)) ,body)) #+:non-standard-analysis (defun non-std-def-lst (def-lst) (if (and (consp def-lst) (null (cdr def-lst))) (let* ((def (car def-lst)) (fn (car def)) (formals (cadr def)) (body (car (last def)))) `((,@(butlast def 1) ,(non-std-body fn formals body)))) (er hard 'non-std-def-lst "Unexpected call; please contact ACL2 implementors."))) ; Rockwell Addition: To support non-executable fns we have to be able, ; at defun time, to introduce an undefined function. So this stuff is ; moved up from other-events.lisp. (defun make-udf-insigs (names wrld) (cond ((endp names) nil) (t (cons (list (car names) (formals (car names) wrld) (stobjs-in (car names) wrld) (stobjs-out (car names) wrld)) (make-udf-insigs (cdr names) wrld))))) (defun intro-udf (insig wrld) ; This function is called during pass 2 of an encapsulate. See the comment ; below about guards. (case-match insig ((fn formals stobjs-in stobjs-out) (putprop fn 'coarsenings nil (putprop fn 'congruences nil (putprop fn 'constrainedp t ; 'constraint-lst comes later (putprop fn 'hereditarily-constrained-fnnames (list fn) (putprop fn 'symbol-class :COMMON-LISP-COMPLIANT (putprop-unless fn 'stobjs-out stobjs-out nil (putprop-unless fn 'stobjs-in stobjs-in nil (putprop fn 'formals formals (putprop fn 'guard ; We are putting a guard of t on a signature function, even though a :guard ; other than t might have been specified for this function. This may seem to ; be an error. However, proofs are skipped during that pass, so an incorrect ; guard proof obligation will not be noticed anyhow. Instead, guard ; verification takes place during the first pass of the encapsulate, which ; could indeed present a problem if we are not careful. However, we call ; function bogus-exported-compliants to check that we are not making that sort ; of mistake; see bogus-exported-compliants. *t* wrld)))))))))) (& (er hard 'store-signature "Unrecognized signature!" insig)))) (defun intro-udf-lst1 (insigs wrld) (cond ((null insigs) wrld) (t (intro-udf-lst1 (cdr insigs) (intro-udf (car insigs) wrld))))) (defun intro-udf-lst2 (insigs kwd-value-list-lst) ; Warning: Keep this in sync with oneify-cltl-code. ; Insigs is a list of internal form signatures, e.g., ((fn1 formals1 stobjs-in1 ; stobjs-out1) ...), and we convert it to a "def-lst" suitable for giving the ; Common Lisp version of defuns, ((fn1 formals1 body1) ...), where each bodyi ; is just a throw to 'raw-ev-fncall with the signal that says there is no body. ; Note that the body we build (in this ACL2 code) is a Common Lisp body but not ; an ACL2 expression! ; kwd-value-list-lst is normally a list that corresponds by position to insigs, ; each of whose elements associates keywords with values; in particular it can ; associate :guard with the guard for the corresponding element of insigs. ; However, kwd-value-list-lst can be the atom 'non-executable-programp, which ; we use for proxy functions (see :DOC defproxy), i.e., :program mode functions ; with the xarg declaration :non-executable :program. (cond ((null insigs) nil) (t (cons `(,(caar insigs) ,(cadar insigs) ,@(cond ((eq kwd-value-list-lst 'non-executable-programp) '((declare (xargs :non-executable :program)))) (t (let ((guard (cadr (assoc-keyword :guard (car kwd-value-list-lst))))) (and guard `((declare (xargs :guard ,guard))))))) ,(null-body-er (caar insigs) (cadar insigs) t)) (intro-udf-lst2 (cdr insigs) (if (eq kwd-value-list-lst 'non-executable-programp) 'non-executable-programp (cdr kwd-value-list-lst))))))) (defun intro-udf-lst (insigs kwd-value-list-lst wrld) ; Insigs is a list of internal form signatures. We know all the function ; symbols are new in wrld. We declare each of them to have the given formals, ; stobjs-in, and stobjs-out, symbol-class :common-lisp-compliant, a guard of t ; and constrainedp of t. We also arrange to execute a defun in the underlying ; Common Lisp so that each function is defined to throw to an error handler if ; called from ACL2. (if (null insigs) wrld (put-cltl-command `(defuns nil nil ,@(intro-udf-lst2 insigs (and (not (eq kwd-value-list-lst t)) kwd-value-list-lst))) (intro-udf-lst1 insigs wrld) wrld))) (defun defun-ctx (def-lst state event-form #+:non-standard-analysis std-p) (if (output-in-infixp state) event-form (cond ((atom def-lst) (msg "( DEFUNS ~x0)" def-lst)) ((atom (car def-lst)) (cons 'defuns (car def-lst))) ((null (cdr def-lst)) #+:non-standard-analysis (if std-p (cons 'defun-std (caar def-lst)) (cons 'defun (caar def-lst))) #-:non-standard-analysis (cons 'defun (caar def-lst))) (t (msg *mutual-recursion-ctx-string* (caar def-lst)))))) (defun install-event-defuns (names event-form def-lst0 symbol-class reclassifyingp non-executablep pair ctx wrld state) ; See defuns-fn. (install-event (cond ((null (cdr names)) (car names)) (t names)) event-form (cond ((null (cdr names)) 'defun) (t 'defuns)) (cond ((null (cdr names)) (car names)) (t names)) (cdr pair) (cond (non-executablep `(defuns nil nil ,@(intro-udf-lst2 (make-udf-insigs names wrld) (and (eq non-executablep :program) 'non-executable-programp)))) (t `(defuns ,(if (eq symbol-class :program) :program :logic) ,(if reclassifyingp 'reclassifying (if (defstobj-functionsp names (global-val 'embedded-event-lst (car pair))) (cons 'defstobj ; The following expression computes the stobj name, e.g., $S, for ; which this defun is supportive. The STOBJS-IN of this function is ; built into the expression created by oneify-cltl-code ; namely, in the throw-raw-ev-fncall expression (see ; oneify-fail-form). We cannot compute the STOBJS-IN of the function ; accurately from the world because $S is not yet known to be a stobj! ; This problem is a version of the super-defun-wart problem. (defstobj-functionsp names (global-val 'embedded-event-lst (car pair)))) nil)) ,@def-lst0))) t ctx (car pair) state)) (defun defuns-fn (def-lst state event-form #+:non-standard-analysis std-p) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. ; On Guards ; When a function symbol fn is defund the user supplies a guard, g, and a ; body b. Logically speaking, the axiom introduced for fn is ; (fn x1...xn) = b. ; After admitting fn, the guard-related properties are set as follows: ; prop after defun ; body b* ; guard g ; unnormalized-body b ; type-prescription computed from b ; symbol-class :ideal ; * We actually normalize the above. During normalization we may expand some ; boot-strap non-rec fns. ; In addition, we magically set the symbol-function of fn ; symbol-function b ; and the symbol-function of *1*fn as a program which computes the logical ; value of (fn x). However, *1*fn is quite fancy because it uses the raw body ; in the symbol-function of fn if fn is :common-lisp-compliant, and may signal ; a guard error if 'guard-checking-on is set to other than nil or :none. See ; oneify-cltl-code for the details. ; Observe that the symbol-function after defun may be a form that ; violates the guards on primitives. Until the guards in fn are ; checked, we cannot let raw Common Lisp evaluate fn. ; Intuitively, we think of the Common Lisp programmer intending to defun (fn ; x1...xn) to be b, and is declaring that the raw fn can be called only on ; arguments satisfying g. The need for guards stems from the fact that there ; are many Common Lisp primitives, such as car and cdr and + and *, whose ; behavior outside of their guarded domains is unspecified. To use these ; functions in the body of fn one must "guard" fn so that it is never called in ; a way that would lead to the violation of the primitive guards. Thus, we ; make a formal precondition on the use of the Common Lisp program fn that the ; guard g, along with the tests along the various paths through body b, imply ; each of the guards for every subroutine in b. We also require that each of ; the guards in g be satisfied. This is what we mean when we say fn is ; :common-lisp-compliant. ; It is, however, often impossible to check the guards at defun time. For ; example, if fn calls itself recursively and then gives the result to +, we ; would have to prove that the guard on + is satisfied by fn's recursive ; result, before we admit fn. In general, induction may be necessary to ; establish that the recursive calls satisfy the guards of their masters; ; hence, it is probably also necessary for the user to formulate general lemmas ; about fn to establish those conditions. Furthermore, guard checking is no ; longer logically necessary and hence automatically doing it at defun time may ; be a waste of time. (with-ctx-summarized (defun-ctx def-lst state event-form #+:non-standard-analysis std-p) (let ((wrld (w state)) (def-lst0 #+:non-standard-analysis (if std-p (non-std-def-lst def-lst) def-lst) #-:non-standard-analysis def-lst) (event-form (or event-form (list 'defuns def-lst)))) (revert-world-on-error (er-let* ((tuple (chk-acceptable-defuns def-lst ctx wrld state #+:non-standard-analysis std-p))) ; Chk-acceptable-defuns puts the 'formals, 'stobjs-in and 'stobjs-out ; properties (which are necessary for the translation of the bodies). ; All other properties are put by the defuns-fn0 call below. (cond ((eq tuple 'redundant) (stop-redundant-event ctx state)) (t (enforce-redundancy event-form ctx wrld (let ((names (nth 1 tuple)) (arglists (nth 2 tuple)) (docs (nth 3 tuple)) (pairs (nth 4 tuple)) (guards (nth 5 tuple)) (measures (nth 6 tuple)) (ruler-extenders-lst (nth 7 tuple)) (mp (nth 8 tuple)) (rel (nth 9 tuple)) (hints (nth 10 tuple)) (guard-hints (nth 11 tuple)) (std-hints (nth 12 tuple)) (otf-flg (nth 13 tuple)) (bodies (nth 14 tuple)) (symbol-class (nth 15 tuple)) (normalizeps (nth 16 tuple)) (reclassifyingp (nth 17 tuple)) (wrld (nth 18 tuple)) (non-executablep (nth 19 tuple)) (guard-debug (nth 20 tuple)) (split-types-terms (nth 21 tuple))) (er-let* ((pair (defuns-fn0 names arglists docs pairs guards measures ruler-extenders-lst mp rel hints guard-hints std-hints otf-flg guard-debug bodies symbol-class normalizeps split-types-terms non-executablep #+:non-standard-analysis std-p ctx wrld state))) ; Pair is of the form (wrld . ttree). (er-progn (chk-assumption-free-ttree (cdr pair) ctx state) (install-event-defuns names event-form def-lst0 symbol-class reclassifyingp non-executablep pair ctx wrld state)))))))))))) (defun defun-fn (def state event-form #+:non-standard-analysis std-p) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. ; The only reason this function exists is so that the defmacro for ; defun is in the form expected by primordial-event-defmacros. (defuns-fn (list def) state (or event-form (cons 'defun def)) #+:non-standard-analysis std-p)) ; Here we develop the :args keyword command that will print all that ; we know about a function. (defun args-fn (name state) (io? temporary nil (mv erp val state) (name) (let ((wrld (w state)) (channel (standard-co state))) (cond ((eq name 'return-last) (pprogn (fms "Special form, basic to ACL2. See :DOC return-last." nil channel state nil) (value name))) ((and (symbolp name) (function-symbolp name wrld)) (let* ((formals (formals name wrld)) (stobjs-in (stobjs-in name wrld)) (stobjs-out (stobjs-out name wrld)) (docp (access-doc-string-database name state)) (guard (untranslate (guard name nil wrld) t wrld)) (tp (find-runed-type-prescription (list :type-prescription name) (getprop name 'type-prescriptions nil 'current-acl2-world wrld))) (tpthm (cond (tp (untranslate (access type-prescription tp :corollary) t wrld)) (t nil))) (constraint (mv-let (some-name constraint-lst) (constraint-info name wrld) (cond ((eq constraint-lst *unknown-constraints*) :unknown-from-dependent-clause-processor) (some-name (untranslate (conjoin constraint-lst) t wrld)) (t t))))) (pprogn (fms "Function ~x0~|~ Formals: ~y1~|~ Signature: ~y2~|~ ~ => ~y3~|~ Guard: ~q4~|~ Guards Verified: ~y5~|~ Defun-Mode: ~@6~|~ Type: ~#7~[built-in (or unrestricted)~/~q8~]~|~ ~#9~[~/Constraint: ~qa~|~]~ ~#d~[~/Documentation available via :DOC~]~%" (list (cons #\0 name) (cons #\1 formals) (cons #\2 (cons name (prettyify-stobj-flags stobjs-in))) (cons #\3 (prettyify-stobjs-out stobjs-out)) (cons #\4 guard) (cons #\5 (eq (symbol-class name wrld) :common-lisp-compliant)) (cons #\6 (defun-mode-string (fdefun-mode name wrld))) (cons #\7 (if tpthm 1 0)) (cons #\8 tpthm) (cons #\9 (if (eq constraint t) 0 1)) (cons #\a constraint) (cons #\d (if docp 1 0))) channel state nil) (value name)))) ((and (symbolp name) (getprop name 'macro-body nil 'current-acl2-world wrld)) (let ((args (macro-args name wrld)) (docp (access-doc-string-database name state)) (guard (untranslate (guard name nil wrld) t wrld))) (pprogn (fms "Macro ~x0~|~ Macro Args: ~y1~|~ Guard: ~Q23~|~ ~#4~[~/Documentation available via :DOC~]~%" (list (cons #\0 name) (cons #\1 args) (cons #\2 guard) (cons #\3 (term-evisc-tuple nil state)) (cons #\4 (if docp 1 0))) channel state nil) (value name)))) ((member-eq name '(let lambda declare quote)) (pprogn (fms "Special form, basic to the Common Lisp language. ~ See for example CLtL." nil channel state nil) (value name))) (t (er soft :args "~x0 is neither a function symbol nor a macro name." name)))))) (defmacro args (name) ":Doc-Section Documentation ~c[args], ~ilc[guard], ~c[type], ~ilc[constraint], etc., of a function symbol~/ ~bv[] Example: :args assoc-eq ~ev[]~/ ~c[Args] takes one argument, a symbol which must be the name of a function or macro, and prints out the formal parameters, the ~il[guard] expression, the output ~il[signature], the deduced type, the ~il[constraint] (if any), and whether ~il[documentation] about the symbol is available via ~c[:]~ilc[doc].~/" (list 'args-fn name 'state)) ; We now develop the code for verify-termination, a macro that is essentially ; a form of defun. (defun make-verify-termination-def (old-def new-dcls wrld) ; Old-def is a def tuple that has previously been accepted by defuns. For ; example, if is of the form (fn args ...dcls... body), where dcls is a list of ; at most one doc string and possibly many DECLARE forms. New-dcls is a new ; list of dcls (known to satisfy plausible-dclsp). We create a new def tuple ; that uses new-dcls instead of ...dcls... but which keeps any member of the ; old dcls not specified by the new-dcls except for the :mode (if any), which ; is replaced by :mode :logic. (let* ((fn (car old-def)) (args (cadr old-def)) (body (car (last (cddr old-def)))) (dcls (butlast (cddr old-def) 1)) (new-fields (dcl-fields new-dcls)) (modified-old-dcls (strip-dcls (add-to-set-eq :mode new-fields) dcls))) (assert$ (not (getprop fn 'non-executablep nil 'current-acl2-world wrld)) `(,fn ,args ,@new-dcls ,@(if (and (not (member-eq :mode new-fields)) (eq (default-defun-mode wrld) :program)) '((declare (xargs :mode :logic))) nil) ,@modified-old-dcls ,body)))) (defun make-verify-termination-defs-lst (defs-lst lst wrld) ; Defs-lst is a list of def tuples as previously accepted by defuns. Lst is ; a list of tuples supplied to verify-termination. Each element of a list is ; of the form (fn . dcls) where dcls satisfies plausible-dclsp, i.e., is a list ; of doc strings and/or DECLARE forms. We copy defs-lst, modifying each member ; by merging in the dcls specified for the fn in lst. If some fn in defs-lst ; is not mentioned in lst, we don't modify its def tuple except to declare it ; of :mode :logic. (cond ((null defs-lst) nil) (t (let ((temp (assoc-eq (caar defs-lst) lst))) (cons (make-verify-termination-def (car defs-lst) (cdr temp) wrld) (make-verify-termination-defs-lst (cdr defs-lst) lst wrld)))))) (defun chk-acceptable-verify-termination1 (lst clique fn1 ctx wrld state) ; Lst is the input to verify-termination. Clique is a list of function ; symbols, fn1 is a member of clique (and used for error reporting only). Lst ; is putatively of the form ((fn . dcls) ...) where each fn is a member of ; clique and each dcls is a plausible-dclsp, as above. That means that each ; dcls is a list containing documentation strings and DECLARE forms mentioning ; only TYPE, IGNORE, and XARGS. We do not check that the dcls are actually ; legal because what we will ultimately do with them in verify-termination-fn ; is just create a modified definition to submit to defuns. Thus, defuns will ; ultimately approve the dcls. By construction, the dcls submitted to ; verify-termination will find their way, whole, into the submitted defuns. We ; return nil or cause an error according to whether lst satisfies the ; restrictions noted above. (cond ((null lst) (value nil)) ((not (and (consp (car lst)) (symbolp (caar lst)) (function-symbolp (caar lst) wrld) (plausible-dclsp (cdar lst)))) (er soft ctx "Each argument to verify-termination must be of the form (name ~ dcl ... dcl), where each dcl is either a DECLARE form or a ~ documentation string. The DECLARE forms may contain TYPE, ~ IGNORE, and XARGS entries, where the legal XARGS keys are ~&0. ~ The argument ~x1 is illegal. See :DOC verify-termination." *xargs-keywords* (car lst))) ((not (member-eq (caar lst) clique)) (er soft ctx "The function symbols whose termination is to be verified must ~ all be members of the same clique of mutually recursive ~ functions. ~x0 is not in the clique of ~x1. The clique of ~x1 ~ consists of ~&2. See :DOC verify-termination." (caar lst) fn1 clique)) (t (chk-acceptable-verify-termination1 (cdr lst) clique fn1 ctx wrld state)))) (defun uniform-defun-modes (defun-mode clique wrld) ; Defun-Mode should be a defun-mode. Clique is a list of fns. If defun-mode is ; :program then we return :program if every element of clique is ; :program; else nil. If defun-mode is :logic we return :logic if ; every element of clique is :logic; else nil. (cond ((null clique) defun-mode) ((programp (car clique) wrld) (and (eq defun-mode :program) (uniform-defun-modes defun-mode (cdr clique) wrld))) (t (and (eq defun-mode :logic) (uniform-defun-modes defun-mode (cdr clique) wrld))))) (defun chk-acceptable-verify-termination (lst ctx wrld state) ; We check that lst is acceptable input for verify-termination. To be ; acceptable, lst must be of the form ((fn . dcls) ...) where each fn is the ; name of a function, all of which are in the same clique and are in :program ; mode, not non-executable, where each dcls above is a plausible-dclsp. We ; cause an error or return (value nil). (cond ((and (consp lst) (consp (car lst)) (symbolp (caar lst))) (cond ((not (function-symbolp (caar lst) wrld)) (er soft ctx "The symbol ~x0 is not a function symbol in the current ACL2 world." (caar lst))) ((not (programp (caar lst) wrld)) ; If (caar lst) was introduced by encapsulate, then recover-defs-lst below will ; cause an implementation error. So we short-circuit our checks here, ; especially given since the uniform-defun-modes assertion below suggests that ; all functions should then be in :logic mode. Eventually, we will generate ; the empty list of definitions and treat the verify-termination as redundant, ; except: as a courtesy to the user, we may cause an error here if the function ; could not have been upgraded from :program mode. (cond ((getprop (caar lst) 'constrainedp nil 'current-acl2-world wrld) (er soft ctx "The :LOGIC mode function symbol ~x0 was originally ~ introduced introduced not with DEFUN, but ~#1~[as a ~ constrained function~/with DEFCHOOSE~]. So ~ VERIFY-TERMINATION does not make sense for this function ~ symbol." (caar lst) (cond ((getprop (caar lst) 'defchoose-axiom nil 'current-acl2-world wrld) 1) (t 0)))) (t (value :redundant)))) ((getprop (caar lst) 'non-executablep nil 'current-acl2-world wrld) (er soft ctx "The :PROGRAM mode function symbol ~x0 is declared non-executable, ~ so ~x1 is not legal for this symbol. Such functions are intended ~ only for hacking with defattach; see :DOC defproxy." (caar lst) 'verify-termination 'defun)) (t (let ((clique (get-clique (caar lst) wrld))) (assert$ ; We maintain the invariant that all functions in a mutual-recursion clique ; have the same defun-mode. This assertion check is not complete; for all we ; know, lst involves two mutual-recursion nests, and only the one for (caar ; lst) has uniform defun-modes. But we include this simple assertion to ; provide an extra bit of checking. (uniform-defun-modes (fdefun-mode (caar lst) wrld) clique wrld) (chk-acceptable-verify-termination1 lst clique (caar lst) ctx wrld state)))))) ((atom lst) (er soft ctx "Verify-termination requires at least one argument.")) (t (er soft ctx "The first argument supplied to verify-termination, ~x0, is not of ~ the form (fn dcl ...)." (car lst))))) (defun verify-termination1 (lst state) (let* ((lst (cond ((and (consp lst) (symbolp (car lst))) (list lst)) (t lst))) (ctx (cond ((null lst) "(VERIFY-TERMINATION)") ((and (consp lst) (consp (car lst))) (cond ((null (cdr lst)) (cond ((symbolp (caar lst)) (cond ((null (cdr (car lst))) (msg "( VERIFY-TERMINATION ~x0)" (caar lst))) (t (msg "( VERIFY-TERMINATION ~x0 ...)" (caar lst))))) ((null (cdr (car lst))) (msg "( VERIFY-TERMINATION (~x0))" (caar lst))) (t (msg "( VERIFY-TERMINATION (~x0 ...))" (caar lst))))) ((null (cdr (car lst))) (msg "( VERIFY-TERMINATION (~x0) ...)" (caar lst))) (t (msg "( VERIFY-TERMINATION (~x0 ...) ...)" (caar lst))))) (t (cons 'VERIFY-TERMINATION lst)))) (wrld (w state))) (er-let* ((temp (chk-acceptable-verify-termination lst ctx wrld state))) (let ((defs (if (eq temp :redundant) nil (recover-defs-lst (caar lst) wrld)))) (value (make-verify-termination-defs-lst defs lst wrld)))))) (defun verify-termination-boot-strap-fn (lst state event-form) (cond ((global-val 'boot-strap-flg (w state)) (when-logic ; It is convenient to use when-logic so that we skip verify-termination during ; pass1 of the boot-strap in axioms.lisp. "VERIFY-TERMINATION" (let ((event-form (or event-form (cons 'VERIFY-TERMINATION lst)))) (er-let* ((verify-termination-defs-lst (verify-termination1 lst state))) (defuns-fn verify-termination-defs-lst state event-form #+:non-standard-analysis nil))))) (t ; We do not allow users to use 'verify-termination-boot-strap. Why? See the ; comment in redundant-or-reclassifying-defunp0 about "verify-termination is ; now just a macro for make-event", and see the discussion about make-event at ; the end of :doc verify-termination. (er soft 'verify-termination-boot-strap "~x0 may only be used while ACL2 is being built. Use ~x1 instead." 'verify-termination-boot-strap 'verify-termination)))) (defmacro when-logic3 (str x) (list 'if '(eq (default-defun-mode-from-state state) :program) (list 'er-progn (list 'skip-when-logic (list 'quote str) 'state) (list 'value ''(value-triple nil))) x)) (defun verify-termination-fn (lst state) (when-logic3 ; We originally used when-logic here so that we would skip verify-termination during ; pass1 of the boot-strap in axioms.lisp. Now we use ; verify-termination-boot-strap for that purpose, but we continue the same ; convention, since by now users might rely on it. ; We could always return a defuns form, but the user may find it more pleasing ; to see a defun when there is a single definition, so we return a defun form ; in that case. "VERIFY-TERMINATION" (er-let* ((verify-termination-defs-lst (verify-termination1 lst state))) (value (cond ((null verify-termination-defs-lst) '(value-triple :redundant)) ((null (cdr verify-termination-defs-lst)) (cons 'defun (car verify-termination-defs-lst))) (t (cons 'defuns verify-termination-defs-lst))))))) ; When we defined instantiablep we included the comment that a certain ; invariant holds between it and the axioms. The functions here are ; not used in the system but can be used to check that invariant. ; They were not defined earlier because they use event tuples. (defun fns-used-in-axioms (lst wrld ans) ; Intended for use only by check-out-instantiablep. (cond ((null lst) ans) ((and (eq (caar lst) 'event-landmark) (eq (cadar lst) 'global-value) (eq (access-event-tuple-type (cddar lst)) 'defaxiom)) ; In this case, (car lst) is a tuple of the form ; (event-landmark global-value . tuple) ; where tuple is a defaxiom of some name, namex, and we are interested ; in all the function symbols occurring in the formula named namex. (fns-used-in-axioms (cdr lst) wrld (all-ffn-symbs (formula (access-event-tuple-namex (cddar lst)) nil wrld) ans))) (t (fns-used-in-axioms (cdr lst) wrld ans)))) (defun check-out-instantiablep1 (fns wrld) ; Intended for use only by check-out-instantiablep. (cond ((null fns) nil) ((instantiablep (car fns) wrld) (cons (car fns) (check-out-instantiablep1 (cdr fns) wrld))) (t (check-out-instantiablep1 (cdr fns) wrld)))) (defun check-out-instantiablep (wrld) ; See the comment in instantiablep. (let ((bad (check-out-instantiablep1 (fns-used-in-axioms wrld wrld nil) wrld))) (cond ((null bad) "Everything checks") (t (er hard 'check-out-instantiablep "The following functions are instantiable and shouldn't be:~%~x0" bad))))) acl2-sources/futures-raw.lisp0000666002132200015000000016262312222115527015743 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We thank David L. Rager for contributing an initial version of this file. ; This file is divided into the following sections. ; Section: Single-threaded Futures ; Section: Multi-threaded Futures ; Section: Futures Interface (in-package "ACL2") ; Essay on Futures ; This futures library provides three primitives for creating, reading, and ; aborting futures. We then use the futures library to implement ; spec-mv-let. Building spec-mv-let upon the futures library makes it more ; easily maintained than if it were built directly upon the low-level ; multi-threading interface. ; Parallelism wart: add to the above "Essay on Futures", with the intent that ; the essay should act as a guide to this file. ; Parallelism wart: clean up this file by removing blank lines that are ; inconsistent with the ACL2 style guide and making other improvements as ; appropriate (e.g., clean up comments about pending work). ;--------------------------------------------------------------------- ; Section: Single-threaded Futures (defstruct st-future ; Unlike mt-future objects, st-future objects execute lazily, i.e., only when ; reading them. (value nil) (valid nil) ; whether the value is valid (closure nil) (aborted nil)) (defmacro st-future (x) ; Speculatively creating a single-threaded future will not cause the future's ; value to be computed. Only reading the future causes such evaluation. `(let ((st-future (make-st-future))) (setf (st-future-closure st-future) (lambda () ,x)) (setf (st-future-valid st-future) nil) ; set to T once the value is known st-future)) (defun st-future-read (st-future) ; Speculatively reading from a single-threaded future will consume unnecessary ; CPU cycles (and could even lead to infinite recursion), so make sure all ; reading is necessary. (assert (st-future-p st-future)) (if (st-future-valid st-future) (values-list (st-future-value st-future)) (progn (setf (st-future-value st-future) (multiple-value-list (funcall (st-future-closure st-future)))) (setf (st-future-valid st-future) t) (values-list (st-future-value st-future))))) (defun st-future-abort (st-future) ; We could do nothing in this function and it would be fine. However, we mark ; it as aborted for book keeping and clear the closure for [earlier] garbage ; collection. (assert (st-future-p st-future)) (setf (st-future-aborted st-future) t) (setf (st-future-closure st-future) nil) st-future) ;--------------------------------------------------------------------- ; Section: Multi-threaded Futures ; Parallelism wart: discuss these notes with Matt. ; Notes on the implementation of adding, removing, and aborting the evaluation ; of closures: ; (1) Producer is responsible for *always* placing the closure on the queue. ; ; (2) Consumer is responsible for *always* removing the closure from the queue, ; regardless of whether there was early termination. Upon early termination, ; it is optional as to whether the early terminated future's barrier is ; signaled. (See defstruct barrier below for information about barriers.) For ; now, the barrier should not be signaled. ; ; (3) Only the producer of a particular future should abort that future. (The ; use of futures by spec-mv-let observes this protocol. Perhaps we should ; consider storing the thread in the future so that an eq test can be used to ; enforce this discipline.) The producer does so by first setting the abort ; flag of the future and then throwing any consumer that could be evaluating ; that future. ; ; (4) When a consumer evaluates a future, it first sets a pointer to itself in ; thread array and secondly checks the future's abort flag. ; ; (5) The combination of (3) and (4) results in the following six potential ; race conditions/scenarios. The first column contains things the producer can ; do, and the second column contains things the consumer might do. ; ; (A) - 12AB ; ; Producer sets the abort flag ; Producer looks for a thread to throw, continues ; Consumer sets the thread ; Consumer checks abort flag, aborts ; WIN ; ; ; (B) 1A2[B] ; ; Producer sets the abort flag ; Consumer sets the thread ; Producer looks for a thread to throw, throws ; ; NON-TRIVIAL to implement, need to check ; ; ; (C) 1AB[2] ; ; Producer sets the abort flag ; Consumer sets the thread ; Consumer checks abort flag, aborts ; SUBSUMED by A ; ; ; (D) A12[B] ; ; Consumer sets the thread ; Producer sets the abort flag ; Producer looks for a thread to throw, throws ; ; NON-TRIVIAL to implement, need to check ; ; ; (E) A1B[2] ; ; Consumer sets the thread ; Producer sets the abort flag ; Consumer checks abort flag, aborts ; WIN ; ; ; (F) AB12 ; Consumer sets the thread ; Consumer checks abort flag, continues ; Producer sets the abort flag ; Producer looks for a thread to throw, throws ; ; WIN ; ; ; LEGEND ; 1 = Producer sets the abort flag ; 2 = Producer looks for a thread to throw, continues/throws ; A = Consumer sets the thread ; B = Consumer checks abort flag, continues/aborts ; ; RULES ; 1 comes before 2 ; A comes before B ; ; ; (6) With the current design, it is assumed that only one thread will be ; issuing early termination orders -- the thread that generated the future ; stored at the given index. It's possible to change the design, but it would ; require more locking and be slower. ; We currently use a feature to control whether resources are tested for ; availability at the level of futures. Since this feature only controls ; futures, it only impacts the implementation underlying spec-mv-let. Thus, ; plet, pargs, pand, and por are unaffected. (push :skip-resource-availability-test *features*) (defstruct atomic-notification (value nil)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Closure evaluators ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Parallelism wart: probably delete the following two paragraphs. ; We continue our array-based approach for storing and grabbing pieces of ; parallelism work. This time, however, we do things a little differently. ; Instead of saving "pieces of parallelism work" to a queue, we only store ; closures. I'm not sure how this will pan out WRT early termination. I might ; end up making it more than just closures. ; There are some optimizations we can make if we assume that only one thread ; will be reading the future's value. E.g., we can remove the wait-count from ; barrier, because there will always be only one thread waiting. (defstruct barrier ; Our version of a barrier is a hybrid between a semaphore and a condition ; variable. What we need is something that once it's signaled once, any thread ; that waits on it will be allowed to proceed. ; Point of clarification that is a little distracting: Our notion of barrier is ; different from the traditional definition of a "multi-threaded programming ; barrier" in the following way: in the traditional definition, a barrier is a ; spot in the program's execution that _n_ threads will eventually reach. Once ; a thread reaches the barrier, it blocks (waits) until _n_ threads have ; reached the barrier. Once all of the _n_ threads have reached the barrier, ; they are all given the green light to proceed. Our notion of a barrier is ; different from this, in that there is no "global wait by _n_ threads". In ; our notion of a barrier, any number of threads can wait on the barrier. Each ; of these threads will block until the barrier is signaled. Once the barrier ; is signaled, all of the blocked threads are allowed to proceed, and any ; thread that waits upon the barrier in the future is also allowed to ; immediately proceed. (value nil) (lock (make-lock)) (wait-count 0) (sem (make-semaphore))) (defun broadcast-barrier (barrier) ; Update the barrier as "clear to proceed" and notify all threads waiting for ; such clearance. (without-interrupts ; we can be stuck in a non-interruptible deadlock (setf (barrier-value barrier) t) (with-lock (barrier-lock barrier) (let ((count (barrier-wait-count barrier))) (loop for i from 0 to count do (signal-semaphore (barrier-sem barrier)) (decf (barrier-wait-count barrier))))))) (defun wait-on-barrier (barrier) (if (barrier-value barrier) t (progn (with-lock (barrier-lock barrier) (incf (barrier-wait-count barrier))) ; There has to be another test after holding the lock. (when (not (barrier-value barrier)) (wait-on-semaphore (barrier-sem barrier)))))) (defstruct mt-future ; Unlike st-future objects, mt-future objects execute eagerly. (index nil) (value nil) (valid (make-barrier)) ; initially contains a nil valid bit for the barrier (closure nil) (aborted nil) (thrown-tag nil)) (define-atomically-modifiable-counter *last-slot-saved* 0) (define-atomically-modifiable-counter *last-slot-taken* 0) ; The three arrays defined below all have the same length, *future-array-size*. ; They correspond as follows: for a future F stored in the ith element of ; *future-array*, the ith element of *thread-array* is the thread executing F, ; and the ith element of *future-dependencies* is a list of all indices (in ; *future-array*) of futures created by F. ; Perhaps we should be concerned that the array elements will be so close ; together, that they'll be in the same cache lines, and the CPU cores will get ; bogged down just keeping the writes to the cache "current". The exact impact ; on performance of this thrashing is unknown. (However, correctness is not an ; issue, since semantically caches are just an optimization, as enforced by ; cache coherency schemes.) Followup: After further thought, David Rager ; believes that this thrashing will be negligible when compared to the rest of ; the parallelism overhead. (defvar *future-array*) (defvar *thread-array*) (defvar *future-dependencies*) (defparameter *future-queue-length-history* ; supports dmr as modified for ACL2(p) nil) (defvar *current-thread-index* ; For this variable, we take advantage of the fact that special variables are ; thread-local. Here we set it to 0 for the main thread. 0) (defconstant *starting-core* 'start) (defconstant *resumptive-core* 'resumptive) (defvar *allocated-core* ; The value of this variable is always *starting-core*, *resumptive-core*, or ; nil. ; We now document a rather strange behavior that resulted in a bug in the ; parallelism system for a good while. This strange behavior justifies giving ; *allocated-core* an initial value of *resumptive-core* instead of ; *starting-core*. To understand why, suppose we instead gave *allocated-core* ; the initial value of *starting-core*. Then, when the main thread encountered ; its first parallelism primitive, it would set *allocated-core* to nil and ; then, when it resumed execution after the parallelized portion was done, it ; would claim a resumptive core, and the main thread would then have ; *resumptive-core* for its value of *allocated-core*. This would be fine, ; except that we'll never reclaim the original *starting-core* for the main ; thread. So, rather than worry about this problem, we side-step it entirely ; and start the main thread as a "resumptive" core. ; Parallelism blemish: the above correction may also need to be made for the ; other parallelism implementation that supports plet/pargs/pand/por. *resumptive-core*) (defvar *decremented-idle-future-thread-count* nil) (defvar *idle-future-core-count* (make-atomically-modifiable-counter *core-count*)) (defvar *idle-future-resumptive-core-count* (make-atomically-modifiable-counter (1- *core-count*))) (defvar *idle-core* (make-semaphore)) (define-atomically-modifiable-counter *idle-future-thread-count* ; Parallelism blemish: on April 6, 2012, Rager observed that ; *idle-future-thread-count* and *threads-waiting-for-starting-core* can sync ; up and obtain the same value. As such, it occurs to Rager that maybe we ; still consider threads that are waiting for a CPU core to be "idle." This ; labeling might be fine, but it's inconsistent with our heuristic for ; determining whether to spawn a closure consumer (but as we explain below, in ; practice, it does not present a problem). Investigate when a thread is ; considered to no longer be idle, and revise the heuristic as needed. Note ; that this investigation isn't absolutely necessary, because we currently ; ensure that the total number of threads that are idle or waiting on a CPU ; core (we call these classifications "available" below) are at least twice the ; number of CPU cores in the system. Thus, counting the same thread twice ; still results in having a number of available threads that's at least the ; number of CPU cores, which is fine. 0) (defvar *future-added* (make-semaphore)) (defvar *idle-resumptive-core* (make-semaphore)) ; Debug variable: (defvar *threads-spawned* 0) (define-atomically-modifiable-counter *unassigned-and-active-future-count* ; We count the number of futures that are in the unassigned or active ; (including both started and resumed) state. Thus, we are not including ; futures that are in the pending state. See also *total-future-count*. ; We treat the initial thread as an active future. 1) (define-atomically-modifiable-counter *total-future-count* ; We count the total number of futures, each of which is in the unassigned, ; active (including both started and resumed), or pending state. See also ; *unassigned-and-active-future-count*, which does not count those in the ; pending state. ; An invariant is that the value of this counter is always less than the value ; of ACL2 state global 'total-parallelism-work-limit. 0) (defconstant *future-array-size* 200000) (defmacro faref (array subscript) `(aref ,array ; Avoid reusing slot 0, which is always reserved for the initial thread. (if (equal 0 ,subscript) 0 (1+ (mod ,subscript (1- *future-array-size*)))))) (defvar *resource-and-timing-based-parallelizations* 0 "Tracks the number of times that we parallelize execution when waterfall-parallelism is set to :resource-and-timing-based") (defvar *resource-and-timing-based-serializations* 0 "Tracks the number of times that we do not parallize execution when waterfall-parallelism is set to :resource-and-timing-based") (defvar *resource-based-parallelizations* 0 "Tracks the number of times that we parallelize execution when waterfall-parallelism is set to :resource-based") (defvar *resource-based-serializations* 0 "Tracks the number of times that we do not parallize execution when waterfall-parallelism is set to :resource-based") (defun reset-future-queue-length-history () (setf *future-queue-length-history* nil)) (defun reset-future-parallelism-variables () ; Warning: this function should only be called after calling ; reset-parallelism-variables, which calls ; send-die-to-worker-threads to kill all worker threads. ; This function is not to be confused with reset-parallelism-variables ; (although it is similar in nature). Both are called by ; reset-all-parallelism-variables. ; Parallelism wart: some relevant variables may be unintentionally omitted from ; this reset. (setf *thread-array* (make-array *future-array-size* :initial-element nil)) (setf *future-array* (make-array *future-array-size* :initial-element nil)) (setf *future-dependencies* (make-array *future-array-size* :initial-element nil)) (setf *future-added* (make-semaphore)) (setf *idle-future-core-count* (make-atomically-modifiable-counter *core-count*)) (setf *idle-future-resumptive-core-count* (make-atomically-modifiable-counter (1- *core-count*))) (setf *idle-core* (make-semaphore)) (setf *idle-resumptive-core* (make-semaphore)) (dotimes (i *core-count*) (signal-semaphore *idle-core*)) (dotimes (i (1- *core-count*)) (signal-semaphore *idle-resumptive-core*)) ; The last slot taken and saved starts at zero, because slot zero is always ; reserved for the initial thread. (setf *last-slot-taken* (make-atomically-modifiable-counter 0)) (setf *last-slot-saved* (make-atomically-modifiable-counter 0)) (setf *threads-spawned* 0) (setf *total-future-count* (make-atomically-modifiable-counter 0)) (setf *unassigned-and-active-future-count* (make-atomically-modifiable-counter 1)) ; If we let the threads expire naturally instead of calling the above ; send-die-to-worker-threads, then this setting is unnecessary. (setf *idle-future-thread-count* (make-atomically-modifiable-counter 0)) ; (setf *pending-future-thread-count* (make-atomically-modifiable-counter 0)) ; (setf *resumptive-future-thread-count* (make-atomically-modifiable-counter 0)) ; (setf *acl2-par-arrays-lock* (make-lock)) (setf *resource-and-timing-based-parallelizations* 0) (setf *resource-and-timing-based-serializations* 0) (setf *resource-based-parallelizations* 0) (setf *resource-based-serializations* 0) ; (setf *aborted-futures-total* 0) (reset-future-queue-length-history) t ; return t ) ; The following invocation would cause errors in Lispworks. It probably isn't ; needed for other Lisps either. But it seems harmless to leave it in, which ; has the advantage of testing reset-future-parallelism-variables during the ; build. #-lispworks (reset-future-parallelism-variables) (defun reset-all-parallelism-variables () (format t "Resetting parallelism and futures variables. This may take a ~ few seconds (often either~% 0 or 15).~%") (reset-parallelism-variables) (reset-future-parallelism-variables) (format t "Done resetting parallelism and futures variables.~%")) (defun futures-parallelism-buffer-has-space-available () ; This test is used only to implement resource-based parallelism for futures. (< (atomically-modifiable-counter-read *unassigned-and-active-future-count*) *unassigned-and-active-work-count-limit*)) (defun not-too-many-futures-already-in-existence () ; See :DOC topic set-total-parallelism-work-limit and :DOC topic ; set-total-parallelism-work-limit-error for more details. (let ((total-parallelism-work-limit (f-get-global 'total-parallelism-work-limit *the-live-state*))) (cond ((equal total-parallelism-work-limit :none) ; If the value is :none, then there is no limit. t) ((< (atomically-modifiable-counter-read *total-future-count*) total-parallelism-work-limit) t) (t ; We are above the total-parallelism-work-limit. Now the question is whether we ; notify the user with an error. (let ((total-parallelism-work-limit-error (f-get-global 'total-parallelism-work-limit-error *the-live-state*))) (cond ((equal total-parallelism-work-limit-error t) ; Cause an error to notify the user that they need to either increase the limit ; or disable the error by setting the global variable ; total-parallelism-work-limit to nil. This is the default behavior. (er hard 'not-too-many-futures-already-in-existence "The system has encountered the limit placed upon the ~ total amount of parallelism work allowed. Either ~ the limit must be increased, or this error must be ~ disabled. See :DOC set-total-parallelism-work-limit ~ and :DOC set-total-parallelism-work-limit-error for ~ more information.")) ((null total-parallelism-work-limit-error) nil) (t (er hard 'not-too-many-futures-already-in-existence "The value for global variable ~ total-parallelism-work-limit-error must be one of ~ t or nil. Please change the value of this global ~ variable to either of those values.")))))))) (defun futures-resources-available () ; This function is our attempt to guess when resources are available. When ; this function returns true, then resources are probably available, and a ; parallelism primitive call will opt to parallelize. We say "probably" ; because correctness does not depend on our answering exactly. For ; performance, we prefer that this function is reasonably close to an accurate ; implementation that would use locks. Perhaps even more important for ; performance, however, is that we avoid the cost of locks to try to remove ; bottlenecks. ; In summary, it is unneccessary to acquire a lock, because we just don't care ; if we miss a few chances to parallelize, or parallelize a few extra times. (and (f-get-global 'parallel-execution-enabled *the-live-state*) (futures-parallelism-buffer-has-space-available) (not-too-many-futures-already-in-existence))) (defmacro unwind-protect-disable-interrupts-during-cleanup (body-form &rest cleanup-forms) ; As the name suggests, this is unwind-protect but with a guarantee that ; cleanup-form cannot be interrupted. Note that CCL's implementation already ; disables interrupts during cleanup. #+ccl `(unwind-protect ,body-form ,@cleanup-forms) #+sb-thread `(unwind-protect ,body-form (without-interrupts ,@cleanup-forms)) ; Parallelism wart: we should specify a Lispworks compile-time definition (as ; we did for CCL and SBCL). Or, perhaps we should merge the CCL definition ; into the LispWorks definition. Regardless, we need to check that interrupts ; are disabled during the cleanup form in LispWorks and then make a note that ; LispWorks has this property (perhaps by merging with the note about CCL, ; above). #-(or ccl sb-thread) `(unwind-protect ,body-form ,@cleanup-forms)) (define-atomically-modifiable-counter *threads-waiting-for-starting-core* ; Once upon a time this variable was only used for debugging purposes, so we ; didn't make its updates atomic. However, we actually observed this variable ; going to a value of -37 (it should never go below 0) when we weren't using ; atomic updates. Plus, now we actually use this variable's value to determine ; whether we spawn closure consumers. So, as of April 2012, it is an atomic ; variable. 0) (defun claim-starting-core () ; Parallelism wart: consider the possibility that the atomic-incf completes, ; and then a control-c causes an interrupt before the unwind-protect is entered ; -- so we leave *threads-waiting-for-starting-core* incremented, and its value ; creeps up this way during the ACL2 session. A solution may be to have a flag ; that is set when the atomic-incf is completed, and set that flag within a ; without-interrupts. (atomic-incf *threads-waiting-for-starting-core*) (let ((notification (make-semaphore-notification))) (unwind-protect-disable-interrupts-during-cleanup (wait-on-semaphore *idle-core* :notification notification) (progn (when (semaphore-notification-status notification) (setf *allocated-core* *starting-core*) (atomic-decf *idle-future-core-count*) ; Parallelism blemish: is this really the right place to do the following setf? (setf *decremented-idle-future-thread-count* t) (atomic-decf *idle-future-thread-count*)) (atomic-decf *threads-waiting-for-starting-core*))))) (defun claim-resumptive-core () ; Parallelism blemish: the following script provokes a bug where the ; *idle-resumptive-core* semaphore signal isn't being appropriately ; received... most likely because it's not being signaled (otherwise it would ; be a CCL issue). ;; (defun make-and-read-future () ;; (future-read (future 3))) ;; (time$ (dotimes (i 100000) ;; (make-and-read-future))) ;; (defvar *making-and-reading-done* ;; (make-semaphore)) ;; (defun make-and-read-future-100000-times () ;; (time$ (dotimes (i 100000) ;; (make-and-read-future))) ;; (signal-semaphore *making-and-reading-done*)) ;; (defun make-and-read-future-in-multiple-threads (thread-count) ;; (time ;; (dotimes (i thread-count) ;; (run-thread "making and reading futures" ;; #'make-and-read-future-100000-times)) ;; (dotimes (i thread-count) ;; (wait-on-semaphore *making-and-reading-done*)))) ;; (make-and-read-future-in-multiple-threads 2) (let ((notification (make-semaphore-notification))) (unwind-protect-disable-interrupts-during-cleanup (wait-on-semaphore *idle-resumptive-core* :notification notification) (when (semaphore-notification-status notification) (setf *allocated-core* *resumptive-core*) (atomic-decf *idle-future-resumptive-core-count*))))) (defun free-allocated-core () ; This function frees an allocated core only if there is one! Thus, it is ; perfectly safe to call this function even when a core has not been allocated ; to the current thread. This notion is thread-local, as is the special ; variable *allocated-core*. (without-interrupts (cond ((eq *allocated-core* *starting-core*) (atomic-incf *idle-future-core-count*) (signal-semaphore *idle-core*) (setf *allocated-core* nil)) ((eq *allocated-core* *resumptive-core*) (atomic-incf *idle-future-resumptive-core-count*) (signal-semaphore *idle-resumptive-core*) (setf *allocated-core* nil)) ; Under early termination, the early terminated thread doesn't acquire a ; resumptive core. (t nil)) t)) (defun early-terminate-children (index) ; With the current design, it is assumed that only one thread will be issuing ; an early termination order to any given future -- the thread that generated ; the future stored at the given index. It's possible to change the design, ; but it would require more locking. ; Due to this more specific design, the function is named ; "early-terminate-children. A more general function could be named ; "early-terminate-relatives". (abort-future-indices (faref *future-dependencies* index)) (setf (faref *future-dependencies* index) nil)) ; Debug variables: (defvar *aborted-futures-via-flag* 0) (defvar *aborted-futures-total* 0) ; Debug variables: (defvar *futures-resources-available-count* 0) (defvar *futures-resources-unavailable-count* 0) (defun set-thread-check-for-abort-and-funcall (future) ; This function sets the current index in *thread-array* to the current thread, ; checks whether the given future has been marked as aborted, and if not then ; executes the closure field of the given future. (let* ((index (mt-future-index future)) (closure (mt-future-closure future)) ; Bind thread-local versions of special variables here. (*allocated-core* nil) (*current-thread-index* index) (*decremented-idle-future-thread-count* nil) (early-terminated t)) (unwind-protect-disable-interrupts-during-cleanup (progn ; It might not be necessary to claim a starting core until after we check ; whether the future has been marked as aborted. But David Rager believes that ; he had a reason for doing things in this order, and the resulting ; inefficiency seems very minor, so we leave this as is. (claim-starting-core) ; It is common to wait here. (setf (faref *thread-array* index) (current-thread)) (if (mt-future-aborted future) (incf *aborted-futures-via-flag*) (progn ;(format t "starting index ~s~%" *current-thread-index*) (setf (mt-future-value future) (multiple-value-list (funcall closure))) ;(format t "done with index ~s~%" *current-thread-index*) (setq early-terminated nil) ; This broadcast used to occur outside the "if", but I think that was a ; potential bug. (broadcast-barrier (mt-future-valid future))))) (progn ; terminate first since we're about to free a cpu core, which would allow ; worker threads to pickup the children sooner (setf (faref *thread-array* index) nil) (when early-terminated (early-terminate-children index)) (setf (faref *future-dependencies* index) nil) (when *decremented-idle-future-thread-count* ; increment paired with decrement in (claim-starting-core) (atomic-incf *idle-future-thread-count*)) (free-allocated-core) (setf (faref *future-array* index) nil) ;; (setf *current-thread-index* -1) ; falls out of scope )))) (defvar *throwable-future-worker-thread* ; A given thread may be interrupted and told to throw the tag ; :result-no-longer-needed, as a means to abort a future. However, it will ; ignore that request if and only if this (thread-local) variable is nil. In ; the case that this variable is nil, there's no point in throwing said tag, ; because there is no work to abort. ; ; *Throwable-future-worker-thread* is unrelated to tag ; :worker-thread-no-longer-needed. ; Parallelism blemish: pick a name that makes it more obvious that this ; variable is unrelated to variable *throwable-worker-thread*. nil) (defun wait-for-a-closure () ; To understand this function, first consider *last-slot-saved* and ; *last-slot-taken*. These are indices into *future-array*, where ; *last-slot-saved* is the maximum index at which a future produced to be ; executed was placed, while *last-slot-taken* is the maximum index from which ; a future has been consumed by a worker thread. So when taken < saved, the ; indices inbetween hold futures that are awaiting execution. Thus, when taken ; >= saved, there is no work waiting to be started. Note that these "indices" ; actually can grow without bound; function faref comprehends the wrap-around ; nature of *future-array*, converting them to actual indices. (loop while (>= (atomically-modifiable-counter-read *last-slot-taken*) (atomically-modifiable-counter-read *last-slot-saved*)) do ; There is no work to be done, so wait on a semaphore that signals the ; placement of a new future in *future-array*. The code below returns when ; either there is a timeout during that wait, or else a new future has been ; added to *future-array*. In the latter case, *last-slot-saved* will have ; been increased. Typically, *last-slot-taken* will not yet have been ; increased -- the current thread will increment it soon after returning from ; this function. (Note that the increment happens before execution of the new ; future by this thread, which will take place when a core becomes available -- ; and that may take awhile). ; Why are we in a while loop? Even though *last-slot-saved* has been increased ; and the current thread has not yet increased *last-slot-taken*, it is ; possible for some other thread to increase *last-slot-taken*. That can ; happen when another thread comes along just after the semaphore notification ; comes to the current thread, below, and the other thread sees the test above ; as false -- so for that thread, the present function does nothing and that ; thread goes on to increment *last-slot-taken*. ; But how long do we wait on the semaphore, below, before timing out? ; As of Feb 19, 2012, instead of picking a somewhat random duration to wait, we ; would always wait 15 seconds. This was fine, except that a proof done by ; Robert Krug caused over 3000 threads to become active at the same time, ; because Rager's Lisp of choice (CCL) was so efficient in its handling of ; threads and semaphore signals. Our solution to this problem involves calling ; the function random, below. Here are more details: ; Put briefly, the implementation of timeouts in CCL is so good, that once a ; proof finishes, if there was a tree of subgoals (suppose those subgoals are ; named Subgoal 10000, Subgoal 9999, ... Subgoal 2) blocked on Subgoal 1 ; finishing (which his how the implementation of waterfall1-lst works as of Feb ; 19, 2012), once Subgoal 1 finishes, each thread associated with Subgoal ; 10000, Subgoal 9999, ... Subgoal 2, Subgoal 1 will finish computing at ; approximately the same time (Subgoal 10000 is waiting for Subgoal 9999, ; Subgoal 9999 is waiting on Subgoal 9998... and so forth). As such, once all ; 10,000 of these threads decide to wait on the semaphore *future-added*, as ; below, they were all enqueued to run at almost exactly the same time (15 ; seconds from when they finished proving their subgoal) by the CPU scheduler. ; This results in the 1-minute Average Load-time (a Linux term, see ; http://www.linuxjournal.com/article/9001 for further info) shooting through ; the roof (upwards of 1000 in some cases), and then the Linux daemon process ; "watchdog" (see the Linux man page for watchdog) tells the machine to reboot, ; because "watchdog" thinks all chaos has broken loose (but, of course, chaos ; has not broken loose). We _could_ argue with system maintainers about what ; an appropriate threshold is for determining when chaos breaks loose, but it ; would be silly. We're not even coding ACL2(p) just for use in one ; environment -- we want it to work at all institutions without having to ; trouble sysadmins. As such, rather than worry about this anymore, we ; circumvent the problem by doing the following: Instead of having every thread ; wait 15 seconds for new parallelism work to enter the system, we have every ; thread wait a random amount of time, within a reasonable range. ; One can see Section "Another Granularity Issue Related to Thread Limitations" ; inside :DOC topic parallelism-tutorial for an explanation of how user-level ; programs can have trees of nested computation. (let ((random-amount-of-time (+ 10 (random 110.0)))) (when (not (wait-on-semaphore *future-added* :timeout random-amount-of-time)) ; Then we timed out. (If the semaphore had been obtained, then the above call ; of wait-on-semaphore would have returned t.) (throw :worker-thread-no-longer-needed nil))))) ; Debug variables: (defvar *busy-wait-var* 0) (defvar *current-waiting-thread* nil) (defvar *fresh-waiting-threads* 0) ; We now develop support for our throw-catch-let macro. Note that "tclet" ; abbreviates "throw-catch-let". (defun make-tclet-thrown-symbol1 (tags first-tag) (if (endp tags) "" (concatenate 'string (if first-tag "" "-OR-") (symbol-name (car tags)) "-THROWN" (make-tclet-thrown-symbol1 (cdr tags) nil)))) (defun make-tclet-thrown-symbol (tags) (intern (make-tclet-thrown-symbol1 tags t) "ACL2")) (defun make-tclet-bindings1 (tags) (if (endp tags) nil (cons (list (make-tclet-thrown-symbol (reverse tags)) t) (make-tclet-bindings1 (cdr tags))))) (defun make-tclet-bindings (tags) (Reverse (make-tclet-bindings1 (reverse tags)))) (defun make-tclet-thrown-tags1 (tags) (if (endp tags) nil (cons (make-tclet-thrown-symbol (reverse tags)) (make-tclet-thrown-tags1 (cdr tags))))) (defun make-tclet-thrown-tags (tags) (reverse (make-tclet-thrown-tags1 (reverse tags)))) (defun make-tclet-catches (rtags body thrown-tag-bindings) (if (endp rtags) body (list 'catch (list 'quote (car rtags)) (list 'prog1 ; 'our-multiple-value-prog1 ; we don't support multiple-values at all (make-tclet-catches (cdr rtags) body (cdr thrown-tag-bindings)) `(setq ,(car thrown-tag-bindings) nil))))) (defun make-tclet-cleanups (thrown-tags cleanups) (if (endp thrown-tags) '((t nil)) (cons (list (car thrown-tags) (car cleanups)) (make-tclet-cleanups (cdr thrown-tags) (cdr cleanups))))) (defmacro throw-catch-let (tags body cleanups) ; This macro takes three arguments: ; Tags is a list of tags that can be thrown from within body. ; Body is the body to execute. ; Cleanups is a list of forms, one of which will be executed in the event that ; the corresponding tag is thrown. The tags and cleanup forms are given their ; association with each other by their order. So, if tag 'x-tag is the third ; element in tags, the cleanup form for 'x-tag should similarly be the third ; form in cleanups. ; This macro does not support throwing multiple-values as a throw's return ; value. (Probably it could, however, by replacing prog1 by ; multiple-value-prog1.) ; Consider the following example. ; (throw-catch-let ; (one two three) ; ; The following might invoke (throw 'one), (throw 'two), and/or ; ; (throw 'three). ; (arbitrary-code-here) ; ((handle-one) ; (handle-two) ; (handle-three))) ; Here is the single-step macroexpansion of the above example. ; (let ((one-thrown t) ; (one-thrown-or-two-thrown t) ; (one-thrown-or-two-thrown-or-three-thrown t)) ; (let ((tclet-result ; (catch 'one ; (prog1 (catch 'two ; (prog1 (catch 'three ; (prog1 ; (arbitrary-code-here) ; (setq ; one-thrown-or-two-thrown-or-three-thrown ; nil))) ; (setq one-thrown-or-two-thrown nil))) ; (setq one-thrown nil))))) ; (prog2 (cond (one-thrown (handle-one)) ; (one-thrown-or-two-thrown (handle-two)) ; (one-thrown-or-two-thrown-or-three-thrown ; (handle-three)) ; (t nil)) ; tclet-result))) ; Here is a more concrete example use of throw-catch-let. ; (throw-catch-let ; (x y) ; (cond ((equal *flg* 3) (throw 'x 10)) ; ((equal *flg* 4) (throw 'y 11)) ; (t 7)) ; ((setq *x-thrown* t) ; (setq *y-thrown* t))) ; While Rager wrote this macro, he thanks Nathan Wetzler for co-development of ; the main ideas. (let* ((thrown-tags (make-tclet-thrown-tags tags))) `(let ,(make-tclet-bindings tags) (let ((tclet-result ,(make-tclet-catches tags body thrown-tags))) (prog2 (cond ,@(make-tclet-cleanups thrown-tags cleanups)) tclet-result))))) (defun eval-a-closure () (let* ((index (atomic-incf *last-slot-taken*)) (*current-thread-index* index) (thrown-tag nil) (thrown-val nil) (future nil)) ; Hopefully very rarely, we busy wait for the future to arrive. That can ; happen because *last-slot-saved* is incremented before the future is actually ; put there. (loop while (not (faref *future-array* index)) do ; Set debugging variables *busy-wait-var*, *current-waiting-thread*, and ; *fresh-waiting-threads*. (incf *busy-wait-var*) (when (not (equal (current-thread) *current-waiting-thread*)) (setf *current-waiting-thread* (current-thread)) (incf *fresh-waiting-threads*))) ; The tags we need to catch for throwing again later are raw-ev-fncall, ; local-top-level, time-limit5-tag, and step-limit-tag. We do not bother ; catching missing-compiled-book, because the code that throws it says it would ; be an ACL2 implementation error to actually execute the throw. If other tags ; are later added to the ACL2 source code, we should add them to the below ; throw-catch-let. (throw-catch-let (raw-ev-fncall local-top-level time-limit5-tag step-limit-tag) (catch :result-no-longer-needed (let ((*throwable-future-worker-thread* t)) (progn (setq future (faref *future-array* index)) (set-thread-check-for-abort-and-funcall future)))) ((progn (setf thrown-tag 'raw-ev-fncall) (setf thrown-val tclet-result)) (progn (setf thrown-tag 'local-top-level) (setf thrown-val tclet-result)) (progn (setf thrown-tag 'time-limit5-tag) (setf thrown-val tclet-result)) (progn (setf thrown-tag 'step-limit-tag) (setf thrown-val tclet-result)))) ; The following does not need to be inside an unwind-protect-cleanup because ; set-thread-check-for-abort-and-funcall also removes the pointer to this ; thread in *thread-array*. (atomic-decf *unassigned-and-active-future-count*) (atomic-decf *total-future-count*) (when thrown-tag (setf (mt-future-thrown-tag future) (cons thrown-tag thrown-val)) ; A future that threw a tag is still a legal future to read. In fact, the ; throw does not re-occur until the future is read; see the throw in ; mt-future-read. (broadcast-barrier (mt-future-valid future))))) (defun eval-closures () ; Worker threads are initialized to run this function; see the call of ; run-thread in spawn-closure-consumers. ; The following is done inside the spawner, spawn-closure-consumers. ; (atomic-incf *idle-future-thread-count*) (catch :worker-thread-no-longer-needed ; The catch of a somewhat analogous tag :result-no-longer-needed, is performed ; inside eval-a-closure (called below). (let ((*throwable-worker-thread* ; Note that at the place we consider throwing to ; :worker-thread-no-longer-needed, we check that *throwable-worker-thread* is t ; as an indicator that the corresponding catcher is set up. t)) ; The following loop is exited by a throw to :worker-thread-no-longer-needed, ; which is performed by wait-for-a-closure when there has been no closure to ; execute for a random-amount-of-time (see wait-for-a-closure; currently from ; 10 to 120 seconds). (loop (wait-for-a-closure) (eval-a-closure)))) ; The following decrement will always execute, unless the user terminates the ; thread in raw Lisp in an unsupported manner. (atomic-decf *idle-future-thread-count*)) (defun number-of-idle-threads-and-threads-waiting-for-a-starting-core () ; See (A) and (B) in the Essay on Parallelism [etc.]. (+ (atomically-modifiable-counter-read ; (A) *idle-future-thread-count*) (atomically-modifiable-counter-read ; (B) *threads-waiting-for-starting-core*))) (defun spawn-closure-consumers () ; A call of a parallelism primitive invokes this function in order to ensure ; that there are threads available to pick up the piece of work that it has ; created (by adding a future to the work queue). As far as we know, we could ; get by just fine by spawning at most one thread, rather than spawning threads ; to bring the total in the (A) or (B) state (see the Essay on Parallelism ; [etc.]) up to *max-idle-thread-count*, as we do here. But in case we have ; missed something in our design that could cause us to have insufficient ; threads ready to do work, we try to keep the number of threads in the (A) or ; (B) state at the limit we have chosen, i.e., *max-idle-thread-count*. (without-interrupts ; Parallelism blemish: there may be a bug where *idle-future-thread-count* is ; incremented to 64 under early termination. ; We bring the number of threads in state (A) or (B), as described above, up to ; our specified limit. Why not count just those in state (A)? In fact, we did ; so up to April, 2012. The problem was that this function could create ; threads in state (A), which transition to (B) and wait for a core, and then ; this function could continue to do this repeatedly, resulting in a huge ; number of threads (in state (B)), which could swamp the system. Our current ; approach solves this problem. Note that it can be common for ; *idle-future-thread-count* to be 0, because the created threads have moved to ; state (B) but not yet been allocated a core. (loop while (< (number-of-idle-threads-and-threads-waiting-for-a-starting-core) *max-idle-thread-count*) do (progn (atomic-incf *idle-future-thread-count*) (incf *threads-spawned*) ; debug variable (run-thread "Worker thread" 'eval-closures))))) (defun make-future-with-closure (closure) ; Create a future with the indicated closure. ; The assertions below can fire if a long-running future is created before the ; current index (*last-slot-saved*) of the *future-array* wraps around to 0 and ; then back to the index of the long-running future. This could happen in the ; use of futures in the ACL2(p) code, i.e., for parallelizing the waterfall, ; but only if very many subgoals (never seen as of April, 2012) are created ; during a single call of the waterfall, so large in fact that a goal's future ; remains active after creating *future-array-size* more futures. ; The basic problem, of potentially overwriting an entry in an array after ; wrapping around, could in principle be solved by just skipping over any such ; indices until finding an available index. Then an error would only occur if ; all *future-array-size* entries are active. However, David Rager has ; explained that the current implementation relies upon incrementing the ; current index by just 1, and in fact takes advantage of that in communication ; between producers and consumers of futures. If we change the implementation ; to allow such skipping, we need to think about communication between ; producers and consumers, and we should think about whether we will lose ; efficiency. And we might well lose efficiency, because we may need to lock ; down the entire array as we look for a free index, rather than using atomic ; increments (and avoiding locks entirely) as we do now. (let ((future (make-mt-future)) (index (atomic-incf *last-slot-saved*))) (assert (not (faref *thread-array* index))) (assert (not (faref *future-array* index))) (assert (not (faref *future-dependencies* index))) (setf (mt-future-index future) index) (setf (faref *future-dependencies* *current-thread-index*) ; Add the index of the new future to the list of futures that must be aborted ; if the current thread is aborted. (cons index (faref *future-dependencies* *current-thread-index*))) (setf (mt-future-closure future) closure) future)) (defun add-future-to-queue (future) ; This function must be called with interrupts disabled. (setf (faref *future-array* (mt-future-index future)) future) (atomic-incf *total-future-count*) (atomic-incf *unassigned-and-active-future-count*) (spawn-closure-consumers) (signal-semaphore *future-added*) future) (defmacro mt-future (x) ; Return a future whose closure, when executed, will execute the given form, x. ; Note that (future x) macroexpands to (mt-future x). (let ((ld-level-sym (gensym)) (ld-level-state-sym (gensym)) (wormholep-sym (gensym)) (local-safe-mode-sym (gensym)) (local-gc-on-sym (gensym))) `(cond (#-skip-resource-availability-test (not (futures-resources-available)) #+skip-resource-availability-test nil ; need to return a "single-threaded" future (incf *futures-resources-unavailable-count*) (st-future ,x)) (t ; normal case (incf *futures-resources-available-count*) ; We need to set up bindings for some special variables to have appropriate ; values when the future is executed. Note that the current value of a special ; variable is irrelevant for its value in a newly created thread, as ; illustrated in the following log. ; ? [RAW LISP] (defvar foo 1) ; FOO ; ? [RAW LISP] (let ((foo 2)) ; (run-thread "asdf" ; (lambda () ; (list (print foo) ; (print (symbol-value 'foo)))))) ; # ; ? [RAW LISP] ; 1 ; 1 (let* ((,ld-level-sym *ld-level*) (,ld-level-state-sym ; We have discussed with David Rager whether it is an invariant of ACL2 that ; *ld-level* and (@ ld-level) have the same value, except perhaps when cleaning ; up with acl2-unwind-protect. If it is, then David believes that it's also an ; invariant of ACL2(p). We add an assertion here to check that. (assert$ (equal *ld-level* (f-get-global 'ld-level *the-live-state*)) (f-get-global 'ld-level *the-live-state*))) (acl2-unwind-protect-stack *acl2-unwind-protect-stack*) (,wormholep-sym *wormholep*) (,local-safe-mode-sym (f-get-global 'safe-mode *the-live-state*)) (,local-gc-on-sym (f-get-global 'guard-checking-on *the-live-state*)) ; Parallelism no-fix: we have considered causing child threads to inherit ; ld-specials from their parents, or even other state globals such as ; *ev-shortcut-okp* and *raw-guard-warningp*, as the following comment from ; David Rager suggests. But this now seems too difficult to justify that ; effort, and we do not feel obligated to do so; see the "IMPORTANT NOTE" in ; :doc parallelism. ; At one point, in an effort to fix printing in parallel from within ; wormholes, I tried rebinding the ld-specials. I now know that my approach ; at that time was doomed to fail, because these specials aren't implemented ; as global variables but instead as a setq of a variable in a completely ; different package. Now that I understand how state global variables work ; in ACL2, it may be worth coming back to this code and trying once again to ; inherit the ld-specials (listed in *initial-ld-special-bindings*). We ; could also consider binding *deep-gstack*, and it seems that we also should ; bind *wormhole-cleanup-form* since we bind *wormholep*, but we haven't done ; so. (closure (lambda () (let ((*ld-level* ,ld-level-sym) (*acl2-unwind-protect-stack* acl2-unwind-protect-stack) (*wormholep* ,wormholep-sym)) (state-free-global-let* ((ld-level ,ld-level-state-sym) (safe-mode ,local-safe-mode-sym) (guard-checking-on ,local-gc-on-sym)) ,x))))) (without-interrupts (let ((future (make-future-with-closure closure))) (without-interrupts ; probably not needed (add-future-to-queue future)) future))))))) (defun mt-future-read (future) (cond ((st-future-p future) (st-future-read future)) ((mt-future-p future) (when (not (barrier-value (mt-future-valid future))) ; Block until the value in the future is available (valid). (let ((notif nil)) (unwind-protect-disable-interrupts-during-cleanup (progn (without-interrupts (free-allocated-core) ; David Rager believes that at one time, he was concerned that the following ; atomic decrement may be broken under early termination, though he didn't see ; how. (atomic-decf *unassigned-and-active-future-count*) (setq notif t)) ; Although we are about to wait for the valid bit to be set, it's possible that ; (barrier-value (mt-future-valid future)) has become true by now, so maybe ; it's tempting to add such a test. However, the call of wait-on-barrier below ; is already doing such a test, and it would save little or no time to do it ; explicitly before calling wait-on-barrier. (wait-on-barrier (mt-future-valid future)) ; The following claiming isn't necessary under early termination, and in fact, ; under early termination, the claiming usually doesn't occur because it's in ; the unprotected part of the unwind-protect. It's unnecessary because if ; we're terminating early, we don't really care whether we "claim" the core. ; In fact, we actually prefer to leave the core unclaimed, because claiming the ; core would require blocking until we temporarily receive a semaphore signal. (claim-resumptive-core)) (when notif ; clean up (atomic-incf *unassigned-and-active-future-count*))))) (when (mt-future-thrown-tag future) (throw (car (mt-future-thrown-tag future)) (cdr (mt-future-thrown-tag future)))) (values-list (mt-future-value future))) (t (error "future-read was given a non-future argument")))) ; Debug variables: (defvar *aborted-futures-via-throw* 0) (defvar *almost-aborted-future-count* 0) (defun mt-future-abort (future) ; All calls to mt-future-abort must be made by the parent of the future being ; aborted. This gives us some notion of single-threadedness, which removes the ; need for locking in some of the code below. This means the aborting ; mechanism would not work for pand/por. ; We might consider relaxing the above precondition, but we would need to think ; first about whether we need to make corresponding changes, even beyond the ; definition of this function. For example, under the above precondition it is ; impossible to interrupt the current thread more than once. Without that ; precondition, we might have two such interrupts, both of which are executing ; code that potentially throws to tag :result-no-longer-needed. As things ; stand, we believe that probably only one such throw will actually take place, ; since the actual throw is conditionalized on a variable that is only true ; when a catcher is in place. Still, if we do indeed relax the above ; precondition, we should think this through carefully. ; But there is an even more serious problem with relaxing the precondition. ; Due to the reading and writing of slots in *future-dependencies*, only one ; thread should be able to create and abort its children. Otherwise those ; slots could become corrupt. ; This abortion is non-blocking; i.e., it will issue an abort and return. It ; doesn't know when the abort will actually occur. ; It's possible that future will be nil. This happens when the future has ; already finished execution and set its position in *future-array* to nil. (incf *aborted-futures-total*) (cond ((st-future-p future) (st-future-abort future)) ((mt-future-p future) ; Parallelism wart: consider deleting the comment just below. ; It doesn't make sense to abort a future that's really just a value. We ; assume that we can't return a future in a future... which may not be ; valid... ouch. We're probably okay, simply because an abort means the value ; doesn't matter. TODO: fix that maybe? (acl2::without-interrupts (let ((index (mt-future-index future))) (assert index) (setf (mt-future-aborted future) t) (let ((thread (faref *thread-array* index))) (when thread (interrupt-thread thread (lambda () (if (equal (mt-future-index future) *current-thread-index*) ; Parallelism wart: review the comment below, perhaps clarifying it, that ; explains how the equal test above could fail. David thinks it has to do with ; the six cases in how a future aborts computation. ; *Almost-aborted-future-count* can be incremented when the ; *current-thread-index* has fallen back to its default value, 0, for when the ; thread is unassociated with any future. It can also be incremented when the ; thread has already picked up a new piece of work, but in practice this latter ; occurrence will almost never happen. (when *throwable-future-worker-thread* (incf *aborted-futures-via-throw*) (throw :result-no-longer-needed nil)) (incf *almost-aborted-future-count*))))))))) ((null future) t) ; future already removed from the future-array (t (error "future-abort was given a non-future argument"))) future) (defun abort-future-indices (indices) ; Parallelism wart: clarify the following comment. ; Only call from future-abort, which has a theoretical read-lock on the value ; of indices (I quite literally mean "theoretical", as only by reasoning can we ; deduce that the source of the argument "indices", array ; *future-dependencies*, is not changing underneath us). (if (endp indices) t (progn (mt-future-abort (faref *future-array* (car indices))) (abort-future-indices (cdr indices))))) (defun print-non-nils-in-array (array n) (if (equal n (length array)) "end" (if (null (faref array n)) (print-non-nils-in-array array (1+ n)) (progn (print n) (print (faref array n)) (print-non-nils-in-array array (1+ n)))))) (defun futures-still-in-flight () ; Returns t if there are futures still in the work-queue to process or if there ; are futures already being processed. (< 1 (atomically-modifiable-counter-read *unassigned-and-active-future-count*))) ;--------------------------------------------------------------------- ; Section: Futures Interface (defmacro future (x) `(mt-future ,x)) (defun future-read (x) (mt-future-read x)) (defun future-abort (x) (mt-future-abort x)) (defun abort-futures (futures) (cond ((endp futures) t) (t (future-abort (car futures)) (abort-futures (cdr futures))))) acl2-sources/GNUmakefile0000664002132200015000000010100612222116056014620 0ustar kaufmannacl2# -*- Fundamental -*- # ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp # Copyright (C) 2013, Regents of the University of Texas # This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright # (C) 1997 Computational Logic, Inc. See the documentation topic NOTES-2-0. # This program is free software; you can redistribute it and/or modify # it under the terms of the LICENSE file distributed with ACL2. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # LICENSE for more details. # Written by: Matt Kaufmann and J Strother Moore # email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu # Department of Computer Science # University of Texas at Austin # Austin, TX 78701 U.S.A. # Example invocations for users: # make ; Build saved_acl2 from scratch. Same as make large. # make large ; Build large-saved_acl2 from scratch. # make LISP=cl PREFIX=allegro- # make LISP=lisp PREFIX=lucid- # make LISP='gcl -eval "(defparameter user::*fast-acl2-gcl-build* t)"' # ; Build in GCL, but with a shortcut that cuts the time by # ; perhaps 2/3 at the cost losing perhaps 1% in run-time # ; performance. # make LISP='gcl -eval "(push :acl2-mv-as-values *features*)"' # ; Build in GCL, with mv and mv-let defined in terms of # ; values and multiple-value-bind (respectively). # make LISP='acl -e "(push :acl2-mv-as-values *features*)"' # ; As above, but for Allegro CL. # make LISP='openmcl -e "(push :acl2-mv-as-values *features*)"' # ; As above, but for OpenMCL. # make LISP="lispworks -init /projects/acl2/devel/lispworks-init.lisp" PREFIX=lispworks- # ; Same as make, except that image is named # ; lispworks-saved_acl2 and the indicated init file is # ; loaded when lispworks is invoked during the build # ; Note from Rich Cohen: # ; The "-init -" tell Lispworks not to load the user's # ; initialisation file. By default Lispworks will load # ; ~/.lispworks at start-up, regardless of the current # ; working directory. Further, when you attempt to save a # ; core image, Lispworks notes that you previously loaded # ; your personal initialisation file, and requires # ; confirmation before saving the core image. # make TAGS ; Create tags table, handy for viewing sources with emacs. # make TAGS! ; Same as TAGS, except forces a rebuild of TAGS. # make certify-books # ; Certify the community books that might reasonably # ; be useful to include in proof developments. # make regression # ; Certify all the distributed books and, if present, the # ; workshops/ books as well. # make regression ACL2=xxx # ; Same as make regression, but use xxx as ACL2, which # ; should either be an absolute filename or a command on # ; one's path. # make regression ACL2_CUSTOMIZATION=xxx # ; Same as make regression, but use xxx as the # ; ACL2 customization file (see :DOC acl2-customization). # ; In particular, this is useful for certifying # ; the books in the regression suite using # ; waterfall parallelism (requires the # ; experimental extension ACL2(p) of ACL2); see # ; file acl2-customization-files/README. # make regression-everything # ; Same as make regression, except that target "everything" # ; is used in community books file, Makefile. # make clean-books ; Remove certificate files, object files, log files, # ; debris, ..., created by `make certify-books', # ; `make regression', etc. ############################################################################### # NOTE: Users need not read below this line. Neither should installers of # ACL2 at sites other than CLI. We have no reason to believe that the make # commands illustrated below will work at sites other than CLI. Indeed, we # have reasons to believe they will not! A typical problem is that we refer # to a file or directory that exists at CLI but that is not created when our # installation instructions are followed at other sites. # Example invocations for CLI implementors: # NOTE: Make large completely recompiles, initializes and # saves. # make full ; A complete recompilation whether needed or not. # make full init ; Completely recompile, initialize and save. # make full-meter init ; Completely recompile with meters, init and save. # make init ; Just build full-size saved_acl2. # make check-sum ; Call only after ACL2 is completely compiled. # make full LISP=lucid PREFIX=lucid- ; makes acl2 in Lucid # make full LISP=cl PREFIX=allegro- ; makes acl2 in allegro # ; Note: Allegro is not always named cl at CLI. See # ; ~moore/allegro/runcl for some clues. # make full LISP=lispworks PREFIX=lispworks- ; makes acl2 in lispworks # make copy-distribution DIR=/stage/ftp/pub/moore/acl2/v2-9/acl2-sources # ; copies all of acl2 plus books, doc, etc., to the named # ; directory, as for compiling on another architecture or # ; moving to the ftp site. # ; Preconditions: # ; (1) The named directory must not already exist; if it # ; does, a harmless error is caused. # ; (2) acl2-book must be gzipped, i.e., if necessary first do # gzip /projects/acl2/v2-9/doc/TEX/acl2-book.ps # gzip /projects/acl2/v2-9/doc/TEX/acl2-book.dvi # make DOC ; Build html and emacs info files # make clean-doc ; Remove files created by make DOC # make red ; Just build full-size saved_acl2, but do so without pass 2 # make proofs ; Assuming sources are compiled, initialize without skipping # ; proofs during pass 2. Does not save an image. Uses same # ; flags used to build full-size image. # Metering: If the currently compiled version is unmetered and you # wish it metered, the fastest thing to do is to (push :acl2-metering # *features*) and then yank in and recompile just those definitions # that mention acl2-metering. However, if you would like to install # metering as part of a system-wide recompilation, use the full-meter # option below. If you want to get rid of the metering in the # compiled code, do make full. LISP = ccl DIR = /tmp # The variable NONSTD should be defined for the non-standard version and not # for the standard version. Non-standard ACL2 images will end in saved_acl2r # rather than saved_acl2. ACL2_HONS, ACL2_PAR, ACL2_DEVEL, and ACL2_WAG (for # feature write-arithmetic-goals) are similar (with suffixes h, # p, d, and w, resp., rather than r), but for the experimental hons and # parallel versions and the feature that writes out arithmetic lemma data to # ~/write-arithmetic-goals.lisp (surely only of interest to implementors!). # DO NOT EDIT ACL2_SUFFIX! Edit the above-mentioned three variables instead. ACL2_SUFFIX := ifdef ACL2_HONS ACL2_SUFFIX := $(ACL2_SUFFIX)h endif ifdef ACL2_PAR ACL2_SUFFIX := $(ACL2_SUFFIX)p endif ifdef ACL2_WAG ACL2_SUFFIX := $(ACL2_SUFFIX)w endif ifdef ACL2_DEVEL ACL2_SUFFIX := $(ACL2_SUFFIX)d endif ifdef NONSTD ACL2_SUFFIX := $(ACL2_SUFFIX)r endif # The following variable, ACL2_PROCLAIMS_ACTION, is intended to be # user-settable to one of three values, as shown below. By default, # it avoids consideration of file acl2-proclaims.lisp: that file is # neither consulted (i.e., reused) nor generated. ifndef ACL2_PROCLAIMS_ACTION # Default action: Do not reuse or build acl2-proclaims.lisp. ACL2_PROCLAIMS_ACTION := default # Use the existing acl2-proclaims.lisp for compilation. # ACL2_PROCLAIMS_ACTION ?= reuse # Do compile/initialize twice, in order to build acl2-proclaims.lisp # and then consult it before the second compile. # ACL2_PROCLAIMS_ACTION ?= generate_and_reuse endif # Variable ACL2_PROCLAIMS_ACTION is not to be set by the user. We use # override directives to ensure this. ifeq ($(ACL2_PROCLAIMS_ACTION), reuse) USE_ACL2_PROCLAIMS := t else USE_ACL2_PROCLAIMS := endif # The user may define PREFIX; otherwise it is implicitly the empty string. PREFIX = PREFIXsaved_acl2 = ${PREFIX}saved_acl2${ACL2_SUFFIX} PREFIXosaved_acl2 = ${PREFIX}osaved_acl2${ACL2_SUFFIX} # One may define ACL2_SAFETY to provide a safety setting. We recommend # ACL2_SAFETY = 3 # for careful error checking. This can cause significant slowdown and for # some Lisp implementations, the regression might not even complete. For # CCL we have had success with safety 3. ACL2_SAFETY = # Set ACL2_COMPILER_DISABLED, say with ACL2_COMPILER_DISABLED=t, to # build the image with (SET-COMPILER-ENABLED NIL STATE), thus # disabling use of the compiler for certify-book and include-book; see # :DOC compilation. This is generally not necessary, but for the use # of some books employing raw Lisp code it could, on rare occasion, be # useful; and for SBCL and CCL (as of this writing, May 2010), # reasonably harmless other than to lose a bit of speed when including # books with many complex defun forms. ACL2_COMPILER_DISABLED = # The following is not advertised. It allows more symbol allocation # when ACL2 package is created; if specified, its value should be a # number to supply for the :size argument of defpackage. For example, # 3000000 has been found a useful such value for a use of the HONS # version of ACL2 built on CCL on a 64-bit machine. ACL2_SIZE = # The following causes the calls of make that use it to continue past # errors. Delete -k if you want to stop at first error and return # non-zero exit status in that case; or, instead of editing the line # below, supply ACL2_IGNORE='' on the make command line. Formerly we # used -i; if you prefer that, use ACL2_IGNORE=-i on the command line. # Note however that the GNU make manual # (http://www.gnu.org/software/make/manual/make.html, May 2013) says # that -i is "obsolete". ACL2_IGNORE ?= -k # The order of the files below is unimportant. sources := axioms.lisp memoize.lisp hons.lisp boot-strap-pass-2.lisp\ basis.lisp parallel.lisp translate.lisp\ type-set-a.lisp linear-a.lisp\ type-set-b.lisp linear-b.lisp\ non-linear.lisp tau.lisp\ rewrite.lisp simplify.lisp bdd.lisp\ other-processes.lisp induct.lisp prove.lisp\ proof-checker-a.lisp history-management.lisp defuns.lisp defthm.lisp\ other-events.lisp ld.lisp proof-checker-b.lisp interface-raw.lisp\ serialize.lisp serialize-raw.lisp\ defpkgs.lisp ifdef ACL2_HONS sources := $(sources) hons-raw.lisp memoize-raw.lisp endif ifdef ACL2_PAR sources := $(sources) multi-threading-raw.lisp parallel-raw.lisp futures-raw.lisp endif # No change to sources for ACL2_DEVEL or ACL2_WAG # Top target: all: large .PHONY: acl2r acl2r: rm -f acl2r.lisp $(MAKE) acl2r.lisp acl2r.lisp: # The following is important if we sometimes build for GCL on Linux and # sometimes on Unix. rm -f acl2-fns.o echo "" > acl2r.lisp if [ "$(NONSTD)" != "" ] ; then \ echo '(or (member :non-standard-analysis *features*) (push :non-standard-analysis *features*))' >> acl2r.lisp ;\ fi if [ "$(ACL2_HONS)" != "" ] ; then \ echo '(or (member :hons *features*) (push :hons *features*))' >> acl2r.lisp ;\ fi if [ "$(ACL2_HONS)" = "h_hack" ] ; then \ echo '(or (member :memoize-hack *features*) (push :memoize-hack *features*))' >> acl2r.lisp ;\ fi if [ "$(ACL2_PAR)" != "" ] ; then \ echo '(or (member :acl2-par *features*) (push :acl2-par *features*))' >> acl2r.lisp ;\ fi if [ "$(ACL2_DEVEL)" != "" ] ; then \ echo '(or (member :acl2-devel *features*) (push :acl2-devel *features*))' >> acl2r.lisp ;\ fi if [ "$(ACL2_WAG)" != "" ] ; then \ mv -f ~/write-arithmetic-goals.lisp.old ; \ mv -f ~/write-arithmetic-goals.lisp ~/write-arithmetic-goals.lisp.old ; \ echo '(or (member :write-arithmetic-goals *features*) (push :write-arithmetic-goals *features*))' >> acl2r.lisp ;\ fi if [ "$(ACL2_SAFETY)" != "" ] ; then \ echo "(defparameter *acl2-safety* $(ACL2_SAFETY))" >> acl2r.lisp ;\ fi if [ "$(ACL2_SIZE)" != "" ] ; then \ echo '(or (find-package "ACL2") (#+(and gcl (not ansi-cl)) defpackage:defpackage #-(and gcl (not ansi-cl)) defpackage "ACL2" (:size $(ACL2_SIZE)) (:use)))' >> acl2r.lisp ;\ fi if [ "$(ACL2_COMPILER_DISABLED)" != "" ] ; then \ echo '(DEFPARAMETER *ACL2-COMPILER-ENABLED* NIL)' >> acl2r.lisp ;\ fi .PHONY: chmod_image chmod_image: if [ -f ${PREFIXsaved_acl2} ]; then \ chmod 775 ${PREFIXsaved_acl2} ;\ fi .PHONY: do_saved # Note: Removed line "chmod g+s saved" before the second chmod below, as it was # causing errors in at least one environment, and instead did final chmod to # 666 instead of 664 in case files in saved/ wind up in an unexpected group. do_saved: rm -fr saved mkdir saved chmod 775 saved cp *.lisp saved chmod 666 saved/*.lisp # Keep the use of :COMPILED below in sync with ACL2 source function # note-compile-ok. .PHONY: check_compile_ok check_compile_ok: @if [ ! -f acl2-status.txt ] ; then \ echo 'Compile FAILED: file acl2-status.txt is missing.' ; \ exit 1 ; \ fi @if [ `cat acl2-status.txt` != :COMPILED ] ; then \ echo 'Compile FAILED: acl2-status.txt should contain :COMPILED.' ; \ exit 1 ; \ fi # Keep the use of :INITIALIZED below in sync with ACL2 source function # initialize-acl2. .PHONY: check_init_ok check_init_ok: @if [ ! -f acl2-status.txt ] ; then \ echo 'Initialization FAILED: file acl2-status.txt is missing.' ; \ exit 1 ; \ fi @if [ `cat acl2-status.txt` != :INITIALIZED ] ; then \ echo 'Initialization FAILED: acl2-status.txt should contain :INITIALIZED.' ; \ exit 1 ; \ fi @echo "Initialization SUCCEEDED." .PHONY: compile-ok compile-ok: date rm -f workxxx echo '(load "init.lisp")' > workxxx echo '(acl2::note-compile-ok)' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx rm -f workxxx .PHONY: check-sum check-sum: date rm -f workxxx echo '(load "init.lisp") (acl2)' > workxxx echo '(acl2::make-check-sum-file)' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx rm -f workxxx .PHONY: full full: TAGS! rm -f workxxx echo '(load "init.lisp")' > workxxx echo '(acl2::compile-acl2 $(USE_ACL2_PROCLAIMS))' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx @$(MAKE) check_compile_ok rm -f workxxx .PHONY: full-meter full-meter: date rm -f workxxx echo '(load "init.lisp")' > workxxx echo '(acl2::make-tags)' >> workxxx echo '(push :acl2-metering *features*)' >> workxxx echo '(acl2::compile-acl2)' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx @$(MAKE) check_compile_ok rm -f workxxx .PHONY: copy-distribution copy-distribution: # WARNING: Execute this from an ACL2 source directory. # You must manually rm -r ${DIR} before this or it will fail without doing # any damage. # Note that below, /projects/acl2/ is not used, because this directory must # match what lisp returns from truename. rm -f workxxx rm -f workyyy rm -f acl2r.lisp echo '(load "init.lisp")' > workxxx echo '(acl2::copy-distribution "workyyy" "${CURDIR}" "${DIR}")' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx chmod 777 workyyy ./workyyy rm -f workxxx rm -f workyyy # You can replace the block of code below if etags doesn't exist on your system, by # removing "#" on the two lines just below and commenting out the block below # them. However, since Lisp function make-tags deals with this issue, such a # change is probably not necessary. #TAGS: # @echo 'Skipping building of a tags table.' TAGS: acl2.lisp acl2-check.lisp acl2-fns.lisp acl2-init.lisp ${sources} rm -f TAGS rm -f workxxx echo '(load "init.lisp")' > workxxx echo '(acl2::make-tags)' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx rm -f workxxx if [ -f TAGS ] ; then chmod 644 TAGS ; fi # The following remakes TAGS even if TAGS is up to date. This target can be # useful when building a hons or parallel version after a normal version, or # vice-versa. .PHONY: TAGS! TAGS!: acl2r rm -f TAGS $(MAKE) TAGS .PHONY: move-to-old move-to-old: if [ -f ${PREFIXsaved_acl2} ] && [ -f ${PREFIXsaved_acl2}.${LISPEXT} ]; then \ echo "Moving " ${PREFIXsaved_acl2}.${LISPEXT} " to ${PREFIXosaved_acl2}.${LISPEXT}"; \ mv -f ${PREFIXsaved_acl2}.${LISPEXT} ${PREFIXosaved_acl2}.${LISPEXT}; \ cat ${PREFIXsaved_acl2} | sed -e 's/saved_acl2${ACL2_SUFFIX}.${LISPEXT}$$/osaved_acl2${ACL2_SUFFIX}.${LISPEXT}/' > ${PREFIXosaved_acl2} ;\ chmod 775 ${PREFIXosaved_acl2} ;\ rm -f ${PREFIXsaved_acl2} ; fi .PHONY: move-new move-new: if [ -f nsaved_acl2.${LISPEXT} ]; then \ mv -f nsaved_acl2.${LISPEXT} ${PREFIXsaved_acl2}.${LISPEXT} ; fi # See Section "PROCLAIMING" in acl2-fns.lisp. acl2-proclaims.lisp: ${sources} rm -f acl2-proclaims.lisp rm -f workxxx rm -f worklispext echo '(load "init.lisp")' > workxxx echo '(in-package "ACL2")' >> workxxx echo '(generate-acl2-proclaims)' >> workxxx echo '(exit-lisp)' >> workxxx ${LISP} < workxxx [ -f acl2-proclaims.lisp ] # If ACL2_PROCLAIMS_ACTION has value generate_and_reuse, then the # following target remakes acl2-proclaims.lisp and recompiles, as # follows. The first subsidiary call of $(MAKE) initializes, i.e., # gets ACL2 to LD its own sources. Then, a second $(MAKE) uses that # file of proclaim forms to recompile. # See also the Section "PROCLAIMING" in acl2-fns.lisp. .PHONY: make-acl2-proclaims make-acl2-proclaims: if [ "$(ACL2_PROCLAIMS_ACTION)" = "generate_and_reuse" ]; then \ $(MAKE) acl2-proclaims.lisp; \ $(MAKE) full ACL2_PROCLAIMS_ACTION=reuse; \ fi .PHONY: init init: make-acl2-proclaims # Note: If you believe that compilation is up-to-date, do # make compile-ok init # rather than # make init. rm -f workxxx rm -f worklispext echo -n "" >> ${PREFIXosaved_acl2} rm -f ${PREFIXosaved_acl2} echo '(load "init.lisp")' > workxxx echo '(in-package "ACL2")' >> workxxx echo '(save-acl2 (quote (initialize-acl2 (quote include-book) acl2::*acl2-pass-2-files*)) "${PREFIXsaved_acl2}")' >> workxxx echo '(exit-lisp)' >> workxxx ${LISP} < workxxx @$(MAKE) check_init_ok # Move to old. if [ -f worklispext ]; then $(MAKE) move-to-old LISPEXT=`cat worklispext` ;\ elif [ -f ${PREFIXsaved_acl2} ]; then \ mv -f ${PREFIXsaved_acl2} ${PREFIXosaved_acl2} ;\ else \ touch ${PREFIXsaved_acl2} ;\ mv -f ${PREFIXsaved_acl2} ${PREFIXosaved_acl2} ;\ fi # End of move to old. # Move new into position. mv -f nsaved_acl2 ${PREFIXsaved_acl2} # For Allegro 5.0 and later and cmulisp, only: if [ -f worklispext ]; then $(MAKE) move-new LISPEXT=`cat worklispext` ; fi # End of move new into position. rm -f worklispext rm -f workxxx $(MAKE) do_saved rm -f workxxx $(MAKE) chmod_image # The following "proofs" target assumes that files for the specified LISP have # been compiled. .PHONY: proofs proofs: compile-ok rm -f workxxx echo '(load "init.lisp")' > workxxx echo '(acl2::load-acl2)' >> workxxx echo '(acl2::initialize-acl2 nil acl2::*acl2-pass-2-files*)' >> workxxx echo '(acl2::exit-lisp)' >> workxxx ${LISP} < workxxx @$(MAKE) check_init_ok rm -f workxxx .PHONY: DOC HTML EMACS_TEX EMACS_ONLY STATS DOC: HTML EMACS_TEX STATS # Use ACL2_DOC_UNDOCUMENTED_FILE if you want to support broken links # (by having them point to a page acknowledging that the link is # broken, rather than by having doc/create-acl2-html simply fail). # Note that this only works for the HTML target, not for the EMACS_TEX # or EMACS_ONLY targets. HTML: @if [ "$(ACL2)" = "" ]; then \ ACL2="../../$(PREFIX)saved_acl2$(ACL2_SUFFIX)" ;\ export ACL2 ;\ doc/create-acl2-html ;\ else \ ACL2=$(ACL2) ;\ export ACL2 ;\ doc/create-acl2-html ;\ fi # Note: doc/create-acl2-tex builds a ps file, so depends on texi2dvi # and dvips. These might not be present on some systems (but is # present at UT CS and have been seen to be present on a Mac where # Latex is installed). Use EMACS_ONLY instead of EMACS_TEX if you # want to avoid this issue. # Note that doc/create-acl2-texinfo certifies doc/write-acl2-texinfo, # but (for efficiency) doc/create-acl2-tex does not. EMACS_TEX: @if [ "$(ACL2)" = "" ]; then \ ACL2="./$(PREFIX)saved_acl2$(ACL2_SUFFIX)" ;\ export ACL2 ;\ doc/create-acl2-texinfo ; doc/create-acl2-tex ;\ else \ ACL2=$(ACL2) ;\ export ACL2 ;\ doc/create-acl2-texinfo ; doc/create-acl2-tex ;\ fi EMACS_ONLY: @if [ "$(ACL2)" = "" ]; then \ ACL2="./$(PREFIX)saved_acl2$(ACL2_SUFFIX)" ;\ export ACL2 ;\ doc/create-acl2-texinfo ;\ else \ ACL2=$(ACL2) ;\ export ACL2 ;\ doc/create-acl2-texinfo ;\ fi # See the Essay on Computing Code Size in the ACL2 source code. STATS: @if [ "$(ACL2)" = "" ]; then \ ACL2="../$(PREFIX)saved_acl2$(ACL2_SUFFIX)" ;\ export ACL2 ;\ ACL2_SOURCES="$(sources)" ;\ export ACL2_SOURCES ;\ doc/create-acl2-code-size ;\ else \ ACL2=$(ACL2) ;\ export ACL2 ;\ ACL2_SOURCES="$(sources)" ;\ export ACL2_SOURCES ;\ doc/create-acl2-code-size ;\ fi .PHONY: clean clean: # Does not remove executable or corresponding scripts # (since there could be many executables that one prefers not to delete), # except for *osaved_acl2* files. rm -f *.o *#* *.c *.h *.data gazonk.* workxxx workyyy *.lib \ *.fasl *.fas *.sparcf *.ufsl *.64ufasl *.ufasl *.dfsl *.dxl \ *.d64fsl *.dx64fsl *.lx64fsl \ *.lx32fsl *.x86f *.o \ TAGS acl2-status.txt acl2r.lisp acl2-proclaims.lisp .acl2rc \ *osaved_acl2 *osaved_acl2.* \ *.log TMP* rm -rf saved rm -f doc/*.o doc/*#* doc/*.c doc/*.h doc/*.data doc/gazonk.* \ doc/workxxx doc/workyyy doc/*.lib \ doc/*.fasl doc/*.fas doc/*.sparcf doc/*.ufsl doc/*.64ufasl doc/*.ufasl doc/*.dfsl \ doc/*.d64fsl doc/*.dx64fsl doc/*.lx64fsl \ doc/*.lx32fsl doc/*.x86f doc/*.o \ doc/*.cert doc/*.out \ doc/*.log doc/TMP* rm -rf doc/TEX doc/HTML doc/EMACS .PHONY: red red: compile-ok rm -f workxxx echo '(load "init.lisp")' > workxxx echo '(in-package "ACL2")' >> workxxx echo '(save-acl2 (quote (initialize-acl2 nil nil)))' >> workxxx echo '(exit-lisp)' >> workxxx ${LISP} < workxxx @$(MAKE) check_init_ok echo -n "" >> red-${PREFIXsaved_acl2} # Note: This needs to be updated for Allegro 5.0 and cmulisp if we decide to # use it. See init. mv -f red-${PREFIXsaved_acl2} red-${PREFIXosaved_acl2} mv -f nsaved_acl2 red-${PREFIXsaved_acl2} rm -f workxxx $(MAKE) do_saved # The .NOTPARALLEL target avoids our doing any build process in # parallel. Uses of makefiles in other directories, even if invoked # from this makefile, can still take advantage of -j (as per the GNU # make documentation). .NOTPARALLEL: .PHONY: large large: acl2r full init .PHONY: large-acl2r large-acl2r: $(MAKE) large NONSTD=r .PHONY: large-acl2h large-acl2h: $(MAKE) large ACL2_HONS=h .PHONY: large-acl2d large-acl2d: $(MAKE) large ACL2_DEVEL=d .PHONY: large-acl2p large-acl2p: $(MAKE) large ACL2_PAR=p # Since ACL2_WAG is for implementors only, we don't bother making a # target for it. Instead one just uses ACL2_WAG=w on the "make" # command line. # Note that move-large may not have the desired effect for Allegro/CMUCL/SBCL # images, because "large-" will not have been written to the core file name in # ${PREFIXsaved_acl2}. .PHONY: move-large move-large: mv ${PREFIXsaved_acl2} large-${PREFIXsaved_acl2} if [ -f worklispext ]; then \ mv ${PREFIXsaved_acl2}.`cat worklispext ` large-${PREFIXsaved_acl2}.`cat worklispext` ;\ fi # Certify books that are not up-to-date, but only those that might reasonably # be useful to include in proof developments. # NOTE: None of the book certification targets use PREFIX. They use # "acl2" by default, but the ACL2 executable can be specified on the command # line with ACL2=. # Success can generally be determined by checking for the absence of ** in the # log. .PHONY: certify-books certify-books: ifndef ACL2 cd books ; $(MAKE) $(ACL2_IGNORE) certify-books ACL2=$(shell pwd)/saved_acl2 else cd books ; $(MAKE) $(ACL2_IGNORE) certify-books ACL2=$(ACL2) endif # Certify books that are not up-to-date, even those less likely to be included # in other books. This does *not* certify the nonstd/ books. It would be # awkward to arrange for that here, because the ACL2 images are different; # they might not even have the same prefix. See target regression-nonstd. # Success can generally be determined by checking for the absence of ** in the # log, or by looking at the Unix exit status. .PHONY: regression regression: uname -a ifndef ACL2 cd books ; $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2 else cd books ; $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) endif .PHONY: regression-everything regression-everything: uname -a ifndef ACL2 cd books ; $(MAKE) $(ACL2_IGNORE) everything ACL2=$(shell pwd)/saved_acl2 else cd books ; $(MAKE) $(ACL2_IGNORE) everything ACL2=$(ACL2) endif .PHONY: regression-nonstd regression-nonstd: uname -a ifndef ACL2 cd books/nonstd ; \ $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2r else cd books/nonstd ; \ $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) endif # Certify main books from scratch. .PHONY: certify-books-fresh certify-books-fresh: clean-books $(MAKE) $(ACL2_IGNORE) certify-books # Do regression tests from scratch. # Success can generally be determined by checking for the absence of ** in the # log. .PHONY: regression-fresh regression-fresh: clean-books ifndef ACL2 $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2 regression else $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) regression endif .PHONY: regression-everything-fresh regression-everything-fresh: clean-books ifndef ACL2 $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2 regression-everything else $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) regression-everything endif .PHONY: regression-nonstd-fresh regression-nonstd-fresh: clean-books-nonstd ifndef ACL2 $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2r regression-nonstd else $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) regression-nonstd endif # The following allows for a relatively short test, in response to a request # from GCL maintainer Camm Maguire. .PHONY: certify-books-short certify-books-short: uname -a ifndef ACL2 cd books ; \ $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2 short-test else cd books ; \ $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) short-test endif # The following target assumes that we are using an image built with # ACL2_DEVEL set. .PHONY: devel-check devel-check: @counter=0 ; \ while [ t ] ;\ do \ echo "(chk-new-verified-guards $$counter) ..." ;\ echo "(chk-new-verified-guards $$counter)" > workxxx.devel-check ;\ $(ACL2) < workxxx.devel-check > devel-check.out ;\ if [ "`fgrep CHK-NEW-VERIFIED-GUARDS-COMPLETE devel-check.out`" ] ; then \ rm -f workxxx.devel-check devel-check.out ;\ echo 'SUCCESS for devel-check' ;\ exit 0 ;\ fi ;\ if [ "`fgrep CHK-NEW-VERIFIED-GUARDS-SUCCESS devel-check.out`" ] ; then \ rm -f workxxx.devel-check devel-check.out ;\ counter=`expr $$counter + 1` ;\ else \ echo '**FAILED** devel-check: output log follows:' ;\ cat devel-check.out ;\ rm -f workxxx.devel-check devel-check.out ;\ exit 1 ;\ fi \ done .PHONY: clean-doc clean-doc: cd doc ; rm -f *.o *~* *#* TAGS *.c *.h *.data gazonk.* workxxx \ *.lbin *.sbin *.fasl *.wfasl *.fas *.lib *.sparcf *.ufsl *.64ufasl *.ufasl *.x86f *.dfsl *.fn *.cert rm -rf doc/EMACS rm -rf doc/EMACS-old/ rm -rf doc/HTML rm -rf doc/HTML-old rm -rf doc/TEX .PHONY: clean-books clean-books: ifndef ACL2 cd books ; $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2 moreclean else cd books ; $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) moreclean endif .PHONY: clean-books-nonstd clean-books-nonstd: ifndef ACL2 cd books/nonstd ; \ $(MAKE) $(ACL2_IGNORE) ACL2=$(shell pwd)/saved_acl2 clean clean-links else cd books/nonstd ; \ $(MAKE) $(ACL2_IGNORE) ACL2=$(ACL2) clean clean-links endif # This following should be executed inside the acl2-sources directory. # You probably need to be the owner of all files in order for the chmod # commands to work, but perhaps not. # Keep tar in sync with tar-workshops and tar-nonstd. .PHONY: tar tar: rm -f acl2.tar.Z acl2.tar.gz acl2.tar rm -f SUM # We want the extracted tar files to have permission for everyone to write, # so that when they use -p with tar they get that permission. # But we don't want the tar file itself to have that permission. We may as # well protect all the other files too from writing by CLI people other than # those in the acl2 group, even though these files aren't the ones transferred # to the ftp server. Those come from the tar file, and we will extract them # without the -p option so that the ftp files will not be world-writable. cd .. ; chmod -R g+r acl2-sources ; chmod -R o+r acl2-sources ; tar cvf /tmp/acl2.tar acl2-sources ; chmod -R o-w acl2-sources mv /tmp/acl2.tar . gzip acl2.tar md5sum acl2.tar.gz > acl2-tar-gz-md5sum # Keep tar-workshops in sync with tar and tar-nonstd. # This target should be executed in the acl2-sources directory. .PHONY: tar-workshops tar-workshops: cd books ; rm -f workshops.tar.Z workshops.tar.gz workshops.tar workshops-tar-gz-md5sum cd books ; chmod -R g+r workshops ; chmod -R o+r workshops ; tar cvf /tmp/workshops.tar workshops ; chmod -R o-w workshops mv /tmp/workshops.tar books/ cd books ; gzip workshops.tar cd books ; (md5sum workshops.tar.gz > workshops-tar-gz-md5sum) # Keep tar-nonstd in sync with tar and tar-workshops. # This target should be executed in the acl2-sources directory. .PHONY: tar-nonstd tar-nonstd: cd books ; rm -f nonstd.tar.Z nonstd.tar.gz nonstd.tar nonstd-tar-gz-md5sum cd books ; chmod -R g+r nonstd ; chmod -R o+r nonstd ; tar cvf /tmp/nonstd.tar nonstd ; chmod -R o-w nonstd mv /tmp/nonstd.tar books/ cd books ; gzip nonstd.tar cd books ; (md5sum nonstd.tar.gz > nonstd-tar-gz-md5sum) .PHONY: mini-proveall mini-proveall: @rm -rf mini-proveall.out @echo '(value :q) (lp) (mini-proveall)' | ./${PREFIXsaved_acl2} > mini-proveall.out @(grep '^ "Mini-proveall completed successfully."' mini-proveall.out > /dev/null) || \ (echo 'Mini-proveall failed!' ; ls -l ./${PREFIXsaved_acl2}; cat mini-proveall.out ; exit 1) @echo 'Mini-proveall passed.' # Target for making an Allegro CL application acl2.exe in a new "bin/" subdirectory. # NOTE: An Allegro CL dynamic runtime license is needed in order for this to work. # Also, a file our-develenv.cl is needed in this (the ACL2 sources) directory. As # explained in file build-allegro-exe.cl: # [File our-develenv.cl] is obtained from a path such as # /AllegroCL-7.0/acl70/develenv.cl, then commenting out those # not allowed in runtime images, as suggested in the above file. ACL2_BIN_DIR = bin .PHONY: allegro-app allegro-app: our-develenv.cl @if [ -L ${PREFIXsaved_acl2} ]; then \ echo "Note: removing link ${PREFIXsaved_acl2}"; \ rm -f ${PREFIXsaved_acl2}; \ elif [ -f ${PREFIXsaved_acl2} ]; then \ echo "ERROR: Please move or remove ${PREFIXsaved_acl2} first."; \ exit 1; \ fi @if [ -d "${ACL2_BIN_DIR}" ]; then \ echo "ERROR: Please remove the ${ACL2_BIN_DIR}/ subdirectory."; \ exit 1; \ fi $(MAKE) full rm -f workxxx echo '(generate-application "acl2.exe" "${ACL2_BIN_DIR}/" (quote ("build-allegro-exe.cl")) :runtime :dynamic :include-compiler t) (exit)' > workxxx ${LISP} < workxxx rm -f workxxx @echo "Creating link from ${PREFIXsaved_acl2} to ${ACL2_BIN_DIR}/acl2.exe ." ln -s ${ACL2_BIN_DIR}/acl2.exe ${PREFIXsaved_acl2} # Support for target allegro-app: our-develenv.cl: @echo "ERROR:" @echo " Please create file our-develenv.cl. This may be obtained by" @echo " copying a file /AllegroCL-7.0/acl70/develenv.cl," @echo " and then commenting out those forms not allowed in runtime" @echo " images, as suggested in the that file." exit 1 # Developer target only. WARNING: Be sure to run "make regression" # first! We could add a dependency on regression, but maybe there # will be some case in which we know part of the regression fails but # we want to run this anyhow and it would get in the way to have a # regression failure (though I don't know how that might happen). .PHONY: chk-include-book-worlds chk-include-book-worlds: uname -a ifndef ACL2 cd books ; $(MAKE) $(ACL2_IGNORE) chk-include-book-worlds ACL2=$(shell pwd)/saved_acl2 else cd books ; $(MAKE) $(ACL2_IGNORE) chk-include-book-worlds ACL2=$(ACL2) endif acl2-sources/history-management.lisp0000664002132200015000000371063312222115527017273 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; Section: Proof Trees ; We develop proof trees in this file, rather than in prove.lisp, because ; print-summary calls print-proof-tree. ; A goal tree is a structure of the following form, with the fields indicated ; below. We put the two non-changing fields at the end; note: ; ACL2 p>:sbt 4 ; ; The Binary Trees with Four Tips ; 2.000 ((2 . 2) 2 . 2) ; 2.250 (1 2 3 . 3) (defrec goal-tree (children processor cl-id . fanout) nil) ; Cl-id is a clause-id record for the name of the goal. ; Children is a list of goal trees whose final cdr is either nil or a positive ; integer. In the latter case, this positive integer indicates the remaining ; number of children for which to build goal trees. ; Fanout is the original number of children. ; Processor is one of the processors from *preprocess-clause-ledge* (except for ; settled-down-clause, which has no use here), except that we have two special ; annotations and two "fictitious" processors. ; Instead of push-clause, we use (push-clause cl-id), where cl-id is the ; clause-id of the clause pushed (e.g., the clause-id corresponding to "*1"). ; Except: (push-clause cl-id :REVERT) is used when we are reverting to the ; original goal, and in this case, cl-id always corresponds to *1; also, ; (push-clause cl-id :ABORT) is used when the proof is aborted by push-clause. ; Instead of a processor pr, we may have (pr :forced), which indicates that ; this processor forced assumptions (but remember, some of those might get ; proved during the final clean-up phase). When we enter the next forcing ; round, we will "decorate" the above "processor" by adding a list of new goals ; created by that forcing: (pr :forced clause-id_1 ... clause-id_n). As we go ; along we may prune away some of those new clause ids. ; Finally, occasionally the top-level node in a goal-tree is "fictitious", such ; as the one for "[1]Goal" if the first forcing round presented more than one ; forced goal, and such as any goal to be proved by induction. In that case, ; the "processor" is one of the keyword labels :INDUCT or :FORCING-ROUND or a ; list headed by such keywords, e.g. if we want to say what induction scheme is ; being used. ; A proof tree is simply a non-empty list of goal trees. The "current" goal ; tree is the CAR of the current proof tree; it's the one for the current ; forcing round or proof by induction. ; There is always a current proof tree, (@ proof-tree), except when we are ; inhibiting proof-tree output or are not yet in a proof. The current goal in ; a proof is always the first one associated with the first subtree of the ; current goal-tree that has a non-nil final CDR, via a left-to-right ; depth-first traversal of that tree. We keep the proof tree pruned, trimming ; away proved subgoals and their children. ; The proof tree is printed to the screen, enclosed in #\n\<0 ... #\n\>. We ; start with # because that seems like a rare character, and we want to leave ; emacs as unburdened as possible in its use of string-matching. And, we put a ; newline in front of \ because in ordinary PRINT-like (as opposed to ; PRINC-like) printing, as done by the prover, \ is always quoted and hence ; would not appear in a sequence such as \?, where ? is any character ; besides \. Naturally, this output can be inhibited, simply by putting ; 'proof-tree on the state global variable inhibit-output-lst. Mike Smith has ; built, and we have modified, a "filter" tool for redirecting such output in a ; nice form to appropriate emacs buffers. People who do not want to use the ; emacs facility (or some other display utility) should probably inhibit ; proof-tree output using :stop-proof-tree. (deflabel proof-tree :doc ":Doc-Section Proof-tree proof tree displays~/ A view of ACL2 proofs may be obtained by way of ``proof tree displays,'' which appear in proof output (~pl[proofs-co]) when proof-tree output is enabled (see below) When ACL2 starts a proof and proof-tree output is enabled, the proof output begins with the following string. ~bv[] << Starting proof tree logging >> ~ev[] Then for each goal encountered during the proof, a corresponding proof tree display (as described below) is printed into the proof output: first the characters in the constant string ~c[*proof-tree-start-delimiter*] are printed, then the proof tree display, and finally the characters in the constant string ~c[*proof-tree-end-delimiter*]. External tools may present proof tree displays in a separate window. In particular, a tool distributed with the ACL2 community books customizes the emacs environment to provide window-based proof tree displays together with commands for traversing the proof transcript; see the discussion of ``ACL2 proof-tree support'' in file ~c[emacs/emacs-acl2.el] distributed with ACL2. The command ~c[:start-proof-tree] enables proof-tree output, while ~c[:stop-proof-tree] disables proof-tree output; ~pl[start-proof-tree] and ~pl[stop-proof-tree].~/ Here is an example of a proof tree display, with comments. Lines marked with ``c'' are considered ``checkpoints,'' i.e., goals whose scrutiny may be of particular value. ~bv[] ( DEFTHM PLUS-TREE-DEL ...) ;currently proving PLUS-TREE-DEL 1 Goal preprocess ;\"Goal\" creates 1 subgoal by preprocessing 2 | Goal' simp ;\"Goal'\" creates 2 subgoals by simplification c 0 | | Subgoal 2 PUSH *1 ;\"Subgoal 2\" pushes \"*1\" for INDUCT ++++++++++++++++++++++++++++++ ;first pass thru waterfall completed c 6 *1 INDUCT ;Proof by induction of \"*1\" has | <5 more subgoals> ; created 6 top-level subgoals. At ; this point, one of those 6 has been ; proved, and 5 remain to be proved. ; We are currently working on the ; first of those 5 remaining goals. ~ev[] ~l[proof-tree-examples] for many examples that contain proof tree displays. But first, we summarize the kinds of lines that may appear in a proof tree display. The simplest form of a proof tree display is a header showing the current event, followed by list of lines, each having one of the following forms. ~bv[] n ... ~ev[] Says that the indicated goal created ~c[n] subgoals using the indicated process. Here ``...'' refers to possible additional information. ~bv[] c n ... ~ev[] As above, but calls attention to the fact that this goal is a ``checkpoint'' in the sense that it may be of particular interest. Some displays may overwrite ``c'' with ``>'' to indicate the current checkpoint being shown in the proof transcript. ~bv[] | ... | | ~ev[] Indicates that the goal just above this line, which is pointed to by the rightmost vertical bar (``|''), has ~c[k] subgoals, none of which have yet been processed. ~bv[] | ... | | ~ev[] As above, except that some subgoals have already been processed. ~bv[] ++++++++++++++++++++++++++++++ ~ev[] Separates successive passes through the ``waterfall''. Thus, this ``fencepost'' mark indicates the start of a new proof by induction or of a new forcing round. ~l[proof-tree-examples] for detailed examples. ~l[checkpoint-forced-goals] to learn how to mark goals as checkpoints that ~il[force] the creation of goals in forcing rounds. Finally, ~pl[proof-tree-details] for some points not covered elsewhere.") (deflabel proof-tree-examples :doc ":Doc-Section Proof-tree proof tree example~/ ~l[proof-tree] for an introduction to proof trees, and for a list of related topics. Here we present a detailed example followed by a shorter example that illustrates proof by induction.~/ Consider the ~il[guard] proof for the definition of a function ~c[cancel_equal_plus]; the body of this definition is of no importance here. The first proof tree display is: ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess | <18 subgoals> ~ev[] This is to be read as follows. ~bq[] At this stage of the proof we have encountered the top-level goal, named \"Goal\", which generated 18 subgoals using the ``preprocess'' process. We have not yet begun to work on those subgoals. ~eq[] The corresponding message from the ordinary prover output is: ~bq[] By case analysis we reduce the conjecture to the following 18 conjectures. ~eq[] Note that the field just before the name of the goal (~c[\"Goal\"]), which here contains the number 18, indicates the number of cases (children) created by the goal using the indicated process. This number will remain unchanged as long as this goal is displayed. The next proof tree display is: ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 18 simp | | <1 subgoal> | <17 more subgoals> ~ev[] which indicates that at this point, the prover has used the simplification (``simp'') process on Subgoal 18 to create one subgoal (``<1 subgoal>''). The vertical bar (``|'') below ``Subgoal 18'', accompanied by the line below it, signifies that there are 17 siblings of Subgoal 18 that remain to be processed. The next proof tree displayed is: ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 18 simp c 2 | | Subgoal 18' ELIM | | | <2 subgoals> | <17 more subgoals> ~ev[] Let us focus on the fourth line of this display: ~bv[] c 2 | | Subgoal 18' ELIM ~ev[] The ``c'' field marks this goal as a ``checkpoint'', i.e., a goal worthy of careful scrutiny. In fact, any goal that creates children by a process other than ``preprocess'' or ``simp'' is marked as a checkpoint. In this case, the destructor-elimination (``~il[ELIM]'') process has been used to create subgoals of this goal. The indentation shows that this goal, Subgoal 18', is a child of Subgoal 18. The number ``2'' indicates that 2 subgoals have been created (by ~il[ELIM]). Note that this information is consistent with the line just below it, which says ``<2 subgoals>''. Finally, the last line of this proof tree display, ~bv[] | <17 more subgoals> ~ev[] is connected by vertical bars (``|'') up to the string ~c[\"Subgoal 18\"], which suggests that there are 17 immediate subgoals of Goal remaining to process after Subgoal 18. Note that this line is indented one level from the second line, which is the line for the goal named ~c[\"Goal\"]. The display is intended to suggest that the subgoals of Goal that remain to be proved consist of Subgoal 18 together with 17 more subgoals. The next proof tree display differs from the previous one only in that now, Subgoal 18' has only one more subgoal to be processed. ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 18 simp c 2 | | Subgoal 18' ELIM | | | <1 more subgoal> | <17 more subgoals> ~ev[] Note that the word ``more'' in ``<1 more subgoal>'' tells us that there was originally more than one subgoal of Subgoal 18. In fact that information already follows from the line above, which (as previously explained) says that Subgoal 18' originally created 2 subgoals. The next proof tree display occurs when the prover completes the proof of that ``1 more subgoal'' referred to above. ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess | <17 more subgoals> ~ev[] Then, Subgoal 17 is processed and creates one subgoal, by simplification: ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 17 simp | | <1 subgoal> | <16 more subgoals> ~ev[] ... and so on. Later in the proof one might find the following successive proof tree displays. ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess | <9 more subgoals> ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 9 simp (FORCED) | <8 more subgoals> ~ev[] These displays tell us that Subgoal 9 simplified to ~c[t] (note that the ``0'' shows clearly that no subgoals were created), but that some rule's hypotheses were ~il[force]d. Although this goal is not checkpointed (i.e., no ``c'' appears on the left margin), one can cause such goals to be checkpointed; ~pl[checkpoint-forced-goals]. In fact, the proof tree displayed at the end of the ``main proof''(the 0-th forcing round) is as follows. ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 9 simp (FORCED) 0 | Subgoal 8 simp (FORCED) 0 | Subgoal 7 simp (FORCED) 0 | Subgoal 6 simp (FORCED) 0 | Subgoal 4 simp (FORCED) 0 | Subgoal 3 simp (FORCED) ~ev[] This is followed by the following proof tree display at the start of the forcing round. ~bv[] 18 Goal preprocess 0 | Subgoal 9 simp (FORCED [1]Subgoal 4) 0 | Subgoal 8 simp (FORCED [1]Subgoal 6) 0 | Subgoal 7 simp (FORCED [1]Subgoal 1) 0 | Subgoal 6 simp (FORCED [1]Subgoal 3) 0 | Subgoal 4 simp (FORCED [1]Subgoal 5) 0 | Subgoal 3 simp (FORCED [1]Subgoal 2) ++++++++++++++++++++++++++++++ 6 [1]Goal FORCING-ROUND 2 | [1]Subgoal 6 preprocess | | <2 subgoals> | <5 more subgoals> ~ev[] This display shows which goals to ``blame'' for the existence of each goal in the forcing round. For example, Subgoal 9 is to blame for the creation of [1]Subgoal 4. Actually, there is no real goal named ~c[\"[1~]Goal\"]. However, the line ~bv[] 6 [1]Goal FORCING-ROUND ~ev[] appears in the proof tree display to suggest a ``parent'' of the six top-level goals in that forcing round. As usual, the numeric field before the goal name contains the original number of children of that (virtual, in this case) goal ~-[] in this case, 6. In our example proof, Subgoal 6 eventually gets proved, without doing any further forcing. At that point, the proof tree display looks as follows. ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 9 simp (FORCED [1]Subgoal 4) 0 | Subgoal 7 simp (FORCED [1]Subgoal 1) 0 | Subgoal 6 simp (FORCED [1]Subgoal 3) 0 | Subgoal 4 simp (FORCED [1]Subgoal 5) 0 | Subgoal 3 simp (FORCED [1]Subgoal 2) ++++++++++++++++++++++++++++++ 6 [1]Goal FORCING-ROUND | <5 more subgoals> ~ev[] Notice that the line for Subgoal 8, ~bv[] 0 | Subgoal 8 simp (FORCED [1]Subgoal 6) ~ev[] no longer appears. That is because the goal [1]Subgoal 6 has been proved, along with all its children; and hence, the proof of Subgoal 8 no longer depends on any further reasoning. The final two proof tree displays in our example are as follows. ~bv[] ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 7 simp (FORCED [1]Subgoal 1) ++++++++++++++++++++++++++++++ 6 [1]Goal FORCING-ROUND 2 | [1]Subgoal 1 preprocess 1 | | [1]Subgoal 1.1 preprocess 1 | | | [1]Subgoal 1.1' simp c 3 | | | | [1]Subgoal 1.1'' ELIM | | | | | <1 more subgoal> ( DEFUN CANCEL_EQUAL_PLUS ...) <> ~ev[] The explanation for the empty proof tree is simple: once [1]Subgoal 1.1.1 was proved, nothing further remained to be proved. In fact, the much sought-after ``Q.E.D.'' appeared shortly after the final proof tree was displayed. Let us conclude with a final, brief example that illustrates proof by induction. Partway through the proof one might come across the following proof tree display. ~bv[] ( DEFTHM PLUS-TREE-DEL ...) 1 Goal preprocess 2 | Goal' simp c 0 | | Subgoal 2 PUSH *1 | | <1 more subgoal> ~ev[] This display says that in the attempt to prove a theorem called ~c[plus-tree-del], preprocessing created the only child Goal' from Goal, and Goal' simplified to two subgoals. Subgoal 2 is immediately pushed for proof by induction, under the name ``*1''. In fact if Subgoal 1 simplifies to ~c[t], then we see the following successive proof tree displays after the one shown above. ~bv[] ( DEFTHM PLUS-TREE-DEL ...) 1 Goal preprocess 2 | Goal' simp c 0 | | Subgoal 2 PUSH *1 ( DEFTHM PLUS-TREE-DEL ...) 1 Goal preprocess 2 | Goal' simp c 0 | | Subgoal 2 PUSH *1 ++++++++++++++++++++++++++++++ c 6 *1 INDUCT | <5 more subgoals> ~ev[] The separator ``+++++...'' says that we are beginning another trip through the waterfall. In fact this trip is for a proof by induction (as opposed to a forcing round), as indicated by the word ``INDUCT''. Apparently *1.6 was proved immediately, because it was not even displayed; a goal is only displayed when there is some work left to do either on it or on some goal that it brought (perhaps indirectly) into existence. Once a proof by induction is completed, the ``PUSH'' line that refers to that proof is eliminated (``pruned''). So for example, when the present proof by induction is completed, the line ~bv[] c 0 | | Subgoal 2 PUSH *1 ~ev[] is eliminated, which in fact causes the lines above it to be eliminated (since they no longer refer to unproved children). Hence, at that point one might expect to see: ~bv[] ( DEFTHM PLUS-TREE-DEL ...) <> ~ev[] However, if the proof by induction of *1 necessitates further proofs by induction or a forcing round, then this ``pruning'' will not yet be done.") (defun start-proof-tree-fn (remove-inhibit-p state) ; Note that we do not override existing values of the indicated state global ; variables. (if remove-inhibit-p (f-put-global 'inhibit-output-lst (remove1-eq 'proof-tree (f-get-global 'inhibit-output-lst state)) state) state)) #+acl2-loop-only (defmacro start-proof-tree () ":Doc-Section Proof-tree start displaying proof trees during proofs~/ Also ~pl[proof-tree] and ~pl[stop-proof-tree]. Note that ~c[:start-proof-tree] works by removing ~c[']~ilc[proof-tree] from the ~c[inhibit-output-lst]; ~pl[set-inhibit-output-lst].~/ Explanations of proof tree displays may be found elsewhere: ~pl[proof-tree]. In a nutshell: ~c[:start-proof-tree] causes proof tree display to be turned on, once it has been turned off by ~c[:]~ilc[stop-proof-tree]. Do not attempt to invoke ~c[start-proof-tree] during an interrupt in the middle of a proof." '(pprogn (start-proof-tree-fn t state) (fms "Proof tree output is now enabled. Note that ~ :START-PROOF-TREE works by removing 'proof-tree from ~ the inhibit-output-lst; see :DOC ~ set-inhibit-output-lst.~%" nil (standard-co state) state nil) (value :invisible))) #-acl2-loop-only (defmacro start-proof-tree () '(let ((state *the-live-state*)) (fms "IT IS ILLEGAL to invoke (START-PROOF-TREE) from raw Lisp. Please ~ first enter the ACL2 command loop with (LP)." nil (proofs-co state) state nil) (values))) (defmacro checkpoint-forced-goals (val) ":Doc-Section Proof-tree Cause forcing goals to be checkpointed in proof trees~/ ~bv[] Example forms: (checkpoint-forced-goals t) (checkpoint-forced-goals nil) ~ev[] Also ~pl[proof-tree].~/ By default, goals are not marked as checkpoints by a proof tree display (as described elsewhere; ~pl[proof-tree]) merely because they ~il[force] some hypotheses, thus possibly contributing to a forcing round. However, some users may want such behavior, which will occur once the command ~c[(checkpoint-forced-goals] ~c[t]) has been executed. To return to the default behavior, use the command ~c[(checkpoint-forced-goals nil)]." `(pprogn (f-put-global 'checkpoint-forced-goals ',val state) (value ',val))) (defun stop-proof-tree-fn (state) (f-put-global 'inhibit-output-lst (add-to-set-eq 'proof-tree (f-get-global 'inhibit-output-lst state)) state)) (defmacro stop-proof-tree () ":Doc-Section Proof-tree stop displaying proof trees during proofs~/ Also ~pl[proof-tree] and ~pl[start-proof-tree]. Note that ~c[:stop-proof-tree] works by adding ~c[']~ilc[proof-tree] to the ~c[inhibit-output-lst]; ~pl[set-inhibit-output-lst].~/ Proof tree displays are explained in the documentation for ~il[proof-tree]. ~c[:Stop-proof-tree] causes proof tree display to be turned off. It is permissible to submit the form ~c[(stop-proof-tree)] during a break. Thus, you can actually turn off proof tree display in the middle of a proof by interrupting ACL2 and submitting the form ~c[(stop-proof-tree)] in raw Lisp." '(pprogn (stop-proof-tree-fn state) (fms "Proof tree output is now inhibited. Note that ~ :STOP-PROOF-TREE works by adding 'proof-tree to the ~ inhibit-output-lst; see :DOC set-inhibit-output-lst.~%" nil (standard-co state) state nil) (value :invisible))) (deflabel proof-tree-details :doc ":Doc-Section Proof-tree proof tree details not covered elsewhere~/ ~l[proof-tree] for an introduction to proof trees, and for a list of related topics. Here we present some details not covered elsewhere.~/ 1. When proof tree display is enabled (because the command ~c[:]~ilc[stop-proof-tree] has not been executed, or has been superseded by a later ~c[:]~ilc[start-proof-tree] command), then time summaries will include the time for proof tree display. This time includes the time spent computing with proof trees, such as the pruning process described briefly above. Even when proof trees are not displayed, such as when their display is turned off in the middle of a proof, this time will be printed if it is not 0. 2. When a goal is given a ~c[:bye] in a proof (~pl[hints]), it is treated for the purpose of proof tree display just as though it had been proved. 3. Several ~il[state] global variables affect proof tree display. ~c[(@ proof-tree-indent)] is initially the string ~c[\"| \"]: it is the string that is laid down the appropriate number of times to effect indentation. ~c[(@ proof-tree-buffer-width)] is initially the value of ~c[(fmt-soft-right-margin state)], and is used to prevent printing of the annotation ``(~il[force]d ...)'' in any greater column than this value. However, ~c[(assign proof-tree-buffer-width nil)] to avoid any such suppression. Finally, ~c[(@ checkpoint-processors)] is a list of processors from the constant list ~c[*preprocess-clause-ledge*], together with ~c[:induct]. You may remove elements of ~c[(@ checkpoint-processors)] to limit which processes are considered checkpoints, but note that this can affect what is printed by gag-mode (~pl[set-gag-mode]). 4. When ~c[:]~ilc[otf-flg] is not set to ~c[t] in a proof, and the prover then decides to revert to the original goal and prove it by induction, the proof tree display will reflect this fact as shown here: ~bv[] c 0 | | Subgoal 2 PUSH (reverting) ~ev[] 5. The usual ~il[failure] message is printed as part of the prooftree display when a proof has failed.") (mutual-recursion (defun insert-into-goal-tree-rec (cl-id processor n goal-tree) (let ((new-children (insert-into-goal-tree-lst cl-id processor n (access goal-tree goal-tree :children)))) (and new-children (change goal-tree goal-tree :children new-children)))) (defun insert-into-goal-tree-lst (cl-id processor n goal-tree-lst) (cond ((consp goal-tree-lst) (let ((new-child (insert-into-goal-tree-rec cl-id processor n (car goal-tree-lst)))) (if new-child (cons new-child (cdr goal-tree-lst)) (let ((rest-children (insert-into-goal-tree-lst cl-id processor n (cdr goal-tree-lst)))) (if rest-children (cons (car goal-tree-lst) rest-children) nil))))) ((integerp goal-tree-lst) (cons (make goal-tree :cl-id cl-id :processor processor :children n :fanout (or n 0)) (if (eql goal-tree-lst 1) nil (1- goal-tree-lst)))) (t nil))) ) (defun insert-into-goal-tree (cl-id processor n goal-tree) ; This function updates the indicated goal-tree by adding a new goal tree built ; from cl-id, processor, and n, in place of the first integer "children" field ; of a subgoal in a left-to-right depth-first traversal of the goal-tree. ; (Recall that an integer represents the number of unproved children remaining; ; hence the first integer found corresponds to the goal that corresponds to the ; parameters of this function.) However, we return nil if no integer ; "children" field is found; similarly for the -rec and -lst versions, above. ; Note that n should be nil or a (strictly) positive integer. Also note that ; when cl-id is *initial-clause-id*, then goal-tree doesn't matter (for ; example, it may be nil). (cond ((equal cl-id *initial-clause-id*) (make goal-tree :cl-id cl-id :processor processor :children n :fanout (or n 0))) (t (insert-into-goal-tree-rec cl-id processor n goal-tree)))) (defun set-difference-equal-changedp (l1 l2) ; Like set-difference-equal, but returns (mv changedp lst) where lst is the set ; difference and changedp is t iff the set difference is not equal to l1. (declare (xargs :guard (and (true-listp l1) (true-listp l2)))) (cond ((endp l1) (mv nil nil)) (t (mv-let (changedp lst) (set-difference-equal-changedp (cdr l1) l2) (cond ((member-equal (car l1) l2) (mv t lst)) (changedp (mv t (cons (car l1) lst))) (t (mv nil l1))))))) (mutual-recursion (defun prune-goal-tree (forcing-round dead-clause-ids goal-tree) ; Removes all proved goals from a goal tree, where all dead-clause-ids are ; considered proved. Actually returns two values: a new goal tree (or nil), ; and a new (extended) list of dead-clause-ids. ; Goals with processor (push-clause id . x) are handled similarly to forced ; goals, except that we know that there is a unique child. ; Note that a non-nil final cdr prevents a goal from being considered proved ; (unless its clause-id is dead, which shouldn't happen), which is appropriate. (let* ((processor (access goal-tree goal-tree :processor)) (cl-id (access goal-tree goal-tree :cl-id)) (goal-forcing-round (access clause-id cl-id :forcing-round))) (cond ((member-equal cl-id dead-clause-ids) (mv (er hard 'prune-goal-tree "Surprise! We didn't think this case could occur.") dead-clause-ids)) ((and (not (= forcing-round goal-forcing-round)) ; So, current goal is from a previous forcing round. (consp processor) (eq (cadr processor) :forced)) ; So, processor is of the form (pr :forced clause-id_1 ... clause-id_n). (mv-let (changedp forced-clause-ids) (set-difference-equal-changedp (cddr processor) dead-clause-ids) (cond ((null forced-clause-ids) (mv nil (cons cl-id dead-clause-ids))) ; Notice that goal-tree may have children, even though it comes from an earlier ; forcing round, because it may have generated children that themselves did ; some forcing. (t (mv-let (children new-dead-clause-ids) (prune-goal-tree-lst forcing-round dead-clause-ids (access goal-tree goal-tree :children)) (cond (changedp (mv (change goal-tree goal-tree :processor (list* (car processor) :forced forced-clause-ids) :children children) new-dead-clause-ids)) (t (mv (change goal-tree goal-tree :children children) new-dead-clause-ids)))))))) ((and (consp processor) (eq (car processor) 'push-clause)) (assert$ (null (access goal-tree goal-tree :children)) ; It is tempting also to assert (null (cddr processor)), i.e., that we have not ; reverted or aborted. But that can fail for a branch of a disjunctive (:or) ; split. (if (member-equal (cadr processor) dead-clause-ids) (mv nil (cons cl-id dead-clause-ids)) (mv goal-tree dead-clause-ids)))) (t (mv-let (children new-dead-clause-ids) (prune-goal-tree-lst forcing-round dead-clause-ids (access goal-tree goal-tree :children)) (cond ((or children ; Note that the following test implies that we're in the current forcing round, ; and hence "decoration" (adding a list of new goals created by that forcing) ; has not yet been done. (and (consp processor) (eq (cadr processor) :forced))) (mv (change goal-tree goal-tree :children children) new-dead-clause-ids)) (t (mv nil (cons cl-id new-dead-clause-ids))))))))) (defun prune-goal-tree-lst (forcing-round dead-clause-ids goal-tree-lst) (cond ((consp goal-tree-lst) (mv-let (x new-dead-clause-ids) (prune-goal-tree forcing-round dead-clause-ids (car goal-tree-lst)) (if x (mv-let (rst newer-dead-clause-ids) (prune-goal-tree-lst forcing-round new-dead-clause-ids (cdr goal-tree-lst)) (mv (cons x rst) newer-dead-clause-ids)) (prune-goal-tree-lst forcing-round new-dead-clause-ids (cdr goal-tree-lst))))) (t (mv goal-tree-lst dead-clause-ids)))) ) (defun prune-proof-tree (forcing-round dead-clause-ids proof-tree) (if (null proof-tree) nil (mv-let (new-goal-tree new-dead-clause-ids) (prune-goal-tree forcing-round dead-clause-ids (car proof-tree)) (if new-goal-tree (cons new-goal-tree (prune-proof-tree forcing-round new-dead-clause-ids (cdr proof-tree))) (prune-proof-tree forcing-round new-dead-clause-ids (cdr proof-tree)))))) (defun print-string-repeat (increment level col channel state) (declare (type (signed-byte 30) col level)) (the2s (signed-byte 30) (if (= level 0) (mv col state) (mv-letc (col state) (fmt1 "~s0" (list (cons #\0 increment)) col channel state nil) (print-string-repeat increment (1-f level) col channel state))))) (defconst *format-proc-alist* '((apply-top-hints-clause-or-hit . ":OR") (apply-top-hints-clause . "top-level-hints") (preprocess-clause . "preprocess") (simplify-clause . "simp") ;;settled-down-clause (eliminate-destructors-clause . "ELIM") (fertilize-clause . "FERT") (generalize-clause . "GEN") (eliminate-irrelevance-clause . "IRREL") ;;push-clause )) (defun format-forced-subgoals (clause-ids col max-col channel state) ; Print the "(FORCED ...)" annotation, e.g., the part after "(FORCED" on this ; line: ; 0 | Subgoal 3 simp (FORCED [1]Subgoal 1) (cond ((null clause-ids) (princ$ ")" channel state)) (t (let ((goal-name (string-for-tilde-@-clause-id-phrase (car clause-ids)))) (if (or (null max-col) ; We must leave room for final " ...)" if there are more goals, in addition to ; the space, the goal name, and the comma. Otherwise, we need room for the ; space and the right paren. (if (null (cdr clause-ids)) (<= (+ 2 col (length goal-name)) max-col) (<= (+ 7 col (length goal-name)) max-col))) (mv-let (col state) (fmt1 " ~s0~#1~[~/,~]" (list (cons #\0 goal-name) (cons #\1 clause-ids)) col channel state nil) (format-forced-subgoals (cdr clause-ids) col max-col channel state)) (princ$ " ...)" channel state)))))) (defun format-processor (col goal-tree channel state) (let ((proc (access goal-tree goal-tree :processor))) (cond ((consp proc) (cond ((eq (car proc) 'push-clause) (mv-let (col state) (fmt1 "~s0 ~@1" (list (cons #\0 "PUSH") (cons #\1 (cond ((eq (caddr proc) :REVERT) "(reverting)") ((eq (caddr proc) :ABORT) "*ABORTING*") (t (tilde-@-pool-name-phrase (access clause-id (cadr proc) :forcing-round) (access clause-id (cadr proc) :pool-lst)))))) col channel state nil) (declare (ignore col)) state)) ((eq (cadr proc) :forced) (mv-let (col state) (fmt1 "~s0 (FORCED" ; Note that (car proc) is in *format-proc-alist*, because neither push-clause ; nor either of the "fake" processors (:INDUCT, :FORCING-ROUND) forces in the ; creation of subgoals. (list (cons #\0 (cdr (assoc-eq (car proc) *format-proc-alist*)))) col channel state nil) (format-forced-subgoals (cddr proc) col (f-get-global 'proof-tree-buffer-width state) channel state))) (t (let ((err (er hard 'format-processor "Unexpected shape for goal-tree processor, ~x0" proc))) (declare (ignore err)) state)))) (t (princ$ (or (cdr (assoc-eq proc *format-proc-alist*)) proc) channel state))))) (mutual-recursion (defun format-goal-tree-lst (goal-tree-lst level fanout increment checkpoints checkpoint-forced-goals channel state) (cond ((null goal-tree-lst) state) ((atom goal-tree-lst) (mv-let (col state) (pprogn (princ$ " " channel state) (print-string-repeat increment (the-fixnum! level 'format-goal-tree-lst) 5 channel state)) (mv-let (col state) (fmt1 "<~x0 ~#1~[~/more ~]subgoal~#2~[~/s~]>~%" (list (cons #\0 goal-tree-lst) (cons #\1 (if (= fanout goal-tree-lst) 0 1)) (cons #\2 (if (eql goal-tree-lst 1) 0 1))) col channel state nil) (declare (ignore col)) state))) (t (pprogn (format-goal-tree (car goal-tree-lst) level increment checkpoints checkpoint-forced-goals channel state) (format-goal-tree-lst (cdr goal-tree-lst) level fanout increment checkpoints checkpoint-forced-goals channel state))))) (defun format-goal-tree (goal-tree level increment checkpoints checkpoint-forced-goals channel state) (let* ((cl-id (access goal-tree goal-tree :cl-id)) (pool-lst (access clause-id cl-id :pool-lst)) (fanout (access goal-tree goal-tree :fanout)) (raw-processor (access goal-tree goal-tree :processor)) (processor (if (atom raw-processor) raw-processor (car raw-processor)))) (mv-letc (col state) (pprogn (mv-letc (col state) (fmt1 "~#0~[c~/ ~]~c1 " (list (cons #\0 (if (or (member-eq processor checkpoints) (and checkpoint-forced-goals (consp raw-processor) (eq (cadr raw-processor) :forced))) 0 1)) (cons #\1 (cons fanout 3))) 0 channel state nil) (print-string-repeat increment (the-fixnum! level 'format-goal-tree) col channel state))) (mv-letc (col state) (if (and (null (access clause-id cl-id :case-lst)) (= (access clause-id cl-id :primes) 0) pool-lst) (fmt1 "~@0 " (list (cons #\0 (tilde-@-pool-name-phrase (access clause-id cl-id :forcing-round) pool-lst))) col channel state nil) (fmt1 "~@0 " (list (cons #\0 (tilde-@-clause-id-phrase cl-id))) col channel state nil)) (pprogn (format-processor col goal-tree channel state) (pprogn (newline channel state) (format-goal-tree-lst (access goal-tree goal-tree :children) (1+ level) fanout increment checkpoints checkpoint-forced-goals channel state))))))) ) (defun format-proof-tree (proof-tree-rev increment checkpoints checkpoint-forced-goals channel state) ; Recall that most recent forcing rounds correspond to the goal-trees closest ; to the front of a proof-tree. But here, proof-tree-rev is the reverse of a ; proof-tree. (if (null proof-tree-rev) state (pprogn (format-goal-tree (car proof-tree-rev) 0 increment checkpoints checkpoint-forced-goals channel state) (if (null (cdr proof-tree-rev)) state (mv-let (col state) (fmt1 "++++++++++++++++++++++++++++++~%" (list (cons #\0 increment)) 0 channel state nil) (declare (ignore col)) state)) (format-proof-tree (cdr proof-tree-rev) increment checkpoints checkpoint-forced-goals channel state)))) (defun print-proof-tree1 (ctx channel state) (let ((proof-tree (f-get-global 'proof-tree state))) (if (null proof-tree) (if (and (consp ctx) (eq (car ctx) :failed)) state (princ$ "Q.E.D." channel state)) (format-proof-tree (reverse proof-tree) (f-get-global 'proof-tree-indent state) (f-get-global 'checkpoint-processors state) (f-get-global 'checkpoint-forced-goals state) channel state)))) (defconst *proof-failure-string* "******** FAILED ********~|") (defun print-proof-tree-ctx (ctx channel state) (let* ((failed-p (and (consp ctx) (eq (car ctx) :failed))) (actual-ctx (if failed-p (cdr ctx) ctx))) (mv-let (erp val state) (state-global-let* ((fmt-hard-right-margin 1000 set-fmt-hard-right-margin) (fmt-soft-right-margin 1000 set-fmt-soft-right-margin)) ; We need the event name to fit on a single line, hence the state-global-let* ; above. (mv-let (col state) (fmt-ctx actual-ctx 0 channel state) (mv-let (col state) (fmt1 "~|~@0" (list (cons #\0 (if failed-p *proof-failure-string* ""))) col channel state nil) (declare (ignore col)) (value nil)))) (declare (ignore erp val)) state))) (defconst *proof-tree-start-delimiter* "#<\\<0") (defconst *proof-tree-end-delimiter* "#>\\>") (defun print-proof-tree-finish (state) (if (f-get-global 'proof-tree-start-printed state) (pprogn (mv-let (col state) (fmt1! "~s0" (list (cons #\0 *proof-tree-end-delimiter*)) 0 (proofs-co state) state nil) (declare (ignore col)) (f-put-global 'proof-tree-start-printed nil state))) state)) (defun print-proof-tree (state) ; WARNING: Every call of print-proof-tree should be underneath some call of the ; form (io? ...). We thus avoid enclosing the body below with (io? proof-tree ; ...). (let ((chan (proofs-co state)) (ctx (f-get-global 'proof-tree-ctx state))) (pprogn (if (f-get-global 'window-interfacep state) state (pprogn (f-put-global 'proof-tree-start-printed t state) (mv-let (col state) (fmt1 "~s0" (list (cons #\0 *proof-tree-start-delimiter*)) 0 chan state nil) (declare (ignore col)) ;print-proof-tree-ctx starts with newline state))) (print-proof-tree-ctx ctx chan state) (print-proof-tree1 ctx chan state) (print-proof-tree-finish state)))) (mutual-recursion (defun decorate-forced-goals-1 (goal-tree clause-id-list forced-clause-id) (let ((cl-id (access goal-tree goal-tree :cl-id)) (new-children (decorate-forced-goals-1-lst (access goal-tree goal-tree :children) clause-id-list forced-clause-id))) (cond ((member-equal cl-id clause-id-list) (let ((processor (access goal-tree goal-tree :processor))) (change goal-tree goal-tree :processor (list* (car processor) :forced forced-clause-id (cddr processor)) :children new-children))) (t (change goal-tree goal-tree :children new-children))))) (defun decorate-forced-goals-1-lst (goal-tree-lst clause-id-list forced-clause-id) (cond ((null goal-tree-lst) nil) ((atom goal-tree-lst) ; By the time we've gotten this far, we've gotten to the next forcing round, ; and hence there shouldn't be any children remaining to process. Of course, a ; forced goal can generate forced subgoals, so we can't say that there are no ; children -- but we CAN say that there are none remaining to process. (er hard 'decorate-forced-goals-1-lst "Unexpected goal-tree in call ~x0" (list 'decorate-forced-goals-1-lst goal-tree-lst clause-id-list forced-clause-id))) (t (cons (decorate-forced-goals-1 (car goal-tree-lst) clause-id-list forced-clause-id) (decorate-forced-goals-1-lst (cdr goal-tree-lst) clause-id-list forced-clause-id))))) ) (defun decorate-forced-goals (forcing-round goal-tree clause-id-list-list n) ; See decorate-forced-goals-in-proof-tree. (if (null clause-id-list-list) goal-tree (decorate-forced-goals forcing-round (decorate-forced-goals-1 goal-tree (car clause-id-list-list) (make clause-id :forcing-round forcing-round :pool-lst nil :case-lst (and n (list n)) :primes 0)) (cdr clause-id-list-list) (and n (1- n))))) (defun decorate-forced-goals-in-proof-tree (forcing-round proof-tree clause-id-list-list n) ; This function decorates the goal trees in proof-tree so that the appropriate ; previous forcing round's goals are "blamed" for the new forcing round goals. ; See also extend-proof-tree-for-forcing-round. ; At the top level, n is either an integer greater than 1 or else is nil. This ; corresponds respectively to whether or not there is more than one goal ; produced by the forcing round. (if (null proof-tree) nil (cons (decorate-forced-goals forcing-round (car proof-tree) clause-id-list-list n) (decorate-forced-goals-in-proof-tree forcing-round (cdr proof-tree) clause-id-list-list n)))) (defun assumnote-list-to-clause-id-list (assumnote-list) (if (null assumnote-list) nil (cons (access assumnote (car assumnote-list) :cl-id) (assumnote-list-to-clause-id-list (cdr assumnote-list))))) (defun assumnote-list-list-to-clause-id-list-list (assumnote-list-list) (if (null assumnote-list-list) nil (cons (assumnote-list-to-clause-id-list (car assumnote-list-list)) (assumnote-list-list-to-clause-id-list-list (cdr assumnote-list-list))))) (defun extend-proof-tree-for-forcing-round (forcing-round parent-clause-id clause-id-list-list state) ; This function pushes a new goal tree onto the global proof-tree. However, it ; decorates the existing goal trees so that the appropriate previous forcing ; round's goals are "blamed" for the new forcing round goals. Specifically, a ; previous goal with clause id in a member of clause-id-list-list is blamed for ; creating the appropriate newly-forced goal, with (car clause-id-list-list) ; associated with the highest-numbered (first) forced goal, etc. (cond ((null clause-id-list-list) ; then the proof is complete! state) (t (let ((n (length clause-id-list-list))) ;note n>0 (f-put-global 'proof-tree (cons (make goal-tree :cl-id parent-clause-id :processor :FORCING-ROUND :children n :fanout n) (decorate-forced-goals-in-proof-tree forcing-round (f-get-global 'proof-tree state) clause-id-list-list (if (null (cdr clause-id-list-list)) nil (length clause-id-list-list)))) state))))) (defun initialize-proof-tree1 (parent-clause-id x pool-lst forcing-round ctx state) ; X is from the "x" argument of waterfall. Thus, if we are starting a forcing ; round then x is list of pairs (assumnote-lst . clause) where the clause-ids ; from the assumnotes are the names of goals from the preceding forcing round ; to "blame" for the creation of that clause. (pprogn ; The user might have started up proof trees with something like (assign ; inhibit-output-lst nil). In that case we need to ensure that appropriate ; state globals are initialized. Note that start-proof-tree-fn does not ; override existing bindings of those state globals (which the user may have ; deliberately set). (start-proof-tree-fn nil state) (f-put-global 'proof-tree-ctx ctx state) (cond ((and (null pool-lst) (eql forcing-round 0)) (f-put-global 'proof-tree nil state)) (pool-lst (f-put-global 'proof-tree (cons (let ((n (length x))) (make goal-tree :cl-id parent-clause-id :processor :INDUCT :children (if (= n 0) nil n) :fanout n)) (f-get-global 'proof-tree state)) state)) (t (extend-proof-tree-for-forcing-round forcing-round parent-clause-id (assumnote-list-list-to-clause-id-list-list (strip-cars x)) state))))) (defun initialize-proof-tree (parent-clause-id x ctx state) ; X is from the "x" argument of waterfall. See initialize-proof-tree1. ; We assume (not (output-ignored-p 'proof-tree state)). (let ((pool-lst (access clause-id parent-clause-id :pool-lst)) (forcing-round (access clause-id parent-clause-id :forcing-round))) (pprogn (io? proof-tree nil state (ctx forcing-round pool-lst x parent-clause-id) (initialize-proof-tree1 parent-clause-id x pool-lst forcing-round ctx state)) (io? prove nil state (forcing-round pool-lst) (cond ((intersectp-eq '(prove proof-tree) (f-get-global 'inhibit-output-lst state)) state) ((and (null pool-lst) (eql forcing-round 0)) (fms "<< Starting proof tree logging >>~|" nil (proofs-co state) state nil)) (t state)))))) (defconst *star-1-clause-id* (make clause-id :forcing-round 0 :pool-lst '(1) :case-lst nil :primes 0)) (mutual-recursion (defun revert-goal-tree-rec (cl-id revertp goal-tree) ; See revert-goal-tree. This nest also returns a value cl-id-foundp, which is ; nil if the given cl-id was not found in goal-tree or any subsidiary goal ; trees, else :or-found if cl-id is found under a disjunctive split, else t. (let ((processor (access goal-tree goal-tree :processor))) (cond ((and (consp processor) (eq (car processor) 'push-clause)) (mv (equal cl-id (access goal-tree goal-tree :cl-id)) (if revertp (change goal-tree goal-tree :processor (list 'push-clause *star-1-clause-id* :REVERT)) goal-tree))) (t (mv-let (cl-id-foundp new-children) (revert-goal-tree-lst (eq processor 'apply-top-hints-clause-or-hit) cl-id revertp (access goal-tree goal-tree :children)) (mv cl-id-foundp (change goal-tree goal-tree :children new-children))))))) (defun revert-goal-tree-lst (or-p cl-id revertp goal-tree-lst) ; Or-p is true if we want to limit changes to the member of goal-tree-lst that ; is, or has a subsidiary, goal-tree for cl-id. (cond ((atom goal-tree-lst) (mv nil nil)) (t (mv-let (cl-id-foundp new-goal-tree) (revert-goal-tree-rec cl-id revertp (car goal-tree-lst)) (cond ((or (eq cl-id-foundp :or-found) (and cl-id-foundp or-p)) (mv :or-found (cons new-goal-tree (cdr goal-tree-lst)))) (t (mv-let (cl-id-foundp2 new-goal-tree-lst) (revert-goal-tree-lst or-p cl-id revertp (cdr goal-tree-lst)) (mv (or cl-id-foundp2 cl-id-foundp) (cons (if (eq cl-id-foundp2 :or-found) (car goal-tree-lst) new-goal-tree) new-goal-tree-lst))))))))) ) (defun revert-goal-tree (cl-id revertp goal-tree) ; If there are no disjunctive (:or) splits, this function replaces every final ; cdr of any :children field of each subsidiary goal tree (including goal-tree) ; by nil, for other than push-clause processors, indicating that there are no ; children left to consider proving. If revertp is true, it also replaces each ; (push-clause *n) with (push-clause *star-1-clause-id* :REVERT), meaning that ; we are reverting. ; The spec in the case of disjunctive splits is similar, except that if cl-id ; is under such a split, then the changes described above are limited to the ; innermost disjunct containing cl-id. (mv-let (cl-id-foundp new-goal-tree) (revert-goal-tree-rec cl-id revertp goal-tree) (assert$ cl-id-foundp new-goal-tree))) ; The pool is a list of pool-elements, as shown below. We explain ; in push-clause. (defrec pool-element (tag clause-set . hint-settings) t) (defun pool-lst1 (pool n ans) (cond ((null pool) (cons n ans)) ((eq (access pool-element (car pool) :tag) 'to-be-proved-by-induction) (pool-lst1 (cdr pool) (1+ n) ans)) (t (pool-lst1 (cdr pool) 1 (cons n ans))))) (defun pool-lst (pool) ; Pool is a pool as constructed by push-clause. That is, it is a list ; of pool-elements and the tag of each is either 'to-be-proved-by- ; induction or 'being-proved-by-induction. Generally when we refer to ; a pool-lst we mean the output of this function, which is a list of ; natural numbers. For example, '(3 2 1) is a pool-lst and *3.2.1 is ; its printed representation. ; If one thinks of the pool being divided into gaps by the ; 'being-proved-by-inductions (with gaps at both ends) then the lst ; has as many elements as there are gaps and the ith element, k, in ; the lst tells us there are k-1 'to-be-proved-by-inductions in the ; ith gap. ; Warning: It is assumed that the value of this function is always ; non-nil. See the use of "jppl-flg" in the waterfall and in ; pop-clause. (pool-lst1 pool 1 nil)) (defun increment-proof-tree (cl-id ttree processor clause-count new-hist signal pspv state) ; Modifies the global proof-tree so that it incorporates the given cl-id, which ; creates n child goals via processor. Also prints out the proof tree. (if (or (eq processor 'settled-down-clause) (and (consp new-hist) (consp (access history-entry (car new-hist) :processor)))) state (let* ((forcing-round (access clause-id cl-id :forcing-round)) (aborting-p (and (eq signal 'abort) (not (equal (tagged-objects 'abort-cause ttree) '(revert))))) (clause-count (cond ((eq signal 'or-hit) (assert$ (eq processor 'apply-top-hints-clause) (length (nth 2 (tagged-object :or ttree))))) (t clause-count))) (processor (cond ((tagged-objectsp 'assumption ttree) (assert$ (and (not (eq processor 'push-clause)) (not (eq signal 'or-hit))) (list processor :forced))) ((eq processor 'push-clause) (list* 'push-clause (make clause-id :forcing-round forcing-round :pool-lst (pool-lst (cdr (access prove-spec-var pspv :pool))) :case-lst nil :primes 0) (if aborting-p '(:ABORT) nil))) ((eq signal 'or-hit) 'apply-top-hints-clause-or-hit) (t processor))) (starting-proof-tree (f-get-global 'proof-tree state)) (new-goal-tree (insert-into-goal-tree cl-id processor (if (eql clause-count 0) nil clause-count) (car starting-proof-tree)))) (pprogn (if new-goal-tree (f-put-global 'proof-tree (if (and (consp processor) (eq (car processor) 'push-clause) (eq signal 'abort) (not aborting-p)) (if (and (= forcing-round 0) (null (cdr starting-proof-tree))) (list (revert-goal-tree cl-id t new-goal-tree)) (er hard 'increment-proof-tree "Internal Error: Attempted to ``revert'' ~ the proof tree with forcing round ~x0 and ~ proof tree of length ~x1. This reversion ~ should only have been tried with forcing ~ round 0 and proof tree of length 1. ~ Please contact the ACL2 implementors." forcing-round (length starting-proof-tree))) (prune-proof-tree forcing-round nil (cons (if (eq signal 'abort) (revert-goal-tree cl-id nil new-goal-tree) new-goal-tree) (cdr starting-proof-tree)))) state) (prog2$ (er hard 'increment-proof-tree "Found empty goal tree from call ~x0" (list 'insert-into-goal-tree cl-id processor (if (= clause-count 0) nil clause-count) (car starting-proof-tree))) state)) (print-proof-tree state))))) (defun goal-tree-with-cl-id (cl-id goal-tree-lst) (cond ((atom goal-tree-lst) nil) ((equal cl-id (access goal-tree (car goal-tree-lst) :cl-id)) (car goal-tree-lst)) (t (goal-tree-with-cl-id cl-id (cdr goal-tree-lst))))) (mutual-recursion (defun goal-tree-choose-disjunct-rec (cl-id disjunct-cl-id goal-tree) ; This is the recursive version of goal-tree-choose-disjunct. It either ; returns (mv nil goal-tree) without any change to the given goal-tree, or else ; it returns (mv t new-goal-tree) where new-goal-tree is not equal to ; goal-tree. (let ((children (access goal-tree goal-tree :children))) (cond ((equal cl-id (access goal-tree goal-tree :cl-id)) (assert$ (eq (access goal-tree goal-tree :processor) 'apply-top-hints-clause-or-hit) (let ((child (goal-tree-with-cl-id disjunct-cl-id children))) (mv t (cond (child (change goal-tree goal-tree :children (list child))) (t ; child was proved (change goal-tree goal-tree :children nil))))))) ((atom children) (mv nil goal-tree)) ; optimization (t (mv-let (found new-children) (goal-tree-choose-disjunct-lst cl-id disjunct-cl-id children) (cond (found (mv t (change goal-tree goal-tree :children new-children))) (t (mv nil goal-tree)))))))) (defun goal-tree-choose-disjunct-lst (cl-id disjunct-cl-id goal-tree-lst) (cond ((consp goal-tree-lst) (mv-let (found new-goal-tree) (goal-tree-choose-disjunct-rec cl-id disjunct-cl-id (car goal-tree-lst)) (cond (found (mv t (cons new-goal-tree (cdr goal-tree-lst)))) (t (mv-let (found new-goal-tree-lst) (goal-tree-choose-disjunct-lst cl-id disjunct-cl-id (cdr goal-tree-lst)) (cond (found (mv t (cons (car goal-tree-lst) new-goal-tree-lst))) (t (mv nil goal-tree-lst)))))))) (t (mv nil goal-tree-lst)))) ) (defun goal-tree-choose-disjunct (cl-id disjunct-cl-id goal-tree) ; Replace the subtree at the goal-tree with the given cl-id with the subtree at ; its child having the given disjunct-cl-id, but eliminating the extra "D" case ; from every clause id in that subtree. (mv-let (foundp new-goal-tree) (goal-tree-choose-disjunct-rec cl-id disjunct-cl-id goal-tree) (assert$ foundp new-goal-tree))) (defun install-disjunct-into-proof-tree (cl-id disjunct-cl-id state) ; Replace disjunct-cl-id by cl-id in the global proof tree, discarding the ; other disjunctive cases under cl-id. (let ((proof-tree (f-get-global 'proof-tree state))) (assert$ (consp proof-tree) (pprogn (f-put-global 'proof-tree (prune-proof-tree (access clause-id cl-id :forcing-round) nil (cons (goal-tree-choose-disjunct cl-id disjunct-cl-id (car proof-tree)) (cdr proof-tree))) state) (print-proof-tree state))))) ; Logical Names ; Logical names are names introduced by the event macros listed in ; primitive-event-macros, e.g., they are the names of functions, ; macros, theorems, packages, etc. Logical names have two main uses ; in this system. The first is in theory expressions, where logical ; names are used to denote times in the past, i.e., "Give me the list ; of all rules enabled when nm was introduced." The second is in the ; various keyword commands available to the user to enquire about his ; current state, i.e., "Show me the history around the time nmwas ; introduced." ; The latter use involves the much more sophisticated notion of ; commands as well as that of events. We will deal with it later. ; We make special provisions to support the mapping from a logical ; name to the world at the time that name was introduced. At the ; conclusion of the processing of an event, we set the 'global-value ; of 'event-landmark to an "event tuple." This happens in stop-event. ; Among other things, an event tuple lists the names introduced by the ; event. The successive settings of 'event-landmark are all visible ; on the world and thus effectively divide the world up into "event ; blocks." Because the setting of 'event-landmark is the last thing ; we do for an event, the world at the termination of a given event is ; the world whose car is the appropriate event tuple. So one way to ; find the world is scan down the current world, looking for the ; appropriate event landmark. ; This however is slow, because often the world is not in physical ; memory and must be paged in. We therefore have worked out a scheme ; to support the faster lookup of names. We could have stored the ; appropriate world on the property list of each symbolic name. We ; did not want to do this because it might cause consternation when a ; user looked at the properties. So we instead associate a unique ; nonnegative integer with each event and provide a mapping from those ; "absolute event numbers" to worlds. We store the absolute event ; number of each symbolic name on the property list of the name (in ; stop-event). The only other logical names are the strings that name ; packages. We find them by searching through the world. (defun logical-namep (name wrld) ; Returns non-nil if name is a logical name, i.e., a symbolic or ; string name introduced by an event, or the keyword :here meaning the ; most recent event. (cond ((symbolp name) (cond ((eq name :here) (not (null wrld))) (t (getprop name 'absolute-event-number nil 'current-acl2-world wrld)))) ((and (stringp name) (find-non-hidden-package-entry name (global-val 'known-package-alist wrld))) t) (t nil))) ; Logical-name-type has been moved up to translate.lisp in support of ; chk-all-but-new-name, which supports handling of flet by translate11. (defun logical-name-type-string (typ) (case typ (package "package") (function "function") (macro "macro") (const "constant") (stobj "single-threaded object") (stobj-live-var "single-threaded object holder") (theorem "theorem") (theory "theory") (label "label") (t (symbol-name typ)))) ; Event Tuples ; Every time an event occurs we store a new 'global-value for the ; variable 'event-landmark in stop-event. The value of ; 'event-landmark is an "event tuple." Abstractly, an event tuple ; contains the following fields: ; n: the absolute event number ; d: the embedded event depth (the number of events containing the event) ; form: the form evaluated that created the event. (This is often a form ; typed by the user but might have been a form generated by a macro. ; The form may be a call of a primitive event macro, e.g., defthm, ; or may be itself a macro call, e.g., prove-lemma.) ; type: the name of the primitive event macro we normally use, e.g., ; defthm, defuns, etc. ; namex: the name or names of the functions, rules, etc., introduced by ; the event. This may be a single object, e.g., 'APP, or "MY-PKG", ; or may be a true list of objects, e.g., '(F1 F2 F3) as in the case ; of a mutually recursive clique. 0 (zero) denotes the empty list of ; names. The unusual event enter-boot-strap-mode has a namex containing ; both symbols and strings. ; symbol-class: ; One of nil, :program, :ideal, or :compliant-common-lisp, indicating ; the symbol-class of the namex. (All names in the namex have the same ; symbol-class.) ; All event tuples are constructed by make-event-tuple, below. By searching ; for all calls of that function you will ascertain all possible event types ; and namex combinations. You will find the main call in add-event-landmark, ; which is used to store an event landmark in the world. There is another call ; in primordial-world-globals, where the bogus initial value of the ; 'event-landmark 'global-value is created with namex 0 and event type nil. ; Add-event-landmark is called in install-event, which is the standard (only) ; way to finish off an ACL2 event. If you search for calls of install-event ; you will find the normal combinations of event types and namex. There are ; two other calls of add-event-landmark. One, in in primordial-world where it ; is called to create the enter-boot-strap-mode event type landmark with namex ; consisting of the primitive functions and known packages. The other, in ; end-prehistoric-world, creates the exit-boot-strap-mode event type landmark ; with namex 0. ; As of this writing the complete list of type and namex pairs ; is shown below, but the algorithm described above will generate ; it for you if you wish to verify this. ; type namex ; enter-boot-strap-mode *see below ; verify-guards 0 (no names introduced) ; defun fn ; defuns (fn1 ... fnk) ; defaxiom name ; defthm name ; defconst name ; defstobj (name the-live-var fn1 ... fnk) ; [Note: defstobj is the type used for both defstobj and ; defabsstobj events.] ; defmacro name ; defpkg "name" ; deflabel name ; deftheory name ; in-theory 0 (no name introduced) ; in-arithmetic-theory 0 (no name introduced) ; push-untouchable 0 ; regenerate-tau-database 0 (no name introduced) ; remove-untouchable 0 ; reset-prehistory 0 ; set-body 0 (no name introduced) ; table 0 (no name introduced) ; encapsulate (fn1 ... fnk) - constrained fns ; include-book "name" ; exit-boot-strap-mode 0 ; *Enter-boot-strap-mode introduces the names in *primitive-formals- ; and-guards* and *initial-known-package-alist*. So its namex is a ; proper list containing both symbols and strings. ; To save space we do not actually represent each event tuple as a 6-tuple but ; have several different forms. The design of our forms makes the following ; assumptions, aimed at minimizing the number of conses in average usage. (1) ; Most events are not inside other events, i.e., d is often 0. (2) Most events ; use the standard ACL2 event macros, e.g., defun and defthm rather than user ; macros, e.g., DEFN and PROVE-LEMMA. (3) Most events are introduced with the ; :program symbol-class. This last assumption is just the simple observation ; that until ACL2 is reclassified from :program to :logic, the ACL2 ; system code will outweigh any application. (defun signature-fns (signatures) ; Assuming that signatures has been approved by chk-signatures, we ; return a list of the functions signed. Before we added signatures ; of the form ((fn * * STATE) => *) this was just strip-cars. ; Signatures is a list of elements, each of which is either of the ; form ((fn ...) => val) or of the form (fn ...). (cond ((endp signatures) nil) ((consp (car (car signatures))) (cons (car (car (car signatures))) (signature-fns (cdr signatures)))) (t (cons (car (car signatures)) (signature-fns (cdr signatures)))))) (defun make-event-tuple (n d form ev-type namex symbol-class) ; An event tuple is always a cons. Except in the initial case created by ; primordial-world-globals, the car is always either a natural (denoting n and ; implying d=0) or a cons of two naturals, n and d. Its cadr is either a ; symbol, denoting its type and signalling that the cdr is the form, the ; symbol-class is :program and that the namex can be recovered from the form, ; or else the cadr is the pair (ev-type namex . symbol-class) signalling that ; the form is the cddr. ; Generally, the val encodes: ; n - absolute event number ; d - embedded event depth ; form - form that created the event ; ev-type - name of the primitive event macro we use, e.g., defun, defthm, defuns ; namex - name or names introduced (0 is none) ; symbol-class - of names (or nil) ; In what we expect is the normal case, where d is 0 and the form is one of our ; standard ACL2 event macros, this concrete representation costs one cons. If ; d is 0 but the user has his own event macros, it costs 3 conses. ; Warning: If we change the convention that n is the car of a concrete event ; tuple if the car is an integer, then change the default value given getprop ; in max-absolute-event-number. (cons (if (= d 0) n (cons n d)) (if (and (eq symbol-class :program) (consp form) (or (eq (car form) ev-type) (and (eq ev-type 'defuns) (eq (car form) 'mutual-recursion))) (equal namex (case (car form) (defuns (strip-cars (cdr form))) (mutual-recursion (strip-cadrs (cdr form))) ((verify-guards in-theory in-arithmetic-theory regenerate-tau-database push-untouchable remove-untouchable reset-prehistory set-body table) 0) (encapsulate (signature-fns (cadr form))) (otherwise (cadr form))))) form (cons (cons ev-type (cons namex symbol-class)) form)))) (defun access-event-tuple-number (x) ; Warning: If we change the convention that n is (car x) when (car x) ; is an integerp, then change the default value given getprop in ; max-absolute-event-number. (if (integerp (car x)) (car x) (caar x))) (defun access-event-tuple-depth (x) (if (integerp (car x)) 0 (cdar x))) (defun access-event-tuple-type (x) (cond ((symbolp (cdr x)) ;eviscerated event nil) ((symbolp (cadr x)) (if (eq (cadr x) 'mutual-recursion) 'defuns (cadr x))) (t (caadr x)))) (defun access-event-tuple-namex (x) ; Note that namex might be 0, a single name, or a list of names. Included in ; the last case is the possibility of the list being nil (as from an ; encapsulate event introducing no constrained functions). (cond ((symbolp (cdr x)) ;eviscerated event nil) ((symbolp (cadr x)) (case (cadr x) (defuns (strip-cars (cddr x))) (mutual-recursion (strip-cadrs (cddr x))) ((verify-guards in-theory in-arithmetic-theory regenerate-tau-database push-untouchable remove-untouchable reset-prehistory set-body table) 0) (encapsulate (signature-fns (caddr x))) (t (caddr x)))) (t (cadadr x)))) (defun access-event-tuple-form (x) (if (symbolp (cadr x)) (cdr x) (cddr x))) (defun access-event-tuple-symbol-class (x) (if (symbolp (cadr x)) :program (cddadr x))) ; Command Tuples ; When LD has executed a world-changing form, it stores a "command tuple" as ; the new 'global-value of 'command-landmark. These landmarks divide the world ; up into "command blocks" and each command block contains one or or event ; blocks. Command blocks are important when the user queries the system about ; his current state, wishes to undo, etc. Commands are enumerated sequentially ; from 0 with "absolute command numbers." ; We define command tuples in a way analogous to event tuples, although ; commands are perhaps simpler because most of their characteristics are ; inherited from the event tuples in the block. We must store the current ; default-defun-mode so that we can offer to redo :program functions after ubt. ; (A function is offered for redoing if its defun-mode is :program. But the ; function is redone by executing the command that created it. The command may ; recreate many functions and specify a :mode for each. We must re-execute the ; command with the same default-defun-mode we did last to be sure that the ; functions it creates have the same defun-mode as last time.) (defrec command-tuple ; Warning: Keep this in sync with the definitions of ; safe-access-command-tuple-number and pseudo-command-landmarkp in community ; book books/system/pseudo-good-worldp.lisp, and function ; safe-access-command-tuple-form in the ACL2 sources. ; See make-command-tuple for a discussion of defun-mode/form. ; If form is an embedded event form, then last-make-event-expansion is nil ; unless form contains a call of make-event whose :check-expansion field is not ; a cons, in which case last-make-event-expansion is the result of removing all ; make-event calls from form. (number defun-mode/form cbd . last-make-event-expansion) t) (defun make-command-tuple (n defun-mode form cbd last-make-event-expansion) ; Defun-Mode is generally the default-defun-mode of the world in which this ; command is being executed. But there are two possible exceptions. See ; add-command-tuple. ; We assume that most commands are executed with defun-mode :program. So we ; optimize our representation of command tuples accordingly. No form that ; creates a function can have a keyword as its car. (make command-tuple :number n :defun-mode/form (if (eq defun-mode :program) form (cons defun-mode form)) :cbd cbd :last-make-event-expansion last-make-event-expansion)) (defun access-command-tuple-number (x) (access command-tuple x :number)) (defun access-command-tuple-defun-mode (x) (let ((tmp (access command-tuple x :defun-mode/form))) (if (keywordp (car tmp)) (car tmp) :program))) (defun access-command-tuple-form (x) ; See also safe-access-command-tuple-form for a safe version (i.e., with guard ; t). (let ((tmp (access command-tuple x :defun-mode/form))) (if (keywordp (car tmp)) (cdr tmp) tmp))) (defun safe-access-command-tuple-form (x) ; This is just a safe version of access-command-tuple-form. (declare (xargs :guard t)) (let ((tmp (and (consp x) (consp (cdr x)) (access command-tuple x :defun-mode/form)))) (if (and (consp tmp) (keywordp (car tmp))) (cdr tmp) tmp))) (defun access-command-tuple-last-make-event-expansion (x) (access command-tuple x :last-make-event-expansion)) (defun access-command-tuple-cbd (x) (access command-tuple x :cbd)) ; Absolute Event and Command Numbers (defun max-absolute-event-number (wrld) ; This is the maximum absolute event number in use at the moment. It ; is just the number found in the most recently completed event ; landmark. We initialize the event-landmark with number -1 (see ; primordial-world-globals) so that next-absolute-event-number returns ; 0 the first time. (access-event-tuple-number (global-val 'event-landmark wrld))) (defun next-absolute-event-number (wrld) (1+ (max-absolute-event-number wrld))) (defun max-absolute-command-number (wrld) ; This is the largest absolute command number in use in wrld. We ; initialize it to -1 (see primordial-world-globals) so that ; next-absolute-command-number works. (access-command-tuple-number (global-val 'command-landmark wrld))) (defun next-absolute-command-number (wrld) (1+ (max-absolute-command-number wrld))) ; Scanning to find Landmarks (defun scan-to-event (wrld) ; We roll back wrld to the first (list order traversal) event landmark ; on it. (cond ((null wrld) wrld) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value)) wrld) (t (scan-to-event (cdr wrld))))) (defun scan-to-command (wrld) ; Scan to the next binding of 'command-landmark. (cond ((null wrld) nil) ((and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value)) wrld) (t (scan-to-command (cdr wrld))))) (defun scan-to-landmark-number (flg n wrld) ; We scan down wrld looking for a binding of 'event-landmark with n as ; its number or 'command-landmark with n as its number, depending on ; whether flg is 'event-landmark or 'command-landmark. #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (cond ((null wrld) (er hard 'scan-to-landmark-number "We have scanned the world looking for absolute ~ ~#0~[event~/command~] number ~x1 and failed to find it. ~ There are two likely errors. Either ~#0~[an event~/a ~ command~] with that number was never stored or the ~ index has somehow given us a tail in the past rather ~ than the future of the target world." (if (equal flg 'event-landmark) 0 1) n)) ((and (eq (caar wrld) flg) (eq (cadar wrld) 'global-value) (= n (if (eq flg 'event-landmark) (access-event-tuple-number (cddar wrld)) (access-command-tuple-number (cddar wrld))))) #+acl2-metering (meter-maid 'scan-to-landmark-number 500 flg n) wrld) (t (scan-to-landmark-number flg n (cdr wrld))))) ; The Event and Command Indices ; How do we convert an absolute event number into the world created by ; that event? The direct way to do this is to search the world for ; the appropriate binding of 'event-landmark. To avoid much of this ; search, we keep a map from some absolute event numbers to the ; corresponding tails of world. ; Rather than store an entry for each event number we will store one ; for every 10th. Actually, *event-index-interval* determines the ; frequency. This is a completely arbitrary decision. A typical :ppe ; or :ubt will request a tail within 5 event of a saved one, on the ; average. At 8 properties per event (the bootstrap right now is ; running 7.4 properties per event), that's about 40 tuples, each of ; the form (name prop . val). We will always look at name and ; sometimes (1/8 of the time) look at prop and the car of val, which ; says we'll need to swap in about 40+40+1/8(40 + 40) = 90 conses. We ; have no idea how much this costs (and without arguments about ; locality, it might be as bad as 90 pages!), but it seems little ; enough. In any case, this analysis suggests that the decision to ; save every nth world will lead to swapping in only 9n conses. ; Assuming that a big proof development costs 3000 events (that's ; about the size of the Piton proof) and that the initial bootstrap is ; about 2000 (right now it is around 1700), we imagine that we will be ; dealing with 5000 events. So our map from event numbers to ; tails of world will contain about 500 entries. Of interest here is ; the choice of representation for that map. ; The requirement is that it be a map from the consecutive positive ; integers to tails of world (or nil for integers not yet claimed). ; It should operate comfortably with 500 entries. It will be the ; value of the world global, 'event-index, and every time we add a ; new entry (i.e., every 10 events), we will rebind that global. ; Thus, by the time the table has 500 entries we will also be holding ; onto the 499 old versions of the table as well. ; Three representations came immediately to mind: a linear array, an ; association list, and a balanced binary tree. A fourth was invented ; to solve the problem. We discuss all four here. ; Linear Array. If the event-index is an array then it will be ; extremely efficient to "search". We will have to grow the array as ; we go, as we do in load-theory-into-enabled-structure. So by the ; time the array has 500 entries the underlying Common Lisp array will ; probably contain around 750 words. The alist version of the array ; will be of length 500 (ignoring the :HEADER) and consume 1000 ; conses. So in all we'll have about 1750 words tied up in this ; structure. Old versions of the table will share the alist ; representation and cost little. However, we imagine keeping only ; one Common Lisp array object and it will always hold the compressed ; version of the latest index. So old versions of the index will be ; "out of date" and will have to be recompressed upon recovery from a ; :ubt, as done by recompress-global-enabled-structure. This ; complicates the array representation and we have decided to dismiss ; it. ; Alist. If the event-index is an alist it will typically be 500 ; long and contain 1000 conses which are all perfectly shared with old ; copies. Adding new entries is very fast, i.e., 2 conses. Lookup is ; relatively slow: .004 seconds, average with an alist of size 500. ; For comparison purposes, we imagine the following scenario: The user ; starts with a world containing 2000 bootstrap events. He adds ; another 3000 events of his own. Every event, however, provokes ; him to do 10 :ppes to look at old definitions. (We are purposefully ; biasing the scenario toward fast lookup times.) Given the ; convention of saving every 10th tail of world in the index, the ; scenario becomes: The user starts with a index containing 200 ; entries. He grows it to 500 entries. However, between each growth ; step he inspects 100 entries spread more or less evenly throughout ; the interval. If the index is represented by an alist, how long ; does this scenario take? Answer: 77 seconds (running AKCL on a Sun ; 360 with 20Mb). ; Balanced Binary Tree. We have done an extensive study of the use of ; balanced binary trees (bbts) for this application. Using bbts, the ; scenario above requires only 13 seconds. However, bbts use a lot ; more space. In particular, the bbt for 500 entries consumes 2000 ; conses (compared to the alist's 1000 conses). Worse, the bbt for ; 500 shares little of the structure for 499, while the alist shares ; it all. (We did our best with structure sharing between successive ; bbts, it's just that rebalancing the tree after an addition ; frequently destroys the possibility for sharing. Of the 2000 conses ; in the 500 entry bbt, 1028 are new and the rest are shared with the ; 499 bbt.) In particular, to keep all 500 of the bbts will cost us ; 156,000 conses. By contrast, the entire world after a bootstrap ; currently costs about 418,000 conses. ; So we need a representation that shares structure and yet is ; efficiently accessed. Why are alists so slow? Because we have to ; stop at every entry and ask "is this the one?" But that is silly ; because we know that if we're looking for 2453 and we see 3000 then ; we have to skip down 547. That is, our values are all associated ; with consecutive integer indices and the alist is ordered. But we ; could just use a positional indexing scheme. ; Zap Table. A zap table is a linear list of values indexed by ; 0-based positions STARTING FROM THE RIGHT. To enable us to count ; from the right we include, as the first element in the list, the ; maximum index. For example, the zap table that maps each of the ; integers from 0 to 9 to itself is: (9 9 8 7 6 5 4 3 2 1 0). To add ; a new (10th) value to the table, we increment the car by 1 and cons ; the new value to the cdr. Thus, we spend two conses per entry and ; share all other structure. To fetch the ith entry we compute how ; far down the list it is with arithmetic and then retrieve it with ; nth. To our great delight this scheme carries out our scenario in ; 13 seconds, as fast as balanced binary trees, but shares as much ; structure as alists. This is the method we use. (defun add-to-zap-table (val zt) ; Given a zap table, zt, that associates values to the indices ; 0 to n, we extend the table to associate val to n+1. (cond ((null zt) (list 0 val)) (t (cons (1+ (car zt)) (cons val (cdr zt)))))) (defun fetch-from-zap-table (n zt) ; Retrieve the value associated with n in the zap table zt, or ; nil if there is no such association. (cond ((null zt) nil) ((> n (car zt)) nil) (t (nth (- (car zt) n) (cdr zt))))) ; These 7 lines of code took 3 days to write -- because we first ; implemented balanced binary trees and did the experiments described ; above. ; Using zap tables we'll keep an index mapping absolute event numbers ; to tails of world. We'll also keep such an index for commands typed ; by the user at the top-level of the ld loop. The following two ; constants determine how often we save events and commands in their ; respective indices. (defconst *event-index-interval* 10) (defconst *command-index-interval* 10) (defun update-world-index (flg wrld) ; Flg is either 'COMMAND or 'EVENT and indicates which of the two ; indices we are to update. ; In the comments below, we assume flg is 'EVENT. ; This function is called every time we successfully complete the ; processing of an event. We here decide if it is appropriate ; to save a pointer to the resulting world, wrld. If so, we update ; the event-index. If not, we do nothing. Our current algorithm ; is to save every *event-index-interval*th world. That is, if ; *event-index-interval* is 10 then we save the worlds whose ; max-absolute-event-numbers are 0, 10, 20, etc., into slots 0, 1, 2, ; etc. of the index. (cond ((eq flg 'EVENT) (let ((n (max-absolute-event-number wrld))) (cond ((= (mod n *event-index-interval*) 0) (let ((event-index (global-val 'event-index wrld))) ; Things will get very confused if we ever miss a multiple of "10." ; For example, if some bug in the system causes us never to call this ; function on a world with absolute-event-number 10, say, then the ; next multiple we do call it on, e.g., 20, will be stored in the ; slot for 10 and things will be royally screwed. So just to be ; rugged we will confirm the correspondence between what we think ; we're adding and where it will go. (cond ((= (floor n *event-index-interval*) (if (null event-index) 0 (1+ (car event-index)))) (global-set 'event-index (add-to-zap-table wrld event-index) wrld)) (t (er hard 'update-world-index "The event-index and the maximum absolute ~ event number have gotten out of sync! ~ In particular, the next available index ~ is ~x0 but the world has event number ~ ~x1, which requires index ~x2." (if (null event-index) 0 (1+ (car event-index))) n (floor n *event-index-interval*)))))) (t wrld)))) (t (let ((n (max-absolute-command-number wrld))) (cond ((= (mod n *command-index-interval*) 0) (let ((command-index (global-val 'command-index wrld))) (cond ((= (floor n *command-index-interval*) (if (null command-index) 0 (1+ (car command-index)))) (global-set 'command-index (add-to-zap-table wrld command-index) wrld)) (t (er hard 'update-world-index "The command-index and the maximum ~ absolute command number have gotten out ~ of sync! In particular, the next ~ available index is ~x0 but the world has ~ command number ~x1, which requires index ~ ~x2." (if (null command-index) 0 (1+ (car command-index))) n (floor n *command-index-interval*)))))) (t wrld)))))) (defun lookup-world-index1 (n interval index wrld) ; Let index be a zap table that maps the integers 0 to k to worlds. ; Instead of numbering those worlds 0, 1, 2, ..., number them 0, ; 1*interval, 2*interval, etc. So for example, if interval is 10 then ; the worlds are effectively numbered 0, 10, 20, ... Now n is some ; world number (but not necessarily a multiple of interval). We wish ; to find the nearest world in the index that is in the future of the ; world numbered by n. ; For example, if n is 2543 and interval is 10, then we will look for ; world 2550, which will be found in the table at 255. Of course, the ; table might not contain an entry for 255 yet, in which case we return ; wrld. (let ((i (floor (+ n (1- interval)) interval))) (cond ((or (null index) (> i (car index))) wrld) (t (fetch-from-zap-table i index))))) (defun lookup-world-index (flg n wrld) ; This is the general-purpose function that takes an arbitrary ; absolute command or event number (flg is 'COMMAND or 'EVENT) and ; returns the world that starts with the indicated number. (cond ((eq flg 'event) (let ((n (min (max-absolute-event-number wrld) (max n 0)))) (scan-to-landmark-number 'event-landmark n (lookup-world-index1 n *event-index-interval* (global-val 'event-index wrld) wrld)))) (t (let ((n (min (max-absolute-command-number wrld) (max n 0)))) (scan-to-landmark-number 'command-landmark n (lookup-world-index1 n *command-index-interval* (global-val 'command-index wrld) wrld)))))) ; Maintaining the Invariants Associated with Logical Names and Events (defun store-absolute-event-number (namex n wrld boot-strap-flg) ; Associated with each symbolic logical name is the ; 'absolute-event-number. This function is responsible for storing ; that property. Namex is either 0, denoting the empty set, an atom, ; denoting the singleton set containing that atom, or a true-list of ; atoms denoting the corresponding set. ; It is convenient to store the 'predefined property here as well. (cond ((equal namex 0) wrld) ((atom namex) ; If namex is "MY-PKG" we act as though it were the empty list. (cond ((symbolp namex) (putprop namex 'absolute-event-number n (cond (boot-strap-flg (putprop namex 'predefined t wrld)) (t wrld)))) (t wrld))) (t (store-absolute-event-number (or (cdr namex) 0) n (if (stringp (car namex)) wrld (putprop (car namex) 'absolute-event-number n (cond (boot-strap-flg (putprop (car namex) 'predefined t wrld)) (t wrld)))) boot-strap-flg)))) (defun the-namex-symbol-class1 (lst wrld symbol-class1) (cond ((null lst) symbol-class1) ((stringp (car lst)) (the-namex-symbol-class1 (cdr lst) wrld symbol-class1)) (t (let ((symbol-class2 (symbol-class (car lst) wrld))) (cond ((eq symbol-class1 nil) (the-namex-symbol-class1 (cdr lst) wrld symbol-class2)) ((eq symbol-class2 nil) (the-namex-symbol-class1 (cdr lst) wrld symbol-class1)) ((eq symbol-class1 symbol-class2) (the-namex-symbol-class1 (cdr lst) wrld symbol-class1)) (t (er hard 'the-namex-symbol-class "The symbolp elements of the namex argument ~ to add-event-landmark are all supposed to ~ have the same symbol-class, but the first ~ one we found with a symbol-class had class ~ ~x0 and now we've found another with ~ symbol-class ~x1. The list of elements, ~ starting with the one that has ~ symbol-class ~x0 is ~x2." symbol-class2 symbol-class1 lst))))))) (defun the-namex-symbol-class (namex wrld) (cond ((equal namex 0) nil) ((atom namex) (cond ((symbolp namex) (symbol-class namex wrld)) (t nil))) (t (the-namex-symbol-class1 namex wrld nil)))) (defun add-event-landmark (form ev-type namex wrld boot-strap-flg) ; We use a let* below and a succession of worlds just to make clear ; the order in which we store the various properties. We update the ; world index before putting the current landmark on it. This ; effectively adds the previous landmark to the index if it was a ; multiple of our interval. We do this just so that the ; event-landmark we are about to lay down is truly the last thing we ; do. Reflection on this issue leads to the conclusion that it is not ; really important whether the index entry is inside or outside of the ; landmark, in the case of event-landmarks. (let* ((n (next-absolute-event-number wrld)) (wrld1 (store-absolute-event-number namex n wrld boot-strap-flg)) (wrld2 (update-world-index 'event wrld1)) (wrld3 (global-set 'event-landmark (make-event-tuple n (length (global-val 'embedded-event-lst wrld)) form ev-type namex (the-namex-symbol-class namex wrld2)) wrld2))) wrld3)) ; Decoding Logical Names (defun scan-to-defpkg (name wrld) ; We wish to give meaning to stringp logical names such as "MY-PKG". We do it ; in an inefficient way: we scan the whole world looking for an event tuple of ; type DEFPKG and namex name. We know that name is a known package and that it ; is not one in *initial-known-package-alist*. (cond ((null wrld) nil) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value) (eq (access-event-tuple-type (cddar wrld)) 'DEFPKG) (equal name (access-event-tuple-namex (cddar wrld)))) wrld) (t (scan-to-defpkg name (cdr wrld))))) (defun scan-to-include-book (full-book-name wrld) ; We wish to give meaning to stringp logical names such as "arith". We ; do it in an inefficient way: we scan the whole world looking for an event ; tuple of type INCLUDE-BOOK and namex full-book-name. (cond ((null wrld) nil) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value) (eq (access-event-tuple-type (cddar wrld)) 'include-book) (equal full-book-name (access-event-tuple-namex (cddar wrld)))) wrld) (t (scan-to-include-book full-book-name (cdr wrld))))) (defun assoc-equal-cadr (x alist) (cond ((null alist) nil) ((equal x (cadr (car alist))) (car alist)) (t (assoc-equal-cadr x (cdr alist))))) (defun multiple-assoc-terminal-substringp1 (x i alist) (cond ((null alist) nil) ((terminal-substringp x (caar alist) i (1- (length (caar alist)))) (cons (car alist) (multiple-assoc-terminal-substringp1 x i (cdr alist)))) (t (multiple-assoc-terminal-substringp1 x i (cdr alist))))) (defun multiple-assoc-terminal-substringp (x alist) ; X and the keys of the alist are presumed to be strings. This function ; compares x to the successive keys in the alist, succeeding on any key that ; contains x as a terminal substring. Unlike assoc, we return the list of all ; pairs in the alist with matching keys. (multiple-assoc-terminal-substringp1 x (1- (length x)) alist)) (defun possibly-add-lisp-extension (str) ; String is a string. If str ends in .lisp, return it. Otherwise, tack .lisp ; onto the end and return that. (let ((len (length str))) (cond ((and (> len 5) (eql (char str (- len 5)) #\.) (eql (char str (- len 4)) #\l) (eql (char str (- len 3)) #\i) (eql (char str (- len 2)) #\s) (eql (char str (- len 1)) #\p)) str) (t (string-append str ".lisp"))))) (defun decode-logical-name (name wrld) ; Given a logical name, i.e., a symbol with an 'absolute-event-number property ; or a string naming a defpkg or include-book, we return the tail of wrld ; starting with the introductory event. We return nil if name is illegal. (cond ((symbolp name) (cond ((eq name :here) (scan-to-event wrld)) (t (let ((n (getprop name 'absolute-event-number nil 'current-acl2-world wrld))) (cond ((null n) nil) (t (lookup-world-index 'event n wrld))))))) ((stringp name) ; Name may be a package name or a book name. (cond ((find-non-hidden-package-entry name (global-val 'known-package-alist wrld)) (cond ((find-package-entry name *initial-known-package-alist*) ; These names are not DEFPKGd and so won't be found in a scan. They ; are introduced by absolute event number 0. (lookup-world-index 'event 0 wrld)) (t (scan-to-defpkg name wrld)))) (t (let ((hits (multiple-assoc-terminal-substringp (possibly-add-lisp-extension name) (global-val 'include-book-alist wrld)))) ; Hits is a subset of the include-book-alist. The form of each ; element is (full-book-name user-book-name familiar-name ; cert-annotations . ev-lst-chk-sum). (cond ((and hits (null (cdr hits))) (scan-to-include-book (car (car hits)) wrld)) (t nil)))))) (t nil))) (defun er-decode-logical-name (name wrld ctx state) ; Like decode-logical-name but causes an error rather than returning nil. (let ((wrld1 (decode-logical-name name wrld))) (cond ((null wrld1) (let ((hits (and (stringp name) (not (find-non-hidden-package-entry name (global-val 'known-package-alist wrld))) (multiple-assoc-terminal-substringp (possibly-add-lisp-extension name) (global-val 'include-book-alist wrld))))) ; Hits is a subset of the include-book-alist. The form of each ; element is (full-book-name user-book-name familiar-name ; cert-annotations . ev-lst-chk-sum). (cond ((and hits (cdr hits)) (er soft ctx "More than one book matches the name ~x0, in particular ~&1. We ~ therefore consider ~x0 not to be a logical name and insist ~ that you use an unambiguous form of it. See :DOC logical-name." name (strip-cars hits))) (t (er soft ctx "The object ~x0 is not a logical name. See :DOC logical-name." name))))) (t (value wrld1))))) (defun renew-lemmas (fn lemmas) ; We copy lemmas, which is a list of rewrite rules, deleting those whose ; runes have fn as their base symbol. These are, we believe, all and only ; the rules stored by the event which introduced fn. (cond ((null lemmas) nil) ((eq (base-symbol (access rewrite-rule (car lemmas) :rune)) fn) (renew-lemmas fn (cdr lemmas))) (t (cons (car lemmas) (renew-lemmas fn (cdr lemmas)))))) (defun renew-name/erase (name old-getprops wrld) ; Name is a symbol, old-getprops is the list returned by getprops on name, ; i.e., an alist dotting properties to values. We map over that list and ; "unbind" every property of name in wrld. We do not touch 'GLOBAL-VALUE ; because that is not a property affected by an event (consider what would ; happen if the user defined and then redefined COMMAND-LANDMARK). Similarly, ; we do not touch 'table-alist or 'table-guard. See the list of properties ; specially excepted by new-namep. (cond ((null old-getprops) wrld) (t (renew-name/erase name (cdr old-getprops) (if (member-eq (caar old-getprops) '(global-value table-alist table-guard)) wrld (putprop name (caar old-getprops) *acl2-property-unbound* wrld)))))) ;; RAG - Hmmm, this code assumes it knows all of the properties stored ;; on a function symbol. Sad. I added 'CLASSICALP to the list. (defun renew-name/overwrite (name old-getprops wrld) ; Name is a function symbol, old-getprops is the list returned by getprops on ; name, i.e., an alist dotting properties to values. We map over that list and ; "unbind" those properties of name in wrld that were stored by the event ; introducing name. ; Note: Even when the ld-redefinition-action specifies :overwrite we sometimes ; change it to :erase (see maybe-coerce-overwrite-to-erase). Thus, this ; function is actually only called on function symbols, not constants or stobjs ; or stobj-live-vars. The erase version, above, is called on those redefinable ; non-functions. ; Finally, we back up our claim that name must be a function symbol. The ; reason is that renew-name is the only place that calls renew-name/overwrite ; and it only does that when the renewal-mode is :overwrite or ; :reclassifying-overwrite. Now renew-name is only called by ; chk-redefineable-namep which sets the renewal-mode using ; redefinition-renewal-mode. ; Finally, if you inspect redefinition-renewal-mode you can see that it only ; returns :overwrite or :reclassifying-overwrite on functions. The proof of ; this for the :overwrite cases is tedious but pretty straightforward. Most ; branches through redefinition-renewal-mode signal an error prohibiting the ; redefinition attempt, a few explicitly return :erase, and the normal cases in ; which :overwrite could be returned are all coming out of calls to ; maybe-coerce-overwrite-to-erase, which returns :erase unless the old and new ; type of the event is FUNCTION. ; The harder part of the proof (that only functions get renewal-mode :overwrite ; or :reclassifying-overwrite) is when we return :reclassifying-overwrite. ; Whether redefinition-renewal-mode does that depends on the argument ; reclassifyingp supplied by chk-redefineable-namep, which in turn depends on ; what value of reclassifyingp is supplied to chk-redefineable-namep by its ; only caller, chk-just-new-name, which in turn just passes in the value it is ; supplied by its callers, of which there are many. However, all but ; chk-acceptable-defuns1 supply reclassifyingp of nil. ; So we know that we reclassify only function symbols and we know that only ; function symbols get :overwrite or :reclassifying-overwrite for their ; renewal-modes. (cond ((null old-getprops) wrld) ((eq (caar old-getprops) 'redefined) (renew-name/overwrite name (cdr old-getprops) wrld)) ((member-eq (caar old-getprops) '(FORMALS STOBJS-IN STOBJS-OUT SYMBOL-CLASS NON-EXECUTABLEP SIBLINGS LEVEL-NO TAU-PAIR QUICK-BLOCK-INFO PRIMITIVE-RECURSIVE-DEFUNP CONSTRAINEDP HEREDITARILY-CONSTRAINED-FNNAMES #+:non-standard-analysis CLASSICALP DEF-BODIES NTH-UPDATE-REWRITER-TARGETP INDUCTION-MACHINE JUSTIFICATION UNNORMALIZED-BODY CONSTRAINT-LST RECURSIVEP TYPE-PRESCRIPTIONS GUARD SPLIT-TYPES-TERM ABSOLUTE-EVENT-NUMBER ; It is tempting to add CONGRUENT-STOBJ-REP to this list. But it is a property ; of stobjs, not functions, so that isn't necessary. ; Note: If you delete RUNIC-MAPPING-PAIRS from this list you must reconsider ; functions like current-theory-fn which assume that if a name has the ; REDEFINED property then its runic-mapping-pairs has been set to ; *acl2-property-unbound*. RUNIC-MAPPING-PAIRS ; This property is stored by defstobj on all supporting functions. STOBJ-FUNCTION)) ; The properties above are stored by the defun, constrain or defstobj ; that introduced name and we erase them. (renew-name/overwrite name (cdr old-getprops) (putprop name (caar old-getprops) *acl2-property-unbound* wrld))) ((eq (caar old-getprops) 'lemmas) ; We erase from the lemmas property just those rules stored by the introductory event. (renew-name/overwrite name (cdr old-getprops) (putprop name 'lemmas (renew-lemmas name (getprop name 'lemmas nil 'current-acl2-world wrld)) wrld))) ((member-eq (caar old-getprops) ; As of this writing, the property in question must be one of the following, ; since name is a function symbol. Note that these are not created by the ; introductory event of name (which must have been a defun or constrain) and ; hence are left untouched here. '(GLOBAL-VALUE LINEAR-LEMMAS FORWARD-CHAINING-RULES ELIMINATE-DESTRUCTORS-RULE COARSENINGS CONGRUENCES INDUCTION-RULES DEFCHOOSE-AXIOM TABLE-GUARD ; functions names can also be table names TABLE-ALIST ; functions names can also be table names PREDEFINED DEFAXIOM-SUPPORTER ATTACHMENT ; see Essay on Defattach re: :ATTACHMENT-DISALLOWED CLAUSE-PROCESSOR TAU-PAIR-SAVED POS-IMPLICANTS NEG-IMPLICANTS UNEVALABLE-BUT-KNOWN SIGNATURE-RULES-FORM-1 SIGNATURE-RULES-FORM-2 BIG-SWITCH TAU-BOUNDERS-FORM-1 TAU-BOUNDERS-FORM-2 )) (renew-name/overwrite name (cdr old-getprops) wrld)) (t (illegal 'renew-name/overwrite "We thought we knew all the properties stored by events ~ introducing redefinable function names, but we don't know about ~ the property ~x0." (list (cons #\0 (caar old-getprops))))))) (defun renew-name (name renewal-mode wrld) ; We make it sort of appear as though name is sort of new in wrld. Ah, to be ; young again... We possibly erase all properties of name (depending on the ; renewal-mode, which must be :erase, :overwrite or :reclassifying-overwrite), ; and we put a 'redefined property on name. Note that we always put the ; 'redefined property, even if name already has that property with that value, ; because one of our interests in this property is in stop-event, which uses it ; to identify which names have been redefined in this event. ; The value of the 'redefined property is (renewal-mode . old-sig), ; where old-sig is either the internal form signature of name if name ; is function and is otherwise nil. ; By storing the renewal-mode we make it possible to recover exactly how the ; final world was obtained from the initial one. For purposes of renewal, we ; treat renewal-mode :reclassifying-overwrite as :overwrite; the only ; difference is that we store the :reclassifying-overwrite in the 'redefined ; property. The only time :reclassifying-overwrite is the renewal-mode is when ; a :program function is being reclassified to an identical-defp :logic ; function. (putprop name 'redefined (cons renewal-mode (cond ((and (symbolp name) (function-symbolp name wrld)) (list name (formals name wrld) (stobjs-in name wrld) (stobjs-out name wrld))) (t nil))) (cond ((eq renewal-mode :erase) (renew-name/erase name (getprops name 'current-acl2-world wrld) wrld)) ((or (eq renewal-mode :overwrite) (eq renewal-mode :reclassifying-overwrite)) (renew-name/overwrite name (getprops name 'current-acl2-world wrld) wrld)) (t wrld)))) (defun renew-names (names renewal-mode wrld) (cond ((endp names) wrld) (t (renew-names (cdr names) renewal-mode (renew-name (car names) renewal-mode wrld))))) (defun collect-redefined (wrld ans) ; We return a list of all redefined names down to the next event-landmark ; except those redefined in the :reclassifying-overwrite mode. (Quoting from a ; comment in renew-name: The only time :reclassifying-overwrite is the ; renewal-mode is when a :program function is being reclassified to an ; identical-defp :logic function.) (cond ((or (null wrld) (and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value))) ans) ((and (eq (cadar wrld) 'redefined) (consp (cddar wrld)) (not (eq (car (cddar wrld)) :reclassifying-overwrite))) (collect-redefined (cdr wrld) (cons (caar wrld) ans))) (t (collect-redefined (cdr wrld) ans)))) (defun scrunch-eq (lst) (cond ((null lst) nil) ((member-eq (car lst) (cdr lst)) (scrunch-eq (cdr lst))) (t (cons (car lst) (scrunch-eq (cdr lst)))))) (defun print-redefinition-warning (wrld ctx state) ; If the 'ld-redefinition-action of state says we should :warn and some names ; were redefined, then we print a warning. See :DOC ld-redefinition-action. ; Note that if the action specifies :warn and a system function is ; redefined, then a query is made. Provided the user approves, the system ; function is redefined and then this warning is printed because the action ; says :warn. This is a bit odd since we try, in general, to avoid warning ; if we have querried. But we don't want to have to determine now if the ; redefined names are system functions, so we warn regardless. (cond ((warning-disabled-p "Redef") state) ((let ((act (f-get-global 'ld-redefinition-action state))) (and (consp act) (or (eq (car act) :warn) (eq (car act) :warn!)))) (let ((redefs (scrunch-eq (reverse (collect-redefined (cond ((and (consp wrld) (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value)) (cdr wrld)) (t (er hard 'print-redefinition-warning "This function is supposed to be called on a world ~ that starts at an event landmark, but this world ~ starts with (~x0 ~x1 . val)." (caar wrld) (cadar wrld)))) nil))))) (cond (redefs (warning$ ctx ("Redef") "~&0 redefined.~%" redefs)) (t state)))) (t state))) (defun initialize-summary-accumulators (state) ; This function is the standard way to start an ACL2 event. We push a 0 onto ; each of the timers, thus protecting the times accumulated by any superior ; (e.g., an encapsulate) and initializing an accumulator for this event. The ; accumulated times AND warnings are printed by print-time-summary. ; Note that some state globals also need to be initialized when starting an ; event, but that is accomplished using the macro save-event-state-globals. #+(and (not acl2-loop-only) acl2-rewrite-meter) ; for stats on rewriter depth (setq *rewrite-depth-max* 0) (progn$ ; If these time-tracker calls are changed, update :doc time-tracker ; accordingly. (time-tracker :tau :end) ; in case interrupt prevented preceding summary (time-tracker :tau :init :times '(1 5) :interval 10 :msg (concatenate 'string (if (f-get-global 'get-internal-time-as-realtime state) "Elapsed realtime" "Elapsed runtime") " in tau is ~st secs; see :DOC time-tracker-tau.~|~%")) (pprogn (cond ((null (cdr (get-timer 'other-time state))) ; top-level event (mv-let (x state) (main-timer state) (declare (ignore x)) state)) (t ; inbetween events (increment-timer 'other-time state))) (push-timer 'other-time 0 state) (push-timer 'prove-time 0 state) (push-timer 'print-time 0 state) (push-timer 'proof-tree-time 0 state) (push-warning-frame state)))) (defun print-warnings-summary (state) (mv-let (warnings state) (pop-warning-frame t state) (io? summary nil state (warnings) (cond ((member-eq 'warnings (f-get-global 'inhibited-summary-types state)) state) ((null warnings) state) (t (let ((channel (proofs-co state))) (mv-let (col state) (fmt1 "Warnings: ~*0~%" (list (cons #\0 (list "None" "~s*" "~s* and " "~s*, " warnings))) 0 channel state nil) (declare (ignore col)) state))))))) (defun print-time-summary (state) ; Print the time line, e.g., ;Time: 0.15 seconds (prove: 0.00, print: 0.02, other: 0.13) ; assuming that the cursor is at the left margin. ; Once upon a time we considered extending fmt so that it knew how to ; print timers. However, fmt needs to know which column it is left in ; and returns that to the user. Thus, if fmt printed a timer (at ; least in the most convenient way) the user could detect the number ; of digits in it. So we are doing it this way. (pprogn (let ((skip-proof-tree-time ; Note that get-timer is untouchable, and :pso calls trans-eval, hence ; translate1; so we must bind skip-proof-tree-time up here, not under the io? ; call below. (and (member-eq 'proof-tree (f-get-global 'inhibit-output-lst state)) (= (car (get-timer 'proof-tree-time state)) 0)))) (io? summary nil state (skip-proof-tree-time) (cond ((member-eq 'time (f-get-global 'inhibited-summary-types state)) state) (t (let ((channel (proofs-co state))) (pprogn (princ$ "Time: " channel state) (push-timer 'total-time 0 state) (add-timers 'total-time 'prove-time state) (add-timers 'total-time 'print-time state) (add-timers 'total-time 'proof-tree-time state) (add-timers 'total-time 'other-time state) (print-timer 'total-time channel state) (pop-timer 'total-time nil state) (princ$ " seconds (prove: " channel state) (print-timer 'prove-time channel state) (princ$ ", print: " channel state) (print-timer 'print-time channel state) (if skip-proof-tree-time state (pprogn (princ$ ", proof tree: " channel state) (print-timer 'proof-tree-time channel state))) (princ$ ", other: " channel state) (print-timer 'other-time channel state) (princ$ ")" channel state) (newline channel state))))))) ; The function initialize-summary-accumulators makes corresponding calls of ; push-timer, not under an io? call. So the balancing calls of pop-timer below ; also are not under an io? call. (pop-timer 'prove-time t state) (pop-timer 'print-time t state) (pop-timer 'proof-tree-time t state) (pop-timer 'other-time t state))) (defun prover-steps (state) ; Returns nil if no steps were taken (or if state global 'last-step-limit is ; nil, though that may be impossible). Otherwise returns the (positive) number ; of steps taken, with one exception: If the number of steps exceeded the ; starting limit, then we return the negative of the starting limit. (let* ((rec (f-get-global 'step-limit-record state)) (start (assert$ rec (access step-limit-record rec :start))) (last-limit (assert$ start (f-get-global 'last-step-limit state)))) (cond ((and last-limit (not (int= start last-limit))) (cond ((eql last-limit -1) ; more than start steps (assert$ (natp start) ; else start <= -2; impossible (- start))) (t (- start last-limit)))) (t nil)))) (defun print-steps-summary (steps state) (cond ((null steps) state) (t (io? summary nil state (steps) (cond ((member-eq 'steps (f-get-global 'inhibited-summary-types state)) state) (t (let ((channel (proofs-co state))) (pprogn (princ$ "Prover steps counted: " channel state) (cond ((<= steps 0) (pprogn (princ$ "More than " channel state) (princ$ (- steps) channel state))) (t (princ$ steps channel state))) (newline channel state))))))))) (defun print-rules-summary (state) (let ((acc-ttree (f-get-global 'accumulated-ttree state))) (mv-let (col state) (io? summary nil (mv col state) (acc-ttree) (let ((channel (proofs-co state))) (cond ((member-eq 'rules (f-get-global 'inhibited-summary-types state)) (mv 0 state)) (t (let ((runes (merge-sort-runes (all-runes-in-ttree acc-ttree nil)))) (fmt1 "Rules: ~y0~|" (list (cons #\0 runes)) 0 channel state nil))))) :default-bindings ((col 0))) (declare (ignore col)) (pprogn (f-put-global 'accumulated-ttree nil state) ; Since we've already printed the appropriate rules, there is no need to print ; them again the next time we want to print rules. That is why we set the ; accumulated-ttree to nil here. If we ever want certify-book, say, to be able ; to print rules when it fails, then we should use a stack of ttrees rather ; than a single accumulated-ttree. state)))) #+acl2-rewrite-meter (defun merge-cdr-> (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((> (cdr (car l1)) (cdr (car l2))) (cons (car l1) (merge-cdr-> (cdr l1) l2))) (t (cons (car l2) (merge-cdr-> l1 (cdr l2)))))) #+acl2-rewrite-meter (defun merge-sort-cdr-> (l) (cond ((null (cdr l)) l) (t (merge-cdr-> (merge-sort-cdr-> (evens l)) (merge-sort-cdr-> (odds l)))))) (defconst *gag-prefix* "([ ") (defconst *gag-suffix* (msg "])~|")) (defun gag-start-msg (cl-id pool-name) (msg "~@0A key checkpoint~#1~[ while proving ~@2 ~ (descended from ~@3)~/~]:" *gag-prefix* (if cl-id 0 1) pool-name (and cl-id (tilde-@-clause-id-phrase cl-id)))) (defun print-gag-info (info state) (fms "~@0~%~Q12~|" (list (cons #\0 (tilde-@-clause-id-phrase (access gag-info info :clause-id))) (cons #\1 (prettyify-clause (access gag-info info :clause) (let*-abstractionp state) (w state))) (cons #\2 (term-evisc-tuple nil state))) (proofs-co state) state nil)) (defun set-checkpoint-summary-limit-fn (val state) (if (or (eq val nil) (eq val t) (natp val) (and (consp val) (or (null (car val)) (natp (car val))) (or (null (cdr val)) (natp (cdr val))))) (let ((val (if (natp val) (cons val val) val))) (pprogn (f-put-global 'checkpoint-summary-limit val state) (value val))) (er soft 'set-checkpoint-summary-limit "Illegal value, ~x0, for checkpoint-summary-limit; see :DOC ~ set-checkpoint-summary-limit." val))) (defmacro set-checkpoint-summary-limit (val) ":Doc-Section switches-parameters-and-modes control printing of key checkpoints upon a proof's failure~/ ~l[set-gag-mode] for a discussion of key checkpoints. ~bv[] Examples: ; (Default) When a proof fails, print all key checkpoints before ; induction but at most 3 key checkpoints after induction: (set-checkpoint-summary-limit (nil . 3)) ; When a proof fails, print at most 3 key checkpoints before ; induction but all 3 key checkpoints after induction: (set-checkpoint-summary-limit (3 . nil)) ; When a proof fails, print at most 3 key checkpoints before ; induction and at most 5 key checkpoints after induction: (set-checkpoint-summary-limit (3 . 5)) ; When a proof fails, print at most 3 key checkpoints before ; induction and at most 3 key checkpoints after induction: (set-checkpoint-summary-limit (3 . 3)) ; or equivalently, (set-checkpoint-summary-limit 3) ; When a proof fails, print all key checkpoints: (set-checkpoint-summary-limit (nil . nil)) ; or equivalently, (set-checkpoint-summary-limit nil) ; When a proof fails, print no information at all about key checkpoints: (set-checkpoint-summary-limit t)~/ General Forms: (set-checkpoint-summary-limit (n1 . n2)) (set-checkpoint-summary-limit n) (set-checkpoint-summary-limit t) ~ev[] where each of ~c[n1] and ~c[n2] is a natural number or ~c[nil]. For the second form, ~c[n] can be a natural number or ~c[nil] and is treated as ~c[(n . n)]. The value ~c[t] inhibits all printing of checkpoint summary information. The values ~c[n1] and ~c[n2] determine printing of key checkpoints generated before the first induction and generated after the first induction, respectively, where at most ~c[n1] or ~c[n2] (respectively) such key checkpoints are printed unless the value is ~c[nil], in which case there is no limitation. The argument ~c[x] for ~c[set-checkpoint-summary-limit], as described above, may be quoted, i.e. supplied as ~c['x] or ~c[(quote x)]. Thus, you may use the keyword form (~pl[keyword-commands]) if you prefer, for example: ~bv[] :set-checkpoint-summary-limit (nil . 3) ~ev[]" (let ((x (if (and (consp val) (eq (car val) 'quote)) val (list 'quote val)))) `(set-checkpoint-summary-limit-fn ,x state))) (defun print-gag-stack-rev (lst limit orig-limit msg chan state) ; Lst is the reverse of the :abort-stack, :top-stack, or :sub-stack field of a ; gag-state. (cond ((endp lst) state) ((eql limit 0) (fms "Note: ~#2~[Not shown~/There~] ~#0~[is~#2~[ the~/~] ~n1~#2~[~/ ~ additional~] key checkpoint~/are~#2~[ the~/~] ~n1~#2~[~/ ~ additional~] key checkpoints~] ~@3. See :DOC ~ set-checkpoint-summary-limit to ~#4~[change the number ~ printed~/print this key checkpoint~/print some or all of these ~ key checkpoints~].~|" (list (cons #\0 lst) (cons #\1 (length lst)) (cons #\2 (if (eql orig-limit 0) 0 1)) (cons #\3 msg) (cons #\4 (cond ((not (eql orig-limit 0)) 0) ((null (cdr lst)) 1) (t 2)))) chan state nil)) (t (pprogn (print-gag-info (car lst) state) (print-gag-stack-rev (cdr lst) (and limit (1- limit)) orig-limit msg chan state))))) (defun maybe-print-nil-goal-generated (gag-state chan state) (cond ((eq (access gag-state gag-state :abort-stack) 'empty-clause) (fms "[NOTE: A goal of ~x0 was generated. See :DOC nil-goal.]~|" (list (cons #\0 nil)) chan state nil)) (t (newline chan state)))) (defun print-gag-state1 (gag-state state) (cond ((eq (f-get-global 'checkpoint-summary-limit state) t) state) (gag-state (let* ((chan (proofs-co state)) (abort-stack (access gag-state gag-state :abort-stack)) (top-stack0 (access gag-state gag-state :top-stack)) (top-stack (if (consp abort-stack) abort-stack top-stack0)) (sub-stack (access gag-state gag-state :sub-stack)) (some-stack (or sub-stack ; We use top-stack0 here instead of top-stack because if the only non-empty ; stack is the :abort-stack, then presumably the proof completed modulo :by ; hints that generated :bye tags in the ttree. top-stack0)) (forcing-round-p (and some-stack (let ((cl-id (access gag-info (car some-stack) :clause-id))) (not (eql 0 (access clause-id cl-id :forcing-round))))))) (cond (some-stack (pprogn (fms "---~|The key checkpoint goal~#0~[~/s~], below, may help you to ~ debug this failure. See :DOC failure and see :DOC ~ set-checkpoint-summary-limit.~@1~|---~|" (list (cons #\0 (if (or (and top-stack sub-stack) (cdr top-stack) (cdr sub-stack)) 1 0)) (cons #\1 (if forcing-round-p " Note that at least one checkpoint is in a ~ forcing round, so you may want to see a full ~ proof." ""))) chan state nil) (cond (top-stack (let ((limit (car (f-get-global 'checkpoint-summary-limit state)))) (pprogn (fms "*** Key checkpoint~#0~[~/s~] ~#1~[before reverting ~ to proof by induction~/at the top level~]: ***" (list (cons #\0 top-stack) (cons #\1 (if (consp abort-stack) 0 1))) chan state nil) (cond (sub-stack (newline chan state)) (t (maybe-print-nil-goal-generated gag-state chan state))) (print-gag-stack-rev (reverse top-stack) limit limit "before induction" chan state)))) (t state)) (cond (sub-stack (let ((limit (cdr (f-get-global 'checkpoint-summary-limit state)))) (pprogn (fms "*** Key checkpoint~#0~[~/s~] under a top-level ~ induction ***" (list (cons #\0 sub-stack)) chan state nil) (maybe-print-nil-goal-generated gag-state chan state) (print-gag-stack-rev (reverse sub-stack) limit limit "under a top-level induction" chan state)))) (t state)))) (t ; no checkpoints; aborted (fms #-acl2-par "*** Note: No checkpoints to print. ***~|" #+acl2-par "*** Note: No checkpoints from gag-mode to print. ***~|" nil chan state nil))))) (t ; no checkpoints; proof never started state))) (defun erase-gag-state (state) ; Avoid repeated printing of the gag state, e.g. for a theorem under several ; levels of encapsulate or under certify-book. We set 'gag-state here rather ; than directly inside print-gag-state because gag-state is untouchable and ; translate11 is called on in the process of running :psog. (pprogn (f-put-global 'gag-state-saved (f-get-global 'gag-state state) state) (f-put-global 'gag-state nil state))) (defun print-gag-state (state) (io? error nil state () (let ((gag-state (f-get-global 'gag-state state))) (pprogn (erase-gag-state state) (print-gag-state1 gag-state state))))) #+acl2-par (defun clause-id-is-top-level (cl-id) (and (null (access clause-id cl-id :pool-lst)) (equal (access clause-id cl-id :forcing-round) 0))) #+acl2-par (defun clause-id-is-induction-round (cl-id) (and (access clause-id cl-id :pool-lst) (equal (access clause-id cl-id :forcing-round) 0))) #+acl2-par (defun clause-id-is-forcing-round (cl-id) ; Note that we do not have a recognizer for inductions that occur while ; forcing. (not (equal (access clause-id cl-id :forcing-round) 0))) #+acl2-par (defun print-acl2p-checkpoints1 (checkpoints top-level-banner-printed induction-banner-printed forcing-banner-printed) (declare (ignorable top-level-banner-printed induction-banner-printed forcing-banner-printed)) (cond ((atom checkpoints) nil) (t (let* ((cl-id (caar checkpoints)) (prettyified-clause (cdar checkpoints)) (top-level-banner-printed (or top-level-banner-printed (if (and (not top-level-banner-printed) (clause-id-is-top-level cl-id)) (prog2$ (cw "~%*** ACL2(p) key checkpoint[s] at the ~ top level: ***~%") t) top-level-banner-printed))) (induction-banner-printed (or induction-banner-printed (if (and (not induction-banner-printed) (clause-id-is-induction-round cl-id)) (prog2$ (cw "~%*** ACL2(p) key checkpoint[s] under ~ induction: ***~%") t) induction-banner-printed))) (forcing-banner-printed (or forcing-banner-printed (if (and (not forcing-banner-printed) (clause-id-is-forcing-round cl-id)) (prog2$ (cw "~%*** ACL2(p) key checkpoint[s] under a ~ forcing round: ***~%") t) forcing-banner-printed)))) (progn$ (cw "~%~s0~%" (string-for-tilde-@-clause-id-phrase cl-id)) (cw "~x0~%" prettyified-clause) (print-acl2p-checkpoints1 (cdr checkpoints) top-level-banner-printed induction-banner-printed forcing-banner-printed)))))) #+acl2-par (deflock *acl2p-checkpoint-saving-lock*) #+acl2-par (defun erase-acl2p-checkpoints-for-summary (state) (with-acl2p-checkpoint-saving-lock (f-put-global 'acl2p-checkpoints-for-summary nil state))) #+acl2-par (defun print-acl2p-checkpoints (state) (with-acl2p-checkpoint-saving-lock ; Technically, this lock acquisition is unnecessary, because we only print ; acl2p checkpoints after we have finished the waterfall (ACL2(p) is operating ; with only a single thread at that point). However, we go ahead and do it ; anyway, as an example of good programming practice. (prog2$ (if (and (f-get-global 'waterfall-parallelism state) (f-get-global 'acl2p-checkpoints-for-summary state)) (prog2$ (cw "~%~%Printing the ACL2(p) key checkpoints that were encountered ~ during the proof attempt (and pushed for induction or ~ sub-induction). Note that some of these checkpoints may have ~ been later proven by induction or sub-induction. Thus, you ~ must decide for yourself which of these checkpoints are ~ relevant to debugging your proof.~%~%") (print-acl2p-checkpoints1 (reverse (f-get-global 'acl2p-checkpoints-for-summary state)) nil nil nil)) nil) ; At first we followed the precedent set by erase-gag-state and tried only ; clearing the set of ACL2(p) checkpoints to print whenever this function is ; called. However, we noticed that succesful proof attempts then do not clear ; the saved checkpoints. As such, we also clear the checkpoints in defthm-fn1. (erase-acl2p-checkpoints-for-summary state)))) (defun character-alistp (x) ":Doc-Section ACL2::ACL2-built-ins recognizer for association lists with characters as keys~/ ~c[(Character-alistp x)] is true if and only if ~c[x] is a list of pairs of the form ~c[(cons key val)] where ~c[key] is a ~ilc[characterp].~/~/" (declare (xargs :guard t)) (cond ((atom x) (eq x nil)) (t (and (consp (car x)) (characterp (car (car x))) (character-alistp (cdr x)))))) (defun tilde-@p (arg) (declare (xargs :guard t)) (or (stringp arg) (and (consp arg) (stringp (car arg)) (character-alistp (cdr arg))))) (defun print-failure (erp ctx state) (pprogn (print-gag-state state) #+acl2-par (print-acl2p-checkpoints state) (io? error nil state (ctx erp) (let ((channel (proofs-co state))) (pprogn (error-fms-channel nil ctx "~@0See :DOC failure." (list (cons #\0 (if (tilde-@p erp) erp ""))) channel state) (newline channel state) (fms *proof-failure-string* nil channel state nil)))))) (defstub initialize-event-user (ctx qbody state) state) (defstub finalize-event-user (ctx qbody state) state) (defdoc initialize-event-user ":Doc-Section switches-parameters-and-modes user-supplied code to initiate ~il[events]~/ This utility is intended for system hackers, not standard ACL2 users. ~l[finalize-event-user] to see how to supply code to be run at the end of ~il[events]. We assume familiarity with ~c[finalize-event-user]; here we focus on how to supply code for the beginning as well as the end of events. As with ~il[finalize-event-user], you attach your own function of argument list ~c[(ctx qbody state)] to ~c[initialize-event-user]. (~l[finalize-event-user] for discussion of ~c[ctx] and ~c[body].) The attachment should return ~c[state] and have a trivial guard, requiring (implicitly) only that ~c[state] satisfies ~c[state-p] unless you use trust tags to avoid that requirement. For example: ~bv[] (defattach initialize-event-user initialize-event-user-test) ~ev[] Why would you want to do this? Presumably you are building a system on top of ACL2 and you want to track your own data. For example, suppose you want to save the time in some ~il[state] global variable, ~c[my-time]. You could do the following. ~bv[] (defun my-init (ctx body state) (declare (xargs :stobjs state :guard-hints ((\"Goal\" :in-theory (enable read-run-time)))) (ignore ctx body)) (mv-let (seconds state) (read-run-time state) (f-put-global 'start-time seconds state))) (defun my-final (ctx body state) (declare (xargs :stobjs state :guard-hints ((\"Goal\" :in-theory (enable read-run-time)))) (ignore ctx body)) (mv-let (seconds state) (read-run-time state) (prog2$ (if (boundp-global 'start-time state) (cw \"Time: ~~x0 seconds~~%\" (- seconds (fix (@ start-time)))) (cw \"BIG SURPRISE!~~%\")) (f-put-global 'end-time seconds state)))) (defattach initialize-event-user my-init) (defattach finalize-event-user my-final) ~ev[] Here is an abbreviated log, showing the time being printed at the end. ~bv[] ACL2 !>(thm (equal (append (append x y) x y x y x y x y) (append x y x y x y x y x y))) *1 (the initial Goal, a key checkpoint) is pushed for proof by induction. .... ACL2 Error in ( THM ...): See :DOC failure. ******** FAILED ******** Time: 869/100 seconds ACL2 !> ~ev[] ~/~/") (defdoc finalize-event-user ":Doc-Section switches-parameters-and-modes user-supplied code to complete ~il[events], e.g., with extra summary output~/ This utility is intended for system hackers, not standard ACL2 users. ACL2 prints summaries at the conclusions of processing ~il[events] (unless summaries are inhibited; ~pl[set-inhibit-output-lst] and also ~pl[set-inhibited-summary-types]). You may arrange for processing to take place just after the summary, by defining a function with argument list ~c[(ctx body state)] that returns one value, namely ~c[state]. We describe ~c[ctx] and ~c[body] at the end below, but you may simply prefer to ignore these arguments.) Your function should normally be a ~il[guard]-verified ~c[:]~ilc[logic] mode function with no guard other than that provided by the input requirement on ~ilc[state], that is, ~c[(state-p state)]; but later below we discuss how to avoid this requirement. You then attach (~pl[defattach]) your function to the function ~c[finalize-event-user]. The following example illustrates how this all works. ~bv[] (defun finalize-event-user-test (ctx body state) (declare (xargs :stobjs state) (ignore ctx body)) (cond ((and (boundp-global 'abbrev-evisc-tuple state) (open-output-channel-p *standard-co* :character state)) (pprogn (if (eq (f-get-global 'abbrev-evisc-tuple state) :DEFAULT) (princ$ \"Abbrev-evisc-tuple has its default value.~~%\" *standard-co* state) (princ$ \"Abbrev-evisc-tuple has been modified.~~%\" *standard-co* state)))) (t state))) (defattach finalize-event-user finalize-event-user-test) ~ev[] After admission of the two events above, an event summary will conclude with extra printout, for example: ~bv[] Note: Abbrev-evisc-tuple has its default value. ~ev[] If the attachment function (above, ~c[finalize-event-user-test]) does not meet all the requirements stated above, then you can use the ~c[:skip-checks] argument of ~ilc[defattach] to get around the requirement, as illustrated by the following example. ~bv[] (defun finalize-event-user-test2 (state) (declare (xargs :stobjs state :mode :program) (ignore ctx body)) (observation 'my-test \"~~|Value of term-evisc-tuple: ~~x0~~|\" (f-get-global 'term-evisc-tuple state))) (defttag t) ; needed for :skip-checks t (defattach (finalize-event-user finalize-event-user-test2) :skip-checks t) ~ev[] So for example: ~bv[] ACL2 !>(set-term-evisc-tuple (evisc-tuple 2 7 nil nil) state) (:TERM) ACL2 !>(defconst *foo6* '(a b c)) Summary Form: ( DEFCONST *FOO6* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ACL2 Observation in MY-TEST: Value of term-evisc-tuple: (NIL 2 7 NIL) *FOO6* ACL2 !> ~ev[] Note that (as of this writing) the macro ~ilc[observation] expands to a call of a ~c[:]~ilc[program]-mode function. Thus, the trick shown above involving ~c[:skip-checks] allows the use of ~c[:program]-mode functions; for example, you can print with ~ilc[fmt]. See community book ~c[books/misc/defattach-bang.lisp] for a variant of ~ilc[defattach] that uses ~ilc[ec-call] to avoid issues of ~il[guard] verification. Also ~pl[initialize-event-user], which discusses the handling of ~il[state] globals by that utility as well as by ~c[finalize-event-user].~/ Finally, as promised above, we briefly describe the arguments ~c[ctx] and ~c[body]. These are the arguments passed to the call of macro ~c[with-ctx-summarized] under which ~c[finalize-event-user] (or ~c[initialize-event-user]) was called. Thus, they are unevaluated expressions. For example, system function ~c[defthm-fn1] has a body of the following form. ~bv[] (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defthm name)) (let ((wrld (w state)) (ens (ens state)) ..... ~ev[] Thus, when ~c[initialize-event-user] and ~c[finalize-event-user] are called on behalf of ~ilc[defthm], ~c[ctx] is the s-expression ~bv[] (if (output-in-infixp state) event-form (cons 'defthm name)) ~ev[] while ~c[body] is the following s-expression (with most code elided). ~bv[] (let ((wrld (w state)) (ens (ens state)) ..... ~ev[] You might find it helpful to use ~ilc[trace$] to get a sense of ~c[ctx] and ~c[body], for example, ~c[(trace$ finalize-event-user)].~/") (defun lmi-seed (lmi) ; The "seed" of an lmi is either a symbolic name or else a term. In ; particular, the seed of a symbolp lmi is the lmi itself, the seed of ; a rune is its base symbol, the seed of a :theorem is the term ; indicated, and the seed of an :instance or :functional-instance is ; obtained recursively from the inner lmi. ; Warning: If this is changed so that runes are returned as seeds, it ; will be necessary to change the use of filter-atoms below. (cond ((atom lmi) lmi) ((eq (car lmi) :theorem) (cadr lmi)) ((or (eq (car lmi) :instance) (eq (car lmi) :functional-instance)) (lmi-seed (cadr lmi))) (t (base-symbol lmi)))) (defun lmi-techs (lmi) (cond ((atom lmi) nil) ((eq (car lmi) :theorem) nil) ((eq (car lmi) :instance) (add-to-set-equal "instantiation" (lmi-techs (cadr lmi)))) ((eq (car lmi) :functional-instance) (add-to-set-equal "functional instantiation" (lmi-techs (cadr lmi)))) (t nil))) (defun lmi-seed-lst (lmi-lst) (cond ((null lmi-lst) nil) (t (add-to-set-eq (lmi-seed (car lmi-lst)) (lmi-seed-lst (cdr lmi-lst)))))) (defun lmi-techs-lst (lmi-lst) (cond ((null lmi-lst) nil) (t (union-equal (lmi-techs (car lmi-lst)) (lmi-techs-lst (cdr lmi-lst)))))) (defun filter-atoms (flg lst) ; If flg=t we return all the atoms in lst. If flg=nil we return all ; the non-atoms in lst. (cond ((null lst) nil) ((eq (atom (car lst)) flg) (cons (car lst) (filter-atoms flg (cdr lst)))) (t (filter-atoms flg (cdr lst))))) (defun print-runes-summary (ttree channel state) ; This should be called under (io? summary ...). (let ((runes (merge-sort-runes (all-runes-in-ttree ttree nil)))) (mv-let (col state) (fmt1 "Rules: ~y0~|" (list (cons #\0 runes)) 0 channel state nil) (declare (ignore col)) state))) (defun use-names-in-ttree (ttree) (let* ((objs (tagged-objects :USE ttree)) (lmi-lst (append-lst (strip-cars (strip-cars objs)))) (seeds (lmi-seed-lst lmi-lst)) (lemma-names (filter-atoms t seeds))) (sort-symbol-listp lemma-names))) (defun by-names-in-ttree (ttree) (let* ((objs (tagged-objects :BY ttree)) (lmi-lst (append-lst (strip-cars objs))) (seeds (lmi-seed-lst lmi-lst)) (lemma-names (filter-atoms t seeds))) (sort-symbol-listp lemma-names))) (defrec clause-processor-hint (term stobjs-out . verified-p) nil) (defun clause-processor-fns (cl-proc-hints) (cond ((endp cl-proc-hints) nil) (t (cons (ffn-symb (access clause-processor-hint (car cl-proc-hints) :term)) (clause-processor-fns (cdr cl-proc-hints)))))) (defun cl-proc-names-in-ttree (ttree) (let* ((objs (tagged-objects :CLAUSE-PROCESSOR ttree)) (cl-proc-hints (strip-cars objs)) (cl-proc-fns (clause-processor-fns cl-proc-hints))) (sort-symbol-listp cl-proc-fns))) (defun print-hint-events-summary (ttree channel state) ; This should be called under (io? summary ...). (flet ((make-rune-like-objs (kwd lst) (and lst ; optimization for common case (pairlis$ (make-list (length lst) :INITIAL-ELEMENT kwd) (pairlis$ lst nil))))) (let* ((use-lst (use-names-in-ttree ttree)) (by-lst (by-names-in-ttree ttree)) (cl-proc-lst (cl-proc-names-in-ttree ttree)) (lst (append (make-rune-like-objs :BY by-lst) (make-rune-like-objs :CLAUSE-PROCESSOR cl-proc-lst) (make-rune-like-objs :USE use-lst)))) (cond (lst (mv-let (col state) (fmt1 "Hint-events: ~y0~|" (list (cons #\0 lst)) 0 channel state nil) (declare (ignore col)) state)) (t state))))) (defun print-splitter-rules-summary (cl-id clauses ttree channel state) ; When cl-id is nil, we are printing for the summary, and clauses is ignored. ; Otherwise we are printing during a proof under waterfall-msg1, for gag-mode. (let ((if-intro (merge-sort-runes (tagged-objects 'splitter-if-intro ttree))) (case-split (merge-sort-runes (tagged-objects 'splitter-case-split ttree))) (immed-forced (merge-sort-runes (tagged-objects 'splitter-immed-forced ttree)))) (cond ((or if-intro case-split immed-forced) (with-output-lock ; only necessary if cl-id is non-nil (pprogn (cond (cl-id (newline channel state)) (t state)) (mv-let (col state) (fmt1 "Splitter ~s0 (see :DOC splitter)~@1~s2~|~@3~@4~@5" (list (cons #\0 (if cl-id "note" "rules")) (cons #\1 (if cl-id ; Since we are printing during a proof (see comment above) but not already ; printing the clause-id, we do so now. This is redundant if (f-get-global ; 'print-clause-ids state) is true, but necessary when parallelism is enabled ; for #+acl2-par, and anyhow, adds a bit of clarity. ; We leave it to waterfall-msg1 to track print-time, so we avoid calling ; waterfall-print-clause-id. (msg " for ~@0 (~x1 subgoal~#2~[~/s~])" (tilde-@-clause-id-phrase cl-id) (length clauses) clauses) "")) (cons #\2 (if cl-id "." ":")) (cons #\3 (cond (case-split (msg " case-split: ~y0" case-split)) (t ""))) (cons #\4 (cond (immed-forced (msg " immed-forced: ~y0" immed-forced)) (t ""))) (cons #\5 (cond (if-intro (msg " if-intro: ~y0" if-intro)) (t "")))) 0 channel state nil) (declare (ignore col)) (cond (cl-id (newline channel state)) (t state)))))) (t state)))) (defun print-rules-and-hint-events-summary (state) (pprogn (io? summary nil state () (let ((channel (proofs-co state)) (acc-ttree (f-get-global 'accumulated-ttree state)) (inhibited-summary-types (f-get-global 'inhibited-summary-types state))) (pprogn (cond ((member-eq 'rules inhibited-summary-types) state) (t (print-runes-summary acc-ttree channel state))) (cond ((member-eq 'hint-events inhibited-summary-types) state) (t (print-hint-events-summary acc-ttree channel state))) (cond ((member-eq 'splitter-rules inhibited-summary-types) state) (t (print-splitter-rules-summary nil nil acc-ttree channel state)))))) ; Since we've already printed from the accumulated-ttree, there is no need to ; print again the next time we want to print rules or hint-events. That is why ; we set the accumulated-ttree to nil here. If we ever want certify-book, say, ; to be able to print rules and hint-events when it fails, then we should use a ; stack of ttrees rather than a single accumulated-ttree. (f-put-global 'accumulated-ttree nil state))) (defun last-prover-steps (state) ":Doc-Section Miscellaneous the number of prover steps most recently taken~/ For discussions of prover step limits, ~l[set-prover-step-limit] and ~pl[with-prover-step-limit]. The value of the form ~c[(last-prover-steps state)] indicates the number of prover steps taken, in the sense described below, for the most recent context in which an event summary would normally be printed. Note that the value of ~c[(last-prover-steps state)] is updated for all ~il[events], and for all other forms such as calls of ~ilc[thm] or ~ilc[certify-book], that would print a summary ~-[] regardless of whether or not such output is inhibited (~pl[set-inhibit-output-lst] and ~pl[set-inhibited-summary-types]). In particular, the value is updated (typically to ~c[nil]) for ~ilc[table] ~il[events], even when no summary is printed; for example, the value is updated to ~c[nil] for ~c[table] events such as ~c[(]~ilc[logic]~c[)], ~c[(]~ilc[program]~c[)], and even calls of ~ilc[set-prover-step-limit]. The value of ~c[(last-prover-steps state)] is determined as follows, based on the most recent summary context (as described above): ~bq[] ~c[nil], if no prover steps were taken; else, the (positive) number of steps taken, if the number of steps did not exceed the starting limit; else, the negative of the starting limit. ~eq[]~/ We conclude with a remark for advanced users who wish to invoke ~c[last-prover-steps] in the development of utilities that track prover steps. Suppose that you want to write a utility that takes some action based on the number of prover steps performed by the first ~c[defun] event that is generated, among others, for example the number of prover steps taken to admit ~c[f1] in the following example. ~bv[] (progn (defun f1 ...) (defun f2 ...)) ~ev[] A solution is to record the steps taken by the first ~ilc[defun] before executing subsequent events, as follows (~pl[make-event]). ~bv[] (progn (defun f1 ...) (make-event (pprogn (f-put-global 'my-step-count (last-prover-steps state) state) (value '(value-triple nil)))) (defun f2 ...)) ~ev[]~/" (f-get-global 'last-prover-steps state)) (defun print-summary (erp noop-flg ctx state) ; This function prints the Summary paragraph. Part of that paragraph includes ; the timers. Time accumulated before entry to this function is charged to ; 'other-time. We then pop the timers, adding their accumulations to the newly ; exposed time. This has the effect of charging superior events for the time ; used by their inferiors. ; For simplicity, we do the above and all other computations even if we are not ; to print the summary or parts of it. For example, we handle ; pop-warning-frame regardless of whether or not we print the warning summary. ; If erp is non-nil, the "event" caused an error and we do not scan for ; redefined names but we do print the failure string. If noop-flg is t then ; the installed world did not get changed by the "event" (e.g., the "event" was ; redundant or was not really an event but was something like a call of (thm ; ...)) and we do not scan the most recent event block for redefined names. ; If erp is a message, as recognized by tilde-@p, then that message will be ; printed by the call below of print-failure. #+(and (not acl2-loop-only) acl2-rewrite-meter) ; for stats on rewriter depth (cond ((atom ctx)) ((symbolp (cdr ctx)) (cond ((not (eql *rewrite-depth-max* 0)) (setq *rewrite-depth-alist* (cons (cons (intern (symbol-name (cdr ctx)) "ACL2") ; We intern into the ACL2 package so that our tools can read this alist back in ; without needing a DEFPKG to be executed first. The name is really all we ; care about here anyhow; all we would do with it is to search for it in the ; indicated file. *rewrite-depth-max*) *rewrite-depth-alist*)) (setq *rewrite-depth-max* 0)))) ((eq (car ctx) 'certify-book) (let* ((bookname (extend-pathname (f-get-global 'connected-book-directory state) (cdr ctx) state)) (filename (concatenate 'string bookname ".lisp"))) (with-open-file (str filename :direction :output :if-exists :rename-and-delete) (format str "~s~%" (cons filename (merge-sort-cdr-> *rewrite-depth-alist*))))) (setq *rewrite-depth-alist* nil))) #-acl2-loop-only (dmr-flush) (let ((wrld (w state)) (steps (prover-steps state))) (pprogn (f-put-global 'last-prover-steps steps state) (cond ((or (ld-skip-proofsp state) (output-ignored-p 'summary state)) (pprogn (increment-timer 'other-time state) (if (or erp noop-flg) state (print-redefinition-warning wrld ctx state)) (pop-timer 'prove-time t state) (pop-timer 'print-time t state) (pop-timer 'proof-tree-time t state) (pop-timer 'other-time t state) (mv-let (warnings state) (pop-warning-frame nil state) (declare (ignore warnings)) state))) (t ; Even if 'summary is inhibited, we still use io? below, and inside some ; functions below, because of its window hacking and saved-output functions. (pprogn (increment-timer 'other-time state) (if (or erp noop-flg) state (print-redefinition-warning wrld ctx state)) (pprogn (io? summary nil state () (let ((channel (proofs-co state))) (cond ((member-eq 'header (f-get-global 'inhibited-summary-types state)) state) (t (pprogn (newline channel state) (princ$ "Summary" channel state) (newline channel state)))))) (io? summary nil state (ctx) (let ((channel (proofs-co state))) (cond ((member-eq 'form (f-get-global 'inhibited-summary-types state)) state) (t (mv-let (col state) (fmt1 "Form: " nil 0 channel state nil) (mv-let (col state) (fmt-ctx ctx col channel state) (declare (ignore col)) (newline channel state)))))))) (print-rules-and-hint-events-summary state) ; call of io? is inside (pprogn (print-warnings-summary state) (print-time-summary state) (print-steps-summary steps state) (progn$ ; If the time-tracker call below is changed, update :doc time-tracker ; accordingly. (time-tracker :tau :print? :min-time 1 :msg (concatenate 'string "For the proof above, the total " (if (f-get-global 'get-internal-time-as-realtime state) "realtime" "runtime") " spent in the tau system was ~st seconds. See :DOC ~ time-tracker-tau.~|~%")) ; At one time we put (time-tracker :tau :end) here. But in community book ; books/hints/basic-tests.lisp, the recursive proof attempt failed just below ; (add-custom-keyword-hint :recurse ...), apparently because the time-tracker ; wasn't initialized for tag :tau when the proof resumed. It's harmless anyhow ; to avoid :end here; after all, we invoke :end before invoking :init anyhow, ; in case the proof was aborted without printing this part of the summary. state)) (cond (erp (pprogn (print-failure erp ctx state) (cond ((f-get-global 'proof-tree state) (io? proof-tree nil state (ctx) (pprogn (f-put-global 'proof-tree-ctx (cons :failed ctx) state) (print-proof-tree state)))) (t state)))) (t (pprogn #+acl2-par (erase-acl2p-checkpoints-for-summary state) state))) (f-put-global 'proof-tree nil state))))))) (defun with-prover-step-limit-fn (limit form no-change-flg) ; See the Essay on Step-limits. (let ((protected-form `(state-global-let* ((step-limit-record (make step-limit-record :start wpsl-limit :strictp wpsl-strictp :sub-limit nil))) (check-vars-not-free (wpsl-limit wpsl-strictp) ,form)))) `(mv-let (wpsl-limit wpsl-strictp) ; for child environment (let ((limit ,limit)) (cond ((or (null limit) (eql limit *default-step-limit*)) (mv *default-step-limit* nil)) ((eq limit :start) ; We inherit the limit and strictness from the parent environment. ; Warning: Keep the following code in sync with initial-step-limit. (let ((rec (f-get-global 'step-limit-record state))) (cond (rec (mv (or (access step-limit-record rec :sub-limit) (f-get-global 'last-step-limit state)) ; Warning: Keep the following case in sync with step-limit-strictp. (access step-limit-record rec :strictp))) (t (let ((limit (step-limit-from-table (w state)))) (mv limit (< limit *default-step-limit*))))))) ((and (natp limit) (< limit *default-step-limit*)) (mv limit t)) (t (mv 0 ; arbitrary (er hard! 'with-prover-step-limit "Illegal value for ~x0, ~x1. See :DOC ~ with-prover-step-limit." 'with-prover-step-limit limit))))) ,(cond (no-change-flg `(state-global-let* ((last-step-limit wpsl-limit)) ,protected-form)) (t `(let ((wpsl-old-limit ; existing value of last-step-limit (f-get-global 'last-step-limit state))) (pprogn (f-put-global 'last-step-limit wpsl-limit state) ; new step-limit (mv-let (erp val state) (check-vars-not-free (wpsl-old-limit) ,protected-form) (let* ((steps-taken ; Even if the value of 'last-step-limit is -1, the following difference ; correctly records the number of prover steps taken, where we consider it a ; step to cause an error at the transition in step-limit from 0 to -1. After ; all, the sub-event will say "more than", which assumes that this final step ; is counted. (- wpsl-limit (f-get-global 'last-step-limit state))) (new-step-limit (cond ((< wpsl-old-limit steps-taken) -1) (t (- wpsl-old-limit steps-taken))))) (pprogn (f-put-global 'last-step-limit new-step-limit state) (cond (erp (mv erp val state)) ; Next we consider the case that the step-limit is exceeded after completion of ; a sub-event of a compound event, for example, between the two defthm events ; below. ; (set-prover-step-limit 100) ; (encapsulate ; () ; (with-prover-step-limit 500 ; (defthm foo ; (equal (append (append x y) z) ; (append x y z)))) ; (defthm bar (equal (car (cons x y)) x))) ((and (eql new-step-limit -1) (step-limit-strictp state)) (step-limit-error t)) (t (value val))))))))))))) #+acl2-loop-only (defmacro with-prover-step-limit (limit form &optional (actual-form 'nil actual-form-p)) ; Warning: Do not attempt to move the extra flag argument to the normal last ; position one might expect of an optional argument, without considering the ; need to change several functions (e.g., chk-embedded-event-form, ; elide-locals-rec, and destructure-expansion) that currently treat ; with-prover-step-limit as with-output is treated: expecting the event form to ; be in the last position. ; See the Essay on Step-limits. ; Form should evaluate to an error triple. A value of :START for limit says ; that we use the current limit, i.e., the value of state global ; 'last-step-limit if the value of state global 'step-limit-record is not nil, ; else the value from the acl2-defaults-table; see initial-step-limit. ":Doc-Section Other limit the number of steps for proofs~/ Logically, ~c[(with-prover-step-limit expr form)] is equivalent to ~c[form], except that if the number of ``prover steps'' executed during evaluation of ~c[form] exceeds a bound specified by the value of ~c[expr], then that proof will abort. ~l[set-prover-step-limit] for a related utility that sets the limit on prover steps globally instead of setting it for just one form, and for a discussion of the notion of ``prover steps'', which could change in future ACL2 releases. For a related utility based on time instead of prover steps, ~pl[with-prover-time-limit]; but as discussed in the ~il[documentation] for ~ilc[set-prover-step-limit], there is at best a loose connection between the counting of steps and ~ilc[with-prover-time-limit]. The arguments of ~c[(with-prover-step-limit expr form)] are evaluated. However, the (optional) argument ~c[flg] is not evaluated in ~c[(with-prover-step-limit expr flg form)]. Depending on the machine you are using, you may have less than a half-hour of time before the number of prover steps exceeds the maximum limit on prover steps that may be imposed, which is one less than the value of ~c[*default-step-limit*]. But there is no limit unless you explicitly call ~c[with-prover-step-limit] or ~ilc[set-prover-step-limit]. For examples of how step-limits work besides those presented below, see the community book ~c[books/misc/misc2/step-limits.lisp]. For a utility that returns an indicator of the number of prover steps most recently taken, ~pl[last-prover-steps]. Note that ~c[with-prover-step-limit] may not be called inside definitions, and that it is simply the identity macro in raw Lisp. However, ~c[with-prover-step-limit!] may be called in place of ~c[with-prover-step-limit], with the effect described here even in raw Lisp. ~bv[] Examples: ; Limit (mini-proveall) to 100,000 prover steps (which happens to suffice) (with-prover-step-limit 100000 (mini-proveall)) ; Same as above for the inner call of with-prover-step-limit; so the ; mini-proveall call completes, but then we get an error because the second ; argument of the outer with-prover-step-limit call took more than 200 ; steps. (with-prover-step-limit 200 (with-prover-step-limit 100000 (mini-proveall))) ; Same as preceding form just above, except that this time there is no error, ; because the inner call of with-prover-step-limit has an extra argument ; of t inserted into the second argument position, which specifies that this ; entire inner call is treated as though it uses no prover steps. (with-prover-step-limit 200 (with-prover-step-limit 100000 t (mini-proveall))) ; The inner call limits (mini-proveall) to 200 prover steps, which fails ; almost immediately. (with-prover-step-limit 100000 (with-prover-step-limit 200 (mini-proveall))) ; Do not limit the number of prover steps, regardless of such a limit imposed ; globally or by the surrounding context. (with-prover-step-limit nil (mini-proveall)) ; Same as just above (indeed, nil above is converted to ; *default-step-limit*): (with-prover-step-limit *default-step-limit* (mini-proveall)) ; Advanced example: Limit the indicated theorem to 100 steps, and when the ; proof does not complete, then put down a label instead. (mv-let (erp val state) (with-prover-step-limit 100 (thm (equal (append (append x x) x) (append x x x)))) (if erp (deflabel foo :doc \"Attempt failed.\") (value (list :succeeded-with val)))) ; Use extra argument of t to avoid ``charging'' steps for the indicated ; form. (with-prover-step-limit 500 (encapsulate () (with-prover-step-limit 500 t ; Don't charge prover steps for this first defthm. (defthm test1 (equal (append x (append y z)) (append (append x y) z)) :rule-classes nil)) (defthm test2 (equal (append x (append y z)) (append (append x y) z)) :rule-classes nil)))~/ General Forms: (with-prover-step-limit expr form) (with-prover-step-limit expr flg form) ~ev[] where ~c[form] is an arbitrary form to evaluate, and ~c[expr] evaluates to one of the following: ~c[nil]; a natural number not exceeding the value of ~c[*default-step-limit*]; or the special value, ~c[:START]. The optional extra argument in the second position, ~c[flg], must be Boolean if supplied. If the value of ~c[expr] is a natural number less than the value of ~c[*default-step-limit*], then that value is the maximum number of prover steps permitted during evaluation of ~c[form] before an error occurs. If however the value of ~c[expr] is ~c[nil] or is the value of ~c[*default-step-limit*], then no limit is placed on the number of prover steps during processing of ~c[form]. Otherwise, the value of ~c[expr] should be the keyword ~c[:START], which is intended for use by the ACL2 implementation and may have semantics that change with new ACL2 versions. Finally we describe the optional extra Boolean argument, ~c[flg]. If ~c[flg] is ~c[nil] or omitted, then a running count of prover steps is maintained after ~c[form] is evaluated; otherwise, that count is not affected by evaluation of ~c[form]. To see how this works when ~c[flg] is nil or omitted, consider an event of shape ~c[(progn form1 form2)], where we are tracking prover steps (say, because of a superior call of ~c[with-prover-step-limit]). If ~c[n] is the number of prover steps available when the ~ilc[progn] form is entered, and if ~c[s] prover steps are executed while evaluating ~c[form1], then ~c[n-s] steps are available for evaluation of ~c[form2] provided ~c[s] does not exceed ~c[n]; otherwise, an error occurs. In particular, this is the case if ~c[form1] is an event ~c[(with-prover-step-limit k form1')]. However, if ~c[form1] is an event ~c[(with-prover-step-limit k t form1')], then because of the extra argument of ~c[t], no steps are ``charged'' to ~c[form]; that is, all ~c[n] steps, rather than ~c[n-s] steps, are available for evaluation of ~c[form2].~/" (declare (xargs :guard (or (not actual-form-p) (booleanp form)))) (cond (actual-form-p (with-prover-step-limit-fn limit actual-form form)) (t (with-prover-step-limit-fn limit form nil)))) #-acl2-loop-only (defmacro with-prover-step-limit (limit form &optional (actual-form 'nil actual-form-p)) (declare (ignore limit)) (cond (actual-form-p actual-form) (t form))) (defmacro with-prover-step-limit! (limit form &optional no-change-flg) (declare (xargs :guard (booleanp no-change-flg))) (with-prover-step-limit-fn limit form no-change-flg)) ; Start development of with-ctx-summarized. But first we need to define ; set-w!. ; Essay on the proved-functional-instances-alist ; The world global 'proved-functional-instances-alist caches information about ; theorems proved on behalf of functional instantiation, in order to avoid the ; cost of re-proving such theorems. In this Essay we track the flow of ; information to and from that world global. ; The above world global is a list of the following records. (defrec proved-functional-instances-alist-entry ; Constraint-event-name is the name of an event such that the functional ; instance of that event's constraint (i.e., function's constraint or axiom's ; 'theorem property) by restricted-alist was proved on behalf of the event ; named behalf-of-event-name. Note that behalf-of-event-name could be 0, e.g., ; for a verify-guards event. We arrange that restricted-alist is sorted; see ; the comment in event-responsible-for-proved-constraint. (constraint-event-name restricted-alist . behalf-of-event-name) t) ; In a nutshell, these records have the following life cycle: ; (1) They are created by hint translation, appealing to the existing value of ; world global 'proved-functional-instances-alist to prune the list. ; (2) They go into tag-trees when hints are applied; see calls of ; add-to-tag-tree with tags :use and :by in apply-use-hint-clauses and ; apply-top-hints-clause1, respectively. ; (3) They are extracted from those tag-trees when events are installed. ; We focus now on (1). Hint translation creates these records with functions ; translate-use-hint and translate-by-hint. Translate-use-hint returns an ; object of the form ; (lmi-lst (hyp1 ... hypn) cl k event-names new-entries) ; while translate-by-hint returns an object of the form ; ; (lmi-lst thm-cl-set constraint-cl k event-names new-entries). ; In each case, new-entries is a list of ; proved-functional-instances-alist-entry records created by ; translate-lmi/functional-instance; just follow the call tree down from ; translate-use-hint or translate-by-hint to translate-lmi and then ; translate-lmi/functional-instance. But following the call tree further, we ; see that new-entries ultimately comes from relevant-constraints, which in ; turn passes along the new-entries created by relevant-constraints1-axioms and ; relevant-constraints1. These two functions use the already-existing records ; from world global 'proved-functional-instances-alist to prune the set of ; generated constraints (and save the behalf-of-event-name fields justifying ; such pruning for a suitable message -- see the call of tilde-@-lmi-phrase in ; apply-top-hints-clause-msg1). These same two functions ; (relevant-constraints1-axioms and relevant-constraints1) also return a list ; of new proved-functional-instances-alist-entry records. ; The records created as above are missing the :behalf-of-event-name field ; (i.e., its value is nil), and that is how they go into tag-trees as ; components of values associated with :use and :by tags. That missing field ; is eventually filled in by ; supply-name-for-proved-functional-instances-alist-entry in ; proved-functional-instances-from-tagged-objects, which is called by ; install-event in order to update world global ; 'proved-functional-instances-alist. ; End of Essay on the proved-functional-instances-alist (defun supply-name-for-proved-functional-instances-alist-entry (name lst) (cond ((endp lst) nil) (t (cons (change proved-functional-instances-alist-entry (car lst) :behalf-of-event-name name) (supply-name-for-proved-functional-instances-alist-entry name (cdr lst)))))) (defun proved-functional-instances-from-tagged-objects (name lst) ; For context, see the Essay on the proved-functional-instances-alist. ; Returns a list of proved-functional-instances-alist-entry records. Lst is a ; list of values generated by calls ; (cdr (assoc-eq key (access prove-spec-var pspv :hint-settings))) ; where key is :use or :by, and where each member of lst is a value returned by ; translate-use-hint and translate-by-hint, ; (list x0 x1 x2 x3 x4 new-entries) ; -- although in the case of :by, this value could be an atom. (cond ((null lst) nil) ((atom (cdr (car lst))) (proved-functional-instances-from-tagged-objects name (cdr lst))) (t (append (supply-name-for-proved-functional-instances-alist-entry name (nth 5 (car lst))) (proved-functional-instances-from-tagged-objects name (cdr lst)))))) ; Statistical and related information related to image size. ; ; Here is some information collected while first creating a small version near ; the completion of Version 1.8. ; ; At one point we had the following size statistic, using GCL 2.0: ; ; -rwxrwxr-x 1 kaufmann 13473876 May 1 11:27 small-saved_acl2 ; ; We were able to account for nearly all of this 13.5 megabytes by the following ; reckoning. Some associated code follows. ; ; 3.2 Raw GCL 2.0 ; 2.9 Additional space from loading ACL2 object files ; [note: not much more than Nqthm, less than Pc-Nqthm!] ; 3.7 Conses (327648) from (count-objects (w state)), less those that ; are from constants: (* 12 (- 327648 (- 21040 145))). Note: ; 36,236 = (length (w state)) ; 0.9 Extra conses (72888) generated by (get sym *CURRENT-ACL2-WORLD-KEY*); ; see code below. The first few such numbers, in order, are: ; ((4207 . EVENT-LANDMARK) (3806 . COMMAND-LANDMARK) ; (3734 . CLTL-COMMAND) (424 . EVENT-INDEX) (384 . COMMAND-INDEX) ; (103 . PC-COMMAND-TABLE) (76 . PRIN1-WITH-SLASHES1) (75 . NTH) ; (74 . NONCONSTRUCTIVE-AXIOM-NAMES) (72 . UPDATE-NTH)) ; 0.3 Extra conses (23380) generated on symbol-plists; see code below ; 0.9 Mystery conses, (- 5.8 (+ 3.7 0.9 0.3)). Where does 5.8 come from? ; It's (* SYSTEM:LISP-PAGESIZE (- 1617 200)), where 1617 is the number ; of cons pages in the ACL2 image and 200 is the number in an image ; obtained by loading the .o files. ; 0.7 Extra cell space, other than cons, over the image obtained from .o ; files only (including string, fixnum, ..., arrays for enabled ; structures and type-set tables, ...): ; (* SYSTEM:LISP-PAGESIZE ; (- (+ 34 162 1 2 73 6 20) ; (+ 3 74 1 1 27 6 18))) ; 0.4 Other extra space, which is probably NOT related to TMP1.o space ; (because presumably that space doesn't show up in (room)): ; (* SYSTEM:LISP-PAGESIZE ; (- (+ 6 107) ; (+ 1 11))) ; 0.4 TMP1.o size calculated by: (- 12195924 11823188), the difference ; in sizes of two images built using (acl2::load-acl2 t) followed by ; (initialize-acl2 nil nil t), but using a patch the second time that ; avoided loading TMP1.o. ; --- ; 13.4 Total ; ; NOTE: From ; ; ACL2>(length (w state)) ; 36351 ; ; we suspect that it would not be easy to significantly reduce the figure from ; (count-objects (w state)) above. ; ; Some relevant code: ; ; ;;;;;;;;;;;;;;; count.lisp ; ; (eval-when (load) ; (si::allocate 'fixnum 100))) ; ; (defvar *monitor-count* nil) ; ; (defvar *string-count* ; (make-array$ '(1) :initial-element (the fixnum 0) :element-type 'fixnum)) ; ; (defvar *cons-count* ; (make-array$ '(1) :initial-element (the fixnum 0) :element-type 'fixnum)) ; ; (defvar *count-hash-table* ; (make-hash-table :test 'eq :size 500000)) ; ; (defun increment-string-count (len) ; (declare (type fixnum len)) ; (cond ((and *monitor-count* ; (= (the fixnum ; (logand (the fixnum (aref *string-count* 0)) ; (the fixnum 4095))) ; 0)) ; (format t "String count: ~s" (aref *string-count* 0)))) ; (setf (aref (the (array fixnum (1)) *string-count*) ; 0) ; (the fixnum (1+ (the fixnum ; (+ (the fixnum len) ; (the fixnum (aref *string-count* 0))))))) ; t) ; ; (defun increment-cons-count () ; (cond ((and *monitor-count* ; (= (the fixnum ; (logand (the fixnum (aref *cons-count* 0)) ; (the fixnum 4095))) ; 0)) ; (format t "Cons count: ~s" (aref *cons-count* 0)))) ; (setf (aref (the (array fixnum (1)) *cons-count*) ; 0) ; (the fixnum (+ 1 (the fixnum (aref *cons-count* 0))))) ; t) ; ; (defvar *acl2-strings*) ; ; (defun count-objects1 (x) ; (cond ; ((consp x) ; (cond ; ((gethash x *count-hash-table*) ; nil) ; (t ; (increment-cons-count) ; (setf (gethash x *count-hash-table*) t) ; (count-objects1 (car x)) ; (count-objects1 (cdr x))))) ; ((stringp x) ; (or (gethash x *count-hash-table*) ; (progn (increment-string-count (the fixnum (length x))) ; (setq *acl2-strings* (cons x *acl2-strings*)) ; (setf (gethash x *count-hash-table*) t)))))) ; ; (defun count-objects (x &optional clear) ; (setq *acl2-strings* nil) ; (setf (aref *cons-count* 0) 0) ; (setf (aref *string-count* 0) 0) ; (when clear ; (clrhash *count-hash-table*)) ; (count-objects1 x) ; (list 'cons-count (aref *cons-count* 0) ; 'string-count (aref *string-count* 0))) ; ; ;;;;;;;;;;;;;;; end of count.lisp ; ; (compile ; (defun extra-count (&aux ans) ; ;; (count-objects (w state)) already done ; (do-symbols (sym "ACL2") ; (let ((temp (get sym *CURRENT-ACL2-WORLD-KEY*))) ; (cond (temp ; (let ((count (count-objects temp))) ; (cond ; (count (push (cons sym count) ans)))))))) ; ans)) ; ; (progn (setq new-alist ; (stable-sort ; (loop for x in (extra-count) ; collect (cons (caddr x) (car x))) ; (function (lambda (x y) (> (car x) (car y)))))) ; 17) ; ; (loop for x in new-alist ; sum (car x)) ; ; ACL2>(take 10 new-alist) ; ((4207 . EVENT-LANDMARK) (3806 . COMMAND-LANDMARK) ; (3734 . CLTL-COMMAND) (424 . EVENT-INDEX) (384 . COMMAND-INDEX) ; (103 . PC-COMMAND-TABLE) (76 . PRIN1-WITH-SLASHES1) (75 . NTH) ; (74 . NONCONSTRUCTIVE-AXIOM-NAMES) (72 . UPDATE-NTH)) ; ; ACL2>(length new-alist) ; 3835 ; ; Note that the symbol-plists also take up space. ; ; (compile ; (defun more-count (&aux ans) ; ;; (count-objects (w state)) already done ; (do-symbols (sym "ACL2") ; (let ((temp (symbol-plist sym))) ; (cond (temp ; (let ((count (count-objects temp))) ; (cond ; (count (push (cons (cadr count) sym) ans)))))))) ; ans)) ; ; (progn (setq more-alist ; (stable-sort ; (more-count) ; (function (lambda (x y) (> (car x) (car y)))))) ; 17) ; ; ACL2>(car more-alist) ; (180 . AREF) ; ; ACL2>(loop for x in more-alist sum (car x)) ; [lots of GCs] ; 38657 ; [Note: Was 7607 using LISP package in raw GCL.] ; ; Note: There are 3835 symbols for which ACL2 causes at least two conses on ; their symbol-plist, in the following sense. ; ; (let ((temp 0)) ; (do-symbols (x "ACL2") ; (when (get x *CURRENT-ACL2-WORLD-KEY*) ; (setq temp (1+ temp)))) ; temp) ; ; But that still leaves (- 38657 (+ 7607 (* 2 3835))) = 23380 conses not ; accounted for. That's 281K of memory for "phantom" symbol-plist conses? ; ; Consider just those conses in (w state) other than 'const conses, since (except ; for the cell used to extend (w state)) these are part of the load image. ; ; (compile (defun foo () ; (let ((temp (loop for trip in (w state) ; when (eq (cadr trip) 'const) ; collect trip))) ; (list (length temp) (count-objects temp))))) ; (foo) ; --> ; (145 (CONS-COUNT 21040 STRING-COUNT 5468)) ; ; End of statistical and related information related to image size. (defun add-command-landmark (defun-mode form cbd last-make-event-expansion wrld) ; As with add-event-landmark above, we first update the world index ; and then add the command-landmark. However, here it is crucial that ; the index be inside the landmark, i.e., that the landmark happen ; last. Suppose we put the landmark down first and then added the ; index for that landmark. If we later did a :ubt of the subsequent ; command, we would kill the index entry. No harm would come then. ; But n commands later we would find the index out of sync with the ; maximum command number. The problem is that :ubt keys on ; 'command-landmark and we ought to keep them outside everything else. ; The function maybe-add-command-landmark, which ld-loop uses to add ; command-landmarks in response to user commands, relies upon the fact ; that well-formed worlds always contain a command-landmark as their ; first element. ; Defun-Mode is generally the default-defun-mode of the world in which this ; form is being executed. But there are two possible exceptions. When we add ; the command landmarks for enter-boot-strap-mode and exit-boot-strap-mode we ; just use the defun-mode :logic. That happens to be correct for ; exit-boot-strap-mode, but is wrong for enter-boot-strap-mode, which today is ; being executed with default-defun-mode :program. But it is irrelevant ; because neither of those two commands are sensitive to the ; default-defun-mode. (global-set 'command-landmark (make-command-tuple (next-absolute-command-number wrld) defun-mode form cbd last-make-event-expansion) (update-world-index 'command wrld))) (defun find-longest-common-retraction1 (wrld1 wrld2) (cond ((equal wrld1 wrld2) wrld1) (t (find-longest-common-retraction1 (scan-to-command (cdr wrld1)) (scan-to-command (cdr wrld2)))))) (defun find-longest-common-retraction1-event (wrld1 wrld2) (cond ((equal wrld1 wrld2) wrld1) (t (find-longest-common-retraction1 (scan-to-event (cdr wrld1)) (scan-to-event (cdr wrld2)))))) (defun find-longest-common-retraction (event-p wrld1 wrld2) ; Wrld1 and wrld2 are two worlds. We find and return a wrld3 that ; concludes with a command-landmark such that both wrld1 and wrld2 are ; extensions of wrld3. Of course, nil would do, but we find the ; longest. (cond (event-p (let* ((n (min (max-absolute-event-number wrld1) (max-absolute-event-number wrld2)))) (find-longest-common-retraction1-event (scan-to-landmark-number 'event-landmark n wrld1) (scan-to-landmark-number 'event-landmark n wrld2)))) (t (let* ((n (min (max-absolute-command-number wrld1) (max-absolute-command-number wrld2)))) (find-longest-common-retraction1 (scan-to-landmark-number 'command-landmark n wrld1) (scan-to-landmark-number 'command-landmark n wrld2)))))) (defun install-global-enabled-structure (wrld state) (cond ((null wrld) ; see initial call of set-w in enter-boot-strap-mode state) (t (let* ((augmented-theory (global-val 'current-theory-augmented wrld)) (ens (f-get-global 'global-enabled-structure state)) (theory-array (access enabled-structure ens :theory-array)) (current-theory-index (global-val 'current-theory-index wrld)) (eq-theories (equal augmented-theory (cdr theory-array)))) (cond ((and eq-theories (eql current-theory-index (access enabled-structure ens :index-of-last-enabling))) state) ((and eq-theories (< current-theory-index (car (dimensions (access enabled-structure ens :array-name) theory-array)))) (f-put-global 'global-enabled-structure (change enabled-structure ens :index-of-last-enabling current-theory-index) state)) (t (mv-let (erp new-ens state) (load-theory-into-enabled-structure :no-check augmented-theory t ens nil current-theory-index wrld 'irrelevant-ctx state) (assert$ (null erp) (f-put-global 'global-enabled-structure new-ens state))))))))) #+(and (not acl2-loop-only) hons) (defvar *defattach-fns*) (defun set-w (flg wrld state) ; Ctx is ignored unless we are extending the current ACL2 world, in which case ; if ctx is not nil, there will be a check on the new theory from a call of ; maybe-warn-about-theory. ; This is the only way in ACL2 (as opposed to raw Common Lisp) to ; install wrld as the current-acl2-world. Flg must be either ; 'extension or 'retraction. Logically speaking, all this function ; does is set the state global value of 'current-acl2-world in state ; to be wrld and possibly set current-package to "ACL2". Practically, ; speaking however, it installs wrld on the symbol-plists in Common ; Lisp. However, wrld must be an extension or retraction, as ; indicated, of the currently installed ACL2 world. ; Statement of Policy regarding Erroneous Events and ; Current ACL2 World Installation: ; Any event which causes an error must leave the current-acl2-world of ; state unchanged. That is, if you extend the world in an event, you ; must revert on error back to the original world. Once upon a time ; we enforced this rule in LD, simply by reverting the world on every ; erroneous command. But then we made that behavior conditional on ; the LD special ld-error-triples. If ld-error-triples is nil, then ; (mv t nil state) is not treated as an error by LD. Hence, an ; erroneous DEFUN, say, evaluated with ld-error-triples nil, does not ; cause LD to revert. Therefore, DEFUN must manage the reversion ; itself. #+acl2-loop-only (declare (xargs :guard (and (or (eq flg 'extension) (eq flg 'retraction)) (plist-worldp wrld) (known-package-alistp (getprop 'known-package-alist 'global-value nil 'current-acl2-world wrld)) (symbol-alistp (getprop 'acl2-defaults-table 'table-alist nil 'current-acl2-world wrld)) (state-p state)))) #+acl2-loop-only (pprogn (f-put-global 'current-acl2-world ; Here comes a slimy trick to avoid compiler warnings. (prog2$ flg wrld) state) (install-global-enabled-structure wrld state) (cond ((find-non-hidden-package-entry (current-package state) (known-package-alist state)) state) (t (f-put-global 'current-package "ACL2" state)))) #-acl2-loop-only (cond ((live-state-p state) (cond ((and *wormholep* (not (eq wrld (w *the-live-state*)))) (push-wormhole-undo-formi 'cloaked-set-w! (w *the-live-state*) nil))) (let (#+hons (*defattach-fns* nil)) (cond ((eq flg 'extension) (extend-world1 'current-acl2-world wrld) state) (t (retract-world1 'current-acl2-world wrld) state)))) (t (f-put-global 'current-acl2-world wrld state) (install-global-enabled-structure wrld state) (cond ((find-non-hidden-package-entry (current-package state) (known-package-alist state)) state) (t (f-put-global 'current-package "ACL2" state)))))) (defun set-w! (wrld state) ; This function makes wrld the current-acl2-world, but doesn't require ; that wrld be either an 'extension or a 'retraction of the current ; one. Note that any two worlds, wrld1 and wrld2, can be related by a ; retraction followed by an extension: retract wrld1 back to the first ; point at which it is a tail of wrld2, and then extend that world to ; wrld2. That is what we do. (let ((w (w state))) (cond ((equal wrld w) state) (t (pprogn (set-w 'retraction (find-longest-common-retraction ; It is important to use events rather than commands here when certifying or ; including a book. Otherwise, when make-event expansion extends the world, we ; will have to revert back to the beginning of the most recent top-level ; command and install the world from there. With a large number of such ; make-event forms, such quadratic behavior could be unfortunate. And, the ; community book books/make-event/stobj-test.lisp illustrates that if after ; make-event expansion we revert to the beginning of the book being certified, ; we could lose the setting of a stobj in that expansion. ; But really, there seems no point in using command landmarks here (as we ; always did until adding this use of event landmarks in Version_3.1). After ; all, why back up all the way to a command landmark? (With a different scheme ; we can imagine a reason; we'll get to that at the end of this long comment.) ; Moreover, if installation of an event were to consult the installed world, it ; would be important that all previous events have already been installed. For ; example, in the hons version of v3-6-1, the following example caused a hard ; error, as shown and explained below. ; (defun foo (x) ; (declare (xargs :guard t)) ; (cons x x)) ; ; (progn ; (defun foo-cond (x) ; (declare (ignore x) ; (xargs :guard 't)) ; nil) ; (memoize 'foo :condition-fn 'foo-cond) ; (progn ; (deflabel some-label) ; (defthm non-thm (equal x y)))) ; HARD ACL2 ERROR in SCAN-TO-LANDMARK-NUMBER: We have scanned the world ; looking for absolute event number 6463 and failed to find it.... ; How could this have happened? First note that the cltl-command for memoize ; invoked function cltl-def-from-name, which was looking in the installed world ; for the definition of foo-cond. But the world containing that 'cltl-command ; had not yet been installed in the state, because extend-world1 only installs ; the world after processing all the new trips. So when the defthm failed, its ; revert-world-on-error first retracted the world all the way back to just ; after the command for foo (not to just after the deflabel). The above error ; occurred because the attempt to fetch the cltl definition of foo-cond was ; passed the installed world: the world just after the introduction of foo. ; Now we don't get that error: instead we only roll back to the event landmark ; for the deflabel, and then we back out gracefully. Actually, we have also ; changed the implementation of memoize to avoid looking in the installed world ; for the cltl definition. But we still like using event landmarks, which can ; result in a lot less processing of world triples since we are not backing up ; all the way to a command landmark. ; Finally we address the "different scheme" promised above for the use of ; comman landmarks. In a chat with Sol Swords we came to realize that it would ; likely be more efficient to pop all the way back to the last command, and not ; extend that command world at all. (Recall that each command is protected by ; ld-read-eval-print.) For example, with the failure of non-thm above, its ; revert-world-on-error puts us at just after the deflabel; then the ; revert-world-on-error for the inner progn puts us at just after the memoize; ; and finally, the revert-world-on-error of the outer progn puts us just after ; the command introducing foo. Why not just revert directly to that final ; world? We could, but the current scheme has stood the test of time as being ; robust and efficient, albeit using command landmarks instead of event ; landmarks (and the latter should help performance rather than hurt it). But ; here is a more compelling reason not to try such a scheme. At the time a ; form fails, it is hard to know whether the error really cannot be tolerated ; and should pop us out to the start of the command. Consider for example ; something like: ; (progn ; (defun h1 (x) x) ; (make-event (mv-let (erp val state) ; (defthm my-bad (equal t nil)) ; (declare (ignore erp val)) ; (value '(value-triple nil)))) ; (defun h2 (x) (h1 x))) ; This event succeeds, and we want that to continue to be the case. It is hard ; to see how that could work if we were left in the world before h1 when the ; make-event failed. ; Old code, using event landmarks (rather than command landmarks) only in the ; indicated situations: ; (or (f-get-global 'certify-book-info state) ; (global-val 'include-book-path w)) t ; always use event landmarks (see comments above) wrld w) state) (set-w 'extension wrld state)))))) (defmacro save-event-state-globals (form) ; Form should evaluate to an error triple. ; We assign to saved-output-reversed, rather than binding it, so that saved ; output for gag-mode reply (using pso or psog) is available outside the scope ; of with-ctx-summarized. `(state-global-let* ((accumulated-ttree nil) (gag-state nil) (print-base 10 set-print-base) (print-radix nil set-print-radix) (proof-tree-ctx nil) (saved-output-p t)) (pprogn (f-put-global 'saved-output-reversed nil state) (with-prover-step-limit! :START ,form)))) (defun attachment-alist (fn wrld) (let ((prop (getprop fn 'attachment nil 'current-acl2-world wrld))) (and prop (cond ((symbolp prop) (getprop prop 'attachment nil 'current-acl2-world wrld)) ((eq (car prop) :attachment-disallowed) prop) ; (cdr prop) follows "because", e.g., (msg "it is bad") (t prop))))) (defun attachment-pair (fn wrld) (let ((attachment-alist (attachment-alist fn wrld))) (and attachment-alist (not (eq (car attachment-alist) :attachment-disallowed)) (assoc-eq fn attachment-alist)))) (defconst *protected-system-state-globals* (let ((val (set-difference-eq (union-eq (strip-cars *initial-ld-special-bindings*) (strip-cars *initial-global-table*)) '(acl2-raw-mode-p ;;; keep raw mode status bddnotes ;;; for feedback after expansion failure ; We handle world and enabled structure installation ourselves, with set-w! and ; revert-world-on-error. We do not want to rely just on state globals because ; the world protection/modification functions do pretty fancy things. current-acl2-world global-enabled-structure inhibit-output-lst ;;; allow user to modify this in a book inhibited-summary-types ;;; allow user to modify this in a book keep-tmp-files ;;; allow user to modify this in a book make-event-debug ;;; allow user to modify this in a book saved-output-token-lst ;;; allow user to modify this in a book print-clause-ids ;;; allow user to modify this in a book fmt-soft-right-margin ;;; allow user to modify this in a book fmt-hard-right-margin ;;; allow user to modify this in a book parallel-execution-enabled ;;; allow user to modify this in a book waterfall-parallelism ;;; allow user to modify this in a book waterfall-parallelism-timing-threshold ;;; see just above waterfall-printing ;;; allow user to modify this in a book waterfall-printing-when-finished ;;; see just above saved-output-reversed ;;; for feedback after expansion failure saved-output-p ;;; for feedback after expansion failure ttags-allowed ;;; propagate changes outside expansion ld-evisc-tuple ;;; see just above term-evisc-tuple ;;; see just above abbrev-evisc-tuple ;;; see just above gag-mode-evisc-tuple ;;; see just above slow-array-action ;;; see just above iprint-ar ;;; see just above iprint-soft-bound ;;; see just above iprint-hard-bound ;;; see just above writes-okp ;;; protected a different way (see ;;; protect-system-state-globals) show-custom-keyword-hint-expansion trace-specs ;;; keep in sync with functions that are ;;; actually traced, e.g. trace! macro timer-alist ;;; preserve accumulated summary info main-timer ;;; preserve accumulated summary info verbose-theory-warning ;;; for warning on disabled mv-nth etc. more-doc-state ;;; for proof-checker :more command pc-ss-alist ;;; for saves under :instructions hints last-step-limit ;;; propagate step-limit past expansion illegal-to-certify-message ;;; needs to persist past expansion splitter-output ;;; allow user to modify this in a book top-level-errorp ;;; allow TOP-LEVEL errors to propagate )))) val)) (defun state-global-bindings (names) (cond ((endp names) nil) (t (cons `(,(car names) (f-get-global ',(car names) state)) (state-global-bindings (cdr names)))))) (defmacro protect-system-state-globals (form) ; Form must return an error triple. This macro not only reverts built-in state ; globals after evaluating form, but it also disables the opening of output ; channels. `(state-global-let* ,(cons `(writes-okp nil) (state-global-bindings *protected-system-state-globals*)) ,form)) (defun formal-value-triple (erp val) ; Keep in sync with formal-value-triple@par. ; Returns a form that evaluates to the error triple (mv erp val state). (fcons-term* 'cons erp (fcons-term* 'cons val (fcons-term* 'cons 'state *nil*)))) #+acl2-par (defun formal-value-triple@par (erp val) ; Keep in sync with formal-value-triple. (fcons-term* 'cons erp (fcons-term* 'cons val *nil*))) (defun@par translate-simple-or-error-triple (uform ctx wrld state) ; First suppose either #-acl2-par or else #+acl2-par with waterfall-parallelism ; disabled. Uform is an untranslated term that is expected to translate either ; to an error triple or to an ordinary value. In those cases we return an ; error triple whose value component is the translated term or, respectively, ; the term representing (mv nil tterm state) where tterm is the translated ; term. Otherwise, we return a soft error. ; Now consider the case of #+acl2-par with waterfall-parallelism enabled. ; Uform is an untranslated term that is expected to translate to an ordinary ; value. In this case, we return an error pair (mv nil val) where val is the ; translated term. Otherwise, uform translates into an error pair (mv t nil). (mv-let@par (erp term bindings state) (translate1@par uform :stobjs-out ; form must be executable '((:stobjs-out . :stobjs-out)) '(state) ctx wrld state) (cond (erp (mv@par t nil state)) (t (let ((stobjs-out (translate-deref :stobjs-out bindings))) (cond ((equal stobjs-out '(nil)) ; replace term by (value@par term) (value@par (formal-value-triple@par *nil* term))) ((equal stobjs-out *error-triple-sig*) (serial-first-form-parallel-second-form@par (value@par term) ; #+ACL2-PAR note: This message is used to check that computed hints and custom ; keyword hints (and perhaps other hint mechanisms too) do not modify state. ; Note that not all hint mechanisms rely upon this check. For example, ; apply-override-hint@par and eval-clause-processor@par perform their own ; checks. ; Parallelism wart: it should be possible to return (value@par term) when ; waterfall-parallelism-hacks-enabled is non-nil. This would allow more types ; of hints to fire when waterfall-parallelism-hacks are enabled. (er@par soft ctx "Since we are translating a form in ACL2(p) intended to be ~ executed with waterfall parallelism enabled, the form ~x0 was ~ expected to represent an ordinary value, not an error triple (mv ~ erp val state), as would be acceptable in a serial execution of ~ ACL2. Therefore, the form returning a tuple of the form ~x1 is ~ an error. See :DOC unsupported-waterfall-parallelism-features ~ and :DOC error-triples-and-parallelism for further explanation." uform (prettyify-stobj-flags stobjs-out)))) #+acl2-par ((serial-first-form-parallel-second-form@par nil (and ; The test of this branch is never true in the non-@par version of the ; waterfall. We need this test for custom-keyword-hints, which are evaluated ; using the function eval-and-translate-hint-expression[@par]. Since ; eval-and-translate-hint-expression[@par] calls ; translate-simple-or-error-triple[@par] to check the return signature of the ; custom hint, we must not cause an error when we encounter this legitimate ; use. ; Parallelism wart: consider eliminating the special case below, given the spec ; for translate-simple-or-error-triple[@par] in the comment at the top of this ; function. This could be achieved by doing the test below before calling ; translate-simple-or-error-triple@par, either inline where we now call ; translate-simple-or-error-triple@par or else with a wrapper that handles this ; special case before calling translate-simple-or-error-triple@par. (equal stobjs-out *cmp-sig*) (eq (car uform) 'custom-keyword-hint-interpreter@par))) (value@par term)) (t (serial-first-form-parallel-second-form@par (er soft ctx "The form ~x0 was expected to represent an ordinary value or ~ an error triple (mv erp val state), but it returns a tuple ~ of the form ~x1." uform (prettyify-stobj-flags stobjs-out)) (er@par soft ctx "The form ~x0 was expected to represent an ordinary value, but ~ it returns a tuple of the form ~x1. Note that error triples ~ are not allowed in this feature in ACL2(p) (see :doc ~ error-triples-and-parallelism)" uform (prettyify-stobj-flags stobjs-out)))))))))) (defun xtrans-eval (uterm alist trans-flg ev-flg ctx state aok) ; NOTE: Do not call this function with er-let* if ev-flg is nil. Use mv-let ; and check erp manually. See the discussion of 'wait below. ; Ignore trans-flg and ev-flg for the moment (or imagine their values are t). ; Then the spec of this function is as follows: ; Uterm is an untranslated term with an output signature of * or (mv * * ; state). We translate it and eval it under alist (after extending alist with ; state bound to the current state) and return the resulting error triple or ; signal a translate or evaluation error. We restore the world and certain ; state globals (*protected-system-state-globals*) after the evaluation. ; If trans-flg is nil, we do not translate. We *assume* uterm is a ; single-threaded translated term with output signature (mv * * state)! ; Ev-flg is either t or nil. If ev-flg is nil, we are to evaluate uterm only ; if all of its free vars are bound in the evaluation environment. If ev-flg ; is nil and we find that a free variable of uterm is not bound, we return a ; special error triple, namely (mv t 'wait state) indicating that the caller ; should wait until it can get all the vars bound. On the other hand, if ; ev-flg is t, it means eval the translated uterm, which will signal an error ; if there is an unbound var. ; Note that we do not evaluate in safe-mode. Perhaps we should. However, we ; experimented by timing certification for community books directory ; books/hints/ without and with safe-mode, and found times of 13.5 and 16.4 ; user seconds, respectively. That's not a huge penalty for safe-mode but it's ; not small, either, so out of concern for scalability we will avoid safe-mode ; for now. (er-let* ((term (if trans-flg (translate-simple-or-error-triple uterm ctx (w state) state) (value uterm)))) (cond ((or ev-flg (subsetp-eq (all-vars term) (cons 'state (strip-cars alist)))) ; We are to ev the term. But first we protect ourselves by arranging ; to revert the world and restore certain state globals. (let ((original-world (w state))) (er-let* ((val (acl2-unwind-protect "xtrans-eval" (protect-system-state-globals (mv-let (erp val latches) (ev term (cons (cons 'state (coerce-state-to-object state)) alist) state (list (cons 'state (coerce-state-to-object state))) nil aok) (let ((state (coerce-object-to-state (cdr (car latches))))) (cond (erp ; An evaluation error occurred. This could happen if we encountered ; an undefined (but constrained) function. We print the error ; message. (er soft ctx "~@0" val)) (t ; Val is the list version of (mv erp' val' state) -- and it really is ; state in that list (typically, the live state). We assume that if ; erp' is non-nil then the evaluation also printed the error message. ; We return an error triple. (mv (car val) (cadr val) state)))))) (set-w! original-world state) (set-w! original-world state)))) (value val)))) (t ; In this case, ev-flg is nil and there are variables in tterm that are ; not bound in the environment. So we tell our caller to wait to ev the ; term. (mv t 'wait state))))) #+acl2-par (defun xtrans-eval-with-ev-w (uterm alist trans-flg ev-flg ctx state aok) ; See xtrans-eval documentation. ; This function was originally introduced in support of the #+acl2-par version. ; We could have named it "xtrans-eval@par". However, this function seems ; worthy of having its own name, suggestive of what it is: a version of ; xtrans-eval that uses ev-w for evaluation rather than using ev. The extra ; function call adds only trivial cost. (er-let*@par ((term (if trans-flg ; #+ACL2-PAR note: As of August 2011, there are two places that call ; xtrans-eval@par with the trans-flg set to nil: apply-override-hint@par and ; eval-and-translate-hint-expression@par. In both of these cases, we performed ; a manual inspection of the code (aided by testing) to determine that if state ; can be modified by executing uterm, that the user will receive an error ; before even reaching this call of xtrans-eval@par. In this way, we guarantee ; that the invariant for ev-w (that uterm does not modify state) is maintained. (translate-simple-or-error-triple@par uterm ctx (w state) state) (value@par uterm)))) (cond ((or ev-flg (subsetp-eq (all-vars term) (cons 'state (strip-cars alist)))) ; #+ACL2-PAR note: we currently discard any changes to the world of the live ; state. But if we restrict to terms that don't modify state, as discussed in ; the #+ACL2-PAR note above, then there is no issue because state hasn't ; changed. Otherwise, if we cheat, the world could indeed change out from ; under us, which is just one example of the evils of cheating by modifying ; state under the hood. (er-let*-cmp ((val (mv-let (erp val) (ev-w term (cons (cons 'state (coerce-state-to-object state)) alist) (w state) (user-stobj-alist state) (f-get-global 'safe-mode state) (gc-off state) nil aok) (cond (erp ; An evaluation error occurred. This could happen if we encountered ; an undefined (but constrained) function. We print the error ; message. (er@par soft ctx "~@0" val)) (t ; Val is the list version of (mv erp' val' state) -- and it really is ; state in that list (typically, the live state). We assume that if ; erp' is non-nil then the evaluation also printed the error message. ; We return an error triple. (mv (car val) (cadr val))))))) (value@par val))) (t ; In this case, ev-flg is nil and there are variables in tterm that are ; not bound in the environment. So we tell our caller to wait to ev the ; term. (mv t 'wait))))) #+acl2-par (defun xtrans-eval@par (uterm alist trans-flg ev-flg ctx state aok) (xtrans-eval-with-ev-w uterm alist trans-flg ev-flg ctx state aok)) (defmacro xtrans-eval-state-fn-attachment (form ctx) ; We call xtrans-eval on (pprogn (fn state) (value nil)), unless we are in the ; boot-strap or fn is unattached, in which cases we return (value nil). ; Note that arguments trans-flg and aok are t in our call of xtrans-eval. (declare (xargs :guard (and (true-listp form) (symbolp (car form))))) `(let ((form ',form) (fn ',(car form)) (ctx ,ctx) (wrld (w state))) (cond ((or (global-val 'boot-strap-flg wrld) (null (attachment-pair fn wrld))) (value nil)) (t (let ((form (list 'pprogn (append form '(state)) '(value nil)))) (mv-let (erp val state) (xtrans-eval form nil ; alist t ; trans-flg t ; ev-flg ctx state t ; aok ) (cond (erp (er soft ctx "The error above occurred during ~ evaluation of ~x0." form)) (t (value val))))))))) (defmacro with-ctx-summarized (ctx body) ; A typical use of this macro by an event creating function is: ; (with-ctx-summarized (cons 'defun name) ; (er-progn ... ; (er-let* (... (v form) ...) ; (install-event ...)))) ; Note that with-ctx-summarized binds the variables ctx and saved-wrld, which ; thus can be used in body. ; If body changes the installed world then the new world must end with an ; event-landmark (we cause an error otherwise). The segment of the new world ; back to the previous event-landmark is scanned for redefined names and an ; appropriate warning message is printed, as per ld-redefinition-action. ; The most obvious way to satisfy this restriction on world is for each ; branch through body to (a) stop with stop-redundant-event, (b) signal an ; error, or (c) conclude with install-event. Two of our current uses of this ; macro do not follow so simple a paradigm. In include-book-fn we add many ; events (in process-embedded-events) but we do conclude with an install-event ; which couldn't possibly redefine any names because no names are defined in ; the segment from the last embedded event to the landmark for the include-book ; itself. In certify-book-fn we conclude with an include-book-fn. So in both ; of those cases the scan for redefined names ends quickly (without going into ; the names possibly redefined in the embedded events) and finds nothing to ; report. ; This macro initializes the timers for an event and then executes the supplied ; body, which should return an error triple. Whether an error is signalled or ; not, the macro prints the summary and then pass the error triple on up. The ; stats must be available from the state. In particular, we print redefinition ; warnings that are recovered from the currently installed world in state and ; we print the runes from 'accumulated-ttree. `(let ((ctx ,ctx) (saved-wrld (w state))) (pprogn (initialize-summary-accumulators state) (mv-let (erp val state) (save-event-state-globals (mv-let (erp val state) (pprogn (push-io-record :ctx (list 'mv-let '(col state) '(fmt "Output replay for: " nil (standard-co state) state nil) (list 'mv-let '(col state) (list 'fmt-ctx (list 'quote ctx) 'col '(standard-co state) 'state) '(declare (ignore col)) '(newline (standard-co state) state))) state) (er-progn (xtrans-eval-state-fn-attachment (initialize-event-user ',ctx ',body) ctx) ,body)) (pprogn (print-summary erp (equal saved-wrld (w state)) ctx state) (er-progn (xtrans-eval-state-fn-attachment (finalize-event-user ',ctx ',body) ctx) (mv erp val state))))) ; In the case of a compound event such as encapsulate, we do not want to save ; io? forms for proof replay that were generated after a failed proof attempt. ; Otherwise, if we do not set the value of 'saved-output-p below to nil, then ; replay from an encapsulate with a failed defthm will pop warnings more often ; than pushing them (resulting in an error from pop-warning-frame). This ; failure (without setting 'saved-output-p below) happens because the pushes ; are only from io? forms saved inside the defthm, yet we were saving the ; pops from the enclosing encapsulate. (pprogn (f-put-global 'saved-output-p nil state) (mv erp val state)))))) (defmacro revert-world-on-error (form) ; With this macro we can write (revert-world-on-error &) and if & ; causes an error the world will appear unchanged (because we revert ; back to the world of the initial state). The local variable used to ; save the old world is a long ugly name only because we prohibit its ; use in ,form. (Historical Note: Before the introduction of ; acl2-unwind-protect we had to use raw lisp to handle this and the ; handling of that special variable was very subtle. Now it is just ; an ordinary local of the let.) `(let ((revert-world-on-error-temp (w state))) (acl2-unwind-protect "revert-world-on-error" (check-vars-not-free (revert-world-on-error-temp) ,form) (set-w! revert-world-on-error-temp state) state))) (defun@par chk-theory-expr-value1 (lst wrld expr macro-aliases ctx state) ; A theory expression must evaluate to a common theory, i.e., a ; truelist of rule name designators. A rule name designator, recall, ; is something we can interpret as a set of runes and includes runes ; themselves and the base symbols of runes, such as APP and ; ASSOC-OF-APP. We already have a predicate for this concept: ; theoryp. This checker checks for theoryp but with better error ; reporting. (cond ((atom lst) (cond ((null lst) (value@par nil)) (t (er@par soft ctx "The value of the alleged theory expression ~x0 is not a ~ true list and, hence, is not a legal theory value. In ~ particular, the final non-consp cdr is the atom ~x1. ~ See :DOC theories." expr lst)))) ((rule-name-designatorp (car lst) macro-aliases wrld) (chk-theory-expr-value1@par (cdr lst) wrld expr macro-aliases ctx state)) (t (er@par soft ctx "The value of the alleged theory expression ~x0 includes the ~ element ~x1, which we do not know how to interpret as a rule ~ name. See :DOC theories and :DOC rune." expr (car lst))))) (defun@par chk-theory-expr-value (lst wrld expr ctx state) ; This checker ensures that expr, whose value is lst, evaluated to a theoryp. ; Starting after Version_3.0.1 we no longer check the theory-invariant table, ; because the ens is not yet available at this point. (chk-theory-expr-value1@par lst wrld expr (macro-aliases wrld) ctx state)) (defun theory-fn-translated-callp (x) ; We return t or nil. If t, then we know that the term x evaluates to a runic ; theory. See also theory-fn-callp. (and (nvariablep x) (not (fquotep x)) (member-eq (car x) '(current-theory-fn e/d-fn executable-counterpart-theory-fn function-theory-fn intersection-theories-fn set-difference-theories-fn theory-fn union-theories-fn universal-theory-fn)) t)) (defun eval-theory-expr (expr ctx wrld state) ; returns a runic theory ; Keep in sync with eval-theory-expr@par. (cond ((equal expr '(current-theory :here)) (mv-let (erp val latches) (ev '(current-theory-fn ':here world) (list (cons 'world wrld)) state nil nil t) (declare (ignore latches)) (mv erp val state))) (t (er-let* ((trans-ans (state-global-let* ((guard-checking-on t) ; see the Essay on Guard Checking ; ;;; (safe-mode t) ; !! long-standing "experimental" deletion ) (simple-translate-and-eval expr (list (cons 'world wrld)) nil "A theory expression" ctx wrld state t)))) ; Trans-ans is (term . val). (cond ((theory-fn-translated-callp (car trans-ans)) (value (cdr trans-ans))) (t (er-progn (chk-theory-expr-value (cdr trans-ans) wrld expr ctx state) (value (runic-theory (cdr trans-ans) wrld))))))))) #+acl2-par (defun eval-theory-expr@par (expr ctx wrld state) ; returns a runic theory ; Keep in sync with eval-theory-expr. (cond ((equal expr '(current-theory :here)) (mv-let (erp val) (ev-w '(current-theory-fn ':here world) (list (cons 'world wrld)) (w state) (user-stobj-alist state) (f-get-global 'safe-mode state) (gc-off state) nil t) (mv erp val))) (t (er-let*@par ((trans-ans (simple-translate-and-eval@par expr (list (cons 'world wrld)) nil "A theory expression" ctx wrld state t ; The following arguments are intended to match the safe-mode and gc-off values ; from the state in eval-theory-expr at the call there of ; simple-translate-and-eval. Since there is a superior state-global-let* ; binding guard-checking-on to t, we bind our gc-off argument below to what ; would be the value of (gc-off state) in that function, which is nil. (f-get-global 'safe-mode state) nil))) ; Trans-ans is (term . val). (cond ((theory-fn-translated-callp (car trans-ans)) (value@par (cdr trans-ans))) (t (er-progn@par (chk-theory-expr-value@par (cdr trans-ans) wrld expr ctx state) (value@par (runic-theory (cdr trans-ans) wrld))))))))) (defun append-strip-cdrs (x y) ; This is (append (strip-cdrs x) y). (cond ((null x) y) (t (cons (cdr (car x)) (append-strip-cdrs (cdr x) y))))) (defun no-rune-based-on (runes symbols) (cond ((null runes) t) ((member-eq (base-symbol (car runes)) symbols) nil) (t (no-rune-based-on (cdr runes) symbols)))) (defun revappend-delete-runes-based-on-symbols1 (runes symbols ans) ; We delete from runes all those with base-symbols listed in symbols ; and accumulate them in reverse order onto ans. (cond ((null runes) ans) ((member-eq (base-symbol (car runes)) symbols) (revappend-delete-runes-based-on-symbols1 (cdr runes) symbols ans)) (t (revappend-delete-runes-based-on-symbols1 (cdr runes) symbols (cons (car runes) ans))))) (defun revappend-delete-runes-based-on-symbols (runes symbols ans) ; In computing the useful theories we will make use of previously stored values ; of those theories. However, those stored values might contain "runes" that ; are no longer runes because of redefinition. The following function is used ; to delete from those non-runes, based on the redefined base symbols. ; This function returns the result of appending the reverse of ans to the ; result of removing runes based on symbols from the given list of runes. It ; should return a runic theory. (cond ((or (null symbols) (no-rune-based-on runes symbols)) ; This case is not only a time optimization, but it also allows sharing. For ; example, runes could be the 'current-theory, and in this case we will just be ; extending that theory. (revappend ans runes)) (t (reverse (revappend-delete-runes-based-on-symbols1 runes symbols ans))))) (defun current-theory1 (lst ans redefined) ; Lst is a cdr of wrld. We wish to return the enabled theory as of the time ; lst was wrld. When in-theory is executed it stores the newly enabled theory ; under the 'global-value of the variable 'current-theory. When new rule names ; are introduced, they are automatically considered enabled. Thus, the enabled ; theory at any point is the union of the current value of 'current-theory and ; the names introduced since that value was set. However, :REDEF complicates ; matters. See universal-theory-fn1. (cond ((null lst) #+acl2-metering (meter-maid 'current-theory1 500) (reverse ans)) ; unexpected, but correct ((eq (cadr (car lst)) 'runic-mapping-pairs) #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (cond ((eq (cddr (car lst)) *acl2-property-unbound*) (current-theory1 (cdr lst) ans (add-to-set-eq (car (car lst)) redefined))) ((member-eq (car (car lst)) redefined) (current-theory1 (cdr lst) ans redefined)) (t (current-theory1 (cdr lst) (append-strip-cdrs (cddr (car lst)) ans) redefined)))) ((and (eq (car (car lst)) 'current-theory) (eq (cadr (car lst)) 'global-value)) ; We append the reverse of our accumulated ans to the appropriate standard ; theory, but deleting all the redefined runes. #+acl2-metering (meter-maid 'current-theory1 500) (revappend-delete-runes-based-on-symbols (cddr (car lst)) redefined ans)) (t #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (current-theory1 (cdr lst) ans redefined)))) (defun first-n-ac-rev (i l ac) ; This is the same as first-n-ac, except that it reverses the accumulated ; result and traffics in fixnums -- more efficient if you want the reversed ; result. (declare (type (unsigned-byte 29) i) (xargs :guard (and (true-listp l) (true-listp ac)))) (cond ((zpf i) ac) (t (first-n-ac-rev (the (unsigned-byte 29) (1- (the (unsigned-byte 29) i))) (cdr l) (cons (car l) ac))))) (defun longest-common-tail-length-rec (old new acc) (declare (type (signed-byte 30) acc)) #-acl2-loop-only (when (eq old new) (return-from longest-common-tail-length-rec (+ (length old) acc))) (cond ((endp old) (assert$ (null new) acc)) (t (longest-common-tail-length-rec (cdr old) (cdr new) (if (equal (car old) (car new)) (1+f acc) 0))))) (defun longest-common-tail-length (old new) ; We separate out this wrapper function so that we don't need to be concerned ; about missing the #-acl2-loop-only case in the recursive computation, which ; could perhaps happen if we are in safe-mode and oneification prevents escape ; into Common Lisp. (longest-common-tail-length-rec old new 0)) (defun extend-current-theory (old-th new-th old-aug-th wrld) ; Logically this function just returns new-th. However, the copy of new-th ; that is returned shares a maximal tail with old-th. A second value similarly ; extends old-aug-th, under the assumption that old-aug-th is the ; augmented-theory corresponding to old-th; except, if old-aug-th is :none then ; the second value is undefined. (let* ((len-old (length old-th)) (len-new (length new-th)) (len-common (cond ((int= len-old len-new) (longest-common-tail-length old-th new-th)) ((< len-old len-new) (longest-common-tail-length old-th (nthcdr (- len-new len-old) new-th))) (t (longest-common-tail-length (nthcdr (- len-old len-new) old-th) new-th)))) (take-new (- len-new len-common)) (nthcdr-old (- len-old len-common)) (new-part-of-new-rev (first-n-ac-rev (the-unsigned-byte! 29 take-new 'extend-current-theory) new-th nil))) (mv (append (reverse new-part-of-new-rev) (nthcdr nthcdr-old old-th)) (if (eq old-aug-th :none) :none (append (augment-runic-theory1 new-part-of-new-rev nil wrld nil) (nthcdr nthcdr-old old-aug-th)))))) (defun update-current-theory (theory0 wrld) (mv-let (theory theory-augmented) (extend-current-theory ; It's not necessarily reasonable to assume that theory0 shares a lot of ; structure with the most recent value of 'current-theory. But it could ; happen, so we take the opportunity to save space. Consider the not uncommon ; case that theory0 is the value of (current-theory :here). Theory0 may be eq ; to the value of 'current-theory, in which case this extend-current-theory ; call below will be cheap because it will just do a single eq test. However, ; theory0 could be a copy of the most recent 'current-theory that doesn't share ; much structure with it, in which case it's a good thing that we are here ; calling extend-current-theory. (global-val 'current-theory wrld) theory0 (global-val 'current-theory-augmented wrld) wrld) (global-set 'current-theory theory (global-set 'current-theory-augmented theory-augmented (global-set 'current-theory-index (1- (get-next-nume wrld)) wrld))))) (defun put-cltl-command (cltl-cmd wrld wrld0) ; We extend wrld by noting cltl-cmd. Wrld0 is supplied because it may more ; efficient for property lookup than wrld; it is critical therefore that wrld0 ; and wrld have the same values of 'include-book-path, ; 'top-level-cltl-command-stack, and 'boot-strap-flg. (let ((wrld (if (or (global-val 'include-book-path wrld0) (global-val 'boot-strap-flg wrld0)) wrld (global-set 'top-level-cltl-command-stack (cons cltl-cmd (global-val 'top-level-cltl-command-stack wrld0)) wrld)))) (global-set 'cltl-command cltl-cmd wrld))) (defun strip-non-nil-base-symbols (runes acc) (cond ((endp runes) acc) (t (strip-non-nil-base-symbols (cdr runes) (let ((b (base-symbol (car runes)))) (cond ((null b) acc) (t (cons b acc)))))))) (defun install-proof-supporters (namex ttree wrld) ; This function returns an extension of wrld in which the world global ; 'proof-supporters-alist is extended by associating namex, when a symbol or ; list of symbols, with the list of names of events used in an admissibility ; proof. This list is sorted (by symbol-<) and is based on event names ; recorded in ttree, including runes as well as events from hints of type :use, ; :by, or :clause-processor. However, if the list of events is empty, then we ; do not extend wrld. See :DOC dead-events. (let* ((use-lst (use-names-in-ttree ttree)) (by-lst (by-names-in-ttree ttree)) (cl-proc-lst (cl-proc-names-in-ttree ttree)) (runes (all-runes-in-ttree ttree nil)) (names (append use-lst by-lst cl-proc-lst (strip-non-nil-base-symbols runes nil))) (sorted-names (and names ; optimization (sort-symbol-listp (cond ((symbolp namex) (cond ((member-eq namex names) ; For example, the :induction rune for namex, or a :use (or maybe even :by) ; hint specifying namex, can be used in the guard proof. (remove-eq namex names)) (t names))) ((intersectp-eq namex names) (set-difference-eq names namex)) (t names)))))) (cond ((and (not (eql namex 0)) sorted-names) (global-set 'proof-supporters-alist (acons namex sorted-names (global-val 'proof-supporters-alist wrld)) wrld)) (t wrld)))) (defun install-event (val form ev-type namex ttree cltl-cmd chk-theory-inv-p ctx wrld state) ; This function is the way to finish off an ACL2 event. Val is the value to be ; returned by the event (in the standard error flag/val/state three-valued ; result). Namex is either 0, standing for the empty set of names, an atom, ; standing for the singleton set of names containing that atom, or a true list ; of symbols, standing for the set of names in the list. Each symbol among ; these names will be given an 'absolute-event-number property. In addition, ; we set 'event-landmark 'global-value to an appropriate event tuple, thus ; marking the world for this event. Cltl-cmd is the desired value of the ; 'global-value for 'cltl-command (see below). Chk-theory-inv-p is generally ; nil, but is non-nil if we are to check theory invariants, and is :PROTECT if ; the call is not already in the scope of a revert-world-on-error. Wrld is the ; world produced by the ACL2 event and state is the current state, and before ; extending it as indicated above, we extend it if necessary by an appropriate ; record of the proof obligations discharged in support of functional ; instantiation, in order to avoid such proofs in later events. ; Ttree is the final ttree of the event. We install it as 'accumulated-ttree ; so that the runes reported in the summary are guaranteed to be those of the ; carefully tracked ttree passed along through the proof. It is possible that ; the 'accumulated-ttree already in state contains junk, e.g., perhaps we ; accumulated some runes from a branch of the proof we have since abandoned. ; We try to avoid this mistake, but just to be sure that a successful proof ; reports the runes that we really believe got used, we do it this way. ; We store the 'absolute-event-number property for each name. We set ; 'event-landmark. We store the cltl-cmd as the value of the variable ; 'cltl-command (if cltl-cmd is non-nil). We update the event index. We ; install the new world as the current ACL2 world in state. Non-logical code ; in set-w notes the 'cltl-command requests in the world and executes ; appropriate raw Common Lisp for effect. This function returns the triple ; indicating a non-erroneous return of val. ; The installation of the world into state causes "secret" side-effects on the ; underlying lisp state, as controlled by 'cltl-command. Generally, the value ; is a raw lisp form to execute, e.g., (defconst name val). But when the car ; of the form is DEFUNS the general form is (DEFUNS defun-mode-flg ignorep def1 ; ... defn). The raw lisp form to execute is actually (DEFUNS def1' ; ... defn'), where the defi' are computed from the defi depending on ; defun-mode-flg and ignorep. Defun-Mode-flg is either nil (meaning the ; function is :non-executable or the parent event is an encapsulate which is ; trying to define the executable counterparts of the constrained functions) or ; a defun-mode (meaning the parent event is an executable DEFUNS and the ; defun-mode is the defun-mode of the defined functions). Ignorep is ; 'reclassifying, '(defstobj . stobj-name), or nil. If ignorep is nil, we add ; each def and its *1* counterpart, after pushing the old bodies on the undo ; stack. If ignorep is 'reclassifying (which means we are reclassifying a ; :program fn to a :logic fn without changing its definition -- which is ; probably hand coded ACL2 source), we define only the *1* counterparts after ; pushing only the *1* counterparts on the undo stack. If ignorep is ; '(defstobj . stobj-name) we do not add the def or its *1* counterpart, but we ; do push both the main name and the *1* name. This is because we know ; defstobj will supply a symbol-function for the main name and its *1* ; counterpart in a moment. We use the stobj-name in the *1* body to compute ; the stobjs-in of the function. See the comment in add-trip. ; One might ask why we make add-trip do the oneify to produce the *1* bodies, ; instead of compute them when we generate the CLTL-COMMAND. The reason is ; that we use the 'cltl-command of a DEFUN as the only place we can recover the ; exact ACL2 defun command that got executed. (Exception: in the case that the ; :defun-mode is nil, i.e., the definition is non-executable, we have replaced ; the body with a throw-raw-ev-fncall.) (let ((currently-installed-wrld (w state))) (mv-let (chk-theory-inv-p theory-invariant-table) (cond ((member-eq (ld-skip-proofsp state) '(include-book include-book-with-locals)) (mv nil nil)) (t (let ((tbl (table-alist 'theory-invariant-table currently-installed-wrld))) (cond ((null tbl) ; avoid work of checking theory invariant (mv nil nil)) (t (mv chk-theory-inv-p tbl)))))) (let* ((new-proved-fnl-insts (proved-functional-instances-from-tagged-objects (cond ((atom namex) ; We deliberately include the case namex = 0 here. namex) (t (car namex))) (revappend (strip-cars (tagged-objects :use ttree)) (reverse ; for backwards compatibility with v4-2 (tagged-objects :by ttree))))) (wrld0 (if (or (ld-skip-proofsp state) (and (atom namex) (not (symbolp namex)))) wrld (install-proof-supporters namex ttree wrld))) (wrld1 (if new-proved-fnl-insts (global-set 'proved-functional-instances-alist (append new-proved-fnl-insts (global-val 'proved-functional-instances-alist wrld0)) wrld0) wrld0)) ; We set world global 'skip-proofs-seen or 'redef-seen if ld-skip-proofsp or ; ld-redefinition-action (respectively) is non-nil and the world global is not ; already true. This information is important for vetting a proposed ; certification world. See the Essay on Soundness Threats. (wrld2 (cond ((and (ld-skip-proofsp state) (not (member-eq ev-type ; Comment on irrelevance of skip-proofs: ; The following event types do not generate any proof obligations, so for these ; it is irrelevant whether or not proofs are skipped. Do not include defaxiom, ; or any other event that can have a :corollary rule class, since that can ; generate a proof obligation. Also do not include encapsulate; even though it ; takes responsibility for setting skip-proofs-seen based on its first pass, ; nevertheless it does not account for a skip-proofs surrounding the ; encapsulate. Finally, do not include defattach; the use of (skip-proofs ; (defattach f g)) can generate bogus data in world global ; 'proved-functional-instances-alist that can be used to prove nil later. '(include-book defchoose defconst defdoc deflabel defmacro defpkg defstobj deftheory in-arithmetic-theory in-theory push-untouchable regenerate-tau-database remove-untouchable reset-prehistory set-body table))) ; We include the following test so that we can distinguish between the ; user-specified skipping of proofs and legitimate skipping of proofs by the ; system, such as including a book. Without the disjunct below, we fail to ; pick up a skip-proofs during the Pcertify step of provisional certification. ; Perhaps someday there will be other times a user-supplied skip-proofs form ; triggers setting of 'skip-proofs-seen even when 'skip-proofs-by-system is ; true; if that turns out to be too aggressive, we'll think about this then, ; but for now, we are happy to be conservative, making sure that ; skip-proofs-seen is set whenever we are inside a call of skip-proofs. (or (f-get-global 'inside-skip-proofs state) (not (f-get-global 'skip-proofs-by-system state))) (let ((old (global-val 'skip-proofs-seen wrld))) (or (not old) ; In certify-book-fn we find a comment stating that "we are trying to record ; whether there was a skip-proofs form in the present book, not merely on ; behalf of an included book". That is why here, we replace value ; (:include-book ...) for 'skip-proofs-seen. (eq (car old) :include-book)))) (global-set 'skip-proofs-seen form wrld1)) (t wrld1))) (wrld3 (cond ((and (ld-redefinition-action state) ; We tolerate redefinition inside a book, because there must have been a trust ; tag that allowed it. We are only trying to protect against redefinition ; without a trust tag, especially in the certification world. (Without a trust ; tag there cannot be any redefinition in a certified book anyhow.) (not (global-val 'include-book-path wrld)) (not (global-val 'redef-seen wrld))) (global-set 'redef-seen form wrld2)) (t wrld2))) (wrld4 (if cltl-cmd (put-cltl-command cltl-cmd wrld3 currently-installed-wrld) wrld3))) (er-let* ((wrld5 (tau-visit-event t ev-type namex (tau-auto-modep wrld4) (ens state) ctx wrld4 state))) ; WARNING: Do not put down any properties here! The cltl-command should be the ; last property laid down before the call of add-event-landmark. We rely on ; this invariant when looking for 'redefined tuples in ; compile-uncompiled-defuns and compile-uncompiled-*1*-defuns. (let ((wrld6 (add-event-landmark form ev-type namex wrld5 (global-val 'boot-strap-flg currently-installed-wrld)))) (pprogn (f-put-global 'accumulated-ttree ttree state) (cond ((eq chk-theory-inv-p :protect) (revert-world-on-error (let ((state (set-w 'extension wrld6 state))) (er-progn (chk-theory-invariant1 :install (ens state) theory-invariant-table nil ctx state) (value val))))) (t (let ((state (set-w 'extension wrld6 state))) (cond (chk-theory-inv-p (er-progn (chk-theory-invariant1 :install (ens state) theory-invariant-table nil ctx state) (value val))) (t (value val))))))))))))) (deflabel redundant-events :doc ":Doc-Section Miscellaneous allowing a name to be introduced ``twice''~/ Sometimes an event will announce that it is ``redundant'', meaning that the the form is not evaluated because ACL2 determines that its effect is already incorporated into the logical ~il[world]. Thus, when this happens, no change to the logical ~il[world] takes place. This feature permits two independent ~il[books], each of which defines some name, to be included sequentially provided they use exactly the same definition. Note that by the definition above, a form can have no effect on the logical ~il[world] yet not be considered to be redundant. Here is an example of such a form. ~bv[] (value-triple (cw \"Hello world.~~%\")) ~ev[]~/ When are two ~il[logical-name] definitions considered ``the same''? It depends upon the kind of name being defined. We consider these below in alphabetical order. See also the Notes below. A ~ilc[defabsstobj] is redundant if there is already an identical ~c[defabsstobj] event in the logical ~il[world]. A ~ilc[defattach] event is never redundant. Note that it doesn't define any name. A ~ilc[defaxiom] or ~ilc[defthm] event is redundant if there is already an axiom or theorem of the given name and both the formula (after macroexpansion) and the ~il[rule-classes] are syntactically identical. Note that a ~ilc[defaxiom] can make a subsequent ~ilc[defthm] redundant, and a ~ilc[defthm] can make a subsequent ~ilc[defaxiom] redundant as well. A ~ilc[defconst] is redundant if the name is already defined either with a syntactically identical ~c[defconst] event or one that defines it to have the same value. A ~ilc[defdoc] event is never redundant because it doesn't define any name. A ~ilc[deflabel] event is never redundant. This means that if you have a ~ilc[deflabel] in a book and that book has been included (without error), then references to that label denote the point in ~il[history] at which the book introduced the label. See the note about shifting logical names, below. A ~ilc[defmacro] event is redundant if there is already a macro defined with the same name and syntactically identical arguments, ~il[guard], and body. A ~ilc[defpkg] event is redundant if a package of the same name with exactly the same imports has been defined. A ~ilc[defstobj] event is redundant if there is already a ~c[defstobj] event with the same name that has exactly the same field descriptors (~pl[defstobj]), in the same order, and with the same ~c[:renaming] value if ~c[:renaming] is supplied for either event. A ~ilc[defthm] event is redundant according to the criterion given above in the discussion of ~c[defaxiom]. A ~ilc[deftheory] is never redundant. The ``natural'' notion of equivalent ~ilc[deftheory] forms is that the names and values of the two theory expressions are the same. But since most theory expressions are sensitive to the context in which they occur, it seems unlikely to us that two ~ilc[deftheory]s coming from two sequentially included ~il[books] will ever have the same values. So we prohibit redundant theory definitions. If you try to define the same theory name twice, you will get a ``name in use'' error. A ~ilc[defun], ~ilc[defuns], or ~ilc[mutual-recursion] event is redundant if for each function to be introduced, there has already been introduced a function with the same name, the same formals, and syntactically identical ~c[:]~ilc[guard], ~c[:measure], type declarations, ~ilc[stobj]~c[s] declarations and ~c[body] (before macroexpansion), and an appropriate ~c[mode] (see the ``Note About Appropriate Modes'' below). Moreover, the order of the combined ~c[:]~ilc[guard] and type declarations must be the same in both cases. Exceptions:~nl[] (1) If either definition is declared ~c[:non-executable] (~pl[defun-nx]), then the two events must be identical.~nl[] (2) It is permissible for one definition to have a ~c[:]~il[guard] of ~c[t] and the other to have no explicit guard (hence, the guard is implicitly ~c[t]).~nl[] (3) The ~c[:measure] check is avoided if the old definition is non-recursive (and not defined within a ~ilc[mutual-recursion]) or we are skipping proofs (for example, during ~ilc[include-book]). Otherwise, the new definition may have a ~c[:measure] of ~c[(:? v1 ... vk)], where ~c[(v1 ... vk)] enumerates the variables occurring in the measure stored for the old definition.~nl[] (4) If either the old or new event is a ~ilc[mutual-recursion] event, then redundancy requires that both are ~ilc[mutual-recursion] events that define the same set of function symbols. An ~ilc[encapsulate] event is most commonly redundant when a syntactically identical ~ilc[encapsulate] has already been executed under the same ~ilc[default-defun-mode], ~ilc[default-ruler-extenders], and ~ilc[default-verify-guards-eagerness]. The full criterion for redundancy of ~c[encapsulate] events is more complex, for example ignoring contents of ~ilc[local] ~il[events]; ~pl[redundant-encapsulate]. An ~ilc[in-theory] event is never redundant. Note that it doesn't define any name. An ~ilc[include-book] event is redundant if the book has already been included. A call of ~ilc[make-event] is never redundant, as its argument is always evaluated to obtain the make-event expansion. However, that expansion may of course be redundant. A ~ilc[mutual-recursion] event is redundant according to the criteria in the discussion above for the case of a ~c[defun] event. A ~ilc[progn] event is never redundant: its subsidiary ~il[events] are always considered. Of course, its sub-events may themselves be redundant. If all of its sub-events are redundant ~-[] or more generally, if none of the sub-events changes the logical ~il[world] ~-[] then the ~c[progn] event also won't change the world. A ~ilc[push-untouchable] event is redundant if every name supplied is already a member of the corresponding list of untouchable symbols. A ~ilc[regenerate-tau-database] event is never redundant. Note that it doesn't define any name. A ~ilc[remove-untouchable] event is redundant if no name supplied is a member of the corresponding list of untouchable symbols. A ~ilc[reset-prehistory] event is redundant if it does not cause any change. A ~ilc[set-body] event is redundant if the indicated body is already the current body. A ~ilc[table] event not define any name. It is redundant when it sets the value already associated with a key of the table, or when it sets an entire table (using keyword ~c[:clear]) to its existing value; ~pl[table]. A ~ilc[verify-guards] event is redundant if the function has already had its ~il[guard]s verified. ~em[Note About Built-in (Predefined) Functions and Macros:] Redundancy is restricted for built-in macros and functions that have special raw Lisp code. Such redundancy is only legal in the context of ~ilc[LOCAL]. This restriction is needed for soundness, for technical reasons omitted here (details may be found in a long comment about redundant-events in source function ~c[chk-acceptable-defuns-redundancy]). ~em[Note About Appropriate Modes:] Suppose a function is being redefined and that the formals, guards, types, stobjs, and bodies are identical. When are the modes (~c[:]~ilc[program] or ~c[:]~ilc[logic]) ``appropriate?'' Identical modes are appropriate. But what if the old mode was ~c[:program] and the new mode is ~c[:logic]? This is appropriate, provided the definition meets the requirements of the logical definitional principle. That is, you may redefine ``redundantly'' a ~c[:program] mode function as a ~c[:logic] mode function provide the measure conjectures can be proved. This is what ~ilc[verify-termination] does. Now consider the reverse style of redefinition. Suppose the function was defined in ~c[:logic] mode and is being identically redefined in ~c[:program] mode. ACL2 will treat the redefinition as redundant, provided the appropriate criteria are met (as though it were in :logic mode). ~em[Note About Shifting Logical Names:] Suppose a book defines a function ~c[fn] and later uses ~c[fn] as a logical name in a theory expression. Consider the value of that theory expression in two different sessions. In session A, the book is included in a ~il[world] in which ~c[fn] is not already defined, i.e., in a ~il[world] in which the book's definition of ~c[fn] is not redundant. In session B, the book is included in a ~il[world] in which ~c[fn] is already identically defined. In session B, the book's definition of ~c[fn] is redundant. When ~c[fn] is used as a logical name in a theory expression, it denotes the point in ~il[history] at which ~c[fn] was introduced. Observe that those points are different in the two sessions. Hence, it is likely that theory expressions involving ~c[fn] will have different values in session A than in session B. This may adversely affect the user of your book. For example, suppose your book creates a theory via ~ilc[deftheory] that is advertised just to contain the names generated by the book. But suppose you compute the theory as the very last event in the book using: ~bv[] (set-difference-theories (universal-theory :here) (universal-theory fn)) ~ev[] where ~c[fn] is the very first event in the book and happens to be a ~ilc[defun] event. This expression returns the advertised set if ~c[fn] is not already defined when the book is included. But if ~c[fn] were previously (identically) defined, the theory is larger than advertised. The moral of this is simple: when building ~il[books] that other people will use, it is best to describe your ~il[theories] in terms of logical names that will not shift around when the ~il[books] are included. The best such names are those created by ~ilc[deflabel]. ~em[Note About Unfortunate Redundancies.] Notice that our syntactic criterion for redundancy of ~ilc[defun] ~il[events] may not allow redefinition to take effect unless there is a syntactic change in the definition. The following example shows how an attempt to redefine a function can fail to make any change. ~bv[] (set-ld-redefinition-action '(:warn . :overwrite) state) (defmacro mac (x) x) (defun foo (x) (mac x)) (defmacro mac (x) (list 'car x)) (set-ld-redefinition-action nil state) (defun foo (x) (mac x)) ; redundant, unfortunately; foo does not change (thm (equal (foo 3) 3)) ; succeeds, showing that redef of foo didn't happen ~ev[] The call of macro ~c[mac] was expanded away before storing the first definition of ~c[foo] for the theorem prover. Therefore, the new definition of ~c[mac] does not affect the expansion of ~c[foo] by the theorem prover, because the new definition of ~c[foo] is ignored. One workaround is first to supply a different definition of ~c[foo], just before the last definition of ~c[foo] above. Then that final definition will no longer be redundant. However, as a courtesy to users, we strengthen the redundancy check for function definitions when redefinition is active. If in the example above we remove the form ~c[(set-ld-redefinition-action nil state)], then the problem goes away: ~bv[] (set-ld-redefinition-action '(:warn . :overwrite) state) (defmacro mac (x) x) (defun foo (x) (mac x)) (defmacro mac (x) (list 'car x)) (defun foo (x) (mac x)) ; no longer redundant (thm (equal (foo 3) 3)) ; fails, as we would like ~ev[] To summarize: If a ~ilc[defun] form is submitted that meets the usual redundancy criteria, then it may be considered redundant even if a macro called in the definition has since been redefined. The analogous problem applie to constants, i.e., symbols defined by ~ilc[defconst] that occur in the definition body. However, if redefinition is currently active the problem goes away: that is, the redundancy check is strengthened to check the ``translated'' body, in which macro calls and constants defined by ~ilc[defconst] are expanded away. The above discussion for ~ilc[defun] forms applies to ~ilc[defconst] forms as well. However, for ~ilc[defmacro] forms ACL2 always checks translated bodies, so such bogus redundancy does not occur. Here is more complex example illustrating the limits of redefinition, based on one supplied by Grant Passmore. ~bv[] (defun n3 () 0) (defun n4 () 1) (defun n5 () (> (n3) (n4))) ; body is normalized to nil (thm (equal (n5) nil)) ; succeeds, trivially (set-ld-redefinition-action '(:warn . :overwrite) state) (defun n3 () 2) (thm (equal (n5) nil)) ; still succeeds, sadly ~ev[] We may expect the final ~ilc[thm] call to fail because of the following reasoning: ~c[(n5)] = ~c[(> (n3) (n4))] = ~c[(> 2 1)] = ~c[t]. Unfortunatly, the body of ~c[n5] was simplified (``normalized'') to ~c[nil] when ~c[n5] was admitted, so the redefinition of ~c[n3] is ignored during the final ~c[thm] call. (Such normalization can be avoided; see the brief discussion of ~c[:normalize] in the documentation for ~ilc[defun].) So, given this unfortunate situation, one might expect at this point simply to redefine ~c[n5] using the same definition as before, in order to pick up the new definition of ~c[n3]. Such ``redefinition'' would, however, be redundant, for the same reason as in the previous example: no syntactic change was made to the definition. Even with redefinition active, there is no change in the body of ~c[n5], even with macros and constants (defined by ~ilc[defconst]) expanded; there are none such! The same workaround applies as before: redefine ~c[n5] to be something different, and then redefine ~c[n5] again to be as desired. A related phenomenon can occur for ~ilc[encapsulate]. As explained above, an ~c[encapsulate] event is redundant if it is identical to one already in the database. (But a weaker condition applies in general; ~pl[redundant-encapsulate].) Consider then the following contrived example. ~bv[] (defmacro mac (x) x) (encapsulate () (defun foo (x) (mac x))) (set-ld-redefinition-action '(:warn . :overwrite) state) (defmacro mac (x) (list 'car x)) (encapsulate () (defun foo (x) (mac x))) ~ev[] The last ~c[encapsulate] event is redundant because it meets the criterion for redundancy: it is identical to the earlier ~c[encapsulate] event. Even though redefinition is active, and hence ACL2 ``should'' be able to see that the new ~ilc[defun] of ~c[foo] is not truly redundant, nevertheless the criterion for redundancy of ~ilc[encapsulate] allows the new ~c[encapsulate] form to be redundant. A workaround can be to add something trivial to the ~c[encapsulate], for example: ~bv[] (encapsulate () (deflabel try2) ; ``Increment'' to try3 next time, and so on. (defun foo (x) x)) ~ev[]") (defun stop-redundant-event-fn (ctx state extra-msg) (let ((chan (proofs-co state))) (pprogn (cond ((ld-skip-proofsp state) state) (t (io? event nil state (chan ctx extra-msg) (mv-let (col state) (fmt "The event " nil chan state nil) (mv-let (col state) (fmt-ctx ctx col chan state) (mv-let (col state) (fmt1 " is redundant. See :DOC ~ redundant-events.~#0~[~/ ~@1~]~%" (list (cons #\0 (if (null extra-msg) 0 1)) (cons #\1 extra-msg)) col chan state nil) (declare (ignore col)) state))) :default-bindings ((col 0))))) (value :redundant)))) (defmacro stop-redundant-event (ctx state &optional extra-msg) `(stop-redundant-event-fn ,ctx ,state ,extra-msg)) ; Examining the World ; The user must be given facilities for poking around the world. To describe ; where in the world he wishes to look, we provide "landmark descriptors." ; A landmark descriptor, lmd, identifies a given landmark in the world. ; It does this by "decoding" to either (COMMAND-LANDMARK . n) or ; (EVENT-LANDMARK . n), where n is an absolute command or event number, as ; appropriate. Then, using lookup-world-index, one can obtain the ; relevant world. The language of lmds is designed to let the user ; poke conveniently given the way we have chosen to display worlds. ; Below is a typical display: ; d 1 (DEFUN APP (X Y) ...) ; d 2 (DEFUN REV (X) ...) ; 3 (ENCAPSULATE (((HD *) => *)) ...) ; D (DEFTHM HD-CONS ...) ; D (DEFTHM HD-ATOM ...) ; 4 (IN-THEORY #) ; Observe firstly that the commands are always displayed in chronological ; order. ; Observe secondly that user commands are numbered consecutively. We ; adopt the policy that the commands are numbered from 1 starting with ; the first command after the boot-strap. Negative integers number ; the commands in "pre-history." These command numbers are not our ; absolute command numbers. Indeed, until we have completed the ; boot-strapping we don't know what "relative" command number to ; assign to the chronologically first command in the boot-strap. We ; therefore internally maintain only absolute command numbers and just ; artificially offset them by a certain baseline when we display them ; to the user. (defrec command-number-baseline-info (current permanent-p . original) nil) (defun absolute-to-relative-command-number (n wrld) (- n (access command-number-baseline-info (global-val 'command-number-baseline-info wrld) :current))) (defun relative-to-absolute-command-number (n wrld) (+ n (access command-number-baseline-info (global-val 'command-number-baseline-info wrld) :current))) (defun normalize-absolute-command-number (n wrld) ; We have arranged that the first value of this function is a flag, which is ; set iff n exceeds the maximum absolute command number in the current world. ; Our intention is to prevent expressions like ; :ubt (:here +1) ; from executing. (let ((m (max-absolute-command-number wrld))) (cond ((> n m) (mv t m)) ((< n 0) (mv nil 0)) (t (mv nil n))))) ; Observe thirdly that events that are not commands are unnumbered. ; They must be referred to by logical name. ; Command Descriptors (CD) ; The basic facilities for poking around the world will operate at the ; command level. We will define a class of objects called "command ; descriptors" which denote command landmarks in the current world. ; We will provide a function for displaying an event and its command ; block, but that will come later. ; The legal command descriptors and their meaning are shown below. N ; is an integer, name is a logical name, and cd is a command descriptor. ; :min -- the chronologically first command of the boot ; :start -- 0 at startup, but always refers to (exit-boot-strap-mode), even ; after a reset-prehistory command ; :max -- the most recently executed command -- synonymous with :x ; n -- the nth command landmark, as enumerated by relative command ; numbers ; name -- the command containing the event that introduced name ; (cd n) -- the command n removed from the one described by cd ; (:search pat cd1 cd2) -- search the interval from cd1 to cd2 for the first ; command whose form (or one of whose event forms) matches pat. ; By "match" we mean "contains all of the elements listed". ; We search FROM cd1 TO cd2, which will search backwards ; if cd2 > cd1. The special case (:search pat) means ; (:search pat :max 1). ; The search cd is implemented as follows: (defun tree-occur (x y) ; Does x occur in the cons tree y? (cond ((equal x y) t) ((atom y) nil) (t (or (tree-occur x (car y)) (tree-occur x (cdr y)))))) (defun cd-form-matchp (pat form) ; We determine whether the form matches pat. We support only a ; rudimentary notion of matching right now: pat is a true list of ; objects and each must occur in form. (cond ((symbolp form) ;eviscerated nil) ((null pat) t) ((tree-occur (car pat) form) (cd-form-matchp (cdr pat) form)) (t nil))) (defun cd-some-event-matchp (pat wrld) ; This is an odd function. At first, it was as simple predicate that ; determined whether some event form in the current command block ; matched pat. It returned t or nil. But then we changed it so that ; if it fails it returns the world as of the next command block. So ; if it returns t, it succeeded; non-t means failure and tells where ; to start looking next. (cond ((null wrld) nil) ((and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value)) wrld) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value) (cd-form-matchp pat (access-event-tuple-form (cddar wrld)))) t) (t (cd-some-event-matchp pat (cdr wrld))))) (defun cd-search (pat earliestp start-wrld end-wrld) ; Start-wrld is a world containing end-wrld as a predecessor. Both ; worlds start on a command landmark. Pat is a true list of objects. ; Earliestp it t or nil initially, but in general is either nil, t, or ; the last successfully matched world seen. ; We search from start-wrld through end-wrld looking for a command ; world that matches pat in the sense that either the command form ; itself or one of the event forms in the command block contains all ; the elements of pat. If earliestp is non-nil we return the ; chronologically earliest matching command world. If earliestp is ; nil we return the chronologically latest matching command world. (cond ((equal start-wrld end-wrld) (cond ((or (cd-form-matchp pat (access-command-tuple-form (cddar start-wrld))) (eq t (cd-some-event-matchp pat (cdr start-wrld)))) start-wrld) ((eq earliestp t) nil) (t earliestp))) ((cd-form-matchp pat (access-command-tuple-form (cddar start-wrld))) (cond (earliestp (cd-search pat start-wrld (scan-to-command (cdr start-wrld)) end-wrld)) (t start-wrld))) (t (let ((wrld1 (cd-some-event-matchp pat (cdr start-wrld)))) (cond ((eq wrld1 t) (cond (earliestp (cd-search pat start-wrld (scan-to-command (cdr start-wrld)) end-wrld)) (t start-wrld))) (t (cd-search pat earliestp wrld1 end-wrld))))))) (defun superior-command-world (wrld1 wrld ctx state) ; Given a world, wrld1, and the current ACL2 world, we return the ; world as of the command that gave rise to wrld1. We do this by ; scanning down wrld1 for the command landmark that occurred ; chronologically before it, increment the absolute command number ; found there by 1, and look that world up in the index. If no such ; world exists then this function has been called in a peculiar way, ; such as (progn (defun fn1 nil 1) (pc 'fn1)) at the top-level. ; Observe that when pc is called, there is not yet a command superior ; to the event fn1. Hence, when we scan down wrld1 (which starts at ; the event for fn1) we'll find the previous command number, and ; increment it to obtain a number that is too big. When this happens, ; we cause a soft error. (let ((prev-cmd-wrld (scan-to-command wrld1))) (cond ((<= (1+ (access-command-tuple-number (cddar prev-cmd-wrld))) (max-absolute-command-number wrld)) (value (lookup-world-index 'command (if prev-cmd-wrld (1+ (access-command-tuple-number (cddar prev-cmd-wrld))) 0) wrld))) (t (er soft ctx "We have been asked to find the about-to-be-most-recent ~ command landmark. We cannot do that because that ~ landmark hasn't been laid down yet!"))))) (defun er-decode-cd (cd wrld ctx state) (let ((msg "The object ~x0 is not a legal command descriptor. See ~ :DOC command-descriptor.")) (cond ((or (symbolp cd) (stringp cd)) (cond ((or (eq cd :max) (eq cd :x)) (value (scan-to-command wrld))) ((eq cd :min) (value (lookup-world-index 'command 0 wrld))) ((eq cd :start) (value (lookup-world-index 'command (access command-number-baseline-info (global-val 'command-number-baseline-info wrld) :original) wrld))) ((and (keywordp cd) (let ((str (symbol-name cd))) (and (eql (char str 0) #\X) (eql (char str 1) #\-) (mv-let (k pos) (parse-natural nil str 2 (length str)) (and k (= pos (length str))))))) ; This little piece of code parses :x-123 into (:max -123). (er-decode-cd (list :max (- (mv-let (k pos) (parse-natural nil (symbol-name cd) 2 (length (symbol-name cd))) (declare (ignore pos)) k))) wrld ctx state)) (t (er-let* ((ev-wrld (er-decode-logical-name cd wrld ctx state))) (superior-command-world ev-wrld wrld ctx state))))) ((integerp cd) (mv-let (flg n) (normalize-absolute-command-number (relative-to-absolute-command-number cd wrld) wrld) (cond (flg (er soft ctx "The object ~x0 is not a legal command descriptor ~ because it exceeds the current maximum command ~ number, ~x1." cd (absolute-to-relative-command-number n wrld))) (t (value (lookup-world-index 'command n wrld)))))) ((and (consp cd) (true-listp cd)) (case (car cd) (:SEARCH (cond ((and (or (= (length cd) 4) (= (length cd) 2)) (or (atom (cadr cd)) (true-listp (cadr cd)))) (let* ((pat (if (atom (cadr cd)) (list (cadr cd)) (cadr cd)))) (er-let* ((wrld1 (er-decode-cd (cond ((null (cddr cd)) :max) (t (caddr cd))) wrld ctx state)) (wrld2 (er-decode-cd (cond ((null (cddr cd)) 0) (t (cadddr cd))) wrld ctx state))) (let ((ans (cond ((>= (access-command-tuple-number (cddar wrld1)) (access-command-tuple-number (cddar wrld2))) (cd-search pat nil wrld1 wrld2)) (t (cd-search pat t wrld2 wrld1))))) (cond ((null ans) (er soft ctx "No command or event in the region from ~ ~x0 to ~x1 contains ~&2. See :MORE-DOC ~ command-descriptor." (cond ((null (cddr cd)) :x) (t (caddr cd))) (cond ((null (cddr cd)) 0) (t (cadddr cd))) pat cd)) (t (value ans))))))) (t (er soft ctx msg cd)))) (otherwise (cond ((and (consp (cdr cd)) (integerp (cadr cd)) (null (cddr cd))) (er-let* ((wrld1 (er-decode-cd (car cd) wrld ctx state))) (mv-let (flg n) (normalize-absolute-command-number (+ (access-command-tuple-number (cddar wrld1)) (cadr cd)) wrld) (cond (flg (er soft ctx "The object ~x0 is not a legal ~ command descriptor because it ~ represents command number ~x1, ~ which exceeds the current maximum ~ command number, ~x2." cd (absolute-to-relative-command-number (+ (access-command-tuple-number (cddar wrld1)) (cadr cd)) wrld) (absolute-to-relative-command-number n wrld))) (t (value (lookup-world-index 'command n wrld))))))) (t (er soft ctx msg cd)))))) (t (er soft ctx msg cd))))) ; Displaying Events and Commands ; When we display an event we will also show the "context" in which ; it occurs, by showing the command block. The rationale is that ; the user cannot undo back to any random event -- he must always ; undo an entire command block -- and thus our convention serves to ; remind him of what will fall should he undo the displayed event. ; Similarly, when we display a command we will sketch the events in ; its block, to remind him of all the effects of that command. ; The commands in the display will be numbered sequentially. Command ; 1 will be the first command typed by the user after bootstrap. Negative ; command numbers refer to prehistoric commands. ; Commands will be displayed in chronological order. This means we ; must print them in the reverse of the order in which we encounter ; them in the world. Actually, it is not exactly the reverse, because ; we wish to print commands, encapsulates, and include-books before ; their events, but they are stored on the world after their events. ; Because some events include others it is possible for the user to ; accidentally ask us to print out large blocks, even though the ; interval specified, e.g., commands 1 through 3, is small. This ; means that a tail recursive implementation is desirable. (We could ; print in reverse order by printing on the way out of the recursion.) ; Because of all these complications, we have adopted a two pass ; approach to printing out segments of the world. Both passes are ; tail recursive. During the first, we collect a list of "landmark ; display directives" (ldd's) and during the second we interpret those ; directives to display the landmarks. Roughly speaking, each ldd ; corresponds to one line of the display. ; Note to Software Archaeologists of the Future: ; As you study this code, you may wonder why the authors are so ; persistent in inventing long, pompous sounding names, e.g., ; "landmark display directives" or "prove spec var" and then ; shortening them to unpronounceable letter sequences, e.g., "ldd" and ; "pspv". This certainly goes against the grain of some software ; scientists who rail against unpronounceable names, acronyms, and ; unnecessary terminology in general. For the record, we are not ; unsympathetic to their pleas. However, by adopting these ; conventions we make it easy to use Emacs to find out where these ; objects are documented and manipulated. Until this code was added ; to the system, the character string "ldd" did not occur in it. Big ; surprise! Had we used some perfectly reasonable English word, e.g., ; "line" (as we might have had we described this code in isolation ; from all other) there would be many false matches. Of course, we ; could adopt an ordinary word, e.g., "item" that just happened not to ; occur in our sources. But unfortunately this suffers not only from ; giving a very technical meaning to an ordinary word, but offers no ; protection against the accidental use of the word later in a ; confusing way. Better, we thought, to come up with the damn ; acronyms which one pretty much has to know about to use. ; In addition to telling us the form to print, an ldd must tell us the ; form to print, whether it is a command or an event, the command ; number, whether it is to be printed in full or only sketched, ; whether it is to be marked, whether it is fully, partially, or not ; disabled, and how far to indent it. (defrec ldd-status (defun-mode-pair disabled memoized) nil) ; could change to t after awhile; this new record was created April 2011 (defun make-ldd-flags (class markp status fullp) ; Class is 'COMMAND or 'EVENT, markp is t or nil indicating whether we are to ; print the ">" beside the line, status is a record containing characters that ; indicate the defun-mode and disabled status (also memoized status for the ; HONS version), and fullp is t or nil indicating whether we are to print the ; form in full or just sketch it. Once upon a time this fn didn't do any ; consing because there were only a small number of combinations and they were ; all built in. But with the introduction of colors (which became defun-modes) ; that strategy lost its allure. (cons (cons class markp) (cons status fullp))) (defun make-ldd (class markp status n fullp form) ; Class is 'command or 'event. ; Markp is t or nil, indicating whether we are to print a ">". ; Status is a an ldd-status record indicating defun-mode, disabled, and ; (for the HONS version) memoized status. ; n is a natural number whose interpretation depends on class: ; if class is 'command, n is the command number; otherwise, ; n is how far we are to indent, where 1 means indent one ; space in from the command column. ; fullp is t or nil, indicating whether we are to print the form ; in full or only sketch it. ; form is the form to print. (cons (make-ldd-flags class markp status fullp) (cons n form))) (defun access-ldd-class (ldd) (caaar ldd)) (defun access-ldd-markp (ldd) (cdaar ldd)) (defun access-ldd-status (ldd) (cadar ldd)) (defun access-ldd-fullp (ldd) (cddar ldd)) (defun access-ldd-n (ldd) (cadr ldd)) (defun access-ldd-form (ldd) (cddr ldd)) (defun big-d-little-d-name1 (lst ens ans) ; Lst is a list of runic-mapping-pairs. The car of each pair is a nume. We ; are considering the enabled status of the runes (numes) in lst. If all ; members of the list are enabled, we return #\E. If all are disabled, we ; return #\D. If some are enabled and some are disabled, we return #\d. Ans ; is #\E or #\D signifying that we have seen some runes so far and they are all ; enabled or disabled as indicated. (cond ((null lst) ans) ((equal ans (if (enabled-numep (caar lst) ens) #\E #\D)) (big-d-little-d-name1 (cdr lst) ens ans)) (t #\d))) (defun big-d-little-d-name (name ens wrld) ; Name is a symbol. If it is the basic symbol of some nonempty set of runes, ; then we return either #\D, #\d, or #\E, depending on whether all, some, or ; none of the runes based on name are disabled. If name is not the basic ; symbol of any rune, we return #\Space. (let ((temp (getprop name 'runic-mapping-pairs nil 'current-acl2-world wrld))) (cond ((null temp) #\Space) (t (big-d-little-d-name1 (cdr temp) ens (if (enabled-numep (caar temp) ens) #\E #\D)))))) (defun big-d-little-d-clique1 (names ens wrld ans) ; Same drill, one level higher. Names is a clique of function symbols. Ans is ; #\E or #\D indicating that all the previously seen names in the clique ; are enabled or disabled as appropriate. We return #\E, #\D or #\d. (cond ((null names) ans) (t (let ((ans1 (big-d-little-d-name (car names) ens wrld))) (cond ((eql ans1 #\d) #\d) ((eql ans1 ans) (big-d-little-d-clique1 (cdr names) ens wrld ans)) (t #\d)))))) (defun big-d-little-d-clique (names ens wrld) ; Names is a list of function symbols. As such, each symbol in it is the basic ; symbol of a set of runes. If all of the runes are enabled, we return ; #\E, if all are disabled, we return #\D, and otherwise we return #\d. We ; assume names is non-nil. (let ((ans (big-d-little-d-name (car names) ens wrld))) (cond ((eql ans #\d) #\d) (t (big-d-little-d-clique1 (cdr names) ens wrld ans))))) (defun big-d-little-d-event (ev-tuple ens wrld) ; This function determines the enabled/disabled status of an event. Ev-Tuple ; is an event tuple. Wrld is the current ACL2 world. ; We return #\D, #\d, #\E or #\Space, with the following interpretation: ; #\D - at least one rule was added by this event and all rules added ; are currently disabled; ; #\d - at least one rule was added by this event and at least one rule added ; is currently disabled and at least one rule added is currently enabled. ; #\E - at least one rule was added by this event and all rules added ; are currently enabled. ; #\Space - no rules were added by this event. ; Note that we do not usually print #\E and we mash it together with #\Space to ; mean all rules added, if any, are enabled. But we need the stronger ; condition to implement our handling of blocks of events. (let ((namex (access-event-tuple-namex ev-tuple))) (case (access-event-tuple-type ev-tuple) ((defun defthm defaxiom) (big-d-little-d-name namex ens wrld)) (defuns (big-d-little-d-clique namex ens wrld)) (defstobj (big-d-little-d-clique (cddr namex) ens wrld)) (otherwise #\Space)))) (defun big-d-little-d-command-block (wrld1 ens wrld s) ; Same drill, one level higher. We scan down wrld1 to the next command ; landmark inspecting each of the event landmarks in the current command block. ; (Therefore, initially wrld1 ought to be just past the command landmark for ; the block in question.) We determine whether this command ought to have a ; #\D, #\E, #\d, or #\Space printed beside it, by collecting that information for ; each event in the block. Wrld is the current ACL2 world and is used to ; obtain both the current global enabled structure and the numes of the runes ; involved. ; The interpretation of the character is as described in big-d-little-d-event. ; We sweep through the events accumulating our final answer in s, which we ; think of as a "state" (but not STATE). The interpretation of s is: ; #\D - we have seen at least one event with status #\D and all the ; events we've seen have status #\D or status #\Space. ; #\E - we have seen at least one event with status #\E and all the ; events we've seen have status #\E or status #\Space ; #\Space - all events seen so far (if any) have status #\Space (cond ((or (null wrld1) (and (eq (caar wrld1) 'command-landmark) (eq (cadar wrld1) 'global-value))) s) ((and (eq (caar wrld1) 'event-landmark) (eq (cadar wrld1) 'global-value)) (let ((s1 (big-d-little-d-event (cddar wrld1) ens wrld))) ; S1 = #\D, #\E, #\d, or #\Space (cond ((or (eql s s1) (eql s1 #\Space)) (big-d-little-d-command-block (cdr wrld1) ens wrld s)) ((or (eql s1 #\d) (and (eql s #\E) (eql s1 #\D)) (and (eql s #\D) (eql s1 #\E))) #\d) (t ; s must be #\Space (big-d-little-d-command-block (cdr wrld1) ens wrld s1))))) (t (big-d-little-d-command-block (cdr wrld1) ens wrld s)))) (defun big-m-little-m-name (name wrld) ; This function, which supports the printing of the memoization status, is ; analogous to function big-d-little-d-name, which supports the printing of the ; disabled status. (cond ((and (function-symbolp name wrld) (not (getprop name 'constrainedp nil 'current-acl2-world wrld))) (if (memoizedp-world name wrld) #\M #\E)) (t #\Space))) (defun big-m-little-m-clique1 (names wrld ans) ; This function, which supports the printing of the memoization status, is ; analogous to function big-d-little-d-clique1, which supports the printing of ; the disabled status. (cond ((null names) ans) (t (let ((ans1 (big-m-little-m-name (car names) wrld))) (cond ((eql ans1 #\m) #\m) ((eql ans1 ans) (big-m-little-m-clique1 (cdr names) wrld ans)) (t #\m)))))) (defun big-m-little-m-clique (names wrld) ; This function, which supports the printing of the memoization status, is ; analogous to function big-d-little-d-clique, which supports the printing of ; the disabled status. (let ((ans (big-m-little-m-name (car names) wrld))) (cond ((eql ans #\m) #\m) (t (big-m-little-m-clique1 (cdr names) wrld ans))))) (defun big-m-little-m-event (ev-tuple wrld) ; This function, which supports the printing of the memoization status, is ; analogous to function big-d-little-d-event, which supports the printing of ; the disabled status. (let ((namex (access-event-tuple-namex ev-tuple))) (case (access-event-tuple-type ev-tuple) ((defun) (big-m-little-m-name namex wrld)) (defuns (big-m-little-m-clique namex wrld)) (defstobj (big-m-little-m-clique (cddr namex) wrld)) (otherwise #\Space)))) (defun big-m-little-m-command-block (wrld1 wrld s) ; This function, which supports the printing of the memoization status, is ; analogous to function big-d-little-d-command-block, which supports the ; printing of the disabled status. (cond ((or (null wrld1) (and (eq (caar wrld1) 'command-landmark) (eq (cadar wrld1) 'global-value))) s) ((and (eq (caar wrld1) 'event-landmark) (eq (cadar wrld1) 'global-value)) (let ((s1 (big-m-little-m-event (cddar wrld1) wrld))) ; S1 = #\M, #\E, #\m, or #\Space (cond ((or (eql s s1) (eql s1 #\Space)) (big-m-little-m-command-block (cdr wrld1) wrld s)) ((or (eql s1 #\m) (and (eql s #\E) (eql s1 #\M)) (and (eql s #\M) (eql s1 #\E))) #\m) (t ; s must be #\Space (big-m-little-m-command-block (cdr wrld1) wrld s1))))) (t (big-m-little-m-command-block (cdr wrld1) wrld s)))) (defun symbol-class-char (symbol-class) ; Note: If you change the chars used below, recall that big-c-little-c-event ; knows that #\v is the symbol-class char for encapsulated fns. (case symbol-class (:program #\P) (:ideal #\L) (:common-lisp-compliant #\V) (otherwise #\Space))) (defun defun-mode-string (defun-mode) (case defun-mode (:logic ":logic") (:program ":program") (otherwise (er hard 'defun-mode-string "Unrecognized defun-mode, ~x0." defun-mode)))) (defun big-c-little-c-event (ev-tuple wrld) ; The big-c-little-c of an event tuple with non-0 namex is a pair of ; characters, (c1 . c2), where each indicates a symbol-class. C1 indicates the ; introductory symbol-class of the namex. C2 indicates the current ; symbol-class. However, if the current class is the same as the introductory ; one, c2 is #\Space. Note that all elements of namex have the same ; symbol-class forever. (Only defuns and encapsulate events introduce more ; than one name, and those cliques of functions are in the same class forever.) ; If an event tuple introduces no names, we return (#\Space . #\Space). ; Note: The name big-c-little-c-event is a misnomer from an earlier age. (case (access-event-tuple-type ev-tuple) ((defuns defun defstobj) (let ((c1 (symbol-class-char (access-event-tuple-symbol-class ev-tuple))) (c2 (symbol-class-char (let ((namex (access-event-tuple-namex ev-tuple))) (cond ((symbolp namex) (symbol-class namex wrld)) (t (symbol-class (car namex) wrld))))))) (cond ((eql c1 c2) (cons c1 #\Space)) (t (cons c1 c2))))) (encapsulate '(#\v . #\Space)) (otherwise '(#\Space . #\Space)))) (defun big-c-little-c-command-block (wrld1 wrld s) ; This function determines the big-c-little-c pair of a block of events. If ; the block contains more than one event, its pair is (#\Space . #\Space) ; because we expect the individual events in the block to have their own ; pairs printed. If the block contains only one event, its pair is ; the pair of the block, because we generally print such blocks as the ; event. ; We scan down wrld1 to the next command landmark inspecting each of the event ; landmarks in the current command block. (Therefore, initially wrld1 ought to ; be just past the command landmark for the block in question.) S is initially ; nil and is set to the pair of the first event we find. Upon finding a ; second event we return (#\Space . #\Space), but if we get to the end of the ; block, we return s. (cond ((or (null wrld1) (and (eq (caar wrld1) 'command-landmark) (eq (cadar wrld1) 'global-value))) ; Can a block contain no events? I don't know anymore. But if so, its ; defun-mode is '(#\Space . #\Space). (or s '(#\Space . #\Space))) ((and (eq (caar wrld1) 'event-landmark) (eq (cadar wrld1) 'global-value)) (cond (s '(#\Space . #\Space)) (t (big-c-little-c-command-block (cdr wrld1) wrld (big-c-little-c-event (cddar wrld1) wrld))))) (t (big-c-little-c-command-block (cdr wrld1) wrld s)))) ; Now we turn to the problem of printing according to some ldd. We first ; develop the functions for sketching a command or event form. This is ; like evisceration (indeed, it uses the same mechanisms) but we handle ; commonly occurring event and command forms specially so that we see ; what we often want to see and no more. (defun print-ldd-full-or-sketch/mutual-recursion (lst) ; See print-ldd-full-or-sketch. (cond ((null lst) nil) (t (cons (list 'defun (cadr (car lst)) (caddr (car lst)) *evisceration-ellipsis-mark*) (print-ldd-full-or-sketch/mutual-recursion (cdr lst)))))) (defun print-ldd-full-or-sketch/encapsulate (lst) ; See print-ldd-full-or-sketch. (cond ((null lst) nil) (t (cons (list (car (car lst)) *evisceration-ellipsis-mark*) (print-ldd-full-or-sketch/encapsulate (cdr lst)))))) ; If a form has a documentation string in the database, we avoid printing ; the string. We'll develop the general handling of doc strings soon. But ; for now we have to define the function that recognizes when the user ; intends his string to be inserted into the database. (defun normalize-char (c hyphen-is-spacep) (if (or (eql c #\Newline) (and hyphen-is-spacep (eql c #\-))) #\Space (char-upcase c))) (defun normalize-string1 (str hyphen-is-spacep j ans) (cond ((< j 0) ans) (t (let ((c (normalize-char (char str j) hyphen-is-spacep))) (normalize-string1 str hyphen-is-spacep (1- j) (cond ((and (eql c #\Space) (eql c (car ans))) ans) (t (cons c ans)))))))) (defun normalize-string (str hyphen-is-spacep) ; Str is a string for which we wish to search. A normalized pattern ; is a list of the chars in the string, all of which are upper cased ; with #\Newline converted to #\Space and adjacent #\Spaces collapsed ; to one #\Space. If hyphen-is-spacep is t, #\- is normalized to ; #\Space too. (normalize-string1 str hyphen-is-spacep (1- (length str)) nil)) (defun string-matchp (pat-lst str j jmax normp skippingp) ; Pat-lst is a list of characters. Str is a string of length jmax. ; 0<=j= j jmax) nil) (t (let ((c (if normp (normalize-char (char str j) (eq normp 'hyphen-is-space)) (char str j)))) (cond ((and skippingp (eql c #\Space)) (string-matchp pat-lst str (1+ j) jmax normp t)) (t (and (eql c (car pat-lst)) (string-matchp (cdr pat-lst) str (1+ j) jmax normp (if normp (eql c #\Space) nil))))))))) (defun string-search1 (pat-lst str j j-max normp) (cond ((< j j-max) (if (string-matchp pat-lst str j j-max normp nil) j (string-search1 pat-lst str (1+ j) j-max normp))) (t nil))) (defun string-search (pat str normp) ; We ask whether pat occurs in str, normalizing both according to ; normp, which is either nil, t, or 'hyphen-is-space. If pat is ; a consp, we assume it is already normalized appropriately. (string-search1 (if (consp pat) pat (if normp (normalize-string pat (eq normp 'hyphen-is-space)) (coerce pat 'list))) str 0 (length str) normp)) (defun doc-stringp (str) ; If this function returns t then the first character (if any) after ; the matching #\Space in the string is at index 13. (and (stringp str) (<= 13 (length str)) (string-matchp '(#\: #\D #\O #\C #\- #\S #\E #\C #\T #\I #\O #\N #\Space) str 0 13 t nil))) ; So now we continue with the development of printing event forms. (defconst *zapped-doc-string* "Documentation available via :doc") (defun zap-doc-string-from-event-form/all-but-last (lst) (cond ((null lst) nil) ((null (cdr lst)) lst) ((doc-stringp (car lst)) (cons *zapped-doc-string* (zap-doc-string-from-event-form/all-but-last (cdr lst)))) (t (cons (car lst) (zap-doc-string-from-event-form/all-but-last (cdr lst)))))) (defun zap-doc-string-from-event-form/second-arg (form) ; Zap a doc string if it occurs in the second arg, e.g., ; (defdoc x doc). (cond ((doc-stringp (third form)) (list (first form) (second form) *zapped-doc-string*)) (t form))) (defun zap-doc-string-from-event-form/third-arg (form) ; Zap a doc string if it occurs in the third arg, e.g., ; (defconst x y doc). But form may only be ; (defconst x y). (cond ((doc-stringp (fourth form)) (list (first form) (second form) (third form) *zapped-doc-string*)) (t form))) (defun zap-doc-string-from-event-form/mutual-recursion (lst) (cond ((null lst) nil) (t (cons (zap-doc-string-from-event-form/all-but-last (car lst)) (zap-doc-string-from-event-form/mutual-recursion (cdr lst)))))) (defun zap-doc-string-from-event-form/doc-keyword (lst) ; This function is supposed to zap out a doc string if it occurs in ; :doc keyword slot. But study the recursion and you'll find that it ; will zap a doc string str that occurs as in: (fn :key1 :doc str). ; Now is that in the :doc keyword slot or not? If the first arg is ; normal, then it is. If the first arg is a : keyword then the :doc ; is a value, BUT then str is in a string in another keyword position. ; So I think this function is actually correct. It doesn't really ; matter since it is just for informative purposes. (cond ((null lst) nil) ((null (cdr lst)) lst) ((and (eq (car lst) :doc) (doc-stringp (cadr lst))) (cons :doc (cons *zapped-doc-string* (zap-doc-string-from-event-form/doc-keyword (cddr lst))))) (t (cons (car lst) (zap-doc-string-from-event-form/doc-keyword (cdr lst)))))) (defun zap-doc-string-from-event-form (form) (case (car form) ((defun defmacro) (zap-doc-string-from-event-form/all-but-last form)) (mutual-recursion (cons 'mutual-recursion (zap-doc-string-from-event-form/mutual-recursion (cdr form)))) ((defthm defaxiom deflabel deftheory include-book defchoose) (zap-doc-string-from-event-form/doc-keyword form)) ((defdoc) (zap-doc-string-from-event-form/second-arg form)) ((defconst defpkg) (zap-doc-string-from-event-form/third-arg form)) ((verify-guards in-theory in-arithmetic-theory regenerate-tau-database push-untouchable remove-untouchable reset-prehistory table encapsulate defstobj) form) (otherwise form))) (defun print-ldd-full-or-sketch (fullp form) ; When fullp is nil, this function is like eviscerate with print-level ; 2 and print-length 3, except that we here recognize several special ; cases. We return the eviscerated form. ; Forms with the cars shown below are always eviscerated as ; shown: ; (defun name args ...) ; (defmacro name args ...) ; (defthm name ...) ; (mutual-recursion (defun name1 args1 ...) etc) ; (encapsulate ((name1 ...) etc) ...) ; which is also: ; (encapsulate (((P * *) ...) etc) ...) ; When fullp is t we zap the documentation strings out of event ; forms. ; It is assumed that form is well-formed. In particular, that it has ; been evaluated without error. Thus, if its car is defun, for ; example, it really is of the form (defun name args dcls* body). ; Technically, we should eviscerate the name and args to ensure that ; any occurrence of the *evisceration-mark* in them is properly protected. ; But the mark is a keyword and inspection of the special forms above ; reveals that there are no keywords among the uneviscerated parts. (cond ((atom form) form) (fullp (zap-doc-string-from-event-form form)) (t (case (car form) ((defun defund defmacro) (list (car form) (cadr form) (caddr form) *evisceration-ellipsis-mark*)) ((defthm defthmd) (list (car form) (cadr form) *evisceration-ellipsis-mark*)) (defdoc (list 'defdoc (cadr form) *evisceration-ellipsis-mark*)) (mutual-recursion (cons 'mutual-recursion (print-ldd-full-or-sketch/mutual-recursion (cdr form)))) (encapsulate (list 'encapsulate (print-ldd-full-or-sketch/encapsulate (cadr form)) *evisceration-ellipsis-mark*)) (t (eviscerate-simple form 2 3 nil nil nil)))))) (defmacro with-base-10 (form) ; Form evaluates to state. Here, we want to evaluate form with the print base ; set to 10 and the print radix set to nil. ; In order to avoid parallelism hazards due to wormhole printing from inside ; the waterfall (see for example (io? prove t ...) in waterfall-msg), we avoid ; calling state-global-let* below when the print-base is already 10, as it ; typically will be (see with-ctx-summarized). The downside is that we are ; replicating the code, form. Without this change, if you build ACL2 with ; #+acl2-par, then evaluate the following forms, you'll see lots of parallelism ; hazard warnings. ; :mini-proveall ; :ubt! force-test ; (set-waterfall-parallelism :pseudo-parallel) ; (set-waterfall-printing :full) ; (f-put-global 'parallelism-hazards-action t state) ; (DEFTHM FORCE-TEST ...) ; see mini-proveall `(cond ((and (eq (f-get-global 'print-base state) 10) (eq (f-get-global 'print-radix state) nil)) ,form) (t (mv-let (erp val state) (state-global-let* ((print-base 10 set-print-base) (print-radix nil set-print-radix)) (pprogn ,form (value nil))) (declare (ignore erp val)) state)))) (defun print-ldd-formula-column (state) (cond ((hons-enabledp state) ; extra column for the memoization status 14) (t 13))) (defun print-ldd (ldd channel state) ; This is the general purpose function for printing out an ldd. (with-base-10 (let ((formula-col (if (eq (access-ldd-class ldd) 'command) (print-ldd-formula-column state) (+ (print-ldd-formula-column state) (access-ldd-n ldd)))) (status (access-ldd-status ldd))) (declare (type (signed-byte 30) formula-col)) (pprogn (princ$ (if (access-ldd-markp ldd) (access-ldd-markp ldd) #\Space) channel state) (let ((defun-mode-pair (access ldd-status status :defun-mode-pair))) (pprogn (princ$ (car defun-mode-pair) channel state) (princ$ (cdr defun-mode-pair) channel state))) (let ((disabled (access ldd-status status :disabled))) (princ$ (if (eql disabled #\E) #\Space disabled) channel state)) (if (hons-enabledp state) (let ((memoized (access ldd-status status :memoized))) (princ$ (if (eql memoized #\E) #\Space memoized) channel state)) state) (let ((cur-col (if (hons-enabledp state) 5 4))) (if (eq (access-ldd-class ldd) 'command) (mv-let (col state) (fmt1 "~c0~s1" (list (cons #\0 (cons (access-ldd-n ldd) 7)) (cons #\1 (cond ((= (access-ldd-n ldd) (absolute-to-relative-command-number (max-absolute-command-number (w state)) (w state))) ":x") (t " ")))) cur-col channel state nil) (declare (ignore col)) state) (spaces (- formula-col cur-col) cur-col channel state))) (fmt-ppr (print-ldd-full-or-sketch (access-ldd-fullp ldd) (access-ldd-form ldd)) t (+f (fmt-hard-right-margin state) (-f formula-col)) 0 formula-col channel state (not (access-ldd-fullp ldd))) (newline channel state))))) (defun print-ldds (ldds channel state) (cond ((null ldds) state) (t (pprogn (print-ldd (car ldds) channel state) (print-ldds (cdr ldds) channel state))))) ; Now we turn to the problem of assembling lists of ldds. There are ; currently three different situations in which we do this and rather ; than try to unify them, we write a special-purpose function for ; each. The three situations are: ; (1) When we wish to print out a sequence of commands: We print only the ; commands, not their events, and we only sketch each command. We ; mark the endpoints. ; (2) When we wish to print out an entire command block, meaning the ; command and each of its events: We will print the command in ; full and marked, and we will only sketch each event. We will ; not show any events in the special case that there is only one ; event and it has the same form as the command. This function, ; make-ldd-command-block, is the simplest of our functions that ; deals with a mixture of commands and events. It has to crawl ; over the world, reversing the order (more or less) of the events ; and taking the command in at the end. ; (3) When we wish to print out an event and its context: This is like ; case (2) above in that we print a command and its block. But we ; only sketch the forms involved, except for the event requested, ; which we print marked and in full. To make things monumentally ; more difficult, we also elide away irrelevant events in the ; block. (defun make-command-ldd (markp fullp cmd-wrld ens wrld) (make-ldd 'command markp (make ldd-status :defun-mode-pair (big-c-little-c-command-block (cdr cmd-wrld) wrld nil) :disabled (big-d-little-d-command-block (cdr cmd-wrld) ens wrld #\Space) :memoized (and (global-val 'hons-enabled wrld) ; else don't care (big-m-little-m-command-block (cdr cmd-wrld) wrld #\Space))) (absolute-to-relative-command-number (access-command-tuple-number (cddar cmd-wrld)) wrld) fullp (access-command-tuple-form (cddar cmd-wrld)))) (defun make-event-ldd (markp indent fullp ev-tuple ens wrld) (make-ldd 'event markp (make ldd-status :defun-mode-pair (big-c-little-c-event ev-tuple wrld) :disabled (big-d-little-d-event ev-tuple ens wrld) :memoized (and (global-val 'hons-enabled wrld) ; else don't care (big-m-little-m-event ev-tuple wrld))) indent fullp (access-event-tuple-form ev-tuple))) (defun make-ldds-command-sequence (cmd-wrld1 cmd2 ens wrld markp ans) ; Cmd-wrld1 is a world that starts on a command landmark. Cmd2 is a command ; tuple somewhere in cmd-wrld1 (that is, cmd1 occurred chronologically after ; cmd2). We assemble onto ans the ldds for sketching each command between the ; two. We mark the two endpoints provided markp is t. If we mark, we use / as ; the mark for the earliest command and \ as the mark for the latest, so that ; when printed chronologically the marks resemble the ends of a large brace. ; If only one command is in the region, we mark it with the pointer character, ; >. (cond ((equal (cddar cmd-wrld1) cmd2) (cons (make-command-ldd (and markp (cond ((null ans) #\>) (t #\/))) nil cmd-wrld1 ens wrld) ans)) (t (make-ldds-command-sequence (scan-to-command (cdr cmd-wrld1)) cmd2 ens wrld markp (cons (make-command-ldd (and markp (cond ((null ans) #\\)(t nil))) nil cmd-wrld1 ens wrld) ans))))) (defun make-ldds-command-block1 (wrld1 cmd-ldd indent fullp super-stk ens wrld ans) ; Wrld1 is a world created by the command tuple described by cmd-ldd. ; Indent is the current indent value for the ldds we create. ; Super-stk is a list of event tuples, each of which is a currently ; open superior event (e.g., encapsulation or include-book). We wish ; to make a list of ldds for printing out that command and every event ; in its block. We print the command marked and in full. We only ; sketch the events, but we sketch each of them. This is the simplest ; function that shows how to crawl down a world and produce ; print-order ldds that suggest the structure of a block. (cond ((or (null wrld1) (and (eq (caar wrld1) 'command-landmark) (eq (cadar wrld1) 'global-value))) (cond (super-stk (make-ldds-command-block1 wrld1 cmd-ldd (1- indent) fullp (cdr super-stk) ens wrld (cons (make-event-ldd nil (1- indent) fullp (car super-stk) ens wrld) ans))) (t (cons cmd-ldd ans)))) ((and (eq (caar wrld1) 'event-landmark) (eq (cadar wrld1) 'global-value)) (cond ((and super-stk (<= (access-event-tuple-depth (cddar wrld1)) (access-event-tuple-depth (car super-stk)))) (make-ldds-command-block1 wrld1 cmd-ldd (1- indent) fullp (cdr super-stk) ens wrld (cons (make-event-ldd nil (1- indent) fullp (car super-stk) ens wrld) ans))) ((or (eq (access-event-tuple-type (cddar wrld1)) 'encapsulate) (eq (access-event-tuple-type (cddar wrld1)) 'include-book)) (make-ldds-command-block1 (cdr wrld1) cmd-ldd (1+ indent) fullp (cons (cddar wrld1) super-stk) ens wrld ans)) (t (make-ldds-command-block1 (cdr wrld1) cmd-ldd indent fullp super-stk ens wrld (cons (make-event-ldd nil indent fullp (cddar wrld1) ens wrld) ans))))) (t (make-ldds-command-block1 (cdr wrld1) cmd-ldd indent fullp super-stk ens wrld ans)))) (defun make-ldds-command-block (cmd-wrld ens wrld fullp ans) ; Cmd-wrld is a world starting with a command landmark. We make a list of ldds ; to describe the entire command block, sketching the command and sketching ; each of the events contained within the block. (let ((cmd-ldd (make-command-ldd nil fullp cmd-wrld ens wrld)) (wrld1 (scan-to-event (cdr cmd-wrld)))) (cond ((equal (access-event-tuple-form (cddar wrld1)) (access-command-tuple-form (cddar cmd-wrld))) ; If the command form is the same as the event form of the ; chronologically last event then that event is to be skipped. (make-ldds-command-block1 (cdr wrld1) cmd-ldd 1 fullp nil ens wrld ans)) (t (make-ldds-command-block1 wrld1 cmd-ldd 1 fullp nil ens wrld ans))))) (defun pcb-pcb!-fn (cd fullp state) (io? temporary nil (mv erp val state) (cd fullp) (let ((wrld (w state)) (ens (ens state))) (er-let* ((cmd-wrld (er-decode-cd cd wrld :pcb state))) (pprogn (print-ldds (make-ldds-command-block cmd-wrld ens wrld fullp nil) (standard-co state) state) (value :invisible)))))) (defun pcb!-fn (cd state) (pcb-pcb!-fn cd t state)) (defun pcb-fn (cd state) (pcb-pcb!-fn cd nil state)) (defmacro pcb! (cd) ":Doc-Section History print in full the ~il[command] block described by a ~il[command] descriptor~/ ~bv[] Examples: :pcb! :max ; print the most recent command block :pcb! :x ; print the most recent command block :pcb! fn ; print the command block that introduced fn :pcb! 5 ; print the fifth command block ~ev[] ~l[command-descriptor].~/ ~c[Pcb!] takes one argument, a ~il[command] descriptor, and prints the ~il[command] block of the ~il[command] described. Unlike ~ilc[pcb], ~c[pcb!] prints the event forms in full; ~pl[pcb] for details.~/" (list 'pcb!-fn cd 'state)) (defun pc-fn (cd state) (io? temporary nil (mv erp val state) (cd) (let ((wrld (w state))) (er-let* ((cmd-wrld (er-decode-cd cd wrld :pc state))) (pprogn (print-ldd (make-command-ldd nil t cmd-wrld (ens state) wrld) (standard-co state) state) (value :invisible)))))) (defmacro pc (cd) ":Doc-Section History print the ~il[command] described by a ~il[command] descriptor~/ ~bv[] Examples: :pc 3 ; print the third command executed :pc :max ; print the most recent command :pc :x ; print the most recent command :pc fn ; print the command that introduced fn ~ev[] ~l[command-descriptor].~/ ~c[Pc] takes one argument, a ~il[command] descriptor, and prints the ~il[command] identified by that descriptor. ~l[command-descriptor]. For example ~bv[] ACL2 !>:pc foo LVd 52 (DEFUN FOO (X) X) ~ev[] ~c[Pc] always prints a space first, followed by three (possibly blank) ~il[characters] (``LVd'' above) explained below (four, in the experimental HONS version, as discussed further below). Then ~c[pc] prints the ~il[command] number, a number uniquely identifying the ~il[command]'s position in the sequence of ~il[command]s since the beginning of the user's session. Finally, the ~il[command] itself is printed. While ~c[pc] always prints a space first, some ~il[history] ~il[command]s, for example ~c[:]~ilc[pcs] and ~c[:]~ilc[pe], use the first column of output to delimit a region of ~il[command]s or to point to a particular event within a ~il[command]. For example, ~c[:pcs 52 54] will print something like ~bv[] /LVd 52 (DEFUN FOO (X) X) LV 53 (DEFUN BAR (X) (CONS X X)) \\ 54 (DEFTHM FOO-BAR (EQUAL (CAR (BAR X)) (FOO X))) : ... 127 (DEFUN LATEST (X) X) ~ev[] Here, the two slash ~il[characters] in the first column are intended to suggest a bracket delimiting ~il[command]s 52 through 54. The last ~il[command] printed by ~ilc[pcs] is always the most recent ~il[command], i.e., the ~il[command] at ~c[:here], and is separated from the rest of the display by an elipsis if some ~il[command]s are omitted. Similarly, the ~c[:]~ilc[pe] ~il[command] will print a particular event within a ~il[command] block and will indicate that event by printing a ``~ilc[>]'' in the first column. The symbol is intended to be an arrow pointing at the event in question. For example, ~c[:]~ilc[pe] ~c[true-listp-app] might print: ~bv[] 1 (INCLUDE-BOOK \"list-book\") \\ > (DEFTHM TRUE-LISTP-APP (EQUAL (TRUE-LISTP (APP A B)) (TRUE-LISTP B))) ~ev[] using the arrow to indicate the event itself. The slash printed to connect the ~il[command], ~ilc[include-book], with the event, ~ilc[defthm], is intended to suggest a tree branch indicating that the event is inferior to (and part of) the ~il[command]. The mysterious ~il[characters] sometimes preceding a ~il[command] have the following interpretations. The first two have to do with the function symbols introduced by the ~il[command] and are blank if no symbols were introduced. At any time we can classify our function symbols into disjoint sets, which we will here name with ~il[characters]. The ``~c[P]'' functions are those in ~c[:]~ilc[program] mode. The ``~c[L]'' functions are those in ~c[:]~ilc[logic] mode whose ~il[guard]s have not been verified. The ``~c[V]'' functions are those in ~c[:]~ilc[logic] mode whose ~il[guard]s have been verified. You may also see the use of (lower-case) ``~c[v]'' to indicate functions introduced by ~ilc[encapsulate]. Note that ~ilc[verify-termination] and ~ilc[verify-guards] cause function symbols to be reclassified. If a ~il[command] introduces function symbols then the first mysterious character indicates the class of the symbols at the time of introduction and the second character indicates the current class of the symbols (if the current class is different from the introductory class). Thus, the display ~bv[] PLd 52 (DEFUN FOO (X) X) ~ev[] tells us that ~il[command] 52 introduced a ~c[:]~ilc[program] function but that some ~il[command] after 52 changed its mode to ~c[:]~ilc[logic] and that the ~il[guard]s of ~c[foo] have not been verified. That is, ~c[foo]'s termination has been verified even though it was not verified as part of the ~il[command] that introduced ~c[foo]. Had a subsequent ~il[command] verified the ~il[guard]s of ~c[foo], the display would contain a ~c[V] where the ~c[L] is. The display ~bv[] P d 52 (DEFUN FOO (X) X) ~ev[] indicates that ~c[foo] was introduced in ~c[:]~ilc[program] mode and still is in that mode. The third character indicates the enabled/disabled status of the ~il[rune]s introduced by the ~il[command]. If the status character is blank then all the ~il[rune]s (if any) introduced are ~il[enable]d. If the status character is ``~c[D]'' then some ~il[rune]s were introduced and they are all ~il[disable]d. If the status character is ``~c[d]'' then at least one, but not all, of the ~il[rune]s introduced is ~il[disable]d. Thus, in the display ~bv[] L d 52 (DEFUN FOO (X) X) ~ev[] we see that some ~il[rune] introduced by ~il[command] 52 is ~il[disable]d. As noted in the documentation for ~il[rune], a ~ilc[defun] ~il[command] introduces many ~il[rune]s, e.g., the axiomatic definition rule, ~c[(:definition fn)], the executable counterpart rule, ~c[(:executable-counterpart fn)], and ~il[type-prescription]s, ~c[(:type-prescription fn)]. The display above does not say which of the ~il[rune]s based on ~c[foo] is ~il[disable]d, but it does tell us one of them is; ~pl[disabledp] for how to obtain the disabled runes for a given function symbol. Finally, for the experimental HONS version only (~pl[hons-and-memoization]), a fourth character is printed, indicating whether functions are memoized. A symbol may be memoized if it is a function symbol that is not constrained (i.e., introduced by ~ilc[defchoose] or in the ~il[signature] of an ~ilc[encapsulate] event). If the command introduces no symbol that may be memoized, then a space is printed. Otherwise, if every memoizable symbol is memoized, an ``~c[M]'' is printed. Otherwise, an ``~c[m]'' is printed.~/" (list 'pc-fn cd 'state)) (defun pcs-fn (cd1 cd2 markp state) ; We print the commands between cd1 and cd2 (relative order of these two cds is ; irrelevant). We always print the most recent command here, possibly elided ; into the cd1-cd2 region. We mark the end points of the region if markp is t. (io? temporary nil (mv erp val state) (cd1 markp cd2) (let ((wrld (w state)) (ens (ens state))) (er-let* ((cmd-wrld1 (er-decode-cd cd1 wrld :ps state)) (cmd-wrld2 (er-decode-cd cd2 wrld :ps state))) (let ((later-wrld (if (>= (access-command-tuple-number (cddar cmd-wrld1)) (access-command-tuple-number (cddar cmd-wrld2))) cmd-wrld1 cmd-wrld2)) (earlier-wrld (if (>= (access-command-tuple-number (cddar cmd-wrld1)) (access-command-tuple-number (cddar cmd-wrld2))) cmd-wrld2 cmd-wrld1))) (pprogn (print-ldds (make-ldds-command-sequence later-wrld (cddar earlier-wrld) ens wrld markp nil) (standard-co state) state) (cond ((= (access-command-tuple-number (cddar later-wrld)) (max-absolute-command-number wrld)) state) ((= (1+ (access-command-tuple-number (cddar later-wrld))) (max-absolute-command-number wrld)) (print-ldd (make-command-ldd nil nil wrld ens wrld) (standard-co state) state)) (t (pprogn (mv-let (col state) (fmt1 "~t0: ...~%" (list (cons #\0 (- (print-ldd-formula-column state) 2))) 0 (standard-co state) state nil) (declare (ignore col)) state) (print-ldd (make-command-ldd nil nil wrld ens wrld) (standard-co state) state)))) (value :invisible))))))) (defmacro pcs (cd1 cd2) ":Doc-Section History print the sequence of ~il[command]s between two ~il[command] descriptors~/ ~bv[] Examples: :pcs 1 5 ; print commands 1 through 5 :pcs 5 1 ; same as above :pcs :x (:x -3) ; print the 3 most recently executed commands :pcs fn assoc-of-fn ; print the commands between the one that introduced ; fn and the one that introduced assoc-of-fn ~ev[]~/ ~c[Pcs] takes two arguments, both of which are ~il[command] descriptors, and prints the ~il[command]s between them with ~ilc[pc]. The order of the two descriptors is irrelevant. ~l[command-descriptor] for a description of ~il[command] descriptors. ~l[pc] for a description of the format in which ~il[command]s are displayed. For a related utility that simply returns a sequence of commands, ~pl[get-command-sequence].~/" (list 'pcs-fn cd1 cd2 t 'state)) (defun get-command-sequence-fn1 (cmd-wrld1 cmd2 ans) ; Keep this in sync with make-ldds-command-sequence. (cond ((equal (cddar cmd-wrld1) cmd2) (cons (access-command-tuple-form (cddar cmd-wrld1)) ans)) (t (get-command-sequence-fn1 (scan-to-command (cdr cmd-wrld1)) cmd2 (cons (access-command-tuple-form (cddar cmd-wrld1)) ans))))) (defun get-command-sequence-fn (cd1 cd2 state) ; We print the commands between cd1 and cd2 (relative order of these two cds is ; irrelevant). We always print the most recent command here, possibly elided ; into the cd1-cd2 region. We mark the end points of the region if markp is t. (let ((wrld (w state)) (ctx 'get-command-sequence)) (er-let* ((cmd-wrld1 (er-decode-cd cd1 wrld ctx state)) (cmd-wrld2 (er-decode-cd cd2 wrld ctx state))) (let ((later-wrld (if (>= (access-command-tuple-number (cddar cmd-wrld1)) (access-command-tuple-number (cddar cmd-wrld2))) cmd-wrld1 cmd-wrld2)) (earlier-wrld (if (>= (access-command-tuple-number (cddar cmd-wrld1)) (access-command-tuple-number (cddar cmd-wrld2))) cmd-wrld2 cmd-wrld1))) (value (get-command-sequence-fn1 later-wrld (cddar earlier-wrld) nil)))))) (defmacro get-command-sequence (cd1 cd2) ":Doc-Section History return list of ~il[command]s that are between two ~il[command] descriptors~/ ~bv[] Examples: (get-command-sequence 4 12) :gcs 4 12 ; same as above (get-command-sequence 4 :x) :gcs 4 :x ; same as above ~ev[] ~l[pcs] for a utility that prints abbreviated information about the ~il[command]s that are between two command descriptors. The utility ~c[get-command-sequence] ~-[] or simply ~c[gcs], so that you can just type ~c[:gcs] at the prompt ~-[] has the same syntax but instead of printing, it simply returns the corresponding list of commands. More precisely, it returns an error triple ~c[(mv erp val state)] (~pl[error-triples]) such that if ~c[erp] is not ~c[nil], then ~c[val] is the desired list of commands.~/~/" (list 'get-command-sequence-fn cd1 cd2 'state)) (defmacro gcs (cd1 cd2) `(get-command-sequence ,cd1 ,cd2)) (defmacro pbt (cd1) ":Doc-Section History print the ~il[command]s back through a ~il[command] descriptor~/ ~bv[] Examples: :pbt :max ; print back through the most recent command :pbt :x ; print back through the most recent command :pbt fn ; print back through the introduction of fn :pbt 5 ; print back through the fifth command executed :pbt (:x -4) ; print back through the most recent five commands ~ev[] ~l[command-descriptor].~/ ~c[Pbt] takes one argument, a ~il[command] descriptor, and prints the ~il[command]s from ~c[:max] (aka ~c[:x]) through the one described. ~l[command-descriptor] for a description of what a ~il[command] descriptor is. ~l[pc] for a description of the format used to display ~il[command]s. ~c[Pbt] will print the ~il[command]s that ~ilc[ubt] will undo.~/" (list 'pcs-fn cd1 :x nil 'state)) (defmacro pcb (cd) ":Doc-Section History print the ~il[command] block described by a ~il[command] descriptor~/ ~bv[] Examples: :pcb :max ; print the most recent command block :pcb :x ; print the most recent command block :pcb fn ; print the command block that introduced fn :pcb 5 ; print the fifth command block ~ev[] ~l[command-descriptor].~/ ~c[Pcb] takes one argument, a ~il[command] descriptor, and prints the ~il[command] block of the ~il[command] described. ~l[command-descriptor] for details of ~il[command] descriptors. ~l[pc] for description of the format in which ~il[command]s are displayed. The ~il[command] block of a ~il[command] consists of the ~il[command] itself and all of the ~il[events] it created. If the ~il[command] created a single event and that event is in fact the ~il[command] (i.e., if the ~il[command] typed was just an event such as a ~ilc[defun] or ~ilc[defthm] rather than a macro that expanded to some event forms), then ~c[pcb] just prints the ~il[command]. ~c[Pcb] sketches ~il[command] and all of the ~il[events] it created, rather than printing them fully. If you wish to see just the ~il[command], in its entirety, use ~ilc[pc]. If you wish to see one of the ~il[events] within the block, in its entirety, use ~ilc[pe]. If you wish to see the ~il[command] sketched and all of the ~il[events] it created, in their entirety, use ~ilc[pcb!].~/" (list 'pcb-fn cd 'state)) (defun print-indented-list-msg (objects indent final-string) ; Indents the indicated number of spaces, then prints the first object, then ; prints a newline; then, recurs. Finally, prints the string final-string. If ; final-string is punctuation as represented by fmt directive ~y, then it will ; be printed just after the last object. (cond ((null objects) "") ((and final-string (null (cdr objects))) (msg (concatenate 'string "~_0~y1" final-string) indent (car objects))) (t (msg "~_0~y1~@2" indent (car objects) (print-indented-list-msg (cdr objects) indent final-string))))) (defun print-indented-list (objects indent last-col channel evisc-tuple state) (cond ((null objects) (mv last-col state)) (t (fmt1 "~@0" (list (cons #\0 (print-indented-list-msg objects indent nil))) 0 channel state evisc-tuple)))) (defun print-book-path (book-path indent channel state) (assert$ book-path (mv-let (col state) (fmt1 "~_0[Included books, outermost to innermost:~|" (list (cons #\0 indent)) 0 channel state nil) (declare (ignore col)) (mv-let (col state) (print-indented-list book-path (1+ indent) 0 channel nil state) (pprogn (if (eql col 0) (spaces indent col channel state) state) (princ$ #\] channel state)))))) (defun pe-fn1 (wrld channel ev-wrld cmd-wrld state) (cond ((equal (access-event-tuple-form (cddar ev-wrld)) (access-command-tuple-form (cddar cmd-wrld))) (print-ldd (make-command-ldd nil t cmd-wrld (ens state) wrld) channel state)) (t (let ((indent (print-ldd-formula-column state)) (ens (ens state))) (pprogn (print-ldd (make-command-ldd nil nil cmd-wrld ens wrld) channel state) (mv-let (col state) (fmt1 "~_0\\~%" (list (cons #\0 indent)) 0 channel state nil) (declare (ignore col)) state) (let ((book-path (global-val 'include-book-path ev-wrld))) (cond (book-path (pprogn (print-book-path (reverse book-path) indent channel state) (fms "~_0\\~%" (list (cons #\0 indent)) channel state nil))) (t state))) (print-ldd (make-event-ldd #\> 1 t (cddar ev-wrld) ens wrld) channel state)))))) (defun pe-fn2 (logical-name wrld channel ev-wrld state) (er-let* ((cmd-wrld (superior-command-world ev-wrld wrld :pe state))) (pprogn (pe-fn1 wrld channel ev-wrld cmd-wrld state) (let ((new-ev-wrld (decode-logical-name logical-name (scan-to-event (cdr ev-wrld))))) (if new-ev-wrld (pe-fn2 logical-name wrld channel new-ev-wrld state) (value :invisible)))))) (defun pe-fn (logical-name state) (io? temporary nil (mv erp val state) (logical-name) (let ((wrld (w state)) (channel (standard-co state))) (cond ((and (symbolp logical-name) (not (eq logical-name :here)) (eql (getprop logical-name 'absolute-event-number nil 'current-acl2-world wrld) 0)) ; This special case avoids printing something like the following, which isn't ; very useful. ; -7479 (ENTER-BOOT-STRAP-MODE :UNIX) ; We make the change here rather than in pe-fn1 because don't want to mess ; around at the level of ldd structures. It's a close call. ; We don't make the corresponding change to pc-fn. With pe, one is asking for ; an event, which in the case of a function is probably a request for a ; definition. We want to serve the intention of that request. With pc, one is ; asking for the full command, so we give it to them. (pprogn (fms "~x0 is built into ACL2, without a defining event.~#1~[ See ~ :DOC ~x0.~/~]~|" (list (cons #\0 logical-name) (cons #\1 (if (assoc-eq logical-name (global-val 'documentation-alist wrld)) 0 1))) channel state nil) (value :invisible))) (t (er-let* ((ev-wrld (er-decode-logical-name logical-name wrld :pe state)) (cmd-wrld (superior-command-world ev-wrld wrld :pe state))) (pprogn (pe-fn1 wrld channel ev-wrld cmd-wrld state) (let ((new-ev-wrld (and (not (eq logical-name :here)) (decode-logical-name logical-name (scan-to-event (cdr ev-wrld)))))) (if (null new-ev-wrld) (value :invisible) (pprogn (fms "Additional events for the logical name ~x0:~%" (list (cons #\0 logical-name)) channel state nil) (pe-fn2 logical-name wrld channel new-ev-wrld state))))))))))) (defmacro pe (logical-name) ":Doc-Section History print the events named by a logical name~/ ~bv[] Example: :pe fn ; sketches the command that introduced fn and ; prints in full the event within it that created fn. ~ev[] ~l[logical-name].~/ ~c[Pe] takes one argument, a logical name, and prints in full the event corresponding to the name. ~c[Pe] also sketches the ~il[command] responsible for that event if the ~il[command] is different from the event itself. ~l[pc] for a description of the format used to display a ~il[command]. To remind you that the event is inferior to the ~il[command], i.e., you can only undo the entire ~il[command], not just the event, the event is indented slightly from the ~il[command] and a slash (meant to suggest a tree branch) connects them. If the given logical name corresponds to more than one event, then ~c[:pe] will print the above information for every such event. Here is an example of such behavior. ~bv[] ACL2 !>:pe nth -4270 (ENCAPSULATE NIL ...) \\ >V (VERIFY-TERMINATION NTH) Additional events for the logical name NTH: PV -4949 (DEFUN NTH (N L) \"Documentation available via :doc\" (DECLARE (XARGS :GUARD (AND (INTEGERP N) (>= N 0) (TRUE-LISTP L)))) (IF (ENDP L) NIL (IF (ZP N) (CAR L) (NTH (- N 1) (CDR L))))) ACL2 !> ~ev[] If you prefer to see only the formula for the given name, for example if it is part of a large ~ilc[mutual-recursion], ~pl[pf].~/" (list 'pe-fn logical-name 'state)) (defmacro pe! (logical-name) ":Doc-Section History deprecated (~pl[pe])~/ Please ~pl[pe]. ~c[:Pe] now has the functionality formerly provided by ~c[:pe!].~/~/" (declare (ignore logical-name)) `(er hard 'pe! "Pe! has been deprecated. Please use :pe, which now has the ~ functionality formerly provided by :pe!; or consider :pcb or :pcb!. ~ See :DOC history.")) (defun command-block-names1 (wrld ans symbol-classes) ; Symbol-Classes is a list of symbol-classes or else is t. We scan down world ; to the next command landmark unioning into ans all the names whose ; introduction-time symbol-class is contained in symbol-classes, where ; symbol-classes t denotes the set of everything (!). Note that symbol-classes ; t is different from symbol-classes (:program :ideal :common-lisp-compliant) ; because some names, e.g., label names, don't have symbol-classes (i.e., have ; access-event-tuple-symbol-class nil). We return the final ans and the wrld ; starting with the next command landmark. Note also that we use the ; symbol-class at introduction, not the current one. (cond ((or (null wrld) (and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value))) (mv ans wrld)) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value)) (cond ((or (eq symbol-classes t) (member-eq (access-event-tuple-symbol-class (cddar wrld)) symbol-classes)) (let ((namex (access-event-tuple-namex (cddar wrld)))) (command-block-names1 (cdr wrld) (cond ((equal namex 0) ans) ((equal namex nil) ans) ((atom namex) ; Might be symbolp or stringp. (add-to-set-equal namex ans)) (t (union-equal namex ans))) symbol-classes))) (t (command-block-names1 (cdr wrld) ans symbol-classes)))) (t (command-block-names1 (cdr wrld) ans symbol-classes)))) (defun command-block-names (wrld symbol-classes) ; Wrld is a world that begins with a command landmark. We collect all the ; names introduced in the symbol-classes listed. Symbol-Classes = t means all ; (including nil). We return the collection of names and the world starting ; with the next command landmark. (command-block-names1 (cdr wrld) nil symbol-classes)) (defun symbol-name-lst (lst) (cond ((null lst) nil) (t (cons (symbol-name (car lst)) (symbol-name-lst (cdr lst)))))) (defun acl2-query-simulate-interaction (msg alist controlledp ans state) (cond ((and (atom ans) (or controlledp (and (not (f-get-global 'window-interfacep state)) ; If a special window is devoted to queries, then there is no way to ; pretend to answer, so we don't. We just go on. Imagine that we ; answered and the window disappeared so quickly you couldn't see the ; answer. (not (eq (standard-co state) *standard-co*))))) (pprogn (fms msg alist (standard-co state) state (ld-evisc-tuple state)) (princ$ ans (standard-co state) state) (newline (standard-co state) state) state)) (t state))) (defun acl2-query1 (id qt alist state) ; This is the function actually responsible for printing the query ; and getting the answer, for the current level in the query tree qt. ; See acl2-query for the context. (let ((dv (cdr-assoc-query-id id (ld-query-control-alist state))) (msg "ACL2 Query (~x0): ~@1 (~*2): ") (alist1 (list (cons #\0 id) (cons #\1 (cons (car qt) alist)) (cons #\2 (list "" "~s*" "~s* or " "~s*, " (symbol-name-lst (evens (cdr qt)))))))) (cond ((null dv) (pprogn (io? query nil state (alist1 msg) (fms msg alist1 *standard-co* state (ld-evisc-tuple state))) (er-let* ((ans (state-global-let* ((infixp nil)) (read-object *standard-oi* state)))) (let ((temp (and (symbolp ans) (assoc-keyword (intern (symbol-name ans) "KEYWORD") (cdr qt))))) (cond (temp (pprogn (acl2-query-simulate-interaction msg alist1 nil ans state) (value (cadr temp)))) (t (acl2-query1 id qt alist state))))))) ((eq dv t) (pprogn (acl2-query-simulate-interaction msg alist1 t (cadr qt) state) (value (caddr qt)))) (t (let ((temp (assoc-keyword (if (consp dv) (car dv) dv) (cdr qt)))) (cond ((null temp) (er soft 'acl2-query "The default response, ~x0, supplied in ~ ld-query-control-alist for the ~x1 query, is not one ~ of the expected responses. The ~x1 query ~ is~%~%~@2~%~%Note the expected responses above. See ~ :DOC ld-query-control-alist." (if (consp dv) (car dv) dv) id (cons msg alist1))) (t (pprogn (acl2-query-simulate-interaction msg alist1 t dv state) (value (cadr temp)))))))))) (defun acl2-query (id qt alist state) ; A query-tree qt is either an atom or a cons of the form ; (str :k1 qt1 ... :kn qtn) ; where str is a string suitable for printing with ~@, each :ki is a ; keyword, and each qti is a query tree. If qt is an atom, it is ; returned. Otherwise, str is printed and the user is prompted for ; one of the keys. When ki is typed, we recur on the corresponding ; qti. Note that the user need not type a keyword, just a symbol ; whose symbol-name is that of one of the keywords. ; Thus, '("Do you want to redefine ~x0?" :y t :n nil) will print ; the question and require a simple y or n answer, returning t or nil ; as appropriate. ; Warning: We don't always actually read an answer! We sometimes ; default. Our behavior depends on the LD specials standard-co, ; standard-oi, and ld-query-control-alist, as follows. ; Let x be (cdr (assoc-eq id (ld-query-control-alist state))). X must ; be either nil, a keyword, or a singleton list containing a keyword. ; If it is a keyword, then it must be one of the keys in (cdr qt) or ; else we cause an error. If x is a keyword or a one-element list ; containing a keyword, we act as though we read that keyword as the ; answer to our query. If x is nil, we read *standard-oi* for an ; answer. ; Now what about printing? Where does the query actually appear? If ; we get the answer from the control alist, then we print both the ; query and the answer to standard-co, making it simulate an ; interaction -- except, if the control alist gave us a singleton ; list, then we do not do any printing. If we get the answer from ; *standard-oi* then we print the query to *standard-co*. In ; addition, if we get the answer from *standard-oi* but *standard-co* ; is not standard-co, we simulate the interaction on standard-co. (cond ((atom qt) (value qt)) ((not (and (or (stringp (car qt)) (and (consp (car qt)) (stringp (caar qt)))) (consp (cdr qt)) (keyword-value-listp (cdr qt)))) (er soft 'acl2-query "The object ~x0 is not a query tree! See the comment in ~ acl2-query." qt)) (t (er-let* ((qt1 (acl2-query1 id qt alist state))) (acl2-query id qt1 alist state))))) (defun collect-names-in-defun-modes (names defun-modes wrld) ; Collect the elements of names (all of which are fn symbols) whose current ; defun-mode is in the given set. (cond ((null names) nil) ((member-eq (fdefun-mode (car names) wrld) defun-modes) (cons (car names) (collect-names-in-defun-modes (cdr names) defun-modes wrld))) (t (collect-names-in-defun-modes (cdr names) defun-modes wrld)))) (defun ubt-ubu-query (kwd wrld1 wrld0 seen kept-commands wrld state banger) ; Wrld0 is a predecessor world of wrld1 which starts with a command landmark. ; We scan down wrld1 until we get to wrld0. For each command encountered we ; ask the user if he wants us to preserve the :program names introduced. ; If so, we add the command to kept-commands. We only ask about the latest ; definition of any name (the accumulator seen contains all the names we've ; asked about). We return the list of commands to be re-executed (in ; chronological -- not reverse chronological -- order). Of course, this is an ; error/value/state function. ; Kwd is :ubt, :ubu, or :ubt-prehistory. ; Note: The kept-commands, when non-nil, always starts with a defun-mode ; keyword command, i.e., :logic or :program. This is the ; default-defun-mode in which the next command on the list, the first "real ; command," was executed. When we grow the kept-command list, we remove ; redundant mode changes. So for example, if kept-commands were ; '(:program cmd2 ...) and we then wished to add cmd1, then if the mode in ; which cmd1 was executed was :program the result is '(:program cmd1 ; cmd2 ...) while if cmd1's mode is :logic the result is '(:logic cmd1 ; :program cmd2 ...). Note that the mode may indeed be :logic, even ; though cmd1 introduces a :program function, because the mode of the ; introduced function may not be the default-defun-mode. The commands are kept ; because the functions they introduce are :program, not because they were ; executed in :program mode. But we must make sure the default mode is ; the same as it was when the command was last executed, just in case the mode ; of the functions is the default one. (cond ((or (null wrld1) (equal wrld1 wrld0)) (value kept-commands)) (t (mv-let (names wrld2) (command-block-names wrld1 '(:program)) ; Names is the list of all names in the current command block whose ; introduction-time symbol-class was :program. (cond ((and names (set-difference-eq names seen)) (er-let* ((ans (if banger (value banger) (let ((logic-names (collect-names-in-defun-modes names '(:logic) wrld))) (acl2-query kwd '("The command ~X01 introduced the :program ~ name~#2~[~/s~] ~&2.~#5~[~/ ~&3 ~#4~[has~/have~] ~ since been made logical.~] Do you wish to ~ re-execute this command after the ~xi?" :y t :n nil :y! :all :n! :none :q :q :? ("We are undoing some commands. We have ~ encountered a command, printed above, that ~ introduced a :program function symbol. It is ~ unusual to use ~xi while defining :program ~ functions, since redefinition is permitted. ~ Therefore, we suspect that you are mixing ~ :program and :logic definitions, as when one is ~ developing utilities for the prover. When ~ undoing through such a mixed session, it is ~ often intended that the :logic functions be ~ undone while the :program ones not be, since the ~ latter ones are just utilities. While we cannot ~ selectively undo commands, we do offer to redo ~ selected commands when we have finished undoing. ~ The situation is complicated by the fact that ~ :programs can become :logic functions after the ~ introductory event and that the same name can be ~ redefined several times. Unless noted in the ~ question above, the functions discussed are all ~ still :program. The commands we offer for ~ re-execution are those responsible for ~ introducing the most recent definitions of ~ :program names, whether the names are still ~ :program or not. That is, if in the region ~ undone there is more than one :program ~ definition of a name, we will offer to redo the ~ chronologically latest one.~%~%If you answer Y, ~ the command printed above will be re-executed. ~ If you answer N, it will not be. The answer Y! ~ means the same thing as answering Y to this and ~ all subsequent queries in this ~xi The answer ~ N! is analogous. Finally, Q means to abort the ~ ~xi without undoing anything." :y t :n nil :y! :all :n! :none :q :q)) (list (cons #\i kwd) (cons #\0 (access-command-tuple-form (cddar wrld1))) (cons #\1 (term-evisc-tuple t state)) (cons #\2 names) (cons #\3 logic-names) (cons #\4 (if (cdr logic-names) 1 0)) (cons #\5 (if (null logic-names) 0 1))) state))))) (cond ((eq ans :q) (mv t nil state)) (t (ubt-ubu-query kwd wrld2 wrld0 (union-eq names seen) (if (or (eq ans t) (eq ans :all)) (cons (access-command-tuple-defun-mode (cddar wrld1)) (cons (access-command-tuple-form (cddar wrld1)) (cond ((eq (access-command-tuple-defun-mode (cddar wrld1)) (car kept-commands)) (cdr kept-commands)) (t kept-commands)))) kept-commands) wrld state (or banger (if (eq ans :all) :all nil) (if (eq ans :none) :none nil))))))) (t (ubt-ubu-query kwd wrld2 wrld0 seen kept-commands wrld state banger))))))) ; We can't define ubt-ubu-fn until we define LD, because it uses LD to replay ; selected commands. So we proceed as though we had defined ubt-ubu-fn. (defmacro ubt (cd) ":Doc-Section History undo the ~il[command]s back through a ~il[command] descriptor~/ ~bv[] Examples: :ubt :max ; undo back through the most recent command ; (which just means undo the most recent command) :ubt :x ; same as :ubt :max :u ; same as :ubt :max with no questions asked :ubt fn ; undo back through the introduction of fn ; (including all the other events in fn's block) :ubt 5 ; undo back through the fifth command executed :ubt (:max -4) ; undo back through the most recent five commands :ubt (:x -4) ; undo back through the most recent five commands ~ev[] ~l[command-descriptor].~/ ~c[Ubt] takes one argument, a ~il[command] descriptor, and undoes the ~il[command]s from ~c[:]~ilc[max] (aka ~c[:x]) through the one described. ~l[command-descriptor]. ~ilc[Pbt] will print the ~il[command]s that ~c[ubt] will undo. ~c[:]~ilc[Oops] will undo the undo. ~l[oops]. ~c[Ubt] can cause errors or queries. To avoid these, ~pl[ubt!]. It is important to remember that a ~il[command] may create several ~il[events]. That is, the ~il[command] that introduces ~c[fn1] may also introduce ~c[fn2]. Undoing the ~il[command] that created either of these will undo them both. The ~il[events] created by a ~il[command] constitute the ~il[command]'s ``block'' and we can only undo entire blocks. Use ~ilc[pcb] to print the ~il[command] block of a ~il[command] if you wish to see what will be lost by undoing the ~il[command]. ~c[Ubt] will not undo into ``prehistory''. ~c[:Ubt 1] will undo all of your ~il[command]s. But ~c[:ubt -5] will cause an error, warning you that ~c[:ubt] cannot undo system initialization. ~l[u] for how to undo just the latest command, and ~pl[ubu] and ~pl[ubu!] for how to undo back up to, but not including, the current command.~/" (list 'ubt-ubu-fn :ubt cd 'state)) (defmacro ubt! (cd) ":Doc-Section History undo ~il[command]s, without a query or an error~/ ~bv[] Example: :ubt! :x-4 ~ev[]~/ The keyword ~il[command] ~c[:ubt!] is the same as ~c[:]~ilc[ubt], but with related queries suppressed appropriately, and with a guarantee that it is ``error-free.'' More precisely, the value returned by ~c[:ubt!] will always be of the form ~c[(mv nil val state)]. ~c[:]~ilc[Oops] will undo the last ~c[:ubt!]. ~l[ubt], ~pl[ubu], and ~pl[u].~/" (list 'ubt!-ubu!-fn :ubt cd 'state)) (defmacro ubu (cd) ":Doc-Section History undo the ~il[command]s back up to (not including) a ~il[command] descriptor~/ ~bv[] Examples: :ubu :x-3 ; undo the last three commands (same as :ubt :x-2) :ubu (:x -3) ; same as above :ubu fn ; undo back up to, but not including the introduction of fn ; (so fn will continue to be defined) :ubu 5 ; undo back up to, but not including, the fifth command ; executed (leaving the first five commands in place) ~ev[] ~l[command-descriptor].~/ ~c[Ubu] takes one argument, a ~il[command] descriptor, and undoes the ~il[command]s from ~c[:]~ilc[max] (aka ~c[:x]) up to, but not including, the indicated command. ~l[command-descriptor]. ~c[Ubu] can cause errors or queries. To avoid these, ~pl[ubu!]. Also ~pl[ubt], which is similar but also undoes the indicated command. As for ~c[:]~ilc[ubt], ~c[:]~ilc[oops] will undo the undo (~pl[oops]) and ~ilc[ubu] will not undo into ``prehistory''. ~l[u] for how to undo just the latest command, and ~pl[ubt] and ~pl[ubt!] for how to undo back through (that is, including) the current command.~/" (list 'ubt-ubu-fn :ubu cd 'state)) (defmacro ubu! (cd) ":Doc-Section History undo ~il[command]s, without a query or an error~/ ~bv[] Example: :ubu! :x-4 ~ev[]~/ The keyword ~il[command] ~c[:ubu!] is the same as ~c[:]~ilc[ubu], but with related queries suppressed appropriately, and with a guarantee that it is ``error-free.'' More precisely, the error triple (~pl[error-triples]) returned by ~c[:ubu!] will always be of the form ~c[(mv nil val state)]. ~c[:]~ilc[Oops] will undo the last ~c[:ubu!]. Also ~pl[ubu], ~pl[ubt], and ~pl[u].~/" (list 'ubt!-ubu!-fn :ubu cd 'state)) (defmacro u nil ":Doc-Section History undo last ~il[command], without a query~/ ~bv[] Example: :u ~ev[]~/ The keyword ~il[command] ~c[:u] is the same as ~c[:]~ilc[ubt] ~c[:]~ilc[max], but with related queries suppressed appropriately. ~c[:]~ilc[Oops] will undo the last ~c[:u]. ~l[ubt], ~pl[ubu], ~pl[ubt!], and ~pl[ubu!].~/" '(ubt! :x)) ; We now develop the most trivial event we have: deflabel. It ; illustrates the basic structure of our event code and we need it for ; all other events because any event with a documentation string uses ; the processing defined here. (Actually defdoc is a bit simpler, and ; we deal with it just after deflabel.) (defun chk-virgin (name new-type wrld) ; Although this function axiomatically always returns the ; value t, it sometimes causes an error. #+acl2-loop-only (declare (ignore name new-type wrld)) #-acl2-loop-only (chk-virgin2 name new-type wrld) t) (deflabel name :doc ":Doc-Section Miscellaneous syntactic rules on logical names~/ ~bv[] Examples Counter-Examples PRIMEP 91 (not a symbolp) F-AC-23 :CHK-LIST (in KEYWORD package) 1AX *PACKAGE* (in the Lisp Package) |Prime Number| 1E3 (not a symbolp) ~ev[]~/ Many different ACL2 entities have names, e.g., functions, macros, variables, constants, packages, theorems, ~il[theories], etc. Package names are strings. All other names are symbols and may not be in the ~c[\"KEYWORD\"] package. Moreover, names of functions, macros, constrained functions, and constants, other than those that are predefined, must not be in the ``main Lisp package'' (which is (~c[\"LISP\"] or ~c[\"COMMON-LISP\"], according to whether we are following CLTL1 or CLTL2). An analogous restriction on variables is given below. ~c[T], ~c[nil], and all names above except those that begin with ampersand (&) are names of variables or constants. ~c[T], ~c[nil], and those names beginning and ending with star (*) are constants. All other such names are variables. Note that names that start with ampersand, such as ~c[&rest], may be lambda list keywords and are reserved for such use whether or not they are in the CLTL constant ~c[lambda-list-keywords]. (See pg 82 of CLTL2.) That is, these may not be used as variables. Unlike constants, variables may be in the main Lisp package as long as they are in the original list of imports from that package to ACL2, the list ~c[*common-lisp-symbols-from-main-lisp-package*], and do not belong to a list of symbols that are potentially ``global.'' This latter list is the value of ~c[*common-lisp-specials-and-constants*]. Our restrictions pertaining to the main Lisp package take into account that some symbols, e.g., ~c[lambda-list-keywords] and ~c[boole-c2], are ``special.'' Permitting them to be bound could have harmful effects. In addition, the Common Lisp language does not allow certain manipulations with many symbols of that package. So, we stay away from them, except for allowing certain variables as indicated above.") (defun chk-boot-strap-redefineable-namep (name ctx wrld state) (cond ((global-val 'boot-strap-pass-2 wrld) (value nil)) ((not (member-eq name (global-val 'chk-new-name-lst wrld))) (er soft ctx "The name ~x0 is already in use and is not among those ~ expected by chk-boot-strap-redefineable-namep to be redundantly defined ~ during initialization. If you wish it to be, add ~x0 to ~ the global-val setting of 'chk-new-name-lst in ~ primordial-world-globals." name)) ((not (chk-virgin name t wrld)) (er soft ctx "Not a virgin name: ~x0." name)) (t (value nil)))) (defun maybe-coerce-overwrite-to-erase (old-type new-type mode) (cond ((and (eq old-type 'function) (eq new-type 'function)) mode) (t :erase))) (defun redefinition-renewal-mode (name old-type new-type reclassifyingp ctx wrld state) ; We use 'ld-redefinition-action to determine whether the redefinition of name, ; currently of old-type in wrld, is to be :erase, :overwrite or ; :reclassifying-overwrite. New-type is the new type name will have and ; reclassifyingp is a non-nil, non-cons value only if this is a :program ; function to identical-defp :logic function redefinition. If this ; redefinition is not permitted, we cause an error, in which case if ; reclassifyingp is a cons then it is an explanatory message to be printed in ; the error message, in the context "Note that ". ; The only time we permit a redefinition when ld-redefinition-action prohibits ; it is when we return :reclassifying-overwrite, except for the case of ; updating non-executable :program mode ("proxy") functions; see :DOC ; defproxy. In the latter case we have some concern about redefining inlined ; functions, so we proclaim them notinline; see install-defs-for-add-trip. ; This function interacts with the user if necessary. See :DOC ; ld-redefinition-action. (let ((act (f-get-global 'ld-redefinition-action state)) (proxy-upgrade-p (and (eq old-type 'function) (consp new-type) ; New-type is (function stobjs-in . stobjs-out); see chk-signature. (eq (car new-type) 'function) (eq (getprop name 'non-executablep nil 'current-acl2-world wrld) :program) ; A non-executable :program-mode function has no logical content, so it is ; logically safe to redefine it. We check however that the signature hasn't ; changed, for the practical reason that we don't want to break existing ; calls. (equal (stobjs-in name wrld) (cadr new-type)) (equal (stobjs-out name wrld) (cddr new-type)))) (attachment-alist (attachment-alist name wrld))) (cond ((and reclassifyingp (not (consp reclassifyingp))) (cond ((member-eq name (f-get-global 'program-fns-with-raw-code state)) (er soft ctx "The function ~x0 must remain in :PROGRAM mode, because it ~ has been marked as a function that has special raw Lisp ~ code." name)) (t (value :reclassifying-overwrite)))) ((and attachment-alist (not (eq (car attachment-alist) :ATTACHMENT-DISALLOWED)) ; During the boot-strap, we may replace non-executable :program mode ; definitions (from defproxy) without removing attachments, so that system ; functions implemented using attachments will not be disrupted. (not (global-val 'boot-strap-flg wrld))) (er soft ctx "The name ~x0 is in use as a ~@1, and it has an attachment. Before ~ redefining it you must remove its attachment, for example by ~ executing the form ~x2. We hope that this is not a significant ~ inconvenience; it seemed potentially too complex to execute such a ~ defattach form safely on your behalf." name (logical-name-type-string old-type) (cond ((programp name wrld) `(defattach (,name nil) :skip-checks t)) (t `(defattach ,name nil))))) ((and (null act) (not proxy-upgrade-p)) ; We cause an error, with rather extensive code below designed to print a ; helpful error message. (mv-let (erp val state) (er soft ctx "The name ~x0 is in use as a ~@1.~#2~[ ~/ (This name is used in ~ the implementation of single-threaded objects.) ~/ Note that ~ ~@3~|~]The redefinition feature is currently off. See :DOC ~ ld-redefinition-action.~@4" name (logical-name-type-string old-type) (cond ((eq new-type 'stobj-live-var) 1) ((consp reclassifyingp) 2) (t 0)) reclassifyingp (cond ((eq (getprop name 'non-executablep nil 'current-acl2-world wrld) :program) (msg " Note that you are attempting to upgrade a proxy, ~ which is only legal using an encapsulate signature ~ that matches the original signature of the function; ~ see :DOC defproxy.")) (t ""))) (declare (ignore erp val)) (er-let* ((ev-wrld (er-decode-logical-name name wrld ctx state))) (pprogn (let ((book-path-rev (reverse (global-val 'include-book-path ev-wrld))) (current-path-rev (reverse (global-val 'include-book-path wrld)))) (io? error nil state (name book-path-rev current-path-rev wrld) (pprogn (cond ((and (null book-path-rev) (acl2-system-namep name wrld)) (fms "Note: ~x0 has already been defined as a system ~ name; that is, it is built into ACL2.~|~%" (list (cons #\0 name)) (standard-co state) state nil)) ((null book-path-rev) (fms "Note: ~x0 was previously defined at the top ~ level~#1~[~/ of the book being certified~].~|~%" (list (cons #\0 name) (cons #\1 (if (f-get-global 'certify-book-info state) 1 0))) (standard-co state) state nil)) (t (pprogn (fms "Note: ~x0 was previously defined in the last ~ of the following books.~|~%" (list (cons #\0 name)) (standard-co state) state nil) (print-book-path book-path-rev 3 (standard-co state) state) (newline (standard-co state) state)))) (cond ((null current-path-rev) state) (t (pprogn (fms "Note: the current attempt to define ~x0 is ~ being made in the last of the following ~ books.~|~%" (list (cons #\0 name)) (standard-co state) state nil) (print-book-path current-path-rev 3 (standard-co state) state) (newline (standard-co state) state))))))) (silent-error state))))) ((and (hons-enabledp state) ; presumably an optimization (cdr (assoc-eq name (table-alist 'memoize-table wrld)))) (er soft ctx "The name ~x0 is in use as a ~@1, and it is currently memoized. ~ You must execute ~x2 before attempting to redefine it." name (logical-name-type-string old-type) (list 'unmemoize (kwote name)))) ((eq new-type 'package) ; Some symbols seen by this fn have new-type package, namely the base ; symbol of the rune naming the rules added by defpkg, q.v. Old-type ; can't be 'package. If this error message is eliminated and ; redefinition is ever permitted, then revisit the call of ; chk-just-new-name in chk-acceptable-defpkg and arrange for it to use ; the resulting world. (er soft ctx "When a package is introduced, a rule is added describing the ~ result produced by (symbol-package-name (intern x pkg)). That ~ rule has a name, i.e., a rune, based on some symbol which must ~ be new. In the case of the current package definition the base ~ symbol for the rune in question is ~x0. The symbol is not new. ~ Furthermore, the redefinition facility makes no provision for ~ packages. Please rename the package or :ubt ~x0. Sorry." name)) ((null (getprop name 'absolute-event-number nil 'current-acl2-world wrld)) ; One might think that (a) this function is only called on old names and (b) ; every old name has an absolute event number. Therefore, why do we ask the ; question above? Because we could have a name introduced by the signature in ; encapsulate that is intended to be local, but was not embedded in a local ; form. (er soft ctx "The name ~x0 appears to have been introduced in the signature list ~ of an encapsulate, yet is being defined non-locally." name)) ; We do not permit any supporter of a single-threaded object implementation to ; be redefined, except by redefining the single-threaded object itself. The ; main reason is that even though the functions like the recognizers appear as ; ordinary predicates, the types are really built in across the whole ; implementation. So it's all or nothing. Besides, I don't really want to ; think about the weird combinations of changing a defstobj supporter to an ; unrelated function, even if the user thinks he knows what he is doing. ((and (defstobj-supporterp name wrld) (not (and (eq new-type 'stobj) (eq old-type 'stobj)))) ; I sweated over the logic above. How do we get here? Name is a defstobj ; supporter. Under what conditions do we permit a defstobj supporter to be ; redefined? Only by redefining the object name itself -- not by redefining ; individual functions. So we want to avoid causing an error if the new and ; old types are both 'stobj (i.e., name is the name of the single-threaded ; object both in the old and the new worlds). ; WARNING: If this function does not cause an error, we proceed, in ; chk-redefineable-namep, to renew name. In the case of stobj names, that ; function renews all the supporting names as well. Thus, it is important to ; maintain the invariant: if this function does not cause an error and name is ; a defstobj supporter, then name is the stobj name. (er soft ctx "The name ~x0 is in use supporting the implementation of ~ the single-threaded object ~x1. We do not permit such ~ names to be redefined except by redefining ~x1 itself with ~ a new DEFSTOBJ." name (defstobj-supporterp name wrld))) ; If we get here, we know that either name is not currently a defstobj ; supporter of any kind or else that it is the old defstobj name and is being ; redefined as a defstobj. (t (let ((sysdefp (acl2-system-namep name wrld))) (cond ((and sysdefp (not (ttag (w state))) (not (and proxy-upgrade-p (global-val 'boot-strap-flg wrld)))) (er soft ctx "Redefinition of system names is not permitted unless there ~ is an active trust tag (ttag). See :DOC defttag.")) (proxy-upgrade-p ; We erase all vestiges of the old function. It may well be safe to return ; :overwrite instead. But at one time we tried that while also leaving the ; 'attachment property unchanged by renew-name/overwrite (rather than making it ; unbound), and we then got an error from the following sequence of events, ; "HARD ACL2 ERROR in LOGICAL-NAME-TYPE: FOO is evidently a logical name but of ; undetermined type." ; (defproxy foo (*) => *) ; (defttag t) ; (defun g (x) x) ; (defattach (foo g) :skip-checks t) ; (defattach (foo nil) :skip-checks t) ; (defstub foo (x) t) ; When we promote boot-strap functions from non-executable :program mode ; ("proxy") functions to encapsulated functions, we thus lose the 'attachment ; property. Outside the boot-strap, where we disallow all redefinition when ; there is an attachment, this is not a restriction. But in the boot-strap, we ; will lose the 'attachment property even though the appropriate Lisp global ; (the attachment-symbol) remains set. This doesn't present a problem, ; however; system functions are special, in that they can very temporarily have ; attachments without an 'attachment property, until the redefinition in ; progress (by an encapsulate) is complete. (cond ((eq (car attachment-alist) :ATTACHMENT-DISALLOWED) (er soft ctx "Implementation error: It is surprising to see ~ attachments disallowed for a non-executable :program ~ mode function (a proxy). See ~ redefinition-renewal-mode.")) (t (value :erase)))) ((eq (car act) :doit!) (value (maybe-coerce-overwrite-to-erase old-type new-type (cdr act)))) ((or (eq (car act) :query) (and sysdefp (or (eq (car act) :warn) (eq (car act) :doit)))) (er-let* ((ans (acl2-query :redef '("~#0~[~x1 is an ACL2 system~/The name ~x1 is in use as ~ a~] ~@2.~#3~[~/ Its current defun-mode is ~@4.~] Do you ~ ~#0~[really ~/~]want to redefine it?~#6~[~/ Note: if ~ you redefine it we will first erase its supporters, ~ ~&7.~]" :n nil :y t :e erase :o overwrite :? ("N means ``no'' and answering that way will abort the ~ attempted redefinition. All other responses allow ~ the redefinition and may render ACL2 unsafe and/or ~ unsound. Y in the current context is the same as ~ ~#5~[E~/O~]. E means ``erase the property list of ~ ~x1 before redefining it.'' O means ``Overwrite ~ existing properties of ~x1 while redefining it'' but ~ is different from erasure only when a function is ~ being redefined as another function. Neither ~ alternative is guaranteed to produce a sensible ACL2 ~ state. If you are unsure of what all this means, ~ abort with N and see :DOC ld-redefinition-action for ~ details." :n nil :y t :e erase :o overwrite)) (list (cons #\0 (if sysdefp 0 1)) (cons #\1 name) (cons #\2 (logical-name-type-string old-type)) (cons #\3 (if (eq old-type 'function) 1 0)) (cons #\4 (if (eq old-type 'function) (defun-mode-string (fdefun-mode name wrld)) nil)) (cons #\5 (if (eq (cdr act) :erase) 0 1)) (cons #\6 (if (defstobj-supporterp name wrld) 1 0)) (cons #\7 (getprop (defstobj-supporterp name wrld) 'stobj nil 'current-acl2-world wrld))) state))) (cond ((null ans) (mv t nil state)) ((eq ans t) (value (maybe-coerce-overwrite-to-erase old-type new-type (cdr act)))) ((eq ans 'erase) (value :erase)) (t (value (maybe-coerce-overwrite-to-erase old-type new-type :overwrite)))))) (t ; If name is a system name, then the car of 'ld-redefinition-action must be ; :warn! If name is not a system name, the car of 'ld-redefinition-action may ; be :warn!, :doit, or :warn. In all cases, we are to proceed with the ; redefinition without any interaction here. (value (maybe-coerce-overwrite-to-erase old-type new-type (cdr act)))))))))) (defun redefined-names1 (wrld ans) (cond ((null wrld) ans) ((eq (cadar wrld) 'redefined) (cond ((eq (car (cddar wrld)) :reclassifying-overwrite) (redefined-names1 (cdr wrld) ans)) (t (redefined-names1 (cdr wrld) (add-to-set-eq (caar wrld) ans))))) (t (redefined-names1 (cdr wrld) ans)))) (defun redefined-names (state) ":Doc-Section Miscellaneous to collect the names that have been redefined~/ ~bv[] Example and General Forms: (redefined-names state) ~ev[]~/ This function collects names that have been redefined in the current ACL2 ~il[state]. ~c[:]~ilc[Program] mode functions that were reclassified to ~c[:]~ilc[logic] functions are not collected, since such reclassification cannot imperil soundness because it is allowed only when the new and old definitions are identical. Thus, if ~c[(redefined-names state)] returns ~c[nil] then no unsafe definitions have been made, regardless of ~ilc[ld-redefinition-action]. ~l[ld-redefinition-action]. WARNING: This function does not report names that are not explicitly redefined in the current logical ~il[world]. Consider for example: ~bv[] (encapsulate () (defun foo () t) (local (defun foo () nil)) (defthm foo-prop (equal (foo) nil) :rule-classes nil)) ~ev[] If you attempt to call ~ilc[certify-book] in the resulting world, ACL2 will appropriately refuse to do the certification: ~bv[] ACL2 Error in (CERTIFY-BOOK \"foo\" ...): At least one command in the current ACL2 world was executed while the value of state global variable 'LD-REDEFINITION-ACTION was not nil: (DEFUN FOO NIL T) Certification is therefore not allowed in this world. You can use :ubt to undo back through this command; see :DOC ubt. ~ev[] However, since the local redefinition of ~c[foo] is gone in the certification world, ~c[(redefined-names state)] will return ~c[nil]. (Technical aside, to be ignored except for those interested in implementation details: ACL2 inhibits ~c[certify-book] in this example because the definition of ~c[foo] is the value of ~c[(global-val 'redef-seen (w state))].)" (redefined-names1 (w state) nil)) (defun chk-redefineable-namep (name new-type reclassifyingp ctx wrld state) ; Name is a non-new name in wrld. We are about to redefine it and make its ; logical-name-type be new-type. If reclassifyingp is non-nil and not a consp ; message (see redundant-or-reclassifying-defunp) then we know that in fact ; this new definition is just a conversion of the existing definition. ; Redefinition is permitted if the value of 'ld-redefinition-action is not nil, ; or if we are defining a function to replace a non-executable :program mode ; function (such as is introduced by defproxy). In all these non-erroneous ; cases, we renew name appropriately and return the resulting world. ; The LD special 'ld-redefinition-action determines how we react to ; redefinition attempts. See :DOC ld-redefinition-action. ; It must be understood that if 'ld-redefinition-action is non-nil then no ; logical sense is maintained, all bets are off, the system is unsound and ; liable to cause all manner of hard lisp errors, etc. (let ((old-type (logical-name-type name wrld nil))) (cond ((and (global-val 'boot-strap-flg wrld) (not (global-val 'boot-strap-pass-2 wrld)) (or (not reclassifyingp) (consp reclassifyingp))) ; If we are in the first pass of booting and name is one of those we know is ; used before it is defined, we act as though it were actually new. (er-progn (chk-boot-strap-redefineable-namep name ctx wrld state) (value wrld))) (t ; In obtaining the renewal mode, :erase or :overwrite, we might cause an error ; that aborts because name is not to be redefined. (er-let* ((renewal-mode (redefinition-renewal-mode name old-type new-type reclassifyingp ctx wrld state))) (cond ((defstobj-supporterp name wrld) ; Because of the checks in redefinition-renewal-mode, we know the ; defstobj-supporterp above returns name itself. But to be rugged I ; will code it this way. If name is a defstobj supporter of any kind, ; we renew all the supporters! (value (renew-names (cons name (getprop (defstobj-supporterp name wrld) 'stobj nil 'current-acl2-world wrld)) renewal-mode wrld))) (t (value (renew-name name renewal-mode wrld))))))))) (defun chk-just-new-name (name new-type reclassifyingp ctx w state) ; Assuming that name has survived chk-all-but-new-name, we check that it is in ; fact new. If it is, we return the world, w. If it is not new, then what we ; do depends on various state variables such as whether we are in boot-strap ; and whether redefinition is allowed. But unless we cause an error we will ; always return the world extending w in which the redefinition is to occur. ; Name is being considered for introduction with logical-name-type new-type. ; Reclassifyingp, when not nil and not a consp, means that this redefinition is ; known to be identical to the existing definition except that it has been ; given the new defun-mode :logic. This will allow us to permit the ; redefinition of system functions. See the comment in ; redundant-or-reclassifying-defunp for more about reclassifyingp. ; Observe that it is difficult for the caller to tell whether redefinition is ; occurring. In fact, inspection of the returned world will reveal the answer: ; sweep down the world to the next event-landmark and see whether any ; 'redefined property is stored. All names with such a property are being ; redefined by this event (possibly soundly by reclassifying :program names). ; This sweep is actually done by collect-redefined on behalf of stop-event ; which prints a suitable warning message. (cond ((new-namep name w) ; If name has no properties in w, then we next check that it is not ; defined in raw Common Lisp. (let ((actual-new-type (cond ((and (consp new-type) (eq (car new-type) 'function)) 'function) (t new-type)))) (cond ((not (chk-virgin name actual-new-type w)) (er soft ctx "Not a virgin name for type ~x0: ~x1." new-type name)) (t (value w))))) ((and (global-val 'boot-strap-flg w) (not (global-val 'boot-strap-pass-2 w)) (or (not reclassifyingp) (consp reclassifyingp))) ; If we are in the first pass of booting and name is one of those we know is ; used before it is defined, we act as though it were actually new. (er-progn (chk-boot-strap-redefineable-namep name ctx w state) (value w))) (t (chk-redefineable-namep name new-type reclassifyingp ctx w state)))) (defun no-new-namesp (lst wrld) ; Lst is a true list of symbols. We return t if every name in it ; is old. (cond ((null lst) t) ((new-namep (car lst) wrld) nil) (t (no-new-namesp (cdr lst) wrld)))) (defun chk-just-new-names (names new-type reclassifyingp ctx w state) ; Assuming that names has survived chk-all-but-new-names, we check that they ; are in fact all new. We either cause an error or return the world, we are to ; use in the coming definition. Observe that it is difficult for the caller to ; tell whether redefinition is occuring. In fact, inspection of world will ; reveal the answer: sweep down world to the next event-landmark and see ; whether any 'redefined property is stored. All names with such a property ; are being redefined by this event. This sweep is actually done by ; collect-redefined on behalf of stop-event which prints a suitable warning ; message. ; Reclassifyingp is as explained in redundant-or-reclassifying-defunp. In ; particular, it can be a message (a cons pair suitable for printing with ~@). (cond ((null names) (value w)) (t (er-let* ((wrld1 (chk-just-new-name (car names) new-type reclassifyingp ctx w state))) (chk-just-new-names (cdr names) new-type reclassifyingp ctx wrld1 state))))) ; We now develop the code for checking that a documentation string ; is well formed. (defconst *return-character* (code-char 13)) (defun read-symbol-from-string1 (str i len ans) (cond ((< i len) (let ((c (char str i))) (cond ((or (eql c #\Space) (eql c #\Newline) ; The following modification is useful for avoiding CR characters in Windows ; systems that use CR/LF for line breaks. (eql c *return-character*)) (mv (reverse ans) i)) (t (read-symbol-from-string1 str (1+ i) len (cons (char-upcase c) ans)))))) (t (mv (reverse ans) i)))) (defun read-symbol-from-string2 (str i len ans) (cond ((< i len) (let ((c (char str i))) (cond ((eql c #\|) (mv (reverse ans) i)) (t (read-symbol-from-string2 str (1+ i) len (cons c ans)))))) (t (mv (reverse ans) i)))) (defun read-symbol-from-string (str i pkg-witness) ; Reads one symbol from str, starting at index i. The symbol will ; either be in the pkg of pkg-witness (which is a symbol) or else ; will be in "KEYWORD" or in "ACL2" if its print representation so ; specifies. Leading whitespace is ignored. Two values are returned: ; the symbol read and the index of the first whitespace character ; after the symbol read. If there is no non-whitespace after i, ; two nils are returned. ; Warning: This is a cheap imitation of the CLTL READ. We put the symbol in ; the keyword package if the first non-whitespace char is a colon. Then we ; read to a certain delimiter, either vertical bar or space/newline, depending ; on whether the next char is a vertical bar. Then we make a symbol out of ; that, even if it has the syntax of a number. And we put it in pkg-witness's ; package unless the first chars of it are ACL2::. Known Discrepancy: We read ; |ACL2::Foo| as ACL2::|Foo| while CLTL reads it as pkg::|ACL2::Foo|. (let* ((len (length str)) (i (scan-past-whitespace str i len))) (cond ((< i len) (mv-let (char-lst j) (cond ((and (eql (char str i) #\:) (< (1+ i) len)) (cond ((eql (char str (1+ i)) #\|) (read-symbol-from-string2 str (+ i 2) len nil)) (t (read-symbol-from-string1 str (1+ i) len nil)))) ((eql (char str i) #\|) (read-symbol-from-string2 str (1+ i) len nil)) (t (read-symbol-from-string1 str i len nil))) (mv (cond ((eql (char str i) #\:) (intern (coerce char-lst 'string) "KEYWORD")) ((and (<= 6 (length char-lst)) (eql #\A (car char-lst)) (eql #\C (cadr char-lst)) (eql #\L (caddr char-lst)) (eql #\2 (cadddr char-lst)) (eql #\: (car (cddddr char-lst))) (eql #\: (cadr (cddddr char-lst)))) (intern (coerce (cddr (cddddr char-lst)) 'string) "ACL2")) (t (intern-in-package-of-symbol (coerce char-lst 'string) pkg-witness))) j))) (t (mv nil nil))))) (defun scan-past-newline (str i maximum) (cond ((< i maximum) (cond ((eql (char str i) #\Newline) (1+ i)) (t (scan-past-newline str (1+ i) maximum)))) (t maximum))) (defun scan-past-newlines (str i maximum) (cond ((< i maximum) (cond ((eql (char str i) #\Newline) (scan-past-newlines str (1+ i) maximum)) (t i))) (t maximum))) (defun scan-past-tilde-slash (str i maximum) (cond ((< i maximum) (cond ((eql (char str i) #\~) (cond ((and (< (1+ i) maximum) (eql (char str (1+ i)) #\/)) (cond ((or (= i 0) (not (eql (char str (1- i)) #\~))) (+ 2 i)) (t (scan-past-tilde-slash str (+ 2 i) maximum)))) (t (scan-past-tilde-slash str (+ 2 i) maximum)))) (t (scan-past-tilde-slash str (1+ i) maximum)))) (t maximum))) (defun scan-to-doc-string-part1 (parti str i maximum) (cond ((= parti 0) i) (t (scan-to-doc-string-part1 (1- parti) str (scan-past-whitespace str (scan-past-tilde-slash str i maximum) maximum) maximum)))) (defun scan-to-doc-string-part (i str) ; We assume str is a doc-stringp. Thus, it has the form: ; ":Doc-Section ~/ ~/ ~/ " ; where the first space above is one or more #\Spaces, the is ; arbitrary whitespace but including at least one #\Newline, and the ; remaining spaces are arbitrary whitespace. It is possible that ; the string terminates after any parti. We return the index of ; the ith part. (let ((len (length str))) (scan-to-doc-string-part1 i str (scan-past-whitespace str (scan-past-newline str 0 len) len) len))) (defun get-one-liner-as-string1 (str i j acc) (cond ((<= i j) (get-one-liner-as-string1 str i (1- j) (cons (char str j) acc))) (t (coerce acc 'string)))) (defun get-one-liner-as-string (str) (let ((i (scan-to-doc-string-part 0 str)) (max (length str))) (get-one-liner-as-string1 str i (- (scan-past-tilde-slash str i max) 3) nil))) (defun read-doc-string-citations1 (name str i) (mv-let (sym1 i) (read-symbol-from-string str i name) (cond ((null i) nil) (t (mv-let (sym2 i) (read-symbol-from-string str i name) (cond ((null i) (cons (cons sym1 0) nil)) (t (cons (cons sym1 sym2) (read-doc-string-citations1 name str i))))))))) (defun read-doc-string-citations (name str) ; This function reads the contents of the citations section of a doc ; string, expecting it to be an even number of symbols and returning ; them as a list of pairs. I.e., ":cite a :cited-by b :cite c" is ; read as ((:cite . a) (:cited-by . b) (:cite . c)). If there are an ; odd number of symbols, a 0 replaces the unsupplied one. Since we ; can't possibly read a 0 as a number (our stupid reader makes ; symbols) this is an unambiguous signal that the string does not ; parse. This function doesn't care whether the symbols in the odd ; positions are :cite and :cited-by or not. I.e., "A B C D" reads as ; ((A . B) (C . D)). (let ((i (scan-to-doc-string-part 3 str))) (read-doc-string-citations1 name str i))) (defun doc-topicp (name wrld) (assoc-equal name (global-val 'documentation-alist wrld))) (defun ignore-doc-string-error (wrld) (cdr (assoc-eq :ignore-doc-string-error (table-alist 'acl2-defaults-table wrld)))) (defmacro er-doc (ctx str &rest str-args) `(let ((er-doc-ign (ignore-doc-string-error (w state)))) (cond ((eq er-doc-ign t) (value nil)) ((eq er-doc-ign :warn) (pprogn (warning$ ,ctx "Documentation" "No :doc string will be stored. ~@0" (check-vars-not-free (er-doc-ign) (msg ,str ,@str-args))) (value nil))) (t (er soft ,ctx "~@0" (check-vars-not-free (er-doc-ign) (msg ,str ,@str-args))))))) (defun chk-doc-string-citations (str citations wrld) ; We know that citations is a list of pairs of symbols, by construction -- it ; was produced by read-doc-string-citations. We check that the car of each ; pair is either :cite or :cited-by and the cdr is a previously documented ; topic symbol, returning an error message or nil. (cond ((null citations) nil) ((or (eq (caar citations) :cite) (eq (caar citations) :cited-by)) (cond ((equal (cdar citations) 0) (msg "The citations section of a formatted documentation string ~ must contain an even number of tokens. The citations below ~ do not. See :DOC doc-string.~|~%~x0.~%" str)) ((doc-topicp (cdar citations) wrld) (chk-doc-string-citations str (cdr citations) wrld)) (t (msg "The symbols cited in the citations section of a formatted ~ documentation string must be previously documented topics. ~ ~x0 is not and, hence, the string below is ill-formed. ~ See :DOC doc-string.~|~%~x1.~%" (cdar citations) str)))) (t (msg "The citations section of a formatted documentation string must ~ contain an even number of tokens. Each token in an odd numbered ~ position must be either :CITE or :CITED-BY. But in the string ~ below ~x0 occurs in an odd numbered position. See :DOC ~ doc-string.~|~%~x1.~%" (caar citations) str)))) (defun chk-well-formed-doc-string (name doc ctx state) ; This function checks that doc is a well-formed doc string. ; It either causes an error or returns (as the value component of ; an error triple) a pair (section-symbol . citations) obtained ; by parsing the doc string. If doc does not even appear to ; be one of our formatted doc strings, we return nil. (let ((wrld (w state))) (cond ((doc-stringp doc) (let ((len (length doc)) (old-doc-tuple (assoc-equal name (global-val 'documentation-alist wrld)))) (cond ; We used to print a warning here when a system DEFLABEL is redefined, which ; advised that the documentation would remain unchanged. Probably we had this ; code as an aid towards proving our way through axioms.lisp, but now we don't ; seem to need it. ((= (scan-past-tilde-slash doc (scan-past-tilde-slash doc (scan-past-newline doc 0 len) len) len) len) (er-doc ctx "Formatted documentation strings must contain at least two ~ occurrences of tilde slash after the first newline so as ~ to delimit the three required parts: the one-liner, the ~ notes, and the details. While the notes section may be ~ empty, the details section may not. The string below ~ violates this requirement. See :DOC doc-string.~|~%~x0.~%" doc)) (t (mv-let (section-sym i) (read-symbol-from-string doc 13 (if (stringp name) 'chk-well-formed-doc-string name)) ; If we're documenting a package or book name, i.e., a stringp, then we can't ; use it to provide the default package in which read-symbol-from-string ; interns its symbols. We use the "ACL2" package. (cond ((null i) (er-doc ctx "Formatted documentation strings must specify a ~ section symbol after the :Doc-Section header and ~ before the first newline character. The string ~ below does not specify a section symbol. See ~ :DOC doc-string.~|~%~y0.~%" doc)) ((and old-doc-tuple (not (equal section-sym (cadr old-doc-tuple)))) (er-doc ctx "The documentation string already in place for ~ the name ~x0 is stored under section name ~x1, ~ but you are trying to store it under a new ~ section name, ~x2. This is not allowed. See ~ :DOC defdoc." name (cadr old-doc-tuple) section-sym)) ((or (equal section-sym name) (doc-topicp section-sym wrld)) (let ((citations (read-doc-string-citations section-sym doc))) (let ((msg (chk-doc-string-citations doc citations wrld))) (cond (msg (er-doc ctx "~@0" msg)) (t (pprogn (if old-doc-tuple (warning$ ctx "Documentation" "The name ~x0 is currently ~ documented. That documentation is ~ about to be replaced." name) state) (value (cons section-sym citations)))))))) (t (er-doc ctx "The section symbol of a formatted ~ documentation string must be either the name ~ being documented or a previously documented ~ name. ~x1 is neither. Thus, the string below ~ is an ill-formed documentation string. See ~ :DOC doc-string.~|~%~x2.~%" name section-sym doc)))))))) (t (value nil))))) (defun translate-doc (name doc ctx state) ; If this function does not cause an error, it returns a pair of the form ; (section-symbol . citations) parsed from the doc string, or nil if the ; doc string is unformatted. (cond ((and doc (not (stringp doc))) (er soft ctx "When a documentation string is supplied the value must ~ be a string, but ~x0 is not. See :DOC doc-string." doc)) ((null name) (cond ((doc-stringp doc) (er soft ctx "Events that introduce no names (e.g., in-theory ~ and verify-guards) are not permitted to have ~ documentation strings that begin with the ~ characters ``:Doc-Section''. See :DOC ~ doc-string.")) (t (value nil)))) (t (chk-well-formed-doc-string name doc ctx state)))) (defun translate-doc-lst (names docs ctx state) (cond ((null names) (value nil)) (t (er-let* ((pair (translate-doc (car names) (car docs) ctx state)) (rst (translate-doc-lst (cdr names) (cdr docs) ctx state))) (value (cons pair rst)))))) (defun get-cites (citations) ; This function collects all the symbols that are paired with ; :cite in the citations alist. (cond ((null citations) nil) ((eq (caar citations) :cite) (add-to-set-equal (cdar citations) (get-cites (cdr citations)))) (t (get-cites (cdr citations))))) (defun alpha-< (x y) ; X and y are symbols or strings. We return t iff x comes before y in ; an alphabetic ordering of their print names. We are somewhat free ; to decide how to handle packages and strings v. symbols. We choose ; to put 'ABC before "ABC" and we use package-names only to break ; ties among two symbols with the same symbol-name. (let ((xstr (if (symbolp x) (symbol-name x) x)) (ystr (if (symbolp y) (symbol-name y) y))) (cond ((string< xstr ystr) t) ((equal xstr ystr) (if (symbolp x) (if (symbolp y) (string< (symbol-package-name x) (symbol-package-name y)) t) nil)) (t nil)))) (defun merge-alpha-< (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((alpha-< (car l1) (car l2)) (cons (car l1) (merge-alpha-< (cdr l1) l2))) (t (cons (car l2) (merge-alpha-< l1 (cdr l2)))))) (defun merge-sort-alpha-< (l) (cond ((null (cdr l)) l) (t (merge-alpha-< (merge-sort-alpha-< (evens l)) (merge-sort-alpha-< (odds l)))))) (defun update-alpha-<-alist (key val alist) ; Alist is an alist whose keys are either symbols or strings and ; ordered by alpha-<. We bind key to val. Key may already be ; present. (cond ((null alist) (list (cons key val))) ((equal key (caar alist)) (cons (cons key val) (cdr alist))) ((alpha-< (caar alist) key) (cons (car alist) (update-alpha-<-alist key val (cdr alist)))) (t (cons (cons key val) alist)))) (defun put-cited-bys (name citations alist) ; This function visits every symbol paired with :cited-by in the ; citations alist and puts name in the citations field of the symbol, ; unless name is either the symbol itself or name already occurs in ; the citations. Alist is the 'documentation-alist. (cond ((null citations) alist) (t (put-cited-bys name (cdr citations) (if (and (eq (caar citations) :cited-by) (not (equal name (cdar citations)))) (let ((doc-tuple (assoc-equal (cdar citations) alist))) (cond ((member-equal name (caddr doc-tuple)) alist) (t (update-alpha-<-alist (cdar citations) (list (cadr doc-tuple) (cons name (caddr doc-tuple)) (cadddr doc-tuple)) alist)))) alist))))) (defun update-doc-database (name doc pair wrld) ; Name is a documented name, i.e., a symbol or a string (package name). ; Pair is the (section-symbol . citations) pair parsed from the doc ; string, or nil if doc is unformatted. If pair is non-nil we add a ; new entry to the documentation database. Each entry has the form ; (name section-symbol cites doc), where cites is the list of all x ; such that (:cite x) occurs citations. Entries are ordered ; alphabetically by name. In addition, add name to the cites list of ; every x such that (:cited-by x) occurs in citations. (cond (pair (global-set 'documentation-alist (put-cited-bys name (cons (cons :cited-by (car pair)) (cdr pair)) (update-alpha-<-alist name (list (car pair) (get-cites (cdr pair)) doc) (global-val 'documentation-alist wrld))) wrld)) (t wrld))) (defun update-doc-database-lst (names docs pairs wrld) (cond ((null names) wrld) (t (update-doc-database-lst (cdr names) (cdr docs) (cdr pairs) (update-doc-database (car names) (car docs) (car pairs) wrld))))) (defun putprop-unless (sym key val exception wrld) ; We do (putprop sym key val wrld) unless val is exception, in ; which case we do nothing. We return the possibly modified wrld. (cond ((equal val exception) wrld) (t (putprop sym key val wrld)))) (defun redefined-warning (redef ctx state) ; Redef is either nil, a true-list of symbols, a single symbol, or a ; single string. In the latter two cases we think of redef denoting a ; singleton list. If the list denoted by redef is non-nil we print a ; warning that every name in that list has been redefined. (if redef (warning$ ctx "Redef" "~&0 redefined.~%~%" (if (atom redef) (list redef) redef)) state)) (defun get-event (name wrld) ; This function has undefined behavior when name was not introduced by an ACL2 ; event. (access-event-tuple-form (cddr (car (lookup-world-index 'event (getprop name 'absolute-event-number 0 'current-acl2-world wrld) wrld))))) (defun redundant-labelp (name event-form wrld) ; The only time a label is considered redundant is during the second pass of ; initialization and only then if it was already defined with the same ; event-form. (and (global-val 'boot-strap-pass-2 wrld) (getprop name 'label nil 'current-acl2-world wrld) (equal event-form (get-event name wrld)))) (defun deflabel-fn (name state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'deflabel name)) (let ((wrld1 (w state)) (event-form (or event-form (list* 'deflabel name (if doc (list :doc doc) nil))))) (cond ((redundant-labelp name event-form wrld1) (stop-redundant-event ctx state)) (t (er-progn (chk-all-but-new-name name ctx 'label wrld1 state) (er-let* ((wrld2 (chk-just-new-name name 'label nil ctx wrld1 state)) (doc-pair (translate-doc name doc ctx state))) (let ((wrld3 (update-doc-database name doc doc-pair (putprop name 'label t wrld2)))) ; The only reason we store the 'label property is so that name-introduced ; recognizes this name. ; Note: We do not permit DEFLABEL to be made redundant. If this ; is changed, change the text of the :DOC for redundant-events. (install-event name event-form 'deflabel name nil nil nil nil wrld3 state))))))))) ; That completes the development of deflabel. But now there is the ; considerable task of printing out documentation strings and help ; info based on the documentation database. First, let us get ; defdoc out of the way. (defun defdoc-fn (name state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defdoc name)) (let ((wrld1 (w state)) (event-form (or event-form (list* 'defdoc name doc)))) (er-progn (if (or (and (symbolp name) name) (stringp name)) (value nil) (er soft ctx "Names to be documented must be strings or non-nil symbols and ~x0 ~ is not." name)) (cond ((global-val 'boot-strap-pass-2 (w state)) ; When the documentation for topic BDD was moved to axioms.lisp, we had the ; following problem: evaluation of (defdoc bdd ...) in the second pass of ; boot-strap was setting the "cites" (subtopics) field to nil. So, now we skip ; defdoc events on the second pass of the boot-strap. (value :skipped)) (t (er-let* ((doc-pair (translate-doc name doc ctx state))) (cond (doc-pair (let ((wrld2 (update-doc-database name doc doc-pair wrld1))) (install-event name event-form 'defdoc 0 nil nil nil nil wrld2 state))) (t (er soft ctx "The doc string supplied for ~x0 is not a valid ACL2 ~ documentation string. See :DOC doc-string." name)))))))))) #+acl2-loop-only (defmacro defdoc (&whole event-form name doc) ;See note ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section Events add a ~il[documentation] topic~/ ~bv[] Examples: (defdoc interp-section \":Doc-Section ...\")~/ General Form: (defdoc name doc-string) ~ev[] where ~c[name] is a symbol or string to be documented and ~ilc[doc-string] is a ~il[documentation] string (~pl[doc-string]). This event adds the ~il[documentation] string for symbol ~c[name] to the ~c[:]~ilc[doc] database. It may also be used to change the ~il[documentation] for ~c[name] if ~c[name] already has ~il[documentation]. The difference between this event and ~ilc[deflabel] is that, unlike ~ilc[deflabel] (but like ~ilc[table]), it does not mark the current ~il[history] with the ~c[name]. But like ~ilc[deflabel], ~c[defdoc] ~il[events] are never considered redundant (~pl[redundant-events]). ~l[deflabel] for a means of attaching a ~il[documentation] string to a name that marks the current ~il[history] with that name. We now elaborate further on how ~c[defdoc] may be useful in place of ~ilc[deflabel]. It is usually sufficient to use ~ilc[deflabel] when you might be tempted to use ~c[defdoc]. However, unlike ~ilc[deflabel], ~c[defdoc] does not mark the current ~il[history] with ~c[name]. Thus, ~c[defdoc] is useful for introducing the ~il[documentation] for a ~ilc[defun] or ~ilc[deftheory] event, for example, several ~il[events] before the function or theory is actually defined. For example, suppose you want to define a theory (using ~ilc[deftheory]). You need to prove the lemmas in that theory before executing the ~ilc[deftheory] event. However, it is quite natural to define a ~c[:Doc-Section] (~pl[doc-string]) whose name is the name of the theory to be defined, and put the ~il[documentation] for that theory's lemmas into that ~c[:Doc-Section]. ~c[Defdoc] is ideal for this purpose, since it can be used to introduce the ~c[:Doc-Section], followed by the lemmas referring to that ~c[:Doc-Section], and finally concluded with a ~ilc[deftheory] event of the same name. If ~ilc[deflabel] were used instead of ~c[defdoc], for example, then the ~ilc[deftheory] event would be disallowed because the name is already in use by the ~ilc[deflabel] event. We also imagine that some users will want to use ~c[defdoc] to insert the ~il[documentation] for a function under development. This ~c[defdoc] event would be followed by definitions of all the subroutines of that function, followed in turn by the function definition itself. Any time ~c[defdoc] is used to attach ~il[documentation] to an already-documented name, the name must not be attached to a new ~c[:Doc-Section]. We make this requirement as a way of avoiding loops in the ~il[documentation] tree. When ~il[documentation] is redefined, a warning will be printed to the terminal.~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (list 'defdoc-fn (list 'quote name) 'state (list 'quote doc) (list 'quote event-form))) (defun access-doc-string-database (name state) ; Name is a symbol or a string. This function would be just ; (assoc-equal name documentation-alist) but for one twist: if name is ; a symbol and not in the database, we try acl2::name instead. We ; return (name section-symbol cites doc), or nil if there is no entry ; for either name. The reason we go to ACL2::name after name fails is ; that: ; MY-PKG !>:doc defthm ; will read as MY-PKG::DEFTHM and we assume that most of the time ; the documentation topics the user is interested in are ours. (cond ((symbolp name) (let ((doc-tuple (assoc-eq name (global-val 'documentation-alist (w state))))) (cond (doc-tuple doc-tuple) ((not (equal (symbol-package-name name) "ACL2")) (assoc-equal (intern-in-package-of-symbol (symbol-name name) 'get-doc-string) (global-val 'documentation-alist (w state)))) (t nil)))) ((stringp name) (assoc-equal name (global-val 'documentation-alist (w state)))) (t nil))) (defun get-doc-string (name state) ; This function is provided simply to let the user see what ; doc strings really look like. (cadddr (access-doc-string-database name state))) (defun get-doc-string-de-indent1 (str i) (cond ((eql (char str i) #\Newline) 0) (t (1+ (get-doc-string-de-indent1 str (1- i)))))) (defun get-doc-string-de-indent (str) ; The text in a doc string is assumed to be indented some to ; avoid screwing up the Emacs formatting commands and to make their ; appearance in source files more pleasant. We de-indent them as we ; print, stripping off a fixed number of #\Spaces after every newline, ; when possible. We compute the de-indent number by looking at the ; indentation of the one-liner part. (get-doc-string-de-indent1 str (1- (scan-to-doc-string-part 0 str)))) (defun use-doc-string-de-indent (d str i maximum) ; If there are d spaces in str starting at i, return i+d; else nil. (cond ((= d 0) i) ((< i maximum) (cond ((eql (char str i) #\Space) (use-doc-string-de-indent (1- d) str (1+ i) maximum)) (t nil))) (t nil))) (defun doc-prefix (state) (if (f-boundp-global 'doc-prefix state) (f-get-global 'doc-prefix state) "| ")) (defun princ-prefix (prefix channel state) (cond ((consp prefix) (pprogn (princ$ (car prefix) channel state) (spaces (cdr prefix) (length (car prefix)) channel state))) (t (princ$ prefix channel state)))) (defun length-prefix (prefix) (cond ((consp prefix) (+ (length (car prefix)) (cdr prefix))) (t (length prefix)))) (defun save-more-doc-state (str i maximum de-indent prefix state) (cond ((or (>= i maximum) (and (int= (+ i 2) maximum) (eql (char str i) #\~) (eql (char str (1+ i)) #\/))) (f-put-global 'more-doc-state nil state)) (t (f-put-global 'more-doc-state (list str i maximum de-indent prefix) state)))) (defun doc-char-subst-table-p (x) ; See comment in terminal-markup-table. (cond ((consp x) (and (consp (car x)) (not (eql (caar x) #\~)) (not (eql (caar x) #\Newline)) (character-listp (car x)) (doc-char-subst-table-p (cdr x)))) (t (null x)))) (defun set-doc-char-subst-table (x state) (if (doc-char-subst-table-p x) (pprogn (f-put-global 'doc-char-subst-table x state) (value :invisible)) (er soft 'set-doc-char-subst-table "The character substitution table must be an alistp whose keys are ~ all characters other than ~~ and values are all strings. The object ~ ~x0 does not have this property." x))) (defun doc-char-subst-table (state) ; See comment in terminal-markup-table. (f-get-global 'doc-char-subst-table state)) (defun doc-fmt-alist (state) (f-get-global 'doc-fmt-alist state)) (defconst *terminal-markup-table* ; Examples of links are as follows. ; ~L (ordinary link) ; ~L[arg] prints ``See :DOC arg'' to the terminal, and something ; analogous in other settings (but possibly with a link established, ; or in the case of printed text, with a reference to a page number ; and a section). ; Example: ~l[program] for how to do rapid prototyping. ; -- Prints to the terminal as ; See :DOC program for how to do rapid prototyping. ; -- A printed version might look more like: ; See :DOC program, Section 1.3, page 92 for how to do rapid prototyping. ; -- Could print to emacs info as something like: ; *See program:: for how to do rapid prototyping. ; ~PL (parenthetical link) ; ~pl[arg] prints ``see :DOC arg'' (just like ~l, but with lower-case ``see'') ; to the terminal; as with ~l, it may establish a link in other settings. ; The name ``parenthetical'' is taken from texinfo, which claims to ; require that, unlike the other kind of link, commas and periods may not ; appear immediately afterwards. For now, we ignore this issue, ; considering that ~pl is distinguished from ~l mainly by the case of ; ``see''. ; ~IL (invisible link): use the word normally and do not draw any special ; attention to the fact that it is a link. ; ~IL[arg] prints ``arg'' ; The following association list maps each such directive (as a ; case-insensitive string) to a flag and fmt message, i.e., to a string ; followed by an association list appropriate for that string. The flag ; determines whether the first part of arg is to be read as a symbol as when it ; represents the name of a link. When flag=t, we split the arg into two parts, ; the symbol and the text. A flag of t is used on those keys which translate ; into HREF links in HTML so that the first of the two args is treated as the ; doc topic identifying the link and the rest is treated as the mouse-sensitive ; button text highlighted by HTML. (Note that even in the terminal and tex ; markup tables we must use flag t on those keys so that those commands get ; only the first doc topic part of the arg.) The string corresponding to key ; will ultimately be printed in place of the ~key[arg] expression, except that ; the corresponding alist will first be extended by mapping #\a to the (first ; part of the) string arg, and mapping #\A to the upper-casing of that string, ; and #\t to the second part of the string arg (provided flag=t). Note also ; that the (doc-char-subst-table state) is used to control the substitution of ; sequences of characters for single characters, both in the arg portion of ; ~key[arg] and in characters not part of such expressions, but *not* in the ; key. To escape a character from substitution by that table, precede it by a ; ~. ; Finally, note that in ~key[arg] we do not allow newline characters. That is ; because they will not get printed appropriately to the terminal. Thus, ~c ; may have to be used more than we'd like. If we really want to include a ; newline for some reason, it should be escaped with ~. `(("-" nil . "--") ("B" nil . "~st") ;bold font ("BF" nil . "") ;begin format -- ; -- like verbatim, but if possible don't change font ("BID" nil . "") ;begin implementation dependent ("BQ" nil . "") ;begin quotation ("BV" nil . "") ;begin verbatim ("C" nil . "~st") ;code, often preferred even if we have invisible link ("EF" nil . "") ;end format ("EID" nil . "") ;end implementation dependent ("EM" nil . "~st") ;emphasis (presumably italics if fonts are available) ("EQ" nil . "") ;end quotation ("EV" nil . "") ;end verbatim ("GIF" nil . "") ;gif file (currently only for HTML) ; ("GIF" nil . "gif file ~st omitted") ;alternate possibility for ~gif ("I" nil . "~st") ;italics font ("ID" nil . "") ;implementation dependent ("IL" t . "~st") ;invisible link, for true hypertext environments ("ILC" t . "~st") ;invisible link, but use code if such links don't show ("L" t . "See :DOC ~ss") ;link at beginning of sentence ("NL" nil . "~%") ;newline ("PAR" nil . "") ;paragraph mark, of no significance at the terminal ("PL" t . "see :DOC ~ss");parenthetical link, i.e., link ; not at beginning of sentence ("SC" nil . "~sT") ;(small, if possible) caps ("ST" nil . "~sT") ;strong emphasis (presumably bold if fonts are available) ("T" nil . "~st") ;typewriter font ("TERMINAL" nil . "~st") ;terminal only; otherwise argument is ignored ("WARN" nil . "<>") ("CLICK-HERE" t . "See :DOC ~ss") ("PCLICK-HERE" t . "see :DOC ~ss") ("FLY" t . "Flying Tour: see :DOC ~ss") ("WALK" t . "Walking Tour: see :DOC ~ss") ("LARGE-WALK" t . "Walking Tour: see :DOC ~ss") ("URL" nil . "~st") ;print as HTML hyperlink (when possible) ) ":Doc-Section Documentation a ~il[markup] table used for printing to the terminal~/ The value of the ACL2 constant ~c[*terminal-markup-table*] is an association list pairing ~il[markup] keys with strings, to be used for printing to the terminal. ~l[markup] for a description of the ACL2 ~il[markup] language.~/ The entries in ~c[*terminal-markup-table*] are of the form ~bv[] (key flag . fmt-string) ~ev[] where ~c[key] is one of the ~il[doc-string] tilde directives (~pl[markup]), ~c[flag] is a Boolean as described below, and ~c[fmt-string] is a string as expected by the ACL2 printing function ~ilc[fmt]. The system arranges that for any ~c[arg], when an expression ~~key[arg] is encountered by the ~il[documentation] printer, ~ilc[fmt] will print ~c[fmt-string] in an association list, binding keys based on ~c[arg] as follows. ~bv[] #\\p ~-[] the `pointer' ; only used if flag is t #\\s ~-[] the print name version of the pointer, e.g., |abc| or ABC #\\c ~-[] the parent file ; only used if flag is t #\\t ~-[] the displayed text #\\T ~-[] uppercased displayed text ~ev[] The first three entries are used only when the ~c[flag] associated with ~c[key] is ~c[t], indicating that the argument ~c[arg] of ~c[~~key] is to be parsed as starting with a symbol; for example, ~c[~~key[foo bar~]] will bind ~c[#\\p] to the symbol ~c[FOO]. The discussion of the above association list for printing ~c[fmt-string] applies when printing ~il[documentation] to other than the terminal as well. Such tools exist for Texinfo and for HTML; see files ~c[doc/write-acl2-html.lisp] and ~c[doc/write-acl2-texinfo.lisp] distributed with ACL2.") (defun doc-markup-table (state) (or (and (f-boundp-global 'doc-markup-table state) (f-get-global 'doc-markup-table state)) *terminal-markup-table*)) (defun doc-scan-past-tilde-key (name orig-position posn str maximum acc state) ; Posn is the position just after the first opening bracket ([) that is at or ; after position posn in the string str, and acc accumulates the characters ; found in the interim. The function returns (mv erp posn key state), where ; key is built from the accumulated characters so that we can view the string ; from the original position as "key[". Note that we deliberately do *not* use ; a char-subst-table here; key is taken literally. (cond ((not (< posn maximum)) (mv-let (erp val state) (er soft "printing documentation string" "In the process of processing the tilde (~~) directive at ~ position ~x0 in the documentation string for ~x1, ~ no opening bracket ([) was found between that tilde ~ and before the end of the string." orig-position name) (declare (ignore erp val)) (mv t nil nil state))) (t (let ((ch (char str posn))) (cond ((eql ch #\[) (mv nil (1+ posn) (coerce (reverse acc) 'string) state)) (t (doc-scan-past-tilde-key name orig-position (1+ posn) str maximum (cons ch acc) state))))))) (defun doc-scan-past-tilde-arg (name orig-position posn str maximum acc state) ; Posn is the position just after the first non-escaped closing bracket (]) at ; or after position posn in the string str, and acc accumulates the characters ; found in the interim. The function returns (mv erp posn arg state), where ; arg is built from the accumulated characters so that we can view the string ; from the original position as "arg]". (cond ((not (< posn maximum)) (mv-let (erp val state) (er soft "printing documentation string" "In the process of processing the tilde (~~) directive whose ~ argument begins at position ~x0 in the documentation string ~ for ~x1, no closing bracket (]) was found corresponding to ~ the preceding opening bracket." orig-position name) (declare (ignore erp val)) (mv t nil nil state))) (t (let ((ch (char str posn))) (cond ((eql ch #\]) (mv nil (1+ posn) (coerce (reverse acc) 'string) state)) ((eql ch #\Newline) ; Why do we have this annoying newline check, which so often bites us when ; writing out the :doc for html or texinfo? A quick answer is that a newline ; could indicate that we are missing a closing bracket in ~key[...], and ; perhaps that's a sufficient answer. But another answer is that otherwise, ; our printing to the terminal is ugly. Without this check we get the ; following after: ; (defdoc foo ; ":Doc-Section Miscellaneous ; ; my one-liner~/ ; ; Here we insert a linebreak: ~c[(fn ; arg)].~/~/") ; Notice the missing `|' at the beginning of a line. ; ACL2 !>:doc! foo ; | FOO my one-liner ; | ; | Here we insert a linebreak: (fn ; arg). ; | ; *- ; ACL2 !> (mv-let (erp val state) (er soft "printing documentation string" "In the process of processing the tilde (~~) directive ~ whose argument begins at position ~x0 in the ~ documentation string for ~x1, a newline was encountered. ~ This is illegal. Consider breaking this tilde directive ~ into several separate ones, each occurring on its own ~ line." orig-position name) (declare (ignore erp val)) (mv t nil nil state))) ((and (eql ch #\~) (< (1+ posn) maximum)) (doc-scan-past-tilde-arg name orig-position (+ 2 posn) str maximum (cons (char str (1+ posn)) acc) state)) (t (doc-scan-past-tilde-arg name orig-position (1+ posn) str maximum (cons ch acc) state))))))) (defun doc-scan-past-tilde (name posn str maximum markup-table state) ; Posn is the position of the first character after a tilde in str, ; in the following sense: ; ....~key[arg].... ; ^ ; We return (mv erp posn entry arg state), where ; ; erp = nil iff the `parse' succeeds; ; posn = new position, after the closing right bracket (]); ; entry = the entry in markup-table associated with k (which is non-empty if ; erp is nil); ; arg = the string enclosed in brackets after the key, as shown above. (mv-let (erp posn key state) (doc-scan-past-tilde-key name posn posn str maximum nil state) (cond (erp (mv erp nil nil nil state)) (t (let ((entry (assoc-string-equal key markup-table))) (cond ((null entry) (mv-let (erp val state) (er soft "printing documentation string" "~|Failed to find key ~x0 in current ~ markup table,~|~% ~x1,~|~%when printing ~ documentation for ~x2.~|" key markup-table name) (declare (ignore erp val)) (mv t nil nil nil state))) (t (mv-let (erp posn arg state) (doc-scan-past-tilde-arg name posn posn str maximum nil state) (cond (erp (mv erp nil nil nil state)) (t (mv nil posn entry arg state))))))))))) (defun assoc-char-alist-stringp (char-alist str len) ; Warning: Just like member-char-stringp, len must be strictly less than the ; length of string! (cond ((null char-alist) nil) (t (or (member-char-stringp (caar char-alist) str len) (assoc-char-alist-stringp (cdr char-alist) str len))))) (defun apply-char-subst-table1 (char-lst acc char-subst-table) ; Consider the result of replacing each character in char-lst with its value in ; char-subst-table when it is bound there, else leaving it unchanged, and then ; appending the result to the front of the list acc of characters. A symbol is ; then returned with that name that resides in the package of orig-symbol if ; orig-symbol is non-nil; otherwise, the string is returned. (cond ((null char-lst) (coerce (reverse acc) 'string)) (t (let ((temp (assoc (car char-lst) char-subst-table))) (cond (temp (apply-char-subst-table1 (cdr char-lst) (revappend (cdr temp) acc) char-subst-table)) (t (apply-char-subst-table1 (cdr char-lst) (cons (car char-lst) acc) char-subst-table))))))) (defun apply-char-subst-table (s char-subst-table spack) ; Consider the result of replacing each character in char-lst with its value in ; char-subst-table when it is bound there, else leaving it unchanged, and then ; appending the result to the front of the list acc of characters. A symbol is ; then returned with that name that resides in the package of orig-symbol if ; orig-symbol is non-nil; otherwise, the string is returned. (cond ((symbolp s) (let ((n (symbol-name s))) (cond ((assoc-char-alist-stringp char-subst-table n (1- (length n))) (intern-in-package-of-symbol (apply-char-subst-table1 (coerce n 'list) nil char-subst-table) spack)) (t s)))) ((stringp s) (cond ((assoc-char-alist-stringp char-subst-table s (1- (length s))) (apply-char-subst-table1 (coerce s 'list) nil char-subst-table)) (t s))) (t (er hard 'apply-char-subst-table "Attempted to apply character substitution table to non-symbol, ~ non-string: ~x0" s)))) (defun read-pointer-and-text1 (lst pacc sacc) (cond ((null lst) (mv (er hard 'read-pointer-and-text "Unbalanced vertical bars, ~x0" (coerce (reverse sacc) 'string)) nil nil)) ((eql (car lst) #\|) (cond ((cdr lst) (cond ((eql (cadr lst) #\Space) (mv (coerce (reverse pacc) 'string) (coerce (reverse (cons #\| sacc)) 'string) (coerce (cddr lst) 'string))) (t (mv (coerce (reverse pacc) 'string) (coerce (reverse (cons #\| sacc)) 'string) (coerce (cdr lst) 'string))))) (t (let ((temp (coerce (reverse pacc) 'string))) (mv temp (coerce (reverse (cons #\| sacc)) 'string) temp))))) (t (read-pointer-and-text1 (cdr lst) (cons (car lst) pacc) (cons (car lst) sacc))))) (defun read-pointer-and-text2 (lst acc) (cond ((eql (car lst) #\Space) (let ((temp (coerce (reverse acc) 'string))) (mv temp temp (coerce (cdr lst) 'string)))) (t (read-pointer-and-text2 (cdr lst) (cons (char-upcase (car lst)) acc))))) (defun read-pointer-and-text-raw (str) ; See the comment in lookup-fmt-alist, especially the table showing ; how we ``read'' a symbol from str. (cond ((eql (char str 0) #\|) (read-pointer-and-text1 (cdr (coerce str 'list)) nil '(#\|))) ((string-search '(#\Space) str nil) (read-pointer-and-text2 (coerce str 'list) nil)) (t (let ((temp (string-upcase str))) (mv temp temp str))))) (defun posn-char-stringp (chr str i) (cond ((zp i) (if (eql chr (char str i)) 0 nil)) ((eql chr (char str i)) i) (t (posn-char-stringp chr str (1- i))))) (defun replace-colons (p) (let ((posn (posn-char-stringp #\: p (1- (length p))))) (if (or (null posn) (eql posn 0)) p (concatenate 'string (subseq p 0 (if (eql (char p (1- posn)) #\:) (1- posn) posn)) "||" (subseq p (1+ posn) (length p)))))) (defun read-pointer-and-text (str bar-sep-p) (if bar-sep-p (mv-let (p s text) (read-pointer-and-text-raw str) (mv (replace-colons p) (replace-colons s) text)) (read-pointer-and-text-raw str))) (defun lookup-fmt-alist (str flag fmt-alist char-subst-table bar-sep-p) ; Warning: Keep this in sync with missing-fmt-alist-chars. ; Consider a tilde-directive ~?[str]. From str we create a fmt alist that is ; used while we print the string associated with ? in the markup table. This ; function creates that fmt alist from str and the flag which indicates whether ; the first part of str is to be read as a symbol, as in ~il[defun Definition], ; or not as in ~c[this Definition]. ; What are the symbols in the fmt alist we need? To find the answer, look in ; the markup table and collect all the fmt vars used. They are: ; #\p -- the "pointer" ; only used if flag is t ; #\s -- the print name version of the pointer, e.g., |abc| or ABC ; #\c -- the parent file ; only used if flag is t ; #\t -- the displayed text ; #\T -- uppercased displayed text ; #\w -- the html anchor for the warning message ; only used on ~WARN[] ; The entries marked "only used if flag is t" are not necessarily used if flag ; is t. For example, the entries in *terminal-markup-table* do not refer to ; #\c, since terminal documentation has no reason to refer to a "parent file". ; If flag is nil, then we bind just the last three. In this case, the ; displayed text is all of str. ; If flag is t, then we first ``read'' a symbol from str, effectively splitting ; str into two parts, sym and text. The split is indicated below. Note that ; sym is a string, not really a symbol. The "pointer" is the symbol-name of ; sym. The "print name of the pointer" is the symbol-name of sym possibly ; surrounded by vertical bars. ; str #\p #\s #\t ; ~?[abc] "ABC" "ABC" "abc" ; ~?[abc ] "ABC" "ABC" "" ; ~?[abc def ghi] "ABC" "ABC" "def ghi" ; ~?[|abc|] "abc" "|abc|" "abc" ; ~?[|abc| ] "abc" "|abc|" "" ; ~?[|abc| def ghi] "abc" "|abc|" "def ghi" ; ~?[|abc|def ghi] "abc" "|abc|" "def ghi" ; Parameter bar-sep-p says that symbols with :: in them, but not starting with ; :, are to be converted to strings with || in place of the colons. ; To find #\c we lookup sym in the fmt-alist provided. Then we bind #\p to sym ; and process text as in the flag=nil case. (cond ((null flag) (cond ((equal str "") ; We don't know that we are in the ~warn[] case so we might need #\t and #\T in ; the alist. We add them. (list* (cons #\t "") (cons #\T "") (cdr (assoc-string-equal "" fmt-alist)))) (t (list (cons #\t (apply-char-subst-table str char-subst-table nil)) (cons #\T (apply-char-subst-table (string-upcase str) char-subst-table nil)))))) (t (mv-let (p s text) (read-pointer-and-text str bar-sep-p) (let ((alist0 (list* (cons #\t (apply-char-subst-table text char-subst-table nil)) (cons #\T (apply-char-subst-table (string-upcase text) char-subst-table nil)) ; Note that it is NOT necessarily an error if the assoc-string-equal returns ; nil in the next line. This may be the case for forms of documentation where ; extra fmt-alist information is not required. For example, terminal ; documentation doesn't require the #\c "parent file" information that HTML ; documentation does, so for "links" produced by markup like ~il[x] and ~ilc[x] ; there may still be no fmt-alist entry for x. (cdr (assoc-string-equal p fmt-alist))))) (if (assoc #\p alist0) alist0 (list* (cons #\p (apply-char-subst-table p char-subst-table nil)) (cons #\s (apply-char-subst-table s char-subst-table nil)) alist0))))))) (defun bar-sep-p (state) (and (f-boundp-global 'bar-sep-p state) (f-get-global 'bar-sep-p state))) (defun char-to-string-alistp (lst) (declare (xargs :guard t :mode :logic)) (cond ((atom lst) (null lst)) (t (and (consp lst) (consp (car lst)) (characterp (caar lst)) (stringp (cdar lst)) (char-to-string-alistp (cdr lst)))))) (defun missing-fmt-alist-chars1 (str char-to-tilde-s-string-alist fmt-alist) ; See documentation for missing-fmt-alist-chars. (declare (xargs :guard (and (stringp str) (char-to-string-alistp char-to-tilde-s-string-alist) (eqlable-alistp fmt-alist)))) (cond ((endp char-to-tilde-s-string-alist) nil) (t (let ((fmt-char (caar char-to-tilde-s-string-alist)) (tilde-str (cdar char-to-tilde-s-string-alist)) (rest (missing-fmt-alist-chars1 str (cdr char-to-tilde-s-string-alist) fmt-alist))) (cond ((and (not (assoc fmt-char fmt-alist)) (search tilde-str str)) (cons fmt-char rest)) (t rest)))))) (defun missing-fmt-alist-chars (str fmt-alist) ; Warning: Keep the characters bound below with the documentation for ; lookup-fmt-alist. (NOTE: As of Oct. 2009 I do not really understand why ; other than #\p and #\c should get any attention here. Note that #\c and #\p ; are used for links in doc/write-acl2-html.lisp and ; doc/write-acl2-texinfo.lisp, respectively.) ; Return a list of characters C for which the given fmt-alist is incomplete for ; the fmt string, str, in the sense that (1) C is one of the characters listed ; below, (2) str contains the substring "~sC", and (3) C is not bound in ; fmt-alist. By calling this function, we can cause a nice error if such a ; character C is found, or even avoid an error by pointing to a special ; "undocumented" topic. (declare (xargs :guard (and (stringp str) (eqlable-alistp fmt-alist)))) (missing-fmt-alist-chars1 str '((#\p . "~sp") (#\s . "~ss") (#\c . "~sc") (#\t . "~st") (#\T . "~sT") (#\w . "~sw")) fmt-alist)) (defun complete-fmt-alist (topic-name fmt-alist undocumented-file char-subst-table) ; Warning: Keep this in sync with the comment about complete-fmt-alist in ; print-doc-string-part1. ; Return an extension of fmt-alist so that ~sc will refer to an undocumented ; topic if #\c is not already bound in fmt-alist (thus supporting the use of ; ~sc by doc/write-acl2-html.lisp at least through Oct. 2009), and similarly ; for ~sp and #\p (thus supporting the use of ~sp by ; doc/write-acl2-texinfo.lisp at least through Oct. 2009). Some day we may ; want to complete with other characters as well. (let* ((c-missing-p (not (assoc #\c fmt-alist))) (p-missing-p (not (assoc #\p fmt-alist))) (fmt-alist (cond (c-missing-p (acons #\c undocumented-file fmt-alist)) (t fmt-alist))) (fmt-alist (cond (p-missing-p (acons #\p (apply-char-subst-table topic-name char-subst-table nil) fmt-alist)) (t fmt-alist)))) fmt-alist)) (defmacro mv-to-state (n form) ; Form should evaluate to an mv of two or more values, all non-stobjs except ; the, which should be state. We return that state, discarding the other ; values. (declare (xargs :guard (and (integerp n) (< 1 n)))) (let ((vars (make-var-lst 'x (1- n)))) `(mv-let (,@vars state) ,form (declare (ignore ,@vars)) state))) (defun print-par-entry (entry fmt-alist char-subst-table channel state) (mv-to-state 2 (fmt1 (cddr entry) (lookup-fmt-alist "" nil fmt-alist char-subst-table (bar-sep-p state)) 0 channel state nil))) (defun print-doc-string-part1 (str i maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state ln undocumented-file vp) ; Parameter ln is a bit complicated, so we describe it in some detail. ; First suppose that ln is a number, in which case it is the number of lines ; printed so far. In this case, we do :more processing until we hit the line ; maximum (at which point we save the more-doc-state to continue) or the ; tilde-slash (at which point we set the more-doc-state to nil). ; When ln is nil, we do not bother to track the number of lines printed and we ; print them all up to the tilde-slash, but we then initialize the ; more-doc-state. The nil setting should be used when you are printing out ; parts 0 or 1 of the doc string. ; When ln is t we behave as for nil, except that we set the more-doc-state to ; nil (as we would for numeric ln) when we hit the tilde-slash. This setting ; is used when we want to dump the entire part 2. ; When ln is :par, then two consecutive newlines, together with all consecutive ; whitespace characters following them, are handled by printing using the entry ; for EPAR in markup-table, if there is one; else, by using the entry for PAR ; if there is one; else, without such special paragraph treatment. In the case ; that EPAR's entry is used, then if there are any remaining characters after ; the block of newlines, the entry for BPAR is then printed. ; The final legal value of ln is :par-off, whose purpose is to avoid printing ; paragraph markers when inside certain preformatted (verbatim) environments, ; typically ~bv/~ev and ~bf/~ev for printing to html and the like (but not the ; terminal or texinfo). Thus, :par-off is treated like :par except that there ; is no special treatment in the case of consecutive newlines. Thus :par-off ; is really treated lke t, except that ln becomes :par in recursive calls when ; the end of a preformatted environment is encountered. The argument vp ; (verbatim pair) never changes, and is nil when ln is not :par or :par-off. ; But vp is a pair (begin-markers . end-markers), for example, (("BV" "BF") ; . ("EV" "EF")), where begin-markers tell us to change ln from :par to ; :par-off and end-markers tell us to change ln from :par-off to :par. ; A precondition for this function is that if ln is :par or :par-off and if ; "EPAR" is bound in markup-table (and hence so is "BPAR"), then the ; begin-paragraph marker (as per "BPAR") has already been printed and the ; end-paragraph marker (as per "EPAR") will be printed. Informatlly, then, it ; is an invariant of this function that at every call in the case that ln is ; :par or :par-off, an end-paragraph marker is pending. (cond ((< i maximum) (let ((c (char str i))) (cond ((eql c #\~) (cond ((< (1+ i) maximum) (let ((c (char str (1+ i)))) (cond ((eql c #\/) (pprogn (newline channel state) (save-more-doc-state str (cond ((null ln) (scan-past-whitespace str (+ 2 i) maximum)) (t maximum)) maximum de-indent prefix state) (mv ln state))) ((eql c #\]) ; This directive, ~], in a documentation string is effective only during the ; processing of part 2, the details, and controls how much we show on each ; round of :more processing. If ln is not a number we are not doing :more ; processing and we act as though the ~] were not present. Otherwise, we put ; out a newline and save the :more state, positioning the string after the ~] ; (or the newlines following it). (cond ((not (integerp ln)) (print-doc-string-part1 str (+ 2 i) maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state ln undocumented-file vp)) (t (pprogn (newline channel state) (save-more-doc-state str (scan-past-newline str (+ 2 i) maximum) maximum de-indent prefix state) (mv ln state))))) ((eql c #\~) (pprogn (princ$ c channel state) (print-doc-string-part1 str (+ 2 i) maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state ln undocumented-file vp))) (t (mv-let (erp posn entry arg state) (doc-scan-past-tilde name (1+ i) str maximum markup-table state) (cond (erp (pprogn (save-more-doc-state str maximum maximum de-indent prefix state) (mv ln state))) (t (let* ((fmt-alist-for-fmt1 (lookup-fmt-alist arg (cadr entry) fmt-alist char-subst-table (bar-sep-p state))) (missing-fmt-alist-chars (missing-fmt-alist-chars (cddr entry) fmt-alist-for-fmt1)) (complete-alist-p (and undocumented-file missing-fmt-alist-chars)) (fmt-alist-for-fmt1 (if complete-alist-p (complete-fmt-alist name fmt-alist-for-fmt1 undocumented-file char-subst-table) fmt-alist-for-fmt1))) (prog2$ (cond ((set-difference-eq missing-fmt-alist-chars ; Keep the list below in sync with complete-fmt-alist. In this case it is ; irrelevant whether or not undocumented-file is specified (non-nil); we will ; get an error either way. '(#\c #\p)) (er hard 'print-doc-string-part1 ; The use of ~| below guarantees reasonable line breaks even if the margins are ; set to very large numbers. "~|Fatal error printing the :DOC string for ~ topic ~x0,~|due to substring:~| ~~~s1[~s2]." name (string-downcase (car entry)) arg)) ((and missing-fmt-alist-chars (not undocumented-file)) (er hard 'print-doc-string-part1 "~|Error printing the :DOC string for topic ~ ~x0,~|due to substring:~| ~~~s1[~s2].~|If ~ this error is not due to a typo, then it ~ can probably be resolved either by:~|-- ~ adding a :DOC string for ~x2,~|OR~|-- ~ passing an UNDOCUMENTED-FILE argument to ~ the appropriate~| translator (e.g., ~ function acl2::write-html-file in~|~ ~ ~ ~ distributed file doc/write-html)." name (string-downcase (car entry)) arg)) (t nil)) (pprogn (cond (missing-fmt-alist-chars (assert$ undocumented-file ; see error above (warning$ 'print-doc-string-part1 "Documentation" ; Add a newline just below, since we may be in a context where the margins have ; been made essentially infinite. "~|Broken link in :doc ~x0: ~ ~~~s1[~s2].~%" name (string-downcase (car entry)) arg))) (t state)) (mv-let (col state) (fmt1 (cddr entry) fmt-alist-for-fmt1 0 channel state nil) (declare (ignore col)) (print-doc-string-part1 str posn maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state (cond ((not vp) ln) ((eq ln :par) (if (member-string-equal (car entry) (car vp)) :par-off :par)) ((eq ln :par-off) (if (member-string-equal (car entry) (cdr vp)) :par :par-off)) (t ln)) undocumented-file vp)))))))))))) (t (pprogn (princ$ c channel state) (newline channel state) (save-more-doc-state str (+ 1 i) maximum de-indent prefix state) (mv ln state))))) ((eql c #\Newline) (mv-let (epar-p entry) (cond ((and (eq ln :par) (< (1+ i) maximum) (eql (char str (1+ i)) #\Newline)) (let ((temp (assoc-string-equal "EPAR" markup-table))) (cond (temp (mv t temp)) (t (let ((temp (assoc-string-equal "PAR" markup-table))) (mv nil temp)))))) (t (mv nil nil))) (cond (entry (let ((next-i (scan-past-whitespace str (+ 2 i) maximum))) (cond ((eql next-i maximum) (pprogn (save-more-doc-state str next-i maximum de-indent prefix state) (mv ln state))) (t (pprogn (print-par-entry entry fmt-alist char-subst-table channel state) (cond ((not epar-p) state) (t (let ((entry2 (assoc-string-equal "BPAR" markup-table))) (prog2$ (or entry2 (er hard 'print-doc-string-part1 "Found EPAR but not BPAR in ~ markup-table,~|~x0." markup-table)) (print-par-entry entry2 fmt-alist char-subst-table channel state))))) (print-doc-string-part1 str next-i maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state ln undocumented-file vp)))))) ((and (integerp ln) (< (1+ i) maximum) (eql (char str (1+ i)) #\Newline) (<= (f-get-global 'more-doc-min-lines state) (+ 2 ln))) (pprogn (newline channel state) (newline channel state) (save-more-doc-state str (or (use-doc-string-de-indent de-indent str (+ 2 i) maximum) (+ 2 i)) maximum de-indent prefix state) (mv ln state))) ((and (integerp ln) (<= (f-get-global 'more-doc-max-lines state) (1+ ln))) (pprogn (newline channel state) (save-more-doc-state str (or (use-doc-string-de-indent de-indent str (+ 1 i) maximum) (+ 1 i)) maximum de-indent prefix state) (mv ln state))) (t (pprogn (newline channel state) (princ-prefix prefix channel state) (print-doc-string-part1 str (or (use-doc-string-de-indent de-indent str (1+ i) maximum) (1+ i)) maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state (if (integerp ln) (1+ ln) ln) undocumented-file vp)))))) (t (pprogn (princ$ (let ((temp (assoc c char-subst-table))) (if temp (coerce (cdr temp) 'string) c)) channel state) (print-doc-string-part1 str (+ 1 i) maximum de-indent prefix markup-table char-subst-table fmt-alist channel name state ln undocumented-file vp)))))) (t (pprogn (newline channel state) (save-more-doc-state str i maximum de-indent prefix state) (mv ln state))))) (defun print-doc-string-part-mv (i str prefix markup-table char-subst-table fmt-alist channel name ln undocumented-file vp state) ; Str is a doc string and i is a part number, 0, 1, or 2. We print the ith ; part of the string to channel. We embed non-empty part 1's between a pair of ; newlines. ; When ln is :par, we interpret two consecutive blank lines as calling for a ; paragraph marker, in the sense described in the comments in ; print-doc-string-part1. When ln is t, we print the entire part; see ; print-doc-string-part1. Note that ln is ignored when i is 0. ; We return (mv new-ln state), where new-ln is the final value of ln. Normally ; we will not need new-ln, but it is useful when printing part 1 followed by ; part 2 in the case that a preformatted environment spans the two parts, e.g., ; "~bv[]...~/...~ev[]". Typically we find this with "Example Forms" followed ; by "General Form". We use the new-ln value in file doc/write-acl2-html.lisp, ; function write-a-doc-section (as of 1/12/2011). (let ((b-entry (assoc-string-equal "BPAR" markup-table)) (e-entry (assoc-string-equal "EPAR" markup-table))) (pprogn (prog2$ (or (iff b-entry e-entry) (er hard 'print-doc-string-part "Found ~x0 but not ~x1 in markup-table,~|~x2." (if b-entry "BPAR" "EPAR") (if b-entry "EPAR" "BPAR") markup-table)) (cond ((and (not (eql i 0)) b-entry (not (eq ln :par-off))) (print-par-entry b-entry fmt-alist char-subst-table channel state)) (t state))) (mv-let (new-ln state) (let ((k (scan-to-doc-string-part i str)) (maximum (length str))) (cond ((= i 1) (if (or (= k maximum) (and (eql (char str k) #\~) (< (1+ k) maximum) (eql (char str (1+ k)) #\/))) ; If the part we are trying to print is empty, then don't do anything. ; except save the more doc state. (pprogn (save-more-doc-state str (scan-past-whitespace str (+ 2 k) maximum) maximum (get-doc-string-de-indent str) prefix state) (mv ln state)) ; Otherwise, put out a newline first and then do it. This elaborate ; code is here to prevent us from putting out an unnecessary newline. (pprogn (princ-prefix prefix channel state) (newline channel state) (princ-prefix prefix channel state) (print-doc-string-part1 str k maximum (get-doc-string-de-indent str) prefix markup-table char-subst-table fmt-alist channel name state ln undocumented-file vp)))) (t (print-doc-string-part1 str k maximum (get-doc-string-de-indent str) prefix markup-table char-subst-table fmt-alist channel name state (if (= i 0) nil ln) undocumented-file vp)))) (pprogn (cond ((and (not (eql i 0)) e-entry (not (eq new-ln :par-off))) (print-par-entry e-entry fmt-alist char-subst-table channel state)) (t state)) (mv new-ln state)))))) (defun print-doc-string-part (i str prefix markup-table char-subst-table fmt-alist channel name ln undocumented-file vp state) (mv-to-state 2 (print-doc-string-part-mv i str prefix markup-table char-subst-table fmt-alist channel name ln undocumented-file vp state))) (defun get-doc-section (section alist) (cond ((null alist) nil) ((and (equal section (cadar alist)) (not (equal section (caar alist)))) (cons (car alist) (get-doc-section section (cdr alist)))) (t (get-doc-section section (cdr alist))))) (defmacro pstate-global-let* (bindings body) ; This macro is useful when you want the effect of state-global-let* ; but you are in a situation in which you are working only with state ; and not with error/val/state triples. `(mv-let (erp val state) (state-global-let* ,bindings (pprogn ,body (value nil))) (declare (ignore erp val)) state)) (mutual-recursion (defun print-doc (name n prefix markup-table char-subst-table fmt-alist channel state) ; Name is either an atom (in which case we look it up in the documentation ; alist) -- it must be there -- or it is a doc-tuple from the alist. ; N should be either 0, 1, or 2. We print the level 0, 1, or 2 for ; doc-tuple. We assume that we are printing into col 0. We always ; end at col 0. (let ((doc-tuple (cond ((atom name) (assoc-equal name (global-val 'documentation-alist (w state)))) (t name))) (start-column (f-get-global 'print-doc-start-column state))) (cond ((= n 0) (pprogn (princ-prefix prefix channel state) (mv-let (col state) (splat-atom (cond ((symbolp (car doc-tuple)) (apply-char-subst-table (car doc-tuple) char-subst-table (car doc-tuple))) ((stringp (car doc-tuple)) (apply-char-subst-table (car doc-tuple) char-subst-table nil)) (t (car doc-tuple))) (print-base) (print-radix) 2 (length-prefix prefix) channel state) (pprogn (cond ((and start-column (>= col start-column)) (let ((length-prefix (length-prefix prefix))) (pprogn (newline channel state) (princ-prefix prefix channel state) (spaces (- start-column length-prefix) length-prefix channel state)))) (t (spaces (if start-column (- start-column col) 2) col channel state))) (print-doc-string-part 0 (cadddr doc-tuple) prefix markup-table char-subst-table fmt-alist channel name nil nil nil state))))) ((= n 1) (pprogn (print-doc-string-part 1 (cadddr doc-tuple) prefix markup-table char-subst-table fmt-alist channel name nil nil nil state) (cond ((caddr doc-tuple) (pprogn (princ-prefix prefix channel state) (newline channel state) (princ-prefix prefix channel state) (princ$ "Subtopics (listed alphabetically):" channel state) (newline channel state) (pstate-global-let* ((more-doc-state (f-get-global 'more-doc-state state))) (print-doc-lst (merge-sort-alpha-< (caddr doc-tuple)) (cons prefix 1) markup-table char-subst-table fmt-alist channel state)) (princ-prefix prefix channel state) (princ$ "[end of subtopics]" channel state) (newline channel state))) (t state)))) (t (pprogn (princ-prefix prefix channel state) (print-doc-string-part 2 (cadddr doc-tuple) prefix markup-table char-subst-table fmt-alist channel name nil nil nil state)))))) (defun print-doc-lst (lst prefix markup-table char-subst-table fmt-alist channel state) (cond ((null lst) state) (t (pprogn (print-doc (car lst) 0 prefix markup-table char-subst-table fmt-alist channel state) (print-doc-lst (cdr lst) prefix markup-table char-subst-table fmt-alist channel state))))) ) ; Now we implement the DWIM feature of doc, which prints out the ; near-misses for an alleged (but erroneous) documentation topic. (defun degree-of-match2 (ch1 ch2 str i maximum) (cond ((< (1+ i) maximum) (if (and (eql ch1 (normalize-char (char str i) nil)) (eql ch2 (normalize-char (char str (1+ i)) nil))) 1 (degree-of-match2 ch1 ch2 str (1+ i) maximum))) (t 0))) (defun degree-of-match1 (pat-lst str maximum) (cond ((null pat-lst) 0) ((null (cdr pat-lst)) 0) (t (+ (degree-of-match2 (car pat-lst) (cadr pat-lst) str 0 maximum) (degree-of-match1 (cdr pat-lst) str maximum))))) (defun degree-of-match (pat-lst str) ; Pat-lst is a normalized string (with hyphen-is-space nil). We ; normalize str similarly and compute the degree of match between ; them. The answer is a rational between 0 and 1. The number is just ; n divided by (length pat)-1, where n is the number of adjacent ; character pairs in pat that occur adjacently in str. This is just ; a Royal Kludge that seems to work. (if (< (length pat-lst) 2) 0 (/ (degree-of-match1 pat-lst str (length str)) (1- (length pat-lst))))) (defun find-likely-near-misses (pat-lst alist) ; Alist is the documentation-alist. Pat-lst is a normalized string ; (with hyphen-is-space nil). We collect the cars of the pairs in ; alist that have a degree of match of more than one half. Again, an ; utter kludge. (cond ((null alist) nil) (t (let ((d (degree-of-match pat-lst (if (stringp (caar alist)) (caar alist) (symbol-name (caar alist)))))) (cond ((<= d 1/2) (find-likely-near-misses pat-lst (cdr alist))) (t (cons (cons d (caar alist)) (find-likely-near-misses pat-lst (cdr alist))))))))) (defun print-doc-dwim (name ctx state) (let ((lst (merge-sort-car-> (find-likely-near-misses (normalize-string (if (stringp name) name (symbol-name name)) nil) (global-val 'documentation-alist (w state)))))) (er soft ctx "There is no documentation for ~x0.~#1~[~/ A similar documented name ~ is ~&2.~/ Similar documented names are ~&2.~]~|~%NOTE: See also ~ :DOC finding-documentation." name (zero-one-or-more (length lst)) (strip-cdrs lst)))) (defun end-doc (channel state) (cond ((f-get-global 'more-doc-state state) (pprogn (princ$ "(type :more for more, :more! for the rest)" channel state) (newline channel state) (value :invisible))) (t (pprogn (princ$ (if (hons-enabledp state) " " ; Boyer preference "*-") channel state) (newline channel state) (value :invisible))))) (defun doc-fn (name state) (cond ((not (symbolp name)) (er soft :doc ":DOC requires a symbol")) (t (io? temporary nil (mv erp val state) (name) (let* ((channel (standard-co state)) (ld-keyword-aliases (ld-keyword-aliases state)) (temp (if (keywordp name) (assoc-eq name ld-keyword-aliases) nil)) (doc-tuple (access-doc-string-database name state))) (cond ((or temp (null doc-tuple)) (let ((temp (cond ((symbolp name) (assoc-eq (intern (symbol-name name) "KEYWORD") ld-keyword-aliases)) ((stringp name) (assoc-eq (intern name "KEYWORD") ld-keyword-aliases)) (t nil)))) (cond ((null temp) (print-doc-dwim name :doc state)) (t (mv-let (col state) (fmt1 "~@0~x1 is ~#2~[a~/an~] ~n3 input alias for ~x4.~%~%" (list (cons #\0 (doc-prefix state)) (cons #\1 (car temp)) (cons #\2 (if (member (cadr temp) '(8 18)) 1 0)) (cons #\3 (cadr temp)) (cons #\4 (caddr temp))) 0 channel state nil) (declare (ignore col)) (cond ((and (symbolp (caddr temp)) (access-doc-string-database (caddr temp) state)) (doc-fn (caddr temp) state)) (t (value :invisible)))))))) (t (pprogn (print-doc doc-tuple 0 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (print-doc doc-tuple 1 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (newline channel state) (end-doc channel state))))))))) (defun more-fn (ln state) (io? temporary nil (mv erp val state) (ln) (let ((more-doc-state (f-get-global 'more-doc-state state)) (channel (standard-co state))) (cond (more-doc-state (pprogn (princ-prefix (car (cddddr more-doc-state)) channel state) (mv-to-state 2 (print-doc-string-part1 (car more-doc-state) (cadr more-doc-state) (caddr more-doc-state) (cadddr more-doc-state) (car (cddddr more-doc-state)) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel "the current item" state ln nil nil)) (end-doc channel state))) (t (end-doc channel state)))))) (defun doc!-fn (name state) (cond ((not (symbolp name)) (er soft :doc! ":DOC! requires a symbol")) (t (io? temporary nil (mv erp val state) (name) (let ((channel (standard-co state)) (doc-tuple (access-doc-string-database name state))) (cond ((null doc-tuple) (print-doc-dwim name :doc state)) (t (pprogn (print-doc doc-tuple 0 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (print-doc doc-tuple 1 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (princ-prefix (doc-prefix state) channel state) (newline channel state) (more-fn t state))))))))) (defmacro more nil ":Doc-Section Documentation your response to ~c[:]~ilc[doc] or ~c[:]~ilc[more]'s ``~c[(type :more...)]''~/ NOTE: The command ~c[:more] only makes sense at the terminal. ~bv[] Example: ACL2 !>:more ~ev[] will continue printing whatever ~il[documentation] was started by ~c[:]~ilc[doc] or ~c[:]~ilc[more-doc].~/ When you type ~c[:doc name], for some documented ~c[name], the system responds by typing the one-liner and the notes sections of the ~il[documentation] for ~c[name]. It then types ``~c[(type :more for more, :more! for the rest)]''. If you then type ~bv[] ACL2 !>:more ~ev[] the system will start to print the details section of ~c[name]. The same thing could be achieved by typing ~c[:more-doc name], but that requires you to type name again. Similarly, if you have typed ~c[:]~ilc[more-doc] name, the system will print the first ``block'' of the details section and then print ``~c[(type :more for more, :more! for the rest)]''. Typing ~c[:more] at that point will cause the next block of the details section to be printed. Eventually ~c[:more] will conclude by printing ``~c[*-]'' which is the indicator that the text has been exhausted. What is a ``block'' of text? ~c[:More] looks for the end of a paragraph (two adjacent newlines) after printing ~c[n] lines. If it doesn't find one before it has printed ~c[k] lines, it just stops there. ~c[N] and ~c[k] here are the values of the two ~il[state] global variables ~c['more-doc-min-lines] and ~c['more-doc-max-lines]. You may use ~ilc[@] and ~ilc[assign] to inspect and set these variables, e.g., ~c[(@ more-doc-max-lines)] will return the current maximum number of lines printed by ~c[:more] and ~c[(assign more-doc-max-lines 19)] will set it to 19. On terminals having only 24 lines, we find min and max settings of 12 and 19 the most pleasant. If you want ~c[:more] to print all of the details instead of feeding them to you one block at a time, type ~c[:]~ilc[more!] instead." '(more-fn 0 state)) (defmacro more! nil ":Doc-Section Documentation another response to ``(type :more for more, :more! for the rest)''~/ NOTE: The command ~c[:more!] only makes sense at the terminal. ~bv[] Example: ACL2 !>:more! ~ev[] will print all of the remaining ~il[documentation] started by the last ~c[:]~ilc[doc] or ~c[:]~ilc[more-doc].~/ ~l[more] for some background. Typing ~c[:more!] will print all remaining blocks of ~il[documentation]. ~c[:More!] is like ~c[:]~ilc[more] except that it prints all the text at once. For example, if you type ~c[:]~ilc[doc] name you will see some text followed by ``~c[(type :more for more, :more! for the rest)]''. If you then type simply ~c[:more!] you will see all of the details, while if you type ~c[:]~ilc[more] you will be fed the next block of details." '(more-fn t state)) (defun print-doc-outline (name prefix markup-table char-subst-table fmt-alist channel state) ; Name is either an atom (in which case it must be a topic in the ; documentation alist) or else is a doc-tuple from the alist. ; This function is sort of like (doc-fn name state) except ; that it just prints the one-liner for name and then the related ; topics, while doc-fn would print the notes section too. (let ((doc-tuple (cond ((atom name) (assoc-equal name (global-val 'documentation-alist (w state)))) (t name)))) (pprogn (print-doc doc-tuple 0 prefix markup-table char-subst-table fmt-alist channel state) (print-doc-lst (merge-sort-alpha-< (caddr doc-tuple)) (cons prefix 1) markup-table char-subst-table fmt-alist channel state) (princ-prefix prefix channel state) (princ$ " See also :MORE-DOC " channel state) (princ$ (car doc-tuple) channel state) (newline channel state)))) (defun print-doc-outline-lst (name-lst prefix markup-table char-subst-table fmt-alist channel state) (cond ((null name-lst) state) (t (pprogn (print-doc-outline (car name-lst) prefix markup-table char-subst-table fmt-alist channel state) (print-doc-outline-lst (cdr name-lst) prefix markup-table char-subst-table fmt-alist channel state))))) (deflabel finding-documentation :doc ":Doc-Section acl2::Miscellaneous searching the documentation~/ The ~c[:]~ilc[doc] and ~c[:]~ilc[doc!] commands will display, at the terminal, ~il[documentation] topics defined in ACL2 or in ~il[books] that have already been included. The ~c[:]~ilc[docs] command allows you to search the ~il[documentation] at the terminal. ~l[docs]. But how can you find documentation for books that are not included in the current ACL2 session? The xdoc manual is a combined manual for the ACL2 sources and the community books. It is built using documentation not only from the ACL2 sources (which is rearranged) but also from the community books (whether included in the current session or not), using an ``xdoc processor'' created by Jared Davis. The ACL2 home page (~url[http://www.cs.utexas.edu/users/moore/acl2/]) has a link to this manual, which as of this writing may be found directly by visiting the following web page: ~url[http://fv.centtech.com/acl2/latest/doc/]. You can build a local copy of this manual from the ACL2 Community Books, following instructions in their ~c[Makefile].~/~/") (deflabel markup :doc ":Doc-Section Documentation the markup language for ACL2 ~il[documentation] strings~/ ACL2 ~il[documentation] strings make special use of the tilde character (~~). In particular, we describe here a ``markup language'' for which the tilde character plays a special role. The markup language is valuable if you want to write ~il[documentation] that is to be displayed outside your ACL2 session. If you are not writing such ~il[documentation], and if also you do not use the character `~~', then there is no need to read on.~/ Three uses of the tilde character (~~) in ~il[documentation] strings are as follows. Below we explain the uses that constitute the ACL2 markup language. ~bq[] ~c[~~/]~nl[] Indicates the end of a documentation ~st[section]; ~pl[doc-string]. ~c[~~~~]~nl[] Indicates the literal insertion of a tilde character (~~). ~c[~~~]]~nl[] This directive in a documentation string is effective only during the processing of part 2, the details (~pl[doc-string]), and controls how much is shown on each round of ~c[:]~ilc[more] processing when printing to the terminal. If the system is not doing ~c[:]~ilc[more] processing, then it acts as though the ~~] is not present. Otherwise, the system put out a newline and halts documentation printing on the present topic, which can be resumed if the user types ~c[:]~ilc[more] at the terminal. ~eq[] The other uses of the tilde character are of the following form. ~bv[] ~~key[arg] ~ev[] Before launching into an explanation of how this works in detail, let us consider some small examples. Here is a word that is code: ~bv[] ~~c[function-name]. ~ev[] Here is a phrase with an ``emphasized'' word, ``not'': ~bv[] Do ~~em[not] do that. ~ev[] Here is the same phrase, but where ``not'' receives stronger emphasis (presumably boldface in a printed version): ~bv[] Do ~~st[not] do that. ~ev[] Here is a passage that is set off as a display, in a fixed-width font: ~bv[] ~~bv[] This passage has been set off as ``verbatim''. The present line starts just after a line break. Normally, printed text is formatted, but inside ~~bv[]...~~ev[], line breaks are taken literally. ~~ev[] ~ev[] In general, the idea is to provide a ``markup language'' that can be reasonably interpreted not only at the terminal (via ~c[:]~ilc[doc]), but also via translators into other languages. In fact, translators have been written into Texinfo and HTML. Let us turn to a more systematic consideration of how to mark text in ~il[documentation] strings using expressions of the form ~c[~~key[arg~]], which we will call ``~il[doc-string] tilde directives.'' The idea is that ~c[key] informs the ~il[documentation] printer (which could be the terminal, a hardcopy printer, or some hypertext tool) about the ``style'' used to display ~c[arg]. The intention is that each such printer should do the best it can. For example, we have seen above that ~c[~~em[arg~]] tells the printer to ~i[emphasize] ~c[arg] if possible, using an appropriate display to indicate emphasis (italics, or perhaps surrounding ~c[arg] with some character like ~c[_], or ...). For another example, the directive for bold font, ~c[~~b[arg~]], says that printed text for ~c[arg] should be in bold if possible, but if there is no bold font available (such as at the terminal), then the argument should be printed in some other reasonable manner (for example, as ordinary text). The ~c[key] part is case-insensitive; for example, you can use ~~BV[] or ~~Bv[] or ~~bV[] in place of ~~bv[]. Every form below may have any string as the argument (inside ~c[[..~]]), as long as it does not contain a newline (more on that below). However, when an argument does not make much sense to us, we show it below as the empty string, e.g., ``~c[~~bv[~]]'' rather than ``~c[~~bv[arg~]]''. ~bv[] ~~-[] Print the equivalent of a dash ~~b[arg] Print the argument in bold font, if available ~~bid[arg] ``Begin implementation dependent'' -- Ignores argument at terminal. ~~bf[] Begin formatted text (respecting spaces and line breaks), but in ordinary font (rather than, say, fixed-width font) if possible ~~bq[] Begin quotation (indented text, if possible) ~~bv[] Begin verbatim (print in fixed-width font, respecting spaces and line breaks) ~~c[arg] Print arg as ``code'', such as in a fixed-width font ~~ef[] End format; balances ~~bf[] ~~eid[arg] ``End implementation dependent'' -- Ignores argument at terminal. ~~em[arg] Emphasize arg, perhaps using italics ~~eq[] End quotation; balances ~~bq[] ~~ev[] End verbatim; balances ~~bv[] ~~i[arg] Print arg in italics font ~~id[arg] ``Implementation dependent'' -- Ignores argument at terminal. ~~il[arg] Print argument as is, but make it a link to another doc topic (for true hypertext environments). Note that the link argument must match the package name for the desired topic unless the package name is ``ACL2''. That is, to link to documentation on ``foo::topic'', one needs to use the full name for ``foo::topic'', including its package name. ~~ilc[arg] Same as ~~il[arg], except that arg should be printed as with ~~c[arg] ~~l[arg] Ordinary link to another doc topic; prints as ``See :DOC arg'' at the terminal (but also see ~~pl below, which puts ``see'' in lower case) ~~nl[] Print a newline ~~par[] Paragraph mark, of no significance at the terminal (can be safely ignored; see also notes below) ~~pl[arg] Parenthetical link (borrowing from Texinfo): same as ~~l[arg], except that ``see'' is in lower case. This is typically used at other than the beginning of a sentence. ~~sc[arg] Print arg in (small, if possible) capital letters ~~st[arg] Strongly emphasize arg, perhaps using a bold font ~~t[arg] Typewriter font; similar to ~~c[arg], but leaves less doubt about the font that will be used. ~~terminal[arg] Terminal only; arg is to be ignored except when reading documentation at the terminal, using :DOC. ~~url[arg] Print arg as HTML hyperlink if possible (else print like ~c[~~c]) ~ev[] ~em[Style notes and further details] It is not a good idea to put ~il[doc-string] tilde directives inside verbatim environments, ~c[~~bv[~] ... ~~ev[~]]. Do not nest ~il[doc-string] tilde directives; that is, do not write ~bv[] The ~~c[~~il[append~]] function ... ~ev[] but note that the ``equivalent'' expression ~bv[] The ~~ilc[append] function ... ~ev[] is fine. The following phrase is also acceptable: ~bf[] ~~bf[]This is ~~em[formatted] text. ~~ef[] ~ef[] because the nesting is only conceptual, not literal. We recommend that for displayed text, ~c[~~bv[~]] and ~c[~~ev[~]] should usually each be on lines by themselves. That way, printed text may be less encumbered with excessive blank lines. Here is an example. ~bf[] Here is some normal text. Now start a display: ~~bv[] 2 + 2 = 4 ~~ev[] And here is the end of that paragraph. Here is the start of the next paragraph. ~ef[] The analogous consideration applies to ~c[~~bf[~]] and ~c[~~ef[~]] as well as ~c[~~bq[~]] and ~c[~~eq[~]]. You may ``quote'' ~il[characters] inside the ~c[arg] part of ~c[~~key[arg~]], by preceding them with ~~. This is, in fact, the only legal way to use a newline character or a right bracket (]) inside the argument to a ~il[doc-string] tilde directive. Write your ~il[documentation] strings without hyphens. Otherwise, you may find your text printed on paper (via TeX, for example) like this ~-[] ~bf[] Here is a hyphe- nated word. ~ef[] even if what you had in mind was: ~bf[] Here is a hyphe- nated word. ~ef[] When you want to use a dash (as opposed to a hyphen), consider using ~~-[], which is intended to be interpreted as a ``dash.'' For example: ~bf[] This sentence ~~-[] which is broken with dashes ~~-[] is boring. ~ef[] would be written to the terminal (using ~c[:]~ilc[doc]) by replacing ~c[~~-[~]] with two hyphen ~il[characters], but would presumably be printed on paper with a dash. Be careful to balance the ``begin'' and ``end'' pairs, such as ~c[~~bv[~]] and ~c[~~ev[~]]. Also, do not use two ``begin'' directives (~c[~~bf[~]], ~c[~~bq[~]], or ~c[~~bv[~]]) without an intervening ``end'' directive. It is permissible (and perhaps this is not surprising) to use the ~il[doc-string] part separator ~c[~~/] while between such a begin-end pair. Because of a bug in texinfo (as of this writing), you may wish to avoid beginning a line with (any number of spaces followed by) the ~ilc[-] character or ~c[~~-[~]]. The ``paragraph'' directive, ~c[~~par[~]], is rarely if ever used. There is a low-level capability, not presently documented, that interprets two successive newlines as though they were ~c[~~par[~]]. This is useful for the HTML driver. For further details, see the authors of ACL2. Emacs code is available for manipulating ~il[documentation] strings that contain ~il[doc-string] tilde-directives (for example, for doing a reasonable job filling such ~il[documentation] strings). See the authors if you are interested. We tend to use ~c[~~em[arg~]] for ``section headers,'' such as ``Style notes and further details'' above. We tend to use ~c[~~st[arg~]] for emphasis of words inside text. This division seems to work well for our Texinfo driver. Note that ~c[~~st[arg~]] causes ~c[arg] to be printed in upper-case at the terminal (using ~c[:]~ilc[doc]), while ~c[~~em[arg~]] causes ~c[arg] to be printed at the terminal as though ~c[arg] were not marked for emphasis. Our Texinfo and HTML drivers both take advantage of capabilities for indicating which ~il[characters] need to be ``escaped,'' and how. Unless you intend to write your own driver, you probably do not need to know more about this issue; otherwise, contact the ACL2 authors. We should probably mention, however, that Texinfo makes the following requirement: when using ~c[~~l[arg~]], where ~c[arg] contains one of the special ~il[characters] ~ilc[@], ~c[{], or ~c[}], you must immediately follow this use with a period or comma. Also, the Emacs ``info'' ~il[documentation] that we generate by using our Texinfo driver has the property that in node names, ~c[:] has been replaced by ~c[|] (because of quirks in info); so for example, the ``~il[proof-checker]'' simplification command, ~c[s], is documented under ~c[acl2-pc||s] rather than under ~c[acl2-pc::s]. We have tried to keep this markup language fairly simple; in particular, there is no way to refer to a link by other than the actual name. So for example, when we want to make ~c[:]~ilc[doc] an invisible link in ``code'' font, we write the following form, which indicates that ~c[:] should be in that font and then ~ilc[doc] should both be in that font and be an invisible link. ~bv[] ~~c[:]~~ilc[doc] ~ev[] ") (deflabel doc-string :doc ":Doc-Section Documentation formatted ~il[documentation] strings~/ ~bv[] Examples: \":Doc-Section name one-liner~~/notes~~/details\" \":Doc-Section name one-liner~~/ notes~~/ details~~/ :cite old-name1 :cited-by old-name2\" ~ev[] Use ~c[(get-doc-string 'name state)] to see other examples. ~il[Documentation] strings not beginning with ``~c[:Doc-Section]'' (case is irrelevant) are ignored. ~l[markup] for how to supply formatting information (such as fonts and displayed text) in ~il[documentation] strings.~/ ACL2 attaches special importance to ~il[documentation] strings beginning with the header ``~c[:Doc-Section]'' (or any variant thereof obtained by changing case). Any ~il[documentation] string that does not begin with such a header is considered unformatted and is ignored. For the rest of this discussion, we use the phrase ``~il[documentation] string'' as though it read ``formatted ~il[documentation] string.'' ~il[Documentation] strings are always processed in the context of some symbol, ~c[name], being defined. (Indeed, if an event defines no symbol, e.g., ~ilc[verify-guards] or ~ilc[in-theory], then it is not permitted to have a formatted ~il[documentation] string.) The string will be associated with name in the ``~il[documentation] database.'' The database is divided into ``sections'' and each section is named by a symbol. Among the sections are ~ilc[events], ~ilc[documentation], ~ilc[history], ~ilc[other], and ~ilc[miscellaneous]. A complete list of the sections may be obtained by typing ~c[:docs *] at the terminal. You can create new sections. The main purpose of sections is simply to partition the large set of names into smaller subsets whose contents can be enumerated separately. The idea is that the user may remember (or recognize) the relevant section name and then read its contents to find interesting items. Within a section are ``~il[documentation] tuples'' which associate with each documented name its ~il[documentation] string and a list of related documented names, called the ``related names'' of the name. When ~c[:]~ilc[doc] prints the ~il[documentation] for name, it always lists the related names. When a formatted ~il[documentation] string is submitted with the defining event of some name, the section name and an initial set of related names are parsed from the string. In addition, the formatted string contains various ``levels'' of detail that are printed out at different times. Finally, it is possible for a string to cause the newly documented name to be added to the related names of any previously documented name. Thus, as new names are introduced they can be grouped with old ones. The general form of an ACL2 formatted ~il[documentation] string is ~bv[] \":DOC-SECTION ~~/ ~~/
~~/ :CITE ... :CITE :CITED-BY ... :CITED-BY \" ~ev[] Before we explain this, let it be noted that ~c[(get-doc-string name state)] will return the ~il[documentation] string associated with ~c[name] in the ~il[documentation] database. You may want to call ~c[get-doc-string] on ~c[']~ilc[pe] and ~c[']~ilc[union-theories] just to see some concrete ~il[documentation] strings. This ~il[documentation] string, which is rather long, is under ~c['doc-string]. A formatted ~il[documentation] string has five parts: the header and section-name (terminating in the first ~c[#\\Newline]), the ~c[], ~c[], and ~c[
] (each terminating in a tilde-slash (``~c[~~/]'') pair), and a citation part. These five parts are parsed into six components. ~c[] is read as the name of a symbol, section-name. ~c[], ~c[], and ~c[
] are arbitrary sequences of ~il[characters] (ignoring initial white space and not including the tilde-slash pairs which terminate them). The ~c[] are read as symbols and assembled into a list called the ``cite'' symbols. The ~c[] are read as symbols and assembled into a list called the ``cited-by'' symbols. See the warning below regarding the hackish nature of our symbol reader. ~c[Section-name] must either be a previously documented symbol or else be ~c[name], the symbol being documented. To open a new section of the database, named ~c[section-name], you should define the logical name section-name (as by ~ilc[deflabel] or any other event; also ~pl[defdoc]) and attach to it a ~il[documentation] string for section section-name. You might wish to print out the ~il[documentation] string we use for some of our section names, e.g., ~c[(get-doc-string 'events state)]. By forcing section names to be documented symbols, we permit sections themselves to have one line descriptions and discussions, presented by the standard ~il[documentation] facilities like the facilities ~c[:]~ilc[doc] and ~c[:]~ilc[more-doc] that may be used at the terminal. Each of the ~c[ni]'s and ~c[mi]'s must be previously documented symbols. Both ~c[] and ~c[
] must be non-empty, i.e., must contain some non-whitespace ~il[characters]. ~c[] may be empty. The ~c[:cite]s and ~c[:cited-by]s pairs may be intermingled and may be separated by either newlines or spaces. The citation part may be empty. When the citation part is empty, the tilde-slash pair terminating the ~c[
] part may be omitted. Thus, the simplest form of a formatted ~il[documentation] string is: ~bv[] \":Doc-Section ~~/~~/
\" ~ev[] Since white space at the front of ~c[], ~c[] and ~c[
] is ignored, we often precede those parts by ~c[#\\Newline]s to make the strings easier to read in our source files. We also typically indent all of the text in the string by starting each line with a few spaces. (The Emacs commands for formatting Lisp get confused if you have arbitrary ~il[characters] on the left margin.) We assume that every line in ~c[], ~c[], and ~c[
] starts with at least as many spaces as ~c[] does, i.e., we assume they are all indented the same amount (or more). Let ~c[d] be the number of spaces separating ~c[] from the ~c[#\\Newline] preceding it. When the various parts are printed, we ``de-indent'' by stripping out the first d spaces following each ~c[#\\Newline]. However, we find that when ~il[documentation] is printed flush against the left margin it is difficult to distinguish the ~il[documentation] text from previous output. We therefore prefix each line we print by a special pad of ~il[characters]. By default, this pad is ``~c[| ]'' so that ~il[documentation] text has a vertical bar running down the left margin. But the pad is just the value of the global variable ~c[doc-prefix] and you may ~ilc[assign] it any string you wish. To add such a string to the database under the symbol ~c[name] we make a new entry in the section-name section of the database. The entry associates ~c[name] with the string and uses the string's cites list as the initial value of the related names field. In addition, we add ~c[name] to the related names field of each of the names listed in the string's cited-by list. We also add ~c[name] to the related names field of its section-name. Observe that the cites list in a string is only the initial value of the related names of the names. Future ~il[documentation] strings may add to it via ~c[:cited-by] or ~c[:Doc-Section]. Indeed, this is generally the case. We discuss this further below. When a brief description of ~c[name] is required (as by ~c[:docs **]), ~c[name] and ~c[] are printed. ~c[] is usually printed starting in column 15 (however ~pl[print-doc-start-column]). Despite its name, ~c[] need not be one line. It usually is one line, however. When you type ~c[:]~ilc[doc] name at the terminal, the first response will be to print ~c[name] and ~c[]. Then ~c[:]~ilc[doc] prints ~c[], if any. Then, if ~c[name] is the name of a section, it prints the ~c[]s for each of its related names. For example, try ~c[:doc events]. If ~c[name] is not a section name but does have some related names, they are merely listed but not explained. Try ~c[:doc theory-functions]. ~c[:more-doc name] prints ~c[
]. Our style is to let each new concept add itself to the related names of old concepts. To do otherwise increases the chances that ~il[documentation] gets outdated because one often forgets to update supposedly complete lists of the relevant topics when new topics are invented. For example, ~c[:doc theory-functions] lists each available theory function. But ~c[get-doc-string] of ~c[']~ilc[theory-functions] just shows a few examples and has an empty cites list. From where do we get the names of the theory functions listed by ~c[:]~ilc[doc]? The answer is that each theory function has its own ~il[documentation] string and those strings each specify ~c[:cited-by] ~il[theory-functions]. See for example ~c[get-doc-string] of ~c[']~ilc[union-theories]. So by the time the entire system is assembled, the related names of ~c[']~ilc[theory-functions] contains all the (documented) theory functions. This makes it easy to add new theory functions without changing the general discussion in ~c[']~ilc[theory-functions]. When an event or ~il[command] form is printed, as by ~c[:]~ilc[pe] or ~c[:]~ilc[pc], that contains a formatted ~il[documentation] string, we do not print the actual ~il[documentation] string (since they are usually large and distracting). Instead we print the string: ~bv[] \"Documentation available via :doc\" ~ev[] inviting you to use ~c[:]~ilc[doc] and ~c[:]~ilc[more-doc] (or ~c[get-doc-string]) if you wish to see the ~il[documentation] at the terminal. ~em[Warning on Reading Symbols from Strings:] When we read a symbol, such as the section-symbol, from a ~il[documentation] string, we use a quick and dirty imitation of the much more powerful CLTL ~c[read] program. In particular, we scan past any whitespace, collect all the ~il[characters] we see until we get to more whitespace or the end of the string, convert the ~il[characters] to upper case, make a string out of them, and ~ilc[intern] that string. Thus, if you typed ~c[\":Doc-Section 123 ...\"] we would read the ~c[123] as the symbol ~c[|123|]. Observe that special ~il[characters], such as parentheses and escape ~il[characters], are not afforded their usual reverence by our hack. Furthermore, the question arises: in which package do we ~ilc[intern] the symbol? The answer is, usually, the package containing the name being defined. I.e., if you are documenting ~c[my-pkg::name] and you attach a ~il[documentation] string that begins ~c[\":Doc-Section: Machines ...\"] then the section-symbol will be ~c[my-pkg::machines]. We recognize two special cases. If the first character read is a colon, we use the ~c[keyword] package. If the first five ~il[characters] read are ~c[acl2::] then we ~il[intern] in the ~c[\"ACL2\"] package. Our own section names, e.g., ~ilc[events], are in the ~c[\"ACL2\"] package. In a related area, when you ask for the ~il[documentation] of a name, e.g., when you type ~c[:doc name] at the terminal, that name is read with the full ACL2 reader, not the hack just described. That name is read into the current package. Thus, if you are operating ~ilc[in-package] ~c[\"MY-PKG\"] and type ~c[:doc events], what is read is ~c[my-pkg::events]. The database may not contain an entry for this symbol. Before reporting that no ~il[documentation] exists, we try ~c[acl2::events]. One last note: ~ilc[defpkg] permits a formatted ~il[documentation] string, which is associated in the database with the name of the package. But the name of the package is a string, not a symbol. It is permitted to access the ~il[documentation] of a string (i.e., package name). But there are no facilities for getting such a ~ilc[stringp] name into the related names of another name nor of making such ~il[stringp] names be section names. That is because we always read symbols from strings and never read strings from strings. I.e., if you did write ~c[\"Doc-Section \\\"MY-PKG\\\" ...\"] it would read in as a weird symbol.~/") (deflabel print-doc-start-column :doc ":Doc-Section Miscellaneous printing the one-liner~/ ~bv[] Examples: (assign print-doc-start-column nil) (assign print-doc-start-column 17) ~ev[]~/ This ~il[state] global variable controls the column in which the ``one-liner'' of a formatted ~il[documentation] string is printed. Generally, when ~c[:]~ilc[doc] is used to print a ~il[documentation] string, the name of the documented concept is printed and then ~c[:]~ilc[doc] tabs over to ~c[print-doc-start-column] and prints the one-liner. If the name extends past the desired column, ~c[:]~ilc[doc] outputs a carriage return and then tabs over to the column. If ~c[print-doc-start-column] is ~c[nil], ~c[:]~ilc[doc] just starts the one-liner two spaces from the end of the name, on the same line. The initial value of ~c[print-doc-start-column] is 15.") (defmacro doc (name) ":Doc-Section Documentation brief ~il[documentation] (type ~c[:doc name])~/ NOTE: The ~c[:doc] command only makes sense at the terminal. Most users will probably access the ACL2 documentation in other ways; ~pl[documentation]. In particular, consider using the xdoc manual at the following location on the web, for topics documented in ACL2 community ~il[books] as well as the ACL2 system (though the latter are rearranged): ~url[http://fv.centtech.com/acl2/latest/doc/] ~bv[] Examples: ACL2 !>:doc DEFTHM ; print documentation of DEFTHM ACL2 !>:doc logical-name ; print documentation of LOGICAL-NAME ACL2 !>:doc \"MY-PKG\" ; print documentation of \"MY-PKG\" Related Topics: :more ; continues last :doc or :more-doc text :more-doc name ; prints more documentation for name :docs ** ; lists all documented symbols :docs \"compil\" ; documented symbols apropos \"compil\" :DOC documentation ; describes how documentation works~/ General Form: ACL2>:doc logical-name ~ev[] where ~ilc[logical-name] is a logical name (~pl[logical-name]) for which you hope there is ~il[documentation]. Chances are there is no ~il[documentation] at the moment, but we are working on adding ~il[documentation] strings for all the user level ACL2 functions. For a general discussion of our treatment of documentation strings, ~pl[documentation]. This is the first cut at online ~il[documentation]. Users can be particularly helpful by sending mail on the inadequacies of the system. Address it just to Moore and put ~il[Documentation] in the subject line. There are several things that trouble me about what I've done here. First, many concepts aren't documented. Ultimately, I'd like to . document (a) every CLTL primitive (e.g., ~ilc[case] and ~ilc[coerce]) and (b) every ACL2 extension (e.g., ~ilc[aref1] and ~c[getprop]). But so far I have focussed on documenting (c) the ACL2 system primitives (e.g., ~ilc[defthm] and what ~ilc[hints] look like). My priorities are (c), then (b), and then (a), following the philosophy that the most unstable features should get online ~il[documentation] in these early releases. Having gotten the basic ~il[documentation] in place, I'll document new things as they are added, and in response to your pleas I'll try to add ~il[documentation] to old things that are widely regarded as important. Second, I worry that the existing ~il[documentation] is unhelpful because it provides too much or too little detail, or it provides the detail too far away from where it is needed. Please be on the lookout for this. Did you get what you needed when you appealed to ~c[:doc] or ~c[:]~ilc[more-doc]? If not, what was it you needed? Would more cross-references ~il[help]? Did you get lost in maze of cross-references?" (list 'doc-fn name 'state)) (defmacro doc! (name) ":Doc-Section Documentation all the ~il[documentation] for a name (type ~c[:doc! name])~/ NOTE: The ~c[:doc!] command only makes sense at the terminal. ~bv[] Examples: ACL2 !>:doc! defthm ACL2 !>:doc! certificate ~ev[]~/ This command is like ~c[:doc name] followed by ~c[:]~ilc[more!]. It prints all the ~il[documentation] of ~c[name]." (list 'doc!-fn name 'state)) (defun more-doc-fn (name state) (cond ((not (symbolp name)) (er soft :more-doc ":MORE-DOC requires a symbol")) (t (io? temporary nil (mv erp val state) (name) (let ((channel (standard-co state)) (doc-tuple (access-doc-string-database name state))) (cond ((null doc-tuple) (print-doc-dwim name :more-doc state)) (t (pprogn (print-doc doc-tuple 2 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (end-doc channel state))))))))) (defmacro more-doc (name) ":Doc-Section Documentation a continuation of the ~c[:]~ilc[doc] ~il[documentation]~/ NOTE: The ~c[:more-doc] command only makes sense at the terminal. ~bv[] Examples: ACL2 !>:more-doc DEFTHM ACL2 !>:more-doc logical-name ~ev[] Often it is assumed in the text provided by ~c[:more-doc] name that you have read the text provided by ~c[:doc name].~/ ~c[:More-doc] just continues spewing out at you the ~il[documentation] string provided with a definition. If the user has done his job, ~c[:]~ilc[doc] will probably remind you of the basics and ~c[:more-doc], if read after ~c[:]~ilc[doc], will address obscure details that are nevertheless worth noting. When ~c[:more-doc] types ``~c[(type :more for more, :more! for the rest)]'' you can get the next block of the continuation by typing ~c[:]~ilc[more] or all of the remaining blocks by typing ~c[:]~ilc[more!]. ~l[more].~/" (list 'more-doc-fn name 'state)) (defun get-doc-section-symbols (alist ans) (cond ((null alist) ans) (t (get-doc-section-symbols (cdr alist) (add-to-set-eq (cadar alist) ans))))) (defun get-docs-apropos1 (pat-lst alist ans) (cond ((null alist) ans) ((string-search pat-lst (cadddr (car alist)) 'hyphen-is-space) (get-docs-apropos1 pat-lst (cdr alist) (cons (car alist) ans))) (t (get-docs-apropos1 pat-lst (cdr alist) ans)))) (defun get-docs-apropos (pat alist) (reverse (get-docs-apropos1 (normalize-string pat t) alist nil))) (defun docs-fn (x state) (io? temporary nil (mv erp val state) (x) (let ((channel (standard-co state))) (cond ((eq x '*) (pprogn (fms "Documentation Sections~%~*0:DOC sect lists the contents of ~ section sect.~%" (list (cons #\0 (list "" "~ ~F*~%" "~ ~F*~%" "~ ~F*~%" (merge-sort-alpha-< (get-doc-section-symbols (global-val 'documentation-alist (w state)) nil))))) channel state nil) (f-put-global 'more-doc-state nil state) (end-doc channel state))) ((eq x '**) (pprogn (print-doc-outline-lst (merge-sort-alpha-< (get-doc-section-symbols (global-val 'documentation-alist (w state)) nil)) (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (f-put-global 'more-doc-state nil state) (end-doc channel state))) ((symbolp x) (doc-fn x state)) ((stringp x) (let ((doc-tuples (get-docs-apropos x (global-val 'documentation-alist (w state))))) (pprogn (fms "Documentation Topics Apropos ~y0~%" (list (cons #\0 x)) channel state nil) (print-doc-lst doc-tuples (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (newline (standard-co state) state) (f-put-global 'more-doc-state nil state) (end-doc channel state)))) (t (er soft :docs "Unrecognized argument, ~x0." x)))))) (defmacro docs (x) ":Doc-Section Documentation available ~il[documentation] topics (by section)~/ NOTE: The ~c[:docs] command only makes sense at the terminal. When the ~c[:docs] command is given a ~ilc[stringp] argument it searches the text produced by ~c[:]~ilc[doc] and ~c[:]~ilc[more-doc] and lists all the documented topics whose text contains the given string. For purposes of this string matching we ignore distinctions of case and the amount and kind (but not presence) of white space. We also treat hyphen as whitespace. However, the following examples show how ~c[:docs] can be used on other than string patterns. ~bv[] Examples: ACL2 !>:docs * ; lists documentation sections ACL2 !>:docs ** ; lists all documented topics within all sections ACL2 !>:docs events ; lists all topics in section EVENTS ACL2 !>:docs \"compil\" ; lists topics ``apropos'' ~ev[]~/ The database of formatted ~il[documentation] strings is structured into sections. Within a section are topics. Each topic has a one-liner, some notes, and some detailed discussions. The ~c[:docs] command provides a view of the entire database. ~c[:docs] takes one argument, as described below: ~bv[] arg effect * list all section headings in the database ** list all section headings and all topics within each section name list all topics in the section named name (where name is some symbol other than * and **). This is always the same as :doc name. pattern list all topics whose :doc or :more-doc text mentions the string pattern. For purposes of this string matching we ignore distinctions of case and the amount and kind (but not presence) of white space. We also treat hyphen as whitespace. ~ev[]~/" (list 'docs-fn x 'state)) (defun print-top-doc-topics (doc-alist channel state) (cond ((endp doc-alist) (newline channel state)) ((eq (car (car doc-alist)) (cadr (car doc-alist))) (pprogn (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ (car (car doc-alist)) channel state) (print-top-doc-topics (cdr doc-alist) channel state))) (t (print-top-doc-topics (cdr doc-alist) channel state)))) (defun help-fn (state) (io? temporary nil (mv erp val state) nil (let ((channel (standard-co state))) (pprogn (princ-prefix (doc-prefix state) channel state) (princ$ (f-get-global 'acl2-version state) channel state) (princ$ " Help. See also :MORE-DOC help." channel state) (newline channel state) ; At one time we printed an outline, and we may choose to do so again some day. ; But for now, we simply print the topics, and a message about them. ; (print-doc-outline-lst '(events documentation history other) ; (doc-prefix state) ; (doc-markup-table state) ; (doc-char-subst-table state) ; (doc-fmt-alist state) ; channel state) (f-put-global 'more-doc-state nil state) (princ-prefix (doc-prefix state) channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ "For information about name, type :DOC name. For an introduction" channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ "to the ACL2 online documentation, type :DOC documentation. For" channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ "release notes, type :DOC release-notes." channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ "Type (a!) to abort to the ACL2 top-level from anywhere." channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ "Type (p!) to pop up one ACL2 LD level from anywhere." channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (princ$ "The top-level topics in the documentatation are:" channel state) (newline channel state) (princ-prefix (doc-prefix state) channel state) (print-top-doc-topics (global-val 'documentation-alist (w state)) channel state) (princ$ "*-" channel state) (newline channel state) (value :invisible))))) (deflabel q :doc ":Doc-Section Other quit ACL2 (type ~c[:q]) ~-[] reenter with ~c[(lp)]~/ ~bv[] Example: ACL2 !>:Q ~ev[]~/ The keyword command ~c[:q] typed at the top-level of the ACL2 loop will terminate the loop and return control to the Common Lisp top-level (or, more precisely, to whatever program invoked ~ilc[lp]). To reenter the ACL2 loop, execute ~c[(acl2::lp)] in Common Lisp. You will be in the same state as you were when you exited with ~c[:q], unless during your stay in Common Lisp you messed the data structures representating the ACL2 ~il[state] (including files, property lists, and single-threaded objects). Unlike all other keyword commands, typing ~c[:q] is not equivalent to invoking the function ~c[q]. There is no function ~c[q].~/") (defmacro help nil ":Doc-Section Documentation brief survey of ACL2 features~/ ~bv[] Example: ACL2 !>:help ~ev[]~/ ~l[lp] for general information about the top-level ~il[command] environment for ACL2." '(help-fn state)) (deflabel logical-name :doc ":Doc-Section Miscellaneous a name created by a logical event~/ ~bv[] Examples: assoc caddr + \"ACL2-USER\" \"arith\" \"project/task-1/arith.lisp\" :here ~ev[]~/ A logical name is either a name introduced by some event, such as ~ilc[defun], ~ilc[defthm], or ~ilc[include-book], or else is the keyword ~c[:here], which refers to the most recent such event. ~l[events]. Every logical name is either a symbol or a string. For the syntactic rules on names, ~pl[name]. The symbols name functions, macros, constants, axioms, theorems, labels, and ~il[theories]. The strings name packages or ~il[books]. We permit the keyword symbol ~c[:here] to be used as a logical name denoting the most recently completed event. The logical name introduced by an ~il[include-book] is the full book name string for the book (~pl[full-book-name]). Thus, under the appropriate setting for the current book directory (~pl[cbd]) the event ~c[(include-book \"arith\")] may introduce the logical name ~bv[] \"/usr/home/smith/project/task-1/arith.lisp\" . ~ev[] Under a different ~ilc[cbd] setting, it may introduce a different logical name, perhaps ~bv[] \"/local/src/acl2/library/arith.lisp\" . ~ev[] It is possible that identical ~ilc[include-book] events forms in a session introduce two different logical names because of the current book directory. A logical name that is a string is either a package name or a book name. If it is not a package name, we support various conventions to interpret it as a book name. If it does not end with the string ~c[\".lisp\"] we extend it appropriately. Then, we search for any book name that has the given logical name as a terminal substring. Suppose ~c[(include-book \"arith\")] is the only ~il[include-book] so far and that ~c[\"/usr/home/smith/project/task-1/arith.lisp\"] is the source file it processed. Then ~c[\"arith\"], ~c[\"arith.lisp\"] and ~c[\"task-1/arith.lisp\"] are all logical names identifying that ~ilc[include-book] event (unless they are package names). Now suppose a second ~c[(include-book \"arith\")] is executed and processes ~c[\"/local/src/acl2/library/arith.lisp\"]. Then ~c[\"arith\"] is no longer a logical name, because it is ambiguous. However, ~c[\"task-1/arith\"] is a logical name for the first ~ilc[include-book] and ~c[\"library/arith\"] is a logical name for the second. Indeed, the first can be named by ~c[\"1/arith\"] and the second by ~c[\"y/arith\"]. Logical names are used primarily in the theory manipulation functions, e.g., ~ilc[universal-theory] and ~ilc[current-theory] with which you may obtain some standard ~il[theories] as of some point in the historical past. The reference points are the introductions of logical names, i.e., the past is determined by the ~il[events] it contains. One might ask, ``Why not discuss the past with the much more flexible language of ~il[command] descriptors?'' (~l[command-descriptor].) The reason is that inside of such ~il[events] as ~ilc[encapsulate] or macro ~il[command]s that expand to ~ilc[progn]s of ~il[events], ~il[command] descriptors provide too coarse a grain. When logical names are used as referents in theory expressions used in ~il[books], one must consider the possibility that the defining event within the book in question becomes redundant by the definition of the name prior to the assumption of the book. ~l[redundant-events].~/") (deflabel command :doc ":Doc-Section Miscellaneous forms you type at the top-level, but...~/ ...the word ``command'' usually refers to a top-level form whose evaluation produces a new logical ~il[world]. ~bv[] Typical commands are: (defun foo (x) (cons x x)) (defthm consp-foo (consp (foo x))) (defrec pair (hd . tl) nil) ~ev[] The first two forms are examples of commands that are in fact primitive ~il[events]. ~l[events]. ~c[Defrec], on the other hand, is a macro that expands into a ~ilc[progn] of several primitive ~il[events]. In general, a ~il[world] extending command generates one or more ~il[events].~/ Both ~il[events] and commands leave landmarks on the ~il[world] that enable us to determine how the given ~il[world] was created from the previous one. Most of your interactions will occur at the command level, i.e., you type commands, you print previous commands, and you undo back through commands. Commands are denoted by command descriptors. ~l[command-descriptor].~/") (deflabel command-descriptor :doc ":Doc-Section Miscellaneous an object describing a particular ~il[command] typed by the user~/ ~bv[] Examples: :max ; the command most recently typed by the user :x ; synonymous with :max (:x -1) ; the command before the most recent one (:x -2) ; the command before that :x-2 ; synonymous with (:x -2) 5 ; the fifth command typed by the user 1 ; the first command typed by the user 0 ; the last command of the system initialization -1 ; the next-to-last initialization command :min ; the first command of the initialization :start ; the last command of the initial ACL2 logical world fn ; the command that introduced the logical name fn (:search (defmacro foo-bar)) ; the first command encountered in a search from :max to ; 0 that either contains defmacro and foo-bar in the ; command form or contains defmacro and foo-bar in some ; event within its block. ~ev[]~/ The recorded ~il[history] of your interactions with the top-level ACL2 ~il[command] loop is marked by the ~il[command]s you typed that changed the logical ~il[world]. Each such ~il[command] generated one or more ~il[events], since the only way for you to change the logical ~il[world] is to execute an event function. ~l[command] and ~pl[events]. We divide ~il[history] into ``~il[command] blocks,'' grouping together each ~il[world] changing ~il[command] and its ~il[events]. A ``~il[command] descriptor'' is an object that can be used to describe a particular ~il[command] in the ~il[history] of the ongoing session. Each ~il[command] is assigned a unique integer called its ``~il[command] number'' which indicates the ~il[command]'s position in the chronological ordering of all of the ~il[command]s ever executed in this session (including those executed to initialize the system). We assign the number 1 to the first ~il[command] you type to ACL2. We assign 2 to the second and so on. The non-positive integers are assigned to ``prehistoric'' ~il[command]s, i.e., the ~il[command]s used to initialize the ACL2 system: 0 is the last ~il[command] of the initialization, -1 is the one before that, etc. The legal ~il[command] descriptors are described below. We use ~c[n] to denote any integer, ~c[sym] to denote any logical name (~pl[logical-name]), and ~c[cd] to denote, recursively, any ~il[command] descriptor. ~bv[] command command descriptor described :max -- the most recently executed command (i.e., the one with the largest command number) :x -- synonymous with :max :x-k -- synonymous with (:x -k), if k is an integer and k>0 :min -- the earliest command (i.e., the one with the smallest command number and hence the first command of the system initialization) :start -- the last command when ACL2 starts up n -- command number n (If n is not in the range :min<=n<=:max, n is replaced by the nearest of :min and :max.) sym -- the command that introduced the logical name sym (cd n) -- the command whose number is n plus the command number of the command described by cd (:search pat cd1 cd2) In this command descriptor, pat must be either an atom or a true list of atoms and cd1 and cd2 must be command descriptors. We search the interval from cd1 through cd2 for the first command that matches pat. Note that if cd1 occurs chronologically after cd2, the search is ``backwards'' through history while if cd1 occurs chronologically before cd2, the search is ``forwards''. A backwards search will find the most recent match; a forward search will find the chronologically earliest match. A command matches pat if either the command form itself or one of the events in the block contains pat (or all of the atoms in pat if pat is a list). (:search pat) the command found by (:search pat :max 0), i.e., the most recent command matching pat that was part of the user's session, not part of the system initialization. ~ev[]") (defun trans-fn (form state) (io? temporary nil (mv erp val state) (form) (let ((wrld (w state)) (channel (standard-co state))) (mv-let (flg val bindings state) (translate1 form :stobjs-out '((:stobjs-out . :stobjs-out)) t ;;; known-stobjs = t (user interface) 'top-level wrld state) (cond ((null flg) (pprogn (fms "~Y01~%=> ~y2~|~%" (list (cons #\0 val) (cons #\1 (term-evisc-tuple nil state)) (cons #\2 (prettyify-stobjs-out (translate-deref :stobjs-out bindings)))) channel state nil) (value :invisible))) (t (er soft 'trans ":Trans has failed. Consider trying :trans! ~ instead; see :DOC trans!."))))))) (defun trans!-fn (form state) (io? temporary nil (mv erp val state) (form) (let ((wrld (w state)) (channel (standard-co state))) (mv-let (flg val bindings state) (translate1 form t nil t ;;; known-stobjs = t (user interface) 'top-level wrld state) (declare (ignore bindings)) (cond ((null flg) (pprogn (fms "~Y01~|~%" (list (cons #\0 val) (cons #\1 (term-evisc-tuple nil state))) channel state nil) (value :invisible))) (t (value :invisible))))))) (defmacro trans (form) ":Doc-Section Other print the macroexpansion of a form~/ ~bv[] Examples: :trans (list a b c) :trans (caddr x) :trans (cond (p q) (r)) ~ev[]~/ This function takes one argument, an alleged term, and translates it, expanding the macros in it completely. Either an error is caused or the formal meaning of the term is printed. We also print the ``output signature'' which indicates how many results are returned and which are single-threaded objects. For example, a term that returns one ordinary object (e.g., an object other than ~ilc[STATE] or a user-defined single-threaded object (~pl[defstobj])) has the output signature ~bv[] => * ~ev[] A term that returns the single-threaded object ~c[STATE] has the output signature ~bv[] => STATE ~ev[] and a term that returns four results might have the output signature ~bv[] => (MV $MEM * * STATE) ~ev[] This signature indicates that the first result is the (user defined) single-threaded object ~c[$MEM], that the next two results are ordinary, and that the last result is ~c[STATE]. ~l[trans!] for a corresponding command that does not enforce restrictions of single-threaded objects. It is sometimes more convenient to use ~ilc[trans1] which is like trans but which only does top-level macroexpansion. For more, ~pl[term].~/" (list 'trans-fn form 'state)) (defmacro trans! (form) ":Doc-Section Other print the macroexpansion of a form without single-threadedness concerns~/ ~bv[] Examples: :trans! (list a b c) :trans! (append x state) ~ev[]~/ ~c[:Trans!] is identical to ~c[:]~ilc[trans], except that unlike ~c[:trans], ~c[:trans!] ignores single-threadedness restrictions. Thus, the second form above is legal for ~c[:trans!]. Also ~pl[trans] and ~pl[trans1].~/" (list 'trans!-fn form 'state)) (defun trans1-fn (form state) (if (and (consp form) (true-listp form) (symbolp (car form)) (getprop (car form) 'macro-body nil 'current-acl2-world (w state))) (macroexpand1 form 'top-level state) (er soft 'top-level "TRANS1 may only be applied to a non-atom form that begins with a ~ symbol with a 'macro-body property."))) (defmacro trans1 (form) ":Doc-Section Other print the one-step macroexpansion of a form~/ ~bv[] Examples: :trans1 (list a b c) :trans1 (caddr x) :trans1 (cond (p q) (r)) ~ev[]~/ This function takes one argument, an alleged term, and expands the top-level macro in it for one step only. Either an error is caused, which happens when the form is not a call of a macro, or the result is printed. Also ~pl[trans], which translates the given form completely.~/" `(trans1-fn ,form state)) (defun tilde-*-props-fn-phrase1 (alist) (cond ((null alist) nil) (t (cons (msg "~y0~|~ ~y1~|" (caar alist) (cdar alist)) (tilde-*-props-fn-phrase1 (cdr alist)))))) (defun tilde-*-props-fn-phrase (alist) (list "none" "~@*" "~@*" "~@*" (tilde-*-props-fn-phrase1 alist))) (defun props-fn (sym state) (cond ((symbolp sym) (io? temporary nil (mv erp val state) (sym) (pprogn (fms "ACL2 Properties of ~y0:~%~*1~%" (list (cons #\0 sym) (cons #\1 (tilde-*-props-fn-phrase (getprops sym 'current-acl2-world (w state))))) (standard-co state) state (ld-evisc-tuple state)) (value :invisible)))) (t (er soft :props "~x0 is not a symbol." sym)))) (defmacro props (sym) ":Doc-Section Other print the ACL2 properties on a symbol~/ ~bv[] Example: :props assoc-eq ~ev[]~/ ~c[Props] takes one argument, a symbol, and prints all of the properties that are on that symbol in the ACL2 ~il[world].~/" (list 'props-fn sym 'state)) (deflabel enter-boot-strap-mode ; WARNING: There is an interface-raw.lisp function of the same name! ; We document this label because the user poking around with :pc will ; come across this name. :doc ":Doc-Section Miscellaneous The first millisecond of the Big Bang~/ ACL2 functions, e.g., ~ilc[if], that show ~c[enter-boot-strap-mode] as their defining ~il[command] are in fact primitives. It is impossible for the system to display defining axioms about these symbols.~/ ~c[Enter-boot-strap-mode] is a Common Lisp function but not an ACL2 function. It magically creates from ~c[nil] an ACL2 property list ~il[world] that lets us start the boot-strapping process. That is, once ~c[enter-boot-strap-mode] has created its ~il[world], it is possible to process the ~ilc[defconst]s, ~ilc[defun]s, and ~ilc[defaxiom]s, necessary to bring up the rest of the system. Before that ~il[world] is created, the attempt by ACL2 even to translate a ~ilc[defun] form, say, would produce an error because ~ilc[defun] is undefined. Several ACL2 functions show ~c[enter-boot-strap-mode] as their defining ~il[command]. Among them are ~ilc[if], ~ilc[cons], ~ilc[car], and ~ilc[cdr]. These functions are characterized by axioms rather than definitional equations ~-[] axioms that in most cases are built into our code and hence do not have any explicit representation among the rules and formulas in the system.") (deflabel exit-boot-strap-mode ; WARNING: There is an interface-raw.lisp function of the same name! ; We document this label because the user poking around with :pc will ; come across this name. :doc ":Doc-Section Miscellaneous the end of pre-history~/ ~c[Exit-boot-strap-mode] is the last step in creating the ACL2 ~il[world] in which the user lives. It has ~il[command] number ~c[0]. ~il[Command]s before it are part of the system initialization and extend all the way back to ~c[:]~ilc[min]. ~il[Command]s after it are those of the user.~/ ~c[Exit-boot-strap-mode] is a Common Lisp function but not an ACL2 function. It is called when every ~ilc[defconst], ~ilc[defun], etc., in our source code has been processed under ACL2 and the ~il[world] is all but complete. ~c[exit-boot-strap-mode] has only one job: to signal the completion of the boot-strapping.") ; We now develop walkabout, an extremely useful tool for exploring (defun walkabout-nth (i x) ; Enumerate the elements of the print representation of the list x, ; from 0. Include the possible dot as an element. ; Example x Example x ; (a b c . d) (a b c d) ; i 0 1 2 3 4 0 1 2 3 ; We fetch the ith element. But how do we return the dot? We ; actually return two values (mv dotp xi). If dotp is true, we're ; really returning the dot. In this case xi is the character #\., ; just in case we want to pretend there was a dot there and try ; to go into it or return it. If dotp is false, then xi is the ; corresponding element of x. (cond ((int= i 0) (cond ((atom x) (mv t #\.)) (t (mv nil (car x))))) ((atom x) (mv nil x)) (t (walkabout-nth (1- i) (cdr x))))) (defun walkabout-ip (i x) ; See the examples above showing how we enumerate the elements of the ; print representation of x. We return t if i is a legal index ; and nil otherwise. (cond ((null x) nil) ((atom x) (or (int= i 0) (int= i 1))) ((int= i 0) t) (t (walkabout-ip (1- i) (cdr x))))) (defun walkabout-huh (state) (pprogn (princ$ "Huh?" *standard-co* state) (newline *standard-co* state) (mv 'continue nil state))) (defun walkabout1 (i x state intern-flg evisc-tuple alt-evisc-tuple) ; X is a list and we are at position i within it. This function ; reads commands from *standard-oi* and moves us around in x. This ; function is inefficient in that it computes the current object, ; xi, from i and x each time. It would be better to maintain the ; current tail of x so nx could be fast. (mv-let (dotp xi) (walkabout-nth i x) (pprogn (mv-let (col state) (fmt1 (if dotp ".~%:" "~y0~|:") (list (cons #\0 xi)) 0 *standard-co* state (if (eq alt-evisc-tuple :none) evisc-tuple alt-evisc-tuple)) (declare (ignore col)) state) (mv-let (signal val state) (mv-let (erp obj state) (state-global-let* ((infixp nil)) (read-object *standard-oi* state)) (cond (erp (mv 'exit nil state)) (t (case (if intern-flg (intern (symbol-name obj) "ACL2") obj) (nx (if (walkabout-ip (1+ i) x) (mv 'continue (1+ i) state) (walkabout-huh state))) (bk (if (= i 0) (walkabout-huh state) (mv 'continue (1- i) state))) (0 (mv 'up nil state)) (pp (mv 'continue-fullp nil state)) (= (mv 'exit xi state)) (q (mv 'exit :invisible state)) (otherwise (cond ((and (integerp obj) (> obj 0)) (cond ((atom xi) (walkabout-huh state)) ((walkabout-ip (1- obj) xi) (walkabout1 (1- obj) xi state intern-flg evisc-tuple :none)) (t (walkabout-huh state)))) ((and (consp obj) (eq (car obj) 'pp)) (mv-let (print-level print-length) (let ((args (cdr obj))) (case-match args ((print-level print-length) (mv print-level print-length)) ((n) (mv n n)) (& (mv :bad nil)))) (cond ((and (or (natp print-level) (null print-level)) (or (natp print-length) (null print-length))) (mv 'continue-fullp (evisc-tuple print-level print-length nil nil) state)) (t (walkabout-huh state))))) ((and (consp obj) (eq (car obj) '=) (consp (cdr obj)) (symbolp (cadr obj)) (null (cddr obj))) (pprogn (f-put-global 'walkabout-alist (cons (cons (cadr obj) xi) (f-get-global 'walkabout-alist state)) state) (mv-let (col state) (fmt1 "(walkabout= ~x0) is~%" (list (cons #\0 (cadr obj))) 0 *standard-co* state (ld-evisc-tuple state)) (declare (ignore col)) (mv 'continue nil state)))) (t (walkabout-huh state)))))))) (cond ((eq signal 'continue) (walkabout1 (or val i) x state intern-flg evisc-tuple :none)) ((eq signal 'up) (mv 'continue nil state)) ((eq signal 'continue-fullp) (walkabout1 i x state intern-flg evisc-tuple val)) (t (mv 'exit val state))))))) (defun walkabout (x state) ":Doc-Section Other explore an ACL2 cons tree~/ By typing ~c[(walkabout x state)] for an ACL2 term ~c[x] whose value is a ~ilc[cons] tree, you can walk around that tree. For example, ACL2 developers often use this utility to explore the ACL2 logical ~il[world].~/ When you issue a ~c[walkabout] command, a summary of commands will be printed before you enter an interactive loop. ~bv[] Commands: 0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q. ~ev[] In the interactive ~c[walkabout] loop, a positive integer n takes you to the nth position, while 0 takes you up a level. The commands ~c[nx] and ~c[bk] take you to the next and previous position, respectively, at the same level. The command ~c[pp] prints the current object in full, while ~c[(pp level length)] hides sub-objects below the indicated level and past the indicated length, if non-~c[nil]; ~pl[evisc-tuple]. The command ~c[(pp n)] abbreviates ~c[(pp n n)], so in particular ~c[(pp nil)] is equivalent to ~c[pp]. The following example illustrates the commands described above. ~bv[] ACL2 !>(walkabout (append '(a (b1 b2 b3)) '(c d e f)) state) Commands: 0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q. (A (B1 B2 B3) C ...) :2 (B1 B2 B3) :3 B3 :0 (B1 B2 B3) :nx C :nx D :0 (A (B1 B2 B3) C ...) :pp (A (B1 B2 B3) C D E F) :(pp 4) (A (B1 B2 B3) C D ...) :(pp 1 4) (A # C D ...) :(pp nil 2) (A (B1 B2 ...) ...) :q ACL2 !> ~ev[] Finally we describe the commands ~c[q], ~c[=], and ~c[(= symb)], where ~c[symb] is a symbol. The command ~c[q] simply causes an exit from the ~c[walkabout] loop. The command ~c[=] also exits, but causes the current object to be printed in full. The command ~c[(= symb)] saves an association of ~c[symb] with the current object, which can be retrieved outside the ~c[walkabout] loop using the macro ~c[walkabout=], as illustrated below. ~bv[] :2 (B1 B2 B3) :(= my-list) (walkabout= MY-LIST) is (B1 B2 B3) :q ACL2 !>(walkabout= MY-LIST) (B1 B2 B3) ACL2 !> ~ev[] Finally, we remark that for trees that are not true-lists, ~c[walkabout] treats the dot as an object that can be ``walked about''. The following example illustrates this point. ~bv[] ACL2 !>(walkabout '(c d e . f) state) Commands: 0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q. (C D E . F) :3 E :nx . :nx F :0 (C D E . F) :4 . :0 (C D E . F) :5 F : ~ev[]~/" (pprogn (fms "Commands:~|0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= ~ symb), and q.~%~%" nil *standard-co* state nil) (mv-let (signal val state) (walkabout1 0 (list x) state (not (equal (current-package state) "ACL2")) (evisc-tuple 2 3 nil nil) :none) (declare (ignore signal)) (value val)))) (defun walkabout=-fn (var state) (cond ((symbolp var) (cdr (assoc-eq var (f-get-global 'walkabout-alist state)))) (t nil))) (defmacro walkabout= (var) `(walkabout=-fn ',var state)) ; Here we develop the code for inspecting the results of using OBDDs. (defun lookup-bddnote (cl-id bddnotes) (cond ((endp bddnotes) nil) ((equal cl-id (access bddnote (car bddnotes) :cl-id)) (car bddnotes)) (t (lookup-bddnote cl-id (cdr bddnotes))))) (defun update-bddnote-with-term (cl-id term bddnotes) (cond ((endp bddnotes) (er hard 'update-bddnote-with-term "Expected to find clause with name ~@0, but did not!" (tilde-@-clause-id-phrase cl-id))) ((equal cl-id (access bddnote (car bddnotes) :cl-id)) (cons (change bddnote (car bddnotes) :term term) (cdr bddnotes))) (t (cons (car bddnotes) (update-bddnote-with-term cl-id term (cdr bddnotes)))))) (defmacro show-bdd (&optional str goal-query-response counterex-query-response term-query-response) ; Not documented below is our use of evisc-tuples that hardwire the print-level ; and print-length, rather than using say the abbrev-evisc-tuple. That seems ; reasonable given the design of show-bdd, which allows printing terms in full ; after the user sees their abbreviated versions. It could add more confusion ; than clarity for us to add such documentation below, but if anyone complains, ; then we should probably do so. ":Doc-Section Bdd inspect failed BDD proof attempts~/ Attempts to use BDDs (~pl[bdd]), using ~c[:]~ilc[bdd] ~il[hints], can fail for various reasons. Sometimes it is useful to explore such failures. To do so, one may simply execute the form ~bv[] (show-bdd) ~ev[] inside the ACL2 loop. The system's response is generally self-explanatory. Perhaps you have already seen ~c[show-bdd] used in some examples (~pl[bdd-introduction] and ~pl[if*]). Here we give some details about ~c[show-bdd].~/ ~c[(Show-bdd)] prints the goal to which the BDD procedure was applied and reports the number of nodes created during the ~il[BDD] computation, followed by additional information depending on whether or not the computation ran to completion or aborted (for reasons explained elsewhere; ~pl[bdd-algorithm]). If the computation did abort, a backtrace is printed that should be useful in understanding where the problem lies. Otherwise, ~c[(show-bdd)] prints out ``falsifying constraints.'' This list of pairs associates ~il[term]s with values and suggests how to construct a binding list for the variables in the conjecture that will falsify the conjecture. It also prints out the ~il[term] that is the result of simplifying the input ~il[term]. In each of these cases, parts of the object may be hidden during printing, in order to avoid creating reams of uninteresting output. If so, the user will be queried about whether he wishes to see the entire object (alist or ~il[term]), which may be quite large. The following responses are legal: ~bq[] ~c[ w] ~-[] Walk around the object with a structure editor ~c[ t] ~-[] Print the object in full ~c[nil] ~-[] Do not print any more of the object ~eq[] ~c[Show-bdd] actually has four optional arguments, probably rarely used. The general form is ~bv[] (show-bdd goal-name goal-ans falsifying-ans term-ans) ~ev[] where ~c[goal-name] is the name of the goal on which the ~c[:]~ilc[bdd] hint was used (or, ~c[nil] if the system should find such a goal), ~c[goal-ans] is the answer to be used in place of the query for whether to print the input goal in full, ~c[falsifying-ans] is the answer to be used in place of the query for whether to print the falsifying constraints in full, and ~c[term-ans] is the answer to be used in place of the query for whether to print the resulting ~il[term] in full." (cond ((not (symbolp goal-query-response)) `(er soft 'show-bdd "The optional second argument of show-bdd must be a symbol, but ~x0 ~ is not." ',goal-query-response)) ((not (symbolp counterex-query-response)) `(er soft 'show-bdd "The optional third argument of show-bdd must be a symbol, but ~x0 ~ is not." ',counterex-query-response)) ((not (symbolp term-query-response)) `(er soft 'show-bdd "The optional fourth argument of show-bdd must be a symbol, but ~x0 ~ is not." ',term-query-response)) (t `(show-bdd-fn ,str ',goal-query-response ',counterex-query-response ',term-query-response state)))) (defun show-bdd-goal (query-response bddnote chan state) (let* ((goal (untranslate (access bddnote bddnote :goal-term) t (w state)))) (pprogn (fms "BDD input term (derived from ~@1):~|" (list (cons #\1 (tilde-@-clause-id-phrase (access bddnote bddnote :cl-id)))) (standard-co state) state nil) (cond (query-response state) (t (fms "~q2~|" (list (cons #\2 goal)) (standard-co state) state (evisc-tuple 5 7 nil nil)))) (cond ((equal goal (eviscerate-simple goal 5 7 nil (table-alist 'evisc-table (w state)) nil)) state) (t (mv-let (erp ans state) (if query-response (let ((query-response (intern (symbol-name query-response) "KEYWORD"))) (value (case query-response (:w :w) (:nil nil) (otherwise t)))) (acl2-query :show-bdd '("Print the goal in full?" :n nil :y t :w :w :? ("Y will print the goal in full. W will put you in a ~ structural display editor that lets you type a ~ positive integer N to dive to the Nth element of the ~ current list, 0 to go up a level, PP to print the ~ current object in full, and Q to quit." :n nil :y t :w :w)) nil state)) (declare (ignore erp)) (cond ((eq ans :w) (mv-let (erp ans state) (walkabout goal state) (declare (ignore erp ans)) state)) (ans (fms "~x0~|" (list (cons #\0 goal)) chan state nil)) (t state)))))))) (defun merge-car-term-order (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((term-order (car (car l1)) (car (car l2))) (cons (car l1) (merge-car-term-order (cdr l1) l2))) (t (cons (car l2) (merge-car-term-order l1 (cdr l2)))))) (defun merge-sort-car-term-order (l) (cond ((null (cdr l)) l) (t (merge-car-term-order (merge-sort-car-term-order (evens l)) (merge-sort-car-term-order (odds l)))))) (defun falsifying-pair-p (term val asst) (cond ((endp asst) nil) ((equal term (caar asst)) (or (and (null val) (equal (cadar asst) *some-non-nil-value*)) (and (null (cadar asst)) (equal val *some-non-nil-value*)) (falsifying-pair-p term val (cdr asst)))) (t nil))) (defun bogus-falsifying-assignment-var (asst) ; Asst is assumed to be sorted by car. (cond ((endp asst) nil) ((falsifying-pair-p (caar asst) (cadar asst) (cdr asst)) (caar asst)) (t (bogus-falsifying-assignment-var (cdr asst))))) (defun show-falsifying-assignment (query-response bddnote chan state) (let ((cst (access bddnote bddnote :cst))) (cond ((cst-tp cst) (fms "There is no falsifying assignment, since ~@0 was proved." (list (cons #\0 (tilde-@-clause-id-phrase (access bddnote bddnote :cl-id)))) chan state nil)) (t (let ((asst (falsifying-assignment cst (access bddnote bddnote :mx-id)))) (pprogn (let ((var (bogus-falsifying-assignment-var (merge-sort-car-term-order asst)))) (cond (var (fms "WARNING: The term ~p0 is assigned both to ~ nil and a non-nil value in the following ~ assignment. This generally occurs because ~ the term is not known to be Boolean. ~ Consider adding appropriate booleanp or ~ boolean-listp hypotheses. See :DOC ~ bdd-introduction." (list (cons #\0 var)) (standard-co state) state (evisc-tuple 5 7 nil nil))) (t state))) (fms "Falsifying constraints:~%" nil chan state nil) (cond (query-response state) (t (fms "~x0~|" (list (cons #\0 asst)) chan state (evisc-tuple 5 (max 7 (length asst)) nil nil)))) (cond ((equal asst (eviscerate-simple asst 5 (max 7 (length asst)) nil (table-alist 'evisc-table (w state)) nil)) state) (t (mv-let (erp ans state) (if query-response (let ((query-response (intern (symbol-name query-response) "KEYWORD"))) (value (case query-response (:w :w) (:nil nil) (otherwise t)))) (acl2-query :show-bdd '("Print the falsifying constraints in full?" :n nil :y t :w :w :? ("Y will print the constraints in full. W will put ~ you in a structural display editor that lets you ~ type a positive integer N to dive to the Nth ~ element of the current list, 0 to go up a level, ~ PP to print the current object in full, and Q to ~ quit." :n nil :y t :w :w)) nil state)) (declare (ignore erp)) (cond ((eq ans :w) (mv-let (erp ans state) (walkabout asst state) (declare (ignore erp ans)) state)) (ans (fms "~x0~|" (list (cons #\0 asst)) chan state nil)) (t state))))))))))) (defun show-bdd-term (query-response bddnote chan state) (let* ((orig-term (access bddnote bddnote :term)) (term (if orig-term orig-term (mv-let (term cst-array) (decode-cst (access bddnote bddnote :cst) (leaf-cst-list-array (access bddnote bddnote :mx-id))) (declare (ignore cst-array)) term)))) (pprogn (cond ((null orig-term) (f-put-global 'bddnotes (update-bddnote-with-term (access bddnote bddnote :cl-id) term (f-get-global 'bddnotes state)) state)) (t state)) (fms "Term obtained from BDD computation on ~@1:~|" (list (cons #\1 (tilde-@-clause-id-phrase (access bddnote bddnote :cl-id)))) (standard-co state) state nil) (cond (query-response state) (t (fms "~x2~|" (list (cons #\2 term)) (standard-co state) state (evisc-tuple 5 7 nil nil)))) (cond ((equal term (eviscerate-simple term 5 7 nil (table-alist 'evisc-table (w state)) nil)) state) (t (mv-let (erp ans state) (if query-response (let ((query-response (intern (symbol-name query-response) "KEYWORD"))) (value (case query-response (:w :w) (:nil nil) (otherwise t)))) (acl2-query :show-bdd '("Print the term in full?" :n nil :y t :w :w :? ("Y will print the term in full. W will put you in a ~ structural display editor that lets you type a ~ positive integer N to dive to the Nth element of the ~ current list, 0 to go up a level, PP to print the ~ current object in full, and Q to quit." :n nil :y t :w :w)) nil state)) (declare (ignore erp)) (cond ((eq ans :w) (mv-let (erp ans state) (walkabout term state) (declare (ignore erp ans)) state)) (ans (fms "~x0~|" (list (cons #\0 term)) chan state nil)) (t state)))))))) (defun tilde-*-substitution-phrase1 (alist is-replaced-by-str evisc-tuple wrld) (cond ((null alist) nil) (t (cons (msg "~P01 ~s2 ~P31" (untranslate (caar alist) nil wrld) evisc-tuple is-replaced-by-str (untranslate (cdar alist) nil wrld)) (tilde-*-substitution-phrase1 (cdr alist) is-replaced-by-str evisc-tuple wrld))))) (defun tilde-*-substitution-phrase (alist is-replaced-by-str evisc-tuple wrld) (list* "" "~@*" "~@* and " "~@*, " (tilde-*-substitution-phrase1 alist is-replaced-by-str evisc-tuple wrld) nil)) (defun show-bdd-backtrace (call-stack cst-array chan state) (cond ((endp call-stack) state) (t (mv-let (term-list cst-array) (decode-cst-lst (strip-cdrs (cdar call-stack)) cst-array) (let ((term (untranslate (caar call-stack) nil (w state))) (alist (pairlis$ (strip-cars (cdar call-stack)) ; Once upon a time we untranslate term-list below, but ; tilde-*-substitution-phrase does an untranslate. term-list))) (pprogn (fms "~X02~| alist: ~*1~|" (list (cons #\0 term) (cons #\1 (tilde-*-substitution-phrase alist ":=" (evisc-tuple 5 (max 7 (length alist)) nil nil) (w state))) (cons #\2 (evisc-tuple 5 7 nil nil))) chan state nil) (show-bdd-backtrace (cdr call-stack) cst-array chan state))))))) (defun show-bdd-fn (str goal-query-response counterex-query-response term-query-response state) (let ((bddnotes (f-get-global 'bddnotes state)) (cl-id (parse-clause-id str)) (separator "==============================~%")) (cond ((and str (null cl-id)) (er soft 'show-bdd "The string ~x0 does not have the syntax of a goal name. See :DOC ~ goal-spec." str)) (t (let ((bddnote (if cl-id ;equivalently, if str (lookup-bddnote cl-id bddnotes) (car bddnotes))) (chan (standard-co state))) (cond ((null bddnote) (er soft 'show-bdd "There is no recent record of applying BDDs~#0~[~/ to ~s1~]." (if str 1 0) (if (eq str t) "Goal" str))) (t (pprogn (show-bdd-goal goal-query-response bddnote chan state) (fms "~@0" (list (cons #\0 separator)) chan state nil) (fms "BDD computation on ~@0 yielded ~x1 nodes.~|~@2" (list (cons #\0 (tilde-@-clause-id-phrase (access bddnote bddnote :cl-id))) (cons #\1 (access bddnote bddnote :mx-id)) (cons #\2 separator)) chan state nil) (cond ((access bddnote bddnote :err-string) (pprogn (fms "BDD computation was aborted on ~@0, and hence there is ~ no falsifying assignment that can be constructed. ~ Here is a backtrace of calls, starting with the ~ top-level call and ending with the one that led to the ~ abort. See :DOC show-bdd.~|" (list (cons #\0 (tilde-@-clause-id-phrase (access bddnote bddnote :cl-id)))) chan state nil) (show-bdd-backtrace (access bddnote bddnote :bdd-call-stack) ; Note that we will probably be building the same array as the one just below ; for show-bdd-term, but that seems a small price to pay for modularity here. (leaf-cst-list-array (access bddnote bddnote :mx-id)) chan state) (value :invisible))) (t (pprogn (show-falsifying-assignment counterex-query-response bddnote chan state) (fms "~@0" (list (cons #\0 separator)) chan state nil) (show-bdd-term term-query-response bddnote chan state) (value :invisible)))))))))))) (defun get-docs (lst) ; Each element of lst is a 5-tuple (name args doc edcls body). We ; return a list in 1:1 correspondence with lst containing the docs ; (each of which is either a stringp or nil). (cond ((null lst) nil) (t (cons (third (car lst)) (get-docs (cdr lst)))))) ; Rockwell Addition: Now when you declare a fn to traffic in the stobj st ; the guard is automatically extended with a (stp st). (defun get-guards2 (edcls targets wrld acc) ; Targets is a subset of (GUARDS TYPES), where we pick up expressions from ; :GUARD and :STOBJS XARGS declarations if GUARDS is in the list and we pick up ; expressions corresponding to TYPE declaraions if TYPES is in the list. ; See get-guards for an example of what edcls looks like. We require that ; edcls contains only valid type declarations, as explained in the comment ; below about translate-declaration-to-guard-var-lst. ; We are careful to preserve the order, except that within a given declaration ; we consider :stobjs as going before :guard. (An example is (defun load-qs ; ...) in community book books/defexec/other-apps/qsort/programs.lisp.) Before ; Version_3.5, Jared Davis sent us the following example, for which guard ; verification failed on the guard of the guard, because the :guard conjuncts ; were unioned into the :type contribution to the guard, leaving a guard of ; (and (natp n) (= (length x) n) (stringp x)). It seems reasonable to ; accumulate the guard conjuncts in the order presented by the user. ; (defun f (x n) ; (declare (xargs :guard (and (stringp x) ; (natp n) ; (= (length x) n))) ; (type string x) ; (ignore x n)) ; t) (cond ((null edcls) (reverse acc)) ((and (eq (caar edcls) 'xargs) (member-eq 'guards targets)) ; We know (from chk-dcl-lst) that (cdar edcls) is a "keyword list" ; and so we can assoc-keyword up it looking for :GUARD. We also know ; that there is at most one :GUARD entry. (let* ((temp1 (assoc-keyword :GUARD (cdar edcls))) (guard-conjuncts (if temp1 (if (and (true-listp (cadr temp1)) (eq (car (cadr temp1)) 'AND)) (or (cdr (cadr temp1)) ; The following (list t) avoids ignoring :guard (and). (list t)) (list (cadr temp1))) nil)) (temp2 (assoc-keyword :STOBJS (cdar edcls))) (stobj-conjuncts (if temp2 (stobj-recognizer-terms (cond ((symbol-listp (cadr temp2)) (cadr temp2)) ((and (cadr temp2) (symbolp (cadr temp2))) (list (cadr temp2))) (t nil)) wrld) nil))) (get-guards2 (cdr edcls) targets wrld (rev-union-equal guard-conjuncts (rev-union-equal stobj-conjuncts acc))))) ((and (eq (caar edcls) 'type) (member-eq 'types targets)) (get-guards2 (cdr edcls) targets wrld ; The call of translate-declaration-to-guard-var-lst below assumes that ; (translate-declaration-to-guard (cadr (car edcls)) 'var wrld) is non-nil. ; This is indeed the case, because edcls is as created by chk-defuns-tuples, ; which leads to a call of chk-dcl-lst to check that the type declarations are ; legal. (rev-union-equal (translate-declaration-to-guard-var-lst (cadr (car edcls)) (cddr (car edcls)) wrld) acc))) (t (get-guards2 (cdr edcls) targets wrld acc)))) (defun get-guards1 (edcls targets wrld) (get-guards2 edcls targets wrld nil)) (defun get-guards (lst split-types-lst split-types-p wrld) ; Warning: see :DOC guard-miscellany for a specification of how conjuncts are ; ordered when forming the guard from :xargs and type declarations. ; Each element of lst is a 5-tuple (name args doc edcls body), where every TYPE ; declaration in edcls is valid (see get-guards2 for an explanation of why that ; is important). We return a list in 1:1 correspondence with lst. Each ; element is the untranslated guard or type expressions extracted from the ; edcls of the corresponding element of lst. A typical value of edcls might be ; '((IGNORE X Y) ; (XARGS :GUARD g1 :MEASURE m1 :HINTS ((id :USE ... :IN-THEORY ...))) ; (TYPE ...) ; (XARGS :GUARD g2 :MEASURE m2)) ; The guard extracted from such an edcls is the conjunction of all the guards ; mentioned. ; We extract only the split-types expressions if split-types-p is true. ; Otherwise, we extract the guard expressions. In both of these cases, the ; result depends on whether or not :split-types was assigned value t in the ; definition for the corresponding member of lst. (cond ((null lst) nil) (t (cons (let ((targets (cond (split-types-p ; We are collecting type declarations for 'split-types-term properties. Thus, ; we only collect these when the user has specified :split-types for a ; definition. (and (car split-types-lst) '(types))) ; Otherwise, we are collecting terms for 'guard properties. We skip type ; declarations when the user has specified :split-types for a definition. ((car split-types-lst) '(guards)) (t '(guards types))))) (conjoin-untranslated-terms (and targets ; optimization (get-guards2 (fourth (car lst)) targets wrld nil)))) (get-guards (cdr lst) (cdr split-types-lst) split-types-p wrld))))) (defun get-guardsp (lst wrld) ; Note that get-guards, above, always returns a list of untranslated terms as ; long as lst and that if a guard is not specified (via either a :GUARD or ; :STOBJS XARG declaration or a TYPE declaration) then *t* is used. But in ; order to default the verify-guards flag in defuns we must be able to decide ; whether no such declaration was specified. That is the role of this ; function. It returns t or nil according to whether at least one of the ; 5-tuples in lst specifies a guard (or stobj) or a type. ; Thus, specification of a type is sufficient for this function to return t, ; even if :split-types t was specified. If that changes, adjust :doc ; set-verify-guards-eagerness accordingly. (cond ((null lst) nil) ((get-guards1 (fourth (car lst)) '(guards types) wrld) t) (t (get-guardsp (cdr lst) wrld)))) (defconst *no-measure* *nil*) (defun get-measures1 (m edcls ctx state) ; A typical edcls is given above, in the comment for get-guards. Note that the ; :MEASURE entry is found in an XARGS declaration. By the check in chk-dcl-lst ; we know there is at most one :MEASURE entry in each XARGS declaration. But ; there may be more than one declaration. If more than one measure is ; specified by this edcls, we'll cause an error. Otherwise, we return the ; measure or the term *no-measure*, which is taken as a signal that no measure ; was specified. ; Our first argument, m, is the measure term found so far, or *no-measure* if ; none has been found. We map down edcls and ensure that each XARGS either ; says nothing about :MEASURE or specifies m. (cond ((null edcls) (value m)) ((eq (caar edcls) 'xargs) (let ((temp (assoc-keyword :MEASURE (cdar edcls)))) (cond ((null temp) (get-measures1 m (cdr edcls) ctx state)) ((equal m *no-measure*) (get-measures1 (cadr temp) (cdr edcls) ctx state)) ((equal m (cadr temp)) (get-measures1 m (cdr edcls) ctx state)) (t (er soft ctx "It is illegal to declare two different ~ measures for the admission of a single ~ function. But you have specified :MEASURE ~ ~x0 and :MEASURE ~x1." m (cadr temp)))))) (t (get-measures1 m (cdr edcls) ctx state)))) (defun get-measures2 (lst ctx state) (cond ((null lst) (value nil)) (t (er-let* ((m (get-measures1 *no-measure* (fourth (car lst)) ctx state)) (rst (get-measures2 (cdr lst) ctx state))) (value (cons m rst)))))) (defun get-measures (symbol-class lst ctx state) ; This function returns a list in 1:1 correspondence with lst containing the ; user's specified :MEASUREs (or *no-measure* if no measure is specified). We ; cause an error if more than one :MEASURE is specified within the edcls of a ; given element of lst. ; If symbol-class is program, we ignore the contents of lst and simply return ; all *no-measure*s. See the comment in chk-acceptable-defuns where ; get-measures is called. (cond ((eq symbol-class :program) (value (make-list (length lst) :initial-element *no-measure*))) (t (get-measures2 lst ctx state)))) (defconst *no-ruler-extenders* :none) (defconst *basic-ruler-extenders* '(mv-list return-last)) (defun get-ruler-extenders1 (r edcls default ctx wrld state) ; This function is analogous to get-measures1, but for obtaining the ; :ruler-extenders xarg. (cond ((null edcls) (value (if (eq r *no-ruler-extenders*) default r))) ((eq (caar edcls) 'xargs) (let ((temp (assoc-keyword :RULER-EXTENDERS (cdar edcls)))) (cond ((null temp) (get-ruler-extenders1 r (cdr edcls) default ctx wrld state)) (t (let ((r0 (cadr temp))) (cond ((eq r *no-ruler-extenders*) (er-let* ((r1 ; If keywords other than :ALL, :BASIC, and :LAMBDAS are supported, then also ; change set-ruler-extenders. (cond ((eq r0 :BASIC) (value *basic-ruler-extenders*)) ((eq r0 :LAMBDAS) (value (cons :lambdas *basic-ruler-extenders*))) ((eq r0 :ALL) (value :ALL)) (t (er-progn (chk-ruler-extenders r0 soft ctx wrld) (value r0)))))) (get-ruler-extenders1 r1 (cdr edcls) default ctx wrld state))) ((equal r r0) (get-ruler-extenders1 r (cdr edcls) default ctx wrld state)) (t (er soft ctx "It is illegal to declare two different ~ ruler-extenders for the admission of a single ~ function. But you have specified ~ :RULER-EXTENDERS ~x0 and :RULER-EXTENDERS ~x1." r r0)))))))) (t (get-ruler-extenders1 r (cdr edcls) default ctx wrld state)))) (defun get-ruler-extenders2 (lst default ctx wrld state) (cond ((null lst) (value nil)) (t (er-let* ((r (get-ruler-extenders1 *no-ruler-extenders* (fourth (car lst)) default ctx wrld state)) (rst (get-ruler-extenders2 (cdr lst) default ctx wrld state))) (value (cons r rst)))))) (defmacro default-ruler-extenders-from-table (alist) `(let ((pair (assoc-eq :ruler-extenders ,alist))) (cond ((null pair) *basic-ruler-extenders*) (t (cdr pair))))) (defun default-ruler-extenders (wrld) ":Doc-Section Miscellaneous the default ~il[ruler-extenders] for ~ilc[defun]'d functions~/ When a ~ilc[defun] is processed and no ~c[:ruler-extenders] ~c[xarg] is supplied, the function ~c[default-ruler-extenders] is used to obtain the current ruler-extenders; ~pl[ruler-extenders]. To find the default ~il[ruler-extenders] of the current ACL2 ~il[world], type ~c[(default-ruler-extenders (w state))].~/ While ~il[events] that change the default ~il[ruler-extenders] are permitted within an ~ilc[encapsulate] or the text of a book, their effects are ~ilc[local] in scope to the duration of the encapsulation or inclusion. ~l[default-defun-mode] for an analogous discussion for defun-modes.~/" (default-ruler-extenders-from-table (table-alist 'acl2-defaults-table wrld))) (defun get-ruler-extenders-lst (symbol-class lst ctx state) ; This function returns a list in 1:1 correspondence with lst containing the ; user's specified :RULER-EXTENDERS (or *no-ruler-extenders* if no ; ruler-extenders is specified). We cause an error if more than one ; :RULER-EXTENDERS is specified within the edcls of a given element of lst. ; If symbol-class is program, we ignore the contents of lst and simply return ; all *no-ruler-extenders. See the comment in chk-acceptable-defuns where ; get-ruler-extenders is called. (cond ((eq symbol-class :program) (value (make-list (length lst) :initial-element *no-ruler-extenders*))) (t (let ((wrld (w state))) (get-ruler-extenders2 lst (default-ruler-extenders wrld) ctx wrld state))))) (defun get-hints1 (edcls) ; A typical edcls might be ; '((IGNORE X Y) ; (XARGS :GUARD g1 :MEASURE m1 :HINTS ((id :USE ... :IN-THEORY ...))) ; (TYPE ...) ; (XARGS :GUARD g2 :MEASURE m2)) ; We find all the :HINTS and append them together. (cond ((null edcls) nil) ((eq (caar edcls) 'xargs) ; We know there is at most one occurrence of :HINTS in this XARGS entry. (let ((temp (assoc-keyword :HINTS (cdar edcls)))) (cond ((null temp) (get-hints1 (cdr edcls))) ((true-listp (cadr temp)) (append (cadr temp) (get-hints1 (cdr edcls)))) (t (er hard 'get-hints "The value of :HINTS must satisfy the predicate ~x0. ~ The value ~x1 is thus illegal. See :DOC hints." 'true-listp (cadr temp)))))) (t (get-hints1 (cdr edcls))))) (defun get-hints (lst) ; Lst is a list of tuples of the form (name args doc edcls body). We ; scan the edcls in each tuple and collect all of the hints together ; into one list of hints. (cond ((null lst) nil) (t (append (get-hints1 (fourth (car lst))) (get-hints (cdr lst)))))) (defun get-guard-hints1 (edcls) ; A typical edcls might be ; '((IGNORE X Y) ; (XARGS :GUARD g1 :MEASURE m1 :GUARD-HINTS ((id :USE ... :IN-THEORY ...))) ; (TYPE ...) ; (XARGS :GUARD g2 :MEASURE m2)) ; We find all the :GUARD-HINTS and append them together. (cond ((null edcls) nil) ((eq (caar edcls) 'xargs) ; We know there is at most one occurrence of :GUARD-HINTS in this ; XARGS entry. (let ((temp (assoc-keyword :GUARD-HINTS (cdar edcls)))) (cond ((null temp) (get-guard-hints1 (cdr edcls))) ((true-listp (cadr temp)) (append (cadr temp) (get-guard-hints1 (cdr edcls)))) (t (er hard 'get-guard-hints "The value of :GUARD-HINTS must satisfy the predicate ~ ~x0. The value ~x1 is thus illegal. See :DOC hints." 'true-listp (cadr temp)))))) (t (get-guard-hints1 (cdr edcls))))) (defun get-guard-hints (lst) ; Lst is a list of tuples of the form (name args doc edcls body). We ; scan the edcls in each tuple and collect all of the guard-hints together ; into one list of hints. (cond ((null lst) nil) (t (append (get-guard-hints1 (fourth (car lst))) (get-guard-hints (cdr lst)))))) #+:non-standard-analysis (defun get-std-hints1 (edcls) ; A typical edcls might be ; '((IGNORE X Y) ; (XARGS :STD-HINTS ((id :USE ... :IN-THEORY ...))) ; (TYPE ...) ; (XARGS :GUARD g2 :MEASURE m2)) ; We find all the :STD-HINTS and append them together. (cond ((null edcls) nil) ((eq (caar edcls) 'xargs) ; We know there is at most one occurrence of :STD-HINTS in this ; XARGS entry. (let ((temp (assoc-keyword :STD-HINTS (cdar edcls)))) (cond ((null temp) (get-std-hints1 (cdr edcls))) ((true-listp (cadr temp)) (append (cadr temp) (get-std-hints1 (cdr edcls)))) (t (er hard 'get-std-hints "The value of :STD-HINTS must satisfy the predicate ~ ~x0. The value ~x1 is thus illegal. See :DOC hints." 'true-listp (cadr temp)))))) (t (get-std-hints1 (cdr edcls))))) #+:non-standard-analysis (defun get-std-hints (lst) ; Lst is a list of tuples of the form (name args doc edcls body). We ; scan the edcls in each tuple and collect all of the std-hints together ; into one list of hints. (cond ((null lst) nil) (t (append (get-std-hints1 (fourth (car lst))) (get-std-hints (cdr lst)))))) (defun get-normalizep (edcls ans ctx state) ; A typical edcls might be ; '((IGNORE X Y) ; (XARGS :GUARD g1 :MEASURE m1 :HINTS ((id :USE ... :IN-THEORY ...))) ; (TYPE ...) ; (XARGS :GUARD g2 :MEASURE m2)) ; We find the first :NORMALIZE, if there is one. But we check that there is ; not more than one. (cond ((null edcls) (value (if (eq ans :absent) t ; default ans))) ((eq (caar edcls) 'xargs) ; We know there is at most one occurrence of :NORMALIZE in this XARGS entry, ; but we are concerned about the possibility of other XARGS entries (from other ; declare forms). Perhaps we should be concerned in other cases too, e.g., ; :HINTS. (let ((temp (assoc-keyword :NORMALIZE (cdar edcls)))) (cond ((null temp) (get-normalizep (cdr edcls) ans ctx state)) ((not (member-eq (cadr temp) '(t nil))) (er soft ctx "The :NORMALIZE keyword specified by XARGS must have value t ~ or nil, but the following has been supplied: ~p0." (cadr temp))) ((eq ans :absent) (get-normalizep (cdr edcls) (cadr temp) ctx state)) (t (er soft ctx "Only one :NORMALIZE keyword may be specified by XARGS."))))) (t (get-normalizep (cdr edcls) ans ctx state)))) (defun get-normalizeps (lst acc ctx state) ; Lst is a list of tuples of the form (name args doc edcls body). We ; scan the edcls in each tuple and collect all of the normalizeps together ; into one list, checking that each is Boolean. (cond ((null lst) (value (reverse acc))) (t (er-let* ((normalizep (get-normalizep (fourth (car lst)) :absent ctx state))) (get-normalizeps (cdr lst) (cons normalizep acc) ctx state))))) (defconst *unspecified-xarg-value* ; Warning: This must be a consp. See comments in functions that use this ; constant. '(unspecified)) (defun get-unambiguous-xargs-flg1/edcls1 (key v edcls event-msg) ; V is the value specified so far for key in the XARGSs of this or previous ; edcls, or else the consp *unspecified-xarg-value* if no value has been ; specified yet. We return an error message if any non-symbol is used for the ; value of key or if a value different from that specified so far is specified. ; Otherwise, we return either *unspecified-xarg-value* or the uniformly agreed ; upon value. Event-msg is a string or message for fmt's tilde-atsign and is ; used only to indicate the event in an error message; for example, it may be ; "DEFUN" to indicate a check for a single definition, or "DEFUN event" or ; "MUTUAL-RECURSION" to indicate a check that is made for an entire clique. (cond ((null edcls) v) ((eq (caar edcls) 'xargs) (let ((temp (assoc-keyword key (cdar edcls)))) (cond ((null temp) (get-unambiguous-xargs-flg1/edcls1 key v (cdr edcls) event-msg)) ((not (symbolp (cadr temp))) (msg "It is illegal to specify ~x0 to be ~x1. The value must be ~ a symbol." key (cadr temp))) ((or (consp v) (eq v (cadr temp))) (get-unambiguous-xargs-flg1/edcls1 key (cadr temp) (cdr edcls) event-msg)) (t (msg "It is illegal to specify ~x0 ~x1 in one place and ~x2 in ~ another within the same ~@3. The functionality controlled ~ by that flag operates on the entire ~@3." key v (cadr temp) event-msg))))) (t (get-unambiguous-xargs-flg1/edcls1 key v (cdr edcls) event-msg)))) (defun get-unambiguous-xargs-flg1/edcls (key v edcls event-msg ctx state) ; This is just a version of get-unambiguous-xargs-flg1/edcls1 that returns an ; error triple. (let ((ans (get-unambiguous-xargs-flg1/edcls1 key v edcls event-msg))) (cond ((or (equal ans *unspecified-xarg-value*) (atom ans)) (value ans)) (t (er soft ctx "~@0" ans))))) (defun get-unambiguous-xargs-flg1 (key lst event-msg ctx state) ; We scan the edcls of lst and either extract a single uniformly agreed upon ; value for key among the XARGS and return that value, or else no value is ; specified and we return the consp *unspecified-xarg-value*, or else two or ; more values are specified and we cause an error. We also cause an error if ; any edcls specifies a non-symbol for the value of key. Thus, if we return a ; symbol it is the uniformly agreed upon value and if we return a consp there ; was no value specified. (cond ((null lst) (value *unspecified-xarg-value*)) (t (er-let* ((v (get-unambiguous-xargs-flg1 key (cdr lst) event-msg ctx state)) (ans (get-unambiguous-xargs-flg1/edcls key v (fourth (car lst)) event-msg ctx state))) (value ans))))) (defun get-unambiguous-xargs-flg (key lst default ctx state) ; Lst is a list of mutually recursive defun tuples of the form (name args doc ; edcls body). We scan the edcls for the settings of the XARGS keyword key. ; If at least one entry specifies a setting, x, and all entries that specify a ; setting specify x, we return x. If no entry specifies a setting, we return ; default. If two or more entries specify different settings, we cause an ; error. ; See also get-unambiguous-xargs-flg-lst for a similar function that instead ; allows a different value for each defun tuple, and returns the list of these ; values instead of a single value. ; We assume every legal value of key is a symbol. If you supply a consp ; default and the default is returned, then no value was specified for key. ; Just to be concrete, suppose key is :mode and default is :logic. The ; user has the opportunity to specify :mode in each element of lst, i.e., he ; may say to make the first fn :logic and the second fn :program. But ; that is nonsense. We have to process the whole clique or none at all. ; Therefore, we have to meld all of his various :mode specs together to come ; up with a setting for the DEFUNS event. This function explores lst and ; either comes up with an unambiguous :mode or else causes an error. (let ((event-msg (if (cdr lst) "MUTUAL-RECURSION" "DEFUN event"))) (er-let* ((x (get-unambiguous-xargs-flg1 key lst event-msg ctx state))) (cond ((consp x) (value default)) (t (value x)))))) (defun get-unambiguous-xargs-flg-lst (key lst default ctx state) ; See get-unambiguous-xargs-flg. Unlike that function, this function allows a ; different value for each defun tuple, and returns the list of these values ; instead of a single value. (cond ((null lst) (value nil)) (t (er-let* ((ans (get-unambiguous-xargs-flg1/edcls key *unspecified-xarg-value* (fourth (car lst)) "DEFUN" ctx state)) (rst (get-unambiguous-xargs-flg-lst key (cdr lst) default ctx state))) (value (cons (if (consp ans) ; ans = *unspecified-xarg-value* default ans) rst)))))) (defun chk-xargs-keywords1 (edcls keywords ctx state) (cond ((null edcls) (value nil)) ((eq (caar edcls) 'xargs) (cond ((null keywords) (er soft ctx "No XARGS declaration is legal in this context.")) ((subsetp-eq (evens (cdar edcls)) keywords) (chk-xargs-keywords1 (cdr edcls) keywords ctx state)) (t (er soft ctx "The only acceptable XARGS keyword~#0~[ in this ~ context is~/s in this context are~] ~&0. Thus, ~ the keyword~#1~[ ~&1 is~/s ~&1 are~] illegal." keywords (set-difference-eq (evens (cdar edcls)) keywords))))) (t (chk-xargs-keywords1 (cdr edcls) keywords ctx state)))) (defun chk-xargs-keywords (lst keywords ctx state) ; Lst is a list of 5-tuples of the form (name args doc edcls body). The ; edcls contain XARGS keyword lists, e.g., a typical edcls might be ; '((IGNORE X Y) ; (XARGS :GUARD g1 :MEASURE m1 :HINTS ((id :USE ... :IN-THEORY ...))) ; (TYPE ...) ; (XARGS :GUARD g2 :MEASURE m2)) ; We check that the only keywords mentioned in the list are those of ; keywords. We once put this check into translate itself, when it ; was producing the edcls. But the keywords allowed by DEFUN are ; different from those allowed by DEFMACRO, and so we've moved this ; check into the specific event file. (cond ((null lst) (value nil)) (t (er-progn (chk-xargs-keywords1 (fourth (car lst)) keywords ctx state) (chk-xargs-keywords (cdr lst) keywords ctx state))))) (defun get-names (lst) (cond ((null lst) nil) (t (cons (caar lst) (get-names (cdr lst)))))) (defun get-bodies (lst) (cond ((null lst) nil) (t (cons (fifth (car lst)) (get-bodies (cdr lst)))))) (mutual-recursion (defun find-nontrivial-rulers (var term) ; Returns a non-empty list of rulers governing an occurrence of var in term, if ; such exists. Otherwise returns :none if var occurs in term and nil if var ; does not occur in term. (cond ((variablep term) (if (eq var term) :none nil)) ((fquotep term) nil) ((eq (ffn-symb term) 'if) (let ((x (find-nontrivial-rulers var (fargn term 2)))) (cond (x (cons (fargn term 1) (if (eq x :none) nil x))) (t (let ((x (find-nontrivial-rulers var (fargn term 3)))) (cond (x (cons (dumb-negate-lit (fargn term 1)) (if (eq x :none) nil x))) (t (find-nontrivial-rulers var (fargn term 1))))))))) (t (find-nontrivial-rulers-lst var (fargs term) nil)))) (defun find-nontrivial-rulers-lst (var termlist flg) (cond ((endp termlist) flg) (t (let ((x (find-nontrivial-rulers var (car termlist)))) (cond ((or (null x) (eq x :none)) (find-nontrivial-rulers-lst var (cdr termlist) (or flg x))) (t x)))))) ) (defun tilde-@-free-vars-phrase (vars term wrld) (declare (xargs :guard (and (symbol-listp vars) (pseudo-termp term) (nvariablep term) (not (fquotep term)) (plist-worldp wrld)))) (cond ((endp vars) "") (t (let ((rulers (find-nontrivial-rulers (car vars) term))) (assert$ rulers ; (car vars) occurs in term, so expect :none if no rulers (cond ((eq rulers :none) (tilde-@-free-vars-phrase (cdr vars) term wrld)) ((null (cdr rulers)) (msg " Note that ~x0 occurs in the context of condition ~ ~x1 from a surrounding IF test." (car vars) (untranslate (car rulers) t wrld))) (t (msg " Note that ~x0 occurs in the following context, ~ i.e., governed by these conditions from ~ surrounding IF tests.~|~% (AND~|~@1" (car vars) (print-indented-list-msg (untranslate-lst rulers t wrld) 3 ")"))))))))) (defun chk-free-vars (name formals term loc-str ctx state) (declare (xargs :guard (and (symbol-listp formals) (pseudo-termp term)))) (cond ((subsetp (all-vars term) formals) (value nil)) ((variablep term) (er soft ctx "The ~@0 ~x1 is a free variable occurrence." loc-str name)) (t (assert$ (not (fquotep term)) (let ((vars (set-difference-eq (all-vars term) formals))) (er soft ctx "The ~@0 ~x1 contains ~#2~[a free occurrence of the ~ variable symbol~/free occurrences of the variable ~ symbols~] ~&2.~@3" loc-str name (set-difference-eq vars formals) (tilde-@-free-vars-phrase vars term (w state)))))))) (defun chk-declared-ignores (name ignores term loc-str ctx state) (declare (xargs :guard (and (symbol-listp ignores) (pseudo-termp term)))) (cond ((intersectp-eq (all-vars term) ignores) (er soft ctx "The ~@0 ~x1 uses the variable symbol~#2~[~/s~] ~&2, ~ contrary to the declaration that ~#2~[it is~/they are~] ~ IGNOREd." loc-str name (intersection-eq (all-vars term) ignores))) (t (value nil)))) (defun chk-free-and-ignored-vars (name formals guard split-types-term measure ignores ignorables body ctx state) (er-progn (chk-free-vars name formals guard "guard for" ctx state) (chk-free-vars name formals split-types-term "split-types expression for" ctx state) (chk-free-vars name formals measure "measure supplied with" ctx state) (chk-free-vars name formals (cons 'list ignores) "list of variables declared IGNOREd in" ctx state) (chk-free-vars name formals (cons 'list ignorables) "list of variables declared IGNORABLE in" ctx state) (chk-free-vars name formals body "body of" ctx state) ; Once upon a time we considered a variable used if it occurred in the ; guard or the measure of a function. Thus, we signaled an error ; if it was declared ignored but used in the guard or measure. ; Now we don't. Why? Because this meant that one was not allowed to ; declare ignored a variable used only in (say) the guard. But when ; the defun is compiled by Allegro, it would complain that the variable ; should have been declared ignored. We simply are not free to give ; semantics to IGNORE. CLTL does that and it only cares about the ; body. (chk-declared-ignores name ignores body "body of" ctx state) (let* ((ignore-ok (cdr (assoc-eq :ignore-ok (table-alist 'acl2-defaults-table (w state))))) (undeclared-ignores ; first conjunct is an optimization (cond ((or (eq ignore-ok t) (and (not (eq ignore-ok nil)) (warning-disabled-p "Ignored-variables"))) nil) (t (set-difference-eq formals (union-eq (all-vars body) (union-eq ignorables ignores))))))) (cond ((and undeclared-ignores (eq ignore-ok nil)) (er soft ctx "The formal variable~#0~[ ~&0 is~/s ~&0 are~] not used in the ~ definition of ~x1 but ~#0~[is~/are~] not DECLAREd IGNOREd or ~ IGNORABLE. Any formal variable not used in the body of a ~ definition must be so declared. To remove this requirement, ~ see :DOC set-ignore-ok." undeclared-ignores name)) (undeclared-ignores ; :warn (pprogn (warning$ ctx ("Ignored-variables") "The formal variable~#0~[ ~&0 is~/s ~&0 are~] not used ~ in the definition of ~x1 but ~#0~[is~/are~] not ~ DECLAREd IGNOREd or IGNORABLE. See :DOC set-ignore-ok ~ for how to either remove this warning or to enforce it ~ by causing an error." undeclared-ignores name) (value nil))) (t (value nil)))))) (defun chk-free-and-ignored-vars-lsts (names arglists guards split-types-terms measures ignores ignorables bodies ctx state) ; This function does all of the defun checking related to the use of free vars ; and ignored/ignorable vars. We package it all up here to simplify the ; appearance (and post-macro-expansion size) of the caller, ; chk-acceptable-defuns. The first 6 args are in 1:1 correspondence. (declare (xargs :guard (and (symbol-listp names) (symbol-list-listp arglists) (pseudo-term-listp guards) (pseudo-term-listp split-types-terms) (pseudo-term-listp measures) (pseudo-term-listp bodies) (symbol-list-listp ignores) (symbol-list-listp ignorables)))) (cond ((null names) (value nil)) (t (er-progn (chk-free-and-ignored-vars (car names) (car arglists) (car guards) (car split-types-terms) (car measures) (car ignores) (car ignorables) (car bodies) ctx state) (chk-free-and-ignored-vars-lsts (cdr names) (cdr arglists) (cdr guards) (cdr split-types-terms) (cdr measures) (cdr ignores) (cdr ignorables) (cdr bodies) ctx state))))) (defun putprop-x-lst1 (symbols key value wrld) ; For every sym in symbols, (putprop sym key value). (cond ((null symbols) wrld) (t (putprop-x-lst1 (cdr symbols) key value (putprop (car symbols) key value wrld))))) (defun putprop-x-lst2 (symbols key vals wrld) ; For corresponding symi,vali pairs in symbols x vals, ; (putprop symi key vali). (cond ((null symbols) wrld) (t (putprop-x-lst2 (cdr symbols) key (cdr vals) (putprop (car symbols) key (car vals) wrld))))) (defun putprop-x-lst2-unless (symbols key vals exception wrld) ; For corresponding symi,vali pairs in symbols x vals, (putprop symi ; key vali), unless vali is exception, in which case we do nothing. (cond ((null symbols) wrld) (t (putprop-x-lst2-unless (cdr symbols) key (cdr vals) exception (putprop-unless (car symbols) key (car vals) exception wrld))))) (defun@par translate-term-lst (terms stobjs-out logic-modep known-stobjs-lst ctx wrld state) ; WARNING: Keep this in sync with translate-measures. ; This function translates each of the terms in terms and returns the ; list of translations or causes an error. It uses the given ; stobjs-out and logic-modep on each term. As it maps over terms it ; maps over known-stobjs-lst and uses the corresponding element for ; the known-stobjs of each translation. However, if known-stobjs-lst ; is t it uses t for each. Note the difference between the treatment ; of stobjs-out and logic-modep, on the one hand, and known-stobjs-lst ; on the other. The former are ``fixed'' in the sense that the same ; setting is used for EACH term in terms, whereas the latter allows a ; different setting for each term in terms. ; Call this function with stobjs-out t if you want ; merely the logical meaning of the terms. Call this function with ; stobjs-out '(nil state nil), for example, if you want to ensure that ; each term has the output signature given. (cond ((null terms) (value@par nil)) (t (er-let*@par ((term (translate@par (car terms) stobjs-out logic-modep (if (eq known-stobjs-lst t) t (car known-stobjs-lst)) ctx wrld state)) (rst (translate-term-lst@par (cdr terms) stobjs-out logic-modep (if (eq known-stobjs-lst t) t (cdr known-stobjs-lst)) ctx wrld state))) (value@par (cons term rst)))))) ; We now turn to the major question of translating user typed hints into ; their internal form. We combine this translation process with the ; checking that ensures that the hints are legal. While our immediate ; interest is in the hints for defuns, we in fact handle all the hints ; supported by the system. ; Defthm takes a keyword argument, :HINTS, whose expected value is a ; "hints" of the form ((str1 . hints1) ... (strn . hintsn)) where ; each stri is a string that parses to a clause-id and each hintsi is ; a keyword/value list of the form :key1 val1 ... :keyk valk, where a ; typical :keyi might be :USE, :DO-NOT-INDUCT, :IN-THEORY, etc. Thus, ; a typical defthm event might be: ; (defthm foo (equal x x) ; :hints (("Goal''" :use assoc-of-append :in-theory *bar*))) ; Defun, the other event most commonly given hints, does not have room ; in its arg list for :HINTS since defun is a CLTL primitive. So we have ; implemented the notion of the XARGS of DEFUN and permit it to have as its ; value a keyword/value list exactly like a keyword/value list in macro ; calls. Thus, to pass the hint above into a defun event you would write ; (defun foo (x) ; (declare (xargs :hints (("Goal''" :use assoc-of-append :in-theory *bar*)))) ; body) ; Making matters somewhat more complicated are the facts that defuns may ; take more than one defun tuple, i.e., one might be defining a clique of ; functions ; (defuns ; (fn1 (x) (DECLARE ...) ... body1) ; ... ; (fnn (x) (DECLARE ...) ... bodyn)) ; and each such tuple may have zero or more DECLARE forms (or, in ; general, arbitrary forms which macroexpand into DECLARE forms). ; Each of those DECLAREs may have zero or more XARGS and we somehow ; have to extract a single list of hints from them collectively. What ; we do is just concatenate the hints from each DECLARE form. Thus, ; it is possible that fn1 will say to use hint settings hs1 on ; "Goal''" and fn2 will say to use hs2 on it. Because we concatenate ; in the presented order, the clause-id's specified by fn1 have the ; first shot. ; The basic function we define below is translate-hints which takes a ; list of the alleged form ((str1 . hint-settings1) ...) and ; translates the strings and processes the keyword/value pairs ; appropriately. ; Just for the record, the complete list of hint keywords that might ; be used in a given hint-settings may be found in *hint-keywords*. ; For each hint keyword, :x, we have a function, ; translate-x-hint-value, that checks the form. Each of these ; functions gets as its arg argument the object that was supplied as ; the value of the keyword. We cause an error or return a translated ; value. Of course, "translate" here means more than just apply the ; function translate; it means "convert to internal form", e.g., ; in-theory hints are evaluated into theories. (defun find-named-lemma (sym lst top-level) ; Sym is a symbol and lst is a list of lemmas, and top-level is initially t. ; We return a lemma in lst whose rune has base-symbol sym, if such a lemma is ; unique and top-level is t. Otherwise we return nil, except we return ; :several if top-level is nil. (cond ((null lst) nil) ((equal sym (base-symbol (access rewrite-rule (car lst) :rune))) (cond ((and top-level (null (find-named-lemma sym (cdr lst) nil))) (car lst)) (top-level nil) (t :several))) (t (find-named-lemma sym (cdr lst) top-level)))) (defun find-runed-lemma (rune lst) ; Lst must be a list of lemmas. We find the first one with :rune rune (but we ; make no assumptions on the form of rune). (cond ((null lst) nil) ((equal rune (access rewrite-rule (car lst) :rune)) (car lst)) (t (find-runed-lemma rune (cdr lst))))) (mutual-recursion (defun free-varsp-member (term vars) ; Like free-varsp, but takes a list of variables instead of an alist. (cond ((variablep term) (not (member-eq term vars))) ((fquotep term) nil) (t (free-varsp-member-lst (fargs term) vars)))) (defun free-varsp-member-lst (args vars) (cond ((null args) nil) (t (or (free-varsp-member (car args) vars) (free-varsp-member-lst (cdr args) vars))))) ) (defun@par translate-expand-term1 (name form free-vars ctx wrld state) ; Returns an error triple (mv erp val state) where if erp is not nil, then val ; is an expand-hint determined by the given rune and alist. (cond ((not (arglistp free-vars)) (er@par soft ctx "The use of :FREE in :expand hints should be of the form (:FREE ~ var-list x), where var-list is a list of distinct variables, unlike ~ ~x0." free-vars)) (t (er-let*@par ((term (translate@par form t t t ctx wrld state))) (cond ((or (variablep term) (fquotep term)) (er@par soft ctx "The term ~x0 is not expandable. See the :expand discussion in :DOC ~ hints." form)) ((flambda-applicationp term) (cond (name (er@par soft ctx "An :expand hint may only specify :WITH for an expression ~ that is the application of a function, unlike ~x0." form)) (t (value@par (make expand-hint :pattern term :alist (if (null free-vars) :none (let ((bound-vars (set-difference-eq (all-vars term) free-vars))) (pairlis$ bound-vars bound-vars))) :rune nil :equiv 'equal :hyp nil :lhs term :rhs (subcor-var (lambda-formals (ffn-symb term)) (fargs term) (lambda-body (ffn-symb term)))))))) (t (mv-let (er-msg rune equiv hyp lhs rhs) (cond (name (let* ((fn (ffn-symb term)) (lemmas (getprop fn 'lemmas nil 'current-acl2-world wrld)) (lemma (cond ((symbolp name) (find-named-lemma (deref-macro-name name (macro-aliases wrld)) lemmas t)) (t (find-runed-lemma name lemmas))))) (cond (lemma (let* ((hyps (access rewrite-rule lemma :hyps)) (lhs (access rewrite-rule lemma :lhs)) (lhs-vars (all-vars lhs)) (rhs (access rewrite-rule lemma :rhs))) (cond ((or (free-varsp-member-lst hyps lhs-vars) (free-varsp-member rhs lhs-vars)) (mv (msg "The ~@0 of a rule given to :with in an :expand ~ hint must not contain free variables that are not ~ among the variables on its left-hand side. The ~ ~#1~[variable ~&1 violates~/variables ~&1 ~ violate~] this requirement." (if (free-varsp-member rhs lhs-vars) "left-hand side" "hypotheses") (if (free-varsp-member rhs lhs-vars) (set-difference-eq (all-vars rhs) lhs-vars) (set-difference-eq (all-vars1-lst hyps nil) lhs-vars))) nil nil nil nil nil)) (t (mv nil (access rewrite-rule lemma :rune) (access rewrite-rule lemma :equiv) (and hyps (conjoin hyps)) lhs rhs))))) (t (mv (msg "Unable to find a lemma for :expand hint (:WITH ~x0 ~ ...)." name) nil nil nil nil nil))))) (t (let ((def-body (def-body (ffn-symb term) wrld))) (cond (def-body (let ((formals (access def-body def-body :formals))) (mv nil (access def-body def-body :rune) 'equal (access def-body def-body :hyp) (cons-term (ffn-symb term) formals) (access def-body def-body :concl)))) (t (mv (msg "The :expand hint for ~x0 is illegal, because ~x1 ~ is not a defined function." form (ffn-symb term)) nil nil nil nil nil)))))) ; We could do an extra check that the lemma has some chance of applying. This ; would involve a call of (one-way-unify lhs term) unless :free was specified, ; in which case we would need to call a full unification routine. That doesn't ; seem worth the trouble merely for early user feedback. (cond (er-msg (er@par soft ctx "~@0" er-msg)) (t (value@par (make expand-hint :pattern term :alist (if (null free-vars) :none (let ((bound-vars (set-difference-eq (all-vars term) free-vars))) (pairlis$ bound-vars bound-vars))) :rune rune :equiv equiv :hyp hyp :lhs lhs :rhs rhs))))))))))) (defun@par translate-expand-term (x ctx wrld state) ; X is a "term" given to an expand hint, which can be a term or the result of ; prepending (:free vars) or (:with name-or-rune), or both, to a term. We ; return (mv erp expand-hint state). (case-match x (':lambdas (value@par x)) ((':free vars (':with name form)) (translate-expand-term1@par name form vars ctx wrld state)) ((':with name (':free vars form)) (translate-expand-term1@par name form vars ctx wrld state)) ((':with name form) (translate-expand-term1@par name form nil ctx wrld state)) ((':free vars form) (translate-expand-term1@par nil form vars ctx wrld state)) (& (cond ((or (atom x) (keywordp (car x))) (er@par soft ctx "An :expand hint must either be a term, or of one of the forms ~ (:free vars term) or (:with name term), or a combination of ~ the two forms. The form ~x0 is thus illegal for an :expand ~ hint. See :DOC hints." x)) (t (translate-expand-term1@par nil x nil ctx wrld state)))))) (defun@par translate-expand-hint1 (arg acc ctx wrld state) (cond ((atom arg) (cond ((null arg) (value@par (reverse acc))) (t (er@par soft ctx "The value of the :expand hint must be a true list, but your ~ list ends in ~x0. See :DOC hints." arg)))) (t (er-let*@par ((xtrans (translate-expand-term@par (car arg) ctx wrld state))) (translate-expand-hint1@par (cdr arg) (cons xtrans acc) ctx wrld state))))) (defun@par translate-expand-hint (arg ctx wrld state) ; Arg is whatever the user typed after the :expand keyword. We ; allow it to be either a term or a list of terms. For example, ; all of the following are legal: ; :expand (append a b) ; :expand ((append a b)) ; :expand (:with append (append a b)) ; :expand ((:with append (append a b))) ; :expand ((:free (a) (append a b))) ; :expand (:with append (:free (a) (append a b))) ; :expand ((:with append (:free (a) (append a b)))) ; Here we allow a general notion of "term" that includes expressions of the ; form (:free (var1 ... varn) term), indicating that the indicated variables ; are instantiatable in term, and (:with rd term), where rd is a runic ; designator (see :doc theories). We also interpret :lambdas specially, to ; represent the user's desire that all lambda applications be expanded. (cond ((eq arg :lambdas) (translate-expand-hint1@par (list arg) nil ctx wrld state)) ((atom arg) ; Arg had better be nil, otherwise we'll cause an error. (translate-expand-hint1@par arg nil ctx wrld state)) ((and (consp arg) (symbolp (car arg)) (not (eq (car arg) :lambdas))) ; In this case, arg is of the form (sym ...). Now if arg were really ; intended as a list of terms to expand, the user would be asking us ; to expand the symbol sym, which doesn't make sense, and so we'd ; cause an error in translate-expand-hint1 above. So we will treat ; this arg as a term. (translate-expand-hint1@par (list arg) nil ctx wrld state)) ((and (consp arg) (consp (car arg)) (eq (caar arg) 'lambda)) ; In this case, arg is of the form ((lambda ...) ...). If arg were ; really intended as a list of terms, then the first object on the ; list is illegal and would cause an error because lambda is not a ; function symbol. So we will treat arg as a single term. (translate-expand-hint1@par (list arg) nil ctx wrld state)) (t ; Otherwise, arg is treated as a list of terms. (translate-expand-hint1@par arg nil ctx wrld state)))) (defun cons-all-to-lst (new-members lst) (cond ((null new-members) nil) (t (cons (cons (car new-members) lst) (cons-all-to-lst (cdr new-members) lst))))) (defun@par translate-substitution (substn ctx wrld state) ; Note: This function deals with variable substitutions. For ; functional substitutions, use translate-functional-substitution. ; Substn is alleged to be a substitution from variables to terms. ; We know it is a true list! We check that each element is of the ; the form (v term) where v is a variable symbol and term is a term. ; We also check that no v is bound twice. If things check out we ; return an alist in which each pair is of the form (v . term'), where ; term' is the translation of term. Otherwise, we cause an error. (cond ((null substn) (value@par nil)) ((not (and (true-listp (car substn)) (= (length (car substn)) 2))) (er@par soft ctx "Each element of a substitution must be a pair of the form (var term), ~ where var is a variable symbol and term is a term. Your alleged ~ substitution contains the element ~x0, which is not of this form. See ~ the discussion of :instance in :MORE-DOC lemma-instance." (car substn))) (t (let ((var (caar substn)) (term (cadar substn))) (cond ((not (legal-variablep var)) (mv-let (x str) (find-first-bad-arg (list var)) (declare (ignore x)) (er@par soft ctx "It is illegal to substitute for the non-variable ~x0. ~ It fails to be a variable because ~@1. See :DOC name ~ and see :DOC lemma-instance, in particular the ~ discussion of :instance." var (or str "LEGAL-VARIABLEP says so, but FIND-FIRST-BAD-ARG ~ can't see why")))) (t (er-let*@par ((term (translate@par term t t t ctx wrld state)) ; known-stobjs = t (stobjs-out = t) (y (translate-substitution@par (cdr substn) ctx wrld state))) (cond ((assoc-eq var y) (er@par soft ctx "It is illegal to bind ~x0 twice in a substitution. ~ See the discussion of :instance in :MORE-DOC ~ lemma-instance." var)) (t (value@par (cons (cons var term) y))))))))))) (defun@par translate-substitution-lst (substn-lst ctx wrld state) (cond ((null substn-lst) (value@par nil)) (t (er-let*@par ((tsubstn (translate-substitution@par (car substn-lst) ctx wrld state)) (rst (translate-substitution-lst@par (cdr substn-lst) ctx wrld state))) (value@par (cons tsubstn rst)))))) (defun get-rewrite-and-defn-runes-from-runic-mapping-pairs (pairs) (cond ((null pairs) nil) ((member-eq (cadr (car pairs)) '(:rewrite :definition)) (cons (cdr (car pairs)) (get-rewrite-and-defn-runes-from-runic-mapping-pairs (cdr pairs)))) (t (get-rewrite-and-defn-runes-from-runic-mapping-pairs (cdr pairs))))) (defun@par translate-restrict-hint (arg ctx wrld state) ; Arg is whatever the user typed after the :restrict keyword. (cond ((atom arg) (cond ((null arg) (value@par nil)) (t (er@par soft ctx "The value of the :RESTRICT hint must be an alistp (association ~ list), and hence a true list, but your list ends in ~x0. See :DOC ~ hints." arg)))) ((not (and (true-listp (car arg)) (cdr (car arg)))) (er@par soft ctx "Each member of a :RESTRICT hint must be a true list associating a name ~ with at least one substitution, but the member ~x0 of your hint ~ violates this requirement. See :DOC hints." (car arg))) ((not (or (symbolp (caar arg)) (and (runep (caar arg) wrld) (member-eq (car (caar arg)) '(:rewrite :definition))))) (er@par soft ctx "Each member of a :RESTRICT hint must be a true list whose first ~ element is either a symbol or a :rewrite or :definition rune in the ~ current ACL2 world. The member ~x0 of your hint violates this ~ requirement." (car arg))) (t (let ((runes (if (symbolp (caar arg)) (get-rewrite-and-defn-runes-from-runic-mapping-pairs (getprop (caar arg) 'runic-mapping-pairs nil 'current-acl2-world wrld)) (list (caar arg))))) (cond ((null runes) (er@par soft ctx "The name ~x0 does not correspond to any :rewrite or :definition ~ runes, so the element ~x1 of your :RESTRICT hint is not valid. ~ See :DOC hints." (caar arg) (car arg))) (t (er-let*@par ((subst-lst (translate-substitution-lst@par (cdr (car arg)) ctx wrld state)) (rst (translate-restrict-hint@par (cdr arg) ctx wrld state))) (value@par (append (cons-all-to-lst runes subst-lst) rst))))))))) (defconst *do-not-processes* '(generalize preprocess simplify eliminate-destructors fertilize eliminate-irrelevance)) (defun coerce-to-process-name-lst (lst) (declare (xargs :guard (symbol-listp lst))) (if lst (cons (intern (string-append (symbol-name (car lst)) "-CLAUSE") "ACL2") (coerce-to-process-name-lst (cdr lst))) nil)) (defun coerce-to-acl2-package-lst (lst) (declare (xargs :guard (symbol-listp lst))) (if lst (cons (intern (symbol-name (car lst)) "ACL2") (coerce-to-acl2-package-lst (cdr lst))) nil)) (defun@par chk-do-not-expr-value (lst expr ctx state) ;; here lst is the raw names, coerced to the "ACL2" package (cond ((atom lst) (cond ((null lst) (value@par nil)) (t (er@par soft ctx "The value of the :DO-NOT expression ~x0 is not a true ~ list and, hence, is not legal. In particular, the final ~ non-consp cdr is the atom ~x1. See :DOC hints." expr lst)))) ((and (symbolp (car lst)) (member-eq (car lst) *do-not-processes*)) (chk-do-not-expr-value@par (cdr lst) expr ctx state)) ((eq (car lst) 'induct) (er@par soft ctx "The value of the alleged :DO-NOT expression ~x0 includes INDUCT, ~ which is not the name of a process to turn off. You probably ~ mean to use :DO-NOT-INDUCT T or :DO-NOT-INDUCT :BYE instead. The ~ legal names are ~&1." expr *do-not-processes*)) (t (er@par soft ctx "The value of the alleged :DO-NOT expression ~x0 includes the ~ element ~x1, which is not the name of a process to turn off. ~ The legal names are ~&2." expr (car lst) *do-not-processes*)))) (defun@par translate-do-not-hint (expr ctx state) ; We translate and evaluate expr and make sure that it produces something that ; is appropriate for :do-not. We either cause an error or return the resulting ; list. (let ((wrld (w state))) (er-let*@par ((trans-ans (if (legal-variablep expr) (value@par (cons nil (list expr))) (serial-first-form-parallel-second-form@par (simple-translate-and-eval expr (list (cons 'world wrld)) nil "A :do-not hint" ctx wrld state t) (simple-translate-and-eval@par expr (list (cons 'world wrld)) nil "A :do-not hint" ctx wrld state t (f-get-global 'safe-mode state) (gc-off state)))))) ; trans-ans is (& . val), where & is either nil or a term. (cond ((not (symbol-listp (cdr trans-ans))) (er@par soft ctx "The expression following :do-not is required either to be a symbol ~ or an expression whose value is a true list of symbols, but the ~ expression ~x0 has returned the value ~x1. See :DOC hints." expr (cdr trans-ans))) (t (er-progn@par (chk-do-not-expr-value@par (coerce-to-acl2-package-lst (cdr trans-ans)) expr ctx state) (value@par (coerce-to-process-name-lst (cdr trans-ans))))))))) (defun@par translate-do-not-induct-hint (arg ctx wrld state) (declare (ignore wrld)) (cond ((symbolp arg) (cond ((member-eq arg '(:otf :otf-flg-override)) (value@par arg)) ((keywordp arg) (let ((name (symbol-name arg))) (cond ((and (<= 3 (length name)) (equal (subseq name 0 3) "OTF")) (er@par soft ctx "We do not allow :do-not-induct hint values in the ~ keyword package whose name starts with \"OTF\", ~ unless the value is :OTF or :OTF-FLG-OVERRIDE, ~ because we suspect you intended :OTF or ~ :OTF-FLG-OVERRIDE in this case. The value ~x0 is ~ thus illegal." arg)) (t (value@par arg))))) (t (value@par arg)))) (t (er@par soft ctx "The :do-not-induct hint should be followed by a symbol: either ~ T, :QUIT, or the root name to be used in the naming of any ~ clauses given byes. ~x0 is an illegal root name. See the ~ :do-not-induct discussion in :MORE-DOC hints." arg)))) (defun@par translate-hands-off-hint1 (arg ctx wrld state) (cond ((atom arg) (cond ((null arg) (value@par nil)) (t (er@par soft ctx "The value of the :hands-off hint must be a true list, but your ~ list ends in ~x0. See the :hands-off discussion in :MORE-DOC ~ hints." arg)))) ((and (consp (car arg)) (eq (car (car arg)) 'lambda) (consp (cdr (car arg))) (true-listp (cadr (car arg)))) ; At this point we know that the car of arg is of the form (lambda ; (...) . &) and we want to translate it. To do so, we create a term ; by applying it to a list of terms. Where do we get a list of the ; right number of terms? We use its own formals! (er-let*@par ((term (translate@par (cons (car arg) (cadr (car arg))) t t t ctx wrld state)) ; known-stobjs = t (stobjs-out = t) (rst (translate-hands-off-hint1@par (cdr arg) ctx wrld state))) ; Below we assume that if you give translate ((lambda ...) ...) and it ; does not cause an error, then it gives you back a function application. (value@par (cons (ffn-symb term) rst)))) ((and (symbolp (car arg)) (function-symbolp (car arg) wrld)) (er-let*@par ((rst (translate-hands-off-hint1@par (cdr arg) ctx wrld state))) (value@par (cons (car arg) rst)))) (t (er@par soft ctx "The object ~x0 is not a legal element of a :hands-off hint. See the ~ :hands-off discussion in :MORE-DOC hints." (car arg))))) (defun@par translate-hands-off-hint (arg ctx wrld state) ; Arg is supposed to be a list of function symbols. However, we ; allow either ; :hands-off append ; or ; :hands-off (append) ; in the singleton case. If the user writes ; :hands-off (lambda ...) ; we will understand it as ; :hands-off ((lambda ...)) ; since lambda is not a function symbol. (cond ((atom arg) (cond ((null arg) (value@par nil)) ((symbolp arg) (translate-hands-off-hint1@par (list arg) ctx wrld state)) (t (translate-hands-off-hint1@par arg ctx wrld state)))) ((eq (car arg) 'lambda) (translate-hands-off-hint1@par (list arg) ctx wrld state)) (t (translate-hands-off-hint1@par arg ctx wrld state)))) (defun truncated-class (rune mapping-pairs classes) ; Rune is a rune and mapping-pairs and classes are the corresponding ; properties of its base symbol. We return the class corresponding to ; rune. Recall that the classes stored are truncated classes, e.g., ; they have the proof-specific parts removed and no :COROLLARY if it ; is the same as the 'THEOREM of the base symbol. By convention, nil ; is the truncated class of a rune whose base symbol has no 'classes ; property. An example of such a rune is (:DEFINITION fn). (cond ((null classes) nil) ((equal rune (cdr (car mapping-pairs))) (car classes)) (t (truncated-class rune (cdr mapping-pairs) (cdr classes))))) (defun tests-and-alists-lst-from-fn (fn wrld) (let* ((formals (formals fn wrld)) (term (fcons-term fn formals)) (quick-block-info (getprop fn 'quick-block-info '(:error "See SUGGESTED-INDUCTION-CANDS1.") 'current-acl2-world wrld)) (justification (getprop fn 'justification '(:error "See SUGGESTED-INDUCTION-CANDS1.") 'current-acl2-world wrld)) (mask (sound-induction-principle-mask term formals quick-block-info (access justification justification :subset))) (machine (getprop fn 'induction-machine nil 'current-acl2-world wrld))) (tests-and-alists-lst (pairlis$ formals (fargs term)) (fargs term) mask machine))) (defun corollary (rune wrld) ; We return the :COROLLARY that justifies the rule named by rune. ; Nil is returned when we cannot recover a suitable formula. ":Doc-Section Miscellaneous the corollary formula of a ~il[rune]~/~/ This is a low-level system function at the present time. ~l[pr] and ~pl[pr!] instead. Also ~pl[rule-classes] for the use of the symbol ~c[:corollary] in specifying a rule class.~/" (let* ((name (base-symbol rune)) (classes (getprop name 'classes nil 'current-acl2-world wrld))) (cond ((null classes) (cond ((or (eq (car rune) :definition) (eq (car rune) :executable-counterpart)) (let ((body (body name t wrld))) (cond ((null body) nil) ((eq (car rune) :definition) (let ((lemma (find-runed-lemma rune (getprop name 'lemmas nil 'current-acl2-world wrld)))) (and lemma (let ((concl (mcons-term* (access rewrite-rule lemma :equiv) (access rewrite-rule lemma :lhs) (access rewrite-rule lemma :rhs)))) (if (access rewrite-rule lemma :hyps) ; impossible? (mcons-term* 'implies (conjoin (access rewrite-rule lemma :hyps)) concl) concl))))) (t (mcons-term* 'equal (cons-term name (formals name wrld)) body))))) ((eq (car rune) :type-prescription) (let ((tp (find-runed-type-prescription rune (getprop name 'type-prescriptions nil 'current-acl2-world wrld)))) (cond ((null tp) *t*) (t (access type-prescription tp :corollary))))) ((and (eq (car rune) :induction) (equal (cddr rune) nil)) (prettyify-clause-set (induction-formula (list (list (cons :p (formals (base-symbol rune) wrld)))) (tests-and-alists-lst-from-fn (base-symbol rune) wrld)) nil wrld)) (t (er hard 'corollary "It was thought to be impossible for a rune to have no ~ 'classes property except in the case of the four or five ~ definition runes described in the Essay on the ~ Assignment of Runes and Numes by DEFUNS. But ~x0 is a ~ counterexample." rune)))) (t (let ((term (cadr (assoc-keyword :COROLLARY (cdr (truncated-class rune (getprop name 'runic-mapping-pairs '(:error "See COROLLARY.") 'current-acl2-world wrld) classes)))))) (or term (getprop name 'theorem nil 'current-acl2-world wrld))))))) (defun formula (name normalp wrld) ; Name may be either an event name or a rune. We return the formula associated ; with name. We may return nil if we can find no such formula. (cond ((consp name) (corollary name wrld)) (t (let ((body (body name normalp wrld))) (cond ((and body normalp) ; We have a defined function. We want to use the original definition, not one ; installed by a :definition rule with non-nil :install-body field. (corollary `(:DEFINITION ,name) wrld)) (body (mcons-term* 'equal (cons-term name (formals name wrld)) body)) (t (or (getprop name 'theorem nil 'current-acl2-world wrld) (getprop name 'defchoose-axiom nil 'current-acl2-world wrld)))))))) (defun pf-fn (name state) (io? temporary nil (mv erp val state) (name) (let ((wrld (w state))) (cond ((or (symbolp name) (runep name wrld)) (let* ((name (if (symbolp name) (deref-macro-name name (macro-aliases (w state))) name)) (term (formula name t wrld))) (mv-let (col state) (cond ((equal term *t*) (fmt1 "The formula associated with ~x0 is simply T.~%" (list (cons #\0 name)) 0 (standard-co state) state nil)) (term (fmt1 "~p0~|" (list (cons #\0 (untranslate term t wrld))) 0 (standard-co state) state (term-evisc-tuple nil state))) (t (fmt1 "There is no formula associated with ~x0.~%" (list (cons #\0 name)) 0 (standard-co state) state nil))) (declare (ignore col)) (value :invisible)))) (t (er soft 'pf "~x0 is neither a symbol nor a rune in the current world." name)))))) (defmacro pf (name) ":Doc-Section History print the formula corresponding to the given name~/ ~bv[] Examples: :pf (:definition fn) ; prints the definition of fn as an equality :pf fn ; same as above :pf (:rewrite foo) ; prints the statement of the rewrite rule foo :pf foo ; same as above ~ev[]~/ ~c[pf] takes one argument, an event name or a ~il[rune], and prints the formula associated with name. If the argument is the name of a macro associated with a function name by ~il[macro-aliases-table], then the function name is used as the argument.~/" (List 'pf-fn name 'state)) (defun merge-symbol-< (l1 l2 acc) (cond ((null l1) (revappend acc l2)) ((null l2) (revappend acc l1)) ((symbol-< (car l1) (car l2)) (merge-symbol-< (cdr l1) l2 (cons (car l1) acc))) (t (merge-symbol-< l1 (cdr l2) (cons (car l2) acc))))) (defun merge-sort-symbol-< (l) (cond ((null (cdr l)) l) (t (merge-symbol-< (merge-sort-symbol-< (evens l)) (merge-sort-symbol-< (odds l)) nil)))) ;; RAG - I added the non-standard primitives here. (defconst *non-instantiable-primitives* ; We could redefine ENDP in terms of CONS so that ATOM doesn't have to be on ; the list below, but this seems unimportant. If we take ATOM off, we need to ; change the definition of MAKE-CHARACTER-LIST. '(NOT IMPLIES O< MEMBER-EQUAL ;;; perhaps not needed; we are conservative here FIX ;;; used in DEFAULT-+-2 BOOLEANP ;;; used in BOOLEANP-CHARACTERP CHARACTER-LISTP ;;; used in CHARACTER-LISTP-COERCE FORCE ;;; just nice to protect CASE-SPLIT ;;; just nice to protect MAKE-CHARACTER-LIST ;;; used in COMPLETION-OF-COERCE EQL ENDP ;;; probably used in others ATOM ;;; used in ENDP; probably used in others BAD-ATOM ;;; used in several defaxioms RETURN-LAST ;;; affects constraints (see remove-guard-holders1) MV-LIST ;;; affects constraints (see remove-guard-holders1) ; The next six are used in built-in defpkg axioms. MEMBER-SYMBOL-NAME SYMBOL-PACKAGE-NAME INTERN-IN-PACKAGE-OF-SYMBOL PKG-IMPORTS SYMBOL-LISTP NO-DUPLICATESP-EQUAL NO-DUPLICATESP-EQ-EXEC ; latter maybe not critical ; We do not want vestiges of the non-standard version in the standard version. #+:non-standard-analysis STANDARDP #+:non-standard-analysis STANDARD-PART #+:non-standard-analysis I-LARGE-INTEGER #+:non-standard-analysis REALFIX #+:non-standard-analysis I-LARGE #+:non-standard-analysis I-SMALL )) (defun instantiablep (fn wrld) ; This function returns t if fn is instantiable and nil otherwise; except, if ; if it has been introduced with the designation of a dependent ; clause-processor, then it returns the name of such a dependent ; clause-processor. (and (symbolp fn) (not (member-eq fn *non-instantiable-primitives*)) ; The list of functions above consists of o<, which we believe is built in ; implicitly in the defun principle, plus every symbol mentioned in any ; defaxiom in axioms.lisp that is not excluded by the tests below. The ; function check-out-instantiablep, when applied to an :init world will check ; that this function excludes all the fns mentioned in axioms. We call this ; function in initialize-acl2 to make sure we haven't forgotten some fns. ; We believe it would be ok to permit the instantiation of any defun'd ; function (except maybe o<) because we believe only one function ; satisfies each of those defuns. It is not clear if we should be biased ; against the other fns above. (function-symbolp fn wrld) ; A :logic mode function symbol is non-primitive if and only if it has an ; 'unnormalized-body or 'constrainedp property. For the forward implication, ; note that the symbol must have been introduced either in the signature of an ; encapsulate, in defuns, or in defchoose. Note that the value of the ; 'constrainedp property can be a clause-processor, in which case that is the ; value we want to return here; so do not switch the order of the disjuncts ; below! (or (getprop fn 'constrainedp nil 'current-acl2-world wrld) (and (body fn nil wrld) t)))) (mutual-recursion (defun all-ffn-symbs (term ans) (cond ((variablep term) ans) ((fquotep term) ans) (t (all-ffn-symbs-lst (fargs term) (cond ((flambda-applicationp term) (all-ffn-symbs (lambda-body (ffn-symb term)) ans)) (t (add-to-set-eq (ffn-symb term) ans))))))) (defun all-ffn-symbs-lst (lst ans) (cond ((null lst) ans) (t (all-ffn-symbs-lst (cdr lst) (all-ffn-symbs (car lst) ans))))) ) (defconst *unknown-constraints* ; This value must not be a function symbol, because functions may need to ; distinguish conses whose car is this value from those consisting of function ; symbols. :unknown-constraints) (defun constraint-info (fn wrld) ; This function returns a pair (mv flg x). In the simplest and perhaps most ; common case, there is no 'constraint-lst property for fn, e.g., when fn is ; defined by defun or defchoose and not in the scope of an encapsulate. In ; this case, flg is nil, and x is the defining axiom for fn. In the other ; case, flg is the name under which the actual constraint for fn is stored ; (possibly name itself), and x is the list of constraints stored there or else ; the value *unknown-constraints* (indicating that the constraints cannot be ; determined because they are associated with a dependent clause-processor). ; We assume that if fn was introduced by a non-local defun or defchoose in the ; context of an encapsulate that introduced constraints, then the defining ; axiom for fn is included in its 'constraint-lst property. That is: in that ; case, we do not need to use the definitional axiom explicitly in order to ; obtain the full list of constraints. (declare (xargs :guard (and (symbolp fn) (plist-worldp wrld)))) (let ((prop (getprop fn 'constraint-lst ; We want to distinguish between not finding a list of constraints, and finding ; a list of constraints of nil. Perhaps we only store non-nil constraints, but ; even if so, there is no need to rely on that invariant, and future versions ; of ACL2 may not respect it. t 'current-acl2-world wrld))) (cond ((eq prop t) (let ((body ; (body fn nil wrld), but easier to guard-verify: (getprop fn 'unnormalized-body nil 'current-acl2-world wrld))) (cond (body ; Warning: Do not apply remove-guard-holders to body. We rely on having all ; ancestors of body present in the constraint-info in our calculation of ; immediate-canonical-ancestors. In particular, all function symbols in a call ; of return-last, especially one generated by mbe, must be collected here, to ; support the correct use of attachments in calls of metafunctions and ; clause-processor functions; see the remark about mbe in the Essay on ; Correctness of Meta Reasoning. (mv nil (fcons-term* 'equal (fcons-term fn (formals fn wrld)) body))) (t (mv nil (or (getprop fn 'defchoose-axiom nil 'current-acl2-world wrld) ; Then fn is a primitive, and has no constraint. *t*)))))) ((and (symbolp prop) prop (not (eq prop *unknown-constraints*))) ; Then prop is a name, and the constraints for fn are found under that name. (mv prop (getprop prop 'constraint-lst '(:error "See constraint-info: expected to find a ~ 'constraint-lst property where we did not.") 'current-acl2-world wrld))) (t (mv fn prop))))) (defdoc constraint ":Doc-Section Miscellaneous restrictions on certain functions introduced in ~ilc[encapsulate] ~il[events]~/ Suppose that a given theorem, ~c[thm], is to be functionally instantiated using a given functional substitution, ~c[alist]. (~l[lemma-instance], or for an example, ~pl[functional-instantiation-example].) What is the set of proof obligations generated? It is the set obtained by applying ~c[alist] to all terms, ~c[tm], such that (a) ~c[tm] mentions some function symbol in the domain of ~c[alist], and (b) either (i) ~c[tm] arises from the ``constraint'' on a function symbol ancestral in ~c[thm] or in some ~ilc[defaxiom] or (ii) ~c[tm] is the body of a ~ilc[defaxiom]. Here, a function symbol is ``ancestral'' in ~c[thm] if either it occurs in ~c[thm], or it occurs in the definition of some function symbol that occurs in ~c[thm], and so on. The remainder of this note explains what we mean by ``constraint'' in the words above.~/ In a certain sense, function symbols are introduced in essentially two ways. The most common way is to use ~ilc[defun] (or when there is mutual recursion, ~ilc[mutual-recursion] or ~ilc[defuns]). There is also a mechanism for introducing ``witness functions''; ~pl[defchoose]. The documentation for these ~il[events] describes the axioms they introduce, which we will call here their ``definitional axioms.'' These definitional axioms are generally the constraints on the function symbols that these axioms introduce. However, when a function symbol is introduced in the scope of an ~ilc[encapsulate] event, its constraints may differ from the definitional axioms introduced for it. For example, suppose that a function's definition is ~ilc[local] to the ~ilc[encapsulate]; that is, suppose the function is introduced in the ~il[signature] of the ~ilc[encapsulate]. Then its constraints include, at the least, those non-~ilc[local] theorems and definitions in the ~ilc[encapsulate] that mention the function symbol. Actually, it will follow from the discussion below that if the ~il[signature] is empty for an ~ilc[encapsulate], then the constraint on each of its new function symbols is exactly the definitional axiom introduced for it. Intuitively, we view such ~c[encapsulates] just as we view ~ilc[include-book] ~il[events]. But the general case, where the ~il[signature] is not empty, is more complicated. In the discussion that follows we describe in detail exactly which constraints are associated with which function symbols that are introduced in the scope of an ~ilc[encapsulate] event. In order to simplify the exposition we make two cuts at it. In the first cut we present an over-simplified explanation that nevertheless captures the main ideas. In the second cut we complete our explanation by explaining how we view certain ~il[events] as being ``lifted'' out of the ~ilc[encapsulate], resulting in a possibly smaller ~ilc[encapsulate], which becomes the target of the algorithm described in the first cut. At the end of this note we present an example showing why a more naive approach is unsound. Finally, before we start our ``first cut,'' we note that any information you want ``exported'' outside an ~ilc[encapsulate] event must be there as an explicit definition or theorem. For example, even if a function ~c[foo] has output type ~c[(mv t t)] in its ~il[signature], the system will not know ~c[(true-listp (foo x))] merely on account of this information. Thus, if you are using functions like ~c[foo] (constrained ~ilc[mv] functions), then you may find it useful to prove (inside the ~c[encapsulate], to be exported) a ~c[:]~ilc[type-prescription] rule for the constrained function, for example, the ~c[:]~ilc[type-prescription] rule ~c[(true-listp (foo x))]. ~em[First cut at constraint-assigning algorithm.] Quite simply, the formulas introduced in the scope of an ~ilc[encapsulate] are conjoined, and each function symbol introduced by the ~ilc[encapsulate] is assigned that conjunction as its constraint. Clearly this is a rather severe algorithm. Let us consider two possible optimizations in an informal manner before presenting our second cut. Consider the (rather artificial) event below. The function ~c[before1] does not refer at all, even indirectly, to the locally-introduced function ~c[sig-fn], so it is unfortunate to saddle it with constraints about ~c[sig-fn]. ~bv[] (encapsulate (((sig-fn *) => *)) (defun before1 (x) (if (consp x) (before1 (cdr x)) x)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) ) ~ev[] We would like to imagine moving the definition of ~c[before1] to just in front of this ~ilc[encapsulate], as follows. ~bv[] (defun before1 (x) (if (consp x) (before1 (cdr x)) x)) (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) ) ~ev[] Thus, we will only assign the constraint ~c[(consp (sig-fn x))], from the theorem ~c[sig-fn-prop], to the function ~c[sig-fn], not to the function ~c[before1]. More generally, suppose an event in an ~ilc[encapsulate] event does not mention any function symbol in the ~il[signature] of the ~ilc[encapsulate], nor any function symbol that mentions any such function symbol, and so on. (We might say that no function symbol from the ~il[signature] is an ``ancestor'' of any function symbol occurring in the event.) Then we imagine moving the event, so that it appears in front of the ~ilc[encapsulate]. We don't actually move it, but we pretend we do when it comes time to assign constraints. Thus, such definitions only introduce definitional axioms as the constraints on the function symbols being defined. In the example above, the event ~c[sig-fn-prop] introduces no constraints on function ~c[before1]. Once this first optimization is performed, we have in mind a set of ``constrained functions.'' These are the functions introduced in the ~ilc[encapsulate] that would remain after moving some of them in front, as indicated above. Consider the collection of all formulas introduced by the ~ilc[encapsulate], except the definitional axioms, that mention these constrained functions. So for example, in the event below, no such formula mentions the function symbol ~c[after1]. ~bv[] (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) (defun after1 (x) (sig-fn x)) ) ~ev[] We can see that there is really no harm in imagining that we move the definition of ~c[after1] out of the ~ilc[encapsulate], to just after the ~ilc[encapsulate]. Many subtle aspects of this rearrangement process have been omitted. For example, suppose the function ~c[fn] uses ~c[sig-fn], the latter being a function in the signature of the encapsulation. Suppose a formula about ~c[fn] is proved in the encapsulation. Then from the discussion above ~c[fn] is among the constrained functions of the encapsulate: it cannot be moved before the encapsulate and it cannot be moved after the encapsulation. But why is ~c[fn] constrained? The reason is that the theorem proved about ~c[fn] may impose or express constraints on ~c[sig-fn]. That is, the theorem proved about ~c[fn] may depend upon properties of the witness used for ~c[sig-fn]. Here is a simple example: ~bv[] (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (declare (ignore x)) 0)) (defun fn (lst) (if (endp lst) t (and (integerp (sig-fn (car lst))) (fn (cdr lst))))) (defthm fn-always-true (fn lst))) ~ev[] In this example, there are no ~ilc[defthm] events that mention ~c[sig-fn] explicitly. One might therefore conclude that it is completely unconstrained. But the witness we chose for it always returns an integer. The function ~c[fn] uses ~c[sig-fn] and we prove that ~c[fn] always returns true. Of course, the proof of this theorem depends upon the properties of the witness for ~c[sig-fn], even though those properties were not explicitly ``called out'' in theorems proved about ~c[sig-fn]. It would be unsound to move ~c[fn-always-true] after the encapsulate. It would also be unsound to constrain ~c[sig-fn] to satisfy just ~c[fn-always-true] without including in the constraint the relation between ~c[sig-fn] and ~c[fn]. Hence both ~c[sig-fn] and ~c[fn] are constrained by this encapsulation and the constraint imposed on each is the same and states the relation between the two as characterized by the equation defining ~c[fn] as well as the property that ~c[fn] always returns true. Suppose, later, one proved a theorem about ~c[sig-fn] and wished to functionally instantiate it. Then one must also functionally instantiate ~c[fn], even if it is not involved in the theorem, because it is only through ~c[fn] that ~c[sig-fn] inherits its constrained properties. This is a pathological example that illustrate a trap into which one may easily fall: rather than identify the key properties of the constrained function the user has foreshadowed its intended application and constrained those notions. Clearly, the user wishing to introduce the ~c[sig-fn] above would be well-advised to use the following instead: ~bv[] (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (declare (ignore x)) 0)) (defthm integerp-sig-fn (integerp (sig-fn x)))) (defun fn (lst) (if (endp lst) t (and (integerp (sig-fn (car lst))) (fn (cdr lst))))) (defthm fn-always-true (fn lst))) ~ev[] Note that ~c[sig-fn] is constrained merely to be an integer. It is the only constrained function. Now ~c[fn] is introduced after the encapsulation, as a simple function that uses ~c[sig-fn]. We prove that ~c[fn] always returns true, but this fact does not constrain ~c[sig-fn]. Future uses of ~c[sig-fn] do not have to consider ~c[fn] at all. Sometimes it is necessary to introduce a function such as ~c[fn] within the ~c[encapsulate] merely to state the key properties of the undefined function ~c[sig-fn]. But that is unusual and the user should understand that both functions are being constrained. Another subtle aspect of encapsulation that has been brushed over so far has to do with exactly how functions defined within the encapsulation use the signature functions. For example, above we say ``Consider the collection of all formulas introduced by the encapsulate, ~em[except the definitional axioms], that mention these constrained functions.'' We seem to suggest that a definitional axiom which mentions a constrained function can be moved out of the encapsulation and considered part of the ``post-encapsulation'' extension of the logical ~il[world], if the defined function is not used in any non-definitional formula proved in the encapsulation. For example, in the encapsulation above that constrained ~c[sig-fn] and introduced ~c[fn] within the encapsulation, ~c[fn] was constrained because we proved the formula ~c[fn-always-true] within the encapsulation. Had we not proved ~c[fn-always-true] within the encapsulation, ~c[fn] could have been moved after the encapsulation. But this suggests an unsound rule because whether such a function can be moved after the encapsulate depend on whether its ~em[admission] used properties of the witnesses! In particular, we say a function is ``subversive'' if any of its governing tests or the actuals in any recursive call involve a function in which the signature functions are ancestral. ~l[subversive-recursions]. (Aside: The definition of ~c[fn] in the first enapsulation above that defines ~c[fn], i.e., the encapsulation with ~c[fn-always-true] inside, is subversive because the call of the macro ~ilc[AND] expands to a call of ~c[IF] that governs a recursive call of ~c[fn], in this case: ~bv[] (defun fn (lst) (if (endp lst) t (if (integerp (sig-fn (car lst))) (fn (cdr lst)) nil))). ~ev[] If we switch the order of conjuncts in ~c[fn], then the definition of ~c[fn] is no longer subversive, but it still ``infects'' the constraint generated for the encapsulation, hence for ~c[sig-fn], because ~c[fn-always-true] blocks the definition of ~c[fn] from being moved back (to after the encapsulation). If we both switch the order of conjuncts and drop ~c[fn-always-true] from the encapsulation, then the definition of ~c[fn] is in essence moved back to after the encapsulation, and the constraint for ~c[sig-fn] no longer includes the definition of ~c[fn]. End of aside.) Another aspect we have not discussed is what happens to nested encapsulations when each introduces constrained functions. We say an ~c[encapsulate] event is ``trivial'' if it introduces no constrained functions, i.e., if its signatures is ~c[nil]. Trivial encapsulations are just a way to wrap up a collection of events into a single event. From the foregoing discussion we see we are interested in exactly how we can ``rearrange'' the events in a non-trivial encapsulation -- moving some ``before'' the encapsulation and others ``after'' the encapsulation. We are also interested in which functions introduced by the encapsulation are ``constrained'' and what the ``constraints'' on each are. We may summarize the observations above as follows, after which we conclude with a more elaborate example. ~em[Second cut at constraint-assigning algorithm.] First, we focus only on non-trivial encapsulations that neither contain nor are contained in non-trivial encapsulations. (Nested non-trivial encapsulations are not rearranged at all: do not put anything in such a nest unless you mean for it to become part of the constraints generated.) Second, in what follows we only consider the non-~c[local] events of such an ~c[encapsulate], assuming that they satisfy the restriction of using no locally defined function symbols other than the signature functions. Given such an ~c[encapsulate] event, move, to just in front of it and in the same order, all definitions and theorems for which none of the signature functions is ancestral. Now collect up all formulas (theorems) introduced in the ~ilc[encapsulate] other than definitional axioms. Add to this set any of those definitional equations that is either subversive or defines a function used in a formula in the set. The conjunction of the resulting set of formulas is called the ``constraint'' and the set of all the signature functions of the ~c[encapsulate] together with all function symbols defined in the ~c[encapsulate] and mentioned in the constraint is called the ``constrained functions.'' Assign the constraint to each of the constrained functions. Move, to just after the ~c[encapsulate], the definitions of all function symbols defined in the ~c[encapsulate] that have been omitted from the constraint. Implementation note. In the implementation we do not actually move ~il[events], but we create constraints that pretend that we did. Here is an example illustrating our constraint-assigning algorithm. It builds on the preceding examples. ~bv[] (encapsulate (((sig-fn *) => *)) (defun before1 (x) (if (consp x) (before1 (cdr x)) x)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) (defun during (x) (if (consp x) x (cons (car (sig-fn x)) 17))) (defun before2 (x) (before1 x)) (defthm before2-prop (atom (before2 x))) (defthm during-prop (implies (and (atom x) (before2 x)) (equal (car (during x)) (car (sig-fn x))))) (defun after1 (x) (sig-fn x)) (defchoose after2 (x) (u) (and (< u x) (during x))) ) ~ev[] Only the functions ~c[sig-fn] and ~c[during] receive extra constraints. The functions ~c[before1] and ~c[before2] are viewed as moving in front of the ~ilc[encapsulate], as is the theorem ~c[before2-prop]. The functions ~c[after1] and ~c[after2] are viewed as being moved past the ~ilc[encapsulate]. The implementation reports the following. ~bv[] In addition to SIG-FN, we export AFTER2, AFTER1, BEFORE2, DURING and BEFORE1. The following constraint is associated with both of the functions DURING and SIG-FN: (AND (EQUAL (DURING X) (IF (CONSP X) X (CONS (CAR (SIG-FN X)) 17))) (CONSP (SIG-FN X)) (IMPLIES (AND (ATOM X) (BEFORE2 X)) (EQUAL (CAR (DURING X)) (CAR (SIG-FN X))))) ~ev[] Notice that the formula ~c[(consp (during x))] is not a conjunct of the constraint. During the first pass of the ~c[encapsulate], this formula is stored as a ~c[:]~ilc[type-prescription] rule deduced during the definition of the function ~c[during]. However, the rule is not exported because of a rather subtle soundness issue. (If you are interested in details, see the comments in source function ~c[putprop-type-prescription-lst].) We conclude by asking (and to a certain extent, answering) the following question: Isn't there an approach to assigning constraints that avoids over-constraining more simply than our ``second cut'' above? Perhaps it seems that given an ~ilc[encapsulate], we should simply assign to each locally defined function the theorems exported about that function. If we adopted that simple approach the events below would be admissible. ~bv[] (encapsulate (((foo *) => *)) (local (defun foo (x) x)) (defun bar (x) (foo x)) (defthm bar-prop (equal (bar x) x) :rule-classes nil)) (defthm foo-id (equal (foo x) x) :hints ((\"Goal\" :use bar-prop))) ; The following event is not admissible in ACL2. (defthm ouch! nil :rule-classes nil :hints ((\"Goal\" :use ((:functional-instance foo-id (foo (lambda (x) (cons x x)))))))) ~ev[] Under the simple approach we have in mind, ~c[bar] is constrained to satisfy both its definition and ~c[bar-prop] because ~c[bar] mentions a function declared in the signature list of the encapsulation. In fact, ~c[bar] is so-constrained in the ACL2 semantics of encapsulation and the first two events above (the ~c[encapsulate] and the consequence that ~c[foo] must be the identity function) are actually admissible. But under the simple approach to assigning constraints, ~c[foo] is unconstrained because no theorem about it is exported. Under that approach, ~c[ouch!] is provable because ~c[foo] can be instantiated in ~c[foo-id] to a function other than the identity function. It's tempting to think we can fix this by including definitions, not just theorems, in constraints. But consider the following slightly more elaborate example. The problem is that we need to include as a constraint on ~c[foo] not only the definition of ~c[bar], which mentions ~c[foo] explicitly, but also ~c[abc], which has ~c[foo] as an ancestor. ~bv[] (encapsulate (((foo *) => *)) (local (defun foo (x) x)) (local (defthm foo-prop (equal (foo x) x))) (defun bar (x) (foo x)) (defun abc (x) (bar x)) (defthm abc-prop (equal (abc x) x) :rule-classes nil)) (defthm foo-id (equal (foo x) x) :hints ((\"Goal\" :use abc-prop))) ; The following event is not admissible in ACL2. (defthm ouch! nil :rule-classes nil :hints ((\"Goal\" :use ((:functional-instance foo-id (foo (lambda (x) (cons x x))) (bar (lambda (x) (cons x x)))))))) ~ev[] ") (defun@par chk-equal-arities (fn1 n1 fn2 n2 ctx state) (cond ((not (equal n1 n2)) (er@par soft ctx "It is illegal to replace ~x0 by ~x1 because the former ~#2~[takes no ~ arguments~/takes one argument~/takes ~n3 arguments~] while the latter ~ ~#4~[takes none~/takes one~/takes ~n5~]. See the :functional-instance ~ discussion in :MORE-DOC :lemma-instance." fn1 fn2 (cond ((int= n1 0) 0) ((int= n1 1) 1) (t 2)) n1 (cond ((int= n2 0) 0) ((int= n2 1) 1) (t 2)) n2)) (t (value@par nil)))) (defun extend-sorted-symbol-alist (pair alist) (cond ((endp alist) (list pair)) ((symbol-< (car pair) (caar alist)) (cons pair alist)) (t (cons (car alist) (extend-sorted-symbol-alist pair (cdr alist)))))) ;; RAG - This checks to see whether two function symbols are both ;; classical or both non-classical #+:non-standard-analysis (defun@par chk-equiv-classicalp (fn1 fn2 termp ctx wrld state) (let ((cp1 (classicalp fn1 wrld)) (cp2 (if termp ; fn2 is a term, not a function symbol (classical-fn-list-p (all-fnnames fn2) wrld) (classicalp fn2 wrld)))) (if (equal cp1 cp2) (value@par nil) (er@par soft ctx "It is illegal to replace ~x0 by ~x1 because the former ~#2~[is ~ classical~/is not classical~] while the latter ~#3~[is~/is not~]." (if (symbolp fn1) fn1 (untranslate fn1 nil wrld)) (if (symbolp fn2) fn2 (untranslate fn2 nil wrld)) (if cp1 0 1) (if cp2 0 1))))) ;; RAG - I modified the following, so that we do not allow substn to ;; map a non-classical constrained function into a classical function ;; or vice versa. (defun@par translate-functional-substitution (substn ctx wrld state) ; Substn is alleged to be a functional substitution. We know that it is a true ; list! We check that each element is a pair of the form (fn1 fn2), where fn1 ; is an instantiable function symbol of arity n and fn2 is either a function ; symbol of arity n or else a lambda expression of arity n with a body that ; translates. We also check that no fn1 is bound twice. ; Note: We permit free variables to occur in the body, we permit implicitly ; ignored variables, and we do not permit declarations in the lambda. That is, ; we take each lambda to be of the form (lambda (v1 ... vn) body) and we merely ; insist that body be a term with no particular relation to the vi. ; If substn satisfies these conditions we return an alist in which each pair ; has the form (fn1 . fn2'), where fn2' is the symbol fn2 or the lambda ; expression (lambda (v1 ... vn) body'), where body' is the translation of ; body. We call this the translated functional substitution. The returned ; result is sorted by car; see event-responsible-for-proved-constraint for how ; we make use of this fact. ; Warning: The presence of free variables in the lambda expressions means that ; capturing is possible during functional substitution. We do not check that ; no capturing occurs, since we are not given the terms into which we will ; substitute. (cond ((null substn) (value@par nil)) ((not (and (true-listp (car substn)) (= (length (car substn)) 2))) (er@par soft ctx "The object ~x0 is not of the form (fi gi) as described in the ~ :functional-instance discussion of :MORE-DOC lemma-instance." (car substn))) (t (let ((fn1 (caar substn)) (fn2 (cadar substn)) (str "The object ~x0 is not of the form (fi gi) as ~ described in the :functional-instance discussion of ~ :MORE-DOC lemma-instance. ~x1 is neither a ~ function symbol nor a pseudo-lambda expression.")) (cond ((not (and (symbolp fn1) (function-symbolp fn1 wrld))) (er@par soft ctx "Each domain element in a functional substitution must be a ~ function symbol, but ~x0 is not. See the :functional-instance ~ discussion of :MORE-DOC lemma-instance." fn1)) ((not (eq (instantiablep fn1 wrld) t)) (er@par soft ctx "The function symbol ~x0 cannot be instantiated~@1. See the ~ :functional-instance discussion about `instantiable' in :DOC ~ lemma-instance." fn1 (if (eq (instantiablep fn1 wrld) nil) "" (msg " because it was introduced in an encapsulate specifying a ~ dependent clause-processor, ~x0 (see DOC ~ define-trusted-clause-processor)" (instantiablep fn1 wrld))))) (t (er-let*@par ((x (cond ((symbolp fn2) (let ((fn2 (deref-macro-name fn2 (macro-aliases wrld)))) (cond ((function-symbolp fn2 wrld) (er-progn@par (chk-equal-arities@par fn1 (arity fn1 wrld) fn2 (arity fn2 wrld) ctx state) #+:non-standard-analysis (chk-equiv-classicalp@par fn1 fn2 nil ctx wrld state) (value@par (cons fn1 fn2)))) (t (er@par soft ctx str (car substn) fn2))))) ((and (true-listp fn2) (= (length fn2) 3) (eq (car fn2) 'lambda)) (er-let*@par ((body (translate@par (caddr fn2) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (er-progn@par (chk-arglist@par (cadr fn2) t ctx wrld state) (chk-equal-arities@par fn1 (arity fn1 wrld) fn2 (length (cadr fn2)) ctx state) #+:non-standard-analysis (chk-equiv-classicalp@par fn1 body t ctx wrld state) (value@par (cons fn1 (make-lambda (cadr fn2) body)))))) (t (er@par soft ctx str (car substn) fn2)))) (y (translate-functional-substitution@par (cdr substn) ctx wrld state))) (cond ((assoc-eq fn1 y) (er@par soft ctx "It is illegal to bind ~x0 twice in a functional ~ substitution. See the :functional-instance discussion ~ of :MORE-DOC lemma-instance." fn1)) (t (value@par (extend-sorted-symbol-alist x y))))))))))) (mutual-recursion ; After Version_3.4, Ruben Gamboa added the variable allow-freevars-p, with the ; following explanation: ; Allow-freevars-p should be set to t in the #-:non-standard-analysis case, but ; otherwise set to nil when we are trying to apply the substitution to a ; non-classical formula. In those cases, free variables in the body can ; capture non-standard objects, resulting in invalid theorems. For example, ; take the following theorem ; ; (standardp x) => (standardp (f x)) ; ; This theorem is true for classical constrained function f. Now instantiate ; (f x) with (lambda (x) (+ x y)). The result is ; ; (standardp x) => (standardp (+ x y)) ; ; This formula is false. E.g., it fails when x=0 and y=(i-large-integer). (defun sublis-fn-rec (alist term bound-vars allow-freevars-p) ; See the comment just above for additional discussion of allow-freevars-p. ; This function carries out the functional substitution into term specified by ; the translated functional substitution alist. It checks that alist does not ; allow capturing of its free variables by lambda expressions in term. If ; allow-freevars-p is nil, it also checks that the alist does not have free ; variables in lambda expressions. The return value is either (mv vars term), ; for vars a non-empty list of variables -- those having captured occurrences ; when allow-freevars-p is true, else all free variables of lambda expressions ; in alist when allow-freevars-p is nil -- or else is (mv nil new-term) when ; there are no such captures or invalid free variables, in which case new-term ; is the result of the functional substitution. Note that the caller can tell ; whether an error is caused by capturing or by having disallowed free ; variables, since in the case that allow-freevars-p is nil, it is impossible ; for free variables to be captured (since no free variables are allowed). ; Let us say that an occurrence of fn in term is problematic if fn is bound to ; lambda-expr in alist and for every variable v that occurs free in ; lambda-expr, this occurrence of fn is not in the scope of a lambda binding of ; v. Key Observation: If there is no problematic occurrence of any function ; symbol in term, then we can obtain the result of this call of sublis-fn by ; first replacing v in lambda-app by a fresh variable v', then carrying out the ; functional substitution, and finally doing an ordinary substitution of v for ; v'. This Key Observation explains why it suffices to check that there is no ; such problematic occurrence. As we recur, we maintain bound-vars to be a ; list that includes all variables lambda-bound in the original term at the ; present occurrence of term. ; Every element of alist is either of the form (fn . sym) or of the form (fn ; . (LAMBDA (v1...vn) body)) where the vi are distinct variables and body is a ; translated term, but it is not known that body mentions only vars in formals. ; The former case, where fn is bound to a sym, is simple to handle: when we see ; calls of fn we replace them by calls of sym. The latter case is not. When ; we hit (g (FOO) y) with the functional substitution in which FOO gets (LAMBDA ; NIL X), we generate (g X y). Note that this "imports" a free X into a term, ; (g (foo) y), where there was no X. ; But there is a problem. If you hit ((lambda (z) (g (FOO) z)) y) with FOO ; gets (LAMBDA NIL X), you would naively produce ((lambda (z) (g X z)) y), ; importing the X into the G term as noted above. But we also just imported ; the X into the scope of a lambda! Even though there is no capture, we now ; have a lambda expression whose body contains a var not among the formals. ; That is not a term! ; The solution is to scan the new lambda body, which is known to be a term, and ; collect the free vars -- vars not bound among the formals of the lambda -- ; and add them both to the lambda formals and to the actuals. (cond ((variablep term) (mv nil term)) ((fquotep term) (mv nil term)) ((flambda-applicationp term) (let ((old-lambda-formals (lambda-formals (ffn-symb term)))) (mv-let (erp new-lambda-body) (sublis-fn-rec alist (lambda-body (ffn-symb term)) (append old-lambda-formals bound-vars) allow-freevars-p) (cond (erp (mv erp new-lambda-body)) (t (mv-let (erp args) (sublis-fn-rec-lst alist (fargs term) bound-vars allow-freevars-p) (cond (erp (mv erp args)) (t (let* ((body-vars (all-vars new-lambda-body)) (extra-body-vars (set-difference-eq body-vars old-lambda-formals))) (mv nil (fcons-term (make-lambda (append old-lambda-formals extra-body-vars) new-lambda-body) (append args extra-body-vars)))))))))))) (t (let ((temp (assoc-eq (ffn-symb term) alist))) (cond (temp (cond ((symbolp (cdr temp)) (mv-let (erp args) (sublis-fn-rec-lst alist (fargs term) bound-vars allow-freevars-p) (cond (erp (mv erp args)) (t (mv nil (cons-term (cdr temp) args)))))) (t (let ((bad (if allow-freevars-p (intersection-eq (set-difference-eq (all-vars (lambda-body (cdr temp))) (lambda-formals (cdr temp))) bound-vars) (set-difference-eq (all-vars (lambda-body (cdr temp))) (lambda-formals (cdr temp)))))) (cond (bad (mv bad term)) (t (mv-let (erp args) (sublis-fn-rec-lst alist (fargs term) bound-vars allow-freevars-p) (cond (erp (mv erp args)) (t (mv nil (sublis-var (pairlis$ (lambda-formals (cdr temp)) args) (lambda-body (cdr temp))))))))))))) (t (mv-let (erp args) (sublis-fn-rec-lst alist (fargs term) bound-vars allow-freevars-p) (cond (erp (mv erp args)) (t (mv nil (cons-term (ffn-symb term) args))))))))))) (defun sublis-fn-rec-lst (alist terms bound-vars allow-freevars-p) (cond ((null terms) (mv nil nil)) (t (mv-let (erp term) (sublis-fn-rec alist (car terms) bound-vars allow-freevars-p) (cond (erp (mv erp term)) (t (mv-let (erp tail) (sublis-fn-rec-lst alist (cdr terms) bound-vars allow-freevars-p) (cond (erp (mv erp tail)) (t (mv nil (cons term tail))))))))))) ) (defun sublis-fn (alist term bound-vars) ; This is just the usual case. We call the more general function ; sublis-fn-rec, which can be used on the non-standard case. (sublis-fn-rec alist term bound-vars t)) (defun sublis-fn-simple (alist term) ; This is the normal case, in which we call sublis-fn with no bound vars and we ; expect no vars to be captured (say, because alist has no free variables). (mv-let (vars result) (sublis-fn-rec alist term nil t) (assert$ (null vars) result))) (defun sublis-fn-lst-simple (alist termlist) ; See sublis-fn-simple, as this is the analogous function for a list of terms. (mv-let (vars result) (sublis-fn-rec-lst alist termlist nil t) (assert$ (null vars) result))) (mutual-recursion (defun instantiable-ffn-symbs (term wrld ans ignore-fns) ; We collect every instantiablep ffn-symb occurring in term except those listed ; in ignore-fns. We include functions introduced by an encapsulate specifying ; a dependent clause-processor. (cond ((variablep term) ans) ((fquotep term) ans) ((flambda-applicationp term) (let ((ans (instantiable-ffn-symbs (lambda-body (ffn-symb term)) wrld ans ignore-fns))) (instantiable-ffn-symbs-lst (fargs term) wrld ans ignore-fns))) (t (instantiable-ffn-symbs-lst (fargs term) wrld (cond ((or (not (instantiablep (ffn-symb term) wrld)) (member-eq (ffn-symb term) ignore-fns)) ans) (t (add-to-set-eq (ffn-symb term) ans))) ignore-fns)))) (defun instantiable-ffn-symbs-lst (lst wrld ans ignore-fns) (cond ((null lst) ans) (t (instantiable-ffn-symbs-lst (cdr lst) wrld (instantiable-ffn-symbs (car lst) wrld ans ignore-fns) ignore-fns)))) ) (defun unknown-constraint-supporters (fn wrld) ; Fn is the constraint-lst property of some function g with a non-Boolean ; constraint-lst property, indicating that g was introduced in a dependent ; clause-processor. The ancestors of g are guaranteed to be among the closure ; under ancestors of the supporters stored for fn in the ; trusted-clause-processor-table. (let ((entry (assoc-eq fn (table-alist 'trusted-clause-processor-table wrld)))) (cond ((or (null entry) (not (eq (cddr entry) t))) (er hard 'unknown-constraint-supporters "Implementation error: Function ~x0 was called on ~x1, which ~ was expected to be a dependent clause-processor function, but ~ apparently is not." 'unknown-constraint-supporters fn)) (t (cadr entry))))) (defun collect-instantiablep1 (fns wrld ignore-fns) ; We assume that fns has no duplicates. (cond ((endp fns) nil) ((and (not (member-eq (car fns) ignore-fns)) (instantiablep (car fns) wrld)) (cons (car fns) (collect-instantiablep1 (cdr fns) wrld ignore-fns))) (t (collect-instantiablep1 (cdr fns) wrld ignore-fns)))) (defun all-instantiablep (fns wrld) (cond ((endp fns) t) ((instantiablep (car fns) wrld) (all-instantiablep (cdr fns) wrld)) (t nil))) (defun collect-instantiablep (fns wrld ignore-fns) ; We assume that fns has no duplicates. (cond ((and (not (intersectp-eq fns ignore-fns)) (all-instantiablep fns wrld)) fns) (t (collect-instantiablep1 fns wrld ignore-fns)))) (defun immediate-instantiable-ancestors (fn wrld ignore-fns) ; We return the list of all the instantiable function symbols ('instantiablep ; property t) that are immediate supporters of the introduction of fn, except ; those appearing in ignore-fns. ; If there are (possibly empty) constraints associated with fn, then we get all ; of the instantiable function symbols used in the constraints, which includes ; the definitional axiom if there is one. Note that the case of a dependent ; clause-processor with *unknown-constraints* is a bit different, as we use its ; supporters appropriately stored in a table. ; If fn was introduced by a defun or defchoose (it should be a non-primitive), ; we return the list of all instantiable functions used in its introduction. ; Note that even if fn is introduced by a defun, it may have constraints if its ; definition was within the scope of an encapsulate, in which case the ; preceding paragraph applies. ; If fn is introduced any other way we consider it primitive and all of the ; axioms about it had better involve non-instantiable symbols, so the answer is ; nil. ; Note: We pass down ignore-fns simply to avoid consing into our answer a ; function that the caller is going to ignore anyway. It is possible for fn to ; occur as an element of its "immediate ancestors" as computed here. This ; happens, for example, if fn is defun'd recursively and fn is not in ; ignore-fns. At the time of this writing the only place we use ; immediate-instantiable-ancestors is in ancestors, where fn is always in ; ignore-fns (whether fn is recursive or not). (mv-let (name x) (constraint-info fn wrld) (cond ((eq x *unknown-constraints*) (let* ((cl-proc (getprop name 'constrainedp '(:error "See immediate-instantiable-ancestors: expected to ~ find a 'constrainedp property where we did not.") 'current-acl2-world wrld)) (supporters (unknown-constraint-supporters cl-proc wrld))) (collect-instantiablep supporters wrld ignore-fns))) (name (instantiable-ffn-symbs-lst x wrld nil ignore-fns)) (t (instantiable-ffn-symbs x wrld nil ignore-fns))))) (defun instantiable-ancestors (fns wrld ans) ; Fns is a list of function symbols. We compute the list of all instantiable ; function symbols that are ancestral to the functions in fns and accumulate ; them in ans, including those introduced in an encapsulate specifying a ; dependent clause-processor. (cond ((null fns) ans) ((member-eq (car fns) ans) (instantiable-ancestors (cdr fns) wrld ans)) (t (let* ((ans1 (cons (car fns) ans)) (imm (immediate-instantiable-ancestors (car fns) wrld ans1)) (ans2 (instantiable-ancestors imm wrld ans1))) (instantiable-ancestors (cdr fns) wrld ans2))))) (mutual-recursion (defun hitp (term alist) ; Alist is a translated functional substitution. We return t iff ; term mentions some function symbol in the domain of alist. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (hitp (lambda-body (ffn-symb term)) alist) (hitp-lst (fargs term) alist))) ((assoc-eq (ffn-symb term) alist) t) (t (hitp-lst (fargs term) alist)))) (defun hitp-lst (terms alist) (cond ((null terms) nil) (t (or (hitp (car terms) alist) (hitp-lst (cdr terms) alist))))) ) (defun event-responsible-for-proved-constraint (name alist proved-fnl-insts-alist) ; For context, see the Essay on the proved-functional-instances-alist. ; Here proved-fnl-insts-alist is of the form of the world global ; 'proved-functional-instances-alist. Thus, it is a list of entries of the ; form (constraint-event-name restricted-alist . behalf-of-event-name), where ; constraint-event-name is the name of an event such that the functional ; instance of that event's constraint (i.e., function's constraint or axiom's ; 'theorem property) by restricted-alist was proved on behalf of the event ; named behalf-of-event-name. (cond ((endp proved-fnl-insts-alist) nil) ((and (eq name (access proved-functional-instances-alist-entry (car proved-fnl-insts-alist) :constraint-event-name)) (equal alist (access proved-functional-instances-alist-entry (car proved-fnl-insts-alist) :restricted-alist))) ; We allow the behalf-of-event-name field (see comment above) to be nil in ; temporary versions of this sort of data structure, but we do not expect to ; find nil for that field in proved-fnl-insts-alist, which comes from the ACL2 ; world. (We store 0 there when there is no event name to use, e.g. when the ; event was a verify-guards event. See the call of ; proved-functional-instances-from-tagged-objects in install-event.) But to be ; safe in avoiding confusion with the first branch of our cond (in which there ; is no appropriate entry for our proof obligation), we check for nil here. (or (access proved-functional-instances-alist-entry (car proved-fnl-insts-alist) :behalf-of-event-name) (er hard 'event-responsible-for-proved-constraint "Implementation error: We expected to find a non-nil ~ :behalf-of-event-name field in the following entry of the world ~ global 'proved-functional-instances-alist, but did not:~%~x0." (car proved-fnl-insts-alist)))) (t (event-responsible-for-proved-constraint name alist (cdr proved-fnl-insts-alist))))) (defun getprop-x-lst (symbols prop wrld) (cond ((null symbols) nil) (t (cons (getprop (car symbols) prop nil 'current-acl2-world wrld) (getprop-x-lst (cdr symbols) prop wrld))))) (defun filter-hitps (lst alist ans) (cond ((endp lst) ans) ((hitp (car lst) alist) (filter-hitps (cdr lst) alist (cons (car lst) ans))) (t (filter-hitps (cdr lst) alist ans)))) (defun relevant-constraints1 (names alist proved-fnl-insts-alist constraints event-names new-entries seen wrld) ; For context, see the Essay on the proved-functional-instances-alist. ; Names is a list of function symbols, each of which therefore has a constraint ; formula. We return three values, corresponding respectively to the following ; three formals, which are initially nil: constraints, event-names, and ; new-entries. The first value is the result of collecting those constraint ; formulas that are hit by the translated functional substitution alist, except ; for those that are known (via proved-fnl-insts-alist) to have already been ; proved. The second is a list of names of events responsible for the validity ; of the omitted formulas. The third is a list of pairs (cons name ; restr-alist), where restr-alist is obtained by restricting the given alist to ; the instantiable function symbols occurring in the constraint generated by ; name (in the sense of constraint-info). ; Exception: We are free to return (mv *unknown-constraints* g cl-proc). ; However, we only do so if the constraints cannot be determined because of the ; presence of unknown constraints on some function g encountered, where g was ; introduced with the designation of a dependent clause-processor, cl-proc. We ; ignore this exceptional case in the comments just below. ; Seen is a list of names already processed. Suppose that foo and bar are both ; constrained by the same encapsulate, and that the 'constraint-lst property of ; 'bar is 'foo. Since both foo and bar generate the same constraint, we want ; to be sure only to process that constraint once. So, we put foo on the list ; seen as soon as bar is processed, so that foo will not have to be processed. ; Note that the current ttree is not available here. If it were, we could ; choose to avoid proving constraints that were already generated in the ; current proof. It doesn't seem that this would buy us very much, though: ; how often does one find more than one :functional-instance lemma instance in ; a single proof, especially with overlapping constraints? ; See also relevant-constraints1-axioms, which is a similar function for ; collecting constraint information from defaxiom events. (cond ((null names) (mv constraints event-names new-entries)) ((member-eq (car names) seen) (relevant-constraints1 (cdr names) alist proved-fnl-insts-alist constraints event-names new-entries seen wrld)) (t (mv-let (name x) (constraint-info (car names) wrld) ; Note that if x is not *unknown-constraints*, then x is a single constraint if ; name is nil and otherwise x is a list of constraints. (cond ((eq x *unknown-constraints*) (let ((cl-proc (getprop name 'constrainedp '(:error "See relevant-constraints1: expected to find ~ a 'constrainedp property where we did not.") 'current-acl2-world wrld))) (cond ((first-assoc-eq (unknown-constraint-supporters cl-proc wrld) alist) (mv x name cl-proc)) (t (relevant-constraints1 (cdr names) alist proved-fnl-insts-alist constraints event-names new-entries seen wrld))))) ((and name (not (eq name (car names))) ; Minor point: the test immediately above is subsumed by the one below, since ; we already know at this point that (not (member-eq (car names) seen)), but we ; keep it in for efficiency. (member-eq name seen)) (relevant-constraints1 (cdr names) alist proved-fnl-insts-alist constraints event-names new-entries (cons (car names) seen) wrld)) (t (let* ((x (cond (name (filter-hitps x alist nil)) ((hitp x alist) x) ; We continue to treat x as a list of constraints or a single constraint, ; depending respectively on whether name is non-nil or nil; except, we will ; use nil for x when there are no constraints even when name is nil. (t nil))) (instantiable-fns (and x ; optimization (cond (name (instantiable-ffn-symbs-lst x wrld nil nil)) (t (instantiable-ffn-symbs x wrld nil nil)))))) (let* ((constraint-alist (and x ; optimization (restrict-alist instantiable-fns alist))) (ev (and x ; optimization: ev unused when (null x) below (event-responsible-for-proved-constraint (or name (car names)) constraint-alist proved-fnl-insts-alist))) (seen (cons (car names) (if (and name (not (eq name (car names)))) (cons name seen) seen)))) (cond ((null x) (relevant-constraints1 (cdr names) alist proved-fnl-insts-alist constraints event-names new-entries seen wrld)) (ev (relevant-constraints1 (cdr names) alist proved-fnl-insts-alist constraints ; Notice that ev could be 0; see event-responsible-for-proved-constraint. ; Where do we handle such an "event name"? Here is an inverted call stack: ; relevant-constraints1 ; called by: ; relevant-constraints ; called by: ; translate-lmi/functional-instance ; called by: ; translate-lmi ; called by: ; translate-use-hint(1) translate-by-hint ; called by: ; translate-x-hint-value ; So, hints are translated. Who looks at the results? Well, ; apply-top-hints-clause adds :use and :by to the tag-tree. ; Who looks at the tag-tree? It's ; apply-top-hints-clause-msg1, which in turn calls ; tilde-@-lmi-phrase -- and THAT is who sees and handles an "event" of 0. ; We might want to construct an example that illustrates this "0 handling" by ; way of providing a :functional-instance lemma-instance in a verify-guards. (add-to-set ev event-names) new-entries seen wrld)) (t (relevant-constraints1 (cdr names) alist proved-fnl-insts-alist (if name (append x constraints) (cons x constraints)) event-names ; On which name's behalf do we note the constraint-alist? If name is not nil, ; then it is a "canonical" name for which constraint-info returns the ; constraints we are using, in the sense that its constraint-lst is a list. ; Otherwise, (car names) is the name used to obtain constraint-info. (cons (make proved-functional-instances-alist-entry :constraint-event-name (or name (car names)) :restricted-alist constraint-alist :behalf-of-event-name ; Eventually, the ``nil'' below may be filled in with the event name on behalf ; of which we are carrying out the current proof. nil) new-entries) seen wrld))))))))))) (defun relevant-constraints1-axioms (names alist proved-fnl-insts-alist constraints event-names new-entries wrld) ; For context, see the Essay on the proved-functional-instances-alist. ; This function is similar to relevant-constraints1, and should be kept more or ; less conceptually in sync with it. However, in this function, names is a ; list of distinct axiom names rather than function names. See ; relevant-constraints1 for comments. (cond ((null names) (mv constraints event-names new-entries)) (t (let* ((constraint (getprop (car names) 'theorem '(:error "See relevant-constraints1-axioms.") 'current-acl2-world wrld)) (instantiable-fns (instantiable-ffn-symbs constraint wrld nil nil))) (cond ((hitp constraint alist) (let* ((constraint-alist (restrict-alist instantiable-fns alist)) (ev (event-responsible-for-proved-constraint (car names) constraint-alist proved-fnl-insts-alist))) (cond (ev (relevant-constraints1-axioms (cdr names) alist proved-fnl-insts-alist constraints (add-to-set ev event-names) new-entries wrld)) (t (relevant-constraints1-axioms (cdr names) alist proved-fnl-insts-alist (cons constraint constraints) event-names (cons (make proved-functional-instances-alist-entry :constraint-event-name (car names) :restricted-alist constraint-alist :behalf-of-event-name nil) new-entries) wrld))))) (t (relevant-constraints1-axioms (cdr names) alist proved-fnl-insts-alist constraints event-names new-entries wrld))))))) (defun relevant-constraints (thm alist proved-fnl-insts-alist wrld) ; For context, see the Essay on the proved-functional-instances-alist. ; Thm is a term and alist is a translated functional substitution. We return ; three values. The first value is the list of the constraints that must be ; instantiated with alist and proved in order to justify the functional ; instantiation of thm. The second value is a list of names of events on whose ; behalf proof obligations were not generated that would otherwise have been, ; because those proof obligations were proved during processing of those ; events. (In such cases we do not include these constraints in our first ; value.) Our third and final value is a list of new entries to add to the ; world global 'proved-functional-instances-alist, as described in the comment ; for event-responsible-for-proved-constraint. ; Keep the following comment in sync with the corresponding comment in ; defaxiom-supporters. ; The relevant theorems are the set of all terms, term, such that ; (a) term mentions some function symbol in the domain of alist, ; AND ; (b) either ; (i) term arises from a definition of or constraint on a function symbol ; ancestral either in thm or in some defaxiom, ; OR ; (ii) term is the body of a defaxiom. ; In translate-lmi/functional-instance we check that variable capture is ; avoided. (let ((nonconstructive-axiom-names (global-val 'nonconstructive-axiom-names wrld))) (mv-let (constraints event-names new-entries) (relevant-constraints1-axioms nonconstructive-axiom-names alist proved-fnl-insts-alist nil nil nil wrld) (assert$ (not (eq constraints *unknown-constraints*)) (let* ((instantiable-fns (instantiable-ffn-symbs-lst (cons thm (getprop-x-lst nonconstructive-axiom-names 'theorem wrld)) wrld nil nil)) (ancestors (instantiable-ancestors instantiable-fns wrld nil))) (relevant-constraints1 ancestors alist proved-fnl-insts-alist constraints event-names new-entries nil wrld)))))) (mutual-recursion (defun bound-vars (term ans) (cond ((variablep term) ans) ((fquotep term) ans) ((flambda-applicationp term) (bound-vars (lambda-body (ffn-symb term)) (bound-vars-lst (fargs term) (union-eq (lambda-formals (ffn-symb term)) ans)))) (t (bound-vars-lst (fargs term) ans)))) (defun bound-vars-lst (terms ans) (cond ((null terms) ans) (t (bound-vars-lst (cdr terms) (bound-vars (car terms) ans))))) ) (defun@par translate-lmi/instance (formula constraints event-names new-entries extra-bindings-ok substn ctx wrld state) ; Formula is some term, obtained by previous instantiations. Constraints ; are the constraints generated by those instantiations -- i.e., if the ; constraints are theorems then formula is a theorem. Substn is an ; alleged variable substitution. We know substn is a true list. ; Provided substn indeed denotes a substitution that is ok to apply to formula, ; we create the instance of formula. We return a list whose car is the ; instantiated formula and whose cdr is the incoming constraints, event-names ; and new-entries, which all pass through unchanged. Otherwise, we cause an ; error. (er-let*@par ((alist (translate-substitution@par substn ctx wrld state))) (let* ((vars (all-vars formula)) (un-mentioned-vars (and (not extra-bindings-ok) (set-difference-eq (strip-cars alist) vars)))) (cond (un-mentioned-vars (er@par soft ctx "The formula you wish to instantiate, ~p3, mentions ~#0~[no ~ variables~/only the variable ~&1~/the variables ~&1~]. Thus, there ~ is no reason to include ~&2 in the domain of your substitution. We ~ point this out only because it frequently indicates that a mistake ~ has been made. See the discussion of :instance in :DOC ~ lemma-instance, which explains how to use a keyword, ~ :extra-bindings-ok, to avoid this error (for example, in case your ~ substitution was automatically generated by a macro)." (zero-one-or-more vars) (merge-sort-symbol-< vars) (merge-sort-symbol-< un-mentioned-vars) (untranslate formula t wrld))) (t (value@par (list (sublis-var alist formula) constraints event-names new-entries))))))) (defun@par translate-lmi/functional-instance (formula constraints event-names new-entries substn proved-fnl-insts-alist ctx wrld state) ; For context, see the Essay on the proved-functional-instances-alist. ; Formula is some term, obtained by previous instantiations. Constraints are ; the constraints generated by those instantiations -- i.e., if the constraints ; are theorems then formula is a theorem. Substn is an untranslated object ; alleged to be a functional substitution. ; Provided substn indeed denotes a functional substitution that is ok to apply ; to both formula and the new constraints imposed, we create the functional ; instance of formula and the new constraints to prove. We return a pair whose ; car is the instantiated formula and whose cdr is the incoming constraints ; appended to the new ones added by this functional instantiation. Otherwise, ; we cause an error. (er-let*@par ((alist (translate-functional-substitution@par substn ctx wrld state))) (mv-let (new-constraints new-event-names new-new-entries) (relevant-constraints formula alist proved-fnl-insts-alist wrld) (cond ((eq new-constraints *unknown-constraints*) (er@par soft ctx "Functional instantiation is disallowed in this context, because the ~ function ~x0 has unknown constraints provided by the dependent ~ clause-processor ~x1. See :DOC define-trusted-clause-processor." new-event-names new-new-entries)) (t (let ((allow-freevars-p #-:non-standard-analysis t #+:non-standard-analysis (classical-fn-list-p (all-fnnames formula) wrld))) (mv-let (erp0 formula0) (sublis-fn-rec alist formula nil allow-freevars-p) (mv-let (erp new-constraints0) (cond (erp0 (mv erp0 formula0)) (t (sublis-fn-rec-lst alist new-constraints nil allow-freevars-p))) (cond (erp ; The following message is surprising in a situation where a variable is ; captured by a binding to itself, sinced for example (let ((x x)) ...) ; translates and then untranslates back to (let () ...). Presumably we could ; detect such cases and not consider them to be captures. But we keep it ; simple and simply expect and hope that such a misleading message is never ; actually seen by a user. (er@par soft ctx (if allow-freevars-p "Your functional substitution contains one or more free ~ occurrences of the variable~#0~[~/s~] ~&0 in its range. ~ Alas, ~#1~[this variable occurrence is~/these variables ~ occurrences are~] bound in a LET or MV-LET expression of ~ ~#2~[the formula you wish to functionally instantiate, ~ ~p3.~|~/the constraints that must be relieved. ~]You must ~ therefore change your functional substitution so that it ~ avoids such ``capture.'' It will suffice for your ~ functional substitution to stay clear of all the variables ~ bound by a LET or MV-LET expression that are used in the ~ target formula or in the corresponding constraints. Thus ~ it will suffice for your substitution not to contain free ~ occurrences of ~v4 in its range, by using fresh variables ~ instead. Once you have fixed this problem, you can :use ~ an :instance of your :functional-instance to bind the ~ fresh variables to ~&4." ; With allow-freevars-p = nil, it is impossible for free variables to be ; captured, since no free variables are allowed. "Your functional substitution contains one or more free ~ occurrences of the variable~#0~[~/s~] ~&0 in its range. ~ Alas, the formula you wish to functionally instantiate is ~ not a classical formula, ~p3. Free variables in lambda ~ expressions are only allowed when the formula to be ~ instantiated is classical, since these variables may admit ~ non-standard values, for which the theorem may be false.") (merge-sort-symbol-< erp) erp (if erp0 0 1) (untranslate formula t wrld) (bound-vars-lst (cons formula new-constraints) nil))) (t (value@par (list formula0 (append constraints new-constraints0) (union-equal new-event-names event-names) (union-equal new-new-entries new-entries))))))))))))) (defun@par translate-lmi (lmi normalizep ctx wrld state) ; Lmi is an object that specifies some instance of a theorem. It may ; specify a substitution instance or a functional instantiation, or ; even some composition of such instances. This function checks that ; lmi is meaningful and either causes an error or returns (as the ; value result of an error/value/state producing function) a list ; (thm constraints event-names new-entries) ; where: ; thm is a term, intuitively, the instance specified; ; constraints is a list of terms, intuitively a list of conjectures which must ; be proved in order to prove thm; ; event-names is a list of names to credit for avoiding certain proof ; obligations in the generation of the constraints; and ; new-entries is the list of new entries for the world global ; 'proved-functional-instances-alist, which we will place in a tag-tree and ; eventually using the name of the event currently being proved (if any). ; A lemma instance is either ; (a) the name of a formula, ; (b) the rune of a corollary, ; (c) (:theorem formula) ; (d) (:instance lmi . substn), or ; (e) (:functional-instance lmi . substn) ; where lmi is another lemma instance and substn is a substitution of the ; appropriate type. ; Normalizep tells us whether to use the normalized body or the ; 'unnormalized-body when the lmi refers to a funcction definition. We use the ; normalized body for :use hints, where added simplification can presumably ; only be helpful (and for backwards compatibility as we introduce normalizep ; in Version_2.7). But we use the 'unnormalized-body for :by hints as a ; courtesy to the user, who probably is thinking of that rather than the ; normalized body when instantiating a definition. (let ((str "The object ~x0 is an ill-formed lemma instance because ~@1. ~ See :DOC lemma-instance.")) (cond ((atom lmi) (cond ((symbolp lmi) (let ((term (formula lmi normalizep wrld))) (cond (term (value@par (list term nil nil nil))) (t (er@par soft ctx str lmi (msg "there is no formula associated with the name ~ ~x0" lmi)))))) (t (er@par soft ctx str lmi "it is an atom that is not a symbol")))) ((runep lmi wrld) (let ((term (and (not (eq (car lmi) :INDUCTION)) (corollary lmi wrld)))) (cond (term (value@par (list term nil nil nil))) (t (er@par soft ctx str lmi "there is no known formula associated with this rune"))))) ((eq (car lmi) :theorem) (cond ((and (true-listp lmi) (= (length lmi) 2)) (er-let*@par ((term (translate@par (cadr lmi) t t t ctx wrld state))) ; known-stobjs = t (stobjs-out = t) (value@par (list term (list term) nil nil)))) (t (er@par soft ctx str lmi "this :THEOREM lemma instance is not a true list of length 2")))) ((or (eq (car lmi) :instance) (eq (car lmi) :functional-instance)) (cond ((and (true-listp lmi) (>= (length lmi) 2)) (er-let*@par ((lst (translate-lmi@par (cadr lmi) normalizep ctx wrld state))) (let ((formula (car lst)) (constraints (cadr lst)) (event-names (caddr lst)) (new-entries (cadddr lst)) (substn (cddr lmi))) (cond ((eq (car lmi) :instance) (mv-let (extra-bindings-ok substn) (cond ((eq (car substn) :extra-bindings-ok) (mv t (cdr substn))) (t (mv nil substn))) (translate-lmi/instance@par formula constraints event-names new-entries extra-bindings-ok substn ctx wrld state))) (t (translate-lmi/functional-instance@par formula constraints event-names new-entries substn (global-val 'proved-functional-instances-alist wrld) ctx wrld state)))))) (t (er@par soft ctx str lmi (msg "this ~x0 lemma instance is not a true list of length at ~ least 2" (car lmi)))))) (t (er@par soft ctx str lmi "is not a symbol, a rune in the current logical world, or a list ~ whose first element is :THEOREM, :INSTANCE, or~ ~ :FUNCTIONAL-INSTANCE"))))) (deflabel functional-instantiation-in-acl2r :doc ":Doc-Section Miscellaneous additional requirements for ~c[:functional-instance] hints in ACL2(r)~/ This documentation topic relates to ACL2(r), the modification of ACL2 that supports the real numbers (~pl[real]). ~l[hints] and ~pl[lemma-instance] for a discussion of ~c[:use] hints that employ the ~c[:functional-instance] keyword. Here, we document additional requirements for such hints that applies to ACL2(r). We assume familiarity with lemma instances; ~pl[lemma-instance]. (1) When functionally instantiating a non-classical formula, it is illegal to use pseudo-lambda expressions in a lemma instance. (2) A classical function symbol must be bound either to a classical function symbol or to a lambda (or, if allowed, pseudo-lambda) expression with a classical body. Similarly, a non-classical function symbol must be bound either to a non-classical function symbol or to a lambda (or, if allowed, pseudo-lambda) expression with a non-classical body.~/~/") (deflabel lemma-instance :doc ":Doc-Section Miscellaneous an object denoting an instance of a theorem~/ Lemma instances are the objects one provides via ~c[:use] and ~c[:by] ~il[hints] (~pl[hints]) to bring to the theorem prover's attention some previously proved or easily provable fact. A typical use of the ~c[:use] hint is given below. The value specified is a list of five lemma instances. ~bv[] :use (reverse-reverse (:type-prescription app) (:instance assoc-of-app (x a) (y b) (z c)) (:functional-instance p-f (p consp) (f flatten)) (:instance (:theorem (equal x x)) (x (flatten a)))) ~ev[] Observe that an event name can be a lemma instance. The ~c[:use] hint allows a single lemma instance to be provided in lieu of a list, as in: ~bv[] :use reverse-reverse ~ev[] or ~bv[] :use (:instance assoc-of-app (x a) (y b) (z c)) ~ev[]~/ A lemma instance denotes a formula which is either known to be a theorem or which must be proved to be a theorem before it can be used. To use a lemma instance in a particular subgoal, the theorem prover adds the formula as a hypothesis to the subgoal before the normal theorem proving heuristics are applied. A lemma instance, or ~c[lmi], is of one of the following five forms: (1) ~c[name], where ~c[name] names a previously proved theorem, axiom, or definition and denotes the formula (theorem) of that name. (2) ~c[rune], where ~c[rune] is a ~il[rune] (~pl[rune]) denoting the ~c[:]~ilc[corollary] justifying the rule named by the ~il[rune]. (3) ~c[(:theorem term)], where ~c[term] is any term alleged to be a theorem. Such a lemma instance denotes the formula ~c[term]. But before using such a lemma instance the system will undertake to prove ~c[term]. (4) ~c[(:instance lmi (v1 t1) ... (vn tn))], where ~c[lmi] is recursively a lemma instance, the ~c[vi]'s are distinct variables and the ~c[ti]'s are terms. Such a lemma instance denotes the formula obtained by instantiating the formula denoted by ~c[lmi], replacing each ~c[vi] by ~c[ti]. Normally ACL2 enforces the requirement that every variable ~c[vi] must be bound in the formula denoted by ~c[lmi]. However, the keyword ~c[:extra-bindings-ok] may be inserted immediately after the lemma instance in order to remove that requirement: ~c[(:instance lmi :extra-bindings-ok (v1 t1) ... (vn tn))]. (5) ~c[(:functional-instance lmi (f1 g1) ... (fn gn))], where ~c[lmi] is recursively a lemma instance and each ~c[fi] is an ``instantiable'' function symbol of arity ~c[ni] and ~c[gi] is a function symbol, a macro alias for a function symbol ~c[gi'] (~pl[macro-aliases-table]) in which case we treat ~c[gi] as ~c[gi'], or a pseudo-lambda expression of arity ~c[ni]. An instantiable function symbol is any defined or constrained function symbol except the primitives ~ilc[not], ~ilc[member], ~ilc[implies], and ~ilc[o<], and a few others, as listed by the constant ~c[*non-instantiable-primitives*]. These are built-in in such a way that we cannot recover the ~il[constraint]s on them. (Special case: a function introduced in the ~c[:partial-theory] of a dependent clause-processor is not instantiable; ~pl[define-trusted-clause-processor].) A pseudo-lambda expression is an expression of the form ~c[(lambda (v1 ... vn) body)] where the ~c[vi] are distinct variable symbols and ~c[body] is any term. No ~i[a priori] relation is imposed between the ~c[vi] and the variables of ~c[body], i.e., ~c[body] may ignore some ~c[vi]'s and may contain ``free'' variables. However, we do not permit ~c[v] to occur freely in ~c[body] if the functional substitution is to be applied to any formula (~c[lmi] or the ~il[constraint]s to be satisfied) in a way that inserts ~c[v] into the scope of a binding of ~c[v] by ~ilc[let] or ~ilc[mv-let] (or, ~ilc[lambda]). If you happen to violate this restriction, an informative error message will be printed. That message will list for you the potentially illegal choices for ~c[v] in the context in which the functional substitution is offered. A ~c[:functional-instance] lemma instance denotes the formula obtained by functionally instantiating the formula denoted by ~c[lmi], replacing ~c[fi] by ~c[gi]. However, before such a lemma instance can be used, the system will generate proof obligations arising from the replacement of the ~c[fi]'s by the ~c[gi]'s in constraints that ``support'' the lemma to be functionally instantiated; ~pl[constraint]. One might expect that if the same instantiated constraint were generated on behalf of several events, then each of those instances would have to be proved. However, for the sake of efficiency, ACL2 stores the fact that such an instantiated constraint has been proved and avoids it in future events. Note that ACL2(r) (~pl[real]) imposes additional requirements for functional instantiation. ~l[functional-instantiation-in-acl2r]. Obscure case for ~il[definition]s. If the lemma instance refers to a ~c[:definition] ~il[rune], then it refers to the ~ilc[corollary] formula of that rune, which can be a simplified (``normalized'') form of the original formula. However, if the hint is a ~c[:by] hint and the lemma instance is based on a name (i.e., a symbol), rather than a rune, then the formula is the original formula of the event, as shown by ~c[:]~ilc[pe], rather than the normalized version, as shown by ~c[:]~ilc[pf]. This is as one would expect: If you supply the name of an event, you expect it to refer to the original event. For ~c[:use] hints we use the simplified (normalized) form instead, which is reasonable since one would expect simplification during the proof that re-traces the normalization done at the time the rule was created. ~l[functional-instantiation-example] for an example of the use of ~c[:functional-instance] (so-called ``functional instantiation).''~/") (defun@par translate-use-hint1 (arg ctx wrld state) ; Arg is a list of lemma instantiations and we return a list of the form (hyps ; constraints event-names new-entries); see translate-by-hint or translate-lmi ; for details. In particular, hyps is a list of the instantiated theorems to ; be added as hypotheses and constraints is a list of the constraints that must ; be proved. (cond ((atom arg) (cond ((null arg) (value@par '(nil nil nil nil))) (t (er@par soft ctx "The value of the :use hint must be a true list but your ~ list ends in ~x0. See the :use discussion in :MORE-DOC ~ hints." arg)))) (t (er-let*@par ((lst1 (translate-lmi@par (car arg) t ctx wrld state)) (lst2 (translate-use-hint1@par (cdr arg) ctx wrld state))) (value@par (list (cons (car lst1) (car lst2)) (append (cadr lst1) (cadr lst2)) (union-eq (caddr lst1) (caddr lst2)) (union-equal (cadddr lst1) (cadddr lst2)))))))) (defun@par translate-use-hint (arg ctx wrld state) ; Nominally, the :use hint is followed by a list of lmi objects. ; However, if the :use hint is followed by a single lmi, we automatically ; make a singleton list out of the lmi, e.g., ; :use assoc-of-append ; is the same as ; :use (assoc-of-append) ; ; :use (:instance assoc-of-append (x a)) ; is the same as ; :use ((:instance assoc-of-append (x a))) ; This function either causes an error or returns (as the value component of ; an error/value/state triple) a list of the form ; (lmi-lst (hyp1 ... hypn) cl k event-names new-entries), ; lmi-lst is the true-list of lmis processed, (hyp1 ... hypn) are the ; hypothesis theorems obtained, cl is a single clause that is the ; conjunction of the constraints, k is the number of conjuncts, ; event-names is a list of names to credit for avoiding certain proof ; obligations in the generation of the constraints, and new-entries is ; the list of new entries for the world global ; 'proved-functional-instances-alist. ; Note: The subroutines of this function deal in answer pairs of the form ; ((hyp1 ... hypn) . constraints), where constraints is a list of all the ; constraint terms. The conversion from that internal convention to the ; external one used in translated :use hints is made here. ; A Brief History of a Rapidly Changing Notation (Feb 28, 1990) ; Once upon a time, lemma instance had the form (assoc-of-append :x ; a). We adopted the policy that if a substitution was going to be ; applied to a lemma, term, and x was in the domain of the ; substitution, then one wrote :x and wrote the substitution "flat", ; without parentheses around the variable/term pairs. In general, :x ; meant "the variable symbol in term whose symbol name was "x"." We ; enforced the restrictin that there was at most one variable symbol ; in a stored formula with a given symbol name. ; At that time we denoted lemma instances with such notation as ; (assoc-of-append :x a :y b :z c). Functional instances were not yet ; implemented. But in order to disambiguate the use of a single ; lemma instance from the use of several atomic instances, e.g., ; :use (assoc-of-append :x a :y b :z c) ; versus ; :use (assoc-of-append rev-rev) ; we relied on the idea that the domain elements of the substitution ; were keywords. ; The implementation of functional instantiation changed all that. ; First, we learned that the translation of a keyword domain element, ; e.g., :fn, into a function symbol could not be done in a way ; analogous to what we were doing with variables. Which function is ; meant by :fn? You might say, "the one with that symbol name in the ; target theorem being instantiated." But there may be no such symbol ; in the target theorem; the user may want to instantiate :fn in some ; constraint being proved for that theorem's instantiation. But then ; you might say "then search the constraint too for a suitable meaning ; for :fn." Ah ha! You can't compute the constraint until you know ; which functions are being instantiated. So the general idea of ; using the target to translate keyword references just fails and it ; was necessary to come up with an unambiguous way of writing a ; substitution. We temporarily adopted the idea that the "keywords" ; in flat substitutions might not be keywords at all. E.g., you could ; write ACL2-NQTHM::X as a domain element. That might have put into ; jeapardy their use to disambiguate :use hint. ; But simultaneously we adopted the idea that lemma instances are ; written as (:instance assoc-of-append ...) or (:functional-instance ; assoc-of-append ...). This was done so lemma instances could be ; nested, to allow functional instances to then be instantiated. But ; with the keyword at the beginning of a lemma instance it suddenly ; became possible to disambiguate :use hints: ; :use (assoc-of-append rev-rev) ; can mean nothing but use two lemma instances because the argument to ; the use is not a lemma instance. ; So we were left with no compelling need to have keywords and flat ; substitutions and a lot of confusion if we did have keywords. So we ; abandoned them in favor of the let-bindings like notation. (cond ((null arg) (er@par soft ctx "Implementation error: Empty :USE hints should not be handled by ~ translate-use-hint (for example, they are handled by ~ translate-hint-settings.")) (t (let ((lmi-lst (cond ((atom arg) (list arg)) ((or (eq (car arg) :instance) (eq (car arg) :functional-instance) (eq (car arg) :theorem) (runep arg wrld)) (list arg)) (t arg)))) (er-let*@par ((lst (translate-use-hint1@par lmi-lst ctx wrld state))) ; Lst is of the form ((hyp1 ... hypn) constraints event-names new-entries), ; where constraints is a list of constraint terms, implicitly conjoined. We ; wish to return something of the form ; (lmi-lst (hyp1 ... hypn) constraint-cl k event-names new-entries) ; where constraint-cl is a clause that is equivalent to the constraints. (value@par (list lmi-lst (car lst) (add-literal (conjoin (cadr lst)) nil nil) (length (cadr lst)) (caddr lst) (cadddr lst)))))))) (defun convert-name-tree-to-new-name1 (name-tree char-lst sym) (cond ((atom name-tree) (cond ((symbolp name-tree) (mv (append (coerce (symbol-name name-tree) 'list) (cond ((null char-lst) nil) (t (cons #\Space char-lst)))) name-tree)) ((stringp name-tree) (mv (append (coerce name-tree 'list) (cond ((null char-lst) nil) (t (cons #\Space char-lst)))) sym)) (t (mv (er hard 'convert-name-tree-to-new-name1 "Name-tree was supposed to be a cons tree of ~ symbols and strings, but this one contained ~ ~x0. One explanation for this is that we ~ liberalized what a goal-spec could be and ~ forgot this function." name-tree) nil)))) (t (mv-let (char-lst sym) (convert-name-tree-to-new-name1 (cdr name-tree) char-lst sym) (convert-name-tree-to-new-name1 (car name-tree) char-lst sym))))) (defun convert-name-tree-to-new-name (name-tree wrld) ; A name-tree is just a cons tree composed entirely of strings ; and symbols. We construct the symbol whose symbol-name is the ; string that contains the fringe of the tree, separated by ; spaces, and then we generate a new name in wrld. For example, ; if name-tree is '(("Guard Lemma for" . APP) . "Subgoal 1.3''") then we ; will return '|Guard Lemma for APP Subgoal 1.3''|, provided that is new. ; To make it new we'll start tacking on successive subscripts, ; as with gen-new-name. The symbol we generate is interned in ; the same package as the first symbol occurring in name-tree, ; or in "ACL2" if no symbol occurs in name-tree. (mv-let (char-lst sym) (convert-name-tree-to-new-name1 name-tree nil 'convert-name-tree-to-new-name) (gen-new-name (intern-in-package-of-symbol (coerce char-lst 'string) sym) wrld))) (defun@par translate-by-hint (name-tree arg ctx wrld state) ; A :BY hint must either be a single lemma instance, nil, or a new ; name which we understand the user intends will eventually become a ; lemma instance. Nil means that we are to make up an appropriate ; new name from the goal-spec. Note: We can't really guarantee that ; the name we make up (or one we check for the user) is new because ; the same name may be made up twice before either is actually ; created. But this is just a courtesy to the user anyway. In the ; end, he'll have to get his names defthm'd himself. ; If arg is an lemma instance, then we return a list of the form (lmi-lst ; thm-cl-set constraint-cl k event-names new-entries), where lmi-lst is a ; singleton list containing the lmi in question, thm-cl-set is the set of ; clauses obtained from the instantiated theorem and which is to subsume the ; indicated goal, constraint-cl is a single clause which represents the ; conjunction of the constraints we are to establish, k is the number of ; conjuncts, event-names is a list of names to credit for avoiding certain ; proof obligations in the generation of the constraints, and new-entries will ; be used to update the world global 'proved-functional-instances-alist. ; If arg is a new name, then we return just arg itself (or the name ; generated). (cond ((or (and arg (symbolp arg) (formula arg t wrld)) (consp arg)) (er-let*@par ((lst (translate-lmi@par arg nil ctx wrld state))) ; Lst is (thm constraints event-names new-entries), where: thm is a term; ; constraints is a list of terms whose conjunction we must prove; event-names ; is a list of names of events on whose behalf we already proved certain proof ; obligations arising from functional instantiation; and new-entries may ; eventually be added to the world global 'proved-functional-instances-alist so ; that the present event can contribute to avoiding proof obligations for ; future proofs. (value@par (list (list arg) (car lst) (add-literal (conjoin (cadr lst)) nil nil) (length (cadr lst)) (caddr lst) (cadddr lst))))) ((null arg) ; The name nil is taken to mean make up a suitable name for this subgoal. (value@par (convert-name-tree-to-new-name name-tree wrld))) ((and (symbolp arg) (not (keywordp arg)) (not (equal *main-lisp-package-name* (symbol-package-name arg))) (new-namep arg wrld)) ; The above checks are equivalent to chk-all-but-new-name and chk-just- ; new-name, but don't cause the error upon failure. The error message ; that would otherwise be generated is confusing because the user isn't ; really trying to define arg to be something yet. (value@par arg)) (t (er@par soft ctx "The :BY hint must be given a lemma-instance, nil, or a new name. ~ ~x0 is none of these. See :DOC hints." arg)))) (defun@par translate-cases-hint (arg ctx wrld state) ; This function either causes an error or returns (as the value component of ; an error/value/state triple) a list of terms. (cond ((null arg) (er@par soft ctx "We do not permit empty :CASES hints.")) ((not (true-listp arg)) (er@par soft ctx "The value associated with a :CASES hint must be a true-list of terms, ~ but ~x0 is not." arg)) (t (translate-term-lst@par arg t t t ctx wrld state)))) (defun@par translate-case-split-limitations-hint (arg ctx wrld state) ; This function returns an error triple. In the non-error case, the value ; component of the error triple is a two-element list that controls the ; case-splitting, in analogy to set-case-split-limitations. (declare (ignore wrld)) #+acl2-par (declare (ignorable state)) (cond ((null arg) (value@par '(nil nil))) ((and (true-listp arg) (equal (len arg) 2) (or (natp (car arg)) (null (car arg))) (or (natp (cadr arg)) (null (cadr arg)))) (value@par arg)) (t (er@par soft ctx "The value associated with a :CASE-SPLIT-LIMITATIONS hint must ~ be either nil (denoting a list of two nils), or a true list of ~ length two, each element which is either nil or a natural ~ number; but ~x0 is not." arg)))) (defun@par translate-no-op-hint (arg ctx wrld state) (declare (ignore arg ctx wrld)) #+acl2-par (declare (ignorable state)) (value@par t)) (defun@par translate-error-hint (arg ctx wrld state) (declare (ignore wrld)) (cond ((tilde-@p arg) (er@par soft ctx "~@0" arg)) (t (er@par soft ctx "The :ERROR hint keyword was included among your hints, with ~ value ~x0." arg)))) (defun@par translate-induct-hint (arg ctx wrld state) (cond ((eq arg nil) (value@par nil)) (t (translate@par arg t t t ctx wrld state)))) ; known-stobjs = t (stobjs-out = t) ; We now turn to :in-theory hints. We develop here only enough to ; translate and check an :in-theory hint. We develop the code for ; the in-theory event and the related deftheory event later. ; Some such code (e.g., eval-theory-expr) was developed earlier in ; support of install-event. (defconst *built-in-executable-counterparts* ; Keep this in sync with cons-term1. '(acl2-numberp binary-* binary-+ unary-- unary-/ < car cdr char-code characterp code-char complex complex-rationalp #+:non-standard-analysis complexp coerce cons consp denominator equal #+:non-standard-analysis floor1 if imagpart integerp intern-in-package-of-symbol numerator pkg-witness pkg-imports rationalp #+:non-standard-analysis realp realpart stringp symbol-name symbol-package-name symbolp #+:non-standard-analysis standardp #+:non-standard-analysis standard-part ;; #+:non-standard-analysis i-large-integer not)) (defconst *s-prop-theory* ; This constant is no longer used in the ACL2 system code -- generally (theory ; 'minimal-theory) is more appropriate -- but we leave it here for use by ; existing books. ; This constant is not well-named, since some of its functions are not ; propositional. But we keep the name since this constant has been used in ; theory hints since nearly as far back as the inception of ACL2. (cons 'iff ; expanded in tautologyp *expandable-boot-strap-non-rec-fns*)) (defconst *definition-minimal-theory* ; We include mv-nth because of the call of simplifiable-mv-nthp in the ; definition of call-stack, which (as noted there) results in a use of the ; definition of mv-nth without tracking it in a ttree. (list* 'mv-nth 'iff *expandable-boot-strap-non-rec-fns*)) (defdoc theories-and-primitives ":Doc-Section Theories warnings from disabling certain built-in functions~/ When you ~il[disable] the ~il[definition] or ~il[executable-counterpart] of a built-in function, you may see a warning, for example as follows. ~bv[] ACL2 !>(in-theory (disable mv-nth)) ACL2 Warning [Theory] in ( IN-THEORY (DISABLE ...)): Although the theory expression (DISABLE MV-NTH) disables the :DEFINITION rule for MV-NTH, some expansions involving this function may still occur. See :DOC theories-and-primitives. ~ev[] This warning can be eliminated by turning off all theory warnings (~pl[set-inhibit-warnings]) or simply by evaluating the following form. ~bv[] (assign verbose-theory-warning nil) ~ev[] But before you eliminate such warnings, you may wish to read the following to understand their significance. First consider the following example, evaluated after the ~ilc[in-theory] event displayed above. ~bv[] ACL2 !>(thm (equal (mv-nth 2 (list a b c d e)) c)) Q.E.D. Summary Form: ( THM ...) Rules: ((:DEFINITION MV-NTH) (:FAKE-RUNE-FOR-TYPE-SET NIL)) Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Prover steps counted: 19 Proof succeeded. ACL2 !> ~ev[] Note that even though the ~il[definition] of ~ilc[mv-nth] had been ~il[disable]d, nevertheless its definition rule was used in proving this theorem. It is as though ~ilc[mv-nth] had not been been disabled after all! The warning is intended to indicate that expansion of ~c[mv-nth] calls may be made by the theorem prover even when ~c[mv-nth] is disabled. Indeed, the prover has special-purpose code for simplifying certain ~c[mv-nth] calls. A similar issue can arise for ~c[executable-counterpart] rules, as the following log illustrates. ~bv[] ACL2 !>(in-theory (disable (:executable-counterpart symbolp))) ACL2 Warning [Theory] in ( IN-THEORY (DISABLE ...)): Although the theory expression (DISABLE (:EXECUTABLE-COUNTERPART SYMBOLP)) disables the :EXECUTABLE-COUNTERPART rule for SYMBOLP, some calls involving this function may still be made. See :DOC theories-and-primitives. Summary Form: ( IN-THEORY (DISABLE ...)) Rules: NIL Warnings: Theory Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) 2921 ACL2 !>(thm (symbolp 'a)) Q.E.D. Summary Form: ( THM ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !> ~ev[] In general, ACL2 warns when ~ilc[in-theory] ~il[events] or ~il[hints] leave you in a theory where a rule for a built-in function is disabled but may be applied in some cases nonetheless, because of special-purpose prover code for handling calls of that function. The built-in function symbols with such ~il[definition] rules or ~il[executable-counterpart] rules are those in the following two lists, respectively. ~bv[] ACL2 !>*definition-minimal-theory* (MV-NTH IFF NOT IMPLIES EQ ATOM EQL = /= NULL ENDP ZEROP SYNP PLUSP MINUSP LISTP RETURN-LAST MV-LIST THE-CHECK WORMHOLE-EVAL FORCE CASE-SPLIT DOUBLE-REWRITE) ACL2 !>*built-in-executable-counterparts* (ACL2-NUMBERP BINARY-* BINARY-+ UNARY-- UNARY-/ < CAR CDR CHAR-CODE CHARACTERP CODE-CHAR COMPLEX COMPLEX-RATIONALP COERCE CONS CONSP DENOMINATOR EQUAL IF IMAGPART INTEGERP INTERN-IN-PACKAGE-OF-SYMBOL NUMERATOR PKG-WITNESS PKG-IMPORTS RATIONALP REALPART STRINGP SYMBOL-NAME SYMBOL-PACKAGE-NAME SYMBOLP NOT) ACL2 !> ~ev[] ~/~/") (defun translate-in-theory-hint (expr chk-boot-strap-fns-flg ctx wrld state) ; We translate and evaluate expr and make sure that it produces a ; common theory. We either cause an error or return the corresponding ; runic theory. ; Keep this definition in sync with minimal-theory and ; translate-in-theory-hint@par. (er-let* ((runic-value (eval-theory-expr expr ctx wrld state))) (let* ((warning-disabled-p (warning-disabled-p "Theory")) (state (cond (warning-disabled-p state) ((and chk-boot-strap-fns-flg (f-get-global 'verbose-theory-warning state) (not (subsetp-equal (getprop 'definition-minimal-theory 'theory nil ; so, returns nil early in boot-strap 'current-acl2-world wrld) runic-value))) (warning$ ctx ("Theory") "Although the theory expression ~x0 disables the ~ :DEFINITION rule~#1~[~/s~] for ~v1, some expansions ~ involving ~#1~[this function~/these functions~] may ~ still occur. See :DOC theories-and-primitives." expr (strip-base-symbols (set-difference-equal (getprop 'definition-minimal-theory 'theory nil 'current-acl2-world wrld) runic-value)) *definition-minimal-theory* '(assign verbose-theory-warning nil))) (t state)))) (let ((state (cond (warning-disabled-p state) ((and chk-boot-strap-fns-flg (f-get-global 'verbose-theory-warning state) (not (subsetp-equal (getprop 'executable-counterpart-minimal-theory 'theory nil ; so, returns nil early in boot-strap 'current-acl2-world wrld) runic-value))) (warning$ ctx ("Theory") "Although the theory expression ~x0 disables the ~ :EXECUTABLE-COUNTERPART rule~#1~[~/s~] for ~v1, some ~ calls involving ~#1~[this function~/these functions~] ~ may still be made. See :DOC theories-and-primitives." expr (strip-base-symbols (set-difference-equal (getprop 'executable-counterpart-minimal-theory 'theory nil 'current-acl2-world wrld) runic-value)) *built-in-executable-counterparts* '(assign verbose-theory-warning nil))) (t state)))) (value runic-value))))) #+acl2-par (defun translate-in-theory-hint@par (expr chk-boot-strap-fns-flg ctx wrld state) ; We translate and evaluate expr and make sure that it produces a ; common theory. We either cause an error or return the corresponding ; runic theory. ; Keep this definition in sync with minimal-theory and ; translate-in-theory-hint. (declare (ignorable chk-boot-strap-fns-flg)) ; suppress irrelevance warning (er-let*@par ((runic-value (eval-theory-expr@par expr ctx wrld state))) (let* ((warning-disabled-p (warning-disabled-p "Theory")) (ignored-val (cond (warning-disabled-p nil) ((and chk-boot-strap-fns-flg (f-get-global 'verbose-theory-warning state) (not (subsetp-equal (getprop 'definition-minimal-theory 'theory nil ; so, returns nil early in boot-strap 'current-acl2-world wrld) runic-value))) (warning$@par ctx ("Theory") "The value of the theory expression ~x0 does not include the ~ :DEFINITION rule~#1~[~/s~] for ~v1. But ~#1~[this function ~ is~/these functions are~] among a set of primitive functions ~ whose definitions are built into the ACL2 system in various ~ places. This set consists of the functions ~&2. While ~ excluding :DEFINITION rules for any functions in this set ~ from the current theory may prevent certain expansions, it ~ may not prevent others. Good luck!~|~%To inhibit this ~ warning, evaluate:~|~x3." expr (strip-base-symbols (set-difference-equal (getprop 'definition-minimal-theory 'theory nil 'current-acl2-world wrld) runic-value)) *definition-minimal-theory* '(assign verbose-theory-warning nil))) (t nil)))) (declare (ignore ignored-val)) (let ((ignored-val (cond (warning-disabled-p nil) ((and chk-boot-strap-fns-flg (f-get-global 'verbose-theory-warning state) (not (subsetp-equal (getprop 'executable-counterpart-minimal-theory 'theory nil ; so, returns nil early in boot-strap 'current-acl2-world wrld) runic-value))) (warning$@par ctx ("Theory") "The value of the theory expression ~x0 does not include the ~ :EXECUTABLE-COUNTERPART rule~#1~[~/s~] for ~v1. But ~ ~#1~[this function is~/these functions are~] among a set of ~ primitive functions whose executable counterparts are built ~ into the ACL2 system. This set consists of the functions ~ ~&2. While excluding :EXECUTABLE-COUNTERPART rules for any ~ functions in this set from the current theory may prevent ~ certain expansions, it may not prevent others. Good ~ luck!~|~%To inhibit this warning, evaluate:~|~x3." expr (strip-base-symbols (set-difference-equal (getprop 'executable-counterpart-minimal-theory 'theory nil 'current-acl2-world wrld) runic-value)) *built-in-executable-counterparts* '(assign verbose-theory-warning nil))) (t nil)))) (declare (ignore ignored-val)) (value@par runic-value))))) (defun all-function-symbolps (fns wrld) (cond ((atom fns) (equal fns nil)) (t (and (symbolp (car fns)) (function-symbolp (car fns) wrld) (all-function-symbolps (cdr fns) wrld))))) (defun non-function-symbols (lst wrld) (cond ((null lst) nil) ((function-symbolp (car lst) wrld) (non-function-symbols (cdr lst) wrld)) (t (cons (car lst) (non-function-symbols (cdr lst) wrld))))) (defun collect-non-logic-mode (alist wrld) (cond ((null alist) nil) ((and (function-symbolp (caar alist) wrld) (logicalp (caar alist) wrld)) (collect-non-logic-mode (cdr alist) wrld)) (t (cons (caar alist) (collect-non-logic-mode (cdr alist) wrld))))) (defun@par translate-bdd-hint1 (top-arg rest ctx wrld state) (cond ((null rest) (value@par nil)) (t (let ((kwd (car rest))) (er-let*@par ((cdar-alist (case kwd (:vars (cond ((eq (cadr rest) t) (value@par t)) ((not (true-listp (cadr rest))) (er@par soft ctx "The value associated with :VARS in the :BDD hint must ~ either be T or a true list, but ~x0 is neither." (cadr rest))) ((collect-non-legal-variableps (cadr rest)) (er@par soft ctx "The value associated with :VARS in the :BDD hint must ~ either be T or a true list of variables, but in the :BDD ~ hint ~x0, :VARS is associated with the following list of ~ non-variables: ~x1." top-arg (collect-non-legal-variableps (cadr rest)))) (t (value@par (cadr rest))))) (:prove (cond ((member-eq (cadr rest) '(t nil)) (value@par (cadr rest))) (t (er@par soft ctx "The value associated with ~x0 in the :BDD hint ~x1 ~ is ~x2, but it needs to be t or nil." kwd top-arg (cadr rest))))) (:literal (cond ((member-eq (cadr rest) '(:conc :all)) (value@par (cadr rest))) ((and (integerp (cadr rest)) (< 0 (cadr rest))) ; The user provides a 1-based index, but we want a 0-based index. (value@par (1- (cadr rest)))) (t (er@par soft ctx "The value associated with :LITERAL in a :BDD hint ~ must be either :CONC, :ALL, or a positive integer ~ (indicating the index, starting with 1, of a ~ hypothesis). The value ~x0 from the :BDD hint ~x1 ~ is therefore illegal." (cadr rest) top-arg)))) (:bdd-constructors (cond ((and (consp (cadr rest)) (eq (car (cadr rest)) 'quote) (consp (cdr (cadr rest))) (null (cddr (cadr rest)))) (er@par soft ctx "The value associated with :BDD-CONSTRUCTORS must be a ~ list of function symbols. It should not be quoted, ~ but the value supplied is of the form (QUOTE x).")) ((not (symbol-listp (cadr rest))) (er@par soft ctx "The value associated with :BDD-CONSTRUCTORS must be a ~ list of symbols, but ~x0 ~ is not." (cadr rest))) ((all-function-symbolps (cadr rest) wrld) (value@par (cadr rest))) (t (er@par soft ctx "The value associated with :BDD-CONSTRUCTORS must be ~ a list of :logic mode function symbols, but ~&0 ~ ~#0~[is~/are~] not." (collect-non-logic-mode ; This is an odd construct, but its saves us from defining a new function since ; we use collect-non-logic-mode elsewhere anyhow. (pairlis$ (cadr rest) nil) wrld))))) (otherwise (er@par soft ctx "The keyword ~x0 is not a legal keyword for a :BDD hint. The ~ hint ~x1 is therefore illegal. See :DOC hints." (car rest) top-arg))))) (er-let*@par ((cdr-alist (translate-bdd-hint1@par top-arg (cddr rest) ctx wrld state))) (value@par (cons (cons kwd cdar-alist) cdr-alist)))))))) (defun@par translate-bdd-hint (arg ctx wrld state) ; Returns an alist associating each of the permissible keywords with a value. (cond ((not (keyword-value-listp arg)) (er@par soft ctx "The value associated with a :BDD hint must be a list of the form (:kw1 ~ val1 :kw2 val2 ...), where each :kwi is a keyword. However, ~x0 does ~ not have this form." arg)) ((not (assoc-keyword :vars arg)) (er@par soft ctx "The value associated with a :BDD hint must include an assignment for ~ :vars, but ~x0 does not." arg)) (t (translate-bdd-hint1@par arg arg ctx wrld state)))) (defun@par translate-nonlinearp-hint (arg ctx wrld state) (declare (ignore wrld)) #+acl2-par (declare (ignorable state)) (if (or (equal arg t) (equal arg nil)) (value@par arg) (er@par soft ctx "The only legal values for a :nonlinearp hint are T and NIL, but ~x0 is ~ neither of these." arg))) (defun@par translate-backchain-limit-rw-hint (arg ctx wrld state) (declare (ignore wrld)) (if (or (natp arg) (equal arg nil)) (value@par arg) (er@par soft ctx "The only legal values for a :backchain-limit-rw hint are NIL and ~ natural numbers, but ~x0 is neither of these." arg))) (defun@par translate-no-thanks-hint (arg ctx wrld state) (declare (ignore ctx wrld)) #+acl2-par (declare (ignorable state)) (value@par arg)) (defun@par translate-reorder-hint (arg ctx wrld state) (declare (ignore wrld)) #+acl2-par (declare (ignorable state)) (if (and (pos-listp arg) (no-duplicatesp arg)) (value@par arg) (er@par soft ctx "The value for a :reorder hint must be a true list of positive integers ~ without duplicates, but ~x0 is not." arg))) (defun arity-mismatch-msg (sym expected-arity wrld) ; This little function avoids code replication in ; translate-clause-processor-hint. Expected-arity is either a number, ; indicating the expected arity, or of the form (list n), where n is the ; minimum expected arity. We return the arity of sym (or its macro alias) if ; it is not as expected, and we return t if sym has no arity and is not a ; macro. Otherwise we return nil. So if sym is a macro, then we return nil ; even though there might be a mismatch (presumably to be detected by other ; means). (let* ((fn (or (deref-macro-name sym (macro-aliases wrld)) sym)) (arity (arity fn wrld))) (cond ((null arity) (if (getprop sym 'macro-body nil 'current-acl2-world wrld) nil (msg "~x0 is neither a function symbol nor a macro name" sym))) ((and (consp expected-arity) (< arity (car expected-arity))) (msg "~x0 has arity ~x1 (expected arity of at least ~x2 for this hint ~ syntax)" fn arity (car expected-arity))) ((and (integerp expected-arity) (not (eql expected-arity arity))) (msg "~x0 has arity ~x1 (expected arity ~x2 for this hint syntax)" fn arity expected-arity)) (t nil)))) (defun@par translate-clause-processor-hint (form ctx wrld state) ; We are given the hint :clause-processor form. We return an error triple ; whose value in the non-error case is a cons pair consisting of the ; corresponding translated term (a legal call of a clause-processor) and its ; associated stobjs-out, suitable for evaluation for a :clause-processor hint. ; Each of the following cases shows legal hint syntax for a signature (or in ; the third case, a class of signatures). ; For signature ((cl-proc cl) => cl-list): ; :CLAUSE-PROCESSOR cl-proc ; :CLAUSE-PROCESSOR (:FUNCTION cl-proc) ; :CLAUSE-PROCESSOR (cl-proc CLAUSE) ; or any form macroexpanding to (cl-proc &) with at most CLAUSE free ; For signature ((cl-proc cl hint) => cl-list): ; :CLAUSE-PROCESSOR (:FUNCTION cl-proc :HINT hint) ; :CLAUSE-PROCESSOR (cl-proc CLAUSE hint) ; or any term macroexpanding to (cl-proc & &) with at most CLAUSE free ; For signature ((cl-proc cl hint stobj1 ... stobjk) => ; (mv erp cl-list stobj1 ... stobjk)): ; :CLAUSE-PROCESSOR (:FUNCTION cl-proc :HINT hint) ; :CLAUSE-PROCESSOR (cl-proc CLAUSE hint stobj1 ... stobjk): ; or any term macroexpanding to (cl-proc & & stobj1 ... stobjk) ; where CLAUSE is the only legal non-stobj free variable #+acl2-par (declare (ignorable state)) (let ((err-msg (msg "The form ~x0 is not a legal value for a ~ :clause-processor hint because ~@1. See :DOC hints." form))) (er-let*@par ((form (cond ((atom form) (cond ((symbolp form) (let ((msg (arity-mismatch-msg form 1 wrld))) (cond (msg (er@par soft ctx "~@0" err-msg msg)) (t (value@par (list form 'clause)))))) (t (er@par soft ctx "~@0" err-msg "it is an atom that is not a symbol")))) ((not (true-listp form)) (er@par soft ctx "~@0" err-msg "it is a cons that is not a true-listp")) (t (case-match form ((':function cl-proc) (cond ((symbolp cl-proc) (let ((msg (arity-mismatch-msg cl-proc 1 wrld))) (cond (msg (er@par soft ctx "~@0" err-msg msg)) (t (value@par (list cl-proc 'clause)))))) (t (er@par soft ctx "~@0" err-msg "the :FUNCTION is not a symbol")))) ((':function cl-proc ':hint hint) (cond ((symbolp cl-proc) (let ((msg (arity-mismatch-msg cl-proc '(2) wrld))) (cond (msg (er@par soft ctx "~@0" err-msg msg)) (t (value@par (list* cl-proc 'clause hint (cddr (stobjs-out cl-proc wrld)))))))) (t (er@par soft ctx "~@0" err-msg "the :FUNCTION is an atom that is not a ~ symbol")))) (& (value@par form))))))) (mv-let@par (erp term bindings state) (translate1@par form :stobjs-out ; form must be executable '((:stobjs-out . :stobjs-out)) t ctx wrld state) (cond (erp (er@par soft ctx "~@0" err-msg "it was not successfully translated (see error message above)")) ((or (variablep term) (fquotep term) (flambda-applicationp term)) (er@par soft ctx "~@0" err-msg "it is not (even after doing macroexpansion) a call of a function ~ symbol")) (t (let ((verified-p (getprop (ffn-symb term) 'clause-processor nil 'current-acl2-world wrld))) (cond ((not (or verified-p (assoc-eq (ffn-symb term) (table-alist 'trusted-clause-processor-table wrld)))) (er@par soft ctx "~@0" err-msg "it is not a call of a clause-processor function")) ((not (eq (fargn term 1) 'clause)) (er@par soft ctx "~@0" err-msg "its first argument is not the variable, CLAUSE")) ((set-difference-eq (non-stobjps (all-vars term) t wrld) '(clause)) (er@par soft ctx "~@0" err-msg (msg "it contains the free variable~#0~[~/s~] ~&0, but the only ~ legal variable (not including stobjs) is ~x1" (set-difference-eq (non-stobjps (all-vars term) t wrld) '(clause)) 'clause))) ; #+ACL2-PAR note: Here, we could check that clause-processors do not modify ; state when waterfall-parallelism is enabled. However, since performing the ; check in eval-clause-processor@par suffices, we do not perform the check ; here. (t (value@par (make clause-processor-hint :term term :stobjs-out (translate-deref :stobjs-out bindings) :verified-p verified-p))))))))))) ; We next develop code for :custom hints. See the Essay on the Design of ; Custom Keyword Hints. (defun@par translate-custom-keyword-hint (arg uterm2 ctx wrld state) ; We run the checker term for the associated custom keyword and handle ; any error it generates. But if no error is generated, the ; translation of arg (the user-supplied value for the custom keyword) ; is arg itself. ; Why do we not allow non-trivial translation of custom keyword hint ; values? The main reason is that custom keyword hints do not see the ; translated values of common standard hints so why should they expect ; to see the translated values of custom hints? While the author of ; custom keyword :key1 might like its argument to be translated, he ; probably doesn't want to know about the translated form of other ; custom keyword values. Finally, when custom keyword hints generate ; new hints, they cannot be expected to translate their values. And ; if they didn't translate their values then after one round of custom ; hint evaluation we could have a mix of translated and untranslated ; hint values: standard hints would not be translated -- no user wants ; to know the internal form of lmi's or theories! -- and some custom ; hint values would be translated and others wouldn't. Furthermore, ; it is impossible to figure out which are which. The only solution ; is to keep everything in untranslated form. Example: ; Let :key1, :key2, and :key3 be custom keywords and suppose the user ; wrote the hint ; :key1 val1 :key2 val2 :in-theory (enable foo) ; If we allowed non-trivial translation of custom hints, then at ; translate-time we'd convert that to ; :key1 val1' :key2 val2' :in-theory (enable foo) ; Note the mix. Then at prove-time we'd run :key1's generator on ; val1' and the whole hint. It might return ; :key2 val2' :key3 val3 :in-theory (enable foo) ; Note the additional mix. We can't tell what's untranslated and what ; isn't, unless we made custom hint authors translate all custom ; hints, even those they don't "own." (er-progn@par (xtrans-eval@par #-acl2-par uterm2 #+acl2-par (serial-first-form-parallel-second-form@par uterm2 (if (equal uterm2 '(value t)) t uterm2)) (list (cons 'val arg) (cons 'world wrld) (cons 'ctx ctx)) t ; trans-flg t ; ev-flg ctx state t) (value@par arg))) (defun custom-keyword-hint (key wrld) ; If key is a custom keyword hint, we return (mv t ugterm ucterm); else ; (mv nil nil nil). The terms are untranslated. (let ((temp (assoc-eq key (table-alist 'custom-keywords-table wrld)))) (cond (temp (mv t (car (cdr temp)) (cadr (cdr temp)))) (t (mv nil nil nil))))) (defun remove-all-no-ops (key-val-lst) (cond ((endp key-val-lst) nil) ((eq (car key-val-lst) :no-op) (remove-all-no-ops (cddr key-val-lst))) (t (cons (car key-val-lst) (cons (cadr key-val-lst) (remove-all-no-ops (cddr key-val-lst))))))) (defun remove-redundant-no-ops (key-val-lst) ; We return a keyword value list equivalent to key-val-lst but ; containing at most one :NO-OP setting on the front. We don't even ; add that unless the hint would be empty otherwise. The associated ; value is always T, no matter what the user wrote. ; (:INDUCT term :NO-OP T :IN-THEORY x :NO-OP NIL) ; => (:INDUCT term :IN-THEORY x) ; (:NO-OP 1 :NO-OP 2) => (:NO-OP T) (cond ((assoc-keyword :no-op key-val-lst) (let ((temp (remove-all-no-ops key-val-lst))) (cond (temp temp) (t '(:no-op t))))) (t key-val-lst))) (defun find-first-custom-keyword-hint (user-hints wrld) ; User-hints is a keyword value list of the form (:key1 val1 :key2 ; val2 ...). We look for the first :keyi in user-hints that is a ; custom keyword hint, and if we find it, we return (mv keyi vali ; uterm1 uterm2), where uterm1 is the untranslated generator for keyi ; and uterm2 is the untranslated checker. (cond ((endp user-hints) (mv nil nil nil nil)) (t (mv-let (flg uterm1 uterm2) (custom-keyword-hint (car user-hints) wrld) (cond (flg (mv (car user-hints) (cadr user-hints) uterm1 uterm2)) (t (find-first-custom-keyword-hint (cddr user-hints) wrld))))))) (defconst *custom-keyword-max-iterations* 100) (defun@par custom-keyword-hint-interpreter1 (keyword-alist max specified-id id clause wrld stable-under-simplificationp hist pspv ctx state keyword-alist0 eagerp) ; On the top-level call, keyword-alist must be known to be a keyword ; value list, e.g., (:key1 val1 ... keyn valn). On subsequent calls, ; that is guaranteed. This function returns an error triple ; (mv erp val state). But a little more than usual is being passed ; back in the erp=t case. ; If erp is nil: val is either nil, meaning that the custom keyword ; hint did not apply or is a new keyword-alist to be used as the hint. ; That hint will be subjected to standard hint translation. ; If erp is t, then an error has occurred and the caller should abort ; -- UNLESS it passed in eagerp=t and the returned val is the symbol ; WAIT. If eagerp is t we are trying to evaluate the custom keyword ; hint at pre-process time rather than proof time and don't have ; bindings for some variables. In that case, an ``error'' is signaled ; with erp t but the returned val is the symbol WAIT, meaning it was ; impossible to eagerly evaluate this form. (cond ((equal specified-id id) ; This is the clause to which this hint applies. (mv-let (keyi vali uterm1 uterm2) (find-first-custom-keyword-hint keyword-alist wrld) (cond ((null keyi) ; There are no custom keyword hints in the list. In this case, ; we're done and we return keyword-alist. (value@par keyword-alist)) ((zp max) (er@par soft ctx "We expanded the custom keyword hints in ~x0 a total of ~x1 times ~ and were still left with a hint containing custom keywords, namely ~ ~x2." keyword-alist0 *custom-keyword-max-iterations* keyword-alist)) (t (let ((checker-bindings (list (cons 'val vali) (cons 'world wrld) (cons 'ctx ctx)))) (er-progn@par (xtrans-eval@par #-acl2-par uterm2 ; Parallelism wart: Deal with the following comment, which appears out of date ; as of 2/4/2012. ; The following change doesn't seem to matter when we run our tests. However, ; we include it, because from looking at the code, David Rager perceives that ; it can't hurt and that it might help. It may turn out that the change to ; translate-custom-keyword-hint (which performs a similar replacement), ; supercedes this change, because that occurs earlier in the call stack (before ; the waterfall). David Rager suspects that the call to ; custom-keyword-hint-interpreter1@par is used inside the waterfall (perhaps ; when the custom keyword hint process it told to 'wait and deal with the hint ; later). If that is the case, then this replacement is indeed necessary! #+acl2-par (serial-first-form-parallel-second-form@par uterm2 (if (equal uterm2 '(value t)) t uterm2)) checker-bindings t ; trans-flg = t t ; ev-flg = t ctx state t) ; We just evaluated the checker term and it did not cause an error. ; We ignore its value (though er-let* doesn't). (mv-let@par (erp val state) (xtrans-eval@par uterm1 (cond (eagerp ; We are trying to evaluate the generator eagerly. That means that ; our given values for some dynamic variables, CLAUSE, ; STABLE-UNDER-SIMPLIFICATIONP, HIST, and PSPV are bogus. We thus ; don't pass them in and we tell xtrans-eval it doesn't really have to ; ev the term if it finds unbound vars. (list* (cons 'keyword-alist keyword-alist) (cons 'id id) ; (cons 'clause clause) ; bogus ; (cons 'stable-under-simplificationp ; stable-under-simplificationp) ; (cons 'hist hist) ; (cons 'pspv pspv) checker-bindings)) (t ; Otherwise, we want all the bindings: (list* (cons 'keyword-alist keyword-alist) (cons 'id id) (cons 'clause clause) ; bogus (cons 'stable-under-simplificationp stable-under-simplificationp) (cons 'hist hist) (cons 'pspv pspv) checker-bindings))) t ; trans-flg (if eagerp nil t) ; ev-flg ctx state t) (cond (erp ; If an error was caused, there are two possibilities. One is that ; the form actually generated an error. But the other is that we were ; trying eager evaluation with insufficient bindings. That second ; case is characterized by eagerp = t and val = WAIT. In both cases, ; we just pass it up. (mv@par erp val state)) ; If no error was caused, we check the return value for our invariant. ((not (keyword-value-listp val)) (er@par soft ctx "The custom keyword hint ~x0 in the context below generated a ~ result that is not of the form (:key1 val1 ... :keyn valn), ~ where the :keyi are keywords. The context is ~y1, and the ~ result generated was ~y2." keyi keyword-alist val)) (t ; We now know that val is a plausible new keyword-alist and replaces ; the old one. (pprogn@par (cond ((f-get-global 'show-custom-keyword-hint-expansion state) (io?@par prove nil state (keyi id keyword-alist val) (fms "~%(Advisory from ~ show-custom-keyword-hint-expansion: The ~ custom keyword hint ~x0, appearing in ~@1, ~ transformed~%~%~Y23,~%into~%~%~Y43.)~%" (list (cons #\0 keyi) (cons #\1 (tilde-@-clause-id-phrase id)) (cons #\2 (cons (string-for-tilde-@-clause-id-phrase id) keyword-alist)) (cons #\3 (term-evisc-tuple nil state)) (cons #\4 (cons (string-for-tilde-@-clause-id-phrase id) val))) (proofs-co state) state nil))) (t (state-mac@par))) (custom-keyword-hint-interpreter1@par val (- max 1) specified-id id clause wrld stable-under-simplificationp hist pspv ctx state keyword-alist0 eagerp))))))))))) (t (value@par nil)))) (defun@par custom-keyword-hint-interpreter (keyword-alist specified-id id clause wrld stable-under-simplificationp hist pspv ctx state eagerp) ; Warning: If you change or rearrange the arguments of this function, ; be sure to change custom-keyword-hint-in-computed-hint-form and ; put-cl-id-of-custom-keyword-hint-in-computed-hint-form. ; This function evaluates the custom keyword hints in keyword-alist. ; It either signals an error or returns as the value component of its ; error triple a new keyword-alist. ; Eagerp should be set to t if this is an attempt to expand the custom ; keyword hints at pre-process time. If eagerp = t, then it is ; assumed that CLAUSE, STABLE-UNDER-SIMPLIFICATIONP, HIST, and ; PSPV are bogus (nil). ; WARNING: This function should be called from an mv-let, not an ; er-let*! The erroneous return from this function should be handled ; carefully when eagerp = t. It is possible in that case that the ; returned value, val, of (mv t erp state), is actually the symbol ; WAIT. This means that during the eager expansion of some custom ; keyword hint we encountered a hint that required the dynamic ; variables. It is not strictly an error, i.e., the caller shouldn't ; abort. (custom-keyword-hint-interpreter1@par keyword-alist *custom-keyword-max-iterations* specified-id id clause wrld stable-under-simplificationp hist pspv ctx state keyword-alist eagerp)) (defun custom-keyword-hint-in-computed-hint-form (computed-hint-tuple) ; Note: Keep this in sync with eval-and-translate-hint-expression. ; That function uses the AND test below but not the rest, because it ; is dealing with the term itself, not the tuple. ; We assume computed-hint-tuple is the internal form of a computed ; hint. If it is a custom keyword hint, we return the non-nil keyword ; alist supplied by the user. Otherwise, nil. ; A translated computed hint has the form ; (EVAL-AND-TRANSLATE-HINT-EXPRESSION name-tree stablep term) and we ; assume that computed-hint-tuple is of that form. A custom keyword ; hint is coded as a computed hint, where term, above, is ; (custom-keyword-hint-interpreter '(... :key val ...) 'cl-id ...) ; We insist that the keyword alist is a quoted constant (we will ; return its evg). We also insist that the cl-id is a quoted ; constant. (let ((term (nth 3 computed-hint-tuple))) (cond ((and (nvariablep term) (not (fquotep term)) ; Parallelism blemish: we do not believe that the quoting below of ; "custom-keyword-hint-interpreter@par" is a problem (as compared to the serial ; case). One can issue a tags search for 'custom-keyword-hint-interpreter, and ; find some changed comparisons. We believe that Matt K. and David R. began to ; look into this, and we were not aware of any problems, so we have decided not ; to try to think it all the way through. (serial-first-form-parallel-second-form@par (eq (ffn-symb term) 'custom-keyword-hint-interpreter) (or (eq (ffn-symb term) 'custom-keyword-hint-interpreter) (eq (ffn-symb term) 'custom-keyword-hint-interpreter@par))) (quotep (fargn term 1)) (quotep (fargn term 2))) (cadr (fargn term 1))) (t nil)))) (defun@par put-cl-id-of-custom-keyword-hint-in-computed-hint-form (computed-hint-tuple cl-id) ; We assume the computed-hint-tuple is a computed hint tuple and has ; passed custom-keyword-hint-in-computed-hint-form. We set the cl-id ; field to cl-id. This is only necessary in order to fix the cl-id ; for :or hints, which was set for the goal to which the :or hint was ; originally attached. (let ((term (nth 3 computed-hint-tuple))) (list 'eval-and-translate-hint-expression (nth 1 computed-hint-tuple) (nth 2 computed-hint-tuple) (fcons-term* (serial-first-form-parallel-second-form@par 'custom-keyword-hint-interpreter 'custom-keyword-hint-interpreter@par) (fargn term 1) (kwote cl-id) (fargn term 3) (fargn term 4) (fargn term 5) (fargn term 6) (fargn term 7) (fargn term 8) (fargn term 9) (fargn term 10) (fargn term 11))))) (defun make-disjunctive-clause-id (cl-id i pkg-name) (change clause-id cl-id :case-lst (append (access clause-id cl-id :case-lst) (list (intern$ (coerce (packn1 (list 'd i)) 'string) pkg-name))) :primes 0)) (defun make-disjunctive-goal-spec (str i pkg-name) (let ((cl-id (parse-clause-id str))) (string-for-tilde-@-clause-id-phrase (make-disjunctive-clause-id cl-id i pkg-name)))) (defun minimally-well-formed-or-hintp (val) (cond ((atom val) (equal val nil)) (t (and (consp (car val)) (true-listp (car val)) (evenp (length (car val))) (minimally-well-formed-or-hintp (cdr val)))))) (defun split-keyword-alist (key keyword-alist) (cond ((endp keyword-alist) (mv nil nil)) ((eq key (car keyword-alist)) (mv nil keyword-alist)) (t (mv-let (pre post) (split-keyword-alist key (cddr keyword-alist)) (mv (cons (car keyword-alist) (cons (cadr keyword-alist) pre)) post))))) (defun distribute-other-hints-into-or1 (pre x post) (cond ((endp x) nil) (t (cons (append pre (car x) post) (distribute-other-hints-into-or1 pre (cdr x) post))))) (defun distribute-other-hints-into-or (keyword-alist) ; We know keyword-alist is a keyword alist, that there is exactly one :OR, and ; that the value, val, of that :OR is a true-list of non-empty ; true-lists, each of which is of even length. We distribute the ; other hints into the :OR. Thus, given: ; (:in-theory a :OR ((:use l1) (:use l2)) :do-not '(...)) ; we return: ; ((:OR ((:in-theory a :use l1 :do-not '(...)) ; (:in-theory a :use l2 :do-not '(...))))) (mv-let (pre post) (split-keyword-alist :OR keyword-alist) (list :OR (distribute-other-hints-into-or1 pre (cadr post) (cddr post))))) (defconst *hint-expression-basic-vars* '(id clause world stable-under-simplificationp hist pspv ctx state)) (defconst *hint-expression-override-vars* (cons 'keyword-alist *hint-expression-basic-vars*)) (defconst *hint-expression-backtrack-vars* (append '(clause-list processor) (remove1-eq 'stable-under-simplificationp *hint-expression-basic-vars*))) (defconst *hint-expression-all-vars* (union-equal *hint-expression-override-vars* (union-equal *hint-expression-backtrack-vars* *hint-expression-basic-vars*))) (defun@par translate-hint-expression (name-tree term hint-type ctx wrld state) ; Term can be either (a) a non-variable term or (b) a symbol. ; (a) We allow a hint of the form term, where term is a term single-threaded in ; state that returns a single non-stobj value or an error triple and contains ; no free vars other than ID, CLAUSE, WORLD, STABLE-UNDER-SIMPLIFICATIONP, ; HIST, PSPV, CTX, and STATE, except that if if hint-type is non-nil then there ; may be additional variables. ; ; If term is such a term, we return the translated hint: ; (EVAL-AND-TRANSLATE-HINT-EXPRESSION name-tree flg term') ; where term' is the translation of term and flg indicates whether ; STABLE-UNDER-SIMPLIFICATIONP occurs freely in it. ; (b) We also allow term to be a symbol denoting a 3, 4, or 7 argument function ; not involving state and returning a single value taking: ; (i) a clause-id, a clause, and world, or, ; (ii) a clause-id, a clause, world, and ; stable-under-simplificationp, or ; (iii) a clause-id, a clause, world, ; stable-under-simplificationp, hist, pspv, and ctx. ; We ``translate'' such a function symbol into a call of the function on the ; appropriate argument variables. ; Here is a form that allows us to trace many of the functions related to ; translating hints. ; (trace$ ; (translate-hints+1) ; (translate-hints+1@par) ; (translate-hints2) ; (translate-hints2@par) ; (translate-hints1) ; (apply-override-hints@par) ; (apply-override-hints) ; (translate-x-hint-value) ; (translate-x-hint-value@par) ; (translate-custom-keyword-hint) ; (translate-custom-keyword-hint@par) ; (custom-keyword-hint-interpreter@par) ; (custom-keyword-hint-interpreter) ; (translate-simple-or-error-triple) ; (translate-simple-or-error-triple@par) ; (xtrans-eval) ; (xtrans-eval-with-ev-w) ; (eval-and-translate-hint-expression) ; (eval-and-translate-hint-expression@par) ; (translate-hint-expression@par) ; (translate-hint-expression) ; (translate-hints1@par) ; (waterfall) ; (find-applicable-hint-settings1) ; (find-applicable-hint-settings1@par) ; (xtrans-eval@par) ; (simple-translate-and-eval@par) ; (simple-translate-and-eval) ; (translate-hints) ; (translate-hints+) ; (thm-fn) ; (formal-value-triple) ; (formal-value-triple@par) ; (eval-clause-processor) ; (eval-clause-processor@par) ; (apply-top-hints-clause@par) ; (apply-top-hints-clause) ; (waterfall-step1) ; (waterfall-step1@par) ; (waterfall-step) ; (waterfall-step@par) ; (translate1) ; (translate1@par) ; (translate) ; (translate@par) ; (translate-doc) ; (translate-clause-processor-hint) ; (translate-clause-processor-hint@par) ; (translate1-cmp)) (cond ((symbolp term) (cond ((and (function-symbolp term wrld) (or (equal (arity term wrld) 3) (equal (arity term wrld) 4) (equal (arity term wrld) 7)) (all-nils (stobjs-in term wrld)) (not (eq term 'return-last)) ; avoid taking stobjs-out (equal (stobjs-out term wrld) '(nil))) (value@par (cond ((equal (arity term wrld) 3) (list 'eval-and-translate-hint-expression name-tree nil (formal-value-triple@par *nil* (fcons-term term '(id clause world))))) ((equal (arity term wrld) 4) (list 'eval-and-translate-hint-expression name-tree t (formal-value-triple@par *nil* (fcons-term term '(id clause world stable-under-simplificationp))))) (t (list 'eval-and-translate-hint-expression name-tree t (formal-value-triple@par *nil* (fcons-term term '(id clause world stable-under-simplificationp hist pspv ctx)))))))) (t (er@par soft ctx "When you give a hint that is a symbol, it must be a function ~ symbol of three, four or seven arguments (not involving STATE ~ or other single-threaded objects) that returns a single ~ value. The allowable arguments are ID, CLAUSE, WORLD, ~ STABLE-UNDER-SIMPLIFICATIONP, HIST, PSPV, and CTX. See :DOC ~ computed-hints. ~x0 is not such a symbol." term)))) (t (er-let*@par ((tterm (translate-simple-or-error-triple@par term ctx wrld state))) (let ((vars (all-vars tterm))) (cond ((subsetp-eq vars (case hint-type (backtrack *hint-expression-backtrack-vars*) (override *hint-expression-override-vars*) (otherwise *hint-expression-basic-vars*))) (value@par (list 'eval-and-translate-hint-expression name-tree (if (member-eq 'stable-under-simplificationp vars) t nil) tterm))) ((and (not hint-type) ; optimization (subsetp-eq vars *hint-expression-all-vars*)) (let ((backtrack-bad-vars (intersection-eq '(CLAUSE-LIST PROCESSOR) vars)) (override-bad-vars (intersection-eq '(KEYWORD-ALIST) vars))) (mv-let (bad-vars types-string) (cond (backtrack-bad-vars (cond (override-bad-vars (mv (append backtrack-bad-vars override-bad-vars) ":BACKTRACK hints or override-hints")) (t (mv backtrack-bad-vars ":BACKTRACK hints")))) (t (assert$ override-bad-vars ; see subsetp-eq call above (mv override-bad-vars "override-hints")))) (er@par soft ctx "The hint expression ~x0 mentions ~&1. But variable~#2~[ ~&2 ~ is~/s ~&2 are~] legal only for ~@3. See :DOC computed-hints." term vars bad-vars types-string)))) (t (mv-let (type-string legal-vars extra-doc-hint) (case hint-type (backtrack (mv ":BACKTRACK hint" *hint-expression-backtrack-vars* " and see :DOC hints for a discussion of :BACKTRACK ~ hints")) (override (mv "override-hint" *hint-expression-override-vars* " and see :DOC override-hints")) (otherwise (mv "Computed" *hint-expression-basic-vars* ""))) (er@par soft ctx "~@0 expressions may not mention any variable symbols other than ~ ~&1. See :DOC computed-hints~@2. But the hint expression ~x3 ~ mentions ~&4." type-string legal-vars extra-doc-hint term vars))))))))) (defun@par translate-backtrack-hint (name-tree arg ctx wrld state) (translate-hint-expression@par name-tree arg 'backtrack ctx wrld state)) (defun@par translate-rw-cache-state-hint (arg ctx wrld state) (declare (ignore wrld)) (cond ((member-eq arg *legal-rw-cache-states*) (value@par arg)) (t (er@par soft ctx "Illegal :rw-cache-state argument, ~x0 (should be ~v1)" arg *legal-rw-cache-states*)))) (mutual-recursion@par (defun@par translate-or-hint (name-tree str arg ctx wrld state) ; Arg is the value of the :OR key in a user-supplied hint settings, ; e.g., if the user typed: :OR ((:in-theory t1 :use lem1) (:in-theory ; t2 :use lem2)) then arg is ((:in-theory t1 :use lem1) (:in-theory t2 ; :use lem2)). The translated form of this is a list as long as arg ; in which each element of the translated list is a pair (orig ; . trans) where orig is what the user typed and trans is its ; translation as a hint-settings. (For example, the two theory ; expressions, t1 and t2, will be expanded into full runic ; theories.) We either cause an error or return (as the value ; component of an error/value/state triple) a list of such pairs. ; Note: str is the original goal-spec string to which this :OR was ; attached. ; Note: Unlike other hints, we do some additional translation of :OR ; hints on the output of this function! See translate-hint. (cond ((atom arg) (if (null arg) (value@par nil) (er@par soft ctx "An :OR hint must be a true-list."))) (t (er-let*@par ((val (translate-hint@par name-tree (cons (make-disjunctive-goal-spec str (length arg) (current-package state)) (car arg)) nil ctx wrld state)) (tl (translate-or-hint@par name-tree str (cdr arg) ctx wrld state))) ; Val is either a translated computed hint expression, whose car ; is eval-and-translate-hint-expression, or else it is a pair of ; the form (cl-id . hint-settings), where cl-id was derived from ; str. (cond ((eq (car val) 'eval-and-translate-hint-expression) (value@par (cons (cons (car arg) val) tl))) (t ; If val is (cl-id . hint-settings), we just let val be hint-settings ; below, as the cl-id is being managed by the :OR itself. (let ((val (cdr val))) (value@par (cons (cons (car arg) val) tl))))))))) (defun@par translate-hint-settings (name-tree str key-val-lst ctx wrld state) ; We assume that key-val-lst is a list of :keyword/value pairs, (:key1 ; val1 ... :keyn valn), and that each :keyi is one of the acceptable ; hint keywords. We convert key-val-lst to alist form, ((:key1 . ; val1') ... (:keyn . valn')), where each vali' is the translated form ; of vali. ; Str is the goal-spec string identifying the clause to which these ; hints are attached. (cond ((null key-val-lst) (value@par nil)) ((and (eq (car key-val-lst) :use) (eq (cadr key-val-lst) nil)) ; We allow empty :use hints, but we do not want to have to think about ; how to process them. (translate-hint-settings@par name-tree str (cddr key-val-lst) ctx wrld state)) (t (er-let*@par ((val (translate-x-hint-value@par name-tree str (car key-val-lst) (cadr key-val-lst) ctx wrld state)) (tl (translate-hint-settings@par name-tree str (cddr key-val-lst) ctx wrld state))) (value@par (cons (cons (car key-val-lst) val) tl)))))) (defun@par translate-x-hint-value (name-tree str x arg ctx wrld state) ; Str is the goal-spec string identifying the clause to which this ; hint was attached. (mv-let (flg uterm1 uterm2) (custom-keyword-hint x wrld) (declare (ignore uterm1)) (cond (flg (translate-custom-keyword-hint@par arg uterm2 ctx wrld state)) (t (case x (:expand (translate-expand-hint@par arg ctx wrld state)) (:restrict (translate-restrict-hint@par arg ctx wrld state)) (:hands-off (translate-hands-off-hint@par arg ctx wrld state)) (:do-not-induct (translate-do-not-induct-hint@par arg ctx wrld state)) (:do-not (translate-do-not-hint@par arg ctx state)) (:use (translate-use-hint@par arg ctx wrld state)) (:or (translate-or-hint@par name-tree str arg ctx wrld state)) (:cases (translate-cases-hint@par arg ctx wrld state)) (:case-split-limitations (translate-case-split-limitations-hint@par arg ctx wrld state)) (:by (translate-by-hint@par name-tree arg ctx wrld state)) (:induct (translate-induct-hint@par arg ctx wrld state)) (:in-theory (translate-in-theory-hint@par arg t ctx wrld state)) (:bdd (translate-bdd-hint@par arg ctx wrld state)) (:clause-processor (translate-clause-processor-hint@par arg ctx wrld state)) (:nonlinearp (translate-nonlinearp-hint@par arg ctx wrld state)) (:no-op (translate-no-op-hint@par arg ctx wrld state)) (:no-thanks (translate-no-thanks-hint@par arg ctx wrld state)) (:reorder (translate-reorder-hint@par arg ctx wrld state)) (:backtrack (translate-backtrack-hint@par name-tree arg ctx wrld state)) (:backchain-limit-rw (translate-backchain-limit-rw-hint@par arg ctx wrld state)) (:error ; We know this case never happens. The error is caught and signalled ; early by translate-hint. But we include it here to remind us that ; :error is a legal keyword. In fact the semantics given here -- ; which causes an immediate error -- is also consistent with the ; intended interpretation of :error. (translate-error-hint@par arg ctx wrld state)) (:rw-cache-state (translate-rw-cache-state-hint@par arg ctx wrld state)) (otherwise (mv@par (er hard 'translate-x-hint-value "The object ~x0 not recognized as a legal hint keyword. See :DOC ~ hints." x) nil state))))))) (defun replace-goal-spec-in-name-tree1 (name-tree goal-spec) (cond ((atom name-tree) (cond ((and (stringp name-tree) (parse-clause-id name-tree)) (mv t goal-spec)) (t (mv nil name-tree)))) (t (mv-let (flg1 name-tree1) (replace-goal-spec-in-name-tree1 (car name-tree) goal-spec) (cond (flg1 (mv t (cons name-tree1 (cdr name-tree)))) (t (mv-let (flg2 name-tree2) (replace-goal-spec-in-name-tree1 (cdr name-tree) goal-spec) (mv flg2 (cons (car name-tree) name-tree2))))))))) (defun replace-goal-spec-in-name-tree (name-tree goal-spec) ; Name-trees are trees of strings and symbols used to generate ; meaningful names for :by hints. Typically, a name tree will have at ; most one goal spec in it, e.g., (name . "Subgoal *1/3") or ; ("Computed hint auto-generated for " (name . "Subgoal *1/3")). We ; search nametree for the first occurrence of a goal spec and replace ; that goal spec by the given one. This is an entirely heuristic ; operation. ; Why do we do this? Suppose an :OR hint is attached to a given goal ; spec and we have a name tree corresponding to that goal spec. To process ; the :OR we will produce a copy of the goal and rename the goal spec ; by adding a "Dj" suffice. We want to replace the original goal spec ; in the name-tree by this modified goal spec. (mv-let (flg new-name-tree) (replace-goal-spec-in-name-tree1 name-tree goal-spec) (cond (flg new-name-tree) (t (cons name-tree goal-spec))))) (defun@par translate-hint (name-tree pair hint-type ctx wrld state) ; Pair is supposed to be a "hint", i.e., a pair of the form (str :key1 ; val1 ... :keyn valn). We check that it is, that str is a string ; that parses to a clause-id, and that each :keyi is a legal hint ; keyword. Then we translate pair into a pair of the form (cl-id . ; hint-settings), where cl-id is the parsed clause-id and ; hint-settings is the translated alist form of the key/val lst above. ; We try to eliminate custom keyword hints by eager expansion. If we ; cannot eliminate all custom hints, we do check that the individual ; :keyi vali pairs are translatable (which, in the case of the custom ; hints among them, means we run their checkers) but we ignore the ; translations. We then convert the entire hint into a computed hint. ; We return a standard error triple. If no custom keyword hints ; appear (or if all custom hints could be eagerly eliminated), the ; value is (cl-id . hint-settings). If an un-eliminable custom ; keyword hint appears, the value is the translated form of a computed ; hint -- with the original version of the hint appearing in it as a ; quoted constant. ; Thus, if the car of the returned value is the word ; 'eval-and-translate-hint-expression the answer is a translated ; computed hint, otherwise it is of the form (cl-id . hint-settings). (cond ((not (and (consp pair) (stringp (car pair)) (keyword-value-listp (cdr pair)))) (er@par soft ctx "Each hint is supposed to be a list of the form (str :key1 val1 ~ ... :keyn valn), but a proposed hint, ~x0, is not. See :DOC ~ hints." pair)) (t (let ((cl-id (parse-clause-id (car pair)))) (cond ((null cl-id) (er@par soft ctx "The object ~x0 is not a goal-spec. See :DOC hints and :DOC ~ goal-spec." (car pair))) ((assoc-keyword :error (cdr pair)) ; If an :error hint was given, we immediately cause the requested error. ; Note that we thus allow :error hints to occur multiple times and just ; look at the first one. If we get past this test, there are no ; :error hints. (translate-error-hint@par (cadr (assoc-keyword :error (cdr pair))) ctx wrld state)) (t (mv-let (keyi vali uterm1 uterm2) (find-first-custom-keyword-hint (cdr pair) wrld) (declare (ignore vali uterm1 uterm2)) (cond (keyi ; There is a custom keyword among the keys. One of two possibilities ; exists. The first is that the hint can be expanded statically ; (``eagerly'') now. The second is that the hint is truly sensitive ; to dynamically determined variables like the variables CLAUSE, HIST, ; STABLE-UNDER-SIMPLIFICATIONP, or PSPV and must consequently be ; treated essentially as a computed hint. But there is no way to find ; out except by trying to evaluate it! That is because even if this ; hint involves none of the dynamic variables it might be that the ; value it computes contains other custom keyword hints that do ; involve those variables. ; Note: Recall that the interpreter runs the checker on a val before ; it runs the generator. So each generator knows that its val is ok. ; But generators do not know that all vals are ok. That is, a ; generator cannot assume that a common hint has a well-formed val or ; that other custom hints have well-formed vals. (mv-let@par (erp val state) (custom-keyword-hint-interpreter@par (cdr pair) cl-id cl-id NIL wrld NIL NIL NIL ctx state t) ; The four NILs above are bogus values for the dynamic variables. The ; final t is the eagerp flag which will cause the interpreter to ; signal the WAIT ``error'' if the expansion fails because of some ; unbound dynamic variable. (cond (erp (cond ((eq val 'WAIT) ; In this case, we must treat this as a computed hint so we will ; manufacture an appropriate one. As a courtesy to the user, we will ; check that all the hints are translatable. But we ignore the ; translations because there is no way to know whether they are ; involved in the actual hint that will be generated by the processing ; of these custom hints when the subgoal arises. (er-let*@par ((hint-settings (translate-hint-settings@par (replace-goal-spec-in-name-tree name-tree (car pair)) (car pair) (cdr pair) ctx wrld state))) ; Note: If you ever consider not ignoring the translated ; hint-settings, recognize how strange it is. E.g., it may have ; duplicate conflicting bindings of standard keys and pairs ; binding custom keywords to their untranslated values, a data ; structure we never use. (translate-hint-expression@par name-tree ; Below we generate a standard computed hint that uses the ; interpreter. Note that the interpreter is given the eagerp ; NIL flag. (serial-first-form-parallel-second-form@par `(custom-keyword-hint-interpreter ',(cdr pair) ',cl-id ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX STATE 'nil) `(custom-keyword-hint-interpreter@par ',(cdr pair) ',cl-id ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX STATE 'nil)) hint-type ctx wrld state))) (t (mv@par t nil state)))) (t ; In this case, we have eliminated all custom keyword hints ; eagerly and val is a keyword alist we ought to ; use for the hint. We translate it from scratch. (translate-hint@par name-tree (cons (car pair) val) hint-type ctx wrld state))))) (t ; There are no custom keywords in the hint. (let* ((key-val-lst (remove-redundant-no-ops (cdr pair))) ; By stripping out redundant :NO-OPs now we allow such lists as (:OR x ; :NO-OP T), whereas normally :OR would "object" to the presence of ; another hint. (keys (evens key-val-lst)) (expanded-hint-keywords (append (strip-cars (table-alist 'custom-keywords-table wrld)) *hint-keywords*))) (cond ((null keys) (er@par soft ctx "There is no point in attaching the empty list of ~ hints to ~x0. We suspect that you have made a ~ mistake in presenting your hints. See :DOC hints. ~ ~ If you really want a hint that changes nothing, ~ use ~x1." (car pair) (cons (car pair) '(:NO-OP T)))) ((not (subsetp-eq keys expanded-hint-keywords)) (er@par soft ctx "The legal hint keywords are ~&0. ~&1 ~ ~#1~[is~/are~] unrecognized. See :DOC hints." expanded-hint-keywords (set-difference-eq keys expanded-hint-keywords))) ((member-eq :computed-hints-replacement keys) ; If translate-hint is called correctly, then we expect this case not to arise ; for well-formed hints. For example, in eval-and-translate-hint-expression we ; remove an appropriate use of :computed-hints-replacement. (er@par soft ctx "The hint keyword ~x0 has been used incorrectly. ~ Its only appropriate use is as a leading hint ~ keyword in computed hints. See :DOC computed-hints." :computed-hints-replacement)) ((not (no-duplicatesp-equal keys)) (er@par soft ctx "You have duplicate occurrences of the hint keyword ~ ~&0 in your hint. While duplicate occurrences of ~ keywords are permitted by CLTL, the semantics ~ ignores all but the left-most. We therefore ~ suspect that you have made a mistake in presenting ~ your hints." (duplicates keys))) ((and (assoc-keyword :OR (cdr pair)) (not (minimally-well-formed-or-hintp (cadr (assoc-keyword :OR (cdr pair)))))) ; Users are inclined to write hints like this: ; ("Goal" :OR ((...) (...)) :in-theory e) ; as abbreviations for ; ("Goal" :OR ((... :in-theory e) (... :in-theory e))) ; We implement this abbreviation below. But we have to know that the ; value supplied to the :OR is a list of non-empty true-lists of even ; lengths to insure that we can append the other hints to it and still ; get reasonable translation errors in the presence of ill-formed ; hints. If not, we cause an error now. We check the rest of the ; restrictions on :OR after the transformation. (er@par soft ctx "The value supplied to an :OR hint must be a ~ non-empty true-list of non-empty true-lists of even ~ length, i.e., of the form ((...) ...). But you ~ supplied the value ~x0." (cdr pair))) ((and (member-eq :induct keys) (member-eq :use keys)) (er@par soft ctx "We do not support the use of an :INDUCT hint with a ~ :USE hint. When a subgoal with an :INDUCT hint ~ arises, we push it for proof by induction. Upon ~ popping it, we interpret the :INDUCT hint to ~ determine the induction and we also install any ~ other non-:USE hints supplied. On the other hand, ~ when a subgoal with a :USE hint arises, we augment ~ the formula with the additional hypotheses supplied ~ by the hint. If both an :INDUCT and a :USE hint ~ were attached to the same subgoal we could either ~ add the hypotheses before induction, which is ~ generally detrimental to a successful induction, or ~ add them to each of the formulas produced by the ~ induction, which generally adds the hypotheses in ~ many more places than they are needed. We ~ therefore do neither and cause this neat, ~ informative error. You are encouraged to attach ~ the :INDUCT hint to the goal or subgoal to which ~ you want us to apply induction and then attach :USE ~ hints to the individual subgoals produced, as ~ necessary. For what it is worth, :INDUCT hints get ~ along just fine with hints besides :USE. For ~ example, an :INDUCT hint and an :IN-THEORY hint ~ would cause an induction and set the post-induction ~ locally enabled theory to be as specified by the ~ :IN-THEORY.")) ((and (member-eq :reorder keys) (intersectp-eq '(:or :induct) keys)) (cond ((member-eq :or keys) (er@par soft ctx "We do not support the use of a :REORDER hint with ~ an :OR hint. The order of disjunctive subgoals ~ corresponds to the list of hints given by the :OR ~ hint, so you may want to reorder that list ~ instead.")) (t (er@par soft ctx "We do not support the use of a :REORDER hint with ~ an :INDUCT hint. If you want this capability, ~ please send a request to the ACL2 implementors.")))) (t (let ((bad-keys (intersection-eq `(:induct ,@*top-hint-keywords*) keys))) (cond ((and (< 1 (length bad-keys)) (not (and (member-eq :use bad-keys) (member-eq :cases bad-keys) (equal 2 (length bad-keys))))) (er@par soft ctx "We do not support the use of a~#0~[n~/~] ~x1 ~ hint with a~#2~[n~/~] ~x3 hint, since they ~ suggest two different ways of replacing the ~ current goal by new goals. ~@4Which is it to ~ be? To summarize: A~#0~[n~/~] ~x1 hint ~ together with a~#2~[n~/~] ~x3 hint is not ~ allowed because the intention of such a ~ combination does not seem sufficiently clear." (if (member-eq (car bad-keys) '(:or :induct)) 0 1) (car bad-keys) (if (member-eq (cadr bad-keys) '(:or :induct)) 0 1) (cadr bad-keys) (cond ((and (eq (car bad-keys) :by) (eq (cadr bad-keys) :induct)) "The :BY hint suggests that the goal follows ~ from an existing theorem, or is to be ~ pushed. However, the :INDUCT hint provides ~ for replacement of the current goal by ~ appropriate new goals before proceeding. ") (t "")))) (t (er-let*@par ((hint-settings (translate-hint-settings@par (replace-goal-spec-in-name-tree name-tree (car pair)) (car pair) (cond ((assoc-keyword :or (cdr pair)) (distribute-other-hints-into-or (cdr pair))) (t (cdr pair))) ctx wrld state))) (cond ; Hint-settings is of the form ((:key1 . val1) ...(:keyn . valn)). ; If :key1 is :OR, we know n=1; translated :ORs always occur as ; singletons. But in ((:OR . val1)), val1 is always ; (((key . val) ...) ... ), i.e., is a list of alists. ; If there is only one alist in that list, then we're dealing ; with an :OR with only one disjunct. ((and (consp hint-settings) (eq (caar hint-settings) :OR) (consp (cdr (car hint-settings))) (null (cddr (car hint-settings)))) ; This is a singleton :OR. We just drop the :OR. (assert$ (null (cdr hint-settings)) (value@par (cons cl-id (car (cdr (car hint-settings))))))) (t (value@par (cons cl-id hint-settings)))))))))))))))))))) ) (defun@par translate-hint-expressions (name-tree terms hint-type ctx wrld state) ; This function translates a true-list of hint expressions. It is used when a ; hint generates a new list of hints. (cond ((endp terms) (cond ((equal terms nil) (value@par nil)) (t (er@par soft ctx "The value of the :COMPUTED-HINT-REPLACEMENT key must be NIL, ~ T, or a true list of terms. Your list ends in ~x0." terms)))) (t (er-let*@par ((thint (translate-hint-expression@par name-tree (car terms) hint-type ctx wrld state)) (thints (translate-hint-expressions@par name-tree (cdr terms) hint-type ctx wrld state))) (value@par (cons thint thints)))))) (defun@par check-translated-override-hint (hint uhint ctx state) (cond ((not (and (consp hint) (eq (car hint) 'eval-and-translate-hint-expression))) (er@par soft ctx "The proposed override-hint, ~x0, was not a computed hint. See ~ :DOC override-hints." uhint)) (t ; term is (caddr (cdr hint)); we allow any term here (value@par nil)))) (defun@par translate-hints1 (name-tree lst hint-type override-hints ctx wrld state) ; A note on the taxonomy of translated hints. A "hint setting" is a pair of ; the form (key . val), such as (:DO-NOT-INDUCT . T) or (:USE . (lmi-lst ; (h1...hn) ...)). Lists of such pairs are called "hint settings." A pair ; consisting of a clause-id and some hint-settings is called a "(translated) ; hint". A list of such pairs is called "(translated) hints." ; Thus, following the :HINTS keyword to defthm, the user types "hints" (in ; untranslated form). This function takes a lst, which is supposed be some ; hints, and translates it or else causes an error. ; Essay on the Handling of Override-hints ; When we translate an explicit (not computed) hint in the presence of at least ; one non-trivial override hint in the world, we append to the front of the ; hint-settings the list (:keyword-alist . x) (:name-tree . n), where x is the ; untranslated keyword-alist corresponding to hint-settings and n is the ; name-tree used for translation: so the hint is (goal-name (:keyword-alist ; . x) (:name-tree . n) (kwd1 . v1) ... (kwdk . vk)). Later, when we select ; the hint with find-applicable-hint-settings, we will see (:keyword-alist . x) ; and apply the override-hints to x, and if the result of apply-override-hints ; is also x, then we will return ((kwd1 . v1) ... (kwdk . vk)); otherwise we ; will translate that result. Note that in the special case that the original ; hint had at least one custom keyword hint but the ultimate resulting ; expansion was an explicit hint, the same technique will apply. Also note ; that the keyword :keyword-alist is illegal for users, and would be flagged as ; such by translate-hint in translate-hints1; so we have full control over the ; use of :keyword-alist (and similarly for :name-tree). ; If however the resulting translated hint is a computed hint, i.e. a list ; whose car is 'eval-and-translate-hint-expression, then no modification is ; necessary; function find-applicable-hint-settings takes care to apply ; override-hints, by calling function eval-and-translate-hint-expression with ; the override-hints supplied. ; And how about backtrack hints? Backtrack hints are computed hints, and ; receive the same treatment as described above, i.e. for computed hints ; selected by find-applicable-hint-settings -- namely, by passing the world's ; override-hints to eval-and-translate-hint-expression. (cond ((atom lst) (cond ((null lst) (value@par nil)) (t (er@par soft ctx "The :HINTS keyword is supposed to have a true-list as ~ its value, but ~x0 is not one. See :DOC hints." lst)))) ((and (consp (car lst)) (stringp (caar lst)) (null (cdar lst))) (translate-hints1@par name-tree (cdr lst) hint-type override-hints ctx wrld state)) (t (er-let*@par ((hint (cond ((and (consp (car lst)) (stringp (caar lst))) (translate-hint@par name-tree (car lst) hint-type ctx wrld state)) (t (translate-hint-expression@par name-tree (car lst) hint-type ctx wrld state)))) (rst (translate-hints1@par name-tree (cdr lst) hint-type override-hints ctx wrld state))) (er-progn@par (cond ((eq hint-type 'override) (check-translated-override-hint@par hint (car lst) ctx state)) (t (value@par nil))) (value@par (cons (cond ((atom hint) hint) ; nil ((and (consp (car lst)) (stringp (caar lst))) (cond (override-hints (list* (car hint) ; (caar lst) (cons :KEYWORD-ALIST (cdar lst)) (cons :NAME-TREE name-tree) (cdr hint))) (t hint))) ((eq (car hint) 'eval-and-translate-hint-expression) hint) (t (er hard ctx "Internal error: Unexpected ~ translation ~x0 for hint ~x1. ~ Please contact the ACL2 ~ implementors." hint (car lst)))) rst))))))) (defun@par warn-on-duplicate-hint-goal-specs (lst seen ctx state) (cond ((endp lst) (state-mac@par)) ((and (consp (car lst)) (stringp (caar lst))) (if (member-equal (caar lst) seen) (pprogn@par (warning$@par ctx ("Hints") "The goal-spec ~x0 is explicitly associated with ~ more than one hint. All but the first of these ~ hints may be ignored. If you intended to give ~ all of these hints, combine them into a single ~ hint of the form (~x0 :kwd1 val1 :kwd2 val2 ...). ~ ~ See :DOC hints-and-the-waterfall." (caar lst)) (warn-on-duplicate-hint-goal-specs@par (cdr lst) seen ctx state)) (warn-on-duplicate-hint-goal-specs@par (cdr lst) (cons (caar lst) seen) ctx state))) (t (warn-on-duplicate-hint-goal-specs@par (cdr lst) seen ctx state)))) (defun@par translate-hints2 (name-tree lst hint-type override-hints ctx wrld state) (cond ((warning-disabled-p "Hints") (translate-hints1@par name-tree lst hint-type override-hints ctx wrld state)) (t (er-let*@par ((hints (translate-hints1@par name-tree lst hint-type override-hints ctx wrld state))) (pprogn@par (warn-on-duplicate-hint-goal-specs@par lst nil ctx state) (value@par hints)))))) (defun override-hints (wrld) (declare (xargs :guard (and (plist-worldp wrld) (alistp (table-alist 'default-hints-table wrld))))) ":Doc-Section Miscellaneous a list of hints given priority in every proof attempt~/ This is an advanced feature, originally implemented to help system designers to create ``modes'' that control the way hints are supplied to the theorem prover. Please ~pl[default-hints] for the much more usual way to install hints that may be applied by default.~/ ~bv[] Examples: ACL2 !>(override-hints (w state)) ((computed-hint-1 clause keyword-alist processor) (computed-hint-2 clause keyword-alist stable-under-simplificationp)) ~ev[] ~c[Override-hints] returns a list of computed hints (~pl[computed-hints]) which, unlike other computed hints, may mention the variable ~c[KEYWORD-ALIST]. Before reading further, please ~pl[hints-and-the-waterfall] to review the basics of how ~il[hints] are applied during a proof. In particular, we assume familiarity with the notion of selecting a hint to be applied to the current goal. If there are override-hints, that hint selection is tentative, because if it reduced to ~c[nil] after the application of override-hints, then that hint will be skipped and the attempt will continue for selecting an applicable hint. (Craft your override-hints so that ~c[:no-op t] is returned in such cases instead of ~c[nil], if you don't want the hint to be skipped.) But we must explain what is meant by ``the application of override-hints'', and we do that now. Suppose that there are override-hints when a hint is selected for the current goal. That selected hint is a keyword-alist, which is an alternating list of hint keywords and their values, whose source is either an explicit hint ~c[(goal-name :key1 val1 ... :keyn valn)] where the ~c[:keyi] are allowed to be custom hint keywords (which are expanded away; ~pl[custom-keyword-hints]), or else is the non-~c[nil] keyword-alist produced by evaluating a computed hint. Then the override-hints are applied to that keyword-alist as follows, one at a time, in order of their occurrence in the list of override-hints (as determined by the use of ~ilc[set-override-hints] and ~ilc[add-override-hints]). The first override-hint is evaluated, in the usual manner of evaluating computed hints but with the variable ~c[KEYWORD-ALIST] bound to the above keyword-alist. That evaluation produces a result that should also be a keyword-alist, or else an error occurs. Any custom keyword hints are then eliminated from that keyword-alist. The resulting keyword-alist must not contain the ~c[:ERROR] hint keyword and must not start with the ~c[:COMPUTED-HINT-REPLACEMENT] keyword; otherwise an error occurs. With ~c[KEYWORD-ALIST] bound to this result, the second override-hint is similarly evaluated. This process continues, and the keyword-alist returned by the final override-hint is the one used when processing the goal at hand. Except: If that keyword-alist is ~c[nil], then the next hint among the pending hints is tentatively selected and the process repeats, applying each override hint to that new tentative selection. Of course we might obtain ~c[nil] again, in which case we tentatively select the next pending hint; and so on. If finally no hint is selected for the current goal, then ~c[KEYWORD-ALIST] is bound to ~c[nil] and the override-hints are applied as described above. But note that this final step is skipped if hint selection is being performed because ~c[stable-under-simplificationp] has just become true, rather than at the top of the waterfall. (Otherwise the override-hints could easily keep firing uselessly yet putting us back at the top of the waterfall, with no change to the given goal, resulting in an infinite loop.) As mentioned above, the ~c[:COMPUTED-HINT-REPLACEMENT] keyword is illegal for the value of an override-hint. But a selected hint may be a computed hint that evaluates to a keyword-alist beginning with prefix ~c[:COMPUTED-HINT-REPLACEMENT val]. What value does ACL2 return for such a computed hint in the presence of override-hints? First, this prefix is stripped off before passing the resulting keyword-alist to the override-hints as described above. If the result of applying override-hints to that keyword-alist is not ~c[nil], then the prefix is put back on the front of that resulting keyword-alist after doing internal processing of the hint, including expansion of any custom keyword hints. Otherwise, the application of override-hints to the computed hint is ~c[nil], so this hint is not selected after all. ~st[WARNING]: Unlike ordinary computed hints, a value of ~c[nil] for an override-hint is not ignored. That is: When an ordinary computed hint evaluates to ~c[nil], it is deemed not to apply, and the next available hint is consulted. But when an override-hint is evaluated, the result is always supplied for the next binding of the variable ~c[KEYWORD-ALIST], even if that result is ~c[nil]. If you want an override-hint to be a no-op, return as the expression the variable ~c[KEYWORD-ALIST] rather than an expression that evaluates to ~c[nil]. This feature can be used in order to implement a form of additive hints. Suppose for example that you want a hint that turns off generalization. A simple but inadequate solution is: ~bv[] (add-default-hints '((quote (:do-not '(generalize))))) ~ev[] The problem is that if there is any explicit hint supplied for a given goal, then it will be the one selected, and the above will be ignored. But suppose that the explicit hint supplied is of the form ~c[(\"Subgoal x.y\" :do-not '(fertilize))]. What we would really want in this case is to generate the hint for the indicated subgoal that binds ~c[:do-not] to a list indicating that both fertilization _and_ generalization are disabled for that goal. A solution is to merge, for example as follows. (The use of ~ilc[prog2$] and ~ilc[cw] is of course optional, included here to provide debug printing.) ~bv[] (add-override-hints '((let* ((tmp (assoc-keyword :do-not KEYWORD-ALIST)) (new-keyword-alist (cond (tmp (list* :do-not `(cons 'generalize ,(cadr tmp)) (remove-keyword :do-not KEYWORD-ALIST))) (t (list* :do-not ''(generalize) KEYWORD-ALIST))))) (prog2$ (cw \"New: ~~x0~~|\" new-keyword-alist) new-keyword-alist)))) ~ev[] ~sc[Remarks] (1) The utilities ~ilc[add-override-hints], ~ilc[add-override-hints!], ~ilc[set-override-hints], ~ilc[set-override-hints!], ~ilc[remove-override-hints], and ~ilc[remove-override-hints!] are also available, in complete analogy to their default-hints versions. (2) The community book ~c[hints/basic-tests.lisp] illustrates the use of override-hints and illuminates a number of corner cases; search in that file for ``Test override-hints.'' (3) The community book ~c[hints/merge-hint.lisp] provides support for merging hints that might be useful for writers of override-hint expressions (see the examples at the end of that file). (4) Override-hints are used in the processing of ~c[:BACKTRACK] hints (~pl[hints]). ~/ :cite set-override-hints :cite set-override-hints! :cite add-override-hints :cite add-override-hints! :cite remove-override-hints :cite remove-override-hints! " (cdr (assoc-eq :override (table-alist 'default-hints-table wrld)))) (defun@par translate-hints (name-tree lst ctx wrld state) (translate-hints2@par name-tree lst nil (override-hints wrld) ctx wrld state)) (defun@par translate-hints+1 (name-tree lst default-hints ctx wrld state) (cond ((not (true-listp lst)) (er@par soft ctx "The :HINTS keyword is supposed to have a true-list as its value, but ~ ~x0 is not one. See :DOC hints." lst)) (t (translate-hints@par name-tree (append lst default-hints) ctx wrld state)))) (defun translate-hints+ (name-tree lst default-hints ctx wrld state) #-acl2-par (translate-hints+1 name-tree lst default-hints ctx wrld state) #+acl2-par (if (f-get-global 'waterfall-parallelism state) (cmp-to-error-triple (translate-hints+1@par name-tree lst default-hints ctx wrld state)) (translate-hints+1 name-tree lst default-hints ctx wrld state))) (defun translate-override-hints (name-tree lst ctx wrld state) #-acl2-par (translate-hints2 name-tree lst 'override nil ; no override-hints are applied ctx wrld state) #+acl2-par (if (f-get-global 'waterfall-parallelism state) (cmp-to-error-triple (translate-hints2@par name-tree lst 'override nil ; no override-hints are applied ctx wrld state)) (translate-hints2 name-tree lst 'override nil ; no override-hints are applied ctx wrld state))) (defun@par apply-override-hint1 (override-hint cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor keyword-alist state) ; Apply the given override-hints to the given keyword-alist (for a hint) to ; obtain a resulting keyword alist. (let* ((tuple override-hint) (flg (cadr (cdr tuple))) (term (caddr (cdr tuple)))) (er-let*@par ((new-keyword-alist (xtrans-eval@par term (list* (cons 'id cl-id) (cons 'clause clause) (cons 'hist hist) (cons 'pspv pspv) (cons 'ctx ctx) (cons 'world wrld) (cons 'clause-list clause-list) (cons 'processor processor) (cons 'keyword-alist keyword-alist) (if flg (cons (cons 'stable-under-simplificationp stable-under-simplificationp) nil) nil)) ; #+ACL2-PAR note: we wish that we could have determined that the translation ; mentioned at the beginning of this function's definition was performed by ; translate-simple-or-error-triple@par (via a call to translate-hints+@par, ; which occurs before entering the waterfall). However, in the case of ; override hints, the translation really occurs when the override hint is added ; (perhaps via a call to "set-override-hints"). As such, even though we would ; like to check the output signature of the override hint, there is no way to ; do so without retranslating. We therefore disallow override hints whenever ; waterfall parallelism is enabled and waterfall-parallelism-hacks have not ; been enabled. nil ; trans-flg = nil because term is already translated t ; ev-flg = t because we have bound all the vars ctx state t))) (cond ((not (keyword-value-listp new-keyword-alist)) (er@par soft ctx "An override-hint, ~x0, has produced an illegal value from ~ keyword-alist ~x1. That value, ~x2, is illegal because it is not a ~ keyword-value-listp, i.e., an alternating list of keywords and ~ values." (untranslate term nil wrld) keyword-alist new-keyword-alist)) (t ; If an override-hint generates a hint with a custom keyword that is sensitive ; to stable-under-simplificationp, then that keyword will not have been ; expanded away at hint translation time. We deal with it now. The following ; example from Ian Johnson failed before invoking ; custom-keyword-hint-interpreter here. ; (set-state-ok t) ; (defun blah (val stable-under-simplificationp state) ; (declare (ignore val stable-under-simplificationp)) ; (value '(:in-theory (enable car-cons)))) ; (add-custom-keyword-hint :test ; (blah val stable-under-simplificationp state)) ; (defun ovrride (keyword-alist state) ; (let ((ret (append keyword-alist (list :test t)))) ; (prog2$ ; (cw "HINTS ~x0 ~%" ret) ; (if keyword-alist ; (value ret) ; (value nil))))) ; (add-override-hints (list '(ovrride keyword-alist state))) ; (thm (equal (* 4 (car (cons x x))) (* x 4)) ; :hints (("Goal" :in-theory (disable car-cons)))) (mv-let@par (erp new-keyword-alist state) (custom-keyword-hint-interpreter@par new-keyword-alist cl-id cl-id clause wrld stable-under-simplificationp hist pspv ctx state nil) (cond (erp (er@par soft ctx "An override-hint applied to ~@0 has generated an illegal custom ~ keyword hint, as reported above." (tilde-@-clause-id-phrase cl-id))) ((eq (car new-keyword-alist) :computed-hints-replacement) (er@par soft ctx "An override-hint, ~x0, has produced an illegal value from ~ keyword-alist ~x1. That value, ~x2, is illegal because it ~ begins with the keyword :COMPUTED-HINT-REPLACEMENT; see :DOC ~ override-hints." (untranslate term nil wrld) keyword-alist new-keyword-alist)) ((assoc-keyword :error new-keyword-alist) (translate-error-hint@par (cadr (assoc-keyword :error new-keyword-alist)) (msg "an override hint applied to ~@0" (tilde-@-clause-id-phrase cl-id)) wrld state)) (t (value@par new-keyword-alist))))))))) (defun@par apply-override-hint (override-hint cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor keyword-alist state) #-acl2-par (apply-override-hint1 override-hint cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor keyword-alist state) #+acl2-par (cond ((and (f-get-global 'waterfall-parallelism state) (not (cdr (assoc-eq 'hacks-enabled (table-alist 'waterfall-parallelism-table (w state)))))) (er@par soft ctx "Override-hints are not officially supported in ACL2(p). If you ~ wish to use override hints anyway, you can call ~x0. See :DOC ~ set-waterfall-parallelism-hacks-enabled for more information." '(set-waterfall-parallelism-hacks-enabled t))) (t (apply-override-hint1@par override-hint cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor keyword-alist state)))) (defun@par apply-override-hints (override-hints cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor keyword-alist state) ; Apply the given override-hints to the given keyword-alist (for a hint) to ; obtain a resulting keyword alist. (cond ((endp override-hints) (value@par keyword-alist)) (t (er-let*@par ((new-keyword-alist (apply-override-hint@par (car override-hints) cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor keyword-alist state))) (apply-override-hints@par (cdr override-hints) cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor new-keyword-alist state))))) (defun@par eval-and-translate-hint-expression (tuple cl-id clause wrld stable-under-simplificationp hist pspv clause-list processor keyword-alist hint-type override-hints ctx state) ; Tuple is of the form (name-tree flg term), where term is a translated ; single-threaded error-triple producing term that mentions, at most, the ; variables ID, CLAUSE, CLAUSE-LIST, PROCESSOR, KEYWORD-ALIST, WORLD, ; STABLE-UNDER-SIMPLIFICATIONP, HIST, PSPV, CTX, and STATE; and flg is a flag ; indicating whether the variable STABLE-UNDER-SIMPLIFICATIONP occurs freely in ; term. We eval term under the corresponding alist, obtaining a value val, and ; if val is non-erroneous and non-nil then we treat it as though it were a ; keyword-alist from an untranslated hint, i.e., (:key1 val1 ...), and ; translate it, using name-tree as the gensym name-tree for :bye hints. We ; return the translated hint-settings or nil. ; The above description is inaccurate in three respects. First, after the ; evaluation of term we restore proof related components of state. Our ; intention is that the user have state so the computed hint can, say, ; translate terms and print error messages. But we cannot prevent the user ; from abusing state and doing things like reading from files (which we can't ; undo) or changing the logical world with defun or defthm (which we can undo). ; So think of us as ignoring the state returned by the evaluation and reverting ; to the original one. ; Second, let's first remind ourselves that a computed hint gets to specify not ; just what the hint-settings is for this application but also gets to affect ; the hints that will be available later. A computed hint can direct the ; system to (a) remove itself from the hints after the application, (b) leave ; itself in after the application, or (c) replace itself with a list of other ; hints. This direction is provided by including the keyword ; :COMPUTED-HINT-REPLACEMENT and an associated value in the result, val, of the ; evaluation. ; The :COMPUTED-HINT-REPLACEMENT keyword and its value, chr, if provided, MUST ; BE THE FIRST two elements of val. ; The first paragraph is correct when val does not start with ; :COMPUTED-HINT-REPLACEMENT. Otherwise, val is of the form ; (:COMPUTED-HINT-REPLACEMENT chr . keyword-alist) and this is what we do. If ; keyword-alist is nil then we return (value nil). Otherwise, we treat ; keyword-alist as an untranslated hint-settings and translate it. We inspect ; chr to see whether it is (a) nil, (b) t, or (c) something else. The first ; two mean the hint is to be (a) deleted or (b) preserved. The last is ; understood as a list of terms to be be spliced into the hints in place of ; this one. But these terms must be translated and so we do that. Then we ; return (:COMPUTED-HINT-REPLACEMENT chr' . hint-settings), where chr' is the ; possibly translated chr and hint-settings' is the translated keyword-alist. ; It is left to our caller to interpret chr' and modify the hints ; appropriately. ; Finally the third inaccuracy of our initial description above is that it ; fails to account for override-hints. We apply the given override-hints if ; the computed hint returns a keyword-value-alistp that is non-nil even after ; stripping off a (:COMPUTED-HINT-REPLACEMENT val) prefix. (let* ((name-tree (car tuple)) (flg (cadr tuple)) (term (caddr tuple)) (custom-keyword-alist ; Keep this is sync with custom-keyword-hint-in-computed-hint-form. This ; variable is set to nil if this is an undistinguished computed hint and is set ; to non-nil if it is a custom keyword hint in computed hint form. The non-nil ; value is a hint keyword alist containing at least one custom keyword. (if (and (nvariablep term) (not (fquotep term)) (serial-first-form-parallel-second-form@par (eq (ffn-symb term) 'custom-keyword-hint-interpreter) (or (eq (ffn-symb term) 'custom-keyword-hint-interpreter) (eq (ffn-symb term) 'custom-keyword-hint-interpreter@par))) (quotep (fargn term 1)) (quotep (fargn term 2))) (cadr (fargn term 1)) nil))) ; The use of flg below might save a few conses. We do this only because we ; can. The real reason we have have the flg component in the computed hint ; tuple has to do with optimizing find-applicable-hint-settings. (er-let*@par ((val0 (xtrans-eval@par term (list* (cons 'id cl-id) (cons 'clause clause) (cons 'clause-list clause-list) (cons 'processor processor) (cons 'keyword-alist keyword-alist) (cons 'world wrld) (cons 'hist hist) (cons 'pspv pspv) (cons 'ctx ctx) (if flg (cons (cons 'stable-under-simplificationp stable-under-simplificationp) nil) nil)) nil ; trans-flg = nil because term is already translated t ; ev-flg = t because we have bound all the vars ctx state t))) (cond ((null val0) (value@par nil)) (t (er-let*@par ((str (value@par (string-for-tilde-@-clause-id-phrase cl-id))) (chr-p ; This is a reasonable place to catch a non-keyword-alist result. Before we ; had override-hints, we waited for the translate-hint call below. But ; override-hints expect keyword-alists, so we do our checking earlier now. (cond ((keyword-value-listp val0) (value@par (eq (car val0) :computed-hint-replacement))) (t (er@par soft ctx "A ~#0~[custom keyword~/computed~] hint for ~x1 has ~ produced a result that is not an alternating list of ~ keywords and values, (str :key1 val1 ... :keyn valn). ~ That result, ~x2, is thus illegal." (if custom-keyword-alist 0 1) str val0)))) (chr (cond ((null chr-p) (value@par :irrelevant)) ; chr is not used (custom-keyword-alist (er@par soft (msg "a custom keyword hint for ~x0" str) "The hint ~x0 produced a :COMPUTED-HINT-REPLACEMENT value as ~ part of its result. It is not permitted for custom keyword ~ hints to produce such a value (only computed hints are allowed ~ to do that). The result produced was ~x1." (cons str (cadr (fargn term 1))) val0)) ((not (consp (cdr val0))) (er@par soft (msg "a computed hint for ~x0: The computed hint ~% ~q1 produced ~ the non-nil result~%~y2. But this is an illegal value" str (untranslate term nil wrld) val0) "The :COMPUTED-HINT-REPLACEMENT keyword must be followed by a ~ list whose first element is NIL, T, or a list of terms. The ~ remaining elements are to be keyword/value pairs.")) ((or (eq (cadr val0) nil) (eq (cadr val0) t)) (value@par (cadr val0))) (t (translate-hint-expressions@par (cons "Computed hint auto-generated for " name-tree) (cadr val0) hint-type 'auto-generated-hint wrld state)))) (val1 (value@par (if chr-p (cddr val0) val0)))) (cond ((null val1) (value@par nil)) (t (er-let*@par ((val (cond ((and (keyword-value-listp val1) (assoc-keyword :ERROR val1)) ; If the hint produced an :ERROR as one of the keys of its result, then rather ; than translate the whole hint we pick out the :ERROR msg and print it ; directly. (translate-error-hint@par (cadr (assoc-keyword :ERROR val1)) (msg "a ~#0~[custom keyword~/computed~] hint for ~x1" (if custom-keyword-alist 0 1) str) wrld state)) (t (apply-override-hints@par override-hints cl-id clause hist pspv ctx wrld stable-under-simplificationp clause-list processor val1 state))))) (cond ((null val) (value@par nil)) (t (er-let*@par ((temp ; Explanation of the call of translate-hint below: The val computed is supposed ; to be of the form (:key1 val1 ...) and we need to check that it really is and ; translate it into the internal form of a hint-settings. We cons str onto the ; front of what we translate to create (str :key1 val1 ...) and then run it ; through the standard hint translator. That string is used in the name ; generated by :by. If no error occurs, we get back either ; (eval-and-translate-hint-expression ...) or (cl-id . hint-settings). The ; former is the translated form of a computed hint. The latter contains the ; translated hint settings we seek. We ignore the cl-id, which comes from the ; str we consed onto val. ; The msg below is the context of any error message generated by this ; translate-hint. It will be printed followed by a colon. (translate-hint@par name-tree (cons str val) hint-type (msg "a ~#0~[custom keyword~/computed~] hint for ~x1: The ~ ~#0~[custom keyword~/computed~] hint ~%~#0~[~x2 ~ ~/~q2~]produced the non-nil result~%~y3.~@4Regarding this ~ value" (if custom-keyword-alist 0 1) str (if custom-keyword-alist custom-keyword-alist (untranslate term nil wrld)) val0 (cond ((equal val val1) "") (t (msg "In turn, override-hints transformed these ~ hint-settings~#0~[ (without the leading ~ :COMPUTED-HINTS-REPLACEMENT value)~/~] into ~ ~x1. " (if (equal val0 val1) 1 0) val)))) wrld state)) (temp1 (cond ((eq (car temp) 'eval-and-translate-hint-expression) (eval-and-translate-hint-expression@par (cdr temp) cl-id clause wrld stable-under-simplificationp hist pspv clause-list processor keyword-alist hint-type nil ; we have already dealt with the override-hints ctx state)) (t (value@par (cdr temp)))))) (cond ((and chr-p (not (eq (car temp1) :computed-hint-replacement))) ; What if chr-p and (eq (car temp1) :computed-hint-replacement)? We take the ; value of the inner :computed-hint-replacement, but we could equally well take ; the outer value or cause an error instead. We have simply chosen the ; simplest alternative to code. (value@par (list* :computed-hint-replacement chr temp1))) (t (value@par temp1))))))))))))))) (deflabel goal-spec :doc ":Doc-Section Miscellaneous to indicate where a hint is to be used~/ ~bv[] Examples: \"Goal\" \"goal\" \"Subgoal *1/3''\" \"subgoal *1/3''\" \"[2]Subgoal *1/3''\" ~ev[]~/ When ~il[hints] are given to the theorem prover, a goal-spec is provided to specify the goal to which the ~il[hints] are to be applied. The ~il[hints] provided are carried along innocuously until the named goal arises. When it arises, the ~il[hints] are ``activated'' for that goal and its descendents. A legal goal specification may be extracted from the theorem prover's output. Certain lines clearly label formulas, as in ~bv[] Subgoal *1/3.2' (IMPLIES ... ...) ~ev[] and these lines all give rise to goal specifications. In general, these lines all start either with ``Goal'' or ``Subgoal'' or else with those words preceded by a number in square brackets, as in ~bv[] [1]Subgoal *1/3.2' (IMPLIES ... ...). ~ev[] A goal specification may be obtained by deleting any surrounding whitespace from such a line and embedding the text in string quotation marks. Thus ~bv[] \"[1]Subgoal *1/3.2'\" ~ev[] is the goal specifier for the goal above. As noted, a hint is applied to a goal when the hint's goal specification matches the name ACL2 assigns to the goal. The matching algorithm is case-insensitive. Thus, alternative goal specifications for the goal above are ~c[\"[1~]subgoal *1/3.2'\"] and ~c[\"[1~]SUBGOAL *1/3.2'\"]. The matching algorithm does not tolerate non-case discrepancies. Thus, ~c[\"[1~]Subgoal*1/3.2'\"] and ~c[\" [1~]Subgoal *1/3.2'\"] are unacceptable. Sometimes a formula is given two names, e.g., ~bv[] Subgoal *1/14.2' (IMPLIES ... ...) Name the formula above *1.1. ~ev[] It is the first name (the one that starts with ``Goal'' or ``Subgoal'') and not the second which constitutes a legal goal-spec. Roughly speaking, when the system prints the line containing the goal specification, it activates any ~il[hints] that are attached to that goal-spec. Consider the example above. Suppose ~c[Subgoal *1/14.2'] could be proved by using a certain lemma instance. Then the appropriate entry in the ~il[hints] would be: ~bv[] (\"Subgoal *1/14.2'\" :use ...) ~ev[] This might surprise you because the system appears to do nothing to ~c[*1/14.2'] besides push it for a subsequent induction. But actually between the time the system printed the goal-spec line and the time it decides to push the goal, you can think of the system as trying everything it has. So a ~c[use] hint activated when ~c[Subgoal *1/14.2'] arises is just what you want. But what if you want to give an ~c[:induct] hint? By the time induction is tried, the formula has been given the name ~c[*1.1]. Well, this is one you just have to remember: ~bv[] (\"Subgoal *1/14.2'\" :induct ...). ~ev[] When the above hint is activated the ~c[:induct] directive short-circuits the rest of the processing and sends immediately the formula into the pool of goals to prove by induction. The induct hint is attached to the formula in the pool and when the time comes to turn our attention to that goal, the induct advice is followed. We conclude by emphasizing a point made above, that a hint is applied to a goal when the hint's goal specification matches the name ACL2 assigns to the goal. If there is no such match, then the hint is ignored. Consider the following example. ~bv[] (thm (equal (append (append x y) z) (append x y z)) :hints ((\"Subgoal *1/\" :in-theory nil))) ~ev[] Normally, ~c[:in-theory] hints are inherited by subgoals (~pl[hints-and-the-waterfall]), so you might expect that the empty theory is used in ~c[Subgoal *1/2] and ~c[Subgoal *1/1]. But in fact, since there is no subgoal printed that is labeled ~c[Subgoal *1/], the above ~c[:in-theory] hint is ignored. The above example is in contrast to the following, where the hint makes the proof fail, because there really is a ~c[Subgoal *1/] in the proof this time. ~bv[] (thm (implies (and (not (endp x)) (not (endp (cdr x)))) (equal (append (append x y) z) (append x y z))) :hints ((\"Subgoal *1/\" :in-theory nil))) ~ev[]~/") (deflabel hints-and-the-waterfall :doc ":Doc-Section Miscellaneous how ~il[hints] fit into the ACL2 proof waterfall~/ Below we describe the flow of an ACL2 proof attempt, with special attention to how ~il[hints] are applied during a proof. For most ACL2 users, only one point is important to take away from this ~il[documentation] topic: you may specify hints during a proof (~pl[hints]; perhaps also ~pl[computed-hints] and ~pl[default-hints]), and they can be expected to behave intuitively. ~l[the-method] for a summary of how to interact with the ACL2 prover; ~pl[introduction-to-the-theorem-prover] for a more detailed tutorial; and ~pl[hints] for an introduction to ACL2 hints, including detailed ~il[documentation] for specific hint types. The remainder of this topic serves as a reference in case one needs a deeper understanding of the workings of ACL2's handling of hints. Also, for examples of the sophisticated use of hints, primarily for experts, see community book ~c[books/hints/basic-tests.lisp]. First, we describe the ACL2 ``waterfall'', which handles each goal either by replacing it with a list (possibly empty) of child goals, or else by putting the goal into a ``pool'' for later proof by induction. Then, we describe how hints are handled by the waterfall. ~st[The Waterfall.] Each goal considered by the ACL2 prover passes through a series of proof processes, called the ``waterfall processes'', as stored in the constant ~c[*preprocess-clause-ledge*]. The top process applies top-level hints, including ~c[:use] hints; the next is a lightweight ``preprocess'' simplifier for ``simple'' rules (~pl[simple]); the next is the main ACL2 simplifier; and finally ACL2 attempts (in this order) destructor elimination, fertilization (heuristic use of equalities), generalization, and elimination of irrelevance. Each process may ``hit'', creating zero or more child goals that are each then handled at the top of the waterfall; or it may ``miss'', in which case the next process in the above sequence is considered. If all processes miss, then a ``push'' process defers the goal until it is later considered for proof by induction. When all goals have been thus handled, the goal most recently pushed for proof by induction is considered, and the process repeats. We next describe the two additional ways in which control can be returned to the top of the waterfall. When the simplification process is attempted unsuccessfully for a goal, the goal is deemed to have ``settled down''. In this case, and if no ancestor of the goal has settled down, then the ``settled-down'' process is deemed to have ``hit'' on the goal, the effect being that the goal makes a new pass through all the waterfall processes. (Other processes can then notice that settling down has occurred and modify their heuristics accordingly.) For example, if ~c[\"Goal\"] simplifies to ~c[\"Subgoal 2\"] (among others), and ~c[\"Subgoal 2\"] simplifies to ~c[\"Subgoal 2.3\"] (among others), which in turn is not further simplified, then the ``settled-down'' process hits on ~c[\"Subgoal 2.3\"] but not on any of its children, their children, and so on. When simplification has missed (and thus the goal has settled down), the next proof process is normally destructor elimination. However, if a computed hint is suitable (in a sense described below; also ~pl[computed-hints], especially the discussion of ~c[stable-under-simplificationp]), then that hint is selected as control is returned to the top of the waterfall. A subtlety is that in this case, if the most recent hit had been from settling down, then the prover ``changes its mind'' and considers that the goal has not yet settled down after all as it continues through the waterfall. Each time a goal is considered at the top of the waterfall, then before passing through the proof processes as described above, ACL2 searches for a relevant hint to select unless it has already been provided a hint in the ``~c[stable-under-simplificationp]'' case mentioned above. We turn now to a more thorough discussion of how hints are selected and applied.~/ ~st[The handling of hints.] In the discussion below we will ignore forcing rounds, as each forcing round is simply treated as a new proof attempt that uses the list of hints provided at the start of the proof. When the theorem prover is called by ~ilc[thm] or ~il[events] such as ~ilc[defthm], ~ilc[defun], and ~ilc[verify-guards], it gathers up the hints that have been supplied, often provided as a ~c[:]~ilc[hints] argument, but for example using a ~c[:guard-hints] argument for ~il[guard] verification proofs. (ACL2(r) users (~pl[real]) may also employ ~c[:std-hints].) It then appends these to the front of the list of default hints (~pl[default-hints]). The resulting list becomes the initial value of the list of ``pending hints'', one of two critical lists maintained by the theorem prover to manage hints. The other critical list is a list of ``hint settings''; the two lists are maintained as follows. When a goal is first considered, a hint is selected from the list of pending hints if any is found to apply, as described below. If a hint is selected, then it takes effect and is removed from the pending hints. Except: if the selected hint is a computed hint with value ~c[t] specified for ~c[:computed-hint-replacement], then it is not removed; and if that value is a list of hints, then that list is appended to the front of the list of pending hints after the selected hint is removed (also ~pl[computed-hints]). The selected hint is also used to update the hint settings, as described below. The list of hint settings associates hint keywords with values. It is passed from the current goal to its children (and hence the children's children, and so on), though modified by hints selected from pending hints, as described below. This list is maintained so that when a goal is pushed for proof by induction, the hint settings are applied at the start of the proof by induction. Note that the list of hint settings is not re-applied to descendents of a goal in the current waterfall; a hint is applied only when it is selected (and also perhaps later as just described, through the stored hint settings at the start of a proof by induction). For example, if the hint selected for ~c[\"Subgoal 3\"] includes ~c[:in-theory (enable foo)], then the hint settings are correspondingly updated when processing ~c[\"Subgoal 3\"], and they persist at subgoals such as ~c[\"Subgoal 3.2\"] and ~c[\"Subgoal 3.2.1\"] (unless overriden by hints on those goals); but the theory specifying ~c[foo] is not re-installed at every such subgoal. When a hint is selected, the list of hint settings is updated so that for each keyword ~c[:kwd] and associated value ~c[val] from the hint, ~c[:kwd] is associated with ~c[val] in the hint settings, discarding any previous association of ~c[:kwd] with a value in the hint settings. Except, certain ``top-level'' hints are never saved in the hint settings: ~c[:use], ~c[:cases], ~c[:by], ~c[:bdd], ~c[:or], and ~c[:clause-processor]. For example, suppose that we specify the following hints, with no default hints. ~bv[] ((\"Goal\" :expand ((bar x y))) (\"Subgoal 3\" :in-theory (enable foo))) ~ev[] These hints then become the initial list of pending hints. When the proof attempt begins, the prover encounters the top-level goal (~c[\"Goal\"]) and pulls the ~c[\"Goal\"] hint from the pending hints, so that the list of hint settings contains a value only for keyword ~c[:expand]. This hint setting will remain for all children of the top-level goal as well, and their children, and so on, and will be inherited by induction ~-[] in other words, it will remain throughout the entire proof. Now consider what happens when the proof reaches ~c[\"Subgoal 3\"]. At this point there is only one pending hint, which is in fact attached to that subgoal. Therefore, this hint is pulled from the pending hints (leaving that list empty), and the hint settings are extended by associating the ~c[:in-theory] keyword with the theory represented by ~c[(enable foo)]. That theory is immediately installed until the prover finishes addressing ~c[\"Subgoal 3\"], its children, their children, and so on; and until that completion is reached, the ~c[:in-theory] keyword remains associated with the ~c[(enable foo)] in the hint settings, although of course there is no re-installation of the theory at any ensuing child goal. When finally ~c[\"Subgoal 3\"] and its descendents have been completed and the prover is about to consider ~c[\"Subgoal 2\"], the ~c[:in-theory] association is removed from the hint settings and the global theory is re-installed. However, the list of pending hints remains empty. It remains to describe how a hint is selected for a goal. When a goal is first considered (hence at the top of the waterfall), the list of pending hints is scanned, in order, until one of the hints is suitable for the goal. An explicit hint ~c[(goal-name :kwd1 val1 ... :kwdn valn)] is suitable if ~c[goal-name] is the name of the current goal and there is at least one keyword. A computed hint is suitable if it evaluates to a non-~c[nil] value. As indicated earlier in this documentation topic, an exception occurs when a computed hint is selected after simplification fails (the ``~c[stable-under-simplificationp]'' case): in that case, the goal returns to the top of the waterfall with that hint as the selected hint, and no additional search for a hint to select is made at that time. The following slightly tricky example illustrates handling of hints. ~bv[] ACL2 !>(set-default-hints '((\"Goal\" :do-not '(preprocess)))) ((\"Goal\" :DO-NOT '(PREPROCESS))) ACL2 !>(thm (equal (append (append x y) z) (append x y z)) :hints ((\"Goal\" :in-theory (disable car-cons)))) ACL2 Warning [Hints] in ( THM ...): The goal-spec \"Goal\" is explicitly associated with more than one hint. All but the first of these hints may be ignored. If you intended to give all of these hints, combine them into a single hint of the form (\"Goal\" :kwd1 val1 :kwd2 val2 ...). See :DOC hints-and-the-waterfall. [Note: A hint was supplied for our processing of the goal above. Thanks!] [Note: A hint was supplied for our processing of the goal above. Thanks!] Name the formula above *1. ~ev[] The warning above is printed because ~c[\"Goal\"] is associated with two pending hints: one given by the ~ilc[set-default-hints] call and one supplied by the ~c[:]~ilc[hints] keyword of the ~ilc[thm] form. The ~c[:in-theory] hint is selected because user-supplied hints are ahead of default hints in the list of pending hints; we then get the first ``Note'' above. The goal progresses through the waterfall without any proof process applying to the goal; in particular, it cannot be further simplified. After the simplification process, a ``settled-down'' process applies, as discussed above, immediately causing another trip through the waterfall. Since the ~c[:in-theory] hint was earlier removed from the list of pending hints when it was applied, the default (~c[:do-not]) hint is now the only pending hint. That hint is applied, resulting in the second ``Note'' above. Again, more examples may be found in the community book ~c[books/hints/basic-tests.lisp]. A particularly tricky but informative example in that book is the one related to ~c[nonlinearp-default-hint]. Also ~pl[override-hints] for an advanced feature that allows modification of the hint selected for a goal.") (deflabel hints :doc ":Doc-Section Miscellaneous advice to the theorem proving process~/ ~bv[] Examples: The following :hints value is nonsensical. Nevertheless, it illustrates all of the available hint keywords except the ``custom keywords'' (~pl[custom-keyword-hints]) definable by the user. :hints ((\"Goal\" :do-not-induct t :do-not '(generalize fertilize) :expand ((assoc x a) :lambdas (:free (y) (:with member (member y z)))) :restrict ((<-trans ((x x) (y (foo x))))) :hands-off (length binary-append) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))) :bdd (:vars (c a0 b0 a1 b1) :prove nil :bdd-constructors (cons)) :clause-processor (:function cl-proc :hint (my-hint clause)) :instructions (:x :prove) :cases ((true-listp a) (consp a)) :by (:instance rev-rev (x (cdr z))) :nonlinearp t :backchain-limit-rw 3 :reorder (4 7 2) :case-split-limitations (20 10) :no-op t :no-thanks t :error (\"Bad value ~~x0.\" 123) :or (hint-kwd-alist-1 ... hint-kwd-alist-k) :rw-cache-state nil :backtrack (my-computed-hint clause processor clause-list))) ~ev[] A very common hint is the ~c[:use] hint, which in general takes as its value a list of ``lemma instances'' (~pl[lemma-instance]) but which allows a single lemma name as a special case: ~bv[] :hints ((\"[1]Subgoal *1/1.2'\" :use lemma23)) ~ev[] ACL2 also provides ``custom keyword'' hints (~pl[custom-keyword-hints]) and even more general ``computed hints'' for the advanced user (~pl[computed-hints]). Only the first hint applicable to a goal, as specified in the user-supplied list of ~c[:hints] followed by the default hints (~pl[default-hints-table]), will be applied to that goal. For an advanced exception, ~pl[override-hints]. For a detailed discussion of how hints fit into the ACL2 waterfall, ~pl[hints-and-the-waterfall]. For examples of the sophisticated use of hints, primarily for experts, see community book ~c[books/hints/basic-tests.lisp].~/ Background: ~c[Hints] are allowed in all ~il[events] that use the theorem prover. During ~ilc[defun] ~il[events] there are two different uses of the theorem prover: one to prove termination and another to verify the ~il[guard]s. To pass a hint to the theorem prover during termination proofs, use the ~c[:hints] keyword in the ~ilc[defun]'s ~ilc[xargs] declaration. To pass a hint to the theorem prover during the ~il[guard] verification portion of admitting a ~ilc[defun], use the ~c[:guard-hints] keyword in the ~ilc[defun]'s ~ilc[xargs] declaration. The ~ilc[verify-guards] event and the ~ilc[defthm] event also use the theorem prover. To pass hints to them, use the ~c[:hints] keyword argument to the event. ~bv[] General Form of Common :hints: ((goal-spec :key1 val1 ... :keyn valn) ... (goal-spec :key1 val1 ... :keyn valn)) ~ev[] where ~ilc[goal-spec] is as described elsewhere (~pl[goal-spec]) and the keys and their respective values are shown below with their interpretations. (We also provide ``computed hints'' but discuss them separately; ~pl[computed-hints].) ~c[:DO-NOT-INDUCT]~nl[] ~c[Value] is ~c[t], ~c[:otf-flg-override], ~c[:otf], ~c[name] or ~c[nil], indicating whether ~il[induction] is permitted under the specified goal. If ~c[value] is ~c[t] or ~c[:otf-flg-override], then the attempt to apply ~il[induction] to the indicated goal or any subgoal under the indicated goal will immediately cause the theorem prover to report ~il[failure], except that if ~c[:otf-flg t] is specified (~pl[otf-flg]) and ~c[value] is ~c[t], then the proof will continue until the time at which the goal pushed for induction is finally considered. The latter behavior is also what occurs if ~c[value] is ~c[:otf]. Thus, any non-~c[nil] value requires the indicated goal to be proved entirely by simplification, destructor elimination, and the other ``waterfall'' processes. ~il[Induction] to prove the indicated goal (or any subgoal) is not permitted. See however the ~c[:induct] hint below. If ~c[value] is a symbol other than ~c[t], ~c[:otf-flg-override], ~c[:otf] or ~c[nil], the theorem prover will give a ``bye'' to any subgoal that would otherwise be attacked with induction. This will cause the theorem prover to fail eventually but will collect the necessary subgoals. If ~c[value] is ~c[nil], this hint means ~il[induction] is permitted. Since that is the default, there is no reason to use the value ~c[nil]. Note that a ~c[:do-not-induct] hint is ignored for any goal on which an ~c[:induct] hint is supplied. For an advanced example of the use of value ~c[:otf] with ~il[override-hints], see community book ~c[books/hints/basic-tests.lisp]. ~c[:DO-NOT]~nl[] ~c[Value] is a term having at most the single free variable ~ilc[world], which when evaluated (with ~ilc[world] bound to the current ACL2 logical ~il[world]) produces a list of symbols that is a subset of the list ~bv[] (preprocess ;propositional logic, simple rules simplify ;as above plus rewriting, linear arithmetic eliminate-destructors fertilize ;use of equalities generalize eliminate-irrelevance). ~ev[] The hint indicates that the ``processes'' named should not be used at or below the goal in question. Thus, to prevent generalization and fertilization, say, include the hint ~bv[] :do-not '(generalize fertilize) ~ev[] If ~c[value] is a single symbol, as in ~bv[] :do-not generalize, ~ev[] it is taken to be ~c['(value)]. ~c[:EXPAND]~nl[] ~c[Value] is a true list of terms, each of which is of one of the forms ~c[(let ((v1 t1)...) b)] or ~c[(fn t1 ... tn)], where ~c[fn] is a defined function symbol with formals ~c[v1, ..., vn,] and ~c[body] ~c[b]. Such a term is said to be ``expandable:'' it can be replaced by the result of substituting the ~c[ti]'s for the ~c[vi]'s in ~c[b]. The terms listed in the ~c[:expand] hint are expanded when they are encountered by the simplifier while working on the specified goal or any of its subgoals. We permit ~c[value] to be a single such term instead of a singleton list. ~st[Remarks]: (1) Allowed are ``terms'' of the form ~c[(:free (var1 var2 ... varn) pattern)] where the indicated variables are distinct and ~c[pattern] is a term. Such ``terms'' indicate that we consider the indicated variables to be instantiatable, in the following sense: whenever the simplifier encounters a term that can be obtained from ~c[pattern] by instantiating the variables ~c[(var1 var2 ... varn)], then it expands that term. (2) Also allowed are ``terms'' of the form ~c[(:with name term)], where ~c[name] is a function symbol, a macro name that denotes a function symbol (~pl[macro-aliases-table]), or a ~il[rune]. The corresponding rule of class ~c[:rewrite], which is often a ~il[definition] rule but need not be, is then used in place of the current body for the function symbol of ~c[term]; ~pl[show-bodies] and ~pl[set-body]. If the rule is of the form ~c[(implies hyp (equiv lhs rhs))], then after matching ~c[lhs] to the current term in a context that is maintaining equivalence relation ~c[equiv], ACL2 will replace the current term with ~c[(if hyp rhs (hide term))], or just ~c[rhs] if the rule is just ~c[(equal lhs rhs)]. (3) A combination of both ~c[:free] and ~c[:with], as described above, is legal. (4) The term ~c[:LAMBDAS] is treated specially. It denotes the list of all lambda applications (i.e., ~ilc[let] expressions) encountered during the proof. Conceptually, this use of ~c[:LAMBDAS] tells ACL2 to treat lambda applications as a notation for substitutions, rather than as function calls whose opening is subject to the ACL2 rewriter's heuristics (specifically, not allowing lambda applications to open when they introduce ``too many'' if terms). ~c[:HANDS-OFF]~nl[] ~c[Value] is a true list of function symbols or lambda expressions, indicating that under the specified goal applications of these functions are not to be rewritten. Note however that subterms will still be rewritten; ~pl[hide] if that is not what is intended. (The community book ~c[books/clause-processors/autohide.lisp] from Jared Davis may also be helpful in that case.) ~c[Value] may also be a single function symbol or lambda expression instead of a list. ~c[:]~ilc[IN-THEORY]~nl[] ~c[Value] is a ``theory expression,'' i.e., a term having at most the single free variable ~ilc[world] which when evaluated (with ~ilc[world] bound to the current ACL2 logical world (~pl[world])) will produce a theory to use as the current theory for the goal specified. ~l[theories]. Note that an ~c[:]~ilc[IN-THEORY] hint will always be evaluated relative to the current ACL2 logical ~il[world], not relative to the theory of a previous goal. Consider the following example. ~bv[] (defthm prop (p (f (g x))) :hints ((\"Goal\" :in-theory (disable f)) (\"Subgoal 3\" :in-theory (enable g)))) ~ev[] Consider in particular the theory in effect at ~c[Subgoal 3]. This call of the ~ilc[enable] macro enables ~c[g] relative to the ~ilc[current-theory] of the current logical ~il[world], ~em[not] relative to the theory produced by the hint at ~c[Goal]. Thus, the ~ilc[disable] of ~c[f] on behalf of the hint at ~c[Goal] will be lost at ~c[Subgoal 3], and ~c[f] will be enabled at ~c[Subgoal 3] if was enabled globally when ~c[prop] was submitted. ~c[:INDUCT]~nl[] ~c[Value] is either ~c[t] or a term containing at least one recursively defined function symbol; if ~c[t], this hint indicates that the system should proceed to apply its induction heuristic to the specified goal produced (without trying simplification, etc.); if ~c[value] is a term other than ~c[t], then not only should the system apply induction immediately, but it should analyze ~c[value] rather than the goal to generate its ~il[induction] scheme. Merging and the other ~il[induction] heuristics are applied. Thus, if ~c[value] contains several mergeable ~il[induction]s, the ``best'' will be created and chosen. E.g., the ~c[:induct] hint ~bv[] (and (nth i a) (nth j a)) ~ev[] suggests simultaneous ~il[induction] on ~c[i], ~c[j], and ~c[a]. If both an ~c[:induct] and a ~c[:do-not-induct] hint are supplied for a given goal then the indicated ~il[induction] is applied to the goal and the ~c[:do-not-induct] hint is inherited by all subgoals generated. ~c[:USE]~nl[] ~c[Value] is a ~il[lemma-instance] or a true list of ~il[lemma-instance]s, indicating that the propositions denoted by the instances be added as hypotheses to the specified goal. ~l[lemma-instance]. Note that ~c[:use] makes the given instances available as ordinary hypotheses of the formula to be proved. The ~c[:instance] form of a ~il[lemma-instance] permits you to instantiate the free variables of previously proved theorems any way you wish; but it is up to you to provide the appropriate instantiations because once the instances are added as hypotheses their variables are no longer instantiable. These new hypotheses participate fully in all subsequent rewriting, etc. If the goal in question is in fact an instance of a previously proved theorem, you may wish to use ~c[:by] below. Note that ~il[theories] may be helpful when employing ~c[:use] hints; ~pl[minimal-theory]. Note that if the value is the name of a function symbol introduced by ~ilc[defun], then the ``normalized'' body of that definition is used, for which ACL2 has propagated ~c[IF] tests upward. This behavior differs from that provided by a ~c[:by] hint, where the original body of the definition is used. ~c[:]~il[BDD]~nl[] This hint indicates that ordered binary decision diagrams (BDDs) with rewriting are to be used to prove or simplify the goal. ~l[bdd] for an introduction to the ACL2 BDD algorithm. ~c[Value] is a list of even length, such that every other element, starting with the first, is one of the keywords ~c[:vars], ~c[:bdd-constructors], ~c[:prove], or ~c[:literal]. Each keyword that is supplied should be followed by a value of the appropriate form, as shown below; for others, a default is used. Although ~c[:vars] must always be supplied, we expect that most users will be content with the defaults used for the other values. ~c[:vars] ~-[] A list of ACL2 variables, which are to be treated as Boolean variables. The prover must be able to check, using trivial reasoning (~pl[type-set]), that each of these variables is Boolean in the context of the current goal. Note that the prover will use very simple heuristics to order any variables that do not occur in ~c[:vars] (so that they are ``greater than'' the variables that do occur in ~c[:vars]), and these heuristics are often far from optimal. In addition, any variables not listed may fail to be assumed Boolean by the prover, which is likely to seriously impede the effectiveness of ACL2's BDD algorithm. Thus, users are encouraged ~em[not] to rely on the default order, but to supply a list of variables instead. Finally, it is allowed to use a value of ~c[t] for ~c[vars]. This means the same as a ~c[nil] value, except that the BDD algorithm is directed to fail unless it can guarantee that all variables in the input term are known to be Boolean (in a sense discussed elsewhere; ~pl[bdd-algorithm]). ~c[:literal] ~-[] An indication of which part of the current goal should receive BDD processing. Possible values are: ~bv[] :all treat entire goal as a single literal (the default) :conc process the conclusion n process the hypothesis with index n (1, 2, ...) ~ev[] ~c[:bdd-constructors] ~-[] When supplied, this value should be a list of function symbols in the current ACL2 ~il[world]; it is ~c[(cons)] by default, unless ~c[:bdd-constructors] has a value in the ~ilc[acl2-defaults-table] by default, in which case that value is the default. We expect that most users will be content with the default. ~l[bdd-algorithm] for information about how this value is used. ~c[:prove] ~-[] When supplied, this value should be ~c[t] or ~c[nil]; it is ~c[t] by default. When the goal is not proved and this value is ~c[t], the entire proof will abort. Use the value ~c[nil] if you are happy to the proof to go on with the simplified term. ~c[:CLAUSE-PROCESSOR] ~c[Value] specifies the application of a user-defined simplifier to the current goal. ~l[clause-processor], which provides necessary background and hint syntax. Also ~pl[define-trusted-clause-processor] for a discussion of ``trusted clause-processors'': goal-level simplifiers that may be external to ACL2 and do not need to be proved correct in ACL2. You can see all current ~c[:clause-processor] rules by issuing the command ~c[(print-clause-processor-rules)], and you can see the names of all trusted clause-processors by issuing the command ~c[(table trusted-clause-processor-table)]. ~c[:INSTRUCTIONS] ~c[Value] is a list of ~il[proof-checker] instructions; ~pl[instructions]. Unlike other hint keywords described here, this one is actually a custom keyword hint (~pl[custom-keyword-hints]) that generates a suitable ~c[:]~ilc[clause-processor] hint. ~c[:CASES]~nl[] ~c[Value] is a non-empty list of terms. For each term in the list, a new goal is created from the current goal by assuming that term; and also, in essence, one additional new goal is created by assuming all the terms in the list false. We say ``in essence'' because if the disjunction of the terms supplied is a tautology, then that final goal will be a tautology and hence will in fact never actually be created. ~c[:BY]~nl[] ~c[Value] is a ~il[lemma-instance], ~c[nil], or a new event name. If the value is a ~il[lemma-instance] (~pl[lemma-instance]), then it indicates that the goal (when viewed as a clause) is either equal to the proposition denoted by the instance, or is subsumed by that proposition when both are viewed as clauses. To view a formula as a clause, union together the negations of the hypotheses and add the conclusion. For example, ~bv[] (IMPLIES (AND (h1 t1) (h2 t2)) (c t1)) ~ev[] may be viewed as the clause ~bv[] {~~(h1 t1) ~~(h2 t2) (c t1)}. ~ev[] Clause ~c[c1] is ``subsumed'' by clause ~c[c2] iff some instance of ~c[c2] is a subset of ~c[c1]. For example, the clause above is subsumed by ~c[{~~(h1 x) (c x)}], which when viewed as a formula is ~c[(implies (h1 x) (c x))]. Note that if the value is the name of a function symbol introduced by ~ilc[defun], then the original form of the body of that definition is used. This behavior differs from that provided by a ~c[:use] hint, where the so-called ``normalized'' body, for which ACL2 has propagated ~c[IF] tests upward. If the value is ~c[nil] or a new name, the prover does not even attempt to prove the goal to which this hint is attached. Instead the goal is given a ``bye'', i.e., it is skipped and the proof attempt continues as though the goal had been proved. If the prover terminates without error then it reports that the proof would have succeeded had the indicated goals been proved and it prints an appropriate ~il[defthm] form to define each of the ~c[:by] names. The ``name'' ~c[nil] means ``make up a name.'' Here is an example (admittedly contrived for illustration purposes). ~bv[] ACL2 !>(thm (equal (append (append x y) z) (append x y z)) :hints ((\"Subgoal *1/2'\" :by nil))) Name the formula above *1. [[... output omitted here ...]] [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/2' (IMPLIES (AND (CONSP X) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But we have been asked to pretend that this goal is subsumed by the yet-to-be-proved |THM Subgoal *1/2'|. Subgoal *1/1 [[... proof goes on; further output omitted here ...]] ~ev[] The system does not attempt to check the uniqueness of the ~c[:by] names (supplied or made up), since by the time those goals are proved the namespace will be cluttered still further. Therefore, the final list of ``appropriate'' ~ilc[defthm] forms may be impossible to admit without some renaming by the user. If you must invent new names, remember to substitute the new ones for the old ones in the ~c[:by] hints themselves. ~c[:RESTRICT]~nl[] Warning: This is a sophisticated hint, suggested by Bishop Brock, that is intended for advanced users. In particular, ~c[:restrict] hints are ignored by the preprocessor, so you might find it useful to give the hint ~c[:do-not '(preprocess)] when using any ~c[:restrict] hints, at least if the rules in question are abbreviations (~pl[simple]). ~c[Value] is an association list. Its members are of the form ~c[(x subst1 subst2 ...)], where: ~c[x] is either (1) a ~il[rune] whose ~ilc[car] is ~c[:]~ilc[rewrite] or ~c[:]~ilc[definition] or (2) an event name corresponding to one or more such ~il[rune]s; and ~c[(subst1 subst2 ...)] is a non-empty list of substitutions, i.e., of association lists pairing variables with terms. First consider the case that ~c[x] is a ~c[:]~ilc[rewrite] or ~c[:]~ilc[definition] ~il[rune]. Recall that without this hint, the rule named ~c[x] is used by matching its left-hand side (call it ~c[lhs]) against the term currently being considered by the rewriter, that is, by attempting to find a substitution ~c[s] such that the instantiation of ~c[lhs] using ~c[s] is equal to that term. If however the ~c[:restrict] hint contains ~c[(x subst1 subst2 ...)], then this behavior will be modified by restricting ~c[s] so that it must extend ~c[subst1]; and if there is no such ~c[s], then ~c[s] is restricted so that it must extend ~c[subst2]; and so on, until the list of substitutions is exhausted. If no such ~c[s] is found, then the rewrite or definition rule named ~c[x] is not applied to that term. Finally, if ~c[x] is an event name corresponding to one or more ~c[:]~ilc[rewrite] or ~c[:]~ilc[definition] ~il[rune]s (that is, ~c[x] is the ``base symbol'' of such ~il[rune]s; ~pl[rune]), say ~il[rune]s ~c[r1], ... ~c[rn], then the meaning is the same except that ~c[(x subst1 subst2 ...)] is replaced by ~c[(ri subst1 subst2 ...)] for each ~c[i]. Once this replacement is complete, the hint may not contain two members whose ~ilc[car] is the same ~il[rune]. Note that the substitutions in ~c[:restrict] hints refer to the variables actually appearing in the goals, not to the variables appearing in the rule being restricted. Here is an example, supplied by Bishop Brock. Suppose that the database includes the following rewrite rule, which is probably kept ~il[disable]d. (We ignore the question of how to prove this rule.) ~bv[] cancel-<-*$free: (implies (and (rationalp x) (rationalp y) (rationalp z)) (equal (< y z) (if (< x 0) (> (* x y) (* x z)) (if (> x 0) (< (* x y) (* x z)) (hide (< y z)))))) ~ev[] Then ACL2 can prove the following theorem (unless other rules get in the way), essentially by multiplying both sides by ~c[x]. ~bv[] (thm (implies (and (rationalp x) (< 1 x)) (< (/ x) 1)) :hints ((\"Goal\" :in-theory (enable cancel-<-*$free) :restrict ((cancel-<-*$free ((x x) (y (/ x)) (z 1))))))) ~ev[] The ~c[:restrict] hint above says that the variables ~c[x], ~c[y], and ~c[z] in the rewrite rule ~c[cancel-<-*$free] above should be instantiated respectively by ~c[x], ~c[(/ x)], and ~c[1]. Thus ~c[(< y z)] becomes ~c[(< (/ x) 1)], and this inequality is replaced by the corresponding instance of the right-hand-side of ~c[cancel-<-*$free]. Since the current conjecture assumes ~c[(< 1 x)], that instance of the right-hand side simplifies to ~bv[] (< (* x (/ x)) (* x 1)) ~ev[] which in turn simplifies to ~c[(< 1 x)], a hypothesis in the present theorem. ~c[:NONLINEARP]~nl[] ~c[Value] is ~c[t] or ~c[nil], indicating whether ~il[non-linear-arithmetic] is active. The default value is ~c[nil]. ~l[non-linear-arithmetic]. ~c[:BACKCHAIN-LIMIT-RW]~nl[] ~c[Value] is a natural number or ~c[nil], indicating the level of backchaining for ~il[rewrite], ~il[meta], and ~il[linear] rules. This overrides, for the current goal and (as with ~c[:]~ilc[in-theory] hints) descendent goals, the default ~il[backchain-limit] (~pl[set-backchain-limit]). ~c[:REORDER]~nl[] ~c[Value] is a list of positive integers without duplicates, corresponding to the numbering of subgoals generated for the ~il[goal-spec] ~c[\"G\"], say ~c[\"G.k\"] down to ~c[\"G.1\"]. Those subgoals are reordered so that if ~c[value] is ~c[(n1 n2 ... nk)], then the goal now numbered ~c[\"G.k\"] will be the goal originally numbered ~c[\"G.n1\"]; the goal now numbered ~c[\"G.k-1\"] will be the goal formerly numbered ~c[\"G.n2\"]; and so on, down the list of ~c[ni], after which the goals not yet printed are printed in their original order. Note that reordering for subgoals of a goal to be proved by induction, such as ~c[*1], is not supported. ~c[:CASE-SPLIT-LIMITATIONS]~nl[] ~c[Value] is the same as for ~ilc[set-case-split-limitations]. The simplifier will behave as though the value had instead been supplied to ~c[set-case-split-limitations]; ~pl[set-case-split-limitations]. This behavior will persist through subgoals unless overridden by another ~c[:CASE-SPLIT-LIMITATIONS] hint. ~c[:NO-OP]~nl[] ~c[Value] is any object and is irrelevant. This hint does nothing. But empty hints, such as ~c[(\"Goal\")], are illegal and there are occasions, especially when writing custom keyword hints (~pl[custom-keyword-hints]) and computed hints (~pl[computed-hints]) where it is convenient to be able to generate a non-empty no-op hint. The standard idiom is ~c[(\"Goal\" :NO-OP T)] but the ~c[T] is completely ignored. Unlike other hint keywords, multiple occurrences of the keyword ~c[:NO-OP] are tolerated. ~c[:NO-THANKS]~nl[] ~c[Value] is any object. This hint does nothing, except that if ~c[value] is non-~c[nil] then the usual ``[Note: A hint was supplied... Thanks!]'' is not printed. ~c[:ERROR]~nl[] ~c[Value] is typically a ``fmt message'' to be printed by the ~ilc[fmt] tilde-directive ~~@ but may be any object. The effect of this hint is to cause an error when the hint is translated. There is no reason to include an ~c[:ERROR] hint in any user-typein, since it will only cause an error when the form is evaluated. ~c[:ERROR] hints are useful in the definition of functions that generate custom keyword hints (~il[custom-keyword-hints]) and computed hints (~il[computed-hints]). For example, if you wish to define a custom keyword hint ~c[:my-hint val] and you wish the hint to signal an error if there is something inappropriate about ~c[val] in the context of the hint, use the following code to generate the hint ~bv[] (list :ERROR (cons \"Your specified value, ~~x0, is inappropriate\" (list (cons \#0 val)))) ~ev[] which is equivalent to ~bv[] (list :ERROR (msg \"Your specified value, ~~x0, is inappropriate\" val)) ~ev[] which, if ~c[val] has the value ~c[123], would evaluate to the hint ~bv[] (:ERROR (\"Your specified value, ~~x0, is inappropriate\" (\#0 . 123))). ~ev[] Note that any time an ~c[:ERROR] keyword is produced during hint processing, including iterations of the expansions of custom keyword hints or of ~il[override-hints], an error will occur. ~c[:OR]~nl[] ~c[Value] is a list ~c[(kwd-val-listp-1 ... kwd-val-listp-k)], where each ~c[kwd-val-listp-i] is a list satisfying ~ilc[keyword-value-listp], i.e., an alternating list of keywords and values. This hint causes an attempt to prove the specified goal using hints ~c[kwd-val-listp-i] in sequence (first ~c[kwd-val-listp-1], then ~c[kwd-val-listp-2], and so on), until the first of these succeeds. If none succeeds, then the prover proceeds after heuristically choosing the ``best'' result, taking into account the goals pushed in each case for proof by induction. The following (contrived but illustrative example illustrates how ~c[:or] hints work. ~bv[] ACL2 !>(thm (f x) :hints ((\"Goal\" :expand ((nth x 3)) :or ((:in-theory (disable car-cons)) (:use cdr-cons :in-theory (enable append))) :do-not '(generalize)))) [Note: A hint was supplied for our processing of the goal above. Thanks!] The :OR hint for Goal gives rise to two disjunctive branches. Proving any one of these branches would suffice to prove Goal. We explore them in turn, describing their derivations as we go. --- Subgoal D2 ( same formula as Goal ). The first disjunctive branch (of 2) for Goal can be created by applying the hint: (\"Subgoal D2\" :EXPAND ((NTH X 3)) :IN-THEORY (DISABLE CAR-CONS) :DO-NOT '(GENERALIZE)). [Note: A hint was supplied for our processing of the goal above. Thanks!] Normally we would attempt to prove this formula by induction. However, we prefer in this instance to focus on the original input conjecture rather than this simplified special case. We therefore abandon our previous work on this conjecture and reassign the name *1 to the original conjecture. (See :DOC otf-flg.) [Note: Thanks again for the hint.] --- Subgoal D1 ( same formula as Goal ). The second disjunctive branch (of 2) for Goal can be created by applying the hint: (\"Subgoal D1\" :EXPAND ((NTH X 3)) :USE CDR-CONS :IN-THEORY (ENABLE APPEND) :DO-NOT '(GENERALIZE)). [Note: A hint was supplied for our processing of the goal above. Thanks!] ACL2 Warning [Use] in ( THM ...): It is unusual to :USE an enabled :REWRITE or :DEFINITION rule, so you may want to consider disabling (:REWRITE CDR-CONS). We augment the goal with the hypothesis provided by the :USE hint. The hypothesis can be obtained from CDR-CONS. We are left with the following subgoal. Subgoal D1' (IMPLIES (EQUAL (CDR (CONS X Y)) Y) (F X)). By the simple :rewrite rule CDR-CONS we reduce the conjecture to Subgoal D1'' (F X). ~ev[] ... and so on. This example illustrates how ACL2 processes ~c[:or] hints in general. For each ~c[i] from 1 to ~c[k], a so-called ``disjunctive'' subgoal is created by splicing ~c[kwd-val-listp-i] into the other hint values (if any) supplied for the given goal, in order. A corresponding subgoal is created for each ~c[i], numbered in the usual manner (hence, counting down) except that the ``~c[D]'' is prefixed to each resulting goal. ~c[:RW-CACHE-STATE]~nl[] ~c[Value] is an element of the list constant ~c[*legal-rw-cache-states*]: ~c[:atom] (the default), ~c[nil], ~c[t], or ~c[:disabled]. This hint applies to the indicated goal and all its descendents, to set the so-called ``rw-cache-state'' to the indicated value; ~pl[set-rw-cache-state]. ~c[:BACKTRACK]~nl[] This is an advanced hint. You can probably accomplish its effect by the use of ordinary computed hints; ~pl[computed-hints]. But if you are an expert, read on. (~l[hints-and-the-waterfall] for some relevant background.) ~c[Value] is a computed hint, which is an expression that evaluates either to ~c[nil] ~-[] indicating that the ~c[:backtrack] hint is to have no effect ~-[] or to a non-empty alternating list of ~c[:keyi :vali] pairs, as expected for a hint. However, unlike ordinary computed hints, ~c[:backtrack] hints are evaluated ~st[after] a goal has been processed to yield zero or more subgoals, not before. Moreover, variables ~c[PROCESSOR] and ~c[CLAUSE-LIST] are allowed, but variable ~c[STABLE-UNDER-SIMPLIFICATIONP] is not. We explain in more detail below, but first consider the following simple example. First we define a standard list reversal function: ~bv[] (defun rev (x) (if (consp x) (append (rev (cdr x)) (cons (car x) nil)) nil)) ~ev[] Now we prove: ~bv[] (thm (true-listp (rev x))) ~ev[] The successful proof includes the following output. ~bv[] Subgoal *1/1' (IMPLIES (AND (CONSP X) (TRUE-LISTP (REV (CDR X)))) (TRUE-LISTP (APPEND (REV (CDR X)) (LIST (CAR X))))). The destructor terms (CAR X) and (CDR X) can be eliminated by using CAR-CDR-ELIM to replace X by (CONS X1 X2), (CAR X) by X1 and (CDR X) by X2. This produces the following goal. Subgoal *1/1'' (IMPLIES (AND (CONSP (CONS X1 X2)) (TRUE-LISTP (REV X2))) (TRUE-LISTP (APPEND (REV X2) (LIST X1)))). ~ev[] But suppose that we attach a ~c[:backtrack] hint to the goal above at which destructor elimination was applied: ~bv[] (thm (true-listp (rev x)) :hints ((\"Subgoal *1/1'\" :backtrack (quote (:do-not '(eliminate-destructors)))))) ~ev[] Then when ACL2 applies destructor elimination as displayed above, this time the ~c[:backtrack] hint applies, evaluating to ~c[(:do-not '(eliminate-destructors))]. Since this list is not ~c[nil], the prover decides not to keep the new subgoal, and instead supplies this ~c[:do-not] hint before attacking the goal again. In this example, ACL2 happens to use a technique later in its ``waterfall'' arsenal than destructor elimination, namely, generalization: ~bv[] Subgoal *1/1' (IMPLIES (AND (CONSP X) (TRUE-LISTP (REV (CDR X)))) (TRUE-LISTP (APPEND (REV (CDR X)) (LIST (CAR X))))). [Note: A hint was supplied for our processing of the goal above, because of a :backtrack hint that is preventing destructor elimination. Thanks!] We generalize this conjecture, replacing (REV (CDR X)) by RV. This produces Subgoal *1/1'' (IMPLIES (AND (CONSP X) (TRUE-LISTP RV)) (TRUE-LISTP (APPEND RV (LIST (CAR X))))). ~ev[] We now provide a careful explanation of how ~c[:backtrack] hints work, but we suggest that you keep the example above in mind. If ``~c[:backtrack form]'' is part of the hint that has been selected for a goal, then ~c[form] is evaluated when one of ACL2's clause processors successfully applies to the current goal to produce a list of subgoals. This evaluation takes place in an environment just like that for any computed hint (~pl[computed-hints]), with the following exceptions. First, the variable ~c[STABLE-UNDER-SIMPLIFICATIONP] is not allowed to occur free in ~c[form], but instead the following new variables are allowed to occur free and are bound for this evaluation as follows: ~c[PROCESSOR] is bound to the processor in the list ~c[*preprocess-clause-ledge*] that has applied to the goal, and ~c[CLAUSE-LIST] is bound to the list of clauses (each a list of literals that is implicitly disjoined) returned by that clause processor. Second, the variables ~c[HIST] and ~c[PSPV] are bound to the history and pspv returned by the clause processor, ~st[not] the ones that were passed to the clause processor. If this evaluation returns an error, then the proof aborts, as for any computed hint whose evaluation returns an error. If this evaluation returns ~c[nil], then the ~c[:backtrack] hint has no effect, and the goal is replaced by the list of goals (the value of ~c[CLAUSE-LIST] described above), as usual. Otherwise, the clause processor is deemed to have failed, and the goal clause is tried again starting at the top of the waterfall after selecting the hint returned by the above evaluation. That hint will normally be an alternating list of hint keywords and their values, but if it is a custom keyword hint (~pl[custom-keyword-hints]), then it will be handled in the usual manner but with the first three variables above bound to the symbol ~c[:OMITTED]. Of course, if the new hint includes a value for ~c[:BACKTRACK] then this process can loop; care should be taken to keep that from happening. A final note about ~c[:BACKTRACK] hints: since these are a form of computed hints, ~il[override-hints] (if any) are applied to their evaluation result just as with any computed hint. That is, the backtrack hint is successively modified with each override-hint, to produce a final hint that is actually used (or, ignored if that final hint is ~c[nil]). ~l[override-hints].~/") (deflabel clause-identifier :doc ":Doc-Section Miscellaneous the internal form of a ~il[goal-spec]~/ To each goal-spec, ~c[str], there corresponds a clause-identifier produced by ~c[(parse-clause-id str)]. For example, ~bv[] (parse-clause-id \"[2]Subgoal *4.5.6/7.8.9'''\") ~ev[] returns ~c[((2 4 5 6) (7 8 9) . 3)]. The function ~c[string-for-tilde-@-clause-id-phrase] inverts ~c[parse-clause-id] in the sense that given a clause identifier it returns the corresponding goal-spec.~/ As noted in the documentation for ~il[goal-spec], each clause printed in the theorem prover's proof attempt is identified by a name. When these names are represented as strings they are called ``goal specs.'' Such strings are used to specify where in the proof attempt a given hint is to be applied. The function ~c[parse-clause-id] converts goal-specs into clause identifiers, which are cons-trees containing natural numbers. Examples of goal-specs and their corresponding clause identifiers are shown below. ~bv[] parse-clause-id --> \"Goal\" ((0) NIL . 0) \"Subgoal 3.2.1'\" ((0) (3 2 1) . 1) \"[2]Subgoal *4.5.6/7.8.9'''\" ((2 4 5 6) (7 8 9) . 3) <-- string-for-tilde-@-clause-id-phrase ~ev[] The caar of a clause id specifies the forcing round, the cdar specifies the goal being proved by induction, the cadr specifies the particular subgoal, and the cddr is the number of primes in that subgoal. Internally, the system maintains clause ids, not goal-specs. The system prints clause ids in the form shown by goal-specs. When a goal-spec is used in a hint, it is parsed (before the proof attempt begins) into a clause id. During the proof attempt, the system watches for the clause id and uses the corresponding hint when the id arises. (Because of the expense of creating and garbage collecting a lot of strings, this design is more efficient than the alternative.)") (deflabel computed-hints :doc ":Doc-Section Miscellaneous computing advice to the theorem proving process~/ ~bv[] General Form of :hints: (hint1 hint2 ... hintk) ~ev[] Each element, hinti, must be either a common hint or a computed hint.~/ A common hint is of the form ~bv[] (goal-spec :key1 val1 ... :keyn valn) ~ev[] where ~c[goal-spec] is as specified in ~il[goal-spec] and each ~c[:keyi] and ~c[vali] is as specified in ~il[hints]. Among the ``common hints'' we include both the primitive hints and user-defined custom keyword hints (~pl[custom-keyword-hints]). A computed hint may be a function symbol, ~c[fn], of three, four or seven arguments. Otherwise, a computed hint is a term with the following properties: (a) the only free variables allowed in the term are ~c[ID], ~c[CLAUSE], ~c[WORLD], ~c[STABLE-UNDER-SIMPLIFICATIONP], ~c[HIST], ~c[PSPV], ~c[CTX], and ~ilc[STATE]; (b) the output signature of the term is either ~c[(MV * * STATE)], which is to be treated as an error triple (see below), or is ~c[*], denoting a single non-~il[stobj] value; and (c) in the former case of (b) above, the term is single-threaded in ~ilc[STATE]. If a computed hint is a function symbol ~c[fn], whose arity n is therefore three, four, or seven, then it is treated as the term resulting from applying that ~c[fn] to the first n variables shown in (a) above. Notice that it must then return a single non-~il[stobj] value, not an error triple, since ~c[state] is not one of the first seven variables shown in (a). Note: Error triples are an ACL2 idiom for implementing ``errors''; ~pl[error-triples]. If a computation returns ~c[(mv erp val state)] in a context in which ACL2 is respecting the error triple convention (~pl[ld-error-triples] and ~pl[ld-error-action]), then an error is deemed to have occurred if ~c[erp] is non-~c[nil]. The computation is expected to have printed an appropriate error message to ~ilc[state] and further computation is aborted. On the other hand, if a computation returns an error triple in which ~c[erp] is nil, then ``value'' of the computation is taken to be the second component, ~c[val], of the triple (along with the possibly modified ~ilc[state]), and computation continues. For more information about programming with error triples, ~pl[programming-with-state]. The function symbol cases are treated as abbreviations of the term ~c[(fn ID CLAUSE WORLD)], ~c[(fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP)], or ~c[(fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX)] as appropriate for the arity of ~c[fn]. (Note that this tells you which argument of ~c[fn] is which.) Moreover, in these cases the value returned must be a single ordinary (non-~il[stobj]) value, not an error triple. In the discussion below we assume all computed hints are of the term form. Indeed, we almost assume all computed hints are of the 3 and 4 argument forms. We only comment briefly on the 7 argument form in ~il[using-computed-hints-8]. The semantics of a computed hint term is as follows. On every subgoal, the term is evaluated in an environment in which the variables mentioned in (a) above are bound to context-sensitive values explained below. Either the computed hint signals an error, in which the proof attempt aborts, or else it returns a value, ~c[val] and a new state, ~ilc[state]. Any changes to those parts of ~ilc[state] that affect logical soundness are undone; more specifically, the values of symbols (sometimes called ``state global variables'') in the list ~c[*protected-system-state-globals*] in the global table of the state (~pl[state]) are restored when changed during evaluation. The value, ~c[val], of a non-erroneous computed hint calculation is either ~c[nil], which means the computed hint did not apply to the subgoal in question, or it is an alternating list of ~c[:keyi vali] pairs as specified in ~il[hints]. With one exception, those new hints are applied to the given subgoal as though the user had typed them explicitly. The exception is that the first keyword in the returned ~c[val] is allowed to be ~c[:COMPUTED-HINT-REPLACEMENT]. Its value should be ~c[nil], ~c[t], or a list of terms. If this keyword is not present, the default value of ~c[nil] is provided. We explain ~c[:COMPUTED-HINT-REPLACEMENT] below. The evaluation of a hint term is done with guard checking turned off (~pl[set-guard-checking]); e.g., the form ~c[(car 23)] in a computed hint returns ~c[nil] as per the axioms. When a non-~c[nil] value is returned, the keyword/value pairs (other than the optional ~c[:COMPUTED-HINT-REPLACEMENT]) are used as the hint for the subgoal in question. Thus, your job as the programmer of computed hints is either to cause an error, typically by invoking ~ilc[er], or to return a non-erroneous value triple whose value is the list of keys and values you would have typed had you supplied a common hint for the subgoal. (In particular, any theory expressions in it are evaluated with respect to the global current-theory, not whatever theory is active at the subgoal in question.) If the generated list of keywords and values is illegal, an error will be signaled and the proof attempt will be aborted. The purpose of the ~c[:COMPUTED-HINT-REPLACEMENT] keyword and its value, ~c[chr], is to change the list of hints. If ~c[chr] is ~c[nil], then the hint which was applied is removed from the list of hints that is passed down to the children of the subgoal in question. This is the default. If ~c[chr] is ~c[t], then the hint is left in the list of hints. This means that the same hint may act on the children of the subgoal. Otherwise, ~c[chr] must be a list of terms, each of which is treated as a computed hint. The hint which was applied is deleted from the list of hints and the hints in ~c[chr] are added to the list of hints passed to the children of the subgoal. The ability to compute new hints and pass them down allows strange and wonderful behavior. For these purposes, the goals produced by induction and the top-level goals of forcing rounds are not considered children; all original hints are available to them. Only the first hint applicable to a goal, as specified in the user-supplied list of ~c[:hints] followed by the ~ilc[default-hints-table], will be applied to that goal. (For an advanced exception, ~pl[override-hints].) It remains only to describe the bindings of the free variables. Suppose the theorem prover is working on some clause, clause, named by some ~ilc[goal-spec], e.g., \"Subgoal *1/2'''\" in some logical world, world. Corresponding to the printed ~c[goal-spec] is an internal data structure called a ``clause identifier'' id. ~l[clause-identifier]. In the case of a common hint, the hint applies if the goal-spec of the hint is the same as the goal-spec of the clause in question. In the case of a computed hint, the variable ~c[ID] is bound to the clause id, the variable ~c[CLAUSE] is bound to the (translated form of the) clause, and the variable ~c[WORLD] is bound to the current ACL2 world. The variable ~c[STABLE-UNDER-SIMPLIFICATIONP] is bound to ~c[t] or ~c[nil]. It is bound to ~c[t] only if the clause is known to be stable under simplification. That is, the simplifier has been applied to the clause and did not change it. Such a clause is sometimes known as a ``simplification checkpoint.'' It is frequently useful to inject hints (e.g., to enable a rule or provide a ~c[:use] hint) only when the goal in question has stabilized. If a hint is provided, the processing of the clause starts over with simplification. As for ~c[CTX] and ~ilc[STATE], they are provided so that you can pass them to the ~ilc[er] macro to print error messages. We recommend not writing computed hints that otherwise change ~ilc[STATE]! The remaining variables, ~c[HIST] and ~c[PSPV] are not documented yet. Only users familiar with the internals of ACL2 are likely to need them or understand their values. For some instruction about how to use computed hints, ~pl[using-computed-hints].") (deflabel using-computed-hints-1 :doc ":Doc-Section Miscellaneous Driving Home the Basics~/~/ The common hint ~bv[] (\"Subgoal 3.2.1''\" :use lemma42) ~ev[] has the same effect as the computed hint ~bv[] (if (equal id '((0) (3 2 1) . 2)) '(:use lemma42) nil) ~ev[] which, of course, is equivalent to ~bv[] (and (equal id '((0) (3 2 1) . 2)) '(:use lemma42)) ~ev[] which is also equivalent to the computed hint ~bv[] my-special-hint ~ev[] provided the following ~c[defun] has first been executed ~bv[] (defun my-special-hint (id clause world) (declare (xargs :mode :program) (ignore clause world)) (if (equal id '((0) (3 2 1) . 2)) '(:use lemma42) nil)) ~ev[] It is permitted for the ~c[defun] to be in :LOGIC mode (~pl[defun-mode]) also. Just to be concrete, the following three events all behave the same way (if ~c[my-special-hint] is as above): ~bv[] (defthm main (big-thm a b c) :hints ((\"Subgoal 3.2.1''\" :use lemma42))) (defthm main (big-thm a b c) :hints ((and (equal id '((0) (3 2 1) . 2)) '(:use lemma42)))) (defthm main (big-thm a b c) :hints (my-special-hint)) ~ev[]") (deflabel using-computed-hints-2 :doc ":Doc-Section Miscellaneous One Hint to Every Top-Level Goal in a Forcing Round~/~/ Suppose the main proof completes with a forcing round on three subgoals, \"[1]Subgoal 3\", \"[1]Subgoal 2\", and \"[1]Subgoal 1\". Suppose you wish to ~c[:use lemma42] in all top-level goals of the first forcing round. This can be done supplying the hint ~bv[] (if test '(:use lemma42) nil), ~ev[] where ~c[test] is an expression that returns ~c[t] when ~c[ID] is one of the clause ids in question. ~bv[] goal-spec (parse-clause-id goal-spec) \"[1]Subgoal 3\" ((1) (3) . 0) \"[1]Subgoal 2\" ((1) (2) . 0) \"[1]Subgoal 1\" ((1) (1) . 0) ~ev[] Recall (~pl[clause-identifier]) that ~c[parse-clause-id] maps from a goal spec to a clause id, so you can use that function on the goal specs printed in the failed proof attempt to determine the clause ids in question. So one acceptable ~c[test] is ~bv[] (member-equal id '(((1) (3) . 0) ((1) (2) . 0) ((1) (1) . 0))) ~ev[] or you could use ~c[parse-clause-id] in your computed hint if you don't want to see clause ids in your script: ~bv[] (or (equal id (parse-clause-id \"[1]Subgoal 3\")) (equal id (parse-clause-id \"[1]Subgoal 2\")) (equal id (parse-clause-id \"[1]Subgoal 1\"))) ~ev[] or you could use the inverse function (~pl[clause-identifier]): ~bv[] (member-equal (string-for-tilde-@-clause-id-phrase id) '(\"[1]Subgoal 3\" \"[1]Subgoal 2\" \"[1]Subgoal 1\")) ~ev[] Recall that what we've shown above are the tests to use in the computed hint. The hint itself is ~c[(if test '(:use lemma42) nil)] or something equivalent like ~c[(and test '(:use lemma42))]. The three tests above are all equivalent. They suffer from the problem of requiring the explicit enumeration of all the goal specs in the first forcing round. A change in the script might cause more forced subgoals and the ones other than those enumerated would not be given the hint. You could write a test that recognizes all first round top-level subgoals no matter how many there are. Just think of the programming problem: how do I recognize all the clause id's of the form ~c[((1) (n) . 0)]? Often you can come to this formulation of the problem by using ~c[parse-clause-id] on a few of the candidate goal-specs to see the common structure. A suitable test in this case is: ~bv[] (and (equal (car id) '(1)) ; forcing round 1, top-level (pre-induction) (equal (len (cadr id)) 1) ; Subgoal n (not Subgoal n.i ...) (equal (cddr id) 0)) ; no primes ~ev[] The test above is ``overkill'' because it recognizes precisely the clause ids in question. But recall that once a computed hint is used, it is (by default) removed from the hints available to the children of the clause. Thus, we can widen the set of clause ids recognized to include all the children without worrying that the hint will be applied to those children. In particular, the following test supplies the hint to every top-level goal of the first forcing round: ~bv[] (equal (car id) '(1)) ~ev[] You might worry that it would also supply the hint to the subgoal produced by the hint -- the cases we ruled out by the ``overkill'' above. But that doesn't happen since the hint is unavailable to the children. You could even write: ~bv[] (equal (car (car id)) 1) ~ev[] which would supply the hint to every goal of the form \"[1]Subgoal ...\" and again, because we see and fire on the top-level goals first, we will not fire on, say, \"[1]Subgoal *1.3/2\", i.e., the id '((1 1 3) (2) . 0) even though the test recognizes that id. Finally, the following test supplies the hint to every top-level goal of every forcing round (except the 0th, which is the ``gist'' of the proof, not ``really'' a forcing round): ~bv[] (not (equal (car (car id)) 0)) ~ev[] Recall again that in all the examples above we have exhibited the ~c[test] in a computed hint of the form ~c[(if test '(:key1 val1 ...) nil)].") (deflabel using-computed-hints-3 :doc ":Doc-Section Miscellaneous Hints as a Function of the Goal (not its Name)~/~/ Sometimes it is desirable to supply a hint whenever a certain term arises in a conjecture. For example, suppose we have proved ~bv[] (defthm all-swaps-have-the-property (the-property (swap x)) :rule-classes nil) ~ev[] and suppose that whenever ~c[(SWAP A)] occurs in a goal, we wish to add the additional hypothesis that ~c[(THE-PROPERTY (SWAP A))]. Note that this is equivalent supplying the hint ~bv[] (if test '(:use (:instance all-swaps-have-the-property (x A))) nil) ~ev[] where ~c[test] answers the question ``does the clause contain ~c[(SWAP A)]?'' That question can be asked with ~c[(occur-lst '(SWAP A) clause)]. Briefly, ~c[occur-lst] takes the representation of a translated term, x, and a list of translated terms, y, and determines whether x occurs as a subterm of any term in y. (By ``subterm'' here we mean proper or improper, e.g., the subterms of ~c[(CAR X)] are ~c[X] and ~c[(CAR X)].) Thus, the computed hint: ~bv[] (if (occur-lst '(swap a) clause) '(:use (:instance all-swaps-have-the-property (x A))) nil) ~ev[] will add the hypothesis ~c[(THE-PROPERTY (SWAP A))] to every goal containing ~c[(SWAP A)] -- except the children of goals to which the hypothesis was added. ~b[A COMMON MISTAKE] users are likely to make is to forget that they are dealing with translated terms. For example, suppose we wished to look for ~c[(SWAP (LIST 1 A))] with ~c[occur-lst]. We would never find it with ~bv[] (occur-lst '(SWAP (LIST 1 A)) clause) ~ev[] because that presentation of the term contains macros and other abbreviations. By using :trans (~pl[trans]) we can obtain the translation of the target term. Then we can look for it with: ~bv[] (occur-lst '(SWAP (CONS '1 (CONS A 'NIL))) clause) ~ev[] Note in particular that you must ~bf[] * eliminate all macros and * explicitly quote all constants. ~ef[] We recommend using ~c[:trans] to obtain the translated form of the terms in which you are interested, before programming your hints. An alternative is to use the expression ~c[(prettyify-clause clause nil nil)] in your hint to convert the current goal clause into the s-expression that is actually printed. For example, the clause ~bv[] ((NOT (CONSP X)) (SYMBOLP Y) (EQUAL (CONS '1 (CAR X)) Y)) ~ev[] ``prettyifies'' to ~bv[] (IMPLIES (AND (CONSP X) (NOT (SYMBOLP Y))) (EQUAL (CONS 1 (CAR X)) Y)) ~ev[] which is what you would see printed by ACL2 when the goal clause is that shown. However, if you choose to convert your clauses to prettyified form, you will have to write your own explorers (like our ~c[occur-lst]), because all of the ACL2 term processing utilities work on translated and/or clausal forms. This should not be taken as a terrible burden. You will, at least, gain the benefit of knowing what you are really looking for, because your explorers will be looking at exactly the s-expressions you see at your terminal. And you won't have to wade through our still undocumented term/clause utilities. The approach will slow things down a little, since you will be paying the price of independently consing up the prettyified term. We make one more note on this example. We said above that the computed hint: ~bv[] (if (occur-lst '(swap a) clause) '(:use (:instance all-swaps-have-the-property (x A))) nil) ~ev[] will add the hypothesis ~c[(THE-PROPERTY (SWAP A))] to every goal containing ~c[(SWAP A)] -- except the children of goals to which the hypothesis was added. It bears noting that the subgoals produced by induction and top-level forcing round goals are not children. For example, suppose the hint above fires on \"Subgoal 3\" and produces, say, \"Subgoal 3'\". Then the hint will not fire on \"Subgoal 3'\" even though it (still) contains ~c[(SWAP A)] because \"Subgoal 3'\" is a child of a goal on which the hint fired. But now suppose that \"Subgoal 3'\" is pushed for induction. Then the goals created by that induction, i.e., the base case and induction step, are not considered children of \"Subgoal 3'\". All of the original hints are available. Alternatively, suppose that \"Subgoal 3'\ is proved but forces some other subgoal, \"[1]Subgoal 1\" which is attacked in Forcing Round 1. That top-level forced subgoal is not a child. All the original hints are available to it. Thus, if it contains ~c[(SWAP A)], the hint will fire and supply the hypothesis, producing \"[1]Subgoal 1'\". This may be unnecessary, as the hypothesis might already be present in \"[1]Subgoal 1\". In this case, no harm is done. The hint won't fire on \"[1]Subgoal 1\" because it is a child of \"[1]Subgoal 1\" and the hint fired on that.") (deflabel using-computed-hints-4 :doc ":Doc-Section Miscellaneous Computing the Hints~/~/ So far we have used computed hints only to compute when a fixed set of keys and values are to be used as a hint. But computed hints can, of course, compute the set of keys and values. You might, for example, write a hint that recognizes when a clause ``ought'' to be provable by a ~c[:BDD] hint and generate the appropriate hint. You might build in a set of useful lemmas and check to see if the clause is provable ~c[:BY] one of them. You can keep all function symbols disabled and use computed hints to compute which ones you want to ~c[:EXPAND]. In general, you can write a theorem prover for use in your hints, provided you can get it to do its job by directing our theorem prover. Suppose for example we wish to find every occurrence of an instance of ~c[(SWAP x)] and provide the corresponding instance of ~c[ALL-SWAPS-HAVE-THE-PROPERTY]. Obviously, we must explore the clause looking for instances of ~c[(SWAP x)] and build the appropriate instances of the lemma. We could do this in many different ways, but below we show a general purpose set of utilities for doing it. The functions are not defined in ACL2 but could be defined as shown. Our plan is: (1) Find all instances of a given pattern (term) in a clause, obtaining a set of substitutions. (2) Build a set of ~c[:instance] expressions for a given lemma name and set of substitutions. (3) Generate a ~c[:use] hint for those instances when instances are found. The pair of functions below find all instances of a given pattern term in either a term or a list of terms. The functions each return a list of substitutions, each substitution accounting for one of the matches of pat to a subterm. At this level in ACL2 substitutions are lists of pairs of the form ~c[(var . term)]. All terms mentioned here are presumed to be in translated form. The functions take as their third argument a list of substitutions accumulated to date and add to it the substitutions produced by matching pat to the subterms of the term. We intend this accumulator to be nil initially. If the returned value is nil, then no instances of pat occurred. ~bv[] (mutual-recursion (defun find-all-instances (pat term alists) (declare (xargs :mode :program)) (mv-let (instancep alist) (one-way-unify pat term) (let ((alists (if instancep (add-to-set-equal alist alists) alists))) (cond ((variablep term) alists) ((fquotep term) alists) (t (find-all-instances-list pat (fargs term) alists)))))) (defun find-all-instances-list (pat list-of-terms alists) (declare (xargs :mode :program)) (cond ((null list-of-terms) alists) (t (find-all-instances pat (car list-of-terms) (find-all-instances-list pat (cdr list-of-terms) alists)))))) ~ev[] Caveat: The following aside has nothing to do with computed hints. Does an instance of ~c[(CAR (CDR x))] occur in ~c[((LAMBDA (V) (CAR V)) (CDR A))]? It does if one beta-reduces the lambda-expression to ~c[(CAR (CDR A))]; the appropriate substitution is to replace ~c[x] by ~c[A]. But the definition of ~c[find-all-instances] above does ~i[not] find this instance because it does not do beta-reduction. We now turn our attention to converting a list of substitutions into a list of lemma instances, each of the form ~bv[] (:INSTANCE name (var1 term1) ... (vark termk)) ~ev[] as written in ~c[:use] hints. In the code shown above, substitutions are lists of pairs of the form ~c[(var . term)], but in lemma instances we must write ``doublets.'' So here we show how to convert from one to the other: ~bv[] (defun pairs-to-doublets (alist) (declare (xargs :mode :program)) (cond ((null alist) nil) (t (cons (list (caar alist) (cdar alist)) (pairs-to-doublets (cdr alist)))))) ~ev[] Now we can make a list of lemma instances: ~bv[] (defun make-lemma-instances (name alists) (declare (xargs :mode :program)) (cond ((null alists) nil) (t (cons (list* :instance name (pairs-to-doublets (car alists))) (make-lemma-instances name (cdr alists)))))) ~ev[] Finally, we can package it all together into a hint function. The function takes a pattern, ~c[pat], which must be a translated term, the name of a lemma, ~c[name], and a clause. If some instances of ~c[pat] occur in ~c[clause], then the corresponding instances of ~c[name] are ~c[:USE]d in the computed hint. Otherwise, the hint does not apply. ~bv[] (defun add-corresponding-instances (pat name clause) (declare (xargs :mode :program)) (let ((alists (find-all-instances-list pat clause nil))) (cond ((null alists) nil) (t (list :use (make-lemma-instances name alists)))))) ~ev[] The design of this particular hint function makes it important that the variables of the pattern be the variables of the named lemma and that all of the variables we wish to instantiate occur in the pattern. We could, of course, redesign it to allow ``free variables'' or some sort of renaming. We could now use this hint as shown below: ~bv[] (defthm ... ... :hints ((add-corresponding-instances '(SWAP x) 'ALL-SWAPS-HAVE-THE-PROPERTY clause))) ~ev[] The effect of the hint above is that any time a clause arises in which any instance of ~c[(SWAP x)] appears, we add the corresponding instance of ~c[ALL-SWAPS-HAVE-THE-PROPERTY]. So for example, if Subgoal *1/3.5 contains the subterm ~c[(SWAP (SWAP A))] then this hint fires and makes the system behave as though the hint: ~bv[] (\"Subgoal *1/3.5\" :USE ((:INSTANCE ALL-SWAPS-HAVE-THE-PROPERTY (X A)) (:INSTANCE ALL-SWAPS-HAVE-THE-PROPERTY (X (SWAP A))))) ~ev[] had been present.") (deflabel using-computed-hints-5 :doc ":Doc-Section Miscellaneous Debugging Computed Hints~/~/ We have found that it is sometimes helpful to define hints so that they print out messages to the terminal when they fire, so you can see what hint was generated and which of your computed hints did it. To that end we have defined a macro we sometimes use. Suppose you have a ~c[:hints] specification such as: ~bv[] :hints (computed-hint-fn (hint-expr id)) ~ev[] If you defmacro the macro below you could then write instead: ~bv[] :hints ((show-hint computed-hint-fn 1) (show-hint (hint-expr id) 2)) ~ev[] with the effect that whenever either hint is fired (i.e., returns non-~c[nil]), a message identifying the hint by the marker (1 or 2, above) and the non-~c[nil] value is printed. ~bv[] (defmacro show-hint (hint &optional marker) (cond ((and (consp hint) (stringp (car hint))) hint) (t `(let ((marker ,marker) (ans ,(if (symbolp hint) `(,hint id clause world stable-under-simplificationp) hint))) (if ans (prog2$ (cw \"~~%***** Computed Hint~~#0~~[~~/ (from hint ~~x1)~~]~~%~~x2~~%~~%\" (if (null marker) 0 1) marker (cons (string-for-tilde-@-clause-id-phrase id) ans)) ans) nil))))) ~ev[] Note that when ~c[show-hint] is applied to a hint that is a symbol, e.g., ~c[computed-hint-fn], it applies the symbol to the four computed-hint arguments: ~c[id], ~c[clause], ~c[world], and ~c[stable-under-simplificationp]. If ~c[computed-hint-fn] is of arity 3 the code above would cause an error. One way to avoid it is to write ~bv[] :hints ((show-hints (computed-hint-fn id clause world) 1) (show-hint (hint-expr id) 2)). ~ev[] If you only use computed hints of arity 3, you might eliminate the occurrence of ~c[stable-under-simplificationp] in the definition of ~c[show-hint] above. Putting a ~c[show-hint] around a common hint has no effect. If you find yourself using this utility let us know and we'll consider putting it into the system itself. But it does illustrate that you can use computed hints to do unusual things.") (deflabel using-computed-hints-6 :doc ":Doc-Section Miscellaneous Using the computed-hint-replacement feature~/~/ So far none of our computed hints have used the ~c[:COMPUTED-HINT-REPLACEMENT] feature. We now illustrate that. The ~c[:computed-hint-replacement] feature can easily lead to loops. So as you experiment with the examples in this section and your own hints using this feature, be ready to interrupt the theorem prover and abort! A non-looping use of the ~c[:computed-hint-replacement] feature would be a hint like this: ~bv[] (if (certain-terms-present clause) '(:computed-hint-replacement t :in-theory (enable lemma25)) '(:computed-hint-replacement t :in-theory (disable lemma25))) ~ev[] In this hint, if certain terms are present in ~c[clause], as determined by the function with the obvious name (here undefined), then this hint enables ~c[lemma25] and otherwise disables it. ~c[Lemma25] might be a very expensive lemma, e.g., one that matches frequently and has an expensive and rarely established hypothesis. One might wish it enabled only under certain conditions. Recall that theories are inherited by children. So once ~c[lemma25] is enabled it ``stays'' enabled for the children, until disabled; and vice versa. If the ~c[:computed-hint-replacement] feature were not present and computed hints were always deleted after they had been used, then ~c[lemma25] would be left enabled (or disabled) for all the childen produced by the first firing of the hint. But with the arrangement here, every subgoal gets a theory deemed suitable by the hint, and the hint persists. Now we will set up a toy to allow us to play with computed hints to understand them more deeply. To follow the discussion it is best to execute the following events. ~bv[] (defstub wrapper (x) t) (defaxiom wrapper-axiom (wrapper x) :rule-classes nil) ~ev[] Now submit the following event and watch what happens. ~bv[] (thm (equal u v) :hints (`(:use (:instance wrapper-axiom (x a))))) ~ev[] The theorem prover adds ~c[(wrapper a)] to the goal and then abandons the proof attempt because it cannot prove the subgoal. Since the computed hint is deleted upon use, the hint is not applied to the subgoal (i.e., the child of the goal). What happens if we do the following? ~bv[] (thm (equal u v) :hints (`(:computed-hint-replacement t :use (:instance wrapper-axiom (x a))))) ~ev[] As one might expect, this loops forever: The hint is applied to the child and adds the hypothesis again. When the hint fires, nothing is actually changed, since ~c[(wrapper a)] is already in the subgoal. So let's change the experiment a little. Let's make the hint add the hypothesis ~c[(wrapper p)] where ~c[p] is the first literal of the clause. This is silly but it allows us to explore the behavior of computed hints a little more. ~bv[] (thm (equal u v) :hints (`(:use (:instance wrapper-axiom (x ,(car clause)))))) ~ev[] So in this case, the theorem prover changes the goal to ~bv[] (IMPLIES (WRAPPER (EQUAL U V)) (EQUAL U V)) ~ev[] which then simplifies to ~bv[] (IMPLIES (WRAPPER NIL) (EQUAL U V)) ~ev[] because the concluding equality can be assumed false in the hypothesis (e.g., think of the contrapositive version). Nothing else happens because the hint has been removed and so is not applicable to the child. Now consider the following -- and be ready to interrupt it and abort! ~bv[] (thm (equal u v) :hints (`(:computed-hint-replacement t :use (:instance wrapper-axiom (x ,(car clause)))))) ~ev[] This time the hint is not removed and so is applied to the child. So from ~c[Goal] we get ~bv[] Goal' (IMPLIES (WRAPPER (EQUAL U V)) (EQUAL U V)) ~ev[] and then ~bv[] Goal'' (IMPLIES (AND (WRAPPER (NOT (WRAPPER (EQUAL U V)))) (WRAPPER (EQUAL U V))) (EQUAL U V)) ~ev[] etc. First, note that the hint is repeatedly applied to its children. That is because we wrote ~c[:computed-hint-replacement t]. But second, note that ~c[Goal'] is not even being simplified before ~c[Goal''] is produced from it. If it were being simplified, the ~c[(equal u v)]'s in the hypotheses would be replaced by ~c[nil]. This is a feature. It means after a computed hint has fired, other hints are given a chance at the result, even the hint itself unless it is removed from the list of hints. As an exercise, let's arrange for the hint to stay around and be applied indefinitely but with a simplification between each use of the the hint. To do this we need to pass information from one application of the hint to the next, essentially to say ``stay around but don't fire.'' First, we will define a function to use in the hint. This is more than a mere convenience; it allows the hint to ``reproduce itself'' in the replacement. ~bv[] (defun wrapper-challenge (clause parity) (if parity `(:computed-hint-replacement ((wrapper-challenge clause nil)) :use (:instance wrapper-axiom (x ,(car clause)))) `(:computed-hint-replacement ((wrapper-challenge clause t))))) ~ev[] Note that this function is not recursive, even though it uses its own name. That is because the occurrence of its name is in a quoted constant. Now consider the following. What will it do? ~bv[] (thm (equal u v) :hints ((wrapper-challenge clause t))) ~ev[] First, observe that this is a legal hint because it is a term that mentions only the free variable ~c[CLAUSE]. When defining hint functions you may sometimes think their only arguments are the four variables ~c[id], ~c[clause], ~c[world], and ~c[stable-under-simplificationp]. That is not so. But in your hints you must call those functions so that those are the only free variables. Note also that the occurrence of ~c[clause] inside the ~c[:computed-hint-replacement] is not an occurrence of the variable clause but just a constant. Just store this note away for a moment. We'll return to it momentarily. Second, the basic cleverness of this hint is that every time it fires it reproduces itself with the opposite parity. When the parity is ~c[t] it actually changes the goal by adding a hypothesis. When the parity is ~c[nil] it doesn't change the goal and so allows simplification to proceed -- but it swaps the parity back to ~c[t]. What you can see with this simple toy is that we can use the computed hints to pass information from parent to child. Ok, so what happens when the event above is executed? Try it. You will see that ACL2 applied the hint the first time. It doesn't get around to printing the output because an error is caused before it can print. But here is a blow-by-blow description of what happens. The hint is evaluated on ~c[Goal] with the ~c[clause] ~c[((equal u v))]. It produces a hint exactly as though we had typed: ~bv[] (\"Goal\" :use (:instance wrapper-axiom (x (equal u v)))) ~ev[] which is applied to this goal. In addition, it produces the new hints argument ~bv[] :hints ((wrapper-challenge clause nil)). ~ev[] By applying the ~c[\"Goal\"] hint we get the new subgoal ~bv[] Goal' (implies (wrapper (equal u v)) (equal u v)) ~ev[] but this is not printed because, before printing it, the theorem prover looks for hints to apply to it and finds ~bv[] (wrapper-challenge clause nil) ~ev[] That is evaluated and produces a hint exactly as though we had typed: ~bv[] (\"Goal'\" ) ~ev[] and the new hints argument: ~bv[] :hints ((wrapper-challenge clause nil)). ~ev[] But if you supply the hint ~c[(\"Goal'\" )], ACL2 will signal an error because it does not allow you to specify an empty hint! So the definition of ~c[wrapper-challenge] above is almost correct but fatally flawed. We need a non-empty ``no-op'' hint. One such hint is to tell the system to expand a term that will always be expanded anyway. So undo ~c[wrapper-challenge], redefine it, and try the proof again. Now remember the observation about ~c[clause] that we asked you to ``store'' above. The new definition of ~c[wrapper-challenge] illustrates what we meant. Note that the first formal parameter of ~c[wrapper-challenge], below, is no longer named ~c[clause] but is called ~c[cl] instead. But the ``call'' of ~c[wrapper-challenge] in the replacements is on ~c[clause]. This may seem to violate the rule that a function definition cannot use variables other than the formals. But the occurrences of ~c[clause] below are not variables but constants in an object that will eventually be treated as hint term. ~bv[] :ubt wrapper-challenge (defun wrapper-challenge (cl parity) (if parity `(:computed-hint-replacement ((wrapper-challenge clause nil)) :use (:instance wrapper-axiom (x ,(car cl)))) `(:computed-hint-replacement ((wrapper-challenge clause t)) :expand ((atom zzz))))) (thm (equal u v) :hints ((wrapper-challenge clause t))) ~ev[] This time, things go as you might have expected! ~c[Goal'] is produced and simplified, to ~bv[] Goal'' (implies (wrapper nil) (equal u v)). ~ev[] Simplification gets a chance because when the new hint ~c[(wrapper-challenge clause nil)] is fired it does not change the goal. But it does change the parity in the hints argument so that before ~c[Goal''] is simplified again, the hint fires and adds the hypothesis: ~bv[] Goal''' (IMPLIES (AND (WRAPPER (NOT (WRAPPER NIL))) (WRAPPER NIL)) (EQUAL U V)). ~ev[] This simplifies, replacing the first ~c[(NOT (WRAPPER NIL))] by ~c[NIL], since ~c[(WRAPPER NIL)] is known to be true here. Thus the goal simplifies to ~bv[] Goal'4' (IMPLIES (WRAPPER NIL) (EQUAL U V)). ~ev[] The process repeats indefinitely. So we succeeded in getting a hint to fire indefinitely but allow a full simplification between rounds.") (deflabel using-computed-hints-7 :doc ":Doc-Section Miscellaneous Using the ~c[stable-under-simplificationp] flag~/~/ A problem with the example in ~il[using-computed-hints-6] is that exactly one simplification occurs between each (effective) firing of the hint. Much more commonly we wish to fire a hint once a subgoal has become stable under simplification. A classic example of this is when we are dealing with an interpreter for some state machine. We typically do not want the ``step'' function to open up on the symbolic representation of a state until that state has been maximally simplified. We will illustrate with a simple state machine. Let us start by defining the step function, ~c[stp], and the corresponding ~c[run] function that applies it a given number of times. ~bv[] (defun stp (s) (+ 1 s)) (defun run (s n) (if (zp n) s (run (stp s) (- n 1)))) ~ev[] The step function here is trivial: a state is just a number and the step function increments it. In this example we will not be interested in the theorems we prove but in how we prove them. The formula we will focus on is ~bv[] (thm (equal (run s 7) xxx)) ~ev[] This is not a theorem, of course. But we want to test our advice on non-theorems because we do not want the advice to work only for proofs that succeed. (In the past, we gave advice about using computed hints and that advice caused the theorem prover to run forever when given formulas that it couldn't prove -- but most of the time the system is presented with formulas it cannot prove!) Furthermore, without some kind of additional rules, the ~c[(run s 7)] expression in the conjecture above will not expand at all, because ACL2's heuristics do not approve. In fact, we do not want to take chances that ~c[run] will be expanded -- we want to control its expansion completely. Therefore, disable ~c[run]. ~bv[] (in-theory (disable run)) ~ev[] Now, what do we want? (That is always a good question to ask!) We want ~c[(run s 7)] to expand ``slowly.'' In particular, we want it to expand once, to ~c[(run (stp s) 6)]. Then we want the ~c[stp] to be expanded and fully simplified before the ~c[run] expression is expanded again. That is, we want to force the expansion of ~c[run] whenever the goal is stable under simplification. This is sometimes called ``staged simplification.'' We can achieve staged simplification for any given function symbol by defining the functions shown below and then using a simple computed hint: ~bv[] (thm (equal (run s 7) xxx) :hints ((stage run))) ~ev[] By inspecting how ~c[stage] is defined you can see how to extend it, but we explain as we go. To experiment, you can just paste the definitions (and defmacro) below into your ACL2 shell and then try the ~c[thm] command. First, define this pair of mutually recursive functions. ~c[Find-first-call] finds the first call of the function symbol ~c[fn] in a given term. ~bv[] (mutual-recursion (defun find-first-call (fn term) ; Find the first call of fn in term. (cond ((variablep term) nil) ((fquotep term) nil) ((equal (ffn-symb term) fn) term) (t (find-first-call-lst fn (fargs term))))) (defun find-first-call-lst (fn lst) ; Find the first call of fn in a list of terms. (cond ((endp lst) nil) (t (or (find-first-call fn (car lst)) (find-first-call-lst fn (cdr lst))))))) ~ev[] We will arrange for the computed hint to generate an ~c[:EXPAND] hint for the first call of ~c[fn], whenever the goal becomes stable under simplification. If no call is found, the hint will do nothing. To make sure the hint will not loop indefinitely (for example, by forcing ~c[fn] to expand only to have the rewriter ``fold'' it back up again), we will provide the hint with a bound that stops it after some number of iterations. Here is the basic function that creates the ~c[expand] hint and replaces itself to count down. ~bv[] (defun stage1 (fn max clause flg) ; If the clause is stable under simplification and there is a call of ; fn in it, expand it. But don't do it more than max times. (let ((temp (and flg (find-first-call-lst fn clause)))) (if temp (if (zp max) (cw \"~~%~~%HINT PROBLEM: The maximum repetition count of ~~ your STAGE hint been reached without eliminating ~~ all of the calls of ~~x0. You could supply a larger ~~ count with the optional second argument to STAGE ~~ (which defaults to 100). But think about what is ~~ happening! Is each stage permanently eliminating a ~~ call of ~~x0?~~%~~%\" fn) `(:computed-hint-replacement ((stage1 ',fn ,(- max 1) clause stable-under-simplificationp)) :expand (,temp))) nil))) ~ev[] Suppose that when ~c[stage1] is called, ~c[fn] is the function we want to expand, ~c[max] is the maximum number of iterations of this expansion, ~c[clause] is the current goal clause, and ~c[flg] is the value of the ~c[stable-under-simplificationp] flag. Then if ~c[clause] is stable and we can find a call of ~c[fn] in it, we ask whether ~c[max] is exhausted. If so, we print an ``error message'' to the comment window with ~ilc[cw] and return ~c[nil] (the value of ~c[cw]). That ~c[nil] means the hint does nothing. But if ~c[max] is not yet exhausted, we return a new hint. As you can see above, the hint replaces itself with another ~c[stage1] hint with the same ~c[fn] and a decremented ~c[max] to be applied to the new ~c[clause] and the then-current value of ~c[stable-under-simplificationp]. The hint also contains an ~c[:expand] directive for the call of ~c[fn] found. Thus, if the computed hint was: ~bv[] (stage1 'run 5 clause stable-under-simplificationp) ~ev[] and ~c[(run s 7)] occurs in the clause, then it will generate ~bv[] (:computed-hint-replacement ((stage1 'run 4 clause stable-under-simplificationp)) :expand ((run s 7))) ~ev[] which will in turn replace the old ~c[stage1] hint with the new one and will apply ~c[:expand ((run s 7))] to the current goal. We can make this more convenient by defining the macro: ~bv[] (defmacro stage (fn &optional (max '100)) `(stage1 ',fn ,max clause stable-under-simplificationp)) ~ev[] Note that the macro allows us to either provide the maximum bound or let it default to 100. Henceforth, we can type ~bv[] (thm (equal (run s 7) xxx) :hints ((stage run))) ~ev[] to stage the opening of ~c[run] up to 100 times, or we can write ~bv[] (thm (equal (run s 7) xxx) :hints ((stage run 5))) ~ev[] to stage it only 5 times. In the latter example, the system with print a ``error message'' after the fifth expansion. Note that if we executed ~bv[] (set-default-hints '((stage run))) ~ev[] then we could attack all theorems (involving ~c[run]) with staged simplification (up to bound 100), without typing an explicit hint. ~bv[] (thm (equal (run s 7) xxx)) ~ev[] Using techniques similar to those above we have implemented ``priority phased simplification'' and provided it as a book. See community book ~c[books/misc/priorities.lisp]. This is an idea suggested by Pete Manolios, by which priorities may be assigned to rules and then the simplifier simplifies each subgoal maximally under the rules of a given priority before enabling the rules of the next priority level. The book above documents both how we implement it with computed hints and how to use it. Here is another example of using the ~c[stable-under-simplificationp] flag to delay certain actions. It defines a default hint, ~pl[DEFAULT-HINTS], which will enable ~il[non-linear-arithmetic] on precisely those goals which are stable-under-simplificationp. It also uses the ~c[HISTORY] and ~c[PSPV] variables to determine when toggling ~il[non-linear-arithmetic] is appropriate. These variables are documented only in the source code. If you start using these variables extensively, please contact the developers of ACL2 or Robert Krug (~c[rkrug@cs.utexas.edu]) and let us know how we can help. ~bv[] (defun nonlinearp-default-hint (stable-under-simplificationp hist pspv) (cond (stable-under-simplificationp (if (not (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp)) '(:computed-hint-replacement t :nonlinearp t) nil)) ((access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp) (if (not (equal (caar hist) 'SETTLED-DOWN-CLAUSE)) '(:computed-hint-replacement t :nonlinearp nil) nil)) (t nil))) ~ev[]") (deflabel using-computed-hints-8 :doc ":Doc-Section Miscellaneous Some Final Comments~/ None of the examples show the use of the variable ~c[WORLD], which is allowed in computed hints. There are some (undocumented) ACL2 utilities that might be useful in programming hints, but these utilities need access to the ACL2 logical world (~pl[world]). A very useful fact to know is that ~c[(table-alist name world)] returns an alist representation of the current value of the ~ilc[table] named ~c[name]. The ACL2 source code is littered with ~c[:]~ilc[program] mode functions for manipulating world. In our source code, the world is usually bound a variable named ~c[wrld]; so searching our code for that name might be helpful.~/ Using these utilities to look at the ~c[WORLD] one can, for example, determine whether a symbol is defined recursively or not, get the body and formals of a defined function, or fetch the statement of a given lemma. Because these utilities are not yet documented, we do not expect users to employ ~c[WORLD] in computed hints. But experts might and it might lead to the formulation of a more convenient language for computed hints. None of our examples illustrated the 7 argument form of a computed hint, ~c[(fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX)]. When used, the variables ~c[HIST], ~c[PSPV], and ~c[CTX], are bound to the clause history, the package of ``special variables'' governing the clause, and the ``error message context.'' These variables are commonly used throughout our source code but are, unfortunately, undocumented. Again, we expect a few experts will find them useful in developing computed hints. If you start using computed hints extensively, please contact the developers of ACL2 and let us know what you are doing with them and how we can help.") (deflabel using-computed-hints :doc ":Doc-Section Miscellaneous how to use computed hints~/ Computed hints (~pl[computed-hints]) are extraordinarily powerful. We show a few examples here to illustrate their use. We recommend that the ~c[using-computed-hints-n] topics be read in the order ~c[using-computed-hints-1], ~c[using-computed-hints-2], and so on. ~/~/ :CITE using-computed-hints-1 :CITE using-computed-hints-2 :CITE using-computed-hints-3 :CITE using-computed-hints-4 :CITE using-computed-hints-5 :CITE using-computed-hints-6 :CITE using-computed-hints-7 :CITE set-default-hints :CITE add-default-hints :CITE remove-default-hints :CITE using-computed-hints-8") ; Essay on Trust Tags (Ttags) ; Here we place the bulk of the code for handling trust tags (ttags). ; A trust tag (ttag) is a keyword that represents where to place responsibility ; for potentially unsafe operations. For example, suppose we define a ; function, foo, that calls sys-call. Any call of sys-call is potentially ; unsafe, in the sense that it can do things not normally expected during book ; certification, such as overwriting a file or a core image. But foo's call of ; sys-call may be one that can be explained somehow as safe. At any rate, ; translate11 allows this call of sys-call if there is an active trust tag ; (ttag), in the sense that the key :ttag is bound to a non-nil value in the ; acl2-defaults-table. See :doc defttag for more on ttags, in particular, the ; ``TTAG NOTE'' mechanism for determining which files need to be inspected in ; order to validate the proper use of ttags. ; There is a subtlety to the handling of trust tags by include-book in the case ; of uncertified books. Consider the following example. We have two books, ; sub.lisp and top.lisp, but we will be considering two versions of sub.lisp, ; as indicated. ; sub.lisp ; (in-package "ACL2") ; ; (defttag :sub-ttag1) ; will be uncommented later ; (defun f (x) x) ; top.lisp ; (in-package "ACL2") ; (encapsulate ; () ;; start lemmas for sub ; ; (include-book "sub") ; ) ; Now take the following steps: ; In a fresh ACL2 session: ; (certify-book "sub") ; (u) ; (certify-book "top") ; Now edit sub.lisp by uncommenting the defttag form. Then, in a fresh ACL2 ; session: ; (certify-book "sub" 0 t :ttags :all) ; (u) ; (include-book "top") ; The (include-book "top") form will fail when the attempt is made to include ; the book "sub". To see why, first consider what happens when a superior book ; "top" includes a subsidiary certified book "sub". When include-book-fn1 is ; called in support of including "sub", the second call of ; chk-acceptable-ttags1 therein uses the certificate's ttags, stored in ; variable cert-ttags, to refine the state global 'ttags-allowed. After that ; check and refinement, which prints ttag notes based on cert-ttags, ; ttags-allowed is bound to cert-ttags for the inclusion of "sub", with further ; ttag notes omitted during that inclusion. ; Returning to our example, the recertification of "sub" results in the ; addition of a ttag for "sub" that has not yet been noticed for "top". So ; when we include "top", state global ttags-allowed is bound to nil, since that ; is the cert-ttags for "top". When sub is encountered, its additional ttag is ; not allowed (because ttags-allowed is nil), and we get an error. ; In a way, this error is unfortunate; after all, top is uncertified, and we ; wish to allow inclusion of uncertified books (with a suitable warning). But ; it seems non-trivial to re-work the scheme described above. In particular, ; it seems that we would have to avoid binding ttags-allowed to nil when ; including "top", before we realize that "top" is uncertified. (The check on ; sub-book checksums occurs after events are processed.) We could eliminate ; this "barrier" under which we report no further ttag notes, but that could ; generate a lot of ttag notes -- even if we defer, we may be tempted to print ; a note for each defttag encountered in a different sub-book. ; That said, if the need is great enough for us to avoid the error described ; above, we'll figure out something. ; Finally, we note that trust tags are always in the "KEYWORD" package. This ; simplifies the implementation of provisional certification. Previously ; (after Version_4.3 but before the next release), Sol Swords sent an example ; in which the Complete operation caused an error, the reason being that an ; unknown package was being used in the post-alist in the certificate file. (defmacro ttags-seen () ; The following is a potentially useful utility, which we choose to include in ; the ACL2 sources rather than in community book books/hacking/hacker.lisp. ; Thanks to Peter Dillinger for his contribution. ":Doc-Section Miscellaneous list some declared trust tags (ttags)~/ ~bv[] General Forms: :ttags-seen (ttags-seen) ~ev[] Suppose the output is as follows. ~bv[] (T NIL) (FOO \"/home/bob/bar.lisp\" \"/home/cindy/bar.lisp\") Warning: This output is minimally trustworthy (see :DOC TTAGS-SEEN). ~ev[] This output indicates that the current logical ~il[world] has seen the declaration of trust tag ~c[T] at the top-level (~pl[defttag]) and the declaration of trust tag ~c[FOO] in the two books included from the listed locations. The warning emphasizes that this command cannot be used to validate the ``purity'' of an ACL2 session, because using a ttag renders enough power to hide from this or any other command the fact that the ttag was ever declared.~/ As discussed elsewhere (~pl[defttag]), the only reliable way to validate the ``purity'' of a session is to watch for ``~c[TTAG NOTE]'' output. Another shortcoming of this command is that it only checks the current logical ~il[world] for ttag declarations. For example, one could execute a ~ilc[defttag] event; then use ~ilc[progn!] and ~ilc[set-raw-mode] to replace system functions with corrupt definitions or to introduce inconsistent axioms in the ~ilc[ground-zero] ~il[world]; and finally, execute ~c[:]~ilc[ubt!]~c[ 1] to remove all evidence of the ttag in the ~il[world] while leaving in place the corrupt definitions or axioms. The base world is now tainted, meaning we could prove ~c[nil] or certify a book that proves ~c[nil], but the resulting session or book would contain no trace of the ttag that tainted it! Despite shortcomings, this command might be useful to system hackers. It also serves to illustrate the inherent flaw in asking a session whether or how it is ``tainted'', justifying the ``~c[TTAG NOTE]'' approach (~pl[defttag])." '(mv-let (col state) (fmt1 "~*0Warning: This output is minimally trustworthy (see :DOC ~ ~x1).~%" `((#\0 "~%" "~q*" "~q*" "~q*" ,(global-val 'ttags-seen (w state))) (#\1 . ttags-seen)) 0 (standard-co state) state ()) (declare (ignore col)) (value ':invisible))) (defrec certify-book-info (full-book-name . cert-op) nil) ; could replace with t sometime (defun active-book-name (wrld state) ; This returns the full book name (an absolute pathname ending in .lisp) of the ; book currently being included, if any. Otherwise, this returns the full book ; name of the book currently being certified, if any. (or (car (global-val 'include-book-path wrld)) (let ((x (f-get-global 'certify-book-info state))) (cond (x (let ((y (access certify-book-info x :full-book-name))) (assert$ (stringp y) y))))))) (defrec deferred-ttag-note (val active-book-name . include-bookp) t) (defun fms-to-standard-co (str alist state evisc-tuple) ; Warning: This function is used for printing ttag notes, so do not change ; *standard-co*, not even to (standard-co state)! (fms str alist *standard-co* state evisc-tuple)) (defun print-ttag-note (val active-book-name include-bookp deferred-p state) ; Active-book-name is nil or else satisfies chk-book-name. If non-nil, we ; print it as "book x" where x omits the .lisp extension, since if the defttag ; event might not be in the .lisp file. For example, it could be in the ; expansion-alist in the book's certificate or, if the book is not certified, ; it could be in the .port file. ; If include-bookp is a cons, then its cdr satisfies chk-book-name. (flet ((book-name-root (book-name) (subseq book-name 0 (- (length book-name) 5)))) (pprogn (let* ((book-name (cond (active-book-name (book-name-root active-book-name)) (t ""))) (included (if include-bookp " (for included book)" "")) (str (if active-book-name "TTAG NOTE~s0: Adding ttag ~x1 from book ~s2." "TTAG NOTE~s0: Adding ttag ~x1 from the top level loop.")) (bound (+ (length included) (length str) (length (symbol-package-name val)) 2 ; for "::" (length (symbol-name val)) (length book-name)))) (mv-let (erp val state) (state-global-let* ((fmt-hard-right-margin bound set-fmt-hard-right-margin) (fmt-soft-right-margin bound set-fmt-soft-right-margin)) (pprogn (fms-to-standard-co str (list (cons #\0 included) (cons #\1 val) (cons #\2 book-name)) state nil) (cond (deferred-p state) (t (newline *standard-co* state))) (value nil))) (declare (ignore erp val)) state)) (cond ((and (consp include-bookp) ; (cons ctx full-book-name) (not deferred-p)) (warning$ (car include-bookp) ; ctx "Ttags" "The ttag note just printed to the terminal indicates a ~ modification to ACL2. To avoid this warning, supply ~ an explicit :TTAGS argument when including the book ~ ~x0." (book-name-root (cdr include-bookp)) ; full-book-name )) (t state))))) (defun show-ttag-notes1 (notes state) (cond ((endp notes) (newline *standard-co* state)) (t (pprogn (let ((note (car notes))) (print-ttag-note (access deferred-ttag-note note :val) (access deferred-ttag-note note :active-book-name) (access deferred-ttag-note note :include-bookp) t state)) (show-ttag-notes1 (cdr notes) state))))) (defun show-ttag-notes-fn (state) (let* ((notes0 (f-get-global 'deferred-ttag-notes-saved state)) (notes (remove-duplicates-equal notes0))) (cond (notes (pprogn (cond ((equal notes notes0) state) (t (fms-to-standard-co "Note: Duplicates have been removed from the ~ list of deferred ttag notes before printing ~ them below.~|" nil state nil))) (show-ttag-notes1 (reverse notes) state) (f-put-global 'deferred-ttag-notes-saved nil state))) (t (fms-to-standard-co "All ttag notes have already been printed.~|" nil state nil))))) (defmacro show-ttag-notes () '(pprogn (show-ttag-notes-fn state) (value :invisible))) (defun set-deferred-ttag-notes (val state) ":Doc-Section switches-parameters-and-modes modify the verbosity of ~c[TTAG NOTE] printing~/ ~bv[] General Form: (set-deferred-ttag-notes val state) ~ev[] where ~c[val] is ~c[t] or ~c[nil].~/ ~l[defttag] for a discussion of trust tags (ttags). By default, a ``~c[TTAG NOTE]'' is printed in order to indicate reliance on a ttag. When many such notes are printed, it may be desirable to avoid seeing them all. Upon evaluation of the form ~bv[] (set-deferred-ttag-notes t state) ~ev[] ACL2 will enter a deferred mode for the printing of ttag notes. In this mode, only the first ttag note is printed for each top-level command, and the remainder are summarized before the next top-level prompt (if any) is printed, hence before the next command is evaluated. That summary is merely a list of ttags, but the summary explains that the full ttag notes may be printed with the command ~c[(show-ttag-notes)]. Note that ~c[(show-ttag-notes)] spares you duplicate ttag notes. If you want to see every ttag note as it would normally appear, for maximum security, do not evaluate the command ~c[(set-deferred-ttag-notes t state)]. You can undo the effect of this command, returning to an immediate mode for printing ttag notes, by evaluating: ~bv[] (set-deferred-ttag-notes nil state) ~ev[]" (let ((ctx 'set-deferred-ttag-notes) (immediate-p (not val))) (cond ((member-eq val '(t nil)) (pprogn (cond ((eq immediate-p (eq (f-get-global 'deferred-ttag-notes state) :not-deferred)) (observation ctx "No change; ttag notes are already ~@0being ~ deferred." (if immediate-p "not " ""))) ((and immediate-p (consp (f-get-global 'deferred-ttag-notes state))) (pprogn (fms-to-standard-co "Note: Enabling immediate printing mode for ttag ~ notes. Below are the ttag notes that have been ~ deferred without being reported." nil state nil) (f-put-global 'deferred-ttag-notes-saved (f-get-global 'deferred-ttag-notes state) state) (f-put-global 'deferred-ttag-notes nil state) (show-ttag-notes-fn state))) (immediate-p (pprogn (observation ctx "Enabling immediate printing mode for ttag notes.") (f-put-global 'deferred-ttag-notes :not-deferred state) (f-put-global 'deferred-ttag-notes-saved nil state))) (t (pprogn (fms-to-standard-co "TTAG NOTE: Printing of ttag notes is being put into ~ deferred mode.~|" nil state nil) (f-put-global 'deferred-ttag-notes :empty state)))) (value :invisible))) (t (er soft ctx "The only legal values for set-deferred-ttag-notes are ~x0 and ~ ~x1. ~ The value ~x2 is thus illegal." t nil val))))) (defun ttags-from-deferred-ttag-notes1 (notes) ; Notes is formed by pushing ttag notes, hence we want to consider the ; corresponding ttags in reverse order. But we'll want to reverse this ; result. (cond ((endp notes) nil) (t (add-to-set-eq (access deferred-ttag-note (car notes) :val) (ttags-from-deferred-ttag-notes1 (cdr notes)))))) (defun ttags-from-deferred-ttag-notes (notes) (reverse (ttags-from-deferred-ttag-notes1 notes))) (defun print-deferred-ttag-notes-summary (state) (let ((notes (f-get-global 'deferred-ttag-notes state))) (cond ((null notes) (f-put-global 'deferred-ttag-notes :empty state)) ((atom notes) ; :empty or :not-deferred state) (t (pprogn (f-put-global 'deferred-ttag-notes-saved notes state) (fms-to-standard-co "TTAG NOTE: Printing of ttag notes has been deferred for the ~ following list of ttags:~| ~y0.To print the deferred ttag ~ notes: ~y1." (list (cons #\0 (ttags-from-deferred-ttag-notes notes)) (cons #\1 '(show-ttag-notes))) state nil) (f-put-global 'deferred-ttag-notes :empty state)))))) (defun notify-on-defttag (val active-book-name include-bookp state) ; Warning: Here we must not call observation or any other printing function ; whose output can be inhibited. The tightest security for ttags is obtained ; by searching for "TTAG NOTE" strings in the output. (cond ((or (f-get-global 'skip-notify-on-defttag state) (eq include-bookp :quiet)) state) ((and (null include-bookp) (equal val (ttag (w state)))) ; Avoid some noise, e.g. in encapsulate when there is already an active ttag. state) ((eq (f-get-global 'deferred-ttag-notes state) :not-deferred) (print-ttag-note val active-book-name include-bookp nil state)) ((eq (f-get-global 'deferred-ttag-notes state) :empty) (pprogn (print-ttag-note val active-book-name include-bookp nil state) (f-put-global 'deferred-ttag-notes nil state))) (t (pprogn (cond ((null (f-get-global 'deferred-ttag-notes state)) (fms-to-standard-co "TTAG NOTE: Deferring one or more ttag notes until the current ~ top-level command completes.~|" nil state nil)) (t state)) (f-put-global 'deferred-ttag-notes (cons (make deferred-ttag-note :val val :active-book-name active-book-name :include-bookp include-bookp) (f-get-global 'deferred-ttag-notes state)) state))))) (defun ttag-allowed-p (ttag ttags active-book-name acc) ; We are executing a defttag event (or more accurately, a table event that ; could correspond to a defttag event). We return nil if the ttag is illegal, ; else t if no update to ttags is required, else a new, more restrictive value ; for ttags that recognizes the association of ttag with active-book-name. (cond ((endp ttags) nil) ((eq ttag (car ttags)) (revappend acc (cons (list ttag active-book-name) (cdr ttags)))) ((atom (car ttags)) (ttag-allowed-p ttag (cdr ttags) active-book-name (cons (car ttags) acc))) ((eq ttag (caar ttags)) (cond ((or (null (cdar ttags)) (member-equal active-book-name (cdar ttags))) t) (t nil))) (t (ttag-allowed-p ttag (cdr ttags) active-book-name (cons (car ttags) acc))))) (defun chk-acceptable-ttag1 (val active-book-name ttags-allowed ttags-seen include-bookp ctx state) ; An error triple (mv erp pair state) is returned, where if erp is nil then ; pair is either of the form (ttags-allowed1 . ttags-seen1), indicating a ; refined value for ttags-allowed and an extended value for ttags-seen, else is ; nil, indicating no such update. By a "refined value" above, we mean that if ; val is a symbol then it is replaced in ttags-allowed by (val ; active-book-name). However, val may be of the form (symbol), in which case ; no refinement takes place, or else of the form (symbol . filenames) where ; filenames is not nil, in which case active-book-name must be a member of ; filenames or we get an error. Active-book-name is nil, representing the top ; level, or a string, generally thought of as an absolute filename. ; This function must be called if we are to add a ttag. In particular, it ; should be called under table-fn; it would be a mistake to call this only ; under defttag, since then one could avoid this function by calling table ; directly. ; This function is where we call notify-on-defttag, which prints strings that ; provide the surest way for someone to check that functions requiring ttags ; are being called in a way that doesn't subvert the ttag mechanism. (let* ((ttags-allowed0 (cond ((eq ttags-allowed :all) t) (t (ttag-allowed-p val ttags-allowed active-book-name nil)))) (ttags-allowed1 (cond ((eq ttags-allowed0 t) ttags-allowed) (t ttags-allowed0)))) (cond ((not ttags-allowed1) (er soft ctx "The ttag ~x0 associated with ~@1 is not among the set of ttags ~ permitted in the current context, specified as follows:~| ~ ~x2.~|See :DOC defttag.~@3" val (if active-book-name (msg "file ~s0" active-book-name) "the top level loop") ttags-allowed (cond ((null (f-get-global 'skip-notify-on-defttag state)) "") (t (msg " This error is unusual since it is occurring while ~ including a book that appears to have been certified, in ~ this case, the book ~x0. Most likely, that book needs to ~ be recertified, though a temporary workaround may be to ~ delete its certificate (i.e., its .cert file). Otherwise ~ see :DOC make-event-details, section ``A note on ttags,'' ~ for a possible explanation." (f-get-global 'skip-notify-on-defttag state)))))) (t (pprogn (notify-on-defttag val active-book-name include-bookp state) (let ((old-filenames (cdr (assoc-eq val ttags-seen)))) (cond ((member-equal active-book-name old-filenames) (value (cons ttags-allowed1 ttags-seen))) (t (value (cons ttags-allowed1 (put-assoc-eq val (cons active-book-name old-filenames) ttags-seen))))))))))) (defun chk-acceptable-ttag (val include-bookp ctx wrld state) ; See the comment in chk-acceptable-ttag1, which explains the result for the ; call of chk-acceptable-ttag1 below. (cond ((null val) (value nil)) (t (chk-acceptable-ttag1 val (active-book-name wrld state) (f-get-global 'ttags-allowed state) (global-val 'ttags-seen wrld) include-bookp ctx state)))) (defun chk-acceptable-ttags2 (ttag filenames ttags-allowed ttags-seen include-bookp ctx state) (cond ((endp filenames) (value (cons ttags-allowed ttags-seen))) (t (er-let* ((pair (chk-acceptable-ttag1 ttag (car filenames) ttags-allowed ttags-seen include-bookp ctx state))) (mv-let (ttags-allowed ttags-seen) (cond ((null pair) (mv ttags-allowed ttags-seen)) (t (mv (car pair) (cdr pair)))) (chk-acceptable-ttags2 ttag (cdr filenames) ttags-allowed ttags-seen include-bookp ctx state)))))) (defun chk-acceptable-ttags1 (vals active-book-name ttags-allowed ttags-seen include-bookp ctx state) ; See chk-acceptable-ttag1 for a description of the value returned based on the ; given active-book-name, tags-allowed, and ttags-seen. Except, for this ; function, an element of vals can be a pair (tag . filenames), in which case ; active-book-name is irrelevant, as it is replaced by each filename in turn. ; If every element of vals has that form then active-book-name is irrelevant. (cond ((endp vals) (value (cons ttags-allowed ttags-seen))) (t (er-let* ((pair (cond ((consp (car vals)) (chk-acceptable-ttags2 (caar vals) (cdar vals) ttags-allowed ttags-seen include-bookp ctx state)) (t (chk-acceptable-ttag1 (car vals) active-book-name ttags-allowed ttags-seen include-bookp ctx state))))) (mv-let (ttags-allowed ttags-seen) (cond ((null pair) (mv ttags-allowed ttags-seen)) (t (mv (car pair) (cdr pair)))) (chk-acceptable-ttags1 (cdr vals) active-book-name ttags-allowed ttags-seen include-bookp ctx state)))))) (defun chk-acceptable-ttags (vals include-bookp ctx wrld state) ; See chk-acceptable-ttag1 for a description of the value returned based on the ; current book being included (if any), the value of state global ; 'tags-allowed, and the value of world global 'ttags-seen. (chk-acceptable-ttags1 vals (active-book-name wrld state) (f-get-global 'ttags-allowed state) (global-val 'ttags-seen wrld) include-bookp ctx state)) ; Next we handle the table event. We formerly did this in other-events.lisp, ; but in v2-9 we moved it here, in order to avoid a warning in admitting ; add-pc-command-1 that the *1* function for table-fn is undefined. (defun chk-table-nil-args (op bad-arg bad-argn ctx state) ; See table-fn1 for representative calls of this weird little function. (cond (bad-arg (er soft ctx "Table operation ~x0 requires that the ~n1 argument to ~ TABLE be nil. Hence, ~x2 is an illegal ~n1 argument. ~ See :DOC table." op bad-argn bad-arg)) (t (value nil)))) (defun chk-table-guard (name key val ctx wrld state) ; This function returns an error triple. In the non-error case, the value is ; nil except when it is a pair as described in chk-acceptable-ttag1, based on ; the current book being included (if any), the value of state global ; 'tags-allowed, and the value of world global 'ttags-seen. (cond ((and (eq name 'acl2-defaults-table) (eq key :include-book-dir-alist) (not (f-get-global 'modifying-include-book-dir-alist state))) (er soft ctx "Illegal attempt to set the :include-book-dir-alist field of the ~ acl2-defaults-table. This can only be done by calling ~ add-include-book-dir or delete-include-book-dir.")) (t (let ((term (getprop name 'table-guard *t* 'current-acl2-world wrld))) (er-progn (mv-let (erp okp latches) (ev term (list (cons 'key key) (cons 'val val) (cons 'world wrld)) state nil nil nil) (declare (ignore latches)) (cond (erp (pprogn (error-fms nil ctx (car okp) (cdr okp) state) (er soft ctx "The TABLE :guard for ~x0 on the key ~x1 and value ~x2 ~ could not be evaluated." name key val))) (okp (value nil)) (t (er soft ctx "The TABLE :guard for ~x0 disallows the combination of key ~ ~x1 and value ~x2. The :guard is ~x3. See :DOC table." name key val (untranslate term t wrld))))) (if (and (eq name 'acl2-defaults-table) (eq key :ttag)) (chk-acceptable-ttag val nil ctx wrld state) (value nil))))))) (defun chk-table-guards-rec (name alist ctx pair wrld state) (if alist (er-let* ((new-pair (chk-table-guard name (caar alist) (cdar alist) ctx wrld state))) (if (and pair new-pair) (assert$ (and (eq name 'acl2-defaults-table) (eq (caar alist) :ttag)) (er soft ctx "It is illegal to specify the :ttag twice in ~ the acl2-defaults-table.")) (chk-table-guards-rec name (cdr alist) ctx new-pair wrld state))) (value pair))) (defun chk-table-guards (name alist ctx wrld state) ; Consider the case that name is 'acl2-defaults-table. We do not allow a ; transition from a non-nil (ttag wrld) to a nil (ttag wrld) at the top level, ; but no such check will be made by chk-table-guard if :ttag is not bound in ; alist. See chk-acceptable-ttag. (er-let* ((pair (cond ((and (eq name 'acl2-defaults-table) (null (assoc-eq :ttag alist))) (chk-acceptable-ttag nil nil ctx wrld state)) (t (value nil))))) (chk-table-guards-rec name alist ctx pair wrld state))) (defun put-assoc-equal-fast (name val alist) ; If there is a large number of table events for a given table all with ; different keys, the use of assoc-equal to update the table (in table-fn1) ; causes a quadratic amount of cons garbage. The following is thus used ; instead. (declare (xargs :guard (alistp alist))) (if (assoc-equal name alist) (put-assoc-equal name val alist) (acons name val alist))) (defun global-set? (var val wrld old-val) (if (equal val old-val) wrld (global-set var val wrld))) (defun cltl-def-from-name2 (fn stobj-function axiomatic-p wrld) ; Normally we expect to find the cltl definition of fn at the first ; 'cltl-command 'global-value triple. But if fn is introduced by encapsulate ; then we may have to search further. Try this, for example: ; (encapsulate ((f (x) x)) ; (local (defun f (x) x)) ; (defun g (x) (f x))) ; (cltl-def-from-name 'f nil (w state)) (cond ((endp wrld) nil) ((and (eq 'cltl-command (caar wrld)) (eq 'global-value (cadar wrld)) (let ((cltl-command-value (cddar wrld))) (assoc-eq fn (if stobj-function (nth (if axiomatic-p 6 4) cltl-command-value) (cdddr cltl-command-value)))))) (t (cltl-def-from-name2 fn stobj-function axiomatic-p (cdr wrld))))) (defrec absstobj-info (st$c . logic-exec-pairs) t) (defun cltl-def-from-name1 (fn stobj-function axiomatic-p wrld) ; See cltl-def-from-name, which is a wrapper for this function in which ; axiomatic-p is nil. When axiomatic-p is t, then we are to return a function ; suitable for oneify, which in the stobj case is the axiomatic definition ; rather than the raw Lisp definition. (and (function-symbolp fn wrld) (let* ((event-number (getprop (or stobj-function fn) 'absolute-event-number nil 'current-acl2-world wrld)) (wrld (and event-number (lookup-world-index 'event event-number wrld))) (def (and wrld (cltl-def-from-name2 fn stobj-function axiomatic-p wrld)))) (and def (or (null stobj-function) (and (not (member-equal *stobj-inline-declare* def)) (or axiomatic-p (not (getprop stobj-function 'absstobj-info nil 'current-acl2-world wrld))))) (cons 'defun def))))) (defun cltl-def-from-name (fn wrld) ; This function returns the raw Lisp definition of fn. If fn does not have a ; 'stobj-function property in wrld, then the returned definition is also the ; definition that is oneified to create the corresponding *1* function. (cltl-def-from-name1 fn (getprop fn 'stobj-function nil 'current-acl2-world wrld) nil wrld)) (defun table-cltl-cmd (name key val op ctx wrld) ; WARNING: For the case that name is 'memoize-table, keep this in sync with ; memoize-fn. ; Currently this function returns nil if name is 'memoize-table except in a ; hons-enabled (#+hons) version, because memoize-table has a table guard of nil ; (actually a hard-error call) in the #-hons version. (let ((unsupported-str "Unsupported operation, ~x0, for updating table ~x1.")) (case name (memoize-table (cond ((eq op :guard) nil) ((not (eq op :put)) (er hard ctx unsupported-str op name)) (val ; We store enough in the cltl-cmd so that memoize-fn can be called (by ; add-trip) without having to consult the world. This is important because in ; the hons version of Version_3.3, hence before we stored the cl-defun and ; condition-defun in this cltl-cmd tuple, we saw an error in the following, as ; explained below. ; (defun foo (x) (declare (xargs :guard t)) x) ; (defun bar (x) (declare (xargs :guard t)) x) ; (progn (defun foo-memoize-condition (x) ; (declare (xargs :guard t)) ; (eq x 'term)) ; (table memoize-table 'foo (list 'foo-memoize-condition 't 'nil)) ; (progn (defun h (x) x) ; (defun bar (x) (cons x x)))) ; Why did this cause an error? The problem was that when restoring the world ; from after bar up to the inner progn (due to its protection by ; revert-world-on-error), memoize-fn was calling cltl-def-from-name on (w ; *the-live-state*), but this world did not contain enough information for that ; call. (Note that set-w! calls find-longest-common-retraction with event-p = ; nil in that case, which is why we roll back to the previous command, not ; event. We might consider rolling back to the previous event in all cases, ; not just when certifying or including a book. But perhaps that's risky, ; since one can execute non-events like defuns-fn in the loop that one cannot ; execute in a book without a trust tag (or in make-event; hmmmm...).) ; See add-trip for a call of memoize-fn using the arguments indicated below. ; We have seen an error result due to revert-world-on-error winding back to a ; command landmark. See set-w! for a comment about this, which explains how ; problem was fixed after Version_3.6.1. However, we prefer to fix the problem ; here as well, by avoiding the call of cltl-def-from-name in memoize-fn that ; could be attempting to get a name during extend-world1 from a world not yet ; installed. So we make that call here, just below, and store the result in ; the cltl-command tuple. (let* ((condition-fn (cdr (assoc-eq :condition-fn val))) (condition-def (and condition-fn (not (eq condition-fn t)) (cltl-def-from-name condition-fn wrld))) (condition (or (eq condition-fn t) ; hence t (car (last condition-def))))) ; maybe nil `(memoize ,key ; fn ,condition ,(cdr (assoc-eq :inline val)) ,(cdr (assoc-eq :trace val)) ,(cltl-def-from-name key wrld) ; cl-defun ,(getprop key 'formals t 'current-acl2-world wrld) ; formals ,(getprop key 'stobjs-in t 'current-acl2-world wrld) ; stobjs-in ,(getprop key 'stobjs-out t 'current-acl2-world ; Normally we would avoid getting the stobjs-out of return-last. But ; return-last is rejected for mamoization anyhow (by memoize-table-chk). wrld) ; stobjs-out ,(and (symbolp condition) condition (not (eq condition t)) (cltl-def-from-name condition wrld)) ; condition-defun ,(and (cdr (assoc-eq :commutative val)) t) ,(cdr (assoc-eq :forget val)) ,(cdr (assoc-eq :memo-table-init-size val)) ,(cdr (assoc-eq :aokp val))))) (t `(unmemoize ,key)))) (t nil)))) (defun table-fn1 (name key val op term ctx wrld state event-form) ; Warning: If the table event ever generates proof obligations, remove it from ; the list of exceptions in install-event just below its "Comment on ; irrelevance of skip-proofs". ; This is just the rational version of table-fn, with key, val, op and ; term all handled as normal (evaluated) arguments. The chart in ; table-fn explains the legal ops and arguments. (case op (:alist (er-progn (chk-table-nil-args :alist (or key val term) (cond (key '(2)) (val '(3)) (t '(5))) ctx state) (value (table-alist name wrld)))) (:get (er-progn (chk-table-nil-args :get (or val term) (cond (val '(3)) (t '(5))) ctx state) (value (cdr (assoc-equal key (getprop name 'table-alist nil 'current-acl2-world wrld)))))) (:put (with-ctx-summarized (if (output-in-infixp state) event-form ctx) (let* ((tbl (getprop name 'table-alist nil 'current-acl2-world wrld))) (er-progn (chk-table-nil-args :put term '(5) ctx state) (cond ((let ((pair (assoc-equal key tbl))) (and pair (equal val (cdr pair)))) (stop-redundant-event ctx state)) (t (er-let* ((pair (chk-table-guard name key val ctx wrld state)) (wrld1 (cond ((null pair) (value wrld)) (t (let ((ttags-allowed1 (car pair)) (ttags-seen1 (cdr pair))) (pprogn (f-put-global 'ttags-allowed ttags-allowed1 state) (value (global-set? 'ttags-seen ttags-seen1 wrld (global-val 'ttags-seen wrld))))))))) (install-event name event-form 'table 0 nil (table-cltl-cmd name key val op ctx wrld) nil ; theory-related events do their own checking nil (putprop name 'table-alist (put-assoc-equal-fast key val tbl) wrld1) state)))))))) (:clear (with-ctx-summarized (if (output-in-infixp state) event-form ctx) (er-progn (chk-table-nil-args :clear (or key term) (cond (key '(2)) (t '(5))) ctx state) (cond ((equal val (table-alist name wrld)) (stop-redundant-event ctx state)) ((not (alistp val)) (er soft 'table ":CLEAR requires an alist, but ~x0 is not." val)) (t (let ((val (if (duplicate-keysp val) (reverse (clean-up-alist val nil)) val))) (er-let* ((wrld1 (er-let* ((pair (chk-table-guards name val ctx wrld state))) (cond ((null pair) (value wrld)) (t (let ((ttags-allowed1 (car pair)) (ttags-seen1 (cdr pair))) (pprogn (f-put-global 'ttags-allowed ttags-allowed1 state) (value (global-set? 'ttags-seen ttags-seen1 wrld (global-val 'ttags-seen wrld)))))))))) (install-event name event-form 'table 0 nil (table-cltl-cmd name key val op ctx wrld) nil ; theory-related events do their own checking nil (putprop name 'table-alist val wrld1) state)))))))) (:guard (cond ((eq term nil) (er-progn (chk-table-nil-args op (or key val) (cond (key '(2)) (t '(3))) ctx state) (value (getprop name 'table-guard *t* 'current-acl2-world wrld)))) (t (with-ctx-summarized (if (output-in-infixp state) event-form ctx) (er-progn (chk-table-nil-args op (or key val) (cond (key '(2)) (t '(3))) ctx state) (er-let* ((tterm (translate term '(nil) nil nil ctx wrld state))) ; known-stobjs = nil. No variable is treated as a stobj in tterm. ; But below we check that the only vars mentioned are KEY, VAL and ; WORLD. These could, in principle, be declared stobjs by the user. ; But when we ev tterm in the future, we will always bind them to ; non-stobjs. (let ((old-guard (getprop name 'table-guard nil 'current-acl2-world wrld))) (cond ((equal old-guard tterm) (stop-redundant-event ctx state)) (old-guard (er soft ctx "It is illegal to change the :guard on a table ~ after it has been given an explicit :guard. ~ The :guard of ~x0 is ~x1 and this can be ~ changed only by undoing the event that set it. ~ See :DOC table." name (untranslate (getprop name 'table-guard nil 'current-acl2-world wrld) t wrld))) ((getprop name 'table-alist nil 'current-acl2-world wrld) ; At one time Matt wanted the option of setting the :val-guard of a ; non-empty table, but he doesn't recall why. Perhaps we'll add such ; an option in the future if others express such a desire. (er soft ctx "It is illegal to set the :guard of the ~ non-empty table ~x0. See :DOC table." name)) (t (let ((legal-vars '(key val world)) (vars (all-vars tterm))) (cond ((not (subsetp-eq vars legal-vars)) (er soft ctx "The only variables permitted in the ~ :guard of a table are ~&0, but your ~ guard uses ~&1. See :DOC table." legal-vars vars)) (t (install-event name event-form 'table 0 nil (table-cltl-cmd name key val op ctx wrld) nil ; theory-related events do the check nil (putprop name 'table-guard tterm wrld) state))))))))))))) (otherwise (er soft ctx "Unrecognized table operation, ~x0. See :DOC table." op)))) (defun table-fn (name args state event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ; This is an unusual "event" because it sometimes has no effect on ; STATE and thus is not an event! In general this function applies ; an operation, op, to some arguments (and to the table named name). ; Ideally, args is of length four and of the form (key val op term). ; But when args is shorter it is interpreted as follows. ; args same as args ; () (nil nil :alist nil) ; (key) (key nil :get nil) ; (key val) (key val :put nil) ; (key val op) (key val op nil) ; Key and val are both treated as forms and evaluated to produce ; single results (which we call key and val below). Op and term are ; not evaluated. A rational version of this function that takes key, ; val, op and term all as normal arguments is table-fn1. The odd ; design of this function with its positional interpretation of op and ; odd treatment of evaluation is due to the fact that it represents ; the macroexpansion of a form designed primarily to be typed by the ; user. ; Op may be any of :alist, :get, :put, :clear, or :guard. Each op ; enforces certain restrictions on the other three arguments. ; op restrictions and meaning ; :alist Key val and term must be nil. Return the table as an ; alist pairing keys to their non-nil vals. ; :get Val and term must be nil.Return the val associated with ; key. ; :put Key and val satisfy :guard and term must be nil. Store ; val with key. ; :clear Key and term must be nil. Clear the table, setting it ; to val if val is supplied (else to nil). Note that val ; must be an alist, and as with :put, the keys and entries ; must satisfy the :guard. ; :guard Key and val must be nil, term must be a term mentioning ; only the variables KEY, VAL, and WORLD, and returning one ; result. The table must be empty. Store term as the ; table's :guard. ; Should table events be permitted to have documentation strings? No. ; The reason is that we do not protect other names from being used as ; tables. For example, the user might set up a table with the name ; defthm. If we permitted a doc-string for that table, :DOC defthm ; would be overwritten. (let* ((ctx (cons 'table name)) (wrld (w state)) (event-form (or event-form `(table ,name ,@args))) (n (length args)) (key-form (car args)) (val-form (cadr args)) (op (cond ((= n 2) :put) ((= n 1) :get) ((= n 0) :alist) (t (caddr args)))) (term (cadddr args))) (er-progn (cond ((not (symbolp name)) (er soft ctx "The first argument to table must be a symbol, but ~ ~x0 is not. See :DOC table." name)) ((< 4 (length args)) (er soft ctx "Table may be given no more than five arguments. In ~ ~x0 it is given ~n1. See :DOC table." event-form (1+ (length args)))) (t (value nil))) (er-let* ((key-pair (simple-translate-and-eval key-form (if (eq name 'acl2-defaults-table) nil (list (cons 'world wrld))) nil (if (eq name 'acl2-defaults-table) "In (TABLE ACL2-DEFAULTS-TABLE key ...), key" "The second argument of TABLE") ctx wrld state nil)) (val-pair (simple-translate-and-eval val-form (if (eq name 'acl2-defaults-table) nil (list (cons 'world wrld))) nil (if (eq name 'acl2-defaults-table) "In (TABLE ACL2-DEFAULTS-TABLE key val ...), val" "The third argument of TABLE") ctx wrld state nil))) (table-fn1 name (cdr key-pair) (cdr val-pair) op term ctx wrld state event-form))))) (defun set-override-hints-fn (lst at-end ctx wrld state) (er-let* ((tlst (translate-override-hints 'override-hints lst ctx wrld state)) (new (case at-end ((t) (value (append (override-hints wrld) tlst))) ((nil) (value (append tlst (override-hints wrld)))) (:clear (value tlst)) (:remove (let ((old (override-hints wrld))) (value (set-difference-equal old tlst)))) (otherwise (er soft ctx "Unrecognized operation in ~x0: ~x1." 'set-override-hints-fn at-end))))) (er-progn (table-fn 'default-hints-table (list :override (kwote new)) state nil) (table-fn 'default-hints-table (list :override) state nil)))) acl2-sources/hons.lisp0000666002132200015000000014037312222115527014424 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; hons.lisp -- Logical definitions for hash cons and fast alists. Note that ; the memoization and watch functionality previously provided by this file have ; been moved into "memoize.lisp". A closely related file is "hons-raw.lisp" ; that provides the Common Lisp implementation of many of the concepts ; introduced below. ; The original version of this file was contributed by Bob Boyer and Warren ; A. Hunt, Jr. The design of this system of hash cons, function memoization, ; and fast association lists (applicative hash tables) was initially ; implemented by Boyer and Hunt. The code has since been improved by Boyer and ; Hunt, and also by Sol Swords, Jared Davis, and Matt Kaufmann. (in-package "ACL2") (defdoc normed ":Doc-Section Hons-and-Memoization Normed objects are ACL2 Objects that are \"canonical\" or \"unique\" in a certain sense.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. In Common Lisp, we can tell whether an ACL2 object is ~st[normed] or not, but there is no way for an ordinary ACL2 function to see whether an object is normed. Hence, whether or not an object is normed is an implementation-level concept. The fundamental property of normed objects is that if A and B are both normed, then ~c[(equal A B)] holds if and only if ~c[(eql A B)] holds. For strings and conses, ~c[(eql A B)] holds only when ~c[(eq A B)], so any normed conses or strings are ~ilc[equal] precisely when they are ~ilc[eq]. The potential benefits of having normed objects include: constant-time equality comparisons, reduced memory usage, fast association lists, and function memoization. Note that in our implementation, all symbols, characters, and numbers are automatically normed, and whenever a cons is normed its car and cdr must also be normed.~/ The Meaning of Normed in Common Lisp. Recall that the ACL2 Objects are certain \"good\" Common Lisp symbols, characters, strings, and numbers, and the conses which can be recursively formed from these good atoms. Not all properties of these objects are visible in the ACL2 logic. An example of an invisible property is the pointer identity of an object. For example, suppose we write the following. ~bv[] (defconst *x* (cons 1 2)) (defconst *y* (cons 1 2)) ~ev[] In Common Lisp, ~ilc[cons] is guaranteed to provide a new pair that is distinct from any previously created pair, so we know that *x* and *y* will be distinct objects and we will be able to tell them apart from one another using ~c[eq]. But this difference is not visible in the ACL2 logic, and no ACL2 function can tell *x* apart from *y*. The normed-ness of an object is a similarly invisible property. In Common Lisp, invisible to ACL2, we maintain a data structure called a \"Hons Space\" that records which objects are normed. So, being normed is not an intrinsic property of an object, but instead is determined by whether the object is mentioned in this Hons Space. Notes about Garbage Collection. The Hons Space includes tables with pointers to every normed cons and string. One consequence of this is that normed objects cannot be reclaimed by Lisp's ordinary garbage collector, even after they have become unreachable from the perspective of an ACL2 program. For CCL users only, ~ilc[hons-wash] is a special kind of garbage collection that allows normed conses to be reclaimed. For other Lisps, the only option is to occasionally, manually clear out these Hons Space tables with ~ilc[hons-clear].") #+(or acl2-loop-only (not hons)) (defn hons-copy (x) ":Doc-Section Hons-and-Memoization ~c[(hons-copy x)] returns a ~il[normed] object that is equal to X.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. In the logic, ~c[hons-copy] is just the identity function; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[hons-copy] does whatever is necessary to produce a ~il[normed] version of X.~/ What might this involve? If X is a symbol, character, or number, then it is already normed and nothing is done. If X is a string, we check if any normed version of X already exists. If so, we return the already-normed version; otherwise, we install X as the normed version for all strings that are ~ilc[equal] to X. If X is a cons, we must determine if there is a normed version of X, or recursively construct and install one. Norming large cons trees can become expensive, but there are a couple of cheap cases. In particular, if X is already normed, or if large subtrees of X are already normed, then not much needs to be done. The slowest case is norming some large ACL2 cons structure that has no subtrees which are already normed. Note that running ~c[hons-copy] on an object that was created with ~ilc[cons] is often slower than just using ~ilc[hons] directly when constructing the object.~/" ;; Has an under-the-hood implementation (declare (xargs :mode :logic)) ; for attaching early to acl2x-expansion-alist x) #+(or acl2-loop-only (not hons)) (defn hons-copy-persistent (x) ":Doc-Section Hons-and-Memoization ~c[(hons-copy-persistent x)] returns a ~il[normed] object that is equal to X and which will be re-normed after any calls to ~ilc[hons-clear].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically ~c[hons-copy-persistent] is the identity; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[hons-copy-persistent] is virtually identical to ~ilc[hons-copy]. The only difference is that when ~ilc[hons-clear] is used, any persistently normed conses are automatically re-normed, and this re-norming can be carried out more efficiently than, say, an ordinary ~ilc[hons-copy].~/~/" ;; Has an under-the-hood implementation x) #+(or acl2-loop-only (not hons)) (defn hons (x y) ":Doc-Section Hons-and-Memoization ~c[(hons x y)] returns a ~il[normed] object equal to ~c[(cons x y)].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. In the logic, ~c[hons] is just ~ilc[cons]; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[hons] does whatever is necessary to ensure that its result is ~il[normed].~/ What might this involve? Since the car and cdr of any normed cons must be normed, we need to ~ilc[hons-copy] x and y. This requires little work if x and y are already normed, but can be expensive if x or y contain large, un-normed cons structures. After that, we need to check whether any normed cons equal to ~c[(x . y)] already exists. If so, we return it; otherwise, we need to construct a new cons for ~c[(x . y)] and install it as the normed version of ~c[(x . y)]. Generally speaking, these extra operations make ~c[hons] much slower than ~c[cons], even when given normed arguments." ;; Has an under-the-hood implementation (cons x y)) #+(or acl2-loop-only (not hons)) (defn hons-equal (x y) ":Doc-Section Hons-and-Memoization ~c[(hons-equal x y)] is a recursive equality check that optimizes when parts of its arguments are ~il[normed].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. In the logic, ~c[hons-equal] is just ~ilc[equal]; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, when ~c[hons-equal] encounters two arguments that are both normed, it becomes a mere ~ilc[eql] check, and hence avoids the overhead of recursively checking large cons structures for equality.~/ Note. If ~c[hons-equal] is given arguments that do not contain many normed objects, it can actually be much slower than ~ilc[equal]! This is because it checks to see whether its arguments are normed at each recursive step, and so you are repeatedly paying the price of such checks. Also ~pl[hons-equal-lite], which only checks at the top level whether its arguments are normed." ;; Has an under-the-hood implementation (equal x y)) #+(or acl2-loop-only (not hons)) (defn hons-equal-lite (x y) ":Doc-Section Hons-and-Memoization ~c[(hons-equal-lite x y)] is a non-recursive equality check that optimizes if its arguments are ~il[normed].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. In the logic, ~c[hons-equal-lite] is just ~ilc[equal]; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[hons-equal-lite] checks whether its arguments are normed, and if so it effectively becomes a ~ilc[eql] check. Otherwise, it immediately calls ~ilc[equal] to determine if its arguments are equal.~/ The idea here is to strike a useful balance between ~c[equal] and ~ilc[hons-equal]. If both arguments happen to be normed, we get to use a very fast equality comparison. Otherwise, we just get out of the way and let ~c[equal] do its work, without the extra overhead of recursively checking whether the subtrees of x and y are normed." ;; Has an under-the-hood implementation (equal x y)) #+(or acl2-loop-only (not hons)) (defn hons-clear (gc) ":Doc-Section Hons-and-Memoization ~c[(hons-clear gc)] is a drastic garbage collection mechanism that clears out the underlying Hons Space.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[hons-clear] just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[hons-clear] brutally (1) clears all the tables that govern which conses are ~il[normed], then (2) optionally garbage collects, per the ~c[gc] argument, then finally (3) re-norms the keys of ~ilc[fast-alists] and persistently normed conses; ~pl[hons-copy-persistent].~/ Note. The hash tables making up the Hons Space retain their sizes after being cleared. If you want to shrink them, see ~ilc[hons-resize]. Note. CCL users might prefer ~ilc[hons-wash], which is relatively efficient and allows for the garbage collection of normed conses without impacting their normed status. Note. It is not recommended to interrupt this function. Doing so may cause persistently normed conses and fast alist keys to become un-normed, which might lead to less efficient re-norming and/or violations of the fast-alist discipline." ;; Has an under-the-hood implementation (declare (ignore gc)) nil) #+(or acl2-loop-only (not hons)) (defn hons-wash () ":Doc-Section Hons-and-Memoization ~c[(hons-wash)] is like ~ilc[gc$] but can also garbage collect ~il[normed] objects (CCL Only).~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[hons-wash] just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, in CCL, ~c[hons-wash] runs a garbage collection after taking special measures to allow normed conses to be collected. In Lisps other than CCL, ~c[hons-wash] does nothing. This is because the efficient implementation of ~c[hons-wash] is specific to the \"static honsing\" scheme which requires CCL-specific features.~/ Why is this function needed? Ordinarily, it is not possible to garbage collect any normed conses. This is because the Hons Space includes pointers to any normed conses, and hence Lisp's garbage collector thinks these objects are live. To correct for this, ~c[hons-wash] (1) clears out these pointers, (2) runs a garbage collection, then (3) re-norms any previously-normed conses which have survived the garbage collection. Note. It is not recommended to interrupt this function. Doing so may cause persistently normed conses and fast alist keys to become un-normed, which might lead to less efficient re-norming and/or violations of the fast-alist discipline." ;; Has an under-the-hood implementation nil) #+(or acl2-loop-only (not hons)) (defn hons-summary () ":Doc-Section Hons-and-Memoization ~c[(hons-summary)] prints basic information about the sizes of the tables in the current Hons Space.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[hons-summary] just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, this function gathers and prints some basic information about the sizes of the tables in the Hons Space.~/ This information may be useful for deciding if you want to garbage collect using functions such as ~ilc[hons-clear] or ~ilc[hons-wash]. It may also be useful for determining good initial sizes for the Hons Space hash tables for your particular computation; ~pl[hons-resize]." ;; Has an under-the-hood implementation nil) (defmacro hons-resize (&key str-ht nil-ht cdr-ht cdr-ht-eql addr-ht other-ht sbits fal-ht persist-ht) ":Doc-Section Hons-and-Memoization ~c[(hons-resize ...)] can be used to manually adjust the sizes of the hash tables that govern which ACL2 Objects are considered ~il[normed].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. General form: ~bv[] (hons-resize [:str-ht size] [:nil-ht size] [:cdr-ht size] [:cdr-ht-eql size] [:addr-ht size] [:other-ht size] [:sbits size] [:fal-ht size] [:persist-ht size]) ~ev[] Example: ~bv[] (hons-resize :str-ht 100000 :addr-ht (expt 2 27)) ~ev[] Logically, ~c[hons-resize] just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[hons-resize] can be used to change the sizes of certain hash tables in the Hons Space. All arguments are optional. The size given to each argument should either be nil (the default) or a natural number. A natural number indicates the newly desired size for the hash table, whereas nil means \"don't resize this table.\" Any non-natural argument is regarded as nil.~/ You may wish to call this function for two reasons. 1. Improving Performance by Resizing Up Since the hash tables in the Hons Space automatically grow as new objects are normed, ~c[hons-resize] is unnecessary. But automatic growth can be slow because it is incremental: a particular hash table might be grown dozens of times over the course of your application. Using ~c[hons-resize] to pick a more appropriate initial size may help to reduce this overhead. 2. Reducing Memory Usage by Resizing Down Resizing can also be used to shrink the hash tables. This might possibly be useful immediately after a ~ilc[hons-clear] to free up memory taken by the hash tables themselves. (Of course, the tables will grow again as you create new normed objects.) Advice for using ~c[hons-resize]. Note that hash tables that are already close to the desired size, or which have too many elements to fit into the desired size, will not actually be resized. This makes resizing relatively \"safe.\" Note that the ~ilc[hons-summary] function can be used to see how large and how full your hash tables are. This may be useful in deciding what sizes you want to give to ~c[hons-resize]. Note that ~c[hons-resize] operates by (1) allocating new hash tables, then (2) copying everything from the old hash table into the new table. Because of this, for best performance you should ideally call it when the hash tables involved are minimally populated, i.e., at the start of your application, or soon after a ~ilc[hons-clear]." `(hons-resize-fn ,str-ht ,nil-ht ,cdr-ht ,cdr-ht-eql ,addr-ht ,other-ht ,sbits ,fal-ht ,persist-ht)) #+(or acl2-loop-only (not hons)) (defn hons-resize-fn (str-ht nil-ht cdr-ht cdr-ht-eql addr-ht other-ht sbits fal-ht persist-ht) (declare (ignore str-ht nil-ht cdr-ht cdr-ht-eql addr-ht other-ht sbits fal-ht persist-ht)) ;; Has an under-the-hood implementation nil) (defdoc hons-note ":Doc-Section Hons-and-Memoization notes about ~il[HONS], especially pertaining to expensive resizing operations~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Certain ``Hons-Notes'' are printed to the terminal to report implementation-level information about the management of ~il[HONS] structures. Some of these may be low-level and of interest mainly to system developers. But below we discuss what users can learn from a particular hons-note, ``ADDR-LIMIT reached''. In short: If you are seeing a lot of such hons-notes, then you may be using a lot of memory. (Maybe you can reduce that memory; for certain BDD operations, for example (as defined in community books ~c[books/centaur/ubdds/]), variable ordering can make a big difference.)~/ By way of background: ~bq[] The ADDR-HT is the main hash table that lets us look up ~il[normed] conses, i.e., ~il[hons]es. It is an ordinary Common Lisp hash table, so when it starts to get too full the Lisp will grow it. It can get really big. (It has been seen to take more than 10 GB of memory just for the hash table's array, not to mention the actual conses!) Hons-Notes may be printed when the ADDR-HT is getting really full. This growth can be expensive and can lead to memory problems. Think about what it takes to resize a hash table: (1) allocate a new, bigger array~nl[] (2) rehash elements, copying them into the new array~nl[] (3) free the old array Until you reach step (3) and a garbage collection takes place, you have to have enough memory to have both the old and new arrays allocated. If the old array was 10 GB and we want to allocate a new one that's 15 GB, this can get pretty bad. Also, rehashing the elements is expensive time-wise when there are lots of elements. Since resizing a hash table is expensive, we want to avoid it. There's a ~ilc[hons-resize] command for this. You may want your books to start with something like the following. ~bv[] (value-triple (set-max-mem (* 30 (expt 2 30)))) ; 30 GB heap (value-triple (hons-resize :addr-ht #u_100_000_000)) ; 100M honses ~ev[] Basically, if you roughly know how many honses your book will need, you can just get them up front and then never to resize.~eq[] The Hons-Notes about ``ADDR-LIMIT reached'' are basically there to warn you that the ADDR-HT is being resized. This can help you realize that your ~ilc[hons-resize] command had too small of an ADDR-HT size, or might suggest that your book should have a ~ilc[hons-resize] command. There are also commands like ~c[(]~ilc[hons-summary]~c[)] and, defined in community book ~c[books/centaur/misc/memory-mgmt-logic.lisp], ~c[(hons-analyze-memory nil)]. These can show you how many honses you currently have, how much space they are taking, and that sort of thing. (A nice trick is to call ~ilc[hons-summary] at the end of a book, to get an idea of how many honses you should ask for in your ~ilc[hons-resize] command). ") (defdoc fast-alists ":Doc-Section Hons-and-Memoization alists with hidden hash tables for faster execution~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. The implementation of fast alists is, in many ways, similar to that of ACL2 arrays. Logically, ~ilc[hons-acons] is just like ~c[acons], and ~ilc[hons-get] is similar to ~ilc[assoc-equal]. But under the hood, hash tables are associated with these alists, and, when a certain ~st[discipline] is followed, these functions execute with hash table speeds. What is this discipline? Each ~c[hons-acons] operation \"steals\" the hash table associated with the alist that is being extended. Because of this, one must be very conscious of which object is the most recent extension of an alist and use that extension exclusively. The only penalty for a failure to keep track of the most recent extension is a loss of execution speed, not of correctness, and perhaps the annoyance of some ~ilc[slow-alist-warning]s. Maintaining discipline may require careful passing of a fast alist up and down through function calls, as with any single threaded object in an applicative setting, but there is ~st[no syntactic enforcement] that insists you only use the most recent extension of an alist as there is for single threaded objects (~ilc[stobj]s). Also, even with perfectly proper code, discipline can sometimes be lost due to user interrupts and aborts.~/ Performance Notes ~l[hons-acons] for how the final ~ilc[cdr] of a fast alist can be used as a size hint or as a name for reports printed by calling ~ilc[fast-alist-summary]. The keys of fast alists are always ~il[normed]. Why? In Common Lisp, equal-based hashing is relatively slow, so to allow the use of eql-based hash tables, ~ilc[hons-acons] and ~ilc[hons-get] always ~ilc[hons-copy] the keys involved. Since alist keys are frequently atoms, this norming activity may often be so fast that you do not need to think about it. But if you are going to use large structures as the keys for your fast alist, this overhead can be significant, and you may want to ensure that your keys are normed ahead of time. There is ~b[no automatic mechanism] for reclaiming the hash tables that are associated with alists. Because of this, to avoid memory leaks, you should call ~ilc[fast-alist-free] to remove the hash table associated with alists that will no longer be used.") (defn hons-assoc-equal (key alist) ":Doc-Section Hons-and-Memoization ~c[(hons-assoc-equal key alist)] is ~st[not fast]; it serves as the logical definition for ~ilc[hons-get].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. The definition of ~c[hons-assoc-equal] is similar to that of ~ilc[assoc-equal], except that (1) it does not require ~ilc[alistp] as a guard, and (2) it \"skips over\" any non-conses when its alist argument is malformed.~/ We typically leave ~c[hons-get] enabled and reason about ~c[hons-assoc-equal] instead. One benefit of this approach is that it avoids certain \"false\" discipline warnings that might arise from execution during theorem proving." (cond ((atom alist) nil) ((and (consp (car alist)) (hons-equal key (caar alist))) (car alist)) (t (hons-assoc-equal key (cdr alist))))) (defdoc slow-alist-warning ":Doc-Section Hons-and-Memoization warnings issued when ~ilc[fast-alists] are used inefficiently~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Obtaining hash-table performance from ~ilc[hons-get] requires one to follow a certain discipline. If this discipline is violated, you may see a \"slow alist warning\". This warning means that the alist you are extending or accessing does not have a valid hash table associated with it, and hence any accesses must be carried out with ~ilc[hons-assoc-equal] instead of ~c[gethash].~/ You can control whether or not you get a warning and, if so, whether or not a break (an error from which you can continue) ensues. For instance: ~bv[] (set-slow-alist-action :warning) ; warn on slow access (default) (set-slow-alist-action :break) ; warn and also call break$ (set-slow-alist-action nil) ; do not warn or break ~ev[] The above forms expand to ~ilc[table] ~il[events], so they can be embedded in ~ilc[encapsulate]s and ~il[books], wrapped in ~ilc[local], and so on.") (table hons 'slow-alist-warning :warning) (defmacro set-slow-alist-action (action) (declare (xargs :guard (or (eq action :warning) (eq action :break) (not action)))) `(table hons 'slow-alist-warning ,action)) (defn get-slow-alist-action (state) (declare (xargs :stobjs state)) (let* ((alist (table-alist 'hons (w state))) (warning (hons-assoc-equal 'slow-alist-warning alist))) (and (consp warning) (cdr warning)))) #+(or acl2-loop-only (not hons)) (defn hons-get (key alist) ":Doc-Section Hons-and-Memoization ~c[(hons-get key alist)] is the efficient lookup operation for ~il[fast-alists].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[hons-get] is just an alias for ~ilc[hons-assoc-equal]; we typically leave it enabled and prefer to reason about ~c[hons-assoc-equal] instead. One benefit of this approach is that it helps to avoids \"false\" discipline warnings that might arise from execution during theorem proving. Under the hood, when ~c[alist] is a fast alist that is associated with a valid hash table, ~c[hons-get] first norms ~c[key] using ~ilc[hons-copy], then becomes a ~c[gethash] operation on the hidden hash table.~/~/" ;; Has an under-the-hood implementation (hons-assoc-equal key alist)) #+(or acl2-loop-only (not hons)) (defn hons-acons (key val alist) ":Doc-Section Hons-and-Memoization ~c[(hons-acons key val alist)] is the main way to create or extend ~il[fast-alists].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[hons-acons] is like ~ilc[acons] except that its guard does not require ~ilc[alistp]; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, two things are done differently. First, the key is copied with ~ilc[hons-copy]; this lets us use EQL-based hash tables instead of EQUAL-based hash tables for better performance. Second, assuming there is a valid hash table associated with this alist, we destructively update the hash table by binding key to val. The hash table will no longer be associated with alist, but will instead be associated with the new alist returned by ~c[hons-acons].~/ Hash Table Creation A new hash table is created by ~c[hons-acons] whenever alist is an atom. Unlike ordinary ~c[acons], we do not require that alist be nil, and in fact you may wish to use a non-nil value for one of two reasons. 1. As a size hint By default, the new hash table will be given a quite modest default capacity of 60 elements. As more elements are added, the table may need to be resized to accommodate them, and this resizing has some runtime cost. When a natural number is used as a fast alist's name, we interpret it as a size hint. For example, ~c[(hons-acons 'foo 'bar 1000)] instructs us to use 1000 as the initial size for this hash table and binds 'foo to 'bar. The resulting table should not need to be resized until more than 1000 elements are added. We ignore size hints that request fewer than 60 elements. Because of hash collisions, hash tables typically need to have a larger size than the actual number of elements they contain. The hash tables for fast alists are told to grow when they reach 70% full. So, an easy rule of thumb might be: multiply the expected number of elements you need by 1.5 to keep your hash tables about 2/3 full. 2. As an alist name We also frequently use a symbol for alist, and think of this symbol as the name of the new alist. We have found that naming alists can be valuable for two reasons: First, the name can be helpful in identifying fast alists that should have been freed, see ~ilc[fast-alist-summary]. Second, names can sometimes be used to avoid a subtle and insidious table-stealing phenomenon that occurs when using fast-alists that are themselves normed; see ~ilc[hons-acons!]. At the moment, there is no way to simultaneously name a fast alist and also give it a size hint. We may eventually allow strings to include embedded name and size components, but for now we have not implemented this capability." ;; Has an under-the-hood implementation (cons (cons key val) alist)) #+(or acl2-loop-only (not hons)) (defn hons-acons! (key val alist) ":Doc-Section Hons-and-Memoization ~c[(hons-acons! key val alist)] is an alternative to ~ilc[hons-acons] that produces ~il[normed], fast alists.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[hons-acons!] is like ~ilc[acons] except that its guard does not require ~ilc[alistp]; we leave it enabled and would think it odd to ever prove a theorem about it. Ordinarily, ~il[fast-alists] are constructed with ~ilc[hons-acons] instead of ~c[hons-acons!]. In such alists, the keys are honsed, but the conses that make up the \"spine\" of the alist itself are ordinary conses. In other words, it is basically correct to say: ~bv[] (hons-acons key val alist) == (cons (cons (hons-copy key) val) alist) ~ev[] In contrast, when ~c[hons-acons!] is used, the conses making up the alist itself are also normed. That is, ~bv[] (hons-acons! key val alist) == (hons (hons key val) alist) ~ev[]~/ Generally, you ~st[should not use] ~c[hons-acons!] unless you really know what you're doing. Drawback 1. ~c[hons-acons!] requires you to ~ilc[hons-copy] all of the values that are being stored in the fast alist. If you are storing large values, this may be expensive. Drawback 2. It can be more difficult to maintain the proper discipline when using ~c[hons-acons!]. For instance, consider the following: ~bv[] (let ((al1 (hons-acons 1 'one (hons-acons 2 'two nil))) (al2 (hons-acons 1 'one (hons-acons 2 'two nil)))) ...) ~ev[] Here, both al1 and al2 are valid fast alists and they can be extended independently without any trouble. But if these alists had instead been constructed with ~c[hons-acons!], then since both al1 and al2 are equal, normed conses, they will literally be ~ilc[eq] and hence will refer to precisely the same hash table. In other words, ~c[hons-acons!] makes it relatively easy to ~st[inadvertently] steal the hash table associated with some other fast alist. This problem can be alleviated somewhat by uniquely naming alists; see the discussion in ~ilc[hons-acons] for details. Despite these drawbacks, ~c[hons-acons!] is the only way besides ~ilc[hons-shrink-alist!] to generate a fast alist that is normed. It is not adequate to ~ilc[hons-copy] a fast alist that was generated by ordinary ~ilc[hons-acons] calls, because this would produce an EQUAL-but-not-EQ object, and this new object would not be associated with the fast alist's hash table." ;; Has an under-the-hood implementation (cons (cons key val) alist)) #+(or acl2-loop-only (not hons)) (defn make-fast-alist (alist) ":Doc-Section Hons-and-Memoization ~c[(make-fast-alist alist)] creates a fast-alist from the input alist, returning ~c[alist] itself or, in some cases, a new object equal to it.~/ Note: it is often better to use ~c[with-fast-alist]; ~pl[with-fast-alist]. Logically, ~c[make-fast-alist] is the identity function. Under the hood, we construct and return an object that is ~c[equal] to ~c[alist] and which is a fast alist. If ~c[alist] is already a fast alist, almost no work is required: we simply return it unchanged. When ~c[alist] is not fast, we must minimally construct a hash table for its bindings. It is often possible to bind this new hash table to ~c[alist] itself. But in certain cases when the alist keys are not ~il[normed], a new alist must be constructed, also, and so we may return an ~c[equal] but not ~c[eq] alist. (In these cases, we still try to avoid at least some consing by reusing the \"longest normed tail\" of the alist.)~/~/" ;; Has an under-the-hood implementation alist) #+(or acl2-loop-only (not hons)) (defn hons-shrink-alist (alist ans) ":Doc-Section Hons-and-Memoization ~c[(hons-shrink-alist alist ans)] can be used to eliminate \"shadowed pairs\" from an alist or to copy ~il[fast-alists].~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. It assumes familiarity with fast alists; ~pl[fast-alists]. Logically, ~c[(hons-shrink-alist alist ans)] is defined as follows: ~bv[] (cond ((atom alist) ans) ((atom (car alist)) (hons-shrink-alist (cdr alist) ans)) ((hons-assoc-equal (car (car alist)) ans) (hons-shrink-alist (cdr alist) ans)) (t (hons-shrink-alist (cdr alist) (cons (car alist) ans)))) ~ev[] The alist argument need not be a fast alist. Typically ans is set to nil or some other atom. In this case, shrinking produces a new, fast alist which is like alist except that (1) any \"malformed,\" atomic entries have been removed, (2) all \"shadowed pairs\" have been removed, and (3) incidentally, the surviving elements have been reversed. This can be useful as a way to clean up any unnecessary bindings in alist, or as a way to obtain a \"deep copy\" of a fast alist that can extended independently from the original while maintaining discipline. Note that hons-shrink-alist is potentially expensive, for the following two reasons. ~bq[] o The alist is copied, dropping any shadowed pairs. This process will require a hash table lookup for each entry in the alist; and it will require creating a new alist, which uses additional memory. o Unless ans is a fast alist that is stolen (see below), a new hash table is created, which uses additional memory. This hash table is populated in time that is linear in the number of unique keys in the alist. ~eq[] When ans is not an atom, good discipline requires that it is a fast alist. In this case, ~c[hons-shrink-alist] steals the hash table for ans and extends it with all of the bindings in alist that are not in ans. From the perspective of ~ilc[hons-assoc-equal], you can think of the resulting alist as being basically similar to ~c[(append ans alist)], but in a different order. Note that when ans is not a fast alist (e.g., ans is an atom) then such stealing does not take place. A common idiom is to replace an alist by the result of shrinking it, in which case it is best to free the input alist, for example as follows. ~bv[] (let ((alist (fast-alist-free-on-exit alist (hons-shrink-alist alist nil)))) ...) ~ev[] ~l[fast-alist-free-on-exit] and ~pl[fast-alist-free].~/~/" ;; Has an under-the-hood implementation (cond ((atom alist) ans) ((atom (car alist)) (hons-shrink-alist (cdr alist) ans)) ((hons-assoc-equal (car (car alist)) ans) (hons-shrink-alist (cdr alist) ans)) (t (hons-shrink-alist (cdr alist) (cons (car alist) ans))))) #+(or acl2-loop-only (not hons)) (defn hons-shrink-alist! (alist ans) ":Doc-Section Hons-and-Memoization ~c[(hons-shrink-alist! alist ans)] is an alternative to ~ilc[hons-shrink-alist] that produces a ~il[normed] result.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically this function is just ~c[hons-shrink-alist]; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, this is the same as ~c[hons-shrink-alist] except that it uses something like ~ilc[hons-acons!] instead of ~ilc[hons-acons]. You generally ~st[should not use] this function unless you really know what you're doing and understand the drawbacks discussed in ~ilc[hons-acons!].~/~/" ;; Has an under-the-hood implementation (hons-shrink-alist alist ans)) #+(or acl2-loop-only (not hons)) (defn fast-alist-len (alist) ":Doc-Section Hons-and-Memoization ~c[(fast-alist-len alist)] counts the number of unique keys in a fast alist.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically this function counts how many elements would remain in the alist were we to shrink it with ~ilc[hons-shrink-alist]. Good discipline requires that the alist is a fast alist. Under the hood, when the alist is a fast alist we can simply call the underlying Common Lisp function ~c[hash-table-count] on the associated hash table, which is very fast and doesn't require us to actually shrink the alist.~/~/" ;; Has an under-the-hood implementation (len (hons-shrink-alist alist nil))) #+(or acl2-loop-only (not hons)) (defn fast-alist-free (alist) ":Doc-Section Hons-and-Memoization ~c[(fast-alist-free alist)] throws away the hash table associated with a fast alist.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, this function is the identity; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, ~c[fast-alist-free] removes the hash table associated with this alist, if one exists. This effectively converts the alist into an ordinary alist. Also ~pl[fast-alist-free-on-exit].~/ Because there is no automatic mechanism for freeing the hash tables used in fast alists, to avoid memory leaks you should manually free any alists that will no longer be used. You may find ~ilc[fast-alist-summary] useful in tracking down alists that were not properly freed. It is safe to call ~c[fast-alist-free] on any argument, including fast alists that have already been freed and objects which are not alists at all." ;; Has an under-the-hood implementation alist) #+(or acl2-loop-only (not hons)) (defn fast-alist-summary () ":Doc-Section Hons-and-Memoization ~c[(fast-alist-summary)] prints some basic statistics about any current fast alists.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, ~c[fast-alist-summary] just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, this function gathers and prints some basic statistics about the current fast alists. It may be useful for identifying fast alists that should have been freed with ~ilc[fast-alist-free].~/~/" ;; Has an under-the-hood implementation nil) (defdoc with-fast-alist ":Doc-Section Hons-and-Memoization ~c[(with-fast-alist name form)] causes ~c[name] to be a fast alist for the execution of ~c[form].~/ Logically, ~c[with-fast-alist] just returns ~c[form]. Under the hood, we cause ~c[alist] to become a fast alist before executing ~c[form]. If doing so caused us to introduce a new hash table, the hash table is automatically freed after ~c[form] completes. More accurately, under the hood ~c[(with-fast-alist name form)] essentially expands to something like: ~bv[] (if (already-fast-alist-p name) form (let (( (make-fast-alist name))) (prog1 form (fast-alist-free )))) ~ev[] Practically speaking, ~c[with-fast-alist] is frequently a better choice then just using ~ilc[make-fast-alist], and is particularly useful for writing functions that can take either fast or slow alists as arguments. That is, consider the difference between: ~bv[] (defun bad (alist ...) (let* ((fast-alist (make-fast-alist alist)) (answer (expensive-computation fast-alist ...))) (prog2$ (fast-alist-free fast-alist) answer))) (defun good (alist ...) (with-fast-alist alist (expensive-computation alist ...))) ~ev[] Either approach is fine if the caller provides a slow ~c[alist]. But if the input ~c[alist] is already fast, ~c[bad] will (perhaps unexpectedly) free it! On the other hand, ~c[good] is able to take advantage of an already-fast argument and will not cause it to be inadvertently freed. See also the macro ~c[with-fast-alists] defined in the community book ~c[\"books/centaur/misc/hons-extra.lisp\"], which allows you to call ~ilc[with-fast-alist] on several alists simultaneously. The community book ~c[\"books/centaur/misc/hons-extra.lisp\"] extends the ~c[b*] macro (defined in the community book ~c[\"books/tools/bstar.lisp\"]) with the ~c[with-fast] pattern binder. That is, after executing ~c[(include-book \"centaur/misc/hons-extra.lisp\" :dir :system)] you may write something like this: ~bv[] (b* (... ((with-fast a b c ...)) ...) ...) ~ev[] which causes ~c[a], ~c[b], and ~c[c] to become fast alists until the completion of the ~c[b*] form. Note that ~c[with-fast-alist] will cause logically tail-recursive functions not to execute tail-recursively if its cleanup phase happens after the tail-recursive call returns.~/~/") (defdoc with-stolen-alist ":Doc-Section Hons-and-Memoization ~c[(with-stolen-alist name form)] ensures that ~c[name] is a fast alist at the start of the execution of ~c[form]. At the end of execution, it ensures that ~c[name] is a fast alist if and only if it was originally. That is, if ~c[name] was not a fast alist originally, its hash table link is freed, and if it was a fast alist originally but its table was modified during the execution of ~c[form], that table is restored. Note that any extended table created from the original fast alist during ~c[form] must be manually freed.~/ Logically, ~c[with-stolen-alist] just returns ~c[form]. Under the hood, we cause ~c[alist] to become a fast alist before executing ~c[form], and we check the various conditions outlined above before returning the final value. Note that ~c[with-stolen-alist] will cause logically tail-recursive functions not to execute tail-recursively if its cleanup phase happens after the tail-recursive call returns.~/~/") (defdoc fast-alist-free-on-exit ":Doc-Section Hons-and-Memoization Free a fast alist after the completion of some form.~/ Logically, ~c[(fast-alist-free-on-exit alist form)] is the identity and returns ~c[form]. Also ~pl[fast-alist-free]. Under the hood, this essentially expands to: ~bv[] (prog1 form (fast-alist-free alist)) ~ev[] In other words, ~c[alist] is not freed immediately, but instead remains a fast alist until the form completes. This may be useful when you are writing code that uses a fast alist but has many return points. See also the macro ~c[fast-alists-free-on-exit] defined in the community book ~c[\"books/centaur/misc/hons-extra.lisp\"], which can be used to free several alists. The community book ~c[\"books/centaur/misc/hons-extra.lisp\"] extends the ~c[b*] macro (defined in the community book ~c[\"books/tools/bstar.lisp\"]) with the ~c[free-on-exit] pattern binder. That is, after executing ~c[(include-book \"centaur/misc/hons-extra.lisp\" :dir :system)], the form ~bv[] (b* (... ((free-on-exit a b c)) ...) ...) ~ev[] causes ~c[a], ~c[b], and ~c[c] to be freed when the ~c[b*] completes, but they remain fast alists until then.~/~/") #+(or acl2-loop-only (not hons)) (defmacro with-fast-alist-raw (alist form) ;; Has an under-the-hood implementation (declare (ignore alist)) form) (defmacro with-fast-alist (alist form) `(return-last 'with-fast-alist-raw ,alist ,form)) #+(or acl2-loop-only (not hons)) (defmacro with-stolen-alist-raw (alist form) ;; Has an under-the-hood implementation (declare (ignore alist)) form) (defmacro with-stolen-alist (alist form) `(return-last 'with-stolen-alist-raw ,alist ,form)) #+(or acl2-loop-only (not hons)) (defmacro fast-alist-free-on-exit-raw (alist form) ;; Has an under-the-hood implementation (declare (ignore alist)) form) (defmacro fast-alist-free-on-exit (alist form) `(return-last 'fast-alist-free-on-exit-raw ,alist ,form)) (defn cons-subtrees (x al) ":Doc-Section Hons-and-Memoization ~c[(cons-subtrees x nil)] builds a fast alist that associates each subtree of X with T, without duplication.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. ~/~/" (cond ((atom x) al) ((hons-get x al) al) (t (cons-subtrees (car x) (cons-subtrees (cdr x) (hons-acons x t al)))))) #+(or acl2-loop-only (not hons)) (defn number-subtrees (x) ":Doc-Section Hons-and-Memoization ~c[(number-subtrees x)] returns the number of distinct subtrees of X, in the sense of ~ilc[equal]~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. In the logic, ~c[number-subtrees] is defined as the length of ~ilc[cons-subtrees]. Under the hood, we first ~ilc[hons-copy] X to obtain a normed version, then count the number of unique conses in X using an EQ hash table.~/~/" ;; Has an under-the-hood implementation (len (cons-subtrees x 'number-subtrees))) #+(or acl2-loop-only (not hons)) (defn clear-hash-tables () ":Doc-Section Hons-and-Memoization deprecated feature~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Deprecated. Calls ~ilc[clear-memoize-tables] and then ~ilc[hons-clear] or ~ilc[hons-wash], whichever makes sense for the underlying Common Lisp.~/~/" ;; Has an under-the-hood implementation nil) (defn flush-hons-get-hash-table-link (alist) ":Doc-Section Hons-and-Memoization deprecated feature~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Deprecated. Alias for ~ilc[fast-alist-free].~/~/" (fast-alist-free alist)) (in-theory (disable ; The execution of honsing and fast alist functions during theorem proving ; could be very subtle. It is easy to imagine discipline failures, inadvertent ; norming, inadvertent clearing of hash tables, etc. We try to prevent this at ; least somewhat by disabling the executable counterparts of many of the above ; functions. This is not a total solution, but seems like a good idea anyway. ;; These would probably be pretty harmless (:executable-counterpart hons) (:executable-counterpart hons-copy) (:executable-counterpart hons-copy-persistent) (:executable-counterpart hons-summary) (:executable-counterpart fast-alist-summary) ;; These could be particularly bad to call by mistake (:executable-counterpart hons-clear) (:executable-counterpart hons-wash) (:executable-counterpart hons-resize-fn) ;; These could lead to discipline failures (:executable-counterpart hons-get) (:executable-counterpart hons-acons) (:executable-counterpart hons-acons!) (:executable-counterpart hons-shrink-alist) (:executable-counterpart hons-shrink-alist!) (:executable-counterpart fast-alist-len) (:executable-counterpart fast-alist-free) (:executable-counterpart flush-hons-get-hash-table-link) )) (defun remove-keyword (word l) (declare (xargs :guard (and (keywordp word) (keyword-value-listp l)))) (cond ((endp l) nil) ((eq word (car l)) (remove-keyword word (cddr l))) (t (list* (car l) (cadr l) (remove-keyword word (cddr l)))))) ; For some additional helper functions and lemmas, see the community books ; books/misc/hons-help.lisp and books/misc/hons-help2.lisp. acl2-sources/hons-raw.lisp0000664002132200015000000044376112222115527015220 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Regarding authorship of ACL2 in general: ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; hons-raw.lisp -- Raw lisp implementation of hash cons and fast alists. Note ; that the memoization and watch functionality previously provided by this file ; have been moved into memoize-raw.lisp. This file has undergone a number of ; updates and changes since its original creation in about 2005. ; The original version of this file was contributed by Bob Boyer and Warren ; A. Hunt, Jr. The design of this system of Hash CONS, function memoization, ; and fast association lists (applicative hash tables) was initially ; implemented by Boyer and Hunt. The code has since been improved by Boyer and ; Hunt, and also by Sol Swords, Jared Davis, and Matt Kaufmann. ; Please direct correspondence about this file to Jared Davis ; and Warren Hunt . (in-package "ACL2") ;; We use the static honsing scheme on 64-bit CCL. #+(and Clozure x86_64) (push :static-hons *features*) ; NOTES ABOUT HL-HONS ; ; The "HL-" prefix was introduced when Jared Davis revised the Hons code, and ; "HL" originally meant "Hons Library." The revision included splitting the ; Hons code from the function memoization code, and took place in early 2010. ; We will now use the term to refer to the new Hons implementation that is ; found below. Some changes made in HL-Hons, as opposed to the old Hons ; system, include: ; ; - We combine all of the special variables used by the Hons code into an ; explicit Hons-space structure. ; ; - We no longer separately track the length of sbits. This change appears ; to incur an overhead of 3.35 seconds in a 10-billion iteration loop, but ; gives us fewer invariants to maintain and makes Ctrl+C safety easier. ; ; - We have a new static honsing scheme where every normed object has an ; address, and NIL-HT, CDR-HT, and CDR-HT-EQL aren't needed when static ; honsing is used. ; ; - Since normed strings are EQ comparable, we now place cons pairs whose ; cdrs are strings into the CDR-HT hash table instead of the CDR-HT-EQL ; hash table in classic honsing. ; ; - Previously fast alists were essentially implemented as flex alists. Now ; we always create a hash table instead. This slightly simplifies the code ; and results in trivially fewer runtime type checks in HONS-GET and ; HONS-ACONS. I think it makes sense to use flex alists in classic ; honsing, where we can imagine often finding cdrs for which we don't have ; at least 18 separate cars. But fast alists are much more targeted; if an ; ACL2 user is building a fast alist, it seems very likely that they know ; they are doing something big (or probably big) -- otherwise they wouldn't ; be bothering with fast alists. ; ESSAY ON CTRL+C SAFETY ; ; The HL-Hons implementation involves certain impure operations. Sometimes, at ; intermediate points during a sequence of updates, the invariants on a Hons ; Space are violated. This is dangerous because a user can interrupt at any ; time using 'Ctrl+C', leaving the system in an inconsistent state. ; ; We have tried to avoid sequences of updates that violate invariants. In the ; rare cases where this isn't possible, we need to protect the sequence of ; updates with 'without-interrupts'. We assume that SETF itself does not ; suffer from this kind of problem and that the Lisp implementation should ; ensure that, e.g., (SETF (GETHASH ...)) does not leave a hash table in an ; internally inconsistent state. #+static-hons (defmacro hl-without-interrupts (&rest forms) `(ccl::without-interrupts . ,forms)) ; CROSS-LISP COMPATIBILITY WRAPPERS ; ; As groundwork toward porting the static honsing scheme to other Lisps that ; might be able to support it, we have added these wrappers for various ccl:: ; functionality. (defun hl-mht-fn (&key (test 'eql) (size '60) (rehash-size '1.5) (rehash-threshold '0.7) (weak 'nil) (shared 'nil) (lock-free 'nil)) ; See hl-mht. (declare (ignorable shared weak lock-free)) (make-hash-table :test test :size size :rehash-size rehash-size :rehash-threshold rehash-threshold #+Clozure :weak #+Clozure weak #+Clozure :shared #+Clozure shared #+Clozure :lock-free #+Clozure lock-free )) #+allegro (declaim (type hash-table *hl-hash-table-size-ht*)) #+allegro (defvar *allegro-hl-hash-table-size-ht* ; See the comment about this hash table in hl-mht. (make-hash-table)) (defmacro hl-mht (&rest args) ; This macro is a wrapper for hl-mht-fn, which in turn is a wrapper for ; make-hash-table. ; Because of our approach to threading, we generally don't need our hash tables ; to be protected by locks. HL-MHT is essentially like make-hash-table, but on ; CCL creates hash tables that aren't shared between threads, which may result ; in slightly faster updates. ; In Allegro CL 9.0 (and perhaps other versions), make-hash-table causes ; creation of a hash table of a somewhat larger size than is specified by the ; :size argument, which can cause hons-shrink-alist to create hash tables of ; ever-increasing size when this is not necessary. The following example ; illustrates this problem, which was first observed in community book ; books/parsers/earley/earley-parser.lisp. ; (defconst *end* :end) ; ; (defn my-hons-shrink-alist (a) ; (let ((ans (hons-shrink-alist a *end*))) ; (prog2$ (fast-alist-free a) ; ans))) ; ; (defun my-fast-alist3 (n ans) ; (declare (type (integer 0 *) n)) ; (cond ((zp n) ans) ; (t (my-fast-alist3 (1- n) ; (my-hons-shrink-alist ; (hons-acons (mod n 10) (* 10 n) ans)))))) ; ; (trace! (hl-mht-fn :native t :exit t)) ; ; (time$ (my-fast-alist3 33 *end*)) ; Our solution is to use a hash table, *allegro-hl-hash-table-size-ht*, to map ; the actual size of a hash table, h, to the :size argument of the call of ; hl-mht that created h. Then, when we want to create a hash table of a given ; :size, new-size-arg, we look in *allegro-hl-hash-table-size-ht* to check ; whether a previous call of hl-mht created a hash table of that size using ; some :size, old-size-arg, and if so, then we call make-hash-table with :size ; old-size-arg instead of new-size-arg. (Note that we don't give this special ; treatment in the case that hl-mth is called without an explicit :size; but ; that seems harmless.) #-allegro `(hl-mht-fn ,@args) #+allegro (let ((tail (and (keyword-value-listp args) (assoc-keyword :size args)))) (cond (tail (let ((size-arg-var (gensym)) (old-size-arg-var (gensym))) `(let* ((,size-arg-var ,(cadr tail)) (,old-size-arg-var (gethash ,size-arg-var *allegro-hl-hash-table-size-ht*))) (cond (,old-size-arg-var (hl-mht-fn :size ,old-size-arg-var ,@(remove-keyword :size args))) (t (let ((ht (hl-mht-fn ,@args))) (setf (gethash (hash-table-size ht) *allegro-hl-hash-table-size-ht*) ,size-arg-var) ht)))))) (t `(hl-mht-fn ,@args))))) ; In CCL, one can use (ccl::static-cons a b) in place of (cons a b) to create a ; cons that will not be moved by the garbage collector. ; ; Unlike an ordinary cons, every static cons has a unique index, which is a ; fixnum, and which may be obtained with (ccl::%staticp x). Per Gary Byers, ; this index will be forever fixed, even after garbage collection, even after ; saving an image. ; ; Even more interesting, the cons itself can be recovered from its index using ; ccl::%static-inverse-cons. Given the index of a static cons, such as ; produced by ccl::%staticp, %static-inverse-cons produces the corresponding ; static cons. Given an invalid index (such as the index of a cons that has ; been garbage collected), %static-inverse-cons produces NIL. Hence, this ; gives us a way to tell if a cons has been garbage collected, and lets us ; implement a clever scheme for "washing" honses. ; ; We now write wrappers for static-cons, %staticp, and %static-inverse-cons, to ; make it easier to plug in alternative implementations in other Lisps. #+static-hons (defabbrev hl-static-cons (a b) (ccl::static-cons a b)) #+static-hons (defabbrev hl-staticp (x) (ccl::%staticp x)) #+static-hons (defabbrev hl-static-inverse-cons (x) (ccl::%static-inverse-cons x)) #+static-hons (defabbrev hl-machine-address-of (x) (ccl::%address-of x)) #+static-hons (defabbrev hl-machine-hash (x) ; NOT A FUNCTION. Note that (EQUAL (HL-MACHINE-HASH X) (HL-MACHINE-HASH X)) is ; not necessarily true, because objects may be moved during garbage collection ; in CCL. ; ; We use the machine address of an object to compute a hash code within [0, ; 2^20). We obtain a good distribution on 64-bit CCL, but we have not tried ; this on 32-bit CCL. ; ; We right-shift the address by five places because smaller shifts led to worse ; distributions. We think this is because the low 3 bits are just tag bits ; (which are not interesting), and the next 2 bits never add any information ; because conses are word-aligned. ; ; To change this from 2^20 to other powers of 2, you should only need to adjust ; the mask. We think 2^20 is a good number, since a 2^20 element array seems ; to require about 8 MB of memory, e.g., our whole cache will take 16 MB. (let* ((addr (hl-machine-address-of x)) (addr>>5 (the fixnum (ash (the fixnum addr) -5)))) ;; (ADDR>>5) % 2^20 (the fixnum (logand (the fixnum addr>>5) #xFFFFF)))) ; ---------------------------------------------------------------------- ; ; CACHE TABLES ; ; ---------------------------------------------------------------------- ; A Cache Table is a relatively simple data structure that can be used to ; (partially) memoize the results of a computation. Cache tables are used by ; the Hons implementation, but are otherwise independent from the rest of HONS. ; We therefore introduce them here, up front. ; ; The operations of a Cache Table are as follows: ; ; (HL-CACHE-GET KEY TABLE) Returns (MV PRESENT-P VAL) ; (HL-CACHE-SET KEY VAL TABLE) Destructively modifies TABLE ; ; In many ways, a Cache Table is similar to an EQ hash table, but there are ; also some important differences. Unlike an ordinary hash table, a Cache ; Table may "forget" some of its bindings at any time. This allows us to ; ensure that the Cache Table does not grow beyond a fixed size. ; ; Cache Tables are not thread safe. ; ; We have two implementations of Cache Tables. ; ; Implementation 1. For Lisps other than 64-bit CCL. (#-static-hons) ; ; A Cache Table is essentially an ordinary hash table, along with a separate ; field that tracks its count. ; ; It is almost an invariant that this count should be equal to the ; HASH-TABLE-COUNT of the hash table, but we do NOT rely upon this for ; soundness and this property might occasionally be violated due to ; interrupts. In such cases, we ensure that the count is always more than ; the HASH-TABLE-COUNT of the hash table. (The only negative consequence of ; this is that the table may be cleared more frequently.) ; ; The basic scheme is as follows. Whenever hl-cache-set is called, if the ; count exceeds our desired threshold, we clear the hash table before we ; continue. The obvious disadvantage of this is that we may throw away ; results that may be useful. But the advantage is that we ensure that the ; cache table does not grow. On one benchmark, this approach was about ; 17% slower than letting the hash table grow without restriction (notably ; ignoring all garbage collection costs), but it lowered the memory usage ; from 2.8 GB to 92 MB. ; ; We have considered improving the performance by experimenting with the ; size of its cache. A larger cache means less frequent clearing but ; requires more memory. We also looked into more smartly clearing out the ; cache. One idea was to throw away only half of the entries "at random" by ; just allowing the maphash order to govern whether we throw out one entry ; or another. But when this was slow, we discovered that, at least on CCL, ; iterating over a hash table is fairly expensive and requires the keys and ; values of the hash table to be copied into a list. For a 500k cache, ; "clearing" half the entries required us to allocate 6 MB, and ruined ; almost all memory savings we had hoped for. Hence, we just use ordinary ; clrhash. ; ; Implementation 2. For 64-bit CCL (#+static-hons) we use a better scheme. ; ; A Cache Table contains two arrays, KEYDATA and VALDATA. These arrays ; associate "hash codes" (array indices) to keys and values, respectively. ; We could have used a single array with (key . val) pairs as its entries, ; but using two separate arrays allows us to implement hl-cache-set with no ; consing. ; ; These hash codes are based upon the actual machine address of KEY, and ; hence (1) may easily collide and (2) are not reliable across garbage ; collections. To give a sensible semantics, in hl-cache-get, we must ; explicitly check that this hash code has the proper KEY. ; ; Our hashing function, hl-machine-hash, performs quite well. According to ; a rough test, it takes only about the same time as three or four fixnum ; additions. Here's the testing code we used: ; ; (defun f (x) ;; (f '(1 . 2)) takes 8.709 seconds ; (let ((acc 0)) ; (declare (type fixnum acc)) ; (time (loop for i fixnum from 1 to 1000000000 ; do ; (setq acc (the fixnum (+ (the fixnum acc) (the fixnum (car x))))) ; (setq acc (the fixnum (+ (the fixnum acc) (the fixnum (cdr x))))) ; (setq acc (the fixnum (+ (the fixnum acc) (the fixnum (car x))))) ; (setq acc (the fixnum (+ (the fixnum acc) (the fixnum (cdr x))))) ; )) ; acc)) ; ; (defun g (x) ;; (g '(1 . 2)) takes 8.005 seconds ; (let ((acc 0)) ; (declare (type fixnum acc)) ; (time (loop for i fixnum from 1 to 1000000000 ; do ; (setq acc (the fixnum (+ acc (the fixnum (hl-machine-hash x))))))) ; acc)) ; ; However, in addition to fast execution speed, we want this function to ; produce a good distribution so that we may hash on its result. We have ; hard-coded in a size of 2^20 for our data arrays, but it would not be very ; to change this. To determine how well it distributes addresses, we ; computed the hash codes for a list of 2^24 objects, which is more than the ; 2^20 hash codes that we have made available. We found that every hash ; code was used precisely 16 times, a perfect distribution! (Of course, ; when this is used on actual data produced by a program, we do not expect ; the results to be so good.) Here is some basic testing code: ; ; (defparameter *hashes* nil) ; ; (let* ((tries (expt 2 24)) ; (hashes (time (loop for i fixnum from 1 to tries collect ; (hl-machine-hash (cons 1 2))))) ; (nhashes (time (len (sets::mergesort hashes))))) ; (setq *hashes* hashes) ; (format t "Got ~:D entries for ~:D tries.~%" nhashes tries)) ; ; (defparameter *dupes* (hons-duplicity-alist *hashes*)) ; (sets::mergesort (strip-cdrs *dupes*)) ; BOZO: Implicitly, our cache table has NIL bound to NIL; this might not ; be appropriate for "memoizing" other applications. #-static-hons (defconstant hl-cache-table-size ;; Size of the cache table 400000) #-static-hons (defconstant hl-cache-table-cutoff ;; Clear the table for hl-norm when it gets to 3/4 full. (floor (* 0.75 hl-cache-table-size))) (defstruct hl-cache #+static-hons (keydata (make-array (expt 2 20) :initial-element nil) :type simple-vector) #+static-hons (valdata (make-array (expt 2 20) :initial-element nil) :type simple-vector) #-static-hons (table (hl-mht :test #'eq :size hl-cache-table-size) :type hash-table) #-static-hons (count 0 :type fixnum)) (defun hl-cache-set (key val cache) (declare (type hl-cache cache)) #+static-hons (let ((keydata (hl-cache-keydata cache)) (valdata (hl-cache-valdata cache)) (code (hl-machine-hash key))) (hl-without-interrupts (setf (svref keydata (the fixnum code)) key) (setf (svref valdata (the fixnum code)) val))) #-static-hons (let ((table (hl-cache-table cache)) (count (hl-cache-count cache))) ;; This is a funny ordering which is meant to ensure the count exceeds or ;; agrees with (hash-table-count table), even in the face of interrupts. (setf (hl-cache-count cache) (the fixnum (+ 1 (the fixnum count)))) (when (> (the fixnum count) (the fixnum hl-cache-table-cutoff)) (clrhash table) ;; We set count to one, not zero, because we're about to add an element. (setf (hl-cache-count cache) 1)) (setf (gethash key table) val))) (defun hl-cache-get (key cache) ; (HL-CACHE-GET KEY CACHE) --> (MV PRESENT-P VAL) ; ; Note that this isn't thread-safe. If we want a truly multithreaded hons, ; we'll need to think about how to protect access to the cache. (declare (type hl-cache cache)) #+static-hons (let* ((keydata (hl-cache-keydata cache)) (code (hl-machine-hash key)) (elem-key (svref keydata (the fixnum code)))) (if (eq elem-key key) (let* ((valdata (hl-cache-valdata cache)) (elem-val (svref valdata (the fixnum code)))) (mv t elem-val)) (mv nil nil))) #-static-hons (let* ((table (hl-cache-table cache)) (val (gethash key table))) (if val (mv t val) (mv nil nil)))) (defun hl-cache-clear (cache) (declare (type hl-cache cache)) #+static-hons (let ((keydata (hl-cache-keydata cache)) (valdata (hl-cache-valdata cache))) (loop for i fixnum from 0 to (expt 2 20) do (setf (svref keydata i) nil) (setf (svref valdata i) nil))) #-static-hons (progn ;; This ordering ensures count >= (hash-table-count table) even in ;; the face of interrupts (clrhash (hl-cache-table cache)) (setf (hl-cache-count cache) 0))) ; ESSAY ON HONS SPACES ; ; The 'ACL2 Objects' are described in the ACL2 function bad-lisp-objectp; ; essentially they are certain "good" symbols, characters, strings, and ; numbers, recursively closed under consing. Note that stobjs are not ACL2 ; Objects under this definition. ; ; The 'Hons Spaces' are fairly complex structures, introduced with the ; defstruct for hl-hspace, which must satisfy certain invariants. At any point ; in time there may be many active Hons Spaces, but separate threads may never ; access the same Hons Space! This restriction is intended to minimize the ; need to lock while accessing Hons Spaces. ; ; Aside. Shareable Hons Spaces might have some advantages. They might ; result in lower overall memory usage and reduce the need to re-hons data ; in multiple threads. They might also be a better fit for Rager's ; parallelism routines. But acquiring locks might slow honsing in ; single-threaded code and make our code more complex. We should ; investigate this later. ; ; ; Fundamental Operations. ; ; A Hons Space provides two fundamental operations. ; ; First, given any ACL2 Object, X, and any Hons Space HS, we must be able to ; determine whether X is 'normed' with respect to HS. The fundamental ; invariant of normed objects is that if A and B are both normed with respect ; to HS, then (EQUAL A B) holds iff (EQL A B). ; ; Second, given any ACL2 Object, X, and any Hons Space HS, we must be able to ; 'norm' X to obtain an ACL2 Object that is EQUAL to X and which is normed with ; respect to HS. Note that norming is 'impure' and destructively modifies HS. ; This modification is really an extension: any previously normed object will ; still be normed, but previously non-normed objects may now also be normed. ; ; ; Tracking Normed Objects. ; ; To support these operations, a Hons Space contains certain hash tables and ; arrays that record which ACL2 Objects are currently regarded as normed. ; ; Symbols, characters, and numbers. These objects automatically and trivially ; satisfy the fundamental invariant of normed objects. We therefore regard all ; symbols, characters, and numbers as normed with respect to any Hons Space, ; and nothing needs to be done to track whether particular symbols, characters, ; or numbers are normed. ; ; Strings. Within each Hons Space, we may choose some particular string, X, as ; the normed version of all strings that are equal to X. We record this choice ; in the STR-HT field of the Hons Space, which is an EQUAL hash table. The ; details of what we record in the STR-HT actually depend on whether 'classic ; honsing' or 'static honsing' is being used. ; ; Conses. Like strings, there are EQUAL conses which are not EQL. We could ; account for this by setting up another equal hash table, as we did for ; strings, but EQUAL hashing of conses can be very slow. More efficient ; schemes are possible if we insist upon two reasonable invariants: ; ; INVARIANT C1. The car of a normed cons must be normed. ; INVARIANT C2. The cdr of a normed cons must be normed. ; ; Using these invariants, we have developed two schemes for tracking which ; conses are normed. One approach, called classic-honsing, makes use of only ; ordinary Common Lisp functions. The second approach, static-honsing, is ; higher performance but requires features that are specific to Clozure Common ; Lisp. ; ESSAY ON CLASSIC HONSING ; ; Prerequisite: see the essay on hons spaces. ; ; Classic Honsing is a scheme for tracking normed conses that can be used in ; any Lisp. Here, every normed cons is recorded in one of three hash tables. ; In particular, whenever X = (A . B) is normed, then X will be found in ; either: ; ; NIL-HT, when B is NIL, ; CDR-HT, when B is a non-NIL symbol, cons, or a string, or ; CDR-HT-EQL otherwise. ; ; The NIL-HT binds A to X whenever X = (A . NIL) is a normed cons. Thanks to ; Invariant C1, which assures us that A will be normed, we only need to use an ; EQL hash table for NIL-HT. ; ; For other conses, we basically implement a two-level hashing scheme. To ; determine if an cons is normed, we first look up its CDR in the CDR-HT or ; CDR-HT-EQL, depending on its type. Both of these tables bind B to the set of ; all normed X such that X = (A . B) for any A. These sets are represented as ; 'flex alists', defined later in this file. So, once we have found the proper ; set for B, we look through it and see whether there is a normed cons in it ; with A as its CAR. ; ; We use the CDR-HT (an EQ hash table) for objects whose CDRs are ; EQ-comparable, and the CDR-HT-EQL (an EQL hash table) for the rest. We may ; use CDR-HT for both strings and conses since, by Invariant C2, we know that ; the CDR is normed and hence pointer equality suffices. ; ; The only other thing to mention is strings. In the classic honsing scheme, ; the STR-HT simply associates each string to its normed version. That is, a ; string X is normed exactly when (eq X (gethash X STR-HT)). It is ; straightforward to norm a string X: a STR-HT lookup tells us whether a normed ; version of X exists, if so, what it is. Otherwise, when no normed version of ; X exists, we effectively 'norm' X by extending the STR-HT by binding X to ; itself. ; ; Taken all together, the STR-HT, NIL-HT, CDR-HT, and CDR-HT-EQL completely ; determine which ACL2 objects are normed in the classic honsing scheme. ; ESSAY ON STATIC HONSING ; ; Prerequisite: see the essay on hons spaces. ; ; Static Honsing is a scheme for tracking normed conses that can be used only ; in Clozure Common Lisp. ; ; Static Honsing is an alternative to classic honsing that exploits static ; conses for greater efficiency. Here, only static conses can be considered ; normed, and SBITS is a bit-array that records which static conses are ; currently normed. That is, suppose X is a static cons and let I be the index ; of X. Then X is considered normed exactly when the Ith bit of SBITS is 1. ; This is a very fast way to determine if a cons is normed! ; ; ; Addresses for Normed Objects. ; ; To support hons construction, we also need to be able to do the following: ; given two normed objects A and B, find the normed version of (A . B) or ; determine that none exists. ; ; Toward this goal, we first develop a reliable 'address' for every normed ; object; this address has nothing to do with machine (X86, PowerPC, or other) ; addresses. To begin, we statically assign addresses to NIL, T, and certain ; small integers. In particular: ; ; Characters are given addresses 0-255, corresponding to their codes ; NIL and T are given addresses 256 and 257, respectively ; Integers in [-2^14, 2^23] are given the subsequent addresses ; ; All other objects are dynamically assigned addresses. In particular, suppose ; that BASE is the start of the dynamically-allocated range. Then, ; ; The address of a static cons, C, is Index(C) + BASE, where Index(C) is the ; index returned by HL-STATICP. ; ; For any other atom, X, we construct an associated static cons, say X_C, ; and then use Index(X_C) + BASE as the address of X. ; ; This scheme gives us a way to generate a unique, reliable address for every ; ACL2 Object we encounter. These addresses start small and increase as more ; static conses are created. ; ; We record the association of these "other atoms" to their corresponding ; static conses in different ways, depending upon their types: ; ; For symbols, the static cons is stored in the 'hl-static-address property ; for the symbol. This results in something a little bizarre: symbol ; addresses are implicitly shared across all Hons Spaces, and so we must take ; care to ensure that our address-allocation code is thread safe. ; ; For strings, the STR-HT binds each string X to the pair (NX . NX_C), where ; NX is the normed version of X and NX_C is the static cons whose index is ; being used as the address for NX. ; ; For any other atoms, the Hons Space includes OTHER-HT, an EQL hash table ; that associates each atom X with X_C, the static cons for X. ; ; In the future, we might want to think about the size of BASE. Gary might be ; be able to extend static cons indicies so that they start well after 128, ; perhaps eliminating the need to add BASE when computing the addresses for ; static conses. On the other hand, it's probably just a matter of who is ; doing the addition, and our current scheme gives us good control over the ; range. ; ; ; Address-Based Hashing. ; ; Given the addresses of two normed objects, A and B, the function ; hl-addr-combine generates a unique integer that can be used as a hash key. ; ; Each Hons Space includes ADDR-HT, an EQL hash table that associates these ; keys to the normed conses to which they correspond. In other words, suppose ; C = (A . B) is a normed cons, and KEY is the key generated for the addresses ; of A and B. Then ADDR-HT associates KEY to C. ; ; Hence, assuming A and B are normed, we can determine whether a normed cons of ; the form (A . B) exists by simply generating the proper KEY and then checking ; whether ADDR-HT includes an entry for this key. ; DEFAULT SIZES. The user can always call hl-hons-resize to get bigger tables, ; but we still want good defaults. These sizes are used in the structures that ; follow. (defparameter *hl-hspace-str-ht-default-size* 1000) (defparameter *hl-ctables-nil-ht-default-size* 5000) (defparameter *hl-ctables-cdr-ht-default-size* 100000) (defparameter *hl-ctables-cdr-ht-eql-default-size* 1000) (defparameter *hl-hspace-addr-ht-default-size* 150000) (defparameter *hl-hspace-sbits-default-size* 16000000) (defparameter *hl-hspace-other-ht-default-size* 1000) (defparameter *hl-hspace-fal-ht-default-size* 1000) (defparameter *hl-hspace-persist-ht-default-size* 100) #-static-hons (defstruct hl-ctables ; CTABLES STRUCTURE. This is only used in classic honsing. We aggregate ; together the NIL-HT, CDR-HT, and CDR-HT-EQL fields so that we can clear them ; out all at once in hl-hspace-hons-clear, for Ctrl+C safety. (nil-ht (hl-mht :test #'eql :size *hl-ctables-nil-ht-default-size*) :type hash-table) (cdr-ht (hl-mht :test #'eq :size *hl-ctables-cdr-ht-default-size*) :type hash-table) (cdr-ht-eql (hl-mht :test #'eql :size *hl-ctables-cdr-ht-eql-default-size*) :type hash-table)) (defun hl-initialize-faltable-table (fal-ht-size) ; Create the initial TABLE for a the FALTABLE. See the Essay on Fast Alists, ; below, for more details. ; ; [Sol]: Note (Sol): The non-lock-free hashing algorithm in CCL seems to have ; some bad behavior when remhashes are mixed in with puthashes in certain ; patterns. One of these is noted below by Jared in the "Truly disgusting ; hack" note. Another is that when a table grows to the threshold where it ; should be resized, it is instead rehashed in place if it contains any deleted ; elements -- so if you grow up to 99% of capacity and then repeatedly insert ; and delete elements, you're likely to spend a lot of time rehashing without ; growing the table. ; ; [Jared]: Truly disgusting hack. As of Clozure Common Lisp revision 14519, in ; the non lock-free version of 'remhash', there is a special case: deleting the ; last remaining element from a hash table triggers a linear walk of the hash ; table, where every element in the vector is overwritten with the ; free-hash-marker. This is devastating when there is exactly one active fast ; alist: every "hons-acons" and "fast-alist-free" operation requires a linear ; walk over the TABLE. This took me two whole days to figure out. To ensure ; that nobody else is bitten by it, and that I am not bitten by it again, here ; I ensure that the TABLE always has at least one fast alist within it. This ; alist is unreachable from any ordinary ACL2 code so it should be quite hard ; to free it. (let ((table (hl-mht :test #'eq :size (max 100 fal-ht-size) :lock-free t :weak :key))) #+Clozure ;; This isn't necessary with lock-free, but doesn't hurt. Note that T is ;; always honsed, so sentinel is a valid fast-alist. I give this a ;; sensible name since it can appear in the (fast-alist-summary). (let* ((entry (cons t t)) (sentinel-al (cons entry 'special-builtin-fal)) (sentinel-ht (hl-mht :test #'eql))) (setf (gethash t sentinel-ht) entry) (setf (gethash sentinel-al table) sentinel-ht)) table)) (defstruct hl-falslot ; FAST-ALIST CACHE SLOT. See the Essay on Fast Alists, below, for more ; details. (key nil) ;; The alist being bound, or NIL for empty slots (val nil) ;; Its backing hash table (uniquep t :type boolean) ;; Flag for memory consistency ; Invariant 1. If KEY is non-nil, then it is a valid fast alist. ; ; Invariant 2. If KEY is non-nil, VAL is the appropriate backing hash table ; for this KEY (i.e., it is not some old/stale hash table or some newer/updated ; hash table.) ; ; Invariant 3. If UNIQUEP is true, then KEY is not bound in the main TABLE, ; i.e., it exists only in this slot. ; ; Invariant 4. No slots ever have the same KEY unless it is NIL. ) (defstruct (hl-faltable (:constructor hl-faltable-init-raw)) ; FAST-ALIST TABLE STRUCTURE. See the Essay on Fast Alists, below, for more ; details. ; ; This is essentially just an association from alists to backing hash tables. ; We previously made the associations an EQ hash table for alists to their ; backing hash tables. And logically, that's all the HL-FALTABLE is. ; ; But, as a tweak, we add a small cache in front. This cache us to avoid ; hashing in the very common cases where we're growing up a new hash table or ; repeatedly doing lookups in just a couple of hash tables. ; ; BOZO consider using CCL weak "populations" to make the slots weak like the ; table. (slot1 (make-hl-falslot) :type hl-falslot) (slot2 (make-hl-falslot) :type hl-falslot) (eject1 nil :type boolean) ;; want to eject slot1 on cache miss? (table (hl-initialize-faltable-table *hl-hspace-fal-ht-default-size*) :type hash-table)) (defun hl-faltable-init (&key (size *hl-hspace-fal-ht-default-size*)) (hl-faltable-init-raw :table (hl-initialize-faltable-table size))) (defstruct (hl-hspace (:constructor hl-hspace-init-raw)) ; HONS SPACE STRUCTURE. See the above essays on hons spaces, classic honsing, ; and static honsing above to understand this structure. (str-ht (hl-mht :test #'equal :size *hl-hspace-str-ht-default-size*) :type hash-table) ;; Classic Honsing #-static-hons (ctables (make-hl-ctables) :type hl-ctables) ;; Static Honsing #+static-hons (addr-ht (hl-mht :test #'eql :size *hl-hspace-addr-ht-default-size*) :type hash-table) #+static-hons (sbits (make-array *hl-hspace-sbits-default-size* :element-type 'bit :initial-element 0) :type (simple-array bit (*))) #+static-hons (other-ht (hl-mht :test #'eql :size *hl-hspace-other-ht-default-size*) :type hash-table) ;; Address limits are discussed in the essay on ADDR-LIMIT, below. #+static-hons (addr-limit *hl-hspace-addr-ht-default-size* :type fixnum) ;; Miscellaneous Fields. ;; NORM-CACHE is described in the essay on HL-HSPACE-NORM, below. (norm-cache (make-hl-cache) :type hl-cache) ;; FALTABLE is described in the documentation for fast alists. (faltable (hl-faltable-init) :type hl-faltable) ;; PERSIST-HT is described in the documentation for hl-hspace-persistent-norm (persist-ht (hl-mht :test #'eq :size *hl-hspace-persist-ht-default-size*) :type hash-table) ) (defun hl-hspace-init (&key (str-ht-size *hl-hspace-str-ht-default-size*) (nil-ht-size *hl-ctables-nil-ht-default-size*) (cdr-ht-size *hl-ctables-cdr-ht-default-size*) (cdr-ht-eql-size *hl-ctables-cdr-ht-eql-default-size*) (addr-ht-size *hl-hspace-addr-ht-default-size*) (sbits-size *hl-hspace-sbits-default-size*) (other-ht-size *hl-hspace-other-ht-default-size*) (fal-ht-size *hl-hspace-fal-ht-default-size*) (persist-ht-size *hl-hspace-persist-ht-default-size*)) ; (HL-HSPACE-INIT ...) --> Hons Space ; ; This is the proper constructor for hons spaces. The arguments allow you to ; override the default sizes for the various tables, which may be useful if you ; have a good idea of what your application will need. ; ; Note that we enforce certain minimum sizes, just because it seems like ; smaller sizes wouldn't really be sensible. #+static-hons (declare (ignore nil-ht-size cdr-ht-size cdr-ht-eql-size)) #+static-hons (hl-hspace-init-raw :str-ht (hl-mht :test #'equal :size (max 100 str-ht-size)) :addr-ht (hl-mht :test #'eql :size (max 100 addr-ht-size)) :addr-limit (max 100 addr-ht-size) :other-ht (hl-mht :test #'eql :size (max 100 other-ht-size)) :sbits (make-array (max 100 sbits-size) :element-type 'bit :initial-element 0) :norm-cache (make-hl-cache) :faltable (hl-faltable-init :size fal-ht-size) :persist-ht (hl-mht :test #'eq :size (max 100 persist-ht-size)) ) #-static-hons (declare (ignore addr-ht-size sbits-size other-ht-size)) #-static-hons (hl-hspace-init-raw #-static-hons :str-ht (hl-mht :test #'equal :size (max 100 str-ht-size)) :ctables (make-hl-ctables :nil-ht (hl-mht :test #'eql :size (max 100 nil-ht-size)) :cdr-ht (hl-mht :test #'eq :size (max 100 cdr-ht-size)) :cdr-ht-eql (hl-mht :test #'eql :size (max 100 cdr-ht-eql-size))) :norm-cache (make-hl-cache) :faltable (hl-faltable-init :size fal-ht-size) :persist-ht (hl-mht :test #'eq :size (max 100 persist-ht-size)) )) ; ESSAY ON FLEX ALISTS (Classic Honsing Only) ; ; Given certain restrictions, a 'flex alist' is similar to an EQL alist, except ; that it is converted into a hash table after reaching a certain size. ; ; RESTRICTION 1. A flex alist must be used according to the single threaded ; discipline, i.e., you must always extend the most recently extended flex ; alist. ; ; RESTRICTION 2. A flex alist must never be extended twice with the same ; key. This ensures that the entry returned by flex-assoc is always EQ to ; the unique entry which was inserted by flex-acons and permits trivial ; optimizations during the conversion to hash tables. ; ; Flex alists may be either ordinary, nil-terminated alists or hash tables. ; The idea is to avoid creating hash tables until there are enough elements to ; warrant doing so. That is, a flex alist starts out as an alist, but may be ; dynamically promoted to a hash table after a certain size is reached. ; ; The main use of flex alists is in the CDR-HT and CDR-HT-EQL fields of a ; hons space. ; ; [Jared]: I wonder if a larger threshold would be better. It might be worth ; having slow honsp checks when alists are in the 20-100 range in exchange for ; lower memory usage. (defabbrev hl-flex-alist-too-long (x) ; (hl-flex-alist-too-long x) == (> (length x) 18) for proper lists. It is ; inspired by the Milawa function len-over-250p. Although it is ugly, it is ; faster than looping and counting. (let ((4cdrs (cddddr x))) (and (consp 4cdrs) (let ((8cdrs (cddddr 4cdrs))) (and (consp 8cdrs) (let* ((12cdrs (cddddr 8cdrs))) (and (consp 12cdrs) (let* ((16cdrs (cddddr 12cdrs)) (18cdrs (cddr 16cdrs))) (consp 18cdrs))))))))) (defabbrev hl-flex-assoc (key al) ; (hl-flex-assoc key al) returns the entry associated with key, or returns nil ; if key is not bound. Note that the comparisons performed by flex-assoc are ; always done with EQL. (if (listp al) (assoc key al) (gethash key (the hash-table al)))) (defabbrev hl-flex-acons (elem al) ; (hl-flex-acons entry al) assumes that entry is a (key . val) pair, and ; extends the flex alist al by binding key to entry. ; ; Note: the caller must ensure to obey the restrictions described in the ; Essay on Flex Alists. ; ; Note about Ctrl+C Safety: this is locally safe assuming that (setf (gethash ; ...)) is safe. In the alist case we're pure, so there aren't any problems. ; In the 'conversion' case, the hash table doesn't become visible to the caller ; unless it's been fully constructed, so we're ok. In the hash table case, ; we're a single setf, which we assume is okay. (if (listp al) (cond ((hl-flex-alist-too-long al) ;; Because of uniqueness, we don't need to worry about shadowed ;; pairs; we can just copy all pairs into the new hash table. (let ((ht (hl-mht))) (declare (type hash-table ht)) (loop for pair in al do (setf (gethash (car pair) ht) pair)) (setf (gethash (car elem) ht) elem) ht)) (t (cons elem al))) (progn (setf (gethash (car elem) (the hash-table al)) elem) al))) ; ---------------------------------------------------------------------- ; ; DETERMINING IF OBJECTS ARE NORMED ; ; ---------------------------------------------------------------------- #+static-hons (declaim (inline hl-hspace-truly-static-honsp)) #+static-hons (defun hl-hspace-truly-static-honsp (x hs) ; (HL-HSPACE-TRULY-STATIC-HONSP X HS) --> BOOL ; ; Static Honsing only. X must be an ACL2 Cons and HS must be a Hons Space. We ; determine if X is a static cons whose bit is set in the SBITS array. If so, ; we X is considered normed with respect to HS. (let* ((idx (hl-staticp x))) (and idx (let ((sbits (hl-hspace-sbits hs))) (and (< (the fixnum idx) (the fixnum (length sbits))) (= 1 (the fixnum (aref sbits (the fixnum idx))))))))) #-static-hons (defabbrev hl-hspace-find-alist-for-cdr (b ctables) ; (HL-HSPACE-FIND-ALIST-FOR-CDR B CTABLES) --> FLEX ALIST ; ; Classic Honsing only. B is any ACL2 Object and CTABLES is the ctables ; structure from a Hons Space. Suppose there is some ACL2 Object, X = (A . B). ; We return the flex alist that X must belong to for classic honsing. Note ; that even though the NIL-HT starts out as a hash table, we can still regard ; it as a flex alist. (cond ((null b) (hl-ctables-nil-ht ctables)) ((or (consp b) (symbolp b) (stringp b)) (gethash b (hl-ctables-cdr-ht ctables))) (t (gethash b (hl-ctables-cdr-ht-eql ctables))))) (declaim (inline hl-hspace-honsp)) (defun hl-hspace-honsp (x hs) ; (HL-HSPACE-HONSP X HS) --> BOOL ; ; X must be an ACL2 Cons and HS is a Hons Space. We determine if X is normed ; with respect to HS. #+static-hons (hl-hspace-truly-static-honsp x hs) #-static-hons (let* ((a (car x)) (b (cdr x)) (ctables (hl-hspace-ctables hs)) (hons-set (hl-hspace-find-alist-for-cdr b ctables)) (entry (hl-flex-assoc a hons-set))) (eq x entry))) (defun hl-hspace-honsp-wrapper (x) ;; Bootstrapping hack for serialize ;; Assumes *default-hs* is already initialized (declare (special *default-hs*)) (hl-hspace-honsp x *default-hs*)) (defun hl-hspace-faltable-wrapper () ;; Bootstrapping hack for serialize ;; Assumes *default-hs* is already initialized (declare (special *default-hs*)) (hl-hspace-faltable *default-hs*)) (defun hl-hspace-normedp (x hs) ; (HL-HSPACE-NORMEDP X HS) --> BOOL ; ; X may be any ACL2 Object and HS is a Hons Space. We determine if X is normed ; with respect to HS. (declare (type hl-hspace hs)) (cond ((consp x) (hl-hspace-honsp x hs)) ((stringp x) (let* ((str-ht (hl-hspace-str-ht hs)) (entry (gethash x str-ht))) (and entry #+static-hons (eq x (car entry)) #-static-hons (eq x entry)))) (t t))) (defun hl-hspace-normedp-wrapper (x) ;; Bootstrapping hack for serialize ;; Assumes *default-hs* is already initialized (declare (special *default-hs*)) (hl-hspace-normedp x *default-hs*)) ; ---------------------------------------------------------------------- ; ; EXTENDED EQUALITY OPERATIONS ; ; ---------------------------------------------------------------------- (defun hl-hspace-hons-equal-lite (x y hs) ; (HL-HSPACE-HONS-EQUAL-LITE X Y HS) --> BOOL ; ; X and Y may be any ACL2 Objects and HS must be a Hons Space. We compute ; (EQUAL X Y). If X and Y happen to be normed conses, we can settle the ; question of their equality via simple pointer equality; otherwise we just ; call (EQUAL X Y). (declare (type hl-hspace hs)) (cond ((eq x y) t) ((and (consp x) (consp y) (hl-hspace-honsp x hs) (hl-hspace-honsp y hs)) nil) (t (equal x y)))) (defun hl-hspace-hons-equal (x y hs) ; (HL-HSPACE-HONS-EQUAL X Y HS) --> BOOL ; ; X and Y may be any ACL2 Objects and HS must be a Hons Space. We recursively ; check (EQUAL X Y), using pointer equality to determine the equality of any ; normed subtrees. (declare (type hl-hspace hs)) (cond ((eq x y) t) ((consp x) (and (consp y) (not (and (hl-hspace-honsp x hs) (hl-hspace-honsp y hs))) (hl-hspace-hons-equal (car x) (car y) hs) (hl-hspace-hons-equal (cdr x) (cdr y) hs))) ((consp y) nil) (t (equal x y)))) ; ---------------------------------------------------------------------- ; ; STATIC HONS ADDRESSING ; ; ---------------------------------------------------------------------- ; Our hashing scheme (see hl-addr-combine) performs best when both addresses ; involved are under 2^30. In other words, there are about 1,073 million ; "fast-hashing" addresses and the rest are "slow-hashing". ; ; Historic notes. ; ; We did not originally statically assign addresses to the characters, and do ; not think it is particularly important to do so. But, we like the idea of ; using numbers besides 0 and 1 as the addresses for T and NIL, under the ; probably misguided and certainly untested theory that perhaps using larger ; numbers will result in a better hashing distribution. ; ; We originally assigned a static, fast-hashing address for all integers in ; [-2^24, 2^24], and this decision "used up" about 33.6 million or 1/32 of the ; available fast-hashing addresses. We later decided that this seemed slightly ; excessive, and we scaled the range down to [-2^14, 2^23]. This new scheme ; uses up 8.4 million or 1/128 of the fast-hashing addresses. As a picture, we ; have: ; ; 8m 1.07 bn ; -|------------------------------- ... -----------------|--------------- ... ; ^ dynamic fast-hashing slow-hashing ; | ; | ; static fast-hashing ; ; We think this change is pretty much irrelevant and you shouldn't spend your ; time thinking about how to improve it. Why? ; ; (1) For most reasonable computations, slow addresses are never even used ; and so this change won't matter at all. ; ; (2) On the other hand, imagine a really massive computation that needs, ; say, 2 billion normed conses. Here, we are already paying the price of ; slow addresses for half the conses. Our change might mean that 1.06 ; billion instead of 1.04 billion of these conses will have fast-hashing ; addresses, but this is only about 1% of the conses, so its effect on ; performance is likely minimal. ; ; Even for applications that just barely exceed the boundary of slow-hashing, ; we're only talking about whether a small percentage of the conses having ; fast- or slow-hashing addresses. #+static-hons (defconstant hl-minimum-static-int ;; Minimum "small integer" that has a statically determined address. (- (expt 2 14))) #+static-hons (defconstant hl-maximum-static-int ;; Maximum "small integer" that has a statically determined address. (expt 2 23)) #+static-hons (defconstant hl-num-static-ints ;; Total number of statically determined addresses needed for small integers. (1+ (- hl-maximum-static-int hl-minimum-static-int))) #+static-hons (defconstant hl-dynamic-base-addr ;; Total number of statically determined addresses for all atoms. This is ;; the sum of: ;; - 256 characters ;; - 2 special symbols (T and NIL) ;; - The number of statically determined integers (+ 256 2 hl-num-static-ints)) #+static-hons (defconstant hl-static-int-shift ;; For integers with static addresses, the address is computed by adding ;; static-int-shift to their value. These integers are in [min, max] where ;; min < 0 and max > 0. The min integer will be assigned to location 258 = ;; 256 characters + 2 special symbols. We then count up from there. (+ 256 2 (- hl-minimum-static-int))) #+static-hons (ccl::defstatic *hl-symbol-addr-lock* ;; lock for hl-symbol-addr; see below. (ccl::make-lock '*hl-symbol-addr-lock*)) #+static-hons (defabbrev hl-symbol-addr (s) ; (HL-SYMBOL-ADDR S) --> NAT ; ; S must be a symbol other than T or NIL. If it already has an address, we ; look it up and return it. Otherwise, we must allocate an address for S and ; return it. ; ; We store the addresses of symbols on the symbol's propertly list. This could ; cause problems in multi-threaded code if two threads were simultaneously ; trying to generate a 'hl-static-address entry for the same symbol. In ; particular, each thread would generate its own static cons and try to use its ; index; the first thread, whose hash key would be overwritten by the second, ; would then be using the wrong address for the symbol. ; ; We could address this by using OTHER-HT instead of property lists, but ; property lists seem to be really fast, and our feeling is that we will really ; not be creating new addresses for symbols very often. So, it's probably ; better to pay the price of locking in this very limited case. ; ; Notes about Ctrl+C Safety. This function does not need to be protected by ; without-interrupts because installing the new 'hl-static-address cons is a ; single setf. (let ((addr-cons (get (the symbol s) 'hl-static-address))) (if addr-cons ;; Already have an address. ADDR-CONS = (S . TRUE-ADDR), where ;; TRUE-ADDR is Index(ADDR-CONS) + BASE. So, we just need to ;; return the TRUE-ADDR. (cdr addr-cons) ;; We need to assign an address. Must lock! (ccl::with-lock-grabbed (*hl-symbol-addr-lock*) ;; Some other thread might have assigned S an address before we ;; got the lock. So, double-check and make sure that there still ;; isn't an address. (setq addr-cons (get (the symbol s) 'hl-static-address)) (if addr-cons (cdr addr-cons) ;; Okay, safe to generate a new address. (let* ((new-addr-cons (hl-static-cons s nil)) (true-addr (+ hl-dynamic-base-addr (hl-staticp new-addr-cons)))) (rplacd (the cons new-addr-cons) true-addr) (setf (get (the symbol s) 'hl-static-address) new-addr-cons) true-addr)))))) #+static-hons (defun hl-addr-of-unusual-atom (x str-ht other-ht) ; See hl-addr-of. This function computes the address of any atom except for T ; and NIL. Wrapping this in a function is mainly intended to avoid code blowup ; from inlining. (cond ((symbolp x) (hl-symbol-addr x)) ((and (typep x 'fixnum) (<= hl-minimum-static-int (the fixnum x)) (<= (the fixnum x) hl-maximum-static-int)) (the fixnum (+ hl-static-int-shift (the fixnum x)))) ((typep x 'array) ; <-- (stringp x), but twice as fast in CCL. ;; Since we assume X is normed, its entry in the STR-HT exists and has ;; the form XC = (NX . TRUE-ADDR), so we just need to return the ;; TRUE-ADDR. (cdr (gethash x str-ht))) ((characterp x) (char-code x)) (t ;; Addresses for any other objects are stored in the OTHER-HT. But ;; these objects do not necessarily have their addresses generated ;; yet. (let* ((entry (gethash x other-ht))) (if entry ;; ENTRY is a static cons that has the form (x . TRUE-ADDR) ;; where TRUE-ADDR is Index(ENTRY) + BASE. So, we just need to ;; return the TRUE-ADDR. (cdr entry) ;; Else, we need to create an entry. (let* ((new-addr-cons (hl-static-cons x nil)) (true-addr (+ hl-dynamic-base-addr (hl-staticp new-addr-cons)))) (rplacd (the cons new-addr-cons) true-addr) (setf (gethash x other-ht) new-addr-cons) true-addr)))))) #+static-hons (defmacro hl-addr-of (x str-ht other-ht) ; (HL-ADDR-OF X STR-HT OTHER-HT) --> NAT and destructively updates OTHER-HT ; ; X is any normed ACL2 Object, and STR-HT and OTHER-HT are the corresponding ; fields of a Hons Space. We determine and return the address of X. This may ; require us to assign an address to X, which may require us to update the Hons ; Space. ; ; Ctrl+C Safety: this function need not be protected by without-interrupts. ; Even though it may modify the hons space, all invariants are preserved by the ; update; the only change is that OTHER-HT may be extended with a new entry, ; but the new entry is already valid by the time it is installed. `(let ((x ,x)) (cond ((consp x) (+ hl-dynamic-base-addr (hl-staticp x))) ((eq x nil) 256) ((eq x t) 257) (t (hl-addr-of-unusual-atom x ,str-ht ,other-ht))))) #+static-hons (defun hl-nat-combine* (a b) ;; See community book books/system/hl-addr-combine.lisp for all documentation ;; and a proof that this function is one-to-one. At one point, this was ;; going to be an inlined version of hl-nat-combine. We later decided not to ;; inline it, since it's a rare case and slow anyway, to avoid excessive ;; expansion in hl-addr-combine*. (+ (let* ((a+b (+ a b)) (a+b+1 (+ 1 a+b))) (if (= (logand a+b 1) 0) (* (ash a+b -1) a+b+1) (* a+b (ash a+b+1 -1)))) b)) #+static-hons (defabbrev hl-addr-combine* (a b) ;; Inlined version of hl-addr-combine. See community book ;; books/system/hl-addr-combine.lisp for all documentation and a proof that ;; this function is one-to-one. The only change we make here is to use typep ;; to see if the arguments are fixnums in the comparisons, which speeds up ;; our test loop by about 1/3. (if (and (typep a 'fixnum) (typep b 'fixnum) (< (the fixnum a) 1073741824) (< (the fixnum b) 1073741824)) ;; Optimized version of the small case (the (signed-byte 61) (- (the (signed-byte 61) (logior (the (signed-byte 61) (ash (the (signed-byte 31) a) 30)) (the (signed-byte 31) b))))) ;; Large case. (- (hl-nat-combine* a b) 576460752840294399))) ; ---------------------------------------------------------------------- ; ; ADDR LIMIT ; ; ---------------------------------------------------------------------- ; ESSAY ON ADDR-LIMIT (Static Honsing Only) ; ; This is a new feature that Jared added in January 2012. ; ; The ADDR-HT can grow very large. For example, as an experiment I made an ; ADDR-HT with room for 100 million honses and filled it up to 99% full. The ; total space it used was about 3.8 GB: 1.6 GB of actual cons data and 2.2 GB ; of overhead just for the table itself. In some of our proofs, we allocate an ; address table with room for 500 million entries. In this case, the size of ; the hash table's array (i.e., not counting the conses) would be 11 GB. ; ; Because of this, resizing the ADDR-HT can be very damaging. What do I mean ; by this? Note that resizing a hash table generally involves: ; ; (1) Allocating a new hash table that is bigger ; (2) Moving elements from the old hash table into the new hash table ; (3) Freeing the old hash table (immediately or later via GC) ; ; Until step 3 completes, we need to simultaneously have both the old and new ; tables allocated. But if the old table is already 11 GB, and we try to ; allocate a new table with 1.5x more space, the new table will be 16.5 GB and ; we'll need a total of 27.5 GB just for these tables. ; ; Practically, grabbing this much memory at once can be a problem. If we're in ; the middle of a big proof and we have giant memoization tables laying around, ; we might already be running close to the max physical memory of the machine. ; In this situation, trying to grab another 16.5 GB just because we want one ; more HONS is probably a terrible idea. It could cause us to start swapping, ; etc. But as a Common Lisp hash table, the ADDR-HT will be resized when the ; Lisp decides, and there's not really anything we can do about it. ; ; Ha ha, only serious. ; ; The ADDR-LIMIT is an attempt to avoid this kind of trouble. The basic idea ; is that shortly before resizing the ADDR-HT, we will call the function ; HL-ADDR-LIMIT-ACTION, which will inspect the ADDR-HT table and see if it's ; big enough to warrant doing anything drastic before the resize. If it is big ; enough, we will do a CLEAR-MEMOIZE-TABLES and a HONS-WASH, which can throw ; away the hash table before growing it. ; ; A practical difficulty of implementing this scheme is that Common Lisp ; doesn't give us a pre-resize hook for a hash table. Instead, we have to keep ; track of how full the ADDR-HT is to decide when to call HL-ADDR-LIMIT-ACTION. ; We just add a counter, ADDR-LIMIT, for this purpose. The idea is that this ; counter gets decreased every time we add a HONS into the ADDR-HT, and when it ; reaches zero we will trigger the action. ; ; The ADDR-LIMIT itself should be regarded merely as a performance counter and ; we generally do not make any claims about its accuracy or relevance to ; anything. We insist that it is a fixnum for performance, and this should ; practically cause no soundness issues. #+static-hons (defparameter *hl-addr-limit-minimum* ;; We don't do anything drastic during the HL-ADDR-LIMIT-ACTION unless the ;; ADDR-HT has grown at least this big. At 50 million entries, we're ;; starting to get up into the gigabyte range on our allocations. This could ;; probably use some tuning. 50000000) #+static-hons (defun hl-make-addr-limit-current (hs) (declare (type hl-hspace hs)) ; Reset the ADDR-LIMIT so that it will reach zero shortly after the table ; becomes 99% full. (let* ((addr-ht (hl-hspace-addr-ht hs)) (count (hash-table-count addr-ht)) (size (hash-table-size addr-ht)) (cutoff (floor (* 0.995 (coerce size 'float))))) (setf (hl-hspace-addr-limit hs) (- cutoff count)))) #+static-hons (defun hl-make-addr-limit-next (hs) (declare (type hl-hspace hs)) ; Reset the ADDR-LIMIT so that it will reach zero shortly after the table is ; resized and then grows to 95% full. Given reasonable rehashing sizes, this ; seems reasonably likely to trigger after one resize but before two resizes. ; At that point, HL-ADDR-LIMIT-ACTION will be able to do a better job of fixing ; it up again. (let* ((addr-ht (hl-hspace-addr-ht hs)) (count (hash-table-count addr-ht)) (size (hash-table-size addr-ht)) (rehash-size (hash-table-rehash-size addr-ht)) (future-cutoff (floor (* 0.995 rehash-size size)))) (setf (hl-hspace-addr-limit hs) (- future-cutoff count)))) #+static-hons (defun hl-addr-ht-fullness (hs) (let* ((addr-ht (hl-hspace-addr-ht hs)) (size (hash-table-size addr-ht)) (count (hash-table-count addr-ht))) (/ (coerce count 'float) (coerce size 'float)))) (defparameter *hl-addr-limit-should-clear-memo-tables* ;; Not sure if this is a good idea or not. t) #+static-hons (defun hl-addr-limit-action (hs) ; (HL-ADDR-LIMIT-ACTION HS) --> NIL and destructively modifies HS ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; See the Essay on ADDR-LIMIT. We see if the ADDR-HT is large and almost full, ; and if so we may clear the memoization tables and trigger a wash. For the ; wash to work properly, callers of this function must not have ADDR-HT bound. ; This is especially important since the ADDR-HT of the new HS may be a new ; hash table, i.e., the old ADDR-HT is not relevant anymore. (declare (type hl-hspace hs)) (unless (> (hl-addr-ht-fullness hs) 0.99) ;; This is a sort of safety mechanism to ensure that we only take ;; corrective action when we're over 99% full. This means we don't have to ;; really work very hard to keep the ADDR-LIMIT accurate, it'll ;; automatically get bumped up if we trigger it too soon. (hl-make-addr-limit-current hs) (return-from hl-addr-limit-action nil)) (let ((note-stream (get-output-stream-from-channel *standard-co*))) ;; We might eventually take this message out, but it seems nice to be able to ;; know that the ADDR-HT is about to be resized. (format note-stream "; Hons-Note: ADDR-LIMIT reached, ~:D used of ~:D slots.~%" (hash-table-count (hl-hspace-addr-ht hs)) (hash-table-size (hl-hspace-addr-ht hs))) (unless (> (hash-table-size (hl-hspace-addr-ht hs)) *hl-addr-limit-minimum*) ;; The table is small so it's not worth doing anything. So, just bump up ;; the ADDR-LIMIT so we won't look at it again until the next resize. (hl-make-addr-limit-next hs) (return-from hl-addr-limit-action nil)) ;; 99% full and the table is huge. Do something drastic. (format note-stream "; Hons-Note: Trying to reclaim space to avoid ADDR-HT resizing...~%") (force-output note-stream) (when *hl-addr-limit-should-clear-memo-tables* (clear-memoize-tables)) (hl-hspace-hons-wash hs) (format note-stream "; Hons-Note: After ADDR-LIMIT actions, ~:D used of ~:D slots.~%" (hash-table-count (hl-hspace-addr-ht hs)) (hash-table-size (hl-hspace-addr-ht hs))) (force-output note-stream) nil)) ; ---------------------------------------------------------------------- ; ; HONS CONSTRUCTION ; ; ---------------------------------------------------------------------- #+static-hons (defun hl-hspace-grow-sbits (idx hs) ; (HL-HSPACE-GROW-SBITS IDX HS) destructively updates HS ; ** may install a new SBITS ; ** callers should not have SBITS let-bound! ; ; Static Honsing only. IDX must be a natural number and HS must be a Hons ; Space. We generally expect this function to be called when SBITS has become ; too short to handle IDX, the static index of some static cons. We copy SBITS ; into a new, larger array and install it into the Hons Space. ; ; Growing SBITS is slow because we need to (1) allocate a new, bigger array, ; and (2) copy the old contents of SBITS into this new array. Accordingly, we ; want to add enough indices so that we can accommodate IDX and also any ; additional static conses that are generated in the near future without having ; to grow again. But at the same time, we don't want to consume excessive ; amounts of memory by needlessly growing SBITS beyond what will be needed. We ; try to balance these factors by increasing our capacity by 30% per growth. ; ; BOZO -- consider different growth factors? ; ; Ctrl+C Safety. This is locally ctrl+c safe assuming (setf (hl-hspace-bits ; hs) ...) is, because we only install the new array into the HS at the very ; end, and the new array is already valid by that time. But if we change this ; to use some kind of array resizing, we might need to add without-interrupts. (declare (type hl-hspace hs)) (let* ((sbits (hl-hspace-sbits hs)) (curr-len (length sbits)) (want-len (floor (* 1.3 (max curr-len idx)))) (new-len (min (1- array-total-size-limit) want-len))) (when (<= new-len curr-len) (error "Unable to grow static hons bit array.")) ;; CHANGE -- added a growth message (time$ (let ((new-sbits (make-array new-len :element-type 'bit :initial-element 0))) (declare (type (simple-array bit (*)) new-sbits)) (loop for i fixnum below curr-len do (setf (aref new-sbits i) (aref sbits i))) (setf (hl-hspace-sbits hs) new-sbits)) :msg "; Hons-Note: grew SBITS to ~x0; ~st seconds, ~sa bytes.~%" :args (list new-len)))) (defun hl-hspace-norm-atom (x hs) ; (HL-HSPACE-NORM-ATOM X HS) --> X' and destructively updates HS. ; ; X is any ACL2 Atom and HS is a Hons Space. We produce a normed version of X, ; extending the Hons Space if necessary. ; ; Ctrl+C Safety. This function does not need to be protected with ; without-interrupts; even though it extends the STR-HT, the invariants on a ; Hons Space still hold after the update. (cond ((typep x 'array) ; <-- (stringp x) (let* ((str-ht (hl-hspace-str-ht hs)) (entry (gethash x str-ht))) #-static-hons ;; In classic honsing, STR-HT just associates strings to their normed ;; versions. We make X the normed version of itself. (or entry (setf (gethash x str-ht) x)) #+static-hons ;; In static honsing, STR-HT associates X with XC = (NX . TRUE-ADDR), ;; where NX is the normed version of X and TRUE-ADDR = Index(XC) + Base. (if entry (car entry) (let* ((new-addr-cons (hl-static-cons x nil)) (true-addr (+ hl-dynamic-base-addr (hl-staticp new-addr-cons)))) (rplacd (the cons new-addr-cons) true-addr) (setf (gethash x str-ht) new-addr-cons) x)))) (t ;; All other atoms are already normed. x))) (defun hl-hspace-hons-normed (a b hint hs) ; (HL-HSPACE-HONS-NORMED A B HINT HS) --> (A . B) and destructively updates HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; A and B may be any normed ACL2 objects and HS is a hons space. HINT is ; either NIL, meaning no hint, or is a cons. ; ; HINT might have nothing to do with anything. But if HINT happens to be a ; cons of the form (A . B), by which we mean its car is literally EQ to A and ; its cdr is literally EQ to B, then we might decide to make HINT become the ; normed version of (A . B). The whole notion of a hint is mainly useful when ; we are re-norming previously normed objects, and might allow us to sometimes ; avoid constructing new conses when a suitable cons already exists. ; ; We produce a normed CONS that is equal to (A . B), possibly extending HS. ; This is the fundamental operation for what used to be called 'hopying' or ; 'hons copying,' and which is now referred to as 'norming' ACL2 objects. ; ; Ctrl+C Safety. This function makes minimal use of without-interrupts to ; ensure its safety, and need not be protected by the caller. #+static-hons ;; Static Honsing Version (let* ((str-ht (hl-hspace-str-ht hs)) (other-ht (hl-hspace-other-ht hs)) (addr-ht (hl-hspace-addr-ht hs)) (addr-a (hl-addr-of a str-ht other-ht)) (addr-b (hl-addr-of b str-ht other-ht)) (key (hl-addr-combine* addr-a addr-b)) (entry (gethash key addr-ht))) (or entry ;; Else, we are going to build and install a new HONS. First, do our ;; addr-limit checking. (cond ((<= (decf (hl-hspace-addr-limit hs)) 0) ;; I don't want to make any assumptions about what ;; HL-ADDR-LIMIT-ACTION does, so after doing the cleanup let's ;; just recur and start over. That way, if somehow the cleanup ;; ends up creating a HONS, we'll be sure we don't accidentally ;; install some competing hons. ;; NOTE: for the washing to be effective, we need to be sure to ;; release our binding of the addr-ht. (setq addr-ht nil) (hl-addr-limit-action hs) (hl-hspace-hons-normed a b hint hs)) (t (let* ((hint-idx (and (consp hint) (eq (car hint) a) (eq (cdr hint) b) (hl-staticp hint))) (pair (if hint-idx ;; Safe to use hint. hint (hl-static-cons a b))) (idx (or hint-idx (hl-staticp pair))) (sbits (hl-hspace-sbits hs))) ;; Make sure there are enough sbits. Ctrl+C Safe. (when (>= (the fixnum idx) (the fixnum (length sbits))) (hl-hspace-grow-sbits idx hs) (setq sbits (hl-hspace-sbits hs))) (hl-without-interrupts ;; Since we must simultaneously update SBITS and ADDR-HT, the ;; installation of PAIR must be protected by without-interrupts. (setf (aref sbits idx) 1) (setf (gethash key addr-ht) pair)) pair))))) #-static-hons ;; Classic Honsing Version (let ((ctables (hl-hspace-ctables hs))) (if (eq b nil) (let* ((nil-ht (hl-ctables-nil-ht ctables)) (entry (gethash a nil-ht))) (or entry (let ((new-cons (if (and (consp hint) (eq (car hint) a) (eq (cdr hint) b)) hint (cons a b)))) ;; Ctrl+C Safe since it's only a single setf. (setf (gethash a nil-ht) new-cons)))) (let* ((main-table (if (or (consp b) (symbolp b) (typep b 'array)) ;; (stringp b) (hl-ctables-cdr-ht ctables) (hl-ctables-cdr-ht-eql ctables))) (flex-alist (gethash b main-table)) (entry (hl-flex-assoc a flex-alist))) (or entry (let* ((was-alistp (listp flex-alist)) (new-cons (if (and (consp hint) (eq (car hint) a) (eq (cdr hint) b)) hint (cons a b))) (new-flex-alist (hl-flex-acons new-cons flex-alist))) ;; Ctrl+C Safety is subtle here. If was-alistp, then the above ;; flex-acons was applicative and didn't alter the Hons Space. ;; We'll go ahead and install the new flex alist, but this ;; installation occurs as an single update to the Hons Space. (when was-alistp (setf (gethash b main-table) new-flex-alist)) ;; Otherwise, the flex-acons was non-applicative, and the Hons ;; Space has already been safely extended, so everything's ok. new-cons)))))) ; ESSAY ON HL-HSPACE-NORM ; ; (HL-HSPACE-NORM X HS) --> X' and destructively updates HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; X is any ACL2 Object and might be normed or not; HS is a Hons Space. We ; return an object that is EQUAL to X and is normed. This may require us to ; destructively extend HS. ; ; This function is non-destructive with respect to X. Because of this, we ; sometimes need to recons portions of X. Why? ; ; One reason is that in static honsing we may only regard static conses as ; normed, so if X includes non-static conses we will need to produce static ; versions of them. ; ; Another scenario is as follows. Suppose X is (A . B), where B is normed ; but A is not normed, and further suppose that there exists some normed A' ; which is EQUAL to A, but no normed X' that is equal to X. Here, we cannot ; simply extend HS to regard X as normed, because this would violate our ; invariant that the car of any normed cons is also normed. Instead, we must ; construct a new cons whose car is A' and whose cdr is B, and then use this ; new cons as X'. ; ; We memoize the norming process to some degree. The NORM-CACHE field of the ; Hons Space is a Cache Table (see above) that associates some recently ; encountered conses with their normed versions. ; ; Historically, we used a hash table instead. A basic problem with this was, ; when should the table be created? We surely do not want to have to create a ; new hash table every time hons-copy is called -- after all, it's called twice ; in every call of hons! On the other, we don't want to use a global hash ; table that never gets cleaned out, because such a table could grow very large ; over time. Our first solution was to split norming into two functions. An ; auxilliary function did all the work, and freely used a hash table without ; regard to how large it might grow. Meanwhile, a top-level wrapper function ; examined the hash table after the auxillary function was finished, and if the ; table had been resized, we threw it away and started over. ; ; Using a global Cache Table nicely solves this problem. The Cache Table keeps ; a fixed size and automatically forgets elements. (defun hl-hspace-norm-aux (x cache hs) ; (HL-HSPACE-NORM-AUX X CACHE HS) --> X' and destructively modifies CACHE and HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; X is an ACL2 Object to copy. CACHE is the cache table from HS, and HS is the ; Hons Space we are updating. We return the normed version of X. (declare (type hl-cache cache) (type hl-hspace hs)) (cond ((atom x) (hl-hspace-norm-atom x hs)) ((hl-hspace-honsp x hs) x) (t (mv-let (present-p val) (hl-cache-get x cache) (if present-p val (let* ((a (hl-hspace-norm-aux (car x) cache hs)) (d (hl-hspace-norm-aux (cdr x) cache hs)) (x-prime (hl-hspace-hons-normed a d x hs))) (hl-cache-set x x-prime cache) x-prime)))))) (defun hl-hspace-norm-expensive (x hs) ; (HL-HSPACE-NORM-EXPENSIVE X HS) --> X' and destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; X is assumed to be not an atom and not a honsp. We put this in a separate ; function, mainly so that hl-hspace-norm can be inlined without resulting in ; too much code expansion. (let ((cache (hl-hspace-norm-cache hs))) (mv-let (present-p val) (hl-cache-get x cache) (if present-p val (hl-hspace-norm-aux x cache hs))))) (declaim (inline hl-hspace-norm)) (defun hl-hspace-norm (x hs) ;; See the essay on HL-HSPACE-NORM. (cond ((atom x) (hl-hspace-norm-atom x hs)) ((hl-hspace-honsp x hs) x) (t (hl-hspace-norm-expensive x hs)))) (defun hl-hspace-persistent-norm (x hs) ; (HL-HSPACE-PERSISTENT-NORM X HS) --> X' and destructively updates HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; X is any ACL2 object and HS is a Hons Space. We produce a new object that is ; EQUAL to X and is normed, which may require us to destructively modify HS. ; ; This function is essentially like hl-hspace-norm, except that when X is a ; cons, we also bind X' to T in the PERSIST-HT field of the Hons Space. This ; ensures that X' will be restored in hl-hspace-hons-clear, and also that it ; cannot be garbage collected during hl-hspace-hons-wash. ; ; INVARIANT P1: Every key in PERSIST-HT is a normed cons. ; ; Ctrl+C Safety. An interrupt might cause X' to not be added to PERSIST-HT, ; but that's fine and doesn't violate any invariants of the hons space. (let ((x (hl-hspace-norm x hs))) (when (consp x) (let ((persist-ht (hl-hspace-persist-ht hs))) (setf (gethash x persist-ht) t))) x)) (defabbrev hl-hspace-hons (x y hs) ; (HL-HSPACE-HONS X Y HS) --> (X . Y) which is normed, and destructively ; updates HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; X and Y may be any ACL2 Objects, whether normed or not, and HS must be a Hons ; Space. We produce a new cons, (X . Y), destructively extend HS so that it is ; considered normed, and return it. (declare (type hl-hspace hs)) (hl-hspace-hons-normed (hl-hspace-norm x hs) (hl-hspace-norm y hs) nil hs)) ; ---------------------------------------------------------------------- ; ; FAST ALISTS ; ; ---------------------------------------------------------------------- ; ESSAY ON FAST ALISTS ; ; Prerequisite: see :doc fast-alists for a user-level overview of fast alists, ; which for instance introduces the crucial notion of discipline. ; ; The implementation of fast alists is actually fairly simple. Each Hons Space ; includes a FALTABLE which associates each "fast alist" with an EQL hash ; table, called its "backing" hash table. ; ; INVARIANTS. For every "fast alist" AL that is bound in the FALTABLE, ; ; 1. AL is non-empty, i.e., atoms are never bound in FALTABLE. ; ; 2. For every (KEY . VAL) pair in AL, KEY is normed. This justifies our ; use of EQL-based backing hash tables. ; ; 3. The backing hash table, HT, must "agree with" AL. In particular, for ; all ACL2 Objects, X, the following relation must be satisfied: ; ; (equal (hons-assoc-equal X AL) ; (gethash (hons-copy X) HT)) ; ; In other words, for every (KEY . VALUE) pair in AL, HT must associate ; KEY to (KEY . VALUE). Meanwhile, if KEY is not bound in AL, then it ; must not be bound in HT. ; ; PREVIOUSLY we also insisted that each AL consists entirely of conses, i.e., ; there are no "malformed" entries in the alist. We abandoned this requirement ; to allow MAKE-FAST-ALIST to be the identity. ; ; The FALTABLE might as well have been an EQ hash table, and historically it ; was. But this meant that each HONS-ACONS had to do: ; ; - (GETHASH ALIST FALTABLE) find the current HT ; - (REMHASH ALIST FALTABLE) disassociate HT from ALIST ; - (SETF (GETHASH KEY HT) VAL) update HT ; - (SETF (GETHASH NEW-ALIST FALTABLE) HT) associate HT with NEW-ALIST ; ; This takes a lot of FALTABLE updates and all of this hashing gets expensive. ; To avoid it, we changed the FALTABLE into a structure which had a hash table ; and also a very small (two slot) cache in the front. This cache lets us be ; working with up to two fast alists without having to hash to find the backing ; hash tables. ; ESSAY ON CTRL+C SAFETY FOR FAST ALISTS ; ; Ctrl+C safety is really difficult for fast alists. The function ; hl-hons-acons provides the simplest example of the problem. You might think ; that the PROGN in this function should be a without-interrupts instead. ; After all, an ill-timed interrupt by the user could cause us to remove the ; old hash table from FALTABLE without installing the new hash table, ; potentially leading to discipline failures even in otherwise perfectly ; disciplined user-level code. ; ; But the problem runs deeper than this. Even if we used without-interrupts, ; it wouldn't be enough. After all, an equally bad scenario is that we ; successfully install the new table into FALTABLE, but then are interrupted ; before ANS can be returned to the user's code. It hardly matters that the ; hash table has been properly installed if they don't have the new handle to ; it. ; ; There really isn't any way for us, in the implementation of fast alists, to ; prevent interrupts from violating single-threaded discipline. Consider for ; instance a sequence such as: ; ; (defconst *foo* (make-fast-alist ...)) ; (defconst *bar* (do-something (hons-acons 0 t *foo*))) ; ; Here, if the user interrupts do-something at any time after the inner ; hons-acons has been executed, then the hash table for *foo* has already been ; extended and there's no practical way to maintain discipline. ; ; Because of this, we abandon the goal of trying to maintain discipline across ; interrupts, and set upon a much easier goal of ensuring that whatever hash ; tables happen to be in the FALTABLE are indeed accurate reflections of the ; alists that are bound to them. This weaker criteria means that the progn ; below is adequate. (defun hl-slow-alist-warning (name) ;; Name is the name of the function wherein we noticed a problem. (let ((action (get-slow-alist-action *the-live-state*))) (when action (format *error-output* " ***************************************************************** Fast alist discipline violated in ~a. See the documentation for fast alists for how to fix the problem, or suppress this warning message with~% ~a~% ****************************************************************~%" name '(set-slow-alist-action nil)) (when (eq action :break) (format *error-output* " To avoid the following break and get only the above warning:~% ~a~%" '(set-slow-alist-action :warning)) (break$))))) (defun hl-faltable-maphash (f faltable) (declare (type hl-faltable faltable)) ; We assume F doesn't modify faltable or any of its slots. (let ((slot1 (hl-faltable-slot1 faltable)) (slot2 (hl-faltable-slot2 faltable)) (table (hl-faltable-table faltable))) ;; Silly, just to make sure we visit each thing only once, bring everything ;; into a unique state. (unless (hl-falslot-uniquep slot1) (remhash (hl-falslot-key slot1) table) (setf (hl-falslot-uniquep slot1) t)) (unless (hl-falslot-uniquep slot2) (remhash (hl-falslot-key slot2) table) (setf (hl-falslot-uniquep slot2) t)) (when (hl-falslot-key slot1) (funcall f (hl-falslot-key slot1) (hl-falslot-val slot1))) (when (hl-falslot-key slot2) (funcall f (hl-falslot-key slot2) (hl-falslot-val slot2))) (maphash f table))) (defun hl-faltable-load-empty-slot (alist slot faltable) (declare (type hl-faltable faltable) (type hl-falslot slot)) ; SLOT[key] must be NIL. ; ; We want to load up SLOT with ALIST and its backing hash table. ALIST should ; be a cons and must not be bound in any other slot. In the case of good ; discipline, the table lookup will succeed and we will get its hash table ; loaded into val. In the case of bad discipline, both the key and val will ; become NIL. (let* ((table (hl-faltable-table faltable)) (val (gethash alist table))) (setf (hl-falslot-uniquep slot) nil) (setf (hl-falslot-val slot) val) (setf (hl-falslot-key slot) ;; Ensure KEY gets set to NIL in the case of bad discipline, so we ;; don't violate Invariant 1. (and val alist)) (remhash alist table) (setf (hl-falslot-uniquep slot) t))) (defun hl-faltable-eject (slot faltable) (declare (type hl-faltable faltable) (type hl-falslot slot)) ; We want to remove any ALIST and VAL from SLOT, and move them back into the ; main table, to free up this slot. We don't care whether SLOT is unique, ; because if it happens to be non-unique, we're going to be putting its value ; back into the table anyway. (let ((key (hl-falslot-key slot))) (when key (setf (hl-falslot-uniquep slot) nil) (setf (gethash key (hl-faltable-table faltable)) (hl-falslot-val slot)) (setf (hl-falslot-key slot) nil) (setf (hl-falslot-val slot) nil) (setf (hl-falslot-uniquep slot) t)))) (defun hl-faltable-get-free-slot (faltable) (declare (type hl-faltable faltable)) ; Choose whichever slot was least recently used and eject it. Returns an empty ; slot. We assume that your goal is to put something into the slot, so we mark ; the OTHER slot as the one to eject. (let* ((eject1 (hl-faltable-eject1 faltable)) (loser (if eject1 (hl-faltable-slot1 faltable) (hl-faltable-slot2 faltable)))) (hl-faltable-eject loser faltable) (setf (hl-faltable-eject1 faltable) (not eject1)) loser)) (defun hl-faltable-slot-lookup (alist faltable) (declare (type hl-faltable faltable)) ; ALIST should be a cons. Try to find ALIST only among the slots of FALTABLE. ; Returns a SLOT (which is guaranteed to be unique) or NIL. (let* ((slot1 (hl-faltable-slot1 faltable)) (slot (if (eq alist (hl-falslot-key slot1)) slot1 (let ((slot2 (hl-faltable-slot2 faltable))) (if (eq alist (hl-falslot-key slot2)) slot2 nil))))) (unless slot (return-from hl-faltable-slot-lookup nil)) (unless (hl-falslot-uniquep slot) ;; The slot may be duplicated in the table, so be sure to delete it and ;; then we can claim it is free. This can happen if there are interrupts ;; at just the right time during hl-faltable-eject, etc. (remhash alist (hl-faltable-table faltable)) (setf (hl-falslot-uniquep slot) t)) (setf (hl-faltable-eject1 faltable) (not (eq slot slot1))) slot)) (defun hl-faltable-general-lookup (alist faltable) (declare (type hl-faltable faltable)) ; ALIST should be a cons. Try to find ALIST first among the slots of FALTABLE; ; otherwise, eject a slot, load it into a slot, and return the slot. In any ; event, this just returns a slot that contains ALIST and is guaranteed to be ; unique. If there is a discipline failure, an empty slot is returned (i.e., ; its key and val are nil). (or (hl-faltable-slot-lookup alist faltable) (let ((slot (hl-faltable-get-free-slot faltable))) ;; The slot is empty, load it up. (hl-faltable-load-empty-slot alist slot faltable) slot))) (defun hl-faltable-remove (alist faltable) (declare (type hl-faltable faltable)) ; ALIST should be a cons. Remove ALIST from the slots or table, wherever it ; may be. We sort of optimize this so that if the alist isn't already in a ; slot, we don't ruin the slots. (let ((slot (hl-faltable-slot-lookup alist faltable))) (cond (slot ;; We know it's unique by the guarantee in hl-faltable-slot-lookup, ;; so we just need to empty this slot. (setf (hl-falslot-key slot) nil) (setf (hl-falslot-val slot) nil) ;; just a hint for gc ;; The slot-lookup set eject1 to the wrong thing since we're going ;; to delete this slot, so set it back to the right thing. (setf (hl-faltable-eject1 faltable) (not (hl-faltable-eject1 faltable)))) (t ;; No slot, so just remove it from the table; this works whether ;; it exists or not. (remhash alist (hl-faltable-table faltable)))))) (defun hl-hspace-fast-alist-free (alist hs) (declare (type hl-hspace hs)) (cond ((atom alist) alist) (t (hl-faltable-remove alist (hl-hspace-faltable hs)) alist))) (defun hl-hspace-hons-get (key alist hs) ; (HL-HSPACE-HONS-GET KEY ALIST HS) --> ANS and destructively modifies HS ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; Fundamental fast alist lookup operation. Norm the key (hence the possible ; modifications to the HS), then look it up in the backing hash table for the ; alist. (declare (type hl-hspace hs)) (if (atom alist) nil (let* ((faltable (hl-hspace-faltable hs)) (slot (hl-faltable-general-lookup alist faltable)) (val (hl-falslot-val slot))) (if val ;; Good discipline, val is the hash table, so look up the key. ;; We have to hons the key to justify EQL hashing. (values (gethash (hl-hspace-norm key hs) val)) ;; Bad discipline, val is just nil and hence is unusable, look ;; up the key slowly in the alist. (progn (hl-slow-alist-warning 'hl-hspace-hons-get) (hons-assoc-equal key alist)))))) (defun hl-hspace-hons-acons (key value alist hs) ; (HL-HSPACE-HONS-ACONS KEY VALUE ALIST HS) --> ALIST' and destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; - KEY and VALUE are any ACL2 Objects, whether normed or not. ; ; - ALIST is an ordinary ACL2 Object; for good discipline ALIST must have a ; hash table supporting it in the FAL-HT. ; ; - HS is the Hons Space whose FAL-HT and other fields may be destructively ; updated. ; ; When ALIST is a natural number, we interpret it as a size hint that says how ; large the new hash table should be, but we impose a minimum of 60 elements. ; We always begin by honsing the key, which justifies our use of EQL hash ; tables. (declare (type hl-hspace hs)) (let* (;; The key must always normed regardless of honsp. (key (hl-hspace-norm key hs)) (entry (cons key value)) (ans (cons entry alist)) (faltable (hl-hspace-faltable hs))) (if (atom alist) ;; New fast alist. Try to use the size hint if one was provided. (let* ((size (if (and (typep alist 'fixnum) (<= 60 (the fixnum alist))) alist 60)) (tab (hl-mht :size size)) (slot (hl-faltable-get-free-slot faltable))) (setf (gethash key (the hash-table tab)) entry) ;; We know the slot is empty and unique, just install the new ;; key/value pair. We install the key last so that the slot ;; still looks empty for as long as possible. (setf (hl-falslot-val slot) tab) (setf (hl-falslot-key slot) ans)) ;; Existing fast alist. (let* ((slot (hl-faltable-general-lookup alist faltable)) (val (hl-falslot-val slot))) (if (not val) ;; Discipline failure, no valid backing alist. (hl-slow-alist-warning 'hl-hspace-hons-acons) (progn ;; We temporarily set the KEY to nil to break the old association ;; from ALIST to VAL. Then, install the new entry into the VAL, ;; and finally set KEY to ANS so that the new association is ;; created. (setf (hl-falslot-key slot) nil) (setf (gethash key (the hash-table val)) entry) (setf (hl-falslot-key slot) ans))))) ans)) (defun hl-alist-stolen-warning (name) ;; Name is the name of the function wherein we noticed a problem. (let ((action (get-slow-alist-action *the-live-state*))) (when action (format *error-output* " ***************************************************************** Fast alist stolen by ~a. See the documentation for fast alists for how to fix the problem, or suppress this warning message with~% ~a~% ****************************************************************~%" name '(set-slow-alist-action nil)) (when (eq action :break) (format *error-output* " To avoid the following break and get only the above warning:~% ~a~%" '(set-slow-alist-action :warning)) (break$))))) (defun hl-hspace-hons-acons! (key value alist hs) ; (HL-HSPACE-HONS-ACONS! KEY VALUE ALIST HS) --> ALIST' and destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; Like HL-HSPACE-HONS-ACONS, except honses the ANS alist as well. This is ; subtle because the ANS we create might already exist! (declare (type hl-hspace hs)) (let* ((key (hl-hspace-norm key hs)) (entry (hl-hspace-hons key value hs)) (ans (hl-hspace-hons entry alist hs)) (faltable (hl-hspace-faltable hs))) (let ((slot (hl-faltable-general-lookup ans faltable))) (when (hl-falslot-key slot) ;; "Inadvertent" hash table stealing. We now print a warning before ;; removing the old binding. (hl-alist-stolen-warning 'hons-acons!) ;; We could do something smart to reuse this alist, but this is a bad ;; case anyway and we don't really expect it to happen much. (setf (hl-falslot-key slot) nil) (setf (hl-falslot-val slot) nil))) (if (atom alist) ;; New fast alist. (let* ((size (if (and (typep alist 'fixnum) (<= 60 (the fixnum alist))) alist 60)) (tab (hl-mht :size size)) (slot (hl-faltable-get-free-slot faltable))) (setf (gethash key (the hash-table tab)) entry) (setf (hl-falslot-val slot) tab) (setf (hl-falslot-key slot) ans)) ;; Existing fast alist. (let* ((slot (hl-faltable-general-lookup alist faltable)) (val (hl-falslot-val slot))) (if (not val) (hl-slow-alist-warning 'hl-hspace-hons-acons) (progn (setf (hl-falslot-key slot) nil) (setf (gethash key (the hash-table val)) entry) (setf (hl-falslot-key slot) ans))))) ans)) (defun hl-alist-longest-normed-tail (alist hs) ; (HL-ALIST-LONGEST-NORMED-TAIL ALIST HS) --> TAIL ; ; ALIST is an arbitrary ACL2 object. This returns the longest tail of ALIST ; where all the keys are already normed. This tail doesn't need to be reconsed ; when we go to make a fast version of ALIST. (declare (type hl-hspace hs)) (let ((ok-tail alist)) ;; ok-tail is a tail of alist on which we haven't yet found any unnormed keys. (loop for tail on alist while (consp tail) do (let ((pair (car tail))) ;; We can just skip over any non-conses since they don't contribute ;; to the alist. (when (and (consp pair) (not (hl-hspace-normedp (car pair) hs))) (setq ok-tail (cdr tail))))) ok-tail)) (defun hl-make-fast-norm-keys (alist tail hs) ; (HL-MAKE-FAST-NORM-KEYS ALIST TAIL HS) --> ALIST' and destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; ALIST is an arbitrary ACL2 object. TAIL is its longest-normed-tail. We ; construct a new ALIST that is EQUAL to ALIST, where all the keys are normed. (declare (type hl-hspace hs)) (if (eq tail alist) alist (let* ((first-cons (list nil)) (last-cons first-cons)) (loop for rest on alist while (and (consp rest) (not (eq rest tail))) do (let* ((pair (car rest)) (cons (list (if (consp pair) (cons (hl-hspace-norm (car pair) hs) (cdr pair)) pair)))) (setf (cdr last-cons) cons) (setq last-cons cons))) (setf (cdr last-cons) tail) (cdr first-cons)))) (defun hl-make-fast-alist-put-pairs (alist ht) ; (HL-MAKE-FAST-ALIST-PUT-PAIRS ALIST HT) --> HT'. ; ; ALIST must have normed keys. Assuming that HT starts empty, we initialize it ; to have the correct values for ALIST. That is, we set HT[KEY] := VALUE for ; each (KEY . VALUE) pair in ALIST, except that we don't do this update when ; HT[KEY] is already bound, i.e., we properly skip shadowed pairs. (declare (type hash-table ht)) (loop for rest on alist while (consp rest) do (let ((pair (car rest))) (when (and (consp pair) (not (gethash (car pair) ht))) (setf (gethash (car pair) ht) pair))))) (defun hl-hspace-make-fast-alist (alist hs) ; (HL-HSPACE-MAKE-FAST-ALIST ALIST HS) --> ALIST' and destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; This function returns a fast ALIST' which is EQUAL to ALIST. Ideally ALIST' ; can just be ALIST, but this is sometimes not possible when ALIST' has keys ; that are not normed. If ALIST is already fast and already has a backing hash ; table, we just return it. Otherwise we build a hash table for it. (declare (type hl-hspace hs)) (if (atom alist) ;; Can't create a hash table for an atom, nothing to do. alist (let* ((faltable (hl-hspace-faltable hs)) (slot (hl-faltable-general-lookup alist faltable)) (alist-table (hl-falslot-val slot))) (if alist-table ;; Already has an associated hash table, nothing to do. alist (let* (;; Find the largest tail of alist in which all keys are normed. (tail (hl-alist-longest-normed-tail alist hs)) ;; Makes a copy of alist in which all keys are normed. (alist (hl-make-fast-norm-keys alist tail hs))) ;; We need to make a new hash table to back ALIST. As in ;; hl-hspace-shrink-alist, we choose a size of (max 60 (* 1/8 ;; length)). (setq alist-table (hl-mht :size (max 60 (ash (len alist) -3)))) (hl-make-fast-alist-put-pairs alist alist-table) ;; The slot is empty, so install everything. Since the value wasn't ;; found, the initial ALIST isn't bound; if we ended up making a new ;; alist due to honsing any keys, it's also not bound because we used ;; cons. So, uniqueness is guaranteed. And we already know from the ;; general lookup that it is unique. (setf (hl-falslot-val slot) alist-table) (setf (hl-falslot-key slot) alist) alist))))) ; (HL-HSPACE-SHRINK-ALIST ALIST ANS HONSP HS) --> ANS' and destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; ALIST is either a fast or slow alist, and ANS should be a fast alist. HONSP ; says whether our extension of ANS' should be made with honses or conses. HS ; is a Hons Space and will be destructively modified. (defun hl-shrink-alist-aux-really-slow (alist ans honsp hs) ;; This is our function of last resort and we only call it if discipline has ;; failed. We don't try to produce a fast alist, because there may not even ;; be a valid way to produce one that corresponds to the logical definition ;; and satisfies the FALTABLE invariants. (cond ((atom alist) ans) ((atom (car alist)) (hl-shrink-alist-aux-really-slow (cdr alist) ans honsp hs)) (t (let* ((key (hl-hspace-norm (caar alist) hs)) (entry (hons-assoc-equal key ans))) (unless entry (if honsp (progn (setq entry (hl-hspace-hons key (cdar alist) hs)) (setq ans (hl-hspace-hons entry ans hs))) (progn (setq entry (cons key (cdar alist))) (setq ans (cons entry ans))))) (hl-shrink-alist-aux-really-slow (cdr alist) ans honsp hs))))) (defun hl-shrink-alist-aux-slow (alist ans table honsp hs) ;; This is somewhat slower than the -fast version, because we don't assume ;; ALIST has normed keys. This is the function we'll use when shrinking an ;; ordinary alist with an existing fast alist or with an atom as the ANS. (declare (type hl-hspace hs) (type hash-table table)) (cond ((atom alist) ans) ((atom (car alist)) (hl-shrink-alist-aux-slow (cdr alist) ans table honsp hs)) (t (let* ((key (hl-hspace-norm (caar alist) hs)) (entry (gethash key table))) (unless entry (if honsp (progn (setq entry (hl-hspace-hons key (cdar alist) hs)) (setq ans (hl-hspace-hons entry ans hs)) (setf (gethash key table) entry)) (progn ;; We recons the entry so the resulting alist has normed keys. (setq entry (cons key (cdar alist))) (setq ans (cons entry ans)) (setf (gethash key table) entry)))) (hl-shrink-alist-aux-slow (cdr alist) ans table honsp hs))))) (defun hl-shrink-alist-aux-fast (alist ans table honsp hs) ;; This is faster than the -slow version because we assume ALIST is has ;; normed keys. This is the function we use to merge two fast alists. (declare (type hl-hspace hs) (type hash-table table)) (cond ((atom alist) ans) ((atom (car alist)) (hl-shrink-alist-aux-fast (cdr alist) ans table honsp hs)) (t (let* ((key (caar alist)) (entry (gethash key table))) (unless entry (if honsp (progn (setq entry (hl-hspace-hons key (cdar alist) hs)) (setq ans (hl-hspace-hons entry ans hs)) (setf (gethash key table) entry)) (progn (setq entry (car alist)) (setq ans (cons entry ans)) (setf (gethash key table) entry)))) (hl-shrink-alist-aux-fast (cdr alist) ans table honsp hs))))) ; If ANS is an atom, we are going to create a new hash table for the result. ; What size should we use? If ALIST is a fast alist, we can see how large its ; table is and use the same size. Otherwise, if ALIST is an ordinary alist, ; it's more difficult to estimate how large the table ought to be; we guess ; a hashtable size that is the maximum of 60 and 1/8 the length of ALIST. (defun hl-hspace-shrink-alist (alist ans honsp hs) (declare (type hl-hspace hs)) (if (atom alist) ans (let* ((faltable (hl-hspace-faltable hs)) (alist-table ;; We see if ALIST has a backing hash table only so that we can use ;; it as a size hint and know whether its keys are honsed. (let ((slot (hl-faltable-general-lookup alist faltable))) (hl-falslot-val slot))) (ans-slot (if (atom ans) (hl-faltable-get-free-slot faltable) (hl-faltable-general-lookup ans faltable))) (ans-table ;; Get the table if it already exists, or build a new one if the ;; ans is an atom. (if (atom ans) ;; Make a new hash table for ANS, with our size guess. (hl-mht :size (cond ((natp ans) (max 60 ans)) (alist-table ;; CHANGE -- this used to be based on ;; hash-table-count (hash-table-size (the hash-table alist-table))) (t (max 60 (ash (len alist) -3))))) ;; Reuse the existing table (hl-falslot-val ans-slot)))) ;; Disassociate the ANS alist if it exists so we can modify its table ;; without regards to interrupts (setf (hl-falslot-key ans-slot) nil) (unless ans-table ;; Bad discipline. ANS is not an atom or fast alist. (hl-slow-alist-warning 'hl-hspace-shrink-alist) (return-from hl-hspace-shrink-alist (hl-shrink-alist-aux-really-slow alist ans honsp hs))) ;; Good discipline. Shove ALIST into ANS-TABLE. (let ((new-alist ;; If ALIST is fast, then by the FAL-HT invariants we know it is a ;; proper cons list and already has normed keys, so we can use the ;; fast version. Else, we can't make these assumptions, and have ;; to use the slow one. (if alist-table (hl-shrink-alist-aux-fast alist ans ans-table honsp hs) (hl-shrink-alist-aux-slow alist ans ans-table honsp hs)))) (when honsp (setq ans-slot (hl-faltable-general-lookup new-alist faltable)) (when (hl-falslot-key ans-slot) ;; "Inadvertent" hash table stealing. We now print a warning ;; before removing the old binding. (hl-alist-stolen-warning 'hons-shrink-alist!) ;; This slot already has the right key, and must have the right ;; value, too. We've already disassociated the old alist. So ;; we're done. (return-from hl-hspace-shrink-alist new-alist))) (unless (atom new-alist) ;; Tricky subtle thing. If ALIST was a list of atoms, and ANS is an ;; atom, then what we arrive at is still an atom. We don't want any ;; atoms bound in the fal-ht, so don't bind it. (setf (hl-falslot-val ans-slot) ans-table) (setf (hl-falslot-key ans-slot) new-alist)) new-alist)))) (defun hl-hspace-fast-alist-len (alist hs) (declare (type hl-hspace hs)) (if (atom alist) 0 (let* ((faltable (hl-hspace-faltable hs)) (slot (hl-faltable-general-lookup alist faltable)) (val (hl-falslot-val slot))) ;; In the case of good discipline, the slot's key/value are set properly, ;; otherwise they are both nil. (if val (hash-table-count val) (progn (hl-slow-alist-warning 'hl-hspace-fast-alist-len) (let* ((fast-alist (hl-hspace-shrink-alist alist nil nil hs)) (result (hl-hspace-fast-alist-len fast-alist hs))) (hl-hspace-fast-alist-free fast-alist hs) result)))))) (defun hl-check-alist-for-serialize-restore (alist hs) ; Causes an error unless every key of ALIST is normed. (declare (type hl-hspace hs)) (cond ((atom alist) nil) ((atom (car alist)) (hl-check-alist-for-serialize-restore (cdr alist) hs)) ((not (hl-hspace-normedp (caar alist) hs)) (error "Can't restore an alist from the serialized file since it has ~ a key that was not re-honsed.~% ~ - Problematic key: ~S~% ~ - Tail of alist: ~S~%" (caar alist) alist)) (t (hl-check-alist-for-serialize-restore (cdr alist) hs)))) (defun hl-hspace-restore-fal-for-serialize (alist count hs) ; (HL-HSPACE-RESTORE-FAL-FOR-SERIALIZE ALIST COUNT HS) destructively modifies HS. ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! ; ; ALIST should have just been read from a serialized object, and was marked as ; a fast alist in a previous ACL2 session. COUNT was its count in the previous ; session, which we will use as its initial size. ; ; If ALIST has any non-honsed keys it is an error, and we check for this case. ; If ALIST already has a hash table, it is a discipline failure. This could ; perhaps happen due to hons-acons! like stealing, when ALIST is itself a hons. (declare (type hl-hspace hs)) (let* ((faltable (hl-hspace-faltable hs)) (slot (hl-faltable-general-lookup alist faltable)) (new-ht (hl-mht :size (max 60 count)))) (hl-check-alist-for-serialize-restore alist hs) (hl-make-fast-alist-put-pairs alist new-ht) (when (hl-falslot-val slot) ;; BOZO how much of an error is this? Do we want to warn about it? (hl-alist-stolen-warning 'hl-hspace-restore-fal-for-serialize)) (setf (hl-falslot-val slot) new-ht) (setf (hl-falslot-key slot) alist))) (defun hl-restore-fal-for-serialize (alist count) ;; Bootstrapping hack for serialize ;; Assumes *default-hs* is already initialized (declare (special *default-hs*)) (hl-hspace-restore-fal-for-serialize alist count *default-hs*)) ; CHANGE -- increased size of number-subtrees-ht to start at 10,000. BOZO ; think about making this higher, or using a more aggressive rehashing size? (defun hl-hspace-number-subtrees-aux (x seen) (declare (type hash-table seen)) (cond ((atom x) nil) ((gethash x seen) nil) (t (progn (setf (gethash x seen) t) (hl-hspace-number-subtrees-aux (car x) seen) (hl-hspace-number-subtrees-aux (cdr x) seen))))) (defun hl-hspace-number-subtrees (x hs) ; ** may install a new ADDR-HT, SBITS ; ** callers should not have ADDR-HT or SBITS let-bound! (declare (type hl-hspace hs)) (let ((x (hl-hspace-norm x hs)) (seen (hl-mht :test 'eq :size 10000))) (hl-hspace-number-subtrees-aux x seen) (hash-table-count seen))) ; ---------------------------------------------------------------------- ; ; CLEARING THE HONS SPACE ; ; ---------------------------------------------------------------------- ; This is a barbaric garbage collection mechanism that can be used in Classic ; or Static honsing. ; ; The idea is to throw away all of the honses in the Hons Space, except that we ; then want to restore any "persistent" honses and fast alist keys (so that ; fast alists don't become slow). ; ; It is generally better to use HONS-WASH instead, but that only works in ; Static Honsing. (defun hl-system-gc () ;; Note that ccl::gc only schedules a GC to happen. So, we need to both ;; trigger one and wait for it to occur. #+Clozure (let ((current-gcs (ccl::full-gccount))) (ccl::gc) (loop do (progn (when (> (ccl::full-gccount) current-gcs) (loop-finish)) (format (get-output-stream-from-channel *standard-co*) "; Hons-Note: Waiting for GC to finish.~%") (finish-output) (sleep 1)))) #-Clozure (gc$)) #-static-hons (defun hl-hspace-classic-restore (x nil-ht cdr-ht cdr-ht-eql seen-ht) ; Returns X and destructively updates the other arguments. ; ; Classic honsing only. This function is used to restore any persistent honses ; after clearing them. ; ; X is an ACL2 Object that we need to recursively reinstall. We assume that X ; was previously normed, so it never contains non-EQL versions of any objects. ; We also assume that all of the strings in X are still normed. ; ; SEEN-HT is a hash table that says which conses we have already reinstalled. ; ; The other arguments are the correspondingly named fields in the hons space, ; which we assume are detatched from any hons space. Because of this, we do ; not need to worry about interrupts and can freely update the fields in an ; order that violates the usual hons space invariants. (declare (type hash-table nil-ht) (type hash-table cdr-ht) (type hash-table cdr-ht-eql) (type hash-table seen-ht)) (cond ((atom x) ;; Nothing to do because we assume all atoms have already been ;; installed. x) ((gethash x seen-ht) ;; Nothing to do because we have already reinstalled X. x) (t (let* ((a (hl-hspace-classic-restore (car x) nil-ht cdr-ht cdr-ht-eql seen-ht)) (b (hl-hspace-classic-restore (cdr x) nil-ht cdr-ht cdr-ht-eql seen-ht))) (setf (gethash x seen-ht) t) ;; Mark X as seen. (if (eq b nil) (setf (gethash a nil-ht) x) (let* ((main-table (if (or (consp b) (symbolp b) (typep b 'array)) ;; (stringp b) cdr-ht cdr-ht-eql)) (flex-alist (gethash b main-table)) (was-alistp (listp flex-alist)) (new-flex-alist (hl-flex-acons x flex-alist))) ;; If was-alistp, then the flex-acons was applicative and we ;; have to install the new flex alist. Otherwise, it's already ;; installed. (when was-alistp (setf (gethash b main-table) new-flex-alist)) x)))))) #-static-hons (defun hl-hspace-hons-clear (gc hs) (declare (type hl-hspace hs)) (let* ((ctables (hl-hspace-ctables hs)) (nil-ht (hl-ctables-nil-ht ctables)) (cdr-ht (hl-ctables-cdr-ht ctables)) (cdr-ht-eql (hl-ctables-cdr-ht-eql ctables)) (faltable (hl-hspace-faltable hs)) (persist-ht (hl-hspace-persist-ht hs)) (norm-cache (hl-hspace-norm-cache hs)) (temp-nil-ht (hl-mht :test #'eql)) (temp-cdr-ht (hl-mht :test #'eq)) (temp-cdr-ht-eql (hl-mht :test #'eql)) (temp-ctables (make-hl-ctables :nil-ht temp-nil-ht :cdr-ht temp-cdr-ht :cdr-ht-eql temp-cdr-ht-eql)) (temp-faltable (hl-faltable-init)) (temp-persist-ht (hl-mht :test #'eq)) (seen-ht (hl-mht :test #'eq :size 10000)) (note-stream (get-output-stream-from-channel *standard-co*))) ;; Very subtle. We're about to violate invariants, so we need to clear out ;; the hons space while we work. Because we aggregated the ctables into a ;; single field, we can 'uninstall' the NIL-HT, CDR-HT, and CDR-HT-EQL all ;; together with a single setf. This gives us Ctrl+C safety and means all ;; our invariants are preserved. ;; Order here is important. We cannot clear ctables before norm-memo-ht, ;; because then we'd have stale allegedly-normed conses in the memo table. ;; Similarly we need to clear the fal-ht and persist-ht before the ctables, ;; or an interrupt might leave us with stale allegedly normed conses in ;; those tables. (hl-cache-clear norm-cache) (setf (hl-hspace-faltable hs) temp-faltable) (setf (hl-hspace-persist-ht hs) temp-persist-ht) (setf (hl-hspace-ctables hs) temp-ctables) (format note-stream "; Hons-Note: clearing normed objects.~%") (clrhash nil-ht) (clrhash cdr-ht) (clrhash cdr-ht-eql) (when gc (hl-system-gc)) (format note-stream "; Hons-Note: re-norming persistently normed objects.~%") (maphash (lambda (key val) (declare (ignore val)) (hl-hspace-classic-restore key nil-ht cdr-ht cdr-ht-eql seen-ht)) persist-ht) (format note-stream "; Hons-Note: re-norming fast alist keys.~%") ;; BOZO we probably want to loop over the alist, rather than the associated ;; hash table, to avoid the maphash overhead (hl-faltable-maphash (lambda (alist associated-hash-table) (declare (ignore alist)) (maphash (lambda (key val) (declare (ignore val)) (hl-hspace-classic-restore key nil-ht cdr-ht cdr-ht-eql seen-ht)) associated-hash-table)) faltable) (format note-stream "; Hons-Note: finished re-norming ~a conses.~%" (hash-table-count seen-ht)) ;; Again order is critical. Ctables must be installed before fal-ht or ;; persist-ht, since parts of fal-ht and persist-ht are expected to be ;; normed. (setf (hl-hspace-ctables hs) ctables) (setf (hl-hspace-faltable hs) faltable) (setf (hl-hspace-persist-ht hs) persist-ht)) nil) #+static-hons (defun hl-hspace-static-restore (x addr-ht sbits str-ht other-ht) ; Returns X and destructively modifies ADDR-HT and SBITS. ; ; Static honsing only. This function is used to restore any persistent honses ; after clearing them. ; ; X is an ACL2 Object that we need to recursively reinstall. We assume that X ; was previously normed (so it never contains non-EQL versions of any objects.) ; We assume that all of the atoms in X are still normed and have addresses. ; ; The other fields are the corresponding fields from a Hons Space, but we ; assume they are detatched from any Hons Space and do not need to be updated ; in a manner that maintains their invariants in the face of interrupts. ; ; Note that we don't bother to do anything about the ADDR-LIMIT in this ; function; we basically assume the ADDR-HT is big enough that it isn't going ; to need resizing, and that the caller will fix up the ADDR-LIMIT after doing ; all of the necessary restoration. (declare (type hash-table addr-ht) (type (simple-array bit (*)) sbits)) (if (atom x) ;; Nothing to do because we assume all atoms have already been ;; installed. x (let ((index (hl-staticp x))) (if (= (aref sbits index) 1) ;; Nothing to do; we've already reinstalled X. x (let* ((a (hl-hspace-static-restore (car x) addr-ht sbits str-ht other-ht)) (b (hl-hspace-static-restore (cdr x) addr-ht sbits str-ht other-ht)) (addr-a (hl-addr-of a str-ht other-ht)) (addr-b (hl-addr-of b str-ht other-ht)) (key (hl-addr-combine* addr-a addr-b))) (setf (aref sbits index) 1) (setf (gethash key addr-ht) x) x))))) #+static-hons (defun hl-hspace-hons-clear (gc hs) (declare (type hl-hspace hs)) (let* ((addr-ht (hl-hspace-addr-ht hs)) (sbits (hl-hspace-sbits hs)) (sbits-len (length sbits)) (faltable (hl-hspace-faltable hs)) (persist-ht (hl-hspace-persist-ht hs)) (str-ht (hl-hspace-str-ht hs)) (other-ht (hl-hspace-other-ht hs)) (norm-cache (hl-hspace-norm-cache hs)) (temp-faltable (hl-faltable-init)) (temp-persist-ht (hl-mht :test #'eq)) (temp-addr-ht (hl-mht :test #'eql)) (temp-sbits (make-array 1 :element-type 'bit :initial-element 0)) (note-stream (get-output-stream-from-channel *standard-co*))) ;; Very subtle. We're about to violate invariants, so we need to clear out ;; the hons space while we work. ;; See also the classic version; order matters, you can't clear out addr-ht ;; and sbits before the other tables. (hl-cache-clear norm-cache) (setf (hl-hspace-faltable hs) temp-faltable) (setf (hl-hspace-persist-ht hs) temp-persist-ht) (hl-without-interrupts (setf (hl-hspace-addr-ht hs) temp-addr-ht) (setf (hl-hspace-sbits hs) temp-sbits)) (format note-stream "; Hons-Note: clearing normed objects.~%") (clrhash addr-ht) (loop for i fixnum below sbits-len do (setf (aref sbits i) 0)) (when gc (hl-system-gc)) (time$ (maphash (lambda (key val) (declare (ignore val)) (hl-hspace-static-restore key addr-ht sbits str-ht other-ht)) persist-ht) :msg "; Hons-Note: re-norm persistents: ~st seconds, ~sa bytes.~%") ;; BOZO we probably want to loop over the alist, rather than the associated ;; hash table, to avoid the maphash overhead (time$ (hl-faltable-maphash (lambda (alist associated-hash-table) (declare (ignore alist)) (maphash (lambda (key val) (declare (ignore val)) (hl-hspace-static-restore key addr-ht sbits str-ht other-ht)) associated-hash-table)) faltable) :msg "; Hons-Note: re-norm fal keys: ~st seconds, ~sa bytes.~%") (format note-stream "; Hons-Note: finished re-norming ~:D conses.~%" (hash-table-count addr-ht)) ;; Order matters, reinstall addr-ht and sbits before fal-ht and persist-ht! (hl-without-interrupts (setf (hl-hspace-addr-ht hs) addr-ht) (setf (hl-hspace-sbits hs) sbits)) (setf (hl-hspace-faltable hs) faltable) (setf (hl-hspace-persist-ht hs) persist-ht) ;; If an interrupt stops us before here, that's fine; there's no soundness ;; burden with the ADDR-LIMIT. (hl-make-addr-limit-current hs)) nil) ; ---------------------------------------------------------------------- ; ; WASHING A HONS SPACE (Static Honsing Only) ; ; ---------------------------------------------------------------------- ; BOZO thread unsafe. It is probably not okay to use this function in a ; multi-threaded environment. In particular, another thread could create a ; static cons while we were restoring conses, and we'd think it had survived ; the garbage collection. So, to make this thread safe we would need to add a ; locking mechanism to prevent access to HONS during the restore. Actually we ; would only need something like (with-lock (gc) (fix-sbits)). We haven't ; added this locking yet since it would slow down honsing. But this might be a ; good argument for using a truly shared hons space. #+static-hons (defun hl-fix-sbits-after-gc (sbits) ; (HL-FIX-SBITS-AFTER-GC SBITS) destructively modifies SBITS and counts up how ; many normed conses survived the garbage collection. Each normed cons that ; did not survive has its entry zeroed out. We return the number of 1 bits in ; the updated SBITS, i.e., the number of normed conses that survived. (declare (type (simple-array bit (*)) sbits)) (let ((num-survivors 0) (max-index (length sbits))) (declare (fixnum max-index) (fixnum num-survivors)) (loop for i fixnum below max-index do (when (= (aref sbits i) 1) (let ((object (hl-static-inverse-cons i))) (if object (incf num-survivors) (setf (aref sbits i) 0))))) num-survivors)) #+static-hons (defun hl-rebuild-addr-ht (sbits addr-ht str-ht other-ht) ; (HL-REBUILD-ADDR-HT SBITS ADDR-HT STR-HT OTHER-HT) destructively modifies ; ADDR-HT. ; ; This is a subtle function which is really the key to washing. We assume that ; SBITS has already been fixed up so that only survivors are marked. We assume ; that ADDR-HT is empty to begin with. We walk over the SBITS and install each ; survivor into its proper place in the ADDR-HT. ; ; The STR-HT and OTHER-HT are only needed for address computations. (declare (type (simple-array bit (*)) sbits) (type hash-table addr-ht)) (let ((max-index (length sbits))) (declare (fixnum max-index)) (loop for i fixnum below max-index do (when (= (aref sbits i) 1) ;; This object was previously normed. (let ((object (hl-static-inverse-cons i))) (cond ((not object) (error "Expected SBITS to already be fixed up.")) (t (let* ((a (car object)) (b (cdr object)) ;; It might be that A or B are not actually ;; normed. So why is it okay to call hl-addr-of? ;; It turns out to be okay. In the atom case, ;; nothing has changed. In the cons case, the ;; address calculation only depends on the static ;; index of a and b, which hasn't changed. (addr-a (hl-addr-of a str-ht other-ht)) (addr-b (hl-addr-of b str-ht other-ht)) (key (hl-addr-combine* addr-a addr-b))) (setf (gethash key addr-ht) object))))))))) #+static-hons (defparameter *hl-addr-ht-resize-cutoff* ;; After we've GC'd the honses in hons-wash, we will look at how many honses ;; survived. If there are fewer than OLD-SIZE * THRESHOLD honses surviving, ;; we'll stay with the old size. Otherwise, we'll allocate a new hash table ;; with room for more entries. This parameter could probably use some ;; tuning. 0.75) (defun hl-hspace-hons-wash (hs) ; (HL-HSPACE-HONS-WASH HS) --> NIL and destructively modifies HS ; ; We try to GC normed honses but we do not try to GC normed atoms that might be ; unreachable except for their entries in the STR-HT and OTHER-HT. We have ; considered how we might extend this function to also collect these atoms, but ; have concluded it probably isn't worth doing. Basically, we would need to ; separately record the indexes of the static conses in these tables, which is ; fine but would require us to allocate some memory. ; ; (BOZO it might be possible to make STR-HT and OTHER-HT weak, but I haven't ; really thought it all the way through.) ; ; IMPORTANT OBLIGATIONS OF THE CALLER. ; ; For this to work practically work correctly, it is critical that the ADDR-HT ; is not LET-bound anywhere in the call stack above this function. We want ; setting (hl-hspace-addr-ht hs) to NIL to be sufficient to make the ADDR-HT ; unreachable, so that it can be GC'd. ; ; After adding the ADDR-LIMIT code, this function can be called by any ordinary ; hons-creating operation such as HL-HSPACE-HONS, HL-HSPACE-HONS-NORM, etc. ; This raises some subtle issues, because you can imagine "ordinary" ; implementation level code that looks like this: ; ; (let ((addr-ht (hl-hspace-addr-ht hs)) ; (fal-ht (hl-hspace-fal-ht hs)) ; (... (... (hl-hspace-hons ...))) ; (... (foo addr-ht fal-ht))) ; ...) ; ; It would be a very bad error to write something like this: one can no longer ; assume that ADDR-HT is the same after a hons-creating operation. (This has ; also always been true of SBITS: that is, calling any hons-creating operation ; might alter SBITS). ; ; What about the other Hons Space fields? Except for the ADDR-HT and SBITS, ; the other fields like the FAL-HT, PERSIST-HT, NORM-CACHE, etc. are all ; restored at the end of a wash, so there's no problem with let-binding them ; and assuming they are the same. It is only the ADDR-HT and SBITS that we ; must be cautious with. (declare (type hl-hspace hs)) #-static-hons (declare (ignore hs)) #-static-hons (format t "; Hons-Note: washing is not available for classic honsing.~%") #+static-hons (let* (;; Note: do not bind ADDR-HT here, we want it to get GC'd. (addr-ht-size (hash-table-size (hl-hspace-addr-ht hs))) (addr-ht-count (hash-table-count (hl-hspace-addr-ht hs))) (addr-ht-rehash-size (hash-table-rehash-size (hl-hspace-addr-ht hs))) (addr-ht-rehash-threshold (hash-table-rehash-threshold (hl-hspace-addr-ht hs))) (str-ht (hl-hspace-str-ht hs)) (sbits (hl-hspace-sbits hs)) (other-ht (hl-hspace-other-ht hs)) (faltable (hl-hspace-faltable hs)) (persist-ht (hl-hspace-persist-ht hs)) (norm-cache (hl-hspace-norm-cache hs)) (temp-faltable (hl-faltable-init)) (temp-addr-ht (hl-mht :test #'eql)) (temp-sbits (make-array 1 :element-type 'bit :initial-element 0)) (temp-persist-ht (hl-mht :test #'eq)) (note-stream (get-output-stream-from-channel *standard-co*))) (format note-stream "; Hons-Note: washing ADDR-HT, ~:D used of ~:D slots.~%" addr-ht-count addr-ht-size) (force-output note-stream) ;; Clear the memo table since it might prevent conses from being garbage ;; collected and it's unsound to leave it as the sbits/addr-ht are cleared. (hl-cache-clear norm-cache) ;; We need to remove SBITS, FAL-HT, and ADDR-HT from HS before continuing, ;; so that if a user interrupts they merely end up with a mostly empty hons ;; space instead of an invalid one. Note that nothing we're about to do ;; invalidates the STR-HT or OTHER-HT, so we leave them alone. (setf (hl-hspace-faltable hs) temp-faltable) (setf (hl-hspace-persist-ht hs) temp-persist-ht) (hl-without-interrupts ;; These two must be done together or not at all. (setf (hl-hspace-addr-ht hs) temp-addr-ht) (setf (hl-hspace-sbits hs) temp-sbits)) ;; At this point, we can do anything we want with FAL-HT, ADDR-HT, and ;; SBITS, because they are no longer part of a Hons Space. ;; ;; Historically, at this point we did: (CLRHASH ADDR-HT) so that conses ;; within it could be garbage collected, explicitly trigger a GC, then ;; "magically" restore the surviving conses using the static-inverse-cons ;; trick (see HL-REBUILD-ADDR-HT). ;; ;; But when we added the ADDR-LIMIT stuff, we realized that this would be ;; the perfect place to grow the ADDR-HT ourselves instead of allowing the ;; Lisp to do it. Here, we can exploit the magic of static-inverse-cons to ;; avoid having to have both the old and new ADDR-HT existing at the same ;; time. So, now we don't CLRHASH the ADDR-HT. We've already overridden ;; the pointer to it inside the hons space. Unless someone else has it ;; bound (which they shouldn't), it can now be GC'd. (hl-system-gc) ;; Now we fix up the SBITS array by zeroing out any conses that got GC'd, ;; and in the process we count how many survivors there are. This will let ;; us make a good decision about resizing the ADDR-HT. If it would still ;; be 75% full (or, really, whatever the *hl-addr-ht-resize-cutoff* says), ;; we'll make the new table bigger. (let* ((num-survivors (hl-fix-sbits-after-gc sbits)) (pct-full (/ (coerce num-survivors 'float) (coerce addr-ht-size 'float))) (addr-ht nil)) (when (> pct-full *hl-addr-ht-resize-cutoff*) (setq addr-ht-size (floor (* addr-ht-size addr-ht-rehash-size)))) (format note-stream "; Hons-Note: Making new ADDR-HT with size ~:D~%" addr-ht-size) (force-output note-stream) ;; This can take several seconds... (setq addr-ht (hl-mht :test #'eql :size addr-ht-size :rehash-size addr-ht-rehash-size :rehash-threshold addr-ht-rehash-threshold)) (format note-stream "; Hons-Note: Restoring ~:D conses~%" num-survivors) (force-output note-stream) ;; This can take hundreds of seconds... (hl-rebuild-addr-ht sbits addr-ht str-ht other-ht) (format note-stream "; Hons-Note: Done restoring~%") (force-output note-stream) ;; All objects restored. The hons space should now be in a fine state ;; once again. Restore it. (hl-without-interrupts (setf (hl-hspace-addr-ht hs) addr-ht) (setf (hl-hspace-sbits hs) sbits)) (setf (hl-hspace-persist-ht hs) persist-ht) (setf (hl-hspace-faltable hs) faltable) (hl-make-addr-limit-current hs))) nil) (defun hl-maybe-resize-ht (size src) ; (HL-MAYBE-RESIZE-HT SIZE SRC) --> HASH TABLE ; ; SRC is a hash table that we would perhaps like to resize, and SIZE is our new ; target size. If SIZE is not sufficiently different from the current size of ; SRC, or if it seems too small for SRC, we just return SRC unchanged. ; Otherwise, we produce a new hash table that is a copy of SRC, but with the ; newly desired SIZE. (declare (type hash-table src)) (let* ((src-size (hash-table-size src)) (src-count (hash-table-count src)) (min-reasonable-size (max 100 (* src-count 1.2))) (target-size (max min-reasonable-size size))) (if (and (< (* src-size 0.8) target-size) (< target-size (* src-size 1.2))) ;; You're already pretty close to the target size. Don't ;; bother resizing. src ;; Okay, size is different enough to warrant resizing. (let ((new-ht (hl-mht :test (hash-table-test src) :size size))) (maphash (lambda (key val) (setf (gethash key new-ht) val)) src) new-ht)))) (defun hl-hspace-resize (str-ht-size nil-ht-size cdr-ht-size cdr-ht-eql-size addr-ht-size other-ht-size sbits-size fal-ht-size persist-ht-size hs) ;; This is mostly entirely straightforward. (declare (type hl-hspace hs) #+static-hons (ignore nil-ht-size cdr-ht-size cdr-ht-eql-size) #-static-hons (ignore addr-ht-size other-ht-size sbits-size)) (when (natp str-ht-size) (setf (hl-hspace-str-ht hs) (hl-maybe-resize-ht str-ht-size (hl-hspace-str-ht hs)))) (when (natp fal-ht-size) (let* ((faltable (hl-hspace-faltable hs)) (table (hl-faltable-table faltable))) (setf (hl-faltable-table faltable) (hl-maybe-resize-ht fal-ht-size table)))) (when (natp persist-ht-size) (setf (hl-hspace-persist-ht hs) (hl-maybe-resize-ht persist-ht-size (hl-hspace-persist-ht hs)))) #+static-hons (progn (when (natp addr-ht-size) (setf (hl-hspace-addr-ht hs) (hl-maybe-resize-ht addr-ht-size (hl-hspace-addr-ht hs)))) (when (natp other-ht-size) (setf (hl-hspace-other-ht hs) (hl-maybe-resize-ht other-ht-size (hl-hspace-other-ht hs)))) (when (natp sbits-size) ;; Tricky. Need to be sure that all 1-valued sbits are preserved. ;; We won't try to support shrinking sbits. (let* ((sbits (hl-hspace-sbits hs)) (new-len (min (1- array-total-size-limit) sbits-size)) (curr-len (length sbits))) (when (> sbits-size curr-len) ;; User wants to grow sbits, so that's okay. (let ((new-sbits (make-array new-len :element-type 'bit :initial-element 0))) (declare (type (simple-array bit (*)) new-sbits)) (loop for i fixnum below curr-len do (setf (aref new-sbits i) (aref sbits i))) (setf (hl-hspace-sbits hs) new-sbits)))))) #-static-hons (let ((ctables (hl-hspace-ctables hs))) (when (natp nil-ht-size) (setf (hl-ctables-nil-ht ctables) (hl-maybe-resize-ht nil-ht-size (hl-ctables-nil-ht ctables)))) (when (natp cdr-ht-size) (setf (hl-ctables-cdr-ht ctables) (hl-maybe-resize-ht cdr-ht-size (hl-ctables-cdr-ht ctables)))) (when (natp cdr-ht-eql-size) (setf (hl-ctables-cdr-ht-eql ctables) (hl-maybe-resize-ht cdr-ht-eql-size (hl-ctables-cdr-ht-eql ctables))))) #+static-hons (hl-make-addr-limit-current hs) nil) ; ---------------------------------------------------------------------- ; ; STATISTICS GATHERING ; ; ---------------------------------------------------------------------- (defun hl-get-final-cdr (alist) (if (atom alist) alist (hl-get-final-cdr (cdr alist)))) (defun hl-hspace-fast-alist-summary (hs) (declare (type hl-hspace hs)) (let* ((faltable (hl-hspace-faltable hs)) (table (hl-faltable-table faltable)) (total-count 0) (total-sizes 0) (total-num 0) (report-entries)) (format t "~%Fast Alists Summary:~%~%") (force-output) (hl-faltable-maphash (lambda (alist associated-ht) (let* ((final-cdr (hl-get-final-cdr alist)) (size (hash-table-size associated-ht)) (count (hash-table-count associated-ht))) (incf total-sizes size) (incf total-count count) (incf total-num) (push (list count size final-cdr) report-entries))) faltable) (format t " - Number of fast alists: ~15:D~%" total-num) (format t " - Size of FAL table: ~15:D~%" (hash-table-size table)) (format t " - Total of counts: ~15:D~%" total-count) (format t " - Total of sizes: ~15:D~%" total-sizes) (format t "~%") (force-output) (setq report-entries (sort report-entries (lambda (x y) (or (> (first x) (first y)) (and (= (first x) (first y)) (> (second x) (second y))))))) (format t "Summary of individual fast alists:~%~%") (format t " Count Size Name~%") (format t " (used slots) (capacity)~%") (format t "--------------------------------------------------~%") (loop for entry in report-entries do (format t "~10:D ~16:D ~:D~%" (first entry) (second entry) (third entry))) (format t "--------------------------------------------------~%") (format t "~%") (force-output))) (defun hl-hspace-hons-summary (hs) (declare (type hl-hspace hs)) (format t "~%Normed Objects Summary~%~%") #+static-hons (let ((addr-ht (hl-hspace-addr-ht hs)) (sbits (hl-hspace-sbits hs)) (other-ht (hl-hspace-other-ht hs))) (format t " - SBITS array length: ~15:D~%" (length sbits)) (format t " New static cons index: ~15:D~%~%" (hl-staticp (hl-static-cons nil nil))) (format t " - ADDR-HT: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count addr-ht) (hash-table-size addr-ht) (* (/ (hash-table-count addr-ht) (hash-table-size addr-ht)) 100.0)) (format t " - OTHER-HT: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count other-ht) (hash-table-size other-ht) (* (/ (hash-table-count other-ht) (hash-table-size other-ht)) 100.0)) ) #-static-hons (let* ((ctables (hl-hspace-ctables hs)) (nil-ht (hl-ctables-nil-ht ctables)) (cdr-ht (hl-ctables-cdr-ht ctables)) (cdr-ht-eql (hl-ctables-cdr-ht-eql ctables))) (format t " - NIL-HT: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count nil-ht) (hash-table-size nil-ht) (* (/ (hash-table-count nil-ht) (hash-table-size nil-ht)) 100.0)) (format t " - CDR-HT: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count cdr-ht) (hash-table-size cdr-ht) (* (/ (hash-table-count cdr-ht) (hash-table-size cdr-ht)) 100.0)) (format t " - CDR-HT-EQL: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count cdr-ht-eql) (hash-table-size cdr-ht-eql) (* (/ (hash-table-count cdr-ht-eql) (hash-table-size cdr-ht-eql)) 100.0)) ) (let ((str-ht (hl-hspace-str-ht hs)) (persist-ht (hl-hspace-persist-ht hs)) (fal-ht (hl-faltable-table (hl-hspace-faltable hs)))) (format t " - STR-HT: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count str-ht) (hash-table-size str-ht) (* (/ (hash-table-count str-ht) (hash-table-size str-ht)) 100.0)) (format t " - PERSIST-HT: ~15:D count, ~15:D size (~5,2f% full)~%" (hash-table-count persist-ht) (hash-table-size persist-ht) (* (/ (hash-table-count persist-ht) (hash-table-size persist-ht)) 100.0)) (format t " - FAL-HT: ~15:D count, ~15:D size (~5,2f% full)~%~%" (hash-table-count fal-ht) (hash-table-size fal-ht) (* (/ (hash-table-count fal-ht) (hash-table-size fal-ht)) 100.0)) ) nil) ; ---------------------------------------------------------------------- ; ; USER-LEVEL WRAPPERS ; ; ---------------------------------------------------------------------- (defparameter *default-hs* ; We hide the hons space from the ACL2 user by making all ACL2-visible ; functions operate with respect to *default-hs*, the "default hons space." ; ; For single-threaded versions of ACL2, we assume that *default-hs* is always ; bound to a valid Hons Space. ; ; But when ACL2-PAR is enabled, we allow *default-hs* to be either NIL or a ; valid hons space. The consume-work-on-work-queue-when-there function in ; acl2-par is responsible for creating all worker threads, and immediately ; binds *default-hs* to NIL, which is quite cheap. The idea is to allow ; threads that don't do any honsing to avoid the overhead of creating a hons ; space. ; ; Maybe we should make this a DEFVAR with no binding, and move it to whatever ; initialization function is run when ACL2 starts. This would keep the hons ; space out of the default ACL2 image. But that probably doesn't matter unless ; we want to adopt much larger defaults, which we don't. (hl-hspace-init)) (declaim #-acl2-par (type hl-hspace *default-hs*) #+acl2-par (type (or hl-hspace null) *default-hs*)) (declaim (inline hl-maybe-initialize-default-hs)) (defun hl-maybe-initialize-default-hs () #-acl2-par nil #+acl2-par (unless *default-hs* (setq *default-hs* (hl-hspace-init)))) (defun hl-maybe-initialize-default-hs-wrapper () ;; Bootstrapping hack for serialize (hl-maybe-initialize-default-hs)) (defun hons (x y) ;; hl-hspace-hons is inlined via defabbrev (hl-maybe-initialize-default-hs) (hl-hspace-hons x y *default-hs*)) (defun hons-copy (x) ;; hl-hspace-norm is inlined via defabbrev (hl-maybe-initialize-default-hs) (hl-hspace-norm x *default-hs*)) (defun hons-copy-persistent (x) ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-persistent-norm x *default-hs*)) (declaim (inline hons-equal)) (defun hons-equal (x y) ;; hl-hspace-hons-equal is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-hons-equal x y *default-hs*)) (declaim (inline hons-equal-lite)) (defun hons-equal-lite (x y) ;; hl-hspace-hons-equal-lite is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-hons-equal-lite x y *default-hs*)) (defun hons-summary () ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-hons-summary *default-hs*)) (defun hons-clear (gc) ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-hons-clear gc *default-hs*)) (defun hons-wash () ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-hons-wash *default-hs*)) (defun hons-resize-fn (str-ht nil-ht cdr-ht cdr-ht-eql addr-ht other-ht sbits fal-ht persist-ht) ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-resize str-ht nil-ht cdr-ht cdr-ht-eql addr-ht other-ht sbits fal-ht persist-ht *default-hs*)) (declaim (inline hons-acons)) (defun hons-acons (key val fal) ;; hl-hspace-hons-acons is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-hons-acons key val fal *default-hs*)) (declaim (inline hons-acons!)) (defun hons-acons! (key val fal) ;; hl-hspace-hons-acons is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-hons-acons! key val fal *default-hs*)) (defun hons-shrink-alist (alist ans) ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-shrink-alist alist ans nil *default-hs*)) (defun hons-shrink-alist! (alist ans) ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-shrink-alist alist ans t *default-hs*)) (declaim (inline hons-get)) (defun hons-get (key fal) ;; hl-hspace-hons-get is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-hons-get key fal *default-hs*)) (declaim (inline fast-alist-free)) (defun fast-alist-free (fal) ;; hl-hspace-fast-alist-free is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-fast-alist-free fal *default-hs*)) (declaim (inline fast-alist-len)) (defun fast-alist-len (fal) ;; hl-hspace-fast-alist-len is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-fast-alist-len fal *default-hs*)) (declaim (inline number-subtrees)) (defun number-subtrees (x) ;; hl-hspace-number-subtrees is not inlined, so we inline the wrapper (hl-maybe-initialize-default-hs) (hl-hspace-number-subtrees x *default-hs*)) (defun fast-alist-summary () ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-fast-alist-summary *default-hs*)) (defun make-fast-alist (alist) ;; no need to inline (hl-maybe-initialize-default-hs) (hl-hspace-make-fast-alist alist *default-hs*)) (defmacro with-fast-alist-raw (alist form) (let ((alist-was-fast-p (gensym)) (alist-var (if (legal-variablep alist) alist (gensym)))) `(progn (hl-maybe-initialize-default-hs) (let* ( ;; If alist isn't a variable, then depend on it being a ;; computation that returns the same (eq) object each time, and ;; that object can be turned into an (eq) fast alist, i.e. its ;; keys are normed. If not, then the user may not find their ;; alist to be fast during the execution of form, but we'll still ;; correctly free it. (,alist-var ,alist) (,alist-was-fast-p (let ((slot (hl-faltable-general-lookup ,alist-var (hl-hspace-faltable *default-hs*)))) (if (hl-falslot-key slot) t nil))) (,alist-var (if ,alist-was-fast-p ,alist-var (make-fast-alist ,alist-var)))) (our-multiple-value-prog1 ,form (if ,alist-was-fast-p nil (fast-alist-free ,alist-var))))))) (defmacro with-stolen-alist-raw (alist form) (let ((alist-was-fast-p (gensym)) (alist-var (if (legal-variablep alist) alist (gensym)))) `(progn (hl-maybe-initialize-default-hs) (let* ( ;; If alist isn't a variable, then depend on it being a ;; computation that returns the same (eq) object each time, and ;; that object can be turned into an (eq) fast alist, i.e. its ;; keys are normed. If not, then the user may not find their ;; alist to be fast during the execution of form, but we'll still ;; correctly free it. (,alist-var ,alist) (,alist-was-fast-p (let ((slot (hl-faltable-general-lookup ,alist-var (hl-hspace-faltable *default-hs*)))) (if (hl-falslot-key slot) t nil))) (,alist-var (if ,alist-was-fast-p ,alist-var (make-fast-alist ,alist-var)))) (our-multiple-value-prog1 ,form (if ,alist-was-fast-p (make-fast-alist ,alist-var) (fast-alist-free ,alist-var))))))) (defmacro fast-alist-free-on-exit-raw (alist form) `(our-multiple-value-prog1 ,form (fast-alist-free ,alist))) ; COMPATIBILITY WITH OLD HONS FUNCTIONS ------------------------ (defun clear-hash-tables () (clear-memoize-tables) #+static-hons (hons-wash) #-static-hons (hons-clear t)) (defun wash-memory () ;; Deprecated. (clear-memoize-tables) (hons-wash)) acl2-sources/induct.lisp0000666002132200015000000042441412222115527014744 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") (defun select-x-cl-set (cl-set induct-hint-val) ; This function produces the clause set we explore to collect ; induction candidates. The x in this name means "explore." If ; induct-hint-val is non-nil and non-t, we use the user-supplied ; induction hint value (which, if t means use cl-set); otherwise, we ; use cl-set. (cond ((null induct-hint-val) cl-set) ((equal induct-hint-val *t*) cl-set) (t (list (list induct-hint-val))))) (defun unchangeables (formals args quick-block-info subset ans) ; We compute the set of all variable names occurring in args in ; unchanging measured formal positions. We accumulate the answer onto ; ans. (cond ((null formals) ans) ((and (member-eq (car formals) subset) (eq (car quick-block-info) 'unchanging)) (unchangeables (cdr formals) (cdr args) (cdr quick-block-info) subset (all-vars1 (car args) ans))) (t (unchangeables (cdr formals) (cdr args) (cdr quick-block-info) subset ans)))) (defun changeables (formals args quick-block-info subset ans) ; We compute the args in changing measured formal positions. We ; accumulate the answer onto ans. (cond ((null formals) ans) ((and (member-eq (car formals) subset) (not (eq (car quick-block-info) 'unchanging))) (changeables (cdr formals) (cdr args) (cdr quick-block-info) subset (cons (car args) ans))) (t (changeables (cdr formals) (cdr args) (cdr quick-block-info) subset ans)))) (defun sound-induction-principle-mask1 (formals args quick-block-info subset unchangeables changeables) ; See sound-induction-principle-mask. (cond ((null formals) nil) (t (let ((var (car formals)) (arg (car args)) (q (car quick-block-info))) (mv-let (mask-ele new-unchangeables new-changeables) (cond ((member-eq var subset) (cond ((eq q 'unchanging) (mv 'unchangeable unchangeables changeables)) (t (mv 'changeable unchangeables changeables)))) ((and (variablep arg) (eq q 'unchanging)) (cond ((member-eq arg changeables) (mv nil unchangeables changeables)) (t (mv 'unchangeable (add-to-set-eq arg unchangeables) changeables)))) ((and (variablep arg) (not (member-eq arg changeables)) (not (member-eq arg unchangeables))) (mv 'changeable unchangeables (cons arg changeables))) (t (mv nil unchangeables changeables))) (cons mask-ele (sound-induction-principle-mask1 (cdr formals) (cdr args) (cdr quick-block-info) subset new-unchangeables new-changeables))))))) (defun sound-induction-principle-mask (term formals quick-block-info subset) ; Term is a call of some fn on some args. The formals and ; quick-block-info are those of fn, and subset is one of fn's measured ; subset (a subset of formals). We wish to determine, in the ; terminology of ACL, whether the induction applies to term. If so we ; return a mask indicating how to build the substitutions for the ; induction from args and the machine for fn. Otherwise we return ; nil. ; Let the changeables be those args that are in measured formal ; positions that sometimes change in the recursion. Let the ; unchangeables be all of the variables occurring in measured actuals ; that never change in recursion. The induction applies if ; changeables is a sequence of distinct variable names and has an ; empty intersection with unchangeables. ; If the induction is applicable then the substitutions should ; substitute for the changeables just as the recursion would, and hold ; each unchangeable fixed -- i.e., substitute each for itself. With ; such substitutions it is possible to prove the measure lemmas ; analogous to those proved when justification of subset was stored, ; except that the measure is obtained by instantiating the measure ; term used in the justification by the measured actuals in unchanging ; slots. Actual variables that are neither among the changeables or ; unchangeables may be substituted for arbitrarily. ; If the induction is applicable we return a mask with as many ; elements as there are args. For each arg the mask contains either ; 'changeable, 'unchangeable, or nil. 'Changeable means the arg ; should be instantiated as specified in the recursion. 'Unchangeable ; means each var in the actual should be held fixed. Nil means that ; the corresponding substitution pairs in the machine for the function ; should be ignored. ; Abstractly, this function builds the mask by first putting either ; 'changeable or 'unchangeable in each measured slot. It then fills ; in the remaining slots from the left so as to permit the actual to ; be instantiated or held fixed as desired by the recursion, provided ; that in so doing it does not permit substitutions for previously ; allocated actuals. (let ((unchangeables (unchangeables formals (fargs term) quick-block-info subset nil)) (changeables (changeables formals (fargs term) quick-block-info subset nil))) (cond ((or (not (no-duplicatesp-equal changeables)) (not (all-variablep changeables)) (intersectp-eq changeables unchangeables)) nil) (t (sound-induction-principle-mask1 formals (fargs term) quick-block-info subset unchangeables changeables))))) (defrec candidate (score controllers changed-vars unchangeable-vars tests-and-alists-lst justification induction-term other-terms xinduction-term xother-terms xancestry ttree) nil) ; This record is used to represent one of the plausible inductions that must be ; considered. The SCORE represents some fairly artificial estimation of how ; many terms in the conjecture want this induction. CONTROLLERS is the list of ; the controllers -- including unchanging controllers -- for all the inductions ; merged to form this one. The CHANGED-VARS is a list of all those variables ; that will be instantiated (non-identically) in some induction hypotheses. ; Thus, CHANGED-VARS include both variables that actually contribute to why ; some measure goes down and variables like accumulators that are just along ; for the ride. UNCHANGEABLE-VARS is a list of all those vars which may not be ; changed by any substitution if this induction is to be sound for the reasons ; specified. TESTS-AND-ALISTS-LST is a list of TESTS-AND-ALISTS which ; indicates the case analysis for the induction and how the various induction ; hypotheses are obtained via substitutions. JUSTIFICATION is the ; JUSTIFICATION that justifies this induction, and INDUCTION-TERM is the term ; that suggested this induction and from which you obtain the actuals to ; substitute into the template. OTHER-TERMS are the induction-terms of ; candidates that have been merged into this one for heuristic reasons. ; Because of induction rules we can think of some terms being aliases for ; others. We have to make a distinction between the terms in the conjecture ; that suggested an induction and the actual terms that suggested the ; induction. For example, if an induction rule maps (fn x y) to (recur x y), ; then (recur x y) will be the INDUCTION-TERM because it suggested the ; induction and we will, perhaps, have to recover its induction machine or ; quick block information to implement various heuristics. But we do not wish ; to report (recur x y) to the user, preferring instead to report (fn x y). ; Therefore, corresponding to INDUCTION-TERM and OTHER-TERMS we have ; XINDUCTION-TERM and XOTHER-TERMS, which are maintained in exactly the same ; way as their counterparts but which deal completely with the user-level view ; of the induction. In practice this means that we will initialize the ; XINDUCTION-TERM field of a candidate with the term from the conjecture, ; initialize the INDUCTION-TERM with its alias, and then merge the fields ; completely independently but analogously. XANCESTRY is a field maintained by ; merging to contain the user-level terms that caused us to change our ; tests-and-alists. (Some candidates may be flushed or merged into this one ; without changing it.) ; The ttree of a candidate contains 'LEMMA tags listing the :induction rules ; (if any) involved in the suggestion of the candidate. (defun count-non-nils (lst) (cond ((null lst) 0) ((car lst) (1+ (count-non-nils (cdr lst)))) (t (count-non-nils (cdr lst))))) (defun controllers (formals args subset ans) (cond ((null formals) ans) ((member (car formals) subset) (controllers (cdr formals) (cdr args) subset (all-vars1 (car args) ans))) (t (controllers (cdr formals) (cdr args) subset ans)))) (defun changed/unchanged-vars (x args mask ans) (cond ((null mask) ans) ((eq (car mask) x) (changed/unchanged-vars x (cdr args) (cdr mask) (all-vars1 (car args) ans))) (t (changed/unchanged-vars x (cdr args) (cdr mask) ans)))) (defrec tests-and-alists (tests alists) nil) (defun tests-and-alists/alist (alist args mask call-args) ; Alist is an alist that maps the formals of some fn to its actuals, ; args. Mask is a sound-induction-principle-mask indicating the args ; for which we will build substitution pairs. Call-args is the list of ; arguments to some recursive call of fn occurring in the induction ; machine for fn. We build an alist mapping the masked args to the ; instantiations (under alist) of the values in call-args. (cond ((null mask) nil) ((null (car mask)) (tests-and-alists/alist alist (cdr args) (cdr mask) (cdr call-args))) ((eq (car mask) 'changeable) (cons (cons (car args) (sublis-var alist (car call-args))) (tests-and-alists/alist alist (cdr args) (cdr mask) (cdr call-args)))) (t (let ((vars (all-vars (car args)))) (append (pairlis$ vars vars) (tests-and-alists/alist alist (cdr args) (cdr mask) (cdr call-args))))))) (defun tests-and-alists/alists (alist args mask calls) ; Alist is an alist that maps the formals of some fn to its actuals, ; args. Mask is a sound-induction-principle-mask indicating the args ; for which we will build substitution pairs. Calls is the list of ; calls for a given case of the induction machine. We build the alists ; from those calls. (cond ((null calls) nil) (t (cons (tests-and-alists/alist alist args mask (fargs (car calls))) (tests-and-alists/alists alist args mask (cdr calls)))))) ; The following record contains the tests leading to a collection of ; recursive calls at the end of a branch through a defun. In general, ; the calls may not be to the function being defuned but rather to ; some other function in the same mutually recursive clique, but in ; the context of induction we know that all the calls are to the same ; fn because we haven't implemented mutually recursive inductions yet. ; A list of these records constitute the induction machine for a function. (defrec tests-and-calls (tests . calls) nil) ; The justification record contains a subset of the formals of the function ; under which it is stored. Only the subset, ruler-extenders, and subversive-p ; fields have semantic content! The other fields are the domain predicate, mp; ; the relation, rel, which is well-founded on mp objects; and the mp-measure ; term which has been proved to decrease in the recursion. The latter three ; fields are correct at the time the function is admitted, but note that they ; might all be local and hence have disappeared by the time these fields are ; read. Thus, we include them only for heuristic purposes, for example as used ; in community book books/workshops/2004/legato/support/generic-theories.lisp. ; A list of justification records is stored under each function symbol by the ; defun principle. (defrec justification (subset . ((subversive-p . (mp . rel)) (measure . ruler-extenders))) nil) (defun tests-and-alists (alist args mask tc) ; Alist is an alist that maps the formals of some fn to its actuals, ; args. Mask is a sound-induction-principle-mask indicating the args ; for which we will build substitution pairs. Tc is one of the ; tests-and-calls in the induction machine for the function. We make ; a tests-and-alists record containing the instantiated tests of tc ; and alists derived from the calls of tc. (make tests-and-alists :tests (sublis-var-lst alist (access tests-and-calls tc :tests)) :alists (tests-and-alists/alists alist args mask (access tests-and-calls tc :calls)))) (defun tests-and-alists-lst (alist args mask machine) ; We build a list of tests-and-alists from machine, instantiating it ; with alist, which is a map from the formals of the function to the ; actuals, args. Mask is the sound-induction-principle-mask that ; indicates the args for which we substitute. (cond ((null machine) nil) (t (cons (tests-and-alists alist args mask (car machine)) (tests-and-alists-lst alist args mask (cdr machine)))))) (defun flesh-out-induction-principle (term formals justification mask machine xterm ttree) ; Term is a call of some function the indicated formals and induction machine. ; Justification is its 'justification and mask is a sound-induction- ; principle-mask for the term. We build the induction candidate suggested by ; term. (make candidate :score (/ (count-non-nils mask) (length mask)) :controllers (controllers formals (fargs term) (access justification justification :subset) nil) :changed-vars (changed/unchanged-vars 'changeable (fargs term) mask nil) :unchangeable-vars (changed/unchanged-vars 'unchangeable (fargs term) mask nil) :tests-and-alists-lst (tests-and-alists-lst (pairlis$ formals (fargs term)) (fargs term) mask machine) :justification justification :induction-term term :xinduction-term xterm :other-terms nil :xother-terms nil :xancestry nil :ttree ttree)) (defun intrinsic-suggested-induction-cand (term formals quick-block-info justification machine xterm ttree ens wrld) ; Note: An "intrinsically suggested" induction scheme is an induction scheme ; suggested by a justification of a recursive function. The rune controlling ; the intrinsic suggestion from the justification of fn is (:induction fn). We ; distinguish between intrinsically suggested inductions and those suggested ; via records of induction-rule type. Intrinsic inductions have no embodiment ; as induction-rules but are, instead, the basis of the semantics of such ; rules. That is, the inductions suggested by (fn x y) is the union of those ; suggested by the terms to which it is linked via induction-rules together ; with the intrinsic suggestion for (fn x y). ; Term, above, is a call of some fn with the given formals, quick-block-info, ; justification and induction machine. We return a list of induction ; candidates, said list either being empty or the singleton list containing the ; induction candidate intrinsically suggested by term. Xterm is logically ; unrelated to term and is the term appearing in the original conjecture from ; which we (somehow) obtained term for consideration. (let ((induction-rune (list :induction (ffn-symb term)))) (cond ((enabled-runep induction-rune ens wrld) (let ((mask (sound-induction-principle-mask term formals quick-block-info (access justification justification :subset)))) (cond (mask (list (flesh-out-induction-principle term formals justification mask machine xterm (push-lemma induction-rune ttree)))) (t nil)))) (t nil)))) (defrec induction-rule (nume (pattern . condition) scheme . rune) nil) (mutual-recursion (defun apply-induction-rule (rule term type-alist xterm ttree seen ens wrld) ; We apply the induction-rule, rule, to term, and return a possibly empty list ; of suggested inductions. The basic idea is to check that the rule is enabled ; and that the :pattern of the rule matches term. If so, we check that the ; :condition of the rule is true under the current type-alist. This check is ; heuristic only and so we indicate that the guards have been checked and we ; allow forcing. We ignore the ttree because we are making a heuristic choice ; only. If type-set says the :condition is non-nil, we fire the rule by ; instantiating the :scheme and recursively getting the suggested inductions ; for that term. Note that we are not recursively exploring the instantiated ; scheme but just getting the inductions suggested by its top-level function ; symbol. ; Seen is a list of numes of induction-rules already encountered, used in order ; to prevent infinite loops. The following are two examples that looped before ; the use of this list of numes seen. ; From Daron Vroon: ; (defun zip1 (xs ys) ; (declare (xargs :measure (acl2-count xs))) ; (cond ((endp xs) nil) ; ((endp ys) nil) ; (t (cons (cons (car xs) (car ys)) (zip1 (cdr xs) (cdr ys)))))) ; (defun zip2 (xs ys) ; (declare (xargs :measure (acl2-count ys))) ; (cond ((endp xs) nil) ; ((endp ys) nil) ; (t (cons (cons (car xs) (car ys)) (zip2 (cdr xs) (cdr ys)))))) ; (defthm zip1-zip2-ind ; t ; :rule-classes ((:induction :pattern (zip1 xs ys) ; :scheme (zip2 xs ys)))) ; (defthm zip2-zip1-ind ; t ; :rule-classes ((:induction :pattern (zip2 xs ys) ; :scheme (zip1 xs ys)))) ; (defstub foo (*) => *) ; ;; the following overflows the stack. ; (thm ; (= (zip1 (foo xs) ys) ; (zip2 (foo xs) ys))) ; From Pete Manolios: ; (defun app (x y) ; (if (endp x) ; y ; (cons (car x) (app (cdr x) y)))) ; (defthm app-ind ; t ; :rule-classes ((:induction :pattern (app x y) ; :condition (and (true-listp x) (true-listp y)) ; :scheme (app x y)))) ; (in-theory (disable (:induction app))) ; (defthm app-associative ; (implies (and (true-listp a) ; (true-listp b) ; (true-listp c)) ; (equal (app (app a b) c) ; (app a (app b c))))) (let ((nume (access induction-rule rule :nume))) (cond ((and (not (member nume seen)) (enabled-numep nume ens)) (mv-let (ans alist) (one-way-unify (access induction-rule rule :pattern) term) (cond (ans (with-accumulated-persistence (access induction-rule rule :rune) (suggestions) suggestions (mv-let (ts ttree1) (type-set (sublis-var alist (access induction-rule rule :condition)) t nil type-alist ens wrld nil nil nil) (declare (ignore ttree1)) (cond ((ts-intersectp *ts-nil* ts) nil) (t (let ((term1 (sublis-var alist (access induction-rule rule :scheme)))) (cond ((or (variablep term1) (fquotep term1)) nil) (t (suggested-induction-cands term1 type-alist xterm (push-lemma (access induction-rule rule :rune) ttree) (cons nume seen) ens wrld))))))))) (t nil)))) (t nil)))) (defun suggested-induction-cands1 (induction-rules term type-alist xterm ttree seen ens wrld) ; We map down induction-rules and apply each enabled rule to term, which is ; known to be an application of the function symbol fn to some args. Each rule ; gives us a possibly empty list of suggested inductions. We append all these ; suggestions together. In addition, if fn is recursively defined and is ; enabled (or, even if fn is disabled if we are exploring a user-supplied ; induction hint) we collect the intrinsic suggestion for term as well. ; Seen is a list of numes of induction-rules already encountered, used in order ; to prevent infinite loops. (cond ((null induction-rules) (let* ((fn (ffn-symb term)) (machine (getprop fn 'induction-machine nil 'current-acl2-world wrld))) (cond ((null machine) nil) (t ; Historical note: Before Version_2.6 we had the following note: ; Note: The intrinsic suggestion will be non-nil only if (:INDUCTION fn) is ; enabled and so here we have a case in which two runes have to be enabled ; (the :DEFINITION and the :INDUCTION runes) to get the desired effect. It ; is not clear if this is a good design but at first sight it seems to ; provide upward compatibility because in Nqthm a disabled function suggests ; no inductions. ; We no longer make any such requirement: the test above (t) replaces the ; following. ; (or (enabled-fnp fn nil ens wrld) ; (and induct-hint-val ; (not (equal induct-hint-val *t*)))) (intrinsic-suggested-induction-cand term (formals fn wrld) (getprop fn 'quick-block-info '(:error "See SUGGESTED-INDUCTION-CANDS1.") 'current-acl2-world wrld) (getprop fn 'justification '(:error "See SUGGESTED-INDUCTION-CANDS1.") 'current-acl2-world wrld) machine xterm ttree ens wrld))))) (t (append (apply-induction-rule (car induction-rules) term type-alist xterm ttree seen ens wrld) (suggested-induction-cands1 (cdr induction-rules) term type-alist xterm ttree seen ens wrld))))) (defun suggested-induction-cands (term type-alist xterm ttree seen ens wrld) ; Term is some fn applied to args. Xterm is some term occurring in the ; conjecture we are exploring and is the term upon which this induction ; suggestion will be "blamed" and from which we have obtained term via ; induction-rules. We return all of the induction candidates suggested by ; term. Lambda applications suggest no inductions. Disabled functions suggest ; no inductions -- unless we are applying the user's induct hint value (in ; which case we, quite reasonably, assume every function in the value is worthy ; of analysis since any function could have been omitted). ; Seen is a list of numes of induction-rules already encountered, used in order ; to prevent infinite loops. (cond ((flambdap (ffn-symb term)) nil) (t (suggested-induction-cands1 (getprop (ffn-symb term) 'induction-rules nil 'current-acl2-world wrld) term type-alist xterm ttree seen ens wrld)))) ) (mutual-recursion (defun get-induction-cands (term type-alist ens wrld ans) ; We explore term and accumulate onto ans all of the induction ; candidates suggested by it. (cond ((variablep term) ans) ((fquotep term) ans) ((eq (ffn-symb term) 'hide) ans) (t (get-induction-cands-lst (fargs term) type-alist ens wrld (append (suggested-induction-cands term type-alist term nil nil ens wrld) ans))))) (defun get-induction-cands-lst (lst type-alist ens wrld ans) ; We explore the list of terms, lst, and accumulate onto ans all of ; the induction candidates. (cond ((null lst) ans) (t (get-induction-cands (car lst) type-alist ens wrld (get-induction-cands-lst (cdr lst) type-alist ens wrld ans))))) ) (defun get-induction-cands-from-cl-set1 (cl-set ens oncep-override wrld state ans) ; We explore cl-set and accumulate onto ans all of the induction ; candidates. (cond ((null cl-set) ans) (t (mv-let (contradictionp type-alist fc-pairs) (forward-chain-top 'induct (car cl-set) nil t nil ; do-not-reconsiderp wrld ens oncep-override state) ; We need a type-alist with which to compute induction aliases. It is of ; heuristic use only, so we may as well turn the force-flg on. We assume no ; contradiction is found. If by chance one is, then type-alist is nil, which ; is an acceptable type-alist. (declare (ignore contradictionp fc-pairs)) (get-induction-cands-lst (car cl-set) type-alist ens wrld (get-induction-cands-from-cl-set1 (cdr cl-set) ens oncep-override wrld state ans)))))) (defun get-induction-cands-from-cl-set (cl-set pspv wrld state) ; We explore cl-set and collect all induction candidates. (let ((rcnst (access prove-spec-var pspv :rewrite-constant))) (get-induction-cands-from-cl-set1 cl-set (access rewrite-constant rcnst :current-enabled-structure) (access rewrite-constant rcnst :oncep-override) wrld state nil))) ; That completes the development of the code for exploring a clause set ; and gathering the induction candidates suggested. ; Section: Pigeon-Holep ; We next develop pigeon-holep, which tries to fit some "pigeons" into ; some "holes" using a function to determine the sense of the word ; "fit". Since ACL2 is first-order we can't pass arbitrary functions ; and hence we pass symbols and define our own special-purpose "apply" ; that interprets the particular symbols passed to calls of ; pigeon-holep. ; However, it turns out that the applications of pigeon-holep require ; passing functions that themselves call pigeon-holep. And so ; pigeon-holep-apply is mutually recursive with pigeon-holep (but only ; because the application functions use pigeon-holep). (mutual-recursion (defun pigeon-holep-apply (fn pigeon hole) ; See pigeon-holep for the problem and terminology. This function ; takes a symbol denoting a predicate and two arguments. It applies ; the predicate to the arguments. When the predicate holds we say ; the pigeon argument "fits" into the hole argument. (case fn (pair-fitp ; This predicate is applied to two pairs, each taken from two substitutions. ; We say (v1 . term1) (the "pigeon") fits into (v2 . term2) (the "hole") ; if v1 is v2 and term1 occurs in term2. (and (eq (car pigeon) (car hole)) (occur (cdr pigeon) (cdr hole)))) (alist-fitp ; This predicate is applied to two substitutions. We say the pigeon ; alist fits into the hole alist if each pair of the pigeon fits into ; a pair of the hole and no pair of the hole is used more than once. (pigeon-holep pigeon hole nil 'pair-fitp)) (tests-and-alists-fitp ; This predicate is applied to two tests-and-alists records. The ; first fits into the second if the tests of the first are a subset ; of those of the second and either they are both base cases (i.e., have ; no alists) or each substitution of the first fits into a substitution of ; the second, using no substitution of the second more than once. (and (subsetp-equal (access tests-and-alists pigeon :tests) (access tests-and-alists hole :tests)) (or (and (null (access tests-and-alists pigeon :alists)) (null (access tests-and-alists hole :alists))) (pigeon-holep (access tests-and-alists pigeon :alists) (access tests-and-alists hole :alists) nil 'alist-fitp)))))) (defun pigeon-holep (pigeons holes filled-holes fn) ; Both pigeons and holes are lists of arbitrary objects. The holes ; are implicitly enumerated left-to-right from 0. Filled-holes is a ; list of the indices of holes we consider "filled." Fn is a ; predicate known to pigeon-holep-apply. If fn applied to a pigeon and ; a hole is non-nil, then we say the pigeon "fits" into the hole. We ; can only "put" a pigeon into a hole if the hole is unfilled and the ; pigeon fits. The act of putting the pigeon into the hole causes the ; hole to become filled. We return t iff it is possible to put each ; pigeon into a hole under these rules. (cond ((null pigeons) t) (t (pigeon-holep1 (car pigeons) (cdr pigeons) holes 0 holes filled-holes fn)))) (defun pigeon-holep1 (pigeon pigeons lst n holes filled-holes fn) ; Lst is a terminal sublist of holes, whose first element has index n. ; We map over lst looking for an unfilled hole h such that (a) we can ; put pigeon into h and (b) we can dispose of the rest of the pigeons ; after filling h. (cond ((null lst) nil) ((member n filled-holes) (pigeon-holep1 pigeon pigeons (cdr lst) (1+ n) holes filled-holes fn)) ((and (pigeon-holep-apply fn pigeon (car lst)) (pigeon-holep pigeons holes (cons n filled-holes) fn)) t) (t (pigeon-holep1 pigeon pigeons (cdr lst) (1+ n) holes filled-holes fn)))) ) (defun flush-cand1-down-cand2 (cand1 cand2) ; This function takes two induction candidates and determines whether ; the first is subsumed by the second. If so, it constructs a new ; candidate that is logically equivalent (vis a vis the induction ; suggested) to the second but which now carries with it the weight ; and heuristic burdens of the first. (cond ((and (subsetp-eq (access candidate cand1 :changed-vars) (access candidate cand2 :changed-vars)) (subsetp-eq (access candidate cand1 :unchangeable-vars) (access candidate cand2 :unchangeable-vars)) (pigeon-holep (access candidate cand1 :tests-and-alists-lst) (access candidate cand2 :tests-and-alists-lst) nil 'tests-and-alists-fitp)) (change candidate cand2 :score (+ (access candidate cand1 :score) (access candidate cand2 :score)) :controllers (union-eq (access candidate cand1 :controllers) (access candidate cand2 :controllers)) :other-terms (add-to-set-equal (access candidate cand1 :induction-term) (union-equal (access candidate cand1 :other-terms) (access candidate cand2 :other-terms))) :xother-terms (add-to-set-equal (access candidate cand1 :xinduction-term) (union-equal (access candidate cand1 :xother-terms) (access candidate cand2 :xother-terms))) :ttree (cons-tag-trees (access candidate cand1 :ttree) (access candidate cand2 :ttree)))) (t nil))) (defun flush-candidates (cand1 cand2) ; This function determines whether one of the two induction candidates ; given flushes down the other and if so returns the appropriate ; new candidate. This function is a mate and merge function used ; by m&m and is hence known to m&m-apply. (or (flush-cand1-down-cand2 cand1 cand2) (flush-cand1-down-cand2 cand2 cand1))) ; We now begin the development of the merging of two induction ; candidates. The basic idea is that if two functions both replace x ; by x', and one of them simultaneously replaces a by a' while the ; other replaces b by b', then we should consider inducting on x, a, ; and b, by x', a', and b'. Of course, by the time we get here, the ; recursion is coded into substitution alists: ((x . x') (a . a')) and ; ((x . x') (b . b')). We merge these two alists into ((x . x') (a . ; a') (b . b')). The merge of two sufficiently compatible alists is ; accomplished by just unioning them together. ; The key ideas are (1) recognizing when two alists are describing the ; "same" recursive step (i.e., they are both replacing x by x', where ; x is somehow a key variable); (2) observing that they do not ; explicitly disagree on what to do with the other variables. (defun alists-agreep (alist1 alist2 vars) ; Two alists agree on vars iff for each var in vars the image of var under ; the first alist is equal to that under the second. (cond ((null vars) t) ((equal (let ((temp (assoc-eq (car vars) alist1))) (cond (temp (cdr temp))(t (car vars)))) (let ((temp (assoc-eq (car vars) alist2))) (cond (temp (cdr temp))(t (car vars))))) (alists-agreep alist1 alist2 (cdr vars))) (t nil))) (defun irreconcilable-alistsp (alist1 alist2) ; Two alists are irreconcilable iff there is a var v that they both ; explicitly map to different values. Put another way, there exists a ; v such that (v . a) is a member of alist1 and (v . b) is a member of ; alist2, where a and b are different. If two substitutions are ; reconcilable then their union is a substitution. ; We rely on the fact that this function return t or nil. (cond ((null alist1) nil) (t (let ((temp (assoc-eq (caar alist1) alist2))) (cond ((null temp) (irreconcilable-alistsp (cdr alist1) alist2)) ((equal (cdar alist1) (cdr temp)) (irreconcilable-alistsp (cdr alist1) alist2)) (t t)))))) (defun affinity (aff alist1 alist2 vars) ; We say two alists that agree on vars but are irreconcilable are ; "antagonists". Two alists that agree on vars and are not irreconcilable ; are "mates". ; Aff is either 'antagonists or 'mates and denotes one of the two relations ; above. We return t iff the other args are in the given relation. (and (alists-agreep alist1 alist2 vars) (eq (irreconcilable-alistsp alist1 alist2) (eq aff 'antagonists)))) (defun member-affinity (aff alist alist-lst vars) ; We determine if some member of alist-lst has the given affinity with alist. (cond ((null alist-lst) nil) ((affinity aff alist (car alist-lst) vars) t) (t (member-affinity aff alist (cdr alist-lst) vars)))) (defun occur-affinity (aff alist lst vars) ; Lst is a list of tests-and-alists. We determine whether alist has ; the given affinity with some alist in lst. We call this occur ; because we are looking inside the elements of lst. But it is ; technically a misnomer because we don't look inside recursively; we ; treat lst as though it were a list of lists. (cond ((null lst) nil) ((member-affinity aff alist (access tests-and-alists (car lst) :alists) vars) t) (t (occur-affinity aff alist (cdr lst) vars)))) (defun some-occur-affinity (aff alists lst vars) ; Lst is a list of tests-and-alists. We determine whether some alist ; in alists has the given affinity with some alist in lst. (cond ((null alists) nil) (t (or (occur-affinity aff (car alists) lst vars) (some-occur-affinity aff (cdr alists) lst vars))))) (defun all-occur-affinity (aff alists lst vars) ; Lst is a list of tests-and-alists. We determine whether every alist ; in alists has the given affinity with some alist in lst. (cond ((null alists) t) (t (and (occur-affinity aff (car alists) lst vars) (all-occur-affinity aff (cdr alists) lst vars))))) (defun contains-affinity (aff lst vars) ; We determine if two members of lst have the given affinity. (cond ((null lst) nil) ((member-affinity aff (car lst) (cdr lst) vars) t) (t (contains-affinity aff (cdr lst) vars)))) ; So much for general-purpose scanners. We now develop the predicates ; that are used to determine if we can merge lst1 into lst2 on vars. ; See merge-tests-and-alists-lsts for extensive comments on the ideas ; behind the following functions. (defun antagonistic-tests-and-alists-lstp (lst vars) ; Lst is a list of tests-and-alists. Consider just the set of all ; alists in lst. We return t iff that set contains an antagonistic ; pair. ; We operate as follows. Each element of lst contains some alists. ; We take the first element and ask whether its alists contain an ; antagonistic pair. If so, we're done. Otherwise, we ask whether ; any alist in that first element is antagonistic with the alists in ; the rest of lst. If so, we're done. Otherwise, we recursively ; look at the rest of lst. (cond ((null lst) nil) (t (or (contains-affinity 'antagonists (access tests-and-alists (car lst) :alists) vars) (some-occur-affinity 'antagonists (access tests-and-alists (car lst) :alists) (cdr lst) vars) (antagonistic-tests-and-alists-lstp (cdr lst) vars))))) (defun antagonistic-tests-and-alists-lstsp (lst1 lst2 vars) ; Both lst1 and lst2 are lists of tests-and-alists. We determine whether ; there exists an alist1 in lst1 and an alist2 in lst2 such that alist1 ; and alist2 are antagonists. (cond ((null lst1) nil) (t (or (some-occur-affinity 'antagonists (access tests-and-alists (car lst1) :alists) lst2 vars) (antagonistic-tests-and-alists-lstsp (cdr lst1) lst2 vars))))) (defun every-alist1-matedp (lst1 lst2 vars) ; Both lst1 and lst2 are lists of tests-and-alists. We determine for every ; alist1 in lst1 there exists an alist2 in lst2 that agrees with alist1 on ; vars and that is not irreconcilable. (cond ((null lst1) t) (t (and (all-occur-affinity 'mates (access tests-and-alists (car lst1) :alists) lst2 vars) (every-alist1-matedp (cdr lst1) lst2 vars))))) ; The functions above are used to determine that lst1 and lst2 contain ; no antagonistic pairs, that every alist in lst1 has a mate somewhere in ; lst2, and that the process of merging alists from lst1 with their ; mates will not produce alists that are antagonistic with other alists ; in lst1. We now develop the code for merging nonantagonistic alists ; and work up the structural hierarchy to merging lists of tests and alists. (defun merge-alist1-into-alist2 (alist1 alist2 vars) ; We assume alist1 and alist2 are not antagonists. Thus, either they ; agree on vars and have no explicit disagreements, or they simply ; don't agree on vars. If they agree on vars, we merge alist1 into ; alist2 by just unioning them together. If they don't agree on vars, ; then we merge alist1 into alist2 by ignoring alist1. (cond ((alists-agreep alist1 alist2 vars) (union-equal alist1 alist2)) (t alist2))) ; Now we begin working up the structural hierarchy. Our strategy is ; to focus on a given alist2 and hit it with every alist1 we have. ; Then we'll use that to copy lst2 once, hitting each alist2 in it ; with everything we have. We could decompose the problem the other ; way: hit lst2 with successive alist1's. That suffers from forcing ; us to copy lst2 repeatedly, and there are parts of that structure ; (i.e., the :tests) that don't change. (defun merge-alist1-lst-into-alist2 (alist1-lst alist2 vars) ; Alist1-lst is a list of alists and alist2 is an alist. We know that ; there is no antagonists between the elements of alist1-lst and in ; alist2. We merge each alist1 into alist2 and return ; the resulting extension of alist2. (cond ((null alist1-lst) alist2) (t (merge-alist1-lst-into-alist2 (cdr alist1-lst) (merge-alist1-into-alist2 (car alist1-lst) alist2 vars) vars)))) (defun merge-lst1-into-alist2 (lst1 alist2 vars) ; Given a list of tests-and-alists, lst1, and an alist2, we hit alist2 ; with every alist1 in lst1. (cond ((null lst1) alist2) (t (merge-lst1-into-alist2 (cdr lst1) (merge-alist1-lst-into-alist2 (access tests-and-alists (car lst1) :alists) alist2 vars) vars)))) ; So now we write the code to copy lst2, hitting each alist in it with lst1. (defun merge-lst1-into-alist2-lst (lst1 alist2-lst vars) (cond ((null alist2-lst) nil) (t (cons (merge-lst1-into-alist2 lst1 (car alist2-lst) vars) (merge-lst1-into-alist2-lst lst1 (cdr alist2-lst) vars))))) (defun merge-lst1-into-lst2 (lst1 lst2 vars) (cond ((null lst2) nil) (t (cons (change tests-and-alists (car lst2) :alists (merge-lst1-into-alist2-lst lst1 (access tests-and-alists (car lst2) :alists) vars)) (merge-lst1-into-lst2 lst1 (cdr lst2) vars))))) (defun merge-tests-and-alists-lsts (lst1 lst2 vars) ; Lst1 and lst2 are each tests-and-alists-lsts from some induction ; candidates. Intuitively, we try to stuff the alists of lst1 into ; those of lst2 to construct a new lst2 that combines the induction ; schemes of both. If we fail we return nil. Otherwise we return the ; modified lst2. The modified lst2 has exactly the same tests as ; before; only its alists are different and they are different only by ; virtue of having been extended with some addition pairs. So the ; justification of the merged induction is the same as the ; justification of the original lst2. ; Given an alist1 from lst1, which alist2's of lst2 do you extend and ; how? Suppose alist1 maps x to x' and y to y'. Then intuitively we ; think "the first candidate is trying to keep x and y in step, so ; that when x goes to x' y goes to y'." So, if you see an alist in ; lst2 that is replacing x by x', one might be tempted to augment it ; by replacing y by y'. However, what if x is just an accumulator? ; The role of vars is to specify upon which variables two ; substitutions must agree in order to be merged. Usually, vars ; consists of the measured variables. ; So now we get a little more technical. We will try to "merge" each ; alist1 from lst1 into each alist2 from lst2 (preserving the case structure ; of lst2). If alist1 and alist2 do not agree on vars then their merge ; is just alist2. If they do agree on vars, then their merge is their ; union, provided that is a substitution. It may fail to be a substitution ; because the two alists disagree on some other variable. In that case ; we say the two are irreconcilable. We now give three simple examples: ; Let vars be {x}. Let alist2 be {(x . x') (z . z')}. If alist1 maps ; x to x'', then their merge is just alist2 because alist1 is ; addressing a different case of the induction. If alist1 maps x to x' ; and y to y', then their merge is {(x . x') (y . y') (z . z')}. If ; alist1 maps x to x' and z to z'', then the two are irreconcilable. ; Two irreconcilable alists that agree on vars are called "antagonists" ; because they "want" to merge but can't. We cannot merge lst1 into lst2 ; if they have an antagonistic pair between them. If an antagonistic pair ; is discovered, the entire merge operation fails. ; Now we will successively consider each alist1 in lst1 and merge it ; into lst2, forming successive lst2's. We insist that each alist1 of ; lst1 have at least one mate in lst2 with which it agrees and is ; reconcilable. (Otherwise, we could merge completely disjoint ; substitutions.) ; Because we try the alist1's successively, each alist1 is actually ; merged into the lst2 produced by all the previous alist1's. That ; produces an apparent order dependence. However, this is avoided by ; the requirement that we never produce antagonistic pairs. ; For example, suppose that in one case of lst1, x is mapped to x' and ; y is mapped to y', but in another case x is mapped to x' and y is ; mapped to y''. Now imagine trying to merge that lst1 into a lst2 in ; which x is mapped to x' and z is mapped to z'. The first alist of ; lst1 extends lst2 to (((x . x') (y . y') (z . z'))). But the second ; alist is then antagonistic. The same thing happens if we tried the two ; alists of lst1 in the other order. Thus, the above lst1 cannot be merged ; into lst2. Note that they can be merged in the other order! That is, ; lst2 can be merged into lst1, because the case structure of lst1 is ; richer. ; We can detect the situation above without forming the intermediate ; lst2. In particular, if lst1 contains an antagonistic pair, then it ; cannot be merged with any lst2 and we can quit. ; Note: Once upon a time, indeed, for the first 20 years or so of the ; existence of the merge routine, we took the attitude that if ; irreconcilable but agreeable alists arose, then we just added to ; alist2 those pairs of alist1 that were reconcilable and we left out ; the irreconcilable pairs. This however resulted in the system often ; merging complicated accumulator using functions (like TAUTOLOGYP) ; into simpler functions (like NORMALIZEDP) by dropping the ; accumulators that got in the way. This idea of just not doing ; "hostile merges" is being tried out for the first time in ACL2. (cond ((antagonistic-tests-and-alists-lstp lst1 vars) nil) ((antagonistic-tests-and-alists-lstsp lst1 lst2 vars) nil) ((not (every-alist1-matedp lst1 lst2 vars)) nil) (t (merge-lst1-into-lst2 lst1 lst2 vars)))) (defun merge-cand1-into-cand2 (cand1 cand2) ; Can induction candidate cand1 be merged into cand2? If so, return ; their merge. The guts of this function is merge-tests-and-alists- ; lsts. The tests preceding it are heuristic only. If ; merge-tests-and-alists-lsts returns non-nil, then it returns a sound ; induction; indeed, it merely extends some of the substitutions in ; the second candidate. (let ((vars (or (intersection-eq (access candidate cand1 :controllers) (intersection-eq (access candidate cand2 :controllers) (intersection-eq (access candidate cand1 :changed-vars) (access candidate cand2 :changed-vars)))) (intersection-eq (access candidate cand1 :changed-vars) (access candidate cand2 :changed-vars))))) ; Historical Plaque from Nqthm: ; We once merged only if both cands agreed on the intersection of the ; changed-vars. But the theorem that, under suitable conditions, (EV ; FLG X VA FA N) = (EV FLG X VA FA K) made us realize it was important ; only to agree on the intersection of the controllers. Note in fact ; that we mean the changing controllers -- there seems to be no need ; to merge two inductions if they only share unchanging controllers. ; However the theorem that (GET I (SET J VAL MEM)) = ... (GET I MEM) ; ... illustrates the situation in which the controllers, {I} and {J} ; do not even overlap; but the accumulators {MEM} do and we want a ; merge. So we want agreement on the intersection of the changing ; controllers (if that is nonempty) or on the accumulators. ; For soundness it does not matter what list of vars we want to agree ; on because no matter what, merge-tests-and-alists-lsts returns ; either nil or an extension of the second candidate's alists. (cond ((and vars (not (intersectp-eq (access candidate cand1 :unchangeable-vars) (access candidate cand2 :changed-vars))) (not (intersectp-eq (access candidate cand2 :unchangeable-vars) (access candidate cand1 :changed-vars)))) (let ((temp (merge-tests-and-alists-lsts (access candidate cand1 :tests-and-alists-lst) (access candidate cand2 :tests-and-alists-lst) vars))) (cond (temp (make candidate :score (+ (access candidate cand1 :score) (access candidate cand2 :score)) :controllers (union-eq (access candidate cand1 :controllers) (access candidate cand2 :controllers)) :changed-vars (union-eq (access candidate cand1 :changed-vars) (access candidate cand2 :changed-vars)) :unchangeable-vars (union-eq (access candidate cand1 :unchangeable-vars) (access candidate cand2 :unchangeable-vars)) :tests-and-alists-lst temp :justification (access candidate cand2 :justification) :induction-term (access candidate cand2 :induction-term) :other-terms (add-to-set-equal (access candidate cand1 :induction-term) (union-equal (access candidate cand1 :other-terms) (access candidate cand2 :other-terms))) :xinduction-term (access candidate cand2 :xinduction-term) :xother-terms (add-to-set-equal (access candidate cand1 :xinduction-term) (union-equal (access candidate cand1 :xother-terms) (access candidate cand2 :xother-terms))) :xancestry (cond ((equal temp (access candidate cand2 :tests-and-alists-lst)) (access candidate cand2 :xancestry)) (t (add-to-set-equal (access candidate cand1 :xinduction-term) (union-equal (access candidate cand1 :xancestry) (access candidate cand2 :xancestry))))) ; Note that :xancestry, computed just above, may not reflect cand1, but we ; always include the :ttree of cand1 just below. This is deliberate, since ; cand1 is contributing to the :score, and hence the eventual induction scheme ; chosen; so we want to report its induction rules in the final proof. :ttree (cons-tag-trees (access candidate cand1 :ttree) (access candidate cand2 :ttree)))) (t nil)))) (t nil)))) (defun merge-candidates (cand1 cand2) ; This function determines whether one of the two induction candidates ; can be merged into the other. If so, it returns their merge. This ; function is a mate and merge function used by m&m and is hence known ; to m&m-apply. (or (merge-cand1-into-cand2 cand1 cand2) (merge-cand1-into-cand2 cand2 cand1))) ; We now move from merging to flawing. The idea is to determine which ; inductions get in the way of others. (defun controller-variables1 (args controller-pocket) ; Controller-pocket is a list of t's and nil's in 1:1 correspondence with ; args, indicating which args are controllers. We collect those controller ; args that are variable symbols. (cond ((null controller-pocket) nil) ((and (car controller-pocket) (variablep (car args))) (add-to-set-eq (car args) (controller-variables1 (cdr args) (cdr controller-pocket)))) (t (controller-variables1 (cdr args) (cdr controller-pocket))))) (defun controller-variables (term controller-alist) ; Controller-alist comes from the def-body of the function symbol, fn, of term. ; Recall that controller-alist is an alist that associates with each function ; in fn's mutually recursive clique the controller pockets used in a given ; justification of the clique. In induction, as things stand today, we know ; that fn is singly recursive because we don't know how to handle mutual ; recursion yet. But no use is made of that here. We collect all the ; variables in controller slots of term. (controller-variables1 (fargs term) (cdr (assoc-eq (ffn-symb term) controller-alist)))) (defun induct-vars1 (lst wrld) ; Lst is a list of terms. We collect every variable symbol occuring in a ; controller slot of any term in lst. (cond ((null lst) nil) (t (union-eq (controller-variables (car lst) (access def-body (def-body (ffn-symb (car lst)) wrld) :controller-alist)) (induct-vars1 (cdr lst) wrld))))) (defun induct-vars (cand wrld) ; Historical Plaque from Nqthm: ; Get all skos occupying controller slots in any of the terms associated ; with this candidate. ; The age of that comment is not known, but the fact that we referred ; to the variables as "skos" (Skolem constants) suggests that it may ; date from the Interlisp version. Meta comment: Perhaps someday some ; enterprising PhD student (in History?) will invent Software ; Archeology, in which decrepit fragments of archive tapes are pieced ; together and scrutinized for clues as to the way people thought back ; in the early days. (induct-vars1 (cons (access candidate cand :induction-term) (access candidate cand :other-terms)) wrld)) (defun vetoedp (cand vars lst changed-vars-flg) ; Vars is a list of variables. We return t iff there exists a candidate ; in lst, other than cand, whose unchangeable-vars (or, if changed-vars-flg, ; changed-vars or unchangeable-vars) intersect with vars. ; This function is used both by compute-vetoes1, where flg is t and ; vars is the list of changing induction vars of cand, and by ; compute-vetoes2, where flg is nil and vars is the list of ; changed-vars cand. We combine these two into one function simply to ; eliminate a definition from the system. (cond ((null lst) nil) ((equal cand (car lst)) (vetoedp cand vars (cdr lst) changed-vars-flg)) ((and changed-vars-flg (intersectp-eq vars (access candidate (car lst) :changed-vars))) t) ((intersectp-eq vars (access candidate (car lst) :unchangeable-vars)) t) (t (vetoedp cand vars (cdr lst) changed-vars-flg)))) (defun compute-vetoes1 (lst cand-lst wrld) ; Lst is a tail of cand-lst. We throw out from lst any candidate ; whose changing induct-vars intersect the changing or unchanging vars ; of another candidate in cand-lst. We assume no two elements of ; cand-lst are equal, an invariant assured by the fact that we have ; done merging and flushing before this. (cond ((null lst) nil) ((vetoedp (car lst) (intersection-eq (access candidate (car lst) :changed-vars) (induct-vars (car lst) wrld)) cand-lst t) (compute-vetoes1 (cdr lst) cand-lst wrld)) (t (cons (car lst) (compute-vetoes1 (cdr lst) cand-lst wrld))))) ; If the first veto computation throws out all candidates, we revert to ; another heuristic. (defun compute-vetoes2 (lst cand-lst) ; Lst is a tail of cand-lst. We throw out from lst any candidate ; whose changed-vars intersect the unchangeable-vars of another ; candidate in cand-lst. Again, we assume no two elements of cand-lst ; are equal. (cond ((null lst) nil) ((vetoedp (car lst) (access candidate (car lst) :changed-vars) cand-lst nil) (compute-vetoes2 (cdr lst) cand-lst)) (t (cons (car lst) (compute-vetoes2 (cdr lst) cand-lst))))) (defun compute-vetoes (cand-lst wrld) ; We try two different techniques for throwing out candidates. If the ; first throws out everything, we try the second. If the second throws ; out everything, we throw out nothing. ; The two are: (1) throw out a candidate if its changing induct-vars ; (the variables in control slots that change) intersect with either ; the changed-vars or the unchangeable-vars of another candidate. (2) ; throw out a candidate if its changed-vars intersect the ; unchangeable-vars of another candidate. ; Historical Plaque from Nqthm: ; This function weeds out "unclean" induction candidates. The ; intuition behind the notion "clean" is that an induction is clean ; if nobody is competing with it for instantiation of its variables. ; What we actually do is throw out any candidate whose changing ; induction variables -- that is the induction variables as computed ; by induct-vars intersected with the changed vars of candidate -- ; intersect the changed or unchanged variables of another candidate. ; The reason we do not care about the first candidates unchanging ; vars is as follows. The reason you want a candidate clean is so ; that the terms riding on that cand will reoccur in both the ; hypothesis and conclusion of an induction. There are two ways to ; assure (or at least make likely) this: change the variables in the ; terms as specified or leave them constant. Thus, if the first ; cand's changing vars are clean but its unchanging vars intersect ; another cand it means that the first cand is keeping those other ; terms constant, which is fine. (Note that the first cand would be ; clean here. The second might be clean or dirty depending on ; whether its changed vars or unchanged vars intersected the first ; cand's vars.) The reason we check only the induction vars and not ; all of the changed vars is if cand1's changed vars include some ; induction vars and some accumulators and the accumulators are ; claimed by another cand2 we believe that cand1 is still clean. ; The motivating example was ; (IMPLIES (MEMBER A C) (MEMBER A (UNION1 B C))) ; where the induction on C is dirty because the induction on B and C ; claims C, but the induction on B and C is clean because the B does ; not occur in the C induction. We do not even bother to check the ; C from the (B C) induction because since it is necessarily an ; accumulator it is probably being constructed and thus, if it ; occurs in somebody else's ind vars it is probably being eaten so ; it will be ok. In formulating this heuristic we did not consider ; the possibility that the accums of one candidate occur as ; constants in the other. Oh well. ; July 20, 1978. We have added an additional heuristic, to be ; applied if the above one eliminates all cands. We consider a cand ; flawed if it changes anyone else's constants. The motivating ; example was GREATEST-FACTOR-LESSP -- which was previously proved ; only by virtue of a very ugly use of the no-op fn ID to make a ; certain induction flawed. (or (compute-vetoes1 cand-lst cand-lst wrld) (compute-vetoes2 cand-lst cand-lst) cand-lst)) ; The next heuristic is to select complicated candidates, based on ; support for non-primitive recursive function schemas. (defun induction-complexity1 (lst wrld) ; The "function" induction-complexity does not exist. It is a symbol ; passed to maximal-elements-apply which calls this function on the list ; of terms supported by an induction candidate. We count the number of ; non pr fns supported. (cond ((null lst) 0) ((getprop (ffn-symb (car lst)) 'primitive-recursive-defunp nil 'current-acl2-world wrld) (induction-complexity1 (cdr lst) wrld)) (t (1+ (induction-complexity1 (cdr lst) wrld))))) ; We develop a general-purpose function for selecting maximal elements from ; a list under a measure. That function, maximal-elements, is then used ; with the induction-complexity measure to collect both the most complex ; inductions and then to select those with the highest scores. (defun maximal-elements-apply (fn x wrld) ; This function must produce an integerp. This is just the apply function ; for maximal-elements. (case fn (induction-complexity (induction-complexity1 (cons (access candidate x :induction-term) (access candidate x :other-terms)) wrld)) (score (access candidate x :score)) (otherwise 0))) (defun maximal-elements1 (lst winners maximum fn wrld) ; We are scanning down lst collecting into winners all those elements ; with maximal scores as computed by fn. Maximum is the maximal score seen ; so far and winners is the list of all the elements passed so far with ; that score. (cond ((null lst) winners) (t (let ((temp (maximal-elements-apply fn (car lst) wrld))) (cond ((> temp maximum) (maximal-elements1 (cdr lst) (list (car lst)) temp fn wrld)) ; PETE ; In other versions the = below is, mistakenly, an int=! ((= temp maximum) (maximal-elements1 (cdr lst) (cons (car lst) winners) maximum fn wrld)) (t (maximal-elements1 (cdr lst) winners maximum fn wrld))))))) (defun maximal-elements (lst fn wrld) ; Return the subset of lst that have the highest score as computed by ; fn. The functional parameter fn must be known to maximal-elements-apply. ; We reverse the accumulated elements to preserve the order used by ; nqthm. (cond ((null lst) nil) ((null (cdr lst)) lst) (t (reverse (maximal-elements1 (cdr lst) (list (car lst)) (maximal-elements-apply fn (car lst) wrld) fn wrld))))) ; All that is left in the heuristic selection of the induction candidate is ; the function m&m that mates and merges arbitrary objects. We develop that ; now. ; The following three functions are not part of induction but are ; used by other callers of m&m and so have to be introduced now ; so we can define m&m-apply and get on with induct. (defun intersectp-eq/union-equal (x y) (cond ((intersectp-eq (car x) (car y)) (cons (union-eq (car x) (car y)) (union-equal (cdr x) (cdr y)))) (t nil))) (defun equal/union-equal (x y) (cond ((equal (car x) (car y)) (cons (car x) (union-equal (cdr x) (cdr y)))) (t nil))) (defun subsetp-equal/smaller (x y) (cond ((subsetp-equal x y) x) ((subsetp-equal y x) y) (t nil))) (defun m&m-apply (fn x y) ; This is a first-order function that really just applies fn to x and ; y, but does so only for a fixed set of fns. In fact, this function ; handles exactly those functions that we give to m&m. (case fn (intersectp-eq/union-equal (intersectp-eq/union-equal x y)) (equal/union-equal (equal/union-equal x y)) (flush-candidates (flush-candidates x y)) (merge-candidates (merge-candidates x y)) (subsetp-equal/smaller (subsetp-equal/smaller x y)))) (defun count-off (n lst) ; Pair the elements of lst with successive integers starting at n. (cond ((null lst) nil) (t (cons (cons n (car lst)) (count-off (1+ n) (cdr lst)))))) (defun m&m-search (x y-lst del fn) ; Y-lst is a list of pairs, (id . y). The ids are integers. If id is ; a member of del, we think of y as "deleted" from y-lst. That is, ; y-lst and del together characterize a list of precisely those y such ; that (id . y) is in y-lst and id is not in del. ; We search y-lst for the first y that is not deleted and that mates ; with x. We return two values, the merge of x and y and the integer ; id of y. If no such y exists, return two nils. (cond ((null y-lst) (mv nil nil)) ((member (caar y-lst) del) (m&m-search x (cdr y-lst) del fn)) (t (let ((z (m&m-apply fn x (cdar y-lst)))) (cond (z (mv z (caar y-lst))) (t (m&m-search x (cdr y-lst) del fn))))))) (defun m&m1 (pairs del ans n fn) ; This is workhorse for m&m. See that fn for a general description of ; the problem and the terminology. Pairs is a list of pairs. The car ; of each pair is an integer and the cdr is a possible element of the ; bag we are closing under fn. Del is a list of the integers ; identifying all the elements of pairs that have already been ; deleted. Abstractly, pairs and del together represent a bag we call ; the "unprocessed bag". The elements of the unprocessed bag are ; precisely those ele such that (i . ele) is in pairs and i is not in ; del. ; Without assuming any properties of fn, this function can be ; specified as follows: If the first element, x, of the unprocessed ; bag, mates with some y in the rest of the uprocessed bag, then put ; the merge of x and the first such y in place of x, delete that y, ; and iterate. If the first element has no such mate, put it in the ; answer accumulator ans. N, by the way, is the next available unique ; identifier integer. ; If one is willing to make the assumptions that the mate and merge ; fns of fn are associative and commutative and have the distributive ; and non-preclusion properties, then it is possible to say more about ; this function. The rest of this comment makes those assumptions. ; Ans is a bag with the property that no element of ans mates with any ; other element of ans or with any element of the unprocessed bag. N ; is the next available unique identifier integer; it is always larger ; than any such integer in pairs or in del. ; Abstractly, this function closes B under fn, where B is the bag ; union of the unprocessed bag and ans. (cond ((null pairs) ans) ((member (caar pairs) del) (m&m1 (cdr pairs) del ans n fn)) (t (mv-let (mrg y-id) (m&m-search (cdar pairs) (cdr pairs) del fn) (cond ((null mrg) (m&m1 (cdr pairs) del (cons (cdar pairs) ans) n fn)) (t (m&m1 (cons (cons n mrg) (cdr pairs)) (cons y-id del) ans (1+ n) fn))))))) (defun m&m (bag fn) ; This function takes a bag and a symbol naming a dyadic function, fn, ; known to m&m-apply and about which we assume certain properties ; described below. Let z be (m&m-apply fn x y). Then we say x and y ; "mate" if z is non-nil. If x and y mate, we say z is the "merge" of ; x and y. The name of this function abbreviates the phrase "mate and ; merge". ; We consider each element, x, of bag in turn and seek the first ; successive element, y, that mates with it. If we find one, we throw ; out both, add their merge in place of x and iterate. If we find no ; mate for x, we deposit it in our answer accumulator. ; The specification above is explicit about the order in which we try ; the elements of the bag. If we try to loosen the specification so ; that order is unimportant, we must require that fn have certain ; properties. We discuss this below. ; First, note that we have implicitly assumed that mate and merge are ; commutative because we haven't said in which order we present the ; arguments. ; Second, note that if x doesn't mate with any y, we set it aside in ; our accumulating answer. We do not even try to mate such an x with ; the offspring of the y's it didn't like. This makes us order ; dependent. For example, consider the bag {x y1 y2}. Suppose x ; won't mate with either y1 or y2, but that y1 mates with y2 to ; produce y3 and x mates with y3 to produce y4. Then if we seek mates ; for x first we find none and it gets into our final answer. Then y1 ; and y2 mate to form y3. The final answer is hence {x y3}. But if ; we seek mates for y1 first we find y2, produce y3, add it to the ; bag, forming {y3 x}, and then mate x with y3 to get the final answer ; {y4}. This order dependency cannot arise if fn has the property ; that if x mates with the merge of y and z then x mates with either y ; or z. This is called the "distributive" property of mate over merge. ; Third, note that if x does mate with y to produce z then we throw x ; out in favor of z. Thus, x is not mated against any but the first ; y. Thus, if we have {x y1 y2} and x mates with y1 to form z1 and x ; mates with y2 to form z2 and there are no other mates, then we can ; either get {z1 y2} or {z2 y1} as the final bag, depending on whether ; we mate x with y1 or y2. This order dependency cannot arise if fn ; has the property that if x mates with y1 and x mates with y2, then ; (a) the merge of x and y1 mates with y2, and (b) merge has the ; "commutativity-2" (merge (merge x y1) y2) = (merge (merge x y2) y1). ; We call property (a) "non-preclusion" property of mate and merge, ; i.e., merging doesn't preclude mating. ; The commutativity-2 property is implied by associativity and (the ; already assumed commutativity). Thus, another way to avoid the ; third order dependency is if legal merges are associative and have ; the non-preclusion property. ; Important Note: The commonly used fn of unioning together two alists ; that agree on the intersection of their domains, does not have the ; non-preclusion property! Suppose x, y1, and y2 are all alists and ; all map A to 0. Suppose in addition y1 maps B to 1 but y2 maps B to ; 2. Finally, suppose x maps C to 3. Then x mates with both y1 and ; y2. But merging y1 into x precludes mating with y2 and vice versa. ; We claim, but do not prove, that if the mate and merge functions for ; fn are commutative and associative, and have the distributive and ; non-preclusion properties, then m&m is order independent. ; For efficiency we have chosen to implement deletion by keeping a ; list of the deleted elements. But we cannot make a list of the ; deleted elements themselves because there may be duplicate elements ; in the bag and we need to be able to delete occurrences. Thus, the ; workhorse function actually operates on a list of pairs, (i . ele), ; where i is a unique identification integer and ele is an element of ; the bag. In fact we just assign the position of each occurrence to ; each element of the initial bag and thereafter count up as we ; generate new elements. ; ; See m&m1 for the details. (m&m1 (count-off 0 bag) nil nil (length bag) fn)) ; We now develop a much more powerful concept, that of mapping m&m over the ; powerset of a set. This is how we actually merge induction candidates. ; That is, we try to mash together every possible subset of the candidates, ; largest subsets first. See m&m-over-powerset for some implementation ; commentary before going on. (defun cons-subset-tree (x y) ; We are representing full binary trees of t's and nil's and ; collapsing trees of all nil's to nil and trees of all t's to t. See ; the long comment in m&m-over-powerset. We avoid consing when ; convenient. (if (eq x t) (if (eq y t) t (if y (cons x y) '(t))) (if x (cons x y) (if (eq y t) '(nil . t) (if y (cons x y) nil))))) (defabbrev car-subset-tree (x) ; See cons-subset-tree. (if (eq x t) t (car x))) (defabbrev cdr-subset-tree (x) ; See cons-subset-tree. (if (eq x t) t (cdr x))) (defun or-subset-trees (tree1 tree2) ; We disjoin the tips of two binary t/nil trees. See cons-subset-tree. (cond ((or (eq tree1 t)(eq tree2 t)) t) ((null tree1) tree2) ((null tree2) tree1) (t (cons-subset-tree (or-subset-trees (car-subset-tree tree1) (car-subset-tree tree2)) (or-subset-trees (cdr-subset-tree tree1) (cdr-subset-tree tree2)))))) (defun m&m-over-powerset1 (st subset stree ans fn) ; See m&m-over-powerset. (cond ((eq stree t) (mv t ans)) ((null st) (let ((z (m&m subset fn))) (cond ((and z (null (cdr z))) (mv t (cons (car z) ans))) (t (mv nil ans))))) (t (mv-let (stree1 ans1) (m&m-over-powerset1 (cdr st) (cons (car st) subset) (cdr-subset-tree stree) ans fn) (mv-let (stree2 ans2) (m&m-over-powerset1 (cdr st) subset (or-subset-trees (car-subset-tree stree) stree1) ans1 fn) (mv (cons-subset-tree stree2 stree1) ans2)))))) (defun m&m-over-powerset (st fn) ; Fn is a function known to m&m-apply. Let (fn* s) be defined to be z, ; if (m&m s fn) = {z} and nil otherwise. Informally, (fn* s) is the ; result of somehow mating and merging all the elements of s into a single ; object, or nil if you can't. ; This function applies fn* to the powerset of st and collects all those ; non-nil values produced from maximal s's. I.e., we keep (fn* s) iff it ; is non-nil and no superset of s produces a non-nil value. ; We do this amazing feat (recall that the powerset of a set of n ; things contains 2**n subsets) by generating the powerset in order ; from largest to smallest subsets and don't generate or test any ; subset under a non-nil fn*. Nevertheless, if the size of set is ; very big, this function will get you. ; An informal specification of this function is that it is like m&m ; except that we permit an element to be merged into more than one ; other element (but an element can be used at most once per final ; element) and we try to maximize the amount of merging we can do. ; For example, if x mates with y1 to form z1, and x mates with y2 to ; form z2, and no other mates occur, then this function would ; transform {x y1 y2} into {z1 z2}. It searches by generate and test: ; s (fn* s) ; (x y1 y2) nil ; (x y1) z1 ; (x y2) z2 ; (x) subsumed ; (y1 y2) nil ; (y1) subsumed ; (y2) subsumed ; nil subsumed ; Here, s1 is "subsumed" by s2 means s1 is a subset of s2. (Just the ; opposite technical definition but exactly the same meaning as in the ; clausal sense.) ; The way we generate the powerset elements is suggested by the ; following trivial von Neumann function, ps, which, when called as in ; (ps set nil), calls PROCESS on each member of the powerset of set, ; in the order in which we generate them: ; (defun ps (set subset) ; (cond ((null set) (PROCESS subset)) ; (t (ps (cdr set) (cons (car set) subset)) ;rhs ; (ps (cdr set) subset)))) ;lhs ; By generating larger subsets first we know that if a subset subsumes ; the set we are considering then that subset has already been ; considered. Therefore, we need a way to keep track of the subsets ; with non-nil values. We do this with a "subset tree". Let U be the ; universe of objects in some order. Then the full binary tree with ; depth |U| can be thought of as the powerset of U. In particular, ; any branch through the tree, from top-most node to tip, represents a ; subset of U by labelling the nodes at successive depth by the ; successive elements of U (the topmost node being labelled with the ; first element of U) and adopting the convention that taking a ; right-hand (cdr) branch at a node indicates that the label is in the ; subset and a left-hand (car) branch indicates that the label is not ; in the subset. At the tip of the tree we store a T indicating that ; the subset had a non-nil value or a NIL indicating that it had a nil ; value. ; For storage efficiency we let nil represent an arbitrarily deep full ; binary tree will nil at every tip and we let t represent the ; analogous trees with t at every tip. Car-subset-tree, ; cdr-subset-tree and cons-subset-tree implement these abstractions. ; Of course, we don't have the tree when we start and we generate it ; as we go. That is a really weird idea because generating the tree ; that tells us who was a subset of whom in the past seems to have little ; use as we move forward. But that is not true. ; Observe that there is a correspondence between these trees and the ; function ps above for generating the power set. The recursion ; labelled "rhs" above is going down the right-hand side of the tree ; and the "lhs" recursion is going down the left-hand side. Note that ; we go down the rhs first. ; The neat fact about these trees is that there is a close ; relationship between the right-hand subtree (rhs) and left-hand ; subtree (lhs) of any given node of the tree: lhs can be obtained ; from rhs by turning some nils into ts. The reason is that the tips ; of the lhs of a node labelled by x denote exactly the same subsets ; as the corresponding tips of the right-hand side, except that on the ; right x was present in the subset and on the left it is not. So ; when we do the right hand side we come back with a tree and if we ; used that very tree for the left hand side (interpreting nil as ; meaning "compute it and see" and t as meaning "a superset of this ; set has non-nil value") then it is correct. But we can do a little ; better than that because we might have come into this node with a ; tree (i.e., one to go into the right hand side with and another to go ; into the left hand side with) and so after we have gone into the ; right and come back with its new tree, we can disjoin the output of ; the right side with the input for the left side to form the tree we ; will actually use to explore the left side. (mv-let (stree ans) (m&m-over-powerset1 st nil nil nil fn) (declare (ignore stree)) ans)) ; Ok, so now we have finished the selection process and we begin the ; construction of the induction formula itself. (defun all-picks2 (pocket pick ans) ; See all-picks. (cond ((null pocket) ans) (t (cons (cons (car pocket) pick) (all-picks2 (cdr pocket) pick ans))))) (defun all-picks2r (pocket pick ans) ; See all-picks. (cond ((null pocket) ans) (t (all-picks2r (cdr pocket) pick (cons (cons (car pocket) pick) ans))))) (defun all-picks1 (pocket picks ans rflg) ; See all-picks. (cond ((null picks) ans) (t (all-picks1 pocket (cdr picks) (if rflg (all-picks2r pocket (car picks) ans) (all-picks2 pocket (car picks) ans)) rflg)))) (defun all-picks (pockets rflg) ; Pockets is a list of pockets, each pocket containing 0 or more ; objects. We return a list of all the possible ways you can pick one ; thing from each pocket. If rflg is nil initially, then the order of ; the resulting list is exactly the same as it was in nqthm. There is ; not much else to recommend this particular choice of definition! ; Historical Plaque from Nqthm: ; (DEFUN ALL-PICKS (POCKET-LIST) ; (COND ((NULL POCKET-LIST) (LIST NIL)) ; (T (ITERATE FOR PICK IN (ALL-PICKS (CDR POCKET-LIST)) ; NCONC (ITERATE FOR CHOICE IN (CAR POCKET-LIST) ; COLLECT (CONS CHOICE PICK)))))) ; Nqthm's construction is a very natural recursive one, except that it ; used nconc to join together the various segments of the answer. If ; we tried the analogous construction here we would have to append the ; segments together and copy a very long list. So we do it via an ; accumulator. The trouble however is that we reverse the order of ; the little buckets in our answer every time we process a pocket. We ; could avoid that if we wanted to recurse down the length of our ; answer on recursive calls, but we were afraid of running out of ; stack, and so we have coded this with tail recursion only. We do ; non-tail recursion only over short things like individual pockets or ; the list of pockets. And so to (a) avoid unnecessary copying, (b) ; non-tail recursion, and (c) constructing our answer in a different ; order, we introduced rflg. Rflg causes us either to reverse or not ; reverse a certain intermediate result every other recursion. It ; would be reassuring to see a mechanically checked proof that this ; definition of all-picks is equivalent to nqthm's. (cond ((null pockets) '(nil)) (t (all-picks1 (car pockets) (all-picks (cdr pockets) (not rflg)) nil rflg)))) (defun dumb-negate-lit-lst-lst (cl-set) ; We apply dumb-negate-lit-lst to every list in cl-set and collect the ; result. You can think of this as negating a clause set (i.e., an ; implicit conjunction of disjunctions), but you have to then imagine ; that the implicit "and" at the top has been turned into an "or" and ; vice versa at the lower level. (cond ((null cl-set) nil) (t (cons (dumb-negate-lit-lst (car cl-set)) (dumb-negate-lit-lst-lst (cdr cl-set)))))) (defun induction-hyp-clause-segments2 (alists cl ans) ; See induction-hyp-clause-segments1. (cond ((null alists) ans) (t (cons (sublis-var-lst (car alists) cl) (induction-hyp-clause-segments2 (cdr alists) cl ans))))) (defun induction-hyp-clause-segments1 (alists cl-set ans) ; This function applies all of the substitutions in alists to all of ; the clauses in cl-set and appends the result to ans to create one ; list of instantiated clauses. (cond ((null cl-set) ans) (t (induction-hyp-clause-segments2 alists (car cl-set) (induction-hyp-clause-segments1 alists (cdr cl-set) ans))))) (defun induction-hyp-clause-segments (alists cl-set) ; Cl-set is a set of clauses. We are trying to prove the conjunction ; over that set, i.e., cl1 & cl2 ... & clk, by induction. We are in a ; case in which we can assume every instance under alists of that ; conjunction. Thus, we can assume any lit from cl1, any lit from ; cl2, etc., instantiated via all of the alists. We wish to return a ; list of clause segments. Each segment will be spliced into the a ; clause we are trying to prove and together the resulting set of ; clauses is supposed to be equivalent to assuming all instances of ; the conjunction over cl-set. ; So one way to create the answer would be to first instantiate each ; of the k clauses with each of the n alists, getting a set of n*k ; clauses. Then we could run all-picks over that, selecting one ; literal from each of the instantiated clauses to assume. Then we'd ; negate each literal within each pick to create a clause hypothesis ; segment. That is nearly what we do, except that we do the negation ; first so as to share structure among the all-picks answers. ; Note: The code below calls (dumb-negate-lit lit) on each lit. Nqthm ; used (negate-lit lit nil ...) on each lit, employing ; negate-lit-lst-lst, which has since been deleted but was strictly ; analogous to the dumb version called below. But since the ; type-alist is nil in Nqthm's call, it seems unlikely that the ; literal will be decided by type-set. We changed to dumb-negate-lit ; to avoid having to deal both with ttrees and the enabled structure ; implicit in type-set. (all-picks (induction-hyp-clause-segments1 alists (dumb-negate-lit-lst-lst cl-set) nil) nil)) (defun induction-formula3 (neg-tests hyp-segments cl ans) ; Neg-tests is the list of the negated tests of an induction ; tests-and-alists entry. hyp-segments is a list of hypothesis clause ; segments (i.e., more negated tests), and cl is a clause. For each ; hyp segment we create the clause obtained by disjoining the tests, ; the segment, and cl. We conjoin the resulting clauses to ans. ; See induction-formula for a comment about this iteration. (cond ((null hyp-segments) ans) (t (induction-formula3 neg-tests (cdr hyp-segments) cl (conjoin-clause-to-clause-set ; Historical Plaque from Nqthm: ; We once implemented the idea of "homographication" in which we combined ; both induction, opening up of the recursive fns in the conclusion, and ; generalizing away some recursive calls. This function did the expansion ; and generalization. If the idea is reconsidered the following theorems are ; worthy of consideration: ; (ORDERED (SORT X)), ; (IMPLIES (ORDERED X) ; (ORDERED (ADDTOLIST I X))), ; (IMPLIES (AND (NUMBER-LISTP X) ; (ORDERED X) ; (NUMBERP I) ; (NOT (< (CAR X) I))) ; (EQUAL (ADDTOLIST I X) (CONS I X))), and ; (IMPLIES (AND (NUMBER-LISTP X) (ORDERED X)) (EQUAL (SORT X) X)). ; Observe that we simply disjoin the negated tests, hyp segments, and clause. ; Homographication further manipulated the clause before adding it to the ; answer. (disjoin-clauses neg-tests (disjoin-clauses (car hyp-segments) cl)) ans))))) (defun induction-formula2 (cl cl-set ta-lst ans) ; Cl is a clause in cl-set, which is a set of clauses we are proving ; by induction. Ta-lst is the tests-and-alists-lst component of the ; induction candidate we are applying to prove cl-set. We are now ; focussed on the proof of cl, using the induction schema of ta-lst ; but getting to assume all the clauses in cl-set in our induction ; hypothesis. We will map across ta-lst, getting a set of tests and ; some alists at each stop, and for each stop add a bunch of clauses ; to ans. (cond ((null ta-lst) ans) (t (induction-formula2 cl cl-set (cdr ta-lst) (induction-formula3 ; Note: Nqthm used (negate-lit-lst ... nil ...), but since the ; type-alist supplied was nil, we decided it was probably no buying us ; much -- not as much as passing up the ttrees would cost in terms of ; coding work! (dumb-negate-lit-lst (access tests-and-alists (car ta-lst) :tests)) (induction-hyp-clause-segments (access tests-and-alists (car ta-lst) :alists) cl-set) cl ans))))) (defun induction-formula1 (lst cl-set ta-lst ans) ; Lst is a tail of cl-set. Cl-set is a set of clauses we are trying to prove. ; Ta-lst is the tests-and-alists-lst component of the induction candidate ; we wish to apply to cl-set. We map down lst forming a set of clauses ; for each cl in lst. Basically, the set we form for cl is of the form ; ... -> cl, where ... involves all the case analysis under the tests in ; ta-lst and all the induction hypotheses from cl-set under the alists in ; each test-and-alists. We add our clauses to ans. (cond ((null lst) ans) (t (induction-formula1 (cdr lst) cl-set ta-lst (induction-formula2 (car lst) cl-set ta-lst ans))))) (defun induction-formula (cl-set ta-lst) ; Cl-set is a set of clauses we are to try to prove by induction, applying ; the inductive scheme described by the tests-and-alists-lst, ta-lst, ; of some induction candidate. The following historical plaque tells all. ; Historical Plaque from Nqthm: ; TESTS-AND-ALISTS-LST is a such a list that the disjunction of the ; conjunctions of the TESTS components of the members is T. Furthermore, ; there exists a measure M, a well-founded relation R, and a sequence of ; variables x1, ..., xn such that for each T&Ai in TESTS-AND-ALISTS-LST, for ; each alist alst in the ALISTS component of T&Ai, the conjunction of the ; TESTS component, say qi, implies that (R (M x1 ... xn)/alst (M x1 ... xn)). ; To prove thm, the conjunction of the disjunctions of the members of CL-SET, ; it is sufficient, by the principle of induction, to prove instead the ; conjunction of the terms qi & thm' & thm'' ... -> thm, where the primed ; terms are the results of substituting the alists in the ALISTS field of the ; ith member of TESTS-AND-ALISTS-LST into thm. ; If thm1, thm2, ..., thmn are the disjunctions of the members of CL-SET, ; then it is sufficient to prove all of the formulas qi & thm' & thm'' ... ; -> thmj. This is a trivial proposition fact, to prove (IMPLIES A (AND B ; C)) it is sufficient to prove (IMPLIES A B) and (IMPLIES A C). ; The (ITERATE FOR PICK ...)* expression below returns a list of ; clauses whose conjunction propositionally implies qi & thm' & ; thm'' ... -> thmj, where TA is the ith member of ; TESTS-AND-ALISTS-LST and CL is the jth member of CL-SET. Proof: ; Let THM have the form: ; ; (AND (OR a1 ...) ; (OR b1 ...) ; ... ; (OR z1 ...)). ; Then qi & thm' & thm'' ... -> thmj has the form: ; (IMPLIES (AND qi ; (AND (OR a1 ... ) ; (OR b1 ... ) ; ... ; (OR z1 ... ))' ; (AND (OR a1 ... ) ; (OR b1 ... ) ; ... ; (OR z1 ... ))'' ; ... ; (AND (OR a1 ... ) ; (OR b1 ... ) ; ... ; (OR z1 ... )))'''...' ; thmj). ; ; Suppose this formula is false for some values of the free variables. Then ; under those values, each disjunction in the hypothesis is true. Thus there ; exists a way of choosing one literal from each of the disjunctions, all of ; which are true. This choice is one of the PICKs below. But we prove that ; (IMPLIES (AND qi PICK) thmj). ; Note: The (ITERATE FOR PICK ...) expression mentioned above is the function ; induction-formula3 above. (m&m (reverse (induction-formula1 cl-set cl-set ta-lst nil)) 'subsetp-equal/smaller)) ; Because the preceding computation is potentially explosive we will ; sometimes reduce its complexity by shrinking the given clause set to ; a singleton set containing a unit clause. To decide whether to do that ; we will use the following rough measures: (defun all-picks-size (cl-set) ; This returns the size of the all-picks computed by induction-formula3. (cond ((null cl-set) 1) (t (* (length (car cl-set)) (all-picks-size (cdr cl-set)))))) (defun induction-formula-size1 (hyps-size concl-size ta-lst) ; We determine roughly the number of clauses that ta-lst will generate when ; the number of all-picks through the hypotheses is hyps-size and the ; number of conclusion clauses is concl-size. The individual cases of ; the tests-and-alists combine additively. But we must pick our way through ; the hyps for each instantiation. (cond ((null ta-lst) 0) (t (+ (* concl-size (expt hyps-size (length (access tests-and-alists (car ta-lst) :alists)))) (induction-formula-size1 hyps-size concl-size (cdr ta-lst)))))) (defun induction-formula-size (cl-set ta-lst) ; This function returns a rough upper bound on the number of clauses ; that will be generated by induction-formula on the given arguments. ; See the comment in that function. (induction-formula-size1 (all-picks-size cl-set) (length cl-set) ta-lst)) ; The following constant determines the limit on the estimated number of ; clauses induct, below, will return. When normal processing would exceed ; this number, we try to cut down the combinatorics by collapsing clauses ; back into terms. (defconst *maximum-induct-size* 100) ; And here is how we convert a hairy set of clauses into a term when we ; have to. (defun termify-clause-set (clauses) ; This function is similar to termify-clause except that it converts a ; set of clauses into an equivalent term. The set of clauses is ; understood to be implicitly conjoined and we therefore produce a ; conjunction expressed as (if cl1 cl2 nil). (cond ((null clauses) *t*) ((null (cdr clauses)) (disjoin (car clauses))) (t (mcons-term* 'if (disjoin (car clauses)) (termify-clause-set (cdr clauses)) *nil*)))) ; Once we have created the set of clauses to prove, we inform the ; simplifier of what to look out for during the early processing. (defun inform-simplify3 (alist terms ans) ; Instantiate every term in terms with alist and add them to ans. (cond ((null terms) ans) (t (inform-simplify3 alist (cdr terms) (add-to-set-equal (sublis-var alist (car terms)) ans))))) (defun inform-simplify2 (alists terms ans) ; Using every alist in alists, instantiate every term in terms and add ; them all to ans. (cond ((null alists) ans) (t (inform-simplify2 (cdr alists) terms (inform-simplify3 (car alists) terms ans))))) (defun inform-simplify1 (ta-lst terms ans) ; Using every alist mentioned in any tests-and-alists record of ta-lst ; we instantiate every term in terms and add them all to ans. (cond ((null ta-lst) ans) (t (inform-simplify1 (cdr ta-lst) terms (inform-simplify2 (access tests-and-alists (car ta-lst) :alists) terms ans))))) (defun inform-simplify (ta-lst terms pspv) ; Historical Plaque from Nqthm: ; Two of the variables effecting REWRITE are TERMS-TO-BE-IGNORED-BY-REWRITE ; and EXPAND-LST. When any term on the former is encountered REWRITE returns ; it without rewriting it. Terms on the latter must be calls of defined fns ; and when encountered are replaced by the rewritten body. ; We believe that the theorem prover will perform significantly faster on ; many theorems if, after an induction, it does not waste time (a) trying to ; simplify the recursive calls introduced in the induction hypotheses and (b) ; trying to decide whether to expand the terms inducted for in the induction ; conclusion. This suspicion is due to some testing done with the idea of ; "homographication" which was just a jokingly suggested name for the idea of ; generalizing the recursive calls away at INDUCT time after expanding the ; induction terms in the conclusion. Homographication speeded the ; theorem-prover on many theorems but lost on several others because of the ; premature generalization. See the comment in FORM-INDUCTION-CLAUSE. ; To avoid the generalization at INDUCT time we are going to try using ; TERMS-TO-BE-IGNORED-BY-REWRITE. The idea is this, during the initial ; simplification of a clause produced by INDUCT we will have the recursive ; terms on TERMS-TO-BE-IGNORED-BY-REWRITE. When the clause settles down -- ; hopefully it will often be proved first -- we will restore ; TERMS-TO-BE-IGNORED-BY-REWRITE to its pre-INDUCT value. Note however that ; we have to mess with TERMS-TO-BE-IGNORED-BY-REWRITE on a clause by clause ; basis, not just once in INDUCT. ; So here is the plan. INDUCT will set INDUCTION-HYP-TERMS to the list of ; instances of the induction terms, and will set INDUCTION-CONCL-TERMS to the ; induction terms themselves. SIMPLIFY-CLAUSE will look at the history of ; the clause to determine whether it has settled down since induction. If ; not it will bind TERMS-TO-BE-IGNORED-BY-REWRITE to the concatenation of ; INDUCTION-HYP-TERMS and its old value and will analogously bind EXPAND-LST. ; A new process, called SETTLED-DOWN-SENT, will be used to mark when in the ; history the clause settled down. ; In a departure from Nqthm, starting with Version_2.8, we do not wait for ; settled-down before turning off the above special consideration given to ; induction-hyp-terms and induction-concl-terms. See simplify-clause for ; details. (change prove-spec-var pspv :induction-concl-terms terms :induction-hyp-terms (inform-simplify1 ta-lst terms nil))) ; Ok, except for our output and putting it all together, that's induction. ; We now turn to the output. Induct prints two different messages. One ; reports the successful choice of an induction. The other reports failure. (defun all-vars1-lst-lst (lst ans) ; Lst is a list of lists of terms. For example, it might be a set of ; clauses. We compute the set of all variables occuring in it. (cond ((null lst) ans) (t (all-vars1-lst-lst (cdr lst) (all-vars1-lst (car lst) ans))))) (defun gen-new-name1 (char-lst wrld i) (let ((name (intern (coerce (append char-lst (explode-nonnegative-integer i 10 nil)) 'string) "ACL2"))) (cond ((new-namep name wrld) name) (t (gen-new-name1 char-lst wrld (1+ i)))))) (defun gen-new-name (root wrld) ; Create from the symbol root a possibly different symbol that ; is a new-namep in wrld. (cond ((new-namep root wrld) root) (t (gen-new-name1 (coerce (symbol-name root) 'list) wrld 0)))) (defun unmeasured-variables3 (vars alist) ; See unmeasured-variables. (cond ((null alist) nil) ((or (member-eq (caar alist) vars) (eq (caar alist) (cdar alist))) (unmeasured-variables3 vars (cdr alist))) (t (cons (caar alist) (unmeasured-variables3 vars (cdr alist)))))) (defun unmeasured-variables2 (vars alists) ; See unmeasured-variables. (cond ((null alists) nil) (t (union-eq (unmeasured-variables3 vars (car alists)) (unmeasured-variables2 vars (cdr alists)))))) (defun unmeasured-variables1 (vars ta-lst) ; See unmeasured-variables. (cond ((null ta-lst) nil) (t (union-eq (unmeasured-variables2 vars (access tests-and-alists (car ta-lst) :alists)) (unmeasured-variables1 vars (cdr ta-lst)))))) (defun unmeasured-variables (measured-vars cand) ; Measured-vars is the :subset of measured variables from the measure of term, ; computed above, for cand. We collect those variables that are changed by ; some substitution but are not measured by our induction measure. These are ; simply brought to the user's attention because we find it often surprising to ; see them. (unmeasured-variables1 measured-vars (access candidate cand :tests-and-alists-lst))) (defun tilde-@-well-founded-relation-phrase (rel wrld) ; We return a ~@ message that prints as "the relation rel (which, by name, is ; known to be well-founded on the domain recognized by mp)" and variants of ; that obtained when name is nil (meaning the well-foundedness is built in) ; and/or mp is t (meaning the domain is the universe). (let* ((temp (assoc-eq rel (global-val 'well-founded-relation-alist wrld))) (mp (cadr temp)) (base-symbol (base-symbol (cddr temp)))) (msg "the relation ~x0 (which~#1~[ ~/, by ~x2, ~]is known to be ~ well-founded~#3~[~/ on the domain recognized by ~x4~])" rel (if (null base-symbol) 0 1) base-symbol (if (eq mp t) 0 1) mp))) (defun measured-variables (cand wrld) (all-vars1-lst (subcor-var-lst (formals (ffn-symb (access candidate cand :induction-term)) wrld) (fargs (access candidate cand :induction-term)) (access justification (access candidate cand :justification) :subset)) nil)) (defun induct-msg/continue (pool-lst forcing-round cl-set induct-hint-val len-candidates len-flushed-candidates len-merged-candidates len-unvetoed-candidates len-complicated-candidates len-high-scoring-candidates winning-candidate estimated-size clauses wrld state) ; Pool-lst is what is passed to form the tilde-@-pool-name-phrase (q.v.) of the ; set of clauses cl-set to which we are applying induction. Len-candidates is ; the length of the list of induction candidates we found. ; Len-flushed-candidates is the length of the candidates after flushing some ; down others, etc. Winning-candidate is the final selection. Clauses is the ; clause set generated by applying winning-candidate to cl-set. Wrld and state ; are the usual. ; This function increments timers. Upon entry, the accumulated time is ; charged to 'prove-time. The time spent in this function is charged ; to 'print-time. ; Warning: This function should be called under (io? prove ...). (cond ((and (gag-mode) (or (eq (gag-mode-evisc-tuple state) t) (cdr pool-lst))) state) (t (pprogn (increment-timer 'prove-time state) (let* ((pool-name (tilde-@-pool-name-phrase forcing-round pool-lst)) (p (cons :p (merge-sort-term-order (all-vars1-lst-lst cl-set nil)))) (measured-variables (measured-variables winning-candidate wrld)) (unmeasured-variables (unmeasured-variables measured-variables winning-candidate)) (attribution-phrase (tilde-*-simp-phrase (access candidate winning-candidate :ttree)))) (fms "~#H~[We have been told to use induction. ~N0 induction ~ scheme~#1~[ is~/s are~] suggested by the induction ~ hint.~/~ We have been told to use induction. ~N0 induction ~ scheme~#1~[ is~/s are~] suggested by this ~ conjecture.~/~ Perhaps we can prove ~@n by induction. ~ ~N0 induction scheme~#1~[ is~/s are~] suggested by this ~ conjecture.~] ~ ~#a~[~/Subsumption reduces that number to ~n2. ~]~ ~#b~[~/These merge into ~n3 derived induction scheme~#4~[~/s~]. ~]~ ~#c~[~/However, ~n5 of these ~#6~[is~/are~] flawed and so we are ~ left with ~nq viable ~#r~[~/candidate~/candidates~]. ~]~ ~#d~[~/By considering those suggested by the largest number of ~ non-primitive recursive functions, we narrow the field ~ to ~n7. ~]~ ~#e~[~/~N8 of these ~ ~#9~[has a score higher than the other~#A~[~/s~]. ~/~ are tied for the highest score. ~]~]~ ~#f~[~/We will choose arbitrarily among these. ~]~ ~|~%We will induct according to a scheme suggested by ~ ~#h~[~pg.~/~pg, but modified to accommodate ~*i.~]~ ~#w~[~/ ~#h~[This suggestion was~/These suggestions were~] ~ produced using ~*x.~] ~ If we let ~pp denote ~@n above then the induction scheme ~ we'll use is~|~ ~Qsy.~ This induction is justified by the same argument used ~ to admit ~xj. ~ ~#l~[~/Note, however, that the unmeasured ~ variable~#m~[ ~&m is~/s ~&m are~] being instantiated. ~]~ When applied to the goal at hand the above induction scheme ~ produces ~#v~[no nontautological subgoals~/one nontautological ~ subgoal~/~no nontautological subgoals~].~ ~#t~[~/ However, to achieve this relatively small number of ~ cases we had to fold ~@n into a single IF-expression. Had we ~ left it as a set of clauses this induction would have produced ~ approximately ~nu cases! Chances are that this proof attempt ~ is about to blow up in your face (and all over our memory ~ boards).~]~|" (list (cons #\H (cond ((null induct-hint-val) 2) ((equal induct-hint-val *t*) 1) (t 0))) (cons #\n pool-name) (cons #\0 len-candidates) (cons #\1 (if (int= len-candidates 1) 0 1)) (cons #\a (if (< len-flushed-candidates len-candidates) 1 0)) (cons #\2 len-flushed-candidates) (cons #\b (if (< len-merged-candidates len-flushed-candidates) 1 0)) (cons #\3 len-merged-candidates) (cons #\4 (if (int= len-merged-candidates 1) 0 1)) (cons #\c (if (< len-unvetoed-candidates len-merged-candidates) 1 0)) (cons #\5 (- len-merged-candidates len-unvetoed-candidates)) (cons #\q len-unvetoed-candidates) (cons #\y (gag-mode-evisc-tuple state)) ; is not t (cons #\r (zero-one-or-more len-unvetoed-candidates)) (cons #\6 (if (int= (- len-merged-candidates len-unvetoed-candidates) 1) 0 1)) (cons #\d (if (< len-complicated-candidates len-unvetoed-candidates) 1 0)) (cons #\7 len-complicated-candidates) (cons #\e (if (< len-high-scoring-candidates len-complicated-candidates) 1 0)) (cons #\8 len-high-scoring-candidates) (cons #\9 (if (int= len-high-scoring-candidates 1) 0 1)) (cons #\A (if (int= (- len-complicated-candidates len-high-scoring-candidates) 1) 0 1)) (cons #\f (if (int= len-high-scoring-candidates 1) 0 1)) (cons #\p p) (cons #\s (prettyify-clause-set (induction-formula (list (list p)) (access candidate winning-candidate :tests-and-alists-lst)) (let*-abstractionp state) wrld)) (cons #\g (access candidate winning-candidate :xinduction-term)) (cons #\h (if (access candidate winning-candidate :xancestry) 1 0)) (cons #\i (tilde-*-untranslate-lst-phrase (access candidate winning-candidate :xancestry) nil wrld)) (cons #\j (ffn-symb (access candidate winning-candidate :xinduction-term))) (cons #\l (if unmeasured-variables 1 0)) (cons #\m unmeasured-variables) (cons #\o (length clauses)) (cons #\t (if (> estimated-size *maximum-induct-size*) 1 0)) (cons #\u estimated-size) (cons #\v (if (null clauses) 0 (if (cdr clauses) 2 1))) (cons #\w (if (nth 4 attribution-phrase) 1 0)) (cons #\x attribution-phrase)) (proofs-co state) state (term-evisc-tuple nil state))) (increment-timer 'print-time state))))) (mutual-recursion (defun rec-fnnames (term wrld) (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (union-eq (rec-fnnames (lambda-body (ffn-symb term)) wrld) (rec-fnnames-lst (fargs term) wrld))) ((getprop (ffn-symb term) 'recursivep nil 'current-acl2-world wrld) (add-to-set-eq (ffn-symb term) (rec-fnnames-lst (fargs term) wrld))) (t (rec-fnnames-lst (fargs term) wrld)))) (defun rec-fnnames-lst (lst wrld) (cond ((null lst) nil) (t (union-eq (rec-fnnames (car lst) wrld) (rec-fnnames-lst (cdr lst) wrld))))) ) (defun induct-msg/lose (pool-name induct-hint-val state) ; We print the message that no induction was suggested. ; This function increments timers. Upon entry, the accumulated time is ; charged to 'prove-time. The time spent in this function is charged ; to 'print-time. ; Warning: This function should be called under (io? prove ...). (pprogn (increment-timer 'prove-time state) (fms "No induction schemes are suggested by ~#H~[the induction ~ hint.~@?~/~@n.~] Consequently, the proof attempt has failed.~|" (list (cons #\H (cond (induct-hint-val 0) (t 1))) (cons #\n pool-name) (cons #\? (and induct-hint-val ; optimization (let* ((wrld (w state)) (ens (ens state)) (all-fns (all-fnnames induct-hint-val)) (rec-fns (rec-fnnames induct-hint-val wrld))) (cond ((null all-fns) " (Note that there is no function symbol ~ occurring in that hint.)") ((and (null rec-fns) (null (cdr all-fns))) (msg " (Note that ~x0 is not defined ~ recursively, so it cannot suggest an ~ induction scheme.)" (car all-fns))) ((null rec-fns) " (Note that none of its function symbols is ~ defined recursively, so they cannot suggest ~ induction schemes.)") ((and (all-variablep (fargs induct-hint-val)) (not (enabled-runep (list :induction (car rec-fns)) ens wrld)) (not (enabled-runep (list :definition (car rec-fns)) ens wrld))) (msg " (Note that ~x0 (including its :induction ~ rune) is disabled, so it cannot suggest an ~ induction scheme. Consider providing an ~ :in-theory hint that enables ~x0 or ~x1.)" (car all-fns) (list :induction (car all-fns)))) (t "")))))) (proofs-co state) state (term-evisc-tuple nil state)) (increment-timer 'print-time state))) ; When induct is called it is supplied the hint-settings that were ; attached to the clause by the user. Induct has the job of loading ; the hint settings into the pspv it returns. Most of the content of ; the hint-settings is loaded into the rewrite-constant of the pspv. (defun@par load-hint-settings-into-rcnst (hint-settings rcnst cl-id wrld ctx state) ; Certain user supplied hint settings find their way into the rewrite-constant. ; They are :expand, :restrict, :hands-off, and :in-theory. Our convention is ; that if a given hint key/val is provided it replaces what was in the rcnst. ; Otherwise, we leave the corresponding field of rcnst unchanged. ; Cl-id is either a clause-id or nil. If it is a clause-id and we install a ; new enabled structure, then we we will use that clause-id to build the array ; name, rather than simply incrementing a suffix. (er-let*@par ((new-ens (cond ((assoc-eq :in-theory hint-settings) (load-theory-into-enabled-structure@par :from-hint (cdr (assoc-eq :in-theory hint-settings)) nil (access rewrite-constant rcnst :current-enabled-structure) (or cl-id t) nil wrld ctx state)) (t (value@par (access rewrite-constant rcnst :current-enabled-structure)))))) (value@par (change rewrite-constant rcnst :rw-cache-state (cond ((assoc-eq :rw-cache-state hint-settings) (cdr (assoc-eq :rw-cache-state hint-settings))) (t (access rewrite-constant rcnst :rw-cache-state))) :expand-lst (cond ((assoc-eq :expand hint-settings) (cdr (assoc-eq :expand hint-settings))) (t (access rewrite-constant rcnst :expand-lst))) :restrictions-alist (cond ((assoc-eq :restrict hint-settings) (cdr (assoc-eq :restrict hint-settings))) (t (access rewrite-constant rcnst :restrictions-alist))) :fns-to-be-ignored-by-rewrite (cond ((assoc-eq :hands-off hint-settings) (cdr (assoc-eq :hands-off hint-settings))) (t (access rewrite-constant rcnst :fns-to-be-ignored-by-rewrite))) :current-enabled-structure new-ens :nonlinearp (cond ((assoc-eq :nonlinearp hint-settings) (cdr (assoc-eq :nonlinearp hint-settings))) (t (access rewrite-constant rcnst :nonlinearp))) :backchain-limit-rw (cond ((assoc-eq :backchain-limit-rw hint-settings) (cdr (assoc-eq :backchain-limit-rw hint-settings))) (t (access rewrite-constant rcnst :backchain-limit-rw))) :case-split-limitations (cond ((assoc-eq :case-split-limitations hint-settings) (cdr (assoc-eq :case-split-limitations hint-settings))) (t (access rewrite-constant rcnst :case-split-limitations))))))) (defun update-hint-settings (new-hint-settings old-hint-settings) (cond ((endp new-hint-settings) old-hint-settings) ((assoc-eq (caar new-hint-settings) old-hint-settings) (update-hint-settings (cdr new-hint-settings) (cons (car new-hint-settings) (delete-assoc-eq (caar new-hint-settings) old-hint-settings)))) (t (update-hint-settings (cdr new-hint-settings) (cons (car new-hint-settings) old-hint-settings))))) ; Thus, a given hint-settings causes us to modify the pspv as follows: (defun@par load-hint-settings-into-pspv (increment-flg hint-settings pspv cl-id wrld ctx state) ; We load the hint-settings into the rewrite-constant of pspv, thereby making ; available the :expand, :case-split-limitations, :restrict, :hands-off, ; :in-theory, and :rw-cache-state hint settings. We also store the ; hint-settings in the hint-settings field of the pspv, making available the ; :induct and :do-not-induct hint settings. ; When increment-flg is non-nil, we want to preserve the input pspv's hint ; settings except when they collide with hint-settings. Otherwise (for ; example, when induct is called), we completely replace the input pspv's ; :hint-settings with hint-settings. ; Warning: Restore-hint-settings-in-pspv, below, is supposed to undo these ; changes while not affecting the rest of a newly obtained pspv. Keep these ; two functions in step. (er-let*@par ((rcnst (load-hint-settings-into-rcnst@par hint-settings (access prove-spec-var pspv :rewrite-constant) cl-id wrld ctx state))) (value@par (change prove-spec-var pspv :rewrite-constant rcnst :hint-settings (if increment-flg (update-hint-settings hint-settings (access prove-spec-var pspv :hint-settings)) hint-settings))))) (defun restore-hint-settings-in-pspv (new-pspv old-pspv) ; This considers the fields changed by load-hint-settings-into-pspv above ; and restores them in new-pspv to the values they have in old-pspv. The ; idea is that we start with a pspv1, load hints into it to get pspv2, ; pass that around the prover and obtain pspv3 (which has a new tag-tree ; and pool etc), and then restore the hint settings as they were in pspv1. ; In this example, new-pspv would be pspv3 and old-pspv would be pspv1. ; We would like the garbage collector to free up space from obsolete arrays of ; enabled-structures. This may be especially important with potentially many ; such arrays associated with different names, due to the new method of ; creating array names after Version_4.1, where the name is typically based on ; the clause-id. Previously, the enabled-structure array names were created ; based on just a few possible suffixes, so it didn't seem important to make ; garbage -- each time such a name was re-used, the previous array for that ; name became garbage. If we change how this is done, revisit the sentence ; about this function in the Essay on Enabling, Enabled Structures, and ; Theories. (let* ((old-rcnst (access prove-spec-var old-pspv :rewrite-constant)) (old-ens (access rewrite-constant old-rcnst :current-enabled-structure)) (old-name (access enabled-structure old-ens :array-name)) (new-rcnst (access prove-spec-var new-pspv :rewrite-constant)) (new-ens (access rewrite-constant new-rcnst :current-enabled-structure)) (new-name (access enabled-structure new-ens :array-name))) (prog2$ (cond ((equal old-name new-name) nil) (t (flush-compress new-name))) (change prove-spec-var new-pspv :rewrite-constant old-rcnst :hint-settings (access prove-spec-var old-pspv :hint-settings))))) (defun remove-trivial-clauses (clauses wrld) (cond ((null clauses) nil) ((trivial-clause-p (car clauses) wrld) (remove-trivial-clauses (cdr clauses) wrld)) (t (cons (car clauses) (remove-trivial-clauses (cdr clauses) wrld))))) #+:non-standard-analysis (defun non-standard-vector-check (vars accum) (if (null vars) accum (non-standard-vector-check (cdr vars) (cons (mcons-term* 'standardp (car vars)) accum)))) #+:non-standard-analysis (defun merge-ns-check (checks clause accum) (if (null checks) accum (merge-ns-check (cdr checks) clause (cons (cons (car checks) clause) accum)))) #+:non-standard-analysis (defun trap-non-standard-vector-aux (cl-set accum-cl checks wrld) (cond ((null cl-set) accum-cl) ((classical-fn-list-p (all-fnnames-lst (car cl-set)) wrld) (trap-non-standard-vector-aux (cdr cl-set) accum-cl checks wrld)) (t (trap-non-standard-vector-aux (cdr cl-set) (append (merge-ns-check checks (car cl-set) nil) accum-cl) checks wrld)))) #+:non-standard-analysis (defun remove-adjacent-duplicates (lst) (cond ((or (null lst) (null (cdr lst))) lst) ((equal (car lst) (car (cdr lst))) (remove-adjacent-duplicates (cdr lst))) (t (cons (car lst) (remove-adjacent-duplicates (cdr lst)))))) #+:non-standard-analysis (defun non-standard-induction-vars (candidate wrld) (remove-adjacent-duplicates (merge-sort-term-order (append (access candidate candidate :changed-vars) ; The following line was changed after Version_3.0.1. It seems like a correct ; change, but we'll leave this comment here until Ruben Gamboa (ACL2(r) author) ; checks this change. (measured-variables candidate wrld))))) #+:non-standard-analysis (defun trap-non-standard-vector (cl-set candidate accum-cl wrld) (trap-non-standard-vector-aux cl-set accum-cl (non-standard-vector-check (non-standard-induction-vars candidate wrld) nil) wrld)) (defun induct (forcing-round pool-lst cl-set hint-settings pspv wrld ctx state) ; We take a set of clauses, cl-set, and return four values. The first ; is either the signal 'lose (meaning we could find no induction to do ; and have explained that to the user) or 'continue, meaning we're ; going to use induction. The second value is a list of clauses, ; representing the induction base cases and steps. The last two ; things are new values for pspv and state. We modify pspv to store ; the induction-hyp-terms and induction-concl-terms for the ; simplifier. ; The clause set we explore to collect the induction candidates, ; x-cl-set, is not necessarily cl-set. If the value, v, of :induct in ; the hint-settings is non-nil and non-*t*, then we explore the clause ; set {{v}} for candidates. (let ((cl-set (remove-guard-holders-lst-lst cl-set)) (pool-name (tilde-@-pool-name-phrase forcing-round pool-lst)) (induct-hint-val (let ((induct-hint-val0 (cdr (assoc-eq :induct hint-settings)))) (and induct-hint-val0 (remove-guard-holders induct-hint-val0))))) (mv-let (erp new-pspv state) (load-hint-settings-into-pspv nil (if induct-hint-val (delete-assoc-eq :induct hint-settings) hint-settings) pspv nil wrld ctx state) (cond (erp (mv 'lose nil pspv state)) (t (let* ((candidates (get-induction-cands-from-cl-set (select-x-cl-set cl-set induct-hint-val) new-pspv wrld state)) (flushed-candidates (m&m candidates 'flush-candidates)) ; In nqthm we flushed and merged at the same time. However, flushing ; is a mate and merge function that has the distributive and non-preclusion ; properties and hence can be done with a simple m&m. Merging on the other ; hand is preclusive and so we wait and run m&m-over-powerset to do ; that. In nqthm, we did preclusive merging with m&m (then called ; TRANSITIVE-CLOSURE) and just didn't worry about the fact that we ; messed up some potential merges by earlier merges. Of course, the ; powerset computation is so expensive for large sets that we can't ; just go into it blindly, so we don't use the m&m-over-powerset to do ; flushing and merging at the same time. Flushing eliminates duplicates ; and subsumed inductions and thus shrinks the set as much as we know how. (merged-candidates (cond ((< (length flushed-candidates) 10) (m&m-over-powerset flushed-candidates 'merge-candidates)) (t (m&m flushed-candidates 'merge-candidates)))) ; Note: We really respect powerset. If the set we're trying to merge ; has more than 10 things in it -- an arbitrary choice at the time of ; this writing -- we just do the m&m instead, which causes us to miss ; some merges because we only use each candidate once and merging ; early merges can prevent later ones. (unvetoed-candidates (compute-vetoes merged-candidates wrld)) (complicated-candidates (maximal-elements unvetoed-candidates 'induction-complexity wrld)) (high-scoring-candidates (maximal-elements complicated-candidates 'score wrld)) (winning-candidate (car high-scoring-candidates))) (cond (winning-candidate (mv-let (erp candidate-ttree state) (accumulate-ttree-and-step-limit-into-state (access candidate winning-candidate :ttree) :skip state) (declare (ignore erp)) (let* ( ; First, we estimate the size of the answer if we persist in using cl-set. (estimated-size (induction-formula-size cl-set (access candidate winning-candidate :tests-and-alists-lst))) ; Next we create clauses, the set of clauses we wish to prove. ; Observe that if the estimated size is greater than ; *maximum-induct-size* we squeeze the cl-set into the form {{p}}, ; where p is a single term. This eliminates the combinatoric ; explosion at the expense of making the rest of the theorem prover ; suffer through opening things back up. The idea, however, is that ; it is better to give the user something to look at, so he see's the ; problem blowing up in front of him in rewrite, than to just ; disappear into induction and never come out. We have seen simple ; cases where failed guard conjectures would have led to inductions ; containing thousands of cases had induct been allowed to compute ; them out. (clauses0 (induction-formula (cond ((> estimated-size *maximum-induct-size*) (list (list (termify-clause-set cl-set)))) (t cl-set)) (access candidate winning-candidate :tests-and-alists-lst))) (clauses1 #+:non-standard-analysis (trap-non-standard-vector cl-set winning-candidate clauses0 wrld) #-:non-standard-analysis clauses0) (clauses (cond ((> estimated-size *maximum-induct-size*) clauses1) (t (remove-trivial-clauses clauses1 wrld)))) ; Now we inform the simplifier of this induction and store the ttree of ; the winning candidate into the tag-tree of the pspv. (newer-pspv (inform-simplify (access candidate winning-candidate :tests-and-alists-lst) (add-to-set-equal (access candidate winning-candidate :xinduction-term) (access candidate winning-candidate :xother-terms)) (change prove-spec-var new-pspv :tag-tree (cons-tag-trees candidate-ttree (access prove-spec-var new-pspv :tag-tree)))))) ; Now we print out the induct message. (let ((state (io? prove nil state (wrld clauses estimated-size winning-candidate high-scoring-candidates complicated-candidates unvetoed-candidates merged-candidates flushed-candidates candidates induct-hint-val cl-set forcing-round pool-lst) (induct-msg/continue pool-lst forcing-round cl-set induct-hint-val (length candidates) (length flushed-candidates) (length merged-candidates) (length unvetoed-candidates) (length complicated-candidates) (length high-scoring-candidates) winning-candidate estimated-size clauses wrld state)))) (mv 'continue clauses newer-pspv state))))) (t ; Otherwise, we report our failure to find an induction and return the ; 'lose signal. (pprogn (io? prove nil state (induct-hint-val pool-name) (induct-msg/lose pool-name induct-hint-val state)) (mv 'lose nil pspv state)))))))))) ; We now define the elimination of irrelevance. Logically this ought ; to be defined when the other processors are defined. But to ; partition the literals of the clause by variables we use m&m, which ; is not defined until induction. We could have moved m&m-apply back ; into the earlier processors, but that would require moving about a ; third of induction back there. So we have just put all of ; irrelevance elimination here. (defun pair-vars-with-lits (cl) ; We pair each lit of clause cl with its variables. The variables are ; in the car of the pair, the singleton set containing the lit is in ; the cdr. (cond ((null cl) nil) (t (cons (cons (all-vars (car cl)) (list (car cl))) (pair-vars-with-lits (cdr cl)))))) (mutual-recursion (defun ffnnames-subsetp (term lst) ; Collect the ffnames in term and say whether it is a subset of lst. ; We don't consider fnnames of constants, e.g., the cons of '(a b). (cond ((variablep term) t) ((fquotep term) t) ((flambda-applicationp term) (and (ffnnames-subsetp-listp (fargs term) lst) (ffnnames-subsetp (lambda-body (ffn-symb term)) lst))) ((member-eq (ffn-symb term) lst) (ffnnames-subsetp-listp (fargs term) lst)) (t nil))) (defun ffnnames-subsetp-listp (terms lst) (cond ((null terms) t) ((ffnnames-subsetp (car terms) lst) (ffnnames-subsetp-listp (cdr terms) lst)) (t nil))) ) ;; RAG - I added realp and complexp to the list of function names ;; simplification can decide. (defun probably-not-validp (cl) ; Cl is a clause. We return t if we think there is an instantiation ; of cl that makes each literal false. It is assumed that cl has ; survived simplification. ; We have two trivial heuristics. One is to detect whether the only ; function symbols in cl are ones that we think make up a fragment ; of the theory that simplification can decide. The other heuristic ; is to bet that any cl consisting of a single literal which is of the ; form (fn v1 ... vn) or (not (fn v1 ... vn)), where the vi are ; distinct variables, is probably not valid. ; It's pretty bold to include a recursive function, namely true-listp, in the ; list below. However, as long as it's the only one, we feel safe. (or (ffnnames-subsetp-listp cl '(consp integerp rationalp #+:non-standard-analysis realp acl2-numberp true-listp complex-rationalp #+:non-standard-analysis complexp stringp characterp symbolp cons car cdr equal binary-+ unary-- < apply)) (case-match cl ((('not (& . args))) (and (all-variablep args) (no-duplicatesp-equal args))) (((& . args)) (and (all-variablep args) (no-duplicatesp-equal args))) (& nil)))) (defun irrelevant-lits (alist) ; Alist is an alist that associates a set of literals with each key. ; The keys are irrelevant. We consider each set of literals and decide ; if it is probably not valid. If so we consider it irrelevant. ; We return the concatenation of all the irrelevant literal sets. (cond ((null alist) nil) ((probably-not-validp (cdar alist)) (append (cdar alist) (irrelevant-lits (cdr alist)))) (t (irrelevant-lits (cdr alist))))) (defun eliminate-irrelevance-clause (cl hist pspv wrld state) ; A standard clause processor of the waterfall. ; We return 4 values. The first is a signal that is either 'hit, or ; 'miss. When the signal is 'miss, the other 3 values are irrelevant. ; When the signal is 'hit, the second result is the list of new ; clauses, the third is a ttree that will become that component of the ; history-entry for this elimination, and the fourth is an ; unmodified pspv. (We return the fourth thing to adhere to the ; convention used by all clause processors in the waterfall (q.v.).) (declare (ignore hist wrld state)) (cond ((not (assoc-eq 'being-proved-by-induction (access prove-spec-var pspv :pool))) (mv 'miss nil nil nil)) (t (let* ((partitioning (m&m (pair-vars-with-lits cl) 'intersectp-eq/union-equal)) (irrelevant-lits (irrelevant-lits partitioning))) (cond ((null irrelevant-lits) (mv 'miss nil nil nil)) (t (mv 'hit (list (set-difference-equal cl irrelevant-lits)) (add-to-tag-tree! 'irrelevant-lits irrelevant-lits (add-to-tag-tree! 'clause cl nil)) pspv))))))) (defun eliminate-irrelevance-clause-msg1 (signal clauses ttree pspv state) ; The arguments to this function are the standard ones for an output ; function in the waterfall. See the discussion of the waterfall. (declare (ignore signal pspv clauses)) (let* ((lits (tagged-object 'irrelevant-lits ttree)) (clause (tagged-object 'clause ttree)) (concl (car (last clause)))) (cond ((equal (length lits) (length clause)) (fms "We suspect that this conjecture is not a theorem. We ~ might as well be trying to prove~|" nil (proofs-co state) state (term-evisc-tuple nil state))) (t (let ((iterms (cond ((member-equal concl lits) (append (dumb-negate-lit-lst (remove1-equal concl lits)) (list concl))) (t (dumb-negate-lit-lst lits))))) (fms "We suspect that the term~#0~[ ~*1 is~/s ~*1 are~] irrelevant to ~ the truth of this conjecture and throw ~#0~[it~/them~] out. We ~ will thus try to prove~|" (list (cons #\0 iterms) (cons #\1 (tilde-*-untranslate-lst-phrase iterms t (w state)))) (proofs-co state) state (term-evisc-tuple nil state))))))) acl2-sources/init.lisp0000664002132200015000000001043512222115527014411 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; This file, init.lisp, is the standard KCL init file. We use this ; tiny init file, which indirects to akcl-init.lisp, so that we can ; avoid loading in the full init file if it has already been loaded. ; This file need not be distributed with ACL2 and is unimportant for ; the correct operation of ACL2. This file is loaded automatically by ; ACKL when it starts up, but not by ACL2 when running in any other ; Common Lisp. ; Bob Boyer sometimes uses the following for debugging in CCL: ;(declaim (optimize (safety 3))) ;(setq *print-level* 10) ;(setq *print-length* 10) ;(setq *compile-verbose* t) ;(setq *compile-print* t) ;(setq *load-print* t) ;(setq *load-verbose* t) ;(setq ccl:*trace-print-level* 10) ;(setq ccl:*trace-print-length* 10) ;(setq ccl::*backtrace-print-length* 10) ;(setq ccl::*backtrace-print-level* 10) (unless (find-package "ACL2") ; File acl2r.lisp is created by the makefile, though the user could create it ; directly (which may be useful in non-Unix environment when make is not ; available). It isn't necessary to create this file, however, when one is ; building a standard image, since all it does it push :non-standard-analysis ; onto *features*. (It IS necessary however, when building a standard image, ; NOT to have acl2r.lisp around if it pushes that feature!) (if (probe-file "acl2r.lisp") (load "acl2r.lisp")) #+sbcl ; keep this in sync with with-warnings-suppressed (handler-bind ((style-warning (lambda (c) (declare (ignore c)) (invoke-restart 'muffle-warning)))) (load "acl2-init.lisp")) #-sbcl (load "acl2-init.lisp")) ; We may need a bigger stack than the default, as evidenced by the failure of ; the event (verify-guards read-utf8-fast ...) in community book ; books/unicode/read-utf8.lisp. We handle this issue here for GCL, and ; elsewhere for some other lisps. However, we have seen GCL 2.6.6 on Windows ; break here, so we skip the stack adjustment for Windows. #+gcl (progn (defvar *acl2-gcl-multiply-stacks-evaluated* nil) (when (not *acl2-gcl-multiply-stacks-evaluated*) ; Formerly we multiplied by 2. But the following problems then bit us in ; ACL2(h). Certification of community book books/arithmetic-5/top.lisp caused ; a stack overflow because of function expansion-alist-pkg-names-memoize; ; books/misc/hons-tests.lisp had a stack overflow because of a memoized ; fibonacci function call; and a stack overflow for ; books/clause-processors/SULFA/books/sat-tests/sudoku.lisp was caused by ; bad-lisp-objectp. Another doubling fixed each of these, but wan't enough for ; certifying books/centaur/aig/random-sim.lisp, again because of ; expansion-alist-pkg-names-memoize. So we now multiply by 8. Camm Maguire ; has suggested that these problems might be solved by avoiding the use of ; interpreted code, and we considered investigating whether that might be done ; (e.g., by adding (comp t) events) in the cases above. But we see no harm in ; simply increasing the stack size, which could be of benefit in cases where ; users execute uncompiled code. And besides, when we start ACL2(h) built on ; GCL we find that (symbol-function 'expansion-alist-pkg-names-memoize) is ; compiled. (setq si::*multiply-stacks* 8)) (setq *acl2-gcl-multiply-stacks-evaluated* t)) ; Suggestion from Camm Maguire, 6/28/06 (GCL 2.6.7 and beyond), for improved ; efficiency; seconded by Bob Boyer. #+gcl (when (acl2::gcl-version->= 2 6 7) (declaim (ftype (function (seqind t) t) si::set-mv))) acl2-sources/interface-raw.lisp0000664002132200015000000141437612222115527016212 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; This file, interface-raw.lisp, contains parts of ACL2 which we ; cannot code in ACL2 because they require constructs not in ACL2, such ; as calling the compiler. ; EVALUATION ; Essay on Evaluation in ACL2 ; This essay is structured as follows. Terminology is explained below. ; A. Introduction ; B. Specification of the problem ; C. Sketch of correctness proof ; D. Why safe mode is necessary ; E. The template for oneification of function definitions ; F. Remarks ; Let us begin. ; A. Introduction ; Evaluation in ACL2, which takes place in the ACL2 loop and during theorem ; proving, is based on the way evaluation was done in Nqthm. The idea is to ; "oneify" the body of a definition by replacing functions by their so-called ; "executable counterparts," sometimes called "*1* functions." The primitives ; have *1* functions that reflect their logical definitions, so that for ; example (*1*car x), or more precisely (acl2_*1*_lisp::car x), returns nil ; when x is an atom -- except that an error occurs if we are checking guards ; (or are in so-called safe mode, as explained below). Defined functions have ; *1* function counterparts that are defined, roughly speaking, by replacing ; each function call in their bodies by a call of the corresponding *1* ; function. ; The evaluation mechanism in ACL2 changed radically in v1-8, when guards were ; removed from the logic. It has changed again in Version_2.6, due to a hole ; in the existing mechanism, as we explain in Part D of this Essay, below. ; B. Specification of the problem ; Our specification begins with the introduction of three notions of evaluation ; and three notions of macroexpansion. Evaluation is relative to an (implicit) ; environment, which binds variables to ACL2 values, and operates on ACL2 terms ; that are free of macro calls. If we want to discuss evaluation of terms that ; involve macro calls, we will compose macroexpansion and evaluation. This ; composition represents the way that both Common Lisp and the ACL2 loop ; evaluate forms. We also assume that there is no distinction between ; evaluation of compiled and interpreted code. Finally, we assume that for all ; of these sorts of evaluation and expansion, macros are expanded before ; function and macro bodies are stored; this is how things are done in the ACL2 ; logic and with *1* functions, and it had better be equivalent to how a Common ; Lisp does its evaluation and expansion. ; We extend classes of function symbols to classes of terms in the obvious way: ; a :logic mode term is one whose function symbols are all :logic mode function ; symbols, and similarly for the notion of :common-lisp-compliant. ; Here then are the notions promised above. ; ACL2 logical evaluation: This procedure is an interpreter that computes ; using definitions in the logic and obvious properties of primitives (e.g., ; (stringp "abc") returns t). ; ACL2 loop evaluation: This is the procedure used in the ACL2 loop, using ; so-called *1* functions (and higher-level routines such as raw-ev-fncall). ; Common Lisp evaluation: As the name implies, this procedure is the one used ; by Common Lisp. ; ACL2 logical macroexpansion: This is any procedure that carries out the ; usual macroexpansion algorithm (outside-in), but entirely using ACL2 ; definitions, including those of :program mode functions. We assume that ; macros have already been similarly expanded in function bodies, before ; evaluation begins. Macro bodies are evaluated using ACL2 logical ; evaluation. This procedure is embodied in the ACL2 definition of the ; function translate. ; ACL2 loop macroexpansion: This is the procedure that ACL2 actually applies ; in order to create terms from user input. Ideally this procedure returns ; the same results as does ACL2 logical macroexpansion; the distinction here ; is between what an appropriate interpreter would return (ACL2 logical ; macroexpansion) and how ACL2 actually translates a term (ACL2 loop ; macroexpansion). ACL2 loop macroexpansion always takes place in safe mode. ; Common Lisp macroexpansion: This is how Common Lisp (actually, an arbitrary ; but fixed implementation) macroexpands forms. ; As an aside, we note the following fact that is useful in establishing the ; guarantees below, but whose proof we omit here. ; (*) If a :common-lisp-compliant function is applied to arguments that ; satisfy its guard (using Common Lisp evaluation), without error, then the ; result agrees with that produced by ACL2 logical evaluation. ; Now our top-level guarantees about evaluation and macroexpansion are as ; follows, where for brevity, "evaluation" of a given type is the composition ; of macroexpansion and evaluation for that type. ; (1) If ACL2 evaluates a :logic mode form without error, then the value ; returned equals the result of ACL2 logical (macroexpansion and) evaluation ; of that form. ; (2) If furthermore that evaluation in done with guard-checking on and the ; result of ACL2 logical macroexpansion is a :common-lisp-compliant term, ; then any non-erroneous Common Lisp evaluation returns that same value. ; C. Sketch of correctness proof ; We now outline a proof of these guarantees by breaking them into the ; following sequence of claims. We write "weakly implements" to mean that two ; procedures give equivalent results on given inputs when they both return ; without error, and we write "implements" if the condition can be weakened to ; assume only that the first procedure returns without error. That is, proc1 ; implements proc2 iff proc1 weakly implements proc2 and whenever proc1 returns ; without error, then so does proc2. Above, "equivalent" means identical ; except as explained otherwise below. Implicit in this notion is that the ; input is appropriate for the procedures; for example, our notions of ; evaluation assume that all function symbols in the input are either ACL2 ; primitive functions or have been defined as functions (not macros) in ACL2. ; A more rigorous argument would proceed by induction on the length of ; histories, showing that the properties in question hold when one extends the ; history with new function and macro definitions. ; (1a) ACL2 loop evaluation implements ACL2 logical evaluation on :logic mode ; terms and, provided safe mode is used, on arbitrary terms. ; (1b) ACL2 loop macroexpansion implements ACL2 logical macroexpansion. ; (2a) ACL2 loop evaluation in safe mode weakly implements Common Lisp ; evaluation. The same claim holds if the assumption of safe mode is ; replaced by the assumption that guard-checking is on, provided that the ; input form expands to a :common-lisp-compliant term. ; (2b) ACL2 loop macroexpansion weakly implements Common Lisp macroexpansion, ; where results r1 (from ACL2 loop macroexpansion) and r2 (from Common Lisp ; macroexpansion) are considered equivalent if for any environment, the ACL2 ; loop evaluation of r1 with guard-checking on returns the same result as the ; Common Lisp evaluation of r2, provided both evaluations return without ; error. ; Sketch of proof that guarantees hold. Clearly (1) follows from (1a) and ; (1b), while (2) follows from (1b) and (2b). (1a) follows by inspection of ; the template presented below, using (*) above. (1b) follows from (1a) by ; computational induction on the macroexpansion, because ACL2 loop ; macroexpansion and ACL2 logical macroexpansion differ only in the use of loop ; or logical evaluation of macro bodies. The first part of (2a) is argued ; similarly to (1a), while the second part is actually quite trivial by ; inspection of the template below. Finally, (2b) follows from (2a) by a ; computational induction just as (1b) follows from (1a), with a bit of ; complication. When we encounter a call of a macro first introduced in ACL2 ; (either during the boot-strap or by a user), then we evaluate the same macro ; body for ACL2 loop evaluation as for Common Lisp evaluation, except that this ; body has first been macroexpanded using ACL2 loop macroexpansion and Common ; Lisp macroexpansion, respectively. But these may be viewed as equivalent by ; the inductive hypothesis (where for purposes of this proof we pretend that ; macroexpansion of the body takes place as part of the process). In the other ; case, the macro already has a Common Lisp definition (as a function or ; macro), and we have arranged that (2) holds. For example, the ACL2 loop ; macroexpansion of (append x y z) is (binary-append x (binary-append y z)), ; and Common Lisp evaluation of the former clearly agrees with ACL2 loop ; evaluation of the latter. Q.E.D. ; D. Why safe mode is necessary ; The soundness of ACL2 potentially rests on the principle of not calling raw ; Lisp counterparts of functions with arguments outside their intended domains, ; as specified by their guards. Here we give three examples illustrating why ; we introduced safe mode in Version_2.6. The third one is a proof of nil! ; Example 1. In our first example below, the defun of bar should fail. It ; does indeed fail starting with Version_2.6, but not in Version_2.5 or (we ; believe) several versions before that. We discuss below how this can lead ; to unsoundness. ; (defmacro foo (x) (car x)) ; (set-guard-checking nil) ; (defun bar (y) ; (declare (xargs :verify-guards t)) ; (cons (foo y) y)) ; :q ; (trace bar) ; (lp) ; Now, the result of evaluating (bar 3) looks as shown below. Notice that the ; Common Lisp function bar is called. If the Common Lisp evaluation of the ; form (car 'y) had returned 1 or 2 (say) instead of breaking, then the Common ; Lisp evaluation of (bar 3) would have returned (cons 1 3) or (cons 2 3), ; respectively. This evaluation could be reflected in theorems (equal (bar 3) ; i) [i=1,2] proved in books certified in two different Common Lisp ; implementations of ACL2. We could then prove nil by including both books ; into the same session. Lest one think that one needs different Lisp ; implementations to expose unsoundness, imagine a single Lisp in which (car ; 'y) sometimes returns 1 and sometimes returns 2. ; ACL2 >(bar 3) ; 1> (ACL2_*1*_ACL2::BAR 3)> ; 2> (BAR 3)> ; ; Error: Y is not of type LIST. ; Fast links are on: do (si::use-fast-links nil) for debugging ; Error signalled by CAR. ; Broken at COND. Type :H for Help. ; ACL2>> ; Here is what ACL2 Version_2.6 prints in an attempt to define function bar, ; above, with guard-checking off. ; ACL2 >(defun bar (y) (foo y)) ; ; ; ACL2 Error in ( DEFUN BAR ...): The guard for the function symbol ; CAR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments ; in the call (CAR 'Y). The guard is being checked because this function ; is a primitive and a "safe" mode is being used, perhaps for macroexpansion. ; ; ; ; ACL2 Error in ( DEFUN BAR ...): In the attempt to macroexpand the ; form (FOO Y), evaluation of the macro body caused an error. ; ; ; Summary ; Form: ( DEFUN BAR ...) ; Rules: NIL ; Warnings: None ; Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ; ; ******** FAILED ******** See :DOC failure ******** FAILED ******** ; ACL2 > ; Example 2. Unlike the previous example, this one causes problems even when ; guard-checking is on. (Thanks to Pete Manolios for helping to construct this ; example, which is simpler than an earlier one we had.) ; (defun my-endp-0 () ; (declare (xargs :mode :program)) ; (endp 0)) ; (defmacro bad-macro () ; (my-endp-0)) ; :q ; (trace my-endp-0 endp) ; (lp) ; (thm (equal (bad-macro) 1)) ; Now look at the following Version_2.5 trace. It highlights a behavior of ; Version_2.5: when a :program mode function (here, my-endp-0) is ; called on arguments satisfying its guard (here, implicitly t), the ; corresponding raw Lisp function is invoked. Thus guards are not checked on ; its subroutines (here, endp). In this example, endp is being called on an ; argument not satisfying its guard. In the abstract, this is problematic ; because we use guards to restrict primitives to arguments for which the ; result is implementation independent. If the result of (endp 0) can depend ; on the implementation, we could prove nil as described in the preceding ; example. ; ACL2 !>(thm (equal (bad-macro) 1)) ; 1> (ACL2_*1*_ACL2::MY-ENDP-0)> ; 2> (MY-ENDP-0)> ; 3> (ENDP 0)> ; ; Error: 0 is not of type LIST. ; Fast links are on: do (si::use-fast-links nil) for debugging ; Error signalled by SYSTEM::TRACE-CALL. ; Broken at COND. Type :H for Help. ; ACL2>> ; The example above may seem contrived (because it is!). However, our foray ; into this territory began on a rather similar but real example. In Allegro ; 6.0, the character (code-char (+ 128 65)) is upper case; in particular it is ; not equal to the result of applying char-downcase to it. However, this is ; not the case for Allegro 5.0.1. Since the result is ; implementation-dependent, it is natural to restrict the application of ; code-char to standard characters, using ACL2's guard mechanism. But the ; example above show that we can bypass such restrictions by using macros. ; Example 3. We can prove nil in Version_2.5 by certifying the following two ; books. The only cheats are that the first book needs to be certified after ; executing the following in the ACL2 loop: ; (set-guard-checking nil) ; First book, call it "ex": ; (in-package "ACL2") ; ; (defun my-eq (x y) ; (declare (xargs :guard t ; "bad" guard ; :mode :program)) ; (eq x y)) ; ; (defmacro bad-macro () ; (my-eq '(a b) '(a b))) ; ; (set-verify-guards-eagerness 0) ; ; (local (verify-termination my-eq)) ; ; (defun bad-fn () ; (bad-macro)) ; ; (defthm bad-thm ; (bad-fn) ; :rule-classes nil) ; Second book, which includes the one above:: ; (in-package "ACL2") ; ; (local (include-book "ex")) ; ; (defthm very-bad ; nil ; :hints (("Goal" :use bad-thm)) ; :rule-classes nil) ; In Version_2.6 we get an error when we try to certify the first book above ; ("ex"): ; ACL2 Error in ( DEFUN BAD-FN ...): The guard for the function symbol ; EQ, which is (IF (SYMBOLP X) T (SYMBOLP Y)), is violated by the arguments ; in the call (EQ '(A B) '(A B)). The guard is being checked because ; this function is a primitive and a "safe" mode is being used, perhaps ; for macroexpansion. ; ; ; ; ACL2 Error in ( DEFUN BAD-FN ...): In the attempt to macroexpand the ; form (BAD-MACRO), evaluation of the macro body caused an error. ; As the first message just above suggests, in Version_2.6 we prevent the bad ; behavior illustrated by the examples above by introducing a "safe mode" for ; use during macroexpansion, in which guards are checked on built-in functions. ; Finally, note that we do not attempt to fix the following "problem." That ; is, the behavior for the example below is unchanged from Version_2.5 to ; Version_2.6. The point is that for macroexpansion to behave properly, we ; really need only guarantee consistency between the logic and Common Lisp; it ; is acceptable if in some modes we get errors even when errors are not ; necessary. ; (defun mac-fn (x) (declare (xargs :guard (consp x))) x) ; (defmacro mac (x) (mac-fn x)) ; (defun bar (x) (mac x)) ; fails ; :set-guard-checking nil ; (defun bar (x) (mac x)) ; succeeds ; E. The template for oneification of function definitions ; Before we present this template, we give a bit of history and show an ; example. ; The following example shows how *1* functions are handled in Version_2.5 and ; before. The ACL2 definition is: ; (defun foo (x) ; (declare (xargs :mode :logic :guard (true-listp x))) ; (if (endp x) 3 (+ 1 (foo (cdr x))))) ; Here is the executable counterpart in Version_2.5, in gcl: ; ACL2>(symbol-function 'ACL2_*1*_ACL2::FOO) ; in gcl, ACL2 Version_2.5 ; (LISP:LAMBDA-BLOCK ACL2_*1*_ACL2::FOO (X) ; (LABELS ((ACL2_*1*_ACL2::FOO (X) ; (IF (ACL2_*1*_LISP::ENDP X) '3 ; (ACL2_*1*_ACL2::BINARY-+ '1 ; (ACL2_*1*_ACL2::FOO (ACL2_*1*_LISP::CDR X)))))) ; (LET ((ACL2_*1*_ACL2::FOO (SYMBOL-CLASS 'FOO (W *THE-LIVE-STATE*))) ; (GUARD-CHECKING-ON ; (F-GET-GLOBAL 'GUARD-CHECKING-ON *THE-LIVE-STATE*))) ; (COND ; ((LET ((*HARD-ERROR-RETURNS-NILP* ; (OR *HARD-ERROR-RETURNS-NILP* ; (NOT GUARD-CHECKING-ON)))) ; (IF (EQ ACL2_*1*_ACL2::FOO :IDEAL) ; (ACL2_*1*_ACL2::TRUE-LISTP X) (TRUE-LISTP X))) ; (IF (EQ ACL2_*1*_ACL2::FOO :IDEAL) (ACL2_*1*_ACL2::FOO X) ; (FOO X))) ; (GUARD-CHECKING-ON ; (THROW-RAW-EV-FNCALL ; (LIST 'EV-FNCALL-GUARD-ER 'FOO (LIST X) '(TRUE-LISTP X) ; '(NIL)))) ; (T (ACL2_*1*_ACL2::FOO X)))))) ; Notice the inefficiency of needlessly checking guards in Version_2.5 in the ; :ideal case when guard-checking is off. We fix that problem in Version_2.6, ; but more importantly, we implement a "safe mode" to be used during ; macroexpansion, so that we can trust that ACL2 and Common Lisp agree when ; they are supposed to, thus avoiding the sort of problem illustrated above ; (function bar and macro mac). We make this idea precise in our discussion of ; "Guarantees", above. ; ACL2>(symbol-function 'ACL2_*1*_ACL2::FOO) ; in gcl, ACL2 Version_2.6 ; (LISP:LAMBDA-BLOCK ACL2_*1*_ACL2::FOO (X) ; (LABELS ((ACL2_*1*_ACL2::FOO (X) ; (IF (ACL2_*1*_LISP::ENDP X) '3 ; (ACL2_*1*_ACL2::BINARY-+ '1 ; (ACL2_*1*_ACL2::FOO (ACL2_*1*_LISP::CDR X)))))) ; (COND ; ((TRUE-LISTP X) (RETURN-FROM ACL2_*1*_ACL2::FOO (FOO X))) ; ((F-GET-GLOBAL 'GUARD-CHECKING-ON *THE-LIVE-STATE*) ; (RETURN-FROM ACL2_*1*_ACL2::FOO ; (THROW-RAW-EV-FNCALL ; (LIST 'EV-FNCALL-GUARD-ER 'FOO (LIST X) '(TRUE-LISTP X) ; '(NIL)))))) ; (ACL2_*1*_ACL2::FOO X))) ; Next, we present a basic template (outline, really) for defining executable ; counterparts. Note that as in the code for Version_2.5, we may optimize away ; consideration of the guard when the guard is t (either implicitly or ; explicitly). Furthermore, we do some optimization when the symbol-class of ; the function is :common-lisp-compliant, as we do in Version_2.5 for :program ; vs. :logic mode. ; The template below uses some abbreviations <...>, which are defined below the ; template. See also oneify-cltl-code for more details, special cases, and ; optimizations. There we also handle the guarded-primitive-p case, which ; pertains to built-in defined functions that need to be responsible for ; checking their guards in safe-mode. That does not however deal with true ; primitives, which are not defined. For those, safe-mode is handled with ; calls of gv in their defun-*1* definitions. ; Finally, this template is only approximate and not necessarily up-to-date. ; It is intended to give a sense of how oneify-cltl-code works; see the code ; for up-to-date comments. You may be better off simply by tracing ; oneify-cltl-code with hiding of the wrld argument, for example with ; (trace! (oneify-cltl-code :entry t :native t)) ; -- and then executing each of the following test cases. Follow each of these ; with (trace$ foo) to see oneification for allowing tracing of recursive calls ; when guard-checking is :none. Then execute :u before the next defun. Oh, ; and try guard violations too. ; (defun foo (x) ; (declare (xargs :guard t)) ; (if (natp x) (if (zp x) 0 (* x (foo (1- x)))) 0)) ; (defun foo (x) ; (declare (xargs :guard t :verify-guards nil)) ; (if (natp x) (if (zp x) 0 (* x (foo (1- x)))) 0)) ; (defun foo (x) ; (declare (xargs :guard t :mode :program)) ; (if (natp x) (if (zp x) 0 (* x (foo (1- x)))) 0)) ; ; (defun foo (x) ; (declare (xargs :guard (natp x))) ; (if (zp x) 0 (* x (foo (1- x))))) ; (defun foo (x) ; (declare (xargs :guard (natp x) :mode :program)) ; (if (zp x) 0 (* x (foo (1- x))))) ; (defun foo (x) ; (declare (xargs :guard (natp x) :verify-guards nil)) ; (if (zp x) 0 (* x (foo (1- x))))) ; ; ; This one reports a guard violation with guard-checking set to :all but not ; ; when it is set to t. ; (defun foo (x) ; (declare (xargs :guard (evenp x) :verify-guards nil)) ; (if (zp x) 0 (* x (foo (1- x))))) ; ; (defun foo (x) ; (if (zp x) 0 (* x (foo (1- x))))) ; (defun <*1*fn> ; ; (let (( (symbol-class ' (w *the-live-state*)))) ; (cond ((eq :common-lisp-compliant) ; (cond ; ((or (equal *t*) ; (not (eq :none)) ; (acl2-system-namep name wrld)) ; (cond ( ; avoid <*1*guard> since guard is known compliant ; (cond ( ; test can often be omitted ; (return-from <*1*fn> ( . ))))) ; ( )) ; ; ; Otherwise fall through to final call of *1* function. ; ; ) ; ; ; The next case is not needed for our guarantees. Rather, it ensures that ; ; evaluation with guard checking on really does check guards at each function ; ; call. ; ; ((and ; (not <*1*guard>)) ; ) ; ((or (eq :all) ; (and ; (eq :program))) ; (return-from <*1*fn> *1*body)) ; ((eq :program) ; (return-from <*1*fn> ( . ))) ; ; ; If we fall through to here, then we compute in the logic, avoiding further ; ; guard checks in recursive calls (where a "special" declaration will take ; ; care of this if we are in a mutual-recursion nest). ; ; (maybe-warn-for-guard ))) ; ; ; In the case (eq :program), we conclude by laying down the call ( ; ; . ). Otherwise, we lay down the following code. ; ; (labels ; ; ; The following local definition of <*1*fn> executes calls of in the ; ; logic, without guard-checking (except for primitives under safe-mode; see ; ; below). Note that it is always legitimate for this local function to cause ; ; an error, so if we want to save space we can fail here, in particular for ; ; :program mode functions encountered during the boot-strap, at least outside ; ; (say) axioms.lisp -- although that would presumably eliminate the ability to ; ; call those functions in macro definitions. ; ; ((<*1*fn> ; ; ; ; Certain functions can take the live state as an argument, and yet do ; ; not ``properly'' handle it in their logic code. Consider for ; ; example (princ$ '(a b) *standard-co* state). With guard-checking ; ; off, and a live state, this form used to cause a hard error. The ; ; problem was that the logical body of princ$ (actually, its ; ; oneification) was being executed. Consider calling a function such ; ; as open-output-channels, which is really a call of nth, on a live ; ; state! We believe that our problem is solved by disallowing calls ; ; of certain built-in functions on a live state argument, when passing ; ; to their oneified bodies. These functions are those in ; ; *super-defun-wart-stobjs-in-alist*, since these are the ones that ; ; pass the state on as though it were a legitimate state object, i.e., ; ; to functions that take non-state values as arguments. ; ; ; Other functions, such as those defined under defstobj, may have a stobj ; ; name as an argument but do not have an appropriate 'stobjs-in ; ; setting in the world, because we have not yet declared that the ; ; stobj name is a stobj. These latter functions are characterized by ; ; having a non-nil stobj-flag, said flag being the stobj name. We ; ; compute here the appropriate stobjs-in. ; ; ; laid down only in cases as described above ; *1*body)) ; (*1*fn . formals))))) ; ; WHERE: ; ; <*1*guard> ; = ; oneification of guard ; ; ; = ; list of formals, e.g., (x1 ... xn) ; ; ; = ; (f-get-global 'guard-checking-on *the-live-state*) ; ; ; = ; [guard of the function being defined] ; ; ; = ; (throw-raw-ev-fncall ; (list 'ev-fncall-guard-er ' (list x) ' '(nil) nil)) ; ; ; = ; (throw-raw-ev-fncall ; (list 'ev-fncall-guard-er ' (list x) ' '(nil) t)) ; ; ; = ; <*1*fn> ; ; <*1*fn> ; = ; (*1*-symbol ) ; ; ; = ; [function symbol being defined] ; ; ; = ; (f-get-global 'safe-mode *the-live-state*) ; ; ; = ; code for protecting against executing <*1*body> on live stobjs ; ; ; = ; test that all of the stobj parameters to the function are indeed the "live" ; stobjs. This is required because the Common Lisp code for stobj access and ; update functions assumes, starting in v2-8, that user-defined stobj parameters ; are live, a restriction enforced by the corresponding *1* functions before ; passing to Common Lisp. ; ; ; = ; a test that is generated to check if one is evaluating a function with ; user-defined stobjs in a wormhole (no wormhole test is performed if the ; function does not take user-defined stobjs as arguments). Only proper updates ; on state are allowed inside wormholes since the wormhole can properly "undo" ; these side effects upon completion. No such mechanism exists for user-defined ; stobjs and thus the test. Before v2-8, this wormhole test was performed in the ; stobj update primitives directly, but it is now performed in the *1* function ; as a matter of efficiency. The exclusion of read access of user-defined stobjs ; in wormholes simplifies the code to generate the *1* body and while technically ; unnecessary, does not seem to be a relevant over-restriction in practice. ; F. Remarks ; Remark 1. Notice that safe mode does not by itself force guard-checking in ; all cases, nor does soundness of safe mode require guard-checking as long as ; we do check guards when evaluating calls of functions that are built into ; Common Lisp. We ensure this in the macro gv, which is changed in Version_2.6 ; to cause an error when in safe mode. ; Remark 2. Consider, in the body of *1*fn, the case that ; holds. If we were to replace it with (or guard-checking-on program) then we ; would always check guards when in program mode, which would give backward ; compatability: this scheme would behave exactly as the scheme from ; Version_2.5 for and before did when the new scheme is used in other than safe ; mode. But we have decided that starting with Version_2.6, we will no longer ; check guards for :program mode functions when 'guard-checking-on has value ; nil (though starting with Version_3.0 you can get this effect when ; 'guard-checking-on has value :none). After all, even with guard-checking on ; in Version_2.5 you can get nasty Lisp breaks, since we slip directly into ; Common Lisp when a guard holds even though guards cannot be verified for ; :program mode functions. ; End of Essay on Evaluation in ACL2 ; ONEIFICATION ; Here are the *1* functions. They should be kept in sync with ; *primitive-formals-and-guards*. We could probably get by with avoiding ; defining those in the list *oneify-primitives*, for example since want to ; avoid calling "slow" functions, e.g., *1*cons instead of cons. But we prefer ; to follow the simple rule that every function has a *1* counterpart, which is ; easy to remember. Moreover, in the case of IF we really do want to define ; *1*if to cause an error, because we insist that the oneification of if remain ; lazy. ; WARNING: Any hand-coded *1* definitions for other than the primitives, such ; as the one for mv-list (see below), must be handled in add-trip and ; compile-uncompiled-*1*-defuns (see the handling there of mv-list). ; We must hand-code the *1* functions for mv-list and return-last, because ; otherwise they will simply call mv-list and return-last (resp.), which can be ; wrong: in the case of return-last, we need the first argument to be a quotep ; for the raw-Lisp macro return-last to do more than lay down a progn. (defun-*1* mv-list (input-arity x) (declare (ignore input-arity)) x) (defun-*1* return-last (fn x y) (cond ((and (equal fn 'mbe1-raw) (f-get-global 'safe-mode *the-live-state*)) ; Since return-last is a special form, we can decide how we want to view it ; with respect to guards. We have decided to check its guard only when in safe ; mode, which is the minimal case needed in order to fix a soundness bug ; related to mbe; see note-4-3. The following log shows what happened in a ; preliminary implementation of that bug fix, in which oneify laid down a ; *1*return-last call unconditionally; note the unfortunate call of the :exec ; function, f2. Of course, that call is not the fault of the old version of ; *1*return-last, which is a function called after its arguments are already ; evaluated; there are other places (ev-rec and oneify) where we avoid even ; calling *1*return-last. But if we do get to this call, for example by way of ; expand-abbreviations calling ev-fncall, at least we can limit the equality ; check here to the case of safe-mode (which is presumably nil, for example, ; under expand-abbreviations). ; ACL2 !>(defn f1 (x) x) ; [[... output omitted ...]] ; F1 ; ACL2 !>(defn f2 (x) x) ; [[... output omitted ...]]; ; F2 ; ACL2 !>(defun f (x) (mbe :logic (f1 x) :exec (f2 x))) ; [[... output omitted ...]] ; F ; ACL2 !>(trace$ f f1 f2) ; ((F) (F1) (F2)) ; ACL2 !>(f 3) ; 1> (ACL2_*1*_ACL2::F 3) ; 2> (ACL2_*1*_ACL2::F2 3) ; 3> (F2 3) ; <3 (F2 3) ; <2 (ACL2_*1*_ACL2::F2 3) ; 2> (ACL2_*1*_ACL2::F1 3) ; 3> (F1 3) ; <3 (F1 3) ; <2 (ACL2_*1*_ACL2::F1 3) ; <1 (ACL2_*1*_ACL2::F 3) ; 3 ; ACL2 !> (if (equal x y) y (gv return-last (fn x y) y))) (t y))) ; We must hand-code the *1* function for wormhole-eval because if it were ; automatically generated it would look like this: ; (defun ACL2_*1*_ACL2::WORMHOLE-EVAL (qname qlambda free-vars) ; (wormhole-eval qname qlambda free-vars)) ; (To see where this code would be generated, find the comment beginning ; "Optimization in a common case" in oneify-cltl-code.) ; But the body of the *1* defun above violates the requirement that the ; wormhole-eval macro always be applied to a quoted name and lambda expression. ; In particular, if the default optimized code above is generated and then ; given to raw lisp, a hard error results when the wormhole-eval macro tries to ; cadr into qname. So what should be here? Intuitively we ought to lay down ; code that checks that qlambda is a well-formed and appropriate lambda ; expression and then apply it to the wormhole status of the wormhole with the ; name qname. But in fact this *1* function should never be called except from ; within the theorem prover when we are evaluating wormhole-eval on quoted ; constants. Thus, we just return nil, it's logical value, without attempting ; to do any of the wormhole stuff. (defun-*1* wormhole-eval (qname qlambda free-vars) (declare (ignore qname qlambda free-vars)) nil) ; Keep the rest of these in sync with the completion-of-* axioms in ; axioms.lisp. (defun-*1* acl2-numberp (x) (numberp x)) (defun-*1* binary-* (x y) (the number (if (numberp x) (if (numberp y) (* x y) (gv binary-* (x y) 0)) (gv binary-* (x y) 0)))) (defun-*1* binary-+ (x y) (the number (if (numberp x) (if (numberp y) (+ (the number x) (the number y)) (gv binary-+ (x y) x)) (gv binary-+ (x y) (if (numberp y) y 0))))) (defun-*1* unary-- (x) (the number (if (numberp x) (- x) (gv unary-- (x) 0)))) (defun-*1* unary-/ (x) (the number (if (and (numberp x) (not (= x 0))) (/ x) (gv unary-/ (x) 0)))) (defun-*1* < (x y) ; If one regards (gv op args val) simply as val, then we can prove that ; the body below is equivalent to the let-expression used for val. Put ; another way, if we use << as the "familiar" less-than on the rationals ; then this definition of < is equivalent to ; (< x y) = (let ((x1 (if (acl2-numberp x) x 0)) ; (y1 (if (acl2-numberp y) y 0))) ; (or (<< (realpart x1) (realpart y1)) ; (and (= (realpart x1) (realpart y1)) ; (<< (imagpart x1) (imagpart y1))))) ; The consideration of the case where both x and y are rational is just ; an optimization. (if (and (rationalp x) (rationalp y)) (< (the rational x) (the rational y)) (gv < (x y) (let ((x1 (if (numberp x) x 0)) (y1 (if (numberp y) y 0))) (or (< (realpart x1) (realpart y1)) (and (= (realpart x1) (realpart y1)) (< (imagpart x1) (imagpart y1)))))))) (defun-*1* apply (x y) (error "We have called apply on ~s and ~s, but we thought we were rid of apply." x y)) (defun-*1* bad-atom<= (x y) (cond ((and (bad-atom x) (bad-atom y)) ; The following should never happen. (error "We have called (the executable counterpart of) bad-atom<= on ~ ~s and ~s, but bad-atom<= has no Common Lisp definition." x y)) (t (gv bad-atom<= (x y) nil)))) (defun-*1* car (x) (cond ((consp x) (car x)) ((null x) nil) (t (gv car (x) nil)))) (defun-*1* cdr (x) (cond ((consp x) (cdr x)) ((null x) nil) (t (gv cdr (x) nil)))) (defun-*1* char-code (x) (if (characterp x) (char-code x) (gv char-code (x) 0))) (defun-*1* characterp (x) (characterp x)) (defun-*1* code-char (x) (if (and (integerp x) (>= x 0) (< x 256)) (code-char x) (gv code-char (x) (code-char 0)))) (defun-*1* complex (x y) (complex (the rational (if (rationalp x) x (gv complex (x y) 0))) (the rational (if (rationalp y) y (gv complex (x y) 0))))) (defun-*1* complex-rationalp (x) (complexp x)) ;; RAG - I added this function to recognize the complex numbers. #+:non-standard-analysis (defun-*1* complexp (x) (complexp x)) (defun-*1* coerce (x y) (cond ((equal y 'list) (if (stringp x) (coerce x 'list) (gv coerce (x y) nil))) ((character-listp x) (if (equal y 'string) (coerce x 'string) (gv coerce (x y) (coerce x 'string)))) (t (gv coerce (x y) (coerce (make-character-list x) 'string))))) (defun-*1* cons (x y) (cons x y)) (defun-*1* consp (x) (consp x)) (defun-*1* denominator (x) (if (rationalp x) (denominator x) (gv denominator (x) 1))) (defun-*1* equal (x y) (equal x y)) #+:non-standard-analysis (defun-*1* floor1 (x) ;; RAG - I added this function to evaluate the special floor1 ;; function, which computes floor with a modulus of 1. (if (rationalp x) (floor x 1) (gv floor1 (x) 0))) (defun-*1* if (x y z) (error "We just can't stand having a non-lazy IF around. But we attempted ~%~ to call the executable counterpart of IF on argument list ~s." (list x y z))) (defun-*1* imagpart (x) (if (numberp x) (imagpart x) (gv imagpart (x) 0))) (defun-*1* integerp (x) (integerp x)) (defun-*1* intern-in-package-of-symbol (x y) (if (and (stringp x) (symbolp y)) (intern-in-package-of-symbol x y) (gv intern-in-package-of-symbol (x y) nil))) (defun-*1* pkg-imports (pkg) (if (stringp pkg) (pkg-imports pkg) (gv pkg-witness (pkg) nil))) (defun-*1* pkg-witness (pkg) (if (stringp pkg) (if (find-non-hidden-package-entry pkg (known-package-alist *the-live-state*)) (let ((ans (intern *pkg-witness-name* pkg))) ; See comment in intern-in-package-of-symbol for an explanation of this trick. ans) (throw-raw-ev-fncall (list 'pkg-witness-er pkg))) (gv pkg-witness (pkg) (intern *pkg-witness-name* "ACL2")))) (defun-*1* numerator (x) (if (rationalp x) (numerator x) (gv numerator (x) 0))) (defun-*1* rationalp (x) (rationalp x)) ;; RAG - I added realp to recognize real numbers. #+:non-standard-analysis (defun-*1* realp (x) (realp x)) (defun-*1* realpart (x) (if (numberp x) (realpart x) (gv realpart (x) 0))) (defun-*1* stringp (x) (stringp x)) (defun-*1* symbol-name (x) (if (symbolp x) (symbol-name x) (gv symbol-name (x) ""))) (defun-*1* symbol-package-name (x) (if (symbolp x) (progn (chk-bad-lisp-object x) (symbol-package-name x)) (gv symbol-package-name (x) ""))) (defun-*1* symbolp (x) (symbolp x)) ;; RAG - I added *1*-defns for the non-standard predicates. Note, ;; however, that the non-standard predicates do NOT have an executable ;; counterpart. (Actually, that's too hasty. Standard-part could be ;; defined as "identity" and standardp could be "t". ;; Nothing can be done about i-large-integer, though.) So, these ;; functions simply throw an error [added by Matt...: -- or, they did ;; at one time. For efficiency it was useful to allow these to compute ;; on valid ACL2 objects (see bad-lisp-objectp); actually Ruben already ;; had made such changes]. #+:non-standard-analysis (progn (defun standardp (x) (declare (ignore x)) t) (defun-*1* standardp (x) (declare (ignore x)) t) (defun standard-part (x) x) (defun-*1* standard-part (x) x) (defun i-large-integer () (throw-raw-ev-fncall '(ev-fncall-null-body-er nil i-large-integer))) (defun-*1* i-large-integer () (throw-raw-ev-fncall '(ev-fncall-null-body-er nil i-large-integer))) ) (defun-one-output macroexpand1! (x) (mv-let (erp val state) (macroexpand1 x 'oneify *the-live-state*) (declare (ignore erp state)) val)) (defvar *acl2-gentemp-counter* 0) (defun-one-output acl2-gentemp (root) (let ((acl2-pkg (find-package "ACL2"))) (loop (let ((name (coerce (packn1 (list root *acl2-gentemp-counter*)) 'string))) (if (not (find-symbol name acl2-pkg)) (return (let ((ans (intern name acl2-pkg))) ; See comment in intern-in-package-of-symbol for an explanation of this trick. ans)) (incf *acl2-gentemp-counter*)))))) (mutual-recursion (defun-one-output oneify-flet-bindings (alist fns w) ; We throw away all type declarations. If we were to keep a type declaration ; (satisfies fn), we would have to find it and convert it (at least in general) ; to (satisfies *1*fn). By ignoring such declarations, we may allow a function ; to avoid a guard violation that we might have expected, for example: ; (flet ((foo (x) (declare (type integer x)) x)) 'a) ; This is however perfectly OK, provided we are clear that flet type ; declarations are only relevant for guard verification, not guard checking. (cond ((endp alist) nil) (t (cons (let* ((def (car alist)) (dcls (append-lst (strip-cdrs (remove-strings (butlast (cddr def) 1))))) (ignore-vars (ignore-vars dcls)) (oneified-body (oneify (car (last def)) fns w))) (list* (*1*-symbol (car def)) (cadr def) (if ignore-vars (list `(declare (ignore ,@ignore-vars)) oneified-body) (list oneified-body)))) (oneify-flet-bindings (cdr alist) fns w))))) (defun-one-output oneify (x fns w) ; Keep this function in sync with translate11. Errors have generally been ; removed, since we know they can't occur. ; Fns is a list of fns that have been flet-bound. It is important not to treat ; any of these as macros. (cond ((or (atom x) (eq (car x) 'quote)) (cond ((keywordp x) (kwote x)) ((symbolp x) ; At one time we returned (defined-constant x w) here in the case that ; (legal-constantp1 x). But when the constant is replaced by its value, we can ; wind up with slow array accesses, since the constant and the written-out ; value need not be EQ. x) ((atom x) (kwote x)) (t x))) ((not (symbolp (car x))) (oneify (list* 'let (listlis (cadr (car x)) (cdr x)) (cddr (car x))) fns w)) ((eq (car x) 'return-last) ; Warning: Keep this in sync with stobj-let-fn and the handling of stobj-let in ; this function, in particular the case in which stobj-let-fn generates a call ; of prog2$. (let* ((qfn (and (consp (cdr x)) (cadr x))) (fn (or (and (consp qfn) (eq (car qfn) 'quote) (consp (cdr qfn)) (cadr qfn)) 'progn))) (cond ((eq fn 'ec-call1-raw) ; In the case of ec-call1-raw, we are already oneifying the last argument -- we ; don't want to call return-last on top of that, or we'll be attempting to take ; the *1*-symbol of the *1*-symbol! (oneify (car (last x)) fns w)) ((eq fn 'mbe1-raw) ; See the discussion in (defun-*1* return-last ...). (let ((oneified-last (oneify (car (last x)) fns w))) `(if (f-get-global 'safe-mode *the-live-state*) (,(*1*-symbol 'return-last) ,qfn ,(oneify (caddr x) fns w) ,oneified-last) ,oneified-last))) (t ; Since fn is not 'ec-call1-raw, the guard of return-last is automatically met ; for the arguments. (let ((args (oneify-lst (cddr x) fns w))) (cons fn args)))))) ((or (member-eq (car x) *oneify-primitives*) ; Note that safe-mode for make-event will require addition of the following two ; lines: ; (member-eq (car x) (primitive-event-macros)) ; (assoc-eq (car x) *super-defun-wart-table*) (member-eq (car x) *macros-for-nonexpansion-in-raw-lisp*)) (let ((args (oneify-lst (cdr x) fns w))) (cons (car x) args))) ((eq (car x) 'mv-let) (let ((value-form (oneify (caddr x) fns w)) (body-form (oneify (car (last x)) fns w))) `(mv-let ,(cadr x) ,value-form ; We leave the DECLAREs in place so that the compiler can see the ; IGNOREs at least. ,@(butlast (cdddr x) 1) ,body-form))) ; Feb 8, 1995. Once upon a time we had the following code here: ; ((eq (car x) 'the) ; (let ((value-form (oneify (caddr x) w))) ; `(the ,(cadr x) ,value-form))) ; But that led to garbage for a user defined function like ; (defun foo (x) (declare (xargs :verify-guards nil)) (the integer x)) ; because (foo 3) = 3 but (foo t) would cause a hard error. We now ; just macroexpand the just like we would any other macro. We are not ; sure why we ever thought we could handle it any other way. ((eq (car x) 'flet) ; (flet ((fn1 ...) (fn2 ...) ...) declare-form* body) (list 'flet (oneify-flet-bindings (cadr x) fns w) (oneify (car (last x)) (union-eq (strip-cars (cadr x)) fns) w))) ((eq (car x) 'translate-and-test) (oneify (caddr x) fns w)) ((eq (car x) 'with-local-stobj) (mv-let (erp st mv-let-form creator) (parse-with-local-stobj (cdr x)) (declare (ignore erp)) ; should be nil (mv-let-for-with-local-stobj mv-let-form st creator fns w))) ((eq (car x) 'stobj-let) ; Stobj-let is rather complicated, so we prefer to take advantage of the logic ; code for that macro. However, bindings of live stobjs vars may be necessary ; so that when we call a traced function on a live stobj that was stobj-let ; bound, then stobj-print-symbol can print the "{instance}" suffix, as it ; should. The easiest way to code that seems to be to go ahead and use the ; logical macroexpansion of stobj-let, and then fix it up with suitable ; bindings. (let ((temp (oneify (stobj-let-fn x) fns w))) (case-match temp ; Warning: Keep these cases in sync with stobj-let-fn. (('let bindings . rest) `(let* ,(append bindings (the-maybe-live-var-bindings (strip-cars bindings))) ,@rest)) (('progn conjoined-no-dups-exprs ('let bindings . rest)) ; Warning: Keep this case in sync with the definition of (prog2$ x y) as ; (return-last 'progn x y), and in sync with the handling of such a return-last ; form by oneify. `(progn ,conjoined-no-dups-exprs (let* ,(append bindings (the-maybe-live-var-bindings (strip-cars bindings))) ,@rest))) (& (interface-er "Implementation error: unexpected form of stobj-let ~ encountered by ~ oneify!.~|~%Input:~|~y0~%Output:~|~y1~%Please ~ contact the ACL2 implementors." x temp))))) ((member-eq (car x) '(let #+acl2-par plet)) (let* (#+acl2-par (granularity-decl (and (eq (car x) 'plet) (eq (car (cadr x)) 'declare) (cadr x))) (args #+acl2-par (if granularity-decl (cddr x) (cdr x)) #-acl2-par (cdr x)) (bindings (car args)) (post-bindings (cdr args)) (value-forms (oneify-lst (strip-cadrs bindings) fns w)) (body-form (oneify (car (last post-bindings)) fns w))) `(,(car x) #+acl2-par ,@(and granularity-decl `((declare (granularity ,(oneify (cadr (cadr (cadr x))) fns w))))) ,(listlis (strip-cars bindings) value-forms) ,@(butlast post-bindings 1) ,body-form))) #+acl2-par ((member-eq (car x) '(pand por pargs)) (if (declare-granularity-p (cadr x)) (list* (car x) `(declare (granularity ,(oneify (cadr (cadr (cadr x))) fns w))) (oneify-lst (cddr x) fns w)) (cons (car x) (oneify-lst (cdr x) fns w)))) ((eq (car x) 'throw-or-attach) ; already handled in oneify-cltl-code (interface-er "Implementation error: Unexpected call of throw-or-attach in oneify:~%~x0" x)) ((and (getprop (car x) 'macro-body nil 'current-acl2-world w) (not (member-eq (car x) fns))) (oneify (macroexpand1! x) fns w)) ((eq (car x) 'wormhole-eval) ; We know that in a well-formed term (wormhole-eval x y z), x is a quoted ; constant naming the wormhole, y is a quoted lambda of either the form (lambda ; (whs) body) or (lambda () body) that will be applied to the wormhole status, ; and z is some well-formed (irrelevant) term. The oneify of a quote is ; itself, so we don't have to do anything to x. But with y, we oneify the ; lambda body. The ``call'' of wormhole-eval laid down below is a reference to ; the macro definition for that symbol in raw Lisp. (let* ((qname (cadr x)) (qlambda (caddr x)) (formals (cadr (cadr qlambda))) (body (caddr (cadr qlambda)))) (list 'wormhole-eval qname (list 'quote (list 'lambda formals (oneify body fns w))) *nil*))) (t (let ((arg-forms (oneify-lst (cdr x) fns w))) (cons (*1*-symbol (car x)) arg-forms))))) (defun-one-output oneify-lst (lst fns w) (cond ((atom lst) nil) (t (let ((x (oneify (car lst) fns w)) (y (oneify-lst (cdr lst) fns w))) (cons x y))))) ) (defun-one-output select-stobj (name stobjs terms) (cond ((endp stobjs) nil) ((eq name (car stobjs)) (car terms)) (t (select-stobj name (cdr stobjs) (cdr terms))))) (defun-one-output super-defstobj-wart-stobjs-in (formals stobj-flag) (cond ((endp formals) nil) ((eq (car formals) stobj-flag) (cons stobj-flag (super-defstobj-wart-stobjs-in (cdr formals) stobj-flag))) (t (cons nil (super-defstobj-wart-stobjs-in (cdr formals) stobj-flag))))) (defun-one-output oneify-fail-form (er-type fn formals guard super-stobjs-in wrld extra) ; Warning: If you change this code, see the comment about "When changing ; oneify-fail-form" in oneify-cltl-code. `(throw-raw-ev-fncall (list ',er-type ',fn (list ,@formals) ',guard ',(or super-stobjs-in (stobjs-in fn wrld) (if formals (er hard 'oneify-cltl-code "I didn't think this could happen, but for fn = ~x0 ~ stobjs-in is nil and formals isn't." fn) nil)) ,extra))) (defun-one-output get-declared-stobjs (edcls) ; Keep this in sync with get-declared-stobj-names (which does checking and ; returns a value triple). (if (endp edcls) nil (union-eq (and (eq (caar edcls) 'xargs) (let ((stobjs (cadr (assoc-keyword :STOBJS (cdar edcls))))) (cond ((symbol-listp stobjs) stobjs) ((and stobjs (symbolp stobjs)) (list stobjs)) (t nil)))) (get-declared-stobjs (cdr edcls))))) (defun-one-output warn-for-guard-body (fn) (assert$ (boundp '*raw-guard-warningp*) (setq *raw-guard-warningp* nil)) (let ((state *the-live-state*)) (warning$ 'top-level "Guards" "Guard-checking will be inhibited on recursive calls of the ~ executable counterpart (i.e., in the ACL2 logic) of ~x0. To ~ check guards on all recursive calls:~% (set-guard-checking ~ :all)~%To leave behavior unchanged except for inhibiting this ~ message:~% (set-guard-checking :nowarn)" fn))) (defun-one-output create-live-user-stobjp-test (stobjs) (if (endp stobjs) t (let* ((stj (car stobjs)) (rst (create-live-user-stobjp-test (cdr stobjs))) (tst `(live-stobjp ,stj))) (cond ((eq stj 'state) rst) ((eq rst t) tst) (t `(and ,tst ,rst)))))) (defun oneify-cltl-code (defun-mode def stobj-flag wrld &optional trace-rec-for-none) ; Warning: Keep this in sync with intro-udf-lst2 for the case that defun-mode ; is nil (i.e., the non-executable case). ; This function is called when add-trip encounters a 'cltl-command triple, ; which is laid down by install-event after the triple for the symbol-class is ; laid down. Thus, the symbol-class for the function at hand has already been ; stored. Stobj-flag is the name of the stobj (whether from defstobj or ; defabsstobj), if any, that the given definition supports. ; See the template above for detailed comments, which however are not ; necessarily kept fully up-to-date. (when (and stobj-flag (null (cadr def))) ; We want to know if (car def) is a stobj creator, but it is premature to call ; stobj-creatorp using wrld because the necessary properties have not yet been ; laid down. So we use the test above. Keep this null test in sync with the ; one in stobj-creatorp. (return-from oneify-cltl-code `(,(*1*-symbol (car def)) nil (throw-raw-ev-fncall ; as in oneify-fail-form (list 'ev-fncall-creator-er ',(car def)))))) (when (null defun-mode) ; We have a non-executable function, where def is generated by intro-udf-lst2. ; Suppose an attachment is to be invoked. In the :non-executable :program case ; (i.e., ``proxy'' case, as in defproxy), we pass control to the *1* function ; for the attachment, since :skip-checks t is specified not to do any checking ; in this case (see :DOC defproxy). Otherwise, we proceed as though all checks ; have been made (again, see :DOC defproxy), in particular going straight to ; the raw Lisp function if we see that the guard is t, since then the guard of ; the attachment is also presumably true. (return-from oneify-cltl-code (case-match def ((fn formals ('declare ('xargs ':non-executable ':program)) ('throw-or-attach fn formals)) `(,(*1*-symbol fn) ,formals (throw-or-attach ,fn ,formals t))) ((fn formals ('declare ('xargs ':guard guard)) ('throw-or-attach fn formals)) `(,(*1*-symbol fn) ,formals ,(cond ((or (eq guard t) (equal guard *t*)) (car (last def))) (t `(throw-or-attach ,fn ,formals t))))) ((fn formals ('throw-or-attach fn formals)) ; implicit :guard of t (prog2$ formals ; avoid compiler warning `(,(*1*-symbol fn) ,@(cdr def)))) ((fn formals ('throw-without-attach 'nil fn formals)) (prog2$ formals ; avoid compiler warning `(,(*1*-symbol fn) ,@(cdr def)))) (& (interface-er "Implementation error: Oneify-cltl-code encountered ~ non-executable definition, ~x0, that was not what we ~ would expect to be generated by intro-udf-lst2." def))))) (let* ((dcls (append-lst (strip-cdrs (remove-strings (butlast (cddr def) 1))))) (split-types (get-unambiguous-xargs-flg1/edcls1 :split-types *unspecified-xarg-value* dcls "irrelevant-error-string")) (guards (get-guards1 dcls (cond ((or (equal split-types *unspecified-xarg-value*) ; default (eq split-types nil)) '(guards types)) (t (assert$ (eq split-types t) ; By the time we get here, we have already done our checks for the defun, ; including the check that split-types above is not an error message, and is ; Boolean. So if the assertion just above fails, then something has gone ; terribly wrong! '(guards)))) wrld)) (guard (cond ((null guards) t) ((null (cdr guards)) (car guards)) (t (cons 'and guards)))) (guard-is-t (and (or (eq guard t) (equal guard *t*)) ; If stobj-flag is true, normally the guard will not be t because it will ; include a corresponding stobj recognizer call. But the guard could be t in ; the case of a function exported by a defabsstobj, where the guard is derived ; from the :logic function specified for that export. We want to avoid the ; optimization of defining the *1* function to call the raw Lisp function in ; this case, so that appropriate live stobj checks can be made. (not (and stobj-flag ; But is it an abstract concrete stobj? (getprop stobj-flag 'absstobj-info nil 'current-acl2-world wrld))))) (fn (car def)) (*1*fn (*1*-symbol fn)) (cl-compliant-p-optimization (and (eq (symbol-class fn wrld) :common-lisp-compliant) (assert$ (eq defun-mode :logic) t))) (formals (cadr def)) (boot-strap-p (global-val 'boot-strap-flg (w *the-live-state*)))) (cond ((or (and guard-is-t cl-compliant-p-optimization) (and boot-strap-p ; optimization (well, except for :redef) (member-eq fn '(thm-fn make-event-fn certify-book-fn ; Keep the following in sync with primitive-event-macros. defun-fn ;; #+:non-standard-analysis ;; defun-std ; defun-fn defuns-fn ; mutual-recursion ;; defuns ; calls defuns-fn, above defthm-fn ;; #+:non-standard-analysis ;; defthm-std ; calls defthm-fn, above defaxiom-fn defconst-fn defstobj-fn defabsstobj-fn defpkg-fn deflabel-fn defdoc-fn deftheory-fn defchoose-fn verify-guards-fn defmacro-fn in-theory-fn in-arithmetic-theory-fn regenerate-tau-database-fn push-untouchable-fn remove-untouchable-fn reset-prehistory-fn set-body-fn table-fn progn-fn encapsulate-fn include-book-fn add-include-book-dir-fn delete-include-book-dir-fn comp-fn verify-termination-fn verify-termination-boot-strap-fn ;; add-match-free-override ; should be fast enough ; Theory-invariant is included in *macros-for-nonexpansion-in-raw-lisp*. The ; remaining members of primitive-event-macros, after theory-invariant, are ; handled well enough already since we included table-fn above. )))) ; Optimization in a common case: avoid labels function. Note that if the guard ; is t then there are no stobjs except for the recognizer, whose raw Lisp code ; can handle non-live stobjs. `(,*1*fn ,formals ,(cons fn formals))) (t (let* ((*1*guard (oneify guard nil wrld)) ; We throw away most declararations and the doc string, keeping only ignore and ; ignorable declarations. Note that it is quite reasonable to ignore ; declarations when constructing ``slow'' functions. (body (car (last def))) (*1*body ; WARNING! We need to be very careful that we only use *1*body in an ; environment where *1*fn refers to the top-level function currently being ; defined. We should not use *1*body in the scope of a top-level flet or ; labels that rebinds *1*fn, since recursive calls there of *1*fn are ; presumably intended to refer to the top-level function, not the local ; function, so that guards can be checked etc. We can, however, use *1*body in ; the binding of such a local definition. We will be free to use *1*body in ; the body of the top-level definition of *1*fn in the case of :program mode ; because we promise not to introduce a top-level flet in that case. (oneify body nil wrld)) (super-stobjs-in ; At a "leaf" of a stobj-based computation? (if stobj-flag ; Then we are looking at a function introduced by a defstobj or defabsstobj ; event. (let ((temp (super-defstobj-wart-stobjs-in formals stobj-flag))) (cond ((find-first-non-nil temp) temp) (t nil))) ; Else see if we are looking at a function that takes state but has logic code ; that does not handle a live state properly (and not just because of calls to ; lower-level functions with that problem). (cdr (assoc-eq fn *super-defun-wart-stobjs-in-alist*)))) (ignore-vars ; If super-stobjs-in is non-nil, then we will lay down a call (oneify-fail-form ; ... :live-stobj) that refers to all the formals; hence ignore-vars should be ; nil if super-stobjs-in is non-nil. When changing oneify-fail-form, consider ; changing this code as well. (and (not super-stobjs-in) (ignore-vars dcls))) (ignorable-vars (ignorable-vars dcls)) (guard-checking-on-form ; Functions in the ev-rec nest have a gc-off parameter that we generally assume ; to correspond with the state global guard-checking-on used here, so that the ; logic-only and raw lisp code agree. See the comment in *ev-shortcut-okp*. (cond (super-stobjs-in '(let ((temp (f-get-global 'guard-checking-on *the-live-state*))) (cond ((or (eq temp :none) (eq temp nil)) ; Calls of a stobj primitive that takes its stobj as an argument are always ; guard-checked. If that changes, consider also changing ; ev-fncall-rec-logical. t) (t temp)))) (t '(f-get-global 'guard-checking-on *the-live-state*)))) (skip-early-exit-code-when-none (and (eq defun-mode :logic) ; :program handled elsewhere ; We generally skip some special "early exit" code when 'guard-checking-on has ; value :none. But it seems scary to allow :none to avoid raw Lisp for ; built-ins, even in :logic mode, because of efficiency. So when we are in the ; boot-strap, we do the early exit code (which can call the raw Lisp function) ; even if 'guard-checking-on has value :none. Exception: We want the-check to ; avoid guard errors when 'guard-checking-on has value :none. (or (not boot-strap-p) (eq fn 'the-check)))) (guard-checking-is-really-on-form ; This variable should only be used in the scope of the binding expression for ; early-exit-form. (cond (super-stobjs-in ; The value of guard-checking-on has already been coerced from :none or nil to ; t, in guard-checking-on-form. t) (skip-early-exit-code-when-none ; As mentioned above, guard-checking-is-really-on-form is only used for ; defining early-exit-form. But evaluation of early-exit-form is skipped when ; 'guard-checking-on has value :none in the present case, where ; skip-early-exit-code-when-none is true. guard-checking-on-form) (t `(and ,guard-checking-on-form (not (eq ,guard-checking-on-form :none)))))) (fail_guard ; form for reporting guard failure (oneify-fail-form 'ev-fncall-guard-er fn formals guard super-stobjs-in wrld (and super-stobjs-in '(cond ((member-eq (f-get-global 'guard-checking-on *the-live-state*) '(nil :none)) :live-stobj) (t ; Suppress "See :DOC set-guard-checking" printed by ev-fncall-guard-er-msg. :no-extra))))) (fail_safe ; form for reporting guard or safe mode failure (oneify-fail-form 'ev-fncall-guard-er fn formals guard super-stobjs-in wrld t)) (safe-form ; Functions in the ev-rec nest have a safe-mode parameter that we generally ; assume to agree with the state global safe-mode, so that the logic-only and ; raw lisp code agree. See the comment in *ev-shortcut-okp*. '(f-get-global 'safe-mode *the-live-state*)) (super-stobjs-chk (if stobj-flag (let ((first-non-nil (find-first-non-nil super-stobjs-in))) `(live-stobjp ,first-non-nil)) `(live-state-p ,(select-stobj 'state super-stobjs-in formals)))) (declared-stobjs (if stobj-flag (list stobj-flag) (get-declared-stobjs dcls))) (user-stobj-is-arg (and declared-stobjs (not (equal declared-stobjs '(state))))) (live-stobjp-test (create-live-user-stobjp-test declared-stobjs)) (declare-stobj-special ; Without a special declaration for the live stobj, a defstobj or defabsstobj ; event will introduce *1* functions in add-trip, via a defuns trip, before the ; defstobj or defabsstobj trip introduces the live stobj variable as special. ; This might not be a big deal unless we compile, by which time (at the end of ; processing the defstobj or defabsstobj trip) the live stobj variable has been ; introduced with defparameter, thus globally declaring it special. However, ; CCL complains because compilation is done when the *1* function is first ; introduced. It seems appropriate to declare the live stobj variable special ; as soon as it is referenced, in such *1* functions, even though CCL might be ; the only Lisp that could need this done. (and stobj-flag `(declare (special ,(the-live-var stobj-flag))))) (guarded-primitive-p ; We want to check guards on the "leaves" of a computation in safe-mode, for ; example, on a call of EQ. Evaluation in the ACL2 logic can only diverge from ; evaluation in (raw) Common Lisp when a guard is violated on a function that ; is already defined in Common Lisp. A function considered here that is at ; risk for such divergence has a non-T guard, is being defined in the ; boot-strap, and is not in the ACL2 package (which is unknown to Common Lisp). ; So as we generate code here, we restrict the additional guard-check in ; safe-mode to such functions. (and (not guard-is-t) ; we are trusting guards on the primitives! boot-strap-p (not (member-equal (symbol-package-name fn) '("ACL2" "ACL2-PC"))))) (*1*fn-binding `(,*1*fn ,formals ,@(and declare-stobj-special (list declare-stobj-special)) ,@(and ignore-vars `((declare (ignore ,@ignore-vars)))) ,@(and ignorable-vars `((declare (ignorable ,@ignorable-vars)))) ,@(and super-stobjs-in ; If the form below is removed, we might expect to get a hard Lisp error from ; the following: ; (defstobj foo (arr :type (array t (10)))) ; (set-guard-checking nil) ; (update-arri 20 4 foo) ; The problem would seem to be that an ill-guarded call of update-nth has ; replaced a copy of the stobj array by a list in (user-stobj-alist ; *the-live-state*), which produces a mismatch with *the-live-foo*. ; However, no such error occurs. At some point we may spend the energy to ; convince ourselves that it is safe to remove this code, but for now, it seems ; harmless enough to leave it here, since super-stobjs-chk is a fast test. `((when ,super-stobjs-chk ,(oneify-fail-form 'ev-fncall-guard-er fn formals guard super-stobjs-in wrld :live-stobj)))) ,*1*body)) (*1*-labels-form `(labels (,*1*fn-binding) (,*1*fn ,@formals))) (logic-recursive-p (and (eq defun-mode :logic) (ffnnamep-mod-mbe fn (body fn nil wrld)))) (labels-can-miss-guard (and logic-recursive-p ; there is no labels form for :program ; If the function is common-lisp-compliant, then the only way we can fall ; through to the labels form is if guard-checking is nil or :none (not :all), ; in which case there is no reason to warn. (not cl-compliant-p-optimization) (not guard-is-t))) (trace-rec-for-none ; If trace-rec-for-none is non-nil, then we guarantee that if ; 'guard-checking-on is set to :none, we will see all the recursive calls. ; This is useful for tracing. Extra code for this case is only necessary for ; :logic mode, since the only issue is the call of a labels function, which ; only occurs in :logic mode. (and trace-rec-for-none logic-recursive-p (eq defun-mode :logic))) (program-only (and (eq defun-mode :program) ; optimization (member-eq fn ; If this test becomes an issue, we might consider reimplementing the ; program-only mechanism by way of :program-only xargs, which would place a ; property that we can look up. Careful though -- at this point we probably ; have not yet installed a world with all the new properties of fn. (f-get-global 'program-fns-with-raw-code *the-live-state*)))) (fail_program-only-safe ; At one time we put down a form here that throws to the tag 'raw-ev-fncall: ; (oneify-fail-form 'program-only-er fn formals guard ; super-stobjs-in wrld ; t) ; However, because that throw is caught (in the function raw-ev-fncall), it ; should be accounted for in the function ev-fncall-rec-logical. However, that ; function does not take state, which is unfortunate since the program-only ; case (under which we lay down this form) is based on state global ; 'program-fns-with-raw-code. We considered moving that global to the world, ; but were concerned about the effects that would have on ACL2s (see for ; example the use of program-fns-with-raw-code in ; workshops/2007/dillinger-et-al/code/hacker.lisp), and in general we'd have to ; add yet another event and deal with whether the event should be local to ; books. Instead, we have decided to cause a raw Lisp error, which is always ; legitimate (after all, Lisp might cause a resource error). `(error "~%~a~%" (fms-to-string "~@0~%~@1" (list (cons #\0 (program-only-er-msg ',fn (list ,@formals) t)) (cons #\1 "~%Note: If you have a reason to ~ prefer an ACL2 error here instead of ~ a hard Lisp error, please contact the ~ ACL2 implementors.")) :evisc-tuple (abbrev-evisc-tuple *the-live-state*) :fmt-control-alist (list (cons 'fmt-hard-right-margin (f-get-global 'fmt-hard-right-margin *the-live-state*)) (cons 'fmt-soft-right-margin (f-get-global 'fmt-soft-right-margin *the-live-state*)))))) (early-exit-code (let ((cl-compliant-code-guard-not-t ; We lay down code for the common-lisp-compliant case that checks the guard and ; acts accordingly: if the guard checks, then it returns the result of calling ; fn, and if not, then it fails if appropriate and otherwise falls through. (and (not guard-is-t) ; optimization for code below ; NOTE: we have to test for live stobjs before we evaluate the guard, since the ; Common Lisp guard may assume all stobjs are live. We actually only need ; stobjs to be live that occur in the guard in other than stobj recognizer ; calls; but we take the easy way out (except for a stobj-flag case below) and ; check that all stobjs are live before evaluating the raw Lisp guard. After ; all, the cost of that check is only some eq tests. `(cond ,(cond ((eq live-stobjp-test t) `(,guard (return-from ,*1*fn (,fn ,@formals)))) (t `((if ,live-stobjp-test ,(if stobj-flag ; Essay on Stobj Guard Attachments ; We disallow attachments during evaluation of guards on behalf of any stobj ; updater. The example below, which is a slight modification of one provided ; by Jared Davis, shows why this is important for defstobj. The idea is that ; in order to preserve the invariant that the stobj recognizer holds, it ; suffices that the initial stobj satisfies that recognizer and that the guard ; holds for each call of an updater. But suppose for example that an ; attachment is used in evaluating the guard for the first update. If later ; (perhaps after many more updates) we change that attachment, then that guard ; now may be false, and thus the recognizer may fail to hold after the first ; update and thus fail to hold currently. On the other hand, if such guard ; evaluation never involves attachments, then since the initial stobj provably ; satisfies the recognizer, then since each updater guard holds (in fact, ; provably holds), the resulting stobjs all satisfy the recognizer. ; The discussion above applies not only in the case of defstobj but also in the ; case of defabsstobj. Consider an abstract function exported from a ; defabsstobj event that updates a stobj. The avoidance of attachments ; guarantees that every abstract stobj update satisfies the abstract function's ; guard and hence, by the {preserved} theorems, results in an abstract stobj ; that satisfies the abstract predicate -- provably, since we are dealing with ; ground terms. Moreover, because of the {guard-thm} theorems we know that the ; concrete predicate provably holds as well, and hence won't be "revoked" as in ; the preceding paragraph. ; Why do we care about guards on concrete stobjs? For all we know, failure to ; respect those guards could result in corruption of the Lisp process. An ; obvious case would be if a stobj field is an array of bits, laid out ; compactly according to that spec, and we update with an arbitrary object ; (say, a cons). Although it seems that only a SATISFIES type declaration ; could result in a attachments being used for guard evaluation, we are ; conservative here. After all, adding a binding of *aokp* is cheap in the ; context of evaluating just the guard. ; Note that it is not sufficient to ensure for an abstract stobj that the ; corresponding concrete stobj always satisfies its recognizer. It is easy to ; imagine a defabsstobj :export field that specifies the identify function for ; its :logic component, returning the stobj unchanged, but for the :exec ; component makes an ill-guarded call to update the stobj, corrupting the Lisp ; imagine, before restoring the stobj. In raw Lisp, this could really happen ; because the export is a macro that calls the :exec function directly; the ; only guard that need be met before this happens is a variant of the :logic ; function's guard, at the level of *1* function of the export. ; Finally, here is the example promised above. ; (progn ; (defstub foop (x) t) ; (defun barp (x) ; (declare (xargs :guard t)) ; (or (not x) ; (foop x))) ; (defstobj st ; (fld :type (satisfies barp))) ; (defthm barp-of-fld ; (implies (stp st) ; (barp (fld st)))) ; (defun my-integerp (x) ; (declare (xargs :guard t)) ; (integerp x))) ; (defattach foop my-integerp) ; (trace$ foop barp my-integerp) ; (update-fld 3 st) ; note that foop calls its attachment, my-integerp ; (defattach foop consp) ; (barp (fld st)) ; nil (ouch) ; (stp st) ; returns t, but is really (logically) nil ; The code just below ensures that the updater will be evaluated without ; attachments. It might needlessly ensure that other functions introduced by ; defstobj (for the given stobj-flag) are evaluated without attachments, for ; example if the getprop below returns nil because the necessary property has ; not yet been put into wrld. But as of this writing, the test seems to apply ; only to stobj updaters and resize functions. (let ((stobjs-out (getprop fn 'stobjs-out nil 'current-acl2-world wrld))) (cond ((and stobjs-out ; property is there (all-nils stobjs-out)) guard) (t `(let ((*aokp* nil)) ,guard)))) guard) ,*1*guard) ,(assert$ ; No user-stobj-based functions are primitives for which we need to give ; special consideration to safe-mode. (not guarded-primitive-p) `(cond (,live-stobjp-test (return-from ,*1*fn (,fn ,@formals)))))))) ,@(cond (super-stobjs-in `((t ,fail_guard))) (guarded-primitive-p `(((or ,guard-checking-is-really-on-form ,safe-form) ,fail_safe))) (t `((,guard-checking-is-really-on-form ,fail_guard)))))))) (if cl-compliant-p-optimization (assert$ (not guard-is-t) ; already handled way above (list cl-compliant-code-guard-not-t)) (let ((cond-clauses `(,@(and (eq defun-mode :logic) ; If the guard is t, then we want to execute the raw Lisp code in the ; :common-lisp-compliant case even if guard-checking is :none. This ; early-exit-code is only executed when guard-checking is not :none, so ; we need to handle that special case (:none, guard t) elsewhere, and ; we do so in *1*-body-forms below. (not guard-is-t) `(((eq (symbol-class ',fn (w *the-live-state*)) :common-lisp-compliant) ,cl-compliant-code-guard-not-t))) ,@(and (not guard-is-t) (cond (super-stobjs-in `(((not ,*1*guard) ,fail_guard))) (guarded-primitive-p `(((and (or ,safe-form ,guard-checking-is-really-on-form) (not ,*1*guard)) ,fail_safe))) (t `(((and ,guard-checking-is-really-on-form (not ,*1*guard)) ,fail_guard))))) ,@(cond ((eq defun-mode :program) ; In the boot-strap world we have :program mode functions whose definitions are ; different in raw Lisp from the logic, such as ev and get-global. If we allow ; :all or :none to serve their purposes for those functions, we can wind up ; with unpleasant guard violations. For example, wet (when we had it) expanded ; to a call of with-error-trace-fn, and if the idea of :all is applied then we ; get the following sequence of calls in the logic (i.e., using their *1* ; functions): with-error-trace-fn, trans-eval, ev, ev-rec, ev-fncall-rec, ; ev-fncall-rec-logical, w-of-any-state, and finally global-table. The last of ; these calls causes a guard violation since *the-live-state* is not a true ; list. ; Also, we want to make sure that built-in :program mode functions run fast, ; for example, defthm-fn. ; And finally, this is where we finish handling of safe-mode for ; guarded-primitive-p functions, specifically those in :program mode since if ; such a function is in :logic mode then either it is non-executable or ; :common-lisp-compliant (see check-none-ideal), hence is handled above. (cond (boot-strap-p `((,safe-form ,(cond (program-only fail_program-only-safe) (t `(return-from ,*1*fn ,*1*body)))))) (t (cond (program-only `((,safe-form ,fail_program-only-safe) ((member-eq ,guard-checking-on-form '(:none :all)) (return-from ,*1*fn ,*1*body)))) ; We will not be laying down a labels call, so we go ahead and stay in the *1* ; world here in the case of safe-mode. (t `(((or (member-eq ,guard-checking-on-form '(:none :all)) ,safe-form) (return-from ,*1*fn ,*1*body)))))))))))) (and cond-clauses (list (cons 'cond cond-clauses))))))) (main-body-before-final-call ; This is the code that is executed before we fall through: to the final labels ; function in the :logic mode case, but to the non-*1* call in the :program ; mode case. (append (and user-stobj-is-arg `((when *wormholep* (wormhole-er (quote ,fn) (list ,@formals))))) (and (eq defun-mode :logic) ; else :program guard-is-t (assert$ ; compliant with guard t is handled early above (not cl-compliant-p-optimization) `((when (eq (symbol-class ',fn (w *the-live-state*)) :common-lisp-compliant) (return-from ,*1*fn (,fn ,@formals)))))) (cond ((and skip-early-exit-code-when-none early-exit-code) ; else nil; next case provides nil (cond (super-stobjs-in early-exit-code) ; optimization (t `((when (not (eq ,guard-checking-on-form :none)) ,@early-exit-code))))) (t early-exit-code)) (cond (trace-rec-for-none `((return-from ,*1*fn ,*1*body))) (labels-can-miss-guard `((when (eq ,guard-checking-on-form :all) (return-from ,*1*fn ,*1*body))))) (and (and labels-can-miss-guard (not trace-rec-for-none)) ; else skip labels form `((when (and *raw-guard-warningp* (eq ,guard-checking-on-form t)) (warn-for-guard-body ',fn)))))) (*1*-body-forms (cond ((eq defun-mode :program) (append main-body-before-final-call `((,fn ,@formals)))) (trace-rec-for-none main-body-before-final-call) (t (append main-body-before-final-call (list *1*-labels-form)))))) `(,*1*fn ,formals ,@(and declare-stobj-special (list declare-stobj-special)) ; At one time we attempted to do some code-sharing using a macro call, by using ; *1*body-call in place of *1*body in the code above, where *1*body-call was ; defined as shown below. But with an ACL2 image built on Allegro, for (ld ; "books/rtl/rel4/support/merge.lisp") with (set-inhibit-output-lst '(prove ; proof-tree)) after (prof:start-profiler), it took 127.5 seconds to run such a ; modification of oneify-cltl-code, as opposed to 103.5 seconds. Granted, we ; chose this file because it was shown in some earlier experiments with ; macrolet to have a particularly bad slowdown over previous versions without ; macrolet. But still, we suffer the extra code for recursive :ideal-mode ; functions rather than generate macrolet forms. Below are the relevant ; bindings used in a previous version of this code, in case we decide to ; revisit this approach. ; (*1*body-call-shared-p ; ; ; We want to keep code size down by using macrolet to share the *1*body ; ; ; expression, but preferably not otherwise, to avoid overhead that we seem to ; ; ; have observed, at least in Allegro CL, for expanding (uncompiled) macrolet ; ; ; calls. The expression below should thus agree with the governing conditions ; ; ; for the occurrences of *1*body-call outside the labels function that will ; ; ; also occur in a corresponding labels function. The latter rules out the case ; ; ; (eq defun-mode :program). ; ; ; (ffnnamep fn (body fn nil wrld))) ; (*1*body-macro (and *1*body-call-shared-p ; (acl2-gentemp "*1*BODY-MACRO"))) ; (*1*body-call (if *1*body-call-shared-p ; `(,*1*body-macro) ; *1*body)) ; ; ;;; end of let* bindings .... and here is the replacement for ,@*1*-body-forms ; ;;; below: ; ; ,@(if *1*body-call-shared-p ; `((macrolet ((,*1*body-macro () ',*1*body)) ; ,@*1*-body-forms)) ; *1*-body-forms) ,@*1*-body-forms)))))) ; PROMPTS ; New ACL2 users sometimes do not notice that they are outside the ACL2 ; read-eval-print loop when in a break. We modify the prompts when we see how ; to do so, so that in a break we see "[RAW LISP]" in the prompt. For most ; lisps, this seems to require changing the prompt at the top-level too, not ; just in a break. (defvar *saved-raw-prompt* nil) (defvar *saved-raw-prompt-p* nil) #+allegro (progn (defun-one-output install-new-raw-prompt () (cond ((not *saved-raw-prompt-p*) (setq *saved-raw-prompt* tpl:*prompt*) (setq tpl:*prompt* (concatenate 'string *saved-raw-prompt* "[RAW LISP] ")) (setq *saved-raw-prompt-p* t)))) (defun-one-output install-old-raw-prompt () (cond (*saved-raw-prompt-p* (setq tpl:*prompt* *saved-raw-prompt*) (setq *saved-raw-prompt-p* nil) (setq *saved-raw-prompt* nil) ; no longer needed; free storage t)))) #+clisp (progn (defun-one-output install-new-raw-prompt () (cond ((not *saved-raw-prompt-p*) (setq *saved-raw-prompt* custom::*prompt-body*) (setq custom::*prompt-body* ; attempt to mimic clisp 2.33 (function (lambda () (if (equal system::*home-package* *package*) (format nil "[RAW LISP]") (format nil "~a [RAW LISP]" (package-name *package*)))))) (setq *saved-raw-prompt-p* t)))) (defun-one-output install-old-raw-prompt () (cond (*saved-raw-prompt-p* (setq custom::*prompt-body* *saved-raw-prompt*) (setq *saved-raw-prompt-p* nil) (setq *saved-raw-prompt* nil) ; no longer needed; free storage t)))) #+cmu (progn (defun-one-output install-new-raw-prompt () (setq debug:*debug-prompt* (function (lambda () (debug::debug-prompt) (format t "[RAW LISP] ") (force-output t))))) (defun-one-output install-old-raw-prompt () (setq debug:*debug-prompt* (function debug::debug-prompt)))) #+ccl (progn (defun-one-output install-new-raw-prompt () (cond ((not *saved-raw-prompt-p*) (setq *saved-raw-prompt* (symbol-function 'ccl::print-listener-prompt)) (let ((ccl:*warn-if-redefine-kernel* nil)) (setf (symbol-function 'ccl::print-listener-prompt) (lambda (stream &rest args) (declare (ignore stream)) (apply *saved-raw-prompt* *debug-io* args) (format *debug-io* "[RAW LISP] ")))) (setq *saved-raw-prompt-p* t)))) (defun-one-output install-old-raw-prompt () (cond (*saved-raw-prompt-p* (let ((ccl:*warn-if-redefine-kernel* nil)) (setf (symbol-function 'ccl::print-listener-prompt) *saved-raw-prompt*)) (setq *saved-raw-prompt-p* nil) (setq *saved-raw-prompt* nil) ; no longer needed; free storage t)))) #+(and gcl (not cltl2)) ; We are a bit sorry that we messed around at this low a level, and choose not ; to do so for ANSI GCL. (progn (defun-one-output install-new-raw-prompt () (cond ((not (and (eql si::*gcl-major-version* 2) (eql si::*gcl-minor-version* 6))) (cond (*lp-ever-entered-p* (er hard 'install-new-raw-prompt "Install-new-raw-prompt is only supported in GCL 2.6 and ~ its sub-versions. This appears to be a GCL ~s0.~s1." si::*gcl-major-version* si::*gcl-minor-version*)) (t (setq *saved-raw-prompt-p* t)))) ((not *saved-raw-prompt-p*) (setq si::*debug-prompt-suffix* "[RAW LISP]") (setf *saved-raw-prompt* (symbol-function 'si::break-level)) (setf (symbol-function 'si::break-level) (symbol-function 'si::break-level-for-acl2)) (setq *saved-raw-prompt-p* t)))) (defun-one-output install-old-raw-prompt () (cond (*saved-raw-prompt-p* (setq si::*debug-prompt-suffix* "") (setf (symbol-function 'si::break-level) ; Since we set si::*debug-prompt-suffix*, we really don't have to revert ; (symbol-function 'si::break-level) -- unless our patch, ; 'si::break-level-for-acl2 is out of sync with the current GCL's ; 'si::break-level. So we play it safe and revert. *saved-raw-prompt*) (setq *saved-raw-prompt-p* nil) (setq *saved-raw-prompt* nil) ; no longer needed; free storage t)))) #-(or allegro clisp cmu ccl (and gcl (not cltl2))) (progn (defun-one-output install-new-raw-prompt () nil) (defun-one-output install-old-raw-prompt () nil) ) ; DYNAMICALLY MONITOR REWRITES ("dmr") ;;; User-settable dmr variables ; User settable to any positive number, indicating the number of pushes on the ; gstack before *dmr-file-name* is written. ; If you set this, consider also setting Emacs Lisp variable ; *acl2-timer-display-interval*. (defvar *dmr-interval* 1000) (defvar *dmr-interval-acl2-par-hack* 300000) (defvar *dmr-interval-used*) ; This variable's positive integer value indicates the maximum indentation for ; each line in the display. Lines that otherwise would exceed this indentation ; are instead shown as ; {x} n. .... ; where x is the 0-based indentation depth. (defvar *dmr-indent-max* 20) ; User settable, but then you need to set *acl2-monitor-buffer-name* and ; *dmr-file-name* in emacs file monitor.el. The main reason to change ; this would be if you are running emacs on a different machine than the one on ; which you are running ACL2. (defvar *dmr-file-name*) (defun dmr-file-name () (let ((user (or (getenv$-raw "USER") (progn (format t "Warning: Unable to determine USER ~ environment variable for ~ dmr-file-name.~%Will treat USER as ~ SOME-USER. In emacs, evaluate:~%(setq ~ *dmr-file-name* ~ \"/tmp/acl2-dmr-SOME-USER\")~%") "SOME-USER")))) (concatenate 'string "/tmp/acl2-dmr-" user))) ;;; Code implementing dmr in ACL2 (defparameter *dmr-stream* nil) (defparameter *dmr-counter* ; For the sake of GCL, we may want to consider consider using a 0-dimensional ; fixnum array instead. If so, then consider whether *dmr-interval* should ; also be similarly changed. 0) #+acl2-par (defun dmr-acl2-par-hack-p () (f-get-global 'waterfall-parallelism *the-live-state*)) (defun dmr-stop-fn-raw () (when *dmr-stream* (let ((str *dmr-stream*)) (setq *dmr-stream* nil) (close str)))) (defun initialize-dmr-interval-used () (setq *dmr-interval-used* #+acl2-par (cond ((dmr-acl2-par-hack-p) *dmr-interval-acl2-par-hack*) (t *dmr-interval*)) #-acl2-par *dmr-interval*)) (defun dmr-start-fn-raw (state) (initialize-dmr-interval-used) (or (boundp '*dmr-file-name*) (setq *dmr-file-name* (dmr-file-name))) (setq *dmr-stream* (open *dmr-file-name* :if-exists :supersede ; :overwrite doesn't open non-existent file in CCL :direction :output #+acl2-par :sharing #+acl2-par :lock ; the default of :private is single-threaded )) state) (defvar *dmr-array* (make-array 10000)) ; start with default length of cw-gstack (defun reverse-into-dmr-array (lst) (let ((len-1 (1- (length lst)))) (when (< (length *dmr-array*) len-1) (setq *dmr-array* (make-array (* 2 len-1)))) (loop for i from len-1 downto 0 as tail on lst do (setf (aref *dmr-array* i) (car tail))) len-1)) (defparameter *dmr-reusable-string* (make-array '(0) :element-type ; SBCL and non-ANSI GCL complain about setting the fill-pointer if we use ; 'base-char. #+(or sbcl (and gcl (not cltl2))) 'character #-(or sbcl (and gcl (not cltl2))) 'base-char :fill-pointer 0 :adjustable t)) (defvar *dmr-indent*) (defmacro dmr-increment-indent () '(setq *dmr-indent* (+ 2 *dmr-indent*))) (defun tilde-@-bkptr-string (calling-sys-fn called-sys-fn bkptr) ; Warning: Keep this in sync with tilde-@-bkptr-phrase. ; This function builds a ~@ phrase explaining how two adjacent frames ; are related, given the calling sys-fn, the called sys-fn and the ; bkptr supplied by the caller. See cw-gframe for the use of this ; phrase. ; WARNING: This function can have a side effect of setting ; *dmr-indent*. It would be cleaner to use multiple values instead; maybe some ; day when we have time (!) or if someone volunteers. (case called-sys-fn (rewrite (cond ((integerp bkptr) (cond ((eq calling-sys-fn 'rewrite-with-lemma) (dmr-increment-indent) (format nil " the atom of hypothesis ~s" bkptr)) ((eq calling-sys-fn 'simplify-clause) (format nil " the atom of literal ~s" bkptr)) (t (format nil " argument ~s" bkptr)))) ((consp bkptr) (dmr-increment-indent) (format nil " the ~arhs of hypothesis ~s" (if (eq (car bkptr) 'rhs2) "rewritten " "") (cdr bkptr))) ((symbolp bkptr) (case bkptr (guard " the guard") (body " the body") (lambda-body " the lambda body") (rewritten-body " the rewritten body") (expansion " the expansion") (equal-consp-hack-car " the equality of the cars") (equal-consp-hack-cdr " the equality of the cdrs") (rhs " the rhs of the conclusion") (meta " the result of the metafunction") (nth-update " the result of the nth/update rewriter") (multiply-alists2 " the product of two polys") (forced-assumption " a forced assumption") (proof-checker " proof-checker top level") (otherwise (er hard 'tilde-@-bkptr-string "When ~x0 calls ~x1 we get an unrecognized ~ bkptr, ~x2." calling-sys-fn called-sys-fn bkptr)))) (t (er hard 'tilde-@-bkptr-string "When ~x0 calls ~x1 we get an unrecognized bkptr, ~x2." calling-sys-fn called-sys-fn bkptr)))) ((rewrite-with-lemma setup-simplify-clause-pot-lst simplify-clause synp) "") (t (er hard 'tilde-@-bkptr-string "When ~x0 calls ~x1 we get an unrecognized bkptr, ~x2." calling-sys-fn called-sys-fn bkptr)))) (defvar *dmr-interp-state* ; This tells us whether we have already printed "argument(s)" when printing ; gframes with sys-fn = rewrite. 0) (defun dmr-interp-fresh-rewrite-p (calling-sys-fn frame) ; Assumes that (access gframe frame :sys-fn) is rewrite. (not (and (eq calling-sys-fn 'rewrite) (integerp (access gframe frame :bkptr))))) (defun dmr-prefix () (if (> *dmr-indent* *dmr-indent-max*) (concatenate 'string (aref1 'acl2-built-in-spaces-array *acl2-built-in-spaces-array* *dmr-indent-max*) (format nil "{~s} " *dmr-indent*)) (aref1 'acl2-built-in-spaces-array *acl2-built-in-spaces-array* *dmr-indent*))) (defun dmr-interp (i calling-sys-fn frame) ; Warning: Keep this in sync with cw-gframe. ; This prints a gframe, frame, which is known to be frame number i and ; was called by calling-sys-fn. (let ((sys-fn (access gframe frame :sys-fn))) (case sys-fn (rewrite (cond ((dmr-interp-fresh-rewrite-p calling-sys-fn frame) (setq *dmr-interp-state* 0) (let ((bkptr-string ; evaluate now, before printing (tilde-@-bkptr-string calling-sys-fn 'rewrite (access gframe frame :bkptr)))) (format nil "~a~s. Rewriting (to ~a)~a" (dmr-prefix) i (let ((obj (cddr (access gframe frame :args)))) (cond ((eq obj nil) "falsify") ((eq obj t) "establish") (t "simplify"))) bkptr-string))) ((eq *dmr-interp-state* 0) (setq *dmr-interp-state* 1) (format nil "; argument(s) ~s" (access gframe frame :bkptr))) (t (format nil "|~s" (access gframe frame :bkptr))))) (rewrite-with-lemma (format nil "~a~s. Applying ~s~%" (dmr-prefix) i (access rewrite-rule (cdr (access gframe frame :args)) :rune))) (add-terms-and-lemmas (let ((len (length (car (access gframe frame :args))))) (format nil "~a~s. Applying linear arithmetic to ~a ~s term~a~%" (dmr-prefix) i (let ((obj (cdr (access gframe frame :args)))) (cond ((eq obj nil) "falsify") ((eq obj t) "establish") (t "simplify"))) len (if (eql len 1) "" "s")))) (non-linear-arithmetic (let ((len (length (access gframe frame :args)))) (format nil "~a~s. Applying non-linear arithmetic to ~s var~a~%" (dmr-prefix) i len (if (eql len 1) "" "s")))) (synp (let ((synp-fn (access gframe frame :args))) (dmr-increment-indent) (format nil "~a~s. Entering ~s for hypothesis ~s~%" (dmr-prefix) i synp-fn (access gframe frame :bkptr)))) (setup-simplify-clause-pot-lst (format nil "~a~s. Setting up the linear pot list~%" (dmr-prefix) i)) (otherwise ; Note that we leave it to pstk to handle simplify-clause. (er hard 'dmr-interp "Unrecognized sys-fn, ~x0" (access gframe frame :sys-fn)))))) (defvar *dmr-delete-string* ; WARNING: Keep this in sync with monitor.el. "delete-from-here-to-end-of-buffer") (defun dmr-string () #+acl2-par (when (dmr-acl2-par-hack-p) (return-from dmr-string (print-interesting-parallelism-variables-str))) (when (null *pstk-stack*) (setq *dmr-counter* *dmr-interval-used*) ; will flush next time (setq *saved-deep-gstack* nil) (setq *deep-gstack* nil) (return-from dmr-string *dmr-delete-string*)) (setf (fill-pointer *dmr-reusable-string*) 0) (let* ((pstk-tokens (loop for x in *pstk-stack* with result = nil do (push (cond ((eq (car x) 'waterfall) (car (nthcdr 8 x))) ; ctx ((eq (car x) 'ev-fncall) (list (car x) (cadr x))) (t (car x))) result) finally (return result))) ; reversed (pstk-tokens-tail pstk-tokens) (len-1 (reverse-into-dmr-array *deep-gstack*)) (calling-sys-fn 'start) (*print-pretty* nil) (counter 0) (*dmr-indent* 3) (no-newline-last-time nil)) (with-output-to-string (s *dmr-reusable-string*) (loop for token in pstk-tokens do (progn (setq pstk-tokens-tail (cdr pstk-tokens-tail)) (cond ((member-eq token '(rewrite-atm setup-simplify-clause-pot-lst)) (return)) (t (princ (format nil " ~s. ~s~%" counter token) s) (incf counter))))) (loop for i from 0 to len-1 do (let* ((frame (aref *dmr-array* i)) (sys-fn (access gframe frame :sys-fn))) (when (not (eq sys-fn 'simplify-clause)) ; First, print a newline if we didn't print one last time and we are not ; printing args for sys-fn = rewrite. (setq no-newline-last-time (eq calling-sys-fn 'rewrite)) (when (and no-newline-last-time ; no newline last time (or ; not printing args for rewrite (not (eq sys-fn 'rewrite)) (dmr-interp-fresh-rewrite-p calling-sys-fn frame))) (terpri s)) (princ (dmr-interp counter calling-sys-fn frame) s) (incf counter)) (setq calling-sys-fn (access gframe frame :sys-fn)))) (when (eq calling-sys-fn 'rewrite) ; no newline last time (terpri s)) (dmr-increment-indent) (loop for token in pstk-tokens-tail ; We avoid printing ev-fncall-meta because such a call can invoke mfc-rw, ; creating lower gstack entries, resulting in several ev-fncall-meta tokens ; being printed here (at the bottom of the displayed stack) rather than ; interspersed in the display. Rather than track gstack/pstk interaction, we ; simply avoid printing ev-fncall-meta tokens, though pstk itself does flush ; the display out to file when entering ev-fncall-meta. when (not (eq token 'ev-fncall-meta)) do (progn (princ (if (eq token 'add-polynomial-inequalities) (format nil "~a~s. ~s: ~s calls~%" (dmr-prefix) counter token *add-polys-counter*) (format nil "~a~s. ~s~%" (dmr-prefix) counter token)) s) (incf counter))) (princ *dmr-delete-string* s))) *dmr-reusable-string*) (declaim (inline dmr-flush1)) (defun dmr-flush1 (&optional reset-counter) (when *dmr-stream* (file-position *dmr-stream* :start) (princ (dmr-string) *dmr-stream*) #-ccl (force-output *dmr-stream*) #+ccl ; fix for "Expected newpos" error (thanks, Gary Byers) (ccl::without-interrupts (force-output *dmr-stream*)) (setq *saved-deep-gstack* *deep-gstack*) (when reset-counter (setq *dmr-counter* 0)) t)) #+acl2-par (defvar *dmr-lock* (make-lock)) (defun dmr-flush (&optional reset-counter) #+acl2-par (when (dmr-acl2-par-hack-p) (return-from dmr-flush (cond ((> *dmr-counter* *dmr-interval-used*) (setq *dmr-counter* 0) (with-lock *dmr-lock* (dmr-flush1))) (t (setq *dmr-counter* (1+ *dmr-counter*)))))) (dmr-flush1 reset-counter)) (defun dmr-display () #+acl2-par (when (dmr-acl2-par-hack-p) (return-from dmr-display (cond ((> *dmr-counter* *dmr-interval-used*) (setq *dmr-counter* 0) (dmr-flush)) (t (setq *dmr-counter* (1+ *dmr-counter*)))))) (dmr-flush)) (defun cw-gstack-short () (let* ((str (dmr-string)) (pos (search *dmr-delete-string* str))) (princ (if pos (subseq str 0 pos) str) *terminal-io*))) ; INITIALIZATION OF CURRENT ACL2 WORLD ; Once upon a time (pre-V2.2) we had the following defvar here: ; (defvar *current-acl2-world-key* (make-symbol "*CURRENT-ACL2-WORLD-KEY*")) ; But compiling under cmulisp showed us that we refer to the value ; of this var earlier in the initialization process. So I have ; moved the defvar to axioms.lisp. (eval-when #-cltl2 (load eval) #+cltl2 (:load-toplevel :execute) (f-put-global 'current-acl2-world nil *the-live-state*) (setf (get 'current-acl2-world 'acl2-world-pair) (cons nil *current-acl2-world-key*))) ; EXTENDING AND RETRACTING PROPERTY LIST WORLDS ; We here sketch the entire world management scheme before diving into ; the details. The software archeologist might think these summaries ; were written just for his use but that is wrong. In fact, these are ; design sketches and refresher courses to bring to mind the salient ; details before getting back down to work. This particular one ; represents the attempt to get back into this frame of mind after ; several days of Christmas preparations, 1990. (Note: This essay has ; been updated since, to track changes such as the adoption, in April, ; 1994, of the restriction that reincarnated undone defpkgs must ; import only a subset of the old imports. That attack on the ; "unintern problem" was sketched as the "Alternative Design Proposal" ; in the December, 1990 essay but rejected as unnecessary as it was ; then thought that we handled reincarnation correctly by uninterning ; all symbols except in abort recovery. But :oops and the second pass ; of include books, etc., exposed the lie.) ; A property list "world" is a list of triples as created by putprop. ; Each triple is of the form (symb key . val). Such a list is ; accessed by getprop, which, logically speaking, scans down it ; looking for a given symb and key. Practically however, we allow a ; given world to be "installed" under any given symbolic name. What ; installation does is assemble into an alist all of the properties of ; each symb in the world and put that alist on the property list of ; the symbol, under some special key that is associated with the name ; of the installed world. ; If name has an 'acl2-world-pair property then name is the name of an ; installed world. The value of the property will be a pair, (alist . ; world-key), where alist is the (eq) world alist installed and ; world-key is a unique symbol associated with this world name and ; under which each symb's property alist is stored. ; The functions extend-world and retract-world will extend and retract ; a named world. Logically speaking, these two functions are identity ; functions. But practically speaking they smash Common Lisp property ; lists. Extend-world must be given a name and a world that is an ; extension (eq) of the one currently installed under the name and ; will install the new properties. An analogous remark holds for ; retract-world. We make these functions available to the ACL2 ; programmer. ; We store our own property list world under the name 'current-acl2- ; world. How do we prevent the ACL2 programmer from smashing our ; properties? Well, extend-world (which is logically a no-op all the ; time) is even a no-op practically on the name 'current-acl2-world. ; To smash property lists you must call extend-world1 (not an ACL2 ; function) and that function works on any name. Our ACL2 function ; set-w, which installs the current-acl2-world, calls extend-world1 in ; its #-acl2-loop-only code. Set-w is, of course, untouchable. ; We include special support for retraction, which of course is the ; basis of undoing. It would suffice, for extension and for getprop, ; if we could expedite the retrieval of the most recently put value of ; every symbol and key. Suppose the world in question is w, named ; name, and suppose it is installed under the property name world-key. ; Suppose the only three triples on w about symb are (symb key1 . b), ; (symb key1 . a), and (symb key2 . c), occurring in that order on w. ; Then for purposes of extension and getprop alone, we could store ; '((key1 . b) (key2 . c)) under symb's world-key property. But now ; suppose we wanted to retract back to where (symb key1 . a) was most ; recent. Then we would need to change the alist stored under symb's ; world-key to '((key1 . a) (key2 . c)) and to find the newly exposed ; value for key1 we would have to search w. This is what we did for ; the first 18 months of ACL2's development. This made :ubt suffer ; because when we undid a property -- especially a property on some ; symbol like binary-+ or cons -- we would have to scan all the back ; down the world to the primordial putprops to recover the newly ; exposed values. This was bad not so much because of the scan time ; but because of the swap time: the world is big and rarely ; referenced, so it tends to get paged out and then when you scan it ; you have to page it back in. This can take a minute or more. ; To avoid this we actually store a stack for each key. The stack is ; the list of all past values of the key, topped by the current value. ; An empty stack indicates that no putprop has occurred for that key ; (or, more accurately, that we have retracted back past the first ; putprop for that key). ; There is another twist to this scheme. To support the execution and ; compilation of ACL2 functions in raw Common Lisp, we interpret a ; certain putprop symb key, namely CLTL-COMMAND GLOBAL-VALUE, as a ; directive to smash the symbol-function, macro-function, or constant ; definition of certain symbols contained in the value. We only do ; this if we are installing 'current-acl2-world, of course. To ; support undoing of these smashes we maintain a stack of the past ; settings of those fields. This is the *undo-stack* of the symb. ; The situation here is complicated and more fully explained in the ; code below. ; The installation of worlds and error recovery are intimately con- ; nected to the problem of uninterning symbols on behalf of undone or ; reincarnated packages. When the CLTL-COMMAND defpkg is encountered, ; the program defpkg is called to create the package. Consider what ; would happen if defpkg were coded so as to unintern the symbols in ; the existing package and set the import list as per the new defini- ; tion (as, indeed, we once did, allowing the reincarnation of undone ; packages). In particular, consider the effect this would have on ; triples yet-to-be installed: if they mentioned symbols in the new ; package then those symbols would suddenly become uninterned. We ; once thought this was ok because symbols in newly defined packages ; couldn't yet exist in the yet-to-be installed world. But that is a ; bogus claim: if we are reinstalling a world after an error abort or ; even an :oops the world might contain symbols in the "just defined" ; package. This is what eventually drove us to implement the restric- ; tion described in :DOC package-reincarnation-import-restrictions. ; Because of the possiblity of user interrupts, it is possible that we ; can have effected some but not all of changes necessary to achieve a ; new state and then have the computation aborted. To handle this, ; extend-world1 and retract-world1 both save the current world alist ; before they begin to make any changes. If they are interrupted, the ; original configuration can be recovered by retracting back to nil ; and then extending to the saved current world. This is admittedly ; inefficient -- all 20,000 properties of a typical current-acl2-world ; might have to be stored again because we didn't bother to remember ; how much of the extension we had done when we were interrupted. On ; the other hand, it is truly simple and elegant and only comes into ; play with aborts during installation. ; Inspection of the lisp code for defpkg will reveal that it is ; sensitive to abort recovery in one other aspect. If we are in abort ; recovery and the "dual package" (the one used to house the lisp ; equivalents of state global variables) already exists, we do not ; unbind all the variables in it but simply leave it untouched. Since ; neither extending nor retracting changes state globals, the state ; global settings at the time of an abort are what they were when *w0* ; was saved. Hence, by doing nothing to the dual package we keep the ; installed world and the state globals in the same relationship. ; So much for the sketch of the world management business. We now get ; down to brass tacks. (defun-one-output fmakunbound! (name) (fmakunbound name) (when (macro-function name) (error "This Common Lisp implementation seems unable to unbind ~~% macro-functions. Please let the ACL2 implementors know about ~%~ this problem."))) (defun-one-output maybe-push-undo-stack (fn name &optional extra) ; See add-trip below for context. Fn is one of the raw Lisp function names ; secretly spawned by CLTL-COMMAND forms, e.g., DEFUN, DEFMACRO, DEFCONST, ; DEFPKG, DEFATTACH, or (for the HONS version) MEMOIZE or UNMEMOIZE. Name is ; generally the symbol or string that is being defined. ; Whenever we smash a CLTL cell we first save its current contents to permit ; redefinition and undoing. Toward this end we maintain a stack for each ; defined symbol, called the *undo-stack* property of the symbol. Very roughly ; speaking, the stack contains the previous values of the cells in question. ; Add-trip will push the old value onto the stack before storing the new and ; undo-trip will pop the stack and restore that old value. Ah, were it only ; that simple... ; There are complications. First, DEFPKG doesn't have a symbol associated with ; it explicitly, so we have to manufacture one for the *undo-stack*. We use ; the ``base symbol'' of the package (see chk-acceptable-defpkg). If the ; symbol-package-name string is "name" then the base symbol is the symbol ; ACL2::name-PACKAGE. (We use that symbol as a rule name associated with the ; defpkg axiom and so we already check that the name is new.) Second, DEFPKG ; makes the notion of "current contents" highly abstract because it not only ; creates a package but imports various symbols into it. So rather than use ; the *undo-stack* to save the "current contents" we use the stack to save a ; form that when evaluated will recreate the "current contents" of the cell in ; question. When a new value is stored (and the cell is already in use) we ; will manufacture a suitable form for recreating the old value and push it. ; Third, extra (formerly called ignorep because of its connection to the ; ignorep variable in add-trip) is either nil, 'reclassifying or '(defstobj ; . stobj). When it is 'reclassifying, we only save the *1* def for name. ; Otherwise, we save both defs. (cond ((and (symbolp name) (fboundp name) (not (eq fn 'attachment))) ; We clear the 'acl2-trace-saved-fn property and reinstall the appropriate ; symbol-function if these have been messed with by tracing. We also do a raw ; Lisp untrace while we're at it, just to be careful. However, we skip all ; that for defattach, since defattach doesn't mess with symbol-functions -- it ; only messes with special variables. (maybe-untrace! name t))) (case fn ((defun defmacro) ; In Common Lisp, a symbol can be either a macro or function, but the ; symbol-function cell is used in both cases to store the associated code. ; Therefore, if we are about to smash the symbol-function cell, e.g., in ; response to a DEFUN event, then we are obliged to remember whether it was ; previously defined as a macro. ; Notice that we are dealing properly here with :inlined stobj functions as ; well as defabsstobj raw Lisp macros. See also the comment about this in ; undo-trip. (cond ((fboundp name) (let ((oneified-name (*1*-symbol name)) (macro-p (macro-function name))) (push `(progn ,@(and (not macro-p) `((maybe-untrace! ',name) ; untrace new function #+hons (maybe-unmemoize ',name))) ,@(if (eq extra 'reclassifying) (assert$ (not macro-p) `((setf (symbol-function ',oneified-name) ',(symbol-function oneified-name)))) `(,@(if (not (iff (eq fn 'defmacro) macro-p)) ; Avoid errors in (at least) CCL, as in this example. ; (redef!) ; (defun foo (x) x) ; (defmacro foo (x) `(quote ,x)) ; (u) `((fmakunbound! ',name))) ,(cond (macro-p `(setf (macro-function ',name) ',(macro-function name))) (t `(setf (symbol-function ',name) ',(symbol-function name)))) ,(cond ((fboundp oneified-name) `(setf (symbol-function ',oneified-name) ',(symbol-function oneified-name))) (t `(fmakunbound! ',oneified-name)))))) (get name '*undo-stack*)))) (t (push `(progn (maybe-untrace! ',name) ; untrace new function #+hons (maybe-unmemoize ',name) (fmakunbound! ',name) (fmakunbound! ',(*1*-symbol name))) (get name '*undo-stack*))))) (defconst ; Note: defstobj events use maybe-push-undo-stack with fn = 'defconst ; to save the values of the name, the live name and also of ; '*user-stobj-alist*! (cond ((boundp name) (push `(progn (setf (symbol-value ',name) ',(symbol-value name)) (setf (get ',name 'redundant-raw-lisp-discriminator) ',(get name 'redundant-raw-lisp-discriminator))) (get name '*undo-stack*))) (t (push `(progn (makunbound ',name) (remprop ',name 'redundant-raw-lisp-discriminator)) (get name '*undo-stack*))))) (defpkg (let ((temp (find-non-hidden-package-entry name (known-package-alist *the-live-state*)))) (cond (temp (push `(defpkg ,name ',(package-entry-imports temp)) (get (packn (cons name '("-PACKAGE"))) '*undo-stack*)))))) (attachment (let ((at-sym (attachment-symbol name))) (push `(progn #+hons (push ',name *defattach-fns*) ,(set-attachment-symbol-form name ; Note that at-sym is bound when name is introduced; see throw-or-attach-call. (symbol-value at-sym))) (get name '*undo-stack*)))) #+hons (memoize (push `(unmemoize-fn ',name) (get name '*undo-stack*))) #+hons (unmemoize (let* ((entry (gethash name *memoize-info-ht*)) (condition (access memoize-info-ht-entry entry :condition)) (inline (access memoize-info-ht-entry entry :inline)) (trace (access memoize-info-ht-entry entry :trace)) (commutative (access memoize-info-ht-entry entry :commutative)) (forget (access memoize-info-ht-entry entry :forget)) (memo-table-init-size (access memoize-info-ht-entry entry :memo-table-init-size)) (aokp (and (access memoize-info-ht-entry entry :ext-anc-attachments) t)) (cl-defun (access memoize-info-ht-entry entry :cl-defun))) (push `(memoize-fn ',name :condition ',condition :inline ',inline :trace ',trace ,@(and commutative `(:commutative t)) ,@(and forget `(:forget t)) ,@(and memo-table-init-size `(:memo-table-init-size ',memo-table-init-size)) ,@(and aokp `(:aokp ',aokp)) ,@(and cl-defun `(:cl-defun ',cl-defun))) (get name '*undo-stack*)))) (otherwise (er hard 'maybe-push-undo-stack "Unrecognized CLTL-COMMAND spawn ~x0" fn)))) (defun-one-output maybe-pop-undo-stack (name) ; See maybe-push-undo-stack. (let* ((name (if (symbolp name) name (packn (cons name '("-PACKAGE"))))) (stk (get name '*undo-stack*))) (cond ((null stk) nil) (t (eval (car stk)) (setf (get name '*undo-stack*) (cdr stk)))))) (defun-one-output flush-undo-stack (name) ; We completely wipe out the undo-stack of name, after returning ; the relevant cell to its initial configuration. (let* ((name (if (symbolp name) name (intern name "ACL2"))) (stk (get name '*undo-stack*))) (cond (stk (eval (car (last stk))))) (remprop name '*undo-stack*))) ; Now we define the two programs that manage the stacks of old ; property values. ; We start with pushing a new value onto the stack for a given key. ; Complicating things is our decision to order the keys in the alists by (a ; priori) frequency of access. The aim is to speed up getprop. We record ; the results of many experiments below. ; Recall that the current-acl2-world is implemented so that the logical ; properties are stored in an alist which is obtained via a raw lisp get of the ; property *current-acl2-world-key*. That alist is then searched with assoc ; :test #'eq. Of interest then are both the order of the properties ; encountered by the raw lisp get and the order of the keys encountered by the ; assoc :test #'eq. ; The basic experiment addressed one particular proof in the Nqthm package. To ; set the stage, the Nqthm package was loaded and then undone back through ; NQTHM-COUNT-SYMBOL-IS-COUNT-FN-UNPACK, a theorem whose reported proof time is ; 35.23 by the current Version 1.8. Then that theorem was proved again while a ; patch was in place inside of fgetprop. The patch collected together an alist ; recording the calls of fgetprop. In particular the alist entries were of the ; form (symb (key1 . cnt1) ... (keyk . cntk)) indicating that (fgetprop symb ; keyi ) was called cnti times during the ; proof. We then wrote and compiled a program that swept the alist and ; repeated every call of fgetprop simply to allow us to measure the total time ; spent in fgetprop. There were a total of 102781 calls. To sweep the alist ; with a no-op function of the same arity as fgetprop required 0.25 seconds. ; We therefore consider that to be the overhead of the sweep itself. To sweep ; with fgetprop required 0.75 seconds, indicating that a "net" 0.50 seconds ; were actually spent in fgetprop on the actual calls in the sample theorem. ; (We will use "net" henceforth to mean the measured time minus 0.25.) This ; gives an expected "per call" time of 4.86E-6. ; For what it is worth, a noop that calls get has an overhead of 0.267 for ; a net of 0.017 or a per call time of 1.65E-7 seconds. Thus an fgetprop ; is about 30 times slower than a get (with the orderings created by the ; current Version 1.8). ; However, we have noticed that *current-acl2-world-key* is not always the ; first property encountered by the raw lisp get. Other properties sometimes ; covering it up include *UNDO-STACK*, *PREDEFINED* and SYSTEM:PNAME. We ; therefore moved *current-acl2-world-key* to the front of every symbol-plist. ; The net sweep time was then 0.30 (for a per call time of 18 gets). ; We now move on to ordering the keys seen by assoc :test #'eq. In prior ; experiments we had determined the frequency with which the various keys are ; accessed (during the entire Nqthm package proof). For what it is worth, here ; is the key list, in order from most frequently accessed to least: ; '(COARSENINGS GLOBAL-VALUE CONGRUENCES SYMBOL-CLASS TYPE-PRESCRIPTIONS ; LEMMAS RUNIC-MAPPING-PAIRS MULTIPLICITY STATE-IN ; RECURSIVEP DEF-BODIES CONSTRAINEDP LINEAR-LEMMAS ; FORMALS MACRO-BODY FORWARD-CHAINING-RULES STATE-OUT TABLE-ALIST ; GUARD MACRO-ARGS ELIMINATE-DESTRUCTORS-RULE CONST LEVEL-NO ; UNNORMALIZED-BODY THEOREM REDEFINED INDUCTION-MACHINE JUSTIFICATION ; INDUCTION-RULES CONTROLLER-ALIST QUICK-BLOCK-INFO ; We therefore reordered the alist so that the keys were stored with the most ; frequently accessed ones first. We added nil COARSENINGS and CONGRUENCES ; properties (and later, as described below, RECURSIVEP) to those function ; symbol property lists for which the value of the property was nil but the ; property was unrecorded. (This saves the time of cdring through the entire ; list to compute the most frequently seen two properties.) Technically, we ; extended and reordered the alist found in (get symb ; *current-acl2-world-key*), for each symbol with a *current-acl2-world- key* ; property and that property was always first on the symbol-plist. ; We then repeated the sweep in a net time of 0.22 seconds (per call = 13 gets). ; We then reversed the "optimal" ordering on the property lists and measured a ; net time of 0.31 (down from 0.30 from the random order of Version 1.8). ; Finally, we perturbed the property lists by adding 10 new property keys and ; values to the front of every (get symb *current-acl2-world-key*) and measured ; a net time of 0.50. ; From this experiment one can make the following conclusions: (a) In this ; theorem, fgetprop is reponsible for less than 2% of the proof time. Making ; fgetprop instantaneous would reduce the 35.23 seconds to 34.73 seconds. ; By ordering the properties (in both senses) we can speed fgetprop up from ; about 30 gets to about 13 gets, more than doubling its speed. ; The rest of this essay on experimental results discusses some detailed ; investigations that led to virtually no further improvement (see stats at the ; end of the essay). The lesson learned is that it may not be worth mucking ; around further with *current-acl2-world-key-ordering*. ; In July 2002, during the development of Version_2.7, we modifed the use of ; the fnstack (specifically, being-openedp) so that for recursive functions we ; look up the representative of a clique, thus avoiding the need to look ; through all members every clique for the function at hand. (A ; mutual-recursion nest with 4,786 defuns at AMD prompted this change.) As a ; result we saw a 1.8% slowdown in the regression suite, reduced to 0.9% with ; some optimizations. Presumably the slowdown was due to the more frequest use ; of the RECURSIVEP property. So we ran experiments using community books ; files books/certify-numbers.lisp and books/rtl/rel2/support/cert.lsp, though ; we aborted the latter partway through lop3.lisp (during the proof of ; BITN-LAM0, which seemed to be bogging down). The results using ; analyze-fgetprop-stats were as follows. ; books/certify-numbers.lisp: ; ; GLOBAL-VALUE 2474980 ; COARSENINGS 2332094 ; TYPE-PRESCRIPTIONS 1162730 ; RUNIC-MAPPING-PAIRS 979110 ; CONGRUENCES 769460 ; RECURSIVEP 676128 ; TABLE-ALIST 675429 ; SYMBOL-CLASS 415118 ; LEMMAS 381015 ; MACRO-BODY 356823 ; STOBJS-OUT 303906 ; FORMALS 213447 ; STOBJS-IN 161261 ; STOBJ 101845 ; GUARD 75749 ; MACRO-ARGS 75221 ; BODY ; changed later to def-bodies 68867 ; CONSTRAINEDP 50190 ; FORWARD-CHAINING-RULES 49839 ; CONST 25601 ; ELIMINATE-DESTRUCTORS-RULE 19922 ; THEOREM 9234 ; LINEAR-LEMMAS 9102 ; ... ; ; books/rtl/rel2/support/cert.lsp (aborted as explained above): ; ; COARSENINGS 30087445 ; GLOBAL-VALUE 28366962 ; CONGRUENCES 27187188 ; RUNIC-MAPPING-PAIRS 13934370 ; TYPE-PRESCRIPTIONS 12058446 ; RECURSIVEP 10080678 ; TABLE-ALIST 4644946 ; SYMBOL-CLASS 2742519 ; LEMMAS 1978039 ; STOBJS-OUT 1943646 ; MACRO-BODY 1837674 ; FORMALS 1185024 ; STOBJS-IN 781274 ; BODY ; changed later to def-bodies 585696 ; STOBJ 509394 ; GUARD 390584 ; MACRO-ARGS 389694 ; CONSTRAINEDP 332418 ; FORWARD-CHAINING-RULES 211225 ; CONST 145628 ; ABSOLUTE-EVENT-NUMBER 93259 ; LINEAR-LEMMAS 34780 ; ... ; As a result, we revised the ordering of keys. We also noticed that although ; GLOBAL-VALUE is high on the list, most of that is accounted for by looking it ; up for symbols RECOGNIZER-ALIST and UNTOUCHABLES, which do not have other ; properties: ; books/certify-numbers.lisp: ; ; RECOGNIZER-ALIST 2056058 ; GLOBAL-VALUE 2056058 ; UNTOUCHABLES 261297 ; GLOBAL-VALUE 261297 ; ; books/rtl/rel2/support/cert.lsp (aborted as explained above): ; ; RECOGNIZER-ALIST 26193957 ; GLOBAL-VALUE 26193957 ; UNTOUCHABLES 1359647 ; GLOBAL-VALUE 1359647 ; The user times (in seconds) for running the regression suite using an Allegro ; 6.0 Linux development Version_2.7 were as follows, with successive ; "improvements" shown. ; 15359.38 ; original time ; 15637.45 ; 1.81% slowdown: first cut at new approach to fnstack for mutrec ; 15496.32 ; 0.89% slowdown: optimizations in being-openedp (made a macro) ; 15497.46 ; 0.90% slowdown: new *current-acl2-world-key-ordering* ; 15481.14 ; 0.79% slowdown: always put recursivep property on function symbols ; March 2006: Here are some new numbers, listing in each case down to about 2 ; orders of magnitude below the most-used property. All were obtained with all ; outpu inhibited. ; ============================================================ ; ; stats0 (books/certify-numbers.lisp): ; ; COARSENINGS 2527582 ; GLOBAL-VALUE 2224181 ; RUNIC-MAPPING-PAIRS 1188675 ; TYPE-PRESCRIPTIONS 1074218 ; CONGRUENCES 730666 ; DEF-BODIES 685868 ; TABLE-ALIST 642459 ; SYMBOL-CLASS 400157 ; LEMMAS 362209 ; ; ============================================================ ; ; stats1 (books/workshops/1999/compiler/proof1): ; ; COARSENINGS 1137397 ; DEF-BODIES 705063 ; GLOBAL-VALUE 587267 ; TABLE-ALIST 360303 ; TYPE-PRESCRIPTIONS 196192 ; CONGRUENCES 194726 ; SYMBOL-CLASS 177363 ; LEMMAS 167682 ; RUNIC-MAPPING-PAIRS 75828 ; STOBJS-OUT 13381 ; MACRO-BODY 10245 ; ; ============================================================ ; ; stats2 (:mini-proveall): ; ; COARSENINGS 87020 ; GLOBAL-VALUE 58987 ; RUNIC-MAPPING-PAIRS 54106 ; TABLE-ALIST 32902 ; DEF-BODIES 26496 ; TYPE-PRESCRIPTIONS 24822 ; CONGRUENCES 20367 ; LEMMAS 17938 ; SYMBOL-CLASS 15271 ; FORWARD-CHAINING-RULES 4820 ; FORMALS 1278 ; MACRO-BODY 1216 ; STOBJS-OUT 1199 ; ELIMINATE-DESTRUCTORS-RULE 962 ; ; ============================================================ ; ; stats3 (osets/map): ; ; DEF-BODIES 288073 ; RUNIC-MAPPING-PAIRS 262004 ; COARSENINGS 235573 ; GLOBAL-VALUE 171724 ; FORMALS 84780 ; TABLE-ALIST 76462 ; UNNORMALIZED-BODY 61718 ; TYPE-PRESCRIPTIONS 56193 ; LEMMAS 54533 ; CONSTRAINEDP 52642 ; SYMBOL-CLASS 43824 ; CONGRUENCES 36786 ; MACRO-BODY 30206 ; STOBJS-OUT 27727 ; THEOREM 15714 ; ; ============================================================ ; ; stats4 (rtl/rel5/support/drnd): ; ; COARSENINGS 20881212 ; GLOBAL-VALUE 10230404 ; RUNIC-MAPPING-PAIRS 7726914 ; TYPE-PRESCRIPTIONS 4177523 ; DEF-BODIES 2732746 ; SYMBOL-CLASS 705776 ; STOBJS-OUT 671763 ; TABLE-ALIST 664941 ; CONGRUENCES 497120 ; LEMMAS 376371 ; MACRO-BODY 294016 ; ; ============================================================ ; ; stats5 (rtl/rel2/support/cert.lsp): ; ; COARSENINGS 21792912 ; GLOBAL-VALUE 15497700 ; RUNIC-MAPPING-PAIRS 8088313 ; TYPE-PRESCRIPTIONS 6554966 ; DEF-BODIES 5365470 ; TABLE-ALIST 2641304 ; SYMBOL-CLASS 1873984 ; CONGRUENCES 1562924 ; LEMMAS 1220873 ; STOBJS-OUT 420330 ; MACRO-BODY 364583 ; FORMALS 248019 ; FORWARD-CHAINING-RULES 245442 ; ; ============================================================ ; End of Experimental Results. ; Below we list the most important property keys according to the results ; above. Keys are stored in alists in this order, i.e., keys occurring earlier ; in this list are stored earlier in the alists. When a key not occurring in ; this list is added to the alist it is as though it occurred at the very end ; of this list, i.e., it gets a low priority. Not all keys used by the current ; system are in this list (see below). (defparameter *current-acl2-world-key-ordering* '(COARSENINGS GLOBAL-VALUE ; mostly looked up for RECOGNIZER-ALIST and UNTOUCHABLES, ; which do not have other properties RUNIC-MAPPING-PAIRS DEF-BODIES TYPE-PRESCRIPTIONS TABLE-ALIST CONGRUENCES SYMBOL-CLASS LEMMAS STOBJS-OUT MACRO-BODY FORMALS FORWARD-CHAINING-RULES ; Note: As of this writing there are many properties not included above, all of ; which fall into the low priority category. We have omitted keys simply to ; keep the list shortened and thus to speed up the insertion program ; (merge-into-alist, on behalf of destructive-push-assoc) a little. This is an ; unanalyzed "optimization". )) (defun-one-output key-lesseqp (key1 key2 ordering) ; We return t if key1 occurs weakly before key2 in the ordering. (cond ((null ordering) t) ((eq key1 (car ordering)) t) ((eq key2 (car ordering)) nil) (t (key-lesseqp key1 key2 (cdr ordering))))) (defun-one-output merge-into-alist (key val alist) ; Alist is a symbol alist, key is a symbol that is not bound in alist. We wish ; to create the alist that is logically equivalent under assoc-eq to (cons ; (cons key val) alist) but we actually place the new pair in the proper place ; according to the *current-acl2-world-key-ordering*. (cond ((null alist) (list (cons key val))) ((key-lesseqp key (caar alist) *current-acl2-world-key-ordering*) (cons (cons key val) alist)) (t (cons (car alist) (merge-into-alist key val (cdr alist)))))) (defun-one-output destructive-push-assoc (key value alist world-key) ; We push value onto the stack associated with key in alist. If key has no ; value in alist, we pretend it has the empty stack. E.g., if alist is '((a . ; (1))) and we push 2 on 'a we get '((a . (2 1))) and if we then push 0 on 'b ; we get '((b . (0)) (a . (2 1))). This function is maximally destructive on ; the cons structure of alist and the stacks, but doesn't touch the cons ; structure of the values. We keep the alists in sorted order iff the ; world-key is our special one, *current-acl2-world-key*. (let ((temp (assoc key alist :test #'eq))) (cond (temp (setf (cdr temp) (cons value (cdr temp))) alist) ((eq world-key *current-acl2-world-key*) (merge-into-alist key (list value) alist)) (t (cons (cons key (list value)) alist))))) (defun-one-output destructive-pop-assoc (key alist) (let ((temp (assoc key alist :test #'eq))) (cond (temp (setf (cdr temp) (cdr (cdr temp))) alist) (t alist)))) (defun-one-output remove-current-acl2-world-key (plist) (cond ((null plist) plist) ((eq (car plist) *current-acl2-world-key*) (cddr plist)) (t (cons (car plist) (cons (cadr plist) (remove-current-acl2-world-key (cddr plist))))))) ; We now develop support for early loading of compiled files, beginning with an ; essay that outlines that development. ; Essay on Hash Table Support for Compilation ; This essay has the following main parts: ; Part 0: High-level summary ; Part 1: A more detailed introduction ; Part 2: Including a certified book ; Part 3: Writing an expansion file for compilation ; Part 0: High-level summary ; We strive for efficiency of include-book. By doing all compilation at ; certify-book time rather than include-book time, we may greatly speed up ; definitional processing in lisps such as CCL and SBCL, which compile every ; definition on the fly. We were motivated by profiling results showing that ; such processing can take 45% of include-book time: a test case from Centaur ; using CCL was spending this proportion of time in the installation of a ; Common Lisp symbol-function for each defun event, in add-trip. The problem ; was that the CCL compiler is called every time a defun is evaluated, and ; although the CCL compiler is impressively fast, it's not instantaneous. Dave ; Greve has reported observing significant such slowdowns using CCL at Rockwell ; Collins. ; Happily, with this change we found the time cut roughly in half for two ; include-book tests from Centaur provided by Sol Swords. Other tests suggest ; no noticeable slowdown for certify-book or include-book for GCL or Allegro ; CL, which do not compile on the fly. ; Our approach is to avoid calling the compiler (by CCL or SBCL, at least) ; every time a definition is encountered by include-book, by instead using ; existing code previously compiled by certify-book, which is loaded before ; processing of events by include-book. Thus, the main efficiency gains from ; this change are expected to be for ACL2 built on CCL or SBCL, as these are ; the Lisps we know of (as of March 2010) that compile all definitions at ; submission time and therefore had been compiling on behalf of add-trip. ; However, this approach may also boost efficiency in some cases even for Lisps ; other than CCL and SBCL. For one thing, include-book will now install a ; compiled symbol-function for each defun, even for those other Lisps, which ; can speed up computations in ensuing defconst forms and defmacro forms of the ; book. Moreover, compiled code will be installed for defmacro and defconst ; forms, which can avoid the need for redoing macroexpansion of the bodies of ; such forms during add-trip. ; A simple-minded approach is to load the compiled file for a book *before* ; processing events in the book. The obvious problem is that ACL2 demands that ; a function not be pre-defined in raw Lisp when evaluating a defun event, and ; for good reason: we want to protect against accidental previous definition in ; raw Lisp. So instead, our solution is to arrange that loading compiled files ; does not actually install definitions, but rather, builds hash tables that ; associate symbols with their definitions. The file to be compiled thus has ; roughly the following structure; the prefix "hcomp" is intended to refer to ; "hash-table-supported compilation". ; (in-package "ACL2") ;;; Introduce some packages, without any imports: ; (maybe-introduce-empty-pkg "MY-PKG") ;;; Save some information about the fni: ; (setq *hcomp-fn-alist* '((fn1 ..) (fn2 ..) ..)) ;;; Build a hash table associating each fni with its pre-existing ;;; symbol-function or special *unbound* mark: ; (hcomp-init) ;;; Generate declaim forms (depending on the Lisp): ; ... ;;; Portcullis commands and events from the book, with make-events expanded: ; ... ;;; *1* definitions to compile: ; ... ; The load of each book in raw Lisp (by function load-compiled-book) is ; followed by code that saves the symbol-function for each fni in a hash table, ; *hcomp-fn-ht* (function hcomp-transfer-to-hash-tables), which in turn is ; associated with the full-book-name in a global hash table, *hcomp-book-ht*. ; But first, the (hcomp-init) form arranges to save -- in a global hash table, ; *hcomp-fn-macro-restore-ht* -- an association of each fi with its existing ; symbol-function (or a "not bound" marker). After all such files are loaded ; in raw Lisp under the top-level included book (by a call of include-book-raw ; under include-book-fn), the relevant *hcomp-fn-ht* hash tables will have been ; populated and saved in the global hash table mentioned above, ; *hcomp-book-ht*, keyed on full-book-names. The top-level include-book will ; finish up after such files are loaded (for that book and subsidiary included ; books), using the global hash table *hcomp-fn-macro-restore-ht* to restore ; the symbol-function of fi (much more typically, to make the symbol-function ; of fi unbound) when the top-level load concludes. ; Above, we say "roughly" because there are numerous complications. For ; example, *1* functions can be defined twice (once for :program mode and once ; for :logic mode); there may be portcullis commands for subsidiary ; include-book events within the book; and the absence of a missing compiled ; file for a sub-book can cause an abort, so some of the above finishing up ; might need to be done in the cleanup form of an acl2-unwind-protect. In this ; Essay we outline our mechanism and explain how we deal with such ; complications. ; We are breaking from ACL2 versions up through 3.6.1, by insisting that the ; compiled file for a book is loaded "early" (if it is loaded at all), i.e., ; before events are processed from that book. This approach not only can boost ; efficiency of include-book, but it also provides a solution to a soundness ; bug in the redundancy of :program mode definitions with preceding :logic mode ; definitions, present from Version_3.5 through Version_3.6.1. To illustrate ; this bug, consider the two books below, which have been certified in ACL2 ; 3.6.1 built on GCL. The problem is that inclusion of bk1 inside bk2 smashes ; the symbol-function of *1*foo, because of loading of compiled file bk1.o. ; (The Allegro CL version merely breaks when attempting to prove BUG.) ; -------------------- bk1.lisp -------------------- ; (in-package "ACL2") ; (defun foo (x) ; (declare (xargs :mode :program)) ; (car x)) ; -------------------- bk2.lisp -------------------- ; (in-package "ACL2") ; (defun foo (x) ; (car x)) ; (defun bar (x) ; (foo x)) ; (defthm fact ; (null (bar 3)) ; :rule-classes nil) ; (encapsulate ; () ; (local (include-book "bk1")) ; (defthm bug ; (not (null (bar 3))) ; :rule-classes nil)) ; (defthm contradiction ; nil ; :hints (("Goal" :use (fact bug) ; :in-theory (disable (bar) bar))) ; :rule-classes nil) ; ---------------------------------------- ; The bug occurs because the local include-book of "bk1" loads bk1.o, which ; smashes the symbol-function of *1*foo to its :program mode version, which ; unlike the :logic mode version passes evaluation directly to raw Lisp, ; causing evaluation of (car 3). Of course we don't really need to solve this ; problem for CCL-based ACL2 images that do not load compiled files. But that ; seems ugly, as one could then certify a book with CCL that cannot be ; certified with another Lisp. ; Another, less serious problem is also solved by early loading of compiled ; files. Consider the following books. ;;; bar.lisp ; (in-package "ACL2") ; (defun foo (x) ; (declare (xargs :guard t)) ; (cons x x)) ;;; foo.lisp ; (in-package "ACL2") ; (defun foo (x) ; (cons x x)) ;;; top.lisp ; (in-package "ACL2") ; (include-book "bar") ; (include-book "foo") ; The *1* function generated for foo in bar.lisp is considerably simpler than ; in the case of foo.lisp, because there need be no check that the symbol-class ; of foo is :common-lisp-compliant. When we include top in Version_3.6.1 or ; earlier, loading compiled files, the compiled file for foo overwrites the one ; for bar, leaving us with the more complicated *1* code. This is clear if one ; uses CLISP and evaluates (disassemble (*1*-symbol 'foo)) in raw Lisp: 13 ; byte-code instructions if one includes foo or top, but only 3 byte-code ; instructions if one includes bar instead. With early loading of compiled ; files, evaluation of (include-book "top") will define this *1* function when ; including bar, but will not define it again when including foo. ; (The above example isn't convincing of much, really, since if we switch the ; order of the include-book forms in top.lisp then we will get the complicated ; *1* compiled definition of foo, because the definition from bar.lisp will be ; redundant. But it still seems preferable to avoid loading compiled files ; that overwrite definitions needlessly, for example to put less stress on the ; garbage collector.) ; Of course, these issues disappear if the compiled file is not loaded at all, ; and we support that too, using state global 'compiler-enabled. ; We conclude this Part with a few words about handling of the case that ; include-book argument :load-compiled-file has argument :comp. The basic idea ; is to wait until the book is included, and then check that either the ; compiled file or the expansion file exists and is not older than the ; certificate; and only then, if the expansion file exists but the compiled ; file does not, do we compile the expansion file and then load it in the ; ordinary way (without messing with hash tables, by leaving the relevant ; variables such as *hcomp-fn-ht* bound to nil). We considered more complex ; approaches but are quite happy with this simple solution, and we don't say ; anything further about the case of :load-compiled-file :comp in this Essay. ; Part 1: A more detailed introduction ; We now give a more detailed global view of our approach based on hash tables. ; Note that since compilation is inherently a raw-Lisp activity, we code ; shamelessly in raw Lisp when that is convenient. ; The idea is for include-book to load an existing compiled file before ; processing events from the book, even before its portcullis commands are ; processed. The compiled definitions are stored in hash tables for subsequent ; use by add-trip, then immediately undone so that existing definitions are ; restored (or, much more often, symbols are again unbound). We must however ; be careful to use these hash tables only when appropriate: in particular, ; verify-termination changes *1* definitions, so there can be two definitions ; generated for the same symbol -- and loading the compiled file provides the ; latter definition, which is inappropriate to use for the first defun but is ; appropriate for the defun generated by the verify-termination. ; (Aside: We might consider using the latter symbol-function for both the ; :program and :logic mode *1* functions. But it's easy to imagine that the ; :logic code asks about the symbol-class of the function symbol under the ; assumption that it's definitely not :program -- and that assumption would be ; violated if we installed that code when the :program mode version is ; introduced. Whether or not that problem actually exists, or at least is easy ; to fix, this example nevertheless illustrates that evaluation in ACL2 is ; complex and delicate. So we prefer to be conservative and not to install a ; :logic mode *1* function definition for a :program mode function.) ; The introduction of make-event in 2006 initiated the writing of what we call ; below an "expansion file", to be compiled instead of the source book, ; creating what we call below the "compiled file". This feature was further ; exploited by incorporating compiled *1* functions into the compiled file ; (Version_3.2.1). We take further advantage of these expansion files by ; putting forms into them to implement the plan outlined above. Note that we ; handle certain events that create 'cltl-command properties, as processed by ; add-trip: defun, defstobj, defabsstobj, defconst, and defmacro, but not ; memoize and unmemoize, even in the #+hons case. Extra forms near the top of ; the expansion file will be evaluated when loading the compiled file, to store ; values in hash tables for later use, when add-trip deals with 'cltl-command ; properties. Those extra forms are based on information deduced during the ; include-book phase of book certification, at which time Lisp global ; *inside-include-book-fn* has value 'hcomp-build. Later, during subsequent ; include-books, that information directs which definitions from the expansion ; file are to be stored in our hash tables. Additional forms are evaluated ; after completion of the load of the compiled file, to transfer the compiled ; definitions to hash tables and eventually to remove each definition installed ; by the expansion file (restoring any pre-existing definitions). This ; eventual removal occurs only after a load completes for the top-level ; compiled file of a book, and hence also for all books included therein. ; Portcullis commands and included sub-books present challenges. Consider for ; example a constant whose value is a symbol in a package defined in a ; sub-book's portcullis commands. If we load the compiled file for the book ; that defines that constant, but treat the include-book of the sub-book as a ; no-op (as was done through Version_3.6.1), then it doesn't seem clear that ; this constant's value would be interned in any package, since its package is ; defined in the portcullis commands of the not-yet-loaded sub-book. Indeed, ; we need to consider loading not only the sub-book but also its portcullis ; commands. At the very least, we want to avoid warnings that could occur when ; encountering a global or function call in the parent book (say, during macro ; expansion) when the definition of that global or function (by defconst or ; defun, respectively) comes from the unloaded sub-book. And certainly we do ; need a sub-book's portcullis commands when loading it, for example in case ; one of those commands defines a function that is used in a defconst form in ; the book. ; We thus write portcullis commands into the expansion file. But with some ; reflection one discovers that a book's initial in-package form itself could ; be problematic, since if the package in question is not the "ACL2" package ; then it needs to be defined in some book's portcullis commands! So we always ; start an expansion file with (in-package "ACL2"), and when we write the forms ; into the expansion file, we always do so relative to the ACL2 package. ; But our problems with packages don't end there! The setq forms defining ; *hcomp-fn-alist* and the like may involve symbols defined in packages ; introduced in the portcullis commands. (Why use setq instead of ; defparameter? Variables such as *hcomp-fn-alist* are already declared ; special using defvar in the ACL2 sources, so setq is certainly legal. We ; found a case with CCL in which the use of defparameter slowed down ; include-book by a factor of more than 100.) But we want to lay these down ; before a call of hcomp-init, which will consult *hcomp-fn-alist* and such ; when storing information to let us undo definitions installed by loading the ; compiled file. This call of hcomp-init, and its preceding definitions of ; *hcomp-fn-alist* and the like, must therefore be laid down before the very ; portcullis commands that may define packages of symbols used in these ; definitions. Our solution is to use defpackage to introduce packages before ; the symbols are read, and to make a note using special variable ; *defpkg-virgins* that such packages are legitimate targets for the defpkg ; forms to come. ; (Aside: Why does it work to start the expansion file with the introduction of ; an empty package, say "MY-PKG", and then lay down forms like the ; *hcomp-fn-alist* form, above, that may refer to symbols written out at the ; end of book certification? The only symbols where one might imagine this is ; an issue are ones that are printed differently when "MY-PKG" is fully defined ; (near the end of certification) than when it is introduced with no imports by ; an initial form that introduces the package as "empty" (no imports). The ; only such symbols are those written without a package prefix, hence included ; in the "ACL2" package, that are in the import list for "MY-PKG". But such ; symbols aren't a problem after all, because any reference to such a symbol in ; the "ACL2" package is really a reference to a symbol of that name in the ; "MY-PKG" package, once that package is "truly" introduced by defpkg. And ; until such a defpkg form is evaluated, ACL2 will not dabble in symbols in the ; "MY-PKG" package, other than to save them in *hcomp-fn-alist* and related ; lists near the top of the expansion file.) ; Note that in a break from ACL2 versions up through 3.6.1, where ACL2 could ; load compiled files for uncertified books, the write-date comparison of the ; compiled file (or expansion file) is against the certificate rather than the ; source .lisp file. (Well, that's not quite true: the comparison remains ; against the source book when include-book is executed in raw mode, since raw ; mode does not involve the certificate file.) ; We designate three "add-trip contexts", according to whether processing of a ; 'cltl-command property by add-trip is assigning a function, a global variable ; (e.g. for defconst), or a macro value. We refer to the symbol being assigned ; a value as an "add-trip symbol", and we call that value a "relevant value" ; (with respect to that context, which is often implicit) for that symbol. ; Whenever we refer to the add-trip symbols of a book, that reference includes ; add-trip symbols for the book's portcullis commands as well, but not add-trip ; symbols of subsidiary included books. Note by the way that a *1* function ; symbol can be an add-trip symbol. The final step after loading a top-level ; compiled file will be to undo the load's assignment of relevant values to ; add-trip symbols. This step will be done in the cleanup form of an ; unwind-protect, so as to clean up if an error or interrupt occurs during ; loading of the compiled file. (The clean-up won't be complete for functions ; defined in raw-mode, just as it hasn't been in earlier versions of ACL2 that ; did not load compiled files early. But raw-mode is ultimately the user's ; responsibility, and we expect problems from such aborts to be rare.) ; We next describe several variables and a constant, which we define before ; include-book-fn. ; Variables *hcomp-fn-ht*, *hcomp-const-ht*, and *hcomp-macro-ht* are ; intended to be let-bound to eq hash tables: one for ACL2 user functions and ; their *1* symbols, one for constants (as with defconst), and one for ; macros. ; Variables *hcomp-fn-alist*, *hcomp-const-alist*, and *hcomp-macro-alist* ; will be be let-bound to alists related to the above hash tables, in senses ; described below. ; The variable *hcomp-fake-value* is used as a "fake value", not in any ; package known in the ACL2 loop, for various purposes below. ; Variables *hcomp-fn-macro-restore-ht* and *hcomp-macro-restore-ht* are ; globally bound to hash tables that are populated as books are included, ; storing existing relevant values (or *hcomp-fake-value* when the relevant ; value is unbound) for add-trip symbols. ; A hash table variable, *hcomp-book-ht*, holds other hash tables, as ; follows. ; A key of *hcomp-book-ht* is a full-book-name. Values in this hash table are ; hcomp-book-ht-entry records, where each record has a status field that ; describes the attempt to load the book's compiled file, and also has optional ; fields corresponding to values of *hcomp-fn-ht*, *hcomp-const-ht*, and ; *hcomp-macro-ht*. When ACL2 encounters an include-book form during an early ; raw-Lisp load of an include-book whose full-book-name is not already a key of ; the world's 'include-book-alist or of *hcomp-book-ht*, then include-book ; loads that sub-book's compiled file, hence with new let-bindings of the ; *hcomp-xxx-alist* and *hcomp-xxx-ht* variables, along with unwind protection ; using the *hcomp-xxx-restore-ht* values that can restore relevant values ; after transferring them to those hash tables. Upon exiting include-book ; successfully, the *hcomp-xxx-ht* variables are associated with the ; full-book-name in *hcomp-book-ht*. ; (Note: One might think that the hash tables one gets by loading the compiled ; file could vary with context, which makes it unreasonable to compute them for ; a sub-book before processing events in the main book. But as long as the ; book and all books under it remain certified and unchanged, we expect that ; all relevant values depend essentially only on the closure under ancestors of ; the events in the sub-book.) ; We next consider the question of whether it really buys us anything to save ; compiled definitions for defconst and defmacro forms. The answer is (or can ; be) yes, because macros may have been expanded away. (See the discussion of ; "minimal compilation" in the Common Lisp Hyperspec: it is defined in ; http://www.lispworks.com/documentation/HyperSpec/Body/03_bbb.htm, and it is ; specified for file compilation in #6 of ; http://www.lispworks.com/documentation/HyperSpec/Body/03_bca.htm.) One ; experiment that drives this point home (we have tried GCL and CCL) is the ; following. Consider the following files, and see the comments in the ; commands below them. ; .................... tmp.lsp .................... ; (in-package "ACL2") ; (defun foo (n) ; (cond ((zp n) 1) ; (t (loop for i from 0 to (1- n) ; when (equal (foo i) 2) ; do (return i)) ; 1))) ; (defmacro mac () (foo 27)) ; .................... tmp2.lsp ................... ; (in-package "ACL2") ; (load "tmp") ; load compiled file ; (defmacro mac2 () (mac)) ; .................... tmp3.lsp ................... ; (in-package "ACL2") ; (load "tmp") ; load compiled file ; (defconst *c* (mac)) ; ................................................. ; Now do the following (with both CCL-based and GCL-based ACL2 images): ; ; :q ; (compile-file "tmp.lsp") ; fast ; (quit) ; ; :q ; (load "tmp2.lsp") ; slow definition of mac2 ; (compile-file "tmp2.lsp") ; slow ; (load "tmp2") ; fast definition of mac2 from compiled file ; (quit) ; ; :q ; (load "tmp2") ; fast definition of mac2 from compiled file ; (quit) ; ; :q ; (load "tmp3.lsp") ; slow definition of *c* ; (compile-file "tmp3.lsp") ; fast ; (load "tmp3") ; fast ; (quit) ; ; :q ; (load "tmp3") ; slow(GCL)/fast(CCL) def of *c* from compiled file ; (load "tmp3") ; faster (some kind of memoization?) ; (quit) ; We conclude this Part with a discussion of some tricky issues for the case ; that an expansion or compiled file is loaded by include-book, i.e., the case ; that a book is being included with a non-nil effective value of ; :load-compiled-file, where by "effective value" we mean the value after ; accounting for state global 'compiler-enabled. ; A stobj may be defined during evaluation of the raw Lisp definition of ; include-book. In that case, the-live-name for that stobj is an add-trip ; symbol, and hence its value is stored in *hcomp-const-ht*. However, the raw ; Lisp definition of defstobj or defabsstobj also assigns to ; *user-stobj-alist*, which we expect will associate the-live-name of a stobj ; with its Lisp relevant value. Now imagine subsequent processing of events by ; the same include-book. When defstobj or defabsstobj is encountered, add-trip ; obtains the value of the-live-name of that stobj from *hcomp-const-ht*, and ; uses that value to update *user-stobj-alist* just as it would if it were ; updating without benefit of *hcomp-const-ht*. The only tricky bit here is ; that we need to ensure that add-trip, along with undo-trip and flush-trip, ; are the only functions that update *user-stobj-alist*. Therefore, we bind ; *user-stobj-alist* to itself when doing an early load of the compiled file or ; expansion file; see include-book-raw. ; If the compiled file or certificate is missing, or else if the compiled file ; is older than the certificate, we may print a warning and go on, assigning ; 'incomplete status to that book in *hcomp-book-ht* -- but there are a couple ; of exceptions. If :load-compiled-file is t for the current book or any ; parent include-book in progress (as recorded by special variable ; *load-compiled-stack*), then we cause an error. If :load-compiled-file is ; not t, then we are content with loading the expansion file in place of the ; compiled file, provided the expansion file is not older than the certificate; ; see load-compiled-book. In that case we obtain interpreted code when ; add-trip reads a hash table for the value to use, for lisps that do not ; compile on-the-fly; but in that case we are really no worse off than if we ; were computing and evaluating the corresponding definition during event ; processing. ; If however the compiled file is up-to-date with respect to the certificate, ; then we may reasonably assume that the compiled file was valid at one time, ; even if the book is now uncertified. (We could gain some confidence that the ; book is certified by insisting that the certificate is not older than the ; book. But some ACL2 users like to update the comments in a book without ; invalidating the certificate.) We load the compiled file with a suitable ; unwind-protect, restoring relevant values after the load completes (whether ; aborted or not). If it turns out that the book is uncertified, say because ; its certificate is out-of-date or is for the wrong ACL2 version, we will ; simply avoid using the hash tables computed when loading its compiled file, ; since we don't trust the relevant values stored in those tables. ; What happens if a compiled file is missing for a sub-book (and, if ; :load-compiled-file is not t, the expansion file is also missing)? Because ; of the possibility of missing packages, at the least, we want to avoid forms ; from the parent book's compiled file (or expansion file -- whichever we are ; loading) that are below the include-book of that sub-book. So, we abort with ; a throw and a warning, associating the partially-constructed hash tables with ; the parent full-book-name in *hcomp-book-ht*, and leaving it to the cleanup ; form of the surrounding unwind-protect to restore symbol-functions in ; existence before the load of the parent book's compiled file. Of course, if ; the parent book is itself a sub-book of a book being included, then its ; parent's load is in turn similarly aborted, and so on up the chain. Note ; that loading the source file in raw Lisp is not an option in general, because ; all of the *hcomp-xxx-alist* setq forms and (hcomp-init) form, not to mention ; that make-event forms in source files are illegal (with the rare exception of ; a consp :check-expansion argument). Again, the expansion file is a good ; candidate for loading if the compiled file is missing, and this is done in ; order to avoid the aborts described above provided we are not under an ; include-book with :load-compiled-file = t. ; Given a top-level include-book event, it may be helpful to visualize all ; included books and sub-books by arranging them into a tree growing downward, ; with the top-level included book as the root at the top, its sub-books as its ; children from left to right in order of their include-book forms in the ; top-level book, and similarly for sub-books of those sub-books, and ; recursively on downward. ACL2 will include books with a depth-first ; left-to-right traversal of this tree. First suppose that the top-level ; include-book form's :load-compile-file argument has an effective value (i.e., ; accounting for 'compiler-enabled and defaults) other than nil. At the first ; failure to load a compiled file (either because it is missing or it is out of ; date with respect to the .cert file), all parent books up the tree are ; considered to have incomplete loads of their compiled files. However, if ; none of these superior books has a :load-compile-file argument with effective ; value of t, then the partially-populated hash tables are considered ; sufficient and the process continues. To see how warnings and errors are ; handled, suppose we have: book1 includes book2 includes book3 includes book4; ; the compiled file for book4 is missing; and the effective value of ; :load-compile-file for all of these is non-nil. Then we will see a "Compiled ; file" warning unless either book4 or one of its ancestor books (book1, book2, ; book3) has a :load-compile-file argument of t, in which case an error will ; occur. ; We close this introduction with a partial disclaimer. The Common Lisp ; Hyperspec does not seem to specify fully which side effects may be caused by ; DEFUN. Thus, although our approach will install symbol-functions, there ; seems to be no guarantee that it will allow other side-effects caused by ; DEFUN. No such side-effect is critical for ACL2. Nevetheless, it is ; fortunate that some such side-effects may still be handled, as illustrated by ; the following experiment in CCL. First, load a compiled definition of ; function foo from a compiled file, save the symbol-function of foo in ; variable xxx, evaluate (fmakunbound 'foo), and then evaluate (setf ; (symbol-function 'foo) xxx). After all this: if the defun of foo has a Lisp ; documentation string, then (documentation 'foo 'function) will still return ; that string; and if moreover ccl::*save-source-locations* is t, then ; (disassemble 'foo) will give the same nice result both before the fmakunbound ; and after the setf. ; We turn next to describing how compiled (and expansion) files are used when ; including a certified book. We defer to Part 3 an explanation of how ; expansion files are created (and thus, how compiled files are created) during ; book certification. ; In this essay, while we occasionally mention the use of raw mode within a ; book, presumably within a progn! form in the presence of a trust tag, we do ; not consider explicitly the evaluation of include-book forms in raw Lisp. ; This case is simpler than evaluation of include-book forms in the ACL2 loop; ; for example, the value of *hcomp-book-ht* is irrelevant for include-book ; performed in raw mode. ; Part 2: Including a certified book ; Fix a book for the following discussion. An add-trip symbol is "qualified" ; if whenever add-trip is to assign a relevant value by including the book in a ; boot-strap world, that value is equal to relevant value of the symbol if ; instead the compiled file is loaded. An add-trip symbol is "semi-qualified" ; if instead add-trip assigns a relevant value exactly twice, where the second ; value equals the relevant value of the symbol at the time the compiled file ; has just been loaded. This can happen if, and we believe only if (unless ; trust tags or make-event with non-nil :check-expansion are involved), the ; symbol is a *1* function symbol that is defined first in :program mode and ; then in :logic mode, say with verify-termination. We call an add-trip symbol ; "unqualified" if it is neither qualified nor semi-qualified. ; Include-book processes the book's compiled file using the following sequence ; of steps. (See also the summary shortly below this description.) ; First, include-book loads the compiled file: ; (1) Each *hcomp-xxx-alist* is an alist assigned by a form (setq ; *hcomp-xxx-alist* (quote ...)) near the top of the expansion file, after ; the initial (in-package "ACL2") but before the portcullis commands. This ; alist associates values with (and only with) all add-trip symbols: t for ; qualified, 'semi for semi-qualified, and nil for unqualified. Note that ; the values of these setq forms are quoted (laid down during book ; certification); hence the values of these globals are independent of the ; environment in which the compiled file is loaded. We use setq rather ; than defparameter because we have seen defparameter result in a slowdown ; of two orders of magnitude in CCL in doing the early load of compiled ; files. ; (2) Function hcomp-init is called (at load time), to do two things. For one, ; it adds to the *hcomp-xxx-restore-ht* hash tables, so that for each ; add-trip symbol that is not already a key of the suitable such hash ; table, that symbol is associated with its relevant value, if it has one, ; and otherwise is associated with a special "unbound" value, ; *hcomp-fake-value*. Also, it populates each *hcomp-xxx-ht* by ; associating each qualified add-trip symbol with t and each semi-qualified ; add-trip symbol with 'semi. Note that while the set of add-trip symbols ; (as well as their status as qualified, semi-qualified, or unqualified) is ; determined when the compiled file is written, the determination of ; relevant values written to *hcomp-xxx-alist* is done at load time. Also ; note that the domain of each *hcomp-xxx-alist*, representing the set of ; add-trip symbols, is the same as in Step (1). ; (3) Relevant values are assigned by loading the remainder of the compiled ; file, which starts with the portcullis commands. These are wrapped in a ; progn to maximize sharing using #n# syntax. ; Note however that the load may abort with a throw as described earlier above ; (missing compiled file for a sub-book). We'll catch any such throw before ; proceeding with the next step. ; (4) Evaluation of the form (hcomp-transfer-to-hash-tables) updates the ; *hcomp-xxx-ht* hash tables for use by add-trip, as follows. Let sym be ; an add-trip symbol with relevant value val and "qualified" status as ; determined by *hcomp-xxx-alist*. If sym is qualified, then sym is ; associated in *hcomp-xxx-ht* with val. Otherwise, if sym is ; semi-qualified, then sym is associated with the so-called "reclassifying ; value" (*hcomp-fake-value* . val). Otherwise, sym is not a key of the ; hash table. ; After attempting to load all compiled (or expansion) files under a top-level ; such load, ACL2 executes the following step as it cleans up using ; unwind-protection; see include-book-raw-top. ; (5) Relevant values are restored (which could mean making some symbols have ; undefined relevant values) for all add-trip symbols, regardless of ; "qualified" status, to what they were before Step (3), using the ; *hcomp-xxx-restore-ht* alists from Step (2). ; In summary, our alist and hash table globals have values as follows during ; the process of including a certified book. (NOTE that they have different ; values during the process of book certification, as discussed in Part 3 ; below.) ; *hcomp-xxx-alist* ; -- Before evaluating hcomp-init (near the top of the expansion file): ; Associates each add-trip symbol with t if qualified, 'semi if ; semi-qualified and otherwise nil ; *hcomp-xxx-ht* ; -- After evaluating hcomp-init (near the top of the expansion file): ; Assigns each qualified add-trip symbol to t and each semi-qualified ; add-trip symbol to 'semi (and these are the only keys) ; -- After loading compiled definitions, hcomp-transfer-to-hash-tables is ; called to populate *hcomp-xxx-ht* by associating a value with each ; qualified or semi-qualified add-trip symbol that has a relevant value, ; val (and only in those cases), as follows: ; + a qualified symbol is bound to val ; + a semi-qualified symbol is bound to the so-called "reclassifying ; value" (*hcomp-fake-value* . val), where val is a :logic mode *1* ; definition ; *hcomp-xxx-restore-ht* ; -- After completing a top-level load of a compiled (or expansion) file: ; Associates each add-trip symbol with its relevant value if any, else to ; *hcomp-fake-value* ; So, how is a relevant value assigned to an add-trip symbol when including a ; certified book? If the symbol is qualified, then its value is obtained from ; the relevant *hcomp-xxx-ht* hash table. Otherwise add-trip proceeds without ; the help of that hash table. However, if the symbol is assigned a ; reclassifying value (*hcomp-fake-value* . val) in the hash table, then even ; though add-trip does not use that value to assign a relevant value, the ; symbol is reassigned to val in the hash table; so if and when subsequently ; this *1* symbol is assigned a :logic-mode value by add-trip it will be val, ; i.e., the symbol will be treated as qualified. That is appropriate because ; if add-trip assigns a new value -- and assuming that redefinition is off, ; which it is unless there is a trust tag -- then the subsequent :logic mode ; definition will be ready for this saved value. ; If raw-mode is entered, then loading the compiled file can assign relevant ; values to symbols other than add-trip symbols. (By the way, we are not ; talking here about memoize and unmemoize, even when #+hons, because these are ; no-ops in raw Lisp.) Upon completion of the above sequence of five steps, ; new relevant values are only assigned for symbols that are not add-trip ; symbols, since as specified in Step (5) above, relevant values for add-trip ; symbols are restored from the *hcomp-xxx-restore-ht* variables after loading ; the compiled files. Users need to manage raw-mode carefully with respect to ; loading compiled files when including a book. ; If future enhancements are to allow add-trip to assign more than one relevant ; value for other than *1* symbols, we expect to be able to deal with such ; cases. If there can in fact be more than two such assignments for the same ; symbol, we can replace a reclassifying value (cons *hcomp-fake-value* val) by ; something like (list* *hcomp-fake-value* count val), where count is initially ; the number of expected re-assignments, it is decremented with each ; assignment, and only when it would be about to decrement to 0 would we ; actually use the hash table's value. Note that some of the initial ; assignments made during certify-book might not be made during include-book, ; because of redundancy, so the last value is the only one that can reliably be ; assigned (if the count ever gets down to 1). ; Part 3: Writing an expansion file for compilation ; We next consider the writing of the expansion file by certify-book. This ; process has three main steps. The first main step is storing relevant values ; in the *hcomp-xxx-ht* hash tables both based on the certification world and ; during the process-embedded-events call during the include-book pass of ; certify-book. The second main step then determines the value of each ; *hcomp-xxx-alist* for the setq forms to be written to the expansion file. ; The third main step actually writes forms to the expansion file. We now ; consider these in turn. Note that we do not access the *hcomp-xxx-alist* ; variables; their part in writing an expansion file is only to occur ; syntactically in the setq forms. ; The first main step populates each *hcomp-xxx-ht*. We begin by let-binding ; each *hcomp-xxx-ht* to its own eq hash table. Then we populate these hash ; tables -- first using the portcullis commands of the certification world, ; then during the process-embedded-events phase of include-book-fn -- updating ; the appropriate hash table for each symbol that is assigned a relevant value ; (because of a 'cltl-command property) by add-trip. (In the case of the ; portcullis commands, we do not actually run add-trip, but rather we mirror ; its necessary effects in function hcomp-build-from-portcullis.) When we ; encounter a symbol that is not already a key of that hash table, then we ; associate it with its relevant value. Otherwise, if the symbol is a *1* ; symbol that already has a value that is not a reclassifying value, and it is ; now being converted from :program to :logic mode, then the symbol is ; associated with the reclassifying value (*hcomp-fake-value* . val), where val ; is its current relevant value. Otherwise the symbol is other than a *1* ; symbol and already has a relevant value -- presumably something unusual has ; occurred by virtue of a trust tag -- and the symbol is associated with ; *hcomp-fake-value*. Note (in particular for the #+hons version) that memoize ; and unmemoize events do not have any effect on the populating of ; *hcomp-xxx-ht*. ; The second main step takes place after the return from ; process-embedded-events, and considers each symbol, sym, and associated value ; in the appropriate *hcomp-xxx-ht*. If the value is a reclassifying value ; (*hcomp-fake-value* . val) and val equals the current relevant value of sym, ; then sym is semi-qualified and is therefore to be associated with 'semi in ; *hcomp-xxx-alist*. For any other value, val, besides *hcomp-fake-value*, ; such that val equals the current relevant value of sym, then sym is qualified ; and is therefore to be associated with t in *hcomp-xxx-alist*. Otherwise VAL ; is unqualified and hence is to be associated with nil in *hcomp-xxx-alist* ; (see function hcomp-alists-from-hts for the check against the current ; relevant value). This last case is likely to be rather unusual, but can ; happen in the #+hons case if memoization occurs after a definition without ; being followed by unmemoization (more on this in the next paragraph). It can ; also happen if a function is redefined in raw-mode, though of course a trust ; tag is needed in that case; but we do not guarantee perfect handling of ; raw-mode, as there might be no raw-mode redefinition during the include-book ; phase of book certification and yet there might be raw-mode redefinition ; later during inclusion of the certified book -- anyhow, uses of raw-mode are ; the user's responsibility. If not for raw-mode, we might simply avoid any ; check and consider every add-trip symbol to be qualified or semi-qualified; ; memoization isn't a problem, since memoize is a no-op in raw Lisp and hash ; tables are populated during early include-books performed in raw Lisp. ; Note that we take a conservative approach, where memoization can make a ; symbol unqualfied. The consequence seems small, since as of this writing, ; memoization is only done in the #+hons version, which is only for ACL2 built ; on CCL, and CCL compiles on-the-fly; so the marking of an add-trip symbol as ; unqualified will not result in interpreted code. A future optimization might ; be to to avoid disqualification due to memoization in suitable cases, perhaps ; by tracking raw-mode or trust tags, or perhaps by somehow taking advantage of ; the 'old-fn field of the *memoize-info-ht* entry. ; It is instructive to consider the case that a :program mode definition is ; redundant with an earlier :logic mode definition made in the book (or its ; portcullis commands), either directly or by way of a redundant encapsulate, ; as per the following example from Jared Davis: ; (encapsulate () (defun f (x) (declare (xargs :mode :program)) x)) ; (verify-termination f) ; (encapsulate () (defun f (x) (declare (xargs :mode :program)) x)) ; Recall that the *1* functions written to the expansion file are based on the ; definitional event installed at the end of the include-book phase of ; certify-book. In this case, that will be the :logic mode definition; the ; redundant event is properly ignored. ; The third main step, writing forms to the expansion file, is rather ; straightforward based on the discussion above. We bind the current package ; to "ACL2", and then write a sequence of forms as follows. ; - (in-package "ACL2") ; - Forms that introduce packages that may be needed for reading symbols in the ; initial setq forms. These are introduced using maybe-introduce-empty-pkg-1 ; and maybe-introduce-empty-pkg-2. The maybe-introduce-empty-pkg-1 forms ; introduce all the packages together, just under the initial in-package ; form, thus avoiding a warning from GCL that can occur unless all defpackage ; forms immediately follow the initial in-package form. The ; maybe-introduce-empty-pkg-2 forms use special variable *defpkg-virgins* to ; let ACL2 know to accept subsequent corressponding defpkg forms. ; - Setq forms for the *hcomp-xxx-alist* variables as described above ; (hcomp-init) ; - Declaim forms (if any) ; - The portcullis commands ; - Book contents, modified according to the expansion-alist in the certificate ; that comes from make-event ; - *1* function definitions from the book (including the portcullis) ; Note that some of these are wrapped together in a progn to maximize sharing ; using #n# syntax. ; End of Essay on Hash Table Support for Compilation (defun hcomp-init () ; For context, see the Essay on Hash Table Support for Compilation. ; This function is called during loading of a compiled or expansion file by ; include-book, immediately after assigning alists to the *hcomp-xxx-alist* ; globals. The keys of each alist are the add-trip symbols for its type, ; associating value t if qualified, 'semi if semi-qualified, else nil. This ; function does two things. First, for each of the three alists, it puts an ; entry into the corresponding *hcomp-xxx-alist* hash table, for each key bound ; to non-nil in the alist. Second, it updates *hcomp-fn-restore-ht* to support ; the eventual restoration of relevant values for add-trip symbols. For ; details, see the Essay on Hash Table Support for Compilation. (when (or (raw-mode-p *the-live-state*) (null *hcomp-fn-ht*)) ; In raw mode, or when loading before compiling for include-book with ; :load-compiled-file :comp, we don't bother with hcomp hash tables and such. ; Rather, we expect that loading of compiled files has the effect one normally ; expects for raw Lisp. (assert (and (null *hcomp-const-ht*) (null *hcomp-macro-ht*))) (return-from hcomp-init nil)) (dolist (pair *hcomp-fn-alist*) (when (cdr pair) (setf (gethash (car pair) *hcomp-fn-ht*) (cdr pair)))) (dolist (pair *hcomp-const-alist*) (when (cdr pair) (setf (gethash (car pair) *hcomp-const-ht*) (cdr pair))) (when *hcomp-const-restore-ht* (multiple-value-bind (old present-p) (gethash (car pair) *hcomp-const-restore-ht*) (declare (ignore old)) (when (not present-p) (setf (gethash (car pair) *hcomp-const-restore-ht*) (cond ((boundp (car pair)) (symbol-value (car pair))) (t *hcomp-fake-value*))))))) (dolist (pair *hcomp-macro-alist*) (when (cdr pair) (setf (gethash (car pair) *hcomp-macro-ht*) (cdr pair)))) (when *hcomp-fn-macro-restore-ht* (dolist (pair (append *hcomp-macro-alist* *hcomp-fn-alist*)) (multiple-value-bind (old present-p) (gethash (car pair) *hcomp-fn-macro-restore-ht*) (declare (ignore old)) (when (not present-p) (setf (gethash (car pair) *hcomp-fn-macro-restore-ht*) (let ((mac (macro-function (car pair)))) (cond (mac (cons 'macro mac)) ((fboundp (car pair)) (cons 'function (symbol-function (car pair)))) (t *hcomp-fake-value*))))))))) (defabbrev reclassifying-value-p (x) ; See the Essay on Hash Table Support for Compilation. (and (consp x) (eq (car x) *hcomp-fake-value*))) (defmacro make-reclassifying-value (x) ; See the Essay on Hash Table Support for Compilation. `(cons *hcomp-fake-value* ,x)) (defmacro unmake-reclassifying-value (x) ; See the Essay on Hash Table Support for Compilation. `(cdr ,x)) (defun hcomp-transfer-to-hash-tables () ; See the Essay on Hash Table Support for Compilation. ; This function populates *hcomp-xxx-ht* hash tables with relevant values of ; qualified and semi-qualified add-trip symbols, after including a compiled or ; expansion file. (dolist (pair *hcomp-fn-alist*) (let ((qualified (gethash (car pair) *hcomp-fn-ht*))) (cond ((and qualified (fboundp (car pair)) ; likely only falsified here by raw mode ) (setf (gethash (car pair) *hcomp-fn-ht*) (cond ((eq qualified t) (symbol-function (car pair))) (t (assert$ (eq qualified 'semi) (make-reclassifying-value (symbol-function (car pair)))))))) (t (remhash (car pair) *hcomp-fn-ht*))))) (dolist (pair *hcomp-const-alist*) (let ((qualified (gethash (car pair) *hcomp-const-ht*))) (cond ((and qualified (boundp (car pair)) ; likely only falsified here by raw mode ) (setf (gethash (car pair) *hcomp-const-ht*) (assert$ (eq qualified t) (symbol-value (car pair))))) (t (remhash (car pair) *hcomp-const-ht*))))) (dolist (pair *hcomp-macro-alist*) (let ((qualified (gethash (car pair) *hcomp-macro-ht*))) (cond ((and qualified (macro-function (car pair)) ; raw mode check, as above ) (setf (gethash (car pair) *hcomp-macro-ht*) (assert$ (eq qualified t) (macro-function (car pair))))) (t (remhash (car pair) *hcomp-macro-ht*)))))) (defvar *saved-hcomp-restore-hts* nil) (defun hcomp-restore-defs () ; See the Essay on Hash Table Support for Compilation. ; This function undoes the effect of loading compiled and expansion files, in ; the sense that it restores relevant values: every add-trip symbol is given ; the relevant value it had before loading these, if any, else is unbound. ; The variable *saved-hcomp-restore-hts* should have just been been assigned to ; the current value of (list* *hcomp-fn-macro-restore-ht* ; *hcomp-const-restore-ht*). (when (null *saved-hcomp-restore-hts*) (er hard 'hcomp-restore-defs "Apparently an interrupt has occurred at exactly the right time to ~ thwart ACL2's attempt to clean up by removing certain definitions in ~ raw Lisp. You are strongly advised to restart ACL2. You could ~ instead try to continue, but you might well encounter errors ~ regarding having definitions in raw Common Lisp.")) (let ((fn-macro-restore-ht (car *saved-hcomp-restore-hts*)) (const-restore-ht (cdr *saved-hcomp-restore-hts*))) (when fn-macro-restore-ht (maphash (lambda (k val) (cond ((eq val *hcomp-fake-value*) ; We use fmakunbound! instead of fmakunbound in case trust tags have allowed ; some raw Lisp code to overwrite a function definition with a macro ; definition. (fmakunbound! k)) ((eq (car val) 'macro) (setf (macro-function k) (cdr val))) (t ; (eq (car val) 'function) (fmakunbound! k) ; remove potential macro-function (setf (symbol-function k) (cdr val))))) fn-macro-restore-ht)) (when const-restore-ht (maphash (lambda (k val) (cond ((eq val *hcomp-fake-value*) (remprop k 'redundant-raw-lisp-discriminator) (makunbound k)) (t ; The 'redundant-raw-lisp-discriminator property may be wrong here; but really, ; we don't expect this case to occur, since redefinition with defconst is not ; supported (unless perhaps extraordinary measures are taken using trust ; tags). (setf (symbol-value k) val)))) const-restore-ht)) nil)) (defun missing-compiled-book (ctx file reason-msg load-compiled-file state) ; This function is called when a compiled file is missing from an attempt to ; include a book. It either causes an error (because of an include-book called ; with :load-compiled-file t) or returns INCOMPLETE, which may be convenient ; when this result is to be placed into the status field of an ; hcomp-book-ht-entry record or is to be the value returned by ; load-compiled-book or include-book-raw. ; For convenience, we also use this function to report failure to complete the ; load of a compiled file when such a failure has previously been reported, but ; no such report has yet been made involving the files above that missing ; compiled file. In this case we pass reason-msg = nil. However, we do not ; expect this case to arise; see the comment about "flaw in our thinking" in ; include-book-raw. ; Warning: Do not change the message printed in the case reason-msg = nil ; without reading the comment in *uninhibited-warning-summaries* about ; "Compiled file". (let ((see-doc " See :DOC include-book.")) (cond ((null load-compiled-file) (er hard ctx "Implementation error: the LOAD-COMPILED-FILE argument is ~x0 ~ in call ~x1." nil `(missing-compiled-book ',ctx ',file ',reason-msg ',load-compiled-file state))) ((or (eq load-compiled-file t) (rassoc-eq t *load-compiled-stack*)) (let ((stack-msg (cond ((eq load-compiled-file t) (tilde-@-book-stack-msg t *load-compiled-stack*)) (t (tilde-@-book-stack-msg (car (rassoc-eq t *load-compiled-stack*)) *load-compiled-stack*))))) (cond (reason-msg (er hard ctx "Unable to load compiled file~| ~s0~|because ~@1.~@2~@3" file reason-msg see-doc stack-msg)) (t (er hard ctx "Unable to complete load of compiled file for book~|~ ~ ~ ~s0,~|as already noted by a warning.~@1~@2" file see-doc stack-msg))))) (reason-msg (warning$ ctx "Compiled file" "Unable to load compiled file for book~| ~s0~|because ~ ~@1.~@2~@3" file reason-msg see-doc (tilde-@-book-stack-msg nil *load-compiled-stack*))) (t (warning$ ctx "Compiled file" "Unable to complete load of compiled file for book~| ~ ~s0,~|as already noted by a previous warning.~@1" file (tilde-@-book-stack-msg nil *load-compiled-stack*))))) 'incomplete) (defun load-compiled-book (file directory-name load-compiled-file ctx state) ; We are processing include-book-raw underneath include-book-fn (hence ; presumably not in raw mode). File is an ACL2 full-book-name and ; load-compiled-file is non-nil. We attempt to load the corresponding compiled ; or perhaps expansion file if not out of date with respect to the book's ; certificate file. Normally, we return COMPLETE if such a suitable compiled ; file or expansion file exists and is loaded to completion, but if file is the ; book being processed by a surrounding include-book-fn and compilation is ; indicated because load-compiled-file is :comp and the expansion file is ; loaded (not the compiled file), then we return TO-BE-COMPILED in that case. ; Otherwise we return INCOMPLETE, that is, either no load is attempted for the ; compiled or expansion file (because they don't exist or are out of date), or ; else such a load but is aborted partway through, which can happen because of ; an incomplete load of a subsidiary include-book's compiled or expansion file. ; As suggested above, we may allow the corresponding expansion file to take the ; place of a missing or out-of-date compiled file. However, we do not allow ; this if load-compiled-file is t or a parent include-book has ; :load-compiled-file t. (assert load-compiled-file) (mv-let (cfile state) (certificate-file file state) (let* ((os-file (pathname-unix-to-os file state)) (cfile-date (and cfile (file-write-date cfile))) (ofile (convert-book-name-to-compiled-name os-file state)) (ofile-exists (probe-file ofile)) (ofile-date (and ofile-exists (file-write-date ofile))) (ofile-p (and ofile-date cfile-date (>= ofile-date cfile-date))) (efile (and (not (eq load-compiled-file t)) (expansion-filename file t state))) (efile-exists (and efile (probe-file efile))) (file-is-older-str "the file-write-date of ~x0 is less than that of ~x1")) (cond ((not cfile) (missing-compiled-book ctx file "that book is not certified" load-compiled-file state)) ((and (not ofile-exists) (not efile-exists)) (missing-compiled-book ctx file "the compiled file does not exist" load-compiled-file state)) ((not cfile-date) (missing-compiled-book ctx file (msg "~x0 is ~x1 (which is odd since file ~x2 exists)" `(file-write-date ,cfile) nil cfile) load-compiled-file state)) ((not (or ofile-p (let ((efile-date (and efile-exists (file-write-date efile)))) (and efile-date (>= efile-date cfile-date))))) (cond (ofile-exists (missing-compiled-book ctx file (msg file-is-older-str ofile cfile) load-compiled-file state)) (t ; hence efile-exists (missing-compiled-book ctx file (msg "the compiled file does not exist and ~@0" (msg file-is-older-str efile cfile)) load-compiled-file state)))) ((and (not ofile-p) ; hence efile is suitable to load, except: (rassoc-eq t *load-compiled-stack*)) (missing-compiled-book ctx file (if ofile-exists "that compiled file does not exist" "that compiled file is out-of-date") load-compiled-file state)) (t ; either ofile or efile is suitable for loading (let ((to-be-compiled-p ; true at top level of include-book-fn with :comp (and (not ofile-p) (null *load-compiled-stack*) (eq load-compiled-file :comp))) (status 'incomplete)) (when (and (not ofile-p) (not to-be-compiled-p)) ; Hence efile is suitable and we are not in the special case of compiling it on ; behalf of include-book-fn. Note that for the case of compiling on behalf of ; include-book-fn, either that compilation will succeed or there will be an ; error -- either way, there is no need to warn here. (warning$ ctx "Compiled file" "Loading expansion file ~x0 in place of compiled file ~ ~x1, because ~@2." efile ofile (cond (ofile-exists (msg file-is-older-str ofile cfile)) (t (msg "the compiled file is missing"))))) (catch 'missing-compiled-book ; bogus compiler warning in LispWorks 6.0.1, gone in LispWorks 6.1 (state-global-let* ((raw-include-book-dir-alist nil) (connected-book-directory directory-name)) (let ((*load-compiled-stack* (acons file load-compiled-file *load-compiled-stack*))) (cond (ofile-p (load-compiled ofile t)) (t (with-reckless-read (load efile)))) (value (setq status (if to-be-compiled-p 'to-be-compiled 'complete)))))) (hcomp-transfer-to-hash-tables) (assert$ (member-eq status '(to-be-compiled complete incomplete)) status))))))) (defun include-book-raw (book-name directory-name load-compiled-file dir ctx state) ; This function is generally called on behalf of include-book-fn. No load ; takes place if load-compiled-file is effectively nil (either nil or else ; compiler-enabled is nil) unless we are in raw mode, in which case we attempt ; to load the source file, book-name. So suppose load-compiled-file is not ; nil. When the call is not under certify-book-fn, the effect is to populate ; *hcomp-book-ht* with *hcomp-xxx-ht* hash tables for the given book and ; (recursively) all its sub-books; see the Essay on Hash Table Support for ; Compilation. Otherwise its effect is as follows: load the compiled file if ; it exists and is up-to-date with respect to the certificate, else load the ; expansion file, else (but only in raw mode) load the source book. (The ; *hcomp-xxx* variables are irrelevant, by the way, if we are not calling ; add-trip or otherwise involving ACL2 event processing.) ; If directory-name is nil, then book-name is a user-book-name. Otherwise ; book-name is a full-book-name whose directory is directory-name. ; Load-compiled-file and dir are the arguments of these names from ; include-book. ; Now suppose that we are not in raw mode, i.e., we are evaluating this call ; underneath some call of include-book-fn. We return nil if no load is ; attempted, for example because load-compiled-file is effectively nil. If the ; compiled file or expansion file is loaded in its entirety, then we return ; 'complete. Otherwise we throw to the tag 'missing-compiled-book with the ; status 'incomplete. (when (not (member-eq load-compiled-file *load-compiled-file-values*)) (er hard ctx "The only legal values for the :LOAD-COMPILED-FILE keyword argument ~ of ~x0 are ~&1. The value ~x2 is thus illegal." 'include-book *load-compiled-file-values* load-compiled-file)) (when *compiling-certified-file* ; See the comment below related to *compiling-certified-file*. (return-from include-book-raw nil)) (let* ((raw-mode-p (raw-mode-p state)) (load-compiled-file (cond ((null (f-get-global 'compiler-enabled state)) nil) ((eq load-compiled-file :default) :warn) (t (or load-compiled-file ; If load-compiled-file is nil but we are in the process of loading the ; compiled file for a superior book, then there is an include-book for such a ; book, B, with a non-nil value of :load-compiled-file. Even if that value is ; :warn or :comp, hence not t, we still need to try to load a compiled file for ; the present book; of course, if a compiled file is missing for the present ; book or any sub-book, then whether that causes an error or only a warning ; depends on whether some such book B has :load-compiled-file t. (and *load-compiled-stack* :warn)))))) (when (and (not raw-mode-p) (null load-compiled-file)) (return-from include-book-raw nil)) (mv-let (full-book-name directory-name ignore-familiar-name) (cond (directory-name (mv book-name directory-name nil)) (t (parse-book-name (cond (dir (or (include-book-dir dir state) (er hard ctx "Unable to find the :dir argument to ~ include-book, ~x0, which should have been ~ defined by add-include-book-dir. Perhaps ~ the book ~x1 needs to be recertified." dir book-name))) (t (f-get-global 'connected-book-directory state))) book-name ".lisp" ctx state))) (declare (ignore ignore-familiar-name)) (cond ((let ((true-full-book-name (our-truename full-book-name :safe))) (and true-full-book-name (assoc-equal true-full-book-name (global-val 'include-book-alist (w state))))) ; In ACL2 Version_4.1 running on Allegro CL, we got an error when attempting to ; certify the following book. ; (in-package "ACL2") ; (include-book "coi/lists/memberp" :dir :system) ; The problem was that truename is (one might say) broken in Allegro CL. ; Fortunately, Allegro CL provides an alternative that seems to work -- ; excl::pathname-resolve-symbolic-links -- and we now use that function (see ; our-truename). The problem goes away if that function is applied to ; full-book-name under the call of assoc-equal below. But the error occurred ; in the context of loading a file just compiled from a book, and in that ; context there is no reason to execute any raw-Lisp include-book. Thus, we ; short-circuit in that case -- see the use of *compiling-certified-file* above ; -- and now we never even get to the above assoc-equal test in that case. ; A final comment in the case that we really do get to this point: ; Since all relevant values have been defined, there is no need to transfer to ; hash tables (as per the Essay on Hash Table Support for Compilation). This ; is the case even if we loaded in raw-mode. It would be harmless enough to ; load in the raw-mode case, and could be desirable if values are ; context-dependent and it is expected that we re-load, but for now we avoid ; the inefficiency of repeated loads. nil) ((or raw-mode-p ; If *hcomp-book-ht* is nil and we are not in raw mode, then we are under an ; include-book-fn being performed on behalf of certify-book. In that case we ; just do a load as we would in raw Lisp, without regard to the hash tables ; described in the Essay on Hash Table Support for Compilation. (null *hcomp-book-ht*)) (state-free-global-let* ((connected-book-directory directory-name)) (let* ((os-file (pathname-unix-to-os full-book-name state)) (ofile (convert-book-name-to-compiled-name os-file state)) (os-file-exists (probe-file os-file)) (ofile-exists (probe-file ofile)) (book-date (and os-file-exists (file-write-date os-file))) (ofile-date (and ofile-exists (file-write-date ofile)))) (cond ((not os-file-exists) (er hard ctx "File ~x0 does not exist." os-file)) ((null load-compiled-file) (assert$ raw-mode-p ; otherwise we already returned above ; If make-event is used in the book, then the following load may cause an ; error. The user of raw mode who supplied a :load-compiled-file argument is ; responsible for the ensuing behavior. (load os-file))) ((and book-date ofile-date (<= book-date ofile-date)) (load-compiled ofile t)) (t (let ((reason (cond (ofile-exists "the compiled file is not at least as ~ recent as the book") (t "the compiled file does not exist")))) (cond ((eq load-compiled-file t) (er hard ctx "The compiled file for ~x0 was not loaded ~ because ~@1." reason)) (t (let* ((efile (expansion-filename full-book-name t state)) (efile-date (and (probe-file efile) (file-write-date efile))) (efile-p (and book-date efile-date (<= book-date efile-date))) (lfile (cond (efile-p efile) (raw-mode-p os-file) (t (er hard ctx "Implementation error: ~ We seem to have ~ called ~ include-book-raw on ~ book ~x0 with non-nil ~ load-compiled-file ~ argument under the ~ include-book-fn call ~ in certify-book-fn." book-name))))) (warning$ ctx "Compiled file" "Attempting to load ~@0 instead of ~ the corresponding compiled file, ~ because ~@1." (msg (cond (efile-p "expansion file ~x0") (t "source file ~x0")) lfile) reason) (cond (efile-p (with-reckless-read (load efile))) (raw-mode-p (load os-file)))))))))))) ((let* ((entry (assert$ *hcomp-book-ht* ; not raw mode, e.g. (gethash full-book-name *hcomp-book-ht*))) (status (and entry (access hcomp-book-ht-entry entry :status)))) ; The status might be nil because of soft links, in analogy to the case for ; soft links described in a comment above. But as explained in that comment, ; this is harmless; it would simply cause us to fall through and deal with the ; book as though it's newly encountered. ; Below, when status is nil then it is because entry is nil, in which case it ; is correct to fall through to the next top-level COND branch. See (defrec ; hcomp-book-ht-entry ...) for a comment on the legal (hence non-nil) status ; values. (cond (raw-mode-p status) ; if nil then there is no entry, so fall through ((and (eq status 'incomplete) ; so not from raw-mode include-book *load-compiled-stack*) ; Can the status really be INCOMPLETE? At first glance it would seem this this ; is impossible. For, imagine that we are loading a book's compiled file (or ; expansion file) in raw Lisp prior to processing its events. At the first ; INCOMPLETE status, the tree of include-book forms rooted at that top ; include-book is no longer consulted -- no further loads occur anywhere to the ; right of the branch above the node on which the INCOMPLETE status is ; returned. Ah, but perhaps the topmost include-book has :load-compiled-file ; nil. The argument above shows that any book with existing INCOMPLETE status ; must have been processed by some earlier include-book having non-nil ; :load-compiled-file. But then the offending book has already been included, ; and hence no raw-Lisp load will take place, since the offending book is on the ; 'include-book-alist of the current world. ; But we keep this case, just in case we later find a flaw in our thinking! ; (If this comment is removed, consider the reference to "flaw in our thinking" ; in function missing-compiled-book.) (error "Implementation error; see include-book-raw.") ; Code we keep in case our thinking above is flawed: (throw 'missing-compiled-book (missing-compiled-book ctx full-book-name nil load-compiled-file state))) (t status)))) (t ; not raw-mode, and load-compiled-file is non-nil (with-hcomp-bindings t (let ((status (let ((*user-stobj-alist* ; Our intention is that the call of load-compiled-book below has no effect on ; the state other than to define some packages and populate *hcomp-xxx-ht* hash ; tables. We therefore protect the one global managed by add-trip that is not ; managed by those hash tables: *user-stobj-alist*. See the Essay on Hash ; Table Support for Compilation. *user-stobj-alist*)) (load-compiled-book full-book-name directory-name load-compiled-file ctx state)))) (setf (gethash full-book-name *hcomp-book-ht*) (make hcomp-book-ht-entry :status status :fn-ht *hcomp-fn-ht* :const-ht *hcomp-const-ht* :macro-ht *hcomp-macro-ht*)) (cond ((member-eq status '(to-be-compiled complete)) status) (status (assert$ (eq status 'incomplete) (cond (*load-compiled-stack* (throw 'missing-compiled-book 'incomplete)) (t 'incomplete)))))))))))) (defun include-book-raw-top (full-book-name directory-name load-compiled-file dir ctx state) (let ((*hcomp-fn-macro-restore-ht* (make-hash-table :test 'eq)) (*hcomp-const-restore-ht* (make-hash-table :test 'eq))) ; We need to be careful about handling interrupts. On the one hand, we want to ; take advantage of the "idempotency" provided by acl2-unwind-protect that is ; described in The Unwind-Protect Essay. On the other hand, cleanup forms of ; acl2-unwind-protect will be evaluated outside the scope of the bindings just ; above. Our solution is for an unwind-protect cleanup form to do nothing more ; than save the above three hash tables -- which we expect can complete without ; interruption, though we check for that in hcomp-restore-defs -- and then for ; acl2-unwind-protect to do the actual cleanup using those saved values. (setq *saved-hcomp-restore-hts* nil) (acl2-unwind-protect "include-book-raw" (unwind-protect (progn (include-book-raw full-book-name directory-name load-compiled-file dir ctx state) (value nil)) (setq *saved-hcomp-restore-hts* (list* *hcomp-fn-macro-restore-ht* *hcomp-const-restore-ht*))) (progn (hcomp-restore-defs) (setq *saved-hcomp-restore-hts* nil) state) (progn (hcomp-restore-defs) (setq *saved-hcomp-restore-hts* nil) state)))) (defmacro hcomp-ht-from-type (type ctx) `(case ,type (defun *hcomp-fn-ht*) (defparameter *hcomp-const-ht*) ((defmacro defabbrev) *hcomp-macro-ht*) (otherwise (er hard ,ctx "Implementation error: Unknown case, ~x0." ,type)))) (defmacro hcomp-build-p () '(and (eq *inside-include-book-fn* 'hcomp-build) ; under certify-book-fn *hcomp-fn-ht* ; compile-flg is true )) (defun install-for-add-trip-hcomp-build (def reclassifyingp evalp) ; Def is a definition starting with defun, defconst, defmacro, or defabbrev. (let* ((type (car def)) (name (cadr def)) (ht (hcomp-ht-from-type type 'install-for-add-trip-hcomp-build)) (oldp (and (eq type 'defparameter) (boundp name)))) (when evalp (eval def)) (assert ht) (multiple-value-bind (old present-p) (gethash name ht) (cond ((eq old *hcomp-fake-value*)) ; then we keep the fake value (present-p (cond ((and reclassifyingp (not (reclassifying-value-p old))) (assert$ (eq type 'defun) ; We expect a *1* function here. If that is not the case (for some odd reason ; we don't foresee), then we will be making a reclassifying value here that ; presumably won't get used. (setf (gethash name ht) (make-reclassifying-value (symbol-function name))))) (t ; This case is presumably impossible unless raw mode is used somehow to allow ; redefinition. But we are conservative here. (setf (gethash name ht) *hcomp-fake-value*)))) (oldp ; Name is already boundp, perhaps even by a defattach in the ACL2 source code. ; Handling of this case supports our fix for a bug described in note-4-2: ; "Fixed a bug in which the wrong attachment could be made...." We hit that ; bug when we tried to attach to acl2x-expansion-alist upon including the ; community book books/make-event/acl2x-help.lisp (see the defattach there for ; that function), causing certification to fail for ; books/make-event/acl2x-help.lisp. That certification failed because the ; attachment was getting its value from *hcomp-const-ht*, which had not seen ; that attachment because the load was aborted due to a missing compiled file ; for a book included under acl2x-help.lisp. Perhaps we should never put a ; defparameter for a defattach into *hcomp-const-ht*, but anyhow, the following ; setf handles the issue. (setf (gethash name ht) *hcomp-fake-value*)) (t (setf (gethash name ht) (case type (defun (symbol-function name)) (defparameter (symbol-value name)) (otherwise (macro-function name))))))))) (defun install-for-add-trip-include-book (type name def reclassifyingp) ; Def is nil if no evaluation of a definition is desired, in which case we ; return true when the definition exists in the appropriate hash table. ; Otherwise def is a definition starting with defun, defconst, defmacro, or ; defabbrev. (let ((ht (hcomp-ht-from-type type 'install-for-add-trip-include-book))) (when (null ht) ; e.g., including uncertified book (return-from install-for-add-trip-include-book (when def (eval def)))) (multiple-value-bind (val present-p) (gethash name ht) (cond (present-p (assert$ (not (eq val *hcomp-fake-value*)) (cond ((reclassifying-value-p val) (assert$ (eq type 'defun) ; presumably a *1* symbol (let ((fixed-val (unmake-reclassifying-value val))) (setf (gethash name ht) fixed-val) (cond (reclassifyingp ; We are converting the definition of some function, F, from :program mode to ; :logic mode. Since reclassifying-value-p holds of val, the book (including ; its portcullis commands) contains both a :program mode definition of F and a ; :logic mode definition of F, and so far we have processed neither while ; including this book. Since parameter reclassifyingp is true, we are now ; converting F from :program mode to :logic mode, which may seem surprising ; given that we have not processed the earlier :program mode definition in the ; book. The situation however is that now, we are including this book in a ; world where F was already defined in :program mode. Since we are now ; reclassifying to :logic mode, there is no need to go through the usual ; two-step process; rather, we can simply define the function now. We probably ; don't need to modify the hash table in this case (as we did above); but this ; case is probably unusual so the potential efficiency hit seems trivial, and ; it seems safest to go ahead and keep only the true value in the hash table ; henceforth. (setf (symbol-function name) fixed-val) t) (t (when def (eval def))))))) (t (case type (defun (setf (symbol-function name) val)) (defparameter (setf (symbol-value name) (cond ((and (consp (caddr def)) (eq (car (caddr def)) 'quote)) ; Remark on Fast-alists. ; We get here from processing of an add-trip form by defconst, immediately ; after setting the 'redundant-raw-lisp-discriminator property for the symbol ; being defined. Now, the raw Lisp definition of defconst (which may be ; invoked during early load of compiled files later in the session) insists ; that the cddr above property agree with (be EQ to) the symbol's symbol-value. ; In the case of a quotep, these are both to be EQ to the cadr of that quotep, ; in support of the #+hons version of ACL2, as described below. So in this ; quotep case, we avoid the value stored in the hash table, i.e., the value ; produced by the compiler. ; To see why we want to avoid the value produced by the compiler in the #+hons ; case, consider the following event. ; (make-event ; `(defconst *foo* ',(make-fast-alist '((1 . 10) (2 . 20))))) ; The intention here is to store a fast-alist in *foo*, and the serialize ; reader supports this when reading from the expansion-alist in the book's ; certificate, where the above fast-alist will be stored. However, that ; fast-alist nature of this constant is lost when the alist comes from the ; book's compiled file. ; See also related comments in defconst-val, make-certificate-file1. (cadr (caddr def))) (t val)))) (otherwise (assert$ (member-eq type '(defabbrev defmacro)) (setf (macro-function name) val)))) t)))) (t (when def (eval def))))))) (defun install-for-add-trip (def reclassifyingp evalp) ; For background on how we use hash tables to support early loading of compiled ; files by include-book, see the Essay on Hash Table Support for Compilation. ; Evalp is only relevant when (hcomp-build), in which case it is passed to ; install-for-add-trip-hcomp-build. (cond ((eq *inside-include-book-fn* t) ; in include-book-fn, not certify-book-fn (install-for-add-trip-include-book (car def) (cadr def) def reclassifyingp)) ((hcomp-build-p) (install-for-add-trip-hcomp-build def reclassifyingp evalp)) (t (eval def)))) (defun install-defs-for-add-trip (defs reclassifying-p wrld declaim-p evalp) ; Defs is a list of definitions, each of which is a call of defun, defabbrev, ; or defmacro, or else of the form (ONEIFY-CLTL-CODE defun-mode def ; stobj-name), where def is the cdr of a call of defun. ; This function, which may destructively modify defs, is responsible for ; declaiming and submitting every definition in defs, while avoiding such ; effort when a definition is already available from *hcomp-fn-ht*. Note that ; if its definition is available from that hash table, then it was already ; declaimed (if necessary) during the load of the expansion file (or the ; compiled version of it) that populated that hash table with its definition. ; The only time we retrieve an existing definition from *hcomp-fn-ht* is during ; include-book-fn but not during certify-book-fn, i.e., when ; *inside-include-book-fn* is t. ; Evalp is only relevant when (hcomp-build),in which case it is passed to ; install-for-add-trip-hcomp-build. ; We start with declaiming of inline and notinline. (loop for tail on defs do (let* ((def (car tail)) (oneify-p (eq (car def) 'oneify-cltl-code)) (def0 (if oneify-p (caddr def) (cdr def))) (name (symbol-name (car def0)))) (cond ((equal (caddr def0) '(DECLARE (XARGS :NON-EXECUTABLE :PROGRAM))) ; We allow redefinition for a function introduced by :defproxy, regardless of ; the value of state global 'ld-redefinition-action. If the original ; definition were inlined, then this redefinition might be ignored, and it ; could reasonably be viewed as our fault, because we would not be able to say ; "all bets are off with the use of ld-redefinition-action". ; If we change or remove this proclaim form, then revisit the comment about ; inlining in redefinition-renewal-mode. (let ((form (list 'notinline (if oneify-p (*1*-symbol (car def0)) (car def0))))) (proclaim form) (push (list 'declaim form) *declaim-list*))) (oneify-p nil) ((terminal-substringp *inline-suffix* name *inline-suffix-len-minus-1* (1- (length name))) (let ((form (list 'inline (car def0)))) (proclaim form) (push (list 'declaim form) *declaim-list*))) ((terminal-substringp *notinline-suffix* name *notinline-suffix-len-minus-1* (1- (length name))) (let ((form (list 'notinline (car def0)))) (proclaim form) (push (list 'declaim form) *declaim-list*)))))) (loop for tail on defs do (let* ((def (car tail)) (oneify-p (eq (car def) 'oneify-cltl-code)) (def0 (if oneify-p (caddr def) (cdr def)))) (cond ((and (eq *inside-include-book-fn* t) (if oneify-p (install-for-add-trip-include-book 'defun (*1*-symbol (car def0)) nil reclassifying-p) (install-for-add-trip-include-book (car def) (cadr def) nil reclassifying-p))) (setf (car tail) nil)) (t (let (form) (cond (oneify-p (let ((*1*-def (cons 'defun (oneify-cltl-code (cadr def) def0 (cdddr def) wrld)))) (setf (car tail) *1*-def) ; While it is tempting to do a declaim for a *1* function, ; make-defun-declare-form isn't up to the task as of the development sources on ; 5/2/2013. Perhaps this would be easy to fix, but since we only declaim for ; GCL, and it is not an important goal to make *1* functions efficient, we skip ; this step. ; (when declaim-p ; (setq form ; (make-defun-declare-form (car def0) ; *1*-def))) )) ((and declaim-p (not (member-eq (car def) '(defmacro defabbrev)))) (setq form (make-defun-declare-form (cadr def) def)))) (when (and form (hcomp-build-p)) (push form *declaim-list*)) (when evalp (eval form))))))) (cond ((eq *inside-include-book-fn* t) (loop for tail on defs when (car tail) do (eval (car tail)))) ((hcomp-build-p) (loop for def in defs do (install-for-add-trip-hcomp-build def reclassifying-p evalp))) (t (loop for def in defs do (eval def))))) (defun hcomp-build-from-portcullis-raw (cltl-cmds state) ; Warning: If you change this function, consider making corresponding changes ; to add-trip. We wrote the present function primarily by eliminating extra ; code from the definition of add-trip, to satisfy the following spec. We also ; eliminated comments; see add-trip for those. ; Cltl-cmds is a list of cltl-command values, each the cddr of some triple in ; the world. We are certifying a book, and we want to populate the ; *hcomp-xxx-ht* hash-tables much as we do when processing events in the book. ; We also start populating *declaim-list*. (let ((*inside-include-book-fn* 'hcomp-build)) (dolist (cltl-cmd cltl-cmds) (let* ((wrld (w state))) (case (car cltl-cmd) (defuns (let ((ignorep (caddr cltl-cmd)) (defun-mode (cadr cltl-cmd)) (new-defs nil) (new-*1*-defs nil)) (dolist (def (cdddr cltl-cmd)) (cond ((and (consp ignorep) (eq (car ignorep) 'defstobj)) nil) (t (or ignorep (setq new-defs (cons (cons 'defun def) new-defs))) (setq new-*1*-defs (cons (list* 'oneify-cltl-code defun-mode def (if (consp ignorep) (cdr ignorep) nil)) new-*1*-defs))))) (install-defs-for-add-trip (nconc new-defs new-*1*-defs) (eq ignorep 'reclassifying) wrld t nil))) ((defstobj defabsstobj) (let ((name (nth 1 cltl-cmd)) (the-live-name (nth 2 cltl-cmd)) (init (nth 3 cltl-cmd)) (raw-defs (nth 4 cltl-cmd)) (ax-defs (nth 6 cltl-cmd)) (new-defs nil)) (install-for-add-trip `(defparameter ,the-live-name ,init) nil nil) (dolist (def raw-defs) (push (cond ((eq (car cltl-cmd) 'defabsstobj) (cons 'defmacro def)) ((member-equal *stobj-inline-declare* def) (cons 'defabbrev (remove-stobj-inline-declare def))) (t (cons 'defun def))) new-defs)) (dolist (def ax-defs) (push (list* 'oneify-cltl-code :logic def name) new-defs)) (setq new-defs (nreverse new-defs)) (install-defs-for-add-trip new-defs nil wrld t nil))) (defconst (install-for-add-trip `(defparameter ,(cadr cltl-cmd) ',(cadddr cltl-cmd)) nil nil)) (defmacro (install-for-add-trip cltl-cmd nil nil)) (attachment ; (cddr trip) is produced by attachment-cltl-cmd (dolist (x (cdr cltl-cmd)) (let ((name (if (symbolp x) x (car x)))) (install-for-add-trip (cond ((symbolp x) (set-attachment-symbol-form x nil)) (t (set-attachment-symbol-form name (cdr x)))) nil nil)))) ; There is nothing to do for memoize or unmemoize. )))) (value nil)) (defun hcomp-alists-from-hts () (let ((fn-alist nil) (const-alist nil) (macro-alist nil)) (maphash (lambda (k val) (push (cons k (cond ((eq val *hcomp-fake-value*) nil) ((not (fboundp k)) nil) ((reclassifying-value-p val) (and (eq (unmake-reclassifying-value val) (symbol-function k)) 'semi)) ((eq val (symbol-function k)) t) (t nil))) fn-alist)) *hcomp-fn-ht*) (maphash (lambda (k val) (push (cons k (cond ((eq val *hcomp-fake-value*) nil) ((not (boundp k)) nil) ((eq val (symbol-value k)) t) (t nil))) const-alist)) *hcomp-const-ht*) (maphash (lambda (k val) (push (cons k (cond ((eq val *hcomp-fake-value*) nil) ((and val (eq val (macro-function k))) t) (t nil))) macro-alist)) *hcomp-macro-ht*) (mv fn-alist const-alist macro-alist))) ; This concludes development of code for early loading of compiled files ; (though other related such code may be found elsewhere). (defun-one-output add-trip (world-name world-key trip) ; Warning: If you change this function, consider making corresponding changes ; to hcomp-build-from-portcullis-raw. ; Add-trip is the function that moves a triple, (symb key . val) from ; a property list world into the von Neumann space of Common Lisp. ; World-name is the name of the world being installed. World-key is ; the property being used to hold the installed properties of that ; world (i.e., the cdr of its 'acl2-world-pair). ; First we set the properties for the global-symbol and *1*-symbol, so that ; these will ultimately be behind the world-key property (as guaranteed at the ; end of the code for this function). (global-symbol (car trip)) (*1*-symbol? (car trip)) ; e.g. hard-error for *1*-symbol with (table :a 3 4) ; Our next step is to push val onto the key stack in (get symb world-key). (setf (get (car trip) world-key) (destructive-push-assoc (cadr trip) (cddr trip) (get (car trip) world-key) world-key)) ; Now, in the case that we are messing with 'current-acl2-world and ; symb is 'CLTL-COMMAND and key is 'GLOBAL-VALUE, we smash the ; symbol-function or symbol-value cell of the appropriate name, first ; saving the old value (form) on the undo-stack. (cond ((and (eq world-name 'current-acl2-world) (eq (car trip) 'cltl-command) (eq (cadr trip) 'global-value) (consp (cddr trip))) (let* ((wrld (w *the-live-state*)) (boot-strap-flg (global-val 'boot-strap-flg wrld))) (case (car (cddr trip)) (defuns ; (cddr trip) is of the form (defuns defun-mode ignorep def1 ... defn). ; Defun-mode non-nil is stored by DEFUNS and defun-mode nil by :non-executable ; DEFUNS and by ENCAPSULATE when it is defining the constrained fns. ; Oneify-cltl-code relies on the fact that functions with defun-mode nil do a ; THROW. ; Observe that we sometimes use oneify-cltl-code to modify the actual Common ; Lisp code. Why don't we modify the defi before storing the cltl-command ; tuple? Because we want to make it easy on ourselves to recover from the ; world the actual defi used to define :program mode functions. See ; verify-termination. ; Recall that ignorep is non-nil if we are to AVOID storing the ; symbol-functions for names. If ignorep is non-nil, then it is either ; reclassifying -- meaning we are reclassifying a symbol from :program ; to :logic mode. We don't want to overwrite its ; symbol-function since that might be ACL2 source code. ; We still write a *1* definition in this case. ; (defstobj . stobj) ; -- meaning the names being introduced are actually being ; defun'd under (defstobj stobj ...) or (defabsstobj stobj ; ...). We don't want to store the code generated by defun ; for these names because defstobj and defabsstobj will ; generate a CLTL-COMMAND containing the made-to-order raw ; defs. We also do not store the *1* definition in this ; case, because in CCL (at least) this would cause a problem ; since the *1* code calls the raw Lisp function, which has ; not yet been defined and in the :inline case is actually a ; macro. (See also the comment in defstobj-functionsp.) ; Why do we need the stobj name in the case of ignorep = '(defstobj . stobj)? ; The reason is that when we generate the *1* code for the function, fn, we ; must generate a throw to handle a guard violation and the argument to that ; throw is an object which includes, among other things, the stobjs-in of fn so ; we will know how to print them. You might think we would get the stobjs-in ; of fn from the world. But we can't because this defun is being done under, ; and as part of, a defstobj or defabsstobj event, and the event will later ; declare stobj to be a stobj name. So the stobjs-in of fn in the world right ; now is wrong. The stobjs-in we need is built into the object thrown and so ; won't be overwritten when the event gets around to declaring stobj a stobj. ; So oneify-cltl-code, called below, takes the stobj name as its input and ; computes the appropriate stobjs-in from the formals. This is a problem ; analogous to the one addressed by the super-defun-wart table. (let ((ignorep (caddr (cddr trip))) (defun-mode (cadr (cddr trip))) (new-defs ; We avoid potential "undefined" warnings by holding off on compilation until ; all the functions have been defined. Moreover, in the case of CCL we ; need to hold off even on defining the functions. So we collect up the ; definitions that need to be made in Common Lisp, proclaiming as we go ; (although proclaiming may be a no-op in some Lisps), then make all the ; definitions, and finally do the compilation as appropriate. nil) (new-*1*-defs nil)) (dolist (def (cdddr (cddr trip))) (cond ((and boot-strap-flg (not (global-val 'boot-strap-pass-2 wrld))) ; During the first pass of initialization, we insist that every function ; defined already be defined in raw lisp. During pass two we can't expect this ; because there may be LOCAL defuns that got skipped during compilation and the ; first pass. (or (fboundp (car def)) ; Note that names of macros are fboundp, so we can get away with symbols that ; are defined to be macros in raw Lisp but functions in the logic (e.g., ; return-last). (interface-er "~x0 is not fboundp!" (car def))) ; But during the first pass of initialization, we do NOT assume that every (or ; any) function's corresponding *1* function has been defined. So we take care ; of that now. (or (member-eq (car def) ; For explanation of the special handling of the first three of the following ; function symbols, see the comments in their defun-*1* forms. For ; *defun-overrides*, we have already taken responsibility for defining *1* ; functions that we don't want to override here. `(mv-list return-last wormhole-eval ,@*defun-overrides*)) (setq new-*1*-defs (cons (list* 'oneify-cltl-code defun-mode def ; The if below returns the stobj name being introduced, if any. (if (consp ignorep) (cdr ignorep) nil)) new-*1*-defs)))) ((and (not ignorep) (equal *main-lisp-package-name* (symbol-package-name (car def)))) (interface-er "It is illegal to redefine a function in ~ the main Lisp package, such as ~x0!" (car def))) ((and (consp ignorep) (eq (car ignorep) 'defstobj)) ; We wait for the cltl-command from the defstobj or defabsstobj (which is laid ; down last by defstobj-fn or defabsstobj-fn, using install-event) before ; defining/compiling the *1* functions, in order to avoid potential "undefined" ; warnings and, more importantly, to avoid defining *1* functions in terms of ; undefined macros (for the :inline case of defstobj and for defabsstobj), ; which confuses CCL as described in a comment in defstobj-functionsp. We ; still save the existing values (if any) of the current def and the current ; *1* def; see the next comment about ignorep. (maybe-push-undo-stack 'defun (car def) ignorep)) (t (maybe-push-undo-stack 'defun (car def) ignorep) ; Note: If ignorep is '(defstobj . stobj), we save both the current def and the ; current *1* def. If ignorep is 'reclassifying, we save only the *1* def. ; The former behavior means that in defstobj, when the defun runs for each ; name, we will save both symbol-function cells, even though we store into ; neither. The code for installing a defstobj CLTL-COMMAND doesn't bother to ; do undo-stack work, because it knows both cells were saved by the defun. (or ignorep (setq new-defs (cons (cons 'defun def) new-defs))) (setq new-*1*-defs (cons (list* 'oneify-cltl-code defun-mode def ; The if below returns the stobj name being introduced, if any. (if (consp ignorep) (cdr ignorep) nil)) new-*1*-defs))))) (dolist (def new-defs) ; Remove the documentation string potentially stored in raw Lisp, if a copy is ; already around in our documentation database, just to save space. (when (and (eq (car def) 'defun) (doc-stringp (documentation (cadr def) 'function))) (setf (documentation (cadr def) 'function) nil))) (setq new-defs (nconc new-defs new-*1*-defs)) (install-defs-for-add-trip new-defs (eq ignorep 'reclassifying) wrld (not boot-strap-flg) t) (cond ((not (eq (f-get-global 'compiler-enabled *the-live-state*) t)) ; Then skip compilation. ) ((or ; It seems critical to compile as we go in CMUCL 18e during the boot-strap, in ; order to avoid stack overflows. This seems to cut about 20% off the ; regression time for Allegro builds, so we go ahead and do this in all Lisps. ; See also the long comment for the case (eq fns :some) in ; compile-uncompiled-*1*-defuns. It is tempting to avoid this on-the-fly ; compilation for GCL, where we have seen build time shrink from over 22 to ; under 7 minutes and have seen roughly a percent or slightly less degradation ; in regression time, probably because of the lack of compilation in that case ; of *1* functions for built-in :program mode functions. But we have decided, ; at least for now, to keep the code simple by doing the same thing for all ; lisps and be happy with even that small improvement in regression time for ; GCL. (Note that by using make with ; LISP='gcl -eval "(defparameter user::*fast-acl2-gcl-build* t)"' ; one can get a faster build, without this on-the-fly compilation, with very ; little performance penalty at runtime. Something like this could be done ; with any Common Lisp, but there is only a point in GCL; see above.) (and #+gcl (not user::*fast-acl2-gcl-build*) boot-strap-flg) ; delete for build speedup (see above) (and (not *inside-include-book-fn*) (default-compile-fns wrld))) (dolist (def new-defs) (assert$ def ; Install-defs-for-add-trip can't make def nil, since either we are in the ; boot-strap or else the value of 'ld-skip-proofsp is not 'include-book. (let ((name (cond ((eq (car def) 'defun) (cadr def)) ((eq (car def) 'oneify-cltl-code) (car (caddr def))) (t (error "Implementation error: ~ unexpected form in ~ add-trip, ~x0" def))))) (eval `(compile ',name))))))))) ((defstobj defabsstobj) ; (cddr trip) is of one of the forms ; (DEFSTOBJ name the-live-name init raw-defs template axiomatic-defs) or ; (DEFABSSTOBJ name the-live-name init raw-defs event axiomatic-defs). ; Init is a form to eval to obtain the initial setting for the live variable. ; Each def in raw-defs and in axiomatic-defs is of the form (name args dcl ; body), where dcl may be omitted. We make a function or macro definition for ; each raw-def, and we make a defun for the oneification of each axiomatic-def. (let ((absp (eq (car (cddr trip)) 'defabsstobj)) (name (nth 1 (cddr trip))) (the-live-name (nth 2 (cddr trip))) (init (nth 3 (cddr trip))) (raw-defs (nth 4 (cddr trip))) (template-or-event (nth 5 (cddr trip))) (ax-defs (nth 6 (cddr trip))) (new-defs ; We avoid "undefined function" warnings by Allegro during compilation by ; defining all the functions first, and compiling them only after they have all ; been defined. But we go further; see the comment in the binding of new-defs ; in the previous case. nil)) (maybe-push-undo-stack 'defconst the-live-name) (maybe-push-undo-stack 'defconst '*user-stobj-alist*) ; See the comment below, just above where we formerly set the symbol-value of ; name. If we re-install that code, then the next line of code also needs to ; be re-installed. ; (maybe-push-undo-stack 'defconst name) (install-for-add-trip `(defparameter ,the-live-name ,init) nil t) ; Memoize-flush expects the variable (st-lst name) to be bound. We take care ; of that directly here. We see no need to involve install-for-add-trip or the ; like. #+hons (let ((var (st-lst name))) (or (boundp var) (eval `(defg ,var nil)))) ; As with defconst we want to make it look like we eval'd this defstobj or ; defabsstobj in raw lisp, so we set up the redundancy stuff: (setf (get the-live-name 'redundant-raw-lisp-discriminator) (cond (absp template-or-event) (t (list* 'defstobj (car template-or-event) (cadr template-or-event) (caddr template-or-event) (if (sixth template-or-event) (congruent-stobj-rep-raw (sixth template-or-event)) name))))) ; At one point we executed the following form. But now we see that this is not ; necessary, since trans-eval binds stobj names anyhow using *user-stobj-alist* ; and even acl2-raw-eval uses *user-stobj-alist* to bind stobj names. If the ; following code is re-installed (uncommented), then also re-install the code ; (maybe-push-undo-stack 'defconst name) above. ; (setf (symbol-value name) (symbol-value the-live-name)) ; The following assignment to *user-stobj-alist* is structured to keep ; new ones at the front, so we can more often exploit the optimization ; in put-assoc-eq-alist. (setq *user-stobj-alist* (cond ((assoc-eq name *user-stobj-alist*) ; This is a redefinition! We'll just replace the old entry. (put-assoc-eq name (symbol-value the-live-name) *user-stobj-alist*)) (t (cons (cons name (symbol-value the-live-name)) *user-stobj-alist*)))) ; We eval and compile the raw lisp definitions first, some of which may be ; macros (because :inline t was supplied for defstobj, or because we are ; handling defabsstobj), before dealing with the *1* functions. (dolist (def raw-defs) (cond ((and boot-strap-flg (not (global-val 'boot-strap-pass-2 wrld))) ; During the first pass of initialization, we insist that every function ; defined already be defined in raw lisp. During pass two we can't expect this ; because there may be LOCAL defuns that got skipped during compilation and the ; first pass. (or (fboundp (car def)) (interface-er "~x0 is not fboundp!" (car def)))) ((equal *main-lisp-package-name* (symbol-package-name (car def))) (interface-er "It is illegal to redefine a function in the main Lisp ~ package, such as ~x0!" (car def))) ; We don't do maybe-push-undo-stack for defuns (whether inlined or not) under ; the defstobj or defabsstobj CLTL-COMMAND, because we did it for their ; defuns. (t (let ((def (cond (absp (cons 'defmacro def)) ((member-equal *stobj-inline-declare* def) ; We now handle the case where we are going to inline the function calls by ; defining the function as a defabbrev. Note that this is allowed for ; access/update/array-length functions for stobjs, but only for these, where ; speed is often a requirement for efficiency. (cons 'defabbrev (remove-stobj-inline-declare def))) (t (cons 'defun def))))) (setq new-defs (cons def new-defs)))))) (dolist (def ax-defs) (setq new-defs (cons (list* 'oneify-cltl-code :logic def name) new-defs))) (setq new-defs ; We reverse new-defs because we want to be sure to define the *1* ; defs after the raw Lisp defs (which may be macros, because of :inline). (nreverse new-defs)) (install-defs-for-add-trip new-defs nil wrld (not boot-strap-flg) t) (when (and (eq (f-get-global 'compiler-enabled *the-live-state*) t) (not *inside-include-book-fn*) (default-compile-fns wrld)) (dolist (def new-defs) (assert$ ; Install-defs-for-add-trip can't make def nil, since the value of ; 'ld-skip-proofsp is not 'include-book. def (let ((name (cond ((or (eq (car def) 'defun) (eq (car def) 'defabbrev) (eq (car def) 'defmacro)) (cadr def)) ((eq (car def) 'oneify-cltl-code) (car (caddr def))) (t (error "Implementation error: ~ unexpected form in add-trip, ~x0" def))))) ; CMUCL versions 18e and 19e cannot seem to compile macros at the top level. ; Email from Raymond Toy on June 9, 2004 suggests that this appears to be a bug ; that exists in CMUCL 18e sources. We'll thus give special treatment to any ; version 18 or 19 of CMUCL, but we'll avod that for CMUCL version 20, since ; 20D at least can compile macros. #+(and cmu (or cmu18 cmu19)) (cond ((and (not (eq (car def) 'defabbrev)) (not (eq (car def) 'defmacro))) (eval `(compile ',name)))) #-(and cmu (or cmu18 cmu19)) (eval `(compile ',name)))))))) (defpkg (maybe-push-undo-stack 'defpkg (cadr (cddr trip))) (eval (cons 'defpkg (cdr (cddr trip))))) (defconst ; Historical remark on defconstant. ; In the beginning we supported defconstant. We changed to ; defparameter and then changed to defconst. As things stand now, ; ACL2 supports defconst, which has the same effect at the raw lisp ; level (i.e., the cltl-command) as defparameter, and in addition ; causes proclaim-file to exectute an appropriate proclamation for the ; parameter, knowing as we do that it is really constant. Here are ; some historical remarks that explain why we have gone down this ; path. ; "Currently we turn defconstants into defparameters at the raw Lisp ; level (that is, the cltl-command for defconstant is a defparameter). ; However, we have begun to contemplate alternatives, as we now ; explain. ; We have run into the following problem with defconstant: the ; compiler won't let us compile certified books containing defconstant ; forms because it thinks that constants are special variables ; (because that is what the defparameter cltl-command does). What can ; we do about this problem? One idea was to temporarily redefine ; defconstant to be defparameter (during the compilation done by ; certify-book), but macrolet has only lexical scope, and anyhow Boyer ; says that it's illegal to redefine a Common Lisp function (as we did ; using setf, macro-function, and unwind-protect). ; Another possibilty is to change defconstant-fn so that it really ; does create defconstants. But the reason we use defparameter now is ; that when we undo we need to unbind (because we're always checking ; to see if something is already bound), and we can't unbind a ; constant. ; Why not just eliminate defconstant in favor of defparameter ; everywhere? This is very appealing, especially because defconstant ; is inherently not amenable to undoing. But, Boyer thinks that when ; you defconstant something to a value that is a fixnum, then the ; compiler knows it's a fixnum. This could be very important for ; speed in type-set reasoning. Without the consideration of ; arithmetic, Schelter thinks that we're only paying the price of two ; memory references for defparameter vs. one for defconstant; but a ; factor of 80 or so seems like too high a price to pay. ; So, how about allowing both defconstant and defparameter, but not ; allowing any undoing back past a defconstant? After all, we already ; have a notion of not undoing into the system initialization, so ; we're just talking about a very reasonable extension of that ; protocol. One problem with this approach is that certify-book ; currently does an include-book after a ubt, and this ubt would ; probably fail. But perhaps we can force this to work. The user ; could then develop his work using defparameter, but certify the ; final "toothbrush" book using defconstant. Perhaps defconst would ; be a convenient macro that could be redefined so as to be one or the ; other of defparameter or defconstant. With this approach it would ; probably be useful to require answering a query in order to execute ; a defconstant. ; Another option would be to have acl2::defconstant be distinct from ; lisp::defconstant, but as Boyer points out, this violates our desire ; to have such Lisp primitives available to the user that he can count ; on. Or, we could define a new package that's just like the acl2 ; package but doesn't import defconstant. But note that ; (symbol-package 'defconstant) would create different answers in the ; ACL2 package than in this package -- ouch!" ; Note: (cddr trip) here is (defconst var form val). (cond (boot-strap-flg (or (boundp (cadr (cddr trip))) (interface-er "~x0 is not boundp!" (cadr (cddr trip))))) ((equal *main-lisp-package-name* (symbol-package-name (cadr (cddr trip)))) (interface-er "It is illegal to redefine a defconst in ~ the main Lisp package, such as ~x0!" (cadr (cddr trip)))) (t (maybe-push-undo-stack 'defconst (cadr (cddr trip))) ; We do not want to eval (defconst var form) here because that will recompute ; val. But we make raw Lisp look like it did that. (setf (get (cadr (cddr trip)) 'redundant-raw-lisp-discriminator) (list* 'defconst (caddr (cddr trip)) ; form (cadddr (cddr trip)))) ; val (install-for-add-trip `(defparameter ,(cadr (cddr trip)) ',(cadddr (cddr trip))) nil t))) (cond ((doc-stringp (and (cadr (cddr trip)) (documentation (cadr (cddr trip)) 'variable))) (setf (documentation (cadr (cddr trip)) 'variable) nil)))) (defmacro (cond (boot-strap-flg (or (fboundp (cadr (cddr trip))) (interface-er "~x0 is not fboundp!" (cadr (cddr trip))))) ((equal *main-lisp-package-name* (symbol-package-name (cadr (cddr trip)))) (interface-er "It is illegal to redefine a macro in the ~ main Lisp package, such as ~x0!" (cadr (cddr trip)))) (t (maybe-push-undo-stack 'defmacro (cadr (cddr trip))) (install-for-add-trip (cddr trip) nil t))) (cond ((doc-stringp (documentation (cadr (cddr trip)) 'function)) (setf (documentation (cadr (cddr trip)) 'function) nil)))) (attachment ; (cddr trip) is produced by attachment-cltl-cmd (dolist (x (cdr (cddr trip))) (let ((name (if (symbolp x) x (car x)))) #+hons (push name *defattach-fns*) (maybe-push-undo-stack 'attachment name) (install-for-add-trip (cond ((symbolp x) (set-attachment-symbol-form x nil)) (t (set-attachment-symbol-form name (cdr x)))) nil t)))) #+hons (memoize (maybe-push-undo-stack 'memoize (cadr (cddr trip))) (let* ((tuple (cddr trip)) (cl-defun (nth 5 tuple))) (assert$ cl-defun (memoize-fn (nth 1 tuple) :condition (nth 2 tuple) :inline (nth 3 tuple) :trace (nth 4 tuple) :cl-defun cl-defun :formals (nth 6 tuple) :stobjs-in (nth 7 tuple) :stobjs-out (nth 8 tuple) :commutative (nth 10 tuple) :forget (nth 11 tuple) :memo-table-init-size (nth 12 tuple) :aokp (nth 13 tuple))))) #+hons (unmemoize (maybe-push-undo-stack 'unmemoize (cadr (cddr trip))) (unmemoize-fn (cadr (cddr trip)))))))) ; Finally, we make sure always to leave the *current-acl2-world-key* as the ; first property on the symbol-plist of the symbol. (let ((temp (get (car trip) *current-acl2-world-key*)) (plist (symbol-plist (car trip)))) (cond ((and temp (not (eq (car plist) *current-acl2-world-key*))) (setf (symbol-plist (car trip)) (cons *current-acl2-world-key* (cons temp (remove-current-acl2-world-key plist)))))))) (defun-one-output undo-trip (world-name world-key trip) ; Undo-trip is the function that removes from the ``real'' Common Lisp ; the things installed by add-trip. It works merely by popping the ; appropriate stacks. (setf (get (car trip) world-key) (destructive-pop-assoc (cadr trip) (get (car trip) world-key))) (cond ((and (eq world-name 'current-acl2-world) (eq (car trip) 'cltl-command) (eq (cadr trip) 'global-value) (consp (cddr trip))) (case (car (cddr trip)) (defuns ; Note that :inlined defstobj functions as well as defabsstobj exported ; functions are processed by eval-event-lst as though they are ordinary defuns, ; even though they correspond to macros in raw Lisp (defined by defabbrev and ; defmacro, respectively). We are relying on the fact that ; maybe-push-undo-stack handled defun and defmacro the same, so that the form ; eveluated by maybe-pop-undo-stack will be appropriate even though the ; "function" is actually a macro. (dolist (tuple (cdddr (cddr trip))) (maybe-pop-undo-stack (car tuple)))) ((defstobj defabsstobj) (let ((name (nth 1 (cddr trip))) (the-live-name (nth 2 (cddr trip)))) (maybe-pop-undo-stack name) (maybe-pop-undo-stack '*user-stobj-alist*) (maybe-pop-undo-stack the-live-name))) (defpkg nil) ((defconst defmacro #+hons memoize #+hons unmemoize) (maybe-pop-undo-stack (cadr (cddr trip)))) (attachment ; (cddr trip) is produced by attachment-cltl-cmd (let ((lst (cdr (cddr trip)))) (dolist (x lst) (let ((name (if (symbolp x) x (car x)))) (maybe-pop-undo-stack name))))) (otherwise nil))))) (defun-one-output flush-trip (name world-key trip) (remprop (car trip) world-key) (cond ((and (eq name 'current-acl2-world) (eq (car trip) 'cltl-command) (eq (cadr trip) 'global-value) (consp (cddr trip))) (case (car (cddr trip)) (defuns ; Note that :inlined stobj functions are handled properly here; see the ; corresponding comment in undo-trip. (dolist (tuple (cdddr (cddr trip))) (flush-undo-stack (car tuple)))) ((defstobj defabsstobj) (let ((name (nth 1 (cddr trip))) (the-live-name (nth 2 (cddr trip)))) (flush-undo-stack name) (flush-undo-stack '*user-stobj-alist*) (flush-undo-stack the-live-name))) (defpkg nil) ((defconst defmacro #+hons memoize #+hons unmemoize) (flush-undo-stack (cadr (cddr trip)))) (attachment ; (cddr trip) is produced by attachment-cltl-cmd (let ((lst (cdr (cddr trip)))) (dolist (x lst) (let ((name (if (symbolp x) x (car x)))) (flush-undo-stack name))))) (otherwise nil))))) (defvar *bad-wrld*) (defun check-acl2-world-invariant (wrld old-wrld) ; Old-wrld is the world currently installed under 'current-acl2-world. ; Wrld is a world we are trying to install there. We check that ; old-world is in fact the current global value of 'current-acl2- ; world. We have gotten out of sync on this once or twice. It is ; cheap to check and pernicious to track down. (cond ((not (eq old-wrld (w *the-live-state*))) (setq *bad-wrld* wrld) (interface-er "Extend-world1 or rollback-world1 has been asked to install ~ a world at a moment when the current global value of ~ 'current-acl2-world was not the installed world! The ~ world we were asked to install may be found in the variable ~ *bad-wrld*.")))) (defparameter *known-worlds* nil) (defun update-wrld-structures (wrld state) (install-global-enabled-structure wrld state) (recompress-global-enabled-structure 'global-arithmetic-enabled-structure wrld) (recompress-stobj-accessor-arrays (strip-cars *user-stobj-alist*) wrld) #+hons (update-memo-entries-for-attachments *defattach-fns* wrld state)) (defun-one-output extend-world1 (name wrld) ; Wrld must be a world that is an extension of the world currently ; installed under name. ; Warning: Even though this program does not take state as an ; argument, it has the effect of smashing the value of the live state ; global 'current-acl2-world if name is 'current-acl2-world. In ; particular, we maintain the invariant that the live global value of ; 'current-acl2-world is always the world installed under that name. ; If you don't want these changes to occur to your state, don't call ; this program! (let ((pair (get name 'acl2-world-pair)) old-wrld world-key new-trips) (cond ((null pair) (setq pair (cons nil (if (eq name 'current-acl2-world) *current-acl2-world-key* (gensym)))) (pushnew name *known-worlds*) (cond ((eq name 'current-acl2-world) (f-put-global 'current-acl2-world nil *the-live-state*))) (setf (get name 'acl2-world-pair) pair))) (setq old-wrld (car pair)) (setq world-key (cdr pair)) ; Pair is of the form (old-wrld . world-key) and means that the world ; currently installed under name is old-wrld and its properties are ; stored at world-key. (cond ((eq name 'current-acl2-world) (check-acl2-world-invariant wrld old-wrld))) ; We now scan down the about-to-be-installed world and push onto the ; temporary new-trips the triples that constitute the extension. If ; we fail to find the old world, we will cause a hard error. It may look ; like we are doing this scan to guarantee that wrld is an extension. ; Were that the reason, we would do this as we installed the properties. ; No, the real reason we do this scan is so that we can collect, in reverse ; order, the triples we must install. The order in which we push the ; values into the property lists is important! (do ((tl wrld (cdr tl))) ((equal tl old-wrld)) ; best to avoid eq; see comment in retract-world1 (cond ((null tl) (setq *bad-wrld* wrld) (er hard 'extend-world1 "Extend-world1 was called upon to ``extend'' ~x0. But ~ the world supplied to extend-world1, which is now the ~ value of the Lisp global *bad-wrld*, is not an ~ extension of the current ~x0. The alist corresponding ~ to the current ~x0 may be obtained via ~x1. No ~ properties were modified -- that is, the symbol-plists ~ still reflect the pre-extend-world1 ~x0." name `(car (get ',name 'acl2-world-pair)))) (t (push (car tl) new-trips)))) (let ((state *the-live-state*)) ; We bind state only so our use of acl2-unwind-protect below isn't so odd ; looking. Logically the body never signals an error, but if an abort ; occurs, we will do recover-world for cleanup. (acl2-unwind-protect "extend-world1" (value ; It is a bit unfortunate to use with-more-warnings-suppressed below, since we ; have such a call in LP. However, this is how we see a way to suppress ; complaints about undefined functions during the build, without suppressing ; compiler warnings entirely during the build. Note that with-compilation-unit ; does not always defer warnings for calls of the compiler in general -- at ; least, we have seen this with CCL and Allegro CL -- but only for calls of ; compile-file. (with-more-warnings-suppressed ; Observe that wrld has recover-world properties (a) and (b). (a) at ; the time of any abort during this critical section, every symbol ; that may have a world-key property is in wrld (because the only ; symbols with a world-key property initially are those in old-wrld, ; wrld is an extension of old-wrld, and during the critical section we ; add world-key properties just to symbols in wrld); and (b): every ; symbol in old-wrld is a symbol in wrld (because wrld is an extension ; of old-wrld). (Of course, by "symbol in" here we mean "symbol ; occuring as the car of an element".) (dolist (trip new-trips) (add-trip name world-key trip)) (setf (car pair) wrld) (cond ((eq name 'current-acl2-world) (f-put-global 'current-acl2-world wrld *the-live-state*) (update-wrld-structures wrld state))))) (recover-world 'extension name old-wrld wrld nil) ; Observe that wrld has recover-world properties (a) and (b). (a) at ; the time of any abort during this critical section, every symbol ; that may have a world-key property is in wrld (because the only ; symbols with a world-key property initially are those in old-wrld, ; wrld is an extension of old-wrld, and during the critical section we ; add world-key properties just to symbols in wrld); and (b): every ; symbol in old-wrld is a symbol in wrld (because wrld is an extension ; of old-wrld). (Of course, by "symbol in" here we mean "symbol ; occuring as the car of an element".) state) ; The acl2-unwind-protect returns (mv nil x *the-live-state*), for some x. ; All three values are ignored. wrld))) (defun-one-output retract-world1 (name wrld) ; Wrld must be a world that is a retraction of the world currently installed ; under name. ; Warning: Even though this program does not take state as an argument, it has ; the effect of smashing the value of the live state global 'current-acl2-world ; if name is 'current-acl2-world. In particular, we maintain the invariant ; that the live global value of 'current-acl2-world is always the world ; installed under that name. We also maintain the invariant that the binding ; of 'current-package is a known package, by setting 'current-package to "ACL2" ; if we have to. If you don't want these changes to occur to your state, don't ; call this program! (let ((pair (get name 'acl2-world-pair)) old-wrld world-key) (cond ((null pair) (setq pair (cons nil (if (eq name 'current-acl2-world) *current-acl2-world-key* (gensym)))) (pushnew name *known-worlds*) (cond ((eq name 'current-acl2-world) (f-put-global 'current-acl2-world nil *the-live-state*))) (setf (get name 'acl2-world-pair) pair))) (setq old-wrld (car pair)) (setq world-key (cdr pair)) ; Pair is of the form (old-wrld . world-key) and means that the world currently ; installed under name is old-wrld and its properties are stored at world-key. (cond ((eq name 'current-acl2-world) (check-acl2-world-invariant wrld old-wrld))) (let ((state *the-live-state*) (pkg (current-package *the-live-state*))) (acl2-unwind-protect "retract-world1" (value (progn ; We now scan down old-wrld until we get to wrld, doing a pop for each triple ; in the initial segment of old-wrld. Note that we do not do the pops in the ; reverse order (as we did the pushes). It doesn't matter. All that matters ; is that we one pop for each push that was done. (do ((tl old-wrld (cdr tl))) ((equal tl ; At one time we used eq here. But old-wrld and wrld are equal, but not eq, ; when retract-world1 is called in the following example. ; (defun f1 (x) x) ; (defun f2 (x) x) ; :ubt! f1 ; (defun f1 (x) x) ; :oops wrld)) (cond ((null tl) (setq *bad-wrld* wrld) (er hard 'retract-world1 "Retract-world1 was called upon to ``retract'' ~ ~x0. But the world supplied to retract-world1, ~ which is now the value of the Lisp global ~ variable *bad-wrld*, is not a retraction of the ~ currently installed ~x0. The alist corresponding ~ to the current ~x0 may be obtained via ~x1. ~ Unfortunately, this problem was not discovered ~ until all of the properties in ~x0 were removed. ~ Those properties can be restored via ~ (recover-world)." name `(car (get ',name 'acl2-world-pair)))) (t (undo-trip name world-key (car tl))))) (setf (car pair) wrld) (cond ((eq name 'current-acl2-world) (f-put-global 'current-acl2-world wrld *the-live-state*) (cond ((not (find-non-hidden-package-entry (current-package *the-live-state*) (known-package-alist *the-live-state*))) ; Note: Known-package-alist returns the new known packages because of the setf ; above! (f-put-global 'current-package "ACL2" *the-live-state*))) (update-wrld-structures wrld state))))) (recover-world 'retraction name old-wrld old-wrld pkg) ; Note that old-wrld has recover-world properties (a) and (b). (a) At the time ; of any abort during this critical section, every symbol that may possibly ; have the world-key property occurs as a car of some element of old-wrld (at ; the beginning of this operation the only symbols with the world-key property ; are in old-wrld and during the critical section we may only remove the ; property). (b) Every symbol in old-wrld is in old-wrld. Note that it is not ; important whether wrld is actually a retraction, because we are just removing ; world-key properties. state) wrld))) (defun-one-output recover-world1 (wrld saved-wrld ans) ; This is like (revappend wrld ans) except that we cons successive tails of ; wrld onto ans instead of successive elements until wrld is either saved-wrld ; or nil. Thus, assuming saved-wrld is nil, if you start with wrld = '(trip3 ; trip2 trip1) you end up with ans = '((trip1) (trip2 trip1) (trip3 trip2 ; trip1)). By scanning ans you will see the successive cdrs of world, starting ; with the shortest. (cond ((eq wrld saved-wrld) ans) ((null wrld) (error "Implementation error in recover-world1")) (t (recover-world1 (cdr wrld) saved-wrld (cons wrld ans))))) (defun-one-output recover-world (op name old-wrld universe pkg) ; If this function is called then an extension or retraction operation (op) was ; aborted. The arguments tell us how to restore the world to the configuration ; it had when the aborted operation was initiated. Toward that end, the two ; operations execute this form on the cleanup1 side of acl2-unwind-protects, ; after saving in locals the name and world before they start making changes to ; the symbol- plists. Our recovery mechanism has two steps. First, we throw ; away all properties stored under the name in question. Second, we extend ; that empty world to the saved one. The extension part is easy. ; But how do we "throw away all properties" stored under a given name? One way ; is to get the world key, world-key, associated with the name, and then visit ; each CLTL symbol, sym, and do (remprop sym world-key). But sweeping through ; all symbols seems to be slow. So instead we enforce the discipline of having ; a "universal list" of all the symbols which might, remotely, have the ; world-key property. This list is actually a list of triples (it is, in fact, ; always one of the two worlds involved in the aborted operation) and we visit ; the symbol in each car of each triple. ; So much for spoon-fed background information. Name is the name of the world ; whose installation was aborted. We know that the cdr of the 'acl2-world-pair ; property of name is some uninterned symbol, world-key, at which all the ; properties for this world are stored. Old-wrld is the alist of triples that ; was actually installed at the time the aborted operation was initiated -- and ; is thus the world we are going to re-install. And universe is a list of ; pairs (triples actually) with the following two properties: ; (a) at the time of any abort during the critical section, each symbol having ; the world-key property occurs as the car of some pair in universe, and (b) ; each symbol occuring as the car of some pair in old-wrld occurs as the car of ; a pair in universe. ; The latter property is necessary to ensure that we can recover from an ; aborted attempt to recover. ; In addition, if operation is 'retraction, then pkg is the current- package at ; the inception of the operation. If operation is not 'retraction, pkg is ; irrelevant. (let* ((pair (get name 'acl2-world-pair)) (world-key (cdr pair)) #+hons *defattach-fns* ; needs to be bound, but not truly used (*in-recover-world-flg* t)) ; The *in-recover-world-flg* is used by the raw lisp implementation of defpkg. ; How is defpkg called from within this function? Add-trip, above, is the ; program used to add the triples in old-wrld to the raw property lists. It ; notes CLTL-COMMAND triples and executes the appropriate Common Lisp to cause ; the raw defuns, defmacros, constants, and packages to come into existence. ; So we execute defpkg when we store a CLTL-COMMAND property with val (DEFPKG ; ...). Normally, defpkg unbinds all the symbols in the dual package (where ; state globals are actually stored). But we can't afford to do that if we ; are recovering the world (because state globals are unaffected by world ; operations). (when (null pair) (er hard 'recover-world "There is no acl2-world-pair stored on ~x0, which is the name of ~ the world we are supposed to recover." name)) (fmt1 "Flushing current installed world.~%" nil 0 (standard-co *the-live-state*) *the-live-state* nil) (dolist (trip universe) (flush-trip name world-key trip)) (let* ((checkpoint-world-len-and-alist (checkpoint-world-len-and-alist)) (chkpt-alist (cddr checkpoint-world-len-and-alist)) (start-wrld (and (eq name 'current-acl2-world) *checkpoint-world-len-and-alist-stack* (let ((chkpt-wrld (car checkpoint-world-len-and-alist)) (chkpt-len (cadr checkpoint-world-len-and-alist)) (old-wrld-len (length old-wrld))) (and (<= chkpt-len old-wrld-len) (eq (nthcdr (- old-wrld-len chkpt-len) old-wrld) chkpt-wrld) (let ((universe-len (length universe))) (and (<= chkpt-len universe-len) (eq (nthcdr (- universe-len chkpt-len) universe) chkpt-wrld) chkpt-wrld))))))) (dolist (x chkpt-alist) (setf (get (car x) world-key) (copy-alist (cdr x)))) (cond ((eq name 'current-acl2-world) (f-put-global 'current-acl2-world start-wrld *the-live-state*))) (setf (car pair) start-wrld) (fmt1 "Reversing the new world.~%" nil 0 (standard-co *the-live-state*) *the-live-state* nil) (let ((rwtls (recover-world1 old-wrld start-wrld nil)) (*inside-include-book-fn* ; We defeat the special hash table processing done by the install-for-add-trip* ; functions. (This binding might not be necessary, but it seems safe.) nil)) (fmt1 "Installing the new world.~%" nil 0 (standard-co *the-live-state*) *the-live-state* nil) ; It is a bit unfortunate to use with-more-warnings-suppressed below, since we ; have such a call in LP. However, this is how we see a way to suppress ; complaints about undefined functions during the build, without suppressing ; compiler warnings entirely during the build. Note that with-compilation-unit ; does not always defer warnings for calls of the compiler in general -- at ; least, we have seen this with CCL and Allegro CL -- but only for calls of ; compile-file. (with-more-warnings-suppressed (do ((tl rwtls (cdr tl))) ((null tl)) (add-trip name world-key (caar tl)) (cond ((eq name 'current-acl2-world) (f-put-global 'current-acl2-world (car tl) *the-live-state*))) (setf (car pair) (car tl))))) (cond ((eq name 'current-acl2-world) (cond ((eq op 'retraction) (f-put-global 'current-package pkg *the-live-state*))) #+hons (setq *defattach-fns* :clear) (update-wrld-structures old-wrld *the-live-state*)))))) ; VIRGINITY ; We do not want the ACL2 user to define any symbol that already has ; any sort of definition. (defun-one-output virginp (name new-type) (and (symbolp name) (if (member-eq new-type '(function macro constrained-function t)) (not (or (fboundp name) (macro-function name) (special-form-or-op-p name))) t) (if (member-eq new-type '(const stobj stobj-live-var t)) (not (boundp name)) t))) (defun-one-output chk-virgin2 (name new-type wrld) (cond ((virginp name new-type) nil) ((global-val 'boot-strap-flg wrld) (setf (get name '*predefined*) t)) ; A name regains its true virginity the moment we decide to give it a ; 'redefined property, which only happens just after the user has said that ; it's OK to redefine it. ((getprop name 'redefined nil 'current-acl2-world wrld) nil) (t (let ((reason (cond ((not (symbolp name)) "it is not a symbol") ((member-eq new-type '(function macro constrained-function t)) (cond ((special-form-or-op-p name) "it is a special form") ((macro-function name) "it has a macro definition") ((fboundp name) "it has a function definition") (t "there is an inconsistency in the definition of ~ virginp and chk-virgin2"))) ((member-eq new-type '(const stobj stobj-live-var t)) (cond ((boundp name) "it has a top level binding") (t "there is an inconsistency in the definition of ~ virginp and chk-virgin2"))) (t "there is an inconsistency in the definition of ~ virginp and chk-virgin2"))) (suggestion (let ((str " If earlier you accidentally made this ~ definition or assignment outside the ACL2 ~ loop, then you might consider exiting the ~ ACL2 loop and executing appropriate ~ Common Lisp to erase those mistakes. ~ This is a potentially perilous act unless ~ you are sure these names were introduced ~ by you and are not involved in any ~ logical code. ~#3~[~/To erase a function ~ or macro definition use (fmakunbound! ~ '~x0). ~]~#4~[~/To erase a variable ~ setting use (makunbound '~x0). ~]")) (cond ((not (symbolp name)) "") ((member-eq new-type '(function macro constrained-function t)) (cond ((special-form-or-op-p name) "") (t str))) (t ; (member-eq new-type '(const stobj ; stobj-live-var t)) str))))) (interface-er "It is illegal to define ~x0 because ~@1 in raw Common Lisp.~@2" name reason suggestion (if (member-eq new-type '(function macro constrained-function t)) 1 0) (if (member-eq new-type '(const stobj stobj-live-var t)) 1 0)))))) ; PACKAGE VIRGINITY (defun-one-output chk-package-reincarnation-import-restrictions2 (name proposed-imports) ; This routine returns nil or causes a hard error. It is used in the ; implementation of the logical function chk-package-reincarnation-import- ; restrictions, which axiomatically always returns t but may cause this hard ; error (which can be thought of as a resource error). ; Note: The "2" in the name stems from chk-virgin2, which plays a similar role ; in chk-virgin. Chk-virgin1 has been lost in the mist of time, but ; chk-package-reincarnation-import-restrictions1 has never existed! (let ((pkg (find-package name)) (temp (find-package-entry name *ever-known-package-alist*))) (cond (pkg (cond (temp (check-proposed-imports name temp proposed-imports) nil) ((member-equal name *defpkg-virgins*) nil) (t ; The package has been built in this Common Lisp but not by defpkg. (interface-er "It is illegal to defpkg ~x0 because a package of that name already ~ exists in this lisp." name)))) (t ; The package has never been built in this Common Lisp. nil)))) ; ACL2 INITIALIZATION ROUTINES #+gcl (defvar user::*acl2-keep-tmp-files* nil) (defun-one-output enter-boot-strap-mode (system-books-dir operating-system) ; If we interrupted an earlier initialization, the following form will undo it. ; This will set the *acl2-unwind-protect-stack* to nil because *ld-level* is ; 0 at the top. (acl2-unwind *ld-level* nil) ; Now provide a frame into which the set-w can push its acl2-unwind-protect ; forms. An abort of the set-w will leave the forms in the frame so that the ; above acl2-unwind will undo them upon the next initialization attempt. But ; if the set-w is successful, it will leave the then empty frame on the stack. (push nil *acl2-unwind-protect-stack*) (set-w 'retraction nil *the-live-state*) (do-symbols (sym (find-package "ACL2_GLOBAL_ACL2")) (makunbound sym)) (do-symbols (sym (find-package (concatenate 'string *global-package-prefix* *main-lisp-package-name*))) (makunbound sym)) (do-symbols (sym (find-package "ACL2_GLOBAL_KEYWORD")) (makunbound sym)) (initialize-state-globals) ; The following patch for save-gprof.lsp may be more heavy-handed than ; necessary, because maybe we don't need to keep all TMP* files. See also ; "Note: temporary files" in save-gprof.lsp. ; If we want to let the user set other variables, we could replace ; user::*acl2-keep-tmp-files* with a variable whose value associates state ; global symbol names with initial values. But then we will need to check that ; none is untouchable, and we will need to make a corresponding change in ; save-gprof.lsp. #+gcl (f-put-global 'keep-tmp-files user::*acl2-keep-tmp-files* *the-live-state*) (f-put-global 'global-enabled-structure (initial-global-enabled-structure "ENABLED-ARRAY-") *the-live-state*) (f-put-ld-specials *initial-ld-special-bindings* *the-live-state*) ; The next set-w will avail itself of the empty frame left above. (set-w 'extension (primordial-world operating-system) *the-live-state*) ; Set the system books directory now that the operating-system has been defined ; (needed by pathname-os-to-unix). (cond (system-books-dir (let ((dir (unix-full-pathname (cond ((symbolp system-books-dir) (symbol-name system-books-dir)) ((stringp system-books-dir) system-books-dir) (t (er hard 'initialize-acl2 "Unable to complete initialization, because the ~ supplied system books directory, ~x0, is not a ~ string." system-books-dir)))))) (f-put-global 'system-books-dir (canonical-dirname! (maybe-add-separator dir) 'enter-boot-strap-mode *the-live-state*) *the-live-state*))) (t (f-put-global 'system-books-dir (concatenate 'string (canonical-dirname! (our-pwd) 'enter-boot-strap-mode *the-live-state*) #-:non-standard-analysis "books/" #+:non-standard-analysis "books/nonstd/") *the-live-state*))) ; Inhibit proof-tree output during the build, including pass-2 if present. (f-put-global 'inhibit-output-lst '(proof-tree) *the-live-state*) ; We now pop the empty frame. There is no way this acl2-unwind will do any ; real work because only an abort would leave stuff in the frame and an abort ; would prevent us from getting here. Note that in the scheme of things, ; namely within the control structure of initialize-acl2, it is strictly ; unnecessary for us to pop this empty frame: each LD called by initialize-acl2 ; will unwind the stack back to nil. But I do it here out of politeness: the ; stack started empty and ends that way. (acl2-unwind *ld-level* nil)) (defun-one-output move-current-acl2-world-key-to-front (wrld) ; This program sweeps the world and makes sure that the current acl2 world key ; is the first property on every property list upon which it is found. We try ; to maintain that invariant in set-w where we always move the property up when ; we mess with a symbol's plist. Of course, one must then wonder why this ; program is ever useful. The reason is that in some lisps, e.g., AKCL, when ; you ask for the symbol-name of a symbol it has the side-effect of storing the ; string on the plist for future use. Thus, for example, during booting of ; ACL2 we keep the world key at the front but then when we print the name of ; the event we accidentally cover the world key up with a SYSTEM:PNAME ; property. This doesn't happen for every name. Apparently for most we access ; the symbol-name during the processing and cause the property to come into ; existence and then store our world key in front of it. But for some names we ; don't, apparently, ever access the symbol-name during processing and then our ; world key is covered up the first time the name is printed by ACL2. Among ; the names so covered up by system properties are the names such as |Make ; RECOGNIZER-TUPLE record| INCLUDE-BOOK-ALIST, CERTIFICATION-TUPLE, ; TYPE-SET-INVERTER-RULES, PROVED-FUNCTIONAL-INSTANCES-ALIST, GENERALIZE-RULES, ; WELL-FOUNDED-RELATION-ALIST, WORLD-GLOBALS, and CHK-NEW-NAME-LST. It is my ; theory that these names are simply never set a second time in booting and ; their initial setting is made before they are first printed. ; In any case, the following sweep takes only a second or two at the end of a ; boot and will make our world key the first property. We hope to keep it that ; way in add-trip, but we cannot guarantee it, since the Lisp system might ; legally at anytime cover it up with some system property. (cond ((null wrld) nil) ((eq (car (symbol-plist (caar wrld))) *current-acl2-world-key*) (move-current-acl2-world-key-to-front (cdr wrld))) (t (let ((temp (get (caar wrld) *current-acl2-world-key*))) (cond (temp (setf (symbol-plist (caar wrld)) (cons *current-acl2-world-key* (cons temp (remove-current-acl2-world-key (symbol-plist (caar wrld))))))))) (move-current-acl2-world-key-to-front (cdr wrld))))) (progn ; Warning: These definitions will replace both the raw Lisp and *1* definitions ; of the first argument of each defun-overrides call. We must ensure that these ; definitions can't be evaluated when proving theorems unless each has ; unknown-constraints and never returns two values for the same input (which is ; automatically taken care of by passing in live states with unknown oracles). (defun-overrides mfc-ts-fn (term mfc state forcep) (mv-let (ans ttree) (mfc-ts-raw term mfc state forcep) (declare (ignore ttree)) ans)) (defun-overrides mfc-ts-ttree (term mfc state forcep) (mfc-ts-raw term mfc state forcep)) (defun-overrides mfc-rw-fn (term obj equiv-info mfc state forcep) (mv-let (ans ttree) (mfc-rw-raw term nil obj equiv-info mfc 'mfc-rw state forcep) (declare (ignore ttree)) ans)) (defun-overrides mfc-rw-ttree (term obj equiv-info mfc state forcep) (mfc-rw-raw term nil obj equiv-info mfc 'mfc-rw state forcep)) (defun-overrides mfc-rw+-fn (term alist obj equiv-info mfc state forcep) (mfc-rw-raw term alist obj equiv-info mfc 'mfc-rw+ state forcep)) (defun-overrides mfc-rw+-ttree (term alist obj equiv-info mfc state forcep) (mv-let (ans ttree) (mfc-rw-raw term alist obj equiv-info mfc 'mfc-rw+ state forcep) (declare (ignore ttree)) ans)) (defun-overrides mfc-relieve-hyp-fn (hyp alist rune target bkptr mfc state forcep) (mfc-relieve-hyp-raw hyp alist rune target bkptr mfc state forcep)) (defun-overrides mfc-relieve-hyp-ttree (hyp alist rune target bkptr mfc state forcep) (mv-let (ans ttree) (mfc-relieve-hyp-raw hyp alist rune target bkptr mfc state forcep) (declare (ignore ttree)) ans)) (defun-overrides mfc-ap-fn (term mfc state forcep) (mfc-ap-raw term mfc state forcep))) (defun-one-output exit-boot-strap-mode () ; We need not unwind the *acl2-unwind-protect-stack* because it must be nil for ; us to have gotten here; had an abort occurred, leaving some frames on the ; stack, we would not get here. The frame we push below is used by the set-w ; and then popped unless an abort occurs. (push nil *acl2-unwind-protect-stack*) (set-w 'extension (end-prehistoric-world (w *the-live-state*)) *the-live-state*) (acl2-unwind *ld-level* nil) (f-put-global 'inhibit-output-lst nil *the-live-state*) (f-put-global 'slow-array-action :warning *the-live-state*) ; This is where to start up proof trees if we ever decide that this should be ; the default. But probably we don't want to do it in MCL. (stop-proof-tree-fn *the-live-state*) (f-put-global 'ld-skip-proofsp nil *the-live-state*) (move-current-acl2-world-key-to-front (w *the-live-state*)) (checkpoint-world1 t (w *the-live-state*) *the-live-state*)) (defun-one-output ld-alist-raw (standard-oi ld-skip-proofsp ld-error-action) `((standard-oi . ,standard-oi) (standard-co . ,*standard-co*) (proofs-co . ,*standard-co*) (current-package . "ACL2") (ld-skip-proofsp . ,ld-skip-proofsp) (ld-redefinition-action . nil) (ld-prompt . ,(if ld-skip-proofsp nil t)) (ld-missing-input-ok . nil) (ld-pre-eval-filter . :all) (ld-pre-eval-print . ,(if ld-skip-proofsp nil t)) (ld-post-eval-print . :command-conventions) (ld-evisc-tuple . ; In order to avoid printing huge forms during initialization when ld-ing files ; that are among the pass two files: ,(abbrev-evisc-tuple *the-live-state*)) (ld-error-triples . t) (ld-error-action . ,ld-error-action) (ld-query-control-alist . t) (ld-verbose . t))) (defun enter-boot-strap-pass-2 () ; We must provide set-w a frame on which to push its undo forms. (push nil *acl2-unwind-protect-stack*) (set-w 'extension (global-set 'boot-strap-pass-2 t (w *the-live-state*)) *the-live-state*) (acl2-unwind *ld-level* nil) ; We use an explicit call of LD-fn to change the defun-mode to :logic just to ; lay down an event in the pre-history, in case we someday want to poke around ; at the boundary. (ld-fn (ld-alist-raw '((logic)) t :error) *the-live-state* nil)) ; The following constant should be changed when we add to the collection of ; files to be processed in :logic default-defun-mode. (defconst *acl2-pass-2-files* ; Note that some books depend on "memoize", "hons", and "serialize", even in ; #-hons. For example, community book books/misc/hons-help.lisp uses hons ; primitives. '("axioms" "memoize" "hons" "serialize" "boot-strap-pass-2" )) ; Next we define fns-different-wrt-acl2-loop-only, used below in ; check-built-in-constants. We base our code loosely on ; functions-defined-in-file in hons-raw.lisp. (defun our-update-ht (key val ht) (let ((old (gethash key ht))) (setf (gethash key ht) (cond ((and (consp old) (eq (car old) :multiple)) (list* (car old) val (cdr old))) (old (list :multiple val)) (t val))))) (defun note-fns-in-form (form ht) ; For every macro and every function defined by form, associate its definition ; with its name in the given hash table, ht. See note-fns-in-files. (and (consp form) (case (car form) ((defun defund defn defproxy defun-nx defun-one-output defstub defmacro defabbrev defun@par defmacro-last defun-overrides) (our-update-ht (cadr form) form ht)) (save-def (note-fns-in-form (cadr form) ht)) (defun-for-state (our-update-ht (defun-for-state-name (cadr form)) form ht)) (define-global (our-update-ht (define-global-name (cadr form)) form ht) (our-update-ht (cadr form) form ht)) ((define-pc-atomic-macro define-pc-bind define-pc-help define-pc-macro define-pc-meta define-pc-primitive) (let ((name (make-official-pc-command (if (eq (car form) 'define-pc-meta-or-macro-fn) (nth 2 form) (nth 1 form))))) (our-update-ht name form ht))) ((mutual-recursion mutual-recursion@par progn) (loop for x in (cdr form) do (note-fns-in-form x ht))) ((encapsulate when) (loop for x in (cddr form) do (note-fns-in-form x ht))) ((skip-proofs local) (note-fns-in-form (cadr form) ht)) (defrec ; pick just one function introduced (our-update-ht (record-changer-function-name (cadr form)) form ht)) ((add-custom-keyword-hint add-macro-alias add-macro-fn #+ccl ccl:defstatic declaim def-basic-type-sets defattach defaxiom defconst defconstant defdoc defg define-@par-macros define-atomically-modifiable-counter define-trusted-clause-processor ; should handle :partial-theory deflabel deflock defparameter defpkg defstruct deftheory defthm defthmd deftype defun-*1* defv defvar eval ; presumably no ACL2 fn or macro underneath eval-when ; presumably no ACL2 fn or macro underneath f-put-global ; in axioms.lisp, before def. of set-ld-skip-proofsp in-package in-theory initialize-state-globals let ; could be arbitrarily complex, but we can only do so much! link-doc-to link-doc-to-keyword logic make-waterfall-parallelism-constants make-waterfall-printing-constants push reset-future-parallelism-variables set-invisible-fns-table set-tau-auto-mode set-waterfall-parallelism setq system-verify-guards table value verify-guards verify-termination-boot-strap) nil) (t (error "Unexpected type of form, ~s. See note-fns-in-form." (car form)))))) (defun note-fns-in-file (filename ht) ; For every macro and every function defined in the indicated file, associate ; its definition with its name in the given hash table, ht. See ; note-fns-in-files. (with-open-file (str filename :direction :input) (let ((avrc (cons nil nil)) x) (loop while (not (eq (setq x (read str nil avrc)) avrc)) do (note-fns-in-form x ht))))) (defun note-fns-in-files (filenames ht loop-only-p) ; For every macro and every function defined in filenames, associate its ; definition with its name in the given hash table, ht. We read each file in ; filenames either with or without feature :acl2-loop-only, according to ; whether loop-only-p is true or false, respectively. (let ((*features* (if loop-only-p (push :acl2-loop-only *features*) (remove :acl2-loop-only *features*)))) (loop for filename in filenames do (note-fns-in-file filename ht)))) (defun raw-source-name-p (filename-without-extension) (let ((len (length filename-without-extension))) (and (<= 4 len) (equal (subseq filename-without-extension (- len 4) len) "-raw")))) ; Set the following to t for a more informative error report. (defvar *check-built-in-constants-debug* nil) (defun fns-different-wrt-acl2-loop-only (acl2-files) ; For each file in acl2-files we collect up all definitions of functions and ; macros, reading each file both with and without feature :acl2-loop-only. We ; return (mv macro-result program-mode-result logic-mode-result), where each of ; macro-result, program-mode-result, and logic-mode-result is a list of ; symbols. Each symbol is the name of a macro, program-mode function, or ; logic-mode function (respectively) defined with feature :acl2-loop-only, ; which has a different (or absent) definition without feature :acl2-loop-only. ; This function is typically called with acl2-files equal to *acl2-files*, in ; the build directory. See the comment about redundant definitions in ; chk-acceptable-defuns-redundancy for a pertinent explanation. (let ((logic-filenames (loop for x in acl2-files when (not (raw-source-name-p x)) collect (concatenate 'string x ".lisp"))) (raw-filenames (loop for x in acl2-files collect (concatenate 'string x ".lisp"))) (ht-raw (make-hash-table :test 'eq)) (ht-logic (make-hash-table :test 'eq)) (macro-result nil) (program-mode-result nil) (logic-mode-result nil) (wrld (w *the-live-state*))) (note-fns-in-files raw-filenames ht-raw nil) (note-fns-in-files logic-filenames ht-logic t) (maphash (lambda (key logic-val) (progn (assert (symbolp key)) (let ((raw-val (gethash key ht-raw))) (or (equal logic-val raw-val) (let ((x (if *check-built-in-constants-debug* (list key :logic logic-val :raw raw-val) key))) (cond ((getprop key 'macro-body nil 'current-acl2-world wrld) (push x macro-result)) ((eq (symbol-class key wrld) :program) (push x program-mode-result)) (t (push x logic-mode-result)))))))) ht-logic) (maphash (lambda (key raw-val) (progn (assert (symbolp key)) (when (not (or (gethash key ht-logic) (assoc key *primitive-formals-and-guards* :test 'eq))) (cond ((getprop key 'macro-body nil 'current-acl2-world wrld) (push key macro-result)) (t (let ((c ; avoid symbol-class (defaults to :program) (getprop key 'symbol-class nil 'current-acl2-world wrld))) (when c (let ((x (if *check-built-in-constants-debug* (list key :raw raw-val) key))) (if (eq c :program) (push x program-mode-result) (push x logic-mode-result)))))))))) ht-raw) (mv macro-result program-mode-result logic-mode-result))) (defun collect-monadic-booleans (fns ens wrld) (cond ((endp fns) nil) ((and (equal (arity (car fns) wrld) 1) (ts= (mv-let (ts ttree) (type-set (fcons-term* (car fns) '(V)) nil nil nil ens wrld nil nil nil) (declare (ignore ttree)) ts) *ts-boolean*)) (cons (car fns) (collect-monadic-booleans (cdr fns) ens wrld))) (t (collect-monadic-booleans (cdr fns) ens wrld)))) (defun check-built-in-constants () ; Certain defconsts are problematic because they build in values that one ; cannot know until the system is built! Getting their values right requires ; some thought or experiment and even then subsequent changes to the system can ; render the values incorrect. To guard against incorrect (obsolete) values ; for these contants, this function causes an error if doesn't like what it ; sees. We document only one such constant, *force-xnume*, which more or less ; describes the situation suffered by all of them. ; Our Code requires that *force-xnume* be the nume of (:EXECUTABLE-COUNTERPART ; FORCE). It would be nice to be able to write: (defconst *force-xnume* ; (fn-rune-nume 'force t t (w state))). But that suffers TWO problems. The ; first is that defconst disallows the use of any variable in the initial value ; form. Thus, state is illegal above. We tried fixing that, in a hypocritical ; way, by allowing it to ourselves in boot-strap even though we denied it to ; the user. But even that doesn't work because of the second problem: The ; first time the defconst is processed, no numes are being allocated because ; everything is done in the :program defun-mode. You might think, therefore, ; we ought to delay the execution of this defconst until after we've done the ; second pass and created the rune in question. But we cannot do that because ; we use *force-xnume* in our code (namely, in ok-to-force) and that function ; can't be defined until *force-xnume* is defined. Thus, this seemingly ; hackish way of doing it, where we actually specify ahead of time which nume ; will be assigned, is not as outlandish as it might at first seem. We check ; that the actual assignment is correct (using this function) after booting. (let ((str "The defconst of ~x0 is ~x1 but should be ~x2. To fix ~ the error, change the offending defconst to the value ~ indicated and rebuild the system. To understand why the code ~ is written this way, see the comment in ~ check-built-in-constants.")) (cond ((not (equal *force-xnume* (fn-rune-nume 'force t t (w *the-live-state*)))) (interface-er str '*force-xnume* *force-xnume* (fn-rune-nume 'force t t (w *the-live-state*))))) (cond ((not (equal *immediate-force-modep-xnume* (fn-rune-nume 'immediate-force-modep t t (w *the-live-state*)))) (interface-er str '*immediate-force-modep-xnume* *immediate-force-modep-xnume* (fn-rune-nume 'immediate-force-modep t t (w *the-live-state*))))) (cond ((not (equal *tau-system-xnume* (fn-rune-nume 'tau-system t t (w *the-live-state*)))) (interface-er str '*tau-system-xnume* *tau-system-xnume* (fn-rune-nume 'tau-system t t (w *the-live-state*))))) (cond ((not (equal *tau-acl2-numberp-pair* (getprop 'acl2-numberp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-acl2-numberp-pair* *tau-acl2-numberp-pair* (getprop 'acl2-numberp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (equal *tau-integerp-pair* (getprop 'integerp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-integerp-pair* *tau-integerp-pair* (getprop 'integerp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (equal *tau-rationalp-pair* (getprop 'rationalp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-rationalp-pair* *tau-rationalp-pair* (getprop 'rationalp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (equal *tau-natp-pair* (getprop 'natp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-natp-pair* *tau-natp-pair* (getprop 'natp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (equal *tau-posp-pair* (getprop 'posp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-posp-pair* *tau-posp-pair* (getprop 'posp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (equal *tau-minusp-pair* (getprop 'minusp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-minusp-pair* *tau-minusp-pair* (getprop 'minusp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (equal *tau-booleanp-pair* (getprop 'booleanp 'tau-pair nil 'current-acl2-world (w *the-live-state*)))) (interface-er str '*tau-booleanp-pair* *tau-booleanp-pair* (getprop 'booleanp 'tau-pair nil 'current-acl2-world (w *the-live-state*))))) (cond ((not (and (equal *min-type-set* #-:non-standard-analysis -8192 #+:non-standard-analysis -65536) (equal *max-type-set* #-:non-standard-analysis 8191 #+:non-standard-analysis 65535))) (interface-er "The minimal and maximal type-sets are incorrectly built into the ~ definition of type-alist-entryp. These type-sets get generated by ~ the call of def-basic-type-sets in type-set-a.lisp are must be ~ mentioned, as above, in axioms.lisp. Evidently, a new type-set bit ~ was added. Update type-alist-entryp."))) (cond ((not (equal *primitive-monadic-booleans* (collect-monadic-booleans (strip-cars *primitive-formals-and-guards*) (ens *the-live-state*) (w *the-live-state*)))) (interface-er str '*primitive-monadic-booleans* *primitive-monadic-booleans* (collect-monadic-booleans (strip-cars *primitive-formals-and-guards*) (ens *the-live-state*) (w *the-live-state*))))) (cond ((not (getprop 'booleanp 'tau-pair nil 'current-acl2-world (w *the-live-state*))) (interface-er "Our code for tau-term assumes that BOOLEANP is a tau predicate. But ~ it has no tau-pair property!"))) (let ((good-lst (chk-initial-built-in-clauses *initial-built-in-clauses* (w *the-live-state*) nil nil))) (cond (good-lst (interface-er "The defconst of *initial-built-in-clauses* is bad because at least ~ one of the records supplies an :all-fnnames that is different from ~ that computed by all-fnnames-subsumer. The correct setting is ~ shown below. To preserve the comments in the source file it might ~ be best to compare -- with EQUAL -- the elements below with the ~ corresponding elements in the current source file and just replace ~ the ones that have changed. Here is a correct setting in 1:1 ~ correspondence with the current setting: ~X01." `(defconst *initial-built-in-clauses* (list ,@good-lst)) nil)))) (mv-let (macros-found program-found logic-found) (fns-different-wrt-acl2-loop-only *acl2-files*) (flet ((my-diff (x y) (if *check-built-in-constants-debug* (loop for tuple in x when (not (member (car tuple) y :test 'eq)) collect tuple) (set-difference-eq x y)))) (let ((bad-macros (my-diff macros-found *primitive-macros-with-raw-code*)) (bad-program (my-diff program-found *primitive-program-fns-with-raw-code*)) (bad-logic (my-diff logic-found *primitive-logic-fns-with-raw-code*))) (when (or bad-macros bad-program bad-logic) (format t "Failed check for coverage of functions with acl2-loop-only code differences! Please send this error message to the ACL2 implementors. Missing functions (use *check-built-in-constants-debug* = t for verbose report): ~s ~s; ~a ~s: ~a" ; We need to update *primitive-macros-with-raw-code*, ; *primitive-program-fns-with-raw-code*, or ; *primitive-logic-fns-with-raw-code*, respectively according to the non-nil ; fields in the error message. (list (list '*primitive-macros-with-raw-code* bad-macros) (list '*primitive-program-fns-with-raw-code* bad-program) (list '*primitive-logic-fns-with-raw-code* bad-logic)) '(lisp-implementation-type) (lisp-implementation-type) '(lisp-implementation-version) (lisp-implementation-version)) (error "Check failed!"))))) ; The following is a start on checking that we don't have superfluous symbols ; in the list values of certain constants. But in fact there can be such ; symbols: we want the value for each constant must be independent of ; features :hons or :acl2-par, yet some macros and functions are only defined ; when such features are present. We may think more about this later. ; (let ((undefined-macros ; (loop for x in *primitive-macros-with-raw-code* ; when (not (or (macro-function x) (symbol-function x))) ; collect x)) ; (undefined-program-fns ; (loop for x in *primitive-program-fns-with-raw-code* ; when (not (fboundp x)) ; collect x)) ; (undefined-logic-fns ; (loop for x in *primitive-logic-fns-with-raw-code* ; when (not (fboundp x)) ; collect x))) ; (when undefined-macros ; (format ; t ; "Undefined macros in *primitive-macros-with-raw-code*:~%~s~%" ; undefined-macros)) ; (when undefined-program-fns ; (format ; t ; "Undefined macros in *primitive-program-fns-with-raw-code*:~%~s~%" ; undefined-program-fns)) ; (when undefined-logic-fns ; (format ; t ; "Undefined macros in *primitive-logic-fns-with-raw-code*:~%~s~%" ; undefined-logic-fns)) ; (when (or undefined-macros undefined-program-fns undefined-logic-fns) ; (error "Check failed!"))) )) (defun-one-output check-none-ideal (trips acc) (cond ((null trips) (cond ((null acc) nil) (t (er hard 'check-none-ideal "The following are :ideal mode functions that are not ~ non-executable. We rely in oneify-cltl-code on the absence ~ of such functions in the boot-strap world (see the comment ~ on check-none-ideal there). These functions should have ~ their guards verified: ~&0." acc)))) (t (let* ((trip (car trips)) (fn ; We need to rule out triples such as the following (but for :ideal mode) ; (EVENT-LANDMARK GLOBAL-VALUE 5054 ; (DEFUN EVENS . :COMMON-LISP-COMPLIANT) ; DEFUN EVENS (L) ; (DECLARE (XARGS :GUARD (TRUE-LISTP L))) ; (COND ((ENDP L) NIL) ; (T (CONS (CAR L) (EVENS (CDDR L)))))) (and (eq (car trip) 'event-landmark) (true-listp trip) (eq (cadr trip) 'global-value) (eq (nth 4 trip) 'defun) (nth 5 trip)))) (cond ((and fn (symbolp fn) (eq (symbol-class fn (w *the-live-state*)) :ideal) (not (eq (getprop fn 'non-executablep nil 'current-acl2-world (w *the-live-state*)) t))) (check-none-ideal (cdr trips) (cons fn acc))) (t (check-none-ideal (cdr trips) acc))))))) (defun check-state-globals-initialized () (let (bad) (do-symbols (sym (find-package "ACL2_GLOBAL_ACL2")) (when (boundp sym) (let ((acl2-sym (intern (symbol-name sym) "ACL2"))) (when (not (or (assoc acl2-sym *initial-global-table* :test 'eq) (assoc acl2-sym *initial-ld-special-bindings* :test 'eq))) (push (cons acl2-sym (symbol-value sym)) bad))))) (when bad (error "The following symbols, perhaps with the values shown, need to~%~ be added to *initial-global-table*:~%~s~%" bad)))) (defun-one-output check-acl2-initialization () (check-built-in-constants) (check-out-instantiablep (w *the-live-state*)) (check-none-ideal (w *the-live-state*) nil) (check-state-globals-initialized)) (defun set-initial-cbd () (let ((state *the-live-state*)) (setq *initial-cbd* (our-pwd)) ; In CCL, it seems that *initial-cbd* as computed above could give a string ; not ending in "/". We fix that here. (cond ((and (stringp *initial-cbd*) (not (equal *initial-cbd* "")) (not (eql (char *initial-cbd* (1- (length *initial-cbd*))) #\/))) (setq *initial-cbd* (concatenate 'string *initial-cbd* "/")))) (cond ((not (absolute-pathname-string-p *initial-cbd* t (get-os))) (error "Our guess for the initial setting of cbd, ~x0, which was ~%~ generated by (our-pwd), is not a legal directory! Before ~%~ entering ACL2, please setq *initial-cbd* to a nonempty ~%~ string that represents an absolute ACL2 (i.e., Unix-style) ~%~ pathname. Sorry for the inconvenience." *initial-cbd*))) (f-put-global 'connected-book-directory *initial-cbd* state))) (defun initialize-acl2 (&optional (pass-2-ld-skip-proofsp 'include-book) (acl2-pass-2-files *acl2-pass-2-files*) system-books-dir skip-comp-exec) ; Note: if system-books-dir is supplied, it should be a Unix-style ; pathname (either absolute or not [doesn't matter which]). ; This function first lds all of the *acl2-files* except boot-strap-pass-2.lisp ; and *-raw.lisp in default-defun-mode :program (which is the default ; default-defun-mode). It then loads the files in acl2-pass-2-files in :logic ; mode. ; During the first pass, ld-skip-proofsp is 'initialize-acl2, which is like the ; setting t (doing syntactic checks but skipping proofs and LOCALs) but omits a ; few of the checks so the bootstrapping can be done. During the second pass, ; ld-skip-proofsp is as specified by the &optional parameter above. It ; defaults to 'include-book, which means we skip LOCALs, all syntactic checks, ; and proofs. By calling this function with pass-2-ld-skip-proofsp nil you can ; arrange for it to try to prove its way through the second pass. However, see ; below. ; Why Two Passes? By doing things in two passes we make it possible to use all ; system functions in hints and other proof commands. In the one-pass ; initialization we used to use, it was impossible to use theory expressions in ; the defthms in axioms.lisp because the necessary theory functions were not ; yet defined and so trans-eval balked on them. (when (null system-books-dir) (let ((dir (getenv$-raw "ACL2_SYSTEM_BOOKS"))) (when (and dir (not (equal dir ""))) (setq system-books-dir dir)))) (with-warnings-suppressed ; Interactive Proofs: Many times, (initialize-acl2 nil) -- which causes the ; system to prove the admissibility of everything done in the second pass -- ; will fail because insufficient lemmas are available to handle new functions ; added since the last such proof. But it will leave you in a state so that ; you can continue to develop proofs. In particular, if you have changed some ; of the proved code, e.g., axioms.lisp, and you wish to re-verify it, you can ; proceed as follows. First, fire up akcl. Then do (acl2::load-acl2). ; Finally do (initialize-acl2 nil) and wait for the first proof to fail. When ; it fails you will be returned to lisp. There, in raw lisp, you should ; execute ; (let ((state *the-live-state*)) ; (reset-ld-specials t) ; ; (set-ld-redefinition-action '(:doit! . :overwrite) state) ;see below ; ) ; This will set the ld-specials to their conventional post-boot-strap setting, ; except (possibly) for ld-redefinition-action which will allow redefinition. ; (We discuss this more below.) Then enter the loop with (LP!), which will set ; *features* to include :acl2-loop-only in case read conditionals are present ; in the sources you wish to develop. Once in the loop, you should be able to ; continue as normal with proof development. ; If the initialization did not get beyond pass one, :q is undefined and the ; only way to exit is to do (mv nil :q state). You will also note that other ; keyword commands, e.g., :pc, are unavailable. You can always execute the ; appropriate form in raw lisp, e.g., ; (let ((state *the-live-state*)) (pc 'fn)) ; If you did get beyond pass one, things will be pretty normal looking except ; that inspection will show that both (global-val 'boot-strap-flg (w state)) ; and (global-val 'boot-strap-pass-2 (w state)) are t. This will manifest ; itself in some perhaps surprising ways during interactive proofs, e.g., ; redundant deflabels are admitted during the second pass. ; Is Redefinition Permission Necessary? Not always. If you are re-verifying ; the sources the chances are you've changed something. Suppose some event is ; no longer admissible. If you have to change a function definition to admit ; it, i.e., one of your new events is incorrectly written, then redefinition ; permission is necessary because what you are trying to do is NOT just a ; simple reclassifying (you're changing the definition). If on the other hand ; you are not changing definitions but adding them, you need not perform the ; set-ld-redefinition-action shown above. A conservative thing to do is to ; leave the redefinition action nil and not set it until necessary. ; How do I Add a New Function Definition? If you try to add to the sources a ; new function definition while in pass one of the initialization it will fail ; because we insist that all functions defined logically already have raw lisp ; definitions. You should define the function first in raw lisp (by exiting ; the LP!) and then in the loop. If you discovered this problem by provoking ; the hard error: ACL2 Error in ACL2-INTERFACE: fn is not fboundp! then you ; must first exit the lisp break with :q. This will revert the world and throw ; you out of the LP!. The world is as it was before the offensive definition, ; so you should define the function in raw lisp, reenter LP! and proceed as ; intended. ; The Special State Discussion: We bind state to *the-live-state* below because ; the macro expansion of ld introduces the variable state. Once upon a time we ; declared state special with (defparameter state *the-live-state*) in this ; file. But that had the effect of preventing tail-recursion removal in ; situations like (pprogn (princ$ ...) (recursive-call ...)) because pprogn ; macro expands to a binding of state and you can't jump out of a binding of a ; special in tail recursion (it is only allowed if it is lexical). Thus, we ; got the curious and frustrating problem that if we recompiled system ; functions (like print-doc-string-part1) then they would no longer have tail ; recursions removed, even though the recursion would be so removed when we ; made the system from scratch. (Reiterating the reason: between the two ; compilations of the function, state became special. Had we declared state ; special in axioms.lisp instead of here in interface-raw.lisp, none of ; tail-recursion removal on state changing functions would have been done!) We ; no longer make state special and hence must bind it to *the-live-state* ; lexically when its use is unavoidable. In point of fact we now evaluate ; (setq state *the-live-state*) in (load-acl2), without making state special, ; and hence state may be used in raw Lisp after the system is loaded as long as ; the form using state is not compiled. ; Finally: another way to prove your way through axioms.lisp is to invoke ; (acl2::load-acl2) and (initialize-acl2), then save the system (e.g., in akcl ; execute (si::save-system "my-saved_acl2")), and now each time you invoke that ; saved image first execute ; (defconst *acl2-pass-2-files* '()) ; in raw Lisp (or, execute this before saving the image), and then after ; executing (LP!) submit the form ; (set-w 'extension (global-set 'boot-strap-pass-2 t (w state)) state) ; to ACL2 in order to allow system DEFLABELs to be considered redundant. ; Save perhaps 12K in image. (set-initial-cbd) (makunbound '*copy-of-common-lisp-symbols-from-main-lisp-package*) (let* ((*features* (cons :acl2-loop-only *features*)) #+akcl ; AKCL compiler note stuff. We have so many tail recursive functions ; that the notes about tail recursion optimization are just too much ; to take. (compiler:*suppress-compiler-notes* t) (state *the-live-state*) pass-2-alist) (enter-boot-strap-mode system-books-dir (get-os)) ; Rockwell Addition: Here we initialize the nu-rewriter memo cache. (initialize-nu-memos 65534) (setq pass-2-alist (let ((ans nil)) (dolist (fl acl2-pass-2-files) (mv-let (erp val state) ; Warning. Because of the read-file here, we have to be careful not to define ; any packages in the pass-2 files that contain symbols mentioned in those ; files. The read-file will break in any such case; the DEFPKG in such a file ; must be processed first. (read-file (coerce (append (coerce fl 'list) (cons #\. (coerce *lisp-extension* 'list))) 'string) *the-live-state*) (declare (ignore state)) (cond (erp (interface-er "Unable to read file ~x0!" fl)) (t (push (cons fl val) ans))))) ans)) (dolist (fl *acl2-files*) (when (not (or (equal fl "boot-strap-pass-2") (raw-source-name-p fl))) (mv-let (er val st) (ld-fn (ld-alist-raw (or (cdr (assoc fl pass-2-alist :test #'equal)) (coerce (append (coerce fl 'list) (cons #\. (coerce *lisp-extension* 'list))) 'string)) 'initialize-acl2 :error) *the-live-state* nil) (declare (ignore val st)) (cond (er ; The load caused an error. We abort quietly without doing anything ; else so we are in the same state. (return-from initialize-acl2 nil)))))) (enter-boot-strap-pass-2) (dolist (fl acl2-pass-2-files) (mv-let (er val st) (ld-fn (ld-alist-raw (or (cdr (assoc fl pass-2-alist :test #'equal)) (interface-er "Did not find ~x0 in pass-2-alist." fl)) pass-2-ld-skip-proofsp :error) state nil) (declare (ignore val st)) (cond (er ; The load caused an error. We abort quietly without doing anything ; else so we are in the same state. (return-from initialize-acl2 nil))))) ; It is important not to wait to write out TMP1.lisp until after we leave the ; boot-strap. By doing so before that time, we ensure that the guard-check ; under safe mode is made for primitives (see oneify-cltl-code), and that ; compile-uncompiled-*1*-defuns will not encounter 'exit-boot-strap-mode and ; thus stop finding functions to compile. We use a call of ld here to make ; possible the subsidiary uses of state-global-let* on behalf of macroexpand1 ; (see the comment in comp-fn for more information). (if (not skip-comp-exec) ; Optimization: Skip this compile for generate-acl2-proclaims. (ld '((comp-fn :exec nil "1" state)))) (exit-boot-strap-mode) (initialize-pc-acl2 *the-live-state*) ; We now set up the ld specials as we wish them for the user's first ; invocation of LP. The ld specials were previously initialized (in ; enter-boot-strap-mode) to the same values used below (because the ; LDs above make reference to some of them so they must be bound). ; But the LD above changes them so we now initialize them again. (f-put-ld-specials *initial-ld-special-bindings* *the-live-state*) ; We now check certain invariants, for example, that we have defined certain ; built-in constants correctly. (or (not acl2-pass-2-files) ; The check for check-built-in-constants in check-acl2-initialization, for one, ; will fail if we do not make a second pass through axioms.lisp. That is ; because all (or at least most) of the 'level-no properties are still 0 before ; then, so chk-initial-built-in-clauses (called in check-built-in-constants) ; will fail, because its subroutine all-fnnames-subsumer does not behave ; properly until the 'level-no properties are set. (check-acl2-initialization)) (cond ((or (not (probe-file *acl2-status-file*)) (delete-file *acl2-status-file*)) (with-open-file (str *acl2-status-file* :direction :output) (format str "~s" ; The next token, :INITIALIZED, is used in GNUmakefile; keep in sync. :INITIALIZED)))) ; If you want the final image to have infixp = t, then put the following ; form here: ; (f-put-global 'infixp t *the-live-state*) t))) ; LP ; Lp is the function that an ACL2 user invokes to get into the ACL2 ; read-eval-print loop. ; Essay on Pathnames ; We use Unix-style pathnames everywhere in ACL2 except when interfacing with ; the operating system. Functions defined in this file, interface-raw.lisp, ; generally use real pathname strings for the host operating system. ; (Exceptions are clearly labeled, including compile-uncompiled-defuns and ; compile-uncompiled-*1*-defuns.) Functions defined outside this file ; (interface-raw.lisp) pass around ACL2 (Unix-style) pathname strings. Here ; are some functions that take pathnames whose form is based on (os (w state)) ; rather than on Unix. ; acl2-compile-file [but see comment there] ; compile-file ; convert-book-name-to-compiled-name [Unix pathname is OK too] ; delete-file ; delete-compiled-file ; load ; probe-file ; proclaim-file ; Before defining lp, we provide support for inhibiting breaks. #-(and gcl (not cltl2)) (defun our-abort (condition y &aux (state *the-live-state*) (btp (member-eq (f-get-global 'debugger-enable *the-live-state*) '(:bt :break-bt :bt-break)))) ; Keep this in sync with :doc set-debugger-enable. (declare (ignore y)) #+acl2-par (setq *reset-parallelism-variables* t) (print-proof-tree-finish state) (when btp (print-call-history)) (cond ((or (debugger-enabledp state) (eql *ld-level* 0) ; (global-val 'boot-strap-flg (w state)) ; avoid the potential error (getprop 'boot-strap-flg 'global-value nil 'current-acl2-world (w state))) nil) (t (let ((*debugger-hook* nil) ; extra care to avoid possible loop #+ccl ; as above, for CCL revision 12090 and beyond (ccl::*break-hook* nil) (*package* (find-package (current-package state))) (continue-p (and (find-restart 'continue) *acl2-time-limit-boundp* (not (eql *acl2-time-limit* 0))))) #+ccl ; for CCL revisions before 12090 (declare (ignorable ccl::*break-hook*)) (terpri t) (format t "***********************************************") (cond (continue-p (format t "~&Note: ~A~ ~& Will attempt to exit the proof in progress;~ ~& otherwise, the next interrupt will abort the proof." condition)) (t (format t "~&************ ABORTING from raw Lisp ***********") (format t "~&Error: ~A" condition))) (when btp (format t "~%NOTE: See above for backtrace.~%")) (format t "~&***********************************************~&") (unless *acl2-error-p* (format t "~%The message above might explain the error. If not, and~%~ if you didn't cause an explicit interrupt (Control-C),~%~ then the root cause may be call of a :program mode~%~ function that has the wrong guard specified, or even no~%~ guard specified (i.e., an implicit guard of t).~%~ See :DOC guards.~&")) (when (not (member-eq 'set-debugger-enable-fn ; (global-val 'untouchable-fns (w state)) (getprop 'untouchable-fns 'global-value nil 'current-acl2-world (w state)))) (format t "~%To enable breaks into the debugger (also see :DOC ~ acl2-customization):~&~s~&" '(set-debugger-enable t))) (force-output t) (cond (continue-p (setq *acl2-time-limit* 0) (invoke-restart 'continue)) (t ; Parallelism blemish: as of May 16, 2011, we also reset all parallelism ; variables in Rager's modified version of the source code. However, that ; strikes Rager as strange, since we went through so much trouble to find out ; where we should reset parallelism variables. So, it is now commented out, ; today, May 16, 2011, and we will wait to see what happens. ; #+acl2-par ; (reset-all-parallelism-variables) ; Parallelism blemish: after a single proof runs for awhile, at least with ; waterfall parallelism enabled, it can take two interrupts before the abort ; occurs. It would be nice if the proof could abort after the first interrupt, ; since when two interrupts are required, the summary does not print the ; ACL2(p) checkpoints. (our-ignore-errors ; might not be in scope of catch (throw 'local-top-level :our-abort)))))))) ; We formerly set *debugger-hook* here, but now we set it in lp; see the ; comment there. #+ccl ; for CCL revisions starting with 12090 (when (boundp 'ccl::*break-hook*) (setq ccl::*break-hook* 'our-abort)) (defun initial-customization-filename () (let* ((cfb00 (getenv$-raw "ACL2_CUSTOMIZATION")) (cfb0 (if (equal cfb00 "NONE") :none (and cfb00 (not (equal cfb00 "")) (extend-pathname (f-get-global 'connected-book-directory *the-live-state*) cfb00 *the-live-state*))))) (cond ((eq cfb0 :none) :none) ((and cfb0 (probe-file cfb0)) cfb0) (cfb0 ; but (not (probe-file cfb0)) (let ((*print-circle* nil)) (format t "~%ERROR: Environment variable ACL2_CUSTOMIZATION has value~%~ ~3T~a~%but file~%~3T~a~%does not appear to exist.~%~ Now quitting ACL2. To fix this problem, you may wish~%~ to fix the value of that environment variable by setting it~%~ to a valid file name, by unsetting it, or by setting it to~%~ the empty string.~%~%" cfb00 cfb0)) (exit-lisp 1)) (t (let* ((cb1 (our-merge-pathnames (f-get-global 'connected-book-directory *the-live-state*) "acl2-customization")) (cfb1 (string-append cb1 ".lsp")) (cfb1a (string-append cb1 ".lisp"))) (cond ((probe-file (pathname-unix-to-os cfb1 *the-live-state*)) cfb1) ((probe-file (pathname-unix-to-os cfb1a *the-live-state*)) cfb1a) (t (let* ((home (our-user-homedir-pathname)) (cb2 (and home (our-merge-pathnames ; The call of pathname-os-to-unix below may seem awkward, since later we apply ; pathname-unix-to-os before calling probe-file. However, our-merge-pathnames ; requires Unix-style pathname arguments, and we prefer not to write an ; analogous function that takes pathnames for the host operating system. (pathname-os-to-unix ; MCL does not seem to handle calls of truename correctly on logical pathnames. ; We should think some more about this, but for now, let's solve this problem ; by brute force. #+(and mcl (not ccl)) (our-truename (common-lisp::translate-logical-pathname home) t) #-(and mcl (not ccl)) (our-truename home "Note: Calling OUR-TRUENAME from ~ INITIAL-CUSTOMIZATION-FILENAME") (os (w *the-live-state*)) *the-live-state*) "acl2-customization"))) (cfb2 (and cb2 (string-append cb2 ".lsp"))) (cfb2a (and cb2 (string-append cb2 ".lisp")))) (cond (cb2 (cond ((probe-file (pathname-unix-to-os cfb2 *the-live-state*)) cfb2) ((probe-file (pathname-unix-to-os cfb2a *the-live-state*)) cfb2a) (t nil)))))))))))) #+(and acl2-par lispworks) (setq system:*sg-default-size* ; Keep the below number in sync with the call to hcl:extend-current-stack in ; lp. 80000) #+(and acl2-par lispworks) (defun spawn-extra-lispworks-listener () ; In Lispworks, we spawn a thread for the listener before we exit lp for the ; first time, so that when we exit lp, multiprocessing does not stop. This ; strategy is derived from the following quote, from Martin Simmons, of ; Lispworks. ; ; "If you want it to run a normal REPL, then you could call ; lw:start-tty-listener when acl2::lp returns. That will make a new thread ; running the REPL, which will prevent multiprocessing from stopping." ; ; Another strategy, which was never released, involved following the ; multiprocessing example in section 15.13 of the Lispworks 6.0 manual. To ; quickly outline that strategy, we (1) renamed "lp" to "lp1", (2) defined "lp" ; to spawn a thread that called "lp1", (3) saved the Lispworks image with the ; ":multiprocessing t" flag, and (4) ensured that the Lispworks image's restart ; function was acl2-default-restart-function, which called "lp". ; ; We feel that Martin's suggested implementation is simpler, and so we ; use that. ; ; We rely on the following property of lw:start-tty-listener: if the tty ; listener is already running, calling lw:start-tty-listener does nothing. (lw:start-tty-listener)) (defun lp (&rest args) ; This function can only be called from within raw lisp, because no ACL2 ; function mentions it. Thus, we assume we are in raw lisp. This is the ; top-level entry to ACL2. Note that truename can cause an error in some ; Common Lisps when the given file or directory does not exist, in which case ; our-truename will generally return nil. Hence, we sometimes call ; our-truename on "" rather than on a file name. (when args (error "LP takes no arguments.")) (with-more-warnings-suppressed ; Parallelism wart: we currently reset the parallelism variables twice on ; startup. Removing the below call to reset-all-parallelism-variables should ; be the correct way to remove this double-reset, because we more thoroughly ; determine where to reset parallelism variables elsewhere in the code. #+acl2-par (reset-all-parallelism-variables) ; Remark for #+acl2-par. Here we set the gc-threshold to a high number. If ; the Lisps support it, this threshold could be based off the actual memory in ; the system. We peform this setting of the threshold in lp, because Lispworks ; doesn't save the GC configuration as part of the Lisp image. ; Parallelism no-fix: the threshold below may cause problems for machines with ; less than that amount of free RAM. At a first glance, this shouldn't ; realistically be a problem. However, a user might actually encounter this ; problem when running several memory-intensive ACL2(p) sessions in parallel ; via make -j. #+acl2-par (when (not *lp-ever-entered-p*) (set-gc-threshold$ (expt 2 30) nil)) #+(and acl2-par lispworks) (when (not *lp-ever-entered-p*) (sys:set-default-segment-size 0 :cons 250) (sys:set-default-segment-size 1 :cons 250) (sys:set-default-segment-size 2 :cons 250) (sys:set-default-segment-size 0 :other 250) (sys:set-default-segment-size 1 :other 250) (sys:set-default-segment-size 2 :other 250)) #+acl2-par (f-put-global 'parallel-execution-enabled t *the-live-state*) (let ((state *the-live-state*) #+(and gcl (not cltl2)) (lisp::*break-enable* (debugger-enabledp *the-live-state*))) (cond ((> *ld-level* 0) (when (raw-mode-p *the-live-state*) (fms "You have attempted to enter the ACL2 read-eval-print loop from ~ within raw mode. However, you appear already to be in that ~ loop. If your intention is to leave raw mode, then execute: ~ :set-raw-mode nil.~|" nil (standard-co *the-live-state*) *the-live-state* nil)) (return-from lp nil)) ((not *lp-ever-entered-p*) (set-initial-cbd) (eval `(in-package ,*startup-package-name*)) ;only changes raw Lisp pkg ; We formerly set *debugger-hook* at the top level using setq, just below the ; definition of our-abort. But that didn't work in Lispworks, where that value ; persisted right up to the saving of an image yet *debugger-hook* was nil ; after starting up that image. Apparently Lispworks 6.0 sets *debugger-hook* ; globally to nil when input comes from a file, which is how ACL2 is built, ; rather than standard-input, #-(and gcl (not cltl2)) (setq *debugger-hook* 'our-abort) ; Even with the setting of *stack-overflow-behaviour* to nil or :warn in ; acl2-init.lisp, we cannot eliminate the following form for LispWorks. (We ; tried with LispWorks 6.0 and Lispworks 6.0.1 with *stack-overflow-behaviour* ; = nil and without the following form, but we got segmentation faults when ; certifying community books books/concurrent-programs/bakery/stutter2 and ; books/unicode/read-utf8.lisp.) #+lispworks (hcl:extend-current-stack 400) #+(and lispworks acl2-par) (when (< (hcl:current-stack-length) ; Keep the below number (currently 80000) in sync with the value given to ; *sg-default-size* (set elsewhere in our code). 80000) (hcl:extend-current-stack ; this calculation sets the current stack length to be within 1% of 80000 (- (round (* 100 (/ (hcl:current-stack-length) 80000))) 100))) ; Acl2-default-restart isn't enough in Allegro, at least, to get the new prompt ; when we start up: (let* ((system-dir (let ((str (getenv$-raw "ACL2_SYSTEM_BOOKS"))) (and str (maybe-add-separator str)))) (save-expansion (let ((s (getenv$-raw "ACL2_SAVE_EXPANSION"))) (and s (not (equal s "")) (not (equal (string-upcase s) "NIL"))))) (user-home-dir-path (our-user-homedir-pathname)) (user-home-dir0 (and user-home-dir-path (our-truename user-home-dir-path "Note: Calling ~ OUR-TRUENAME from LP."))) (user-home-dir (and user-home-dir0 (if (eql (char user-home-dir0 (1- (length user-home-dir0))) *directory-separator*) (subseq user-home-dir0 0 (1- (length user-home-dir0))) user-home-dir0)))) (when system-dir (f-put-global 'system-books-dir (canonical-dirname! (unix-full-pathname system-dir) 'lp *the-live-state*) *the-live-state*)) (when (and save-expansion (not (equal (string-upcase save-expansion) "NIL"))) (f-put-global 'save-expansion-file t *the-live-state*)) (when user-home-dir (f-put-global 'user-home-dir user-home-dir *the-live-state*))) (set-gag-mode-fn :goals *the-live-state*) #-hons ; Hons users are presumably advanced enough to tolerate the lack of a ; "[RAW LISP]" prompt. (install-new-raw-prompt) #+hons (f-put-global 'serialize-character-system #\Z state) #+(and (not acl2-loop-only) acl2-rewrite-meter) (setq *rewrite-depth-alist* nil) ; Without the following call, it was impossible to read and write with ACL2 I/O ; functions to *standard-co* in CLISP 2.30. Apparently the appropriate Lisp ; streams at the time of the build were closed when the ACL2 image was brought ; up. So we "refresh" the appropriate property lists with the current such ; Lisp streams. (setup-standard-io) ; The following applies to CLISP 2.30, where charset:iso-8859-1 is defined, not to ; CLISP 2.27, where charset:utf-8 is not defined. It apparently has to be ; executed in the current Lisp session. We tried executing the following form ; before saving an image, but the value of custom:*default-file-encoding* at ; startup was #. #+(and clisp unicode) (setq custom:*default-file-encoding* charset:iso-8859-1) #+gcl ; In GCL, at least through Version 2.6.7, there are only 1024 indices n ; available for the #n= reader macro. That is such a small number that for ; GCL, we turn off the use of this reader macro when printing out files such as ; .cert files. (f-put-global 'print-circle-files nil state) (let ((customization-full-file-name (initial-customization-filename))) (cond ((or (eq customization-full-file-name :none) (global-val 'boot-strap-flg (w state))) nil) (customization-full-file-name ; If the ACL2 customization file exists (and we are not booting) then it hasn't ; been included yet, and we include it now. (fms "Customizing with ~x0.~%" (list (cons #\0 customization-full-file-name)) *standard-co* state nil) (let ((old-infixp (f-get-global 'infixp *the-live-state*))) (f-put-global 'infixp nil *the-live-state*) (with-suppression ; package locks, not just warnings, for read (state-free-global-let* ((connected-book-directory (f-get-global 'connected-book-directory state))) (ld-fn (put-assoc-eq 'standard-oi customization-full-file-name (put-assoc-eq 'ld-error-action :return (f-get-ld-specials *the-live-state*))) *the-live-state* nil))) (f-put-global 'infixp old-infixp *the-live-state*))))) (f-put-global 'ld-error-action :continue *the-live-state*))) (with-suppression ; package locks, not just warnings; to read 'cl::foo (cond ((and *return-from-lp* (not *lp-ever-entered-p*)) (f-put-global 'standard-oi `(,*return-from-lp* (value :q)) *the-live-state*) (setq *return-from-lp* nil) (setq *lp-ever-entered-p* t) (state-free-global-let* ((ld-verbose nil) (ld-prompt nil) (ld-post-eval-print nil)) (ld-fn (f-get-ld-specials *the-live-state*) *the-live-state* nil))) (t (setq *lp-ever-entered-p* t) (f-put-global 'standard-oi *standard-oi* *the-live-state*) (ld-fn (f-get-ld-specials *the-live-state*) *the-live-state* nil) (fms "Exiting the ACL2 read-eval-print loop. To re-enter, ~ execute (LP)." nil *standard-co* *the-live-state* nil)))) #+(and acl2-par lispworks) (spawn-extra-lispworks-listener) (values)))) (defmacro lp! (&rest args) `(let ((*features* (add-to-set-eq :acl2-loop-only *features*))) (lp ,@args))) ; COMPILING, SAVING, AND RESTORING (defun acl2-compile-file (full-book-name os-expansion-filename) ; Full-book-name is a Unix-style pathname. Os-expansion-filename is a pathname ; for the current operating system of the file we want to compile. We compile ; os-expansion-filename but into the compiled filename corresponding to ; full-book-name. ; To compile os-expansion-filename, we need to make sure that uses in the file ; of backquote and comma conform in meaning to those that were in effect during ; certification. ; We assume that this function is called only after forms in the given ; full-book-name have already been evaluated and (if appropriate) proclaimed, ; hence in particular so that macros have been defined. (progn (chk-book-name full-book-name full-book-name 'acl2-compile-file *the-live-state*) (let ((*readtable* *acl2-readtable*) (ofile (convert-book-name-to-compiled-name (pathname-unix-to-os full-book-name *the-live-state*) *the-live-state*)) (stream (get (proofs-co *the-live-state*) *open-output-channel-key*))) ; It is tempting to evaluate (proclaim-file os-expansion-filename). However, ; all functions in full-book-name were presumably already proclaimed, as ; appropriate, during add-trip. (let ((*readtable* *reckless-acl2-readtable*) ; We reduce the compiled file size produced by CCL, even in the #+hons case ; where we may have set ccl::*save-source-locations* to t. We have seen an ; example where this binding reduced the .dx64fsl size from 13696271 to 24493. #+ccl (ccl::*save-source-locations* nil)) (compile-file os-expansion-filename :output-file ofile)) ; Warning: Keep the following "compile on the fly" readtime conditional in sync ; with the one in initialize-state-globals. Here, we avoid loading the ; compiled file when compiling a certified book, because all functions are ; already compiled. Thus, the code dealing with hons-enabledp below is ; irrelevant as long as under-the-hood hons/memoize code is only used in CCL ; (or SBCL) builds. #-(or ccl sbcl) (let ((*compiling-certified-file* ; See the comment about an optimization using *compiling-certified-file* in the ; raw Lisp definition of acl2::defconst. t) (alist (and (hons-enabledp *the-live-state*) (loop for pair in (table-alist 'memoize-table (w *the-live-state*)) when (fboundp (car pair)) ; always true? collect (cons (car pair) (symbol-function (car pair))))))) (load-compiled ofile t) (loop for pair in alist ; nil if not hons-enabledp when (not (eq (symbol-function (car pair)) (cdr pair))) do (setf (symbol-function (car pair)) (cdr pair))) (terpri stream) (prin1 ofile stream)) (terpri stream) (terpri stream)))) (defun-one-output delete-auxiliary-book-files (full-book-name) (let* ((file (pathname-unix-to-os full-book-name *the-live-state*)) (ofile (convert-book-name-to-compiled-name file *the-live-state*)) (efile (expansion-filename file nil *the-live-state*)) (err-string "A file created for book ~x0, namely ~x1, exists and ~ cannot be deleted with Common Lisp's delete-file. We ~ do not know for sure whether this file was produced by ~ ACL2 and we do not even know that it corresponds to the ~ book ~x0. If ~x1 exists at the time of an ~ (include-book ~x0), it might be erroneously loaded, ~ possibly causing inconsistency.")) (when (probe-file ofile) (cond ((delete-file ofile) nil) (t (er hard 'delete-auxiliary-book-files err-string full-book-name ofile)))) #+clisp (let* ((len (length file)) (lib-file (assert$ (equal (subseq file (- len 5) len) ".lisp") (concatenate 'string (subseq file 0 (- len 5)) ".lib")))) (when (probe-file lib-file) (cond ((delete-file lib-file) nil) (t (er hard 'delete-auxiliary-book-files err-string full-book-name lib-file))))) (when (probe-file efile) (cond ((delete-file efile) nil) (t (er hard 'delete-auxiliary-book-files err-string full-book-name efile)))))) (defun delete-expansion-file (expansion-filename state) (delete-file expansion-filename) (io? event nil state (expansion-filename) (fms "Note: Deleting book expansion file,~%~s0.~|" (list (cons #\0 expansion-filename)) (proofs-co state) state nil))) (defun compile-uncompiled-defuns (file &optional (fns :some) gcl-flg &aux (state *the-live-state*)) ; File should be given in Unix-style syntax. Hence for example, "TMP" is ; relative to the current directory, even though on a Macintosh this might ; appear to be an absolute pathname for a file. ; Compile-uncompiled-defuns compiles the non-built-in defuns that are not ; currently compiled if FNS is :some. Otherwise, FNS should be a list of ; functions to compile. (when (and (not (symbol-listp fns)) (not (eq fns :some))) (er hard 'compile-uncompiled-defuns "The argument to compile-uncompiled-defuns must be either a true list ~ of symbols or the keyword :some. The argument ~x0 is thus illegal." fns)) (cond ((null fns) (warning$ 'compile-uncompiled-defuns nil "No functions to compile.") (return-from compile-uncompiled-defuns file))) (let ((os-file (pathname-unix-to-os file state))) (state-global-let* ((print-circle (f-get-global 'print-circle-files state)) (serialize-character (f-get-global 'serialize-character-system state))) (with-print-controls :defaults ((*print-circle* (f-get-global 'print-circle state))) (let ((seen (make-hash-table :test 'eq)) (fns (cond ((eq fns :uncompiled) :some) ((eq fns t) :all) (t fns))) (fn-file (format nil "~a.lisp" file))) ; (Warning: Do not delete the following comment without considering the pointer ; to it in compile-uncompiled-*1*-defuns.) ; The following use of with-output-object-channel-sharing causes #n= sharing ; notation to be used when printing each defun. The number of such indices (n) ; starts fresh with each definition. This should be OK since each defun will ; presumably be read separately -- quoting the CL HyperSpec, Section "2.4.8.15 ; Sharpsign Equal-Sign": ... The scope of the label is the expression being ; read by the outermost call to read; within this expression, the same label ; may not appear twice. (with-output-object-channel-sharing chan fn-file (let ((str0 (get-output-stream-from-channel chan))) (format str0 "; This file is automatically generated, to be ~ compiled.~%; Feel free to delete it after compilation.~%") ; We print (in-package "...") but we do it this way to guarantee that the ; symbol 'in-package is printed correctly. (print-object$ (list 'in-package (current-package state)) chan state) (dolist (trip (w state)) (cond ((and (eq fns :some) (eq (car trip) 'command-landmark) (eq (cadr trip) 'global-value) (equal (access-command-tuple-form (cddr trip)) '(exit-boot-strap-mode))) (return)) ((and (eq (car trip) 'cltl-command) (eq (cadr trip) 'global-value) (consp (cddr trip)) (eq (caddr trip) 'defuns) ; The next test asks whether the ignorep field of the defuns tuple is ; '(defstobj . stobj). If it is, this triple didn't actually make ; those definitions. (not (and (consp (caddr (cddr trip))) (eq (car (caddr (cddr trip))) 'defstobj)))) (dolist (x (cdddr (cddr trip))) (cond ((and (not (gethash (car x) seen)) (or (eq fns :some) (member-eq (car x) fns))) (setf (gethash (car x) seen) t) (when (not (compiled-function-p! (car x))) (cond ((or (member-eq (car x) (f-get-global 'program-fns-with-raw-code state)) (member-eq (car x) (f-get-global 'logic-fns-with-raw-code state))) (format t "; (ACL2 Note) Attempting ~ separate compilation due to ~ raw code: ~s~&" ; We ignore errors (if possible), since for example, we have seen LispWorks ; complain when (car x) names a function that is a lexical closure. (car x)) (our-ignore-errors (compile (car x)))) (t (print-object$ (cons 'defun x) chan state)))))))) ((and (eq (car trip) 'cltl-command) (eq (cadr trip) 'global-value) (consp (cddr trip)) (eq (caddr trip) 'defstobj)) (dolist (x (car (cddddr (cddr trip)))) ; (cddr trip) is of the form ; (DEFSTOBJ name the-live-name init raw-defs template) ; and x here is one of the raw-defs. Note that since raw Lisp definitions for ; defabsstobj are defmacros, we do not deal with defabsstobj, just as we skip ; the defstobj case when defabbrev is used for raw Lisp definitions, as ; determined by (member-equal *stobj-inline-declare* x) as shown below. (cond ((and (not (gethash (car x) seen)) (not (member-equal *stobj-inline-declare* x)) (or (eq fns :some) (member-eq (car x) fns))) (setf (gethash (car x) seen) t) (when (not (compiled-function-p! (car x))) (print-object$ (cons 'defun x) chan state)))))) ((eq (cadr trip) 'redefined) ; This case avoids redefining a macro back to an overritten function in the ; following example provided by Eric Smith. ; (defun foo (x) x) ; :redef! ; (defmacro foo (x) x) ; :comp t (setf (gethash (car trip) seen) t)))) (newline chan state) (close-output-channel chan state))) (when (not (eq fns :some)) (let (missing) (dolist (fn fns) (when (not (gethash fn seen)) (push fn missing))) (when missing (format t "~%Warning: The following functions have not been ~ compiled.~% ~s~%Perhaps you have not defined them ~ inside the ACL2 command loop.~%" missing)))) (cond (gcl-flg #+gcl (compile-file (our-truename (pathname-unix-to-os fn-file state) "Note: Calling OUR-TRUENAME from ~ COMPILE-UNCOMPILED-DEFUNS (under gcl-flg and #+gcl).") :c-file t :h-file t) #-gcl (er hard 'compile-uncompiled-defuns "The gcl-flg argument to compile-uncompiled-defuns is only ~ legal when running under GCL.")) (t (let ((lisp-file (our-truename (pathname-unix-to-os fn-file state) "Note: Calling OUR-TRUENAME from ~ COMPILE-UNCOMPILED-DEFUNS."))) (compile-file lisp-file) (when (not (keep-tmp-files state)) (delete-file lisp-file) #+clisp (delete-file (concatenate 'string os-file ".lib")))))) (load-compiled os-file t) (if (not (keep-tmp-files state)) (delete-file (concatenate 'string os-file "." *compiled-file-extension*)))) (value nil))) os-file)) (defun compile-uncompiled-*1*-defuns (file &optional (fns :some) gcl-flg chan0 &aux (state *the-live-state*) (wrld (w *the-live-state*))) ; This is similar to compile-uncompiled-defuns, but for *1* functions. ; However, an optional chan0 may be supplied; if non-nil, then it is an open ; output channel of type :object, which is closed by this function. ; File should be given in Unix-style syntax. Hence for example, "TMP" is ; relative to the current directory, even though on a Macintosh this might ; appear to be an absolute pathname for a file. ; If chan0 is not nil, then unlike compile-uncompiled-defuns, we write out all ; relevant *1* function definitions, even those that are currently compiled. (when (and (not (symbol-listp fns)) (not (eq fns :some))) (er hard 'compile-uncompiled-*1*-defuns "The argument to compile-uncompiled-*1*-defuns must be either a true ~ list of symbols or the keyword :some. The argument ~x0 is thus ~ illegal." fns)) (cond ((and (null fns) (null chan0)) (warning$ 'compile-uncompiled-defuns nil "No functions to compile.") (return-from compile-uncompiled-*1*-defuns file))) (let ((os-file (pathname-unix-to-os file state))) (state-global-let* ((print-circle (f-get-global 'print-circle-files state)) (serialize-character (f-get-global 'serialize-character-system state))) (with-print-controls :defaults ((*print-circle* (f-get-global 'print-circle state))) (let ((seen (let ((tbl (make-hash-table :test 'eq))) (when (not (eq fns :some)) (dolist (fn fns) (setf (gethash fn tbl) :init))) tbl)) (fns (cond ((eq fns :uncompiled) :some) ((eq fns t) :all) (t fns))) (fn-file (format nil "~a.lisp" file)) (not-boot-strap-p (null (global-val 'boot-strap-flg wrld)))) ; See the comment just above the call of with-output-object-channel-sharing in ; compile-uncompiled-defuns. (with-output-object-channel-sharing chan fn-file (cond ((null chan) (return-from compile-uncompiled-*1*-defuns (er hard 'compile-uncompiled-*1*-defuns "Unable to open file ~x0 for object output." fn-file))) (t (let ((defs nil) ; only used in the case chan0 is not nil (str0 (get-output-stream-from-channel chan))) (cond ((null chan0) ; new output file (format str0 "; This file is automatically generated, to be ~ compiled.~%; Feel free to delete it after ~ compilation.~%") ; We print (in-package "...") but we do it this way to guarantee that the ; symbol 'in-package is printed correctly. (print-object$ (list 'in-package (current-package state)) chan state)) (t state)) (dolist (trip wrld) (cond ((and (eq fns :some) (eq (car trip) 'command-landmark) (eq (cadr trip) 'global-value) (equal (access-command-tuple-form (cddr trip)) '(exit-boot-strap-mode))) ; If we are compiling while building the system, then we will never see ; 'exit-boot-strap-mode, which allows us to explore the entire world. But when ; a user executes (comp t), thus calling this function with argument fns equal ; to :some, the exploration should only consider user-defined events. (return)) ((and (eq (car trip) 'cltl-command) (eq (cadr trip) 'global-value) (consp (cddr trip)) (eq (caddr trip) 'defuns)) (dolist (x (cdddr (cddr trip))) (when (not (member-eq (car x) `(mv-list return-last wormhole-eval ,@*defun-overrides*))) (let ((*1*fn (*1*-symbol (car x)))) (cond ((and (fboundp *1*fn) (cond ((eq fns :some) (and (not (gethash (car x) seen)) ; We have seen during development of v2-9 that in Allegro CL, when compiling ; *1* functions on the fly during boot-strap (because of code in add-trip), the ; image size increases from 29.3M to 36.9M if we instead use the following ; code, which avoids writing *1* definitions to TMP1.lisp for compiled :logic ; mode functions at the end of the boot-strap. ; (and (not (compiled-function-p! *1*fn)) ; (or not-boot-strap-p ; (not (eq (cadr (cddr trip)) ; :program)))) ; That is, when we wrote out a TMP1.lisp for all :logic mode functions at the ; end of initialization and compiled it, we saved 7.6M. This result remains a ; mystery, but we prefer the smaller image so we keep the code below. The ; resulting increase in wall-clock build time was only about 3 seconds. See ; also the corresponding comment mentioning compile-uncompiled-*1*-defuns in ; add-trip. (if not-boot-strap-p (not (compiled-function-p! *1*fn)) ; We have noticed about a corresponding 0.6% to 1.2% slowdown in the regression ; suite when we avoid compiling :program mode *1* functions for GCL during the ; build and also at the end, when TMP1.lisp is written, as discussed in the ; comment above "boot-strap-flg ; delete for build speed-up (see above)" in ; add-trip. With that mod, we have tried the following additional mod so that ; for GCL we still compile built-in :program mode *1* functions after all, by ; writing out a huge TMP1.lisp file (8.3 MB instead 0.4 MB). ; #+gcl t #-gcl ; But this made things worse. Here are examples for v2-9-1 (on the way to ; v2-9-2): ; ; During the build, compile :program mode functions on the fly (as usual): ; 9763.160u 146.760s 2:51:33.10 96.2% 0+0k 0+0io 13673004pf+0w ; ; During the build, do not compile :program mode functions at all: ; 9827.570u 149.730s 2:52:29.27 96.4% 0+0k 0+0io 14549410pf+0w ; ; During the build, do not compile :program mode functions until the end ; (creating very large TMP1.lisp file): ; 9893.870u 150.240s 2:54:22.62 95.9% 0+0k 0+0io 14528555pf+0w ; ; Moroever, the saved_acl2.gcl file went from 43 MB, for the first two, to 104 ; MB for the last. So let's not write :program mode *1* functions to ; TMP1.lisp. See the long comment about *fast-acl2-gcl-build* in add-trip. (not (eq (cadr (cddr trip)) :program))) (setf (gethash (car x) seen) t))) ((eq (gethash (car x) seen) :init) (setf (gethash (car x) seen) t) (or chan0 (not (compiled-function-p! *1*fn)))))) (let ((*1*def (cons 'defun (oneify-cltl-code (cadr (cddr trip)) ; defun-mode x (getprop (car x) 'stobj-function nil 'current-acl2-world wrld) wrld)))) (cond (chan0 (push *1*def defs)) (t (print-object$ *1*def chan state)))))))))) ((eq (cadr trip) 'redefined) ; This case avoids a hard error message when encountering a macro redefined ; from an earlier defun, in the following example provided by Eric Smith. ; (defun foo (x) x) ; :redef! ; (defmacro foo (x) x) ; :comp t (setf (gethash (car trip) seen) t)))) (when chan0 ; Print all the defs in a single progn, for maximum structure sharing via #n= ; and #n#. (print-object$ (cons 'progn (nreverse defs)) chan state)) (newline chan state) (cond (chan0 (return-from compile-uncompiled-*1*-defuns os-file)) (t (close-output-channel chan state)))))) chan0) (when (not (eq fns :some)) (let (missing) (dolist (fn fns) (when (not (eq (gethash fn seen) t)) (push fn missing))) (when missing (format t "~%Warning: The following executable-counterpart ~ functions have not been compiled.~% ~s~%Perhaps you ~ have not defined them inside the ACL2 command loop.~%" missing)))) (cond (gcl-flg #+gcl (compile-file (our-truename (pathname-unix-to-os fn-file state) "Note: Calling OUR-TRUENAME from ~ COMPILE-UNCOMPILED-*1*-DEFUNS (under gcl-flg and ~ #+gcl).") :c-file t :h-file t) #-gcl (er hard 'compile-uncompiled-defuns "The gcl-flg argument to compile-uncompiled-*1*-defuns is only ~ legal when running under GCL.")) (t (let ((lisp-file (our-truename (pathname-unix-to-os fn-file state) "Note: Calling OUR-TRUENAME from ~ COMPILE-UNCOMPILED-*1*-DEFUNS."))) (compile-file lisp-file) (when (not (keep-tmp-files state)) (delete-file lisp-file) #+clisp (delete-file (concatenate 'string os-file ".lib")))))) (load-compiled os-file t) (if (not (keep-tmp-files state)) (delete-file (concatenate 'string os-file "." *compiled-file-extension*))) (value nil)))) os-file)) (defun compile-certified-file (expansion-filename full-book-name state) ; Warning: File full-book-name should already have been included in order that ; macros have been defined. But more than that, expansion-filename must ; already have been written. (let* ((os-full-book-name (pathname-unix-to-os full-book-name state)) (os-full-book-name-compiled (convert-book-name-to-compiled-name os-full-book-name state))) (when (probe-file os-full-book-name-compiled) (delete-file os-full-book-name-compiled)) (acl2-compile-file full-book-name expansion-filename) state)) (defun compile-for-include-book (full-book-name certified-p ctx state) (cond ((not certified-p) ; We warn rather than cause an error, since if one is including an uncertified ; book then one is presumably willing to live with the result. It could be ; annoying if an include-book :load-compiled-file :comp occurs inside another ; book, since one might not want to edit the parent book. (pprogn (warning$ ctx "Compiled file" "An include-book form for book ~x0 has specified option ~ :load-compiled-file :comp. But this book is ~ uncertified, so compilation is being skipped." full-book-name) (value nil))) (t (let* ((efile (expansion-filename full-book-name t state)) (entry (and *hcomp-book-ht* (gethash full-book-name *hcomp-book-ht*))) (status (and entry (access hcomp-book-ht-entry entry :status)))) (cond ((eq status 'complete) (value nil)) (t (mv-let (cfile state) (certificate-file full-book-name state) (let* ((cfile (and cfile (pathname-unix-to-os cfile state))) (cfile-write-date (and cfile (file-write-date cfile))) (efile-write-date (and (probe-file efile) (file-write-date efile))) (reason (cond ((not (probe-file cfile)) "the certificate file does not exist") ((not (probe-file efile)) "the expansion file does not exist") ((not (eq status 'to-be-compiled)) "the expansion file or compiled file ~ appears not to have been loaded to ~ completion") ((and cfile-write-date efile-write-date (<= cfile-write-date efile-write-date)) nil) (t "the write-date of the expansion file is ~ not greater than the write date of the ~ certificate file")))) (cond (reason (er soft ctx "An include-book event with option ~ :load-compiled-file :comp has failed for ~ book~|~s0,~|because ~@1. See :DOC ~ include-book and see :DOC ~ book-compiled-file." full-book-name reason)) (t (observation ctx "Compiling file ~x0, as specified by ~ include-book option :load-compiled-file ~ :comp." full-book-name) (acl2-compile-file full-book-name efile) (value nil))))))))))) ; MISCELLANEOUS (defun-one-output enabled-structurep (x) ; This function is basically a hack. We return t if x is probably an ; enable-structure. This is just part of the test of recognizing ; something we don't want to print out when tracing. See below. ; Without something like this, it is just too uncomfortable to trace ; many ACL2 functions because too much output is printed since ; enabled-structures typically take hundreds of lines to print. ; WARNING: Keep this in sync with enabled-structure. (case-match x (((index-of-last-enabling . theory-array) (array-name . array-length) array-name-root . array-name-suffix) (and (integerp index-of-last-enabling) (symbolp array-name) (array1p array-name theory-array) (integerp array-length) (character-listp array-name-root) (integerp array-name-suffix))) (& nil))) (defun-one-output rcnstp (x) ; This is another function in the spirit of enabled-structurep, above. ; WARNING: Keep this in sync with rewrite-constant. (case-match x (((current-enabled-structure) (& & . &) (& . &) (& . &) . &) (enabled-structurep current-enabled-structure)) (& nil))) (defvar *trace-alist* (list (cons 'state '|*the-live-state*|))) (defun-one-output assoc-eq-trace-alist (val alist) (cond ((endp alist) nil) ((and (boundp (caar alist)) (eq val (symbol-value (caar alist)))) (car alist)) (t (assoc-eq-trace-alist val (cdr alist))))) (defun-one-output print-list-without-stobj-arrays (lst) (loop for x in lst collect (or (and (arrayp x) (stobj-print-symbol x *user-stobj-alist*)) x))) (defun-one-output stobj-print-symbol (x user-stobj-alist-tail) ; Finds the (first) name of a pair (name . val) in user-stobj-alist-tail such ; that x is the symbol-value of that name's live var, and returns the symbol to ; print when encountering x during tracing. (and user-stobj-alist-tail (let ((pair (car user-stobj-alist-tail))) (if (eq x (symbol-value (the-live-var (car pair)))) (let ((name (stobj-print-name (car pair)))) (intern-in-package-of-symbol (cond ((eq x (cdr pair)) name) (t (concatenate 'string name "{instance}"))) (car pair))) (stobj-print-symbol x (cdr user-stobj-alist-tail)))))) (defun-one-output trace-hide-world-and-state (l) ; This function intuitively belongs over in init.lisp but it is here so ; that it will get compiled so we won't get stack overflow when ; tracing large objects. It is used to replace certain offensive ; objects by less offensive ones before trace prints the args and ; results of traced functions. It may not work well with local stobjs. ; In some functions, notably trace-fix-exit-raw and trace-fix-exit for GCL, we ; assume that trace-hide-world-and-state and its subroutines do not call mv. ; If that changes then we should use protect-mv there as we do in some other ; places. (let* ((stobj-pair (rassoc l *user-stobj-alist*)) (l (cond (stobj-pair (intern-in-package-of-symbol (stobj-print-name (car stobj-pair)) (car stobj-pair))) (t ; consider local stobjs (or (and (arrayp l) (stobj-print-symbol l *user-stobj-alist*)) l)))) (pair (assoc-eq-trace-alist l *trace-alist*))) (cond (pair (cdr pair)) ((atom l) l) ((eq l (w *the-live-state*)) '|current-acl2-world|) ((rcnstp l) '|some-rcnst|) ((enabled-structurep l) '|some-enabled-structure|) ((and (consp l) (or (eq (car l) 'event-index) (eq (car l) 'command-index)) (consp (cdr l)) (eq (car (cdr l)) 'global-value)) (list* (car l) 'global-value '|some-index|)) ; I have been known to put this in here ; ((and (consp l) ; (consp (car l)) ; (symbolp (car (car l))) ; (consp (cdr (car l))) ; (eq (car (cdr (car l))) 'global-value)) ; '|some-other-world-perhaps|) (t (cons (trace-hide-world-and-state (car l)) (trace-hide-world-and-state (cdr l))))))) ; The following would create warnings in MCL 4.2, presumably because this file ; is compiled in that Lisp; so we avoid it for MCL. It was originally in ; acl2-init.lisp, but cmulisp warned that *open-output-channel-key*, ; print-idate, and idate were undefined. #-(and mcl (not ccl)) (defun-one-output saved-build-date-string () (with-output-to-string (str) (setf (get 'tmp-channel *open-output-channel-key*) str) (print-idate (idate) 'tmp-channel *the-live-state*) (remprop 'tmp-channel *open-output-channel-key*) str)) (defun-one-output get-stobjs-out-for-declare-form (fn) ; Warning: Keep this in sync with stobjs-out. ; This function is used in acl2-fns.lisp. ; Here we essentially open-code stobjs-out, except that we allow for the ; possibility that fn is defined in raw Lisp. (cond ((eq fn 'cons) ; We call this function on cons so often we optimize it. '(nil)) ((member-eq fn '(if return-last)) (interface-er "Implementation error in ~ get-stobjs-out-for-declare-form: Attempted to find ~ stobjs-out for ~x0." fn)) (t (let ((w (w *the-live-state*))) (or (getprop fn 'stobjs-out nil 'current-acl2-world w) (and (getprop fn 'symbol-class nil 'current-acl2-world w) '(nil))))))) ; The definition of fix-trace and its subfunction fix-trace-untrace can go ; anywhere, but since they are raw Lisp, we will put them in this file. (defun fix-trace-untrace (new-trace-specs old-trace-specs) ; Collect functions traced in new-trace-specs that are not traced in ; old-trace-specs. (cond ((endp new-trace-specs) nil) ((assoc-eq (caar new-trace-specs) old-trace-specs) (fix-trace-untrace (cdr new-trace-specs) old-trace-specs)) (t (cons (caar new-trace-specs) (fix-trace-untrace (cdr new-trace-specs) old-trace-specs))))) (defun fix-trace (old-trace-specs) (let* ((new-trace-specs (f-get-global 'trace-specs *the-live-state*)) (to-untrace (fix-trace-untrace new-trace-specs old-trace-specs)) (to-retrace (set-difference-equal old-trace-specs new-trace-specs))) (when to-untrace (eval `(untrace$ ,@to-untrace))) (when to-retrace (eval `(trace$ ,@to-retrace))))) ; We define acl2-books-revision here rather than in acl2-init.lisp, so that ; f-get-global is already defined. (defun acl2-books-revision () ; We define acl2-books-revision here in interface-raw.lisp rather than in ; acl2-init.lisp, so that f-get-global is already defined. (multiple-value-bind (exit-code line) (system-call+ (concatenate 'string (f-get-global 'acl2-sources-dir *the-live-state*) "bin/svn-info-books") (list (f-get-global 'system-books-dir *the-live-state*))) (cond ((or (not (eql exit-code 0)) (equal line "")) "????") (t (svn-revision-from-line line))))) acl2-sources/ld.lisp0000664002132200015000000463450012222317024014052 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; This file, ld.lisp, provides the definition of the ACL2 macro ld, ; which implements both the ACL2 read-eval-print loop and the ACL2 ; file loader. (defun default-print-prompt (channel state) ; This is the default function for printing the ACL2 ld loop prompt. A typical ; prompt looks like: ACL2 !>, where the number of >'s indicates the ld-level. ; The prompt is printed by (fmt "~@0~sr ~@1~*2" a channel state nil), where a ; is an alist computed from current-package, ld-level, default-defun-mode, ; guard-checking-on, and ld-skip-proofsp, and #\r is bound to "" except for the ; #+:non-standard-analysis version, where it is bound to "(r)". To keep from ; consing up this alist every time, we memoize it, storing in 'prompt-memo the ; tuple (pkg level skipp defun-mode+ gc-on a), where defun-mode+ is the ; default-defun-mode except in raw-mode, where defun-mode+ is nil. Thus, if ; the current settings are as in the memo, we use the a in the memo. ; Otherwise, we compute and store a new memo. ; Warning: If you change the default prompt format, be sure to change it ; in eval-event-lst, where we print it by hand. ":Doc-Section Miscellaneous the default ~il[prompt] printed by ~ilc[ld]~/ ~bv[] Example prompt: ACL2 p!s> ~ev[] The ~il[prompt] printed by ACL2 displays the current package, followed by a space, followed by zero or more of the three ~il[characters] as specified below, followed by the character ~ilc[>] printed one or more times, reflecting the number of recursive calls of ~ilc[ld]. The three ~il[characters] in the middle are as follows: ~bv[] p ; when (default-defun-mode (w state)) is :program ! ; when guard checking is on s ; when (ld-skip-proofsp state) is t ~ev[] ~l[default-defun-mode], ~pl[set-guard-checking], and ~pl[ld-skip-proofsp].~/ Also ~pl[ld-prompt] to see how to install your own ~il[prompt]. Here are some examples with ~c[ld-skip-proofsp nil]. ~bv[] ACL2 !> ; logic mode with guard checking on ACL2 > ; logic mode with guard checking off ACL2 p!> ; program mode with guard checking on ACL2 p> ; program mode with guard checking off ~ev[] Here are some examples with ~ilc[default-defun-mode] of ~c[:]~ilc[logic]. ~bv[] ACL2 > ; guard checking off, ld-skip-proofsp nil ACL2 s> ; guard checking off, ld-skip-proofsp t ACL2 !> ; guard checking on, ld-skip-proofsp nil ACL2 !s> ; guard checking on, ld-skip-proofsp t ~ev[] Finally, here is the prompt in raw mode (~pl[set-raw-mode]), regardless of the settings above: ~bv[] ACL2 P> ~ev[]~/ " (let ((prompt-memo (and (f-boundp-global 'prompt-memo state) (f-get-global 'prompt-memo state)))) (cond ((and prompt-memo (equal (car prompt-memo) (f-get-global 'current-package state)) (equal (cadr prompt-memo) (f-get-global 'ld-level state)) (eq (caddr prompt-memo) (f-get-global 'ld-skip-proofsp state)) (eq (cadddr prompt-memo) (and (not (raw-mode-p state)) (default-defun-mode (w state)))) ; In the following, we could use iff instead of eq, because the dependence of ; defun-mode-prompt on (f-get-global 'guard-checking-on state) is restricted to ; whether or not the latter is nil/:none. But it's cheap to update the ; prompt-memo so we keep the more restrictive eq test for robustness, in case ; the code for defun-mode-prompt changes. (eq (car (cddddr prompt-memo)) (f-get-global 'guard-checking-on state))) (fmt1 "~@0~sr ~@1~*2" (cadr (cddddr prompt-memo)) 0 channel state nil)) (t (let ((alist (list (cons #\0 (f-get-global 'current-package state)) (cons #\1 (defun-mode-prompt-string state)) (cons #\2 (list "" ">" ">" ">" (make-list-ac (f-get-global 'ld-level state) nil nil))) (cons #\r #+:non-standard-analysis "(r)" #-:non-standard-analysis "")))) (pprogn (f-put-global 'prompt-memo (list (f-get-global 'current-package state) (f-get-global 'ld-level state) (f-get-global 'ld-skip-proofsp state) (and (not (raw-mode-p state)) (default-defun-mode (w state))) (not (gc-off state)) ; There is no need to memoize the binding of #\r for the purpose of checking if ; the prompt is current, since it never changes during a given session. Of ; course, #\r is bound in the alist. alist) state) (fmt1 "~@0~sr ~@1~*2" alist 0 channel state nil))))))) (defun print-prompt (prompt output-channel state) (with-output-forced output-channel (col state) (let ((prompt-fn (cond ((null prompt) nil) ((eq prompt t) (f-get-global 'prompt-function state)) (t prompt)))) (cond ((null prompt-fn) (mv 0 state)) ((eq prompt-fn 'default-print-prompt) (default-print-prompt output-channel state)) (t (mv-let (erp trans-ans state) (trans-eval (list prompt-fn (list 'quote output-channel) 'state) 'print-prompt state t) ; If erp is non-nil, trans-ans is of the form (stobjs-out . valx). We ; strongly expect that stobjs-out is (nil state). (That is true if ; prompt is in fact ld-prompt.) That being the case, we expect ; valx to be (col replaced-state). (cond ((or erp (not (and (equal (car trans-ans) '(nil state)) (integerp (car (cdr trans-ans)))))) (fmt1 "~%~%Bad Prompt~%See :DOC ld-prompt>" nil 0 output-channel state nil)) (t (mv (car (cdr trans-ans)) state))))))))) (defun initialize-timers (state) (pprogn (set-timer 'prove-time '(0) state) (set-timer 'print-time '(0) state) (set-timer 'proof-tree-time '(0) state) (set-timer 'other-time '(0) state))) (defun maybe-add-command-landmark (old-wrld old-default-defun-mode form trans-ans state) ; Old-wrld is the world before the trans-evaluation of form. That ; trans-evaluation returned trans-ans, which is thus of the form (stobjs-out ; . valx). If valx contains a state (then it must in fact contain the state ; state), and the current world of that state is different from old-wrld and ; does not end with a command landmark, we add a command landmark for form. ; We pass in old-default-defun-mode as the default-defun-mode of old-wrld. ; This way, we can compute that value at a time that old-wrld is still ; installed, so that the corresponding getprop will be fast. (let ((wrld (w state))) (cond ((and (member-eq 'state (car trans-ans)) (not (and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value))) (not (equal old-wrld wrld))) (er-progn (get-and-chk-last-make-event-expansion ; For purposes of tracking make-event, we allow time$ only at the top level. ; If there is user demand, we could consider allowing it in arbitrary positions ; of embedded event forms, though in that case we should be careful to check ; that nested calls work well. Note that we look for time$, not for ; return-last, because we are looking at a user-supplied form, not its ; macroexpansion. (cond ((consp form) (case (car form) (time$ (cadr form)) (otherwise form))) (t form)) wrld 'top-level state (primitive-event-macros)) (pprogn (cond ((raw-mode-p state) ; If we are in raw mode, then it is scary to imagine that we have changed the ; logical world. (warning$ 'top-level "Raw" "The ACL2 world is being modified while in raw ~ mode. See :DOC set-raw-mode. Further ~ computation in this ACL2 session may have some ~ surprising results.")) (t state)) (set-w 'extension (add-command-landmark old-default-defun-mode form (f-get-global 'connected-book-directory state) (f-get-global 'last-make-event-expansion state) wrld) state) (value nil)))) (t (value nil))))) (defun replace-last-cdr (x val) (cond ((atom x) val) ((atom (cdr x)) (cons (car x) val)) (t (cons (car x) (replace-last-cdr (cdr x) val))))) (defun ld-standard-oi-missing (val file-name ld-missing-input-ok ctx state) (cond ((eq ld-missing-input-ok t) (value nil)) (t (let ((msg (msg "~@0 It is likely that the file you requested, ~ ~x1, does not exist." (msg *ld-special-error* 'standard-oi val) file-name))) (cond (ld-missing-input-ok ; not t, so :warn (pprogn (warning$ ctx "ld-missing-input" "~@0" msg) (value nil))) (t (er soft ctx "~@0" msg))))))) (defun chk-acceptable-ld-fn1-pair (pair ld-missing-input-ok ctx state co-string co-channel) ; We check that pair, which is of the form (var . val) where var is a symbolp, ; specifies a legitimate "binding" for the LD special var. This means that we ; check that var is one of the state globals that LD appears to bind (i.e., ; push and pop in an unwind-protected way) and that val is a reasonable value ; of that global. For example, 'standard-oi is an LD special but must be bound ; to a true-list of objects or an open object input channel. ; Co-string and co-channel are here to provide a very subtle feature of LD. If ; the same string is specified for both standard-co and proofs-co then we open ; one channel and use it in both places. Our caller, chk-acceptable-ld-fn1, is ; responsible for maintaining these two accumulators as we map down the list of ; pairs. It puts into co-string and co-channel the string and returned channel ; for the first of standard-co or proofs-co encountered. (let ((var (car pair)) (val (cdr pair))) ; The first three LD specials, namely the three channels, are special because ; we may have to open a channel and create a new pair. Once we get past those ; three, we can just use the standard checkers and return the existing pair. (case var (standard-oi (cond ((and (symbolp val) (open-input-channel-p val :object state)) (value pair)) ((true-listp val) (value pair)) ((stringp val) (let ((file-name (extend-pathname (f-get-global 'connected-book-directory state) val state))) (mv-let (ch state) (open-input-channel file-name :object state) (cond (ch (value (cons 'standard-oi ch))) (t (ld-standard-oi-missing val file-name ld-missing-input-ok ctx state)))))) ((consp val) (let ((last-cons (last val))) (cond ((and (symbolp (cdr last-cons)) (open-input-channel-p (cdr last-cons) :object state)) (value pair)) ((stringp (cdr last-cons)) (let ((file-name (extend-pathname (f-get-global 'connected-book-directory state) (cdr last-cons) state))) (mv-let (ch state) (open-input-channel file-name :object state) (cond (ch (value (cons 'standard-oi (replace-last-cdr val ch)))) (t (ld-standard-oi-missing val file-name ld-missing-input-ok ctx state)))))) (t (er soft ctx *ld-special-error* 'standard-oi val))))) (t (er soft ctx *ld-special-error* 'standard-oi val)))) (standard-co (cond ((and (symbolp val) (open-output-channel-p val :character state)) (value pair)) ((equal val co-string) (value (cons 'standard-co co-channel))) ((stringp val) (mv-let (ch state) (open-output-channel (extend-pathname (f-get-global 'connected-book-directory state) val state) :character state) (cond (ch (value (cons 'standard-co ch))) (t (er soft ctx *ld-special-error* 'standard-co val))))) (t (er soft ctx *ld-special-error* 'standard-co val)))) (proofs-co (cond ((and (symbolp val) (open-output-channel-p val :character state)) (value pair)) ((equal val co-string) (value (cons 'proofs-co co-channel))) ((stringp val) (mv-let (ch state) (open-output-channel (extend-pathname (f-get-global 'connected-book-directory state) val state) :character state) (cond (ch (value (cons 'proofs-co ch))) (t (er soft ctx *ld-special-error* 'proofs-co val))))) (t (er soft ctx *ld-special-error* 'proofs-co val)))) (current-package (er-progn (chk-current-package val ctx state) (value pair))) (ld-skip-proofsp (er-progn (chk-ld-skip-proofsp val ctx state) (value pair))) (ld-redefinition-action (er-progn (chk-ld-redefinition-action val ctx state) (value pair))) (ld-prompt (er-progn (chk-ld-prompt val ctx state) (value pair))) (ld-missing-input-ok (er-progn (chk-ld-missing-input-ok val ctx state) (value pair))) (ld-pre-eval-filter (er-progn (chk-ld-pre-eval-filter val ctx state) (value pair))) (ld-pre-eval-print (er-progn (chk-ld-pre-eval-print val ctx state) (value pair))) (ld-post-eval-print (er-progn (chk-ld-post-eval-print val ctx state) (value pair))) (ld-evisc-tuple (er-progn (chk-evisc-tuple val ctx state) (value pair))) (ld-error-triples (er-progn (chk-ld-error-triples val ctx state) (value pair))) (ld-error-action (er-progn (chk-ld-error-action val ctx state) (value pair))) (ld-query-control-alist (er-progn (chk-ld-query-control-alist val ctx state) (value pair))) (ld-verbose (er-progn (chk-ld-verbose val ctx state) (value pair))) (otherwise (er soft ctx "The variable ~x0 is not an authorized LD special and ~ hence cannot be bound by LD." var))))) (defun close-channels (channel-closing-alist state) ; It is necessary to close the channels that we open. We must in fact ; record them somewhere in state so that if we abort LD with a hard error or ; user interrupt that throws us into the unwind-protect code of LP, they are ; still closed. To enable such "remote closings" we invent the notion of a ; "channel closing alist" which is an alist that pairs opened channels to ; their "types", where a type is either 'oi (object input) or 'co (character ; output). Given such an alist we close each channel in it, if the channel ; is in fact open. (cond ((null channel-closing-alist) state) (t (pprogn (cond ((eq (cdar channel-closing-alist) 'oi) (cond ((open-input-channel-p (caar channel-closing-alist) :object state) (close-input-channel (caar channel-closing-alist) state)) (t state))) ((eq (cdar channel-closing-alist) 'co) (cond ((open-output-channel-p (caar channel-closing-alist) :character state) (close-output-channel (caar channel-closing-alist) state)) (t state))) (t (let ((temp (er hard 'close-channels "The channel ~x0 was tagged with an unimplemented ~ channel type, ~x1." (caar channel-closing-alist) (cdar channel-closing-alist)))) (declare (ignore temp)) state))) (close-channels (cdr channel-closing-alist) state))))) (defun chk-acceptable-ld-fn1 (alist ld-missing-input-ok ctx state co-string co-channel new-alist channel-closing-alist) ; We copy alist (reversing it) onto new-alist, checking that each pair in it ; binds an LD special to a legitimate value. We open the requested files as we ; go and replace the file names with the open channels. We also accumulate ; into channel-closing-alist the pairs necessary to close (with close-channels) ; the channels we have opened. We return a pair consisting of the new-alist ; and the final channel-closing-alist. See chk-acceptable-ld-fn1-pair for an ; explanation of co-string and co-channel. ; Implementation Note: This odd structure has the single redeeming feature that ; if any given pair of alist causes an error, we have in our hands enough ; information to close any channels we might have opened thus far. If we get ; all the way down alist without causing an error, the channel-closing-alist ; will be used in the acl2-unwind-protect cleanup form and enable us to "close ; on pop" -- which was its original purpose. But an earlier coding of this ; function suffered from the problem that we could open several channels and ; then, right here, cause an error (e.g., the proposed 'current-package setting ; is bad). If that happened, those open channels would never be closed. It is ; still possible to "lose" an opened channel: abort this function after some ; files have been opened. ; This flaw cannot be fixed, at least with the current set of primitives. To ; close a channel we must have the channel. We don't have the channel until ; after we have opened it, i.e., the way we get our hands on a channel in ACL2 ; is to open a file, but the way we close a channel is to call ; close-output-channel on the channel object (rather than the file). Thus, ; there is no way we can unwind protect code that opens a channel so as to ; guarantee to close the channel because we can't get the object we are to ; "cleanup" (the channel) until after we have "modified" (opened) it. So there ; is a window of vulnerability between the time we open the channel and the ; time we stash it away in some location known to our cleanup form. During ; that window an abort can cause us to lose a channel in the sense that we do ; not close it. Now we can make that window much smaller than it is now. As ; things stand now we are vulnerable to aborts from the time we start ; processing alist here until we finish and enter the acl2-unwind-protect in ; ld-fn that "binds" the ld specials. But all this vulnerability means is that ; lisp fails to close some opened channels during an abort. If such a thing ; happens, the user could detect it with some poking around. For example, he ; could just type ; (open-output-channel-p 'ACL2-OUTPUT-CHANNEL::STANDARD-CHARACTER-OUTPUT-i ; :character state) ; for a bunch of i starting at 0 and see if there are some he doesn't know ; about. This is not a catastrophic error. It is as though the abort placed ; in the open-output-channels field of the state an additional channel or two. ; The only way, as far as we can see, that this can be a problem is in the ; sense of resource exhaustion: operating systems (and thus lisps) generally ; allow a finite number of open channels. ; If we someday endeavor to plug this hole some additional care must be taken ; because the act of opening an ACL2 channel (in raw lisp) is non-atomic -- we ; have to open the stream, generate a channel symbol, and store some stuff on ; the property list of the symbol. So an abort there can cause an ; irretrievable loss of an open channel unless the problem is addressed down ; there as well. ; Finally we would just like to note that soft errors are handled perfectly ; here in the sense that if some channels are opened and then we get a soft ; error, we close the channels. And aborts are handled perfectly once we get ; outside of the window of vulnerability discussed. (cond ((null alist) (let ((new-alist (cond ((eq ld-missing-input-ok :missing) (put-assoc-eq 'ld-verbose nil (put-assoc-eq 'ld-prompt nil new-alist))) (t new-alist)))) (value (cons new-alist channel-closing-alist)))) (t (mv-let (erp pair state) (chk-acceptable-ld-fn1-pair (car alist) ld-missing-input-ok ctx state co-string co-channel) (cond (erp (pprogn (close-channels channel-closing-alist state) (mv t nil state))) (t (mv-let (pair ld-missing-input-ok) (cond ((null pair) (assert$ (eq (caar alist) 'standard-oi) (mv (cons 'standard-oi nil) :missing))) (t (mv pair ld-missing-input-ok))) (chk-acceptable-ld-fn1 (cdr alist) ld-missing-input-ok ctx state (cond ((and (null co-string) (or (eq (car pair) 'standard-co) (eq (car pair) 'proofs-co)) (stringp (cdr (car alist)))) (extend-pathname (f-get-global 'connected-book-directory state) (cdr (car alist)) state)) (t co-string)) (cond ((and (null co-channel) (or (eq (car pair) 'standard-co) (eq (car pair) 'proofs-co)) (stringp (cdr (car alist)))) (cdr pair)) (t co-channel)) (cons pair new-alist) (cond ((eq (car pair) 'standard-oi) (cond ((stringp (cdr (car alist))) (cons (cons (cdr pair) 'oi) channel-closing-alist)) ((and (consp (cdr (car alist))) (stringp (cdr (last (cdr (car alist)))))) (cons (cons (cdr (last (cdr pair))) 'oi) channel-closing-alist)) (t channel-closing-alist))) ((and (or (eq (car pair) 'standard-co) (eq (car pair) 'proofs-co)) (stringp (cdr (car alist)))) (cons (cons (cdr pair) 'co) channel-closing-alist)) (t channel-closing-alist)))))))))) (defun chk-acceptable-ld-fn (alist state) ; Alist is an alist that pairs LD specials with proposed values. We check ; that those values are legitimate and that only authorized LD specials are ; bound. If strings are supplied for the specials standard-oi, standard-co, ; and proofs-co, we open corresponding channels and put those channels in ; for the values in the alist. We return a pair consisting of the modified ; alist and a channel closing alist that pairs opened channels with the ; type information it takes to close them. (let ((ctx 'ld)) (er-progn (cond ((or (null (f-boundp-global 'current-acl2-world state)) (null (w state))) (er soft ctx "The theorem prover's database has not yet been initialized. To ~ initialize ACL2 to its full theory, which currently takes about 3 ~ minutes on a Sparc 2 (Dec. 1992), invoke (initialize-acl2) from ~ Common Lisp.")) (t (value nil))) (cond ((symbol-alistp alist) (value nil)) (t (er soft ctx "The argument to ld-fn must be a symbol-alistp and ~x0 is ~ not." alist))) (cond ((assoc-eq 'standard-oi alist) (value nil)) (t (er soft ctx "The alist argument to ld-fn must specify a value ~ for 'standard-oi and ~x0 does not." alist))) (cond ((not (duplicate-keysp-eq alist)) (value nil)) (t (er soft ctx "The alist argument to ld-fn must contain no duplications ~ among the LD specials to be bound. Your alist contains ~ duplicate values for ~&0." (duplicates (strip-cars alist))))) (chk-acceptable-ld-fn1 alist (cdr (assoc-eq 'ld-missing-input-ok alist)) ctx state nil nil nil nil)))) (defun f-put-ld-specials (alist state) ; Alist is an alist that pairs LD specials with their new values. We ; f-put-global each special. Because f-put-global requires an explicitly ; quoted variable, we case split on the authorized LD-specials. This is ; easier and safer than making translate give us special treatment. To add ; a new LD-special you must change this function, as well as ; f-get-ld-specials and the checker chk-acceptable-ld-fn1-pair. ; Warning: Somebody else better have checked that the values assigned are ; legitimate. For example, we here set 'current-package to whatever we are ; told to set it. This is not a function the user should call! (cond ((null alist) state) (t (pprogn (case (caar alist) (standard-oi (f-put-global 'standard-oi (cdar alist) state)) (standard-co (f-put-global 'standard-co (cdar alist) state)) (proofs-co (f-put-global 'proofs-co (cdar alist) state)) (current-package (f-put-global 'current-package (cdar alist) state)) (ld-skip-proofsp (f-put-global 'ld-skip-proofsp (cdar alist) state)) (ld-redefinition-action (f-put-global 'ld-redefinition-action (cdar alist) state)) (ld-prompt (f-put-global 'ld-prompt (cdar alist) state)) (ld-missing-input-ok (f-put-global 'ld-missing-input-ok (cdar alist) state)) (ld-pre-eval-filter (f-put-global 'ld-pre-eval-filter (cdar alist) state)) (ld-pre-eval-print (f-put-global 'ld-pre-eval-print (cdar alist) state)) (ld-post-eval-print (f-put-global 'ld-post-eval-print (cdar alist) state)) (ld-evisc-tuple (f-put-global 'ld-evisc-tuple (cdar alist) state)) (ld-error-triples (f-put-global 'ld-error-triples (cdar alist) state)) (ld-error-action (f-put-global 'ld-error-action (cdar alist) state)) (ld-query-control-alist (f-put-global 'ld-query-control-alist (cdar alist) state)) (ld-verbose (f-put-global 'ld-verbose (cdar alist) state)) (otherwise (let ((x (er hard 'f-put-ld-specials "Someone is using ~x0 as an unauthorized LD-special." (caar alist)))) (declare (ignore x)) state))) (f-put-ld-specials (cdr alist) state))))) (defun f-get-ld-specials (state) ; Make an alist, suitable for giving to f-put-ld-specials, that records the ; current values of all LD-specials. To add a new LD-special you must ; change this function, f-put-ld-specials, and the checker ; chk-acceptable-ld-fn1-pair. (list (cons 'standard-oi (f-get-global 'standard-oi state)) (cons 'standard-co (f-get-global 'standard-co state)) (cons 'proofs-co (f-get-global 'proofs-co state)) (cons 'current-package (f-get-global 'current-package state)) (cons 'ld-skip-proofsp (f-get-global 'ld-skip-proofsp state)) (cons 'ld-redefinition-action (f-get-global 'ld-redefinition-action state)) (cons 'ld-prompt (f-get-global 'ld-prompt state)) (cons 'ld-missing-input-ok (f-get-global 'ld-missing-input-ok state)) (cons 'ld-pre-eval-filter (f-get-global 'ld-pre-eval-filter state)) (cons 'ld-pre-eval-print (f-get-global 'ld-pre-eval-print state)) (cons 'ld-post-eval-print (f-get-global 'ld-post-eval-print state)) (cons 'ld-evisc-tuple (f-get-global 'ld-evisc-tuple state)) (cons 'ld-error-triples (f-get-global 'ld-error-triples state)) (cons 'ld-error-action (f-get-global 'ld-error-action state)) (cons 'ld-query-control-alist (f-get-global 'ld-query-control-alist state)) (cons 'ld-verbose (f-get-global 'ld-verbose state)))) (defun ld-read-keyword-command1 (n state) (cond ((= n 0) (value nil)) (t (mv-let (eofp obj state) (read-standard-oi state) (cond (eofp (er soft 'ld-read-keyword-command "Unfinished keyword command at eof on (standard-oi ~ state).")) (t (er-let* ((rst (ld-read-keyword-command1 (1- n) state))) ; Note: We take advantage of the fact that this function ALWAYS returns a list ; of quoted objects. See the call of strip-cadrs in ld-read-keyword-command ; below. So if you optmize away some of the quotes, beware! (value (cons (list 'quote obj) rst))))))))) (defun exit-ld (state) ; This is the function most commonly aliased to the keyword command :q. Its ; evaluation causes LD to terminate immediately. Any function that returns ; three results, the first of which is nil, the second of which is :q and the ; third of which is STATE will do the same. (value :q)) (defun macro-minimal-arity1 (lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) 0) ((lambda-keywordp (car lst)) 0) (t (1+ (macro-minimal-arity1 (cdr lst)))))) (defun macro-minimal-arity (sym default wrld) (let ((args (getprop sym 'macro-args default 'current-acl2-world wrld))) (macro-minimal-arity1 (if (eq (car args) '&whole) (cddr args) args)))) (defun ld-read-keyword-command (key state) ; ld supports the convention that when a keyword :key is typed ; as a command and the corresponding symbol in the "ACL2" package, ; ACL2::key is a function or macro of arity n, we read n more ; objects, quote them, and apply the ACL2 function or macro. ; Thus, ; MY-PKG !>:ubt foo ; is the same thing as ; MY-PKG !>(ACL2::UBT 'foo) ; We require that the macro not have any lambda keyword arguments, since ; that makes it hard or impossible to determine how many things we should ; read. ; We also support the convention that if :key is bound on 'ld-keyword-aliases ; in state, say in the entry (:key n fn), we manufacture (fn 'x1 ... 'xn) ; instead of requiring that key be a function and returning (key 'x1 ... 'xn). ; This function returns four results, (mv erp keyp form state). If erp is t an ; error was caused and the message has been printed. Otherwise, keyp is ; non-nil or nil according to whether the keyword hack was involved. Form is ; the parsed form of the command read, e.g., (acl2::ubt 'foo). If non-nil, ; keyp is the actual list of objects read, e.g., (:ubt foo). (let ((temp (assoc-eq key (ld-keyword-aliases state)))) (cond (temp (mv-let (erp args state) (ld-read-keyword-command1 (cadr temp) state) (cond (erp (mv t nil nil state)) (t (mv nil (cons key (strip-cadrs args)) (cons (caddr temp) args) state))))) ((eq key :q) ; Here is the only place we recognize :q as a special command. Essentially :q ; is an alias for (exit-ld state) except it is overridden by any other aliases ; for :q. (mv nil '(:q) '(exit-ld state) state)) (t (let* ((sym (intern (symbol-name key) "ACL2")) (wrld (w state)) (len (cond ((function-symbolp sym wrld) (length (formals sym wrld))) ((getprop sym 'macro-body nil 'current-acl2-world wrld) (macro-minimal-arity sym `(:error "See LD-READ-KEYWORD-COMMAND.") wrld)) (t nil)))) (cond (len (mv-let (erp args state) (ld-read-keyword-command1 len state) (cond (erp (mv t nil nil state)) (t (mv nil (cons key (strip-cadrs args)) (cons sym args) state))))) (t (mv-let (erp val state) (er soft 'LD "Unrecognized keyword command ~x0." key) (declare (ignore erp val)) (mv t nil nil state))))))))) (defun ld-read-command (state) ; This function reads an ld command from the standard-oi channel of state and ; returns it. It implements the keyword command hack. We return five results: ; (mv eofp erp keyp form state). Eofp means we exhausted standard-oi. Erp, ; when t, indicates that an error occurred, e.g., an ill-formed keyword command ; was read. The error message has been printed. Keyp, when non-nil, indicates ; that form is the parsed form of a keyword command. The list of objects ; actually read is the non-nil value of keyp and that list, without the ; enclosing parentheses, should be printed instead of form. Thus, if :kons is ; an alias for cons, then :kons x y will parse into (cons 'x 'y) and keyp will ; be (:kons x y). (mv-let (eofp val state) (read-standard-oi state) (pprogn (cond ((int= (f-get-global 'ld-level state) 1) (let ((last-index (iprint-last-index state))) (cond ((> last-index (iprint-soft-bound state)) (rollover-iprint-ar nil last-index state)) (t state)))) (t state)) (cond (eofp (mv t nil nil nil state)) ((keywordp val) (mv-let (erp keyp form state) (ld-read-keyword-command val state) (mv nil erp keyp form state))) ((stringp val) (let ((upval (string-upcase val))) (cond ((find-non-hidden-package-entry upval (global-val 'known-package-alist (w state))) (mv nil nil nil `(in-package ,upval) state)) (t (mv nil nil nil val state))))) (t (mv nil nil nil val state)))))) (deflabel acl2-customization :doc ":Doc-Section switches-parameters-and-modes file of initial commands for ACL2 to run at ~il[startup]~/ ACL2 provides a mechanism to load automatically a so-called ``ACL2 customization file,'' via ~ilc[ld], the first time ~ilc[lp] is called in an ACL2 session. ACL2 looks for this file as follows. ~bq[] o If the host Lisp reads a non-empty value for the system's environment variable ~c[ACL2_CUSTOMIZATION], then that string value is used for the customization file name. In this case, if the file does not exist or if the string is \"NONE\" then there is no customization file. Notes. (1) If the customization file name is a relative pathname (~pl[pathname]), then the pathname is considered relative to the connected book directory (~pl[cbd]). (2) If this variable is not already defined, then its value is set to ~c[NONE] when the ACL2 makefile system is invoked (specifically, using community books file ~c[books/Makefile-generic]), e.g., for a regression. o Otherwise (empty environment variable value), file ~c[\"acl2-customization.lsp\"] or ~c[\"acl2-customization.lisp\"] on the connected book directory (~pl[cbd]), generally the current directory, is the customization file (in that order) if either exists. o Otherwise file ~c[\"acl2-customization.lsp\"] or ~c[\"acl2-customization.lisp\"] on your home directory is the customization file (in that order), if either exists (except, this case is skipped on Windows operating systems.~eq[] Except for the fact that this ~ilc[ld] command is not typed explicitly by you, it is a standard ~ilc[ld] command, with one exception: any settings of ~ilc[ld] specials are remembered once this call of ~ilc[ld] has completed. For example, suppose that you start your customization file with ~c[(set-ld-skip-proofsp t state)], so that proofs are skipped as it is loaded with ~ilc[ld]. Then the ~ilc[ld] special ~ilc[ld-skip-proofsp] will remain ~c[t] after the ~ilc[ld] has completed, causing proofs to be skipped in your ACL2 session, unless your customization file sets this variable back to ~c[nil], say with ~c[(set-ld-skip-proofsp nil state)].~/ If the customization file exists, it is loaded with ~ilc[ld] using the usual default values for the ~ilc[ld] specials (~pl[ld]). Thus, if an error is encountered, no subsequent forms in the file will be evaluated. To create a customization file it is recommended that you first give it a name other than ~c[\"acl2-customization.lsp\"] or ~c[\"acl2-customization.lisp\"] so that ACL2 does not try to include it prematurely when you next enter ~ilc[lp]. Then, while in the uncustomized ~ilc[lp], explicitly invoke ~ilc[ld] on your evolving (but renamed) customization file until all forms are successfully evaluated. The same procedure is recommended if for some reason ACL2 cannot successfully evaluate all forms in your customization file: temporarily rename your customization file so that ACL2 does not try to ~ilc[ld] it automatically and then debug the new file by explicit calls to ~ilc[ld]. WARNING! If you certify a book after the (automatic) loading of a customization file, the forms in that file will be part of the ~il[portcullis] of the ~il[books] you certify! That is, the forms in your customization file at certification time will be loaded whenever anybody uses the ~il[books] you are certifying. Since customization files generally contain idiosyncratic ~il[command]s, you may not want yours to be part of the ~il[books] you create for others. Thus, if you have a customization file then you may want to invoke ~c[:]~ilc[ubt]~c[ 1] before certifying any ~il[books]; alternatively, ~pl[certify-book!] for automatic invocation of ~ilc[ubt]. On the other hand, if you wish to prevent undoing commands from the customization file, ~pl[reset-prehistory]. Finally, we note that except on Windows-based systems, if there is a file ~c[acl2-init.lsp] in your home directory, then it will be loaded into raw Lisp when ACL2 is invoked.~/ :cited-by Programming") (deflabel keyword-commands :doc ":Doc-Section Miscellaneous how keyword commands are processed~/ ~bv[] Examples: user type-in form evaluated :pc 5 (ACL2::PC '5) :pcs app rev (ACL2::PCS 'app 'rev) :length (1 2 3) (ACL2::LENGTH '(1 2 3)) :quit (ACL2::QUIT) ; Note: avoid optional argument ~ev[]~/ When a keyword, ~c[:key], is read as a command, ACL2 determines whether the symbol with the same name in the ~c[\"ACL2\"] package, ~c[acl2::key], is a function or simple macro of n arguments. If so, ACL2 reads ~c[n] more objects, ~c[obj1], ..., ~c[objn], and then acts as though it had read the following form (for a given ~c[key]): ~bv[] (ACL2::key 'obj1 ... 'objn) ~ev[] Thus, by using the keyword command hack you avoid typing the parentheses, the ~c[\"ACL2\"] package name, and the quotation marks. ~l[ld-keyword-aliases] for how to customize this behavior. Note the generality of this hack. Any function or macro in the ~c[\"ACL2\"] package can be so invoked, not just ``commands.'' Indeed, there is no such thing as a distinguished class of commands. Users may take advantage of the keyword command hack by defining functions and macros in the ~c[\"ACL2\"] package. The one caveat is that when the keyword hack is used to invoke a macro, only the required arguments for that macro are read before calling that macro: none of the ~c[&optional], ~c[&rest], ~c[&body], or ~c[&key] arguments are read for that call. The macro is thus called with only its required arguments. The following log illustrates this caveat. ~bv[] ACL2 !>:set-iprint t ACL2 Query (:SET-IPRINT): Action (T, NIL, RESET, RESET-ENABLE, SAME, Q or ?): ACL2 Observation in SET-IPRINT: Iprinting has been enabled. ACL2 !> ~ev[] What happened? First, the command ~c[:set-iprint] was read. Since the macro ~ilc[set-iprint] has no required arguments, the ACL2 evaluator was then called on the form ~c[(set-iprint)], that is, calling the macro on no arguments. ~c[Set-iprint] is defined to query the ACL2 user when its first argument is omitted. The log shows that query, which is set up to read the next form from the input stream. That form was available immediately: the form ~c[t] that had been supplied by the user. So the query returned immediately and the ~c[set-iprint] call was completed.~/") (defun ld-print-command (keyp form col state) (with-base-10 (mv-let (col state) (cond ((not (eq (ld-pre-eval-print state) t)) (mv col state)) (keyp (fmt1 "~*0~|" (list (cons #\0 (list "" "~x*" "~x* " "~x* " keyp))) col (standard-co state) state (ld-evisc-tuple state))) (t (fmt1 "~q0~|" (list (cons #\0 form)) col (standard-co state) state (ld-evisc-tuple state)))) (declare (ignore col)) state))) (defun ld-filter-command (form state) (let ((filter (ld-pre-eval-filter state))) (cond ((eq filter :all) (value t)) ((eq filter :query) (acl2-query :filter '("~#0~[~Y12?~/Eval?~]" :y t :n nil :r :return :q :error :? ("We are in the LD read-eval-print loop, ~ processing the forms in standard-oi. The ~ form printed above is one of those forms. Do ~ you want to evaluate it (Y) or not (N)? You ~ may also answer R, meaning ``return ~ immediately from LD (without reading or ~ evaluating any more forms)'' or Q meaning ~ ``return immediately from LD, signalling an ~ error.''" :y t :n nil :r :return :q :error)) (list (cons #\0 (if (eq (ld-pre-eval-print state) t) 1 0)) (cons #\1 form) (cons #\2 (ld-evisc-tuple state))) state)) (t (value t))))) #-acl2-loop-only (defun-one-output ppr? (x raw-x col channel state) (cond ((and (raw-mode-p state) (bad-lisp-objectp x)) (if (not (eq channel *standard-co*)) (error "Attempted to print LD results to other than *standard-co*!")) (format t "[Note: Printing non-ACL2 result.]") (terpri) (prin1 raw-x) state) (t (ppr x col channel state t)))) (defun ld-print-results (trans-ans state) ; This is the function used by ld to print the results of the ; trans-evaluation of the form read. Trans-ans is of the form ; (stobjs-out . valx). ; If ld-post-eval-print is nil we print nothing. If it is t, we ; print with the standard evisceration (ld-evisc-tuple). If it is ; :command-conventions, we hide error/value/state pairs by just printing ; value and we don't print anyting when the value is :invisible. (let ((flg (ld-post-eval-print state)) (output-channel (standard-co state))) ; In raw mode in Allegro Common Lisp (and not GCL, but perhaps other lisps), ; evaluation of (time ...) causes the result value to be printed at the end of ; a comment line printed by time, which is unfortunate. This sort of printing ; problem does not seem to have come up in other than raw mode, and besides, we ; do not want to try to model this sort of maybe-newline printing in the ; logic. So we restrict this solution to raw mode. Furthermore, the lisps ; listed below do not need this fix, and they all print a newline even with ; "~&" when apparently not necessary, so we exclude them from this fix. #-(or acl2-loop-only gcl cmu sbcl lispworks ccl) (when (raw-mode-p state) (format (get-output-stream-from-channel output-channel) "~&")) (cond ((null flg) state) (t (let* ((stobjs-out (car trans-ans)) (valx (cdr trans-ans)) (evisc-tuple (ld-evisc-tuple state)) (evisc-alist (world-evisceration-alist state (car evisc-tuple))) (print-level (cadr evisc-tuple)) (print-length (caddr evisc-tuple))) (mv-let (eviscerated-valx state) (eviscerate-stobjs-top (evisceration-stobj-marks stobjs-out nil) valx print-level print-length evisc-alist (table-alist 'evisc-table (w state)) nil state) (cond ((and (eq flg :command-conventions) (ld-error-triples state) (equal stobjs-out *error-triple-sig*)) ; We get here if we are following command-conventions and the form ; returned triple (mv erp val state). Note that erp must be a ; non-stobj (typically a Boolean) but that val may be a stobj or not. (cond ((eq (cadr valx) :invisible) state) (t (pprogn (princ$ (if (stringp (f-get-global 'triple-print-prefix state)) (f-get-global 'triple-print-prefix state) "") output-channel state) ; The following raw code is identical to the logic code below except that the ; raw code handles infix printing, which is, at the moment, entirely ; extra-logical. #-acl2-loop-only (let ((col (if (stringp (f-get-global 'triple-print-prefix state)) (length (f-get-global 'triple-print-prefix state)) 0)) (evg (cadr eviscerated-valx))) (cond ((and (live-state-p state) (output-in-infixp state)) (print-infix evg nil (- (fmt-hard-right-margin state) col) 0 col (get-output-stream-from-channel output-channel) t) *the-live-state*) (t (ppr? evg (cadr valx) col output-channel state)))) #+acl2-loop-only (ppr (cadr eviscerated-valx) (if (stringp (f-get-global 'triple-print-prefix state)) (length (f-get-global 'triple-print-prefix state)) 0) output-channel state t) (newline output-channel state))))) (t (pprogn #-acl2-loop-only (cond ((and (live-state-p state) (output-in-infixp state)) (print-infix eviscerated-valx nil (fmt-hard-right-margin state) 0 0 (get-output-stream-from-channel output-channel) t) *the-live-state*) (t (ppr? eviscerated-valx valx 0 output-channel state))) #+acl2-loop-only (ppr eviscerated-valx 0 output-channel state t) (newline output-channel state)))))))))) (defun ld-print-prompt (state) ; Like print-prompt except may print the prompt both to *standard-co* ; and (standard-co state). (mv-let (col state) (print-prompt (ld-prompt state) (standard-co state) state) (cond ((and (eq (standard-oi state) *standard-oi*) (not (eq (standard-co state) *standard-co*))) (mv-let (irrel-col state) (print-prompt (ld-prompt state) *standard-co* state) (declare (ignore irrel-col)) (mv col state))) (t (mv col state))))) (defun good-bye-fn (status) (declare (xargs :mode :logic :guard t)) #-acl2-loop-only (exit-lisp (ifix status)) status) (defmacro good-bye (&optional (status '0)) ":Doc-Section Other quit entirely out of Lisp~/ ~bv[] Examples: ACL2 !>(good-bye) ; [ACL2 is exited] ACL2 !>(good-bye 3) ; [ACL2 is exited with Unix exit status 3] ~ev[] Note: Your entire session will disappear forever when you evaluate ~c[(good-bye)].~/ The command ~c[(good-bye)] quits not only out of the ACL2 ~il[command] loop, but in fact quits entirely out of the underlying Lisp. Thus, there is no going back! You will ~st[not] be able to re-enter the ~il[command] loop after typing ~c[(good-bye)]! All your work will be lost!!! This command may not work in some underlying Common Lisp implementations. In such cases, there is no harm in trying; ACL2 will let you know how to proceed if it cannot exit. In some systems, typing ~c[control-d] at the top-level ACL2 prompt (~c[control-c control-d] if inside emacs) will call this function. If you give ~c[good-bye] an argument, it should be a natural number, and it indicates the Unix (Linux) exit status. If you merely want to exit the ACL2 ~il[command] loop, use ~c[:q] instead (~pl[q])." (declare (xargs :guard (natp status))) `(good-bye-fn ,status)) (defun ld-return-error (state) (let ((action (ld-error-action state))) (cond ((eq action :return!) (mv :return (list :stop-ld (f-get-global 'ld-level state)) state)) (t (mv action :error state))))) (defun initialize-accumulated-warnings () #-acl2-loop-only (setq *accumulated-warnings* nil) nil) (defun ld-read-eval-print (state) ; This is LD's read-eval-print step. We read a form from standard-oi, eval it, ; and print the result to standard-co, will lots of bells and whistles ; controlled by the various LD specials. The result of this function is a ; triple (mv signal val state), where signal is one of :CONTINUE, :RETURN, or ; :ERROR. When the signal is :continue or :error, val is irrelevant. When the ; signal is :return, val is the "reason" we are terminating and is one of ; :exit, :eof, :error, :filter, or (:stop-ld n) where n is the ld-level at the ; time of termination. (pprogn (cond ((<= (f-get-global 'ld-level state) 1) (pprogn (f-put-global 'trace-level 0 state) (print-deferred-ttag-notes-summary state))) (t state)) (mv-let (col state) (ld-print-prompt state) (mv-let (eofp erp keyp form state) (ld-read-command state) (cond (eofp (cond ((ld-prompt state) (pprogn (princ$ "Bye." (standard-co state) state) (newline (standard-co state) state) ; In versions before v2-8, typing ctrl-d (ctrl-c ctrl-d in Emacs) did not ; immediately kill the Lisp if the resulting eof condition was detected by BRR ; processing. The code below fixes that; let's hope it doesn't "fix" anything ; else! (prog2$ (and (equal (standard-oi state) *standard-oi*) (good-bye)) state) (mv :return :eof state))) (t (mv :return :eof state)))) (erp (ld-return-error state)) (t (pprogn (ld-print-command keyp form col state) (mv-let (erp ans state) (ld-filter-command form state) (cond (erp (ld-return-error state)) ((null ans) (mv :continue nil state)) ((eq ans :error) (mv :error nil state)) ((eq ans :return) (mv :return :filter state)) (t (pprogn (cond ((<= (f-get-global 'ld-level state) 1) (prog2$ (initialize-accumulated-warnings) (initialize-timers state))) (t state)) (f-put-global 'last-make-event-expansion nil state) (let* ((old-wrld (w state)) (old-default-defun-mode (default-defun-mode old-wrld))) (mv-let (error-flg trans-ans state) (revert-world-on-error (mv-let (error-flg trans-ans state) (if (raw-mode-p state) (acl2-raw-eval form state) (trans-eval form 'top-level state t)) ; If error-flg is non-nil, trans-ans is (stobjs-out . valx). (er-progn (chk-absstobj-invariants nil state) (cond (error-flg (mv t nil state)) ((and (ld-error-triples state) (equal (car trans-ans) *error-triple-sig*) (car (cdr trans-ans))) (mv t nil state)) (t (er-progn (maybe-add-command-landmark old-wrld old-default-defun-mode form trans-ans state) (mv nil trans-ans state))))))) ; If error-flg is non-nil, trans-ans is (stobjs-out . valx) and we know ; that valx is not an erroneous error triple if we're paying attention to ; error triples. ; The code inside the revert-world-on-error arranges to revert if either ; trans-eval returns an error, or the value is to be thought of as an ; error triple and it signals an error. Error-flg, now, is set to t ; iff we reverted. (cond (error-flg (ld-return-error state)) ((and (equal (car trans-ans) *error-triple-sig*) (eq (cadr (cdr trans-ans)) :q)) (mv :return :exit state)) (t (pprogn (ld-print-results trans-ans state) (cond ((and (ld-error-triples state) (not (eq (ld-error-action state) :continue)) (equal (car trans-ans) *error-triple-sig*) (let ((val (cadr (cdr trans-ans)))) (and (consp val) (eq (car val) :stop-ld)))) (mv :return (list* :stop-ld (f-get-global 'ld-level state) (cdr (cadr (cdr trans-ans)))) state)) (t ; We make the convention of checking the new-namep filter immediately after ; we have successfully eval'd a form (rather than waiting for the next form) ; so that if the user has set the filter up he gets a satisfyingly ; immediate response when he introduces the name. (let ((filter (ld-pre-eval-filter state))) (cond ((and (not (eq filter :all)) (not (eq filter :query)) (not (new-namep filter (w state)))) (er-progn ; We reset the filter to :all even though we are about to exit this LD ; with :return. This just makes things work if "this LD" is the top-level ; one and LP immediately reenters. (set-ld-pre-eval-filter :all state) (mv :return :filter state))) (t (mv :continue nil state)))))))))))))))))))))) (defun ld-loop (state) ; Note: We use a bit of raw lisp to ensure that the ACL2 unwind protect stack ; is properly configured before we execute the prompt for the next command. ; This acl2-unwind can be exercised, we think, by evaluating LD recursively ; and aborting the inferior LD so that it fails to cleanup after itself. (mv-let (signal val state) #+acl2-loop-only (ld-read-eval-print state) #-acl2-loop-only (progn (acl2-unwind *ld-level* t) (ld-read-eval-print state)) (cond ((eq signal :continue) (ld-loop state)) ((eq signal :return) (value val)) (t (mv t nil state))))) ; The following raw lisp special variable controls whether the raw lisp version ; of ld-fn-body, below, prints the header as per ld-verbose or does not. The ; handling of aborts in ld-fn forces us to call ld-fn-body again after each ; abort and we wish to suppress the header message after all entrances other ; than the first. This only happens after an abort (all bets are off) and the ; idea is to fool the user into thinking a normal error was signalled. #-acl2-loop-only (defvar *first-entry-to-ld-fn-body-flg*) (defun update-cbd (standard-oi0 state) ; For the case that standard-oi0 is a string (representing a file), we formerly ; used extend-pathname to compute the new cbd from the old cbd and ; standard-oi0. However, this caused us to follow soft links when that was ; undesirable. Here is a suitable experiment, after building the nonstd books ; by connecting to books/nonstd/ and running "make clean-nonstd" followed by ; "make all-nonstd". In this experiment, we had already certified the regular ; books using ACL2(h), and an error occurred because of an attempt to read ; books/arithmetic/equalities.cert, which used a special hons-only format. ; cd /projects/acl2/devel/books/nonstd/arithmetic/ ; /projects/acl2/devel/allegro-saved_acl2r ; (ld "top.lisp") (let ((old-cbd (f-get-global 'connected-book-directory state))) (cond ((and old-cbd (stringp standard-oi0) (position *directory-separator* standard-oi0)) (let* ((os (os (w state))) (filename-dir (expand-tilde-to-user-home-dir (concatenate 'string (remove-after-last-directory-separator standard-oi0) *directory-separator-string*) os 'update-cbd state))) (f-put-global 'connected-book-directory (if (absolute-pathname-string-p filename-dir nil os) filename-dir (our-merge-pathnames old-cbd filename-dir)) state))) (t state)))) (defun ld-fn-body (standard-oi0 new-ld-specials-alist state) ; This function is defined only to make it convenient for ld-fn to execute its ; "body" either inside or outside an acl2-unwind-protect. ; WARNING: Because of the hidden acl2-unwind in the raw code for ld-loop above ; do not try to use acl2-unwind-protect in this function. The cleanup form for ; it will be executed before the first form is read because ld-loop rolls back ; to the initialized version of the frame. Furthermore, do not execute ; non-idempotent state changing forms here, i.e., incrementing or decrementing ; some counter in state, because the abort handling may cause this body to be ; reentered after an abort while the logical semantics suggests that it is ; entered only once. (Of course, aborts mean all bets are off, but the idea is ; to make it seem like they are errors.) We once incremented and decremented ; ld-level here and found the load level going down every time an abort ; occurred (because its increment was undone by the hidden acl2-unwind in ; ld-loop, mentioned above, and it was decremented at every abort). #+(and acl2-par (not acl2-loop-only)) (when (and (not *wormholep*) ; We do not reset parallelism variables while in a wormhole (say from :brr), ; because that could interfere with a surrounding (outside the wormhole) prover ; call. ; Fortunately, it isn't necessary to do that reset, because there is nothing to ; clean up: we (plan as of Feb. 2011 to) disable entry to the prover from a ; wormhole when parallelism is enabled for the prover. (or (eql *ld-level* 1) *reset-parallelism-variables*)) ; We claim that the parallelism variables are reset when either (1) we are ; entering the top-level ACL2 loop from raw Lisp, or else (2) a raw Lisp break ; has occurred. Let's see how the conditions above guarantee that claim. If ; (1) holds then the initial call of ld-fn-body in ld-fn0 will get us here with ; *ld-level* 1. When (2) holds then our-abort threw to 'local-top-level after ; setting *reset-parallelism-variables* to t, and the ld-fn-body call in ld-fn0 ; is re-entered after that throw is caught, and here we are! ; In rare cases we might get here without (1) or (2) holding -- say, after :a!. ; But it's OK to call reset-all-parallelism-variables in such cases; we simply ; prefer to minimize the frequency of calls, for efficiency. (reset-all-parallelism-variables)) (pprogn (f-put-ld-specials new-ld-specials-alist state) (update-cbd standard-oi0 state) (cond (#+acl2-loop-only (ld-verbose state) #-acl2-loop-only (and *first-entry-to-ld-fn-body-flg* (ld-verbose state)) ; We print the file name rather than the channel. (cond ((eq (ld-verbose state) t) (fms (if (eq standard-oi0 *standard-oi*) "ACL2 loading *standard-oi*.~%" "ACL2 loading ~x0.~%") (list (cons #\0 (cond ((consp standard-oi0) (kwote standard-oi0)) (t standard-oi0)))) (standard-co state) state (ld-evisc-tuple state))) (t (with-base-10 (fms "~@0" (list (cons #\0 (ld-verbose state)) (cons #\v (f-get-global 'acl2-version state)) (cons #\l (f-get-global 'ld-level state)) (cons #\c (f-get-global 'connected-book-directory state)) (cons #\b (f-get-global 'system-books-dir state))) (standard-co state) state (ld-evisc-tuple state)))))) (t state)) (mv-let (erp val state) (ld-loop state) (pprogn (cond ((eq (ld-verbose state) t) (fms (if (eq standard-oi0 *standard-oi*) "Finished loading *standard-oi*.~%" "Finished loading ~x0.~%") (list (cons #\0 (cond ((consp standard-oi0) (kwote standard-oi0)) (t standard-oi0)))) (standard-co state) state (ld-evisc-tuple state))) (t state)) (mv erp val state))))) (defun ld-fn1 (standard-oi0 alist state bind-flg) ; If this function weren't defined we would have to duplicate its body twice in ; ld-fn, once in the #+acl2-loop-only section and again in the ; #-acl2-loop-only section in the case where the state is not the live state. ; The reason we grab the old ld-level and use it in the cleanup form rather ; than just decrementing the then current value is that we do not know how many ; times the cleanup form will be tried before it is not interrupted. (let* ((old-ld-level (f-get-global 'ld-level state)) (new-ld-level (1+ old-ld-level)) (old-cbd (f-get-global 'connected-book-directory state))) (er-let* ((pair (chk-acceptable-ld-fn alist state))) (let ((old-ld-specials-alist (f-get-ld-specials state)) (new-ld-specials-alist (car pair)) (channel-closing-alist (cdr pair))) (if bind-flg (acl2-unwind-protect "ld-fn" (pprogn (f-put-global 'ld-level new-ld-level state) (ld-fn-body standard-oi0 new-ld-specials-alist state)) (pprogn (f-put-global 'ld-level old-ld-level state) (f-put-global 'connected-book-directory old-cbd state) (f-put-ld-specials old-ld-specials-alist state) (close-channels channel-closing-alist state)) (pprogn (f-put-global 'ld-level old-ld-level state) (f-put-global 'connected-book-directory old-cbd state) (f-put-ld-specials old-ld-specials-alist state) (close-channels channel-closing-alist state))) (acl2-unwind-protect "ld-fn" (pprogn (f-put-global 'ld-level new-ld-level state) (ld-fn-body standard-oi0 new-ld-specials-alist state)) (f-put-global 'ld-level old-ld-level state) (f-put-global 'ld-level old-ld-level state))))))) (defun ld-fn-alist (alist state) (let ((standard-oi (cdr (assoc 'standard-oi alist))) (dir (cdr (assoc 'dir alist))) (ctx 'ld) (os (os (w state)))) (cond ((and (stringp standard-oi) dir) (let ((standard-oi-expanded (expand-tilde-to-user-home-dir standard-oi os ctx state))) (cond ((absolute-pathname-string-p standard-oi-expanded nil os) (er hard ctx "It is illegal to supply a :DIR argument to LD here ~ because the supplied filename,~|~% ~s0,~|~%is an ~ absolute pathname (see :DOC pathname), and hence ~ there is no reasonable way to merge it with a :DIR ~ value." standard-oi)) (t (let ((resolve-dir (include-book-dir-with-chk hard 'ld dir))) (cond (resolve-dir (put-assoc-eq 'standard-oi (our-merge-pathnames resolve-dir standard-oi-expanded) (delete-assoc-eq 'dir alist))) (t alist))))))) ((assoc-eq 'dir alist) (delete-assoc-eq 'dir alist)) (t alist)))) #-acl2-loop-only (defmacro with-interrupts (&rest forms) ; This macro allows, in raw Lisp for underlying Common Lisp implementations ; where we know how to do this, the interrupting of evaluation of any of the ; given forms. We expect this behavior to take priority over any enclosing ; call of without-interrupts. #+ccl `(ccl:with-interrupts-enabled ,@forms) #+sbcl `(sb-sys:with-interrupts ,@forms) #+gcl `(let ((system:*interrupt-enable* t)) ,@forms) #-(or ccl sbcl gcl) `(progn ,@forms)) (defun ld-fn0 (alist state bind-flg) ; We set the ld specials to the values specified in alist and then enter the ; standard ACL2 read-eval-print loop. If bind-flg is t then the ld specials ; are restored to their pre-call values upon exit or abort. Otherwise they are ; not. Another interpretation of the flag is: if bind-flg is t then the load ; specials are merely "bound" locally to the values in alist, otherwise, they ; are globally smashed to values in alist. If this call is considered the ; "top-level" call of ld-fn, bind-flg ought to be nil: the final values of the ; load specials established during the interaction survive exiting to raw lisp ; and are present when ld-fn is reentered later. If this call is not ; "top-level" then the values established during interaction are lost on exit. ; Advice: It is best to read this function as though ld-fn1's body were ; substituted below. Ld-fn1 is just a way to avoid duplication of code and has ; nothing to do with the unwind protection we are really implementing. (let ((alist (ld-fn-alist alist state))) #+acl2-loop-only (ld-fn1 (cdr (assoc-eq 'standard-oi alist)) alist state bind-flg) ; The part in UPPERCASE below is raw lisp that manages the unwind stack and ; *ld-level*. The part in lowercase is identical to the pure ACL2 in ld-fn1 ; above. It is helpful to split the buffer, put the pure ACL2 in the top ; window and read what follows in the bottom one. Observe that if the state is ; not live, we just use the pure ACL2. So start with the PROGN below. #-acl2-loop-only (COND ((LIVE-STATE-P STATE) (PROGN (ACL2-UNWIND *LD-LEVEL* NIL) (PUSH NIL *ACL2-UNWIND-PROTECT-STACK*) (LET* ((*LD-LEVEL* (1+ *LD-LEVEL*)) (*READTABLE* *ACL2-READTABLE*) (*FIRST-ENTRY-TO-LD-FN-BODY-FLG* T) (ABORT-OBJ (CONS 'ABORT NIL)) (THROWN-VAL NIL) (LD-ERP ABORT-OBJ) (LD-VAL NIL)) ; below implies an abort happened (let* ((old-ld-level (f-get-global 'ld-level state)) (new-ld-level (1+ old-ld-level)) (old-cbd (f-get-global 'connected-book-directory state))) (MV-LET (ERP pair STATE) (chk-acceptable-ld-fn alist state) (COND (ERP (ACL2-UNWIND (1- *LD-LEVEL*) NIL) (MV ERP PAIR STATE)) (T (let ((old-ld-specials-alist (f-get-ld-specials state)) (new-ld-specials-alist (car pair)) (channel-closing-alist (cdr pair))) (PUSH-CAR (CONS "ld-fn" (IF bind-flg (FUNCTION (LAMBDA NIL (pprogn (f-put-global 'ld-level old-ld-level state) (f-put-global 'connected-book-directory old-cbd state) (f-put-ld-specials old-ld-specials-alist state) (close-channels channel-closing-alist state)))) (FUNCTION (LAMBDA NIL (pprogn (f-put-global 'ld-level old-ld-level state)))))) *ACL2-UNWIND-PROTECT-STACK* 'LD-FN) (TAGBODY LOOP (UNWIND-PROTECT (pprogn (f-put-global 'ld-level new-ld-level state) (PROGN (SETQ THROWN-VAL (CATCH 'LOCAL-TOP-LEVEL (MV-LET (ERP VAL STATE) (ld-fn-body (cdr (assoc-eq 'standard-oi alist)) new-ld-specials-alist state) (PROGN (WHEN bind-flg (f-put-global 'connected-book-directory old-cbd state)) (SETQ LD-ERP ERP) (SETQ LD-VAL VAL) NIL)))) STATE)) (WITH-INTERRUPTS ; We allow interrupts for the cleanup form. This seems acceptable because of ; how we handle ACL2 unwind-protects, calling ACL2-UNWIND; see The ; Unwind-Protect Essay. It also seems acceptable because some Lisps don't ; disable interrupts during evaluation of unwind-protect cleanup forms, so we ; expect to allow interrupts anyhow. And it seems important to do so, in case ; printing the gag-state needs to be interrupted; see the call of ; print-pstack-and-gag-state in prove-loop0. (COND (*ACL2-PANIC-EXIT-STATUS* (exit-lisp *ACL2-PANIC-EXIT-STATUS*)) ((EQ LD-ERP ABORT-OBJ) ; We get here if the ld-fn-body failed to terminate normally. This can happen ; either because lisp caused some error or because we threw to the tag above. ; If we threw to the tag then LD-ERP is ABORT-OBJ (because we didn't get to ; the SETQ above) and THROW-VAL is whatever we threw. If we did not throw, ; then THROWN-VAL is NIL (because the lisp error prevented us from doing the ; SETQ THROWN-VAL). We make the convention that we always throw non-nil ; values to the tag so as to distinguish these two cases. #+akcl (si::RESET-STACK-LIMITS) (COND ((EQ THROWN-VAL :ABORT) ; THROWN-VAL is always either NIL (meaning no throw occurred) or else the ; "reason" we threw. Currently the possibilities are :ABORT (thrown when the ; user types (a!)), :POP (thrown when the user types (p!)) or :WORMHOLE-ER ; (thrown when we tried to make a non-undoable change to state while in a ; wormhole). We only care about :ABORT. :WORMHOLE-ER is treated as a "normal" ; lisp error, i.e., we just unwind back to here and continue at this level. ; :ABORT means we are to exit all the way back to *LD-LEVEL* 1. :POP means ; that we are to pop up one level unless we are already at the top level. (COND ((= *LD-LEVEL* 1) ; At *LD-LEVEL* = 1 we know *standard-co* is *STANDARD-OUTPUT*. (PRINC "Abort to ACL2 top-level" *STANDARD-OUTPUT*) (TERPRI *STANDARD-OUTPUT*)) (T (THROW 'LOCAL-TOP-LEVEL :ABORT)))) ((EQ THROWN-VAL :POP) (COND ((= *LD-LEVEL* 1) (PRINC "Currently at ACL2 top-level" *STANDARD-OUTPUT*)) (t (COND ((= *LD-LEVEL* 2) (PRINC "Pop up to ACL2 top-level" *STANDARD-OUTPUT*)) (t (PRINC "Pop up one LD level" *STANDARD-OUTPUT*))) (WHEN (NOT (EQ (LD-ERROR-ACTION STATE) :ERROR)) (SET-LD-ERROR-ACTION :RETURN! STATE)))) (TERPRI *STANDARD-OUTPUT*))) (ACL2-UNWIND *LD-LEVEL* T) ; We first unwind back to the current level so STANDARD-OI and LD-ERROR-ACTION ; are correctly set. (COND ((EQ (LD-ERROR-ACTION STATE) :CONTINUE) (SETQ *FIRST-ENTRY-TO-LD-FN-BODY-FLG* (COND ((EQ THROWN-VAL :ABORT) T) (T NIL))) (SETQ NEW-LD-SPECIALS-ALIST NIL) (SETQ THROWN-VAL NIL) (GO LOOP)) ((EQ (LD-ERROR-ACTION STATE) :RETURN) (ACL2-UNWIND (1- *LD-LEVEL*) NIL) (RETURN-FROM LD-FN0 (VALUE :ERROR))) ((EQ (LD-ERROR-ACTION STATE) :RETURN!) (ACL2-UNWIND (1- *LD-LEVEL*) NIL) (RETURN-FROM LD-FN0 (VALUE (LIST :STOP-LD (F-GET-GLOBAL 'LD-LEVEL STATE))))) (T (ACL2-UNWIND (1- *LD-LEVEL*) NIL) (RETURN-FROM LD-FN0 (MV T NIL STATE))))) (T (ACL2-UNWIND (1- *LD-LEVEL*) NIL) (RETURN-FROM LD-FN0 (MV LD-ERP LD-VAL STATE))))))))))))))) (T (ld-fn1 (cdr (assoc-eq 'standard-oi alist)) alist state bind-flg))))) (defun ld-fn (alist state bind-flg) ; See ld-fn0. Here, we just provide a little wrapper for top-level calls of ; ld-fn0 in case that there is an interrupt that isn't handled inside ld-fn0. ; To see this issue in action, evaluate the following four forms and interrupt ; the last one twice: once late in the proof attempt and once immediately upon ; printing the checkpoint summary (which is done by a call of acl2-unwind in ; the cleanup form of an unwind-protect, on behalf of a call of ; acl2-unwind-protect inside prove-loop0 that invokes ; print-pstack-and-gag-state upon an error). ; (defun foo (n acc) ; (if (zp n) ; acc ; (foo (1- n) ; (cons `(equal (nth ,n x) x) ; acc)))) ; ; (defmacro mac (n) ; (cons 'and (foo n nil))) ; ; (set-rewrite-stack-limit 10000) ; ; (thm ; (mac 1000) ; :otf-flg t ; :hints (("Goal" :do-not '(preprocess)))) #-acl2-loop-only (cond (*load-compiled-stack* (error "It is illegal to call LD while loading a compiled book, in ~ this case:~%~a .~%See :DOC calling-ld-in-bad-contexts." (caar *load-compiled-stack*))) ((= *ld-level* 0) (return-from ld-fn (let ((complete-flg nil)) (unwind-protect (mv-let (erp val state) (ld-fn0 alist state bind-flg) (progn (setq complete-flg t) (mv erp val state))) (when (and (not complete-flg) (not *acl2-panic-exit-status*)) (fms "***NOTE***: An interrupt or error has occurred in the ~ process of cleaning up from an earlier interrupt or ~ error. This is likely to leave you at the raw Lisp ~ prompt after you abort to the top level. If so, then ~ execute ~x0 to re-enter the ACL2 read-eval-print ~ loop.~|~%" (list (cons #\0 '(lp))) *standard-co* state nil))))))) (cond ((not (f-get-global 'ld-okp state)) (er soft 'ld "It is illegal to call LD in this context. See DOC ~ calling-ld-in-bad-contexts.")) (t (ld-fn0 alist state bind-flg)))) (defmacro ld (standard-oi &key dir (standard-co 'same standard-cop) (proofs-co 'same proofs-cop) (current-package 'same current-packagep) (ld-skip-proofsp 'same ld-skip-proofspp) (ld-redefinition-action 'same ld-redefinition-actionp) (ld-prompt 'same ld-promptp) (ld-missing-input-ok 'same ld-missing-input-okp) (ld-pre-eval-filter 'same ld-pre-eval-filterp) (ld-pre-eval-print 'same ld-pre-eval-printp) (ld-post-eval-print 'same ld-post-eval-printp) (ld-evisc-tuple 'same ld-evisc-tuplep) (ld-error-triples 'same ld-error-triplesp) (ld-error-action ':RETURN!) (ld-query-control-alist 'same ld-query-control-alistp) (ld-verbose 'same ld-verbosep)) ":Doc-Section Other the ACL2 read-eval-print loop, file loader, and ~il[command] processor~/ ~bv[] Examples: (LD \"foo.lisp\") ; read and evaluate each form in file ; \"foo.lisp\", in order (LD \"foo.lisp\" :ld-pre-eval-print t) ; as above, but print each form to standard ; character output just before it is evaluated General Form: (LD standard-oi ; open obj in channel, stringp file name ; to open and close, or list of forms ; Optional keyword arguments: :dir ... ; use this add-include-book-dir directory :standard-co ... ; open char out or file to open and close :proofs-co ... ; open char out or file to open and close :current-package ... ; known package name :ld-skip-proofsp ... ; nil, 'include-book, or t ; (~pl[ld-skip-proofsp]) :ld-redefinition-action ... ; nil or '(:a . :b) :ld-prompt ... ; nil, t, or some prompt printer fn :ld-missing-input-ok ... ; nil, t, :warn, or warning message :ld-pre-eval-filter ... ; :all, :query, or some new name :ld-pre-eval-print ... ; nil, t, or :never :ld-post-eval-print ... ; nil, t, or :command-conventions :ld-evisc-tuple ... ; nil or '(alist nil nil level length) :ld-error-triples ... ; nil or t :ld-error-action ... ; :return!, :return, :continue or :error :ld-query-control-alist ... ; alist supplying default responses :ld-verbose ...) ; nil or t~/ ~ev[] ~c[Ld] is the top-level ACL2 read-eval-print loop. (When you call ~ilc[lp], a little initialization is done in raw Common Lisp and then ~c[ld] is called.) ~c[Ld] is also a general-purpose ACL2 file loader and a ~il[command] interpreter. ~c[Ld] is actually a macro that expands to a function call involving ~ilc[state]. ~c[Ld] returns an ``error triple'' ~c[(mv erp val state)] as explained below. (For much more on error triples, ~pl[programming-with-state].) ~l[rebuild] for a variant of ~c[ld] that skips proofs. ~l[output-to-file] for examples showing how to redirect output to a file. The arguments to ~c[ld], except for ~c[:dir], all happen to be global variables in ~ilc[state] (~pl[state] and ~pl[programming-with-state]). For example, ~c[']~ilc[current-package] and ~c[']~ilc[ld-verbose] are global variables, which may be accessed via ~c[(@ current-package)] and ~c[(@ ld-verbose)]. When ~c[ld] is called, it ``binds'' these variables. By ``binds'' we actually mean the variables are globally set but restored to their old values on exit. Because ~c[ld] provides the illusion of ~il[state] global variables being bound, they are called ``~c[ld] specials'' (after the Lisp convention of calling a variable ``special'' if it is referenced freely after having been bound). Note that all arguments but the first are passed via keyword. Any variable not explicitly given a value in a call retains its pre-call value, with the exception of ~c[:]~ilc[ld-error-action], which defaults to ~c[:return!] if not explicitly specified. Just as an example to drive the point home: If ~ilc[current-package] is ~c[\"ACL2\"] and you typed ~bv[] (ld *standard-oi* :current-package \"MY-PKG\") ~ev[] you would find yourself in (an inner) read-eval-print loop in which the ~il[current-package] was ~c[\"MY-PKG\"]. You could operate there as long as you wished, changing the current package at will. But when you typed ~c[:]~ilc[q] you would return to the outer read-eval-print loop where the current package would still be ~c[\"ACL2\"]. Roughly speaking, ~c[ld] repeatedly reads a form from ~ilc[standard-oi], evaluates it, and prints its result to ~ilc[standard-co]. It does this until the form is ~c[:]~ilc[q] or evaluates to an error triple whose value component is ~c[:]~ilc[q], or until the input channel or list is emptied. However, ~c[ld] has many bells and whistles controlled by the ~c[ld] specials. Each such special is documented individually. For example, see the documentation for ~ilc[standard-oi], ~ilc[current-package], ~ilc[ld-pre-eval-print], etc. A more precise description of ~c[ld] is as follows. In the description below we use the ~c[ld] specials as variables, e.g., we say ``a form is read from ~ilc[standard-oi].'' By this usage we refer to the current value of the named ~il[state] global variable, e.g., we mean ``a form is read from the current value of ~c[']~ilc[standard-oi].'' This technicality has an important implication: If while interacting with ~c[ld] you change the value of one of the ~c[ld] specials, e.g., ~c[']~ilc[standard-oi], you will change the behavior of ~c[ld], e.g., subsequent input will be taken from the new value. Three ~c[ld] specials are treated as channels: ~ilc[standard-oi] is treated as an object input channel and is the source of forms evaluated by ~c[ld]; ~ilc[standard-co] and ~ilc[proofs-co] are treated as character output channels and various flavors of output are printed to them. However, the supplied values of these specials need not actually be channels; several special cases are recognized. If the supplied value of one of these is in fact an open channel of the appropriate type, that channel is used and is not closed by ~c[ld]. If the supplied value of one of these specials is a string, the string is treated as a file name in (essentially) Unix syntax (~pl[pathname]) and a channel of the appropriate type is opened to/from that file. Any channel opened by ~c[ld] during the binding of the ~c[ld] specials is automatically closed by ~c[ld] upon termination. If ~ilc[standard-co] and ~ilc[proofs-co] are equal strings, only one channel to that file is opened and is used for both. As a special convenience, when ~ilc[standard-oi] is a string and the ~c[:dir] argument provided and not ~c[nil], we look up ~c[:dir] in the table of directories maintained by ~ilc[add-include-book-dir], and prepend this directory to ~ilc[standard-oi] to create the filename. (In this case, however, we require that ~c[standard-oi] is a relative pathname, not an absolute pathname.) For example, one can write ~c[(ld \"arithmetic/top-with-meta.lisp\" :dir :system)] to ~c[ld] that particular community books library. (Of course, you should almost always load books like ~c[arithmetic/top-with-meta] using ~ilc[include-book] instead of ~c[ld].) If ~c[:dir] is not specified, then a relative pathname is resolved using the connected book directory; ~pl[cbd]. Several other alternatives are allowed for ~ilc[standard-oi]. If ~ilc[standard-oi] is a true list then it is taken as the list of forms to be processed. If ~ilc[standard-oi] is a list ending in an open channel, then ~c[ld] processes the forms in the list and then reads and processes the forms from the channel. Analogously, if ~ilc[standard-oi] is a list ending a string, an object input channel from the named file is opened and ~c[ld] processes the forms in the list followed by the forms in the file. That channel is closed upon termination of ~c[ld]. In the cases that a string is to be converted to an object input channel ~-[] that is, when ~ilc[standard-oi] is a string or is a list ending in a string ~-[] an error occurs by default if the conversion fails, presumably because the file named by the string does not exist. However, if keyword argument ~c[:ld-missing-input-ok] is ~c[t], then ~c[ld] immediately returns without error in this case, but without reading or executing any forms, as though ~c[standard-oi] is ~c[nil] and keyword arguments ~c[:ld-verbose] and ~c[ld-prompt] both have value ~c[nil]. The other legal values for ~c[:ld-missing-input-ok] are ~c[nil], which gives the default behavior, and ~c[:warn], which behaves the same as ~c[t] except that a warning is printed, which contains the same information as would be printed for the default error described above. The remaining ~c[ld] specials are handled more simply and generally have to be bound to one of a finite number of tokens described in the ~c[:]~ilc[doc] entries for each ~c[ld] special. Should any ~c[ld] special be supplied an inappropriate value, an error message is printed. Next, if ~ilc[ld-verbose] is ~c[t], ~c[ld] prints the message ``ACL2 loading name'' where ~c[name] names the file or channel from which forms are being read. At the conclusion of ~c[ld], it will print ``Finished loading name'' if ~ilc[ld-verbose] is ~c[t]. Finally, ~c[ld] repeatedly executes the ACL2 read-eval-print step, which may be described as follows. A ~il[prompt] is printed to ~ilc[standard-co] if ~ilc[ld-prompt] is non-~c[nil]. The format of the ~il[prompt] is determined by ~ilc[ld-prompt]. If it is ~c[t], the default ACL2 ~il[prompt] is used. If it is any other non-~c[nil] value then it is treated as an ACL2 function that will print the desired ~il[prompt]. ~l[ld-prompt]. In the exceptional case where ~c[ld]'s input is coming from the terminal ~c[(*standard-oi*)] but its output is going to a different sink (i.e., ~ilc[standard-co] is not ~ilc[*standard-co*]), we also print the ~il[prompt] to the terminal. ~c[Ld] then reads a form from ~ilc[standard-oi]. If the object read is a keyword, ~c[ld] constructs a ``keyword command form'' by possibly reading several more objects. ~l[keyword-commands]. This construction process is sensitive to the value of ~ilc[ld-keyword-aliases]. ~l[ld-keyword-aliases]. Otherwise, the object read is treated as the command form. ~c[Ld] next decides whether to evaluate or skip this form, depending on ~ilc[ld-pre-eval-filter]. Initially, the filter must be either ~c[:all], ~c[:query], or a new name. If it is ~c[:all], it means all forms are evaluated. If it is ~c[:query], it means each form that is read is displayed and the user is queried. Otherwise, the filter is a name and each form that is read is evaluated as long as the name remains new, but if the name is ever introduced then no more forms are read and ~c[ld] terminates. ~l[ld-pre-eval-filter]. If the form is to be evaluated, then ~c[ld] first prints the form to ~ilc[standard-co], if ~ilc[ld-pre-eval-print] is ~c[t]. With this feature, ~c[ld] can process an input file or form list and construct a script of the session that appears as though each form was typed in. ~l[ld-pre-eval-print]. ~c[Ld] then evaluates the form, with ~ilc[state] bound to the current ~il[state]. The result is some list of (multiple) values. If a ~il[state] is among the values, then ~c[ld] uses that ~il[state] as the subsequent current ~il[state]. Depending on ~ilc[ld-error-triples], ~c[ld] may interpret the result as an ``error.'' ~l[ld-error-triples]. We first discuss ~c[ld]'s behavior if no error signal is detected (either because none was sent or because ~c[ld] is ignoring them because ~ilc[ld-error-triples] is ~c[nil]). In the case of a non-erroneous result, ~c[ld] does two things: First, if the logical ~il[world] in the now current ~il[state] is different than the ~il[world] before execution of the form, ~c[ld] adds to the ~il[world] a ``~il[command] landmark'' containing the form evaluated. ~l[command-descriptor]. Second, ~c[ld] prints the result to ~ilc[standard-co], but only if ~ilc[ld-post-eval-print] is not ~c[nil]. The result is printed as a list of (multiple) values unless ~ilc[ld-post-eval-print] is ~c[:command-conventions], ~ilc[ld-error-triples] is ~c[t], and the result is an ``error triple'', i.e., of the form ~c[(mv * * state)] (~pl[error-triples]). In that case, only the non-erroneous ``value'' component of the result is printed. ~l[ld-post-eval-print]. Whenever ~c[ld] prints anything (whether the input form, a query, or some results) it ``eviscerates'' it if ~c[ld-evisc-tuple] is non-~c[nil]. Essentially, evisceration is a generalization of Common Lisp's use of ~c[*print-level*] and ~c[*print-length*] to hide large substructures. ~l[evisc-tuple] and also ~pl[set-iprint]. We now return to the case of a form whose evaluation signals an error. In this case, ~c[ld] first restores the ACL2 logical ~il[world] to what it was just before the erroneous form was evaluated. Thus, a form that partially changes the ~il[world] (i.e., begins to store properties) and then signals an error, has no effect on the ~il[world]. You may see this happen on ~il[command]s that execute several ~il[events] (e.g., an ~ilc[encapsulate] or a ~ilc[progn] of several ~ilc[defuns]): even though the output makes it appear that the initial ~il[events] were executed, if an error is signalled by a later event the entire block of ~il[events] is discarded. After rolling back, ~c[ld] takes an action determined by ~ilc[ld-error-action]. If the action is ~c[:continue], ~c[ld] merely iterates the read-eval-print step. Note that nothing suggestive of the value of the ``erroneous'' form is printed. If the action is ~c[:return], ~c[ld] terminates normally; similarly if the action is ~c[:return!], but a special value is returned that can cause superior ~c[ld] commands to terminate; ~pl[ld-error-action] for details. If the action is ~c[:error], ~c[ld] terminates signalling an error to its caller. If its caller is in fact another instance of ~c[ld] and that instance is watching out for error signals, the entire ~il[world] created by the inner ~c[ld] will be discarded by the outer ~c[ld] if the inner ~c[ld] terminates with an error. ~c[Ld] returns an error triple, ~c[(mv erp val state)]. ~c[Erp] is ~c[t] or ~c[nil] indicating whether an error is being signalled. If no error is signalled, ~c[val] is the ``reason'' ~c[ld] terminated and is one of ~c[:exit] (meaning ~c[:]~ilc[q] was read), ~c[:eof] (meaning the input source was exhausted), ~c[:error] (meaning an error occurred but has been supressed), ~c[:filter] (meaning the ~ilc[ld-pre-eval-filter] terminated ~c[ld]), or a cons pair whose first component is the symbol ~c[:STOP-LD], which typically indicates that an error occurred while the value of variable ~c[']~ilc[ld-error-action] was ~c[:RETURN!]. ~l[ld-error-action] for details of this last case." `(ld-fn (list ,@(append (list `(cons 'standard-oi ,standard-oi)) (if dir (list `(cons 'dir ,dir)) nil) (if standard-cop (list `(cons 'standard-co ,standard-co)) nil) (if proofs-cop (list `(cons 'proofs-co ,proofs-co)) nil) (if current-packagep (list `(cons 'current-package ,current-package)) nil) (if ld-skip-proofspp (list `(cons 'ld-skip-proofsp ,ld-skip-proofsp)) nil) (if ld-redefinition-actionp (list `(cons 'ld-redefinition-action ,ld-redefinition-action)) nil) (if ld-promptp (list `(cons 'ld-prompt ,ld-prompt)) nil) (if ld-missing-input-okp (list `(cons 'ld-missing-input-ok ,ld-missing-input-ok)) nil) (if ld-pre-eval-filterp (list `(cons 'ld-pre-eval-filter ,ld-pre-eval-filter)) nil) (if ld-pre-eval-printp (list `(cons 'ld-pre-eval-print ,ld-pre-eval-print)) nil) (if ld-post-eval-printp (list `(cons 'ld-post-eval-print ,ld-post-eval-print)) nil) (if ld-evisc-tuplep (list `(cons 'ld-evisc-tuple ,ld-evisc-tuple)) nil) (if ld-error-triplesp (list `(cons 'ld-error-triples ,ld-error-triples)) nil) (list `(cons 'ld-error-action ,ld-error-action)) (if ld-query-control-alistp (list `(cons 'ld-query-control-alist ,ld-query-control-alist)) nil) (if ld-verbosep (list `(cons 'ld-verbose ,ld-verbose)) nil))) state t)) (defdoc calling-ld-in-bad-contexts ":Doc-Section ld errors caused by calling ~ilc[ld] in inappropriate contexts~/ The macro ~ilc[ld] was designed to be called directly in the top-level ACL2 loop, although there may be a few occasions for calling it from functions. ACL2 cannot cope with invocations of ~ilc[ld] during the process of loading a compiled file for a book, so this is an error. To see how that can happen, consider the following book, where file ~c[const.lsp] contains the single form ~c[(defconst *foo* '(a b))]. ~bv[] (in-package \"ACL2\") (defttag t) (progn! (ld \"const.lsp\")) ~ev[] An attempt to certify this book will cause an error, but that particular error can be avoided, as discussed below. If the book is certified, however, with production of a corresponding compiled file (which is the default behavior for ~ilc[certify-book]), then any subsequent call of ~ilc[include-book] that loads this compiled file will cause an error. Again, this error is necessary because of how ACL2 is designed; specifically, this ~ilc[ld] call would interfere with tracking of constant definitions when loading the compiled file for the book. Because including such a book (with a compiled file) causes an error, then as a courtesy to the user, ACL2 arranges that the certification will fail (thus avoiding a surprise later when trying to include the book). The error in that case will look as follows. ~bv[] ACL2 Error in LD: It is illegal to call LD in this context. See DOC calling-ld-in-bad-contexts. ~ev[] If you really think it is OK to avoid this error, you can get around it by setting ~il[state] global variable ~c[ld-okp] to t: ~c[(assign ld-okp t)]. You can then certify the book in the example above, but you will still not be able to include it with a compiled file.~/~/") (defmacro quick-test nil ; We might want to add other events to the list below to test a wide variety of ; features. '(ld '((defun app (x y) (declare (xargs :guard (true-listp x))) (if (eq x nil) y (cons (car x) (app (cdr x) y)))) (defthm true-listp-app (implies (true-listp x) (equal (true-listp (app x y)) (true-listp y)))) :program (defun rev (x) (declare (xargs :guard (true-listp x))) (if (eq x nil) nil (app (rev (cdr x)) (list (car x))))) :logic (verify-termination rev) (verify-guards rev) (defthm true-listp-rev (implies (true-listp x) (true-listp (rev x))) :rule-classes :type-prescription) (defthm rev-rev (implies (true-listp x) (equal (rev (rev x)) x)))) :ld-pre-eval-print t :ld-error-action :return)) (defun wormhole-prompt (channel state) (fmt1 "Wormhole ~s0~sr ~@1~*2" (list (cons #\0 (f-get-global 'current-package state)) (cons #\1 (defun-mode-prompt-string state)) (cons #\r #+:non-standard-analysis "(r)" #-:non-standard-analysis "") (cons #\2 (list "" ">" ">" ">" (make-list-ac (- (f-get-global 'ld-level state) 1) nil nil)))) 0 channel state nil)) (defun reset-ld-specials-fn (reset-channels-flg state) ; We restore all of the ld specials to their initial, top-level ; values, except for the three channels, standard-oi, standard-co, and ; proofs-co, which are not reset unless the reset-channels-flg is t. ; Of course, if this function is called while under a recursive ld, ; then when we pop out of that ld, the reset values will be lost. (f-put-ld-specials (cond (reset-channels-flg *initial-ld-special-bindings*) (t (cdddr *initial-ld-special-bindings*))) state)) (defmacro reset-ld-specials (reset-channels-flg) ":Doc-Section Other restores initial settings of the ~ilc[ld] specials~/ ~bv[] Examples: (reset-ld-specials t) (reset-ld-specials nil) ~ev[]~/ Roughly speaking, the ~ilc[ld] specials are certain ~il[state] global variables, such as ~ilc[current-package], ~ilc[ld-prompt], and ~ilc[ld-pre-eval-filter], which are managed by ~ilc[ld] as though they were local variables. These variables determine the channels on which ~ilc[ld] reads and prints and control many options of ~ilc[ld]. ~l[ld] for the details on what the ~ilc[ld] specials are. This function, ~c[reset-ld-specials], takes one Boolean argument, ~c[flg]. The function resets all of the ~ilc[ld] specials to their initial, top-level values, except for the three channel variables, ~ilc[standard-oi], ~ilc[standard-co], and ~ilc[proofs-co], which are reset to their initial values only if ~c[flg] is non-~c[nil]. Of course, if you are in a recursive call of ~ilc[ld], then when you exit that call, the ~ilc[ld] specials will be restored to the values they had at the time ~ilc[ld] was called recursively. To see what the initial values are, inspect the value of the constant ~c[*initial-ld-special-bindings*]." `(reset-ld-specials-fn ,reset-channels-flg state)) (defun maybe-reset-defaults-table1 (key pre-defaults-tbl post-defaults-tbl state) (let* ((pre-val (cdr (assoc-eq key pre-defaults-tbl))) (post-val (cdr (assoc-eq key post-defaults-tbl))) (cmd `(table acl2-defaults-table ,key ',pre-val))) (if (equal pre-val post-val) (value nil) (er-let* ((ans (acl2-query :ubt-defaults '("The default ~s0 was ~x1 before undoing, but will be ~x2 after ~ undoing unless the command ~x3 is executed. Do you wish to ~ re-execute this command after the :ubt?" :y t :n nil :? ("If you answer in the affirmative, then the command ~X34 will ~ be executed on your behalf. This will make the default ~s0 ~ equal to ~x1, which is what it was just before your :ubt ~ command was executed. Otherwise, the default ~s0 will be ~ what it is in the world after the undoing, namely ~x2. See ~ also :DOC acl2-defaults-table." :y t :n nil)) (list (cons #\0 (string-downcase (symbol-name key))) (cons #\1 pre-val) (cons #\2 post-val) (cons #\3 cmd) (cons #\4 nil)) state))) (if ans (ld (list cmd) :ld-pre-eval-filter :all :ld-pre-eval-print t :ld-post-eval-print :command-conventions :ld-evisc-tuple (abbrev-evisc-tuple state) :ld-error-triples t :ld-error-action :return) (value nil)))))) (defun maybe-reset-defaults-table2 (keys pre-defaults-tbl post-defaults-tbl state) (if keys (er-progn (maybe-reset-defaults-table1 (car keys) pre-defaults-tbl post-defaults-tbl state) (maybe-reset-defaults-table2 (cdr keys) pre-defaults-tbl post-defaults-tbl state)) (value nil))) (defun maybe-reset-defaults-table (pre-defaults-tbl post-defaults-tbl state) (maybe-reset-defaults-table2 (union-equal (strip-cars pre-defaults-tbl) (strip-cars post-defaults-tbl)) pre-defaults-tbl post-defaults-tbl state)) (defun delete-something (lst) ; Lst must be non-nil. We return a list that is one shorter than lst by either ; dropping the first nil we find in lst or, if there are no nils, the last ; element. (cond ((null (cdr lst)) nil) ((null (car lst)) (cdr lst)) (t (cons (car lst) (delete-something (cdr lst)))))) (defun store-in-kill-ring (x0 ring) ; A "kill ring" is a fancy queue that stores a fixed number, say n, of non-nil ; items in the order in which they were stored. Only the most recent n non-nil ; items stored are saved. When a non-nil item is stored and the ring is full, ; the oldest item is dropped out and lost. So we have described a queue so ; far. The only other operation on kill rings is "rotate" which selects an ; item from the kill ring but does not remove it. Given a ring containing n ; items, n+1 rotations will return the each of the items in turn and in the ; reverse order in which they were stored. More on rotation later. ; Kill rings are just lists of the n items, in order. The length of the list ; is n but there may be nils in the list. The initial kill ring of length n ; is just n nils. (cond ((or (null x0) ; item is nil or the size of the (null ring)) ; ring is 0. We store nothing. ring) (t (cons x0 (delete-something ring))))) (defun rotate-kill-ring1 (ring xn) (cond ((null ring) xn) ((car ring) (append ring xn)) (t (rotate-kill-ring1 (cdr ring) (append xn (list nil)))))) (defun rotate-kill-ring (ring xn) ; See store-in-kill-ring for background on rings. Xn is an element to add to ; the ring. We step the ring once, returning (mv item ring'), where item is ; the most recently added item in ring and ring' is the result of removing that ; item and adding xn as the oldest item in the ring. Thus, a series of ; rotate-kill-ring n+1 long will return us to the original configuration. (cond ((null (car ring)) (mv nil ring)) (t (mv (car ring) (rotate-kill-ring1 (cdr ring) (list xn)))))) (defun ubt-ubu-fn1 (kwd wrld pred-wrld state) (let ((pre-defaults-table (table-alist 'acl2-defaults-table wrld))) (er-let* ((redo-cmds (ubt-ubu-query kwd wrld pred-wrld nil nil wrld state nil))) (pprogn (f-put-global 'undone-worlds-kill-ring (store-in-kill-ring wrld (f-get-global 'undone-worlds-kill-ring state)) state) (set-w 'retraction pred-wrld state) (let ((redo-cmds (if (eq (car redo-cmds) (default-defun-mode pred-wrld)) (cdr redo-cmds) redo-cmds))) (er-progn (if redo-cmds (mv-let (col state) (fmt "Undoing complete. Redoing started...~%" nil (standard-co state) state nil) (declare (ignore col)) (value nil)) (value nil)) (if redo-cmds (ld redo-cmds :ld-redefinition-action '(:doit! . :overwrite) :ld-pre-eval-filter :all :ld-pre-eval-print t :ld-post-eval-print :command-conventions :ld-evisc-tuple (abbrev-evisc-tuple state) :ld-error-triples t :ld-error-action :return :ld-query-control-alist (cons '(:redef :y) (ld-query-control-alist state))) (value nil)) (if redo-cmds (mv-let (col state) (fmt1 "Redoing complete.~%~%" nil 0 (standard-co state) state nil) (declare (ignore col)) (value nil)) (value nil)) (maybe-reset-defaults-table pre-defaults-table (table-alist 'acl2-defaults-table (w state)) state) (io? event nil (mv erp val state) () (pcs-fn :x :x nil state)) (value :invisible))))))) (defun ubt-ubu-fn (kwd cd state) ; Kwd is :ubt or :ubu. (let* ((wrld (w state)) (command-number-baseline (access command-number-baseline-info (global-val 'command-number-baseline-info wrld) :current))) (er-let* ((cmd-wrld (er-decode-cd cd wrld kwd state))) (cond ((if (eq kwd :ubt) (<= (access-command-tuple-number (cddar cmd-wrld)) command-number-baseline) (< (access-command-tuple-number (cddar cmd-wrld)) command-number-baseline)) ; We prevent ubt and ubu from going into prehistory, thus burning users due to ; typos. But sometimes developers need to do it. Here is how from within the ; ACL2 loop: ; (set-state-ok t) ; (defun my-ubt-ubu-fn (inclp x state) (declare (xargs :guard t)) (value x)) ; :q ; Grab this defun, rename it to my-ubt-ubu-fn, edit out the cond clause ; containing this comment and define my-ubt-ubu-fn in raw lisp. ; (lp) ; (my-ubt-ubu-fn t 'sys-fn state), where sys-fn is the desired target of the ; ubt. (cond ((let ((command-number-baseline-original (access command-number-baseline-info (global-val 'command-number-baseline-info wrld) :original))) (if (eq kwd :ubt) (<= (access-command-tuple-number (cddar cmd-wrld)) command-number-baseline-original) (< (access-command-tuple-number (cddar cmd-wrld)) command-number-baseline-original))) (er soft kwd "Can't undo into system initialization.")) (t (er soft kwd "Can't undo into prehistory. See :DOC ~ reset-prehistory.")))) ((and (eq kwd :ubu) (equal wrld cmd-wrld)) (er soft kwd "Can't undo back to where we already are!")) (t (let ((pred-wrld (if (eq kwd :ubt) (scan-to-command (cdr cmd-wrld)) cmd-wrld))) (ubt-ubu-fn1 kwd wrld pred-wrld state))))))) (defun ubt!-ubu!-fn (kwd cd state) ; Kwd is :ubt or :ubu. (state-global-let* ((ld-query-control-alist (list* `(,kwd :n!) '(:ubt-defaults :n) (@ ld-query-control-alist))) (inhibit-output-lst (union-equal '(observation warning error) (@ inhibit-output-lst)))) (mv-let (erp val state) (ubt-ubu-fn kwd cd state) (declare (ignore erp val)) (value :invisible)))) (defmacro ubt-prehistory () ":Doc-Section History undo the ~il[command]s back through the last ~ilc[reset-prehistory] event~/ This command is only used to eliminate a ~ilc[reset-prehistory] event. If your most recent ~c[reset-prehistory] event does not have a flag argument of ~c[t], then ~c[:ubt-prehistory] undoes all command back through, and including, that ~c[reset-prehistory] event.~/~/" (list 'ubt-prehistory-fn 'state)) (defun ubt-prehistory-fn (state) (let* ((ctx 'ubt-prehistory) (wrld (w state)) (command-number-baseline-info (global-val 'command-number-baseline-info wrld)) (command-number-baseline (access command-number-baseline-info command-number-baseline-info :current))) (cond ((eql command-number-baseline (access command-number-baseline-info command-number-baseline-info :original)) (er soft ctx "There is no reset-prehistory event to undo.")) ((access command-number-baseline-info command-number-baseline-info :permanent-p) (er soft ctx "It is illegal to undo a reset-prehistory event that had its ~ permanent-p flag set to t. See :DOC reset-prehistory.")) (t (er-let* ((val (ubt-ubu-fn1 :ubt-prehistory wrld (scan-to-command (cdr (lookup-world-index 'command command-number-baseline wrld))) state))) (er-progn (reset-kill-ring t state) (prog2$ #-acl2-loop-only (pop *checkpoint-world-len-and-alist-stack*) #+acl2-loop-only nil (value val)))))))) (defun oops-warning (state) ; If the set of Lisps that compile all functions changes from {sbcl, ccl}, then ; change the #+/#- below accordingly. #+(or sbcl ccl) (fms "Installing the requested world.~|~%" nil (f-get-global 'standard-co state) state nil) #-(or sbcl ccl) (fms "Installing the requested world. Note that functions being re-defined ~ during this procedure will not have compiled definitions, even if ~ they had compiled definitions before the last :ubt or :u.~|~%" nil (f-get-global 'standard-co state) state nil)) (defun oops-fn (state) (mv-let (new-wrld new-kill-ring) (rotate-kill-ring (f-get-global 'undone-worlds-kill-ring state) (w state)) (cond ((null new-wrld) (cond ((null (f-get-global 'undone-worlds-kill-ring state)) (er soft :oops "Oops has been disabled in this ACL2 session. ~ See :DOC reset-kill-ring")) (t (er soft :oops "ACL2 cannot execute :oops at this time, ~ presumably because you have never executed :ubt ~ or :u during this ACL2 session (at least not ~ since the last invocation of reset-kill-ring).")))) (t (er-progn (revert-world-on-error (pprogn (oops-warning state) (set-w! new-wrld state) (er-progn (pcs-fn :x :x nil state) (value nil)))) (pprogn (f-put-global 'undone-worlds-kill-ring new-kill-ring state) (value :invisible))))))) (defmacro oops nil ":Doc-Section History undo a ~c[:u] or ~c[:]~ilc[ubt]~/ The keyword ~il[command] ~c[:oops] will undo the most recent ~c[:]~ilc[ubt] (or ~c[:u], which we here consider just another ~c[:]~ilc[ubt]). A second ~c[:oops] will undo the next most recent ~c[:]~ilc[ubt], a third will undo the ~c[:]~ilc[ubt] before that one, and a fourth ~c[:oops] will return the logical ~il[world] to its configuration before the first ~c[:oops].~/ Consider the logical world (~pl[world]) that represents the current extension of the logic and ACL2's rules for dealing with it. The ~c[:]~ilc[ubt] and ~c[:u] ~il[command]s ``roll back'' to some previous ~il[world] (~pl[ubt]). Sometimes these ~il[command]s are used to inadvertently undo useful work and user's wish they could ``undo the last undo.'' That is the function provided by ~c[:oops]. ~c[:Oops] is best described in terms of an implementation. Imagine a ring of four ~il[world]s and a marker (~c[*]) indicating the current ACL2 ~il[world]: ~bv[] * w0 / \\ w3 w1 \\ / w2 ~ev[] This is called the ``kill ring'' and it is maintained as follows. When you execute an event the current ~il[world] is extended and the kill ring is not otherwise affected. When you execute ~c[:]~ilc[ubt] or ~c[:u], the current ~il[world] marker is moved one step counterclockwise and that ~il[world] in the ring is replaced by the result, say ~c[w0'], of the ~c[:]~ilc[ubt] or ~c[:u]. ~bv[] w0 / \\ *w0' w1 \\ / w2 ~ev[] If you were to execute ~il[events] at this point, ~c[w0'] would be extended and no other changes would occur in the kill ring. When you execute ~c[:oops], the marker is moved one step clockwise. Thus the kill ring becomes ~bv[] * w0 / \\ w0' w1 \\ / w2 ~ev[] and the current ACL2 ~il[world] is ~c[w0] once again. That is, ~c[:oops] ``undoes'' the ~c[:]~ilc[ubt] that produced ~c[w0'] from ~c[w0]. Similarly, a second ~c[:oops] will move the marker to ~c[w1], undoing the undo that produced ~c[w0] from ~c[w1]. A third ~c[:oops] makes ~c[w2] the current ~il[world]. Note however that a fourth ~c[:oops] restores us to the configuration previously displayed above in which ~c[w0'] has the marker. In general, the kill ring contains the current ~il[world] and the three most recent ~il[world]s in which a ~c[:]~ilc[ubt] or ~c[:u] were done. While ~c[:]~ilc[ubt] may appear to discard the information in the ~il[events] undone, we can see that the ~il[world] in which the ~c[:]~ilc[ubt] occurred is still available. No information has been lost about that ~il[world]. But ~c[:]~ilc[ubt] does discard information! ~c[:]~ilc[Ubt] discards the information necessary to recover from the third most recent ~ilc[ubt]! An ~c[:oops], on the other hand, discards no information, it just selects the next available ~il[world] on the kill ring and doing enough ~c[:oops]es will return you to your starting point. We can put this another way. You can freely type ~c[:oops] and inspect the ~il[world] that you thus obtain with ~c[:]~ilc[pe], ~c[:]~ilc[pc], and other ~il[history] ~il[command]s. You can repeat this as often as you wish without risking the permanent loss of any information. But you must be more careful typing ~c[:]~ilc[ubt] or ~c[:u]. While ~c[:oops] makes ~c[:]~ilc[ubt] seem ``safe'' because the most recent ~c[:]~ilc[ubt] can always be undone, information is lost when you execute ~c[:]~ilc[ubt]. We note that ~c[:ubt] and ~c[:u] may remove compiled definitions (but note that in some Lisps, including CCL (OpenMCL) and SBCL, functions are always compiled). When the original world is restored using ~c[:oops], restored functions will not generally be compiled (except for Lisps as above), though the user can remedy this situation; ~pl[comp]. Finally, we note that our implementation of ~c[oops] can use a significant amount of memory, because of the saving of old logical ~il[world]s. Most users are unlikely to experience a memory problem, but if you do, then you may want to disable ~c[oops] by evaluting ~c[(reset-kill-ring 0 state)]; ~pl[reset-kill-ring]." '(oops-fn state)) (defmacro i-am-here () ":Doc-Section Miscellaneous a convenient marker for use with ~ilc[rebuild]~/ ~bv[] Example Input File for Rebuild: (defun fn1 (x y) ...) (defthm lemma1 ...) (defthm lemma2 ...) (i-am-here) The following lemma won't go through. I started typing the hint but realized I need to prove a lemma first. See the failed proof attempt in foo.bar. I'm going to quit for the night now and resume tomorrow from home. (defthm lemma3 ... :hints ((\"Goal\" :use (:instance ??? ... ~ev[]~/ By putting an ~c[(i-am-here)] form at the ``frontier'' of an evolving file of ~il[command]s, you can use ~ilc[rebuild] to load the file up to the ~c[(i-am-here)]. ~c[I-am-here] simply returns an error triple (~pl[error-triples]) that indicates an error, and any form that ``causes an error'' will do the same job. Note that the text of the file after the ~c[(i-am-here)] need not be machine readable." '(mv-let (col state) (fmt1 "~ I-AM-HERE~|" nil 0 (standard-co state) state nil) (declare (ignore col)) (mv t nil state))) (defun rebuild-fn-read-filter (file state) (state-global-let* ((standard-oi *standard-oi*) (standard-co *standard-co*)) (er-let* ((ans (acl2-query :rebuild '("How much of ~x0 do you want to process?" :t :all :all :all :query :query :until :until :? ("If you answer T or ALL, then the entire file will be ~ loaded. If you answer QUERY, then you will be asked ~ about each command in the file. If you answer UNTIL, ~ then you should also type some name after the UNTIL ~ and we will then proceed to process all of the events ~ in file until that name has been introduced. Rebuild ~ automatically stops if any command causes an error. ~ When it stops, it leaves the logical world in the ~ state it was in immediately before the erroneous ~ command. Thus, another way to use rebuild is to get ~ into the habit of planting (i-am-here) -- or any other ~ form that causes an error when executed -- and then ~ using the filter T or ALL when you rebuild." :t :all :all :all :query :query :until :until)) (list (cons #\0 file)) state))) (cond ((eq ans :until) (state-global-let* ((infixp nil)) (read-object *standard-oi* state))) (t (value ans)))))) (defun rebuild-fn (file filter filterp dir state) (er-let* ((filter (if filterp (value (if (eq filter t) :all filter)) (rebuild-fn-read-filter file state)))) (mv-let (erp val state) (ld file :dir dir :standard-co *standard-co* :proofs-co *standard-co* :ld-skip-proofsp t :ld-prompt nil :ld-missing-input-ok nil :ld-pre-eval-filter filter :ld-pre-eval-print nil :ld-post-eval-print :command-conventions :ld-evisc-tuple (abbrev-evisc-tuple state) :ld-error-triples t :ld-error-action :return! :ld-query-control-alist '((:filter . nil) . t) :ld-verbose t) (declare (ignore erp val)) (value t)))) (defmacro rebuild (file &optional (filter 'nil filterp) &key dir) ":Doc-Section Other a convenient way to reconstruct your old ~il[state]~/ ~bv[] Examples: ACL2 !>(rebuild \"project.lisp\") ACL2 !>(rebuild \"project.lisp\" t) ACL2 !>(rebuild \"project.lisp\" t :dir :system) ACL2 !>(rebuild \"project.lisp\" :all) ACL2 !>(rebuild \"project.lisp\" :query) ACL2 !>(rebuild \"project.lisp\" 'lemma-22) ~ev[]~/ ~c[Rebuild] allows you to assume all the ~il[command]s in a given file or list, supplied in the first argument. Because ~c[rebuild] processes an arbitrary sequence of ~il[command]s with ~ilc[ld-skip-proofsp] ~c[t], it is unsound! However, if each of these ~il[command]s is in fact admissible, then processing them with ~c[rebuild] will construct the same logical ~il[state] that you would be in if you typed each ~il[command] and waited through the proofs again. Thus, ~c[rebuild] is a way to reconstruct a ~il[state] previously obtained by proving your way through the ~il[command]s. The second, optional argument to ~c[rebuild] is a ``filter'' (~pl[ld-pre-eval-filter]) that lets you specify which ~il[command]s to process. You may specify ~c[t], ~c[:all], ~c[:query], or a new logical name. ~c[t] and ~c[:all] both mean that you expect the entire file or list to be processed. ~c[:query] means that you will be asked about each ~il[command] in turn. A new name means that all ~il[command]s will be processed as long as the name is new, i.e., ~c[rebuild] will stop processing ~il[command]s immediately after executing a ~il[command] that introduces name. ~C[Rebuild] will also stop if any ~il[command] causes an error. You may therefore wish to plant an erroneous form in the file, e.g., ~c[(mv t nil state)], (~pl[ld-error-triples]), to cause ~c[rebuild] to stop there. The form ~c[(i-am-here)] is such a pre-defined form. If you do not specify a filter, ~c[rebuild] will query you for one. Inspection of the definition of ~c[rebuild], e.g., via ~c[:]~ilc[pc] ~c[rebuild-fn], will reveal that it is just a glorified call to the function ~ilc[ld]. ~l[ld] if you find yourself wishing that ~c[rebuild] had additional functionality. If you supply the above ``filter'' argument, then you may also supply the keyword argument ~c[:dir], which is then passed to ~c[ld]; ~pl[ld]." `(rebuild-fn ,file ,filter ,filterp ,dir state)) ; The Tall Texas Tale about BIG-CLOCK ; Like any Lisp system, it may be said, loosely speaking, that ACL2 ; typically reads a form, evaluates it in the current state, and ; prints the result. This read-eval-print activity in ACL2 is done by ; the function ld-fn. When the user enters ACL2 by invoking (LP), ; ld-fn is called to do the work. ; The read phase of the read-eval-print activity is done with the ; read-object function, which calls the Common Lisp read function. ; This read is influenced by *package*, *readtable*, and *features*, ; as described in acl2.lisp. ; The semantics of an ACL2 read-eval-print cycles is best desribed ; from the logical point of view via the logic programming pradigm, to ; which we degress momentarity. In the Lisp paradigm, one thinks ; of an interaction as always being something like ; > (fact 3) = ? ; wherein a variable free term is evaluated to obtain a suitable ; value, say 6. In logic programming, as in Baroque or Prolog, one ; can ask a question like: ; ? (fact x) = 6 ; i.e. does there exist an x whose factorial is 6? The system then ; attempts to answer the question and may find one or several values for ; x that does the job, e.g. 3. In fact, one can even imagine asking ; ? (fact x) = y ; to obtain a variety of values of x and y that satisfy the relation. ; Or might might merely be informed that that, yes, there do exist ; values of x and y satisfying the relation, without being given x and ; y explicitly. ; The point of this digression is merely to mention the well-known ; (but non-Lispish) idea that the input to a computation need not ; always be given entirely in advance of the commencement of a ; computation. In truth, even in regular Common Lisp, the input is not ; really always given entirely in advance because the charcters that ; may appear in *standard-input* or the file system need not be known ; before evaluation commences. ACL2 employs this ``incompletely ; specified at evaluation commencement'' idea. ; From the logical point of view, an ACL2 ``state'' is any object in ; the logic satifying the state-p predicate, q.v. in axioms.lisp. ; There is a long comment in axioms.lisp under the heading STATE which ; describes the many fields that a state has. ; At the beginning of any interaction with the top-level ACL2 ld-fn, ; there is a ``partial current state'', which may be partially ; perceived, without side-effect, in Common Lisp, but outside of ACL2, ; by invoking (what-is-the-global-state). This partial current-state ; includes (a) the names, types, and times of the open input and ; output channels (but not the characters read or written to those ; channels), (b) the symbols in the global table, (c) the t-stack, (d) ; the 32-bit stack, and (e) the file clock. We say that an object o ; satisfying state-p is ``consistent with the current paritial state'' ; provided that every fact revealed by (what-is-the-global-state) and ; by examination of the bound globals is true about o. ; In Lisp (as opposed to Prolog) the input form has no explicit free ; variable. In ACL2, however, one free variable is permitted, and ; this variable, always named STATE, refers, loosely speaking to the ; ``value of the state at the time of input''. In ACL2, the variable ; STATE includes the input via files and channels. ; Common LISP IO ; If we have a Common Lisp system that is connected to an IO system, ; then at each tick of time, the system may (a) print a character, ; byte, or object to any of the open streams, (b) read a character, ; byte, or object from any of the open streams, (c) open a file for ; reading or writing and (c) close an open stream. ; Suppose that old and new are two objects satisfying state-p and that ; we have an implementation of ACL2 in a Common Lisp which is ; connected to an IO system. We say that old and new are ``IO ; consistent with the Common Lisp IO system's behavior in the time ; period between old and new'' provided that the relationships between ; the various io fields of old and new are just what happened. For ; example, suppose that old and new are different only in that in new ; on one input character channel one character has been consumed. ; Then that is consistent with a Common Lisp IO system in which the ; stream corresponding to the channel was read to get just one ; character. As another example, suppose that old and new are ; different only because a file is now on read-files that was not ; there before and file-clock has been ticked twice and the two most ; recent values of the file clock are the open and close time of the ; read file. Then that is consistent with a Common Lisp IO system in ; which a stream for a file of the read file's name was opened and ; consumed and the characters read were exactly the characters ; associated with the file name in readable-files at the file-clock ; upon open. This concept needs to be completely and fully spelled ; out, but we believe it is all boring and obvious: the file clock is ; to keep track of the opening and closing times. The read-files and ; written-files entries record closing times and contents. The ; readable-files and input channels entries record characters actually ; consumed. ; In the extremely important degenerate case, old and new are ; consistent with the Common Lisp IO system's behavior over a time ; interval if all the fields of old and new are identical, excepting ; only the global-table, stacks, and big-clock entries, and no IO ; occurred in the time interval. ; The ACL2 ld theorem ; Let us suppose, without loss of generality, that run is a function ; of one argument, state, that has been defined by the user, and ; accepted by ACL2. Let us further suppose that run returns a single ; state value. (There is no loss of generality here because any ; particular arguments or output value that the user wishes to provide ; or see can be placed in state globals. For example, one could add ; two to three by defining run as (defun run (state) (f-set-global ; 'foo (+ 2 3)))). Let us suppose that an ACL2 interaction of the ; form ; ACL2 !> (run state) ; completes. What is the theorem that describes the relationship ; between the old current partial state and the new current partial ; state? The theorem is that (a) there exists an object, old, which ; satisfies the predicate statep and an object, new, which also ; satisfies the predicate statep such that old is consistent with the ; partial current state at the time of the input and new is consistent ; with the partial current state at the time of the output (b) new and ; old are IO consistent with the Common Lisp IO system's behavior in ; the time period between the beginning and ending of the evaluation ; (c) new = (trans-eval '(run state) nil old t), and (d) (run old) = ; (trans-eval '(run state) nil old t) except in the big-clock field. ; In the important degenerate case in which no io occurs, this means ; essentially that there exists (in the constructive sense) a ; big-clock entry in old which is ``large enough'' to perform the ; trans-eval of the input form without ``running out of time''. ACL2 ; does not reveal to the user how much ``time'' was required, but ; merely guarantees that there exists a sufficiently large amount of ; time. In fact, because we ``jump into compiled code'' in ; raw-ev-fncall, we have no way of efficiently keeping track of how ; much time has been used. ; Note that there is no commitment to a uniform value for big-clock ; across all ACL2 interactions. In particular, there obviously exists ; an infinite sequence of forms, say (fact 1), (fact 2), (fact 3), ; .... which would require an infinitely increasing series of ; big-clocks. An ACL2 evaluation effort may fail for a variety of ; reasons, including resource errors and certain design decisions, ; e.g. the detection that a function should not be clobbered because ; there is already a function by that name with a symbol-function ; property. If evaluation fails, some characters may nevertheless ; have been printed or read and state may have been changed. (defconst *basic-sweep-error-str* "The state back to which we have been asked to roll would contain an ~ object that is inconsistent with the requested resetting of the ~ ACL2 known-package-alist. Logical consistency would be imperiled ~ if the rollback were undertaken. Please get rid of pointers to ~ such objects before attempting such a rollback.~|~%") (defun sweep-symbol-binding-for-bad-symbol (sym obj deceased-packages state) (cond ((symbolp obj) (cond ((member-equal (symbol-package-name obj) deceased-packages) (er soft "undo consistency check" "~@0In particular, the value of the global ~ variable ~x1 contains the symbol ~x2 in package ~ ~x3, which we have been asked to remove. ~ Please reset ~x1, as with (assign ~x1 nil)." *basic-sweep-error-str* sym obj (symbol-package-name obj))) (t (value nil)))) ((atom obj) (value nil)) ((equal obj (w state)) (value nil)) (t (er-progn (sweep-symbol-binding-for-bad-symbol sym (car obj) deceased-packages state) (sweep-symbol-binding-for-bad-symbol sym (cdr obj) deceased-packages state))))) (defun sweep-global-lst (l deceased-packages state) (cond ((null l) (value nil)) (t (er-progn (sweep-symbol-binding-for-bad-symbol (car l) (get-global (car l) state) deceased-packages state) (sweep-global-lst (cdr l) deceased-packages state))))) (defun sweep-stack-entry-for-bad-symbol (name i obj deceased-packages state) (cond ((symbolp obj) (cond ((member-equal (symbol-package-name obj) deceased-packages) (er soft "undo consistency check" "~@0In particular, the entry in the ~@1 at ~ location ~x2 contains the symbol ~x3 in package ~ ~x4, which we have been asked to undo. Please ~ change the ~@1 entry at location ~x2 or ~ shrink the ~@1." *basic-sweep-error-str* name i obj (symbol-package-name obj))) (t (value nil)))) ((atom obj) (value nil)) ((equal obj (w state)) (value nil)) (t (er-progn (sweep-stack-entry-for-bad-symbol name i (car obj) deceased-packages state) (sweep-stack-entry-for-bad-symbol name i (cdr obj) deceased-packages state))))) (defun sweep-t-stack (i deceased-packages state) (cond ((> i (t-stack-length state)) (value nil)) (t (er-progn (sweep-stack-entry-for-bad-symbol "t-stack" i (aref-t-stack i state) deceased-packages state) (sweep-t-stack (+ 1 i) deceased-packages state))))) (defun sweep-acl2-oracle (i deceased-packages state) ; A valid measure is (- (len (acl2-oracle state)) if we want to admit this ; function in logic mode, since read-acl2-oracle replaces the acl2-oracle of ; the state with its cdr. (mv-let (nullp car-oracle state) (read-acl2-oracle state) (cond (nullp (value nil)) (t (er-progn (sweep-stack-entry-for-bad-symbol "acl2-oracle" i car-oracle deceased-packages state) (sweep-acl2-oracle (+ 1 i) deceased-packages state)))))) (defun sweep-global-state-for-lisp-objects (deceased-packages state) ; This function sweeps every component of the state represented by ; *the-live-state* to verify that no symbol is contained in a package that we ; are about to delete. This is sensible before we undo a defpkg, for example, ; which may ``orphan'' some objects held in, say, global variables in the ; state. We look in the global variables, the t-stack, and acl2-oracle. If a ; global variable, t-stack entry, or acl2-oracle entry contains such an object, ; we cause an error. This function is structurally similar to ; what-is-the-global-state in axioms.lisp. ; The components of the state and their disposition are: ; open-input-channels - there are no objects in the dynamic channels. ; Objects obtained from those channels will be ; read into an otherwise ok state. ; open-output-channels - there are no objects in the dynamic channels ; global-table - the global table is the most likely place we will find ; bad objects. However, we know that the value of ; 'current-acl2-world is not bad, because after an undo ; it is set to a previously approved value. (er-progn (sweep-global-lst (global-table-cars state) deceased-packages state) ; t-stack - this stack may contain bad objects. (sweep-t-stack 0 deceased-packages state) (sweep-acl2-oracle 0 deceased-packages state)) ; The remaining fields contain no ``static'' objects. The fields are: ; 32-bit-integer-stack ; big-clock ; idates ; file-clock ; readable-files ; written-files ; read-files ; writeable-files ; list-all-package-names-lst ) (deflabel compilation :doc ":Doc-Section ACL2::Programming compiling ACL2 functions~/ ACL2 has several mechanisms to speed up the evaluation of function calls by ~em[compiling] functions: ~pl[comp], ~pl[set-compile-fns], and ~pl[certify-book]. The intention is that compilation never changes the value returned by a function call, though it could cause the call to succeed rather than fail, for example by avoiding a stack overflow. The ~ilc[state] global variable ~c['compiler-enabled] is set automatically when the system is built, and may depend on the underlying Lisp implementation. (In order to disable the compiler at build time, which will defeat the speed-up but usually be pretty harmless when the host Lisp is CCL or SBCL, see the discussion of ~c[ACL2_COMPILER_DISABLED] in distributed file ~c[GNUmakefile].) The value of ~c['compiler-enabled], as returned by ~c[(@ compiler-enabled)], can be ~c[t], ~c[:books], or ~c[nil]. If the value is ~c[nil], then ~ilc[include-book] and ~ilc[certify-book] coerce their arguments ~c[:load-compile-file] and ~c[compile-flg] arguments (respectively) to ~c[nil]. Otherwise, the value is ~c[:books] or ~c[t] and there is no such coercion; but if the value is not ~c[t], then ~ilc[comp] and ~ilc[set-compile-fns] are no-ops, which is probably desirable for Lisps such as CCL and SBCL that compile on-the-fly even when the compiler is not explicitly invoked. However, you may have reason to want to change the above (default) behavior. To enable compilation by default for ~ilc[certify-book] and ~ilc[include-book] but not for ~ilc[comp] or ~ilc[set-compile-fns]: ~bv[] (set-compiler-enabled :books state) ~ev[] To enable compilation not only as above but also for ~ilc[comp] and ~ilc[set-compile-fns]: ~bv[] (set-compiler-enabled t state) ~ev[] To suppress compilation and loading of compiled files by ~ilc[include-book] (for example, if you get a raw Lisp error such as ``Wrong FASL version''): ~bv[] (set-compiler-enabled nil state) ~ev[]~/ ~l[book-compiled-file] for more discussion about compilation and ~il[books].~/") (defdoc book-compiled-file ":Doc-Section books creating and loading of compiled and expansion files for ~il[books]~/ An effect of ~il[compilation] is to speed up the execution of the functions defined in a book. Compilation can also remove tail recursion, thus avoiding stack overflows. The presence of compiled code for the functions in the book should not otherwise affect the performance of ACL2. ~l[guard] for a discussion; also ~l[compilation]. By default, the ~ilc[certify-book] command compiles the book that it certifies. ~pl[certify-book] for how to control this behavior. By default, the ~ilc[include-book] command loads the compiled file for the book. The details of how this loading works are subtle, and do not need to be understood by most users. The ACL2 source code contains an ``Essay on Hash Table Support for Compilation'' that explains such details for those interested. All that users should generally need to know about this is that the compiled file is always the result of compiling a so-called ``expansion file'', which contains certain additional code besides the book itself. The relevance to users of the expansion file is that it can be loaded if the compiled file is missing (except when ~c[:load-compiled-file t] is specified by the ~ilc[include-book] form), and its existence is required in order for ~ilc[include-book] to create a book's compiled file, as described below. Most users can skip the remainder of this documentation topic, which addresses the uncommon activity of using ~ilc[include-book] to compile books. ~c[Include-book] can be made to compile a book by supplying its keyword argument ~c[:load-compiled-file] the value ~c[:comp]. However, a compiled file can only be produced if there is already an ~em[expansion file] that is at least as recent as the book's ~il[certificate]. Such a file, whose name happens to be the result of concatenating the string ~c[\"@expansion.lsp\"] to the book name (without the ~c[\".lisp\"] suffix), is created by ~ilc[certify-book] when state global variable ~c['save-expansion-file] has a non-~c[nil] value. That will be the case if ACL2 started up when environment variable ~c[ACL2_SAVE_EXPANSION] was ~c[t] (or any value that is not the empty string and whose ~ilc[string-upcase] is not ~c[\"NIL\"]), until the time (if any) that ~c['save-expansion-file] is assigned a different value by the user. In most respects, the ~c[:comp] setting is treated exactly the same as ~c[:warn]; but after all events in the book are processed, the expansion file is compiled if a compiled file was not loaded, after which the resulting compiled file is loaded. One can thus, for example, compile books for several different host Lisps ~-[] useful when installing ACL2 executables at the same site that are built on different host Lisps. A convenient way to do this in an environment that provides Gnu `make' is to certify the community books using the shell command ``~c[make regression]'' in the ~c[acl2-sources/] directory, after setting environment variable ~c[ACL2_SAVE_EXPANSION] to ~c[t], and then moving to the ~c[books] directory and executing the appropriate `make' commands to compile the books (targets ~c[fasl], ~c[o], and so on, according to the compiled file extension for the host Lisp). We conclude by saying more about the ~c[:load-compiled-file] argument of ~ilc[include-book]. We assume that ~il[state] global ~c['compiler-enabled] has a non-~c[nil] value; otherwise ~c[:load-compiled-file] is always treated as ~c[nil]. We do not consider raw mode below (~pl[set-raw-mode]), which presents a special case: ACL2 will attempt to load the book itself whenever it would otherwise load the expansion or compiled file, but cannot (either because the ~c[:load-compiled-file] argument is ~c[nil], or for each of the expansion and compiled files, either it does not exist or it is out of date with respect to the ~c[.cert] file). The ~c[:load-compiled-file] argument is not recursive: calls of ~c[include-book] that are inside the book supplied to ~c[include-book] use their own ~c[:load-compiled-file] arguments. However, those subsidiary ~c[include-book] calls can nevertheless be sensitive to the ~c[:load-compiled-file] arguments of enclosing ~c[include-book] calls, as follows. If ~c[:load-compiled-file] has value ~c[t], then every subsidiary ~c[include-book] is required to load a compiled file. Moreover, if a book's compiled file or expansion file is loaded in raw Lisp, then an attempt will be made to load the compiled file or expansion file for any ~ilc[include-book] form encountered during that load. If that attempt fails, then that load immediately aborts, as does its parent load, and so on up the chain. If, when going up the chain, an ~ilc[include-book] is aborted for which keyword argument ~c[:load-compiled-file] has value ~c[t], then an error occurs. When loading a book's compiled file or expansion file, ~c[FILE], it is possible to encounter an ~ilc[include-book] form for a book that has no suitable compiled file or expansion file. In that case, the load of ~c[FILE] is aborted at that point. Similarly, the load of ~c[FILE] is aborted in the case that this ~c[include-book] form has a suitable compiled file or expansion file whose load is itself aborted. Thus, whenever any ~c[include-book] aborts, so do all of its parent ~c[include-book]s, up the chain. Such an abort causes an error when the ~c[include-book] form specifies a ~c[:load-compiled-file] value of ~c[t].~/~/") (deflabel escape-to-common-lisp :doc ":Doc-Section Miscellaneous escaping to Common Lisp~/ ~bv[] Example: ACL2 !>:Q ~ev[]~/ There is no Common Lisp escape feature in the ~ilc[lp]. This is part of the price of purity. To execute a form in Common Lisp as opposed to ACL2, exit ~ilc[lp] with ~c[:]~ilc[q], submit the desired forms to the Common Lisp read-eval-print loop, and reenter ACL2 with ~c[(lp)].~/") (deflabel copyright :doc ":Doc-Section Miscellaneous ACL2 copyright, license, sponsorship~/~/ ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp Copyright (C) 2013, Regents of the University of Texas This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. This program is free software; you can redistribute it and/or modify it under the terms of the LICENSE file distributed with ACL2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE for more details. Written by: Matt Kaufmann and J Strother Moore email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu Department of Computer Science University of Texas at Austin Austin, TX 78701 U.S.A. Please also ~pl[acknowledgments].~/") (deflabel acknowledgments :doc ":Doc-Section Miscellaneous some contributors to the well-being of ACL2~/~/ The development of ACL2 was initially made possible by funding from the U. S. Department of Defense, including ARPA and ONR. We thank all the organizations that have contributed support, including the following (in alphabetical order).~bq[] o AMD, for providing significant time over several years for Matt Kaufmann to carry out ACL2 research, support, and development~nl[] o Computational Logic, Inc. and its president, Don Good, where the first eight years of ACL2 development occurred~nl[] o Centaur Technology~nl[] o DARPA~nl[] o Digital Equipment Corporation~nl[] o EDS, which provided some time for Matt Kaufmann's ACL2 work 1998-1999~nl[] o ForrestHunt and, more generally, Warren A. Hunt, Jr. (see below)~nl[] o IBM~nl[] o NSF~nl[] o ONR~nl[] o Rockwell Collins~nl[] o SRC~nl[] o Sun Microsystems~nl[] o University of Texas at Austin (in particular support to J Moore through the Admiral B. R. Inman Chair of Computing Theory) ~eq[]We are especially grateful to Warren A. Hunt, Jr. for his unrivaled efforts in securing support for the entire ACL2 research group at both Computational Logic, Inc., and the University of Texas at Austin. Without his efforts, we would have spent less time working on the system and fewer students would have been funded to apply it. ACL2 was started in August, 1989 by Boyer and Moore working together. They co-authored the first versions of axioms.lisp and basis.lisp, with Boyer taking the lead in the formalization of ``~il[state]'' and the most primitive ~il[io] functions. Boyer also had a significant hand in the development of the early versions of the files interface-raw.lisp and translate.lisp. For several years, Moore alone was responsible for developing the ACL2 system code, though he consulted often with both Boyer and Kaufmann. In August, 1993, Kaufmann became jointly responsible with Moore for developing the system. Boyer has continued to provide valuable consulting on an informal basis. Bishop Brock was the heaviest early user of ACL2, and provided many suggestions for improvements. In particular, the ~c[:cases] and ~c[:restrict] ~il[hints] were his idea; he developed an early version of congruence-based reasoning for Nqthm; and he helped in the development of some early ~il[books] about arithmetic. In a demonstration of his courage and faith in us, he pushed for Computational Logic, Inc., to agree to the Motorola CAP contract -- which required formalizing a commercial DSP in the untested ACL2 -- and moved to Scottsdale, AZ, to do the work with the Motorola design team. His demonstration of ACL2's utility was an inspiration, even to those of us designing ACL2. John Cowles also helped in the development of some early ~il[books] about arithmetic, and also provided valuable feedback and bug reports. Other early users of ACL2 at Computational Logic, Inc. helped influence its development. In particular, Warren Hunt helped with the port to Macintosh Common Lisp, and Art Flatau and Mike Smith provided useful general feedback. Mike Smith helped develop the Emacs portion of the implementation of proof trees. Bill Schelter made some enhancements to akcl (now gcl) that helped to enhance ACL2 performance in that Common Lisp implementation, and more generally, responded helpfully to our bug reports. Camm Maguire has since provided wonderful gcl support, and has created a Debian package for ACL2 built on GCL. We are also grateful to developers of other Common Lisp implementations. Kent Pitman helped in our interaction with the ANSI Common Lisp standardization committee, X3J13. John Cowles helped with the port to Windows (98) by answering questions and running tests. Ruben Gamboa created a modification of ACL2 to allow reasoning about the real numbers using non-standard analysis. His work has been incorporated into the ACL2 distribution; ~pl[real]. Rob Sumners has made numerous useful suggestions. In particular, he has designed and implemented improvements for ~il[stobj]s and been key in our development of locally-bound stobjs; ~pl[note-2-6]. Robert Krug has designed and implemented many changes in the vicinity of the linear arithmetic package and its connection to type-set and rewrite. He was also instrumental in the development of ~il[extended-metafunctions]. Pete Manolios has made numerous useful suggestions. In particular, Pete helped us to organize the first workshop and was a wonderful equal partner with the two of us (Kaufmann and Moore) in producing the books that arose from that workshop. Pete and his student, Daron Vroon, provided the current implementation of ~il[ordinals]. Jared Davis and Sol Swords have our gratitude for starting the acl2-books repository, ~url[http://acl2-books.googlecode.com/]. We thank David L. Rager for contributing an initial version of the support for ~il[parallelism] in an experimental extension of ACL2. Bob Boyer and Warren A. Hunt, Jr. developed a canonical representation for ACL2 data objects and a function memoization mechanism to facilitate reuse of previously computed results. We thank them for their extensive efforts for the corresponding experimental (as of 2008 and 2009) extension of ACL2; ~pl[hons-and-memoization]. We also thank the contributors to the ACL2 workshops for some suggested improvements and for the extensive collection of publicly distributed benchmark problems. And we thank participants at the ACL2 seminar at the University of Texas for useful feedback. More generally, we thank the ACL2 community for feedback, contributed ~il[books] (~pl[community-books]), and their interest in the ACL2 project. ~em[Regarding the documentation:] ~bq[] Bill Young wrote significant portions of the original ~c[acl2-tutorial] section of the ACL2 documentation, including what is now called ~il[alternative-introduction]. This was an especially important task in the early years when there was no guide for how to use ACL2 and we are very grateful. He, Bishop Brock, Rich Cohen, and Noah Friedman read over considerable amounts of the documentation, and made many useful comments. Others, particularly Bill Bevier and John Cowles, have also made useful comments on the ~il[documentation]. Art Flatau helped develop the ACL2 ~il[markup] language and translators from that language to Texinfo and HTML. Michael ``Bogo'' Bogomolny created a search engine, beginning with Version 2.6, and for that purpose modified the HTML translator to create one file per topic (a good idea in any case). Laura Lawless provided many hours of help in marking up appropriate parts of the ~il[documentation] in typewriter font. Noah Friedman developed an Emacs tool that helped us insert ``invisible links'' into the ~il[documentation], which improve the usability of that documentation under HTML readers such as Mosaic. Richard Stallman contributed a texinfo patch, to be found in the file ~c[doc/texinfo.tex].~eq[] ") (deflabel breaks :doc ":Doc-Section ACL2::ACL2-built-ins Common Lisp breaks~/ ~bv[] Example: Broken at PROVE. Type :H for Help. >>:Q ACL2 !> ~ev[]~/ You may interrupt the system by typing various control character sequences. The precise sequences are determined by the host Lisp and operating system environment. For example, in GCL and Allegro Common Lisp, a console interrupt is caused by typing ``~c[ctrl-c]''. If, however, the GCL or Allegro is running in an Emacs shell buffer, one must type ``ctrl-c ctrl-c''. If a break occurs, for example because of a bug in ACL2 or a user interrupt, the break will run a Common Lisp read-eval-print loop, not an ACL2 read-eval-print loop. This may not be obvious if the ~il[prompt]s in the two loops are similar. Because you are typing to a Common Lisp evaluator, you must be careful. It is possible to damage your ACL2 state in irreparable ways by executing non-ACL2 Common Lisp. It is even possible to disrupt and render inaccurate the interrupted evaluation of a simple ACL2 expression. For ACL2 built on most host Common Lisps, you will see the string ~c[[RAW LISP~]] in the ~il[prompt] at a break, to emphasize that one is inside a break and hence should quit from the break. For some host Common Lisps, the top-level prompt also contains the string ~c[[RAW LISP~]]. ~l[prompt] for how to control printing of that string. The most reliable way to return to the ACL2 top level is by executing the following command: ~c[(]~ilc[abort!]~c[)]. Appropriate cleanup will then be done, which should leave you in an appropriate state. However, you may be able to quit from the break in the normal Lisp manner (as with ~c[:q] in GCL or CCL, ~c[:reset] in Allegro CL, and ~c[q] in CMU CL). If this attempt to quit is successful, it will return you to the innermost ACL2 read-eval-print loop, with appropriate cleanup performed first. Note that if you are within a ~ilc[brr] environment when the break occurs, quitting from the break will only return you to that environment, not to the top of ACL2's read-eval-print loop.~/") (deflabel ordinals :doc ":Doc-Section Miscellaneous ordinals in ACL2~/ Ordinals are used in ACL2 for proving termination in the admission of recursive function definitions. For a proof that the ACL2 ordinals are well-founded, ~pl[proof-of-well-foundedness]. The representation of ordinals changed in ACL2 Version_2.8, and is due to Pete Manolios and Daron Vroon. They have also defined algorithms for ordinal arithmetic, created a library of theorems to reason about ordinal arithmetic, and written the rest of this documentation in order to explain this change. We thank them for their efforts. Although they have provided the implementation and even modified the community books as needed, we have looked over their work and are maintaining it (and this documentation); if there are any bugs, they should be considered ours (Matt Kaufmann and J Moore). A book is included for compatibility with the representation before Version_2.8. For books that contain events relying on the previous ordinal implementation, insert the following lines before the first such event: ~bv[] (include-book \"ordinals/e0-ordinal\" :dir :system) (set-well-founded-relation e0-ord-<) ~ev[] The new ordinal representation is based on a slightly different version of Cantor Normal Form than that used by the old ordinals. An advantage of the new representation is that it is exponentially more succinct than the old representation. While pre-Version_2.8 ACL2 versions provided built-in functions for checking if an object is an ordinal and for comparing two ordinals, they did not provide support for reasoning about and constructing ordinals. The community books directory ~c[books/ordinals] provides such support. First, it provides efficient algorithms for ordinal arithmetic (including addition, subtraction, multiplication, and exponentiation). The algorithms and their complexity are described in the following paper. ~bf[] Manolios, Panagiotis & Vroon, Daron. Algorithms for ordinal arithmetic. Baader, Franz (ed), 19th International Conference on Automated Deduction--CADE-19. Pages 243-257 of LNAI, vol. 2741. Springer-Verlag. ~ef[] Second, the algorithms are mechanically verified and libraries of theorems which can be used to automate reasoning involving the ordinals are provided. For details, see the following paper. ~bf[] Manolios, Panagiotis & Vroon, Daron. Ordinal arithmetic in ACL2. Kaufmann, Matt, & Moore, J Strother (eds). Fourth International Workshop on the ACL2 Theorem Prover and Its Applications (ACL2-2003), July, 2003. See ~url[http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/]. ~ef[] We now describe aspects of the above mentioned books in more detail. The new ordering function is ~ilc[o<] and the new ordinal recognizer is ~ilc[o-p]. See also ~ilc[natp], ~ilc[posp], ~ilc[o<=], ~ilc[o>], ~ilc[o>=], ~ilc[o-first-expt], ~ilc[o-first-coeff], ~ilc[o-rst], ~ilc[make-ord], ~ilc[o-finp], and ~ilc[o-infp]. ~/ The old ordinals were based on the following formulation of Cantor Normal Form: For any ordinal, ~c[a < epsilon-0], there exist natural numbers ~c[p] and ~c[n], and ordinals ~c[a1 >= a2 >= ... >= an > 0] such that ~c[a > a1] and ~c[a = w^(a1) + w^(a2) + ... + w^(an) + p]. Thus, a predicate recognizing ACL2's old ordinals is given by the following definition. ~bv[] (defun e0-ordinalp (x) (if (consp x) (and (e0-ordinalp (car x)) (not (equal (car x) 0)) (e0-ordinalp (cdr x)) (or (atom (cdr x)) (not (e0-ord-< (car x) (cadr x))))) (and (integerp x) (>= x 0)))) ~ev[] The new representation is based on a corollary to the above theorem, which we get by the left distributive property of ordinal multiplication over ordinal addition. Thus, ~c[w^a + w^a = (w^a)2], ~c[w^a + w^a + w^a = (w^a)3] and so forth. The corollary is as follows: For any ordinal, ~c[a < epsilon-0], there exist natural numbers ~c[p] and ~c[n], positive integers ~c[x1, x2, ..., xn] and ordinals ~c[a1 > a2 > ... > an > 0] such that ~c[a > a1] and ~c[a = w^(a1)x1 + w^(a2)x2 + ... + w^(an)xn + p]. Instead of representing an ordinal as a list of non-increasing ordinals, we represent it as a list of exponent-coefficient pairs, such that the exponents are strictly decreasing (~pl[o-p]). Note that this representation is exponentially more efficient than the old representation. The ordinal arithmetic functions: ~c[o+], ~c[o-], ~c[o*], and ~c[o^] are defined in the ordinals library (in the community books directory ~c[books/ordinals]). To use them, include the book ~c[ordinals-without-arithmetic] or ~c[ordinals], depending on whether you want the arithmetic books included or not (~c[ordinals] includes community book ~c[books/arithmetic/top-with-meta]). To use the old ordinals, include the book ~c[e0-ordinal] and run the command ~c[(set-well-founded-relation e0-ord-<)] The community book ~c[books/arithmetic/natp-posp] is a book for reasoning about ~c[posp] and ~c[natp]. We recommend using this book if you have to reason about ~c[posp] and ~c[natp]. It is included in community book ~c[books/arithmetic/top], which is included in community book ~c[books/arithmetic/top-with-meta], which is included in community book ~c[books/ordinals/ordinals]. If you have a good reason to use the old definitions of the ordinals (e.g., because of legacy code and theorems), then we provide a convenient way to do this. The book ~c[ordinal-isomorphism] proves that the new ordinals are order-isomorphic to the old ordinals and thus theorems proved in one context can be directly transferred to the other. For an example of how to do this, look at the book ~c[defmul] in the community books directory ~c[books/workshops/2000/ruiz/multiset]. The ordinals books have been used to prove non-trivial theorems. For a good example, see the books in the community books directory ~c[books/workshops/2003/sustik/support], where Matyas Sustik proves Dickson's lemma. Finally, many termination proofs can be carried out with weaker orderings than the ordinals up to ~c[epsilon-0]. For example, many inductive theorem provers only know that the lexicographic ordering on natural numbers is well-founded. The book ~c[lexicographic-ordering] contains a definition of such an ordering ~c[l<] whose arguments are either a list of natural numbers, or a natural number. In the book we prove that ~c[l<] is well-founded (that is, we prove a ~c[:well-founded-relation] ~ilc[defthm] and provide a macro ~c[llist] to simplify the generation of measure functions. We also show how to use ~c[l<] to prove that the famous Ackermann function terminates. Finally, since ~c[l<] does something reasonable with natural numbers, it gets along with ~ilc[acl2-count], the default measure chosen by ACL2.") (defmacro wet (form &rest kwd-args) ":Doc-Section Trace evaluate a form and print subsequent error trace~/ The acronym ``wet'' stands for ``with-error-trace''. ~c[Wet] provides a convenient way to obtain a backtrace when evaluation causes a guard violation or other error. The basic idea is that ~c[(wet form)] evaluates ~c[form] and, if there is an error, shows a backtrace of calls that led to that error. Note however that by default only calls of user-defined (not built-in) functions ``supporting'' ~c[form] in the following sense will show up in the backtrace: those that occur in the macroexpansion of ~c[form] or (recursively) support any of those functions. So for example, since ~c[(make-event form)] macroexpands to ~c[(make-event-fn (quote form) ...)], calls of functions occurring in ~c[form] will likely not show up in the backtrace by default. The option ~c[:fns all] overrides this default, with potential loss of speed; more on this below. The following example explains the use of ~c[wet]. First, submit the following three definitions: ~bv[] (defun foo (x) (declare (xargs :guard (consp x))) (car x)) (defun bar (x) (foo (cdr x))) (defun g (x) (bar (cdr x))) ~ev[] Now imagine you have obtained the following guard violation: ~bv[] ACL2 !>(g '(3 4)) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X), which is (CONSP X), is violated by the arguments in the call (FOO NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> ~ev[] With ~c[wet], you can get a backtrace of user-defined functions. The package prefixes shown below, ~c[ACL2_*1*_], indicate that the executable (logical) counterparts of the corresponding raw Lisp functions are being called; ~pl[guard]. Don't forget to start with ~c[(include-book \"misc/wet\" :dir :system)]. ~bv[] ACL2 !>(wet (g '(3 4))) ; Fast loading /projects/acl2/devel/books/misc/wet.fasl TTAG NOTE: Adding ttag :TRACE! from the top level loop. ACL2 Error in WET: The guard for the function call (FOO X), which is (CONSP X), is violated by the arguments in the call (FOO NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. Backtrace stack: ---------------- 1. (ACL2_*1*_ACL2::FOO NIL) 2. (ACL2_*1*_ACL2::BAR (4)) 3. (ACL2_*1*_ACL2::G (3 4)) ACL2 !> ~ev[] By default, large structures are hidden during the printing of the backtrace stack. But you can supply a value for keyword argument ~c[:evisc-tuple] to modify the printing: ~c[nil] to avoid hiding, else a suitable evisc-tuple, as shown below (~pl[evisc-tuple]). ~bv[] ACL2 !>(wet (g '(3 4)) :evisc-tuple (evisc-tuple 1 1 nil nil)) ; Fast loading /projects/acl2/devel/books/misc/wet.fasl TTAG NOTE: Adding ttag :TRACE! from the top level loop. ACL2 Error in WET: The guard for the function call (FOO X), which is (CONSP X), is violated by the arguments in the call (FOO NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. Backtrace stack: ---------------- 1. (ACL2_*1*_ACL2::FOO ...) 2. (ACL2_*1*_ACL2::BAR ...) 3. (ACL2_*1*_ACL2::G ...) ACL2 !> ~ev[] For a backtrace as a data object, evaluate the form ~c[(@ wet-stack)]. But note that this object may not be a legal ACL2 value, for example because of the ``~c[*1*]'' symbols shown above.~/ ~bv[] General Form: (wet form ; an arbitrary form :book bk-form ; optional, not evaluated ;;; the rest are optional and evaluated: :evisc-tuple e ; an evisc-tuple :fns fns ; :all, or a list of functions to show in a backtrace :compile c ; :same, t, or nil; default :same (nil if :fns supplied) ~ev[] ~c[Form] is evaluated. If there is an error, a backtrace stack is printed to the standard output (~ilc[*standard-co*]), containing (by default) the user-defined function calls made before the error. Such printing is controlled by the ~c[:evisc-tuple] if supplied; otherwise, hiding of large structures will occur. (Technical detail: by default the global abbrev-evisc-tuple is used, if bound; ~pl[set-evisc-tuple]. The ~c[:fns] option. As mentioned above, by default the ~c[wet] backtrace shows user-defined functions that syntactically ``support'' the form being evaluated. This default can be overridden by supplying an explicit list, ~c[fns], of functions, using option ~c[:fns fns]; these will then be the functions whose calls are eligible for inclusion in the backtrace. The special value ~c[:fns :all] will allow all user-defined function calls in the backtrace. This value can be useful when using ~ilc[oracle-apply], for example, since the function being applied isn't typically included as a syntactic supporter of the form being evaluated. The ~c[:compile] option. ~c[Wet] uses the ~ilc[trace$] utility to modify the definitions of affected functions so that they can record information for the backtrace. As described above, these affected functions are those that syntactically ``support'' the form unless specified by the ~c[:fns] option. As is the case for ~c[trace$] ~-[] ~pl[trace$] ~-[] the new definitions of these affected functions may or may not be compiled. For ~c[trace$] and for ~c[wet], the default is to compile the new definition if and only if the original definition was compiled, except: For ~c[wet], if option ~c[:fns :all] is provided, then the default is not to compile the affected definitions. And for ~c[trace$] and ~c[wet], the ~c[:compile] option overrides the default, to specify what will be compiled: value ~c[:same] to compile each affected function if and only if its original definition was compiled, value ~c[t] to compile all affected functions, and value ~c[nil] to skip compilation. The ~c[:book] option. ~c[Wet] actually works by temporarily including a community book, ~bv[] (include-book \"misc/wet\" :dir :system) ~ev[] and then passing its arguments to macro ~c[wet!], defined in that book. The keyword argument ~c[:book] allows you to specify a different book that defines a macro ~c[wet!] to which to pass its arguments. If the value of ~c[:book] is a string, then the book named by that string is temporarily included using ~ilc[include-book]: ~c[(include-book \"bk\")]. Otherwise ~c[:book] should be a list of arguments, to be provided (unevaluated) to ~ilc[include-book], for example ~c[(\"my-wet\" :dir :my-utils)]. Thus you can experiment by copying community book ~c[books/misc/wet.lisp] to your own directory and making modifications to the copy. If you make changes, we invite you to share them with the ACL2 community (~pl[books]). Note that you can also supply ~c[:book nil], in which case the definition of ~c[wet!] in your current session will be used without including a book. Also ~pl[trace$] for a general tracing utility. As mentioned above, ~c[wet] is implemented using ~c[trace$]. ~c[Wet] actually first applies ~ilc[untrace$]; upon completion, ~c[wet] then applies ~ilc[trace$] to re-trace any functions that it had untraced, using their original trace specs.~/" (let* ((book-tail (member-eq :book kwd-args)) (kwd-args (if book-tail (remove-keyword :book kwd-args) kwd-args)) (book-form (if book-tail (cond ((null book-tail) nil) ((stringp (cadr book-tail)) (list 'include-book (cadr book-tail))) (t (cons 'include-book (cadr book-tail)))) '(include-book "misc/wet" :dir :system)))) `(with-output :off (summary event) (make-event (mv-let (erp val state) (progn ,@(and book-form (list book-form)) (wet! ,form ,@kwd-args)) (cond (erp (mv "WET failed!" nil state)) (t (value `(value-triple ',val))))))))) (defmacro disassemble$ (fn &rest args &key (recompile ':default) ; And, in case community book books/misc/disassemble.lisp changes between ; releases: &allow-other-keys) ":Doc-Section Other disassemble a function~/ The macro ~c[disassemble$] provides a convenient interface to the underlying ~c[disassemble] utility of the host Common Lisp implementation, which prints assembly code for a given function symbol at the terminal. It works by including the community book ~c[books/misc/disassemble.lisp], which defines the supporting function ~c[disassemble$-fn], and then by calling that function. Note that the arguments to ~c[disassemble$] are evaluated. Also note that ~c[disassemble$] is intended as a top-level utility for the ACL2 loop, not to be called in code; for such a purpose, include the above book and call ~c[disassemble$-fn] directly. ~bv[] Example Forms: (disassemble$ 'foo) (disassemble$ 'foo :recompile t)~/ General Forms: (disassemble$ form) (disassemble$ form :recompile flg) ~ev[] where ~c[form] evaluates to a function symbol and ~c[flg] evaluates to any value. If ~c[flg] is ~c[nil], then the existing definition of that function symbol is disassembled. But if ~c[flg] is supplied and has a value other than ~c[nil] or ~c[:default], and if that function symbol is defined in the ACL2 loop (not merely in raw Lisp; for example, ~pl[set-raw-mode]), then the disassembly will be based on a recompilation of that ACL2 definition. Normally this recompilation is not necessary, but for some host Lisps, it may be useful; in particular, for CCL the above book arranges that source code information is saved, so that the output is annotated with such information. When recompilation takes place, the previous definition is restored after disassembly is complete. Finally, if ~c[flg] is omitted or has the value ~c[:default] ~-[] i.e., in the default case ~-[] then recompilation may take place or not, depending on the host Lisp. The values of ~c[(@ host-lisp)] for which recompilation takes place by default may be found by looking at the above book, or by including it and evaluating the constant ~c[*host-lisps-that-recompile-by-default*]. As of this writing, CCL is the only such Lisp (because that is the one for which we can obtain source annotation in the output by recompiling).~/" `(with-ubt! (with-output :off (event expansion summary proof-tree) (progn (include-book "misc/disassemble" :dir :system :ttags '(:disassemble$)) (value-triple (disassemble$-fn ,fn ,recompile (list ,@args))))))) (deflabel release-notes :doc ":Doc-Section release-notes pointers to what has changed~/~/ This section of the online ~il[documentation] contains notes on the changes that distinguish successive released versions of ACL2. The current version of ACL2 is the value of the constant ~c[(@ acl2-version)].") (deflabel note1 :doc ":Doc-Section release-notes Acl2 Version 1.1 Notes~/ The new features are extensively documented. The relevant topics are:~/ It is especially important to read all of of the ~il[documentation] for ~il[books] before trying to use books. However, the new ~c[:]~ilc[more] keyword command is so handy for reading long ~il[documentation] strings that we recommend you start with ~c[:]~ilc[doc] more if reading at the terminal. Some documentation has been written for ~il[guard]s which you might find interesting.~/ :cite books :cite more :cite guard :cite redundant-events") (deflabel note2 :doc ":Doc-Section release-notes Acl2 Version 1.2 Notes~/ Hacker mode has been eliminated and ~il[programming] mode has been added. ~il[Programming] mode is unsound but does syntax checking and permits redefinitions of names. See ~c[:]~ilc[doc] ~c[load-mode] and ~c[:]~ilc[doc] ~c[g-mode]. The arguments to ~ilc[ld] have changed. ~ilc[Ld] is now much more sophisticated. ~l[ld]. For those occasions on which you wish to look at a large list structure that you are afraid to print, try ~c[(walkabout x state)], where ~c[x] is an Acl2 expression that evaluates to the structure in question. I am afraid there is no ~il[documentation] yet, but it is similar in spirit to the Interlisp structure editor. You are standing on an object and commands move you around in it. E.g., 1 moves you to its first element, 2 to its second, etc.; 0 moves you up to its parent; ~c[nx] and ~c[bk] move you to its next sibling and previous sibling; ~c[pp] prettyprints it; ~ilc[q] exits returning ~c[nil]; ~ilc[=] exits returning the thing you're standing on; ~c[(= symb)] assigns the thing you're standing on to the ~il[state] global variable ~c[symb]. Several new ~il[hints] have been implemented, including ~c[:by] and ~c[:do-not]. The old ~c[:do-not-generalize] has been scrapped in favor of such new ~il[hints] as ~c[:do-not] ~c[(generalize elim)]. ~c[:By] lets you say ``this goal is subsumed by'' a given lemma instance. The ~c[:by] hint also lets you say ``this goal can't be proved yet but skip it and see how the rest of the proof goes.'' ~l[hints].~/ ") (deflabel note3 :doc ":Doc-Section release-notes Acl2 Version 1.3 Notes~/ ~il[Programming] mode has been eliminated. Instead, all functions have a ``color'' which indicates what can be done with the function. For example, ~c[:red] functions can be executed but have no axioms describing them. Thus, ~c[:red] functions can be introduced after passing a simple syntactic check and they can be redefined without undoing. But nothing of consequence can be proved about them. At the other extreme are ~c[:gold] functions which can be executed and which also have passed both the termination and the ~il[guard] verification proofs. The color of a function can be specified with the new ~ilc[xargs] keyword, ~c[:color], which, if omitted defaults to the global setting of ~c[ld-color]. ~c[Ld-color] replaces ~c[load-mode]. Setting ~c[ld-color] to ~c[:red] causes behavior similar to the old ~c[:g-mode]. Setting ~c[ld-color] to ~c[:gold] causes behavior similar to the old ~c[:v-mode]. It is possible to prototype your system in ~c[:red] and then convert ~c[:red] functions to :~c[blue] individually by calling ~ilc[verify-termination] on them. They can then be converted to ~c[:gold] with ~ilc[verify-guards]. This allows us to undertake to verify the termination and ~il[guard]s of system functions. See ~c[:]~ilc[doc] color for an introduction to the use of colors. Type prescription rules have been added. Recall that in Nqthm, some ~ilc[rewrite] rules were actually stored as ``~il[type-prescription]s.'' Such rules allow the user to inform Nqthm's primitive type mechanism as to the kinds of shells returned by a function. Earlier versions of Acl2 did not have an analogous kind of rule because Acl2's type mechanism is complicated by ~il[guard]s. Version 1.3 supports ~ilc[type-prescription] rules. ~l[type-prescription]. Three more new ~il[rule-classes] implement congruence-based rewriting. It is possible to identify a binary relation as an equivalence relation (~pl[equivalence]), to show that one equivalence relation refines another (~pl[refinement]) and to show that a given equivalence relation is maintained when rewriting a given function call, e.g., ~c[(fn ...xk...)], by maintaining another equivalence relation while rewriting the ~c[k]th argument (~pl[congruence]). If ~c[r] has been shown to be an ~il[equivalence] relation and then ~c[(implies hyps (r (foo x) (bar x)))] is proved as a ~c[:]~ilc[rewrite] rule, then instances of ~c[(foo x)] will be replaced by corresponding instances of ~c[(bar x)] provided the instance occurs in a slot where the maintainence of ~c[r-equivalence] is known to be sufficient and ~c[hyps] can be established as usual. In Version 1.2, ~il[rule-classes] were simple keywords, e.g., ~c[:]~ilc[rewrite] or ~c[:]~ilc[elim]. In Version 1.3, ~il[rule-classes] have been elaborated to allow you to specify how the theorem ought to be used as a rule. That is, the new ~il[rule-classes] allows you to separate the mathematical statement of the formula from its interpretation as a rule. ~l[rule-classes]. Rules used to be named by symbols, e.g., ~ilc[car] and ~c[car-cons] were the names of rules. Unfortunately, this was ambiguous because there are three rules associated with function symbols: the symbolic definition, the executable counterpart, and the ~il[type-prescription]; many different rules might be associated with theorems, depending on the rule classes. In Version 1.3 rules are named by ``~il[rune]s'' (which is just short hand for ``rule names''). Example ~il[rune]s are ~c[(:definition car)], ~c[(:executable-counterpart car)], and ~c[(:type-prescription car . 1)]. Every rule added by an event has a different name and you can ~il[enable] and ~il[disable] them independently. ~l[rune] and ~pl[theories]. The identity function ~ilc[force], of one argument, has been added and given a special interpretation by the functions responsible for establishing hypotheses in backchaining: When the system fails to establish some hypothesis of the form ~c[(force term)], it simply assumes it is true and goes on, delaying until later the establishment of term. In particular, pushes a new subgoal to prove term in the current context. When that subgoal is attacked, all of the resources of the theorem prover, not just rewriting, are brought to bear. Thus, for example, if you wish to prove the rule ~c[(implies (good-statep s) (equal (exec s n) s'))] and it is your expectation that every time ~c[exec] appears its first argument is a ~c[good-statep] then you might write the rule as ~c[(implies (force (good-statep s)) (equal (exec s n) s'))]. This rule is essentially an unconditional rewrite of ~c[(exec s n)] to ~c[s'] that spawns the new goal ~c[(good-statep s)]. ~l[force]. Because you can now specify independently how a theorem is used as a rule, you need not write the ~ilc[force] in the actual theorem proved. ~l[rule-classes]. Version 1.3 supports a facility similar to Nqthm's ~ilc[break-lemma]. ~l[break-rewrite]. You can install ``~il[monitor]s'' on ~il[rune]s that will cause interactive breaks under certain conditions. Acl2 also provides ``~il[wormhole]s'' which allow you to write functions that cause interaction with the user but which do not require that you have access to ~ilc[state]. ~l[wormhole]. The rewriter now automatically backchains to stronger recognizers. There is no user hook to this feature but it may simplify some proofs with which older versions of Acl2 had trouble. For example, if the rewriter is trying to prove ~c[(rationalp (foo a b c))] it is now smart enough to try lemmas that match with ~c[(integerp (foo a b c))].~/ ") (deflabel note4 :doc ":Doc-Section release-notes Acl2 Version 1.4 Notes~/ Once again ~ilc[ld] only takes one required argument, as the ~c[bind-flg] has been deleted. Three commands have been added in the spirit of ~c[:]~ilc[pe]. ~c[:]~ilc[Pe!] is similar to ~c[:]~ilc[pe] but it prints all ~il[events] with the given name, rather than just the most recent. The command ~c[:]~ilc[pf] prints the corollary formula corresponding to a name or ~il[rune]. The command ~c[:]~ilc[pl] (print lemmas) prints rules whose top function symbol is the given name. ~l[pe!], ~pl[pf], and ~pl[pl]. Book naming conventions have been changed somewhat. The once-required ~c[.lisp] extension is now prohibited! Directories are supported, including a notion of ``connected book directory''. ~l[book-name]. Also, the second argument of ~ilc[certify-book] is now optional, defaulting to ~c[0]. ~il[Compilation] is now supported inside the Acl2 loop. ~l[comp] and ~pl[set-compile-fns]. The default color is now part of the Acl2 ~il[world]; see ~c[:]~ilc[doc] ~c[default-color]. ~c[Ld-color] is no longer an ~ilc[ld] special. Instead, colors are ~il[events]; see the documentation for ~c[red], ~c[pink], ~c[blue], and ~c[gold]. A ~il[table] exists for controlling whether Acl2 prints comments when it ~il[force]s hypotheses of rules; see ~c[:]~ilc[doc] ~c[force-table]. Also, it is now possible to turn off the forcing of assumptions by disabling the definition of ~il[force]; ~pl[force]. The event ~c[defconstant] is no longer supported, but a very similar event, ~ilc[defconst], has been provided in its place. ~l[defconst]. The event for defining ~il[congruence] relations is now ~ilc[defcong] (formerly, ~c[defcon]). Patterns are now allowed in ~c[:expand] ~il[hints]. See the documentation for ~c[:expand] inside the documentation for ~il[hints]. We have improved the way we report rules used by the simplifier. All ~il[rune]s of the same type are reported together in the running commentary associated with each goal, so that for example, executable counterparts are listed separately from definitions, and rewrite rules are listed separately from ~il[linear] rules. The preprocessor now mentions ``simple'' rules; ~pl[simple]. The mechanism for printing warning messages for new rewrite rules, related to subsumption, now avoids worrying about nonrecursive function symbols when those symbols are ~il[disable]d. These messages have also been eliminated for the case where the old rule is a ~c[:]~ilc[definition] rule. Backquote has been modified so that it can usually provide predictable results when used on the left side of a rewrite rule. Time statistics are now printed even when an event fails. The Acl2 trace package has been modified so that it prints using the values of the Lisp globals ~c[*print-level*] and ~c[*print-length*] (respectively). ~il[Table] has been modified so that the ~c[:clear] option lets you replace the entire ~il[table] with one that satisfies the ~c[val] and key guards (if any); ~pl[table]. We have relaxed the translation rules for ~c[:measure] ~il[hints] to ~ilc[defun], so that the the same rules apply to these terms that apply to terms in ~ilc[defthm] ~il[events]. In particular, in ~c[:measure] ~il[hints] ~ilc[mv] is treated just like ~ilc[list], and ~ilc[state] receives no special handling. The ~il[loop-stopper] test has been relaxed. The old test required that every new argument be strictly less than the corresponding old argument in a certain ~il[term-order]. The new test uses a lexicographic order on term lists instead. For example, consider the following rewrite rule. ~bv[] (equal (variable-update var1 val1 (variable-update var2 val2 vs)) (variable-update var2 val2 (variable-update var1 val1 vs))) ~ev[] This rule is permutative. Now imagine that we want to apply this rule to the term ~bv[] (variable-update u y (variable-update u x vs)). ~ev[] Since the actual corresponding to both ~c[var1] and ~c[var2] is ~c[u], which is not strictly less than itself in the ~il[term-order], this rule would fail to be applied in this situation when using the old test. However, since the pair ~c[(u x)] is lexicographically less than the pair ~c[(u y)] with respect to our ~il[term-order], the rule is in fact applied using our new test. Messages about ~il[events] now contain a space after certain left parentheses, in order to assist emacs users. For example, the event ~bv[] (defthm abc (equal (+ (len x) 0) (len x))) ~ev[] leads to a summary containing the line ~bv[] Form: ( DEFTHM ABC ...) ~ev[] and hence, if you search backwards for ``~c[(defthm abc]'', you won't stop at this message. More tautology checking is done during a proof; in fact, no goal printed to the screen, except for the results of applying ~c[:use] and ~c[:by] ~il[hints] or the top-level goals from an induction proof, are known to Acl2 to be tautologies. The ~ilc[ld-query-control-alist] may now be used to suppress printing of queries; ~pl[ld-query-control-alist]. Warning messages are printed with short summary strings, for example the string ``~c[Use]'' in the following message. ~bv[] Acl2 Warning [Use] in DEFTHM: It is unusual to :USE an enabled :REWRITE or :DEFINITION rule, so you may want to consider disabling FOO. ~ev[] At the end of the event, just before the time is printed, all such summary strings are printed out. The keyword command ~c[:u] has been introduced as an abbreviation for ~c[:]~ilc[ubt] ~c[:]~ilc[max]. Printing of query messages is suppressed by ~c[:u]. The keyword ~c[:cheat] is no longer supported by any event form. Some irrelevant formals are detected; ~pl[irrelevant-formals]. A bug in the application of metafunctions was fixed: now if the output of a metafunction is equal to its input, the application of the metafunction is deemed unsuccessful and the next metafunction is tried. An example has been added to the documentation for ~il[equivalence] to suggest how to make use of ~il[equivalence] relations in rewriting. The following Common Lisp functions have been added to Acl2: ~ilc[alpha-char-p], ~ilc[upper-case-p], ~ilc[lower-case-p], ~ilc[char-upcase], ~ilc[char-downcase], ~ilc[string-downcase], ~ilc[string-upcase], and ~c[digit-charp-p]. A documentation section called ~ilc[proof-checker] has been added for the interactive facility, whose documentation has been slightly improved. See in particular the documentation for ~il[proof-checker], ~ilc[verify], and ~il[macro-command]. A number of ~il[events] that had been inadvertently disallowed in ~il[books] are now permitted in ~il[books]. These are: ~ilc[defcong], ~c[defcor], ~ilc[defequiv], ~ilc[defrefinement], ~ilc[defstub], and ~ilc[verify-termination]. ~/ ") (deflabel note5 :doc ":Doc-Section release-notes Acl2 Version 1.5 Notes~/ Acl2 now allows ``complex rationals,'' which are complex numbers whose real parts are rationals and whose imaginary parts are non-zero rationals. ~l[complex]. A new way of handling ~ilc[force]d hypotheses has been implemented. Rather than cause a case split at the time the ~ilc[force] occurs, we complete the main proof and then embark on one or more ``forcing rounds'' in which we try to prove the ~il[force]d hypotheses. ~l[forcing-round]. To allow us to compare the new handling of ~ilc[force] with the old, Version 1.5 implements both and uses a flag in ~ilc[state] to determine which method should be used. Do ~c[(assign old-style-forcing t)] if you want ~ilc[force] to be handled as it was in Version 1.4. However, we expect to eliminate the old-style forcing eventually because we think the new style is more effective. To see the difference between the two approaches to forcing, try proving the associativity of ~il[append] under both settings of ~c[old-style-forcing]. To get the new behavior invoke: ~bv[] (thm (implies (and (true-listp a) (true-listp b)) (equal (append (append a b) c) (append a (append b c))))) ~ev[] Then ~c[(assign old-style-forcing t)] and invoke the ~c[thm] ~il[command] above again. A new ~c[:cases] ~il[hints] allows proof by cases. ~l[hints]. ~ilc[Include-book] and ~ilc[encapsulate] now restore the ~ilc[acl2-defaults-table] when they complete. ~l[include-book] and ~pl[encapsulate]. The ~il[guard]s on many Acl2 primitives defined in ~c[axioms.lisp] have been weakened to permit them to be used in accordance with lisp custom and tradition. It is possible to attach heuristic filters to ~c[:]~ilc[rewrite] rules to limit their applicability. ~l[syntaxp]. A tutorial has been added (but as of Version_3.6.1 it has become obsolete). ~il[Events] now print the Summary paragraph listing ~il[rune]s used, time, etc., whether they succeed or fail. The format of the ``~il[failure] banner'' has been changed but still has multiple asterisks in it. ~c[Thm] also prints a Summary, whether it succeeds or fails; but ~c[thm] is not an event. A new event form ~ilc[skip-proofs] has been added; ~pl[skip-proofs]. A user-specific customization facility has been added in the form of a book that is automatically included, if it exists on the current directory. ~l[acl2-customization]. A facility for conditional metalemmas has been implemented; ~pl[meta]. The acceptable values for ~ilc[ld-skip-proofsp] have changed. In the old version (Version 1.4), a value of ~c[t] meant that proofs and ~ilc[local] ~il[events] are to be skipped. In Version 1.5, a value of ~c[t] means proofs (but not ~ilc[local] ~il[events]) are to be skipped. A value of ~c[']~ilc[include-book] means proofs and ~ilc[local] ~il[events] are to be skipped. There are two other, more obscure, acceptable values. ~l[ld-skip-proofsp]. In order to turn off the forcing of assumptions, one should now ~il[disable] the ~c[:]~ilc[executable-counterpart] of ~ilc[force] (rather than the ~c[:]~ilc[definition] of ~ilc[force], as in the previous release); ~pl[force]. The macros ~ilc[enable-forcing] and ~ilc[disable-forcing] make it convenient to ~il[enable] or ~il[disable] forcing. ~l[enable-forcing] and ~pl[disable-forcing]. The new commands ~c[:]~ilc[pr] and ~c[:]~ilc[pr!] print the rules created by an event or command. ~l[pr] and ~pl[pr!]. The new ~il[history] ~il[command]s ~c[:]~ilc[puff] and ~c[:]~ilc[puff*] will replace a compound ~il[command] such as an ~ilc[encapsulate] or ~ilc[include-book] by the sequence of ~il[events] in it. That is, they ``~il[puff] up'' or ``lift'' the subevents of a ~il[command] to the ~il[command] level, eliminating the formerly superior ~il[command] and lengthening the ~il[history]. This is useful if you want to ``partially undo'' an ~ilc[encapsulate] or book or other compound ~il[command] so you can experiment. ~l[puff] and ~pl[puff*]. Theory expressions now are allowed to use the free variable ~ilc[world] and prohibited from using the free variable ~ilc[state]. ~l[theories], although it is essentially the same as before except it mentions ~ilc[world] instead of ~ilc[state]. ~l[world] for a discussion of the Acl2 logical ~il[world]. Allowing ~ilc[in-theory] ~il[events] to be state-sensitive violated an important invariant about how ~il[books] behaved. ~ilc[Table] keys and values now are allowed to use the free variable ~ilc[world] and prohibited from using the free variable ~ilc[state]. See the note above about theory expressions for some explanation. The macro for minus, ~ilc[-], used to expand ~c[(- x 3)] to ~c[(+ x -3)] and now expands it to ~c[(+ -3 x)] instead. The old macro, if used in the left-hand sides of rewrite rules, produced inapplicable rules because the constant occurs in the second argument of the ~ilc[+], but potential target terms generally had the constant in the first argument position because of the effect of ~c[commutativity-of-+]. A new class of rule, ~c[:linear-alias] rules, allows one to implement the nqthm package and similar hacks in which a ~il[disable]d function is to be known equivalent to an arithmetic function. A new class of rule, ~c[:built-in-clause] rules, allows one to extend the set of clauses proved silently by ~ilc[defun] during measure and ~il[guard] processing. ~l[built-in-clause]. The new command ~ilc[pcb!] is like ~ilc[pcb] but sketches the ~il[command] and then prints its subsidiary ~il[events] in full. ~l[pcb!]. ~c[:]~ilc[Rewrite] class rules may now specify the ~c[:]~ilc[loop-stopper] field. ~l[rule-classes] and ~pl[loop-stopper]. The rules for how ~il[loop-stopper]s control permutative rewrite rules have been changed. One effect of this change is that now when the built-in commutativity rules for ~ilc[+] are used, the terms ~c[a] and ~c[(- a)] are permuted into adjacency. For example, ~c[(+ a b (- a))] is now normalized by the commutativity rules to ~c[(+ a (- a) b)]; in Version 1.4, ~c[b] was considered syntactically smaller than ~c[(- a)] and so ~c[(+ a b (- a))] is considered to be in normal form. Now it is possible to arrange for unary functions be be considered ``invisible'' when they are used in certain contexts. By default, ~ilc[unary--] is considered invisible when its application appears in the argument list of ~ilc[binary-+]. ~l[loop-stopper] and see :DOC set-invisible-fns-table. Extensive documentation has been provided on the topic of Acl2's ``term ordering.'' ~l[term-order]. Calls of ~ilc[ld] now default ~ilc[ld-error-action] to ~c[:return] rather than to the current setting. The ~il[command] descriptor ~c[:x] has been introduced and is synonymous with ~c[:]~ilc[max], the most recently executed ~il[command]. ~il[History] ~il[command]s such as ~c[:]~ilc[pbt] print a ~c[:x] beside the most recent ~il[command], simply to indicate that it ~st[is] the most recent one. The ~il[command] descriptor ~c[:x-23] is synonymous with ~c[(:x -23)]. More generally, every symbol in the keyword package whose first character is ~c[#\\x] and whose remaining ~il[characters] parse as a negative integer is appropriately understood. This allows ~c[:]~ilc[pbt] ~c[:x-10] where ~c[:]~ilc[pbt] ~c[(:max -10)] or ~c[:]~ilc[pbt] ~c[(:here -10)] were previously used. The old forms are still legal. The order of the arguments to ~ilc[defcong] has been changed. The simplifier now reports the use of unspecified built-in type information about the primitives with the phrase ``primitive type reasoning.'' This phrase may sometimes occur in situations where ``propositional calculus'' was formerly credited with the proof. The function ~ilc[pairlis] has been replaced in the code by a new function ~ilc[pairlis$], because Common Lisp does not adequately specify its ~ilc[pairlis] function. Some new Common Lisp functions have been added, including ~ilc[logtest], ~ilc[logcount], ~ilc[integer-length], ~ilc[make-list], ~ilc[remove-duplicates], ~ilc[string], and ~ilc[concatenate]. The source file ~c[/slocal/src/acl2/axioms.lisp] is the ultimate reference regarding Common Lisp functions in Acl2. The functions ~ilc[defuns] and ~ilc[theory-invariant] have been documented. ~l[defuns] and ~pl[theory-invariant]. A few symbols have been added to the list ~c[*acl2-exports*]. A new key has been implemented for the ~ilc[acl2-defaults-table], ~c[:irrelevant-formals-ok]. ~l[set-irrelevant-formals-ok]. The connected book directory, ~ilc[cbd], must be nonempty and begin and end with a slash. It is set (and displayed) automatically upon your first entry to ~ilc[lp]. You may change the setting with ~ilc[set-cbd]. ~l[cbd]. ~c[:]~ilc[oops] will undo the last ~c[:]~ilc[ubt]. ~l[oops]. Documentation has been written about the ordinals. See :DOC ~c[e0-ordinalp] and see :DOC ~c[e0-ord-<]. [Note added later: Starting with Version_2.8, instead ~pl[o-p] and ~pl[o<].~/ The color ~il[events] ~-[] (red), (pink), (blue), and (gold) ~-[] may no longer be enclosed inside calls of ~ilc[local], for soundness reasons. In fact, neither may any event that sets the ~ilc[acl2-defaults-table]. ~l[embedded-event-form]. ~l[ld-keyword-aliases] for an example of how to change the exit keyword from ~c[:]~ilc[q] to something else. The attempt to install a ~il[monitor] on ~c[:]~ilc[rewrite] rules stored as simple abbreviations now causes an error because the application of abbreviations is not tracked. A new message is sometimes printed by the theorem prover, indicating that a given simplification is ``specious'' because the subgoals it produces include the input goal. In Version 1.4 this was detected but not reported, causing behavior some users found bizarre. ~l[specious-simplification]. ~c[:]~ilc[Definition] rules are no longer always required to specify the ~c[:clique] and ~c[:controller-alist] fields; those fields can be defaulted to system-determined values in many common instances. ~l[definition]. A warning is printed if a macro form with keyword arguments is given duplicate keyword values. Execute ~c[(thm t :doc nil :doc \"ignored\")] and read the warning printed. A new restriction has been placed on ~ilc[encapsulate]. Non-~ilc[local] recursive definitions inside the ~ilc[encapsulate] may not use, in their tests and recursive calls, the constrained functions introduced by the ~ilc[encapsulate]. ~l[subversive-recursions]. (Note added in Version 2.3: Subversive recursions were first recognized by us here in Version 1.5, but our code for recognizing them was faulty and the bug was not fixed until Version 2.3.) The ~il[events] ~ilc[defequiv], ~ilc[defcong], ~ilc[defrefinement], and ~ilc[defevaluator] have been reimplemented so that they are just macros that expand into appropriate ~ilc[defthm] or ~ilc[encapsulate] ~il[events]; they are no longer primitive ~il[events]. See the ~il[documentation] of each affected event. The ~c[defcor] event, which was a shorthand for a ~ilc[defthm] that established a ~il[corollary] of a named, previously proved event, has been eliminated because its implementation relied on a technique we have decided to ban from our code. If you want the effect of a ~c[defcor] in Version 1.5 you must submit the corresponding ~ilc[defthm] with a ~c[:by] hint naming the previously proved event. Error reporting has been improved for inappropriate ~ilc[in-theory] ~il[hints] and ~il[events], and for syntax errors in rule classes, and for non-existent filename arguments to ~ilc[ld]. Technical Note: We now maintain the Third Invariant on ~c[type-alists], as described in the Essay on the Invariants on Type-alists, and Canonicality. This change will affect some proofs, for example, by causing a to rewrite more quickly to ~c[c] when ~c[(equiv a b)] and ~c[(equiv b c)] are both known and ~c[c] is the canonical representative of the three. ~/ ") (deflabel note6 :doc ":Doc-Section release-notes Acl2 Version 1.6 Notes~/ A new key has been implemented for the ~ilc[acl2-defaults-table], ~c[:ignore-ok]. ~l[set-ignore-ok]. It is now legal to have color ~il[events], such as ~c[(red)], in the ~il[portcullis] of a book. More generally, it is legal to set the ~ilc[acl2-defaults-table] in the ~il[portcullis] of a book. For example, if you execute ~c[:red] and then certify a book, the event ~c[(red)] will show up in the ~il[portcullis] of that book, and hence the definitions in that book will all be red (except when overridden by appropriate declarations or ~il[events]). When that book is included, then as always, its ~il[portcullis] must first be ``raised,'' and that will cause the default color to become red before the ~il[events] in the book are executed. As always, the value of ~ilc[acl2-defaults-table] immediately after execution of an ~ilc[include-book], ~ilc[certify-book], or ~ilc[encapsulate] form will be the same as it was immediately before execution (and hence, so will the default color). ~l[portcullis] and, for more about books, ~pl[books]. A theory ~ilc[ground-zero] has been defined to contain exactly those rules that are ~il[enable]d when Acl2 starts up. ~l[ground-zero]. The function ~ilc[nth] is now ~il[enable]d, correcting an oversight from Version 1.5. Customization files no longer need to meet the syntactic restrictions put on ~il[books]; rather, they can contain arbitrary Acl2 forms. ~l[acl2-customization]. Structured directory names and structured file names are supported; see especially the documentation for ~il[pathname], ~il[book-name], and ~ilc[cbd]. Acl2 now works with some Common Lisp implementations other than akcl, including Lucid, Allegro, and MCL. A facility has been added for displaying proof trees, especially using emacs; ~pl[proof-tree]. There is a considerable amount of new ~il[documentation], in particular for the printing functions ~ilc[fmt], ~ilc[fmt1], and ~ilc[fms], and for the notion of Acl2 term (~pl[term]). It is possible to introduce new well-founded relations, to specify which relation should be used by ~ilc[defun], and to set a default relation. ~l[well-founded-relation]. It is possible to make functions suggest new inductions. ~l[induction]. It is possible to change how Acl2 expresses ~il[type-set] information; in particular, this affects what clauses are proved when ~il[force]d assumptions are generated. ~l[type-set-inverter]. A new restriction has been added to ~ilc[defpkg], having to do with undoing. If you undo a ~ilc[defpkg] and define the same package name again, the imports list must be identical to the previous imports or else an explanatory error will occur. ~l[package-reincarnation-import-restrictions]. ~ilc[Theory-invariant] and ~ilc[set-irrelevant-formals-ok] are now embedded event forms. The command ~c[:]~ilc[good-bye] may now be used to quit entirely out of Lisp, thus losing your work forever. This command works in akcl but may not work in every Common Lisp. A theory ~ilc[ground-zero] has been added that contains exactly the ~il[enable]d rules in the ~il[startup] theory. ~l[ground-zero]. ~c[Define-pc-macro] and ~c[define-pc-atomic-macro] now automatically define ~c[:red] functions. (It used to be necessary, in general, to change color to ~c[:red] before invoking these.) ~/ For a proof of the well-foundedness of ~c[e0-ord-<] on the ~c[e0-ordinalp]s, ~pl[proof-of-well-foundedness]. [Note added later: Starting with Version_2.8, ~ilc[o<] and ~ilc[o-p] replace ~c[e0-ord-<] and ~c[e0-ordinalp], respectively.] Free variables are now handled properly for hypotheses of ~c[:]~ilc[type-prescription] rules. When the system is loaded or saved, ~ilc[state] is now bound to ~c[*the-live-state*]. ~ilc[Certify-book] has been modified so that when it compiles a file, it loads that object file. ~ilc[Defstub] has been modified so that it works when the color is hot (~c[:red] or ~c[:pink]). Several basic, but not particularly commonly used, ~il[events] have been added or changed. The obscure axiom ~c[symbol-name-intern] has been modified. The definition of ~c[firstn] has been changed. ~ilc[Butlast] is now defined. The definition of ~ilc[integer-length] has been modified. The left-hand side of the rewrite rule ~c[rational-implies2] has been changed from ~c[(* (numerator x) (/ (denominator x)))] to ~c[(* (/ (denominator x)) (numerator x))], in order to respect the fact that ~ilc[unary-/] is invisible with respect to ~ilc[binary-*]. ~l[loop-stopper]. The `preprocess' process in the waterfall (~pl[hints] for a discussion of the ~c[:do-not] hint) has been changed so that it works to avoid case-splitting. The `simplify' process refuses to force (~pl[force]) when there are ~ilc[if] terms, including ~ilc[and] and ~ilc[or] terms, in the goal being simplified. The function ~c[apply] is no longer introduced automatically by translation of user input to internal form when functions are called on inappropriate explicit values, e.g., ~c[(car 3)]. The choice of which variable to use as the measured variable in a recursive definition has been very slightly changed. ~/ ") (deflabel note7 :doc ":Doc-Section release-notes ACL2 Version 1.7 (released October 1994) Notes~/ ~ilc[Include-book] now takes (optionally) an additional keyword argument, indicating whether a compiled file is to be loaded. The default behavior is unchanged, except that a warning is printed when a compiled file is not loaded. ~l[include-book]. A ~il[markup] language for ~il[documentation] strings has been implemented, and many of the source files have been marked up using this language (thanks largely to the efforts of Laura Lawless). ~l[markup]. Moreover, there are translators that we have used to provide versions of the ACL2 ~il[documentation] in info (for use in emacs), html (for Mosaic), and tex (for hardcopy) formats. A new event ~ilc[defdoc] has been implemented. It is like ~ilc[deflabel], but allows redefinition of ~il[doc] strings and has other advantages. ~l[defdoc]. We used to ignore corollaries when collecting up the axioms introduced about constrained functions. That bug has been fixed. We thank John Cowles for bringing this bug to our attention. The macro ~ilc[defstub] now allows a ~c[:]~ilc[doc] keyword argument, so that ~il[documentation] may be attached to the name being introduced. A new command ~ilc[nqthm-to-acl2] has been added to help Nqthm users to make the transition to ACL2. ~l[nqthm-to-acl2], which also includes a complete listing of the relevant tables. Many function names, especially of the form ``foo~c[-lst]'', have been changed in order to support the following convention, for any ``foo'': ~bf[] ~c[(foo-listp lst)] represents the notion ~c[(for x in lst always foop x)]. ~ef[] A complete list of these changes may be found at the end of this note. All of them except ~c[symbolp-listp] and ~c[list-of-symbolp-listp] have the string ``~c[-lst]'' in their names. Note also that ~c[keyword-listp] has been renamed ~ilc[keyword-value-listp]. Accumulated persistence has been implemented. It is not connected to ~c[:]~ilc[brr] or rule monitoring. ~l[accumulated-persistence]. ~c[:Trigger-terms] has been added for ~c[:]~ilc[linear] rule classes, so you can hang a ~il[linear] rule under any addend you want. ~l[linear], which has been improved and expanded. ACL2 now accepts ~c[256] ~il[characters] and includes the Common Lisp functions ~ilc[code-char] and ~ilc[char-code]. However, ACL2 controls the lisp reader so that ~c[#\\c] may only be used when ~c[c] is a single standard character or one of ~c[Newline], ~c[Space], ~c[Page], ~c[Rubout], ~c[Tab]. If you want to enter other ~il[characters] use ~ilc[code-char], e.g., ~c[(coerce (list (code-char 7) (code-char 240) #\a) 'string)]. ~l[characters]. Note: our current handling of ~il[characters] makes the set of theorems different under Macintosh Common Lisp (MCL) than under other Common Lisps. We hope to rectify this situation before the final release of ACL2. A new ~il[table], ~ilc[macro-aliases-table], has been implemented, that associates macro names with function names. So for example, since ~ilc[append] is associated with ~ilc[binary-append], the form ~c[(disable append)] it is interpreted as though it were ~c[(disable binary-append)]. ~l[macro-aliases-table], ~pl[add-macro-alias] and ~pl[remove-macro-alias]. The implementation of conditional metalemmas has been modified so that the metafunction is applied before the hypothesis metafunction is applied. ~l[meta]. The Common Lisp functions ~ilc[acons] and ~ilc[endp] have been defined in the ACL2 logic. We have added the symbol ~ilc[declare] to the list ~c[*acl2-exports*], and hence to the package ~c[\"ACL2-USER\"]. A new hint, ~c[:restrict], has been implemented. ~l[hints]. It used to be that if ~c[:]~ilc[ubt] were given a number that is greater than the largest current ~il[command] number, it treated that number the same as ~c[:]~ilc[max]. Now, an error is caused. The ~il[table] ~c[:force-table] has been eliminated. A command ~c[:]~ilc[disabledp] (and macro ~ilc[disabledp]) has been added; ~pl[disabledp]. ~il[Compilation] via ~c[:]~ilc[set-compile-fns] is now suppressed during ~ilc[include-book]. In fact, whenever the ~il[state] global variable ~ilc[ld-skip-proofsp] has value ~c[']~ilc[include-book]. ~/ Here are some less important changes, additions, and so on. Unlike previous releases, we have not proved all the theorems in ~c[axioms.lisp]; instead we have simply assumed them. We have deferred such proofs because we anticipate a fairly major changed in Version 1.8 in how we deal with ~il[guard]s. We used to (accidentally) prohibit the ``redefinition'' of a ~il[table] as a function. That is no longer the case. The check for whether a ~il[corollary] follows tautologically has been sped up, at the cost of making the check less ``smart'' in the following sense: no longer do we expand primitive functions such as ~ilc[implies] before checking this propositional implication. The ~il[command] ~ilc[ubt!] has been modified so that it never causes or reports an error. ~l[ubt!]. ACL2 now works in Harlequin LispWorks. The user can now specify the ~c[:trigger-terms] for ~c[:]~ilc[linear] rules. ~l[linear]. The name of the system is now ``ACL2''; no longer is it ``Acl2''. The raw lisp counterpart of ~ilc[theory-invariant] is now defined to be a no-op as is consistent with the idea that it is just a call of ~ilc[table]. A bug was fixed that caused ~il[proof-checker] ~il[instructions] to be executed when ~ilc[ld-skip-proofsp] was ~c[t]. The function ~ilc[rassoc] has been added, along with a corresponding function used in its ~il[guard], ~c[r-eqlable-alistp]. The ~ilc[in-theory] event and hint now print a warning not only when certain ``primitive'' ~c[:]~ilc[definition] rules are ~il[disable]d, but also when certain ``primitive'' ~c[:]~ilc[executable-counterpart] rules are ~il[disable]d. The modified version of ~c[trace] provided by ACL2, for use in raw Lisp, has been modified so that the lisp special variable ~c[*trace-alist*] is consulted. This alist associates, using ~ilc[eq], values with their print representations. For example, initially ~c[*trace-alist*] is a one-element list containing the pair ~c[(cons state '|*the-live-state*|)]. The system now prints an observation when a form is skipped because the default color is ~c[:red] or ~c[:pink]. (Technically: ~c[when-cool] has been modified.) Additional protection exists when you submit a form to raw Common Lisp that should only be submitted inside the ACL2 read-eval-print loop. Here is a complete list of the changes in function names described near the top of this note, roughly of the form ~bv[] foo-lst --> foo-listp ~ev[] meaning: the name ``~c[foo-lst]'' has been changed to ``~c[foo-listp].'' ~bv[] symbolp-listp --> symbol-listp list-of-symbolp-listp --> symbol-list-listp {for consistency with change to symbol-listp} rational-lst --> rational-listp {which in fact was already defined as well} integer-lst --> integer-listp character-lst --> character-listp stringp-lst --> string-listp 32-bit-integer-lst --> 32-bit-integer-listp typed-io-lst --> typed-io-listp open-channel-lst --> open-channel-listp readable-files-lst --> readable-files-listp written-file-lst --> written-file-listp read-file-lst --> read-file-listp writeable-file-lst --> writable-file-listp {note change in spelling of ``writable''} writeable-file-lst1 --> writable-file-listp1 pseudo-termp-lst --> pseudo-term-listp hot-termp-lst --> hot-term-listp {by analogy with pseudo-term-listp} weak-termp-lst --> weak-term-listp weak-termp-lst-lst --> weak-termp-list-listp ts-builder-case-lstp -> ts-builder-case-listp quotep-lst --> quote-listp termp-lst --> term-listp instr-lst --> instr-listp spliced-instr-lst --> spliced-instr-listp rewrite-fncallp-lst --> rewrite-fncallp-listp every-occurrence-equiv-hittablep1-lst --> every-occurrence-equiv-hittablep1-listp some-occurrence-equiv-hittablep1-lst --> some-occurrence-equiv-hittablep1-listp {by analogy with the preceding, even though it's a ``some'' instead of ``all'' predicate] almost-quotep1-lst --> almost-quotep1-listp ffnnames-subsetp-lst --> ffnnames-subsetp-listp boolean-lstp --> boolean-listp subst-expr1-lst-okp --> subst-expr1-ok-listp ~ev[] ~/ ") (deflabel note8 :doc ":Doc-Section release-notes ACL2 Version 1.8 (May, 1995) Notes~/ ~l[note8-update] for yet more recent changes. ~il[Guard]s have been eliminated from the ACL2 logic. A summary is contained in this brief note. Also ~pl[defun-mode] and ~pl[set-guard-checking]. ~il[Guard]s may be included in ~il[defuns] as usual but are ignored from the perspective of admission to the logic: functions must terminate on all arguments. As in Nqthm, primitive functions, e.g., ~ilc[+] and ~ilc[car], logically default unexpected arguments to convenient values. Thus, ~c[(+ 'abc 3)] is ~c[3] and ~c[(car 'abc)] is ~c[nil]. ~l[programming], and see the ~il[documentation] for the individual primitive functions. In contrast to earlier versions of ACL2, Version 1.8 logical functions are executed at Nqthm speeds even when ~il[guard]s have not been verified. In versions before 1.8, such functions were interpreted by ACL2. Colors have been eliminated. Two ``~il[defun-mode]s'' are supported, ~c[:]~ilc[program] and ~c[:]~ilc[logic]. Roughly speaking, ~c[:]~ilc[program] does what ~c[:red] used to do, namely, allow you to prototype functions for execution without any proof burdens. ~c[:]~ilc[Logic] mode does what ~c[:blue] used to do, namely, allow you to add a new definitional axiom to the logic. A global ~il[default-defun-mode] is comparable to the old default color. The system comes up in ~c[:]~ilc[logic] mode. To change the global ~il[defun-mode], type ~c[:]~ilc[program] or ~c[:]~ilc[logic] at the top-level. To specify the ~il[defun-mode] of a ~ilc[defun] locally use ~bv[] ~c[(declare (xargs :mode mode))]. ~ev[] The ~il[prompt] has changed. The initial ~il[prompt], indicating ~c[:]~ilc[logic] mode, is ~bv[] ACL2 !> ~ev[] If you change to ~c[:]~ilc[program] mode the ~il[prompt] becomes ~bv[] ACL2 p!> ~ev[] ~il[Guard]s can be seen as having either of two roles: (a) they are a specification device allowing you to characterize the kinds of inputs a function ``should'' have, or (b) they are an efficiency device allowing logically defined functions to be executed directly in Common Lisp. If a ~il[guard] is specified, as with ~ilc[xargs] ~c[:]~ilc[guard], then it is ``verified'' at defun-time (unless you also specify ~ilc[xargs] ~c[:verify-guards nil]). ~il[Guard] verification means what it always has: the input ~il[guard] is shown to imply the ~il[guard]s on all subroutines in the body. If the ~il[guard]s of a function are verified, then a call of the function on inputs satisfying the ~il[guard] can be computed directly by Common Lisp. Thus, verifying the ~il[guard]s on your functions will allow them to execute more efficiently. But it does not affect their logical behavior and since you will automatically get Nqthm speeds on unverified logical definitions, most users will probably use ~il[guard]s either as a specification device or only use them when execution efficiency is extremely important. Given the presence of ~il[guard]s in the system, two issues are unavoidable. Are ~il[guard]s verified as part of the ~ilc[defun] process? And are ~il[guard]s checked when terms are evaluated? We answer both of those questions below. Roughly speaking, in its initial ~il[state] the system will try to verify the ~il[guard]s of a ~ilc[defun] if a ~c[:]~ilc[guard] is supplied in the ~ilc[xargs] and will not try otherwise. However, ~il[guard] verification in ~ilc[defun] can be inhibited ``locally'' by supplying the ~ilc[xargs] ~c[:]~ilc[verify-guards] ~c[nil]. ``Global'' inhibition can be obtained via the ~c[:]~ilc[set-verify-guards-eagerness]. If you do not use the ~c[:]~ilc[guard] ~ilc[xargs], you will not need to think about ~il[guard] verification. We now turn to the evaluation of expressions. Even if your functions contain no ~il[guard]s, the primitive functions do and hence you have the choice: when you submit an expression for evaluation do you mean for ~il[guard]s to be checked at runtime or not? Put another way, do you mean for the expression to be evaluated in Common Lisp (if possible) or in the logic? Note: If Common Lisp delivers an answer, it will be the same as in the logic, but it might be erroneous to execute the form in Common Lisp. For example, should ~c[(car 'abc)] cause a ~il[guard] violation error or return ~c[nil]? The top-level ACL2 loop has a variable which controls which sense of execution is provided. To turn ``~il[guard] checking on,'' by which we mean that ~il[guard]s are checked at runtime, execute the top-level form ~c[:set-guard-checking t]. To turn it off, do ~c[:set-guard-checking nil]. The status of this variable is reflected in the ~il[prompt]. ~bv[] ACL2 !> ~ev[] means ~il[guard] checking is on and ~bv[] ACL2 > ~ev[] means ~il[guard] checking is off. The exclamation mark can be thought of as ``barring'' certain computations. The absence of the mark suggests the absence of error messages or unbarred access to the logical axioms. Thus, for example ~bv[] ACL2 !>(car 'abc) ~ev[] will signal an error, while ~bv[] ACL2 >(car 'abc) ~ev[] will return ~c[nil]. Note that whether or not ~il[guard]s are checked at runtime is independent of whether you are operating in ~c[:]~ilc[program] mode or ~c[:]~ilc[logic] mode and whether theorems are being proved or not. (Although it must be added that functions defined in ~c[:]~ilc[program] mode cannot help but check their ~il[guard]s because no logical definition exists.) Version 1.8 permits the verification of the ~il[guard]s of theorems, thus insuring that all instances of the theorem will evaluate without error in Common Lisp. To verify the ~il[guard]s of a theorem named ~c[name] execute the event ~bv[] (verify-guards name). ~ev[] If a theorem's ~il[guard]s have been verified, the theorem is guaranteed to evaluate without error to non-~c[nil] in Common Lisp (provided resource errors do not arise). Caveat about ~ilc[verify-guards]: ~ilc[implies] is a function symbol, so in the term ~c[(implies p q)], ~c[p] cannot be assumed true when ~c[q] is evaluated; they are both evaluated ``outside.'' Hence, you cannot generally verify the ~il[guard]s on a theorem if ~ilc[implies] is used to state the hypotheses. Use ~ilc[if] instead. In a future version of ACL2, ~ilc[implies] will likely be a macro. See sum-list-example.lisp for a nice example of the use of Version 1.8. This is roughly the same as the documentation for ~il[guard-example]. We have removed the capability to do ``old-style-forcing'' as existed before Version 1.5. ~l[note5]. NOTE: Some low level details have, of course, changed. One such change is that there are no longer two distinct type prescriptions stored when a function is admitted with its ~il[guard]s verified. So for example, the type prescription ~il[rune] for ~ilc[binary-append] is now ~bv[] (:type-prescription binary-append) ~ev[] while in Versions 1.7 and earlier, there were two such ~il[rune]s: ~bv[] (:type-prescription binary-append . 1) (:type-prescription binary-append . 2) ~ev[] Nqthm-style forcing on ~il[linear] arithmetic assumptions is no longer executed when forcing is ~il[disable]d. Functional instantiation now benefits from a trick also used in Nqthm: once a ~il[constraint] generated by a ~c[:functional-instance] lemma instance (~pl[lemma-instance]) has been proved on behalf of a successful event, it will not have to be re-proved on behalf of a later event. ~ilc[1+] and ~ilc[1-] are now macros in the logic, not functions. Hence, for example, it is ``safe'' to use them on left-hand sides of rewrite rules, without invoking the common warning about the presence of nonrecursive function symbols. A new ~il[documentation] section ~il[file-reading-example] illustrates how to process forms in a file. A new ~il[proof-checker] command ~c[forwardchain] has been added; ~pl[acl2-pc::forwardchain]. It is now possible to use quantifiers. ~l[defun-sk] and ~pl[defchoose]. There is a new event ~ilc[set-inhibit-warnings], which allows the user to turn off warnings of various types. ~pl[set-inhibit-warnings]. An unsoundness relating ~ilc[encapsulate] and ~c[:functional-instance] ~il[hints] has been remedied, with a few small effects visible at the user level. The main observable effect is that ~ilc[defaxiom] and non-local ~ilc[include-book] ~il[events] are no longer allowed in the scope of any ~ilc[encapsulate] event that has a non-empty ~il[signature]. When ~ilc[certify-book] is called, we now require that the default ~il[defun-mode] (~pl[default-defun-mode]) be ~c[:]~ilc[logic]. On a related note, the default ~il[defun-mode] is irrelevant to ~ilc[include-book]; the mode is always set to ~c[:]~ilc[logic] initially, though it may be changed within the book and reverts to its original value at the conclusion of the ~ilc[include-book]. A bug in ~ilc[include-book] prevented it from acting this way even though the ~il[documentation] said otherwise. The ~il[documentation] has been substantially improved. A new section ``Programming'' contains ~il[documentation] of many useful functions provided by ACL2; ~pl[programming]. Also, the ~il[documentation] has been ``marked up'' extensively. Thus in particular, users of Mosaic will find many links in the ~il[documentation]. The symbols ~ilc[force], ~ilc[mv-nth], and ~c[acl2-count] have been added to the list ~c[*acl2-exports*]. We now permit most names from the main Lisp package to be used as names, except for names that define functions, macros, or constants. ~l[name]. We have changed the list of imports from the Common Lisp package to ACL2, i.e., the list ~c[*common-lisp-symbols-from-main-lisp-package*], to be exactly those external symbols of the Common Lisp package as specified by the draft Common Lisp standard. In order to accommodate this change, we have renamed some ACL2 functions as shown below, but these and other ramifications of this change should be transparent to most ACL2 users. ~bv[] warning --> warning$ print-object --> print-object$ ~ev[] Proof trees are no longer enabled by default. To start them up, ~c[:]~ilc[start-proof-tree]. We have added the capability of building smaller images. The easiest way to do this on a Unix (trademark of AT&T) system is: ~c[make small]. ~/ Here we will put some less important changes, additions, and so on. We have added definitions for the Common Lisp function ~ilc[position] (for the test ~ilc[eql]), as well as corresponding versions ~ilc[position-equal] and ~ilc[position-eq] that use tests ~ilc[equal] and ~ilc[eq], respectively. ~l[position], ~pl[position-equal], and ~pl[position-eq]. The ~ilc[defthm] event ~c[rational-listp-implies-rationalp-car] no longer exists. We fixed a bug in the hint mechanism that applied ~c[:by], ~c[:cases], and ~c[:use] ~il[hints] to the first induction goal when the prover reverted to proving the original goal by induction. We fixed a bug in the handling of ~c[(set-irrelevant-formals-ok :warn)]. In support of removing the old-style forcing capability, we deleted the initialization of ~il[state] global ~c[old-style-forcing] and deleted the definitions of ~c[recover-assumptions], ~c[recover-assumptions-from-goal], ~c[remove-assumptions1], ~c[remove-assumptions], and ~c[split-on-assumptions], and we renamed ~c[split-on-assumptions1] to ~c[split-on-assumptions]. The special value ~c['none] in the ~il[proof-checker] commands ~c[claim] and ~ilc[=] has been replaced by ~c[:none]. A bug in the handling of ~il[hints] by subgoals has been fixed. For example, formerly a ~c[:do-not] hint could be ``erased'' by a ~c[:use] hint on a subgoal. Thanks go to Art Flatau for noticing the bug. The functions ~c[weak-termp] and ~c[weak-term-listp] have been deleted, and their calls have been replaced by corresponding calls of ~ilc[pseudo-termp] and ~c[pseudo-term-listp]. The notion of ~ilc[pseudo-termp] has been slightly strenthened by requiring that terms of the form ~c[(quote ...)] have length 2. Performance has been improved in various ways. At the prover level, backchaining through the recognizer alist has been eliminated in order to significantly speed up ACL2's rewriter. Among the other prover changes (of which there are several, all technical): we no longer clausify the input term when a proof is interrupted in favor of inducting on the input term. At the ~il[IO] level, we have improved performance somewhat by suitable declarations and proclamations. These include technical modifications to the macros ~ilc[mv] and ~ilc[mv-let], and introduction of a macro ~c[the-mv] analogous to the macro ~ilc[the] but for forms returning multiple values. The function ~c[spaces] now takes an extra argument, the current column. A bug in the ~il[proof-checker] ~c[equiv] command was fixed. The function ~c[intersectp] has been deleted, because it was essentially duplicated by the function ~ilc[intersectp-equal]. We now proclaim functions in AKCL and GCL before compiling ~il[books]. This should result in somewhat increased speed. The function ~c[repeat] has been eliminated; use ~ilc[make-list] instead. The ~il[proof-checker] command ~c[expand] has been fixed so that it eliminates ~ilc[let] (lambda) expressions when one would expect it to. A new primitive function, ~ilc[mv-nth], has been introduced. ~ilc[Mv-nth] is equivalent to ~ilc[nth] and is used in place of ~ilc[nth] in the translation of ~ilc[mv-let] expressions. This allows the user to control the simplification of ~ilc[mv-let] expressions without affecting how ~ilc[nth] is treated. In that spirit, the rewriter has been modified so that certain ~ilc[mv-nth] expressions, namely those produced in the translation of ~c[(mv-let (a b c)(mv x y z) p)], are given special treatment. A minor bug in ~c[untranslate] has been fixed, which for example will fix the printing of conjunctions. ~c[Translate] now takes a ~c[logicp] argument, which indicates whether it enforces the restriction that ~c[:]~ilc[program] mode functions do not occur in the result. The modified version of ~c[trace] provided by ACL2, for use in raw Lisp, has been modified so that the lisp special variable ~c[*trace-alist*] has a slightly different functionality. This alist associates, using ~ilc[eq], symbols with the print representations of their values. For example, initially ~c[*trace-alist*] is a one-element list containing the pair ~c[(cons 'state '|*the-live-state*|)]. Thus, one may cons the pair ~c[(cons '*foo* \"It's a FOO!\")] on to ~c[*trace-alist*]; then until ~c[*foo*] is defined, this change will have no effect, but after for example ~bv[] (defconst *foo* 17) ~ev[] then ~c[trace] will print ~c[17] as ~c[\"It's a FOO!\"]. ~c[Trace] also traces the corresponding logic function. ~il[Proof-tree] display has been improved slightly in the case of successful proofs and certain event failures. The function ~c[positive-integer-log2] has been deleted. The macro ~ilc[skip-proofs] now prints a warning message when it is encountered in the context of an ~ilc[encapsulate] event or a book. ~l[skip-proofs]. Some functions related to ~c[the-fn] and ~c[wormhole1] now have ~il[defun-mode] ~c[:]~ilc[program], but this change is almost certain to be inconsequential to all users. ~/ ") (deflabel note8-update :doc ":Doc-Section release-notes ACL2 Version 1.8 (Summer, 1995) Notes~/ ACL2 can now use Ordered Binary Decision Diagram technology. ~l[bdd]. There is also a ~il[proof-checker] ~c[bdd] command. ACL2 is now more respectful of the intention of the function ~ilc[hide]. In particular, it is more careful not to dive inside any call of ~ilc[hide] during equality substitution and case splitting. The ~ilc[ld] special (~pl[ld]) ~ilc[ld-pre-eval-print] may now be used to turn off printing of input forms during processing of ~ilc[encapsulate] and ~ilc[certify-book] forms, by setting it to the value ~c[:never], i.e., ~c[(set-ld-pre-eval-print :never state)]. ~l[ld-pre-eval-print]. The TUTORIAL documentation section (now obsolete) has, with much help from Bill Young, been substantially improved to a bona fide introduction. The term pretty-printer has been modified to introduce ~c[(<= X Y)] as an abbreviation for ~c[(not (< Y X))]. Forward chaining and linear arithmetic now both benefit from the evaluation of ground subterms. A new macro ~ilc[set-inhibit-output-lst] has been defined. This should be used when setting the ~il[state] global ~c[inhibit-output-lst]; ~pl[set-inhibit-output-lst] and ~pl[proof-tree]. The test for redundancy in definitions includes the ~il[guard] and type declarations. ~l[redundant-events]. ~l[generalized-booleans] for a discussion of a potential soundness problem for ACL2 related to the question: Which Common Lisp functions are known to return Boolean values? ~/ Here we will put some less important changes, additions, and so on. A bug has been fixed so that now, execution of ~c[:comp t] (~pl[comp]) correctly handles non-standard characters. A bug in ~ilc[digit-char-p] has been fixed, so that the ``default'' is ~c[nil] rather than ~c[0]. ~ilc[True-listp] now tests the final ~ilc[cdr] against ~c[nil] using ~ilc[eq] instead of ~ilc[equal], for improved efficiency. The logical meaning is, however, unchanged. ~ilc[Put-assoc-equal] has been added to the logic (it used to have ~c[:]~ilc[defun-mode] ~c[:]~ilc[program], and has been documented. ~/ ") (deflabel note9 :doc ":Doc-Section release-notes ACL2 Version 1.9 (Fall, 1996) Notes~/ By default, when the system is started it is illegal to use the variable ~ilc[STATE] as a formal parameter of a function definition. The aim is to prevent novice users from stumbling into the Byzantine syntactic restrictions on that variable symbol. Use ~bv[] :set-state-ok t ~ev[] or, equivalently, ~bv[] (set-state-ok t) ~ev[] to switch back to the old default mode. ~l[set-state-ok] ~c[Set-state-ok] is an event that affects the ACL2 defaults table (~pl[acl2-defaults-table]). Recall that when books are included, the defaults table is restored to its pre-inclusion state. Thus, while a ~c[set-state-ok] form will permit the book to define a ~c[state]-using function, it will not permit the user of the book to make such a definition. We recommend putting ~c[(set-state-ok t)] in any book that defines a ~c[state] using function. Books certified under Version 1.8 must be recertified under Version 1.9. See :DOC version. The simplifier has been made to look out for built-in clauses, whereas in past versions such clauses were only noticed by the ``preprocessor'' at the top of the waterfall. THIS CHANGE MAY PREVENT OLD SCRIPTS FROM REPLAYING! The undesirable side-effect is caused by the fact that ~c[:HINTS] require you to refer to clauses by their exact name (~pl[goal-spec]) and because the new simplifier proves more clauses than before, the goals produced have different names. Thus, if a script uses ~c[:HINTS] that refer to clauses other than \"Goal\", e.g., \"Subgoal 1.3\" then the hint may be applied to a different subgoal than originally intended. The use of built-in-clauses has been made more efficient. If a set of clauses arise often in a piece of work, it might be advantageous to build them in even if that results in a large set (hundreds?) of built-in clauses. ~l[built-in-clause] Wormholes can now be used in :logic mode functions. ~l[wormhole] It is now possible to provide ``computed hints.'' For example, have you ever wished to say ``in all goals with a name like this, :use that'' or ``if this term is in the subgoal, then :use that''? Well, ~pl[computed-hints] and the extraordinarily long example in ~pl[using-computed-hints]. ~c[Hide] terms may be rewritten with :rewrite rules about ~c[hide]. ~l[hide], where we also now explain why ~c[hide] terms are sometimes introduced into your proof attempts.~/ A bug that sometimes caused the ``non-lazy IF'' hard error message was fixed. A bug that sometimes caused a hard error in forward chaining was fixed. A bug in print-rules (:pr) was fixed. We report the use of :executable-counterparts in the evaluation of SYNTAXP forms. Some documentation errors were fixed. A bug in parent-tree tracking in add-literal-and-pt was fixed. A bug in ok$, go$ and eval$ was fixed. Clausify now optimizes (mv-nth 'k (list x0 ... xk ... xn)) to xk. ~/ ") (deflabel note-2-0 :doc ":Doc-Section release-notes ACL2 Version 2.0 (July, 1997) Notes~/ This is the first version of ACL2 released under the copyright of the University of Texas (UT). Future releases of ACL2 will be made from UT rather than Computational Logic, Inc. (CLI). Version 2.0 is just Version 1.9 as released by CLI, with a few bugs fixed. A bug causing an infinite loop was fixed in functional instantiation. The bug manifested itself when two conditions occurred simultaneously: First, the functional substitution replaces a function symbol, e.g., ~c[FOO], with a ~c[LAMBDA] expression containing a free variable (a variable not among in the ~c[LAMBDA] formals). And, second, in one of the constraints being instantiated there is a call of the function symbol ~c[FOO] within the scope of another ~c[LAMBDA] expression. Unless you used such a functional substitution, this bug fix will not affect you. ~/ Less important notes: The implementation of ~c[PRINC$] was changed so that it was no longer sensitive to the external setting of ~c[*print-base*] and other Common Lisp special variables. Typographical errors were fixed in the documentation. ~/ ") (deflabel note-2-1 :doc ":Doc-Section release-notes ACL2 Version 2.1 (December, 1997) Notes~/ The identity function ~ilc[case-split] has been added. It is similar to ~ilc[force] but causes an immediate split of the top-level goal on whether the indicated hypothesis is true. ~/ Less important notes: Minor bugs in the documentation were fixed. ~/ ") (deflabel note-2-2 :doc ":Doc-Section release-notes ACL2 Version 2.2 (August, 1998) Notes~/ Important changes: A bug was fixed in the compile command, ~c[:comp]. The compiled code produced by ~c[:comp] in previous versions could be wildly incorrect because of a confusion between the printer and the reader regarding what was the current Lisp ~c[*package*]. This bug could manifest itself only if you used the ~c[:comp] command to compile previously uncompiled functions while the current package was different from ~c[\"ACL2\"]. What happened in that situation depended upon what symbols were imported into your current package. The most likely behavior is that the compiler would break or complain or the resulting compiled code would call functions that did not exist. There have been no other important changes to the code. However, this release contains some useful new books, notably those on the ~c[books] subdirectories ~c[cli-misc] and ~c[ihs]. Both have ~c[README] files. The ~c[ihs] books provide support for integer hardware specifications. These books were crucial to Bishop Brock's successful modeling of the Motorola CAP. We thank Bishop for producing them and we thank all those who worked so hard to get these books released. We highly recommend the ~c[ihs] books to those modeling ALUs and other arithmetic components of microprocessors or programming languages. In previous versions of ACL2, the arithmetic books, found on ~c[books/arithmetic/], included the addition of several unproved axioms stating properties of the rationals that we believed could be derived from our ``official'' axioms but which we had not mechanically proved. The axioms were found in the book ~c[rationals-with-axioms.lisp], which was then used in the uppermost arithmetic books ~c[top.lisp] and ~c[top-with-meta.lisp]. John Cowles has now provided us with ACL2 proofs of those ``axioms'' and so in this release you will find both ~c[rationals-with-axioms.lisp] and ~c[rationals-with-axioms-proved.lisp]. The former is provided for compatibility's sake. The latter is identical but contains ~c[defthm]s where the former contains ~c[defaxiom]s. The top-most books have been rebuilt using ``~c[-axioms-proved]'' book. Thanks John. ~/ Less important notes: Bishop Brock found a bug in ~c[translated-acl2-unwind-protectp4]. Jun Sawada reported a bug in linear arithmetic that caused us not to prove certain trivial theorems concluding with ~c[(not (equal i j))]. We have fixed both. We now prohibit definitions that call certain event commands such as ~c[DEFTHM] and ~c[TABLE] because our Common Lisp implementations of them differ from their ACL2 meanings (so that compiled books can be loaded correctly and efficiently). Minor bugs in the documentation were fixed. ~/ ") (deflabel note-2-3 :doc ":Doc-Section release-notes ACL2 Version 2.3 (October, 1998) Notes~/ Important changes: Versions of ACL2 preceding this one contain a subtle soundness bug! We found a flaw in our detection of ~il[subversive-recursions]. The bug allowed some subversive recursions to slip through undetected. We believe it would have been difficult to have exploited this flaw inadvertently. In particular, the following five conditions are necessary. ~nl[]~nl[] (1) Introduce a constrained function, say ~c[f], via an ~c[encapsulate]. ~nl[]~nl[] (2) In the same encapsulation, define a clique of mutually recursive functions. This clique must be non-~c[local] and in ~c[:logic] mode. ~nl[]~nl[] (3) In that mutually recursive clique, use the constrained function ~c[f] (perhaps indirectly) so that the termination argument for the clique depends on properties of the ~i[witness] for ~c[f]. Thus, ~c[f] or some other function dependent upon ~c[f], must be used in an argument in a recursive call or in a term governing a recursive call. Furthermore, the use of ~c[f] must be such that the termination proof cannot be done without exploiting properties of the witness for ~c[f]. Other uses of the constrained functions in the clique are ok. ~nl[]~nl[] (4) Fail to include the exploited properties of ~c[f] among the constraints of the encapsulation. ~nl[]~nl[] (5) Later, outside the encapsulation, explicitly use a functional instantiation in which ~c[f] is replaced by a function not enjoying the crucial properties. ~nl[]~nl[] See ~il[subversive-recursions] for details. ~/ Less important notes: We have begun to write some introductory tutorial material for those who wish to learn to program in ACL2. Most of this material is HTML-based. See the Hyper-Card on the ACL2 home page. The documentation of ~ilc[verify-guards] was improved to explain why one might wish to verify the ``guards'' of a ~c[defthm] event. The missing documentation was noticed by John Cowles. A bug was fixed in cross fertilization. The bug caused the system to report that it had substituted one term for another when in fact no substitution occurred. The bug was noticed by Bill McCune. ~/ ") (deflabel note-2-4 :doc ":Doc-Section release-notes ACL2 Version 2.4 (August, 1999) Notes~/ Important changes: We corrected a soundness bug in Version 2.3 related to the handling of ~ilc[immediate-force-modep]. The bad behavior was noticed by Robert Krug. Thanks! We corrected a bug that permitted ~ilc[verify-guards] to accept a function even though a subfunction had not yet had its guards verified. Thanks to John Cowles for noticing this. User defined single-threaded objects are now supported. See ~il[stobj]. ~/ Less important notes: We corrected a bug that prevented the intended expansion of some recursive function calls. We changed the handling of the primitive function ~ilc[ILLEGAL], which is logically defined to be ~c[nil] but which is programmed to signal an error, so that when it is evaluated as part of a proof, it does not signal an error. The old handling of the function prevented some guard proofs involving ~ilc[THE] or ~ilc[LET]s with internal declarations. We corrected a bug that permitted some ~c[LOCAL] ~c[DEFAXIOM] events to slip into certified books. We corrected a bug that prevented the correct undoing of certain ~c[DEFPKG] forms. Changes were made to support CMU Lisp. Pete Manolios helped with these changes. Changes were made to make the make files more compatible with Allegro Common Lisp. Jun Sawada, who has been a great help with keeping ACL2 up and running at UT on various platforms, was especially helpful. Thanks Jun. ~/ ") (deflabel note-2-5 :doc ":Doc-Section release-notes ACL2 Version 2.5 (June, 2000) Notes~/ Important Changes: Concurrent with the release of ACL2 Version 2.5 is the publication of two books about ACL2. See the ``Books and Papers about ACL2 and Its Applications'' on the ACL2 Home Page. The ~c[books] subdirectory now contains many new certifiable books, including solutions to the exercises in the two published books and full scripts for the case studies. See ~c[books/README.html]. Improved Unix ~c[Makefile] support for book certification has also been written. See ~c[books/README.html]. The list of symbols in ~c[*acl2-exports*] has been considerably expanded. If you have packages built by importing ~c[*acl2-exports*] you might want to look carefully at the new value of that constant. The new value includes all ~c[:logic] mode functions as of Version 2.5, as well as all documented macros and all built-in theorem names. ~ilc[Include-book] and ~ilc[certify-book] were modified to have some additional keyword arguments. It is possible to certify a book containing ~ilc[defaxiom] and/or ~ilc[skip-proofs] events and get warning messages or errors signaled, according to the settings of these new flags. In addition, it is possible to specify in ~c[include-book] whether the book must be certified (under penalty of error if not). The default values of these new arguments cause warnings to be printed rather than errors signaled. The above change involved altering the form of certificate files. When books certified under previous versions are included, more warnings will be generated because these books are considered possibly to contain ~c[defaxiom] and/or ~c[skip-proofs] events. We anticipate further changes to this aspect of books and consider the current mechanisms (for controlling whether warnings or errors are signaled) just a prototype. See also the discussion below of ``soundness related'' warnings. Your suggestions are welcome. A discrepancy between ACL2 and Common Lisp was fixed, having to do with ~c[declare ignore]. In past versions of ACL2, a formal parameter of a ~c[defun] was considered ignored if it was not used in the body, the guard or the measure of the ~c[defun]. That meant that a variable used only in the guard could not be declared ignored in ACL2; but some Common Lisp compilers would complain because the variable was not used in the body. Now, ACL2 considers a variable ignored if it is not used in the body. ACL2 can now be built in releases 5.0 and later of Allegro Common Lisp. (Other releases of Allegro Common Lisp and of other lisps continue to be supported as well.) This includes Allegro Common Lisp running on Windows 98 platforms. John Cowles helped us do some testing and answered questions for us. Thanks John! We incorporated Ruben Gamboa's changes to allow the building of a variant, ACL2(r), of ACL2, in which the user can reason about the real numbers using non-standard analysis. ~l[real]. Note that ACL2(r) and ACL2 have different underlying theories, and books certified in one system may not be included in the other. For backward compatibility and to ensure a smooth transition, ACL2 is built by default, not ACL2(r). This is a compile-time switch; see the makefile for instructions. There should be no changes to ACL2 resulting from the capability of building ACL2(r) from the same sources. Also ~pl[acknowledgments] for more on the history of ACL2(r). A large number of bugs (some affecting soundness) were fixed, and many small new features were added. See below. ~/ Less Important Changes: Some warnings are now considered ``soundness related,'' namely, those that advise you that an uncertified book has been included or that a book containing ~c[DEFAXIOM] or ~c[SKIP-PROOFS] events. (Technically, ~c[DEFAXIOM]s do not imperil soundness in the proof- theoretic sense, though they may imperil the validity of theorems. But you sould know when a book has added an axiom to your logic!) In previous versions of ACL2, all warnings were inhibited if the token ~c[warning] was included in the argument to ~ilc[set-inhibit-output-lst]. Now, soundness related warnings are printed even if ~c[warning]s have been inhibited. To inhibit all warnings, supply the token ~c[warning!] to ~c[set-inhibit-output-lst]. Several bugs in ~ilc[defstobj] were fixed, relating to the possibility that some of the subfunctions introduced by the ~c[defstobj] were already defined. ~c[:]~ilc[Puff] no longer tries to expand ~ilc[defstobj] events. Previously, the attempt would cause a hard error. A soundness bug was fixed. The bug might have been exercised if you had an alternative definition (implies hyps (equiv (fn ...) body)) in which equiv is an equivalence relation other than EQUAL. In this case, calls of fn might have been expanded to body in places that were not equiv-hittable. An obscure soundness bug was fixed. The bug was exercised only if you had a metafunction with a computed hypothesis (i.e., a ``meta hypothesis function''), the hypothesis contained a free variable, i.e., a variable not involved in the term being rewritten, and the free variable occurred in the output of the metafunction. The possibility of this bug was brought to our attention by Robert Krug. We fixed a bug in the handling of ~c[hide] related to the question of whether a variable symbol occurs in a term. The old code did not find the variable and could cause the system to throw away a hypothesis about it on the grounds that it was never mentioned. Rob Sumners helped discover this problem. The handling of ~c[:]~ilc[elim] rules was generalized, permitting arbitrary known equivalence relations instead of merely ~c[equal] in the concluding equality. The printing of runes (rule names; ~pl[rune]) used has been made \"deterministic,\" both in proof output and in proof attempt summaries, by sorting the runes before printing. The handling of free variables has been improved for hypotheses such as ~c[(< 0 X)], and more generally, any hypotheses involving a comparison with ~c[0] (even for example ~c[(< X 1)] where ~c[X] is known to be an integer, which is handled as ~c[(<= X 0)]). Thanks to Robert Krug for bringing relevant examples to our attention. A new value, ~c[:comp], has been implemented for the ~c[:load-compiled-file] keyword of ~ilc[include-book]. If this value is supplied, then a compiled file will always be loaded, even if that requires creating the compiled file first. The event ~c[include-book] now generates a warning when a compiled file is expected but not found (~pl[include-book]). Formerly, it only did so when executed at the top level; it failed to generate the warning when executed on behalf of a surrounding ~c[include-book] command. Certain redefinition warnings generated by Allegro Common Lisp have been eliminated. A new key has been implemented for the ~ilc[acl2-defaults-table], ~c[:bogus-mutual-recursion-ok], set with ~c[:]~ilc[set-bogus-mutual-recursion-ok]. Thanks to David Russinoff for pointing out the utility of such a key. A bug was fixed in ~ilc[defun-sk] that prevented its generated events from being accepted when guard verification is being performed. Thanks to Bill Young for bringing this problem to our attention. A second bug was brought to our attention by Pete Manolios, which was causing certain ~ilc[defun-sk] events to be rejected. That problem has been fixed, and an \"Infected\" warning has also been eliminated. The command ~ilc[good-bye] now works with Allegro Common Lisp. A low-level bug was fixed that could, for example, cause an error such as \"Error: Expected 5 args but received 4 args\" when interrupting a ~c[local] event. A bug has been fixed in the ~il[proof-checker] related to definition expansion. Thanks to Pete Manolios for bringing this to our attention with a simple example. A bug has been fixed related to the ~c[:]~il[bdd] hint in the presence of ~il[equivalence] relations. Thanks to Pete Manolios for bringing this to our attention with a simple example. The functions ~ilc[position] and ~ilc[position-equal] formerly required the second argument to be a true list. In accordance with Common Lisp, we now also allow the second argument to be a string. This could cause earlier proofs about these functions to fail unless ~ilc[true-listp] is known to hold where necessary. Robert Krug wrote a patch, which has been incorporated, to prevent certain infinite loops that can arise in linear arithmetic. Thanks, Robert! The macro ~ilc[let*] no longer requires the bound variables to be distinct. An obscure bug was fixed related to congruence rules. The bug would sometimes cause ACL2 to behave as though no rules (other than equality) were available for some argument positions. Thanks to Pete Manolios for bringing this bug to our attention. Documentation topics have been added for ~ilc[hard-error] and ~ilc[prog2$], and the documentation for ~ilc[illegal] has been improved. Thanks to Rob Sumners for a useful suggestion in the examples in documentation for ~c[prog2$] and a fix in documentation for ~ilc[sublis]. The event form ~ilc[certify-book] was made more secure, in that it can now catch attempts to write a book to disk during its certification. Thanks to Rob Sumners for pointing out the insecurity of the existing mechanism. A Y2K problem was fixed with our applicative handling of dates. Accessors and updaters for ~ilc[stobj]s have been made more efficient when the underlying lisp is Allegro Common Lisp, by the use of appropriate simple array declarations. A raw Lisp break had been possible when a certified book that had no guard verification was included in a session after ~c[(]~ilc[set-verify-guards-eagerness]~c[ 2)]. This has been fixed. The keyword command ~c[:]~ilc[comp] can now be used to compile only raw Lisp functions, excluding executable counterparts, by supplying the argument ~c[:raw]. Rewrite rule ~c[nth-of-character-listp] was removed from source file axioms.lisp since it is essentially subsumed by ~c[characterp-nth]. Printing has been sped up. In one example the improvement was over 50% in both Allegro and GCL. We now allow printing in a \"downcase\" mode, where symbols are printed in lower case. All printing functions except ~c[print-object$] now print characters in lower case for a symbol when the ACL2 state global variable ~c[print-case] has value ~c[:downcase] and vertical bars are not necessary for printing that symbol. ~l[IO] for a discussion of the macros ~c[acl2-print-case] and ~c[set-acl2-print-case]. The default printing remains unchanged, i.e., symbols are printed in upper case when vertical bars are not required. A low-level printing function (~c[prin1$]) was modified so that it is not sensitive to various Common Lisp globals related to printing. So for example, the function ~ilc[fmt] is no longer sensitive to the value of Common Lisp global ~c[*print-case*]. (The preceding paragraph explains how to control the case for printing in ACL2.) The definition of ~ilc[array1p] was fixed so that the ~c[:maximum-length] of an array must be strictly greater than the number specified in the ~c[:dimensions] field; they may no longer be equal. This was always the intention; the documentation (~pl[arrays]) has remained unchanged. The corresponding change was also made to ~ilc[array2p]. Allegro Common Lisp formerly caused an error when ~ilc[compress1] was called on an array where the numbers above were equal; now, we get a guard violation instead, which is appropriate. In the context of theories, a name now represents not just the corresponding ~c[:definition] ~il[rune], as it has done in earlier versions of ACL2, but also the corresponding ~c[:]~ilc[induction] rune. ~l[theories] for a discussion of runic designators. Most users will rarely, if ever, notice this change. One situation where this change will make a difference is after executing ~c[(in-theory (current-theory 'foo))] followed by ~c[(in-theory (enable bar))], where function ~c[bar] is introduced after event ~c[foo], and ~c[bar] is recursively defined. The latter ~ilc[in-theory] form now enables the rune ~c[(:induction bar)], which implies that the prover can use the induction scheme stored at definition time for ~c[bar]. Formerly, the rune ~c[(:induction bar)] was not enabled by ~c[(in-theory (enable bar))], and hence the induction scheme for ~c[bar] was ignored even when explicit ~c[:induct] hints were supplied. You may now supply ~ilc[xargs] keyword pair ~c[:normalize nil] in order to prevent certain definitions from ``hanging'' when there are many ~c[if]-subexpressions. ~pl[defun]. We now translate type declarations of ~c[real] into guards, as we have already done for other types such as ~c[rational]. For example, ~c[(declare (type real x))] generates the ~il[guard] ~c[(rationalp x)]. ~l[type-spec]. The theorem prover now behaves reasonably under the combination of specifying a value of ~c[t] both for ~c[:]~ilc[otf-flg] and for a hint ~c[:do-not-induct]. Previously, it aborted the first time it would have otherwise pushed a goal for induction, but now, it will continue and wait until all induction subgoals have been pushed before it aborts. We changed slightly the definition of ~ilc[round]. However, we believe that the new definition is equivalent to the old. The definition of Common Lisp function ~ilc[substitute] has been added. The following changes have been made in the use of file names within ACL2. We thank Warren Hunt and John Cowles for running some tests of these changes on Macintosh and Windows 98 platforms (respectively).~bq[] (1) Names of directories and files now use a syntax like that used for Unix (trademark of AT&T), where directories are separated using the ``~c[/]'' character even when the operating system is not Unix or Linux. ~l[pathname]. ACL2 also continues to support its notion of ~em[structured pathnames] from Version 2.4 and before, but might not do so in future releases and hence no longer documents such syntax. (2) The command ~c[:]~ilc[set-cbd] may now take a relative pathname as an argument. (3) When the macro ~ilc[ld] is given a file name as a value for ~ilc[standard-oi], then if that file name is a relative pathname it refers to the result of prepending the connected book directory (~pl[pathname], ~pl[cbd], and ~pl[set-cbd]) in order to obtain an absolute pathname. Simiarly for the ~c[ld] specials ~ilc[standard-co] and ~ilc[proofs-co]. ~eq[]It is no longer necessary to issue ~c[:]~ilc[set-state-ok]~c[ t] if you include a ~il[stobj] declaration for ~ilc[state], for example: ~bv[] (declare (xargs :stobjs state)) ~ev[] ~l[declare-stobjs]. The ~il[proof-checker] has been cleaned up a bit, including the documentation and the capability (once again) to define pc-macro commands (~pl[define-pc-macro]) and proof-checker meta commands (~pl[define-pc-meta]). Recall that events generate summaries that include a line beginning with ``~c[Warnings:]'', which is followed (on the same line) by zero or more brief strings that summarize the warnings generated by that event. Formerly, this warnings summary for an ~ilc[encapsulate] or ~ilc[include-book] event did not include the summary strings for warnings generated by subsidiary events. This has been fixed. Macro ~ilc[cw] has been documented and now expands to a call of a ~c[;]~ilc[logic] mode function. ~l[cw] for a way to print to the screen without having to involve the ACL2 ~ilc[state]. Thanks to Rob Sumners for suggesting that we document this useful utility. Functions ~c[duplicates], ~c[add-to-set-equal], ~c[intersection-eq], ~c[evens], and ~c[odds] are now ~c[:]~ilc[logic] mode functions. ~/ ") ; Do not make note-2-5(r) below conditional on #+:non-standard-analysis, ; because we want to make just one version of the documentation. (deflabel |NOTE-2-5(R)| :doc ":Doc-Section release-notes ACL2 Version 2.5(r) (June, 2000) Notes~/ Important changes to non-standard version: ~/ Please ~pl[note-2-5] for changes to Version 2.5 of ACL2. We hope to write more documentation for ACL2(r) in the future. ~/ ") (deflabel note-2-6 :doc ":Doc-Section release-notes ACL2 Version 2.6 (November, 2001) Notes~/ Because of the large number of modifications, we have divided up the Version 2.6 notes into the following subtopics.~bq[] o New functionality (~pl[note-2-6-new-functionality]):~nl[] o Changes in proof engine (~pl[note-2-6-proofs]):~nl[] o Changes in rules and definitions (~pl[note-2-6-rules]):~nl[] o Guard-related changes (~pl[note-2-6-guards]):~nl[] o Proof-checker changes (~pl[note-2-6-proof-checker]):~nl[] o System-level changes (~pl[note-2-6-system]):~nl[] o Other (minor) changes (~pl[note-2-6-other]):~nl[] ~eq[]~/~/") (deflabel note-2-6-new-functionality :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on New Functionality~/ A fundamental change is the provision of the ``nu-rewriter'' for simplifying expressions composed of ~c[NTH], ~c[UPDATE-NTH], and ~c[UPDATE-NTH-ARRAY] applications and ~c[LET] expressions and other calls of non-recursive functions or ~c[LAMBDA] expressions involving those symbols. The nu-rewriter applies the obvious rewrite rule for ~c[(NTH i (UPDATE-NTH j v s))] and the analogous rule for ~c[UPDATE-NTH-ARRAY]. ~l[nu-rewriter] The nu-rewriter can be enabled with ~ilc[set-nu-rewriter-mode]. A new flag has been added to the ~c[xargs] of ~ilc[defun] permitting the declaration that the function is ~c[non-executable]. The usage is ~c[(declare (xargs :non-executable t))] and the effect is that the function has no executable counterpart. On the positive side: the function is permitted to use single-threaded object names and functions arbitrarily, as in theorems rather than as in executable definitions. Such functions are not permitted to declare any names ~c[:]~ilc[stobj]~c[s] but accessors, etc., may be used, just as in theorems. A new flag has been added to permit the system to abbreviate output by introducing ~c[LET*] notation identifying common subterms. The formula being proved is not affected; this flag changes its displayed form only. See ~il[set-let*-abstractionp]. A ``raw mode'' has been added, primarily for faster loading of applications. ~pl[set-raw-mode]. Functions ~ilc[alphorder] and ~ilc[lexorder] have been put in ~c[:]~ilc[logic] mode. ~c[Lexorder] is now a total order ordering of the ACL2 universe, and theorems are included to that effect. Thanks to Pete Manolios for suggesting the idea and providing events to use, and to Rob Sumners for assistance with some modifications. See also the new book ~c[books/misc/total-order] for an irreflexive total order. The ACL2 user can now make system calls to the host operating system. ~l[sys-call] and ~pl[sys-call-status]. Thanks to Rob Sumners for working out this idea with Pete Manolios and Robert Krug, who we also thank, and for working out the implementation with us. It is no longer required to use absolute ~il[pathname]s in ~ilc[include-book] forms that have been executed before a ~ilc[certify-book]. Any relative pathname strings in such contexts will be expanded into absolute pathnames before they are saved in the ~ilc[portcullis] of the ~ilc[certificate] of the book being certified. ACL2 can now be built on top of Allegro Common Lisp 6.0, and also on Windows platforms on top of Allegro Common Lisp and GCL. Thanks to Pete Manolios and Vinay K. Siddhavanahalli for their help with Windows. Rob Sumners has designed and provided an initial implementation for two improvements to ~ilc[defstobj] (also ~pl[stobj]). First, array fields can now be resized. Resize and length functions are provided for array fields, which can be used to resize stobj array fields dynamically. The recognizers for array fields have been simplified to accommodate this change, so that they only check that each element of the array field has the specified type. Second, performance has been improved for stobjs with a large number of fields, by changing their Common Lisp implementation to store the fields in a simple vector instead of a list. Now ~il[stobj]s may be bound locally; ~pl[with-local-stobj]. Thanks to Rob Sumners, who encouraged us to implement this capability, was an early user of it, and participated usefully in discussions on its design. New functions ~ilc[fms!], ~ilc[fmt!], and ~ilc[fmt1!] are the same as their respective functions without the ``~c[!],'' except that the ``~c[!]'' functions are guaranteed to print forms that can be read back in (at a slight readability cost). We added ~ilc[extended-metafunctions], metafunctions which allow ~ilc[state] and context sensitive rewriting to some extent. We thank Robert Krug for pushing for and on this idea. The documentation has been improved. In particular, a new documentation topic provides a gentle introduction to ACL2 ~ilc[arrays] ~-[] ~pl[arrays-example] ~-[] and additional documentation has been provided for getting started with proof trees in emacs ~-[] ~pl[proof-tree]. New Makefile targets ~c[fasl] and ~c[o] have been added to the ~c[books/] directory of the distribution. For example, you might first certify books using an ACL2 built on top of GCL (which creates compiled files with suffix ~c[o]). Then, when standing in the ~c[books/] directory, you might execute the command~bq[] make fasl ACL2=my-allegro-acl2 ~eq[]which will create compiled (~c[.fasl]) files for Allegro Common Lisp, assuming that ~c[my-allegro-acl2] starts up an ACL2 built on that Common Lisp. The macro ~ilc[let*] now allows variables to be declared ignored. ~l[let*] and ~pl[let]. The user may now control backchaining. This feature was designed and primarily implemented by Robert Krug (though the authors of ACL2 are resposible for any errors); thanks, Robert! ~l[backchain-limit]. It is now possible to ``slow down'' the rate at which case splits are generated by the simplifier. ~l[set-case-split-limitations]. Accesses to ~il[stobj]s using ~ilc[nth] or ~ilc[update-nth] are now displayed using symbolic constants instead of numeric indices. For example, given the event ~bv[] (defstobj foo a b :renaming ((b c))) ~ev[] then the term ~c[(nth 0 foo)] will be displayed (for example, during proofs) as ~c[(nth *a* foo)] while ~c[(nth 1 foo)] will be displayed as ~c[(nth *c* foo)]. The ~ilc[defstobj] event now correspondingly introduces a ~ilc[defconst] event for each field accessor function, introducing a constant whose name is obtained from the accessor's name by prefixing and suffixin a ``~c[*],'' as in the example above: accessor ~c[a] generates ~c[(defconst *a* 0)] and accessor ~c[c] generates ~c[(defconst *c* 1)]. ~l[nth-aliases-table] for how to extend this feature for alternate names of ~il[stobj]s. Computed hints have been improved. It is now possible to detect within a computed hint whether the goal clause is stable under simplification; it is also possible for a computed hint to change the list of available hints. ~l[computed-hints]. It is now possible to provide ``default hints'' that are appended to the hints explicitly provided. ~l[set-default-hints]. Using computed hints (~pl[computed-hints]) and default hints (~pl[set-default-hints]) it is possible to implement a book that supports ``priority phased simplification.'' Using this book you can assign priorities to your rules and cause the theorem prover to simplify each goal maximally under all the rules of one priority before enabling rules of the next priority. See ~c[books/misc/priorities.lisp]. The macro ~ilc[defabbrev] has been improved to allow ~ilc[declare] forms and documentation strings and to do more error-checking. Thanks to Rob Sumners for designing this enhancement and providing the first implementation. ~l[defabbrev]. Further changes were made to support CMU Lisp. Wolfhard Buss helped with these changes. A new table was added that is used when printing proof output, so that nests of right-associated calls of a binary function are replaced by corresponding macro calls, as has been the case for ~ilc[binary-+] and ~ilc[+], ~ilc[binary-append] and ~ilc[append], and so on. ~l[add-binop]. Operators ~ilc[logand], ~ilc[logior], ~ilc[logxor], and ~ilc[logeqv] are now macros (formerly, they were functions) that call corresponding binary functions (e.g., ~c[binary-logand]) defined in source file ~c[\"axioms.lisp\"]. Thanks to Rob Sumners for this enhancement. Proof output will however continue to show calls of ~ilc[logand], ~ilc[logior], ~ilc[logxor], and ~ilc[logeqv]. Function ~c[(]~ilc[allocate-fixnum-range]~c[ fixnum-lo fixnum-hi)] sets aside more \"permanent\" fixnums in GCL. ACL2 now runs under ~c[CLISP]. Thanks to Wolfhard Buss and Sam Steingold for their assistance with the port. Michael ``Bogo'' Bogomolny has created a search engine, accessible from the ACL2 home page. For that purpose he modified the HTML translator to create one file per topic (a good idea in any case). Thanks, Bogo! An emacs file of potential (but optional) use for ACL2 users may be found in ~c[emacs/emacs-acl2.el]. In particular, this file supports the use of proof trees (~pl[proof-tree]). Some ~il[books] have been added or modified. In particular, Robert Krug has contributed ~c[books/arithmetic-2/], which provides an alternative to the existing collection of books about arithmetic, ~c[books/arithmetic/]. For a discussion of the distributed books see the link to ~c[README.html] in the installation instructions. ~/~/") (deflabel note-2-6-proofs :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on Changes in Proof Engine~/ Certain optimizations are performed when converting terms to clausal form. For example, ~c[(< 0 1)] is known to be ~c[t], ~c[(HARD-ERROR ctx str alist)] is known to be ~c[nil], and ~c[(INTEGERP n)] is known to imply ~c[(RATIONALP n)]. In earlier versions of ACL2, the conversion of a term to clausal form expanded ~c[LAMBDA] applications. That may no longer occur. Some proofs may slow down (or fail) because your ~c[LAMBDA]-expressions are not expanded away when you ``expected'' them to be. Robert Krug found a soundness bug in our linear arithmetic package. The bug was caused by the derivation of an equation from two inequalities without taking adequate precautions to ensure that both sides of the inequalities were numeric. Robert also kindly provided a fix which we adopted. Thanks Robert! We fixed a bug that could prevent the application of a metatheorem. A bug has been fixed that had caused bogus forcing rounds (~pl[forcing-round]). The bug could occur when the hypothesis of a rule was forced (~pl[force]) before the prover decided to start over and prove the original goal by induction. Thanks to Rob Sumners for drawing our attention to this problem. Some low-level fixes have been made that prevent certain infinite loops, based on reports by users. We thank Yunja Choi, Matt Wilding, and Pete Manolios for reporting such problems. An obscure potential soundness hole has been fixed by redoing the way evaluation takes place in the ACL2 loop and during theorem proving. We expect that users will see no difference based on this change. (Those interested in the details can see the long comment ``Essay on Evaluation in ACL2'' in source file interface-raw.lisp.) A small change was made in computation for a heuristic that controls backchaining. This will speed up proofs dramatically in a very few cases but should have a very small impact in general. The simplifier has been modified to avoid eliminating hypotheses of goals that can be established by contextual (specifically, type-set) reasoning alone. We believe that this change will generally strengthen ACL2's reasoning engine, although on rare occasions a lemma that formerly was provable may require user assistance. Thanks to Robert Krug for suggesting this change and providing its implementation. Case splits are now limited, by default. This may allow some proof attempts to provide output where previously the prover would appear to ``go out to lunch.'' For a more complete discussion, including instructions for how users can control case splitting, ~pl[set-case-split-limitations]. A bug has been fixed in the handling of ~c[:]~ilc[type-prescription] rules by the ~il[bdd] package. Thanks to Rob Sumners for discovering this bug and supplying a helpful example. ACL2 may now use the built-in induction scheme for a function symbol even if that function symbol is disabled. Formerly, if a function symbol was disabled then its induction scheme was only considered if an explicit induction hint was supplied, other than ~c[:induct t]. We eliminated the rule-class ~c[linear-alias]. This rule class was seldom used and complicated the linear arithmetic decision procedure in ways that made it difficult to extend to handle some non-linear special cases. The only use of the rule-class that we know of was in our own ~c[nqthm] books, which were an attempt to provide an embedding of the Nqthm logic and theorem prover into ACL2. But that facility was also practically never used, as far as we know. So both ~c[linear-alias] rules and the ~c[nqthm] books have been eliminated. In earlier versions of ACL2, when the ~c[IF]-form of ~c[(AND p q)] was assumed true -- as when rewriting the ~c[alpha] expression in ~c[(IF (AND p q) alpha beta)] -- the assumption mechanism did not deduce that ~c[p] and ~c[q] are true, only that their conjunction, in its ~c[IF]-form, is true. This has long been known as a deficiency in both ACL2 and the earlier Nqthm but it was tedious to do better when one considered the full range of ~c[IF]-forms one might encounter in the test of another ~c[IF]. Rather than code all the cases, we just waited until clausification got rid of them. Robert Krug developed a pretty nice treatment of the general case and we added it in this version. This also involved a surprising number of changes elsewhere in the system because the improved handling of assumptions caused the theorem prover often to ``erase'' hypotheses provided by ~c[:use] hints because it could simplify them to ~c[t]. Thank you Robert! In response to a suggestion from Robert Krug, we added ~c[mfc-ap] so that extended metafunctions can take advantage of linear arithmetic. ~l[extended-metafunctions]. There is less delay in printing goals. In previous versions, a goal was not printed until its subgoals were created (or the goal was proved). Now, the goal is printed essentially as soon as it is created. A small technical change has been made in the function ~ilc[term-order], to give priority on the function symbol count over the weighting of constants. So for example, while previously the term ~c[(f)] preceded the constant 2, that is no longer the case. If this change is noticed at all, it will probably be noticed in how so-called ~em[permutative] rewrite rules are applied; ~pl[loop-stopper]. Thanks to Robert Krug for suggesting this improvement and providing part of the implemtation. ~/~/") (deflabel note-2-6-rules :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on Changes in Rules and Constants~/ The following symbols have been added to the list constant ~c[*common-lisp-specials-and-constants*]: ~c[REPLACE], ~c[FILL], ~c[CHARACTER], ~c[=], ~c[BREAK], and ~c[PRIN1]. This was done in support of ports to Allegro 6.0 and Windows platforms (~pl[note-2-6-new-functionality]). The list of symbols in ~c[*acl2-exports*] has been modified, for example to include ~c[show-accumulated-persistence] and the legal arguments to ~ilc[set-inhibit-output-lst]. Functions ~ilc[zp] and ~ilc[zip] are now handled slightly differently. They are are now disabled, but each comes with a ~c[:]~ilc[rewrite] rule that allows their expansion on non-variable terms, and also with a ~c[:]~ilc[compound-recognizer] rule that avoids the need for opening up these functions when applied to variables. The resulting behavior should be very similar to the behavior of previous versions, except that case splits will be avoided when these functions are applied to variables. Function ~ilc[standard-string-alistp] replaces function ~c[string-alistp]. For further discussion, ~pl[note-2-6-guards]. Rules of class ~c[:]~ilc[rewrite] whose conclusion is a term of the form ~c[(equal lhs rhs)] have always been stored in the expected way: ~c[lhs] rewrites to ~c[rhs]. This way of storing ~c[:rewrite] rules has been extended to allow ~ilc[=], ~ilc[eq], or ~ilc[eql] in place of ~ilc[equal]. Rewrite rule ~c[nth-update-nth], in source file ~c[axioms.lisp], has been strengthened. A new rewrite rule ~c[equal-constant-+] has been added to the book ~c[arithmetic/equalities]. This should generally be a beneficial change, but existing proofs involving the arithmetic books could conceivably be affected. Function ~ilc[symbol-package-name] and constant ~c[*main-lisp-package-name*] have undergone small changes. This change should rarely be noticed by users and is discussed elsewhere; ~pl[note-2-6-system]. We mention here that proofs involving ~il[stobj]s may need to be modified because of changes in auxiliary functions generated by ~ilc[defstobj]. (These changes were made in support of a new resizing capability, mentioned elsewhere in these release notes; ~pl[note-2-6-new-functionality]. In the distributed book directory ~c[books/arithmetic/], the book ~c[rationals-with-axioms-proved.lisp] has been renamed ~c[rationals.lisp]. (ACL2(r) only) Rewrite rules ~c[realp-+], ~c[realp-*], ~c[realp-unary--], and ~c[realp-unary-/] have been added in analogy to existing rules ~c[rationalp-+], ~c[rationalp-*], ~c[rationalp-unary--], and ~c[rationalp-unary-/]. Thanks to Jun Sawada for suggesting this change. The definition of ~ilc[aref1] has been modified slightly. Previously, if ~c[*my-a*] were an array then ~c[(aref1 'some-name *my-a* :header)] would evaluate to the ~c[cdr] of the ~ilc[header] of ~c[*my-a*] rather than to its ~ilc[default]. ~l[arrays]. Changes have been made in the ~c[ihs] books, based on suggestions from Jun Sawada, that support its use with ACL2(r) (~pl[real]). The primary change is to replace calls of ~ilc[rationalp] with calls of ~ilc[real/rationalp], which should have no effect on users of standard ACL2. ~/~/") (deflabel note-2-6-guards :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on Guard-related Changes~/ When you ~ilc[declare] that a function treats certain formals as ~c[:]~ilc[stobj]~c[s], the ~il[guard] of the function is automatically extended to include the corresponding stobj-recognizer calls. For example, if a definition includes ~c[(declare (xargs :stobjs (ST)))] then the guard of the function is changed by the addition of the conjunct ~c[(ST-P ST)]. One impact of this is that if you use the built-in ACL2 ~ilc[state] as a formal parameter of a function, ~c[(STATE-P STATE)] is added to the guard. This may introduce a guard where there was none in previous versions of the system. In older versions, therefore, no attempt would be made to ~ilc[verify-guards], while in the new version, we would attempt guard verification. You may wish to add ~c[(declare (xargs :verify-guards nil))] to such definitions. A related change affects users who do not use stobjs or ~c[state]. In previous versions of the system ~-[] as now ~-[] a ~c[type] declaration extended the guard you provided explicitly. Thus, if you wrote ~c[(declare (type integer n))] then ~c[(INTEGERP n)] was added to your guard. This is still the case and ~c[:stobjs] recognizers are similarly added. But in older versions of the system we ``added'' the conjuncts without checking whether they were already present in the guard you provided. This sometimes produced such guards as ~c[(and (integerp n) (integerp n))] where the first was produced by your ~c[type] declaration and the second was your ~c[:guard]. We now eliminate redundant conjuncts; this may rearrange the order of the conjuncts. The guard conjectures for functions using ~c[stobj]s have been simplified somewhat by taking advantage of the syntactic restrictions checked for single-threaded objects. The following functions have been modified so that character and string arguments are restricted to standard characters. (~l[standard-char-p] and ~pl[standard-char-listp].)~bq[] ~ilc[upper-case-p] ~ilc[lower-case-p] ~ilc[char-upcase] ~ilc[char-downcase] ~c[string-downcase1] ~ilc[string-downcase] ~c[string-upcase1] ~ilc[string-upcase] ~ilc[char-equal] ~c[string-equal1] ~ilc[string-equal] ~eq[]Also, function ~ilc[standard-string-alistp] replaces function ~c[string-alistp], with concomitant changes in the guard to ~ilc[assoc-string-equal], and in variable ~c[*acl2-exports*]. Also, lemma ~c[standard-string-alistp-forward-to-alistp] replaces lemma ~c[string-alistp-forward-to-alistp]. There is a new lemma ~c[standard-char-p-nth], which has also been added to ~c[*acl2-exports*]. The guard had been inadvertently omitted from the definition of the function ~ilc[substitute] (and its subroutine ~c[substitute-ac]). This omission has been corrected; also, the guard is slightly stronger than the documentation had claimed (and that has been corrected). ~/~/") (deflabel note-2-6-proof-checker :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on Proof-checker Changes~/ The proof-checker command ~c[=], when used with no arguments, now reports which hypothesis is being used. The output from ~il[proof-checker] command ~c[type-alist] has been improved. A slight change has been made to the ~il[proof-checker] for commands ~c[promote], ~c[casesplit], ~c[equiv], and ~c[=], so that terms of the form ~c[(if x nil y)] are recognized as conjunctions, ~c[(and (not x) y)]. Thanks to Pete Manolios for suggesting that we consider such a change. There is a new ~il[proof-checker] command ~c[print-all-concs] that prints all the conclusions of the unproved goals. A new ~ilc[proof-checker] command, ~c[runes], has been added. It reports the ~il[rune]s that have participated in the interactive proof up to the current point. ~/~/") (deflabel note-2-6-system :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on System-level Changes~/ We modified the tracking of ~ilc[skip-proofs] events and the use of ~ilc[state] global ~c[ld-skip-proofsp] in order to avoid some soundness issues. For example, ~ilc[skip-proofs] events buried in locally-included books are now tracked. The ``Essay on Skip-proofs'' in source file ~c[axioms.lisp] gives several examples of dicey behavior that is no longer supported. We fixed a problem with some of the makefiles, so that recursive invocations of `make' now use the version of `make' specified on the command line. Files were fixed to help non-Unix/Linux users with book certification. Thanks to John Cowles for finding some problems and suggesting fixes to ~c[books/certify-numbers.lisp], ~c[books/arithmetic/certify.lsp], and ~c[books/cowles/certify.lsp]. We thank Scott Burson for noticing and fixing some other such problems. Moreover, a bdd test was being ignored entirely in Version 2.5; this problem has been fixed as well. A minor change in system function save-acl2-in-allegro will allow this function to continue to work in Allegro CL versions starting (someday) with 10.0. Thanks to Art Flatau for suggesting such a fix. The ~c[books/case-studies/] directory has been removed. These books are in support of the first (1998) ACL2 workshop, and are accessible via the ACL2 home page on the Web, ~url[http://www.cs.utexas.edu/users/moore/acl2/]. Also, the ~c[books/cli-misc] directory has been renamed ~c[books/misc], and the ~c[books/nqthm] directory has been removed. The notion of ACL2 version has been slightly modified to catch unsoundness due to implementation dependencies. ~l[version]. Another change to eliminate such unsoundness is that built-in symbols now have a ~ilc[symbol-package-name] of ~c[\"COMMON-LISP\"]; formerly, this string was ~c[\"LISP\"] for ACL2 images built on GCL. ~l[symbol-package-name]. At a low level, the (undocumented) constant ~c[*main-lisp-package-name*] is now ~c[\"COMMON-LISP\"]; before, it was ~c[\"LISP\"] for GCL. ~/~/") (deflabel note-2-6-other :doc ":Doc-Section note-2-6 ACL2 Version 2.6 Notes on Other (Minor) Changes~/ Warning strings are now case-insensitive. ~l[set-inhibit-warnings]. ACL2 causes a warning when an ~il[in-theory] hint or event causes a 0-ary function's definition to be disabled but its ~c[:]~ilc[executable-counterpart] to be enabled. A minor modification has been made to ~ilc[defstobj] that can have a positive impact on performance in Allegro Common Lisp. (For Lisp hackers: the stobj name was formerly declared special, and that was disabling Allegro's tail-merging routing for compilation of some recursive functions using stobjs.) The downside is that stobj names can no longer be evaluated in raw Lisp. However, raw Lisp is not the right place to be evaluating ACL2 forms anyhow; ~pl[set-raw-mode]. We thank Rob Sumners for bringing this issue to our attention. Before Version 2.6, there has been the following problem with ~ilc[defstub] and ~ilc[encapsulate] in the case that the current package is not the ACL2 package. If a ~il[signature] was specified using the symbol ~c[=>], then that symbol had have been imported into the current package from the ACL2 package when the current package was defined. There are no longer any package restrictions on the use of ~c[=>]. Thanks to John Cowles for bringing this problem to our attention. Bugs in ~ilc[defun-sk] have been fixed. ~c[Defun-sk] forms introducing functions of no arguments were failing to be admitted, for example: ~c[(defun-sk always-p1 () (forall (x) (p1 x)))]. Thanks to John Cowles for bringing this problem to our attention. Also, ~c[defun-sk] failed on an example in the documentation (~pl[tutorial4-defun-sk-example]), as pointed out by Matyas Sustik; this bug has been fixed as well. The trace mechanism has been fixed to handle ~il[stobj]s, and to avoid the printing of so-called ~em[enabled structures]. The ~ilc[brr] command ~c[:type-alist] now produces more readable output. An ~ilc[include-book] of an uncertified book no longer loads an associated compiled file. We added a few checks to make sure that the underlying lisp is suitable, for example checking that the reader is case-insensitive and reads in symbols with upper-case names where appropriate. We now warn when forcing (~pl[force]) or immediate force mode (~pl[immediate-force-modep]) change state between enabled and disabled. Also ~pl[enable-immediate-force-modep] and ~pl[disable-immediate-force-modep] for information about these new macros, which may be used to control immediate force mode. We have eliminated the use of a low-level raw Lisp constant, ~c[*most-recent-multiplicity*]. Our test suite saw a speed-up of approximately 2% as a result for an ACL2 image built on GCL (but no significant speed-up for an ACL2 image built on Allegro Common Lisp). We thank Rob Sumners for suggesting this improvement. Fixnum declarations are now realized as ~c[(signed-byte 29)] instead of ~c[(signed-byte 27)]. We check that the underlying Common Lisp recognizes objects of type ~c[(signed-byte 29)] as fixnums, with the exception of CLISP, which is said to have an efficient bignum implementation. A new documentation topic ~il[functional-instantiation-example] illustrates functional instantiation. A bug has been fixed in the monitoring of runes (~pl[monitor]). Thanks to Dave Greve for sending an example that clearly showed the problem. A warning is now issued when it is detected that a ~c[:]~ilc[type-prescription] rule may not be as strong as it appears because it is not sufficient to prove itself by type reasoning. An error is caused for rules of class ~c[:]~ilc[meta] when the function symbol ~c[IF] is among the ~c[:trigger-fns]. (~c[IF] was ignored anyhow; the point of this change is to avoid misleading the user.) A minor bug has been fixed in ~c[:]~ilc[pr], evident for example if this command was applied to ~c[IF]. A minor hole in ~c[:]~ilc[set-bogus-mutual-recursion-ok] did not permit the acceptance of ~ilc[mutual-recursion] forms that include constant function definitions. This has been fixed. Thanks to Eric Smith for coming up with a simple example illustrating the problem. The temporary files \"TMP.lisp\" and \"TMP1.lisp\" written out by ~c[:]~ilc[comp] are now written to the connected book directory (~pl[cbd]). Previously, the Allegro compiler was not eliminating tail recursion for executable counterparts of functions, because of the way one of its flags had been set. As a result, calls of functions whose guards had not been verified could run out of stack space when this was not necessary. This situation has been fixed. Executable counterparts could have slow array accesses. This has been fixed (specifically, constants are no longer replaced with their values in the definitions of executable counterparts). Various improvements have been made to the documentation. Thanks in particular to Eric Smith for pointing out a numbers of places where fixes were in order. File \"mcl-acl2-startup.lisp\" has been updated, thanks to feedback from Philippe Georgelin. Inefficiencies in GCL fixnum computations were remedied for macros ~c[+f] and ~c[*f]. Thanks to Rob Sumners for pointing out this issue. ~/~/") ; Do not make note-2-6(r) below conditional on #+:non-standard-analysis, ; because we want to make just one version of the documentation. (deflabel |NOTE-2-6(R)| :doc ":Doc-Section release-notes ACL2 Version 2.6(r) (November, 2001) Notes~/ Important changes to non-standard version: None since Version 2.5. ~/ Please ~pl[note-2-6] for changes to Version 2.6 of ACL2. We hope to write more documentation for ACL2(r) in the future. ~/ ") (deflabel note-2-7 :doc ":Doc-Section release-notes ACL2 Version 2.7 (November, 2002) Notes~/ The Version_2.7 notes are divided into the subtopics below. Here we give only a brief summary of a few of the changes that seem most likely to impact existing proofs. Not included in this brief summary, but included in the subtopics, are descriptions of improvements (including bug fixes and new functionality) that should not get in the way of existing proof efforts. In particular, please ~pl[note-2-7-new-functionality] for discussion of a number of new features that you may find useful. Acknowledgements and elaboration, as well as other changes, can be found in the subtopics listed below. o Bug fixes (~pl[note-2-7-bug-fixes]):~bq[] + Three soundness bugs were fixed. These bugs were probably rarely hit, so users may well not notice these changes. + ~ilc[Certify-book] now requires ~c[:skip-proofs-ok t] (respectively, ~c[:defaxioms-okp t]) if there are ~ilc[skip-proofs] (respectively, ~ilc[defaxiom]) events in the book or any included sub-books. + When ~c[:by] hints refer to a definition, they now use the original body of that definition rather than the simplfied (``normalized'') body. + When ~ilc[ld] is applied to a stringp file name, it now temporarily sets the connected book directory (~pl[cbd]) to the directory of that file while evaluating forms in that file.~eq[] o New functionality (~pl[note-2-7-new-functionality]):~bq[] + ACL2 now works harder to apply ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear] rules with free variables in the hypotheses. ~l[note-2-7-new-functionality], in particular its first two paragraphs, for details. ~il[Forward-chaining] also does more with free variables.~eq[] o Changes in proof engine (~pl[note-2-7-proofs]):~bq[] + Some prover heuristics have changed slightly. Among other consequences, this can cause subgoal ~il[hints] to change. For example, suppose that the Version_2.6 proof of a particular theorem generated \"Subgoal 2\" and \"Subgoal 1\" while Version_2.7 only generates the second of these. Then a subgoal hint attached to \"Subgoal 1\" in Version_2.6 would have to be attached to \"Goal'\" in Version_2.7. (~l[goal-spec].) The full topic has details (~pl[note-2-7-proofs]).~eq[] o Changes in rules and definitions (~pl[note-2-7-rules]):~bq[] + The package name of a generated variable has changed for ~ilc[defcong].~eq[] o Guard-related changes (~pl[note-2-7-guards]):~bq[] + ~ilc[Guard] verification formerly succeeded in a few cases where it should have failed. + Guards generated from type declarations now use functions ~c[signed-byte-p] and ~c[unsigned-byte-p], now defined in source file ~c[axioms.lisp] and formerly defined rather similarly under ~c[books/ihs/].~eq[] o Proof-checker changes (~pl[note-2-7-proof-checker]):~bq[] + See the above doc topic.~eq[] o System-level changes (~pl[note-2-7-system]):~bq[] + See the above doc topic.~eq[] o Other changes (~pl[note-2-7-other]):~bq[] + A new ~ilc[table], ~ilc[invisible-fns-table], takes the place of the handling of invisible functions in the ~ilc[acl2-defaults-table], + The ~ilc[theory-invariant] event has been modified so that the default action is an error rather than a warning. + Proof output that reports destructor elimination no longer uses the word ``generalizing''.~eq[] Again, please proceed to the subtopics for more thorough release notes. ~/~/") (deflabel note-2-7-bug-fixes :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on Bug Fixes~/ Francisco J. Martin-Mateos emailed us a soundness bug (!) in our handling of functional instantiation (for example ~pl[functional-instantiation-example]). We are grateful for that email, which clearly illustrated the problem. It is included just below the definition of ~c[push-clause] in ACL2 source file ~c[prove.lisp], where we have fixed the bug. This bug was fixed in a re-release of Version 2.6 in February, 2002. Rob Sumners emailed us a soundness bug (!) in function ~c[commutative-p1], which is used by the ACL2 ~il[bdd] package. We are grateful for his help; his email gave a proof of nil and also pointed to the problem function. This bug was fixed in a re-release of Version 2.6 in February, 2002. We discovered and fixed a soundness bug illustrated by the book below, which was certifiable in Version 2.6 and ends in a proof of ~c[nil]. The event ~c[(verify-guards foo)] should have been rejected, because ~c[foo] calls a function whose guards have not been verified, namely, ~c[bar]. However, ACL2 did not notice the call of function ~c[bar] in the body of ~c[foo] because it was looking in the simplified (normalized) body of ~c[foo] rather than in the original body of ~c[foo]. During processing of the book below, the logical definition of ~c[zp] is used before ~c[(verify-guards foo)], and ~c[(zp -3)] reduces to ~c[t] in the logic. After ~c[(verify-guards foo)], ACL2 simplifies ~c[(foo -3)] by going into raw Lisp, where ~c[(zp -3)] is evaluated and reduces to ~c[nil]. ~bv[] (in-package \"ACL2\") (defun bar (x) (zp x)) (defthm zp-false-on-negatives (implies (< x 0) (bar x)) :rule-classes :type-prescription) (defun foo (x) (declare (xargs :guard (rationalp x) :verify-guards nil)) (if (< x 0) (if (bar x) 0 1) ; simplified body reduces this line to 0 17)) (defthm foo-of-minus-3-is-0 (equal (foo -3) 0) :rule-classes nil) (verify-guards foo) (defthm foo-of-minus-3-is-1 (equal (foo -3) 1) :rule-classes nil) (defthm bug nil :rule-classes nil :hints ((\"Goal\" :use (foo-of-minus-3-is-0 foo-of-minus-3-is-1)))) ~ev[] The above bug exploited the fact that ~ilc[zp] has a different definition in raw Lisp than in the logic for arguments that violate its guard). The following example caused a hard error in raw Lisp, though not a soundness error. ~bv[] (in-package \"ACL2\") (defun bar (x) (cons (car x) (car x))) (defun foo (x) (declare (xargs :guard t :verify-guards nil)) (if (bar x) x nil)) (verify-guards foo) (defthm bug (equal (foo 3) t) :rule-classes nil) ~ev[] We have made a minor change to the notion of the ~em[formula] of a function symbol, related to the change above, which however is unlikely to be noticeable. In order to make it harder to hit problems like the guard problem above, we have slighly modified the raw Lisp definition of ~ilc[zp]. A ~ilc[break-rewrite] command, ~c[:ancestors], was broken, but has been fixed. Thanks to Eric Smith for bringing the problem to our attention, and to Robert Krug for supplying the final part of the fix. Some ~il[proof-checker] commands caused errors when all goals have already been proved. This has been fixed. Thanks to Matt Wilding for reporting this bug. Fixed a bug in ~c[:]~ilc[comp]. When compiling uncompiled functions with very large definitions, ACL2 was inserted a backslash (~c[\\]) character into generated files. Fixed the ~c[:type-alist] ~c[:]~ilc[brr] command (~pl[brr-commands]), whose output was difficult to read when typed after an ~c[:eval].. Fixed some clumsy handling of errors when including an uncertified book, for example, with the error message when including an uncertified book with a bad ~ilc[deftheory] event. Thanks to Eric Smith for pointing out this problem. Two modifications to ~ilc[certify-book] now cause it to reflect natural expectations with respect to soundness. First, it now has default values of ~c[nil] instead of ~c[t] for keyword arguments ~c[:skip-proofs-okp] and ~c[:defaxioms-okp]. Thanks to Robert Krug for suggesting this change and the ACL2 seminar at the University of Texas for discussing it. Second, when ~c[:skip-proofs-okp] (respectively, ~c[:defaxioms-okp]) is ~c[nil], either explicitly or by default, then ~ilc[skip-proofs] commands (respectively, ~ilc[defaxiom] events) are disallowed inside any included books, regardless of the keyword parameters passed to ~ilc[include-book]. This had not been the case for previous versions of ACL2, regardless of the values of ~c[:skip-proofs-okp] or ~c[:defaxioms-okp] passed to ~ilc[include-book]. Improved warnings and errors for ~ilc[certify-book] and ~ilc[include-book] to mention the ~il[portcullis] as a possible source of ~ilc[skip-proofs] and ~ilc[defaxiom]s. ACL2 formerly caused an error when ~il[hints] in a ~c[:]~ilc[corollary] were not well-formed. This situation could arise as follows when certifying a book. A lemma FOO is proved ~ilc[LOCAL]ly to the book (or, is present in a sub-book that is included locally). The ~c[:corollary] of a subsequent theorem, BAR, disables that rule in a hint. When BAR is proved, this is not a problem. But ~ilc[certify-book] makes a second pass after processing the events in a book: it essentially does an ~ilc[include-book]. During the ~c[include-book] pass, FOO is not known (because it was ~ilc[local]), and therefore ACL2 fails to process the ~ilc[disable] of FOO in an ~ilc[in-theory] hint. The fix is that during ~ilc[include-book], ~il[hints] are ignored in corollaries just as they have been for the main theorem (or definition). It was possible for guard verification to succeed where it should have failed. We have fixed the bug (which was in source function (ironically named!) ~c[fcons-term-smart]). Thanks to Robert Krug for sending us an example of bungled guard verification. It turns out that this bug was also present in Version_2.6. The ~il[proof-checker] command ~c[=] has been improved. Formerly, it could fail to apply when certain ~ilc[implies] terms were in the context. Thanks to Pete Manolios for bringing this problem to our attention. The command ~ilc[add-binop] failed to work. This has been fixed. Thanks to Rob Sumners for pointing out this problem. Also ~pl[note-2-7-other] for a discussion of how this and another ~il[table] are no longer part of the ~ilc[acl2-defaults-table]. Book certification could cause a segmentation fault in cases where the certification world (~pl[certify-book]) has a very large number of events. This has been fixed. We now allow empty ~c[:use] ~il[hints] and empty hints, as requested by Eric Smith. Examples: ~bv[] (\"Goal\" :use ()) (\"Goal\") ~ev[] A large ~ilc[mutual-recursion] nest could cause a stack overflow when executing either ~c[:pr FN], ~c[:pr! FN], or ~c[:monitor (:definition FN) t], where ~c[FN] is in that large mutual recursion nest. This has been fixed (implementation detail: function ~c[actual-props] has been made tail-recursive). NOTE: If you just want the definition of ~c[FN], ~c[:]~ilc[pf]~c[ FN] can be much faster than ~c[:]~ilc[pr]~c[ FN] if ~c[FN] is in a large ~ilc[mutual-recursion]. Hard Lisp errors could occur when including uncertified books. This has been fixed; ACL2 now does syntax-checking formerly omitted when including uncertified books. Previously, the evaluation of ~ilc[defstobj] and ~ilc[mutual-recursion] forms could cause ``undefined'' warnings when the form was compiled. This has been fixed. Thanks to Eric Smith for bring a ~c[mutual-recursion] example to our attention. A bug has been fixed in the syntactic check for valid ~c[:]~ilc[loop-stopper] values. Formerly, valid ~c[:loop-stopper] values were erroneously restricted to lists of length at most 2 (a minor problem, since these lists typically have length 1), and the function symbol(s) need not have been defined in the current ACL2 ~il[world]. Thanks to Eric Smith for sending an example to demonstrate the latter problem. Functions definitions that are ~c[:non-executable] (~pl[xargs]) had never been recognized as redundant, but this has been fixed. Thanks to Vernon Austel for pointing out this problem. Compilation using ~c[:]~ilc[comp] now compiles user-defined ~c[:]~ilc[program] mode functions. Formerly only ~c[:]~ilc[logic] mode functions could be compiled using ~c[:comp]. Handling of ~c[:by] hints has been improved in essentially three ways. The primary change is that now, when the current goal exactly matches the supplied lemma instance, the subsumption test will always succeeds (~pl[hints], in particular the discussion of ~c[:by]). Second, certain proof failures involving ~c[:by] ~il[hints] were failing silently, with duplicate messages ``As indicated by the hint, this goal is subsumed by....'' This could happen when the original goal was among the goals generated by applying the hint. This problem has been fixed by no longer considering this proof step to be specious (~pl[specious-simplification]). Third and finally, when the ~il[lemma-instance] refers to a definition, the original body of that definition is used rather than the simplfied (``normalized'') body. In addition to the obove, we now recognize more cases of specious simplification (~pl[specious-simplification]). Thanks to Eric Smith for bringing this issue to our attention. Fixed building of ACL2 under CLISP so that (1) the appropriate ACL2 startup message is printed out when ACL2 starts up, and (2) the lisp process supplied to make, e.g., LISP=/usr/bin/clisp, is the one written out to the saved ACL2 file. Thanks to Dave Greve and Noah Friedman for suggesting (2). Also, ACL2 now works with CLISP 2.30. We have accommodated a change in CLISP's handling of streams and its package-locking mechanism, as well as certain non-standard characters that formerly could cause CLISP 2.30 to break, even when those characters are in comments. Eliminated compiler warnings for CMU Lisp. Fixed an incorrect error supplied when book certification proceeded so quickly that the file write dates of the book (~c[.lisp] file) and the corresponding compiled file are equal. Now that error only occurs if the compiled file has a strictly earlier write date, which probably should never happen. Fixed an infinite loop when executing ~c[make clean-books] (and hence `make' with targets that call ~c[clean-books], namely, ~c[certify-books-fresh], ~c[regression-fresh], and ~c[regression-nonstd-fresh]), which could occur when any subdirectories of ~c[books/] are missing ~-[] even ~c[workshops/], which is intended to be optional. Thanks to Pete Manolios for pointing out this bug. The ~ilc[include-book] command now works properly even when filenames, or their directories or parent directories (etc.) are links. Thanks to Matt Wilding for pointing out this problem. The commands ~c[:]~ilc[puff] ~c[:]~ilc[puff*] have been fixed. Formerly, there was a bug when ~c[:puff] or ~c[:puff*] caused the execution of an ~ilc[include-book] for an absolute ~il[pathname], ~c[P], that was other than the current connected book directory (~pl[cbd]). When including ~c[P], any subsidiary ~ilc[include-book] with a relative pathname would be erroneously considered relative to the current ~ilc[cbd] rather than relative to the directory of ~c[P]. Thanks to Pete Manolios and Matt Wilding for pointing out this problem. It had been possible in a ``large'' ACL2 image to call ~ilc[verify-termination] successfully on built-in function ~ilc[sys-call], with undesirable results. This hole has been plugged. Thanks to Rob Sumners for pointing out this problem. The new function ~ilc[gc$] must also stay in ~c[:]~ilc[program] mode. ACL2 no longer warns when certifying a book based on ~ilc[local] functions whose ~il[guard]s have not yet been verified. Thanks to Pete Manolios for pointing out this issue. An occasional ``slow array warning'' had been possible during proofs. The following sequence shows how to evoke that warning in previous versions. ~bv[] (in-theory (disable binary-append)) (in-theory (enable binary-append)) (in-theory (disable binary-append)) (ubt 2) (thm (equal (car (cons x y)) x)) ~ev[] (~l[note-2-7-other] for a discussion of a change to ~ilc[compress1] in support of this fix; however, users should not need to read that discussion.) The raw Lisp code for ~ilc[defchoose] had a small bug, which was only evidenced in CLISP implementations as far as we know. It has been fixed. When ~ilc[ld] is applied to a stringp file name, it now temporarily sets the connected book directory (~pl[cbd]) to the directory of that file while evaluating forms in that file. To see the effect of this change, imagine a subdirectory ~c[\"sub\"] of the current directory, and imagine executing ~c[(ld \"sub/foo.lisp\")], where file ~c[foo.lisp] contains the form ~c[(include-book \"bar\")]. Presumably the intention was to consider the file ~c[bar.lisp] in the same directory, ~c[sub/], as ~c[foo.lisp]. ~c[Ld] now honors that intention, but in previous versions ~c[\"bar.lisp\"] would have been a reference to a file in the current directory, not in ~c[sub/]. For users of ~c[run-acl2] [perhaps there are none!]: A fix has been provided by a Debian user via Camm Maguire so that acl2-mode anyone using that?] will work in Xemacs, which apparently uses variable ~c[lisp-mode-shared-map] rather than ~c[shared-lisp-mode-map]. ACL2 has, for a long time (always?), had a mechanism for avoiding re-proving ~il[constraint]s generated by ~c[:functional-instance] ~il[lemma-instance]s in ~c[:use] and ~c[:by] hints. But this mechanism had not applied to defined (as opposed to constrained) functions. This has been fixed. Thanks to Francisco J. Martin-Mateos (ChesKo) for pointing out this problem by sending a clear example. ~/~/") (deflabel note-2-7-new-functionality :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on New Functionality~/ ACL2 now has a more powerful technique for relieving a ~c[:]~ilc[rewrite] or ~c[:]~ilc[linear] rule's hypothesis that contains free variables. A new ~il[documentation] section has been written describing the handling free variables in rules; ~pl[free-variables]. In brief, the primary change is that when a free-variable match for the current hypothesis fails to allow subsequent hypotheses to be relieved, then additional matches may be attempted until they have all been tried. Also ~pl[rule-classes] (discussion of ~c[:match-free]). Also ~pl[set-match-free-error], ~pl[set-match-free-default], and ~pl[add-match-free-override] for interfaces provided to the user for controlling the way ACL2 deals with free variables in hypotheses. We thank Rob Sumners for several helpful discussions about the designs of those interfaces, as well as Eric Smith and Robert Krug for helpful related discussions. Robert Krug also found a performance bug in a preliminary version, for which we are grateful. WARNING: Book certification attempts may take much longer now that, by default, ACL2 looks for more free variable matches (see paragraph just above). You can get the old behavior by inserting the form ~bv[] (set-match-free-default :once) ~ev[] just after the initial ~ilc[in-package] form. However, rules from included books that have free variables can still slow down certification. This can be fixed by inserting ~bv[] (add-match-free-override :once t) ~ev[] before the first event in the file that generates a proof. ~il[Forward-chaining] has been made more powerful in the presence of free variables (~pl[free-variables]), thanks to a contribution by Erik Reeber. Both before and now, when an attempt is made to relieve (prove) a hypothesis of a ~c[:forward-chaining] rule in the case that at least one variable in that hypothesis is not yet bound, ACL2 looks in the current context for an instance of that hypothesis. If it finds one, then it binds the unbound variables and continues to the next hyopothesis. What is new is that ACL2 can now looks for multiple instances of that hypothesis. Consider the following example; an explanation is below. ~bv[] (encapsulate (((op * *) => *)) (local (defun op (x y) (< x y))) (defthm transitivity-of-op (implies (and (op x y) (op y z)) (op x z)) :rule-classes :forward-chaining)) ; fails in Version_2.6; succeeds in in Version_2.7 (thm (implies (and (op a b) (op b c) (op b e)) (op a c))) ~ev[] Before Version_2.7, the proof of the ~c[thm] above fails. When the ~c[:forward-chaining] rule ~c[transitivity-of-op] binds ~c[x] to ~c[a] and ~c[y] to ~c[b], it then looks for an instance of ~c[(op y z)] in the current context, with ~c[y] bound to ~c[b] but ~c[z] unbound. It happens to find ~c[(op b e)] before ~c[(op b c)], and it then adds ~c[(op a e)] to the context. But starting with Version_2.7, it continues to look for additional instances and finds ~c[(op b c)] in the context as well, chaining forward to ~c[(op a c)] and thus proving the theorem. A new macro, ~ilc[bind-free], provides a simple way to get much or most of the power of ~il[meta]functions. Thanks to Eric Smith for coming up with the idea and to Robert Krug for providing an implementation (which we modified only very slightly) and documentation. ~l[bind-free] and ~pl[bind-free-examples]. With the addition of ~ilc[bind-free] (mentioned above), ~ilc[syntaxp] has become a macro, although that change should be transparent to the user. More importantly, the argument of ~c[syntaxp] may now refer to variables ~c[mfc] and ~c[state], giving ~c[syntaxp] some of the power of extended metafunctions; ~pl[syntaxp] and ~pl[extended-metafunctions]. Thanks to Robert Krug for implementing that extension. Also, the argument of ~ilc[syntaxp] may now include calls of ~c[:]~ilc[program] mode functions. ~l[syntaxp] and ~pl[syntaxp-examples] (thanks to Robert Krug for updating the former and creating the latter documentation). The linear-arithmetic decision procedure (~pl[linear-arithmetic]) has now been extended so that ACL2 can reason about non-linear arithmetic as well (~pl[non-linear-arithmetic] for how to turn on this feature). We thank Robert Krug for the initial implementation of this, and Eric Smith for finding a couple of bugs in it. Some ~ilc[trace] utilities have been made available in the ACL2 loop.~bq[] o Function ~ilc[trace$] (and also ~ilc[untrace$]) calls the corresponding underlying Lisp routine ~c[trace] (and ~c[untrace]), which however continues (as it has for some time) to be enhanced for GCL and Allegro CL. o Macro ~ilc[open-trace-file] causes trace output to go to a specified file. Macro ~ilc[close-trace-file] causes trace output to go to the screen (which is the default). o Macro ~c[with-error-trace] (or, ~c[wet] for short) causes a backtrace to be written out for many failures, including guard violations. ~l[trace], ~pl[trace$], and see :DOC ~c[wet] [** NOTE: eliminated after Version 3.3]. ~eq[] A new ~ilc[theory], ~ilc[minimal-theory] has been provided (~pl[theories]). It can be particularly useful for speeding up proofs involving ~c[:use] ~il[hints]. New ~ilc[events] ~ilc[defund] and ~ilc[defthmd] behave exactly like ~ilc[defun] and ~ilc[defthm], respectively, except that these new events disable the new name. The new macro ~ilc[with-output] can be used to suppress output that would normally result from evaluation of a form. The form ~c[(]~ilc[pstack]~c[)] can give the user an idea of what the prover has been up to during a proof, or after a user-aborted proof. Moreover, by evaluating ~c[(verbose-pstack t)] (~pl[verbose-pstack]) one can get ~il[trace]-like information about prover functions, including time summaries, printed to the screen during a proof. Thanks to Bill Legato and Robert Krug for initiating this work and to Robert for providing some initial implementation. The new command ~c[:]~ilc[comp-gcl] is identical in functionality, except that it always leaves ~c[.c] and ~c[.h] files when compiling in GCL. Thanks to Rob Sumners and Vernon Austel for suggesting such a capability. The macro ~ilc[e/d] provides a convenient way to ~ilc[enable] some rules and ~ilc[disable] others. It was formerly in a book supplied with the distribution, ~c[books/ihs/ihs-init.lisp], written by Bishop Brock (who we thank for providing this useful macro). New distributed books include those in ~c[books/ordinals/], ~c[books/rtl/rel3/], and ~c[books/misc/simplify-defuns.lisp] (which is documented in ~c[books/misc/simplify-defuns.txt]). The ~c[:expand] hint now accepts a special value, ~c[:LAMBDAS], that tells the ACL2 rewriter to expand all lambda applications (~ilc[let] expressions). ~l[hints]. A new function ~ilc[zpf] has been added as fast test against 0 for nonnegative fixnums. A new macro ~ilc[gc$] allows the user to call the garbage collector of the underlying Common Lisp. Thanks to Rob Sumners for suggesting this feature. It is now possible to ~ilc[monitor] ~il[simple] (abbreviation) rules. However, as a warning explains, they are still not considered monitored during preprocessing; ~pl[monitor]. Thanks to Robert Krug for providing this improvement. The second argument of ~ilc[certify-book], if supplied, formerly had to be either ~c[t] or a non-negative integer. Now it can be the symbol ~c[?], in the ~c[ACL2] package, indicating that the usual check should be suppressed on the number of commands that have been executed to create the world in which ~ilc[certify-book] was called. ~/~/") (deflabel note-2-7-proofs :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on Changes in Proof Engine~/ An improvement in the linear arithmetic heuristics has been provided by Robert Krug. For information about this change, search for the comment in ~c[add-linear-lemma] (file ~c[rewrite.lisp]) that begins as follows. ~bv[] ; Previous to Version_2.7, we just went ahead and used the result of ~ev[] Thanks, Robert! Also thanks to Eric Smith for providing a motivating example. The non-linear-arithmetic addition (~pl[non-linear-arithmetic]) led to several small changes in the linear-arithmetic decision procedure (~pl[linear-arithmetic]). Two of these changes could affect existing proofs.~bq[] First, when we are setting up the initial arithmetic database (which we call the ``pot-lst''), we have always scanned it to see if there were any pairs of inequalities from which we could derive a previously unknown equality. In some cases we added this equality to the clause and in others we used it to rewrite the clause, substituting one side of the equality for the other throughout the clause. Previously, the heuristics that we used to determine whether we performed the substitution differed from those used in several other places in the code. This has now been regularized, and similar heuristics are now used throughout the code. The second change to the linear-arithmetic decision procedure is that we now explicitly add inequalities derived from type reasoning to the pot-lst. Previously, we performed cancellations against these inequalities without adding them to the pot-lst. This change results in there being more inequalities in the pot-lst than before, and so more chances for there to be a pair of inequalities from which an equality can be derived. In effect, certain simple consequences of the current goal (~pl[type-set]) may now be added as hypotheses of the goal or used to peform equality substitutions. ~eq[] A slight improvement has been made to the way certain rewrite rules are stored. It was already the case that a rewrite rule rule whose conclusion ~c[C] is not a call of a known equivalence relation (or ~ilc[eq], ~ilc[eql], or ~ilc[=]) is stored as ~c[(iff C t)], except that if ACL2 can determine (using its ~ilc[type-set] mechanism) that ~c[C] is Boolean, then the rule is stored as ~c[(equal C t)]. The iprovement is that if ~c[C] and ~c[C'] are Boolean, then a rule stated as ~c[(iff C C')] is stored as ~c[(equal C C')]. Thanks to Pete Manolios for providing an example that led us to consider this improvement. The heuristic use of equalities (fertilization) has been modified. Previously, ACL2 would sometimes substitute using an equality but keep the equality, and then undo the substitution by using the equality again. Now, when ACL2 keeps an equality after using it, it puts the equality inside a call of ~ilc[hide]. Descendents of that goal that are unchanged by simplification will have this call of ~ilc[hide] removed so that the equality can once again contribute to the proof. This change can cause some proofs to succeed that otherwise would fail. In the unlikely event that a proof fails that formerly succeeded, the following hint on \"Goal\" may fix the problem (~pl[hints]): ~bv[] :expand ((:free (x) (hide x))) ~ev[] We have refined the heuristics employed when an ~ilc[IF] form is assumed true or false. Our previous attempt (see ~il[note-2-6-proofs] for the original announcement) was not as general as we had believed. We have also improved some low-level code responsible for rewriting ~c[IF] expressions. In earlier versions of ACL2, it was possible to have the truth or falsity of an ~c[IF] expression explicitly recorded in the type-alist, and yet not use this information during rewriting. This problem has been corrected. Thanks to Robert Krug for noticing this problem and implementing the fix. We have sped up the rewriter in some cases where there are large collections of mutually-recursive functions (~pl[mutual-recursion]). (Implementation notes: technically, we have modified the way function ~c[being-openedp] operates on the ~c[fnstack], and we have modified ~c[*current-acl2-world-key-ordering*] as described in the essay above its definition.) ~il[Forward-chaining] is now done in the preprocessing phase of proof attempts (see the discussion of ~c[:DO-NOT] ~-[] ~pl[hints]). This is part of a technical change, made in support of translation of type declarations to ~il[guard]s (~pl[note-2-7-guards]). Previously, whenever ACL2 checked for ~il[built-in-clause]s, it then looked for a contradiction using ~ilc[type-set] reasoning if it did not find a suitable built-in clause. The change is to perform forward-chaining in such cases (i.e., when a built-in clause is not found). A couple of changes have been made in the generation of goals for ~il[forcing-round]s. Thanks to Eric Smith for bringing issues to our attention that led to these changes. For one, ~il[guard]s are no longer relevant in such goal generation. Formerly, the addition of a guard could make a proof fail that otherwise succeeded. Secondly, contextual information is now always kept when it involves a constrained constant, i.e., a zero-ary function introduced in the signature of an ~ilc[encapsulate]. ~/~/") (deflabel note-2-7-rules :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on Changes in Rules and Constants~/ The ~ilc[defcong] macro has been slightly changed. The difference is that the variable generated with suffix ~c[-EQUIV] will now be in the same package as the name of the variable from which it is generated, rather than always belonging to the ACL2 package. Thanks to Hanbing Liu for suggesting this change. (Note that a couple of books have been modified to accommodate this change, e.g., ~c[books/finite-set-theory/set-theory].) In Version_2.6, a change was made for rules of class ~c[:]~ilc[rewrite] whose conclusion is a term of the form ~c[(EQV lhs rhs)], where ~c[EQV] is ~ilc[=], ~ilc[eq], or ~ilc[eql]: the rule was stored as though ~c[EQV] were ~ilc[equal]. (~l[note-2-6-rules].) This change has been extended to rules of class ~c[:]~ilc[definition]. ~/~/") (deflabel note-2-7-guards :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on Guard-related Changes~/ It was possible for guard verification to succeed where it should have failed. See the discussion under ~il[note-2-7-bug-fixes]. There have been changes in the guards generated from type declarations for the following cases. Thanks to Dave Greve and Matt Wilding for suggesting such changes. ~bv[] (type (signed-byte n) val) (type (unsigned-byte n) val) (type (integer m n) val) ~ev[] The following examples illustrate the changes. ~bv[] (type (signed-byte 4) x) ==> [old] (AND (INTEGERP X) (<= -8 X) (<= X 7)) ==> [new] (SIGNED-BYTE-P 4 X) (type (unsigned-byte 4) x) ==> [old] (AND (INTEGERP X) (<= 0 X) (<= X 15)) ==> [new] (UNSIGNED-BYTE-P 4 X) ~ev[] ~/~/") (deflabel note-2-7-proof-checker :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on Proof-checker Changes~/ Output from the ~il[proof-checker] can now be inhibited by supplying the symbol ~c[proof-checker] in the list given to ~il[set-inhibit-output-lst]. ~/~/") (deflabel note-2-7-system :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on System-level Changes~/ ACL2 now runs (once again) under LispWorks, specifically, LispWorks 4.2.0. However, we needed a patch, which presumably will be unnecessary after 4.2.7. From LispWorks support: ~bq[] Users with LispWorks4.2.7 should ask us at lisp-support@xanalys.com for the transform-if-node patch. It will be helpful if they quote (Lisp Support Call #11372) when doing so. Also, they must send a bug form generated from their LispWorks image: instructions at http://www.lispworks.com/support/bug-report.html. ~eq[] File ~c[books/Makefile-generic] has been improved so that failed attempts to certify a book will cause the `make' to fail. Previously, an existing ~c[.cert] file was left in place, and that sufficed for the `make' to be considered a success. Now, the old ~c[.cert] file is first removed when recertification is found to be necessary. A change has been made to source file ~c[acl2.lisp] to accommodate GCL 2.4.3. (ACL2 Version 2.6 does not work with some versions of GCL 2.4.3.) The error message has been improved when certain forms are typed to raw Lisp and the ACL2 loop has never been entered (with ~c[(]~ilc[LP]~c[)]). The following symbols in the ACL2 package have been made untouchable, meaning that they are not available to the user: ~c[ev-fncall], ~c[ev], ~c[ev-lst], ~c[ev-acl2-unwind-protect], ~c[ev-fncall!], and ~c[user-stobj-alist-safe]. The reason is that these functions can not be called safely except under certain restrictions. If you want to call the ACL2 evaluator, consider using the built-in system functions ~c[trans-eval] or simple-translate-and-eval. CLISP Version_2.30 implements a notion of ``locking'' the \"LISP\" package that is incompatible with building ACL2. (CLISP Version_2.27 does not appear to have had this feature.) We have gotten around this problem by unlocking the \"LISP\" package in ACL2 images built on such CLISPs. Automatic proclaiming for GCL, which has (for a long time) been done for functions in compiled books, has been improved. Formerly, the only time a non-trivial output type (i.e., other than ~c[t]) was inferred was when macroexpansion produced an explicit call of ~ilc[the]. Now, ~ilc[if] expressions can also generate non-~c[t] output types. Consider the following example. ~bv[] (defmacro the-fixnum (n) (list 'the '(signed-byte 29) n)) (defmacro 1+f (x) (list 'the-fixnum (list '1+ (list 'the-fixnum x)))) (defun foo (x) (declare (type (unsigned-byte 27) x)) (if (zp x) 0 (1+f (foo (1-f x))))) ~ev[] Formerly, the ~c[proclaim] forms for ~c[foo], before and after this improvement, are as shown below. ~bv[] (PROCLAIM '(FTYPE (FUNCTION ((UNSIGNED-BYTE 27)) T) FOO)) ;old (PROCLAIM '(FTYPE (FUNCTION ((UNSIGNED-BYTE 27)) (SIGNED-BYTE 29)) FOO)) ;new ~ev[] Compiler info messages sent to error stream were eliminated for CMUCL. ~/~/") (deflabel note-2-7-other :doc ":Doc-Section note-2-7 ACL2 Version 2.7 Notes on Miscellaneous Changes~/ Made several minor ~il[documentation] improvements. We are grateful to Eric Smith for suggesting (most of) these. Improved ~c[(show-bdd)] (~pl[bdd]) to give more useful feedback when there are ``leaf'' terms not known to be Boolean. Sped up processing of large mutual-recursion nests. In one large example the speedup was roughly two orders of magnitude. Modified event printing so that if both ~c['prove] and ~c['event] are inhibited, then events are no longer printed on behalf of ~ilc[certify-book], ~ilc[encapsulate], or ~ilc[defstobj]. Thanks to Eric Smith for prompting consideration of such a change. The following technical change was made to support ~c[with-error-trace] and ~c[wet] (~pl[note-2-7-new-functionality]), but may be of interest to those who do low-level programming using the ACL2 logical ~ilc[world]. The ~c['unnormalized-body] property is now stored not only for functions defined in ~c[:]~ilc[logic] mode, but also for functions defined by the user in ~c[:]~ilc[program] mode. (~c[:Program] mode Functions built into ACL2 still have their ~c['unnormalized-body] property omitted, in order to save space.) The handling of ``invisible'' functions for purposes of controlling rewriting (~pl[loop-stopper]) has been moved to a new table; ~pl[invisible-fns-table]. Macros that access and modify this table are called ``~c[...-invisible-fns-table]'' in place of their former names, ``~c[...-invisible-fns-alist].'' This feature was formerly implemented in the ~ilc[acl2-defaults-table], which prevented a book from exporting lists of invisible functions intended to work with the ~il[rewrite] rules developed in the book. Thanks to Eric Smith and Rob Sumners for suggesting this change. ~l[set-invisible-fns-table] (formerly ~c[set-invisible-fns-alist]), and also ~pl[add-invisible-fns] and ~pl[remove-invisible-fns], which provides ways to incrementally add to and remove from this table, respectively. The handling of printing binary function call nests using macros (~l[add-binop]) has also been moved out of the ~ilc[acl2-defaults-table] as suggested by Eric and Rob, but this feature didn't work anyhow (~pl[note-2-7-bug-fixes]). Incidentally, the symbols ~c[binop-table], ~ilc[add-binop], and ~ilc[remove-binop] have all been added to the list ~c[*acl2-exports*] (~pl[acl2-user]), ~ilc[add-invisible-fns] and ~ilc[remove-invisible-fns] have been added to that list, and ~c[set-invisible-fns-alist] has been replaced in that list by ~ilc[set-invisible-fns-table]. Function ~c[invisible-fns-alistp] is no longer defined and has been removed from ~c[*acl2-exports*]. We now enforce the stated restriction on the pairings in ~c[macro-aliases-table] (~pl[macro-aliases-table]), namely, that it associates names of macros with names of funcions (with respect to the current ACL2 logical ~il[world]). We make a similar requirement on ~ilc[invisible-fns-table]. The ~ilc[theory-invariant] event has been modified so that the default action is an error rather than a warning. Thanks to Eric Smith for suggesting this change. Also, the value returned upon successful execution of a ~ilc[theory-invariant] event is now the key. Proof output that reports destructor elimination no longer uses the word ``generalizing''. This small change may help in browsing proof output, since now ``generaliz'' takes you to true uses of generalization. Thanks to Matyas Sustik for suggesting such a change. The command ~c[:]~ilc[pl] now prints an abbreviated controller-alist for ~c[;]~ilc[definition] rules. Formerly the output from ~c[:pl] could be overwhelming when the supplied function was part of a large ~ilc[mutual-recursion] nest. The defaults for keyword parameters of ~ilc[certify-book] have changed. ~l[note-2-7-bug-fixes], in particular, the discussion there of two modifications to ~c[certify-book]. Technical changes have been made to ~ilc[compress1] and ~ilc[compress2] that should usually be invisible to users. The next paragraph describes them in detail, only for competeness (i.e., that description can be ignored by most users). But first, here is an example showing an effect on users. The slow array warning was not there previously. Notice that the warning only arises if the event form is changed. The solution is to be sure that redundant ~ilc[defconst] forms are syntactically identical. ~bv[] ACL2 !>(defconst *a* (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (1 . one) (0 . zero)))) Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) *A* ACL2 !>(aref1 'demo *a* 0) ZERO ACL2 !>(defconst *a* (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (1 . one) (0 . zero)))) This event is redundant. See :DOC redundant-events. Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :REDUNDANT ACL2 !>(aref1 'demo *a* 0) ZERO ACL2 !>(defconst *a* (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero) (1 . one)))) This event is redundant. See :DOC redundant-events. Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :REDUNDANT ACL2 !>(aref1 'demo *a* 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ACL2 !> ~ev[] As before, the von Neumann structure stored in the ~c['acl2-array] property of the array name contains the array list object in its ~ilc[car]. However, previously it was the case that ~c[compress1] and ~c[compress2] did not update that ~c[car] when its new value would be equal to its old value. This was done largely in support of some type-set tables defined using ~ilc[defconst] in ~c[type-set-b.lisp]. The new versions of ~ilc[compress1] and ~ilc[compress2] are simpler in that no such exception is made in the case of equal lists, although instead the entire compression process is short-circuited when the input array list object is ~ilc[eq] to the ~c[car] of the ~c['acl2-array] property. This change was made because the equality test was causing a ``slow array access'' warning to be printed in rare cases during proofs, as described elswhere (~pl[note-2-7-bug-fixes]). We no longer distribute documentation specific to Lucid Emacs. The Info documentation in directory ~c[doc/EMACS/] works well both for Gnu Emacs and XEmacs. A little-advertised macro, ~c[value], has long been allowed for top-level forms in ~il[books]; ~pl[embedded-event-form]. This has been replaced by a new macro, ~c[value-triple]. The two have the same semantics at the top-level of books, where ~ilc[state] is ``live''. However, ~c[value-triple] should be used at the top-level of a book, while ~c[value] should be used in function definitions (as before). This change eliminates a warning put out by the Allegro Common Lisp compiler for top-level ~c[value] forms in ~il[books]. ~/~/") (deflabel |NOTE-2-7(R)| :doc ":Doc-Section release-notes ACL2 Version 2.7(r) (November, 2002) Notes~/ In source file ~c[axioms.lisp], in order for proofs to succeed, (~c[make proofs]), the definitions of ~ilc[acl2-count] and ~c[explode-atom] have been modified slightly, and lemma ~c[standard-numberp-one] [modified after Version_3.4 to become ~c[standardp-one]] has been given ~c[:rule-classes nil]. All ~ilc[skip-proofs] forms have been eliminated from the nonstd books, thanks to Ruben Gamboa. The directory ~c[books/sqrt/], which was intended for ACL2(r), has been moved to ~c[books/nonstd/sqrt/] and added as appropriate to ~c[books/nonstd/Makefile]. ~/ Please ~pl[note-2-7] for changes to Version_2.7 of ACL2. ~/ ") (deflabel note-2-8 :doc ":Doc-Section release-notes ACL2 Version 2.8 (March, 2004) Notes~/ BRIEF SUMMARY. The Version_2.8 notes are divided into the indicated subtopics. Here we give only a brief summary of just a few of the major new features and changes that seem most likely to impact existing proofs. Not included in this brief summary, but included in the subtopics, are descriptions of many improvements (including bug fixes and new functionality) that should not get in the way of existing proof efforts. In the description below we also omit discussion of changes that will become clear by way of error messages if they affect you. In particular, please ~pl[note-2-8-new-functionality] for discussion of a number of new features that you may find useful. Acknowledgements and elaboration, as well as other changes, can be found in the subtopics listed below. o Some of the bug fixes (~pl[note-2-8-bug-fixes]):~bq[] + Some soundness bugs were fixed. + The handling of free variables in hypotheses (~pl[free-variables]) of rewrite and linear rules had a bug that prevented some proofs from going through. Now that this bug has been fixed, you may find some proofs running much more slowly than before. You can use ~ilc[accumulated-persistence] and ~ilc[add-match-free-override] to remedy this situation; ~pl[note-2-8-bug-fixes] for details. + The ~il[default-hints] in the current logical ~il[world] are no longer ignored by ~ilc[verify-guards]. + Forms violating guard-checking such as ~c[(defconst *silly* (car 3))] are now allowed in ~il[books].~eq[] o Some of the new functionality (~pl[note-2-8-new-functionality]):~bq[] + WARNING: You may find that ~c[control-d] (in emacs, ~c[control-c control-d]) can throw you completely out of Lisp where it had not formerly done so. + ACL2 now starts up inside the ACL2 loop ~-[] that is, ~c[(]~ilc[LP]~c[)] is executed automatically ~-[] when built on CLISP or Allegro CL. This was already the case for GCL and CMUCL, and it still is not true for LispWorks. + ~l[note-2-8-ordinals] for a discussion of a significant change in ordinal represtation, and in particular, for how to preserve existing proofs that depend on the previous ordinal representation. + Macros ~ilc[mbe] (``must be equal''), ~ilc[mbt] (``must be true''), and ~ilc[defexec] have been introduced, which allow the user to attach alternate executable definitions to functions. + The user can now control multiple matching for free variables in hypotheses for ~c[:]~ilc[forward-chaining] rules, as has already been supported for ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear] rules. + It is no longer necessary to specify ~c[(set-match-free-error nil)] in order to avoid errors when a rule with free variables in its hypotheses is missing the ~c[:match-free] field. + The form ~c[(break-on-error)] causes, at least for most Lisps, entry into the Lisp debugger whenever ACL2 causes an error. + A new ~ilc[table] has been provided so that advanced users can override the built-in ~c[untranslate] functionality. ~l[user-defined-functions-table]. + The ~ilc[pstack] (`process [prover] stack'') mechanism, formerly denoted ~c[checkpoints], has been improved. One of these improvements is to show actual parameters with ~c[(pstack t)] rather than formals. + The ~ilc[defstobj] event is now allowed to take an ~c[:inline] argument, which can speed up execution. + Macro ~ilc[cw-gstack] no longer takes arguments for the ~c[gstack] or ~ilc[state]. To print terms in full rather than abbreviated: ~c[(cw-gstack :evisc-tuple nil)]. + The ~ilc[include-book] event now has an additional (optional) keyword, ~c[:dir]. In particular, ~c[(include-book \"foo/bar\" :dir :system)] will include the indicated book after prepending the path of the built-in ~c[books/] directory. You will probably not find ~c[:dir :system] to be useful if you move the executable image or distributed books; ~pl[include-book], in particular its ``soundness warning''. + The printing of results in raw mode (~pl[set-raw-mode]) may now be partially controlled by the user: ~pl[add-raw-arity]. + For those using Unix/Linux `make': A ~c[cert.acl2] file can contain forms to be evaluated before an appropriate ~ilc[certify-book] command is invoked automatically (not included in ~c[cert.acl2]).~eq[] o Some of the changes in the proof engine (~pl[note-2-8-proofs]):~bq[] + ACL2 now prevents certain rewriting loops; ~pl[rewrite-stack-limit]. + Small changes have been made to heuristics for controlling rewriting during proofs by induction and in handling certain ``weak'' ~il[compound-recognizer] rules. + The handling of free variables in a hypothesis of a ~il[rewrite] rule (~pl[free-variables]) has been improved in the case that the hypothesis is of the form ~c[(equiv x y)], where ~c[equiv] is a known equivalence relation (~pl[equivalence]). + We have modified how the ACL2 simplifier handles the application of a defined function symbol to constant arguments, by avoiding the introduction of ~il[hide] when evaluation fails if the term can be rewritten. + The generation of \"Goal\" for recursive (and mutually-recursive) definitions now uses the subsumption/replacement limitation (default 500). ~l[case-split-limitations]. + Default hints now apply to hints given in definitions, not just theorems. ~l[default-hints]. + Linear arithmetic now uses the conclusions of ~ilc[forward-chaining] rules, and ~ilc[type-set] now uses a small amount of linear reasoning when deciding inequalities.~eq[] o Some of the changes in rules, definitions, and constants (~pl[note-2-8-rules]):~bq[] + See the above doc topic.~eq[] o Guard-related changes are described in ~pl[note-2-8-bug-fixes]. o Some of the proof-checker changes (~pl[note-2-8-proof-checker]):~bq[] + Added new ~il[proof-checker] commands ~c[wrap1], ~c[wrap], and ~c[wrap-induct], to combine multiple conjuncts or goals. + The ~c[type-alist] command now takes optional arguments that control whether or not the governors and/or conclusion are used in computing the context.~eq[] o Some of the system-level changes (~pl[note-2-8-system]):~bq[] + ACL2 now runs on OpenMCL and on MCL 5.0.~eq[] o Some of the other changes (~pl[note-2-8-other]):~bq[] + Emacs file ~c[emacs/emacs-acl2.el] has been updated (~pl[note-2-8-other] for details). + When ~c[:pl] is given a term other than a symbol, it will print all rewrite rules that match that term. + A new function, ~ilc[pkg-witness], returns a symbol in the given package. + The list constant ~c[*acl2-exports*] has been extended. + A new release of the rtl library has been included: ~c[books/rtl/rel4/]. See the ~c[README] file in that directory.~eq[] Again, please proceed to the subtopics for more thorough release notes. ~/~/") (deflabel note-2-8-bug-fixes :doc ; Fixes not included below, and other notes: ; The tautology checker bug mentioned in the :doc below was in call-stack. ; A bug was fixed in assign-wormhole-output [renamed assign-wormhole-status ; after v3-6-1]: er-progn replaces pprogn. ; It is no longer legal for user code to call include-book (this is disallowed ; in translate11). See the comment in *inside-include-book-fn*. ; The missing argument in the first (er hard ...) in rewrite-fncall has been ; supplied. ; Subterm-one-way-unify has been modified in order to avoid any possibility of ; calling fargs on a quotep. ; Here is a way to exhibit the proof-checker expand bug described in the first ; paragraph of the documentation below: ; (in-package "ACL2") ; ; (encapsulate ; (((foo *) => *) ; ((bar *) => *)) ; ; (local (defun foo (x) x)) ; (local (defun bar (x) (not x))) ; ; (defthm foo-open ; (equal (foo x) x) ; :rule-classes :definition) ; ; (defthm bar-not-foo ; (equal (bar x) (not (foo x))) ; :rule-classes :definition)) ; ; (defthm bad (equal (foo x) (bar x)) ; :rule-classes nil ; :instructions ; ((:dv 1) :expand :nx :expand :top :s)) ; ; (defthm contradiction ; nil ; :rule-classes nil ; :hints (("Goal" :use bad))) ; The second proof-checker bug mentioned below can be exhibited as follows: ; (encapsulate ; () ; (local ; (defthm bug-lemma (if x (if x t nil) nil) ; :rule-classes nil ; :instructions ((dive 2 3) :s))) ; (defthm bug nil ; :rule-classes nil ; :hints (("Goal" :use ((:instance bug-lemma (x nil))))))) ; The function ev-acl2-unwind-protect was fixed to incorporate a change made ; long ago, by J, to acl2-unwind-protect. This function was subsequently ; replaced by ev-w-acl2-unwind-protect. ; Without the new fix based on Matyas's suggestion (see paragraph on tautology ; checker below and Qiang's example), we had to change subgoal numbers in hints ; in the following books: ; direct-incorporation-sound-iff in ; workshops/2003/matlin-mccune/support/simp.lisp ; graph-equivp1-load-graph1 in ; workshops/2003/greve-wilding_mbe/support/run-fpst.lisp ; Regarding the soundness bug about type-prescription rules, whose description ; below mentions local-incompatibility and refers to an example in that :doc ; topic: The full example is below. It actually proved in every GCL image of ; ACL2 from v2-7 back to at least v2-4. The problem goes back at least through ; v2-3 as well; evaluate :PROPS FOO to see a type-prescription record with a ; :basic-ts of nil. Our fix is to modify add-type-prescription-rule to cause a ; hard error when destructure-type-prescription fails; previously we had ; ignored the erp return value from destructure-type-prescription. ; (in-package "ACL2") ; ; (defun my-natp (x) ; (declare (xargs :guard t)) ; (and (integerp x) ; (<= 0 x))) ; ; (defun foo (x) ; (nfix x)) ; ; (in-theory (disable foo (:type-prescription foo))) ; ; (encapsulate ; () ; (local (defthm my-natp-cr ; (equal (my-natp x) ; (and (integerp x) ; (<= 0 x))) ; :rule-classes :compound-recognizer)) ; (defthm foo-type-prescription ; (my-natp (foo x)) ; :hints (("Goal" :in-theory (enable foo))) ; :rule-classes ((:type-prescription :typed-term (foo x))))) ; ; (defthm rationalp-foo ; (rationalp (foo x)) ; :hints (("Goal" :in-theory (enable foo))) ; :rule-classes :type-prescription) ; ; (defthm bad-lemma ; (equal (foo x) 1) ; :rule-classes nil) ; ; (defthm bad ; nil ; :rule-classes nil ; :hints (("Goal" :use ((:instance bad-lemma (x 1)))))) ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Bug Fixes~/ We have fixed a soundness bug in the tautology checker's handling of expressions of the form ~c[(not (not x))]. This bug has gone back at least as far as Version_2.4. All of the regression tests passed after the fix, without modification. So we hope that this bug has rarely bitten anyone. Thanks to Qiang Zhang for sending us a proof of ~c[nil] that led us to this fix: ~c[(thm (equal (and p q) (not (or (not p) (not q)))))]. And thanks to Matyas Sustik for an observation that led to an improvement of our initial fix. The preceding version (2.7) introduced a soundness bug in handling of ACL2 ~il[arrays], in which functions ~ilc[compress1] and ~ilc[compress2] were returning the input alist rather than compressing it appropriately. Here is a proof of ~c[nil] that no longer succeeds, based on a bug report from Warren Hunt, who we thank for bringing this problem to our atttention. ~bv[] (defthm bad (not (let* ((ar2 (aset1 'my-array ar1 3 10)) (ar3 (compress1 'my-array ar2)) (ar4 (reverse (reverse ar2))) (ar5 (compress1 'my-array ar4))) (and (equal ar2 ar4) (not (equal ar3 ar5))))) :rule-classes nil) (defthm contradiction nil :rule-classes nil :hints ((\"Goal\" :use ((:instance bad (ar1 (compress1 'my-array '((3 . 5) (:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 0 :NAME MY-ARRAY))))))))) ~ev[] On a related note, a new function ~ilc[flush-compress] can be used for subtle control of under-the-hood raw Lisp support for fast array access, although we expect it to be very rare that users need this extra support. Previous versions have had two soundness bugs that can occur when using the ~il[proof-checker]: ~bq[] o The first bug pertains to the ~c[expand] command, and hence ~c[x] and ~c[x-dumb] commands (which call ~c[expand]); ~pl[proof-checker-commands]. The bug can occur when applying the above commands when the current term is a call of a constrained function symbol for which there is a ~c[:]~ilc[definition] rule. Now, the ~c[expand] command will succeed only when the function symbol of the current term is a defined function symbol, in which case the original definition is always used, in analogy to how the ~c[:expand] hint works in the prover; ~pl[hints]. Thanks to John Erickson for sending an example that led us to wonder if there might be a soundness problem. o The second bug pertains to the ~c[s] command (and commands that call it, e.g., ~c[s-prop]). The proof-checker forms a context out of the top-level hypotheses and the ~c[if]-terms governing the current term. If there is a contradiction in the top-level hypotheses, the proof-checker can appropriately consider the goal to be proved, and it does so. But formerly, the criterion was weaker: the contradiction could involve the combination of the top-level hypotheses and ~c[if]-term governors. Thanks to Rob Sumners for noticing this bug.~eq[] A soundness bug could be provoked in some Lisps by applying ~ilc[defpkg] to the empty string. This has been disallowed. We fixed a soundness bug related to packages caused by a failure to track axioms introduced ~ilc[local]ly on behalf of ~ilc[defpkg] events. ~l[hidden-death-package]. We fixed a soundness bug caused by a failure to check that a ~c[:]~ilc[type-prescription] rule can be processed when proofs are skipped or under a ~ilc[defequiv] event. The former case can occur when processing an ~ilc[encapsulate] or ~ilc[include-book] event, where the rule could depend on a ~ilc[local] ~c[:]~ilc[compound-recognizer] rule preceding the proposed ~c[:]~ilc[type-prescription] rule under the same ~ilc[encapsulate] or ~ilc[include-book] event. ~l[local-incompatibility] for such an example. We fixed a potential soundness bug relating to reclassifying a ~c[:program] mode function to ~c[:logic] mode (as done by ~ilc[verify-termination] or the submission of an appropriate ``redundant'' definition) without adequate checking that ~ilc[stobj] usage was identical. Allegedly redundant definitions must now preserve the ~c[stobjs] declaration as well as the formals, body, guard and type declarations. We thank Vernon Austel for pointing out this problem. It was possible to get a raw Lisp error by introducing a ~ilc[local]ly defined function with ~il[guard] verification inhibited and then subsequently introducing the same definition non-locally without that inhibition. The following example will clarify. ~bv[] (encapsulate nil (local (defun foo (x) (declare (xargs :guard t :verify-guards nil)) (car x))) (defun foo (x) (declare (xargs :guard t)) (car x))) ; The following causes a raw lisp error because ACL2 runs the Common Lisp ; definition of foo, because it thinks that foo's guard of t was verified. (thm (equal (foo 3) xxx)) ~ev[] Thanks to Jared Davis for bringing this problem to our attention. We are particularly grateful to Jared because his example exploited this bug by applying it to a function defined using ~ilc[mbe] (introduced in this same version, 2.8), in order to prove ~c[nil]! The sort of error message shown below can legitimately occur when certifying a book in a certification world where there was an ~ilc[include-book] command with a relative pathname (~pl[pathname]). However, it was occurring more often than necessary. This has been fixed. ~bq[] ACL2 Error in (CERTIFY-BOOK \"foo\" ...): The certification world has include-book commands for book \"bar\" that correspond to different full pathnames, namely \"/u/dir1/bar\" and \"/u/dir2/bar\". ACL2 cannot currently certify a book in such a world. To work around this problem, use an absolute pathname for at least one of these books (see :DOC pathname).~eq[] Bugs were fixed in ~ilc[with-output], in particular related to the use of values ~c[:all]. Also, documentation for ~c[with-output] has been improved. Thanks to Vernon Austel for pointing out the bugs. Fixed a lisp error occurring when ~c[bash] proof-checker command was given illegal syntax, e.g., ~c[(bash ((\"Goal\" :in-theory (enable binary-append))))] instead of ~c[(bash (\"Goal\" :in-theory (enable binary-append)))]. We added an appropriate guard to ~ilc[find-rules-of-rune], which will avoid hard lisp errors when this function is called on non-~il[rune] arguments. Thanks to Eric Smith for pointing out this issue. It was possible for a redundant ~ilc[include-book] form (~pl[redundant-events]) to leave a ~il[command] in the ACL2 logical ~il[world] and to cause (re-)loading of a compiled file. These behaviors have been fixed. In particular, if ~c[book1] has already been included in the current ACL2 ~il[world] and ~c[(include-book \"book1\")] occurs in ~c[book2], then the compiled file for ~c[book1] will not be loaded again when ~c[book2] is included. Thanks to Dave Greve for bringing our attention to these problems, and to Eric Smith for bringing up a special case earlier (where \"//\" occurred in the book name). The summary printed at the end of a proof had not listed ~c[:]~ilc[induction] rules used in a proof. This has been corrected. The use of proof trees in emacs redefined `~c[control-c control-c]' in such a way that in telnet mode, the telnet session was interrupted and perhaps could not be continued. This has been fixed. Source function ~c[load-theory-into-enabled-structure] contained a guard-violating call of ~ilc[compress1]. Thanks to Vernon Austel for bringing this problem to our attention; even though this bug was benign (as he pointed out), we like keeping the source code free of guard violations. A number of proof-checker atomic macros caused a hard error when all goals have already been proved. This has been fixed. Thanks to John Erickson for sending an example of the issue. A bug has been fixed in ~ilc[add-match-free-override]. Formerly, a ~ilc[table] ~il[guard] violation occurred when calling ~ilc[add-match-free-override] more than once with first argument other than ~c[:clear]. Defininitions of functions involving large constants could cause stack overflows. This has been fixed, at least in some of the most egregious cases (by making a source function ~c[fn-count-evg] tail-recursive). Thanks to Jared Davis for bringing this problem to our attention. Evaluation of computed hints could cause stack overflows. This has been fixed. Thanks to Eric Smith for bringing this problem to our attention. Evaluation of ~c[:]~ilc[monitor] on ~c[:]~ilc[definition] ~il[rune]s is now fast even if the specified function is part of a very large ~ilc[mutual-recursion] nest. Thanks to Eric Smith for sending an example showing that this wasn't always the case. Fixed a bug in ~c[books/bdd/cbf.lisp] that was causing certification of distributed bdd books to fail when the connected book directory (~pl[cbd]) differs from the current working directory. Thanks to Scott Guthery for bringing this bug to our attention and supplying a helpful log. Duplicate rule names have been eliminated from warnings generated upon the use of enabled ~c[:]~ilc[rewrite] or ~c[:]~ilc[definition] rules. Thanks to Eric Smith for pointing out this problem. The trace utilities (~pl[trace]), as modified for GCL and Allegro Common Lisp, had failed to show more than the first return value for so-called ``~c[*1*]'' functions (essentially, ~il[executable-counterpart] functions) when they were returning multiple values (via ~il[mv]). This has been fixed. Thanks to Erik Reeber for pointing out this problem. Also, it is now possible to refer to ~c[arglist] in ~il[trace$] forms when ACL2 is built on GCL, not just when ACL2 is built on Allegro Common Lisp. Uses of ~ilc[hide] introduced during proofs by failed attempts to evaluate constrained functions (~pl[hide]) are now tracked, so that the ~il[rune] ~c[(:DEFINITION HIDE)] will show up in the summary. The following bug, introduced back in Version 2.7, has been fixed. The bug applied only to GCL and may well not have affected anyone. But the function proclamation computed by ACL2 for compilation usually had an output type of ~c[nil] where it should have been ~c[t]. The macro ~ilc[gc$] had a bug exhibited when it was supplied one or more arguments. This has been fixed. The macro ~ilc[defabbrev] broke when supplied a string and no documentation, e.g., ~c[(defabbrev foo () \"\")]. Thanks to Rob Sumners for noticing this problem and providing a fix, which we have incorporated. For ACL2 executables built on Allegro Common Lisp, a Lisp error occurred when ~ilc[trace$] was called on other than a defined function symbol. Now ACL2 prints a more useful error message. The proof-checker no longer accepts a ~c[(]~ilc[verify]~c[)] command when some function symbol in the original goal no longer exists in the current ACL2 logical ~il[world]. Thanks to John Erickson for bringing this issue to our attention. The function ~c[ld-redefinition-action] may now be called by the user. Thanks to Vernon Austel for suggesting that we remove this symbol from the list of so-called untouchables. The handling of free variables in hypotheses (~pl[free-variables]) of rewrite and linear rules had a bug that prevented some proofs from going through. Here is a simple example, essentially provided by Diana Moisuc, who we thank for bringing this issue to our attention. The proof of the ~ilc[thm] below had failed, but now will succeed. This particular bug prevented, for example, the ~c[:all] behavior from occurring when the first hypothesis of the rule does not have free variables. NOTE: Now that this bug has been fixed, you may find some proofs running much more slowly than before. You can use ~ilc[accumulated-persistence] to locate rules that are slowing down your proofs because of excessive attention to free variables, and then execute ~ilc[add-match-free-override] for those rules (or, just change the rules themselves to specify ~c[:once] in the ~c[:]~ilc[rule-classes]). ~bv[] (defstub foo1 (* ) => *) (skip-proofs (defthm aux-foo1 (implies (and (integerp a) (integerp i) (equal (foo1 0) (list 0 i))) (equal (foo1 a) (list 0 (+ a i)))) :rule-classes ((:rewrite :match-free :all)))) (thm (implies (and (integerp i) (integerp a) (equal (foo1 0) (list 0 i))) (equal (foo1 a) (list 0 (+ a i))))) ~ev[] Formerly, creation of large arrays could cause an error in the underlying Common Lisp implementation without helpful messages for the user. Now, we check Common Lisp restrictions on arrays and print a helpful error message if they are violated, namely: each dimension must be less than the value of Common Lisp constant ~c[array-dimension-limit], and the product of the dimensions must be less than the value of Common Lisp constant ~c[array-total-size-limit]. Thanks to Warren Hunt for bringing this issue to our attention. Note: this change also removes a former restriction of ~ilc[stobj] array fields to size smaller than 2^28-1, provided the underlying Lisp can support larger arrays. The ~il[default-hints] in the current logical ~il[world] were ignored by ~ilc[verify-guards]. This has been fixed. Thanks to Jared Davis for pointing out this bug and sending a helpful example. The ~ilc[brr] mechanism has been cleaned up in order to avoid hard errors and infinite loops that can arrive when typing interrupts (~c[control-c]) or end-of-files (~c[control-d]) inside the ~ilc[brr] loop. Thanks to Dave Greve, Olga Matlin, Eric Smith, and Serita Van Groningen for bringing this issue to our attention. As a byproduct, if you type ~c[control-d] (or if inside emacs, ~c[control-c control-d]), you may now quit entirely out of ACL2 and lisp (~pl[good-bye]) in some cases where you formerly would not have, for example when sitting at the ACL2 prompt (which formerly, in Allegro Common Lisp for example, would merely take you into raw Lisp rather than quitting everything). We have eliminated structural flaws in the HTML documentation pages that could make them unreadable in some browsers. Thanks to Bill Young for bringing this issue to our attention and to Joe Hendrix for diagnosing the problem. The ~il[proof-checker] could run very slowly after many instructions in a given session. This has been fixed; thanks to Art Flatau for bringing this problem to our attention. (Implementation detail: We now keep tag-trees duplicate-free when we accumulate them into state. This change could have minor speed advantages for some top-level proofs too, not just in the proof-checker.) The printing of accesses to stobjs using nth or update-nth has been done using symbolic constants since ACL2 Version_2.6. However, there was a bug that prevented this feature from working for ~ilc[update-nth] except at a top-level call. This has been fixed. Thanks to Julien Schmaltz for bringing this problem to our attention. For example, consider these events: ~bv[] (defstobj st field0 field1) (thm (equal (nth 1 (update-nth 0 17 st)) (car (cons xxx yyy))) :hints ((\"Goal\" :in-theory (disable nth update-nth)))) ~ev[] Before the fix, the proof attempt of the above silly thm printed the following. ~bv[] (NTH 1 (UPDATE-NTH *FIELD0* 17 ST)) ~ev[] After the fix, we instead see the following. ~bv[] (NTH *FIELD1* (UPDATE-NTH *FIELD0* 17 ST)) ~ev[] It is now possible to certify and subsequently include ~il[books] that require guard-checking to be off. For example, the book can contain the form ~c[(defconst *silly* (car 3))] even though ~c[3] fails to satisfy the guard of ~ilc[car]. Formerly, it was necessary to execute ~c[:]~ilc[set-guard-checking]~c[ nil] before a ~ilc[certify-book] or ~ilc[include-book] in order for such a form to be handled without error. Thanks to Hanbing Liu for bringing this problem to our attention. Fixed a ~il[proof-checker] bug that could cause probably cause strange error, ``Attempt to access the plist field''. Thanks to Bill Young for bringing this problem to our attention. Fixed a ~il[proof-checker] bug that was failing to record applications of rewrite rules using the proof-checker's ~c[:rewrite] command, causing the proof summary to omit mention of that rule (for example, when using the proof-checker's ~c[:exit] command to generate an ~c[:instructions] hint). Thanks to Bill Young for pointing out this bug. Modernized some of the proof-tree emacs and infix printing stuff, thanks to suggestions made by Camm Maguire. ~/~/") (deflabel note-2-8-new-functionality ; Not mentioned in detail below: ; Rob's stobj :inline mod moves the live stobj tests from the raw Lisp ; definitions to the *1* code. The idea is not even to call the raw Lisp code ; with non-live stobjs. :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on New Functionality~/ WARNING: You may find that ~c[control-d] (in emacs, ~c[control-c control-d]) can throw you completely out of Lisp where it had not formerly done so. (CLISP and Allegro CL only) ACL2 now starts up inside the ACL2 loop ~-[] that is, ~c[(]~ilc[LP]~c[)] is executed automatically ~-[] when built on CLISP or Allegro CL. This was already the case for GCL and CMUCL, and it still is not true for LispWorks. Thanks to Joe Corneli for bringing the CLISP command-line option ~c[\"-i\"] to our attention, which led to this CLISP change and inspired reconsideration of how to do this for Allegro CL. Pete Manolios and Daron Vroon have changed the representation of ordinals in ACL2, defined algorithms for ordinal arithmetic, and created a library of theorems to reason about ordinal arithmetic. We thank them for these nice contributions. ~l[note-2-8-ordinals] for details, in particular, for how to preserve existing proofs that depend on the previous ordinal representation. Sometimes users create rules of class ~c[:]~ilc[rewrite] that cause an infinite loop in the ACL2 rewriter. This has lead to Lisp stack overflows and even segmentation faults. Now, the depth of calls of functions in the ACL2 rewriter is limited, and under user control. ~l[rewrite-stack-limit]. Macros ~ilc[mbe] (``must be equal'') and ~ilc[mbt] (``must be true'') have been introduced, which allow the user to attach fast executable definitions to (presumably slower) ~c[:]~ilc[logic] mode functions. Thanks to Vernon Austel for a key idea. Also provided is a macro ~ilc[defexec], which employs ~ilc[mbe] but enforces the requirement that the executable definition also terminates. Thanks to Jose Luis Ruiz Reina for collaborating in the design and development of ~ilc[defexec], and for useful comments from a number of others as well in the development of ~c[mbe] including Joe Hendrix and Rob Sumners. Definitions have been added for functions ~ilc[rassoc-eq] and ~ilc[rassoc-equal], which are like ~ilc[rassoc] but use different tests and have different guards. (Compare ~ilc[assoc-eq] and ~ilc[assoc-equal], which are in similar relation to ~ilc[assoc].) The user can now control multiple matching for free variables in hypotheses for ~c[:]~ilc[forward-chaining] rules, as has already been supported for ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear] rules. For ~c[:forward-chaining] rules, ``free variables'' are those in the hypotheses not bound by a given trigger term. As for ~c[:rewrite] and ~c[:linear] rules, free-variable matching may be limited to the first successful attempt by specifying ~c[:match-free :once] with ~c[:forward-chaining] in the ~c[:]~ilc[rule-classes], and ~ilc[add-match-free-override] may be used to modify the behavior of an existing rule. Thanks to Erik Reeber for most of the implementation of these new capabilities, as well as significant assistance with a corresponding new documentation topic (~pl[free-variables-examples-forward-chaining]). It is no longer necessary to specify ~c[(set-match-free-error nil)] in order to avoid errors when a rule with free variables in its hypotheses is missing the ~c[:match-free] field. (This was already true during book certification, but now it is the case in interactive sessions as well.) The form ~c[(break-on-error)] causes, at least for most Lisps, entry into the Lisp debugger whenever ACL2 causes an error. ~l[break-on-error]. Thanks to John Erickson for providing encouragement to provide this feature. A new ~ilc[table] has been provided so that advanced users can override the built-in ~c[untranslate] functionality. ~l[user-defined-functions-table]. The ~ilc[pstack] mechanism (formerly denoted ~c[checkpoints]) has been improved. The ``process [prover] stack,'' or pstack, is automatically printed when proofs abort. Evaluation of function calls on explicit arguments during proofs is now tracked. Actual parameters are shown with ~c[(pstack t)] rather than formals. Thanks to Bill Legato for suggesting the first two of these improvements and, in general, encouraging changes that make ACL2 easier to use. The ~ilc[defstobj] event is now allowed to take an ~c[:inline] argument, which can speed up execution. Thanks to Rob Sumners for suggesting and implementing this new feature. Macro ~ilc[assert$] has been added in order to make it easy to write assertions in one's code. Semantically, ~c[(assert$ test form)] is the same as ~c[form], but it causes a hard error (using ~ilc[illegal]) if ~c[test] evaluates to ~c[nil]. Macro ~ilc[cw-gstack] no longer takes arguments for the gstack or ~ilc[state]. However, it now takes a keyword argument (which is optional), ~c[:evisc-tuple], that can be used to control how it prints terms. In particular, ~c[cw-gstack] abbreviates large terms by default, but ~c[(cw-gstack :evisc-tuple nil)] causes terms to be printed in full. Thanks to Robert Krug and Eric Smith for requesting this improvement. The advanced user now has more control over the evisceration of terms. ~l[ld-evisc-tuple], in particular the new paragraph on ``The printing of error messages and warnings.'' The ~ilc[include-book] event now has an additional (optional) keyword, ~c[:dir]. The value of ~c[:dir] should be a keyword that is associated with an absolute directory pathname to be used in place of the current book directory (~pl[cbd]) for resolving the first argument of ~c[include-book] to an absolute pathname. At start-up, the only such keyword is ~c[:system], so that for example ~c[(include-book \"arithmetic/top\" :dir :system)] will include the book ~c[\"arithmetic/top\"] under the ~c[\"books/\"] directory of your ACL2 installation. But you can associate ``projects'' with keywords using ~ilc[add-include-book-dir], e.g., ~c[(add-include-book-dir :my-project \"/u/smith/project0/\")]. ~l[add-include-book-dir] and also ~pl[delete-include-book-dir] and ~pl[include-book]. Note: You will probably not find ~c[:dir :system] to be useful if the distributed books are not placed in the path of their original location, pointed to by ~c[:dir :system], which will often happen if the executable image is obtained from another site. Also ~pl[include-book], in particular its ``soundness warning''. The printing of results in raw mode (~pl[set-raw-mode]) may now be partially controlled by the user: ~pl[add-raw-arity]. Also, newlines are printed when necessary before the value is printed. For those using Unix/Linux `make': A ~c[cert.acl2] file can contain forms to be evaluated before an appropriate ~ilc[certify-book] command is invoked automatically (not included in ~c[cert.acl2]). Jared Davis has contributed a new set of books for ordered finite set theory to the standard distribution, ~c[books/finite-set-theory/osets-0.81/]. See the ~c[README] file in that directory. Thanks, Jared. Robert Krug has contributed two related changes (thanks, Robert!) in support of stronger arithmetic reasoning. First, one can now enable and disable nonlinear arithmetic with a ~c[:nonlinearp] hint, which will override the default provided by ~ilc[set-non-linearp] (initially, ~c[nil]). ~l[hints]. Second, ~il[computed-hints] can now have access to the ~c[HISTORY], ~c[PSPV], and ~c[CTX] variables of the waterfall, which (for example) allows the writing of a hint which will enable nonlinear arithmetic on precisely those goals that are ~c[stable-under-simplificationp]. ~l[computed-hints]. Robert Krug has contributed a new set of arithmetic books to the standard distribution, ~c[books/arithmetic-3/]. See the ~c[README] file in that directory. Thanks, Robert. ~/~/") (deflabel note-2-8-proofs :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Changes in Proof Engine~/ ACL2 now prevents certain rewriting loops; ~pl[rewrite-stack-limit]. During the computation of ~ilc[constraint]s for functional instantiation, ~c[(prog2$ term1 term2)] and ~c[(the type term2)] are now treated as ~c[term2]. A change has been made in heuristics for controlling rewriting during proofs by induction. Formerly, during induction proofs, ACL2 suppressed rewriting of certain ``induction hypothesis'' terms, and forced expansion of certain ``induction conclusion'' terms, until rewriting had stabilized. This meddling with the rewriter is still turned off when rewriting has stabilized, but it is now turned off earlier once an ancestor has been through the rewriter and the current goal is free of ``induction conclusion'' terms. Thanks to Dave Greve and Matt Wilding for providing an example and associated analysis that led us to look for a heuristic modification. A change has been made in the heuristics for handling certain ``weak'' ~il[compound-recognizer] rules when building contexts. Those who want to dig deeply into this change are welcome to look at the code following the call of ~c[most-recent-enabled-recog-tuple] in the code for function ~c[assume-true-false] in the ACL2 sources. The handling of free variables in a hypothesis of a ~il[rewrite] rule (~pl[free-variables]) has been improved in the case that the hypothesis is of the form ~c[(equiv x y)], where ~c[equiv] is a known equivalence relation (~pl[equivalence]). Previously, if the rewriter was attempting to rewrite the hypothesis ~c[(equiv x y)] of a rewrite rule, in a context where ~c[x'] is an instance of ~c[x], then the rewriter could fail to notice a term ~c[(equiv x' y')] true in the current context where ~c[y'] is an instance of ~c[y], in the case that ~c[x'] precedes ~c[y'] in the ~ilc[term-order]. This has been remedied. This improvement applies regardless of whether ~c[x], ~c[y], or (we believe) both are already fully instantiated in the present context. Thanks to Joe Hendrix for bringing up an example and to Vernon Austel for providing another, simple example. A very minor change has been made to the rewriter in the case that an equality appears on the left-hand side of a ~c[:]~ilc[rewrite] rule. Formerly, when such an equality ~c[(equal x y)] was commuted to ~c[(equal y x)] in order for the rule to match the current term, then all equalities on the instantiated right-hand side of the rule were commuted, except for those occurring inside another equality. The instantiated right-hand side is no longer modified. It seems very unlikely that this change will cause proofs to fail, though we cannot completely rule out that possibility. We have modified how the ACL2 simplifier handles the application of a defined function symbol to constant arguments in certain cases, which we now describe. As before, ACL2 attempts to simplify such a function application by evaluation, provided the ~c[:]~ilc[executable-counterpart] of the function is enabled. And as before, if that evaluation fails due to a subroutine call of a constrained function (introduced by ~ilc[encapsulate]), ACL2 may wrap a call of ~c[hide] around this function application. (~l[hide].) But now, ACL2 attempts to apply definitions and rewrite rules in the case that this evaluation fails, and only if the resulting term is unchanged does ACL2 wrap ~ilc[hide] around this function application. Thanks to Matt Wilding for bringing up the idea of this modification. The generation of \"Goal\" for recursive (and mutually-recursive) definitions now uses the subsumption/replacement limitation (default 500). ~l[case-split-limitations]. Default hints now apply to hints given in definitions, not just theorems. ~l[default-hints]. Thanks to Robert Krug for implementing the following two improvements involving linear arithmetic reasoning: linear arithmetic now uses the conclusions of ~ilc[forward-chaining] rules, and ~ilc[type-set] now uses a small amount of linear reasoning when deciding inequalities. ~/~/") (deflabel note-2-8-rules :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Changes in Rules, Definitions, and Constants~/ The ~il[theory] ~ilc[minimal-theory] has been changed by adding the ~il[definition] ~il[rune] for ~ilc[mv-nth] to the theory. A corresponding change has been made to the theory warning mechanism, which was failing to warn if the definition of ~c[mv-nth] is disabled, even though calls of ~c[mv-nth] can be expanded by special-purpose code in the rewriter. Thanks to Serita Van Groningen for pointing out this problem with the theory warning mechanism. The ~ilc[defevaluator] event has been modified so that in the body of the evaluator function, to add a new case ~c[(ATOM X)] (returning ~c[nil]) has been inserted immediately after the case ~c[(EQ (CAR X) 'QUOTE)]. This is a no-op semantically but may speed up proofs. Thanks to Warren Hunt for suggesting this change. A new form of ~c[:]~ilc[compound-recognizer] rule is now allowed: ~bv[] (if (fn x) concl1 concl2) ~ev[] This is equivalent to an existing form: ~bv[] (and (implies (fn x) concl1) (implies (not (fn x)) concl2)) ~ev[] Thanks to Josh Purinton for bringing this to our attention. Rewrite rules ~c[realpart-+] and ~c[imagpart-+] have been added in order to simplify the ~ilc[realpart] and ~ilc[imagpart] (respectively) of a sum. They follow from a theorem ~c[add-def-complex] that equates a sum with the complex number formed by adding real and imaginary parts. All three of these theorems may be found in source file ~c[axioms.lisp]. Thanks to Eric Smith for raising a question leading to these additions, as well as to Joe Hendrix and Vernon Austel for helpful suggestions. ~/~/") (deflabel note-2-8-guards :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Guard-related Changes~/ All the guard-related changes may be found elsewhere; in particular, ~pl[note-2-8-bug-fixes]. ~/~/") (deflabel note-2-8-proof-checker :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Proof-checker Changes~/ Added new ~il[proof-checker] commands ~c[wrap1], ~c[wrap], and ~c[wrap-induct]. ~c[Wrap] replaces multiple goals by their conjunction: ~c[(wrap instr1 instr2 ...)] employs ~c[wrap1] so that the indicated instructions create only at most one new goal. ~c[Wrap-induct] is a simple example of the use of ~c[wrap], so that induction creates only one goal (the conjunction of the base and induction steps). ~c[Wrap1] can be used immediately after a prover call (~c[bash], ~c[prove], ~c[reduce], ~c[bdd], or ~c[induct]) to collapse the new goals into one. ~l[proof-checker-commands]. The ~il[proof-checker] command ~c[=] failed to work as expected when a governing ~c[IF]-test of the current term is T. This has been fixed (by fixing source function ~c[conjuncts-of]). Thanks to Yoann Padioleau for bringing this problem to our attention. The ~c[type-alist] command now takes optional arguments that control whether or not the governors and/or conclusion are used in computing the context that is printed (~pl[proof-checker-commands], specifically subtopic ~c[type-alist]). Thanks to Rob Sumners for suggesting this improvement. The macro ~ilc[toggle-pc-macro] has always taken an optional second argument of ~c[atomic-macro] or ~c[macro]. However, this was not clearly documented, and those two symbols had to be in the ~c[ACL2] package. Both of these problems have been remedied. Thanks to John Erickson for bringing the lack of documentation of the second argument to our attention. ~/~/") (deflabel note-2-8-system :doc ; Modified compile-uncompiled-defuns/compile-uncompiled-*1*-defuns to write ; comment to the top of TMP.lisp/TMP1.lisp saying that file can be deleted. ; (Matyas Sustik had asked about these files.) ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on System-level Changes~/ ACL2 now runs on OpenMCL, ``an opensourced Common Lisp implementation, derived from Digitool's Macintosh Common Lisp product.'' Thanks to Greg Wright and Robert Krug for doing most of the work for this port. When ~c[(]~ilc[LP]~c[)] is first executed, the underlying raw Lisp package will change to ~c[\"ACL2\"] (if that is not already the current package in raw Lisp). This is a minor change that will probably not be noticed, since up to now it has probably been the case that the ACL2 executable starts up with ~c[\"ACL2\"] as the underlying raw Lisp package. But this change was made because we have been informed that ACL2 executables based on OpenMCL need not start up with ~c[\"ACL2\"] as the underlying raw Lisp package. ACL2 now runs on MCL 5.0. Thanks to Pascal Costanza for updates to the instructions in file ~c[mcl-acl2-startup.lisp] and for an update to the ACL2 sources (parameter ~c[*compiled-file-extension*]). ~/~/") (deflabel note-2-8-ordinals :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Changes to the Ordinals~/ Please ~pl[ordinals].~/~/") (deflabel note-2-8-other ; Changed ev-fncall, ev, etc. so that we could have versions that take the ; world (see note below about user-defined-functions-table). ; Used memoization to speed up global-symbol and *1*-symbol by avoiding string ; concatentation. ; Eliminated all-new-flg/all-new-flag from the following, as it always had ; value nil (Robert Krug agrees with this change). ; new-vars-in-pot-lst (formerly arg 3) ; expanded-new-vars-in-pot-lst (formerly arg 3) ; add-polys-and-lemmas1-nl (formerly arg 2) ; add-polys-and-lemmas (formerly arg 2) ; add-disjunct-polys-and-lemmas (formerly arg 3) ; add-disjuncts-polys-and-lemmas (formerly arg 2) ; add-terms-and-lemmas (formerly arg 4) :doc ":Doc-Section note-2-8 ACL2 Version 2.8 Notes on Miscellaneous Changes~/ Execution of ~ilc[table] events has been sped up in many cases by avoiding excessive consing. ACL2 now warns if ~c[:]~ilc[rewrite] (or ~c[:]~ilc[definition]) rules contain free variables on the right-hand side. Thanks to Dave Greve for raising this issue. Emacs file ~c[emacs/emacs-acl2.el] has been updated to better comprehend the notion of the ``ACL2 shell'', which is the buffer to which ACL2 forms are written by commands defined in the above file. Thus, command ~c[control-t e] has been modified always to write to the ACL2 shell (which is ~c[\"*shell*\"] by default), and the following new commands have been defined. ~bq[] o control-t c~nl[] Set the ACL2 shell to the current buffer. o control-t b~nl[] Change to the ACL2 shell. ~eq[] The commands ~c[:]~ilc[pl] and ~c[:]~ilc[pr] may now be given a macro name that corresponds via the ~c[macro-aliases-table] to a function name, so that for example ~c[:pl append] is treated the same as ~c[:pl binary-append]. A more interesting improvement, for ~c[:pl] only, is that ~c[:pl] may now take any term. When ~c[:pl] is given a term other than a symbol, it will print all rewrite rules that match that term. Thanks to David Russinoff, Robert Krug, and Bill Legato for getting this going. A new function, ~ilc[pkg-witness], returns a symbol in the given package. The installation instructions have been updated, for example to give more guidance on obtaining Lisp implementations and to mention the acl2-help mailing list. Jared Davis has suggested some symbols to be added to ~c[*acl2-exports*], and we have done so. Thanks, Jared. ~bq[] o ~c[MFC] (used in ~ilc[syntaxp] and ~ilc[extended-metafunctions]; thanks also to Robert Krug for this one) o ~c[ID], ~c[CLAUSE], ~c[WORLD], and ~c[STABLE-UNDER-SIMPLIFICATIONP] (used in ~ilc[computed-hints]) o ~ilc[SET-DEFAULT-HINTS] ~eq[] The command ~c[:]~ilc[pe] has been improved so that when the event is inside an included book, the path of included books (from the top-level book down to the one containing the event) is shown. Thanks to Eric Smith (perhaps among others) for pointing out the utility of this improvement. A new release of the rtl library has been included: ~c[books/rtl/rel4/]. See the ~c[README] file in that directory. ~/~/") (deflabel |NOTE-2-8(R)| :doc ":Doc-Section release-notes ACL2 Version 2.8(r) (March, 2003) Notes~/ The ~c[Makefile] has been modified by adding a new target, ~c[clean-links]. This can be used in order to remove all soft links, which is useful if the directory is copied or moved to a new location or if there are file system changes that cause problems with link pathnames. ~/ Please also ~pl[note-2-8] for changes to Version_2.8 of ACL2. ~/ ") (deflabel note-2-9 ; BUG FIXES: ; Example for the soundness bug involving *1* package names: ; ============================== sub.lisp ============================== ; ; #| ; (defpkg "ACL2_*1*_MYPKG" ()) ; (certify-book "sub" 1) ; |# ; ; (in-package "ACL2_*1*_MYPKG") ; ; (acl2::defun foo (x) ; (acl2::declare (acl2::xargs :verify-guards acl2::t)) ; x) ; ; ============================== top.lisp ============================== ; ; #| ; (include-book "sub") ; (defpkg "MYPKG" ()) ; (certify-book "top" 2) ; |# ; ; (in-package "ACL2") ; ; (defthm lemma1 ; (equal (acl2_*1*_mypkg::foo 3) 3) ; :rule-classes nil) ; ; (defun mypkg::foo (x) ; (cons x x)) ; ; (defthm lemma2 ; (equal (acl2_*1*_mypkg::foo 3) '(3 . 3)) ; :rule-classes nil) ; ; (defthm ouch ; nil ; :rule-classes nil ; :hints (("Goal" :use (lemma1 lemma2)))) ; End of example related to *1* package names. ; Example related to soundness bug on local and redundancy checking: ; (encapsulate ; () ; (defun foo (x) ; (declare (xargs :mode :program)) ; (zp x)) ; (local (verify-termination foo)) ; (defun bar (x) ; (foo x)) ; (defthm thm-1 ; (bar -1) ; :rule-classes nil)) ; ; (defthm thm-2 ; (not (bar -1)) ; :rule-classes nil) ; ; (defthm bad ; nil ; :rule-classes nil ; :hints (("Goal" :use (thm-1 thm-2)))) ; Here's a related example, showing how to get a world where a ; :common-lisp-compliant function, bar, calls an :ideal function, foo. ; ============================== abc.lisp ============================== ; ; (in-package "ACL2") ; ; (defun foo (x) ; (declare (xargs :guard (consp x))) ; (car x)) ; ; ============================== abc-top.lisp ============================== ; ; (in-package "ACL2") ; ; (local (include-book "abc")) ; ; (defun foo (x) ; (declare (xargs :guard (consp x) :verify-guards nil)) ; (car x)) ; ; (defun bar (x) ; (declare (xargs :guard (consp x))) ; (foo x)) ; End of example related to soundness bug on local and redundancy checking. ; Example related to soundness bug pertaining to safe-mode. ; ============================== bad1.lisp ============================== ; ; (in-package "ACL2") ; ; (defconst *c* '(((a b)))) ; ; (defconst *d* *c*) ; ; (defmacro bad-macro () ; (list 'quote (union-eq-cars (list *c* *d*)))) ; ; (defthm thm1 ; (equal (bad-macro) ; '((a b))) ; :rule-classes nil) ; ; ============================== bad2.lisp ============================== ; ; (in-package "ACL2") ; ; (defconst *c* '(((a b)))) ; ; (defconst *d* '(((a b)))) ; ; (defmacro bad-macro () ; (list 'quote (union-eq-cars (list *c* *d*)))) ; ; (defthm thm2 ; (equal (bad-macro) ; '((a b) (a b))) ; :rule-classes nil) ; ; ============================== bad.lisp ============================== ; ; (in-package "ACL2") ; ; (include-book "bad1" :load-compiled-file nil) ; ; (include-book "bad2" :load-compiled-file nil) ; ; (defthm ouch ; nil ; :hints (("Goal" :use (thm1 thm2))) ; :rule-classes nil) ; ; ====================================================================== ; End of example related to soundness bug pertaining to safe-mode. :doc ":Doc-Section release-notes ACL2 Version 2.9 (October, 2004) Notes~/ ~bf[] ~st[TABLE OF CONTENTS.]~nl[] ============================== BUG FIXES. NEW FUNCTIONALITY. CHANGES IN PROOF ENGINE. GUARD-RELATED CHANGES. PROOF-CHECKER CHANGES. SYSTEM-LEVEL CHANGES. BOOK CHANGES. MISCELLANEOUS CHANGES. ============================== ~ef[] ~st[BUG FIXES.] We fixed a soundness bug due to a conflict between user-supplied package names and internal package names (obtained by prepending a Lisp constant, ~c[*1*-package-prefix*]) and user-supplied package names. For example, the form ~c[(defpkg \"ACL2_*1*_MYPKG\" ())] is no longer legal. Thanks to Robert Krug for asking a question that led directly to the discovery of this bug. We fixed a soundness bug that allows ~c[:]~ilc[logic] mode functions to call ~c[:]~ilc[program] mode functions. The fix furthermore prevents functions with ~il[guard]s verified from calling functions with guards not verified. We had thought we already prevented all this, but there was a problem with the interaction of ~ilc[local] definitions and redundancy checking (~pl[redundant-events]). We fixed a soundness bug that could occur when built-in functions were called during macroexpansion (a hole in so-called ``safe-mode''). Fixed a minor bug in system functions ~c[genvar1] and ~c[genvar], where ~ilc[eq] had been used in place of ~ilc[eql]. This bug was discovered during the plugging of a hole in safe-mode, mentioned just above. We fixed handling of the ~c[:inline] keyword for ~ilc[defstobj], which previously could cause raw Lisp errors in OpenMCL and CMU Common Lisp. Thanks to John Matthews for bringing this issue to our attention. Calls of ~ilc[include-book] could result in a state for which some function definitions were not compiled that should have been. The result could be performance degradation or even stack overflows. This situation could arise in the following two ways.~bq[] o Inclusion of a book with an absolute pathname that differs from the absolute pathname at certification time, presumably because of the use of soft links. Thanks to Bob Boyer and Warren Hunt for bringing a stack overflow to our attention that led us to this fix. o Large ~ilc[mutual-recursion] nests (more than 20 functions) are executed in a superior book. ~eq[] We fixed some performance bugs that can increase the speed of ~ilc[include-book] calls by a factor of close to 2. Thanks to Eric Smith for asking if we could speed up ~ilc[include-book] processing; we have done so in the past, but primarily focusing on large ~ilc[mutual-recursion] nests (which have nothing in particular to do with the current improvements). Also, thanks to Rob Sumners for a very useful remark early in the process that kept us from drawing an incorrect conclusion at that point. We fixed ~c[:]~ilc[pl] so that it can be run on a form that returns multiple values, which it could not do previouslly. Thanks to Eric Smith for pointing out this problem. Fixed a bug in the Allegro ACL2 trace utility (~pl[trace$]) that was causing ``~c[10>]'' to be printed as ``~c[9>]'', ``~c[11>]'' to be printed as ``~c[10 >]'', ``~c[12>]'' to be printed as ``~c[11 >]'', and so on. Fixed a ~il[proof-checker] bug that was preventing the use of the ~c[DV] command (or a numerical command) on ~ilc[let] expressions. Thanks to Bill Young for pointing out this problem. Fixed a bug in a comment on how to set ~c[ACL2_BOOKS_DIR] in the makefile. Thanks to Dave Greve for pointing out this problem. Fixed a potential soundness bug in the linear arithmetic routines. Thanks to Jared Davis for noticing this problem and to Robert Krug for implementing the fix. (Technical details: We had been assuming that polynomials were being normalized -- see the definition of good-polyp in linear-a.lisp -- but this assumption was false.) When the macro ~ilc[open-trace-file] is opened twice in succession, it now automatically closes the first trace output channel before opening another. It is now possible to use `make' to build ACL2 on Windows systems that support `make'. Thanks to Pete Manolios and Mike Thomas for pointing out the problem, to Jared Davis and Mike for helping us to analyze the problem, and to Jared for testing the fix. Fixed a bug in the ~il[guard] of ~ilc[with-output] that was causing a needless guard violation. ~st[NEW FUNCTIONALITY.] The new events ~ilc[add-default-hints] and ~ilc[remove-default-hints] allow one to append to or subtract from the current list of default hints. The event ~ilc[set-default-hints] continues to set the list of default hints, discarding the previous value of the ~ilc[default-hints]. Note that ~ilc[set-default-hints] is still ~ilc[local] to the ~ilc[encapsulate] or book in which it occurs, and ~ilc[add-default-hints] has the same property, although neither is implemented any longer using the ~ilc[acl2-defaults-table]. New events ~ilc[add-default-hints!], ~ilc[remove-default-hints!], and ~ilc[set-default-hints!] are the same as ~ilc[add-default-hints], ~ilc[remove-default-hints], and ~ilc[set-default-hints], respectively, except that the former three events are not ~ilc[local] to their enclosing ~ilc[encapsulate] or book. Thanks to Jared Davis for suggesting these enhancements. OpenMCL's tracing routines have been modified in a similar manner as those of Allegro CL. Thanks to Robert Krug for providing this enhancement. Guard-checking can now be caused to happen on recursive calls. See ``GUARD-RELATED CHANGES'' below for details. Advanced users can now inhibit compilation of so-called ``*1* functions'' with the ~c[:comp] command; ~pl[comp]. Thanks to Rob Sumners for suggesting this enhancement. Added new legal argument ~c[hard?] for the ~ilc[er] macro, which is now documented. ~l[er]. Thanks to Rob Sumners for a discussion leading to this change. Also, the three legal first arguments to ~ilc[er] ~-[] ~c[hard], ~c[hard?], and ~c[soft] ~-[] may now be in any package (thanks to Jared Davis for bringing this issue to our attention). We have removed the requirement that for a rule's hypothesis ~c[(bind-free term var-list)], at least one variable must occur free in ~c[term]. For example, the expression ~c[(bind-free (bind-divisor a b) (x))] was legal because ~c[a] and ~c[b] occur free in ~c[(bind-divisor a b)]; but ~c[(bind-free (foo (bar)) (x))] was not legal. The latter is no longer disallowed. (Technical note: this allows ~ilc[bind-free] to be used to create explicit substitutions in metafunction hypotheses.) The following two enhancements have been implemented for rules of class ~c[:]~ilc[meta]. Thanks to Eric Smith for requesting more control of reasoning with ~c[:]~ilc[meta] rules, which led to these enhancements, and to him and Robert Krug for helpful discussions.~bq[] o It is now possible to control backchaining in rules of class ~c[:]~ilc[meta] by providing a ~c[:backchain-limit-lst] argument, as was already allowed for rules of class ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear]. ~l[rule-classes]. However, unlike those other two rule classes, the value for ~c[:backchain-limit-lst] is prohibited from being a non-empty list; it must be either ~c[nil] or a non-negative integer. o (For advanced users.) It is now legal for hypothesis metafunctions to generate, in essense, calls of ~ilc[syntaxp] and ~ilc[bind-free], handled essentially as they are handled in hypotheses of ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear] rules. We say ``essentially'' primarily because both ~ilc[syntaxp] and ~ilc[bind-free] are actually macros, but hypothesis metafunctions must generate translated terms (~pl[term]). The enterprising advanced user can call ~c[:]~ilc[trans] to see examples of translated terms corresponding to calls of ~ilc[syntaxp] and ~ilc[bind-free]. ~eq[]A new command ~c[:]~ilc[trans!] has been added, which is like ~c[:]~ilc[trans] except that ~c[:]~ilc[trans!] ignored issues of single-threadedness. ~l[trans!]. Thanks to Eric Smith for suggesting this addition. The ~c[:]~ilc[pf] command now works when the argument is the name of a macro associated with a function by ~il[macro-aliases-table]. ~st[CHANGES IN PROOF ENGINE.] The simplifier has been changed slightly in order to avoid using ~il[forward-chaining] facts derived from a literal (essentially, a top-level hypothesis or conclusion) that has been rewritten. As a practical matter, this may mean that the user should not expect forward-chaining to take place on a term that can be rewritten for any reason (generally function expansion or application of rewrite rules). Formerly, the restriction was less severe: forward-chaining facts from a hypothesis could be used as long as the hypothesis was not rewritten to ~c[t]. Thanks to Art Flatau for providing an example that led us to make this change; see the comments in source function ~c[rewrite-clause] for details. The rewriter has been modified to work slightly harder in relieving hypotheses. Thanks to Eric Smith for providing an example that inspired the following, which illustrates the issue. Suppose we introduce functions ~c[foo] and ~c[bar] with the (non-~ilc[local]) properties shown below. ~bv[] (encapsulate (((foo *) => *) ((bar *) => *)) (local (defun foo (x) (declare (ignore x)) t)) (local (defun bar (x) (declare (ignore x)) t)) (defthm foo-holds (implies x (equal (foo x) t))) (defthm bar-holds-propositionally (iff (bar x) t))) ~ev[] Consider what happens when ACL2's rewriter is used to prove the following theorem. ~bv[] (thm (foo (bar y))) ~ev[] With ACL2's inside-out rewriting, ~c[(bar y)] is first considered, but rewrite rule ~c[bar-holds-propositionally] does not apply because the context requires preserving equality, not mere Boolean (~c[iff]) equivalence. Then the rewriter moves its attention outward and sees the term ~c[(foo (bar y))]. It attempts to apply the rule ~c[foo-holds], in a context created by binding its variable ~c[x] to the term ~c[(bar y)]. It then attempts to relieve the hypothesis ~c[x] of rule ~c[foo-holds] in that context. Before this change, ACL2 basically assumed that since rewriting was inside out, then ~c[(bar y)] had already been rewritten as much as possible, so the rewrite of ~c[x] in the aforementioned context (binding ~c[x] to ~c[(bar y)]) simply returned ~c[(bar y)], and the attempt to relieve the hypothesis of ~c[foo-holds] failed. The change is essentially for ACL2's rewriter to make a second pass through the rewriter when the attempt fails to rewrite a variable to ~c[t], this time using the fact that we are in a Boolean context. (We mention that source function ~c[rewrite-solidify-plus] implements this idea, for those who want to dig deeply into this issue.) In our example, that means that the rewriter considers ~c[(bar y)] in a Boolean context, where it may apply the rule ~c[bar-holds-propositionally] to relieve the hypothesis successfully. When ~c[(]~ilc[set-non-linearp]~c[ t)] has been executed, ~il[non-linear-arithmetic] can now be applied in some cases for which it previously was not. Thanks to Robert Krug for supplying this modification and to Julien Schmaltz for providing a motivating example. We modified the rewriter to avoid certain infinite loops caused by an interaction of the opening of recursive functions with equality reasoning. (This change is documented in detail in the source code, in particular functions ~c[rewrite-fncall] and ~c[fnstack-term-member].) Thanks to Fares Fraij for sending us an example that led us to make this change. The ~c[:]~ilc[executable-counterpart] of function ~ilc[hide] is now disabled when ACL2 starts up. This removes an anomoly, for example that ~bv[] (thm (not (equal 1 (* (hide 0) a)))) ~ev[] succeeded while ~bv[] (thm (equal (foo (equal 1 (* (hide 0) a))) (foo nil))) ~ev[] failed. Now both fail. The theory ~c[*s-prop-theory*] is no longer used by the ~i[proof-checker]; it has been replaced by ~c[(theory ']~ilc[minimal-theory]. We have left the constant ~c[*s-prop-theory*] defined in the source code in support of existing books, however. This change eliminates annoying theory warnings printed upon invocation of ~il[proof-checker] commands ~c[s-prop], ~c[sl], and ~c[split]. Terms are now kept in an internal form that avoids calls of primitive functions (built-ins without explicit definitions; see code for ~c[cons-term] for details), in favor of the constants that result from evlaluating those calls. So for example, the internal form for ~c[(cons 1 2)] is ~c[(quote (1 . 2))]. This change was made at around the same time as changes in support of ~ilc[bind-free]; see above. One consequence is that the splitting of goals into cases (technically, source function ~c[clausify] and even more technically, source function ~c[call-stack]) has been modified, which can cause subgoal numbers to change. ~st[GUARD-RELATED CHANGES.] Guard-checking can now be caused to happen on recursive calls, where this was formerly not the case for ~c[:]~ilc[program] mode functions and, perhaps more important, for ~c[:]~ilc[logic] mode functions whose ~il[guard]s have not been verified. Moreover, a warning is printed when ACL2 does not rule out the exclusion of guard-checking on recursive calls. ~l[set-guard-checking]. Thanks to David Rager for bringing this issue to our attention, and to Rob Sumners and the Univ. of Texas ACL2 seminar in general for their feedback. Guard violations are reported with less of the offending term hidden. Thanks to Jared Davis for suggesting that we look at this issue. ~st[PROOF-CHECKER CHANGES.] We fixed the ~il[proof-checker] so that diving works as you might expect for a macro call ~c[(op a b c)] representing ~c[(binary-op a (binary-op b c))]. In the past, if the current term was of the form ~c[(append t1 t2 t3)], then ~c[(DV 3)] (and ~c[3]) would dive to ~c[t3] even though the corresponding function call is ~c[(binary-append t1 (binary-append t2 t3))]. This is still the case, but now this behavior holds for any macro associated with a function in ~c[binop-table] (~pl[add-binop]). Moreover, users can now write customized diving functions; ~pl[dive-into-macros-table], and also see ~c[books/misc/rtl-untranslate.lisp] for example calls to ~ilc[add-dive-into-macro]. Of course, the old behavior can still be obtained using the ~il[proof-checker]'s ~c[DIVE] command; ~pl[proof-checker-commands]. The ~c[runes] command in the ~il[proof-checker] now shows only the ~il[rune]s used by the most recent primitive or macro command (as shown by ~c[:comm]), unless it is given a non-~c[nil] argument. Also, ~il[proof-checker] command ~c[lemmas-used] has been added as, in essence, an alias for ~c[runes]. (The following two items are also mentioned above under ``BUG FIXES.'') Fixed a ~il[proof-checker] bug that was preventing the use of the ~c[DV] command (or a numerical command) on ~ilc[let] expressions. Thanks to Bill Young for pointing out this problem. The theory ~c[*s-prop-theory*] is no longer used by the ~i[proof-checker]; it has been replaced by ~c[(theory ']~ilc[minimal-theory]. We have left the constant ~c[*s-prop-theory*] defined in the source code in support of existing books, however. This change eliminates annoying theory warnings printed upon invocation of ~il[proof-checker] commands ~c[s-prop], ~c[sl], and ~c[split]. ~st[SYSTEM-LEVEL CHANGES.] Fixed a problem with building ACL2 on CMUCL in some systems (source function ~c[save-acl2-in-cmulisp]). Thanks to Bill Pase for bringing this to our attention. The installation instructions have been extended to include instructions for building on GCL in Mac OS X. Thanks to Jun Sawada and Camm Maguire. Initial pre-allocation of space has been updated for GCL to reflect more current GCL executables (we considered GCL 2.6.1-38). This can help avoid running out of memory for large ACL2 sessions. The main ~c[Makefile] has been replaced by ~c[GNUmakefile], in order to enforce the use of GNU `make'. If you use another `make' program, you'll get an error message that may help you proceed. (GCL only) SGC is no longer turned on for GCL 2.6 sub-versions through 2.6.3 if ~c[si::*optimize-maximum-pages*] is bound to ~c[T], due to an apparent issue with their interaction in those sub-versions. Also, we have eliminated preallocation for all versions after 2.6.1 because GCL doesn't need it (comments are in source function ~c[save-acl2-in-akcl]). Thanks to Camm Maguire for excellent GCL help and guidance, and to Camm and Bob Boyer for useful discussions. We have removed support for so-called ``small'' images. Thus, ~c[:]~ilc[doc], ~c[:]~ilc[pe] and ~c[:]~ilc[pc], ~ilc[verify-termination], and other commands are fully supported in ACL2 saved images. Because of this and other changes in the generation of the so-called ``*1*'' logical functions, related to guards (as described above in -GUARD-RELATED CHANGES'', and related to the discussion of safe-mode in ``BUG FIXES'' above), image sizes have increased substantially. We no longer ~c[time] or run ``~c[nice]'' the certification of individual books. The file ~c[books/Makefile-generic] had done these by default, and some individual distributed and workshop book directories had ~c[Makefile]s that did so as well. Thanks to Mike Thomas, who pointed out the lack of ~c[nice] on some Windows systems (and we decided on this simple solution). Overall targets in ~c[books/Makefile] still ~c[time] their runs by default, and the partiular ~c[time] program is now controlled by a ~c[Makefile] variable. Failures during ~c[make certify-books] or ~c[make regression] now show up in the log as ``~c[**CERTIFICATION FAILED**]'', regardless of the operating system (as long as it supports `make'). Formerly, one searched for ``~c[**]'' but this did not appear in openMCL runs. We have eliminated ``Undefined function'' warnings that could occur in OpenMCL. ~st[BOOK CHANGES.] Reconciled the definitions of ~c[firstn] in ~c[book/misc/csort.lisp], ~c[books/bdd/bdd-primitives.lisp], ~c[books/ordinals/ordinal-definitions.lisp], and ~c[books/data-structures/list-defuns.lisp]. Thanks to Ray Richards for bringing this issue to our attention. Distributed book ~c[books/misc/defpun] now can handle ~il[stobj]s where it did not previously. Thanks to John Matthews for bringing this issue to our attention. The \"make\" variable ~c[COMPILE_FLG] in file ~c[books/Makefile-generic] formerly only had an effect if there was a ~c[cert.acl2] file present. That oversight has been remedied. File ~c[\"books/arithmetic/certify.lsp\"] was missing a ~ilc[certify-book] command for ~c[\"natp-posp\"]. Thanks to John Cowles for noticing this deficiency and supplying a fix. (This file is of use to those who want to certify the ~c[\"books/arithmetic/\"] books without using ~c[\"make\"].) A few small changes have been made to ~c[\"books/rtl/rel4\"]. Small changes were made to books ~c[misc/symbol-btree] and ~c[misc/rtl-untranslate]. In particular, the definition of ~c[symbol-btreep] was strengthened. We made a minor fix to ~c[books/ordinals/e0-ordinal.lisp], adding ~c[(verify-guards ob+)] and hence ~c[(verify-guards ocmp)] as well. This was necessitated by the fix prohibiting functions with guards verified from calling functions with guards not verified (see also the related discussion under ``BUG FIXES'' above). ~st[MISCELLANEOUS CHANGES.] Further sped up processing of large ~ilc[mutual-recursion] nests (extending what was done for Version_2.7), perhaps by a factor of two in some cases. As promised in Version_2.5 (~pl[note-2-5]), structured pathnames are no longer supported. So for example, the argument to ~ilc[include-book] must now be a string constant. Some documentation has been improved, for ~il[stobj]s thanks to suggestions by John Matthews and much of the rest thanks to feedback from Eric Smith. The function ~ilc[current-package] is now available to users (it has been taken off the list of so-called ``untouchables''). Thanks to Jared Davis for bringing this issue to our attention. The documentation for topic ~il[using-computed-hints-7] has been improved. Thanks to Doug Harper and Eric Smith for inspiring this improvement. We added several symbols to ~c[*acl2-exports*]: ~ilc[cw], ~ilc[er], ~ilc[intern$], ~ilc[set-case-split-limitations], and ~ilc[set-difference-eq]. Thanks to Jared Davis for suggesting most of these. Now, a ~ilc[table] event that sets the value for a key, ~c[(table tbl key val :put)], is redundant (~pl[redundant-events]) when it does not change the value associated with an existing key of the table. In particular, ~ilc[define-pc-macro] is now fully redundant when it does not change an existing ~il[proof-checker] macro-command definition. Thanks to Bill Young for bringing the latter issue to our attention. The definitions of unused system functions ~c[ev-w] and ~c[ev-w-lst] have been deleted. ACL2 now prints a warning if a ~ilc[defpkg] event introduces a package name with lower-case letters, since there is opportunity for later confusion in that case. Thanks to Frederic Peschanski for bringing this problem to our attention and Sandip Ray for encouragement. ACL2 now works in Version 19 of CMU Common Lisp. The function ~ilc[sys-call] has been modified so that for ACL2 built on Allegro Common Lisp in Unix or Linux, the existing environment is used. Thanks to Erik Reeber for bringing this issue to our attention. The function ~ilc[disabledp] can now be given a macro name that has a corresponding function; ~pl[macro-aliases-table]. Also, ~ilc[disabledp] now has a ~il[guard] of ~c[t] but causes a hard error on an inappropriate argument. ~/~/") (deflabel |NOTE-2-9(R)| :doc ":Doc-Section release-notes ACL2 Version 2.9(r) (October, 2004) Notes~/ No changes have been made for support of non-standard analysis, other than a minor modification or two in ~c[books/nonstd/] books. ~/ Please also ~pl[note-2-9] for changes to Version_2.9 of ACL2. ~/ ") (deflabel note-2-9-1 ; Changes that are too low-level for the user documentation: ; The name *soundness-related-warning-summaries* has been changed to ; *uninhibited-warning-summaries*, and "Compiled file" has been added to this ; list, along with "Tainted". When we added "Tainted", we found that a ; "Compiled file" warning could be made without the "Compiled file" showing up ; in the event summary. That appears to be because we circumvented the basic ; design of warnings in function unfound-compiled-file by locally binding state ; global 'ld-skip-proofsp to nil. ; Here is an example of how to exploit the soundness bug, mentioned below, in ; symbol-package-name. ; ; ++++++++++++++++++++++++++++++ bad-1.lisp ++++++++++++++++++++++++++++++ ; ; (in-package "ACL2") ; ; (defthm lemma-1 ; (equal (symbol-package-name (intern (car (cons "X-EQUIV" xxx)) "COMMON-LISP")) ; "LISP") ; :rule-classes nil) ; ; ++++++++++++++++++++++++++++++ bad-2.lisp ++++++++++++++++++++++++++++++ ; ; (in-package "ACL2") ; ; (defthm lemma-2 ; (equal (symbol-package-name 'COMMON-LISP::X-EQUIV) ; "COMMON-LISP") ; :rule-classes nil) ; ; ++++++++++++++++++++++++++++++ bad.lisp ++++++++++++++++++++++++++++++ ; ; (in-package "ACL2") ; ; (include-book "bad-1") ; ; (include-book "bad-2") ; ; (defthm bad ; nil ; :hints (("Goal" :use lemma-1)) ; :rule-classes nil) ; ; ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ :doc ":Doc-Section release-notes ACL2 Version 2.9.1 (December, 2004) Notes~/ (GCL only) A bug in ~ilc[symbol-package-name] has been fixed that could be exploited to prove ~c[nil], and hence is a soundness bug. Thanks to Dave Greve for sending us an example of a problem with ~ilc[defcong] (see below) that led us to this discovery. ACL2 now warns when ~ilc[defcong] specifies ~ilc[equal] as the first equivalence relation, e.g., ~c[(defcong equal iff (member x y) 2)]. The warning says that the rule has no effect because ~ilc[equal] already refines all other equivalence relations. Formerly, this caused an error unless ~c[:event-name] was supplied (~pl[defcong]), and in fact the error was a nasty raw Lisp error on GCL platforms due to some mishandling of packages by ACL2 that has been fixed (see the paragraph about ~ilc[symbol-package-name] above). Thanks to Dave Greve for sending a helpful example in his report of this problem. (GCL only) The build process was broken for GCL 2.6.0 (and perhaps some earlier versions), and has been fixed. Thanks to Jose Luis Ruiz-Reyna for bringing this problem to our attention. (GCL only) We have increased the hole size to at least 20% of max-pages, which may eliminate some garbage collection at the expense of larger virtual memory (not larger resident memory or larger image). Thanks to Camm Maguire for helpful explanations on this topic. We have clarified the ~il[guard] warning message that is printed during evaluation of recursively-defined functions whose ~il[guard]s have not been verified, for example: ~bv[] ACL2 Warning [Guards] in TOP-LEVEL: Guard-checking may be inhibited on some recursive calls of executable counterparts (i.e., in the ACL2 logic), including perhaps EVENLP. To check guards on all recursive calls: (set-guard-checking :all) To leave behavior unchanged except for inhibiting this message: (set-guard-checking :nowarn) ~ev[] And, ACL2 no longer prints that message when the ~il[guard] was unspecified for the function or was specified as ~c[T]. Thanks to Serita Nelesen for bringing the latter issue to our attention. Finally, ACL2 now prints such a warning at most once during the evaluation of any top-level form; thanks to Bill Young for pointing out this issue. The function ~ilc[verbose-pstack] has been enhanced to allow specified prover functions ~st[not] to be traced. ~l[verbose-pstack]. Added ~ilc[lp], ~c[wet], and ~ilc[set-non-linearp] to ~c[*acl2-exports*], and hence to the ~c[\"]~ilc[ACL2-USER]~c[\"] package. The distributed book ~c[books/arithmetic-3/bind-free/integerp.lisp] has been modified in order to prevent potential looping; specifically, the definition of function ~c[reduce-integerp-+-fn-1]. Thanks to Robert Krug for providing this change. A small improvement was made in the ~c[wet] failure message when the error occurs during translation to internal form. Thanks to Jared Davis for pointing out the obscurity of some ~c[wet] error messages. We have improved ACL2's evaluation mechanism for the function ~c[bad-atom<=], which now is specified to return ~c[nil] if neither argument is a so-called ``bad atom'' (as recognized by function ~c[bad-atom]). The following events had caused a hard error, for example. (We're sorry that ~c[bad-atom] and ~c[bad-atom<=] are not documented, but we also consider it unlikely that anyone needs such documentation; otherwise, please contact the implementors.) ~bv[] (defun foo (x y) (declare (xargs :guard t)) (bad-atom<= x y)) (defun bar (x y) (declare (xargs :guard t)) (foo x y)) (thm (equal (bar 3 4) 7)) ~ev[] We have also changed the guard on ~ilc[alphorder] to require both arguments to be atoms. For forms ~c[(local x)] that are skipped during ~ilc[include-book], or during the second pass of ~ilc[certify-book] or ~ilc[encapsulate], ACL2 had nevertheless checked that ~c[x] is a legal event form. This is no longer the case. The ~il[proof-checker] now does non-linear arithmetic when appropriate. It had formerly ignored ~ilc[set-non-linearp] executed in the ACL2 command loop. Incremental releases are now supported. ~l[version] and {obsolete after Version 4.3} set-tainted-okp. Thanks to Hanbing Liu for discovering a flaw in our original design. The pattern-matching algorithm for ~c[:]~ilc[rewrite] rules has been made slightly more restrictive, thanks to a suggestion and examples from Robert Krug. For example, previously one could get an infinite loop as follows. ~bv[] (defstub foo (x) t) (defaxiom foo-axiom (equal (foo (+ 1 x)) (foo x))) (thm (foo 0)) ; or replace 0 by any integer! ~ev[] That is because the term ~c[(foo 0)] was considered to match against the pattern ~c[(foo (+ 1 x))], with ~c[x] bound to ~c[-1]. While such matching is sound, it leads to an infinite loop since it allows ~c[foo-axiom] to rewrite ~c[(foo 0)] to ~c[(foo -1)], and then ~c[(foo -1)] to ~c[(foo -2)], and so on. The fix is to insist that the new value, in this case ~c[-1], is no larger in size according to ~ilc[acl2-count] than the old value, in this case ~c[0]. Since that test fails, the match is considered to fail and the loop no longer occurs. An analogous fix has been made for multiplication, where now we only match when the new term is still a non-zero integer. That change avoids a loop here. ~bv[] (defstub foo (x) t) (defaxiom foo-axiom (equal (foo (* 2 x)) (foo x))) (thm (foo 0)) ; or try (thm (foo 4)) ~ev[] Added macro ~c[find-lemmas] in ~c[books/misc/find-lemmas.lisp] (see brief documentation there) for finding all lemmas that mention all function symbols in a given list. ~c[:Restrict] ~il[hints] now work for ~c[:]~ilc[definition] rules, though they continue to be ignored by the preprocessor and hence you may want to use ~c[:do-not '(preprocess)] with any restrict hints. Thanks to John Matthews for pointing out the lack of support for ~c[:definition] rules in ~c[:restrict] hints. Some books have been updated. In particular, there is a new directory ~c[books/workshops/2004/] in workshops distribution, for the 2004 ACL2 workshop. There is also a new version of Jared Davis's ordered sets library, formerly in ~c[books/finite-set-theory/osets-0.81/] but now in ~c[books/finite-set-theory/osets/]. Fixed a bug in the (under-the-hood) raw Lisp definition of ~ilc[defchoose], which had been causing a warning in CMU Common Lisp. [Technical improvements related to the use of ``~c[make dependencies]'' for certifying distributed books:]~nl[]File ~c[books/Makefile-generic] now does a better job with ``~c[make dependencies],'' specifically with respect to handling ~c[*.acl2] files and handling ~ilc[include-book] commands with ~c[:dir :system]. Regarding the latter, suppose for example that book ~c[basic.lisp] contains the line: ~bv[] (include-book \"arithmetic/top-with-meta\" :dir :system) ~ev[] Then ~c[make dependencies] would generate the following line: ~bv[] basic.cert: $(ACL2_SRC_BOOKS)/arithmetic/top-with-meta.cert ~ev[] Thus, if ~c[:dir :system] is used with ~ilc[include-book], the corresponding ~c[Makefile] should define the variable ~c[ACL2_SRC_BOOKS]. A standard ~c[Makefile] header for a books directory could thus be as follows. ~bv[] # The following variable should represent the ACL2 source directory. It is the # only variable in this Makefile that may need to be edited. ACL2_SRC = ../../../../../.. ACL2_SRC_BOOKS = $(ACL2_SRC)/books include $(ACL2_SRC_BOOKS)/Makefile-generic ACL2 = $(ACL2_SRC)/saved_acl2 ~ev[] Finally, the ``~c[-s]'' flag may now be omitted when running ``~c[make dependencies].'' ~/~/") (deflabel note-2-9-2 ; Example for item below, ; "Guard-related warnings could be printed during proofs. These warnings have ; been eliminated." ; ;(defun my-test1 (expr) ; (declare (xargs :guard (true-listp expr) ; :verify-guards nil)) ; (if (atom expr) ; expr ; (cons (my-test1 (car expr)) ; (my-test1 (cdr expr))))) ; ;(defun foo (n) ; (declare (xargs :measure (acl2-count n))) ; (if (zp n) n (foo (equal (list (my-test1 '(a b c)) (my-test1 '(a b c))) 17)) )) ; ;(thm (equal (list (my-test1 '(a b c)) (my-test1 '(a b c))) 17)) :doc ":Doc-Section release-notes ACL2 Version 2.9.2 (April, 2005) Notes~/ Also ~pl[note-2-9-1] for other changes since the last non-incremental release (Version_2.9). There was a bug in non-linear arithmetic (~pl[non-linear-arithmetic]) that caused the following error: ~bv[] ACL2 !>(include-book \"rtl/rel4/lib/top\" :dir :system) .... ACL2 !>(set-non-linearp t) T ACL2 !>(thm (implies (and (bvecp a 77) (bvecp b 50)) (bvecp (fl (/ (* a b) (expt 2 23))) 104)) :hints ((\"Goal\" :in-theory (enable bvecp)))) [Note: A hint was supplied for our processing of the goal above. Thanks!] By the simple :definition BVECP, the :executable-counterparts of EXPT and UNARY-/ and the simple :rewrite rule ASSOCIATIVITY-OF-* we reduce the conjecture to Goal' (IMPLIES (AND (INTEGERP A) (<= 0 A) (< A 151115727451828646838272) (INTEGERP B) (<= 0 B) (< B 1125899906842624)) (BVECP (FL (* A B 1/8388608)) 104)). HARD ACL2 ERROR in VARIFY: This should not have happened. The supposed variable, '1/8388608, is instead a constant. ACL2 !> ~ev[] Thanks to Robert Krug for providing a fix for the above error. Guard-checking was being inhibited (since v2-9) for calls of built-in primitives on explicit values, e.g., ~c[(car 3)]. This has been fixed. Guard-related warnings could be printed during proofs (this bug was introduced in Version_2.9.1). These warnings have been eliminated. Compound-recognizer rules ~c[natp-compound-recognizer] and ~c[posp-compound-recognizer] are now built into ACL2 for predicates ~ilc[natp] and ~ilc[posp], and hence have been deleted from book ~c[natp-posp.lisp] (where they were called ~c[natp-cr] and ~c[posp-cr], respectively). The function ~c[file-clock-p], which recognizes a component of the ACL2 ~ilc[state], is now defined using ~ilc[natp] instead of ~ilc[integerp]. Thanks to Jared Davis for suggesting this change. (Technical explanation about functions in ACL2 source file ~c[axioms.lisp]: With a ~c[file-clock] of -1, the call of ~c[make-input-channel] in ~c[open-input-channel] will create a channel that can't be closed; see the guard of ~c[close-input-channel].) (Allegro CL users only) Support is now provided for building an Allegro CL application, provided you have an Allegro CL dynamic runtime license. (Our belief is that with such a license, many users can use the same application, rather than each user needing a separate license.) See new GNUmakefile target ~c[allegro-app] and file ~c[build-allegro-exe.cl] for more information. The new home page now contains a link to a new page ~c[other-releases.html], which contains information about other ACL2 releases. (This is in one's local home page, but may not show up on the central ACL2 home page until the next non-incremental release.) Thanks to Warren Hunt for suggesting this addition. We thank Erik Reeber for suggesting a solution to output redirection using ~ilc[sys-call], which we have described at the end of its documentation. A new documentation topic fixes the flawed argument for conservativity of the ~ilc[defchoose] event that appears in Appendix B of Kaufmann and Moore's paper, ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203). ~l[conservativity-of-defchoose]. Thanks to John Cowles and Ruben Gamboa for helpful feedback on drafts of this note. The solution to exercise 6.15 in ~c[books/textbook/chap6/solutions.txt] has been fixed. Thanks to Aaron Smith for pointing out the problem. A new documentation topic ~il[defun-sk-example] gives a little more help in using ~ilc[defun-sk] effectively. Thanks to Julien Schmaltz for presenting this example as a challenge. (GCL only) There is now a way to speed up GCL builds of ACL2, at the cost of perhaps a percent or so in performance of the resulting image. Using `make' one supplies the following. ~bv[] LISP='gcl -eval \"(defparameter user::*fast-acl2-gcl-build* t)\" ~ev[] Various makefiles have been improved in several ways. ~bq[] (1) Parallel book certification, using GNU make's ~c[-j] option, can be used. (2) Book certifications now stops at the first failure if ~c[books/Makefile] or ~c[books/Makefile-generic] is used, and returns non-zero exit status. However, the various make targets in the ACL2 source directory (~c[regression], ~c[certify-books], etc.) still continue past failures unless you provide ~c[ACL2_IGNORE=' '] on the `make' command line. (3) The build process has been modified (file ~c[GNUmakefile]) so that it stops upon a failed compile or a failed initialization. (4) The automatic dependency generation (from ``~c[make dependencies]'' has been improved so that commands of the form ~c[(ld \"my-book.lisp\")] in ~c[.acl2] files cause the appropriate depedencies to be generated.~eq[] Thanks to comments from several users that led to the above Makefile improvements: Ray Richards, Doug Harper, and the Rockwell ACL2 users for (1) and (2) (and inspiring (4)), and David Rager for (2) and (3). In particular, Doug Harper sent a replacement for the ~c[.date] mechanism, which was interfering with ~c[make -n]; so, these files are no longer written. A mechanism has been added for saving output. In particular, you can now call ~ilc[ld] on a file with output turned off, for efficiency, and yet when a proof fails you can then display the proof attempt for the failed (last) event. ~l[set-saved-output]. Another new command ~-[] ~pl[set-print-clause-ids] ~-[] causes subgoal numbers to be printed during proof attempts when output is inhibited. Documentation has been added for using ACL2's makefile support to automate the certification of collections of books. ~l[books-certification-classic]. Fixed a bug in ~ilc[sys-call-status] that was causing hard Lisp errors. Improved ~ilc[cw-gstack] to allow a ~c[:frames] argument to specify a range of one or more frames to be printed. ~pl[cw-gstack]. Fixed a bug in ~il[proof-checker] command ~c[forwardchain]. Thanks to Ming-Hsiu Wang for bringing this bug to our attention. We have provided a mechanism for saving an executable image. ~l[saving-and-restoring] and ~pl[save-exec]. We have eliminated obsolete functions ~c[note-lib] and ~c[make-lib]. Modified the ~ilc[ground-zero] ~il[theory] so that it contains all of the built-in rules (in ACL2 source file ~c[axioms.lisp]). It had formerly failed to include rules from some definitions and theorems near the end of ~c[axioms.lisp]. A new event, ~ilc[set-enforce-redundancy], allows the enforcement of ~ilc[defthm], ~ilc[defun], and most other events during book development. ~l[set-enforce-redundancy]. A bug has been fixed that had allowed ~ilc[deftheory] ~il[events] to cause a hard Lisp error when calling ~ilc[union-theories] on ill-formed theories after, for example: ~bv[] :set-guard-checking nil (in-theory (union-theories '((:rewrite no-such-rule)) (current-theory 'ground-zero))) ~ev[] The handling of ~il[guard] checking has been modified somewhat in a way that should only very rarely affect users. (An ``Essay on Guard Checking'' in the ACL2 source code explains this point to anyone interested in implementation details.) (GCL ONLY) Removed the -dir setting in the ACL2 wrapper script for GCL. This should generally have no effect for most users, but it eliminates a potential source of error down the road. Several interesting new definitions and lemmas have been added to the rtl library developed at AMD, and incorporated into ~c[books/rtl/rel4/lib/]. Other book changes include a change to lemma ~c[truncate-rem-elim] in ~c[books/ihs/quotient-remainder-lemmas.lisp], as suggested by Jared Davis. The macro ~ilc[real/rationalp] may now be referred to in ~ilc[in-theory] ~il[events] and ~il[hints], thanks to a new ~ilc[add-macro-alias] event. Thanks to Jared Davis for this suggestion. ACL2 terms of the form ~c[(if p 'nil 't)] are now printed as ~c[(not p)], where in some setting they had been printed as ~c[(and (not p) t)]. Thanks to Robert Krug for this improvement. (GCL ONLY) Added profiling support, based heavily on code supplied by Camm Maguire. See file ~c[save-gprof.lsp] for instructions. Thanks to Camm, and also to David Hardin for inspiring this addition. Added support for preprocessing before printing (untranslating) a term. ~l[user-defined-functions-table], in particular the discussion of ~c[untranslate-preprocess]. Thanks to Jared Davis for inspiring this addition, and for providing a book that takes advantage of it (~c[books/misc/untranslate-patterns.lisp]). The documentation has been improved for explaining how ~il[rune]s are assigned; ~pl[rune]. Thanks to Robert Krug for pointing out inaccuracies in the existing documentation. ~/~/") (deflabel note-2-9-3 :doc ; Things that seem too minor to mention: ; (Intern (coerce (list #\Page) 'string) "ACL2") was printing as control-L ; without surrounding |..|, which cannot be read back in (at least in GCL). A ; similar problem occurs with (Intern (coerce (list #\A #\Page #\B) 'string) ; "ACL2") So, added #\Page to *slashable-chars*. ; Eliminated a warning in CMUCL 19b due to missing arguments in error cases for ; source functions find-alternative-start1 and find-alternative-stop. ":Doc-Section release-notes ACL2 Version 2.9.3 (August, 2005) Notes~/ Also ~pl[note-2-9-1] and ~pl[note-2-9-2] for other changes since the last non-incremental release (Version_2.9). We fixed a soundness bug that exploited the ability to define ~c[:]~ilc[program] mode functions that are improperly guarded, and then to use those functions in ~ilc[defconst] forms. The fix is to evaluate ~ilc[defconst] forms using the same ``safe-mode'' that is used in macroexpansion (~pl[guards-and-evaluation]). Here is a proof of ~c[nil] that succeeded in Allegro Common Lisp (but not, for example, GCL). See also a long comment in source function ~c[defconst-fn] for an example that does not require the use of ~c[:set-guard-checking]. ~bv[] :set-guard-checking nil ; execute before certifying the book below (in-package \"ACL2\") (encapsulate () (local (defun f1 () (declare (xargs :mode :program)) (char-upcase (code-char 224)))) (local (defconst *b* (f1))) (defun f1 () (char-upcase (code-char 224))) (defconst *b* (f1)) (defthm bad (not (equal *b* (code-char 224))) :rule-classes nil)) (defthm ouch nil :hints ((\"Goal\" :use bad)) :rule-classes nil) ~ev[] We fixed a soundness hole due to the fact that the \"LISP\" package does not exist in OpenMCL. We now explicitly disallow this package name as an argument to ~ilc[defpkg]. Thanks to Bob Boyer and Warren Hunt for bringing an issue to our attention that led to this fix. ACL2 now requires all package names to consist of standard characters (~pl[standard-char-p], none of which is lower case. The reason is that we have seen at least one lisp implementation that does not handle lower case package names correctly. Consider for example the following raw lisp log (some newlines omitted). ~bv[] >(make-package \"foo\") #<\"foo\" package> >(package-name (symbol-package 'FOO::A)) \"foo\" >(package-name (symbol-package '|FOO|::A)) \"foo\" > ~ev[] Distributed book ~c[books/textbook/chap10/compiler], as well as workshop books in directory ~c[books/workshops/2004/cowles-gamboa/support/], were modified to accommodate the above change. Added ~c[newline], ~ilc[add-to-set-eql], ~c[the-fixnum], and ~c[the-fixnum!] to ~c[*acl2-exports*]. Thanks to Jared Davis for bringing these to our attention. Added a line to ~c[acl2.lisp] to support CMUCL running on Mac OSX, thanks to a suggestion from Fabricio Chalub Barbosa do Rosario. The executable scripts for saved ACL2 images now include ~c[$*], so that command-line arguments will be passed along. (For GCL profiling only) Fixed a colon (~c[:]) that should have been a semicolon (~c[;]) in file ~c[save-gprof.lsp]. Thanks to David Hardin for pointing out this bug. The documentation for ~c[:]~ilc[elim] rules has been expanded and improved, thanks to useful feedback from Hanbing Liu. Fixed a bug in the guard for function ~c[include-book-dir]. For those who want to experiment with an alternate implementation of ~ilc[mv] and ~ilc[mv-let], there is now support for under-the-hood implementation of these in terms of raw Lisp functions ~c[values] and ~c[multiple-value-bind], respectively. The regression suite has seen about a 10% speed-up in Allegro CL and about an 8% slowdown in GCL for builds with this change. See the makefile (~c[GNUmakefile]) for examples of how to build ACL2 by including the feature, ~c[:acl2-mv-as-values]. Source file ~c[init.lsp] has been renamed to ~c[init.lisp] in support of this change (technical detail: otherwise GCL loads the init file too soon, before its ~c[-eval] argument is evaluated). Thanks to David Rager for inspiring this change, by pointing out the problematic use of globals by the existing ~ilc[mv] implementation from the standpoint of supporting parallel evaluation. This capability is experimental: there is likely to be some remaining work to be done on it. A change related to the one just above is that we now limit the maximum number of arguments to any call of ~ilc[mv] to 32. Thanks to Bob Boyer for raising a question that lead to this change. Eliminated some compiler warnings in OpenMCL. In the rtl library (~c[books/rtl/rel4/]), functions ~c[bits] and ~c[setbits] have had their ~il[guard]s improved (as they had been too restrictive, especially for ~c[setbits]). A new function ~ilc[time$] permits timing of forms, by using (under the hood) the host Common Lisp's ~c[time] utility. We fixed an infinite loop that could occur during destructor elimination (~pl[elim]). Thanks to Sol Swords to bringing this to our attention and sending a nice example, and to Doug Harper for sending a second example that we also found useful. The method of speeding up GCL-based builds (~pl[note-2-9-2]) has changed slightly from Version_2.9.2. Now, in the `make' command: ~bv[] LISP='gcl -eval \"(defparameter user::*fast-acl2-gcl-build* t)\" ~ev[] We improved the pretty-printer's handling of keywords. For example, before this change one might see the following printed by ACL2. ~bv[] (MODIFY TH S :KEY1 VAL1 :KEY2 (IF (IF X Y Z) AAAAAAAAAA BBBBBBB)) ~ev[] Now, the above might print as follows. Notice that we have avoided breaking after a keyword (~pl[keywordp]) that is preceded by other forms on the same line. ~bv[] (MODIFY TH S :KEY1 VAL1 :KEY2 (IF (IF X Y Z) AAAAAAAAAA BBBBBBB)) ~ev[] ~l[note-2-9-3-ppr-change] for a detailed discussion of this change. (GCL ONLY) Evaluation in a break is no longer inhibited by ACL2 when built on top of GCL, so GCL now matches other Common Lisps in this respect. For ACL2 built on most host Common Lisps, you will see the string ~c[[RAW LISP~]] in the prompt, at least at a break, to emphasize that one is inside a break and hence should probably quit from the break. ~l[breaks]. Jared Davis suggested improvements to lemmas ~c[len-update-nth] (in source file ~c[axioms.lisp]) and ~c[append-true-listp-type-prescription] (in ~c[books/meta/term-defuns.lisp]), which have been incorporated. The former required a change in ~c[books/workshops] book ~c[2004/ruiz-et-al/support/q-dag-unification.cert], which has been made. The ~il[proof-checker] command ~c[rewrite] allows further binding of free variables in hypotheses, with new optional argument ~c[instantiate-free]. Proof-checker command ~c[show-rewrites] (~c[sr]) gives corresponding additional information. Documentation for these commands has been improved; ~pl[proof-checker-commands]. Thanks to John Matthews and Bill Young for suggestions and feedback leading to these improvements. Fixed downcase printing so that the package name of a symbol is also downcased. For example, after execution of ~c[(defpkg \"FOO\" nil)] and ~c[(set-acl2-print-case :downcase)], ~c['foo::ab] will print back as the same, rather than as ~c['FOO::ab]. It is now possible to control the output so that numbers are printed in binary, octal, or hex, though the default is still radix 10. ~l[set-print-base]. Note that in support of this change, built-in functions ~ilc[explode-nonnegative-integer] and ~c[explode-atom] now take an extra ~c[print-base] argument. Different support for radix conversion may be found in a book newly contributed by Jun Sawada, ~c[books/misc/radix.lisp]. Built-in axiom ~c[car-cdr-elim] is now only an ~c[:]~ilc[elim] rule. It was formerly both an ~c[:elim] rule and a ~c[:]~ilc[rewrite] rule. A new rule, ~c[cons-car-cdr], takes the place of the old ~c[:rewrite] rule, but is instead a hypothesis-free rule that can cause a case split (see source file ~c[axioms.lisp]). Thanks to Jared Davis for suggesting this change. Lemmas about ~ilc[alphorder] (~c[alphorder-reflexive], ~c[alphorder-transitive], ~c[alphorder-anti-symmetric], and ~c[alphorder-total]) are now available. (They had been ~ilc[local] in source file ~c[axioms.lisp].) Thanks to Serita Nelesen for bringing this issue to our attention. ACL2 has, for some time, printed a space in the event summary after the open parenthesis for a ~ilc[defthm] event, in order to ease backward searching for the original form, for example ~c[(defthm bar ...)]: ~bv[] Form: ( DEFTHM BAR ...) ~ev[] The intention was that this extra space should be printed for every event form; but it was missing in some cases, for example, for ~ilc[verify-guards]. This has been fixed. In analogy to ~ilc[include-book], now ~ilc[ld] takes the (optional) keyword argument ~c[:dir]. Thanks to Jared Davis for providing an implementation of this feature and to Eric Smith and Jeff Marshall for requesting this feature. We fixed a bug in ~ilc[include-book] that could cause an error when redefinition is on, for example: ~bv[] (set-ld-redefinition-action '(:warn! . :overwrite) state) (include-book \"/u/acl2/books/arithmetic/top\") ~ev[] The behavior of ~ilc[include-book] now matches the documentation: handling of compiled files for uncertified ~il[books] will follow the same rules as for certified books. In particular, if you create an object file in raw Lisp for some book, then including that book will load that object file. Thanks to Jared Davis for bringing this issue to our attention. New documentation explains the interaction of redefinition and redundancy. ~l[redundant-events] ~-[] the ``Note About Unfortunate Redundancies'' is new. Thanks to Grant Passmore for providing examples that led us to write this additional documentation. Solutions to exercises in ``How To Prove Theorems Formally'' (~url[http://www.cs.utexas.edu/users/moore/publications/how-to-prove-thms]) are now available in distributed book ~c[books/misc/how-to-prove-thms.lisp]. Also in that directory may be found a new book ~c[hanoi.lisp] that contains a solution to the Towers of Hanoi problem. ~/~/") (deflabel note-2-9-3-ppr-change :doc ":Doc-Section note-2-9-3 change in pretty-printing for ACL2 Version_2.9.3~/ We have improved pretty-printing in ACL2 Version_2.9.3 to handle keywords a little differently. To see a discussion of the basics of this change, ~pl[note-2-9-3]. In this note we describe it in considerable detail.~/ Those who wish to understand the ACL2 pretty-printer's implementation can now find considerably more comments on it in the source code. In this note, we do not focus on the implementation. Rather, we motivate the change and show how the improved prettyprinter performs. Why do we want better keyword handling? Imagine a macro that builds a new state from an old state by changing the values in the affected fields, leaving everything else unchanged. One could write ~bv[] (modify th s :key1 val1 :key2 val2 :key3 val3) ~ev[] where the three keys identify fields in the state. To make it easier to read new concrete states, we may have a function that prints them ``relative'' to a given base state, expressing the new state as a modification of the given base state. So we may find ourselves prettyprinting modify forms like that above. The previous prettyprinter will sometimes print the form above as follows. ~bv[] (modify th s :key1 val1 :key2 val2 :key3 val3) ~ev[] This can be unpleasant to read, because of the way ~c[:key1] and ~c[val1] are separated. Here is an example of the old prettyprinter and the new one, both printing an expression from the ACL2 source code in a width of 40: ~bv[] Old: (ADD-TO-TAG-TREE 'ASSUMPTION (MAKE ASSUMPTION :TYPE-ALIST TYPE-ALIST :TERM TERM :REWRITTENP REWRITTENP :IMMEDIATEP IMMEDIATEP :ASSUMNOTES (LIST (MAKE ASSUMNOTE :CL-ID NIL :RUNE RUNE :TARGET TARGET))) TTREE) New: (ADD-TO-TAG-TREE 'ASSUMPTION (MAKE ASSUMPTION :TYPE-ALIST TYPE-ALIST :TERM TERM :REWRITTENP REWRITTENP :IMMEDIATEP IMMEDIATEP :ASSUMNOTES (LIST (MAKE ASSUMNOTE :CL-ID NIL :RUNE RUNE :TARGET TARGET))) TTREE) ~ev[] Basically the change we made forces the prettyprinter to print each ~c[:key] on a new line unless they all fit on a single line. So we would now get either ~bv[] (modify th s :key1 val1 :key2 :val2 :key3 val3) ~ev[] or ~bv[] (modify th s :key1 val1 :key2 val2 :key3 val3) ~ev[] Furthermore, we fixed it so that if ~c[val1] (say) is a big s-expression we may still print it on the same line as its key. The old prettyprinter enforced the rule that if you wanted to print ~c[(foo a b)] and ~c[b] gets broken up into several lines, then it has to start on a new line. Thus, we'd never print ~bv[] (foo a (bbb (mum x))) ~ev[] but would print instead ~bv[] (foo a (bbb (mum x))) ~ev[] Now, if a is a keyword, we can print the first way. So here are some nice examples of prettyprinted keyword forms. All of these are printed for a page of width 40. ~bv[] <-- 40 chars -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (MODIFY TH S :KEY1 V1 :KEY2 V2) (MODIFY TH S :KEY1 V1 :KEY2 V2 :KEY3 V3) (MODIFY TH S1 ; Because of the extra char :KEY1 V1 ; in S1 the flat size exceeds :KEY2 V2 ; 40 and we break it. :KEY3 V3) ~ev[] The old ppr would have printed this as: ~bv[] (MODIFY TH S1 :KEY1 V1 :KEY2 V2 :KEY3 V3) ~ev[] Returning to new examples: ~bv[] <-- 40 chars -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (MODIFY TH S :KEY1 (IF (IF X Y Z) AAAA BBBB) :KEY2 VAL2 :KEY3 VAL3) ~ev[] Now we extend ~c[AAAA] and ~c[BBBB] by one char each, so it would overflow the right margin if printed as above, and we get: ~bv[] <-- 40 chars -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (MODIFY TH S :KEY1 (IF (IF X Y Z) AAAAX BBBBX) :KEY2 VAL2 :KEY3 VAL3) ~ev[] If we make these names even longer we force the value off the line containing ~c[:key1]: ~bv[] (MODIFY TH S :KEY1 (IF (IF X Y Z) AAAAXXXXX BBBBXXXXX) :KEY2 VAL2 :KEY3 VAL3) ~ev[] Here are some examples from the ACL2 source code, printed in 40 characters: ~bv[] (DEFTHM ALPHORDER-ANTI-SYMMETRIC (IMPLIES (AND (NOT (CONSP X)) (NOT (CONSP Y)) (ALPHORDER X Y) (ALPHORDER Y X)) (EQUAL X Y)) :HINTS ((\"Goal\" :IN-THEORY (UNION-THEORIES '(STRING< SYMBOL-<) (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY)) :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X) (S2 Y)) (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y)) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C X))))) :RULE-CLASSES ((:FORWARD-CHAINING :COROLLARY (IMPLIES (AND (ALPHORDER X Y) (NOT (CONSP X)) (NOT (CONSP Y))) (IFF (ALPHORDER Y X) (EQUAL X Y))) :HINTS ((\"Goal\" :IN-THEORY (DISABLE ALPHORDER)))))) ~ev[] Here is that same one, printed in a width of 60. ~bv[] (DEFTHM ALPHORDER-ANTI-SYMMETRIC (IMPLIES (AND (NOT (CONSP X)) (NOT (CONSP Y)) (ALPHORDER X Y) (ALPHORDER Y X)) (EQUAL X Y)) :HINTS ((\"Goal\" :IN-THEORY (UNION-THEORIES '(STRING< SYMBOL-<) (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY)) :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X) (S2 Y)) (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y)) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C X))))) :RULE-CLASSES ((:FORWARD-CHAINING :COROLLARY (IMPLIES (AND (ALPHORDER X Y) (NOT (CONSP X)) (NOT (CONSP Y))) (IFF (ALPHORDER Y X) (EQUAL X Y))) :HINTS ((\"Goal\" :IN-THEORY (DISABLE ALPHORDER)))))) ~ev[] Just for comparison, here is the above printed in 60 columns by the old prettyprinter. ~bv[] (DEFTHM ALPHORDER-ANTI-SYMMETRIC (IMPLIES (AND (NOT (CONSP X)) (NOT (CONSP Y)) (ALPHORDER X Y) (ALPHORDER Y X)) (EQUAL X Y)) :HINTS ((\"Goal\" :IN-THEORY (UNION-THEORIES '(STRING< SYMBOL-<) (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY)) :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X) (S2 Y)) (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y)) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C X))))) :RULE-CLASSES ((:FORWARD-CHAINING :COROLLARY (IMPLIES (AND (ALPHORDER X Y) (NOT (CONSP X)) (NOT (CONSP Y))) (IFF (ALPHORDER Y X) (EQUAL X Y))) :HINTS ((\"Goal\" :IN-THEORY (DISABLE ALPHORDER)))))) ~ev[] Of course, given that you cannot tell for sure whether the keywords you're seeing are part of a keyword/value parameter list or part of some constant containing random keywords, the prettyprinter can't solve the problem perfectly. We just tried to make it work nicely on well-formed keyword/value parameter lists. For example, here is a form printed by the each prettyprinter. ~bv[] Old: (MEMBER X '(:MONDAY :MON :TUESDAY :TUES :WEDNESDAY :WED :THURSDAY :THURS :FRIDAY :FRI :SATURDAY :SAT :SUNDAY :SUN)) New: (MEMBER X '(:MONDAY :MON :TUESDAY :TUES :WEDNESDAY :WED :THURSDAY :THURS :FRIDAY :FRI :SATURDAY :SAT :SUNDAY :SUN)) ~ev[] The new way is not how one would print it by hand! But then, neither is the old way.~/") (deflabel note-2-9-4 ; The soundness bug "due to inadequate checking of :meta rules" is documented ; in chk-acceptable-rules. ; Things that seem too minor to mention: ; Added a force-output call to print-prompt by way of new macro ; with-output-forced. This seemed necessary for SBCL. ; In load-acl2, replaced (eval '(setq state *the-live-state*)) with what we ; used to do only for cmulisp, namely (set 'state *the-live-state*). ; Made small fix to translate11's handling of synp, to avoid requiring ; quote-normal form but to insist that the QUOTE calls have one argument. ; The error message for guard violations was cleaned up: added a sentence for ; new users, added ev-fncall-guard-er-msg for code sharing, and added initial ; part for new users to :doc set-guard-checking. ; To see the change mentioned below for set-let*-abstractionp, try this: ; (defpkg "FOO" (union-eq *acl2-exports* ; *common-lisp-symbols-from-main-lisp-package*)) ; (in-package "FOO") ; (defstub foo (x) t) ; (defstub bar (x) t) ; :set-let*-abstractionp t ; (thm (equal (foo (append x y)) (bar (append x y)))) ; Added hard! severity value for er (not advertised). ; Replaced conjuncts-of by flatten-ands-in-lit. ; Modified books/workshops/2004/sumners-ray/support/Makefile to avoid having to ; remake success.txt when that is unnecessary. ; Regarding this item below: ; ; We fixed an inefficiency that could cause an ~ilc[ld] command to seem to hang ; at its conclusion. Thanks to Sandip Ray for pointing out this problem. ; ; Here is a way to see the problem. The change was to maybe-add-command-landmark. ; (include-book "arithmetic/top-with-meta" :dir :system) ; (ld '((u) (include-book "arithmetic/top-with-meta" :dir :system))) ; Added (type (signed-byte 29) col) declarations for parameter col in fmt0 and ; fmt1. ; Modified many warnings to say something like "A rule generated by FOO" ; instead of "The rule generated by FOO", in response to an email from John ; Cowles. This may not really deal with the objection that it appears that ; more than one copy of the same warning can appear for a single event (for the ; case of more than one rule), but at least the new wording is more accurate, ; and much more of a change could require considerable effort. :doc ":Doc-Section release-notes ACL2 Version 2.9.4 (February, 2006) Notes~/ Also ~pl[note-2-9-1], ~pl[note-2-9-2], and ~pl[note-2-9-3] for other changes since the last non-incremental release (Version_2.9). A soundness bug has been fixed that was due to inadequate checking of ~c[:]~ilc[meta] rules in the presence of ~ilc[local] ~il[events]. Specifically, a ~c[local] ~ilc[defevaluator] event is insufficient for supporting a ~c[:meta] rule (an example is shown in source function ~c[chk-acceptable-rules]). Thanks to Dave Greve and Jared Davis for bringing this bug to our attention, by sending a proof of ~c[nil] that exploited this bug. The fix is to check legality of ~c[:meta] rules even when skipping proofs during an ~ilc[include-book] event or the second pass of an ~ilc[encapsulate] event. Fixed problem with parallel make for workshop books by adding a dependency line to ~c[books/workshops/2003/Makefile]. Default hints (~pl[set-default-hints]) no longer prevent the use of ~c[:INSTRUCTIONS] (~pl[proof-checker]). Thanks to Jared Davis for pointing out this problem. New functions ~ilc[remove-eq] and ~ilc[remove-equal] have been defined, in analogy to ~ilc[remove]. These two symbols have also been added to ~c[*acl2-exports*]. Thanks to David Rager for pointing out that ~c[remove-equal] was missing. Moreover, the definitions of ~c[delete1-eq] and ~c[delete1-equal] have been eliminated. Function ~c[remove1-eq], now in ~c[:]~ilc[logic] mode in source file ~c[axioms.lisp], serves in place of ~c[delete1-eq], with corresponding new function definitions for ~ilc[remove1] and ~ilc[remove1-equal]. The symbol ~ilc[assert$] has been added to ~c[*acl2-exports*]. Thanks to Jared Davis for the suggestion. Added SBCL support. Thanks to Juho Snellman for significant assistance with the port. Thanks to Bob Boyer for suggesting the use of feature ~c[:acl2-mv-as-values] with SBCL, which can allow thread-level parallelism in the underlying lisp; we have done so when feature ~c[:sb-thread] is present. We have continued to incorporate suggestions for wording improvements in documentation and error messages. Thanks to all who send these suggestions, especially to Eric Smith, who has suggested the vast majority of them. Made a small improvement to errors and warnings caused on behalf of ~il[set-enforce-redundancy], to indicate when an event of the same name already exists. Fixed a bug in ~c[books/misc/rtl-untranslate.lisp] that was causing a guard violation when adding a new entry for an existing key. Fixed a bug in translation to internal form that caused ~ilc[defun-sk] and ~ilc[defchoose] to have difficulties handling ignored variables in ~ilc[let] forms. Thanks to Sandip Ray to bringing this issue to our attention with a helpful example. The form ~c[(push :acl2-mv-as-values *features*)] has been added in source file ~c[acl2-init.lisp] for SBCL and OpenMCL only, in order to support parallel execution (looking to the future...). Default-hints (~pl[set-default-hints]) were being ignored inside the ~il[proof-checker], but no longer. Thanks to John Erickson for bringing this problem to our attention and providing a simple example of it. Modified the ~c[TAGS] ~c[\"make\"] target (specifically, function ~c[make-tags]) so that it is gracefully skipped if the ~c[etags] program is not found. Thanks to David Rager for pointing out this issue. Sandip Ray has re-worked the supporting materials for his ACL2 Workshop 2003 talk (originally with John Matthews and Mark Tuttle), to run in a few minutes. The result is in ~c[workshops/2003/ray-matthews-tuttle/support/] and is included in the full ACL2 regression suite. Thanks, Sandip. Debian releases of ACL2 had created superfluous ~c[.cert.final] files when certifying books. This has been fixed; thanks to Jared Davis for noticing this problem. Jared Davis has pointed out that ``If you add a ~c[:backchain-limit-lst 0] to a rewrite rule whose hypotheses are all forced, then ACL2 `really assumes them' without trying to relieve them right there through rewriting.'' Relevant documentation has been added for ~c[:backchain-limit-lst]; ~pl[rule-classes]. A new version of the rtl library has been included in ~c[books/rtl/rel5/]. Thanks to David Russinoff for contributing hand proofs for the new lemmas, and to Matt Kaufmann for carrying out their mechanization. Fixed a bug in ~ilc[save-exec] that was failing to set the initial ~c[cbd] according to the current directory when starting up ACL2. Thanks to Camm Maguire for bringing our attention to this problem. Variables introduced during ~c[let*] abstraction are now in the current package. Thanks to Jared Davis for suggesting such a change. ~l[set-let*-abstractionp]. It is now allowed for two definitions to be considered the same from the standpoint of redundancy (~pl[redundant-events]) when one specifies a ~c[:]~ilc[guard] of ~c[t] and the other has no explicit ~c[:guard] (hence, the guard is implicitly ~c[t]). Thanks to Jared Davis for bringing this issue to our attention. (For users of ~c[emacs/emacs-acl2.el]) There have been a few enhancements to distributed file ~c[emacs/emacs-acl2. el] (skip this paragraph if you don't use that file): ~bq[] o ~c[Control-t q] continues to compare windows ignoring whitespace, but now, a prefix argument can be given to control case is also ignored (ignore case if positive, else use case). o ~c[Control-t Control-l] has been defined to be similar to ~c[Control-t l], except that proofs are skipped and output is suppressed. o ~c[Control-t u] has been defined to print, to the shell buffer, a ~c[:]~ilc[ubt!] form for the command containing the cursor. o Control-t Control-f buries the current buffer. o ~c[Meta-x new-shell] now puts the new shell buffer in ~c[shell-mode] (thanks to David Rager for noticing this issue).~eq[] Linear arithmetic has been modified so that we do not generate the equality ~c[(equal term1 term2)] from the pair of inequalities ~c[(<= term1 term2)] and ~c[(<= term2 term1)] in the case that we would have to ~ilc[force] both ~c[term1] and ~c[term2] to be ~ilc[acl2-numberp]s. Thanks to Dave Greve for providing a motivating example and to Robert Krug for providing a fix. The event ~ilc[delete-include-book-dir] had not been allowed inside ~il[books] and ~ilc[encapsulate] forms. This was an oversight, and has been fixed. Sandip Ray has contributed a new library of books to support proofs of partial and total correctness of sequential programs based on assertional reasoning, in ~c[books/symbolic/]. This work is based on the paper J. Matthews, J S. Moore, S. Ray, and D. Vroon, ``A Symbolic Simulation Approach to Assertional Program Verification,'' currently in draft form. In particular, the books include the macro ~c[defsimulate], which automatically transforms inductive assertion proofs of correctness of sequential programs to the corresponding interpreter proofs. See the ~c[README] in that directory. We have changed the implementation of ~c[:dir :system] for ~ilc[ld] and ~ilc[include-book]. This change will not affect you if you build an ACL2 executable in the normal manner, leaving in place the ~c[books/] subdirectory of the source directory; nor will it affect you if you download a GCL Debian binary distribution. The change is that if environment variable ~c[ACL2_SYSTEM_BOOKS] is set, then it specifies the distributed books directory, i.e., the directory determined by ~c[:dir :system]. You may find it convenient to set this variable in your ACL2 script file (typically, ~c[saved_acl2]). If it is set when you build ACL2, the generated script for running ACL2 will begin by setting ~c[ACL2_SYSTEM_BOOKS] to that value. Thanks to various people who have discussed this issue, in particular Jared Davis who sent an email suggesting consideration of the use of an environment variable, and to Eric Smith who helped construct this paragraph. (Note that this use of ~c[ACL2_SYSTEM_BOOKS] replaces the use of ~c[ACL2_SRC_BOOKS] described previously; ~pl[note-2-9-1].) ACL2 now automatically deletes files ~c[TMP*.lisp] created during the build process and created by ~c[:]~ilc[comp]. If you want these to be saved, evaluate ~c[(assign keep-tmp-files t)] in the ACL2 loop or in raw Lisp. The ~c[clean] target for the standard `make' process for certifying books (~pl[books-certification-classic]) will however delete all files ~c[TMP*.*]. The ~c[TMP] files discussed just above now generally include the current process ID in their names, e.g., ~c[TMP@16388@1.lisp] instead of ~c[TMP1.lisp]. Thanks to Bob Boyer for suggesting this measure, which will reduce the possibility that two different processes will attempt to access the same temporary file. Now, ~c[:]~ilc[pe] will print the information formerly printed by ~c[:pe!], slightly enhanced to work for logical names that are strings, not just symbols. Thanks to Warren Hunt for leading us to this change by suggesting that ~c[:pe nth] print the definition of ~ilc[nth]. We eliminated spurious warnings that could occur in raw mode in OpenMCL or CMUCL when ~il[stobj]s are present. We thank Juho Snellman for pointing out the relevant bug and appropriate fix. ~c[Mfc-rw] now takes a third argument that can specify an arbitrary known equivalence relation; ~pl[extended-metafunctions]. Thanks to Dave Greve for discussions suggesting this improvement. A small modification to a symbol-reading function allows documentation string processing on Windows systems that use CR/LF for line breaks. Thanks to William Cook for bringing this issue to our attention. The documentation has been improved on how to control the printing of ACL2 terms. ~l[user-defined-functions-table]. Thanks to Sandip Ray for asking a question that led to the example presented there. We fixed an inefficiency that could cause an ~ilc[ld] command to seem to hang at its conclusion. Thanks to Sandip Ray for pointing out this problem. We checked that ACL2 runs under LispWorks 4.4.5, and have inhibited redefinition warnings. Two changes have been made on behalf of congruence-based reasoning. Thanks to Dave Greve for examples and discussions that have led to these changes, and to Eric Smith and Vernon Austel, who also sent relevant examples. ~bq[] o When a call of the new unary function ~ilc[double-rewrite] is encountered by the rewriter, its argument will be rewritten twice. This solves certain problems encountered in congruence-based rewriting. Warnings for ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear] rules will suggest when calls of ~ilc[double-rewrite] on variables in hypotheses are likely to be a good idea. ~l[double-rewrite]. o Hypotheses of the form ~c[(equiv var (double-rewrite term))], where ~c[equiv] is a known ~il[equivalence] relation and ~c[var] is a free variable (~pl[free-variables]), will bind ~c[var] to the result of rewriting ~c[term] twice. Previously, hypotheses of the form ~c[(equal var term)] would bind a free variable ~c[var], but the call had to be of ~c[equal] rather than of an arbitrary known equivalence relation.~eq[] The following improvements were made in support of ACL2 on top of OpenMCL. ~bq[] o New versions of OpenMCL that do not have ~c[:mcl] in Lisp variable ~c[*features*] will now work with ACL2. Thanks to David Rager for bringing this issue to our attention. o Added support for OpenMCL 1.0 for 64-bit DarwinPPC/MacOS X, thanks to Robert Krug. o Fixed tracing in OpenMCL so that the level is reset to 1 even if there has been an abort. o Added support in OpenMCL for ~c[WET]. o Incorporated suggestions from Gary Byers for printing the ``Welcome to OpenMCL'' prompt before initially entering the ACL2 loop and, and for setting useful environment variable ~c[CCL_DEFAULT_DIRECTORY] in the ACL2 script.~eq[] Fixed a long-standing bug in forward-chaining, where variable-free hypotheses were being evaluated even if the ~il[executable-counterpart]s of their function symbols had been disabled. Thanks to Eric Smith for bringing this bug to our attention by sending a simple example that exhibited the problem. Improved reporting by the ~il[break-rewrite] utility upon failure to relieve hypotheses in the presence of free variables, so that information is shown about the attempting bindings. ~l[free-variables-examples-rewrite]. Thanks to Eric Smith for requesting this improvement. Also improved the ~il[break-rewrite] loop so that terms, in particular from unifying substitutions, are printed without hiding subterms by default. The user can control such hiding (``evisceration''); see :DOC ~c[set-brr-term-evisc-tuple]. A new directory ~c[books/defexec/] contains books that illustrate the use of ~ilc[mbe] and ~ilc[defexec]. Thanks to the contributors of those books (see the ~c[README] file in that directory). The directories ~c[books/rtl/rel2] and ~c[books/rtl/rel3] are no longer distributed. They are still available by email request. (Subdirectory ~c[rel1/] supports some of the optional ~c[workshop/] books, so it is still distributed.) Added book ~c[books/misc/sticky-disable.lisp] to manage ~il[theories] that might otherwise be modified adversely by ~ilc[include-book]. Thanks to Ray Richards for a query that led to our development of this tool. The commands ~c[(exit)] and ~c[(quit)] may now be used to quit ACL2 and Lisp completely; in fact they macroexpand to calls of the same function as does ~ilc[good-bye] (which is now a macro). Thanks to Jared Davis for suggesting the new aliases. (OpenMCL-only comment:) These all work for OpenMCL even inside the ACL2 loop. The macro ~c[wet] now hides structure by default on large expressions. However, a new optional argument controls this behavior, for example avoiding such hiding if that argument is ~c[nil]. Thanks to Hanbing Liu for pointing out that ~c[wet] was not helpful for very large terms. We have fixed a bug in the forward-chaining mechanism that, very rarely, could cause a proof to be aborted needlessly with an obscure error message. Thanks to Jared Davis for sending us an example that evoked this bug. Fixed a bug that was causing proof output on behalf of ~c[:functional-instance] to be confusing, because it failed to mention that the number of constraints may be different from the number of subgoals generated. Thanks to Robert Krug for pointing out this confusing output. The fix also causes the reporting of rules used when silently simplifying the constraints to create the subgoals. Fixed a bug in handling of leading ~c[./] in pathnames, as in: ~c[(include-book \"./foo\")]. Thanks to Jared Davis for bringing this bug to our attention. Made a small fix for handling of free variables of ~c[:]~il[forward-chaining] rules, which had erroneously acted as though a hypothesis ~c[(equal var term)] can bind the variable ~c[var]. A small change has been made for ~c[:]~ilc[type-prescription] rules for hypotheses of the form ~c[(equal var term)], where ~c[var] is a free variable and no variable of ~c[term] is free (~pl[free-variables]). As with ~c[:]~ilc[rewrite] and ~c[:]~ilc[linear] rules, we now bind ~c[var] to ~c[term] even if ~c[(equal u term)] happens to be known in the current context for some term ~c[u]. Also as with ~c[:rewrite] and ~c[:linear] rules, similar handling is given to hypotheses ~c[(equiv var (double-rewrite term))] where ~c[equiv] is a known ~il[equivalence] relation. We changed the handling of free variables in hypotheses of ~c[:]~ilc[rewrite] rules being handled by the ~il[proof-checker]'s ~c[rewrite] (~c[r]) command, in complete analogy to the change described just above for ~c[:]~ilc[type-prescription] rules. The installation instructions have been updated for obtaining GCL on a Macintosh. Thanks to Robert Krug for supplying this information and to Camm Maguire for simplifying the process by eliminating the ~c[gettext] dependency. The macro ~ilc[comp] is now an event, so it may be placed in ~il[books]. Previously, a ~ilc[save-exec] call could fail because of file permission issues, yet ACL2 (and the underlying Lisp) would quit anyhow. This has been fixed. Thanks to Peter Dillinger for bringing this problem to our attention. Jared Davis, with assistance from David Rager, has updated his ordered sets library, ~c[books/finite-set-theory/osets/]. See file ~c[CHANGES.html] in that directory. A new function, ~ilc[reset-kill-ring], has been provided for the rare user who encounters memory limitations. ~l[reset-kill-ring]. ~/~/") (deflabel note-2-9-5 ; Things that seem too minor to mention: ; Modified translate11 to complain about LOCAL in code. Before this fix, the ; definition ; (defun foo (x state) (local (value x))) ; caused (foo 3 state) to cause a hard error in getprop. ; Removed "time nice" from books/defexec/dag-unification/Makefile (doesn't work ; in some environments, as I recall). ; Added targets d64fsl and all-d64fsl to books/Makefile*, to support ; compilation of already-certified books on 64-bit OpenMCL. ; Added initial bindings of acl2-raw-mode-p and raw-mode-restore-lst to nil (in ; *initial-global-table*), as suggested by Peter Dillinger. ; Fixed warning stack issue with print-summary, which caused new ; theory-invariant implementation to avoid popping warning stacks because of ; with-output calls. ; Inlined call of equivalence-relation-to-geneqv with something more ; appropriate, and elminated equivalence-relation-to-geneqv. ; Changed rewrite-with-lemmas so that :expand hints (from user or from ; induction) are always followed, without the being-openedp call that used to ; be there. ; The change for bodies (new 'def-bodies property) causes the following ; to use the latest definition for the body (e.g., affecting :expand ; hints). ; - non-rec-defun, called by deref for nu-rewriter ; - recursivep, called in rewrite-with-lemma and bdd-rules-alist1 ; - expand-and-or, used by preprocess ; - non-recursive-fnnames, used only in warnings ; - proof-checker's expand command (hence also x and x-dumb commands) ; - induction heuristics that use controller-alist ; Modified extend-type-alist to add check for (not (fquotep term)), which had ; been assumed but was not necessarily true because of the call of ; extend-type-alist from extend-type-alist-with-bindings. ; Updated *current-acl2-world-key-ordering*, and comments about it. ; Replaced the use of proclaim by the use of declaim (thanks to Bob Boyer for ; the suggestion to consider this). ; Fixed typo in uncovered-equivs-alist, 'f instead of 'if, that could cause ; incorrect double-rewrite warnings. But went beyond this and gave more ; appropriate special handling for 'if. ; Fixed obscure bug in maybe-push-undo-stack, which could happen with: ; ; :redef! ; (defun exit-boot-strap-mode () t) ; ; The problem was that we assumed that a *1* function is defined when its raw ; Lisp counterpart is defined. ; Added missing dependencies in books/ihs/Makefile. ; Changed chk-embedded-event-form so that it returns the expansion rather than ; the original form, in support of the implementation of make-event. ; Eliminated get-check-sums-lst and check-sum-file (dead code). ; Acl2-compile-file now loads the compiled file. ; Simplified *compiled-file-extension* according to a suggestion from Gary ; Byers. ; With Robert Krug's help, including arithmetic-3/bind-free/top will now print ; a message about turning on non-linear. ; Made slight improvement in ev-fncall-guard-er-msg so that we don't suggest ; set-guard-checking nil when the problem is safe-mode or stobjs. ; Documentation and comments have been changed, for the most part, so that the ; word ``multiple'' is used correctly. Thanks to David Russinoff for pointing ; out this issue. ; Fixed getpid$ for clisp to use 'system::process-id rather than 'process-id. ; This puts the process id into the name of TMP* files produced by :comp t, ; which hadn't been done for clisp because of the getpid$ bug. ; Changed definition of macro state-global-let* (specifically, changed ; definition of supporting function state-global-let*-cleanup) to produce much ; more compact code for large numbers of bindings as in ; protect-system-state-globals. Without that change, lispworks ; reported: ; **++++ Error in ACL2::PROTECTED-EVAL-WITH-PROOFS: ; Function size 87365 is too large. ; Eliminated compiler note "Ignoring free ignore declaration" for mbe, for ; CMUCL and SBCL (had already done so for OpenMCL). ; Fixed a bug in make-include-books-absolute that caused an "Implementation ; error" when with-output occurs in a progn with an include-book in the ; certification world, as in: ; (progn (include-book "cowles/acl2-asg" :dir :system) ; (with-output :off summary (defun abc (x) x))) ; Added a call to the garbage collector before saving in Allegro CL, CMUCL, ; SBCL, CLISP, and OpenMCL. (There was already such a call in GCL and ; LispWorks.) We saw a little performance increase and significant shrinkage ; of the saved image when we did this for Allegro CL. :doc ":Doc-Section release-notes Changes in Version 3.0 since Version 2.9.4~/ Fixed a bug in ~ilc[cw-gstack] that was causing a hard error when attempting to report on a forced assumption. Thanks to Jared Davis for pointing this out and sending an example that helped us to determine a fix. Added ~ilc[set-backchain-limit] to the set of legal ~il[events] that can be placed in ~ilc[encapsulate] forms and ~il[books]. Thanks to John Cowles for bringing this issue to our attention. Fixed a bug that broke ~c[wet]. Thanks to David Rager for bringing this bug to our attention. Guard verification now evaluates ground subexpressions (those with no free variables) when computing the guard conjecture for the body of a function. Thanks to Jared Davis for useful conversations leading to this change. ~l[verify-guards], in particular its ``Note on computation of guard conjectures and evaluation'' near the end of that topic, for more details. Added a warning when a ~ilc[theory-invariant] is redefined. Thanks to Jared Davis for suggesting a warning in this case and providing an informative example. Also, ~ilc[theory-invariant]s are now maintained more completely, as they are checked at the end of every event except for events executed on behalf of an ~ilc[include-book] or the second pass of an ~ilc[encapsulate]. Fixed the handling of runic designators to match their specification (~pl[theories]), so that disabling the name of a ~ilc[defthm] event ~ilc[disable]s all rules generated for that event. (For those who do numerous builds using feature ~c[:acl2-mv-as-values], currently only OpenMCL and multi-threaded SBCL by default:) You can speed up builds by adding the following parameter to `make', under conditions described in ~c[GNUmakefile]: ~c[USE_ACL2_PROCLAIMS=:REUSE]. Arranged that traced functions (~pl[trace$]) are automatically untraced when events are undone (for example ~pl[ubt]), at least for most underlying Common Lisp implementations. The macro ~ilc[defun-sk] now creates non-executable functions, which allows ~ilc[stobj]s to be used where they had previously been prohibited. More generally, the user now has control over ~ilc[declare] forms to be used by the underlying ~ilc[defun]'d function; ~pl[defun-sk]. Thanks to Sandip Ray for pointing out the need for such a modification. ~c[:]~ilc[Definition] rules are now treated, at least by default, as truly first-class definitions. In particular, ~c[:expand] ~il[hints] use the latest ~c[:]~ilc[definition] rule by default. You may specify ~c[:install-body nil] to get the previous behavior of ~c[:definition] rules; ~l[definition], and you may choose a previously-installed ~c[:definition] rule to provide the current body; ~pl[set-body]. Also ~pl[rule-classes] for details of the ~c[:install-body] field, and ~pl[hints] to see a new ~c[:with] directive for controlling expansion. The ~c[:with] directive for ~c[:expand] hints can even direct the use of a ~c[:]~ilc[rewrite] rule for expansion! Thanks to various people, including Sandip Ray and Rob Sumners, for discussions on the issue of the applicability of ~c[:definition] rules for ~c[:expand] ~il[hints]. ~il[Constraint]s for functional instantiation now use the original definition rather than a simplified (``normalized'') version of it. Fixed a bug that caused the prompt to stay the same when guard-checking is off (~pl[set-guard-checking]) and raw-mode is changed (~pl[set-raw-mode]). Lemma names in directory ~c[books/ordinals] have been changed by replacing ~c[/\\] with ~c[&] and replacing ~c[\\/] with ~c[V]. We made this change because backslash is an escape character and hence disappears unless it is itself escaped. Fixed ~il[proof-tree] output so that failed non-proof events do not cause the proof-tree to be re-printed. Thus for example, if you have already advanced the checkpoint marker, it will not be reset by subequent failed non-proof events. Thanks to Pete Manolios and Peter Dillinger for bringing this bug to our attention. Fixed a bug that was preventing the printing of ~il[stobj] fields as constants instead of numbers in certain cases. (Note that this bug only affected printing, not soundness.) Thanks to Eric Smith for bringing this problem to our attention and providing the following example (which now works fine). ~bv[] (defstobj st fld1 fld2) (in-theory (disable update-nth)) (defund run (st) (declare (xargs :stobjs (st))) ;adding this didn't seem to help.. st) ;works great; *fld1* prints as *fld1* (thm (equal (update-nth *fld1* 'abc st) (car (cons x y)))) ;*fld1* gets printed as 0, presumably because the call to run intervenes. (thm (equal (update-nth *fld1* 'abc (run st)) (car (cons x y)))) ~ev[] The macro ~ilc[progn] now allows the use of macros defined within its bodies even when at the event level, as illustrated by the following example. ~bv[] (progn (defmacro my-defun (&rest args) `(defun ,@args)) (my-defun g (x) x)) ~ev[] Thanks to Anna Slobodova for bringing this issue to our attention. A related change is that all arguments of ~ilc[progn] must now be embedded event forms (~pl[embedded-event-form]), so use ~ilc[er-progn] instead if this is not the case. The change to ~ilc[progn] mentioned above also fixes a bug in handling ~il[local] events inside a ~ilc[progn] that is inside an ~ilc[encapsulate] or in a book. For example, the following form formerly caused an error. ~bv[] (encapsulate () (defun foo (x) x) (progn (local (defun bar (x) x)) (defun abc (x) x))) ~ev[] We fixed two bugs in ~c[:]~ilc[puff] and ~c[:]~ilc[puff*]. The first, brought to our attention by Eric Smith (who we thank), caused a cryptic error message when puffing a command with no subsidiary stored events; try, for example, ~c[(encapsulate () (value-triple 3))]. The second was due to a failure to restore the ~ilc[acl2-defaults-table]. Suppose for example that we have certified the book ~c[foo.lisp], which contains ~c[(]~ilc[program]~c[)] followed by some definitions and/or theorems. Now suppose we start ACL2 and execute the following. ~bv[] (include-book \"foo\") (defthm test-thm (equal x x) :rule-classes nil) ~ev[] If we now execute ~c[:]~ilc[puff]~c[ 1], ACL2 will roll back the world to before the ~ilc[include-book]; then ``puff'' the include-book, which will leave us in ~c[:]~ilc[program] mode; and finally skip re-execution of the ~ilc[defthm] because such ~il[events] are skipped in ~c[:]~ilc[program] mode. The fix is to re-install the ~ilc[acl2-defaults-table] immediately after the ~ilc[include-book] to its pre-~ilc[include-book] value. A new event, ~ilc[make-event], provides something like macros that take state. For example, one can use it to put tests into certified books, do proof search, and generate new function names. Many examples appear in directory ~c[books/make-event/]. ~l[make-event]. Thanks to Bob Boyer and Jared Davis for useful feedback and to Warren Hunt, David Rager, and Sandip Ray for helpful discussions leading to some of the examples in directory ~c[books/make-event/]. In support of ~ilc[make-event], which is described in the preceding paragraph, ~c[certify-book] has a new keyword argument, ~c[:save-expansion], that controls whether the result of expanding ~ilc[make-event] forms is written out to a file. ~l[certify-book]; and for a discussion of book expansion files, ~pl[make-event]. We fixed a soundness bug that did not correctly detect ~ilc[local] events. For example, the following event was admitted. ~bv[] (encapsulate () (local (encapsulate () (local (progn (program))) ; or, (local (with-output :off summary (program))) (set-irrelevant-formals-ok t) (defun foo (x) (declare (xargs :measure (acl2-count x))) (1+ (foo x))))) (defthm inconsistent nil :hints ((\"Goal\" :use foo)) :rule-classes nil)) ~ev[] A new value for ~il[guard] checking, ~c[:none], is now allowed. If you execute ~c[:]~ilc[set-guard-checking]~c[ :none], then no guard checking will take place (but raw Lisp code will not be executed in this case). As a result, you should never see a guard violation, even for calls of ~c[:]~c[program] mode functions. We thank Pete Manolios, who has long wanted this feature, and also Peter Dillinger, for asking for it. New documentation explains the interaction between the ~il[defun-mode] and the value supplied to ~c[:]~ilc[set-guard-checking]. ~l[guard-evaluation-table], ~pl[guard-evaluation-examples-script], and ~pl[guard-evaluation-examples-log]. In the course of adding the ~il[guard]-checking value ~c[:none] described in the paragraph above, we eliminated an optimization that eliminated guard checking for some recursive calls of ~c[:]~il[logic] mode mutually-recursive functions that have not had their guards verified. But we doubt that this change will be noticed by any users!) The ACL2 hyper-card has been enhanced, thanks to David Rager, with a listing of ``Useful EMACS Commands'' to match comments in ~c[emacs/emacs-acl2.el]. Users contributed books following the ~c[Readme.lsp] methodology: ~c[data-structures/memories] and ~c[unicode] (Jared Davis), ~c[proofstyles] (Sandip Ray and J Moore). Made some improvements to ~c[books/Makefile-generic] (a file discussed elsewhere; ~pl[books-certification-classic]). In particular, improved handling of ~c[.acl2] files for ~c[dependencies] target. (Only OpenMCL and, with feature ~c[:acl2-mv-as-values], GCL) Fixed a bug that was causing proclaiming to fail when definitions are submitted interactively. The default stack size has been increased for several lisps. (Very technical) A restriction has been weakened on the use of ~ilc[local] ~il[stobj]s under a call of an ACL2 evaluator (~c[trans-eval] or ~c[simple-translate-and-eval]). Now, the error can only take place for ~ilc[stobj] names that occur in the term being evaluated. Thanks to Erik Reeber for bringing this issue to our attention. The notion of ``ancestor'' has been changed slightly. This notion is used by extended metafunctions and ~il[break-rewrite] (~pl[extended-metafunctions] and ~pl[brr@]), and also with backchain limits (~pl[backchain-limit] and ~pl[set-backchain-limit]). Basically, each time a hypothesis is encountered during application of a ~il[rewrite] rule, that hypothesis is pushed (after instantiating and negating) onto the current list of ancestors before it is rewritten. However, hypotheses of the form ~c[(equal var term)], where ~c[var] is free (~pl[free-variables]), had not been included in the ancestors (similarly for ~c[(equiv var (double-rewrite term))] where ~c[equiv] is a known ~il[equivalence] relation). Now such ``binding hypotheses'' are included in a special way in ancestors data structures. In particular, ~c[(null (mfc-ancestors mfc))] will now be true if and only if the term being rewritten is part of the current goal as opposed to a hypothesis from a rule encountered during backchaining, even if that hypothesis is a binding hypothesis. Thanks to Dave Greve for bringing this issue to our attention. Termination and induction analysis now continue through both arguments of ~ilc[prog2$], not just the second. (Normally, the gathering up of ~ilc[if] tests stops at function calls; but it continued through the second argument of ~ilc[prog2$], and now it will continue through both arguments.) Thanks to Sol Swords for discussion leading to this change. The ACL2 distribution is now kept on the http server rather than the ftp server (but the home page has not been moved). Thanks to Robert Krug for letting us know that some ACL2 users have found it inconvenient to fetch ACL2 using ftp. The file ~c[books/README.html] has been renamed to ~c[books/Readme.html], since some browsers don't show the former in the directory listing. ~/~/") (deflabel note-3-0 ; See note-2-9-5 for some comments enumerating changes not in the :doc since ; v2-9-4. :doc ":Doc-Section release-notes ACL2 Version 3.0 (June, 2006) Notes~/ Please ~pl[note-2-9-5] for a description of changes since Version 2.9.4. These include the new ~ilc[make-event] feature, a soundness bug fix, an improvement for ~c[:expand] ~il[hints], evaluation in the logic by way of ~c[:]~ilc[set-guard-checking]~c[ :none], and many other improvements. More generally, there have been several incremental releases since Version 2.9: ~pl[note-2-9-1], ~pl[note-2-9-2], ~pl[note-2-9-3], ~pl[note-2-9-4], and ~pl[note-2-9-5]. A very few users have contributed books following the instructions on the web. We expect that when more contributions come in, we will give more attention to the question of how to organize the distributed and workshop books. For now, we have simply added the new contributions according to the old-style distribution methodology. ~/~/") (deflabel |NOTE-3-0(R)| :doc ":Doc-Section release-notes ACL2 Version 3.0(r) (June, 2006) Notes~/ No significant changes have been made since Version 2.9 for support of non-standard analysis in particular. ~/ Please also ~pl[note-3-0] for changes to Version 3.0 of ACL2. ~/ ") (deflabel note-3-0-1 ; Things that seem too minor to mention: ; Modified proclaiming to understand (declare (type fixnum ...)) for function ; arguments. ; Regarding the subsumption item below: We still do the old subsumption check ; for chk-evaluator and processing of :by hints, because the user has some idea ; in these cases of what is going on (plus, the former will probably always be ; fast). But we do the restricted check in the clause-set-subsumes call of ; chk-acceptable-equivalence-rule and (for induction) in ; some-pool-member-subsumes because those are kind of subtle and it's OK if ; they fail. ; The new requirement on the argument of satisfies resulted in an extra wrld ; argument for translate-declaration-to-guard (and other source functions), ; which is used in several books. ; Took suggestion from Bob Boyer to arrange to throw out extra values returned ; by intern, for efficiency (see intern-in-package-of-symbol and comments "See ; comment in intern-in-package-of-symbol"). ; Fixed *initial-global-table* to satisfy ordered-symbol-alistp, and added a ; check for this just after the definition of *initial-global-table*. ; The improvement in handling of theories included an extra argument in ; set-difference-theories-fn and in union-theories-fn, lst1-known-to-be-runic. ; Users who are sophisticated and brave enough to use these unadvertised ; internal functions should be able to figure out this change, so we choose not ; to confuse other users by mentioning that in this :doc topic. ; Regarding the item below on hard lisp error and safe mode: We added a number ; of function/macro symbols to the list in *oneify-primitives* and to avoid ; oneifying, in order to support the use of safe-mode with make-event (which ; however we have abandoned for now because it seems difficult to ensure that ; we are dealing properly with *1* functions with corresponding hand-coded raw ; Lisp definitions). We also fixed bugs discovered as we attempted to certify ; the books/make-event books: ; - state-global-let*-get-globals to use f-boundp-global in place of ; boundp-global; ; - the call of fmt1 in print-warnings-summary; ; - the call (eq new-type-alist type-alist) in type-alist-clause-finish; and ; - the calls of eq in changed-pot-vars. ; Oneify-cltl-code has been slightly optimized for the case that ; 'guard-checking-on is :none, by eliminating some code that has no effect. ; The event comp now compiles properly for lists of functions even when some ; are in the boot-strap world. In general, the code for ; compile-uncompiled-defuns and compile-uncompiled-*1*-defuns was cleaned up ; considerably, including the removal of proclaiming since add-trip is ; responsible for that. ; Deleted now-dead code collect-ideal-user-defuns and ; collect-ideal-user-defuns1. ; Among the "miscellaneous efficiency improvements not listed above" is an ; improvement to assume-true-false, which now takes an ignore argument that ; avoids some computation of the true-type-alist or false-type-alist when these ; are to be thrown away anyhow. ; Improved the "Guards" warning from certify-book to avoid rather odd mention ; of loading into raw Lisp. Thanks to Sandip Ray and Robert Krug for helpful ; discussions. ; Modified documentation for topics ``~ilc[brr]'' and ``~ilc[breaks]'' to ; clarify that if you are at a raw Lisp break, then ~c[(]~ilc[abort!]~c[)] will ; get you back to the ACL2 top level. Thanks to Dave Greve and Eric Smith for ; bringing this issue to our attention. ; Made minor mods, e.g. to avoid an eq test of n against header in the raw Lisp ; code for aref1. ; Improved support for window interfaces, in particular with new state globals ; window-interface-prelude and window-interface-postlude, thanks to ; contributions from Peter Dillinger. :doc ":Doc-Section release-notes ACL2 Version 3.0.1 (August, 2006) Notes~/ NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Fixed a soundness bug, introduced in the previous release, due to a failure to disallow ~ilc[table] ~il[events] that set the ~ilc[acl2-defaults-table] in a ~ilc[local] context. Here is a proof of ~c[nil] that exploits the bug. ~bv[] (encapsulate () (local (program)) (defun foo () (declare (xargs :measure 17)) (+ 1 (foo)))) (thm nil :hints ((\"Goal\" :in-theory (disable foo (foo)) :use foo))) ~ev[] Fixed a bug in the alternatives to ~ilc[good-bye], which are the ~ilc[exit] and ~ilc[quit] commands. Thanks to Jared Davis and Peter Dillinger for pointing this out right away. The definition of ~ilc[len] has been highly optimized in raw Lisp. Thanks to Bob Boyer and Warren Hunt for suggesting such an improvement and providing a lot of help in coming up with the current implementation. The clause subsumption algorithms have been improved, both to improve efficiency during warnings for ~c[:]~ilc[rewrite] rules and to punt when the subsumption computation for induction appears to be blowing up. Thanks to Robert Krug for bringing this issue to our attention and supplying a useful example. A bug has been fixed that prevented ~ilc[time$] from working properly in OpenMCL and multi-threaded SBCL (actually, in any ACL2 image where feature ~c[:acl2-mv-as-values] is present). Thanks to Sol Swords for bringing this problem to our attention. A ~il[type-spec] of the form ~c[(satisfies pred)] carries the requirement that ~c[pred] be a unary function symbol in the current ACL2 ~il[world]; otherwise, it is illegal. Thanks to Dave Greve for pointing out that Common Lisp has this requirement. Installed a fix provided by Gary Byers (for ACL2 source function ~c[install-new-raw-prompt]), for OpenMCL, that fixes an issue exposed in some versions of OpenMCL when compiler optimization is off. Fixed a bug in contributed book ~c[misc/untranslate-patterns.lisp] that was causing calls of ~c[add-untranslate-pattern] to be rejected in ~il[books]. Thanks to Ray Richards for pointing out this bug and to Jared Davis for assisting in the fix. Fixed a bug in ~ilc[defstobj] when keywords ~c[:initially] and ~c[:resizable] are both supplied. In this case, the definition of the resizing function mistakenly failed to quote the ~c[:initially] value, even though this value is not to be evaluated. One could even get an error in this case, as in the following example supplied by Erik Reeber, whom we thank for bringing this bug to our attention: ~bv[] (defstobj $test (test-x :type (array t (5)) :initially (0) :resizable t)) ~ev[] A new feature, ~ilc[with-prover-time-limit], allows the setting of time limits during proofs. This is ~st[not] a general-purpose time-limit utility, nor is it guaranteed to implement a strict bound; it only attempts to limit time approximately during proofs. Thanks to Pete Manolios and Daron Vroon, who made the most recent request for such a feature, and to Robert Krug for a helpful discussion. (GCL only) Fixed a bug in the procedure for building a profiling image. Thanks to Sol Swords for bringing this bug to our attention and to Eric Smith for bringing a subsequent problem to our attention. Handling of ~il[theories] can now use significantly less time and space. A regression suite run took about 25% longer before this change than it did after making this change (and also the ones in the next two paragraphs). Thanks to Vernon Austel for bringing this issue to our attention and for supplying code, quite some time ago, that provided detailed, useful implementation suggestions. Also thanks to the folks at Rockwell Collins, Inc. for pushing the limits of the existing implementation, thus encouraging this improvement. Fixed a performance bug in obtaining executable counterpart symbols. We now avoid certain computations made on behalf of warnings, when such warnings are disabled. We have relaxed the checks made when including an uncertified book, to match the checks made when including a certified book. Thanks to Eric Smith for suggesting this change. Fixed a bug in ~c[:]~ilc[pso] (~pl[set-saved-output]) that caused an error when printing the time summary. Made fixes to avoid potential hard Lisp errors caused by the use of ~c[:]~ilc[program] mode functions. The fix was to use a ``safe mode,'' already in use to prevent such errors during macroexpansion; ~pl[guards-and-evaluation]. However, such errors were possible during evaluation of macro ~il[guard]s, for example as follows: ~bv[] (defun foo (x) (declare (xargs :mode :program)) (car x)) (defmacro mac (x) (declare (xargs :guard (foo 3))) x) (defun g (x) (mac x)) ~ev[] A similar issue existed for calls of ~ilc[defpkg], ~ilc[in-theory], ~ilc[table], ~ilc[make-event], and ~c[value-triple], but has been fixed for all but ~c[in-theory] and ~c[make-event], where technical issues have caused us to defer this change. Fixed a bug in ~c[wet] that caused problems in OpenMCL, and perhaps other Lisp implementations, when the argument to ~c[wet] calls, or depends on, certain built-ins including ~ilc[prog2$], ~ilc[time$], ~ilc[mbe], and ~ilc[must-be-equal]. Thanks to David Rager for bringing this problem to our attention. The file ~c[books/Makefile-generic] has been improved so that when book certification fails with `make', the failure message contains the book filename. Documentation has been written to explain how to avoid an expensive immediate rewrite of the result of applying a ~c[:]~ilc[rewrite] or ~c[:]~ilc[meta] rule. ~l[meta]. Thanks to Robert Krug for supplying this trick, and to Eric Smith and Dave Greve for useful discussions. (OpenMCL only) OpenMCL-based ACL2 image names formerly had extension ~c[\".dppccl\"], which was correct only for some platforms (including 32-bit Darwin PPC). That has been fixed, thanks to a suggestion from Gary Byers. It is now legal to attach both a ~c[:use] and a ~c[:cases] hint at the same goal. Thanks to Eric Smith for (most recently) requesting this feature. It is now permissible to include the same symbol more than once in the imports list of a ~ilc[defpkg] form (i.e., its second argument). Also, the evaluation of ~ilc[defpkg] forms with long import lists now uses a reasonably efficient sorting routine to check for two different symbols with the same name (see also ~c[books/misc/sort-symbols.lisp]). If you currently call a function like ~c[remove-duplicates-eql] for your imports list, as had been suggested by a ~ilc[defpkg] error message, then you may experience some speed-up by removing that call. Thanks to Eric Smith for helping to discover this issue through profiling. Made miscellaneous efficiency improvements not listed above (for example, following a suggestion of Eric Smith to avoid checking for so-called ``bad Lisp objects'' during ~ilc[include-book], which saved almost 3% in time on one large example). Modified the notion of ``untouchable'' to separate the notion of untouchable functions and macros from the notion of untouchable state global variables. ~l[push-untouchable]. Thanks to Bob Boyer for sending an example, ~c[(put-global 'ld-evisc-tuple t state)], that suggested to us the need for more restrictive handling of untouchables. In particular, many ~c[ld] specials (~pl[ld]) are now untouchable. You may be able to work around this restriction by calling ~ilc[ld]; see for example the change to ~c[books/misc/expander.lisp]. But please contact the ACL2 implementors if this sort of workaround does not appear to be sufficient for your purposes. Fixed a bug in function ~c[set-standard-oi] (~pl[standard-oi]). Fixed a bug in the use of ~ilc[ld-evisc-tuple]. The bad behavior was an improper use of the print-level and print-length components of the tuple (specifically, taking its ~ilc[caddr] and ~ilc[cadddr] instead of taking its ~ilc[cadr] and ~ilc[caddr]). Thanks to Bob Boyer for bringing this bug to our attention. A new argument to the ~c[compile-flg] argument of ~ilc[certify-book], ~c[:all], causes creation of a file to be compiled in place of the given book, where that file contains not only a copy of the book (with ~ilc[make-event] forms expanded) but also contains definitions of the so-called ``executable counterparts'' of the functions defined in the book. Then, functions defined in the book will be run compiled when including the book, even for functions whose ~il[guard]s have not been verified, or are in ~c[:program] mode and running in so-called ``safe mode'' (for example, during expansion of macros). The default behavior, value ~c[t] of ~c[compile-flg], is unchanged. Moreover, a new ~c[:comp!] argument of ~ilc[include-book] now compiles the executable counterparts when creating the book's compiled file, and unlike ~c[:comp], always deletes the old compiled file first so that one always gets a fresh compile. Now, ~ilc[certify-book] gives a \"Guards\" warning only for ~c[:]~ilc[logic] mode functions that are defined in the given book but have not had their guards verified. Previously, it also warned about such functions that were defined in the certification world or in sub-books. A new command, ~ilc[redo-flat], facilitates the debugging of failed ~ilc[encapsulate] and ~ilc[progn] forms by evaluating preliminary forms in order to leave one at the point of failure. ~l[redo-flat]. Thanks to Ray Richards and others for asking for such a utility, and to Sandip Ray for useful discussions. We have changed the automatic declaration of of function types (still done in GCL and OpenMCL only, for now). Our motivation was to avoid the assumption that Common Lisp functions return one value when ACL2 says that they do; thanks to Bob Boyer for bringing this issue to our attention with the example of defining ~c[(foo x y)] to be ~c[(floor x y)]. ACL2 was saying that ~c[foo] returns a single value, but because ~c[floor] returns two values in raw Lisp, so does ~c[foo]. Other changes to automatic declaration include comprehending ~ilc[defund], not just ~ilc[defun]. A new function, ~ilc[mod-expt], computes ~c[(mod (expt base exp) m)], and does so efficiently in some implementations (currently only in GCL 2.7.0, which is not yet released). Thanks to Warren Hunt for suggesting such an addition. New functions ~ilc[getenv$] and ~ilc[setenv$] have been made available for reading and writing environment variables. Thanks to Jun Sawada for requesting these utilities. The query utility ~c[:]~ilc[pl] has been improved in several ways. As before, ~c[:]~ilc[meta] rules are only printed if the argument is a symbol; but the information printed for them is now more appropriate. The following are changes for the case that the argument is not a symbol, but rather, a term. (1) Rules are displayed that have ~il[equivalence] relations other than ~ilc[equal]. (2) All matching ~c[:]~ilc[definition] rules are displayed, where previously ~c[:definition] rules were only shown if they were ``simple'' rules (sometimes known as ``abbreviations''); ~pl[simple]. (3) The ``Equiv'' field is printed for terms, not just symbols. (4) The substitution is shown that, when applied to the left-hand side of the rule, will yield the specified term. Thanks to Eric Smith for suggesting these changes. The ~il[proof-checker] command ~c[;show-rewrites] has been improved to match the changes described above for ~c[:]~ilc[pl]. In particular, ~c[:]~ilc[definition] rules that are not ``~il[simple]'' are now displayed by the ~il[proof-checker]'s ~c[show-rewrites] (and ~c[sr]) command, and the ~il[proof-checker]'s ~c[rewrite] command has been correspondingly modified to accept these ~c[:definition] rules. Fixed `make' targets ~c[copy-distribution], ~c[copy-workshops], and ~c[copy-nonstd] so that they should also work for non-developers. Fixed a bug that was causing ~c[:]~ilc[pr] to display ~ilc[syntaxp] hypotheses oddly in some cases, in particular ~c[(syntaxp (let ...))]. (The problem was in the ``untranslate'' display of the internal form of ~c[syntaxp] calls.) Thanks to Robert Krug for bringing this problem to our attention. We also removed the restriction on ~ilc[bind-free] that its argument could not be a variable, a constant, or (more interestingly) a ~ilc[lambda] application (i.e., a ~ilc[let] or ~ilc[mv-let] expression). ~/~/") (deflabel |NOTE-3-0-1(R)| :doc ":Doc-Section release-notes ACL2 Version 3.0.1(r) (August, 2006) Notes~/ No significant changes have been made since Version 3.0 for support of non-standard analysis in particular. ~/ Please also ~pl[note-3-0-1] for changes to Version 3.0.1 of ACL2. ~/ ") (deflabel note-3-0-2 ; Things that seem too minor to mention to users: ; Added ev-w and ev-w-lst. ; The "soundness bug in linear arithmetic" mentioned below was confined to ; linearize1. Robert provided the fix and we checked it. Below is an example ; from Robert that proves nil in ACL2 Version_3.0.1 but fails after the patch. ; (defun id (x) x) ; ; (defthm id-rationalp ; (implies (force (rationalp x)) ; (rationalp (id x))) ; :rule-classes :type-prescription) ; ; (in-theory (disable id)) ; ; (defun id2 (x y) ; (if (zp x) ; y ; (id2 (+ -1 x) y))) ; ; (in-theory (disable (:type-prescription id2))) ; ; (defthm bad ; (implies (and (not (equal (id x) (id2 y z))) ; (acl2-numberp y) ; (integerp z) ; (<= 0 z)) ; (or (< (id x) (id2 y z)) ; (< (id2 y z) (id x)))) ; :hints (("[1]Goal" :in-theory (enable (:type-prescription id2)))) ; :rule-classes nil) ; ; (set-guard-checking :none) ; ; (let ((x "foo") ; (y 0) ; (z 0)) ; (implies (and (not (equal (id x) (id2 y z))) ; (acl2-numberp y) ; (integerp z) ; (<= 0 z)) ; (or (< (id x) (id2 y z)) ; (< (id2 y z) (id x))))) ; ; (thm ; nil ; :hints (("Goal" :use (:instance bad (x "foo") (y 0) (z 0))))) ; | ; Added type declaration in ts-subsetp (but seemed not to make a measurable ; difference in time, at least for fast GCL build). ; Here is evidence for the bug in symbol-package-name-pkg-witness-name: ; (defthm contradiction ; nil ; :hints (("Goal" ; :use ((:instance symbol-package-name-pkg-witness-name ; (pkg-name "")) ; (:instance intern-in-package-of-symbol-symbol-name ; (x (pkg-witness "")) ; (y 3))) ; :in-theory (disable (pkg-witness) ; intern-in-package-of-symbol-symbol-name))) ; :rule-classes nil) ; Implementation note: for reset-prehistory, the key idea is to manipulate ; world global 'command-number-baseline-info. ; Set ld-skip-proofsp to 'include-book during loading of compiled file by ; include-book. ; The "what could be considered a soundness hole" could be exploited as ; follows. ; (in-package "ACL2") ; ; ; Portcullis commands: ; #| ; (set-ld-redefinition-action '(:warn! . :overwrite) state) ; ; (encapsulate ; () ; (defun foo () t) ; (local (defun foo () nil)) ; (defthm foo-prop ; (equal (foo) nil) ; :rule-classes nil)) ; |# ; | ; ; ; NOTE: After the above commands: ; ; ACL2 !>(redefined-names state) ; ; NIL ; ; ACL2 !> ; ; ; Now execute: ; ; ; (certify-book "bad" 1) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use foo-prop)) ; :rule-classes nil) ; After certifying the book we can do this: ; (include-book "bad") ; (thm nil :hints (("Goal" :use contradiction))) ; The documentation can now be built on Linux and probably Mac OS (seems that ; Linux texinfo issues have been solved). ; Made a correction in :doc guard-evaluation-table for built-in :program mode ; functions. ; Improved error message when compound event (including certify-book and ; include-book) has sub-event that is not an embedded event form. If the ; sub-event is a macro call, then then "Note" at the end will give the original ; form as the macro call, not as the compound event. ; Improved max-output-type-for-declare-form-lst to add missing arguments in ; error message, as suggested by Robert. Also improved ; max-output-type-for-declare-form to check for true lists before calling ; max-output-type-for-declare-form-lst. ; Improved translate error message from mutual-recursion so that the ctx ; identifies the problem function. Thanks to Robert Krug for pointing out the ; value of making such an identification. ; Expanded *initial-global-table* to include all built-in state globals, so as ; do allow a more inclusive value of *protected-state-globals-for-make-event* ; (called *protected-system-state-globals* starting with v3-2). Thanks to ; Peter Dillinger for pointing out that ; *protected-state-globals-for-make-event* was incomplete. ; In the course of implementing ttags for include-book-fn, we noticed that we ; were accessing a world with global-val (in particular on property ; 'boot-strap-flg) after calling chk-certificate-file, which calls ; chk-raise-portcullis, which can extend the world. We fixed this to call ; global-val on an installed world instead. It seems possiblee that for large ; proof efforts, this change might provide performance improvements. ; Moved Boyer's scary sys-call example from :doc make-event to :doc sys-call, ; since make-event isn't the issue. ; Here are the books changed to accommodate the fix for the soundness bug in ; the redundancy criterion for defun events. It's possible that a few of these ; changes are no longer necessary now that we ignore the measure for redundancy ; when including books. ; ; workshops/2000/moore-manolios/partial-functions/report.lisp ; rtl/rel1/lib1/float.lisp ; rtl/rel1/lib1/round.lisp ; rtl/rel1/lib3/float.lisp ; rtl/rel1/lib3/round.lisp ; rtl/rel2/lib/bits.lisp ; rtl/rel2/lib/float.lisp ; rtl/rel3/lib/bits.lisp ; rtl/rel3/lib/float.lisp ; ; Problem with rtl/rel3/lib/top.lisp: can't have :? before real measure.. ; Solution: Modify the following to use :? in expo: ; rtl/rel3/support/bvecp-lemmas.lisp ; rtl/rel4/support/bvecp-lemmas.lisp ; (Might not be necessary with the skip-proofs change, but it's a nice change ; anyhow.) ; Then also needed (even after skip-proofs mod) to make such a change in: ; rtl/rel4/lib/bvecp-raw-helpers.lisp ; ; rtl/rel4/lib/bits.lisp ; rtl/rel4/lib/float.lisp ; rtl/rel4/lib/fadd.lisp ; rtl/rel5/lib/log.lisp ; rtl/rel5/lib/float.lisp ; rtl/rel5/lib/add.lisp ; finite-set-theory/osets-0.9/sets.lisp ; finite-set-theory/osets/sets.lisp ; workshops/2004/davis/support/sets.lisp ; workshops/2000/russinoff-short/summary.lisp ; Improved the error message for a bad bind-free alist result, based on an ; example sent by Serita Nelesen to the acl2-help mailing list. ; Directory books/defexec/chapter3 has been renamed books/defexec/other-apps/ ; because the records stuff is part of Section 4 of the corresponding paper, ; not Section 3. :doc ":Doc-Section release-notes ACL2 Version 3.0.2 (December, 2006) Notes~/ NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Fixed soundness bugs in the handling of primitive function ~ilc[pkg-witness], and improved its documentation. (The executable counterpart returned an incorrect default value, and the axiom ~c[symbol-package-name-pkg-witness-name] needed ~c[pkg-name] to be other than ~c[\"\"] in order to avoid the default value of \"ACL2\".) As fallout, a new built-in ~c[:]~ilc[forward-chaining] rule, ~c[symbol-package-name-of-symbol-is-not-empty-string], now asserts that the ~ilc[symbol-package-name] of a symbol is never ~c[\"\"]. Thanks to Mike Gordon for bringing these soundness bugs to our attention by attempting to prove translations of ACL2 axioms in HOL4. Fixed a soundness bug in linear arithmetic, due to incomplete tracking of forced assumptions while deriving inequalities. Thanks to Robert Krug for providing a fix and a proof of ~c[nil] before the fix. Fixed a soundness bug in the redundancy criterion for ~ilc[defun] events, which has been modified; ~pl[redundant-events]. This bug is illustrated below. Thanks to Peter Dillinger and Jared Davis for contributions to an email thread that led us to discover this bug. The solution is that for a definition to be redundant with an earlier definition, ACL2 no longer ignores ~c[:]~ilc[measure] ~ilc[xargs] except when skipping proofs (e.g., during ~ilc[include-book]). However, a new ``measure'', ~c[(:? v1 ... vk)], is supported, for specifying a measured subset of the set of formals, i.e., a set of formals that serves as the set of parameters for some valid measure. ~bv[] (encapsulate () (local (defun foo (x y) (declare (xargs :measure (acl2-count y))) (if (and (consp x) (consp y)) (foo (cons x x) (cdr y)) y))) ; So the following is redundant -- but it guesses a measure ; of (acl2-count x), which isn't right! (defun foo (x y) (if (and (consp x) (consp y)) (foo (cons x x) (cdr y)) y))) ; end of encapsulate ; Now we prove a non-theorem by exploiting the bug above, ; erroneously replacing formal y by a constant in the induction ; scheme hinted below. (This should not be allowed, as y should be ; labeled as a measured formal.) (defthm bad (atom x) :rule-classes nil :hints ((\"Goal\" :induct (foo x '(3))))) ; It's easy to get a contradiction by instantiating the ; non-theorem just above. (defthm contradiction nil :rule-classes nil :hints ((\"Goal\" :use ((:instance bad (x '(7))))))) ~ev[] Fixed a bug in ~c[:]~ilc[pl] and the ~il[proof-checker]'s ~c[show-rewrites] (~c[sr]) command that was causing a Lisp break. For ~c[:]~ilc[pl], also improved the display of unifying substitutions, modified output to take binding hypotheses ~c[(equal var term)] into account properly, and arranged for inclusion of ~il[meta] rules that modify the given term. Thanks to Eric Smith for bringing these issues to our attention. Introduced new utilities for undoing ~il[command]s, ~c[:]~ilc[ubu] and ~c[:]~ilc[ubu!], which are analogous to ~c[:]~ilc[ubt] and ~c[:]~ilc[ubt!] (respectively) except that they only undo back up to, but not including, the indicated command. Fixed a performance bug, pointed out by Eric Smith, that was negating efforts made for the preceding release to avoid computation for disabled warnings. Added ~ilc[time$] and ~c[value-triple] to ~c[*acl2-exports*]. Thanks to Bob Boyer and Erik Reeber (respectively) for bringing these issues to our attention. Improved the automatic proclaiming of function types for GCL and OpenMCL, specifically to use an output format consistent with the Common Lisp spec. Thanks to Bob Boyer for bringing this issue to our attention. Added ~c[books/misc/transfinite.lisp], which deals with transfinite induction in ACL2. Thanks to Eric Smith for contributing this book. Added ~c[books/misc/process-book-readme.lisp] to the distribution. Thanks to Sandip Ray for pointing out its omission. Added contributions ~c[books/concurrent-programs/bakery/] and ~c[books/concurrent-programs/german-protocol/]. These contributions can be used as tutorials, especially by new ACL2 users, for learning how to model concurrent protocols in ACL2 and the steps involved in reasoning about their correctness. Thanks to Sandip Ray for these contributions. See the ~c[Readme.lsp] files in these directories. Theory invariants may now involve the variable ~c[ENS] instead of the variable ~c[THEORY]. The practical effect of this change is that any expression of the form ~c[(MEMBER-EQUAL rune THEORY)] occurring in a ~ilc[theory-invariant] expression should be replaced by ~c[(ACTIVE-RUNEP rune)]. ~l[theory-invariant]. Thanks to Eric Smith and Dave Greve for pointing out an inefficiency in the handling of theory invariants that led to this change, which can speed up their handling by orders of magnitude on large examples, and to Eric for testing this change and pointing out problems with an early implementation of it. Theory invariants (~pl[theory-invariant]) are no longer checked on theories defined by ~ilc[deftheory] ~il[events]. After all, one can define a theory with ~c[deftheory] that is not intended to be used as the current theory, but rather is intended to be combined with other ~il[theories] (~pl[theory-functions]). Thanks to Eric Smith for bringing this issue to our attention. ~ilc[Theory-invariant] errors had been reported with very little detail when warnings were inhibited. This problem has been fixed; thanks to Eric Smith for bringing it to our attention and providing an example. We have also improved the handling of redundancy for ~ilc[theory-invariant] ~il[events]. The macro ~ilc[defun-sk] now has a new optional keyword, ~c[rewrite], that can be used to change the form of the ~c[:]~ilc[rewrite] rule generated when the quantifier is ~ilc[forall]. Thanks to Eric Smith and Sandip Ray for useful discussions on this topic. We have also slightly modified the ~il[hints] for the ~ilc[defthm] event underneath a ~c[defun-sk] in order to make the proof more reliably efficient. A new event, ~ilc[reset-prehistory], allows setting of a barrier before which undoing is illegal. An argument to this macro allows the barrier to be made permanent; otherwise, it can be removed with ~c[:]~ilc[ubt-prehistory]. Thanks to Peter Dillinger for useful conversations leading to the addition of ~ilc[reset-prehistory]. A new query, ~c[(]~ilc[wormhole-p]~c[ ]~ilc[state]~c[)], allows users to determine whether or not they are in a ~ilc[wormhole]. Thanks to Peter Dillinger for providing this utility. ~c[Value-triple] no longer evaluates its form during ~ilc[include-book], and in raw Lisp its calls trivially macroexpand to ~c[nil], without any consideration of its argument. This change avoids errors and warnings when ~il[stobj] names occur in the argument. We fixed what could be considered a soundness hole that could occur by exploiting redefinition in a particular way. Thanks to Peter Dillinger for raising a question that led to discovery of this hole. A bug has been fixed in handling of illegal ~il[theory] expressions. Thanks to Eric Smith, who reported this problem and provided the example ~c[(in-theory '((:definition natp) (:rewrite doesntexist)))] to show how a hard error could occur. Improved error reporting by ~ilc[certify-book] when the certification ~il[world] contains inadmissible forms. Modified ~ilc[defchoose] to add two new keyword arguments. There is now a ~c[:doc] keyword argument; previously, an optional documentation string (~pl[doc-string]) was to be placed just before the body, without a keyword. There is also a ~c[:strengthen] argument that strengthens the axiom added, which allows for the definition of ``fixing'' functions for equivalence relations that choose canonical representatives of equivalence classes. ~l[defchoose]. Thanks for Dave Greve for useful discussions that led us to this ~c[:strengthen] enhancement. Added ~c[books/misc/bash.lisp], which provides utilities for simplifying a goal into a list of subgoals (as documented at the top of that file). Thanks to Dave Greve for requesting this utility and suggesting refinements to its functionality, which have been incorporated. (For Emacs users only) The command ~c[meta-x new-shell] provided by file ~c[emacs/emacs-acl2.el] now puts you in shell-mode, which for example supports directory tracking. Thanks to Jared Davis for suggesting this change. Fixed some mishandling of ~il[stobj]s by ~ilc[make-event] expansion. Introduced a new event, ~ilc[defttag], that introduces a ``trust tag'' (``ttag'') allowing for extensions of ACL2 and for the use of generally unsafe ACL2 constructs. Thanks to Peter Dillinger, Sandip Ray, and Erik Reeber for useful discussions on ~c[defttag] and the following related items. ~bq[] A new event, ~ilc[remove-untouchable], can be used to give users access to system functions and data structures. We also fixed a bug in ~ilc[push-untouchable]; and, it no longer is a no-op in ~c[:]~ilc[program] mode. Thanks to Peter Dillinger for proposing ~ilc[remove-untouchable] and suggesting that it and ~ilc[push-untouchable] be functional in ~c[:]~ilc[program] mode. Raw-mode (~pl[set-raw-mode]) no longer disables ~ilc[certify-book]. However, ~ilc[set-raw-mode] is now disallowed unless there is an active ttag (~pl[defttag]). If you want to execute ~c[(]~ilc[set-raw-mode]~c[ t)] and there is no active ttag, consider executing ~c[(]~ilc[set-raw-mode-on!]~c[)] instead. Redefinition of system functions is disallowed unless there is an active ttag. However, ~ilc[redef!] now introduces ~c[(defttag :redef!)] in order to allow redefinition of system functions. A new event, ~ilc[progn!], is a legal embedded event form that can go in ~il[books] and both ~ilc[encapsulate] and ~ilc[progn] forms (~pl[embedded-event-form]), and is similar to ~ilc[progn] except that it allows arbitrary forms. Thus, a ~ilc[progn!] form is potentially dangerous and can only be evaluated if there is an active ttag. ~l[ttags-seen] for information about how to find the ttags known in the current ACL2 ~il[world], and for related caveats. A new book created with Peter Dillinger, ~c[books/misc/hacker.lisp] (added after Version_3.3: now ~c[books/hacking/hacker.lisp]), uses ~ilc[progn!] to define utiliities ~c[with-raw-mode] and ~c[with-redef-allowed], which respectively allow raw Lisp evaluation and redefinition to take place within a certifiable book (!).~eq[] Macro ~ilc[with-output] is no longer allowed in function bodies because it does not have (and has never had) any effect in raw Lisp. ~l[with-output] for a workaround. Fixed a bug in redundancy of ~ilc[defstobj] in raw Lisp, which caused an error when certifying a book with a redundant ~ilc[defstobj] event whose ~ilc[stobj] had already been modified. Here is an example: ~bv[] (defstobj st fld) (update-fld 3 st) (certify-book \"foo\" 1) ; where foo.lisp contains (defstobj st fld) ~ev[] New books illustrating ~ilc[make-event] have been contributed in directory ~c[books/make-event/]: ~c[dotimes.lisp] (David Rager), ~c[stobj-test.lisp], and ~c[logical-tangent.lisp] (Peter Dillinger). Modified ~c[print-object$] (~pl[io]) so that it no longer prints an extra space at the end. Replaced the ``draconian restriction to avoid capture'' that had prevented some ~c[:functional-instance] ~il[hints] from being legal. The corresponding check now only requires that no variable free in the functional substitution is captured by a ~ilc[let] or ~ilc[mv-let] (or ~ilc[lambda]) binding. ~l[lemma-instance]. Added new extended metafunction, ~c[mfc-rw+], which is equivalent to ~c[mfc-rw] except that it takes an alist argument, which may be useful for efficiency. ~l[extended-metafunctions]. Thanks to Robert Krug for suggesting this more efficient variant of ~c[mfc-rw]. Added support for the ~c[ignorable] ~ilc[declare] form. We now cause an error on a call of ~c[open-input-channel] (~pl[io]) with an argument string whose first character is the ~c[|] character. Thanks to Bob Boyer for providing an example (several months ago) showing the danger of such calls, namely that the following command would log you out and kill all of your processes when running on top of GCL in Linux:~nl[] ~c[(open-input-channel \"|kill -9 -1\" :object state)] Restricted the use of ~ilc[make-event] to contexts in which it can be tracked properly, under legal ~il[events] (~pl[embedded-event-form]). Thanks to Peter Dillinger for bringing an example to our attention that led to this fix. Fixed a bug that was avoiding ~il[guard]-checking for the functions ~ilc[compress1] and ~ilc[compress2]. Thanks to David Rager for bringing this bug to our attention. Added an error message when a ~ilc[defun] or ~ilc[mutual-recursion] event fails, to clarify whether failure is for the ~il[measure] conjecture or for the ~il[guard] conjecture. Thanks to David Rager for requesting clarification for such failures. Fixed a bug in reporting of ~il[guard] violations (hard Lisp error) when certain macros (for example, ~ilc[cond]) are used in the ~il[guard]. Thanks to Jared Davis for bringing this problem to our attention and providing assistance with the solution, in particular by providing a helpful example. Grant Passmore has contributed a resolution/paramodulation prover written in ACL2, in directory ~c[books/deduction/passmore/]. Thanks, Grant. Improved the error message when illegal theories are encountered. Improved the suppression of output for ~c[inhibit-output] arguments of routines in the book ~c[books/misc/expander.lisp]. Thanks to Qiang Zhang for pointing out the possibility for improvement here. Added a new directory ~c[books/arithmetic-3/extra/] that extends ~c[books/arithmetic-3] with additional rules, contributed by Alex Spiridonov with guidance from Robert Krug. WARNING: This directory is under development. It may undergo large changes in future releases, so please consider it experimental and subject to change. Feedback is welcomed. As part of the work mentioned just above, Robert Krug and Alex Spiridonov contributed improvements to ~c[books/arithmetic-3/]: ~bq[] o A new rule ~c[|(* (/ x) (/ (expt x n)))|] in ~c[bind-free/collect.lisp], which is important for reducing ~c[collect-*] expressions though it slowed down one proof (see comment above this rule in ~c[bind-free/collect.lisp]). o Slight improvements of rules ~c[integerp-mod] and ~c[rationalp-mod] in ~c[floor-mod/floor-mod.lisp]. o To avoid conflict with ~c[books/rtl/rel6/arithmetic/], renamed rule ~c[mod-minus] to ~c[mod-neg] in ~c[floor-mod/floor-mod.lisp], and renamed ~c[integerp-+-reduce-leading-constant] to ~c[integerp-+-reduce-leading-rational-constant] in ~c[bind-free/integerp.lisp].~eq[] (GCL on Windows only) Made a low-level change to avoid multiplying stacks for GCL on Windows, since GCL 2.6.6 broke while doing this. Fixed bugs in linear arithmetic (rarely evidenced, it seems) involving using ~c[<] to compare complex rational constants. Thanks to Robert Krug for helping with the fixes. Added a new event, ~ilc[assert-event], for checking that forms evaluate to non-~c[nil] values. Thanks to Peter Dillinger for suggesting and collaborating on this addition. ~/~/") (deflabel note-3-1 :doc ":Doc-Section release-notes ACL2 Version 3.1 (December, 2006) Notes~/ NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Please ~pl[note-3-0-2] for a description of changes since Version 3.0.1, and also ~pl[note-3-0-1] for additional changes since Version 3.0. ~/~/") (deflabel |NOTE-3-1(R)| :doc ":Doc-Section release-notes ACL2 Version 3.1(r) (December, 2006) Notes~/ No significant changes have been made since Version 3.0 for support of non-standard analysis in particular. ~/ Please also ~pl[note-3-1] for changes to Version 3.1 of ACL2. ~/ ") (deflabel note-3-2 ; The "soundness bug that was allowing unknown packages" mentioned below was ; due to binding *inside-include-book-fn* to t in certify-book-fn, which was ; causing avoidance of the chk-bad-lisp-object check done by read-object. Here ; is a proof of nil in Version_3.1. Each book is to be certified in a fresh ; session (fresh for each book). ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; ---------------- ; Book sub.lisp: ; ---------------- ; ; Portcullis command: ; ; (make-event (er-progn (defpkg "FOO" nil) (value '(value-triple nil)))) ; ; (in-package "ACL2") ; ; (defconst *c* 'foo::a) ; ; (defthm thm1 ; (equal (symbol-package-name *c*) "FOO") ; :rule-classes nil) ; ; ---------------- ; Book top.lisp: ; ---------------- ; ; Portcullis command: ; ; (defpkg "FOO" '(a)) ; ; (in-package "ACL2") ; ; (include-book "sub") ; ; (defthm contradiction ; nil ; :hints (("Goal" :use thm1)) ; :rule-classes nil) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Here is Peter Dillinger's original example related to package stuff. After ; certifying "pkg" (after executing the given defpkg) and then (after :ubt! 1) ; "use", then (include-book "use") will break (unknown package) in a new ; session when an attempt is made to read use.cert. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; File pkg.lisp: ; #| (defpkg "FOO" ()) |# ; ; (in-package "ACL2") ; ; (defun foo::foo () 42) ; ; (defun bar () '(value-triple (foo::foo))) ;;; File use.lisp: ; (in-package "ACL2") ; ; (include-book "pkg") ; ; (make-event (bar)) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; We came up with the following example when dealing with the issues exposed in ; the two examples above. If you certify "book1", "book2", and then "top", ; with portcullis commands as shown, then (include-book "top") will break ; (unknown package) when run in a new session, when reading top.cert. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; File book1.lisp: ; ; (defpkg "P1" nil) ; ; (certify-book "book1" 1) ; ; (in-package "ACL2") ; ; (defun f1 (x) x) ;;; File book2.lisp: ; ; (defpkg "P1" nil) ; ; (defmacro my-id (x) x) ; ; (defpkg "P2" (my-id '(p1::a p1::b x y))) ; ; (certify-book "book2" 3) ; ; (in-package "ACL2") ; ; (defun f2 (x) x) ;;; File top.lisp: ; (in-package "ACL2") ; ; (include-book "book1") ; ; (local (include-book "book2")) ; ; (defun f3 (x) x) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Fixed a bug in books/arithmetic-3/extra/Makefile discovered by Gary Byers ; (our fault, not Alex Spiridonov's). Also disabled two rules in ext.lilsp in ; that directory, in part following suggestion of Robert Krug. ; Following a suggestion from Peter Dillinger, makunbound-global and put-global ; can now take a first argument that is not a quoted symbol, provided there are ; no untouchables. The latter can be accomplished by, for example: ; (defttag t) ; (make-event ; (let ((names (global-val 'untouchable-vars (w state)))) ; (value `(remove-untouchable (,@names) nil)))) ; Made minor efficiency improvements to termination-machine. ; An expansion file is no longer written by certify-book if :save-expansion and ; the compile-flg are nil. ; Fixed a bug in ffnnames-subsetp that made it true too often when lambda terms ; are involved, and hence could allow probably-not-validp to be true too ; often. This in turn could lead irrelevant-lits to return too large a list, ; which could lead eliminate-irrelevance-clause to strengthen the clause too ; much. So this was a (probably rare) heuristic issue but not a soundness ; issue. ; Improved with-warnings-suppressed for Allegro and CLISP, and removed ; corresponding suppressions elsewhere since lp calls ld-fn under ; with-warnings-suppressed already. ; Installed fix from Robert Krug to ; books/arithmetic-3/bind-free/default-hint.lisp that prints messages when ; enabling or disabling nonlinear arithmetic using a particular computed hint. ; For the "subtle soundness bug related to ~c[:]~ilc[meta] rules" item below, ; the developer-only directory tests/meta/ has some relevant tests. ; Updated files books/Makefile* to comprehend OpenMCL compiled files on ; 64-bit Linux and Intel-based Macs. Thanks to Robert Krug for pointing out ; that the ``clean'' target wasn't cleaning up such files. Note however that ; we no longer create compiled files for OpenMCL anyhow. ; The raw version of mfc-rw appeared to be missing a throw in the case that the ; obj is not a member of '(t nil ?). This has been fixed. ; Added a missing unsigned-byte (fixnum) declaration to rewrite-solidify-plus. ; Modified state-global-let* to take an optional ``setter'' function with each ; binding. ; For GCL, at Peter Dillinger's request, read-object now binds si::*notify-gbc* ; to nil (but only when reading from *standard-ci* or *standard-oi*). ; Fixed failure to lay down a command landmark when value :q is returned, e.g.: ; (er-progn (defun foo (x) x) (value :q)) ; In maybe-add-command-landmark, eliminated redundant (eq wrld old-wrld) test ; that had been conjoined with (raw-mode-p state) check. ; Fixed complex comment about make-event in encapsulate-pass-2. ; Disallowed theory-invariant in code just as we do for table, in translate11. ; Also cleaned up theory-invariant a bit by separating out its raw Lisp version ; and removing its name from *macros-for-nonexpansion-in-raw-lisp*. ; Modified handling of state global 'inhibit-output-lst, so that if it's set ; during the expansion phase of a make-event, then it will persist. Thanks to ; Jared Davis for asking about allowing the setting inhibit-output-lst as an ; embedded event form. ; Regarding "The rewriter has been tweaked to run faster": Here is the example ; from Eric (and Jared). Note that we get a slow-down by a factor of about 10 ; even without set-nu-rewriter-mode in versions before we added the following ; test near the end of rewrite, in v3-2: ; (not (member-eq (nu-rewriter-mode wrld) '(nil :literals))) ; (defun repeat (n v) ; (declare (xargs :guard (natp n))) ; (if (zp n) ; nil ; (cons v (repeat (1- n) v)))) ; ; (defthmd len-of-cons ; (equal (len (cons a x)) ; (+ 1 (len x)))) ; ; (in-theory (disable len)) ; ; (set-rewrite-stack-limit nil) ; ; (set-nu-rewriter-mode t) ; slows things down by a factor of almost 20 ; ; (thm (equal (len (repeat 7000 a)) 7000) ; :hints (("goal'" :in-theory (enable len-of-cons)) ; ("Goal" ; :do-not '(preprocess) ; :expand (:free (a x) (repeat a x))))) ; Regarding the slow array warning related to wormholes mentioned below: here ; is a way to cause that problem before Version_3.2. ; (wormhole t 'interactive-break nil '(value 'hi!)) ; (in-theory (disable binary-append)) ; :a! ; (thm (equal (car (cons x y)) x)) ; The modification for tag-trees caused about a 1% slowdown. However, a ; trivial modification to ancestors-check, which avoids recursion if ancestors ; is nil, caused about a 1.5% speedup. ; Here is how to cause the slow-array-warning mentioned below in Version_3.1 ; and before. ; ; (wormhole t 'interactive-break nil '(value 'hi!)) ; (in-theory (disable binary-append)) ; :a! ; (thm (equal (car (cons x y)) x)) ; Fixed books/Makefile-generic to use :ttags :all for include-book commands ; that support compilation (targets all-o, all-fasl, etc.). ; Fixed include-book with :load-compiled-file :comp (or :comp!) so that it ; always deletes a new expansion file. ; Built-in state global variables may no longer be made unbound with ; makunbound-global. See always-boundp-global. ; Removed some noise printed even using with-output, e.g.: ; (with-output :off :all (encapsulate nil (program))) ; Thanks to Peter D. for requesting such a change. ; Make-event can now be used inside state-global-let* when there is an active ; trust tag. See the discussion in :doc make-event of "the one exception to ; this restriction". Also, we have eliminated compiler warnings for calls of ; progn!. The problem was that state was unbound at a top-level call of ; progn!, but when we modified the raw Lisp code for progn! to bind state to ; *the-live-state*, we found that (pprogn .... (progn! ....)) was giving us a ; compiler warning since state no longer occurred free in the last form! So we ; fixed pprogn as well to declare state ignorable above the last form. ; Fixed spacing after a period that was resulting in output such as the ; following after a successful termination proof: ; ; "admit this function under the principle of definition.We could deduce" :Doc ":Doc-Section release-notes ACL2 Version 3.2 (April, 2007) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Before this release, a raw Lisp error could put the ACL2 user into the debugger of the host Common Lisp. Now, such cases will generally put the user back at the top-level loop after an informative message. For details, ~pl[set-debugger-enable]; also ~pl[break$]. Fixed a soundness bug that was allowing unknown packages to sneak into a book and wreak havoc. Thanks to Peter Dillinger for sending an interesting example that led us to an exploration resulting in finding this bug. (A comment in the source code for ~c[note-3-2] shows such an example.) That example led us to fix a couple of other bugs related to packages. ~l[hidden-death-package] if you are generally interested in such issues, and for associated examples, see comments in ~c[note-3-2] in the ACL2 source code. Fixed subtle soundness bugs related to ~c[:]~ilc[meta] rules by restricting evaluators (~pl[defevaluator]), as discussed in a new documentation topic: ~pl[evaluator-restrictions]. Fixed a soundness bug that was allowing redefinition from ~c[:]~ilc[logic] to ~c[:]~ilc[program] mode. This prohibition had been in ACL2 for awhile but was accidentally removed in the preceding version. Fixed a soundness bug related to ~ilc[trace$]. Thanks to Peter Dillinger for bringing it to our attention and for useful discussions, and providing a proof of ~c[nil], the essence of which is illustrated as follows: ~bv[] (value-triple (trace$ (bar :entry (defun foo () 17)))) ~ev[] Thus, ~ilc[trace$] could be used to cause destructive raw Lisp behavior. Now, ~c[trace$] fails unless it is either given a list of symbols or else there is an active trust tag (~pl[defttag]); otherwise, consider using ~c[trace!] instead. Closed a loophole that could be viewed as compromising soundness. It was possible to write files during book certification by exploiting ~ilc[make-event] expansion, but that is no longer the case by default. A new function ~ilc[open-output-channel!] is identical as a function to ~c[open-output-channel], except that the new function may be called even during ~ilc[make-event] expansion and ~ilc[clause-processor] ~il[hints], but requires that there is an active trust tag (~pl[defttag]). Thanks to Peter Dillinger for producing a convincing example (forging a ~il[certificate] during book certification; ~pl[open-output-channel!]) and to him, Sandip Ray, and Jared Davis for useful discussions on the topic. Added book ~c[books/defexec/reflexive/reflexive.lisp] to illustrate reflexive functions. ACL2 now generate scripts that invoke the saved image with ~c[exec]. (Previously this was only done for GCL and CLISP.) The benefit of this change can be to avoid the lingering of ACL2 processes after enclosing processes have exited. Thanks to Peter Dillinger for pointing out this issue. ACL2 has a better implementation of ~c[(]~ilc[good-bye]~c[)] (hence of synonyms ~c[(]~ilc[quit]~c[)] and ~c[(]~ilc[exit]~c[)]). As a result, you should now be able to exit ACL2 and Lisp from within the ACL2 read-eval-print loop with any of the above; formerly, this was not supported for some Lisp implementations, and was slow in OpenMCL. Thanks to SBCL developer Harald Hanche-Olsen for useful advice. Fixed a bug in raw-mode (~pl[set-raw-mode]) that was causing hard errors when evaluating calls of ~ilc[er-progn], or of macros expanding to such calls. Fixed a few Makefile dependencies, necessary only for parallel `make'. A new book, ~c[misc/defpun-exec-domain-example.lisp], provides an example showing how partial functions which return a unique value for arguments in a specified domain can be efficiently executed with ACL2. Execution is achieved using the ~ilc[mbe] construct. Thanks to Sandip Ray for providing this example. Existing function ~ilc[mod-expt] computes ~c[(mod (expt base exp) mod)] with great efficiency in GCL, but not in other Lisps. Now, the book ~c[arithmetic-3/floor-mod/mod-expt-fast.lisp] defines a function ~c[mod-expt-fast] that should provide significantly improved performance for such expressions in other Lisps as well, though still probably not as fast as when using ~c[mod-expt] in GCL. Thanks to Warren Hunt, with contributions from Robert Krug, for providing this book, Modified macro ~il[break-on-error] to print of an error message before entering a break, and to cause a hard error if the underlying Lisp cannot handle it (formerly, a raw Lisp break would occur). Thanks to Bob Boyer for bringing these issues to our attention. The book ~c[books/misc/defpun.lisp], as well as other books related to the ~c[defpun] macro, has been modified to avoid namespace collisions by prefixing function symbol names with ~c[\"DEFPUN-\"]; for example ~c[base] has been replaced by ~c[defpun-base]. Thanks to Dave Greve for providing a first version of this update to ~c[defpun.lisp]. A theory, ~c[base], in ~c[books/arithmetic-3/bind-free/top.lisp], has been renamed ~c[arithmetic-3-bind-free-base], to avoid potential name conflicts. Fixed ~c[books/arithmetic-3/bind-free/banner.lisp] to print (as before) a message about how to turn on non-linear arithmetic, by modifying the call of ~c[value-triple] to use ~c[:on-skip-proofs t]. Thanks to Robert Krug for bringing this issue to our attention. Modified ~c[books/Makefile-subdirs] and ~c[books/Makefile-psubdirs] so that they can be used with ~c[books/Makefile-generic]. Thus, one can set things up so that `make' can be used to certify books both in the current directory and subdirectories, for example as follows. ~bv[] ACL2 = ../../saved_acl2 arith-top: top all all: top DIRS = pass1 bind-free floor-mod include ../Makefile-subdirs include ../Makefile-generic top.cert: top.lisp top.cert: bind-free/top.cert top.cert: floor-mod/floor-mod.cert top.cert: floor-mod/mod-expt-fast.cert ~ev[] An experimental extension of ACL2 is under development by Bob Boyer and Warren Hunt to support function memoization, hash conses, and an applicative version of hash tables. The default build of ACL2 does not include this extension, other than simple logic definitions of functions in new source file ~c[hons.lisp]. Future versions of ACL2 may fully incorporate this experimental extension. The ~ilc[defevaluator] event macro has been modified primarily by adding a new constraint as follows, where ~c[evl] is the evaluator. The idea is that for the evaluation of a function call, one may replace each argument by the quotation of its evaluation and then also replace the alist environment with ~c[nil]. ~bv[] (DEFTHMD UNHIDE-evl-CONSTRAINT-0 (IMPLIES (AND (CONSP X) (SYNTAXP (NOT (EQUAL A ''NIL))) (NOT (EQUAL (CAR X) 'QUOTE))) (EQUAL (evl X A) (evl (CONS (CAR X) (KWOTE-LST (UNHIDE-evl-LIST (CDR X) A))) NIL)))) ~ev[] In order to support this change, there is another change: an evaluator maps ~c[nil] to ~c[nil] (note ~c[(AND X (CDR (ASSOC-EQ X A)))] in place of ~c[(CDR (ASSOC-EQ X A))] below). ~bv[] (DEFTHM UNHIDE-evl-CONSTRAINT-1 (IMPLIES (SYMBOLP X) (EQUAL (UNHIDE-evl X A) (AND X (CDR (ASSOC-EQ X A)))))) ~ev[] With the new ~ilc[defevaluator], Dave Greve has been able to do a proof about beta reduction that seemed impossible before (see ~c[books/misc/beta-reduce.lisp]). Thanks to Dave for suggesting an initial version of this change. Explicit compilation is now avoided for OpenMCL, resulting in fewer files to manage (no more files resulting from compilation) and, according to some tests, slightly faster run times. ~l[compilation]. Thanks to Bob Boyer and Warren Hunt for suggesting this possibility. Now, the ~c[term-evisc-tuple] (~pl[ld-evisc-tuple]) is overridden by state global ~c[user-term-evisc-tuple] in all cases. Formerly, this was only the case when ~c[term-evisc-tuple] was called with non-~c[nil] first argument. Symbols with the dot (~c[.]) character are generally no longer printed with vertical bars. For example, before this change: ~bv[] ACL2 !>'ab.c |AB.C| ACL2 !> ~ev[] After this change: ~bv[] ACL2 !>'ab.c AB.C ACL2 !> ~ev[] Thanks to Jared Davis for suggesting this improvement. Fixed bugs in ~c[guard] verification for theorems. The following examples illustrate these bugs. If either theorem's body is executed in raw Lisp there is likely to be a hard Lisp error, even though ~ilc[verify-guards] was supposed to ensure against that behavior. ~bv[] ; Example: Verify-guards failed to check that all functions in the theorem ; had already been guard-verified. (defun my-car (x) (car x)) (defthm my-car-compute-example (equal (my-car 3) (my-car 3))) (verify-guards my-car-compute-example) ; Example: Verify guards of a theorem whose body uses state improperly. (defthm bad-state-handler (if (state-p state) (equal (car state) (car state)) t) :rule-classes nil) (verify-guards bad-state-handler) ~ev[] ~l[GCL] for an example, developed with Warren Hunt and Serita Nelesen, that shows how to get fast fixnum (small integer) arithmetic operations in GCL. Fixnum declarations are now realized as ~c[(signed-byte 30)] and ~c[(unsigned-byte 29)] instead of what was generally ~c[(signed-byte 29)] and ~c[(unsigned-byte 28)]. MCL users may thus find better performance if they switch to OpenMCL. Note that some definitions have changed correspondingly; for example, ~ilc[zpf] now ~ilc[declare]s its argument to be of type ~c[(unsigned-byte 29)] instead of ~c[(unsigned-byte 28)]. A few ~il[books] may thus need to be adjusted; for example, changes were made to books in ~c[books/data-structures/memories/]. ACL2's rewriter now avoids removing certain true hypotheses and false conclusions. When a hypothesis rewrites to true or a conclusion rewrites to false, ACL2 formerly removed that hypothesis or conclusion. Now, it only does such removal when the hypothesis or conclusion is either a call of ~ilc[equal] or an equivalence relation (~pl[equivalence]), or else is sufficiently trivial (roughly, either redundant with another hypothesis or conclusion or else trivially true without considering the rest of the goal). A specific example may be found in source file ~c[simplify.lisp]; search for ``; But we need to do even more work''. Thanks to Robert Krug for providing the idea for this improvement and its initial implementation. As is common with heuristic changes, you may find it necessary on occasion to rename some subgoals in your ~il[hints]. And in this case, you might also find it necessary on rare occasions to add ~c[:do-not '(generalize)] ~il[hints]. A new function, ~c[mfc-relieve-hyp], allows (for example) for more powerful ~ilc[bind-free] hypotheses, by providing an interface to the rewriter's routine for relieving hypotheses. ~l[extended-metafunctions]. Thanks to Robert Krug for providing the idea for this feature and its initial implementation. Two improvements have been made to non-linear arithmetic (~pl[non-linear-arithmetic]). One allows for deducing strict inequality (~c[<]) for the result of certain polynomial multiplications, where previously only non-strict inequality (~c[<=]) was deduced. A second allows the use of the product of two polynomials when at least one of them is known to be rational. We had previously restricted the use of the product to the case where both were known to be rational. Thanks to Robert Krug for these improvements. (OpenMCL and Allegro CL only) Fixed ACL2's redefinitions of raw Lisp ~c[trace] and ~c[untrace] in OpenMCL and Allegro CL so that when given no arguments, they return the list of traced functions. For ~c[trace], this is an ANSI spec requirement. Note that ~ilc[trace$] and ~ilc[untrace$] continue to return ~c[nil] in the ACL2 loop. Fixed a bug that was allowing the symbol ~c[&whole] to appear in other than the first argument position for a ~ilc[defmacro] event, in violation of the Common Lisp spec (and leading to potentially surprising behavior). Thanks to Peter Dillinger for bringing this bug to our attention. It had been illegal to use ~ilc[make-event] under some calls of ~ilc[ld]. This has been fixed. Thanks to Jared Davis for bringing this issue to our attention with a simple example, in essence: ~bv[] (ld '((defmacro my-defun (&rest args) `(make-event '(defun ,@args))) (my-defun f (x) x))) ~ev[] ACL2 no longer prohibits certain ~ilc[make-event] forms when including uncertified ~il[books]. Thanks to Peter Dillinger for first bringing this issue to our attention. Hard errors arose when using ~il[break-rewrite] stack display commands, in particular ~c[:path] and ~c[:frame], from inside the ~il[proof-checker]. This has been fixed. Fixed a bug that could cause functions that call system built-ins ~c[f-put-global], ~c[f-get-global], or ~c[f-boundp-global] to cause a raw Lisp error even when proving theorems. Thanks to Peter Dillinger, for reporting such a failure for the form ~c[(thm (w '(1 2 3)))]. Renamed the formal parameters of function ~c[set-equal] in distributed book ~c[books/arithmetic-3/bind-free/normalize.lisp] so that more distributed books can be included together in the same session. In particular books ~c[books/data-structures/set-theory] and ~c[books/arithmetic-3/extra/top-ext] can now be included together. Thanks to Carl Eastlund for bringing this problem to our attention and to Robert Krug for suggesting the formals renaming as a fix. Metafunctions must now be executable. ~l[meta]. New utilities allow for user-defined simplifiers at the goal level, both verified and unverified (``trusted''), where the latter can even be defined by programs outside ACL2. ~l[clause-processor], which points to a new directory ~c[books/clause-processors/] that contains examples of these new utilities, including for example a system (``SULFA'') contributed by Erik Reeber that implements a decision procedure (thanks, Erik). Also ~pl[proof-checker-commands] for the new ~il[proof-checker] command ~c[clause-processor] (or for short, ~c[cl-proc]). The rewriter has been tweaked to run faster in some cases involving very large terms. Thanks to Eric Smith and Jared Davis for providing a helpful example that helped us locate the source of this inefficiency. Added ~c[books/make-event/defspec.lisp]. This book shows how one can mimic certain limited forms of higher-order statements in ACL2 by use of macros, ~ilc[make-event], and ~ilc[table] events. Thanks to Sandip Ray for his contribution. A new release of the RTL library, ~c[books/rtl/rel7/], replaces the previous version, ~c[books/rtl/rel6/]. Thanks to Hanbing Liu and David Russinoff for providing this new version. We thank David Russinoff for providing a proof of the law of quadratic reciprocity. See ~c[books/quadratic-reciprocity/Readme.lsp]. Eliminated a slow array warning (~pl[slow-array-warning]) that could occur when exiting a ~il[wormhole] after executing an ~ilc[in-theory] event in that wormhole. Thanks to Dave Greve for bringing this problem to our attention. A new accessor, ~c[(mfc-rdepth mfc)], provides a new field, the remaining rewrite stack depth, which has been added to metafunction context structures; ~pl[extended-metafunctions]. Thanks to Eric Smith for suggesting this addition. The algorithms were modified for collecting up rule names and other information used in proofs, into so-called ``tag-trees''. Tag-trees are now free of duplicate objects, and this change can dramatically speed up some proofs that involve many different rules. Thanks to Eric Smith for doing some profiling that brought this issue to our attention, and for reporting that this change reduced proof time on an example by about 47% (from 3681.46 reported seconds down to 1954.69). All legal ~c[xargs] keywords may now be used in ~ilc[verify-termination] ~il[events]. In particular, this is the case for ~c[:normalize]. (SBCL and CMUCL only) Fixed a problem with stobj array resizing functions that was causing a hard error in ACL2 images built on SBCL or CMUCL. A new ~il[table], ~ilc[evisc-table], allows you to introduce print abbreviations, for example for large constants. Moreover, a new reader macro ~-[] ~c[#,] ~-[] makes it convenient to reference constants even inside a quote. ~l[evisc-table]. Thanks to Bob Boyer and Warren Hunt for useful discussions leading to this feature. The macros in ~c[books/misc/expander.lisp] now have a new keyword argument, ~c[:simplify-hyps-p]. The default behavior is as before, but now case splitting from hypothesis simplification can be avoided. For details, evaluate ~c[(include-book \"misc/expander\" :dir :system)] and then ~c[:doc! defthm?] and ~c[:doc! symsym]. Thanks to Daron Vroon for sending a question that prompted this additional functionality. ACL2 failed to apply ~c[:]~ilc[restrict] hints to rules of class ~c[:]~ilc[definition], except for the simplest sorts (~pl[simple]). This has been fixed. Thanks to Jared Davis for pointing out this bug by sending a small example. Added a new ~c[:msg] argument to ~c[assert-event]; ~pl[assert-event]. The implementation of ~c[value-triple] has been modified to support this change. Fixed a bug in macro ~c[io?] that now allows the ~c[commentp] argument to be ~c[t]. This provides a way other than ~c[cw] to print without modifying state, for example as follows. (Warning: Certain errors may leave you in a ~il[wormhole], in which case use ~c[:a!] to abort.) ~bv[] ACL2 !>(prog2$ (io? event t state () (fms \"Howdy~~%\" nil *standard-co* state nil)) (+ 3 4)) Howdy 7 ACL2 !>:set-inhibit-output-lst (proof-tree event) (PROOF-TREE EVENT) ACL2 !>(prog2$ (io? event t state () (fms \"Howdy~~%\" nil *standard-co* state nil)) (+ 3 4)) 7 ACL2 !> ~ev[] ACL2 now disallows calls of ~ilc[progn!] inside function bodies, just as it already disallowed such calls of ~ilc[progn], since in both cases the Common Lisp meaning differs from the ACL2 meaning. Redefinition of system functions now always requires an active trust tag (~pl[defttag]). This restriction was intended before, but there was a hole that allowed a second redefinition without an active trust tag. Thanks to Peter Dillinger for pointing out this bug. ~ilc[Verify-termination] has been disabled for a few more built-in functions that are in ~c[:]~ilc[program] mode. (If you are curious about which ones they are, evaluate ~c[(f-get-global 'built-in-program-mode-fns state)].) [Note added for Version_3.4: This state global has been changed to 'program-fns-with-raw-code.] Moreover, such functions now will execute only their raw Lisp code, so for example they cannot be called during macroexpansion. Thanks to Peter Dillinger and Sandip Ray for useful discussions on details of the implementation of this restriction. New untouchable state global variables, ~c[temp-touchable-vars] and ~c[temp-touchable-fns], can control the enforcement of untouchability. ~l[remove-untouchable]. Thanks to Peter Dillinger for suggesting these features. The ``TTAG NOTE'' string was being printed by ~ilc[encapsulate] events whenever an active trust tag was already in effect (~pl[defttag]), even if the encapsulate event contained no ~ilc[defttag] event. This has been fixed. Thanks to Peter Dillinger for a query leading to this fix. Fixed a bug in ~ilc[progn!] that could leave the user in raw-mode (~pl[set-raw-mode]). This could occur when certifying a book with a ~c[compile-flg] value of ~c[t] (~pl[certify-book]), when that book contained a ~ilc[progn!] event setting raw-mode to ~c[t] without setting raw-mode back to ~c[nil]: ~bv[] (progn! (set-raw-mode t) ...) ~ev[] ~/~/") (deflabel |NOTE-3-2(R)| :doc ":Doc-Section release-notes ACL2 Version 3.2(r) (April, 2007) Notes~/ Changed the default distributed ~il[books] directory for ACL2(r) from ~c[books/] to ~c[books/nonstd/]. ~l[include-book], in particular the discussion of ``Distributed Books Directory''. Added directory ~c[books/arithmetic-3/] and its subdirectories to ~c[books/nonstd/]. (But a chunk of theorems from ~c[arithmetic-3/extra/ext.lisp] are ``commented out'' using #-:non-standard-analysis because they depend on ~c[books/rtl/rel7/], which is not yet in ~c[books/nonstd/]; feel free to volunteer to remedy this!) Incorporated changes from Ruben Gamboa to some (linear and non-linear) arithmetic routines in the theorem prover, to comprehend the reals rather than only the rationals. ~/ Please also ~pl[note-3-2] for changes to Version 3.2 of ACL2. ~/ ") (deflabel note-3-2-1 ; Here is the proof of nil from Sol Swords referenced in the mbe bug discussion ; below. ; (defun foo (a b) ; (mbe :logic (mv a b) ; :exec (mv a b))) ; ; ;; (foo 'a 'b) returns (A NIL); ; ;; should return (A B) ; ; (defthm foo-1-nil ; (equal (mv-nth 1 (foo 'a 'b)) nil) ; :rule-classes nil) ; ; (verify-guards foo) ; ;; now the correct behavior returns: ; ;; (foo 'a 'b) returns (A B). ; ; (defthm foo-1-b ; (equal (mv-nth 1 (foo 'a 'b)) 'b) ; :rule-classes nil) ; ; (thm ; nil ; :hints (("Goal" :use (foo-1-nil foo-1-b)))) ; Modified warning$ a bit to avoid what appears to be a GCL compilation bug. ; Fixed *hons-primitives*, and hence *acl2-exports*, to be independent of ; whether or not #+hons. This should make it possible to certify a book in the ; hons version and include it in the non-hons version, or vice-versa. ; Added patch for non-ANSI GCL to compiler::wrap-literals. ; Modified rune-< to comprehend the cddr of a rune. ; Added books/misc/misc2/ and put new book misc.lisp in it. ; Changed handling of built-in-program-mode-fns [called ; program-fns-with-raw-code starting in Version_3.4] outside boot-strap so that ; with guard-checking-on equal to :all or :none and safe-mode off, they behave ; the same as other functions. Thanks to Peter Dillinger for this change. ; Also introduced two similar state globals -- logic-fns-with-raw-code and ; macros-with-raw-code -- and corresponding defconsts for the initial values of ; all three. And, used the two *fns-with-raw-code state globals in place of ; *non-exec-fns* in compile-uncompiled-defuns, and eliminated *non-exec-fns*. ; Improved redefinition error message to show book in which redefinition is ; being attempted, in addition to already-shown book in which previous ; definition resides. ; Fixed books/clause-processors/Makefile so that "make clean" will also clean ; directories SULFA/c-files/ and SULFA/scripts/. ; Fixed error output redirection in books/Makefile-generic. Now, for example, ; various tests under clause-processors/SULFA/ will now direct all output to ; .out files as expected, rather than producing some at the terminal. ; Here is the example promised in the release note below about "handling of ; computed hints related to the stable-under-simplificationp parameter". ; Consider a computed hint like the following (taken from an actual example). ; ; `(:computed-hint-replacement ; ((adviser-default-hint id clause world stable-under-simplificationp)) ; :use ,(build-hints literals rules hyps nil) ; :expand ,(build-expand-hint literals)) ; ; Here is a new version, which after the fix causes the previous behavior. The ; idea is that the bug caused ACL2 to go to the preprocess-clause ledge of the ; waterfall, rather than to the top where :use hints are processed. Thus, the ; :expand hint was applied first, and then after a return to the top of the ; waterfall, the :use hint was applied. With the new version just below, that ; behavior is encoded explicitly. ; ; `(:computed-hint-replacement ; ('(:computed-hint-replacement ; ((adviser-default-hint id clause world stable-under-simplificationp)) ; :use ,(build-hints literals rules hyps nil))) ; :expand ,(build-expand-hint literals)) ; Sped up checksums by perhaps 5% in GCL by avoiding some arithmetic and ; perhaps indirection or boxing in function 32-bit-integerp. ; Made trivial mod to books/Makefile-generic, thanks to Rockwell Collins, that ; could avoid problems when cert.acl2 ends with a line containing `;' and no ; newline. ; In a test of v3-2 vs. the development version (both GCL), with efficiency ; mods (those below through the add-include-book-dir mod), cut the time by 57% ; for this include-book (could be representative): ; (time$ (include-book "rtl/rel7/support/support/top" :dir :system)) ; 8.74 s vs. 3.73 s (3 run average, run-gbc + run time) ; Eliminated FAILED target from books/Makefile-generic, inlining its function ; instead. ; Added dependency to books/make-event/Makefile: ; defspec.cert: eval.cert ; Improved (not yet advertised) feature rewrite-equiv to avoid some loops. ; Thanks to Dave Greve for bringing this to our attention with a very helpful ; example, which has been incorporated (slightly modified) as a comment, with ; the fix, into rewrite-solidify-rec. ; Modified certify-book-fn to do a full GC (garbage collection) in GCL after ; world rollback, just before the include-book phase. We have some hope that ; this could help reclaim the space taken by .o files from subsidiary books ; that are about to be re-included. :Doc ":Doc-Section release-notes ACL2 Version 3.2.1 (June, 2007) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. (OpenMCL and multi-threaded SBCL only) Fixed a soundness bug in the evaluation of ~ilc[mbe] forms in the presence of Lisp feature ~c[:acl2-mv-as-values]. Thanks to Sol Swords for reporting this problem and sending a simple proof of ~c[nil] (which can be found in a comment in the ACL2 sources, in ~c[(deflabel note-3-2-1 ...)]). Added a new utility, ~ilc[dmr] (Dynamicaly Monitor Rewrites), for watching the activity of the rewriter and some other proof processes. ~l[dmr]. We thank Robert Krug for useful contributions. Fixed a bug in evaluation of calls of ~ilc[with-prover-time-limit]. Fixed the writing of executable scripts when building ACL2, so that the build-time value of environment variable ~c[ACL2_SYSTEM_BOOKS] is no longer written there. Thanks to Dave Greve for discussing this change. Fixed bugs in ~c[:]~ilc[pl] (which are similarly present in the ~il[proof-checker]'s ~c[sr] (~c[show-rewrites]) command. The first bug was evident from the following forms sent by Robert Krug, which caused an error. ~bv[] (include-book \"arithmetic-3/floor-mod/floor-mod\" :dir :system) :pl (mod (+ 1 x) n) ~ev[] The second bug was due to a failure to take note of which rules are disabled, and could be seen by executing the following (very slow!). ~bv[] (defstub modulus () t) (include-book \"arithmetic-3/floor-mod/floor-mod\" :dir :system) :pl (mod (+ x y) (modulus)) ~ev[] Modified ~ilc[certify-book] so that by default, all executable-counterpart functions (sometimes called ``*1* functions'') are compiled. This is the behavior that was already supported with a ~c[compile-flg] argument of ~c[:all]; the change is that argument ~c[t] now has this behavior as well (and ~c[:all] is supported only for legacy purposes). A new value for ~c[compile-flg], ~c[:raw], gives the behavior formerly produced by value ~c[t], namely where executable-counterpart functions are not compiled. The above changes are irrelevant if compilation is suppressed; ~pl[compilation]. Finally, if environment variable ~c[ACL2_COMPILE_FLG] is set, then after converting to upper-case this environment variable's value of ~c[\"T\"], ~c[\"NIL\"], or ~c[\":RAW\"] will determine the value of the optional ~c[compile-flg] argument to be ~c[t], ~c[nil], or ~c[:raw], respectively, when this argument is not explicitly supplied. Modified ~ilc[include-book] so that ~c[:comp] argument now acts like ~c[:comp!], i.e., compiling a file that includes the file together with all executable counterpart (so-called ``*1*'') functions. A new argument, ~c[:comp-raw], has the behavior that ~c[:comp] had formerly, i.e., compiling the actual book only. The function ~ilc[nonnegative-integer-quotient] is now computed in raw Lisp by calling ~ilc[floor] on its arguments. This change was suggested by Peter Dillinger, in order to avoid stack overflows such as reported by Daron Vroon. A new book, ~c[books/misc/misc2/misc.lisp], contains a proof of equivalence of ~ilc[nonnegative-integer-quotient] and ~ilc[floor], and serves as a repository for other miscellaeous proofs, including those justifying ACL2 modifications such as this one. Enhanced ~ilc[accumulated-persistence] to break down results by useful vs. useless rule applications. In particular, this provides information about which rules were ever applied successfully, as requested by Bill Young. Added coverage of ~c[:]~ilc[meta] rules to the ~ilc[accumulated-persistence] statistics. Fixed a bug that was causing a ~c[:]~ilc[clause-processor] hint to fire on a subgoal of the goal to which it was attached, when the original application didn't change the clause. Thanks to Dave Greve for pointing out this bug and providing a useful example. Fixed a bug in handling of computed ~il[hints] related to the ~c[stable-under-simplificationp] parameter (~pl[computed-hints]). There were actually two bugs. A minor but confusing bug was that the same goal was printed twice upon application of such a hint. The major bug was that ~c[:use] ~il[hints] (as well as other ``top'' hints: ~c[:by], ~c[:cases], and ~c[:clause-processor]) were not being applied properly. Thanks to Jared Davis for sending an example some time ago that showed the duplicate printing, and to Dave Greve for sending an example showing mis-application of ~c[:]~ilc[clause-processor] ~il[hints]. Note that you may find that existing computed hints using the ~c[stable-under-simplificationp] parameter no longer have the same behavior; see a comment about computed hints in ~c[note-3-2-1], ACL2 source file ~c[ld.lisp], for an example of how you might want to fix such computed hints. David Russinoff has contributed an updated version of ~c[books/quadratic-reciprocity/] including minor modifications of the treatment of prime numbers and a proof that there exist infinitely many primes. Thanks to David for contributing this work, and to Jose Luis Ruiz-Reina for posing the challenge. Reduced the sizes of some ~il[certificate] (~c[.cert]) files by relaxing the test that allows original ~ilc[defpkg] ~il[events] to be placed there, rather than evaluating the import list term into an explicit list of symbols. Improved execution efficiency slightly for function ~c[rcdp] in file ~c[books/misc/records.lisp], by using ~ilc[mbe] to introduce a tail-recursive body. The executable script generated by ~ilc[save-exec] (and by the normal build process) now includes a time stamp as a comment. Thanks to Jared Davis for suggesting this change in order to support his use of ~c[omake]. In the process, we also arranged that the startup banner for an executable created by ~ilc[save-exec] shows all of the build (save) times, not just the one for the original image. Sped up most redundant ~ilc[defpkg] ~il[events] by avoiding evaluation and sorting of the imports list in the case of identical event forms. And, for ~ilc[defpkg] events that are not redundant, sped up their processing in Allegro CL (and perhaps other Lisps, but apparently not GCL) by using our own ~c[import] function. Modified ~ilc[add-include-book-dir] so that it refuses to associate a keyword with a different directory string than one it is already bound to. ~l[delete-include-book-dir] for how to remove the existing binding first. Thanks to Robert Krug for pointing out that without this change, one can find it difficult to debug a failure due to rebinding a keyword with ~ilc[add-include-book-dir]. Added a new value for the ~c[:do-not-induct] hint (~pl[hints]), ~c[:otf-flg-override], which causes ACL2 to ignore the ~c[:]~ilc[otf-flg] when considering whether to abort the proof because of a ~c[:do-not-induct] hint. Thanks to Daron Vroon for suggesting such an addition. Modified the printing of messages for entering and exiting raw mode (~pl[set-raw-mode]), so that in particular they are inhibited during ~ilc[include-book] or whenever ~c[observation]s are inhibited (~pl[set-inhibit-output-lst]). Thanks to Peter Dillinger for suggesting such a change. (For system hackers only.) The handling of ~il[events] of the form ~c[(progn! (state-global-let* ...))] had a bug that was causing bindings to be evaluated twice. Moreover, the use of system function ~ilc[state-global-let*] is suspect in raw Lisp. We have eliminated special treatment of ~c[state-global-let*] by ~c[progn!] in favor of a new keyword argument, ~c[state-global-bindings], that provides the intended functionality. ~l[progn!]. Moreover, special handling that allowed ~ilc[make-event] forms under ~c[state-global-let*] has been removed; the desired effect can be obtained using (progn! :state-global-bindings ...). Thanks to Peter Dillinger for pointing out the above bug and collaborating on these changes. Incorporated backward-compatible enhancements to ~c[books/misc/expander.lisp] from Daron Vroon (documented near the top of that file). The specification of ~c[:backchain-limit-lst] had required that only a single (~c[:]~ilc[rewrite], ~c[:]~ilc[linear], or ~c[:]~ilc[meta]) rule be generated. We have weakened this restriction to allow more than one rule provided that each rule has the same list of hypotheses. For example, the rule class ~c[(:rewrite :backchain-limit-lst 1)] is now legal for the corollary formula ~c[(implies (f x) (and (g x) (h x)))], where this was not formerly the case. Thanks to Dave Greve for bringing this issue to our attention. ~/~/") (deflabel |NOTE-3-2-1(R)| :doc ":Doc-Section release-notes ACL2 Version 3.2.1(r) (June, 2007) Notes~/ ~/ Please also ~pl[note-3-2-1] for changes to Version 3.2.1 of ACL2. ~/ ") (deflabel note-3-3 ; Modified function error-trace-suggestion to suggest WET only for lisps that ; support it. ; Fixed emacs/log-emacs-code.el (not distributed) to try to be ; platform-independent. Modified the following makefiles in support of that: ; books/workshops/2003/hbl/support/Makefile ; books/workshops/2004/sumners-ray/support/Makefile ; books/workshops/2003/kaufmann/support/rtl/Makefile ; books/workshops/1999/multiplier/Makefile ; books/Makefile ; Book defpun-exec-domain-example.lisp was moved from misc to misc2, so that a ; form depending on make-event could be uncommented. ; Fixed spacing issue in defun messages: too many spaces before ; type-prescription message. ; Modified books/data-structures/structures.lisp to eliminate compiler warnings ; for ignored variables. ; Improved a heretofore misleading error message produced by guess-measure when ; no controller-alist is supplied with a :definition rule. The message had ; made it appear (erroneously) that the function's original definition doesn't ; have a controller-alist. Thanks to John Cowles for bringing this to our ; attention. ; Eliminated (defmacro checkpoints ...) and (defun print-checkpoints ...), ; which were defined merely to cause errors for those who used pstack features ; back in Version_2.7. ; Fixed a bug in parse-primes: eq was called for character comparison instead ; of eql, causing a violation during macroexpansion (because of safe-mode). ; Modified some command printing (see print-ldd-full-or-sketch) to recognize ; defund and defthmd. ; Fixed books/misc/bash.lisp so that state global 'guard-checking-on is bound ; to nil, as required by pc-prove (and now documented there). ; Modified message about abandoning induction when a "formula is subsumed by ; one of its parents", following a conversation initiated by Bill Legato. ; Changed name of function print-goal to print-pc-goal. ; Fixed dependencies in Makefiles under ; books/workshops/2003/kaufmann/support/, which were causing problems with ; "make -j 8". ; Fixed problem with error message in check-exact-free-vars. ; Changed calls of len to calls of length in newly-defined-top-level-fns. ; Modified and-list and or-list in books/clause-processors/equality.lisp in ; order to be a bit more efficient using guards. ; Added a type (essentially, fixnum) declaration for a return case in fmt0 (but ; it's not clear how much GCL boxing that will avoid), and added calls of ; the-fixnum in enabled-numep and enabled-arith-numep. ; Improved error message on encapsulate or include-book failure to give ; information when encountering a record-expansion form, to help in debugging ; problems related to user's make-event forms. ; Changed io? type in print-rule-storage-dependencies from 'prove to 'event. ; Error reporting has been improved slightly when mv is called with the wrong ; number of arguments in a function body. Thanks to Warren Hunt for suggesting ; that the error message be improved. ; Changed names mod* and floor* to mod-induct-fn and floor-induct-fn, ; respectively, in books/arithmetic-3/floor-mod/floor-mod.lisp. This change ; probably won't affect anyone, except to make arithmetic-3 more compatiible ; with the rtl libraries (which define mod*), since these functions are only ; used to set up inductions. ; Eliminated an error when using state-global-let* with an empty binding list, ; e.g., (progn! :state-global-bindings nil). ; Fixed warning: ; ignoring old commands for target `clean-more' ; with 'make clean' in books/nonstd/workshops/1999/calculus/. ; Here are examples that illustrate bugs (1) through (3) mentioned in the ; paragraph on "Fixed the following issues with packages and book ; certificates". Try these in Version_3.2.1 and you will see the bugs; but, ; these work fine in Version_3.3. In the following, foo.lisp has just the ; forms (in-package "ACL2") and (defun foo (x) x). ; ; (1) After doing the following, foo.cert has needless defpkg forms among the ; portcullis commands in Version_3.2.1 but not in Version_3.3. ; ; (include-book "arithmetic/top" :dir :system) ; (certify-book "foo" 1) ; ; (2) Version_3.2.1 causes errors when following the directions below, but ; Version_3.3 does not. Here, foo.lisp is as above and bar.lisp has only the ; forms (in-package "ACL2") and (defun bar (x) x). ; ; (defpkg "FOO" nil) ; (defconst *a* 'foo::a) ; (certify-book "foo" 2) ; ; [Restart ACL2] ; (encapsulate () (local (include-book "foo")) (defun h (x) x)) ; (certify-book "bar" 1) ; ; (3) Watch (include-book "bar") fail at the end of the following instructions ; in Version_3.2.1, but not in Version_3.3. ; ; (set-ignore-ok :warn) ; (defpkg "FOO" (let ((x 3)) '(a b))) ; (certify-book "foo" 2) ; ; [Restart ACL2] ; (encapsulate () (local (include-book "foo")) (defun h (x) x)) ; (certify-book "bar" 1) ; ; [Restart ACL2] ; (include-book "bar") ; Fixed books/clause-processors/Makefile to work on Sun/Solaris. :Doc ":Doc-Section release-notes ACL2 Version 3.3 (November, 2007) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.2.1 into new features, bug fixes, prover algorithm enhancements, and miscellaneous. Also ~pl[note-3-2-1] for other changes since Version 3.2. ~st[NEW FEATURES] A new ``gag-mode'' provides less verbose, more helpful feedback from the theorem prover, in support of The Method (~pl[the-method]). ~l[set-gag-mode]. We recommend the use of gag-mode, which may become the default in future ACL2 releases, and we welcome suggestions for improvement. We thank Robert Krug and Sandip Ray for helpful feedback in the design of ~il[gag-mode]. Note that when proofs fail, then even without gag-mode and even if proof output is inhibited, the summary will contain a useful listing of so-called ``key checkpoints'' (~pl[set-gag-mode]). Added support for a leading `~c[~~]' in filenames. Thanks to Bob Boyer for suggesting this enhancement. Note that since `~c[~~/]' depends on the user, it is disallowed in ~il[books] to be certified (~pl[certify-book]), since otherwise an ~ilc[include-book] form in a book, ~c[b], could have a different meaning at certification time than at the time ~ilc[include-book] is later executed on book ~c[b]. Made a change to allow ~c[(time$ FORM)] and ~c[(with-prover-time-limit TIME FORM)] when ~c[FORM] includes ~ilc[make-event] calls that change the ACL2 ~il[world]. Thanks to Jared Davis for requesting such support for ~ilc[time$]. Computed ~il[hints] (~pl[computed-hints]) may now produce a so-called ``error triple'', i.e., a result of the form ~c[(mv erp val state)], where a non-~c[nil] ~c[erp] causes an error, and otherwise ~c[val] is the value of the hint. It remains legal for a computed hint to return a single ordinary value; indeed, the symbol form of a computed hint must still be a function that returns an ordinary single value. New hints provide additional control of the theorem prover, as follows. ~l[hints] for more details, and see new distributed book directory ~c[books/hints/] for examples, in particular file ~c[basic-tests.lisp] in that directory for simple examples. ~bq[] o The hint ~c[:OR (hints-1 ... hints-k)] causes an attempt to prove the specified goal using each ~c[hints-i] in turn, until the first of these succeeds. If none succeeds, then the prover proceeds after heuristically choosing the ``best'' result, taking into account the goals pushed in each case for proof by induction. o A custom hint is a keyword that the user associates with a corresponding hint-generating function by invoking ~ilc[add-custom-keyword-hint]. Thus, a custom hint may be viewed as a convenient sort of computed hint. o A custom hint, ~c[:MERGE], is implemented in distributed book ~c[books/hints/merge.lisp]. It is useful for combining hints. o A sophisticated yet useful custom hint is the ~c[:CONSIDER] hint implemented in distributed book ~c[books/hints/consider-hint.lisp]. With this hint, you can for example give the equivalent of a ~c[:USE] hint without the need to supply an instantiation. Include that book in order to see documentation online with ~c[:doc consideration], and see the book ~c[books/hints/consider-hint-tests.lisp] for examples.~eq[] A new hint, ~c[:]~ilc[reorder], allows the specification of which subgoals are to be considered first. Thanks to Sandip Ray for putting forward this idea. Enhanced ~ilc[set-saved-output] by supporting a second argument of ~c[:same], which avoids changing which output is inhibited. Added macros ~c[thm?] and ~c[not-thm?] to distributed book ~c[books/make-event/eval.lisp], so that it's easy to test within a certified book that a proof attempt succeeds or that it fails. Added printing function ~ilc[cw!], which is analogous to ~ilc[cw] just as ~ilc[fmt!] is to ~ilc[fmt], i.e., printing so that the result can be read back in. Thanks to Jared Davis for suggesting this enhancement (after doing his own implementation). The ACL2 customization file can now be specified using environment variable ~c[ACL2-CUSTOMIZATION] [note: starting with Version_4.0, ~c[ACL2_CUSTOMIZATION]]. ~l[acl2-customization]. Thanks to Peter Dillinger for requesting this feature. Added new emacs capabilities for proof trees (all documented in emacs): ~bq[] o New function start-proof-tree-noninteractive, for example~nl[] (start-proof-tree-noninteractive \"*shell*\") o C-z o Switch to another frame o C-z b Switch to prooftree buffer o C-z B Switch to prooftree buffer in \"prooftree-frame\" frame~eq[] Added Common Lisp function, ~c[search], as a macro in ~ilc[logic] mode, with limited support for keyword arguments. Thanks to Warren Hunt for requesting this addition. Sandip Ray has contributed a book, ~c[books/make-event/defrefine.lisp], that provides a collection of macros to aid in reasoning about ACL2 functions via refinement. Wrote and incorporated new utility for listing all the theorems in an included book. See ~c[books/misc/book-thms.lisp]. Thanks to Jared Davis for requesting this functionality. The new distributed book ~c[misc/defp.lisp] generalizes the ~ilc[defpun] macro to allow more general forms of tail recursion. (Low-level printing improvement) A new function, ~c[set-ppr-flat-right-margin], allows the right margin for certain kinds of ``flat'' printing to exceed column 40. Thanks to Jared Davis for pointing out that state global variables ~c['fmt-hard-right-margin] and ~c['fmt-soft-right-margin] are not alone sufficient to extend the right margin in all cases. The event ~ilc[add-include-book-dir] can now take a relative pathname as an argument. Formerly, it required an absolute pathname. A new book, ~c[books/misc/defopener.lisp], provides a utility creating a theorem that equates a term with its simplification. ACL2 now provides limited support for the Common Lisp primitive ~c[FLET], which supports local function bindings. ~l[flet]. Thanks to Warren Hunt for requesting this feature. Added a definition of ~ilc[boole$], a close analogue of Common Lisp function ~c[boole]. Thanks to Bob Boyer for providing an initial implementation. ~st[BUG FIXES] Fixed ~ilc[defstobj] to inhibit a potentially useless theory warning. Fixed a bug in the application of ~ilc[certify-book] to relative pathnames for files in other than the current directory. Thanks to Amr Helmy for bringing this bug to our attention. Fixed a bug in ~c[:]~ilc[pl] and ~c[:]~ilc[pr] for displaying rules of class ~c[:]~ilc[meta]. Thanks to Jared Davis for finding this bug and providing a fix. Formerly, ~ilc[set-default-backchain-limit] was not a legal event form for ~ilc[encapsulate] forms and ~il[books]. This has been fixed. Thanks to Robert Krug and Sandip Ray for bringing this bug to our attention. Fixed the handling of ~il[hints] in ~il[proof-checker] commands for the prover, such as ~c[bash] ~-[] ~pl[proof-checker-commands] ~-[] so that the user can override the default settings of hints, in particular of ~c[:do-not] and ~c[:do-not-induct] hints attached to ~c[\"Goal\"]. This fix also applies to the distributed book ~c[misc/bash.lisp], where Robert Krug noticed that he got an error with ~c[:hints ((\"Goal\" :do-not '(preprocess)))]; we thank Robert for pointing out this problem. Fixed a bug in handling of ~il[stobj]s occurring in guards of functions whose ~il[guard]s have been verified. In such cases, a raw Lisp error was possible when proving theorems about non-''live'' ~il[stobj]s. We thank Daron Vroon for sending us an example that highlighted this bug. The following (simpler) example causes such an error in previous versions of ACL2. ~bv[] (defstobj st fld) (defun foo (st) (declare (xargs :stobjs st :guard (fld st))) st) (thm (equal (foo '(3)) '(3))) ~ev[] The ~il[dmr] feature for dynamic monitoring of rewrites had a bug, where the file used for communicating with emacs was the same for all users, based on who built the ACL2 executable image. This has been fixed. Thanks to Robert Krug for bringing this bug to our attention. Fixed a bug in some warnings, in particular the warning for including an uncertified book, that was giving an incorrect warning summary string. Inclusion of uncertified books erroneously re-used ~ilc[make-event] expansions that were stored in stale ~il[certificate]s. This is no longer the case. Thanks to Jared Davis for bringing this bug to our attention. Fixed a bug that was disallowing calls of ~ilc[with-output] in ~il[events] that were executing before calling ~ilc[certify-book]. Modified the functionality of ~c[binop-table] so other than binary function symbols are properly supported (hence with no action based on right-associated arguments). ~l[add-binop]. Fixed small ~il[proof-checker] issues related to packages. Emacs commands ~c[ctrl-t d] and ~c[ctrl-t ctrl-d] now work properly with colon (`~c[:]') and certain other punctuation characters. The ~c[p-top] command now prints ``~c[***]'' regardless of the current package. Fixed a bug that allowed ~ilc[certify-book] to succeed without specifying value ~c[t] for keyword argument ~c[:skip-proofs-okp], even with ~ilc[include-book] ~il[events] in the certification ~il[world] depending on events executed under ~ilc[skip-proofs]. Improved ~ilc[show-accumulated-persistence] in the following two ways. Thanks to Robert Krug and Bill Young for requesting these improvements and for providing useful feedback. ~bq[] o It can provide more complete information when aborting a proof. o The ~c[:frames] reported for a rule are categorized as ``useful'' and ``useless'' according to whether they support ``useful'' or ``useless'' ~c[:tries] of that rule, respectively. ~l[accumulated-persistence] for further explanation.~eq[] Modified ~ilc[make-event] so that the reported time and warnings include those from the expansion phase. In analogy with ~ilc[encapsulate] and ~ilc[progn], the rules reported still do not include those from subsidiary events (including the expansion phase). A related change to ~ilc[ld] avoids resetting summary information (time, warnings) with each top-level form evaluation; ~il[events] already handle this information themselves. Fixed ~ilc[set-inhibit-output-lst] so that all warnings are inhibited when ~c[warning!] but not ~c[warning] is included in the list. Formerly, only soundness-related warnings were inhibited in this case. Thanks to Eric Smith for bringing this bug to our attention. Distributed directory ~c[doc/HTML/] now again includes installation instructions (which was missing in Version_3.2.1), in ~c[doc/HTML/installation/installation.html]. Some fixes have been made for ~il[proof-tree] support. ~bq[] o ~il[Proof-tree] output is no longer inhibited automatically during ~ilc[certify-book], though it continues to be inhibited by default (i.e., ACL2 continues to start up as though ~ilc[set-inhibit-output-lst] has been called with argument ~c['(proof-tree)]). o Fixed a bug in Xemacs support for ~il[proof-tree] help keys ~c[C-z h] and ~c[C-z ?]. o Fixed a bug in ~il[proof-tree]s that was failing to deal with the case that a goal pushed for proof by induction is subsumed by such a goal to be proved later. Now, the proof-tree display regards such subsumed goals as proved, as is reported in the theorem prover's output.~eq[] Fixed a bug that was disallowing ~ilc[value-triple] forms inside ~ilc[encapsulate] forms in a certification ~il[world] (~pl[portcullis]). If the ~c[:load-compiled-file] argument of a call of ~ilc[include-book] is ~c[:comp], then an existing compiled file will be loaded, provided it is more recent than the corresponding book (i.e., ~c[.lisp] file). A bug was causing the compiled file to be deleted and then reconstructed in the case of ~c[:comp], where this behavior was intended only for ~c[:comp!]. Fixed a bug that was avoiding compilation of some executable counterparts (sometimes called ``*1* functions'') during ~ilc[certify-book], and also during ~ilc[include-book] with ~c[:load-compiled-file] value of ~c[:comp] or ~c[:comp!]). Thanks to Eric Smith for sending a small example to bring this bug to our attention. Incorporated a fix from Eric Smith for a typo (source function ~c[ancestors-check1]) that could cause hard Lisp errors. Thanks, Eric! Fixed the following issues with packages and book ~il[certificate]s. ~l[hidden-death-package] if you are generally interested in such issues, and for associated examples, see comments on ``Fixed the following issues with packages'' in ~c[note-3-3] in the ACL2 source code. ~bq[] o Reduced the size of ~c[.cert] files by eliminating some unnecessary ~ilc[defpkg] events generated for the ~il[portcullis]. o Fixed a bug that has caused errors when reading symbols from a ~il[portcullis] that are in undefined packages defined in locally included books. o Fixed a bug that could lead to failure of ~ilc[include-book] caused by a subtle interaction between ~ilc[set-ignore-ok] and ~ilc[defpkg] events generated for the ~il[portcullis] of a ~il[certificate].~eq[] ~st[PROVER ALGORITHM ENHANCEMENTS] Non-linear arithmetic (~pl[non-linear-arithmetic]) has been improved to be more efficient or more powerful in some cases. Thanks to Robert Krug for contributing these improvements. Improved certain (so-called ``~ilc[type-set]'') reasoning about whether or not expressions denote integers. Thanks to Robert Krug for contributing code to implement this change, along with examples illustrating its power that are now distributed in the book ~c[books/misc/integer-type-set-test.lisp]. Improved ACL2's heuristics for relieving hypotheses, primarily to use linear reasoning on conjuncts and disjuncts of the test of an ~ilc[if] expression. For example, given a hypothesis of the form ~c[(if (or term1 term2) ...)], ACL2 will now use linear reasoning to attempt to prove both ~c[term1] and ~c[term2], not merely for ~c[term2]. Thanks to Robert Krug for supplying examples illustrating the desirability of such an improvement and for useful discussions about the fix. Made a slight heuristic change, so that when a hypothesis with ~ilc[let] or ~ilc[mv-let] subterms (i.e. ~ilc[lambda] subterms) rewrites to ~c[t], then that hypothesis is necessarily eliminated. Thanks to Jared Davis for sending an example that led us to develop this change, and thanks to Robert Krug for a helpful discussion. ~st[MISCELLANEOUS] Added documentation on how to use ~ilc[make-event] to avoid duplicating expensive computations, thanks to Jared Davis. ~l[using-tables-efficiently]. Modified the error message for calls of undefined functions to show the arguments. Thanks to Bob Boyer for requesting this enhancement. Modified utilies ~c[:]~ilc[pr], ~c[:]~ilc[pr!], ~c[:]~ilc[pl], and ~c[:]~ilc[show-bodies] to incorporate code contributed by Jared Davis. That code defines low-level source functions ~c[info-for-xxx] that collect information about rules, which is thus available to advanced users. Dynamic monitoring of rewrites (~pl[dmr]) has been improved in the following ways, as suggested by Robert Krug. ~bq[] o Some stale entries from the rewrite stack are no longer printed, in particular above ~c[ADD-POLYNOMIAL-INEQUALITIES]. o An additional rewrite stack entry is made when entering non-linear arithmetic (~pl[non-linear-arithmetic]). o An ~c[ADD-POLYNOMIAL-INEQUALITIES] entry is printed with a counter, to show how often this process is called.~eq[] Modified ~ilc[save-exec] so that the newly-saved image will have the same raw Lisp package as the existing saved image. This is a very technical change that will likely not impact most users; for example, the package in the ACL2 read-eval-print loop (~pl[lp]) had already persisted from the original to newly-saved image. Thanks to Jared Davis for suggesting this change. Changed ~ilc[make-event] expansion so that changes to ~ilc[set-saved-output], ~ilc[set-print-clause-ids], ~c[set-fmt-soft-right-margin], and ~c[set-fmt-hard-right-margin] will persist after being evaluated during ~c[make-event] expansion. (Specifically, ~c[*protected-system-state-globals*] has been modified; ~pl[make-event-details].) Thanks to Jared Davis for bringing this issue to our attention. Output from the ~il[proof-checker] is now always enabled when invoking ~ilc[verify], even if it is globally inhibited (~pl[set-inhibit-output-lst]). Improved the message printed when an ~c[:induct] hint fails, to give more information in some cases. Thanks to Rex Page for suggesting where an improvement could be made and providing useful feedback on an initial improvement. Added a warning for ~il[congruence] rules (~pl[defcong]) that specify ~ilc[iff] as the second equivalence relation when ~ilc[equal] can be used instead. Those who heed these warnings can eliminate certain subsequent ~ilc[double-rewrite] warnings for ~il[rewrite] rules with conclusions of the form ~c[(iff term1 term2)], and hence implicitly for Boolean conclusions ~c[term1] that are interpreted as ~c[(iff term1 t)]. Thanks to Sarah Weissman for sending us an example that highlighted the need for such a warning. Modified macro ~c[:]~ilc[redef!] (which is for system implementors) so that it eliminates untouchables. Several improvements have been made to the experimental hons/memoization version of ACL2. ~l[hons-and-memoization]. The distributed books directory, ~c[(@ distributed-books-dir)], is now printed in the start-up message. ~/~/") (deflabel |NOTE-3-3(R)| :doc ":Doc-Section release-notes ACL2 Version 3.3(r) (November, 2007) Notes~/ ~/ Please also ~pl[note-3-3] for changes to Version 3.3 of ACL2. ~/ ") (deflabel note-3-4 ; Here is the example promised in :doc note-3-4 for a soundness bug, namely a ; proof of nil based on the use of mbe inside an encapsulate event. The proof ; goes through for this example in Version_3.3 but not in Version_3.4. ; (encapsulate ; ((f (x) t)) ; ; (local (defun f (x) ; (declare (xargs :guard t)) ; x)) ; ; (defun g (x) ; (declare (xargs :guard t)) ; (mbe :logic (f x) ; :exec x))) ; ; (defthm g-3-is-3 ; (equal (g 3) 3) ; :rule-classes nil) ; ; (defthm f-3-is-3 ; (equal (f 3) 3) ; :hints (("Goal" ; :in-theory (disable (g)) ; :use g-3-is-3))) ; ; (defun foo (x) ; (declare (ignore x)) ; 4) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use ((:functional-instance f-3-is-3 ; (f foo))))) ; :rule-classes nil) ; Fixed a hard error during an error message caused by the use of the wrong ~@ ; argument in unknown-binding-msg. Thanks to Eric Smith for pointing this out. ; The source function for "Fixed bugs in" proof-checker code was ; pc-relieve-hyp. ; The following events prove in GCL and Allegro builds of ACL2 3.3. Both the ; new trace and fixes to the existing replacements for trace (i.e., now using ; :native t) will fix this. ; ; (defun f (x y) ; (mv x y)) ; ; (defthm fact-about-f ; (equal (f 3 4) ; (mv 3 4)) ; :rule-classes nil) ; ; (defttag :trace) ; ; (trace$ (f :exit ; (mv-let (u v) ; (mv 7 8) ; (equal u v)))) ; ; ; But the call of mv in the :exit condition messes up multiple-value ; ; handling by ACL2, and now (f 3 4) is (mv 3 8). ; ; (defthm contradiction ; nil ; :hints (("Goal" :use fact-about-f)) ; :rule-classes nil) ; Fixed error message at the end of translate-hint-expression (thanks to Robert ; Krug for the fix) to acknowledge that STATE can be used. ; Here is the example Peter Dillinger sent for the bug in the interaction of ; proof-trees with :induct hint value :otf-flg-override. The fix was to ; consider do-not-induct-otf-flg-override in the definition of aborting-p in ; the definition of increment-proof-tree. ; (start-proof-tree) ; ; (defun limit-induction-hint-fn (i) ; `(or (and (length-exceedsp (car id) ,i) ; (endp (cdadr id)) ; (eq (cddr id) 0) ; '(:computed-hint-replacement ; t ; :do-not-induct :otf-flg-override)) ; (and (> (cddr id) 20) ; '(:computed-hint-replacement ; t ; :do-not (eliminate-destructors eliminate-irrelevance ; generalize fertilize) ; :in-theory nil)))) ; ; (add-default-hints ; `(,(limit-induction-hint-fn 1))) ; ; (defun sum-from (i lst) ; (if (and (natp i) ; (< i (len lst))) ; (+ (nth i lst) (sum-from (1+ i) lst)) ; 0)) ; Fixed warning messages (error messages too actually) for computed hints, so ; that mere warnings don't say that a value is illegal. Thanks to Robert Krug ; for pointing out this problem and sending the following example: ; (in-theory (disable mv-nth eq)) ; ; (thm ; (equal (* x y) (+ x y)) ; :hints ((if stable-under-simplificationp ; '(:in-theory (enable eq)) ; nil))) ; Changed constant *primitive-event-macros* into zero-ary function ; primitive-event-macros, in support of Peter Dillinger's desire to be able to ; change this function with ttags. ; Modified GNUmakefile to use ACL2_SUFFIX for saved_acl2 suffix. ; ACL2 source macro defrec can now be used in :logic mode even when the cheap ; flag is nil. Formerly, the presence of record-error made that impossible, ; but now record-error is in :logic mode, and with guards verified by using (er ; hard? ...) instead of (er hard ...). ; Strengthened the guard for er-let*. ; Fixed walking and flying tour pages to put the gif linking to the next page ; at the top too, not just the bottom of the page. ; Sped up macroexpansion of with-output forms by changing how OR is called in ; with-output in the loop. ; Fixed a bug in :pso, specifically in its "So we now return to" messages, ; which were printing a goal that had just been proved for induction. The fix ; involved handling of of jppl-flg values in functions pop-clause-msg1 and ; pop-clause-msg. ; The bug that could cause certify-book to fail with local make-event forms was ; fixed by changing eval-event-lst so that it always concludes by setting ; state-global 'last-make-event-expansion to nil. Before that, certification ; failed for a book whose only form (other than the leading in-package) is ; (local (make-event '(defun foo (x) x))). ; Modified expansion-alist-pkg-names slightly; memory escapes just why, but ; notes suggest that it was to fix a bug reported by Eric Smith. ; Fixed a bug in the guard for find-clauses1. ; Modified acl2-traced-fns to work in versions of CCL after June 2008. ; Following Bob Boyer, added (debug 0) to the optimize form for CCL (OpenMCL) ; in acl2.lisp. ; The soundness bug in value-triple-fn could be exploited not only to prove nil ; but also to get a raw Lisp error, by way of these events at the top level or ; in a book: ; ; (defun foo (x) ; (declare (xargs :mode :program :guard t)) ; (car x)) ; (value-triple (foo 3)) ; Moved #-acl2-loop-only code in getprop-default into the logic. ; Stopped creating io-record forms whose :io-marker is other than ctx or nil; ; see comment in io-record. ; Fixed get-hints1, get-guard-hints1, and get-std-hints1 to avoid raw Lisp ; errors for :hints values not satisfying true-listp. So now we get a cleaner ; error, for example, on the following: ; (defun foo (x) (declare (xargs :hints 3)) x) ; For developers, "make" variable ACL2_SAFETY can be set in order to change the ; optimization safety setting from its default of 0; see GNUmakefile. The ; OPTIMIZE form has been changed from a DECLAIM to a PROCLAIM to support this ; change. ; Edited :doc |Other Requirements| to remove MCL from the list of supported ; Lisps. It was already the case that MCL was not included in ; installation/requirements.html. If ACL2 works in MCL, it's just luck at this ; point. ; Fixed warning "Unable to determine USER environment variable for ; dmr-file-name", which has been seen on Windows, so that it only occurs at the ; first time in a session that dmr-start is run. Also printed what to do in ; Emacs when that happens. ; Following an observation of Robert Krug, now a message "*** Note: No ; checkpoints to print. ***" is printed when a proof fails but no checkpoints ; have been generated. ; ACL2 source function trace-multiplicity now returns nil instead of 1 for ; functions not known to the ACL2 loop. ; In gag-mode, when a proof fails to produce checkpoints, we now see "*** Note: ; No checkpoints to print. ***". Thanks to Robert Krug for requesting this ; additional output. To get an infinite loop without checkpoints, to check ; this fix: ; ; (defstub foo (x) t) ; (defaxiom ax1 ; (implies (syntaxp (variablep x)) ; (equal (foo x) (foo (cons 3 x))))) ; (defaxiom ax2 (equal (foo (cons x y)) (foo y))) ; (thm (foo a) ; :hints ((and stable-under-simplificationp ; '(:computed-hint-replacement t :in-theory (enable ax2))) ; (and (not stable-under-simplificationp) ; '(:computed-hint-replacement t :in-theory (disable ax2))))) ; Jared's email leading to the expansion of *acl2-exports*, as noted below also ; led to fixing of our-abort to print in the ACL2 current-package. :Doc ":Doc-Section release-notes ACL2 Version 3.4 (August, 2008) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.3 into changes to existing features, new features, bug fixes, new and updated books, and Emacs support. Each change is described just once, though of course many changes could be placed in more than one category. ~st[CHANGES TO EXISTING FEATURES] Fixed a long-standing potential infinite loop in the rewriter. Thanks to Sol Swords for sending a concise example illustrating the looping behavior. (Those interested in details are welcome to look at the comment about loop behavior in source function ~c[rewrite-equal].) Incorporated a slight strengthening of non-linear arithmetic contributed by Robert Krug (thanks, Robert). With non-linear arithmetic enabled, the problem was essentially that ACL2 made the following ``optimization'': given inequalities ~c[(< a u)] and ~c[(< b v)], for positive rational constants ~c[a] and ~c[b] terms ~c[u] and ~c[v] of which at least one is known to be rational, infer ~c[(< (* a b) (* u v))]. Without this optimization, however, ACL2 now infers the stronger inequality obtained by direct multiplication of the two given inequalities. To see the effect of this change, submit the event ~c[(set-non-linearp t)] followed by: ~bv[] (thm (implies (and (rationalp x) (< 3 x) (rationalp y) (< 4 y)) (< 0 (+ 12 (* -4 x) (* -3 y) (* x y))))) ~ev[] The utility ~ilc[set-checkpoint-summary-limit] has been modified in several ways: it now takes a single argument (no longer takes ~ilc[state] as an argument); a natural number ~c[n] abbreviates the pair ~c[(n . n)]; the argument is no longer evaluated, but it still optionally may be quoted; and a new value, ~c[t], suppresses all printing of the checkpoint summary. Thanks to Jared Davis for suggesting most of these improvements. There was formerly a restriction on ~ilc[mbe] that the ~c[:exec] argument may not contain a call of ~ilc[mbe]. This restriction has been removed, thanks to a request from Jared Davis and Sol Swords. Thanks also to Sandip Ray, who pointed out that this restriction may have been in place in order that ~ilc[defexec] can guarantee termination using the ~c[:exec] code; its ~il[documentation] has therefore been updated to clarify this situation. Rules of class ~c[:]~ilc[rewrite] are now stored by performing certain logical simplifications on the left side of the conclusion: ~c[(prog2$ X Y)] is replaced by ~c[Y], ~c[(mbe :logic X :exec Y)] is replaced by ~c[X] (more precisely, the analogous change is made to the generated call of ~ilc[must-be-equal]); and ~c[(the TYPE X)] is replaced by ~c[X] (again, the change is actually made on the macroexpanded form). Thanks to Jared Davis and Sol Swords for requesting this change. An analogous change has also been made for rules of class ~c[:]~ilc[forward-chaining]. The ~ilc[trace$] utility has been reimplemented to work independently of the underlying Lisp trace. It thus works the same for every host Lisp, except as provided by an interface to the underlying host Lisp trace (the ~c[:native] option). Note that the host Lisp trace continues to be modified for GCL, Allegro CL, and CCL (OpenMCL); ~pl[trace]. ~l[trace$] for updated detailed documentation on tracing options, many of which are new, for example an ~c[:evisc-tuple] option that can be set to ~c[:no-print] if you want the function traced without the usual entry and exit printing. The previous ~ilc[trace$] had some issues, including the following, which have all been fixed. Thanks to Peter Dillinger for assistance in determining desired functionality of the new ~ilc[trace$] and for helping to test it. ~bq[] Recursive calls were not always shown in the trace for two reasons. (1) Compiler inlining could prevent recursive calls from being shown in the trace, in particular in CCL (OpenMCL). Thanks to Jared Davis and Warren Hunt for pointing out this issue and requesting a fix, and to Bob Boyer and Gary Byers for relevant helpful discussions. (2) ACL2's algorithm for producing executable counterparts prevented tracing of recursive calls even after ~c[(set-guard-checking :none)]. Thanks to Peter Dillinger for requesting a fix. It was possible to exploit a bug in the interaction of multiple values and trace to prove a contradiction. An example is in a comment in ~c[(deflabel note-3-4 ...)] in the ACL2 source code. Certain large structures could cause expensive computations for printing even when a ~c[:cond] condition was specified and evaluated to ~c[nil]. ~ilc[Trace!] now suppresses printing of the event summary, and returns the value that would be returned (if there is an active trust tag) by the corresponding call of ~ilc[trace$]. Some bugs have been fixed in the underlying native trace installed by ACL2 for GCL, Allegro CL, and CCL (OpenMCL), including the following. In GCL it had been impossible to use the variable ~c[ARGLIST] in a ~c[:cond] expression. In Allegro CL and CCL, a ~ilc[trace$] bug mishandled tracing non-ACL2 functions when directives such as ~c[:entry] and ~c[:exit] were supplied. GCL trace now hides the world even when tracing non-ACL2 functions. Tracing in CCL no longer causes a Lisp error when untracing a traced function defined outside the ACL2 loop; for example ~c[(trace$ len1)] followed by ~c[(untrace$ len1)] no longer causes an error.~eq[] The macro ~c[wet] has been changed, for the better we think. ~pl[wet]. The generation of goals for ~il[forcing-round]s has been changed to avoid dropping assumptions formerly deemed ``irrelevant''. (A simple example may be found in a comment in source function ~c[unencumber-assumption], source file ~c[prove.lisp].) Thanks to Jared Davis for sending us an example that led us to make this change. Modified the implementation of ~ilc[make-event] so that in the ~il[certificate] of a book, ~ilc[local] events arising from ~ilc[make-event] forms are elided. For example, if ~c[(make-event )] expands to ~c[(local )], then where the latter had been stored in the certificate, now instead ~c[(local (value-triple :ELIDED))] will be stored. Thanks to Eric Smith for requesting this improvement. He has reported that a preliminary version of this improvement shrunk a couple of his ~c[.cert] files from perhaps 40MB each to about 140K each. Now, a ~ilc[table] event that sets the entire table, ~c[(table tbl nil alist :clear)], is redundant (~pl[redundant-events]) when the supplied ~c[alist] is equal to the current value of the table. Thanks to Peter Dillinger for requesting this change. The event constructor ~ilc[progn!] now returns the value that is returned by evaluation of its final form if no error occurs, except that it still returns ~c[nil] if the that final evaluation leaves ACL2 in raw-mode. ~c[:]~ilc[Pso] and ~c[:]~ilc[psog] have been improved so that they show the key checkpoint summary at the end of a failed proof. (For a discussion of key checkpoints, ~pl[set-gag-mode].) As a result, a call of ~ilc[set-checkpoint-summary-limit] now affects subsequent evaluation of ~c[:]~ilc[pso] and ~c[:]~ilc[psog]. In particular, you no longer need to reconstruct a proof (by calling ~ilc[thm] or ~ilc[defthm]) in order to see key checkpoints that were omitted due to the limit; just call ~ilc[set-checkpoint-summary-limit] and then run ~c[:pso] or ~c[:psog]. The ~il[proof-checker] behaves a little differently under ~il[gag-mode]. Now, proof-checker commands that call the theorem prover to create new proof-checker goals, such as ~c[bash] and ~c[induct] (~pl[proof-checker-commands]), will show key checkpoints when in ~il[gag-mode]. As before, proof-checker commands ~c[pso] and ~c[pso!] (and now, also ~c[psog]) ~-[] ~pl[pso], ~pl[psog], and ~pl[pso!] ~-[] can then show the unedited proof log. However, unlike proof attempts done in the ACL2 loop, such proof attempts will not show a summary of key checkpoints at the end, because from a prover perspective, all such goals were considered to be temporarily ``proved'' by giving them ``byes'', to be dispatched by later proof-checker commands. A little-known feature had been that a ~il[measure] of ~c[0] was treated as though no measure was given. This has been changed so that now, a ~il[measure] of ~c[nil] is treated as though no measure was given. Expanded ~c[*acl2-exports*] to include every documented symbol whose name starts with ~c[\"SET-\"]. Thanks to Jared Davis for remarking that ~ilc[set-debugger-enable] was omitted from ~c[*acl2-exports*], which led to this change. The ~il[trace] mechanism has been improved so that the ~c[:native] and ~c[:multiplicity] options can be used together for Lisps that support the trace ~c[:exit] keyword. These Lisps include GCL and Allegro CL, whose native trace utilities have been modified for ACL2. For SBCL and CCL (OpenMCL), which use the built-in Lisp mechanism for returning multiple values in ACL2 (~pl[mv]), the use of ~c[:multiplicity] with ~c[:native] remains unnecessary and will be ignored. In support of this change, the modification of native Allegro CL tracing for ACL2 was fixed to handle ~c[:exit] forms correctly that involve ~ilc[mv]. ~st[NEW FEATURES] The command ~c[:]~ilc[redef!] is just like ~c[:]~ilc[redef], but prints a warning rather than doing a query. The old version of ~c[:redef!] was for system hackers and has been renamed to ~c[:]~ilc[redef+]. Introduced a new utility for evaluating a function call using the so-called executable counterpart ~-[] that is, executing the call in the logic rather than in raw Lisp. ~l[ec-call]. Thanks to Sol Swords for requesting this utility and participating in its high-level design. ~l[print-gv] for a new utility that assists with debugging guard violations. Thanks to Jared Davis for requesting more tool assistance for debugging guard violations. Improved the guard violation error message to show the positions of the formals, following to a suggestion of Peter Dillinger. Added new ~ilc[guard-debug] capability to assist in debugging failed attempts at ~il[guard] verification. ~l[guard-debug]. Thanks to Jared Davis for requesting a tool to assist in such debugging and to him, Robert Krug, and Sandip Ray for useful discussions. New utilities provide the formula to be proved by ~ilc[verify-guards]. ~l[verify-guards-formula] and ~pl[guard-obligation], Thanks to Mark Reitblatt for making a request leading to these utilities. These utilities can be applied to a term, not just an event name; thanks to Peter Dillinger for correspondence that led to this extension. A new utility causes runes to be printed as lists in proof output from simplification, as is done already in proof summaries. ~l[set-raw-proof-format]. Thanks to Jared Davis for requesting this utility. An experimental capability allows for parallel evaluation. ~l[parallelism]. Thanks to David Rager for providing an initial implementation of this capability. Defined ~ilc[xor] in analogy to ~ilc[iff]. Thanks to Bob Boyer, Warren Hunt, and Sol Swords for providing this definition. Improved distributed file ~c[doc/write-acl2-html.lisp] so that it can now be used to build HTML documentation files for ~il[documentation] strings in user ~il[books]. See the comment in the definition of macro ~c[acl2::write-html-file] at the end of that file. Thanks to Dave Greve and John Powell for requesting this improvement. It is now possible to specify ~c[:]~ilc[hints] for non-recursive function definitions (which can be useful when definitions are automatically generated). ~l[set-bogus-defun-hints-ok]. Thanks to Sol Swords for requesting such a capability. Keyword argument ~c[:dir] is now supported for ~ilc[rebuild] just as it has been for ~ilc[ld]. We relaxed the criteria for functional substitutions, so that a function symbol can be bound to a macro symbol that corresponds to a function symbol in the sense of ~ilc[macro-aliases-table]. So for example, a functional substitution can now contain the doublet ~c[(f +)], where previously it would have been required instead to contain ~c[(f binary-+)]. We now allow arbitrary packages in raw mode (~pl[set-raw-mode]) ~-[] thanks to Jared Davis for requesting this enhancement ~-[] and more than that, we allow arbitrary Common Lisp in raw mode. Note however that for arbitrary packages, you need to be in raw mode when the input is read, not just when the input form is evaluated. Two new keywords are supported by the ~ilc[with-output] macro. A ~c[:gag-mode] keyword argument suppresses some prover output as is done by ~ilc[set-gag-mode]. Thanks to Jared Davis for asking for a convenient way to set ~il[gag-mode] inside a book, in particular perhaps for a single theorem; this keyword provides that capability. A ~c[:stack] keyword allows sub-~il[events] of ~ilc[progn] or ~ilc[encapsulate] to ``pop'' the effect of a superior ~ilc[with-output] call. Thanks to Peter Dillinger for requesting such a feature. ~l[with-output]. The command ~ilc[good-bye] and its aliases ~ilc[exit] and ~ilc[quit] now all take an optional status argument, which provides the Unix exit status for the underlying process. Thanks to Florian Haftmann for sending a query to the ACL2 email list that led to this enhancement. Keyword commands now work for macros whose argument lists have lambda list keywords. For a macro with a ~c[lambda] list keyword in its argument list, the corresponding keyword command reads only the minimum number of required arguments. ~l[keyword-commands]. It is now legal to ~ilc[declare] variables ~c[ignorable] in ~ilc[let*] forms, as in ~c[(let* ((x (+ a b)) ...) (declare (ignorable x)) ...)]. Thanks to Jared Davis for requesting this enhancement. Added a warning when more than one hint is supplied explicitly for the same goal. It continues to be the case that only the first hint applicable to a given goal will be applied, as specified in the user-supplied list of ~c[:hints] followed by the ~ilc[default-hints-table]. Thanks to Mark Reitblatt for sending a question that led both to adding this clarification to the ~il[documentation] and to adding this warning. You may now use ~ilc[set-non-linear], ~ilc[set-let*-abstraction], ~c[set-tainted-ok], and ~ilc[set-ld-skip-proofs] in place of their versions ending in ``~c[p]''. Thanks to Jared Davis for suggesting consideration of such a change. All ``~c[set-]'' utilites now have a version without the final ``~c[p]'' (and most do not have a version with the final ``~c[p]''). Added a \"Loop-Stopper\" warning when a ~c[:]~ilc[rewrite] rule is specified with a ~c[:]~ilc[loop-stopper] field that contains illegal entries that will be ignored. Thanks to Jared Davis for recommending such a warning. Added a substantial documentation topic that provides a beginner's guide to the use of quantification with ~ilc[defun-sk] in ACL2. Thanks to Sandip Ray for contributing this guide, to which we have made only very small modifications. ~l[quantifier-tutorial]. ~ilc[Defun-sk] now allows the keyword option ~c[:strengthen t], which will generate the extra constraint that that is generated for the corresponding ~c[defchoose] event; ~pl[defchoose]. Thanks to Dave Greve for suggesting this feature. ~st[BUG FIXES] Fixed a soundness bug related to the use of ~ilc[mbe] inside ~ilc[encapsulate] events. An example proof of ~c[nil] (before the fix) is in a comment in ~c[(deflabel note-3-4 ...)] in the ACL2 source code. We therefore no longer allow calls of ~ilc[mbe] inside ~ilc[encapsulate] events that have non-empty ~il[signature]s. Fixed a bug related to the definition of a function supporting the macro ~ilc[value-triple]. Although this bug was very unlikely to affect any user, it could be carefully exploited to make ACL2 unsound: ~bv[] (defthm fact (equal (caadr (caddr (value-triple-fn '(foo 3) nil nil))) 'value) ; but it's state-global-let* in the logic :rule-classes nil) (defthm contradiction nil :hints ((\"Goal\" :use fact :in-theory (disable (value-triple-fn)))) :rule-classes nil) ~ev[] Non-~ilc[LOCAL] definitions of functions or macros are no longer considered redundant with built-ins when the built-ins have special raw Lisp code, because ACL2 was unsound without this restriction! A comment about redundant definitions in source function ~c[chk-acceptable-defuns] shows how one could prove ~c[nil] without this new restriction. Note that system utility ~c[:]~ilc[redef+] removes this restriction. Although ACL2 already prohibited the use of certain built-in ~c[:]~ilc[program] mode functions for ~ilc[verify-termination] and during macroexpansion, we have computed a much more complete list of functions that need such restrictions, the value of constant ~c[*primitive-program-fns-with-raw-code*]. Modified what is printed when a proof fails, to indicate more clearly which event failed. Fixed a problem with ~ilc[dmr] in CCL (OpenMCL) that was causing a raw Lisp break after an interrupt in some cases. Thanks to Gary Byers for a suggestion leading to this fix. Fixed bugs in ~il[proof-checker] code for dealing with free variables in hypotheses. Upon an abort, the printing of ~ilc[pstack] and ~il[gag-mode] summary information for other than GCL was avoided when inside a call of ~ilc[ld]. This has been fixed. (Windows only) Fixed bugs for ACL2 built on SBCL on Windows, including one that prevented ~ilc[include-book] parameters ~c[:dir :system] from working, and one that prevented certain compilation. Thanks to Peter Dillinger for bringing these to our attention and supplying a fix for the second. Thanks also to Andrew Gacek for bringing ~ilc[include-book] issues to our attention. Also, fixed writing of file ~c[saved_acl2] at build time so that for Windows, Unix-style pathnames are used. Fixed a hard Lisp error that could occur with keywords as ~ilc[table] names, e.g., ~c[(table :a :a nil :put)]. Thanks to Dave Greve for bringing this problem to our attention and providing this example. Fixed handling of ~c[:OR] ~il[hints] so that proof attempts under an ~c[:OR] hint do not abort (reverting to induction on the original input conjecture) prematurely. Thanks to Robert Krug for pointing out this problem and pointing to a possible initial fix. (SBCL and CLISP only) It is now possible to read symbols in the ~c[\"COMMON-LISP\"] package inside the ACL2 command loop (~pl[lp]). This could cause a raw Lisp error in previous versions of ACL2 whose host Common Lisp was SBCL or CLISP. Thanks to Peter Dillinger for bringing this issue to our attention. Fixed a bug that was preventing certain ~il[hints], such as ~c[:do-not] hints, from being used after the application of an ~c[:or] hint. Thanks to Robert Krug for bringing this bug to our attention. (Hons version only) Fixed a bug in the interaction of ~ilc[memoize] (~ilc[hons] version only) with event processing, specifically in interaction with failures inside a call of ~ilc[progn] or ~ilc[encapsulate]. Thanks to Jared Davis for bringing this bug to our attention and sending an example. A simplified example may be found in a comment in source function ~c[table-cltl-cmd], source file ~c[history-management.lisp]; search for ``Version_3.3'' there. Fixed ~ilc[cw-gstack] so that its ~c[:evisc-tuple] is applied to the top clause, instead of using ~c[(4 5 nil nil)] in all cases. If no ~c[:evisc-tuple] is supplied then ~c[(term-evisc-tuple t state)] is used for the top clause, as it is already used for the rest of the stack. Fixed a bug in the interaction of ~il[proof-tree]s with ~c[:induct] hint value ~c[:otf-flg-override]. Thanks to Peter Dillinger for reporting this bug and sending an example that evokes it. Fixed bugs in ~c[:]~ilc[pr] and ~ilc[find-rules-of-rune] for the case of rule class ~c[:]~ilc[elim]. Thanks to Robert Krug and Jared Davis for bringing these related bugs to our attention. Improved failure messages so that the key checkpoints are printed only once when there is a proof failure. Formerly, a proof failure would cause the key checkpoints to be printed for every ~ilc[encapsulate] or ~ilc[certify-book] superior to the proof attempt. Fixed a bug in generation of ~il[guard]s for calls of ~ilc[pkg-witness]. Thanks to Mark Reitblatt for sending an example showing this bug. The bug can be in play when you see the message: ``HARD ACL2 ERROR in MAKE-LAMBDA-APPLICATION: Unexpected unbound vars (\"\")''. A distillation of Mark's example that causes this hard error is as follows. ~bv[] (defun foo (x) (declare (xargs :guard t)) (let ((y x)) (pkg-witness y))) ~ev[] The ~ilc[cond] macro now accepts test/value pairs of the form ~c[(T val)] in other than the last position, such as the first such pair in ~c[(cond (t 1) (nil 2) (t 3))]. Thanks to Jared Davis for sending this example and pointing out that ACL2 was sometimes printing goals that have such a form, and hence cannot be submitted back to ACL2. A few macros corresponding to ~ilc[cond] in some books under ~c[books/rtl] and ~c[books/bdd] were similarly modified. (A second change will probably not be noticeable, because it doesn't affect the final result: singleton ~ilc[cond] clauses now generate a call of ~ilc[or] in a single step of macroexpansion, not of ~ilc[if]. For example, ~c[(cond (a) (b x) (t y))] now expands to ~c[(OR A (IF B X Y))] instead of ~c[(IF A A (IF B X Y))]. See the source code for ~c[cond-macro] for a comment about this change.) Fixed a bug in the interaction of ~il[proof-checker] command ~c[DV], including numeric (``diving'') commands, with the ~ilc[add-binop] event. Specifically, if you executed ~c[(add-binop mac fn)] with ~c[fn] having arity other than 2, a ~il[proof-checker] command such as 3 or ~c[(dv 3)] at a call of ~c[mac] could have the wrong effect. We also fixed a bug in diving with ~c[DV] into certain ~c[AND] and ~c[OR] calls. Thanks for Mark Reitblatt for bringing these problems to our attention with helpful examples. Fixed a couple of bugs that were causing an error, ``HARD ACL2 ERROR in RENEW-NAME/OVERWRITE''. Thanks to Sol Swords for bringing the first of these bugs to our attention. Fixed a bug that could cause ~ilc[certify-book] to fail in certain cases where there are ~ilc[local] ~ilc[make-event] forms. Fixed a bug in ~ilc[start-proof-tree] that could cause Lisp to hang or produce an error. Thanks to Carl Eastlund for sending an example to bring this bug to our attention. Fixed a bug in the proof output, which was failing to report cases where the current goal simplifies to itself or to a set including itself (~pl[specious-simplification]). Fixed a bug in ~ilc[with-prover-time-limit] that was causing a raw Lisp error for a bad first argument. Thanks to Peter Dillinger for pointing out this bug. The following was claimed in ~c[:doc] ~ilc[note-3-3], but was not fixed until the present release:~nl[] Distributed directory ~c[doc/HTML/] now again includes installation instructions, in ~c[doc/HTML/installation/installation.html]. In certain Common Lisp implementations ~-[] CCL (OpenMCL) and LispWorks, at least ~-[] an interrupt could leave you in a break such that quitting the break would not show the usual summary of key checkpoints. This has been fixed. ~st[NEW AND UPDATED BOOKS] Updated ~c[books/clause-processors/SULFA/] with a new version from Erik Reeber; thanks, Erik. Added new books directory ~c[tools/] from Sol Swords. See ~c[books/tools/Readme.lsp] for a summary of what these books provide. The distributed book ~c[books/misc/file-io.lisp] includes a new utility, ~c[write-list!], which is like ~c[write-list] except that it calls ~ilc[open-output-channel!] instead of ~ilc[open-output-channel]. Thanks to Sandip Ray for requesting this utility and assisting with its implementation. Added ~c[record-update] macro supplied by Sandip Ray to distributed book ~c[books/misc/records.lisp]. Sandip Ray has contributed books that prove soundness and completeness of different proof strategies used in sequential program verification. Distributed directory ~c[books/proofstyles/] has three new directories comprising that contribution: ~c[soundness/], ~c[completeness/], and ~c[counterexamples/]. The existing ~c[books/proofstyles/] directory has been moved to its subdirectory ~c[invclock/]. Jared Davis has contributed a profiling utility for ACL2 built on CCL (OpenMCL). See ~c[books/misc/oprof.lisp]. Thanks, Jared. ACL2 utilities ~ilc[getprop] and ~ilc[putprop] take advantage of under-the-hood Lisp (hashed) property lists. The new book ~c[books/misc/getprop.lisp] contains an example showing how this works. Added the following new book directories: ~c[books/paco/], which includes a small ACL2-like prover; and ~c[books/models/jvm/m5], which contains the definition of one of the more elaborate JVM models, M5, along with other files including JVM program correctness proofs. See files ~c[Readme.lsp] in these directories, and file ~c[README] in the latter. Added ~i[books] about sorting in ~c[books/sorting]. See ~c[Readme.lsp] in that directory for documentation. Added book ~c[books/misc/computed-hint-rewrite.lisp] to provide an interface to the rewriter for use in computed hints. Thanks to Jared Davis for requesting this feature. Jared Davis has provided a pseudorandom number generator, in ~c[books/misc/random.lisp]. Robert Krug has contributed a new library, ~c[books/arithmetic-4/], for reasoning about arithmetic. He characterizes it as being more powerful than its predecessor, ~c[books/arithmetic-3/], and without its predecessor's rewriting loops, but significantly slower than its predecessor on some theorems. Incorporated changes from Peter Dillinger to verify guards for functions in ~c[books/ordinals/lexicographic-ordering.lisp] (and one in ~c[ordinal-definitions.lisp] in that directory). A new directory, ~c[books/hacking/], contains a library for those who wish to use trust tags to modify or extend core ACL2 behavior. Thanks to Peter Dillinger for contributing this library. Obsolete version ~c[books/misc/hacker.lisp] has been deleted. Workshop contribution ~c[books/workshops/2007/dillinger-et-al/code/] is still included with the workshops/ tar file, but should be considered deprecated. In ~c[books/make-event/assert.lisp], changed ~c[assert!] and ~c[assert!-stobj] to return ~c[(value-triple :success)] upon success instead of ~c[(value-triple nil)], following a suggestion from Jared Davis. ~st[EMACS SUPPORT] Changed ~c[emacs/emacs-acl2.el] so that the fill column default (for the right margin) is only set (still to 79) in lisp-mode. Modified Emacs support in file ~c[emacs/emacs-acl2.el] so that names of events are highlighted just as ~ilc[defun] has been highlighted when it is called. Search in the above file for ~c[font-lock-add-keywords] for instructions on how to eliminate this change. The name of the temporary file used by some Emacs utilities defined in file ~c[emacs/emacs-acl2.el] has been changed to have extension ~c[.lsp] instead of ~c[.lisp]; thus it is now ~c[temp-emacs-file.lsp]. Also, `make' commands to `clean' books will delete such files (specifically, ~c[books/Makefile-generic] has been changed to delete ~c[temp-emacs-file.lsp]). ~/~/") (deflabel |NOTE-3-4(R)| :doc ":Doc-Section release-notes ACL2 Version 3.4(r) (August, 2008) Notes~/ ~/ Please also ~pl[note-3-4] for changes to Version 3.4 of ACL2. Fixed makefiles, ~c[books/nonstd/Makefile] and ~c[GNUmakefile]. The old set-up seemed to work fine as long as all books certified, but it was really broken, for example only certifying some of the books in ~c[books/nonstd/nsa/], and then only when required by books in other directories. Also fixed the ``~c[clean]'' target to clean links rather than to make links. ~/ ") (deflabel note-3-5 ; See the long comment just after this (deflabel note-3-5 ...) for a discussion ; of changes made March 9-16 for more efficient handling of certificates, etc. ; Added ignorable declaration to (expansion of) er-progn, anticipating ; optimizations in GCL 2.7.0 when it is ready. (Camm Maguire reported warnings ; that er-progn-not-to-be-used-elsewhere-val is unused.) ; Replaced sloop::sloop by loop. ; :Doc at the terminal had sometimes suggested using :more when there's nothing ; more to print. This has been fixed. Thanks to Bob Boyer for bringing this ; issue to our attention. ; Regarding "~il[Gag-mode] now suppresses some messages" below: Search for the ; following comment to find code to modify if we want to undo this change: ; ; Suppress printing for :OR splits; see also other comments with this header. ; Regarding "sped up ~il[guard] generation for some functions", the change was ; in built-in-clausep to delay the call to trivial-clause-p. ; Note that we changed set-debugger-enable-fn so that it no longer sets ; *debugger-hook*. Instead, our-abort is always called (in other than non-ansi ; GCL) but if the debugger is enabled, then only print-proof-tree-finish (along ; perhaps with print-call-history) is invoked. ; For hons version: Fixed typos 'current-acl2-wrld in function ; memoize-table-chk (should have been 'current-acl2-world), which has no ; functional impact but might improve performance of memoize commands. ; The item "Added a restriction in the linear arithmetic procedure for ; deleting" relates to a change to poly-weakerp, which necessitated (for ; non-linear arithmetic) a corresponding change to bounds-poly-with-var ; (formerly called bounds-polys-with-var1). ; The item "Fixed a bug that could cause simplifications to fail" pertains to a ; change to process-equational-polys. ; Fixed an apparent bug in translate-expand-term1, where after translating a ; form we tested if the UNtranslated form was a variablep or fquotep. ; Regarding "Fixed a bug in processing of ~c[:]~ilc[type-set-inverter] rules", ; below: Changed call of if-tautologyp to tautologyp in ; chk-acceptable-type-set-inverter-rule. Examples that now work and didn't ; before: ; (defun my-consp (x) (consp x)) ; (defthm foo (equal (my-consp x) (consp x)) ; :rule-classes :type-set-inverter) ; Alternatively (but disable foo before trying this): ; (defthm bar (equal (my-consp x) (consp x)) ; :rule-classes ((:type-set-inverter ; :corollary (equal (my-consp x) (consp x)) ; :type-set 1536))) ; Modified save-acl2-in-sbcl-aux and save-acl2-in-cmulisp-aux to store an ; absolute pathname for the underlying Lisp, after John Erickson reported ; problems with making an sbcl-based ACL2 image using a relative pathname for ; LISP= in the ACL2 make. ; Followed Jared Davis's suggestion of fixing message "To enable breaks into ; the debugger..." to print the suggested (SET-DEBUGGER-ENABLE T) correctly for ; the current package. ; Dropped the deceased-packages (second) argument of bad-lisp-objectp and ; chk-bad-lisp-object. ; Eliminated *read-object-eof* in favor of a dynamic-extent cons (in ; read-object), thanks to Bob Boyer. ; Eliminated constant *acl2-files* from the logic; it is now a constant defined ; in acl2.lisp. ; Regarding "Fixed a soundness bug in the handling of inequalities by the ; ~il[type-set] mechanism": The fix is in the handling of lambda applications ; inside type-set (see the comment "The pot-lst is not valid...."). ; Here is how to see that bug in action. The following theorem proves "using ; trivial observations", but the instance of it below evaluates to nil. ; (thm ; (IMPLIES (AND (< (CAR Y) (CADR X)) ; (TRUE-LISTP X) ; (TRUE-LISTP Y) ; (< (CAR X) (CAR Y))) ; (LET ((X Y) (Y X)) ; (AND (TRUE-LISTP X) ; (TRUE-LISTP Y) ; (< (CAR X) (CAR Y)))))) ; ; (let ((x '(1 7 3)) (y '(4 5 6))) ; (IMPLIES (AND (< (CAR Y) (CADR X)) ; (TRUE-LISTP X) ; (TRUE-LISTP Y) ; (< (CAR X) (CAR Y))) ; (LET ((X Y) (Y X)) ; (AND (TRUE-LISTP X) ; (TRUE-LISTP Y) ; (< (CAR X) (CAR Y)))))) ; A trace of type-set before the fix shows some odd behavior, a simpler version ; of which is below. The pot-lst represents (< (car x) (car y)), which should ; be irrelevant inside the body of the lambda, but (erroneously) is used to ; return a type-set of *ts-t*. ; (TYPE-SET '((LAMBDA (X Y) ; (< (CAR X) (CAR Y))) ; Y X) ; NIL NIL ; '((Y 576) (X 512)) ; NIL (ENS STATE) (W STATE) NIL ; '(((0) ; (CAR Y) ; ((((((CAR Y) . 1) ((CAR X) . -1)) ; (3) ; (PT . 3)) ; 0 < NIL)))) ; NIL) ; ; From (trace$ type-set), notice the use of the pot-lst inside the body of the ; ; lambda: ; 2> (TYPE-SET ((LAMBDA (X Y) (< (CAR X) (CAR Y))) Y X) ; NIL NIL ((Y 576) (X 512)) ; NIL |some-enabled-structure| ; |current-acl2-world| NIL ; (((0) ; (CAR Y) ; ((((((CAR Y) . 1) ((CAR X) . -1)) ; (3) ; (PT . 3)) ; 0 < NIL)))) ; NIL) ; ... ; 3> (TYPE-SET (< (CAR X) (CAR Y)) ; NIL NIL ((X 576) (Y 512)) ; T |some-enabled-structure| ; |current-acl2-world| NIL ; (((0) ; (CAR Y) ; ((((((CAR Y) . 1) ((CAR X) . -1)) ; (3) ; (PT . 3)) ; 0 < NIL)))) ; NIL) ; Changed fixnum declarations in some array functions to (signed-byte 32) and ; (unsigned-byte 31) declarations. ; The :multiplicity trace$ option was added in order to support break-on-error, ; so that raw-ev-fncall could be traced without using :native, so that the ; :cond option could be used. ; The fourth argument of observation1 is now a Boolean rather than an ; evisc-tuple. We don't advertise this mod since observation1 is not ; advertised. ; Added suitable environment variable settings to ACL2 scripts saved for CCL ; (OpenMCL) and SBCL, thanks (respectively) to assistance from Gary Byers and ; Nikodemus Siivola. In particular, profiling is now available for ACL2 in ; CCL using CHUD (see ; http://ccl.clozure.com/ccl-documentation.html#Profiling-Using-Apples-CHUD-metering-tools) ; and in SBCL, at least for SBCL 1.0.23 and later, e.g.: ; ; :q ; (require :sb-sprof) ; (sb-sprof:with-profiling (:max-samples 100000 ; :alloc-interval 1 ; :mode :alloc ; :sample-interval .00001 ; :report :graph ; :loop nil ; :threads :all ; :show-progress nil) ; (ld '((mini-proveall)))) ; The modification pertaining to the mention of term-order below was carried ; out by eliminating code for variants of var-fn-count/var-fn-count-lst and ; arith-term-order, by generalizing the term-order algorithm so that ; arith-term-order is a special case. See term-order1. The main change to ; arith-term-order (which is used in linear arithmetic) is that it is now ; sensitive to the "pseudo function count", which (roughly speaking) is the ; number of implicit constructor function calls in constants. These counts are ; compared for two terms after their variable counts and function call counts, ; but before the terms are compared lexicographically. ; Made tiny change to get-os in case :unix and :apple features (for example) ; are both present. ; Moved state-global-let* bindings of guard-checking-on and in-prove-flg down ; from prove to prove-loop0. Note that proof-checker calls prove-loop; a ; comment in pc-prove in fact clarified that guard-checking-on is already bound ; to nil by pc-single-step-primitive, but this new way seems a safer way to go ; (maybe others will call prove-loop), and besides, now in-prove-flg is set, ; which is better for break-on-error. ; Added to GNUmakefile ACL2_SIZE for ACL2 package size, implementing an idea ; from Jared Davis. ; Eliminated a skip-proofs warning caused by chk-embedded-event-form when in a ; make-event but not an encapsulate, include-book, or certify-book. ; Note that the new ruler-extenders feature handles prog2$ slightly differently ; than before, since merging of base cases can now take place under a prog2$ ; where before it did not. But don't really say that in the :doc string below, ; because it seems too subtle to be worth the distraction. Also notice that ; although ec-call is now a default ruler-extender where before it was not ; (unlike prog2$), nevertheless, since ec-call was likely not applied to calls ; of if, lambdas, or prog2$, it seems unlikely that the inclusion of ec-call as ; a default ruler-extender should have any effect. ; Regarding the "subtle soundness bug in the forming of constraints from ; deduced type prescriptions": This simplified iteratively-grow-constraint1, ; but the following is even more significant. Now, the constraints are based ; entirely on the lexical content of the events, not on which runes are enabled ; in the current world -- see constraints-introduced and ; definitional-constraints. This change may address other lurking soundness ; bugs, evoked using functional instantiation on the same encapsulated ; functions but for different worlds, of which we haven't even been aware. :Doc ":Doc-Section release-notes ACL2 Version 3.5 (May, 2009) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.4 into the following categories: changes to existing features, new features, heuristic improvements, bug fixes, new and updated books, Emacs support, and experimental ~ilc[hons] version. Each change is described in just one category, though of course many changes could be placed in more than one category. ~st[CHANGES TO EXISTING FEATURES] Many improvements have been made to ACL2's ``evisceration'' mechanism for hiding substructures of objects before they are printed, and to related documentation: ~bq[] o A new documentation topic explains evisc-tuples. ~l[evisc-tuple]. o A new interface, ~ilc[set-evisc-tuple], has been provided for setting the four global evisc-tuples. ~l[set-evisc-tuple]. o A new mode, ``iprinting'', allows eviscerated output to be read back in. ~l[set-iprint]. o Function ~c[default-evisc-tuple] has been deprecated and will probably be eliminated in future releases; use ~c[abbrev-evisc-tuple] instead. Also eliminated is the brr-term-evisc-tuple (also the user-brr-term-evisc-tuple). The term-evisc-tuple controls printing formerly controlled by the brr-term-evisc-tuple or user-brr-term-evisc-tuple. o ACL2 output is done in a more consistent manner, respecting the intention of those four global evisc-tuples. In particular, more proof output is sensitive to the term-evisc-tuple. Again, ~pl[set-evisc-tuple]. o A special value, ~c[:DEFAULT], may be provided to ~ilc[set-evisc-tuple] in order to restore these ~il[evisc-tuple]s to their original settings. o (Details for heavy users of the evisc-tuple mechanism) (1) There are no longer ~il[state] globals named ~c[user-term-evisc-tuple] or ~c[user-default-evisc-tuple]. (2) Because of the above-mentioned ~c[:DEFAULT], if you have referenced state globals directly, you should use accessors instead, for example ~c[(abbrev-evisc-tuple state)] instead of ~c[(@ abbrev-evisc-tuple)]. (3) For uniformity, ~ilc[set-trace-evisc-tuple] now takes a second argument, ~c[state].~eq[] Improved ~ilc[break-on-error] in several ways. First, it breaks earlier in a more appropriate place. Thanks to Dave Greve for highlighting this problem with the existing implementation. Also, ~ilc[break-on-error] now breaks on hard errors, not only soft errors (~pl[er], options ~c[hard] and ~c[hard?]). Thanks to Warren Hunt and Anna Slobodova for sending an example that showed a flaw in an initial improvement. Finally, new options cause printing of the call stack for some host Common Lisps. ~l[break-on-error]. Thanks to Bob Boyer for requesting this feature. ~ilc[Trace!] may now be used in raw Lisp (though please note that all soundness claims are off any time you evaluate forms in raw Lisp!). Thanks to Bob Boyer for feedback that led to this enhancement. ACL2 now searches for file ~c[acl2-customization.lsp] in addition to (and just before) its existing search for ~c[acl2-customization.lisp]; ~l[acl2-customization]. Thanks to Jared Davis for suggesting this change, which supports the methodology that files with a ~c[.lisp] extension are certifiable books (thus avoiding the need to set the ~c[BOOKS] variable in makefiles; ~pl[books-certification-classic]). Improved the error message for illegal ~ilc[declare] forms of the form ~c[(type (satisfies ...))]. Thanks to Dave Greve for sending an example highlighting the issue. If trace output is going to a file (because ~ilc[open-trace-file] has been executed), then a note will be printed to that effect at the time that a call of ~ilc[trace$] or ~ilc[trace!] is applied to one or more ~il[trace] specs. The notion of redundancy (~pl[redundant-events]) has been made more restrictive for ~ilc[mutual-recursion] events. Now, if either the old or new event is a ~ilc[mutual-recursion] event, then redundancy requires that both are ~ilc[mutual-recursion] events that define the same set of function symbols. Although we are not aware of any soundness bugs fixed by this modification, nevertheless we believe that it reduces the risk of soundness bugs in the future. The definition of ~c[trace*] has been moved to a book, ~c[misc/trace1.lisp]. A new version, used in ACL2s, is in book ~c[misc/trace-star.lisp]. ~il[Trace] utilities ~ilc[trace$] and ~ilc[trace!] are still built into ACL2. [Note: File ~c[misc/trace1.lisp] was deleted after Version 4.2.] Certain ~il[certificate] files will now be much smaller, by printing in a way that takes advantage of structure sharing. Certifying the following example produces a ~c[.cert] file of over 3M before this change, but less than 1K after the change. ~bv[] (defun nest (i) ;; Makes an exponentially-sized nest of conses i deep. (if (zp i) nil (let ((next (nest (1- i)))) (cons next next)))) (make-event `(defconst *big* ',(nest 20))) ~ev[] Thanks to Sol Swords for providing the above example and to him as well as to Bob Boyer, Jared Davis, and Warren Hunt for encouraging development of this improvement. We have also applied this improvement to the printing of function definitions to files on behalf of ~ilc[certify-book] and ~ilc[comp]. Names of symbols are now printed with vertical bars according to the Common Lisp spec. Formerly, if the first character of a symbol name could be the first character of the print representation of a number, then the symbol was printed using vertical bars (~c[|..|]) around its name. Now, a much more restrictive test for ``potential numbers'' is used, which can result in fewer such vertical bars. Base 16 is now carefully considered as well; ~pl[set-print-base]. Thanks to Bob Boyer for requesting this improvement. Note that macros ~c[set-acl2-print-base] and ~c[set-acl2-print-case] have been replaced by functions; ~pl[set-print-base] and ~pl[set-print-case]. The ACL2 reader now supports `~c[#.]' syntax in place of the `~c[#,] syntax formerly supported. Thanks to Bob Boyer for requesting this change. ~l[sharp-dot-reader]. NOTE that because of this change, `~c[#.]' no longer causes an abort; instead please use ~c[(a!)] or optionally, if in the ACL2 loop, ~c[:a!]; ~pl[a!]. Some small changes have been made related to ~il[gag-mode]: ~bq[] o ~il[Gag-mode] now suppresses some messages that were being printed upon encountering disjunctive splits from ~c[:OR] ~il[hints]. Thanks to Sol Swords for suggesting this improvement. o ACL2 had printed ``~c[Q.E.D.]'' with all output suppressed and ~ilc[gag-mode] enabled. Now, ``~c[Q.E.D.]'' will be suppressed when ~c[PROVE] and ~c[SUMMARY] output are suppressed, even if ~c[gag-mode] is enabled. o The use of ~ilc[set-gag-mode] had drastic effects on the inhibited output (~pl[set-inhibit-output-lst]), basically inhibiting nearly all output (even most warnings) when turning on gag-mode and enabling all output except ~c[proof-tree] output when turning off gag-mode. Now, ~ilc[set-gag-mode] only inhibits or enables proof (~c[PROVE]) output, according to whether gag-mode is being turned on or off (respectively). The related utility ~ilc[set-saved-output] has also been modified, basically to eliminate ~c[:all] as a first argument and to allow ~c[t] and ~c[:all] as second arguments, for inhibiting prover output or virtually all output, respectively (~pl[set-saved-output]).~eq[] A ~ilc[defstub] event ~il[signature] specifying output of the form ~c[(mv ...)] now introduces a ~c[:]~ilc[type-prescription] rule asserting that the new function returns a ~ilc[true-listp] result. Thanks to Bob Boyer for sending the following example, which motivated this change. ~bv[] (defstub census (*) => (mv * *)) (defn foo (x) (mv-let (a1 a2) (census x) (list a1 a2))) ~ev[] Improved the efficiency of ~ilc[string-append] so that in raw Lisp, it calls ~ilc[concatenate]. Thanks to Jared Davis for suggesting this change, including the use of ~ilc[mbe]. A minor change was made to the definition of ~ilc[concatenate] to support this change, and the lemma ~c[append-to-nil] was added (see below). The checksum algorithm used for ~il[certificate] files of ~il[books] has been changed. Thanks to Jared Davis for contributing the new code. This change will likely not be noticed unless one is using the experimental ~ilc[hons] version of ACL2, where it can greatly speed up book certification and inclusion because of function memoization (of source function ~c[fchecksum-obj]). Fewer calls are made to the checksum algorithm on behalf of ~ilc[certify-book] and a few other operations. Thanks to Jared Davis for providing data that helped lead us to these changes. Formatted printing directives ~c[~~p], ~c[~~q], ~c[~~P], and ~c[~~Q] are deprecated, though still supported. ~l[fmt]. Instead, please use ~c[~~x], ~c[~~y], ~c[~~X], and ~c[~~Y] (respectively). As a by-product, rule names in proof output are no longer hyphenated. A new keyword, ~c[:multiplicity], is available for tracing raw Lisp functions using the ACL2 ~il[trace] utility. ~l[trace$]. Users may now control whether or not a slow array access results in a warning printed to the screen (which is the default, as before), and if so, whether or not the warning is followed by a break. ~l[slow-array-warning]. On linux-like systems (including Mac OS X and SunOS), ~c[:]~ilc[comp] will now write its temporary files into the ~c[\"/tmp\"] directory, which is the value of ~ilc[state] global ~c['tmp-dir]. You can change that directory with ~c[(assign tmp-dir \"\")]. The messages printed for uncertified books have been enhanced. Thanks to Jared Davis for requesting such an improvement. A function definition that would be redundant if in ~c[:]~ilc[logic] mode is now considered redundant even if it (the new definition) is in ~c[:]~ilc[program] mode. That is, if a definition is ``downgraded'' from ~c[:logic] to ~c[:program] mode, the latter (~c[:program] mode) definition is considered redundant. Previously, such redundancy was disallowed, but we have relaxed that restriction because of a scenario brought to our attention by Jared Davis: include a book with the ~c[:logic] mode definition, and then include a book with the ~c[:program] mode definition followed by ~ilc[verify-termination]. Thanks, Jared. The ACL2 reader no longer accepts characters other than those recognized by ~ilc[standard-char-p] except for ~c[#\\Tab], ~c[#\\Page], and ~c[#\\Rubout] (though it still accepts strings containing such characters). As a result, no ~ilc[make-event] expansion is allowed to contain any such unacceptable character or string. Thanks to Sol Swords for sending an example that led us to make this restriction. A simple example is the following book: ~bv[] (in-package \"ACL2\") (defconst *my-null* (code-char 0)) (make-event `(defconst *new-null* ,*my-null*)) ~ev[] For this book, a call of ~ilc[certify-book] formerly broke during the compilation phase, but if there was no compilation, then a call of ~ilc[include-book] broke. Now, the error occurs upon evaluation of the ~ilc[make-event] form. ACL2 now collects up ~il[guard]s from ~ilc[declare] forms more as a user might expect, without introducing an unexpected ordering of conjuncts. We thank Jared Davis for sending us the following illustrative example, explained below. ~bv[] (defun f (x n) (declare (xargs :guard (and (stringp x) (natp n) (= (length x) n))) (type string x) (ignore x n)) t) ~ev[] Formerly, a guard was generated for this example by unioning the conjuncts from the ~c[:guard] onto a list containing the term ~c[(string x)] generated from the ~c[type] declaration, resulting in an effective guard of: ~bv[] (and (natp n) (= (length x) n) (stringp x)) ~ev[] The guard of this guard failed to be verified because ~c[(stringp x))] now comes after the call ~c[(length x)]. With the fix, contributions to the guards are collected up in the order in which they appear. So in the above example, the effective guard is the specified ~c[:guard]; the contribution ~c[(stringp x)] comes later, and is thus dropped since it is redundant. NOTE by the way that if ~c[:guard] and ~c[:stobjs] are specified in the same ~ilc[xargs] form, then for purposes of collecting up the effective guard as described above, ~c[:stobjs] will be treated as through it comes before the ~c[:guard]. Modified ~ilc[close-output-channel] to try to do a better job flushing buffers. Thanks to Bob Boyer for helpful correspondence. The notion of ``subversive recursion'' has been modified so that some functions are no longer marked as subversive. ~l[subversive-recursions], in particular the discussion elaborating on the notion of ``involved in the termination argument'' at the end of that ~il[documentation] topic. Formerly, ~c[:]~ilc[type-prescription] rules for new definitions inside ~ilc[encapsulate] forms were sometimes added as ~il[constraint]s. This is no longer the case. See also discussion of the ``soundness bug in the forming of constraints'', which is related. ~st[NEW FEATURES] It is now possible to affect ACL2's termination analysis (and resulting induction analysis). Thanks to Peter Dillinger for requesting this feature. The default behavior is essentially unchanged. But for example, the following definition is accepted by ACL2 because of the use of the new ~c[:ruler-extenders] features; ~l[ruler-extenders]. ~bv[] (defun f (x) (declare (xargs :ruler-extenders :all)) (cons 3 (if (consp x) (f (cdr x)) nil))) ~ev[] The following lemma was added in support of the improvement to ~ilc[string-append] described above: ~bv[] (defthm append-to-nil (implies (true-listp x) (equal (append x nil) x))) ~ev[] A mechanism has been provided for users to contribute documentation. ~l[managing-acl2-packages] for an example, which contains a link to an external web page on effective use of ACL2 packages, kindly provided by Jared Davis. ACL2 ~il[documentation] strings may now link to external web pages using the new symbol, ~c[~~url]; ~pl[markup]. Of course, those links appear in the web version of the documentation, but you made need to take a bit of action in order for these to appear as links in the Emacs Info version; ~pl[documentation]. Added ~ilc[intersectp] (similar to ~ilc[intersectp-eq] and ~ilc[intersectp-equal]). The user now has more control over how ACL2 prints forms; ~l[print-control]. Thanks to Bob Boyer for useful discussions leading to this enhancement. Some Common Lisp implementations only allow the syntax ~c[pkg-name::expression] when ~c[expression] is a symbol. The ACL2 reader has been modified to support a package prefix for arbitrary expressions; ~pl[sharp-bang-reader]. Thanks to Hanbing Liu for a query that led to this feature and to Pascal J. Bourguignon for suggesting an implmentation. Ill-formed ~il[documentation] strings need not cause an error. ~l[set-ignore-doc-string-error]. Thanks to Bob Boyer for requesting this feature. Type declarations are now permitted in ~c[let*] forms; ~pl[let*], ~pl[declare], and ~pl[type-spec]. (For Lisp programmers) Macro ~c[with-live-state] has been provided for programmers who refer to free variable ~c[STATE], for example with macros that generate uses of ~c[STATE], and want to avoid compiler warnings when evaluating in raw Lisp. For example, the following form can be submitted either inside or outside the ACL2 loop to get the desired effect (~pl[doc-string]): ~c[(with-live-state (f-put-global 'doc-prefix \" \" state))]. For another example use of this macro, see the definition of ~c[trace$] (ACL2 source file ~c[other-events.lisp]). (System hackers only) Added ~c[:]~ilc[redef-] to undo the effect of ~c[:]~ilc[redef+]. ~l[redef-]. Function ~ilc[random$] is a built-in random number generator. ~l[random$]. Thanks to Sol Swords for requesting this feature and providing an initial implementation. ~st[HEURISTIC IMPROVEMENTS] Sped up ~il[guard] generation for some functions with large if-then-else structures in their bodies. Thanks to Sol Swords for sending an illustrative example. Sped up ~il[guard] generation in some cases by evaluating ground (variable-free) subexpressions. Thanks to Bob Boyer for sending a motivating example: ~c[(defn foo (x) (case x ((1 2) 1) ((3 4) 3) ... ((999 1000) 999)))]. Modified slightly a heuristic association of ``size'' with constants, which can result in significant speed-ups in proofs involving constants that are very large ~c[cons] trees. Added a restriction in the linear arithmetic procedure for deleting polynomial inequalities from the linear database. Formerly, an inequality could be deleted if it was implied by another inequality. Now, such deletion requires that certain heuristic ``parent tree'' information is at least as restrictive for the weaker inequality as for the stronger. Thanks to Dave Greve for bringing a relevant example to our attention and working with us to figure out some surprising behavior, and to Robert Krug for making a key observation leading to the fix. (GCL especially) Improved compiled code slightly by communicating to raw Lisp the output type when a function body is of the form ~c[(the character ...)]. This tiny improvement will probably only be observed in GCL, if at all. Applied a correction suggested by Robert Krug to the variant of ~ilc[term-order] used in parts of ACL2's arithmetic reasoning. ~st[BUG FIXES] Fixed bugs in the handling of ~ilc[flet] expressions, one of which had the capability of rendering ACL2 unsound. Thanks to Sol Swords for pointing out two issues and sending examples. One example illustrated how ACL2 was in essence throwing away outer ~ilc[flet] bindings when processing an inner ~c[flet]. We have exploited that example to prove a contradiction, as follows: this book was certifiable before this fix. ~bv[] (in-package \"ACL2\") (defun a (x) (list 'c x)) ; Example from Sol Swords, which failed to be admitted (claiming that ; function A is undefined) without the above definition of A. (defun foo1 (x y) (flet ((a (x) (list 'a x))) (flet ((b (y) (list 'b y))) (b (a (list x y)))))) (defthm not-true (equal (foo1 3 4) '(b (c (3 4)))) :hints ((\"Goal\" :in-theory (disable (:executable-counterpart foo1)))) :rule-classes nil) (defthm contradiction nil :hints ((\"Goal\" :use not-true)) :rule-classes nil) ~ev[] Sol's second example, below, pointed to a second bug related to computing output signatures in the presence of nested ~c[flet] expressions, which we have also fixed: this form failed before the fix. ~bv[] :trans (flet ((foo (a) (list (flet ((bar (b) b)) a)))) x) ~ev[] Fixed a subtle soundness bug in the forming of constraints from deduced type prescriptions. As a result, when ACL2 prints a warning message labeling encapsulated functions as ``subversive'', ACL2 will no longer deduce ~c[:]~ilc[type-prescription] rules for those functions. Examples that exploit the bug in ACL2 Version_3.4 may be found in comments in ACL2 source function ~c[convert-type-set-to-term] (file ~c[other-processes.lisp]) and especially in function ~c[putprop-type-prescription-lst] (file ~c[defuns.lisp]). For more on the general issue of ``subversive recursions,'' ~pl[subversive-recursions].) Fixed a soundness bug in the handling of inequalities by the ~il[type-set] mechanism, which was using the inequality database inside the body of a ~ilc[lambda]. Fixed a long-standing soundness bug in ~ilc[compress1] and ~ilc[compress2], whose raw Lisp code gave the logically incorrect result in the case of a single entry other than the ~ilc[header], where that entry mapped an index to the ~ilc[default] value. Also fixed soundness bugs in ~ilc[compress1], in the case of ~c[:order >], where the raw Lisp code could drop the ~ilc[header] from the result or, when the input alist had entries in ascending order, fail to return an alist in descending order. For example, the following book certified successfully. ~bv[] (in-package \"ACL2\") (defthm true-formula-1 (equal (compress1 'a '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT 1 :NAME A :ORDER <) (1 . 1))) '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT 1 :NAME A :ORDER <))) :hints ((\"Goal\" :in-theory (disable (compress1)))) :rule-classes nil) (defthm ouch-1 nil :hints ((\"Goal\" :use true-formula-1)) :rule-classes nil) (defthm true-formula-2 (equal (compress1 'a '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT NIL :NAME A :ORDER >) (1 . 1) (2 . 2))) '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT NIL :NAME A :ORDER >) (2 . 2) (1 . 1))) :hints ((\"Goal\" :in-theory (disable (compress1)))) :rule-classes nil) (defthm ouch-2 nil :hints ((\"Goal\" :use true-formula-2)) :rule-classes nil) (defthm true-formula-3 (equal (compress1 'a '((:HEADER :DIMENSIONS (3) :MAXIMUM-LENGTH 4 :NAME A :ORDER >) (1 . B) (0 . A))) '((:HEADER :DIMENSIONS (3) :MAXIMUM-LENGTH 4 :NAME A :ORDER >) (1 . B) (0 . A))) :hints ((\"Goal\" :in-theory (disable (compress1)))) :rule-classes nil) (defthm ouch-3 nil :hints ((\"Goal\" :use true-formula-3)) :rule-classes nil) ~ev[] Fixed a soundness bug involving measured subsets and ~ilc[verify-termination], by changing ~ilc[verify-termination] so that it uses ~ilc[make-event]. ~l[verify-termination], in particular the discussion about ~ilc[make-event] near the end of that ~il[documentation] topic. Peter Dillinger first raised the idea to us of making such a change; when we found this soundness bug, we were certainly happy to do so! Fixed a bug that could cause a hard Lisp error but not, apparently, unsoundness. The bug was in the lack of attention to the order of guard and type declarations when checking for redundancy. In the following example, the second definition was redundant during the first pass of the ~ilc[encapsulate] form. The second definition, however, was stored on the second pass with a guard of ~c[(and (consp (car x)) (consp x))], which caused a hard Lisp error when evaluating ~c[(foo 3)] due to a misguided attempt to evaluate ~c[(car 3)] in raw Lisp. The fix is to restrict redundancy of definitions so that the guard and type declarations must be in the same order for the two definitions. ~bv[] (encapsulate () (local (defun foo (x) (declare (xargs :guard (consp x))) (declare (xargs :guard (consp (car x)))) x)) (defun foo (x) (declare (xargs :guard (consp (car x)))) (declare (xargs :guard (consp x))) x)) ; Now we get a hard Lisp error from evaluation of the guard of foo: (foo 3) ~ev[] Fixed a bug in the guard violation report for function ~ilc[intern-in-package-of-symbol]. Thanks to Dave Greve for bringing this bug to our attention. Made a change to allow certain ~il[hints], in particular certain ~c[:]~ilc[clause-processor] hints, that had previously caused errors during termination proofs by viewing the function being defined as not yet existing. Thanks to Sol Swords for bringing this issue to our attention with a nice example. ACL2 now properly handles interrupts (via control-c) issued during printing of the checkpoint summary. Previously it was possible on some platforms to make ACL2 hang when interrupting both during a proof and during the ensuing printing of the checkpoint summary. Thanks to Jared Davis and Sol Swords for bringing this problem to our attention. Fixed a bug that was preventing, inside some book ~c[\"b\"], the use of a ~c[:dir] argument to ~ilc[include-book] that refers to a directory defined using ~ilc[add-include-book-dir] earlier in the book ~c[\"b\"]. (We found this ourselves, but we thank John Cowles for observing it independently and sending us a nice example.) (GCL and CCL only) Fixed a bug in certain under-the-hood type inferencing. Thanks to Sol Swords for sending an example using ~il[stobj]s defined with the ~c[:inline] keyword, along with a helpful backtrace in CCL, and to Gary Byers for his debugging help. Fixed a bug in ~ilc[print-gv], which was mishandling calls of functions with more than one argument. Fixed a bug in the handling of ~ilc[AND] and ~ilc[OR] terms by the ~il[proof-checker] command ~c[DV], including numeric (``diving'') commands. Thanks for Mark Reitblatt for bringing this problems to our attention with a helpful example. Fixed printing of goal names resulting from the application of ~c[:OR] ~il[hints] so that they aren't ugly when working in other than the ~c[\"ACL2\"] package. Thanks to Sol Swords for bringing this issue to our attention. Fixed ~il[proof-tree] printing so that interrupts will not cause problems with hiding ordinary output because of incomplete proof-tree output. Thanks to Peter Dillinger for pointing out this issue. Fixed a hard error that could be caused by mishandling a ~ilc[force]d hypothesis during ~il[forward-chaining]. Thanks to Peter Dillinger for bringing this bug to our attention by sending a useful example. Fixed a bug that could cause simplifications to fail because of alleged ``specious simplification.'' This bug could appear when deriving an equality from the linear arithmetic database, and then attempting to add this equality to the current goal's hypotheses when it was already present. Thanks to Eric Smith for sending a helpful example (in July 2005!) that helped us debug this issue. Fixed a bug in processing of ~c[:]~ilc[type-set-inverter] rules. Fixed a bug that was causing an error, at least for an underlying Lisp of CCL (OpenMCL), when ~ilc[ec-call] was applied to a term returning multiple values. Thanks to Sol Swords for sending an example that brought this bug to our attention. Fixed handling of array orders to treat keyword value ~c[:order :none] correctly from an array's ~ilc[header]. Previously, there were two problems. One problem was that ~c[:order :none] was treated like the default for ~c[:order], ~c[<], while ~c[:order nil] was treated in the manner specified by ~c[:order :none] (~pl[arrays]). Now, both ~c[:order :none] and ~c[:order nil] are treated as ~c[:order nil] had been treated, i.e., so that there is no reordering of the alist by ~ilc[compress1]. The other problem with this case of ~c[:order] was that the ~c[:maximum-length] field of the ~ilc[header] was not being respected: the length could grow without bound. Now, as previously explained (but not previously implemented) ~-[] ~pl[arrays] ~-[] a ~ilc[compress1] call made on behalf of ~c[aset1] causes a hard error if the header of the supplied array specifies an ~c[:order] of ~c[:none] or ~c[nil]. An ~c[ignorable] ~ilc[declare] form had caused an error in some contexts when it should have been allowed. In particular, this problem could arise when using an ~c[ignorable] declaration at the top level in a ~ilc[defabbrev] form. It could also arise upon calling ~ilc[verify-termination] when the corresponding ~ilc[defun] form contained an ~c[ignorable] declaration at the top level. These bugs have been fixed. Contrary to existing documentation (~pl[make-event-details]), the value of ``~ilc[ld] special variable'' ~ilc[ld-skip-proofsp] was always set to ~c[nil] during ~ilc[make-event] expansion, not merely when the ~c[make-event] form has a non-~c[nil] value for keyword parameter ~c[:check-expansion]. This has been fixed. Thanks to Sol Swords for bringing this issue to our attention. We have disallowed the certification of a book when not at the top-level, either directly in the top-level loop or at the top level of ~ilc[ld]. Before this restriction, the following would certify a book with a definition such as ~c[(defun foo (x) (h x))] that calls function ~c[h] before defining it, if the certification was by way of the form such as: ~bv[] (er-progn (defun h (x) x) (certify-book \"my-book\")) ~ev[] But a subsequent ~ilc[include-book] of ~c[\"my-book\"] would then fail, because ~c[h] is not defined at the top level. Printing with ~ilc[fmt] directive ~c[~~c] now works properly even when the print-base is other than 10. Thanks to Sol Swords for reporting this bug and providing a fix for it. (SBCL, CMUCL, and CCL only) Fixed a bug in ~ilc[sys-call-status] in the case that the underlying Common Lisp is SBCL, CMUCL, or CCL (OpenMCL). Thanks to Jun Sawada for bringing this bug to our attention and providing a fix. Fixed a bug that was preventing ~ilc[local] ~ilc[defstobj] events in ~ilc[encapsulate] events. Thanks to Jared Davis for bringing this bug to our attention. Fixed a bug evidenced by error message ``Unexpected form in certification world'', which could result from attempting to certify a book after evaluating an ~ilc[encapsulate] form with a local ~ilc[defmacro]. Thanks to Jared Davis for pointing out this bug and sending the example: ~bv[] (encapsulate () (local (defmacro foo (x) `(table foo 'bar ,x))) (local (foo 3))) ~ev[] Formerly, evaluating a ~ilc[trace$] form inside a ~il[wormhole] such as the ~il[break-rewrite] loop could leave the user in a bad state after returning to the top level, in which that function could not be untraced. This has been fixed. Note however that when you proceed from a break in the ~il[break-rewrite] loop, the tracing state will be the same as it was when you entered that break: all effects of calling ~ilc[trace$] and ~ilc[untrace$] are erased when you proceed from the break. A ~c[:]~ilc[guard] of ~c[(and)] is no longer ignored. Thanks to Sol Swords for bringing this bug to our attention. A bug has been fixed that could result in needlessly weak induction schemes in the case that a recursive call is made in the first argument of ~ilc[prog2$]. This has been fixed by including ~ilc[prog2$] as a default ruler-extender in the new ruler-extenders feature (see above, and ~pl[ruler-extenders]). For details on this bug see Example 11 in distributed book ~c[books/misc/misc2/ruler-extenders-tests.lisp]. (For CCL/OpenMCL on Windows) ACL2 should now build on CCL (OpenMCL) on Windows. Thanks to David Rager for bringing this issue to our attention and helping with a fix that worked for CCL 1.2, and to the CCL team for improving handling of Windows-style filenames in CCL 1.3. ~st[NEW AND UPDATED BOOKS] See ~url[http://code.google.com/p/acl2-books/wiki/BooksSince34] for a list of books in Version 3.5 of ACL2 but not Version 3.4. Run the shell command ~bv[] ~c[svn log -r 94:HEAD] ~ev[] to see all changes to ~c[books/] since the release of Version 3.4. Here are just a few highlights. Thanks largely to Jared Davis, many ~c[Makefile]s have been improved to do automatic dependency analysis. ~l[books-certification-classic] for how to get your own ~c[Makefile]s to do this by adding a line: ~c[-include Makefile-deps]. Libraries ~c[books/arithmetic-4/] and ~c[books/rtl/rel7/] have been eliminated from the default book certification (~c[make regression]), in favor of new libraries ~c[books/arithmetic-5/] and ~c[books/rtl/rel8/] contributed by Robert Krug and Hanbing Liu, respectively. They and Jun Sawada have arranged the compatibility of these libraries; i.e., it is possible to evaluate both of the following in the same session: ~bv[] (include-book \"arithmetic-5/top\" :dir :system) (include-book \"rtl/rel8/lib/top\" :dir :system) ~ev[] Library ~c[books/rtl/rel1/] is no longer certified by default (though it is still distributed in support of ACL2(r); ~pl[real]). ~st[EMACS SUPPORT] Slightly modified ~c[Control-t e] (defined in ~c[emacs/emacs-acl2.el]) to comprehend the notion of an ``ACL2 scope'', and added ~c[Control-t o] to insert a superior ~ilc[encapsulate] defining such a scope. See the Emacs documentation for ~c[Control-t e] (generally obtained after typing ~c[Control-h k]). Modified distributed file ~c[emacs/emacs-acl2.el] so that if you put the following two forms in your ~c[~~/.emacs] file above the form that loads ~c[emacs/emacs-acl2.el], then Emacs will not start up a shell. Thanks to Terry Parks for leading us to this modification. ~bv[] (defvar acl2-skip-shell nil) (setq acl2-skip-shell t) ~ev[] ~st[EXPERIMENTAL HONS VERSION] Bob Boyer and others have contributed numerous changes for the experimental ``~c[hons]'' version of ACL2 (~pl[hons-and-memoization]). The ACL2 ~ilc[state] can now be queried with ~c[(@ hons-enabled)] so that a result of ~c[t] says that one is in the experimental ~c[hons] version, while ~c[nil] says the opposite. ~/~/") ; Changes made March 9-16, 2009 (after v3-4), for more efficient handling of ; certificates, etc.: ; The Essay on Skip-proofs was redone, and a new Essay on Soundness Threats was ; added, that explain current handling of skip-proofs, redef, etc. The basic ; idea is that we have eliminated state globals 'skipped-proofsp and ; include-book-alist-state, instead tracking more in the world ; (e.g. include-book-alist-all). See in particular install-event, which ; handles such matters, and note that maybe-add-command-landmark no longer does ; so. (We also changed include-book-fn and encapsulate-fn for this purpose.) ; We added state global 'skip-proofs-by-system to help (again, see ; install-event). ; Of course, there were miscellaneous supporting changes, some in comments. In ; particular, we (about a month later) eliminated chk-certification-worldxxx. ; Also, eval-event-lst now returns an extra element, which can be a natural ; number we can supply to nthcdr to eliminate some expense from our call of ; expansion-alist-pkg-names in certify-book-fn. This value is passed to ; process-embedded-events, and back from it in the case that the caller is ; 'certify-book. ; We also changed the use of check-sum so that we don't include the ; expansion-alist with the events from the actual book. For calls of ; check-sum-obj on event lists that support the handling of certificates, we ; now use only the events from the book ev-lst and no longer include events in ; the expansion-alist. Instead, we rely on the check-sum of the cert-obj, ; which is still incorporated in the certificate, for ensuring that we have the ; right expansion-alist. (deflabel |NOTE-3-5(R)| :doc ":Doc-Section release-notes ACL2 Version 3.5(r) (May, 2009) Notes~/ ~/ Please also ~pl[note-3-5] for changes in Version 3.5 of ACL2. This release incorporates improvements from Ruben Gamboa in support for non-standard analysis in ACL2(r), in the following ways: ACL2(r) now supports non-classical objects that are not also numeric, e.g., non-classical cons pairs. Consequently, the built-in ~c[standard-numberp] has been replaced with ~ilc[standardp]. If ~c[f] is a classical function, the value ~c[(f x1 ... xn)] is guaranteed to be standard when the ~c[xi] are standard. ACL2(r) can now recognize this fact automatically, using ~c[defun-std]. For example, the following can be used to assert that the square root of 2 is a standard value. ~bv[] (defthm-std sqrt-2-rational (standardp (acl2-sqrt 2))) ~ev[] More generally, the expression ~c[(f x1 ... xn)] can contain free variables, but the result is guaranteed to be standard when the variables take on standard variables, as in the following: ~bv[] (defthm-std sqrt-x-rational (implies (standardp x) (standardp (acl2-sqrt x)))) ~ev[] A potential soundness bug in ~ilc[encapsulate] was fixed. Specifically, when a classical, constrained function is instantiated with a lambda expression containing free variables, it may produce non-standard values depending on the values of the free variables. This means that the functional instantiation cannot be used to justify a non-classical theorem. For example, consider the following sequence: ~bv[] (encapsulate ((f (x) t)) (local (defun f (x) x))) (defthm-std f-x-standard (implies (standardp x) (standardp (f x)))) (defthm plus-x-standard (implies (standardp x) (standardp (+ x y))) :hints ((\"Goal\" :use ((:functional-instance f-x-standard (f (lambda (x) (+ x y)))))))) (defthm plus-x-eps-not-standard (implies (standardp x) (not (standardp (+ x (/ (i-large-integer))))))) (defthm nil-iff-t nil :hints ((\"Goal\" :use ((:instance plus-x-standard (y (/ (i-large-integer)))) (:instance plus-x-eps-not-standard))))) ~ev[] ACL2(r) also supports the introduction of non-classical functions with ~ilc[defchoose]. These behave just as normal functions introduced with ~ilc[defchoose], but they have a non-classical choice property. Finally, ACL2(r) now comes with a revamped library supporting non-standard analysis, still distributed separately as ~c[books/nonstd/]. The new library uses ~ilc[defchoose] to state more natural and useful versions of the IVT, MVT, etc. It also supports the introduction of inverse functions, e.g., logarithms. Finally, the library has much more extensive support for differentiation. ~/ ") (deflabel note-3-6 ; Introduced qfuncall to avoid CCL warnings. ; Regarding "Fixed a bug in reporting failures when monitoring rewrite rules ; with free variables in the hypotheses": the bug was in ; tilde-@-failure-reason-free-phrase, taking a car before checking we had a ; consp. However, we also found a bug (by inspection, never evoking it) in ; relieve-hyps1-free-2, and we added comments to relieve-hyps1-free-1 and ; relieve-hyps1-free-2 to explain better what we are returning. ; Some code has been refactored in the following functions in support of Daron ; Vroon's work on CCG analysis: put-induction-info, chk-acceptable-defuns, ; defuns-fn0, and defuns-fn. ; Delete dead code, redundant-event-tuplep and (now dead) good-defun-mode-p. ; Here is the proof of nil (using ACL2 Version 3.5) promised below regarding ; the soundness bug related to redundancy", as a certifiable book. ;;;;; start proof of nil ;;;;; ; (in-package "ACL2") ; ; (encapsulate ; () ; ; (set-ruler-extenders :all) ; ; (local ; (encapsulate ; () ; (defun f (x) ; (if (equal x 17) ; x ; (cons (if (consp x) (f (cdr x)) x) ; 10))))) ; ; (set-ruler-extenders :basic) ; ; (encapsulate ; () ; (defun f (x) ; (if (equal x 17) ; x ; (cons (if (consp x) (f (cdr x)) x) ; 10))))) ; ; (defthm bad ; (implies (true-listp x) ; (consp x)) ; :hints (("Goal" :induct (f x)))) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use ((:instance bad (x nil))))) ; :rule-classes nil) ;;;;; end proof of nil ;;;;; ; Here are the proofs of nil (using ACL2 Version 3.5) promised below regarding ; the "soundness bugs related to the handling of ~il[subversive-recursion]", as ; certifiable books. The first exploits the bug in subversive-cliquep, while ; the second exploits the bug in subversivep. ;;;;; start proof of nil ;;;;; ; (in-package "ACL2") ; ; (encapsulate ; ((my-test (x) t)) ; (local (defun my-test (x) ; (declare (ignore x)) ; nil)) ; (set-bogus-mutual-recursion-ok t) ; (mutual-recursion (defun f (x) ; (if (consp x) ; (f (cdr x)) ; x)) ; (defun g (x) ; (declare (xargs :measure (acl2-count x))) ; (if (my-test x) ; (not (g x)) ; x)))) ; ; (defthm my-test-is-nil ; (null (my-test x)) ; :hints (("Goal" :use g)) ; :rule-classes nil) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use ((:instance (:functional-instance my-test-is-nil ; (my-test identity)) ; (x t))))) ; :rule-classes nil) ;;;;; end proof of nil ;;;;; ; As promised above, the following exploits the bug in subversivep in ACL2 ; Version 3.5. ;;;;; start proof of nil ;;;;; ; (in-package "ACL2") ; ; (encapsulate ; ((my-test (x) t)) ; (local (defun my-test (x) ; (declare (ignore x)) ; nil)) ; (defun g (x) ; (declare (xargs :measure (acl2-count x))) ; (if (equal x (cons x x)) ; (g x) ; (if (my-test x) ; (not (g x)) ; x)))) ; ; (defthm my-test-is-nil ; (null (my-test x)) ; :hints (("Goal" :use g)) ; :rule-classes nil) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use ((:instance (:functional-instance my-test-is-nil ; (my-test identity)) ; (x t))))) ; :rule-classes nil) ;;;;; end proof of nil ;;;;; ; Peter Dillinger contributed a small refactoring of the code for relocating ; certificate files, including function make-certificate-file-relocated. ; Made a couple of changes in messages printed by our-abort, which is ; responsible for throwing one back to the top-level ACL2 loop in the event of ; a hard Lisp error. We incorporated Peter Dillinger's request that the ; set-debugger-enable suggested be inhibited if set-debugger-enable-fn is ; untouchable. Also, we no longer print "(Use (a!) ..."; see the comment ; there. ; Commented out a dynamic-extent declaration in read-object in case it is ; responsible for random Allegro CL failures related to reading certificates. :Doc ":Doc-Section release-notes ACL2 Version 3.6 (August, 2009) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.5 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental ~il[hons-and-memoization] version. Each change is described in just one category, though of course many changes could be placed in more than one category. Note that (starting with ACL2 Version 3.5) LispWorks is no longer supported as a platform for ACL2, as the LispWorks compiler could not handle the ACL2 sources; see comments in the ACL2 sources about ``function size'' being ``too large''. ~st[CHANGES TO EXISTING FEATURES] In the ~ilc[xargs] ~ilc[declare] form, the function symbol (or symbols, plural, in the case of ~ilc[mutual-recursion]) being defined may now be used in the specified ~c[:measure] and ~c[:]~ilc[guard] terms. Note that, the definition(s) of the new symbol(s) are still not used in the termination proof. Thanks to Daron Vroon for discussions leading to this addition for the measure and to Dave Greve for requesting such an enhancement for the guard. Processing of the ~c[:guard-hints] in an ~ilc[xargs] ~ilc[declare] form is now delayed until the start of ~il[guard] verification. As a result, the function symbol(s) being defined may now be used as one might expect in such hints, for example in an ~c[:in-theory] form. Thanks to Jared Davis for suggesting that we make such an improvement and providing an example. Made a low-level change to ~ilc[make-event] in support of the ACL2s utility ``~c[dynamic-make-event]''. Thanks to Peter Dillinger for explaining the issue leading to this change. Modified the failure message printed when a measure conjecture fails to be proved, to indicate whether or not the hint ~c[:ruler-extenders :all] would create a different measure conjecture. Thanks to Peter Dillinger for suggesting such a modification. A call of ~ilc[add-default-hints] had added hints to the end of the current list of default hints. Now, it adds them to the beginning of that list, so that they are tried first. However, ~ilc[add-default-hints] now takes a keyword argument, ~c[:at-end]. If that argument is supplied and evaluates to other than ~c[nil], then the previous behavior occurs. When ~ilc[save-exec] is used to save ACL2 images, the build dates are now printed on separate lines at startup and in the executable script. Thanks To Bob Boyer for requesting some newlines. ~ilc[Forward-chaining] rules are now generated so that every ~ilc[let] (also ~ilc[let*] and ~ilc[lambda]) expression is expanded away, as is every call of a so-called ``guard holder'' (~ilc[must-be-equal], ~ilc[prog2$], ~ilc[ec-call], ~ilc[the]). These were formerly only expanded away in the conclusion. Thanks to Konrad Slind for a helpful email leading to this change. ~ilc[Current-theory] now causes a hard error when applied to a name not found in the current ACL2 logical ~il[world], rather than simply returning ~c[nil]. When the underlying Common Lisp is GCL, ACL2 no longer uses the ~c[#n=] reader macro when writing out certain files, including ~il[certificate] files. In all other Lisps, it now uses the ~c[#n=] ``print-circle'' feature not only for ~il[certificate] files and ``~c[expansion.lsp]'' files written for example in support of ~ilc[make-event], but also for files written in support of ~c[:]~ilc[comp]. This is all managed with new ~il[state] global variable ~c[print-circle-files]; ~pl[print-control]. Thanks to Dave Greve for pointing out that GCL is limited by default to 1024 indices for ~c[#n=]. ~st[NEW FEATURES] A documentation topic explains in some detail how ~il[hints] work with the ACL2 proof ``waterfall'': ~pl[hints-and-the-waterfall]. This topic may be useful to those who write computed hints (~pl[computed-hints]) or other advanced hints. Added a new hint keyword, ~c[:no-thanks], that avoids printing the usual ``Thanks'' message for ~il[hints]. Thanks to Peter Dillinger for requesting this feature. Added a new hint keyword, ~c[:backtrack], that checks the goals produced by processing a goal, and can cause the goal to be re-processed using a new hint. ~l[hints]. Thanks to Pete Manolios for a conversation that led to the idea of this hint. Added a new class of hints, ~il[override-hints], that is similar to ~il[default-hints], except that override-hints are always applied, even if the user has supplied a hint explicitly for the goal. ~l[override-hints]. Thanks to Pete Manolios and Harsh Raju Chamarthi for useful discussions on this topic, including its application to testing. When a goal ready to be pushed for proof by induction is given the new hint ``~c[:do-not-induct :otf]'', it is indeed pushed for proof by induction, rather than causing immediate failure as in the case of the hint ``~c[:do-not-induct t]''. Instead, the proof fails when the goal is later picked to be proved by induction. Thanks to Peter Dillinger for discussions leading to this feature. Related to computed hints only: Each history entry in the list stored in variable ~c[HIST] (~pl[computed-hints]) now has a ~c[:CLAUSE] field, which provide's access to a goal's parent, parent's parent, and so on (within the same induction and forcing round only). It is now possible to inhibit warnings produced by ~ilc[in-theory] ~il[events] and ~il[hints] that occur when certain built-in definitions and executable-counterparts are disabled: just evaluate ~c[(assign verbose-theory-warning nil)]. Thanks to Jared Davis (and probably others in the past) for requesting such a mechanism. ~st[HEURISTIC IMPROVEMENTS] A source function (~c[linearize-lst]) was replaced by tail-recursive code, which can avoid a stack overflow. Thanks to Dave Greve for sending a helpful example. The heuristics for limiting ~il[forward-chaining] have been slightly relaxed, to allow derivations based on the occurrence of all arguments of the forward-chaining rule's conclusion in the goal (after stripping leading calls of ~c[NOT]). Thanks to Dave Greve for contributing this improvement and providing a motivating example. We simplified induction schemes by eliminating each hypothesis of the form ~c[(not (equal term (quote const)))] for which some other hypothesis in the same case equates term with some (presumably other) quoted constant. Thanks to Dave Greve for requesting an improvement of this sort. ~st[BUG FIXES] Fixed a soundness bug related to redundancy of ~ilc[encapsulate] events (~pl[redundant-events]) and ~il[ruler-extenders]. A proof of ~c[nil] in ACL2 Version 3.5 appears in a comment in ~c[(deflabel note-3-6 ...)] in the ACL2 source code. The fix is to insist that in order for one ~ilc[encapsulate] event to be redundant with another, they must be evaluated with the same ~il[default-ruler-extenders]. Analogous to this issue of ~il[default-ruler-extenders] for ~ilc[encapsulate]s is an issue of the ~il[default-verify-guards-eagerness], which has similarly been fixed. Fixed soundness bugs related to the handling of ~il[subversive-recursions] for ~il[constraint]s. Proofs of ~c[nil] in ACL2 Version 3.5 appear in a comment in ~c[(deflabel note-3-6 ...)] in the ACL2 source code. Fixed a bug that could cause the following error during calls of ~ilc[certify-book] in the presence of calls of ~ilc[skip-proofs] in the book: ~bv[] ACL2 Warning [Skip-proofs] in HARD ACL2 ERROR in FMT0: Illegal Fmt Syntax. The tilde-@ directive at position 0 of the string below is illegal because its variable evaluated to 0, which is neither a string nor a list. \"~~@0\" ~ev[] Thanks to Dave Greve for reporting this bug and making available a very helpful test case. The ~c[:corollary] of a rule (~pl[rule-classes]) failed to use the ~ilc[default-hints] of the logical ~il[world]. This has been fixed. (CCL only) We removed a call, for CCL 1.3 (and beyond) only, of foreign function ~c[sync] in the closing of output channels. Thanks to Daron Vroon for reporting issues with such a call on a non-Intel platform. Fixed a bug in reporting failures when monitoring rewrite rules with free variables in the hypotheses, that could cause a hard Lisp error (from which ACL2 continues, however). Thanks to Eric Smith for sending a very helpful example with his bug report. Fixed the handling of ~c[:induct] ~il[hints], which had thrown away hint information from parent goals. For example, the ~ilc[thm] form below failed to prove even though the second hint is in some sense superfluous; induction occurs automatically at ~c[\"Goal'\"] even without the hint on that. The failure was due to discarding the hint information on ~c[\"Goal\"]. ~bv[] (in-theory (disable append)) (thm (equal (cdr (cons a (append (append x y) z))) (append x y z)) :hints ((\"Goal\" :in-theory (enable append)) (\"Goal'\" :induct t) ; failed unless this line is commented out )) ~ev[] Fixed a bug in the ~ilc[args] command that was failing to show the formals of primitives (built-in functions like ~c[consp] that do not come with explicit definitions). Thanks to John Cowles for pointing out this bug. (At a lower level, the bug was that primitives failed to have ~c['stobjs-in] or ~c['stobjs-out] properties.) Fixed bugs in the utility supporting moving directories of certified books, sometimes used in Debian builds (as described in source function ~c[make-certificate-file]). Thanks to Alan Dunn for pointing out such a bug, in paths associated with ~ilc[defpkg] events in ~il[portcullis] commands in ~il[certificate]s (which are used for error reporting). There were also bugs, now fixed, that prevented renaming some book paths. Please note that this mechanism is not guaranteed to be sound; in particular, it can probably misbehave when macros are used to generate portcullis events. However, it seems likely that such issues will be very rare. Eliminated warnings that could arise when tracing a function with ~ilc[trace$]. Now, when ~ilc[trace$] is applied to a function without option ~c[:native], that function's declarations and documentation are discarded. Fixed a bug that could cause a failure when building an executable image using SBCL as the underlying Common Lisp. Thanks to Jun Sawada for reporting this failure. We made a similar fix for CMUCL. Fixed a bug in ~ilc[save-exec] in the case that an absolute pathnanme is supplied. Thanks to Jared Davis for bringing this bug to our attention. Fixed a bug in the use of ~ilc[trace$] with the ~c[:native] and ~c[:multiplicity] options that caused hard errors for some underlying Lisps. Fixed a bug in the interaction of ~ilc[trace$] and ~c[:]~ilc[comp], which caused error as ~ilc[comp] tried to re-trace functions that it temporarily untraced. ~st[NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE] See ~url[http://code.google.com/p/acl2-books/wiki/BooksSince35] for a list of books in Version 3.6 of ACL2 but not Version 3.5. For ~em[changes] to existing books rather than ~em[additions], see the log entries at ~url[http://code.google.com/p/acl2-books/source/list] starting with revision r286 up through revision r329. It is no longer required to specify a value for environment (or `make') variable ~c[ACL2_SYSTEM_BOOKS] when running `make' in the distributed book directory, even when that directory is not under the directory containing the ACL2 executable. Thanks to Peter Dillinger for providing this improvement, by modifying ~c[books/Makefile-generic] and, as needed, distributed book Makefiles. Thanks to Peter Dillinger, some books in support of the ACL2 Sedan (ACL2s) are more easily available for ACL2 users who do not use ACL2s. ~l[acl2-sedan]. ~st[EMACS SUPPORT] If the following form is evaluated before the load of ~c[emacs/emacs-acl2.el], then variables are now set to reflect the directory containing that file. ~bv[] (if (boundp '*acl2-sources-dir*) (makunbound '*acl2-sources-dir*)) ~ev[] Fixed info-dir-entry line in generated info file (by patching ~c[doc/write-acl2-texinfo.lisp]). Thanks to Alan Dunn for providing this patch. ~st[EXPERIMENTAL HONS VERSION] Bob Boyer and others have contributed numerous changes for the experimental ``~c[hons]'' version of ACL2 (~pl[hons-and-memoization]). A number of these have been crafted to work specifically with CCL (Clozure Common Lisp, formerly OpenMCL), which is now required as the underlying Lisp for the ``~c[hons]'' version of ACL2. A heuristic (source function ~c[too-many-ifs] has been made more scalable (for the non-HONS version as well, in fact), but with no functional change. Thanks to Jared Davis for noticing performance issues and suggesting fixes. Other changes including the following, quoting Bob Boyer: ~bq[] The CCL CASE macro now does better than a dumb linear search in some CASEes. SH and CSH are functions to talk to the underlying Gnu-Linux from CCL. Good for repeated calling when you simply cannot afford the copying cost of a FORK because you are using, say, a dozen gigabytes. Added CCL compiler-macros for IF and OR, to support some 'coverage' analysis, cf. IF-REPORT, extending the profiling. Introduced the type 'mfixnum' so that things like counting honses and counting calls to memoized or profiled functions can run fast in CCL 64 bits and yet still run at all under 32 bits. Moved all HONSes to CCL's newish static space, which permits the address of a cons to be used as a hash key, as in most Lisps. (CCL moves most conses and most everything when it does a compacting-gc.) Quite a few changes in the memoize-fn reporting. Added a timer facility, cf. call-with-timeout. Good for running under throttle some gross thoughts like 'Is it true that you can't fit 12 pigeons into 11 holes' on some propositional calculus systems/functions. Added rwx-size, pid-owner-cmdlines, rss, and proc-stat to help see what is really going on virtually in Gnu-Linux. Fixed at least one bug in compact-print-file and helped make its integration into ACL2's read-object$ a little more sound. Still worried some about *print-readably* vs. readtable-case. Does anyone else stay awake late at night worrying about readtable-case? Revised how the *watch-dog-process* interrupts the main process for the thousandth time, cf. watch. Haven't changed it in weeks, which means that (a) it is getting tolerable or (b) I've run out of gas. ~eq[] ~/~/") (deflabel |NOTE-3-6(R)| :doc ":Doc-Section release-notes ACL2 Version 3.6(r) (August, 2009) Notes~/ ~/ Please also ~pl[note-3-6] for changes in Version 3.6 of ACL2. ~/ ") (deflabel note-3-6-1 :Doc ":Doc-Section release-notes ACL2 Version 3.6.1 (September, 2009) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. The essential changes to ACL2 since Version 3.6 are the two bug fixes described below. There was also some functionality-neutral code refactoring, as requested by Daron Vroon in support of the ACL2 Sedan (~pl[acl2-sedan]). Also see ~url[http://code.google.com/p/acl2-books/wiki/BooksSince36] for a list of books in Version 3.6.1 of ACL2 but not Version 3.6. For ~em[changes] to existing books rather than ~em[additions], see the log entries at ~url[http://code.google.com/p/acl2-books/source/list] starting with revision r329 up through revision 350. Fixed a soundness bug in the handling of ~il[ruler-extenders], specifically in the handling of ~ilc[LET]-expressions. Thanks to Pete Manolios, who sent us a proof of ~c[nil], essentially as follows. In the termination proof for ~c[foo] below, the binding of ~c[x] to ~c[(cons t x)] was not substituted into the recursive call of ~c[foo] for purposes of the termination proof. ~bv[] (defun foo (x) (declare (xargs :ruler-extenders :all)) (let ((x (cons t x))) (if (endp (cdr x)) x (cons t (foo (cdr x)))))) (defthm foo-bad nil :hints ((\"Goal\" :use ((:instance foo (x '(3)))) :in-theory (disable foo (foo)))) :rule-classes nil) ~ev[] Fixed a typo in code supporting ~il[ruler-extenders] (specifically, swapped arguments in a recursive call of ACL2 source function ~c[get-ruler-extenders2], which could cause problems for functions defined using ~ilc[mutual-recursion]). Thanks to Daron Vroon for bringing this bug to our attention, pointing out the swapped arguments. ~/~/") (deflabel note-4-0 ; Thanks to Alan Dunn for contributing improvements to :doc markup and :doc ; truncate. ; Regarding "Fixed a bug in ~c[:]~ilc[pso] during the printing of failure ; messages for termination proofs.": The bug was the duplication of NAMES in ; the VARS for an IO? form in prove-termination. The macro IO? now has a guard ; prohibiting such duplicates. ; Modified *1*-symbol (also, but probably less important, *1*-symbol? and ; global-symbol) to use find-package-fast, resulting in about 2% performance ; improvement for a hons-based include-book that can take a couple of minutes. ; Regarding the change in the too-many-ifs heuristic: See the Essay on ; Too-many-ifs. ; We improved the error provided when certify-book or include-book fails ; because :skip-proofs-okp or other keywords need to be non-nil. ; We improved The error provided when computing with a live ; stobj with :guard-checking :none, to suggest (set-guard-checking nil). ; Thanks to Harsh Raju Chamarthi for related helpful conversations. ; In support of the new :case-split-limitations hint (see :doc below), the the ; rewrite-constant record was modified, and clausify now takes an sr-limit ; instead of the world. ; In support of the change allowing user control over compilation of books by ; default, raw Lisp variable *suppress-compile* has been renamed ; *suppress-compile-build-time* and state global 'suppress-compile has been ; replaced by state global 'compiler-enabled, which has the opposite parity. ; Removed the prohibition of eval-when for #+cmu; not an issue at least as far ; back as CMUCL 19e. ; Used with-standard-io-syntax for ACL2 source macro write-exec-file so that ; executable scripts can be printed correctly even when implementation-specific ; print variables, such as ccl::*print-string-length*, are set. Thanks to Bob ; Boyer for suggesting that some such change might be a good idea. ; Added a missing return-from in the raw Lisp GCL code for mod-expt. ; Fixed a bug that was leaving documentation strings in raw Lisp for functions ; that have ACL2 documentation strings. ; Fixed bug in two attempts to coerce a character (instead of a list of one ; character) to a string in bad-lisp-objectp. Thanks to David Rager for ; pointing out a compiler warning printed by SBCL 1.0.34.2. ; Load-compiled now takes an optional verbosity argument, which is occasionally ; used to print a message saying that a compiled file is being loaded. ; Some changes to include-book should be invisible to the user, but include a ; default for :load-compiled-file of :default instead of :warn. See :doc ; include-book, which has been improved. ; Weakened the definition of legal-acl2-character-p, in response to a helpful ; email from Jared Davis. ; Eliminated any checks for integers in bad-lisp-objectp, as explained in a ; comment there. ; We now print symbols in suitable packages for some :brr messages, thanks to ; feedback from Jared Davis. ; The directory stored for :dir :system is now always an absolute pathname ; (Unix style). ; In support of using portcullis commands in check-sum computations, ; eliminated the final check-sum-alist message from certify-book, ; which didn't seem appropriate anyhow. ; To prove nil in ACL2 Version_3.6.1 by exploiting the fact that portcullis ; commands were not included in check-sum computations, evaluate (ld ; "script.lsp") in a directory with contents shown below. ;;; % cat sub1.lisp ;;; (in-package "ACL2") ;;; ;;; ; Possible portcullis command: ;;; ; (defun f () 1) ;;; ;;; (defun g () 0) ;;; ;;; % cat sub2.lisp ;;; (in-package "ACL2") ;;; ;;; ; Possible portcullis command: ;;; ; (defun f () 2) ;;; ;;; (defun h () 0) ;;; % cat top1.lisp ;;; (in-package "ACL2") ;;; ;;; (include-book "sub1") ;;; (include-book "sub2") ;;; ;;; ; Certify with sub1's portcullis command. ;;; (defthm f-val-1 ;;; (equal (f) 1)) ;;; % cat top2.lisp ;;; (in-package "ACL2") ;;; ;;; (include-book "sub1") ;;; (include-book "sub2") ;;; ;;; ; Certify with sub2's portcullis command. ;;; (defthm f-val-2 ;;; (equal (f) 2)) ;;; % cat top.lisp ;;; (in-package "ACL2") ;;; ;;; (include-book "top1") ;;; (include-book "top2") ;;; ;;; (defthm contradiction ;;; nil ;;; :rule-classes nil ;;; :hints (("Goal" :use (f-val-1 f-val-2) ;;; :in-theory (theory 'ground-zero)))) ;;; % cat script.lsp ;;; ; (ld "script.lsp") ;;; ;;; (defun f () 1) ;;; (certify-book "sub1" 1) ;;; (u) ;;; (u) ;;; (certify-book "sub2") ;;; (u) ;;; (certify-book "top1") ;;; (u) ;;; ;;; (defun f () 2) ;;; (certify-book "sub2" 1) ;;; (u) ;;; (u) ;;; (certify-book "sub1") ;;; (u) ;;; (certify-book "top2") ;;; (u) ;;; ;;; (certify-book "top") ;;; % ; Added .NOTPARALLEL to the dependencies of GNUmakefile target, large. We hope ; this eliminates some build errors when using the -j option. ; Fixed a bug in "make copy-distribution" in GCL builds, where the presence of ; a filename that extends the designated directory string caused an error, ; erroneously stating that the directory already exists. ; Regarding "Fixed a bug that could make ~c[:]~ilc[oops] cause an error.": The ; fix is to use equal instead of eq in retract-world1; see the comment there. ; We similarly modified extend-world1. ; Fixed replace-colons so that something like ~ilc[:oops] in a :doc string ; doesn't cause an error when calling write-texinfo-file. ; Added small optimization in relevant-constraints1, to avoid a call of ; event-responsible-for-proved-constraint. ; Made small changes in doc/write-acl2-texinfo.lisp to avoid warnings when ; building the documentation. ; Improved make clean-doc. ; Eliminated unbound-macro-function and *unbound-macro-function*. We now check ; in fmakunbound! that fmakunbound really does work on macros. ; Eliminated certify-book-disabledp. ; Fixed a small output bug, by modifying tilde-@-lmi-phrase to account properly ; for the case that functional instances were previously proved only by ; nameless events. Here is an example: ; (encapsulate ; ((f1 (x) t) ; (f2 (x) t)) ; (local (defun f1 (x) x)) ; (local (defun f2 (x) x)) ; (defthm f1-f2 ; (equal (f1 x) (f2 x)))) ; ; (encapsulate ; ((g1 (x) t) ; (g2 (x) t)) ; (local (defun g1 (x) x)) ; (local (defun g2 (x) x)) ; (defthm g1-g2 ; (equal (g1 x) (g2 x)))) ; ; (defthm f-thm1 ; (equal (f1 (cons x x)) ; (f2 (cons x x)))) ; ; (defun k1 (x) ; (declare (xargs :guard (equal (g1 (cons x x)) ; (g2 (cons x x))))) ; x) ; ; (defun k2 (x) ; (k1 x)) ; ; (verify-guards k2 ; :hints (("Goal" :by (:functional-instance f-thm1 ; (f1 g1) ; (f2 g2))))) ; Added Essay on the proved-functional-instances-alist, to clarify the life ; cycle of entries in world global 'proved-functional-instances-alist ; (supporting the caching of proved functional instances). ; There is a new source file, multi-threading-raw.lisp, which is loaded only ; when feature :acl2-par is present (see acl2::*acl2-files*). It was ; contributed by David Rager to support the version of ACL2 that allows ; parallel evaluation (see :doc parallelism). ; Made a small change to the code for exit-lisp. ; In CCL, set ccl::*record-source-file* to nil after discussion with Gary ; Byers, to avoid paying the cost of recording source file information that can ; cause slowdown and isn't typically exploited by ACL2 users. The HONS version ; still sets this variable to T. ; We now clean up stale values of 'redundant-raw-lisp-discriminator property ; when undoing a defconst or defstobj event, which could free up memory. ; Eliminated the notion of "era" in the ACL2 sources. ; Improved the message after the Q.E.D. for hints :by nil, because the old ; message was a bit misleading in the case of defun (where the goals may not ; actually pass translate). Thanks to Jared Davis for pointing out this ; issue. ; The function iteratively-grow-constraint was significantly rewritten, and ; since its algorithm was no longer described by that name, we renamed it to ; encapsulate-constraint. ; Changed set-w! to call find-longest-common-retraction with event-p = t in all ; cases, not just when certifying or including books. Thanks to Sol Swords for ; sending a relevant failure that led us to this change; see the comments in ; set-w!. ; Substantially improved the Essay on Correctness of Meta Reasoning. :Doc ":Doc-Section release-notes ACL2 Version 4.0 (July, 2010) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.6.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. Also ~pl[note-3-6-1] for other changes since Version 3.6. ~st[CHANGES TO EXISTING FEATURES] There have been extensive changes to the handling of compiled files for books, which may generally be invisible to most users. The basic idea is that when compiled files are loaded on behalf of ~ilc[include-book], they are now loaded before events in the book are processed, not afterwards. This can speed up calls of ~c[include-book], especially if the underlying Lisp compiles all definitions on the fly (as is the case for CCL and SBCL). One functional change is that for keyword argument ~c[:load-compiled-file] of ~ilc[include-book], the values ~c[:comp-raw], ~c[:try], and ~c[:comp!] are no longer legal. (Note that the handling of ~c[:comp-raw] was actually broken, so it seems that this value wasn't actually used by anyone; also, the handling of ~c[:comp!] formerly could cause an error in some Lisp platforms, including SBCL.) Another change is that if ~ilc[include-book] is called with ~c[:load-compiled-file t], then each sub-book must have a compiled file or a so-called ``expansion file''; ~pl[book-compiled-file]. In the unlikely event that this presents a problem, the makefile provides a way to build with compilation disabled; ~pl[compilation]. Users of raw mode (~pl[set-raw-mode]) will be happy to know that ~ilc[include-book] now works if there an up-to-date compiled file for the book, since ~ilc[portcullis] commands are now incorporated into that compiled file. The mechanism for saving expansion files has changed, and the ~c[:save-expansion] argument of ~ilc[certify-book] has been eliminated; ~pl[certify-book]. More discussion of ACL2's new handling of book compilation is described in a new documentation topic; ~pl[book-compiled-file]. It was possible to get a hard Lisp error when certifying a book with a redundant ~ilc[defconst] form whose term is not identical to the existing ~ilc[defconst] form for the same name. The following example illustrates the problem, which has been fixed (as part of the change in handling of compiled files for books, described above). Imagine that after the initial ~c[(in-package \"ACL2\")] form, file ~c[foo.lisp] has just the form ~c[(defconst *a* (append nil nil))]. Then before the fix, we could have: ~bv[] ACL2 !>(defconst *a* nil) [[output omitted]] ACL2 !>(certify-book \"foo\" 1) [[initial output omitted] * Step 5: Compile the functions defined in \"/v/joe/foo.lisp\". Compiling /v/joe/foo.lisp. End of Pass 1. End of Pass 2. OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3 Finished compiling /vjoe/foo.lisp. Loading /v/joe/foo.lisp Error: Illegal attempt to redeclare the constant *A*. ~ev[] The ~ilc[wormhole] facility has been changed to repair a bug that allowed guard violations to go undetected. The major change has to do with the treatment of what used to be called the ``pseudo-flag'' argument which has been replaced by a quoted lambda expression. ~l[note-4-0-wormhole-changes] for help in converting calls of ~ilc[wormhole]. Also see ~pl[wormhole] and ~pl[wormhole-eval]. The function ~c[assign-wormhole-output] has been eliminated but its functionality can be provided by ~ilc[wormhole-eval]. The ACL2 tutorial has been greatly expanded, for example to include a self-contained discussion of rewriting. ~l[acl2-tutorial]. Formerly, the ~ilc[mbe] macro and ~ilc[must-be-equal] function were disallowed in any definition within an encapsulate having a non-empty signature. Now, these are allowed provided the definition has been declared to be non-executable (~pl[defun-nx]). As a result, ~ilc[defevaluator] ~il[events] may now include ~ilc[must-be-equal] among the function symbols known by the evaluator; this had not previously been allowed. Thanks to Sol Swords for discussions leading to this relaxation for ~ilc[defevaluator]. ~ilc[Princ$] now prints strings more efficiently. Thanks to Jared Davis for suggesting the improvements to ~c[princ$]. The use of ~ilc[xargs] declaration ~c[:non-executable t] no longer requires the absence of ~ilc[state] or declared ~ilc[stobj]s among the formal parameters of a function definition. As before, the use of ~c[:non-executable t] turns off single-threadedness checking for the body, and also as before, attempts to execute the function will fail. Thanks to Sol Swords for requesting this relaxation (for automatic generation of so-called ``flag functions'' for definitions using ~ilc[mutual-recursion]). The ~il[documentation] has been improved for explaining to advanced users the details of the ACL2 hint mechanism; ~pl[hints-and-the-waterfall], and see the example about ~c[nonlinearp-default-hint] in distributed book ~c[books/hints/basic-tests.lisp]. Thanks to Robert Krug for useful discussions, in particular suggesting the above example as one to be explained with the documentation. The ~c[time$] macro has been enhanced to allow user control of the timing message that is printed, and of when it is printed. ~l[time$]. Thanks to Jared Davis for providing the essential design, helpful documentation (largely incorporated), and an initial implementation for raw Lisp. The ~c[:ttags] argument to ~ilc[include-book] had been required when including a certified book that uses trust tags. This is no longer the case: essentially, ~c[:ttags] defaults to ~c[:all] except that warnings will be printed. Thanks to Jared Davis for requesting such a relaxation, and to him and Sandip Ray for useful discussions. The definition of ~ilc[mv-let] has been modified so that the single-step macroexpansion (~pl[trans1]) of its calls can be evaluated. Thanks to Pete Manolios for bringing this evaluation issue to our attention and ensuing discussions. All calls of so-called ``guard-holders'' ~-[] ~ilc[prog2$], ~ilc[must-be-equal] (from calls of ~pl[mbe]), ~ilc[ec-call], and ~ilc[mv-list] ~-[] are now removed before storing hypotheses of rules of class ~c[:]~ilc[rewrite] or ~c[:]~ilc[linear]. Thanks to Sol Swords for requesting this enhancement and sending the following example in the case of ~il[rewrite] rules. ~bv[] (defthm foo (prog2$ (cw \"asdf\") (and (equal (car (cons x y)) x) (equal (cdr (cons x y)) y)))) ~ev[] The handling of ~ilc[fmt] directive ~c[~~s] has been modified so that if the argument is a symbol that would normally be printed using vertical bars (~c[|]), then that symbol is printed as with ~c[~~f]. Thanks to Jared Davis for providing the following example showing that treatment of ~c[~~s] was a bit unexpected: ~c[(cw \"~~s0.~~%\" '|fo\\|o|)]. Error messages have been improved for ill-formed terms (in ACL2's so-called ``translation'' routines). Thanks to Jared Davis for requesting such an enhancement. Modified ~ilc[defun-sk] so that it executes in ~c[:]~ilc[logic] mode. Previously, evaluation of a ~ilc[defun-sk] event in ~c[:]~ilc[program] mode caused a somewhat inscrutable error, but now, ~c[:]~ilc[program] mode is treated the same as ~c[:]~ilc[logic] mode for purposes of ~ilc[defun-sk]. The ``system hacker'' commands ~c[(]~ilc[redef+]~c[)] and ~c[(]~ilc[redef-]~c[)] are now embedded event forms (~pl[embedded-event-form]), hence may be used in ~il[books] as well as in ~ilc[progn] and ~ilc[encapsulate] ~il[events]. Also, these two commands are now no-ops in raw Lisp. The function symbol ~c[worldp] (in the ~c[\"ACL2\"] package) has been renamed to ~c[plist-worldp]. The function ~c[gc$-fn] (resulting from macroexpansion of ~ilc[gc$]) is now in ~c[:]~ilc[logic] mode. Thanks to Jared Davis for requesting this change. The user now has control over whether compilation is used, for example whether or not ~ilc[certify-book] compiles by default, using function ~c[set-compiler-enabled]. ~l[compilation]. Modified the conversion of relative to absolute pathnames in ~il[portcullis] ~il[command]s for book certification. Now, more pathnames remain as relative pathnames. The ~c[\"Ttags\"] warning that can be printed by ~ilc[include-book] is now given even if ~ilc[set-inhibit-output-lst] has specified `~c[warning]'. To suppress it, specify ~c[warning!] instead, for example, ~c[(set-inhibit-output-lst '(acl2::warning! acl2::proof-tree))]. On occasion, ACL2 prints the message ``Flushing current installed world'' as it cleans up when certain actions (installing a ~il[world]) are interrupted. This operation has been sped up considerably. If your session includes many ~il[events], you can probably speed up any such operation further by invoking ~ilc[reset-prehistory]. Thanks to Jared Davis for sending a query that led us to make this improvement. Calls of the form ~c[(ec-call (must-be-equal logic exec))] are no longer allowed, since we do not have confidence that they would be handled correctly. The underlying function for ~ilc[good-bye] (and hence for ~ilc[exit] and ~ilc[quit]) is now in ~c[:]~ilc[logic] mode. Thanks to Jared Davis for requesting this enhancement. We now require that every function symbol in the ~il[signature] of an ~ilc[encapsulate] event have a ~c[:]~ilc[logic] mode definition at the end of the first pass, not merely a ~c[:]~ilc[program] mode definition (which formerly was sufficient). You can still define such a function in ~c[:program] mode, provided it is followed by a ~c[:logic] mode definition (where of course both definitions are ~il[local], since we are discussing functions is introduced in the ~il[signature]). Thanks to Carl Eastlund for bringing this issue to our attention. (Note: An analogous modification has been made for ~c[:]~ilc[bdd] ~il[hints] as well.) The following functions now have raw Lisp implementations that may run faster than their ACL2 definitions: ~ilc[assoc-eq], ~ilc[assoc-equal], ~ilc[member-eq], ~ilc[member-equal], ~c[subsetp-eq], ~ilc[subsetp-equal], ~ilc[remove-eq], ~ilc[remove-equal], ~ilc[position-eq], and ~ilc[position-equal]. Thanks to Jared Davis for suggesting that we consider such an improvement. We now avoid infinite loops caused when tracing functions that implement ~ilc[trace$]. Thanks to Rob Sumners and Eric Smith for useful discussions. The implementation of ~ilc[trace!] has been modified slightly, to accommodate the fix for ``some holes in the handling of trust tags'' described later, below. This item applies unless the host Lisp is GCL. An interrupt (control-c) will now cause a proof to exit normally in most cases, by simulating a timeout, as though ~ilc[with-prover-time-limit] had been called with a time-limit of 0. If the first interrupt doesn't terminate the proof, a second one should do so (because a different, more ``severe'' mechanism is used after the first attempt). As a result, ~ilc[redo-flat] should work as one might expect even if a proof is interrupted. Thanks to Dave Greve for requesting this enhancement to ~ilc[redo-flat]. Technical note: for reasons related to this change, time-limits are no longer checked in evaluator functions (~c[ev-fncall], ~c[ev], ~c[ev-lst], ~c[ev-fncall-w], ~c[ev-w], and ~c[ev-w-lst]). It is now legal for ~il[proof-checker] ~il[macro-command]s to appear in ~c[:]~ilc[instructions] that are used in place of ~c[:]~ilc[hints]. Thanks to Sandip Ray for (most recently) requesting this feature. The value of ~c[:command-conventions] for ~ilc[ld] special variable ~c[ld-post-eval-print] is now treated as though it were ~c[t] if the value ~ilc[ld] special variable ~c[ld-error-triples] is ~c[nil]. The following example illustrates this change. ~bv[] ACL2 !>(ld-post-eval-print state) ; default :COMMAND-CONVENTIONS ACL2 !>(ld-error-triples state) ; default T ACL2 !>(set-ld-error-triples nil state) *** Then, before the change: ACL2 !>(mv t 3 state) 3 *** Instead, after the change: ACL2 !>(mv t 3 state) (T 3 ) ~ev[] The default behavior of ~ilc[ld] has been changed. Formerly when an error occurred that halted a subsidiary call of ~c[ld], then the parent ~c[ld] would continue. That is no longer the case. Consider the following example. ~bv[] (ld '((ld '((defun f (x) x) (defun bad (x)) ; ERROR -- missing the body )) (defun g (x) x))) ~ev[] Formerly, ~c[g] would be defined in the resulting logical ~il[world]. Now, the error halts not only the inner ~c[ld] but also the outer ~c[ld]. ~l[ld], and for details of the new default value for ~c[:ld-error-action], ~c[:RETURN!], see ~pl[ld-error-action]. Also see the paragraph below about a new utility, ~c[:]~ilc[p!]. Thanks to Robert Krug and Sandip Ray for helpful discussions. Environment variable ~c[ACL2-CUSTOMIZATION] has been replaced by ~c[ACL2_CUSTOMIZATION] ~-[] that is, the hyphen has been replaced by an underscore ~-[] so that it can be set conveniently in the ~c[bash] shell. ~l[acl2-customization]. The ``~c[Warnings:]'' summary is now omitted when there are no warnings, where formerly ``~c[Warnings: None]'' was printed. Thanks to Jared Davis for suggesting this change. We have modified the generation of ~c[constraint]s for ~ilc[encapsulate] ~il[events] in two primary ways, neither of them likely to affect many users. One change is that the virtual movement of definitions and theorems to in front of an ~ilc[encapsulate] event, or of definitions to behind that event, is no longer inhibited in the case of nested encapsulates with non-empty ~il[signature]s. The following example illustrates the other change, as discussed below. ~bv[] (encapsulate ((f (x) t)) (local (defun f (x) x)) (defun g (x) (cons x (f x))) (defun h (x) (g x)) (defthm h-is-f (equal (car (h x)) x))) ~ev[] Previously, the ~il[constraint] on ~c[f] and ~c[h] was essentially the conjunction of the definition of ~c[h] and the theorem ~c[h-is-f]. Now, the definition of ~c[g] is conjoined as well; moreover, ~c[g] receives the same ~il[constraint] as do ~c[f] and ~c[h], where previously ~c[g] was only constrained by its definition. While we are not aware of a soundness bug caused by the previous approach, the new approach follows more precisely the intended notion of ~il[constraint]. The use of ~ilc[trace$] (or ~ilc[trace!]) option ~c[:multiplicity] had been required when option ~c[:native] was supplied. This is no longer the case. Also, a bug has been fixed that had prevented ~c[:multiplicity] from working properly in GCL and Allegro CL. Several errors have been eliminated that formerly occurred when the constraints for a function symbol were unknown because it was constrained using a dependent clause-processor (~pl[define-trusted-clause-processor]. Now, it is assumed that the ~c[supporters] argument in a ~ilc[define-trusted-clause-processor] event is such that every ancestor of any function symbol constrained by the ``promised encapsulate'' of that event among, or ancestral in, those ~c[supporters]. Thanks to Sol Swords, Sandip Ray, and Jared Davis for helpful discussions. The notion of ~il[constraint] for functions introduced by ~ilc[defun] has been modified slightly. No longer do we remove from the body of the definition calls of so-called ``guard-holders'': ~ilc[prog2$], ~ilc[must-be-equal], ~il[ec-call], and ~il[mv-list], and uses of ~c[the-error] generated by ~ilc[the]. Also, we now expand calls of ~c[the-error] with the same aggressive heuristics applied to a number of other functions (technically, adding it to the list ~c[*expandable-boot-strap-non-rec-fns*]). ~st[NEW FEATURES] A new event, ~ilc[defattach], allows evaluation of calls of constrained (~ilc[encapsulate]d) functions. In particular, users can now, in principle, soundly modify ACL2 source code; please feel free to contact the ACL2 implementors if you are interested in doing so. ~l[defattach]. Eric Smith has noticed that if you exit the ~il[break-rewrite] loop using ~c[:]~ilc[a!] during an ~ilc[ld] of a file, then all changes to the logical ~il[world] are discarded that were made during that call of ~ilc[ld]. A new utility, ~c[:]~ilc[p!], pops just one level instead, and avoids discarding that work. (This change is related to an item above, ``The default behavior of ~ilc[ld] has been changed.'') Thanks to Eric for pointing out this issue. New function ~ilc[mv-list] is the identity function logically, but converts multiple values to lists. The first argument is the number of values, so an example form is as follows, where ~c[foo] returns three values: ~c[(mv-list 3 (foo x y))]. Thanks to Sol Swords for requesting this feature and for reporting a soundness bug in one of our preliminary implementations. A new ~ilc[state] global variable, ~c[host-lisp], has as its value a keyword whose value depends on the underlying Common Lisp implementation. Use ~c[(@ host-lisp)] to see its value. It is now possible to write ~il[documentation] for HTML without error when there are links to nonexistent documentation topics. See the comments in macro ~c[acl2::write-html-file] at the end of file ~c[doc/write-acl2-html.lisp]. When there are such errors, they should be easier to understand than previously. Thanks to Alan Dunn for providing the initial modifications. It is now possible to inhibit specified parts of the Summary printed at the conclusion of an event. ~l[set-inhibited-summary-types]. Also ~pl[with-output], in particular the discussion of the new ~c[:summary] keyword. Thanks to Sol Swords for requesting more control over the Summary. A new ~c[:]~ilc[hints] keyword, ~c[:case-split-limitations], can override the default case-split-limitations settings (~pl[set-case-split-limitations]) in the simplifier. Thanks to Ian Johnson for requesting this addition and providing an initial implementation. It is now possible to defer and avoid some ttag-related output; ~pl[set-deferred-ttag-notes]. Thanks to Jared Davis for requesting less verbosity from ttag-related output. A new ~il[command], ~c[:]~ilc[pl2], allows you to restrict the rewrite rules printed that apply to a given term. ~l[pl2]. Thanks to Robert Krug for requesting such a capability. ACL2 now provides a utility for canonicalizing filenames, so that soft links are resolved; ~pl[canonical-pathname]. Moreover, ACL2 uses this utility in its own sources, which can eliminate some issues. In particular, ~ilc[include-book] with argument ~c[:ttags :all] no longer breaks when given a book name differing from the book name that was used at certification time; thanks to Sol Swords for reporting that problem. Also, certain errors have been eliminated involving the combination of packages in the certification world and trust tags; thanks to Jared Davis for sending an example of that problem. You can now suppress or enable guard-checking for an individual form; ~pl[with-guard-checking]. Thanks to Sol Swords for requesting this feature. The ~ilc[walkabout] utility has been documented (thanks to Rob Sumners for suggesting this documentation). This utility can make it easy to explore a large ~c[cons] tree. New interactive commands ~c[(pp n)] and ~c[(pp print-level print-length)] have been added to restrict how much of the current object is displayed. ~l[walkabout]. Rules of class ~c[:]~ilc[type-prescription] may now be provided a ~c[:backchain-limit-lst] keyword. The default behavior is unchanged, but now ~ilc[type-set] is sensitive not only to the new ~c[:backchain-limit-lst] of a ~c[:]~ilc[type-prescription] rule (if supplied) but to the ~ilc[default-backchain-limit] of the current logical ~il[world]. Setting of backchain-limits can now specify either the new (type-set) limit or the old limit (for rewriting); ~pl[set-default-backchain-limit] and ~pl[set-backchain-limit]. Moreover, the functions ~ilc[default-backchain-limit] and ~ilc[backchain-limit] now take a second argument of ~c[:ts] or ~c[:rewrite] to specify which backchain-limit is desired. ~st[HEURISTIC IMPROVEMENTS] The so-called ``too-many-ifs'' heuristic has been modified. Such a heuristic has been employed in ACL2 (and previous Boyer-Moore provers) for many years, in order to limit the introduction of calls of ~ilc[IF] by non-recursive functions. Most users need not be concerned with this change, but two proofs in the regression suite (out of thousands) needed trivial adjustment, so user proofs could need tweaking. In one application, this modification sped up proofs by 15%; but the change in runtime for the regression suite is negligible, so such speedups may vary. Thanks to Sol Swords for providing a test from ACL2 runs at Centaur Technology, which was useful in re-tuning this heuristic. Guard proof obligations could have size quadratic in the number of clauses in a ~ilc[case] statement. This inefficiency has been removed with a change that eliminates a hypothesis of the form ~c[(not (eql term constant))] when there is already a stronger hypothesis, equating the same term with a different constant. Thanks to Sol Swords for bringing this problem to our attention and suggesting an alternate approach to solving it, which we may consider in the future if related efficiency problems persist. We adjusted the heuristics for determining induction schemes in the presence of ~il[ruler-extenders], when handling calls of a function symbol that is a ruler-extender, in either of two cases: either the function takes only one argument; or the function is ~ilc[prog2$] or ~ilc[ec-call], and the first argument contains no recursive call. These cases are treated more directly as though the ruler-extender call is replaced by the unique (in the case of ~ilc[prog2$] and ~ilc[ec-call], the second) argument. A new ~c[:]~ilc[type-prescription] rule, ~c[true-listp-append], has been added: ~bv[] (implies (true-listp b) (true-listp (append a b))) ~ev[] If you are interested in the motivation for adding this rule, see comments in ~c[true-listp-append] in ACL2 source file ~c[axioms.lisp]. The use of ~c[:forward-chaining] lemmas has been improved slightly. In previous versions, a conclusion derived by forward chaining was discarded if it was derivable by type-set reasoning, since it was ``already provable.'' But this heuristic prevented the conclusion from triggering further forward chaining. This has been fixed. Thanks to Dave Greve for pointing out this problem. The fundamental utility that turns an ~c[IF] expression into a set of clauses has been optimized to better handle tests of the form ~c[(equal x 'constant)] and their negations. This eliminates an exponential explosion in large case analyses. But it comes at the inconveience of sometimes reordering the clauses produced. The latter aspect of this change may require you to change some Subgoal numbers in proof hints. We apologize for the inconvenience. Certification can now run faster (specifically, the compilation phase) for books with very large structures generated by ~ilc[make-event], when there is significant sharing of substructure, because of a custom optimization of the Lisp reader. Thanks to Sol Swords for bringing this efficiency issue to our attention. Jared Davis reported inefficiency in certain ~ilc[make-event] evaluation due to a potentially expensive ``bad lisp object'' check on the expansion produced by the ~c[make-event]. This check has been eliminated except in the case that the expansion introduces packages (for example, by including a book during the expansion phase that introduces packages). Thanks to Jared for providing a helpful example. The application of rules of class ~c[:]~ilc[induction] had the potential to loop (as commented in ACL2 source function ~c[apply-induction-rule]). This has been fixed. Thanks to Daron Vroon and Pete Manolios for sending nice examples causing the loop. Heuristics have been tweaked so that false goals may be simplified to ~c[nil] that had formerly been left unchanged by simplification, perhaps resulting in useless and distracting proofs by induction. Thanks to Pete Manolios for pointing out this issue by sending the following example: ~c[(thm (<= (+ 1 (acl2-count x)) 0))]. (Technical explanation: When every literal in a clause simplifies to ~c[nil], even though we might not normally delete one or more such literals, we will replace the entire clause by the false clause.) Improved the efficiency of the built-in function, ~ilc[take]. Thanks to Bob Boyer for suggesting this improvement. ACL2 can now use evaluation to relieve hypotheses when applying ~c[:]~ilc[type-prescription] rules. Thanks to Peter Dillinger and Dave Greve for requesting this enhancement, and to Robert Krug for a relevant discussion long ago. Evaluation has been sped up during theorems for calls of ~ilc[mv-let], by avoiding repeated evaluation of the expression to which its variables are bound. Thanks to Sol Swords for requesting this improvement and sending an illustrative example. Modified a heuristic to avoid the opening up non-recursive function calls on calls of ~ilc[hide] involving ~ilc[if]-expressions. For example, the ~ilc[thm] form below is now admitted ~bv[] (defun bar (x) (cons x x)) (thm (equal (bar (hide (if a b c))) (cons (hide (if a b c)) (hide (if a b c))))) ~ev[] ~st[BUG FIXES] Fixed a soundness bug in destructor elimination, which was preventing some cases from being generated. Thanks to Eric Smith for reporting this bug and sending a helpful example. (Technical detail: the fixes were in ACL2 source functions ~c[apply-instantiated-elim-rule] and ~c[eliminate-destructors-clause1], and comments in the former contain Eric's example.) Fixed a bug that supported a proof of ~c[nil] by exploiting the fact that ~il[portcullis] ~il[command]s were not included in check-sum computations in a book's ~il[certificate]. For such a proof of ~c[nil], see the relevant comment in the ACL2 source file ~c[ld.lisp] under ~c[(deflabel note-4-0 ...)]. Changed the implementation of ~ilc[add-include-book-dir]. The previous implementation could allow relative pathnames to be stored in the ~il[portcullis] ~il[command]s of ~il[certificate]s of ~il[books], which perhaps could lead to unsoundness (though we did not try to exploit this to prove ~c[nil]). Thanks to Jared Davis for reporting a bug in our first new implementation. An additional change to both ~ilc[add-include-book-dir] and ~ilc[delete-include-book-dir] is that these now work in raw-mode (~pl[set-raw-mode]). (Thanks to Dave Greve for suggesting a reduction in the warnings we produced related to raw-mode.) Note that it is no longer permitted to make a direct call of the form ~c[(table acl2-defaults-table :include-book-dir-alist ...)]; use ~ilc[add-include-book-dir] instead. Fixed a soundness bug related to ~ilc[xargs] keyword ~c[:non-executable]. New macros, ~c[defun-nx] and ~c[defund-nx], have been provided for declaring functions to be non-executable; ~pl[defun-nx]. While we expect this bug to occur only rarely if at all in practice, the following example shows how it could be evoked. ~bv[] ;;;;;;;;;;;;;;;;;;;; ;;; Book sub.lisp ;;;;;;;;;;;;;;;;;;;; (in-package \"ACL2\") (defun f () (declare (xargs :guard t :non-executable t)) (mv-let (a b c) (mv 3 4) (declare (ignore a b)) c)) (defun g () (declare (xargs :guard t)) (prog2$ (mv-let (x y z) (mv 2 3 4) (declare (ignore x y z)) nil) (f))) (defthm g-nil (equal (g) nil) :hints ((\"Goal\" :in-theory (disable (f)))) :rule-classes nil) ;;;;;;;;;;;;;;;;;;;; ;;; Book top.lisp ;;;;;;;;;;;;;;;;;;;; (in-package \"ACL2\") (include-book \"sub\") (defthm contradiction nil :hints ((\"Goal\" :use g-nil)) :rule-classes nil) ~ev[] The modification described above pertaining to ~ilc[defun-nx] also prevents execution of non-executable functions that have been ~il[trace]d. The following example illustrates the problem; now, the following ~ilc[defun] of ~c[g] is illegal, and the problem disappears if ~ilc[defun-nx] is used instead. ~bv[] (defun g (x) ; Use defun-nx to avoid an error after Version_3.6.1. (declare (xargs :guard t :non-executable t)) x) (g 3) ; causes error, as expected (trace$ g) (g 3) ; returned 3 before the bug fix; after fix, causes error as expected ~ev[] A hard error was possible when attempting to include an uncertified book containing ~il[events] of the form ~c[(make-event '(local ...))]. This has been fixed. Thanks to Sol Swords for bringing this issue to our attention. Fixed a bug in the heuristic improvement described for Version_3.6 (~pl[note-3-6]) as ``We simplified induction schemes....'' The bug had prevented, in unusual cases such as the following (notice the impossible case), a proof by induction. ~bv[] (defun foo (a x) (and (consp x) (case a (0 (foo (car x) (cdr x))) (1 (foo (cdr x) (car x))) (0 (foo a (cons x x)))))) (in-theory (disable (:type-prescription foo))) (thm (atom (foo a x))) ~ev[] Macro ~ilc[cw-gstack] did not work with an ~c[:evisc-tuple] argument. This has been fixed by changing ~c[cw-gstack] so that it now evaluates its arguments. Thanks to Sol Swords for bringing this bug to our attention. Fixed a bug in ~c[:]~ilc[pso] during the printing of failure messages for termination proofs. Fixed a bug in the handling of ~c[#.] (~pl[sharp-dot-reader]). Thanks to Bob Boyer for bringing this bug to our attention. Replaced a hard Lisp error with a clean error, in certain cases that a ~c[:]~ilc[hints] value is erroneously supplied as a non-~c[nil] atom. Example: ~c[(thm (equal x x) :hints 3)]. Fixed a bug in the interaction of function tracing with conversion of a function from ~c[:]~ilc[program] to ~c[:]~ilc[logic] mode. The following example illustrates what had been wrong. ~bv[] (defun f (x) (declare (xargs :mode :program)) (car x)) (f 3) ; raw Lisp hard error (trace$ f) (f 3) ; raw Lisp hard error (still) (defun f (x) (car x)) ; upgrade f to :logic mode (f 3) ; clean guard violation; f is no longer traced (trace$) ; uh oh - f is shown as traced (untrace$ f) (f 3) ; OUCH: hard Lisp error because old :program mode definition of ; the executable counterpart (sometimes called *1*f) was restored! ~ev[] Made a fix so that when building ACL2 with `make' option ~c[ACL2_SAFETY=3], there will no longer be any safety-0 compiled code generated. Thanks to Gary Byers for bringing this bug to our attention. Fixed a bug in the handling of ~il[override-hints] that generate custom keyword hints (~pl[custom-keyword-hints]) involving the variable ~c[stable-under-simplificationp]. Thanks to Ian Johnson for bringing this bug to our attention with explanation that included a helpful example, included as comment in the ACL2 source code for function ~c[apply-override-hint]. The ~c[saved_acl2] script in CLISP could contain unexpected characters where simple newlines were expected. Dave Greve found this in a Cygwin environment on Windows. Thanks to Dave for reporting this bug and experimenting with a fix, and thanks to the CLISP folks for providing helpful information. Fixed a bug that could make ~c[:]~ilc[oops] cause an error. Also, the ~ilc[oops] command can no longer take you back before a ~ilc[reset-prehistory] event. (GCL only) Fixed a bug that could occur when calling ~c[trace] in raw Lisp in GCL. Proof summaries have been improved, so that they account for ~il[rune]s used in rewriting that takes place when generating goals to be proved in a forcing round. Thanks to Jared Davis for sending us an example illustrating this issue. Fixed a bug that (at least in CCL) could put extra backslashes (`~c[\\]') in a pathname that ACL2 writes out to the executable script created by a build. Thanks to Gary Byers for explaining that the CCL behavior is legal (for our previous use of Common Lisp function ~c[merge-pathnames]). We closed some holes in the handling of trust tags (also known as ``ttags''; ~pl[defttag]) by ~ilc[include-book]. The following example illustrates this rather subtle situation. Consider the following book. ~bv[] (in-package \"ACL2\") (make-event (er-progn (encapsulate () (defttag :foo) (value-triple \"Imagine something bad here!\")) (value '(value-triple :some-value))) :check-expansion t) ~ev[] Formerly, the following commands succeeded. ~bv[] (certify-book \"test3\" 0 t :ttags :all) :u (include-book \"test3\" :ttags nil) ~ev[] But because of ~ilc[make-event] keyword argument ~c[:check-expansion t], we know that the event ~c[(defttag :foo)] is evaluated by the above ~ilc[include-book] form, and hence the ~c[:ttags] argument of ~c[include-book], above, should have specified ~c[:foo]. The problem was that ~ilc[defttag] forms evaluated during ~ilc[make-event] expansion did not contribute to the trust tag information stored in the book's ~il[certificate]. Note: Because of this change, one should avoid using ~ilc[make-event] with ~c[:check-expansion t] when the expansion would introduce a ~ilc[defttag] event during ~ilc[include-book] but not ~ilc[certify-book] time. For an example illustrating this issue, ~pl[make-event-details], specifically the new version of the section labeled ``A note on ttags'' at the end of that ~il[documentation] topic. Closed a small loophole that had the potential, in rare circumstances, to violate atomicity of under-the-hood updates for ACL2 ~il[arrays]. The following example was formerly allowed, but resulted in a guard-verified function (here, ~c[g]) whose guard proof obligation is not a theorem outside the ~ilc[encapsulate] event. We now disallow ~il[guard] verification for functions introduced non-~il[local]ly inside an ~ilc[encapsulate] event unless we determine that the proof obligations hold outside the ~ilc[encapsulate] event as well. ~bv[] (encapsulate ((f (x) t)) (local (defun f (x) (declare (xargs :guard t)) (consp x))) ;; ERROR! (defun g (x) (declare (xargs :guard (f x))) (car x))) ~ev[] The use of ~c[:]~ilc[comp] on ~il[stobj] functions had potentially caused a hard Lisp error; for example, this could occur when ~c[(defstobj foo fld)] was followed by ~c[:comp foop]. This has been fixed. Fixed a bug that could cause a raw Lisp error when the first argument of ~ilc[with-local-stobj] is not a symbol. It had been possible to use the reserved keyword :computed-hints-replacement as the name of a custom keyword hint (~pl[custom-keyword-hints]). This has been fixed. Thanks to Dave Greve, who pointed out a confusing hint error message (which has also been fixed) that led us to this issue. Fixed a bug that could cause a hard Lisp error, instead of a graceful ACL2 error, if keyword ~c[:backchain-limit-lst] in a rule class is given a cons that is not a true list, such as ~c[(1 . 1)]. Eliminated an error that could occur when redefining a function as a macro and then compiling, as in the example below. ~bv[] (defun foo (x) x) :redef! (defmacro foo (x) x) :comp t ~ev[] Thanks to Eric Smith for sending the above example in his bug report. Fixed a bug that could result in an assertion when a ~il[clause-processor] causes an error. ~st[NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE] See ~url[http://code.google.com/p/acl2-books/source/list] for a record of books changed or added since the preceding release, with log entries. We note in particular the new ~c[system/] directory, which begins to specify ACL2 system code in anticipation of opening the architecture of ACL2 (~pl[defattach] for a relevant tool). Some system functions were changed slightly (but with the expectation of not generally affecting ACL2 behavior) in support of the development of this directory. Those interested in contributing to further such efforts are invited to contact the ACL2 implementors. New utilities have been provided for certifying most of the distributed books with more `make'-level parallelism. For example, we have obtained close to a 12x reduction in time by using `~c[make -j 24 regression-fast]' on a 24-processor machine. For more information see ~c[books/make-targets], or to include the ~c[books/workshops] in the regression run, see ~c[books/regression-targets]. Thanks to Sol Swords for providing these nice utilities. The top-level makefile, ~c[GNUmakefile], has been fixed so that the build processes (which are inherently sequential) will ignore the ~c[-j] option of `make'. Note that regressions can still, however, be done in parallel, as the ~c[-j] option will be passed automatically to the appropriate `make' command. ~st[EMACS SUPPORT] ~st[EXPERIMENTAL VERSIONS] The HONS version, supported primarily by Bob Boyer and Warren Hunt (~pl[hons-and-memoization]), has undergone numerous improvements. For example, keyword argument ~c[:FORGET] is now supported when calling ~ilc[memoize] from within the ACL2 loop, and system function ~c[worse-than] is ~il[memoize]d with the ~c[:condition] that both terms are function applications (clearing the memo-table after each prover invocation). Thanks to Jared Davis and Sol Swords for investigating the memoization of ~c[worse-than], and with suitable ~c[condition]. Thanks also to Jared Davis for contributing structural modifications to the implementation of ~ilc[hons]. David Rager contributed modifications to the parallel version (~pl[parallelism]), which include taking advantage of atomic increments available at least since Version 1.0.21 of SBCL and Version 1.3 of CCL. ~/~/") (deflabel |NOTE-4-0(R)| :doc ":Doc-Section release-notes ACL2 Version 4.0(r) (July, 2010) Notes~/ ~/ Please ~pl[note-4-0] for changes in Version 4.0 of ACL2. ~/ ") (deflabel note-4-0-wormhole-changes :Doc ":Doc-Section note-4-0 how to convert calls of wormhole for Version 4.0~/ Here we describe how to convert an ``old-style'' call of ~ilc[wormhole] ~-[] that is, a call suitable for ACL2 versions preceding 4.0 ~-[] in which the ~c[pseudo-flag] was ~c[t]. In order to convert such a call ~bv[] (wormhole t 'name input form ...) ~ev[] to a new-style call, the following steps must be carried out. Note that the wormhole name must always be quoted now. First, eliminate the first argument, ~c[t], and add a new second argument that is the quoted lambda expression ~bv[] '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) ~ev[] Setting the entry code to ~c[:ENTER] is not necessary if you maintain the invariant (after initialization) that it is always ~c[:ENTER]. In that case, the simpler quoted lambda will suffice: ~bv[] '(lambda (whs) whs) ~ev[] Second, change the ~c[form] argument so that instead of talking about the state-global variable ~c[wormhole-output] it talks about the state-global variable ~c[wormhole-status]. Look for ~c[(@ wormhole-output)], ~c[(assign wormhole-output ...)], ~c[(f-get-global 'wormhole-output ...)] and ~c[(f-put-global 'wormhole-output ...)] in ~c[form] and replace them with expressions involving ~c[wormhole-status]. However, remember that the old data stored in ~c[wormhole-output] is now in the ~c[wormhole-data] component of the ~c[wormhole-status]. Thus, for example, an old use of ~c[(@ wormhole-output)] will typically be replaced by ~c[(wormhole-data (@ wormhole-status))] and an old use of ~c[(assign wormhole-output ...)] will typically be replaced by ~bv[] (assign wormhole-status (set-wormhole-data (@ wormhole-status) ...)) ~ev[] In summary, an old-style call like ~bv[] (wormhole t 'name input '(...1 (@ wormhole-output) ...2 ...3 (assign wormhole-output ...4) ...5) ...6) ~ev[] can become ~bv[] (wormhole 'name '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) input '(...1 (wormhole-data (@ wormhole-status)) ...2 ...3 (assign wormhole-status (set-wormhole-data (@ wormhole-status) ...4) ...5) ...6) ~ev[] In any case, and especially if your ~c[wormhole] call had a ~c[pseudo-flag] other than ~c[t], we recommend that you ~pl[wormhole].~/~/") (deflabel note-4-1 ; Eliminated state global 'translate-error-depth (unused, as noticed by David ; Rager). ; Fixed the definition of gv to pass the appropriate stobjs-in to ; throw-raw-ev-fncall. Before this fix, the following caused a guard violation ; for remove-stobjs-in-by-position. (After this fix, a different error ; occurred because of safe-mode, but that is irrelevant to this discussion.) ; (assign safe-mode 3) ; (car 3) ; (print-gv) ; Made trivial syntactic simplification in #-acl2-loop-only definition of ; with-prover-time-limit. :Doc ":Doc-Section release-notes ACL2 Version 4.1 (September, 2010) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 4.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. ~st[CHANGES TO EXISTING FEATURES] The ~il[guard] associated with calls of the macro, ~ilc[search], has been weakened so that now, given strings are no longer restricted to contain only standard characters unless the ~c[:test] argument is ~ilc[char-equal]. Modified the writing of ``hidden ~ilc[defpkg]'' forms into ~il[certificate] files (~pl[hidden-defpkg]), to support moving certificate files for distributed books, as is done by ACL2s (~pl[acl2-sedan]) and Debian releases of ACL2. Thanks to Camm Maguire for reporting a problem with Debian releases of ACL2 that led to this change. Expanded the constant ~c[*acl2-exports*] by adding ~c[intersection-equal] to the list. Thanks to Jared Davis for requesting this change. The ~c[:]~ilc[comp] utility now compiles functions that have code conditionalized for raw Lisp only (presumably because a trust tag was active when they were defined). Previously, this was not the case when ~c[:comp] was applied to more than a single function symbol. ~st[NEW FEATURES] A new macro, ~ilc[top-level], allows evaluation directly in the top level loop for forms that normally need to be evaluated inside function bodies, such as ~ilc[with-local-stobj]. ~l[top-level]. Thanks to Jared Davis for requesting such a utility. Added ~ilc[count], a Common Lisp function, to ACL2. In support of that addition, also added rewrite rule ~c[eqlablep-nth]. ~st[HEURISTIC IMPROVEMENTS] [None this time.] ~st[BUG FIXES] We fixed a soundness bug that could occur when a function that returns multiple values that is called in its own guard. Thanks to Sol Swords for reporting this bug and sending a small self-contained example, which is included in a comment in the function ~c[chk-acceptable-defuns1] in ACL2 source file ~c[defuns.lisp]. It was possible to cause an error when giving theory hints during redefinition of functions. This has been fixed. Thanks to Ian Johnson for sending an example that nicely illustrated this problem. Fixed system function ~c[io?] for the case that formal parameter ~c[commentp] is ~c[t] and ~c[vars] is non-empty. Thanks to David Rager for bringing to our attention the fact that ~c[io?] was broken for such a combination of parameters. Not exactly a bug fix, but: ~ilc[defun-sk] was breaking when a ~c[:]~ilc[guard] is specified, so we have improved the documentation (~pl[defun-sk]) to explain how to provide verified guards for a function introduced by ~ilc[defun-sk]. Thanks to Jared Davis for bringing this issue to our attention. Made a fix to the handling of interrupts, which in rare cases might have left one in a state where all subsequent proof attempts were labeled as ``Aborting due to an interrupt''. Fixed ~c[:]~ilc[pso] and related utilities, so that when proof output is redirected to a file, all summary output goes to that file rather than to the terminal. (GCL on Windows only) Removed an inappropriate check, resulting in an error about ``pathname-device,'' that could prevent Windows GCL builds of ACL2. Thanks to Camm Maguire for reporting this problem and a helpful discussion. (Windows only) Modified the computation of canonical pathnames to avoid issues of case-insensitivity, in particular for the drive (e.g., ~c[\"C:\"] vs. ~c[\"c:\"]). Thanks to Harsh Raju Chamarthi for reporting this issue and helping with its debugging. (Windows only) The value of ~c[(@ distributed-books-dir)] no longer will be missing the Windows drive prefix, for example, ~c[\"C:\"]. Thanks to Harsh Raju Chamarthi for reporting this issue and helping with its debugging. ~st[NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE] See ~url[http://code.google.com/p/acl2-books/source/list] for a record of books changed or added since the preceding release, with log entries. Modified ~c[books/Makefile-generic] by adding a new ~c[BOOKS_SKIP_COMP] variable, which is used in ~c[Makefile]s in some subdirectories of ~c[books/], in order to avoid errors when compiling certified books for multiple Lisps. ~st[EMACS SUPPORT] Distributed file ~c[emacs/emacs-acl2.el] has been modified so that the forms ~c[control-t e] and ~c[control-t control-e] now pick up package markers (~pl[sharp-bang-reader]), in the following sense: if the top-level form is preceded by a line starting with ~c[#!], then that line is included in the inserted string. Thanks to Jared Davis for suggesting this enhancement and providing a preliminary implementation. ~st[EXPERIMENTAL VERSIONS] For the ~c[HONS] version there have been some changes to ~ilc[memoize]: ~bq[] ~ilc[Memoize] accepts a new keyword, ~c[:recursive], that is a synonym for the existing keyword ~c[:inline]. Thanks to Sol Swords for requesting this addition. Moreover, it is now enforced that these keywords have Boolean values. ~ilc[Memoize] may now be called on ~c[:]~ilc[program] mode functions. Thanks to Sol Swords for requesting this enhancement. A bug has been fixed. Now, if ~ilc[memoize] is called with a ~c[:condition-fn] (with value other than ~c[nil] or ~c[t]), then the ~il[guard] of the memoized function and the ~c[:condition-fn] must be the same. Previously, one could exploit the lack of such a check to get a hard Lisp error, for example as follows. ~bv[] (defun f (x) (declare (xargs :guard t)) x) (defun cf (x) (declare (xargs :guard (consp x))) (car x)) (memoize 'f :condition-fn 'cf) (f 3) ~ev[] Memoization is now illegal for built-in functions that use underlying raw Lisp in their implementations. To see why, consider the form ~c[(gc$)], which is a macro call expanding to ~c[(gc$-fn nil)]. Previously, after evaluation of ~c[(memoize 'gc$-fn)], a call of ~c[gc$] would no longer call the garbage collector, which had been invoked by raw Lisp code. Now, evaluation of ~c[(memoize 'gc$-fn)] causes an error.~eq[] ~/~/") (deflabel |NOTE-4-1(R)| :doc ":Doc-Section release-notes ACL2 Version 4.1(r) (September, 2010) Notes~/ ~/ Please ~pl[note-4-1] for changes in Version 4.1 of ACL2. ~/ ") (deflabel note-4-2 ; Fixed a bug in ld-fn0 that was passing NIL as the second argument of a ; pprogn, and in that same area of code inserted a progn to obey the intended ; syntax of mv-let. It's not clear that any of this actually caused problems, ; however. ; Modified (in function tilde-*-book-check-sums-phrase1) the printing of ; include-book errors due to mismatch of sub-book's certificate with parent ; books' certificate, so that a full-book-name is used instead of a ; familiar-name. ; Added brief documentation for quote, following email from Sandip Ray. ; Eliminated inclp argument of functions in the translate11 nest. ; Made minor changes in include-book-fn1 that could conceivably affect handling ; of uncertified books with stale certificates. But since this may be rare, ; and it doesn't seem important to invest time to come up with an example ; illustrating such a change in behavior, we merely leave this comment rather ; than adding to the :doc string below. ; Modified the implementation of print-indented-list by having it call new ; function print-indented-list-msg, which in turn is used directly in the new ; "error message for free variables" described in this :doc topic (in ; tilde-@-free-vars-phrase, called by chk-free-vars). ; Fixed bugs in :doc set-backchain-limit. ; A potential soundness hole was plugged in the proof-checker by making ; variable pc-assign untouchable. But we don't mention this in the release ; notes proper because we have not been able to exploit this potential hole. ; Changed fmt-abbrev1 to print the message about :DOC set-iprint in column 0 ; after a newline, because otherwise the new message printed immediately after ; evaluating (retrieve ...) looks odd. ; Introduced new function our-truename, which we use in place of truename. ; This was done in support of the item below mentioning "truename", about ; "certain errors in including books". ; Fixed documentation and error message for the case that only some functions ; in a mutual-recursion are non-executable. ; Improved error message for forms such as (defattach f g :hints ...), in which ; the first argument is a symbol but there are more than two arguments. A lot ; of defattach code was changed to support the use of :program mode functions ; and a few other small changes were made in the process, e.g., world global ; 'proved-functional-instances-alist isn't (needlessly) set to its existing ; value. ; Changed the names of the arrays stored in enabled-structures that are created ; by hints during the waterfall. We did this with David Rager, in order to ; support parallel evaluation for the waterfall. ; Added source file boot-strap-pass-2.lisp, processed only during pass 2 of the ; boot-strap, which is useful for defattach. Deleted obsolete function ; load-acl2-execution-environment, rather than figure out whether it should ; also load this new file (and perhaps other pass-2 files as well). ; Split GNUmakefile target DOC into HTML, EMACS, and TEX targets (where EMACS ; replaces the old TEXINFO target). Users should not generally need to build ; the :doc themselves, but with this split we make that possible even if they ; are missing the texi2dvi and dvips programs needed for building a .ps file ; (as provided by the TEX target). ; Function elide-locals-rec had an odd case for time$1, which we have replaced ; there by time$ along with a comment that this case seems irrelevant anyhow. ; Improved note-fns-in-form to do a more thorough check, for example diving ; into skip-proofs forms (e.g., open-output-channel had been missing from ; *primitive-logic-fns-with-raw-code*, but we hadn't caught that). ; Here is the example promised in the item below labeled: "Fixed a bug in which ; the wrong attachment could be made....". A more subtle example is described ; in function install-for-add-trip-hcomp-build. ; ; ;;;;; file sub.lisp ;;;;; ; (in-package "ACL2") ; (defun sub-fn (x) x) ; ;;;;;;;;;;;;;;;;;;;;;;;;; ; ; ;;;;; file foo.lisp (note command for certification world) ;;;;; ; ; Portcullis command: ; ; (progn (defstub f (x) t) (defattach f identity)) ; (in-package "ACL2") ; (include-book "sub") ; (defun g (x) ; (declare (xargs :guard t)) ; (cons x x)) ; (defattach f g) ; ;;;;;;;;;;;;;;;;;;;;;;;;; ; ; First certify both books, first submitting the above portcullis command in ; the case of foo.lisp; then delete the compiled file for sub.lisp; then start ; ACL2 and evaluate (include-book "foo"); and finally, evaluate the form (f 3). ; The result was 3 where it should have been (3 . 3). ; Functions print-call-history and maybe-print-call-history are now in :logic ; mode, guard-verified. :Doc ":Doc-Section release-notes ACL2 Version 4.2 (January, 2011) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 4.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. ~st[CHANGES TO EXISTING FEATURES] The ~ilc[accumulated-persistence] utility can now do finer-grained tracking, providing data for individual hypotheses and the conclusion of a rule. ~l[accumulated-persistence]. To try this out, evaluate the form ~c[(accumulated-persistence :all)]; then ~pl[accumulated-persistence] for a discussion of display options using ~c[show-accumulated-persistence]. Thanks to Dave Greve for suggesting this new capability and collaborating on its design and implementation. The ~ilc[defattach] utility now permits the use of ~c[:]~ilc[program] mode functions, though this requires the use of a trust tag (~pl[defttag]). ~l[defattach] and for discussion of the new capability, ~pl[defproxy], which explains how part of this change involves allowing ~c[:]~ilc[program] mode functions to be declared ~il[non-executable]. Redefinition (~pl[ld-redefinition-action]) is no longer permitted for functions that have attachments (~pl[defattach]). In such cases, the attachment must be removed first, e.g. with ~c[(defattach foo nil)]. Made small changes to ~ilc[mv-nth] and ~ilc[defun-sk] in order to permit guard verification of functions introduced with more than one quantified variable in a ~ilc[defun-sk] form. The change to ~ilc[mv-nth] is to weaken the ~il[guard] by eliminating the requirement that the second argument satisfy ~ilc[true-listp], and replacing the call of ~ilc[endp] in the definition body by a corresponding call of ~ilc[atom]. The new definition of ~ilc[mv-nth] is thus logically equivalent to the old definition, but with a weaker guard. Thanks to Sol Swords for sending the following example, for which the final ~ilc[verify-guards] form had failed but now succeeds. ~bv[] (defstub foo (a b c) nil) (defun-sk forall-a-b-foo (c) (forall (a b) (foo a b c)) :witness-dcls ((declare (Xargs :guard t :verify-guards nil)))) (verify-guards forall-a-b-foo) ~ev[] The implementations of ~ilc[prog2$], ~ilc[time$], ~ilc[with-prover-time-limit], ~ilc[with-guard-checking], ~ilc[mbe] (and ~ilc[must-be-equal]), and ~ilc[ec-call] have changed. See the discussion below of the new utility, ~ilc[return-last]. A consequence is that ~ilc[trace$] is explicitly disallowed for these and related symbols, which formerly could cause hard Lisp errors, because they are now macros. Tracing of return-last is also disallowed. Another consequence is that time$ now prints a more abbreviated message by default, but a version of the old behavior can be obtained with ~c[:mintime nil]. The following utilities no longer print an observation about raw-mode transitions: ~c[set-raw-mode-on], ~ilc[set-raw-mode-on!], ~ilc[set-raw-mode], and ~c[set-raw-mode-off]. Thanks to Jared Davis for suggestion this change in the case of ~ilc[include-book] (which proved awkward to restrict to that case). The system function ~c[translate-and-test] now permits its ~c[LAMBDA] form to refer to the variable ~c[WORLD], which is bound to the current ACL2 logical ~il[world]. Modified abort handling to avoid talking about an interrupt when the error was caused by a Lisp error rather than an interrupt. The value of the constant ~c[*acl2-exports*], which is still a list, has been extended significantly, though only with the addition of symbols that one might reasonably have expected all along to belong to this list. A new distributed book, ~c[books/misc/check-acl2-exports.lisp], checks (at certification time) that no documented constant, macro, or function symbol in the ~c[\"ACL2\"] package has been accidentally omitted from ~c[*acl2-exports*]. Thanks to Dave Greve for helpful discussions related to this change. Improved the built-in `~c[untranslate]' functions to produce ~c[let*] expressions when appropriate (more to help with tools that call ~c[untranslate] and the like, than to help with proof output). The utility ~ilc[redo-flat] now works for ~ilc[certify-book] failures, just as it continues to work for failures of ~ilc[encapsulate] and ~ilc[progn]. The following only affects users who use trust tags to add to list values of either of the ~ilc[state] global variables ~c[program-fns-with-raw-code] or ~c[logic-fns-with-raw-code]. For functions that belong to either of the above two lists, ~c[trace$] will supply a default value of ~c[:fncall] to keyword ~c[:notinline], to avoid discarding raw-Lisp code for the function. The ~il[guard] of the macro ~ilc[intern] has been strengthened so that its second argument may no longer be either the symbol ~c[*main-lisp-package-name*] or the string ~c[\"COMMON-LISP\"]. That change supports another change, namely that the following symbols in the ~c[\"COMMON-LISP\"] package are no longer allowed into ACL2: symbols in that package that are not members of the list constant ~c[*common-lisp-symbols-from-main-lisp-package*] yet are imported into the ~c[\"COMMON-LISP\"] package from another package. ~l[pkg-imports] and ~pl[symbol-package-name]. To see why we made that change, consider for example the following theorem, which ACL2 was able to prove when the host Lisp is GCL. ~bv[] (let ((x \"ALLOCATE\") (y 'car)) (implies (and (stringp x) (symbolp y) (equal (symbol-package-name y) \"COMMON-LISP\")) (equal (symbol-package-name (intern-in-package-of-symbol x y)) \"SYSTEM\"))) ~ev[] Now suppose that one includes a book with this theorem (with ~c[:]~ilc[rule-classes] ~c[nil]), using an ACL2 built on top of a different host Lisp, say CCL, that does not import the symbol ~c[SYSTEM::ALLOCATE] into the ~c[\"COMMON-LISP\"] package. Then then one can prove ~c[nil] by giving this theorem as a ~c[:use] hint. The axioms introduced by ~ilc[defpkg] have changed. See the discussion of ~ilc[pkg-imports] under ``NEW FEATURES'' below. The error message for free variables (e.g., in definition bodies and guards) now supplies additional information when there are governing IF conditions. Thanks to Jared Davis for requesting this enhancement and collaborating in its design. The command ~c[:]~ilc[redef-] now turns off redefinition. Improved proof output in the case of a ~c[:]~ilc[clause-processor] hint that proves the goal, so that the clause-processor function name is printed. The ~ilc[proof-checker] command `~c[then]' now stops at the first failure (if any). It is no longer permitted to submit definitions in ~c[:logic] mode for merely part of an existing ~ilc[mutual-recursion] event. Such an action left the user in an odd state and seemed a potential soundness hole. The function ~ilc[break$] is now in ~c[:]~ilc[logic] mode. Thanks to Jared Davis for requesting this enhancement. The macro ~ilc[verify-termination] now provides clearer output in the case that it is redundant. More important perhaps, as a courtesy it now causes an error when applied to a constrained function, since presumably such an application was unintended (as the constrained function could never have been in ~c[:]~ilc[program] mode). Note that if one desires different behavior, one can create one's own version of ~ilc[verify-termination] (but with a different name). Improved the ~il[guard]s for the following functions, often weakening them, to reflect more precisely the requirements for calling ~ilc[eq]: ~c[alist-difference-eq], ~c[intersection-eq], ~c[intersection1-eq], ~ilc[intersectp-eq], ~c[not-in-domain-eq], ~c[set-difference-assoc-eq], ~c[set-equalp-eq], and ~ilc[union-eq]. Thanks to Jared Davis for pointing out this issue for ~ilc[intersectp-eq]. (CCL only) Made a change that can reduce the size of a compiled file produced by ~ilc[certify-book] when the host Lisp is CCL, by discarding source information (for example, discarding ~ilc[local] events). ~st[NEW FEATURES] See the discussion above about new statistics that can be gathered by the ~ilc[accumulated-persistence] utility. A new hint, ~c[:]~ilc[instructions], allows use of the ~il[proof-checker] at the level of ~il[hints] to the prover. Thanks to Pete Manolios for requesting this feature (in 2001!). ~l[instructions]. (For system hackers) There are new versions of system functions ~c[translate1] and ~c[translate], namely ~c[translate1-cmp] and ~c[translate-cmp] respectively, that do not take or return ~ilc[state]. See the Essay on Context-message Pairs for relevant information. Thanks to David Rager for collaborating on this enhancement. A new utility, ~ilc[return-last], is now the unique ACL2 function that can pass back a multiple value result from one of its arguments. Thus, now the following are macros whose calls ultimately expand to calls of ~ilc[return-last]: ~ilc[prog2$], ~ilc[time$], ~ilc[with-prover-time-limit], ~ilc[with-guard-checking], ~ilc[mbe] (and ~ilc[must-be-equal]), and ~ilc[ec-call]. With an active trust tag, an advanced user can now write code that has side effects in raw Lisp; ~pl[return-last]. Thanks to Jared Davis for requesting this feature. A new function, ~ilc[pkg-imports], specifies the list of symbols imported into a given package. The axioms for ~ilc[defpkg] have been strengthened, taking advantage of this function. Now one can prove theorems using ACL2 that we believe could not previously be proved using ACL2, for example the following. ~bv[] (equal (symbol-package-name (intern-in-package-of-symbol str t)) (symbol-package-name (intern-in-package-of-symbol str nil))) ~ev[] Thanks to Sol Swords for a helpful report, which included the example above. ~l[pkg-imports] and ~pl[defpkg]. Added function ~ilc[no-duplicatesp-eq]. Added a new hint keyword, ~c[:]~ilc[backchain-limit-rw], to control the level of backchaining for ~il[rewrite], ~il[meta], and ~il[linear] rules. This overrides, for the current goal and (as with ~c[:]~ilc[in-theory] hints) descendent goals, the default ~il[backchain-limit] (~pl[set-backchain-limit]). Thanks to Jared Davis for requesting this feature. Support is now provided for creating and certifying books that do not depend on trust tags, in the case that the only use of trust tags is during ~ilc[make-event] expansion. ~l[set-write-acl2x]. Thanks to Sol Swords for reporting a couple of bugs in a preliminary implementation. Function ~c[(file-write-date$ filename state)] has been added, giving the write date of the given file. ~l[forward-chaining-reports] for how to get new reports on the forward chaining activity occurring in your proof attempts. Thanks to Dave Greve for inspiring the addition of this utility. It is now possible to use ACL2's printing utilities to return strings, by opening output channels to the keyword ~c[:STRING] rather than to filenames. ~l[io]. Thanks to Jared Davis for a helpful conversation that led us to add this feature. ~st[HEURISTIC IMPROVEMENTS] We have slightly improved the handling of ~c[:]~il[forward-chaining] rules that contain free variables. Formerly, such rules might fire only once, when the first match for a free variable is discovered, and would not fire again even if subsequent forward chaining made available another match. This made it difficult to predict whether a rule with free variables would fire or not, depending as it did on the order in which newly derived conclusions were added. The new handling is a little slower but more predictable. Thanks to Dave Greve for sending a helpful example that led us to consider making such an improvement. We have slightly improved the so-called ``~il[type-set]'' heuristics to work a bit harder on terms of the form ~c[(rec term)], where ~c[rec] is a so-called ``compound-recognizer'' function, that is, a function with a corresponding enabled ~c[:]~ilc[compound-recognizer] rule. Thanks to Jared Davis for sending a helpful example (found, in essence, in the modified function ~c[type-set-rec], source file ~c[type-set-b.lisp]). We made three heuristic improvements in the way contexts (so-called ``type-alists'') are computed from goals (``clauses''). Although these changes did not noticeably affect timing results for the ACL2 regression suite, they can be very helpful for goals with many hypotheses. Thanks to Dave Greve for sending a useful example (one where we found a goal with 233 hypotheses!). The algorithm for substituting alists into terms was modified. This change is unlikely to affect many users, but in one example it resulted in a speed-up of about 21%. Thanks to Dave Greve for supplying that example. Sped up ~ilc[include-book] a bit by memoizing checksums of symbols. (This change pertains to ``normal'' ACL2 only, not the ~ilc[hons] version (~pl[hons-and-memoization], where such memoization already occurred.) We found about a 23% speed-up on an example from Dave Greve. Made a small change to the algorithm used to prove hypotheses of ~c[:]~ilc[type-prescription] rules (ACL2 source function ~c[type-set-relieve-hyps]). One change avoids a linear walk through the context (the ``type-alist'' structure), while the other could avoid storing unnecessary ~ilc[force]d assumptions (into the so-called ``tag-tree''). ~st[BUG FIXES] Fixed a long-standing soundness bug caused by the interaction of ~ilc[force]d hypotheses with destructor ~il[elim]ination. The fix was to avoid using forcing when building the context (so-called ``type-alist'') when the goal is considered for destructor elimination; those who are interested can see a discussion in source function ~c[eliminate-destructors-clause1], which includes a proof of ~c[nil] that no longer succeeds. A similar fix was made for generalization, though we have not exploited the previous code to prove ~c[nil] in that case. Fixed a bug that allowed book certification to ignore ~c[skip-proofs] around ~ilc[encapsulate] ~il[events]. Thus, a book could contain an event of the form ~c[(skip-proofs (encapsulate ...))], and a call of ~ilc[certify-book] on that book could succeed even without supplying keyword ~c[:skip-proofs-okp t]. This bug was introduced in Version 3.5 (May, 2009). Fixed a bug that could occur when including a book that attempts to redefine a function as a macro, or vice-versa. (For details of the issue, see the comment in the definition of variable ~c[*hcomp-fn-macro-restore-ht*] in source file ~c[other-events.lisp].) (Windows only) Fixed handling of the Windows drive so that an executable image saved on one machine can be used on another, even with a different drive. Thanks to Harsh Raju Chamarthi for reporting this issue and doing a lot of testing and collaboration to help us get this right. Made a change to avoid possible low-level errors, such as bus errors, when quitting ACL2 by calling ~ilc[good-bye] or its synonyms. This was occurring in CCL, and we are grateful to Gary Byers for helping us find the source of those errors (which basically was that ACL2 was attempting to quit while already in the process of quitting). Fixed a bug in ~ilc[with-guard-checking], which was being ignored in function bodies. Fixed a bug in ~ilc[top-level], which was not reverting the logical ~il[world] when an error resulted from evaluation of the given form. Thanks to Jared Davis for bringing this bug to our attention. Fixed a long-standing bug (back through Version 2.7) that was discarding changes to the connected book directory (~pl[cbd]) when exiting and then re-entering the top-level ACL2 loop (with ~ilc[lp]). In some host Lisps, it has been possible to be in a situation where it is impossible to interrupt checkpoint printing during the summary. We had thought this solved when the host Lisp was CCL, but Sol Swords sent us an example (for which we are grateful) illustrating that this behavior could occur. This has been fixed. Fixed a bug in a proof obligation generated for ~c[:]~ilc[meta] and ~c[:]~ilc[clause-processor] rules, that the ~il[guard] on the metafunction or clause-processor function, ~c[fn], holds under suitable assumptions. Those assumptions include not only that the first argument of ~c[fn] satisfies ~ilc[pseudo-termp], but also that all ~il[stobj] inputs satisfy the corresponding stobj recognizer predicates. We had erroneously considered stobj outputs of ~c[fn] instead of stobj inputs. Thanks to Sol Swords for bringing this bug to our attention with a simple example, and correctly pointing us to the bug in our code. Fixed the following bugs in ~ilc[defattach]. We hadn't always been applying the full functional substitution when generating guard proof obligations. We had been able to hit an assertion when reattaching to more than one function. Attachment was permitted in the case of an untouchable function (~pl[remove-untouchable]). Finally, the guard proof obligation could fail in the case that the two functions have different formal parameter lists, as in the following example. ~bv[] (encapsulate ((foo (x) x :guard (symbolp x))) (local (defun foo (x) x))) (defun bar (x2) (declare (xargs :guard (symbolp x2))) x2) (defattach foo bar) ~ev[] Fixed a raw Lisp error that could be caused by including a book using ~ilc[make-event] to define a function symbol in a locally-introduced package. An example appears in a comment in ACL2 source function ~c[write-expansion-file]. Made a change that can prevent an error near the end of book certification when the underlying Host Lisp is Allegro Common Lisp, in the case that environment variable ~c[ACL2_SYSTEM_BOOKS] has been set to the name of a directory with a parent that is a soft link. Thanks to Dave Greve for supplying an example to led us to this fix, which involves avoiding Allegro CL's implementation of the Common Lisp function, ~c[truename]. Fixed a bug that was failing to substitute fully using bindings of free variables in ~ilc[force]d hypotheses. A related change is that instead of binding such a free variable to a new variable of the form ~c[???-Y], the new variable is now of the form ~c[UNBOUND-FREE-Y]. Fixed a bug that could inhibit the printing of certain theory warnings (and probably, in the other direction, cause inappropriate such printing). We eliminated excessive ~c[\"Raw-mode\"] warnings about ~ilc[add-include-book-dir] that could be generated by the use of raw-mode during ~ilc[include-book]. Thanks to Dave Greve for bringing this issue to our attention. Fixed the printing of results from forms within an ~ilc[encapsulate], so that they are abbreviated according to the ~ilc[ld-evisc-tuple]. It is now possible to evaluate ~il[stobj]-related forms after evaluating ~c[:]~ilc[set-guard-checking] ~c[:none] or ~c[:]~ilc[set-guard-checking] ~c[nil], even in cases where such evaluation formerly caused a guard violation due to a bug in ACL2. Here is an example of an error that no longer occurs. ~bv[] ACL2 !>(defstobj st fld) Summary Form: ( DEFSTOBJ ST ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ST ACL2 !>(set-guard-checking :none) Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fld st) ACL2 Error in TOP-LEVEL: The guard for the function call (FLD ST), which is (STP ST), is violated by the arguments in the call (FLD ST). [... etc. ...] ~ev[] You can understand how things now work by imagining that when a function introduced by ~ilc[defstobj] is called, ~ilc[guard]-checking values of ~c[:none] or ~c[nil] are temporarily converted to ~c[t]. Thanks to Pete Manolios, Ian Johnson, and Harsh Raju Chamarthi for requesting this improvement. Fixed a bug in which the wrong attachment could be made when the same function has an attachment in a book and another in the certification world of that book (possibly even built into ACL2), if the load of a compiled file is aborted because a sub-book's compiled file is missing. The bug has been present since the time that ~ilc[defattach] was added (Version_4.0). An example may be found in a comment in the ~ilc[deflabel] for ~c[note-4-2] (ACL2 source file ~c[ld.lisp]). The ~c[:]~ilc[doc] and related utilities now cause a clean error when provided other than a symbol. Thanks to Jared Davis for pointing out the raw Lisp error that had occurred in such cases. It had been the case that in raw-mode (~pl[set-raw-mode]), it was possible to confuse ~ilc[include-book] when including a book in a directory different from the current directory. This has been fixed. Thanks to Hanbing Liu for bringing this problem to our attention with a small example. ~st[NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE] Many changes have been made to the distributed books, thanks to an active ACL2 community. You can contribute books and obtain updates between ACL2 releases by visiting the ~c[acl2-books] project web page, ~url[http://acl2-books.googlecode.com/]. There is new ~c[Makefile] support for certifying just some of the distributed books. ~l[books-certification-classic], in particular discussion of the variable ~c[ACL2_BOOK_DIRS]. Thanks to Sandip Ray for requesting this enhancement. The ~il[documentation] for ~ilc[make-event] now points to a new book, ~c[books/make-event/defrule.lisp], that shows how ~c[make-event] can be used to do macroexpansion before generating ~il[events]. Thanks to Carl Eastlund for useful interaction on the acl2-help mailing list that led us to add this example. ~st[EMACS SUPPORT] Incorporated a version of changes from Jared Davis to the ~c[control-t f] emacs utility (distributed file ~c[emacs/emacs-acl2.el]), so that one can fill a format string from anywhere within the string. ~st[EXPERIMENTAL VERSIONS] We refrain from listing changes here to experimental versions, other than an enhancement to the ~il[HONS] version that can reduce sizes of ~il[certificate] files, by applying ~ilc[hons-copy] to introduce structure sharing (ACL2 source function ~c[make-certificate-file1]). ~/~/") (deflabel |NOTE-4-2(R)| :doc ":Doc-Section release-notes ACL2 Version 4.2(r) (January, 2011) Notes~/ ~/ Please ~pl[note-4-2] for changes in Version 4.2 of ACL2. ~/ ") (deflabel note-4-3 ; The following example illustrates the soundness bug related to mbe that is ; mentioned in :doc note-4-3. To prove nil: ; (certify-book "sub") ; :u ; (certify-book "mid") ; :u ; (certify-book "top") ; The problem is that the macro mac expands differently in the ACL2 loop from ; how it expands in raw Lisp. The bug fix is to check equality of the values ; of the :logic and :exec forms when in safe-mode. ;;;;;;;;;;;;;;; ; Book sub.lisp ;;;;;;;;;;;;;;; ; (in-package "ACL2") ; ; (defmacro mac () ; (mbe :logic ''logic ; :exec ''exec)) ; ; (defconst *a* ; (mac)) ;;;;;;;;;;;;;;; ; Book mid.lisp ;;;;;;;;;;;;;;; ; (in-package "ACL2") ; ; (local (include-book "sub")) ; ; (defmacro mac () ; (mbe :logic ''logic ; :exec ''exec)) ; ; (defconst *a* ; (mac)) ; ; (defthm got-exec ; (equal *a* 'exec) ; :rule-classes nil) ;;;;;;;;;;;;;;; ; Book top.lisp ;;;;;;;;;;;;;;; ; (in-package "ACL2") ; ; (defmacro mac () ; (mbe :logic ''logic ; :exec ''exec)) ; ; (defconst *a* ; (mac)) ; ; (defthm got-logic ; (equal *a* 'logic) ; :rule-classes nil) ; ; (include-book "mid") ; ; (defthm contradiction ; nil ; :hints (("Goal" :use (got-exec got-logic))) ; :rule-classes nil) ;;;;;;;;;;;;;;; end of example ; Modified some doc-printing functions in support of translation to xdoc. the ; process, the HTML output has become prettier; see item about HTML, below. ; Added new macro mv-to-state. ; Changed the name mv-let? to bdd-mv-let to avoid potential confusion with ; mv?-let. ; Functions enable-iprint-ar and disable-iprint-ar now return two values ; instead of three. ; Here is the example promised in :doc note-4-3 to illustrate a bug in the ; loop-checking done on behalf of defattach. The bug was fixed in function ; update-attachment-records1. ;;;;;;;;;;;;;;; start example ; (progn ; (encapsulate ; ((f1 (x) t) ; (f2 (x) t)) ; (local (defun f1 (x) x)) ; (local (defun f2 (x) x))) ; ; (encapsulate ; ((g1 (x) t) ; (g2 (x) t)) ; (local (defun g1 (x) x)) ; (local (defun g2 (x) x)) ; (defthm g1-f1 ; (equal (g1 (f1 x)) ; (f1 x)))) ; ; (encapsulate ; ((h1 (x) t) ; (h2 (x) t)) ; (local (defun h1 (x) x)) ; (local (defun h2 (x) x)))) ; ; (defattach f2 h1) ; ; ; The following should cause the following loop to be reported, but didn't: ; G1 is an extended ancestor of H1. ; H1 is an extended ancestor of F1. ; F1 is an extended ancestor of G1. ; (defattach h2 g2) ;;;;;;;;;;;;;;; end of example ; Replaced a couple of calls of 1+ by 1+f in fn-count-evg-rec, after Robert ; Krug brought these to our attention. ; Improved the error message when discovering during load of compiled or ; expansion file that a defconst is not redundant. ; Fixed macro io? so that we are not left in a wormhole when there is an error ; (as happened previously when the commentp argument of io? was t). ; Regarding "Fixed a bug in detection of package redefinition.": The use of ; member-equal instead of assoc-equal in maybe-introduce-empty-pkg-2 allows (at ; least on quick analysis) every package with empty imports to be considered a ; "virgin" package, which may have allowed (again, on quick analysis) illegal ; package redefinition to occur. ; Made efficiency improvement in check-vars-not-free, which is minor but ; perhaps worth a couple percent since we have added equality-variants. ; We no longer make some duplicate cons-tag-trees calls in defaxiom-fn and ; defthm-fn1. ; Removed needless (and confusing) #+ansi-cl in handler-bind call for sbcl in ; with-warnings-suppressed. ; Modified the handling of package definitions in expansion files (macro ; maybe-introduce-empty-pkg-1). ; We took preliminary steps towards removing uses of the big-clock field of ; state. ; Modified deletion of compiled file of acl2-fns.lisp to occur at the Lisp ; level instead of using GNUmakefile. ; Deleted delete-pair and remove-first-pair, which each duplicated the ; functionality of delete-assoc-eq. ; Eliminated cons-into-ttree in favor of cons-tag-trees. ; Moved assert$ to the right place in cmp-to-error-triple (thanks to David ; Rager for correcting an error in our initial change). ; Below we show how to obtain a hard Lisp error in Version_4.2, when, as ; mentioned in the :doc string below, "including books with hidden packages". ; The problem was that write-expansion-file was deciding whether to push ; maybe-introduce-empty-pkg-1 and maybe-introduce-empty-pkg-2 forms based on ; the known-package-alist just before pass 1 of certify-book, rather than just ; after it. But a second defpkg form can use symbols defined in the first, so ; since it's pretty cheap just to lay down all such forms, that's what we do. ; sloth:~/temp> cat sub.lisp ; ; First execute these two commands in the certification world: ; ; (defpkg "FOO" '(a)) ; ; (defpkg "BAR" '(foo::b)) ; ; ; Then: ; ; (certify-book "sub" 2) ; ; (in-package "ACL2") ; ; (defun g (x) x) ; sloth:~/temp> cat top.lisp ; ; Just do this: ; ; (certify-book "top") ; ; (in-package "ACL2") ; ; (local (include-book "sub")) ; ; (defun h (x) x) ; sloth:~/temp> ; ; ACL2 !>(include-book "top") ; ; *********************************************** ; ************ ABORTING from raw Lisp *********** ; Error: There is no package named "FOO" . ; *********************************************** :Doc ":Doc-Section release-notes ACL2 Version 4.3 (July, 2011) Notes~/ NOTE! New users can ignore these release notes, because the ~il[documentation] has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 4.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level and to distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. ~st[CHANGES TO EXISTING FEATURES] Significant changes have been made for list-processing primitives such as ~ilc[member] and ~ilc[assoc]; ~pl[equality-variants]. In summary: instead of separate functions based on ~ilc[eq], ~ilc[eql], and ~ilc[equal], there is essentially just one function, which is based on ~ilc[equal]; the ~ilc[eq] and ~ilc[eql] variants are logically just the ~ilc[equal] variant. For example, ~ilc[member-eq] and ~ilc[member] are macros that generate corresponding calls of ~ilc[member-equal] in the logic, although in raw Lisp they will execute using tests ~ilc[eq] and ~ilc[eql], respectively. References to any of these in logical contexts such as ~il[theories] are now references to the function based on ~ilc[equal]; for example, the hint ~c[:in-theory (disable member)] is completely equivalent to the hint ~c[:in-theory (disable member-equal)]. Distributed books have been modified as necessary to accommodate this change. While the need for such changes was relatively infrequent, changes were for example needed in contexts where terms are manipulated directly; for example, ~ilc[defevaluator] needs to mention ~ilc[member-equal] rather than ~ilc[member], just as it was already the case to mention, say, ~ilc[binary-append] rather than ~ilc[append]. Again, ~pl[equality-variants] for more information about equality variants. A few improvements were made in support of the modified treatment of equality variants discussed above. The changes include the following.~bq[] o We now allow the use of macro aliases (~pl[macro-aliases-table] in ~c[:trigger-fns] of rules (~pl[rule-classes]). o We now remove so-called ``guard holders'' (including calls of ~ilc[return-last], hence of ~ilc[mbe]) in ~c[:trigger-terms] of rules. o We also remove guard holders in formulas of ~c[:]~ilc[congruence] and ~ilc[type-prescription] rules. o Macros ~c[union-eq] and ~c[intersection-eq] can now take any positive number of arguments, and ~ilc[union-eq] can take zero arguments. (Thanks to Jared Davis for requesting this enhancement.) The same can be said for new macros ~ilc[union$] and ~ilc[intersection$], respectively. o A few changes were made to built-in theorems from source file ~c[axioms.lisp], in particular disabling ~c[:]~ilc[type-prescription] rule ~c[consp-assoc-equal] (formerly two enabled rules, ~c[consp-assoc-eq] and ~c[consp-assoc]) but adding this theorem as a ~c[:forward-chaining] rule, and similarly for ~c[true-list-listp-forward-to-true-listp-assoc-equal] (and eliminating rule ~c[true-list-listp-forward-to-true-listp-assoc-eq]; and disabling rule ~c[true-listp-cadr-assoc-eq-for-open-channels-p]. Also, theorem ~c[all-boundp-preserves-assoc] has been renamed to ~c[all-boundp-preserves-assoc-equal] and also strengthened. o Some ~il[guard]s were slightly improved (logically weaker or the same).~eq[] Improved ~c[get-output-stream-string$] to allow for a context and to do genuine error printing instead of using ~ilc[cw]. ~l[io]. Added the symbols ~ilc[flet] and ~ilc[with-local-stobj] to ~c[*acl2-exports*]. A small change was made to the processing of more than one ~c[:]~ilc[guard] declaration for the same function. In particular, a guard of ~c[t] is essentially ignored. Attachments are now allowed during evaluation of the first argument of ~ilc[prog2$] even in contexts (such as proofs) during which the use of attachments is normally prohibited. More generally, the second of the three arguments of ~ilc[return-last] has this property, except in the case of ~ilc[mbe] (or related macros like ~ilc[mbe1]), where the ~c[exec] argument may provide the value. Thanks to Sol Swords for useful discussions leading us to implement this enhancement. The restriction has been removed that prohibited the use of ~ilc[mbe] inside ~ilc[encapsulate] ~il[events] with a non-empty ~il[signature]. This restriction was introduced in Version 3.4, but has not been necessary since Version 4.0, when we first started disallowing ~il[guard] verification for functions introduced non-~il[local]ly inside such ~ilc[encapsulate] events. We weakened the checks involving common ancestors for evaluator and ~il[meta] (and ~il[clause-processor]) functions (~pl[evaluator-restrictions]) so that except in the ~ilc[mbe] case, the next-to-last argument of ~ilc[return-last] is not considered. Thanks to Sol Swords for bringing this issue to our attention. The macro ~ilc[append] no longer requires at least two arguments. Thanks to Dave Greve for requesting this enhancement. (Mostly for system hackers) Now, ~ilc[break-on-error] breaks at a more appropriate (earlier) time for certain system functions that do not return state, such as ~c[translate11]. Thanks to David Rager for requesting this improvement. ~ilc[Show-accumulated-persistence] may take a new argument, ~c[:runes], which simply causes an alphabetical list of ~il[rune]s to be printed out. Improved ~ilc[trace$] so that ~c[:entry], ~c[:exit], and ~c[:cond] forms may reference ~c[state] even if the function being traced does not include ~c[state] as a formal. The system function ~c[acl2x-expansion-alist] now takes a second argument, namely ~ilc[state]. This change allows for more flexibility in the sorts of attachments that can be made to this function (~pl[defattach]). Thanks to Jared Davis and Sol Swords for requesting this enhancement and providing a preliminary implementation. An obscure ~il[proof-checker] change, unlikely to affect users, replaces the state global variables ~c[erp], ~c[val], ~c[print-macroexpansion-flg], and ~c[print-prompt-and-instr-flg] by ~c[pc-erp], ~c[pc-val], ~c[pc-print-macroexpansion-flg], and ~c[pc-print-prompt-and-instr-flg], respectively. ~il[State] globals ~c[fmt-hard-right-margin] and ~c[fmt-soft-right-margin] are now untouchable (~pl[set-fmt-hard-right-margin] and ~pl[push-untouchable]). If you bind these ~c[state] globals with ~ilc[state-global-let*], then you will need to do so with appropriate setters to restore their values, for example as follows. ~bv[] (state-global-let* ((fmt-hard-right-margin 500 set-fmt-hard-right-margin) (fmt-soft-right-margin 480 set-fmt-soft-right-margin)) ...) ~ev[] The error message has been improved for the case of evaluating an undefined function that has an attachment (~pl[defattach]). Thanks to Jared Davis for sending the following example, which illustrates the additional part of the message. ~bv[] ACL2 !>(defstub foo (x) t) [[... output omitted ...]] ACL2 !>(defattach foo identity) [[... output omitted ...]] ACL2 !>(defconst *x* (foo 3)) ACL2 Error in ( DEFCONST *X* ...): ACL2 cannot ev the call of undefined function FOO on argument list: (3) Note that because of logical considerations, attachments (including IDENTITY) must not be called in this context. [[... additional output omitted ...]] ~ev[] The directory string supplied to ~ilc[add-include-book-dir] no longer must terminate with the `~c[/]' character, as had been required in some Lisp implementations. Thanks to Sol Swords for bringing this issue to our attention. We no longer print induction schemes with ~il[gag-mode]; use ~c[:]~ilc[pso] if you want to see them. Thanks to Dave Greve for this suggestion. It is now legal to supply a constant for a ~il[stobj] array dimension. ~l[defstobj]. Thanks to Warren Hunt for requesting this enhancement. We cleaned up a few issues with ~ilc[defpkg].~bq[] o It is no longer illegal to submit a ~ilc[defpkg] form in raw-mode (~pl[set-raw-mode]). Thanks to Jun Sawada for reporting an example in which an ~ilc[include-book] form submitted in raw-mode caused an error because of a `hidden' ~ilc[defpkg] form (~pl[hidden-defpkg]). There will no longer be an error in such cases. o It had been the case that ~il[local]ly including a book could make it possible to use a package defined by that book. Consider for example the following book, ~c[foo.lisp]. ~bv[] (in-package \"ACL2\") (local (include-book \"arithmetic/top\" :dir :system)) ~ev[] After certifying this book, it had been possible to admit the following events in a new session. ~bv[] (include-book \"foo\") (defconst acl2-asg::*foo* 3) (defconst *c* 'acl2-asg::xyz) ~ev[] In Version_4.3, neither of these ~ilc[defconst] events is admitted. o A hard Lisp error is now avoided that had been possible in rare cases when including books with hidden packages (~pl[hidden-defpkg]). An example may be found in a comment in the ~ilc[deflabel] for ~c[note-4-3] (in ACL2 source file ~c[ld.lisp]).~eq[] The undocumented (but sometimes useful) functions ~c[packn1] and ~c[packn] are now ~il[guard]-verified ~c[:]~ilc[logic] mode functions. Thanks to Sandip Ray for requesting this enhancement. It had been the case that when including a book, functions defined in the book's certification ~il[world] (that is, in its ~il[portcullis] ~il[command]s) were typically not given compiled code. That has been fixed. The commands ~c[:]~ilc[pl] and ~c[:]~ilc[pl2] have been improved, primarily by printing information for more rule classes. ~l[pl] and ~pl[pl2]. See also the item below about the new ~il[proof-checker] command, ~c[show-type-prescriptions]. ~st[NEW FEATURES] New macros ~ilc[mv?-let] and ~ilc[mv?] extend the funtionality of ~ilc[mv-let] and ~ilc[mv] (respectively) to the case of a single value. Macro ~il[with-local-state] is available for system programmers who wish bind ~ilc[state] locally, essentially using ~il[with-local-stobj]. But this should only be done with extreme care, and it requires an active trust tag; ~pl[with-local-state]. Formatted printing functions now have analogues that print to strings and do not take an output channel or ~ilc[state] as arguments. ~l[printing-to-strings]. The system function ~c[ancestors-check] is now available for verified modification by users, i.e., attachment using ~c[(defattach ancestors-check )]. Thanks to Robert Krug for providing the necessary proof support, which we modified only in small ways. New macros, ~c[observation-cw] and ~c[warning$-cw], provide formatted printing of ~ilc[observation]s and warnings (respectively) without ~ilc[state]. Thanks to Harsh Raju Chamarthi and David Rager for requests leading to these utilities. ~c[Observation-cw] is now used in some of the distributed books (thanks to Robert Krug for useful interaction for that). The ~il[proof-checker] command ~c[type-alist] (~pl[proof-checker-commands]) now takes an optional third argument that causes the production of forward-chaining reports (~pl[forward-chaining-reports]). Thanks to Dave Greve for requesting such an enhancement. The reports generated by forward-chaining, ~il[forward-chaining-reports], have been changed to indicate when a conclusion reached by forward chaining is ~c[REDUNDANT] with respect to the type information already known. Thanks to Dave Greve for suggesting this enhancement. The utility ~ilc[with-prover-time-limit] is now legal for ~il[events] (~pl[embedded-event-form]). For example, the following is now legal. ~bv[] (encapsulate () (with-prover-time-limit 2 (defthm append-assoc (equal (append (append x y) z) (append x (append y z)))))) ~ev[] The new utility ~ilc[with-prover-step-limit] is analogous to the utility ~ilc[with-prover-time-limit], but counts ``prover steps'' rather than checking for time elapsed. ~l[with-prover-step-limit]. Also ~pl[set-prover-step-limit] to provide a default step-limit. Note that just as ~ilc[with-prover-time-limit] may now be used to create ~il[events], as discussed just above, ~ilc[with-prover-step-limit] may also be used to create ~il[events]. Thanks to Carl Eastlund for requesting support for step-limits. The macro ~ilc[progn$] is analogous to ~ilc[prog2$], but allows an arbitrary number of arguments. For example: ~bv[] ACL2 !>:trans1 (progn$ (f1 x) (f2 x) (f3 x)) (PROG2$ (F1 X) (PROG2$ (F2 X) (F3 X))) ACL2 !> ~ev[] Thanks to David Rager for contributing this macro. The macro ~ilc[defattach] may now be supplied the argument ~c[:skip-checks :cycles]. In this case, as with argument ~c[:skip-checks t], a trust tag is reuired (~pl[defttag]), and no logical claims are made. The effect is to avoid the usual check that the extended ancestor relation has no cycles (~pl[defattach]). Thanks to Dave Greve for requesting this feature. You can now limit the printing of subgoal names when using ~c[:]~ilc[set-gag-mode]~c[ :goals]. ~l[set-print-clause-ids]. Thanks to Karl Hoech for a suggestion leading to this enhancement. A new ~il[proof-checker] command, ~c[show-type-prescriptions], or ~c[st] for short, provides information about ~c[:]~ilc[type-prescription] rules that match a given term. Thanks to Dave Greve for requesting this enhancement. See also the item above about related improvements to commands ~c[:]~ilc[pl] and ~c[:]~ilc[pl2]. ~st[HEURISTIC IMPROVEMENTS] ACL2 now avoids some repeated attempts to rewrite hypotheses of rewrite rules. ~l[set-rw-cache-state] for a discussion of this behavior and how to avoid it. The default behavior has been observed to reduce by 11% the overall time required to complete a regression. Here are the directories that had the top three time decreases and top three time increases, shown in seconds. ~bv[] -368 coi/gacc (1064 down to 696: decrease of 35%) -220 workshops/1999/ste (664 down to 444: decrease of 33%) -148 unicode (331 down to 183: decrease of 45%) .... +7 workshops/2002/cowles-flat/support (229 up to 236: increase of 3%) +8 workshops/1999/ivy/ivy-v2/ivy-sources (508 up to 516: increase of 2%) +12 workshops/2009/hardin/deque-stobj (78 up to 91: increase of 17%) ~ev[] The so-called ``ancestors check,'' which is used to limit backchaining, has been strengthened so that two calls of ~ilc[equal] are considered the same even if their arguments appear in the opposite order. Thanks to Robert Krug for providing an implementation and a useful discussion. The check for ~il[irrelevant-formals] in processing of ~ilc[defun]s has been made more efficient. Thanks to Eric Smith for reporting this issue in 2001 (!) and thanks to Warren Hunt for recently sending an example. For that example, we have seen the time for the ~il[irrelevant-formals] check reduced from about 10 seconds to about 0.04 seconds. (GCL only) The macro ~ilc[mv] has been modified so that certain fixnum boxing can be avoided. (Allegro CL only) We have set to ~c[nil] four Allegro CL variables that otherwise enable storing of certain source information (for details, see the discussion of ``cross-referencing'' in ACL2 source file ~c[acl2-init.lisp]). As a result of this change we have about a 6% speedup on the regression suite, but a 27% time reduction on an example that includes a lot of books. Exhaustive matching for the case of ~il[free-variables] has been extended to ~il[type-prescription] rules, in analogy to the default setting ~c[:match-free :all] already in place for ~il[rewrite], ~il[linear], and ~il[forward-chaining] rules. ~l[free-variables-type-prescription]. Thanks to Dave Greve for requesting this enhancement. ~st[BUG FIXES] A soundness bug was fixed in some raw-Lisp code implementing the function, ~ilc[take]. Thanks to Sol Swords for pointing out this bug with (essentially) the following proof of ~c[nil]. ~bv[] (defthmd take-1-nil-logic (equal (take 1 nil) '(nil)) :hints((\"Goal\" :in-theory (disable (take))))) (thm nil :hints ((\"Goal\" :use take-1-nil-logic))) ~ev[] Calls of ~ilc[mbe] in ``safe-mode'' situations ~-[] i.e., during evaluation of ~ilc[defconst], ~ilc[value-triple], and ~ilc[defpkg] forms, and during macroexpansion ~-[] are now guard-checked. Thus, in these situations both the ~c[:logic] and ~c[:exec] forms will be evaluated, with an error if the results are not equal. Formerly, only the ~c[:logic] form was evaluated, which was a soundness bug that could be exploited to prove ~c[nil]. For a such a proof and a bit of further explanation, see the example at the top of the comments for ~c[(deflabel note-4-3 ..)] in ACL2 source file ~c[ld.lisp]. It had been possible to prove ~c[nil] by proving the following theorem using ACL2 built on CCL and then proving its negation using ACL2 built on a different host Lisp. ~bv[] (defthm host-lisp-is-ccl (equal (cdr (assoc 'host-lisp *initial-global-table*)) :ccl) :rule-classes nil) ~ev[] This hole has been plugged by moving the setting of ~c['host-lisp] out of the constant ~c[*initial-global-table*]. Fixed ~ilc[trace$] for arguments that are ~il[stobj] accessors or updaters. It also gives an informative error in this case when the accessor or updater is a macro (because the introducing ~ilc[defstobj] event specified ~c[:inline t]). Avoided a potential error that could occur when no user home directory is located. Our previous solution for Windows simply avoided looking for ACL2 customization files (~pl[acl2-customization]) and ~c[acl2-init.lsp] files in a user's home directory. With this change, we handle such files the same for Windows as for non-Windows systems: we always look for ACL2 customization files (~pl[acl2-customization]) and ~c[acl2-init.lsp] files in a user's home directory, but only if such a directory exists. Thanks to Hanbing Liu for reporting this issue. (GCL only) Fixed a bug that prevented the use of ~ilc[get-output-stream-string$] when the host Lisp is GCL. Fixed ~ilc[with-live-state] to work properly for executable counterparts (so-called ``*1*'' functions). Fixed a bug in the error message caused by violating the ~il[guard] of a macro call. Fixed a bug in an error message that one could get when calling ~ilc[defattach] with argument ~c[:skip-checks t] to attach to a ~c[:]~ilc[program] mode function symbol that was introduced with ~ilc[defun]. (This is indeed an error, but the message was confusing.) Thanks to Robert Krug for bringing this bug to our attention. Fixed a bug in the loop-checking done on behalf of ~ilc[defattach], which could miss a loop. For an example, see the comment about loop-checking in the comments for ~c[(deflabel note-4-3 ..)] in ACL2 source file ~c[ld.lisp]. Terms of the form ~c[(hide )] without free variables could be simplified, contrary to the purpose of ~ilc[hide]. This is no longer the case, Thanks to Dave Greve for reporting this issue. An infinite loop could occur when an error was encountered in a call of ~ilc[wormhole-eval], for example with the following form, and this has been fixed. ~bv[] (wormhole-eval 'demo '(lambda () (er hard 'my-top \"Got an error!\")) nil) ~ev[] Fixed a bug in detection of package redefinition. While we have no example demonstrating this as a soundness bug, we cannot rule it out. Fixed a bug in the message produced by an erroneous call of ~ilc[flet]. Thanks to Jared Davis for reporting this bug and sending a helpful example. For a failed ~ilc[defaxiom] or ~ilc[defthm] event, we now avoid printing ~il[rune]s that are used only in processing proposed rules to be stored, but not in the proof itself. Thanks to Dave Greve for sending us an example that led us to make this fix. ACL2 did not reliably enforce the restriction against non-~ilc[local] ~ilc[include-book] ~il[events] inside ~ilc[encapsulate] events, as illustrated by the following examples. ~bv[] ; not permitted (as expected) (encapsulate () (include-book \"foo\")) ; permitted (as expected) (encapsulate () (local (include-book \"foo\"))) ; formerly permitted (surprisingly); now, not permitted (local (encapsulate () (include-book \"foo\"))) ~ev[] Moreover, the corresponding error message has been fixed. Thanks to Jared Davis and Sandip Ray for relevant discussions. When ~ilc[include-book] is given a first argument that is not a string, a more graceful error now occurs, where previously an ugly raw Lisp error had occurred. Thanks to Eric Smith for bringing this bug to our attention. Fixed a bug in an error message that was printed when an unexpected expression has occurred where a ~ilc[declare] form is expected. (Since all functions are compiled when the host Lisp is CCL or SBCL, the following bug fix did not occur for those host Lisps.) After evaluation of ~c[(]~ilc[set-compile-fns]~c[ t)], all defined functions are expected to run with compiled code; but this was not the case for functions exported from an ~ilc[encapsulate] event. This has been fixed. It had been the case that the ~c[:]~ilc[puff] command was broken for ~ilc[include-book] form whose book had been certified in a world with an ~ilc[add-include-book-dir] event. This has been fixed. Evaluation of ~il[stobj] updaters (~pl[defstobj]) may no longer use attachments (~pl[defattach]). This is a subtle point that will likely not affect many users. Thanks to Jared Davis for bringing this issue to our attention; a slight variant of his example appears in a comment in ACL2 source function ~c[oneify-cltl-code]. It had been the case that even when a ~il[stobj] creator function was declared to be untouchable (~pl[push-untouchable]), a ~ilc[with-local-stobj] form based on that same stobj was permitted. Now, such forms are not admitted. Thanks to Jared Davis for a query leading to this fix. Fixed a buggy message upon ~il[guard] violations, which was suggesting the use of ~c[(set-guard-checking :none)] in some cases when guard-checking was already set to ~c[:none]. It had been possible to get a hard Lisp error when computing with ~ilc[ec-call] in ~il[books]. The following is an example of such a book, whose certification no longer causes an error. ~bv[] (in-package \"ACL2\") (defun f (x) x) (defconst *c* (ec-call (f 3))) (defun g (x) (cons x x)) ~ev[] The command ~c[:]~ilc[pl2], and also the ~il[proof-checker] commands ~c[rewrite] and ~c[show-rewrites] (and hence their respective aliases ~c[r] and ~c[sr]), now take rule-id arguments that can be ~c[:]~ilc[definition] ~il[rune]s. These commands dealt with definition rules already, e.g. ~bv[] :pl2 (append x y) binary-append ~ev[] but they did not allow explicit specification of ~c[:definition] runes, e.g.: ~bv[] :pl2 (append x y) (:definition binary-append) ~ev[] The following example illustrates a bug in the processing of (admittedly obscure) ~il[hints] of the form ~c[:do-not-induct name], where ~c[name] is not ~c[t], ~c[:otf-flg-override], ~c[:otf], or ~c[nil]. In this example, ACL2 had essentially ignored the hint and reverted to prove the original goal by induction, rather than to skip the goal temporarily as is expected for such hints. Thanks to David Rager for a helpful discussion. ~bv[] (thm (and (equal (append (append x y) z) (append x y z)) (equal (append (append x2 y2) z2) (append x2 y2 z2))) :hints ((\"Subgoal 1\" :do-not-induct some-name))) ~ev[] Fixed a slight bug in the definitions of built-in ~ilc[theories]. For example, in a fresh ACL2 session the value of the following form is ~c[nil], but formerly included several ~c[:]~ilc[definition] ~il[rune]s. ~bv[] (let ((world (w state))) (set-difference-theories (function-theory :here) (function-theory 'ground-zero))) ~ev[] ~st[CHANGES AT THE SYSTEM LEVEL AND TO DISTRIBUTED BOOKS] Many changes have been made to the distributed books, as recorded in svn logs under the `Source' and 'Updates' links at ~url[http://acl2-books.googlecode.com/]. Here we list some of the more significant changes.~bq[] o A large library has been graciously contributed by the formal verification group at Centaur Technology. See ~c[books/centaur/] and, in particular, file ~c[books/centaur/README], which explains how the library depends on the experimental HONS extension (~pl[hons-and-memoization]). o Among the new books is an illustration of ~ilc[defattach], ~c[books/misc/defattach-example.lisp], as well as a variant of defattach that avoids the need for ~il[guard] verification, ~c[books/misc/defattach-bang.lisp]. o Distributed book ~c[books/misc/trace1.lisp] has been deleted. It had provided slightly more friendly ~il[trace] output for new users, but distributed book ~c[books/misc/trace-star.lisp] may be better suited for that purpose.~eq[] ACL2 can once again be built on LispWorks (i.e., as the host Lisp), at least with LispWorks 6.0. Thanks to David Rager for useful conversations. Several changes have been made from previous LispWorks-based ACL2 executables:~nl[] o ACL2 now starts up in its read-eval-print loop.~nl[] o You can save an image with ~ilc[save-exec].~nl[] o Multiprocessing is not enabled.~nl[] o The stack size is managed using a LispWorks variable that causes the stack to grow as needed.~nl[] o When ACL2 is built a script file is written, as is done for other host Lisps. Thus, (assuming that no ~c[PREFIX] is specified), ~c[saved_acl2] is just a small text file that invokes a binary executable, which for Lispworks is ~c[saved_acl2.lw]. The HTML documentation no longer has extra newlines in
 environments.

  Statistics on ACL2 code size may be found in distributed file
  ~c[doc/acl2-code-size.txt].  This file and other information can be found in
  a new ~il[documentation] topic, ~il[about-acl2].

  Fixed the build process to pay attention to environment variable
  ~c[ACL2_SYSTEM_BOOKS] (which may be supplied as a command-line argument to
  `make').  An ACL2 executable can thus now be built even when there is no
  ~c[books/] subdirectory if a suitable replacement directory is supplied.

  Some warnings from the host Lisp are now suppressed that could formerly
  appear.  For example, the warnings shown below occurs in Version  4.2 using
  Allegro CL, but not in Version  4.3.
  ~bv[]
  ACL2 !>(progn (set-ignore-ok t)
                (set-irrelevant-formals-ok t)
                (defun bar (x y)
                  x))
  [[.. output omitted ..]]
   BAR
  ACL2 !>:comp bar
  ; While compiling BAR:
  Warning: Variable Y is never used.
  ; While compiling (LABELS ACL2_*1*_ACL2::BAR ACL2_*1*_ACL2::BAR):
  Warning: Variable Y is never used.
   BAR
  ACL2 !>
  ~ev[]

  ~st[EMACS SUPPORT]

  The distributed Emacs file ~c[emacs/emacs-acl2.el] now indents calls of
  ~c[er@par] and ~c[warning$@par] the same way that calls of ~c[defun] are
  indented.

  ~st[EXPERIMENTAL VERSIONS]

  The parallel version (~pl[parallelism]) now supports parallel evaluation of
  the ``waterfall'' part of the ACL2 prover; ~pl[set-waterfall-parallelism].
  Thanks to David Rager for doing the primary design and implementation work.

  A new macro, ~ilc[spec-mv-let], supports speculative and parallel execution
  in the parallel version, courtesy of David Rager.

  Among the enhancements for the HONS version (~pl[hons-and-memoization]) are
  the following.
  ~bq[]
  ~ilc[Memoize]d functions may now be traced (~pl[trace$]).  Thanks to Sol
  Swords for requesting this enhancement.

  ~ilc[Memoize-summary] and ~ilc[clear-memoize-statistics] are now
  ~c[:]~ilc[logic] mode functions that return ~c[nil].  Thanks to Sol Swords
  for this enhancement.

  ~ilc[Memoize] is now explicitly illegal for constrained functions.  (Already
  such memoization was ineffective.)

  A new keyword argument, ~c[:AOKP], controls whether or not to allow
  memoization to take advantage of attachments; ~pl[memoize] and for relevant
  background, ~pl[defattach].

  ~ilc[Memoize] is now illegal by default for ~c[:]~ilc[logic] mode functions
  that have not had their guards verified.  ~l[memoize] (keyword
  ~c[:ideal-okp]) and ~pl[acl2-defaults-table] (key ~c[:memoize-ideal-okp]) for
  and explanation of this restriction and how to avoid it.

  ~il[History] commands such as ~c[:]~ilc[pe] and ~c[:]~ilc[pbt] now display
  ``~c[M]'' or ``~c[m]'' to indicate memoized functions.  ~l[pc].
  ~eq[]

  ~/~/")

(deflabel |NOTE-4-3(R)|
  :doc
  ":Doc-Section release-notes

  ACL2 Version  4.3(r) (July, 2011) Notes~/

  ~/

  Please ~pl[note-4-3] for changes in Version  4.3 of ACL2.

  ~/
  ")

(deflabel note-5-0

; Total release note items: 125.

; Improved comments about step-limits.

; Here is a slightly simplified version of the example sent to us by Warren
; Hunt for the defstobj bug involving, quoting the :doc below, "excessively
; restrictive type declarations".

;   (defstobj x86-32
;     (mem :type (array (unsigned-byte 8) (4294967296)) ;; 2^32
;          :initially 0
;          :resizable nil)
;     :inline t)

; It has for some time been illegal to do guard verification inside a
; non-trivial encapsulate for an exported function whose body or guard depends
; on signature functions; see function bogus-exported-compliants.  However, we
; made exceptions both for constrained functions and for non-executable
; functions.  The latter exception has been removed; after all, if the guard
; obligations aren't theorems of the post-encapsulate theory, it's a bit odd to
; allow them.

; For more about the increase in speed for ACL2 array reads, see the Essay on
; Array Caching.

; Regarding the fix to getenv$ etc. mentioned below: we also changed
; throw-nonexec-error, get-output-stream-string$-fn, and set-debugger-enable-fn
; just a bit, just to be safe, though we're not aware of a soundness bug for
; those functions.

; We removed a #-acl2-loop-only shortcut in plist-worldp, which remains as a
; comment in that function.

; Modified the check in acl2-check.lisp related to
; *common-lisp-specials-and-constants*, so that it no longer causes an error
; when attempting to build with ECL (which bound the symbol
; COMMON-LISP:FUNCTION), in case this is something we try in the future.

; The soundness bug for with-live-state has been recorded in a comment where
; that macro is defined.

; (Lispworks mods courtesy of Martin Simmons) Changed calls of
; special-form-or-op-p to expand to calls of special-operator-p.  Changed
; 'cl::getenv to 'hcl::getenv and changed 'cl::setenv to 'hcl::setenv.
; Changed lisp::quit to lispworks:quit in acl2.lisp.

; Modified the DOC targets (and HTML, etc. targets under it) so that one can
; specify ACL2= on the command line with the make command or as an
; environment variable.  If ACL2 is not thus specified, then PREFIX and
; ACL2_SUFFIX will be used, as before.

; The time-reporting bug was in initialize-summary-accumulators, which had
; called main-timer without accumulating times.  The following simple example
; clearly illustrates the bug, as the summary time should match the runtime
; reported by time$.

; (defun fib (n) (if (or (zp n) (eql n 1)) 1 (+ (fib (- n 1)) (fib (- n 2)))))
; (time$ (progn (defun f1 (x) x)
;               (value-triple (length (make-list 10000000)))
;               (defun f2 (x) x)))

; Cleaned up special-form-or-op-p.

; Modified script saved for sbcl to double the control-stack-size, which
; allowed "make DOC" to complete.

; Fixed the function INDUCT so that the second argument of the inform-simplify
; call is less likely to have duplicates.

; Here we say a bit more about the "logical modeling of the ACL2 evaluator"
; mentioned below.  The function ev-fncall-rec is defined in the logic to be
; ev-fncall-rec-logical, but they did not actually match up.  For example, the
; result differed if ev-fncall-rec below was changed to ev-fncall-rec-logical.
;   (defstub foo () t)
;   (defttag t)
;   (remove-untouchable ev-fncall-rec t)
;   (ev-fncall-rec 'foo nil (w state) 100000 nil nil nil nil t)
; We found several such discrepancies and have fixed them.

; After remarks from Gary Byers, improved fgetprop a bit by using defconstant
; to introduce *current-acl2-world-key* and by using symbol-value for
; 'ACL2_GLOBAL_ACL2::CURRENT-ACL2-WORLD.

; Here is an example showing the "security hole" mentioned in these release
; notes, below.

;   ;;;;; File foo.lisp ;;;;;
;   (in-package "ACL2")
;
;   (defun foo (x)
;     x)
;
;   ;;;;; File foo.acl2x ;;;;;
;   ((1 . (defun foo (x) (cons x x))))
;
;   ;;;;; File top.lisp ;;;;;
;   (in-package "ACL2")
;
;   (include-book "foo")
;
;   (defthm foo-cons
;     (equal (foo x)
;            (cons x x)))
;
;   ;;;;; File top2.lisp ;;;;;
;   (in-package "ACL2")
;
;   (include-book "top")
;
;   (defthm ouch
;     nil
;     :hints (("Goal" :in-theory (disable foo-cons)
;              :use foo-cons))
;     :rule-classes nil)
;
;   ;;;;; File cert.lsp ;;;;;
;
;   (certify-book "foo" 0 t :acl2x t)
;   (u)
;   (certify-book "top")
;   (u)
;   (certify-book "foo" 0 t)
;   (u)
;   (certify-book "top2")
;
;   ;;;;; Now evaluate (ld "cert.lsp") ;;;;;

; We improved the function chk-ld-skip-proofsp to cause a soft error instead of
; a hard error.  For this purpose, we moved set-ld-skip-proofsp and replaced
; its use in axioms.lisp with an f-put-global,

; Modified observation-cw for ACL2(p).  Also, in support of ACL2(p) but also of
; general applicability, we modified io? in the commentp=t case to check that
; the form can be translated, at least by default (see new argument
; chk-translatable).

; The previous definition of cons-term1 has been eliminated, and cons-term2 has
; been renamed to cons-term1.  Thanks to Harsh Raju Chamarthi for pointing out
; the dead code that led to this change.

; We tweaked the implementation of defconst to support fast-alists, based on
; discussions with David Rager and Jared Davis.  To see relevant code, search
; for "Remark on Fast-alists" and also see the new call (remprop k
; 'redundant-raw-lisp-discriminator) in the const-restore-ht case of function
; hcomp-restore-defs.

; The new macro ill-formed-certificate-er is called to provide much more
; information than had been provided by use of the constant,
; *ill-formed-certificate-msg*.

; The change "All trust tags are now in the ~il[keyword] package" was made in
; support of provisional certification.  Sol Swords sent an example in which
; the Complete operation caused an error, the reason being that an unknown
; package was being used in the post-alist in the certificate file.

; Here is the example promised in :doc note-5-0 and in a comment about
; :SKIPPED-PROOFSP in certify-book-fn, regarding "Fixed a soundness bug based
; on the use of ~ilc[skip-proofs] ...."  First consider the following two
; books.

;   -- foo.lisp --

;   (in-package "ACL2")
;   (defun f1 (x) x)
;   ; (defthm bad nil :rule-classes nil)

;   -- bar.lisp --

;   (in-package "ACL2")
;   (local (include-book "foo"))
;   (defthm bad nil :rule-classes nil)

;   --

; Notice that foo.lisp ends in a commented-out form.  Now proceed as follows.
; If you prefer, you can eliminate the two calls of set-ld-skip-proofsp by,
; instead, wrapping a skip-proofs around the defthm.

;  (set-ld-skip-proofsp t state)
;  (defthm bad nil :rule-classes nil)
;  (set-ld-skip-proofsp nil state)
;  (certify-book "foo" 1 t :skip-proofs-okp t)
;  Uncomment out the defthm form in foo.lisp.
;  (ubt! 1)
;  (certify-book "foo" t)
;  (ubt! 1)
;  (certify-book "bar")

; You will see no warnings when certifying bar, and its certificate will show
; no trace of skip-proofs.

; Among the changes made to support congruent stobjs are latching of stobjs in
; raw-ev-fncall; the use of FLET in :print-gv output; and a change to
; processing of rewrite rules such that interpret-term-as-rewrite-rule1 now has
; the previous functionality of interpret-term-as-rewrite-rule, except for
; removing lambdas.

; Improved the :use hint warning by adding the goal name and pointing to a new
; :doc topic, using-enabled-rules.  Thanks to David Rager for pointing out how
; the existing warning could be improved.

; To support guard verification (in new books distributed in books/system/):
; Updated several guards and termination conditions (to endp).  Functions
; affected include sublis-var and sublis-var-lst and some ancestors; subst-var
; and subst-var-lst; and subst-expr1 and subst-expr1-lst and subst-expr.
; Thanks to David Rager for his part in this effort, including his addition of
; books that verify guards for these functions.

; Fixed ill-guarded calls of eq and union-eq in non-recursive-fnnames and
; non-recursive-fnnames-lst, respectively.

; Here is an example of the proof-checker "bug that could result in duplicate
; goal names in the case of forced hypotheses".  The log shown was created by
; using Version_4.3 to execute the six events just below.

;   (defstub f (x) t)
;   (defstub g (x) t)
;   (defstub h (x) t)
;   (defstub k (x) t)
;
;   (defaxiom prop
;     (implies (and (f x) (g x))
;              (h x)))
;
;   (defaxiom prop-ts
;     (implies (force (k x)) (f x))
;     :rule-classes :type-prescription)
;
; The log promised above is then as follows.
;
;   ACL2 !>(verify (h x))
;   ->: (r 1)
;   Rewriting with PROP.
;   --NOTE-- Using the following runes in addition to the indicated rule:
;     ((:TYPE-PRESCRIPTION PROP-TS)).
;   NOTE (forcing):  Creating one new goal due to forcing assumptions.
;
;   Creating two new goals:  (MAIN . 1) and (MAIN . 1).
;
;   The proof of the current goal, MAIN, has been completed.  However,
;   the following subgoals remain to be proved:
;     (MAIN . 1) and (MAIN . 1).
;   Now proving (MAIN . 1).
;   ->: goals
;
;   (MAIN . 1)
;   (MAIN . 1)
;   ->:

; Made a small change to newline printing in ubt-ubu-query and ubt-ubu-fn1 in
; support of the change to top-level (to avoid a bogus newline).

; While fixing the cons-term bug, we improved the efficiency of kwote a bit,
; avoiding some unnecessary consing.

; Improved several :doc topics by clarifying the role of type declarations in
; specifying the guard of a function.

; We eliminated some needless property names from renew-name/overwrite, and
; added a comment clarifying why it is only called on function symbols.

; Added new severity option HARD?! for er, so that the guard of theory-fn can
; be t (avoiding the expense of doing the theory-namep check twice).

; We now print notes to use :a! and see :DOC set-evisc-tuple when evaluating
; :brr t.  Thanks to Robert Krug for suggesting this improvement.

; Eliminated warnings at build time for CLISP due to two definitions of the
; same function from both defproxy and encapsulate, by marking those
; encapsulate forms (in boot-strap-pass-2.lisp) as #+acl2-loop-only.

  :doc
  ":Doc-Section release-notes

  ACL2 Version  5.0 (August, 2012) Notes~/

  NOTE!  New users can ignore these release notes, because the
  ~il[documentation] has been updated to reflect all changes that are recorded
  here.

  Below we roughly organize the changes since Version  4.3 into the following
  categories of changes: existing features, new features, heuristic
  improvements, bug fixes, changes at the system level and to distributed
  books, Emacs support, and experimental versions.  Each change is described in
  just one category, though of course many changes could be placed in more than
  one category.

  NOTE: ACL2 is now distributed under Version 2 of the GNU General Public
  License.  [Added later: The license has changed since Version_5.0.  See
  LICENSE.]  Formerly, any later version had been acceptable.  Moreover, books
  are no longer distributed from a University of Texas website, but rather,
  from Google Code at ~url[http://code.google.com/p/acl2-books/downloads/].

  ~st[CHANGES TO EXISTING FEATURES]

  A fatal error now occurs if environment variable ~c[ACL2_CUSTOMIZATION] has a
  value other than ~c[NONE] or the empty string, but is not the name of an
  existing file.  Thanks to Harsh Raju Chamarthi for requesting such a change.

  Functions ~c[read-acl2-oracle] (and ~c[read-acl2-oracle@par]),
  ~c[read-run-time], and ~c[main-timer] are no longer untouchable
  (~pl[remove-untouchable]).

  We now avoid certain duplicate conjuncts in the ~il[constraint] stored for
  ~ilc[encapsulate] ~il[events].  For example, the constraint stored for the
  following event formerly included ~c[(EQUAL (FOOP (CONS X Y)) (FOOP Y))] and
  ~c[(BOOLEANP (FOOP X))] twice each, but no more.
  ~bv[]
  (encapsulate
   ((foop (x) t))
   (local (defun foop (x) (declare (ignore x)) t))
   (defthm foop-constraints
     (and (booleanp (foop x))
          (equal (foop (cons x y)) (foop y)))
     :rule-classes
     ((:type-prescription :corollary (booleanp (foop x)))
      (:rewrite :corollary (equal (foop (cons x y)) (foop y))))))
  ~ev[]

  The ~c[:]~ilc[guard] for a constrained function (~pl[signature]) may now
  mention function symbols introduced in the same ~ilc[encapsulate] event that
  introduces that function.  Thanks to Nathan Wetzler for a helpful discussion
  leading to this improvement.

  The test for redundancy (~pl[redundant-events]) of ~ilc[encapsulate]
  ~il[events] has been improved in cases involving redefinition
  (~pl[ld-redefinition-action]).  Thanks to Jared Davis for providing the
  following example, which illustrates the problem.
  ~bv[]
    (redef!)

    (encapsulate ()
      (defun g (x)
        (+ 3 x)))

    (g 0) ; 3, as expected

    (encapsulate ()
      (defun g (x)
        (+ 4 x)))

    (g 0) ; 4, as expected

    ; Unfortunately, the following was flagged as redundant because it agreed
    ; with the first encapsulate above.  That has been fixed; now, it is
    ; recognized as not being redundant.
    (encapsulate ()
      (defun g (x)
        (+ 3 x)))
  ~ev[]

  The test for redundancy of ~ilc[defun] and ~ilc[defconst] events has been
  improved in the case that redefinition is active.  In that case, redundancy
  now additionally requires that the ``translated'' body is unchanged, i.e.,
  even after expanding macro calls and replacing constants (defined by
  ~ilc[defconst]) with their values.  Thanks to Sol Swords for requesting this
  enhancement, and to Jared Davis for pointing out a bug in a preliminary
  change.  ~l[redundant-events], in particular the ``Note About Unfortunate
  Redundancies''.  Note that this additional requirement was already in force
  for redundancy of ~ilc[defmacro] events.

  The macro ~ilc[defmacro-last] and the ~il[table] ~ilc[return-last-table] have
  been modified so that when they give special treatment to a macro ~c[mac] and
  its raw Lisp counterpart ~c[mac-raw], a call ~c[(return-last 'mac-raw ...)]
  can be made illegal when encountered directly in the top level loop, as
  opposed to inside a function body.  ~l[return-last].  Thanks to Harsh Raju
  Chamarthi for showing us an example that led us to make this improvement.

  We removed a barrier to admitting function definitions, as we explain using
  the following example.
  ~bv[]
  (defun foo (m state)
    (declare (xargs :stobjs state))
    (if (consp m)
        (let ((state (f-put-global 'last-m m state)))
          (foo (cdr m) state))
      state))
  ~ev[]
  Previously, ACL2 complained that it could not determine the outputs of the
  ~ilc[LET] form, as is necessary in order to ensure that ~ilc[STATE] is
  returned by it.  ACL2 now works harder to solve this problem as well as the
  analogous problem for ~ilc[MV-LET] and, more generally for
  ~ilc[mutual-recursion].  (The main idea is to reverse the order of processing
  the ~ilc[IF] branches if necessary.)  We thank Sol Swords for contributing a
  version of the above example and requesting this improvement.

  It is no longer the case that ~ilc[break-on-error] causes a Lisp break when
  encountering an error during translation of user input into internal
  (translated) form (~pl[term]).  The reason is that an improvement to the
  translation process, specifically the one described in the preceding
  paragraph, allows certain backtracking from ``errors'', which are intended to
  be silent rather than causing breaks into raw Lisp.  Thanks to Jared Davis
  for sending an example leading to this change.

  (CCL and SBCL only) When the host Lisp is CCL or SBCL, then since all
  functions are compiled, a ~ilc[certify-book] command will no longer load the
  newly-compiled file (and similarly for ~ilc[include-book] with argument
  ~c[:load-compiled-file :comp]).

  ~ilc[Set-write-acl2x] now returns an error triple and can take more values,
  some of which automatically allow including uncertified books when
  ~ilc[certify-book] is called with argument :acl2x t.

  The environment variable ~c[COMPILE_FLG] has been renamed
  ~c[ACL2_COMPILE_FLG]; ~pl[certify-book].

  The macros ~ilc[defthmd] and ~ilc[defund] no longer return an error triple
  with value ~c[:SKIPPED] when proofs are being skipped.  Rather, the value
  returned is the same as would be returned on success when proofs are not
  skipped.

  For those who use ~ilc[set-write-acl2x]: now, when ~ilc[certify-book] is
  called without a ~c[:ttagsx] argument supplied, then the value of ~c[:ttagsx]
  defaults to the (explicit or default) value of the ~c[:ttags] argument.

  The ~c[:]~ilc[pl] and ~c[:]~ilc[pl2] ~il[command]s can now accept ~il[term]s
  that had previously been rejected.  For example, the command
  ~c[:pl (member a (append x y))] had caused an error, but now it works as one
  might reasonably expect, treating ~ilc[member] as ~ilc[member-equal]
  (~pl[equality-variants] for relevant background).  Thanks to Jared Davis for
  reporting this problem by sending the above example.

  We have eliminated some hypotheses in built-in ~il[rewrite] rules
  ~c[characterp-nth] and ~c[ordered-symbol-alistp-delete-assoc-eq].

  Added the symbols ~ilc[f-get-global], ~ilc[f-put-global], and
  ~ilc[state-global-let*] to ~c[*acl2-exports*].

  Added to the ~il[guard]s of ~ilc[push-untouchable] and
  ~ilc[remove-untouchable] the requirement that the second argument must be a
  Boolean.  Thanks to Jared Davis for sending an example that led to this
  change.

  The built-in function ~c[string-for-tilde-@-clause-id-phrase] has been put
  into ~c[:]~ilc[logic] mode and had its guards verified, as have some
  subsidiary functions.  A few new rules have been added in support of this
  work; search for ~c[string-for-tilde-@-clause-id-phrase] in ACL2 source file
  ~c[boot-strap-pass-2.lisp] if interested.  Thanks to David Rager for
  contributing an initial version of this improvement.

  All trust tags are now in the ~il[keyword] package.  The ~ilc[defttag] event
  may still take a symbol in an arbitrary package, but the trust tag created
  will be in the keyword package (with the same ~ilc[symbol-name] as the symbol
  provided).  Similarly, non-~c[nil] symbols occurring in the ~c[:ttags]
  argument of an ~ilc[include-book] or ~ilc[certify-book] command will be
  converted to corresponding keywords.  ~l[defttag].

  There have been several changes to ~il[gag-mode].  It is now is initially set
  to ~c[:goals], suppressing most proof commentary other than key checkpoints;
  ~pl[set-gag-mode].  (As before, ~pl[pso] for how to recover the proof
  output.)  Also, top-level induction schemes are once again printed when
  gag-mode is on, though these as well as printing of guard conjectures can be
  abbreviated (``eviscerated'') with a new ~il[evisc-tuple];
  ~pl[set-evisc-tuple], in particular the discussion there of ~c[:GAG-MODE].
  Finally, the commentary printed within ~il[gag-mode] that is related to
  ~il[forcing-round]s is now less verbose.  Thanks to Dave Greve and David
  Rager for discussions leading to the change in the printing of induction
  schemes under gag-mode; thanks to Warren Hunt for an email that led us to
  similar handling for printing of guard conjectures; and thanks to Robert Krug
  for a suggestion that led us to restore, in abbreviated form, important
  information about the sources of forcing round goals.

  An error now occurs if ~ilc[ld] is called while loading a compiled book.
  ~l[calling-ld-in-bad-contexts].  Thanks to David Rager for reporting a
  low-level assertion failure that led us to make this change.

  The ~il[proof-checker] interactive loop is more robust: most errors will
  leave you in that loop, rather than kicking you out of the proof-checker and
  thus back to the main ACL2 read-eval-print loop.  Thanks to David Hardin for
  suggesting this improvement in the case of errors arising from extra right
  parentheses.

  The summary at the end of a proof now prints the following note when
  appropriate:
  ~bv[]
  [NOTE: A goal of NIL was generated.  See :DOC nil-goal.]
  ~ev[]
  ~l[nil-goal].

  Improved ~ilc[dmr] to show the function being called in the case of
  explicit evaluation: ``~c[(EV-FNCALL function-being-called)]''.

  It is now permitted to bind any number of ~il[stobjs] to themselves in the
  bindings of a ~ilc[LET] expression.  But if any stobj is bound to other than
  itself in ~ilc[LET] bindings, then there still must be only one binding in
  that ~c[LET] expression.  The analogous relaxation holds for ~ilc[LAMBDA]
  expressions.  Thanks to Sol Swords for requesting such a change, which was
  needed for some code generated by macro calls.

  The macro ~ilc[top-level] now returns without error; ~l[top-level].
  Formerly, this macro always returned an error triple ~c[(mv t .. state)],
  which meant that normal calls of ~ilc[ld] would stop after encountering a
  call of ~c[top-level].  Thanks to Jared Davis for bringing this issue to our
  attention.

  It is no longer the case that when you specify ~ilc[xargs] keyword
  ~c[:non-executable t] in a ~ilc[defun] form rather than using ~ilc[defun-nx],
  then the form of the body need match only the shape
  ~c[(prog2$ (throw-nonexec-error ... ...) ...)].  We now require that the body
  of the definition of a function symbol, ~c[fn], with formals ~c[(x1 ... xk)],
  be of the form ~c[(prog2$ (throw-nonexec-error 'fn (list x1 ... xk)) ...)].
  This fixes the following odd behavior, which could be considered a bug.
  Consider a book that contains the following two events.
  ~bv[]
  (defun foo (x)
    (declare (xargs :guard t :non-executable t :mode :logic))
    (prog2$ (throw-nonexec-error 'bar (list x))
            (cons 3 x)))
  (defn h (x)
    (foo x))
  ~ev[]
  After certifying this book and then including it in a new session, the
  behavior occurred that is displayed below; notice the mention of ~c[BAR].
  However, if the two forms were submitted directly in the loop, then the error
  message had mentioned ~c[FOO] instead of ~c[BAR].  This discrepancy has been
  eliminated, by rejecting the proposed definition of ~c[foo] because the name
  in the first argument of ~c[throw-nonexec-error] was ~c['bar] where now it
  must be ~c['foo].
  ~bv[]
  ACL2 !>(h 3)


  ACL2 Error in TOP-LEVEL:  ACL2 cannot ev the call of undefined function
  BAR on argument list:

  (3)

  To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.

  ACL2 !>
  ~ev[]

  A tautology checker used in the ACL2 sources (function ~c[if-tautologyp]) has
  been limited somewhat in the effort it makes to recognize a tautology.  While
  we expect it to be rare for the effect of this change to be noticeable, we
  thank Sol Swords for sending us an example that motivated this change: a
  ~il[guard] verification that took about 5 seconds in Version_4.3 now takes,
  on the same machine, about 0.07 seconds.

  The behavior of backquote (~c[`]) has been changed slightly to be compatible
  with its behavior in raw Lisp.  The change is to allow the use of
  comma-atsign (~c[,@]) at the end of a list, as in the following example.
  ~bv[]
  (let ((x 3) (y 2) (z 7)) `(,x ,y ,@z))
  ~ev[]
  Formerly, evaluation of this form had caused a guard violation in the ACL2
  loop unless guard-checking was off (i.e., ~ilc[set-guard-checking] was
  invoked with ~c[nil] or ~c[:none]), in which case it returned ~c[(3 2)].
  But we observed evaluation of this form to return ~c[(3 2 . 7)] in every host
  Lisp on which ACL2 runs (Allegro CL, CCL, CLISP, CMUCL, GCL, LispWorks, and
  SBCL).  Now, ACL2 behaves like these Lisps.

  A call of the ~ilc[theory] macro had previously returned ~c[nil] when applied
  to other than the name of name of a previously executed ~ilc[deftheory]
  event.  Now, a hard error occurs.

  The ~il[table] ~c[binop-table] has been replaced by the table
  ~ilc[untrans-table].  However, ~ilc[add-binop] and ~ilc[remove-binop]
  continue to have the same effect as before.  ~l[add-macro-fn], which is a new
  feature discussed below.

  The function ~ilc[booleanp] is now defined using ~ilc[eq] instead of
  ~ilc[equal], which may increase its efficiency.  Thanks to Jared Davis for
  this change.

  For pairs ~c[(key . val)] in the ~ilc[macro-aliases-table], there had been a
  requirement that ~c[val] is a known function symbol.  Now, it only needs to
  be a symbol.  (This change was made to support the new feature,
  ~ilc[defun-inline], described elsewhere in these release notes.)

  ~st[NEW FEATURES]

  A new ``tau system'' provides a kind of ``type checker.''  ~l[tau-system].
  Thanks to Dave Greve for supplying a motivating example (on which this system
  can provide significant speedup), and to Sol Swords for sending a very
  helpful bug report on a preliminary implementation.

  Users may now arrange for additional summary information to be printed at the
  end of ~il[events].  [Note added at Version_6.1: Formerly we pointed here to
  ~c[print-summary-user], but now, ~pl[finalize-event-user]; also
  ~pl[note-6-1]].  Thanks to Harsh Raju Chamarthi for requesting this feature
  and participating in a design discussion.

  A new, advanced ~il[proof-checker] command, ~c[geneqv], shows the generated
  equivalence relation at the current subterm.  Thanks to Dave Greve for an
  inquiry leading to this enhancement.

  A new reader macro, ~c[#u], permits the use of underscore characters in a
  number.  ~l[sharp-u-reader].  Thanks to Jared Davis for requesting this
  capability.

  New ~il[proof-checker] commands ~c[pl] and ~c[pr] provide interfaces to the
  ACL2 commands ~c[:]~ilc[pl] and ~c[:]~ilc[pr], respectively.  These can be
  useful if you want to see trivially-proved hypotheses, as now clarified in
  the ~il[proof-checker] documentation for its ~c[show-rewrites] command.
  ~l[proof-checker-commands].  Thanks to Pete Manolios for suggesting such
  clarification and capability.

  It is now legal to call ~il[non-executable] functions without the usual
  ~il[signature] restrictions imposed on executable code.  For example,
  the third event below was not admissible, but now it is.
  ~bv[]
  (defstobj foo fld)
  (defun-nx id (x)
    x)
  (defun f (foo)
    (declare (xargs :stobjs foo :verify-guards nil))
    (cons 3 (id foo)))
  ~ev[]
  Thanks to Jared Davis for requesting this enhancement, in particular for
  calling non-executable functions in the ~c[:logic] part of an ~ilc[mbe]
  call.  Here is Jared's example, which is admissible now but formerly was
  not.
  ~bv[]
  (defstobj foo (fld))
  (defun-nx my-identity (x) x)
  (defun my-fld (foo)
    (declare (xargs :stobjs foo))
    (mbe :logic (my-identity foo)
         :exec (let ((val (fld foo)))
                 (update-fld val foo))))
  ~ev[]

  A new macro, ~ilc[non-exec], allows the use of non-executable code, for
  example inside ordinary function definitions.  Thanks to Sol Swords for
  requesting this enhancement.

  A new ``provisional certification'' process is supported that can allow
  ~il[books] to be certified before their included sub-books have been
  certified, thus allowing for potentially much greater `make'-level
  parallelism.  ~l[provisional-certification].  Thanks to Jared Davis for
  requesting this feature and for helpful discussions, based in part on
  rudimentary provisional certification schemes that he developed first at
  Rockwell Collins and later for his `Milawa' project.  Also, thanks to Jared
  and to Sol Swords for testing this feature and for providing a fix for a bug
  in a preliminary implementation, and thanks to Sol for providing performance
  feedback and a crucial suggestion that led to an improved implementation.

  Event summaries now show the names of events that were mentioned in
  ~il[hints] of type ~c[:use], ~c[:by], or ~c[:clause-processor].
  ~l[set-inhibited-summary-types].  Thanks to Francisco J. Martin Mateos for
  requesting such an enhancement (actually thanks to the community, as his
  request is the most recent but this has come up from time to time before).

  ACL2 now stores a data structure representing the relation ``Event A is used
  in the proof of Event B.''  ~l[dead-events], which explains this data
  structure and mentions one application: to identify dead code and unused
  theorems.  Thanks to Shilpi Goel for requesting such a feature and for
  helpful feedback.

  A new ~il[documentation] topic provides a guide to programming with state;
  ~pl[programming-with-state].  Thanks to Sarah Weissman for suggesting that
  such a guide might be useful, and to David Rager for helpful feedback on a
  preliminary version.  There also has been some corresponding reorganization
  of the documentation as well as creation of additional documentation (e.g.,
  ~pl[state-global-let*]).  Now, most built-in functions and macros commonly
  used in programs (as opposed to ~il[events] like ~ilc[defun], for example)
  are subtopics of a new topic ~-[] ~pl[acl2-built-ins] ~-[] which is a
  subtopic of ~il[programming], a topic that in turn has considerably fewer
  direct subtopics than before.

  It is now possible to bind extra variables in a ~c[:USE] hint, thus avoiding
  the error message: ``The formula you wish to instantiate, ..., mentions only
  the variable(s) ...''.  ~l[lemma-instance], in particular the discussion of
  keyword ~c[:extra-bindings-ok].  Thanks to Sol Swords for requesting such an
  enhancement.

  The function ~c[read-object-suppress] is like ~c[read-object] except that it
  avoids errors and discards the value read.  ~l[io].

  A ~il[stobj] may now be passed as an argument where another stobj is expected
  if the two are ``congruent''.  ~l[defstobj], in particular, its discussion of
  the new ~c[:congruent-to] keyword of ~c[defstobj].  Thanks to Sol Swords for
  requesting this enhancement and for useful discussions contributing to its
  design.

  A new top-level utility has been provided that shows the assembly language
  for a defined function symbol; ~pl[disassemble$].  Thanks to Jared Davis for
  requesting such a utility and to Shilpi Goel for pointing out an
  inconvenience with the initial implementation.  Note that it uses the
  distributed book ~c[books/misc/disassemble.lisp], which users are welcome to
  modify (see ~url[http://www.cs.utexas.edu/users/moore/acl2/]).

  The macro ~c[set-accumulated-persistence] is an alias for
  ~ilc[accumulated-persistence].  Thanks to Robert Krug for suggesting this
  addition.

  A new ~il[documentation] topic lists lesser-known and advanced ACL2 features,
  intended for those with prior ACL2 experience who wish to extend their
  knowledge of ACL2 capabilities.  ~l[advanced-features].  Thanks to Warren
  Hunt and Anna Slobodova for requesting such information.

  A new macro, ~ilc[deftheory-static], provides a variant of ~ilc[deftheory]
  such that the resulting theory is the same at ~ilc[include-book] time as it
  was at ~ilc[certify-book] time.  Thanks to Robert Krug for helpful
  discussions on this new feature and for updating his ~c[books/arithmetic-5/]
  distributed books to use this feature.

  A new event, ~ilc[defabsstobj], provides a new way to introduce
  single-threaded objects (~pl[stobj] and ~pl[defstobj]).  These so-called
  ``abstract ~il[stobj]s'' permit user-provided logical definitions for
  primitive operations on stobjs, for example using an alist-based
  representation instead of a list-based representation for array fields.
  Moreover, the proof obligations guarantee that the recognizer is preserved;
  hence the implementation avoids executing the recognizer, which may be an
  arbitrarily complex invariant that otherwise would be an expensive part of
  ~il[guard] checks.  Thanks to Warren Hunt for a request leading us to design
  and implement this new feature, and thanks to Rob Sumners for a request
  leading us to implement a related utility, ~ilc[defabsstobj-missing-events].
  ~l[defabsstobj].  Also thanks to Sol Swords for sending an example exhibiting
  a bug in the initial implementation, which has been fixed.

  A new command, ~c[:psof ], is like ~c[:pso] but directs proof
  replay output to the specified file.  For large proofs, ~c[:]~ilc[psof] may
  complete much more quickly than ~c[:]~ilc[pso].  ~pl[psof].  More generally,
  a new utility, ~ilc[wof] (an acronym for ``With Output File''), directs
  standard output and proofs output to a file; ~pl[wof].

  The new macro ~ilc[defnd] defines a function with ~c[:]~ilc[guard] ~c[t] and
  ~il[disable]s that function, in analogy to how ~ilc[defund] defines with
  ~ilc[defun] and then ~il[disable]s.  Thanks to Shilpi Goel for requesting
  this feature.

  The ~c[:]~ilc[pl2] command now shows ~c[:]~ilc[linear] rules; and a new
  ~il[proof-checker] command, ~c[show-linears] (equivalently, ~c[sls]), is an
  analogue of the ~il[proof-checker] ~c[show-rewrites] (~c[sr]) command, but
  for ~il[linear] rules.  Thanks to Shilpi Goel for requesting this new
  proof-checker command.  Finally, a corresponding new proof-checker command,
  ~c[apply-linear] (~c[al]), is an analogue of the ~il[proof-checker]
  ~c[rewrite] (~c[r]) command, but for ~il[linear] rules.

  The macros ~ilc[add-macro-fn] and ~ilc[remove-macro-fn] replace macros
  ~ilc[add-binop] and ~ilc[remove-binop], respectively, though the latter
  continue to work.  The new macros allow you to decide whether or not to
  display calls of binary macros as flat calls for right-associated arguments,
  e.g., ~c[(append x y z)] rather than ~c[(append x (append y z))].
  ~l[add-macro-fn].

  It is now possible to request that the host Lisp compiler inline calls of
  specified functions, or to direct that the host Lisp compiler not inline such
  calls.  ~l[defun-inline] and ~pl[defun-notinline].  We thank Jared Davis for
  several extensive, relevant conversations, and for finding a bug in a
  preliminary implementation.  We also thank others who have engaged in
  discussions with us about inlining for ACL2; besides Jared Davis, we recall
  such conversations with Rob Sumners, Dave Greve, and Shilpi Goel.

  ~st[HEURISTIC IMPROVEMENTS]

  Reading of ACL2 ~ilc[arrays] (~pl[aref1], ~pl[aref2]) has been made more
  efficient (as tested with CCL as the host Lisp) in the case of consecutive
  repeated reads of the same named array.  Thanks to Jared Davis and Sol Swords
  for contributing this improvement.

  Slightly modified the induction schemes stored, so that calls of so-called
  ``guard-holders'' (such as ~ilc[mbe] and ~ilc[prog2$] ~-[] indeed, any call
  of ~ilc[return-last] ~-[] and ~ilc[the]) are expanded away.  In particular,
  calls of equality variants such as ~ilc[member] are treated as their
  corresponding function calls, e.g., ~ilc[member-equal];
  ~pl[equality-variants].  Guard-holders are also now expanded away before
  storing ~il[constraint]s for ~ilc[encapsulate] ~il[events], which can
  sometimes result in simpler constraints.

  Improved the performance of ~ilc[dmr] (technical note: by modifying raw Lisp
  code for function ~c[dmr-flush], replacing ~c[finish-output] by
  ~c[force-output]).

  We now avoid certain rewriting loops.  A long comment about this change,
  including an example of a loop that no longer occurs, may be found in source
  function ~c[expand-permission-result].

  Slightly strengthened ~il[type-set] reasoning at the level of literals (i.e.,
  top-level hypotheses and conclusions).  See the comment in ACL2 source
  function ~c[rewrite-atm] about the ``use of dwp = t'' for an example of a
  theorem provable only after this change.

  Strengthened the ability of ~il[type-set] reasoning to make deductions about
  terms being integers or non-integer rationals.  The following example
  illustrates the enhancement: before the change, no simplification was
  performed, but after the change, the conclusion simplifies to ~c[(foo t)].
  Thanks to Robert Krug for conveying the problem to us and outlining a
  solution.
  ~bv[]
  (defstub foo (x) t)
  (thm ; should reduce conclusion to (foo t)
   (implies (and (rationalp x)
                 (rationalp y)
                 (integerp (+ x (* 1/3 y))))
            (foo (integerp (+ y (* 3 x))))))
  ~ev[]

  ~st[BUG FIXES]

  Fixed a class of soundness bugs involving each of the following functions:
  ~ilc[getenv$], ~ilc[get-wormhole-status], ~ilc[cpu-core-count],
  ~ilc[wormhole-p], ~ilc[random$], ~c[file-write-date$], and
  ~c[serialize-read-fn], and (for the HONS version of ACL2)
  ~ilc[clear-memoize-table] and ~ilc[clear-memoize-tables] as well as (possible
  soundness bug) ~c[serialize-write-fn].  For example, we were able to admit
  the following events, but that is no longer the case (neither for ~c[getenv$]
  as shown, nor analogously for other functions listed above).
  ~bv[]
  (defthm not-true
    (stringp (cadr (getenv$ \"PWD\" (build-state))))
    :rule-classes nil)

  (defthm contradiction
    nil
    :hints ((\"Goal\"
             :in-theory (disable (getenv$))
             :use not-true))
    :rule-classes nil)
  ~ev[]

  Fixed a soundness bug involving ~c[with-live-state], which could cause an
  error in the use of ~ilc[add-include-book-dir] or
  ~ilc[delete-include-book-dir] in a book or its ~il[portcullis] commands.
  ~l[with-live-state], as the documentation for this macro has been updated; in
  particular it is now untouchable (~pl[remove-untouchable]) and is intended
  only for system hackers.  Thanks to Jared Davis for reporting a bug in the
  use of ~ilc[add-include-book-dir] after our first attempt at a fix.

  Fixed a soundness bug based on the use of ~ilc[skip-proofs] together with the
  little-used argument ~c[k=t] for ~ilc[certify-book].  An example proof of
  ~c[nil] appears in a comment in the ACL2 sources, in
  ~c[(deflabel note-5-0 ...)].

  Fixed a soundness bug that allowed users to define new ~il[proof-checker]
  primitive commands.  Before this fix, a book proving ~c[nil] could be
  certified, as shown in a comment now in the introduction of the ~il[table]
  ~c[pc-command-table] in source file ~c[proof-checker-a.lisp].

  (Technical change, primarily related to ~ilc[make-event]:) Plugged a security
  hole that allowed ~il[books]' ~il[certificate]s to be out-of-date with
  respect to ~ilc[make-event] expansions, but not recognized as such.  The
  change is to include the so-called expansion-alist in the certificate's
  checksum.  An example appears in a comment in the ACL2 sources, in
  ~c[(deflabel note-5-0 ...)].

  Fixed a bug in ~il[guard] verification due to expanding calls of primitives
  when translating user-level terms to internal form, so called ``translated
  terms'' (~pl[term]).  While we have not observed a soundness hole due to this
  bug, we have not ruled it out.  Before the bug fix, the following event was
  admissible, as guard verification succeeded (but clearly should not have).
  ~bv[]
  (defun f ()
    (declare (xargs :guard t))
    (car (identity 3)))
  ~ev[]
  For those who want details about this bug, we analyze how ACL2 generates
  ~il[guard] proof obligations for this example.  During that process, it
  evaluates ground subexpressions.  Thus, ~c[(identity '3)] is first simplified
  to ~c['3]; so a term must be built from the application of ~c[car] to ~c['3].
  Guard-checking is always turned on when generating guard proof obligations,
  so now, ACL2 refuses to simplify ~c[(car '3)] to ~c['nil].  However, before
  this bug fix, when ACL2 was building a term by applying ~c[car] to argument
  ~c['3], it did so directly without checking guards; source code function
  ~c[cons-term] is `smart' that way.  After the fix, such term-building
  reduction is only performed when the primitive's guard is met.

  While calls of many event macros had been prohibited inside executable code,
  others should have been but were not.  For example, the following was
  formerly allowed.
  ~bv[]
  (defun foo (state)
    (declare (xargs :mode :program :stobjs state))
    (add-custom-keyword-hint :my-hint (identity nil)))
  (foo state) ; Caused hard raw Lisp error!
  ~ev[]
  Thus, several event macros (including for example
  ~ilc[add-custom-keyword-hint]) may no longer be called inside executable
  code.

  Fixed an assertion that could occur, for example, after reverting to prove
  the original goal by induction and generating a goal of ~c[NIL].  Thanks to
  Jared Davis for sending us a helpful example to bring this bug to our
  attention.

  It was possible for ~ilc[defstobj] to generate raw Lisp code with
  excessively restrictive type declarations.  This has been fixed.  Thanks to
  Warren Hunt for reporting this bug and sending an example that illustrates
  it.  ~l[stobj-example-2] for examples of such raw Lisp code; now, one finds
  ~c[(and fixnum (integer 0 *))] where formerly the type was restricted to
  ~c[(integer 0 268435455)].

  Fixed a bug in that was ignoring the use of ~c[:computed-hint-replacement] in
  certain cases involving a combination of computed hints and custom keyword
  hints.  Thanks to Robert Krug for reporting this bug and sending a very
  helpful example.

  Fixed a bug in the output from ~ilc[defattach], which was failing to list
  previous ~il[events] in the message about ``bypassing constraints that have
  been proved when processing the event(s)''.

  (GCL only) Fixed a bug in ~ilc[set-debugger-enable] (which was only a bug in
  GCL, not an issue for other host Lisps).

  Fixed ACL2 trace output to indent properly for levels above 99 (up to 9999).
  Thanks to Warren Hunt for bringing this bug to our attention.

  Fixed a bug in the reporting of times in event summaries ~-[] probably one
  that has been very long-standing!  The times reported had often been too
  small in the case of compound ~il[events], notably ~ilc[include-book].
  Thanks to everyone who reported this problem (we have a record of emails from
  Eric Smith and Jared Davis on this issue).

  Fixed a bug in ~c[:expand] ~il[hints], where the use of ~c[:lambdas] could
  prevent other parts of such a hint.  For example, the following invocation of
  ~ilc[thm] failed before this fix was made.
  ~bv[]
  (defund foo (x) (cons x x))
  (thm (equal (car (foo x)) x)
  :hints ((\"Goal\" :expand (:lambdas (foo x)))))
  ~ev[]

  Certain ``program-only'' function calls will now cause hard Lisp errors.
  (The rather obscure reason for this fix is to support logical modeling of the
  ACL2 evaluator.  A relevant technical discussion may be found in source
  function ~c[oneify-cltl-code], at the binding of variable
  ~c[fail_program-only-safe].)

  There was an unnecessary restriction that ~ilc[FLET]-bound functions must
  return all ~il[stobj]s among their inputs.  For example, the following
  definition was rejected because ~c[state] was not among the outputs of ~c[h].
  This restriction has been removed.
  ~bv[]
  (defun foo (state)
    (declare (xargs :stobjs state))
    (flet ((h (state) (f-boundp-global 'x state)))
      (h state)))
  ~ev[]

  We fixed a bug, introduced in the preceding release (Version  4.3), in the
  check for irrelevant formals (~pl[irrelevant-formals]).  That check had been
  too lenient in its handling of lambda (~il[LET]) expressions, for example
  allowing the following definition to be admitted in spite of its first formal
  parameter obviously being irrelevant.
  ~bv[]
  (defun foo (x clk)
    (if (zp clk)
        :diverge
      (let ((clk (1- clk)))
        (foo x clk))))
  ~ev[]

  Fixed a bug in the ~c[mini-proveall] target in ~c[GNUmakefile].  The fix
  includes a slight change to the ~c[:mini-proveall] ~il[command] (an extra
  event at the end).  Thanks to Camm Maguire for reporting this bug.

  Fixed a bug that occurred when ~ilc[certify-book] was called after using
  ~ilc[set-fmt-soft-right-margin] or ~ilc[set-fmt-hard-right-margin] to set a
  small right margin.

  Fixed ~ilc[set-inhibit-warnings] so that it takes effect for a subsequent
  ~ilc[include-book] event.  Thanks to Jared Davis and David Rager for queries
  that led to this fix.

  Hard Lisp errors are now avoided for certain ~c[:]~ilc[rewrite] rules: those
  whose ~il[equivalence] relation is other than ~c[equal] when the rule is
  originally processed, but is no longer a known equivalence relation when the
  rule is to be stored.  Thanks to Jared Davis for sending a useful example, a
  minor variant of which is included in a comment in source function
  ~c[interpret-term-as-rewrite-rule] (file ~c[defthm.lisp]).

  Fixed a bug in the ACL2 evaluator (source function ~c[raw-ev-fncall]), which
  was unlikely to be exhibited in practice.

  Fixed a hard Lisp error that could occur for ill-formed ~c[:]~ilc[meta]
  ~il[rule-classes], e.g., ~c[(:meta :trigger-fns '(foo))].

  It is now an error to include a ~il[stobj] name in the ~c[:renaming] alist
  (~pl[defstobj]).

  Some bogus warnings about non-recursive function symbols have been eliminated
  for rules of class ~c[:]~ilc[type-prescription].

  (Allegro CL host Lisp only) Fixed an obsolete setting of compiler variable
  ~c[comp:declared-fixnums-remain-fixnums-switch], which may have been
  responsible for intermittent (and infrequent) checksum errors encountered
  while including books during certification of the regression suite.

  Fixed a ~il[proof-checker] bug that could result in duplicate goal names in
  the case of forced hypotheses.  An example showing this bug, before the fix,
  appears in a comment in the ACL2 sources, in ~c[(deflabel note-5-0 ...)].

  We fixed a bug in a prover routine involved in ~il[type-set] computations
  involving linear arithmetic.  This bug has been around since at least as far
  back as Version_3.3 (released November, 2007).  We are not aware of any
  resulting unsoundness, though it did have the potential to weaken the prover.
  For example, the following is proved now, but was not proved before the bug
  was fixed.
  ~bv[]
  (thm
   (implies (and (rationalp x)
                 (rationalp y)
                 (integerp (+ (* 1/3 y) x)))
            (integerp (+ y (* 3 x))))
   :hints ((\"Goal\" :in-theory (disable commutativity-of-+))))
  ~ev[]

  Although all bets are off when using redefinition
  (~pl[ld-redefinition-action]), we wish to minimize negative effects of its
  use, especially raw Lisp errors.  The examples below had caused raw Lisp
  errors, but no longer.
  ~bv[]
  (defstobj st fld :inline t)
  (redef!)
  (defstobj st new0 fld)
  (u)
  (fld st) ; previously an error, which is now fixed

  ; Fresh ACL2 session:
  (redef!)
  (defun foo (x) x)
  (defmacro foo (x) `(quote ,x))
  (u)

  ; Fresh ACL2 session:
  (redef!)
  (defmacro foo (x) (cons 'list x))
  (defun foo (x) x)
  ~ev[]

  Fixed a bug that could cause hard Lisp errors in an ~ilc[encapsulate] event.
  Thanks to Sol Swords for sending an example that exhibited this bug.  Here is
  a simpler such example; the bug was in how it was checked whether the
  ~il[guard] for a guard-verified function (here, ~c[g]) depends on some
  function introduced in the ~il[signature] of the ~ilc[encapsulate] (here, the
  function ~c[f]).
  ~bv[]
  (encapsulate
   ((f (x) t))
   (local (defun f (x) (declare (xargs :guard t)) x))
   (defun g (x)
     (declare (xargs :guard (if (integerp x) (f x) t)))
     x))
  ~ev[]

  Fixed a bug in ~c[mfc-relieve-hyp] that we believe could prohibit its use on
  the last hypothesis.  Thanks to Sol Swords for reporting this bug and
  providing a fix.

  The syntax ~c[#!] (~pl[sharp-bang-reader]) was broken after a skipped
  readtime conditional.  For example, the following input line caused an
  error.
  ~bv[]
  #+skip #!acl2(quote 3)
  ~ev[]
  This bug has been fixed.

  Fixed a bug in the ~il[break-rewrite] utility, which was evidenced by error
  messages that could occur when dealing with free variables.  An example of
  such an error message is the following; we thank Robert Krug for sending us
  an example that produced this error and enabled us to produce a fix.
  ~bv[]
  HARD ACL2 ERROR in TILDE-@-FAILURE-REASON-PHRASE1:  Unrecognized failure
  reason, ((MEM-ARRAY . X86) (ADDR QUOTE 9)).
  ~ev[]

  We fixed an obscure bug that we believe could interfere with ~ilc[defproxy]
  because of an incorrect ~c[(declaim (notinline ))] form.

  ~st[CHANGES AT THE SYSTEM LEVEL AND TO DISTRIBUTED BOOKS]

  Improvements have been made related to the reading of characters.  In
  particular, checks are now done for ASCII encoding and for the expected
  ~ilc[char-code] values for ~c[Space], ~c[Tab], ~c[Newline], ~c[Page], and
  ~c[Rubout].  Also, an error no longer occurs with certain uses of
  non-standard characters.  For example, it had caused an error to certify a
  book after a single ~il[portcullis] ~il[command] of
  ~c[(make-event `(defconst *my-null* ,(code-char 0)))]; but this is no longer
  an issue.  Thanks to Jared Davis for helpful correspondence that led us to
  make these improvements.

  The character encoding for reading from files has been fixed at iso-8859-1.
  ~l[character-encoding].  Thanks to Jared Davis for bringing this portability
  issue to our attention (as this change arose in order to deal with a change
  in the default character encoding for the host Lisp, CCL), and pointing us in
  the right direction for dealing with it.  In many cases, the character
  encoding for reading from the terminal is also iso-8859-1; but this is not
  guaranteed.  In particular, when the host Lisp is SBCL this may not be the
  case.

  Although the HTML documentation is distributed with ACL2, it had not been
  possible for users to build that documentation without omitting graphics, for
  example on the ACL2 home page.  That has been fixed, as files
  ~c[graphics/*.gif] are now distributed.

  Compiler warnings are suppressed more completely than they had been before.
  For example, the following had produced a compiler warning when the host Lisp
  is CCL, but no longer does so.
  ~bv[]
  (defun f () (car 3))
  (trace$ f)
  ~ev[]

  Removed support for ``tainted'' ~il[certificate]s.  One reason is that there
  are rarely incremental releases.  A stronger reason is that for the
  compatibility of a new release is with the previous non-incremental release,
  it's not particularly relevant whether or not the new release is incremental.

  The `make' variable ~c[BOOKS] can now be defined above the line that includes
  Makefile-generic.  (For relevant background,
  ~pl[books-certification-classic].)

  (SBCL only) ACL2 images built on SBCL now have an option,
  ~c[--dynamic-space-size 2000], that can avoid space problems that could
  previously have caused the session to die.

  The default value for variable ~c[LISP] in file ~c[GNUmakefile] is now
  ~c[ccl].  Thus, if you use `make' in the standard way to build an ACL2
  executable, the default host Lisp is ~c[ccl] rather than ~c[gcl].

  ~st[EMACS SUPPORT]

  ~st[EXPERIMENTAL VERSIONS]

  For the version supporting the reals, ACL2(r) (~pl[real]), the supporting
  function ~c[floor1] has been defined in raw Lisp.  This avoids an error
  such as in the following case.
  ~bv[]
  (defun f () (declare (xargs :guard t)) (floor1 8/3))
  (f) ; had caused raw Lisp error, before the fix
  ~ev[]

  Among the enhancements for the parallel version, ACL2(p) (~pl[parallelism]),
  are the following.  We thank David Rager for his work in developing ACL2(p)
  and these improvements in particular.~bq[]

  The macro ~c[set-parallel-evaluation] has been renamed
  ~ilc[set-parallel-execution].

  Calls of the macro ~ilc[set-waterfall-printing] are no longer ~il[events], so
  may not be placed at the top level of ~il[books].  However, it is easy to
  create events that have these effects; ~pl[set-waterfall-printing].  Note
  that now, ~c[:]~ilc[ubt] and similar commands do not change the settings for
  either waterfall-parallelism or waterfall-printing.

  The implementation of ~ilc[deflock] has been improved.  Now, the macro it
  defines can provide a lock when invoked inside a ~il[guard]-verified or
  ~c[:]~ilc[program] mode function.  Previously, this was only the case if the
  function definition was loaded from raw Lisp, typically via a compiled file.

  The underlying implementation for waterfall parallelism
  (~pl[set-waterfall-parallelism]) has been improved.  As a result, even the
  largest proofs in the regression suite can be run efficiently in
  ~c[:resource-based] waterfall parallelism mode.  Among these improvements is
  one that can prevent machines from rebooting because operating system limits
  have been exceeded; thanks to Robert Krug for bringing this issue to our
  attention.

  There is also a new flag for configuring the way waterfall parallelism
  behaves once underlying system resource limits are reached.  This flag is
  most relevant to ~c[:full] waterfall parallelism.
  ~pl[set-total-parallelism-work-limit] for more information.

  The ~ilc[dmr] utility has the same behavior in ACL2(p) as it has in ACL2
  unless waterfall-parallelism has been set to a non-~c[nil] value
  (~pl[set-waterfall-parallelism]), in which case statistics about parallel
  execution are printed instead of the usual information.

  The user can now build the regression suite using waterfall ~il[parallelism].
  See the distributed file ~c[acl2-customization-files/README] for details, and
  ~pl[unsupported-waterfall-parallelism-features] for a disclaimer related to
  building the regression suite using waterfall parallelism.

  When building ACL2 with both the hons and parallelism extensions (what is
  called ``ACL2(hp)''), the functions that are automatically memoized by the
  hons extension are now automatically unmemoized and memoized when the user
  toggles waterfall parallelism on and off, respectively.

  Calling ~ilc[set-waterfall-parallelism] with a flag of ~c[t] now results in
  the same settings as if it were called with a flag of ~c[:resource-based],
  which is now the recommended mode for waterfall parallelism.  Thanks to
  Shilpi Goel for requesting this feature.

  The prover now aborts in a timely way in response to interrupts issued during
  a proof with waterfall parallelism enabled.  (This had often not been the
  case.)  Thanks to Shilpi Goel for requesting this improvement.
  ~eq[]

  Among the enhancements for the HONS extension (~pl[hons-and-memoization])
  are the following.~bq[]

  The compact-print code has been replaced by new serialization routines
  contributed by Jared Davis.  This may improve performance when including
  books that contain ~ilc[make-event]s that expand to very large constants.
  You can also now save objects to disk without going into raw lisp;
  ~pl[serialize] for details.

  Printing of certain messages has been sped up (by using Lisp function
  ~c[force-output] in place of ~c[finish-output]).  Thanks to Jared Davis for
  contributing this improvement.

  ~il[Stobj] array writes are perhaps twice as fast.

  It is now permitted to ~il[memoize] functions that take user-defined
  ~il[stobj]s as inputs, provided that no ~il[stobj]s are returned.  Even if
  stobjs are returned, memoization is permitted provided the condition is
  ~c[nil], as when profiling (~pl[profile]).  Thanks to Sol Swords for an
  observation that led to this improvement and for useful conversations,
  including follow-up leading us to improve our initial implementation.

  Fixes have been made for memoizing with a non-~c[nil] value of
  ~c[:ideal-okp].  Errors had occurred when memoizing with a ~c[:condition]
  other than ~c[t] for a ~c[:]~ilc[logic] mode function that had not been
  ~il[guard]-verified, even with a non-~c[nil] value of ~c[:ideal-okp]; and
  after successfully memoizing such a function (without such ~c[:condition]),
  it had not been possible to ~ilc[unmemoize] it.  Thanks to Sol Swords for
  reporting issues with the ~c[:ideal-okp] argument of ~ilc[memoize].

  If a book defined a function that was subsequently ~il[memoize]d in that
  book, the function would no longer behaves as memoized upon completion of
  book certification (unless that ~ilc[certify-book] command was undone and
  replaced by evaluation of a corresponding ~ilc[include-book] command).  This
  has been fixed.  Thanks to David Rager for pointing out the problem by
  sending an example.

  We now support ACL2(h) built not only on 64-bit CCL but also on all supported
  host Ansi Common Lisps (i.e., all supported host Lisps except GCL).  Thanks
  to Jared Davis for doing much of the work to make this improvement.  Note
  that performance will likely be best for 64-bit CCL; for some Lisps,
  performance may be much worse, probably depending in part on the underlying
  implementation of hash tables.
  ~eq[]

  ~/~/")

(deflabel note-6-0

; Total number of release note items: 53.

; Added analogues simple-translate-and-eval-error-double and
; simple-translate-and-eval-cmp of simple-translate-and-eval, which instead of
; (mv erp val state) return (mv erp val) and a context-message pair,
; respectively.  Note that error output is never inhibited for these functions
; as implemented.

; Two new functions, which are rarely if ever called by users, have been made
; untouchable: stobj-evisceration-alist and trace-evisceration-alist.  Before
; this change, (trace-evisceration-alist state) and (stobj-evisceration-alist
; nil state) could return non-ACL2 objects, and the former even caused a hard
; Lisp error when a user stobj had previously been introduced.

; Fixed an error message that was complaining about redefinition of a function
; previously defined at the top level, or at the top level of a book, when the
; function was actually built into ACL2.  For example:
;
; ; old:
; ACL2 !>(defun rewrite (x) x)
; ....
; Note: REWRITE was previously defined at the top level.
;
; ; new:
; ACL2 !>(defun rewrite (x) x)
; ....
; Note: REWRITE has already been defined as a system name; that is, it
; is built into ACL2.

; Fixed bugs in case-match calls: final branch cased on t instead of &.  The
; functions were translate-rule-class-alist and bdd-clause1; the former could
; be exploited to get a misleading error message, but that's all, while we
; didn't investigate the consequences of the bdd-clause1 bug.

; The defrec utility now defines a recognizer with guard t.  An optional
; argument specifies the name of the recognizer, which by default for (defrec
; foo ...) is the symbol WEAK-FOO-P, in the same package as foo.

; Among the lower-level changes made in support of meta-extract hypotheses are
; the following:

;   If x is an atom then (sublis-var1-lst alist x) is x; formerly it was nil.
;   This change was originally made to support meta-extract hypotheses, but
;   probably is no longer necessary.  Still, it seems like a good change, since
;   it "almost" allows us to prove that (sublis-var nil x) = x -- "almost"
;   because this is only true when x is in quote-normal form.

;   Some mfc-xx functions now have implicit constraints because of
;   meta-extract-contextual-fact, so *unattachable-primitives* has been
;   extended accordingly.

;   The Essay on Correctness of Meta Reasoning has been substantially
;   extended.

; The definition of constraint-info has changed very slightly in order to make
; it easier to make :common-lisp-compliant (as required in order to make
; meta-extract-formula :common-lisp-compliant).  The main change is that some
; zero-ary function constraints might not be in quote-normal form, though that
; actually seems impossible since the changes away from cons-term would only be
; relevant for executable zero-ary primitives, of which there are none!

; We eliminated the nonlinearp condition in add-polys1.  For details, see the
; comment there about this change.

; The file doc/texinfo.tex has been removed, the result being that if one
; builds one's own Postscript version of the documentation, then the look will
; quite possibly be somewhat different than it was previously.

; Here is an example from Sol Swords, as promised in the item below about a
; soundness bug in defabsstobj based on guards.

;   (defstobj my-stobj-impl (my-fld :type (integer 0 *) :initially 0))
;
;   (trace$ len)
;
;   (defun bad-accessor-logic (my-stobj-logic)
;      (declare (xargs :guard (equal (len my-stobj-logic) 0)))
;      (mbe :logic 0
;           :exec (len my-stobj-logic)))
;
;   (defun bad-accessor-exec (my-stobj-impl)
;      (declare (xargs :stobjs my-stobj-impl)
;               (ignorable my-stobj-impl))
;      1)
;
;   (defun create-my-stobj-logic ()
;      (declare (xargs :guard t))
;      (list 0))
;
;   (defun my-stobj-logicp (x)
;      (declare (xargs :guard t))
;      (AND (TRUE-LISTP X)
;           (= (LENGTH X) 1)
;           (MY-FLDP (NTH 0 X))))
;
;   (defun-nx my-stobj-corr (my-stobj-i my-stobj-l)
;      (and (my-stobj-implp my-stobj-i)
;           (equal my-stobj-i my-stobj-l)))
;
;   (DEFTHM CREATE-MY-STOBJ-ABS{CORRESPONDENCE}
;            (MY-STOBJ-CORR (CREATE-MY-STOBJ-IMPL)
;                           (CREATE-MY-STOBJ-LOGIC))
;            ;; added by Matt K.:
;            :hints (("Goal" :in-theory (disable (my-stobj-corr)))))
;
;   (DEFTHM CREATE-MY-STOBJ-ABS{PRESERVED}
;            (MY-STOBJ-LOGICP (CREATE-MY-STOBJ-LOGIC)))
;
;   (DEFTHM BAD-ACCESSOR{CORRESPONDENCE}
;            (IMPLIES (AND (MY-STOBJ-CORR MY-STOBJ-IMPL MY-STOBJ-ABS)
;                          (EQUAL (LEN MY-STOBJ-ABS) 0))
;                     (EQUAL (BAD-ACCESSOR-EXEC MY-STOBJ-IMPL)
;                            (BAD-ACCESSOR-LOGIC MY-STOBJ-ABS))))
;
;   (defabsstobj my-stobj-abs
;      :concrete my-stobj-impl
;      :recognizer (my-stobj-absp :logic my-stobj-logicp :exec my-stobj-implp)
;      :creator (create-my-stobj-abs :logic create-my-stobj-logic :exec
;                                    create-my-stobj-impl)
;      :corr-fn my-stobj-corr
;      :exports ((bad-accessor :logic bad-accessor-logic :exec bad-accessor-exec)))
;
;   ; Test added by Matt K.:
;   (bad-accessor my-stobj-abs) ; note trace of len here: (LEN ||)
;
;   (defun length-of-my-stobj-abs ()
;      (declare (xargs :guard t))
;      (with-local-stobj my-stobj-abs
;        (mv-let (len my-stobj-abs)
;          (let ((len (ec-call (bad-accessor my-stobj-abs))))
;            (mv len my-stobj-abs))
;          len)
;        create-my-stobj-abs))
;
;   (defthm length-of-my-stobj-abs-by-def
;      (equal (length-of-my-stobj-abs) 0)
;      :hints(("Goal" :in-theory (disable (length-of-my-stobj-abs))))
;      :rule-classes nil)
;
;   (defthm length-of-my-stobj-abs-by-exec
;      (equal (length-of-my-stobj-abs) 1))

; Replaced "data base" and "data-base" by "database".

; The following change supports certification of books/centaur/ books in ACL2,
; not just ACL2(h).  In order to eliminate errors upon reference to
; *never-profile-ht* in raw Lisp code such as is found in
; books/centaur/vl/util/print-htmlencode.lisp, *never-profile-ht* is now
; defined in ACL2.

; Changed equal to eq in the body of the definition of pos-listp.

; Fixed a typo (":type-prescrption", in chk-acceptable-type-prescription-rule)
; that could have affected warnings printed when :type-prescription rules are
; submitted.

; Modified the definition of mbe to use ignorable.  Some Lisps might warn, but
; they probably warned before.  With this change, CCL svn rev 15527 doesn't
; warn.

; Modified the layout of the history-entry record so that it's not dependent on
; #+acl2-par, and hence its generated macros are the same for ACL2 and
; ACL2(p).  As a consequence, made a few related changes.

  :doc
  ":Doc-Section release-notes

  ACL2 Version  6.0 (December, 2012) Notes~/

  NOTE!  New users can ignore these release notes, because the
  ~il[documentation] has been updated to reflect all changes that are recorded
  here.

  Below we roughly organize the changes since Version  5.0 into the following
  categories of changes: existing features, new features, heuristic
  improvements, bug fixes, changes at the system level, Emacs support, and
  experimental versions.  Each change is described in just one category, though
  of course many changes could be placed in more than one category.

  NOTE.  But we start with one major change that is outside the usual
  categories:

  ~st[LICENSE change]

  The ACL2 license has been changed from GPL Version 2 to a 3-clause BSD
  license, found in the ~c[LICENSE] file distributed with ACL2.

  ~st[CHANGES TO EXISTING FEATURES]

  Function ~ilc[fmt-to-string] and similar functions (~pl[printing-to-strings])
  now use the default right margin settings; formerly the right margin had been
  set at 10,000.  If you want the former behavior, you can use the
  ~c[:fmt-control-alist], as illustrated below.
  ~bv[]
  (fmt-to-string \"~~x0\"
                 (list (cons #\\0 (make-list 30)))
                 :fmt-control-alist
                 `((fmt-soft-right-margin . 10000)
                   (fmt-hard-right-margin . 10000)))
  ~ev[]

  The use of attachments (~pl[defattach]) has been made more efficient,
  avoiding some low-level checks (Common Lisp `~c[boundp]' checks).  Thanks to
  Shilpi Goel for constructing an example that we used to help direct us to
  remove inefficiency.  The following results for that example ~-[] a Fibonacci
  program run on a machine interpreter in raw-mode (~pl[set-raw-mode]) ~-[]
  give a sense of the potential speedup, though we note that a full ACL2(h)
  regression showed no significant speedup.~bv[]

  ; Time before the change:
  ; 0.89 seconds realtime, 0.90 seconds runtime

  ; Time after the change:
  ; 0.75 seconds realtime, 0.75 seconds runtime

  ; Time when cheating to avoid the cost of attachments, by redefining a
  ; function to BE its attachment (so, this gives a lower bound on possible
  ; execution time):
  ; 0.72 seconds realtime, 0.72 seconds runtime
  ~ev[]

  Functions ~c[read-acl2-oracle] and ~c[read-acl2-oracle@par] are no longer
  untouchable (~pl[remove-untouchable]).  We reported this change for
  Version_5.0 but it was not made; thanks to Jared Davis for bringing this to
  our attention.  Function ~c[get-timer] also is no longer untouchable.

  The function ~ilc[butlast] now behaves more reasonably on arguments violating
  its ~il[guard].  For example, ~c[(butlast '(1 2 3) -1)] is now provably equal
  to ~c[(1 2 3)] instead of to ~c[(1 2 3 nil)].  Thanks to Jared Davis for
  suggesting a change to the definition of ~c[butlast].

  The utilities ~c[mfc-ts] and ~c[mfc-ap] (~pl[extended-metafunctions])
  formerly never used forcing (~pl[force]).  Now, by default, forcing is
  allowed during execution of these functions if and only if it is permitted in
  the rewriting environment where they are called.  Moreover, these and the
  ~c[mfc-xx] utilities ~-[] ~c[mfc-rw], ~c[mfc-rw+], and ~c[mfc-relieve-hyp]
  ~-[] are now macros that take (optional) keyword arguments ~c[:forcep] and
  ~c[:ttreep].  The ~c[:forcep] argument is ~c[:same] by default, providing the
  forcing behavior inherited from the environment (as described above); but it
  can be the symbol ~c[t] or ~c[nil], indicating that forcing is to be enabled
  or disabled, respectively.  The ~c[:ttree] argument is ~c[nil] by default,
  but when it is ~c[t], then a second value is returned, which is a tag-tree.
  ~l[extended-metafunctions].

  Many improvements have been made to the tau-system (~pl[tau-system]),
  including support for arithmetic intervals bounded by constants.  Thus, for
  example, ~c[(and (<= 0 x) (<= x 15))] is a tau predicate.  The
  ~il[documentation] has also been improved
  (~pl[introduction-to-the-tau-system]). Also ~pl[time-tracker-tau] for
  discussion of how the new ~ilc[time-tracker] utility can help discover ways
  to detect slowdown related to the tau-system.

  The ~ilc[defthm] ~il[events] printed by ~ilc[defabsstobj], namely those that
  remain to be proved, are now given with ~c[:rule-classes nil] since there is
  probably no intention to use them as rules.  Thanks to Robert Krug for
  suggesting that we consider this change.

  The formal parameters for a macro definition (~pl[defmacro]) may now include
  ~ilc[state] and user-defined ~ilc[stobj]s.  (However, macro formals may not
  be declared as stobjs; ~pl[xargs].)  Thanks to Jose Luis Ruiz-Reina for
  raising this issue and to Rob Sumners for helpful conversations ~-[] both of
  these nearly 10 years ago!

  The utilities ~ilc[defun-inline], ~ilc[defun-notinline], ~ilc[defund-inline],
  and ~ilc[defund-notinline] have been simplified, by taking advantage of the
  lifting of restrictions on formal parameters of macro definitions mentioned
  above (involving symbols that happen to be ~il[stobj] names).  Now, when any
  of the above four utilities is called with a given set of formal parameters,
  those formals will be used not only for the generated ~ilc[defun] event but
  also for the generated ~ilc[defmacro] event.  (Previously, they had been
  renamed for the ~ilc[defmacro] event in order to respect the stobj name
  restriction that no longer exists.)  Thanks to Jared Davis for pointing out
  the value of making this change.

  The ~il[events] ~ilc[add-invisible-fns] and ~ilc[remove-invisible-fns] now
  convert arguments as appropriate using the ~ilc[macro-aliases-table].  For
  example, the event ~c[(add-invisible-fns append car)] is now legal (though
  probably not a good idea), because ~c[add-invisible-fns] is now sensitive
  to the fact that ~ilc[append] maps to ~ilc[binary-append] in the
  ~ilc[macro-aliases-table].

  When ~c[:]~c[pe] is applied to a built-in function that does not have a
  defining event, such as ~ilc[symbolp], ~c[:pe] now gives more useful output
  that points to the documentation instead of printing a call of
  ~c[ENTER-BOOT-STRAP-MODE].  Thanks to Anthony Knape for bringing this issue
  to our attention.

  The macros ~ilc[memoize] and ~ilc[unmemoize] now cause a warning rather than
  an error in ACL2 (and work as before in ACL2(h)).

  Terms are now parsed into ~c[:]~ilc[type-prescription] rules in a manner that
  removes ~ilc[let] bindings both at the top level and in the conclusion (but
  still not in the hypotheses of the rule).  ~l[type-prescription].  Thanks to
  Jared Davis for requesting such an enhancement.

  Printing of numbers is now appropriately sensitive to the print radix;
  ~pl[set-print-radix].  Thanks to Shilpi Goel for requesting this enhancement.

  The system function ~c[explode-atom] no longer includes the radix indicator.
  The new function ~c[explode-atom+] may be used for that purpose.

  ~st[NEW FEATURES]

  Among the new features for system hackers are analogues of system function
  ~c[simple-translate-and-eval] that do not return ~ilc[state].  (Thanks to
  David Rager for requesting this feature and helpful conversations on its
  implementation.)  This and other low-level changes are typically documented
  in comments in the corresponding release note event, which in this case is
  ~c[(deflabel note-6-0 ...)].

  More built-in functions are now ~il[guard]-verified (and in ~c[:]~ilc[logic]
  mode).  Furthermore, a mechanism exists for marking yet more built-in
  functions as guard-verified based on ~il[books] contributed by users; see
  Part II of
  ~url[http://www.cs.utexas.edu/users/moore/acl2/open-architecture/].  The
  current state of that enterprise may be viewed by evaluating the constant
  ~c[*system-verify-guards-alist*], which associates a community book name with
  a list of functions.  When ACL2 is built in the normal way, each of those
  functions is marked as guard-verified when ACL2 is started up; but a special
  developer build can be used to check that the indicated book, together with
  its sub-books, proves that those functions are guard-verified.

  Metatheorems (~pl[meta]) may now have additional hypotheses, called
  ``meta-extract hypotheses'', that allow metafunctions to depend on the
  validity of certain terms extracted from the context or the logical
  ~il[world].  ~l[meta-extract].  Thanks to Sol Swords for providing an initial
  implementation, together with very helpful discussions as well as a community
  book, ~c[books/clause-processors/meta-extract-user.lisp], that extends the
  power of meta-extract hypotheses.

  New utilities ~ilc[oracle-funcall], ~ilc[oracle-apply], and
  ~ilc[oracle-apply-raw] call a function argument on specified arguments.
  Thanks to Jared Davis for requesting this utility.

  A new utility makes it convenient to track time spent inside specified
  function calls or, more generally, during specified evaluation.
  ~l[time-tracker].

  New runic designators make it easy to refer to macro names when building
  theories.  Thus, for example, the object ~c[(:i append)] may be used in
  theory expressions to designate the ~il[rune] ~c[(:induction binary-append)].
  ~l[theories].  Thanks to Jared Davis for a useful discussion leading to this
  enhancement.

  ~ilc[Defabsstobj] ~il[events] now take an optional ~c[:congruent-to] keyword
  argument, much like ~ilc[defstobj].  Thanks to Sol Swords for requesting this
  feature and for suggesting a very nice optimization that avoids the need to
  prove additional lemmas.

  ~ilc[Flet] may now include ~c[inline] and ~c[notinline] declarations.  Thanks
  to Jared Davis for requesting this feature.

  The utility ~c[gc-verbose] controls printing of messages by the garbage
  collector, for certain host Lisps.  ~l[gc-verbose].  Thanks to Shilpi Goel
  for requesting this utility.

  Added definitions of functions ~ilc[nat-listp] and ~ilc[acl2-number-listp].
  Thanks to Harsh Raju Chamarthi for requesting these additions.  Many
  community books had varying definitions of these functions; these additions
  guarantee that all books must agree on how these two functions are
  defined.  (Some community books have been changed in order that they remain
  certifiable, given these additions.)  Note that a few built-in
  ~c[:]~ilc[forward-chaining] rules were modified in order to accommodate these
  additions, and the definition of ~ilc[integer-listp] was modified to call
  ~ilc[eq] instead of ~ilc[equal], like the other such definitions.

  ~l[get-command-sequence] for a new utility that returns a list of
  ~il[command]s between two given command descriptors.

  ~st[HEURISTIC IMPROVEMENTS]

  We obtained a substantial speedup ~-[] 13% observed for the regression suite,
  and 8% observed for the ACL2(h) regression suite ~-[] by tweaking the
  ~il[break-rewrite] implementation to eliminate virtually all of its overhead
  when it is not in use (the default, which holds until ~c[:]~ilc[brr]~c[ t] is
  evaluated).  Thanks to David Rager for a conversation involving ACL2(p)
  performance statistics that suggested looking at changing ~il[break-rewrite]
  to boost performance.

  The heuristics for automatically expanding recursive function calls have been
  changed during proofs by induction.  Now, during induction, more terms that
  suggested the induction scheme are automatically expanded.  Thanks to David
  Rager for providing an example and having discussions with us that spurred us
  to develop this heuristic improvement.

  ~st[BUG FIXES]

  Fixed a soundness bug in ~ilc[defabsstobj] based on ~ilc[guard]s that
  violated single-threadedness restrictions.  Thanks to Sol Swords for bringing
  this bug to our attention and supplying a proof of ~c[nil], which we include
  as a comment in source file ~c[ld.lisp], in ~c[(deflabel note-6-0 ...)].  We
  also thank Sol for helpful discussions about ~il[guard]s of functions
  introduced by ~c[defabsstobj], which has led us to enhance the
  ~il[documentation]; ~pl[defabsstobj].

  Fixed a soundness bug in ~ilc[defabsstobj] based on interrupted updates of
  abstract stobjs.  As part of the fix a new keyword, ~c[:PROTECT], has been
  introduced for ~c[defabsstobj] exports, along with a new top-level
  ~c[defabsstobj] keyword, ~c[:PROTECT-DEFAULT]; ~pl[defabsstobj].  We do some
  analysis that we expect will avoid the use of ~c[:PROTECT] in many cases,
  which is fortunate since the use of ~c[:PROTECT t] may cause a slight
  slowdown in (abstract) stobj updates.  Thanks to Sol Swords for bringing this
  bug to our attention and supplying a proof of ~c[nil], which we include as a
  comment in source file ~c[other-events.lisp], in the definition of function
  ~c[set-absstobj-debug].

  Fixed a raw Lisp error that occurred when tracing a ~i[stobj] resize
  function, thanks to an error report from Warren Hunt, Marijn Heule, and
  Nathan Wetzler.

  Fixed a raw Lisp error that occurred for certain ill-formed signatures, as in
  the following example.
  ~bv[]
  ACL2 !>(encapsulate
             (((f (*) => * :guard t)))
             (local (defun f (x) (consp x))))

  ***********************************************
  ************ ABORTING from raw Lisp ***********
  Error:  value (F (*) => * :GUARD T) is not of the expected type SYMBOL.
  ***********************************************
  ~ev[]

  The notion of ``error triple'' (~pl[error-triples]) had been implemented
  ambiguously, with the result that for a ~il[stobj], ~c[st], the result of
  evaluating the following two forms was the same: ~c[(mv nil st state)] and
  ~c[(mv t st state)].  Of course, these are just examples; in general, a
  result of ~c[(mv erp val state)] was sometimes treated as an error triple
  even when ~c[val] is a ~il[stobj].  Now, ~c[(mv erp val state)] is an error
  triple only when ~c[erp] and ~c[val] are ordinary (non-~il[stobj]) values.
  Thanks to Warren Hunt and Marijn Heule for bringing this problem to our
  attention.

  The ``with-error-trace'' utility, ~ilc[wet], now works in the non-error case
  when given a form that returns multiple values.  (Note however that
  ~ilc[STATE] will be printed as ~c[REPLACED-STATE]; and similarly, a
  user-defined ~il[stobj], say ~c[ST], will be printed as ~c[REPLACED-ST].)

  Some possible error messages for ~ilc[defabsstobj] have been fixed that had
  been ill-formed.  Thanks to Sol Swords for bringing this bug to our
  attention.

  Fixed a bug that sometimes caused the times displayed in the summary for
  ~ilc[certify-book] to be smaller than the actual times.

  Fixed a bug in the ~il[guard]s to system functions ~c[fmt-char] and
  ~c[fmt-var], which are no longer ~c[:]~ilc[logic]-mode, guard-verified
  functions.

  (GCL only) Fixed a bug present in Gnu Common Lisp for
  ~c[#u] (~pl[sharp-u-reader]).

  ~st[CHANGES AT THE SYSTEM LEVEL]

  The ~il[state] global variable ~c['distributed-books-dir] has been renamed
  ~c['system-books-dir].  On a related note, the ~il[documentation] now refers
  to ``community books'' rather than ``distributed books'', and there is a
  corresponding new documentation topic; ~pl[community-books].

  Fixed a bug in the implementation of ~ilc[wet] (which is actually in the
  community book ~c[books/misc/wet.lisp]).

  A directory, ~c[interface/], is no longer part of the ACL2 distribution.
  Rather, it is a subdirectory of the ACL2 community books.  Thus, if you fetch
  those books in the usual way (see the installation instructions on the ACL2
  home page), you will find a directory ~c[books/interface/].  Subdirectory
  ~c[emacs/] of that ~c[interface] directory provides Emacs support for
  ~il[proof-tree]s as well an ~c[acl2-mode].  This change has been reflected in
  ACL2 file ~c[emacs/emacs-acl2.el], so users will probably not be impacted if
  they load that file into Emacs.

  The community books file ~c[books/Makefile-generic] now causes, by default, a
  backtrace to be printed when there is a raw Lisp error.

  Some changes have been made to how regressions are run, i.e., to how the
  community books are certified.  (1) The standard regression now includes
  community books directory ~c[books/centaur].  To skip these (for example, a
  Windows system has encountered difficulty with them even after installing
  Perl), include ~c[ACL2_CENTAUR=skip] with your `make' command.  (2) A new
  `make' (or environment) variable, ~c[ACL2_JOBS], specifies the number of
  parallel jobs to run, serving as a replacement for the ~c[-j] argument of
  `make' that works for all community books, including those under directory
  ~c[centaur]; ~pl[books-certification-classic].  (3) It is no longer necessary
  to do an ACL2(h) regression in order to build a copy of the documentation
  generated by Jared Davis's xdoc utility at
  ~c[books/xdoc-impl/manual/preview.html]; a vanilla ACL2 regression will build
  this manual.  (4) It is no longer necessary to set the ~c[ACL2] environment
  variable for ACL2(h) regressions if you want to use the executable
  ~c[saved_acl2h] in the ACL2 sources directory.

  The ACL2 home page now has a search utility for documentation and books.
  Thanks to Shilpi Goel and David Rager for feedback on a preliminary version
  of this utility.

  (only for SBCL with 64-bit ACL2(h)) The value of SBCL command line option
  ~c[--dynamic-space-size] for ACL2(h) on 64-bit platforms has been increased
  from 2000 to 16000 (as explained in a comment in the ACL2 source definition
  of ~c[*sbcl-dynamic-space-size*]).

  ~st[EMACS SUPPORT]

  ~st[EXPERIMENTAL/ALTERNATE VERSIONS]

  Among the enhancements for ACL2(r) (~pl[real]) are the following.~bq[]

  Thanks to Ruben Gamboa for his helpful role in making the following
  improvements made with Ruben Gamboa in support for non-standard analysis in
  ACL2(r).

  Constrained functions can now be introduce as non-classical.  ~l[signature].

  ~ilc[Defun-sk] now takes a new keyword argument, ~c[:CLASSICALP], that
  determines whether or not the named function is classical.  ~l[defun-sk].

  Incorporated a bug fix from Ruben Gamboa for ~ilc[ceiling].  The default (for
  `bad' arguments) had been 1, but now we follow normal ACL2 practice by
  returning 0 in that case.
  ~eq[]

  Among the enhancements for the HONS extension (~pl[hons-and-memoization])
  are the following.~bq[]

  Macros ~ilc[with-fast-alist], ~ilc[with-stolen-alist], and
  ~ilc[fast-alist-free-on-exit] are now defined in ACL2(h), rather than being
  defined in the community book ~c[\"books/centaur/misc/hons-extra.lisp\"].
  Thanks to Jared Davis and Sol Swords for donating this code, and thanks to
  Jared for helpful discussions leading to this change.
  ~eq[]

  Among the enhancements for ACL2(p) (~pl[parallelism]) are the following.  We
  thank David Rager for his work in developing ACL2(p) and for his helpful role
  in these improvements.~bq[]

  A bug has been fixed that could leave one in a ~il[wormhole], awaiting input,
  after an error, such as an error in an ~c[:in-theory] hint during a proof.
  Thanks to Shilpi Goel for bringing this bug to our attention.

  A key checkpoint for a given goal is now printed only once.  Previously, if a
  key checkpoint led to more than one goal pushed for proof by induction, the
  key checkpoint would be printed once for each such goal during the proof, and
  also once for each such goal in the summary at the end.
  ~eq[]

  ~/~/")

(deflabel note-6-1

; Total number of release note items: 26.

; As usual, we made a number of improvements to the documentation, based in
; part on useful feedback from users.

; "Theory" warnings about disabling primitives (such as mv-nth) have been made
; much more compact, pointing to a new :doc topic, theories-and-primitives.
; Thanks to David Rager and Jared Davis for helpful discussions leading to this
; change.

; Avoided a bogus call of all-vars in
; defstobj-component-recognizer-axiomatic-defs, and removed a false comment in
; translate-declaration-to-guard that had "justified" this.

; Removed argument value of :raw-lisp for defstobj-template.

; Regarding Expansion/Defstobj Bug (technical remarks followed by example): The
; problem was that the raw Lisp code for defstobj, which is called when loading
; an expansion file, in turn called function defstobj-raw-defs in a world that
; did not include the definition of the `satisfies' predicate, which in turn
; called defstobj-component-recognizer-axiomatic-defs on that world, which in
; turn called translate-declaration-to-guard on that world, which translated
; the `satisfies' type-spec (below) to nil.  Here is the promised example (file
; bug.lisp).

;   ; acl2
;   ; (assign save-expansion-file t)
;   ; (certify-book "bug")
;   ; (quit)
;   ; rm bug.lx64fsl
;   ; acl2
;   ; (include-book "bug")
;   ; (defthm bug
;   ;   nil
;   ;   :hints (("Goal" :use obvious))
;   ;   :rule-classes nil)
;
;   (in-package "ACL2")
;
;   (defun my-natp (x)
;     (declare (xargs :guard t))
;     (natp x))
;
;   (defstobj st1 (fld1 :type (satisfies my-natp) :initially 0))
;
;   (defthm obvious
;     (fld1p 3)
;     :rule-classes nil)
;
;   ; [End of file bug.lisp.]

; Regarding memoize/congruent stobj bug: The following example illustrates the
; bug.  Technical remark: The problem was that the true congruent stobj
; representative was not stored where expected.

;   ; acl2h
;   ; (certify-book "foo")
;   ; (quit)
;   ; acl2h
;   ; (include-book "foo")
;   ; (memoize 'fld3)
;   ; (defthm bug
;   ;   nil
;   ;   :hints (("Goal" :use foo-is-17))
;   ;   :rule-classes nil)
;
;   (in-package "ACL2")
;
;   (defstobj st1 fld1)
;   (defstobj st2 fld2 :congruent-to st1)
;   (defstobj st3 fld3 :congruent-to st2)
;
;   (defun foo ()
;     (with-local-stobj
;      st3
;      (mv-let (result st3)
;              (prog2$ (fld3 st3)
;                      (let ((st3 (update-fld3 17 st3)))
;                        (mv (fld3 st3) st3)))
;              result)))
;
;   (defthm foo-is-17
;     (equal (foo) 17)
;     :rule-classes nil)

; Here is a proof of nil for "a soundness bug involving system function
; canonical-pathname...."  This is a trivial modification (being very slightly
; simpler) of the example sent by Jared Davis and Sol Swords.

;   (in-package "ACL2")
;
;   (defchoose state-for-canonical-pathname (st) ()
;      (not (canonical-pathname nil nil st)))
;
;   (defevaluator ncp-ev ncp-ev-lst ((state-for-canonical-pathname)
;                                     (canonical-pathname a b c)
;                                     (if a b c)
;                                     (equal a b)))
;
;   (defun run-canonical-pathname-cp (clause hints state)
;      (declare (xargs :guard (pseudo-term-listp clause)
;                      :stobjs state)
;               (ignore hints))
;      (mv nil
;          (if (equal clause '((equal (canonical-pathname
;                                      'nil 'nil
;                                      (state-for-canonical-pathname))
;                                     'nil)))
;              (if (canonical-pathname nil nil state)
;                  (list clause)
;                nil)
;            (list clause))
;          state))
;
;   (defthm run-canonical-pathname-cp-correct
;      (implies (and (pseudo-term-listp clause)
;                    (alistp a)
;                    (ncp-ev (conjoin-clauses
;                             (clauses-result
;                              (run-canonical-pathname-cp clause hint state)))
;                            a))
;               (ncp-ev (disjoin clause) a))
;      :hints (("goal" :use ((:instance state-for-canonical-pathname
;                                       (st state)))))
;      :rule-classes :clause-processor)
;
;   (defthm canonical-pathname-is-nil
;      (equal (canonical-pathname
;              'nil 'nil
;              (state-for-canonical-pathname))
;             'nil)
;      :hints (("goal" :clause-processor
;               (run-canonical-pathname-cp clause nil state))))
;
;   (defun foo (x dir-p st)
;      ;; Matches the constraints of canonical-pathname but never returns NIL.
;      (declare (ignore x dir-p st))
;      "hello")
;
;   (defthm foo-never-returns-nil
;      (foo x dir-p st))
;
;   (defchoose state-for-foo (st) ()
;      (not (foo nil nil st)))
;
;   (defthm foo-sometimes-returns-nil
;      (not (foo nil nil (state-for-foo)))
;      :hints (("goal" :use ((:functional-instance
;                             canonical-pathname-is-nil
;                             (canonical-pathname foo)
;                             (state-for-canonical-pathname
;                              state-for-foo))))))
;
;   (defthm contradiction
;      nil
;      :rule-classes nil
;      :hints (("Goal" :use ((:instance foo-sometimes-returns-nil)))))

; Elaborating here on "the functions ~ilc[sys-call] and ~ilc[sys-call-status]
; are now ~il[guard]-verified ~c[:]~ilc[logic]-mode functions": Sys-call-status
; now modifies the oracle, not the file-clock.  It was misguided anyhow to
; expect that modifying the file-clock would provide a coherent story, since
; one might call sys-call to modify the file-system but never call
; sys-call-status to update the file-clock.  Since a trust-tag is necessary in
; order to call sys-call, there is no soundness problem here.  Indeed, :doc
; sys-call is already quite clear on such matters.

; As part of the process of fixing two soundness bugs -- one related to
; canonical-pathname and the other about defaxiom and defattach -- we made a
; number of changes, in particular improving the Essay on Correctness of Meta
; Reasoning and the Essay on Defattach.  In particular, the Essay on Defattach
; now comprehends the use of defaxiom.  Also note that now, as part of fixing
; that first bug, canonical-pathname and mfc-xx functions have been given
; unknown-constraints (by introducing them with dependent clause processors),
; and are no longer in *unattachable-primitives* (as their unattachability is
; enforced by their having unknown-constraints).

; We improved (and slowed down) the algorithm for computing the tau of a term.
; For example it dives into NOT now. The biggest change is that it in
; Version_6.0, preprocess-clause tried tau only before the first simplification
; (when hist=nil) and after the clause was stable under simplification.  The
; new one tries tau more aggressively: before the first three simplifications.
; We found proofs where the more aggressive use of tau -- try after a little
; rewriting got rid of functions the users means to expand -- helped.
;
; Some tests showed that the more aggressive use of tau slows down the
; regression a little compared to the less aggressive use of tau.  But because
; as more tau-based scripts are developed, we expect the more aggressive use of
; tau will pay for itself.
;
; Perhaps more important is the comparison between these two alternatives and
; Version_6.0.  They don't have identical regression suites (of course).  But
; they have 3,075 books in common as of Feb. 2013.  The new .out comparison
; utility can compare total reported book certification time for books in
; common.  Based on that, the less aggressive use of tau was measured at about
; 1% faster than Version_6.0 and the more aggressive use of tau was measured as
; about the same speed as Version_6.0.

; Source function simple-array-type formerly accepted '* as a valid array-etype
; for deducing a type of `(simple-vector *).  But since '* is not a valid
; type-spec, we should never hit this case, so we now cause a hard error in
; order to detect a mistake in that thinking.  Note that we tried evaluating
; (make-array '(5) :element-type *) in several (raw) Lisps, and often got an
; error (though it took two evaluations in CCL to get the error).

  :doc
  ":Doc-Section release-notes

  ACL2 Version  6.1 (February, 2013) Notes~/

  NOTE!  New users can ignore these release notes, because the
  ~il[documentation] has been updated to reflect all changes that are recorded
  here.

  Below we roughly organize the changes since Version  6.0 into the following
  categories of changes: existing features, new features, heuristic
  improvements, bug fixes, changes at the system level, Emacs support, and
  experimental versions.  Each change is described in just one category, though
  of course many changes could be placed in more than one category.

  ~st[CHANGES TO EXISTING FEATURES]

  More system functions are in ~c[:]~ilc[logic] mode, ~il[guard]-verified.
  Evaluate
  ~bv[]
  (strip-cars (cdr (assoc-equal \"system/top\" *system-verify-guards-alist*)))
  ~ev[]
  for the list of functions checked to be guard-verifiable in the community
  books.  Thanks to those who have contributed to this effort, as shown in file
  headers in directory ~c[system/] of the community books.

  The macro ~ilc[defund] now avoids an error when ~c[:mode :program] has been
  specified in an ~ilc[xargs] form of a ~ilc[declare] form, for example:
  ~c[(defund f (x) (declare (xargs :mode :program)) x)].  It does this by
  avoiding the generation of ~ilc[in-theory] ~il[events] in such cases.  Thanks
  to David Rager and Jared Davis for requesting such a change, and for ensuing
  helpful discussions.

  Added a field ~c[:UNIFY-SUBST] to metafunction contexts
  (~pl[EXTENDED-METAFUNCTIONS]), accessed with function ~c[mfc-unify-subst].
  Thanks to Sol Swords for requesting this enhancement.

  The functions ~ilc[sys-call] and ~ilc[sys-call-status] are now
  ~il[guard]-verified ~c[:]~ilc[logic]-mode functions.

  It had been the case that if any supporter of a dependent clause processor
  (~pl[define-trusted-clause-processor]) is among the ancestors of a given
  formula, then it was illegal to apply functional instantiation
  (~pl[lemma-instance]) to that formula.  Now, this is illegal only if some
  such supporter is in the domain of the functional substitution.

  The tau system (~pl[tau-system], or if you are unfamiliar with the tau
  system, ~pl[introduction-to-the-tau-system]) now allows the user to define
  and verify functions that compute bounds on arithmetic expressions.
  ~l[bounders].

  The utility ~c[print-summary-user] has been replaced by
  ~ilc[finalize-event-user], which is described below.  If you previously
  attached a function to ~c[print-summary-user], say ~c[my-print-summary-user],
  then you can get the effect you had previously as follows.
  ~bv[]
  (defun my-finalize-event-user (state)
    (declare (xargs :mode :logic :stobjs state))
    (prog2$ (my-print-summary-user state)
            state))
  (defattach finalize-event-user my-finalize-event-user)
  ~ev[]

  It had been the case that when you ~ilc[LD] a file, the connected book
  directory (~pl[cbd]) was set to the canonical pathname of that file's
  directory for the duration of the ~c[LD] call.  This could cause problems,
  however, if the file is actually a soft link: an ~ilc[include-book] form in
  the book with a relative pathname for the book would be resolved with respect
  to the absolute pathname for that link, which is probably not what was
  intended.  So soft links are no longer followed when computing the above
  connected book directory.  The following example, which is how we discovered
  this problem, may clarify.  We attempted to execute the form
  ~c[(ld \"top.lisp\")] using ACL2(r) (~pl[real]) in community books directory
  ~c[nonstd/arithmetic/], where all of the ~c[.lisp] files are soft links to
  files in ~c[arithmetic/].  Thus, the form ~c[(include-book \"equalities\")]
  attempted to include ~c[arithmetic/equalities] instead of
  ~c[nonstd/arithmetic/equalities], which caused an error.

  We no longer document the use of value ~c[:START] for
  ~ilc[with-prover-step-limit].  This value has always been used by the ACL2
  implementation and may have semantics that change with new ACL2 versions.  If
  you have reason to use this value, please contact the ACL2 implementors.

  ~st[NEW FEATURES]

  By default, the prover now gives information about case splits.
  ~l[splitter].  Thanks to many ACL2 users, most recently David Rager, for
  requesting such a capability.  Also thanks to David Rager and Jared Davis for
  helpful discussions, and thanks to Robert Krug for feedback on the initial
  implementation and documentation that led us to make improvements.

  New utilities ~ilc[initialize-event-user] and ~ilc[finalize-event-user] allow
  the user to run ~il[state]-modifying code at the start and end of
  ~il[events].  Thanks to Harsh Raju Chamarthi for requesting these
  capabilities.  Note that ~ilc[finalize-event-user] replaces
  ~c[print-summary-user].

  ~st[HEURISTIC IMPROVEMENTS]

  Several heuristic improvements have been made to the tau system, even if you
  do not explicitly use the new capability for computing bounds on arithmetic
  expressions, mentioned above.  ~l[tau-system], or if you are unfamiliar with
  the tau system, ~pl[introduction-to-the-tau-system].

  ~st[BUG FIXES]

  A soundness bug has been fixed that exploited the use of expansion files
  (~pl[book-compiled-file]) together with ~ilc[defstobj].  For an example
  illustrating this bug, see the comment about ``Expansion/Defstobj Bug'' in
  the form ~c[(deflabel note-6-1 ...)] in ACL2 source file ~c[ld.lisp].

  We fixed a soundness bug involving system function ~ilc[canonical-pathname]
  and (most likely) other functions in the former value of constant
  ~c[*unattachable-primitives*].  Thanks to Jared Davis and Sol Swords for
  bringing this bug to our attention by way of an example.  We include a very
  slight variant of that example in a comment within the form
  ~c[(deflabel note-6-1 ...)] in ACL2 source file ~c[ld.lisp].

  There was a soundness bug that allowed attachments to prove ~c[nil] in a
  consistent logical ~il[world] involving ~ilc[defaxiom] ~il[events].  This has
  been fixed, by requiring that no function symbol ancestral in a
  ~ilc[defaxiom] formula is allowed to get an attachment.  ~l[defattach], in
  particular discussion of ``a restriction based on a notion of a function
  symbol syntactically supporting an event'', which concludes with a proof of
  ~c[nil] that is no longer possible.

  (ACL2(h) only) We fixed a soundness bug in the interaction of memoization
  with congruent stobjs, in cases where the ~c[:congruent-to] field of
  ~ilc[defstobj] was not the canonical representative in the congruence class.
  For an example illustrating this bug, see the comment about
  ``memoize/congruent stobj bug'' in the form ~c[(deflabel note-6-1 ...)]  in
  ACL2 source file ~c[ld.lisp].

  Functions defined by ~ilc[defstobj] had failed to be compiled when certifying
  books, except in host Lisps that compile on-the-fly (CCL, SBCL).  This has
  been fixed for all host Lisps.  A related change, probably less significant,
  was made for ~ilc[defabsstobj].  Thanks to Sol Swords for reporting bugs that
  turned out to be mistakes in a preliminary implementation of this change.

  Fixed an assertion error involving linear arithmetic.  Thanks to Sol Swords
  for sending an example illustrating the bug (now appearing as a comment in
  ACL2 source function ~c[linearize1]).

  Fixed a bug that was breaking the ACL2s build mechanism (~pl[acl2-sedan]) by
  causing certain needless evaluation of ``hidden ~ilc[defpkg]'' forms in
  ~il[certificate] files when executing a call of ~ilc[include-book].  The bug
  could also affect rare error messages arising from ill-formed
  ~il[certificate] files.  Thanks to Harsh Raju Chamarthi for bringing this bug
  to our attention by sending us an example script of the sort that was
  breaking during an ACL2s build.

  Fixed handling of pathnames by some low-level code (system function
  ~c[our-truename]) that could cause errors, for example for host-Lisp GCL on
  some platforms when environment variable ~c[HOME] points to a non-existent
  directory.  Thanks to Camm Maguire for bringing this issue to our attention
  and helping with the debugging.

  Fixed a coding bug in generation of stobj resizing functions for a stobj
  named ~c[OLD].  The following example illustrates the bug.
  ~bv[]
  (defstobj old
    (fld :type (array (unsigned-byte 31) (8))
          :initially 0 :resizable t))
  (resize-fld 10 old)
  ; The following returned 8 but should have returned 10:
  (fld-length old)
  ~ev[]

  Fixed a bug in ~ilc[defabsstobj-missing-events] (which macroexpanded
  incorrectly).  Thanks to Sol Swords for bringing this bug to our attention.

  Fixed two bugs in the handling of step-limits.  Thanks to Hanbing Liu for
  bringing the main such bug to our attention, which was that ACL2 could report
  a step-limit violation during ~ilc[certify-book] (in fact, during any
  compound event such as a call of ~ilc[encapsulate] or ~ilc[progn]), even
  without direct user involvement in managing step-limits
  (~pl[set-prover-step-limit] and ~pl[with-prover-step-limit]).  The other bug
  was that a bad argument to ~ilc[set-prover-step-limit] could result in a raw
  Lisp error, for example: ~c[(progn (set-prover-step-limit '(a b)))].

  ~st[CHANGES AT THE SYSTEM LEVEL]

  The ~c[books/] directory no longer needs to exist in order to build an ACL2
  executable.  Thanks to Robert Krug for pointing out that the installation
  instructions had suggested that this was already the case.

  Many changes have been made to the community books (~pl[community-books]).
  For example, some community books now include ~c[std/lists/rev.lisp], which
  contains the rule ~c[revappend-removal], which may cause some proofs
  involving ~ilc[revappend] to fail where they formerly succeeded, or
  vice-versa.  When a proof fails that formerly succeeded, it may be useful for
  you to look over the ~il[rune]s printed in the event summary.

  ~st[EMACS SUPPORT]

  ~st[EXPERIMENTAL/ALTERNATE VERSIONS]

  For ACL2(p), ~ilc[wormhole-eval] is now locked by default; thanks to David
  Rager for suggesting this change.  But there is a way to avoid the lock;
  ~pl[wormhole-eval].  In particular, the lock is avoided in the
  implementations of ~il[accumulated-persistence] and
  ~il[forward-chaining-reports], which are not supported in ACL2(p)
  (~pl[unsupported-waterfall-parallelism-features]).

  ~/~/")

(deflabel note-6-2

; Total number of release note items: 41.

; Here is the example from Sol Swords for the item below about acl2-magic-mfc
; and acl2-magic-canonical-pathname.  The problem was that these "placeholder"
; functions are really clause processors, and hence should return a list of
; clauses, not (as erroneously done before) a single clause.
;
;  (defun foo (x)
;     (declare (ignore x))
;     nil)
;
;  (defthm foo-of-t
;     (foo t)
;     :hints (("goal" :clause-processor acl2-magic-mfc)))
;
;  (thm nil :hints (("goal" :use foo-of-t)))

; Defined function set-new-dispatch-macro-character, which we use instead of
; set-dispatch-macro-character to extend the reader with #\@, #\!, #\u, and in
; ACL2(h), also #\Y and #\Z.  This way, we check that the character hasn't
; already being appropriated for the reader by the host Lisp.  Now, for
; example, CLISP causes an error when trying to build ACL2(h), because #\Y is
; already defined as a dispatch macro character.

; Here is Robert Krug's example, trivially modified, for the heuristic
; improvement pertaining to the ancestors check.

;   (defstub rm-low-32 (addr x86) t)
;
;   (defaxiom n32p-rm-low-32-axiom
;     (implies (and (integerp addr)
;                   (<= 0 addr)
;                   (force (< (+ 3 addr) 1000)))
;              (and (integerp (rm-low-32 addr x86))
;                   (<= 0 (rm-low-32 addr x86)))))
;
;   (defstub foo-p (x) t)
;
;   (defaxiom axiom-2
;     (implies (<= 0 (rm-low-32 addr x86))
;              (foo-p (rm-low-32 addr x86))))
;
;   ; The following fails, which is to be expected.  But we expect to see a forcing
;   ; round.  This is indeed the case now, but it was not in Version_6.1 (and
;   ; probably many versions preceding that one).
;   (thm (implies (and (integerp addr)
;                      (<= 0 addr))
;                 (foo-p (rm-low-32 addr x86))))

; Improved error messages and documentation pertaining to certain ill-formed
; uses of syntaxp, in response to feedback from Robert Krug.

; Regarding the change for THE, note that the-error has been replaced by
; the-check, a 3-place function that results in a macroexpansion of THE forms
; that differs from what we got previously.

; In the process of modifying the ancestors-check heuristic to use var-counts,
; as mentioned below and explained further in the definition of macro
; var-or-fn-count-<, we changed var-fn-count to be a macro defined in terms of
; (partly) tail-recursive "flag" function var-fn-count-1, in analogy to what we
; already did for fn-count and fn-count-1.  Var-fn-count-1 is in :logic mode;
; the old var-fn-count nest was not.

; It had been possible to violate the first invariant on type-alists: no quotep
; is bound in a type-alist, but we fixed that.  See the long comment in
; subst-type-alist1.

; Added macro our-ignore-errors in raw Lisp, to ignore errors except for CLtL1
; (i.e., non-ANSI GCL).  Used it to define safe-open, which is open wrapped
; with our-ignore-errors.  Used safe-open to implement the change mentioned
; below for open-input-channel open-output-channel: "no longer cause an error
; when failing to open a channel".

; The Essay on Correctness of Meta Reasoning has been greatly improved, in
; particular with respect to its handling of meta-extract hypotheses.

; Here is an example that formerly broke into raw lisp, but no longer after the
; fix for "A hard Lisp error was possible for certain illegal functional
; substitutions", mentioned in the :doc below.
;
;   (encapsulate
;    ((f (x) t))
;    (local (defun f (x) (cons x x)))
;    (defthm f-prop
;      (consp (f x))))
;
;   (defthm main
;     (let ((y x))
;       (listp (f y))))
;
;   (defun g (x y)
;     (cons x y))
;
;   (defthm g-prop
;     (listp (g x y))
;     :hints (("Goal"
;              :use
;              (:functional-instance main
;                                    (f (lambda (v) (g v y)))))))

; In the course of modifying ACL2 to run on top of ANSI GCL, we did
; miscellaneous clean-up of various comments and documentation topics, and in a
; few cases, code.  In particular, ANSI GCL exposed a flaw in
; intern-in-package-of-symbol, which we slightly reworked as a result.

; In support of mfc-xx fixes documented below, mfc-relieve-hyp-raw now
; returns two values, as expected by call from mfc-relieve-hyp macro.

; We made the following changes during the process of adding support for
; building ACL2(h) on ANSI GCL.  While we have run a significant portion of the
; ACL2(h) regression suite on ACL2(h) built on a version of ANSI GCL, which was
; gracefully built for us by Camm Maguire, it stalled out with
; books/models/y86/y86-basic/common/x86-state.lisp.  Here is a summary of those
; changes, some of which might benefit other Lisps, although currently we only
; do automatic proclaiming for defuns for ANSI GCL.
;
; - We improved output-type-for-declare-form-rec and
;   output-type-for-declare-form for gcl, but as a result, no longer
;   attempt to do such declaims for *1* functions (see
;   install-defs-for-add-trip).
;
; - We now avoid function declaims for an abstract stobj export, which
;   is defined as a macro.
;
; - We now avoid using defun-one-output for functions like mfc-ts-raw
;   that return two values (the second of which is a ttree).
;
; - We rationalized saving the system in GCL (function
;   save-acl2-in-akcl), in particular to use the function
;   acl2-default-restart.
;
; - With-print-controls no longer messes with *print-pprint-dispatch*.
;
; - We did miscellaneous cleanup, including changing #+DRAFT-ANSI-CL-2
;   to #+cltl2.
;
; - The definition of global *float-internal-units-per-second* was
;   clearly intended to be the definition of global
;   *float-internal-time-units-per-second*.  This has been fixed.
;
; - For ANSI GCL, we added a workaround for undefinedness of
;   compiler-macro-function.
;
; - For ANSI GCL, we fixed memoize-fn to quote the symbol-function
;   passed to funcall.  Perhaps that could be done in other Lisps too,
;   but it seemed unwise to risk it.
;
; - We guarded an occurrence of (start-sol-gc) with #+Clozure, since
;   start-sol-gc is undefined otherwise.
;
; - We moved a type declaration on formals in the definition of
;   ser-decode-nat-large to be just after the formals (which avoids a
;   complaint by ANSI GCL, but is probably a good thing to do
;   regardless).

; Fixed bug in rare error message in check-certificate-file-exists, for the
; case: "argument k is t for certify-book".

; Arranged that character encoding for files in LispWorks is always as expected
; (latin-1 with linefeed for :EOL-STYLE).

; Strengthened the identification of built-ins with raw Lisp definitions (see
; constant *primitive-logic-fns-with-raw-code*, as well as source function
; fns-different-wrt-acl2-loop-only and related functions).

; Added documentation about guards in :doc princ$ and :doc io.

; Removed trailing whitespace from the ACL2 sources.  Thanks to Warren Hunt for
; suggesting this change.

; We improved :doc redundant-events, especially to clarify that progn and
; make-event forms are never redundant.  Thanks to Harsh Raju Chamarthi for an
; email exchange that led us to make this improvement.

; For GCL only, further increased the binding stack (except, still, on
; Windows); see the setting of si::*multiply-stacks* in init.lisp.

; Modified *home-page* for searching books, to point to tag
; #Searching_and_browsing_the_books.

; For SBCL only, increased --control-stack-size from 4 to 8.

  :doc
  ":Doc-Section release-notes

  ACL2 Version  6.2 (June, 2013) Notes~/

  NOTE!  New users can ignore these release notes, because the
  ~il[documentation] has been updated to reflect all changes that are recorded
  here.

  Below we roughly organize the changes since Version 6.1 into the following
  categories of changes: existing features, new features, heuristic
  improvements, bug fixes, changes at the system level, Emacs support, and
  experimental versions.  Each change is described in just one category, though
  of course many changes could be placed in more than one category.

  ~st[CHANGES TO EXISTING FEATURES]

  The macro ~ilc[top-level] has been changed, so that evaluation of a form
  ~c[(top-level x)] results in an error when evaluation of ~c[x] results in an
  error.  Thanks to Jared Davis for observing that when evaluating a file using
  ~ilc[ld], an interrupt of a call of a ~ilc[top-level] call in that file would
  not prevent evaluation of later forms in the file.

  The macro ~ilc[THE] no longer causes an error when ~il[guard]-checking is
  ~c[:NONE].  For example, it had been the case that evaluation of
  ~c[(the integer t)] always caused an error; but now, there is no error after
  executing command ~c[:]~ilc[set-guard-checking]~c[ :NONE].  Thanks to Jared
  Davis for asking for a way to avoid such errors.

  The error printed when attempting to ``reincarnate'' a package ~-[] that is,
  to define a package that is not part of the ACL2 logical ~il[world] but
  exists in raw Lisp because it was once part of the world ~-[] is now much
  more instructive.  In particular, it shows pathnames for the previous and
  proposed ~ilc[defpkg] events, and it shows the symbols that are imported by
  one but not the other.  Thanks to Jared Davis for requesting this
  improvement.

  Functions ~ilc[open-input-channel] and ~ilc[open-output-channel] no longer
  cause an error when failing to open a channel because of a permissions
  problem, but instead return ~c[(mv nil state)].  Thanks to Jared Davis for
  requesting this change.  (Note: this change does not apply if the host Lisp
  is non-ANSI, i.e., if the host Lisp is non-ANSI GCL.)

  The advanced ~il[meta-extract] mechanisms, provided for using facts from the
  ~il[world] or metafunction context, have been enhanced in the following
  ways, in collaboration with Sol Swords.  ~l[meta-extract] for more
  details.~bq[]

  It is now permissible to use calls of ~c[meta-extract-global-fact] in
  hypotheses of ~ilc[clause-processor] rules, much as they are used in
  hypotheses of ~ilc[meta] rules.  ~l[meta-extract].  Thanks to Sol Swords for
  requesting this feature.

  The utility ~c[meta-extract-global-fact] is now a macro, which expands to a
  corresponding call of the new function, ~c[meta-extract-global-fact+].  This
  new function takes an alternate, extra ~il[state] as an argument; it is not
  to be executed, and it operates on the alternate state, whose logical
  ~il[world] is intended to be the same as that of the ``live'' (usual) state.

  A new sort of value for the ~c[obj] argument is supported for
  ~c[meta-extract-global-fact] (and ~c[meta-extract-global-fact+]), which
  results in a term equating a function application to its result.
  ~l[meta-extract], in particular the discussion of ~c[:fncall].
  ~eq[]

  It is now possible for ~c[trace$] to avoid printing prefixes of the form
  ~c[\"n> \"] and ~c[\")].  Now, forcing will take place as expected; ~pl[force].
  Thanks to Robert Krug for bringing this issue to our attention and sending an
  example, which we include as a comment in the ACL2 source code (see
  ~c[(deflabel note-6-2 ...)]).

  The heuristic is now delayed until after we check whether the hypothesis is
  already known, using ~il[type-set] reasoning alone (in particular, not using
  rewriting), to be true or to be false.  We believe that this is now the
  ``right'' order for those two operations.  We saw a slight speed up in the
  regression tests (about a percent) with this change, but that might be in the
  noise.

  A technical change makes the heuristic slightly less aggressive in preventing
  backchaining.  Roughly speaking, ordering checks based on function symbol
  counts could suffice to permit backchaining, where now variable counts also
  suffice.  Thanks to Robert Krug for showing us an example where backchaining
  led to a term with no free variables that was nevertheless subject to the
  ancestors check, preventing it from being rewritten.

  (For those who use ~ilc[defattach] to attach to ~c[ancestors-check])  We have
  used ~c[defrec] to introduce an `~c[ancestor]' data structure.  A new function,
  ~c[strip-ancestor-literals], should be used to obtain the literals from a
  list of ancestors, although ~c[strip-cars] will still work at this time.
  ~eq[]

  When we rewrite the current literal of the current clause we assume the
  falsity of the other literals and of the conclusions produced by forward
  chaining.  We have changed the order in which those assumptions are made,
  which affects the type-alist used during rewriting.  This has three effects:
  the new type-alist, which is sometimes stronger than the old one, may allow
  additional rules to fire, the choice of free vars may be different, and the
  order of the literals in forced subgoals may be different.  Should ``legacy''
  proofs fail under the new type-alist, we recommend looking for rules that are
  fired in the new proof that were not fired (on that same subgoal) in the old
  one.  Thanks to Dave Greve for sending us an example that led us to make this
  change.

  ~st[BUG FIXES]

  We fixed a soundness bug that could be exploited by calling system functions
  ~c[acl2-magic-mfc] or ~c[acl2-magic-canonical-pathname].  Thanks to Sol
  Swords for bringing this bug to our attention.

  We fixed a soundness bug in the handling of ~il[stobj]s, in which strings
  were recognized as stobjs in raw Lisp.  Thanks to Jared Davis for sending us
  a proof of ~c[nil] that exploited this bug.  We now have a much simpler
  example of this bug, as follows.
  ~bv[]
  (defstobj st fld)
  (defthm bad (stp \"abc\") :rule-classes nil)
  (defthm contradiction
    nil
    :hints ((\"Goal\" :in-theory (disable (stp)) :use bad))
    :rule-classes nil)
  ~ev[]

  We fixed bugs in extended metafunctions (~pl[extended-metafunctions]).  The
  macro ~c[mfc-ap] no longer takes a ~c[:TTREEP] keyword argument, because this
  argument could allow returning a tag tree that does not properly account for
  forcing.  The remaining ~c[mfc-xx] macros ~-[] ~c[mfc-relieve-hyp],
  ~c[mfc-rw+], ~c[mfc-rw], and ~c[mfc-ts] ~-[] still take a ~c[:TTREEP] keyword
  argument, but the corresponding functions when ~c[:TTREEP] is ~c[t] ~-[]
  ~c[mfc-relieve-hyp-ttree], ~c[mfc-rw+-ttree], ~c[mfc-rw-ttree], and
  ~c[mfc-ts-ttree] ~-[] were introduced with incorrect output signatures.  A
  complication is that ~c[mfc-relieve-hyp-ttree] was improperly defined in raw
  Lisp in a way that actually matched the incorrect signature!  All of these
  bugs have been fixed.  Perhaps any of them could have made it possible to
  prove ~c[nil], though we have not tried to do so.

  (Windows only) On Windows, it had been possible for ACL2 not to consider two
  pathnames to name the same file when the only difference is the case of the
  drive, e.g., `~c[C:]' vs. `~c[c:]'.  This has been fixed.  Thanks to Sol
  Swords for reporting this issue.

  Fixed a bug in the storing of rules for the tau system; ~pl[tau-system].
  (The error message mentions
  PARTITION-SIGNATURE-HYPS-INTO-TAU-ALIST-AND-OTHERS.)  Thanks to Sol Swords
  for reporting this bug and sending a simple example to illustrate it.

  It had been possible to admit the missing ~ilc[defthm] events printed by
  ~ilc[defabsstobj], and yet get an error when subsequently submitting the same
  ~c[defabsstobj] event, stating: ``Note discrepancy with existing formula''.
  The problem could occur when an expression of the form ~c[(or X Y)] occurred
  in one of those missing events, because ACL2 created it from the term
  ~c[(if X 't Y)] but then translated ~c[(or X Y)] to ~c[(if X X Y)], resulting
  in a mismatch.  This has been fixed.  Thanks to Jared Davis for reporting
  this bug using a simple example.

  A hard Lisp error was possible for certain illegal functional
  substitutions (~pl[lemma-instance]).  Thanks to Sol Swords for reporting this
  bug.

  We fixed a bug in the case that an exported function of a ~ilc[defabsstobj]
  event had a ~il[guard] of ~c[t].  Thanks to Jared Davis for sending a simple
  example when reporting this bug.

  We now avoid an infinite loop that could occur when attempting to close the
  standard character output channel (~pl[standard-co]).  Instead, an error
  message explains how to accomplish what was probably intended.  Thanks to
  Shilpi Goel for bringing this issue to our attention.

  (Windows only) Fixed a bug that was causing a hard error on Windows when ACL2
  encountered filenames starting with the tilde character (~c[~~]), for
  example, ~c[(ld \"~~/acl2-customization.lsp\")].  Thanks to Sol Swords for
  bringing this bug to our attention.  Also thanks to Harsh Raju Chamarthi for
  a useful conversation that led to a better fix than our first one.

  ~st[CHANGES AT THE SYSTEM LEVEL]

  ACL2 may now be built on recent versions of a new host Lisp, ANSI Gnu Common
  Lisp (GCL).  Traditional (non-ANSI) GCL was the original host Lisp underlying
  ACL2, and we are grateful for GCL support that we received from the late Bill
  Schelter and, more recently and particularly for ANSI GCL, from Camm Maguire.

  The `make' process suggested for book certification has changed
  substantially, thanks in large part to contributions from Jared Davis and Sol
  Swords.  We have seen the new process provide better performance on machines
  with many cores, and we expect maintenance advantages such as eliminating the
  need for Makefiles in individual book directories.  The ``classic'' process,
  which was based on community books file ~c[books/Makefile-generic], is still
  supported (~pl[books-certification-classic]) but may disappear in a future
  release of ACL2.  ~l[books-certification].  Most changes should be invisible
  to the user, other than improved `make'-level parallelism, with the exception
  of the following.
  ~bq[]
  o Variable ~c[ACL2_JOBS] is no longer supported, nor is it necessary; simply
  use `make' option `-j' instead.

  o Regressions now use `make' option ~c[-k] by default, which causes the
  regression to keep going after errors, rather than ~c[-i], which ignores
  errors.  If you encounter problems because of this change, use
  ~c[ACL2_IGNORE=-i] with your `make' command.

  o The `regression' target works for the experimental extension, ACL2(h)
  (~pl[hons-and-memoization]); target `regression-hons' no longer exists.
  ~eq[]
  Please let us know if you run into problems with the new infrastructure, as
  we consider the legacy infrastructure to be deprecated and we will probably
  eliminate much of it in the future.  In particular, circular dependencies
  were formerly prohibited at the directory level, but that is no longer the
  case, and we expect such cycles to occur in the future.

  Although ACL2 users don't typically modify raw Lisp variables, we have
  arranged to reset Lisp variable ~c[*default-pathname-defaults*] if necessary
  at startup so that it will not interfere with ACL2, in particular by messing
  up the initial connected book directory (~pl[cbd]).  Thanks to Jared Davis,
  Sol Swords, and Raymond Toy for helping us to identify this issue.

  ~st[EMACS SUPPORT]

  ~st[EXPERIMENTAL/ALTERNATE VERSIONS]

  In ACL2(h), ~ilc[print-object$] no longer uses the serialize printer except
  in system applications as before (e.g., write out ~c[.cert] files).  Thanks
  to Dave Greve for bringing this issue to our attention.

  Jared Davis contributed changes related to the ~ilc[memoize] utility of
  ACL2(h), including some low-level changes as well as the following.

  o ~ilc[Never-memoize] specifies that a given function should never
  be memoized.

  o Removed ~c[memoize-let], which may never have ever been used.

  o Removed the ~c[:inline] keyword option to memoize, which was just an alias
    for the ~c[:recursive] option.

  For ACL2(p), some anomalous behavior may no longer occur because prover
  calls (more specifically, trips through the ACL2 ``waterfall'') will return
  only after all sub-computations (threads) have finished.  Thanks to David
  Rager for contributing this improvement.

  ACL2(pr), which includes ~il[parallelism] (as for ACL2(p)) and non-standard
  analysis support for the ~il[real]s (as for ACL2(r)), now builds and can
  certify the community ~c[nonstd/] books.  Thanks to David Rager for his
  contribution to this capability.

  ~/~/")

(deflabel note-6-3

; Total number of release note items: 36.

; (CMUCL only) Allocated larger stack for CMUCL on 64-bit x86 installations.

; Evaluation of a redundant encapsulate event, in the case that the event is
; not equal to the corresponding earlier encapsulate event, now causes an
; additional message to be printed that directs the user to a new :doc topic --
; redundant-events -- which explains the situation (and includes some :doc that
; was formerly in :doc encapsulate).  Because of this, stop-redundant-event has
; been modified.  Thanks to Carl Eastlund for suggesting improvements along
; these lines.

; Modified functions stobjs-out and get-stobjs-out-for-declare-form according
; to a suggestion from Jared Davis, to cause an error when attempting to
; determine the stobjs-out for IF just as we already do for RETURN-LAST.

; Regarding the "bug in the case of a field of a (concrete) stobj that is an
; abstract stobj": this was a bug in the #-acl2-loop-only case of
; get-stobj-creator.

; Here is a little example showing the change to :by hints involving
; quote-normal form.  The proof of the THM failed until this change was made.
;
;   (defthm my-thm
;     (equal (car '(3 . 4)) 3)
;     :rule-classes nil)
;
;   (thm (equal (car (cons 3 4)) 3)
;        :hints (("Goal" :by my-thm)))

; (GCL only) Just below is a book, certifiable in ACL2 Version_6.2,
; illustrating the "obscure soundness bug due to an error in the GCL
; implementation of set-debugger-enable".  The problem was that the definition
; of set-debugger-enable-fn ended with #-acl2-loop-only code of the form (when
; (live-state-p state) ... state), which erroneously returns nil for a non-live
; state.
;
;   (in-package "ACL2")
;
;   (defthm false-formula
;     (equal (set-debugger-enable-fn nil (build-state))
;            nil)
;     :rule-classes nil)
;
;   (defthm true-formula
;     (implies (state-p1 s)
;              (state-p1 (set-debugger-enable-fn nil s)))
;     :hints (("Goal" :in-theory (enable state-p1)))
;     :rule-classes nil)
;
;   (defthm contradiction
;     nil
;     :hints (("Goal"
;              :use (false-formula
;                    (:instance true-formula
;                               (s (build-state))))))
;     :rule-classes nil)

; Improved our-truename (definition and calls) so that in case of an error, we
; get additional information.

; Added new :doc topic, ignored-attachment, pointing to it in the message
; printed by function ignored-attachment-msg.

; Changed all-equal to have a test of atom instead of endp, so that it
; could be guard-verifiable as requested by Eric Smith.

; Updated :doc make-event to clarify that it is legal to call make-event during
; make-event expansion, even outside an event context.

; Modified function maybe-add-command-landmark to avoid giving special
; treatment to with-prover-time-limit, since with-prover-time-limit is
; a full-fledged event constructor and hence this is not necessary.

; Removed definition of obsolete function, called acl2, from the
; sources.

; Slightly changed wording pertaining to the "combined" (xdoc) manual on the
; ACL2 home page.

; Fixed TAGS! target in GNUmakefile to do what it has been claming to
; do: rebuild TAGS even when TAGS is up-to-date with respect to the
; source files.

; Added new state global, acl2-sources-dir, in support of the new ACL2 startup
; banner in the case of an svn version.

; The guard for function ENDP has potentially been made trivially more
; efficient by using EQ to test against nil instead of EQUAL.

; In support of :by hint processing (see the :doc string below),
; remove-guard-holders now returns a term in quote normal form, even when the
; input term contains no guard holders.

; The startup banner can now be suppressed, though only by unsanctioned hacking
; in raw Lisp, as requested by Jared Davis.  See *print-startup-banner*, which
; has a comment explaining more about how this may not be appropriate and what
; needs to be done to suppress startup information.

; Removed support for "make fast", "make very-fast", and "make fast-meter",
; which as far as we know are no longer in use.  Thus, deleted
; quick-compile-acl2.  Also deleted no-object-file-or-out-of-date-object-file,
; since it was there only to support quick-compile-acl2.

; Improved the error message for #.expr when expr hasn't been defined by
; defconst.

; Modified printing of the banner for GCL, especially for versions 2.6.9 and
; later, due to a change in how GCL prints such banners.  (Printing of the
; banner still works fine with older versions of GCL.)

; For SBCL only, increased --control-stack-size from 8 to 16.

; For CMUCL only, declaimed len to be notinline, to avoid what appears to be a
; CMUCM compiler bug that shows up when attempting to certify the community
; book books/models/jvm/m1/defsys.lisp.

; Incorporated fix from Sol Swords for ACL2(h): "memoize time
; tracking: fix some subtle RDTSC-related problems that could cause
; safe-incf errors on machines that often switch your thread across
; cores".

; Just below is a book containing a proof of nil, which exploits the soundness
; bug reported by Jen Davis and Dave Greve, permitting a stobj to be bound by a
; let or mv-let form without being among the outputs of that form.  The bug was
; in translate11-let (and also, similarly, in translate11-mv-let); it was a
; coding bug that failed to distinguish between the original value of a formal,
; tbody, and an updated version of that formal.

;   (in-package "ACL2")
;
;   (defstobj st halt)
;
;   (defun foo (st)
;     (declare (xargs :stobjs st))
;     (let ((st (update-halt 0 st)))
;       (halt st)))
;
;   (defun bar ()
;     (declare (xargs :guard t))
;     (with-local-stobj
;      st
;      (mv-let (result st)
;              (let ((x (foo st)))
;                (declare (ignore x))
;                (mv (halt st) st))
;              result)))
;
;   (defthm thm1
;     (equal (bar) 0)
;     :rule-classes nil)
;
;   (defthm thm2
;     (equal (bar) nil)
;     :hints (("Goal" :in-theory (disable (bar))))
;     :rule-classes nil)
;
;   (defthm contradiction
;     nil
;     :hints (("Goal" :use (thm1 thm2)))
;     :rule-classes nil)

  :doc
  ":Doc-Section release-notes

  ACL2 Version  6.3 (October, 2013) Notes~/

  NOTE!  New users can ignore these release notes, because the
  ~il[documentation] has been updated to reflect all changes that are recorded
  here.

  Below we roughly organize the changes since Version  6.2 into the following
  categories of changes: existing features, new features, heuristic
  improvements, bug fixes, changes at the system level, Emacs support, and
  experimental versions.  Each change is described in just one category, though
  of course many changes could be placed in more than one category.

  ~st[CHANGES TO EXISTING FEATURES]

  The evaluation of a term from a ~ilc[bind-free] hypothesis had been expected
  to produce an alist binding free variables to terms.  While that is still
  legal, it is also legal for that evaluation to produce a list of such alists:
  then each is considered, until one of them permits all remaining hypotheses
  to be relieved.  ~l[bind-free].  Thanks to Sol Swords for requesting this
  enhancement.

  ACL2 continues to provide a way to specify keyword command abbreviations for
  the top-level loop; ~pl[ld-keyword-aliases].  However,
  ~ilc[ld-keyword-aliases] is now a ~il[table] rather than a ~il[state] global;
  it is thus no longer a so-called ~il[LD] special.  The functionality of
  ~c[set-ld-keyword-aliases] has essentially been preserved, except that it is
  now an event (~pl[events]), hence it may appear in a book; it is ~il[local]
  to a book (or ~ilc[encapsulate] event); and the ~ilc[state] argument is
  optional, and deprecated.  A non-local version (~c[set-ld-keyword-aliases!])
  has been added, along with corresponding utilities ~c[add-keyword-alias] and
  ~c[add-keyword-alias!] for adding a single keyword alias.
  ~l[ld-keyword-aliases].  Thanks to Jared Davis for correspondence that led us
  to make this change.

  The ~il[proof-checker] command ~c[(exit t)] now exits without a query (but
  still prints an event to show the ~c[:INSTRUCTIONS]).  Thanks to Warren Hunt
  for feedback leading us to make this change.

  We made the following minor changes to the behavior or ~c[dmr]; ~pl[dmr].
  First, if ~c[dmr] monitoring is enabled, then ~c[(dmr-start)] will have no
  effect other than to print a corresponding observation, and if monitoring is
  disabled, then ~c[(dmr-stop)] will have no effect other than to print a
  corresponding observation.  Second, it had been the case that when
  ~c[(dmr-start)] is invoked, the debugger was always automatically enabled
  with value ~c[t] (~pl[set-debugger-enable]), and the debugger remained
  enabled when ~c[(dmr-stop)] was invoked.  Now, the debugger is only enabled
  by ~c[(dmr-start)] if it is not already enabled and does not have setting
  ~c[:never].  Moreover, if such automatic enabling takes place, then the old
  setting for the debugger is restored by ~c[(dmr-stop)] unless
  ~ilc[set-debugger-enable] has first been called after that automatic
  enabling.  Finally, if the value of ~il[state] global variable
  ~c['debugger-enable] is ~c[:bt], then the new value will be ~c[:break-bt],
  not ~c[t].

  When a call of ~ilc[progn] is executed in the ACL2 loop, its constituent
  ~il[events] and their results are printed, just as was already done for calls
  of ~ilc[encapsulate].  Thanks to Jared Davis for a conversation causing us to
  consider this change.

  (CCL only) When ~ilc[set-debugger-enable] is invoked with an argument that
  prints a backtrace and CCL is the host Lisp, the backtrace will be limited to
  10,000 stack frames.  (We have seen more than 65,000 stack frames before this
  change.)  This limit is the value of raw Lisp variable
  ~c[*ccl-print-call-history-count*], which may be assigned another positive
  integer value to serve as the maximum number of stack frames to be printed.

  Improvements have been made pertaining to the disabling (inhibiting) of
  individual types of warning.  Now, inhibited warnings are implemented in a
  straightforward way using a separate ~il[table] for this purpose, the
  ~c[inhibit-warnings-table], rather than using the ~ilc[acl2-defaults-table].
  ~l[set-inhibit-warnings], and ~pl[set-inhibit-warnings!] for a variant that
  is not ~ilc[local] to an ~ilc[encapsulate] or a book in which it occurs.
  Thanks to Sol Swords for sending examples showing how
  ~ilc[set-inhibit-warnings] did not always behave as one might reasonably
  expect when books are involved.

  It had been the case that ~ilc[lp] took a single argument, ~c['raw].  This
  argument was not documented and also caused an error, so it has been
  eliminated.

  The functionality of ~ilc[make-event] has been significantly expanded.
  First: if the expansion is of the form ~c[(:OR e1 e2 ...)], then event forms
  ~c[e1], ~c[e2], and so on are evaluated, in order, until the evaluation of
  some ~c[ek] completes without error.  In that case, the expansion is treated
  simply as ~c[ek].  With this capability, alternative expansions can be
  attempted and the successful one does not need to be evaluated again.  See
  the new version of community book ~c[books/make-event/proof-by-arith.lisp]
  for an example.  Second, an expansion may be of the form ~c[(:DO-PROOFS e)],
  in which case the event ~c[e] is evaluated with proofs ~st[not] skipped;
  ~pl[ld-skip-proofsp].  Third, new keyword ~c[:EXPANSION?] can be used to
  avoid storing expansions in certificate files.  ~l[make-event].

  When a ~ilc[defun] event prints a failure message in the summary, that
  message now indicates when the failure is due to a failed proof of guard
  verification or a failed proof of the measure theorem.  Thanks to Shilpi Goel
  for requesting this enhancement.

  ~st[NEW FEATURES]

  ACL2 can now be instructed to time activities using real time (wall clock
  time) instead of run time (typically, cpu time).  ~l[get-internal-time].
  Thanks to Jared Davis for asking to be able to obtain real-time reports in
  event summaries.

  A new utility, ~ilc[sys-call+], is similar to existing utility ~ilc[sys-call]
  in that it executes a command.  Unlike ~c[sys-call], however, ~c[sys-call+]
  returns values that include output from the command (in addition to the exit
  status), rather than simply printing the command.  ~l[sys-call+].

  The new macro ~ilc[verify-guards+] extends the functionality of
  ~ilc[verify-guards] by permitting macro-aliases (~pl[macro-aliases-table]).
  ~l[verify-guards+].  Thanks to Jared Davis for requesting this feature and
  suggesting the use of ~ilc[make-event] in its implementation.  We have also
  modified ~ilc[verify-guards] to print a friendlier error message when its
  argument is a macro-alias.

  ~l[last-prover-steps] for a new utility that returns the number of prover
  steps most recently taken.

  ~st[HEURISTIC IMPROVEMENTS]

  The processing of ~c[:use] and ~c[:by] ~il[hints] has been changed in the
  following two rather subtle ways, thanks to suggestions from Sol Swords.
  ~bq[]

  o For ~c[:by] hints, the simplest check was an equality check, rather than a
  more general subsumption check.  That equality check was made after removing
  so-called ``guard holders'' (~ilc[must-be-equal], ~ilc[prog2$],
  ~ilc[ec-call], ~ilc[the]) from both the previous theorem and the purported
  theorem.  Now, guard-holder removal has been strengthened, so that the
  results are also put into so-called quote-normal form, for example replacing
  ~c[(cons '3 '4)] by ~c['(3 . 4)].

  o For a ~il[lemma-instance] provided to a ~c[:use] or ~c[:by] hint that
  is a ~c[:functional-instance], if a ~c[:do-not] hint (~pl[hints]) has
  specified that ~c[preprocess-clause] is not to be used, then preprocessing
  will not be used on the constraints.~eq[]

  We eliminated certain warnings about being ``weak'' for every
  ~c[:]~ilc[type-prescription] rule whose conclusion designates that the
  function call can be equal to one of its arguments, e.g.,
  ~c[(or (integerp (foo y)) (equal (foo y) y))].  In many cases (such as the
  one above), such warnings about ``weak'' simply aren't correct.

  ~st[BUG FIXES]

  Fixed a soundness bug that was permitting a ~il[stobj] to be bound by a
  ~ilc[let] or ~ilc[mv-let] form, without being among the outputs of that form.
  Thanks to Jen Davis and Dave Greve for reporting this bug.  Their report
  included an example which forms the basis for a proof of ~c[nil], included as
  a comment in the form ~c[(deflabel note-6-3 ...)] in ACL2 source file
  ~c[ld.lisp].

  (GCL only) Fixed an obscure soundness bug due to an error in the GCL
  implementation of ~ilc[set-debugger-enable].  For details, see the relevant
  comment in the ACL2 source code under ~c[(deflabel note-6-3 ...)].

  Fixed a bug in the case of a field of a (concrete) stobj that is an abstract
  stobj (~pl[nested-stobjs]).  Thanks to David Rager for bringing this bug to
  our attention.

  Splitter output for type ~c[if-intro] (~pl[splitter]) could formerly occur
  even when at most one subgoal is generated.  This has been fixed.

  Fixed a bug in ~ilc[wof], hence in ~ilc[psof] (which uses ~c[wof]), that was
  causing the printing of a bogus error message.

  A small logical bug has been fixed in the logical definition of
  ~ilc[sys-call-status].  Formerly it always returned ~c[(mv nil state)]
  whenever the oracle of the state is non-empty (~pl[state]).

  Fixed a bug that was causing an error upon evaluation of the form
  ~c[(set-prover-step-limit nil)].  Thanks to David Russinoff for reporting
  this error.

  The ~c[:measure] (if supplied) is now ignored when checking redundancy with
  respect to a non-recursive definition that is not defined within a
  ~ilc[mutual-recursion].  (~l[redundant-events] and ~pl[xargs].)  It had been
  possible to get a low-level ACL2 error in this situation.  Thanks to Jared
  Davis for reporting this bug with a helpful example.

  Eliminated a potential error when using ~ilc[comp] to compile an uncompiled
  function defined under ~ilc[progn!], which we observed in LispWorks.

  ~st[CHANGES AT THE SYSTEM LEVEL]

  The ACL2 sources are now publicly available between ACL2 releases, using svn;
  see the new ``~c[acl2-devel]'' project hosted by Google code at
  ~url[http://acl2-devel.googlecode.com].  Although such a copy of ACL2 is
  likely to work well with the latest svn (trunk) revision of the ACL2
  community books (~pl[community-books]), please take seriously the warning
  message printed at startup: ``The authors of ACL2 consider svn distributions
  to be experimental; they may be incomplete, fragile, and unable to pass our
  own regression.''  That message also provides instructions for bug reports.
  If you decide to use svn versions of either the community books or ACL2, then
  you should use both, as they tend to be kept in sync.  We fully expect ACL2
  releases to continue from time to time, as usual.  Thanks to Jared Davis for
  his efforts in setting up the new acl2-devel project and svn repository, and
  to him and David Rager for convincing us to distribute ACL2 sources via svn
  between releases.

  Thanks to a suggestion from Jared Davis, over 30 built-in functions are now
  declared to be inline in order to boost performance.  (The list may be found
  by searching ACL2 source file ~c[axioms.lisp] for ``~c[(declaim (inline]''.)

  Better support has been provided for command line arguments, especially those
  supplied directly by the user when calling ACL2.  For one, problems with
  quoting have been solved using ~c[\"$@\"] in place of ~c[$*].  Also, the
  function ~ilc[save-exec] now allows specification of arguments, both for the
  host Lisp as well as ``inert'' arguments that can be passed along to calls of
  programs (as with ~ilc[sys-call]).  A keyword argument, ~c[:return-from-lp],
  specifies a form to evaluate before quitting the read-eval-print loop at
  startup.  ~l[save-exec].  Also see the source function ~c[user-args-string]
  and its comments, source file ~c[acl2-init.lisp], for more information.
  Thanks to Jared Davis for suggesting the use of ~c[\"$@\"], as well as
  modifications to ~ilc[save-exec] and helpful conversations about that.

  A rather extensive overhaul has taken place for the function proclaiming
  mechanism.  As before, this is only used when the host Lisp is GCL.  However,
  building an executable is now faster for some Lisps, including GCL, by
  avoiding repeated recompilation and perhaps repeated initialization.

  (CCL only) We increased stack sizes when the host Lisp is CCL.  The default
  for recent CCL versions is equivalent to specifying `~c[-Z 2M]' on the
  command line, but saved ACL2 scripts (including experimental versions
  ACL2(h), ACL2(p), ACL2(r), and combinations of them) to `~c[-Z 64M]',
  representing a 32-fold increase.  Thanks to Jared Davis for pointing us to
  community books file ~c[books/centaur/ccl-config.lsp] and to Sol Swords for
  helpful discussions.

  (SBCL only) Fixed ~c[save-exec] for host Lisp SBCL to provide the same export
  of variable ~c[SBCL_HOME] that was provided in the original ~c[saved_acl2]
  script.

  (GCL only) We made changes, following suggestions from Camm Maguire (whom we
  thank for these suggestions), to support ACL2 builds on recent versions of
  GCL (2.6.8 and 2.6.10; we recommend against using GCL 2.6.9, since issues
  there were fixed in 2.6.10).  Specifically, we no longer set the hole size,
  and we allocate contiguous pages sufficient to run an ACL2 regression without
  failing due to memory limitations.

  ~st[EMACS SUPPORT]

  Modified file ~c[emacs/emacs-acl2.el] to eliminate some warnings that were
  appearing in a recent Emacs version, replacing ~c[(end-of-buffer)] by
  ~c[(goto-char (point-max))] and ~c[next-line] by ~c[forward-line].  Thanks to
  Warren Hunt for bringing the warnings to our attention.

  ~st[EXPERIMENTAL/ALTERNATE VERSIONS]

  (Allegro CL only) ACL2(h) now avoids blow-ups in hash table sizes that could
  be caused by ~il[hons-shrink-alist].  Thanks to Jared Davis for helping to
  debug this problem, and to David Rager for contributing the community book
  ~c[books/parsers/earley/earley-parser.lisp], which highlighted this problem.

  (SBCL only) Fixed a bug that was causing a Lisp break after turning on
  ~il[waterfall-parallelism].  Thanks to David Rager for confirming that our
  proposed fix is correct.

  ~/~/")

(deflabel the-method
  :doc
  ":Doc-Section Miscellaneous

  how to find proofs~/

  Also ~pl[introduction-to-the-theorem-prover] for a more detailed tutorial on
  how to prove theorems with ACL2.

  Many users develop proof scripts in an Emacs buffer and submit one event at a
  time to the theorem prover running in a ~c[*shell*] buffer.  The script
  buffer is logically divided into two regions: the events that have been
  accepted by the theorem prover and those that have not yet been accepted.  An
  imaginary ``barrier'' divides these two regions.  The region above the
  barrier describes the state of the ~c[*shell*] buffer (and ACL2's logical
  world).  The region below the barrier is the ``to do'' list.

  We usually start a proof project by typing the key lemmas, and main goal into
  the to do list.  Definitions are here just regarded as theorems to
  prove (i.e., the measure conjectures).  Then we follow ``The Method.''

  (1) Think about the proof of the first theorem in the to do list.  Structure
  the proof either as an induction followed by simplification or just
  simplification.  Have the necessary lemmas been proved? That is, are the
  necessary lemmas in the done list already?  If so, proceed to Step 2.
  Otherwise, add the necessary lemmas at the front of the to do list and repeat
  Step 1.

  (2) Call the theorem prover on the first theorem in the to do list and let
  the output stream into the *shell* buffer.  Abort the proof if it runs more
  than a few seconds.

  (3) If the theorem prover succeeded, advance the barrier past the successful
  command and go to Step 1.

  (4) Otherwise, inspect the failed proof attempt, starting from the beginning,
  not the end.  Basically you should look for the first place the proof attempt
  deviates from your imagined proof.  If your imagined proof was inductive,
  inspect the induction scheme used by ACL2.  If that is ok, then find the
  first subsequent subgoal that is stable under simplification and think about
  why it was not proved by the simplifier.  If your imagined proof was not
  inductive, then think about the first subgoal stable under simplification, as
  above.  Modify the script appropriately.  It usually means adding lemmas to
  the to do list, just in front of the theorem just tried.  It could mean
  adding hints to the current theorem.  In any case, after the modifications go
  to Step 1.~/

  We do not seriously suggest that this or any rotely applied algorithm will
  let you drive ACL2 to difficult proofs.  Indeed, to remind you of this we
  call this ``The Method'' rather than ``the method.''  That is, we are aware
  of the somewhat pretentious nature of any such advice.  But these remarks
  have helped many users approach ACL2 in a constructive and disciplined way.

  We say much more about The Method in the ACL2 book.  See the home page.  Also
  ~pl[set-gag-mode] for a discussion of a way for ACL2 to help you to use The
  Method.  And again, ~pl[introduction-to-the-theorem-prover] for a more
  detailed tutorial.

  Learning to read failed proofs is a useful skill.  There are several kinds of
  ``checkpoints'' in a proof: (1) a formula to which induction is being (or
  would be) applied, (2) the first formula stable under simplification, (3) a
  formula that is possibly generalized, either by cross-fertilizing with and
  throwing away an equivalence hypothesis or by explicit generalization of a
  term with a new variable.

  At the induction checkpoint, confirm that you believe the formula being
  proved is a theorem and that it is appropriately strong for an inductive
  proof.  Read the selected induction scheme and make sure it agrees with your
  idea of how the proof should go.

  At the post-simplification checkpoint, which is probably the most commonly
  seen, consider whether there are additional rewrite rules you could prove to
  make the formula simplify still further.  Look for compositions of function
  symbols you could rewrite.  Look for contradictions among hypotheses and
  prove the appropriate implications: for example, the checkpoint might contain
  the two hypotheses ~c[(P (F A))] and ~c[(NOT (Q (G (F A))))] and you might
  realize that ~c[(implies (p x) (q (g x)))] is a theorem.  Look for signs that
  your existing rules did not apply, e.g., for terms that should have been
  rewritten, and figure out why they were not.  Possible causes include that
  they do not exactly match your old rules, that your old rules have hypotheses
  that cannot be relieved here -- perhaps because some other rules are missing,
  or perhaps your old rules are disabled.  If you cannot find any further
  simplifications to make in the formula, ask yourself whether it is valid.  If
  so, sketch a proof.  Perhaps the proof is by appeal to a combination of
  lemmas you should now prove?

  At the two generalization checkpoints --- where hypotheses are discarded or
  terms are replaced by variables --- ask yourself whether the result is a
  theorem.  It often is not.  Think about rewrite rules that would prove the
  formula.  These are often restricted versions of the overly-general formulas
  created by the system's heuristics.

  ~l[proof-tree] for a discussion of a tool to help you navigate through ACL2
  proofs.")

(deflabel lp
  :doc
  ":Doc-Section Miscellaneous

  the Common Lisp entry to ACL2~/

  To enter the ACL2 ~il[command] loop from Common Lisp, call the Common
  Lisp program ~c[lp] (which stands for ``loop,'' as in ``read-eval-print
  loop'' or ``~il[command] loop.'')  The ACL2 ~il[command] loop is actually
  coded in ACL2 as the function ~ilc[ld] (which stands for ``load'').  The
  ~il[command] loop is just what you get by loading from the standard
  object input channel, ~ilc[*standard-oi*].  Calling ~ilc[ld] directly from
  Common Lisp is possible but fragile because hard lisp errors or
  aborts throw you out of ~ilc[ld] and back to the top-level of Common Lisp.
  ~c[Lp] calls ~ilc[ld] in such a way as to prevent this and is thus the
  standard way to get into the ACL2 ~il[command] loop.  Also
  ~pl[acl2-customization] for information on the loading of an
  initialization file.~/

  All of the visible functionality of ~c[lp] is in fact provided by ~ilc[ld],
  which is written in ACL2 itself.  Therefore, you should ~pl[ld]
  for detailed ~il[documentation] of the ACL2 ~il[command] loop.  We sketch it
  below, for novice users.

  Every expression typed to the ACL2 top-level must be an ACL2
  expression.

  Any ACL2 expression may be submitted for evaluation.  Well-formedness is checked.
  Some well-formed expressions cannot be evaluated because they involve (at some level)
  undefined constrained functions (~pl[encapsulate]).  In addition, ACL2 does not
  allow ``global variables'' in expressions to be evaluated.  Thus, ~c[(car '(a b c))]
  is legal and evaluates to ~c[A], but ~c[(car x)] is not, because there is no
  ``global context'' or binding environment that gives meaning to the variable symbol
  ~c[x].

  There is an exception to the global variable rule outlined above:
  single-threaded objects (~pl[stobj]) may be used as global variables
  in top-level expressions.  The most commonly used such object is the
  ACL2 ``current state,'' which is the value of the variable symbol
  ~ilc[state].  This variable may occur in the top-level form to be
  evaluated, but must be passed only to ACL2 functions ``expecting'' ~c[state]
  as described in the documentation for ~ilc[state] and for ~ilc[stobj]s in general.
  If the form returns a new ~il[state] object as one of its
  values, then that is considered the new ``current'' ~il[state] for
  the evaluation of the subsequent form.  ~l[state].

  ACL2 provides some support for the functionality usually provided by
  global variables in a read-eval-print loop, namely the saving of the
  result of a computation for subsequent re-use in another expression.
  ~l[assign] and ~pl[@].

  If the form read is a single keyword, e.g., ~c[:]~ilc[pe] or ~c[:]~ilc[ubt], then
  special procedures are followed.  ~l[keyword-commands].

  The ~il[command] loop keeps track of the ~il[command]s you have typed and
  allows you to review them, display them, and roll the logical ~il[state]
  back to that created by any ~il[command].  ~l[history].

  ACL2 makes the convention that if a top-level form returns three
  values, the last of which is an ACL2 ~il[state], then the first is
  treated as a flag that means ``an error occurred,'' the second is
  the value to be printed if no error occurred, and the third is (of
  course) the new ~il[state].  When ``an error occurs'' no value is
  printed.  Thus, if you execute a top-level form that happens to
  return three such values, only the second will be printed (and that
  will only happen if the first is ~c[nil]!).  ~l[ld] for details.")

#-acl2-loop-only
(defun-one-output compiled-function-p! (fn)

; In CMU Lisp, compiled-function-p is braindead.  It seems that the
; symbol-function of every defun'd function is a ``compiled'' object.
; Some are # and others are #.
; I think the following test works.  Fn is assumed to be a symbol.

  #+cmu
  (not (eq (type-of (symbol-function fn)) 'eval:interpreted-function))
  #-cmu
  (compiled-function-p (symbol-function fn)))

(defun compile-function (ctx fn0 state)

; Fn0 can either be a symbol, (:raw sym), or (:exec sym).

  (declare (xargs :guard
                  (and (or (symbolp fn0)
                           (and (consp fn0)
                                (member-eq (car fn0) '(:raw :exec))
                                (consp (cdr fn0))
                                (null (cddr fn0))))
                       (state-p state))))
  (let ((wrld (w state))
        (fn (if (consp fn0)
                (cadr fn0)
              fn0)))
    (cond
     ((not (eq (f-get-global 'compiler-enabled state) t))
      (value (er hard ctx
                 "Implementation error: Compile-function called when ~x0."
                 '(not (eq (f-get-global 'compiler-enabled state) t)))))
     ((eq (getprop fn 'formals t 'current-acl2-world wrld)
          t)
      (er soft ctx
          "~x0 is not a defined function in the current ACL2 world."
          fn))
     (t
      (state-global-let*
       ((trace-specs (f-get-global 'trace-specs state))
        (retrace-p t))
       (prog2$
        #+acl2-loop-only
        nil
        #-acl2-loop-only
        (let ((trace-spec
               (assoc-eq fn (f-get-global 'trace-specs state))))
          (when trace-spec
            (untrace$-fn (list fn) state))
          (let* ((form (cltl-def-from-name fn wrld))
                 (*1*fn (*1*-symbol fn))
                 (raw-only-p  (and (consp fn0) (eq (car fn0) :raw)))
                 (exec-only-p (and (consp fn0) (eq (car fn0) :exec))))
            (cond
             ((not (or exec-only-p
                       (compiled-function-p! fn)))
              (cond (form
                     (eval (make-defun-declare-form fn form))))
              (compile fn)))
            (cond
             ((and (not raw-only-p)
                   (fboundp *1*fn)
                   (not (compiled-function-p! *1*fn)))
              #-acl2-mv-as-values ; may delete this restriction in the future
              (eval
               (make-defun-declare-form
                fn
                (cons 'defun (oneified-def fn wrld))))
              (compile *1*fn)))
            (when trace-spec
              (trace$-fn trace-spec ctx state))))
        (value fn)))))))

#-acl2-loop-only
(defun getpid$ ()

; This function is intended to return the process id.  But it may return nil
; instead, depending on the underlying lisp platform.

  (let ((fn
         #+allegro 'excl::getpid
         #+gcl 'si::getpid
         #+sbcl 'sb-unix::unix-getpid
         #+cmu 'unix::unix-getpid
         #+clisp (or (let ((fn0 (find-symbol "PROCESS-ID" "SYSTEM")))
                       (and (fboundp fn0) ; CLISP 2.34
                            fn0))
                     (let ((fn0 (find-symbol "PROGRAM-ID" "SYSTEM")))
                       (and (fboundp fn0) ; before CLISP 2.34
                            fn0)))
         #+ccl 'ccl::getpid
         #+lispworks 'system::getpid
         #-(or allegro gcl sbcl cmu clisp ccl lispworks) nil))
    (and fn
         (fboundp fn)
         (funcall fn))))

#-acl2-loop-only
(defun-one-output tmp-filename (dir suffix)

; Warning: If this function is changed, look at its call in save-gprof.lsp.

; Dir should be a filename in Unix-style syntax, possibly "".  We return a
; filename in Unix-style syntax.

  (let ((pid (and (not (eq (f-get-global 'keep-tmp-files *the-live-state*)
                           :no-pid))
                  (getpid$)))
        (dir (if (and (not (equal dir ""))
                      (not (eql (char dir (1- (length dir)))
                                *directory-separator*)))
                 (concatenate 'string dir *directory-separator-string*)
               dir)))
    (coerce (packn1 (list* dir
                           "TMP"
                           (if pid
                               (if suffix
                                   (list "@" pid "@" suffix)
                                 (list "@" pid "@"))
                             (if suffix
                                 (list suffix)
                               nil))))
            'string)))

(defun keep-tmp-files (state)
  (f-get-global 'keep-tmp-files state))

(defun comp-fn (fns gcl-flg tmp-suffix state)

; Gcl-flg should only be used with GCL, and causes .c and .h files to be left
; around after compilation.

  (declare (xargs :guard (and (state-p state)
                              (or (and (true-listp fns) fns)
                                  (symbolp fns))
                              (stringp tmp-suffix)
                              (not (equal tmp-suffix ""))))
           #+acl2-loop-only
           (ignore tmp-suffix))
  (cond
   ((eql 0 (f-get-global 'ld-level state))
    (pprogn (warning$ 'comp "Comp"
                      "Comp is ignored outside the ACL2 loop.")
            (value nil)))
   #-gcl
   (gcl-flg
    (er soft 'comp
        "Comp-gcl may only be used in GCL implementations."))
   ((not (eq (f-get-global 'compiler-enabled state) t))
    (value nil))
   (t
    (let ((fns (cond
                ((or (and (symbolp fns)
                          (not (eq fns t))
                          (not (eq fns :raw))
                          (not (eq fns :exec))
                          (not (eq fns nil)))
                     (and (consp fns)
                          (member-eq (car fns) '(:raw :exec))
                          (consp (cdr fns))
                          (null (cddr fns))))
                 (list fns))
                (t fns))))
      (cond
       ((and (consp fns)
             (null (cdr fns))
             (not gcl-flg))
        (compile-function 'comp (car fns) state))
       ((null fns)
        (er soft 'comp
            "We do not allow the notion of compiling the empty list of ~
             functions.  Perhaps you meant to do something else."))
       (t
        #+acl2-loop-only
        (value t)
        #-acl2-loop-only
        (state-global-let*
         ((retrace-p t))
         (let ((*package* *package*)
               (dir (or (f-get-global 'tmp-dir state)
                        (f-get-global 'connected-book-directory state)
                        ""))
               (raw-fns nil)
               (exec-fns nil)
               (trace-specs nil))
           (cond
            ((consp fns)
             (dolist (fn fns)
               (cond
                ((and (consp fn)
                      (member-eq (car fn) '(:raw :exec)))
                 (cond ((and (consp (cdr fn))
                             (null (cddr fn))
                             (symbolp (cadr fn)))
                        (cond ((eq (car fn) :raw)
                               (setq raw-fns (cons (cadr fn) raw-fns)))
                              (t ; :exec
                               (setq exec-fns (cons (cadr fn) exec-fns)))))
                       (t
                        (er hard 'comp
                            "Unexpected function specifier, ~x0."
                            fn))))
                ((symbolp fn)
                 (setq raw-fns (cons fn raw-fns))
                 (setq exec-fns (cons fn exec-fns)))
                (t (er hard 'comp
                       "Unexpected function specifier, ~x0."
                       fn)))
               (setq raw-fns (nreverse raw-fns))
               (setq exec-fns (nreverse exec-fns))))
            (t (setq raw-fns fns)
               (setq exec-fns fns)))
           (when (not (eq fns :exec))
             (setq trace-specs
                   (f-get-global 'trace-specs state))
             (untrace$)
             (let ((tmpfile (tmp-filename dir nil)))
               (compile-uncompiled-defuns
                tmpfile
                (if (or (eq fns t)
                        (eq fns :raw))
                    :some
                  raw-fns)
                gcl-flg)))
           (when (not (eq fns :raw))
             (when (and (null trace-specs)
                        (f-get-global 'trace-specs state))
               (setq trace-specs
                     (f-get-global 'trace-specs state))
               (untrace$))
             (let ((tmpfile (tmp-filename dir tmp-suffix)))
               (compile-uncompiled-*1*-defuns
                tmpfile
                (if (member-eq fns '(t :exec))
                    :some
                  exec-fns)
                gcl-flg)))
           (when trace-specs
             (trace$-lst trace-specs 'comp state))
           (value t)))))))))

#-acl2-loop-only
(defmacro comp (fns)
  (declare (ignore fns))
  nil)

#+acl2-loop-only
(defmacro comp (fns)

  ":Doc-Section Events

  compile some ACL2 functions~/

  NOTE: ~c[Comp] is a no-op if explicit compilation is suppressed;
  ~pl[compilation].  The documentation here assumes that this is not the case.

  ~bv[]
  Examples:
  :comp t          ; compile all uncompiled ACL2 functions
  (comp t)         ; same as above, but can be put into a book
  (comp :exec)     ; compile all uncompiled logic (``*1*'') definitions
  :comp foo        ; compile the defined function foo
  :comp (:raw foo) ; compile the raw Lisp version of the defined function foo
                     but not the corresponding logic definition
  :comp (foo bar)  ; compile the defined functions foo and bar
  :comp (foo (:raw bar))  ; compile the defined functions foo and bar, but for
                          ; bar do not compile the corresponding logic definition

  General Form:
  :comp specifier
  where specifier is one of the following:

    t                     compile all user-defined ACL2 functions that are
                            currently uncompiled (redefined built-in functions
                            are not recompiled)
    :exec                 same as t, except that only logic versions are
                            compiled (see below), not raw Lisp definitions
    :raw                  same as t, except that only raw Lisp definitions are
                            compiled, not logic version (see below)
    (name-1 ... name-k)   a non-empty list of names of functions defined by
                            DEFUN in ACL2, except that each name-i can be of
                            the form (:raw sym) or (:exec sym), where sym is
                          the name of such a function
    name                  same as (name)
  ~ev[]

  When you define a function in ACL2, you are really causing two definitions to
  be made ``under the hood'' in Common Lisp: the definition is submitted
  explicitly to raw Lisp, but so is a corresponding ``logic definition''.  If
  guards have not been verified, then only the logic definition will be
  evaluated; ~pl[guards-and-evaluation], in particular the section titled
  ``Guards and evaluation V: efficiency issues''.

  Thus, if you are not verifying ~il[guard]s and you want the benefit of Lisp
  compilation for speed and space efficiency, then you may want to place the
  form ~c[(comp :exec)] in your ~il[books].

  Generally it is not necessary to place the form ~c[(comp t)], or the form
  ~c[(comp :raw)], in a book, because ~ilc[certify-book] compiles the raw Lisp
  definitions anyhow, by default.  But you may wish to put ~c[(comp t)] or
  ~c[(comp fn1 fn2 ... fnk)] in a book when such a form precedes expensive
  calls of functions, for example for proofs involving calls of functions on
  large constants, or to support computationally expensive macroexpansion.

  As suggested by the examples above, if a function specifier is of the form
  ~c[(:raw fn)], then ~c[fn] will be compiled in raw Common Lisp but its
  corresponding logic definition will not be compiled; and for ~c[(:exec fn)],
  it's the other way around.

  The use of ~c[:comp] may create various files whose names start with
  ``~c[TMP*]'', but it then deletes them.  If you want to save these files,
  evaluate ~c[(assign keep-tmp-files t)].~/

  Also ~pl[set-compile-fns] for a way to compile each function as it is
  defined.  But note that ~c[set-compile-fns] is ignored during
  ~ilc[include-book].

  Note that if functions are traced (~pl[trace$]), then ~c[comp] will first
  untrace the functions that are to be compiled, then will do the compile(s),
  and finally will re-trace the functions that it untraced (using their
  original trace specs).  In particular, if you have traced a function and then
  you compile it using ~c[:comp], the resulting traced function will be
  compiled as well unless you specified ~c[:compile nil] in your trace spec;
  and after you untrace the function it will definitely run compiled.

  We conclude with a technical remark only for those who use trust tags to
  write raw Lisp code.  ~c[:Comp] generally creates files to compile unless it
  is given a single function to compile.  Those files contain the ACL2
  definitions of all functions to compile, omitting those in the lists obtained
  by evaluating the forms ~c[(@ logic-fns-with-raw-code)] and
  ~c[(@ program-fns-with-raw-code)].  ~c[:Comp] skips compilation for functions
  that are already compiled, as is typically the case when you redefine
  functions in raw Lisp using the utility ~c[include-raw] defined in community
  book ~c[books/tools/include-raw.lisp].  But if you define interpreted (as
  opposed to compiled) functions with raw Lisp code, say by using trust
  tags (~pl[defttag]) and ~ilc[progn!], then you are advised to add all such
  symbols to one of the lists stored in the two ~il[state] globals above: to
  ~c[logic-fns-with-raw-code] if the function symbol is in ~c[:]~ilc[logic]
  mode, else to ~c[program-fns-with-raw-code].  Then, instead of the
  corresponding ACL2 definition (without raw Lisp code) being written to a
  file, the function symbol will be passed directly to the Lisp ~c[compile]
  function.  Note that the above two state globals are both untouchable, so you
  may need to deal with that before modifying them, for example as
  follows (also ~pl[remove-untouchable]).
  ~bv[]
  (defttag t)
  (state-global-let*
   ((temp-touchable-vars t set-temp-touchable-vars))
   (progn! (f-put-global 'logic-fns-with-raw-code
                         (cons 'my-fn (@ logic-fns-with-raw-code))
                         state)))
  ~ev[]~/

  :cited-by Programming"

  `(comp-fn ,fns nil "1" state))

(defmacro comp-gcl (fns)

  ":Doc-Section Comp

  compile some ACL2 functions leaving .c and .h files~/~/

  ~c[Comp-gcl] is for use by experts who want to examine the results of GCL
  compilation, and it may only be used with ACL2 implementations built on top
  of GCL.  It takes exactly the same arguments as ~ilc[comp], and has the same
  basic functionality (~pl[comp]), but has two additional effects.  First,
  files ~c[\"TMP.lisp\"] and ~c[\"TMP1.lisp\"] are always created, even when a
  single function is specified.  Second, ~c[comp-gcl] always leaves files
  ~c[\"TMP.c\"], ~c[\"TMP.h\"], ~c[\"TMP1.c\"], and ~c[\"TMP1.h\"] when
  compilation is complete.~/

  :cited-by Programming"

  `(comp-fn ,fns t "1" state))

(defun scan-past-deeper-event-landmarks (depth wrld)

; We scan down wrld until either it is exhausted or we find a command-landmark
; or we find an event-landmark whose access-event-tuple-depth is depth or less.
; Thus, the world we return is either nil or begins with a command-landmark or
; event-landmark.

  (cond
   ((or (null wrld)
        (and (eq (car (car wrld)) 'command-landmark)
             (eq (cadr (car wrld)) 'global-value)))
    wrld)
   ((and (eq (car (car wrld)) 'event-landmark)
         (eq (cadr (car wrld)) 'global-value))
    (cond
     ((> (access-event-tuple-depth (cddr (car wrld))) depth)
      (scan-past-deeper-event-landmarks depth (cdr wrld)))
     (t wrld)))
   (t (scan-past-deeper-event-landmarks depth (cdr wrld)))))

(defun puffable-encapsulate-p (cddr-car-wrld)
  (and (eq (access-event-tuple-type cddr-car-wrld) 'encapsulate)
       (let ((last-form (car (last (access-event-tuple-form cddr-car-wrld)))))
         (case-match last-form
           (('table
             'trusted-clause-processor-table
             &
             ('quote (& . t))) ; t indicates a dependent clause processor
            nil)
           (& t)))))

(defun puffable-command-blockp (wrld cmd-form)

; Initially, wrld should be the cdr of a world starting at some
; command-landmark.  Cmd-form should be the command-tuple form of that
; landmark (note that it will be nil for the very first
; command-landmark ever laid down, the one with
; access-command-tuple-number -1).

; This function returns t if the command block starting at wrld satisfies
; either of the following:

; (a) the first (last executed) event-tuple in the command block has a
;     different form than cmd-form, or
; (b) the first event form is either an encapsulate whose last form is not a
;     dependent clause processor declaration, or is an include-book.

; Suppose neither obtains.  Then we return nil.  What does this mean?
; It means the command and event tuples are the same (so no macros
; were invovled in hiding the event) and the event wasn't an an
; encapsulate or include-book.

  (cond
   ((or (null wrld)
        (and (eq (car (car wrld)) 'command-landmark)
             (eq (cadr (car wrld)) 'global-value)))
    nil)
   ((and (eq (car (car wrld)) 'event-landmark)
         (eq (cadr (car wrld)) 'global-value))
    (or (and cmd-form
             (not (equal cmd-form (access-event-tuple-form (cddr (car wrld))))))
        (puffable-encapsulate-p (cddr (car wrld)))
        (eq (access-event-tuple-type (cddr (car wrld))) 'include-book)))
   (t (puffable-command-blockp (cdr wrld) cmd-form))))

(defun puffable-command-numberp (i state)

; Let i be a legal relative command number for (w state).  We determine whether
; the command at i is puffable.

  (mv-let (flg n)
          (normalize-absolute-command-number
           (relative-to-absolute-command-number i (w state))
           (w state))
          (and (null flg)
               (let ((wrld (lookup-world-index 'command n (w state))))
                 (puffable-command-blockp
                  (cdr wrld)
                  (access-command-tuple-form (cddr (car wrld))))))))

(defun puff-command-block (wrld ans restore-cbd ctx state)

; Wrld is a world that starts just after a command landmark.  We scan down to
; the next command landmark and return the list of events in this command
; block.  We replace every encapsulate and include-book by the events in its
; body or file, which exposes the LOCAL events that are not actually part of
; wrld now.  However, we do not recursively flatten the encapsulates and
; include-books that are exposed by this flattening.

  (cond
   ((or (null wrld)
        (and (eq (car (car wrld)) 'command-landmark)
             (eq (cadr (car wrld)) 'global-value)))
    (value (if restore-cbd
               (append ans (list `(set-cbd ,(cbd))))
             ans)))
   ((and (eq (car (car wrld)) 'event-landmark)
         (eq (cadr (car wrld)) 'global-value))
    (cond
     ((eq (access-event-tuple-type (cddr (car wrld))) 'encapsulate)

; In the case of an encapsulate event, flattening means do the body of the
; encapsulate -- including the LOCAL events.  Note that this destroys the sense
; of those encapsulates that introduce constrained functions!  After flattening
; the constrained functions are defined as their witnesses!  We cannot recover
; the LOCAL events by a scan through wrld since they are not in wrld.  We must
; instead re-execute the body of the encapsulate.  Therefore, we just append
; the body of the encapsulate to our evolving ans.

; Now there is a problem here.  The body of the encapsulate might contain a
; macro form such as (defstub fn (x y) t) which when executed will expand to an
; encapsulate and which, intuitively, we ought to flatten.  Because it is a
; macro form, we cannot here recognize it as an encapsulate nor could we figure
; out its body.

; The way out of this problem, if one wants to recursively flatten, is to
; re-execute the events in our returned ans, thereby exposing the next layer of
; flattenable events, and then flatten the area again.

      (puff-command-block
       (scan-past-deeper-event-landmarks
        (access-event-tuple-depth (cddr (car wrld)))
        (cdr wrld))
       (cond ((puffable-encapsulate-p (cddr (car wrld)))
              (append (cddr (access-event-tuple-form (cddr (car wrld)))) ans))
             (t (cons (access-event-tuple-form (cddr (car wrld)))
                      ans)))
       restore-cbd ctx state))
     ((eq (access-event-tuple-type (cddr (car wrld))) 'include-book)

; Comments similar to those about encapsulate apply to include-book.  We simply
; go to the file named by the include-book and read the events in it, appending
; them to our ans.  Recursive include-books are not flattened here.

      (let ((full-book-name (access-event-tuple-namex (cddr (car wrld)))))
        (er-progn
         (chk-input-object-file full-book-name ctx state)
         (chk-book-name full-book-name full-book-name ctx state)
         (er-let*
          ((ev-lst (read-object-file full-book-name ctx state))
           (cert-obj (chk-certificate-file
                      full-book-name
                      nil
                      'puff
                      ctx
                      state
                      '((:uncertified-okp . t)
                        (:defaxioms-okp t)
                        (:skip-proofs-okp t))
                      nil))
           (expansion-alist
            (value (and cert-obj
                        (access cert-obj cert-obj :expansion-alist)))))
          (let
           ((ev-lst-chk-sum
             (check-sum-cert (and cert-obj
                                  (access cert-obj cert-obj
                                          :cmds))
                             expansion-alist
                             ev-lst)))
           (cond
            ((not (integerp ev-lst-chk-sum))

; This error should never arise because check-sum-obj is only called on
; something produced by read-object, which checks that the object is ACL2
; compatible.  And if it somehow did happen, it is presumably not because of
; the expansion-alist, which must be well-formed since it is in the book's
; certificate.

             (er soft ctx
                 "The file ~x0 is not a legal list of embedded event forms ~
                  because it contains an object, ~x1, which check sum was ~
                  unable to handle."
                 full-book-name ev-lst-chk-sum))
            (t (let ((temp (assoc-equal full-book-name
                                        (global-val 'include-book-alist
                                                    (w state)))))

; Temp is of the form (full-book-name user-book-name familiar-name
; cert-annotations . ev-lst-chk-sum).

                 (cond
                  ((and (cddddr temp)
                        (not (equal ev-lst-chk-sum (cddddr temp))))
                   (er soft ctx
                       "When the certified book ~x0 was included, its check ~
                        sum was ~x1.  The check sum for ~x0 is now ~x2.  The ~
                        file has thus been modified since it was last ~
                        included and we cannot now recover the events that ~
                        created the current logical world."
                       full-book-name
                       (cdddr temp)
                       ev-lst-chk-sum))
                  (t (puff-command-block
                      (scan-past-deeper-event-landmarks
                       (access-event-tuple-depth (cddr (car wrld)))
                       (cdr wrld))
                      (append (cons `(set-cbd
                                      ,(remove-after-last-directory-separator
                                        full-book-name))
                                    (cons (assert$
                                           (and (consp (car ev-lst))
                                                (eq (caar ev-lst) 'in-package))
                                           (car ev-lst))
                                          (subst-by-position expansion-alist
                                                             (cdr ev-lst)
                                                             1)))
                              `((maybe-install-acl2-defaults-table
                                 ',(table-alist 'acl2-defaults-table wrld)
                                 state))
                              ans)
                      t ctx state)))))))))))
     (t (puff-command-block (cdr wrld)
                            (cons (access-event-tuple-form (cddr (car wrld)))
                                  ans)
                            restore-cbd ctx state))))
   (t (puff-command-block (cdr wrld) ans restore-cbd ctx state))))

(defun commands-back-to (wrld1 wrld2 ans)

; Wrld2 is a tail of wrld1.  Each starts with a command-landmark initially.  We
; collect all the non-eviscerated commands back to (but not including) the one
; at wrld2.

  (cond
   ((equal wrld1 wrld2) ans)
   ((and (eq (car (car wrld1)) 'command-landmark)
         (eq (cadr (car wrld1)) 'global-value))
    (commands-back-to (cdr wrld1) wrld2
                      (cons (access-command-tuple-form (cddr (car wrld1)))
                            ans)))
   (t (commands-back-to (cdr wrld1) wrld2 ans))))

(defun puffed-command-sequence (cd ctx wrld state)

; Cd is a command descriptor.  We puff up the command at cd, into the list of
; immediate subevents, and then append to that list the commands in wrld that
; chronologically followed cd.

  (er-let*
   ((cmd-wrld (er-decode-cd cd wrld ctx state)))
   (cond
    ((puffable-command-blockp (cdr cmd-wrld)
                              (access-command-tuple-form (cddr (car cmd-wrld))))
     (er-let*
      ((ans (puff-command-block (cdr cmd-wrld)
                                (commands-back-to wrld cmd-wrld nil)
                                nil ctx state)))
      (value ans)))
    (t (er soft ctx
           "The command at ~x0, namely ~X12, cannot be puffed.  See :DOC puff."
           cd
           (access-command-tuple-form (cddr (car cmd-wrld)))
           ;;; (evisc-tuple 2 3 nil nil)
           '(nil 2 3 nil))))))

(defun puff-fn1 (cd state)

; This function is essentially :puff except that it does no printing.
; It returns a pair, (i . j), where i and j are the relative command numbers
; delineating the region inserted by the puff.  In particular, cd points to
; the command with relative command number i, that command got puffed up,
; and the new commands have the numbers i through j, inclusive.

  (state-global-let*
   ((modifying-include-book-dir-alist

; The Essay on Include-book-dir-alist explains that the above state global must
; be t in order to set the acl2-defaults-table.  The idea is to enforce the
; rule that the acl2-defaults-table is used for the include-book-dir-alist when
; in the ACL2 loop, but state global 'raw-include-book-dir-alist is used
; instead when in raw Lisp (see for example add-include-book-dir-fn).  Here, we
; are presumably evaluating puff or puff* in the loop rather than inside
; include-book, since these are not embedded event forms.  So we need not worry
; about puff being evaluated inside an event inside a book.  (Note that
; make-event is not legal inside a book except with a check-expansion argument
; that is used as the expansion -- re-expansion does not take place.)  Now,
; with raw mode one can in principle call all sorts of ACL2 system functions in
; raw Lisp that we never intended to be called there -- but that requires a
; trust tag, so it's not our problem!

     t))
   (let ((wrld (w state))
         (ctx 'puff))
     (er-let* ((cmd-wrld (er-decode-cd cd wrld :puff state)))
       (cond ((<= (access-command-tuple-number (cddar cmd-wrld))
                  (access command-number-baseline-info
                          (global-val 'command-number-baseline-info wrld)
                          :current))

; See the similar comment in ubt-ubu-fn.

              (cond
               ((<= (access-command-tuple-number (cddar cmd-wrld))
                    (access command-number-baseline-info
                            (global-val 'command-number-baseline-info wrld)
                            :original))
                (er soft :puff
                    "Can't puff a command within the system initialization."))
               (t
                (er soft :puff
                    "Can't puff a command within prehistory.  See :DOC ~
                     reset-prehistory."))))
             (t
              (er-let*
                  ((cmds (puffed-command-sequence cd :puff wrld state)))
                (let* ((pred-wrld (scan-to-command (cdr cmd-wrld)))
                       (i (absolute-to-relative-command-number
                           (max-absolute-command-number cmd-wrld)
                           (w state)))
                       (k (- (absolute-to-relative-command-number
                              (max-absolute-command-number (w state))
                              (w state))
                             i)))
                  (pprogn
                   (set-w 'retraction pred-wrld state)
                   (er-let*
                       ((defpkg-items
                          (defpkg-items
                            (global-val 'known-package-alist cmd-wrld)
                            ctx pred-wrld state)))
                     (er-progn
                      (state-global-let*
                       ((guard-checking-on nil)) ; agree with include-book
                       (ld (append (let ((kpa (global-val
                                               'known-package-alist
                                               pred-wrld)))
                                     (new-defpkg-list defpkg-items kpa kpa))
                                   cmds)
                           :ld-skip-proofsp 'include-book-with-locals
                           :ld-verbose nil
                           :ld-prompt nil
                           :ld-missing-input-ok nil
                           :ld-pre-eval-filter :all
                           :ld-pre-eval-print :never
                           :ld-post-eval-print nil
                           :ld-error-triples t
                           :ld-error-action :error
                           :ld-query-control-alist
                           (cons '(:redef :y)
                                 (ld-query-control-alist state))))
                      (value (cons i
                                   (- (absolute-to-relative-command-number
                                       (max-absolute-command-number (w state))
                                       (w state))
                                      k))))))))))))))

(defun puff-report (caller new-cd1 new-cd2 cd state)
  (cond ((eql new-cd1 (1+ new-cd2))
         (pprogn (io? temporary nil state
                      (caller cd)
                      (fms "Note: ~x0 is complete, but no events were ~
                            executed under the given command descriptor, ~
                            ~x1.~|"
                           (list (cons #\0 caller)
                                 (cons #\1 cd))
                           (standard-co state) state nil))
                 (value :invisible)))
        (t (pcs-fn new-cd1 new-cd2 t state))))

(defun puff-fn (cd state)
  (er-let* ((pair (puff-fn1 cd state)))
           (puff-report :puff (car pair) (cdr pair) cd state)))

(defun puff*-fn11 (ptr k i j state)

; If there is a command whose relative command number, n, is i<=n<=j, then we
; puff the command with the smallest such n.  Then, we iterate, over the
; interval [ptr, max-k], where max is the maximum relative command number in
; the puffed world.  This function must be protected with
; revert-world-on-error.

  (cond
   ((> i j) (value (cons ptr j)))
   ((puffable-command-numberp i state)
    (er-progn
     (puff-fn1 i state)
     (puff*-fn11 ptr k
                 ptr (- (absolute-to-relative-command-number
                         (max-absolute-command-number (w state))
                         (w state))
                        k)
                 state)))
   (t (puff*-fn11 ptr k (1+ i) j state))))

(defun puff*-fn1 (ptr k state)

; Ptr is a relative command number.  K is an integer.  Let max be the maximum
; relative command number in (w state).  We are to recursively puff all the
; commands whose relative command numbers lie between ptr and max-k,
; inclusively.  Thus, for example, if ptr is 12, max is 21 and k is 2, we are
; to puff all the commands that lie in the interval [12, 19].  Observe that
; this means we leave the last k commands of (w state) unpuffed.  Observe that
; every time we puff a command in the interval, max grows (or stays fixed) and
; the width of the region to be puffed grows (weakly).  See the comment in
; puff*-fn for an example.

; We therefore find the first command (the command with the smallest number) in
; the region that is puffable, we puff it, and we iterate.  We stop when no
; command in the region is puffable.  This function uses
; revert-world-on-error because it is possible that the attempt to puff some
; command will cause an error (e.g., because some book's check sum no longer
; agrees with include-book-alist).

  (revert-world-on-error
   (puff*-fn11 ptr k
               ptr
               (- (absolute-to-relative-command-number
                   (max-absolute-command-number (w state))
                   (w state))
                  k)
               state)))

(defun puff*-fn (cd state)
  (let ((wrld (w state)))
    (er-let* ((cmd-wrld (er-decode-cd cd wrld :puff* state)))
             (cond ((<= (access-command-tuple-number (cddar cmd-wrld))
                        (access command-number-baseline-info
                                (global-val 'command-number-baseline-info wrld)
                                :current))

; See the similar comment in ubt-ubu-fn.

                    (cond
                     ((<= (access-command-tuple-number (cddar cmd-wrld))
                          (access command-number-baseline-info
                                  (global-val 'command-number-baseline-info wrld)
                                  :original))
                      (er soft :puff*
                          "Can't puff* a command within the system ~
                           initialization."))
                     (t
                      (er soft :puff*
                          "Can't puff* a command within prehistory.  See :DOC ~
                           reset-prehistory."))))
                   (t
                    (let* ((mx (absolute-to-relative-command-number
                                (max-absolute-command-number wrld)
                                wrld))
                           (ptr (absolute-to-relative-command-number
                                 (max-absolute-command-number cmd-wrld)
                                 wrld))
                           (k (- mx ptr)))
                      (er-let*
                       ((pair (puff*-fn1 ptr k state)))

; The difference between puff and puff* is that puff* iterates puff across the
; region generated by the first puff until there are no more commands that are
; puffable.  Before continuing, we illustrate how we determine the bounds of
; the region in question.  We bound the region with relative command numbers.
; Suppose we are asked to puff* cd, where cd points to relative command number
; 12 below.

; 12   cmd1   ; ptr = 12 = the relative command number indicated by cd
; 13   cmd2
; 14   cmd3   ; mx = latest command

; Then mx, above, will be 14 and ptr will be 12.  Observe that there are two
; commands then that are not part of the region to be puffed, namely commands
; 13 and 14.  Now after puffing once, we will have something like:

; 12   cmd1a
; 13   cmd1b
; ...
; 19   cmd1h
; 20   cmd2
; 21   cmd3

; Observe that the new max command number is 21.  The region to be recursively
; puffed now lies between 12 and 19, inclusive.  The last two commands, now
; numbered 20 and 21, are outside the region.

; Let k be (- mx ptr), i.e., 2 in this example and, in general, the number of
; commands not in the region.  Then in general we should recursively puff
; commands whose numbers are between ptr and (- max k), where max is the
; current maximum relative command number, inclusive.  Initially this region
; contains just one command, the one we are to puff first.  ;

                      (puff-report :puff* (car pair) (cdr pair) cd
                                   state))))))))

(defmacro puff (cd)

  ":Doc-Section History

  replace a compound ~il[command] by its immediate subevents~/
  ~bv[]
  Example Forms:
  ACL2 !>:puff :max
  ACL2 !>:puff :x
  ACL2 !>:puff 15
  ACL2 !>:puff \"book\"~/

  General Form:
  :puff cd
  ~ev[]
  where ~c[cd] is a ~il[command] descriptor (~pl[command-descriptor]) for a
  ``puffable'' ~il[command] (see below).  ~c[Puff] replaces the ~il[command] at
  ~c[cd] by the immediate subevents of the ~il[command], executed as
  ~il[command]s.  ~c[Puff] then prints, using ~ilc[pcs], the ~c[puff]ed region.

  We consider ~c[puff] to be a sort of hack; it is generally robust and sound,
  but that is not guaranteed.  If any existing ACL2 event resulted from
  ~c[puff], ACL2 considers proofs to have been skipped, and thus
  ~ilc[certify-book] is disallowed until such events have been undone
  (~pl[ubt]).

  A ``puffable'' ~il[command] is an ~ilc[encapsulate] ~il[command], an
  ~ilc[include-book] ~il[command], or any ~il[command] other than those
  consisting of a single primitive event.  For example, since ~ilc[defun] is a
  primitive event, a ~ilc[defun] ~il[command] is not puffable.  But a macro
  form that expands into several ~ilc[defun] ~il[events] is puffable.  The only
  primitive ~il[events] that are puffable are calls of ~ilc[encapsulate] or
  ~ilc[include-book].  A puffable ~il[command] contains (interesting)
  subevents, namely, the ~il[events] in the body of the ~ilc[encapsulate], in
  the file of the book included, or in the ~il[command] block.

  (Obscure exceptions.  (1) An ~ilc[encapsulate] command generated by the macro
  ~ilc[define-trusted-clause-processor] is not puffable.  (2) If a use of
  ~ilc[make-event] results in ~ilc[local] ~il[events] in the scope of an
  ~ilc[encapsulate] or ~ilc[include-book] event within a command, then an
  attempt to ~c[puff] that command will result in an error, leaving the
  ~il[world] unchanged, if any such ~ilc[local] event was necessary to support
  other events in the command.  (3) An attempt to ~c[puff] an ~c[include-book]
  command may fail for a book that has been modified, as describe late in this
  documentation topic.)

  The puff ~il[command] ``lifts'' the immediate subevents of the indicated
  ~il[command] so that they become ~il[command]s themselves.  The ~il[command]
  ~ilc[puff*] recursively puffs the newly introduced ~il[command]s.  ~l[puff*],
  which also gives an example illustrating both ~c[puff] and ~ilc[puff*].
  ~c[Puff] undoes the ~il[command] at ~c[cd] and replaces it by its immediate
  subevents.  Thus, in general the length of the ~il[history] grows when a puff
  ~il[command] is executed.  If ~c[puff] causes an error (see below), the
  logical ~il[world] remains unchanged from its initial configuration.

  The intended use of ~c[puff] is to allow the user access to the ~il[events]
  ``hidden'' inside compound ~il[command]s.  For example, while trying to prove
  some theorem, ~c[p], about a constrained function, ~c[fn], one might find
  that the ~ilc[encapsulate], ~c[cd], that introduced ~c[fn] failed to include
  an important ~il[constraint], ~c[q].  Without ~c[puff], the only way to
  proceed is to undo back through ~c[cd], create a suitable ~ilc[encapsulate]
  that proves and exports ~c[q] as well as the old ~il[constraint]s, re-execute
  the new ~ilc[encapsulate], re-execute the ~il[events] since ~c[cd], and then
  try ~c[p] again.  Unfortunately, it may be hard to prove ~c[q] and additional
  ~il[events] may have to be inserted into the ~ilc[encapsulate] to prove it.
  It may also be hard to formulate the ``right'' ~c[q], i.e., one that is
  provable in the ~ilc[encapsulate] and provides the appropriate facts for use
  in the proof of ~c[p].

  Using ~c[puff], the user can erase the ~ilc[encapsulate] at ~c[cd], replacing
  it by the ~il[events] in its body.  Now the formerly constrained function,
  ~c[fn], is defined as its witness.  The user can experiment with formulations
  and proofs of ~c[q] suitable for ~c[p].  Of course, to get into the
  ultimately desired ~il[state] ~-[] where ~c[fn] is constrained rather than
  defined and ~c[q] is exported by an ~ilc[encapsulate] at ~c[cd] ~-[] the user
  must ultimately undo back to ~c[cd] and carry out the more tedious program
  described above.  But by using ~c[puff] it is easier to experiment.

  Similar applications of ~c[puff] allow the user of a book to expose the
  innards of the book as though they had all be typed as ~il[command]s.  The
  user might then ``partially undo'' the book, keeping only some of the
  ~il[events] in it.

  ~c[Puff] operates as follows.  First, it determines the list of immediate
  subevents of the ~il[command] indicated by ~c[cd].  It causes an error if
  there is only one subevent and that subevent is identical to the ~il[command]
  ~-[] i.e., if the ~il[command] at ~c[cd] is a primitive.  Next, ~c[puff]
  undoes back through the indicated ~il[command].  This not only erases the
  ~il[command] at ~c[cd] but all the ~il[command]s executed after it.  Finally,
  ~c[puff] re-executes the subevents of (the now erased) ~c[cd] followed by all
  the ~il[command]s that were executed afterwards.

  Observe that the ~il[command]s executed after ~c[cd] will generally have
  higher ~il[command] numbers than they did before the puff.  For example,
  suppose 100 ~il[command]s have been executed and that ~c[:puff 80] is then
  executed.  Suppose ~il[command] 80 contains 5 immediate subevents (i.e., is
  an encapsulation of five ~il[events]).  Then, after puffing, ~il[command] 80
  is the first event of the puffed ~il[command], ~il[command] 81 is the second,
  and so on; 104 ~il[command]s appear to have been executed.

  When puffing an ~ilc[encapsulate] or ~ilc[include-book], the ~ilc[local]
  ~il[command]s are executed.  Note that this will replace constrained
  functions by their witnesses.

  Finally, it is impossible to ~c[puff] in the presence of ~ilc[include-book]
  ~il[events] for certified books that have been altered since they were
  included.  (Note that this restriction only applies to ~ilc[include-book],
  not to ~ilc[certify-book].)  To be specific, suppose ~c[\"arith\"] is a
  certified book that has been included in a session.  Suppose that after
  ~c[\"arith\"] was included, the source file is modified.  (This might happen
  if the user of ~c[\"arith\"] is not its author and the author happens to be
  working on a new version of ~c[\"arith\"] during the same time period.)  Now
  suppose the user tries to ~c[puff] the ~il[command] that included
  ~c[\"arith\"].  The attempt to obtain the subevents in ~c[\"arith\"] will
  discover that the check sum of ~c[\"arith\"] has changed and an error will be
  caused.  No change is made in the logical ~il[world].  A similar error is
  caused if, in this same situation, the user tries to puff any command that
  occurred before the inclusion of ~c[\"arith\"]!  That is, ~c[puff] may cause
  an error and leave the ~il[world] unchanged even if the ~il[command] puffed
  is not one involving the modified book.  This happens because in order to
  reconstruct the ~il[world] after the puffed ~il[command], ~c[puff] must
  obtain the ~il[events] in the book and if the book's source file has changed
  there is no assurance that the reconstructed ~il[world] is the one the user
  intends.

  Warning: We do not detect changes to uncertified ~il[books] that have been
  included and are then puffed or re-included!  The act of including an
  uncertified book leaves no trace of the check sum of the book.  Furthermore,
  the act prints a warning message disclaiming soundness.  In light of this,
  ~c[:puff] quietly ``re-''executes the current contents of the book."

  `(puff-fn ,cd state))

(defmacro puff* (cd)

  ":Doc-Section History

  replace a compound ~il[command] by its subevents~/
  ~bv[]
  Example Forms:
  ACL2 !>:puff* :max
  ACL2 !>:puff* :x
  ACL2 !>:puff* 15
  ACL2 !>:puff* \"book\"~/

  General Form:
  :puff* cd
  ~ev[]
  where ~c[cd] is a ~il[command] descriptor (~pl[command-descriptor]) for a
  ``puffable'' ~il[command].  ~l[puff] for the definition of ``puffable'' and
  for a description of the basic act of ``puffing'' a ~il[command].  In
  particular, ~pl[puff] for a discussion of a sense in which ~c[puff], and
  hence ~c[puff*], should be viewed as a hack.  ~c[Puff*] is just the recursive
  application of ~il[puff].  ~c[Puff*] prints the region ~il[puff]ed, using
  ~ilc[pcs].

  To ~il[puff] a ~il[command] is to replace it by its immediate subevents, each
  of which is executed as a ~il[command].  To ~c[puff*] a ~il[command] is to replace
  the ~il[command] by each of its immediate subevents and then to ~c[puff*]
  each of the puffable ~il[command]s among the newly introduced ones.

  For example, suppose ~c[\"ab\"] is a book containing the following
  ~bv[]
  (in-package \"ACL2\")
  (include-book \"a\")
  (include-book \"b\")
  ~ev[]
  Suppose that book ~c[\"a\"] only contained ~ilc[defun]s for the functions ~c[a1]
  and ~c[a2] and that ~c[\"b\"] only contained ~ilc[defun]s for ~c[b1] and ~c[b2].

  Now consider an ACL2 ~il[state] in which only two ~il[command]s have been
  executed, the first being ~c[(include-book \"ab\")] and the second being
  ~c[(include-book \"c\")].  Thus, the relevant part of the display
  produced by ~c[:]~ilc[pbt] 1 would be:
  ~bv[]
  1 (INCLUDE-BOOK \"ab\")
  2 (INCLUDE-BOOK \"c\")
  ~ev[]
  Call this ~il[state] the ``starting ~il[state]'' in this example, because we
  will refer to it several times.

  Suppose ~c[:puff 1] is executed in the starting ~il[state].  Then the first
  ~il[command] is replaced by its immediate subevents and ~c[:pbt 1] would
  show:
  ~bv[]
  1 (INCLUDE-BOOK \"a\")
  2 (INCLUDE-BOOK \"b\")
  3 (INCLUDE-BOOK \"c\")
  ~ev[]
  Contrast this with the execution of ~c[:puff* 1] in the starting
  ~il[state].  ~c[Puff*] would first ~il[puff] ~c[(include-book \"ab\")] to get the
  ~il[state] shown above.  But then it would recursively ~c[puff*] the puffable
  ~il[command]s introduced by the first ~il[puff].  This continues recursively
  as long as any ~il[puff] introduced a puffable ~il[command].  The end result
  of ~c[:puff* 1] in the starting ~il[state] is
  ~bv[]
  1 (DEFUN A1 ...)
  2 (DEFUN A2 ...)
  3 (DEFUN B1 ...)
  4 (DEFUN B2 ...)
  5 (INCLUDE-BOOK \"c\")
  ~ev[]
  Observe that when ~c[puff*] is done, the originally indicated ~il[command],
  ~c[(include-book \"ab\")], has been replaced by the corresponding
  sequence of primitive ~il[events].  Observe also that puffable ~il[command]s
  elsewhere in the ~il[history], for example, ~il[command] 2 in the starting
  ~il[state], are not affected (except that their ~il[command] numbers grow as a
  result of the splicing in of earlier ~il[command]s)."

 `(puff*-fn ,cd state))

(defmacro mini-proveall nil

; ACL2 (a)>:mini-proveall

; will change the default-defun-mode to :logic and do a short proveall.  The
; final defun-mode will be :logic.

  '(ld
    '(:logic

; We start with a nice example of forcing, involving primitive fns.

      (thm (implies (and (true-listp x)
                         (true-listp y))
                    (equal (revappend (append x y) z)
                           (revappend y (revappend x z)))))
      (defun app (x y)
        (if (consp x)
            (cons (car x) (app (cdr x) y))
            y))
      (defthm assoc-of-app
        (equal (app (app a b) c) (app a (app b c))))
      (defun rev (x)
        (if (consp x)
            (app (rev (cdr x)) (cons (car x) nil))
            nil))
      (defthm true-listp-rev
        (true-listp (rev x))
        :rule-classes (:REWRITE :GENERALIZE))

; Here we test the proof-checker using the same theorem as the one that
; follows (but not storing it as a :rewrite rule).

      (defthm rev-app-proof-checker
        (equal (rev (app a b)) (app (rev b) (rev a)))
        :rule-classes nil
        :instructions
        (:induct :bash :induct :bash :split (:dv 1)
                 :x :nx (:dv 1)
                 :x :top :s :bash (:dive 1 1)
                 := (:drop 2)
                 :top :bash))
      (defthm rev-app
        (equal (rev (app a b)) (app (rev b) (rev a))))
      (defthm rev-rev
        (implies (true-listp x) (equal (rev (rev x)) x)))

;    The following events are the big example in deflabel equivalence.

      (encapsulate (((lt * *) => *))
                   (local (defun lt (x y) (declare (ignore x y)) nil))
                   (defthm lt-non-symmetric (implies (lt x y) (not (lt y x)))))

      (defun insert (x lst)
        (cond ((atom lst) (list x))
              ((lt x (car lst)) (cons x lst))
              (t (cons (car lst) (insert x (cdr lst))))))

      (defun insert-sort (lst)
        (cond ((atom lst) nil)
              (t (insert (car lst) (insert-sort (cdr lst))))))

      (defun del (x lst)
        (cond ((atom lst) nil)
              ((equal x (car lst)) (cdr lst))
              (t (cons (car lst) (del x (cdr lst))))))

      (defun mem (x lst)
        (cond ((atom lst) nil)
              ((equal x (car lst)) t)
              (t (mem x (cdr lst)))))

      (defun perm (lst1 lst2)
        (cond ((atom lst1) (atom lst2))
              ((mem (car lst1) lst2)
               (perm (cdr lst1) (del (car lst1) lst2)))
              (t nil)))

      (defthm perm-reflexive
        (perm x x))

      (defthm perm-cons
        (implies (mem a x)
                 (equal (perm x (cons a y))
                        (perm (del a x) y)))
        :hints (("Goal" :induct (perm x y))))

      (defthm perm-symmetric
        (implies (perm x y) (perm y x)))

      (defthm mem-del
        (implies (mem a (del b x)) (mem a x))
        :rule-classes ((:rewrite :match-free :once)))

      (defthm perm-mem
        (implies (and (perm x y)
                      (mem a x))
                 (mem a y))
        :rule-classes ((:rewrite :match-free :once)))

      (defthm mem-del2
        (implies (and (mem a x)
                      (not (equal a b)))
                 (mem a (del b x))))

      (defthm comm-del
        (equal (del a (del b x)) (del b (del a x))))

      (defthm perm-del
        (implies (perm x y)
                 (perm (del a x) (del a y))))

      (defthm perm-transitive
        (implies (and (perm x y) (perm y z)) (perm x z))
        :rule-classes ((:rewrite :match-free :once)))

      (defequiv perm)

      (in-theory (disable perm perm-reflexive perm-symmetric perm-transitive))

      (defcong perm perm (cons x y) 2)

      (defcong perm iff (mem x y) 2)

      (defthm atom-perm
        (implies (not (consp x)) (perm x nil))
        :rule-classes :forward-chaining
        :hints (("Goal" :in-theory (enable perm))))

      (defthm insert-is-cons
        (perm (insert a x) (cons a x)))

      (defthm insert-sort-is-id
        (perm (insert-sort x) x))

      (defcong perm perm (app x y) 2)

      (defthm app-cons
        (perm (app a (cons b c)) (cons b (app a c))))

      (defthm app-commutes
        (perm (app a b) (app b a)))

      (defcong perm perm (app x y) 1 :hints (("Goal" :induct (app y x))))

      (defthm rev-is-id (perm (rev x) x))

      (defun == (x y)
        (if (consp x)
            (if (consp y)
                (and (equal (car x) (car y))
                     (== (cdr x) (cdr y)))
                nil)
            (not (consp y))))

      (defthm ==-symmetric (== x x))

      (defthm ==-reflexive (implies (== x y) (== y x)))

      (defequiv ==)

      (in-theory (disable ==-symmetric ==-reflexive))

      (defcong == == (cons x y) 2)

      (defcong == iff (consp x) 1)

      (defcong == == (app x y) 2)

      (defcong == == (app x y) 1)

      (defthm rev-rev-again (== (rev (rev x)) x))

; This next block tests forcing.

      (defun ends-in-a-0 (x)
        (declare (xargs :guard t))
        (if (consp x) (ends-in-a-0 (cdr x)) (equal x 0)))

      (defun app0 (x y)
        (declare (xargs :guard (ends-in-a-0 x)))
        (if (ends-in-a-0 x)
            (if (equal x 0) y (cons (car x) (app0 (cdr x) y)))
            'default))

      (defun rev0 (x)
        (declare (xargs :guard (ends-in-a-0 x)))
        (if (ends-in-a-0 x)
            (if (equal x 0) 0 (app0 (rev0 (cdr x)) (cons (car x) 0)))
            'default))

      (defthm app0-right-id
        (implies (force (ends-in-a-0 x)) (equal (app0 x 0) x)))

      (defun ends-in-a-zero (x) (ends-in-a-0 x))

      (defthm ends-in-a-zero-app0
        (implies (force (ends-in-a-zero x)) (ends-in-a-0 (app0 x (cons y 0)))))

      (in-theory (disable ends-in-a-zero))

; The following theorem causes two forcing rounds.  In the first, there
; are three goals, all variants of one another.  An inductive proof of one
; of them is done and generates the second forcing round.

      (defthm force-test
        (and (implies (ends-in-a-0 x) (equal (app0 (rev0 x) 0) (rev0 x)))
             (implies (ends-in-a-0 y) (equal (app0 (rev0 y) 0) (rev0 y)))
             (implies (ends-in-a-0 z) (equal (app0 (rev0 z) 0) (rev0 z))))
        :hints (("[2]Goal" :in-theory (enable ends-in-a-zero))))

; This defun does a lot of proving for both termination and guard verification.

      (defun proper-cons-nest-p (x)
        (declare (xargs :guard (pseudo-termp x)))
        (cond ((symbolp x) nil)
              ((fquotep x) (true-listp (cadr x)))
              ((eq (ffn-symb x) 'cons)
               (proper-cons-nest-p (fargn x 2)))
              (t nil)))

; This defthm has two forcing rounds and is very realistic.

      (defthm ordered-symbol-alistp-delete-assoc-eq-test
        (implies (and (ordered-symbol-alistp l)
                      (symbolp key)
                      (assoc-eq key l))
                 (ordered-symbol-alistp (delete-assoc-eq key l)))
        :hints (("Goal" :in-theory (disable ordered-symbol-alistp-delete-assoc-eq))))

      (value-triple "Mini-proveall completed successfully.")

      )
    :ld-skip-proofsp nil
    :ld-redefinition-action nil
    :ld-pre-eval-print t
    :ld-error-action :return!))

(defmacro exit (&optional (status '0))

  ":Doc-Section Other

  quit entirely out of Lisp~/

  Same as ~ilc[good-bye].~/~/"

  (declare (xargs :guard (natp status)))
  `(good-bye-fn ,status))

(defmacro quit (&optional (status '0))

  ":Doc-Section Other

  quit entirely out of Lisp~/

  Same as ~ilc[good-bye].~/~/"

  (declare (xargs :guard (natp status)))
  `(good-bye-fn ,status))

(defmacro set-guard-checking (flg)
  (declare (xargs :guard
                  (let ((flg (if (and (consp flg)
                                      (eq (car flg) 'quote)
                                      (consp (cdr flg)))
                                 (cadr flg)
                               flg)))
                    (member-eq flg *guard-checking-values*))))

  ":Doc-Section switches-parameters-and-modes

  control checking ~il[guard]s during execution of top-level forms~/

  Detailed comments about the arguments of this function may be found
  elsewhere: ~pl[guard-evaluation-table].  Here we provide an introduction to
  the use of ~c[set-guard-checking].

  New users are encouraged to execute one of the following forms in order to
  avoid evaluation errors due to ~il[guard]s:
  ~bv[]
  (set-guard-checking :none)
  (set-guard-checking nil)
  ~ev[]
  The former avoids all guard-checking on user-defined functions and should
  generally work fine for new users, the only drawback being efficiency loss on
  compute-intensive problems.  All settings other than ~c[:none] check guards,
  but a value of ~c[nil] allows evaluation to continue in the logic when guards
  fail (avoiding the raw Lisp definition in that case).

  You may put one of the above forms in the ~c[\"acl2-customization.lsp\"]
  file in your current directory (~pl[cbd]) or your home directory;
  ~pl[acl2-customization].

  Note that ~il[guard]s are not part of the ACL2 logic, and hence new users can
  completely ignore the notion of ~il[guard] (and the rest of this
  documentation section after this paragraph!).  For example, ~c[(car 3)] and
  ~c[nil] can be proved equal in the ACL2 logic, as follows, even though the
  ~il[guard] on ~ilc[car] requires its first argument to be a ~ilc[cons] pair
  or ~c[nil].
  ~bv[]
  (thm (equal (car 3) nil))
  ~ev[]
  Moreover, unless your functions or top-level forms call built-in ACL2
  functions that are defined in ~c[:]~ilc[program] mode, the following property
  will hold.
  ~bq[]
  Evaluation of ~c[(set-guard-checking :none)] will allow evaluation of forms
  such as ~c[(car 3)] to take place without error in the top level loop, not
  only when proving theorems.
  ~eq[]

  If you feel bold, then you may wish to read the rest of this
  documentation topic; also ~pl[guard].

  ~l[guard-evaluation-table] for a succinct table, with associated discussion,
  that covers in detail the material presented in the rest of the present
  topic.~/

  The top-level ACL2 loop has a variable which controls which sense of
  execution is provided.  To turn ``~il[guard] checking on,'' by which we mean
  that ~il[guard]s are checked at runtime, execute the top-level form
  ~c[:set-guard-checking t].  To allow guard violations, do
  ~c[:set-guard-checking nil], or do ~c[:set-guard-checking :none] to turn off
  all guard-checking, so that raw Lisp definitions of user-defined functions
  are avoided unless their ~il[guard] is ~c[t]. The status of guard-checking is
  reflected in the ~il[prompt].
  ~bv[]
  ACL2 !>
  ~ev[]
  means ~il[guard] checking is on and
  ~bv[]
  ACL2 >
  ~ev[]
  means ~il[guard] checking is off.  The exclamation mark can be thought of
  as ``barring'' certain computations.  The absence of the mark
  suggests the absence of error messages or unbarred access to the
  logical axioms.  Thus, for example
  ~bv[]
  ACL2 !>(car 'abc)
  ~ev[]
  will signal an error, while
  ~bv[]
  ACL2 >(car 'abc)
  ~ev[]
  will return ~c[nil].

  We will return at the end of this documentation topic to discuss two other
  values, ~c[:all] and ~c[:nowarn], for ~c[:set-guard-checking].  We also note
  that evaluation of built-in ~c[:program] mode functions always takes place in
  raw Lisp.

  Whether ~il[guard]s are checked during evaluation is independent of the
  ~ilc[default-defun-mode].  We note this simply because it is easy to
  confuse ``~c[:]~ilc[program] mode'' with ``evaluation in Common Lisp'' and
  thus with ``~il[guard] checking on;'' and it is easy to confuse
  ``~c[:]~ilc[logic] mode'' with ``evaluation in the logic'' and with ``~il[guard]
  checking off.''  But the ~ilc[default-defun-mode] determines whether
  newly submitted definitions introduce programs or add logical
  axioms.  That mode is independent of whether evaluation checks
  ~il[guard]s or not.  You can operate in ~c[:]~ilc[logic] mode with runtime ~il[guard]
  checking on or off.  Analogously, you can operate in ~c[:]~ilc[program]
  mode with runtime ~il[guard] checking on or off.

  For further discussion on evaluation and guards ~pl[guards-and-evaluation],
  in particular the exception for safe-mode in the ``Aside'' there.  ~l[guard]
  for a general discussion of ~il[guard]s.

  Now we fulfill our promise above to discuss two other values for
  ~c[:set-guard-checking]:
  ~bv[]
  :set-guard-checking :nowarn
  :set-guard-checking :all
  ~ev[]
  The meaning of these values is perhaps best described by the following
  example provided by David Rager.
  ~bv[]
  ACL2 !>(defun my-test (expr)
           (declare (xargs :guard (true-listp expr)
                           :verify-guards nil))
           (if (atom expr)
               expr
             (cons (my-test (car expr))
                   (my-test (cdr expr)))))

  The admission of MY-TEST is trivial, using the relation O< (which is
  known to be well-founded on the domain recognized by O-P) and the measure
  (ACL2-COUNT EXPR).  We could deduce no constraints on the type of MY-
  TEST.  However, in normalizing the definition we used primitive type
  reasoning.

  Summary
  Form:  ( DEFUN MY-TEST ...)
  Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
  Warnings:  None
  Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
   MY-TEST
  ACL2 !>(my-test '(a b c))

  ACL2 Warning [Guards] in TOP-LEVEL:  Guard-checking will be inhibited
  on recursive calls of the executable counterpart (i.e., in the ACL2
  logic) of MY-TEST.  To check guards on all recursive calls:
    (set-guard-checking :all)
  To leave behavior unchanged except for inhibiting this message:
    (set-guard-checking :nowarn)

  (A B C)
  ACL2 !>
  ~ev[]
  If you think about evaluation of ~c[(my-test '(a b c))], you will see that it
  leads to the recursive call ~c[(my-test 'a)], which one might expect to cause
  a guard violation since the symbol ~c[a] is not a ~ilc[true-listp].  However,
  as the warning above explains, we do not by default check guards on recursive
  calls.  The reason is efficiency ~-[] imagine a simple definition with a
  guard that is slow to evaluate.  The values ~c[:nowarn] and ~c[:all] for
  ~c[:set-guard-checking] have been introduced as ways of dealing with the
  above warning.  The value ~c[:nowarn] simply turns off the warning above.
  The value ~c[:all] causes all guards to be checked, even on recursive calls
  and even on all calls of non-built-in ~c[:]~ilc[program] mode functions ~-[]
  unless, of course, a call is made of a function whose guard has been verified
  (~pl[verify-guards]), where the arguments satisfy the guard, in which case
  the corresponding call is made in raw Lisp without subsidiary guard-checking.
  We still say that ``guard-checking is on'' after ~c[:set-guard-checking] is
  invoked with values ~c[t], ~c[:nowarn], and ~c[:all], otherwise (after value
  ~c[nil]) we say ``guard-checking is off.

  For technical reasons, ~c[:all] does not have its advertised effect in the
  case of built-in ~c[:]~ilc[program]-mode functions.  If you are interested in
  this technical detail, see the comment ``In the boot-strap world...'' in
  source function ~c[oneify-cltl-code].

  We conclude with a remark about the use of ~c[:set-guard-checking] for
  experimenting with ACL2 as a logic or as a programming language.  If one
  views ACL2 as a logic, one may wish to use ~c[:set-guard-checking :none],
  while if instead one views ACL2 as a functional programming language, one may
  wish to use ~c[:set-guard-checking :all].  The following transcript
  illustrates this distinction by way of example.  Specifically, ~c[(car 3)] is
  equal to ~c[nil] in the ACL2 logic, but may be viewed as a programming
  error.  The default of ~c[:set-guard-checking t] is problematic for learning
  ACL2 using ~c[:]~ilc[program] mode functions, since one can get raw Lisp
  errors.  In the example below, the raw Lisp error occurs because ~c[foo]
  implicitly has a ~il[guard] of ~c[t], hence ~c[(foo 3)] is evaluated in raw
  Lisp, which leads to a raw Lisp call of c[(car 3)].
  ~bv[]
  ACL2 !>(defun foo (x)
           (declare (xargs :mode :program))
           (car x))

  Summary
  Form:  ( DEFUN FOO ...)
  Rules: NIL
  Warnings:  None
  Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
   FOO
  ACL2 !>(foo 3)
  Error: Attempt to take the car of 3 which is not listp.
    [condition type: TYPE-ERROR]

  Restart actions (select using :continue):
   0: Abort entirely from this (lisp) process.
  [Current process: Initial Lisp Listener]
  [1] ACL2(1): [RAW LISP] :pop
  ACL2 !>:set-guard-checking :none

  Turning off guard checking entirely.  To allow execution in raw Lisp
  for functions with guards other than T, while continuing to mask guard
  violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.

  ACL2 >(foo 3)
  NIL
  ACL2 >:set-guard-checking :all

  Turning guard checking on, value :ALL.

  ACL2 !>(foo 3)


  ACL2 Error in TOP-LEVEL:  The guard for the function symbol CAR, which
  is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
  call (CAR 3).  See :DOC trace for a useful debugging utility.  See :DOC
  set-guard-checking for information about suppressing this check with
  (set-guard-checking :none), as recommended for new users.

  ACL2 !>
  ~ev[]~/

  :cited-by guard"

  `(let ((current-flg (f-get-global 'guard-checking-on state))
         (flg ,(if (and (consp flg) (eq (car flg) 'quote) (consp (cdr flg)))
                   (cadr flg)
                 flg)))
     (cond
      ((and (raw-mode-p state) flg)
       (er soft 'set-guard-checking
           "It is illegal (and anyhow, would be useless) to attempt to modify ~
            guard checking while in raw mode, since guards are not checked in ~
            raw mode."))
      ((eq flg current-flg)
       (pprogn
        (fms "Guard-checking-on already has value ~x0.~%~%"
             (list (cons #\0 flg))
             *standard-co* state nil)
        (value :invisible)))
      ((null flg)
       (pprogn (f-put-global 'guard-checking-on nil state)
               (fms "Masking guard violations but still checking guards ~
                     except for self-recursive calls.  To avoid guard ~
                     checking entirely, :SET-GUARD-CHECKING :NONE.  See :DOC ~
                     set-guard-checking.~%~%"
                    nil *standard-co* state nil)
               (value :invisible)))
      ((eq flg :none)
       (pprogn (f-put-global 'guard-checking-on :none state)
               (fms "Turning off guard checking entirely.  To allow execution ~
                     in raw Lisp for functions with guards other than T, ~
                     while continuing to mask guard violations, ~
                     :SET-GUARD-CHECKING NIL.  See :DOC ~
                     set-guard-checking.~%~%"
                    nil *standard-co* state nil)
               (value :invisible)))
      (t (pprogn
          (f-put-global 'guard-checking-on flg state)
          (assert$ (and flg (not (eq flg current-flg)))
                   (cond ((member-eq current-flg '(nil :none))
                          (fms "Turning guard checking on, value ~x0.~%~%"
                               (list (cons #\0 flg))
                               *standard-co* state nil))
                         (t
                          (fms "Leaving guard checking on, but changing value ~
                                to ~x0.~%~%"
                               (list (cons #\0 flg))
                               *standard-co* state nil))))
          (value :invisible))))))

; Next: dmr

(defun dmr-stop-fn (state)
  (declare (xargs :guard (state-p state)))
  (let ((dmrp (f-get-global 'dmrp state)))
    (cond (dmrp #-acl2-loop-only
                (dmr-stop-fn-raw)
                (pprogn (f-put-global 'dmrp nil state)
                        (if (consp dmrp)
                            (set-debugger-enable-fn (car dmrp) state)
                          state)))
          (t (observation 'dmr-stop
                          "Skipping dmr-stop (dmr is already stopped).")))))

(defmacro dmr-stop ()
  '(dmr-stop-fn #+acl2-loop-only state
                #-acl2-loop-only *the-live-state*))

(defun dmr-start-fn (state)
  (declare (xargs :guard (state-p state)))
  (cond ((f-get-global 'dmrp state)
         (observation 'dmr-start
                      "Skipping dmr-start (dmr is already started)."))
        (t (let* ((old-debugger-enable (f-get-global 'debugger-enable state))
                  (new-debugger-enable ; for interactive use of dmr-flush
                   (case old-debugger-enable
                     ((nil) t)
                     (:bt :break-bt))))
             (pprogn
              (if new-debugger-enable
                  (set-debugger-enable-fn new-debugger-enable state)
                state)
              #-acl2-loop-only
              (dmr-start-fn-raw state)
              (f-put-global 'dmrp
                            (if new-debugger-enable
                                (list old-debugger-enable)
                              t)
                            state))))))

(defmacro dmr-start ()
  '(dmr-start-fn #+acl2-loop-only state
                 #-acl2-loop-only *the-live-state*))

(defconst *home-page*

; The numeric fmt variables used in the home page are resolved as follows:
; 0 (@ acl2-version)
; 1 "acl2-doc-major-topics.html"
; 2 "acl2-doc-index.html"
; 3 version subdirectory, e.g., "v2-1" for ACL2 Version 2.1
; 4 build date month, e.g., "January"
; 5 build date day, e.g., 8
; 6 build date year, e.g., 1998
; 7 HREF for the Warning message explanation, e.g., "acl2-doc-47.html#Tiny..."

; These variables are set in write-home-page in doc/write-acl2-html.lisp.

; Alphabetic fmt variables used below are defined in the defconst for
; *home-page-references*, immediately following the one for
; *home-page*.

          "~


~s0



\"ACL2\"

~s0

ACL2 is both a programming language in which you can model computer systems and a tool to help you prove properties of those models.

ACL2 is part of the Boyer-Moore family of provers, for which its authors have received the 2005 ACM Software System Award.

SEARCH


Start Here: Applications, Tours, and Tutorials/Demos ACL2 Workshops, UT Seminar, and Course Materials
Publications about ACL2 and Its Applications The User's Manual and Hyper-Card
Community Books: Lemma Libraries and Utilities Mailing Lists
Recent changes to this page Obtaining, Installing, and License
Differences from Version 6.2 Other Releases
How to contribute libraries and documentation

Matt Kaufmann and J Strother Moore
University of Texas at Austin
~s4 ~f5, ~f6


Libraries of books (files containing definitions and theorems) extend the code that we have written. These community books are contributed and maintained by the members of the ACL2 community; see the acl2-books project page.

The libraries and the ACL2 source code are under revision control, using svn. Experimental copies of ACL2 and the libraries are thus available between ACL2 releases. The authors of ACL2 consider svn distributions to be experimental; while they will likely be fully functional in most cases, they could however be incomplete, fragile, and unable to pass our own regression. If you decide to use svn versions of either the libraries or ACL2, then you should use both, as they tend to be kept in sync. See the project websites, acl2-books and acl2-devel, for the ACL2 libraries and development sources, respectively.

A combined manual, known as the xdoc manual, incorporates not only The User's Manual for ACL2 (with some topics rearranged) but also documentation for many books. Thanks to Jared Davis for building the xdoc processor for creating this view of the documentation.

The ACL2 distribution includes the following extensions, which were contributed by the individuals shown.

  • ACL2(r)
    Support for the real numbers by way of non-standard analysis
    Ruben Gamboa
  • ACL2(h)
    Support for hash cons, applicative hash tables, and function memoization for reuse of previously computed results
    Bob Boyer, Warren A. Hunt, Jr., Jared Davis, and Sol Swords
  • ACL2(p)
    Support for parallel evaluation
    David L. Rager

Another extension of ACL2 is the Eclipse-based ACL2 Sedan (ACL2s). Unlike the systems above, ACL2s is distributed and maintained by Pete Manolios and his research group. ACL2s comes with a standard executable ACL2 image for Windows, but it also comes with pre-certified community books and an extension of ACL2 with additional features, including extra automation for termination proofs as well as counterexample generation.



We gratefully acknowledge substantial support from the following. (These are included in a much more complete acknowledgments page.)
  • DARPA
  • National Science Foundation
    • This material is based upon work supported by the National Science Foundation under Grant Nos. EIA-0303609, CNS-0429591, ISS-0417413, CCF-0945316, and CNS-0910913.
    • Any opinions, findings and conclusions or recomendations expressed in this material are those of the authors and do not necessarily reflect the views of the National Science Foundation.
  • Advanced Micro Devices, Inc.
  • ForrestHunt, Inc.
  • Rockwell Collins, Inc.
  • Sun Microsystems, Inc.

The User's Manual

ACL2's user manual is a vast hypertext document. You can wander through it here, in its HTML format.

Here are the two common entries to the documentation graph:
Major Topics (Table of Contents)
Index of all documented topics
The tiny warning signs, , indicate that the links lead out of introductory level material and into reference manual material. Once in the reference manual, virtually all links are into the manual and none of them is marked with the warning sign! It is easy for the newcomer to get lost.

Here is how we recommend you use this documentation.

If you are a newcomer to ACL2, we do not recommend that you wander off into the full documentation. Instead start with the ACL2-TUTORIAL documentation topic. Experienced users tend mostly to use the ``Index'' to look up concepts mentioned in error messages or vaguely remembered from their past experiences with ACL2.

Note: The documentation is available for reading in a Web browser (recommended), in Emacs Info, using the ACL2 :DOC command, or as a printed book (over 2200 pages). These are available as follows.

  • For web browsing using an HTML version of our documentation, click on Major Topics (here, or above). Better yet, view a local copy, which you can find under your local acl2-sources/ diretory at doc/HTML/acl2-doc.html.
  • Alternatively, for web browsing you can use documentation generated by Jared Davis's xdoc utility. You can build a local copy from the ACL2 Community Books, following instructions in books/Makefile.
  • Those familiar with Emacs Info can read the documentation in that format by loading the file emacs/emacs-acl2.el distributed with ACL2 (under the acl2-sources directory), and then evaluating meta-x acl2-info. Alternatively, within Emacs Info press g and then enter the following path (of course, replacing PATH_TO_ACL2-SOURCES by the path of your acl2-sources directory):
    (PATH_TO_ACL2-SOURCES/doc/EMACS/acl2-doc-emacs.info)top.
  • To read a printed copy, obtain a Postscript version here (about 2.6 MB).



Community Books: Lemma Libraries and Utilities, and How to Contribute

A companion to ACL2 is the library of community books, which have been developed by many users over the years. These books contain definitions and theorems that you might find useful in your models and proofs. In addition, some books contain ACL2 reasoning or analysis tools built by users. The installation instructions explain how to download and install the community books.

We strongly encourage users to submit additional books by following the instructions at the acl2-books project page hosted at http://acl2-books.googlecode.com.

We also distribute a few interface tools, such as support for infix printing. For these, see the Utilities section of Books and Papers about ACL2 and Its Applications. Some of the papers mentioned in that collection contain utilities, scripts, or ACL2 books for the problem domains in question.



How to contribute libraries and documentation

As mentioned above, we strongly encourage users to submit additional books by following the instructions at the acl2-books project page hosted at http://acl2-books.googlecode.com.

Also, if you have written up (or are interested in writing) text that may be helpful to other ACL2 users, we invite you to contribute it to the community. Such user documentation may be in any format that is readable by web browsers (for example html, pdf, and plain text). User-contributed documentation can link back to the ACL2 documentation proper, by using links such as:
http://www.cs.utexas.edu/users/moore/acl2/current/MAKE-EVENT.html
(In general, substitute the upper-case of the documentation topic for \"MAKE-EVENT\" in the example above.)

To contribute user documentation, send email to the ACL2 developers, Matt Kaufmann and J Strother Moore.

Searching documentation and books

The links below may help you to search the ACL2 documentation and the ACL2 community books, respectively. Our approach is low-tech, but intended to be reliable and easy to use on any platform. You might want to add a bookmark for each of these.
  • The following link will take you to a search box on a Google page, which has the following contents.
    search_term site:http://www.cs.utexas.edu/users/moore/acl2/v6-3
    
    Now simply replace the word `search_term' with your topic. For example, replace `search_term' by `tail recursion' to get documentation about tail recursion.
    tail recursion site:http://www.cs.utexas.edu/users/moore/acl2/v6-3
    
    Now you are ready to follow the link.

    SEARCH THE DOCUMENTATION

  • The next link will take you to the community books website (which is external to the present ACL2 website), specifically to the section entitled ``Searching and browsing the books.'' There, you will see a search box in which you can enter your search term. For example, if you type `tail recursion' and then <RETURN>, you will see text from several books in the svn trunk that deal with the topic of tail recursion, with an accompanying ``File Path'' shown at the end of each book's text.

    SEARCH THE COMMUNITY BOOKS

























































") (defconst *home-page-references* '(|The Tours| ;;; a ACL2-Tutorial ;;; b events ;;; c programming ;;; d rule-classes ;;; e books ;;; f note-6-3 ;;; g ; current release notes the-method ;;; h introduction-to-the-theorem-prover ;;; i ; This is not used right now. interesting-applications ;;; j )) (deflabel |Pages Written Especially for the Tours| :doc ":Doc-Section |Pages Written Especially for the Tours| Pages Written Especially for the Tours~/ The ACL2 Home Page is generated from ACL2's online documentation strings. (How else could we achieve the total integration of ACL2's online documentation with the home page?) This page is just an artifact of the structure of our documentation strings: each string must belong to a ``major section'' of the documentation database. This page is not structured to be used by a person browsing via the Web. It contains, in an arbitrary order, the pages written specificially for the Web user. Furthermore, browsing the pages below via the ACL2 :DOC command or via TexInfo is often unsatisfying because those browsers do not support gif files and the notion of going ``back'' to a node just visited. If you wish to look at the pages below, we strongly recommend that you do so via a HTML-based Web browser. Indeed, you should simply visit ACL2's Home Page and take one of the Tours.~/ Generally, the topics listed above will not be of use to the ACL2 user.") (deflabel |Undocumented Topic| :doc ":Doc-Section |Pages Written Especially for the Tours| Undocumented Topic~/~/ This topic has not yet been documented. Sorry") (deflabel |Common Lisp| :doc ":Doc-Section |Pages Written Especially for the Tours| Common Lisp~/~/ ~walk[|An Example Common Lisp Function Definition|] ~gif[common-lisp.gif] The logic of ACL2 is based on Common Lisp. Common Lisp is the standard list processing programming language. It is documented in: Guy L. Steele, ~b[Common Lisp The Language], Digital Press, 12 Crosby Drive, Bedford, MA 01730, 1990. See also ~url[http://www.cs.cmu.edu/Web/Groups/AI/html/cltl/cltl2.html]. ACL2 formalizes only a subset of Common Lisp. It includes such familiar Lisp functions as ~c[cons], ~c[car] and ~c[cdr] for creating and manipulating list structures, various arithmetic primitives such as ~c[+], ~c[*], ~c[expt] and ~c[<=], and ~c[intern] and ~c[symbol-name] for creating and manipulating symbols. Control primitives include ~c[cond], ~c[case] and ~c[if], as well as function call, including recursion. New functions are defined with ~c[defun] and macros with ~c[defmacro]. ~l[programming] ~warn[] for a list of the Common Lisp primitives supported by ACL2. ACL2 supports five of Common Lisp's datatypes: * the precisely represented, unbounded numbers (integers, rationals, and the complex numbers with rational components, called the ``complex rationals'' here), * the characters with ASCII codes between 0 and 255 * strings of such characters * symbols (including packages) * conses ACL2 is a very small subset of full Common Lisp. ACL2 does not include the Common Lisp Object System (CLOS), higher order functions, circular structures, and other aspects of Common Lisp that are ~b[non-applicative]. Roughly speaking, a language is applicative if it follows the rules of function application. For example, ~c[f(x)] must be equal to ~c[f(x)], which means, among other things, that the value of ~c[f] must not be affected by ``global variables'' and the object ~c[x] must not change over time. ~walk[|An Example Common Lisp Function Definition|]") (deflabel |An Example Common Lisp Function Definition| :doc ":Doc-Section |Pages Written Especially for the Tours| An Example Common Lisp Function Definition~/~/ ~walk[|An Example of ACL2 in Use|] Consider the binary trees ~c[x] and ~c[y] below. ~terminal[x = (list 'a 'b) y = (list 'c 'd 'e)] ~gif[binary-trees-x-y.gif] In Lisp, ~c[x] is written as the list ~c['(A B)] or, equivalently, as ~c['(A B . NIL)]. Similarly, ~c[y] may be written ~c['(C D E)]. Suppose we wish to replace the right-most tip of ~c[x] by the entire tree ~c[y]. This is denoted ~c[(app x y)], where ~c[app] stands for ``append''. ~gif[binary-trees-app.gif] We can define ~c[app] with: ~bv[] ~b[(defun app (x y)] ~i[; Concatenate x and y.] ~b[(declare (type (satisfies true-listp) x))]~i[; We expect x to end in NIL.] ~b[(cond ((endp x) y)] ~i[; If x is empty, return y.] ~b[(t (cons (car x)] ~i[; Else, copy first node] ~b[(app (cdr x) y)))))] ~i[; and recur into next.] ~ev[] If you defined this function in some Common Lisp, then to run ~c[app] on the ~c[x] and ~c[y] above you could then type ~bv[] (app '(A B) '(C D E)) ~ev[] and Common Lisp will print the result ~c[(A B C D E)]. ~walk[|An Example of ACL2 in Use|]") (deflabel |The Tours| :doc ":Doc-Section |Pages Written Especially for the Tours| The Tours~/~/ ACL2 is a very large, multipurpose system. You can use it as a programming language, a specification language, a modeling language, a formal mathematical logic, or a semi-automatic theorem prover, just to name its most common uses. It has been used on a number of ~il[interesting-applications industrial applications]. If you're uncertain as to whether your project is appropriate for ACL2 we urge you to look over this list or contact the ACL2 developers. This home page includes all of ACL2's online documentation, which is quite extensive (over 4 megabytes). To help ease your introduction to ACL2, we have built two tours through this documentation. If you are familiar with at least some of the ~il[interesting-applications industrial applications] of ACL2, then you will understand the distance between the simple examples we talk about in these tours and the kinds of things ACL2 users do with the system. Newcomers to ACL2 should first take the ``Flying Tour.'' Then, if you want to know more, take the ``Walking Tour.'' On your first reading, follow the two Tours linearly, clicking only on the icon of the Tour you're on. Beware of other links, which might jump you from one tour to the other or into the reference manual! Once you've had a coherent overview of the system, you might quickly repeat both Tours to see if there are unvisited links you're interested in, using your brower's Back Button to return to your starting points. If after all this you want to learn how to use the theorem prover (!), ~pl[introduction-to-the-theorem-prover]. To start a tour, click on the appropriate icon below. ~fly[|A Flying Tour of ACL2|] ~walk[|A Walking Tour of ACL2|] For readers using our :DOC or our TexInfo format in Emacs: The tours will probably be unsatisfying because we use gif files and assume you can navigate ``back.''") (deflabel |A Flying Tour of ACL2| :doc ":Doc-Section |Pages Written Especially for the Tours| A Flying Tour of ACL2~/~/ ~large-fly[|About the ACL2 Home Page|] On this tour you will learn a little about what ACL2 is for rather than how ACL2 works. At the top and bottom bottom of the ``page'' there are ``flying tour'' icons. Click on either icon to go to the next page of the tour. The tour visits the following topics sequentially. But on your first reading, don't navigate through the tour by clicking on these links; they are shown as live links only so that later you can determine what you've visited. Instead, just use the flying tour icons. ~bf[] ~b[The Flight Plan] * ~il[|About the ACL2 Home Page| This Documentation] * ~il[|What Is ACL2(Q)| What is ACL2?] * ~il[|What is a Mathematical Logic(Q)| Mathematical Logic] * ~il[|What is a Mechanical Theorem Prover(Q)| Mechanical Theorem Proving] * ~il[|About Models| Mathematical Models in General] * ~il[|Models of Computer Hardware and Software| Mathematical Models of Computing Machines] ~il[|A Typical State| Formalizing Models] ~il[|Running Models| Running Models] ~il[|Symbolic Execution of Models| Symbolic Execution of Models] ~il[|Proving Theorems about Models| Proving Theorems about Models] * Requirements of ACL2 ~il[|What is Required of the User(Q)| The User's Skills] ~il[|How Long Does It Take to Become an Effective User(Q)| Training] ~il[|Other Requirements| Host System] ~ef[] On your first reading, don't explore other links you see in the tour. Some of them lead to the Walking Tour, which you can take coherently when you finish this tour. Others lead into the extensive hyptertext documentation and you are liable to get lost there unless you're trying to answer a specific question. We intend the tour to take about 10 minutes of your time. ~fly[|About the ACL2 Home Page|]") (deflabel |About the ACL2 Home Page| :doc ":Doc-Section |Pages Written Especially for the Tours| About the ACL2 Home Page~/~/ ~fly[|What Is ACL2(Q)|] The ACL2 Home Page is integrated into the ACL2 online documentation. Over 4 megabytes of hypertext is available here. The vast majority of the text is user-level documentation. For example, to find out about ~il[rewrite] ~warn[] rules you could click on the link. (If you do that, remember to use your browser's ~b[Back Button] to come back here.) The tiny warning signs ~warn[] mark links that lead out of the introductory-level material and into the user documentation. We advise against following such links upon your first reading of the documentation. At the end of the tours you will have a chance to revisit them quickly to explore alternative paths more fully. Finally, every page contains two icons at the bottom. The ACL2 icon leads you back to the ACL2 Home Page. The Index icon allows you to browse an alphabetical listing of all the topics in ACL2's online documentation. But both icons take you off the main route of the tour. ~fly[|What Is ACL2(Q)|]") (deflabel |A Walking Tour of ACL2| :doc ":Doc-Section |Pages Written Especially for the Tours| A Walking Tour of ACL2~/~/ ~large-walk[|Common Lisp|] On this tour you will learn a little more about the ACL2 logic, the theorem prover, and the user interface. This time we will stick with really simple things, such as the associativity of list concatenation. We assume you have taken the Flying Tour but that you did not necessarily follow all the ``off-tour'' links because we encouraged you not to. With the Walking Tour we encourage you to visit off-tour links ~-[] provided they are not marked with the tiny warning sign (~warn[]). But they are ``branches'' in the tour that lead to ``dead ends.'' When you reach a dead end, remember to use your browser's Back Button to return to the Walking Tour to continue. When you get to the end of the tour we'll give you a chance to repeat quickly both the Flying and the Walking Tours to visit any off-tour links still of interest. ~walk[|Common Lisp|]") (deflabel |What Is ACL2(Q)| :doc ":Doc-Section |Pages Written Especially for the Tours| What Is ACL2?~/~/ ~fly[|What is a Mathematical Logic(Q)|] ACL2 is a ~b[mathematical logic] together with a ~b[mechanical theorem prover] to help you reason in the logic. The logic is just a subset of applicative ~il[|Common Lisp| Common Lisp]. (This link takes you off the main route of the tour. You'll see some Common Lisp on the tour, so visit this later!) The theorem prover is an ``industrial strength'' version of the Boyer-Moore theorem prover, Nqthm. ~b[Models] of all kinds of computing systems can be built in ACL2, just as in Nqthm, even though the formal logic is Lisp. Once you've built an ACL2 model of a system, you can run it. You can also use ACL2 to prove theorems about the model. ~fly[|What is a Mathematical Logic(Q)|]") (deflabel |About Models| :doc ":Doc-Section |Pages Written Especially for the Tours| About Models~/~/ ~fly[|Models of Computer Hardware and Software|] ACL2 is used to construct mathematical models of computer hardware and software (i.e., ``digital systems''). ~gif[computing-machine.gif] A ~b[mathematical model] is a set of mathematical formulas used to predict the behavior of some artifact. The use of mathematical models allows ~b[faster] and ~b[cheaper] delivery of ~b[better] systems. Models need not be ~b[complete] or ~b[perfectly accurate] to be useful to the trained engineer. ~click-here[|Models in Engineering|] for more discussion of these assertions in an engineering context. ~fly[|Models of Computer Hardware and Software|]") (deflabel |Models in Engineering| :doc ":Doc-Section |Pages Written Especially for the Tours| Models in Engineering~/~/ ~gif[bridge.gif] Frequently, engineers use mathematical models. Use of such models frequently lead to ~b[better designs], ~b[faster completion of acceptable products], and ~b[reduced overall cost], because models allow the trained user to study the design before it is built and analyze its properties. Usually, testing and analyzing a model is cheaper and faster than fabricating and refabricating the product. ~gif[bridge-analysis.gif] ") (deflabel |The Falling Body Model| :doc ":Doc-Section |Pages Written Especially for the Tours| The Falling Body Model~/~/ ~gif[pisa.gif] One particularly famous and very simple model is the equation of a falling body: the distance d an object falls is proportional to the square of the time t. If the time is measured in seconds and the distance in feet, the equation relating these two is ~bv[] 2 d = 16t ~ev[] This equation is a ~b[model] of falling objects. It can be used to predict how long it takes a cannonball to fall from the top of a 200 foot tower (3.5 seconds). This might be important if your product is designed to drop cannonballs on moving targets.") (deflabel |Corroborating Models| :doc ":Doc-Section |Pages Written Especially for the Tours| Corroborating Models~/~/ ~fly[|Models of Computer Hardware and Software|] After producing a model, it must be ~b[corroborated] against reality. The Falling Body Model has been corroborated by a vast number of experiments in which the time and distance were measured and compared according to the formula. In general all models must be corroborated by experiment. The Falling Body Model can be derived from deeper models, namely Newton's laws of motion and the assertion that, over the limited distances concerned, graviation exerts a constant acceleration on the object. When the model in question can be derived from other models, it is the other models that are being corroborated by our experiments. Because nature is not formal, we cannot ~b[prove] that our models of it are correct. All we can do is test our models against nature's behavior. Such testing often exposes restrictions on the applicability of our models. For example, the Falling Body Model is inaccurate if air resistance is significant. Thus, we learn not to use that model to predict how long it takes a feather to fall from a 200 foot tower in the earth's atmosphere. In addition, attempts at corroboration might reveal that the model is actually incorrect. Careful measurements might expose the fact that the gravitational force increases as the body falls closer to earth. Very careful measurements might reveal relativistic effects. Technically, the familiar Falling Body Model is just wrong, even under excessive restrictions such as ``in a perfect vacuum'' and ``over small distances.'' But it is an incredibly useful model nonetheless. There are several morals here. ~b[Models need not be complete to be useful.] ~b[Models need not be perfectly accurate to be useful.] ~b[The user of a model must understand its limitations.] ~fly[|Models of Computer Hardware and Software|]") (deflabel |Models of Computer Hardware and Software| :doc ":Doc-Section |Pages Written Especially for the Tours| Models of Computer Hardware and Software~/~/ ~fly[|A Typical State|] ~gif[computing-machine.gif]~par[] Computing machines, whether hardware or software or some combintation, are frequently modeled as ``state machines.'' To so model a computing machine we must represent its ~b[states] as objects in our mathematical framework. ~b[Transitions] are functions or relations on state objects. In what language shall we define these objects, functions, and relations? The mathematical languages we were taught in high school ~b[algebra], ~b[geometry], ~b[trignometry], and ~b[calculus] are often inappropriate for modeling digital systems. They primarily let us talk about numbers and continuous functions. To see what kind of expressive power we need, take a closer look at what a typical state contains. ~fly[|A Typical State|]") (deflabel |A Typical State| :doc ":Doc-Section |Pages Written Especially for the Tours| A Typical State~/~/ ~fly[|Functions for Manipulating these Objects|] ~gif[state-object.gif] Observe that the states in typical models talk about ~bf[] ~b[booleans] ~b[integers] ~b[vectors] ~b[records] ~b[caches] ~b[bits] ~b[symbols] ~b[arrays] ~b[stacks] ~b[files] ~b[characters] ~b[strings] ~b[sequences] ~b[tables] ~b[directories] ~ef[] These objects are ~b[discrete] rather than ~b[continuous]; furthermore they are built incrementally or ~b[inductively] by repeatedly using primitive operations to put together smaller pieces. The functions we need to manipulate these objects do things like ~b[concatenate], ~b[reverse], ~b[sort], ~b[search], ~b[count], etc. ~fly[|Functions for Manipulating these Objects|]") (deflabel |Functions for Manipulating these Objects| :doc ":Doc-Section |Pages Written Especially for the Tours| Functions for Manipulating these Objects~/~/ ~fly[|Modeling in ACL2|] Consider a typical ``stack'' of control frames. ~gif[stack.gif] Suppose the model required that we express the idea of ``the most recent frame whose return program counter points into ~c[MAIN].'' The natural expression of this notion involves ~b[function application] ~-[] ``fetch the ~c[return-pc] of this frame'' ~b[case analysis] ~-[] ``~b[if] the pc is ~c[MAIN], ~b[then] ...'' ~b[iteration] or ~b[recursion] ~-[] ``pop this frame off and repeat.'' The designers of ACL2 have taken the position that a ~b[programming] ~b[language] is the natural language in which to define such notions, ~b[provided] the language has a mathematical foundation so that models can be analyzed and properties derived logically. Common Lisp is the language supported by ACL2. To be precise, a small applicative subset of Common Lisp is the language supported by ACL2. ~fly[|Modeling in ACL2|]") (deflabel |Common Lisp as a Modeling Language| :doc ":Doc-Section |Pages Written Especially for the Tours| Common Lisp as a Modeling Language~/~/ ~gif[common-lisp.gif] In ACL2 we have adopted Common Lisp as the basis of our modeling language. If you have already read our brief note on Common Lisp and recall the example of ~c[app], please proceed. Otherwise ~pclick-here[|Common Lisp|] for an exceedingly brief introduction to Common Lisp and then come ~b[back] here. In Common Lisp it is very easy to write systems of formulas that manipulate discrete, inductively constructed data objects. In building a model you might need to formalize the notion of sequences and define such operations as concatenation, length, whether one is a permutation of the other, etc. It is easy to do this in Common Lisp. Furthermore, if you have a Common Lisp ``theory of sequences'' you can ~b[run] the operations and relations you define. That is, you can execute the functions on concrete data to see what results your formulas produce. If you define the function ~c[app] as shown above and then type ~bv[] (app '(A B) '(C D E)) ~ev[] in any Common Lisp, the answer will be computed and will be ~c[(A B C D E)]. The ~b[executable] nature of Common Lisp and thus of ACL2 is very handy when producing models. But executability is not enough for a modeling language because the purpose of models is to permit analysis. ~click-here[|Analyzing Common Lisp Models|] to continue.") (deflabel |Analyzing Common Lisp Models| :doc ":Doc-Section |Pages Written Especially for the Tours| Analyzing Common Lisp Models~/~/ To analyze a model you must be able to reason about the operations and relations involved. Perhaps, for example, some aspect of the model depends upon the fact that the concatenation operation is associative. In any Common Lisp you can confirm that ~bv[] (app '(A B) (app '(C D) '(E F))) ~ev[] and ~bv[] (app (app '(A B) '(C D)) '(E F))) ~ev[] both evaluate to the same thing, ~c[(A B C D E F)]. But what distinguishes ACL2 (the logic) from applicative Common Lisp (the language) is that in ACL2 you can ~b[prove] that the concatenation function ~c[app] is associative when its arguments are true-lists, whereas in Common Lisp all you can do is test that proposition. That is, in ACL2 it makes sense to say that the following formula is a ``theorem.'' ~bv[] ~b[Theorem] Associativity of App (implies (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c)))) ~ev[] Theorems about the properties of models are proved by symbolically manipulating the operations and relations involved. If the concatenation of sequences is involved in your model, then you may well need the theorem above in order to that your model has some particular property.") (deflabel |What is a Mathematical Logic(Q)| :doc ":Doc-Section |Pages Written Especially for the Tours| What is a Mathematical Logic?~/~/ ~fly[|What is a Mechanical Theorem Prover(Q)|] ~gif[proof.gif] A mathematical logic is a formal system of formulas (~b[axioms]) and ~b[rules] for deriving other formulas, called ~b[theorems]. A ~b[proof] is a derivation of a theorem. To see a concrete proof tree, ~pclick-here[|A Trivial Proof|]. Why should you care? The neat thing about Theorems is that they are ``true.'' More precisely, if all the axioms are valid and the rules are validity preserving, then anything derived from the axioms via the rules is valid. So, if you want to determine if some formula is true, ~b[prove it]. ~fly[|What is a Mechanical Theorem Prover(Q)|]") (deflabel |A Trivial Proof| :doc ":Doc-Section |Pages Written Especially for the Tours| A Trivial Proof~/~/ ~Terminal[This doc string displays a picture of a trivial proof.] ~gif[concrete-proof.gif]") (deflabel |What is a Mechanical Theorem Prover(Q)| :doc ":Doc-Section |Pages Written Especially for the Tours| What is a Mechanical Theorem Prover?~/~/ ~fly[|What is a Mechanical Theorem Prover(Q) (cont)|] A ~b[mechanical theorem prover] is a computer program that finds proofs of theorems. ~gif[automatic-theorem-prover.gif] The ideal mechanical theorem prover is ~b[automatic]: you give it a formula and it gives you a proof of that formula or tells you there is no proof. Unfortunately, automatic theorem provers can be built only for very simple logics (e.g., ~b[propositional calculus]) and even then practical considerations (e.g., how many centuries you are willing to wait) limit the problems they can solve. ~fly[|What is a Mechanical Theorem Prover(Q) (cont)|]") (deflabel |What is a Mechanical Theorem Prover(Q) (cont)| :doc ":Doc-Section |Pages Written Especially for the Tours| What is a Mechanical Theorem Prover? (cont)~/~/ ~fly[|About Models|] To get around this, mechanical theorem provers often require help from the ~b[user]. ~gif[interactive-theorem-prover-a.gif] ~click-here[|ACL2 as an Interactive Theorem Prover|] to continue downward. ~fly[|About Models|]") (deflabel |ACL2 as an Interactive Theorem Prover| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 as an Interactive Theorem Prover~/~/ The ACL2 theorem prover finds proofs in the ACL2 logic. It can be automatic. But most often the user must help it. ~gif[interactive-theorem-prover.gif] The user usually guides ACL2 by suggesting that it first prove key ~b[lemmas]. Lemmas are just theorems used in the proofs of other theorems.") (deflabel |ACL2 as an Interactive Theorem Prover (cont)| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 as an Interactive Theorem Prover (cont)~/~/ ~walk[|ACL2 System Architecture|] When ACL2 proves a lemma, it is converted into one or more ~b[rules] and stored in a ~b[database]. The theorem prover is ~b[rule-driven]. By proving lemmas you can configure ACL2 to behave in certain ways when it is trying to prove formulas in a certain problem domain. The expert user can make ACL2 do amazingly ``smart'' looking things. But it would be wrong to think that ACL2 ~i[knows] the mathematical content of a formula just because it has proved it. What ACL2 knows ~-[] all ACL2 knows ~-[] is what is encoded in its rules. There are many types of rules (~pl[rule-classes] ~warn[]). Many formulas can be effectively coded as rules. But by the same token, it is possible to encode a formula as a rule that is so ineffective it cannot even prove itself! The way a formula is stored as a rule is entirely up to the user. That is, ~b[you] determine how ACL2 should use each formula that it proves. The most common kind of rule is the ~b[rewrite rule]. It is so common that if you don't tell ACL2 how to store a formula, it stores it as a rewrite rule. ~walk[|ACL2 System Architecture|]") (deflabel |ACL2 System Architecture| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 System Architecture~/~/ ~walk[|Rewrite Rules are Generated from DEFTHM Events|] ~gif[acl2-system-architecture.gif] The user interacts with the theorem prover by giving it definitions, theorems and advice. Most often the advice is about how to store each proved theorem as a rule. Sometimes the advice is about how to prove a specific theorem. The database consists of all the rules ACL2 ``knows.'' It is possible to include in the database all of the rules in some certified file of other events. Such certified files are called ~il[books] ~warn[]. Interesting proofs are usually built on top of many books, some of which are written especially for that problem domain and others of which are about oft-used domains, like arithmetic or list processing. ACL2's distribution includes many books written by users. See the ``books'' link under the ~b[Lemma Libraries and Utilities] ~warn[] link of the ACL2 home page. ~walk[|Rewrite Rules are Generated from DEFTHM Events|]") (deflabel |Modeling in ACL2| :doc ":Doc-Section |Pages Written Especially for the Tours| Modeling in ACL2~/~/ ~fly[|Running Models|] ~gif[computing-machine-a.gif]~par[] Below we define ~b[mc(s,n)] to be the function that ~b[single-step]s ~b[n] times from a given starting state, ~b[s]. In Common Lisp, ``mc(s,n)'' is written ~c[(mc s n)]. ~bv[] ~b[(defun mc (s n)] ; To step ~b[s] ~b[n] times: ~b[(if (zp n)] ; If ~b[n] is 0 ~b[s] ; then return ~b[s] ~b[(mc (single-step s) (- n 1))))] ; else step ~b[single-step(s)] ~b[n-1] times. ~ev[] This is an example of a formal model in ACL2. ~fly[|Running Models|]") (deflabel |Running Models| :doc ":Doc-Section |Pages Written Especially for the Tours| Running Models~/~/ ~fly[|Symbolic Execution of Models|] Suppose the machine being modeled is some kind of arithmetic unit. Suppose the model can be initialized so as to ~b[multiply] ~b[x] times ~b[y] and leave the answer in ~b[z]. Then if we initialize ~b[s] to ~b[multiply] with ~b[x=5] and ~b[y=7] and run the machine long enough, we can read the answer ~b[35] in the final state.~par[] ~gif[computing-machine-5x7.gif]~par[] Because ACL2 is a programming language, our model can be ~b[run] or ~b[executed]. If you defined the model in ACL2 and then typed ~bv[] (lookup 'z (mc (s 'mult 5 7) 29)) ~ev[] then ACL2 would compute 35. (Here we assume that the function ~c[s] creates a state ready to run a given application on given inputs ~c[x] and ~c[y].) You can ~b[emulate] or ~b[test] the model of your machine. This is ~b[obvious] because ACL2 is Common Lisp; and Common Lisp is a ~b[programming language]. ~fly[|Symbolic Execution of Models|]") (deflabel |Symbolic Execution of Models| :doc ":Doc-Section |Pages Written Especially for the Tours| Symbolic Execution of Models~/~/ ~fly[|Proving Theorems about Models|] But ACL2 is more than a programming language. Initialize ~b[x] to 5 and let ~b[y] be ~b[any legal value].~par[] ~gif[computing-machine-5xy.gif]~par[] Because ACL2 is a mathematical language, we can simplify the expression ~bv[] (lookup 'z (mc (s 'mult 5 y) 29)) ~ev[] and get (+ y y y y y). This is ~b[symbolic execution] because not all of the parameters are known. ~fly[|Proving Theorems about Models|]") (deflabel |Proving Theorems about Models| :doc ":Doc-Section |Pages Written Especially for the Tours| Proving Theorems about Models~/~/ ~fly[|What is Required of the User(Q)|] But ACL2 is a ~b[logic]. We can ~b[prove theorems about the model].~par[] ~gif[computing-machine-xxy.gif]~par[] ~bv[] ~b[Theorem. MC 'mult is a multiplier] (implies (and (natp x) (natp y)) (equal (lookup 'z (mc (s 'mult x y) (mclk x))) (* x y))). ~ev[] This theorem says that a certain program running on the ~b[mc] machine will correctly multiply ~b[any two natural numbers]. It is a statement about an ~b[infinite] number of test cases! We know it is true about the model because we ~b[proved] it. Of course, models of actual machines usually only accept a finite number of different inputs. For example, engineers at Advanced Micro Devices (AMD), Centaur, and IBM have ACL2 models of floating point units that operate on double precision IEEE floating point numbers. These are finite models. But the size of their inputs is sufficiently large that they are verified by the same mathematical methods used to prove theorems about infinite state systems like our little ~c[mc]. ~fly[|What is Required of the User(Q)|]") (deflabel |What is Required of the User(Q)| :doc ":Doc-Section |Pages Written Especially for the Tours| What is Required of the User?~/~/ ~fly[|How Long Does It Take to Become an Effective User(Q)|] It is not easy to build ACL2 models of complex systems. To do so, the user must ~b[understand] ~bf[] * the system being modeled, and * ACL2, at least as a programming language. ~ef[] It is not easy to get ACL2 to prove hard theorems. To do so, the user must ~b[understand] ~bf[] * the model, * ACL2 as a mathematical logic, and * be able to construct a proof (in interaction with ACL2). ~ef[] ACL2 will help construct the proof but its primary role is to prevent logical mistakes. The creative burden ~-[] the mathematical insight into ~b[why the model has the desired property] ~-[] is the user's responsibility. ~fly[|How Long Does It Take to Become an Effective User(Q)|]") (deflabel |How Long Does It Take to Become an Effective User(Q)| :doc ":Doc-Section |Pages Written Especially for the Tours| How Long Does It Take to Become an Effective User?~/~/ ~fly[|Other Requirements|] We expect that a talented undergraduate majoring in computer science (or perhaps mathematics) will probably take several weeks to become an effective ACL2 user. The time will depend, of course, on that student's familiarity with logic (or formal methods) and Lisp programming, as well as willingness to read and study the ACL2 User's Manual. Of course, it is critical to do some projects in order to gain proficiency. (Hence access to an ACL2 implementation is also a requirement, for example by downloading and installing following links from the ACL2 home page.) But it is critical to start with ``toy'' projects before tackling a ``grand challenge.'' ~fly[|Other Requirements|]") (deflabel |Other Requirements| :doc ":Doc-Section |Pages Written Especially for the Tours| Other Requirements~/~/ ~fly[|The End of the Flying Tour|] ACL2 is distributed on the Web without fee. There is a ~b[license] agreement based on the 3-clause BSD license. See the file LICENSE in the ACL2 distribution. ACL2 currently runs on ~b[Unix], ~b[Linux], ~b[Windows], and ~b[Macintosh OS X] operating systems. It can be built in any of the following Common Lisps: ~bf[] * ~b[Allegro Common Lisp], * ~b[CCL] (formerly OpenMCL) * ~b[CLISP], * ~b[CMU Common Lisp], * ~b[GCL] (Gnu Common Lisp), * ~b[LispWorks], and * ~b[SBCL] (Steel Bank Common Lisp) ~ef[] ~fly[|The End of the Flying Tour|]") (deflabel |The End of the Flying Tour| :doc ":Doc-Section |Pages Written Especially for the Tours| The End of the Flying Tour~/~/ ~gif[landing.gif]~par[] This completes the Flying Tour. We recommend that you now take ~il[|A Walking Tour of ACL2|]. Thanks.~nl[] Matt Kaufmann and J Moore ~walk[|A Walking Tour of ACL2|]") (deflabel |An Example of ACL2 in Use| :doc ":Doc-Section |Pages Written Especially for the Tours| An Example of ACL2 in Use~/ ~walk[|How To Find Out about ACL2 Functions|] To introduce you to ACL2 we will consider the ~c[app] function discussed in the ~il[|Common Lisp| Common Lisp] page, ~b[except] we will omit for the moment the ~b[declare] form, which in ACL2 is called a ~b[guard]. Guards are arbitrary ACL2 terms that express the ``intended domain'' of functions. In that sense, guards are akin to type signatures. However, Common Lisp and ACL2 are untyped programming languages: while the language supports several different data types and the types of objects can be determined by predicates at runtime, any type of object may be passed to any function. Thus, guards are ``extra-logical.'' Recognizing both the practical and intellectual value of knowing that your functions are applied to the kinds of objects you intend, ACL2 imposes guards on Common Lisp and provides a means of proving that functions are used as intended. But the story is necessarily complicated and we do not recommend it to the new user. Get used to the fact that any ACL2 function may be applied to any objects and program accordingly. Read about guards later. Here is the definition again ~bv[] ~b[(defun app (x y)] ~b[(cond ((endp x) y)] ~b[(t (cons (car x) ] ~b[(app (cdr x) y)))))] ~ev[]~/ ~par[] The next few stops along the Walking Tour will show you ~bf[] * how to use the ACL2 documentation, * what happens when the above definition is submitted to ACL2, * what happens when you evaluate calls of ~c[app], * what one simple theorem about ~c[app] looks like, * how ACL2 proves the theorem, and * how that theorem can be used in another proof. ~ef[] Along the way we will talk about the ~b[definitional principle], ~b[types], the ACL2 ~b[read-eval-print loop], and how the ~b[theorem prover] works. When we complete this part of the tour we will return briefly to the notion of ~b[guards] and revisit several of the topics above in that context. ~walk[|How To Find Out about ACL2 Functions|]") (deflabel |How To Find Out about ACL2 Functions| :doc ":Doc-Section |Pages Written Especially for the Tours| How To Find Out about ACL2 Functions~/~/ ~walk[|How To Find Out about ACL2 Functions (cont)|] Most ACL2 primitives are documented. Here is the definition of ~c[app] again, with the documented topics highlighted. ~warn[] All of the links below lead into the ACL2 reference manual. So follow these links if you wish, but use your ~b[Back Button] to return here! ~bv[] (~il[defun] app (x y) (~il[cond] ((~il[endp] x) y) (t (~il[cons] (~il[car] x) (app (~il[cdr] x) y))))) ~ev[] By following the link on ~il[endp] ~warn[] we see that it is a Common Lisp function and is defined to be the same as ~il[atom] ~warn[], which recognizes non-conses. But ~c[endp] has a guard. Since we are ignorning guards for now, we'll ignore the guard issue on ~c[endp]. So this definition reads ``to ~c[app] ~c[x] and ~c[y]: if ~c[x] is an atom, return ~c[y]; otherwise, ~c[app] ~c[(cdr x)] and ~c[y] and then cons ~c[(car x)] onto that.'' ~walk[|How To Find Out about ACL2 Functions (cont)|]") (deflabel |How To Find Out about ACL2 Functions (cont)| :doc ":Doc-Section |Pages Written Especially for the Tours| How To Find Out about ACL2 Functions (cont)~/~/ ~walk[|The Admission of App|] You can always use the Index ~warn[] icon below to find the documentation of functions. Try it. Click on the Index icon below. Then use the Find command of your browser to find ``endp'' in that document and follow the link. But remember to come back here. The ACL2 documentation is also available via Emacs' TexInfo, allowing you to explore the hyperlinked documentation in the comfort of a text editor that can also interact with ACL2. In addition, runtime images of ACL2 have the hyperlinked text as a large ACL2 data structure that can be explored with ACL2's ~b[:doc] command. If you have ACL2 running, try the command ~b[:doc endp]. Another way to find out about ACL2 functions, if you have an ACL2 image available, is to use the command :~ilc[args] ~warn[] which prints the formals, type, and guard of a function symbol. Of course, the ACL2 documentation can also be printed out as a very long book but we do not recommend that! See the ACL2 Home Page to download the Postscript. Now let's continue with ~c[app]. ~walk[|The Admission of App|]") (deflabel |The Admission of App| :doc ":Doc-Section |Pages Written Especially for the Tours| The Admission of App~/~/ ~walk[|Revisiting the Admission of App|] Here is what it looks like to submit the definition of ~c[app] to ACL2: ~gif[green-line.gif] ~bv[] ACL2 !>~b[(defun app (x y)] ~b[(cond ((endp x) y)] ~b[(t (cons (car x) ] ~b[(app (cdr x) y)))))] The admission of APP is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We observe that the type of APP is described by the theorem (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)). We used primitive type reasoning. Summary Form: ( DEFUN APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, other: 0.03) APP ~ev[] ~gif[green-line.gif] The text between the lines above is one interaction with the ACL2 command loop. Interacting with the latest version of ACL2 may not produce the very same output, but we trust you'll recognize the basics. Above you see the user's ~b[input] and how the system responds. This little example shows you what the syntax looks like and is a very typical ~b[successful] interaction with the definitional principle. Let's look at it a little more closely. ~walk[|Revisiting the Admission of App|]") (deflabel |A Tiny Warning Sign| :doc ":Doc-Section |Pages Written Especially for the Tours| A Tiny Warning Sign~/ ~/ ~gif[warning.gif] This warning sign, which usually appears as ``~gif[twarning.gif]'', indicates that the link it marks takes you into ACL2's online documentation. The documentation is a vast graph of documented topics intended to help the ~em[user] of ACL2 rather than the ~em[potential user]. If you are exploring ACL2's home page to learn about the system, perhaps you should go back rather than follow the link marked with this sign. But you are welcome to explore the online documentation as well. Good luck.") (deflabel |Revisiting the Admission of App| :doc ":Doc-Section |Pages Written Especially for the Tours| Revisiting the Admission of App~/~/ ~walk[|Evaluating App on Sample Input|] ~terminal[This :DOC item is useful only in HTML settings.] Here is the definition of ~c[app] again with certain parts highlighted. If you are taking the Walking Tour, please read the text carefully and click on each of the links below, ~b[except those marked] ~warn[]. Then come ~b[back] here. ~gif[green-line.gif] ~bv[] ~il[|About the Prompt| ACL2 !>]~b[(defun app (x y)] ~b[(cond ((endp x) y)] ~b[(t (cons (car x) ] ~b[(app (cdr x) y)))))] The ~il[|About the Admission of Recursive Definitions| admission] of APP is trivial, using the relation ~il[O<] ~warn[] (which is known to be well-founded on the domain recognized by ~il[O-P] ~warn[]) and the measure (~il[ACL2-COUNT] ~warn[] X). We ~il[|Guessing the Type of a Newly Admitted Function| observe] that the ~il[|About Types| type] of APP is described by the theorem (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)). We used primitive type reasoning. ~il[|The Event Summary| Summary] Form: ( DEFUN APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, other: 0.03) APP ~ev[] ~gif[green-line.gif] ~walk[|Evaluating App on Sample Input|]") (deflabel |Evaluating App on Sample Input| :doc ":Doc-Section |Pages Written Especially for the Tours| Evaluating App on Sample Input~/~/ ~walk[|The Associativity of App|] ~gif[green-line.gif] ~bv[] ACL2 !>~b[(app nil '(x y z))] (X Y Z) ACL2 !>~b[(app '(1 2 3) '(4 5 6 7))] (1 2 3 4 5 6 7) ACL2 !>~b[(app '(a b c d e f g) '(x y z))] ; ~pclick-here[|Conversion|] for an explanation (A B C D E F G X Y Z) ACL2 !>~b[(app (app '(1 2) '(3 4)) '(5 6))] (1 2 3 4 5 6) ACL2 !>~b[(app '(1 2) (app '(3 4) '(5 6)))] (1 2 3 4 5 6) ACL2!>~b[(let ((a '(1 2))] ~b[(b '(3 4))] ~b[(c '(5 6)))] ~b[(equal (app (app a b) c)] ~b[(app a (app b c))))] T ~ev[] ~gif[green-line.gif] As we can see from these examples, ACL2 functions can be executed more or less as Common Lisp. The last three examples suggest an interesting property of ~c[app]. ~walk[|The Associativity of App|]") (deflabel |Conversion| :doc ":Doc-Section |Pages Written Especially for the Tours| Conversion to Uppercase~/ When symbols are read by Common Lisp they are converted to upper case. Note carefully that this remark applies to the characters in ~em[symbols]. The characters in strings are not converted upper case.~/ To type a symbol containing lower case characters you can enclose the symbol in vertical bars, as in ~c[|AbC|] or you can put a ``backslash'' before each lower case character you wish to preserve, as in ~c[A\\bC]. ~c[|AbC|] and ~c[A\\bC] are two different ways of writing the same symbol (just like 2/4 and 1/2 are two different ways of writing the same rational and 123 and 0123 are two different ways to write the same natural number). The symbol has three characters in its name, the middle one of which is a lower case b.") ; ACL2 !>~b[(app '(a b c) 27)] ; ~pclick-here[|ACL2 is an Untyped Language|] for an explanation. ; (A B C . 27) ; ; ACL2 !>~b[(app 7 27)] ; ~pclick-here[|Hey Wait! Is ACL2 Typed or Untyped(Q)|] for an explanation. ; ; ACL2 Error in TOP-LEVEL: The guard for the function symbol ENDP, which ; is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the ; call (ENDP 7). ; ; ACL2 !>~b[:set-guard-checking nil] ; ~pclick-here[|Undocumented Topic|] for an explanation. ; ; ACL2 >~b[(app 7 27)] ; ~pclick-here[|Undocumented Topic|] for an explanation. ; 27 (deflabel |The Associativity of App| :doc ":Doc-Section |Pages Written Especially for the Tours| The Associativity of App~/~/ ~walk[|The Theorem that App is Associative|] ~gif[green-line.gif] ~bv[] ACL2!>~b[(let ((a '(1 2))] ~b[(b '(3 4))] ~b[(c '(5 6)))] ~b[(equal (app (app a b) c)] ~b[(app a (app b c))))] T ~ev[] ~gif[green-line.gif] Observe that, for the particular ~c[a], ~c[b], and ~c[c] above, ~c[(app (app a b) c)] returns the same thing as ~c[(app a (app b c))]. Perhaps ~c[app] is ~b[associative]. Of course, to be associative means that the above property must hold for all values of ~c[a], ~c[b], and ~c[c], not just the ones ~b[tested] above. Wouldn't it be cool if you could type ~bv[] ACL2!>~b[(equal (app (app a b) c)] ~b[(app a (app b c)))] ~ev[] and have ACL2 compute the value ~c[T]? Well, ~b[you can't!] If you try it, you'll get an error message! The message says we can't evaluate that form because it contains ~b[free] variables, i.e., variables not given values. ~click-here[|Free Variables in Top-Level Input|] to see the message. We cannot evaluate a form on an infinite number of cases. But we can prove that a form is a theorem and hence know that it will always evaluate to true. ~walk[|The Theorem that App is Associative|]") (deflabel |The Theorem that App is Associative| :doc ":Doc-Section |Pages Written Especially for the Tours| The Theorem that App is Associative~/~/ ~walk[|The Proof of the Associativity of App|] ~bv[] ACL2!>~b[(defthm associativity-of-app] ~b[(equal (app (app a b) c)] ~b[(app a (app b c))))] ~ev[] The formula above says ~c[app] is associative. The ~ilc[defthm] ~warn[] command instructs ACL2 to prove the formula and to name it ~c[associativity-of-app]. Actually, the ~c[defthm] command also builds the formula into the database as a ~ilc[rewrite] ~warn[] rule, but we won't go into that just yet. What we will consider is how the ACL2 theorem prover proves this formula. If you proceed you will find the actual output of ACL2 in response to the command above. Some of the text is highlighted for the purposes of the tour. ACL2 does not highlight its output. You will note that we sometimes highlight a single open parenthesis. This is our way of drawing your attention to the subformula that begins with that parenthesis. By clicking on the parenthesis you will get an explanation of the subformula or its processing. ~walk[|The Proof of the Associativity of App|]") ; uaap= Unguarded-App-Assoc-Proof (deflabel |Free Variables in Top-Level Input| :doc ":Doc-Section |Pages Written Especially for the Tours| Free Variables in Top-Level Input~/~/ ~bv[] ACL2 !>~b[(equal (app (app a b) c)] ~b[(app a (app b c))))] ACL2 Error in TOP-LEVEL: Global variables, such as C, B, and A, are not allowed. See :DOC ASSIGN and :DOC @. ~ev[] ACL2 does not allow ``global variables'' in top-level input. There is no ``top-level binding environment'' to give meaning to these variables. Thus, expressions involving no variables can generally be evaluated, ~bv[] ACL2 !>~b[(equal (app (app '(1 2) '(3 4)) '(5 6))] ~b[(app '(1 2) (app '(3 4) '(5 6))))] (1 2 3 4 5 6) ~ev[] but expressions containing variables cannot. There is an exception to this rule. References to ``single-threaded objects'' may appear in top-level forms. ~l[stobj] ~warn[]. A single-threaded object is an ACL2 object, usually containing many fields, whose use is syntactically restricted so that it may be given as input only to certain functions and must be returned as output by certain functions. These restrictions allow single- threaded objects to be efficiently manipulated. For example, only a single copy of the object actually exists, even though from a logical perspective one might expect the object to be ``copied on write.'' The most commonly used single-threaded object in ACL2 is the ACL2 system state, whose current value is always held in the variable ~ilc[state] ~warn[]. ACL2 provides a way for you to use ~c[state] to save values of computations at the top-level and refer to them later. See ~il[assign] ~warn[] and ~il[@] ~warn[].") (deflabel |The Proof of the Associativity of App| :doc ":Doc-Section |Pages Written Especially for the Tours| The Proof of the Associativity of App~/~/ ~walk[|Overview of the Simplification of the Induction Step to T|] Here is the theorem prover's output when it processes the ~b[defthm] command for the associativity of ~c[app]. We have highlighted text for which we offer some explanation, and broken the presentation into several pages. (The most recent version of ACL2 may print slightly different output but the basics are the same.) Just follow the Walking Tour after exploring the explanations. However, before exploring this output you should understand that ACL2 users rarely read successful proofs! Instead, they look at certain subgoals printed in failed proofs, figure whether and how those subgoals can be proved, and give ACL2 directions for proving them, usually by simply proving other lemmas. Furthermore, to be a good user of ACL2 you do not have to understand how the theorem prover works. You just have to understand how to interact with it. We explain this in great detail later. But basically all new users are curious to know how ACL2 works and this little tour attempts to give some answers, just to satisfy your curiosity. ~gif[green-line.gif] ~bv[] ACL2!>~b[(defthm associativity-of-app] ~b[(equal (app (app a b) c)] ~b[(app a (app b c))))] Name the formula above ~il[|Name the Formula Above| *1]. ~il[|Perhaps| Perhaps] we can prove *1 by induction. Three induction schemes are ~il[|Suggested Inductions in the Associativity of App Example| suggested] by this conjecture. ~il[|Subsumption of Induction Candidates in App Example| Subsumption] reduces that number to two. However, one of these is ~il[|Flawed Induction Candidates in App Example| flawed] and so we are left with one viable candidate. We will induct according to a scheme suggested by (APP A B). If we let (:P A B C) denote *1 above then the induction scheme we'll use is ~il[|The Induction Scheme Selected for the App Example| (]AND ~il[|The Induction Step in the App Example| (]IMPLIES (AND (NOT (ENDP A)) (:P (CDR A) B C)) (:P A B C)) ~il[|The Base Case in the App Example| (]IMPLIES (ENDP A) (:P A B C))). This induction is ~il[|The Justification of the Induction Scheme| justified] by the same argument used to admit APP, namely, the measure (ACL2-COUNT A) is decreasing according to the relation O< (which is known to be well-founded on the domain recognized by O-P). When ~il[|The Instantiation of the Induction Scheme| applied] to the goal at hand the above induction scheme produces the following two ~il[|Nontautological Subgoals| nontautological subgoals]. ~ev[] ~walk[|Overview of the Simplification of the Induction Step to T|]") (deflabel |Name the Formula Above| :doc ":Doc-Section |Pages Written Especially for the Tours| Name the Formula Above~/~/ When the theorem prover explicitly assigns a name, like ~c[*1], to a formula, it has decided to prove the formula by induction.") (deflabel |Perhaps| :doc ":Doc-Section |Pages Written Especially for the Tours| Perhaps~/~/ The theorem prover's proof is printed in real time. At the time it prints ``Perhaps'' it does not know the proof will succeed.") (deflabel |Suggested Inductions in the Associativity of App Example| :doc ":Doc-Section |Pages Written Especially for the Tours| Suggested Inductions in the Associativity of App Example~/~/ To find a plausible induction argument, the system studies the recursions exhibited by the terms in the conjecture. Roughly speaking, a call of a recursive function ``suggests'' an induction if the argument position decomposed in recursion is occupied by a variable. In this conjecture, three terms suggest inductions: ~bv[] (app ~b[a] b) (app ~b[b] c) (app ~b[a] (app b c)) ~ev[] The variable recursively decomposed is indicated in ~b[bold].") (deflabel |Subsumption of Induction Candidates in App Example| :doc ":Doc-Section |Pages Written Especially for the Tours| Subsumption of Induction Candidates in App Example~/~/ After collecting induction suggestions from these three terms ~bv[] (app ~b[a] b) (app ~b[b] c) (app ~b[a] (app b c)) ~ev[] the system notices that the first and last suggest the same decomposition of ~c[a]: case split on whether ~c[a] is empty (i.e., ~c[(endp a)]), and in the case where it is not empty, recursively process ~c[(cdr a)]. So we are left with two ideas about how to induct: Decompose ~b[a] as we would to unwind (app ~b[a] b). Decompose ~b[b] as we would to unwind (app ~b[b] c).") (deflabel |Flawed Induction Candidates in App Example| :doc ":Doc-Section |Pages Written Especially for the Tours| Flawed Induction Candidates in App Example~/~/ Induction on ~b[a] is unflawed: every occurrence of ~b[a] in the conjecture ~bv[] (equal (app (app ~b[a] b) c) (app ~b[a] (app b c))) ~ev[] is in a position being recursively decomposed! Now look at the occurrences of ~c[b]. The first (shown in ~b[bold] below) is in a position that is held constant in the recursion of ~c[(app a b)]. It would be ``bad'' to induct on ~c[b] here. ~bv[] (equal (app (app a ~b[b]) c) (app a (app b c))) ~ev[]") (deflabel |The Induction Scheme Selected for the App Example| :doc ":Doc-Section |Pages Written Especially for the Tours| The Induction Scheme Selected for the App Example~/~/ ~bv[] (AND (IMPLIES (AND (NOT (ENDP A)) ; Induction Step: test (:P (CDR A) B C)) ; and induction hypothesis (:P A B C)) ; implies induction conclusion. (IMPLIES (ENDP A) (:P A B C))) ; Base Case ~ev[] The formula beginning with this parenthesis is the induction scheme suggested by ~c[(APP A B)] applied to ~c[(P A B C)]. It is a ~b[conjunction] (~ilc[AND] ~warn[]) of two formulas. The first is the ~b[induction step] and the second is the ~b[base case].") (deflabel |The Induction Step in the App Example| :doc ":Doc-Section |Pages Written Especially for the Tours| The Induction Step in the App Example~/~/ This formula is the ~b[Induction Step]. It basically consists of three parts, a test identifying the inductive case, an induction hypothesis and an induction conclusion. ~bv[] (IMPLIES (AND (NOT (ENDP A)) ~b[; Test] (:P (CDR A) B C)) ~b[; Induction Hypothesis] (:P A B C)) ~b[; Induction Conclusion] ~ev[] When we prove this we can assume ~bf[] * ~c[A] is not empty, and that * the associativity conjecture holds for a ``smaller'' version of ~c[A], namely, ~c[(CDR A)]. ~ef[] Under those hypotheses we have to prove the associativity conjecture for ~c[A] itself.") (deflabel |The Base Case in the App Example| :doc ":Doc-Section |Pages Written Especially for the Tours| The Base Case in the App Example~/~/ This formula is the ~b[Base Case]. It consists of two parts, a test identifying the non-inductive case and the conjecture to prove. ~bv[] (IMPLIES (ENDP A) ~b[; Test] (:P A B C)) ~b[; Conjecture] ~ev[] When we prove this we can assume ~bf[] * ~c[A] is empty ~ef[] and we have to prove the conjecture for ~c[A].") (deflabel |The Justification of the Induction Scheme| :doc ":Doc-Section |Pages Written Especially for the Tours| The Justification of the Induction Scheme~/~/ This paragraph explains why the induction selected is legal. The explanation is basically the same as the explanation for why the recursion in ~c[(APP A B)] terminates. ") (deflabel |The Instantiation of the Induction Scheme| :doc ":Doc-Section |Pages Written Especially for the Tours| The Instantiation of the Induction Scheme~/~/ The induction scheme just shown is just an abbreviation for our real goal. To obtain our actual goals we have to replace the schema ~c[:P] by the associativity conjecture (instantiated as shown in the scheme). This produces two actual goals, the induction step and the base case. ") (deflabel |Nontautological Subgoals| :doc ":Doc-Section |Pages Written Especially for the Tours| Prover output omits some details~/~/ The theorem prover's proof output is intended to suggest an outline of the reasoning process employed by its proof engine, which is virtually always more than is necessary for the ACL2 user. In particular, the output often omits subgoals that are sufficiently trivial, including tautologies.") (deflabel |Overview of the Simplification of the Induction Step to T| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Simplification of the Induction Step to T~/~/ ~walk[|Overview of the Simplification of the Base Case to T|] ~bv[] ~il[|On the Naming of Subgoals| Subgoal *1/2] (IMPLIES (AND (NOT (ENDP A)) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). By the simple :definition ~il[|Overview of the Expansion of ENDP in the Induction Step| ENDP] we reduce the conjecture to Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). ~il[|Overview of the Simplification of the Induction Conclusion| But] simplification reduces this to T, using the :definition APP, the :rewrite rules CDR-CONS and CAR-CONS and primitive type reasoning. ~ev[] ~walk[|Overview of the Simplification of the Base Case to T|]") (deflabel |On the Naming of Subgoals| :doc ":Doc-Section |Pages Written Especially for the Tours| On the Naming of Subgoals~/~/ ~c[Subgoal *1/2] is the ~b[induction step] from the scheme, obtained by instantiating the scheme with our conjecture. We number the cases ``backward'', so this is case ``2'' of the proof of ``*1''. We number them backward so you can look at a subgoal number and get an estimate for how close you are to the end.") (deflabel |Overview of the Expansion of ENDP in the Induction Step| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Expansion of ENDP in the Induction Step~/~/ In this message the system is saying that ~c[Subgoal *1/2] has been rewritten to the ~c[Subgoal *1/2'], by expanding the definition of ~b[endp]. This is an example of ~b[simplification], one of the main proof techniques used by the theorem prover. ~click-here[|The Expansion of ENDP in the Induction Step (Step 0)|] if you would like to step through the simplification of ~c[Subgoal *1/2].") (deflabel |The Expansion of ENDP in the Induction Step (Step 0)| :doc ":Doc-Section |Pages Written Especially for the Tours| The Expansion of ENDP in the Induction Step (Step 0)~/~/ ~bv[] Subgoal *1/2 (IMPLIES (AND (NOT ~il[|The Expansion of ENDP in the Induction Step (Step 1)| (]ENDP A)) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above (the open parenthesis before ~c[ENDP]) to replace ~c[(ENDP A)] by its definition.") (deflabel |The Expansion of ENDP in the Induction Step (Step 1)| :doc ":Doc-Section |Pages Written Especially for the Tours| The Expansion of ENDP in the Induction Step (Step 1)~/~/ ~bv[] Subgoal *1/2 (IMPLIES (AND ~il[|The Expansion of ENDP in the Induction Step (Step 2)| (]NOT ~b[(NOT (CONSP A))]) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] The ~b[bold] text is the instantiated definition of ~c[ENDP]. Now click on the link above to simplify (NOT (NOT (CONSP A)))") (deflabel |The Expansion of ENDP in the Induction Step (Step 2)| :doc ":Doc-Section |Pages Written Especially for the Tours| The Expansion of ENDP in the Induction Step (Step 2)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND ~b[(CONSP A)] (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Note that this is ~c[Subgoal *1/2'.] You may ~pclick-here[|Overview of the Simplification of the Induction Step to T|] to return to the main proof.") (deflabel |Overview of the Simplification of the Induction Conclusion| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Simplification of the Induction Conclusion~/~/ In this message the system is saying that ~c[Subgoal *1/2'] has been rewritten to T using the rules noted. The word ``~b[But]'' at the beginning of the sentence is a signal that the goal has been proved. ~Click-here[|The Simplification of the Induction Conclusion (Step 0)|] to step through the proof of ~c[Subgoal *1/2'].") (deflabel |The Simplification of the Induction Conclusion (Step 0)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 0)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP ~il[|The Simplification of the Induction Conclusion (Step 1)| (]APP A B) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to replace ~c[(APP A B)] by its definition.") (deflabel |The Simplification of the Induction Conclusion (Step 1)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 1)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP ~b[(IF ]~il[|The Simplification of the Induction Conclusion (Step 2)| (]~b[CONSP A)] ~b[(CONS (CAR A) (APP (CDR A) B))] ~b[B)] C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Note that the ~c[IF] expression above is the simplified body of ~c[APP]. But we know the test ~c[(CONSP A)] is true, by the first hypothesis. Click on the link above to replace the test by ~c[T]. Actually this step and several subsequent ones are done during the simplification of the body of ~c[APP] but we want to illustrate the basic principles of simplification without bothering with every detail.") (deflabel |The Simplification of the Induction Conclusion (Step 2)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 2)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP ~il[|The Simplification of the Induction Conclusion (Step 3)| (]IF ~b[T] (CONS (CAR A) (APP (CDR A) B)) B) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to apply the Axiom ~c[(IF T x y) = x].") (deflabel |The Simplification of the Induction Conclusion (Step 3)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 3)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL ~il[|The Simplification of the Induction Conclusion (Step 4)| (]APP ~b[(CONS (CAR A) (APP (CDR A) B))] C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to expand the definition of ~c[APP] here.") (deflabel |The Simplification of the Induction Conclusion (Step 4)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 4)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF ~il[|The Simplification of the Induction Conclusion (Step 5)| (]~b[CONSP (CONS (CAR A) (APP (CDR A) B)))] ~b[(CONS (CAR (CONS (CAR A) (APP (CDR A) B)))] ~b[(APP (CDR (CONS (CAR A) (APP (CDR A) B)))] ~b[C))] ~b[C)] (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to apply the Axiom ~c[(CONSP (CONS x y)) = T].") (deflabel |The Simplification of the Induction Conclusion (Step 5)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 5)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF ~b[T] (CONS ~il[|The Simplification of the Induction Conclusion (Step 6)| (]CAR (CONS (CAR A) (APP (CDR A) B))) (APP (CDR (CONS (CAR A) (APP (CDR A) B))) C)) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to apply the Axiom ~c[(CAR (CONS x y)) = x].") (deflabel |The Simplification of the Induction Conclusion (Step 6)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 6)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF T (CONS ~b[(CAR A)] (APP ~il[|The Simplification of the Induction Conclusion (Step 7)| (]CDR (CONS (CAR A) (APP (CDR A) B))) C)) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to apply the Axiom ~c[(CDR (CONS x y)) = y].") (deflabel |The Simplification of the Induction Conclusion (Step 7)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 7)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL ~il[|The Simplification of the Induction Conclusion (Step 8)| (]IF T (CONS (CAR A) (APP ~b[(APP (CDR A) B)] C)) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to apply the Axiom ~c[(IF T x y) = x].") (deflabel |The Simplification of the Induction Conclusion (Step 8)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 8)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL ~b[(CONS (CAR A)] ~b[(APP (APP (CDR A) B)] ~b[C))] ~il[|The Simplification of the Induction Conclusion (Step 9)| (]APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to expand the definition of ~c[APP] here. This time, we'll do the whole expansion at once, including the simplification of the resulting ~c[IF]. This is how ACL2 actually does it.") (deflabel |The Simplification of the Induction Conclusion (Step 9)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 9)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) ~il[|The Simplification of the Induction Conclusion (Step 10)| (]EQUAL (CONS (CAR A) (APP (APP (CDR A) B) C)) ~b[(CONS (CAR A)] ~b[(APP (CDR A) (APP B C))]))). ~ev[] ~gif[green-line.gif] Click on the link above to apply the Axiom that ~c[(EQUAL (CONS x y) (CONS u v))] is equal to the conjunction of ~c[(EQUAL x u)] and ~c[(EQUAL y v)]. In this case, ~c[(EQUAL x u)] is trivial, ~c[(EQUAL (CAR A) (CAR A))].") (deflabel |The Simplification of the Induction Conclusion (Step 10)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 10)~/~/ ~bv[] Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) ~il[|The Simplification of the Induction Conclusion (Step 11)| (]~b[EQUAL (APP (APP (CDR A) B) C)] ~b[(APP (CDR A) (APP B C)))]). ~ev[] ~gif[green-line.gif] Click on the link above to use the Induction Hypothesis (which is the second of the two hypotheses above and which is identical to the rewritten conclusion).") (deflabel |The Simplification of the Induction Conclusion (Step 11)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 11)~/~/ ~bv[] Subgoal *1/2' ~il[|The Simplification of the Induction Conclusion (Step 12)| (]IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) ~b[T]) ~ev[] ~gif[green-line.gif] Click on the link above to use the definition of ~c[IMPLIES]. Since the conclusion of the implication is now identically ~c[T], the implication simplifies to ~t[T].") (deflabel |The Simplification of the Induction Conclusion (Step 12)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Simplification of the Induction Conclusion (Step 12)~/~/ ~bv[] Subgoal *1/2' ~b[T] ~ev[] ~gif[green-line.gif] So, indeed, ~c[Subgoal *1/2'] ~b[does] simplify to T! You can see that even in an example as simple as this one, quite a lot happens in simplification. You may ~pclick-here[|Overview of the Simplification of the Induction Step to T|] to return to the main proof.") (deflabel |Overview of the Simplification of the Base Case to T| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Simplification of the Base Case to T~/~/ ~walk[|The End of the Proof of the Associativity of App|] ~bv[] ~il[|Overview of the Expansion of ENDP in the Base Case| Subgoal *1/1] (IMPLIES (ENDP A) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). By the simple :definition ENDP we reduce the conjecture to Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). ~il[|Overview of the Final Simplification in the Base Case| But] simplification reduces this to T, using the :definition APP and primitive type reasoning. ~ev[] ~walk[|The End of the Proof of the Associativity of App|]") (deflabel |Overview of the Expansion of ENDP in the Base Case| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Expansion of ENDP in the Base Case~/~/ ~c[Subgoal *1/1] is the ~b[Base Case] of our induction. It simplifies to ~c[Subgoal *1/1'] by expanding the ~b[ENDP] term in the hypothesis, just as we saw in the earlier proof of ~c[Subgoal *1/2].") (deflabel |Overview of the Final Simplification in the Base Case| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Final Simplification in the Base Case~/~/ The ~b[But] is our signal that the goal is proved. ~click-here[|The Final Simplification in the Base Case (Step 0)|] to step through the proof. It is very simple.") (deflabel |The Final Simplification in the Base Case (Step 0)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Final Simplification in the Base Case (Step 0)~/~/ ~bv[] Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP ~il[|The Final Simplification in the Base Case (Step 1)| (]APP A B) C) (APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to replace ~c[(APP A B)] by its definition. Note that the hypothesis ~c[(NOT (CONSP A))] allows us to simplify the ~c[IF] in ~c[APP] to its ~b[false branch] this time.") (deflabel |The Final Simplification in the Base Case (Step 1)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Final Simplification in the Base Case (Step 1)~/~/ ~bv[] Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP ~b[B] C) ~ilc[|The Final Simplification in the Base Case (Step 2)| (]APP A (APP B C)))). ~ev[] ~gif[green-line.gif] Click on the link above to expand the definition of ~c[APP]. Again, we come out through the false branch because of the hypothesis.") (deflabel |The Final Simplification in the Base Case (Step 2)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Final Simplification in the Base Case (Step 2)~/~/ ~bv[] Subgoal *1/1' (IMPLIES (NOT (CONSP A)) ~il[|The Final Simplification in the Base Case (Step 3)| (]EQUAL (APP B C) ~b[(APP B C)])). ~ev[] ~gif[green-line.gif] Click on the link above to use the Axiom ~c[(EQUAL x x) = t]") (deflabel |The Final Simplification in the Base Case (Step 3)| :doc ":Doc-Section |Pages Written Especially for the Tours| the Final Simplification in the Base Case (Step 3)~/~/ ~bv[] Subgoal *1/1' (IMPLIES (NOT (CONSP A)) ~b[T]) ~ev[] ~gif[green-line.gif] Now that its conclusion is identically ~c[T] the ~c[IMPLIES] will simplify to ~c[T] (not shown) and we are done with ~c[Subgoal *1/1']. You may ~pclick-here[|Overview of the Simplification of the Base Case to T|] to return to the main proof.") (deflabel |The End of the Proof of the Associativity of App| :doc ":Doc-Section |Pages Written Especially for the Tours| The End of the Proof of the Associativity of App~/~/ ~walk[|Guiding the ACL2 Theorem Prover|] ~bv[] That ~il[|Popping out of an Inductive Proof| completes] the proof of *1. ~il[|The Q.E.D. Message| Q.E.D.] Summary Form: ( DEFTHM ASSOCIATIVITY-OF-APP ...) ~il[|The Rules used in the Associativity of App Proof| Rules]: ((:REWRITE CDR-CONS) (:REWRITE CAR-CONS) (:DEFINITION NOT) (:DEFINITION ENDP) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:DEFINITION APP)) Warnings: None Time: 0.27 seconds (prove: ~il[|The Time Taken to do the Associativity of App Proof| 0.10], print: 0.05, other: 0.12) ASSOCIATIVITY-OF-APP ~ev[] ~gif[green-line.gif] ~walk[|Guiding the ACL2 Theorem Prover|]") (deflabel |Popping out of an Inductive Proof| :doc ":Doc-Section |Pages Written Especially for the Tours| Popping out of an Inductive Proof~/~/ Recall that our induction scheme (~pclick-here[|The Proof of the Associativity of App|] to revisit it) had two cases, the induction step (~c[Subgoal *1/2]) and the base case (~c[Subgoal *1/1]). Both have been proved!") (deflabel |The Q.E.D. Message| :doc ":Doc-Section |Pages Written Especially for the Tours| The Q.E.D. Message~/~/ ~b[Q.E.D.] stands for ``quod erat demonstrandum'' which is Latin for ``which was to be demonstrated'' and is the signal that a proof is completely done.") (deflabel |The Rules used in the Associativity of App Proof| :doc ":Doc-Section |Pages Written Especially for the Tours| The Rules used in the Associativity of App Proof~/~/ Note that under ~b[Rules] we list the ~il[rune runes] ~warn[] of all the rules used in the proof. This list says that we used the rewrite rules ~c[CAR-CONS] and ~c[CDR-CONS], the definitions of the functions ~c[NOT], ~c[ENDP] and ~c[APP], and primitive type reasoning (which is how we simplified the ~c[IF] and ~c[EQUAL] terms). For what it is worth, ~c[IMPLIES] and ~c[AND] are actually ~il[defmacro macros] ~warn[] that are expanded into ~c[IF] expressions before the proof ever begins. The use of macros is not reported among the rules.") (deflabel |The Time Taken to do the Associativity of App Proof| :doc ":Doc-Section |Pages Written Especially for the Tours| The Time Taken to do the Associativity of App Proof~/~/ The time it took us to explain this proof may leave the impression that the proof is complicated. In a way, it is. But it happens quickly. The time taken to do this proof is about 1/10 second. The rest of the time (about 2/10 seconds) is spent in pre- and post-processing. Basically, this proof flashes across your screen before you can read it; you see the ~b[Q.E.D.] and don't bother to scroll back to read it. You have more important things to do than read successful proofs.") (deflabel |Guiding the ACL2 Theorem Prover| :doc ":Doc-Section |Pages Written Especially for the Tours| Guiding the ACL2 Theorem Prover~/~/ ~walk[|ACL2 as an Interactive Theorem Prover (cont)|] Now that you have seen the theorem prover in action you might be curious as to how you guide it. ~gif[interactive-theorem-prover.gif] Look at the picture above. It is meant to suggest that ~i[Q] is an important lemma needed for the proof of ~i[P]. Note that to lead the prover to the proof of ~i[P] the user first proves ~i[Q]. In a way, the formulation and proof of ~i[Q] is a hint to the prover about how to prove ~i[P]. The user usually doesn't think of ~i[Q] or recognize the need to prove it separately until he or she sees the theorem prover ~b[fail] to prove ~i[P] without it ``knowing'' ~i[Q]. The way the user typically discovers the need for ~i[Q] is to look at failed proofs. ~walk[|ACL2 as an Interactive Theorem Prover (cont)|]") (deflabel |Rewrite Rules are Generated from DEFTHM Events| :doc ":Doc-Section |Pages Written Especially for the Tours| Rewrite Rules are Generated from DEFTHM Events~/~/ ~walk[|You Must Think about the Use of a Formula as a Rule|] By reading the documentation of ~ilc[defthm] ~warn[] (and especially of its :~il[rule-classes] ~warn[] argument) you would learn that when we submitted the command ~bv[] ~b[(defthm associativity-of-app] ~b[(equal (app (app a b) c)] ~b[(app a (app b c))))] ~ev[] we not only command the system to prove that ~c[app] is an associative function but ~bf[] * ~b[we commanded it to use that fact as a rewrite rule]. ~ef[] That means that every time the system encounters a term of the form ~bv[] (app (app ~b[x] ~b[y]) ~b[z]) ~ev[] it will replace it with ~bv[] (app ~b[x] (app ~b[y] ~b[z]))! ~ev[] ~walk[|You Must Think about the Use of a Formula as a Rule|]") (deflabel |You Must Think about the Use of a Formula as a Rule| :doc ":Doc-Section |Pages Written Especially for the Tours| You Must Think about the Use of a Formula as a Rule~/~/ ~walk[|Using the Associativity of App to Prove a Trivial Consequence|] This is ~b[good] and ~b[bad]. The good news is that you can ~b[program] ACL2's simplifier. The bad news is that when you command ACL2 to prove a theorem you must give some thought to ~b[how that theorem is to be used as a rule]! For example, if after proving ~c[associativity-of-app] as previously shown, you engaged in the mathematically trivial act of proving it again but with the equality reversed, you would have programmed ACL2's rewriter to loop forever. You can avoid adding any rule by using the command: ~bv[] (defthm associativity-of-app (equal (app (app a b) c) (app a (app b c))) ~b[:rule-classes nil]) ~ev[] ~walk[|Using the Associativity of App to Prove a Trivial Consequence|]") (deflabel |Using the Associativity of App to Prove a Trivial Consequence| :doc ":Doc-Section |Pages Written Especially for the Tours| Using the Associativity of App to Prove a Trivial Consequence~/~/ ~walk[|Overview of the Proof of a Trivial Consequence|] If we have proved the ~c[associativity-of-app] rule, then the following theorem is trivial: ~bv[] (defthm trivial-consequence (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7) (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7)))))) ~ev[] Below we show the proof ~walk[|Overview of the Proof of a Trivial Consequence|]") (deflabel |Overview of the Proof of a Trivial Consequence| :doc ":Doc-Section |Pages Written Especially for the Tours| Overview of the Proof of a Trivial Consequence~/~/ ~walk[|The End of the Walking Tour|] ~gif[green-line.gif] ~bv[] ACL2 !>~b[(defthm trivial-consequence] ~b[(equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7)] ~b[(app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7))))))] ~il[|The WARNING about the Trivial Consequence| ACL2 Warning] [Subsume] in ( DEFTHM TRIVIAL-CONSEQUENCE ...): The previously added rule ASSOCIATIVITY-OF-APP subsumes the newly proposed :REWRITE rule TRIVIAL-CONSEQUENCE, in the sense that the old rule rewrites a more general target. Because the new rule will be tried first, it may nonetheless find application. By the simple :rewrite rule ~il[|The First Application of the Associativity Rule| ASSOCIATIVITY-OF-APP] we reduce the conjecture to Goal' (EQUAL (APP X1 (APP X2 (APP X3 (APP X4 (APP X5 (APP X6 X7)))))) (APP X1 (APP X2 (APP X3 (APP X4 (APP X5 (APP X6 X7))))))). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. Summary Form: ( DEFTHM TRIVIAL-CONSEQUENCE ...) Rules: ((:REWRITE ASSOCIATIVITY-OF-APP) (:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: ~il[|The Summary of the Proof of the Trivial Consequence| Subsume] Time: 0.20 seconds (prove: 0.02, print: 0.00, other: 0.18) TRIVIAL-CONSEQUENCE ~ev[] ~gif[green-line.gif] You might explore the links before moving on. ~walk[|The End of the Walking Tour|]") (deflabel |The WARNING about the Trivial Consequence| :doc ":Doc-Section |Pages Written Especially for the Tours| The WARNING about the Trivial Consequence~/~/ This ~b[Warning] alerts us to the fact that when treated as a ~b[rewrite] rule, the new rule ~c[TRIVIAL-CONSEQUENCE], rewrites terms of the same form as a rule we have already proved, namely ~c[ASSOCIATIVITY-OF-APP]. When you see this warning you should ~b[think about your rules]! In the current case, it would be a good idea ~b[not] to make ~c[TRIVIAL-CONSEQUENCE] a rule at all. We could do this with ~c[:]~ilc[rule-classes] ~warn[] nil. ACL2 proceeds to try to prove the theorem, even though it printed some warnings. The basic assumption in ACL2 is that the ~b[user] ~b[understands what he or she is doing] but may need a little reminding just to manage a complicated set of facts.") (deflabel |The First Application of the Associativity Rule| :doc ":Doc-Section |Pages Written Especially for the Tours| The First Application of the Associativity Rule~/~/ So here we see our associativity rule being used! The rewriter sweeps the conjecture in a ~b[leftmost innermost] fashion, applying rewrite rules as it goes. The associativity rule is used many times in this sweep. The first ``target'' is highlighted below. Click on it to see what happens: ~bv[] ~b[Current Conjecture]: (equal (app (app ~il[|A Sketch of How the Rewriter Works| (app (app x1 x2) (app x3 x4))] (app x5 x6)) x7) (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7))))) ~ev[]") (deflabel |A Sketch of How the Rewriter Works| :doc ":Doc-Section |Pages Written Especially for the Tours| A Sketch of How the Rewriter Works~/~/ Below we show the first target term, extracted from the current conjecture. Below it we show the associativity rule. ~gif[uaa-rewrite.gif] The variables of the rewrite rule are ~b[instantiated] so that the ~b[left-hand side] of the rule matches the target: ~bv[] variable term from target a x1 b x2 c (app x3 x4) ~ev[] Then the target is ~b[replaced] by the instantiated ~b[right-hand side] of the rule. Sometimes rules have ~b[hypotheses]. To make a long story short, if the rule has hypotheses, then after matching the left-hand side, the rewriter instantiates the hypotheses and rewrites them recursively. This is called ~b[backchaining]. If they all rewrite to true, then the target is replaced as above. We discuss the rewriter in more detail in the extended introduction to how to use the theorem prover, ~pl[introduction-to-the-theorem-prover], which we will recommend you work through ~b[after] you have finished the two tours.") (deflabel |The Summary of the Proof of the Trivial Consequence| :doc ":Doc-Section |Pages Written Especially for the Tours| The Summary of the Proof of the Trivial Consequence~/~/ Note that at the conclusion of the proof, the system reminds you of the earlier ~b[Warning]. It is a good idea, when the ~b[Q.E.D.] flys by, to see if there were any Warnings.") (deflabel |The End of the Walking Tour| :doc ":Doc-Section |Pages Written Especially for the Tours| The End of the Walking Tour~/~/ ~gif[sitting.gif] This completes the Walking Tour. We intend to document many other parts of the system this way, but we just haven't gotten around to it. To start the two tours over again from the beginning, click on the icons below. If you are really interested in learning how to use ACL2, we recommend that you repeat each tour at least once more to explore branches of the tour that you might have missed. If you want to learn how to use the theorem prover, we now recommend that you devote the time necessary to work your way through the extended introduction to how to use the prover. ~l[introduction-to-the-theorem-prover]. This will explain how to interact with ACL2 and has some sample problems for you to solve including some challenge proofs to make ACL2 find. We hope you enjoy ACL2. We do. Matt Kaufmann and J Strother Moore ~fly[|A Flying Tour of ACL2|] ~walk[|A Walking Tour of ACL2|]") (deflabel |ACL2 is an Untyped Language| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 is an Untyped Language~/~/ The example ~bv[] ACL2 !>~b[(app '(a b c) 27)] (A B C . 27) ~ev[] illustrates the fact that ACL2's logic is untyped (~pclick-here[|About Types|] for a brief discussion of the typed versus untyped nature of the logic). The definition of ~c[app] makes no restriction of the arguments to lists. The definition says that if the first argument satisfies ~ilc[endp] ~warn[] then return the second argument. In this example, when ~c[app] has recursed three times down the ~c[cdr] of its first argument, ~c['(a b c)], it reaches the final ~c[nil], which satisfies ~c[endp], and so 27 is returned. It is naturally consed into the emerging list as the function returns from successive recursive calls (since ~c[cons] does not require its arguments to be lists, either). The result is an ``improper'' list, ~c[(a b c . 27)]. You can think of ~c[(app x y)] as building a binary tree by replacing the right-most tip of the tree ~c[x] with the tree ~c[y].") (deflabel |Hey Wait! Is ACL2 Typed or Untyped(Q)| :doc ":Doc-Section |Pages Written Especially for the Tours| Hey Wait! Is ACL2 Typed or Untyped?~/~/ The example ~bv[] ACL2 !>~b[(app 7 27)] ACL2 Error in TOP-LEVEL: The guard for the function symbol ENDP, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (ENDP 7). ~ev[] illustrates the fact that while ACL2 is an untyped language the ACL2 evaluator can be configured so as to check ``types'' at runtime. We should not say ``types'' here but ``guards.'' ~click-here[|Undocumented Topic|] for a discussion of guards. The guard on ~ilc[endp] ~warn[] requires its argument to be a true list. Since 7 is not a true list, and since ACL2 is checking guards in this example, an error is signaled by ACL2. How do you know ACL2 is checking guards? Because the prompt tells us (~pclick-here[|About the Prompt|]) with its ``!''.") (deflabel |Guards| :doc ":Doc-Section |Pages Written Especially for the Tours| Guards~/~/ Common Lisp functions are partial; they are not defined for all possible inputs. But ACL2 functions are total. Roughly speaking, the logical function of a given name in ACL2 is a ~b[completion] of the Common Lisp function of the same name obtained by adding some arbitrary but ``natural'' values on arguments outside the ``intended domain'' of the Common Lisp function. ACL2 requires that every ACL2 function symbol have a ``guard,'' which may be thought of as a predicate on the formals of the function describing the intended domain. The guard on the primitive function ~ilc[car] ~warn[], for example, is ~c[(or (consp x) (equal x nil))], which requires the argument to be either an ordered pair or ~c[nil]. We will discuss later how to specify a guard for a defined function; when one is not specified, the guard is ~c[t] which is just to say all arguments are allowed. ~b[But guards are entirely extra-logical]: they are not involved in the axioms defining functions. If you put a guard on a defined function, the defining axiom added to the logic defines the function on ~b[all] arguments, not just on the guarded domain. So what is the purpose of guards? The key to the utility of guards is that we provide a mechanism, called ``guard verification,'' for checking that all the guards in a formula are true. ~l[verify-guards]. This mechanism will attempt to prove that all the guards encountered in the evaluation of a guarded function are true every time they are encountered. For a thorough discussion of guards, see the paper [km97] in the ACL2 ~il[bibliography].") (deflabel |About the Prompt| :doc ":Doc-Section |Pages Written Especially for the Tours| About the Prompt~/ The string ``~c[ACL2 !>]'' is the ACL2 prompt.~/ The prompt tells the user that an ACL2 ~il[command] ~warn[]is expected. In addition, the prompt tells us a little about the current state of the ACL2 command interpreter. We explain the prompt briefly below. But first we talk about the command interpreter. An ACL2 command is generally a Lisp expression to be evaluated. There are some unusual commands (such as :~il[q] ~warn[] for ~b[quitting] ACL2) which cause other behavior. But most commands are read, evaluated, and then have their results printed. Thus, we call the command interpreter a ``read-eval-print loop.'' The ACL2 command interpreter is named ~ilc[LD] ~warn[] (after Lisp's ``load''). When a command is read, all the symbols in it are converted to uppercase. Thus, typing ~c[(defun app ...)] is the same as typing ~c[(DEFUN APP ...)] or ~c[(defun App ...)]. There are ways to force lowercase case characters into symbols but we won't discuss them here. A consequence of Common Lisp's default uppercasing is that you'll see a general lack of concern over the case used when symbols are displayed in this documentation. In addition, symbols ``belong'' to ``packages'' which give the user a way to control namespaces. The prompt tells us which package is the default one, namely ~c[\"ACL2\"]. That means when we call ~c[car], for example, we are invoking the standard definition of that symbol. If the packager were ~c[\"JONES\"] then ~c[car] would refer to the definition of that symbol in that package (which may or may not be different depending on what symbols were imported into that package. A command like ~b[(defun app (x y) ...)] causes ACL2 to evaluate the ~il[defun] ~warn[] function on ~b[app], ~b[(x y)] and ~b[...]. When that command is evaluated it prints some information to the terminal explaining the processing of the proposed definition. It returns the symbol ~c[APP] as its value, which is printed by the command interpreter. (Actually, ~c[defun] is not a function but a ~il[defmacro macro] ~warn[] which expands to a form that involves ~ilc[state] ~warn[], a necessary precondition to printing output to the terminal and to ``changing'' the set of axioms. But we do not discuss this further here.) The ~c[defun] command is an example of a special kind of command called an ``event.'' ~il[Events] ~warn[] are those commands that change the ``logical world'' by adding such things as axioms or theorems to ACL2's database. ~l[world] ~warn[]. But not every command is an event command. A command like ~b[(app '(1 2 3) '(4 5 6 7))] is an example of a non-event. It is processed the same general way: the function ~b[app] is applied to the indicated arguments and the result is printed. The function ~b[app] does not print anything and does not change the ``world.'' A third kind of command is one that display information about the current logical world or that ``roll back'' to previous versions of the world. Such commands are called ``~il[history]'' ~warn[] commands. What does the ACL2 prompt tell us about the read-eval-print loop? The prompt ``~c[ACL2 !>]'' tells us that the command will be read with ~ilc[current-package] ~warn[] set to ~c[\"ACL2\"], that guard checking (~pl[set-guard-checking] ~warn[]) is on (``~c[!]''), and that we are at the top-level (there is only one ``~c[>]''). For more about the prompt, ~pl[default-print-prompt] ~warn[]. You should now return to ~il[|Revisiting the Admission of App| the Walking Tour].") (deflabel |The Event Summary| :doc ":Doc-Section |Pages Written Especially for the Tours| The Event Summary~/~/ At the conclusion of most events (~pclick-here[|About the Prompt|] for a brief discussion of events or ~pl[events] ~warn[]), ACL2 prints a summary. The summary for ~c[app] is: ~bv[] Summary Form: ( DEFUN APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, other: 0.03) APP ~ev[] The ``rules'' listed are those used in function admission or proof summarized. What is actually listed are ``runes'' (~pl[rune]) ~warn[]) which are list-structured names for rules in the ACL2 database or ``~il[world]'' ~warn[]. Using ~il[theories] ~warn[] you can ``enable'' and ``disable'' rules so as to make them available (or not) to the ACL2 theorem prover. The ``warnings'' mentioned (none are listed for ~c[app]) remind the reader whether the event provoked any warnings. The warnings themselves would have been printed earlier in the processing and this part of the summary just names the earlier warnings printed. The ``time'' indicates how much processing time was used and is divided into three parts: the time devoted to proof, to printing, and to syntactic checks, pre-processing and database updates. Despite the fact that ACL2 is an applicative language it is possible to measure time with ACL2 programs. The ~ilc[state] ~warn[] contains a clock. The times are printed in decimal notation but are actually counted in integral units. Note that by default, each time is a runtime, also known as a cpu time, as opposed to being a real time, also known as a wall clock time. The final ~c[APP] is the value of the ~c[defun] command and was printed by the read-eval-print loop. The fact that it is indented one space is a subtle reminder that the command actually returned an ``error triple'', consisting of a flag indicating (in this case) that no error occurred, a value (in this case the symbol ~c[APP]), and the final ~ilc[state] ~warn[]). ~l[ld-post-eval-print] ~warn[] for some details. If you really want to follow that link, however, you might ~pl[ld] ~warn[] first. You should now return to ~il[|Revisiting the Admission of App| the Walking Tour].") (deflabel |About the Admission of Recursive Definitions| :doc ":Doc-Section |Pages Written Especially for the Tours| About the Admission of Recursive Definitions~/ You can't just add any formula as an axiom or definition and expect the logic to stay sound! For example, if we were permitted to define ~c[(APP X Y)] so that it was equal to ~c[(NOT (APP X Y))] then we could prove anything. The purported ``definition'' of ~c[APP] must have several properties to be admitted to the logic as a new axiom.~/ The key property a recursive definition must have is that the recursion terminate. This, along with some syntactic criteria, ensures us that there exists a function satisfying the definition. Termination must be proved before the definition is admitted. This is done in general by finding a measure of the arguments of the function and a well-founded relation such that the arguments ``get smaller'' every time a recursive branch is taken. For ~c[app] the measure is the ``size'' of the first argument, ~c[x], as determined by the primitive function ~ilc[acl2-count] ~warn[]. The well-founded relation used in this example is ~ilc[o-p] ~warn[], which is the standard ordering on the ordinals less than ``epsilon naught.'' These particular choices for ~c[app] were made ``automatically'' by ACL2. But they are in fact determined by various ``default'' settings. The user of ACL2 can change the defaults or specify a ``hint'' to the ~ilc[defun] ~warn[] command to specify the measure and relation. You should now return to ~il[|Revisiting the Admission of App| the Walking Tour].") (deflabel |About Types| :doc ":Doc-Section |Pages Written Especially for the Tours| About Types~/ The universe of ACL2 objects includes objects of many different types. For example, ~c[t] is a ``symbol'' and 3 is an ``integer.'' Roughly speaking the objects of ACL2 can be partitioned into the following types: ~bf[] ~il[|Numbers in ACL2| Numbers] ~c[3, -22/7, #c(3 5/2)] ~il[|ACL2 Characters| Characters] ~c[#\\A, #\\a, #\\Space] ~il[|ACL2 Strings| Strings] ~c[\"This is a string.\"] ~il[|ACL2 Symbols| Symbols] ~c['abc, 'smith::abc] ~il[|ACL2 Conses or Ordered Pairs| Conses (or Ordered Pairs)] ~c['((a . 1) (b . 2))] ~ef[]~/ When proving theorems it is important to know the types of object returned by a term. ACL2 uses a complicated heuristic algorithm, called ~ilc[type-set] ~warn[], to determine what types of objects a term may produce. The user can more or less program the ~c[type-set] algorithm by proving ~ilc[type-prescription] ~warn[] rules. ACL2 is an ``untyped'' logic in the sense that the syntax is not typed: It is legal to apply a function symbol of n arguments to any n terms, regardless of the types of the argument terms. Thus, it is permitted to write such odd expressions as ~c[(+ t 3)] which sums the symbol ~c[t] and the integer 3. Common Lisp does not prohibit such expressions. We like untyped languages because they are simple to describe, though proving theorems about them can be awkward because, unless one is careful in the way one defines or states things, unusual cases (like ~c[(+ t 3)]) can arise. To make theorem proving easier in ACL2, the axioms actually define a value for such terms. The value of ~c[(+ t 3)] is 3; under the ACL2 axioms, non-numeric arguments to ~c[+] are treated as though they were 0. You might immediately wonder about our claim that ACL2 is Common Lisp, since ~c[(+ t 3)] is ``an error'' (and will sometimes even ``signal an error'') in Common Lisp. It is to handle this problem that ACL2 has ~b[guards]. We will discuss guards later in the Walking Tour. However, many new users simply ignore the issue of guards entirely and that is what we recommend for now. You should now return to ~il[|Revisiting the Admission of App| the Walking Tour].") (deflabel |Numbers in ACL2| :doc ":Doc-Section |Pages Written Especially for the Tours| Numbers in ACL2~/~/ ACL2 numbers are precisely represented and unbounded. They can be partitioned into the following subtypes: ~bf[] Rationals Integers Positive integers ~c[3] Zero ~c[0] Negative Integers ~c[-3] Non-Integral Rationals Positive Non-Integral Rationals ~c[19/3] Negative Non-Integral Rationals ~c[-22/7] Complex Rational Numbers ~c[#c(3 5/2) ; = 3+(5/2)i] ~ef[] Signed integer constants are usually written (as illustrated above) as sequences of decimal digits, possibly preceded by ~c[+] or ~c[-]. Decimal points are not allowed. Integers may be written in binary, as in ~c[#b1011] (= 23) and ~c[#b-111] (= -7). Octal may also be used, ~c[#o-777] = -511. Non-integral rationals are written as a signed decimal integer and an unsigned decimal integer, separated by a slash. Complex rationals are written as #c(rpart ipart) where rpart and ipart are rationals. Of course, 4/2 = 2/1 = 2 (i.e., not every rational written with a slash is a non-integer). Similarly, #c(4/2 0) = #c(2 0) = 2. The common arithmetic functions and relations are denoted by ~c[+], ~c[-], ~c[*], ~c[/], ~c[=], ~c[<], ~c[<=], ~c[>] and ~c[>=]. However there are many others, e.g., ~c[floor], ~c[ceiling], and ~c[lognot]. We suggest you ~pl[programming] ~warn[] where we list all of the primitive ACL2 functions. Alternatively, see any Common Lisp language documentation. The primitive predicates for recognizing numbers are illustrated below. The following ACL2 function will classify an object, x, according to its numeric subtype, or else return 'NaN (not a number). We show it this way just to illustrate programming in ACL2. ~bv[] (defun classify-number (x) (cond ((rationalp x) (cond ((integerp x) (cond ((< 0 x) 'positive-integer) ((= 0 x) 'zero) (t 'negative-integer))) ((< 0 x) 'positive-non-integral-rational) (t 'negative-non-integral-rational))) ((complex-rationalp x) 'complex-rational) (t 'NaN))) ~ev[]") (deflabel |ACL2 Characters| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 Characters~/~/ ACL2 accepts 256 distinct characters, which are the characters obtained by applying the function ~ilc[code-char] ~warn[] to each integer from 0 to 255. Among these, Common Lisp designates certain ones as ~c[*standard-characters*], namely those of the form ~c[(code-char n)] where n is from 33 to 126, together with ~c[#\\Newline] and ~c[#\\Space]. The actual standard characters may be viewed by evaluating the constant expression ~c[*standard-chars*]. The standard character constants are written by writing a hash mark followed by a backslash (#\\) followed by the character. The function ~ilc[characterp] ~warn[] recognizes characters. For more details, ~l[characters] ~warn[].") (deflabel |ACL2 Strings| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 Strings~/~/ Strings of ACL2 ~il[|ACL2 Characters| characters] are written as sequences of characters delimited by ``double quotation marks'' (\"). To put a double quotation mark in a string (or, any other character such as backslash or newline that seems to cause problems), escape it by preceding it with a backslash (\\). The function ~ilc[stringp] ~warn[] recognizes strings and ~ilc[char] ~warn[] will fetch the nth character of a string. There are many other primitives for handling strings, such as ~ilc[string<] ~warn[] for comparing two strings lexicographically. We suggest you ~l[programming] ~warn[] where we list all of the primitive ACL2 functions. Alternatively, see any Common Lisp language documentation.") (deflabel |ACL2 Symbols| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 Symbols~/~/ Common Lisp's symbols are a data type representing words. They are frequently regarded as atomic objects in the sense that they are not frequently broken down into their constituents. Often the only important properties of symbols is that they are not numbers, characters, strings, or lists and that two symbols are not equal if they look different (!). Examples of symbols include ~c[PLUS] and ~c[SMITH::ABC]. All function and variable names in ACL2 are symbols. When symbols are used as constants they must be quoted, as in ~c['PLUS]. The symbol ~c[T] is commonly used as the Boolean ``true.'' The symbol ~c[NIL] is commonly used both as the Boolean ``false'' and as the ``empty list.'' Despite sometimes being called the ``empty list'' ~c[NIL] is a ~b[symbol] not an ``empty cons.'' Unlike other symbols, ~c[T] and ~c[NIL] may be used as constants without quoting them. Usually, symbols are written as sequences of alphanumeric characters other than those denoting numbers. Thus, ~c[A12], ~c[+1A] and ~c[1+] are symbols but ~c[+12] is a number. Roughly speaking, when symbols are read lower case characters are converted to upper case, so we frequently do not distinguish ~c[ABC] from ~c[Abc] or ~c[abc]. ~click-here[|Conversion|] for information about case conversion when symbols are read. However, any character can be used in a symbol, but some characters must be ``escaped'' to allow the Lisp reader to parse the sequence as a symbol. For example, ~c[|Abc|] is a symbol whose first character is capitalized and whose remaining characters are in lower case. ~c[|An odd duck|] is a symbol containing two #\\Space characters. See any Common Lisp documentation for the syntactic rules for symbols. Technically, a symbol is a special kind of pair consisting of a package name (which is a string) and a symbol name (which is also a string). (~l[symbol-package-name] ~warn[] and ~pl[symbol-name] ~warn[].) The symbol SMITH::ABC is said to be in package \"SMITH\" and to have the symbol name \"ABC\". The symbol ~c[ABC] in package \"SMITH\" is generally not equal to the symbol ~c[ABC] in package \"JONES\". However, it is possible to ``import'' symbols from one package into another one, but in ACL2 this can only be done when the package is created. (~l[defpkg] ~warn[].) If the ~ilc[current-package] ~warn[] is \"SMITH\" then ~c[SMITH::ABC] may be more briefly written as just ~c[ABC]. ~ilc[Intern] ~warn[] ``creates'' a symbol of a given name in a given package.") (deflabel |ACL2 Conses or Ordered Pairs| :doc ":Doc-Section |Pages Written Especially for the Tours| ACL2 Conses or Ordered Pairs~/~/ The function ~ilc[cons] ~warn[] creates an ordered pair. ~ilc[Car] ~warn[] and ~ilc[cdr] ~warn[] return the first and second components, respectively, of an ordered pair. The function ~ilc[consp] ~warn[] recognizes ordered pairs. Ordered pairs are used to represent lists and trees. See any Common Lisp documentation for a discussion of how list constants are written and for the many list processing functions available. Also, ~pl[programming] ~warn[] where we list all the ACL2 primitive functions. Here are some examples of list constants to suggest their syntax. ~bv[] '(a . b) ; a pair whose car is 'a and cdr is 'b '(a . nil) ; a pair whose car is 'a and cdr is nil '(a) ; another way to write the same thing '(a b) ; a pair whose car is 'a and cdr is '(b) '(a b c) ; a pair whose car is 'a and cdr is '(b c) ; i.e., a list of three symbols, a, b, and c. '((a . 1) (b . 2)) ; a list of two pairs ~ev[] It is useful to distinguish ``proper'' conses from ``improper'' ones, the former being those cons trees whose right-most branch terminates with ~c[nil]. A ``true list'' (~pl[true-listp] ~warn[]) is either ~c[nil] or a proper cons. ~c[(A b c . 7)] is an improper cons and hence not a true list.") (deflabel |Guessing the Type of a Newly Admitted Function| :doc ":Doc-Section |Pages Written Especially for the Tours| Guessing the Type of a Newly Admitted Function~/~/ When a function is admitted to the logic, ACL2 tries to ``guess'' what type of object it returns. This guess is codified as a term that expresses a property of the value of the function. For ~c[app] the term is ~bv[] (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)) ~ev[] which says that ~c[app] returns either a cons or its second argument. This formula is added to ACL2's rule base as a ~ilc[type-prescription] ~warn[] rule. Later we will discuss how rules are used by the ACL2 theorem prover. The point here is just that when you add a definition, the database of rules is updated, not just by the addition of the definitional axiom, but by several new rules. You should now return to ~il[|Revisiting the Admission of App| the Walking Tour].") ; Essay on Metafunction Support, Part 2 ; For the first part of this essay, see ``Metafunction Support, Part ; 1'' in axioms.lisp. This code is here at the end of ld so that it ; can use all our utilities and functions. ; We here turn to the problem of defining the uninterpreted functions ; that can actually be executed within a meta-level function. Review Part 1 of ; the essay for the background and basic strategy. We take up from there. ; Note: You can add other uninterpreted functions linked to theorem ; prover :program functions. However, you should obey the following ; rules. ; (1) Of course, the metafunction context must be rich enough (or made ; rich enough) to provide the necessary arguments. If you change the ; structure of metafunction-context, you must modify the accessors ; defined above mfc-clause, in axioms.lisp, or else the build will fail ; with a redefinition error. ; (2) Include STATE as an argument to the uninterpreted symbol, ; whether it is otherwise needed or not. (defconst *meta-level-function-problem-1* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to the ~ non-term ~x1. The meta-level function computation was ignored.~%~%") (defconst *meta-level-function-problem-1a* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to an ~ alist argument with ~@1. The meta-level function computation was ignored.~%~%") (defconst *meta-level-function-problem-1b* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to ~ the non-rune ~x1 for the rune argument. The meta-level function ~ computation was ignored.~%~%") (defconst *meta-level-function-problem-1c* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to ~ the expression ~x1 for the target argument. This expression must be a ~ term that is the application of a function symbol; but it is not. The ~ meta-level function computation was ignored.~%~%") (defconst *meta-level-function-problem-1d* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to ~ the rune ~x1 and the target ~x2. This is illegal, because there is no ~ rewrite, definition, meta, or linear lemma named ~x1 whose top-level ~ function symbol is ~x3. The meta-level function computation was ~ ignored.~%~%") (defconst *meta-level-function-problem-1e* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to ~ the ~x1 for the bkptr argument, which is not a valid one-based index into ~ the hypothesis list of the lemma named by rune ~x2. The meta-level ~ function computation was ignored.~%~%") (defconst *meta-level-function-problem-2* "~%~%Meta-level function Problem: Some meta-level function applied ~x0 to a ~ context different from the one passed to the meta-level function ~ itself. We cannot authenticate manufactured contexts. The ~ manufactured context was ~X12. The meta-level function computation ~ was ignored.~%~%") (defconst *meta-level-function-problem-3* "~%~%Meta-level function Problem: You or some meta-level function applied ~x0 but not ~ from within the theorem prover's meta-level function handler. This ~ suggests you are trying to test a meta-level function and have evidently ~ manufactured an allegedly suitable context. Perhaps so. But that ~ is so difficult to check that we don't bother. Instead we cause ~ this error and urge you to test your meta-level function by having the ~ meta-level function handler invoke it as part of a test proof-attempt. To ~ do this, assume the metatheorem that you intend eventually to ~ prove. You may do this by executing the appropriate DEFTHM event ~ embedded in a SKIP-PROOFS form. Then use THM to submit ~ conjectures for proof and observe the behavior of your ~ metafunction. Remember to undo the assumed metatheorem before you ~ attempt genuine proofs! If this suggestion isn't applicable to ~ your situation, contact the authors.~%~%") ; We next introduce uninterpreted :logic mode functions with ; execute-only-in-meta-level-functions semantics, as per defun-overrides calls ; for mfc-ts-fn and such. (defun acl2-magic-mfc (x) ; This function is a sort of placeholder, used in a ; define-trusted-clause-processor event for noting that various mfc functions ; have unknown constraints. (declare (xargs :guard t)) (list x)) #+acl2-loop-only (encapsulate () (define-trusted-clause-processor acl2-magic-mfc (mfc-ts-fn mfc-ts-ttree mfc-rw-fn mfc-rw-ttree mfc-rw+-fn mfc-rw+-ttree mfc-relieve-hyp-fn mfc-relieve-hyp-ttree mfc-ap-fn) :partial-theory (encapsulate (((mfc-ap-fn * * state *) => *) ((mfc-relieve-hyp-fn * * * * * * state *) => *) ((mfc-relieve-hyp-ttree * * * * * * state *) => (mv * *)) ((mfc-rw+-fn * * * * * state *) => *) ((mfc-rw+-ttree * * * * * state *) => (mv * *)) ((mfc-rw-fn * * * * state *) => *) ((mfc-rw-ttree * * * * state *) => (mv * *)) ((mfc-ts-fn * * state *) => *) ((mfc-ts-ttree * * state *) => (mv * *))) (logic) (set-ignore-ok t) (set-irrelevant-formals-ok t) (local (defun mfc-ts-fn (term mfc state forcep) t)) (local (defun mfc-ts-ttree (term mfc state forcep) (mv t t))) (local (defun mfc-rw-fn (term obj equiv-info mfc state forcep) t)) (local (defun mfc-rw-ttree (term obj equiv-info mfc state forcep) (mv t t))) (local (defun mfc-rw+-fn (term alist obj equiv-info mfc state forcep) t)) (local (defun mfc-rw+-ttree (term alist obj equiv-info mfc state forcep) (mv t t))) (local (defun mfc-relieve-hyp-fn (hyp alist rune target bkptr mfc state forcep) t)) (local (defun mfc-relieve-hyp-ttree (hyp alist rune target bkptr mfc state forcep) (mv t t))) (local (defun mfc-ap-fn (term mfc state forcep) t))))) #-acl2-loop-only (progn (defun mfc-ts-raw (term mfc state forcep) (declare (xargs :guard (state-p state))) ; Type-set doesn't really use state. We originally used the presence of the ; live state as authorization to execute, believing that the live state object ; cannot arise in an execution on behalf of an evaluation of a subexpression in ; a theorem or proof. We now know that this is not the case; see the ; "soundness bug involving system function canonical-pathname" in :doc ; note-6-1. However, we keep state around for legacy reasons. If a reason is ; brought to our attention why it would be useful to remove state as a ; parameter, we can consider doing so. (let ((ev-fncall-val `(ev-fncall-null-body-er nil mfc-ts ,term mfc state))) (cond ((not (live-state-p state)) ; This function acts like an undefined function unless it is applied to the ; live state. See comment above. (throw-raw-ev-fncall ev-fncall-val)) (*metafunction-context* ; We are within the application of a meta-level function by the theorem prover. (cond ((eq mfc *metafunction-context*) (cond ((termp term (access metafunction-context mfc :wrld)) ; At this point we can code freely. In general, any data used below ; (i.e., any actuals passed in above) must be vetted as shown above. ; There is absolutely no reason to believe that the user has called ; mfc-ts correctly, even in a verified meta-level function and we must defend ; against hard errors. ; Note by the way that even though we have access to the mfc-ancestors, we do ; not use this data. The reason is that type-set does not use the ancestors ; provided by the rewriter either. Put another way: Type-set does not take ; ancestors as an argument, and calls of type-set-rec occur (at least as of ; this writing) only in the mutual-recursion nest where type-set is defined. (type-set term (mfc-force-flg forcep mfc) nil ;;; dwp (access metafunction-context mfc :type-alist) (access rewrite-constant (access metafunction-context mfc :rcnst) :current-enabled-structure) (access metafunction-context mfc :wrld) nil ;;; ttree nil nil)) (t (cw *meta-level-function-problem-1* 'mfc-ts term) (throw-raw-ev-fncall ev-fncall-val)))) (t (cw *meta-level-function-problem-2* 'mfc-ts mfc (abbrev-evisc-tuple *the-live-state*)) (throw-raw-ev-fncall ev-fncall-val)))) ; We are not within the application of a meta-level function by the theorem ; prover. We don't actually know if we are in the theorem prover. This could ; be a proof-time evaluation of a subterm of a conjecture about MFC-TS (e.g., ; the proof of the metatheorem justifying a metafunction using MFC-TS, or the ; proof of a lemma involved in that metatheorem proof). Or, this could be a ; top-level call of MFC-TS or some function using it, as part of the user's ; testing of a meta-level function's development. (*hard-error-returns-nilp* ; This evaluation is part of a conjecture being proved. Quietly act as though ; mfc-ts is an undefined function. It is believed that this can never happen, ; because STATE is live. (throw-raw-ev-fncall ev-fncall-val)) (t ; This is a top-level call of mfc-ts or some function using it. Cause an error ; no matter what context the user has supplied. See the error message. (cw *meta-level-function-problem-3* 'mfc-ts) (throw-raw-ev-fncall ev-fncall-val))))) (defun mfc-rw-raw (term alist obj equiv-info mfc fn state forcep) (declare (xargs :guard (state-p state))) (let ((ev-fncall-val `(ev-fncall-null-body-er nil mfc-rw-raw ,term ,alist ',obj ,equiv-info mfc ,fn state))) (cond ((not (live-state-p state)) (throw-raw-ev-fncall ev-fncall-val)) (*metafunction-context* (cond ((eq mfc *metafunction-context*) (let ((wrld (access metafunction-context mfc :wrld)) (rcnst (access metafunction-context mfc :rcnst))) (cond ((not (termp term wrld)) (cw *meta-level-function-problem-1* fn term) (throw-raw-ev-fncall ev-fncall-val)) ((let ((msg (term-alistp-failure-msg alist wrld))) (when msg (cw *meta-level-function-problem-1a* fn msg) (throw-raw-ev-fncall ev-fncall-val)))) ((member-eq obj '(t nil ?)) (sl-let (rw ttree) (let ((gstack (access metafunction-context mfc :gstack)) (rcnst (update-rncst-for-forcep forcep rcnst))) (rewrite-entry (rewrite term alist 'meta) :rdepth (access metafunction-context mfc :rdepth) :step-limit (initial-step-limit wrld state) :type-alist (access metafunction-context mfc :type-alist) :geneqv (cond ((eq equiv-info t) *geneqv-iff*) ((eq equiv-info nil) nil) ; nil means EQUAL ((and (symbolp equiv-info) (equivalence-relationp equiv-info wrld) (car (geneqv-lst equiv-info nil (access rewrite-constant rcnst :current-enabled-structure) wrld)))) (t (prog2$ (or (congruence-rule-listp equiv-info wrld) (er hard! fn "~x0 has been passed an ~ equiv-info argument that is ~ neither t, nil, a known ~ equivalence relation, nor a ~ list of congruence rules:~| ~ ~x1" fn equiv-info)) equiv-info))) :wrld wrld :fnstack (access metafunction-context mfc :fnstack) :ancestors (access metafunction-context mfc :ancestors) :backchain-limit (access metafunction-context mfc :backchain-limit) :simplify-clause-pot-lst (access metafunction-context mfc :simplify-clause-pot-lst) :rcnst rcnst :gstack gstack :ttree nil)) (declare (ignore step-limit)) (mv rw ttree))) (t (cw "~%~%Meta-level function Problem: Some meta-level function ~ called ~x0 with the OBJ argument set to ~x1. That ~ argument must be one of the three symbols ?, T, or NIL." fn obj) (throw-raw-ev-fncall ev-fncall-val))))) (t (cw *meta-level-function-problem-2* fn mfc (abbrev-evisc-tuple *the-live-state*)) (throw-raw-ev-fncall ev-fncall-val)))) (*hard-error-returns-nilp* (throw-raw-ev-fncall ev-fncall-val)) (t (cw *meta-level-function-problem-3* fn) (throw-raw-ev-fncall ev-fncall-val))))) (defun mfc-relieve-hyp-raw (hyp alist rune target bkptr mfc state forcep) ; We ignore issues concerning memoization and free variables below. ; As we gain experience with the use of this function, we may want ; to reconsider this. (declare (xargs :guard (state-p state))) (let ((ev-fncall-val `(ev-fncall-null-body-er nil mfc-relieve-hyp ,hyp ,alist ,rune ,target ,bkptr mfc state))) (cond ((not (live-state-p state)) (throw-raw-ev-fncall ev-fncall-val)) (*metafunction-context* (cond ((eq mfc *metafunction-context*) (let ((wrld (access metafunction-context mfc :wrld)) (rcnst (access metafunction-context mfc :rcnst)) (ancestors (access metafunction-context mfc :ancestors))) (cond ((not (termp hyp wrld)) (cw *meta-level-function-problem-1* 'mfc-relieve-hyp hyp) (throw-raw-ev-fncall ev-fncall-val)) ((let ((msg (term-alistp-failure-msg alist wrld))) (when msg (cw *meta-level-function-problem-1a* 'mfc-relieve-hyp msg) (throw-raw-ev-fncall ev-fncall-val)))) ((not (runep rune wrld)) (cw *meta-level-function-problem-1b* 'mfc-relieve-hyp rune) (throw-raw-ev-fncall ev-fncall-val)) ((not (and (termp target wrld) (nvariablep target) (not (fquotep target)) (symbolp (ffn-symb target)))) (cw *meta-level-function-problem-1c* 'mfc-relieve-hyp target) (throw-raw-ev-fncall ev-fncall-val)) (t (let* ((linearp (eq (car rune) :linear)) (lemmas (getprop (ffn-symb target) (if linearp 'linear-lemmas 'lemmas) nil 'current-acl2-world wrld)) (lemma (if linearp (find-runed-linear-lemma rune lemmas) (find-runed-lemma rune lemmas)))) (cond ((null lemma) (cw *meta-level-function-problem-1d* 'mfc-relieve-hyp rune target (ffn-symb target)) (throw-raw-ev-fncall ev-fncall-val)) ((not (and (posp bkptr) (<= bkptr (length (if linearp (access linear-lemma lemma :hyps) (access rewrite-rule lemma :hyps)))))) (cw *meta-level-function-problem-1e* 'mfc-relieve-hyp bkptr rune) (throw-raw-ev-fncall ev-fncall-val))) (sl-let (wonp failure-reason new-unify-subst ttree memo) (rewrite-entry (relieve-hyp rune target hyp alist bkptr nil) :rdepth (access metafunction-context mfc :rdepth) :step-limit (initial-step-limit wrld state) :type-alist (access metafunction-context mfc :type-alist) :obj nil ; ignored by relieve-hyp :geneqv nil ; ignored by relieve-hyp :wrld wrld :fnstack (access metafunction-context mfc :fnstack) :ancestors ancestors :backchain-limit (new-backchain-limit (if (and (not linearp) (eq (access rewrite-rule lemma :subclass) 'meta)) (access rewrite-rule lemma :backchain-limit-lst) (nth (1- bkptr) (if linearp (access linear-lemma lemma :backchain-limit-lst) (access rewrite-rule lemma :backchain-limit-lst)))) (access metafunction-context mfc :backchain-limit) ancestors) :simplify-clause-pot-lst (access metafunction-context mfc :simplify-clause-pot-lst) :rcnst (update-rncst-for-forcep forcep rcnst) :gstack (access metafunction-context mfc :gstack) :ttree nil) (declare (ignore step-limit failure-reason new-unify-subst memo)) (if (member-eq wonp '(t :unify-subst-list)) (mv t ttree) (mv nil nil)))))))) (t (cw *meta-level-function-problem-2* 'mfc-relieve-hyp mfc (abbrev-evisc-tuple *the-live-state*)) (throw-raw-ev-fncall ev-fncall-val)))) (*hard-error-returns-nilp* (throw-raw-ev-fncall ev-fncall-val)) (t (cw *meta-level-function-problem-3* 'mfc-relieve-hyp) (throw-raw-ev-fncall ev-fncall-val))))) (defun-one-output mfc-ap-raw (term mfc state forcep) (declare (xargs :guard (state-p state))) (let ((ev-fncall-val `(ev-fncall-null-body-er nil mfc-ap ,term mfc state))) (cond ((not (live-state-p state)) (throw-raw-ev-fncall ev-fncall-val)) (*metafunction-context* (cond ((eq mfc *metafunction-context*) (cond ((termp term (access metafunction-context mfc :wrld)) (let* ((force-flg (mfc-force-flg forcep mfc)) (linearized-list (linearize term t ;;; positivep (access metafunction-context mfc :type-alist) (access rewrite-constant (access metafunction-context mfc :rcnst) :current-enabled-structure) force-flg (access metafunction-context mfc :wrld) nil ;;; ttree state))) (cond ((null linearized-list) nil) ((null (cdr linearized-list)) (mv-let (contradictionp new-arith-db) (add-polys (car linearized-list) (access metafunction-context mfc :simplify-clause-pot-lst) (access rewrite-constant (access metafunction-context mfc :rcnst) :pt) (access rewrite-constant (access metafunction-context mfc :rcnst) :nonlinearp) (access metafunction-context mfc :type-alist) (access rewrite-constant (access metafunction-context mfc :rcnst) :current-enabled-structure) force-flg (access metafunction-context mfc :wrld)) (declare (ignore new-arith-db)) contradictionp)) (t (mv-let (contradictionp1 new-arith-db) (add-polys (car linearized-list) (access metafunction-context mfc :simplify-clause-pot-lst) (access rewrite-constant (access metafunction-context mfc :rcnst) :pt) (access rewrite-constant (access metafunction-context mfc :rcnst) :nonlinearp) (access metafunction-context mfc :type-alist) (access rewrite-constant (access metafunction-context mfc :rcnst) :current-enabled-structure) force-flg (access metafunction-context mfc :wrld)) (declare (ignore new-arith-db)) (if contradictionp1 (mv-let (contradictionp2 new-arith-db) (add-polys (cadr linearized-list) (access metafunction-context mfc :simplify-clause-pot-lst) (access rewrite-constant (access metafunction-context mfc :rcnst) :pt) (access rewrite-constant (access metafunction-context mfc :rcnst) :nonlinearp) (access metafunction-context mfc :type-alist) (access rewrite-constant (access metafunction-context mfc :rcnst) :current-enabled-structure) force-flg (access metafunction-context mfc :wrld)) (declare (ignore new-arith-db)) contradictionp2) nil)))))) (t (cw *meta-level-function-problem-1* 'mfc-ap term) (throw-raw-ev-fncall ev-fncall-val)))) (t (cw *meta-level-function-problem-2* 'mfc-ap mfc (abbrev-evisc-tuple *the-live-state*)) (throw-raw-ev-fncall ev-fncall-val)))) (*hard-error-returns-nilp* (throw-raw-ev-fncall ev-fncall-val)) (t (cw *meta-level-function-problem-3* 'mfc-ap) (throw-raw-ev-fncall ev-fncall-val))))) ) (defmacro mfc-ts (term mfc st &key (forcep ':same) ttreep) (declare (xargs :guard (and (member-eq forcep '(t nil :same)) (booleanp ttreep)))) (if ttreep `(mfc-ts-ttree ,term ,mfc ,st ,forcep) `(mfc-ts-fn ,term ,mfc ,st ,forcep))) (defmacro mfc-rw (term obj equiv-info mfc st &key (forcep ':same) ttreep) ; We introduced mfc-rw+ after Version_3.0.1. It was tempting to eliminate ; mfc-rw altogether (and then use the name mfc-rw for what we now call ; mfc-rw+), but we decided to leave mfc-rw unchanged for backward ; compatibility. Worth mentioning: An attempt to replace mfc-rw by ; corresponding calls of mfc-rw+ in community book books/arithmetic-3/ resulted ; in a failed proof (of floor-floor-integer in community book ; books/arithmetic-3/floor-mod/floor-mod.lisp). (declare (xargs :guard (and (member-eq forcep '(t nil :same)) (booleanp ttreep)))) (if ttreep `(mfc-rw-ttree ,term ,obj ,equiv-info ,mfc ,st ,forcep) `(mfc-rw-fn ,term ,obj ,equiv-info ,mfc ,st ,forcep))) (defmacro mfc-rw+ (term alist obj equiv-info mfc st &key (forcep ':same) ttreep) (declare (xargs :guard (and (member-eq forcep '(t nil :same)) (booleanp ttreep)))) (if ttreep `(mfc-rw+-ttree ,term ,alist ,obj ,equiv-info ,mfc ,st ,forcep) `(mfc-rw+-fn ,term ,alist ,obj ,equiv-info ,mfc ,st ,forcep))) (defmacro mfc-relieve-hyp (hyp alist rune target bkptr mfc st &key (forcep ':same) ttreep) (declare (xargs :guard (and (member-eq forcep '(t nil :same)) (booleanp ttreep)))) (if ttreep `(mfc-relieve-hyp-ttree ,hyp ,alist ,rune ,target ,bkptr ,mfc ,st ,forcep) `(mfc-relieve-hyp-fn ,hyp ,alist ,rune ,target ,bkptr ,mfc ,st ,forcep))) (defmacro mfc-ap (term mfc st &key (forcep ':same)) (declare (xargs :guard (member-eq forcep '(t nil :same)))) `(mfc-ap-fn ,term ,mfc ,st ,forcep)) (defun congruence-rule-listp (x wrld) (if (atom x) (null x) (and (let ((rule (car x))) (case-match rule ((nume equiv . rune) (and (equivalence-relationp equiv wrld) (or (runep rune wrld) (equal rune *fake-rune-for-anonymous-enabled-rule*)) (eql (fnume rune wrld) nume))))) (congruence-rule-listp (cdr x) wrld)))) (defun term-alistp-failure-msg (alist wrld) ; Returns nil if alist is an alist binding variables to terms. Otherwise, ; returns a message suitable for use in *meta-level-function-problem-1a*. (cond ((atom alist) (and alist (msg "a non-nil final cdr"))) ((atom (car alist)) (msg "a non-consp element, ~x0" (car alist))) ((not (and (termp (caar alist) wrld) (variablep (caar alist)))) (msg "an element, ~p0, whose car is not a variable" (caar alist))) ((not (termp (cdar alist) wrld)) (msg "an element, ~p0, whose cdr is not a term" (cdar alist))) (t (term-alistp-failure-msg (cdr alist) wrld)))) (defun find-runed-linear-lemma (rune lst) ; Lst must be a list of lemmas. We find the first one with :rune rune (but we ; make no assumptions on the form of rune). (cond ((null lst) nil) ((equal rune (access linear-lemma (car lst) :rune)) (car lst)) (t (find-runed-linear-lemma rune (cdr lst))))) (defun mfc-force-flg (forcep mfc) (cond ((eq forcep :same) (ok-to-force (access metafunction-context mfc :rcnst))) (t forcep))) (defun update-rncst-for-forcep (forcep rcnst) (cond ((or (eq forcep :same) (iff forcep (ok-to-force rcnst))) rcnst) (t (change rewrite-constant rcnst :force-info (if forcep t 'weak))))) ; Essay on Saved-output ; Starting with Version_2.9.2, ACL2 has the capability of running not only with ; output inhibited but also with output saved, to be printed upon demand by pso ; and pso! (see their documentation). This capability is controlled by state ; global variables whose names start with SAVED-OUTPUT-, namely: ; 'saved-output-reversed, 'saved-output-token-lst, and 'saved-output-p. State ; global 'print-clause-ids was also introduced at the same time, in order to ; allow printing of clause ids with output inhibited in order that the user can ; observe progress of the proof. ; Why do we need both 'saved-output-p and 'saved-output-token-lst? The latter ; records the output that the user wants saved (typically, :all or nil). The ; former activates the saving of output, which is why it is bound to t in ; with-ctx-summarized. The idea is that we do not want to save output that ; comes from top-level calls by the user that are not event forms, so ; 'saved-output-p remains nil at the top level. ; Perhaps we should add a mechanical check that there are no nested calls of ; io?, since such calls could confuse our mechanism for saving output. ; Implementation note: Calls of io? on a given body take as an argument a ; listing of all the free variables of that body. After the definitions below, ; a macro call (av body) will print out such a list. ; (defun all-vars-untrans (form state) ; (declare (xargs :mode :program :stobjs state)) ; (mv-let (erp val bindings state) ; (translate1 form ; :stobjs-out ; '((:stobjs-out . :stobjs-out)) ; t 'top-level ; (w state) state) ; (declare (ignore erp bindings)) ; (value (remove1-eq 'state (all-vars val))))) ; ; (defmacro av (form) ; `(all-vars-untrans ',form state)) (defun trans-eval-lst (lst ctx state aok) (cond ((endp lst) (value :invisible)) (t (er-progn (trans-eval (car lst) ctx state aok) (trans-eval-lst (cdr lst) ctx state aok))))) (defun print-saved-output (inhibit-output-lst gag-mode state) (let ((saved-output (reverse (io-record-forms (f-get-global 'saved-output-reversed state)))) (channel (standard-co state)) (ctx 'print-saved-output)) (cond ((or (null saved-output) (and (null (cdr saved-output)) (eq (access io-record (car (f-get-global 'saved-output-reversed state)) :io-marker) :ctx))) (er-progn (if saved-output (trans-eval (car saved-output) ctx state t) (value nil)) (pprogn (fms "There is no saved output to print. ~ See :DOC set-saved-output.~|" nil channel state nil) (value :invisible)))) (t (let ((old-gag-state (f-get-global 'gag-state state))) (state-global-let* ((saved-output-reversed nil) ; preserve this (value doesn't matter) (inhibit-output-lst inhibit-output-lst) (gag-mode gag-mode) (gag-state-saved (f-get-global 'gag-state-saved state))) (pprogn (initialize-summary-accumulators state) (save-event-state-globals (pprogn (if old-gag-state state ; Otherwise we set gag-state to nil after saving the gag-state in ; gag-state-saved. (f-put-global 'gag-state (f-get-global 'gag-state-saved state) state)) (state-global-let* ((saved-output-p nil)) (trans-eval-lst saved-output ctx state t))))))))))) (defmacro pso () ":Doc-Section Other show the most recently saved output~/ Evaluate ~c[:pso] in order to print output that was generated in an environment where output was being saved; ~pl[set-saved-output] for details. However, ~il[proof-tree] output will be suppressed; use ~c[:]~ilc[pso!] if you want that output to be printed as well.~/ Also ~pl[psog], for printing saved output in ~il[gag-mode].~/" '(print-saved-output '(proof-tree) nil state)) (defmacro psog () ":Doc-Section Other show the most recently saved output in ~il[gag-mode]~/ Evaluate ~c[:psog] in order to print output in ~il[gag-mode] that was generated in an environment where output was being saved; ~pl[set-saved-output] for details.~/ Also ~pl[pso] and ~pl[pso!] for printing the full output.~/" '(print-saved-output '(proof-tree) t state)) (defmacro pso! () ":Doc-Section Other show the most recently saved output, including ~il[proof-tree] output~/ Evaluate ~c[:pso!] in order to print output that was generated in an environment where output was being saved; ~pl[set-saved-output] for details. Note that ~il[proof-tree] will be included; use ~c[:]~ilc[pso] if you want that output to be suppressed.~/ Also ~pl[psog], for printing saved output in ~il[gag-mode].~/" '(print-saved-output nil nil state)) (defdoc nil-goal ":Doc-Section Miscellaneous how to proceed when the prover generates a goal of ~c[nil]~/ At the end of a failed proof, one typically sees so-called ``key checkpoints'' (~pl[set-gag-mode]). These may be annotated as follows. ~bv[] [NOTE: A goal of NIL was generated. See :DOC nil-goal.] ~ev[] This ~il[documentation] topic gives some ideas about how to think about the situation described by that message: some goal has reduced to ~c[nil]. Suppose then that you see the above NOTE. If you look back at the proof log, even with ~il[gag-mode] enabled, you will see a message saying that a goal of ~c[NIL] ``has been generated''. This may indicate that the original goal is not a theorem, since most of the prover's activity is to replace a goal by an equivalent conjunction of its child subgoals. However, if some ancestor of the ~c[nil] goal has undergone a process other than simplification or destructor elimination ~-[] fertilization (heuristic use of equalities), generalization, or elimination of irrelevance ~-[] then it is quite possible that the prover got to the ~c[nil] goal by replacing a goal by a stronger (and perhaps false) conjunction of child subgoals. At present, if you are using ~il[gag-mode] (the default) then you will need to issue the command ~c[:]~ilc[pso] (``Print Saved Output'') if you want to see whether the situation above has occurred. However, that might not be necessary. A good rule of thumb is that if the ~c[nil] goal is under more level of induction (e.g., with a prefix ``*i.j'' such as ``Subgoal *1.1/2.2''), then there is some likelihood that the situation above did indeed occur, and you can spend your time and energy looking at the key checkpoints printed in the summary to see if they suggest useful ~il[rewrite] rules to prove. On the other hand, if the ~c[nil] goal is at the top level (e.g. with a name not starting with ``*'', such as ``Subgoal 3.2''), then the original conjecture is probably not a theorem. If you do not quickly see why that is the case, then you might find it useful to issue the command ~c[:]~ilc[pso] to see which case reduced to ~c[nil], in order to get insight about how the theorem might be falsified.~/~/") (defmacro set-saved-output (save-flg inhibit-flg) ":Doc-Section switches-parameters-and-modes save proof output for later display with ~c[:]~ilc[pso] or ~c[:]~ilc[pso!]~/ ~bv[] Examples: (set-saved-output t t) ; save proof output for later, but inhibit it now (set-saved-output t :all) ; save proof output for later, but inhibit all ; output (except WARNING!, for critical warnings, ; and ERROR, unless these are already inhibited) :set-saved-output t :all ; same as the line above (set-saved-output t nil) ; save proof output for later, but print it now too (set-saved-output nil t) ; do not save proof output, and inhibit it (set-saved-output nil nil); do not save proof output or inhibit output (set-saved-output nil :same), (set-saved-output t :same) ; save proof output or not, as indicated, but do ; not change which output is inhibited (set-saved-output nil :normal) ; the behavior when ACL2 first starts up: do not ; save output, and only inhibit proof-tree output (set-saved-output t '(warning observation proof-tree prove)) ; save proof output for later, and inhibit the ; indicated kinds of output~/ General Form: (set-saved-output save-flg inhibit-flg) ~ev[] Parameter ~c[save-flg] is ~c[t] to cause output to be saved for later display using ~c[pso] or ~c[pso!]; ~pl[pso] and ~pl[pso!], and see the documentation for ~il[proof-checker] commands of the same names. Set ~c[save-flg] to ~c[nil] to turn off this feature; except, it always stays on in proof-checker sessions entered with ~ilc[verify]. The other argument, ~c[inhibit-flg], controls whether output should be inhibited when it is created (normally, during a proof attempt). So a common combination is to set both arguments to ~c[t], to indicate that output should be suppressed for now but saved for printing with ~ilc[pso] or ~ilc[pso!]. The examples above give a good summary of the functionality for the second argument. Saved output is cleared at the start of every event, and also at the start of every ~il[proof-checker] commands that invoke the prover. Note that interactive ~il[proof-checker] commands, that is, from a proof-checker session entered with ~ilc[verify], are always run with output saved. Also ~pl[set-gag-mode]; and ~pl[set-print-clause-ids], which causes subgoal numbers to be printed during proof attempts when output is inhibited. ~l[set-inhibit-output-lst] if you want to inhibit certain output from the prover but not other output (e.g., not the summary), and you don't want to save any output.~/" (let ((save-flg-original save-flg) (save-flg (if (and (consp save-flg) (eq (car save-flg) 'quote)) (cadr save-flg) save-flg)) (inhibit-flg-original inhibit-flg) (inhibit-flg (if (and (consp inhibit-flg) (eq (car inhibit-flg) 'quote)) (cadr inhibit-flg) inhibit-flg))) `(prog2$ (and (gag-mode) (er hard 'set-saved-output "It is illegal to call set-saved-output explicitly while ~ gag-mode is active. First evaluate ~x0." '(set-gag-mode nil))) (pprogn ,(cond ((eq save-flg t) '(f-put-global 'saved-output-token-lst :all state)) ((null save-flg) '(f-put-global 'saved-output-token-lst nil state)) ((true-listp save-flg) `(f-put-global 'saved-output-token-lst ',save-flg state)) (t (er hard 'set-saved-output "Illegal first argument to set-saved-output (must ~ be ~x0 or a true-listp): ~x1." t save-flg-original))) ,(if (eq inhibit-flg :same) 'state `(f-put-global 'inhibit-output-lst ,(cond ((eq inhibit-flg t) '(add-to-set-eq 'prove (f-get-global 'inhibit-output-lst state))) ((eq inhibit-flg :all) '(set-difference-eq *valid-output-names* (set-difference-eq '(error warning!) (f-get-global 'inhibit-output-lst state)))) ((eq inhibit-flg :normal) ''(proof-tree)) ((true-listp inhibit-flg) (list 'quote inhibit-flg)) (t (er hard 'set-saved-output "Illegal second argument to ~ set-saved-output (must be ~v0, ~ or a true-listp): ~x1." '(t :all :normal :same) inhibit-flg-original))) state)))))) (defmacro set-raw-proof-format (flg) ":Doc-Section switches-parameters-and-modes print runes as lists in proof output from simplification~/ ~bv[] General Forms: (set-raw-proof-format t) :set-raw-proof-format t (set-raw-proof-format nil) :set-raw-proof-format nil ~ev[] This command affects output from the theorem prover only when ~c['prove] output is not inhibited (~pl[set-inhibit-output-lst]) and gag-mode is off (~pl[set-gag-mode]). Calling this macro with value ~c[t] as shown above will cause simplification steps from proof output, including steps from preprocess (~pl[simple]), to print the list of runes used in a list format, rather than in the English proof commentary. This ``raw'' format can be handy when you want to use that list as a basis for ~ilc[hints] that you construct for a subsequent proof attempt.~/~/" (declare (xargs :guard (member-equal flg '(t 't nil 'nil)))) (let ((flg (if (atom flg) (list 'quote flg) flg))) `(f-put-global 'raw-proof-format ,flg state))) (defmacro set-print-clause-ids (flg) ":Doc-Section switches-parameters-and-modes cause subgoal numbers to be printed when ~c['prove] output is inhibited~/ ~bv[] General Forms: (set-print-clause-ids t) :set-print-clause-ids t (set-print-clause-ids nil) :set-print-clause-ids nil ~ev[] This command affects output from the theorem prover only when ~c['prove] output is inhibited (~pl[set-inhibit-output-lst]) or gag-mode is on (but in that case the ~c[:goals] setting issues this command automatically; ~pl[set-gag-mode]). Calling this macro with value ~c[t] as shown above will cause subsequent proof attempts with ~c['prove] output inhibited to print the subgoal number, so that you can see the progress of the proof; value ~c[nil] reverts to the default behavior, where this is not the case. On a related note, we point out that you can cause output to be saved for later display; ~pl[pso] and ~pl[pso!].~/ If ~c['prove] output is inhibited or gag-mode is on, and if you issue ~c[(set-print-clause-ids t)] (either explicitly or with ~c[(set-gag-mode :goals)]), then you can restrict when subgoal numbers are printed. In the following example we restrict to subgoals that are no more than four inductions deep, no more than four casesplits deep, and no more than four single-subgoals deep. For additional relevant explanation, ~pl[clause-identifier] and ~pl[defattach]. ~bv[] (defun print-clause-id-okp-level-4 (cl-id) (declare (xargs :mode :logic :guard (clause-id-p cl-id))) (and (<= (length (access clause-id cl-id :pool-lst)) 4) (<= (length (access clause-id cl-id :case-lst)) 4) (<= (access clause-id cl-id :primes) 4))) (defattach print-clause-id-okp print-clause-id-okp-level-4) ~ev[]~/" (declare (xargs :guard (member-equal flg '(t 't nil 'nil)))) (let ((flg (if (atom flg) (list 'quote flg) flg))) `(f-put-global 'print-clause-ids ,flg state))) (defun set-standard-co-state (val state) (declare (xargs :stobjs state :mode :program)) (mv-let (erp x state) (set-standard-co val state) (declare (ignore x)) (prog2$ (and erp (er hard? 'set-standard-co-state "See above for error message.")) state))) (defun set-proofs-co-state (val state) (declare (xargs :stobjs state :mode :program)) (mv-let (erp x state) (set-proofs-co val state) (declare (ignore x)) (prog2$ (and erp (er hard? 'set-proofs-co-state "See above for error message.")) state))) (defmacro with-standard-co-and-proofs-co-to-file (filename form) `(mv-let (wof-chan state) (open-output-channel ,filename :character state) (cond ((null wof-chan) (er soft 'with-standard-co-and-proofs-co-to-file "Unable to open file ~x0 for output." ,filename)) (t (pprogn (princ$ "-*- Mode: auto-revert -*-" wof-chan state) (newline wof-chan state) (mv-let (erp val state) (state-global-let* ((standard-co wof-chan set-standard-co-state) (proofs-co wof-chan set-proofs-co-state)) (check-vars-not-free (wof-chan) ,form)) (pprogn (close-output-channel wof-chan state) (cond (erp (silent-error state)) (t (value val)))))))))) (defmacro wof (filename form) ; Acronym: With Output File ":Doc-Section Other direct standard output and proofs output to a file~/ ~bv[] Example Form: (wof \"tmp\" (pso)) ; same as (psof \"tmp\")~/ General Form: (wof filename form) ~ev[] where ~c[filename] is a writable filename and ~c[form] is any form that evaluates to an error triple (~pl[programming-with-state]), that is, a multiple value of the form ~c[(mv erp val state)]. All output to channels ~ilc[standard-co] and ~ilc[proofs-co] will be directed to the indicated file. It is acceptable to replace ~c[filename] with ~c[(quote filename)]. Note that so-called comment-window output (~pl[cw] and ~pl[observation-cw]) is not redirected by ~c[wof] to a file, nor is printing from a ~il[wormhole].~/" `(with-standard-co-and-proofs-co-to-file ,filename ,form)) (defmacro psof (filename) ":Doc-Section Other show the most recently saved output~/ For a similar utility, ~pl[pso]. Like ~c[:pso], the ~c[:psof] command prints output that was generated in an environment where output was being saved, typically ~ilc[gag-mode]; also ~pl[set-saved-output]. But unlike ~c[:pso], ~c[:psof] takes a filename argument and saves output to that file, instead of to the terminal. For large proofs, ~c[:]~ilc[psof] may complete more quickly than ~c[:]~ilc[pso]. Note that as with ~c[:pso], ~il[proof-tree] output will be suppressed. The first line of output from ~c[:psof] directs the Emacs editor to use auto-revert mode. You can change the frequency of auto-reverting the buffer connected to a file by evaluating a suitable command in Emacs. For example, the command ~c[(setq auto-revert-interval .1)] arranges for auto-revert mode to update as needed every 1/10 of a second.~/~/" (declare (xargs :guard (or (stringp filename) (and (consp filename) (consp (cdr filename)) (null (cddr filename)) (eq (car filename) 'quote) (stringp (cadr filename)))))) `(cond #+acl2-par ((f-get-global 'waterfall-parallelism state) (er soft 'psof "The PSOF command is disabled with waterfall-parallelism ~ enabled, because in that case most prover output is printed to ~ *standard-co* (using wormholes), so cannot be redirected.")) (t (wof ,(if (consp filename) (cadr filename) filename) (pso))))) (defun set-gag-mode-fn (action state) ; Warning: Keep this in sync with with-output-fn, in particular with respect to ; the legal values for action and for the state-global-let* generated there. (let ((action (if (and (consp action) (consp (cdr action)) (eq (car action) 'quote)) (cadr action) action))) (pprogn (f-put-global 'gag-mode nil state) ; to allow set-saved-output (case action ((t) (pprogn (set-saved-output t :same) (f-put-global 'gag-mode action state) (set-print-clause-ids nil))) (:goals (pprogn (set-saved-output t :same) (f-put-global 'gag-mode action state) (set-print-clause-ids t))) ((nil) (pprogn ; (f-put-global 'gag-mode nil state) ; already done (set-saved-output nil :same) (set-print-clause-ids nil))) (otherwise (prog2$ (er hard 'set-gag-mode "Unknown set-gag-mode argument, ~x0" action) state)))))) (defmacro set-gag-mode (action) ":Doc-Section switches-parameters-and-modes modify the nature of proof output~/ ~bv[] Examples: :set-gag-mode t ; enable gag-mode, suppressing most proof commentary (set-gag-mode t) ; same as above :set-gag-mode :goals ; same as above, but print names of goals when produced :set-gag-mode nil ; disable gag-mode~/ General Forms: (set-gag-mode val) :set-gag-mode val ~ev[] where ~c[val] is one of ~c[t], ~c[nil], or ~c[:goals]. The basic idea of ~il[gag-mode] is to avoid much of the verbose output from the theorem prover, leaving only output that is expected to be helpful. You are strongly encouraged to put the form ~bv[] (set-gag-mode t) ; or, (set-gag-mode :goals) ~ev[] in your ACL2 customization file; ~pl[acl2-customization]. The default value is ~c[:goals]. The basic idea of gag-mode is to focus attention on so-called ``key checkpoints''. By default, a checkpoint is a goal that cannot be simplified. (Below we discuss how to change this default.) A key checkpoint is a checkpoint that is not descended from another checkpoint. (Technical point: ``Descended'' signifies that both goals are at the top level in the same forcing round, or are in the same proof by induction.) Successful ACL2 users generally focus their attention on key checkpoints; for a discussion of how to use ACL2 prover output in an effective manner, ~pl[the-method], and ~pl[introduction-to-the-theorem-prover] for a more detailed tutorial. In gag-mode, a key checkpoint is only displayed when ACL2 is unable to make any further progress on that goal or some descendent of it, other than with a proof by induction. Evaluation of ~c[set-gag-mode t] enters gag-mode, so that only key checkpoints are printed. Evaluation of ~c[set-gag-mode :goals] also enters gag-mode, but will additionally cause the name of a goal to be printed as soon as it is generated (by invoking ~il[set-print-clause-ids]). The ~c[:goals] setting is useful for cases in which the prover spends very little of its time generating goals to be proved by induction, yet you want to see that it is making progress. For finer-grained feedback about the simplifier's activity, ~pl[dmr]. The current value of ~il[gag-mode] is returned by a macro of the same name: ~bv[] (gag-mode) ; evaluates to t, nil, or :goals ~ev[] An alternative to gag-mode is to use proof-trees; ~pl[proof-tree]. With proof-trees it is not so important to avoid excessive prover output, since the proof-tree display provides structure that makes it easy to monitor proof attempts and navigate output for a proof that has failed or seems to be failing. Still, output can take time to print, so you may get better performance with gag-mode. The intention of gag-mode is to show you only the parts of a proof attempt that are relevant for debugging a failure; additional output is generally more likely to be distracting than truly helpful. But on occasion you may want to see the full proof output after an attempt made with gag-mode. If so, then ~pl[pso] and ~pl[pso!]. Since ~c[set-gag-mode] takes responsibility for the saving of output, related utility ~ilc[set-saved-output] is disabled when gag-mode is active. Also note that calling ~c[set-gag-mode] erases the currently saved output, if any. You may notice that gag-mode tends to print relatively little information about goals pushed for proof by sub-induction ~-[] i.e., a proof of *i.j, *i.j.k, etc. The principle here is that sub-inductions that do not succeed should generally be avoided, not analyzed for ways to make them succeed. Instead, the key checkpoint that generated the goal pushed for this induction is more appropriate to analyze. In general, the ``higher level'' the checkpoint, the more worthy it is of attention. Thus, we suggest that look at the top-level checkpoints before looking at those labeled ``Key checkpoints under a top-level induction''. We conclude with remarks for advanced users. The notion of ``checkpoint'' can be modified by the user. The default, as discussed above, is for a checkpoint to be a goal that cannot be simplified. Put differently, a checkpoint is acted on by one of the processes in the value of the form ~c[(@ checkpoint-processors)]; ~pl[@]. Any or all of the symbols ~c[eliminate-destructors-clause], ~c[fertilize-clause], ~c[generalize-clause], or ~c[eliminate-irrelevance-clause] can be removed from this value in order that invocation of the corresponding proof process does not cause its input goal to be labeled a checkpoint. For example, if you do not want destructor elimination to be treated differently from simplification for purposes of labeling checkpoints, you can evaluate the following form (~pl[assign]): ~bv[] (assign checkpoint-processors (remove 'eliminate-destructors-clause (@ checkpoint-processors))) ~ev[] Note that the value of ~c[(@ checkpoint-processors)] also affects the proof tree display; ~pl[proof-tree-details]. End of Remark.) ~l[set-evisc-tuple], in particular the discussion there of ~c[:GAG-MODE], for how to influence slightly just what is printed in gag-mode." `(set-gag-mode-fn ,action state)) ; Saving an Executable Image #-acl2-loop-only (defparameter *initial-cbd* nil) #-acl2-loop-only (defvar *return-from-lp* nil) (defun save-exec-fn (exec-filename extra-startup-string host-lisp-args toplevel-args inert-args return-from-lp) #-acl2-loop-only (progn ; Parallelism blemish: it may be a good idea to reset the parallelism variables ; in all #+acl2-par compilations before saving the image. (setq *return-from-lp* return-from-lp) #-sbcl (when toplevel-args (er hard 'save-exec "Keyword argument :toplevel-args is only allowed when the ~ host Lisp is SBCL.")) (if (not (eql *ld-level* 0)) (er hard 'save-exec "Please type :q to exit the ACL2 read-eval-print loop and then try ~ again.")) (if (equal extra-startup-string "") (er hard 'save-exec "The extra-startup-string argument of save-exec must be ~x0 or ~ else a non-empty string." nil) (setq *saved-string* (format nil "~a~%MODIFICATION NOTICE:~%~%~a~%" *saved-string* (cond ((null extra-startup-string) "This ACL2 executable was created by saving a session.") (t extra-startup-string))))) #-(or gcl cmu sbcl allegro clisp ccl lispworks) (er hard 'save-exec "Sorry, but save-exec is not implemented for this Common Lisp.") ; The forms just below, before the call of save-exec-raw, are there so that the ; initial (lp) will set the :cbd correctly. (f-put-global 'connected-book-directory nil *the-live-state*) (setq *initial-cbd* nil) (setq *startup-package-name* (package-name *package*)) (setq *saved-build-date-lst* ; By using setq here for *saved-build-date* instead of a let-binding for ; save-exec-raw, it happens that saving more than once in the same session (for ; Lisps that allow this, such as Allegro CL but not GCL) would result in extra ; "; then ..." strings. But that seems a minor problem, and avoids having to ; think about the effect of having a let-binding in force above a save of an ; image. (cons (saved-build-date-string) *saved-build-date-lst*)) (save-exec-raw exec-filename host-lisp-args #+sbcl toplevel-args inert-args)) #+acl2-loop-only (declare (ignore exec-filename extra-startup-string host-lisp-args toplevel-args inert-args return-from-lp)) nil ; Won't get to here in GCL and perhaps other lisps ) (defmacro save-exec (exec-filename extra-startup-string &key host-lisp-args toplevel-args inert-args return-from-lp) ":Doc-Section Other save an executable image and a wrapper script~/ ~c[Save-exec] saves your ACL2 state so that you can immediately re-start later in that same state. This utility can be useful for a project with ~il[books] to be included every time ACL2 is started, to avoid time taken to run ~ilc[include-book]. Another use of ~c[save-exec] is to save an executable that takes command-line arguments beyond those normally passed to the host Lisp executable. All arguments of a call of ~c[save-exec] are evaluated. ~bv[] Examples: ; Save an executable script named my-saved_acl2, with the indicated message ; added to the start-up banner: (save-exec \"my-saved_acl2\" \"This saved image includes Version 7 of Project Foo.\") ; Same as above, but instead with a generic comment in the start-up banner: (save-exec \"my-saved_acl2\" nil) ; Arrange that the generated script passes the indicated arguments to be ; processed by the Lisp (ACL2) executable (where this example is specific to ; the case that CCL is the host Lisp): (save-exec \"my-saved_acl2\" nil :host-lisp-args \"--no-init -Z 256M\") ; Arrange that the generated script passes along the indicated arguments ; to Lisp (ACL2), but that they are not processed by Lisp other than to ; record the additional arguments (see (6) below). (save-exec \"my-saved_acl2\" nil :inert-args \"abc xyz -i foo\") ; Combining the preceding two examples: (save-exec \"my-saved_acl2\" nil :host-lisp-args \"--no-init -Z 256M\" :inert-args \"abc xyz -i foo\") ; Immediately exit the ACL2 read-eval-print loop after starting up. (save-exec \"my-acl2\" nil :return-from-lp t) ; Immediately exit the ACL2 read-eval-print loop after starting up and ; defining function FOO in the logic. (save-exec \"my-acl2\" \"Start with foo defined.\" :return-from-lp '(with-output :off :all (defun foo (x) x))) ; Immediately exit the ACL2 read-eval-print loop after starting up and ; defining variable xxx in raw Lisp. (save-exec \"my-acl2\" \"Start with xxx defined.\" :return-from-lp '(with-output :off :all (ld '((set-raw-mode-on!) (defvar xxx (make-list 10)) (set-raw-mode nil) (u))))) ~ev[] Each example above generates a file named \"my-saved_acl2\". That file is quite similar in form to the script generated when building ACL2 directly from source code; details are below. For example, here are the contents of that generated file if the host Lisp is CCL (but where dates and pathnames are specific to one's environment). Here, we break lines using `\\', but the ~c[exec] command is actually on a single line. ~bv[] #!/bin/sh # Saved August 16, 2013 23:06:49 # then August 17, 2013 11:01:56 export CCL_DEFAULT_DIRECTORY=\"/projects/acl2/lisps/ccl/15542/ccl\" exec \"/projects/ccl/lx86cl64\" -I \"/u/smith/my-saved_acl2.lx86cl64\" \\ -Z 64M -K ISO-8859-1 -e \"(acl2::acl2-default-restart)\" \\ --no-init -Z 256M \\ -- \\ abc xyz -i foo \\ \"$@\" ~ev[]~/ ~bv[] General Form: (save-exec exec-filename extra-startup-string :host-lisp-args host-lisp-args :inert-args inert-args :return-from-lp return-from-lp) ~ev[] where the keyword arguments are optional, and arguments are as follows. ~bq[] ~c[Exec-filename] is the filename of the proposed executable. ~c[Extra-startup-string] is a non-empty string to be printed after the normal ACL2 startup message when you start up the saved image. However, ~c[extra-startup-string] is allowed to be ~c[nil], in which case a generic string will be printed instead. ~c[Host-lisp-args] can be ~c[nil] (the default), but if it is a non-~c[nil] value, then it is a string to be inserted into the command line in the saved script, specifying additional arguments that are to be processed by the host Lisp executable. (Note for SBCL only: these are runtime options; for toplevel options, see (8) below.) ~c[Inert-args] can be ~c[nil] (the default), but if it is a non-~c[nil] value, then it is a string to be inserted into the command line in the saved script, specifying additional arguments that are not to be processed by the host Lisp executable.~eq[] ~c[Return-from-lp] is ~c[nil] by default. Regardless of the value of ~c[return-from-lp], ACL2 starts up and enters its read-eval-print loop as usual; ~pl[lp]. Normally you'll stay inside that loop, but if ~c[return-from-lp] is not ~c[nil], then it is evaluated in the loop, which is then exited, leaving you in raw Lisp. Evaluation of ~c[return-from-lp] is done with ~ilc[ld] options that minimize output; also ~pl[with-output] to minimize output. Suggestion: let ~c[return-from-lp] be ~c[t] if you simply want to exit the read-eval-print loop at startup, without evaluating any (nontrivial) form. The remainder of this documentation focuses on the options other than ~c[return-from-lp]. ~st[Details]: (1) You must first exit the ACL2 read-eval-print loop, typically by executing ~c[:q], before evaluating a ~c[save-exec] call; otherwise an error occurs. (2) The image will be saved so that in the new image, the raw Lisp package and the package in the ACL2 read-eval-print loop (~pl[lp]) will be the same as their respective values at the time ~c[save-exec] is called. (3) ~c[Save-exec] generates a small script file (e.g., ~c[\"my-saved_acl2\"] in the examples above), similar in form (see (4) below) to the script generated when building ACL2 directly from source code, but with a comment line indicating the time at which the new script is written. ~c[Save-exec] also saves an associated binary file. The binary file's name is obtained by putting a suffix on the script filename; for example, if the host Lisp is GCL running on a Linux or Darwin (MacOS) system, then that binary file has the name ~c[my-saved_acl2.gcl] in the examples above. (4) If ~c[inert-args] is ~c[nil] (for example if keyword ~c[:inert-args] is omitted), then when the generated ACL2 script is invoked with command line arguments, those arguments will be passed to the host Lisp; otherwise they will not. Thus for the example above, suppose we invoke the generated script as follows. ~bv[] my-saved_acl2 -a bcd -e fgh ~ev[] If ~c[my-saved_acl2] was generated using a ~c[save-exec] command with a non-~c[nil] value specified for keyword ~c[:inert-args], then the arguments ``~c[-a bcd -e fgh]'' will not be passed to the host Lisp; otherwise, they will be. Note that for ACL2 executable scripts generated by an ordinary ACL2 build from sources, the latter case (i.e., without ~c[inert-args]) takes place. (5) The generated script, which specifies execution with ~c[/bin/sh], will generally contain a line of one of the following forms. (But for SBCL, see (8) below.) In the examples that follow, ~c[ACL2_options] is a suitable list of command-line arguments given to the ACL2 executable. The quoted string ~c[\"$@\"] is intended to allow the user to pass additional command-line arguments to that executable. ~bq[] If ~c[host-lisp-args] and ~c[inert-args] are omitted (or ~c[nil]): ~bv[] exec \"$@\" ~ev[] More generally, ~c[host-lisp-args] is inserted immediately after ~c[], but only if it is non-~c[nil] (hence a string). If ~c[inert-args] is ~c[nil], we thus get: ~bv[] exec host-lisp-args \"$@\" ~ev[] If ~c[host-lisp-args] redefines a value from ~c[], then it is up to the host lisp which value to use. For example, experiments show that in CCL, if ~c[-Z] appears twice, each with a legal value, then the second value is the one that is used (i.e. it does indeed override the original value written out by ACL2 in ~c[]. But experiments also show that in LispWorks, where ``~c[-init -]'' is included in ~c[], then inclusion of ``~c[-init foo.lisp]'' in ~c[host-lisp-args] is ignored. The remaining cases below are for a non-~c[nil] value of ~c[inert-args]. In each case, if ~c[host-lisp-args] is ~c[nil] then it should be omitted from the displayed command. If ~c[inert-args] is ~c[t] then an additional argument, `~c[--]', indicates that when ACL2 is given command line arguments, these should not be processed by the host Lisp (other than recording them; see (6) below): ~bv[] exec host-lisp-args -- \"$@\" ~ev[] If ~c[inert-args] is a string then the result is similar to the above, except that ~c[inert-args] is added immediately after `~c[--]': ~bv[] exec host-lisp-args -- inert-args \"$@\" ~ev[]~eq[] (6) See community books ~c[books/oslib/argv] for a utility that returns a list of all ~c[inert-args] from an invocation of ACL2. (7) Suppose that you invoke an ACL2 script, say ~c[\"my-saved_acl2\"], that was generated by ~c[save-exec], and then optionally evaluate some forms. Then you may save a new ACL2 script with ~c[save-exec]. The new script will contain comment lines that extend comment lines in ~c[\"my-saved_acl2\"] with a new write date, but otherwise will be identical to the script that would have been generated by executing the new ~c[save-exec] call after invoking the original ACL2 executable (built directly from ACL2 sources) instead of ~c[\"my-saved_acl2\"]. In other words, the options added by the earlier ~c[save-exec] call that created ~c[\"my-saved_acl2\"] are discarded by the new ~c[save-exec] call. However, the ~c[.core] file will built on top of the ~c[.core] file that was consulted when ~c[\"my-saved_acl2\"] was invoked. (8) The following note pertains only to the case that the host Lisp is SBCL. For SBCL, the scripts written are analogous to, but slightly different from, those shown above. Please note that for SBCL, the ~c[host-lisp-args] are what the SBCL manual calls ``runtime options''. For SBCL only, an extra keyword argument, ~c[:toplevel-args], may be used for specifying what the SBCL manual calls ``toplevel options. As with ~c[:host-lisp-args], this value, ~c[toplevel-args], should be ~c[nil] (the default) or a string. Here is an example. ~bv[] (save-exec \"my-saved_acl2\" nil :host-lisp-args \"--dynamic-space-size 12000\" :toplevel-args \"--eval '(print \\\"HELLO\\\")'\" :inert-args \"--my-option my-value\") ~ev[] The script generated by this example call of ~c[save-exec] contains a line such as the following (with the same convention for `\\' as before) ~bv[] exec \"/projects/sbcl-1.1.7-x86-64-linux/src/runtime/sbcl\" \\ --dynamic-space-size 2000 --control-stack-size 8 \\ --core \"/u/smith/my-saved_acl2.core\" --dynamic-space-size 12000 \\ --end-runtime-options \\ --no-userinit --eval '(acl2::sbcl-restart)' \\ --eval '(print \"HELLO\")' \\ --end-toplevel-options \\ --my-option my-value \\ \"$@\" ~ev[] In general, the generated script is of one of the following forms (with the same convention for `\\' as before). ~bq[] For the case that ~c[inert-args] is ~c[nil]: ~bv[] exec \\ host-lisp-args --end-runtime-options \\ host-lisp-args \\ \"$@\" ~ev[] For the case that ~c[inert-args] is non-~c[nil]: ~bv[] exec \\ host-lisp-args --end-runtime-options \\ host-lisp-args --end-toplevel-options \\ inert-args \"$@\" ~ev[]~eq[] Notice that as before, when the generated script is invoked (for example, at the shell), additional command-line arguments provided at that time are passed to Lisp if and only if ~c[inert-args] is ~c[nil]. For SBCL, when they are passed to Lisp they are passed as toplevel options, not as runtime options.~/" `(save-exec-fn ,exec-filename ,extra-startup-string ,host-lisp-args ,toplevel-args ,inert-args ,return-from-lp)) (defdoc command-line ":Doc-Section Other handling of command-line arguments when ACL2 is invoked~/ You may provide command-line arguments when invoking ACL2, which are passed to the host Lisp. For more information on this topic, along with a discussion of how to save an ACL2 executable that avoids passing command-line arguments to the host Lisp, ~pl[save-exec].~/~/ :cite save-exec") (link-doc-to saving-and-restoring miscellaneous save-exec) (deflabel about-acl2 :doc ":Doc-Section about-acl2 about ACL2~/ This is ACL2 Version 6.3, copyright (C) 2013, Regents of the University of Texas, authored by Matt Kaufmann and J Strother Moore. For past versions, see ~url[http://www.cs.utexas.edu/users/moore/acl2/current/other-releases.html]. For statistics on ACL2 code size, see file ~c[doc/acl2-code-size.txt]. ~l[documentation] for how to access the user's manual. See the home page at ~url[http://www.cs.utexas.edu/users/moore/acl2/] for additional information including tutorials, applications, mailing lists, related publications, libraries, ACL2 workshops and seminars, installation instructions, and acknowledgements. ~l[copyright] for license and copyright information. ~/~/") ; We now develop code for without-evisc. (defun defun-for-state-name (name) (intern-in-package-of-symbol (concatenate 'string (symbol-name name) "-STATE") name)) (defmacro defun-for-state (name args) `(defun ,(defun-for-state-name name) ,args (mv-let (erp val state) (,name ,@args) (declare (ignore val)) (prog2$ (and erp (er hard 'top-level "See error message above.")) state)))) (defun set-ld-evisc-tuple (val state) (set-evisc-tuple val :sites :ld :iprint :same)) (defun-for-state set-ld-evisc-tuple (val state)) (defun set-abbrev-evisc-tuple (val state) (set-evisc-tuple val :sites :abbrev :iprint :same)) (defun-for-state set-abbrev-evisc-tuple (val state)) (defun set-gag-mode-evisc-tuple (val state) (set-evisc-tuple val :sites :gag-mode :iprint :same)) (defun-for-state set-gag-mode-evisc-tuple (val state)) (defun set-term-evisc-tuple (val state) (set-evisc-tuple val :sites :term :iprint :same)) (defun-for-state set-term-evisc-tuple (val state)) (defun without-evisc-fn (form state) (state-global-let* ((abbrev-evisc-tuple nil set-abbrev-evisc-tuple-state) (gag-mode-evisc-tuple nil set-gag-mode-evisc-tuple-state) (term-evisc-tuple nil set-term-evisc-tuple-state)) (er-progn (ld (list form) :ld-verbose nil :ld-prompt nil :ld-evisc-tuple nil) (value :invisible)))) (defmacro without-evisc (form) ":Doc-Section IO print output in full~/ ~bv[] General Form: (without-evisc form) ~ev[] where ~c[form] is any expression to evaluate. The effect is to evaluate ~c[form] as though the ~c[without-evisc] wrapper were absent, except that expressions are printed in full for the ensuing output, regardless of the current evisc-tuples (~pl[set-evisc-tuple]). ~l[set-iprint] for an example. More precisely, ~c[without-evisc] binds each of the term-evisc-tuple, ld-evisc-tuple, abbrev-evisc-tuple and gag-mode-evisc-tuple to ~c[nil] (~pl[set-evisc-tuple]). It does not modify the trace evisc-tuple, so trace output is not modified by ~c[without-evisc]. Also note that calls of printing functions such as ~ilc[fmt] that include explicit evisc-tuples will not have those evisc-tuples overridden. The following example illustrates this point. ~bv[] ACL2 !>(without-evisc (fms \"~~x0~~%\" (list (cons #\0 '((a b ((c d)) e f g) u v w x y))) *standard-co* state (evisc-tuple 2 3 nil nil))) ((A B # ...) U V ...) ACL2 !> ~ev[] We conclude with two remarks. (1) A call of ~c[without-evisc] on expression ~c[exp] actually invokes a specialized call of ~ilc[ld] on a one-element list containing ~c[exp], which prints the value returned by evaluation of ~c[exp] but actually returns the useless value ~c[(mv nil :invisible state)]. So do not use ~c[without-evisc] in programs; just use it at the top level of the ACL2 read-eval-print loop, or at least the top level of ~c[ld]. (2) Even when using without-evisc, if the ACL2 logical ~il[world] is part of the value returned, it will be printed in abbreviated form because the ACL2 read-eval-print loop always arranges for this to be the case, regardless of the ld-evisc-tuple. For example: ~bv[] ACL2 !>(without-evisc (w state)) ACL2 !> ~ev[]~/ An alternative to the use of ~c[without-evisc] is to explore large objects using the ACL2 function ~c[(walkabout object state)]. Some brief documentation is printed when you enter an interactive loop upon evaluating a call of ~c[walkabout]. We may add documentation for ~c[walkabout] if that is requested.~/" `(without-evisc-fn ',form state)) acl2-sources/LICENSE0000664002132200015000000000340012063640127013556 0ustar kaufmannacl2[Note: The license below is based on the template found Dec. 16, 2012 at: http://opensource.org/licenses/BSD-3-Clause. It applies to all files distributed from http://www.cs.utexas.edu/users/moore/acl2/current/ except as otherwise noted.] Copyright (c) 2012, Regents of the University of Texas All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the University of Texas, Austin nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. acl2-sources/linear-a.lisp0000666002132200015000000046342212222115527015150 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ;================================================================= ; This file defines the basics of the linear arithmetic decision ; procedure. We also include clause histories, parent trees, ; tag-trees, and assumptions; all of which are needed by add-poly ; and friends. ;================================================================= ; We begin with some general support functions. They should ; probably be organized and moved to axioms.lisp. (defabbrev ts-acl2-numberp (ts) (ts-subsetp ts *ts-acl2-number*)) (defabbrev ts-rationalp (ts) (ts-subsetp ts *ts-rational*)) (defabbrev ts-real/rationalp (ts) #+non-standard-analysis (ts-subsetp ts *ts-real*) #-non-standard-analysis (ts-subsetp ts *ts-rational*)) (defabbrev ts-integerp (ts) (ts-subsetp ts *ts-integer*)) (defun all-quoteps (lst) (cond ((null lst) t) (t (and (quotep (car lst)) (all-quoteps (cdr lst)))))) (mutual-recursion (defun dumb-occur (x y) ; This function determines if term x occurs in term y, but does not ; look for x inside of quotes. It is thus equivalent to occur if you ; know that x is not a quotep. (cond ((equal x y) t) ((variablep y) nil) ((fquotep y) nil) (t (dumb-occur-lst x (fargs y))))) (defun dumb-occur-lst (x lst) (cond ((null lst) nil) (t (or (dumb-occur x (car lst)) (dumb-occur-lst x (cdr lst)))))) ) ;================================================================= ; Clause Histories ; Clauses carry with them their histories, which describe which processes ; have produced them. A clause history is a list of history-entry records. ; A process, such as simplify-clause, might inspect the history of its ; input clause to help decide whether to perform a certain transformation. (defrec history-entry ; Important Note: This record is laid out this way so that we can use ; assoc-eq on histories to detect the presence of a history-entry for ; a given processor. Do not move the processor field! (processor ttree clause signal . cl-id) t) ; Processor is a waterfall processor (e.g., 'simplify-clause). The ; ttree and signal are, respectively, the ttree and signal produced by ; the processor on clause. Each history-entry is built in the ; waterfall, but we inspect them for the first time in this file. ;================================================================= ; Essay on Parent Trees ; Structurally, a "parent tree" or pt is either nil, a number, or the cons ; of two parent trees. Parent trees are used to represent sets of ; literals. In particular, every number in a pt is the position of some ; literal in the current-clause variable of simplify-clause1 and the tree ; may be thought of as representing that set of literals. Pts are used ; to avoid tail biting. An earlier implementation of this used "clause-tails." ; We explain everything below. ; "Tail biting" is our name for the insidious phenomenon that occurs when ; one assumes p false while trying to prove p and then, carelessly, ; rewrites the goal p to false on the basis of that assumption. Observe ; that this is sound but detrimental to success. One way to prevent ; tail biting is to not assume p false while trying to prove it, but we ; found that too weak. The way we avoid tail biting is to keep careful ; track of what we're trying to prove, which literal we are working on, ; and what assumptions have been used to derive what results; we never use ; the assumption that p is false (or anything derived from it) to rewrite ; p to false. Despite our efforts, tail biting by simplify-clause is ; possible. See "On Tail Biting by Simplify-clause" for more. ; The easiest to understand use of parent trees in this regard is in ; linear arithmetic. In simplify-clause1 we setup the ; simplify-clause-pot-lst, by expressing all the arithmetic hypotheses of ; the conjecture as polynomial inequalities. When new inequalities are ; introduced, as when trying to relieve the hypothesis of some rule, we ; can combine them with the preprocessed "polys" to quickly settle certain ; arithmetic statements. To avoid duplication of effort, our ; simplify-clause-pot-lst contains polys derived from all possible ; literals of the current clause. This is because a great deal of work ; may be done (via linear lemmas and rewriting) to derive a poly about a ; given suggestive subterm of a given literal and we do not want to do it ; each time we assume that literal false. Note the ease with which we ; could bite our tail: the list of inequalities is derived from the ; negations of every literal so we might easily use an inequality to ; falsify the literal from which it was derived. To avoid this, each poly ; is tagged with one or more parent trees. Intuitively the poly derived ; from an inequality literal is tagged with that literal. But other ; literals may have been used, e.g., to establish certain terms rational, ; so one must think of the polys as being tagged with sets of literals. ; Then, when we are rewriting a particular literal we tell ourselves (by ; making a note in the :pt field of the rcnst) to avoid any poly ; descending from the goal literal. Similar use is made of parent trees ; in the fc-pair-lst -- a list of preprocessed conclusions obtained by ; forward chaining from the current clause. ; The problem is made subtle by the fact that the literals we are ; rewriting change before we get to them and thus cannot be recognized by ; their structure alone. Consider the clause {lit1 lit2 lit3}. Now ; suppose we forward chain from ~lit3 and deduce concl. Then fc-pair-lst ; will contain (concl . ttree) where ttree contains a parent tree ; acknowledging our dependence on lit3. We may thus use concl when we are ; working on lit1 and lit2. But suppose that in simplifying lit1 we ; produce the literal (not (equal var 27)). Then we can substitute 27 for ; var everywhere and will actually do so. Thus, by the time we get to ; work on the third literal of the clause above it will not be lit3 but ; some reduced instance, lit3', of lit3. If the parent tree literally ; contained lit3, it would be impossible to recognize that concl was to be ; avoided. ; Therefore, we actually refer to literals by their position in the ; current-clause of simplify-clause1 (from which the preprocessing was ; done) and we keep careful track as we simplify what the original pt for ; each literal was. As we scan over the literals to simplify we maintain ; a map, an enumeration of pts, giving the pt for each literal. Thus, ; while we actually go to work on lit3' above, we will actually have in ; our hand the fact that lit3 is its parent. Keeping track of the parents ; of the literals we are working on is made harder by the fact that ; sometimes literal merge. For example, in {lit1 lit2 lit3} lit1 may ; simplify to lit3 and thus we may merge them. The surviving literal is ; given the parent tree that contains both 1 and 3 so we know not to use ; conclusions derived from either. The rewrite-constant, rcnst, in use ; below simplify-clause1 contains as one of its fields the ; :current-clause. Thus, given the rewrite-constant and a pt it is ; possible to recover the original parent literals. ; We generally use "pt" to refer to a single parent tree. "Pts" is a list ; of parent trees, implicitly in "weak 1:1 correspondence" with some list ; of terms. By "weak" we mean pts may be shorter than the list of terms ; and "excess terms" have the nil pt. That is, it is ok to cdr pts as you ; cdr down the list of terms and every time you need a pt for a term you ; take the car of pts. There is no need to store the nil pt in tag-trees, ; so we don't. Thus, a commonly used convention is to supply a pts of nil ; to a function that stores 'pts, causing it to store no pts. ; In the early days we did not use parent trees but "clause-tails" -- the ; tail of clause starting with the parent literal. This was adopted to ; avoid the confusion caused by duplicate literals. But it was rendered ; unworkable when we implemented the Satriani hacks and started ; substituting for variables as we went. It also suffered other problems ; due to sloppy implementation. (defun pt-occur (n pt) ; Determine whether n occurs in the set denoted by pt. (cond ((null pt) nil) ((consp pt) (or (pt-occur n (car pt)) (pt-occur n (cdr pt)))) (t (= n pt)))) (defun pt-intersectp (pt1 pt2) ; Determine whether the intersection of the sets denoted by pt1 and pt2 ; is nonempty. (cond ((null pt1) nil) ((consp pt1) (or (pt-intersectp (car pt1) pt2) (pt-intersectp (cdr pt1) pt2))) (t (pt-occur pt1 pt2)))) ;================================================================= ; Essay on Tag-Trees ; If you add a new tag, be sure to include it in all-runes-in-ttree! ; Tags in Tag-Trees ; After Version_4.2 we switched to a representation of a tag-tree as an alist, ; associating a key with a non-empty list of values, rather than building up ; tag-trees with operations (acons tag value ttree) and (cons ttree1 ttree2). ; Note that we view these lists as sets, and are free to ignore order and ; duplications (though we attempt to avoid duplicates). Our motivation was to ; allow the addition of a new key, associated with many values, without ; degrading performance significantly. ; Each definition of a primitive for manipulating tag-trees has the comment: " ; Note: Tag-tree primitive". ; See all-runes-in-ttree for the set of all legal tags and their associated ; values. Some of the tags and associated values are as follows. ; 'lemma ; The tagged object is either a lemma name (a symbolp) or else is the ; integer 0 indicating the use of linear arithmetic. ; 'pt ; The tagged object is a "parent tree". See the Essay on Parent Trees. ; The tree identifies a set of literals in the current-clause of ; simplify-clause1 used in the derivation of poly or term with which the ; tree is associated. We need this information for two reasons. First, ; in order to avoid tail biting (see below) we do not use any poly that ; descends from the assumption of the falsity of the literal we are trying ; to prove. Second, in find-equational-poly we seek two polys that can be ; combined to derive an equality, and we use 'pt to identify those that ; themselves descend from equality hypotheses. ; 'assumption ; The tagged object is an assumption record containing, among other things, a ; type-alist and a term which must be true under the type-alist in order to ; assure the validity of the poly or rewrite with which the tree is associated. ; We cannot linearize (- x), for example, without knowing (rationalp x). If we ; cannot establish it by type set reasoning, we add that 'assumption to the ; poly generated. If we eventually use the poly in a derivation, the ; 'assumption will infect it and when we get up to the simplify-clause level we ; will split on them. ; 'find-equational-poly ; The tagged object is a pair of polynomials. During simplify clause ; we try to find two polys that can be combined to form an equation we ; don't have explicitly in the clause. If we succeed, we add the ; equation to the clause. However, it may be simplified into ; unrecognizable form and we need a way to avoid re-generation of the ; equation in future calls of simplify. We do this by infecting the ; tag-tree with this tag and the two polys used. ; Historical Note from the days when tag-trees were constructed using (acons ; tag value ttree) and (cons-tag-trees ttree1 ttree2): ; ; The invention of tag-trees came about during the designing of the linear ; ; package. Polynomials have three "arithmetic" fields, the constant, alist, ; ; and relation. But they then have many other fields, like lemmas, ; ; assumptions, and literals. At the time of this writing they have 5 other ; ; fields. All of these fields are contaminants in the sense that all of the ; ; contaminants of a poly contaminate any result formed from that poly. The ; ; same is true with the second answer of rewrite. ; ; If we represented the 5-tuple of the ttree of a poly as full-fledged fields ; ; in the poly the best we could do is to use a balanced binary tree with 8 ; ; tips. In that case the average time to change some field (including the ; ; time to cons a new element onto any of the 5 contaminants) is 3.62 conses. ; ; But if we clump all the contaminants into a single field represented as a ; ; tag-tree, the cost of adding a single element to any one of them is 2 ; ; conses and the average cost of changing any of the 4 fields in a poly is ; ; 2.5 conses. Furthermore, we can effectively union all 5 contaminants of ; ; two different polys in one cons! (deflabel ttree :doc ":Doc-Section Miscellaneous tag-trees~/ Many low-level ACL2 functions take and return ``tag trees'' or ``ttrees'' (pronounced ``tee-trees'') which contain various useful bits of information such as the lemmas used, the linearize assumptions made, etc.~/ Abstractly a tag-tree represents a list of sets, each member set having a name given by one of the ``tags'' (which are symbols) of the ttree. The elements of the set named ~c[tag] are all of the objects tagged ~c[tag] in the tree. You are invited to browse the source code. Definitions of primitives are labeled with the comment ``; Note: Tag-tree primitive''. The rewriter, for example, takes a term and a ttree (among other things), and returns a new term, term', and new ttree, ttree'. Term' is equivalent to term (under the current assumptions) and the ttree' is an extension of ttree. If we focus just on the set associated with the tag ~c[LEMMA] in the ttrees, then the set for ttree' is the extension of that for ttree obtained by unioning into it all the ~il[rune]s used by the rewrite. The set associated with ~c[LEMMA] can be obtained by ~c[(tagged-objects 'LEMMA ttree)].") ; The following function determines whether val with tag tag occurs in ; tree: (defun tag-tree-occur (tag val ttree) ; Note: Tag-tree primitive (let ((pair (assoc-eq tag ttree))) (and pair ; optimization (member-equal val (cdr pair))))) (defun remove-tag-from-tag-tree (tag ttree) ; Note: Tag-tree primitive ; In this function we do not assume that tag is a key of ttree. See also ; remove-tag-from-tag-tree, which does make that assumption. (cond ((assoc-eq tag ttree) (delete-assoc-eq tag ttree)) (t ttree))) (defun remove-tag-from-tag-tree! (tag ttree) ; Note: Tag-tree primitive ; In this function we assume that tag is a key of ttree. See also ; remove-tag-from-tag-tree, which does not make that assumption. (delete-assoc-eq tag ttree)) ; To add a tagged object to a tree we use the following function. Observe ; that it does nothing if the object is already present. ; Note: ; If you add a new tag, be sure to include it in all-runes-in-ttree! (defmacro extend-tag-tree (tag vals ttree) ; Note: Tag-tree primitive ; Warning: We assume that tag is not a key of ttree and vals is not nil. `(acons ,tag ,vals ,ttree)) (defun add-to-tag-tree (tag val ttree) ; Note: Tag-tree primitive ; See also add-to-tag-tree!, for the case that tag is known not to be a key of ; ttree. (cond ((eq ttree nil) ; optimization (list (list tag val))) (t (let ((pair (assoc-eq tag ttree))) (cond (pair (cond ((member-equal val (cdr pair)) ttree) (t (acons tag (cons val (cdr pair)) (remove-tag-from-tag-tree! tag ttree))))) (t (acons tag (list val) ttree))))))) (defun add-to-tag-tree! (tag val ttree) ; Note: Tag-tree primitive ; It is legal (and more efficient) to use this instead of add-to-tag-tree if we ; know that tag is not a key of ttree. (extend-tag-tree tag (list val) ttree)) ; A Little Foreshadowing: ; We will soon introduce the notion of a "rune" or "rule name." To ; each rune there corresponds a numeric equivalent, or "nume," which ; is the index into the "enabled structure" for the named rule. We ; push runes into ttrees under the 'lemma property to record their ; use. ; We have occasion for "fake-runes" which look like runes but are not. ; See the Essay on Fake-Runes below. One such rune is shown below, ; and is the name of otherwise anonymous rules that are always considered ; enabled. When this rune is used, its use is not recorded in the ; tag-tree. (defconst *fake-rune-for-anonymous-enabled-rule* '(:FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE nil)) (defabbrev push-lemma (rune ttree) ; This is just (add-to-tag-tree 'lemma rune ttree) and is named in honor of the ; corresponding act in Nqthm. We do not record uses of the fake rune. Rather ; than pay the price of recognizing the *fake-rune-for-anonymous-enabled-rule* ; perfectly we exploit the fact that no true rune has ; :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE as its token. (cond ((eq (car rune) :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE) ttree) (t (add-to-tag-tree 'lemma rune ttree)))) ; Historical Note from the days when tag-trees were constructed using (acons ; tag value ttree) and (cons-tag-trees ttree1 ttree2): ; ; To join two trees we use cons-tag-trees. Observe that if the first tree is ; ; nil we return the second (we can't cons a nil tag-tree on and their union ; ; is the second anyway). Otherwise we cons, possibly duplicating elements. ; ; But starting in Version_3.2, we keep tagged objects unique in tag-trees, by ; ; calling scons-tag-trees when necessary, unioning the tag-trees rather than ; ; merely consing them. The immediate prompt for this change was a report ; ; from Eric Smith on getting stack overflows from tag-tree-occur, but this ; ; problem has also occurred in the past (as best Matt can recall). (defun delete-assoc-eq-assoc-eq-1 (alist1 alist2) (declare (xargs :guard (and (symbol-alistp alist1) (symbol-alistp alist2)))) (cond ((endp alist2) (mv nil nil)) (t (mv-let (changedp x) (delete-assoc-eq-assoc-eq-1 alist1 (cdr alist2)) (cond ((assoc-eq (caar alist2) alist1) (mv t x)) (changedp (mv t (cons (car alist2) x))) (t (mv nil alist2))))))) (defun delete-assoc-eq-assoc-eq (alist1 alist2) (mv-let (changedp x) (delete-assoc-eq-assoc-eq-1 alist1 alist2) (declare (ignore changedp)) x)) (defun cons-tag-trees1 (ttree1 ttree2 ttree3) ; Note: Tag-tree primitive supporter ; Accumulate into ttree3, whose keys are disjoint from those of ttree1, the ; values of keys in ttree1 each augmented by their values in ttree2. ; It might be more efficient to accumulate into ttree3, so that this function ; is tail-recursive. But we prefer that the tags at the front of ttree1 are ; also at the front of the returned ttree, since presumably the values of those ; tags are more likely to be updated frequently, and an update generates fewer ; conses the closer the tag is to the front of the ttree. (cond ((endp ttree1) ttree3) (t (let ((pair (assoc-eq (caar ttree1) ttree2))) (cond (pair (acons (caar ttree1) (union-equal (cdar ttree1) (cdr pair)) (cons-tag-trees1 (cdr ttree1) ttree2 ttree3))) (t (cons (car ttree1) (cons-tag-trees1 (cdr ttree1) ttree2 ttree3)))))))) (defun cons-tag-trees (ttree1 ttree2) ; Note: Tag-tree primitive ; We return a tag-tree whose set of keys is the union of the keys of ttree1 and ; ttree2, and whose value for each key is the union of the values of the key in ; ttree1 and ttree2 (in that order). In addition, we attempt to avoid needless ; consing. (cond ((null ttree1) ttree2) ((null ttree2) ttree1) ((null (cdr ttree2)) (let* ((pair2 (car ttree2)) (tag (car pair2)) (pair1 (assoc-eq tag ttree1))) (cond (pair1 (acons tag (union-equal (cdr pair1) (cdr pair2)) (delete-assoc-eq tag ttree1))) (t (cons pair2 ttree1))))) (t (let ((ttree3 (delete-assoc-eq-assoc-eq ttree1 ttree2))) (cons-tag-trees1 ttree1 ttree2 ttree3))))) (defmacro tagged-objects (tag ttree) ; Note: Tag-tree primitive ; See also tagged-objectsp for a corresponding predicate. `(cdr (assoc-eq ,tag ,ttree))) (defmacro tagged-objectsp (tag ttree) ; Note: Tag-tree primitive ; This is used instead of tagged-objects (but is Boolean equivalent to it) when ; we want to emphasize that our only concern is whether or not there is at ; least one tagged object associated with tag in ttree. `(assoc-eq ,tag ,ttree)) (defun tagged-object (tag ttree) ; Note: Tag-tree primitive ; This function returns obj for the unique obj associated with tag in ttree, or ; nil if there is no object with that tag. If there may be more than one ; object associated with tag in ttree, use (car (tagged-objects tag ttree)) ; instead to obtain one such object, or use (tagged-objectsp tag ttree) if you ; only want to answer the question "Is there any object associated with tag in ; ttree?". (let ((objects (tagged-objects tag ttree))) (and objects (assert$ (null (cdr objects)) (car objects))))) ; We accumulate our ttree into the state global 'accumulated-ttree so that if a ; proof attempt is aborted, we can still recover the lemmas used within it. If ; we know a ttree is going to be part of the ttree returned by a successful ; event, then we want to store it in state. We are especially concerned about ; storing a ttree if we are about to inform the user, via output, that the ; runes in it have been used. (That is, we want to make sure that if a proof ; fails after the system has reported using some rune then that rune is tagged ; as a 'lemma in the 'accumulated-ttree of the final state.) This encourages ; us to cons a new ttree into the accumulator every time we do output. (deflock *ttree-lock*) (defun@par accumulate-ttree-and-step-limit-into-state (ttree step-limit state) ; We add ttree to the 'accumulated-ttree in state and return an error triple ; whose value is ttree. Before Version_3.2 we handled tag-trees a bit ; differently, allowing duplicates and using special markers for portions that ; had already been accumulated into state. Now we keep tag-trees ; duplicate-free and avoid adding such markers to the returned value. ; We similarly save the given step-limit in state, unless its value is :skip. (declare (ignorable state)) (pprogn@par (cond ((eq step-limit :skip) (state-mac@par)) (t ; Parallelism no-fix: the following call of (f-put-global@par 'last-step-limit ; ...) may be overridden by another similar call performed by a concurrent ; thread. But we can live with that because step-limits do not affect ; soundness. (f-put-global@par 'last-step-limit step-limit state))) (cond ((eq ttree nil) (value@par nil)) (t (pprogn@par (with-ttree-lock ; In general, it is dangerous to set the same state global in two different ; threads, because the first setting is blown away by the second. But here, we ; are _accumulating_ into a state global (namely, 'accumulated-ttree), and we ; don't care about the order in which the accumulation occurs (even though such ; nondeterminism isn't explained logically -- after all, we are modifying state ; without passing it in, so we already are punting on providing a logical story ; here). Our only concern is that two such accumulations interfere with each ; other, but the lock just above takes care of that (i.e., provides mutual ; exclusion). (f-put-global@par 'accumulated-ttree (cons-tag-trees ttree (f-get-global 'accumulated-ttree state)) state)) (value@par ttree)))))) (defun pts-to-ttree-lst (pts) (cond ((null pts) nil) (t (cons (add-to-tag-tree! 'pt (car pts) nil) (pts-to-ttree-lst (cdr pts)))))) ; Previously, we stored the parents of a poly in the poly's :ttree field ; and used to-be-ignoredp. However, tests have shown that under certain ; conditions to-be-ignoredp was taking up to 80% of the time spent by ; add-poly. We now store the poly's parents in a seperate field and ; use ignore-polyp. The next few functions are used in the implementation ; of this change. (defun marry-parents (parents1 parents2) ; We return the 'eql union of the two arguments. When we create a ; new poly from two other polys via cancellation, we need to ensure ; that the new poly depends on all the literals that either of the ; others do. (if (null parents1) parents2 (marry-parents (cdr parents1) (add-to-set-eql (car parents1) parents2)))) (defun collect-parents1 (pt ans) (cond ((null pt) ans) ((consp pt) (collect-parents1 (car pt) (collect-parents1 (cdr pt) ans))) (t (add-to-set-eql pt ans)))) (defun collect-parents0 (pts ans) (cond ((null pts) ans) (t (collect-parents0 (cdr pts) (collect-parents1 (car pts) ans))))) (defun collect-parents (ttree) ; We accumulate in reverse order all the objects (parents) in the pts in the ; ttree. When we create a new poly via linearize, we extract a list of all its ; parents from the poly's 'ttree and store this list in the poly's 'parents ; field. This function does the extracting. (collect-parents0 (tagged-objects 'pt ttree) nil)) (defun ignore-polyp (parents pt) ; Consider the set, P, of all parents mentioned in the list parents. ; Consider the set, B, of all parents mentioned in the parent tree pt. We ; return t iff P and B have a non-empty intersection. From a more applied ; perspective, assuming parents is the parents list associated with some ; poly, P is the set of literals upon which the poly depends. B is ; generally the set of literals we are to avoid dependence upon. The poly ; should be ignored if it depends on some literal we are to avoid. (if (null parents) nil (or (pt-occur (car parents) pt) (ignore-polyp (cdr parents) pt)))) (defun to-be-ignoredp1 (pts pt) (cond ((endp pts) nil) (t (or (pt-intersectp (car pts) pt) (to-be-ignoredp1 (cdr pts) pt))))) (defun to-be-ignoredp (ttree pt) ; Consider the set, P, of all parents mentioned in the 'pt tags of ttree. ; Consider the set, B, of all parents mentioned in the parent tree pt. We ; return t iff P and B have a non-empty intersection. From a more applied ; perspective, assuming ttree is the tree associated with some poly, P is the ; set of literals upon which the poly depends. B is generally the set of ; literals we are to avoid dependence upon. The poly should be ignored if it ; depends on some literal we are to avoid. ; This function was originally written to do the job described above. But then ; Robert Krug suggested the efficiency of maintaining the parents list and ; introduced ignore-polyp. Now this function is only used elsewhere, but the ; above comments still apply mutatis mutandis. (to-be-ignoredp1 (tagged-objects 'pt ttree) pt)) ;================================================================= ; Assumptions ; We are prepared to force assumptions of certain terms by adding ; them to the tag-tree under the 'assumption tag. This is always done ; via force-assumption. All assumptions are embedded in an ; assumption record: (defrec assumnote (cl-id rune . target) t) ; The cl-id is the clause id (as maintained by the waterfall) of the clause ; currently being worked upon. Rune is either the rune (or a symbol, as per ; force-assumption) that forced this assumption. Target is the term to which ; rune was being applied. Because the :assumnotes field of an assumption is ; always non-nil, there is at least one assumnote in it, but the cl-id field in ; that assumnote might be nil because we do not know the clause id just yet. ; We fill in the :cl-id field later so that we don't have to pass such static ; information all the way down to the places where assumptions are forced. ; When an assumption is generated, it has exactly one assumnote. But later we ; will "merge" assumptions together (actually, delete some via subsumption) and ; when we do we will union the assumnotes together to keep track of why we are ; dealing with that assumption. (defrec assumption ((type-alist . term) immediatep rewrittenp . assumnotes) t) ; An assumption record records the fact that we must prove term under ; the assumption of type-alist. Immediatep indicates whether it is ; the user's desire to split the main goal on term immediately ; (immediatep = 'case-split), prove the term under alist immediately ; (t) or delay the proof to a forcing round (nil). ; WARNING: The system can be unsound if immediatep takes on any but ; these three values. In functions like collect-assumptions we assume ; that collecting all the 'case-splits and then collecting all the t's ; gets all the non-nils! ; Assumnotes is involved with explaining to the user what we are doing. It is ; a non-empty list of assumnote records. ; We now turn to the question of whether term has been rewritten or not. If it ; has not been, and we know the context in which rewriting should be tried, it ; is presumably a good idea to try to rewrite term before we try a full-fledged ; proof: a proof requires converting the type-alist and term into a clause and ; then simplifying all the literals of that clause, whereas we expect many ; times that the type-alist will allow term to rewrite to t. One might ask why ; we don't always rewrite before forcing. The answer is simple: type-set ; forces and cannot use the rewriter because it is defined well before the ; rewriter. So type-set forces unrewritten terms often. The problem with the ; simple idea of trying first to prove those terms by rewriting is that REWRITE ; takes many additional context-specifying arguments, the most complicated ; being the simplify-clause-pot-lst. Having set the stage for an explanation, ; we now give it: ; Rewrittenp indicates whether we have already tried to rewrite term. Recall ; that relieve-hyp first rewrites and forces the rewritten term only if ; rewriting fails. Thus, at least within the rewriter, we will see both ; rewritten and unrewritten assumptions coming back in the ttrees we generate. ; Rewrittenp is either a term or nil. If it is a term, forced-term, then it is ; the term we were asked to force and term is the result of rewriting ; forced-term. We use the unrewritten term in a heuristic that sometimes ; throws out supposedly irrelevant hypotheses from the clauses we ultimately ; prove to establish the assumptions. See the discussion of "disguarding." If ; rewrittenp is nil, we have not yet tried to rewrite term and term is ; literally what was forced. The simplifier will collect the unrewritten ; assumptions generated during rewrite and will rewrite them in the ; "appropriate context" as discussed below. ; The view we take is that from within the rewriter, all assumptions are ; rewritten before being forced. That cannot be implemented directly, so ; we do it cleverly, by rewriting them after the force but not telling ; the user. It just seems like a good idea for the rewriter, of all the ; processes, to produce only rewritten assumptions. Now those rewritten ; assumptions aren't maximally rewritten. For example, an assumption ; might rewrite to an if and normalization etc. might produce a provable ; set of assumptions. But we do not use normalization or clausification on ; assumptions until it is time to hit them with the full prover. ; The following record definition is decidedly out of place, belonging as it ; does to the code for forward-chaining. But we must make it now to allow ; us to define contain-assumptionp. This record is documented in comments ; in the essay entitled: "Forward Chaining Derivations - fc-derivation - fcd" (defrec fc-derivation (((concl . ttree) . (fn-cnt . p-fn-cnt)) . ((inst-trigger . rune) . (fc-round . unify-subst))) t) ; WARNING: If you change fc-derivation, go visit the "virtual" declaration of ; the record in simplify.lisp and update the comments. See the essay "Forward ; Chaining Derivations - fc-derivation - fcd". (mutual-recursion (defun contains-assumptionp (ttree) ; We return t iff ttree contains an assumption "at some level" where we ; know that 'fc-derivations contain ttrees that may contain assumptions. ; See the discussion in force-assumption. (or (tagged-objectsp 'assumption ttree) (contains-assumptionp-fc-derivations (tagged-objects 'fc-derivation ttree)))) (defun contains-assumptionp-fc-derivations (lst) (cond ((endp lst) nil) (t (or (contains-assumptionp (access fc-derivation (car lst) :ttree)) (contains-assumptionp-fc-derivations (cdr lst)))))) ) (defun remove-assumption-entries-from-type-alist (type-alist) ; We delete from type-alist any entry, (term ts . ttree), whose ttree contains ; an assumption. Thus, if ttree2 below is the ; only one of the three to contain an assumption, the type-alist ; ((v1 ts1 . ttree1)(v2 ts2 . ttree2)(v3 ts3 . ttree3)) ; is transformed into ; ((v1 ts1 . ttree1)(v3 ts3 . ttree3)). ; It is always sound to delete a hypothesis. See the discussion in ; force-assumption. (cond ((endp type-alist) nil) ((contains-assumptionp (cddar type-alist)) (remove-assumption-entries-from-type-alist (cdr type-alist))) (t (cons (car type-alist) (remove-assumption-entries-from-type-alist (cdr type-alist)))))) (defun force-assumption1 (rune target term type-alist rewrittenp immediatep ttree) (let* ((term (cond ((equal term *nil*) (er hard 'force-assumption "Attempt to force nil!")) ((null rune) (er hard 'force-assumption "Attempt to force the nil rune!")) (t term)))) (cond ((not (member-eq immediatep '(t nil case-split))) (er hard 'force-assumption1 "The :immediatep of an ASSUMPTION record must be ~ t, nil, or 'case-split, but we have tried to create ~ one with ~x0." immediatep)) (t (add-to-tag-tree 'assumption (make assumption :type-alist type-alist :term term :rewrittenp rewrittenp :immediatep immediatep :assumnotes (list (make assumnote :cl-id nil :rune rune :target target))) ttree))))) (defun dumb-occur-in-type-alist (var type-alist) (cond ((null type-alist) nil) ((dumb-occur var (caar type-alist)) t) (t (dumb-occur-in-type-alist var (cdr type-alist))))) (defun all-dumb-occur-in-type-alist (vars type-alist) (cond ((null vars) t) (t (and (dumb-occur-in-type-alist (car vars) type-alist) (all-dumb-occur-in-type-alist (cdr vars) type-alist))))) (defun force-assumption (rune target term type-alist rewrittenp immediatep force-flg ttree) ; Warning: Rune may not be a rune! It may be a function symbol. ; This function adds (implies type-alist' term) as an 'assumption to ttree. ; Rewrittenp is either nil, meaning term has not yet been rewritten, or is the ; term that was rewritten to obtain term. Rune is the name of the rule in ; whose behalf term is being assumed, and rune is being applied to the target ; term target. If rune is a symbol then it is actually a primitive ; function symbol and this is a split on the guard of that symbol. There is ; even an exception to that: sometimes rune is the function symbol equal. But ; the guard of equal is t and so is never forced! What is going on? In ; linearize we force term2 to be real if term1 is real and we are ; linearizing (equal term1 term2). ; The type-alist actually stored in the assumption record, type-alist', is not ; type-alist! We remove from type-alist all the entries depending upon ; assumptions. It is legitimate to throw away any hypothesis, thus we can ; delete the entries we choose. Why do we throw out the type-alist entries ; depending on assumptions? The reason is that in the forcing round we ; actually generate a formula representing (implies type-alist' term) and this ; formula does not encode the fact that a given hyp depends upon certain ; assumptions. ; Because assumptions can be generated during forward chaining, the type-alist ; may contain 'fc-derivations tags among its ttrees. These records record how ; a given hypothesis was derived and may itself have 'assumptions in its ttree. ; We therefore consider a ttree to "contain assumptions" if it contains an ; fc-derivation that contains assumptions. ; It could be thought that the creation of type-alist' from type-alist is ; merely an efficiency aimed at saving a few conses. This is not correct. ; This change has a dramatic effect on the size of our ttrees. Before we did ; this, it was possible for a ttree to contain an assumption which (by virtue ; of the :type-alist) contained a ttree which contained an assumption which ; contained a ttree, etc. We have seen this sort of thing nested to depth 9. ; Furthermore, it was frequently the case that a ttree contained some proper ; subttree x which occurred also in an assumption contained in the parent ; ttree. Thus, the ttree x was duplicated. While the parent ttree was small ; (in the sense that it contained on a few nodes) the tree was very large when ; printed, because of this duplication. We have seen a ttree that contained 5 ; million nodes (when explored in this root-and-branch way through 'assumptions ; and 'fc-derivations) but which actually was composed of only 100 distinct ; (non-equal) subtrees. Again, one might think this was a problem only if one ; printed out the ttree, but some processes, such as expunge-fc-derivations, do ; root-and-branch exploration. On the tree in question the system simply hung ; up and appeared to be in an infinite loop. This fix keeps ttrees small (even ; when viewed in the root-and-branch way) and is crucial to our practice of ; using them. ; Once upon a time, we allowed rune to be nil. We have since changed that and ; now use the *fake-rune-for-anonymous-enabled-rule* when we don't know a ; better rune. But we have put a check in here to make sure no one uses the ; nil "rune" anymore. Wanting a genuine rune here is just a reflection of the ; output routine that explains the origins of each forcing round. ; Force-flg is known to be non-nil; it may be either t or 'weak. It's tempting ; to allow force-flg = nil and handle that case here (trivially), but the case ; structure in functions like type-set-binary-+ suggests that it's better to ; deal with that case up front, in order to avoid lots of tests that are ; irrelevant (since the same trivial thing happens in all cases when force-flg ; is nil). ; This function is a No-Change Loser, meaning that if it fails and returns nil ; as its first result, it returns the unmodified ttree as its second. Note ; that either force-flg or nil is returned as the first argument; hence, a ; "successful" force with force-flg = 'weak will result in an unchanged ; force-flg being returned. If the first value returned is nil, we are to ; pretend that we weren't allowed to force in the first place. ; At the time of this writing we have temporarily abandoned the idea of ; allowing force-flg to be 'weak: it will always be t or nil. See the comment ; in ok-to-force. (let ((type-alist (remove-assumption-entries-from-type-alist type-alist))) (cond ((not force-flg) (mv force-flg (er hard 'force-assumption "Force-assumption called with null force-flg!"))) ; We experimented with allowing force-flg to be 'weak. However, currently ; force-flg is known to be t or nil. See the comment in ok-to-force. ; ((or (eq force-flg t) ; (all-dumb-occur-in-type-alist (all-vars term) type-alist)) ; (mv force-flg ; (force-assumption1 ; rune target term type-alist rewrittenp immediatep ttree))) ; (t ; (mv nil ttree)) (t (mv force-flg (force-assumption1 rune target term type-alist rewrittenp immediatep ttree)))))) (defun tag-tree-occur-assumption-nil-1 (lst) (cond ((endp lst) nil) ((equal (access assumption (car lst) :term) *nil*) t) (t (tag-tree-occur-assumption-nil-1 (cdr lst))))) (defun tag-tree-occur-assumption-nil (ttree) ; This is just (tag-tree-occur 'assumption <*nil*> ttree) where by <*nil*> we ; mean any assumption record with :term *nil*. (tag-tree-occur-assumption-nil-1 (tagged-objects 'assumption ttree))) (defun assumption-free-ttreep (ttree) ; This is a predicate that returns t if ttree contains no 'assumption tag. It ; also checks for 'fc-derivation tags, since they could hide 'assumptions. An ; error-causing version of this function is chk-assumption-free-ttree. Keep ; these two in sync. ; This check is stronger than necessary, of course, since an fc-derivation ; object need not contain an assumption. See also contains-assumptionp (and ; chk-assumption-free-ttree-1) for a slightly more expensive, but more precise, ; check. (cond ((tagged-objectsp 'assumption ttree) nil) ((tagged-objectsp 'fc-derivation ttree) nil) (t t))) ; The following assumption is impossible to satisfy and is used as a marker ; that we sometimes put into a ttree to indicate to our caller that the ; attempted force should be abandoned. (defconst *impossible-assumption* (make assumption :type-alist nil :term *nil* :rewrittenp *nil* :immediatep nil ; must be t, nil, or 'case-split :assumnotes (list (make assumnote :cl-id nil :rune *fake-rune-for-anonymous-enabled-rule* :target *nil*)))) ;================================================================= ; We are about to get into the linear arithmetic stuff quite heavily. ; This code started in Nqthm in 1979 and migrated more or less ; untouched into ACL2, with the exception of the addition of the ; rationals. However, around 1998, Robert Krug began working on an ; improved arithmetic book and after a year or so realized he wanted ; to make serious changes in the linear arithmetic procedures. ; Robert's hand is now felt all over this code. ; Essay on the Logical Basis for Linear Arithmetic. ; This essay was written for some early version of ACL2. It still ; applies to the linear arithmetic decision procedure as of Version_2.7, ; although some of the details may need revision. ; We list here the "algebraic laws" we assume. We point back to this ; list from the places we assume them. It is crucial to realize that ; by < and + here we do not mean the familiar "guarded" functions of ; Common Lisp and algebra, but rather the "completed" functions of the ; ACL2 logic. In particular, nonnumeric arguments to + are defaulted ; to 0 and complex numbers may be added to rational ones to yield ; complex ones, etc. The < relation coerces nonnumeric arguments to 0 ; and then compares the resulting numbers lexicographically on the ; real and imaginary parts, using the familiar less-than relation on ; the rationals. ; Let us use << as the "familiar" less-than. Then ; (< x y) = (let ((x1 (if (acl2-numberp x) x 0)) ; (y1 (if (acl2-numberp y) y 0))) ; (or (<< (realpart x1) (realpart y1)) ; (and (= (realpart x1) (realpart y1)) ; (<< (imagpart x1) (imagpart y1))))) ; The wonderful thing about this definition, is that it enjoys the algebraic ; laws we need to support linear arithmetic. The "box" below contains the ; complete listing of the algegraic laws supporting linear arithmetic ; ("alsla"). ; However, interspersed around them in the box are some events that ACL2's ; completed < and + have the ALSLA properties. To enable us to use the theorem ; prover, we define some new symbols like < and + and prove that those symbols ; have the desired properties. This is a bit tricky because the completed < ; and + must be defined in terms of the partial < and + which work on the ; rationals and complexes, respectively, and we do not want to rely on any ; built in properties of those primitive symbols. ; Therefore, we constrain three new symbols, PLUS, TIMES, and LESSP which you ; may think of as being the familiar, partial versions of +, *, and <. ; (Indeed, the witnesses in the constraints are those primitives. The ; encapsulate below merely exports the properties that we are going to assume.) ; Then we define completed versions of these functions, called CPLUS, CTIMES, ; and CLESSP and we prove the ALSLA properties of those functions. ; Note: This exercise is still suspicious because it involves equality ; goals between arithmetic terms and there is no reason to believe that our ; "untrusted" linear arithmetic isn't contributing to their proof. Well, a ; search through the output produces no sign of "linear" after the ; encapsulation below, but that could indicate an io bug. A more convincing ; proof would be to eliminate the use of the arithmetic data types altogether ; but that would be a little nasty, faking rationals and complexes. A still ; more convincing proof would be to construct the proof formally, as we hope to ; do when we have proof objects. ; (progn ; ; ; Perhaps this axiom can be proved from given ones, but I haven't taken the ; ; time to work it out. I will add it. I believe it! ; ; (defaxiom *-preserves-< ; (implies (and (rationalp c) ; (rationalp x) ; (rationalp y) ; (< 0 c)) ; (equal (< (* c x) (* c y)) ; (< x y)))) ; ; (defthm realpart-rational ; (implies (rationalp x) (equal (realpart x) x))) ; ; (defthm imagpart-rational ; (implies (rationalp x) (equal (imagpart x) 0))) ; ; (encapsulate (((plus * *) => *) ; ((times * *) => *) ; ((lessp * *) => *)) ; ; ; Plus and lessp here are the rational versions of those functions. They are ; ; intended to be the believable, intuitive, functions. You should read the ; ; properties we export to make sure you believe that the high school plus and ; ; lessp have those properties. We prove the properties, but we prove them from ; ; witnesses of plus and lessp that are ACL2's completed + and < supported by ; ; ACL2's linear arithmetic and hence, if the soundness of ACL2's arithmetic is ; ; in doubt, as it is in this exercise, then no assurrance can be drawn from the ; ; constructive nature of this axiomatization of rational arithmetic. ; ; (local (defun plus (x y) ; (declare (xargs :verify-guards nil)) ; (+ x y))) ; (local (defun times (x y) ; (declare (xargs :verify-guards nil)) ; (* x y))) ; (local (defun lessp (x y) ; (declare (xargs :verify-guards nil)) ; (< x y))) ; (defthm rationalp-plus ; (implies (and (rationalp x) ; (rationalp y)) ; (rationalp (plus x y))) ; :rule-classes (:rewrite :type-prescription)) ; (defthm plus-0 ; (implies (rationalp x) ; (equal (plus 0 x) x))) ; (defthm plus-commutative-and-associative ; (and (implies (and (rationalp x) ; (rationalp y)) ; (equal (plus x y) (plus y x))) ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp z)) ; (equal (plus x (plus y z)) ; (plus y (plus x z)))) ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp z)) ; (equal (plus (plus x y) z) ; (plus x (plus y z)))))) ; (defthm rationalp-times ; (implies (and (rationalp x) ; (rationalp y)) ; (rationalp (times x y)))) ; (defthm times-commutative-and-associative ; (and (implies (and (rationalp x) ; (rationalp y)) ; (equal (times x y) (times y x))) ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp z)) ; (equal (times x (times y z)) ; (times y (times x z)))) ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp z)) ; (equal (times (times x y) z) ; (times x (times y z))))) ; :hints ; (("Subgoal 2" ; :use ((:instance associativity-of-*) ; (:instance commutativity-of-* (x x)(y (* y z))))))) ; (defthm times-distributivity ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp z)) ; (equal (times x (plus y z)) ; (plus (times x y) (times x z))))) ; (defthm times-0 ; (implies (rationalp x) ; (equal (times 0 x) 0))) ; (defthm times-1 ; (implies (rationalp x) ; (equal (times 1 x) x))) ; (defthm plus-inverse ; (implies (rationalp x) ; (equal (plus x (times -1 x)) 0)) ; :hints ; (("Goal" ; :use ((:theorem (implies (rationalp x) ; (not (< 0 (+ x (* -1 x)))))) ; (:theorem (implies (rationalp x) ; (not (< (+ x (* -1 x)) 0)))))))) ; (defthm plus-inverse-unique ; (implies (and (rationalp x) ; (rationalp y) ; (equal (plus x (times -1 y)) 0)) ; (equal x y)) ; :rule-classes nil) ; (defthm lessp-irreflexivity ; (implies (rationalp x) ; (not (lessp x x)))) ; (defthm lessp-antisymmetry ; (implies (and (rationalp x) ; (rationalp y) ; (lessp x y)) ; (not (lessp y x)))) ; (defthm lessp-trichotomy ; (implies (and (rationalp x) ; (rationalp y) ; (not (equal x y)) ; (not (lessp x y))) ; (lessp y x))) ; (defthm lessp-plus ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp u) ; (rationalp v) ; (lessp x y) ; (not (lessp v u))) ; (lessp (plus x u) (plus y v)))) ; (defthm not-lessp-plus ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp u) ; (rationalp v) ; (not (lessp y x)) ; (not (lessp v u))) ; (not (lessp (plus y v) (plus x u))))) ; (defthm 1+trick-for-lessp ; (implies (and (integerp x) ; (integerp y) ; (lessp x y)) ; (not (lessp y (plus 1 x))))) ; (defthm times-positive-preserves-lessp ; (implies (and (rationalp c) ; (rationalp x) ; (rationalp y) ; (lessp 0 c)) ; (equal (lessp (times c x) (times c y)) ; (lessp x y))))) ; ; ; Now we "complete" +, *, <, and <= to the complex rationals and thence to the ; ; entire universe. The results are CPLUS, CTIMES, CLESSP, and CLESSEQP. You ; ; should buy into the claim that these functions are what we intended in ACL2's ; ; completed arithmetic. ; ; ; Note: At first sight it seems odd to do it this way. Why not just assume ; ; plus, above, is the familiar operation on the complex rationals? We tried ; ; it and it didn't work very well, because ACL2 does not reason very well ; ; about complex arithmetic. It seemed more direct to make the definition of ; ; complex addition and multiplication be explicit for the purposes of this ; ; proof. ; ; (defun cplus (x y) ; (declare (xargs :verify-guards nil)) ; (let ((x1 (fix x)) ; (y1 (fix y))) ; (complex (plus (realpart x1) (realpart y1)) ; (plus (imagpart x1) (imagpart y1))))) ; ; (defun ctimes (x y) ; (declare (xargs :verify-guards nil)) ; (let ((x1 (fix x)) ; (y1 (fix y))) ; (complex (plus (times (realpart x1) (realpart y1)) ; (times -1 (times (imagpart x1) (imagpart y1)))) ; (plus (times (realpart x1) (imagpart y1)) ; (times (imagpart x1) (realpart y1)))))) ; ; (defun clessp (x y) ; (declare (xargs :verify-guards nil)) ; (let ((x1 (fix x)) ; (y1 (fix y))) ; (or (lessp (realpart x1) (realpart y1)) ; (and (equal (realpart x1) (realpart y1)) ; (lessp (imagpart x1) (imagpart y1)))))) ; ; (defun clesseqp (x y) ; (declare (xargs :verify-guards nil)) ; (not (clessp y x))) ; ; ; A trivial theorem about fix, allowing us hereafter to disable it. ; ; (defthm fix-id (implies (acl2-numberp x) (equal (fix x) x))) ; ; (in-theory (disable fix)) ; ; ;----------------------------------------------------------------------------- ; ; The Algebraic Laws Supporting Linear Arithmetic (ALSLA) ; ; ; All the operators FIX their arguments ; ; (equal (+ x y) (+ (fix x) (fix y))) ; ; (equal (* x y) (* (fix x) (fix y))) ; ; (equal (< x y) (< (fix x) (fix y))) ; ; (fix x) = (if (acl2-numberp x) x 0) ; ; (defthm operators-fix-their-arguments ; (and (equal (cplus x y) (cplus (fix x) (fix y))) ; (equal (ctimes x y) (ctimes (fix x) (fix y))) ; (equal (clessp x y) (clessp (fix x) (fix y))) ; (equal (fix x) (if (acl2-numberp x) x 0))) ; :rule-classes nil ; :hints (("Subgoal 1" :in-theory (enable fix)))) ; ; ; + Associativity, Commutativity, and Zero ; ; (equal (+ (+ x y) z) (+ x (+ y z))) ; ; (equal (+ x y) (+ y x)) ; ; (equal (+ 0 y) (fix y)) ; ; (defthm cplus-associativity-etc ; (and (equal (cplus (cplus x y) z) (cplus x (cplus y z))) ; (equal (cplus x y) (cplus y x)) ; (equal (cplus 0 y) (fix y)))) ; ; ; * Distributes Over + ; ; (equal (+ (* c x) (* d x)) (* (+ c d) x)) ; ; (defthm ctimes-distributivity ; (equal (cplus (ctimes c x) (ctimes d x)) (ctimes (cplus c d) x))) ; ; ; * Associativity, Commutativity, Zero and One ; ; (equal (* (* x y) z) (* x (* y z))) ; See note below ; ; (equal (* x y) (* y x)) ; ; (equal (* 0 x) 0) ; ; (equal (* 1 x) (fix x)) ; ; (defthm ctimes-associativity-etc ; (and (equal (ctimes (ctimes x y) z) (ctimes x (ctimes y z))) ; (equal (ctimes x y) (ctimes y x)) ; (equal (ctimes 0 y) 0) ; (equal (ctimes 1 x) (fix x)))) ; ; ; + Inverse ; ; (equal (+ x (* -1 x)) 0) ; ; (defthm cplus-inverse ; (equal (cplus x (ctimes -1 x)) 0)) ; ; ; Reflexivity of <= ; ; (<= x x) ; ; (defthm clesseqp-reflexivity ; (clesseqp x x)) ; ; ; Antisymmetry ; ; (implies (< x y) (not (< y x))) ; (implies (< x y) (<= x y)) ; ; (defthm clessp-antisymmetry ; (implies (clessp x y) ; (not (clessp y x)))) ; ; ; Trichotomy ; ; (implies (and (acl2-numberp x) ; ; (acl2-numberp y)) ; ; (or (< x y) ; ; (< y x) ; ; (equal x y))) ; ; (defthm clessp-trichotomy ; (implies (and (acl2-numberp x) ; (acl2-numberp y)) ; (or (clessp x y) ; (clessp y x) ; (equal x y))) ; :rule-classes nil) ; ; ; Additive Properties of < and <= ; ; (implies (and (< x y) (<= u v)) (< (+ x u) (+ y v))) ; ; (implies (and (<= x y) (<= u v)) (<= (+ x u) (+ y v))) ; ; ; We have to prove three lemmas first. But then we nail these suckers! ; ; (defthm not-lessp-plus-instance-u=v ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp u) ; (not (lessp y x))) ; (not (lessp (plus y u) (plus x u))))) ; ; (defthm lessp-plus-commuted1 ; (implies (and (rationalp x) ; (rationalp y) ; (rationalp u) ; (rationalp v) ; (lessp x y) ; (not (lessp v u))) ; (lessp (plus u x) (plus v y))) ; :hints (("goal" :use (:instance lessp-plus)))) ; ; (defthm irreflexive-revisited-and-commuted ; (implies (and (rationalp x) ; (rationalp y) ; (lessp y x)) ; (equal (equal x y) nil))) ; ; (defthm clessp-additive-properties ; (and (implies (and (clessp x y) ; (clesseqp u v)) ; (clessp (cplus x u) (cplus y v))) ; (implies (and (clesseqp x y) ; (clesseqp u v)) ; (clesseqp (cplus x u) (cplus y v))))) ; ; ; The 1+ Trick ; ; (implies (and (integerp x) ; ; (integerp y) ; ; (< x y)) ; ; (<= (+ 1 x) y)) ; ; (defthm cplus-1-trick ; (implies (and (integerp x) ; (integerp y) ; (clessp x y)) ; (clesseqp (cplus 1 x) y))) ; ; ; Cross-Multiplying Allows Cancellation ; ; (implies (and (< c1 0) ; ; (< 0 c2)) ; ; (equal (+ (* c1 (abs c2)) (* c2 (abs c1))) 0)) ; ; ; Three lemmas lead to the result. ; ; (defthm times--1--1 ; (equal (times -1 -1) 1) ; :hints ; (("goal" ; :use ((:instance plus-inverse-unique (x (times -1 -1)) (y 1)))))) ; ; (defthm times--1-times--1 ; (implies (rationalp x) ; (equal (times -1 (times -1 x)) x)) ; :hints (("Goal" ; :use (:instance times-commutative-and-associative ; (x -1) ; (y -1) ; (z x))))) ; (defthm reassocate-to-cancel-plus ; (implies (and (rationalp x) ; (rationalp y)) ; (equal (plus x (plus y (plus (times -1 x) (times -1 y)))) ; 0)) ; :hints ; (("Goal" :use ((:instance plus-commutative-and-associative ; (x y) ; (y (times -1 x)) ; (z (times -1 y))))))) ; ; ; Multiplication by Positive Preserves Inequality ; ;(implies (and (rationalp c) ; see note below ; ; (< 0 c)) ; ; (iff (< x y) ; ; (< (* c x) (* c y)))) ; ; (defthm multiplication-by-positive-preserves-inequality ; (implies (and (rationalp c) ; (clessp 0 c)) ; (iff (clessp x y) ; (clessp (ctimes c x) (ctimes c y))))) ; ; ; The Zero Trichotomy Trick ; ; (implies (and (acl2-numberp x) ; ; (not (equal x 0)) ; ; (not (equal x y))) ; ; (or (< x y) (< y x))) ; ; (defthm complex-equal-0 ; (implies (and (rationalp x) ; (rationalp y)) ; (equal (equal (complex x y) 0) ; (and (equal x 0) ; (equal y 0))))) ; ; (defthm zero-trichotomy-trick ; (implies (and (acl2-numberp x) ; (not (equal x 0)) ; (not (equal x y))) ; (or (clessp x y) (clessp y x))) ; :rule-classes nil :hints (("goal" :in-theory (enable fix)))) ; ; ; ; The Find Equational Poly Trick ; ; (implies (and (<= x y) (<= y x)) (equal (fix x) (fix y))) ; ; (defthm find-equational-poly-trick ; (implies (and (clesseqp x y) ; (clesseqp y x)) ; ; (equal (fix x) (fix y))) ; :hints (("Goal" :in-theory (enable fix)))) ; ; ) ;----------------------------------------------------------------------------- ; Thus ends the ALSLA. However, there are, no doubt, a few more that we ; will discover when we implement proof objects! ; Note that in Multiplication by Positive Preserves Inequality we require the ; positive to be rational. Multiplication by a "positive" complex rational ; does not preserve the inequality. For example, the following evaluates ; to nil: ; (let ((c #c(1 -10)) ; (x #c(1 1)) ; (y #c(2 -2))) ; (implies (and ; (rationalp c) ; omit the rationalp hyp ; (< 0 c)) ; (iff (< x y) ; t ; (< (* c x) (* c y))))) ; nil ; Thus, the coefficients in our polys must be rational. ; End of Essay on the Logical Basis for Linear Arithmetic. (deflabel linear-arithmetic :doc ":Doc-Section Miscellaneous A description of the linear arithmetic decision procedure~/~/ We describe the procedure very roughly here. Fundamental to the procedure is the notion of a linear polynomial inequality. A ``linear polynomial'' is a sum of terms, each of which is the product of a rational constant and an ``unknown.'' The ``unknown'' is permitted to be ~c[1] simply to allow a term in the sum to be constant. Thus, an example linear polynomial is ~c[3*x + 7*a + 2]; here ~c[x] and ~c[a] are the (interesting) unknowns. However, the unknowns need not be variable symbols. For example, ~c[(length x)] might be used as an unknown in a linear polynomial. Thus, another linear polynomial is ~c[3*(length x) + 7*a]. A ``linear polynomial inequality'' is an inequality (either ~ilc[<] or ~ilc[<=]) relation between two linear polynomials. Note that an equality may be considered as a pair of inequalities; e.q., ~c[3*x + 7*a + 2 = 0] is the same as the conjunction of ~c[3*x + 7*a + 2 <= 0] and ~c[0 <= 3*x + 7*a + 2]. Certain linear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third inequality with fewer unknowns. If this deduced inequality is manifestly false, a contradiction has been deduced from the assumed inequalities. For example, suppose we have two assumptions ~bv[] p1: 3*x + 7*a < 4 p2: 3 < 2*x ~ev[] and we wish to prove that, given ~c[p1] and ~c[p2], ~c[a < 0]. As suggested above, we proceed by assuming the negation of our goal ~bv[] p3: 0 <= a. ~ev[] and looking for a contradiction. By cross-multiplying and adding the first two inequalities, (that is, multiplying ~c[p1] by ~c[2], ~c[p2] by ~c[3] and adding the respective sides), we deduce the intermediate result ~bv[] p4: 6*x + 14*a + 9 < 8 + 6*x ~ev[] which, after cancellation, is: ~bv[] p4: 14*a + 1 < 0. ~ev[] If we then cross-multiply and add ~c[p3] to ~c[p4], we get ~bv[] p5: 1 <= 0, ~ev[] a contradiction. Thus, we have proved that ~c[p1] and ~c[p2] imply the negation of ~c[p3]. All of the unknowns of an inequality must be eliminated by cancellation in order to produce a constant inequality. We can choose to eliminate the unknowns in any order, but we eliminate them in term-order, largest unknowns first. (~l[term-order].) That is, two polys are cancelled against each other only when they have the same largest unknown. For instance, in the above example we see that ~c[x] is the largest unknown in each of ~c[p1] and ~c[p2], and ~c[a] in ~c[p3] and ~c[p4]. Now suppose that this procedure does not produce a contradiction but instead yields a set of nontrivial inequalities. A contradiction might still be deduced if we could add to the set some additional inequalities allowing further cancellations. That is where ~c[:linear] lemmas come in. When the set of inequalities has stabilized under cross-multiplication and addition and no contradiction is produced, we search the database of ~c[:]~ilc[linear] rules for rules about the unknowns that are candidates for cancellation (i.e., are the largest unknowns in their respective inequalities). ~l[linear] for a description of how ~c[:]~ilc[linear] rules are used. See also ~ilc[non-linear-arithmetic] for a description of an extension to the linear-arithmetic procedure described here.") ;================================================================= ; Arith-term-order ; As of Version_2.6, we now use a different term-order when ordering ; the alist of a poly. Arith-term-order is almost the same as ; term-order (which was used previously) except that 'UNARY-/ is ; `invisible' when it is directly inside a 'BINARY-*. The motivation ; for this change lies in an observation that, when reasoning about ; floor and mod, terms such as (< (/ x y) (floor x y)) are common. ; However, when represented within the linear-pot-lst, (BINARY-* X ; (UNARY-/ Y)) was a heavier term than (FLOOR X Y) and so the linear ; rule (<= (floor x y) (/ x y)) never got a chance to fire. Now, ; (FLOOR X Y) is the heavier term. ; Note that this function is something of a hack in that it works ; because "F" is later in the alphabet than "B". It might be better ; to allow the user to specify an order; but, if the linear rules ; present in the community books are representative this ; is sufficient. Perhaps this should be reconsidered later. ;; RAG - I thought about adding lines here for real numbers, but since we ;; cannot construct actual real constants, I don't think this is ;; needed here. Besides, I'm not sure what the right value would be ;; for a real number! (defmacro fn-count-evg-max-val () ; Warning: (* 2 (fn-count-evg-max-val)) must be a (signed-byte 30); see ; fn-count-evg-rec and max-form-count-lst. Modulo that requirement, we just ; pick a large natural number rather arbitrarily. 200000) (defmacro fn-count-evg-max-val-neg () (-f (fn-count-evg-max-val))) (defmacro fn-count-evg-max-calls () ; Warning: The following plus 2 must be a (signed-byte 30); see ; fn-count-evg-rec. ; Modulo that requirement, the choice of 1000 below is rather arbitrary. We ; chose 1000 for *default-rewrite-stack-limit*, so for no great reason we ; repeat that choice here. 1000) (defun min-fixnum (x y) ; This is a fast version of min, for fixnums. We avoid the name minf because ; it's already used in the regression suite. (declare (type (signed-byte 30) x y)) (the (signed-byte 30) (if (< x y) x y))) (defun fn-count-evg-rec (evg acc calls) ; See the comment in var-fn-count for an explanation of how this function ; counts the size of evgs. (declare (xargs :measure (acl2-count evg) :ruler-extenders :all) (type (unsigned-byte 29) acc calls)) (the (unsigned-byte 29) (cond ((or (>= calls (fn-count-evg-max-calls)) (>= acc (fn-count-evg-max-val))) (fn-count-evg-max-val)) ((atom evg) (cond ((rationalp evg) (cond ((integerp evg) (cond ((< evg 0) (cond ((<= evg (fn-count-evg-max-val-neg)) (fn-count-evg-max-val)) (t (min-fixnum (fn-count-evg-max-val) (+f 2 acc (-f evg)))))) (t (cond ((<= (fn-count-evg-max-val) evg) (fn-count-evg-max-val)) (t (min-fixnum (fn-count-evg-max-val) (+f 1 acc evg))))))) (t (fn-count-evg-rec (numerator evg) (fn-count-evg-rec (denominator evg) (1+f acc) (1+f calls)) (+f 2 calls))))) #+:non-standard-analysis ((realp evg) (prog2$ (er hard? 'fn-count-evg "Encountered an irrational in fn-count-evg!") 0)) ((complex-rationalp evg) (fn-count-evg-rec (realpart evg) (fn-count-evg-rec (imagpart evg) (1+f acc) (1+f calls)) (+f 2 calls))) #+:non-standard-analysis ((complexp evg) (prog2$ (er hard? 'fn-count-evg "Encountered a complex irrational in ~ fn-count-evg!") 0)) ((symbolp evg) (cond ((null evg) ; optimization: len below is 3 (min-fixnum (fn-count-evg-max-val) (+f 8 acc))) (t (let ((len (length (symbol-name evg)))) (cond ((<= (fn-count-evg-max-val) len) (fn-count-evg-max-val)) (t (min-fixnum (fn-count-evg-max-val) (+f 2 acc (*f 2 len))))))))) ((stringp evg) (let ((len (length evg))) (cond ((<= (fn-count-evg-max-val) len) (fn-count-evg-max-val)) (t (min-fixnum (fn-count-evg-max-val) (+f 1 acc (*f 2 len))))))) (t ; (characterp evg) (1+f acc)))) (t (fn-count-evg-rec (cdr evg) (fn-count-evg-rec (car evg) (1+f acc) (1+f calls)) (+f 2 calls)))))) (defmacro fn-count-evg (evg) `(fn-count-evg-rec ,evg 0 0)) (defun var-fn-count-1 (flg x var-count-acc fn-count-acc p-fn-count-acc invisible-fns invisible-fns-table) ; Warning: Keep this in sync with fn-count-1. ; We return a triple --- the variable count, the function count, and the ; pseudo-function count --- derived from term (and the three input ; accumulators). "Invisible" functions not inside quoted objects are ignored, ; in the sense of the global invisible-fns-table. ; The fn count of a term is the number of function symbols in the unabbreviated ; term. Thus, the fn count of (+ (f x) y) is 2. The primitives of ACL2, ; however, do not give very natural expression of the constants of the logic in ; pure first order form, i.e., as a variable-free nest of function ; applications. What, for example, is 2? It can be written (+ 1 (+ 1 0)), ; where 1 and 0 are considered primitive constants, i.e., 1 is (one) and 0 is ; (zero). That would make the fn count of 2 be 5. However, one cannot even ; write a pure first order term for NIL or any other symbol or string unless ; one adopts NIL and 'STRING as primitives too. It is probably fair to say ; that the primitives of CLTL were not designed for the inductive construction ; of the objects in an orderly way. But we would like for our accounting for a ; constant to somehow reflect the structure of the constant rather than the ; structure of the rather arbitrary CLTL term for constructing it. 'A seems to ; have relatively little to do with (intern (coerce (cons #\A 'NIL) 'STRING)) ; and it is odd that the fn count of 'A should be larger than that of 'STRING, ; and odder still that the fn count of "STRING" be larger than that of 'STRING ; since the latter is built from the former with intern. ; We therefore adopt a story for how the constants of ACL2 are actually ; constructed inductively and the pseudo-fn count is the number of symbols in ; that construction. The story is as follows. (z), zero, is the only ; primitive integer. Positive integers are constructed from it by the ; successor function s. Negative integers are constructed from positive ; integers by unary minus. Ratios are constructed by the dyadic function quo ; on an integer and a natural. For example, -2/3 is inductively built as (quo ; (- (s(s(z)))) (s(s(s(z))))). Complex rationals are similarly constructed ; from pairs of rationals. All characters are primitive and are constructed by ; the function of the same name. Strings are built from the empty string, (o), ; by "string-cons", cs, which adds a character to a string. Thus "AB" is ; formally (cs (#\A) (cs (#\B) (o))). Symbols are constructed by "packing" a ; string with p. Conses are conses, as usual. Again, this story is nowhere ; else relevant to ACL2; it just provides a consistent model for answering the ; question "how big is a constant?" (Note that we bound the pseudo-fn count; ; see fn-count-evg.) ; Previously we had made no distinction between the fn-count and the ; pseudo-fn-count, but Jun Sawada ran into difficulty because (term-order (f) ; '2). Note also that before we had ; (term-order (a (b (c (d (e (f (g (h (i x))))))))) (foo y '2/3)) ; but ; (term-order (foo y '1/2) (a (b (c (d (e (f (g (h (i x)))))))))). (declare (xargs :guard (and (if flg (pseudo-term-listp x) (pseudo-termp x)) (integerp var-count-acc) (integerp fn-count-acc) (integerp p-fn-count-acc) (symbol-listp invisible-fns) (alistp invisible-fns-table) (symbol-list-listp invisible-fns-table)) :verify-guards NIL)) (cond (flg (cond ((atom x) (mv var-count-acc fn-count-acc p-fn-count-acc)) (t (mv-let (var-cnt fn-cnt p-fn-cnt) (let* ((term (car x)) (fn (and (nvariablep term) (not (fquotep term)) (ffn-symb term))) (invp (and fn (not (flambdap fn)) ; optimization (member-eq fn invisible-fns)))) (cond (invp (var-fn-count-1 t (fargs term) var-count-acc fn-count-acc p-fn-count-acc (cdr (assoc-eq fn invisible-fns-table)) invisible-fns-table)) (t (var-fn-count-1 nil term var-count-acc fn-count-acc p-fn-count-acc nil invisible-fns-table)))) (var-fn-count-1 t (cdr x) var-cnt fn-cnt p-fn-cnt invisible-fns invisible-fns-table))))) ((variablep x) (mv (1+ var-count-acc) fn-count-acc p-fn-count-acc)) ((fquotep x) (mv var-count-acc fn-count-acc (+ p-fn-count-acc (fn-count-evg (cadr x))))) (t (var-fn-count-1 t (fargs x) var-count-acc (1+ fn-count-acc) p-fn-count-acc (and invisible-fns-table ; optimization (let ((fn (ffn-symb x))) (and (symbolp fn) (cdr (assoc-eq fn invisible-fns-table))))) invisible-fns-table)))) (defmacro var-fn-count (term invisible-fns-table) ; See the comments in var-fn-count-1. `(var-fn-count-1 nil ,term 0 0 0 nil ,invisible-fns-table)) (defmacro var-or-fn-count-< (var-count1 var-count2 fn-count1 fn-count2 p-fn-count1 p-fn-count2) ; We use this utility when deciding if an ancestors check should inhibit ; further backchaining. It says that either the var-counts are in order, or ; else the fn-counts are in (lexicographic) order. ; We added the var-counts check after analyzing an example from Robert Krug, in ; which the ancestors check was refusing to allow relieve-hyp on a ground term. ; Originally we tried a lexicographic order based on the var-count first, then ; (as before) the fn-count and p-fn-count. But this led to at least two ; regression failures that led us to reconsider. The current solution meets ; the goal of weakening the ancestors check (for example, to allow backchaining ; on ground terms as in Robert's example). (declare (xargs :guard ; avoid capture (and (symbolp var-count1) (symbolp var-count2) (symbolp fn-count1) (symbolp fn-count2) (symbolp p-fn-count1) (symbolp p-fn-count2)))) `(cond ((< ,var-count1 ,var-count2) t) ((< ,fn-count1 ,fn-count2) t) ((> ,fn-count1 ,fn-count2) nil) (t (< ,p-fn-count1 ,p-fn-count2)))) (defun term-order1 (term1 term2 invisible-fns-table) ; A simple -- or complete or total -- ordering is a relation satisfying: ; "antisymmetric" XrY & YrX -> X=Y, "transitive" XrY & Y&Z -> XrZ, and ; "trichotomy" XrY v YrX. A partial order weakens trichotomy to "reflexive" ; XrX. ; Term-order1 is a simple ordering on terms. (term-order1 term1 term2 nil) if ; and only if (a) the number of occurrences of variables in term1 is strictly ; less than the number in term2, or (b) the numbers of variable occurrences are ; equal and the fn-count of term1 is strictly less than that of term2, or (c) ; the numbers of variable occurrences are equal, the fn-counts are equal, and ; the pseudo-fn-count of term1 is strictly less than that of term2, or (d) the ; numbers of variable occurrences are equal, the fn-counts are equal, the ; pseudo-fn-counts are equal, and (lexorder term1 term2). If the third ; argument is non-nil, then it has the form as returned by function ; invisible-fns-table, and in the same manner as the table of that name, ; specifies functions to ignore when doing the above counts. However, for ; simplicity we use lexorder, independent of invisible-fns-table, if all the ; counts agree between the two terms. ; Moreover, we usually call term-order1 with a third argument of nil. The third ; argument is new in Version_3.5, as a way of eliminating the ; arithmetic-specific counting functions that had been used in defining ; function arith-term-order. It may be worth reconsidering our use of the ; wrapper term-order+ for term-order1 in loop-stopper-rec, now that a third ; argument of term-order1 makes it more flexible; but this seems unimportant. ; Fix a third argument, tbl, and let (STRICT-TERM-ORDER X Y) be the LISP ; function defined as (AND (TERM-ORDER1 X Y tbl) (NOT (EQUAL X Y))). For a ; fixed, finite set of function symbols and variable symbols STRICT-TERM-ORDER ; is well founded, as can be proved with the following lemma. ; Lemma. Suppose that M is a function whose range is well ordered by r and ; such that the inverse image of any member of the range is finite. Suppose ; that L is a total order. Define (LESSP x y) = (OR (r (M x) (M y)) (AND ; (EQUAL (M x) (M y)) (L x y) (NOT (EQUAL x y)))). < is a well-ordering. ; Proof. Suppose ... < t3 < t2 < t1 is an infinite descending sequence. ..., ; (M t3), (M t2), (M t1) is weakly descending but not infinitely descending and ; so has a least element. WLOG assume ... = (M t3) = (M t2) = (M t1). By the ; finiteness of the inverse image of (M t1), { ..., t3, t2, t1} is a finite ; set, which has a least element under L, WLOG t27. But t28 L t27 and t28 /= ; t27 by t28 < t27, contradicting the minimality of t27. QED ; If (TERM-ORDER1 x y nil) and t2 results from replacing one occurrence of y ; with x in t1, then (TERM-ORDER1 t2 t1 nil). Cases on why x is less than y. ; 1. If the number of occurrences of variables in x is strictly smaller than in ; y, then the number in t2 is strictly smaller than in t1. 2. If the number of ; occurrences of variables in x is equal to the number in y but the fn-count of ; x is smaller than the fn-count of y, then the number of variable occurrences ; in t1 is equal to the number in t2 but the fn-count of t1 is less than the ; fn-count of t2. 3. A similar argument to the above applies if the number of ; variable occurrences and fn-counts are the same but the pseudo-fn-count of x ; is smaller than that of y. 4. If the number of variable occurrences and ; parenthesis occurrences in x and y are the same, then (LEXORDER x y). ; (TERM-ORDER1 t2 t1 nil) reduces to (LEXORDER t2 t1) because the number of ; variable and parenthesis occurrences in t2 and t1 are the same. The ; lexicographic scan of t1 and t2 will be all equals until x and y are hit. (mv-let (var-count1 fn-count1 p-fn-count1) (var-fn-count term1 invisible-fns-table) (mv-let (var-count2 fn-count2 p-fn-count2) (var-fn-count term2 invisible-fns-table) (cond ((< var-count1 var-count2) t) ((> var-count1 var-count2) nil) ((< fn-count1 fn-count2) t) ((> fn-count1 fn-count2) nil) ((< p-fn-count1 p-fn-count2) t) ((> p-fn-count1 p-fn-count2) nil) (t (lexorder term1 term2)))))) (defun arith-term-order (term1 term2) (term-order1 term1 term2 '((BINARY-* UNARY-/)))) ;================================================================= ; Polys ; Historical note: Polys are now ; (< 0 (+ constant (* k1 t1) ... (* kn tn))) ; rather than ; (< (+ constant (* k1 t1) ... (* kn tn)) 0) ; as in Version_2.6 and before. (defrec poly (((alist parents . ttree) . (constant relation rational-poly-p . derived-from-not-equalityp))) t) ; A poly represents an implication hyps -> concl, where hyps is the ; conjunction of the terms in the 'assumptions of the ttree and concl is ; (< 0 (+ constant (* k1 t1) ... (* kn tn))), if relation = '< ; (<= 0 (+ constant (* k1 t1) ... (* kn tn))), otherwise. ; Constant is an ACL2 numberp, possibly complex-rationalp but usually ; rationalp. Alist is an alist of pairs of the form (ti . ki) where ti is a ; term and ki is a rationalp. The alist is kept ordered by arith-term-order on ; the ti. The largest ti is at the front. Relation is either '< or '<=. ; The ttree in a poly is a tag-tree. ; There are three tags we use here: lemma, assumption, and pt. The lemma tag ; indicates a lemma name used to produce the poly. The assumption tag ; indicates a term assumed true to produce the poly. For example, an ; assumption might be (rationalp (foo a b)). The pt tag indicates literals of ; current-clause used in the production of the poly. ; The parents field is generally a list of parents and is set-eql to the union ; over all 'pt tags in ttree of the tips of the pts. (But see the comment ; labeled ":parent wart" in linear-b.lisp for an exception.) It is used in the ; code that ignores polynomials descended from the current literal. (This used ; to be done by to-be-ignoredp, which used to take up to 80% of the time spent ; by add-poly.) See collect-parents and marry-parents for how we establish and ; maintain this relationship, and ignore-polyp for its use. ; Rational-poly-p is a booolean flag used in non-linear arithmetic. When it is ; true, then the right-hand side of the inequality (the polynomial) is known to ; have a rational number value. (But note that for ACL2(r), i.e. for ; #+:non-standard-analysis, the value need only be real. Through the linear ; and non-linear arithmetic code, references to "rational" should be considered ; as references to "real".) The flag is needed because of the presence of ; complex numbers in ACL2's logic. Note that sums and products of rational ; polys are rational. When rational-poly-p is true we know that the product of ; two positive polys is also positive. ; Derived-from-not-equalityp keeps track of whether the poly in question was ; derived directly from a top-level negated equality. This field is new to ; v2_8 --- previously its value was calculated as needed. In the rest of this ; comment, we address two issues --- (1) What derived-from-not-equalityp is ; used for. (2) Differences from earlier behavior. ; (1) What derived-from-not-equalityp is used for: In process-equational-polys, ; we scan through the simplify-clause-pot-lst and look for complementary pairs ; of inequalities from which we can derive an equality. Example: from (<= x y) ; and (<= y x) we can derive (equal x y). However, the two inequalities could ; have themselves been derived from the very equality we are about to generate, ; and this could lead to looping. Thus, we tag those inequalities which stem ; directly from the linearization of a (negated) equality with ; :derived-from-not-equalityp = t. This field is then examined in ; process-equational-polys (or rather its sub-functions), and the result is ; used to prune the list of candidate inequalities. ; (2) Differences from earlier behavior: ; Previously, the function descends-from-not-equalityp played the role of the ; new field :derived-from-not-equalityp. This function was much more ; conservative in its judgement and threw out any poly which descended from an ; inequality in any way, rather than only those which were derived directly ; from a (negated) equality. Matt Kaufmann noticed difference and provoked an ; email exchange with Robert Krug, who did the research and initial coding ; leading to this version of linear). Here is Robert's reply. ; Matt is right, I did inadvertantly change ACL2's meaning for ; descends-from-not-equalityp. Perhaps this change is also responsible ; for some of the patches required for the regression suite. However, ; this change was inadvertant only because I did not properly understand ; the old behaviour which seems odd to me. I believe that the new ; behaviour is the ``correct'' one. Let us look at an example: ; ; Input: ; ; x = y (1) ; a + y >= b (2) ; a + x <= b (3) ; ; After cancellation: ; ; y: x <= y (1a) ; b <= y + a (2) ; ; y <= x (1b) ; ; x: x + a <= b (3) ; ; b <= x + a (4) = (1b + 2) ; ; I think that some form of x + a = b should be generated and added to ; the clause. Under the new order, (3) and (4) would be allowed to ; combine, because neither of them descended \emph{directly} from an ; inequality. This seems like the kind of fact that I, as a user, would ; expect ACL2 to know and use. Under the old regime however, since (1b) ; was used in the derivation of (4), this was not allowed. ; ; This raises the qestion of whether the new test is too liberal. For ; example, from ; ; input: ; x = y ; a + x = b + y ; ; We would now generate the equality a = b. I do not see any harm in ; this. Perhaps another example will convince me that we need to ; tighten the heuristic up. ; [End of Robert's reply.] ; Note: In Nqthm, we thought of polynomials being inequalities in a different ; logic, or at least, in an extension of the Nqthm logic that included the ; rationals. In ACL2, we think of a poly as simply being an alternative ; represention of a term, in which we have normalized by the use of certain ; algebraic laws governing the ACL2 function symbols <, <=, +, and *. We ; noted these above (see ALSLA). In addition, we think of the operations ; performed upon polys being just ordinary inferences within the logic, ; justified by still other algebraic laws, such as that allowing the addition ; of inequalities. The basic idea behind the linear arithmetic procedure is ; to convert the (arithmetic) assumptions in a problem (including the ; negation of the conclusion) to their normal forms, make a bunch of ordinary ; forward-chaining-like inferences from those assumptions guided by certain ; principles, and if a contradiction is found, deduce that the original ; assumptions imply the original conclusion. The point is that linear ; arithmetic is not some model-theoretic step appealing to the correspondence ; of theorems in two different theories but rather an entirely ; proof-theoretic step. (defabbrev first-var (p) (caar (access poly p :alist))) (defabbrev first-coefficient (p) (cdar (access poly p :alist))) ; We expect polys to meet the following invariant implied in the discussion ; above: ; 1. The leading coefficient is +/-1 ; 2. The leading unknown: ; a. Is not a quoted constant --- Not much of an unknown/variable ; b. Is not itself a sum --- A poly represents a sum of terms ; c. Is not of the form (* c x), where c is a rational constant --- ; The c should have been ``pulled out''. ; d. Is not of the form (- c), (* c d), or (+ c d) where c and d are ; rational constants --- These terms should be evaluated and added ; onto the constant, not used as an unknown. ; Some of these are implied by others, but we check them each ; independently. ; The following three functions (weakly) capture this notion. ; Note: These invariants are referred to elsewhere by number, e.g., ; ``2.a'' If you change the above, search for occurrences of ; ``good-polyp''. If you refer to these invariants, be sure to ; include the string ``good-polyp'' somewhere nearby. (defun good-coefficient (c) (equal (abs c) 1)) (defun good-pot-varp (x) (and (not (quotep x)) (not (equal (fn-symb x) 'BINARY-+)) (not (and (equal (fn-symb x) 'BINARY-*) (quotep (fargn x 1)) (real/rationalp (unquote (fargn x 1))))) (not (and (equal (fn-symb x) 'UNARY--) (quotep (fargn x 1)) (real/rationalp (unquote (fargn x 1))))))) (defun good-polyp (p) (and (good-coefficient (first-coefficient p)) (good-pot-varp (first-var p)))) ; We need to define executable versions of the logical functions for <, <=, ; and abs. We know, however, that we will only apply them to acl2-numberps ; so we do not need to consider fixing the arguments. ;; RAG - I changed rational to real in the test to use < as the comparator. (defun logical-< (x y) (declare (xargs :guard (and (acl2-numberp x) (acl2-numberp y)))) (cond ((and (real/rationalp x) (real/rationalp y)) (< x y)) ((< (realpart x) (realpart y)) t) (t (and (= (realpart x) (realpart y)) (< (imagpart x) (imagpart y)))))) ;; RAG - Another change of rational to real in the test to use <= as the ;; comparator. (defun logical-<= (x y) (declare (xargs :guard (and (acl2-numberp x) (acl2-numberp y)))) (cond ((and (real/rationalp x) (real/rationalp y)) (<= x y)) ((< (realpart x) (realpart y)) t) (t (and (= (realpart x) (realpart y)) (<= (imagpart x) (imagpart y)))))) (defun evaluate-ground-poly (p) ; We assume the :alist of poly p is nil and thus p is a ground poly. ; We compute its truth value. (if (eq (access poly p :relation) '<) (logical-< 0 (access poly p :constant)) (logical-<= 0 (access poly p :constant)))) (defun impossible-polyp (p) (and (null (access poly p :alist)) (eq (evaluate-ground-poly p) nil))) (defun true-polyp (p) (and (null (access poly p :alist)) (evaluate-ground-poly p))) (defun silly-polyp (poly) ; For want of a better name, we say a poly is "silly" if it contains ; the *nil* assumption among its 'assumptions. (tag-tree-occur-assumption-nil (access poly poly :ttree))) (defun impossible-poly (ttree) (make poly :alist nil :parents (collect-parents ttree) :rational-poly-p t :derived-from-not-equalityp nil :ttree ttree :constant -1 :relation '<)) (defun base-poly0 (ttree parents relation rational-poly-p derived-from-not-equalityp) ; Warning: Keep this in sync with base-poly. (make poly :alist nil :parents parents :rational-poly-p rational-poly-p :derived-from-not-equalityp derived-from-not-equalityp :ttree ttree :constant 0 :relation relation)) (defun base-poly (ttree relation rational-poly-p derived-from-not-equalityp) ; Warning: Keep this in sync with base-poly0. (make poly :alist nil :parents (collect-parents ttree) :rational-poly-p rational-poly-p :derived-from-not-equalityp derived-from-not-equalityp :ttree ttree :constant 0 :relation relation)) (defun poly-alist-equal (alist1 alist2) ; This function is essentially EQUAL for two alists, but is faster ; (at least with poly alists). (cond ((null alist1) (null alist2)) ((null alist2) nil) (t (and (eql (cdar alist1) (cdar alist2)) (equal (caar alist1) (caar alist2)) (poly-alist-equal (cdr alist1) (cdr alist2)))))) (defun poly-equal (poly1 poly2) ; This function is essentially EQUAL for two polys, but is faster. (and (eql (access poly poly1 :constant) (access poly poly2 :constant)) (eql (access poly poly1 :relation) (access poly poly2 :relation)) (poly-alist-equal (access poly poly1 :alist) (access poly poly2 :alist)))) (defun poly-weakerp (poly1 poly2 parents-check) ; We return t if poly1 is ``weaker'' than poly2. ; Pseudo-examples: ; (<= 3 (* x y)) is weaker than both (< 3 (* x y)) and (<= 17/5 (* x y)); ; but is not weaker than (< 17 (+ w (* x y))), (< 17 (* 5 x y)), ; or (< 17 (* y x)). ; Normally parents-check is t; if poly2 has a parent not in the parents of ; poly1, then poly1 might be usable in a context where poly2 is not usable. ; Use parents-check = nil if such a consideration does not apply. (let ((c1 (access poly poly2 :constant)) (c2 (access poly poly1 :constant))) (and (or (logical-< c1 c2) ; The above inequality test is potentially confusing. In the comments, it is ; said that (<= 3 (* x y)) is weaker than (<= 17/5 (* x y)). Recall that the ; polys are stored in a format suggested by: (< (+ constant (* k1 t1) ... (* kn ; tn)) 0). Thus, the two constants would be stored as a -3 and a -17/5, and ; the above test is correct. -17/5 < -3. (and (eql c1 c2) (or (eq (access poly poly1 :relation) '<=) (eq (access poly poly2 :relation) '<)))) (poly-alist-equal (access poly poly1 :alist) (access poly poly2 :alist)) (if parents-check (subsetp (access poly poly2 :parents) (access poly poly1 :parents)) t)))) (defun poly-member (p lst) ; P is a poly and lst is a list of polys. This function used to return t if p ; was in lst (ignoring tag-trees). Now, it returns t if p is weaker than ; some poly in lst. ; This change was motivated by an observation that after several linear rules ; have fired and a couple of rounds of cancellation have occurred, one will ; occasionally see the linear pot fill up with weak polys. In most cases ; this idea makes no real performance difference; but Robert Krug has seen ; examples where it makes a tremendous difference. (and (consp lst) (or (poly-weakerp p (car lst) t) (poly-member p (cdr lst))))) (defun new-and-ugly-linear-varsp (lst flag term) ; Lst is a list of polys, term is the linear var which triggered the ; addition of the polys in lst, and flag is a boolean indicating ; whether we have maxed out the the loop-stopper-value associated ; with term. If flag is true, we check whether any of the polys are ; arith-term-order worse than term. ; Historical Note: Once upon a time, in Version_2.5 and earlier, this ; function actually insured that term wasn't in lst, i.e., that term was ; "new". But in Version_2.6, we changed the meaning of the function without ; changing its name. The word "new" in the name is now a mere artifact. ; This is intended to catch certain loops that can arise from linear ; lemmas. See the "Mini-essay on looping and linear arithmetic" below. (cond ((not flag) nil) ((null lst) nil) ((arith-term-order term (first-var (car lst))) t) (t (new-and-ugly-linear-varsp (cdr lst) flag term)))) (defun filter-polys (lst ans) ; We scan the list of polys lst. If we find an impossible one, we ; return it as our first result. If we find a true one we skip it. ; If we find a poly that is ``weaker'' (see poly-member and poly-weakerp) ; than one of those already filtered, we skip it. ; Otherwise we just accumulate them into ans. We return two values: ; the standard indication of contradiction and, otherwise in the ; second, the filtered list. This list in the reverse order from that ; produced by nqthm. (cond ((null lst) (mv nil ans)) ((impossible-polyp (car lst)) (mv (car lst) nil)) ((true-polyp (car lst)) (filter-polys (cdr lst) ans)) ((poly-member (car lst) ans) (filter-polys (cdr lst) ans)) (t (filter-polys (cdr lst) (cons (car lst) ans))))) ;================================================================= ; Here we define some functions for constructing polys. (defun add-linear-variable1 (n var alist) ; N is a rational constant and var is an arbitrary term -- a linear "variable". ; Alist is a polynomial alist and we are to add the new pair (var . n) to it. ; We keep the alist sorted on term-order on the terms with the largest var ; first. Furthermore, if there is already an entry for var we merely add n to ; it. If the resulting coefficient is 0 we delete the pair. ; We assume n is not 0 to begin with. (cond ((null alist) (cons (cons var n) nil)) ((arith-term-order var (caar alist)) (cond ((equal var (caar alist)) (let ((k (+ (cdar alist) n))) (cond ((= k 0) (cdr alist)) (t (cons (cons var k) (cdr alist)))))) (t (cons (car alist) (add-linear-variable1 n var (cdr alist)))))) (t (cons (cons var n) alist)))) (defun zero-factor-p (term) ; The following code recognizes terms of the form (* a1 ... 0 ... ak) ; so that we can treat them as though they were 0. Two sources of these ; 0-factor terms are: the original clause for which we are ; constructing a pot-lst, and a term introduced by forward chaining, ; which doesn't use rewrite. (The latter might commonly occur via an ; fc rule like (implies (and (< 0 x) (< y y+)) (< (* x y) (* x y+))) ; triggered by (* x y+). The free var y might be chosen to be 0, as ; would happen if (< 0 y+) were available. The result would be the ; term (* 0 y).) (cond ((variablep term) nil) ((fquotep term) (equal term *0*)) ((eq (ffn-symb term) 'BINARY-*) (or (zero-factor-p (fargn term 1)) (zero-factor-p (fargn term 2)))) (t nil))) (defun get-coefficient (term acc) ; We are about to add term onto a poly. We want to enforce the ; poly invariant 2.c. (Described shortly before the definition of ; good-polyp.) We therefore accumulate onto acc any leading constant ; coefficients. We return the (possibly) stripped term and its ; coefficient. (if (and (eq (fn-symb term) 'BINARY-*) (quotep (fargn term 1)) (real/rationalp (unquote (fargn term 1)))) (get-coefficient (fargn term 2) (* (unquote (fargn term 1)) acc)) (mv acc term))) (defun add-linear-variable (term side p) (mv-let (n term) (cond ((zero-factor-p term) (mv 0 nil)) ((and (eq (fn-symb term) 'BINARY-*) (quotep (fargn term 1)) (real/rationalp (unquote (fargn term 1)))) (mv-let (coeff new-term) (get-coefficient term 1) (if (eq side 'lhs) (mv (- coeff) new-term) (mv coeff new-term)))) ((eq side 'lhs) (mv -1 term)) (t (mv 1 term))) (if (= n 0) p (change poly p :alist (add-linear-variable1 n term (access poly p :alist)))))) (defun dumb-eval-yields-quotep (term) ; We are about to add term onto a poly. We want to enforce the poly invariant ; 2.d. (Described shortly before the definition of good-polyp.) Here, we ; check whether we should evaluate term. If so, we do the evaluation in ; dumb-eval immediately below. (cond ((variablep term) nil) ((fquotep term) t) ((equal (ffn-symb term) 'BINARY-*) (and (dumb-eval-yields-quotep (fargn term 1)) (dumb-eval-yields-quotep (fargn term 2)))) ((equal (ffn-symb term) 'BINARY-+) (and (dumb-eval-yields-quotep (fargn term 1)) (dumb-eval-yields-quotep (fargn term 2)))) ((equal (ffn-symb term) 'UNARY--) (dumb-eval-yields-quotep (fargn term 1))) (t nil))) (defun dumb-eval (term) ; See dumb-eval-yields-quotep, above. This function evaluates (fix ; ,term) and produces the corresponding evg, not a term. Thus, ; (binary-+ '1 '2) dumb-evals to 3 not '3, and (quote abc) dumb-evals ; to 0. (cond ((variablep term) (er hard 'dumb-eval "Bad term. We were expecting a constant, but encountered the variable ~x." term)) ((fquotep term) (if (acl2-numberp (unquote term)) (unquote term) 0)) ((equal (ffn-symb term) 'BINARY-*) (* (dumb-eval (fargn term 1)) (dumb-eval (fargn term 2)))) ((equal (ffn-symb term) 'BINARY-+) (+ (dumb-eval (fargn term 1)) (dumb-eval (fargn term 2)))) ((equal (ffn-symb term) 'UNARY--) (- (dumb-eval (fargn term 1)))) (t (er hard 'dumb-eval "Bad term. The term ~x was not as expected by dumb-eval." term)))) (defun add-linear-term (term side p) ; Side is either 'rhs or 'lhs. This function adds term to the ; indicated side of the poly p. It is the main way we construct a ; poly. See linearize. (cond ((variablep term) (add-linear-variable term side p)) ; We enforce poly invariant 2.d. (Described shortly before the ; definition of good-polyp.) ((dumb-eval-yields-quotep term) (let ((temp (dumb-eval term))) (if (eq side 'lhs) (change poly p :constant (+ (access poly p :constant) (- temp))) (change poly p :constant (+ (access poly p :constant) temp))))) (t (let ((fn1 (ffn-symb term))) (case fn1 (binary-+ (add-linear-term (fargn term 1) side (add-linear-term (fargn term 2) side p))) (unary-- (add-linear-term (fargn term 1) (if (eq side 'lhs) 'rhs 'lhs) p)) (binary-* ; We enforce the poly invariants 2.b. and 2.c. (Described shortly ; before the definition of good-polyp.) (cond ((and (quotep (fargn term 1)) (real/rationalp (unquote (fargn term 1))) (equal (fn-symb (fargn term 2)) 'BINARY-+)) (add-linear-term (mcons-term* 'BINARY-+ (mcons-term* 'BINARY-* (fargn term 1) (fargn (fargn term 2) 1)) (mcons-term* 'BINARY-* (fargn term 1) (fargn (fargn term 2) 2))) side p)) ((and (quotep (fargn term 1)) (real/rationalp (unquote (fargn term 1))) (equal (fn-symb (fargn term 2)) 'BINARY-*) (quotep (fargn (fargn term 2) 1)) (real/rationalp (unquote (fargn (fargn term 2) 1)))) (add-linear-term (mcons-term* 'BINARY-* (kwote (* (unquote (fargn term 1)) (unquote (fargn (fargn term 2) 1)))) (fargn (fargn term 2) 2)) side p)) (t (add-linear-variable term side p)))) (otherwise (add-linear-variable term side p))))))) (defun add-linear-terms-fn (rst) (cond ((null (cdr rst)) (car rst)) ((eq (car rst) :lhs) `(add-linear-term ,(cadr rst) 'lhs ,(add-linear-terms-fn (cddr rst)))) ((eq (car rst) :rhs) `(add-linear-term ,(cadr rst) 'rhs ,(add-linear-terms-fn (cddr rst)))) (t (er hard 'add-linear-terms-fn "Bad term ~x0" rst)))) (defmacro add-linear-terms (&rest rst) ; There are a couple of spots where we wish to add several pieces at ; a time to a poly. This macro and its associated function enable us ; to circumvent ACL2's requirement that all functions take a fixed ; number of arguments. ; Example usage: ; (add-linear-terms :lhs term1 ; :lhs ''1 ; :rhs term2 ; (base-poly ts-ttree ; '<= ; t ; nil)) (add-linear-terms-fn rst)) (defun normalize-poly1 (coeff alist) (cond ((null alist) nil) (t (acons (caar alist) (/ (cdar alist) coeff) (normalize-poly1 coeff (cdr alist)))))) (defun normalize-poly (p) ; P is a poly. We normalize it, so that the leading coefficient ; is +/-1. (if (access poly p :alist) (let ((c (abs (first-coefficient p)))) (cond ((eql c 1) p) (t (change poly p :alist (normalize-poly1 c (access poly p :alist)) :constant (/ (access poly p :constant) c))))) p)) (defun normalize-poly-lst (poly-lst) (cond ((null poly-lst) nil) (t (cons (normalize-poly (car poly-lst)) (normalize-poly-lst (cdr poly-lst)))))) ;================================================================= ; Linear Pots (defrec linear-pot ((loop-stopper-value . negatives) . (var . positives)) t) ; Var is a "linear variable", i.e., any term. Positives and negatives are ; lists of polys with the properties that var is the first (heaviest) linear ; variable in each poly in each list and var occurs positively in the one and ; negatively in the other. Loop-stopper-value is a natural number counter that ; is used to avoid looping, starting at 0 and incremented, using ; *max-linear-pot-loop-stopper-value* as a bound. (defun modify-linear-pot (pot pos neg) ; We do the equivalent of: ; (change linear-pot pot :positives pos :negatives neg) ; except that we avoid unnecessary consing. (if (equal neg (access linear-pot pot :negatives)) (if (equal pos (access linear-pot pot :positives)) pot (change linear-pot pot :positives pos)) (if (equal pos (access linear-pot pot :positives)) (change linear-pot pot :negatives neg) (change linear-pot pot :positives pos :negatives neg)))) ; Mini-essay on looping and linear arithmetic ; Robert Krug has written code to solve a problem with infinite loops related ; to linear arithmetic. The following example produces the loop in ACL2 ; Versions 2.4 and earlier. ; (defaxiom *-strongly-monotonic ; (implies (and (< a b)) ; (< (* a c) (* b c))) ; :rule-classes :linear) ; ; (defaxiom commutativity-2-of-* ; (equal (* x y z) ; (* y x z))) ; ; (defstub foo (x) t) ; ; (thm ; (implies (and (< a (* a c)) ; (< 0 evil)) ; (foo x))) ; The defconst below stops the loop. We may want to increase it in the future, ; but it appears to be sufficient for certifying ACL2 community books. It is ; used together with the field loop-stopper-value of the record linear-pot. ; When a linear-pot is first created, its loop-stopper-value is 0 (see ; add-poly). See add-linear-lemma for how loop-stopper-value is used to detect ; loops. ; Robert has provided the following trace, in which one can still see the first ; few iterations of the loop before it is caught by the loop-stopping mechanism ; now added. He suggests tracing new-and-ugly-linear-varp and worse-than to ; get some idea as to why this loop was not caught before due to the presence ; of the inequality (< 0 evil). ; (trace (add-linear-lemma ; :entry (list (list 'term (nth 0 si::arglist)) ; (list 'lemma (access linear-lemma ; (nth 1 si::arglist) ; :rune)) ; (list 'max-term (access linear-lemma ; (nth 1 si::arglist) ; :max-term)) ; (list 'conclusion (access linear-lemma ; (nth 1 si::arglist) ; :concl)) ; (list 'type-alist (show-type-alist ; (nth 2 si::arglist)))) ; :exit (if (equal (nth 9 si::arglist) ; (mv-ref 1)) ; '(no change) ; (list (list 'old-pot-list ; (show-pot-lst (nth 9 si::arglist))) ; (list 'new-potlist ; (show-pot-lst (mv-ref 1))))))) (defconst *max-linear-pot-loop-stopper-value* 3) (defun loop-stopper-value-of-var (var pot-lst) ; We return the value of loop-stopper-value associated with var in the ; pot-lst. If var does not appear we return 0. (cond ((null pot-lst) 0) ((equal var (access linear-pot (car pot-lst) :var)) (access linear-pot (car pot-lst) :loop-stopper-value)) (t (loop-stopper-value-of-var var (cdr pot-lst))))) (defun set-loop-stopper-values (new-vars new-pot-lst term value) ; New-vars is a list of new variables in new-pot-lst. Term is the trigger-term ; which caused the new pots to be added, and value is the loop-stopper-value ; associated with it. If a new-var is term-order greater than term, we set its ; loop-stopper-value to value + 1. Otherwise, we set it to value. ; Note that new-vars is in the same order as the vars of new-pot-lst. (cond ((null new-vars) new-pot-lst) ((equal (car new-vars) (access linear-pot (car new-pot-lst) :var)) (cond ((arith-term-order term (car new-vars)) (cons (change linear-pot (car new-pot-lst) :loop-stopper-value (1+ value)) (set-loop-stopper-values (cdr new-vars) (cdr new-pot-lst) term value))) (t (cons (change linear-pot (car new-pot-lst) :loop-stopper-value value) (set-loop-stopper-values (cdr new-vars) (cdr new-pot-lst) term value))))) (t (cons (car new-pot-lst) (set-loop-stopper-values new-vars (cdr new-pot-lst) term value))))) (defun var-in-pot-lst-p (var pot-lst) ; Test whether var is the label of any of the pots in pot-lst. (cond ((null pot-lst) nil) ((equal var (access linear-pot (car pot-lst) :var)) t) (t (var-in-pot-lst-p var (cdr pot-lst))))) (defun bounds-poly-with-var (poly-lst pt bounds-poly) ; We cdr down poly-lst, looking for a bounds poly. Poly-lst is either the ; :positives or :negatives from a pot. We would like to believe that the first ; bounds poly we find is, in fact, the strongest one present in poly-lst ; because we filter out any ones that are weaker than one already present with ; poly-member before adding it. However, that filtering was done using ; poly-weakerp with parameter parents-check = t, yet here we do not have any ; preference based on parents, other than that they do not disqualify the poly ; (based on argument pt) -- we just want the strongest bounds poly. (cond ((null poly-lst) bounds-poly) ((and (null (cdr (access poly (car poly-lst) :alist))) (rationalp (access poly (car poly-lst) :constant)) (not (ignore-polyp (access poly (car poly-lst) :parents) pt))) (bounds-poly-with-var (cdr poly-lst) pt (cond ((and bounds-poly (poly-weakerp (car poly-lst) bounds-poly nil)) bounds-poly) (t (car poly-lst))))) (t (bounds-poly-with-var (cdr poly-lst) pt bounds-poly)))) (defun bounds-polys-with-var (var pot-lst pt) ; A bounds poly is one in which the there is only one var in the ; alist. Such a poly can be regarded as "bounding" said var. ; Pseudo-examples: ; 3 < x is a bounds poly. ; 3 < x + y is not. ; #(1,1) < x is not. ; We insist that the constant c be rational. ; We return a list of the strongest bounds polys in the pot labeled ; with var. If there are no such polys, we return nil. (cond ((null pot-lst) nil) ((equal var (access linear-pot (car pot-lst) :var)) (let ((neg (bounds-poly-with-var (access linear-pot (car pot-lst) :negatives) pt nil)) (pos (bounds-poly-with-var (access linear-pot (car pot-lst) :positives) pt nil))) (cond (neg (if pos (list neg pos) (list neg))) (t (if pos (list pos) nil))))) (t (bounds-polys-with-var var (cdr pot-lst) pt)))) (defun polys-with-var1 (var pot-lst) (cond ((null pot-lst) nil) ((equal var (access linear-pot (car pot-lst) :var)) (append (access linear-pot (car pot-lst) :negatives) (access linear-pot (car pot-lst) :positives))) (t (polys-with-var1 var (cdr pot-lst))))) (defun polys-with-var (var pot-lst) ; We return a list of all the polys in the pot labeled with var. ; If there is no pot in pot-lst labeled with var, we return nil. ; We may occasionally be calling this function with an improper ; var. We catch this early, rather than stepping through the whole ; pot (see add-inverse-polys and add-inverse-polys1). (if (eq (fn-symb var) 'BINARY-+) nil (polys-with-var1 var pot-lst))) (defun polys-with-pots (polys pot-lst ans) ; We filter out those polys in polys which do not have a pot in ; pot-lst to hold them. Ans is initially nil. (cond ((null polys) ans) ((var-in-pot-lst-p (first-var (car polys)) pot-lst) (polys-with-pots (cdr polys) pot-lst (cons (car polys) ans))) (t (polys-with-pots (cdr polys) pot-lst ans)))) (defun new-vars-in-pot-lst (new-pot-lst old-pot-lst include-variableps) ; We return all the new vars of new-pot-lst. A "var" of a pot-lst is the :var ; component of a linear-pot in the pot-lst. A var is considered "new" if the ; var is not a var of the old-pot-lst and moreover, if include-variableps is ; false then it is not a variablep (i.e., is a function application). ; New-pot-lst is an extension of old-pot-lst, obtained by successive calls of ; add-poly. Every variable of old-pot-lst is in the new, but not vice versa. ; Since both lists are ordered by the vars we can recur down both the new and ; the old pot lists simultaneously. (cond ((null new-pot-lst) nil) ; This function used to be wrong! We incorrectly optimized the case for a pot ; with a variablep :var. Consider an old-pot-lst with one pot, (foo x), and a ; new-pot-lst with two pots, x and (foo x). Previously, since (variablep ; (access linear-pot (car new-pot-lst) :var)) would be true, we would recur on ; the cdr of both pots and then determine that (foo x) was new. I suspect that ; the variablep test was added to the function after the rest had been written ; (and, the include-variablesp argument was definitely added more recently than ; any of the rest of this comment). Here is the old code. This bug was ; discovered by Robert Krug. ; (or all-new-flg ; (null old-pot-lst) ; (not (equal (access linear-pot (car new-pot-lst) :var) ; (access linear-pot (car old-pot-lst) :var))))) ; (cons (access linear-pot (car new-pot-lst) :var) ; (new-vars-in-pot-lst (cdr new-pot-lst) ; old-pot-lst all-new-flg))) ((or (null old-pot-lst) (not (equal (access linear-pot (car new-pot-lst) :var) (access linear-pot (car old-pot-lst) :var)))) (if (or include-variableps (not (variablep (access linear-pot (car new-pot-lst) :var)))) (cons (access linear-pot (car new-pot-lst) :var) (new-vars-in-pot-lst (cdr new-pot-lst) old-pot-lst include-variableps)) (new-vars-in-pot-lst (cdr new-pot-lst) old-pot-lst include-variableps))) (t (new-vars-in-pot-lst (cdr new-pot-lst) (cdr old-pot-lst) include-variableps)))) (defun changed-pot-vars (new-pot-lst old-pot-lst to-be-ignored-lst) ; New-pot-lst is an extension of old-pot-lst. To-be-ignored-lst is a ; list of pots which we are to ignore. We return the list of pot ; labels (i.e., vars) of the pots which are changed with respect to ; old-pot-lst (a new pot is considered changed) which are not in ; to-be-ignored-lst. (cond ((null new-pot-lst) nil) ((equal (access linear-pot (car new-pot-lst) :var) (access linear-pot (car old-pot-lst) :var)) (if (or (equal (car new-pot-lst) (car old-pot-lst)) (member-equal (access linear-pot (car new-pot-lst) :var) to-be-ignored-lst)) (changed-pot-vars (cdr new-pot-lst) (cdr old-pot-lst) to-be-ignored-lst) (cons (access linear-pot (car new-pot-lst) :var) (changed-pot-vars (cdr new-pot-lst) (cdr old-pot-lst) to-be-ignored-lst)))) (t (cons (access linear-pot (car new-pot-lst) :var) (changed-pot-vars (cdr new-pot-lst) old-pot-lst to-be-ignored-lst))))) (defun infect-polys (lst ttree parents) ; We extend the :ttree of every poly in lst with ttree. We similarly ; expand :parents with parents. (cond ((null lst) nil) (t (cons (change poly (car lst) :ttree (cons-tag-trees ttree (access poly (car lst) :ttree)) :parents (marry-parents parents (access poly (car lst) :parents))) (infect-polys (cdr lst) ttree parents))))) (defun infect-first-n-polys (lst n ttree parents) ; We assume that parents is always (collect-parents ttree) when this is called. ; See infect-new-polys. (cond ((int= n 0) lst) (t (cons (change poly (car lst) :ttree (cons-tag-trees ttree (access poly (car lst) :ttree)) :parents (marry-parents parents (access poly (car lst) :parents))) (infect-first-n-polys (cdr lst) (1- n) ttree parents))))) (defun infect-new-polys (new-pot-lst old-pot-lst ttree) ; We must infect with ttree every poly in new-pot-lst that is not in ; old-pot-lst. By "infect" we mean cons ttree onto the ttree of the ; poly. However, we know that new-pot-lst is an extension of ; old-pot-lst via add-poly. For every linear-pot in old-pot-lst there ; is a pot in the new pot-lst with the same var. Furthermore, the ; linear pots are ordered so that by cdring down both new and old ; simultaneously when they have equal vars we keep them in step. ; Finally, every list of polys in new is an extension of its ; corresponding list in old. I.e., the positives of some pot in new ; with the same var as a pot in old is an extension of the positives ; of that pot in old. Hence, to visit every new poly in that list it ; suffices to visit just the first n, where n is the difference in the ; lengths of the new and old positives. ; See add-disjunct-polys-and-lemmas. (cond ((null new-pot-lst) nil) ((or (null old-pot-lst) (not (equal (access linear-pot (car new-pot-lst) :var) (access linear-pot (car old-pot-lst) :var)))) (let ((new-new-pot-lst (infect-new-polys (cdr new-pot-lst) old-pot-lst ttree))) (cons (modify-linear-pot (car new-pot-lst) (infect-polys (access linear-pot (car new-pot-lst) :positives) ttree (collect-parents ttree)) (infect-polys (access linear-pot (car new-pot-lst) :negatives) ttree (collect-parents ttree))) new-new-pot-lst))) (t (let ((new-new-pot-lst (infect-new-polys (cdr new-pot-lst) (cdr old-pot-lst) ttree))) (cons (modify-linear-pot (car new-pot-lst) (infect-first-n-polys (access linear-pot (car new-pot-lst) :positives) (- (length (access linear-pot (car new-pot-lst) :positives)) (length (access linear-pot (car old-pot-lst) :positives))) ttree (collect-parents ttree)) (infect-first-n-polys (access linear-pot (car new-pot-lst) :negatives) (- (length (access linear-pot (car new-pot-lst) :negatives)) (length (access linear-pot (car old-pot-lst) :negatives))) ttree (collect-parents ttree))) new-new-pot-lst))))) ;================================================================= ; Process-equational-polys ; Having set up the simplify-clause-pot-lst simplify clause we take ; advantage of it to find derived equalities that can help simplify ; the clause. In this section we develop process-equational-polys. (defun fcomplementary-multiplep1 (alist1 alist2) ; Both alists are polynomial alists, e.g., the car of each pair is a ; term and the cdr of each pair is a rational. We determine whether ; negating each cdr in alist2 yields alist1. (cond ((null alist1) (null alist2)) ((null alist2) nil) ((and (equal (caar alist1) (caar alist2)) (= (cdar alist1) (- (cdar alist2)))) (fcomplementary-multiplep1 (cdr alist1) (cdr alist2))) (t nil))) (defun fcomplementary-multiplep (poly1 poly2) ; We determine whether multiplying poly2 by some negative rational ; produces poly1. We assume that both polys have the same relation, ; e.g., <=, and the same first-var. ; Since we now normalize polys so that their first coefficient is ; +/-1. That makes this function simpler. In particular, we now need ; only check whether poly2 is the (arithmetic) negation of poly1. (and (= (access poly poly1 :constant) (- (access poly poly2 :constant))) (fcomplementary-multiplep1 (cdr (access poly poly1 :alist)) (cdr (access poly poly2 :alist))))) (defun already-used-by-find-equational-polyp-lst (poly1 lst) (cond ((endp lst) nil) (t (or (poly-equal (car (car lst)) poly1) (already-used-by-find-equational-polyp-lst poly1 (cdr lst)))))) (defun already-used-by-find-equational-polyp (poly1 hist) ; Poly1 is a positive poly. Let poly2 be its negative version. We are ; considering using them to create an equation as part of ; find-equational-poly. We wish to know whether they have ever been ; so used before. The answer is found by looking into the history of ; the clause being worked on, hist, for every 'simplify-clause entry. ; Each such entry is of the form (simplify-clause clause ttree). We ; search ttree for (poly1 . poly2) tagged with 'find-equational-poly. ; Historical Note: Once upon a time, polys were not normalized in the ; sense that the leading coefficient is 1. Thus, 2x <= 6 and 3 <= x ; were complementary. To discover whether a poly had been used ; before, we had to know both the positive and the negative form ; involved. But now polys are normalized and the only complement to 3 ; <= x is x <= 3. Thus, we could change the tag value to be a single ; positive poly instead of both. You will note that we never actually ; need poly2. (cond ((null hist) nil) ((and (eq (access history-entry (car hist) :processor) 'simplify-clause) (already-used-by-find-equational-polyp-lst poly1 (tagged-objects 'find-equational-poly (access history-entry (car hist) :ttree)))) t) (t (already-used-by-find-equational-polyp poly1 (cdr hist))))) (defun cons-term-binary-+-constant (x term) ; x is an acl2-numberp, possibly complex, term is a rational type term. We ; make a term equivalent to (binary-+ 'x term). (cond ((= x 0) term) ((quotep term) (kwote (+ x (cadr term)))) (t (fcons-term* 'binary-+ (kwote x) term)))) (defun cons-term-unary-- (term) (cond ((variablep term) (fcons-term* 'unary-- term)) ((fquotep term) (kwote (- (cadr term)))) ((eq (ffn-symb term) 'unary--) (fargn term 1)) (t (fcons-term* 'unary-- term)))) (defun cons-term-binary-*-constant (x term) ; x is a number (possibly complex), term is a rational type term. We make a ; term equivalent to (binary-* 'x term). (cond ((= x 0) (kwote 0)) ((= x 1) term) ((= x -1) (cons-term-unary-- term)) ((quotep term) (kwote (* x (cadr term)))) (t (fcons-term* 'binary-* (kwote x) term)))) (defun find-equational-poly-rhs1 (alist) ; See find-equational-poly-rhs. (cond ((null alist) *0*) ((null (cdr alist)) (cons-term-binary-*-constant (- (cdar alist)) (caar alist))) (t (cons-term 'binary-+ (list (cons-term-binary-*-constant (- (cdar alist)) (caar alist)) (find-equational-poly-rhs1 (cdr alist))))))) (defun find-equational-poly-rhs (poly1) ; Suppose poly1 and poly2 are complementary multiple <= polys, as ; described in find-equational-poly. We wish to form the rhs term ; returned by that function. We know the two polys have the form ; poly1: k0 + k1*t1 + k2*t2 ... <= 0, k1 positive ; poly2 j0 + j1*t1 + j2*t2 ... <= 0, j1 negative ; and if q = k1/j1 then q is negative and ji*q = -ki for each i. ; Thus, k0 + k1*t1 + k2*t2 ... = 0. ; The equation created by find-equational-poly will be lhs = rhs, where lhs ; is t1. We are to create rhs. That is: ; rhs = -k0/k1 - k2/k1*t2 ... ; which, if we let c be -1/k1 ; rhs = (+ c*k0 (+ c*k2*t2 ...)) ; which is what we return. ; However now that we normalize polys, k1 = 1 and j1 = -1, so that q = ; -1 and c = -1. Hence we now negate, rather than multiplying by c. (cons-term-binary-+-constant (- (access poly poly1 :constant)) (find-equational-poly-rhs1 (cdr (access poly poly1 :alist))))) (defun find-equational-poly3 (poly1 poly2 hist) ; See find-equational-poly. This is the function that actually builds ; the affirmative answer returned by that function. Between this function ; and that one are two others whose only job is to iterate across all the ; potentially acceptable positives and negatives and give to this function ; a potentially appropriate poly1 and poly2. ; We know that poly1 is a positive <= poly that does not descend from ; a (not (equal & &)). We know that poly2 is a negative <= poly that ; does not descend from a (not (equal & &)). We know they have the same ; first-var. ; We first determine whether they are complementary multiples of eachother ; and have not been used by find-equational-poly already. If so, we ; return a ttree and two terms, as described by find-equational-poly. (cond ((and (fcomplementary-multiplep poly1 poly2) (not (already-used-by-find-equational-polyp poly1 hist))) (mv (add-to-tag-tree 'find-equational-poly (cons poly1 poly2) (cons-tag-trees (access poly poly1 :ttree) (access poly poly2 :ttree))) (first-var poly1) (find-equational-poly-rhs poly1))) (t (mv nil nil nil)))) (defun find-equational-poly2 (poly1 negatives hist) ; See find-equational-poly. Poly1 is a positive <= poly with the same ; first var as all the members of negatives. We scan negatives looking ; for a poly2 that is acceptable. (cond ((null negatives) (mv nil nil nil)) ((or (not (eq (access poly (car negatives) :relation) '<=)) (access poly (car negatives) :derived-from-not-equalityp)) (find-equational-poly2 poly1 (cdr negatives) hist)) (t (mv-let (msg lhs rhs) (find-equational-poly3 poly1 (car negatives) hist) (cond (msg (mv msg lhs rhs)) (t (find-equational-poly2 poly1 (cdr negatives) hist))))))) (defun find-equational-poly1 (positives negatives hist) ; See find-equational-poly. Positives and negatives are the ; appropriate fields of the same linear pot. All the first-vars are ; equal. We scan the positives and for each <= poly there we look for ; an acceptable member of the negatives. (cond ((null positives) (mv nil nil nil)) ((or (not (eq (access poly (car positives) :relation) '<=)) (access poly (car positives) :derived-from-not-equalityp)) (find-equational-poly1 (cdr positives) negatives hist)) (t (mv-let (msg lhs rhs) (find-equational-poly2 (car positives) negatives hist) (cond (msg (mv msg lhs rhs)) (t (find-equational-poly1 (cdr positives) negatives hist))))))) (defun find-equational-poly (pot hist) ; Look for an equation to be derived from this pot. We look for a ; weak inequality in positives whose negation is a member of ; negatives, which was not the result of linearizing a (not (equal lhs ; rhs)), and which has never been found (and recorded in hist) before. ; The message we look for is our business (we generate and recognize ; them) but they must be in the tag-tree stored in the 'simplify-clause ; entries of hist. ; We return three values. If we find no acceptable poly, we return ; three nils. Otherwise we return a non-nil ttree and two terms, lhs ; and rhs. In this case, it is a truth (assuming pot and the ; 'assumptions in the ttree) that lhs = rhs. As a matter of fact, lhs ; will be the var of the linear-pot pot and rhs will be a +-tree of ; lighter vars. Of course, the equation can be rearranged and used ; arbitrarily by the caller. ; If the equation is used in the current simplification, the ttree we ; return must find its way into the hist entry for that ; simplify-clause. ; Historical note: The affect of the newly (v2_8) introduced field, ; :derived-from-not-equalityp, is different from that of the ; earlier function descends-from-not-equalityp. We are now more ; liberal about the polys we can generate here. See the discussion ; accompanying the definition of a poly. (Search for ``(defrec poly''.)) (find-equational-poly1 (access linear-pot pot :positives) (access linear-pot pot :negatives) hist)) ;================================================================= ; Add-polys (defun get-coeff-for-cancel1 (alist1 alist2) ; Alist1 and alist2 are the alists from two polys which we are about ; to cancel. We calculate the absolute value of what would be the ; leading coefficient if we added the two alists. This is in support ; of cancel, which see. (cond ((null alist1) (if (null alist2) 1 (abs (cdar alist2)))) ((null alist2) (abs (cdar alist1))) ((not (arith-term-order (caar alist1) (caar alist2))) (abs (cdar alist1))) ((equal (caar alist1) (caar alist2)) (let ((temp (+ (cdar alist1) (cdar alist2)))) (if (eql temp 0) (get-coeff-for-cancel1 (cdr alist1) (cdr alist2)) (abs temp)))) (t (abs (cdar alist2))))) (defun cancel2 (alist coeff) (cond ((null alist) nil) (t (cons (cons (caar alist) (/ (cdar alist) coeff)) (cancel2 (cdr alist) coeff))))) (defun cancel1 (alist1 alist2 coeff) ; Alist1 and alist2 are the alists from two polys which we are about ; to cancel. We create a new alist by adding alist1 and alist2, using ; coeff to normalize the result. (cond ((null alist1) (cancel2 alist2 coeff)) ((null alist2) (cancel2 alist1 coeff)) ((not (arith-term-order (caar alist1) (caar alist2))) (cons (cons (caar alist1) (/ (cdar alist1) coeff)) (cancel1 (cdr alist1) alist2 coeff))) ((equal (caar alist1) (caar alist2)) (let ((temp (/ (+ (cdar alist1) (cdar alist2)) coeff))) (cond ((= temp 0) (cancel1 (cdr alist1) (cdr alist2) coeff)) (t (cons (cons (caar alist1) temp) (cancel1 (cdr alist1) (cdr alist2) coeff)))))) (t (cons (cons (caar alist2) (/ (cdar alist2) coeff)) (cancel1 alist1 (cdr alist2) coeff))))) (defun cancel (p1 p2) ; P1 and p2 are polynomials with the same first var and opposite ; signs. We construct the poly obtained by cross-multiplying and ; adding p1 and p2 so as to cancel out the first var. ; Polys are now normalized such that the leading coefficients are ; +/-1. Hence we no longer need to cross-multiply before adding ; p1 and p2. (The variables co1 and co2 in the original version ; are now guaranteed to be 1.) We do add a twist to the naive ; implementation though. Rather than adding the two alists, and ; then normalizing the result, we calculate what would have been ; the leading coeficient and normalize as we go (dividing by its ; absolute value). ; We return two values. The first indicates whether we have ; discovered a contradiction. If the first result is non-nil then it ; is the impossible poly formed by cancelling p1 and p2. The ttree of ; that poly will be interesting to our callers because it contains ; such things as the assumptions made and the lemmas used to get the ; contradiction. When we return a contradiction, the second result is ; always nil. Otherwise, the second result is either nil (meaning that ; the cancellation yeilded a trivially true poly) or is the newly ; formed poly. ; Historical note: The affect of the newly (v2_8) introduced field, ; :derived-from-not-equalityp, is different from that of the ; earlier function descends-from-not-equalityp. See the discussion ; accompanying the definition of a poly. (Search for ``(defrec poly''.)) ; Note: It is sometimes convenient to trace this function with ; (trace (cancel ; :entry (list (show-poly (car si::arglist)) ; (show-poly (cadr si::arglist))) ; :exit (let ((flg (car values)) ; (val (car (mv-refs 1)))) ; (cond (flg (append values (mv-refs 1))) ; (val (list nil (show-poly val))) ; (t (list nil nil)))))) ; Since we now normalize polys, the cars of the two alists will ; cancel each other out and all we have to concern ourselves with ; are their cdrs. (let* ((alist1 (cdr (access poly p1 :alist))) (alist2 (cdr (access poly p2 :alist))) (coeff (get-coeff-for-cancel1 alist1 alist2)) (p (make poly :constant (/ (+ (access poly p1 :constant) (access poly p2 :constant)) coeff) :alist (cancel1 alist1 alist2 coeff) :relation (if (or (eq (access poly p1 :relation) '<) (eq (access poly p2 :relation) '<)) '< '<=) :ttree (cons-tag-trees (access poly p1 :ttree) (access poly p2 :ttree)) :rational-poly-p (and (access poly p1 :rational-poly-p) (access poly p2 :rational-poly-p)) :parents (marry-parents (access poly p1 :parents) (access poly p2 :parents)) :derived-from-not-equalityp nil))) (cond ((impossible-polyp p) (mv p nil)) ((true-polyp p) (mv nil nil)) (t (mv nil p))))) (defun cancel-poly-against-all-polys (p polys pt ans) ; P is a poly, polys is a list of polys, the first var of p is the same ; as the first of every poly in polys and has opposite sign. We are to ; cancel p against each member of polys, getting in each case a ; contradiction, a true poly (which we discard) or a new shorter poly. ; Pt is a parent tree indicating literals we are to avoid. ; We return two answers. The first is either nil or the first ; contradiction we find. When the first is a contradiction, the ; second is nil. Otherwise, the second is the list of all newly ; produced polys. ; Ans is supposed to be nil initially and is the site at which we ; accumulate the new polys. This is a No-Change Loser. (cond ((null polys) (mv nil ans)) ((ignore-polyp (access poly (car polys) :parents) pt) (cancel-poly-against-all-polys p (cdr polys) pt ans)) (t (mv-let (contradictionp new-p) (cancel p (car polys)) (cond (contradictionp (mv contradictionp nil)) (t (cancel-poly-against-all-polys p (cdr polys) pt ; We discard polys which are ``weaker'' (see poly-member and ; poly-weakerp) than one already accumulated into ans. (if (and new-p (not (poly-member new-p ans))) (cons new-p ans) ans)))))))) ; Historical note: ; The following functions --- add-polys0 and its callees --- have been ; substantially rewritten. Previous to Version_2.8 the following ; two comments were in add-poly and add-poly1 (which no longer exists) ; respectively: ; Add-poly historical comment ; ; This is the fundamental function for changing a pot-lst. It adds a ; ; single poly p to pot-lst. All the other functions which construct ; ; pot lists do it, ultimately, via calls to add-poly. ; ; ; In nqthm this function was called add-equation but since its argument ; ; is a poly we renamed it. ; ; ; This function adds a poly p to the pot-lst. Since the pot-lst is ; ; ordered by term-order on the vars, we recurse down the pot-lst just ; ; far enough to find where p fits. There are three cases: p goes ; ; before the current pot, p goes in the current pot, or p goes after ; ; the current pot. The first is simplest: make a pot for p and stick ; ; it at the front of the pot-lst. The second is not too bad: cancel p ; ; against every poly of opposite sign in this pot to generate a bunch ; ; of new polys that belong earlier in the pot-lst and then add p to ; ; the current pot. The third is the worst: Recursively add p to the ; ; rest of the pot-lst, get back a bunch of polys that need processing, ; ; process the ones that belong where you're standing and pass up the ; ; ones that go earlier. ; Add-poly1 historical comment ; ; This is a subroutine of add-poly. See the comment there. Suppose ; ; we've just gotten back from a recursive call of add-poly and it ; ; returned to us a bunch of polys that belong earlier in the pot-lst ; ; (from it). Some of those polys may belong here where we are ; ; standing. Others should be passed up. ; ; ; To-do is the list of polys produced by the recursive add-poly. Var, ; ; positives, and negatives are the appropriate components of the pot ; ; that add-poly is standing on. We process those polys in to-do that ; ; go here, producing new positives and negatives, and set aside those ; ; that don't go here. The processing of the ones that do go here may ; ; create some additional polys that don't go here. To-do-next is the ; ; accumulation site for the to-do's we don't handle and the ones our ; ; processing creates. ; Add-poly is still the fundamental routine for adding a poly to the ; pot-lst. However, we now merely gather up newly generated polys and ; pass them back out to add-polys --- changing the routines which ; add polys to the pot list from a depth-first search to a ; breadth-first search. (defun add-poly (p pot-lst to-do-next pt nonlinearp) ; This is the fundamental function for changing a pot-lst. It adds a ; single poly p to pot-lst. All the other functions which construct ; pot lists do it, ultimately, via calls to add-poly. ; This function adds a poly p to the pot-lst and returns 3 values. ; The first is the standard contradictionp. The second value, of ; interest only when we don't find a contradiction, is the new pot-lst ; obtained by adding p to pot-lst. The third value is a list of new ; polys generated by the adding of p to pot-lst, which must be ; processed. We cons our own generated polys onto the incoming ; to-do-next to form this result. ; An invariant exploited by infect-new-polys is that all of the new ; polys in any linear pot occur at the front of the list and no polys ; are ever deleted. That is, if this or any other function wants to ; add a poly to the positives, say, it must cons it onto the front. ; In general, if we have an old linear pot and a new one produced from ; it and we want to process all the polys in the positives, say, of the ; new pot that are not in the old one, it suffices to process the first ; n elements of the new positives, where n is the difference in their ; lengths. ; Note: If adding a poly creates a new pot, its loop-stopper value is set to ; 0. This is changed to the correct value (if necessary) in ; add-linear-lemma. ; Trace Note: ; (trace (add-poly ; :entry (let ((args si::arglist)) ; (list (show-poly (nth 0 args)) ;p ; (show-pot-lst (nth 1 args)) ;pot-lst ; (show-poly-lst (nth 2 args)) ;to-do-next ; (nth 3 args) ; (nth 4 args) ; '|ens| (nth 6 args) '|wrld|)) ; :exit (cond ((null (car values)) ; (list nil ; (show-pot-lst (mv-ref 1)) ; (show-poly-lst (mv-ref 2)))) (cond ((time-limit5-reached-p "Out of time in linear arithmetic (add-poly).") ; nil, or throws (mv nil nil nil)) ((or (null pot-lst) (not (arith-term-order (access linear-pot (car pot-lst) :var) (first-var p)))) (mv nil (cons (if (< 0 (first-coefficient p)) ; p is normalized (below too) (make linear-pot :var (first-var p) :loop-stopper-value 0 :positives (list p) :negatives nil) (make linear-pot :var (first-var p) :loop-stopper-value 0 :positives nil :negatives (list p))) pot-lst) to-do-next)) ((equal (access linear-pot (car pot-lst) :var) (first-var p)) (cond ((poly-member p (if (< 0 (first-coefficient p)) (access linear-pot (car pot-lst) :positives) (access linear-pot (car pot-lst) :negatives))) (mv nil pot-lst to-do-next)) (t (mv-let (contradictionp to-do-next) (cancel-poly-against-all-polys p (if (< 0 (first-coefficient p)) (access linear-pot (car pot-lst) :negatives) (access linear-pot (car pot-lst) :positives)) pt to-do-next) (cond (contradictionp (mv contradictionp nil nil)) ; Non-linear optimization ; Magic number. If non-linear arithmetic is enabled, and there are ; more than 20 polys in the appropriate side of the pot, we give up ; and do not add the new poly. This has proven to be a useful heuristic. ; Increasing this number will slow ACL2 down sometimes, but it may ; allow more proofs to go through. So far I have not seen one which ; needs more than 20, but less than 100 --- which is too much. ; Note that the pot-lst isn't changed (i.e., poly wasn't added to its ; pot) but we will propagate the children poly and (possibly) add them ; to their pots. These children are "orphans" because a parent is ; missing from the pot-lst. ((and nonlinearp (>=-len (if (< 0 (first-coefficient p)) (access linear-pot (car pot-lst) :positives) (access linear-pot (car pot-lst) :negatives)) 21)) (mv nil pot-lst to-do-next)) (t (mv nil (cons (if (< 0 (first-coefficient p)) (change linear-pot (car pot-lst) :positives (cons p (access linear-pot (car pot-lst) :positives))) (change linear-pot (car pot-lst) :negatives (cons p (access linear-pot (car pot-lst) :negatives)))) (cdr pot-lst)) to-do-next))))))) (t (mv-let (contradictionp cdr-pot-lst to-do-next) (add-poly p (cdr pot-lst) to-do-next pt nonlinearp) (cond (contradictionp (mv contradictionp nil nil)) (t (mv nil (cons (car pot-lst) cdr-pot-lst) to-do-next))))))) (defun prune-poly-lst (poly-lst ans) (cond ((null poly-lst) ans) ((endp (cddr (access poly (car poly-lst) :alist))) (prune-poly-lst (cdr poly-lst) (cons (car poly-lst) ans))) (t (prune-poly-lst (cdr poly-lst) ans)))) (defun add-polys1 (lst pot-lst new-lst pt nonlinearp max-rounds rounds-completed) ; This function adds every element of the poly list lst to pot-lst and ; accumulates the new polys in new-lst. When lst is exhausted it ; starts over on the ones in new-lst and iterates that until no new polys ; are produced. It returns 2 values: the standard contradictionp in the ; the first and the final pot-lst in the second. (cond ((eql max-rounds rounds-completed) (mv nil pot-lst)) ((null lst) (cond ((null new-lst) (mv nil pot-lst)) ; Non-linear optimization ; Magic number. If non-linear arithmetic is enabled, and there are ; more than 100 polys in lst waiting to be added to the pot-lst, we ; try pruning the list of new polys. This has proven to be a useful ; heuristic. Increasing this number will slow ACL2 down sometimes, ; but it may allow more proofs to go through. So far I have not seen ; one which needs more than 100, but less than 500 --- which is too ; much. After Version_5.0, we eliminated the nonlinearp condition ; and prune when there are more than 100 polys in the new list. ((and ; nonlinearp (>=-len new-lst 101)) (add-polys1 (prune-poly-lst new-lst nil) pot-lst nil pt nonlinearp max-rounds (+ 1 rounds-completed))) (t (add-polys1 new-lst pot-lst nil pt nonlinearp max-rounds (+ 1 rounds-completed))))) (t (mv-let (contradictionp new-pot-lst new-lst) (add-poly (car lst) pot-lst new-lst pt nonlinearp) (cond (contradictionp (mv contradictionp nil)) (t (add-polys1 (cdr lst) new-pot-lst new-lst pt nonlinearp max-rounds rounds-completed))))))) (defun add-polys0 (lst pot-lst pt nonlinearp max-rounds) ; Lst is a list of polys. We filter out the true ones (and detect any ; impossible ones) and then normalize and add the rest to pot-lst. ; Any new polys thereby produced are also added until there's nothing ; left to do. We return the standard contradictionp and a new pot-lst. (mv-let (contradictionp lst) (filter-polys lst nil) (cond (contradictionp (mv contradictionp nil)) (t (add-polys1 lst pot-lst nil pt nonlinearp max-rounds 0))))) ;================================================================= ; "Show-" functions ; The next group of "show-" functions are not part of the system but are ; convenient for system debugging. (show-poly poly) will create a list ; structure that prints so as to show a polynomial in the conventional ; notation. The term enclosed in an extra set of parentheses is the leading ; term of the poly. An example show-poly is '(3 J + (I) + 77 <= 4 M + 2 N). ; (defun show-poly2 (pair lst) ; (let ((n (abs (cdr pair))) ; (x (car pair))) ; (cond ((= n 1) (cond ((null lst) (list x)) ; (t (list* x '+ lst)))) ; (t (cond ((null lst) (list n x)) ; (t (list* n x '+ lst))))))) ; ; (defun show-poly1 (alist lhs rhs) ; ; ; Note: This function ought to return (mv lhs rhs) but when it is used in ; ; tracing multiply valued functions that functionality hurts us: the ; ; computation performed during the tracing destroys the multiple value being ; ; manipulated by the function being traced. So that we can use this function ; ; conveniently during tracing, we make it a single valued function. ; ; (cond ((null alist) (cons lhs rhs)) ; ((logical-< 0 (cdar alist)) ; (show-poly1 (cdr alist) lhs (show-poly2 (car alist) rhs))) ; (t (show-poly1 (cdr alist) (show-poly2 (car alist) lhs) rhs)))) ; ; (defun show-poly (poly) ; (let* ((pair (show-poly1 ; (cond ((null (access poly poly :alist)) nil) ; (t (cons (cons (list (caar (access poly poly :alist))) ; (cdar (access poly poly :alist))) ; (cdr (access poly poly :alist))))) ; (cond ((= (access poly poly :constant) 0) ; nil) ; ((logical-< 0 (access poly poly :constant)) nil) ; (t (cons (- (access poly poly :constant)) nil))) ; (cond ((= (access poly poly :constant) 0) ; nil) ; ((logical-< 0 (access poly poly :constant)) ; (cons (access poly poly :constant) nil)) ; (t nil)))) ; (lhs (car pair)) ; (rhs (cdr pair))) ; ; ; The let* above would be (mv-let (lhs rhs) (show-poly1 ...) ...) had ; ; show-poly1 been specified to return two values instead of a pair. ; ; See note above. ; ; (append (or lhs '(0)) ; (cons (access poly poly :relation) (or rhs '(0)))))) ; ; (defun show-poly-lst (poly-lst) ; (cond ((null poly-lst) nil) ; (t (cons (show-poly (car poly-lst)) ; (show-poly-lst (cdr poly-lst)))))) ; ; ; (defun show-pot-lst (pot-lst) ; (cond ; ((null pot-lst) nil) ; (t (cons ; (list* :var (access linear-pot (car pot-lst) :var) ; (append (show-poly-lst ; (access linear-pot (car pot-lst) :negatives)) ; (show-poly-lst ; (access linear-pot (car pot-lst) :positives)))) ; (show-pot-lst (cdr pot-lst)))))) ; ; (defun show-type-alist (type-alist) ; (cond ((endp type-alist) nil) ; (t (cons (list (car (car type-alist)) ; (decode-type-set (cadr (car type-alist)))) ; (show-type-alist (cdr type-alist)))))) ; ; ; (defun number-of-polys (pot-lst) ; (cond ((null pot-lst) 0) ; (t (+ (len (access linear-pot (car pot-lst) :negatives)) ; (len (access linear-pot (car pot-lst) :positives)) ; (number-of-polys (cdr pot-lst)))))) acl2-sources/linear-b.lisp0000666002132200015000000011275712222115527015153 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ;================================================================= ; We continue our development of linear arithmetic. In particular, ; we define the functions add-polys and linearize. ;================================================================= ; Add-polys (defun polys-from-type-set (term force-flag dwp type-alist ens wrld ttree) ; If possible, we create a list of polys based upon the type-set ; of term. ; Warning: This function should not be used with any terms that are ; not legitimate pot-vars. See the definition of good-pot-varp. ; Assuming that term is a legitimate pot-label --- meets all the ; invariants --- we do not have to normalize any of the polys below. ; We check that term is OK and throw an error if not. It would, ; however, not be very expensive to wrap the below in a call to ; normalize-poly-lst, and thereby avoid the potential error situation. (if (good-pot-varp term) (mv-let (ts ts-ttree) (type-set term force-flag dwp type-alist ens wrld ttree nil nil) (cond ((ts-subsetp ts *ts-zero*) (list ;; 0 <= term (add-linear-terms :rhs term (base-poly ts-ttree '<= t nil)) ;; term <= 0 (add-linear-terms :lhs term (base-poly ts-ttree '<= t nil)))) ((ts-subsetp ts *ts-positive-integer*) (list ;; 1 <= term (add-linear-terms :lhs *1* :rhs term (base-poly ts-ttree '<= t nil)))) ((ts-subsetp ts *ts-negative-integer*) (list ;; term <= -1 (add-linear-terms :lhs term :rhs ''-1 (base-poly ts-ttree '<= t nil)))) ((ts-subsetp ts #-:non-standard-analysis *ts-positive-rational* #+:non-standard-analysis *ts-positive-real*) (list ;; 0 < term (add-linear-terms :rhs term (base-poly ts-ttree '< t nil)))) ((ts-subsetp ts #-:non-standard-analysis *ts-negative-rational* #+:non-standard-analysis *ts-negative-real*) (list ;; term < 0 (add-linear-terms :lhs term (base-poly ts-ttree '< t nil)))) ((ts-subsetp ts #-:non-standard-analysis *ts-non-negative-rational* #+:non-standard-analysis *ts-non-negative-real*) (list ;; 0 <= term (add-linear-terms :rhs term (base-poly ts-ttree '<= t nil)))) ((ts-subsetp ts #-:non-standard-analysis *ts-non-positive-rational* #+:non-standard-analysis *ts-non-positive-real*) (list ;; term <= 0 (add-linear-terms :lhs term (base-poly ts-ttree '<= t nil)))) (t nil))) (er hard 'inverse-polys "A presumptive pot-label, ~x0, has turned out to be illegitimate. ~ If possible, please send a script reproducing this error ~ to the authors of ACL2." term))) (defun add-type-set-polys (var-lst new-pot-lst old-pot-lst already-noted-vars pt nonlinearp type-alist ens force-flg wrld) ; We have just finished adding a bunch of polys to a pot-lst. In ACL2 ; versions prior to 2.7, these polys were canceled against any ; type-set information on the fly. We now add the type-set ; information explicitly. This function checks which pots have been ; modified (any new polys in these pots would have been canceled ; against type-set knowledge in the past), derives polys (using ; type-set information) about the vars of the modified pots, and adds ; them to the pots. (cond ((null var-lst) (let ((new-var-lst (changed-pot-vars new-pot-lst old-pot-lst already-noted-vars))) (if new-var-lst (add-type-set-polys new-var-lst new-pot-lst new-pot-lst new-var-lst pt nonlinearp type-alist ens force-flg wrld) (mv nil new-pot-lst)))) (t (mv-let (contradictionp new-new-pot-lst) (add-polys0 (polys-from-type-set (car var-lst) force-flg nil ;;; dwp type-alist ens wrld nil) ;;; ttree new-pot-lst pt nonlinearp t) (cond (contradictionp (mv contradictionp nil)) (t (add-type-set-polys (cdr var-lst) new-new-pot-lst old-pot-lst already-noted-vars pt nonlinearp type-alist ens force-flg wrld))))))) (defun add-polynomial-inequalities (lst pot-lst pt nonlinearp type-alist ens force-flg wrld) ; Wrapper for the old add-polys (now add-polys0) so that we can ; eliminate the use of cancel-poly-against-type-set from within add-poly. ; We add the polys in lst to the pot-lst, and then call add-type-set-polys ; which performs a similar function to the old calls to ; cancel-poly-against-type-set. ; Historical Note: The nearest approximation to this function in Nqthm ; was named add-equations. (mv-let (contradictionp new-pot-lst) (add-polys0 lst pot-lst pt nonlinearp t) (cond (contradictionp (mv contradictionp nil)) (t (let ((changed-pot-vars (changed-pot-vars new-pot-lst pot-lst nil))) (add-type-set-polys changed-pot-vars new-pot-lst new-pot-lst changed-pot-vars pt nonlinearp type-alist ens force-flg wrld)))))) #-acl2-loop-only (defparameter *add-polys-counter* ; This is used by dmr-string to show the cumulative number of calls of ; add-polys, as requested by Robert Krug. 0) (defun add-polys (lst pot-lst pt nonlinearp type-alist ens force-flg wrld) #-acl2-loop-only (when (f-get-global 'dmrp *the-live-state*) (return-from add-polys (progn (incf *add-polys-counter*) (pstk (add-polynomial-inequalities lst pot-lst pt nonlinearp type-alist ens force-flg wrld))))) (add-polynomial-inequalities lst pot-lst pt nonlinearp type-alist ens force-flg wrld)) ;================================================================= ; Linearize (mutual-recursion (defun eval-ground-subexpressions (term ens wrld state ttree) (cond ((or (variablep term) (fquotep term) (eq (ffn-symb term) 'hide)) (mv nil term ttree)) ((flambda-applicationp term) (mv-let (flg args ttree) (eval-ground-subexpressions-lst (fargs term) ens wrld state ttree) (cond ((all-quoteps args) (mv-let (flg val ttree) (eval-ground-subexpressions (sublis-var (pairlis$ (lambda-formals (ffn-symb term)) args) (lambda-body (ffn-symb term))) ens wrld state ttree) (declare (ignore flg)) (mv t val ttree))) (flg ; We could look for just those args that are quoteps, and substitute those, ; presumably then calling make-lambda-application to create a lambda out of the ; result. But we'll put that off for another time, or even indefinitely, ; noting that through Version_2.9.4 we did not evaluate any ground lambda ; applications. (mv t (cons-term (ffn-symb term) args) ttree)) (t (mv nil term ttree))))) ((eq (ffn-symb term) 'if) (mv-let (flg1 arg1 ttree) (eval-ground-subexpressions (fargn term 1) ens wrld state ttree) (cond ((quotep arg1) (if (cadr arg1) (mv-let (flg2 arg2 ttree) (eval-ground-subexpressions (fargn term 2) ens wrld state ttree) (declare (ignore flg2)) (mv t arg2 ttree)) (mv-let (flg3 arg3 ttree) (eval-ground-subexpressions (fargn term 3) ens wrld state ttree) (declare (ignore flg3)) (mv t arg3 ttree)))) (t (mv-let (flg2 arg2 ttree) (eval-ground-subexpressions (fargn term 2) ens wrld state ttree) (mv-let (flg3 arg3 ttree) (eval-ground-subexpressions (fargn term 3) ens wrld state ttree) (mv (or flg1 flg2 flg3) (if (or flg1 flg2 flg3) (fcons-term* 'if arg1 arg2 arg3) term) ttree))))))) (t (mv-let (flg args ttree) (eval-ground-subexpressions-lst (fargs term) ens wrld state ttree) (cond ; The following test was taken from rewrite with a few modifications ; for the formals used. ((and (logicalp (ffn-symb term) wrld) ; maybe fn is being admitted (all-quoteps args) (enabled-xfnp (ffn-symb term) ens wrld) ; We don't mind disallowing constrained functions that have attachments, ; because the call of ev-fncall below disallows the use of attachments (last ; parameter, aok, is nil). (not (getprop (ffn-symb term) 'constrainedp nil 'current-acl2-world wrld))) (mv-let (erp val latches) (pstk (ev-fncall (ffn-symb term) (strip-cadrs args) state nil t nil)) (declare (ignore latches)) (cond (erp (cond (flg (mv t (cons-term (ffn-symb term) args) ttree)) (t (mv nil term ttree)))) (t (mv t (kwote val) (push-lemma (fn-rune-nume (ffn-symb term) nil t wrld) ttree)))))) (flg (mv t (cons-term (ffn-symb term) args) ttree)) (t (mv nil term ttree))))))) (defun eval-ground-subexpressions-lst (lst ens wrld state ttree) (cond ((null lst) (mv nil nil ttree)) (t (mv-let (flg1 x ttree) (eval-ground-subexpressions (car lst) ens wrld state ttree) (mv-let (flg2 y ttree) (eval-ground-subexpressions-lst (cdr lst) ens wrld state ttree) (mv (or flg1 flg2) (if (or flg1 flg2) (cons x y) lst) ttree)))))) ) (defun poly-set (op poly1 poly2) ; Suppose linearize is called on some term, term. The output of ; linearize is either nil, (list (list poly1 poly2)), or (list (list ; poly1) (list poly2)). An answer of the first form means "no ; arithmetic information can be extracted from the assumption that ; term is true." An answer of the second form means "both poly1 and ; poly2 are true if term is true." An answer of the third form means ; "either poly1 or poly2 is true if term is true." ; This functions takes two polys and an operation, op, and constructs ; the answer returned by linearize. Op is either 'and or 'or and ; determines whether we construct (list (list poly1 poly2)), when ; op='and, or (list (list poly1) (list poly2)), when op='or. ; However, there are two special cases. ; First, it is sometimes the case that we want to construct an 'and ; but only have one poly, the other one being nil. This may happen ; when we were going to construct an 'or but noticed that one branch ; is contradictory. When this happens it is always poly1 that is nil ; and poly2 that we want to return. ; Second, it may happen that either or both of the polys are silly in ; the sense that they are based on false assumptions. Since silly polys ; are logically true, we act accordingly. Thus, if we are to return ; a conjunction and one of the polys is silly we return the other. ; But if we are to return a disjunction and one is silly, we return nil, ; meaning we could extract no arithmetic information. For example, ; suppose poly2 is silly and we are to return (list (list poly1) (list poly2)). ; Then the silliness of poly2 means the second disjunct is true, which ; is represented here as (list (list poly1) nil). That, by the way, was ; nqthm's answer in this case. However, this disjunct gives the caller ; no help because it means either poly1 is true or T is true. So we ; just tell the caller "no information". ; Note about Nqthm: As remarked above, nqthm's linearize can return ; (list (list poly1) nil). How is this used? It is put on the ; split-lst when we start adding polynomials to the pot. We then see if ; we can get a contradiction from one side and if so, we assume the other. ; We certainly can't get a contradiction from the nil side. Suppose ; we got a contradiction from the other. Then we'd assume the other ; side, which is a no-op. The end result is that returning such an ; answer causes work but is guaranteed to have no effect. ; It is unlikely that we can generate a silly poly2 without generating a ; silly poly1, since silliness stems from requiring a false rationalp ; assumption. However, rather than convince ourselves that they are ; both silly if either is, we'll just check both. (cond ((eq op 'and) (cond ((or (eq poly1 nil) (silly-polyp poly1)) (cond ((silly-polyp poly2) nil) (t (list (list poly2))))) ((silly-polyp poly2) (list (list poly1))) (t (list (list poly1 poly2))))) ((or (silly-polyp poly1) (silly-polyp poly2)) nil) ((impossible-polyp poly1) (list (list (change poly poly2 :ttree (cons-tag-trees (access poly poly1 :ttree) (access poly poly2 :ttree)) :parents (marry-parents (access poly poly1 :parents) (access poly poly2 :parents)))))) ((impossible-polyp poly2) (list (list (change poly poly1 :ttree (cons-tag-trees (access poly poly1 :ttree) (access poly poly2 :ttree)) :parents (marry-parents (access poly poly1 :parents) (access poly poly2 :parents)))))) (t (list (list poly1) (list poly2))))) ;; RAG - I changed complex-rational to complex, and rational to real, ;; to stand for the non-zero numbers. (defun linearize1 (term positivep type-alist ens force-flg wrld ttree state) ; See the comment in linearize. Linearize1 does all the work of linearize ; except that the latter maps normalize-poly over the former. (mv-let (flg temp ttree) (eval-ground-subexpressions term ens wrld state ttree) (declare (ignore flg)) (mv-let (not-flg atm) (strip-not temp) ; Let us pause for a moment here. Term is the original term we are to ; linearize and is preserved for the use of add-linear-assumption. Temp is ; the result of replacing all ground subexpressions in term by their ; values, and atm is temp with any 'not stripped off. Recall that we ; are attempting to derive a contradiction by assuming either (1) that ; term is true if positivep is true, or (2) that term is false if ; positivep is false. Since not-flg tells us whether atm is the ; negation of term/temp, we use it to reset positivep to reflect its ; new role with respect to atm. (let ((positivep (if not-flg (not positivep) positivep))) (cond ((inequalityp atm) (let ((lhs (fargn atm 1)) (rhs (fargn atm 2))) (mv-let (ts-lhs ts-ttree) (type-set lhs force-flg nil type-alist ens wrld ttree nil nil) (mv-let (ts-rhs ts-ttree) (type-set rhs force-flg nil type-alist ens wrld ts-ttree nil nil) (if positivep ; (< lhs rhs) (cond ((and (ts-integerp ts-lhs) (ts-integerp ts-rhs)) ; (implies (and (< lhs rhs) ; (integerp lhs) ; (integerp rhs)) ; (<= (1+ lhs) rhs)) (poly-set 'and nil (add-linear-terms :lhs lhs :lhs *1* :rhs rhs (base-poly ts-ttree '<= t nil)))) (t ; still (< lhs rhs), but not both integerp (poly-set 'and nil (add-linear-terms :lhs lhs :rhs rhs (let ((rationalp-flg (and (ts-real/rationalp ts-lhs) (ts-real/rationalp ts-rhs)))) (base-poly0 (if rationalp-flg ts-ttree ttree) ; :Parent wart: ; In a slight break from tradition (here and in three other places below that ; refer to this comment), we use the parents from the original ttree. When ; fixing a (probably long-standing) bug in Version_3.0.1 by recording ts-ttree ; above in the case that rationalp-flg is true, we found that the regression ; suite broke in three places without this tweak on the parents. Since ; rationalp-flg is only used in non-linear arithmetic, this seems like a minor ; break from our traditional propagation of parent trees. We considered making ; a similar change for all calls of base-poly in this function, but that led to ; a proof failure in community book ; books/workshops/2004/schmaltz-borrione/support/routing_defuns.lisp that ; looked like it would be painful to fix, and we took that as a sign that such ; loss of backward compatibility could be painful for other users, and ; potentially even a bad heuristic. (collect-parents ttree) '< rationalp-flg nil)))))) ; The (not positivep) case. Note: ; (implies (not (< lhs rhs)) ; (<= rhs lhs)) (poly-set 'and nil (add-linear-terms :lhs rhs :rhs lhs (let ((rationalp-flg (and (ts-real/rationalp ts-lhs) (ts-real/rationalp ts-rhs)))) (base-poly0 (if rationalp-flg ts-ttree ttree) ; See the "break from tradition" comment above for a discussion of the ; parents. (collect-parents ttree) '<= rationalp-flg nil))))))))) ((equalityp atm) (let ((lhs (fargn atm 1)) (rhs (fargn atm 2))) (mv-let (ts-lhs ts-ttree) (type-set lhs force-flg nil type-alist ens wrld ttree nil nil) (mv-let (ts-rhs ts-ttree) (type-set rhs force-flg nil type-alist ens wrld ts-ttree nil nil) ; Here is the one place that we can construct a poly which is derived ; from a negated equality. Note that the final argument to base-poly ; is 'T. (if positivep ; (implies (equal lhs rhs) ; (and (<= lhs rhs) (<= rhs lhs))) (let ((rationalp-flg (and (ts-real/rationalp ts-lhs) (ts-real/rationalp ts-rhs)))) (poly-set 'and (add-linear-terms :lhs lhs :rhs rhs (base-poly0 (if rationalp-flg ts-ttree ttree) ; See the "break from tradition" comment above for a discussion of the ; parents. (collect-parents ttree) '<= rationalp-flg t)) (add-linear-terms :lhs rhs :rhs lhs (base-poly0 (if rationalp-flg ts-ttree ttree) ; See the "break from tradition" comment above for a discussion of the ; parents. (collect-parents ttree) '<= rationalp-flg t)))) ; Other case: (not (equal lhs rhs)). But we need additional (type) information ; in order to derive inequalities. (cond ((and (ts-subsetp ts-lhs *ts-integer*) (ts-subsetp ts-rhs *ts-integer*)) ; (implies (and (not (equal lhs rhs)) ; (integerp lhs) ; (integerp rhs)) ; (or (<= (1+ lhs) rhs) ; (<= (1+ rhs) lhs))) (poly-set 'or (add-linear-terms :lhs lhs :lhs *1* :rhs rhs (base-poly ts-ttree '<= t nil)) (add-linear-terms :lhs rhs :lhs *1* :rhs lhs (base-poly ts-ttree '<= t nil)))) ((if (ts-subsetp ts-lhs *ts-acl2-number*) (or (ts-subsetp ts-rhs *ts-acl2-number*) (ts-disjointp ts-lhs *ts-zero*)) (and (ts-subsetp ts-rhs *ts-acl2-number*) (ts-disjointp ts-rhs *ts-zero*))) ; (implies (and (not (equal lhs rhs)) ; (or (and (acl2-numberp lhs) ; (acl2-numberp rhs)) ; (and (acl2-numberp lhs) ; (not (equal lhs 0))) ; (and (acl2-numberp rhs) ; (not (equal rhs 0))))) ; (or (< lhs rhs) ; (< rhs lhs))) (let ((rationalp-flg (and (ts-real/rationalp ts-lhs) (ts-real/rationalp ts-rhs)))) (poly-set 'or (add-linear-terms :lhs lhs :rhs rhs (base-poly ts-ttree '< rationalp-flg nil)) (add-linear-terms :lhs rhs :rhs lhs (base-poly ts-ttree '< rationalp-flg nil))))) ((and (ts-acl2-numberp ts-lhs) force-flg (ts-intersectp ts-rhs *ts-acl2-number*)) ; (implies (and (not (equal lhs rhs)) ; (acl2-numberp lhs) ; (force (acl2-numberp rhs))) ; (or (< lhs rhs) ; (< rhs lhs))) (mv-let (flg new-ttree) (add-linear-assumption term `(acl2-numberp ,rhs) type-alist ens (immediate-forcep nil ens) force-flg wrld ts-ttree) ; We strongly suspect that add-linear-assumption will succeed with flg = ; :added, since (ts-intersectp ts-rhs *ts-acl2-number*). But we do not depend ; on this without checking it. Indeed, it fails for the following example, ; sent to us by Sol Swords. ; (defstub bar-p (x) nil) ; (defstub foo (x) nil) ; ; (defaxiom type-of-foo ; (implies (force (bar-p x)) ; (or (and (rationalp (foo x)) ; (<= 0 (foo x))) ; (equal (foo x) nil))) ; :rule-classes :type-prescription) ; ; (thm (implies (not (rationalp (foo x))) (equal 0 (foo x)))) (cond ((and (not (eq flg :failed)) (not (eq flg :known-false))) (poly-set 'or (add-linear-terms :lhs lhs :rhs rhs (base-poly new-ttree '< nil nil)) (add-linear-terms :lhs rhs :rhs lhs (base-poly new-ttree '< nil nil)))) (t nil)))) ((and (ts-acl2-numberp ts-rhs) force-flg (ts-intersectp ts-lhs *ts-acl2-number*)) ; (implies (and (not (equal lhs rhs)) ; (acl2-numberp rhs) ; (force (acl2-numberp lhs))) ; (or (< lhs rhs) ; (< rhs lhs))) (mv-let (flg new-ttree) (add-linear-assumption term `(acl2-numberp ,lhs) type-alist ens (immediate-forcep nil ens) force-flg wrld ts-ttree) (cond ((and (not (eq flg :failed)) (not (eq flg :known-false))) (poly-set 'or (add-linear-terms :lhs lhs :rhs rhs (base-poly new-ttree '< nil nil)) (add-linear-terms :lhs rhs :rhs lhs (base-poly new-ttree '< nil nil)))) (t nil)))) (t nil))))))) ((quotep atm) ; This is a strange one. It can happen that the ; eval-ground-subexpressions can reduce the term to a constant. We ; saw this happen in a bug reported by Jun Sawada. Suppose (<= 2 (foo ; x)) is a :LINEAR lemma about foo. Suppose we wish to prove (not ; (equal 1 (foo 3))). This should be obvious. But we assume the ; negation of the conclusion and get (foo 3) = 1. We then find the ; linear lemma and instantiate it to get (<= 2 (foo 3)). We then ; rewrite-linear-concl and get (<= 2 1), which we eval to 'nil! If we ; do not recognize that we've succeeded here, we do not prove the ; theorem because all manner of other heuristics prevent us from using ; (<= 2 (foo x)) against the current literal. Not surprisingly, this ; is an example of tail biting: we've used the negation of the goal to ; prevent ourselves from proving it! One could probably construct a ; more insidious example of tail biting from this example -- an ; example that is not solved by the hack here of recognizing when eval ; solved our problem -- by arranging for rewrite-linear-concl to ; rewrite the inequality to something that we can't use but which ; doesn't eval to nil. ; Here is another curious example: ; ACL2 !> ; (thm ; (implies (and (not (consp x)) ; (true-listp x)) ; (equal (reverse (reverse x)) x))) ; ; But simplification reduces this to T, using the :executable-counterparts ; of EQUAL and REVERSE and linear arithmetic. ; ; Q.E.D. ; ; Summary ; Form: ( THM ...) ; Rules: ((:DEFINITION NOT) ; (:EXECUTABLE-COUNTERPART EQUAL) ; (:EXECUTABLE-COUNTERPART REVERSE) ; (:FAKE-RUNE-FOR-LINEAR NIL)) ; Warnings: None ; Time: 0.01 seconds (prove: 0.01, print: 0.00, other: 0.00) ; ; Proof succeeded. ; Note the presence of (:FAKE-RUNE-FOR-LINEAR NIL). ; This oddity is due to the fact that we now rewrite all terms (not ; just the conclusion of linear lemmas) before adding them to the ; linear-pot-lst. (if positivep ; Recall that we are hoping to derive a contradiction from assuming atm ; true. Hence, we win iff atm is 'NIL. (if (equal atm *nil*) (poly-set 'and nil (impossible-poly ttree)) nil) ; We are hoping to derive a contradiction from assuming atm false. Hence, ; we win iff atm is not 'NIL. (if (not (equal atm *nil*)) (poly-set 'and nil (impossible-poly ttree)) nil))) (t nil)))))) (defun linearize (term positivep type-alist ens force-flg wrld ttree state) ; If positivep we are to linearize term; else we are to linearize the negation ; of term. The linearization of a term is either NIL, meaning no linear ; information was extracted from the term; or else it is a list of length 1 ; containing a list of polynomials ((p1...pn)) such that term implies their ; conjunction (p1&...&pn); or else it is a list of length 2, ((p1...pn) ; (q1...qn)), such that term implies the disjunction (p1&...&pn) \/ ; (q1&...&qn). The claim that the term implies the polys has to be taken with ; a grain of salt: the additional 'assumptions in the ttree fields of the polys ; must be considered. ; There are two situations where this code might add an assumption to the polys ; it creates. The first is that we sometimes call type-set and may get back a ; ttree with assumptions in it, which then infect our polys. The second ; involves the linearization of negative equalities, where we insist that both ; x and y be numeric in order to derive (or (< x y) (< y x)) from (not (equal x ; y)). Otherwise, we do not add any assumptions to our polys. ; We store ttree in the ttree of the poly. ; Trace Note: ; (trace (linearize ; :entry ; (list* (car si::arglist) (cadr si::arglist) (caddr si::arglist) ; '|ens| (car (cddddr si::arglist)) '(|wrld| |ttree| |state|)) ; :exit ; (cond ((null (car values)) (list nil)) ; ((null (cdr (car values))) ; (list (cons 'and (show-poly-lst (car (car values)))))) ; (t (list ; (list 'or ; (cons 'and (show-poly-lst (car (car values)))) ; (cons 'and (show-poly-lst (cadr (car values)))))))))) (let ((temp (linearize1 term positivep type-alist ens force-flg wrld ttree state))) (cond ((null temp) nil) ((null (cdr temp)) (list (normalize-poly-lst (car temp)))) (t (list (normalize-poly-lst (car temp)) (normalize-poly-lst (cadr temp))))))) (defun linearize-lst1 (term-lst ttrees positivep type-alist ens force-flg wrld state poly-lst split-lst) (cond ((null term-lst) (mv (reverse poly-lst) (reverse split-lst))) (t (let ((lst (linearize (car term-lst) positivep type-alist ens force-flg wrld (car ttrees) state))) (cond ((null lst) (linearize-lst1 (cdr term-lst) (cdr ttrees) positivep type-alist ens force-flg wrld state poly-lst split-lst)) ((null (cdr lst)) (linearize-lst1 (cdr term-lst) (cdr ttrees) positivep type-alist ens force-flg wrld state (revappend (car lst) poly-lst) split-lst)) (t (linearize-lst1 (cdr term-lst) (cdr ttrees) positivep type-alist ens force-flg wrld state poly-lst (cons lst split-lst)))))))) (defun linearize-lst (term-lst ttrees positivep type-alist ens force-flg wrld state) ; This function linearizes every term in term-lst, using the parity ; indicated by positivep, and type-alist and wrld. This effectively ; assumes true/false (as per positivep) each term in term-lst and produces ; some polys. Ttrees is in weak 1:1 correspondence with term-lst and ; enumerates the parent trees to use for each term in all the polys ; generated for the term; if ttrees is nil, no parent tree is stored. ; We return two values, poly-lst and split-lst. The first is a list of ; polys that may be assumed true. I.e., all these polys are implied by the ; assumption of term-lst. The second is a list of doublets (lst1 lst2), ; such that each lst is a list of polys and the assumption of term-lst ; implies one of either lst1 or lst2 for each doublet. (linearize-lst1 term-lst ttrees positivep type-alist ens force-flg wrld state nil nil)) acl2-sources/Makefile0000664002132200015000000000273411605147054014224 0ustar kaufmannacl2# Only GNU make should be used. The real makefile is GNUmakefile. # The following include all targets in GNUmakefile, obtained by: # grep '^[a-zA-Z].*:' GNUmakefile all: err small: echo 'Target "small" is no longer supported. Use "make large" or simply "make".' exit 1 acl2r: err protections: err chmod_image: err do_saved: err check_compile_ok: err check_init_ok: err fast: err check: err compile-ok: err very-fast: err fast-meter: err check-sum: err full: err full-meter: err copy: err copy-distribution: err copy-workshops: err copy-nonstd: err copy-developers: err TAGS: err move-to-old: err move-new: err init: err proofs: err DOC: err TEXINFO: err HTML: err clean: err red: err large: err large-acl2r: err move-large: err certify-books: err regression: err regression-nonstd: err certify-books-fresh: err regression-fresh: err regression-nonstd-fresh: err certify-books-short: err certify-books-test: err infix-init: err infix-init10: err infix-fin: err clean-doc: err clean-books: err clean-books-nonstd: err tar: err tar-workshops: err tar-nonstd: err test: err big-test: err arch: err mini-proveall: err allegro-app: err err: @echo "ERROR:" @echo "Apparently you are using other than GNU make" @echo "(which MIGHT be found in /lusr/gnu/bin/make)." @echo "Only GNU make is supported. If you think your" @echo "'make' might work, you can move or delete" @echo "ACL2's Makefile and copy or rename ACL2's" @echo "GNUmakefile to Makefile." @echo "Exiting with error...." @exit 1 acl2-sources/mcl-acl2-startup.lisp0000666002132200015000000001235312123665115016546 0ustar kaufmannacl2 Here are instructions for how to install ACL2 on MCL (Macintosh Common Lisp). These instructions were developed for MCL 4.0 and have been used successfully on MCL 4.2. Thanks to Pascal Costanza for providing information pertaining to MCL 5.0 on Mac OS X and to Warren Hunt for additional information about linebreaks. ==================== SECTION: Dealing with Unix linebreaks =================== The ACL2 sources use Unix linebreaks. MCL (at least up to and including 5.0) expects Macintosh (classic) linebreaks. So you need to make sure that the linebreaks are transformed accordingly. You can do this on the command line with "tr" right on the uncompressed tar file, for example as follows. tr "\n" "\r" < acl2.tar > acl2-mac.tar The conversion has to be made on the uncompressed "tar" file, not the ".tgz" or ".tar.gz" file. The double quotation marks (") are important, since without them, the "\n" and the "\r" may be interpreted by some kind of reader, e.g., a shell, prior to being passed to "tr". Alternatively, instead of using "tr", you can drag and drop the resulting folder on the LineBreak utility which is available from http://www.macalester.edu/~jaas/linebreak.html. Or you can avoid the tar files and fetch the files directly with appropriate linebreak handling; for example, when Fetch determines that it is downloading ASCII text files, it automatically converts line feeds into carriage returns , which will probably do what is necessary. =============================== SECTION: Memory ============================== ACL2 consumes quite a lot of heap space. You may want to select the MCL application and use Command-I to set (in particular, increase) the memory. 30 or (probably) more megabytes might be a good quantity. We invite user suggestions for a better number, and more generally for any improvements to this file. MCL 5.0 uses the "classic" approach for the allocation of heap space. You can change the memory size settings for MCL 5.0 as follows: - Select the MCL 5.0 app. - Press Command-I. - Check the box "Open in the Classic environment" [sic!]. - Select the "Memory" settings and set the memory size. - Uncheck the box "Open in the Classic environment". - Close the Info dialog. =========================== SECTION: Preliminaries =========================== The instructions below mention a certain pathname repeatedly. The pathname as it appears here is "xyz". Before executing these instructions, you should replace "xyz" by the full pathname of the ACL2 source directory, without the final ":". For example, "xyz" might be replaced by "Macintosh HD:Applications:acl2-sources" Every occurrence of "xyz" below should be so replaced. This also holds for MCL 5.0 because it still expects "classic" pathnames. Start up a Listener and execute each of the commands below. Make sure that you type all the commands directly in the listener, or copy and paste them from this file. Don't execute them from here via Command-E or the like. ===================== SECTION: Build commands (Lisp code) ==================== ; See the comments above. (setq *default-pathname-defaults* #P"xyz:") (ccl::set-mac-default-directory *default-pathname-defaults*) (load "xyz:init.lsp") (in-package "ACL2") (check-suitability-for-acl2) (compile-acl2) (initialize-acl2 'include-book *acl2-pass-2-files*) (or (not (probe-file *acl2-status-file*)) (delete-file *acl2-status-file*)) (with-open-file (str *acl2-status-file* :direction :output) (format str "~s" :initialized)) ; You may need to evaluate the following form if ccl::%doc-string-file ; is not already defined. You can perhaps evaluate the MCL form ; (directory "**:mcl help") to obtain all of the MCL Help files on your ; machine and choose one that is appropriate for the MCL you are running. ; Unfortunately, you may need to do this each time you start up a saved image. ; (setq ccl::%doc-string-file "MCL Help") ; Finally, save an ACL2 application: (cl-user::save-application "saved_acl2") ============================ SECTION: Running ACL2 =========================== The commands above build an ACL2 application saved_acl2 on your MacIntosh in the acl2-sources directory where you saved it ("xyz" above). When the application is saved, your Lisp Listener disappears. Now move saved_acl2 to the directory with the MCL kernel, library and compiler files, which is probably the directory with the MCL application that you started up in order to build ACL2. Alternatively, you can copy those files to the folder that contains saved_acl2, or put aliases to them in that folder. You can now start ACL2 by opening saved_acl2. Now it is time to re-certify the arithmetic and meta books that come with ACL2. First make sure that the books subdirectory of the top-level ACL2 directory has been installed on your machine. Start up the saved_acl2 image created above and execute: ; The following have been replaced starting with v2-6 with the set-cbd command ; below, which may suffice. ; (setq *default-pathname-defaults* "xyz:books:") ; (ccl::set-mac-default-directory *default-pathname-defaults*) (lp) ; In the next form, XYZ is the result of replacing ":" by "/" in xyz. (set-cbd "/XYZ/books") (ld "certify-numbers.lisp" :standard-co "certify.out" :proofs-co "certify.proofs") Then quit from the saved_acl2. acl2-sources/memoize.lisp0000664002132200015000000014303312222115527015114 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; memoize.lisp -- Logical definitions for memoization functions, only ; to be included in the experimental HONS version of ACL2. ; The original version of this file was contributed by Bob Boyer and ; Warren A. Hunt, Jr. The design of this system of Hash CONS, ; function memoization, and fast association lists (applicative hash ; tables) was initially implemented by Boyer and Hunt. (in-package "ACL2") (defmacro defn (f a &rest r) ":Doc-Section Events definition with ~il[guard] ~c[t]~/ ~c[Defn] is ~ilc[defun] with ~il[guard] ~c[t].~/~/" `(defun ,f ,a (declare (xargs :guard t)) ,@r)) (defmacro defnd (f a &rest r) ":Doc-Section Events ~il[disable]d definition with ~il[guard] ~c[t]~/ ~c[Defnd] is ~ilc[defund] with ~il[guard] ~c[t].~/~/" `(defund ,f ,a (declare (xargs :guard t)) ,@r)) (defdoc hons-and-memoization ":Doc-Section Hons-and-Memoization hash cons, function memoization, and applicative hash tables~/ Bob Boyer and Warren Hunt, and later Jared Davis and Sol Swords, have developed a canonical representation for ACL2 data objects and a function memoization mechanism to facilitate reuse of previously computed results. This facility includes procedures to read and print ACL2 expressions in such a way that repetition of some ACL2 objects is eliminated, thereby permitting a kind of on-the-fly file compression. The implementation does not alter the semantics of ACL2 except to add a handful of definitions. We give the name ``ACL2(h)'' to the resulting experimental extension of the ACL2 system, which includes hash cons, function memoization, and fast association lists (applicative hash tables). It is optimized for Clozure Common Lisp (CCL), but other ANSI-compliant host Lisp implementations may also work, provided there are sufficient machine resources. If you want to use ACL2(h), you might find it helpful to consult the document ~c[centaur/README.html] in the ACL2 community books. An easy way is to build ACL2(h) is to include the following with a `make' command: ~bv[] ACL2_HONS=h ~ev[] So for example, to make an executable image and also documentation (which will appear in subdirectories ~c[doc/EMACS] and ~c[doc/HTML]): ~bv[] make large DOC ACL2_HONS=h ~ev[]~/ Much of the documentation for the remainder of this topic is taken from the paper ``Function Memoization and Unique Object Representation for ACL2 Functions'' by Robert S. Boyer and Warren A. Hunt, Jr., which has appeared in the Sixth International Workshop on the ACL2 Theorem Prover and Its Applications, ACM Digital Library, 2006. In the implementation of the ACL2 logic, ACL2 data objects are represented by Common Lisp objects of the same type, and the ACL2 pairing operation is internally implemented by the Common Lisp ~ilc[cons] function. In Common Lisp, ~c[cons] is guaranteed to provide a new pair, distinct from any previously created pair. We have defined a new ACL2 function ~ilc[HONS] that is logically identical to the ACL2 ~c[cons] function, but whose implementation usually reuses an existing pair if its components are identical to the components of an existing pair. A record of ACL2 HONS objects is kept, and when an ACL2 function calls ~c[hons] ACL2 searches for an existing identical pair before allocating a new pair; this operation been called ``hash consing''. It appears that hash consing was first conceived by A. P. Ershov in 1957, to speed up the recognition of common subexpressions. Ershov showed how to collapse trees to minimal DAGs by traversing trees bottom up, and he used hashing to eliminate the re-evaluation of common subexpressions. Later, Eiichi Goto implemented a Lisp system with a built-in hash consing operation: his h-CONS cells were rewrite protected and free of duplicate copies, and Goto used this hash consing operation to facilitate the implementation of a symbolic algebra system he developed. Memoizing functions also has a long history. In 1967, Donald Michie proposed using memoized functions to improve the performance of machine learning. Rote learning was improved by a learning function not forgetting what it had previously learned; this information was stored as memoized function values. The use of hash consing has appeared many times. For instance, Henry Baker used hash consing to improve the performance of the well-known Boyer rewriting benchmark. Baker used both hash consing and function memoization to improve the speed of the Takeuchi function, exactly in the spirit of our implementation, but without the automated, system-wide integration we report here. The ACL2 implementation permits memoization of user-defined functions. During execution a user may enable or disable function memoization on an individual function basis, may clear memoization tables, and may even keep a stack of memoization tables. This facility takes advantage of our implementation where we keep one copy of each distinct ACL2 data object. Due to the functional nature of ACL2, it is sufficient to have at most one copy of any data structure; thus, a user may arrange to keep data canonicalized. This implementation extends to the entire ACL2 system the benefits enjoyed by BDDs: canonicalization, memoization, and fast equality check. We have defined various algorithms using these features, and we have observed, in some cases, substantial performance increases. For instance, we have implemented unordered set intersection and union operations that operate in time roughly linear in the size of the sets. Without using arrays, we defined a canonical representation for Boolean functions using ACL2 objects. We have investigated the performance of rewriting and tree consensus algorithms to good effect, and we believe function memoization offers interesting opportunities to simplify algorithm definition while simultaneously providing performance improvements. We recommend that users focus at first on the logical definitions of ~ilc[hons] and other primitives rather than their underlying Common Lisp implementations. Integrated with this memoization system is a performance monitoring system, which can provide real-time feedback on the operation and usefulness of ~ilc[hons] and function memoization. For a more detailed description of these tools, please see the ACL2 2006 workshop paper mentioned above. The Fibonacci function is a small example that demonstrates the utility of function memoization. The following definition exhibits a runtime that is exponential in its input argument. ~bv[] (defun fib (x) (declare (xargs :guard (natp x))) (mbe :logic (cond ((zp x) 0) ((= x 1) 1) (t (+ (fib (- x 1)) (fib (- x 2))))) :exec (if (< x 2) x (+ (fib (- x 1)) (fib (- x 2)))))) ~ev[] Below we show how the ACL2 ~ilc[time$] utility can measure the time to execute a call to the ~c[fib] function (with some editing to avoid noise from the underlying Common Lisp implementation). ~ilc[Memoize] is actually an ACL2 macro that expands to a call of the ACL2 function used to identify a function for memoization; ~pl[memoize]. This function also accepts a well-formed term that must be true in order for the system to memoize a call of the memoized function; to ensure that an instance of the term is safe for evaluation in Common Lisp, a check is performed that if the ~il[guard] of the memoized function is satisfied, then this instance will execute without error. ~bv[] ACL2 !>(time$ (fib 40)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.99 seconds realtime, 0.98 seconds runtime ; (1,296 bytes allocated). 102334155 ACL2 !>(memoize 'fib) Summary Form: ( TABLE MEMOIZE-TABLE ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) Summary Form: ( PROGN (TABLE MEMOIZE-TABLE ...) ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FIB ACL2 !>(time$ (fib 40)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.00 seconds realtime, 0.00 seconds runtime ; (2,864 bytes allocated). 102334155 ACL2 !>(time$ (fib 100)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.00 seconds realtime, 0.00 seconds runtime ; (7,024 bytes allocated). 354224848179261915075 ACL2 !>(unmemoize 'fib) ~ev[] We see that once the function ~c[fib] is identified as a function for which function calls should be memoized, the execution times are substantially reduced. Finally, we can prevent ~c[fib] from being further memoized; in fact, ~ilc[unmemoize] erases the previously memoized values. The implementation of hash consing, memoization, and applicative hash tables involves several facets: canonical representation of ACL2 data, function memoization, and the use of Lisp hash tables to improve the performance of association list operations. We discuss each of these in turn, and we mention some subtle interrelationships. Although it is not necessary to understand the discussion in this section, it may permit some users to better use this implementation. This discussion may be confusing for some ACL2 users as it makes references to Lisp implementation features. The ACL2 system is actually implemented as a Lisp program that is layered on top of a Common Lisp system implementation. ACL2 data constants are implemented with their corresponding counterparts in Common Lisp; that is, ACL2 cons pairs, strings, characters, numbers, and symbols are implemented with their specific Common Lisp counterparts. This choice permits a number of ACL2 primitive functions to be implemented with their corresponding Common Lisp functions, but there are indeed differences. ACL2 is a logic, and as such, it does not specify anything to do with physical storage or execution performance. When ACL2 is used, the knowledgeable user can write functions to facilitate the reuse of some previously computed values. Recall the three mechanisms under discussion: hash consing, function memoization, and fast association list operations. The function memoization mechanism takes advantage of the canonical representation of data objects provided by the ~ilc[hons] operation as does the fast association list operation mechanism. Each time ~c[hons] is invoked, its arguments are themselves converted, if necessary, to uniquely represented objects. The ACL2 universe is recursively closed under the ~c[cons] pairing operation and the atoms. Hash consing (~ilc[hons]) is logically identical to ~c[cons], but a set of tables is used to record each ~c[hons] pair. When a ~c[hons] pair is requested, the implementation checks, in the current set of tables, whether the requested pair already exists. If not, a new pair is created and a record of that pair is made; otherwise, a reference to the previously created pair is returned. Thus, any data object created with ~c[hons] has a unique representation, as does every subcomponent. We also arrange for strings to have a unique representation ~-[] only one copy of each different string is kept ~-[] and when any previously unseen string is an argument to ~c[hons], we add the string to a unique table of strings. A system-wide benefit of having a canonical representation for data is that equality comparisons between any two data objects can be done in constant time. The definition of ~ilc[hons] in no way changes the operation of ~c[cons] ~-[] ~c[hons] creates a ~c[cons] pair. When asked to create a ~c[hons], the implementation checks to see if there is a ~c[cons] with the same ~ilc[car] and ~ilc[cdr] as the ~c[hons] being requested. Thus, the only difference between the results of a ~c[hons] call and a corresponding ~c[cons] call is a notation in some invisible (to the ACL2 logic) tables where we record which ~c[cons] elements are also ~c[hons] elements. Since a ~c[hons] is nothing but a ~c[cons], the operation of ~c[car] and ~c[cdr] is unchanged. In fact, the implementation is designed so that at any time it is safe to clear the table identifying which ~c[cons] elements are also considered ~c[hons] elements. User-defined functions with defined and verified guards can be memoized. When a function is memoized, a user-supplied condition restricts the domain when memoization is attempted. When the condition is satisfied, memoization is attempted (assuming that memoization for the function is presently enabled); otherwise, the function is just evaluated. Each memoized function has a hash table that is used to keep track of a unique list of function arguments paired with their values. If appropriate, for each function the corresponding table is checked to see if a previous call with exactly the same arguments already exists in the table: if so, then the associated value is returned; if not, then the function is evaluated and a new key-value pair is added to the table of memoized values so that some future call will benefit from the memoization. With ACL2 user functions memoization can be dynamically enabled and disabled. There is an ACL2 function that clears a specific memoization table. And finally, just as with the ~c[hons] table, a stack of these function memoization tables is maintained; that is, it is possible to memoize a function, use it a bit, set the memoized values aside, start a new table, use it, and then return to the original table. We next discuss the fast lookup operation for association lists. When a pair is added to an association list using the functions ~ilc[hons-acons] or ~ilc[hons-acons!], the system also records the key-value pair in an associated hash table. As long as a user only used these two functions when placing key-value pairs on an association list, the key-value pairs in the corresponding hash table will be synchronized with the key-value pairs in the association list. Later, if ~ilc[hons-get] is used to look up a key, then instead of performing a linear search of the association list we consult the associated hash table. If a user does not strictly follow this discipline, then a linear search may be required. In some sense, these association lists are much like ACL2 arrays, but without the burden of explicitly naming the arrays. The ACL2 array ~ilc[compress1] function is analogous to the functions ~ilc[hons-shrink-alist] and ~ilc[hons-shrink-alist!]. There are user-level ACL2 functions that allow the associated hash tables to be cleared and removed. Finally, we would advise anyone who is using CCL in a research environment to stay plugged into the ``trunk'' or ``bleeding edge'' of CCL development. This is very easy to do by typing a few commands to a shell, for example standing above the target directory as follows, provided one has ~c[svn] working. ~bv[] For linux: rm -rf ccl svn co http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx8664/ccl For an x86 Macintosh running the Darwin OS: svn co http://svn.clozure.com/publicsvn/openmcl/trunk/darwinx8664/ccl To keep up to date, you may find it sufficient to do: cd ccl svn update Whether obtaining a fresh CCL or just updating, finally issue these commands. ./lx86cl64 (rebuild-ccl :full t) (quit) ./lx86cl64 (rebuild-ccl :full t) (quit) ~ev[] ~sc[References] Baker, Henry G., The Boyer Benchmark at Warp Speed. ACM Lisp Pointers V,3 (Jul-Sep 1992), pages 13-14. Baker, Henry G., A Tachy 'TAK'. ACM Lisp Pointers Volume 3, July-September, 1992, pages 22-23. Robert S. Boyer and Warren A. Hunt, Jr., Function Memoization and Unique Object Representation for ACL2 Functions, in the Sixth International Workshop on the ACL2 Theorem Prover and Its Applications, ACM Digital Library, 2006. A. P. Ershov. On Programming of Arithmetic Operations. In the Communications of the ACM, Volume 118, Number 3, August, 1958, pages 427-430. Eiichi Goto, Monocopy and Associative Algorithms in Extended Lisp, TR-74-03, University of Toyko, 1974. Richard P. Gabriel. Performance and Evaluation of Lisp Systems. MIT Press, 1985. Donald Michie. Memo functions: a Language Feature with Rote Learning Properties. Technical Report MIP-R-29, Department of Artificial Intelligence, University of Edinburgh, Scotland, 1967. Donald Michie. Memo Functions and Machine Learning. In Nature, Volumne 218, 1968, pages 19-22. ~/") #+(or acl2-loop-only (not hons)) (defn clear-memoize-table (fn) ":Doc-Section Hons-and-Memoization forget values remembered for the given function~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. This function returns its argument, ~c[fn], unchanged. The values memoized for ~c[fn] are forgotten.~/~/" fn) #+(or acl2-loop-only (not hons)) (defn clear-memoize-tables () ":Doc-Section Hons-and-Memoization forget values remembered for all the memoized functions~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. ~c[Clear-memoize-tables] is a logical no-op. All memoized values are forgotten. It returns ~c[nil], invoking ~ilc[clear-memoize-table] for each memoized function.~/~/" nil) #+(or acl2-loop-only (not hons)) (defn memoize-summary () ":Doc-Section Hons-and-Memoization display all collected profiling and memoization table info~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, this function just returns ~c[nil], but it displays profiling and memoization table information. The profiling statistics may be cleared with ~c[(]~ilc[clear-memoize-statistics]~c[)].~/~/" nil) #+(or acl2-loop-only (not hons)) (defn clear-memoize-statistics () ":Doc-Section Hons-and-Memoization clears all profiling info displayed by ~c[(]~ilc[memoize-summary]~c[)]~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, this function just returns ~c[nil]. It clears all profiling info displayed by ~c[(]~ilc[memoize-summary]~c[)]~/~/" nil) (defmacro memsum () ":Doc-Section Hons-and-Memoization display all collected profiling and memoization info~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. This macro is an abbreviation for ~ilc[memoize-summary]. Logically, it just returns ~c[nil].~/~/" '(memoize-summary)) ; The macros MEMOIZE-ON and MEMOIZE-OFF typically cause "under the hood" ; effects that, though not changing the semantics of what ACL2 returns, may ; affect the speed and/or space utilization of the computation. ; The functions memoize and unmemoize have rather innocent looking ; semantics. But under the hood, they enable and disable memoization. ; The function memoize might cause errors due to compilation problems. (defconst *hons-primitive-fns* ;; This affects acl2-exports, maybe other stuff '(hons-copy hons hons-equal hons-equal-lite hons-clear hons-wash hons-summary hons-resize-fn get-slow-alist-action hons-get hons-acons hons-acons! hons-shrink-alist hons-shrink-alist! fast-alist-len fast-alist-free fast-alist-summary cons-subtrees number-subtrees clear-hash-tables flush-hons-get-hash-table-link ;; from memoize.lisp clear-memoize-table clear-memoize-tables memoize-summary clear-memoize-statistics)) (defconst *hons-primitives* ;; hons-related macros and primitive fns (append '(hons-resize set-slow-alist-action memoize unmemoize memoize-on memoize-off memsum) *hons-primitive-fns*)) (defconst *mht-default-size* 60) (defun memoize-form (fn condition condition-p condition-fn hints otf-flg inline trace commutative forget memo-table-init-size aokp ideal-okp) ; Jared Davis suggests that we consider bundling up these 13 parameters, for ; example into an alist. He says: "Various subsets of these arguments occur in ; spaghetti fashion throughout the code for memoize, add-trip, the ; memoize-table stuff, etc." (declare (xargs :guard t)) (let ((condition (cond ((equal condition ''t) t) ((equal condition ''nil) nil) (t condition)))) (cond ((and condition-fn (null condition-p)) `(progn (table memoize-table (deref-macro-name ,fn (macro-aliases world)) (list* (cons :condition-fn ,condition-fn) (cons :inline ,inline) (cons :trace ,trace) (cons :commutative ,commutative) (cons :forget ,forget) (cons :memo-table-init-size ,(or memo-table-init-size *mht-default-size*)) (cons :aokp ,aokp) (and (not (eq ,ideal-okp :default)) (list (cons :ideal-okp ,ideal-okp))))) (value-triple (deref-macro-name ,fn (macro-aliases (w state)))))) ((and condition-p (not (eq condition t)) (not (eq condition nil))) `(make-event (let* ((wrld (w state)) (fn (deref-macro-name ,fn (macro-aliases wrld))) (condition ,condition) (formals (and (symbolp fn) ; guard for getprop (getprop fn 'formals t 'current-acl2-world wrld))) (stobjs-in (getprop fn 'stobjs-in t 'current-acl2-world wrld)) (condition-fn (or ,condition-fn (intern-in-package-of-symbol (concatenate 'string (symbol-name fn) "-MEMOIZE-CONDITION") fn))) (hints ,hints) (otf-flg ,otf-flg) (inline ,inline) (trace ,trace) (commutative ,commutative) (forget ,forget) (memo-table-init-size ,memo-table-init-size) (aokp ,aokp) (ideal-okp ,ideal-okp)) (cond ((not (and (symbolp fn) (not (eq t formals)) (not (eq t stobjs-in)) (not (eq t (getprop fn 'stobjs-out t ; Normally we would avoid getting the stobjs-out of return-last. But ; return-last will eventually be rejected for mamoization anyhow (by ; memoize-table-chk). 'current-acl2-world wrld))) (cltl-def-from-name fn wrld))) (er hard 'memoize "The symbol ~x0 is not a known function symbol, and thus ~ it cannot be memoized." fn)) ; Certify-book seems to do things twice, so the following is commented out. ; ((cdr (assoc-eq fn (table-alist 'memoize-table wrld))) ; (er hard 'memoize "~x0 is already memoized." fn)) ((not (member-eq inline '(t nil))) (er hard 'memoize "The value ~x0 for inline is illegal (must be ~x1 or ~x2)." inline t nil)) ((not (member-eq trace '(t nil))) (er hard 'memoize "The value ~x0 for trace is illegal (must be ~x1 or ~x2)." trace t nil)) (t `(progn (defun ,condition-fn ,formals (declare (ignorable ,@formals) (xargs :guard ,(getprop fn 'guard *t* 'current-acl2-world wrld) :verify-guards nil ,@(let ((stobjs (remove nil stobjs-in))) (and stobjs `(:stobjs ,stobjs))))) ,condition) (verify-guards ,condition-fn ,@(and hints `(:hints ,hints)) ,@(and otf-flg `(:otf-flg ,otf-flg))) (table memoize-table ',fn (list* (cons :condition-fn ',condition-fn) (cons :inline ',inline) (cons :trace ',trace) (cons :commutative ',commutative) (cons :forget ',forget) (cons :memo-table-init-size (or ,memo-table-init-size *mht-default-size*)) (cons :aokp ',aokp) (and (not (eq ',ideal-okp :default)) (list (cons :ideal-okp ',ideal-okp))))) (value-triple ',fn))))))) (t `(progn (table memoize-table (deref-macro-name ,fn (macro-aliases world)) (list* (cons :condition-fn ,condition) ; t or nil (cons :inline ,inline) (cons :trace ,trace) (cons :commutative ,commutative) (cons :forget ,forget) (cons :memo-table-init-size (or ,memo-table-init-size *mht-default-size*)) (cons :aokp ',aokp) (and (not (eq ',ideal-okp :default)) (list (cons :ideal-okp ',ideal-okp))))) (value-triple (deref-macro-name ,fn (macro-aliases (w state))))))))) (defmacro memoize (fn &key (condition 't condition-p) condition-fn hints otf-flg (recursive 't) trace commutative forget memo-table-init-size aokp (ideal-okp ':default) (verbose 't)) ; WARNING: If you add a new argument here, consider making corresponding ; modifications to memoize-form, table-cltl-cmd, maybe-push-undo-stack, and ; add-trip. (These were the functions modified when adding the FORGET ; argument; the process was to see how the COMMUTATIVE argument was handled.) ; If condition and condition-fn are both non-nil, then the intent is ; that we do exactly what we would normally do for condition except ; that we use the name condition-fn. ; Parallelism blemish: when waterfall parallelism is enabled (detected by ; seeing whether ACL2 global 'waterfall-parallelism is non-nil), memoize and ; unmemoize should be changed to modify the 'saved-memoize-table instead of ; 'memoize-table. ":Doc-Section Events turn on memoization for a specified function~/ This ~il[documentation] topic relates to an experimental extension of ACL2 under development by Bob Boyer and Warren Hunt. ~l[hons-and-memoization] for a general discussion of memoization and the related features of hash consing and applicative hash tables. ~bv[] Examples: (memoize 'foo) ; remember the values of calls ; of foo (memoize 'foo :condition t) ; same as above (memoize 'foo :condition '(test x)) ; memoize for args satisfying ; the given condition (memoize 'foo :condition-fn 'test) ; memoize for args satisfying ; a call of the given function (memoize 'foo :recursive nil) ; don't memoize recursive calls (memoize 'foo :aokp t) ; attachments OK for stored results (memoize 'foo :ideal-okp t) ; memoize even if foo is in :logic mode ; but has not been guard-verified~/ General Form: (memoize fn ; memoizes fn and returns fn :condition condition ; optional (default t) :condition-fn condition-fn ; optional :hints hints ; optional, for verifying the ; guards of condition-fn :otf-flg otf-flg ; optional, for verifying the ; guards of condition-fn :recursive t/nil ; optional (default t) :commutative t/lemma-name ; optional (default nil) :forget t/nil ; optional (default nil) :memo-table-init-size size ; optional (default *mht-default-size*) :aokp t/nil ; optional (default nil) :ideal-okp t/:warn/nil ; optional (default nil) :verbose t/nil ; optional (default t) ) ~ev[] where ~c[fn] evaluates to a user-defined function symbol; ~c[condition] is either ~c[t] (the default), ~c['t], ~c[nil], or ~c['nil], or else evaluates to an expression whose free variables are among the formal parameters of ~c[fn]; and ~c[condition-fn] is either ~c[nil] (the default) or else evaluates to a legal function symbol. Further restrictions and options are discussed below. Note that all arguments are evaluated (but for the special handling of value ~c[t] for ~c[:commutative], the argument must literally be ~c[t]; see below). Generally ~c[fn] must evaluate to a defined function symbol. However, this value can be the name of a macro that is associated with such a function symbol; ~pl[macro-aliases-table]. That associated function symbol is the one called ``memoized'' in the discussion below, but we make no more mention of this subtlety. In the most common case, ~c[memoize] takes a single argument, which evaluates to a function symbol. We call this function symbol the ``memoized function'' because ``memos'' are saved and re-used, in the following sense. When a call of the memoized function is evaluated, the result is ``memoized'' by associating the call's arguments with that result, in a suitable table. But first an attempt is made to avoid such evaluation, by doing a lookup in that table on the given arguments for the result, as stored for a previous call on those arguments. If such a result is found, then it is returned without further computation. This paragraph also applies if ~c[:condition] is supplied but is ~c[t] or ~c['t]. If keyword argument ~c[:condition-fn] is supplied, but ~c[:condition] is not, then the result of evaluating ~c[:condition-fn] must be a defined function symbol whose formal parameter list and ~ilc[guard] are the same as for the function being memoized. If ~c[fn] is in ~c[:]~ilc[logic] mode, then ~il[guard]s must have been verified for ~c[:condition-fn]. Such a ``condition function'' will be run whenever the memoized function is called, on the same parameters, and the lookup or table store described above are only performed if the result from the condition function call is non-~c[nil]. Suppose however that ~c[:condition] is supplied. If the value supplied is ~c[t] or ~c['t], then the lookup and table store described above are always done. If the value is ~c[nil] or ~c['nil], then this lookup and table store are never done, although statistics may be gathered; ~pl[profile]. Now consider other values for ~c[:condition]. An attempt will be made to define a condition function whose ~il[guard] and formal parameters list are the same as those of the memoized function, and whose body is the result, ~c[r], of evaluating the given ~c[condition]. The name of that condition function is the result of evaluating ~c[:condition-fn] if supplied, else is the result of concatenating the string ~c[\"-MEMOIZE-CONDITION\"] to the end of the name of the memoized function. The condition function will be defined with ~il[guard] verification turned off, but that definition will be followed immediately by a ~ilc[verify-guards] event; and this is where the optional ~c[:hints] and ~c[:otf-flg] are attached. At evaluation time the condition function is used as described in the preceding paragraph; so in effect, the condition (~c[r], above) is evaluated, with its variables bound to the corresponding actuals of the memoized function call, and the memoized function attempts a lookup or table store if and only if the result of that evaluation is non-~c[nil]. Note that ~c[fn] can be either a ~c[:]~ilc[logic] mode function or a ~c[:]~ilc[program] mode function. However, only the corresponding raw Lisp function is actually memoized, so ~il[guard] violations can defeat memoization, and ~c[:]~ilc[logic] mode functions without their ~il[guard]s verified will only be memoized when called by ~c[:]~ilc[program] mode functions. (~l[guards-and-evaluation] for more information about guards and evaluation in ACL2.) If ~c[fn] is a ~c[:]~ilc[logic] mode function and ~c[:condition] is supplied and not ~c[t] or ~c[nil], then the condition must be a ~il[guard]-verified function. Calls of this macro generate events of the form ~c[(table memoize-table fn ((:condition-fn fn) ...))]. When successful, the returned value is of the form ~c[(mv nil function-symbol state)]. Suppose that a function is already memoized. Then it is illegal to memoize that function. Moreover, if the function was memoized with an associated condition (i.e., was memoized with keyword ~c[:condition] or ~c[:condition-fn] having value other than ~c[t] or ~c[nil]), then it is also illegal to convert the function from ~c[:]~ilc[program] to ~c[:]~ilc[logic] mode (~pl[verify-termination]). To turn off memoization, ~pl[unmemoize]. ~c[Memoize] is illegal for a function if its arguments include ~c[state] or if it returns any ~il[stobj]s. Also, ~c[memoize] never allows attachments to be used (~pl[defattach]); if an attachment is used during evaluation, then the evaluation result will not be stored. We conclude with by documenting keyword parameters not discussed above. Keyword parameter ~c[:recursive] is ~c[t] by default, which means that recursive calls of ~c[fn] will be memoized just as ``top-level'' calls of ~c[fn]. When ~c[:recursive] is instead set to ~c[nil], memoization is only done at the top level. Using ~c[:recursive nil] is similar to writing a wrapper function that just calls ~c[fn], and memoizing the wrapper instead of ~c[fn]. If ~c[:trace] has a non-~c[nil] value, then ~c[memoize] also traces in a traditional Lisp style. If ~c[:trace] has value ~c[notinline] or ~c[notinline], then a corresponding declaration is added at the beginning of the new definition of ~c[fn]. A non-~c[nil] value for ~c[:commutative] can be supplied if ~c[fn] is a binary function in ~c[:logic] mode. If the ~c[memoize] event is successful, then subsequently: whenever each argument to ~c[fn] is either a rational number or a ~il[hons], then when the evaluation of ~c[fn] on those arguments is memoized, the evaluation of ~c[fn] on the swap of those arguments is, in essence, also memoized. If ~c[:commutative] is supplied and is not ~c[nil] or ~c[t], then it should be the name of a previously-proved theorem whose formula states the commutativity of ~c[fn], i.e., is the formula ~c[(equal (fn x y) (fn y x))] for a pair ~c[{x,y}] of distinct variables. If ~c[:commutative] is ~c[t] ~-[] but not merely an expression that evaluates to ~c[t] ~-[] then an attempt to prove such a lemma will be made on-the-fly. The name of the lemma is the symbol in the same package as ~c[fn], obtained by adding the suffix ~c[\"-COMMUTATIVE\"] to the ~ilc[symbol-name] of ~c[fn]. If the proof attempt fails, then you may want first to prove the lemma yourself with appropriate hints and perhaps supporting lemmas, and then supply the name of that lemma as the value of ~c[:commutative]. If ~c[:commutative] is supplied, and a non-commutative condition is provided by ~c[:condition] or ~c[:condition-fn], then although the results will be correct, the extra memoization afforded by ~c[:commutative] is unspecified. If ~c[:memo-table-init-size] is supplied, then it should be a positive integer specifying the initial size of an associated hash table. Argument ~c[:aokp] is relevant only when attachments are used; ~pl[defattach] for background on attachments. When ~c[:aokp] is ~c[nil], the default, computed values are not stored when an attachment was used, or even when an attachment may have been used because a function was called that had been memoized using ~c[:aokp t]. Otherwise, computed values are always stored, but saved values are not used except when attachments are allowed. To summarize: ~bf[] aokp=nil (default): ``Pure'', i.e., values do not depend on attachments - Fetch: always legal - Store: only store resulting value when attachments were not used aokp=t: ``Impure'', i.e., values may depend on attachments - Fetch: only legal when attachments are allowed (e.g., not during proofs) - Store: always legal ~ef[] If ~c[:ideal-okp] is supplied and not ~c[nil], then it is permitted to memoize an ``ideal-mode'' function: one in ~c[:]~ilc[logic] mode whose ~il[guard]s have not been verified. In general, it is ill-advised to memoize an ideal-mode function, because its calls are typically evaluated ``in the logic'' without calling a memoized ``raw Lisp'' version of the function. However, if the function is called by a ~c[:]~ilc[program] mode function, evaluation can transfer to raw Lisp before reaching the call of the memoized function, in which case memoization will take place. For such situations you can provide value ~c[:warn] or ~c[t] for keyword parameter ~c[:ideal-okp]. Both of these values allow memoization of ideal-mode functions, but if ~c[:warn] is supplied then a warning will take place. Note that you may set the key ~c[:memoize-ideal-okp] of the ~ilc[acl2-defaults-table] to value ~c[t] or ~c[:warn] to change the default, but if parameter ~c[:ideal-okp] is supplied, the ~ilc[acl2-defaults-table] value is ignored. If ~c[:verbose] is supplied, it should either be ~c[nil], which will inhibit proof, event, and summary output (~pl[with-output]), or else ~c[t] (the default), which does not inhibit output. If the output baffles you, try ~bv[] :trans1 (memoize ...) ~ev[] to see the single-step macroexpansion of your ~c[memoize] call. The default for ~c[:forget] is ~c[nil]. If ~c[:forget] is supplied, and not ~c[nil], then it must be ~c[t], which causes all memoization done for a top-level call of ~c[fn] to be forgotten when that top-level call exits.~/ :cite hons-and-memoization :cited-by hons-and-memoization" (declare (xargs :guard (booleanp recursive)) (ignorable condition-p condition condition-fn hints otf-flg recursive trace commutative forget memo-table-init-size aokp ideal-okp verbose)) #-acl2-loop-only `(progn (when (eql *ld-level* 0) ; We are not inside the ACL2 loop (hence not in certify-book, for ; example). (let ((state *the-live-state*)) (warning$ 'memoize nil "No change for function ~x0: Memoization ~ requests are ignored in raw Lisp. In raw ~ Lisp, use memoize-fn." ',fn))) (value-triple nil)) #+acl2-loop-only (let* ((inline recursive) (form (cond ((eq commutative t) `(make-event (let* ((fn ,fn) (commutative (intern-in-package-of-symbol (concatenate 'string (symbol-name fn) "-COMMUTATIVE") fn))) (list ; use encapsulate so that each form is printed first 'encapsulate () (list 'defthm commutative (list 'equal (list fn 'x 'y) (list fn 'y 'x)) :rule-classes nil) (memoize-form (kwote fn) ',condition ',condition-p ',condition-fn ',hints ',otf-flg ',inline ',trace (kwote commutative) ',forget ',memo-table-init-size ',aokp ',ideal-okp))))) (t (memoize-form fn condition condition-p condition-fn hints otf-flg inline trace commutative forget memo-table-init-size aokp ideal-okp))))) (cond (verbose form) (t `(with-output :off (summary prove event) :gag-mode nil ,form))))) (defmacro unmemoize (fn) ":Doc-Section Events turn off memoization for the specified function~/ ~bv[] Example: (unmemoize 'foo) ; turn off memoization of foo~/ General Form: (unmemoize fn) ~ev[] where ~c[fn] evaluates to a function symbol that is currently memoized; ~pl[memoize]. An exception is that as with ~ilc[memoize], ~c[fn] may evaluate to the name of a macro that is associated with such a function symbol; ~pl[macro-aliases-table]. Calls of this macro generate events of the form ~c[(table memoize-table fn nil)]. When successful, the returned value is of the form ~c[(mv nil function-symbol state)]. To remove the effects of all ~ilc[memoize] ~il[events], evaluate: ~c[(clear-memo-table)]. To save and restore memoization, ~pl[save-and-clear-memoization-settings] and ~pl[restore-memoization-settings].~/ :cite hons-and-memoization :cited-by hons-and-memoization" (declare (xargs :guard t)) #-acl2-loop-only `(progn (when (eql *ld-level* 0) ; We are not inside the ACL2 loop (hence not in certify-book, for ; example). (warning$ 'unmemoize nil "No change for function ~x0: Unmemoization requests are ~ ignored in raw Lisp. In raw Lisp, use unmemoize-fn." ',fn)) (value-triple nil)) #+acl2-loop-only `(with-output :off summary (progn (table memoize-table (deref-macro-name ,fn (macro-aliases world)) nil) (value-triple (deref-macro-name ,fn (macro-aliases (w state))))))) (defmacro profile (fn &rest r) ":Doc-Section Events turn on profiling for one function~/ NOTE: An alternative to this utility, which has a lot less functionality but doesn't depend on the experimental extension of ACL2 mentioned just below, may be found in community book ~c[books/misc/profiling.lisp]. This documentation topic relates to an experimental extension of ACL2 under development by Bob Boyer and Warren Hunt. ~l[hons-and-memoization] for a general discussion of memoization, on which this ~c[profile] utility is built, and the related features of hash consing and applicative hash tables. ~c[Profile] can be useful in Common Lisp debugging and performance analysis, including examining the behavior of ACL2 functions. ~bv[] Example: (profile 'fn) ; keep count of the calls of fn (profile 'fn ; as above, with with some memoize options :trace t :forget t) (memsum) ; report statistics on calls of memoized functions (e.g., fn) (clear-memoize-statistics) ; clear memoization (and profiling) statistics ~ev[] Evaluation of ~c[(profile 'fn)] redefines ~c[fn] so that a count will be kept of the calls of FN. The information recorded may be displayed, for example, by invoking ~c[(]~ilc[memoize-summary]~c[)] or (equivalently) ~c[(memsum)]. If you wish to gather fresh statistics for the evaluation of a top-level form, ~pl[clear-memoize-statistics]. ~c[Profile] is just a macro that calls ~ilc[memoize] to do its work. ~c[Profile] gives the two keyword parameters ~c[:condition] and ~c[:recursive] of ~ilc[memoize] the value ~c[nil]. Other keyword parameters for ~c[memoize], which must not include ~c[:condition], ~c[:condition-fn], or ~c[:recursive], are passed through. To eliminate profiling, use ~ilc[unmemoize]; for example, to eliminate profiling for function ~c[fn], evaluate ~c[(unmemoize 'fn)]. You may find raw Lisp functions ~c[profile-all] and ~c[profile-acl2] to be useful. Please contact the ACL2 developers if you want versions of these functions to be executable from inside the ACL2 read-eval-print loop.~/~/" (declare (xargs :guard (and (keyword-value-listp r) (not (assoc-keyword :condition r)) (not (assoc-keyword :condition-fn r)) (not (assoc-keyword :recursive r))))) `(memoize ,fn :condition nil :recursive nil ,@r)) #-hons (defmacro memoize-on-raw (fn form) (declare (ignore fn)) form) (defmacro memoize-on (fn form) ; MEMOIZE-ON evaluates form. During the evaluation the symbol fn has as ; its symbol-function what it had immediately AFTER the memoization of ; fn. Hence, the values of calls of fn may be remembered during the ; evaluation and later. Warning: to use MEMOIZE-ON, fn must already ; be memoized. `(return-last 'memoize-on-raw ,fn ,form)) #-hons (defmacro memoize-off-raw (fn form) (declare (ignore fn)) form) (defmacro memoize-off (fn form) ; MEMOIZE-OFF evaluates form. During the evaluation the symbol fn has as ; its symbol-function what it had immediately BEFORE the memoization ; of fn. Hence the values of calls of fn may not be remembered during ; the evaluation. Warning: to use MEMOIZE-OFF, fn must already be ; memoized." `(return-last 'memoize-off-raw ,fn ,form)) (defmacro memoizedp-world (fn wrld) `(let ((fn ,fn) (wrld ,wrld)) (cond ((not (global-val 'hons-enabled wrld)) (er hard 'memoizedp "Memoizedp cannot be called in this ACL2 image, as it requires a ~ hons-aware ACL2. See :DOC hons-and-memoization.")) (t (cdr (assoc-eq fn (table-alist 'memoize-table wrld))))))) (defmacro memoizedp (fn) (declare (xargs :guard t)) `(memoizedp-world ,fn (w state))) ;;; hons-shrink-alist ; HONS-SHRINK-ALIST, when called with an atomic second ; argument, produces an alist that is alist-equivalent ; to the first argument, but with all irrelevant entries in ; the first argument deleted. Informal remark: the alist ; returned is a hons when the initial ANS is not an atom. ; Comment about the last clause above. Or really? ; Counterexamples? ; ; mbu> stp ; ? (honsp (hons-shrink-alist '((a . b) (a . b2)) (hons-acons 1 2 3))) ; NIL ; ; mbu> stp ; ? (honsp (hons-shrink-alist '((a . b) (a . b2)) nil)) ; NIL ; ? ; Some centaur/ books put entries in *never-profile-ht*. In order to allow ; those books to certify in vanilla ACL2, we define a default value for that ; variable here. #+(and (not hons) (not acl2-loop-only)) (defparameter *never-profile-ht* (make-hash-table :test 'eq)) #+(or acl2-loop-only (not hons)) (defun never-memoize-fn (fn) (declare (xargs :guard (symbolp fn)) (ignore fn)) nil) (defmacro never-memoize (fn) ":Doc-Section Hons-and-Memoization Mark a function as unsafe to memoize.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; ~pl[hons-and-memoization]. Logically, this function just returns ~c[nil]. But execution of ~c[(never-memoize fn)] records that ~c[fn] must never be memoized, so that any attempt to memoize ~c[fn] will fail. Any function can be marked as unsafe to memoize; in fact, ~c[fn] need not even be defined at the time it is marked. This is useful for prohibiting the memoization of functions that are known to involve destructive functions like ~c[nreverse].~/~/" (declare (xargs :guard (symbolp fn))) `(make-event (prog2$ (never-memoize-fn ',fn) '(value-triple :invisible)) :check-expansion t)) acl2-sources/memoize-raw.lisp0000664002132200015000000047703112222115527015713 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; memoize-raw.lisp -- Raw lisp definitions for memoization functions, only to ; be included in the experimental HONS version of ACL2. ; The original version of this file was contributed by Bob Boyer and ; Warren A. Hunt, Jr. The design of this system of Hash CONS, ; function memoization, and fast association lists (applicative hash ; tables) was initially implemented by Boyer and Hunt. (in-package "ACL2") (eval-when (:execute :compile-toplevel :load-toplevel) #-hons ;; [Jared]: Is there a real reason that memoization needs hons? (error "memoize-raw.lisp should only be included when #+hons is set.") ;; [Jared]: MEMOIZE IS NOT THREAD SAFE. I am getting rid of all of the locking ;; stuff that was here before. It was just muddying the waters and making ;; things harder to understand. We'll need to think hard and do it right, ;; later. ;; One may comment out the following PUSHNEW and rebuild to get profiling ;; times not based upon the curious and wondrous RDTSC instruction of some x86 ;; processors. On a machine with several cores, the RDTSC values returned by ;; different cores may be slightly different, which could lead one into such ;; nonsense as instructions apparently executing in negative time, when timing ;; starts on one core and finishes on another. To some extent we ignore such ;; RDTSC nonsense, but we still can report mysterious results since we have no ;; clue about which core we are running on in CCL. #+Clozure (when (fboundp 'ccl::rdtsc) (pushnew :RDTSC *features*)) ) ; MFIXNUM --------------------------------------------------------------------- ; ; We use the type mfixnum for counting things that are best counted in the ; trillions or more. Mfixnums happen to coincide with regular fixnums on ; 64-bit CCL and SBCL. ; ; [Jared]: this seems fine, probably a good idea. (defconstant most-positive-mfixnum (1- (expt 2 60))) (deftype mfixnum () `(integer ,(- -1 most-positive-mfixnum) ,most-positive-mfixnum)) ; OUR-SYNTAX ----------------------------------------------------------------- ; ; [Jared]: This is just some printing stuff. I don't think I need/want any of ; this in the new memoize library. But there's nothing really wrong with it. ; A big part of the complexity here was probably due to supporting the old ; compact-print/compact-read mechanism, but we just don't need that anymore ; with serialize. (defmacro our-syntax (&rest args) "OUR-SYNTAX is similar to Common Lisp's WITH-STANDARD-IO-SYNTAX. The settings in OUR-SYNTAX are oriented towards reliable, standard, vanilla, mechanical reading and printing, and less towards human readability. Please, before changing the following, consider existing uses of this macro insofar as the changes might impact reliable, standard, vanilla, mechanical printing. Especially consider COMPACT-PRINT-FILE. Consider using OUR-SYNTAX-NICE." ; We use the *ACL2-PACKAGE* and the *ACL2-READTABLE* because we use ; them almost all the time in our code. `(with-standard-io-syntax ; Note for GCL: ; As of late May 2013, with-standard-io-syntax seems to work properly in ANSI ; GCL. If necessary one could use our-with-standard-io-syntax here, but better ; would be to use an up-to-date ANSI GCL. (let ((*package* *acl2-package*) (*readtable* *acl2-readtable*)) ,@args))) (defmacro our-syntax-nice (&rest args) ;; OUR-SYNTAX-NICE offers slightly more pleasant human readabilty. `(our-syntax (setq *print-case* :downcase) (setq *print-pretty* t) (setq *print-readably* nil) (setq *print-right-margin* 70) (setq *print-miser-width* 100) ,@args)) (defmacro our-syntax-brief (&rest args) ;; Within OUR-SYNTAX-BRIEF printing may be greatly abbreviated. `(our-syntax-nice (setq *print-length* 10) (setq *print-level* 5) (setq *print-lines* 10) ,@args)) (defmacro ofn (&rest r) ; For forming strings. `(our-syntax (format nil ,@r))) (defun-one-output ofnum (n) ; For forming numbers. (check-type n number) (if (= n 0) (setq n 0)) (our-syntax (cond ((typep n '(integer -99 999)) (format nil "~8d" n)) ((or (< -1000 n -1/100) (< 1/100 n 1000)) (format nil "~8,2f" n)) (t (format nil "~8,2e" n))))) (defmacro ofni (&rest r) ; For forming symbols in package ACL2. `(our-syntax (intern (format nil ,@r) *acl2-package*))) (defmacro ofnm (&rest r) ; For forming uninterned symbols. `(our-syntax (make-symbol (format nil ,@r)))) (defmacro oft (&rest r) ; For writing to *standard-output*. `(progn (format t ,@r) (force-output *standard-output*))) (defmacro oftr (&rest r) ; For writing to *trace-output*. `(progn (format *trace-output* ,@r) (force-output *trace-output*))) ; Number of arguments --------------------------------------------------------- ; ; [Jared]: dealt with this in books/memoize/numargs (defv *number-of-arguments-and-values-ht* (let ((ht (make-hash-table))) (declare (hash-table ht)) (loop for pair in '((bad-lisp-objectp . (1 . 1)) (apropos . (nil . 0)) (aref . (nil . 1)) (array-displacement . (1 . 2)) (decode-float . (1 . 3)) (expansion-alist-pkg-names-memoize . (1 . 1)) (fchecksum-obj . (1 . 1)) (worse-than . (2 . 1)) (find-symbol . (nil . 2)) (function . (nil . 1)) (get-properties . (2 . 3)) (gethash . (nil . 2)) (integer-decode-float (1 . 3)) (intern . (nil . 2)) (lambda . (nil . 1)) (list . (nil . 1)) (list* . (nil . 1)) (macroexpand . (nil . 2)) (macroexpand-1 . (nil . 2)) (pprint-dispatch . (nil . 2)) (prog1 . (nil . 1)) (prog2 . (nil . 1)) (quote . (1 . 1))) do (setf (gethash (car pair) ht) (cdr pair))) (loop for sym in '(car cdr caar cadr cdar cddr caaar cadar cdaar cddar caadr caddr cdadr cdddr caaaar cadaar cdaaar cddaar caadar caddar cdadar cdddar caaadr cadadr cdaadr cddadr caaddr cadddr cdaddr cddddr) do (setf (gethash sym ht) '(1 . 1))) ht) "The hash table *NUMBER-OF-ARGUMENTS-AND-VALUES-HT* maps a symbol fn to a cons pair (a . d), where a is the number of inputs and d is the number of outputs of fn. NIL for a or d indicates 'don't know'.") (declaim (hash-table *number-of-arguments-and-values-ht*)) (defun-one-output input-output-number-error (fn) ;; [Jared]: bozo this is horrible, we should provide an sane interface ;; to this instead of just exposing it... (format t "What is the number of inputs and output of ~a, please? ~%; To ~ assert that ~a takes, say, 2 inputs and returns 1 output, do ~% ~ (setf (gethash '~a acl2::*number-of-arguments-and-values-ht*) ~ (cons 2 1))." fn fn fn) (error "input-output-number-error: **: ~a" fn)) (defun-one-output number-of-arguments (fn) ;; A NIL value returned by NUMBER-OF-ARGUMENTS means 'don't know'. (let* ((state *the-live-state*) (w (w state)) (pair (gethash fn *number-of-arguments-and-values-ht*))) (cond ((not (symbolp fn)) nil) ((and (consp pair) (integerp (car pair))) (car pair)) ((let ((formals (getprop fn 'formals t 'current-acl2-world w))) (and (not (eq t formals)) (length formals)))) ((not (fboundp fn)) nil) ((macro-function fn) nil) ((special-operator-p fn) nil) #+Clozure ((multiple-value-bind (req opt restp keys) (ccl::function-args (symbol-function fn)) (and (null restp) (null keys) (integerp req) (eql opt 0) req))) (t nil)))) (defun-one-output number-of-return-values (fn) ;; A NIL value returned by NUMBER-OF-RETURN-VALUES means 'don't know'. (let* ((pair (gethash fn *number-of-arguments-and-values-ht*)) (state *the-live-state*) (w (w state))) (cond ((not (symbolp fn)) nil) ((and (consp pair) (integerp (cdr pair))) (cdr pair)) ((member fn '(let let* mv-let progn if return-last)) (error "It is curious to ask about 'the' number of return values of ~a ~ because the answer is that it depends." fn)) ((not (eq t (getprop fn 'formals t 'current-acl2-world w))) (len (stobjs-out fn w)))))) ; Timing Utilities ------------------------------------------------------------ ; ; [Jared]: dealt with this in books/memoize/timer.lsp (defg *float-ticks/second* 1.0) (defg *float-internal-time-units-per-second* (float internal-time-units-per-second)) (declaim (float *float-ticks/second* *float-internal-time-units-per-second*)) (defabbrev internal-real-time () #+(and RDTSC (not 32-bit-target)) ; faster for 64 (the mfixnum (ccl::rdtsc)) #+(and RDTSC 32-bit-target) ; slower for 32 (the mfixnum (mod (ccl::rdtsc64) most-positive-mfixnum)) #-RDTSC (the mfixnum (mod (get-internal-real-time) most-positive-fixnum))) (defun-one-output float-ticks/second-init () (setq *float-ticks/second* #+RDTSC (let ((i1 (ccl::rdtsc64)) (i2 (progn (sleep .01) (ccl::rdtsc64)))) (if (>= i2 i1) (* 100 (float (- i2 i1))) (error "(float-ticks/second-init)."))) #-RDTSC *float-internal-time-units-per-second*) (check-type *float-ticks/second* (and float (satisfies plusp)))) ; SAFE-INCF ------------------------------------------------------------------ ; ; [Jared]: this is horrible and is bombing out on us in some big scripts. we ; probably would rather wrap around for performance counting than cause an ; error, or, can we maybe even just use bignums here? (defun-one-output safe-incf-aux-error (x inc where) (error "~%; SAFE-INCF-AUX: ** Error: ~a." (list :x x :inc inc :where where))) (defmacro safe-incf-aux (x inc where) (cond ((not (or (symbolp inc) (and (< inc most-positive-mfixnum) (> inc 0)))) (safe-incf-aux-error x inc where)) ((and (true-listp x) (equal (len x) 3) (member (car x) '(aref svref)) (symbolp (nth 1 x)) (consp (nth 2 x))) (let ((idx (make-symbol "IDX"))) `(let ((,idx (the fixnum ,(nth 2 x)))) (declare (type fixnum ,idx)) (safe-incf (,(nth 0 x) ,(nth 1 x) ,idx) ,inc ',where)))) (t (let ((v (make-symbol "V"))) `(let ((,v (the mfixnum ,x))) (declare (type mfixnum ,v)) (cond ((<= ,v (the mfixnum (- most-positive-mfixnum (the mfixnum ,inc)))) (setf (the mfixnum ,x) (the mfixnum (+ ,v (the mfixnum ,inc)))) nil) (t (safe-incf-aux-error ',x ',inc ',where)))))))) (defmacro safe-incf (x inc &optional where) "SAFE-INCF is a raw Lisp macro that behaves the same as INCF when both X and INC are nonnegative MFIXNUMs and their sum is a nonnegative MFIXNUM. In a call of (SAFE-INCF x inc), X must be a place that holds an MFIXNUM. INC must evaluate to an MFIXNUM. Both X and INC must evaluate without side effects, so that it is impossible to tell which was executed first or whether only one or both were executed. If INC is not positive, no update takes place at all. Otherwise, if the sum of the values of X and INC is not an MFIXNUM, which is tested without causing an error, a run-time error will be caused. Else, if the sum is an MFIXNUM then, as with INCF, the place X will be set to hold the sum of the old value of that place and the value of INC. The value returned by SAFE-INCF is NIL. Caution: INC may be evaluated first, which is why side effects are prohibited. An optional third parameter is merely to help with error location identification. In (SAFE-INCF (AREF A (FOO)) INC), (FOO) is only evaluted once. Same for SVREF." (cond ((integerp inc) (if (<= inc 0) nil `(safe-incf-aux ,x ,inc ,where))) ((symbolp inc) `(if (>= 0 (the mfixnum ,inc)) nil (safe-incf-aux ,x ,inc ,where))) (t (let ((incv (make-symbol "INCV"))) `(let ((,incv (the mfixnum ,inc))) (declare (type mfixnum ,incv)) (if (>= 0 ,incv) nil (safe-incf-aux ,x ,incv ,where))))))) ; PONSING --------------------------------------------------------------------- ; ; [Jared]: dealt with this in books/memoize/pons.lsp (defparameter *count-pons-calls* t "If *COUNT-PONS-CALLS*, then each call of PONS increments *PONS-CALL-COUNTER* by 1, and each call of PONS that does not find the desired PONS to already exist increments *PONS-MISSES-COUNTER* by 1.") (defg *pons-call-counter* 0) (defg *pons-misses-counter* 0) (declaim (type mfixnum *pons-call-counter*)) (declaim (type mfixnum *pons-misses-counter*)) (defmacro maybe-count-pons-calls () (and *count-pons-calls* '(safe-incf *pons-call-counter* 1 maybe-count-pons-calls))) (defmacro maybe-count-pons-misses () (and *count-pons-calls* '(safe-incf *pons-misses-counter* 1 maybe-count-pons-misses))) (defun-one-output assoc-no-error-at-end (x l) ;; We assume that every element of L is CONSP. (if (typep x '(or cons symbol (and array string))) (loop (if (consp l) (let ((al (car l))) (if (eq x (car al)) (return al) (setq l (cdr l)))) (return nil))) (loop (if (consp l) (let ((al (car l))) (if (eql x (car al)) (return al) (setq l (cdr l)))) (return nil))))) (defun-one-output too-long (x n) (declare (type fixnum n)) ;; (TOO-LONG x n) == (> (LENGTH x) n) provided x a noncircular list and ;; n is a nonnegative fixnum. TOO-LONG is perhaps faster than LENGTH ;; because (a) LENGTH has to worry about its argument being a circular ;; list, (b) LENGTH may worry about the answer exceeding ;; MOST-POSITIVE-FIXNUM, and (c) LENGTH must consider the possibility ;; that its argument is a vector. (loop (cond ((atom x) (return nil)) ((eql n 0) (return t)) (t (setq x (cdr x)) (setq n (the fixnum (1- n))))))) (defconstant atom-case-fudge (+ 129 (expt 2 25))) (defconstant most-positive-fudge (1- (expt 2 24))) (defconstant most-negative-fudge (- (expt 2 24))) (defconstant -most-negative-fudge (- most-negative-fudge)) #+Clozure (defun-one-output atom-case (s) (cond ((symbolp s) (cond ((eq s nil) 0) ((eq s t) 1) (t (let ((v (get (the symbol s) 'hons-hash-key))) (cond ((null v) (let ((c (ccl::static-cons s nil))) (setq v (+ atom-case-fudge (the fixnum (ccl::%staticp c)))) (setf (get (the symbol s) 'hons-hash-key) c) (rplacd (the cons c) v) v)) (t (cdr (the cons v)))))))) ((and (typep s 'fixnum) (> (the fixnum s) most-negative-fudge) (<= (the fixnum s) most-positive-fudge)) (the fixnum (+ -most-negative-fudge (the fixnum s)))))) (defmacro sqmpf () (isqrt most-positive-fixnum)) (defmacro hmnf () ; Half MOST-NEGATIVE-FIXNUM. (ceiling (/ most-negative-fixnum 2))) (defmacro static-hons-shift () (ceiling (/ (integer-length most-positive-fixnum) 2))) #+Clozure (defun-one-output addr-for (x y) (let ((idx (let ((n (ccl::%staticp x))) (cond (n (+ atom-case-fudge (the fixnum n))) (t (atom-case x))))) (large-case nil)) (cond (idx (cond ((and (typep idx 'fixnum) (< (the fixnum idx) (sqmpf)) nil)) (t (setq large-case t)))) (t (return-from addr-for nil))) (let ((idy (let ((n (ccl::%staticp y))) (cond (n (+ atom-case-fudge (the fixnum n))) (t (atom-case y)))))) (cond (idy (cond ((and (typep idy 'fixnum) (< (the fixnum idy) (sqmpf))) nil) (t (setq large-case t)))) (t (return-from addr-for nil))) ; ADDR-FOR is 1-1, in a sense, for a two argument function, when not ; NIL. That is, for all ACL2 objects x1, x2, y1, and y1, if (addr-for ; x1 y1) is not NIL and is equal to (addr-for x2 y2), then x1 is equal ; to x2 and y1 is equal to y2. ; Here is a sketch of a proof that if mpf = 2^60-1 and mnf = -2^60, ; then the ranges of large-case and the non-large case of ADDR-FOR do ; not intersect. In the large case, one of idx or idy, must be >= ; 2^30, so (+ (/ (* (idx+idy+1) (idx+idy)) 2) idy) > 2^59. Adding in ; -2^59 means that the large result will be positive. In the non-large ; case, the result of the logior will be <= 2^60-1, so the result of ; adding -2^60 will make the non-large result negative. (cond (large-case (let* ((z (+ idx idy)) (z1 (+ 1 z))) (if (oddp z) (setq z1 (ash z1 -1)) (setq z (ash z -1))) (+ idy (+ (hmnf) (* z z1))))) (t (+ (the fixnum (logior (the fixnum (ash (the fixnum idx) (static-hons-shift))) (the fixnum idy))) most-negative-fixnum)))))) ; This code has the 'feature' that if the condition causes an error, ; so will the memoized function. ; PONS differs from HONS in that it does not honsify its arguments and ; in that it takes a hash table as a third argument. We use PONS in ; memoization. ; We use PONS instead of HONS in memoization because we could not ; afford to honsify (using hons-shrink-alist!) certain alists in ; certain biology tests. About the same time, we (gratuitously) ; decided to stop hons'ifying the output of memoized functions. (defun-one-output pons (x y ht) (declare (hash-table ht)) ; A crucial fact is: ; (implies (equal (pons x y ht) (pons x' y' ht)) ; (and (equal x x') ; (equal y y')) ; Ignore the ht for the moment. Suppose that ; (equal (pons x (pons y z)) (pons x' (pons y' z'))). ; ; It follows then that x=x', y=y', and z=z'. (let ((xval nil) (yval nil) (ans nil)) ; We have taken string normalization out of pons because there might ; be a chance of confusing a 'normal' string with a stobj. ; If x1, ..., xn is pointwise EQL to y1, ..., yn, then are we sure ; that (pist* x1 ... xn) is EQ to (pist* y1 ... yn)? ; If CONS exists, then return it. Does CDR exist in hash table? #+Clozure (let ((addr (addr-for x y))) (when addr (return-from pons addr))) (maybe-count-pons-calls) (setq yval (gethash y (the hash-table ht))) ; Does CAR exist in hash table? (cond (yval (cond ((not (consp yval)) (setq xval (gethash x (the hash-table yval))) (cond (xval (setq ans xval)))) ((setq ans (assoc-no-error-at-end x yval)))))) (cond ; If PONS found, then return previous CONS from hash table. (ans) ; Otherwise, maybe create new CONS and install in hash table. (t (setq yval (gethash y ht)) (cond ((null yval) (setq ans (cons x y)) (setf (gethash y ht) (list ans)) ans) ((consp yval) (let ((ans (assoc-no-error-at-end x yval))) (cond (ans) (t (let ((ans (cons (cons x y) yval))) (maybe-count-pons-misses) (cond ;; Gary Byers recalls Lisp folklore that alists are faster ;; than hash tables up to length 18. ((too-long ans 18) (let ((tab (hl-mht))) (declare (hash-table tab)) (loop for pair in ans do (setf (gethash (car pair) tab) pair)) (setf (gethash y ht) tab) (car ans))) (t (setf (gethash y ht) ans) (car ans)))))))) (t (setq xval (gethash x (the hash-table yval))) (cond ((not xval) (maybe-count-pons-misses) (setf (gethash x (the hash-table yval)) (setq ans (cons x y)))) (t (setq ans xval))) ans)))))) (defmacro pist* (table &rest x) (cond ((atom x) x) ((atom (cdr x)) (car x)) (t (list 'pons (car x) (cons 'pist* (cons table (cdr x))) table)))) ; Identifying functions that are safe to memoize -------------------------- ; ; [Jared]: This is really gross and I don't think it's the right way to do it ; at all. We need to figure out something better. Maybe we should instead use ; some kind of marking scheme on the symbol to say never memoize this function. ; (Modified by Matt K. 8/26/13 to avoid a weird error in profile-all after ; (include-book "memoize/old/profile" :dir :system).) (defvar *never-memoize-ht* (let ((ht (make-hash-table :test 'eq))) (loop for x in '(bytes-used memoize-summary memoize-summary-after-compute-calls-and-times #+rdtsc ccl::rdtsc * + - < <= = > >= abort adjoin adjust-array allocate-instance append apply apropos apropos-list aref arrayp assoc assoc-if assoc-if-not atan atom bit bit-and bit-andc1 bit-andc2 bit-eqv bit-ior bit-nand bit-nor bit-not bit-orc1 bit-orc2 bit-xor break butlast car cdr ceiling cerror change-class char-equal char-greaterp char-lessp char-not-equal char-not-greaterp char-not-lessp char/= char< char<= char= char> char>= clear-input clear-memoize-tables clear-output compile compile-file compile-file-pathname compiler-macro-function complex compute-restarts concatenate continue copy-pprint-dispatch copy-readtable copy-symbol count count-if count-if-not decode-universal-time delete delete-duplicates delete-if delete-if-not describe digit-char digit-char-p directory dribble ed encode-universal-time enough-namestring ensure-directories-exist ensure-generic-function eq eql error eval every export fboundp fceiling ffloor file-position fill find find-class find-if find-if-not find-method find-restart find-symbol finish-output fixnum-to-symbol float float-sign floor force-output format fresh-line fround ftruncate funcall gensym gentemp get get-dispatch-macro-character get-internal-real-time get-internal-run-time get-macro-character get-properties get-setf-expansion getf gethash if import initialize-instance intern internal-real-time intersection invalid-method-error invoke-restart last ld-fn len len1 length lisp-implementation-type list list* listen load log macro-function macroexpand macroexpand-1 make-array make-broadcast-stream make-concatenated-stream make-condition make-dispatch-macro-character make-hash-table make-instance make-list make-load-form make-load-form-saving-slots make-package make-pathname make-random-state make-sequence make-string make-string-input-stream make-string-output-stream map map-into mapc mapcan mapcar mapcon mapl maplist max member member-if member-if-not memoize-call-array-grow memoize-eval-compile memoize-fn merge merge-pathnames method-combination-error mf-1st-warnings mf-2nd-warnings mf-warnings mismatch muffle-warning nbutlast nconc nintersection no-applicable-method no-next-method not notany notevery nset-difference nset-exclusive-or nstring-capitalize nstring-downcase nstring-upcase nsublis nsubst nsubst-if nsubst-if-not nsubstitute nsubstitute-if nsubstitute-if-not null nunion open pairlis parse-integer parse-namestring pathname-device pathname-directory pathname-host pathname-name pathname-type peek-char position position-if position-if-not pprint pprint-dispatch pprint-fill pprint-indent pprint-linear pprint-newline pprint-tab pprint-tabular prin1 princ princ-to-string print print-object profile-fn profile-acl2 profile-all random rassoc rassoc-if rassoc-if-not read read-byte read-char read-char-no-hang read-delimited-list read-from-string read-line read-preserving-whitespace read-sequence reduce reinitialize-instance remove remove-duplicates remove-if remove-if-not rename-file rename-package replace require reverse-strip-cars reverse-strip-cdrs room round sbit search set-difference set-dispatch-macro-character set-exclusive-or set-macro-character set-pprint-dispatch set-syntax-from-char shadow shadowing-import shared-initialize signal signum slot-missing some sort stable-sort store-value string-capitalize string-downcase string-equal string-greaterp string-lessp string-not-equal string-not-greaterp string-not-lessp string-upcase string/= string< string<= string= string> string>= stringp sublis subseq subsetp subst subst-if subst-if-not substitute substitute-if substitute-if-not subtypep svref symbol-to-fixnum symbol-to-fixnum-create symbolp sync-memoize-call-array terpri translate-logical-pathname translate-pathname tree-equal true-listp truncate typep unexport unintern union unread-char unuse-package update-instance-for-different-class update-instance-for-redefined-class upgraded-array-element-type upgraded-complex-part-type use-package use-value user-homedir-pathname values vector-push-extend warn wild-pathname-p write write-byte write-char write-line write-sequence write-string write-to-string y-or-n-p yes-or-no-p) do (setf (gethash x ht) t)) ht)) (defun never-memoize-fn (fn) (setf (gethash fn *never-memoize-ht*) t)) (defun never-memoize-p (fn) (if (gethash fn *never-memoize-ht*) t nil)) ; The following was inserted by Jared to keep his place; stuff above is "done" ; and stuff below is "todo". ; ------------------------------------------------------------------------- ; ------------------------------------------------------------------------- ; ZZ THE GREPPABLE LINE ZZ ; ------------------------------------------------------------------------- ; ------------------------------------------------------------------------- ; recording vars ; To minimize metering overhead costs, one may set these "*RECORD-" ; variables to NIL before memoizing. ; *RECORD-BYTES* and other *RECORD-...* variables are bound in ; REMEMOIZE-ALL, so we use DEFPARAMETER rather than DEFG. (defparameter *record-bytes* ;; BOZO why not do this in all ccls...? (and (member :Clozure *features*) (> most-positive-fixnum (expt 2 32))) "In 64-bit Clozure Common Lisp, if *RECORD-BYTES* is not NIL when a function is memoized, we keep track of heap bytes allocated during calls of that function.") (defparameter *record-calls* t "If *RECORD-CALLS* when a function is memoized, we count all calls of the function.") (defparameter *record-hits* t "If *RECORD-HITS* is not NIL when a function is memoized, we count the number of times that a previously computed answer is used again.") (defparameter *record-hons-calls* t "If *RECORD-HONS-CALLS* in not NIL a function is memoized, HONS calls are counted.") (defparameter *record-mht-calls* t "If *RECORD-HONS-CALLS* is not NIL at the time a function is memoized, we record the number of times that a memo hash-table for the function was is counted.") (defparameter *record-pons-calls* t "If *RECORD-PONS-CALLS* is not NIL at the time a function is memoized, pons calls are counted.") (defparameter *record-time* t "If *RECORD-TIME* is not NIL the time a function is memoized, we record the elapsed time for each outermost call of the function.") ; reporting vars (defv *report-bytes* #+Clozure t #-Clozure nil "If *REPORT-BYTES* is not NIL, then MEMOIZE-SUMMARY prints the number of bytes allocated on the heap.") (defv *report-calls* t "If *REPORT-CALLS* is not NIL, MEMOIZE-SUMMARY prints the number of calls.") (defv *report-calls-from* t "If *REPORT-CALLS-FROM* is not NIL, MEMOIZE-SUMMARY prints which functions called a function, how many times, and how long the calls took.") (defv *report-calls-to* t "If *REPORT-CALLS-TO* is not NIL, MEMOIZE-SUMMARY prints which functions were called by given function, how many times, and how long the calls took.") (defv *report-hits* t "If *REPORT-HITS* is not NIL, MEMOIZE-SUMMARY prints the number of times that a previously computed answer was reused.") (defv *report-hons-calls* t "If *REPORT-HONS-CALLS* is not NIL, then MEMOIZE-SUMMARY prints the number of times that hons was called.") (defv *report-mht-calls* t "If *REPORT-MHT-CALLS* is not NIL, then MEMOIZE-SUMMARY prints the number of times that a memo hash-table for the function was created. This may be of interest to those who memoize functions that deal in changing stobjs; the memoization machinery sometimes 'forgets' an entire memoization hash table out of an abundance of caution, and then may later need to create it afresh.") (defv *report-pons-calls* t "If *REPORT-PONS-CALLS* is not NIL, MEMOIZE-SUMMARY prints the number of calls of PONS.") (defv *report-time* t "If *REPORT-TIME* is not NIL, MEMOIZE-SUMMARY prints the total time used to compute the outermost calls.") (defv *report-on-memo-tables* t "If *REPORT-ON-MEMO-TABLES* is not NIL, MEMOIZE-SUMMARY prints information about memo tables.") (defv *report-on-pons-tables* t "If *REPORT-ON-PONS-TABLES* is not NIL, MEMOIZE-SUMMARY prints information about pons tables.") (defg *memoize-info-ht* ;; This has two mappings in one: ;; ;; 1. It maps each currently memoized function symbol, fn, to a ;; DEFREC record of type MEMO-INFO-HT-ENTRY. ;; ;; 2. It maps each NUM back to the corresponding symbol. (hl-mht)) (declaim (hash-table *memoize-info-ht*)) (defrec memoize-info-ht-entry ;; vaguely ordered by most frequently referenced first (ext-anc-attachments ;; see the Essay on Memoization with Attachments ;; start-time is a symbol whose val is the start time of the current, ;; outermost call of fn, or -1 if no call of fn is in progress. start-time num ;; an integer, unique to fn. (for stats?) tablename ;; a symbol whose value is the memoize table for fn. ponstablename ;; a symbol whose value is the pons table for fn condition ;; T or NIL. :condition arg as passed to memoize-fn inline ;; T or NIL. :inline arg as passed to memoize-fn memoized-fn ;; the new value of (symbol-function fn) old-fn ;; the old value of (symbol-function fn), or nil. fn ;; a symbol, the name of the function being memoized sts ;; the stobj memotable lists for fn trace ;; T or NIL. :trace arg as passed to memoize-fn before ;; form to evaluate first ;; the function body actually used, in the inline=t ;; case, as supplied (or as computed, if not supplied) cl-defun formals ;; as supplied (or as computed, if not supplied) commutative ;; asserts this is a binary commutative function specials ;; horrible hack for raw functions that read *specials* stobjs-in ;; as supplied (or as computed, if not supplied) stobjs-out ;; as supplied (or as computed, if not supplied) record-bytes ;; value as bound at the time MEMOIZE-FN is called record-calls ;; '' record-hits ;; '' record-hons-calls ;; '' record-mht-calls ;; '' record-pons-calls ;; '' record-time ;; '' forget ;; Boolean, clears memo when outermost call exits. memo-table-init-size ;; integer, default *mht-default-size* ) t) (defg *memoize-call-array* (make-array 1 :element-type 'mfixnum :initial-element 0) "*MEMOIZE-CALL-ARRAY*, 'ma' for short, is used for storage of the monitoring information for memoized functions. ma has as its length 4 times the square of the maximum number of memoized functions. ma is initialized in MEMOIZE-INIT. Think of ma as a two dimensional array with dimensions (twice the max number of memoized functions) x (twice the max number of memoized functions). Each 'column' corresponds to info about a memoized function, but the first five columns are 'special'. We count rows and columns starting at 0. Column 0 is used as scratch space by COMPUTE-CALLS-AND-TIMES for sums across all functions. Columns 1, 2, and 3 are not currently used at all. Column 4 is for the anonymous 'outside caller'. Column 5 is for the first memoized function. In columns 5 and greater, row 0 is used to count 'bytes', 1 'hits', 2 MHT calls, 3 HONS calls, and 4 PONS calls. The elements of an ma column starting at row 10 are for counting and timing info. Suppose column 7 corresponds to the memoized function FOO and column 12 corresponds to the memoized function BAR. Whenever FOO calls BAR, element 2*12 of column 7 will be incremented by 1, and the total elapsed time for the call will be added to element 2*12+1 of column 7. Though ma may 'grow', it may not grow while any memoized function is running, and here is why: every memoized function has a cached opinion about the size of ma. To avoid an abort during a call of MEMOIZE one may call (MEMOIZE-HERE-COME n) to assure that ma has room for at least n more memoized functions.") (defg *compute-array* (make-array 0) "*COMPUTE-ARRAY*, ca for short, is an array of proper lists. At the end of a call of COMPUTE-CALLS-AND-TIMES, which is called by MEMOIZE-SUMMARY, (aref ca n) will contain the numbers of the functions that have called the function numbered n.") (declaim (type (simple-array t (*)) *compute-array*)) (eval-when #-cltl2 (load eval) #+cltl2 (:load-toplevel :execute) (proclaim `(type (simple-array mfixnum (*)) *memoize-call-array*))) (defv *initial-max-memoize-fns* 500) (defg *2max-memoize-fns* (* 2 *initial-max-memoize-fns*)) (defconstant *ma-bytes-index* 0) (defconstant *ma-hits-index* 1) (defconstant *ma-mht-index* 2) (defconstant *ma-hons-index* 3) (defconstant *ma-pons-index* 4) (defconstant *ma-initial-max-symbol-to-fixnum* 4) (defg *max-symbol-to-fixnum* *ma-initial-max-symbol-to-fixnum*) (declaim (type fixnum *max-symbol-to-fixnum* *initial-2max-memoize-fns* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*)) (defg *caller* (* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*) "When memoized functions are executing in parallel, the value of *CALLER* and of statistics derived therefrom may be meaningless and random.") (declaim (type fixnum *caller*)) (defn memoize-here-come (n) (let ((m (ceiling (+ 100 (* 1.1 (- n (- (/ *2max-memoize-fns* 2) (floor (/ (hash-table-count *memoize-info-ht*) 2))))))))) (when (posp m) (memoize-call-array-grow (* 2 m))))) ; Essay on Memoization Involving Stobjs ; We allow memoization of functions that take user-defined stobjs (not state) ; as arguments but do not return stobjs. The key is the use of memoize-flush ; to "forget" all that was remembered for certain functions that use certain ; stobjs. We must keep memoize-flush very fast in execution so as not to slow ; down stobj update or resize operations in general. Indeed, memoize-flush may ; (according to tests run) incur essentially no cost (after Version_4.3) as ; long as no functions with stobj arguments are actually memoized. ; The following example shows why we disallow memoization of functions that ; return stobjs. First, redefine memoize-table-chk by eliminating the branch ; that causes an error in the presence of stobj names in stobjs-out. Then ; start up ACL2 and submit the forms below. The problem is that we do not ; inhibit storing a result in the case that the stobj has changed from the time ; the function was called to the time the result is to be stored. ; (defstobj st fld) ; (defun foo (st) ; (declare (xargs :stobjs st)) ; (let ((st (update-fld (cons (fld st) (fld st)) st))) ; (mv (fld st) st))) ; (foo st) ; updates (fld st), returns (mv (nil) st) ; (memoize 'foo) ; (foo st) ; updates (fld st), returns (mv ((nil) nil) st) ; (foo st) ; no longer updates (fld st) ; (foo st) ; no longer updates (fld st) ; (fld st) ; still ((nil . nil). (nil . nil)) (defun memoize-flush1 (lst) ; Experiments showed that when lst is nil, it is faster to call this function ; then to inline its code into the body of memoize-flush. ; We "forget" all memoized values by clearing all necessary memoize tables; see ; the comment about memoize-flush in memoize-fn. We leave the pons table alone ; in order to keep this flushing operation as fast as possible. Note that the ; pons table merely stores keys to be looked up in the memo table, so there is ; no soundness issue, and in fact those pons table entries might remain useful; ; the cost is the space taken up by the pons tables. (loop for sym in lst do (when (boundp (the symbol sym)) ; Is this test needed? (let ((old (symbol-value (the symbol sym)))) (unless (or (null old) (empty-ht-p old)) (setf (symbol-value (the symbol sym)) nil)))))) (defmacro memoize-flush (st) ; See memoize-flush1 for a relevant discussion. (let ((s (st-lst st))) `(when ,s ; optimization (memoize-flush1 ,s)))) (defparameter *memo-max-sizes* ;; Binds function names to memo-max-sizes-entry structures. ;; ;; Jared originally added this table because he wanted to know how big ;; memoization tables were getting (so that he could set up appropriate ;; initial sizes), but when tables are cleared they are thrown away, so for ;; tables that are frequently cleared it wasn't possible to see how large the ;; table had become. ;; ;; After seeing the information, we thought it might be a good idea to use it ;; to infer what a good table size might be when we recreate the memo table. ;; See the function predict-good-memoize-table-size for details. (make-hash-table)) ; BOZO should we use this information to try to guess better sizes and ; rehash thresholds for memoize tables? (defrec memo-max-sizes-entry ;; A single entry in the *memo-table-max-sizes* table. (num-clears ; how many times has this table been cleared (nat) max-pt-size ; maximum size of the pons table before any clear (nat) max-mt-size ; maximum size of the memo table before any clear (nat) avg-pt-size ; average size of pons table before any clear (float) avg-mt-size ; average size of memo table before any clear (float) ) t) (defun make-initial-memoize-hash-table (fn init-size) ; FN is the name of a function. INIT-SIZE is the initial size that the user ; says we should use. We want to come create and return a new hash table for ; this function's memoization table. One possible implementation of this ; function would just be: ; ; (hl-mht :size init-size) ; ; But we hope to do better. Our idea is to look at how large the table has ; been in the past, and use that size to make a good prediction of how large ; the table will be this time. ; ; The idea here is to build a table that's just slightly bigger than the ; average size we've seen so far. We arbitrarily say that "slightly bigger" ; means 1.2x the previous average. ; ; By itself this would be scary. Big hash tables can use a lot of memory: a ; rule of thumb in CCL is that 1 MB of space buys you 44,000 entries. I want ; to avoid creating a hundred-megabyte memo tables for a function just because ; it was used heavily for a short while and then cleared once before. On the ; other hand, if a memo table truly does get large on a regular basis, then we ; do want to guess a big size for it. ; ; So in this code, I enforce an artificial maximum on our guess, but I allow ; this maximum to grow with the number of times we've cleared the table. ; Basically I allow the maximum guess to grow at a rate of 1 MB per clear. If ; a table has been cleared 100 times, I think we have a pretty good sense of ; its average usage and we can be comfortable allocating up to 100 MB for it. ; If it's been cleared more than 1000 times, the cap is a gigabyte. But of ; course, to actually reach such a large guess, you'd have to be repeatedly ; filling up the table to contain millions of entries and then clearing it. (let* ((max-sizes ;; The previously recorded sizes of this table, if any exist. (gethash fn *memo-max-sizes*)) (size-to-use (if (not max-sizes) ;; We never cleared this memoize table before, so we don't have ;; anything to go on besides what the user says. Do what they ;; say. init-size (let* ((nclears (access memo-max-sizes-entry max-sizes :num-clears)) (avg-mt-size (access memo-max-sizes-entry max-sizes :avg-mt-size)) (our-guess (ceiling (* 1.20 avg-mt-size))) (capped-guess (min our-guess (* nclears 44000))) (final-guess (max 60 init-size capped-guess))) final-guess)))) ;; BOZO also try to guess a better rehash-size? (hl-mht :size size-to-use))) (defun make-initial-memoize-pons-table (fn init-size) (declare (ignorable init-size)) ; This is similar to make-initial-memoize-table, but for the pons table. (let* ((max-sizes (gethash fn *memo-max-sizes*)) (size-to-use (if (not max-sizes) ;; We've never cleared this pons table before, so we don't have ;; anything to go on besides what the user says. Now, this is ;; subtle. Originally I just returned init-size here, i.e., "do ;; what the user says." But while this makes sense for the memo ;; table, it doesn't necessarily make much sense for the pons ;; table. In particular, we can sometimes avoid ponsing by using ;; our static-cons-index-hashing scheme. ;; ;; In some sense it would probably be good to give the user ;; explicit control over the pons table size. But for now, the ;; main use of our memoize table size controls is to set things ;; up for big BDD/AIG/SEXPR operations where we've got honsed ;; data. So, I'm going to just use 60 here, and say that the ;; memo-table-init-size only affects the memoize table and not ;; the pons table. 60 (let* ((nclears (access memo-max-sizes-entry max-sizes :num-clears)) (avg-pt-size (access memo-max-sizes-entry max-sizes :avg-pt-size)) (our-guess (ceiling (* 1.20 avg-pt-size))) (capped-guess (min our-guess (* nclears 44000))) (final-guess (max 60 init-size capped-guess))) final-guess)))) ;; BOZO also try to guess a better rehash-size? (hl-mht :size size-to-use))) (defun update-memo-max-sizes (fn pt-size mt-size) ;; Called during clear-one-memo-and-pons-hash when the tables existed. ;; When called, pt-size and mt-size are nonzero. (let ((old (gethash fn *memo-max-sizes*))) (if (not old) (setf (gethash fn *memo-max-sizes*) (make memo-max-sizes-entry :num-clears 1 :max-pt-size pt-size :max-mt-size mt-size :avg-pt-size (coerce pt-size 'float) :avg-mt-size (coerce mt-size 'float))) (let* ((old.num-clears (access memo-max-sizes-entry old :num-clears)) (old.max-pt-size (access memo-max-sizes-entry old :max-pt-size)) (old.max-mt-size (access memo-max-sizes-entry old :max-mt-size)) (old.avg-pt-size (access memo-max-sizes-entry old :avg-pt-size)) (old.avg-mt-size (access memo-max-sizes-entry old :avg-mt-size)) (new.num-clears (+ 1 old.num-clears))) (setf (gethash fn *memo-max-sizes*) (make memo-max-sizes-entry :num-clears new.num-clears :max-pt-size (max pt-size old.max-pt-size) :max-mt-size (max mt-size old.max-mt-size) :avg-pt-size (/ (+ pt-size (* old.avg-pt-size old.num-clears)) new.num-clears) :avg-mt-size (/ (+ mt-size (* old.avg-mt-size old.num-clears)) new.num-clears)))))) nil) (defun print-memo-max-sizes () (when (equal (hash-table-count *memo-max-sizes*) 0) (return-from print-memo-max-sizes nil)) (format t "Memo table statistics gathered at each from when they were cleared:~%~%") (let ((indent 8) ;; length of "Function" (indent-str nil)) (maphash (lambda (fn entry) (declare (ignore entry)) (setq indent (max indent (length (symbol-name fn))))) *memo-max-sizes*) (setq indent-str (format nil "~a" (+ 2 indent))) (format t (concatenate 'string "~" indent-str ":@a") "Function") (format t " ~10:@a | ~15:@a ~15:@a | ~15:@a ~15:@a~%" "Clears" "PT Max" "PT Avg" "MT Max" "MT Avg") (maphash (lambda (fn entry) (let* ((num-clears (access memo-max-sizes-entry entry :num-clears)) (max-pt-size (access memo-max-sizes-entry entry :max-pt-size)) (max-mt-size (access memo-max-sizes-entry entry :max-mt-size)) (avg-pt-size (access memo-max-sizes-entry entry :avg-pt-size)) (avg-mt-size (access memo-max-sizes-entry entry :avg-mt-size))) (format t (concatenate 'string "~" indent-str ":@a ~10:D | ~15:D ~15:D | ~15:D ~15:D~%") fn num-clears max-pt-size (floor avg-pt-size) max-mt-size (floor avg-mt-size)))) *memo-max-sizes*) (format t "~%")) nil) ; MEMOIZE FUNCTIONS #+Clozure (defmacro heap-bytes-allocated () '(the mfixnum (ccl::%heap-bytes-allocated))) (defn sync-memoize-call-array () ; To be called only by MEMOIZE-INIT, MEMOIZE-CALL-ARRAY-GROW, or ; SAVE-MEMOIZE-CALL-ARRAY. (let ((n1 (the fixnum (* *2max-memoize-fns* *2max-memoize-fns*))) (n2 (1+ *max-symbol-to-fixnum*))) (declare (type fixnum n1 n2)) (unless (eql n1 (length *memoize-call-array*)) (unless (eql 1 (length *memoize-call-array*)) (setq *memoize-call-array* (make-array 1 :element-type 'mfixnum :initial-element 0)) (gc$)) (setq *memoize-call-array* (make-array n1 :element-type 'mfixnum :initial-element 0))) (unless (eql n2 (length *compute-array*)) (setq *compute-array* (make-array n2 :initial-element nil))) (setq *caller* (* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*)))) (defun memoize-call-array-grow (&optional (2nmax (* 2 (ceiling (* 3/2 (/ *2max-memoize-fns* 2)))))) (unless (integerp 2nmax) (error "(memoize-call-array-grow ~s). Arg must be an integer." 2nmax)) (unless (evenp 2nmax) (error "(memoize-call-array-grow ~s). Arg must be even." 2nmax)) (unless (> 2nmax 100) (error "(memoize-call-array-grow ~s). Arg must be > 100." 2nmax)) (when (<= 2nmax *2max-memoize-fns*) (cw "; memoize-call-array-grow: *memoize-call-array* already big enough.~%") (return-from memoize-call-array-grow)) (unless (<= (* 2nmax 2nmax) most-positive-fixnum) (error "memoize-call-array-grow: most-positive-fixnum exceeded. Too ~ many memoized functions.")) (unless (< (* 2nmax 2nmax) array-total-size-limit) (error "memoize-call-array-grow: ARRAY-TOTAL-SIZE-LIMIT exceeded. Too ~ many memoized functions.")) (unless (eql *caller* (* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*)) (cw "; MEMOIZE-CALL-ARRAY-GROW was called while a memoized function~%~ ; was executing, so call reports may be quite inaccurate.~%")) (setq *memoize-call-array* (make-array 1 :element-type 'mfixnum :initial-element 0)) (setq *2max-memoize-fns* 2nmax) (sync-memoize-call-array) (rememoize-all) nil) (defun-one-output symbol-to-fixnum-create (s) (check-type s symbol) (let ((g (gethash s *memoize-info-ht*))) (if g (access memoize-info-ht-entry g :num) (let (new) (loop for i fixnum from (if (eql *caller* (* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*)) (1+ *ma-initial-max-symbol-to-fixnum*) (1+ *max-symbol-to-fixnum*)) below (the fixnum (floor *2max-memoize-fns* 2)) do (unless (gethash i *memoize-info-ht*) (setq new i) (return))) (cond (new (setq *max-symbol-to-fixnum* (max *max-symbol-to-fixnum* new)) new) (t (memoize-call-array-grow) (safe-incf *max-symbol-to-fixnum* 1 symbol-to-fixnum-create) *max-symbol-to-fixnum*)))))) (defun-one-output symbol-to-fixnum (s) (check-type s symbol) (let ((g (gethash s *memoize-info-ht*))) (if g (access memoize-info-ht-entry g :num) (error "(symbol-to-fixnum ~s). Illegal symbol." s)))) (defun-one-output fixnum-to-symbol (n) (check-type n fixnum) (or (gethash n *memoize-info-ht*) (error "(fixnum-to-symbol ~s). Illegal number." n))) (defun-one-output coerce-index (x) (if (and (typep x 'fixnum) (>= x 0) (< x (length *memoize-call-array*))) x (symbol-to-fixnum x))) (defun-one-output memoize-eval-compile (def) (unless (and (consp def) (eq 'defun (car def)) (consp (cdr def)) (symbolp (cadr def))) (error "MEMOIZE-EVAL-COMPILE: Bad input:~%~s." def)) (compile (eval def)) nil) (defun-one-output memoizedp-raw (fn) (and (symbolp fn) (values (gethash fn *memoize-info-ht*)))) (defg *hons-gentemp-counter* 0) (defun-one-output hons-gentemp (root) (check-type root string) (loop (safe-incf *hons-gentemp-counter* 1 hons-gentemp) (let ((name (ofn "HONS-G-~s,~s" root *hons-gentemp-counter*))) (multiple-value-bind (sym status) (intern name (find-package "ACL2_INVISIBLE")) (if (null status) (return sym)))))) (defun-one-output st-lst (st) ; ST-LST returns a symbol whose value is a list in which are saved the ; names of the memoize tables that will be set to nil whenever the ; stobj st is changed. (check-type st symbol) (intern (ofn "HONS-S-~s,~s" (package-name (symbol-package st)) (symbol-name st)) (find-package "ACL2_INVISIBLE"))) (defun-one-output dcls (l) (loop for dec in l nconc (let ((temp (if (consp dec) (loop for d in (cdr dec) nconc (if (and (consp d) (eq (car d) 'ignore)) nil (cons d nil)))))) (if temp (cons (cons 'declare temp) nil))))) ; PRINE - princ eviscerated (defg *assoc-eq-hack-ht* (hl-mht :test 'eql)) (declaim (hash-table *assoc-eq-hack-ht*)) (defn assoc-eq-hack (x y) (cond ((atom y) nil) (t (let ((h (gethash y *assoc-eq-hack-ht*))) (cond (h (gethash x (the hash-table h))) (t (setq h (hl-mht :test 'eq)) (setf (gethash y *assoc-eq-hack-ht*) h) (loop for pair in y do (setf (gethash (car pair) (the hash-table h)) pair)) (gethash x (the hash-table h)))))))) (defun abbrev (x &optional (level *print-level*) (length *print-length*)) (cond ((atom x) x) ((eql level 0) '?) ((eql length 0) '?) (t (let ((pair (assoc-eq-hack x (table-alist 'evisc-table (w *the-live-state*))))) (cond (pair (cdr pair)) (t (let ((a (abbrev (car x) (and level (1- level)) length)) (d (abbrev (cdr x) level (and length (1- length))))) (cond ((and (eq a (car x)) (eq d (cdr x))) x) ((and (eq a '?) (eq d '?)) '?) (t (cons a d)))))))))) (defun prine (obj &optional stream) (let ((*print-pretty* nil)) (princ (abbrev obj *print-level* *print-length*) stream))) (defun prine-alist (obj &optional stream) ; Does not print the last atom. ; Prints "=" between pairs. (let ((*print-pretty* t)) (let ((max 6)) (cond ((loop for tail on obj always (and (consp (car tail)) (atom (caar tail)) (setq max (max max (if (symbolp (caar tail)) (length (symbol-name (caar tail))) 0))))) (loop for tail on obj do (cond ((eq obj tail) (write-char #\Space stream)) (t (oft "~& "))) (princ (caar tail) stream) (loop for i fixnum below (- max (if (symbolp (caar tail)) (length (symbol-name (caar tail))) 0)) do (write-char #\Space stream)) (princ " = ") (prine (cdar tail) stream))) (t (prine obj stream)))))) ; MEMOIZE-FN (defun-one-output mf-trace-exit (fn nrv ans) (oftr "~%< ~s " fn) (cond ((> nrv 1) (oftr "returned ~@r values:" nrv) (loop for i fixnum from 1 to nrv do (oftr "~%~@r.~8t " i) (prine (car ans) *trace-output*))) (t (prine ans *trace-output*))) (oftr ")~%")) (defg *memoize-fn-signature-error* " Memoize-fn: could not determine a signature for ~a.~%~ To assert the (number-of-inputs . number-of-outputs)~%~ signature of ~:*~a, put a cons of two numbers in the hash-table ~%~ *NUMBER-OF-ARGUMENTS-AND-VALUES-HT* under ~:*~a. For example, ~%~ do (setf (gethash '~:*~a *NUMBER-OF-ARGUMENTS-AND-VALUES-HT*) '(3 . 1))") (defg *sort-to-from-by-calls* nil) (defvar *memoize-use-attachment-warning-p* t) (defun memoize-use-attachment-warning (fn at-fn) (when *memoize-use-attachment-warning-p* (let ((state *the-live-state*)) (warning$ 'top-level "Attachment" "Although the function ~x0 is memoized, a result is not being ~ stored because ~@1. Warnings such as this one, about not ~ storing results, will remain off for all functions for the ~ remainder of the session unless the variable ~x2 is set to a ~ non-nil value in raw Lisp." fn (mv-let (lookup-p at-fn) (if (consp at-fn) (assert$ (eq (car at-fn) :lookup) (mv t (cdr at-fn))) (mv nil at-fn)) (cond (lookup-p (msg "a stored result was used from a call of ~ memoized function ~x0, which may have ~ been computed using attachments" at-fn)) (t (msg "an attachment to function ~x0 was used ~ during evaluation of one of its calls" at-fn)))) '*memoize-use-attachment-warning-p*)) (setq *memoize-use-attachment-warning-p* nil))) (defun-one-output memoize-fn-suffix (str sym) (check-type str string) (check-type sym symbol) (let ((spkn (package-name (symbol-package sym))) (sn (symbol-name sym))) (format nil "~s,~s,~s" str spkn sn))) (defun-one-output mis-ordered-commutative-args (x y) #-Clozure ;; [Jared]: Lisps besides Clozure don't have static conses so we can't ;; reorder arguments based on their indices. It's possible we could do ;; something like sort based on address, maybe, but I haven't thought about ;; it. (declare (ignore x y)) #-Clozure nil #+Clozure (cond ((eql x y) nil) (t (let ((idx (or (ccl::%staticp x) (and (typep x 'fixnum) x)))) (cond (idx (let ((idy (or (ccl::%staticp y) (and (typep y 'fixnum) y)))) (cond (idy (< (the fixnum idy) (the fixnum idx))) ((rationalp y) (< y (the fixnum idx)))))) ((rationalp x) (let ((idy (or (ccl::%staticp y) (and (typep y 'fixnum) y)))) (cond (idy (< (the fixnum idy) x)) ((rationalp y) (< y x)))))))))) (defun our-function-lambda-expression (sym) (let ((temp (get sym 'acl2-saved-def))) (cond (temp (values temp t)) (t (let* ((fn (symbol-function sym)) (lam (and fn (function-lambda-expression fn)))) (cond (lam (values lam nil)) (t (values nil nil)))))))) (defun memoize-look-up-def (fn cl-defun inline wrld) ;; [Jared] I *think* what is going on here is the following: ;; ;; When you call MEMOIZE-FN you can give a :cl-defun argument. This defaults ;; to :default, which means: "Look up the function's definition from the world ;; or from the Lisp." ;; ;; But you can alternately provide an explicit definition. I'm not sure ;; quite what the format of this is supposed to be. It looks like this ;; definition gets recorded in the info-table for fn, and maybe it gets used ;; in restoring and turning memoize on/off, etc. (cond ((eq cl-defun :default) (if inline (cond ((not (fboundp fn)) (error "MEMOIZE-LOOK-UP-DEF: ** ~a is undefined." fn)) ((let ((def (cltl-def-from-name fn wrld))) (cond (def (assert (eq (car def) 'defun)) (cdr def))))) ((multiple-value-bind (def flg) (our-function-lambda-expression fn) (cond (flg (cdr def)) (def (assert (eq (car def) 'lambda)) def)))) (t #+Clozure (unless (and ccl::*save-source-locations* ccl::*fasl-save-definitions*) (format t "; Check the settings of CCL::*SAVE-SOURCE-LOCATIONS* ~ and CCL::*FASL-SAVE-DEFINITIONS*.")) (error "MEMOIZE-LOOK-UP-DEF: ** Cannot find a definition for ~ ~a via ACL2 or OUR-FUNCTION-LAMBDA-EXPRESSION." fn))) nil)) ((eq (car cl-defun) 'defun) (cdr cl-defun)) (t cl-defun))) (defun fix-time (ticks ctx) (declare (type integer ticks)) (if (and (<= 0 ticks) (< ticks (* 864000 ;; 10 days, in seconds *float-ticks/second*))) ticks (progn (format t "Ignoring time increment of ~a sec for ~a~%" (/ ticks *float-ticks/second*) ctx) 0))) (defg *memoize-init-done* nil) (defun memoize-fn (fn &key (condition t) (inline t) (trace nil) (cl-defun :default) (formals :default) (stobjs-in :default) (stobjs-out :default) (commutative nil) (specials nil) (forget nil) (memo-table-init-size '60) (aokp nil) &aux (wrld (w *the-live-state*))) "The documentation for MEMOIZE-FN is very incomplete. One may invoke (MEMOIZE-FN fn) on the name of a Common Lisp function FN from outside the ACL2 loop to get results of possible interest in terms of memoization activity and profiling information. MEMOIZE-FN already has a dozen parameters. MEMOIZE-FN replaces the SYMBOL-FUNCTION for the symmbol FN with 'enhanced' raw Common Lisp code that, supposedly, does not affect the value returned by FN but may make some notes and may even obtain some return values by remembering them from a previous call. If the CONDITION parameter is not NIL, then whenever FN is called, and there is not as yet any value remembered for a call of FN on the given arguments, then if the evaluation of the CONDITION parameter is not NIL, the values that are computed for FN on the given arguments will be saved under the given arguments. If the CONDITION parameter is the name of an ACL2 function, the body of that function is used as the condition. If the INLINE parameter is T, then when MEMOIZE-FN creates a new body for FN, we place the old body of FN within the new body, i.e., 'in line'. However, if the INLINE parameter is NIL, then we place code that calls the existing SYMBOL-FUNCTION for FN within the new body. One might well argue that our parity for the INLINE parameter to MEMOIZE-fn is backwards, but we don't think so. The TRACE parameter to MEMOIZE-FN may be T, NIL, :INLINE, or :NOTINLINE. One may lie to MEMOIZE-FN to force the memoization of a function that has ACL2's state as an explicit parameter by using fraudulent FORMALS, STOBJS-IN, and STOBJS-OUT parameters to MEMOIZE-FN. If the COMMUTATIVE parameter is not NIL, then the two arguments may be swapped before further processing. We hope/presume that ACL2 will have been used first to prove that commutativity. If the CL-DEFN parameter is not NIL, we pretend that the current body of FN is that parameter, and similarly for FORMALS, STOBJS-IN, and STOBJS-OUT. If FN is a raw Common Lisp function and not an ACL2-approved function, it may make reference to a variable, say S, that has a SPECIAL binding, in which case one needs to consider what in the world the meaning is for memoizing such a function at all. If S is a member of the SPECIALS parameter, then it is assumed that FN does not alter but only refers to S. MEMOIZE-FN acts as though FN had S as an extra argument for purposes of memoization. If the FORGET parameter is not NIL, the pons and memo tables of FN are discarded at the end of every outermost call of FN." ; MIS-ORDERED-COMMUTATIVE-ARGS apparently, but only apparently, ; introduces nondeterminism into the values returned by ACL2 functions ; redefined with MEMOIZE-FN, something highly suspicious because it ; can so easily lead to a contradition. ; We believe that the use of the nondeterministic function ; MIS-ORDERED-COMMUTATIVE-ARGS in the memoization of an ACL2 function ; of two arguments that has been proven commutative is justified by ; the fact that the memoized function will return, modulo EQUAL, the ; same result regardless of what MIS-ORDERED-COMMUTATIVE-ARGS returns, ; and hence the nondeterminism cannot be detected by the ACL2 logic. ; The :CONDITION parameter of MEMOIZE-FN can either be T, or a ; function symbol defined by the user within the ACL2 loop, or a LISTP ; (CONSP or NIL). In the last case we think of the condition as an ; expression in the formals of FN. If the :INLINE parameter T, then ; the body of FN is placed inline in the memoized definition; ; otherwise, a funcall of the original function is placed there. #-hons (return-from memoize-fn (progn (when (not (zerop *ld-level*)) (warning$ 'memoize nil "No change for function ~x0: Memoization ~ requests are ignored in this ACL2 ~ executable because it is not hons-enabled." fn)) fn)) (when (equal condition *nil*) (setq condition nil)) (maybe-untrace! fn) ; See the comment about Memoization in trace$-def. (with-warnings-suppressed ; Big old bunch of error checking... (unless *memoize-init-done* (error "Memoize-fn: *MEMOIZE-INIT-DONE* is still nil.")) (unless (symbolp fn) (error "Memoize-fn: ~s is not a symbol." fn)) (unless (or (fboundp fn) (not (eq cl-defun :default))) (error "Memoize-fn: ~s is not fboundp." fn)) (when (or (macro-function fn) (special-operator-p fn) (and (fboundp 'compiler-macro-function) ; for GCL as of 5/2013 (compiler-macro-function fn))) (error "Memoize-fn: ~s is a macro or a special operator or has a ~ compiler macro." fn)) (when (never-memoize-p fn) (error "Memoize-fn: ~s must never be memoized" fn)) (when (memoizedp-raw fn) (format t "; Memoize-fn: ** Warning: ~s is currently memoized.~%~ ; We will first unmemoize it, then memoize it again." fn) (unmemoize-fn fn)) (when (member fn (eval '(trace))) (format t "; Memoize-fn: Untracing ~s before memoizing it." fn) (eval `(untrace ,fn))) ; TRACE, UNTRACE, OLD-TRACE, and OLD-UNTRACE are macros that get ; redefined sometimes. So we use EVAL in calling them. #+Clozure (when (ccl::%advised-p fn) (error "~%; Memoize-fn: Please unadvise ~s before calling memoize-fn on ~ it." fn)) (when (and (fboundp 'old-trace) (member fn (eval '(old-trace)))) (format t "; Memoize-fn: Old-untracing ~s before memoizing it." fn) (eval `(old-untrace ,fn))) (when (eq fn 'return-last) (error "Memoize-fn: RETURN-LAST may not be memoized.")) (when (getprop fn 'constrainedp nil 'current-acl2-world wrld) (error "Memoize-fn: ~s is constrained; you may instead wish to memoize a ~ caller or to memoize its attachment (see :DOC defattach)." fn)) ;; Doesn't this get checked below? See the lambda-list intersection thing #+Clozure (when (multiple-value-bind (req opt restp keys) (ccl::function-args (symbol-function fn)) (or restp keys (not (integerp req)) (not (eql opt 0)))) (error "Memoize-fn: ~a has non-simple arguments." fn)) (let* ((cl-defun (memoize-look-up-def fn cl-defun inline wrld)) (formals ;; Magic code to try to figure out what the formals of the function are. ;; For ACL2 functions this works via looking up the 'formals property ;; For raw Lips functions we may be able to extract the formals from the ;; cl-defun above, or we may generate a list (X1 ... XN) in some cases? (if (eq formals :default) (let ((fo (getprop fn 'formals t 'current-acl2-world wrld))) (if (eq fo t) (if (consp cl-defun) (cadr cl-defun) (let ((n (number-of-arguments fn))) (if n (loop for i fixnum below n collect (ofni "X~a" i)) (error *memoize-fn-signature-error* fn)))) fo)) formals)) (stobjs-in ;; Either the ACL2 stobjs-in property or (T T T T ...) for the number ;; of formals that we inferred the function has. (if (eq stobjs-in :default) (let ((s (getprop fn 'stobjs-in t 'current-acl2-world wrld))) (if (eq s t) (make-list (len formals)) s)) stobjs-in)) (stobjs-out ;; Either the ACL2 stobjs-out property or (T T T T ...) for the number ;; of return values that we inferred the function has. (if (eq stobjs-out :default) (let ((s (getprop fn 'stobjs-out t 'current-acl2-world wrld))) (if (eq s t) (let ((n (number-of-return-values fn))) (cond (n (make-list n)) ((and (null condition) (null trace)) (list nil nil)) (t (error "Memoize-fn: cannot determine the ~ number of return values of ~a.~%~ You may put a cons of ~ two numbers in the hash-table~%~ *NUMBER-OF-ARGUMENTS-AND-VALUES-HT* ~ under ~a. ~%For example, do (setf ~ (gethash '~a ~ *NUMBER-OF-ARGUMENTS-AND-VALUES-HT*) ~ '(~a . 1))" fn fn fn (len stobjs-in))))) s)) stobjs-out))) ;; Not sure why this check is necessary or useful (unless (and (symbol-listp formals) (no-duplicatesp formals) (loop for x in formals never (constantp x))) (error "Memoize-fn: FORMALS, ~a, must be a true list of distinct, ~ nonconstant symbols." formals)) (when (intersection lambda-list-keywords formals) (error "Memoize-fn: FORMALS, ~a, may not intersect ~ LAMBDA-LIST-KEYWORDS." formals)) ;; Don't memoize functions involving state. Fair enough. ;; Can you memoize functions with other stobjs?? (when (and condition (or (member 'state stobjs-in) (member 'state stobjs-out))) (error "Memoize-fn: ~s uses STATE." fn)) (let* ((fnn (symbol-to-fixnum-create fn)) ; performance counting; see memoize-call-array (2mfnn (* *2max-memoize-fns* fnn)) ; performance counting; see memoize-call-array (*acl2-unwind-protect-stack* (or *acl2-unwind-protect-stack* (cons nil *acl2-unwind-protect-stack*))) (old-fn (if (fboundp fn) (symbol-function fn))) (body (if (or inline (null old-fn)) (car (last cl-defun)) `(funcall #-gcl ,old-fn #+gcl ',old-fn ; old-fn could be (lisp:lambda-block ...) ,@formals))) (body-name (make-symbol "BODY-NAME")) (body-call (list body-name)) (condition-body (cond ((booleanp condition) condition) ((symbolp condition) ;; Bizarre thing where the condition can be just the name of an ACL2 function, ;; see the documentation above (car (last (cltl-def-from-name condition wrld)))) (t condition))) (dcls (dcls (cddr (butlast cl-defun)))) (start-time (let ((v (hons-gentemp (memoize-fn-suffix "START-TIME-" fn)))) (eval `(prog1 (defg ,v -1) (declaim (type integer ,v)))))) (tablename ;; Submits the defg form and returns the crazy name that gets generated. (eval `(defg ,(hons-gentemp (memoize-fn-suffix "MEMOIZE-HT-FOR-" fn)) nil))) (ponstablename ;; Submits the defg form and returns the crazy name that gets generated. (eval `(defg ,(hons-gentemp (memoize-fn-suffix "PONS-HT-FOR-" fn)) nil))) (localtablename (make-symbol "TABLENAME")) (localponstablename (make-symbol "PONSTABLENAME")) (sts ; Here we support memoize-flush, which keeps memoize tables coherent in the ; presence of user-defined stobjs. For each (user-level) stobj input name, st, ; we collect up the variable (st-lst st), whose value is the list of names of ; memoize tables that need to be cleared whenever that stobj changes. Below, ; we will push the present function's table name onto each of these lists. ; Ultimately, stobj updates are made by stobj primitives. After the stobj ; primitive for stobj st makes an update, the memo table for a function f ; taking st as its nth argument (say) is no longer valid for saved calls of f ; for which the nth argument is st. Because of congruent stobjs and abstract ; stobjs, that nth argument need not be st, and we believe that in principle, ; we could restrict flushing of memo table entries of f to those whose nth ; argument is eq to the stobj being updated (whether st, congruent to st, or an ; abstract stobj whose concrete stobj is congruent to st). But for now we take ; the coarser approach, which has the advantage that we simply throw away the ; memo-table for f when flushing, leaving it to be garbage collected; see ; memoize-flush1. (and condition ; else no memo table usage, so skip flushing (remove-duplicates-eq (loop for st in (union stobjs-in stobjs-out) when st collect (assert$ (not (and condition (eq st 'state))) ; see memoize-table-chk (st-lst (congruent-stobj-rep ; In the case that st is an abstract stobj, we replace it with the ; corresponding concrete stobj before getting a canonical (congruent) ; representative; see the rather long comment just above that mentions ; "abstract" stobjs. (or (concrete-stobj st wrld) st) wrld))))))) ;; Number of arguments. Specials only matter for common lisp functions, see the notes above in memoize-fn. ;; Basically if the function reads from specials we want to count them as args. (nra (+ (len formals) (len specials))) def success) (let* ((mf-trace-exit ;; Ignore this, just some kind of tracing... (and trace `((mf-trace-exit ',fn ,(length stobjs-out) ,*mf-ans*)))) (mf-record-mht ;; performance counting, see memoize-call-array (and *record-mht-calls* `((safe-incf (aref ,*mf-ma* ,2mfnn) 1 ,fn)))) (mf-record-hit ;; performance counting, see memoize-call-array (and *record-hits* condition-body `((safe-incf (aref ,*mf-ma* ,(+ 2mfnn *ma-hits-index*)) 1 ,fn)))) (lookup-marker (cons :lookup fn)) (body3 ;; Main part of the new function definition... `(let (,*mf-ans* ,*mf-args* ,*mf-ans-p*) (declare (ignorable ,*mf-ans* ,*mf-args* ,*mf-ans-p*)) (cond ((not ,condition-body) ,@mf-record-hit ; sort of a hit ,(if (not trace) body-call (if (cdr stobjs-out) `(progn (setq ,*mf-ans* (multiple-value-list ,body-call)) ,@mf-trace-exit (values-list ,*mf-ans*)) `(progn (setq ,*mf-ans* ,body-call) ,@mf-trace-exit ,*mf-ans*)))) ,@(and condition-body `((t ;; reinitialize tables if they don't exist... (when (null ,tablename) ,@mf-record-mht (setq ,tablename (make-initial-memoize-hash-table ',fn ,memo-table-init-size)) ,@(if (> nra 1) ; number of arguments `((setq ,ponstablename (make-initial-memoize-pons-table ',fn ,memo-table-init-size))))) ;; To avoid a remotely possible parallelism gethash error. (jared: what?!?) ,@(if (> nra 1) `((setq ,localponstablename (or ,ponstablename ;; BOZO should this be a make-initial-memoize-pons-table? (hl-mht))))) ;; Generate the pons key... if there's just one arg, pist* just returns the arg and ;; doesn't do any ponsing. (setq ,*mf-args* (pist* ,localponstablename ,@formals ,@specials)) ;; dunno what this is for... maybe we're binding a localtablename variable to avoid ;; doing special lookups or some such? (setq ,localtablename ;; BOZO should this be a make-initial-memoize-hash-table? (or ,tablename (hl-mht))) ;; this is the lookup of the memoized result. (multiple-value-setq (,*mf-ans* ,*mf-ans-p*) ,(let ((gethash-form `(gethash ,*mf-args* (the hash-table ,localtablename)))) (cond (aokp `(cond (*aokp* ,gethash-form) (t (mv nil nil)))) (t gethash-form)))) (cond (,*mf-ans-p* ;; Memoized lookup succeeds. Do profiling things, return answer... ,@(when aokp `((update-attached-fn-called ',lookup-marker))) ,@(if trace `((oftr "~% ~s remembered." ',fn))) ,@mf-record-hit ,@(cond ((null (cdr stobjs-out)) `(,@mf-trace-exit ,*mf-ans*)) (t ;; No idea what this is doing... (let ((len-1 (1- (length stobjs-out)))) `(,@(and trace `(progn (let* ((,*mf-ans* (append (take ,len-1 ,*mf-ans*) (list (nthcdr ,len-1 ,*mf-ans*))))) ,@mf-trace-exit))) ,(cons 'mv (nconc (loop for i fixnum below len-1 collect `(pop ,*mf-ans*)) (list *mf-ans*)))))))) (t ;; Memoized lookup failed. Need to run the function and get its results... ,(let* ((vars ;; Make variables (O0 O1 ... 0N) for the outputs? Don't really understand what stobjs-out is ;; for here... (loop for i fixnum below (if (cdr stobjs-out) (length stobjs-out) 1) collect (ofni "O~a" i))) (prog1-fn (if (cdr stobjs-out) 'multiple-value-prog1 'prog1)) (mf-trace-exit+ (and mf-trace-exit `((let ((,*mf-ans* ,(if stobjs-out `(list* ,@vars) (car vars)))) ,@mf-trace-exit))))) `(let (,*attached-fn-temp*) (mv?-let ,vars (let (*attached-fn-called*) ;; This is where the actual function is being run. The 01...0N vars are being bound to ;; the results... (,prog1-fn ,body-call (setq ,*attached-fn-temp* *attached-fn-called*))) (progn (cond ,@(and (not aokp) `((,*attached-fn-temp* (memoize-use-attachment-warning ',fn ,*attached-fn-temp*)))) (t ;; Actually install the results (setf (gethash ,*mf-args* (the hash-table ,localtablename)) (list* ,@vars)))) (update-attached-fn-called ,*attached-fn-temp*) ,@mf-trace-exit+ (mv? ,@vars))))))))))))) (body2 ;; Bunch of extra profiling stuff wrapped around body3. `(let ((,*mf-old-caller* *caller*) #+Clozure ,@(and *record-bytes* `((,*mf-start-bytes* (heap-bytes-allocated)))) ;; [Jared]: removing this, hons tracking hasn't worked since hl-hons ;; ,@(and *record-hons-calls* ;; `((,*mf-start-hons* *hons-call-counter*))) ,@(and *record-pons-calls* `((,*mf-start-pons* *pons-call-counter*)))) (declare (ignorable #+Clozure ,@(and *record-bytes* `(,*mf-start-bytes*)) ;; ,@(and *record-hons-calls* `(,*mf-start-hons*)) ,@(and *record-pons-calls* `(,*mf-start-pons*))) (type fixnum ,*mf-old-caller* ;; ,@(and *record-hons-calls* `(,*mf-start-hons*)) ,@(and *record-pons-calls* `(,*mf-start-pons*)) #+Clozure ,@(and *record-bytes* `(,*mf-start-bytes*)))) (unwind-protect (progn (setq ,start-time ,(if *record-time* '(internal-real-time) '0)) (setq *caller* ,2mfnn) ,body3) ;; [Jared]: removing this, hons tracking hasn't worked since hl-hons ;; ,@(and *record-hons-calls* ;; `((safe-incf (aref ,*mf-ma* ,(+ *ma-hons-index* 2mfnn)) ;; (the mfixnum (- *hons-call-counter* ,*mf-start-hons*)) ,fn))) ,@(and *record-pons-calls* `((safe-incf (aref ,*mf-ma* ,(+ *ma-pons-index* 2mfnn)) (the mfixnum (- *pons-call-counter* ,*mf-start-pons*)) ,fn))) #+Clozure ,@(and *record-bytes* `((safe-incf (aref ,*mf-ma* ,(+ *ma-bytes-index* 2mfnn)) (the mfixnum (- (heap-bytes-allocated) ,*mf-start-bytes*)) ,fn))) ,@(and *record-time* `((safe-incf (aref ,*mf-ma* (the mfixnum (1+ ,*mf-count-loc*))) (the integer (fix-time (the integer (- (internal-real-time) ,start-time)) ',fn)) ,fn))) ,@(and forget ; Below, we "clear" the tables by setting them to nil, rather than by calling ; clrhash. The latter approach would probably avoid reallocation of space, but ; we suspect that a gain in space efficiency may be offset by a loss in time ; efficiency. The present approach has been working, so we prefer not to ; change it. Below is just a little space analysis. ; When we evaluated (defconstant *a* (make-list 1000)) in raw Lisp (CCL) and ; then ran (time$ (bad-lisp-objectp *a*)), we saw about 71K bytes allocated, ; which dropped to just 1,136 bytes after evaluating (unmemoize-fn ; 'bad-lisp-objectp). Then we evaluated (memoize-fn 'bad-lisp-objectp) -- this ; time without a :forget argument -- and found about 71K bytes allocated on the ; first timing of (bad-lisp-objectp *a*), but only 1,136 bytes allocated on ; subsequent evaluations, presumably because we weren't starting from scratch. ; We suspect that the byte allocation on subsequent runs may have been well ; under 71K even after memoizing with :forget t, if the tables had been cleared ; with clrhash rather than being blown away as is done just below. `((setq ,tablename nil) ,@(if (> nra 1) `((setq ,ponstablename nil))))) (setq ,start-time -1) (setq *caller* ,*mf-old-caller*))))) (setq def `(defun ,fn ,formals ,@dcls ,@(if trace (if (member trace '(notinline inline)) `((declare (,trace ,fn))) `((declare (notinline ,fn))))) (declare (ignorable ,@formals ,@specials)) ,@(and commutative `((cond ((mis-ordered-commutative-args ,(car formals) ,(cadr formals)) (rotatef ,(car formals) ,(cadr formals)))))) ,@(and trace `((oftr "~%(> ~s (" ',fn) ,@(loop for v in (append formals specials) append `((oftr "~& ~s = " ',v) (prine ,v *trace-output*))) (oftr "~& )"))) (let* ((,*mf-count-loc* ,(if (or *record-calls* *record-time*) `(the fixnum (+ *caller* (* 2 ,fnn))) 0)) (,*mf-ma* *memoize-call-array*) ,localtablename ,localponstablename) (declare (type fixnum ,*mf-count-loc*) (ignorable ,*mf-count-loc* ,*mf-ma* ,localponstablename ,localtablename) (type (simple-array mfixnum (*)) ,*mf-ma*)) ,@(and *record-calls* `((safe-incf (aref ,*mf-ma* ,*mf-count-loc*) 1 ,fn))) (flet ((,body-name () ,body)) (if (eql -1 ,start-time) ,body2 ,body3)))))) (setf (gethash fn *number-of-arguments-and-values-ht*) (cons (length stobjs-in) (length stobjs-out))) (unwind-protect (progn (let ((ma *memoize-call-array*)) (declare (type (simple-array mfixnum (*)) ma)) (loop for i fixnum from 2mfnn below (the fixnum (+ 2mfnn *2max-memoize-fns*)) unless (eql (aref ma i) 0) do (setf (aref ma i) 0))) (memoize-eval-compile def) (setf (gethash fn *memoize-info-ht*) (make memoize-info-ht-entry :fn fn :ext-anc-attachments (and aokp (extended-ancestors fn wrld)) :tablename tablename :ponstablename ponstablename :old-fn old-fn :memoized-fn (symbol-function fn) :condition condition :inline inline :num fnn :sts sts :trace trace :start-time start-time :cl-defun cl-defun :formals formals :commutative commutative :specials specials :stobjs-in stobjs-in :stobjs-out stobjs-out :record-bytes *record-bytes* :record-calls *record-calls* :record-hits *record-hits* :record-hons-calls *record-hons-calls* :record-mht-calls *record-mht-calls* :record-pons-calls *record-pons-calls* :record-time *record-time* :forget forget :memo-table-init-size memo-table-init-size)) (setf (gethash fnn *memoize-info-ht*) fn) (and condition (loop for s in sts do (push tablename (symbol-value s)))) (setq success t)) (unless success (setf (symbol-function fn) old-fn) (remhash fn *memoize-info-ht*) (remhash fnn *memoize-info-ht*) (and condition (loop for s in sts when (eq tablename (car (symbol-value (the symbol s)))) do (pop (symbol-value (the symbol s))))) ;; [Jared] is there some reason this shouldn't just be an error? (format t "Memoize-fn: Failed to memoize ~s." fn) (setq fn nil)))))) fn) (defun-one-output unmemoize-fn (fn) #-hons (return-from unmemoize-fn (progn (when (not (zerop *ld-level*)) (warning$ 'unmemoize nil "No change for function ~x0: Unmemoization ~ requests are ignored in this ACL2 ~ executable because it is not hons-enabled." fn)) fn)) (maybe-untrace! fn) ; See the comment about Memoization in trace$-def. (let* ((ma *memoize-call-array*) (l (memoizedp-raw fn))) (declare (type (simple-array mfixnum (*)) ma)) (unless l (error "Unmemoize-fn: ~s is not memoized." fn)) (let* ((num (the fixnum (access memoize-info-ht-entry l :num))) (tablename (and l (access memoize-info-ht-entry l :tablename))) (n2 (* num *2max-memoize-fns*)) ) (declare (fixnum num n2)) ; Note: condition is a first-class ACL2 function, not to be messed ; with here. (let (#+Clozure (ccl:*warn-if-redefine-kernel* nil)) (let ((old-fn (access memoize-info-ht-entry l :old-fn))) (if old-fn (setf (symbol-function (the symbol fn)) old-fn) (fmakunbound fn)))) (loop for i fixnum from n2 below (the fixnum (+ n2 *2max-memoize-fns*)) unless (eql (aref ma i) 0) do (setf (aref ma i) 0)) (remhash fn *memoize-info-ht*) (remhash num *memoize-info-ht*) (setf (symbol-value (the symbol tablename)) nil) (setf (symbol-value (the symbol (access memoize-info-ht-entry l :ponstablename))) nil) (loop for s in (access memoize-info-ht-entry l :sts) do (when (boundp s) (setf (symbol-value (the symbol s)) (remove tablename (symbol-value (the symbol s)))))))) fn) (defun-one-output maybe-unmemoize (fn) ; We rely on the normal undoing mechanism (for :ubt etc.) to take care of ; unmemoization. However, as a courtesy to users who memoize using raw Lisp, ; we provide and call this utility for unmemoizing functions that are not known ; to ACL2 (via the memoize-table) as being memoized. (when (and (memoizedp-raw fn) (not (cdr (assoc-eq fn (table-alist 'memoize-table (w *the-live-state*)))))) (unmemoize-fn fn))) (defun-one-output memoized-functions () ;; Get the names of all memoized functions as a list. (let (ans) (maphash (lambda (fn info) (declare (ignore info)) (when (symbolp fn) (push fn ans))) *memoize-info-ht*) ans)) (defun-one-output length-memoized-functions () "(length-memoized-functions) returns the number of currently memoized/profiled functions." (values (floor (1- (hash-table-count (the hash-table *memoize-info-ht*))) 2))) (defun-one-output unmemoize-all () "(UNMEMOIZE-ALL) unmemoizes all currently memoized functions, including all profiled functions." ; WARNING: ACL2 users should probably avoid this function, using ; (clear-memo-table) in the ACL2 loop instead. ; A warning to would-be code improvers. It would be a bad idea to ; redefine UNMEMOIZE-ALL to MAPHASH over *MEMOIZE-INFO-HT* because of ; the ANSI rules restricting which hash table entries may be modified ; during a MAPHASH. (loop for x in (memoized-functions) do (unmemoize-fn x))) (defun-one-output memoize-info (k) "(MEMOIZE-INFO k) returns the settings of the various arguments to MEMOIZE-FN and the settings of the special variables that influence MEMOIZE-FN during the memoization of K." (let ((v (gethash k *memoize-info-ht*))) (and v (list (list (access memoize-info-ht-entry v :fn) :condition (access memoize-info-ht-entry v :condition) :inline (access memoize-info-ht-entry v :inline) :trace (access memoize-info-ht-entry v :trace) :cl-defun (access memoize-info-ht-entry v :cl-defun) :formals (access memoize-info-ht-entry v :formals) :stobjs-in (access memoize-info-ht-entry v :stobjs-in) :specials (access memoize-info-ht-entry v :specials) :commutative (access memoize-info-ht-entry v :commutative) :stobjs-out (access memoize-info-ht-entry v :stobjs-out) :forget (access memoize-info-ht-entry v :forget) :memo-table-init-size (access memoize-info-ht-entry v :memo-table-init-size)) (list (access memoize-info-ht-entry v :record-bytes) (access memoize-info-ht-entry v :record-calls) (access memoize-info-ht-entry v :record-hits) (access memoize-info-ht-entry v :record-hons-calls) (access memoize-info-ht-entry v :record-mht-calls) (access memoize-info-ht-entry v :record-pons-calls) (access memoize-info-ht-entry v :record-time)))))) (defun-one-output rememoize-all () (let (l) (maphash (lambda (k v) (declare (ignore v)) (when (symbolp k) (push (memoize-info k) l))) *memoize-info-ht*) (loop for x in l do (unmemoize-fn (caar x))) (gc$) (setq *max-symbol-to-fixnum* *ma-initial-max-symbol-to-fixnum*) (loop for x in l do (progv '(*record-bytes* *record-calls* *record-hits* *record-hons-calls* *record-mht-calls* *record-pons-calls* *record-time*) (cadr x) (apply 'memoize-fn (car x)))))) (defun-one-output uses-state (fn) (let* ((w (w *the-live-state*)) (stobjs-in (getprop fn 'stobjs-in t 'current-acl2-world w)) (stobjs-out (getprop fn 'stobjs-out t 'current-acl2-world w))) (or (and (consp stobjs-in) (member 'state stobjs-in)) (and (consp stobjs-out) (member 'state stobjs-out))))) (defun profile-fn (fn &rest r &key (condition nil) (inline nil) &allow-other-keys) (apply #'memoize-fn fn :condition condition :inline inline r)) (defun-one-output profiled-functions () ; The profiled functions are hereby arbitrarily defined as those ; produced by MEMOIZE-FN with null :CONDITION and :INLINE fields. (let (l) (maphash (lambda (k v) (when (and (symbolp k) (null (access memoize-info-ht-entry v :condition)) (null (access memoize-info-ht-entry v :inline))) (push k l))) *memoize-info-ht*) l)) (defun-one-output unmemoize-profiled () "UNMEMOIZE-PROFILED is a raw Lisp function. (UNMEMOIZE-PROFILED) unmemoizes and unprofiles all functions currently memoized with :CONDITION=NIL and :INLINE=NIL." (loop for x in (profiled-functions) do (unmemoize-fn (car x)))) (defmacro memoize-on-raw (fn x) `(let* ((,*mo-f* ,fn) (,*mo-h* (memoizedp-raw ,*mo-f*))) (unless ,*mo-h* (error "~s is not memoized" ,*mo-f*)) (let ((,*mo-o* (symbol-function (the symbol ,*mo-f*)))) (unwind-protect (progn (setf (symbol-function (the symbol ,*mo-f*)) (access memoize-info-ht-entry ,*mo-h* :memoized-fn)) ,x) (setf (symbol-function (the symbol ,*mo-f*)) ,*mo-o*))))) (defmacro memoize-off-raw (fn x) `(let* ((,*mo-f* ,fn) (,*mo-h* (memoizedp-raw ,*mo-f*))) (unless ,*mo-h* (error "~s is not memoized" ,*mo-f*)) (let ((,*mo-o* (symbol-function (the symbol ,*mo-f*)))) (unwind-protect (progn (setf (symbol-function (the symbol ,*mo-f*)) (access memoize-info-ht-entry ,*mo-h* :old-fn)) ,x) (setf (symbol-function (the symbol ,*mo-f*)) ,*mo-o*))))) (defun-one-output memoize-condition (fn) (let ((x (gethash fn *memoize-info-ht*))) (if x (access memoize-info-ht-entry x :condition) nil))) (defn global-restore-memoize () (maphash (lambda (k l) (when (symbolp k) (setf (symbol-function k) (access memoize-info-ht-entry l :memoized-fn)))) *memoize-info-ht*)) ; STATISTICS GATHERING AND PRINTING ROUTINES (defg *memoize-summary-order-list* '(total-time number-of-calls) "*MEMOIZE-SUMMARY-ORDER-LIST* is a raw Lisp variable. It is a list of functions that MEMOIZE-SUMMARY uses to sort all functions that are currently memoized in preparation for displaying information about them. The order used is lexicographical, with the first order having the most weight. Each order function takes one argument, a symbol, and returns a rational. The default is '(total-time number-of-calls). Options for the functions include: bytes-allocated bytes-allocated/call event-number execution-order hits/calls hons-calls pons-calls number-of-calls number-of-hits number-of-memoized-entries number-of-mht-calls symbol-name-order time-for-non-hits/call time/call total-time. ") (defg *memoize-summary-limit* 20 "*MEMOIZE-SUMMARY-LIMIT* is a raw Lisp variable whose value is the maximum number of functions upon which MEMOIZE-SUMMARY reports. A NIL value means report on all.") (defg *shorten-ht* (hl-mht :test 'eql)) (defn shorten (x n) (cond ((and (stringp x) (<= (length x) n)) x) (t (let ((x ;; Jared -- ugh, this was using maybe-str-hash directly. It ;; looks like X is supposed to be a string or symbol. Here's ;; a dumb approximation of the previous behavior: (if (stringp x) (hons-copy x) x))) (cond ((gethash x *shorten-ht*)) (t (let ((*print-pretty* nil) (*print-length* 10) (*print-level* 5) (*print-lines* 2)) (let ((str (with-output-to-string (s) (cond ((stringp x) (princ x s)) (t (prin1 x s)))))) (setf (gethash x *shorten-ht*) (cond ((<= (length str) n) str) ((setf (gethash x *shorten-ht*) (concatenate 'string (subseq str 0 (max 0 n)) "..."))))))))))))) (defg *memoize-summary-order-reversed* nil "*MEMOIZE-SUMMARY-ORDER-REVERSED* is a raw Lisp variable. When it is not NIL, then MEMOIZE-SUMMARY reports on functions in order from least to greatest.") (defg *print-alist-width* 45) (defun-one-output print-alist (alist separation) (check-type separation (integer 0)) (setq alist (loop for x in alist collect (progn (check-type x (cons (or string symbol) (cons (or string (integer 0)) null))) (list (shorten (car x) *print-alist-width*) (if (integerp (cadr x)) (ofnum (cadr x)) (cadr x)))))) (let* ((max1 (loop for x in alist maximize (length (car x)))) (max2 (loop for x in alist maximize (length (cadr x)))) (width (max (or *print-right-margin* 70) (+ separation max1 max2)))) (loop for x in alist do (fresh-line) (princ (car x)) (loop for i below (- width (+ (length (car x)) (length (cadr x)))) do (write-char #\Space)) (princ (cadr x)))) nil) ; VERY-UNSAFE-INCF (defmacro very-unsafe-incf (x inc &rest r) ; returns NIL ! (declare (ignore r)) (unless (symbolp x) ;; Compile-time sanity check (error "very-unsafe-incf should only be used on symbols, not ~a" x)) `(locally (declare (type mfixnum ,x)) (setq ,x (the mfixnum (+ ,x (the mfixnum ,inc)))) nil)) (defmacro very-very-unsafe-aref-incf (ar loc) `(setf (aref (the (simple-array mfixnum (*)) ,ar) ,loc) (the mfixnum (1+ (aref (the (simple-array mfixnum (*)) ,ar) ,loc))))) (defun-one-output pons-summary () (our-syntax-nice (let ((sssub 0) (nponses 0) (nsubs 0) (nponstab 0)) (declare (type mfixnum sssub nponses nsubs)) (oft "(defun pons-summary~%") (maphash (lambda (k v) (cond ((symbolp k) (let ((tab (symbol-value (access memoize-info-ht-entry v :ponstablename)))) (when tab (very-unsafe-incf nponstab 1 pons-summary) (maphash (lambda (k v) (declare (ignore k)) (cond ((not (listp v)) (very-unsafe-incf sssub (hash-table-size (the hash-table v)) pons-summary) (very-unsafe-incf nponses (hash-table-count (the hash-table v)) pons-summary) (very-unsafe-incf nsubs 1 pons-summary)) (t (very-unsafe-incf nponses (length v) pons-summary)))) tab)))))) *memoize-info-ht*) (print-alist `((" Pons hits/calls" ,(let* ((c *pons-call-counter*) (d *pons-misses-counter*)) (format nil "~,1e / ~,1e = ~,2f" d c (/ (- c d) (+ .0000001 c))))) (" Number of pons tables" ,(ofnum nponstab)) (" Number of pons sub tables" ,(ofnum nsubs)) (" Sum of pons sub table sizes" ,(ofnum sssub)) (" Number of ponses" ,(ofnum nponses))) 5) (oft ")") nil))) (defun memoized-values (&optional (fn (memoized-functions))) "(MEMOIZED-VALUES fn) prints all the currently memoized values for FN." (cond ((listp fn) (mapc #'memoized-values fn)) ((not (memoizedp-raw fn)) (oft "~%; Memoized-values: ~s is not memoized." fn)) (t (let ((tb (symbol-value (access memoize-info-ht-entry (gethash fn *memoize-info-ht*) :tablename)))) (cond ((and tb (not (eql 0 (hash-table-count (the hash-table tb))))) (oft "~%; Memoized values for ~s." fn) (maphash (lambda (key v) (format t "~%~s~%=>~%~s" key v)) (the hash-table tb))))))) nil) (defn print-call-stack () "(PRINT-CALL-STACK) prints the stack of memoized function calls currently running and the time they have been running." (let (l (time (internal-real-time)) (*print-case* :downcase)) (maphash (lambda (k v) (cond ((symbolp k) (let ((x (symbol-value (the symbol (access memoize-info-ht-entry v :start-time))))) (declare (type mfixnum x)) (when (> x 0) (push (cons k x) l)))))) *memoize-info-ht*) (setq l (sort l #'< :key #'cdr)) (setq l (loop for pair in l collect (list (car pair) (ofnum (/ (- time (cdr pair)) *float-ticks/second*))))) (our-syntax-brief (when - (oft "~%? ~a" -))) ;; [Jared]: what? ;; (our-syntax-brief ;; (when (boundp '*acl2--*) (oft "~%> ~a~%" *acl2--*))) (when l (terpri) (print-alist (cons '("Stack of monitored function calls." "Time since outermost call.") l) 5)))) (defun-one-output hons-calls (x) "For a memoized function fn, (HONS-CALLS fn) is the number of times fn has called hons." (setq x (coerce-index x)) (aref *memoize-call-array* (the mfixnum (+ *ma-hons-index* (the mfixnum (* *2max-memoize-fns* (the mfixnum x))))))) (defun-one-output pons-calls (x) "For a memoized function X, (PONS-CALLS x) is the number of times X has called pons." (setq x (coerce-index x)) (aref *memoize-call-array* (the mfixnum (+ *ma-pons-index* (the mfixnum (* *2max-memoize-fns* (the mfixnum x))))))) #+Clozure (defun-one-output bytes-allocated (x) "For a memoized function X, (BYTES-ALLOCATED x) is the number of heap bytes X has caused to be allocated on the heap." (setq x (coerce-index x)) (aref *memoize-call-array* (the mfixnum (+ *ma-bytes-index* (the mfixnum (* *2max-memoize-fns* (the mfixnum x))))))) (defun-one-output number-of-hits (x) "For a memoized function X, (NUMBER-OF-HITS x) is the number of times that a call of X returned a remembered answer." (setq x (coerce-index x)) (aref *memoize-call-array* (the mfixnum (+ *ma-hits-index* (the mfixnum (* *2max-memoize-fns* (the mfixnum x))))))) (defun-one-output number-of-memoized-entries (x) "For a memoized function X, (NUMBER-OF-MEMOIZED-ENTRIES x) is the number of entries currently stored for X." (let ((h (gethash x *memoize-info-ht*))) (unless h (error "~a is not memoized." x)) (let* ((sym (access memoize-info-ht-entry h :tablename)) (val (symbol-value sym))) (if (hash-table-p val) (hash-table-count (the hash-table val)) 0)))) (defun-one-output number-of-mht-calls (x) "For a memoized function X, (NUMBER-OF-MHT-CALLS x) is the number of times that the memoize hash-table for X was created." (setq x (coerce-index x)) (aref *memoize-call-array* (the mfixnum (+ *ma-mht-index* (the mfixnum (* *2max-memoize-fns* (the mfixnum x))))))) (defun-one-output time-for-non-hits/call (x) (setq x (coerce-index x)) (let ((n (- (number-of-calls x) (number-of-hits x)))) (if (zerop n) 0 (/ (total-time x) n)))) (defun-one-output time/call (x) (setq x (coerce-index x)) (let ((n (number-of-calls x))) (if (zerop n) 0 (/ (total-time x) n)))) (defun-one-output hits/calls (x) (setq x (coerce-index x)) (let ((n (number-of-calls x))) (if (zerop n) 0 (/ (number-of-hits x) (float n))))) #+Clozure (defun-one-output bytes-allocated/call (x) (setq x (coerce-index x)) (let ((n (number-of-calls x))) (if (eql n 0) 0 (/ (bytes-allocated x) n)))) (defn char-list-fraction (l) (if (atom l) 0 (+ (char-code (car l)) (/ (char-list-fraction (cdr l)) 256)))) (defn symbol-name-order (s) "SYMBOL-NAME-ORDER maps symbols to rationals preserving lexicographic order." (unless (symbolp s) (setq s (fixnum-to-symbol s))) (- (char-list-fraction (coerce (symbol-name s) 'list)))) (defun-one-output execution-order (s) (unless (symbolp s) (setq s (fixnum-to-symbol s))) (the mfixnum (symbol-value (the symbol (access memoize-info-ht-entry (gethash s *memoize-info-ht*) :start-time))))) (defn compute-calls-and-times () (let ((ma *memoize-call-array*) (2m *2max-memoize-fns*) (ca *compute-array*) (n (the fixnum (1+ *max-symbol-to-fixnum*)))) (declare (type (simple-array mfixnum (*)) ma) (type (simple-array t (*)) ca) (type fixnum 2m n)) (cond ((eql (length ca) n) (loop for i fixnum below n do (setf (aref ca i) nil))) (t (setq *compute-array* (make-array n :initial-element nil)) (setq ca *compute-array*))) (loop for i fixnum below (the fixnum (* 2 n)) do (setf (aref ma i) 0)) (loop for i fixnum from *ma-initial-max-symbol-to-fixnum* to *max-symbol-to-fixnum* do (let ((2im (the fixnum (* i 2m)))) (declare (type fixnum 2im)) (loop for j fixnum from *ma-initial-max-symbol-to-fixnum* to *max-symbol-to-fixnum* do (let* ((2j (the fixnum (* 2 j))) (index (the fixnum (+ 2j 2im)))) (declare (type fixnum 2j index)) (let ((calls (the mfixnum (aref ma index)))) (declare (type mfixnum calls)) (when (> calls 0) (let ((time (aref ma (the mfixnum (1+ index))))) (declare (type mfixnum time)) (setf (aref ma 2j) (the mfixnum (+ (aref ma 2j) calls))) (setf (aref ma (the mfixnum (1+ 2j))) (the mfixnum (+ (aref ma (the mfixnum (1+ 2j))) time))) (push i (aref ca j)))))))))) ) (defun-one-output number-of-calls (x) (setq x (coerce-index x)) ; One must call COMPUTE-CALLS-AND-TIMES before invoking ; NUMBER-OF-CALLS to get sensible results. (aref *memoize-call-array* (the mfixnum (* 2 (the mfixnum x))))) (defun-one-output print-not-called () "(PRINT-NOT-CALLED) prints, one to a line, in alphabetic order, the currently memoized functions that have never been called. Possibly useful when looking for test coverage." (compute-calls-and-times) (let ((ans nil)) (maphash (lambda (k v) (declare (ignore v)) (when (and (symbolp k) (eql 0 (number-of-calls k))) (push k ans))) *memoize-info-ht*) (loop for x in (sort ans 'alphorder) do (print x)))) (defun-one-output total-time (x) (setq x (coerce-index x)) ; One must call COMPUTE-CALLS-AND-TIMES before invoking ; TOTAL-TIME to get sensible results. (/ (aref *memoize-call-array* (the fixnum (1+ (the fixnum (* 2 x))))) *float-ticks/second*)) (defn lex-> (l1 l2) (cond ((or (atom l1) (atom l2)) nil) ((> (car l1) (car l2)) t) ((< (car l1) (car l2)) nil) (t (lex-> (cdr l1) (cdr l2))))) (defun-one-output memoize-summary-sort () (let (pairs) (maphash (lambda (fn v) (when (symbolp fn) (let ((num (access memoize-info-ht-entry v :num))) (declare (type fixnum num)) (when (< 0 (number-of-calls num)) (push (cons fn (loop for order in *memoize-summary-order-list* collect (funcall order fn))) pairs))))) *memoize-info-ht*) (sort pairs (if *memoize-summary-order-reversed* (lambda (x y) (lex-> y x)) #'lex->) :key #'cdr))) (defun-one-output memoize-summary () "(MEMOIZE-SUMMARY) reports data stored during the execution of the functions in (MEMOIZED-FUNCTIONS). Typically each call of a memoized function, fn, is counted. The elapsed time until an outermost function call of fn ends, the number of heap bytes allocated in that period (CCL only), and other 'charges' are 'billed' to fn. That is, quantities such as elapsed time and heap bytes allocated are not charged to subsidiary recursive calls of fn while an outermost call of fn is running. Recursive calls of fn, and memoized 'hits', are counted, unless fn was memoized with NIL as the value of the :INLINE parameter of MEMOIZE. The settings of the following determine, at the time a function is given to MEMOIZE, the information that is collected for calls of the function: Variable type *RECORD-BYTES* boolean (available in CCL only) *RECORD-CALLS* boolean *RECORD-HITS* boolean *RECORD-HONS-CALLS* boolean *RECORD-MHT-CALLS* boolean *RECORD-PONS-CALLS* boolean *RECORD-TIME* boolean The settings of the following determine, at the time that MEMOIZE-SUMMARY is called, what information is printed: *REPORT-BYTES* boolean (available in CCL only) *REPORT-CALLS* boolean *REPORT-CALLS-FROM* boolean *REPORT-CALLS-TO* boolean *REPORT-HITS* boolean *REPORT-HONS-CALLS* boolean *REPORT-MHT-CALLS* boolean *REPORT-PONS-CALLS* boolean *REPORT-TIME* boolean *REPORT-ON-MEMO-TABLES* boolean *REPORT-ON-PONS-TABLES* boolean *MEMOIZE-SUMMARY-LIMIT* (or integerp null) *MEMOIZE-SUMMARY-ORDER-LIST* (symbol symbol ... symbol) *MEMOIZE-SUMMARY-ORDER-REVERSED* boolean Functions are sorted lexicographically according to the ordering induced by the function names in *MEMOIZE-SUMMARY-ORDER-LIST*, each member of which must be a unary function that returns a rational. (CLEAR-MEMOIZE-TABLES) forgets the remembered values of all memoized function calls. It does not alter a function's status as being a memoized function, nor does not it the monitoring data accumulated. (UNMEMOIZE-ALL) undoes the memoization status of all memoized functions. (CLEAR-MEMOIZE-CALL-ARRAY) zeroes out the monitoring information for all functions. It does not alter any function's status as a memoized function nor does it change the values remembered for any memoized function. Here is an example of hacking with *MEMOIZE-SUMMARY-ORDER-LIST* that instructs MEMOIZE-SUMMARY to print information about, say, 1ST-MOD-ERR first: (PUSH (LAMBDA (FN) (IF (EQ FN '1ST-MOD-ERR) 1 0)) *MEMOIZE-SUMMARY-ORDER-LIST*)." (compute-calls-and-times) (memoize-summary-after-compute-calls-and-times) nil) (defg *short-symbol-name-width* 30) (defn short-symbol-name (sym) (let ((str (symbol-name sym))) (cond ((> (length str) *short-symbol-name-width*) (intern (format nil "~a..." (subseq str 0 *short-symbol-name-width*)) (symbol-package sym))) (t sym)))) (defun-one-output outside-p (x) (or (<= x *ma-initial-max-symbol-to-fixnum*) (null (gethash x *memoize-info-ht*)))) (defun-one-output memoize-summary-after-compute-calls-and-times () ; If COMPUTE-CALLS-AND-TIMES is not called shortly before this ; function, MEMOIZE-SUMMARY-AFTER-COMPUTE-CALLS-AND-TIMES, is called, ; the information reported may be quite untimely. (let* ((fn-pairs (memoize-summary-sort)) (ma *memoize-call-array*) (len-orig-fn-pairs (len fn-pairs)) (len-fn-pairs 0) (global-total-time 0) #+Clozure (global-bytes-allocated 0) (global-hons-calls 0) (global-pons-calls 0)) (declare (type (simple-array mfixnum (*)) ma) (type fixnum len-orig-fn-pairs len-fn-pairs)) (when (and *memoize-summary-limit* (> len-orig-fn-pairs *memoize-summary-limit*)) (setq fn-pairs (loop for i fixnum from 1 to *memoize-summary-limit* as x in fn-pairs collect x))) (setq len-fn-pairs (len fn-pairs)) (when (> len-fn-pairs 0) (oft "~%Sorted by *memoize-summary-order-list* = ~a." *memoize-summary-order-list*) (when (< len-fn-pairs len-orig-fn-pairs) (oft "~%Reporting on ~:d of ~:d functions because ~ *memoize-summary-limit* = ~a." len-fn-pairs len-orig-fn-pairs *memoize-summary-limit*))) (setq global-total-time (loop for pair in fn-pairs sum (total-time (car pair)))) #+Clozure (setq global-bytes-allocated (+ 1 (loop for pair in fn-pairs sum (bytes-allocated (car pair))))) (setq global-hons-calls (+ 1 (loop for pair in fn-pairs sum (hons-calls (car pair))))) (setq global-pons-calls (+ 1 (loop for pair in fn-pairs sum (pons-calls (car pair))))) (when (null fn-pairs) (oft "~%(memoize-summary) has nothing to report.~%")) (loop for pair in fn-pairs do (let* ((fn (car pair)) (l (gethash fn *memoize-info-ht*)) (tablename (symbol-value (access memoize-info-ht-entry l :tablename))) (ponstablename (symbol-value (access memoize-info-ht-entry l :ponstablename))) (start-time (the mfixnum (symbol-value (the symbol (access memoize-info-ht-entry l :start-time))))) (num (the fixnum (access memoize-info-ht-entry l :num))) (nhits (the mfixnum (number-of-hits num))) (nmht (the mfixnum (number-of-mht-calls num))) (ncalls (the mfixnum (max 1 (the mfixnum (number-of-calls num))))) (hons-calls (the mfixnum (hons-calls num))) (pons-calls (the mfixnum (pons-calls num))) (no-hits (or (not *report-hits*) (null (memoize-condition fn)))) #+Clozure (bytes-allocated (bytes-allocated num)) (tt (max .000001 (total-time num))) (t/c (time/call num)) (tnh (time-for-non-hits/call num)) (in-progress-str (if (eql start-time -1) " " ", running, ")) (selftime tt)) (declare (type integer start-time) (type fixnum num) (type mfixnum nhits nmht ncalls hons-calls pons-calls #+Clozure bytes-allocated)) (print-alist `((,(format nil "(defun ~s~a~a" (short-symbol-name fn) (if no-hits (ofn " call~a" (if (eql nhits 1) "" "s")) " hits/calls") in-progress-str) ,(if (or *report-calls* *report-hits*) (if no-hits (format nil "~a" (ofnum ncalls)) (format nil "~8,2e/~8,2e = ~4,1f%" nhits ncalls (* 100 (/ nhits (float ncalls))))) "")) ,@(if (and *report-mht-calls* (>= nmht 2)) `((" Number of calls to mht" ,(ofnum nmht)))) ,@(if *report-time* `((" Time of all outermost calls" ,(format nil "~a; ~4,1f%" (ofnum tt) (* 100 (/ tt global-total-time)))) (" Time per call" ,(ofnum t/c)))) ,@(if (let ((v (gethash fn *memoize-info-ht*))) (and (null (access memoize-info-ht-entry v :condition)) (null (access memoize-info-ht-entry v :inline)) (< t/c 1e-6))) `((,(format nil " Doubtful timing info for ~a." fn) "Heisenberg effect."))) #+Clozure ,@(if (and (> bytes-allocated 0) *report-bytes*) `((" Heap bytes allocated" ,(format nil "~a; ~4,1f%" (ofnum bytes-allocated) (* 100 (/ bytes-allocated global-bytes-allocated)))) (" Heap bytes allocated per call" ,(ofnum (/ bytes-allocated ncalls))))) ,@(if (and (> hons-calls 0) (> global-hons-calls 0) *report-hons-calls*) `((" Hons calls" ,(format nil "~a; ~4,1f%" (ofnum hons-calls) (* 100 (/ hons-calls global-hons-calls)))))) ,@(if (and (> pons-calls 0) (> global-pons-calls 0) *report-pons-calls*) `((" Pons calls" ,(format nil "~a; ~4,1f%" (ofnum pons-calls) (* 100 (/ pons-calls global-pons-calls)))))) ,@(if (and *report-hits* *report-time* (not (eql 0 nhits))) `((" Time per missed call" ,(ofnum tnh)))) ,@(if *report-calls-to* (let ((l nil) (outside-fn-time 0) (outside-fn-count 0)) (declare (type mfixnum outside-fn-count)) (loop for callern in (loop for i below (length *compute-array*) when (member num (aref *compute-array* i)) collect i) do (let* ((call-loc (the fixnum (+ (* 2 callern) (the fixnum (* num *2max-memoize-fns*))))) (calls (aref ma call-loc))) (declare (fixnum call-loc) (type mfixnum calls)) (let* ((time-loc (the fixnum (1+ call-loc))) (time (aref ma time-loc)) (ltt (/ time *float-ticks/second*))) (decf selftime ltt) (cond ((or (outside-p callern) (< (* 100 ltt) tt)) (incf outside-fn-time ltt) (incf outside-fn-count calls)) (t (push `((,(format nil " To ~a" (fixnum-to-symbol callern)) ,(format nil "~8,2e calls took~8,2e;~5,1f%" calls ltt (if (> (* 10000 ltt) tt) (* 100 (/ ltt tt)) '?))) . ,(if *sort-to-from-by-calls* calls time)) l)))))) (when (> outside-fn-time 0) (push `((,(format nil " To other functions") ,(format nil "~8,2e calls took~8,2e;~5,1f%" outside-fn-count outside-fn-time (if (> (* 10000 outside-fn-time) tt) (* 100 (/ outside-fn-time tt)) '?))) . ,(if *sort-to-from-by-calls* outside-fn-count outside-fn-time)) l)) (when (and (> selftime 0) (not (= selftime tt))) (push `((,(ofn " To self/unprofiled functions") ,(ofn "~8,2e;~5,1f%" selftime (if (> (* 10000 selftime) tt) (* 100 (/ selftime tt)) '?))) . ,(if *sort-to-from-by-calls* 0 ;; ? (* selftime *float-ticks/second*))) l)) (setq l (sort l #'> :key #'cdr)) ; (cw "l: ~x0~%" l) (strip-cars l))) ,@(if *report-calls-from* (let ((l nil) (2num (the fixnum (* 2 num))) (outside-caller-time 0) (outside-caller-count 0)) (declare (fixnum 2num)) (loop for callern in (aref *compute-array* num) do (let* ((call-loc (the fixnum (+ 2num (the fixnum (* callern *2max-memoize-fns*))))) (calls (aref ma call-loc))) (declare (fixnum call-loc) (type mfixnum calls)) (let* ((time-loc (the fixnum (1+ call-loc))) (time (aref ma time-loc)) (ltt (/ time *float-ticks/second*))) (cond ((or (outside-p callern) (< (* 100 ltt) tt)) (incf outside-caller-time ltt) (incf outside-caller-count calls)) (t (push `((,(ofn " From ~a" (fixnum-to-symbol callern)) ,(ofn "~8,2e calls took~8,2e;~5,1f%" calls ltt (if (< .001 ltt tt) (* 100 (/ ltt tt)) '?))) . ,(if *sort-to-from-by-calls* calls time)) l)))))) (when (> outside-caller-time 0) (push `((,(ofn " From other functions") ,(ofn "~8,2e calls took~8,2e;~5,1f%" outside-caller-count outside-caller-time (if (< .001 outside-caller-time tt) (* 100 (/ outside-caller-time tt)) '?))) . ,(if *sort-to-from-by-calls* outside-caller-count outside-caller-time)) l)) (setq l (sort l #'> :key #'cdr)) (strip-cars l))) . ,(if (and (not *report-on-memo-tables*) (not *report-on-pons-tables*)) nil (let ((spsub 0) (nponses 0) (npsubs 0)) (declare (type mfixnum spsub nponses npsubs)) (and (and ponstablename *report-on-pons-tables*) (maphash (lambda (key value) (declare (ignore key)) (cond ((not (listp value)) (very-unsafe-incf spsub (hash-table-size (the hash-table value)) memoize-summary) (very-unsafe-incf nponses (hash-table-count (the hash-table value)) memoize-summary) (very-unsafe-incf npsubs 1 memoize-summary)) (t (very-unsafe-incf nponses (length value) memoize-summary)))) ponstablename)) `(,@(and (and tablename *report-on-memo-tables*) `((,(ofn " Memoize table count/size") ,(ofn "~8,2e/~8,2e = ~4,1f%" (hash-table-count (the hash-table tablename)) (hash-table-size (the hash-table tablename)) (* 100 (/ (hash-table-count (the hash-table tablename)) (hash-table-size (the hash-table tablename)))))))) ,@(and (and ponstablename *report-on-pons-tables*) `((" (Pons table count/size" ,(ofn "~:d/~:d = ~4,1f%)" (hash-table-count (the hash-table ponstablename)) (hash-table-size (the hash-table ponstablename)) (* 100 (/ (hash-table-count (the hash-table ponstablename)) (hash-table-size (the hash-table ponstablename)))))) (" (Number of pons sub tables" ,(ofn "~a)" (ofnum npsubs))) (" (Sum of pons sub table sizes" ,(ofn "~a)" (ofnum spsub))) (" (Number of ponses" ,(ofn "~a)" (ofnum nponses))))))))) 5)) (oft ")")))) (defun-one-output empty-ht-p (x) (and (hash-table-p x) (eql 0 (hash-table-count (the hash-table x))))) (defn clear-one-memo-and-pons-hash (l) ; It is debatable whether one should use the CLRHASH approach or ; the set-to-NIL approach in CLEAR-ONE-MEMO-AND-PONS-HASH. The ; CLRHASH approach, in addition to reducing the number of ; MAKE-HASH-TABLE calls necessary, has the effect of immediately ; clearing a hash-table even if some other function is holding on ; to it, so more garbage may get garbage collected sooner than ; otherwise. The set-to-NIL approach has the advantage of costing ; very few instructions and very little paging. (let* ((fn (access memoize-info-ht-entry l :fn)) (mt (symbol-value (access memoize-info-ht-entry l :tablename))) (pt (symbol-value (access memoize-info-ht-entry l :ponstablename))) (mt-count (and mt (hash-table-count mt))) (pt-count (and pt (hash-table-count pt)))) (when mt (setf (symbol-value (access memoize-info-ht-entry l :tablename)) nil)) (when pt (setf (symbol-value (access memoize-info-ht-entry l :ponstablename)) nil)) (when (or mt-count pt-count) (update-memo-max-sizes fn (or pt-count 1) (or mt-count 1))) nil)) (defun-one-output clear-memoize-table (k) ; See also hons.lisp. (when (symbolp k) (let ((l (gethash k *memoize-info-ht*))) (when l (clear-one-memo-and-pons-hash l)))) k) (defun-one-output clear-memoize-tables () ; See hons.lisp. (let (success) (unwind-protect (progn (maphash (lambda (k l) (when (symbolp k) (clear-one-memo-and-pons-hash l))) *memoize-info-ht*) (setq success t)) (or success (error "clear-memoize-tables failed.")))) nil) (defn clear-memoize-call-array () "(CLEAR-MEMOIZE-CALL-ARRAY) zeros out all records of function calls as far as reporting by MEMOIZE-SUMMARY, etc. is concerned." (let ((m *memoize-call-array*)) (declare (type (simple-array mfixnum (*)) m)) (loop for i fixnum below (length m) do (setf (aref m i) 0)))) (defn clear-memoize-statistics () (clear-memoize-call-array) nil) (defun-one-output memoize-init () (when *memoize-init-done* ;; Already done (return-from memoize-init nil)) (unless (eql *caller* (the fixnum (* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*))) (error "memoize-init: A memoized function is running.")) (let (success) (unwind-protect (progn (setq *pons-call-counter* 0) (setq *pons-misses-counter* 0) (setq *memoize-info-ht* (hl-mht)) (setf (gethash *ma-initial-max-symbol-to-fixnum* *memoize-info-ht*) "outside-caller") (setq *max-symbol-to-fixnum* *ma-initial-max-symbol-to-fixnum*) (setq *2max-memoize-fns* (* 2 *initial-max-memoize-fns*)) (sync-memoize-call-array) (setq success t)) (if success (setq *memoize-init-done* t) (error "memoize-init failed.")))) nil) ;; [Jared and Sol]: It is gross to leave these in here, but we're going to do ;; it, because right now they're used within WATCH. If we eventually decide ;; that WATCH is deprecated or anything like that, we can move these to ;; centaur/misc/memory-mgmt, where they are actually used now. (defg *max-mem-usage* (expt 2 32) "*MAX-MEM-USAGE* an upper bound, in bytes of memory used, that when exceeded results in certain garbage collection actions. Note: not used by ACL2(h) itself; see the centaur/misc/memory-mgmt books.") (defg *gc-min-threshold* (expt 2 30) "Note: not used by ACL2(h) itself; see the centaur/misc/memory-mgmt books.") #+Clozure (defun-one-output set-gc-threshold (bound) ;; bozo used? (when (< (ccl::lisp-heap-gc-threshold) bound) (ccl::set-lisp-heap-gc-threshold bound) (ccl::use-lisp-heap-gc-threshold)) nil) #+Clozure (defmacro globlet (bindings &rest rest) ;; [Jared]: this is only used in with-lower-overhead AFAICT. "GLOBLET is reminiscent of LET. It is intended to be used in CCL with variables that are introduced via DEFG or DEFV, i.e., may not be LET or LAMBDA bound. UNWIND-PROTECT is used to try to make sure that the old value, which is assumed to exist, is restored." (unless (and (symbol-alistp bindings) (loop for pair in bindings always (let ((var (car pair)) (d (cdr pair))) (and (consp d) (null (cdr d)) (not (constantp var)))))) (error "GLOBLET: ** bad bindings ~a." bindings)) (let ((vars (loop for b in bindings collect (make-symbol (symbol-name (car b)))))) `(let ,(loop for b in bindings as v in vars collect (list v (car b))) (unwind-protect (progn (psetq ,@(loop for b in bindings nconc (list (car b) (cadr b)))) ,@rest) (psetq ,@(loop for b in bindings as v in vars nconc (list (car b) v))))))) #-Clozure (defmacro globlet (&rest r) ; See #+Clozure definition for an explanation of why we need this macro. In ; Lispworks, we get this warning if we use the #+Clozure definition: ; ;;;*** Warning in ACL2::HONS-INIT-HOOK-MEMOIZATIONS: #:*COUNT-PONS-CALLS* bound lexically. ; So we use a much simpler definition here. `(let ,@r)) (defmacro with-lower-overhead (&rest r) `(let ((*record-bytes* nil) (*record-calls* ; It is a mystery why we need to set *record-calls* to t in LispWorks. But we ; do. Otherwise, for example, evaluation hangs for (bad-lisp-objectp ; (make-list 100000)) when bad-lisp-objectp is in the initial memoized state ; produced by calling acl2h-init (see acl2h-init-memoizations) #-lispworks nil #+lispworks t) (*record-hits* nil) (*record-hons-calls* nil) (*record-mht-calls* nil) (*record-pons-calls* nil) (*record-time* nil)) (globlet ((*count-pons-calls* nil)) ,@ r))) ; [Jared]: this acl2h-init-memoizations stuff is almost certainly broken in ; some sense. Why unmemoize/rememoize exactly these functions? The answer is ; probably: it works for using ACL2(p) in most books, where no other ; memoization has taken place. But this is just an accident. Really ACL2(p) ; needs a way to turn off memoization globally for awhile, unless we're going ; going to get to a point where memoization is thread-safe. (defun acl2h-init-memoizations () ; Keep in sync with acl2h-init-unmemoizations. ; We pull out the memoization calls so we can unmemoize and re-memoize these ; functions when the user enables and disables waterfall parallelism, ; respectively. (when (not (memoizedp-raw 'bad-lisp-objectp)) (with-lower-overhead (memoize-fn 'bad-lisp-objectp :forget t))) (when (not (memoizedp-raw 'worse-than-builtin)) ;; Warning: If this is changed or removed, visit the comment in ;; worse-than-builtin. (with-lower-overhead (memoize-fn 'worse-than-builtin :condition ; Sol Swords suggestion '(and (nvariablep term1) (not (fquotep term1)) (nvariablep term2) (not (fquotep term2)))))) (when (not (memoizedp-raw 'fchecksum-obj)) ;; Warning: If this memoization is removed, modify the comment in ;; fchecksum-obj about memoization of that function. (with-lower-overhead (memoize-fn 'fchecksum-obj :forget t))) (when (not (memoizedp-raw 'expansion-alist-pkg-names-memoize)) ;; Warning: If this memoization is removed, modify the comment in ;; expansion-alist-pkg-names about memoization of that function. (with-lower-overhead (memoize-fn 'expansion-alist-pkg-names-memoize :forget t))) ) (defun acl2h-init-unmemoizations () ; Keep in sync with acl2h-init-memoizations. (when (memoizedp-raw 'bad-lisp-objectp) (unmemoize-fn 'bad-lisp-objectp)) (when (memoizedp-raw 'worse-than-builtin) (unmemoize-fn 'worse-than-builtin)) (when (memoizedp-raw 'fchecksum-obj) (unmemoize-fn 'fchecksum-obj)) (when (memoizedp-raw 'expansion-alist-pkg-names-memoize) (unmemoize-fn 'expansion-alist-pkg-names-memoize))) ;;;;;;;;;; ;;; Start memory management code (start-sol-gc) ;;;;;;;;;; ; This section of code was suggested by Jared Davis as a way to regain ; performance of ACL2(h) on regressions at UT CS. Initially, these regressions ; shows significant slowdown upon including new memoization code from Centaur ; on 3/28/2013: ; ; old: ; 24338.570u 1357.200s 1:19:02.75 541.7% 0+0k 0+1918864io 0pf+0w ; ; new: ; 33931.460u 1017.070s 1:43:24.28 563.2% 0+0k 392+1931656io 0pf+0w ; After restoring (start-sol-gc) in function acl2h-init, we regained the old ; level of performance for a UT CS ACL2(h) regression, with the new memoizaion ; code. (defun looking-at (str1 str2 &key (start1 0) (start2 0)) ; (LOOKING-AT str1 str2 :start1 s1 :start2 s2) is non-NIL if and only ; if string STR1, from location S1 to its end, is an initial segment ; of string STR2, from location S2 to its end. (unless (typep str1 'simple-base-string) (error "looking at: ~a is not a string." str1)) (unless (typep str2 'simple-base-string) (error "looking at: ~a is not a string." str2)) (unless (typep start1 'fixnum) (error "looking at: ~a is not a fixnum." start1)) (unless (typep start2 'fixnum) (error "looking at: ~a is not a fixnum." start2)) (locally (declare (simple-base-string str1 str2) (fixnum start1 start2)) (let ((l1 (length str1)) (l2 (length str2))) (declare (fixnum l1 l2)) (loop (when (>= start1 l1) (return t)) (when (or (>= start2 l2) (not (eql (char str1 start1) (char str2 start2)))) (return nil)) (incf start1) (incf start2))))) (defun meminfo (pat) ; General comment about PROBE-FILE. PROBE-FILE, according to Gary ; Byers, may reasonably cause an error. He is undoubtedly right. In ; such cases, however, Boyer generally thinks and wishes that it ; returned NIL, and generally, therefore, ensconces a PROBE-FILE ; within an IGNORE-ERROR in his code. (or (and (our-ignore-errors (probe-file "/proc/meminfo")) (with-standard-io-syntax ; Note for GCL: ; As of late May 2013, with-standard-io-syntax seems to work properly in ANSI ; GCL. If necessary one could use our-with-standard-io-syntax here, but better ; would be to use an up-to-date ANSI GCL. (with-open-file (stream "/proc/meminfo") (let (line) (loop while (setq line (read-line stream nil nil)) do (when (looking-at pat line) (return (values (read-from-string line nil nil :start (length pat)))))))))) 0)) (let ((physical-memory-cached-answer nil)) (defun physical-memory () (or physical-memory-cached-answer (setq physical-memory-cached-answer (meminfo "MemTotal:"))))) (defvar *sol-gc-installed* nil) #+Clozure (defun set-and-reset-gc-thresholds () (let ((n (max (- *max-mem-usage* (ccl::%usedbytes)) *gc-min-threshold*))) (cond ((not (eql n (ccl::lisp-heap-gc-threshold))) (ccl::set-lisp-heap-gc-threshold n) ;; Commented out since ofvv no longer is defined: ; (ofvv "~&; set-and-reset-gc-thresholds: Reserving ~:d additional bytes.~%" ; n) ))) (ccl::use-lisp-heap-gc-threshold) ; (ofvv "~&; set-and-reset-gc-thresholds: Calling ~ ; ~%(use-lisp-heap-gc-threshold).") (cond ((not (eql *gc-min-threshold* (ccl::lisp-heap-gc-threshold))) (ccl::set-lisp-heap-gc-threshold *gc-min-threshold*) ;; Commented out since ofvv no longer is defined: ; (ofvv "~&; set-and-reset-gc-thresholds: Will reserve ~:d bytes after ;next GC.~%" ; *gc-min-threshold*) ))) #+Clozure (defun start-sol-gc () ; Sol Sword's scheme to control GC in CCL ; ; The goal is to get CCL to perform a GC whenever we're using almost ; all the physical memory, but not otherwise. ; ; The usual way of controlling GC on CCL is via LISP-HEAP-GC-THRESHOLD. ; This value is approximately amount of memory that will be allocated ; immediately after GC. This means that the next GC will occur after ; LISP-HEAP-GC-THRESHOLD more bytes are used (by consing or array ; allocation or whatever.) But this means the total memory used by the ; time the next GC comes around is the threshold plus the amount that ; remained in use at the end of the previous GC. This is a problem ; because of the following scenario: ; ; - We set the LISP-HEAP-GC-THRESHOLD to 3GB since we'd like to be able ; to use most of the 4GB physical memory available. ; ; - A GC runs or we say USE-LISP-HEAP-GC-THRESHOLD to ensure that 3GB ; is available to us. ; ; - We run a computation until we've exhausted this 3GB, at which point ; a GC occurs. ; ; - The GC reclaims 1.2 GB out of the 3GB used, so there is 1.8 GB ; still in use. ; ; - After GC, 3GB more is automatically allocated -- but this means we ; won't GC again until we have 4.8 GB in use, meaning we've gone to ; swap. ; ; What we really want is, instead of allocating a constant additional ; amount after each GC, to allocate up to a fixed total amount including ; what's already in use. To emulate that behavior, we use the hack ; below. This operates as follows (assuming the same 4GB total physical ; memory as in the above example:) ; ; 1. We set the LISP-HEAP-GC-THRESHOLD to (3.5G - used bytes) and call ; USE-LISP-HEAP-GC-THRESHOLD so that our next GC will occur when we've ; used a total of 3.5G. ; ; 2. We set the threshold back to 1GB without calling ; USE-LISP-HEAP-GC-THRESHOLD. ; ; 3. Run a computation until we use up the 3.5G and the GC is called. ; Say the GC reclaims 1.2GB so there's 2.3GB in use. 1GB more (the ; current LISP-HEAP-GC-THRESHOLD) is allocated so the ceiling is 3.3GB.) ; ; 4. A post-GC hook runs which again sets the threshold to (3.5G - ; used bytes), calls USE-LISP-HEAP-GC-THRESHOLD to raise the ceiling to ; 3.5G, then sets the threshold back to 1GB, and the process repeats. ; ; A subtlety about this scheme is that post-GC hooks runs in a separate ; thread from the main execution. A possible bug is that in step 4, ; between checking the amount of memory in use and calling ; USE-LISP-HEAP-GC-THRESHOLD, more memory might be used up by the main ; execution, which would set the ceiling higher than we intended. To ; prevent this, we interrupt the main thread to run step 4. ; The following settings are highly heuristic. We arrange that gc ; occurs at 1/8 of the physical memory size in bytes, in order to ; leave room for the gc point to grow (as per ; set-and-reset-gc-thresholds). If we can determine the physical ; memory; great; otherwise we assume that it it contains at least 4GB, ; a reasonable assumption we think for anyone using the HONS version ; of ACL2. (let* ((phys (physical-memory)) (memsize (cond ((> phys 0) (* phys 1024)) ; to bytes (t (expt 2 32))))) (setq *max-mem-usage* (min (floor memsize 8) (expt 2 31))) (setq *gc-min-threshold* (floor *max-mem-usage* 4))) ; OLD COMMENT: ; Trigger GC after we've used all but (1/8, but not more than 1 GB) of ; the physical memory. (unless *sol-gc-installed* (ccl::add-gc-hook #'(lambda () (ccl::process-interrupt (slot-value ccl:*application* 'ccl::initial-listener-process) #'set-and-reset-gc-thresholds)) :post-gc) (setq *sol-gc-installed* t)) (set-and-reset-gc-thresholds)) ;;;;;;;;;; ;;; End of memory management code (start-sol-gc) ;;;;;;;;;; (defun-one-output acl2h-init () ; We assume ACL2-DEFAULT-RESTART will be called at least once. We suspect it ; will be called whenever an ACL2H CCL saved image is started up. We know that ; ACL2-DEFAULT-RESTART calls ACL2H-INIT. We are unsure how many times ; ACL2-DEFAULT-RESTART might be called, and so we code ACL2H-INIT so that it ; may be called multiple times. ;; [Jared]: if this in-package matters, I don't understand anything. ;; (in-package "ACL2") (memoize-init) (float-ticks/second-init) ;; [Jared]: Not sure whether we care about this anymore. With serialize ;; we probably do not. It might be best NOT to mess with this to ;; minimize differences between ACL2 and ACL2(h). Trying to remove it. ;; "If the ACL2 global PRINT-CIRCLE is not t, then .cert files may be huge." ;; (f-put-global 'print-circle t *the-live-state*) ;; (f-put-global 'print-circle-files t *the-live-state*) #+static-hons ;; [Jared]: With *print-array* turned on, we end up sometimes seeing the ;; SBITS array in backtraces, etc, which can effectively kill your session ;; (can't interrupt, etc.). This is only a problem with static-honsing since ;; classic honsing doesn't have sbits anyway. (setq *print-array* nil) ;; [Jared]: why do we do this? presumably it affects raw lisp. should this ;; be anything we care about? Maybe ACL2 should set this instead of ACL2(h). (setq *print-pretty* t) ;; The following is restored after including mods from Jared Davis, ;; 3/29/2013. See comment "Start memory management code (start-sol-gc)". #+Clozure (start-sol-gc) (acl2h-init-memoizations) #+Clozure (progn ;; [Jared]: Is there any reason to care about site names? I'm going ;; to remove these for now. ;; (setq ccl::*long-site-name* ;; (ignore-errors (ccl::getenv "HOSTNAME"))) ;; (setq ccl::*short-site-name* ;; (subseq ccl::*long-site-name* ;; 0 (search "." ccl::*long-site-name*))) ;; [Jared]: Is there any reason to mess with this? The ;; *print-right-margin* is a CLHS thing, not CCL-specific, so why it ;; this in #+Clozure? How about we don't mess with it. ;; (setq *print-right-margin* ;; (ignore-errors (read-from-string (ccl::getenv "COLUMNS")))) "Permit FUNCTION-LAMBDA-EXPRESSION to return the form used in the DEFUN of a function symbol." ;; [Jared]: How many of these are actually needed for FLE to work? Which ;; of them do we actually want? See also comments in acl2.lisp, "Some ;; hacks for CCL", where these settings used to be getting redundantly set. ;; What do these do? (setq ccl::*save-definitions* t) (setq ccl::*save-source-locations* ;; This contradicts setting in acl2.lisp; do we need it? t) (setq ccl::*fasl-save-definitions* t) (setq ccl::*record-source-file* t) "Allow control-d to exit from CCL." ;; [Jared]: This is useful, but, shouldn't it be part of just ordinary ACL2 ;; instead of ACL2(h), though? Or shouldn't we have it as part of our ;; lisp-config file, if we ever implement that sort of thing? (setq ccl::*quit-on-eof* t) "It is usually best for the user to know what the garbage collector is doing when using HONS and MEMOIZE." ;; [Jared]: This sets full-gc messages on but leaves EGC messages off. ;; We're disabling EGC anyway, but when we've experimented with leaving it ;; on, the EGC messages are way too verbose. (ccl::gc-verbose t nil) ;; [Jared]: we turn off EGC because it doesn't seem to work well with ;; memoizing worse-than-builtin and sometimes seems buggy; but we want to ;; investigate this more. (ccl::egc nil)) nil) ;;; SHORTER, OLDER NAMES ; Note: memsum is defined in memoize.lisp. (defun memstat (&rest r) (apply #'memoized-values r)) (defmacro memo-on (&rest r) `(memoize-on ,@r)) (defmacro memo-off (&rest r) `(memoize-off ,@r)) (defun clear-memo-tables (&rest r) (setq *pons-call-counter* 0) (setq *pons-misses-counter* 0) (apply #'clear-memoize-tables r)) (defn lower-overhead () ;; Doesn't help much. (setq *record-bytes* nil) (setq *record-calls* ; See the comment about LispWorks in with-lower-overhead; we make the analogous ; adjustment for LispWorks here, in case it's necessary. #-lispworks nil #+lispworks t) (setq *record-hits* nil) (setq *record-hons-calls* nil) (setq *record-mht-calls* nil) (setq *record-pons-calls* nil) (setq *record-time* nil) (setq *count-pons-calls* nil)) #+Clozure (defun our-gctime () (ccl::timeval->microseconds ccl::*total-gc-microseconds*)) (defun update-memo-entry-for-attachments (fns entry wrld) ; We return (mv changed-p new-entry), where if changed-p is not t or nil then ; it is a function symbol whose attachment has changed, which requires clearing ; of the corresponding memo table. (let* ((ext-anc-attachments (access memoize-info-ht-entry entry :ext-anc-attachments)) (valid-p (if (eq fns :clear) :clear (or (null ext-anc-attachments) (ext-anc-attachments-valid-p fns ext-anc-attachments wrld t))))) (cond ((eq valid-p t) (mv nil entry)) (t (mv (if (eq valid-p nil) t valid-p) (change memoize-info-ht-entry entry :ext-anc-attachments (extended-ancestors (access memoize-info-ht-entry entry :fn) wrld))))))) (defun update-memo-entries-for-attachments (fns wrld state) (let ((ctx 'top-level) (fns (if (eq fns :clear) fns (strict-merge-sort-symbol-< (loop for fn in fns collect (canonical-sibling fn wrld)))))) (when (eq fns :clear) (observation ctx "Memoization tables for functions memoized with :AOKP T ~ are being cleared.")) (when fns ; optimization (maphash (lambda (k entry) (when (symbolp k) (mv-let (changedp new-entry) (update-memo-entry-for-attachments fns entry wrld) (when changedp (when (not (or (eq changedp t) (eq fns :clear))) (observation ctx "Memoization table for function ~x0 ~ is being cleared because ~ attachment to function ~x1 has ~ changed." k changedp) (clear-one-memo-and-pons-hash entry)) (setf (gethash k *memoize-info-ht*) new-entry))))) *memoize-info-ht*)))) acl2-sources/multi-threading-raw.lisp0000666002132200015000000013240612222115527017337 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We thank David L. Rager for contributing an initial version of this file. ; This file is divided into the following sections. ; Section: Enabling and Disabling Interrupts ; Section: Multi-Threading Interface ; Section: Multi-Threading Utility Functions and Macros ; Section: Multi-Threading Constants (in-package "ACL2") ; For readability we use #+sb-thread" instead of #+(and sbcl sbl-thread). We ; therefore make the following check to ensure that these two readtime ; conditionals are equivalent. #+(and (not sbcl) sb-thread) (error "Feature sb-thread not supported on Lisps other than SBCL") ; We would like to use the semaphore notification objects supported in SBCL ; 1.0.54+, but version 1.0.54 has a heap exhaustion error that we wish to ; avoid. As such, for now, we remain compatible with 1.0.53 and below. While ; we can consider uncommenting the following feature push once SBCL's support ; for semaphore notification objects is stable, the following questions seem ; difficult to answer: How can we determine when that is the case, and how ; confident are we that Rager's implementation using semaphore notification ; objects is correct? Rager determined that 1.0.54 was unstable by making all ; of the books with parallelism enabled (and experiencing the "heap exhausted" ; error in one of the ordinal books [at the moment of this writing, he does not ; recall which one]). Thus, Rager's code has not been completely tested using ; semaphore notification objects. ; #+sbcl ; (push :sbcl-sno-patch *features*) ;--------------------------------------------------------------------- ; Section: Enabling and Disabling Interrupts ; "Without-interrupts" means that there will be no interrupt from the Lisp ; system, including ctrl+c from the user or an interrupt from another ; thread/process. For example, if *thread1* is running (progn ; (without-interrupts (process0)) (process1)), then execution of ; (interrupt-thread *thread1* (lambda () (break))) will not interrupt ; (process0). ; But note that "without-interrupts" does not guarantee atomicity; for example, ; it does not mean "without-setq". (defmacro without-interrupts (&rest forms) ; This macro prevents interrupting evaluation (including throws, e.g., as done ; in support of por) of any of the indicated forms in a parallel lisp. In a ; non-parallel environment, we simply evaluate the forms. This behavior takes ; priority over any enclosing call of with-interrupts. Since we do not have a ; good use case for providing with-interrupts, we omit it from this interface. #+ccl `(ccl:without-interrupts ,@forms) #+sb-thread `(sb-sys:without-interrupts ,@forms) #+lispworks ; Lispworks decided to remove "without-interrupts" from their system, because ; its use has changed from meaning "atomic" to meaning "can't be interrupted by ; other threads or processes". Thus, we use the new primitive, ; "with-interrupts-blocked". `(mp:with-interrupts-blocked ,@forms) #-(or ccl sb-thread lispworks) `(progn ,@forms)) (defmacro unwind-protect-disable-interrupts-during-cleanup (body-form &rest cleanup-forms) ; As the name suggests, this is unwind-protect but with a guarantee that ; cleanup-form cannot be interrupted. Note that CCL's implementation already ; disables interrupts during cleanup. #+ccl `(unwind-protect ,body-form ,@cleanup-forms) #+sb-thread `(unwind-protect ,body-form (without-interrupts ,@cleanup-forms)) #+lispworks `(hcl:unwind-protect-blocking-interrupts-in-cleanups ,body-form ,@cleanup-forms) #-(or ccl sb-thread lispworks) `(unwind-protect ,body-form ,@cleanup-forms)) ;--------------------------------------------------------------------- ; Section: Threading Interface ; ; The threading interface is intended for system level programmers. It is not ; intended for the ACL2 user. When writing system-level multi-threaded code, ; we use implementation-independent interfaces. If you need a function not ; covered in this interface, create the interface! ; Many of the functions in this interface (lockp, make-lock, and so on) are not ; used elsewhere, but are included here in case we find a use for them later. ; We take a conservative approach for implementations that do not support ; parallelism. For example, if the programmer asks for a semaphore or lock in ; an unsupported Lisp, then nil is returned. ; We employ counting semaphores. For details, including a discussion of ; ordering, see comments in the definition of function make-semaphore. ; Note: We use parts of the threading interface for our implementation of the ; parallelism primitives. #+sb-thread (defstruct (atomically-modifiable-counter (:constructor make-atomically-modifiable-counter-raw)) ; SBCL has an atomic increment and decrement interface. In all small tests, ; this interface works well. However, when we use this interface to ; parallelize the waterfall of ACL2, it doesn't work. The observable bug that ; we find is that our Fixnum number is decremented too much (so it becomes -1, ; which is a really large non-negative fixnum). Rather than sort out the cause ; of this bug, we simply revert to using locks in SBCL. However, we leave the ; code that implements the use of these atomic increments and decrements. This ; achieves two things: (1) The code is there if we decide we want to use it ; again later (maybe after reading on the SBCL email lists that a bug with ; these atomic operations was fixed) and (2) We will have an on-going record of ; the code that causes us to observe this bug (note that ACL2 4.2 also ; contains the implementation that uses atomic operations). Maybe someone will ; be able to tell us what we are doing wrong. (val 0 ; :type (unsigned-byte #+x86-64 64 #-x86-64 32) ) (lock (sb-thread:make-mutex :name "counter lock"))) (defun make-atomically-modifiable-counter (initial-value) #+ccl initial-value #+sb-thread (make-atomically-modifiable-counter-raw :val initial-value) #+lispworks initial-value #-(or ccl sb-thread lispworks) initial-value) (defmacro define-atomically-modifiable-counter (name initial-value) `(defvar ,name (make-atomically-modifiable-counter ,initial-value))) (defmacro atomically-modifiable-counter-read (counter) #+ccl counter #+sb-thread `(atomically-modifiable-counter-val ,counter) #+lispworks counter #-(or ccl sb-thread lispworks) counter) (defmacro atomic-incf (x) ; Warning: CCL and SBCL return different values for atomic-incf. As of Oct ; 2009, CCL returns the new value, but SBCL returns the old value. We ; artificially add one to the SBCL return value to make them consistent. Both ; the CCL maintainer Gary Byers and the SBCL community have confirmed the ; return value of atomic-incf/decf to be reliable. #+ccl `(ccl::atomic-incf ,x) #+sb-thread ; Parallelism blemish: we (David Rager) performed experiments in either 2009 or ; 2010 that indicated that there was a problem with using SBCL's built-in ; atomic increment and decrements. Because of this observation, we roll our ; own atomic increments and decrements based on locking. For future reference, ; we leave the version of the code that used SBCL's built-in primitives as a ; comment. ; `(progn ; (sb-debug:backtrace) ; (1+ (sb-ext:atomic-incf ; (atomically-modifiable-counter-val ,x)))) ; Parallelism blemish: we do not recall why nil appears in the call to ; with-recursive-lock below. Probably it can be omitted. `(sb-thread:with-recursive-lock ((atomically-modifiable-counter-lock ,x)) nil ; see comment above (incf (atomically-modifiable-counter-val ,x))) #+lispworks `(system:atomic-incf ,x) #-(or ccl sb-thread lispworks) `(incf ,x)) (defmacro atomic-incf-multiple (counter count) ; Warning: CCL and SBCL return different values for atomic-incf. As of Oct ; 2009, CCL returns the new value, but SBCL returns the old value. We ; artificially add one to the SBCL return value to make them consistent. Both ; the CCL maintainer Gary Byers and the SBCL community have confirmed the ; return value of atomic-incf/decf to be reliable. According to the Lispworks ; documentation, Lispworks returns the new value. #+ccl `(without-interrupts ; Admittedly, the following loop can be viewed as not being "atomic". But the ; important thing is that two threads don't increment from the same value, and ; the use of atomic-incf guarantees that. (dotimes (i ,count) (ccl::atomic-incf ,counter))) #+sb-thread ; `(+ (sb-ext:atomic-incf ; (atomically-modifiable-counter-val ,counter) ,count) ; ,count) `(sb-thread:with-recursive-lock ((atomically-modifiable-counter-lock ,counter)) nil (incf (atomically-modifiable-counter-val ,counter) ,count)) #+lispworks `(system:atomic-incf ,counter ,count) #-(or ccl sb-thread lispworks) `(incf ,counter ,count)) (defmacro atomic-decf (x) ; Warning: CCL and SBCL return different values for atomic-incf. As of Oct ; 2009, CCL returns the new value, but SBCL returns the old value. We ; artificially subtract one from the SBCL return value to make them consistent. ; Both the CCL maintainer Gary Byers and the SBCL community have confirmed the ; return value of atomic-incf/decf to be reliable. #+ccl `(ccl::atomic-decf ,x) #+sb-thread ; `(let ((ret-val (1- (sb-ext::atomic-decf (atomically-modifiable-counter-val ; ,x) 1)))) ; (when (> ret-val 999999999999) ; (print "Warning: resetting atomically modifiable counter to 0") ; (setf ,x (make-atomically-modifiable-counter-raw :val 0))) ; 0) `(sb-thread:with-recursive-lock ((atomically-modifiable-counter-lock ,x)) nil (decf (atomically-modifiable-counter-val ,x))) #+lispworks `(system:atomic-decf ,x) #-(or ccl sb-thread lispworks) `(decf ,x)) (defun lockp (x) #+ccl (cl:typep x 'ccl::recursive-lock) #+sb-thread (cl:typep x 'sb-thread::mutex) #+lispworks (cl:typep x 'mp:lock) #-(or ccl sb-thread lispworks) ; We return nil in the uni-threaded case in order to stay in sync with ; make-lock, which returns nil in this case. In a sense, we want (lockp ; (make-lock x)) to be a theorem if there is no error. (null x)) (defun make-lock (&optional lock-name) ; See also deflock. ; If lock-name is supplied, it must be nil or a string. ; Even though CCL nearly always uses a FIFO for threads blocking on a lock, ; it does not guarantee so: no such promise is made by the CCL ; documentation or implementor (in fact, we are aware of a race condition that ; would violate FIFO properties for locks). Thus, we make absolutely no ; guarantees about ordering; for example, we do not guarantee that the ; longest-blocked thread for a given lock is the one that would enter a ; lock-guarded section first. However, we suspect that this is usually the ; case for most implementations, so assuming such an ordering property is ; probably a reasonable heuristic. We would be somewhat surprised to find ; significant performance problems in our own application to ACL2's parallelism ; primitives due to the ordering provided by the underlying system. #-(or ccl sb-thread lispworks) (declare (ignore lock-name)) #+ccl (ccl:make-lock lock-name) #+sb-thread (sb-thread:make-mutex :name lock-name) #+lispworks (mp:make-lock :name lock-name) #-(or ccl sb-thread lispworks) ; We return nil in the uni-threaded case in order to stay in sync with lockp. nil) (defmacro reset-lock (bound-symbol) ; This macro binds the given global (but not necessarily special) variable to a ; lock that is new, at least from a programmer's perspective. ; Reset-lock should only be applied to bound-symbol if deflock has previously ; been applied to bound-symbol. `(setq ,bound-symbol (make-lock ,(symbol-name bound-symbol)))) (defmacro with-lock (bound-symbol &rest forms) ; Grab the lock, blocking until it is acquired; evaluate forms; and then ; release the lock. This macro guarantees mutual exclusion. #-(or ccl sb-thread lispworks) (declare (ignore bound-symbol)) (let ((forms ; We ensure that forms is not empty because otherwise, in CCL alone, ; (with-lock some-lock) evaluates to t. We keep the code simple and consistent ; by modifying forms here for all cases, not just CCL. (or forms '(nil)))) #+ccl `(ccl:with-lock-grabbed (,bound-symbol) nil ,@forms) #+sb-thread `(sb-thread:with-recursive-lock (,bound-symbol) nil ,@forms) #+lispworks `(mp:with-lock (,bound-symbol) nil ,@forms) #-(or ccl sb-thread lispworks) `(progn ,@forms))) (defun run-thread (name fn-symbol &rest args) ; Apply fn-symbol to args. We follow the precedent set by LISP machines (and ; in turn CCL), which allowed the user to spawn a thread whose initial ; function receives an arbitrary number of arguments. ; We expect this application to occur in a fresh thread with the given name. ; When a call of this function returns, we imagine that this fresh thread can ; be garbage collected; at any rate, we don't hang on to it! ; Note that run-thread returns different types in different Lisps. ; A by-product of our use of lambdas is that fn-symbol doesn't have to be a ; function symbol. It's quite fine to call run-thread with a lambda, e.g. ; ; (run-thread "hello" (lambda () (print "hi"))) ; ; A more sophisticated version of run-thread would probably check whether ; fn-symbol was indeed a symbol and only create a new lambda if it was. #-(or ccl sb-thread lispworks) (declare (ignore name)) #+ccl (ccl:process-run-function name (lambda () (apply fn-symbol args))) #+sb-thread (sb-thread:make-thread (lambda () (apply fn-symbol args)) :name name) #+lispworks (mp:process-run-function name nil (lambda () (apply fn-symbol args))) ; We're going to be nice and let the user's function still run, even though ; it's not split off. #-(or ccl sb-thread lispworks) (apply fn-symbol args)) (defun interrupt-thread (thread function &rest args) ; Interrupt the indicated thread and then, in that thread, apply function to ; args. Note that function and args are all evaluated. When this function ; application returns, the thread resumes from the interrupt (from where it ; left off). #-(or ccl sb-thread lispworks) (declare (ignore thread function args)) #+ccl (apply #'ccl:process-interrupt thread function args) #+sb-thread (if args (error "Passing arguments to interrupt-thread not supported in SBCL.") (sb-thread:interrupt-thread thread function)) #+lispworks (apply #'mp:process-interrupt thread function args) #-(or ccl sb-thread lispworks) nil) (defun kill-thread (thread) #-(or ccl sb-thread lispworks) (declare (ignore thread)) #+ccl (ccl:process-kill thread) #+sb-thread (sb-thread:terminate-thread thread) #+lispworks (mp:process-kill thread) #-(or ccl sb-thread lispworks) nil) (defun all-threads () #+ccl (ccl:all-processes) #+sb-thread (sb-thread:list-all-threads) #+lispworks (mp:list-all-processes) #-(or ccl sb-thread lispworks) (error "We don't know how to list threads in this Lisp.")) (defun current-thread () #+ccl ccl:*current-process* #+sb-thread sb-thread:*current-thread* #+lispworks mp:*current-process* #-(or ccl sb-thread lispworks) nil) (defun thread-wait (fn &rest args) ; Thread-wait provides an inefficient mechanism for the current thread to wait ; until a given condition, defined by the application of fn to args, is true. ; When performance matters, we advise using a signaling mechanism instead of ; this relatively highly-latent function. #+ccl (apply #'ccl:process-wait "Asynchronously waiting on a condition" fn args) #+lispworks (apply #'mp:process-wait "Asynchronously waiting on a condition" fn args) #-(or ccl lispworks) (loop while (not (apply fn args)) do (sleep 0.05))) #+(or sb-thread lispworks) (defmacro with-potential-timeout (body &key timeout) ; There is no implicit progn for the body argument. This is different from ; sb-sys:with-deadline, but we figure the simplicity is more valuable than ; randomly passing in a :timeout value. ; Note that if there is a timeout, the return value is unspecified. #+sb-thread `(if ,timeout (handler-case (sb-sys:with-deadline (:seconds ,timeout) ,body) (sb-ext:timeout ())) ,body) #+lispworks (lispworks:with-unique-names (process timer) `(catch 'lispworks-timeout (let* ((,process mp:*current-process*) (,timer (mp:make-timer (lambda () (mp:process-interrupt ,process (lambda () (throw 'lispworks-timeout nil))))))) (unwind-protect-disable-interrupts-during-cleanup (progn (mp:schedule-timer-relative ,timer ,timeout) ,body) (mp:unschedule-timer ,timer)))))) ; We would like to find a clean way to provide the user with an implicit progn, ; while still maintaining timeout as a keyword argument. ; #+sb-thread ; (defmacro with-potential-sbcl-timeout (&rest body &key timeout) ; ; ; The below use of labels is only neccessary because we provide an implicit ; ; progn for the body of with-potential-sbcl-timeout. ; ; (let ((correct-body ; (labels ((remove-keyword-from-list ; (lst keyword) ; (if (or (atom lst) (atom (cdr lst))) ; lst ; (if (equal (car lst) :timeout) ; (cddr lst) ; (cons (car lst) (remove-keyword-from-args (cdr lst))))))) ; (remove-keyword-from-args body :timeout)))) ; ; ; `(if ,timeout ; (handler-case ; (sb-sys:with-deadline ; (:seconds ,timeout) ; ,@correct-body) ; ; (sb-ext:timeout ())) ; ,@correct-body))) ; Essay on Condition Variables ; A condition variable is a data structure that can be passed to corresponding ; "wait" and "signal" functions. When a thread calls the wait function on a ; condition variable, c, the thread blocks until "receiving a signal" from the ; application of the signal function to c. Only one signal is sent per call of ; the signal function; so, at most one thread will unblock. (There is a third ; notion for condition variable, namely the broadcast function, which is like ; the signal function except that all threads blocking on the given condition ; variable will unblock. But we do not support broadcast functions in this ; interface, in part because we use semaphores for CCL, and there's no way ; to broadcast when you're really using a semaphore.) ; The design of our parallelism library is simpler when using condition ; variables for the following reason: Since a worker must wait for two ; conditions before consuming work, it is better to use a condition variable ; and test those two conditions upon waking, rather than try and use two ; semaphores. ; Implementation Note: As of March 2007, our CCL implementation does not ; yield true condition variables. A condition variable degrades to a ; semaphore, so if one thread first signals a condition variable, then that ; signal has been stored. Then later (perhaps much later), when another thread ; waits for that signal, that thread will be able to proceed by decrementing ; the count. As a result the later thread will "receive" the signal, even ; though that signal occurred in the past. Fortunately, this isn't a ; contradiction of the semantics of condition variables, since with condition ; variables there is no specification of how far into the future the waiting ; thread will receive a signal from the signalling thread. ; Note: Condition variables should not be used to store state. They are only a ; signaling mechanism, and any state update implied by receiving a condition ; variable's signal should be checked. This usage is believed to be consistent ; with traditional condition variable semantics. (defun make-condition-variable () ; If CCL implements condition variables, we will want to change the CCL ; expansion and remove the implementation note above. ; Because implementing broadcast for condition variables in CCL is much more ; heavyweight than a simple semaphore, we keep it simple until we have a use ; case for a broadcast. Such simple requirements are satisfied by using a ; semaphore. #+ccl (ccl:make-semaphore) #+sb-thread (sb-thread:make-waitqueue) #+lispworks (mp:make-condition-variable) #-(or ccl sb-thread lispworks) nil) (defmacro signal-condition-variable (cv) #-(or ccl sb-thread lispworks) (declare (ignore cv)) #+ccl `(ccl:signal-semaphore ,cv) #+sb-thread ; According to an email sent by Gabor Melis, of SBCL help, on 2007-02-25, if ; there are two threads waiting on a condition variable, and a third thread ; signals the condition variable twice before either can receive the signal, ; then both threads should receive the signal. If only one thread unblocks, it ; is considered a bug. `(sb-thread:condition-notify ,cv) #+lispworks `(mp:condition-variable-signal ,cv) ; Parallelism wart: we would like for signal-condition-variable to return nil ; to signify that this macro is only evaluated for side-effects. However, it ; appears that this macro's return value is actually needed, because the ; theorem prover hangs consistently in SBCL and LispWorks when we do return nil ; here. As such, we currently remove the returning of nil. But, this is a ; hotfix. A much better solution is to change the reliance upon this macro's ; return value and then uncomment the following nil. ; nil ) (defmacro broadcast-condition-variable (cv) #-(or sb-thread lispworks) (declare (ignore cv)) #+ccl (error "Broadcasting condition variables is unsupported in CCL") #+sb-thread `(sb-thread:condition-broadcast ,cv) #+lispworks `(mp:condition-variable-broadcast ,cv) nil) (defun wait-on-condition-variable (cv lock &key timeout) ; This is only executed for side-effect. ; A precondition to this function is that the current thread "owns" lock. This ; is a well-known part of how condition variables work. This is also ; documented in the SBCL manual in section 12.5 entitled "Waitqueue/condition ; variables." ; Parallelism blemish: when timeout is provided, the return value of t can be ; misleading. This isn't a huge problem, because condition variables are only ; used for signaling, and so, assuming the programmer is following the ; discipline associated with using condition variables, any thread waiting on a ; condition variable should check the state of the program for the property it ; needs anyway. And, if that property is not provided, the thread will just ; rewait on the condition variable. So, the only real penalty should be a ; slight loss of performance. Of course, this small penalty depends on the ; following the discipline of using condition variables correctly. We may not ; wish to rely upon the programmer's ability to follow this discipline, so we ; leave it as a parallelism blemish. #-(or sb-thread lispworks) (declare (ignore cv lock timeout)) #+ccl (error "Waiting on a condition variable is unsupported in CCL") #+sb-thread (with-potential-timeout (sb-thread:condition-wait cv lock) :timeout timeout) #+lispworks (mp:condition-variable-wait cv lock :timeout timeout) t) #+sb-thread (defstruct acl2-semaphore (lock (sb-thread:make-mutex)) (cv (sb-thread:make-waitqueue)) ; condition variable (count 0)) #+lispworks (defstruct acl2-semaphore (lock (mp:make-lock)) (cv (mp:make-condition-variable)) ; condition variable (count 0)) (defun make-semaphore (&optional name) ; Make-semaphore, signal-semaphore, and semaphorep work together to implement ; counting semaphores for the threading interface. ; This function creates "counting semaphores", which are data structures that ; include a "count" field, which is a natural number. A thread can "wait on" a ; counting semaphore, and it will block in the case that the semaphore's count ; is 0. To "signal" such a semaphore means to increment that field and to ; notify a unique waiting thread (we will discuss a relaxation of this ; uniqueness shortly) that the semaphore's count has been incremented. Then ; this thread, which is said to "receive" the signal, decrements the ; semaphore's count and is then unblocked. This mechanism is typically much ; faster than busy waiting. ; In principle more than one waiting thread could be notified (though this ; seems rare in practice). In this case, only one would be the receiving ; thread, i.e., the one that decrements the semaphore's count and is then ; unblocked. ; If semaphore usage seems to perform inefficiently, could this be due to ; ordering issues? For example, even though CCL nearly always uses a FIFO ; for blocked threads, it does not guarantee so: no such promise is made by the ; CCL documentation or implementor. Thus, we make absolutely no guarantees ; about ordering; for example, we do not guarantee that the longest-blocked ; thread for a given semaphore is the one that would receive a signal. ; However, we suspect that this will usually be the case for most ; implementations, so assuming such an ordering property is probably a ; reasonable heuristic. We would be somewhat surprised to find significant ; performance problems in our own application to ACL2's parallelism primitives ; due to the ordering provided by the underlying system. ; CCL provides us with semaphores for signaling. SBCL provides condition ; variables for signaling. Since we want to code for one type of signaling ; between parents and children, we create a semaphore wrapper for SBCL's ; condition variables. The structure sbcl-semaphore implements the data for ; this wrapper. ; Followup: SBCL has recently (as of November 2010) implemented semaphores, and ; the parallelism code could be changed to reflect this. However, since SBCL ; does not implement semaphore-nofication-object's, we choose to stick with our ; own implementation of semaphores for now. (declare (ignore name)) #+ccl (ccl:make-semaphore) #+(or (and sb-thread (not sbcl-sno-patch)) lispworks) (make-acl2-semaphore) #+(and sb-thread sbcl-sno-patch) (sb-thread:make-semaphore) #-(or ccl sb-thread lispworks) ; We return nil in the uni-threaded case in order to stay in sync with ; semaphorep. nil) (defun semaphorep (semaphore) ; Make-semaphore, signal-semaphore, and semaphorep work together to implement ; counting semaphores for our threading interface. ; This function recognizes our notion of semaphore structures. #+ccl (typep semaphore 'ccl::semaphore) #+sb-thread (and (acl2-semaphore-p semaphore) (typep (acl2-semaphore-lock semaphore) 'sb-thread::mutex) (typep (acl2-semaphore-cv semaphore) 'sb-thread::waitqueue) (integerp (acl2-semaphore-count semaphore))) #+lispworks (and (acl2-semaphore-p semaphore) (typep (acl2-semaphore-lock semaphore) 'mp::lock) (typep (acl2-semaphore-cv semaphore) 'mp::condition-variable) (integerp (acl2-semaphore-count semaphore))) #-(or ccl sb-thread lispworks) ; We return nil in the uni-threaded case in order to stay in sync with ; make-semaphore, which returns nil in this case. In a sense, we want ; (semaphorep (make-semaphore x)) to be a theorem if there is no error. (null semaphore)) (defun make-semaphore-notification () ; This function returns an object that records when a corresponding semaphore ; has been signaled (for use when wait-on-semaphore is called with that ; semaphore and that object). #+ccl (ccl:make-semaphore-notification) #+(or (and sb-thread (not sbcl-sno-patch)) lispworks) (make-array 1 :initial-element nil) #+(and sb-thread sbcl-sno-patch) (sb-thread:make-semaphore-notification) #-(or ccl sb-thread lispworks) nil) (defun semaphore-notification-status (semaphore-notification-object) #-(or ccl sb-thread lispworks) (declare (ignore semaphore-notification-object)) #+ccl (ccl:semaphore-notification-status semaphore-notification-object) #+(or (and sb-thread (not sbcl-sno-patch)) lispworks) (aref semaphore-notification-object 0) #+(and sb-thread sbcl-sno-patch) (sb-thread:semaphore-notification-status semaphore-notification-object) #-(or ccl sb-thread lispworks) ; t may be the wrong default, but we don't have a use case for this return ; value yet, so we postpone thinking about the "right" value until we are aware ; of a need. t) (defun clear-semaphore-notification-status (semaphore-notification-object) #-(or ccl sb-thread lispworks) (declare (ignore semaphore-notification-object)) #+ccl (ccl:clear-semaphore-notification-status semaphore-notification-object) #+(or (and sb-thread (not sbcl-sno-patch)) lispworks) (setf (aref semaphore-notification-object 0) nil) #+(and sb-thread sbcl-sno-patch) (sb-thread:clear-semaphore-notification semaphore-notification-object) #-(or ccl sb-thread lispworks) nil) ; We implement this only for SBCL and Lispworks, because even a system-level ; programmer is not expected to use this function. We use it only from within ; the threading interface to implement wait-on-semaphore for SBCL and ; Lispworks. (defun set-semaphore-notification-status (semaphore-notification-object) #-(or sb-thread lispworks) (declare (ignore semaphore-notification-object)) #+(or sb-thread lispworks) (setf (aref semaphore-notification-object 0) t) #-(or sb-thread lispworks) (error "Set-semaphore-notification-status not supported outside SBCL or Lispworks")) (defun signal-semaphore (semaphore) ; Make-semaphore, signal-semaphore, and semaphorep work together to implement ; counting semaphores for our threading interface. ; This function is executed for side effect; the value returned is irrelevant. #-(or ccl sb-thread lispworks) (declare (ignore semaphore)) #+ccl (ccl:signal-semaphore semaphore) #+(or (and sb-thread (not sbcl-sno-patch)) lispworks) (with-lock (acl2-semaphore-lock semaphore) (without-interrupts (incf (acl2-semaphore-count semaphore)) (signal-condition-variable (acl2-semaphore-cv semaphore)))) #+(and sb-thread sbcl-sno-patch) (sb-thread:signal-semaphore semaphore) nil) ; Once upon a time, we optimized the manual allocation and deallocation of ; semaphores so that they could be recycled. CCL and SBCL have since evolved, ; and as such, we have removed the implementation code and its corresponding ; uses. (defun wait-on-semaphore (semaphore &key notification timeout) ; This function is guaranteed to return t when there is no timeout provided and ; the function call received the signal. Its return value when the signal has ; not been received is unspecified. As such, we provide the semaphore ; notification object as a means for determining whether a signal was actually ; received. ; Parallelism blemish: if a timeout is included, then it is our intent that a ; call of this function that times-out should return nil. This is known to be ; the case in CCL, but while we have written the code for SBCL and LispWorks, ; we have not actually tested the code for this property. ; This function only returns normally after receiving a signal for the given ; semaphore, setting the notification status of notification (if supplied and ; non-nil) to true; see semaphore-notification-status. But control can leave ; this function abnormally, for example if the thread executing a call of this ; function is interrupted (e.g., with interface function interrupt-thread) with ; code that does a throw, in which case notification is unmodified. ; We need the ability to know whether we received a signal or not. CCL ; provides this through a semaphore notification object. SBCL does not provide ; this mechanism currently, so we might "unreceive the signal" in the cleanup ; form of the implementation. We do this by only decrementing the count of the ; semaphore iff we set the notification object. This means we have to resignal ; the semaphore if we were interrupted while signaling, but we would have to do ; this anyway. #-(or ccl sb-thread lispworks) (declare (ignore semaphore notification timeout)) #+ccl (if timeout (ccl:timed-wait-on-semaphore semaphore timeout notification) (progn (ccl:wait-on-semaphore semaphore notification) t)) #+(or (and sb-thread (not sbcl-sno-patch)) lispworks) (let ((received-signal nil)) ; If we did not use a variable like "received-signal", we could have the ; following race condition: ; -- Suppose Thread A waits for the semaphore and is waiting for the ; signal from the CV ; -- Suppose Thread B is in the same state, that is, waiting for the ; semaphore and for the signal from the CV ; -- Then thread C signals the CV ; -- Thread A is awakened by the signal but is immediately interrupted ; by another thread and forced to throw a tag which is caught higher up ; in Thread A's call stack. ; -- The signal is effectively "lost", so while the semaphore count may ; be accurate, Thread B is still waiting on a signal that will never ; come. ; We guard against this lost signal by always re-signaling the condition ; variable (unless we're sure we received the signal, which we would know ; because "received-signal" would be set to t). ; This signaling during the unwind portion of the unwind protect definitely ; results in some inefficient execution. This is brought about because now any ; thread that waits on a signal will automatically signal the condition ; variable any time that it doesn't receive the signal (either due to a timeout ; or an interrupt+throw/error). However, this is the price of liveness for our ; system (which requires we implement semaphores in user space because we need ; semaphore-notification-objects). (with-lock (acl2-semaphore-lock semaphore) (unwind-protect-disable-interrupts-during-cleanup (with-potential-timeout (progn (loop while (<= (acl2-semaphore-count semaphore) 0) do ; The current thread missed the chance to decrement and must rewait. This can ; only occur if another thread grabbed the lock and decremented the (wait-on-condition-variable (acl2-semaphore-cv semaphore) (acl2-semaphore-lock semaphore))) (setq received-signal t) t) ; if this progn returns, this t is the return value :timeout timeout) (if received-signal ; The current thread was able to record the receipt of the signal. The current ; thread will decrement the count of the semaphore and set the semaphore ; notification object. (progn (decf (acl2-semaphore-count semaphore)) (when notification (set-semaphore-notification-status notification))) ; The current thread may have received the signal but been unable to record it. ; In this case, the current thread will signal the condition variable again, so ; that any other thread waiting on the semaphore can have a chance at acquiring ; the said semaphore. This results in needlessly signaling the condition ; variable portion of the semaphore every time a timeout occurs. However, that ; is the cost of a "live" implementation ("live" loosely means "makes progress"). (signal-condition-variable (acl2-semaphore-cv semaphore))))) (if timeout received-signal t)) #+(and sb-thread sbcl-sno-patch) (if timeout (sb-thread:wait-on-semaphore semaphore :timeout timeout :notification notification) (progn (sb-thread:wait-on-semaphore semaphore :timeout timeout :notification notification) t)) #-(or ccl sb-thread lispworks) t) ; default is to receive a semaphore/lock ;--------------------------------------------------------------------- ; Section: Multi-Threading Utility Functions and Macros ; ; These functions and macros could be defined in parallel-raw.lisp, except that ; we also use them in futures-raw.lisp. Rather than create another file, we ; place them here, at the end of the multi-threading interface. (defvar *throwable-worker-thread* ; When we terminate threads due to a break and abort, we need a way to ; terminate all threads. We implement this by having them throw the ; :worker-thread-no-longer-needed tag. Unfortunately, sometimes the threads ; are outside the scope of the associated catch, when throwing the tag would ; cause an error. We avoid this warning by maintaining the dynamically-bound ; variable *throwable-worker-thread*. When the throwable context is entered, ; we let a new copy of the variable into existence and set it to T. Now, when ; we throw :worker-thread-no-longer-needed, we only throw it if ; *throwable-worker-thread* is non-nil. nil) (defun throw-all-threads-in-list (thread-list) ; We interrupt each of the given threads with a throw to the catch at the top ; of consume-work-on-work-queue-when-there, which is the function called ; by run-thread in spawn-worker-threads-if-needed. ; Compare with kill-all-threads-in-list, which kills all of the given threads ; (typically all user-produced threads), not just those self-identified as ; being within the associated catch block. (if (endp thread-list) nil (progn #-sbcl (interrupt-thread (car thread-list) #'(lambda () (when *throwable-worker-thread* (throw :worker-thread-no-longer-needed nil)))) #+sbcl (handler-case (interrupt-thread (car thread-list) #'(lambda () (when *throwable-worker-thread* (throw :worker-thread-no-longer-needed nil)))) (sb-thread:interrupt-thread-error () ; Parallelism no-fix: it turns out that this error is common. And since we ; think that the error is caused by the thread being finished by the time we ; get around to interrupting it, we do not print the following warning. The ; error message we are trying to avoid is: ; debugger invoked on a SB-THREAD:INTERRUPT-THREAD-ERROR in thread #: ; Interrupt thread failed: thread # has exited. ; (format t "Warning: We tried to interrupt a thread and throw it with ~%~ ; the :worker-thread-no-longer-needed tag, but we ~%~ ; encountered an error. Since the error is likely benign, ~%~ ; this is only a warning. Unless you are an ACL2(p) ~%~ ; maintainer, you can ignore this warning.~%") )) (throw-all-threads-in-list (cdr thread-list))))) (defun kill-all-threads-in-list (thread-list) ; Compare with throw-all-threads-in-list, which uses throw instead of killing ; threads directly, but only affects threads self identified as being within an ; associated catch block. (if (endp thread-list) nil (progn (kill-thread (car thread-list)) (kill-all-threads-in-list (cdr thread-list))))) (defun thread-name (thread) #+ccl (ccl:process-name thread) #+sbcl (sb-thread:thread-name thread) #+lispworks (mp:process-name thread) #-(or ccl sbcl lispworks) (error "Function thread-name is unimplemented in this Lisp.")) (defconstant *worker-thread-name* "Worker thread") (defun worker-threads1 (threads) (cond ((endp threads) nil) ((equal (thread-name (car threads)) *worker-thread-name*) (cons (car threads) (worker-threads1 (cdr threads)))) (t (worker-threads1 (cdr threads))))) (defun worker-threads () ; Returns the subset of the current list of threads that are worker threads ; spawned by ACL2(p). (worker-threads1 (all-threads))) (defun all-worker-threads-are-dead () #+sbcl (<= (length (all-threads)) 1) #-sbcl (null (worker-threads))) #+ccl (defun all-given-threads-are-reset (threads) (if (endp threads) t (and (equal (ccl:process-whostate (car threads)) "Reset") (all-given-threads-are-reset (cdr threads))))) (defun all-worker-threads-are-dead-or-reset () ; CCL has a bug that if you try to throw a thread that is "reset" ("reset" is a ; CCL term), it ignores the throw (and the thread never expires). As such, we ; do not let threads in the "reset" state prevent us from finishing the ; resetting of parallelism variables. #+ccl (all-given-threads-are-reset (worker-threads)) #-ccl (null (worker-threads))) (defun send-die-to-worker-threads () ; This function is evaluated only for side effect. (throw-all-threads-in-list (worker-threads)) (let ((round 0)) (loop do ; We used to call "(thread-wait 'all-worker-threads-are-dead)". ; However, we noticed a synchronization problem between what we might prefer ; the underlying Lisp to do (in this one case) and what the Lisp actually does. ; In particular, when we call run-thread, the call to run-thread returns, ; before we know that the thread has actually started running. This is ; probably fine in the general case. However, in this instance, it is possible ; for the threads that are about to be run to not receive the throw, because ; they haven't really started running yet. Rather than work out the details of ; this problem, we punt, and simply wait for one second before issuing a new ; throw. In practice, this works just fine. (when (equal (mod round 10) 0) (throw-all-threads-in-list (worker-threads)) ; The following commented code is only for debugging ; (format t "Waiting for parallelism worker threads to halt.~%") ; (format t "Current threads are ~%~s~%~%" (all-threads)) ) (sleep 0.03) (incf round) while (not (all-worker-threads-are-dead-or-reset))))) (defun kill-all-worker-threads () ; This function is evaluated only for side effect. (kill-all-threads-in-list (worker-threads)) (thread-wait 'all-worker-threads-are-dead)) ;--------------------------------------------------------------------- ; Section: Multi-Threading Constants ; ; These constants could go in parallel-raw.lisp, except that they are also used ; in futures-raw.lisp. Rather than create another file, we place them here, at ; the end of the multi-threading interface. (defun core-count-raw (&optional (ctx nil) default) ; If ctx is supplied, then we cause an error using the given ctx. Otherwise we ; return a suitable default value (see below). #+ccl (declare (ignore ctx default)) #+ccl (ccl:cpu-count) #-ccl (if ctx (error "It is illegal to call cpu-core-count in this Common Lisp ~ implementation.") ; If the host Lisp does not provide a means for obtaining the number of cores, ; then we simply estimate on the high side. A high estimate is desired in ; order to make it unlikely that we have needlessly idle cores. We thus ; believe that 16 cores is a reasonable estimate for early 2011; but we may ; well want to increase this number later. (or default 16))) (defvar *core-count* ; *core-count* is a variable so that we can redefine it dynamically (for ; performance testing) and not have to redefine everything that uses it. (core-count-raw) "The total number of CPU cores in the system.") (defvar *unassigned-and-active-work-count-limit* ; The *unassigned-and-active-work-count-limit* limits work on the *work-queue* ; to what we think the system will be able to process in a reasonable amount of ; time. Suppose we have 8 CPU cores. This means that there can be 8 active ; work consumers, and that generally not many more than 24 pieces of ; parallelism work are stored in the *work-queue* to be processed. This ; provides us the guarantee that if all worker threads were immediately to ; finish their piece of parallelism work, that each of them would immediately ; be able to grab another piece from the work queue. ; We could increase the following coefficient from 4 and further guarantee that ; consumers have parallelism work to process, but this would come at the ; expense of backlogging the *work-queue". We prefer simply to avoid the ; otherwise parallelized computations in favor of their serial equivalents. (* 4 *core-count*)) (defconstant *max-idle-thread-count* ; Note that although this is a defconstant and ; *unassigned-and-active-work-count-limit* is a defvar, David Rager says that ; there's no particular reason he recalls for this distinction. ; We don't want to spawn more worker threads (which are initially idle) when we ; already have sufficiently many idle worker threads. We use ; *max-idle-thread-count* to limit this spawning in function ; spawn-worker-threads-if-needed. (* 2 *core-count*)) acl2-sources/new.html0000664002132200015000000000365212222316625014241 0ustar kaufmannacl2 ACL2 Version 6.3 News

ACL2 Version 6.3 News

Table of Contents

ACL2 sources availability between releases

ACL2 sources are now available between releases, as described in
this announcement.

VSTTE 2012 Competition

A team of four ACL2 users entered the VSTTE 2012 competition. For information, including the team's solution, visit this
link.

ACL2 Books Repository

The acl2-books Google group allows you to contribute ACL2 books (input files), and also to update to the latest version if you don't want to wait for the next ACL2 release. Quoting from that web site:
This site is for community-driven development for the basic ACL2 libraries, which deal with topics like arithmetic, data structures, and hardware modelling. We're working with the authors of ACL2 and our changes are eventually incorporated into official ACL2 releases.

Performance Comparisons

To appear































































acl2-sources/non-linear.lisp0000666002132200015000000012154112222115527015513 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ;================================================================= ; We here begin the support functions for non-linear arithmetic. ;================================================================= (defun cleanse-type-alist (type-alist pt) ; This function removes equality facts from the type-alist in ; preparation for the call to rewrite-linear-term-lst in ; add-terms-and-lemmas. ; In order to see why we need this function now, redefine ; cleanse-type-alist to be the identity and trace ; rewrite-linear-term-lst and linearize in the (failed) proof ; of the following lemma: ; (defthm uniqueness-of-+-inverses-lemma ; (implies (and (acl2-numberp x) ; (acl2-numberp y) ; (equal (+ x y) ; 0)) ; (equal (- x) y)) ; :rule-classes nil) (cond ((null type-alist) nil) ((to-be-ignoredp (cddar type-alist) pt) (cleanse-type-alist (cdr type-alist) pt)) (t (cons (car type-alist) (cleanse-type-alist (cdr type-alist) pt))))) (defun var-with-divisionp (var) ; We test whether var is of the form ; 1. (/ x) or (expt (/ x) n), or [don't change this line without ; 2. (expt x c) or (expt x (* c y)) seeing the Warning below] ; where c is a negative constant. ; Warning: If you change this code, search for other instances of ; ``1. (/ x) or (expt (/ x) n)'' and adjust those comments ; appropriately. ; Warning: Keep this function in sync with invert-var. (cond ((eq (fn-symb var) 'EXPT) (let ((base (fargn var 1)) (exponent (fargn var 2))) (or (and (eq (fn-symb base) 'UNARY-/) (not (eq (fn-symb (fargn base 1)) 'BINARY-+))) (and (not (eq (fn-symb base) 'BINARY-+)) (quotep exponent) (integerp (unquote exponent)) (< (unquote exponent) 0)) (and (not (eq (fn-symb base) 'BINARY-+)) (eq (fn-symb exponent) 'BINARY-*) (quotep (fargn exponent 1)) (integerp (unquote (fargn exponent 1))) (< (unquote (fargn exponent 1)) 0))))) (t (and (eq (fn-symb var) 'UNARY-/) (not (eq (fn-symb (fargn var 1)) 'BINARY-+)))))) (defun varify (x) ; We ensure that x is a legitimate pot-label. See invert-var from whence ; this is called. (cond ((quotep x) (er hard 'varify "This should not have happened. The supposed ~ variable, ~x0, is instead a constant." x)) ((equal (fn-symb x) 'BINARY-+) ;;; We have to pick one. (if (quotep (fargn x 1)) (varify (fargn x 2)) (varify (fargn x 1)))) ((and (equal (fn-symb x) 'BINARY-*) (quotep (fargn x 1))) (varify (fargn x 2))) (t x))) (defun varify! (x) (let ((temp (varify x))) (if (good-pot-varp temp) temp (er hard 'varify! "Varify! is supposed to return a good-pot-varp, but ~ returned ~x0 on ~x1." temp x)))) (defun varify!-lst1 (lst acc) (if (null lst) acc (varify!-lst1 (cdr lst) (cons (varify! (car lst)) acc)))) (defun varify!-lst (lst) ; This is used in expanded-new-vars-in-pot-lst, and we want to ; reverse the list. Thus the use of an accumulator. (varify!-lst1 lst nil)) (defun invert-var (var) ; Var is an arithmetic ACL2 term. We return a term suitable for use ; as an unknown in a poly, but that's all we guarantee. The idea is ; that the term is ``relevant'' to the non-linear properties of var ; and we try to return the multiplicative inverse. We expect to go ; find additional polys about this term. (cond ((eq (fn-symb var) 'EXPT) (let ((base (fargn var 1)) (exponent (fargn var 2))) (cond ((eql exponent ''-1) (varify! base)) ((eq (fn-symb base) 'UNARY-/) (fcons-term* 'EXPT (fargn base 1) exponent)) ((eq (fn-symb exponent) 'UNARY--) (fcons-term* 'EXPT base (fargn exponent 1))) ((and (quotep exponent) (integerp (unquote exponent)) (< (unquote exponent) 0)) (fcons-term* 'EXPT base (kwote (- (unquote exponent))))) ((and (eq (fn-symb exponent) 'BINARY-*) (quotep (fargn exponent 1)) (integerp (unquote (fargn exponent 1))) (< (unquote (fargn exponent 1)) 0)) (fcons-term* 'EXPT base (cons-term 'BINARY-* (list (kwote (- (unquote (fargn exponent 1)))) (fargn exponent 2))))) (t (fcons-term* 'EXPT (cons-term 'UNARY-/ (list base)) exponent))))) ((eq (fn-symb var) 'UNARY-/) (varify! (fargn var 1))) (t (cons-term 'UNARY-/ (list var))))) (defun part-of1 (var1 var2) ; NOTE: Note that we are implicitly assuming that var2 is right ; associated. This should be taken care of some day. Perhaps what I ; should do is take the fringe of both vars, and do a simple set ; difference. (cond ((or (variablep var2) (fquotep var2)) (if (equal var1 var2) *1* nil)) ((eq (ffn-symb var2) 'BINARY-*) (let ((arg1 (fargn var2 1)) (arg2 (fargn var2 2))) (if (equal var1 arg1) arg2 (let ((new-var2 (part-of1 var1 arg2))) (cond ((null new-var2) nil) ((equal new-var2 ''1) arg1) (t (cons-term 'BINARY-* (list arg1 new-var2)))))))) (t (if (equal var1 var2) *1* nil)))) (defun part-of (var1 var2) ; We ask whether the factors of var1 are a subset of the factors of ; var2. If so, we return a naive calculation of (/ var2 var1). (cond ((or (variablep var1) (fquotep var1)) (part-of1 var1 var2)) ((eq (ffn-symb var1) 'BINARY-*) (let ((new-var2 (part-of (fargn var1 1) var2))) (cond (new-var2 (part-of (fargn var1 2) new-var2)) (t nil)))) (t (part-of1 var1 var2)))) (defun product-already-triedp (var-list products-already-tried) ; Var-list is a list of ACL2 terms, products-already-tried is a ; list of lists of ACL2 terms. If (a permutation of) var-list ; appears in products-already-tried, we return t. Otherwise, we ; return nil. (cond ((null products-already-tried) nil) (t (or (equal var-list (car products-already-tried)) (product-already-triedp var-list (cdr products-already-tried)))))) (defun too-many-polysp (var-lst pot-lst counter) ; Var-list is a list of pot-labels from pot-lst, and counter is initially ; 1. We we are about to multiply the polys from the pots in var-lst, ; we first check whether doing so would generate too many polys. ; Note: This function has a magic number, 20, which probably should be ; settable. (cond ((null var-lst) (< 20 counter)) (t (too-many-polysp (cdr var-lst) pot-lst (* counter (length (polys-with-var1 (car var-lst) pot-lst))))))) (defun expanded-new-vars-in-pot-lst2 (new-var vars-to-skip vars-to-return) ; We explore the term new-var and accumulate into vars-to-skip and ; vars-to-return all the tips of the BINARY-* tree and the inverses of ; vars with division. (cond ((or (member-equal new-var vars-to-skip) (quotep new-var) (eq (fn-symb new-var) 'BINARY-+)) (mv vars-to-skip vars-to-return)) ((eq (fn-symb new-var) 'BINARY-*) (let ((new-factor (fargn new-var 1))) (if (or (member-equal new-factor vars-to-skip) (quotep new-factor) (eq (fn-symb new-factor) 'BINARY-+)) (expanded-new-vars-in-pot-lst2 (fargn new-var 2) vars-to-skip vars-to-return) (expanded-new-vars-in-pot-lst2 (fargn new-var 2) (cons new-factor vars-to-skip) (cons new-factor vars-to-return))))) ((var-with-divisionp new-var) (let ((inverted-var (invert-var new-var))) (if (member-equal inverted-var vars-to-skip) (mv (cons new-var vars-to-skip) (cons new-var vars-to-return)) (mv (cons new-var (cons inverted-var vars-to-skip)) (cons new-var (cons inverted-var vars-to-return)))))) (t (mv (cons new-var vars-to-skip) (cons new-var vars-to-return))))) (defun expanded-new-vars-in-pot-lst1 (new-pot-lst vars-to-skip vars-to-return) ; We explore all the terms in new-pot-lst and collect all the ``new'' ; vars (relative to vars-to-skip) and the inverses of the vars with ; division. We also collect all the factors of the vars we collect ; above, and their inverses. We accumulate onto both vars-to-skip and ; vars-to-return, but only return the latter. (if (null new-pot-lst) vars-to-return (let ((new-var (access linear-pot (car new-pot-lst) :var))) (cond ((member-equal new-var vars-to-skip) (expanded-new-vars-in-pot-lst1 (cdr new-pot-lst) vars-to-skip vars-to-return)) ((var-with-divisionp new-var) (let* ((inverse-var (invert-var new-var)) ; We keep vars-to-skip a superset of vars-to-return. (new-vars-to-skip (if (member-equal inverse-var vars-to-skip) (cons new-var vars-to-skip) (cons new-var (cons inverse-var vars-to-skip)))) (new-vars-to-return (if (member-equal inverse-var vars-to-skip) (cons new-var vars-to-return) (cons new-var (cons inverse-var vars-to-return))))) (expanded-new-vars-in-pot-lst1 (cdr new-pot-lst) new-vars-to-skip new-vars-to-return))) ((eq (fn-symb new-var) 'BINARY-*) (mv-let (new-vars-to-skip new-vars-to-return) (expanded-new-vars-in-pot-lst2 new-var vars-to-skip vars-to-return) (expanded-new-vars-in-pot-lst1 (cdr new-pot-lst) (cons new-var new-vars-to-skip) (cons new-var new-vars-to-return)))) (t (expanded-new-vars-in-pot-lst1 (cdr new-pot-lst) (cons new-var vars-to-skip) (cons new-var vars-to-return))))))) (defun expanded-new-vars-in-pot-lst (new-pot-lst old-pot-lst) ; This is a variant of new-vars-in-pot-lst. See the comments there. ; Here, if a new var is a product, we recursively add all its individual ; factors to the list of new vars as well as the product itself. ; E. g., if a new var is (* x (foo y) (bar z)), we add ; x, (foo y), (bar z), and (* x (foo y) (bar z)) to the new var list. ; In addition, if a var or one of its factors is of the form ; 1. (/ x) or (expt (/ x) n) or ; 2. (expt x c) or (expt x (* c y)), where c is a negative integer, ; we expand the multiplicative inverse. ; We need to generate a list of vars to skip. We expand the vars in ; old-pot-lst. (let ((vars-to-skip (expanded-new-vars-in-pot-lst1 old-pot-lst nil ; vars-to-skip nil))) (varify!-lst (expanded-new-vars-in-pot-lst1 new-pot-lst vars-to-skip nil)))) (defun extract-bounds (bounds-polys) ; Bounds-polys is a list of bounds-polys as returned by bounds-polys-with-var. ; It is either nil meaning no bounds were found, a list of one element ; which is either an upper or lower bound poly, or a list of length two ; consisting of an upper and lower bound poly. We return six items --- ; any of which may be nil if it was not found --- the lower bound, its ; relation, its ttree, an upper bound, its relation, and its ttree. (cond ((null bounds-polys) (mv nil nil nil nil nil nil)) ((null (cdr bounds-polys)) ;; We have only a lower, or upper bound. (if (< (first-coefficient (car bounds-polys)) 0) ;; ((var < c)), we have only an upper bound. (mv nil nil nil (access poly (car bounds-polys) :constant) (access poly (car bounds-polys) :relation) (access poly (car bounds-polys) :ttree)) ;; ((c < var)), we have only a lower bound. (mv (- (access poly (car bounds-polys) :constant)) (access poly (car bounds-polys) :relation) (access poly (car bounds-polys) :ttree) nil nil nil))) (t ;; We have both a lower and upper bound. (if (< (first-coefficient (car bounds-polys)) 0) ;; ((var < c) (c < var)), upper bound lower bound. (mv (- (access poly (cadr bounds-polys) :constant)) (access poly (cadr bounds-polys) :relation) (access poly (cadr bounds-polys) :ttree) (access poly (car bounds-polys) :constant) (access poly (car bounds-polys) :relation) (access poly (car bounds-polys) :ttree)) ;; ((c < var) (var < c)), lower bound upper bound. (mv (- (access poly (car bounds-polys) :constant)) (access poly (car bounds-polys) :relation) (access poly (car bounds-polys) :ttree) (access poly (cadr bounds-polys) :constant) (access poly (cadr bounds-polys) :relation) (access poly (cadr bounds-polys) :ttree)))))) (defun good-bounds-in-pot (var pot-lst pt) ; Is bds good enough for deal-with-division? That is, can we use ; the information in pot-lst to bound var either away from zero or ; at zero? ; This information is needed in deal-with-division where we may, ; for instance, multiply x and (/ x). If we know that x is non-zero, ; their product will be equal to one. If we know that x is zero, their ; product is equal to zero. (let ((bounds-polys (bounds-polys-with-var var pot-lst pt))) (mv-let (var-lbd var-lbd-rel var-lbd-ttree var-ubd var-ubd-rel var-ubd-ttree) (extract-bounds bounds-polys) (declare (ignore var-lbd-ttree var-ubd-ttree)) (or (and (eql var-lbd 0) (eql var-ubd 0)) (and var-lbd (< 0 var-lbd)) (and (eql var-lbd 0) (eq var-lbd-rel '<)) (and var-ubd (< var-ubd 0)) (and (eql var-ubd 0) (eq var-ubd-rel '<)))))) (defun inverse-polys (var inv-var pot-lst ttree pt) ; Var and inv-var are as in add-inverse-polys. Ttree is the ttree ; from the call to type-set in inverse-polys. ; Bounds-polys-for-var extracts any bounds polys from pot-lst. ; Extract-bounds deconstructs any bounds polys found. This ; information is then used to try to tighten up our knowledge about ; both var and inv-var. We return a list of bounds polys constructed ; using this information. ; A couple of simple examples will help to understand what we are doing: ; 1. If we can determine that (< 4 x), we can add both (< 0 (/ x)) and ; (< (/ x) 1/4). ; 2. If we can determine that (< 0 x) and (< x 4), we can add ; (< 0 (/ x)) and (< (/ x) 1/4). ; 3. If we can only determine that (< -2 x), we cannot add anything about ; (/ x) to the pot-lst. ; We handle the symmetric cases for negative x. (if (and (good-pot-varp var) (good-pot-varp inv-var)) (let ((bounds-polys-for-var (bounds-polys-with-var var pot-lst pt)) (bounds-polys-for-inv-var (bounds-polys-with-var inv-var pot-lst pt))) (mv-let (var-lbd var-lbd-rel var-lbd-ttree var-ubd var-ubd-rel var-ubd-ttree) (extract-bounds bounds-polys-for-var) (mv-let (inv-var-lbd inv-var-lbd-rel inv-var-lbd-ttree inv-var-ubd inv-var-ubd-rel inv-var-ubd-ttree) (extract-bounds bounds-polys-for-inv-var) (cond ((and (or (eql var-lbd 0) (eql inv-var-lbd 0)) (or (eql var-ubd 0) (eql inv-var-ubd 0))) ; Assume that all four relations are <=. That is a weaker assumption ; than whatever is really the case. From that assumption, we conclude ; that var and inv-var are 0. We make sure that this is recorded in ; the pot-lst. If the actual case is that one of the relations is a ; strict <, then the case is contradictory and we can add any other ; polys we want, including these. Note that at least two of the four ; polys we are about to add are already in the pot-lst. (list ;; 0 <= var (add-linear-terms :rhs var (base-poly (cons-tag-trees ttree (cons-tag-trees var-lbd-ttree inv-var-lbd-ttree)) '<= t nil)) ;; var <= 0 (add-linear-terms :lhs var (base-poly (cons-tag-trees ttree (cons-tag-trees var-ubd-ttree inv-var-ubd-ttree)) '<= t nil)) ;; 0 <= inv-var (add-linear-terms :rhs inv-var (base-poly (cons-tag-trees ttree (cons-tag-trees var-lbd-ttree inv-var-lbd-ttree)) '<= t nil)) ;; inv-var <= 0 (add-linear-terms :lhs inv-var (base-poly (cons-tag-trees ttree (cons-tag-trees var-ubd-ttree inv-var-ubd-ttree)) '<= t nil)))) ((or (and var-lbd (< 0 var-lbd)) (and inv-var-lbd (< 0 inv-var-lbd))) ; We try to gather bounds polys in four stages --- a lower bound for inv-var, ; a lower bound for var, an upper bound for inv-var, and an upper bound ; for var. (let* ((ttree1 (cons-tag-trees ttree (cons-tag-trees var-lbd-ttree inv-var-lbd-ttree))) (bounds-polys1 (cond ((and var-ubd (not (eql var-ubd 0)) (or (null inv-var-lbd) (< inv-var-lbd (/ var-ubd)))) (list ;; (/ var-ubd) [<,<=] inv-var (add-linear-terms :lhs (kwote (/ var-ubd)) :rhs inv-var (base-poly (cons-tag-trees ttree1 var-ubd-ttree) var-ubd-rel t nil)))) ((null inv-var-lbd) (list ;; 0 < inv-var (add-linear-terms :rhs inv-var (base-poly ttree1 '< t nil)))) (t nil))) (bounds-polys2 (cond ((and inv-var-ubd (not (eql inv-var-ubd 0)) (or (null var-lbd) (< var-lbd (/ inv-var-ubd)))) (cons ;; (/ inv-var-ubd) [<,<=] var (add-linear-terms :lhs (kwote (/ inv-var-ubd)) :rhs var (base-poly (cons-tag-trees ttree1 inv-var-ubd-ttree) inv-var-ubd-rel t nil)) bounds-polys1)) ((null var-lbd) ;; 0 < var (cons (add-linear-terms :rhs var (base-poly ttree1 '< t nil)) bounds-polys1)) (t bounds-polys1))) (bounds-polys3 (cond ((and var-lbd (not (eql var-lbd 0)) (or (null inv-var-ubd) (< (/ var-lbd) inv-var-ubd))) (cons ;; inv-var [<,<=] (/ var-lbd) (add-linear-terms :lhs inv-var :rhs (kwote (/ var-lbd)) (base-poly ttree1 var-lbd-rel t nil)) bounds-polys2)) (t bounds-polys2))) (bounds-polys4 (cond ((and inv-var-lbd (not (eql inv-var-lbd 0)) (or (null var-ubd) (< (/ inv-var-lbd) var-ubd))) (cons ;; var [<,<=] (/ inv-var-lbd) (add-linear-terms :lhs var :rhs (kwote (/ inv-var-lbd)) (base-poly ttree1 inv-var-lbd-rel t nil)) bounds-polys3)) (t bounds-polys3)))) bounds-polys4)) ((or (and var-ubd (< var-ubd 0)) (and inv-var-ubd (< inv-var-ubd 0))) ; We try to gather bounds polys in four stages --- a upper bound for inv-var, ; a upper bound for var, an lower bound for inv-var, and an lower bound ; for var. (let* ((ttree1 (cons-tag-trees ttree (cons-tag-trees var-ubd-ttree inv-var-ubd-ttree))) (bounds-polys1 (cond ((and var-lbd (not (eql var-lbd 0)) (or (null inv-var-ubd) (< (/ var-lbd) inv-var-ubd))) (list ;; inv-var [<,<=] (/ var-lbd) (add-linear-terms :lhs inv-var :rhs (kwote (/ var-lbd)) (base-poly (cons-tag-trees ttree1 var-lbd-ttree) var-lbd-rel t nil)))) ((null inv-var-ubd) (list ;; inv-var < 0 (add-linear-terms :lhs inv-var (base-poly ttree1 '< t nil)))) (t nil))) (bounds-polys2 (cond ((and inv-var-lbd (not (eql inv-var-lbd 0)) (or (null var-ubd) (< (/ inv-var-lbd) var-ubd))) (cons ;; var [<,<=] (/ inv-var-lbd) (add-linear-terms :lhs var :rhs (kwote (/ inv-var-lbd)) (base-poly (cons-tag-trees ttree1 inv-var-lbd-ttree) var-lbd-rel t nil)) bounds-polys1)) ((null var-ubd) ;; var < 0 (cons (add-linear-terms :lhs var (base-poly ttree1 '< t nil)) bounds-polys1)) (t bounds-polys1))) (bounds-polys3 (cond ((and var-ubd (not (eql var-ubd 0)) (or (null inv-var-lbd) (< inv-var-lbd (/ var-ubd)))) (cons ;; (/ var-ubd) [<,<=] inv-var (add-linear-terms :lhs (kwote (/ var-ubd)) :rhs inv-var (base-poly ttree1 var-ubd-rel t nil)) bounds-polys2)) (t bounds-polys2))) (bounds-polys4 (cond ((and inv-var-ubd (not (eql inv-var-ubd 0)) (or (null var-lbd) (< var-lbd (/ inv-var-ubd)))) (cons ;; (/ inv-var-ubd) [<,<=] var (add-linear-terms :lhs (kwote (/ inv-var-ubd)) :rhs var (base-poly ttree1 inv-var-ubd-rel t nil)) bounds-polys3)) (t bounds-polys3)))) bounds-polys4)) ((and (eql var-lbd 0) (eq var-lbd-rel '<)) ;; 0 < inv-var (list (add-linear-terms :rhs inv-var (base-poly (cons-tag-trees ttree var-lbd-ttree) '< t nil)))) ((and (eql inv-var-lbd 0) (eq inv-var-lbd-rel '<)) ;; 0 < var (list (add-linear-terms :rhs var (base-poly (cons-tag-trees ttree inv-var-lbd-ttree) '< t nil)))) ((and (eql var-ubd 0) (eq var-ubd-rel '<)) ;; inv-var < 0 (list (add-linear-terms :lhs inv-var (base-poly (cons-tag-trees ttree var-ubd-ttree) '< t nil)))) ((and (eql inv-var-ubd 0) (eq inv-var-ubd-rel '<)) ;; var < 0 (list (add-linear-terms :lhs var (base-poly (cons-tag-trees ttree inv-var-ubd-ttree) '< t nil)))) (t nil))))) (er hard 'inverse-polys "A presumptive pot-label, ~x0, has turned out to be illegitimate. ~ If possible, please send a script reproducing this error ~ to the authors of ACL2." (if (good-pot-varp var) inv-var var)))) (defun add-inverse-polys (var type-alist wrld simplify-clause-pot-lst force-flg ens pt) ; If var is of the form ; 1. (/ x) or (expt (/ x) n) and x is rational, or ; 2. (expt x c) or (expt x (* c y)), where c is a negative integer ; and x is rational, ; we try to find bounds for either var or its multiplicative inverse and ; then use any bounds found to bound the other. The work of gathering ; the polys is done in inverse-polys. (if (var-with-divisionp var) (let ((inverted-var (invert-var var))) (mv-let (base-ts base-ttree) (type-set inverted-var force-flg nil ; dwp type-alist ens wrld nil ; ttree nil ; pot-lst nil) ; pt (if (ts-real/rationalp base-ts) (let ((inverse-polys (inverse-polys var inverted-var simplify-clause-pot-lst base-ttree pt))) (add-polys inverse-polys simplify-clause-pot-lst pt t ; nonlinearp hint type-alist ens force-flg wrld)) (mv nil simplify-clause-pot-lst)))) (mv nil simplify-clause-pot-lst))) (defun add-polys-from-type-set (var pot-lst type-alist pt force-flg ens wrld) ; At the time of this writing, this function is called only from ; add-polys-and-lemmas3. When doing non-linear arithmetic, we ; have found this extra information gathering useful. ; Warning: This function should not be used with any terms that are ; not legitimate pot-vars. See the definition of good-pot-varp. ; Assuming that term is a legitimate pot-label --- meets all the ; invariants --- we do not have to normalize any of the polys below. ; It would, however, not be very expensive to wrap the below in a call ; to normalize-poly-lst. (if (good-pot-varp var) (add-polys (polys-from-type-set var force-flg nil ; dwp type-alist ens wrld nil) ; ttree pot-lst pt t ; nonlinearp hint type-alist ens force-flg wrld) (mv (er hard 'add-polys-from-type-set "A presumptive pot-label, ~x0, has turned out to be illegitimate. ~ If possible, please send a script reproducing this error ~ to the authors of ACL2." var) nil))) (defun length-of-shortest-polys-with-var (poly-lst pt n) ; Poly-lst is a list of polys, pt is a parent tree of polys to be ; ignored, and n is the length of the shortest alist found so far, or ; t if we haven't found any yet. We cdr down pot-lst looking for a ; poly whose alist is shorter than n, and return the length of the ; shortest alist found. (cond ((null poly-lst) n) ((and (or (eq n t) (< (length (access poly (car poly-lst) :alist)) n)) (not (ignore-polyp (access poly (car poly-lst) :parents) pt))) (length-of-shortest-polys-with-var (cdr poly-lst) pt (length (access poly (car poly-lst) :alist)))) (t (length-of-shortest-polys-with-var (cdr poly-lst) pt n)))) (defun shortest-polys-with-var1 (poly-lst pt n) (cond ((or (null poly-lst) (eq n t)) nil) ((and (or (equal (length (access poly (car poly-lst) :alist)) n) (equal (length (access poly (car poly-lst) :alist)) (+ n 1))) (not (ignore-polyp (access poly (car poly-lst) :parents) pt))) (cons (car poly-lst) (shortest-polys-with-var1 (cdr poly-lst) pt n))) (t (shortest-polys-with-var1 (cdr poly-lst) pt n)))) (defun shortest-polys-with-var (var pot-lst pt) ; Var is a pot-label in pot-lst and pt is a parent tree of polys to ignore. ; We return a list of the polys with the shortest alists in the pot ; labeled with var. These polys can be considered as the ``simplest'' ; polys about var. (cond ((null pot-lst) nil) ((equal var (access linear-pot (car pot-lst) :var)) ; We have found the pot we are looking for. We find the length of the ; shortest polys in the pot. We then find all the polys in the pot ; with alists of that length, and return a list of those polys. (let ((n (length-of-shortest-polys-with-var (append (access linear-pot (car pot-lst) :negatives) (access linear-pot (car pot-lst) :positives)) pt t))) (append (shortest-polys-with-var1 (access linear-pot (car pot-lst) :negatives) pt n) (shortest-polys-with-var1 (access linear-pot (car pot-lst) :positives) pt n)))) (t (shortest-polys-with-var var (cdr pot-lst) pt)))) (defun binary-*-leaves (term) (if (eq (fn-symb term) 'BINARY-*) (cons (fargn term 1) (binary-*-leaves (fargn term 2))) (list term))) (defun binary-*-tree (leaves) ; Return the BINARY-* term with leaves at its tips. In practice, ; leaves always contains at least two elements. (cond ((null (cdr leaves)) (car leaves)) ((null (cddr leaves)) (cons-term 'BINARY-* (list (car leaves) (cadr leaves)))) (t (cons-term 'BINARY-* (list (car leaves) (binary-*-tree (cdr leaves))))))) (defun merge-arith-term-order (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((arith-term-order (car l2) (car l1)) (cons (car l2) (merge-arith-term-order l1 (cdr l2)))) (t (cons (car l1) (merge-arith-term-order (cdr l1) l2))))) (defun insert-arith-term-order (item list) (cond ((null list) (list item)) ((arith-term-order item (car list)) (cons item list)) (t (cons (car list) (insert-arith-term-order item (cdr list)))))) (defun sort-arith-term-order (list) (cond ((null list) nil) (t (insert-arith-term-order (car list) (sort-arith-term-order (cdr list)))))) (defun multiply-alist-and-const (alist const poly) (cond ((null alist) poly) (t (let ((temp-poly (add-linear-term (cons-term 'BINARY-* (list (kwote (* const (cdar alist))) (caar alist))) 'rhs poly))) (multiply-alist-and-const (cdr alist) const temp-poly))))) (defun collect-rational-poly-p-lst (poly-lst) (cond ((endp poly-lst) nil) ((access poly (car poly-lst) :rational-poly-p) (cons (car poly-lst) (collect-rational-poly-p-lst (cdr poly-lst)))) (t (collect-rational-poly-p-lst (cdr poly-lst))))) acl2-sources/openmcl-acl2-trace.lisp0000666002132200015000000002063212222115527017020 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; This file originally written by: Robert Krug ; email: rkrug@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We don't intend this file to be compiled. ; TRACE stuff ; This file is allegro-acl2-trace, with modifications. ; CCL's trace facilities are somewhat limited. However it does have a ; function, advise, which is sufficiently general to allow it to imitate GCL's ; trace facilities as provided within ACL2. This function seems to have ; limited documentation, but see the file ccl/lib/encapsulate.lisp in the ; CCL sources. ; We put over into old-trace the macro for trace that comes with CCL. ; Thus one can type (old-trace foo) and get the effect that (trace ; foo) would have previously provided. We do not guarantee that using ; old-trace works well with trace$, however. (cond ((null (macro-function 'old-trace)) (setf (macro-function 'old-trace) (macro-function 'trace)))) (cond ((null (macro-function 'old-untrace)) (setf (macro-function 'old-untrace) (macro-function 'untrace)))) ; The variables *trace-arglist* and *trace-values* will contain the ; cleaned up arglist and values of a traced function. The alist ; *trace-sublis* allows one to refer to these variables by more ; common names. (defvar *trace-arglist*) (defvar *trace-values*) (defparameter *trace-sublis* '((values . *trace-values*) (ccl::values . *trace-values*) (arglist . *trace-arglist*) (ccl::arglist . *trace-arglist*) )) (defun trace-pre-process (lst &aux (state *the-live-state*)) ; The user has provided arguments lst to trace. Here we return the result of ; converting lst to a list of entries (fn orig-fn . rst). Each fn in lst is ; treated as (fn), and then each (fn . rst) is replaced with (fn fn . rst) and, ; if fn is known to ACL2, also (*1*fn fn . rst). (let ((new-lst nil)) (dolist (x lst new-lst) (let ((sym (cond ((symbolp x) x) ((and (consp x) (symbolp (car x))) (car x)) (t (interface-er "Not a symbol or a cons of a symbol: ~x0" x))))) (if (function-symbolp sym (w state)) ; We have an ACL2 function. (cond ((symbolp x) (push (list (*1*-symbol x) x) new-lst) (push (list x x) new-lst)) (t (push (list* (*1*-symbol (car x)) (car x) (cdr x)) new-lst) (push (list* (car x) (car x) (cdr x)) new-lst))) ; We do not have an ACL2 function. (if (fboundp sym) (if (symbolp x) (push (list x x) new-lst) (push (list* (car x) (car x) (cdr x)) new-lst)) (interface-er "~s0 is not a bound function symbol." sym))))))) (defun trace-entry (name l) ; We construct the (ccl:advise ... :when :before) form that performs the ; tracing on entry. (cond ((null l) ; no :entry directive was found `(ccl:advise ,name (progn (setq *trace-arglist* ccl::arglist) (custom-trace-ppr :in (cons ',name (trace-hide-world-and-state *trace-arglist*)))) :when :before)) ((eq (car l) :entry) `(ccl:advise ,name (progn (setq *trace-arglist* ccl::arglist) (custom-trace-ppr :in (cons ',name (trace-hide-world-and-state ,(sublis *trace-sublis* (cadr l)))))) :when :before)) (t (trace-entry name (cdr l))))) (defun trace-values (name) (declare (ignore name)) 'values) #-acl2-mv-as-values (error "Trace-exit probably needs to be modified for CCL if we are to ~%~ avoid feature acl2-mv-as-values. See corresponding mods made for ~%~ Version 3.4 in akcl-acl2-trace.lisp and allegro-acl2-trace.lisp.") (defun trace-exit (name original-name l) ; We construct the (ccl:advise ... :when :after) form that performs ; the tracing on exit. (cond ((null l) `(ccl:advise ,name (progn (setq *trace-values* ,(trace-values original-name)) (custom-trace-ppr :out (cons ',name (trace-hide-world-and-state *trace-values*)))) :when :after)) ((eq (car l) :exit) `(ccl:advise ,name (progn (setq *trace-values* ,(trace-values original-name)) (setq *trace-arglist* ccl::arglist) (custom-trace-ppr :out (cons ',name (trace-hide-world-and-state ,(sublis *trace-sublis* (cadr l)))))) :when :after)) (t (trace-exit name original-name (cdr l))))) (defun traced-fns-lst (lst) (list 'QUOTE (mapcar #'car lst))) (defun trace-process (lst) ; We perform a little error checking, and gather together all the (ccl:advise ; ...) calls. (let ((new-lst (list (traced-fns-lst lst)))) ; for the returned value (dolist (x lst new-lst) (cond ((member :cond (cddr x)) (interface-er "The use of :cond is not supported in CCL.")) ((member :break (cddr x)) (interface-er "The use of :break is not supported in CCL. ~ However, you can use either (~s0 :entry (break)) ~ or (~s0 :exit (break)). See any Lisp ~ documentation for more on break and its options." (car x))) (t (push (trace-exit (car x) (cadr x) (cddr x)) new-lst) (push (trace-entry (car x) (cddr x)) new-lst) (push `(ccl:unadvise ,(car x)) new-lst)))))) (defun acl2-traced-fns () (sort (delete-duplicates (strip-cars (ccl:advisedp t))) #'symbol-<)) (let ((temp ccl::*warn-if-redefine-kernel*)) (setf ccl::*warn-if-redefine-kernel* nil) (defmacro trace (&rest fns) (if fns (cons 'progn (trace-process (trace-pre-process fns))) '(acl2-traced-fns))) (setf ccl::*warn-if-redefine-kernel* temp)) (let ((temp ccl::*warn-if-redefine-kernel*)) (setf ccl::*warn-if-redefine-kernel* nil) (defmacro untrace (&rest fns) (if (null fns) '(prog1 (acl2-traced-fns) (ccl:unadvise t)) (cons 'progn (let ((ans nil)) (dolist (fn fns ans) (push `(when (fboundp ',fn) (ccl:unadvise ,fn)) ans) (push `(when (fboundp ',(*1*-symbol fn)) (ccl:unadvise ,(*1*-symbol fn))) ans)))))) (setf ccl::*warn-if-redefine-kernel* temp)) acl2-sources/other-events.lisp0000664002132200015000000630211712222115527016100 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; We permit macros under the following constraints on the args. ; 1. No destructuring. (Maybe some day.) ; 2. No &aux. (LET* is better.) ; 3. Initforms must be quotes. (Too hard for us to do evaluation right.) ; 4. No &environment. (Just not clearly enough specified in CLTL.) ; 5. No nonstandard lambda-keywords. (Of course.) ; 6. No multiple uses of :allow-other-keys. (Implementations differ.) ; There are three nests of functions that have the same view of ; the subset of macro args that we support: macro-vars..., ; chk-macro-arglist..., and bind-macro-args... Of course, it is ; necessary to keep them all with the same view of the subset. ; The following code is a ``pseudo'' translation of the functions between ; chk-legal-init-msg and chk-macro-arglist. Those checkers cause errors when ; their requirements are violated and these functions are just predicates. ; However, they are ``pseudo'' translations because they do not check, for ; example, that alleged variable symbols really are legal variable symbols. ; They are used in the guards for the functions leading up to and including ; macro-vars, which recovers all the variable symbols used in the formals list ; of an acceptable defmacro. (defun legal-initp (x) (and (consp x) (true-listp x) (equal 2 (length x)) (eq (car x) 'quote))) ; The following function is just the negation of chk-macro-arglist-keysp, when ; applied to a true-listp args. The reason it must be applied to a true-listp ; is that macro-arglist-keysp terminates on an endp test and its counterpart ; checker terminates on a null test and may recur one additional time on ; non-true-lists. (defun macro-arglist-keysp (args keys-passed) (declare (xargs :guard (and (true-listp args) (true-listp keys-passed)))) (cond ((endp args) t) ((eq (car args) '&allow-other-keys) (null (cdr args))) ((atom (car args)) (cond ((symbolp (car args)) (let ((new (intern (symbol-name (car args)) "KEYWORD"))) (and (not (member new keys-passed)) (macro-arglist-keysp (cdr args) (cons new keys-passed))))) (t nil))) ((or (not (true-listp (car args))) (> (length (car args)) 3)) nil) (t (and (or (symbolp (caar args)) (and (true-listp (caar args)) (equal (length (caar args)) 2) (keywordp (car (caar args))) (symbolp (cadr (caar args))))) (implies (> (length (car args)) 1) (legal-initp (cadr (car args)))) (implies (> (length (car args)) 2) (symbolp (caddr (car args)))) (let ((new (cond ((symbolp (caar args)) (intern (symbol-name (caar args)) "KEYWORD")) (t (car (caar args)))))) (and (not (member new keys-passed)) (macro-arglist-keysp (cdr args) (cons new keys-passed)))))))) (defun macro-arglist-after-restp (args) (declare (xargs :guard (true-listp args))) (cond ((endp args) t) ((eq (car args) '&key) (macro-arglist-keysp (cdr args) nil)) (t nil))) (defun macro-arglist-optionalp (args) (declare (xargs :guard (true-listp args))) (cond ((endp args) t) ((member (car args) '(&rest &body)) (cond ((and (cdr args) (symbolp (cadr args)) (not (lambda-keywordp (cadr args)))) (macro-arglist-after-restp (cddr args))) (t nil))) ((eq (car args) '&key) (macro-arglist-keysp (cdr args) nil)) ((symbolp (car args)) (macro-arglist-optionalp (cdr args))) ((or (atom (car args)) (not (true-listp (car args))) (not (< (length (car args)) 4))) nil) ((not (symbolp (car (car args)))) nil) ((and (> (length (car args)) 1) (not (legal-initp (cadr (car args))))) nil) ((and (equal (length (car args)) 3) (not (symbolp (caddr (car args))))) nil) (t (macro-arglist-optionalp (cdr args))))) (defun macro-arglist1p (args) (declare (xargs :guard (true-listp args))) (cond ((endp args) t) ((not (symbolp (car args))) nil) ((member (car args) '(&rest &body)) (cond ((and (cdr args) (symbolp (cadr args)) (not (lambda-keywordp (cadr args)))) (macro-arglist-after-restp (cddr args))) (t nil))) ((eq (car args) '&optional) (macro-arglist-optionalp (cdr args))) ((eq (car args) '&key) (macro-arglist-keysp (cdr args) nil)) (t (macro-arglist1p (cdr args))))) (defun subsequencep (lst1 lst2) (declare (xargs :guard (and (eqlable-listp lst1) (true-listp lst2)))) ; We return t iff lst1 is a subsequence of lst2, in the sense that ; '(a c e) is a subsequence of '(a b c d e f) but '(a c b) is not. (cond ((endp lst1) t) (t (let ((tl (member (car lst1) lst2))) (cond ((endp tl) nil) (t (subsequencep (cdr lst1) (cdr tl)))))))) (defun collect-lambda-keywordps (lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((lambda-keywordp (car lst)) (cons (car lst) (collect-lambda-keywordps (cdr lst)))) (t (collect-lambda-keywordps (cdr lst))))) (defun macro-args-structurep (args) (declare (xargs :guard t)) (and (true-listp args) (let ((lambda-keywords (collect-lambda-keywordps args))) (and (or (subsequencep lambda-keywords '(&whole &optional &rest &key &allow-other-keys)) (subsequencep lambda-keywords '(&whole &optional &body &key &allow-other-keys))) (and (not (member-eq '&whole (cdr args))) (implies (member-eq '&allow-other-keys args) (member-eq '&allow-other-keys (member-eq '&key args))) (implies (eq (car args) '&whole) (and (consp (cdr args)) (symbolp (cadr args)) (not (lambda-keywordp (cadr args))) (macro-arglist1p (cddr args)))) (macro-arglist1p args)))))) (defun macro-vars-key (args) (declare (xargs :guard (and (true-listp args) (macro-arglist-keysp args nil)))) ; We have passed &key. (cond ((endp args) nil) ((eq (car args) '&allow-other-keys) (cond ((null (cdr args)) nil) (t (er hard nil "macro-vars-key")))) ((atom (car args)) (cons (car args) (macro-vars-key (cdr args)))) (t (let ((formal (cond ((atom (car (car args))) (car (car args))) (t (cadr (car (car args))))))) (cond ((int= (length (car args)) 3) (cons formal (cons (caddr (car args)) (macro-vars-key (cdr args))))) (t (cons formal (macro-vars-key (cdr args))))))))) (defun macro-vars-after-rest (args) ; We have just passed &rest or &body. (declare (xargs :guard (and (true-listp args) (macro-arglist-after-restp args)))) (cond ((endp args) nil) ((eq (car args) '&key) (macro-vars-key (cdr args))) (t (er hard nil "macro-vars-after-rest")))) (defun macro-vars-optional (args) (declare (xargs :guard (and (true-listp args) (macro-arglist-optionalp args)))) ; We have passed &optional but not &key or &rest or &body. (cond ((endp args) nil) ((eq (car args) '&key) (macro-vars-key (cdr args))) ((member (car args) '(&rest &body)) (cons (cadr args) (macro-vars-after-rest (cddr args)))) ((symbolp (car args)) (cons (car args) (macro-vars-optional (cdr args)))) ((int= (length (car args)) 3) (cons (caar args) (cons (caddr (car args)) (macro-vars-optional (cdr args))))) (t (cons (caar args) (macro-vars-optional (cdr args)))))) (defun macro-vars (args) (declare (xargs :guard (macro-args-structurep args) :guard-hints (("Goal" :in-theory (disable LAMBDA-KEYWORDP))))) (cond ((endp args) nil) ((eq (car args) '&whole) (cons (cadr args) (macro-vars (cddr args)))) ((member (car args) '(&rest &body)) (cons (cadr args) (macro-vars-after-rest (cddr args)))) ((eq (car args) '&optional) (macro-vars-optional (cdr args))) ((eq (car args) '&key) (macro-vars-key (cdr args))) ((or (not (symbolp (car args))) (lambda-keywordp (car args))) (er hard nil "macro-vars")) (t (cons (car args) (macro-vars (cdr args)))))) (defun chk-legal-defconst-name (name state) (cond ((legal-constantp name) (value nil)) ((legal-variable-or-constant-namep name) (er soft (cons 'defconst name) "The symbol ~x0 may not be declared as a constant because ~ it does not begin and end with the character *." name)) (t (er soft (cons 'defconst name) "Constant symbols must ~*0. Thus, ~x1 may not be ~ declared as a constant. See :DOC name and :DOC ~ defconst." (tilde-@-illegal-variable-or-constant-name-phrase name) name)))) (defun defconst-fn1 (name val doc doc-pair w state) (let ((w (update-doc-database name doc doc-pair (putprop name 'const (kwote val) w)))) (value w))) #-acl2-loop-only (progn ; See the Essay on Hash Table Support for Compilation. (defvar *hcomp-fn-ht* nil) (defvar *hcomp-const-ht* nil) (defvar *hcomp-macro-ht* nil) (defvar *hcomp-fn-alist* nil) (defvar *hcomp-const-alist* nil) (defvar *hcomp-macro-alist* nil) (defconstant *hcomp-fake-value* 'acl2_invisible::hcomp-fake-value) (defvar *hcomp-book-ht* nil) (defvar *hcomp-const-restore-ht* nil) (defvar *hcomp-fn-macro-restore-ht* ; We use a single hash table to restore both function and macro definitions. ; In v4-0 and v4-1 we had separate hash tables for these, but after a bug ; report from Jared Davis that amounted to a CCL issue (error upon redefining a ; macro as a function), we discovered an ACL2 issue, which we now describe ; using an example. ; In our example, the file fn.lisp has the definition ; (defun f (x) ; (declare (xargs :guard t)) ; (cons x x)) ; while the file mac.lisp has this: ; (defmacro f (x) ; x) ; After certifying both books in v4-1, the following sequence of events then ; causes the error shown below in v4-1, as does the sequence obtained by ; switching the order of the include-book forms. The problem in both cases is ; a failure to restore properly the original definition of f after the failed ; include-book. ; (include-book "fn") ; (include-book "mac") ; fails, as expected (redefinition error) ; (defun g (x) ; (declare (xargs :guard t)) ; (f x)) ; (g 3) ; "Error: The function F is undefined." ; By using a single hash table (in functions hcomp-init and hcomp-restore-defs) ; we avoid this problem. nil) (defvar *declaim-list* nil) ) (defrec hcomp-book-ht-entry ; Note that the status field has value COMPLETE, TO-BE-COMPILED, or INCOMPLETE; ; the value of this field is never nil. The other fields can be nil if the ; status field is such that we don't need them. (status fn-ht const-ht macro-ht) t) #-acl2-loop-only (defun defconst-val-raw (full-book-name name) (let* ((entry (and *hcomp-book-ht* (gethash full-book-name *hcomp-book-ht*))) (const-ht (and entry (access hcomp-book-ht-entry entry :const-ht)))) (cond (const-ht (multiple-value-bind (val present-p) (gethash name const-ht) (cond (present-p val) (t *hcomp-fake-value*)))) (t *hcomp-fake-value*)))) (defun defconst-val (name form ctx wrld state) #+acl2-loop-only (declare (ignore name)) #-acl2-loop-only (let ((full-book-name (car (global-val 'include-book-path wrld)))) (when full-book-name (let ((val (defconst-val-raw full-book-name name))) (when (not (eq val *hcomp-fake-value*)) (return-from defconst-val (value val)))))) (er-let* ((pair (state-global-let* ((safe-mode ; Warning: If you are tempted to bind safe-mode to nil outside the boot-strap, ; then revisit the binding of *safe-mode-verified-p* to t in the ; #-acl2-loop-only definition of defconst. See the defparameter for ; *safe-mode-verified-p*. ; Why do we need to bind safe-mode to t? An important reason is that we will ; be loading compiled files corresponding to certified books, where defconst ; forms will be evaluated in raw Lisp. By using safe-mode, we can guarantee ; that these evaluations were free of guard violations when certifying the ; book, and hence will be free of guard violations when loading such compiled ; files. ; But even before we started loading compiled files before processing ; include-book events (i.e., up through Version_3.6.1), safe-mode played an ; important role. The following legacy comment explains: ; Otherwise [without safe-mode bound to t], if we certify book char-bug-sub ; with a GCL image then we can certify char-bug with an Allegro image, thus ; proving nil. The problem is that f1 is not properly guarded, yet we go ; directly into the raw Lisp version of f1 when evaluating the defconst. That ; is just the sort of problem that safe-mode prevents. See also :doc ; note-2-9-3 for another example, and see the comment about safe-mode related ; to redundancy of a :program mode defun with a previous :logic mode defun, in ; redundant-or-reclassifying-defunp. And before deciding to remove safe-mode ; here, consider an example like this: ; (defun foo () (declare (xargs :mode :program)) (mbe :logic t :exec nil)) ; (defconst *a* (foo)) ; ... followed by a theorem about *a*. If *a* is proved nil, that could ; conflict with a theorem that *a* is t proved after (verify-termination foo). ; Anyhow, here is the char-bug-sub example mentioned above. ; ;;; char-bug-sub.lisp ; (in-package "ACL2") ; ; (defun f1 () ; (declare (xargs :mode :program)) ; (char-upcase (code-char 224))) ; ; (defconst *b* (f1)) ; ; (defthm gcl-not-allegro ; (equal (code-char 224) *b*) ; :rule-classes nil) ; ;;; char-bug.lisp ; (in-package "ACL2") ; ; (include-book "char-bug-sub") ; ; (defthm ouch ; nil ; :hints (("Goal" :use gcl-not-allegro)) ; :rule-classes nil) ; However, it is not practical to bind safe-mode to t during the boot-strap ; with user::*fast-acl2-gcl-build*, because we have not yet compiled the *1* ; functions (see add-trip). For the sake of uniformity, we go ahead and allow ; raw Lisp calls, avoiding safe mode during the boot-strap, even for other ; lisps. (not (global-val 'boot-strap-flg wrld)))) (simple-translate-and-eval form nil nil "The second argument of defconst" ctx wrld state nil)))) (value (cdr pair)))) (defun defconst-fn (name form state doc event-form) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defconst name)) (let ((wrld1 (w state)) (event-form (or event-form (list* 'defconst name form (if doc (list doc) nil))))) (er-progn (chk-all-but-new-name name ctx 'const wrld1 state) (chk-legal-defconst-name name state) (let ((const-prop (getprop name 'const nil 'current-acl2-world wrld1))) (cond ((and const-prop (not (ld-redefinition-action state)) (equal event-form (get-event name wrld1))) ; We stop the redundant event even before evaluating the form. We believe ; that this is merely an optimization, even if the form calls compress1 or ; compress2 (which will not update the 'acl2-array property when supplied the ; same input as the last time the compress function was called). We avoid this ; optimization if redefinition is on, in case we have redefined a constant or ; macro used in the body of this defconst form. (stop-redundant-event ctx state)) (t (er-let* ((val (defconst-val name form ctx wrld1 state))) (cond ((and (consp const-prop) (equal (cadr const-prop) val)) ; When we store the 'const property, we kwote it so that it is a term. ; Thus, if there is no 'const property, we will getprop the nil and ; the consp will fail. (stop-redundant-event ctx state)) (t (enforce-redundancy event-form ctx wrld1 (er-let* ((wrld2 (chk-just-new-name name 'const nil ctx wrld1 state)) (doc-pair (translate-doc name doc ctx state)) (wrld3 (defconst-fn1 name val doc doc-pair wrld2 state))) (install-event name event-form 'defconst name nil (list 'defconst name form val) nil nil wrld3 state))))))))))))) (defun chk-legal-init-msg (x) ; See the note in chk-macro-arglist before changing this fn to ; translate the init value. (cond ((and (consp x) (true-listp x) (int= 2 (length x)) (eq (car x) 'quote)) nil) (t (msg "Illegal initial value. In ACL2 we require that initial ~ values be quoted forms and you used ~x0.~#1~[ You should ~ just write '~x0 instead. Warren Teitelman once remarked ~ that it was really dumb of a Fortran compiler to say ~ ``missing comma!'' ``If it knows a comma is missing, why ~ not just put one in?'' Indeed.~/~] See :DOC macro-args." x (if (or (eq x nil) (eq x t) (acl2-numberp x) (stringp x) (characterp x)) 0 1))))) (defun chk-legal-init (x ctx state) (let ((msg (chk-legal-init-msg x))) (cond (msg (er soft ctx "~@0" msg)) (t (value nil))))) (defun chk-macro-arglist-keys (args keys-passed) (cond ((null args) nil) ((eq (car args) '&allow-other-keys) (cond ((null (cdr args)) nil) (t (msg "&ALLOW-OTHER-KEYS may only occur as the last member ~ of an arglist so it is illegal to follow it with ~x0. ~ See :DOC macro-args." (cadr args))))) ((atom (car args)) (cond ((symbolp (car args)) (let ((new (intern (symbol-name (car args)) "KEYWORD"))) (cond ((member new keys-passed) (msg "The symbol-name of each keyword parameter ~ specifier must be distinct. But you have used ~ the symbol-name ~s0 twice. See :DOC ~ macro-args." (symbol-name (car args)))) (t (chk-macro-arglist-keys (cdr args) (cons new keys-passed)))))) (t (msg "Each keyword parameter specifier must be either a ~ symbol or a list. Thus, ~x0 is illegal. See :DOC ~ macro-args." (car args))))) ((or (not (true-listp (car args))) (> (length (car args)) 3)) (msg "Each keyword parameter specifier must be either a symbol or a ~ truelist of length 1, 2, or 3. Thus, ~x0 is illegal. See ~ :DOC macro-args." (car args))) (t (or (cond ((symbolp (caar args)) nil) (t (cond ((or (not (true-listp (caar args))) (not (equal (length (caar args)) 2)) (not (keywordp (car (caar args)))) (not (symbolp (cadr (caar args))))) (msg "Keyword parameter specifiers in which ~ the keyword is specified explicitly, ~ e.g., specifiers of the form ((:key var) ~ init svar), must begin with a truelist ~ of length 2 whose first element is a ~ keyword and whose second element is a ~ symbol. Thus, ~x0 is illegal. See :DOC ~ macro-args." (car args))) (t nil)))) (let ((new (cond ((symbolp (caar args)) (intern (symbol-name (caar args)) "KEYWORD")) (t (car (caar args)))))) (or (cond ((member new keys-passed) (msg "The symbol-name of each keyword parameter ~ specifier must be distinct. But you have used ~ the symbol-name ~s0 twice. See :DOC ~ macro-args." (symbol-name new))) (t nil)) (cond ((> (length (car args)) 1) (chk-legal-init-msg (cadr (car args)))) (t nil)) (cond ((> (length (car args)) 2) (cond ((symbolp (caddr (car args))) nil) (t (msg "~x0 is an illegal keyword parameter ~ specifier because the ``svar'' ~ specified, ~x1, is not a symbol. See ~ :DOC macro-args." (car args) (caddr (car args)))))) (t nil)) (chk-macro-arglist-keys (cdr args) (cons new keys-passed)))))))) (defun chk-macro-arglist-after-rest (args) (cond ((null args) nil) ((eq (car args) '&key) (chk-macro-arglist-keys (cdr args) nil)) (t (msg "Only keyword specs may follow &REST or &BODY. See :DOC ~ macro-args.")))) (defun chk-macro-arglist-optional (args) (cond ((null args) nil) ((member (car args) '(&rest &body)) (cond ((and (cdr args) (symbolp (cadr args)) (not (lambda-keywordp (cadr args)))) (chk-macro-arglist-after-rest (cddr args))) (t (msg "~x0 must be followed by a variable symbol. See :DOC ~ macro-args." (car args))))) ((eq (car args) '&key) (chk-macro-arglist-keys (cdr args) nil)) ((symbolp (car args)) (chk-macro-arglist-optional (cdr args))) ((or (atom (car args)) (not (true-listp (car args))) (not (< (length (car args)) 4))) (msg "Each optional parameter specifier must be either a symbol or a ~ true list of length 1, 2, or 3. ~x0 is thus illegal. See ~ :DOC macro-args." (car args))) ((not (symbolp (car (car args)))) (msg "~x0 is an illegal optional parameter specifier because the ~ ``variable symbol'' used is not a symbol. See :DOC macro-args." (car args))) ((and (> (length (car args)) 1) (chk-legal-init-msg (cadr (car args))))) ((and (int= (length (car args)) 3) (not (symbolp (caddr (car args))))) (msg "~x0 is an illegal optional parameter specifier because the ~ ``svar'' specified, ~x1, is not a symbol. See :DOC macro-args." (car args) (caddr (car args)))) (t (chk-macro-arglist-optional (cdr args))))) (defun chk-macro-arglist1 (args) (cond ((null args) nil) ((not (symbolp (car args))) (msg "~x0 is illegal as the name of a required formal parameter. ~ See :DOC macro-args." (car args))) ((member (car args) '(&rest &body)) (cond ((and (cdr args) (symbolp (cadr args)) (not (lambda-keywordp (cadr args)))) (chk-macro-arglist-after-rest (cddr args))) (t (msg "~x0 must be followed by a variable symbol. See :DOC ~ macro-args." (car args))))) ((eq (car args) '&optional) (chk-macro-arglist-optional (cdr args))) ((eq (car args) '&key) (chk-macro-arglist-keys (cdr args) nil)) (t (chk-macro-arglist1 (cdr args))))) (defun chk-macro-arglist-msg (args chk-state wrld) ; This "-msg" function supports the community book books/misc/defmac.lisp. ; Any modification to this function and its subordinates must cause ; one to reflect on the two function nests bind-macro-args... and ; macro-vars... because they assume the presence of the structure that ; this function checks for. See the comment before macro-vars for the ; restrictions we impose on macros. ; The subordinates of this function do not check that symbols that ; occur in binding spots are non-keywords and non-constants and ; without duplicates. That check is performed here, with chk-arglist, ; as a final pass. ; Important Note: If ever we change this function so that instead of ; just checking the args it "translates" the args, so that it returns ; the translated form of a proper arglist, then we must visit a similar ; change on the function primordial-event-macro-and-fn, which currently ; assumes that if a defmacro will be processed without error then ; the macro-args are exactly as presented in the defmacro. ; The idea of translating macro args is not ludicrous. For example, ; the init-forms in keyword parameters must be quoted right now. We might ; want to allow naked numbers or strings or t or nil. But then we'd ; better go look at primordial-event-macro-and-fn. ; It is very suspicious to think about allowing the init forms to be ; anything but quoted constants because Common Lisp is very vague about ; when you get the bindings for free variables in such expressions ; or when such forms are evaluated. (or (and (not (true-listp args)) (msg "The arglist ~x0 is not a true list. See :DOC macro-args." args)) (let ((lambda-keywords (collect-lambda-keywordps args)) (err-string-for-&whole "When the &whole lambda-list keyword is used it must be the first ~ element of the lambda-list and it must be followed by a variable ~ symbol. This is not the case in ~x0. See :DOC macro-args.")) (cond ((or (subsequencep lambda-keywords '(&whole &optional &rest &key &allow-other-keys)) (subsequencep lambda-keywords '(&whole &optional &body &key &allow-other-keys))) (cond (args (cond ((member-eq '&whole (cdr args)) (msg err-string-for-&whole args)) ((and (member-eq '&allow-other-keys args) (not (member-eq '&allow-other-keys (member-eq '&key args)))) ; The Common Lisp Hyperspec does not seem to guarantee the normal expected ; functioning of &allow-other-keys unless it is preceded by &key. We have ; observed in Allegro CL 8.0, for example, that if we define, ; (defmacro foo (x &allow-other-keys) (list 'quote x)), then we get an error ; with (foo x :y 3). (msg "The use of ~x0 is only permitted when preceded by ~ ~x1. The argument list ~x2 is thus illegal." '&allow-other-keys '&key args)) ((eq (car args) '&whole) (cond ((and (consp (cdr args)) (symbolp (cadr args)) (not (lambda-keywordp (cadr args)))) (chk-macro-arglist1 (cddr args))) (t (msg err-string-for-&whole args)))) (t (chk-macro-arglist1 args)))) (t nil))) (t (msg "The lambda-list keywords allowed by ACL2 are &WHOLE, ~ &OPTIONAL, &REST, &BODY, &KEY, and &ALLOW-OTHER-KEYS. These ~ must occur (if at all) in that order, with no duplicate ~ occurrences and at most one of &REST and &BODY. The argument ~ list ~x0 is thus illegal." args)))) (chk-arglist-msg (macro-vars args) chk-state wrld))) (defun chk-macro-arglist (args chk-state ctx state) (let ((msg (chk-macro-arglist-msg args chk-state (w state)))) (cond (msg (er soft ctx "~@0" msg)) (t (value nil))))) (defun defmacro-fn1 (name args doc doc-pair guard body w state) (let ((w (update-doc-database name doc doc-pair (putprop name 'macro-args args (putprop name 'macro-body body ; Below we store the guard. We currently store it in unnormalized form. ; If we ever store it in normalized form -- or in any form other than ; the translated user input -- then reconsider redundant-defmacrop ; below. (putprop-unless name 'guard guard *t* w)))))) (value w))) (defun chk-defmacro-width (rst) (cond ((or (not (true-listp rst)) (not (> (length rst) 2))) (mv "Defmacro requires at least 3 arguments. ~x0 is ~ ill-formed. See :DOC defmacro." (cons 'defmacro rst))) (t (let ((name (car rst)) (args (cadr rst)) (value (car (last rst))) (dcls-and-docs (butlast (cddr rst) 1))) (mv nil (list name args dcls-and-docs value)))))) (defun redundant-defmacrop (name args guard body w) ; We determine whether there is already a defmacro of name with the ; given args, guard, and body. We know that body is a term. Hence, ; it is not nil. Hence, if name is not a macro and there is no ; 'macro-body, the first equal below will fail. (and (getprop name 'absolute-event-number nil 'current-acl2-world w) ; You might think the above test is redundant, given that we look for ; properties like 'macro-body below and find them. But you would be wrong. ; Certain defmacros, in particular, those in *initial-event-defmacros* have ; 'macro-body and other properties but haven't really been defined yet! (equal (getprop name 'macro-body nil 'current-acl2-world w) body) (equal (macro-args name w) args) (equal (guard name nil w) guard))) (defun defmacro-fn (mdef state event-form) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defmacro (car mdef))) (let ((wrld1 (w state)) (event-form (or event-form (cons 'defmacro mdef)))) (mv-let (err-string four) (chk-defmacro-width mdef) (cond (err-string (er soft ctx err-string four)) (t (let ((name (car four)) (args (cadr four)) (dcls (caddr four)) (body (cadddr four))) (er-progn (chk-all-but-new-name name ctx 'macro wrld1 state) ; Important Note: In chk-macro-arglist-msg there is a comment warning us about ; the idea of "translating" the args to a macro to obtain the "internal" form ; of acceptable args. See that comment before implementing any such change. (chk-macro-arglist args nil ctx state) (er-let* ((edcls (collect-declarations dcls (macro-vars args) 'defmacro state ctx))) (let ((doc (if (stringp (car edcls)) (car edcls) nil)) (edcls (if (stringp (car edcls)) (cdr edcls) edcls))) (er-let* ((tguard (translate (conjoin-untranslated-terms (get-guards1 edcls '(guards) wrld1)) '(nil) nil nil ctx wrld1 state))) (mv-let (ctx1 tbody) (translate-cmp body '(nil) nil nil ctx wrld1 (default-state-vars t)) (cond (ctx1 (cond ((null tbody) ; This case would seem to be impossible, since if translate (or translate-cmp) ; causes an error, there is presumably an associated error message. (er soft ctx "An error occurred in attempting to ~ translate the body of the macro. It is ~ very unusual however to see this ~ message; feel free to contact the ACL2 ~ implementors if you are willing to help ~ them debug how this message occurred.")) ((member-eq 'state args) (er soft ctx "~@0~|~%You might find it useful to ~ understand that although you used STATE ~ as a formal parameter, it does not refer ~ to the ACL2 state. It is just a ~ parameter bound to some piece of syntax ~ during macroexpansion. See :DOC ~ defmacro." tbody)) (t (er soft ctx "~@0" tbody)))) ((redundant-defmacrop name args tguard tbody wrld1) (cond ((and (not (f-get-global 'in-local-flg state)) (not (global-val 'boot-strap-flg (w state))) (not (f-get-global 'redundant-with-raw-code-okp state)) (member-eq name (f-get-global 'macros-with-raw-code state))) ; See the comment in chk-acceptable-defuns-redundancy related to this error in ; the defuns case. (er soft ctx "~@0" (redundant-predefined-error-msg name))) (t (stop-redundant-event ctx state)))) (t (enforce-redundancy event-form ctx wrld1 (er-let* ((wrld2 (chk-just-new-name name 'macro nil ctx wrld1 state)) (ignored (value (ignore-vars edcls))) (ignorables (value (ignorable-vars edcls))) (doc-pair (translate-doc name doc ctx state))) (er-progn (chk-xargs-keywords1 edcls '(:guard) ctx state) (chk-free-and-ignored-vars name (macro-vars args) tguard *nil* ; split-types-term *no-measure* ignored ignorables tbody ctx state) (er-let* ((wrld3 (defmacro-fn1 name args doc doc-pair tguard tbody wrld2 state))) (install-event name event-form 'defmacro name nil (cons 'defmacro mdef) nil nil wrld3 state))))))))))))))))))) ; The following functions support boot-strapping. Consider what ; happens when we begin to boot-strap. The first form is read. ; Suppose it is (defconst nil 'nil). It is translated wrt the ; initial world. Unless 'defconst has a macro definition in that ; initial world, we won't get off the ground. The same remark holds ; for the other primitive event functions encountered in axioms.lisp. ; Therefore, before we first call translate we have got to construct a ; world with certain properties already set. ; We compute those properties with the functions below, from the ; following constant. This constant must be the quoted form of the ; event defmacros found in axioms.lisp! It was obtained by ; going to the axioms.lisp buffer, grabbing all of the text in the ; "The *initial-event-defmacros* Discussion", moving it over here, ; embedding it in "(defconst *initial-event-defmacros* '(&))" and ; then deleting the #+acl2-loop-only commands, comments, and documentation ; strings. (defconst *initial-event-defmacros* '((defmacro in-package (str) (list 'in-package-fn (list 'quote str) 'state)) (defmacro defpkg (&whole event-form name form &optional doc book-path) (list 'defpkg-fn (list 'quote name) (list 'quote form) 'state (list 'quote doc) (list 'quote book-path) (list 'quote hidden-p) (list 'quote event-form))) (defmacro defchoose (&whole event-form &rest def) (list 'defchoose-fn (list 'quote def) 'state (list 'quote event-form))) (defmacro defun (&whole event-form &rest def) (list 'defun-fn (list 'quote def) 'state (list 'quote event-form) #+:non-standard-analysis ; std-p nil)) (defmacro defuns (&whole event-form &rest def-lst) (list 'defuns-fn (list 'quote def-lst) 'state (list 'quote event-form) #+:non-standard-analysis ; std-p nil)) (defmacro verify-termination-boot-strap (&whole event-form &rest lst) (list 'verify-termination-boot-strap-fn (list 'quote lst) 'state (list 'quote event-form))) (defmacro verify-guards (&whole event-form name &key hints otf-flg guard-debug doc) (list 'verify-guards-fn (list 'quote name) 'state (list 'quote hints) (list 'quote otf-flg) (list 'quote guard-debug) (list 'quote doc) (list 'quote event-form))) (defmacro defmacro (&whole event-form &rest mdef) (list 'defmacro-fn (list 'quote mdef) 'state (list 'quote event-form))) (defmacro defconst (&whole event-form name form &optional doc) (list 'defconst-fn (list 'quote name) (list 'quote form) 'state (list 'quote doc) (list 'quote event-form))) (defmacro defstobj (&whole event-form name &rest args) (list 'defstobj-fn (list 'quote name) (list 'quote args) 'state (list 'quote event-form))) (defmacro defthm (&whole event-form name term &key (rule-classes '(:REWRITE)) instructions hints otf-flg doc) (list 'defthm-fn (list 'quote name) (list 'quote term) 'state (list 'quote rule-classes) (list 'quote instructions) (list 'quote hints) (list 'quote otf-flg) (list 'quote doc) (list 'quote event-form) #+:non-standard-analysis ; std-p nil)) (defmacro defaxiom (&whole event-form name term &key (rule-classes '(:REWRITE)) doc) (list 'defaxiom-fn (list 'quote name) (list 'quote term) 'state (list 'quote rule-classes) (list 'quote doc) (list 'quote event-form))) (defmacro deflabel (&whole event-form name &key doc) (list 'deflabel-fn (list 'quote name) 'state (list 'quote doc) (list 'quote event-form))) (defmacro defdoc (&whole event-form name doc) (list 'defdoc-fn (list 'quote name) 'state (list 'quote doc) (list 'quote event-form))) (defmacro deftheory (&whole event-form name expr &key doc) (list 'deftheory-fn (list 'quote name) (list 'quote expr) 'state (list 'quote doc) (list 'quote event-form))) (defmacro in-theory (&whole event-form expr &key doc) (list 'in-theory-fn (list 'quote expr) 'state (list 'quote doc) (list 'quote event-form))) (defmacro in-arithmetic-theory (&whole event-form expr &key doc) (list 'in-arithmetic-theory-fn (list 'quote expr) 'state (list 'quote doc) (list 'quote event-form))) (defmacro regenerate-tau-database (&whole event-form &key doc) (list 'regenerate-tau-database-fn 'state (list 'quote doc) (list 'quote event-form))) (defmacro push-untouchable (&whole event-form name fn-p &key doc) (list 'push-untouchable-fn (list 'quote name) (list 'quote fn-p) 'state (list 'quote doc) (list 'quote event-form))) (defmacro reset-prehistory (&whole event-form &optional permanent-p doc) (list 'reset-prehistory-fn (list 'quote permanent-p) 'state (list 'quote doc) (list 'quote event-form))) (defmacro set-body (&whole event-form fn name-or-rune) (list 'set-body-fn (list 'quote fn) (list 'quote name-or-rune) 'state (list 'quote event-form))) (defmacro table (&whole event-form name &rest args) (list 'table-fn (list 'quote name) (list 'quote args) 'state (list 'quote event-form))) (defmacro progn (&rest r) (list 'progn-fn (list 'quote r) 'state)) (defmacro encapsulate (&whole event-form signatures &rest cmd-lst) (list 'encapsulate-fn (list 'quote signatures) (list 'quote cmd-lst) 'state (list 'quote event-form))) (defmacro include-book (&whole event-form user-book-name &key (load-compiled-file ':default) (uncertified-okp 't) (defaxioms-okp 't) (skip-proofs-okp 't) (ttags 'nil) dir doc) (list 'include-book-fn (list 'quote user-book-name) 'state (list 'quote load-compiled-file) (list 'quote :none) (list 'quote uncertified-okp) (list 'quote defaxioms-okp) (list 'quote skip-proofs-okp) (list 'quote ttags) (list 'quote doc) (list 'quote dir) (list 'quote event-form))) (defmacro local (x) (list 'if '(equal (ld-skip-proofsp state) 'include-book) '(mv nil nil state) (list 'if '(equal (ld-skip-proofsp state) 'initialize-acl2) '(mv nil nil state) (list 'state-global-let* '((in-local-flg t)) (list 'when-logic "LOCAL" x))))) (defmacro defattach (&whole event-form &rest args) (list 'defattach-fn (list 'quote args) 'state (list 'quote event-form))) )) ; Because of the Important Boot-Strapping Invariant noted in axioms.lisp, ; we can compute from this list the following things for each event: ; the macro name ; the macro args ; the macro body ; the -fn name corresponding to the macro ; the formals of the -fn ; The macro name and args are easy. The macro body must be obtained ; from the list above by translating the given bodies, but we can't use ; translate yet because the world is empty and so, for example, 'list ; is not defined as a macro in it. So we use the following boot-strap ; version of translate that is capable (just) of mapping the bodies above ; into their translations under a properly initialized world. (defun boot-translate (x) (cond ((atom x) (cond ((eq x nil) *nil*) ((eq x t) *t*) ((keywordp x) (kwote x)) ((symbolp x) x) (t (kwote x)))) ((eq (car x) 'quote) x) ((eq (car x) 'if) (list 'if (boot-translate (cadr x)) (boot-translate (caddr x)) (boot-translate (cadddr x)))) ((eq (car x) 'equal) (list 'equal (boot-translate (cadr x)) (boot-translate (caddr x)))) ((eq (car x) 'ld-skip-proofsp) (list 'ld-skip-proofsp (boot-translate (cadr x)))) ((or (eq (car x) 'list) (eq (car x) 'mv)) (cond ((null (cdr x)) *nil*) (t (list 'cons (boot-translate (cadr x)) (boot-translate (cons 'list (cddr x))))))) ((eq (car x) 'when-logic) (list 'if '(eq (default-defun-mode-from-state state) ':program) (list 'skip-when-logic (list 'quote (cadr x)) 'state) (boot-translate (caddr x)))) (t (er hard 'boot-translate "Boot-translate was called on ~x0, which is ~ unrecognized. If you want to use such a form in one ~ of the *initial-event-defmacros* then you must modify ~ boot-translate so that it can translate the form." x)))) ; The -fn name corresponding to the macro is easy. Finally to get the ; formals of the -fn we have to walk through the actuals of the call of ; the -fn in the macro body and unquote all the names but 'STATE. That ; is done by: (defun primordial-event-macro-and-fn1 (actuals) (cond ((null actuals) nil) ((equal (car actuals) '(quote state)) (cons 'state (primordial-event-macro-and-fn1 (cdr actuals)))) #+:non-standard-analysis ((or (equal (car actuals) nil) (equal (car actuals) t)) ; Since nil and t are not valid names for formals, we need to transform (car ; actuals) to something else. Up until the non-standard extension this never ; happened. We henceforth assume that values of nil and t correspond to the ; formal std-p. (cons 'std-p (primordial-event-macro-and-fn1 (cdr actuals)))) ((and (consp (car actuals)) (eq (car (car actuals)) 'list) (equal (cadr (car actuals)) '(quote quote))) (cons (caddr (car actuals)) (primordial-event-macro-and-fn1 (cdr actuals)))) (t (er hard 'primordial-event-macro-and-fn1 "We encountered an unrecognized form of actual, ~x0, ~ in trying to extract the formals from the actuals in ~ some member of *initial-event-defmacros*. If you ~ want to use such a form in one of the initial event ~ defmacros, you must modify ~ primordial-event-macro-and-fn1 so that it can recover ~ the corresponding formal name from the actual form." (car actuals))))) (defun primordial-event-macro-and-fn (form wrld) ; Given a member of *initial-event-defmacros* above, form, we check that ; it is of the desired shape, extract the fields we need as described, ; and putprop them into wrld. (case-match form (('defmacro 'local macro-args macro-body) (putprop 'local 'macro-args macro-args (putprop 'local 'macro-body (boot-translate macro-body) (putprop 'ld-skip-proofsp 'symbol-class :common-lisp-compliant (putprop 'ld-skip-proofsp 'formals '(state) (putprop 'ld-skip-proofsp 'stobjs-in '(state) (putprop 'ld-skip-proofsp 'stobjs-out '(nil) ; See the fakery comment below for an explanation of this infinite ; recursion! This specious body is only in effect during the ; processing of the first part of axioms.lisp during boot-strap. It ; is overwritten by the accepted defun of ld-skip-proofsp. Similarly ; for default-defun-mode-from-state and skip-when-logic. (putprop 'ld-skip-proofsp 'def-bodies (list (make def-body :formals '(state) :hyp nil :concl '(ld-skip-proofsp state) :rune *fake-rune-for-anonymous-enabled-rule* :nume 0 ; fake :recursivep nil :controller-alist nil)) (putprop 'default-defun-mode-from-state 'symbol-class :common-lisp-compliant (putprop 'default-defun-mode-from-state 'formals '(state) (putprop 'default-defun-mode-from-state 'stobjs-in '(state) (putprop 'default-defun-mode-from-state 'stobjs-out '(nil) (putprop 'default-defun-mode-from-state 'def-bodies (list (make def-body :formals '(str state) :hyp nil :concl '(default-defun-mode-from-state state) :rune *fake-rune-for-anonymous-enabled-rule* :nume 0 ; fake :recursivep nil :controller-alist nil)) (putprop 'skip-when-logic 'symbol-class :common-lisp-compliant (putprop 'skip-when-logic 'formals '(str state) (putprop 'skip-when-logic 'stobjs-in '(nil state) (putprop 'skip-when-logic 'stobjs-out *error-triple-sig* (putprop 'skip-when-logic 'def-bodies (list (make def-body :formals '(str state) :hyp nil :concl '(skip-when-logic str state) :rune *fake-rune-for-anonymous-enabled-rule* :nume 0 ; fake :recursivep nil :controller-alist nil)) wrld)))))))))))))))))) (('defmacro name macro-args ('list ('quote name-fn) . actuals)) (let* ((formals (primordial-event-macro-and-fn1 actuals)) (stobjs-in (compute-stobj-flags formals t wrld)) ; known-stobjs = t but, in this case it could just as well be ; known-stobjs = '(state) because we are constructing the primordial world ; and state is the only stobj. (macro-body (boot-translate (list* 'list (kwote name-fn) actuals)))) ; We could do a (putprop-unless name 'guard *t* *t* &) and a ; (putprop-unless name-fn 'guard *t* *t* &) here, but it would be silly. (putprop name 'macro-args macro-args (putprop name 'macro-body macro-body (putprop name-fn 'symbol-class :common-lisp-compliant (putprop name-fn 'formals formals (putprop name-fn 'stobjs-in stobjs-in (putprop name-fn 'stobjs-out *error-triple-sig* ; The above may make sense, but the following act of fakery deserves ; some comment. In order to get, e.g. defconst-fn, to work before ; it is defined in a boot-strap, we give it a body, which makes ; ev-fncall think it is ok to take a short cut and use the Common Lisp ; definition. Of course, we are asking for trouble by laying down ; this recursive call! But it never happens. (putprop name-fn 'def-bodies (list (make def-body :formals formals :hyp nil :concl (cons name-fn formals) :rune *fake-rune-for-anonymous-enabled-rule* :nume 0 ; fake :recursivep nil :controller-alist nil)) wrld))))))))) (& (er hard 'primordial-event-macro-and-fn "The supplied form ~x0 was not of the required ~ shape. Every element of ~ *initial-event-defmacros* must be of the form ~ expected by this function. Either change the ~ event defmacro or modify this function." form)))) (defun primordial-event-macros-and-fns (lst wrld) ; This function is given *initial-event-defmacros* and just sweeps down it, ; putting the properties for each event macro and its corresponding -fn. (cond ((null lst) wrld) (t (primordial-event-macros-and-fns (cdr lst) (primordial-event-macro-and-fn (car lst) wrld))))) ; We need to declare the 'type-prescriptions for those fns that are ; referenced before they are defined in the boot-strapping process. ; Actually, apply is such a function, but it has an unrestricted type ; so we leave its 'type-prescriptions nil. (defconst *initial-type-prescriptions* (list (list 'o-p (make type-prescription :rune *fake-rune-for-anonymous-enabled-rule* :nume nil :term '(o-p x) :hyps nil :backchain-limit-lst nil :basic-ts *ts-boolean* :vars nil :corollary '(booleanp (o-p x)))) (list 'o< (make type-prescription :rune *fake-rune-for-anonymous-enabled-rule* :nume nil :term '(o< x y) :hyps nil :backchain-limit-lst nil :basic-ts *ts-boolean* :vars nil :corollary '(booleanp (o< x y)))))) (defun collect-world-globals (wrld ans) (cond ((null wrld) ans) ((eq (cadar wrld) 'global-value) (collect-world-globals (cdr wrld) (add-to-set-eq (caar wrld) ans))) (t (collect-world-globals (cdr wrld) ans)))) (defun primordial-world-globals (operating-system) ; This function is the standard place to initialize a world global. ; Among the effects of this function is to set the global variable ; 'world-globals to the list of all variables initialized. Thus, ; it is very helpful to follow the discipline of initializing all ; globals here, whether their initial values are important or not. ; Historical Note: Once upon a time, before we kept a stack of ; properties on the property lists representing installed worlds, it ; was necessary, when retracting from a world, to scan the newly ; exposed world to find the new current value of any property removed. ; This included the values of world globals and it often sent us all ; the way back to the beginning of the primordial world. We then ; patched things up by using this collection of names at the end of ; system initialization to "float" to the then-top of the world the ; values of all world globals. That was the true motivation of ; collecting the initialization of all globals into one function: so ; we could get 'world-globals so we knew who to float. (let ((wrld (global-set-lst (list* (list 'event-landmark (make-event-tuple -1 0 nil nil 0 nil)) (list 'command-landmark (make-command-tuple -1 :logic nil nil nil)) (list 'known-package-alist *initial-known-package-alist*) (list 'well-founded-relation-alist (list (cons 'o< (cons 'o-p *fake-rune-for-anonymous-enabled-rule*)))) (list 'recognizer-alist *initial-recognizer-alist*) (list 'built-in-clauses (classify-and-store-built-in-clause-rules *initial-built-in-clauses* nil ; The value of wrld supplied below, nil, just means that all function symbols ; of initial-built-in-clauses will seem to have level-no 0. nil)) (list 'half-length-built-in-clauses (floor (length *initial-built-in-clauses*) 2)) (list 'type-set-inverter-rules *initial-type-set-inverter-rules*) (list 'global-arithmetic-enabled-structure (initial-global-enabled-structure "ARITHMETIC-ENABLED-ARRAY-")) (let ((globals '((event-index nil) (command-index nil) (event-number-baseline 0) (embedded-event-lst nil) (cltl-command nil) (top-level-cltl-command-stack nil) (hons-enabled ; Why are we comfortable making hons-enabled a world global? Note that even if ; if hons-enabled were a state global, the world would be sensitive to whether ; or not we are in the hons version: for example, we get different evaluation ; results for the following. ; (getprop 'memoize-table 'table-guard *t* 'current-acl2-world (w state)) ; By making hons-enabled a world global, we can access its value without state ; in history query functions such as :pe. #+hons t #-hons nil) (include-book-alist nil) (include-book-alist-all nil) (pcert-books nil) (include-book-path nil) (certification-tuple nil) (documentation-alist nil) (proved-functional-instances-alist nil) (nonconstructive-axiom-names nil) (standard-theories (nil nil nil nil)) (current-theory nil) (current-theory-augmented nil) (current-theory-index -1) (generalize-rules nil) ; Make sure the following tau globals are initialized this same way ; by initialize-tau-globals: (tau-runes nil) (tau-next-index 0) (tau-conjunctive-rules nil) (tau-mv-nth-synonyms nil) (tau-lost-runes nil) (clause-processor-rules nil) (boot-strap-flg t) (boot-strap-pass-2 nil) (skip-proofs-seen nil) (redef-seen nil) (free-var-runes-all nil) (free-var-runes-once nil) (chk-new-name-lst (if iff implies not in-package defpkg defun defuns mutual-recursion defmacro defconst defstobj defthm defaxiom progn encapsulate include-book deflabel defdoc deftheory in-theory in-arithmetic-theory regenerate-tau-database push-untouchable remove-untouchable set-body table reset-prehistory verify-guards verify-termination-boot-strap local defchoose ld-skip-proofsp in-package-fn defpkg-fn defun-fn defuns-fn mutual-recursion-fn defmacro-fn defconst-fn defstobj-fn defthm-fn defaxiom-fn progn-fn encapsulate-fn include-book-fn deflabel-fn defdoc-fn deftheory-fn in-theory-fn in-arithmetic-theory-fn regenerate-tau-database-fn push-untouchable-fn remove-untouchable-fn reset-prehistory-fn set-body-fn table-fn verify-guards-fn verify-termination-boot-strap-fn defchoose-fn apply o-p o< defattach defattach-fn default-defun-mode-from-state skip-when-logic ; The following names are here simply so we can deflabel them for ; documentation purposes: state declare apropos finding-documentation enter-boot-strap-mode exit-boot-strap-mode lp acl2-defaults-table let let* complex complex-rationalp )) (ttags-seen nil) (untouchable-fns nil) (untouchable-vars nil) (defined-hereditarily-constrained-fns nil) (attachment-records nil) (proof-supporters-alist nil)))) (list* `(operating-system ,operating-system) `(command-number-baseline-info ,(make command-number-baseline-info :current 0 :permanent-p t :original 0)) globals))) nil))) (global-set 'world-globals (collect-world-globals wrld '(world-globals)) wrld))) (defun arglists-to-nils (arglists) (declare (xargs :guard (true-list-listp arglists))) (cond ((endp arglists) nil) (t (cons (make-list (length (car arglists))) (arglists-to-nils (cdr arglists)))))) (defconst *unattachable-primitives* ; This constant contains the names of function symbols for which we must ; disallow attachments for execution. Our approach is to disallow all ; attachments to these functions, all of which are constrained since defined ; functions cannot receive attachments for execution. So we search the code ; for encapsulated functions that we do not want executed. '(big-n decrement-big-n zp-big-n ; At one time we also included canonical-pathname and various mfc-xx functions. ; But these are all handled now by dependent clause-processors, which gives ; them unknown-constraints and hence defeats attachability. )) ;; RAG - I added the treatment of *non-standard-primitives* (defun primordial-world (operating-system) (let ((names (strip-cars *primitive-formals-and-guards*)) (arglists (strip-cadrs *primitive-formals-and-guards*)) (guards (strip-caddrs *primitive-formals-and-guards*)) (ns-names #+:non-standard-analysis *non-standard-primitives* #-:non-standard-analysis nil)) (add-command-landmark :logic (list 'enter-boot-strap-mode operating-system) nil ; cbd is only needed for user-generated commands nil (add-event-landmark (list 'enter-boot-strap-mode operating-system) 'enter-boot-strap-mode (append (strip-cars *primitive-formals-and-guards*) (strip-non-hidden-package-names *initial-known-package-alist*)) (initialize-tau-preds *primitive-monadic-booleans* (putprop 'equal 'coarsenings '(equal) (putprop-x-lst1 names 'absolute-event-number 0 (putprop-x-lst1 names 'predefined t (putprop-defun-runic-mapping-pairs names nil (putprop-x-lst1 ns-names ; nil in the #-:non-standard-analysis case 'classicalp nil (putprop-x-lst1 ns-names 'constrainedp t (putprop-x-lst1 names 'symbol-class :common-lisp-compliant (putprop-x-lst2-unless names 'guard guards *t* (putprop-x-lst2 names 'formals arglists (putprop-x-lst2 (strip-cars *initial-type-prescriptions*) 'type-prescriptions (strip-cdrs *initial-type-prescriptions*) (putprop-x-lst1 names 'coarsenings nil (putprop-x-lst1 names 'congruences nil (putprop-x-lst2 names 'stobjs-in (arglists-to-nils arglists) (putprop-x-lst1 names 'stobjs-out '(nil) (primordial-event-macros-and-fns *initial-event-defmacros* ; This putprop must be here, into the world seen by ; primordial-event-macros-and-fns! (putprop 'state 'stobj '(*the-live-state*) (primordial-world-globals operating-system)))))))))))))))))) t)))) (defun same-name-twice (l) (cond ((null l) nil) ((null (cdr l)) nil) ((equal (symbol-name (car l)) (symbol-name (cadr l))) (list (car l) (cadr l))) (t (same-name-twice (cdr l))))) (defun conflicting-imports (l) ; We assume that l is sorted so that if any two elements have the same ; symbol-name, then two such are adjacent. (same-name-twice l)) (defun chk-new-stringp-name (ev-type name ctx w state) (cond ((not (stringp name)) (er soft ctx "The first argument to ~s0 must be a string. You provided ~ the object ~x1. See :DOC ~s." (cond ((eq ev-type 'defpkg) "defpkg") (t "include-book")) name)) (t (let ((entry (find-package-entry name (global-val 'known-package-alist w)))) (cond ((and entry (not (and (eq ev-type 'defpkg) (package-entry-hidden-p entry)))) (er soft ctx "The name ~x0 is in use as a package name. We do not permit ~ package names~s1 to participate in redefinition. If you must ~ redefine this name, use :ubt to undo the existing definition." name (if (package-entry-hidden-p entry) " (even those that are hidden; see :DOC hidden-death-package" ""))) ((assoc-equal name (global-val 'include-book-alist w)) ; Name is thus a full-book-name. (cond ((eq ev-type 'include-book) (value name)) (t (er soft ctx "The name ~x0 is in use as a book name. You are trying to ~ redefine it as a package. We do not permit package names ~ to participate in redefinition. If you must redefine this ~ name, use :ubt to undo the existing definition." name)))) (t (value nil))))))) (deflabel package-reincarnation-import-restrictions :doc ":Doc-Section Miscellaneous re-defining undone ~ilc[defpkg]s~/ Suppose ~c[(defpkg \"pkg\" imports)] is the most recently executed successful definition of ~c[\"pkg\"] in this ACL2 session and that it has since been undone, as by ~c[:]~ilc[ubt]. Any future attempt in this session to define ~c[\"pkg\"] as a package must specify an identical imports list.~/ The restriction stems from the need to implement the reinstallation of saved logical ~il[world]s as in error recovery and the ~c[:]~ilc[oops] ~il[command]. Suppose that the new ~ilc[defpkg] attempts to import some symbol, ~c[a::sym], not imported by the previous definition of ~c[\"pkg\"]. Because it was not imported in the original package, the symbol ~c[pkg::sym], different from ~c[a::sym], may well have been created and may well be used in some saved ~il[world]s. Those saved ~il[world]s are Common Lisp objects being held for you ``behind the scenes.'' In order to import ~c[a::sym] into ~c[\"pkg\"] now we would have to unintern ~c[pkg::sym], rendering those saved ~il[world]s ill-formed. It is because of saved ~il[world]s that we do not actually clear out a package when it is undone. At one point we thought it was sound to allow the new ~ilc[defpkg] to import a subset of the old. But that is incorrect. Suppose the old definition of ~c[\"pkg\"] imported ~c[a::sym] but the new one does not. Suppose we allowed that and implemented it simply by setting the imports of ~c[\"pkg\"] to the new subset. Then consider the conjecture ~c[(eq a::sym pkg::sym)]. This ought not be a theorem because we did not import ~c[a::sym] into ~c[\"pkg\"]. But in fact in AKCL it is a theorem because ~c[pkg::sym] is read as ~c[a::sym] because of the old imports." ; Once upon a time the documentation included the following additional text. ; We deleted it on the grounds that we shouldn't tell the user how to go behind ; our backs. But we might want to recall this hack in the future for our own ; use. ; If you really must change the imports list of a previously defined ; but now undone package, we recommend that you either invent a new ; package name for subsequent use in this session or that you save ; your state and reconstruct it in a new ACL2 session. If you wish to ; try behind the scenes surgery to allow the new ~ilc[defpkg] to succeed ~-[] ; at the expense of ACL2's soundness in the rest of the session ~-[] ; exit ~ilc[lp] and type the following to raw Lisp: ; ~bv[] ; (let ((name \"pkg\") ; fill in pkg name ; (new-imports '(...)) ; fill in new imports ; (p (find-package name))) ; (do-symbols (sym p) (unintern sym p)) ; (import new-imports p) ; (setq *ever-known-package-alist* ; (cons (make-package-entry :name name :imports new-imports) ; (remove-package-entry name *ever-known-package-alist*))) ; name) ; ~ev[] ; This will render ill-formed any saved ~il[world]s involving symbols in ; ~c[\"pkg\"] and it may be impossible to recover from certain errors. In ; addition, because ACL2 is probably unsound after this hack we ; recommend that you treat the rest of the session as merely ; exploratory. ) (defun chk-package-reincarnation-import-restrictions (name proposed-imports) ; Logically, this function always returns t, but it may cause a hard ; error because we cannot create a package with the given name and imports. ; See :DOC package-reincarnation-import-restrictions. #+acl2-loop-only (declare (ignore name proposed-imports)) #-acl2-loop-only (chk-package-reincarnation-import-restrictions2 name proposed-imports) t) (defun convert-book-name-to-cert-name (x cert-op) ; X is assumed to satisfy chk-book-name. We generate the corresponding ; certification file name. ; The cddddr below chops off the "lisp" from the end of the filename but leaves ; the dot. (coerce (append (reverse (cddddr (reverse (coerce x 'list)))) (case cert-op ((t) '(#\c #\e #\r #\t)) ((:create-pcert :create+convert-pcert) '(#\p #\c #\e #\r #\t #\0)) (:convert-pcert '(#\p #\c #\e #\r #\t #\1)) (otherwise ; including :write-acl2x (er hard 'convert-book-name-to-cert-name "Bad value of cert-op for ~ convert-book-name-to-cert-name: ~x0" cert-op)))) 'string)) (defun unrelativize-book-path (lst dir) (cond ((endp lst) nil) ((consp (car lst)) (assert$ (eq (caar lst) :system) ; see relativize-book-path (cons (concatenate 'string dir (cdar lst)) (unrelativize-book-path (cdr lst) dir)))) (t (cons (car lst) (unrelativize-book-path (cdr lst) dir))))) (defun tilde-@-defpkg-error-phrase (name package-entry new-not-old old-not-new book-path defpkg-book-path w distrib-books-dir) (let ((book-path (unrelativize-book-path book-path distrib-books-dir)) (defpkg-book-path (unrelativize-book-path defpkg-book-path distrib-books-dir))) (list "The proposed defpkg conflicts with a previously-executed defpkg for ~ name ~x0~@1. ~#a~[For example, symbol ~s2::~s3 is in the list of ~ imported symbols for the ~s4 definition but not for the other.~/The two ~ have the same lists of imported symbols, but not in the same order.~] ~ The previous defpkg is ~#5~[at the top level.~/in the certificate file ~ for the book ~x7, which is included at the top level.~/in the ~ certificate file for the book ~x7, which is included via the following ~ path, from top-most book down to the above file.~| ~F8~]~@9~@b" (cons #\0 name) (cons #\1 (if (package-entry-hidden-p package-entry) " that no longer exists in the current ACL2 logical world ~ (see :DOC hidden-death-package)" "")) (cons #\a (if (or new-not-old old-not-new) 0 1)) (cons #\2 (symbol-package-name (if new-not-old (car new-not-old) (car old-not-new)))) (cons #\3 (symbol-name (if new-not-old (car new-not-old) (car old-not-new)))) (cons #\4 (if new-not-old "current" "previous")) (cons #\5 (zero-one-or-more book-path)) (cons #\7 (car book-path)) (cons #\8 (reverse book-path)) (cons #\9 (if defpkg-book-path "~|This previous defpkg event appears to have been created ~ because of a defpkg that was hidden by a local include-book; ~ see :DOC hidden-death-package." "")) (cons #\b (let ((include-book-path (global-val 'include-book-path w))) (if (or include-book-path defpkg-book-path) (msg "~|The new proposed defpkg event may be found by ~ following the sequence of include-books below, ~ from top-most book down to the book whose ~ portcullis contains the new proposed defpkg ~ event.~| ~F0" (reverse (append defpkg-book-path include-book-path))) "")))))) (defconst *1*-pkg-prefix* ; Unfortunately, *1*-package-prefix* is defined in raw Lisp only, early in the ; boot-strap. We mirror that constant here for use below. (let ((result "ACL2_*1*_")) #-acl2-loop-only (or (equal result *1*-package-prefix*) (er hard '*1*-pkg-prefix* "Implementation error: Failed to keep *1*-package-prefix* and ~ *1*-pkg-prefix* in sync.")) result)) (defun chk-acceptable-defpkg (name form defpkg-book-path hidden-p ctx w state) ; Warning: Keep this in sync with the redefinition of this function in ; community book books/misc/redef-pkg.lisp. ; We return an error triple. The non-error value is either 'redundant or a ; triple (tform value . package-entry), where tform and value are a translated ; form and its value, and either package-entry is nil in the case that no ; package with name name has been seen, or else is an existing entry for name ; in known-package-alist with field hidden-p=t (see the Essay on Hidden ; Packages). (let ((package-entry (and (not (global-val 'boot-strap-flg w)) (find-package-entry name (global-val 'known-package-alist w))))) (cond ((and package-entry (or hidden-p (not (package-entry-hidden-p package-entry))) (equal (caddr (package-entry-defpkg-event-form package-entry)) form)) (value 'redundant)) (t (er-progn (cond ((or package-entry (eq (ld-skip-proofsp state) 'include-book)) (value nil)) ((not (stringp name)) (er soft ctx "Package names must be string constants and ~x0 is not. See ~ :DOC defpkg." name)) ((equal name "") ; In Allegro CL, "" is prohibited because it is already a nickname for the ; KEYWORD package. But in (non-ANSI, at least) GCL we could prove nil up ; through v2-7 by certifying the following book with the indicated portcullis: ; (in-package "ACL2") ; ; Portcullis: ; (defpkg "" nil) ; ; (defthm bug ; nil ; :hints (("Goal" :use ((:instance intern-in-package-of-symbol-symbol-name ; (x '::abc) (y 17))))) ; :rule-classes nil) (er soft ctx "The empty string is not a legal package name for defpkg." name)) ((not (standard-char-listp (coerce name 'list))) (er soft ctx "~x0 is not a legal package name for defpkg, which requires the ~ name to contain only standard characters." name)) ((not (equal (string-upcase name) name)) (er soft ctx "~x0 is not a legal package name for defpkg, which disallows ~ lower case characters in the name." name)) ((equal name "LISP") (er soft ctx "~x0 is disallowed as a a package name for defpkg, because this ~ package name is used under the hood in some Common Lisp ~ implementations." name)) ((let ((len (length *1*-pkg-prefix*))) (and (<= len (length name)) (string-equal (subseq name 0 len) *1*-pkg-prefix*))) ; The use of string-equal could be considered overkill; probably equal provides ; enough of a check. But we prefer not to consider the possibility that some ; Lisp has case-insensitive package names. Probably we should similarly use ; member-string-equal instead of member-equal below. (er soft ctx "It is illegal for a package name to start (even ignoring case) ~ with the string \"~@0\". ACL2 makes internal use of package ~ names starting with that string." *1*-pkg-prefix*)) ((not (true-listp defpkg-book-path)) (er soft ctx "The book-path argument to defpkg, if supplied, must be a ~ true-listp. It is not recommended to supply this argument, ~ since the system makes use of it for producing useful error ~ messages. The defpkg of ~x0 is thus illegal." name)) (t (value nil))) ; At one time we checked that if the package exists, i.e. (member-equal name ; all-names), and we are not in the boot-strap, then name must previously have ; been introduced by defpkg. But name may have been introduced by ; maybe-introduce-empty-pkg, or even by a defpkg form evaluated in raw Lisp ; when loading a compiled file before processing events on behalf of an ; include-book. So we leave it to defpkg-raw1 to check that a proposed package ; is either new, is among *defpkg-virgins*, or is consistent with an existing ; entry in *ever-known-package-alist*. (state-global-let* ((safe-mode ; Warning: If you are tempted to bind safe-mode to nil outside the boot-strap, ; then revisit the binding of *safe-mode-verified-p* to t in the ; #-acl2-loop-only definition of defpkg-raw. See the defparameter for ; *safe-mode-verified-p*. ; In order to build a profiling image for GCL, we have observed a need to avoid ; going into safe-mode when building the system. (not (global-val 'boot-strap-flg w)))) (er-let* ((pair (simple-translate-and-eval form nil nil "The second argument to defpkg" ctx w state nil))) (let ((tform (car pair)) (imports (cdr pair))) (cond ((not (symbol-listp imports)) (er soft ctx "The second argument of defpkg must eval to a list of ~ symbols. See :DOC defpkg.")) (t (let* ((imports (sort-symbol-listp imports)) (conflict (conflicting-imports imports)) (base-symbol (packn (cons name '("-PACKAGE"))))) ; Base-symbol is the the base symbol of the rune for the rule added by ; defpkg describing the properties of symbol-package-name on interns ; with the new package. (cond ((member-symbol-name *pkg-witness-name* imports) (er soft ctx "It is illegal to import symbol ~x0 because its name ~ has been reserved for a symbol in the package being ~ defined." (car (member-symbol-name *pkg-witness-name* imports)))) (conflict (er soft ctx "The value of the second (imports) argument of defpkg ~ may not contain two symbols with the same symbol ~ name, e.g. ~&0. See :DOC defpkg." conflict)) (t (cond ((and package-entry (not (equal imports (package-entry-imports package-entry)))) (er soft ctx "~@0" (tilde-@-defpkg-error-phrase name package-entry (set-difference-eq imports (package-entry-imports package-entry)) (set-difference-eq (package-entry-imports package-entry) imports) (package-entry-book-path package-entry) defpkg-book-path w (f-get-global 'system-books-dir state)))) ((and package-entry (or hidden-p (not (package-entry-hidden-p package-entry)))) (prog2$ (chk-package-reincarnation-import-restrictions name imports) (value 'redundant))) (t (er-progn (chk-new-stringp-name 'defpkg name ctx w state) (chk-all-but-new-name base-symbol ctx nil w state) ; Note: Chk-just-new-name below returns a world which we ignore because ; we know redefinition of 'package base-symbols is disallowed, so the ; world returned is w when an error isn't caused. ; Warning: In maybe-push-undo-stack and maybe-pop-undo-stack we rely ; on the fact that the symbol name-PACKAGE is new! (chk-just-new-name base-symbol 'theorem nil ctx w state) (prog2$ (chk-package-reincarnation-import-restrictions name imports) (value (list* tform imports package-entry ; hidden-p is true ))))))))))))))))))) (defun defpkg-fn (name form state doc book-path hidden-p event-form) ; Important Note: Don't change the formals of this function without ; reading the *initial-event-defmacros* discussion in axioms.lisp. ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ; Like defconst, defpkg evals its second argument. ; We forbid interning into a package before its imports are set once and for ; all. In the case of the main Lisp package, we assume that we have no control ; over it and simply refuse requests to intern into it. (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defpkg name)) (let ((w (w state)) (event-form (or event-form (list* 'defpkg name form (if (or doc book-path) (list doc) nil) (if book-path (list book-path) nil))))) (er-let* ((doc-pair (translate-doc name doc ctx state)) (tform-imports-entry (chk-acceptable-defpkg name form book-path hidden-p ctx w state))) (cond ((eq tform-imports-entry 'redundant) (stop-redundant-event ctx state)) (t (let* ((imports (cadr tform-imports-entry)) (w1 (global-set 'known-package-alist (cons (make-package-entry :name name :imports imports :hidden-p hidden-p :book-path (append book-path (global-val 'include-book-path w)) :defpkg-event-form event-form :tterm (car tform-imports-entry)) (if (cddr tform-imports-entry) (remove-package-entry name (known-package-alist state)) (global-val 'known-package-alist w))) w)) ; Defpkg adds an axiom, labelled ax below. We make a :REWRITE rule out of ax. ; Warning: If the axiom added by defpkg changes, be sure to consider the ; initial packages that are not defined with defpkg, e.g., "ACL2". In ; particular, for each primitive package in *initial-known-package-alist* there ; is a defaxiom in axioms.lisp exactly analogous to the add-rule below. So if ; you change this code, change that code. (w2 (cond (hidden-p w1) (t (let ((ax `(equal (pkg-imports (quote ,name)) (quote ,imports)))) (add-rules (packn (cons name '("-PACKAGE"))) `((:REWRITE :COROLLARY ,ax)) ax ax (ens state) w1 state))))) (w3 (cond (hidden-p w2) ; may as well skip :doc on hidden pkg (t (update-doc-database name doc doc-pair w2))))) (install-event name event-form 'defpkg name nil (list 'defpkg name form) :protect ctx w3 state)))))))) ; We now start the development of deftheory and theory expressions. ; First, please read the Essay on Enabling, Enabled Structures, and ; Theories for a refresher course on such things as runes, common ; theories, and runic theories. Roughly speaking, theory expressions ; are terms that produce common theories as their results. Recall ; that a common theory is a truelist of rule name designators. A rule ; name designator is an object standing for a set of runes; examples ; include APP, which might stand for {(:DEFINITION app)}, (APP), which ; might stand for {(:EXECUTABLE-COUNTERPART app)}, and LEMMA1, which ; might stand for the set of runes {(REWRITE lemma1 . 1) (REWRITE ; lemma1 . 2) (ELIM lemma1)}. Of course, a rune is a rule name designator ; and stands for the obvious: the singleton set containing that rune. ; To every common theory there corresponds a runic theory, obtained ; from the common theory by unioning together the designated sets of ; runes and then ordering the result by nume. Runic theories are ; easier to manipulate (e.g., union together) because they are ; ordered. ; To define deftheory we need not define any any "theory manipulation ; functions" (e.g., union-theories, or universal-theory) because ; deftheory just does a full-blown eval of whatever expression the ; user provides. We could therefore define deftheory now. But there ; are a lot of useful theory manipulation functions and they are ; generally used only in deftheory and in-theory, so we define them ; now. ; Calls of these functions will be typed by the user in theory ; expressions. Those expressions will be executed to obtain new ; theories. Furthermore, the user may well define his own theory ; producing functions which will be mixed in with ours in his ; expressions. How do we know a "theory expression" will produce a ; theory? We don't. We just evaluate it and check the result. But ; this raises a more serious question: how do we know our theory ; manipulation functions are given theories as their arguments? ; Indeed, they may not be given theories because of misspellings, bugs ; in the user's functions, etc. Because of the presence of ; user-defined functions in theory expressions we can't syntactically ; check that an expression is ok. And at the moment we don't see that ; it is worth the trouble of making the user prove "theory theorems" ; such as (THEORYP A W) -> (THEORYP (MY-FN A) W) that would let us so ; analyze his expressions. ; So we have decided to put run-time checks into our theory functions. ; We have two methods available to us: we could put guards on them or ; we could put checks into them. The latter course does not permit us ; to abort on undesired arguments -- because we don't want theory ; functions to take STATE and be multi-valued. Thus, once past the ; guards all we can do is coerce unwanted args into acceptable ones. ; There are several sources of tension. It was such tensions that ; led to the idea of "common" v. "runic" theories and, one level deeper, ; "rule name designators" v. runes. ; (1) When our theory functions are getting input directly from the ; user we wish they did a throrough job of checking it and were ; forgiving about such things as order, e.g., sorted otherwise ok ; lists, so that the user didn't need to worry about order. ; (2) When our theory functions are getting input produced by one of ; our functions, we wish they didn't check anything so they could ; just fly. ; (3) These functions have to be admissible under the definitional principle ; and not cause errors when called on the utter garbage that the user ; might type. ; (4) Checking the well-formedness of a theory value requires access to ; wrld. ; We have therefore chosen the following strategy. ; First, all theory manipulation functions take wrld as an argument. ; Some need it, e.g., the function that returns all the available rule ; names. Others wouldn't need it if we made certain choices on the ; handling of run-time checks. We've chosen to be uniform: all have ; it. This uniformity saves the user from having to remember which ; functions do and which don't. ; Second, all theory functions have guards that check that their ; "theory" arguments "common theories." This means that if a theory ; function is called on utter garbage the user will get an error ; message. But it means we'll pay the price of scanning each theory ; value on each function entry in his expression to check ; rule-name-designatorp. ; To compute on theories we will convert common theories to runic ones ; (actually, all the way to augmented runic theories) and we will ; always return runic theories because they can be verified faster. ; This causes a second scan every time but in general will not go into ; sorting because our intermediate results will always be ordered. ; This gives us "user-friendliness" for top-level calls of the theory ; functions without (too much?) overhead. ; Now we define union, intersect, and set-difference for lists of rule ; names. (defun theory-fn-callp (x) ; We return t or nil. If t, and the evaluation of x does not cause an error, ; then the result is a runic-theoryp. Here x is an untranslated term; see also ; theory-fn-translated-callp for translated terms x. It would be sound to ; return non-nil here if theory-fn-translated-callp returns non-nil, but that ; doesn't seem useful for user-level terms (though we may want to reconsider). (and (consp x) (member-eq (car x) '(current-theory disable e/d enable executable-counterpart-theory function-theory intersection-theories set-difference-theories theory union-theories universal-theory)) t)) (defun intersection-augmented-theories-fn1 (lst1 lst2 ans) ; Let lst1 and lst2 be augmented theories: descendingly ordered lists ; of pairs mapping numes to runes. We return the intersection of the ; two theories -- as a runic theory, not as an augmented runic theory. ; That is, we strip off the numes as we go. This is unesthetic: it ; would be more symmetric to produce an augmented theory since we take ; in augmented theories. But this is more efficient because we don't ; have to copy the result later to strip off the numes. (cond ((null lst1) (revappend ans nil)) ((null lst2) (revappend ans nil)) ((= (car (car lst1)) (car (car lst2))) (intersection-augmented-theories-fn1 (cdr lst1) (cdr lst2) (cons (cdr (car lst1)) ans))) ((> (car (car lst1)) (car (car lst2))) (intersection-augmented-theories-fn1 (cdr lst1) lst2 ans)) (t (intersection-augmented-theories-fn1 lst1 (cdr lst2) ans)))) (defmacro check-theory (lst wrld ctx form) `(cond ((theoryp! ,lst ,wrld) ,form) (t (er hard ,ctx "A theory function has been called on an argument that does ~ not represent a theory. See the **NOTE**s above and see ~ :DOC theories.")))) (defun intersection-theories-fn (lst1 lst2 wrld) (check-theory lst1 wrld 'intersection-theories-fn (check-theory lst2 wrld 'intersection-theories-fn (intersection-augmented-theories-fn1 (augment-theory lst1 wrld) (augment-theory lst2 wrld) nil)))) (defmacro intersection-theories (lst1 lst2) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories intersect two ~il[theories]~/ ~bv[] Example: (intersection-theories (current-theory :here) (theory 'arith-patch))~/ General Form: (intersection-theories th1 th2) ~ev[] where ~c[th1] and ~c[th2] are theories (~pl[theories]). To each of the arguments there corresponds a runic theory. This function returns the intersection of those two runic ~il[theories], represented as a list and ordered chronologically. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (list 'intersection-theories-fn lst1 lst2 'world)) (defun union-augmented-theories-fn1 (lst1 lst2 ans) ; Let lst1 and lst2 be augmented theories: descendingly ordered lists ; of pairs mapping numes to runes. We return their union as an ; unagumented runic theory. See intersection-augmented-theories-fn1. (cond ((null lst1) (revappend ans (strip-cdrs lst2))) ((null lst2) (revappend ans (strip-cdrs lst1))) ((int= (car (car lst1)) (car (car lst2))) (union-augmented-theories-fn1 (cdr lst1) (cdr lst2) (cons (cdr (car lst1)) ans))) ((> (car (car lst1)) (car (car lst2))) (union-augmented-theories-fn1 (cdr lst1) lst2 (cons (cdr (car lst1)) ans))) (t (union-augmented-theories-fn1 lst1 (cdr lst2) (cons (cdr (car lst2)) ans))))) (defun union-theories-fn1 (lst1 lst2 nume wrld ans) ; Lst2 is an augmented runic theory: descendingly ordered list of pairs mapping ; numes to runes. Lst1 is an unaugmented runic theory, which may be thought of ; as the strip-cdrs of an augmented runic theory. Nume is either nil or else ; is the nume of the first element of lst1. We accumulate into ans and ; ultimately return the result of adding all runes in lst2 to lst1, as an ; unaugmented runic theory. (cond ((null lst1) (revappend ans (strip-cdrs lst2))) ((null lst2) (revappend ans lst1)) (t (let ((nume (or nume (runep (car lst1) wrld)))) (assert$ nume (cond ((int= nume (car (car lst2))) (union-theories-fn1 (cdr lst1) (cdr lst2) nil wrld (cons (car lst1) ans))) ((> nume (car (car lst2))) (union-theories-fn1 (cdr lst1) lst2 nil wrld (cons (car lst1) ans))) (t (union-theories-fn1 lst1 (cdr lst2) nume wrld (cons (cdar lst2) ans))))))))) (defun union-theories-fn (lst1 lst2 lst1-known-to-be-runic wrld) ; We make some effort to share structure with lst1 if it is a runic theory, ; else with lst2 if it is a runic theory. Argument lst1-known-to-be-runic is ; an optimization: if it is true, then lst1 is known to be a runic theory, so ; we can skip the runic-theoryp check. (cond ((or lst1-known-to-be-runic (runic-theoryp lst1 wrld)) (check-theory lst2 wrld 'union-theories-fn (union-theories-fn1 lst1 (augment-theory lst2 wrld) nil wrld nil))) ((runic-theoryp lst2 wrld) (check-theory lst1 wrld 'union-theories-fn (union-theories-fn1 lst2 (augment-theory lst1 wrld) nil wrld nil))) (t (check-theory lst1 wrld 'union-theories-fn (check-theory lst2 wrld 'union-theories-fn (union-augmented-theories-fn1 ; We know that lst1 is not a runic-theoryp, so we open-code for a call of ; augment-theory, which should be kept in sync with the code below. (duplicitous-sort-car nil (convert-theory-to-unordered-mapping-pairs lst1 wrld)) (augment-theory lst2 wrld) nil)))))) (defmacro union-theories (lst1 lst2) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories union two ~il[theories]~/ ~bv[] Example: (union-theories (current-theory 'lemma3) (theory 'arith-patch))~/ General Form: (union-theories th1 th2) ~ev[] where ~c[th1] and ~c[th2] are theories (~pl[theories]). To each of the arguments there corresponds a runic theory. This function returns the union of those two runic ~il[theories], represented as a list and ordered chronologically. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (cond ((theory-fn-callp lst1) (list 'union-theories-fn lst1 lst2 t 'world)) ((theory-fn-callp lst2) (list 'union-theories-fn lst2 lst1 t 'world)) (t (list 'union-theories-fn lst1 lst2 nil 'world)))) (defun set-difference-augmented-theories-fn1 (lst1 lst2 ans) ; Let lst1 and lst2 be augmented theories: descendingly ordered lists ; of pairs mapping numes to runes. We return their set-difference as ; an unagumented runic theory. See intersection-augmented-theories-fn1. (cond ((null lst1) (revappend ans nil)) ((null lst2) (revappend ans (strip-cdrs lst1))) ((= (car (car lst1)) (car (car lst2))) (set-difference-augmented-theories-fn1 (cdr lst1) (cdr lst2) ans)) ((> (car (car lst1)) (car (car lst2))) (set-difference-augmented-theories-fn1 (cdr lst1) lst2 (cons (cdr (car lst1)) ans))) (t (set-difference-augmented-theories-fn1 lst1 (cdr lst2) ans)))) (defun set-difference-theories-fn1 (lst1 lst2 nume wrld ans) ; Lst2 is an augmented runic theory: descendingly ordered list of pairs mapping ; numes to runes. Lst1 is an unaugmented runic theory, which may be thought of ; as the strip-cdrs of an augmented runic theory. Nume is either nil or else ; is the nume of the first element of lst1. We accumulate into ans and ; ultimately return the result of removing all runes in lst2 from lst1, as an ; unaugmented runic theory. (cond ((null lst1) (reverse ans)) ((null lst2) (revappend ans lst1)) (t (let ((nume (or nume (runep (car lst1) wrld)))) (assert$ nume (cond ((int= nume (car (car lst2))) (set-difference-theories-fn1 (cdr lst1) (cdr lst2) nil wrld ans)) ((> nume (car (car lst2))) (set-difference-theories-fn1 (cdr lst1) lst2 nil wrld (cons (car lst1) ans))) (t (set-difference-theories-fn1 lst1 (cdr lst2) nume wrld ans)))))))) (defun set-difference-theories-fn (lst1 lst2 lst1-known-to-be-runic wrld) ; We make some effort to share structure with lst1 if it is a runic theory. ; Argument lst1-known-to-be-runic is an optimization: if it is true, then lst1 ; is known to be a runic theory, so we can skip the runic-theoryp check. (cond ((or lst1-known-to-be-runic (runic-theoryp lst1 wrld)) (check-theory lst2 wrld 'set-difference-theories-fn (set-difference-theories-fn1 lst1 (augment-theory lst2 wrld) nil wrld nil))) (t (check-theory lst1 wrld 'set-difference-theories-fn (check-theory lst2 wrld 'set-difference-theories-fn (set-difference-augmented-theories-fn1 ; We know that lst1 is not a runic-theoryp, so we open-code for a call of ; augment-theory, which should be kept in sync with the code below. (duplicitous-sort-car nil (convert-theory-to-unordered-mapping-pairs lst1 wrld)) (augment-theory lst2 wrld) nil)))))) (defmacro set-difference-theories (lst1 lst2) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories difference of two ~il[theories]~/ ~bv[] Example: (set-difference-theories (current-theory :here) '(fact (fact)))~/ General Form: (set-difference-theories th1 th2) ~ev[] where ~c[th1] and ~c[th2] are ~il[theories] (~pl[theories]). To each of the arguments there corresponds a runic theory. This function returns the set-difference of those two runic ~il[theories], represented as a list and ordered chronologically. That is, a ~il[rune] is in the result iff it is in the first runic theory but not in the second. The standard way to ``disable'' a theory, ~c[lst], is: ~c[(in-theory (set-difference-theories (current-theory :here) lst))]. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (list 'set-difference-theories-fn lst1 lst2 (theory-fn-callp lst1) 'world)) ; Now we define a few useful theories. (defun universal-theory-fn1 (lst ans redefined) ; Lst is a cdr of the current world. We scan down lst accumulating onto ans ; every rune in every 'runic-mapping-pairs property. Our final ans is ; descendingly ordered. We take advantage of the fact that the world is ; ordered reverse-chronologically, so the runes in the first ; 'runic-mapping-pairs we see will have the highest numes. ; If at any point we encounter the 'global-value for the variable ; 'standard-theories then we assume the value is of the form (r-unv r-fn1 r-fn2 ; r-fn3), where r-unv is the reversed universal theory as of that world, r-fn1 ; is the reversed function symbol theory, r-fn2 is the reversed executable ; counterpart theory, and r-fn3 is the reversed function theory. If we find ; such a binding we stop and revappend r-unv to our answer and quit. By this ; hack we permit the precomputation of a big theory and save having to scan ; down world -- which really means save having to swap world into memory. ; At the end of the bootstrap we will save the standard theories just to ; prevent the swapping in of prehistoric conses. ; Note: :REDEF complicates matters. If a name is redefined the runes based on ; its old definition are invalid. We can tell that sym has been redefined when ; we encounter on lst a triple of the form (sym RUNIC-MAPPING-PAIRS ; . :ACL2-PROPERTY-UNBOUND). This means that all runes based on sym ; encountered subsequently must be ignored or deleted (ignored when encountered ; as RUNIC-MAPPING-PAIRS and deleted when seen in the stored standard theories. ; The list redefined contains all such syms encountered. (cond ((null lst) #+acl2-metering (meter-maid 'universal-theory-fn1 500) (reverse ans)) ; unexpected, but correct ((eq (cadr (car lst)) 'runic-mapping-pairs) #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (cond ((eq (cddr (car lst)) *acl2-property-unbound*) (universal-theory-fn1 (cdr lst) ans (add-to-set-eq (car (car lst)) redefined))) ((member-eq (car (car lst)) redefined) (universal-theory-fn1 (cdr lst) ans redefined)) (t (universal-theory-fn1 (cdr lst) (append-strip-cdrs (cddr (car lst)) ans) redefined)))) ((and (eq (car (car lst)) 'standard-theories) (eq (cadr (car lst)) 'global-value)) #+acl2-metering (meter-maid 'universal-theory-fn1 500) (revappend-delete-runes-based-on-symbols (car (cddr (car lst))) redefined ans)) (t #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (universal-theory-fn1 (cdr lst) ans redefined)))) (defun universal-theory-fn (logical-name wrld) ; Return the theory containing all of the rule names in the world created ; by the event that introduced logical-name. (declare (xargs :guard (logical-namep logical-name wrld))) ; It is possible that wrld starts with a triple of the form (name REDEFINED ; . mode) in which case that triple is followed by an arbitrary number of ; triples "renewing" various properties of name. Among those properties is, ; necessarily, RUNIC-MAPPING-PAIRS. This situation only arises if we are ; evaluating a theory expression as part of an event that is in fact redefining ; name. These "mid-event" worlds are odd precisely because they do not start ; on event boundaries (with appropriate interpretation given to the occasional ; saving of worlds and theories). ; Now we are asked to get a theory as of logical-name and hence must decode ; logical name wrt wrld, obtaining some tail of wrld, wrld1. If we are in the ; act of redefining name then we add to wrld1 the triple unbinding ; RUNIC-MAPPING-PAIRS of name. Why not add all the renewing triples? The ; reason is that this is the only renewed property that is relevant to ; universal-theory1, the workhorse here. (let* ((wrld1 (decode-logical-name logical-name wrld)) (redefined (collect-redefined wrld nil)) (wrld2 (putprop-x-lst1 redefined 'runic-mapping-pairs *acl2-property-unbound* wrld1))) (assert$-runic-theoryp (universal-theory-fn1 wrld2 nil nil) wrld))) (defmacro universal-theory (logical-name) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories all rules as of logical name~/ ~bv[] Examples: (universal-theory :here) (universal-theory 'lemma3) ~ev[] ~l[logical-name].~/ ~bv[] General Form: (universal-theory logical-name) ~ev[] Returns the theory consisting of all the ~il[rune]s that existed immediately after ~ilc[logical-name] was introduced. ~l[theories] and ~pl[logical-name]. The theory includes ~ilc[logical-name] itself (if there is a rule by that name). (Note that since some ~il[events] do not introduce rules (e.g., ~ilc[defmacro], ~ilc[defconst] or ~ilc[defthm] with ~c[:]~ilc[rule-classes] ~c[nil]), the universal-theory does not necessarily include a ~il[rune] for every event name.) The universal-theory is very long and you will probably regret printing it. You may experience a fencepost problem in deciding which ~il[logical-name] to use. ~ilc[Deflabel] can always be used to mark unambiguously for future reference a particular point in the development of your theory. This is convenient because ~ilc[deflabel] does not introduce any rules and hence it doesn't matter if you count it as being in the interval or not. The order of ~il[events] in the vicinity of an ~ilc[encapsulate] is confusing. ~l[encapsulate]. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world]. Also ~pl[current-theory]. ~c[Current-theory] is much more commonly used than ~c[universal-theory]. The former includes only the ~il[enable]d ~il[rune]s as of the given ~ilc[logical-name], which is probably what you want, while the latter includes ~il[disable]d ones as well.~/ :cited-by theory-functions" (list 'universal-theory-fn logical-name 'world)) (defun function-theory-fn1 (token lst ans redefined) ; Token is either :DEFINITION, :EXECUTABLE-COUNTERPART or something ; else. Lst is a cdr of the current world. We scan down lst and ; accumulate onto ans all of the runes of the indicated type (or both ; if token is neither of the above). ; As in universal-theory-fn1, we also look out for the 'global-value of ; 'standard-theories and for *acl2-property-unbound*. See the comment there. (cond ((null lst) #+acl2-metering (meter-maid 'function-theory-fn1 500) (reverse ans)) ; unexpected, but correct ((eq (cadr (car lst)) 'runic-mapping-pairs) #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (cond ((eq (cddr (car lst)) *acl2-property-unbound*) (function-theory-fn1 token (cdr lst) ans (add-to-set-eq (car (car lst)) redefined))) ((member-eq (car (car lst)) redefined) (function-theory-fn1 token (cdr lst) ans redefined)) ((eq (car (cdr (car (cddr (car lst))))) :DEFINITION) ; The test above extracts the token of the first rune in the mapping pairs and ; this is a function symbol iff it is :DEFINITION. (function-theory-fn1 token (cdr lst) (case token (:DEFINITION (cons (cdr (car (cddr (car lst)))) ans)) (:EXECUTABLE-COUNTERPART ; Note that we might be looking at the result of storing a :definition rule, in ; which case there will be no :executable-counterpart rune. So, we check that ; we have something before accumulating it. (let ((x (cdr (cadr (cddr (car lst)))))) (if (null x) ans (cons x ans)))) (otherwise (cons (cdr (car (cddr (car lst)))) (cons (cdr (cadr (cddr (car lst)))) ans)))) redefined)) (t (function-theory-fn1 token (cdr lst) ans redefined)))) ((and (eq (car (car lst)) 'standard-theories) (eq (cadr (car lst)) 'global-value)) #+acl2-metering (meter-maid 'function-theory-fn1 500) (revappend-delete-runes-based-on-symbols (case token (:DEFINITION (cadr (cddr (car lst)))) (:EXECUTABLE-COUNTERPART (caddr (cddr (car lst)))) (otherwise (cadddr (cddr (car lst))))) redefined ans)) (t #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (function-theory-fn1 token (cdr lst) ans redefined)))) (defun function-theory-fn (logical-name wrld) ; Return the theory containing all of the function names in the world ; created by the user event that introduced logical-name. (declare (xargs :guard (logical-namep logical-name wrld))) ; See universal-theory-fn for an explanation of the production of wrld2. (let* ((wrld1 (decode-logical-name logical-name wrld)) (redefined (collect-redefined wrld nil)) (wrld2 (putprop-x-lst1 redefined 'runic-mapping-pairs *acl2-property-unbound* wrld1))) (assert$-runic-theoryp (function-theory-fn1 :DEFINITION wrld2 nil nil) wrld))) (defmacro function-theory (logical-name) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories function symbol rules as of logical name~/ ~bv[] Examples: (function-theory :here) (function-theory 'lemma3) ~ev[] ~l[logical-name].~/ ~bv[] General Form: (function-theory logical-name) ~ev[] Returns the theory containing all the ~c[:]~ilc[definition] ~il[rune]s, whether ~il[enable]d or not, that existed immediately after ~ilc[logical-name] was introduced. See the documentation for ~il[theories], ~il[logical-name] and ~ilc[executable-counterpart-theory]. You may experience a fencepost problem in deciding which logical name to use. ~ilc[Deflabel] can always be used to mark unambiguously for future reference a particular point in the development of your theory. The order of ~il[events] in the vicinity of an ~ilc[encapsulate] is confusing. ~l[encapsulate]. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (list 'function-theory-fn logical-name 'world)) (defun executable-counterpart-theory-fn (logical-name wrld) ; Return the theory containing all of the executable-counterpart names ; in the world created by the event that introduced logical-name. (declare (xargs :guard (logical-namep logical-name wrld))) ; See universal-theory-fn for an explanation of the production of wrld2. (let* ((wrld1 (decode-logical-name logical-name wrld)) (redefined (collect-redefined wrld nil)) (wrld2 (putprop-x-lst1 redefined 'runic-mapping-pairs *acl2-property-unbound* wrld1))) (function-theory-fn1 :executable-counterpart wrld2 nil nil))) (defmacro executable-counterpart-theory (logical-name) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories executable counterpart rules as of logical name~/ ~bv[] Examples: (executable-counterpart-theory :here) (executable-counterpart-theory 'lemma3) ~ev[] ~l[logical-name].~/ ~bv[] General Form: (executable-counterpart-theory logical-name) ~ev[] Returns the theory containing all the ~c[:]~ilc[executable-counterpart] ~il[rune]s, whether ~il[enable]d or not, that existed immediately after ~ilc[logical-name] was introduced. See the documentation for ~il[theories], ~il[logical-name], ~il[executable-counterpart] and ~ilc[function-theory]. You may experience a fencepost problem in deciding which logical name to use. ~ilc[Deflabel] can always be used to mark unambiguously for future reference a particular point in the development of your theory. The order of ~il[events] in the vicinity of an ~ilc[encapsulate] is confusing. ~l[encapsulate]. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (list 'executable-counterpart-theory-fn logical-name 'world)) ; Having defined the functions for computing the standard theories, ; we'll now define the function for precomputing them. (defun standard-theories (wrld) (list (universal-theory-fn1 wrld nil nil) (function-theory-fn1 :definition wrld nil nil) (function-theory-fn1 :executable-counterpart wrld nil nil) (function-theory-fn1 :both wrld nil nil))) (defun current-theory-fn (logical-name wrld) ; We return the theory that was enabled in the world created by the ; event that introduced logical-name. ; See universal-theory-fn for an explanation of the production of wrld2. (let* ((wrld1 (decode-logical-name logical-name wrld)) (redefined (collect-redefined wrld nil)) (wrld2 (putprop-x-lst1 redefined 'runic-mapping-pairs *acl2-property-unbound* wrld1))) (prog2$ (or wrld1 (er hard 'current-theory "The name ~x0 was not found in the current ACL2 logical ~ world; hence no current-theory can be computed for that name." logical-name)) (assert$-runic-theoryp (current-theory1 wrld2 nil nil) wrld)))) (defmacro current-theory (logical-name) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories currently ~il[enable]d rules as of logical name~/ ~bv[] Examples: (current-theory :here) (current-theory 'lemma3) ~ev[] ~l[logical-name].~/ ~bv[] General Form: (current-theory logical-name) ~ev[] Returns the current theory as it existed immediately after the introduction of ~ilc[logical-name] provided it is evaluated in an environment in which the variable symbol WORLD is bound to the current ACL2 logical world, ~c[(w state)]. Thus, ~bv[] ACL2 !>(current-theory :here) ~ev[] will cause an (unbound variable) error while ~bv[] ACL2 !>(let ((world (w state))) (current-theory :here)) ~ev[] will return the current theory in world. ~l[theories] and ~pl[logical-name] for a discussion of theories in general and why the commonly used ``theory functions'' such as ~c[current-theory] are really macros that expand into terms involving the variable ~c[world]. The theory returned by ~c[current-theory] is in fact the theory selected by the ~ilc[in-theory] event most recently preceding logical name, extended by the rules introduced up through ~ilc[logical-name]. You may experience a fencepost problem in deciding which logical name to use. ~ilc[Deflabel] can always be used to mark unambiguously for future reference a particular point in the development of your theory. The order of ~il[events] in the vicinity of an ~ilc[encapsulate] is confusing. ~l[encapsulate]. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (list 'current-theory-fn logical-name 'world)) ; Essay on Theory Manipulation Performance ; Below we show some statistics on our theory manipulation functions. ; These are recorded in case we someday change these functions and ; wish to compare the old and new implementations. The expressions ; shown should be executed in raw lisp, not LP, because they involve ; the time function. These expressions were executed in a newly ; initialized ACL2. The times are on a Sparc 2 (Rana). ; The following expression is intended as a "typical" heavy duty ; theory expression. For the record, the universal theory at the time ; of these tests contained 1307 runes. ; (let ((world (w *the-live-state*))) ; (time ; (length ; (union-theories ; (intersection-theories (current-theory :here) ; (executable-counterpart-theory :here)) ; (set-difference-theories (universal-theory :here) ; (function-theory :here)))))) ; Repeated runs were done. Typical results were: ; real time : 0.350 secs ; run time : 0.233 secs ; 993 ; The use of :here above meant that all the theory functions involved ; just looked up their answers in the 'standard-theories at ; the front of the initialized world. The following expression forces ; the exploration of the whole world. In the test, "ACL2-USER" was ; the event printed by :pc -1, i.e., the last event before ending the ; boot. ; (let ((world (w *the-live-state*))) ; (time ; (length ; (union-theories ; (intersection-theories (current-theory "ACL2-USER") ; (executable-counterpart-theory "ACL2-USER")) ; (set-difference-theories (universal-theory "ACL2-USER") ; (function-theory "ACL2-USER")))))) ; Repeated tests produced the following typical results. ; real time : 0.483 secs ; run time : 0.383 secs ; 993 ; The first run, however, had a real time of almost 10 seconds because ; wrld had to be paged in. ; The final test stresses sorting. We return to the :here usage to ; get our theories, but we reverse the output every chance we get so ; as force the next theory function to sort. In addition, we ; strip-cadrs all the input runic theories to force the reconstruction ; of runic theories from the wrld. ; (let ((world (w *the-live-state*))) ; (time ; (length ; (union-theories ; (reverse ; (intersection-theories ; (reverse (strip-base-symbols (current-theory :here))) ; (reverse (strip-base-symbols (executable-counterpart-theory :here))))) ; (reverse ; (set-difference-theories ; (reverse (strip-base-symbols (universal-theory :here))) ; (reverse (strip-base-symbols (function-theory :here))))))))) ; Typical times were ; real time : 1.383 secs ; run time : 0.667 secs ; 411 ; The size of the result is smaller because the strip-cadrs identifies ; several runes, e.g., (:DEFINITION fn) and (:EXECUTABLE-COUNTERPART ; fn) both become fn which is then understood as (:DEFINITION fn). ; End of performance data. (defconst *initial-return-last-table* '((time$1-raw . time$1) (memoize-on-raw . memoize-on) (memoize-off-raw . memoize-off) (with-prover-time-limit1-raw . with-prover-time-limit1) (with-fast-alist-raw . with-fast-alist) (with-stolen-alist-raw . with-stolen-alist) (fast-alist-free-on-exit-raw . fast-alist-free-on-exit) ; Keep the following comment in sync with *initial-return-last-table* and with ; chk-return-last-entry. ; The following could be omitted since return-last gives them each special ; handling: prog2$ and mbe1 are used during the boot-strap before tables are ; supported, and ec-call1 and (in ev-rec-return-last) with-guard-checking gets ; special handling. It is harmless though to include them explicitly, in ; particular at the end so that they do not add time in the expected case of ; finding one of the other entries in the table. If we decide to avoid special ; handling (which we have a right to do, by the way, since users who modify ; return-last-table are supposed to know what they are doing, as a trust tag is ; needed), then we should probably move these entries to the top where they'll ; be seen more quickly. (progn . prog2$) (mbe1-raw . mbe1) (ec-call1-raw . ec-call1) (with-guard-checking1-raw . with-guard-checking1))) (defun end-prehistoric-world (wrld) (let* ((wrld1 (global-set-lst (list (list 'untouchable-fns (append *initial-untouchable-fns* (global-val 'untouchable-fns wrld))) (list 'untouchable-vars (append *initial-untouchable-vars* (global-val 'untouchable-vars wrld))) (list 'standard-theories (standard-theories wrld)) (list 'boot-strap-flg nil) (list 'boot-strap-pass-2 nil) (list 'command-number-baseline-info (let ((command-number-baseline (next-absolute-command-number wrld))) (make command-number-baseline-info :current command-number-baseline :permanent-p t :original command-number-baseline))) (list 'event-number-baseline (next-absolute-event-number wrld)) (list 'skip-proofs-seen nil) (list 'redef-seen nil) (list 'proof-supporters-alist nil)) (putprop 'acl2-defaults-table 'table-alist *initial-acl2-defaults-table* (putprop 'return-last-table 'table-alist *initial-return-last-table* wrld)))) (wrld2 (update-current-theory (current-theory1 wrld nil nil) wrld1))) (add-command-landmark :logic '(exit-boot-strap-mode) nil ; cbd is only needed for user-generated commands nil (add-event-landmark '(exit-boot-strap-mode) 'exit-boot-strap-mode 0 wrld2 t)))) (defun theory-namep (name wrld) ; We return t or nil according to whether name is the name of a theory, ; i.e., a name introduced by deftheory. (and (symbolp name) (not (eq (getprop name 'theory t 'current-acl2-world wrld) t)))) (defun theory-fn (name wrld) ; We deliver the value of the defined theory named name. (declare (xargs :guard t)) (cond ((theory-namep name wrld) (getprop name 'theory nil 'current-acl2-world wrld)) (t (er hard?! 'theory "The alleged theory name, ~x0, is not the name of a previously ~ executed deftheory event. See :DOC theory." name)))) (defmacro theory (name) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories retrieve named theory~/ ~bv[] Example: (theory 'ground-zero) ~ev[] In the example above, the theory returned is the one in force when ACL2 is started up (~pl[ground-zero]).~/ ~bv[] General Form: (theory name) ~ev[] where ~c[name] is the name of a previously executed ~ilc[deftheory] event (otherwise a hard error occurs). Returns the named theory. ~l[theories]. This ``function'' is actually a macro that expands to a term mentioning the single free variable ~ilc[world]. When theory expressions are evaluated by ~ilc[in-theory] or the ~c[:]~ilc[in-theory] hint, ~ilc[world] is bound to the current ACL2 ~il[world].~/ :cited-by theory-functions" (list 'theory-fn name 'world)) (defun deftheory-fn (name expr state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ; Historical Note: Once upon a time deftheory-fn did not exist even ; though deftheory did. We defined deftheory as a macro which expanded ; into a defconstant-fn expression. In particular, ; (deftheory *a* (union *b* (universe w))) ; was mapped to ; (er-let* ((lst (translate-in-theory-hint ; '(union *b* (universe w)) ; nil ; '(deftheory . *a*) ; (w state) ; state))) ; (defconstant-fn '*a* ; (list 'quote lst) ; state ; nil)) ; Thus, the "semantics" of a successful execution of deftheory was that of ; defconstant. This suffered from letting theories creep into formulas. For ; example, one could later write in a proposed theorem (member 'foo *a*) and ; the truth of that proposition depended upon the particular theory computed ; for *a*. This made it impossible to permit either the use of state in ; "theory expressions" (since different theories could be computed for ; identical worlds, depending on ld-skip-proofsp) or the use of deftheory in ; encapsulate (see below). The state prohibition forced upon us the ugliness ; of permitting the user to reference the current ACL2 world via the free ; variable W in theory expressions, which we bound appropriately before evaling ; the expressions. ; We abandoned the use of defconstant (now defconst) for these reasons. ; Here is a comment that once illustrated why we did not allow deftheory ; to be used in encapsulate: ; We do not allow deftheory expressions in encapsulate. This may be a ; severe restriction but it is necessary for soundness given the current ; implementation of deftheory. Consider the following: ; (encapsulate nil ; (local (defun foo () 1)) ; (deftheory *u* (all-names w)) ; (defthm foo-thm (member 'foo *u*))) ; where all-names is a user defined function that computes the set of ; all names in a given world. [Note: Intuitively, (all-names w) is ; (universal-theory nil w). Depending on how event descriptors are ; handled, that may or may not be correct. In a recent version of ; ACL2, (universal-theory nil w), if used in an encapsulate, had the ; effect of computing all the names in the theory as of the last ; world-chaning form executed by the top-level loop. But because ; encapsulate did not so mark each term as it executed them, ; universal-theory backed up to the point in w just before the ; encapsulate. Thus, universal-theory could not be used to get the ; effect intended here. However, (all-names w) could be defined by ; the user to get what is intended here.] ; When the above sequence is processed in pass 1 of encapsulate *u* ; includes 'foo and hence the defthm succeeds. But when it is processed ; in pass 2 *u* does not include 'foo and so the assumption of the ; defthm is unsound! In essence, permitting deftheory in encapsulate is ; equivalent to permitting (w state) in defconst forms. That is ; disallowed too (as is the use of any variable in an defconst form). ; If you can set a constant as a function of the world, then you can use ; the constant to determine which encapsulate pass you are in. (when-logic "DEFTHEORY" (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'deftheory name)) (let ((wrld (w state)) (event-form (or event-form (list* 'deftheory name expr (if doc (list :doc doc) nil))))) (er-progn (chk-all-but-new-name name ctx nil wrld state) (er-let* ((wrld1 (chk-just-new-name name 'theory nil ctx wrld state)) (doc-pair (translate-doc name doc ctx state)) (theory0 (translate-in-theory-hint expr nil ctx wrld1 state))) (mv-let (theory theory-augmented-ignore) ; The following call is similar to the one in update-current-theory. But here, ; our aim is just to create an appropriate theory, without extending the ; world. (extend-current-theory (global-val 'current-theory wrld) theory0 :none wrld) (declare (ignore theory-augmented-ignore)) (let ((wrld2 (update-doc-database name doc doc-pair (putprop name 'theory theory wrld1)))) ; Note: We do not permit DEFTHEORY to be made redundant. If this ; is changed, change the text of the :doc for redundant-events. (install-event (length theory) event-form 'deftheory name nil nil nil ; global theory is unchanged nil wrld2 state))))))))) ; And now we move on to the in-theory event, in which we process a theory ; expression into a theory and then load it into the global enabled ; structure. (defun in-theory-fn (expr state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (when-logic "IN-THEORY" (with-ctx-summarized (if (output-in-infixp state) event-form (cond ((atom expr) (cond ((null doc) (msg "( IN-THEORY ~x0)" expr)) (t (cons 'in-theory expr)))) ((symbolp (car expr)) (cond ((null doc) (msg "( IN-THEORY (~x0 ...))" (car expr))) (t (msg "( IN-THEORY (~x0 ...) ...)" (car expr))))) ((null doc) "( IN-THEORY (...))") (t "( IN-THEORY (...) ...)"))) (let ((wrld (w state)) (event-form (or event-form (list* 'in-theory expr (if doc (list :doc doc) nil))))) (er-let* ((doc-pair (translate-doc nil doc ctx state)) (theory0 (translate-in-theory-hint expr t ctx wrld state))) (let* ((ens1 (ens state)) (force-xnume-en1 (enabled-numep *force-xnume* ens1)) (imm-xnume-en1 (enabled-numep *immediate-force-modep-xnume* ens1)) (wrld1 (update-current-theory theory0 wrld))) ; Note: We do not permit IN-THEORY to be made redundant. If this ; is changed, change the text of the :doc for redundant-events. (er-let* ((val (install-event (length theory0) event-form 'in-theory 0 nil nil :protect nil wrld1 state))) (pprogn (if (member-equal expr '((enable (:EXECUTABLE-COUNTERPART force)) (disable (:EXECUTABLE-COUNTERPART force)) (enable (:EXECUTABLE-COUNTERPART immediate-force-modep)) (disable (:EXECUTABLE-COUNTERPART immediate-force-modep)))) state (maybe-warn-about-theory ens1 force-xnume-en1 imm-xnume-en1 (ens state) ctx wrld state)) (value (list :NUMBER-OF-ENABLED-RUNES val)))))))))) (defun in-arithmetic-theory-fn (expr state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ; After Version_3.0, the following differs from the fancier in-theory-fn. The ; latter calls update-current-theory to deal with the 'current-theory and ; related properties, 'current-theory-augmented and 'current-theory-index. ; Someday we may want to make analogous changes to the present function. (when-logic "IN-ARITHMETIC-THEORY" (with-ctx-summarized (if (output-in-infixp state) event-form (cond ((atom expr) (cond ((null doc) (msg "( IN-ARITHMETIC-THEORY ~x0)" expr)) (t (cons 'in-arithmetic-theory expr)))) ((symbolp (car expr)) (cond ((null doc) (msg "( IN-ARITHMETIC-THEORY (~x0 ...))" (car expr))) (t (msg "( IN-ARITHMETIC-THEORY (~x0 ...) ...)" (car expr))))) ((null doc) "( IN-ARITHMETIC-THEORY (...))") (t "( IN-ARITHMETIC-THEORY (...) ...)"))) (let ((wrld (w state)) (event-form (or event-form (list* 'in-arithmetic-theory expr (if doc (list :doc doc) nil))))) (cond ((not (quotep expr)) (er soft ctx "Arithmetic theory expressions must be quoted constants. ~ See :DOC in-arithmetic-theory.")) (t (er-let* ((doc-pair (translate-doc nil doc ctx state)) (theory (translate-in-theory-hint expr t ctx wrld state)) (ens (load-theory-into-enabled-structure expr theory nil (global-val 'global-arithmetic-enabled-structure wrld) nil nil wrld ctx state))) (let ((wrld1 (global-set 'global-arithmetic-enabled-structure ens wrld))) ; Note: We do not permit IN-THEORY to be made redundant. If this ; is changed, change the text of the :doc for redundant-events. (install-event (length theory) event-form 'in-arithmetic-theory 0 nil nil nil ; handles its own invariants checking nil wrld1 state))))))))) (defmacro disable (&rest rst) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories deletes names from current theory~/ ~bv[] Example: (disable fact (fact) associativity-of-app)~/ General Form: (disable name1 name2 ... namek) ~ev[] where each ~c[namei] is a runic designator; ~pl[theories]. The result is the theory that contains all the names in the current theory except those listed. Note that this is merely a function that returns a theory. The result is generally a very long list of ~il[rune]s and you will probably regret printing it. The standard way to ``disable'' a fixed set of names, is as follows; ~pl[hints] and ~pl[in-theory]. ~bv[] :in-theory (disable name1 name2 ... namek) ; in a hint (in-theory (disable name1 name2 ... namek)) ; as an event (local ; often desirable, to avoid exporting from the current context (in-theory (disable name1 name2 ... namek))) ~ev[] Note that all the names are implicitly quoted. If you wish to disable a computed list of names, ~c[lst], use the theory expression ~c[(set-difference-theories (current-theory :here) lst)].~/ :cited-by theory-functions" (list 'set-difference-theories-fn '(current-theory :here) (kwote rst) t 'world)) (defmacro enable (&rest rst) ; Warning: The resulting value must be a runic-theoryp. See theory-fn-callp. ":Doc-Section Theories adds names to current theory~/ ~bv[] Example: (enable fact (fact) associativity-of-app)~/ General Form: (enable name1 name2 ... namek) ~ev[] where each ~c[namei] is a runic designator; ~pl[theories]. The result is the theory that contains all the names in the current theory plus those listed. Note that this is merely a function that returns a theory. The result is generally a very long list of ~il[rune]s and you will probably regret printing it. The standard way to ``enable'' a fixed set of names, is as follows; ~pl[hints] and ~pl[in-theory]. ~bv[] :in-theory (enable name1 name2 ... namek) ; in a hint (in-theory (enable name1 name2 ... namek)) ; as an event (local ; often desirable, to avoid exporting from the current context (in-theory (enable name1 name2 ... namek))) ~ev[] Note that all the names are implicitly quoted. If you wish to enable a computed list of names, ~c[lst], use the theory expression ~c[(union-theories (current-theory :here) lst)].~/ :cited-by theory-functions" (list 'union-theories-fn '(current-theory :here) (kwote rst) t 'world)) ; The theory-invariant-table maps arbitrary keys to translated terms ; involving only the variables THEORY and STATE: (table theory-invariant-table nil nil :guard (and (consp val) (consp (cdr val)) (let ((tterm (access theory-invariant-record val :tterm))) (and (termp tterm world) (booleanp (access theory-invariant-record val :error)) (subsetp-eq (all-vars tterm) '(ens state)))))) #+acl2-loop-only (defmacro theory-invariant (&whole event-form term &key key (error 't)) ; Note: This macro "really" expands to a TABLE event (after computing ; the right args for it!) and hence it should inherit the TABLE event's ; semantics under compilation, which is to say, is a noop. This ; requirement wasn't noticed until somebody put a THEORY-INVARIANT ; event into a book and then the compiled book compiled the logical ; code below and thus loading the .o file essentially tried to ; reexecute the table event after it had already been executed by the ; .lisp code in the book. A hard error was caused. ; Therefore, we also define this macro as a trivial no-op in raw Lisp. ":Doc-Section Events user-specified invariants on ~il[theories]~/ ~bv[] Examples: (theory-invariant (not (and (active-runep '(:rewrite left-to-right)) (active-runep '(:rewrite right-to-left)))) :key my-invariant :error nil) ; Equivalent to the above: (theory-invariant (incompatible (:rewrite left-to-right) (:rewrite right-to-left)) :key my-invariant :error nil)~/ General Form: (theory-invariant term &key key error) ~ev[] where:~bq[] o ~c[term] is a term that uses no variables other than ~c[ens] and ~ilc[state]; o ~c[key] is an arbitrary ``name'' for this invariant (if omitted, an integer is generated and used); and o ~c[:error] specifies the action to be taken when an invariant is violated ~-[] either ~c[nil] if a warning is to be printed, else ~c[t] (the default) if an error is to be caused. ~eq[]~c[Theory-invariant] is an event that adds to or modifies the ~il[table] of user-supplied theory invariants that are checked each time a theory expression is evaluated. The theory invariant mechanism is provided via a table (~pl[table]) named ~c[theory-invariant-table]. In fact, the ~c[theory-invariant] ``event'' is just a macro that expands into a use of the ~ilc[table] event. More general access to the ~c[theory-invariant] ~il[table] is provided by ~ilc[table] itself. For example, the ~il[table] can be inspected or cleared with ~ilc[table]; you can clear an individual theory invariant by setting the invariant to ~c[t], or eliminate all theory invariants with the command ~c[(table theory-invariant-table nil nil :clear)]. ~c[Theory-invariant-table] maps arbitrary keys to records containing terms that mention, at most, the variables ~c[ens] and ~ilc[state]. Every time an alleged theory expression is evaluated, e.g., in the ~ilc[in-theory] event or ~c[:]~ilc[in-theory] hint, each of the terms in ~c[theory-invariant-table] is evaluated with ~c[ens] bound to a so-called ``enabled structure'' obtained from the theory expression and ~ilc[state] bound to the current ACL2 state (~pl[state]). Users generally need not know about the enabled structure, other than that it can be accessed using the macros ~c[active-runep] and ~c[incompatible]; ~pl[active-runep] and ~pl[incompatible]. If the result is ~c[nil], a message is printed and an error occurs (except, only a warning occurs if ~c[:error nil] is specified). Thus, the ~il[table] can be thought of as a list of conjuncts. Each ~c[term] in the ~il[table] has a ``name,'' which is just the key under which the term is stored. When a theory violates the restrictions specified by some term, both the name and the term are printed. By calling ~c[theory-invariant] with a new term but the same name, you can overwrite that conjunct of the theory invariant; but see the Local Redefinition Caveat at the end of this note. You may want to avoid using explicit names, since otherwise the subsequent inclusion of another book that defines a theory invariant with the same name will override your theory invariant. Theory invariants are particularly useful in the context of large rule sets intended for re-use. Such sets often contain conflicting rules, e.g., rules that are to be ~il[enable]d when certain function symbols are ~il[disable]d, rules that rewrite in opposite directions and thus loop if simultaneously ~il[enable]d, groups of rules which should be ~il[enable]d in concert, etc. The developer of such rule sets understands these restrictions and probably documents them. The theory invariant mechanism allows the developer to codify his restrictions so that the user is alerted when they are violated. Since theory invariants are arbitrary terms, macros may be used to express commonly used restrictions. For example, executing the event ~bv[] (theory-invariant (incompatible (:rewrite left-to-right) (:rewrite right-to-left))) ~ev[] would subsequently cause an error any time the current theory contained both of the two ~il[rune]s shown. Of course, ~il[incompatible] is just defined as a macro. Its definition may be inspected with ~c[:pe incompatible]. In order for a ~c[theory-invariant] event to be accepted, the proposed theory invariant must be satisfied by the current theory (~pl[current-theory]). The value returned upon successful execution of the event is the key (whether user-supplied or generated). Local Redefinition Caveat. Care needs to be taken when redefining a theory invariant in a ~il[local] context. Consider the following example. ~bv[] (theory-invariant (active-runep '(:definition binary-append)) :key app-inv) (encapsulate () (local (theory-invariant t :key app-inv)) (in-theory (disable binary-append)) (defthm ...)) ~ev[] The second pass of the ~ilc[encapsulate] will fail, because the ~ilc[in-theory] event violates the original ~c[theory-invariant] and the ~ilc[local] ~c[theory-invariant] is skipped in the second pass of the ~ilc[encapsulate]. Of course, ~ilc[local] ~ilc[theory-invariant]s in ~il[books] can cause the analogous problem in the second (~ilc[include-book]) pass of a ~ilc[certify-book]. In both cases, though, the theory invariants are only checked at the conclusion of the (~c[include-book] or ~c[encapsulate]) event. Indeed, theory invariants are checked at the end of every event related to ~il[theories], including ~ilc[defun], ~ilc[defthm], ~ilc[in-theory], ~ilc[encapsulate], and ~ilc[include-book], except for events executed on behalf of an ~ilc[include-book] or the second pass of an ~ilc[encapsulate].~/" `(when-logic "THEORY-INVARIANT" (with-ctx-summarized 'theory-invariant (er-let* ((tterm (translate ',term '(nil) nil '(state) 'theory-invariant (w state) state))) ; known-stobjs ='(state). All other variables in term are treated as ; non- stobjs. This is ok because the :guard on the ; theory-invariant-table will check that the only variables involved ; in tterm are THEORY and STATE and when we ev the term THEORY will be ; bound to a non-stobj (and STATE to state, of course). (let* ((inv-table (table-alist 'theory-invariant-table (w state))) (key ,(if key `(quote ,key) '(1+ (length inv-table))))) (er-let* ((val (with-output :off summary (table-fn1 'theory-invariant-table key (make theory-invariant-record :tterm tterm :error ',error :untrans-term ',term) :put nil 'theory-invariant (w state) state ',event-form)))) (cond ((eq val :redundant) (value val)) (t (pprogn (cond ((assoc-equal key inv-table) (warning$ 'theory-invariant "Theory" "An existing theory invariant, named ~ ~x0, is being overwritten by a new ~ theory invariant with that name.~@1" key (cond ((f-get-global 'in-local-flg state) " Moreover, this override is ~ being done LOCALly; see :DOC ~ theory-invariant (in particular, ~ the Local Redefinition Caveat ~ there), especially if an error ~ occurs.") (t "")))) (t state)) (mv-let (erp val state) (with-output :off summary (in-theory (current-theory :here))) (declare (ignore val)) (cond (erp (er soft 'theory-invariant "The specified theory invariant fails for ~ the current ACL2 world, and hence is ~ rejected. This failure can probably be ~ overcome by supplying an appropriate ~ in-theory event first.")) (t (value key))))))))))))) #-acl2-loop-only (defmacro theory-invariant (&rest args) (declare (ignore args)) nil) (defmacro incompatible (rune1 rune2) ":Doc-Section Theories declaring that two rules should not both be ~il[enable]d~/ ~bv[] Example: (theory-invariant (incompatible (:rewrite left-to-right) (:rewrite right-to-left)))~/ General Form: (incompatible rune1 rune2) ~ev[] where ~c[rune1] and ~c[rune2] are two specific ~il[rune]s. The arguments are not evaluated. ~c[Invariant] is just a macro that expands into a term that checks that not both ~il[rune]s are enabled. ~l[theory-invariant].~/" (cond ((and (consp rune1) (consp (cdr rune1)) (symbolp (cadr rune1)) (consp rune2) (consp (cdr rune2)) (symbolp (cadr rune2))) ; The above condition is similar to conditions in runep and active-runep. `(not (and (active-runep ',rune1) (active-runep ',rune2)))) (t (er hard 'incompatible "Each argument to ~x0 should have the shape of a rune, ~ (:KEYWORD BASE-SYMBOL), unlike ~x1." 'incompatible (or (and (consp rune1) (consp (cdr rune1)) (symbolp (cadr rune1)) rune2) rune1))))) ; We now begin the development of the encapsulate event. Often in this ; development we refer to the Encapsulate Essay. See the comment in ; the function encapsulate-fn, below. (deflabel signature :doc ":Doc-Section Miscellaneous how to specify the arity of a constrained function~/ We start with a gentle introduction to signatures, where we pretend that there are no single-threaded objects (more on that below ~-[] for now, if you don't know anything about single-threaded objects, that's fine!). Here are some simple examples of signatures. ~bv[] ((hd *) => *) ((pair * *) => *) ((foo * *) => (mv * * *)) ~ev[] The first of these says that ~c[hd] is a function of one argument, while the other two say that ~c[pair] and ~c[foo] are functions that each take two arguments. The first two say that ~c[hd] and ~c[pair] return a single value. The third says that ~c[foo] returns three values, much as the following definition returns three values: ~bv[] (defun bar (x y) (mv y x (cons x y))) ~ev[] Corresponding ``old-style'' signatures are as follows. In each case, a function symbol is followed by a list of formal parameters and then either ~c[t], to denote a single value return, or ~c[(mv t t t)], to denote a multiple value return (in this case, returning three values). ~bv[] (hd (x) t) (pair (x y) t) (foo (x y) (mv t t t)) ~ev[] That concludes our gentle introduction. The documentation below is more general, for example covering single-threaded objects and keyword values such as ~c[:guard]. When reading what follows below, it is sufficient to know about single-threaded objects (or ``stobjs'') that each has a unique symbolic name and that ~ilc[state] is the name of the only built-in single-threaded object. All other stobjs are introduced by the user via ~ilc[defstobj] or ~ilc[defabsstobj]. An object that is not a single-threaded object is said to be ``ordinary.'' For a discussion of single-threaded objects, ~pl[stobj].~/ ~bv[] Examples: ((hd *) => *) ((hd *) => * :formals (x) :guard (consp x)) ((printer * state) => (mv * * state)) ((mach * mach-state * state) => (mv * mach-state)) General Form: ((fn ...) => *) ((fn ...) => stobj) or ((fn ...) => (mv ...)) or for part1 and part2 as above, (part1 => part2 :kwd1 val1 ... :kwdn valn) ~ev[] where ~c[fn] is the constrained function symbol, ~c[...] is a list of asterisks and/or the names of single-threaded objects, ~c[stobj] is a single-threaded object name, and the optional ~c[:kwdi] and ~c[:vali] are as described below. ACL2 also supports an older style of signature, described below after we describe the preferred style. Signatures specify three syntactic aspects of a function symbol: (1) the ``arity'' or how many arguments the function takes, (2) the ``multiplicity'' or how many results it returns via ~c[MV], and (3) which of those arguments and results are single-threaded objects and which objects they are. A signature typically has the form ~c[((fn x1 ... xn) => val)]. Such a signature has two parts, separated by the symbol ``=>''. The first part, ~c[(fn x1 ... xn)], is suggestive of a call of the constrained function. The number of ``arguments,'' ~c[n], indicates the arity of ~c[fn]. Each ~c[xi] must be a symbol. If a given ~c[xi] is the symbol ``*'' then the corresponding argument must be ordinary. If a given ~c[xi] is any other symbol, that symbol must be the name of a single-threaded object and the corresponding argument must be that object. No stobj name may occur twice among the ~c[xi]. The second part, ~c[val], of a signature is suggestive of a term and indicates the ``shape'' of the output of ~c[fn]. If ~c[val] is a symbol then it must be either the symbol ``*'' or the name of a single-threaded object. In either case, the multiplicity of ~c[fn] is 1 and ~c[val] indicates whether the result is ordinary or a stobj. Otherwise, ~c[val] is of the form ~c[(mv y1 ... yk)], where ~c[k] > 1. Each ~c[yi] must be either the symbol ``*'' or the name of a stobj. Such a ~c[val] indicates that ~c[fn] has multiplicity ~c[k] and the ~c[yi] indicate which results are ordinary and which are stobjs. No stobj name may occur twice among the ~c[yi], and a stobj name may appear in ~c[val] only if appears among the ~c[xi]. A signature may have the form ~c[((fn x1 ... xn) => val . k)], where ~c[k] is a ~ilc[keyword-value-listp], i.e., an alternating list of keywords and values starting with a keyword. In this case ~c[((fn x1 ... xn) => val)] must be a legal signature as described above. The legal keywords in ~c[k] are ~c[:GUARD] and ~c[:FORMALS] (except that for ACL2(r), also see the remark about ~c[:CLASSICALP] later in this topic). The value following ~c[:FORMALS] is to be the list of formal parameters of ~c[fn], while the value following ~c[:GUARD] is a term that is to be the ~il[guard] of ~c[fn]. Note that this guard is never actually evaluated, and is not subject to the guard verification performed on functions introduced by ~ilc[defun] (~pl[verify-guards]). Said differently: this guard need not itself have a guard of ~c[t]. Indeed, the guard is only used for attachments; ~pl[defattach]. Note that if ~c[:GUARD] is supplied then ~c[:FORMALS] must also be supplied (in order to related the variables occurring in the guard to the parameters of ~c[fn]). One final observation about guards: if the ~c[:GUARD] keyword is omitted, then the guard defaults to ~c[T]. Before ACL2 supported user-declared single-threaded objects there was only one single-threaded object: ACL2's built-in notion of ~ilc[state]. The notion of signature supported then gave a special role to the symbol ~c[state] and all other symbols were considered to denote ordinary objects. ACL2 still supports the old form of signature, but it is limited to functions that operate on ordinary objects or ordinary objects and ~c[state]. ~bv[] Old-Style General Form: (fn formals result . k) ~ev[] where ~c[fn] is the constrained function symbol, ~c[formals] is a suitable list of formal parameters for it, ~c[k] is an optional ~ilc[keyword-value-listp] (see below), and ~c[result] is either a symbol denoting that the function returns one result or else ~c[result] is an ~ilc[mv] expression, ~c[(mv s1 ... sn)], where ~c[n>1], each ~c[si] is a symbol, indicating that the function returns ~c[n] results. At most one of the formals may be the symbol ~c[STATE], indicating that corresponding argument must be ACL2's built-in ~ilc[state]. If ~c[state] appears in ~c[formals] then ~c[state] may appear once in ~c[result]. All ``variable symbols'' other than ~c[state] in old style signatures denote ordinary objects, regardless of whether the symbol has been defined to be a single-threaded object name! The optional ~c[k] is as described above for newer-style signatures, except that the user is also allowed to declare which symbols (besides ~c[state]) are to be considered single-threaded object names. Thus ~c[:STOBJS] is also a legal keyword. The form ~bv[] (fn formals result ... :stobjs names ...) ~ev[] specifies that ~c[names] is either the name of a single-threaded object or else is a list of such names. Every name in ~c[names] must have been previously defined as a stobj via ~ilc[defstobj] or ~ilc[defabsstobj]. As promised above, we conclude with a remark about an additional keyword, ~c[:CLASSICALP], that is legal for ACL2(r) (~pl[real]). The value of this keyword must be ~c[t] (the default) or ~c[nil], indicating respectively whether ~c[fn] is classical or not.~/") (defconst *generic-bad-signature-string* "The object ~x0 is not a legal signature. A basic signature is of one of ~ the following two forms: ((fn sym1 ... symn) => val) or (fn (var1 ... ~ varn) val). In either case, keywords may also be specified. See :DOC ~ signature.") (defconst *signature-keywords* '(:GUARD #+:non-standard-analysis :CLASSICALP :STOBJS :FORMALS)) (defun duplicate-key-in-keyword-value-listp (l) (declare (xargs :guard (keyword-value-listp l))) (cond ((endp l) nil) ((assoc-keyword (car l) (cddr l)) (car l)) (t (duplicate-key-in-keyword-value-listp (cddr l))))) (defun chk-signature (x ctx wrld state) ; Warning: If you change the acceptable form of signatures, change the raw lisp ; code for encapsulate in axioms.lisp and change signature-fns. ; X is supposed to be the external form of a signature of a function, fn. This ; function either causes an error (if x is ill-formed) or else returns (insig ; kwd-value-list . wrld1), where: insig is of the form (fn formals' stobjs-in ; stobjs-out), where formals' is an appropriate arglist, generated if ; necessary; kwd-value-list is the keyword-value-listp from the signature (see ; below); and wrld1 is the world in which we are to perform the constraint of ; fn. ; The preferred external form of a signature is of the form: ; ((fn . pretty-flags) => pretty-flag . kwd-value-list) ; ((fn . pretty-flags) => (mv . pretty-flags) . kwd-value-list) ; where fn is a new or redefinable name, pretty-flag is an asterisk or stobj ; name, pretty-flags is a true list of pretty flags, and kwd-value-list ; specifies additional information such as the guard and formals. (let ((bad-kwd-value-list-string "The object ~x0 is not a legal signature. It appears to specify ~x1 ~ as the keyword alist, which however is not syntactically a ~ keyword-value-listp because ~@2.")) (mv-let (msg fn formals val stobjs kwd-value-list) (case-match x (((fn . pretty-flags1) arrow val . kwd-value-list) (cond ((not (and (symbolp arrow) (equal (symbol-name arrow) "=>"))) (mv (msg *generic-bad-signature-string* x) nil nil nil nil nil)) ((not (and (symbol-listp pretty-flags1) (no-duplicatesp-equal (collect-non-x '* pretty-flags1)))) (mv (msg "The object ~x0 is not a legal signature because ~x1 is not ~ applied to a true-list of distinct symbols but to ~x2 instead." x fn pretty-flags1) nil nil nil nil nil)) ((not (or (symbolp val) (and (consp val) (eq (car val) 'mv) (symbol-listp (cdr val)) (no-duplicatesp-equal (collect-non-x '* (cdr val)))))) (mv (msg "The object ~x0 is not a legal signature because the result, ~ ... => ~x1, is not a symbol or an MV form containing distinct ~ symbols." x val) nil nil nil nil nil)) ((or (member-eq t pretty-flags1) (member-eq nil pretty-flags1) (eq val t) (eq val nil) (and (consp val) (or (member-eq t (cdr val)) (member-eq nil (cdr val))))) (mv (msg "The object ~x0 is not a legal signature because it mentions T ~ or NIL in places that must be filled by asterisks (*) or ~ single-threaded object names." x) nil nil nil nil nil)) ((not (subsetp-eq (collect-non-x '* (if (consp val) (cdr val) (list val))) pretty-flags1)) (mv (msg "The object ~x0 is not a legal signature because the result, ~ ~x1, refers to one or more single-threaded objects, ~&2, not ~ displayed among the inputs in ~x3." x val (set-difference-eq (if (consp val) (cdr val) (list val)) (cons '* pretty-flags1)) (cons fn pretty-flags1)) nil nil nil nil nil)) ((not (keyword-value-listp kwd-value-list)) (mv (msg bad-kwd-value-list-string x kwd-value-list (reason-for-non-keyword-value-listp kwd-value-list)) nil nil nil nil nil)) ((duplicate-key-in-keyword-value-listp kwd-value-list) (mv (msg "The object ~x0 is not a legal signature because the keyword ~ ~x1 appears more than once." x (duplicate-key-in-keyword-value-listp kwd-value-list)) nil nil nil nil nil)) ((assoc-keyword :STOBJS kwd-value-list) (mv (msg "The object ~x0 is not a legal signature. The :STOBJS ~ keyword is only legal for the older style of signature ~ (but may not be necessary for the newer style that you ~ are using); see :DOC signature." x) nil nil nil nil nil)) ((and (assoc-keyword :GUARD kwd-value-list) (not (assoc-keyword :FORMALS kwd-value-list))) (mv (msg "The object ~x0 is not a legal signature. The :GUARD ~ keyword is only legal for the newer style of signature ~ when the :FORMALS keyword is also supplied; see :DOC ~ signature." x) nil nil nil nil nil)) #+:non-standard-analysis ((not (booleanp (cadr (assoc-keyword :CLASSICALP ; If :CLASSICALP is not bound in kwd-value-list, then the above test reduces to ; (not (booleanp nil)), which is false, which is appropropriate. kwd-value-list)))) (mv (msg "The object ~x0 is not a legal signature. The value of ~ :CLASSICALP keyword must be Boolean; see :DOC signature." x) nil nil nil nil nil)) (t (let* ((formals-tail (assoc-keyword :FORMALS kwd-value-list)) (formals (if formals-tail (cadr formals-tail) (gen-formals-from-pretty-flags pretty-flags1))) (kwd-value-list (if formals-tail (remove-keyword :FORMALS kwd-value-list) kwd-value-list)) ; Note: Stobjs will contain duplicates iff formals does. Stobjs will ; contain STATE iff formals does. (stobjs (collect-non-x '* pretty-flags1))) (mv nil fn formals val stobjs kwd-value-list))))) ((fn formals val . kwd-value-list) (cond ((not (true-listp formals)) (mv (msg "The object ~x0 is not a legal signature because its second ~ element, representing the formals, is not a true-list." x) nil nil nil nil nil)) ((not (keyword-value-listp kwd-value-list)) (mv (msg bad-kwd-value-list-string x kwd-value-list (reason-for-non-keyword-value-listp kwd-value-list)) nil nil nil nil nil)) ((duplicate-key-in-keyword-value-listp kwd-value-list) (mv (msg "The object ~x0 is not a legal signature because the keyword ~ ~x1 appears more than once." x (duplicate-key-in-keyword-value-listp kwd-value-list)) nil nil nil nil nil)) ((assoc-keyword :FORMALS kwd-value-list) (mv (msg "The object ~x0 is not a legal signature. The :FORMALS ~ keyword is only legal for the newer style of signature; ~ see :DOC signature." x) nil nil nil nil nil)) #+:non-standard-analysis ((not (booleanp (cadr (assoc-keyword :CLASSICALP ; See comment above about :CLASSICALP. kwd-value-list)))) (mv (msg "The object ~x0 is not a legal signature. The value of ~ :CLASSICALP keyword must be Boolean; see :DOC signature." x) nil nil nil nil nil)) (t (let* ((stobjs-tail (assoc-keyword :STOBJS kwd-value-list)) (kwd-value-list (if stobjs-tail (remove-keyword :STOBJS kwd-value-list) kwd-value-list))) (cond ((not stobjs-tail) (let ((stobjs (if (member-eq 'state formals) '(state) nil))) (mv nil fn formals val stobjs kwd-value-list))) ((or (symbolp (cadr stobjs-tail)) (symbol-listp (cadr stobjs-tail))) (let* ((stobjs0 (if (symbolp (cadr stobjs-tail)) (list (cadr stobjs-tail)) (cadr stobjs-tail))) (stobjs (if (and (member-eq 'state formals) (not (member-eq 'state stobjs0))) (cons 'state stobjs0) stobjs0))) (mv nil fn formals val stobjs kwd-value-list))) (t (mv (msg "The object ~x0 is not a legal signature because ~ the proffered stobj names are ill-formed. The ~ stobj names are expected to be either a single ~ symbol or a true list of symbols." x) nil nil nil nil nil))))))) (& (mv (msg *generic-bad-signature-string* x) nil nil nil nil nil))) (cond (msg (er soft ctx "~@0" msg)) ((not (subsetp-eq (evens kwd-value-list) *signature-keywords*)) (er soft ctx "The only legal signature keywords are ~&0. The proposed ~ signature ~x1 is thus illegal." *signature-keywords* x)) (t (er-progn (chk-all-but-new-name fn ctx 'constrained-function wrld state) (chk-arglist formals (not (member-eq 'state stobjs)) ctx wrld state) (chk-all-stobj-names stobjs (msg "~x0" x) ctx wrld state) (cond ((not (or (symbolp val) (and (consp val) (eq (car val) 'mv) (symbol-listp (cdr val)) (> (length val) 2)))) (er soft ctx "The purported signature ~x0 is not a legal signature ~ because ~x1 is not a legal output description. Such a ~ description should either be a symbol or of the form (mv ~ sym1 ... symn), where n>=2." x val)) (t (value nil))) (let* ((syms (cond ((symbolp val) (list val)) (t (cdr val)))) (stobjs-in (compute-stobj-flags formals stobjs wrld)) (stobjs-out (compute-stobj-flags syms stobjs wrld))) (cond ((not (subsetp (collect-non-x nil stobjs-out) (collect-non-x nil stobjs-in))) (er soft ctx "It is impossible to return single-threaded objects (such as ~ ~&0) that are not among the formals! Thus, the input ~ signature ~x1 and the output signature ~x2 are incompatible." (set-difference-eq (collect-non-x nil stobjs-out) (collect-non-x nil stobjs-in)) formals val)) ((not (no-duplicatesp (collect-non-x nil stobjs-out))) (er soft ctx "It is illegal to return the same single-threaded object in ~ more than one position of the output signature. Thus, ~x0 ~ is illegal because ~&1 ~#1~[is~/are~] duplicated." val (duplicates (collect-non-x nil stobjs-out)))) (t (er-let* ((wrld1 (chk-just-new-name fn (list* 'function stobjs-in stobjs-out) nil ctx wrld state))) (value (list* (list fn formals stobjs-in stobjs-out) kwd-value-list wrld1)))))))))))) (defun chk-signatures (signatures ctx wrld state) ; We return a triple (sigs kwd-value-list-lst . wrld) containing the list of ; internal signatures, their corresponding keyword-value-lists, and the final ; world in which we are to do the introduction of these fns, or else cause an ; error. (cond ((atom signatures) (cond ((null signatures) (value (list* nil nil wrld))) (t (er soft ctx "The list of the signatures of the functions ~ constrained by an encapsulation is supposed to ~ be a true list, but yours ends in ~x0. See ~ :DOC encapsulate." signatures)))) ((and (consp (cdr signatures)) (symbolp (cadr signatures)) (equal (symbol-name (cadr signatures)) "=>")) ; This clause is meant as an optimization helpful to the user. It is ; an optimization because if we didn't have it here we would proceed ; to apply chk-signature first the (car signatures) -- which will ; probably fail -- and then to '=> -- which would certainly fail. ; These error messages are less understandable than the one we ; generate here. (er soft ctx "The signatures argument of ENCAPSULATE is supposed to ~ be a list of signatures. But you have provided ~x0, ~ which might be a single signature. Try writing ~x1." signatures (list signatures))) (t (er-let* ((trip1 (chk-signature (car signatures) ctx wrld state)) (trip2 (chk-signatures (cdr signatures) ctx (cddr trip1) state))) (let ((insig (car trip1)) (kwd-value-list (cadr trip1)) (insig-lst (car trip2)) (kwd-value-list-lst (cadr trip2)) (wrld1 (cddr trip2))) (cond ((assoc-eq (car insig) insig-lst) (er soft ctx "The name ~x0 is mentioned twice in the ~ signatures of this encapsulation. See :DOC ~ encapsulate." (car insig))) (t (value (list* (cons insig insig-lst) (cons kwd-value-list kwd-value-list-lst) wrld1))))))))) (defun chk-acceptable-encapsulate1 (signatures form-lst ctx wrld state) ; This function checks that form-lst is a plausible list of forms to evaluate ; and that signatures parses into a list of function signatures for new ; function symbols. We return the internal signatures, corresponding keyword ; alists, and the world in which they are to be introduced, as a triple (insigs ; kwd-alist-lst . wrld1). This function is executed before the first pass of ; encapsulate. (er-progn (cond ((not (and (true-listp form-lst) (consp form-lst) (consp (car form-lst)))) ; Observe that if the car is not a consp then it couldn't possibly be an ; event. We check this particular case because we fear the user might get ; confused and write an explicit (progn expr1 ... exprn) or some other ; single expression and this will catch all but the open lambda case. (er soft ctx "The arguments to encapsulate, after the first, are ~ each supposed to be embedded event forms. There must ~ be at least one form. See :DOC encapsulate and :DOC ~ embedded-event-form.")) (t (value nil))) (chk-signatures signatures ctx wrld state))) ; The following is a complete list of the macros that are considered ; "primitive event macros". This list includes every macro that calls ; install-event except for defpkg, which is omitted as ; explained below. In addition, the list includes defun (which is ; just a special call of defuns). Every name on this list has the ; property that while it takes state as an argument and possibly ; changes it, the world it produces is a function only of the world in ; the incoming state and the other arguments. The function does not ; change the world as a function of, say, some global variable in the ; state. ; The claim above, about changing the world, is inaccurate for include-book! ; It changes the world as a function of the contents of some arbitrarily ; named input object file. How this can be explained, I'm not sure. ; All event functions have the property that they install into state ; the world they produce, when they return non-erroneously. More ; subtly they have the property that when the cause an error, they do ; not change the installed world. For simple events, such as DEFUN ; and DEFTHM, this is ensured by not installing any world until the ; final STOP-EVENT. But for compound events, such as ENCAPSULATE and ; INCLUDE-BOOK, it is ensured by the more expensive use of ; REVERT-WORLD-ON-ERROR. (defun primitive-event-macros () (declare (xargs :guard t :mode :logic)) ; Warning: If you add to this list, consider adding to ; find-first-non-local-name and to the list in translate11 associated with a ; comment about primitive-event-macros. ; Warning: Keep this in sync with oneify-cltl-code (see comment there about ; primitive-event-macros). ; Warning: See the warnings below! ; Note: This zero-ary function used to be a constant, *primitive-event-macros*. ; But Peter Dillinger wanted to be able to change this value with ttags, so ; this function has replaced that constant. We keep the lines sorted below, ; but only for convenience. '( #+:non-standard-analysis defthm-std #+:non-standard-analysis defun-std add-custom-keyword-hint add-default-hints! add-include-book-dir add-ld-keyword-alias! add-match-free-override comp defabsstobj defattach defaxiom defchoose defconst defdoc deflabel defmacro ; defpkg ; We prohibit defpkgs except in very special places. See below. defstobj deftheory defthm defttag defun defuns delete-include-book-dir encapsulate in-arithmetic-theory in-theory include-book logic mutual-recursion progn progn! program push-untouchable regenerate-tau-database remove-default-hints! remove-untouchable reset-prehistory set-backchain-limit set-body set-bogus-defun-hints-ok set-bogus-mutual-recursion-ok set-case-split-limitations set-compile-fns set-default-backchain-limit set-default-hints! set-enforce-redundancy set-ignore-doc-string-error set-ignore-ok set-inhibit-warnings! set-invisible-fns-table set-irrelevant-formals-ok set-let*-abstractionp set-match-free-default set-measure-function set-non-linearp set-nu-rewriter-mode set-override-hints-macro set-prover-step-limit set-rewrite-stack-limit set-ruler-extenders set-rw-cache-state! set-state-ok set-tau-auto-mode set-verify-guards-eagerness set-well-founded-relation table theory-invariant value-triple verify-guards verify-termination-boot-strap )) ; Warning: If a symbol is on this list then it is allowed into books. ; If it is allowed into books, it will be compiled. Thus, if you add a ; symbol to this list you must consider how compile will behave on it ; and what will happen when the .o file is loaded. Most of the symbols ; on this list have #-acl2-loop-only definitions that make them ; no-ops. At least one, defstub, expands into a perfectly suitable ; form involving the others and hence inherits its expansion's ; semantics for the compiler. ; Warning: If this list is changed, inspect the following definitions, ; down through CHK-EMBEDDED-EVENT-FORM. Also consider modifying the ; list *fmt-ctx-spacers* as well. ; We define later the notion of an embedded event. Only such events ; can be included in the body of an ENCAPSULATE or a file named by ; INCLUDE-BOOK. ; We do not allow defpkg as an embedded event. In fact, we do not allow ; defpkg anywhere in a blessed set of files except in files that contain ; nothing but top-level defpkg forms (and those files must not be compiled). ; The reason is explained in deflabel embedded-event-form below. ; Once upon a time we allowed in-package expressions inside of ; encapsulates, in a "second class" way. That is, they were not ; allowed to be hidden in LOCAL forms. But the whole idea of putting ; in-package expressions in encapsulated event lists is silly: ; In-package is meant to change the package into which subsequent ; forms are read. But no reading is being done by encapsulate and the ; entire encapsulate event list is read into whatever was the current ; package when the encapsulate was read. ; Here is an example of why in-package should never be hidden (i.e., ; in LOCAL), even in a top-level list of events in a file. ; Consider the following list of events: ; (DEFPKG ACL2-MY-PACKAGE '(DEFTHM SYMBOL-PACKAGE-NAME EQUAL)) ; (LOCAL (IN-PACKAGE "ACL2-MY-PACKAGE")) ; (DEFTHM GOTCHA (EQUAL (SYMBOL-PACKAGE-NAME 'IF) "ACL2-MY-PACKAGE")) ; When processed in pass 1, the IN-PACKAGE is executed and thus ; the subsequent form (and hence the symbol 'IF) is read into package ; ACL2-MY-PACKAGE. Thus, the equality evaluates to T and GOTCHA is a ; theorem. But when processed in pass 2, the IN-PACKAGE is not ; executed and the subsequent form is read into the "ACL2" package. The ; equality evaluates to NIL and GOTCHA is not a theorem. (deflabel embedded-event-form :doc ":Doc-Section Miscellaneous forms that may be embedded in other ~il[events]~/ ~bv[] Examples: (defun hd (x) (if (consp x) (car x) 0)) (local (defthm lemma23 ...)) (progn (defun fn1 ...) (local (defun fn2 ...)) ...)~/ General Form: An embedded event form is a term, x, such that:~ev[]~bq[] o ~c[x] is a call of an event function other than ~ilc[DEFPKG] (~pl[events] for a listing of the event functions); o ~c[x] is of the form ~c[(]~ilc[LOCAL]~c[ x1)] where ~c[x1] is an embedded event form; o ~c[x] is of the form ~c[(]~ilc[SKIP-PROOFS]~c[ x1)] where ~c[x1] is an embedded event form; o ~c[x] is of the form ~c[(]~ilc[MAKE-EVENT]~c[ &)], where ~c[&] is any term whose expansion is an embedded event (~pl[make-event]); o ~c[x] is of the form ~c[(]~ilc[WITH-OUTPUT]~c[ ... x1)], ~c[(]~ilc[WITH-PROVER-STEP-LIMIT]~c[ ... x1 ...)], or ~c[(]~ilc[WITH-PROVER-TIME-LIMIT]~c[ ... x1)], where ~c[x1] is an embedded event form; o ~c[x] is a call of ~ilc[ENCAPSULATE], ~ilc[PROGN], ~ilc[PROGN!], or ~ilc[INCLUDE-BOOK]; o ~c[x] macroexpands to one of the forms above; or o [intended only for the implementation] ~c[x] is ~c[(RECORD-EXPANSION x1 x2)], where ~c[x1] and ~c[x2] are embedded event forms. ~eq[] An exception: an embedded event form may not set the ~ilc[acl2-defaults-table] when in the context of ~ilc[local]. Thus for example, the form ~bv[] (local (table acl2-defaults-table :defun-mode :program)) ~ev[] is not an embedded event form, nor is the form ~c[(local (program))], since the latter sets the ~ilc[acl2-defaults-table] implicitly. An example at the end of the discussion below illustrates why there is this restriction. Only embedded event forms are allowed in a book after its initial ~ilc[in-package] form. ~l[books]. However, you may find that ~ilc[make-event] allows you to get the effect you want for a form that is not an embedded event form. For example, you can put the following into a book, which assigns the value 17 to ~ilc[state] global variable ~c[x]: ~bv[] (make-event (er-progn (assign x 17) (value '(value-triple nil))) :check-expansion t) ~ev[] When an embedded event is executed while ~ilc[ld-skip-proofsp] is ~c[']~ilc[include-book], those parts of it inside ~ilc[local] forms are ignored. Thus, ~bv[] (progn (defun f1 () 1) (local (defun f2 () 2)) (defun f3 () 3)) ~ev[] will define ~c[f1], ~c[f2], and ~c[f3] when ~ilc[ld-skip-proofsp] is ~c[nil] or ~c[t], but will define only ~c[f1] and ~c[f3] when ~ilc[ld-skip-proofsp] is ~c[']~ilc[include-book]. ~em[Discussion:] ~ilc[Encapsulate], ~ilc[progn], and ~ilc[include-book] place restrictions on the kinds of forms that may be processed. These restrictions ensure that the non-local ~il[events] are indeed admissible provided that the sequence of ~ilc[local] and non-local ~il[events] is admissible when proofs are done, i.e., when ~c[ld-skip-proofs] is ~c[nil]. But ~ilc[progn!] places no such restrictions, hence is potentially dangerous and should be avoided unless you understand the ramifications; so it is illegal unless there is an active trust tag (~pl[defttag]). ~ilc[Local] permits the hiding of an event or group of ~il[events] in the sense that ~ilc[local] ~il[events] are processed when we are trying to establish the admissibility of a sequence of ~il[events] embedded in ~ilc[encapsulate] forms or in ~il[books], but are ignored when we are constructing the ~il[world] produced by assuming that sequence. Thus, for example, a particularly ugly and inefficient ~c[:]~ilc[rewrite] rule might be made ~ilc[local] to an ~il[encapsulate] that ``exports'' a desirable theorem whose proof requires the ugly lemma. To see why we can't allow just anything as an embedded event, consider allowing the form ~bv[] (if (ld-skip-proofsp state) (defun foo () 2) (defun foo () 1)) ~ev[] followed by ~bv[] (defthm foo-is-1 (equal (foo) 1)). ~ev[] When we process the ~il[events] with ~ilc[ld-skip-proofsp] is ~c[nil], the second ~ilc[defun] is executed and the ~ilc[defthm] succeeds. But when we process the ~il[events] with ~ilc[ld-skip-proofsp] ~c[']~ilc[include-book], the second ~ilc[defun] is executed, so that ~c[foo] no longer has the same definition it did when we proved ~c[foo-is-1]. Thus, an invalid formula is assumed when we process the ~ilc[defthm] while skipping proofs. Thus, the first form above is not a legal embedded event form. If you encounter a situation where these restrictions seem to prevent you from doing what you want to do, then you may find ~c[make-event] to be helpful. ~l[make-event]. ~ilc[Defpkg] is not allowed because it affects how things are read after it is executed. But all the forms embedded in an event are read before any are executed. That is, ~bv[] (encapsulate nil (defpkg \"MY-PKG\" nil) (defun foo () 'my-pkg::bar)) ~ev[] makes no sense since ~c[my-pkg::bar] must have been read before the ~ilc[defpkg] for ~c[\"MY-PKG\"] was executed. Finally, let us elaborate on the restriction mentioned earlier related to the ~ilc[acl2-defaults-table]. Consider the following form. ~bv[] (encapsulate () (local (program)) (defun foo (x) (if (equal 0 x) 0 (1+ (foo (- x)))))) ~ev[] ~l[local-incompatibility] for a discussion of how ~ilc[encapsulate] processes event forms. Briefly, on the first pass through the ~il[events] the definition of ~c[foo] will be accepted in ~ilc[defun] mode ~c[:]~ilc[program], and hence accepted. But on the second pass the form ~c[(local (program))] is skipped because it is marked as ~ilc[local], and hence ~c[foo] is accepted in ~ilc[defun] mode ~c[:]~ilc[logic]. Yet, no proof has been performed in order to admit ~c[foo], and in fact, it is not hard to prove a contradiction from this definition!~/") ; One can imagine adding new event forms. The requirement is that ; either they not take state as an argument or else they not be ; sensitive to any part of state except the current ACL2 world. (defun name-introduced (trip functionp) ; Trip is a triple from a world alist. We seek to determine whether ; this triple introduces a new name, and if so, which name. We return ; the name or nil. If functionp is T we only return function names. ; That is, we return nil if the name introduced is not the name of a ; function, e.g., is a theorem or constant. Otherwise, we return any ; logical name introduced. The event functions are listed below. ; Beside each is listed the triple that we take as the unique ; indication that that event introduced name. Only those having ; FORMALS are considered to be function names. ; event function identifying triple ; defun-fn (name FORMALS . &) ; defuns-fn (name FORMALS . &) ; defthm-fn (name THEOREM . &) ; defaxiom-fn (name THEOREM . &) ; defconst-fn (name CONST . &) ; defstobj-fn (name STOBJ . names) ; [Name is a single-threaded ; object, e.g., $st, and has the ; associated recognizers, accessors ; and updaters. But those names are ; considered introduced by their ; associated FORMALS triples.] ; defabsstobj-fn (name STOBJ . names) [as above for defstobj-fn] ; deflabel-fn (name LABEL . T) ; defdoc-fn --- ; deftheory-fn (name THEORY . &) ; defchoose-fn (name FORMALS . &) ; verify-guards-fn --- ; defmacro-fn (name MACRO-BODY . &) ; in-theory-fn --- ; in-arithmetic-theory-fn --- ; regenerate-tau-database --- ; push-untouchable-fn --- ; remove-untouchable-fn --- ; reset-prehistory --- ; set-body-fn --- ; table-fn --- ; encapsulate-fn --- [However, the signature functions ; are introduced with (name FORMALS . &) ; and those names, along with any others ; introduced by the embedded events, are ; returned.] ; include-book-fn (CERTIFICATION-TUPLE GLOBAL-VALUE ; ("name" "user name" "short name" ; cert-annotations . chk-sum)) ; Those marked "---" introduce no names. ; If redefinition has occurred we have to avoid being fooled by trips such ; as (name FORMALS . *acl2-property-unbound*) and ; (name THEOREM . *acl2-property-unbound*). (cond ((eq (cddr trip) *acl2-property-unbound*) nil) ((eq (cadr trip) 'formals) (car trip)) (functionp nil) ((member-eq (cadr trip) '(theorem const macro-body label theory stobj)) (car trip)) ((and (eq (car trip) 'certification-tuple) (eq (cadr trip) 'global-value) (cddr trip)) ; The initial value of 'certification-tuple is nil (see initialize- ; world-globals) so we filter it out. Observe that name is a string ; here. This name is not the name that occurs in the include-book ; event -- that name is called "user name" in the identifying triple ; column above -- but is in fact the full name of the book, complete ; with the current-book-directory. (car (cddr trip))) (t nil))) (defun chk-embedded-event-form-orig-form-msg (orig-form state) (cond (orig-form (msg " Note: the above form was encountered during processing of ~X01." orig-form (term-evisc-tuple t state))) (t ""))) (defun chk-embedded-event-form (form orig-form wrld ctx state names portcullisp in-local-flg in-encapsulatep make-event-chk) ; WARNING: Keep this in sync with destructure-expansion, elide-locals-rec, ; make-include-books-absolute, and find-first-non-local-name. ; Note: For a test of this function, see the reference to foo.lisp below. ; Orig-form is used for error reporting. It is either nil, indicating that ; errors should refer to form, or else it is a form from a superior call of ; this function. So it is typical, though not required, to call this with ; orig-form = nil at the top level. If we encounter a macro call and orig-form ; is nil, then we set orig-form to the macro call so that the user can see that ; macro call if the check fails. ; This function checks that form is a tree whose tips are calls of the symbols ; listed in names, and whose interior nodes are each of one of the following ; forms. ; (local &) ; (skip-proofs &) ; (with-output ... &) ; (with-prover-step-limit ... &) ; (with-prover-time-limit ... &) ; (make-event #) ; where each & is checked. The # forms above are unrestricted, although the ; result of expanding the argument of make-event (by evaluation) is checked. ; Note that both 'encapsulate and 'progn are typically in names, and their ; sub-events aren't checked by this function until evaluation time. ; In addition, if portcullisp is t we are checking that the forms are ; acceptable as the portcullis of some book and we enforce the additional ; restriction noted below. ; (local &) is illegal because such a command would be skipped ; when executing the portcullis during the subsequent include-book. ; Formerly we also checked here that include-book is only applied to absolute ; pathnames. That was important for insuring that the book that has been read ; into the certification world is not dependent upon :cbd. Remember that ; (include-book "file") will find its way into the portcullis of the book we ; are certifying and there is no way of knowing in the portcullis which ; directory that book comes from if it doesn't explicitly say. However, we now ; use fix-portcullis-cmds to modify include-book forms that use relative ; pathnames so that they use absolute pathnames instead, or cause an error ; trying. ; We allow defaxioms, skip-proofs, and defttags in the portcullis, but we mark ; the book's certificate appropriately. ; In-local-flg is used to enforce restrictions in the context of LOCAL on the ; use of (table acl2-defaults-table ...), either directly or by way of events ; such as defun-mode events and set-compile-fns that set this table. (We used ; to make these restrictions when portcullisp is t, because we restored the ; initial acl2-defaults-table before certification, and hence it was misguided ; for the user to be setting the defun-mode or the compile flag in the ; certification world since they were irrelevant to the world in which the ; certification is done.) A non-nil value of in-local-flg means that we are in ; the scope of LOCAL. In that case, if we are lexically within an encapsulate ; but not LOCAL when restricted to the nearest such encapsulate, then ; in-local-flg is 'local-encapsulate. Otherwise, if we are in the scope of ; LOCAL, but we are in an included book and not in the scope of LOCAL with ; respect to that book, then in-local-flg is 'local-include-book. ; Moreover, we do not allow local defaxiom events. Imagine locally including a ; book that has nil as a defaxiom. You can prove anything you want in your ; book, and then when you later include the book, there will be no trace of the ; defaxiom in your logical world! ; We do not check that the tips are well-formed calls of the named functions ; (though we do ensure that they are all true lists). ; If names is primitive-event-macros and form can be translated and evaluated ; without error, then it is in fact an embedded event form as described in :DOC ; embedded-event-form. ; We sometimes call this function with names extended by the addition of ; 'DEFPKG. ; If form is rejected, the error message is that printed by str, with #\0 bound ; to the subform (of form) that was rejected. ; We return a value triple (mv erp val state). If erp is nil then val is the ; event form to be evaluated. Generally that is the result of macroexpanding ; the input form. However, if (perhaps after some macroexpansion) form is a ; call of local that should be skipped, then val is nil. (let* ((er-str ; Below, the additional er arguments are as follows: ; ~@1: a reason specific to the context, or "" if none is called for. ; ~@2: original form message. ; ~@3: additional explanation, or "". (if portcullisp "The command ~x0, used in the construction of the current ~ world, cannot be included in the portcullis of a certified ~ book~@1. See :DOC portcullis.~@2~@3" "The form ~x0 is not an embedded event form~@1. See :DOC ~ embedded-event-form.~@2~@3")) (local-str "The form ~x0 is not an embedded event form in the ~ context of LOCAL~@1. See :DOC embedded-event-form.~@2~@3") (encap-str "The form ~x0 is not an embedded event form in the ~ context of ENCAPSULATE~@1. See :DOC ~ embedded-event-form.~@2~@3")) (cond ((or (atom form) (not (symbolp (car form))) (not (true-listp (cdr form)))) (er soft ctx er-str form "" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((and (eq (car form) 'local) (consp (cdr form)) (null (cddr form))) (cond (portcullisp ; We will miss this case if we have an ill-formed call of local: ; (not (and (consp (cdr form)) (null (cddr form)))). However, macroexpansion ; of local will fail later, so that isn't a problem. (er soft ctx er-str form " because LOCAL commands are not executed by include-book" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((eq (ld-skip-proofsp state) 'include-book) ; Keep this in sync with the definition of the macro local; if we evaluate the ; cadr of the form there, then we need to check it here. (value nil)) (t (er-let* ((new-form (chk-embedded-event-form (cadr form) orig-form wrld ctx state names portcullisp t in-encapsulatep make-event-chk))) (value (and new-form (list (car form) new-form))))))) ((and (eq in-local-flg t) (consp form) (eq (car form) 'table) (consp (cdr form)) (eq (cadr form) 'acl2-defaults-table)) (er soft ctx local-str form " because it sets the acl2-defaults-table in a local context. ~ A local context is not useful when setting this table, since ~ the acl2-defaults-table is restored upon completion of ~ encapsulate, include-book, and certify-book forms; that is, ~ no changes to the acl2-defaults-table are exported" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((and (eq in-local-flg t) (consp form) (member-eq (car form) '(add-custom-keyword-hint add-include-book-dir add-match-free-override defttag delete-include-book-dir logic program set-backchain-limit set-bogus-defun-hints-ok set-bogus-mutual-recursion-ok set-case-split-limitations set-compile-fns set-default-backchain-limit set-enforce-redundancy set-ignore-doc-string-error set-ignore-ok set-irrelevant-formals-ok set-let*-abstractionp set-match-free-default set-measure-function set-non-linearp set-nu-rewriter-mode set-prover-step-limit set-rewrite-stack-limit set-ruler-extenders set-state-ok set-tau-auto-mode set-verify-guards-eagerness set-well-founded-relation))) (er soft ctx local-str form " because it implicitly sets the acl2-defaults-table in a ~ local context. A local context is not useful when setting ~ this table, since the acl2-defaults-table is restored upon ~ completion of encapsulate, include-book, and certify-book ~ forms; that is, no changes to the acl2-defaults-table are ~ exported" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((and in-local-flg (eq (car form) 'defaxiom)) (er soft ctx local-str form " because it adds an axiom whose traces will disappear" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((and in-encapsulatep (eq (car form) 'defaxiom)) (er soft ctx encap-str form " because we do not permit defaxiom events in the scope of an ~ encapsulate" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((and (eq (car form) 'include-book) in-encapsulatep (or (eq in-local-flg nil) (eq in-local-flg 'local-encapsulate))) ; Through Version_4.2, the error message below added: "We fear that such forms ; will generate unduly large constraints that will impede the successful use of ; :functional-instance lemma instances." However, this message was printed ; even for encapsulates with empty signatures. ; It is probably sound in principle to lift this restriction, but in that case ; case we will need to visit all parts of the code which could be based on the ; assumption that include-book forms are always local to encapsulate events. ; See for example the comment about encapsulate in make-include-books-absolute; ; the paragraph labeled (2) in the Essay on Hidden Packages (file axioms.lisp); ; and the comment about "all include-books are local" near the end of ; encapsulate-fn. By no means do we claim that these examples are exhaustive! ; Even if we decide to loosen this restriction, we might want to leave it in ; place for encapsulates with non-empty signatures, for the reason explained in ; the "We fear" quote above. (er soft ctx encap-str form " because we do not permit non-local include-book forms in the ~ scope of an encapsulate. Consider moving your include-book ~ form outside the encapsulates, or else making it local" (chk-embedded-event-form-orig-form-msg orig-form state) "")) ((member-eq (car form) names) ; Names is often primitive-event-macros or an extension, and hence ; contains encapsulate and include-book. This is quite reasonable, ; since they do their own checking. And because they restore the ; acl2-defaults-table when they complete, we don't have to worry that ; they are sneaking in a ``local defun-mode.'' (value form)) ((and (eq (car form) 'skip-proofs) (consp (cdr form)) (null (cddr form))) (pprogn (cond ((global-val 'embedded-event-lst wrld) (warning$ ctx "Skip-proofs" "ACL2 has encountered a SKIP-PROOFS form, ~x0, ~ in the context of a book or an encapsulate ~ event. Therefore, no logical claims may be ~ soundly made in this context. See :DOC ~ SKIP-PROOFS." form)) (t state)) (er-let* ((new-form (chk-embedded-event-form (cadr form) orig-form wrld ctx state names portcullisp in-local-flg in-encapsulatep make-event-chk))) (value (and new-form (list (car form) new-form)))))) ((and (member-eq (car form) '(with-output with-prover-step-limit with-prover-time-limit)) (true-listp form)) ; The macro being called will check the details of the form structure. (er-let* ((new-form (chk-embedded-event-form (car (last form)) orig-form wrld ctx state names portcullisp in-local-flg in-encapsulatep make-event-chk))) (value (and new-form (append (butlast form 1) (list new-form)))))) ((eq (car form) 'make-event) (cond ((and make-event-chk (not (and (true-listp form) (or (consp (cadr (member-eq :check-expansion form))) (consp (cadr (member-eq :expansion? form))))))) (er soft ctx "Either the :check-expansion or :expansion? argument of ~ make-event should be a consp in the present context. ~ Unless you called record-expansion explicitly, this is ~ an ACL2 bug; please contact the ACL2 implementors. ~ Current form:~|~%~X01" form nil)) (t (value form)))) ((eq (car form) 'record-expansion) ; a macro that we handle specially (cond ((not (and (cdr form) (cddr form) (null (cdddr form)))) (er soft ctx "The macro ~x0 takes two arguments, so ~x1 is illegal." 'record-expansion form)) (t (er-progn (chk-embedded-event-form (cadr form) nil wrld ctx state names portcullisp in-local-flg in-encapsulatep nil) (chk-embedded-event-form (caddr form) (or orig-form form) wrld ctx state names portcullisp in-local-flg in-encapsulatep t))))) ((getprop (car form) 'macro-body nil 'current-acl2-world wrld) (cond ((member-eq (car form) (global-val 'untouchable-fns wrld)) (er soft ctx er-str form "" (chk-embedded-event-form-orig-form-msg orig-form state) (msg "~|The macro ~x0 may not be used to generate an event, ~ because it has been placed on untouchable-fns. See ~ :DOC push-untouchable." (car form)))) ((member-eq (car form) '(mv mv-let translate-and-test with-local-stobj)) (er soft ctx er-str form "" (chk-embedded-event-form-orig-form-msg orig-form state) (msg "~|Calls of the macro ~x0 do not generate an event, ~ because this macro has special meaning that is not ~ handled by ACL2's event-generation mechanism. Please ~ contact the implementors if this seems to be a ~ hardship." (car form)))) (t (er-let* ((expansion (macroexpand1 form ctx state))) (chk-embedded-event-form expansion (or orig-form form) wrld ctx state names portcullisp in-local-flg in-encapsulatep make-event-chk))))) (t (er soft ctx er-str form "" (chk-embedded-event-form-orig-form-msg orig-form state) ""))))) ; We have had a great deal of trouble correctly detecting embedded defaxioms! ; Tests for this have been incorporated into community book ; books/make-event/embedded-defaxioms.lisp. (defun destructure-expansion (form) ; WARNING: Keep this in sync with chk-embedded-event-form and elide-locals-rec. (declare (xargs :guard (true-listp form))) (cond ((member-eq (car form) '(local skip-proofs with-output with-prover-step-limit with-prover-time-limit)) (mv-let (wrappers base-form) (destructure-expansion (car (last form))) (mv (cons (butlast form 1) wrappers) base-form))) (t (mv nil form)))) (defun rebuild-expansion (wrappers form) (cond ((endp wrappers) form) (t (append (car wrappers) (list (rebuild-expansion (cdr wrappers) form)))))) (defun set-raw-mode-on (state) (pprogn (cond ((raw-mode-p state) state) (t (f-put-global 'acl2-raw-mode-p t state))) (value :invisible))) (defun set-raw-mode-off (state) (pprogn (cond ((raw-mode-p state) (f-put-global 'acl2-raw-mode-p nil state)) (t state)) (value :invisible))) (defmacro set-raw-mode-on! () ":Doc-Section switches-parameters-and-modes enter ``raw mode,'' a raw Lisp environment~/ This is the same as ~c[(]~ilc[set-raw-mode]~c[ t)] except that it first introduces a so-called ``trust tag'' (``ttag'') so that ~c[set-raw-mode] will be legal. ~l[defttag] for a discussion of ttags and how they affect ~ilc[certify-book] and ~ilc[include-book].~/ ~l[set-raw-mode] for a discussion of raw-mode.~/" '(er-progn (ld '((defttag :raw-mode-hack) (set-raw-mode-on state)) :ld-prompt nil :ld-verbose nil :ld-post-eval-print nil) (value :invisible))) (defmacro set-raw-mode (flg) (declare (xargs :guard (member-equal flg '(t 't nil 'nil)))) ":Doc-Section switches-parameters-and-modes enter or exit ``raw mode,'' a raw Lisp environment~/ Below we discuss raw-mode. In brief: The simplest way to turn raw-mode on is ~c[:SET-RAW-MODE-ON!], and to turn it off, ~c[:SET-RAW-MODE NIL]. Also ~pl[set-raw-mode-on!].~/ ACL2 users often find its careful syntax checking to be helpful during code development. Sometimes it is even useful to do code development in ~c[:]~ilc[logic] mode, where ACL2 can be used to check termination of (mutually) recursive functions, verify guards, or even prove properties of the functions. However, loading code using ~ilc[include-book] is much slower than using Common Lisp ~c[load] in raw Lisp, and in this sense ACL2 can get in the way of efficient execution. Unfortunately, it is error-prone to use ACL2 sources (or their compilations) in raw Lisp, primarily because a number of ACL2 primitives will not let you do so. Perhaps you have seen this error message when trying to do so: ~bv[] HARD ACL2 ERROR in ACL2-UNWIND-PROTECT: Apparently you have tried to execute a form in raw Lisp that is only intended to be executed inside the ACL2 loop. ~ev[] Even without this problem it is important to enter the ACL2 loop (~pl[lp]), for example in order to set the ~ilc[cbd] and (to get more technical) the readtable. ACL2 provides a ``raw mode'' for execution of raw Lisp forms. In this mode, ~ilc[include-book] reduces essentially to a Common Lisp ~c[load]. More generally, the ACL2 logical ~ilc[world] is not routinely extended in raw mode (some sneaky tricks are probably required to make that happen). To turn raw mode off or on: ~bv[] :set-raw-mode t ; turn raw mode on :set-raw-mode nil ; turn raw mode off ~ev[] The way you can tell that you are in raw mode is by looking at the prompt (~pl[default-print-prompt]), which uses a capital ``~c[P]'' (suggesting something like program mode, but more so). ~bv[] ACL2 P> ~ev[] Typical benefits of raw mode are fast loading of source and compiled files and the capability to hack arbitrary Common Lisp code in an environment with the ACL2 sources loaded (and hence with ACL2 primitives available). In addition, ACL2 hard errors will put you into the Lisp debugger, rather than returning you to the ACL2 loop, and this may be helpful for debugging; ~pl[hard-error] and ~pl[illegal], but also ~pl[break-on-error]. However, it probably is generally best to avoid raw mode unless these advantages seem important. We expect the main benefit of raw mode to be in deployment of applications, where load time is much faster than the time required for a full-blown ~ilc[include-book], although in certain cases the fast loading of books and treatment of hard errors discussed above may be useful during development. Raw mode is also useful for those who want to build extensions of ACL2. For example, the following form can be put into a certifiable book to load an arbitrary Common Lisp source or compiled file. ~bv[] (progn (defttag my-application) (progn! (set-raw-mode t) (load \"some-file\"))) ~ev[] Also see ~c[with-raw-mode] defined in community book ~c[books/hacking/hacker.lisp], ~pl[defttag], and ~pl[progn!]. Below are several disadvantages to raw mode. These should discourage users from using it for general code development, as ~c[:]~ilc[program] mode is generally preferable. ~bf[] -- Forms are in essence executed in raw Lisp. Hence: -- Syntax checking is turned off; and -- Guard checking is completely disabled. -- Table events, including ~ilc[logic], are ignored, as are many other ~ilc[events], including ~ilc[defthm] and ~ilc[comp]. -- Soundness claims are weakened for any ACL2 session in which raw mode was ever entered; ~pl[defttag]. -- The normal undoing mechanism (~pl[ubt]) is not supported. -- Unexpected behavior may occur when you return from raw-mode. For example, if you redefine a :logic mode function whose guards have not been verified, you will not see the change inside the ACL2 loop because there, the raw Common Lisp definition is only executed after guards have been verified; ~pl[guards-and-evaluation] and ~pl[guard-evaluation-table]. ~ef[] We conclude with some details. ~em[Printing results]. The rules for printing results are unchanged for raw mode, with one exception. If the value to be printed would contain any Lisp object that is not a legal ACL2 object, then the ~c[print] routine is used from the host Lisp, rather than the usual ACL2 printing routine. The following example illustrates the printing used when an illegal ACL2 object needs to be printed. Notice how that ``command conventions'' are observed (~pl[ld-post-eval-print]); the ``~c[[Note]'' occurs one space over in the second example, and no result is printed in the third example. ~bv[] ACL2 P>(find-package \"ACL2\") [Note: Printing non-ACL2 result.] # ACL2 P>(mv nil (find-package \"ACL2\") state) [Note: Printing non-ACL2 result.] # ACL2 P>(mv t (find-package \"ACL2\") state) ACL2 P>(mv 3 (find-package \"ACL2\")) [Note: Printing non-ACL2 result.] (3 #) ACL2 P> ~ev[] If you have trouble with large structures being printed out, you might want to execute appropriate Common Lisp forms in raw mode, for example, ~c[(setq *print-length* 5)] and ~c[(setq *print-level* 5)]. ~em[Include-book]. The ~il[events] ~ilc[add-include-book-dir] and ~ilc[delete-include-book-dir] have been designed to work with raw mode. However, if you enter raw mode and then evaluate such forms, then the effects of these forms will disappear when you exit raw mode, in which case you can expect to see a suitable warning. Regarding ~em[include-book] itself: it should work in raw mode as you might expect, at least if a compiled file or expansion file was created when the book was certified; ~pl[certify-book]. ~em[Packages]. Raw mode disallows the use of ~ilc[defpkg]. If you want to create a new package, first exit raw mode with ~c[:set-raw-mode nil]; you can subsequently re-enter raw mode with ~c[:set-raw-mode t] if you wish.~/" (if (or (null flg) (equal flg '(quote nil))) '(set-raw-mode-off state) '(set-raw-mode-on state))) #-acl2-loop-only (defun-one-output stobj-out (val) ; Warning: This function assumes that we are not in the context of a local ; stobj. As of this writing, it is only used in raw mode, so this does not ; concern us too much. With raw mode, there are no guarantees. (if (eq val *the-live-state*) 'state (car (rassoc val *user-stobj-alist* :test 'eq)))) #-(or acl2-loop-only acl2-mv-as-values) (defun mv-ref! (i) ; This silly function is just mv-ref, but without the restriction that the ; argument be an explicit number. (case i (1 (mv-ref 1)) (2 (mv-ref 2)) (3 (mv-ref 3)) (4 (mv-ref 4)) (5 (mv-ref 5)) (6 (mv-ref 6)) (7 (mv-ref 7)) (8 (mv-ref 8)) (9 (mv-ref 9)) (10 (mv-ref 10)) (11 (mv-ref 11)) (12 (mv-ref 12)) (13 (mv-ref 13)) (14 (mv-ref 14)) (15 (mv-ref 15)) (16 (mv-ref 16)) (17 (mv-ref 17)) (18 (mv-ref 18)) (19 (mv-ref 19)) (20 (mv-ref 20)) (21 (mv-ref 21)) (22 (mv-ref 22)) (23 (mv-ref 23)) (24 (mv-ref 24)) (25 (mv-ref 25)) (26 (mv-ref 26)) (27 (mv-ref 27)) (28 (mv-ref 28)) (29 (mv-ref 29)) (30 (mv-ref 30)) (31 (mv-ref 31)) (otherwise (error "Illegal value for mv-ref!")))) (defmacro add-raw-arity (name val) (declare (xargs :guard (and (symbolp name) (or (and (integerp val) (<= 0 val)) (eq val :last))))) ":Doc-Section Set-raw-mode add arity information for raw mode~/ Technical note: This macro is a no-op, and is not necessary, when ACL2 is built with #-acl2-mv-as-values. Users of raw mode (~pl[set-raw-mode]) can use arbitrary raw Lisp functions that are not known inside the usual ACL2 loop. In such cases, ACL2 may not know how to display a multiple value returned by ACL2's ~ilc[mv] macro. The following example should make this clear. ~bv[] ACL2 P>(defun foo (x y) (mv y x)) FOO ACL2 P>(foo 3 4) Note: Unable to compute number of values returned by this evaluation because function FOO is not known in the ACL2 logical world. Presumably it was defined in raw Lisp or in raw mode. Returning the first (perhaps only) value for calls of FOO. 4 ACL2 P>(add-raw-arity foo 2) RAW-ARITY-ALIST ACL2 P>(foo 3 4) (4 3) ACL2 P> ~ev[] The first argument of ~c[add-raw-arity] should be a symbol, representing the name of a function, macro, or special form, and the second argument should either be a non-negative integer (denoting the number of values returned by ACL2) or else the symbol ~c[:LAST], meaning that the number of values returned by the call is the number of values returned by the last argument.~/ The current arity assignments can be seen by evaluating ~c[(@ raw-arity-alist)]. ~l[remove-raw-arity] for how to undo a call of ~c[add-raw-arity].~/" #+acl2-mv-as-values (declare (ignore name val)) #+acl2-mv-as-values '(value nil) #-acl2-mv-as-values `(pprogn (f-put-global 'raw-arity-alist (put-assoc-eq ',name ,val (f-get-global 'raw-arity-alist state)) state) (value 'raw-arity-alist))) (defmacro remove-raw-arity (name) (declare (xargs :guard (symbolp name))) ":Doc-Section Set-raw-mode remove arity information for raw mode~/ Technical note: This macro is a no-op, and is not necessary, when ACL2 is built with #-acl2-mv-as-values. The form ~c[(remove-raw-arity fn)] undoes the effect of an earlier ~c[(remove-raw-arity fn val)]. ~l[add-raw-arity].~/~/" #+acl2-mv-as-values (declare (ignore name)) #+acl2-mv-as-values '(value nil) #-acl2-mv-as-values `(pprogn (f-put-global 'raw-arity-alist (delete-assoc-eq ',name (f-get-global 'raw-arity-alist state)) state) (value 'raw-arity-alist))) #-(or acl2-loop-only acl2-mv-as-values) (defun raw-arity (form wrld state) (cond ((atom form) 1) ((eq (car form) 'mv) (length (cdr form))) ((eq (car form) 'if) (let ((arity1 (raw-arity (caddr form) wrld state))) (if (cdddr form) (let ((arity2 (raw-arity (cadddr form) wrld state))) (if (eql arity1 arity2) arity1 (let ((min-arity (min arity1 arity2))) (prog2$ (warning$ 'top-level "Raw" "Unable to compute arity of the following ~ IF-expression in raw mode because the true branch ~ has arity ~x0 but the false branch has arity ~x1, ~ so we assume an arity of ~x2 ~ (see :DOC add-raw-arity):~% ~x3." arity1 arity2 min-arity form) min-arity)))) arity1))) ((eq (car form) 'return-last) (raw-arity (car (last form)) wrld state)) (t (let ((arity (cdr (assoc-eq (car form) (f-get-global 'raw-arity-alist state))))) (cond ((eq arity :last) (raw-arity (car (last form)) wrld state)) ((and (integerp arity) (<= 0 arity)) arity) (arity (error "Ill-formed value of ~s." '(@ raw-arity-alist))) (t (let ((stobjs-out (getprop (car form) 'stobjs-out t 'current-acl2-world wrld))) (cond ((eq stobjs-out t) (multiple-value-bind (new-form flg) (macroexpand-1 form) (cond ((null flg) ; Remember that our notion of multiple value here is ACL2's notion, not Lisp's ; notion. So the arity is 1 for calls of Common Lisp functions. (when (not (member-eq (car form) *common-lisp-symbols-from-main-lisp-package*)) (fms "Note: Unable to compute number of values ~ returned by this evaluation because function ~x0 ~ is not known in the ACL2 logical world. ~ Presumably it was defined in raw Lisp or in raw ~ mode. Returning the first (perhaps only) value ~ for calls of ~x0. See :DOC add-raw-arity.~|" (list (cons #\0 (car form))) *standard-co* state nil)) 1) (t (raw-arity new-form wrld state))))) (t (length stobjs-out)))))))))) (defun alist-to-bindings (alist) (cond ((endp alist) nil) (t (cons (list (caar alist) (kwote (cdar alist))) (alist-to-bindings (cdr alist)))))) #-acl2-loop-only (defun-one-output acl2-raw-eval-form-to-eval (form) `(let ((state *the-live-state*) ,@(alist-to-bindings *user-stobj-alist*)) ; CCL prints "Unused lexical variable" warnings unless we take some ; measures, which we do now. We notice that we need to include #+cmu for the ; second form, so we might as well include it for the first, too. #+(or ccl cmu sbcl) ,@(mapcar #'(lambda (x) `(declare (ignorable ,(car x)))) *user-stobj-alist*) #+(or ccl cmu sbcl) (declare (ignorable state)) ,(cond ((and (consp form) (eq (car form) 'in-package) (or (and (consp (cdr form)) (null (cddr form))) (er hard 'top-level "IN-PACKAGE takes one argument. The form ~p0 is ~ thus illegal." form))) ; The package must be one that ACL2 knows about, or there are likely to be ; problems involving the prompt and the ACL2 reader. Also, we want the ; in-package form to reflect in the prompt. (list 'in-package-fn (list 'quote (cadr form)) 'state)) (t form)))) #-(or acl2-loop-only acl2-mv-as-values) (defun acl2-raw-eval (form state) (or (eq state *the-live-state*) (error "Unexpected state in acl2-raw-eval!")) (if (or (eq form :q) (equal form '(EXIT-LD STATE))) (mv nil '((NIL NIL STATE) NIL :Q REPLACED-STATE) state) (let ((val (eval (acl2-raw-eval-form-to-eval form))) (index-bound (raw-arity form (w state) state))) (if (<= index-bound 1) (mv nil (cons (list (stobj-out val)) val) state) (let ((ans nil) (stobjs-out nil)) (do ((i (1- index-bound) (1- i))) ((eql i 0)) (let ((x (mv-ref! i))) (push x ans) (push (stobj-out x) stobjs-out))) (mv nil (cons (cons (stobj-out val) stobjs-out) (cons val ans)) state)))))) #+(and (not acl2-loop-only) acl2-mv-as-values) (defun acl2-raw-eval (form state) (or (eq state *the-live-state*) (error "Unexpected state in acl2-raw-eval!")) (if (or (eq form :q) (equal form '(EXIT-LD STATE))) (mv nil '((NIL NIL STATE) NIL :Q REPLACED-STATE) state) (let* ((vals (multiple-value-list (eval (acl2-raw-eval-form-to-eval form)))) (arity (length vals))) (if (<= arity 1) (let ((val (car vals))) (mv nil (cons (list (stobj-out val)) val) state)) (mv nil (loop for val in vals collect (stobj-out val) into stobjs-out finally (return (cons stobjs-out vals))) state))))) #+acl2-loop-only (defun acl2-raw-eval (form state) (trans-eval form 'top-level state t)) (defun get-and-chk-last-make-event-expansion (form wrld ctx state names) (let ((expansion (f-get-global 'last-make-event-expansion state))) (cond (expansion (mv-let (erp val state) (state-global-let* ((inhibit-output-lst *valid-output-names*)) (chk-embedded-event-form form nil ; orig-form wrld ctx state names nil ; portcullisp nil ; in-local-flg nil ; in-encapsulatep nil ; make-event-chk )) (declare (ignore val)) (cond (erp (er soft ctx "Make-event is only legal in event contexts, where it ~ can be tracked properly; see :DOC make-event. The ~ form ~p0 has thus generated an illegal call of ~ make-event. This form's evaluation will have no ~ effect on the ACL2 logical world." form)) (t (value expansion))))) (t (value nil))))) (defconst *local-value-triple-elided* ; Warning: Do not change the value of this constant without searching for all ; occurrences of (value-triple :elided) in the sources (especially, ; :doc strings). '(local (value-triple :elided))) (mutual-recursion (defun elide-locals-rec (form strongp) ; WARNING: Keep this in sync with chk-embedded-event-form, ; destructure-expansion, and make-include-books-absolute. ; We assume that form is a legal event form and return (mv changed-p new-form), ; where new-form results from eliding top-level local events from form, and ; changed-p is true exactly when such eliding has taken place. Note that we do ; not dive into encapsulate forms when strongp is nil, the assumption being ; that such forms are handled already in the construction of record-expansion ; calls in eval-event-lst. (cond ((atom form) (mv nil form)) ; note that progn! can contain atoms ((equal form *local-value-triple-elided*) (mv nil form)) ((eq (car form) 'local) (mv t *local-value-triple-elided*)) ((member-eq (car form) '(skip-proofs with-output with-prover-time-limit with-prover-step-limit record-expansion ; Can time$ really occur in an event context? At one time we seemed to think ; that time$1 could, but it currently seems doubtful that either time$1 or ; time$ could occur in an event context. It's harmless to leave the next line, ; but it particulary makes no sense to us to use time$1, so we use time$ ; instead. time$)) (mv-let (changed-p x) (elide-locals-rec (car (last form)) strongp) (cond (changed-p (mv t (append (butlast form 1) (list x)))) (t (mv nil form))))) ((or (eq (car form) 'progn) (and (eq (car form) 'progn!) (not (and (consp (cdr form)) (eq (cadr form) :state-global-bindings))))) (mv-let (changed-p x) (elide-locals-lst (cdr form) strongp) (cond (changed-p (mv t (cons (car form) x))) (t (mv nil form))))) ((eq (car form) 'progn!) ; hence :state-global-bindings case (mv-let (changed-p x) (elide-locals-lst (cddr form) strongp) (cond (changed-p (mv t (list* (car form) (cadr form) x))) (t (mv nil form))))) ((and strongp (eq (car form) 'encapsulate)) (mv-let (changed-p x) (elide-locals-lst (cddr form) strongp) (cond (changed-p (mv t (list* (car form) (cadr form) x))) (t (mv nil form))))) (t (mv nil form)))) (defun elide-locals-lst (x strongp) (cond ((endp x) (mv nil nil)) (t (mv-let (changedp1 first) (elide-locals-rec (car x) strongp) (mv-let (changedp2 rest) (elide-locals-lst (cdr x) strongp) (cond ((or changedp1 changedp2) (mv t (cons first rest))) (t (mv nil x)))))))) ) (defun elide-locals (form environment strongp) ; We do not elide locals if we are at the top level, as opposed to inside ; certify-book, because we don't want to lose potential information about local ; skip-proofs events. (As of this writing, 3/15/09, it's not clear that such ; risk exists; but we will play it safe.) Note that our redundancy test for ; encapsulates should work fine even if the same encapsulate form has a ; different expansion in some certification world and in some book, since for ; redundancy it suffices to compare the original make-event to the new one in ; each case. Note that we track skip-proofs events in the certification world, ; even those under LOCAL; see the Essay on Skip-proofs. (cond ((member-eq 'certify-book environment) ; In this case, we know that certify-book has not been called only to write out ; a .acl2x file (as documented in eval-event-lst). If we are writing a .acl2x ; file, then we need to keep local events to support certification. (mv-let (changed-p x) (elide-locals-rec form strongp) (declare (ignore changed-p)) x)) (t form))) (defun make-record-expansion (event expansion) (case-match event (('record-expansion a &) ; & is a partial expansion (list 'record-expansion a expansion)) (& (list 'record-expansion event expansion)))) (defun eval-event-lst (index expansion-alist ev-lst quietp environment in-local-flg last-val other-control kpa ctx channel state) ; This function takes a true list of forms, ev-lst, and successively evals each ; one, cascading state through successive elements. However, it insists that ; each form is an embedded-event-form. We return a tuple (mv erp value ; expansion-alist kpa-result state), where erp is 'non-event if some member of ; ev-lst is not an embedded event form and otherwise is as explained below. If ; erp is nil, then: value is the final value (or nil if ev-lst is empty); ; expansion-alist associates the (+ index n)th member E of ev-lst with its ; expansion if there was any make-event expansion subsidiary to E, ordered by ; index from smallest to largest (accumulated in reverse order); and kpa-result ; is derived from kpa as described below. If erp is not nil, then let n be the ; (zero-based) index of the event in ev-lst that translated or evaluated to ; some (mv erp0 ...) with non-nil erp0. Then we return (mv t (+ index n) ; state) if the error was during translation, else (mv (list erp0) (+ index n) ; state). Except, in the special case that there is no error but we find that ; make-event was called under some non-embedded-event form, we return (mv ; 'make-event-problem (+ index n) state). ; Environment is a list containing at most one of 'certify-book or 'pcert, and ; also perhaps 'encapsulate indicate whether we are under a certify-book ; (possibly doing provisional certification) and/or an encapsulate. Note that ; 'certify-book is not present when certify-book has been called only to write ; out a .acl2x file. ; Other-control is either :non-event-ok, used for progn!, or else t or nil for ; the make-event-chk in chk-embedded-event-form. ; Kpa is generally nil and not of interest, in which case kpa-result (mentioned ; above) is also nil. However, if eval-event-lst is being called on behalf of ; certify-book, then kpa is initially the known-package-alist just before ; evaluation of the forms in the book. As soon as a different (hence larger) ; known-package-alist is observed, kpa is changed to the current index, i.e., ; the index of the event that caused this change to the known-package-alist; ; and this parameter is not changed on subsequent recursive calls and is ; ultimately returned. Ultimately certify-book will cdr away that many ; expansion-alist entries before calling expansion-alist-pkg-names. ; Channel is generally (proofs-co state), but doesn't have to be. ; A non-nil value of quietp suppresses printing of the event and the result. (cond ((null ev-lst) (pprogn (f-put-global 'last-make-event-expansion nil state) (mv nil last-val (reverse expansion-alist) kpa state))) (t (pprogn (cond (quietp state) (t (io? event nil state (channel ev-lst) (fms "~%~@0~sr ~@1~*2~#3~[~Q45~/~]~|" (list (cons #\0 (f-get-global 'current-package state)) (cons #\1 (defun-mode-prompt-string state)) (cons #\2 (list "" ">" ">" ">" (make-list-ac (1+ (f-get-global 'ld-level state)) nil nil))) (cons #\3 (if (eq (ld-pre-eval-print state) :never) 1 0)) (cons #\4 (car ev-lst)) (cons #\5 (term-evisc-tuple nil state)) (cons #\r #+:non-standard-analysis "(r)" #-:non-standard-analysis "")) channel state nil)))) (mv-let (erp form state) (cond ((eq other-control :non-event-ok) (mv nil (car ev-lst) state)) (t (chk-embedded-event-form (car ev-lst) nil (w state) ctx state (primitive-event-macros) nil in-local-flg (member-eq 'encapsulate environment) other-control))) (cond (erp (pprogn (f-put-global 'last-make-event-expansion nil state) (mv 'non-event index nil nil state))) ((null form) (eval-event-lst (1+ index) expansion-alist (cdr ev-lst) quietp environment in-local-flg nil other-control kpa ctx channel state)) (t (mv-let (erp trans-ans state) (pprogn (f-put-global 'last-make-event-expansion nil state) (if (raw-mode-p state) (acl2-raw-eval form state) (trans-eval form ctx state t))) ; If erp is nil, trans-ans is ; ((nil nil state) . (erp' val' replaced-state)) ; because ev-lst contains nothing but embedded event forms. (let* ((tuple (cond ((eq other-control :non-event-ok) (let* ((stobjs-out (car trans-ans)) (result (replace-stobjs stobjs-out (cdr trans-ans)))) (if (null (cdr stobjs-out)) ; single value (list nil result) result))) (t (cdr trans-ans)))) (erp-prime (car tuple)) (val-prime (cadr tuple))) (cond ((or erp erp-prime) (pprogn (cond ((and (consp (car ev-lst)) (eq (car (car ev-lst)) 'record-expansion)) (let ((chan (proofs-co state))) (io? error nil state (chan ev-lst) (fmt-abbrev "~%Note: The error reported above ~ occurred when processing the ~ make-event expansion of the form ~ ~x0." (list (cons #\0 (cadr (car ev-lst)))) 0 chan state "~|~%")))) (t state)) (f-put-global 'last-make-event-expansion nil state) (mv (if erp t (list erp-prime)) index nil kpa state))) (t (pprogn (cond (quietp state) (t (io? summary nil state (val-prime channel) (cond ((member-eq 'value (f-get-global 'inhibited-summary-types state)) state) (t (mv-let (col state) (fmt1 "~y0" (list (cons #\0 val-prime)) 0 channel state (ld-evisc-tuple state)) (declare (ignore col)) state)))))) (mv-let (erp expansion0 state) ; We need to cause an error if we have an expansion but are not properly ; tracking expansions. For purposes of seeing if such tracking is being done, ; it should suffice to do the check in the present world rather than the world ; present before evaluating the form. (get-and-chk-last-make-event-expansion (car ev-lst) (w state) ctx state (primitive-event-macros)) (cond (erp (pprogn (f-put-global 'last-make-event-expansion nil state) (mv 'make-event-problem index nil nil state))) (t (eval-event-lst (1+ index) (cond (expansion0 (acons index (make-record-expansion (car ev-lst) (elide-locals (mv-let (wrappers base-form) (destructure-expansion form) (declare (ignore base-form)) (rebuild-expansion wrappers expansion0)) environment ; We use strongp = nil here because sub-encapsulates are already taking care of ; eliding their own locals. nil)) expansion-alist)) (t expansion-alist)) (cdr ev-lst) quietp environment in-local-flg val-prime other-control (cond ((or (null kpa) (integerp kpa) (equal kpa (known-package-alist state))) kpa) (t index)) ctx channel state)))))))))))))))) ; After we have evaluated the event list and obtained wrld2, we ; will scrutinize the signatures and exports to make sure they are ; appropriate. We will try to give the user as much help as we can in ; detecting bad signatures and exports, since it may take him a while ; to recreate wrld2 after fixing an error. Indeed, he has already ; paid a high price to get to wrld2 and it is a real pity that we'll ; blow him out of the water now. The guilt! It's enough to make us ; think about implementing some sort of interactive version of ; encapsulate, when we don't have anything else to do. (We have since ; implemented redo-flat, which helps with the guilt.) (defun equal-insig (insig1 insig2) ; Suppose insig1 and insig2 are both internal form signatures, (fn ; formals stobjs-in stobjs-out). We return t if they are ``equal.'' ; But by equal we mean only that the fn, stobjs-in and stobjs-out are ; the same. If the user has declared that fn has formals (x y z) and ; then witnessed fn with a function with formals (u v w), we don't ; care -- as long as the stobjs among the two lists are the same in ; corresponding positions. But that information is captured in the ; stobjs-in. (and (equal (car insig1) (car insig2)) (equal (caddr insig1) (caddr insig2)) (equal (cadddr insig1) (cadddr insig2)))) ;; RAG - I changed this so that non-classical witness functions are ;; not allowed. The functions introduced by encapsulate are ;; implicitly taken to be classical, so a non-classical witness ;; function presents a (non-obvious) signature violation. (defun bad-signature-alist (insigs kwd-value-list-lst udf-fns wrld) ; Warning: If you change this function, consider changing the message printed ; by any function that uses the result of this function. ; For ACL2 (as opposed to ACL2(r)), we do not use kwd-value-list-lst. It is ; convenient though to keep it as a formal, to avoid proliferation of ; #-:non-standard-analysis readtime conditionals. We are tempted to declare ; kwd-value-list-lst as IGNOREd, in order to avoid the complaint that ; kwd-value-list-lst is an irrelevant formal. However, ACL2 then complains ; because of the recursive calls of this function. Fortunately, declaring ; kwd-value-list-lst IGNORABLE also turns off the irrelevance check. #-:non-standard-analysis (declare (ignorable kwd-value-list-lst)) (cond ((null insigs) nil) ((member-eq (caar insigs) udf-fns) (bad-signature-alist (cdr insigs) (cdr kwd-value-list-lst) udf-fns wrld)) (t (let* ((declared-insig (car insigs)) (fn (car declared-insig)) (actual-insig (list fn (formals fn wrld) (stobjs-in fn wrld) (stobjs-out fn wrld)))) (cond ((and (equal-insig declared-insig actual-insig) #+:non-standard-analysis ; If the function is specified to be classical, then it had better have a ; classical witness. But in fact the converse is critical too! Consider the ; following example. ; (encapsulate ; ((g (x) t :classicalp nil)) ; (local (defun g (x) x)) ; (defun f (x) ; (g x))) ; This is clearly not what we intend: a classical function (f) that depends ; syntactically on a non-classical function (g). We could then probably prove ; nil (though we haven't done it) by deriving a property P about f that fails ; for some non-classical function h, then deriving the trivial corollary that P ; holds for g in place of f (since f and g are equal), and then functionally ; instantiating this corollary for g mapped to h. But even if such a proof ; attempt were somehow to fail, we prefer not to allow the situation above, ; which seems bound to lead to unsoundness eventually! (eq (classicalp fn wrld) (let ((tail (assoc-keyword :classicalp (car kwd-value-list-lst)))) (cond (tail (cadr tail)) (t t))))) (bad-signature-alist (cdr insigs) (cdr kwd-value-list-lst) udf-fns wrld)) (t (cons (list fn declared-insig actual-insig) (bad-signature-alist (cdr insigs) (cdr kwd-value-list-lst) udf-fns wrld)))))))) (defmacro if-ns (test tbr fbr ctx) ; This is just (list 'if test tbr fbr), except that we expect test always to be ; false in the standard case. #+:non-standard-analysis (declare (ignore ctx)) #-:non-standard-analysis (declare (ignore tbr)) (list 'if test #+:non-standard-analysis tbr #-:non-standard-analysis `(er hard ,ctx "Unexpected intrusion of non-standard analysis into standard ~ ACL2! Please contact the implementors.") fbr)) (defun tilde-*-bad-insigs-phrase1 (alist) (cond ((null alist) nil) (t (let* ((fn (caar alist)) (dcl-insig (cadar alist)) (act-insig (caddar alist))) (cons (if-ns (equal-insig dcl-insig act-insig) (msg "The signature you declared for ~x0 and the local ~ witness for that function do not agree on whether the ~ function is classical. If you are seeing this error ~ in the context of an attempt to admit a call of ~ DEFUN-SK without a :CLASSICALP keyword supplied, then ~ a solution is likely to be the addition of :CLASSICALP ~ ~x1 to the DEFUN-SK form." fn nil) (msg "The signature you declared for ~x0 is ~x1, but ~ the signature of your local witness for it is ~ ~x2." fn (unparse-signature dcl-insig) (unparse-signature act-insig)) 'tilde-*-bad-insigs-phrase1) (tilde-*-bad-insigs-phrase1 (cdr alist))))))) (defun tilde-*-bad-insigs-phrase (alist) ; Each element of alist is of the form (fn insig1 insig2), where ; insig1 is the internal form of the signature presented by the user ; in his encapsulate and insig2 is the internal form signature of the ; witness. For each element we print a sentence of the form "The ; signature for your local definition of fn is insig2, but the ; signature you declared for fn was insig1." (list "" "~@*" "~@*" "~@*" (tilde-*-bad-insigs-phrase1 alist))) (defun union-eq-cars (alist) (cond ((null alist) nil) (t (union-eq (caar alist) (union-eq-cars (cdr alist)))))) (defun chk-acceptable-encapsulate2 (insigs kwd-value-list-lst wrld ctx state) ; Wrld is a world alist created by the execution of an event list. Insigs is a ; list of internal form function signatures. We verify that they are defined ; as functions in wrld and have the signatures listed. ; This is an odd little function because it may generate more than one error ; message. The trouble is that this wrld took some time to create and yet will ; have to be thrown away as soon as we find one of these errors. So, as a ; favor to the user, we find all the errors we can. (let ((udf-fns ; If we are going to insist on functions being defined (see first error below), ; we might as well insist that they are defined in :logic mode. (collect-non-logic-mode insigs wrld))) (mv-let (erp1 val state) (cond (udf-fns (er soft ctx "You provided signatures for ~&0, but ~#0~[that function ~ was~/those functions were~] not defined in :logic mode by the ~ encapsulated event list. See :DOC encapsulate." (merge-sort-symbol-< udf-fns))) (t (value nil))) (declare (ignore val)) (mv-let (erp2 val state) (let ((bad-sig-alist (bad-signature-alist insigs kwd-value-list-lst udf-fns wrld))) (cond (bad-sig-alist (er soft ctx "The signature~#0~[~/s~] provided for the function~#0~[~/s~] ~ ~&0 ~#0~[is~/are~] incorrect. See :DOC encapsulate. ~*1" (strip-cars bad-sig-alist) (tilde-*-bad-insigs-phrase bad-sig-alist))) (t (value nil)))) (declare (ignore val)) (mv (or erp1 erp2) nil state))))) (defun conjoin-into-alist (fn thm alist) ; Alist is an alist that maps function symbols to terms. Fn is a function ; symbol and thm is a term. If fn is not bound in alist we add (fn . thm) ; to it. Otherwise, we change the binding (fn . term) in alist to ; (fn . (if thm term *nil*)). (cond ((null alist) (list (cons fn thm))) ((eq fn (caar alist)) (cons (cons fn (conjoin2 thm (cdar alist))) (cdr alist))) (t (cons (car alist) (conjoin-into-alist fn thm (cdr alist)))))) (defun classes-theorems (classes) ; Classes is the 'classes property of some symbol. We return the list of all ; corollary theorems from these classes. (cond ((null classes) nil) (t (let ((term (cadr (assoc-keyword :corollary (cdr (car classes)))))) (if term (cons term (classes-theorems (cdr classes))) (classes-theorems (cdr classes))))))) (defun constraints-introduced1 (thms fns ans) (cond ((endp thms) ans) ((ffnnamesp fns (car thms)) ; By using union-equal below, we handle the case that an inner encapsulate may ; have both an 'unnormalized-body and 'constraint-lst property, so that if ; 'unnormalized-body has already been put into ans, then we don't include that ; constraint when we see it here. (constraints-introduced1 (cdr thms) fns (union-equal (flatten-ands-in-lit (car thms)) ans))) (t (constraints-introduced1 (cdr thms) fns ans)))) (defun new-trips (wrld3 proto-wrld3 seen acc) ; Important: This function returns those triples in wrld3 that are after ; proto-wrld3, in the same order they have in wrld3. See the comment labeled ; "Important" in the definition of constrained-functions. ; As with the function actual-props, we are only interested in triples ; that aren't superseded by *acl2-property-unbound*. We therefore do ; not copy to our answer any *acl2-property-unbound* triple or any ; chronologically earlier bindings of the relevant symbol and key! ; That is, the list of triples returned by this function contains no ; *acl2-property-unbound* values and makes it appear as though the ; property list was really erased when that value was stored. ; Note therefore that the list of triples returned by this function ; will not indicate when a property bound in proto-wrld3 becomes ; unbound in wrld3. However, if a property was stored during the ; production of wrld3 and the subsequently in the production of wrld3 ; that property was set to *acl2-property-unbound*, then the property ; is gone from the new-trips returned here. ; Warning: The value of this function is sometimes used as though it ; were the 'current-acl2-world! It is a legal property list world. ; If it gets into a getprop on 'current-acl2-world the answer is ; correct but slow. Among other things, we use new-trips to compute ; the ancestors of a definition defined within an encapsulate -- ; knowing that functions used in those definitions but defined outside ; of the encapsulate (and hence, outside of new-trips) will be treated ; as primitive. That way we do not explore all the way back to ground ; zero when we are really just looking for the subfunctions defined ; within the encapsulate. ; Note on this recursion: The recursion below is potentially ; disastrously slow. Imagine that proto-wrld3 is a list of 10,000 ; repetitions of the element e. Imagine that wrld3 is the extension ; produced by adding 1000 more copies of e. Then the equal below will ; fail the first 1000 times, but it will only fail after confirming ; that the first 10,000 e's in wrld3 are the same as the corresponding ; ones in proto-wrld3, i.e., the equal will do a root-and-branch walk ; through proto-wrld3 1000 times. When finally the equal succeeds it ; potentially does another root-and-branch exploration of proto-wrld3. ; However, this worst-case scenario is not likely. More likely, if ; wrld3 is an extension of proto-wrld3 then the first element of wrld3 ; differs from that of proto-wrld3 -- because either wrld3 begins with ; a putprop of a new name or a new list of lemmas or some other ; property. Therefore, most of the time the equal below will fail ; immediately when the two worlds are not equal. When the two worlds ; are in fact equal, they will be eq, because wrld3 was actually ; constructed by adding triples to proto-wrld3. So the equal will ; succeed on its initial eq test and avoid a root-and-branch ; exploration. This analysis is crucial to the practicality of this ; recursive scheme. Our worlds are so large we simply cannot afford ; root-and-branch explorations. ; In fact, we did see performance issues when seen was kept as a list ; of triples. So, we have restructured it as an alist, whose values ; are alists, in which triple (key1 key2 . val) is found in the alist ; associated with key1. (cond ((equal wrld3 proto-wrld3) (reverse acc)) ((let ((key-alist (assoc-eq (caar wrld3) seen))) (and key-alist ; optimization (assoc-eq (cadar wrld3) (cdr key-alist)))) (new-trips (cdr wrld3) proto-wrld3 seen acc)) ((eq (cddr (car wrld3)) *acl2-property-unbound*) (new-trips (cdr wrld3) proto-wrld3 (put-assoc-eq (caar wrld3) (cons (cdar wrld3) (cdr (assoc-eq (caar wrld3) seen))) seen) acc)) (t (new-trips (cdr wrld3) proto-wrld3 (put-assoc-eq (caar wrld3) (cons (cdar wrld3) (cdr (assoc-eq (caar wrld3) seen))) seen) (cons (car wrld3) acc))))) (defun constraints-introduced (new-trips fns ans) ; New-trips is a list of triples from a property list world, none of them with ; cddr *acl2-property-unbound*. We return the list of all formulas represented ; in new-trips that mention any function symbol in the list fns (each of which ; is in :logic mode), excluding definitional (defuns, defchoose) axioms. We ; may skip properties such as 'congruences and 'lemmas that can only be there ; if some other property has introduced a formula for which the given ; property's implicit formula is a consequence. A good way to look at this is ; that the only events that can introduce axioms are defuns, defthm, ; encapsulate, defaxiom, and include-book, and we have ruled out the last two. ; Encapsulate is covered by the 'constraint-lst property. (cond ((endp new-trips) ans) (t (constraints-introduced (cdr new-trips) fns (let ((trip (car new-trips))) (case (cadr trip) (constraint-lst ; As promised in a comment in encapsulate-constraint, here we explain why the ; 'constraint-lst properties must be considered as we collect up formulas for ; an encapsulate event. That is, we explain why after virtually moving ; functions in front of an encapsulate where possible, then any ; sub-encapsulate's constraint is a formula that must be collected. The ; following example illustrates, starting with the following event. ; (encapsulate ; ((f1 (x) t) ; (f2 (x) t)) ; (local (defun f1 (x) x)) ; (local (defun f2 (x) x)) ; (encapsulate ; ((g (x) t)) ; (local (defun g (x) x)) ; (defthm g-prop (and (equal (f1 x) (g x)) ; (equal (f2 x) (g x))) ; :rule-classes nil))) ; Suppose we did not collect up g-prop here, considering it to be a sort of ; definitional axiom for g. Then we would collect up nothing, which would make ; g a candidate to be moved back, as though we had the following events. Here, ; we use a skip-proofs to mimic the behavior we are contemplating. ; (encapsulate ; ((f1 (x) t) ; (f2 (x) t)) ; (local (defun f1 (x) x)) ; (local (defun f2 (x) x))) ; ; (skip-proofs ; (encapsulate ; ((g (x) t)) ; (local (defun g (x) x)) ; (defthm g-prop (and (equal (f1 x) (g x)) ; (equal (f2 x) (g x))) ; :rule-classes nil))) ; We can then prove nil as follows. ; (defthm f1-is-f2 ; (equal (f1 x) (f2 x)) ; :hints (("Goal" :use g-prop))) ; ; (defthm contradiction ; nil ; :hints (("Goal" :use ((:functional-instance ; f1-is-f2 ; (f1 (lambda (x) (cons x x))) ; (f2 (lambda (x) (consp x))))))) ; :rule-classes nil) ; The moral of the story is that our treatment of encapsulates for which some ; signature function is ancestral must be analogous to our treatment of ; subversive defuns: their constraints must be considered. An easy way to ; provide this treatment is for the following call of constraints-introduced to ; collect up constraints. One might think this unnecessary, since every defthm ; contributing to a constraint has a 'theorem property that will be collected. ; However, an "infected" defun can contribute to a constraint (because neither ; [Front] nor [Back] applies to it within its surrounding encapsulate event), ; and we are deliberately not collecting defun formulas. Moreover, we prefer ; not to rely on the presence of 'theorem properties for constraints. (let ((constraint-lst (cddr trip))) (cond ((eq constraint-lst *unknown-constraints*) ; This case should not happen. The only symbols with *unknown-constraints* are ; those introduced in a non-trivial encapsulate (one with non-empty signature ; list). But we are in such an encapsulate already, for which we cannot yet ; have computed the constraints as *unknown-constraints*. So the ; 'constraint-lst property in question is on a function symbol that was ; introduced in an inner encapsulate, which should have been illegal since that ; function symbol is in the scope of two (nested) non-trivial encapsulates, ; where the inner one designates a dependent clause-processor, and such ; non-unique promised encapsulates are illegal. (er hard 'constraints-introduced "Implementation error in constraints-introduced: ~ Please contact the ACL2 developers.")) ((symbolp constraint-lst) ; Then the constraint list for (car trip) is held in the 'constraint-lst ; property of (cddr trip). We know that this kind of "pointing" is within the ; current encapsulate, so it is safe to ignore this property, secure in the ; knowledge that we see the real constraint list at some point. ans) (t (constraints-introduced1 (cddr trip) fns ans))))) (theorem (cond ((ffnnamesp fns (cddr trip)) (union-equal (flatten-ands-in-lit (cddr trip)) ans)) (t ans))) (classes (constraints-introduced1 (classes-theorems (cddr trip)) fns ans)) (otherwise ans))))))) (defun putprop-constraints (fn constrained-fns constraint-lst dependent-clause-processor wrld3) ; Wrld3 is almost wrld3 of the encapsulation essay. We have added all the ; exports, but we have not yet stored the 'constraint-lst properties of the ; functions in the signature of the encapsulate. Fn is the first function ; mentioned in the signature, while constrained-fns includes the others as well ; as all functions that have any function in the signature as an ancestor. We ; have determined that the common constraint for all these functions is ; constraint-lst, which has presumably been obtained from all the new theorems ; introduced by the encapsulate that mention any functions in (fn ; . constrained-fns). ; We actually store the symbol fn as the value of the 'constraint-lst property ; for every function in constrained-fns. For fn, we store a 'constraint-lst ; property of constraint-lst. ; Note that we store a 'constraint-lst property for every function in (fn ; . constrained-fns). The function constraint-info will find this property ; rather than looking for an 'unnormalized-body or 'defchoose-axiom. (putprop-x-lst1 constrained-fns 'constraint-lst fn (putprop fn 'constraint-lst constraint-lst (cond (dependent-clause-processor (putprop-x-lst1 constrained-fns 'constrainedp dependent-clause-processor (putprop fn 'constrainedp dependent-clause-processor wrld3))) (t wrld3))))) (deflabel local-incompatibility :doc ":Doc-Section Miscellaneous when non-local ~il[events] won't replay in isolation~/ Sometimes a ``~ilc[local] incompatibility'' is reported while attempting to embed some ~il[events], as in an ~ilc[encapsulate] or ~ilc[include-book]. This is generally due to the use of a locally defined name in a non-local event or the failure to make a witnessing definition ~ilc[local].~/ ~ilc[Local] incompatibilities may be detected while trying to execute the strictly non-local ~il[events] of an embedding. For example, ~ilc[encapsulate] operates by first executing all the ~il[events] (~ilc[local] and non-local) with ~ilc[ld-skip-proofsp] ~c[nil], to confirm that they are all admissible. Then it attempts merely to assume the non-local ones to create the desired theory, by executing the ~il[events] with ~ilc[ld-skip-proofsp] set to ~c[']~ilc[include-book]. Similarly, ~ilc[include-book] assumes the non-local ones, with the understanding that a previously successful ~ilc[certify-book] has performed the admissiblity check. How can a sequence of ~il[events] admitted with ~ilc[ld-skip-proofsp] ~c[nil] fail when ~ilc[ld-skip-proofsp] is ~c[']~ilc[include-book]? The key observation is that in the latter case only the non-local ~il[events] are processed. The ~ilc[local] ones are skipped and so the non-local ones must not depend upon them. Two typical mistakes are suggested by the detection of a ~ilc[local] incompatibility: (1) a locally defined function or macro was used in a non-~ilc[local] event (and, in the case of ~ilc[encapsulate], was not included among the ~il[signature]s) and (2) the witnessing definition of a function that was included among the ~il[signature]s of an ~ilc[encapsulate] was not made ~ilc[local]. An example of mistake (1) would be to include among your ~il[encapsulate]d ~il[events] both ~c[(local (defun fn ...))] and ~c[(defthm lemma (implies (fn ...) ...))]. Observe that ~c[fn] is defined locally but a formula involving ~c[fn] is defined non-locally. In this case, either the ~ilc[defthm] should be made ~ilc[local] or the ~ilc[defun] should be made non-local. An example of mistake (2) would be to include ~c[(fn (x) t)] among your ~il[signature]s and then to write ~c[(defun fn (x) ...)] in your ~il[events], instead of ~c[(local (defun fn ...))]. One subtle aspect of ~ilc[encapsulate] is that if you constrain any member of a mutually recursive clique you must define the entire clique locally and then you must constrain those members of it you want axiomatized non-locally. Errors due to ~ilc[local] incompatibility should never occur in the assumption of a fully certified book. Certification ensures against it. Therefore, if ~ilc[include-book] reports an incompatibility, we assert that earlier in the processing of the ~ilc[include-book] a warning was printed advising you that some book was uncertified. If this is not the case ~-[] if ~ilc[include-book] reports an incompatibility and there has been no prior warning about lack of certification ~-[] please report it to us. When a ~ilc[local] incompatibility is detected, we roll-back to the ~il[world] in which we started the ~ilc[encapsulate] or ~ilc[include-book]. That is, we discard the intermediate ~il[world] created by trying to process the ~il[events] skipping proofs. This is clean, but we realize it is very frustrating because the entire sequence of ~il[events] must be processed from scratch. Assuming that the embedded ~il[events] were, once upon a time, processed as top-level ~il[command]s (after all, at some point you managed to create this sequence of ~il[command]s so that the ~ilc[local] and non-local ones together could survive a pass in which proofs were done), it stands to reason that we could define a predicate that would determine then, before you attempted to embed them, if ~ilc[local] incompatibilities exist. We hope to do that, eventually. We conclude with a subtle example of ~ilc[local] incompatibility. The problem is that in order for ~c[foo-type-prescription] to be admitted using the specified ~c[:typed-term] ~c[(foo x)], the conclusion ~c[(my-natp (foo x))] depends on ~c[my-natp] being a ~il[compound-recognizer]. This is fine on the first pass of the ~ilc[encapsulate], during which lemma ~c[my-natp-cr] is admitted. But ~c[my-natp-cr] is skipped on the second pass because it is marked ~ilc[local], and this causes ~c[foo-type-prescription] to fail on the second pass. ~bv[] (defun my-natp (x) (declare (xargs :guard t)) (and (integerp x) (<= 0 x))) (defun foo (x) (nfix x)) (encapsulate () (local (defthm my-natp-cr (equal (my-natp x) (and (integerp x) (<= 0 x))) :rule-classes :compound-recognizer)) (defthm foo-type-prescription (my-natp (foo x)) :hints ((\"Goal\" :in-theory (enable foo))) :rule-classes ((:type-prescription :typed-term (foo x))))) ~ev[]") (defun maybe-install-acl2-defaults-table (acl2-defaults-table state) (cond ((equal acl2-defaults-table (table-alist 'acl2-defaults-table (w state))) (value nil)) ; Otherwise, we call table-fn directly, rather than calling table by way of ; eval-event-lst, to circumvent the restriction agains calling ; acl2-defaults-table in the context of a LOCAL. (t (state-global-let* ((inhibit-output-lst (cons 'summary (@ inhibit-output-lst))) (modifying-include-book-dir-alist t)) (table-fn 'acl2-defaults-table `(nil ',acl2-defaults-table :clear) state `(table acl2-defaults-table nil ',acl2-defaults-table :clear)))))) (defun in-encapsulatep (embedded-event-lst non-trivp) ; This function determines if we are in the scope of an encapsulate. ; If non-trivp is t, we restrict the interpretation to mean ``in the ; scope of a non-trivial encapsulate'', i.e., in an encapsulate that ; introduces a constrained function symbol. (cond ((endp embedded-event-lst) nil) ((and (eq (car (car embedded-event-lst)) 'encapsulate) (if non-trivp (cadr (car embedded-event-lst)) t)) t) (t (in-encapsulatep (cdr embedded-event-lst) non-trivp)))) (defun update-for-redo-flat (n ev-lst state) ; Here we update the state globals 'redo-flat-succ and 'redo-flat-fail on ; behalf of a failure of progn, encapsulate, or certify-book. N is the ; zero-based index of the event in ev-lst that failed. (assert$ (and (natp n) (< n (length ev-lst))) (pprogn (f-put-global 'redo-flat-succ (append? (take n ev-lst) (f-get-global 'redo-flat-succ state)) state) (if (null (f-get-global 'redo-flat-fail state)) (f-put-global 'redo-flat-fail (nth n ev-lst) state) state)))) (defmacro redo-flat (&key (succ-ld-skip-proofsp 't) (label 'r) (succ 't) (fail 't) (pbt 't) (show 'nil)) ":Doc-Section Other redo on failure of a ~ilc[progn], ~ilc[encapsulate], or ~ilc[certify-book]~/ When one submits an ~ilc[encapsulate], ~ilc[progn], or ~ilc[certify-book] command and there is a failure, ACL2 restores its logical ~il[world] as though the command had not been run. But sometimes one would like to debug the failure by re-executing all sub-events that succeeded up to the point of failure, and then re-executing the failed sub-event. Said differently, imagine that the ~il[events] under an ~c[encapsulate], ~c[progn], or ~c[certify-book] form were flattened into a list of events that were then submitted to ACL2 up to the point of failure. This would put us in the state in which the original failed event had failed, so we could now replay that failed event and try modifying it, or first proving additional events, in order to get it admitted. ~c[Redo-flat] is provided for this purpose. Consider the following (rather nonsensical) example, in which the ~ilc[defun] of ~c[f3] fails (the body is ~c[y] but the formal parameter list is ~c[(x)]). ~bv[] (encapsulate () (defun f1 (x) x) (encapsulate () (local (defthm hack (equal (car (cons x y)) x)))) (encapsulate () (local (defthm hack (equal (+ x y) (+ y x))))) (encapsulate () (make-event '(defun f2 (x) x)) (progn (defthm foo (equal x x) :rule-classes nil) (defun f3 (x) y))) (defun f4 (x) x) (defun f5 (x) y)) ~ev[] After this attempt fails, you can evaluate the following form. ~bv[] (redo-flat) ~ev[] This will first lay down a ~ilc[deflabel] event, ~c[(deflabel r)], so that you can eventually remove your debugging work with ~c[(:ubt! r)]. Then the successful sub-events that preceded the failure will be executed with proofs skipped (so that this execution is fast). Then, the failed event will be executed. Finally, a ~c[:]~ilc[pbt] command is executed so that you can see a summary of the events that executed successfully. You can eliminate some of the steps above by supplying keyword values, as follows. ~bv[] (redo-flat :succ succ ; Skip the successful sub-events if val is nil. :fail fail ; Skip the failed sub-event if val is nil. :label lab ; Skip deflabel if lab or succ is nil, else use (deflabel lab). :pbt val ; Skip the final :pbt if val, lab, or succ is nil. ) ~ev[] Also, you can avoid skipping proofs for the successful sub-events by supplying keyword ~c[:succ-ld-skip-proofsp] with a valid value for ~c[ld-skip-proofsp]; ~pl[ld-skip-proofsp]. For example, you might want to execute ~c[(redo-flat :succ-ld-skip-proofsp nil)] if you use the ~c[must-fail] utility from community book ~c[make-event/eval.lisp], since for example ~c[(must-fail (thm (equal x y)))] normally succeeds but would cause an error if proofs are skipped. If you prefer only to see the successful and failed sub-events, without any events being re-executed, you may evaluate the following form instead. ~bv[] (redo-flat :show t) ~ev[] For the example above, this command produces the following output. ~bv[] List of events preceding the failure: ((DEFUN F1 (X) X) (ENCAPSULATE NIL (LOCAL (DEFTHM HACK (EQUAL (CAR (CONS X Y)) X)))) (ENCAPSULATE NIL (LOCAL (DEFTHM HACK (EQUAL (+ X Y) (+ Y X))))) (MAKE-EVENT '(DEFUN F2 (X) X)) (DEFTHM FOO (EQUAL X X) :RULE-CLASSES NIL)) Failed event: (DEFUN F3 (X) Y) ACL2 !> ~ev[] ~c[Redo-flat] uses a scheme that should not cause spurious name conflicts for ~ilc[local] events. Above, it is mentioned that events are ``flattened''; now we clarify this notion. Each sub-event that succeeds and is an ~ilc[encapsulate] or ~ilc[progn] is left intact. Only such events that fail are replaced by their component events. Thus, in the example above, there is no conflict between the two ~ilc[local] sub-events named ``~c[hack],'' because these are contained in successful ~c[encapsulate] sub-events, which are therefore not flattened. The ~ilc[progn] and two ~ilc[encapsulate] events surrounding the definition of ~c[f3] are, however, flattened, because that definition failed to be admitted. Normally, ~c[redo-flat] will have the desired effect even if you interrupted a proof (with control-c). However, ~c[redo-flat] will not produce the desired result after an interrupt if you have enabled the debugger using ~c[(set-debugger-enable t)],~/~/" `(if (null (f-get-global 'redo-flat-fail state)) (pprogn (fms "There is no failure saved from an encapsulate, progn, or ~ certify-book.~|" nil (standard-co state) state nil) (value :invisible)) ,(if show `(pprogn (fms "List of events preceding the failure:~|~%~x0~|" (list (cons #\0 (f-get-global 'redo-flat-succ state))) (standard-co state) state (ld-evisc-tuple state)) (fms "Failed event:~|~%~x0~|" (list (cons #\0 (f-get-global 'redo-flat-fail state))) (standard-co state) state (ld-evisc-tuple state)) (value :invisible)) `(let ((redo-flat-succ (f-get-global 'redo-flat-succ state)) (redo-flat-fail (f-get-global 'redo-flat-fail state))) (state-global-let* ((redo-flat-succ redo-flat-succ) (redo-flat-fail redo-flat-fail)) (ld (list ,@(and succ label `('(deflabel ,label))) ,@(and succ (list (list 'list ''ld (list 'cons ''list (list 'kwote-lst 'redo-flat-succ)) :ld-skip-proofsp succ-ld-skip-proofsp))) ,@(and fail (list (list 'list ''ld (list 'list ''list (list 'list ''quote 'redo-flat-fail)) :ld-error-action :continue :ld-pre-eval-print t))) ,@(and pbt succ label `('(pprogn (newline (proofs-co state) state) (pbt ',label))))))))))) (defun cert-op (state) ; Possible return values: ; - t ; Ordinary certification; ; ; also the Complete procedure of provisional certification ; - :create-pcert ; Pcertify (pcert0) procedure of provisional certification ; - :create+convert-pcert ; Pcertify but also creating .pcert1 file ; - :convert-pcert ; Convert (pcert1) procedure of provisional certification ; - :write-acl2x ; Write .acl2x file ; - :write-acl2xu ; Write .acl2x file, allowing uncertified sub-books ; - nil ; None of the above (let ((certify-book-info (f-get-global 'certify-book-info state))) (and certify-book-info (or (access certify-book-info certify-book-info :cert-op) t)))) (defun eval-event-lst-environment (in-encapsulatep state) (let* ((x (if in-encapsulatep '(encapsulate) nil))) (case (cert-op state) ((nil :write-acl2x :write-acl2xu) x) ((t :create+convert-pcert) (cons 'certify-book x)) (otherwise ; :create-pcert or :convert-pcert ; We need to avoid eliding locals for make-event forms when building the ; .pcert0 file, unless we are doing the :create+convert-pcert operation. We ; might as well also not bother eliding locals for building the .pcert1 file as ; well, since ultimately we expect to use the pcert0-file's make-event ; expansions (but we could reconsider this decision if a reason arises). (cons 'pcert x))))) (defun process-embedded-events (caller acl2-defaults-table skip-proofsp pkg ee-entry ev-lst index make-event-chk ctx state) ; Warning: This function uses set-w and hence may only be called within a ; revert-world-on-error. See the statement of policy in set-w. ; This function is the heart of the second pass of encapsulate, include-book, ; and certify-book. Caller is in fact one of the symbols 'encapsulate-pass-1, ; 'encapsulate-pass-2, 'include-book, 'certify-book, 'defstobj, or ; 'defabsstobj. Note: There is no function encapsulate-pass-1, but it is still ; a ``caller.'' ; Acl2-defaults-table is either a legal alist value for acl2-defaults-table or ; else is :do-not-install. If the former, then that alist is installed as the ; acl2-defaults-table (if it is not already there) after executing the events ; in ev-lst. ; The name ee-entry stands for ``embedded-event-lst'' entry. It is consed onto ; the embedded-event-lst for the duration of the processing of ev-lst. The ; length of that list indicates how deep these evs are. For example, if the ; embedded-event-lst is ; ((defstobj ...) ; (encapsulate nil) ; (include-book ...) ; (encapsulate ((p (x y) (nil nil) (nil)) ...))) ; then the ev-lst is the ``body'' of a defstobj, which occurs in the body of an ; encapsulate, which is in an include-book, which is in an encapsulate. ; The shape of an ee-entry is entirely up to the callers and the customers of ; the embedded-event-lst, with three exceptions: ; (a) the ee-entry must always be a consp; ; (b) if the car of the ee-entry is 'encapsulate then the cadr is the internal ; form signatures of the functions being constrained; and ; (c) if the car of the ee-entry is 'include-book then the cadr is the ; full-book-name. ; We refer to the signatures in (b) as insigs below and think of insigs as nil ; for all ee-entries other than encapsulates. ; Ev-lst is the list of alleged events. Pkg is the value we should use for ; current-package while we are processing the events. This affects how forms ; are prettyprinted. It also affects how the prompt looks. ; We first extend the current world of state by insigs (if caller is ; 'encapsulate-pass-2) and extend the embedded event list by ee-entry. We then ; extend further by doing each of events in ev-lst while ld-skip-proofsp is set ; to skip-proofsp, checking that they are indeed embedded-event-forms. If that ; succeeds, we restore embedded-event-lst, install the world, and return. ; If caller is not 'encapsulate-pass-2, then the return value includes an ; expansion-alist that records the result of expanding away every make-event ; call encountered in the course of processing the given ev-lst. Each pair (n ; . ev) in expansion-alist asserts that ev is the result of expanding away ; every make-event call during evaluation of the nth member of ev-lst (starting ; with index for the initial member of ev-lst), though if no such expansion ; took place then this pair is omitted. If caller is 'certify-book, then the ; return value is the cons of this expansion-alist onto either the initial ; known-package-alist, if that has not changed, or else onto the index of the ; first event that changed the known-package-alist (where the initial ; in-package event has index 0). ; If caller is 'encapsulate-pass-2, then since the final world is in STATE, we ; use the value component of the non-erroneous return triple to return the ; world extended by the signatures (and the incremented depth). That world, ; called proto-wrld3 in the encapsulate essay and below, is useful only for ; computing (via difference) the names introduced by the embedded events. We ; still need the expansion-alist described in the preceding paragraph, so the ; value returned for 'encapsulate-pass-2 is the cons of that expansion-alist ; with this proto-wrld3. ; If an error is caused by the attempt to embed the events, we print a warning ; message explaining and pass the error up. ; The world names used here are consistent with the encapsulate essay. (let* ((wrld1 (w state)) (kpa (known-package-alist state)) (old-embedded-event-lst (global-val 'embedded-event-lst wrld1)) (new-embedded-event-lst (cons ee-entry old-embedded-event-lst)) ; We now declare the signatures of the hidden functions (when we're in pass 2 ; of encapsulate), producing what we here call proto-wrld3. We also extend the ; embedded event list by ee-entry. After installing that world in state we'll ; execute the embedded events on it to produce the wrld3 of the encapsulation ; essay. (proto-wrld3 (global-set 'embedded-event-lst new-embedded-event-lst (cond ((eq caller 'encapsulate-pass-2) (intro-udf-lst (cadr ee-entry) (cddr ee-entry) wrld1)) (t wrld1))))) (let ((state (set-w 'extension proto-wrld3 state))) (er-progn (cond ((not (find-non-hidden-package-entry pkg kpa)) (er soft 'in-package "The argument to IN-PACKAGE must be a known package ~ name, but ~x0 is not. The known packages are~*1" pkg (tilde-*-&v-strings '& (strip-non-hidden-package-names kpa) #\.))) (t (value nil))) ; If we really executed an (in-package-fn pkg state) it would do the check ; above and cause an error if pkg was unknown. But we just bind ; current-package to pkg (with "unwind protection") and so we have to make the ; check ourselves. (mv-let (erp expansion-alist-and-final-kpa state) (state-global-let* ((current-package pkg) (skip-proofs-by-system ; When we pass in a non-nil value of skip-proofsp, we generally set ; skip-proofs-by-system to a non-nil value here so that install-event will not ; store a 'skip-proofs-seen marker in the world saying that the user has ; specified the skipping of proofs. However, if we are already skipping proofs ; by other than the system, then we do not want to make such an exception. (let ((user-skip-proofsp (and (ld-skip-proofsp state) (not (f-get-global 'skip-proofs-by-system state))))) (and (not user-skip-proofsp) skip-proofsp))) (ld-skip-proofsp skip-proofsp)) (er-progn ; Once upon a time, under the same conditions on caller as shown below, we ; added '(logic) to the front of ev-lst before doing the eval-event-lst below. ; But if the caller is an include-book inside a LOCAL, then the (LOGIC) event ; at the front is rejected by chk-embedded-event-form. One might wonder ; whether an erroneous ev-lst would have left us in a different state than ; here. The answer is no. If ev-lst causes an error, eval-event-lst returns ; whatever the state was at the time of the error and does not do any cleanup. ; The error is passed up to the revert-world-on-error we know is above us, ; which will undo the (logic) as well as anything else we changed. ; The above remark deals with include-book, but the issue is similar for ; defstobj except that we also need to handle ignored and irrelevant formals as ; well. Actually we may only need to handle these in the case that we do not ; allow defstobj array resizing, for the resizing and length field functions. ; But for simplicity, we always lay them down for defstobj and defabsstobj. (cond ((eq caller 'include-book) ; The following is equivalent to (logic), without the PROGN (value :invisible). ; The PROGN is illegal in Common Lisp code because its ACL2 semantics differs ; from its CLTL semantics. Furthermore, we can't write (TABLE ; acl2-defaults-table :defun-mode :logic) because, like PROGN, its CLTL ; semantics is different. (state-global-let* ((inhibit-output-lst (cons 'summary (@ inhibit-output-lst)))) (table-fn 'acl2-defaults-table '(:defun-mode :logic) state '(table acl2-defaults-table :defun-mode :logic)))) ((member-eq caller ; see comments above '(defstobj defabsstobj)) (state-global-let* ((inhibit-output-lst (cons 'summary (@ inhibit-output-lst)))) (er-progn (table-fn 'acl2-defaults-table '(:defun-mode :logic) state '(table acl2-defaults-table :defun-mode :logic)) (table-fn 'acl2-defaults-table '(:ignore-ok t) state '(table acl2-defaults-table :ignore-ok t)) (table-fn 'acl2-defaults-table '(:irrelevant-formals-ok t) state '(table acl2-defaults-table :irrelevant-formals-ok t))))) (t (value nil))) (mv-let (erp val expansion-alist final-kpa state) (pprogn (cond ((or (eq caller 'encapsulate-pass-1) (eq caller 'certify-book)) (pprogn (f-put-global 'redo-flat-succ nil state) (f-put-global 'redo-flat-fail nil state))) (t state)) (eval-event-lst index nil ev-lst (and (ld-skip-proofsp state) (not (eq caller 'certify-book))) (eval-event-lst-environment (in-encapsulatep new-embedded-event-lst nil) state) (f-get-global 'in-local-flg state) nil make-event-chk (cond ((eq caller 'certify-book) kpa) (t nil)) ctx (proofs-co state) state)) (cond (erp (pprogn (cond ((or (eq caller 'encapsulate-pass-1) (eq caller 'certify-book)) (update-for-redo-flat (- val index) ev-lst state)) (t state)) (mv erp val state))) (t (er-progn (if (eq acl2-defaults-table :do-not-install) (value nil) (maybe-install-acl2-defaults-table acl2-defaults-table state)) (value (cons expansion-alist final-kpa)))))))) (cond (erp ; The evaluation of the embedded events caused an error. If skip-proofsp is t, ; then we have a local incompatibility (because we know the events were ; successfully processed while not skipping proofs earlier). If skip-proofsp ; is nil, we simply have an inappropriate ev-lst. (cond ((member-eq caller '(defstobj defabsstobj)) (value (er hard ctx "An error has occurred while ~x0 was ~ defining the supporting functions. This is ~ supposed to be impossible! Please report this ~ error to the ACL2 implementors." caller))) (t (pprogn (warning$ ctx nil (cond ((or (eq skip-proofsp nil) (eq skip-proofsp t)) "The attempted ~x0 has failed while ~ trying to establish the ~ admissibility of one of the (local ~ or non-local) forms in ~#1~[the body ~ of the ENCAPSULATE~/the book to be ~ certified~].") ((eq caller 'encapsulate-pass-2) "The error reported above is the ~ manifestation of a local ~ incompatibility. See :DOC ~ local-incompatibility. The ~ attempted ~x0 has failed.") (t "The error reported above indicates ~ that this book is incompatible ~ with the current logical world. ~ The attempted ~x0 has failed.")) (if (or (eq caller 'encapsulate-pass-1) (eq caller 'encapsulate-pass-2)) 'encapsulate caller) (if (eq caller 'encapsulate-pass-1) 0 1)) (mv t nil state))))) (t ; The evaluation caused no error. The world inside state is the current one ; (because nothing but events were evaluated and they each install the world). ; Pop the embedded event list and install that world. We let our caller extend ; it with constraints if that is necessary. We return proto-wrld3 so the ; caller can compute the difference attributable to the embedded events. This ; is how the constraints are determined. (let ((state (set-w 'extension (global-set 'embedded-event-lst old-embedded-event-lst (w state)) state))) (cond ((eq caller 'encapsulate-pass-2) (value (cons (car expansion-alist-and-final-kpa) proto-wrld3))) ((eq caller 'certify-book) (value expansion-alist-and-final-kpa)) (t (value (car expansion-alist-and-final-kpa)))))))))))) (defun constrained-functions (exported-fns sig-fns new-trips) ; New-trips is the list of triples introduced into wrld3 from proto-wrld3, ; where wrld3 is the world created from proto-wrld3 by the second pass of an ; encapsulate, the one in which local events have been skipped. (See the ; encapsulate essay.) We return all the functions in exported-fns that, ; according to the world segment represented by new-trips, have a member of ; sig-fns among their ancestors. We include sig-fns in the result as well. ; We are allowed to return a larger set of functions, if for no other reason ; than that we can imagine adding (equal (foo x) (foo x)) for some foo in ; sig-fns to the ancestors of any member of exported-fn. ; Important: The new-trips needs to be in the same order as in wrld3, because ; of the call of instantiable-ancestors below. (cond ((endp exported-fns) sig-fns) (t (let ((ancestors (instantiable-ancestors (list (car exported-fns)) new-trips nil))) (cond ((intersectp-eq sig-fns ancestors) (cons (car exported-fns) (constrained-functions (cdr exported-fns) sig-fns new-trips))) (t (constrained-functions (cdr exported-fns) sig-fns new-trips))))))) (defun collect-logicals (names wrld) ; Names is a list of function symbols. Collect the :logic ones. (cond ((null names) nil) ((logicalp (car names) wrld) (cons (car names) (collect-logicals (cdr names) wrld))) (t (collect-logicals (cdr names) wrld)))) (defun exported-function-names (new-trips) (cond ((endp new-trips) nil) (t (let ((new-name (name-introduced (car new-trips) t))) ; Because of the second argument of t, above, new-name is known to be ; a function name. (cond (new-name (cons new-name (exported-function-names (cdr new-trips)))) (t (exported-function-names (cdr new-trips)))))))) (defun get-subversives (fns wrld) (cond ((endp fns) nil) (t (let ((j (getprop (car fns) 'justification nil 'current-acl2-world wrld))) (cond ((and j (access justification j :subversive-p)) (cons (car fns) (get-subversives (cdr fns) wrld))) (t (get-subversives (cdr fns) wrld))))))) (defun ancestral-ffn-symbs-lst (lst trips ans) (let ((fns (instantiable-ffn-symbs-lst lst trips ans nil))) (instantiable-ancestors fns trips ans))) (defun constraints-list (fns wrld acc seen) (cond ((endp fns) acc) (t (mv-let (name x) (constraint-info (car fns) wrld) (cond ((eq x *unknown-constraints*) *unknown-constraints*) (name (cond ((member-eq name seen) (constraints-list (cdr fns) wrld acc seen)) (t (constraints-list (cdr fns) wrld (union-equal x acc) (cons name seen))))) (t (constraints-list (cdr fns) wrld (cons x acc) seen))))))) (defun encapsulate-constraint (sig-fns exported-names new-trips wrld) ; This function implements the algorithm described in the first paragraph of ; the section of :DOC constraint labeled "Second cut at constraint-assigning ; algorithm." A read of that paragraph may help greatly in understanding the ; comments below. ; Sig-fns is the list of functions appearing in the signature of an ; encapsulate. Exported-names is the list of all functions introduced ; (non-locally) in the body of the encapsulate (it doesn't include sig-fns). ; New-trips is the list of property list triples added to the initial world to ; form wrld. Wrld is the result of processing the non-local events in body. ; We return (mv constraints constrained-fns subversive-fns infectious-fns fns), ; where constraints is a list of the formulas that constrain all of the ; functions listed in constrained-fns. Subversive-fns is a list of exported ; functions which are not ``tight'' wrt the initial world (see ; subversive-cliquep). Infectious-fns is the list of fns (other than ; subversive-fns) whose defuns are in the constraint. This could happen ; because some non-subversive definition is ancestral in the constraint. Fns ; is the list of all exported-names not moved forward, i.e., for which some ; function in sig-fns is ancestral. ; We do not actually rearrange anything. Instead, we compute the constraint ; formula generated by this encapsulate as though we had pulled certain events ; out before generating it. (assert$ sig-fns (let* ((fns ; Here we implement the [Front] rule mentioned in the Structured Theory paper, ; i.e. where we (virtually) move every axiomatic event that we can to be in ; front of the encapsulate. (We say "virtually" because we do not actually ; move anything, although we create a property list world that is essentially ; based our having done the moves.) What's left is the list we define here: ; the function symbols introduced by the encapsulate for which the signature ; functions are ancestral. Fns includes the signature functions. (constrained-functions (collect-logicals exported-names wrld) sig-fns new-trips)) (subversive-fns (get-subversives exported-names wrld)) (formula-lst1 ; Having in essence applied the [Front] rule, the remaining work is related to ; the [Back] rule mentioned in the Structured Theory paper, in which certain ; axiomatic events are (virtually) moved to after the encapsulate event. We ; collect up formulas that will definitely stay inside the encapsulate, ; avoiding of course formulas that are to be moved in front. We start with ; subversive definitional axioms and then gather all non-definitional formulas ; for which some signature function is ancestral -- equivalently (and this is ; what we implement here), all non-definitional formulas that mention at least ; one function symbol in fns. ; A long comment in constraints-introduced explains why we collect up ; 'constraint-lst properties here, rather than restricting ourselves to ; formulas from defun and defchoose events. (constraints-introduced new-trips fns (constraints-list subversive-fns wrld nil nil))) (constrained-fns ; The functions to receive a constraint from this encapsulate are those that ; remain introduced inside the encapsulate: the sig-fns and subversive ; functions, and all functions ancestral in one or more of the above-collected ; formulas. We intersect with fns because, as stated above, we do not want to ; include functions whose introducing axioms can be moved in front of the ; encapsulate. (intersection-eq fns (ancestral-ffn-symbs-lst formula-lst1 new-trips (append subversive-fns sig-fns)))) (infectious-fns ; The "infected" functions are those from the entire set of to-be-constrained ; functions (those introduced inside the encapsulate in spite of the [Front] ; and [Back] rules) that are neither signature functions nor subversive. (set-difference-eq (set-difference-eq constrained-fns subversive-fns) sig-fns)) (constraints ; Finally, we obtain all constraints. Recall that we built formula-lst1 above ; without including any definitions; so now we include those. Perhaps we only ; need defun and defchoose axioms at this point, having already included ; constraint-lst properties; but to be safe we go ahead and collect all ; constraints. ; We apply remove-guard-holders in order to clean up a bit. Consider for ; example: ; (defun-sk foo (x) (forall e (implies (member e x) (integerp e)))) ; If you then evaluate ; (getprop 'foo-witness 'constraint-lst nil 'current-acl2-world (w state)) ; you'll see a much simpler result, with return-last calls removed, than if we ; did not apply remove-guard-holders-lst here. (remove-guard-holders-lst (constraints-list infectious-fns wrld formula-lst1 nil)))) (mv constraints constrained-fns subversive-fns infectious-fns fns)))) (defun new-dependent-clause-processors (new-tbl old-tbl) ; New-tbl and old-tbl are values of the trusted-clause-processor-table. We ; return a list of all dependent clause-processors from new-tbl that are not ; identically specified in old-tbl. (cond ((endp new-tbl) nil) ((and (cddr (car new-tbl)) ; dependent case (not (equal (car new-tbl) (assoc-eq (caar new-tbl) old-tbl)))) (cons (caar new-tbl) (new-dependent-clause-processors (cdr new-tbl) old-tbl))) (t (new-dependent-clause-processors (cdr new-tbl) old-tbl)))) (defun bogus-exported-compliants (names exports-with-sig-ancestors sig-fns wrld) ; Names is a list of function symbols exported from an encapsulate event. ; Exports-with-sig-ancestors contains each element of names that has at least ; one signature function of that encapsulate among its ancestors. We return ; those elements of names whose body or guard has at least one ancestor in ; sig-fns, except for those that are constrained, because the guard proof ; obligations may depend on local properties. Consider the following example. ; (encapsulate ; ((f (x) t)) ; (local (defun f (x) (declare (xargs :guard t)) (consp x))) ; (defun g (x) ; (declare (xargs :guard (f x))) ; (car x))) ; Outside the encapsulate, we do not know that (f x) suffices as a guard for ; (car x). ; We considered exempting non-executable functions, but if we are to bother ; with their guard verification, it seems appropriate to insist that the guard ; proof obligation really does hold in the theory produced by the encapsulate, ; not merely in the temporary theory of the first pass of the encapsulate. ; See also the comment about this function in intro-udf. (cond ((endp names) nil) ((and (eq (symbol-class (car names) wrld) :common-lisp-compliant) (not (getprop (car names) 'constrainedp nil 'current-acl2-world wrld)) ; We can only trust guard verification for (car names) if its guard proof ; obligation can be moved forward. We could in principle save that proof ; obligation, or perhaps we could recompute it; and then we could check that no ; signature function is ancestral. But an easy sufficient condition for ; trusting that the guard proof obligation doesn't depend on functions ; introduced in the encapsulate, and one that does not seem overly restrictive, ; is to insist that neither the body of the function nor its guard have any ; signature functions as ancestors. (or (member-eq (car names) exports-with-sig-ancestors) (intersectp-eq sig-fns (instantiable-ancestors (all-fnnames (guard (car names) nil wrld)) wrld nil)))) (cons (car names) (bogus-exported-compliants (cdr names) exports-with-sig-ancestors sig-fns wrld))) (t (bogus-exported-compliants (cdr names) exports-with-sig-ancestors sig-fns wrld)))) (defun encapsulate-pass-2 (insigs kwd-value-list-lst ev-lst saved-acl2-defaults-table only-pass-p ctx state) ; Warning: This function uses set-w and hence may only be called within a ; revert-world-on-error. See the statement of policy in set-w. ; This is the second pass of the encapsulate event. We assume that the ; installed world in state is wrld1 of the encapsulate essay. We assume that ; chk-acceptable-encapsulate1 has approved of wrld1 and ; chk-acceptable-encapsulate2 has approved of the wrld2 generated in with ; ld-skip-proofsp nil. Insigs is the internal form signatures list. We either ; cause an error and return a state in which wrld1 is current or else we return ; normally and return a state in which wrld3 of the essay is current. In the ; case of normal return and only-pass-p = nil, the value is a list containing ; * constrained-fns - the functions for which a new constraint-lst will ; be stored ; * constraints - the corresponding list of constraints ; * exported-names - the exported names ; * subversive-fns - the subversive (non-tight) functions encountered ; * infectious-fns - list of (non-subversive) fns whose defun equations were ; moved into the constraint ; However, if only-pass-p = t, then the value returned is an expansion-alist ; mapping, in reverse increasing order, indices of events in ev-lst to the ; result of expanding away make-event calls. ; This information is used by the output routines. ; Note: The function could be declared to return five values, but we would ; rather use the standard state and error primitives and so it returns three. (let* ((wrld1 (w state)) (saved-trusted-clause-processor-table (table-alist 'trusted-clause-processor-table wrld1))) (er-let* ((expansion-alist-and-proto-wrld3 ; The following process-embedded-events, which requires world reversion on ; errors, is protected by virtue of being in encapsulate-pass-2, which also ; requires such reversion. ; Note: The proto-wrld3 returned below is wrld1 above extended by the ; signatures. The installed world after this process-embedded-events has the ; non-local events of ev-lst in it. (state-global-let* ((in-local-flg ; As we start processing the events in the encapsulate, we are no longer in the ; lexical scope of LOCAL for purposes of disallowing setting of the ; acl2-defaults-table. (and (f-get-global 'in-local-flg state) 'local-encapsulate))) (process-embedded-events 'encapsulate-pass-2 saved-acl2-defaults-table 'include-book (current-package state) (list* 'encapsulate insigs ; The non-nil final cdr signifies that we are in pass 2 of encapsulate; see ; context-for-encapsulate-pass-2. (or kwd-value-list-lst t)) ev-lst 0 ; If only-pass-p is t then we need to allow make-event with :check-expansion ; that is not a cons. Consider the following example. ; (make-event '(encapsulate () ; (make-event '(defun test3 (x) (cons x x)))) ; :check-expansion t) ; This event has the following expansion (eliding uninteresting parts with #). ; (record-expansion # ; (make-event '(encapsulate () ; (make-event '(defun test3 (x) (cons x x)))) ; :check-expansion ; (encapsulate () ; (record-expansion # ; (defun test3 (x) (cons x x)))))) ; The outermost make-event will initially expand the value of the quoted ; expression after it, yielding this expansion. ; (encapsulate () ; (make-event '(defun test3 (x) (cons x x)))) ; When this encapsulate skips its first pass, it will encounter the indicated ; make-event, which has no expansion. (not only-pass-p) ; make-event-chk ctx state)))) (let* ((expansion-alist (car expansion-alist-and-proto-wrld3)) (proto-wrld3 (cdr expansion-alist-and-proto-wrld3)) (wrld (w state)) (new-trips (new-trips wrld proto-wrld3 nil nil)) (exported-names (exported-function-names new-trips)) (trusted-clause-processor-table (table-alist 'trusted-clause-processor-table (w state))) (new-dependent-cl-procs (and insigs ; else cl-procs belong to a parent encapsulate (not (equal ; optimization trusted-clause-processor-table saved-trusted-clause-processor-table)) (new-dependent-clause-processors trusted-clause-processor-table saved-trusted-clause-processor-table)))) (cond ((and new-dependent-cl-procs exported-names) (er soft ctx "A dependent clause-processor that has a promised ~ encapsulate (partial theory) must introduce only the ~ functions listed in that encapsulate's signature. ~ However, the dependent clause-processor ~x0 is ~ introduced with an encapsulate whose signature's list ~ of names, ~x1, is missing the function name~#2~[~/s~] ~ ~&2 that is also introduced by that encapsulate. See ~ :DOC define-trusted-clause-processor." (car new-dependent-cl-procs) (strip-cars insigs) exported-names)) ((and expansion-alist (not only-pass-p)) (value (er hard ctx "Implementation error: Unexpected expansion-alist ~ ~x0 for second pass of encapsulate. Please ~ contact the ACL2 implementors." expansion-alist))) ((null insigs) (value (if only-pass-p expansion-alist (list nil nil exported-names)))) (new-dependent-cl-procs ; so (not exported-names) by test above (let* ((sig-fns (strip-cars insigs)) (state (set-w 'extension (putprop-constraints (car sig-fns) (cdr sig-fns) *unknown-constraints* (car new-dependent-cl-procs) wrld) state))) (value (if only-pass-p expansion-alist (list sig-fns *unknown-constraints* new-dependent-cl-procs nil nil))))) (t ; We are about to collect the constraint generated by this encapsulate on the ; signature functions. We ``optimize'' one common case: if this is a top-level ; encapsulation with a non-empty signature (so it introduces some constrained ; functions but no superior encapsulate does so), with no dependent ; clause-processor and no encapsulate in its body that introduces any ; constrained functions, then we may use the theorems [Front] and [Back] of the ; ``Structured Theory'' paper to ``rearrange'' the events within this ; encapsulate. Otherwise, we do not rearrange things. Of course, the whole ; point is moot if this encapsulate has an empty signature -- there will be no ; constraints anyway. (let* ((new-trips (new-trips wrld wrld1 nil nil)) (sig-fns (strip-cars insigs))) (mv-let (constraints constrained-fns subversive-fns infectious-fns exports-with-sig-ancestors) (encapsulate-constraint sig-fns exported-names new-trips wrld) (let* ((wrld2 (putprop-constraints (car sig-fns) (remove1-eq (car sig-fns) constrained-fns) constraints nil (if constrained-fns (assert$ (subsetp-eq subversive-fns constrained-fns) (assert$ (subsetp-eq infectious-fns constrained-fns) (putprop-x-lst1 constrained-fns 'siblings constrained-fns wrld))) wrld))) (state (set-w 'extension wrld2 state)) (bogus-exported-compliants (bogus-exported-compliants exported-names exports-with-sig-ancestors sig-fns wrld2))) (cond (bogus-exported-compliants (er soft ctx "For the following function~#0~[~/s~] introduced ~ by this encapsulate event, guard verification ~ may depend on local properties that are not ~ exported from that encapsulate event: ~&0." bogus-exported-compliants)) (t (value (if only-pass-p expansion-alist (list constrained-fns constraints exported-names subversive-fns infectious-fns)))))))))))))) ; Here I have collected a sequence of encapsulates to test the implementation. ; After each is an undo. They are not meant to co-exist. Just eval each ; of the forms in this comment. You should never get an error. ; (set-state-ok t) ; ; (defun test (val) ; (declare (xargs :mode :program)) ; (if val ; 'ok ; (er hard 'test "This example failed!"))) ; ; ; I start with a collection of simple encapsulates, primarily to test the ; ; handling of signatures in their three forms. I need a stobj. ; ; (defstobj $s x y) ; ; ; Here is a simple, typical encapsulate. ; (encapsulate ((p (x) t)) ; (local (defun p (x) (declare (ignore x)) t)) ; (defthm booleanp-p (booleanp (p x)))) ; ; (test ; (equal ; (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((booleanp (P X))))) ; ; (u) ; ; ; The next set just look for errors that should never happen. ; ; The following all cause errors. ; ; (encapsulate (((p x) => x)) ; (local (defun p (x) x))) ; ; (encapsulate ((p x) => x) ; (local (defun p (x) x))) ; ; (encapsulate (((p x $s) => (mv x $s))) ; (local (defun p (x $s) (declare (xargs :stobjs ($s))) (mv x $s)))) ; ; (encapsulate (((p * state $s) => state)) ; (local (defun p (x state $s) ; (declare (xargs :stobjs nil) (ignore x $s)) ; state))) ; ; (encapsulate (((p * state *) => $s)) ; (local (defun p (x state $s) ; (declare (xargs :stobjs $s) (ignore x state)) ; $s))) ; ; ; Here are some of the "same" errors provoked in the old notation. ; ; (encapsulate ((p (x $s) (mv * $s) :stobjs *)) ; (local (defun p (x $s) (declare (xargs :stobjs ($s))) (mv x $s)))) ; ; (encapsulate ((p (* state $s) state)) ; (local (defun p (x state $s) ; (declare (xargs :stobjs nil) (ignore x $s)) ; state))) ; ; (encapsulate ((p (y state $s) $s)) ; (local (defun p (x state $s) ; (declare (xargs :stobjs $s) (ignore x state)) ; $s))) ; ; (encapsulate ((p (x state y) $s)) ; (local (defun p (x state $s) ; (declare (xargs :stobjs $s) (ignore x state)) ; $s))) ; ; ; The rest of my tests are concerned with the constraints produced. ; ; ; Here is one that contains a function that can be moved forward out ; ; of encapsulate, even though it is used in the constraint. Note that ; ; not every theorem proved becomes a constraint. The theorem evp-+ is ; ; moved forward too. ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (declare (ignore x)) 2)) ; (defun evp (n) (if (zp n) t (if (zp (- n 1)) nil (evp (- n 2))))) ; (defthm evp-+ (implies (and (integerp i) ; (<= 0 i) ; (evp i) ; (integerp j) ; (<= 0 j) ; (evp j)) ; (evp (+ i j)))) ; (defthm evp-p (evp (p x)))) ; ; (test ; (equal ; (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((EVP (P X))))) ; ; (u) ; ; ; This illustrates a function which uses the signature function p but ; ; which can be moved back out of the encapsulate. The only constraint ; ; on p is (EVP (P X)). ; ; ; But if the function involves the constrained function, it cannot ; ; be moved forward. It may be moved back, or it may become part of the ; ; constraint, depending on several things. ; ; ; Case 1. The function uses p in a benign way and nothing is proved ; ; about the function. ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (ifix x))) ; (defun mapp (x) ; (if (consp x) ; (cons (p (car x)) (mapp (cdr x))) ; nil)) ; (defthm integerp-p (integerp (p x)))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((integerp (p x)))) ; (equal (getprop 'mapp 'constraint-lst nil 'current-acl2-world (w state)) ; nil))) ; ; (u) ; ; ; The constraint, above, on p is (INTEGERP (P X)). ; ; ; Case 2. The function is subversive, i.e., uses p in a way critical to ; ; its termination. ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (cdr x))) ; (defthm len-p (implies (consp x) (< (len (p x)) (len x)))) ; (defun bad (x) ; (if (consp x) ; (not (bad (p x))) ; t))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; ; Modified for v3-5: ; (reverse '((EQUAL (BAD X) ; (IF (CONSP X) ; (NOT (BAD (P X))) ; 'T)) ; ; (IF (EQUAL (BAD X) 'T) ; ; 'T ; ; (EQUAL (BAD X) 'NIL)) ; (IMPLIES (CONSP X) ; (< (LEN (P X)) (LEN X)))))) ; (equal (getprop 'bad 'constraint-lst nil 'current-acl2-world (w state)) ; 'p))) ; ; (u) ; ; ; The constraint above is associated both with p and bad. That is, if you ; ; functionally instantiate p, the new function must satisfy the axiom for bad ; ; too, which means you must instantiate bad. Similarly, if you instantiate ; ; bad, you must instantiate p. ; ; ; It would be better if you did this: ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (cdr x))) ; (defthm len-p (implies (consp x) (< (len (p x)) (len x))))) ; ; (test ; (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((IMPLIES (CONSP X) ; (< (LEN (P X)) (LEN X)))))) ; ; ; The only constraint on p is ; ; (IMPLIES (CONSP X) (< (LEN (P X)) (LEN X))). ; ; Now you can define bad outside: ; ; (defun bad (x) ; (declare (xargs :measure (len x))) ; (if (consp x) ; (not (bad (p x))) ; t)) ; ; (u) ; (u) ; ; ; Case 3. The function uses p in a benign way but something is proved ; ; about the function, thus constraining p. ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (ifix x))) ; (defun mapp (x) ; (if (consp x) ; (cons (p (car x)) (mapp (cdr x))) ; nil)) ; (defthm mapp-is-a-list-of-ints ; (integer-listp (mapp x)))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((EQUAL (MAPP X) ; (IF (CONSP X) ; (CONS (P (CAR X)) (MAPP (CDR X))) ; 'NIL)) ; ; No longer starting with v3-5: ; ; (TRUE-LISTP (MAPP X)) ; (INTEGER-LISTP (MAPP X)))) ; (equal (getprop 'mapp 'constraint-lst nil 'current-acl2-world (w state)) ; 'p))) ; ; (u) ; ; ; The constraint above, on both p and mapp, is ; ; (AND (EQUAL (MAPP X) ; ; (AND (CONSP X) ; ; (CONS (P (CAR X)) (MAPP (CDR X))))) ; ; (TRUE-LISTP (MAPP X)) ; ; (INTEGER-LISTP (MAPP X))) ; ; ; Here is another case of a subversive definition, illustrating that ; ; we do not just check whether the function uses p but whether it uses ; ; p ancestrally. ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (cdr x))) ; (defun bad1 (x) (p x)) ; (defun bad2 (x) ; (if (consp x) ; (not (bad2 (bad1 x))) ; t))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((EQUAL (BAD1 X) (P X)) ; (EQUAL (BAD2 X) ; (IF (CONSP X) ; (NOT (BAD2 (BAD1 X))) ; 'T)) ; ; No longer starting with v3-5: ; ; (IF (EQUAL (BAD2 X) 'T) ; ; 'T ; ; (EQUAL (BAD2 X) 'NIL)) ; )) ; (equal (getprop 'bad1 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (equal (getprop 'bad2 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (equal (getprop 'bad2 'induction-machine nil ; 'current-acl2-world (w state)) ; nil))) ; ; ; (u) ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (cdr x))) ; (defun bad1 (x) ; (if (consp x) (bad1 (cdr x)) (p x))) ; (defun bad2 (x) ; (if (consp x) ; (not (bad2 (bad1 x))) ; t))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((EQUAL (BAD1 X) ; (IF (CONSP X) ; (BAD1 (CDR X)) ; (P X))) ; (EQUAL (BAD2 X) ; (IF (CONSP X) ; (NOT (BAD2 (BAD1 X))) ; 'T)) ; ; No longer starting with v3-5: ; ; (IF (EQUAL (BAD2 X) 'T) ; ; 'T ; ; (EQUAL (BAD2 X) 'NIL)) ; )) ; (equal (getprop 'bad1 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (equal (getprop 'bad2 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (not (equal (getprop 'bad1 'induction-machine nil ; 'current-acl2-world (w state)) ; nil)) ; (equal (getprop 'bad2 'induction-machine nil ; 'current-acl2-world (w state)) ; nil))) ; ; (u) ; ; ; Once up a time we had a bug in encapsulate, because subversiveness was ; ; based on the induction machine rather than the termination machine ; ; and no induction machine is constructed for mutually recursive definitions. ; ; Here is an example that once led to unsoundness: ; ; (encapsulate ; ((fn1 (x) t)) ; (local (defun fn1 (x) ; (cdr x))) ; (mutual-recursion ; (defun fn2 (x) ; (if (consp x) ; (not (fn3 (fn1 x))) ; t)) ; (defun fn3 (x) ; (if (consp x) ; (not (fn3 (fn1 x))) ; t)))) ; ; (test ; (and (equal (getprop 'fn1 'constraint-lst nil 'current-acl2-world (w state)) ; ; Reversed as shown starting with v3-5: ; '((EQUAL (FN2 X) ; (IF (CONSP X) ; (NOT (FN3 (FN1 X))) ; 'T)) ; ; No longer starting with v3-5: ; ; (IF (EQUAL (FN2 X) 'T) ; ; 'T ; ; (EQUAL (FN2 X) 'NIL)) ; (EQUAL (FN3 X) ; (IF (CONSP X) ; (NOT (FN3 (FN1 X))) ; 'T)) ; ; No longer starting with v3-5: ; ; (IF (EQUAL (FN3 X) 'T) ; ; 'T ; ; (EQUAL (FN3 X) 'NIL)) ; )) ; (equal (getprop 'fn2 'constraint-lst nil 'current-acl2-world (w state)) ; 'fn1) ; (equal (getprop 'fn3 'constraint-lst nil 'current-acl2-world (w state)) ; 'fn1) ; (equal (getprop 'fn2 'induction-machine nil ; 'current-acl2-world (w state)) ; nil) ; (equal (getprop 'fn3 'induction-machine nil ; 'current-acl2-world (w state)) ; nil))) ; ; ; Now, fn1, fn2, and fn3 share both definitional constraints. ; ; ; It is possible to prove the following lemma ; ; (defthm lemma ; (not (equal (fn1 '(a)) '(a))) ; :rule-classes nil ; :hints (("Goal" :use (:instance fn3 (x '(a)))))) ; ; ; But in the unsound version it was then possible to functionally ; ; instantiate it, choosing the identity function for fn1, to derive ; ; a contradiction. Here is the old killer: ; ; ; (defthm bad ; ; nil ; ; :rule-classes nil ; ; :hints (("Goal" :use (:functional-instance lemma (fn1 identity))))) ; ; (u) ; (u) ; ; ; Now when you do that you have to prove an impossible theorem about ; ; fn3, namely ; ; ; (equal (fn3 x) (if (consp x) (not (fn3 x)) t)) ; ; ; The only way to prove this is to show that nothing is a cons. ; ; ; This examples shows that a function can call a subversive one and ; ; not be subversive. ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (cdr x))) ; (defun bad1 (x) (p x)) ; tight: non-recursive ; ; (defun bad2 (x) ; not tight: recursive call involves ; (if (consp x) ; a fn (bad1) defined inside the encap ; (not (bad2 (bad1 x))) ; t)) ; (defun bad3 (x) ; (if (consp x) ; (bad2 (bad3 (cdr x))) ; nil))) ; tight: even though it calls bad2 ; ; ; Bad2 is swept into the constraint because it is not tight (subversive). Bad1 ; ; is swept into it because it introduces a function (bad1) used in the enlarged ; ; constraint. Bad3 is not swept in. Indeed, bad3 is moved [Back]. ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((EQUAL (BAD1 X) (P X)) ; (EQUAL (BAD2 X) ; (IF (CONSP X) ; (NOT (BAD2 (BAD1 X))) ; 'T)) ; ; No longer starting with v3-5: ; ; (IF (EQUAL (BAD2 X) 'T) ; ; 'T ; ; (EQUAL (BAD2 X) 'NIL)) ; )) ; (equal (getprop 'bad1 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (equal (getprop 'bad2 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (equal (getprop 'bad3 'constraint-lst nil 'current-acl2-world (w state)) ; nil) ; (equal (getprop 'bad2 'induction-machine nil ; 'current-acl2-world (w state)) ; nil) ; (not (equal (getprop 'bad3 'induction-machine nil ; 'current-acl2-world (w state)) ; nil)))) ; ; (u) ; ; ; Now what about nested encapsulates? ; ; ; Let us first consider the two simplest cases: ; ; (encapsulate ((p (x) t)) ; (local (defun p (x) (declare (ignore x)) 23)) ; (encapsulate nil ; (defthm lemma1 (equal x x) :rule-classes nil) ; (defthm main (equal x x) :rule-classes nil)) ; (defthm integerp-p (integerp (p x)))) ; ; ; We are permitted to rearrange this, because the inner encap has a nil ; ; signature. So we get what we expect: ; ; (test ; (equal ; (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((integerp (P X))))) ; ; (u) ; ; ; The other simple case is ; ; (encapsulate nil ; (defthm lemma1 (equal x x) :rule-classes nil) ; (defthm main (equal x x) :rule-classes nil) ; (encapsulate ((p (x) t)) ; (local (defun p (x) (declare (ignore x)) 23)) ; (defun benign (x) ; (if (consp x) (benign (cdr x)) x)) ; (defthm integerp-p (integerp (p x))))) ; ; ; Note that benign doesn't constrain p, because the containing encap ; ; contains no sig fns. ; ; (test ; (equal ; (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((integerp (P X))))) ; ; (u) ; ; ; If we have a pair of encaps, each of which introduces a sig fn, ; ; we lost the ability to rearrange things in v3-6-1 but not v4-0: ; ; (encapsulate ((p1 (x) t)) ; (local (defun p1 (x) x)) ; (defun benign1 (x) ; (if (consp x) (benign1 (cdr x)) t)) ; (defthm p1-constraint (benign1 (p1 x))) ; (encapsulate ((p2 (x) t)) ; (local (defun p2 (x) x)) ; (defun benign2 (x) ; (if (consp x) (benign2 (cdr x)) t)) ; (defthm p2-constraint (benign2 (p2 x))))) ; ; (test ; (and (equal (getprop 'p1 'constraint-lst nil 'current-acl2-world (w state)) ; '((BENIGN1 (P1 X)))) ; (equal (getprop 'p2 'constraint-lst nil 'current-acl2-world (w state)) ; '((BENIGN2 (P2 X)))) ; (equal (getprop 'benign2 'constraint-lst nil 'current-acl2-world (w state)) ; nil) ; (equal (getprop 'benign1 'constraint-lst nil 'current-acl2-world (w state)) ; nil))) ; ; (u) ; ; (encapsulate ((f1 (x) t)) ; (local (defun f1 (x) (declare (ignore x)) 0)) ; (defun bad (x) ; (if (consp x) ; (if (and (integerp (bad (cdr x))) ; (<= 0 (bad (cdr x))) ; (< (bad (cdr x)) (acl2-count x))) ; (bad (bad (cdr x))) ; (f1 x)) ; 0))) ; ; (test ; (and (equal (getprop 'f1 'constraint-lst nil 'current-acl2-world (w state)) ; ; No longer generates this constraint starting with v3-5: ; ; '((EQUAL (BAD X) ; ; (IF (CONSP X) ; ; (IF (IF (INTEGERP (BAD (CDR X))) ; ; (IF (NOT (< (BAD (CDR X)) '0)) ; ; (< (BAD (CDR X)) (ACL2-COUNT X)) ; ; 'NIL) ; ; 'NIL) ; ; (BAD (BAD (CDR X))) ; ; (F1 X)) ; ; '0))) ; nil) ; (equal ; (getprop 'bad 'constraint-lst nil 'current-acl2-world (w state)) ; ; No longer starting with v3-5: ; ; 'f1 ; nil ; ) ; ; No longer subversive, starting with v3-5: ; ; (equal ; (getprop 'bad 'induction-machine nil 'current-acl2-world (w state)) ; ; nil) ; )) ; ; (u) ; ; ; ; Here is a sample involving defchoose. In this example, the signature ; ; function is ancestral in the defchoose axiom. ; ; (encapsulate ((p (y x) t)) ; (local (defun p (y x) (member-equal y x))) ; (defchoose witless x (y) (p y x)) ; (defthm consp-witless ; (consp (witless y)) ; :rule-classes :type-prescription ; :hints (("Goal" :use (:instance witless (x (cons y nil))))))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((IMPLIES (P Y X) ; ((LAMBDA (X Y) (P Y X)) (WITLESS Y) Y)) ; (CONSP (WITLESS Y)))) ; (equal ; (getprop 'witless 'constraint-lst nil 'current-acl2-world (w state)) ; 'p) ; (equal ; (getprop 'witless 'defchoose-axiom nil 'current-acl2-world (w state)) ; '(IMPLIES (P Y X) ; ((LAMBDA (X Y) (P Y X)) (WITLESS Y) Y))))) ; ; (u) ; ; ; and in this one it is not, indeed, the defchoose function can be ; ; moved to the [Front] even though it is used in the constraint of p. ; ; (encapsulate ((p (y x) t)) ; (local (defun p (y x) (member-equal y x))) ; (defchoose witless x (y) (member-equal y x)) ; (defthm p-constraint (p y (witless y)) ; :hints (("Goal" :use (:instance witless (x (cons y nil))))))) ; ; (test ; (and (equal (getprop 'p 'constraint-lst nil 'current-acl2-world (w state)) ; '((p y (witless y)))) ; (equal ; (getprop 'witless 'constraint-lst nil 'current-acl2-world (w state)) ; nil) ; (equal ; (getprop 'witless 'defchoose-axiom nil 'current-acl2-world (w state)) ; '(IMPLIES (member-equal Y X) ; ((LAMBDA (X Y) (member-equal Y X)) (WITLESS Y) Y))))) ; ; (u) ; ; (quote (the end of my encapsulate tests -- there follow two undo commands)) ; (u) ; (u) (defun tilde-@-abbreviate-object-phrase (x) ; This function produces a tilde-@ phrase that describes the ; object x, especially if it is a list. This is just a hack ; used in error reporting. (cond ((atom x) (msg "~x0" x)) ((symbol-listp x) (cond ((< (length x) 3) (msg "~x0" x)) (t (msg "(~x0 ... ~x1)" (car x) (car (last x)))))) ((atom (car x)) (cond ((and (consp (cdr x)) (atom (cadr x))) (msg "(~x0 ~x1 ...)" (car x) (cadr x))) (t (msg "(~x0 ...)" (car x))))) ((atom (caar x)) (cond ((and (consp (cdar x)) (atom (cadar x))) (msg "((~x0 ~x1 ...) ...)" (caar x) (cadar x))) (t (msg "((~x0 ...) ...)" (caar x))))) (t "(((...) ...) ...)"))) (defun encapsulate-ctx (signatures form-lst) ; This function invents a suitable error context, ctx, for an ; encapsulate with the given signatures and form-lst. The args have ; not been translated or checked. Thus, this function is rough. ; However, we have to have some way to describe to the user which ; encapsulation is causing the problem, since we envision them often ; being nested. Our guess is that the signatures, if non-nil, will be ; the most recognizable aspect of the encapsulate. Otherwise, we'll ; abbreviate the form-lst. (cond (signatures (cond ((and (consp signatures) (consp (car signatures)) (consp (caar signatures))) (msg "( ENCAPSULATE (~@0 ...) ...)" (tilde-@-abbreviate-object-phrase (car signatures)))) (t (msg "( ENCAPSULATE ~@0 ...)" (tilde-@-abbreviate-object-phrase signatures))))) (form-lst (msg "( ENCAPSULATE NIL ~@0 ...)" (tilde-@-abbreviate-object-phrase (car form-lst)))) (t "( ENCAPSULATE NIL)"))) (defun print-encapsulate-msg1 (insigs form-lst state) (declare (ignore insigs)) (cond ((ld-skip-proofsp state) state) (t (io? event nil state (form-lst) (fms "To verify that the ~#0~[~/~n1 ~]encapsulated event~#0~[~/s~] ~ correctly extend~#0~[s~/~] the current theory we will evaluate ~ ~#0~[it~/them~]. The theory thus constructed is only ~ ephemeral.~|~#2~[~%Encapsulated Event~#0~[~/s~]:~%~/~]" (list (cons #\0 form-lst) (cons #\1 (length form-lst)) (cons #\2 (if (eq (ld-pre-eval-print state) :never) 1 0))) (proofs-co state) state nil))))) (defun print-encapsulate-msg2 (insigs form-lst state) (declare (ignore insigs)) (cond ((ld-skip-proofsp state) state) (t (io? event nil state (form-lst) (fms "End of Encapsulated Event~#0~[~/s~].~%" (list (cons #\0 form-lst)) (proofs-co state) state nil))))) (defun print-encapsulate-msg3/exported-names (insigs lst) ; This returns a list of tilde-@ phrases. The list always has either ; 0 or 1 things in it. The single element describes the exports of ; an encapsulation (if any). Insigs is the list of internal form ; signatures of the constrained fns. (cond ((null lst) ; Say nothing if there are no additional names. nil) (insigs (list (msg "In addition to ~&0, we export ~&1.~|~%" (strip-cars insigs) lst))) (t (list (msg "We export ~&0.~|~%" lst))))) (defun print-encapsulate-msg3/constraints (constrained-fns constraints clause-processors wrld) ; The clause-processors argument is ignored unless constraints is ; *unknown-constraints*. (cond ((null constraints) ; It's tempting in this case to say something like, "No new constraints are ; associated with any function symbols." However, one could argue with that ; statement, since DEFUN introduces constraints in some sense, for example. ; This problem does not come up if there are constrained functions, since in ; that case (below), we are honestly reporting all of the constraints on the ; indicated functions. So, we simply print nothing in the present case. nil) ((null constrained-fns) (er hard 'print-encapsulate-msg3/constraints "We had thought that the only way that there can be constraints is if ~ there are constrained functions. See ~ print-encapsulate-msg3/constraints.")) ((eq constraints *unknown-constraints*) (list (msg "An unknown constraint is associated with ~#0~[the function~/both ~ of the functions~/every one of the functions~] ~&1. Note that ~ this encapsulate introduces dependent clause processor~#2~[~/s~] ~ ~&2.~|~%" (let ((n (length constrained-fns))) (case n (1 0) (2 1) (otherwise 2))) constrained-fns clause-processors))) (t (list (msg "The following constraint is associated with ~#0~[the ~ function~/both of the functions~/every one of the functions~] ~ ~&1:~|~%~p2~|" (let ((n (length constrained-fns))) (case n (1 0) (2 1) (otherwise 2))) constrained-fns (untranslate (conjoin constraints) t wrld)))))) (defun print-encapsulate-msg3 (ctx insigs form-lst exported-names constrained-fns constraints-introduced subversive-fns infectious-fns wrld state) ; This function prints a sequence of paragraphs, one devoted to each ; constrained function (its arities and constraint) and one devoted to ; a summary of the other names created by the encapsulation. ; In the case that constrained-fns is *unknown-constraints*, exported-names is ; actually the list of dependent clause-processors designated by the ; encapsulate. (cond ((ld-skip-proofsp state) state) (t (io? event nil state (infectious-fns ctx subversive-fns wrld constraints-introduced constrained-fns exported-names insigs form-lst) (pprogn (fms "Having verified that the encapsulated event~#0~[ ~ validates~/s validate~] the signatures of the ~ ENCAPSULATE event, we discard the ephemeral theory ~ and extend the original theory as directed by the ~ signatures and the non-LOCAL events.~|~%~*1" (list (cons #\0 form-lst) (cons #\1 (list "" "~@*" "~@*" "~@*" (append (print-encapsulate-msg3/exported-names insigs exported-names) (print-encapsulate-msg3/constraints constrained-fns constraints-introduced exported-names wrld) )))) (proofs-co state) state (term-evisc-tuple nil state)) (print-defun-msg/signatures (strip-cars insigs) wrld state) (if subversive-fns (warning$ ctx "Infected" "Note that ~&0 ~#0~[is~/are~] ``subversive.'' See ~ :DOC subversive-recursions. Thus, ~#0~[its ~ definitional equation infects~/their definitional ~ equations infect~] the constraint of this ~ en~-cap~-su~-la~-tion. Furthermore, ~#0~[this ~ function~/these functions~] will not suggest any ~ induction schemes or type-prescription rules to the ~ theorem prover. If possible, you should remove ~ ~#0~[this definition~/these definitions~] from the ~ encapsulate and introduce ~#0~[it~/them~] ~ afterwards. A constraint containing a definitional ~ equation is often hard to use in subsequent ~ functional instantiations." subversive-fns) state) (if infectious-fns (warning$ ctx "Infected" "Note that the definitional equation~#0~[~/s~] for ~ ~&0 infect~#0~[s~/~] the constraint of this ~ en~-cap~-su~-la~-tion. That can be caused because a ~ function ancestrally involves the constrained ~ functions of an encapsulate and is ancestrally ~ involved in the constraining theorems of those ~ functions. In any case, if at all possible, you ~ should move ~#0~[this definition~/these ~ definitions~] out of the encapsulation. A ~ constraint containing a definitional equation is ~ often hard to use in subsequent functional ~ instantiations. See :DOC subversive-recursions for ~ a discussion of related issues." infectious-fns) state)))))) (mutual-recursion (defun find-first-non-local-name (x) ; Keep this in sync with chk-embedded-event-form and primitive-event-macros; ; see comments below. ; This function is used heuristically to help check redundancy of encapsulate ; events. ; X is allegedly an embedded event form, though we do not guarantee this. It ; may be a call of some user macro and thus completely unrecognizable to us. ; But it could be a call of one of our primitive fns. We are interested in the ; question "If x is successfully executed, what is a logical name it will ; introduce?" Since no user event will introduce nil, we use nil to indicate ; that we don't know about x (or, equivalently, that it is some user form we ; don't recognizer, or that it introduces no names, or that it is ill-formed ; and will blow up). Otherwise, we return a logical name that x will create. ; We are interested only in returning symbols, not book names or packages. (let ((val (case-match x ; We are typically looking at events inside an encapsulate form. Below, we ; handle local and defun first, since these are the most common. We then ; handle all event forms in (primitive-event-macros) that introduce a new name ; that is a symbol. Finally, we deal with compound event forms that are ; handled by chk-embedded-event-form. (('local . &) nil) (('defun name . &) name) ; Others from (primitive-event-macros); see comment above. (('defaxiom name . &) name) (('defchoose name . &) name) (('defconst name . &) name) (('deflabel name . &) name) (('defmacro name . &) name) (('deftheory name . &) name) (('defuns (name . &) . &) name) (('defstobj name . &) name) (('defabsstobj name . &) name) (('defthm name . &) name) (('encapsulate (((name . &) arrow . &) . &) . &) (and (symbolp arrow) (equal (symbol-name arrow) "=>") name)) (('encapsulate ((name . &) . &) . &) name) (('encapsulate nil . ev-lst) (find-first-non-local-name-lst ev-lst)) (('mutual-recursion ('defun name . &) . &) name) (('progn . ev-lst) (find-first-non-local-name-lst ev-lst)) ; Keep the following in sync with chk-embedded-event-form; see comment above. ((sym . lst) (and (member-eq sym '(skip-proofs with-output with-prover-step-limit with-prover-time-limit)) (find-first-non-local-name (car (last lst))))) (& nil)))) (and (symbolp val) val))) (defun find-first-non-local-name-lst (lst) ; Challenge: If lst is a true list of embedded event forms that is ; successfully processed with ld-skip-proofsp nil, name one name that ; would be created. Now lst might not be a list of embedded event ; forms. Or the forms might be doomed to cause errors or might be ; unrecognizable user macro calls. So we return nil if we can't spot a ; suitable name. Otherwise we return a name. The only claim made is ; this: if we return non-nil and lst were successfully processed, then ; that name is a logical name that would be created. Consequently, if ; that name is new in a world, we know that this lst has not been ; processed before. (cond ((atom lst) nil) (t (or (find-first-non-local-name (car lst)) (find-first-non-local-name-lst (cdr lst)))))) ) (defun corresponding-encap-events (old-evs new-evs ans) (cond ((endp old-evs) (and (null new-evs) ans)) ((endp new-evs) nil) (t (let ((old-ev (car old-evs)) (new-ev (car new-evs))) (cond ((equal old-ev new-ev) (corresponding-encap-events (cdr old-evs) (cdr new-evs) ans)) ((and (eq (car old-ev) 'record-expansion) (equal (cadr old-ev) new-ev)) (corresponding-encap-events (cdr old-evs) (cdr new-evs) :expanded)) ((equal (mv-let (changedp x) (elide-locals-rec old-ev t) (declare (ignore changedp)) x) (mv-let (changedp y) (elide-locals-rec new-ev t) (declare (ignore changedp)) y)) (corresponding-encap-events (cdr old-evs) (cdr new-evs) :expanded)) (t nil)))))) (defun corresponding-encaps (old new) (assert$ (eq (car new) 'encapsulate) (and (eq (car old) 'encapsulate) (true-listp new) (equal (cadr old) (cadr new)) (corresponding-encap-events (cddr old) (cddr new) t)))) (defun redundant-encapsulate-tuplep (event-form mode ruler-extenders vge event-number wrld) ; We return non-nil iff the non-prehistoric (if that's where we start) part of ; wrld later than the given absolute event number (unless it's nil) contains an ; event-tuple whose form is essentially equal to event-form. We return t if ; they are equal, else we return the old form. See also the Essay on ; Make-event. (cond ((or (null wrld) (and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value) (equal (access-command-tuple-form (cddar wrld)) '(exit-boot-strap-mode))) (and (integerp event-number) (eq (cadar wrld) 'absolute-event-number) (integerp (cddar wrld)) (<= (cddar wrld) event-number))) nil) ((and (eq (caar wrld) 'event-landmark) (eq (cadar wrld) 'global-value) (let* ((old-event-form (access-event-tuple-form (cddar wrld))) (equal? (and (eq (car old-event-form) 'encapsulate) (corresponding-encaps old-event-form event-form)))) (and equal? (let ((adt (table-alist 'acl2-defaults-table wrld))) (and (eq (default-defun-mode-from-table adt) mode) (equal (default-ruler-extenders-from-table adt) ruler-extenders) (eql (default-verify-guards-eagerness-from-table adt) vge) (if (eq equal? :expanded) old-event-form t))))))) (t (redundant-encapsulate-tuplep event-form mode ruler-extenders vge event-number (cdr wrld))))) (defun redundant-encapsulatep (signatures ev-lst event-form wrld) ; We wish to know if is there an event-tuple in wrld that has event-form as its ; form. We do know that event-form is an encapsulate with the given two ; arguments. We don't know if event-form will execute without error. But ; suppose we could find a name among signatures and ev-lst that is guaranteed ; to be created if event-form were successful. Then if that name is new, we ; know we won't find event-form in wrld and needn't bother looking. If the ; name is old and was introduced by a corresponding encapsulate (in the sense ; that the signatures agree and each form of the new encapsulate either equals ; the corresponding form of the old encapsulate or else, roughly speaking, does ; so before expansion of the old form -- see corresponding-encaps), then the ; event is redundant. Otherwise, if this correspondence test fails or if we ; can't even find a name -- e.g., because signatures is nil and all the events ; in ev-lst are user macros -- then we suffer the search through wrld. How bad ; is this? We expect most encapsulates to have a readily recognized name among ; their new args and most encapsulates are not redundant, so we think most of ; the time, we'll find a name and it will be new. ; If we find that the current encapsulate is redundant, then we return t unless ; the earlier corresponding encapsulate is not equal to it, in which case we ; return that earlier encapsulate, which is stored in expanded form. See also ; the Essay on Make-event. Otherwise we return nil. (cond (signatures (let ((name (case-match signatures ((((name . &) arrow . &) . &) (and (symbolp arrow) (equal (symbol-name arrow) "=>") name)) (((name . &) . &) name)))) (and name (symbolp name) (not (new-namep name wrld)) (let* ((wrld-tail (lookup-world-index 'event (getprop name 'absolute-event-number 0 'current-acl2-world wrld) wrld)) (event-tuple (cddr (car wrld-tail))) (old-event-form (access-event-tuple-form event-tuple)) (equal? (corresponding-encaps old-event-form event-form))) (and equal? (let ((old-adt (table-alist 'acl2-defaults-table wrld-tail)) (new-adt (table-alist 'acl2-defaults-table wrld))) (and (eq (default-defun-mode-from-table old-adt) (default-defun-mode-from-table new-adt)) (equal (default-ruler-extenders-from-table old-adt) (default-ruler-extenders-from-table new-adt)) (eql (default-verify-guards-eagerness-from-table old-adt) (default-verify-guards-eagerness-from-table new-adt)) (if (eq equal? :expanded) old-event-form t)))))))) (t (let ((name (find-first-non-local-name-lst ev-lst))) (and (or (not name) ; A non-local name need not be found. But if one is found, then redundancy ; fails if that name is new. (not (new-namep name wrld))) (let ((new-adt (table-alist 'acl2-defaults-table wrld))) (redundant-encapsulate-tuplep event-form (default-defun-mode-from-table new-adt) (default-ruler-extenders-from-table new-adt) (default-verify-guards-eagerness-from-table new-adt) (and name (getprop name 'absolute-event-number nil 'current-acl2-world wrld)) wrld))))))) (defun mark-missing-as-hidden-p (a1 a2) ; A1 and a2 are known-package-alists. Return the result of marking each ; package-entry in a1 that is missing in a2 with hidden-p equal to t. (cond ((endp a1) nil) ((or (find-package-entry (package-entry-name (car a1)) a2) (package-entry-hidden-p (car a1))) (cons (car a1) (mark-missing-as-hidden-p (cdr a1) a2))) (t (cons (change-package-entry-hidden-p (car a1) t) (mark-missing-as-hidden-p (cdr a1) a2))))) (defun known-package-alist-included-p (a1 a2) ; Return true if every package-entry in a1 is present in a2, and moveover, is ; present non-hidden in a2 if present non-hidden in a1. (cond ((endp a1) t) (t (and (let ((a2-entry (find-package-entry (package-entry-name (car a1)) a2))) (and a2-entry (or (package-entry-hidden-p (car a1)) (not (package-entry-hidden-p a2-entry))))) (known-package-alist-included-p (cdr a1) a2))))) (defun encapsulate-fix-known-package-alist (pass1-k-p-alist wrld) ; Pass1-k-p-alist is the known-package-alist from the end of the first pass of ; an encapsulate, and we are now at the end of the second pass in the given ; world, wrld. The known-package-alist of wrld may be missing some ; package-entries from pass1-k-p-alist because of defpkg events that were only ; executed under locally included books in the first pass. We return the ; result of setting the known-package-alist of the given world by marking each ; package-entry in pass1-k-p-alist that is missing in the current world's ; known-package-alist with hidden-p equal to t. ; The call of known-package-alist-included-p below checks that the second pass ; does not introduce any packages beyond those introduced in the first pass, ; nor does the second pass "promote" any package to non-hidden that was hidden ; in the first pass. We rely on this fact in order to use the ; known-package-alist from the first pass as a basis for the alist returned, so ; that any package-entry present in the second pass's alist is present in the ; result alist, and moveover is non-hidden in the result if non-hidden in the ; second pass's alist. ; In fact we believe that the known-package-alist at the end of the second pass ; of an encapsulate is the same as at the beginning of the encapsulate, since ; local events are all skipped and include-books are all local. However, we do ; not rely on this belief. (let ((pass2-k-p-alist (global-val 'known-package-alist wrld))) (cond ((equal pass1-k-p-alist pass2-k-p-alist) ; optimize for a common case wrld) (t (assert$ (known-package-alist-included-p pass2-k-p-alist pass1-k-p-alist) (global-set 'known-package-alist (mark-missing-as-hidden-p pass1-k-p-alist pass2-k-p-alist) wrld)))))) (defun subst-by-position1 (alist lst index acc) ; See the comment in subst-by-position. (cond ((endp alist) (revappend acc lst)) ((endp lst) (cond ((endp alist) nil) (t (er hard 'subst-by-position1 "Implementation error: lst is an atom, so unable to ~ complete call ~x0." `(subst-by-position1 ,alist ,lst ,index ,acc))))) ((eql index (caar alist)) (subst-by-position1 (cdr alist) (cdr lst) (1+ index) (cons (cdar alist) acc))) (t (subst-by-position1 alist (cdr lst) (1+ index) (cons (car lst) acc))))) (defun subst-by-position (alist lst index) ; Alist associates index-based positions in lst with values. We ; return the result of replacing each element of lst with its corresponding ; value from alist. Alist should have indices in increasing order and should ; only have indices i for which index+i is less than the length of lst. (cond (alist (cond ((< (caar alist) index) (er hard 'subst-by-position "Implementation error: The alist in subst-by-position ~ must not start with an index less than its index ~ argument, so unable to compute ~x0." `(subst-by-position ,alist ,lst ,index))) (t (subst-by-position1 alist lst index nil)))) (t ; optimize for common case lst))) (defun intro-udf-guards (insigs kwd-value-list-lst wrld-acc wrld ctx state) ; Insigs is a list of signatures, each in the internal form (list fn formals ; stobjs-in stobjs-out); see chk-signature. Kwd-value-list-lst corresponds ; positionally to insigs. We return an extension of wrld-acc in which the ; 'guard property has been set according to insigs. ; Wrld is the world we used for translating guards. Our intention is that it ; is used in place of the accumulator, wrld-acc, because it is installed. (cond ((endp insigs) (value wrld-acc)) (t (er-let* ((tguard (let ((tail (assoc-keyword :GUARD (car kwd-value-list-lst)))) (cond (tail (translate (cadr tail) t ; stobjs-out for logic, not exec t ; logic-modep nil ; known-stobjs ctx wrld state)) (t (value nil)))))) (let* ((insig (car insigs)) (fn (car insig)) (formals (cadr insig)) (stobjs-in (caddr insig)) (stobjs (collect-non-x nil stobjs-in)) (stobj-terms (stobj-recognizer-terms stobjs wrld))) (er-progn (cond (tguard (chk-free-vars fn formals tguard "guard for" ctx state)) (t (value nil))) (intro-udf-guards (cdr insigs) (cdr kwd-value-list-lst) (putprop-unless fn 'guard (cond (tguard (conjoin (append stobj-terms (list tguard)))) (t (conjoin stobj-terms))) *t* wrld-acc) wrld ctx state))))))) (defun intro-udf-non-classicalp (insigs kwd-value-list-lst wrld) (cond ((endp insigs) wrld) (t (let* ((insig (car insigs)) (fn (car insig)) (kwd-value-list (car kwd-value-list-lst)) (tail (assoc-keyword :CLASSICALP kwd-value-list)) (val (if tail (cadr tail) t))) (intro-udf-non-classicalp (cdr insigs) (cdr kwd-value-list-lst) (putprop-unless fn 'classicalp val t ; default wrld)))))) (defun assoc-proof-supporters-alist (sym alist) (cond ((endp alist) nil) ((if (consp (caar alist)) ; namex key is a consp (member-eq sym (caar alist)) (eq sym (caar alist))) (car alist)) (t (assoc-proof-supporters-alist sym (cdr alist))))) (defun update-proof-supporters-alist-3 (names local-alist old new wrld) (cond ((endp names) (mv (reverse old) new)) ((getprop (car names) 'absolute-event-number nil 'current-acl2-world wrld) ; We'd like to say that if the above getprop is non-nil, then (car names) ; is non-local. But maybe redefinition was on and some local event redefined ; some name from before the encapsulate. Oh well, redefinition isn't ; necessarily fully supported in every possible way, and that obscure case is ; one such way. Note that we get here with a wrld that has already erased old ; properties of signature functions (if they are being redefined), via ; chk-acceptable-encapsulate; so at least we don't need to worry about those. (update-proof-supporters-alist-3 (cdr names) local-alist (cons (car names) old) new wrld)) (t (let ((car-names-supporters (cdr (assoc-proof-supporters-alist (car names) local-alist)))) (update-proof-supporters-alist-3 (cdr names) local-alist old (strict-merge-symbol-< car-names-supporters new nil) wrld))))) (defun posn-first-non-event (names wrld idx) (cond ((endp names) nil) ((getprop (car names) 'absolute-event-number nil 'current-acl2-world wrld) (posn-first-non-event (cdr names) wrld (1+ idx))) (t idx))) (defun update-proof-supporters-alist-2 (names local-alist wrld) (let ((n (posn-first-non-event names wrld 0))) (cond ((null n) names) (t (mv-let (rest-old-event-names rest-new-names) (update-proof-supporters-alist-3 (nthcdr n names) local-alist nil nil wrld) (strict-merge-symbol-< (append (take n names) rest-old-event-names) rest-new-names nil)))))) (defun update-proof-supporters-alist-1 (namex names local-alist proof-supporters-alist wrld) (assert$ names ; sanity check; else we wouldn't have updated at install-event (let ((non-local-names (update-proof-supporters-alist-2 names local-alist wrld))) (cond ((getprop (if (symbolp namex) namex (car namex)) 'absolute-event-number nil 'current-acl2-world wrld) ; See comment for similar getprop call in update-proof-supporters-alist-2. (mv local-alist (if non-local-names (acons namex non-local-names proof-supporters-alist) proof-supporters-alist))) (t (mv (acons namex non-local-names local-alist) proof-supporters-alist)))))) (defun update-proof-supporters-alist (new-proof-supporters-alist proof-supporters-alist wrld) ; Both alists are indexed by namex values that occur in reverse order of ; introduction; for example, the caar (if non-empty) is the most recent namex. (cond ((endp new-proof-supporters-alist) (mv nil proof-supporters-alist)) (t (mv-let (local-alist proof-supporters-alist) (update-proof-supporters-alist (cdr new-proof-supporters-alist) proof-supporters-alist wrld) (update-proof-supporters-alist-1 (caar new-proof-supporters-alist) (cdar new-proof-supporters-alist) local-alist proof-supporters-alist wrld))))) (defun install-proof-supporters-alist (new-proof-supporters-alist installed-wrld wrld) (let ((saved-proof-supporters-alist (global-val 'proof-supporters-alist installed-wrld))) (mv-let (local-alist proof-supporters-alist) (update-proof-supporters-alist new-proof-supporters-alist saved-proof-supporters-alist installed-wrld) (declare (ignore local-alist)) (global-set 'proof-supporters-alist proof-supporters-alist wrld)))) (defun encapsulate-fn (signatures ev-lst state event-form) ; Important Note: Don't change the formals of this function without reading ; the *initial-event-defmacros* discussion in axioms.lisp. ; The Encapsulate Essay ; The motivation behind this event is to permit one to extend the theory by ; introducing function symbols, and theorems that describe their properties, ; without completely tying down the functions or including all of the lemmas ; and other hacks necessary to lead the system to the proofs. Thus, this ; mechanism replaces the CONSTRAIN event of Nqthm. It also offers one way of ; getting some name control, comparable to scopes. However, it is better than ; just name control because the "hidden" rules are not just apparently hidden, ; they simply don't exist. ; Encapsulate takes two main arguments. The first is a list of ; "signatures" that describe the function symbols to be hidden. By ; signature we mean the formals, stobjs-in and stobjs-out of the ; function symbol. The second is a list of events to execute. Some ; of these events are tagged as "local" events and the others are not. ; Technically, each element of ev-lst is either an "event form" or ; else an s-expression of the form (LOCAL ev), where ev is an "event ; form." The events of the second form are the local events. ; Informally, the local events are present only so that we can justify ; (i.e., successfully prove) the non-local events. The local events ; are not visible in the final world constructed by an encapsulation. ; Suppose we execute an encapsulation starting with ld-skip-proofsp nil in ; wrld1. We will actually make two passes through the list of events. The ; first pass will execute each event, proving things, whether it is local or ; not. This will produce wrld2. In wrld2, we check that every function symbol ; in signatures is defined and has the signature alleged. Then we back up to ; wrld1, declare the hidden functions with the appropriate signatures ; (producing what we call proto-wrld3) and replay only the non-local events. ; (Note: if redefinitions are allowed and are being handled by query, the user ; will be presented with two queries for each redefining non-local event. ; There is no assurance that he answers the same way both times and different ; worlds may result. C'est la vie avec redefinitions.) During this replay we ; skip proofs. Having constructed that world we then collect all of the ; theorems that mention any of the newly-introduced functions and consider the ; resulting list as the constraint for all those functions. (This is a ; departure from an earlier, unsound implementation, in which we only collected ; theorems mentioning the functions declared in the signature.) However, we ; "optimize" by constructing this list of theorems using only those ; newly-introduced functions that have as an ancestor at least one function ; declared in the signature. In particular, we do not introduce any ; constraints if the signature is empty, which is reasonable since in that ; case, we may view the encapsulate event the same as we view a book. At any ; rate, the world we obtain by noting this constraint on the appropriate ; functions is called wrld3, and it is the world produced by a successful ; encapsulation. By putting enough checks on the kinds of events executed we ; can guarantee that the formulas assumed to create wrld3 from wrld1 are ; theorems that were proved about defined functions in wrld2. ; This is a non-trivial claim and will be the focus of much of our discussion ; below. This discussion could be eliminated if the second pass consisted of ; merely adding to wrld1 the formulas of the exported names, obtained from ; wrld2. We do not do that because we want to be able to execute an ; encapsulation quickly if we process one while skipping proofs. That is, ; suppose the user has produced a script of some session, including some ; encapsulations, and the whole thing has been processed with ld-skip-proofsp ; nil, once upon a time. Now the user wants to assume that script and and ; continue -- i.e., he is loading a "book". ; Suppose we hit the encapsulation when skipping proofs. Suppose we are ; again in wrld1 (i.e., processing the previous events of this script ; while skipping proofs has inductively left us in exactly the same ; state as when we did them with proofs). We are given the event list ; and the signatures. We want to do here exactly what we did in the ; second pass of the original proving execution of this encapsulate. ; Perhaps more informatively put, we want to do in the second pass of ; the proving execution exactly what we do here -- i.e., the relative ; paucity of information available here (we only have wrld1 and not ; wrld2) dictates how we must handle pass two back there. Remember, our ; goal is to ensure that the final world we create, wrld3, is absolutely ; identical to that created above. ; Our main problem is that the event list is in untranslated form. ; Two questions arise. ; (1) If we skip an event because it is tagged LOCAL, how will we know ; we can execute (or even translate) the subsequent events without ; error? For example, suppose one of the events skipped is the ; defmacro of deflemma, and then we see a (deflemma &). We will have ; to make sure this doesn't happen. The key here is that we know that ; the second pass of the proving execution of this encapsulate did ; whatever we're doing and it didn't cause an error. But this is an ; important point about the proving execution of an encapsulate: even ; though we make a lot of checks before the first pass, it is possible ; for the second pass to fail. When that happens, we'll revert back ; to wrld1 for sanity. This is unfortunate because it means the user ; will have to suffer through the re-execution of his event list ; before seeing if he has fixed the last error. We should eventually ; provide some sort of trial encapsulation mechanism so the user can ; see if he's got his signatures and exports correctly configured. ; (2) How do we know that the formulas generated during the second ; pass are exactly the same as those generated during the first pass? ; For example, one of the events might be: ; (if (ld-skip-proofsp state) ; (defun foo () 3) ; (defun foo () 2)) ; In this case, (foo) would be 2 in wrld2 but 3 in wrld3. ; The key to the entire story is that we insist that the event list ; consist of certain kinds of events. For lack of a better name, we ; call these "embedded event forms". Not everything the user might ; want to type in an interactive ACL2 session is an embedded event ; form! Roughly speaking, an event form translates to a PROGN of ; "primitive events", where the primitive events are appropriate calls ; of such user-level functions as defun and defthm. By "appropriate" ; we mean STATE only appears where specified by the stobjs-in for each ; event. The other arguments, e.g., the name of a defthm, must be ; occupied by state free terms -- well, almost. We allow uses of w so ; that the user can compute things like gensyms wrt the world. In a ; rough analogy with Lisp, the events are those kinds of commands that ; are treated specially when they are seen at the top-level of a file ; to be compiled. ; Events have the property that while they take state as an argument ; and change it, their changes to the world are a function only of the ; world (and their other arguments). Because of this property, we ; know that if s1 and s1' are states containing the same world, and s2 ; and s2' are the states obtained by executing an event on the two ; initial states, respectively, then the worlds of s2 and s2' are ; equal. ; Thus ends the encapsulate essay. (let ((ctx (encapsulate-ctx signatures ev-lst))) (with-ctx-summarized (if (output-in-infixp state) event-form ctx) (let* ((wrld1 (w state)) (saved-acl2-defaults-table (table-alist 'acl2-defaults-table wrld1)) (event-form (or event-form (list* 'encapsulate signatures ev-lst)))) (revert-world-on-error (let ((r (redundant-encapsulatep signatures ev-lst event-form wrld1))) (cond (r (pprogn (if (eq r t) state (f-put-global 'last-make-event-expansion r state)) (stop-redundant-event ctx state (and (not (eq r t)) "(This event is redundant with a previous encapsulate ~ event even though the two are not equal; see :DOC ~ redundant-encapsulate.)")))) ((and (not (eq (ld-skip-proofsp state) 'include-book)) (not (eq (ld-skip-proofsp state) 'include-book-with-locals)) (not (eq (ld-skip-proofsp state) 'initialize-acl2))) ; Ld-skip-proofsp is either t or nil. But whatever it is, we will be ; processing the LOCAL events. We are no longer sure why we do so when ; ld-skip-proofsp is t, but a reasonable theory is that in such a case, the ; user's intention is to do everything that one does other than actually ; calling prove -- so in particular, we do both passes of an encapsulate. (er-let* ((trip (chk-acceptable-encapsulate1 signatures ev-lst ctx wrld1 state))) (let ((insigs (car trip)) (kwd-value-list-lst (cadr trip)) (wrld1 (cddr trip))) (pprogn (set-w 'extension (global-set 'proof-supporters-alist nil wrld1) state) (print-encapsulate-msg1 insigs ev-lst state) (er-let* ((expansion-alist (state-global-let* ((in-local-flg ; As we start processing the events in the encapsulate, we are no longer in the ; lexical scope of LOCAL for purposes of disallowing setting of the ; acl2-defaults-table. (and (f-get-global 'in-local-flg state) 'local-encapsulate))) (process-embedded-events 'encapsulate-pass-1 saved-acl2-defaults-table (ld-skip-proofsp state) (current-package state) (list 'encapsulate insigs) ev-lst 0 nil ctx state)))) (let* ((wrld2 (w state)) (post-pass-1-skip-proofs-seen (global-val 'skip-proofs-seen wrld2)) (post-pass-1-include-book-alist-all (global-val 'include-book-alist-all wrld2)) (post-pass-1-pcert-books (global-val 'pcert-books wrld2)) (post-pass-1-ttags-seen (global-val 'ttags-seen wrld2)) (post-pass-1-proof-supporters-alist (global-val 'proof-supporters-alist wrld2))) (pprogn (print-encapsulate-msg2 insigs ev-lst state) (er-progn (chk-acceptable-encapsulate2 insigs kwd-value-list-lst wrld2 ctx state) (let* ((pass1-known-package-alist (global-val 'known-package-alist wrld2)) (new-ev-lst (subst-by-position expansion-alist ev-lst 0)) (state (set-w 'retraction wrld1 state))) (er-let* ((temp ; The following encapsulate-pass-2 is protected by the revert-world-on ; error above. (encapsulate-pass-2 insigs kwd-value-list-lst new-ev-lst saved-acl2-defaults-table nil ctx state))) (let ((wrld3 (w state)) (constrained-fns (nth 0 temp)) (constraints-introduced (nth 1 temp)) (exports (nth 2 temp)) (subversive-fns (nth 3 temp)) (infectious-fns (nth 4 temp)) (new-event-form (and expansion-alist (list* 'encapsulate signatures new-ev-lst)))) (pprogn (print-encapsulate-msg3 ctx insigs new-ev-lst exports constrained-fns constraints-introduced subversive-fns infectious-fns wrld3 state) (f-put-global 'last-make-event-expansion new-event-form state) (er-let* ((wrld3a (intro-udf-guards insigs kwd-value-list-lst wrld3 wrld3 ctx state)) #+:non-standard-analysis (wrld3a (value (intro-udf-non-classicalp insigs kwd-value-list-lst wrld3a)))) (install-event t (or new-event-form event-form) 'encapsulate (strip-cars insigs) nil nil t ctx (let* ((wrld4 (encapsulate-fix-known-package-alist pass1-known-package-alist wrld3a)) (wrld5 (global-set? 'ttags-seen post-pass-1-ttags-seen wrld4 (global-val 'ttags-seen wrld3))) (wrld6 (install-proof-supporters-alist post-pass-1-proof-supporters-alist wrld3 wrld5)) (wrld7 (cond ((or (global-val 'skip-proofs-seen ; We prefer that an error report about skip-proofs in certification world be ; about a non-local event. wrld3) (null post-pass-1-skip-proofs-seen)) wrld6) (t (global-set 'skip-proofs-seen post-pass-1-skip-proofs-seen wrld6)))) (wrld8 (global-set? 'include-book-alist-all post-pass-1-include-book-alist-all wrld7 (global-val 'include-book-alist-all wrld3))) (wrld9 (global-set? 'pcert-books post-pass-1-pcert-books wrld8 (global-val 'pcert-books wrld3)))) wrld9) state)))))))))))))) (t ; (ld-skip-proofsp state) = 'include-book ; 'include-book-with-locals or ; 'initialize-acl2 ; We quietly execute our second pass. (er-let* ((trip (chk-signatures signatures ctx wrld1 state))) (let ((insigs (car trip)) (kwd-value-list-lst (cadr trip)) (wrld1 (cddr trip))) (pprogn (set-w 'extension wrld1 state) (er-let* ; The following encapsulate-pass-2 is protected by the revert-world-on ; error above. ((expansion-alist (encapsulate-pass-2 insigs kwd-value-list-lst ev-lst saved-acl2-defaults-table t ctx state))) (let ((wrld3 (w state)) (new-event-form (and expansion-alist (list* 'encapsulate signatures (subst-by-position expansion-alist ev-lst 0))))) (pprogn (f-put-global 'last-make-event-expansion new-event-form state) (er-let* ((wrld3a (intro-udf-guards insigs kwd-value-list-lst wrld3 wrld3 ctx state)) #+:non-standard-analysis (wrld3a (value (intro-udf-non-classicalp insigs kwd-value-list-lst wrld3a)))) (install-event t (if expansion-alist new-event-form event-form) 'encapsulate (strip-cars insigs) nil nil nil ; irrelevant, since we are skipping proofs ctx ; We have considered calling encapsulate-fix-known-package-alist on wrld3a, just ; as we do in the first case (when not doing this on behalf of include-book). ; But we do not see a need to do so, both because all include-books are local ; and hence skipped (hence the known-package-alist has not changed from before ; the encapsulate), and because we do not rely on tracking packages during ; include-book, :puff (where ld-skip-proofsp is include-book-with-locals), or ; initialization. wrld3a state)))))))))))))))) (defun progn-fn1 (ev-lst progn!p bindings state) ; Important Note: Don't change the formals of this function without reading ; the *initial-event-defmacros* discussion in axioms.lisp. ; If progn!p is nil, then we have a progn and bindings is nil. Otherwise we ; have a progn! and bindings is a list of bindings as for state-global-let*. (let ((ctx (cond (ev-lst (msg "( PROGN~s0 ~@1 ...)" (if progn!p "!" "") (tilde-@-abbreviate-object-phrase (car ev-lst)))) (t (if progn!p "( PROGN!)" "( PROGN)")))) (in-encapsulatep (in-encapsulatep (global-val 'embedded-event-lst (w state)) nil))) (with-ctx-summarized ctx (revert-world-on-error (mv-let (erp val expansion-alist ignore-kpa state) (pprogn (f-put-global 'redo-flat-succ nil state) (f-put-global 'redo-flat-fail nil state) (eval-event-lst 0 nil ev-lst (or (ld-skip-proofsp state) progn!p) ; quietp (eval-event-lst-environment in-encapsulatep state) (f-get-global 'in-local-flg state) nil (if progn!p :non-event-ok ; It is unknown here whether make-event must have a consp :check-expansion, but ; if this progn is in such a context, chk-embedded-event-form will check that ; for us. nil) nil ctx (proofs-co state) state)) (declare (ignore ignore-kpa)) (pprogn (if erp (update-for-redo-flat val ev-lst state) state) (cond ((eq erp 'non-event) (er soft ctx "PROGN may only be used on legal event forms (see :DOC ~ embedded-event-form). Consider using ER-PROGN instead.")) (erp (er soft ctx "~x0 failed!~@1" (if progn!p 'progn! 'progn) (if (and progn!p (consp erp)) (msg " Note that the ~n0 form evaluated to a ~ multiple value (mv erp ...) with non-nil ~ erp, ~x1; see :DOC progn!." (list (1+ val)) (car erp)) ""))) (t (pprogn (f-put-global 'last-make-event-expansion (and expansion-alist (cons (if progn!p 'progn! 'progn) (if bindings (assert$ progn!p `(:state-global-bindings ,bindings ,@(subst-by-position expansion-alist ev-lst 0))) (subst-by-position expansion-alist ev-lst 0)))) state) (value (and (not (f-get-global 'acl2-raw-mode-p state)) ; If we allow a non-nil value in raw-mode (so presumably we are in progn!, not ; progn), then it might be a bad-lisp-objectp. Of course, in raw-mode one can ; assign bad lisp objects to state globals which then become visible out of ; raw-mode -- so the point here isn't to make raw-mode sound. But this nulling ; out in raw-mode should prevent most bad-lisp-objectp surprises from progn!. val))))))))))) (defun progn-fn (ev-lst state) (progn-fn1 ev-lst nil nil state)) (defun progn!-fn (ev-lst bindings state) (state-global-let* ((acl2-raw-mode-p (f-get-global 'acl2-raw-mode-p state)) (ld-okp (let ((old (f-get-global 'ld-okp state))) (if (eq old :default) nil old)))) (progn-fn1 ev-lst t bindings state))) (defun make-event-ctx (event-form) (msg "( MAKE-EVENT ~@0~@1)" (tilde-@-abbreviate-object-phrase (cadr event-form)) (if (cddr event-form) " ..." ""))) (defun protected-eval (form on-behalf-of ctx state aok) ; We assume that this is executed under a revert-world-on-error, so that we do ; not have to protect the world here. Form should evaluate either to an ; ordinary value, val, or to (mv nil val state stobj1 ... stobjk), where k may ; be 0. If so, we return (value (cons val new-kpa)), where new-kpa is the ; known-package-alist immediately after form is evaluated; and if not, we ; return a soft error. (let ((original-wrld (w state))) (protect-system-state-globals (er-let* ((result ; It would be nice to add (state-global-let* ((safe-mode t)) here. But some ; *1* functions need always to call their raw Lisp counterparts. Although we ; have made progress in oneify-cltl-code to that end by keeping functions like ; certify-book-fn from being replaced by their *1* counterparts, still that ; process is not complete, so we play it safe here by avoiding safe-mode. ; If we bind safe-mode to t here, visit occurrences of comments "; Note that ; safe-mode for make-event will require addition". Those comments are ; associated with membership tests that, for now, we avoid for efficiency. (trans-eval form ctx state aok))) (let* ((new-kpa (known-package-alist state)) (new-ttags-seen (global-val 'ttags-seen (w state))) (stobjs-out (car result)) (vals (cdr result)) (safep (equal stobjs-out '(nil)))) (cond (safep (value (list* vals new-kpa new-ttags-seen))) ((or (null (cdr stobjs-out)) (not (eq (caddr stobjs-out) 'state)) (member-eq nil (cdddr stobjs-out))) (er soft ctx "The expansion of a make-event form must either return a ~ single ordinary value or else should return a tuple (mv ~ erp val state stobj1 stobj2 ... stobjk) for some k >= 0. ~ But the shape of ~x0 is ~x1." form (prettyify-stobjs-out stobjs-out))) ((stringp (car vals)) (er soft ctx (car vals))) ((tilde-@p (car vals)) ; a message (er soft ctx "~@0" (car vals))) ((car vals) (er soft ctx "Error in MAKE-EVENT ~@0from expansion of:~| ~y1" (cond (on-behalf-of (msg "on behalf of~| ~y0~|" on-behalf-of)) (t "")) form)) (t (pprogn (set-w! original-wrld state) (value (list* (cadr vals) new-kpa new-ttags-seen)))))))))) (defun make-event-debug-pre (form on-behalf-of state) (cond ((null (f-get-global 'make-event-debug state)) (value nil)) (t (let ((depth (f-get-global 'make-event-debug-depth state))) (pprogn (fms "~x0> Expanding for MAKE-EVENT~@1~| ~y2~|" (list (cons #\0 depth) (cons #\1 (if on-behalf-of (msg " on behalf of~| ~Y01:" on-behalf-of (term-evisc-tuple nil state)) ":")) (cons #\2 form)) (proofs-co state) state nil) (value depth)))))) (defun make-event-debug-post (debug-depth expansion0 state) (cond ((null debug-depth) state) (t (fms "<~x0 Returning MAKE-EVENT expansion:~| ~Y12~|" (list (cons #\0 debug-depth) (cons #\1 expansion0) (cons #\2 (term-evisc-tuple nil state))) (proofs-co state) state nil)))) (defmacro do-proofs? (do-proofsp form) `(if ,do-proofsp (state-global-let* ((ld-skip-proofsp nil)) ,form) ,form)) (table acl2-system-table nil nil ; This table is used when we need to lay down an event marker. We may find ; other uses for it in the future, in which we will support other keys. Users ; should stay away from this table since it might change out from under them! ; But there is no soundness issue if they do use it. :guard (eq key 'empty-event-key)) (defun make-event-fn2 (expansion0 whole-form in-encapsulatep check-expansion wrld ctx state) (mv-let (do-proofsp expansion0) (case-match expansion0 ((':DO-PROOFS x) (mv (ld-skip-proofsp state) x)) (& (mv nil expansion0))) (er-let* ((expansion1a ; apply macroexpansion to get embedded event form (do-proofs? ; This wrapper of do-proofs? avoids errors in checking expansions when ; ld-skip-proofsp is 'include-book. See the "Very Technical Remark" in ; community book books/make-event/read-from-file.lisp. check-expansion (chk-embedded-event-form expansion0 whole-form wrld ctx state (primitive-event-macros) nil ; portcullisp (f-get-global 'in-local-flg state) in-encapsulatep nil))) (expansion1 (value (or expansion1a ; Else the alleged embedded event form, from the expansion, is nil, presumably ; because of local. *local-value-triple-elided*))) (stobjs-out-and-raw-result (do-proofs? do-proofsp (trans-eval ; Note that expansion1 is guaranteed to be an embedded event form, which (as ; checked just below) must evaluate to an error triple. expansion1 ctx state t)))) (let ((raw-result (cdr stobjs-out-and-raw-result))) (cond ((car raw-result) (silent-error state)) (t (value (list* expansion1 (car stobjs-out-and-raw-result) (cadr raw-result))))))))) (defun make-event-fn2-lst (expansion-lst whole-form in-encapsulatep check-expansion wrld ctx state) (cond ((atom expansion-lst) (er soft ctx "Evaluation failed for all expansions.")) (t (pprogn (cond ((f-get-global 'make-event-debug state) (fms "Attempting evaluation of next expansion:~|~Y01" (list (cons #\0 (car expansion-lst)) (cons #\1 (abbrev-evisc-tuple state))) (proofs-co state) state nil)) (t state)) (mv-let (erp val state) (make-event-fn2 (car expansion-lst) whole-form in-encapsulatep check-expansion wrld ctx state) (cond (erp (make-event-fn2-lst (cdr expansion-lst) whole-form in-encapsulatep check-expansion wrld ctx state)) (t (value val)))))))) (defun make-event-fn1 (expansion0 whole-form in-encapsulatep check-expansion wrld ctx state) (cond ((and (consp expansion0) (eq (car expansion0) :OR)) (make-event-fn2-lst (cdr expansion0) whole-form in-encapsulatep check-expansion wrld ctx state)) (t (make-event-fn2 expansion0 whole-form in-encapsulatep check-expansion wrld ctx state)))) (defun ultimate-expansion (x) ; We dive inside values of :expansion? keywords, starting with x, and stepping ; past wrappers (in the sense of destructure-expansion). Except, if ; :expansion? is provided but :check-expansion is non-nil (hence t), then ; :expansion? is ignored for this purpose, so that we can avoid destroying the ; surrounding make-event that should be saved for purposes of :check-expansion. ; The idea is that when including a book (or doing the second pass of an ; encapsulate), we replace a make-event form directly by its :expansion? value ; unless :check-expansion is t, in which case the make-event form and the ; :expansion? value are not equivalent, because the make-event form redoes the ; expansion process. ; Warning: Be careful not to use this function unless each make-event form ; encountered during the traversal that has a value for the :expansion? keyword ; can be trusted to have an expansion suitably consistent with that value. (case-match x (('make-event & . kwd-alist) (let ((exp (cadr (assoc-keyword :expansion? kwd-alist)))) (cond ((and exp (not (cadr (assoc-keyword :check-expansion kwd-alist)))) (ultimate-expansion exp)) (t x)))) (& (mv-let (w y) (destructure-expansion x) (cond (w (rebuild-expansion w (ultimate-expansion y))) (t x)))))) (defun make-event-fn (form expansion? check-expansion on-behalf-of whole-form state) (let ((ctx (make-event-ctx whole-form)) #-acl2-loop-only (old-kpa (known-package-alist state))) (with-ctx-summarized ctx (cond ((and (eq (cert-op state) :convert-pcert) (not (f-get-global 'in-local-flg state)) (not (consp check-expansion)) ; This case should not happen, because all make-event forms should already be ; expanded away when we do the Convert procedure of provisional certification, ; since a suitable expansion-alist should have been stored in the .pcert0 file. ; We include this check just for robustness. (eql (f-get-global 'make-event-debug-depth state) ; We only enforce the above consp requirement at the top-level. If we have ; (make-event ... :check-expansion exp ...), and this event is admissible ; (perhaps when skipping proofs) then we know that the result will be exp and ; will be independent of the current state. In particular, exp will not be a ; call of make-event if form is admissible. 0)) (er soft ctx "Implementation error: You should not be seeing this message! ~ Please contact the ACL2 implementors.~|~%Make-event expansion is ~ illegal during the Convert procedure of provisional certification ~ (unless :check-expansion is supplied a consp argument). ~ Expansion for make-event of the form ~x0 is thus not allowed. ~ The use of a .acl2x file can sometimes solve this problem. See ~ :DOC provisional-certification." form)) ((not (or (eq check-expansion nil) (eq check-expansion t) (consp check-expansion))) (er soft ctx "The check-expansion flag of make-event must be t, nil, or a cons ~ pair. The following check-expansion flag is thus illegal: ~x0. ~ See :DOC make-event." check-expansion)) ((and expansion? (consp check-expansion)) ; We considered allowing :EXPANSION? FORM1 and :CHECK-EXPANSION FORM2 (where ; FORM2 is not nil or t), and if someone presents a natural example for which ; this would be useful, we might do so. But the semantics of this would be ; potentially confusing. Which one is consulted when including a book or ; running in raw Lisp? If FORM1 = FORM2, this looks redundant. Otherwise, ; this is, oddly, inherently contradictory, in the sense that FORM1 should ; never be the expansion (unless one is deliberately arranging for evaluation ; of the make-event call to fail -- but there are simpler ways to do that). ; If we decide to support the combination of expansion? and (consp ; check-expansion), then we need to be careful to handle that combination -- ; something we don't do now, but we code defensively, giving priority to (consp ; check-expansion). (er soft ctx "It is illegal to supply a non-nil value for the keyword argument ~ :EXPANSION? of make-event when keyword argument :CHECK-EXPANSION ~ is give a value other than T or NIL. If you think you have a ~ reason why such a combination should be supported, please contact ~ the ACL2 implementors.")) (t (revert-world-on-error (state-global-let* ((make-event-debug-depth (1+ (f-get-global 'make-event-debug-depth state)))) (let ((wrld (w state))) (er-let* ((debug-depth (make-event-debug-pre form on-behalf-of state)) (expansion0/new-kpa/new-ttags-seen (cond ((and expansion? (eq (ld-skip-proofsp state) 'include-book) ; Even if expansion? is specified, we do not assume it's right if ; check-expansion is t. (assert$ (iff check-expansion ; In code above, we disallowed the combination of non-nil expansion? with a ; consp value of :check-expansion. (eq check-expansion t)) (not (eq check-expansion t)))) (value (list* expansion? nil nil))) (t (do-proofs? (or check-expansion ; For example, a must-fail form in community book books/make-event/defspec.lisp ; will fail during the Pcertify process of provisional certification unless we ; turn proofs on during expansion at that point. It's reasonable to do proofs ; under make-event expansion during the Pcertify process: after all, we need ; the expansion done in order for other books to include the make-event's book ; with the .pcert0 certificate, and also proofs might well be necessary in ; order to come up with the correct expansion (else why do them?). We could ; indeed always do proofs, but it's pretty common to do proofs only during ; certification as a way of validating some code. So our approach is only to ; move proofs from the Convert procedure to the Pcertify procedure. (eq (cert-op state) :create-pcert)) (protected-eval form on-behalf-of ctx state t))))) (expansion0 (value (car expansion0/new-kpa/new-ttags-seen))) (new-kpa (value (cadr expansion0/new-kpa/new-ttags-seen))) (new-ttags-seen (value (cddr expansion0/new-kpa/new-ttags-seen))) (new-ttags-p (pprogn (make-event-debug-post debug-depth expansion0 state) (cond ((equal new-ttags-seen (global-val 'ttags-seen wrld)) (value nil)) (t (pprogn (set-w! (global-set 'ttags-seen new-ttags-seen wrld) state) (value t)))))) (wrld0 (value (w state))) (expansion1/stobjs-out/result (make-event-fn1 expansion0 whole-form (in-encapsulatep (global-val 'embedded-event-lst wrld0) nil) check-expansion wrld0 ctx state))) (let* ((expansion1 (car expansion1/stobjs-out/result)) (stobjs-out (cadr expansion1/stobjs-out/result)) (result (cddr expansion1/stobjs-out/result)) (expansion2 (cond ((f-get-global 'last-make-event-expansion state) (mv-let (wrappers base) (destructure-expansion expansion1) ; At this point we know that (car base) is from the list '(make-event progn ; progn! encapsulate); indeed, just after the release of v3-5, we ran a ; regression in community book books/make-event with the code C below replaced ; by (assert$ (member-eq (car base) X) C), where X is the above quoted list. ; However, we do not add that assertion, so that for example the ccg book of ; ACL2s can create make-event expansions out of events other than the four ; types above, e.g., defun. (declare (ignore base)) (rebuild-expansion wrappers (ultimate-expansion (f-get-global 'last-make-event-expansion state))))) (t (ultimate-expansion expansion1))))) (assert$ (equal stobjs-out *error-triple-sig*) ; evaluated an event form (let ((expected-expansion (if (consp check-expansion) check-expansion (and (eq (ld-skip-proofsp state) 'include-book) check-expansion expansion?)))) (cond ((and expected-expansion (not (equal expected-expansion ; easy try first expansion2)) (not (equal (ultimate-expansion expected-expansion) expansion2))) (er soft ctx "The current MAKE-EVENT expansion differs from ~ the expected (original or specified) expansion. ~ ~ See :DOC make-event.~|~%~|~%Make-event ~ argument:~|~%~y0~|~%Expected ~ expansion:~|~%~y1~|~%Current expansion:~|~%~y2~|" form expected-expansion expansion2)) (t (let ((actual-expansion (cond ((or (consp check-expansion) (equal expansion? expansion2) ; easy try first (equal (ultimate-expansion expansion?) expansion2)) ; The original make-event form does not generate a make-event replacement (see ; :doc make-event). nil) (check-expansion (assert$ (eq check-expansion t) ; from macro guard (list* 'make-event form ; Note that we deliberately omit :expansion? here, even if it was supplied ; originally. If :expansion? had been supplied and appropropriate, then we ; would be in the previous case, where we don't generate a make-event around ; the expansion. :check-expansion expansion2 (and on-behalf-of `(:on-behalf-of ,on-behalf-of))))) (t expansion2)))) #-acl2-loop-only (let ((msg ; We now may check the expansion to see if an unknown package appears. The ; following example shows why this can be important. Consider a book "foo" ; with this event. ; (make-event ; (er-progn ; (include-book "foo2") ; introduces "MY-PKG" ; (assign bad (intern$ "ABC" "MY-PKG")) ; (value `(make-event ; (list 'defconst '*a* ; (list 'length ; (list 'symbol-name ; (list 'quote ',(@ bad))))))))) ; ; where "foo2" is as follows, with the indicated portullis command: ; (in-package "ACL2") ; ; ; (defpkg "MY-PKG" nil) ; ; (defun foo (x) ; x) ; In ACL2 Version_3.4, we certified these books; but then, in a new ACL2 ; session, we got a raw Lisp error about unknown packages when we try to ; include "foo". ; On the other hand, the bad-lisp-objectp test is potentially expensive for ; large objects such as are encountered at Centaur Tech. in March 2010. The ; value returned by expansion can be expected to be a good lisp object in the ; world installed at the end of expansion, so if expansion doesn't extend the ; world with any new packages, then we can avoid this check. (and (not (eq old-kpa new-kpa)) (bad-lisp-objectp actual-expansion)))) (when msg (er hard ctx "Make-event expansion for the form ~x0 has ~ produced an illegal object for the ~ current ACL2 world. ~@1" form msg))) (pprogn (f-put-global 'last-make-event-expansion actual-expansion state) (cond ((f-get-global 'make-event-debug state) (fms "Saving make-event replacement into state ~ global 'last-make-event-expansion (debug ~ level ~x0):~|~Y12" (list (cons #\0 debug-depth) (cons #\1 actual-expansion) (cons #\2 (abbrev-evisc-tuple state))) (proofs-co state) state nil)) (t state)) (er-progn (cond ((and new-ttags-p ; optimization (let ((wrld1 (w state))) (not (and (eq (caar wrld1) 'event-landmark) (eq (cadar wrld1) 'global-value))))) ; We lay down an event landmark. Before we did so, an error was reported by ; print-redefinition-warning in the following example, because we weren't ; looking at an event landmark. ; (redef!) ; (make-event (er-progn (defttag t) ; (value '(value-triple nil)))) ; A cheap way to get an event landmark is with a table event, so that's what we ; do. The table-fn call below is the macroexpansion of: ; (table acl2-system-table 'empty-event-key ; (not (cdr (assoc-eq 'empty-event-key ; (table-alist 'acl2-system-table world))))) (state-global-let* ((inhibit-output-lst (add-to-set-eq 'summary (f-get-global 'inhibit-output-lst state)))) (TABLE-FN 'ACL2-SYSTEM-TABLE '('EMPTY-EVENT-KEY (NOT (CDR (ASSOC-EQ 'EMPTY-EVENT-KEY (TABLE-ALIST 'ACL2-SYSTEM-TABLE WORLD))))) STATE '(TABLE ACL2-SYSTEM-TABLE 'EMPTY-EVENT-KEY (NOT (CDR (ASSOC-EQ 'EMPTY-EVENT-KEY (TABLE-ALIST 'ACL2-SYSTEM-TABLE WORLD)))))))) (t (value nil))) (value result)))))))))))))))))) ; Now we develop the book mechanism, which shares a lot with what ; we've just done. In the discussion that follows, Unix is a ; trademark of Bell Laboratories. ; First, a broad question: how much security are we trying to provide? ; After all, one could always fake a .cert file, say by calling checksum ; onesself. Our claim is simply that we only fully "bless" certification runs, ; from scratch, of entire collections of books, without intervention. Thus, ; there is no soundness problem with using (include-book "hd:ab.lisp") in a ; book certified in a Unix file system and having it mean something completely ; different on the Macintosh. Presumably the attempt to certify this ; collection on the Macintosh would simply fail. ; How portable do we intend book names to be? Suppose that one has a ; collection of books, some of which include-book some of the others, where all ; of these include-books use relative path names. Can we set things up so that ; if one copies all of these .lisp and .cert files to another file system, ; preserving the hierarchical directory relationship, then we can guarantee ; that this collection of books is certifiable (modulo resource limitations)? ; The answer is yes: We use Unix-style pathnames within ACL2. See :doc ; pathname, and see the Essay on Pathnames in interface-raw.lisp. (Before ; Version_2.5 we also supported a notion of structured pathnames, similar to ; the "structured directories" concept in CLtL2. However, the CLtL2 notion was ; just for directories, not file names, and we "deprecated" structured ; pathnames by deleting their documentation around Version_2.5. We continued ; to support structured pathnames through Version_2.8 for backwards ; compatibility, but no longer.) ; Note. It is important that regardless of what initial information we store ; in the state that is based on the surrounding operating system, this ; information not be observable in the logical theory. For example, it would ; really be unfortunate if we did something like: ; (defconst *directory-separator* ; #+apple #\: ; #-apple #\/) ; because then we could certify a book in one ACL2 that contains a theorem ; (equal *directory-separator* #\/), and include this book in another world ; where that theorem fails, thus deriving a contradiction. In fact, we make ; the operating-system part of the state (as a world global), and figure ; everything else out about book names using that information. (deflabel books :doc ":Doc-Section Books files of ACL2 event forms~/ This ~il[documentation] topic is about ACL2 input files. However, there are two traditional (paper) books published about ACL2: a textbook and a case studies book. Further information on those two paper books is available by following links from the ACL2 home page, ~url[http://www.cs.utexas.edu/users/moore/acl2/]. A ``book'' is a file of ACL2 ~il[events] that have been certified as admissible. Using ~ilc[include-book] you can construct a new logical ~il[world] by assuming the ~il[events] in any number of mutually compatible books. Relevant documented topics are listed below. Following this list is a ``guided tour'' through the topics. ~terminal[You may start the guided tour by typing :more.] You can contribute books to the ACL2 community and obtain updates inbetween ACL2 releases by visiting the ~c[acl2-books] project web page, ~url[http://acl2-books.googlecode.com/]. Also ~pl[community-books].~/ ~em[Introduction.] A ``book'' is a file of ACL2 forms. Books are prepared entirely by the user of the system, i.e., they are ``source'' files not ``object'' files. Some of the forms in a book are marked ~ilc[local] and the others are considered ``non-local.'' ~ilc[Include-book] lets you load a book into any ACL2 ~il[world]. If completed without error, the inclusion of a book extends the logic of the host ~il[world] by the addition of just the non-local ~il[events] in the book. You may extend the ~il[world] by successively including a variety of books to obtain the desired collection of definitions and rules. Unless name conflicts occur (which are detected and signalled) inclusion of a book is consistency preserving provided the book itself is consistent as discussed later. However, ~ilc[include-book] merely assumes the validity of the ~il[events] in a book; if you include a book that contains an inconsistency (e.g., an inadmissible definition) then the resulting theory is inconsistent. It is possible to ``certify'' a book, with ~ilc[certify-book], guaranteeing that the error-free inclusion of the certified forms will produce a consistent extension of a consistent logic. Certification processes both the ~ilc[local] and non-local forms, so you can mark as ~ilc[local] those ~il[events] you need for certification that you want to hide from users of the book (e.g., hacks, crocks, and kludges on the way to a good set of ~c[:]~ilc[rewrite] rules). Certification can also ``compile'' a book, thereby speeding up the execution of the functions defined within it. The desire to compile books is largely responsible for the restrictions we put on the forms allowed in books. Extensive ~il[documentation] is available on the various aspects of books. We recommend that you read it all before using books. It has been written so as to make sense when read in a certain linear sequence, called the ``guided tour'', though in general you may browse through it randomly. If you are on the guided tour, you should next read the ~il[documentation] on book-example (~pl[book-example]~terminal[ and use :more to read through it]).~/ :cite include-book") (link-doc-to certifying-books books books-certification) (link-doc-to book-makefiles books books-certification) (deflabel community-books :doc ":Doc-Section Books ~il[books] contributed by the ACL2 community~/ For background on ACL2 books, which can contain useful definitions and theorems, ~pl[books]. The ACL2 ``community books'' is a collection of books developed since the early 1990s by members of the ACL2 community. The installation instructions suggest installing these books in the ~c[books/] subdirectory of your local ACL2 installation. You can contribute books to the ACL2 community and obtain updates inbetween ACL2 releases by visiting the ~c[acl2-books] project web page, ~url[http://acl2-books.googlecode.com/]. To certify the community books, ~pl[regression].~/~/") (defun chk-book-name (book-name full-book-name ctx state) ; Book-name is something submitted by the user as a book name. ; Full-book-name is the first result of calling parse-book-name on ; book-name and state. We check that full-book-name is a string ; ending in ".lisp" or cause an error. But the error reports ; book-name as the offender. ; This check is important because to form the certification extension we strip ; off the "lisp" and replace it by "cert". So if this is changed, change ; convert-book-name-to-cert-name and convert-book-name-to-compiled-name. ; Note: Because it is our own code, namely parse-book-name, that tacks on the ; ".lisp" extension, this check is now redundant. Once upon a time, the user ; was expected to supply the .lisp extension, but that made the execution of ; (include-book "arith.lisp") in raw lisp load the .lisp file rather than the ; .o file. We've left the redundant check in because we are not sure that ; parse-book-name will be kept in its current form; it has changed a lot ; lately. (cond ((and (stringp full-book-name) (let ((n (length full-book-name))) (and (> n 5) (eql (char full-book-name (- n 5)) #\.) (eql (char full-book-name (- n 4)) #\l) (eql (char full-book-name (- n 3)) #\i) (eql (char full-book-name (- n 2)) #\s) (eql (char full-book-name (- n 1)) #\p)))) (value nil)) ((null full-book-name) (er soft ctx "~x0 is not a legal book name. See :DOC book-name." book-name)) (t (er soft ctx "~x0 is not a legal book name because it does not specify the ~ ``.lisp'' extension. See :DOC book-name." book-name)))) ; The portcullis of a book consists of two things, a sequence of ; commands which must be executed with ld-skip-proofs nil without error ; and an include-book-alist-like structure which must be a subset of ; include-book-alist afterwards. We describe the structure of an ; include-book-alist below. (defun include-book-alist-subsetp (alist1 alist2) ; The include-book-alist contains elements of the ; general form example value ; (full-book-name ; "/usr/home/moore/project/arith.lisp" ; user-book-name ; "project/arith.lisp" ; familiar-name ; "arith" ; cert-annotations ; ((:SKIPPED-PROOFSP . sp) ; (:AXIOMSP . axp) ; (:TTAGS . ttag-alistp)) ; . ev-lst-chk-sum) ; 12345678 ; The include-book-alist becomes part of the certificate for a book, playing a ; role in both the pre-alist and the post-alist. In the latter role some ; elements may be marked (LOCAL &). When we refer to parts of the ; include-book-alist entries we have tried to use the tedious names above, to ; help us figure out what is used where. Please try to preserve this ; convention. ; Cert-annotations is an alist. The alist has three possible keys: ; :SKIPPED-PROOFSP, :AXIOMSP, and :TTAGS. The possible values of the first two ; are t, nil, or ?, indicating the presence, absence, or possible presence of ; skip-proof forms or defaxioms, respectively. The forms in question may be ; either LOCAL or non-LOCAL and are in the book itself (not just in some ; subbook). Even though the cert-annotations is an alist, we compare ; include-book-alists with equality on that component, not ``alist equality.'' ; So we are NOT free to drop or rearrange keys in these annotations. ; If the book is uncertified, the chk-sum entry is nil. ; Suppose the two alist arguments are each include-book-alists from different ; times. We check that the first is a subset of the second, in the sense that ; the (familiar-name cert-annotations . chk-sum) parts of the first are all ; among those of the second. We ignore the full names and the user names ; because they may change as the book or connected book directory moves around. (subsetp-equal (strip-cddrs alist1) (strip-cddrs alist2))) (defun get-portcullis-cmds (wrld cmds cbds names ctx state) ; When certify-book is called, we scan down wrld to collect all the user ; commands (more accurately: their make-event expansions) into cmds. This ; answer is part of the portcullis of the certificate, once it has been cleaned ; up by fix-portcullis-cmds and new-defpkg-list. We also collect into cbds the ; connected-book-directory values for cmds. (cond ((null wrld) (mv nil cmds cbds state)) ((and (eq (caar wrld) 'command-landmark) (eq (cadar wrld) 'global-value)) (let ((form (or (access-command-tuple-last-make-event-expansion (cddar wrld)) (access-command-tuple-form (cddar wrld)))) (cbd (access-command-tuple-cbd (cddar wrld)))) (cond ((equal form '(exit-boot-strap-mode)) (mv nil cmds cbds state)) (t (mv-let (erp val state) (chk-embedded-event-form form nil wrld ctx state names t nil nil t) (declare (ignore val)) (cond (erp (mv erp nil nil state)) (t (get-portcullis-cmds (cdr wrld) (cons form cmds) (cons cbd cbds) names ctx state)))))))) (t (get-portcullis-cmds (cdr wrld) cmds cbds names ctx state)))) (defun remove-after-last-directory-separator (p) (let* ((p-rev (reverse p)) (posn (position *directory-separator* p-rev))) (if posn (subseq p 0 (1- (- (length p) posn))) (er hard 'remove-after-last-directory-separator "Implementation error! Unable to handle a directory string.")))) (defun merge-using-dot-dot (p s) ; P is a directory pathname without the final "/". S is a pathname (for a file ; or a directory) that may start with any number of sequences "../" and "./". ; We want to "cancel" the leading "../"s in s against directories at the end of ; p, and eliminate leading "./"s from s (including leading "." if that is all ; of s). The result should syntactically represent a directory (end with a "/" ; or "." or be "") if and only if s syntactically represents a directory. ; This code is intended to be simple, not necessarily efficient. (cond ((equal p "") s) ((equal s "..") (concatenate 'string (remove-after-last-directory-separator p) *directory-separator-string*)) ((equal s ".") (concatenate 'string p *directory-separator-string*)) ((and (>= (length s) 3) (eql (char s 0) #\.) (eql (char s 1) #\.) (eql (char s 2) #\/)) (merge-using-dot-dot (remove-after-last-directory-separator p) (subseq s 3 (length s)))) ((and (>= (length s) 2) (eql (char s 0) #\.) (eql (char s 1) #\/)) (merge-using-dot-dot p (subseq s 2 (length s)))) (t (concatenate 'string p *directory-separator-string* s)))) (defun our-merge-pathnames (p s) ; This is something like the Common Lisp function merge-pathnames. P and s are ; (Unix-style) pathname strings, where s is a relative pathname. (If s may be ; an absolute pathname, use extend-pathname instead.) We allow p to be nil, ; which is a case that arises when p is (f-get-global 'connected-book-directory ; state) during boot-strapping; otherwise p should be an absolute directory ; pathname (though we allow "" as well). (cond ((and (not (equal s "")) (eql (char s 0) *directory-separator*)) (er hard 'our-merge-pathnames "Attempt to merge with an absolute filename, ~p0. Please contact the ~ ACL2 implementors." s)) ((or (null p) (equal p "")) s) ((stringp p) ; checked because of structured pathnames before Version_2.5 (merge-using-dot-dot (if (eql (char p (1- (length p))) *directory-separator*) (subseq p 0 (1- (length p))) p) s)) (t (er hard 'our-merge-pathnames "The first argument of our-merge-pathnames must be a string, ~ but the following is not: ~p0." p)))) (defun expand-tilde-to-user-home-dir (str os ctx state) ; Note that character `~' need not get special treatment by Windows. See ; comment just above error message below, and see absolute-pathname-string-p. (cond ((or (equal str "~") (and (< 1 (length str)) (eql (char str 0) #\~) (eql (char str 1) #\/))) (let ((user-home-dir (f-get-global 'user-home-dir state))) (cond (user-home-dir (concatenate 'string user-home-dir (subseq str 1 (length str)))) (t ; On Linux or Mac OS, it is surprising to find that user-home-dir is nil. (See ; the definition of lp to see how it is set.) But on Windows, it seems that ; this could be the case, say outside an environment like Cygwin, MSYS, or ; MinGW. (let ((certify-book-info (f-get-global 'certify-book-info state))) (prog2$ (and (or certify-book-info (not (eq os :mswindows))) (er hard ctx "The use of ~~/ for the user home directory ~ in filenames is not supported ~@0." (if certify-book-info "inside books being certified" "for this host Common Lisp"))) str)))))) (t str))) #-acl2-loop-only (progn (defvar *canonical-unix-pathname-action* ; The value can be nil, :warning, or :error. It is harmless for the value to ; be nil, which will just cause canonicalization of filenames by ; canonical-unix-pathname to fail silently, returning the unchanged filename. ; But the failures we are considering are those for which (truename x) is some ; non-nil value y and yet (truename y) is not y. We prefer to know about such ; cases, but the user is welcome to replace :error here with :warning or :nil ; and rebuild ACL2. :error) (defun canonical-unix-pathname (x dir-p state) ; Warning: Although it may be tempting to use pathname-device in this code, be ; careful if you do! Camm Maguire sent an example in which GCL on Windows ; returned ("Z:") as the value of (pathname-device (truename "")), and it ; appears that this is allowed by the Lisp standard even though we might expect ; most lisps to return a string rather than a list. ; X is a string representing a filename in the host OS. First suppose dir-p is ; nil. Return nil if there is no file with name x. Otherwise, return a ; Unix-style filename equivalent to x, preferably one that is canonical. If ; the file exists but we fail to find a canonical pathname with the same ; truename, we may warn or cause an error; see ; *canonical-unix-pathname-action*. ; If dir-p is true, then return the value above unless it corresponds to a file ; that is not a directory, or if the "true" name cannot be determined, in which ; case return nil. (let ((truename (our-truename x))) (and truename (let ((dir (pathname-directory truename)) (name (pathname-name truename)) (type (pathname-type truename))) (and (implies dir-p (not (or (stringp name) (stringp type)))) (assert$ (and (true-listp dir) (eq (car dir) #+gcl :ROOT #-gcl :ABSOLUTE)) (let* ((mswindows-drive (mswindows-drive (namestring truename) state)) (tmp (if mswindows-drive (concatenate 'string mswindows-drive "/") "/"))) (dolist (x dir) (when (stringp x) (setq tmp (concatenate 'string tmp x "/")))) (when (stringp name) (setq tmp (concatenate 'string tmp name))) (when (stringp type) (setq tmp (concatenate 'string tmp "." type))) (let ((namestring-tmp (namestring (truename tmp))) (namestring-truename (namestring truename))) (cond ((equal namestring-truename namestring-tmp) tmp) ((and mswindows-drive ; In Windows, it appears that the value returned by truename can start with ; (for example) "C:/" or "c:/" depending on whether "c" is capitalized in the ; input to truename. (See the comment in mswindows-drive1.) Since tmp is ; constructed from mswindows-drive and components of truename, we are really ; just doing a minor sanity check here, so we content ourselves with a ; case-insensitive string-equality check. That seems reasonable for Windows, ; whose pathnames are generally (as far as we know) considered to be ; case-insensitive. (string-equal namestring-truename namestring-tmp)) tmp) (t (case *canonical-unix-pathname-action* (:warning (let ((state *the-live-state*)) (warning$ 'canonical-unix-pathname "Pathname" "Unable to compute ~ canonical-unix-pathname ~ for ~x0. (Debug info: ~ truename is ~x1 while ~ (truename tmp) is ~x2.)" x namestring-truename namestring-tmp))) (:error (er hard 'canonical-unix-pathname "Unable to compute ~ canonical-unix-pathname for ~ ~x0. (Debug info: truename is ~ ~x1 while (truename tmp) is ~ ~x2.)" x namestring-truename namestring-tmp))) (and (not dir-p) ; indeterminate if dir-p x))))))))))) (defun unix-truename-pathname (x dir-p state) ; X is intended to be a Unix-style pathname. If x is not a string or the file ; named by x does not exist, then we return nil. Otherwise, assuming dir-p is ; nil, we return the corresponding truename, also Unix-style, if we can compute ; it; else we return x. If dir-p is true, however, and the above-referenced ; file is not a directory, then return nil. ; Notice that we do not modify state, here or in the ACL2 interface to this ; function, canonical-pathname. We imagine that the result depends on the ; file-clock of the state, which must change if any files actually change. (and (stringp x) (canonical-unix-pathname (pathname-unix-to-os x state) dir-p state))) ) #-acl2-loop-only (defun chk-live-state-p (fn state) (or (live-state-p state) ; It is perhaps a bit extreme to call interface-er, which calls (raw Lisp) ; error. But this is the conservative thing to do, and it doesn't cause a ; problem with the rewriter provided fn is constrained; see the comment about ; chk-live-state-p in rewrite. (interface-er "Function ~x0 was passed a non-live state!" fn))) #-acl2-loop-only (defun-overrides canonical-pathname (pathname dir-p state) ; This is essentially an interface to raw Lisp function unix-truename-pathname. ; See the comments for that function. (unix-truename-pathname pathname dir-p state)) (defun acl2-magic-canonical-pathname (x) ; This function is a sort of placeholder, used in a ; define-trusted-clause-processor event for noting that canonical-pathname has ; unknown constraints. (declare (xargs :guard t)) (list x)) #+acl2-loop-only (encapsulate () (define-trusted-clause-processor acl2-magic-canonical-pathname (canonical-pathname) :partial-theory (encapsulate (((canonical-pathname * * state) => *)) (logic) (local (defun canonical-pathname (x dir-p state) (declare (xargs :mode :logic)) (declare (ignore dir-p state)) (if (stringp x) x nil))) (defthm canonical-pathname-is-idempotent (equal (canonical-pathname (canonical-pathname x dir-p state) dir-p state) (canonical-pathname x dir-p state))) (defthm canonical-pathname-type (or (equal (canonical-pathname x dir-p state) nil) (stringp (canonical-pathname x dir-p state))) :rule-classes :type-prescription)))) (defdoc canonical-pathname ":Doc-Section ACL2::ACL2-built-ins the true absolute filename, with soft links resolved~/ For the name ~c[fname] of a file, the form ~c[(Canonical-pathname fname nil state)] evaluates to a Unix-style absolute filename representing the same file as ~c[fname], but generally without any use of soft links in the name. (Below, we explain the qualifier ``generally''.) If however the file indicated by ~c[fname] does not exist, ~c[(canonical-pathname fname nil state)] is ~c[nil]. Thus, ~c[canonical-pathname] can be used as one would use the raw Lisp function ~c[probe-file]. The specification of ~c[(Canonical-pathname fname dir-p state)] when ~c[dir-p] is not ~c[nil] is simlar, except that if the specified file exists but is not a directory, then the result is ~c[nil].~/ The function ~c[canonical-pathname] has a guard of ~c[t], though the second argument must be the ACL2 ~ilc[state]. This function is introduced with the following properties. ~bv[] (defthm canonical-pathname-is-idempotent (equal (canonical-pathname (canonical-pathname x dir-p state) dir-p state) (canonical-pathname x dir-p state))) (defthm canonical-pathname-type (or (equal (canonical-pathname x dir-p state) nil) (stringp (canonical-pathname x dir-p state))) :rule-classes :type-prescription) ~ev[] We use the qualifier ``generally'', above, because there is no guarantee that the filename will be canonical without soft links, though we expect this to be true in practice. ACL2 attempts to compute the desired result and then checks that the input and result have the same Common Lisp ``~c[truename]''. This check is expected to succeed, but if it fails then the input string is returned unchanged, and to be conservative, the value returned is ~c[nil] in this case if ~c[dir-p] is true.") (defun canonical-dirname! (pathname ctx state) (declare (xargs :guard t)) (or (canonical-pathname pathname t state) (let ((x (canonical-pathname pathname nil state))) (cond (x (er hard? ctx "The file ~x0 is not known to be a directory." x)) (t (er hard? ctx "The directory ~x0 does not exist." pathname)))))) (defun directory-of-absolute-pathname (pathname) (let* ((lst (coerce pathname 'list)) (rlst (reverse lst)) (temp (member *directory-separator* rlst))) (coerce (reverse temp) 'string))) (defun extend-pathname (dir file-name state) ; Dir is a string representing an absolute directory name, and file-name is a ; string representing a file or directory name. We want to extend dir by ; file-name if subdir is relative, and otherwise return file-name. Except, we ; return something canonical, if possible. (let* ((os (os (w state))) (file-name1 (expand-tilde-to-user-home-dir file-name os 'extend-pathname state)) (abs-filename (cond ((absolute-pathname-string-p file-name1 nil os) file-name1) (t (our-merge-pathnames dir file-name1)))) (canonical-filename (canonical-pathname abs-filename nil state))) (or canonical-filename ; If a canonical filename doesn't exist, then presumably the file does not ; exist. But perhaps the directory exists; we try that next. (let ((len (length abs-filename))) (assert$ (not (eql len 0)) ; absolute filename starts with "/" (cond ((eql (char abs-filename (1- (length abs-filename))) #\/) ; we have a directory, which we know doesn't exist abs-filename) (t ; Let's go ahead and at least try to canonicalize the directory of the file (or ; parent directory, in the unlikely event that we have a directory). (let* ((dir0 (directory-of-absolute-pathname abs-filename)) (len0 (length dir0)) (dir1 (assert$ (and (not (eql len0 0)) (eql (char dir0 (1- len0)) #\/)) (canonical-pathname dir0 t state)))) (cond (dir1 (concatenate 'string dir1 (subseq abs-filename len0 len))) (t ; return something not canonical; at least we tried! abs-filename)))))))))) (defun maybe-add-separator (str) (if (and (not (equal str "")) (eql (char str (1- (length str))) *directory-separator*)) str (string-append str *directory-separator-string*))) (defun set-cbd-fn (str state) (let ((os (os (w state))) (ctx (cons 'set-cbd str))) (cond ((not (stringp str)) (er soft ctx "The argument of set-cbd must be a string, unlike ~x0. See :DOC ~ cbd." str)) (t (let ((str (expand-tilde-to-user-home-dir str os ctx state))) (cond ((absolute-pathname-string-p str nil os) (assign connected-book-directory (canonical-dirname! (maybe-add-separator str) ctx state))) ((not (absolute-pathname-string-p (f-get-global 'connected-book-directory state) t os)) (er soft ctx "An attempt was made to set the connected book directory ~ (cbd) using relative pathname ~p0, but surprisingly, the ~ existing cbd is ~p1, which is not an absolute pathname. ~ This appears to be an implementation error; please contact ~ the ACL2 implementors." str (f-get-global 'connected-book-directory state))) (t (assign connected-book-directory (canonical-dirname! (maybe-add-separator (our-merge-pathnames (f-get-global 'connected-book-directory state) str)) ctx state))))))))) (defmacro set-cbd (str) ":Doc-Section books to set the connected book directory~/ ~bv[] Example Forms: ACL2 !>:set-cbd \"/usr/home/smith/\" ACL2 !>:set-cbd \"my-acl2/books\" ~ev[] ~l[cbd] for a description of the connected book directory.~/ ~bv[] General Form: (set-cbd str) ~ev[] where ~c[str] is a nonempty string that represents the desired directory (~pl[pathname]). This command sets the connected book directory (~pl[cbd]) to the string representing the indicated directory. Thus, this command may determine which files are processed by ~ilc[include-book] and ~ilc[certify-book] ~il[command]s typed at the top-level. However, the ~ilc[cbd] is also temporarily set by those two book processing ~il[command]s. ~sc[Important]: Pathnames in ACL2 are in the Unix (trademark of AT&T) style. That is, the character ``~c[/]'' separates directory components of a pathname, and pathnames are absolute when they start with this character, and relative otherwise. ~l[pathname]." `(set-cbd-fn ,str state)) (defun set-cbd-state (str state) ; This is similar to set-cbd-fn, but returns state and should be used only when ; no error is expected. (mv-let (erp val state) (set-cbd-fn str state) (declare (ignore val)) (prog2$ (and erp (er hard 'set-cbd-state "Implementation error: Only use ~x0 when it is known that ~ this will not cause an error." 'set-cbd-state)) state))) (defun parse-book-name (dir x extension ctx state) ; This function takes a directory name, dir, and a user supplied book name, x, ; which is a string, and returns (mv full dir familiar), where full is the full ; book name string, dir is the directory name, and familiar is the familiar ; name string. Extension is either nil or a string such as ".lisp" and the ; full book name is given the extension if it is non-nil. ; Given dir and x with extension=".lisp" ; "/usr/home/moore/" "nasa-t3/arith" ; user name ; this function produces ; (mv "/usr/home/moore/nasa-t3/arith.lisp" ; full name ; "/usr/home/moore/nasa-t3/" ; directory name ; "arith") ; familiar name ; On the other hand, if x is "/usr/home/kaufmann/arith" then the result is ; (mv "/usr/home/kaufmann/arith.lisp" ; "/usr/home/kaufmann/" ; "arith") ; We work with Unix-style pathnames. ; Note that this function merely engages in string processing. It does not ; actually guarantee that the named file exists or that the various names are ; in any sense well-formed. It does not change the connected book directory. ; If x is not a string and not well-formed as a structured pathname, the result ; is (mv nil nil x). Thus, if the full name returned is nil, we know something ; is wrong and the short name returned is whatever junk the user supplied. (cond ((stringp x) (let* ((lst (coerce x 'list)) (rlst (reverse lst)) (temp (member *directory-separator* rlst))) ; If x is "project/task3/arith.lisp" then temp is "project/task3/" except is a ; list of chars and is in reverse order (!). (let ((familiar (coerce (reverse (first-n-ac (- (length x) (length temp)) rlst nil)) 'string)) (dir1 (extend-pathname dir (coerce (reverse temp) 'string) state))) (mv (if extension (concatenate 'string dir1 familiar extension) (concatenate 'string dir1 familiar)) dir1 familiar)))) (t (mv (er hard ctx "A book name must be a string, but ~x0 is not a string." x) nil x)))) (defun cbd-fn (state) (or (f-get-global 'connected-book-directory state) (er hard 'cbd "The connected book directory has apparently not yet been set. ~ This could be a sign that the top-level ACL2 loop, generally ~ entered using (LP), has not yet been entered."))) (defmacro cbd nil ":Doc-Section Books connected book directory string~/ ~bv[] Example: ACL2 !>:cbd \"/usr/home/smith/\" ~ev[] The connected book directory is a nonempty string that specifies a directory as an absolute pathname. (~l[pathname] for a discussion of file naming conventions.) When ~ilc[include-book] is given a relative book name it elaborates it into a full book name, essentially by appending the connected book directory string to the left and ~c[\".lisp\"] to the right. (For details, ~pl[book-name] and also ~pl[full-book-name].) Furthermore, ~ilc[include-book] temporarily sets the connected book directory to the directory string of the resulting full book name so that references to inferior ~il[books] in the same directory may omit the directory. ~l[set-cbd] for how to set the connected book directory string.~/ ~bv[] General Form: (cbd) ~ev[] This is a macro that expands into a term involving the single free variable ~ilc[state]. It returns the connected book directory string. The connected book directory (henceforth called the ``~c[cbd]'') is used by ~ilc[include-book] to elaborate the supplied book name into a full book name (~pl[full-book-name]). For example, if the ~c[cbd] is ~c[\"/usr/home/smith/\"] then the elaboration of the ~il[book-name] ~c[\"project/task-1/arith\"] (to the ~c[\".lisp\"] extension) is ~c[\"/usr/home/smith/project/task-1/arith.lisp\"]. That ~il[full-book-name] is what ~il[include-book] opens to read the source text for the book. The ~c[cbd] may be changed using ~ilc[set-cbd] (~pl[set-cbd]). Furthermore, during the processing of the ~il[events] in a book, ~ilc[include-book] sets the ~c[cbd] to be the directory string of the ~il[full-book-name] of the book. Thus, if the ~c[cbd] is ~c[\"/usr/home/smith/\"] then during the processing of ~il[events] by ~bv[] (include-book \"project/task-1/arith\") ~ev[] the ~c[cbd] will be set to ~c[\"/usr/home/smith/project/task-1/\"]. Note that if ~c[\"arith\"] recursively includes a subbook, say ~c[\"naturals\"], that resides on the same directory, the ~ilc[include-book] event for it may omit the specification of that directory. For example, ~c[\"arith\"] might contain the event ~bv[] (include-book \"naturals\"). ~ev[] In general, suppose we have a superior book and several inferior ~il[books] which are included by ~il[events] in the superior book. Any inferior book residing on the same directory as the superior book may be referenced in the superior without specification of the directory. We call this a ``relative'' as opposed to ``absolute'' naming. The use of relative naming is preferred because it permits ~il[books] (and their accompanying inferiors) to be moved between directories while maintaining their ~il[certificate]s and utility. Certified ~il[books] that reference inferiors by absolute file names are unusable (and rendered uncertified) if the inferiors are moved to new directories. ~em[Technical Note and a Challenge to Users:] After elaborating the book name to a full book name, ~ilc[include-book] opens a channel to the file to process the ~il[events] in it. In some host Common Lisps, the actual file opened depends upon a notion of ``connected directory'' similar to our connected book directory. Our intention in always elaborating book names into absolute filename strings (~pl[pathname] for terminology) is to circumvent the sensitivity to the connected directory. But we may have insufficient control over this since the ultimate file naming conventions are determined by the host operating system rather than Common Lisp (though, we do check that the operating system ``appears'' to be one that we ``know'' about). Here is a question, which we'll pose assuming that we have an operating system that calls itself ``Unix.'' Suppose we have a file name, filename, that begins with a slash, e.g., ~c[\"/usr/home/smith/...\"]. Consider two successive invocations of CLTL's ~bv[] (open filename :direction :input) ~ev[] separated only by a change to the operating system's notion of connected directory. Must these two invocations produce streams to the same file? A candidate string might be something like ~c[\"/usr/home/smith/*/usr/local/src/foo.lisp\"] which includes some operating system-specific special character to mean ``here insert the connected directory'' or, more generally, ``here make the name dependent on some non-ACL2 aspect of the host's state.'' If such ``tricky'' name strings beginning with a slash exist, then we have failed to isolate ACL2 adequately from the operating system's file naming conventions. Once upon a time, ACL2 did not insist that the ~c[cbd] begin with a slash and that allowed the string ~c[\"foo.lisp\"] to be tricky because if one were connected to ~c[\"/usr/home/smith/\"] then with the empty ~c[cbd] ~c[\"foo.lisp\"] is a full book name that names the same file as ~c[\"/usr/home/smith/foo.lisp\"]. If the actual file one reads is determined by the operating system's state then it is possible for ACL2 to have two distinct ``full book names'' for the same file, the ``real'' name and the ``tricky'' name. This can cause ACL2 to include the same book twice, not recognizing the second one as redundant." `(cbd-fn state)) ; We now develop code to "fix" the commands in the certification world before ; placing them in the portcullis of the certificate, in order to eliminate ; relative pathnames in include-book forms. See the comment in ; fix-portcullis-cmds. (mutual-recursion (defun make-include-books-absolute (form cbd dir names make-event-parent os ctx state) ; WARNING: Keep this in sync with chk-embedded-event-form, ; destructure-expansion, and elide-locals-rec. ; See the comment in fix-portcullis-cmds for a discussion. Starting after ; Version_3.6.1, we allow an include-book pathname for a portcullis command to ; remain a relative pathname if it is relative to the cbd of the book. That ; change avoids a failure to certify community book ; books/fix-cert/test-fix-cert1.lisp that initially occurred when we started ; including portcullis commands in the check-sum (with the introduction of ; function check-sum-cert, caused by the renaming of an absolute pathname in an ; include-book portcullis command. ; Form is a command from the current ACL2 world that is known to be an embedded ; event form with respect to names. Keep this function in sync with ; chk-embedded-event-form. ; Cbd is either nil (arising from an argument of t for certify-book; see the ; corresponding call of chk-acceptable-certify-book1 in ; chk-acceptable-certify-book) or is the connected-book-directory at the ; conclusion of form, and hence (since form is an embedded event form) at the ; beginning of form. Dir is the directory of the book being certified. See ; the include-book case below for how these are used. ; If make-event-parent is non-nil, then it is a make-event form whose expansion ; is being considered, and we cause an error rather than converting. (cond ((atom form) (value form)) ; This should never happen. ((eq (car form) 'skip-proofs) (er-let* ((x (make-include-books-absolute (cadr form) cbd dir names make-event-parent os ctx state))) (value (list (car form) x)))) ((eq (car form) 'local) ; Local events will be skipped when including a book, and in particular when ; evaluating portcullis commands from a book's certificate, so we can ignore ; local events here. (value form)) ((eq (car form) 'progn) (er-let* ((rest (make-include-books-absolute-lst (cdr form) cbd dir names make-event-parent os ctx state))) (value (cons (car form) rest)))) ((eq (car form) 'value) (value form)) ((and (eq (car form) 'include-book) ; Recall that we are processing the portcullis commands for a book, bk, that is ; in the process of being certified. We may want to ensure that form, an ; include-book form, uses an absolute pathname, so that form refers to the same ; book as when originally processed as it does when later being processed as a ; portcullis command of bk. ; Consider the normal case, where cbd is not nil. When bk is later included, ; the connected-book-directory will be bound to dir, which is the directory of ; the book being certified. Therefore, if the connected-book-directory at the ; time form was processed, namely cbd, is the same as dir, then we do not need ; bk to be an absolute pathname: the same connected-book-directory as when ; originally processed (namely, cbd) will be used as the ; connected-book-directory when the book is being included as a portcullis ; command of bk (namely, connected-book-directory dir). ; If cbd is nil then we are recovering portcullis commands from an existing ; certificate, so relative pathnames have already been converted to absolute ; pathnames when necessary, and no conversion is needed here. ; To summarize: if cbd is nil or if cbd and dir are equal, we can skip any ; pathname conversion and fall through to the next top-level COND branch, where ; form is returned unchanged. We thus consider pathname conversion when both ; of those conditions fail. (and cbd (not (equal cbd dir))) ; We do not need to convert a relative pathname to an absolute pathname if the ; :dir argument already specifies how to do this. Recall that the table guard ; of the acl2-defaults-table specifies that :dir arguments are absolute ; pathnames. (assert$ (keyword-value-listp (cddr form)) ; as form is a legal event (not (assoc-keyword :dir form))) (assert$ (stringp (cadr form)) ; as form is a legal event (not (absolute-pathname-string-p (cadr form) nil ; no directory check necessary here os)))) (cond (make-event-parent ; It would be a mistake to create an absolute pathname in a case like ; (make-event '(include-book "foo") :check-expansion t), because the expansion ; check would later fail. Instead, in such a (presumably rare) case, the user ; needs to modify the make-event form to create an absolute pathname. (er soft ctx "Each include-book form in the certification world must be given ~ absolute pathname before it is saved in the certificate as a ~ portcullis command. ACL2 generally figures out a suitable ~ absolute pathname when the pathname is relative. But the present ~ form, ~x0, comes from the expansion of a make-event form with ~ (first) argument ~x1 and non-nil :check-expansion argument. ~ ~ Consider changing this make-event form to produce an include-book ~ with an absolute pathname instead." form (cadr make-event-parent))) (t (mv-let (full-book-name directory-name familiar-name) (parse-book-name cbd (cadr form) nil ctx state) (declare (ignore directory-name familiar-name)) (value (list* 'include-book full-book-name (cddr form))))))) ((and (eq (car form) 'add-include-book-dir) ; This case is very similar to the include-book case handled in the preceding ; COND branch, above. See that case for explanatory comments. In order to see ; an unfortunate include-book failure WITHOUT this case, try the following. We ; assume two directories, D and D/SUB/, and trivial books D/foo.lisp and ; D/SUB/bar.lisp. ; In directory D, start up ACL2 and then: ; (add-include-book-dir :main "./") ; (certify-book "foo" 1) ; (u) ; :q ; (save-exec "my-acl2" "testing") ; Then in directory D/SUB/, start up ../my-acl2 and then: ; (include-book "foo" :dir :main) ; (certify-book "bar" 2) ; Finally, in directory D/SUB/, start up ../my-acl2 and then: ; (include-book "bar") ; You'll see this error, because ; ACL2 Error in ( INCLUDE-BOOK "foo" ...): There is no file named ; "D/SUB/foo.lisp" that can be opened for input. (and cbd (not (equal cbd dir))) (assert$ (stringp (caddr form)) ; as form is a legal event (not (absolute-pathname-string-p (caddr form) nil ; no directory check necessary here os)))) (cond (make-event-parent (er soft ctx "Each add-include-book-dir form in the certification world must be ~ given absolute pathname before it is saved in the certificate as a ~ portcullis command. ACL2 generally figures out a suitable ~ absolute pathname when the pathname is relative. But the present ~ form, ~x0, comes from the expansion of a make-event form with ~ (first) argument ~x1 and non-nil :check-expansion argument. ~ ~ Consider changing this make-event form to produce an ~ add-include-book-dir with an absolute pathname instead." form (cadr make-event-parent))) (t (value (list 'add-include-book-dir (cadr form) (extend-pathname cbd (caddr form) state)))))) ((member-eq (car form) names) ; Note that we do not have a special case for encapsulate. Every include-book ; inside an encapsulate is local (see chk-embedded-event-form), hence would not ; be changed by this function anyhow. If we allow non-local include-books in ; an encapsulate, then we will need to add a case for encapsulate that is ; similar to the case for progn. (value form)) ((eq (car form) 'make-event) (let ((expansion (cadr (assoc-keyword :check-expansion (cddr form))))) (cond ((not (consp expansion)) (er soft ctx "Implementation error: we had thought that every make-event ~ form in the certification world would have a consp ~ :check-expansion field, yet we found the following. Please ~ contact the ACL2 implementors.~|~x0" form)) (t (er-progn (make-include-books-absolute expansion cbd dir names form os ctx state) (value form)))))) ((and (member-eq (car form) '(with-output with-prover-step-limit with-prover-time-limit)) (consp (cdr form))) (er-let* ((form1 (make-include-books-absolute (car (last form)) cbd dir names make-event-parent os ctx state))) (value (append (butlast form 1) (list form1))))) ((getprop (car form) 'macro-body nil 'current-acl2-world (w state)) (er-let* ((form1 (macroexpand1 form ctx state))) (make-include-books-absolute form1 cbd dir names make-event-parent os ctx state))) (t (value (er hard ctx "Implementation error in make-include-books-absolute: ~ unrecognized event type, ~x0. Make-include-books-absolute ~ needs to be kept in sync with chk-embedded-event-form. ~ Please send this error message to the implementors." (car form)))))) (defun make-include-books-absolute-lst (forms cbd dir names make-event-parent os ctx state) (if (endp forms) (value nil) (er-let* ((first (make-include-books-absolute (car forms) cbd dir names make-event-parent os ctx state)) (rest (make-include-books-absolute-lst (cdr forms) cbd dir names make-event-parent os ctx state))) (value (cons first rest))))) ) (defun first-known-package-alist (wrld-segment) (cond ((null wrld-segment) nil) ((and (eq (caar wrld-segment) 'known-package-alist) (eq (cadar wrld-segment) 'global-value)) (let* ((kpa (cddar wrld-segment))) (if (eq kpa *acl2-property-unbound*) ; We do not expect to find *acl2-property-unbound* here. If we do find it, ; then we cause an error. (er hard 'first-known-package-alist "Implementation error! Unexpected find of unbound ~ known-package-alist value! Please contact the ACL2 ~ implementors and send this message.") kpa))) (t (first-known-package-alist (cdr wrld-segment))))) (defmacro string-prefixp (root string) ; We return a result propositionally equivalent to ; (and (<= (length root) (length string)) ; (equal root (subseq string 0 (length root)))) ; but, unlike subseq, without allocating memory. `(search ,root ,string :start2 0)) (defun relativize-book-path (filename root) ; If the given filename is an absolute pathname extending the absolute ; directory name root, then return (:system . suffix), where suffix is a ; relative pathname that points to the same file with respect to root. ; To admit this in :logic mode with guards verified, first prove: ; (defthm coerce-to-list-is-positive-linear ; (implies (and (not (equal filename "")) ; (stringp filename)) ; (< 0 (len (coerce filename 'list)))) ; :hints (("Goal" ; :use ((:instance coerce-inverse-2 ; (x filename))) ; :expand ((len (coerce filename 'list))))) ; :rule-classes :linear) (declare (xargs :guard (and (stringp filename) (stringp root)))) (cond ((and (stringp filename) ; could already be (:system . fname) (string-prefixp root filename)) (cons :system (subseq filename (length root) nil))) (t filename))) (defun relativize-book-path-lst1 (lst root) (declare (xargs :guard (and (string-listp lst) (stringp root)))) (cond ((endp lst) nil) (t (cons (relativize-book-path (car lst) root) (relativize-book-path-lst1 (cdr lst) root))))) (defun relativize-book-path-lst (lst root current) (declare (xargs :guard (and (string-listp lst) (stringp root) (stringp current)))) (cond ((string-prefixp root current) (relativize-book-path-lst1 lst root)) (t lst))) (defun defpkg-items-rec (new-kpa old-kpa system-books-dir connected-book-directory ctx w state acc) ; For background on the discussion below, see the Essay on Hidden Packages. ; We are given a world w (for example, the certification world of a ; certify-book command). Old-kpa is the known-package-alist of w. New-kpa is ; another known-package-alist, which may include entries not in old-kpa (for ; example, the known-package-alist after executing each event in the ; admissibility pass of certify-book). We return a list of "defpkg items" for ; names of new-kpa not in old-kpa, where each item is of the form (list name ; imports body doc book-path). The intention is that the item can be used to ; form a defpkg event with indicated name, body, doc and book-path, where body ; may have been modified from a corresponding defpkg event so that it is ; suitable for evaluation in w. Here, book-path is the book-path to be used if ; such an event is to be added to the end of the portcullis commands in the ; certificate of a book being certified. ; It is helpful for efficiency if w is the current-acl2-world or a reasonably ; short extension of it, since we call termp and untranslate on that world. (cond ((endp new-kpa) (value acc)) (t (let* ((e (car new-kpa)) (n (package-entry-name e))) (cond ((find-package-entry n old-kpa) (defpkg-items-rec (cdr new-kpa) old-kpa system-books-dir connected-book-directory ctx w state acc)) (t (let* ((imports (package-entry-imports e)) (event (package-entry-defpkg-event-form e)) (name (cadr event)) (body (caddr event)) (doc (cadddr event)) (tterm (package-entry-tterm e)) (book-path ; We use relative pathnames when possible, to support relocation of .cert files ; (as is done as of August 2010 by Debian ACL2 release and ACL2s). (relativize-book-path-lst (package-entry-book-path e) system-books-dir connected-book-directory))) (mv-let (erp pair state) ; It's perfectly OK for erp to be non-nil here. That case is handled below. ; So if you have called break-on-error and wind up here, it's a reasonable bet ; that it's nothing to worry about! (simple-translate-and-eval body nil nil "The second argument to defpkg" ctx w state nil) (defpkg-items-rec (cdr new-kpa) old-kpa system-books-dir connected-book-directory ctx w state (cons (list name imports (assert$ event (assert$ (equal n name) (cond ((and (not erp) (or (equal (cdr pair) ; optimization imports) (equal (sort-symbol-listp (cdr pair)) imports)) (equal tterm (car pair))) body) ((termp tterm w) tterm) (t (kwote imports))))) doc book-path) acc)))))))))) (defun defpkg-items (new-kpa ctx w state) ; This is just a wrapper for defpkg-items-rec, with error output turned off ; (because of calls of translate). See the comment for defpkg-items-rec. (state-global-let* ((inhibit-output-lst (cons 'error (f-get-global 'inhibit-output-lst state)))) (defpkg-items-rec new-kpa (global-val 'known-package-alist w) (f-get-global 'system-books-dir state) (f-get-global 'connected-book-directory state) ctx w state nil))) (defun new-defpkg-list2 (imports all-defpkg-items acc seen) ; Extends acc with items (cons pkg-name rest) from all-defpkg-items not already ; in acc or seen for which pkg-name is the symbol-package-name of a symbol in ; imports. (cond ((endp imports) acc) (t (let ((p (symbol-package-name (car imports)))) (cond ((or (assoc-equal p acc) (assoc-equal p seen)) (new-defpkg-list2 (cdr imports) all-defpkg-items acc seen)) (t (let ((item (assoc-equal p all-defpkg-items))) (cond (item (new-defpkg-list2 (cdr imports) all-defpkg-items (cons item acc) seen)) (t (new-defpkg-list2 (cdr imports) all-defpkg-items acc seen)))))))))) (defun make-hidden-defpkg (name imports/doc/book-path) ; Warning: Keep this in sync with equal-modulo-hidden-defpkgs. (let ((imports (car imports/doc/book-path)) (doc (cadr imports/doc/book-path)) (book-path (caddr imports/doc/book-path))) `(defpkg ,name ,imports ,doc ,book-path t))) (defun new-defpkg-list1 (defpkg-items all-defpkg-items base-kpa earlier-kpa added-defpkgs) ; See the comment in new-defpkg-list. Here, we maintain an accumulator, ; added-defpkgs, that contains the defpkg events that need to be added based on ; what we have already processed in defpkg-items, in reverse order. (cond ((endp defpkg-items) added-defpkgs) (t (let* ((added-defpkgs (new-defpkg-list1 (cdr defpkg-items) all-defpkg-items base-kpa earlier-kpa added-defpkgs)) (item (car defpkg-items)) (name (car item))) (cond ((find-package-entry name base-kpa) added-defpkgs) (t ; we want to add event, so may need to add some already "discarded" (cons (make-hidden-defpkg name (cddr item)) (new-defpkg-list1 (new-defpkg-list2 (cadr item) ; imports all-defpkg-items nil added-defpkgs) all-defpkg-items ; We are considering all defpkg events added in support of import lists. We ; need to take the appropriate closure in order to get all supporting defpkg ; events that are not represented in earlier-kpa, so this call uses earlier-kpa ; in place of base-kpa. earlier-kpa earlier-kpa added-defpkgs)))))))) (defun new-defpkg-list (defpkg-items base-kpa earlier-kpa) ; For background on the discussion below, see the Essay on Hidden Packages. ; Defpkg-items is a list of "defpkg items" each of the form (list name imports ; body doc book-path) representing a list of package definitions. We return a ; list of defpkg events, corresponding to some of these defpkg items, that can ; be executed in a world whose known-package-alist is earlier-kpa. The primary ; reason a defpkg is in the returned list is that its package is not in ; base-kpa (not even hidden). The second reason is that we need to define a ; package P1 not already in earlier-kpa if we add another package P2 whose ; import list contains a symbol in package P1; we close under this process. ; This function is called at the end of the include-book phase of certify-book. ; In that case, base-kpa is the known-package-alist at that point, earlier-kpa ; is the known-package-alist of the certification world, and defpkg-items ; contains an item for each name of a package in the known-package-alist at the ; end of the earlier, admissibility pass of certify-book that was not defined ; in the certification world. To illustrate the "second reason" above, let us ; suppose that the book being certified contains forms (include-book "book1") ; and (local (include-book "book2")), where book1 defines (defpkg "PKG1" ...) ; and book2 defines (defpkg "PKG2" '(PKG1::SYM)). Then we want to add the ; definition of "PKG2" to the portcullis, but in order to do so, we need to add ; the definition of "PKG1" as well, even though it will eventually be included ; by way of book1. And, we need to be sure to add the defpkg of "PKG1" before ; that of "PKG2". ; This function is also called on behalf of puff-fn1, where defpkg-items ; corresponds to the packages in known-package-alist in the world at completion ; of the command about to be puffed, and base-kpa and earlier-kpa correspond to ; the known-package-alist just before that command. In that case there is no ; need for the "second reason" above, but for simplicity we call this same ; function. (reverse (remove-duplicates-equal (new-defpkg-list1 defpkg-items defpkg-items base-kpa earlier-kpa nil)))) (mutual-recursion ; We check that a given term or list of terms is acceptable even if (cdr ; (assoc-eq ':ignore-ok (table-alist 'acl2-defaults-table w))) is nil. (defun term-ignore-okp (x) (cond ((or (atom x) (fquotep x)) t) ((symbolp (ffn-symb x)) (term-list-ignore-okp (fargs x))) (t ; lambda (and (null (set-difference-eq (lambda-formals (ffn-symb x)) (all-vars (lambda-body (ffn-symb x))))) (term-list-ignore-okp (fargs x)))))) (defun term-list-ignore-okp (x) (cond ((endp x) t) ((term-ignore-okp (car x)) (term-list-ignore-okp (cdr x))) (t nil))) ) (defun hidden-defpkg-events1 (kpa system-books-dir connected-book-directory w ctx state acc) ; Warning: Keep this in sync with hidden-depkg-events-simple. (cond ((endp kpa) (value (reverse acc))) ((not (package-entry-hidden-p (car kpa))) (hidden-defpkg-events1 (cdr kpa) system-books-dir connected-book-directory w ctx state acc)) (t (let* ((e (car kpa)) (n (package-entry-name e)) (imports (package-entry-imports e)) (event (package-entry-defpkg-event-form e)) (name (cadr event)) (body (caddr event)) (doc (cadddr event)) (tterm (package-entry-tterm e)) (book-path (relativize-book-path-lst (package-entry-book-path e) system-books-dir connected-book-directory))) (mv-let (erp pair state) (simple-translate-and-eval body nil nil "The second argument to defpkg" ctx w state nil) (hidden-defpkg-events1 (cdr kpa) system-books-dir connected-book-directory w ctx state (cons `(defpkg ,name ,(assert$ event (assert$ (equal n name) (cond ((and (not erp) (or (equal (cdr pair) ; optimization imports) (equal (sort-symbol-listp (cdr pair)) imports)) (equal tterm (car pair))) (if (term-ignore-okp tterm) body (kwote imports))) ((and (termp tterm w) (term-ignore-okp tterm)) tterm) (t (kwote imports))))) ,doc ,book-path t) acc))))))) (defun hidden-defpkg-events (kpa w ctx state) (state-global-let* ((inhibit-output-lst *valid-output-names*)) (hidden-defpkg-events1 kpa (f-get-global 'system-books-dir state) (f-get-global 'connected-book-directory state) w ctx state nil))) (defun fix-portcullis-cmds1 (dir cmds cbds ans names os ctx state) (cond ((null cmds) (value ans)) (t (er-let* ((cmd (make-include-books-absolute (car cmds) (car cbds) dir names nil os ctx state))) (fix-portcullis-cmds1 dir (cdr cmds) (cdr cbds) (cons cmd ans) names os ctx state))))) (defun fix-portcullis-cmds (dir cmds cbds names os wrld ctx state) ; This function is called during certification of a book whose directory's ; absolute pathname is dir. It modifies cmds by making relative pathnames ; absolute when necessary, and also by adding defpkg events for hidden packages ; from the certification world, as explained in the Essay on Hidden Packages. ; We explain these two aspects in turn. ; Certify-book needs to insist that each pathname for an include-book in the ; portcullis refer to the intended file, in particular so that the actual file ; read is not dependent upon cbd. Consider for example: ; :set-cbd "/usr/home/moore/" ; (include-book "prelude") ; :set-cbd "/usr/local/src/library/" ; (certify-book "user") ; A naive implementation would provide a portcullis for "user" that contains ; (include-book "prelude"). But there is no clue as to the directory on which ; "prelude" resides. Note that "prelude" does not represent an absolute ; pathname. If it did represent an absolute pathname, then it would have to be ; the full book name because parse-book-name returns x when x represents an ; absolute pathname. ; We deal with the issue above by allowing relative pathnames for include-book ; commands in the certification world, but modifying them, when necessary, to ; be appropriate absolute pathnames. We say "when necessary" because ; include-book-fn sets the cbd to the directory of the book, so if the relative ; pathname resolves against that cbd to be the correct full book name, then no ; modification is necessary. ; This function takes the original cmds and a list of embedded event forms. ; We return a list of commands that is guaranteed to be free of include-books ; of relative pathnames, that nevertheless is equivalent to the original cmds ; from the standpoint of subsequent embedded events. (Or, we return an error, ; but in fact we believe that that will not happen.) ; As mentioned at the outset above, this function also adds defpkg events. We ; trust that the portcullis is a legal sequence of commands (actually, events), ; so the only point is to added hidden packages as per the Essay on Hidden ; Packages. ; Call this function using the same names parameter as that used when verifying ; that cmds is a list of embedded event forms. (er-let* ((new-cmds (fix-portcullis-cmds1 dir cmds cbds nil names os ctx state)) (new-defpkgs (hidden-defpkg-events (global-val 'known-package-alist wrld) wrld ctx state))) (value (revappend new-cmds new-defpkgs)))) (defun collect-uncertified-books (alist) ; Alist is an include-book-alist and thus contains elements of the ; form described in include-book-alist-subsetp. A typical element is ; (full-book-name user-book-name familiar-name cert-annotations ; . ev-lst-chk-sum) and ev-lst-chk-sum is nil if the book has not been ; certified. (cond ((null alist) nil) ((null (cddddr (car alist))) ; ev-lst-chk-sum (cons (caar alist) ; full-book-name (collect-uncertified-books (cdr alist)))) (t (collect-uncertified-books (cdr alist))))) (defun chk-in-package (channel file empty-okp ctx state) ; Channel must be an open input object channel. We assume (for error ; reporting purposes) that it is associated with the file named file. ; We read the first form in it and cause an error unless that form is ; an in-package. If it is an in-package, we return the package name. (state-global-let* ((current-package "ACL2")) (mv-let (eofp val state) (read-object channel state) (cond (eofp (cond (empty-okp (value nil)) (t (er soft ctx "The file ~x0 is empty. An IN-PACKAGE form, ~ at the very least, was expected." file)))) ((and (true-listp val) (= (length val) 2) (eq (car val) 'in-package) (stringp (cadr val))) (cond ((find-non-hidden-package-entry (cadr val) (known-package-alist state)) (value (cadr val))) (t (er soft ctx "The argument to IN-PACKAGE must be a known ~ package name, but ~x0, used in the first form ~ in ~x1, is not. The known packages are~*2" (cadr val) file (tilde-*-&v-strings '& (strip-non-hidden-package-names (known-package-alist state)) #\.))))) (t (er soft ctx "The first form in ~x0 was expected to be ~ (IN-PACKAGE \"pkg\") where \"pkg\" is a known ~ ACL2 package name. See :DOC book-contents. The first ~ form was, in fact, ~x1." file val)))))) (defmacro ill-formed-certificate-er (ctx mark file1 file2 &optional (bad-object 'nil bad-objectp)) ; Mark should be a symbol or a msg. `(er soft ,ctx "The certificate for the book ~x0 is ill-formed. Delete or rename the ~ file ~x1 and recertify ~x0. Remember that the certification world for ~ ~x0 is described in the portcullis of ~x1 (see :DOC portcullis) so you ~ might want to look at ~x1 to remind yourself of ~x0's certification~ ~ world.~|Debug note for developers:~|~@2~@3" ,file1 ,file2 ,(if (and (consp mark) (eq (car mark) 'quote) (symbolp (cadr mark))) (symbol-name (cadr mark)) mark) ,(if bad-objectp ; Developer debug: ; `(msg "~|Bad object: ~X01" ,bad-object nil) `(msg "~|Bad object: ~x0" ,bad-object) ""))) (defun include-book-er-warning-summary (keyword suspect-book-action-alist state) ; See include-book-er for how this result is used. We separate out this part ; of the computation so that we know whether or not something will be printed ; before computing the warning or error message. ; We return nil to cause a generic error, a keyword to cause an error ; suggesting the use of value t for that keyword, and a string for a potential ; warning. (let ((keyword-string (case keyword (:uncertified-okp "Uncertified") (:skip-proofs-okp "Skip-proofs") (:defaxioms-okp "Defaxioms") (t (if (eq keyword t) nil (er hard 'include-book-er "Include-book-er does not know the include-book keyword ~ argument ~x0." keyword)))))) (cond ((eq keyword t) nil) ((assoc-eq keyword suspect-book-action-alist) (cond ((cdr (assoc-eq keyword suspect-book-action-alist)) (cond ((if (eq keyword :skip-proofs-okp) (not (f-get-global 'skip-proofs-okp-cert state)) (and (eq keyword :defaxioms-okp) (not (f-get-global 'defaxioms-okp-cert state)))) ; Although suspect-book-action-alist allows this (implicit) include-book, we ; are attempting this include-book underneath a certify-book that disallows ; this keyword. We signify this case by overloading warning-summary to be this ; keyword. keyword) (t keyword-string))) (t keyword))) (t (er hard 'include-book-er "There is a discrepancy between the keywords in the ~ suspect-book-action-alist, ~x0, and the keyword, ~x1, supplied ~ to include-book-er." suspect-book-action-alist keyword))))) (defun include-book-er1 (file1 file2 msg warning-summary ctx state) (cond ((null warning-summary) (er soft ctx "~@2" file1 file2 msg)) ((symbolp warning-summary) ; keyword (er soft ctx "~@0 This is illegal because we are currently attempting ~ certify-book or include-book with ~x1 set to NIL. You can ~ avoid this error by using a value of T for ~x1; see :DOC ~ certify-book and see :DOC include-book." (list "~@2" (cons #\0 file1) (cons #\1 file2) (cons #\2 msg)) warning-summary)) (t (pprogn (warning$ ctx warning-summary "~@2" file1 file2 msg) (value nil))))) (defun include-book-er (file1 file2 msg keyword suspect-book-action-alist ctx state) ; Depending on various conditions we either do nothing and return (value nil), ; print a warning, or cause an error. File1 and file2 are the full book name ; and its .cert file, respectively. (Well, sometimes file2 is nil -- we never ; use it ourselves but msg might and supplies it when needed.) Msg is an ; arbitrary ~@ fmt message, which is used as the error message and used in the ; warning message. Suspect-book-action-alist is the alist manufactured by ; include-book, specifying the values of its keyword arguments. Among these ; are arguments that control our behavior on these errors. Keyword specifies ; the kind of error this is, using the convention that it is either t, meaning ; cause an error, or the keyword used by include-book to specify the behavior. ; For example, if this error reports the lack of a certificate, then keyword is ; :uncertified-okp. (let ((warning-summary (include-book-er-warning-summary keyword suspect-book-action-alist state))) ; If warning-summary is nil, we cause an error. Otherwise, it is summary ; of the desired warning. (include-book-er1 file1 file2 msg warning-summary ctx state))) (defun post-alist-from-channel (x y ch state) ; We assume that all necessary packages exist so that we can read the ; certificate file for full-book-name, without errors caused by unknown package ; names in symbols occurring in the porcullis commands or make-event ; expansions. If that assumption may not hold, consider using ; post-alist-from-pcert1 instead. (mv-let (eofp obj state) (cond ((eq y ; last object read ':EXPANSION-ALIST) ; We really don't need this special case, given the assumptions expressed in ; the comment above. But we might as well use read-object-suppress here, since ; maybe it does less consing. However, we cannot do the same for ; :BEGIN-PORTCULLIS-CMDS, because an indefinite number of event forms follows ; that keyword (until :END-PORTCULLIS-CMDS). (mv-let (eofp state) (read-object-suppress ch state) (mv eofp nil state))) (t (read-object ch state))) (cond ((or eofp (eq obj :PCERT-INFO)) (mv x state)) (t (post-alist-from-channel y obj ch state))))) (defun certificate-file-and-input-channel1 (full-book-name cert-op state) (let ((cert-name (convert-book-name-to-cert-name full-book-name cert-op))) (mv-let (ch state) (open-input-channel cert-name :object state) (mv ch cert-name state)))) (defmacro pcert-op-p (cert-op) `(member-eq ,cert-op '(:create-pcert :create+convert-pcert :convert-pcert))) (defun certificate-file-and-input-channel (full-book-name old-cert-op state) ; Old-cert-op is non-nil when we are looking for an existing certificate file ; built for that cert-op. Otherwise we first look for a .cert file, then a ; .pcert0 file, and otherwise (finally) a .pcert1 file. We prefer a .pcert0 to ; a .pcert1 file simply because a .pcert1 file is produced by copying from a ; .pcert0 file; thus a .pcert1 file may be incomplete if it is consulted while ; that copying is in progress. (The .pcert0 file, on the other hand, is ; produced atomically just as a .cert file is produced atomically, by moving a ; temporary file.) (cond (old-cert-op (mv-let (ch cert-name state) (certificate-file-and-input-channel1 full-book-name old-cert-op state) (mv ch cert-name (if (pcert-op-p old-cert-op) old-cert-op nil) state))) (t (mv-let ; try .cert first (ch cert-name state) (certificate-file-and-input-channel1 full-book-name t state) (cond (ch (mv ch cert-name nil state)) (t (mv-let ; try .pcert0 next (ch cert-name state) (certificate-file-and-input-channel1 full-book-name :create-pcert state) (cond (ch (mv ch cert-name :create-pcert state)) (t (mv-let ; finally try .pcert1 (ch cert-name state) (certificate-file-and-input-channel1 full-book-name :convert-pcert state) (mv ch cert-name :convert-pcert state))))))))))) (defun cert-annotations-and-checksum-from-cert-file (full-book-name state) ; See the requirement in post-alist-from-channel, regarding necessary packages ; existing. (mv-let (ch cert-name pcert-op state) (certificate-file-and-input-channel full-book-name (if (eq (cert-op state) :convert-pcert) :create-pcert nil) state) (declare (ignore cert-name pcert-op)) (cond (ch (mv-let (x state) (post-alist-from-channel nil nil ch state) (pprogn (close-input-channel ch state) (value (cdddr (car x)))))) (t (silent-error state))))) (defun tilde-@-cert-post-alist-phrase (full-book-name familiar-name cdr-reqd-entry cdr-actual-entry state) (declare (ignore cdr-reqd-entry)) (mv-let (erp pair state) (cert-annotations-and-checksum-from-cert-file full-book-name state) (mv (let ((cert-maybe-unchanged-p (cond (erp ; certificate was deleted nil) ((null (cdr cdr-actual-entry)) ; But it is possible that checksum in the current include-book-alist is nil ; only because of a problem with a subsidiary book. So we don't want to print ; the scary "BUT NOTE" below in this case. t) (t (equal cdr-actual-entry pair))))) (cond (erp (msg "~|AND NOTE that file ~x0 does not currently ~ exist, so you will need to recertify ~x1 and the ~ books the depend on it (and, if you are using an ~ image created by save-exec, then consider ~ rebuilding that image)" (concatenate 'string familiar-name ".cert") familiar-name)) (cert-maybe-unchanged-p " so book recertification is probably required") (t (msg "~|AND NOTE that file ~x0 changed after ~x1 was ~ included, so you should probably undo back ~ through the command that included ~x1 (or, if ~ you are using an image created by save-exec, ~ consider rebuilding that image)" (concatenate 'string familiar-name ".cert") familiar-name)))) state))) (defun tilde-*-book-check-sums-phrase1 (reqd-alist actual-alist-cddrs state) ; The two alists are strip-cddrs of include-book-alists. Thus, each ; entry in each is of the form (familiar-name cert-annotations ; . ev-lst-chk-sum). For each entry in reqd-alist we either find an ; identical entry in actual-alist-cddrs or else we print a message. (cond ((null reqd-alist) (mv nil state)) (t (let* ((reqd-entry (cddr (car reqd-alist))) (full-book-name (car (car reqd-alist))) (familiar-name (car reqd-entry)) (actual-entry (assoc-equal familiar-name actual-alist-cddrs))) ; We know there is an entry for familiar-name because otherwise we would have ; caused an error. The question is only whether we found a cert file ; for it, etc. (cond ((equal reqd-entry actual-entry) (tilde-*-book-check-sums-phrase1 (cdr reqd-alist) actual-alist-cddrs state)) (t (mv-let (msgs state) (tilde-*-book-check-sums-phrase1 (cdr reqd-alist) actual-alist-cddrs state) (mv-let (phrase state) (tilde-@-cert-post-alist-phrase full-book-name familiar-name (cdr reqd-entry) (cdr actual-entry) state) (mv (cons (msg "-- its certificate requires the book \"~s0\" with ~ certificate annotations~| ~x1~|and check sum ~x2, but ~ we have included ~@3~@4" full-book-name (cadr reqd-entry) ;;; cert-annotations (cddr reqd-entry) ;;; ev-lst-chk-sum (cond ((null (cddr actual-entry)) (msg "an uncertified version of ~x0 with certificate ~ annotations~| ~x1," familiar-name (cadr actual-entry) ; cert-annotations )) (t (msg "a version of ~x0 with certificate ~ annotations~| ~x1~|and check sum ~x2," familiar-name (cadr actual-entry) ; cert-annotations (cddr actual-entry)))) phrase) msgs) state))))))))) (defun tilde-*-book-check-sums-phrase (reqd-alist actual-alist state) ; The two alists each contain pairs of the form (full-book-name user-book-name ; familiar-name cert-annotations . ev-lst-chk-sum). Reqd-alist shows what is ; required and actual-alist shows that is actual (presumably, present in the ; world's include-book-alist). We know reqd-alist ought to be a `include-book ; alist subset' of actual-alist but it is not. (mv-let (phrase1 state) (tilde-*-book-check-sums-phrase1 reqd-alist (strip-cddrs actual-alist) state) (mv (list "" "~%~@*" "~%~@*;~|" "~%~@*;~|" phrase1) state))) (defun get-cmds-from-portcullis1 (eval-hidden-defpkgs ch ctx state ans) ; Keep this in sync with equal-modulo-hidden-defpkgs, make-hidden-defpkg, and ; the #-acl2-loop-only and #+acl2-loop-only definitions of defpkg. ; Also keep this in sync with chk-raise-portcullis2. ; We read successive forms from ch, stopping when we get to ; :END-PORTCULLIS-CMDS and returning the list of forms read, which we ; accumulate onto ans as we go. Ans should be nil initially. (mv-let (eofp form state) (state-global-let* ((infixp nil)) (read-object ch state)) (cond (eofp (mv t nil state)) ((eq form :END-PORTCULLIS-CMDS) (value (reverse ans))) ((and eval-hidden-defpkgs (case-match form (('defpkg & & & & 't) t) (& nil))) (er-progn (trans-eval form ctx state ; Perhaps aok could be t, but we use nil just to be conservative. nil) (get-cmds-from-portcullis1 eval-hidden-defpkgs ch ctx state (cons form ans)))) (t (get-cmds-from-portcullis1 eval-hidden-defpkgs ch ctx state (cons form ans)))))) (defun hidden-defpkg-events-simple (kpa acc) ; Warning: Keep this in sync with hidden-depkg-events. (cond ((endp kpa) (reverse acc)) ((not (package-entry-hidden-p (car kpa))) (hidden-defpkg-events-simple (cdr kpa) acc)) (t (let* ((e (car kpa)) (n (package-entry-name e)) (imports (package-entry-imports e)) (event (package-entry-defpkg-event-form e)) (name (cadr event))) (hidden-defpkg-events-simple (cdr kpa) (cons `(defpkg ,name ,(assert$ event (assert$ (equal n name) (kwote imports)))) acc)))))) (defun get-cmds-from-portcullis (file1 file2 eval-hidden-defpkgs ch ctx state) ; In order to read the certificate's portcullis for a book that has been ; included only locally in the construction of the current world, we may need ; to evaluate the hidden packages (see the Essay on Hidden Packages) ; created by that book. We obtain the necessary defpkg events by calling ; hidden-defpkg-events-simple below. ; See the comment about "eval hidden defpkg events" in chk-raise-portcullis. (revert-world-on-error (let* ((wrld (w state)) (events (hidden-defpkg-events-simple (global-val 'known-package-alist wrld) nil))) (er-progn (if events (state-global-let* ((inhibit-output-lst (remove1-eq 'error *valid-output-names*))) (trans-eval (cons 'er-progn events) ctx state t)) (value nil)) (mv-let (erp val state) (get-cmds-from-portcullis1 eval-hidden-defpkgs ch ctx state nil) (cond (erp (ill-formed-certificate-er ctx 'get-cmds-from-portcullis file1 file2)) (t (pprogn (if events ; optimization (set-w! wrld state) state) (value val))))))))) (defun convert-book-name-to-port-name (x) ; X is assumed to satisfy chk-book-name. We generate the corresponding ; .port file name. See the related function, convert-book-name-to-cert-name. (coerce (append (reverse (cddddr (reverse (coerce x 'list)))) '(#\p #\o #\r #\t)) 'string)) (defun chk-raise-portcullis2 (file1 file2 ch port-file-p ctx state ans) ; Keep this in sync with get-cmds-from-portcullis1. ; We read successive forms from ch and trans-eval them. We stop when we get to ; end of file or, in the common case that port-file-p is false, ; :END-PORTCULLIS-CMDS. We may cause an error. It is assumed that each form ; evaluated is a DEFPKG or an event form and is responsible for installing its ; world in state. This assumption is checked by chk-acceptable-certify-book, ; before a .cert file or .port file is written. (The user might violate this ; convention by manually editing a .port file, but .port files are only used ; when including uncertified books, where all bets are off anyhow.) We return ; the list of forms read, which we accumulate onto ans as we go. Ans should be ; nil initially. (mv-let (eofp form state) (state-global-let* ((infixp nil)) (read-object ch state)) (cond (eofp (cond (port-file-p (value (reverse ans))) (t (ill-formed-certificate-er ctx 'chk-raise-portcullis2{port} file1 file2)))) ((and (eq form :END-PORTCULLIS-CMDS) (not port-file-p)) (value (reverse ans))) (t (mv-let (error-flg trans-ans state) (trans-eval form (msg (if port-file-p "the .port file for ~x0" "the portcullis for ~x0") file1) state t) ; If error-flg is nil, trans-ans is of the form ; ((nil nil state) . (erp' val' replaced-state)) ; because form is a DEFPKG or event form. (let ((erp-prime (car (cdr trans-ans)))) (cond ((or error-flg erp-prime) ;erp' (pprogn (cond (port-file-p (warning$ ctx "Portcullis" "The error reported above was caused while ~ trying to execute commands from file ~x0 ~ while including uncertified book ~x1. In ~ particular, we were trying to execute ~x2 ~ when the error occurred. Because of this ~ error, we cannot complete the include-book ~ operation for the above book, in the current ~ world. You can perhaps eliminate this error ~ by removing file ~x0." (convert-book-name-to-port-name file1) file1 form)) (t (warning$ ctx "Portcullis" "The error reported above was caused while ~ trying to raise the portcullis for the book ~ ~x0. In particular, we were trying to ~ execute ~x1 when the error occurred. ~ Because we cannot raise the portcullis, we ~ cannot include this book in this world. ~ There are two standard responses to this ~ situation. Either change the current ~ logical world so that this error does not ~ occur, e.g., redefine one of your functions, ~ or recertify the book in a different ~ environment." file1 form))) (mv t nil state))) (t (chk-raise-portcullis2 file1 file2 ch port-file-p ctx state (cons form ans)))))))))) (defun chk-raise-portcullis1 (file1 file2 ch port-file-p ctx state) ; After resetting the acl2-defaults-table, we read and eval each of the forms ; in ch until we get to :END-PORTCULLIS-CMDS. However, we temporarily skip ; proofs (in an error protected way). We return the list of command forms in ; the portcullis. (state-global-let* ((ld-skip-proofsp 'include-book) (skip-proofs-by-system t) (in-local-flg ; As we start processing events on behalf of including a book, we are no longer ; in the lexical scope of LOCAL for purposes of disallowing setting of the ; acl2-defaults-table. (and (f-get-global 'in-local-flg state) 'local-include-book))) (er-progn (maybe-install-acl2-defaults-table ; The point here is to re-create the environment in which the book to be ; included was originally certified. If we do not install the original ; acl2-defaults-table then we can, for example, certify a book defining (foo ; x) = (car x), then in a new session include this book after ; (set-verify-guards-eagerness 2), and then get a hard error with (foo 3). *initial-acl2-defaults-table* state) (chk-raise-portcullis2 file1 file2 ch port-file-p ctx state nil)))) (defun mark-local-included-books (post-alist1 post-alist2) ; See make-certificate-file for an explanation of this function. Roughly ; speaking, we copy post-alist1 (which is the include-book-alist after the ; events in the main book were successfully proved) and every time we find a ; non-local book in it that is not in post-alist2 (which is the ; include-book-alist after the main book was included by certify-book's second ; pass), we mark that element LOCAL. We know that post-alist2 is a subset of ; post-alist1. Thus, if we then throw out all the elements marked LOCAL we get ; post-alist2. ; One might ask why we mark post-alist1 this way rather than just put ; post-alist2 into the certificate object in the first place. One reason ; is to allow a hand inspection of the certificate to see exactly what ; versions of the local subbooks participated in the certification. But a more ; critical reason is to note the use of skip-proofs in locally included ; subbooks; see the Essay on Skip-proofs. ; Recall that each element of an include-book-alist is (full-book-name ; user-book-name familiar-name cert-annotations . ev-lst-chk-sum). We ; only look at the full-book-name components below. (cond ((null post-alist1) nil) ((eq (caar post-alist1) 'local) (cons (car post-alist1) (mark-local-included-books (cdr post-alist1) post-alist2))) ((assoc-equal (caar post-alist1) post-alist2) (cons (car post-alist1) (mark-local-included-books (cdr post-alist1) post-alist2))) (t (cons (list 'local (car post-alist1)) (mark-local-included-books (cdr post-alist1) post-alist2))))) (defun unmark-and-delete-local-included-books (post-alist3) ; See make-certificate-file for an explanation of this function. Roughly ; speaking, this function undoes what mark-local-included-books does. If ; post-alist3 is the result of marking post-alist1 and post-alist2, then this ; function produces post-alist2 from post-alist3. Given our use of it, it ; produces the include-book-alist you should have after any successful ; inclusion of the main book. (cond ((null post-alist3) nil) ((eq (caar post-alist3) 'LOCAL) (unmark-and-delete-local-included-books (cdr post-alist3))) (t (cons (car post-alist3) (unmark-and-delete-local-included-books (cdr post-alist3)))))) (defun decimal-string-to-number (s bound expo) ; Returns 10^expo times the integer represented by the digits of string s from ; 0 up through bound-1 (most significant digit at position 0), but returns a ; hard error if any of those "digits" are not digits. (declare (xargs :guard (and (stringp s) (natp expo) (<= bound (length s))))) (cond ((zp bound) 0) (t (let* ((pos (1- bound)) (ch (char s pos))) (cond ((member ch '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) (let ((digit (case ch (#\0 0) (#\1 1) (#\2 2) (#\3 3) (#\4 4) (#\5 5) (#\6 6) (#\7 7) (#\8 8) (otherwise 9)))) (+ (* (expt 10 expo) digit) (decimal-string-to-number s pos (1+ expo))))) (t (er hard 'decimal-string-to-number "Found non-decimal digit in position ~x0 of string ~ \"~s1\"." pos s))))))) (defun parse-version (version) ; Version is an ACL2 version string, as in state global 'acl2-version. We ; return (mv major minor incrl rest), where either major is nil, indicating an ; ill-formed version; or else major, minor, and incrl are natural numbers ; indicating the major, minor, and incrl version, and rest is the part of the ; string starting with #\(, if any. For example, ; (parse-version "ACL2 Version 2.10") is (mv 2 10 0 "") and ; (parse-version "ACL2 Version 2.10.1(r)") is (mv 2 10 1 "(r)"). (let* ((root "ACL2 Version") (pos0 (if (and (stringp version) (<= 13 (length version)) (equal (subseq version 0 12) root) (or (eql (char version 12) #\Space) (eql (char version 12) #\_))) 13 nil)) (pos-lparen (position #\( version)) (end0 (or pos-lparen (length version))) (rest (subseq version end0 (length version))) (from-pos0 (and pos0 (subseq version pos0 end0))) (pos1-from-pos0 (and pos0 (position #\. from-pos0))) (pos1 (and pos1-from-pos0 (+ pos0 pos1-from-pos0))) (major (and pos1 (decimal-string-to-number (subseq version pos0 pos1) (- pos1 pos0) 0))) (from-pos1 (and pos1 (subseq version (1+ pos1) end0))) (pos2-from-pos1 (and pos1 (position #\. from-pos1))) (pos2 (if pos2-from-pos1 (+ (1+ pos1) pos2-from-pos1) (and pos1 end0))) (minor (and pos2 (decimal-string-to-number (subseq version (1+ pos1) pos2) (1- (- pos2 pos1)) 0))) (incrl (if (and pos2 (< pos2 end0)) (decimal-string-to-number (subseq version (1+ pos2) end0) (1- (- end0 pos2)) 0) 0))) (mv major minor incrl rest))) #-acl2-loop-only (defun-one-output latest-release-note-string () (mv-let (major minor incrl rest) (parse-version (f-get-global 'acl2-version *the-live-state*)) (declare (ignore rest)) (if (zerop incrl) (format nil "note-~s-~s" major minor) (format nil "note-~s-~s-~s" major minor incrl)))) (defun earlier-acl2-versionp (version1 version2) ; This function ignores the part of each version string after the first ; parenthesis (if any). While it is no longer used in the sources (as of May ; 1, 2010), it is used in community book books/hacking/ and is a handy utility, ; so we leave it here. (mv-let (major1 minor1 incrl1 rest1) (parse-version version1) (declare (ignore rest1)) (mv-let (major2 minor2 incrl2 rest2) (parse-version version2) (declare (ignore rest2)) (cond ((or (null major1) (null major2)) (er hard 'earlier-acl2-versionp "We are surprised to find an ACL2 version string, ~x0, that ~ cannot be parsed." (if (null major1) version1 version2))) (t (or (< major1 major2) (and (int= major1 major2) (assert$ (and (natp minor1) (natp minor2)) (or (< minor1 minor2) (and (int= minor1 minor2) (< incrl1 incrl2))))))))))) (defun acl2-version-r-p (version) (let ((p (position #\( version))) (and p (< (+ p 2) (length version)) (equal (subseq version p (+ p 3)) "(r)")))) (defun ttag-alistp (x) ; We don't check that pathnames are absolute, but that isn't important here. (cond ((atom x) (null x)) (t (and (consp (car x)) (symbolp (caar x)) (true-listp (cdar x)) (string-listp (remove1 nil (cdar x))) (ttag-alistp (cdr x)))))) (defun cert-annotationsp (x) (case-match x (((':SKIPPED-PROOFSP . sp) (':AXIOMSP . ap) . ttags-singleton) (and (member-eq sp '(t nil ?)) (member-eq ap '(t nil ?)) (or (null ttags-singleton) (case-match ttags-singleton (((':TTAGS . ttags)) (ttag-alistp ttags)) (& nil))))) (& nil))) (defun include-book-alist-entryp (entry) (and (consp entry) (stringp (car entry)) ;;; full-book-name (consp (cdr entry)) (stringp (cadr entry)) ;;; user-book-name (consp (cddr entry)) (stringp (caddr entry)) ;;; familiar-name (consp (cdddr entry)) (cert-annotationsp (cadddr entry)) ;;; cert-annotations (or (integerp (cddddr entry)) ;;; ev-lst-chk-sum (eq (cddddr entry) nil)))) (defun include-book-alistp1 (x local-markers-allowedp) (cond ((atom x) (equal x nil)) ((and local-markers-allowedp (consp (car x)) (eq (car (car x)) 'local) (consp (cdr (car x))) (equal (cddr (car x)) nil)) (and (include-book-alist-entryp (cadr (car x))) (include-book-alistp1 (cdr x) local-markers-allowedp))) (t (and (include-book-alist-entryp (car x)) (include-book-alistp1 (cdr x) local-markers-allowedp))))) (defun include-book-alistp (x local-markers-allowedp) ; We check whether x is a legal include-book-alist in the given version. If ; local-markers-allowedp we consider entries of the form (LOCAL e) to be legal ; if e is legal; otherwise, LOCAL is given no special meaning. (We expect to ; give this special treatment for post-alists; see the comments in ; make-certificate-file.) (include-book-alistp1 x local-markers-allowedp)) (defrec cert-obj ; The :pcert-info field is used for provisional certification. Its value is ; either an expansion-alist that has not had locals elided (as per elide-locals ; and related functions), or one of tokens :proved or :unproved. Note that an ; expansion-alist, even a nil value, implicitly indicates that proofs have been ; skipped when producing the corresponding certificate file (a .pcert0 file); ; the explicit value :unproved is stored when constructing a cert-obj from a ; .pcert1 file. ((cmds . pre-alist) post-alist expansion-alist . pcert-info) t) (defun check-sum-cert-obj (cmds pre-alist post-alist expansion-alist) ; The inputs are potential fields of a cert-obj record. We deliberately omit ; the :pcert-info field of a cert-obj from the checksum: we don't want the ; checksum changing from the .pcert0 file to the .pcert1 file, and anyhow, its ; only function is to assist in proofs for the Convert procedure of provisional ; certification. (check-sum-obj (cons (cons cmds pre-alist) (cons post-alist expansion-alist)))) (defun chk-raise-portcullis (file1 file2 ch light-chkp caller ctx state suspect-book-action-alist evalp) ; File1 is a book and file2 is its certificate file. The version string ; recorded with the file is version. Ch is an open object input channel to the ; certificate. We have already read past the initial (in-package "ACL2"), ; acl2-version and the :BEGIN-PORTCULLIS-CMDS in ch. We now read successive ; commands and, if evalp is true, evaluate them in state. Ld-skip-proofsp is ; 'include-book for this operation because these commands have all been ; successfully carried out in a boot strap world. If this doesn't cause an ; error, then we read the optional :expansion-alist, the pre- and post- check ; sum alists, and the final check sum. If these objects are (except the ; optional :expansion-alist) not present or are of the wrong type, or there is ; additional text in the file, or the final check sum is inaccurate, we cause ; an error. ; Light-chkp is t when we are content to avoid rigorous checks on the ; certificate, say because we are simply interested in some information that ; need not be fully trusted. ; Unless we are told to ignore the pre-alist, we check that it is a subset of ; the current include-book-alist. Failure of this check may lead either to an ; error or to the assumption that the book is uncertified, according to the ; suspect-book-action-alist. If we don't cause an error we return either the ; certificate object, which is a cert-obj record, or else we return nil, ; indicating that the book is presumed uncertified. (with-reckless-readtable ; We may use with-reckless-readtable above because the files we are reading ; were written out automatically, not by users. (er-let* ((portcullis-cmds (if evalp (chk-raise-portcullis1 file1 file2 ch nil ctx state) (get-cmds-from-portcullis file1 file2 ; When we are raising the portcullis on behalf of the Convert procedure of ; provisional certification, we may need to eval hidden defpkg events from the ; portcullis. Each such eval is logically a no-op (other than restricting ; potential logical extensions made later with defpkg), but it permits reading ; the rest of the certificate file. See the comment in chk-bad-lisp-object for ; an example from Sol Swords showing how this can be necessary. (eq caller 'convert-pcert) ch ctx state)))) (state-global-let* ((infixp nil)) (mv-let (eofp pre-alist state) (read-object ch state) (er-let* ((expansion-alist (cond (eofp (ill-formed-certificate-er ctx 'chk-raise-portcullis{expansion-alist-1} file1 file2)) ((eq pre-alist :expansion-alist) (mv-let (eofp expansion-alist state) (read-object ch state) (cond (eofp (ill-formed-certificate-er ctx 'chk-raise-portcullis{expansion-alist-2} file1 file2)) (t (value expansion-alist))))) (t (value nil)))) (pre-alist (cond ((eq pre-alist :expansion-alist) (mv-let (eofp pre-alist state) (read-object ch state) (cond (eofp (ill-formed-certificate-er ctx 'chk-raise-portcullis{expansion-alist-3} file1 file2)) (t (value pre-alist))))) (t (value pre-alist)))) (pre-alist (cond ((include-book-alistp pre-alist nil) (value pre-alist)) (t (ill-formed-certificate-er ctx 'chk-raise-portcullis{pre-alist} file1 file2 pre-alist))))) (let ((actual-alist (global-val 'include-book-alist (w state)))) (mv-let (eofp post-alist3 state) (read-object ch state) (er-let* ((post-alist3 (cond ((include-book-alistp post-alist3 t) (value post-alist3)) (t (ill-formed-certificate-er ctx 'chk-raise-portcullis{post-alist-1} file1 file2 post-alist3))))) (cond (eofp (ill-formed-certificate-er ctx 'chk-raise-portcullis{post-alist-2} file1 file2)) (t (mv-let (eofp chk-sum1 state) (read-object ch state) (cond (eofp (ill-formed-certificate-er ctx 'chk-raise-portcullis{chk-sum-1} file1 file2)) ((not (integerp chk-sum1)) (ill-formed-certificate-er ctx 'chk-raise-portcullis{chk-sum-2} file1 file2 chk-sum1)) (t (mv-let (eofp temp state) (read-object ch state) (cond ((not (or eofp (eq temp :pcert-info))) (ill-formed-certificate-er ctx 'chk-raise-portcullis{pcert-info-1} file1 file2 temp)) (t (er-let* ((pcert-info (cond ((or eofp (not (eq caller 'convert-pcert))) (value nil)) (t (mv-let (eofp1 temp1 state) (read-object ch state) (cond (eofp1 (ill-formed-certificate-er ctx 'chk-raise-portcullis{pcert-info-2} file1 file2)) (t (value temp1)))))))) (let ((chk-sum2 (and (not light-chkp) ; optimization (check-sum-cert-obj portcullis-cmds ; :cmds pre-alist ; :pre-alist post-alist3 ; :post-alist expansion-alist ; :expansion-alist )))) (cond ((and (not light-chkp) (or (not (integerp chk-sum2)) (not (int= chk-sum1 chk-sum2)))) (ill-formed-certificate-er ctx 'chk-raise-portcullis{pcert-info-3} file1 file2 (list :chk-sum1 chk-sum1 :chk-sum2 chk-sum2 ; Developer debug: ; :portcullis-cmds portcullis-cmds ; :pre-alist pre-alist ; :post-alist3 post-alist3 ; :expansion-alist expansion-alist ))) ((and (not light-chkp) (not (include-book-alist-subsetp pre-alist actual-alist))) ; Note: Sometimes I have wondered how the expression above deals with ; LOCAL entries in the alists in question, because ; include-book-alist-subsetp does not handle them. The answer is: ; there are no LOCAL entries in a pre-alist because we prohibit local ; events in the portcullis commands. ; Our next step is to call include-book-er, but we break up that computation so ; that we avoid needless computation (potentially reading certificate files) if ; no action is to be taken. (let ((warning-summary (include-book-er-warning-summary :uncertified-okp suspect-book-action-alist state))) (cond ((and (equal warning-summary "Uncertified") (warning-disabled-p "Uncertified")) (value nil)) (t (mv-let (msgs state) (tilde-*-book-check-sums-phrase pre-alist actual-alist state) (include-book-er1 file1 file2 (cons "After evaluating the ~ portcullis commands for ~ the book ~x0:~|~*3." (list (cons #\3 msgs))) warning-summary ctx state)))))) (t (value (make cert-obj :cmds portcullis-cmds :pre-alist pre-alist :post-alist post-alist3 :expansion-alist expansion-alist :pcert-info pcert-info))))))))))))))))))))))) (defun chk-certificate-file1 (file1 file2 ch light-chkp caller ctx state suspect-book-action-alist evalp) ; File1 is a book name and file2 is its associated certificate file name. Ch ; is a channel to file2. We assume we have read the initial (in-package ; "ACL2") and temporarily slipped into that package. Our caller will restore ; it. We now read the rest of file2 and either open the portcullis (skipping ; evaluation if evalp is nil) and return a cert-obj record or nil if we are ; assuming the book, or we cause an error. ; The code below is tedious and we here document it. The first thing we look ; for is the ACL2 Version number printed immediately after the in-package. ; This function is made more complicated by four facts. We do not know for ; sure that the certificate file is well-formed in any version. Also, we do ; not know whether include-book-er causes an error or just prints a warning ; (because that is determined by suspect-book-action-alist and the values of ; the state globals defaxioms-okp-cert and skip-proofs-okp-cert). Suppose we ; read a purported version string, val, that does not match the current ; acl2-version. Then we cause an include-book-er which may or may not signal ; an error. If it does not then we are to assume the uncertified book so we ; must proceed with the certificate check as though the version were ok. ; Basically this means we want to call chk-raise-portcullis, but we must first ; make sure we've read to the beginning of the portcullis. If val looks like ; an ACL2 Version string, then this file is probably a well-formed Version 1.9+ ; file and we must read the :BEGIN-PORTCULLIS-CMDS before proceeding. ; Otherwise, this isn't well-formed and we cause an error. (mv-let (eofp version state) (state-global-let* ((infixp nil)) (read-object ch state)) (cond (eofp (ill-formed-certificate-er ctx 'chk-certificate-file1{empty} file1 file2)) (t (let* ((version-okp (equal version (f-get-global 'acl2-version state)))) (cond (version-okp (mv-let (eofp key state) (state-global-let* ((infixp nil)) (read-object ch state)) (cond (eofp (ill-formed-certificate-er ctx 'chk-certificate-file1{begin-portcullis-cmds-1} file1 file2)) ((not (eq key :begin-portcullis-cmds)) (ill-formed-certificate-er ctx 'chk-certificate-file1{begin-portcullis-cmds-2} file1 file2 key)) (t (chk-raise-portcullis file1 file2 ch light-chkp caller ctx state suspect-book-action-alist evalp))))) ((not (equal (acl2-version-r-p (f-get-global 'acl2-version state)) (acl2-version-r-p version))) (er soft ctx "We do not permit ACL2 books to be processed by ACL2(r) or vice ~ versa. The book ~x0 was last certified with ~s1 but this is ~ ~s2." file1 version (f-get-global 'acl2-version state))) (t (mv-let (erp val state) (include-book-er file1 file2 (cons "~x0 was apparently certified with ~sa. The inclusion of ~ this book in the current ACL2 may render this ACL2 ~ session unsound! We recommend you recertify the book ~ with the current version, ~sb. See :DOC version. No ~ compiled file will be loaded with this book." (list (cons #\a version) (cons #\b (f-get-global 'acl2-version state)))) :uncertified-okp suspect-book-action-alist ctx state) ; Because the book was certified under a different version of ACL2, we ; consider it uncertified and, at best, return nil rather than a ; certificate object below. Of course, we might yet cause an error. (cond (erp (mv erp val state)) ((and (stringp version) (<= 13 (length version)) (equal (subseq version 0 13) "ACL2 Version ")) (mv-let (eofp key state) (state-global-let* ((infixp nil)) (read-object ch state)) (cond (eofp (ill-formed-certificate-er ctx 'chk-certificate-file1{begin-portcullis-cmds-3} file1 file2)) ((not (eq key :begin-portcullis-cmds)) (ill-formed-certificate-er ctx 'chk-certificate-file1{begin-portcullis-cmds-4} file1 file2 key)) (t (er-progn (chk-raise-portcullis file1 file2 ch light-chkp caller ctx state suspect-book-action-alist t) (value nil)))))) (t (ill-formed-certificate-er ctx 'chk-certificate-file1{acl2-version} file1 file2 version))))))))))) (defun certificate-file (full-book-name state) (mv-let (ch cert-name pcert-op state) (certificate-file-and-input-channel full-book-name nil state) (declare (ignore pcert-op)) (pprogn (cond (ch (close-input-channel ch state)) (t state)) (mv (and ch cert-name) state)))) (defun chk-certificate-file (file1 dir caller ctx state suspect-book-action-alist evalp) ; File1 is a full book name. We see whether there is a certificate on file for ; it. If so, and we can get past the portcullis (evaluating it if evalp is ; true), we return the certificate object, a cert-obj record, or nil if we ; presume the book is uncertified. ; Dir is either nil or the directory of file1. ; This function may actually execute some events or even some DEFPKGs as part ; of the raising of the portcullis in the case that evalp is true. Depending ; on the caller, we do not enforce the requirement that the books included by ; the portcullis commands have the specified check sums, and (for efficiency) ; we do not check the check-sum of the certificate object represented in the ; certificate file. This feature is used when we use this function to recover ; from an old certificate the portcullis commands to recertify the file. ; We make the convention that if a file has no certificate or has an invalid ; certificate, we will either assume it anyway or cause an error depending on ; suspect-book-action-alist. In the case that we pronouce this book ; uncertified, we return nil. (let ((dir (or dir (directory-of-absolute-pathname file1)))) (mv-let (ch file2 pcert-op state) (certificate-file-and-input-channel file1 (if (eq caller 'convert-pcert) :create-pcert nil) state) (cond ((null ch) (include-book-er file1 file2 "There is no certificate on file for ~x0." :uncertified-okp suspect-book-action-alist ctx state)) (t (er-let* ((pkg (state-global-let* ((infixp nil)) (chk-in-package ch file2 nil ctx state)))) (cond ((not (equal pkg "ACL2")) (ill-formed-certificate-er ctx 'chk-certificate-file{pkg} file1 file2 pkg)) (t (state-global-let* ((current-package "ACL2") (connected-book-directory dir set-cbd-state)) (mv-let (error-flg val state) (chk-certificate-file1 file1 file2 ch (case caller ; light-chkp (convert-pcert nil) (certify-book t) ; k=t (include-book nil) (puff t) (otherwise (er hard ctx "Implementation error in ~ chk-certificate-file: ~ Unexpected case!"))) caller ctx state suspect-book-action-alist evalp) (let ((val (cond ((and val pcert-op (not (access cert-obj val :pcert-info))) ; We don't print a :pcert-info field to the .pcert1 file, because it will ; ultimately be moved to a .cert file. (We could live with such fields in ; .cert files, but we are happy to avoid dealing with them.) We also don't ; bother printing a :pcert-info field to a .pcert0 file when its value is nil ; (perhaps an arbitrary decision). We now deal with the above observations. (change cert-obj val :pcert-info (if (eq pcert-op :create-pcert) :unproved (assert$ (eq pcert-op :convert-pcert) :proved)))) (t val)))) (pprogn (close-input-channel ch state) (mv error-flg val state))))))))))))) ; All of the above is used during an include-book to verify that a ; certificate is well-formed and to raise the portcullis of the book. ; It happens that the code is also used by certify-book to recover the ; portcullis of a book from an old certificate. We now continue with ; certify-book's checking, which next moves on to the question of ; whether the environment in which certify-book was called is actually ; suitable for a certification. (defun equal-modulo-hidden-defpkgs (cmds1 cmds2) ; Keep this in sync with get-cmds-from-portcullis1, make-hidden-defpkg, and the ; #-acl2-loop-only and #+acl2-loop-only definitions of defpkg. ; Test equality of cmds1 and cmds2, except that cmds2 may have hidden defpkg ; events missing from cmds1. (cond ((endp cmds2) (endp cmds1)) ((and cmds1 (equal (car cmds1) (car cmds2))) (equal-modulo-hidden-defpkgs (cdr cmds1) (cdr cmds2))) (t (let ((cmd (car cmds2))) (case-match cmd (('defpkg & & & & 't) ; keep in sync with make-hidden-defpkg (equal-modulo-hidden-defpkgs cmds1 (cdr cmds2))) (& nil)))))) (defun cert-obj-for-convert (full-book-name dir pre-alist fixed-cmds suspect-book-action-alist ctx state) ; Here we check that the pre-alists and portcullis commands correspond, as ; explained in the error messages below. See also certify-book-finish-convert ; and certify-book-fn, respectively, for analogous checks on the post-alists ; and expansion-alists. (er-let* ((cert-obj (chk-certificate-file full-book-name dir 'convert-pcert ctx state suspect-book-action-alist nil))) (cond ((not (equal-modulo-hidden-defpkgs fixed-cmds (access cert-obj cert-obj :cmds))) (er soft ctx "The Convert procedure of provisional certification requires ~ that the current ACL2 world at the start of that procedure ~ agrees with the current ACL2 world present at the start of ~ the Pcertify procedure. However, these worlds appear to ~ differ! To see the current commands, use :pbt! 1. To see ~ the portcullis commands from the .pcert0 file, evaluate the ~ following form:~|~Y01~|Now compare the result of that ~ evaluation, ignoring DEFPKG events whose fifth argument (of ~ five) is T, with (``fixed'') portcullis commands of the ~ current ACL2 world:~|~y2" `(er-let* ((cert-obj (chk-certificate-file ,full-book-name ,dir 'convert-pcert ',ctx state ',suspect-book-action-alist nil))) (value (access cert-obj cert-obj :cmds))) nil fixed-cmds)) ((not (equal pre-alist (access cert-obj cert-obj :pre-alist))) (er soft ctx "The Convert procedure of provisional certification requires ~ that the include-book-alist at the start of that procedure ~ (the ``pre-alist'') agrees with the one present at the start ~ of the Pcertify procedure. However, these appear to differ! ~ The current world's pre-alist is:~|~% ~y0~|~%The pre-alist ~ from the Pcertify procedure (from the .pcert0 file) is:~|~% ~ ~y1~|~%" pre-alist (access cert-obj cert-obj :pre-alist))) (t (value cert-obj))))) (defun symbol-name-equal (x str) (declare (xargs :guard (stringp str))) (and (symbolp x) (equal (symbol-name x) str))) (defun chk-acceptable-certify-book1 (file dir k cmds cert-obj cbds names cert-op suspect-book-action-alist wrld ctx state) ; This function is checking the appropriateness of the environment in which ; certify-book is called. ; File is a full-book-name. If certify-book is called with k=t, then here k is ; '?, cert-obj is a cert-obj constructed from an existing certificate, and cmds ; is nil. Otherwise (in the more usual case), this subroutine is called after ; we have the k proposed portcullis commands and wrld; cmds and cbds are ; returned by (get-portcullis-cmds wrld nil nil names ctx state); and cert-obj ; is nil. ; Unless we cause an error, we return a cert-obj constructed from the ; certificate file for the given book, file. ; Note that for the Convert procedure of provisional certification, we keep the ; expansion-alist (and pcert-info) from the existing .pcert0 file. But in all ; other cases, we do not keep an existing expansion-alist, even if the original ; argument k for certify-book is t (or any symbol with name "T"). (let ((pre-alist (global-val 'include-book-alist wrld)) (cmds (or cmds (and cert-obj (access cert-obj cert-obj :cmds)))) (uncert-books (and (not (eq cert-op :write-acl2xu)) ; else uncertified books are OK (collect-uncertified-books ; During the Pcertify and Convert procedures of provisional certification, the ; value of 'include-book-alist-all can be based on the inclusion of books that ; have a certificate file with suffix .pcert0 or .pcert1. This is OK because ; for purposes of those procedures, we really do consider such books to be ; certified. (global-val 'include-book-alist-all wrld))))) (cond ((not (eq (default-defun-mode wrld) :logic)) (er soft ctx "Books must be certified in :LOGIC mode. The current mode is ~x0." (default-defun-mode wrld))) ((and (not (integerp k)) (not (symbol-name-equal k "?"))) (er soft ctx "The second argument to certify-book must be one of the symbols T ~ or ? (in any package), or an integer. You supplied ~x0. See :DOC ~ certify-book." k)) ((and (not (symbol-name-equal k "?")) (not (eql k (length cmds)))) (er soft ctx "You indicated that the portcullis for ~x0 would be of length ~x1 ~ but it is actually of length ~x2. Perhaps you had better inspect ~ the world and call certify-book again." file k (length cmds))) ((assoc-equal file pre-alist) ; Why do we do this? By insuring that file is not in the include-book-alist ; initially, we ensure that it gets into the alist only at the end when we ; include-book the book. This lets us cdr it off. If it happened to be the ; alist initially, then the include-book would not add it and the cdr wouldn't ; remove it. See the end of the code for certify-book. (er soft ctx "We cannot certify ~x0 in a world in which it has already been ~ included." file)) (uncert-books (er soft ctx "It is impossible to certify any book in the current world because ~ it is built upon ~*0 which ~#1~[is~/are~] uncertified." (tilde-*-&v-strings '& uncert-books #\,) uncert-books)) (cert-obj (value cert-obj)) (t (er-let* ((fixed-cmds (cond ((and (eq cert-op :convert-pcert) cert-obj) ; This case comes from handling the case of argument k = t from certify-book. ; We do not use fixed-cmds below in this case, so we avoid the expense of ; computing it here. (value 'irrelevant)) (t ; Now that we know we have a list of embedded event forms, we are ready to ; replace relative pathnames by absolute pathnames. See fix-portcullis-cmds. ; At one time we considered not fixing the portcullis commands when the cert-op ; is :write-acl2x or :write-acl2xu. But we keep it simple here and fix ; unconditionally. (fix-portcullis-cmds dir cmds cbds names (os wrld) wrld ctx state))))) (cond ((eq cert-op :convert-pcert) (cert-obj-for-convert file dir pre-alist fixed-cmds suspect-book-action-alist ctx state)) (t (value (make cert-obj :cmds fixed-cmds :pre-alist (cond (cert-obj (access cert-obj cert-obj :pre-alist)) (t pre-alist)) :post-alist nil ; to be filled in later :expansion-alist nil ; explained above ))))))))) (defun translate-book-names (filenames cbd state acc) (declare (xargs :guard (true-listp filenames))) ; one member can be nil (cond ((endp filenames) (value (reverse acc))) ((null (car filenames)) (translate-book-names (cdr filenames) cbd state (cons nil acc))) (t (translate-book-names (cdr filenames) cbd state (cons (extend-pathname cbd (possibly-add-lisp-extension (car filenames)) state) acc))))) (defun fix-ttags (ttags ctx cbd state seen acc) ; Seen is a list of symbols, nil at the top level. We use this argument to ; enforce the lack of duplicate ttags. Acc is the accumulated list of ttags to ; return, which may include symbols and lists (sym file1 ... filek). (declare (xargs :guard (true-listp ttags))) (cond ((endp ttags) (value (reverse acc))) (t (let* ((ttag (car ttags)) (sym (if (consp ttag) (car ttag) ttag))) (cond ((not (and (symbolp sym) sym (or (atom ttag) (string-listp (remove1-eq nil (cdr ttag)))))) (er soft ctx "A :ttags value for certify-book or include-book must ~ either be the keyword :ALL or else a list, each of whose ~ members is one of the following: a non-nil symbol, or the ~ CONS of a non-nil symbol onto a true list consisting of ~ strings and at most one nil. The value ~x0 is thus an ~ illegal member of such a list." ttag)) ((member-eq sym seen) (er soft ctx "A :ttags list may not mention the same ttag symbol more ~ than once, but the proposed list mentions ~x0 more than ~ once." sym)) ((symbolp ttag) (fix-ttags (cdr ttags) ctx cbd state (cons sym seen) (cons sym acc))) (t (er-let* ((full-book-names (translate-book-names (cdr ttag) cbd state nil))) (fix-ttags (cdr ttags) ctx cbd state (cons sym seen) (cons (cons sym full-book-names) acc))))))))) (defun chk-well-formed-ttags (ttags cbd ctx state) (cond ((or (null ttags) ; optimization (eq ttags :all)) (value ttags)) ((not (true-listp ttags)) (er soft ctx "A valid :ttags value must either be :all or a true list, The ~ following value is thus illegal: ~x0." ttags)) (t (fix-ttags ttags ctx cbd state nil nil)))) (defun check-certificate-file-exists (full-book-name cert-op ctx state) ; A certificate file is required: either the .pcert0 file, in case cert-op ; specifies the Convert procedure of provisional certification, or else because ; a certify-book command has specified recovery of the certification world from ; an existing certificate (argument k = t). We cause an error when the ; certificate file is missing. (mv-let (ch cert-name state) (certificate-file-and-input-channel1 full-book-name (cond ((eq cert-op :convert-pcert) :create-pcert) (t t)) state) (cond (ch (pprogn (close-input-channel ch state) (value nil))) ((eq cert-op :convert-pcert) (er soft ctx "The file ~x0 cannot be opened for input; perhaps it is ~ missing. But that file is required for the Convert ~ procedure of provisional certification of the book ~x1." cert-name full-book-name)) (t ; argument k is t for certify-book (er soft ctx "There is no certificate (.cert) file for ~x0. But you told ~ certify-book to recover the certi~-fication world from the ~ old certificate. You will have to construct the ~ certi~-fication world by hand (by executing the desired ~ commands in the current logical world) and then call ~ certify-book again." full-book-name))))) (defun chk-acceptable-certify-book (book-name full-book-name dir suspect-book-action-alist cert-op k ctx state) ; This function determines that it is ok to run certify-book on full-book-name, ; cert-op, and k. Unless an error is caused we return a cert-obj that ; contains, at least, the two parts of the portcullis, where the commands are ; adjusted to include make-event expansions of commands in the certification ; world). If cert-op is :convert-pcert then we check that the portcullis ; commands from the certification world agree with those in the .pcert0 file, ; and we fill in fields of the cert-obj based on the contents of the .pcert0 ; file. Otherwise, if k is t it means that the existing certificate file ; specifies the intended portcullis. It also means that there must be such a ; file and that we are in the ground zero state. If all those things check ; out, we will actually carry out the portcullis (extending the world with it) ; to get into the right state by the time we return. ; Dir is either nil or the directory of full-book-name. (let ((names (cons 'defpkg (primitive-event-macros))) (wrld (w state)) (dir (or dir (directory-of-absolute-pathname full-book-name)))) (er-progn (cond ((and (ld-skip-proofsp state) (not (eq cert-op ':write-acl2xu))) (er soft ctx "Certify-book must be called with ld-skip-proofsp set to nil ~ (except when writing .acl2x files in the case that ~ set-write-acl2x has specified skipping proofs).")) ((f-get-global 'in-local-flg state) (er soft ctx "Certify-book may not be called inside a LOCAL command.")) ((and (global-val 'skip-proofs-seen wrld) (not (cdr (assoc-eq :skip-proofs-okp suspect-book-action-alist)))) (er soft ctx "At least one event in the current ACL2 world was executed ~ with proofs skipped, either with a call of skip-proofs or by ~ setting ``LD special'' variable '~x0 to a non-nil value. ~ Such an event was:~|~% ~y1~%(If you did not explicitly use ~ skip-proofs or set-ld-skip-proofsp, or call ld with ~ :ld-skip-proofsp not nil, then some other function did so, ~ for example, rebuild or :puff.) Certification is therefore ~ not allowed in this world unless you supply certify-book ~ with :skip-proofs-okp t. See :DOC certify-book." 'ld-skip-proofsp (global-val 'skip-proofs-seen wrld))) ((global-val 'redef-seen wrld) (er soft ctx "At least one command in the current ACL2 world was executed ~ while the value of state global variable '~x0 was not ~ nil:~|~% ~y1~%Certification is therefore not allowed in ~ this world. You can use :ubt to undo back through this ~ command; see :DOC ubt." 'ld-redefinition-action (global-val 'redef-seen wrld))) ((and (not (pcert-op-p cert-op)) (global-val 'pcert-books wrld)) (let ((books (global-val 'pcert-books wrld))) (er soft ctx "Certify-book has been invoked in an ACL2 world that ~ includes the book~#0~[ below, which is~/s below, each of ~ which is~] only provisionally certified: there is a ~ certificate file with extension .pcert0 or .pcert1, but ~ not with extension .cert.~|~%~@1~|~%A certify-book command is thus ~ illegal in this world unless a :pcert keyword argument is ~ specified to be :create or :convert." books (print-indented-list-msg books 2 "")))) ((ttag wrld) ; We disallow an active ttag at certification time because we don't want to ; think about certain oddly redundant defttag events. Consider for example ; executing (defttag foo), and then certifying a book containing the following ; forms, (certify-book "foo" 1 nil :ttags ((foo nil))), indicating that ttag ; foo is only active at the top level, not inside a book. ; (defttag foo) ; (defun f () ; (declare (xargs :mode :program)) ; (sys-call "ls" nil)) ; The defttag expands to a redundant table event, hence would be allowed. ; Perhaps this is OK, but it is rather scary since we then have a case of a ; book containing a defttag of which there is no evidence of this in any "TTAG ; NOTE" string or in the book's certificate. While we see no real problem ; here, since the defttag really is ignored, still it's very easy for the user ; to work around this situation by executing (defttag nil) before ; certification; so we take this conservative approach. (er soft ctx "It is illegal to certify a book while there is an active ~ ttag, in this case, ~x0. Consider undoing the corresponding ~ defttag event (see :DOC ubt) or else executing ~x1. See ~ :DOC defttag." (ttag wrld) '(defttag nil))) ((f-get-global 'illegal-to-certify-message state) (er soft ctx "It is illegal to certify a book in this session, as ~ explained by the message on a possible invariance violation, ~ printed earlier in this session. To see the message again, ~ evaluate the following form:~|~x0" '(fmx "~@0~%~%" (@ illegal-to-certify-message)))) (t (value nil))) (chk-book-name book-name full-book-name ctx state) (cond ((or (eq cert-op :convert-pcert) (symbol-name-equal k "T")) ; Cause early error now if certificate file is missing. (check-certificate-file-exists full-book-name cert-op ctx state)) (t (value nil))) (mv-let (erp cmds cbds state) (get-portcullis-cmds wrld nil nil names ctx state) (cond (erp (silent-error state)) ((symbol-name-equal k "T") (cond (cmds (er soft ctx (cond ((eq cert-op :convert-pcert) "When you carry out the Convert procedure of provisional ~ certification using the certification world from the ~ provisional (.pcert0) certificate, you must call ~ certify-book in the initial ACL2 logical world. Use :pbt 1 ~ to see the current ACL2 logical world.") (t "When you tell certify-book to recover the certification ~ world from the old certificate, you must call certify-book ~ in the initial ACL2 logical world -- so we don't have to ~ worry about the certification world clashing with the ~ existing logical world. But you are not in the initial ~ logical world. Use :pbt 1 to see the current ACL2 logical ~ world.")))) (t ; So k is t, we are in the initial world, and there is a certificate file ; from which we can recover the portcullis. Do it. (er-let* ((cert-obj (chk-certificate-file full-book-name dir 'certify-book ctx state (cons '(:uncertified-okp . nil) suspect-book-action-alist) t)) ; evalp = t, so world can change (cert-obj-cmds (value (and cert-obj (access cert-obj cert-obj :cmds))))) (chk-acceptable-certify-book1 full-book-name dir '? ; no check needed for k = t nil cert-obj nil ; no cbds should be needed names cert-op suspect-book-action-alist (w state) ; see evalp comment above ctx state))))) (t (chk-acceptable-certify-book1 full-book-name dir k cmds nil cbds names cert-op suspect-book-action-alist wrld ctx state))))))) (defun print-objects (lst ch state) (cond ((null lst) state) (t (pprogn (print-object$ (car lst) ch state) (print-objects (cdr lst) ch state))))) (defun replace-initial-substring (s old old-length new) ; Old is a string with length old-length. If s is a string with old as an ; initial subsequence, then replace the initial subsequence of s by new. ; Otherwise, return s. (cond ((and (stringp s) (> (length s) old-length) (equal old (subseq s 0 old-length))) (concatenate 'string new (subseq s old-length (length s)))) (t s))) (defun replace-string-prefix-in-tree (tree old old-length new) ; Search through the given tree, and for any string with prefix old (which has ; length old-length), replace that prefix with new. This could be coded much ; more efficiently, by avoiding re-consing unchanged structures. (cond ((atom tree) (replace-initial-substring tree old old-length new)) (t (cons (replace-string-prefix-in-tree (car tree) old old-length new) (replace-string-prefix-in-tree (cdr tree) old old-length new))))) (defmacro with-output-object-channel-sharing (chan filename body &optional chan0) ; Attempt to open an output channel in a way that allows structure sharing, as ; per print-circle. Except, if chan0 is non-nil, then it is a channel already ; opened with this macro, and we use chan0 instead. ; Warning: The code in body is responsible for handling failure to open an ; output channel and, if it does open a channel, for closing it. (declare (xargs :guard ; avoid eval twice in macro expansion (and (symbolp chan) (symbolp chan0)))) #+acl2-loop-only `(mv-let (,chan state) (if ,chan0 (mv ,chan0 state) (open-output-channel ,filename :object state)) ,body) #-acl2-loop-only `(if (and (null ,chan0) *print-circle-stream*) (error "A stream is already open for printing with structure sharing, ~ so we cannot~%open such a stream for file ~s." ,filename) (mv-let (,chan state) (if ,chan0 (mv ,chan0 state) (open-output-channel ,filename :object state)) (let ((*print-circle-stream* (if ,chan0 *print-circle-stream* (and ,chan (get-output-stream-from-channel ,chan))))) ; Commented out upon addition of serialize: ; #+hons (when (null ,chan0) (setq *compact-print-file-n* 0)) ,body)))) (defun elide-locals-and-split-expansion-alist (alist acl2x-alist x y) ; This function supports provisional certification. It takes alist, an ; expansion-alist that was produced during the Pcertify (not Pcertify+) ; procedure without eliding locals (hence strongp=t in the call below of ; elide-locals-rec). It extends x and y (initially both nil) and reverses ; each, to return (mv x y), where x is the result of eliding locals from alist, ; and y is the result of accumulating original entries from alist that were ; changed before going into x, but only those that do not already equal ; corresponding entries in acl2x-alist (another expansion-alist). We will ; eventually write the elided expansion-alist (again, obtained by accumulating ; into x) into the :EXPANSION-ALIST field of the .pcert0 file, and the ; non-elided part (again, obtained by accumulating into y) will become the ; value of the :PCERT-INFO field of the .pcert0 file. The latter will be ; important for providing a suitable expansion-alist for the Convert procedure ; of provisional certification, where local events are needed in order to ; support proofs. (cond ((endp alist) (mv (reverse x) (reverse y))) (t (assert$ ; the domain of acl2x-alist is extended by alist (or (null acl2x-alist) (<= (caar alist) (caar acl2x-alist))) (let ((acl2x-alist-new (cond ((and acl2x-alist (eql (caar alist) (caar acl2x-alist))) (cdr acl2x-alist)) (t acl2x-alist)))) (mv-let (changedp form) (elide-locals-rec (cdar alist) t) (cond (changedp (elide-locals-and-split-expansion-alist (cdr alist) acl2x-alist-new (acons (caar alist) form x) (cond ((and acl2x-alist ; optimization (equal (car alist) (car acl2x-alist))) y) (t (cons (car alist) y))))) (t (elide-locals-and-split-expansion-alist (cdr alist) acl2x-alist-new (cons (car alist) x) y))))))))) (defun make-certificate-file1 (file portcullis certification-file post-alist3 expansion-alist pcert-info cert-op ctx state) ; See make-certificate-file. ; Warning: For soundness, we need to avoid using iprinting when writing to ; certificate files. We do all such writing with print-object$, which does not ; use iprinting. ; Warning: The use of with-output-object-channel-sharing and ; with-print-defaults below should be kept in sync with analogous usage in ; copy-pcert0-to-pcert1. (assert$ (not (member-eq cert-op ; else we exit certify-book-fn before this point '(:write-acl2x :write-acl2xu))) (assert$ (implies (eq cert-op :convert-pcert) (eq (cert-op state) :create+convert-pcert)) (let ((chk-sum (check-sum-cert-obj (car portcullis) ; :cmds (cdr portcullis) ; :pre-alist post-alist3 ; :post-alist expansion-alist ; :expansion-alist ))) (cond ((not (integerp chk-sum)) (value (er hard ctx "Check-sum-obj returned a non-integerp value on the ~ portcullis and post-alist3!"))) (t (with-output-object-channel-sharing ch certification-file (cond ((null ch) (er soft ctx "We cannot open a certificate file for ~x0. The file we tried ~ to open for output was ~x1." file certification-file)) (t (with-print-defaults ((current-package "ACL2") (print-circle (f-get-global 'print-circle-files state))) (pprogn (print-object$ '(in-package "ACL2") ch state) (print-object$ (f-get-global 'acl2-version state) ch state) (print-object$ :BEGIN-PORTCULLIS-CMDS ch state) (print-objects ; We could apply hons-copy to (car portcullis) here, but we don't. See the ; Remark on Fast-alists in install-for-add-trip-include-book. (car portcullis) ch state) (print-object$ :END-PORTCULLIS-CMDS ch state) (cond (expansion-alist (pprogn (print-object$ :EXPANSION-ALIST ch state) (print-object$ ; We could apply hons-copy to expansion-alist here, but we don't. See the ; Remark on Fast-alists in install-for-add-trip-include-book. expansion-alist ch state))) (t state)) (print-object$ (cdr portcullis) ch state) (print-object$ post-alist3 ch state) (print-object$ chk-sum ch state) (cond (pcert-info (pprogn (print-object$ :PCERT-INFO ch state) (print-object$ ; We could apply hons-copy to pcert-info (as it may be an expansion-alist ; without local elision), but we don't. See the Remark on Fast-alists in ; install-for-add-trip-include-book. pcert-info ch state))) (t state)) (close-output-channel ch state) (value certification-file)))))))))))) (defun make-certificate-file-relocated (file portcullis certification-file post-alist3 expansion-alist pcert-info old-dir new-dir cert-op ctx state) ; This function supports the creation of relocated .cert files for Debian GCL ; distributions. See make-certificate-file. ; Warning: It is tempting to replace strings in (car portcullis), which is the ; list of portcullis commands, and the expansion-alist. However, that will ; result in an uncertified book after moving the .cert.final file to the .cert ; file, because post-alist3 contains a checksum for the book as computed by ; function check-sum-cert, using the portcullis commands and the ; expansion-alist. (make-certificate-file1 file (cons (car portcullis) (replace-string-prefix-in-tree (cdr portcullis) old-dir (length old-dir) new-dir)) certification-file (replace-string-prefix-in-tree post-alist3 old-dir (length old-dir) new-dir) expansion-alist pcert-info cert-op ctx state)) (defun make-certificate-file (file portcullis post-alist1 post-alist2 expansion-alist pcert-info cert-op ctx state) ; This function writes out, and returns, a certificate file. We first give ; that file a temporary name. Our original motivation was the expectation that ; afterwards, compilation is performed and then the certificate file is renamed ; to its suitable .cert name. This way, we expect that that the compiled file ; will have a write date that is later than (or at least, not earlier than) the ; write date of the certificate file; yet, we can be assured that "make" ; targets that depend on the certificate file's existence will be able to rely ; implicitly on the compiled file's existence as well. After Version_4.3 we ; arranged that even when not compiling we use a temporary file, so that (we ; hope) once the .cert file exists, it has all of its contents. ; We assume file satisfies chk-book-name. The portcullis is a pair (cmds ; . pre-alist), where cmds is the list of portcullis commands that created the ; world in which the certification was done, and pre-alist is the ; include-book-alist just before certification was done. Post-alist1 is the ; include-book-alist after proving the events in file and post-alist2 is the ; include-book-alist after just including the events in file. If they are ; different it is because the book included some subbooks within LOCAL forms ; and those subbooks did not get loaded for post-alist2. ; For efficiency, we pass in a check-sum, chk-sum, already computed for: ; (make cert-obj ; :cmds (car portcullis) ; :pre-alist (cdr portcullis) ; :post-alist post-alist3 ; :expansion-alist expansion-alist) ; To verify that a subsequent inclusion is ok, we really only need post-alist2. ; That is, if the book included some LOCAL subbook then it is not necessary ; that that subbook even exist when we include the main book. On the other ; hand, we trace calls of skip-proofs using the call of ; skipped-proofsp-in-post-alist in include-book-fn, which requires ; consideration of LOCALly included books; and besides, it might be useful to ; know what version of the subbook we used during certification, although the ; code at the moment makes no use of that. So we massage post-alist1 so that ; any subbook in it that is not in post-alist2 is marked LOCAL. Thus, ; post-alist3, below, will be of the form ; ((full1 user1 familiar1 cert-annotations1 . chk-sum1) ; ... ; (LOCAL (fulli useri familiari cert-annotationsi . chk-sumi)) ; ... ; (fullk userk familiark cert-annotationsk . chk-sumk)) ; and thus is not really an include-book-alist. By deleting the LOCAL ; elements from it we obtain post-alist2. ; We write a certificate file for file. The certificate file has the ; following form: ; (in-package "ACL2") ; "ACL2 Version x.y" ; :BEGIN-PORTCULLIS-CMDS ; this is here just to let us check that the file ; cmd1 ; is not a normal list of events. ; ... ; cmdk ; :END-PORTCULLIS-CMDS ; pre-alist ; post-alist3 ; chk-sum ; where chk-sum is the check sum of ((cmds . pre-alist) . post-alist3). ; The reason the portcullis commands are written this way, rather than ; as a single object, is that we can't read them all at once since ; they may contain DEFPKGs. We have to read and eval the cmdi ; individually. ; Optionally, create .cert.final file as well; see comment below. (let ((certification-file (convert-book-name-to-cert-name file cert-op)) (post-alist3 (mark-local-included-books post-alist1 post-alist2))) (er-progn ; For Debian release: ; Warning: The following mechanism should work well in most cases, but it is ; not guaranteed to be sound. For example, function fix-path will ignore ; macros that expand (non-trivially) to event forms that should be handled by ; fix-path. With some effort, we could use macroexpand to deal with this ; issue, by storing the macroexpanded forms in the portcullis in such cases. ; Even then, however, there would be problems with progn! (albeit only with ; active trust tags), since we can't really control what's in such forms. And ; we would have to be careful to handle make-event forms properly, if we wanted ; an ironclad correctness guarantee for this mechanism. End of Warning. ; A .cert.final file is created if state globals 'old-certification-dir and ; 'new-certification-dir are set to strings. For example, in your ; ACL2 customization file you might put: ; (f-put-global 'old-certification-dir "/fix/debian/acl2/acl2-xx/books" state) ; (f-put-global 'new-certification-dir "/usr/share/acl2-xx/books" state) ; This will create a second certificate file, .cert.final (in addition to the ; .cert file), with post-alist3 fixed up so that for each string with prefix ; equal to the value of state global 'old-certification-dir, that prefix is ; replaced by the value of state global 'new-certification-dir. The books in ; defpkg forms of the portcullis are similarly fixed as well. ; Warning: This .cert.final process works as described above, without ; modification in the case that we are doing provisional certification. If we ; want to use this process with provisional certification, some additional work ; may be required. (let ((old-dir (and (f-boundp-global 'old-certification-dir state) (f-get-global 'old-certification-dir state))) (new-dir (and (f-boundp-global 'new-certification-dir state) (f-get-global 'new-certification-dir state)))) (cond (old-dir (cond ((and (stringp old-dir) (stringp new-dir) (not (equal old-dir "")) (not (equal new-dir "")) (not (equal (char old-dir (1- (length old-dir))) *directory-separator*)) (not (equal (char new-dir (1- (length new-dir))) *directory-separator*))) (make-certificate-file-relocated file portcullis (concatenate 'string certification-file ".final") post-alist3 expansion-alist pcert-info old-dir new-dir cert-op ctx state)) (t (er soft ctx "Attempted to create ~x0 because state global ~ 'old-certification-dir is bound to a non-nil ~ value, ~x1. However, in this case we require that ~ both this variable and 'new-certification-dir are ~ bound to non-empty strings not terminating in ~s2; ~ but this is not the case." (concatenate 'string certification-file ".final") old-dir *directory-separator-string*)))) (t (value :irrelevant-value)))) (make-certificate-file1 file portcullis (concatenate 'string certification-file ".temp") post-alist3 expansion-alist pcert-info cert-op ctx state)))) (defun make-certificate-files (full-book-name portcullis post-alist1 post-alist2 expansion-alist pcert-info cert-op ctx state) ; This function returns a renaming alist with entries (temp-file ; . desired-file). (cond ((eq cert-op :create+convert-pcert) (er-let* ((pcert0-file (make-certificate-file full-book-name portcullis post-alist1 post-alist2 expansion-alist pcert-info :create-pcert ctx state))) (er-let* ((pcert1-file (make-certificate-file full-book-name portcullis post-alist1 post-alist2 expansion-alist nil ; pcert-info for .pcert1 file :convert-pcert ctx state))) (value (list (cons pcert0-file (convert-book-name-to-cert-name full-book-name :create-pcert)) (cons pcert1-file (convert-book-name-to-cert-name full-book-name :convert-pcert))))))) (t (er-let* ((cert-file (make-certificate-file full-book-name portcullis post-alist1 post-alist2 expansion-alist pcert-info cert-op ctx state))) (value (list (cons cert-file (convert-book-name-to-cert-name full-book-name cert-op)))))))) ; We now develop a general-purpose read-object-file, which expects ; the given file to start with an IN-PACKAGE and then reads into that ; package all of the remaining forms of the file, returning the list ; of all forms read. (defun open-input-object-file (file ctx state) ; If this function returns without error, then a channel is returned. ; In our use of this function in INCLUDE-BOOK we know file is a string. ; Indeed, it is a book name. But we write this function slightly more ; ruggedly so that read-object-file, below, can be used on an ; arbitrary alleged file name. (cond ((stringp file) (mv-let (ch state) (open-input-channel file :object state) (cond ((null ch) (er soft ctx "There is no file named ~x0 that can be ~ opened for input." file)) (t (value ch))))) (t (er soft ctx "File names in ACL2 must be strings, so ~x0 is not a ~ legal file name." file)))) (defun read-object-file1 (channel state ans) ; Channel is an open input object channel. We have verified that the ; first form in the file is an in-package and we are now in that ; package. We read all the remaining objects in the file and return ; the list of them. (mv-let (eofp val state) (read-object channel state) (cond (eofp (value (reverse ans))) (t (read-object-file1 channel state (cons val ans)))))) (defun read-object-file (file ctx state) ; We open file for object input (causing an error if file is ; inappropriate). We then get into the package specified by the ; (in-package ...) at the top of file, read all the objects in file, ; return to the old current package, close the file and exit, ; returning the list of all forms read (including the IN-PACKAGE). (er-let* ((ch (open-input-object-file file ctx state)) (new-current-package (chk-in-package ch file nil ctx state))) (state-global-let* ((current-package new-current-package)) (er-let* ((lst (read-object-file1 ch state nil))) (let ((state (close-input-channel ch state))) (value (cons (list 'in-package new-current-package) lst))))))) (defun chk-cert-annotations (cert-annotations portcullis-skipped-proofsp portcullis-cmds full-book-name suspect-book-action-alist ctx state) ; Warning: Chk-cert-annotations and chk-cert-annotations-post-alist are nearly ; duplicates of one another. If you change one, e.g., to add a new kind of ; annotation and its checker, change the other. (er-progn (cond (portcullis-skipped-proofsp ; After Version_3.4, we don't expect this case to be evaluated, because we ; already checked the certification world for skipped proofs in ; chk-acceptable-certify-book. For now, we leave this inexpensive check for ; robustness. If we find a reason that it's actually necessary, we should add ; a comment here explaining that reason. (include-book-er full-book-name nil (cons "The certification world for book ~x0 contains one or more ~ SKIP-PROOFS events~@3." (list (cons #\3 (if (and (consp portcullis-skipped-proofsp) (eq (car portcullis-skipped-proofsp) :include-book)) (msg " under (subsidiary) book \"~@0\"" (cadr portcullis-skipped-proofsp)) "")))) :skip-proofs-okp suspect-book-action-alist ctx state)) ((eq (cdr (assoc-eq :skipped-proofsp cert-annotations)) nil) (value nil)) ((eq (cdr (assoc-eq :skipped-proofsp cert-annotations)) t) (include-book-er full-book-name nil (if portcullis-cmds "The book ~x0 (including events from its portcullis) ~ contains one or more SKIP-PROOFS events." "The book ~x0 contains one or more SKIP-PROOFS events.") :skip-proofs-okp suspect-book-action-alist ctx state)) (t (include-book-er full-book-name nil (if portcullis-cmds "The book ~x0 (including events from its ~ portcullis) may contain SKIP-PROOFS events." "The book ~x0 may contain SKIP-PROOFS events.") :skip-proofs-okp suspect-book-action-alist ctx state))) (cond ((eq (cdr (assoc :axiomsp cert-annotations)) nil) (value nil)) ((eq (cdr (assoc :axiomsp cert-annotations)) t) (include-book-er full-book-name nil (if portcullis-cmds "The book ~x0 (including events from its portcullis) ~ contains one or more DEFAXIOM events." "The book ~x0 contains one or more DEFAXIOM events.") :defaxioms-okp suspect-book-action-alist ctx state)) (t (include-book-er full-book-name nil (if portcullis-cmds "The book ~x0 (including events from its ~ portcullis) may contain DEFAXIOM events." "The book ~x0 may contain DEFAXIOM events.") :defaxioms-okp suspect-book-action-alist ctx state))))) (defun chk-cert-annotations-post-alist (post-alist portcullis-cmds full-book-name suspect-book-action-alist ctx state) ; Warning: Chk-cert-annotations and chk-cert-annotations-post-alist are nearly ; duplicates of one another. If you change one, e.g., to add a new kind of ; annotation and its checker, change the other. ; We are in the process of including the book full-book-name. Post-alist is ; its locally-marked include-book alist as found in the .cert file. We look ; at every entry (LOCAL or not) and check that its cert annotations are ; consistent with the suspect-book-action-list. (cond ((endp post-alist) (value nil)) (t ; An entry in the post-alist is (full user familiar cert-annotations . chk). ; It may optionally be embedded in a (LOCAL &) form. (let* ((localp (eq (car (car post-alist)) 'local)) (full-subbook (if localp (car (cadr (car post-alist))) (car (car post-alist)))) (cert-annotations (if localp (cadddr (cadr (car post-alist))) (cadddr (car post-alist))))) (er-progn (cond ((eq (cdr (assoc-eq :skipped-proofsp cert-annotations)) nil) (value nil)) ((eq (cdr (assoc-eq :skipped-proofsp cert-annotations)) t) (include-book-er full-book-name nil (cons "The book ~x0~sp~#a~[~/ locally~] includes ~xb, which ~ contains one or more SKIP-PROOFS events." (list (cons #\a (if localp 1 0)) (cons #\b full-subbook) (cons #\p (if portcullis-cmds " (including events from its portcullis)" "")))) :skip-proofs-okp suspect-book-action-alist ctx state)) (t (include-book-er full-book-name nil (cons "The book ~x0~sp~#a~[~/ locally~] includes ~xb, which ~ may contain SKIP-PROOFS events." (list (cons #\a (if localp 1 0)) (cons #\b full-subbook) (cons #\p (if portcullis-cmds " (including events from its portcullis)" "")))) :skip-proofs-okp suspect-book-action-alist ctx state))) (cond ((eq (cdr (assoc :axiomsp cert-annotations)) nil) (value nil)) ((eq (cdr (assoc :axiomsp cert-annotations)) t) (include-book-er full-book-name nil (cons "The book ~x0~sp~#a~[~/ locally~] includes ~xb, which ~ contains one or more DEFAXIOM events." (list (cons #\a (if localp 1 0)) (cons #\b full-subbook) (cons #\p (if portcullis-cmds " (including events from its portcullis)" "")))) :defaxioms-okp suspect-book-action-alist ctx state)) (t (include-book-er full-book-name nil (cons "The book ~x0~sp~#a~[~/ locally~] includes ~xb, which ~ may contain DEFAXIOM events." (list (cons #\a (if localp 1 0)) (cons #\b full-subbook) (cons #\p (if portcullis-cmds " (including events from its ~ portcullis)" "")))) :defaxioms-okp suspect-book-action-alist ctx state))) (chk-cert-annotations-post-alist (cdr post-alist) portcullis-cmds full-book-name suspect-book-action-alist ctx state)))))) (defun chk-input-object-file (file ctx state) ; This checks that an object file named file can be opened for input. ; It either causes an error or returns t. It changes the state -- ; because it opens and closes a channel to the file -- and it may well ; be that the file does not exist in the state returned! C'est la ; guerre. The purpose of this function is courtesy to the user. It ; is nice to rather quickly determine, in include-book for example, ; whether an alleged file exists. (er-let* ((ch (open-input-object-file file ctx state))) (let ((state (close-input-channel ch state))) (value t)))) (defun include-book-dir (dir state) (cond ((eq dir :system) (f-get-global 'system-books-dir state)) (t (let* ((alist0 (f-get-global 'raw-include-book-dir-alist state)) (alist (cond ((eq alist0 :ignore) (cdr (assoc-eq :include-book-dir-alist (table-alist 'acl2-defaults-table (w state))))) (t alist0)))) (cdr (assoc-eq dir alist)))))) (defmacro include-book-dir-with-chk (soft-or-hard ctx dir) `(let ((ctx ,ctx) (dir ,dir)) (let ((dir-value (include-book-dir dir state))) (cond ((null dir-value) ; hence, dir is not :system (er ,soft-or-hard ctx "The legal values for the :DIR argument are keywords that ~ include :SYSTEM as well as those added by a call of ~ add-include-book-dir. However, that argument is ~x0, which ~ is not among the list of those legal values, ~x1." dir (cons :system (strip-cars (cdr (assoc-eq :include-book-dir-alist (table-alist 'acl2-defaults-table (w state)))))))) (t ,(if (eq soft-or-hard 'soft) '(value dir-value) 'dir-value)))))) (defun newly-defined-top-level-fns-rec (trips collect-p full-book-name acc) ; Trips is a world segment in reverse order, i.e., with oldest events first. ; Initially trips corresponds to the part of the world added by an include-book ; event for full-book-name. We accumulate into acc (which is eventually ; returned) the list of function symbols defined in trips whose definition ; comes from the top level of the book with path full-book-name, rather than ; some sub-book; or, if full-book-name is nil, then we accumulate events not ; inside any book. Collect-p is true only when we are to collect up such ; function symbols. (cond ((endp trips) acc) ((and (eq (caar trips) 'include-book-path) (eq (cadar trips) 'global-value)) (newly-defined-top-level-fns-rec (cdr trips) (equal (car (cddar trips)) full-book-name) full-book-name acc)) ((not collect-p) (newly-defined-top-level-fns-rec (cdr trips) nil full-book-name acc)) ((and (eq (caar trips) 'cltl-command) (eq (cadar trips) 'global-value) (equal (caddar trips) 'defuns)) (newly-defined-top-level-fns-rec (cdr trips) collect-p full-book-name (union-eq (strip-cars (cdddr (cddar trips))) acc))) (t (newly-defined-top-level-fns-rec (cdr trips) collect-p full-book-name acc)))) (defun newly-defined-top-level-fns (old-wrld new-wrld full-book-name) ; New-wrld is the installed world, an extension of old-wrld. (let ((old-len (length old-wrld)) (new-len (length new-wrld))) (assert$ (<= old-len new-len) (let* ((len-old-past-boot-strap (cond ((equal (access-command-tuple-form (cddar old-wrld)) '(exit-boot-strap-mode)) ; optimization for common case 0) (t (- old-len (length (lookup-world-index 'command (access command-number-baseline-info (global-val 'command-number-baseline-info new-wrld) ; installed world :original) new-wrld))))))) (newly-defined-top-level-fns-rec (first-n-ac-rev (- new-len old-len) new-wrld nil) t full-book-name (newly-defined-top-level-fns-rec (first-n-ac-rev len-old-past-boot-strap old-wrld nil) t nil nil)))))) (defun accumulate-post-alist (post-alist include-book-alist) ; Post-alist is a tail of a post-alist from the certificate of a book. ; Include-book-alist is an include-book-alist, typically a value of world ; global 'include-book-alist-all. We accumulate post-alist into ; include-book-alist, stripping off each LOCAL wrapper. (cond ((endp post-alist) include-book-alist) (t (let* ((entry0 (car post-alist)) (entry (if (eq (car entry0) 'LOCAL) (cadr entry0) entry0))) (cond ((member-equal entry include-book-alist) (accumulate-post-alist (cdr post-alist) include-book-alist)) (t (cons entry (accumulate-post-alist (cdr post-alist) include-book-alist)))))))) (defun skipped-proofsp-in-post-alist (post-alist) (cond ((endp post-alist) nil) (t ; An entry in the post-alist is (full user familiar cert-annotations . chk). ; It may optionally be embedded in a (LOCAL &) form. (let* ((localp (eq (car (car post-alist)) 'local)) (cert-annotations (if localp (cadddr (cadr (car post-alist))) (cadddr (car post-alist))))) (cond ((cdr (assoc-eq :skipped-proofsp cert-annotations)) (if localp (car (cadr (car post-alist))) (car (car post-alist)))) (t (skipped-proofsp-in-post-alist (cdr post-alist)))))))) (defun check-sum-cert (portcullis-cmds expansion-alist book-ev-lst) ; This function computes a check-sum for post-alists in .cert files. It is a ; bit odd because get-portcullis-cmds gives the results of make-event expansion ; but book-ev-lst does not. But that seems OK. (check-sum-obj (list* portcullis-cmds expansion-alist book-ev-lst))) ; For a discussion of early loading of compiled files for include-book, which ; is supported by the next few forms, see the Essay on Hash Table Support for ; Compilation. #+acl2-loop-only (defmacro with-hcomp-bindings (do-it form) (declare (ignore do-it)) form) #-acl2-loop-only (defmacro with-hcomp-bindings (do-it form) (let ((ht-form (and do-it '(make-hash-table :test 'eq)))) `(let ((*hcomp-fn-ht* ,ht-form) (*hcomp-const-ht* ,ht-form) (*hcomp-macro-ht* ,ht-form) (*hcomp-fn-alist* nil) (*hcomp-const-alist* nil) (*hcomp-macro-alist* nil) (*declaim-list* nil)) ,@(and do-it '((declare (type hash-table *hcomp-fn-ht* *hcomp-const-ht* *hcomp-macro-ht*)))) ,form))) #+acl2-loop-only (defmacro with-hcomp-ht-bindings (form) form) #-acl2-loop-only (defmacro with-hcomp-ht-bindings (form) ; Consider a call of include-book-fn. If it is on behalf of certify-book-fn, ; then a call of with-hcomp-bindings (in certify-book-fn) has already bound the ; *hcomp-xxx-ht* variables. Otherwise, this macro binds them, as needed for ; the calls under include-book-fn1 of chk-certificate-file (which evaluates ; portcullis commands) and process-embedded-events, in order to use the ; relevant values stored in the three hash tables associated with the book from ; the early load of its compiled file. Note that since these three hash table ; variables are destructively modified, we won't lose changes to them in the ; behalf-of-certify-flg case when we pop these bindings. ; Warning: Behalf-of-certify-flg and full-book-name need to be bound where this ; macro is called. `(let* ((entry (and (not behalf-of-certify-flg) (and *hcomp-book-ht* ; for load without compiled file (gethash full-book-name *hcomp-book-ht*)))) (*hcomp-fn-ht* (if behalf-of-certify-flg *hcomp-fn-ht* (and entry (access hcomp-book-ht-entry entry :fn-ht)))) (*hcomp-const-ht* (if behalf-of-certify-flg *hcomp-const-ht* (and entry (access hcomp-book-ht-entry entry :const-ht)))) (*hcomp-macro-ht* (if behalf-of-certify-flg *hcomp-macro-ht* (and entry (access hcomp-book-ht-entry entry :macro-ht))))) ,form)) (defun get-declaim-list (state) #+acl2-loop-only (read-acl2-oracle state) #-acl2-loop-only (value *declaim-list*)) (defun tilde-@-book-stack-msg (reason load-compiled-stack) ; Reason is t if the present book was to be included with :load-compiled-file ; t; it is nil if we are only to warn on missing compiled files; and otherwise, ; it is the full-book-name of a parent book that was to be included with ; :load-compiled-file t. (let* ((stack-rev (reverse (strip-cars load-compiled-stack))) (arg (cond (stack-rev (msg " Here is the sequence of books with loads of compiled or ~ expansion files that have led down to the printing of this ~ message, where the load for each is halted during the load ~ for the next:~|~%~*0" `(" " ; what to print if there's nothing to print " ~s*" ; how to print the last element " ~s*~|" ; how to print the 2nd to last element " ~s*~|" ; how to print all other elements ,stack-rev))) (t " No load was in progress for any parent book.")))) (cond ((eq reason t) (msg " This is an error because an include-book for this book ~ specified :LOAD-COMPILE-FILE ~x0; see :DOC include-book.~@1" reason arg)) (reason (msg " This is an error because we are underneath an include-book ~ for~| ~y0that specified :LOAD-COMPILE-FILE ~x1; see :DOC ~ include-book.~@2" reason t arg)) (t arg)))) (defun convert-book-name-to-acl2x-name (x) ; X is assumed to satisfy chk-book-name. We generate the corresponding ; acl2x file name, in analogy to how convert-book-name-to-cert-name generates ; certificate file. ; See the Essay on .acl2x Files (Double Certification). (coerce (append (reverse (cddddr (reverse (coerce x 'list)))) '(#\a #\c #\l #\2 #\x)) 'string)) (defun acl2x-alistp (x index len) (cond ((atom x) (and (null x) (< index len))) ((consp (car x)) (and (integerp (caar x)) (< index (caar x)) (acl2x-alistp (cdr x) (caar x) len))) (t nil))) (defun read-acl2x-file (acl2x-file full-book-name len acl2x ctx state) (mv-let (acl2x-date state) (file-write-date$ acl2x-file state) (cond ((not acl2x) (pprogn (cond (acl2x-date (warning$ ctx "acl2x" "Although the file ~x0 exists, it is being ~ ignored because keyword option :ACL2X T was ~ not supplied to certify-book." acl2x-file full-book-name)) (t state)) (value nil))) (t (mv-let (book-date state) (file-write-date$ full-book-name state) (cond ((or (not (natp acl2x-date)) (not (natp book-date)) (< acl2x-date book-date)) (cond ((eq acl2x :optional) (value nil)) (t (er soft ctx "Certify-book has been instructed with option :ACL2X T to ~ read file ~x0. However, this file ~#1~[does not exist~/has ~ not been confirmed to be at least as recent as the book ~ ~x2~]. See :DOC set-write-acl2x." acl2x-file (if acl2x-date 1 0) full-book-name)))) (t (er-let* ((chan (open-input-object-file acl2x-file ctx state))) (state-global-let* ((current-package "ACL2")) (cond (chan (mv-let (eofp val state) (read-object chan state) (cond (eofp (er soft ctx "No form was read in acl2x file ~x0.~|See ~ :DOC certify-book." acl2x-file)) ((acl2x-alistp val 0 len) (pprogn (observation ctx "Using expansion-alist containing ~n0 ~ ~#1~[entries~/entry~/entries~] from ~ file ~x2." (length val) (zero-one-or-more val) acl2x-file) (value val))) (t (er soft ctx "Illegal value in acl2x file:~|~x0~|See :DOC ~ certify-book." val))))) (t (value nil)))))))))))) (defun eval-port-file (full-book-name ctx state) (let ((port-file (convert-book-name-to-port-name full-book-name)) (dir (directory-of-absolute-pathname full-book-name))) (pprogn (mv-let (ch state) (open-input-channel port-file :object state) (cond ((null ch) (value nil)) (t (er-let* ((pkg (state-global-let* ((infixp nil)) (chk-in-package ch port-file t ctx state)))) (cond ((null pkg) ; empty .port file (value nil)) ((not (equal pkg "ACL2")) (er soft ctx "File ~x0 is corrupted. It was expected either to contain no ~ forms or to start with the form (in-package \"ACL2\")." port-file)) (t (pprogn (io? event nil state (port-file) (fms "Note: Reading .port file, ~s0.~|" (list (cons #\0 port-file)) (proofs-co state) state nil)) (state-global-let* ((current-package "ACL2") (connected-book-directory dir set-cbd-state)) (mv-let (error-flg val state) (revert-world-on-error (with-reckless-readtable ; Here we read the .port file. We use with-reckless-readtable so that we can ; read characters such as #\Null; otherwise, for example, we get an error using ; CCL if we certify a book on top of the command (make-event `(defconst ; *new-null* ,(code-char 0))). Note that the .port file is not intended to be ; written directly by users, so we can trust that we are reading back in what ; was written unless a different host Lisp was used for reading and writing the ; .port file. Fortunately, the .port file is generally only used when ; including uncertified books, where all bets are off. ; Note that chk-raise-portcullis1 resets the acl2-defaults-table just as would ; be done when raising the portcullis of a certified book. (chk-raise-portcullis1 full-book-name port-file ch t ctx state))) (pprogn (close-input-channel ch state) (cond (error-flg (silent-error state)) (t (pprogn (cond ((null val) ; We considered printing "Note: file ~x0 contains no commands.~|", but that ; could be annoying since in this common case, the user might not even be ; thinking about .port files. state) (t (io? event nil state (port-file val) (fms "ACL2 has processed the ~n0 ~ command~#1~[~/s~] in file ~x2.~|" (list (cons #\0 (length val)) (cons #\1 val) (cons #\2 port-file)) (proofs-co state) state nil)))) (value val))))))))))))))))) (defun getenv! (str state) ; This is just getenv$, except that "" is coerced to nil. (declare (xargs :stobjs state :guard (stringp str))) (er-let* ((temp (getenv$ str state))) (value (and (not (equal temp "")) temp)))) (defun update-pcert-books (full-book-name pcert-p wrld) (cond (pcert-p (global-set 'pcert-books (cons full-book-name (global-val 'pcert-books wrld)) wrld)) (t wrld))) (defun convert-non-nil-symbols-to-keywords (x) (cond ((null x) nil) ((symbolp x) (intern (symbol-name x) "KEYWORD")) ((atom x) x) (t (cons (convert-non-nil-symbols-to-keywords (car x)) (convert-non-nil-symbols-to-keywords (cdr x)))))) (defun include-book-fn1 (user-book-name state load-compiled-file expansion-alist uncertified-okp defaxioms-okp skip-proofs-okp ttags doc ; Bound above and used below: ctx full-book-name directory-name familiar-name behalf-of-certify-flg cddr-event-form) #+acl2-loop-only (declare (ignore load-compiled-file)) (let* ((wrld0 (w state)) (old-skip-proofs-seen (global-val 'skip-proofs-seen wrld0)) (active-book-name (active-book-name wrld0 state)) (old-ttags-seen (global-val 'ttags-seen wrld0)) #-(or acl2-loop-only hons) (*fchecksum-symbol-memo* (if *inside-include-book-fn* *fchecksum-symbol-memo* (make-hash-table :test 'eq))) #-acl2-loop-only (*inside-include-book-fn* (if behalf-of-certify-flg 'hcomp-build t)) (old-include-book-path (global-val 'include-book-path wrld0)) (saved-acl2-defaults-table (table-alist 'acl2-defaults-table wrld0)) ; If you add more keywords to the suspect-book-action-alist, make sure you do ; the same to the list constructed by certify-book-fn. You might wish to ; handle the new warning summary in warning1. (suspect-book-action-alist (list (cons :uncertified-okp (if (member-eq (cert-op state) '(nil :write-acl2xu)) uncertified-okp nil)) (cons :defaxioms-okp defaxioms-okp) (cons :skip-proofs-okp skip-proofs-okp))) (include-book-alist0 (global-val 'include-book-alist wrld0))) (er-progn (chk-book-name user-book-name full-book-name ctx state) (chk-input-object-file full-book-name ctx state) (revert-world-on-error (cond ((and (not (global-val 'boot-strap-flg wrld0)) full-book-name (assoc-equal full-book-name include-book-alist0)) (stop-redundant-event ctx state)) (t (let* ((wrld1 (global-set 'include-book-path (cons full-book-name old-include-book-path) wrld0))) (pprogn (set-w 'extension wrld1 state) (er-let* ((redef (chk-new-stringp-name 'include-book full-book-name ctx wrld1 state)) (doc-pair (translate-doc full-book-name doc ctx state)) (cert-obj (if behalf-of-certify-flg (value nil) (with-hcomp-ht-bindings (chk-certificate-file full-book-name directory-name 'include-book ctx state suspect-book-action-alist t)))) (wrld2 (er-progn (cond ((or cert-obj behalf-of-certify-flg) (value nil)) (t (eval-port-file full-book-name ctx state))) (value (w state)))) (post-alist (value (and cert-obj (access cert-obj cert-obj :post-alist)))) (cert-full-book-name (value (car (car post-alist))))) (cond ; We try the redundancy check again, because it will be cert-full-book-name ; that is stored on the world's include-book-alist, not full-book-name (if the ; two book names differ). ((and (not (equal full-book-name cert-full-book-name)) (not (global-val 'boot-strap-flg wrld2)) cert-full-book-name (assoc-equal cert-full-book-name include-book-alist0)) ; Chk-certificate-file calls chk-certificate-file1, which calls ; chk-raise-portcullis, which calls chk-raise-portcullis1, which evaluates, for ; example, maybe-install-acl2-defaults-table. So we need to revert the world ; here. (pprogn (set-w 'retraction wrld0 state) (stop-redundant-event ctx state))) (t (er-let* ((ev-lst (read-object-file full-book-name ctx state))) ; Cert-obj above is either nil, indicating that the file is uncertified, or is ; a cert-obj record, which contains the now raised portcullis and the check sum ; alist of the files that should be brought in by this inclusion. The first ; element of post-alist is the one for this book. It should look like this: ; (full-book-name' user-book-name' familiar-name cert-annotations ; . ev-lst-chk-sum), where the first two names are irrelevant here because they ; reflect where the book was when it was certified rather than where the book ; resides now. However, the familiar-name, cert-annotations and the ; ev-lst-chk-sum ought to be those for the current book. (let ((ev-lst-chk-sum (and cert-obj ; hence not behalf-of-certify-flg (check-sum-cert (access cert-obj cert-obj :cmds) (access cert-obj cert-obj :expansion-alist) ev-lst)))) (cond ((and cert-obj (not (integerp ev-lst-chk-sum))) ; This error should never arise because check-sum-obj (called by ; check-sum-cert) is only called on something produced by read-object, which ; checks that the object is ACL2 compatible, and perhaps make-event expansion. ; The next form causes a soft error, assigning proper blame. (er soft ctx "ACL2 has enountered an object, ~x0, which check ~ sum was unable to handle." ev-lst-chk-sum)) (t (er-let* ((no-errp-1 ; Notice that we are reaching inside the certificate object to retrieve ; information about the book from the post-alist. (Car post-alist)) is in fact ; of the form (full-book-name user-book-name familiar-name cert-annotations ; . ev-lst-chk-sum). (cond ((and cert-obj (not (equal (caddr (car post-alist)) familiar-name))) (include-book-er full-book-name nil (cons "The cer~-ti~-fi~-cate on file for ~x0 lists the ~ book under the name ~x3 whereas we were expecting ~ it to give the name ~x4. While we allow a ~ certified book to be moved from one directory to ~ another after cer~-ti~-fi~-ca~-tion, we insist ~ that it keep the same familiar name. This allows ~ the cer~-ti~-fi~-cate file to contain the familiar ~ name, making it easier to identify which ~ cer~-ti~-fi~-cates go with which files and ~ inspiring a little more confidence that the ~ cer~-ti~-fi~-cate really does describe the alleged ~ file. In the present case, it looks as though the ~ familiar book name was changed after ~ cer~-ti~-fi~-ca~-tion. For what it is worth, the ~ check sum of the file at cer~-ti~-fi~-ca~-tion was ~ ~x5. Its check sum now is ~x6." (list (cons #\3 (caddr (car post-alist))) (cons #\4 familiar-name) (cons #\5 (cddddr (car post-alist))) (cons #\6 ev-lst-chk-sum))) :uncertified-okp suspect-book-action-alist ctx state)) (t (value t)))) (no-errp-2 (cond ((and cert-obj (not (equal (cddddr (car post-alist)) ev-lst-chk-sum))) (include-book-er full-book-name nil (cons "The certificate on file for ~x0 lists the check ~ sum of the certified book as ~x3. But the check ~ sum computed for that book is now ~x4. This ~ generally indicates that the file has been ~ modified since it was last certified (though it ~ could be the portcullis commands or the make-event ~ expansions that have changed)." ; Developer debug: ; ~|~%Developer note: ~ ; the latter was computed as:~|~%~X56" (list (cons #\3 (cddddr (car post-alist))) (cons #\4 ev-lst-chk-sum) ; Developer debug: ; (cons #\5 ; `(check-sum-cert ; ',(access cert-obj cert-obj ; :cmds) ; ',(access cert-obj cert-obj ; :expansion-alist) ; ',ev-lst)) ; (cons #\6 nil) )) :uncertified-okp suspect-book-action-alist ctx state)) (t (value t)))) (certified-p (value (and cert-obj no-errp-1 no-errp-2))) (acl2x-file (value (convert-book-name-to-acl2x-name full-book-name))) (expansion-alist (cond (behalf-of-certify-flg (value expansion-alist)) (certified-p (value (access cert-obj cert-obj :expansion-alist))) (t (value nil))))) (let* ((cert-annotations (cadddr (car post-alist))) (cert-ttags (cdr (assoc-eq :ttags cert-annotations))) (cert-obj-skipped-proofsp (and cert-obj (cdr (assoc-eq :skipped-proofsp cert-annotations)))) (warn-for-ttags-default (and (eq ttags :default) (not (warning-off-p "Ttags" state)))) (ttags (if (eq ttags :default) :all (convert-non-nil-symbols-to-keywords ttags)))) #-acl2-loop-only (when (and (not certified-p) (not behalf-of-certify-flg) *hcomp-book-ht*) ; The book is not certified, but we may have loaded compiled definitions for it ; into its hash tables. We eliminate any such hash tables now, before calling ; process-embedded-events. Note that we may have already evaluated the ; portcullis commands from an invalid certificate using these hash tables. ; However, even before we implemented early loading of compiled files for ; include book (as described in the Essay on Hash Table Support for ; Compilation), we loaded portcullis commands in such cases -- and we have ; checked that the compiled (or expansion) file is no older than the ; certificate file, to ensure that the hash tables really do go with the ; certificate. So at least we have not compounded the error of evaluating ; portcullis commands by using the relevant values from the hash tables. (remhash full-book-name *hcomp-book-ht*)) (er-let* ((ttags (chk-well-formed-ttags ttags directory-name ctx state)) (ignored-val (cond ((or cert-obj-skipped-proofsp (and cert-obj (cdr (assoc-eq :axiomsp cert-annotations)))) (chk-cert-annotations cert-annotations nil (access cert-obj cert-obj :cmds) full-book-name suspect-book-action-alist ctx state)) (t (value nil)))) (ttags-info ; this value is ignored if not certified-p (cond ((not certified-p) (value nil)) (t (er-progn ; We check that the ttags supplied as an argument to include-book are ; sufficiently inclusive to allow the ttags from the certificate. No global ; state is updated, not even 'ttags-allowed; this is just a check. (chk-acceptable-ttags1 cert-ttags nil ; the active-book-name is irrelevant ttags nil ; ttags-seen is irrelevant :quiet ; do not print ttag notes ctx state) ; From the check just above, we know that the ttags supplied as arguments are ; sufficient to allow the certificate's ttags. We next check that the global ; ttags-allowed are also sufficient to allow the certificate's ttags. The ; following call returns a pair to be bound to ttags-info (above), consisting ; of a refined ttags-allowed and an extended ttags-seen. It prints all ; relevant ttag notes if the book is certified; below, we bind ; skip-notify-on-defttag in that case so that we don't see ttag notes for ; individual events in the book. (chk-acceptable-ttags1 ; With some effort, perhaps we could find a way to avoid causing an error when ; this call of chk-acceptable-ttags1 returns an error. But that would take ; some effort; see the Essay on Trust Tags (Ttags). cert-ttags active-book-name (f-get-global 'ttags-allowed state) old-ttags-seen (if warn-for-ttags-default (cons ctx full-book-name) t) ctx state))))) (skip-proofsp ; At one time we bound this variable to 'initialize-acl2 if (or cert-obj ; behalf-of-certify-flg) is false. But cert-obj is non-nil even if the ; check-sum is wrong, so we were distinguishing between two kinds of ; uncertified books: those with bad certificates and those with no ; certificates. And inclusion of either sort of uncertified book is an "all ; bets are off" situation. So it seems fine to use 'include-book here in all ; cases. But why do we want to do so? Eric Smith sent a nice example of a ; book with forms (local (include-book "bar")) and (local (my-macro)), where ; my-macro is defined in bar.lisp. With 'initialize-acl2, ; chk-embedded-event-form recurs through the local calls and reports that ; (my-macro) is not an embedded event form (because the local inclusion of ; "bar" prevent my-macro from being defined). With 'include-book, we can ; include the book. More generally, Eric would like uncertified books to be ; treated by include-book much like certified books, in order to assist his ; development process. That seems reasonable. (value 'include-book)) ; The following process-embedded-events is protected by the revert-world- ; on-error above. See the Essay on Guard Checking for a discussion of the ; binding of guard-checking-on below. (ttags-allowed1 (state-global-let* ((axiomsp nil) (ttags-allowed (if certified-p cert-ttags (f-get-global 'ttags-allowed state))) (skip-notify-on-defttag (and ttags-info ; hence certified-p full-book-name)) (connected-book-directory directory-name) (match-free-error nil) (guard-checking-on nil) (in-local-flg (and (f-get-global 'in-local-flg state) 'local-include-book))) (er-progn (with-hcomp-ht-bindings (process-embedded-events 'include-book ; We do not allow process-embedded-events-to set the ACL2 defaults table at the ; end. For, consider the case that (defttag foo) has been executed just before ; the (include-book "bar") being processed. At the start of this ; process-embedded-events we clear the acl2-defaults-table, removing any :ttag. ; If we try to restore the acl2-defaults-table at the end of this ; process-embedded-events, we will fail because the include-book-path was ; extended above to include full-book-name (for "bar"), and the restoration ; installs a :ttag of foo, yet in our example there is no :ttags argument for ; (include-book "bar"). So, instead we directly set the 'table-alist property ; of 'acl2-defaults-table directory for the install-event call below. :do-not-install skip-proofsp (cadr (car ev-lst)) (list 'include-book full-book-name) (subst-by-position expansion-alist (cdr ev-lst) 1) 1 (and (eq skip-proofsp 'include-book) ; We want to skip the make-event check when including an uncertified book. (or certified-p behalf-of-certify-flg)) ctx state)) (value (if ttags-info ; hence certified-p (car ttags-info) (f-get-global 'ttags-allowed state))))))) ; The above process-embedded-events call returns what might be called ; proto-wrld3, which is equivalent to the current world of state before the ; process-embedded-events (since the insigs argument is nil), but it has an ; incremented embedded-event-depth. We don't care about this world. The ; interesting world is the one current in the state returned by ; process-embedded-events. It has all the embedded events in it and we are ; done except for certification issues. (let* ((wrld3 (w state)) (actual-alist (global-val 'include-book-alist wrld3))) (er-let* ((certified-p (cond ((and certified-p (not (include-book-alist-subsetp (unmark-and-delete-local-included-books (cdr post-alist)) actual-alist))) ; Our next step is to call include-book-er, but we break up that computation so ; that we avoid needless computation (potentially reading certificate files) if ; no action is to be taken. (let ((warning-summary (include-book-er-warning-summary :uncertified-okp suspect-book-action-alist state))) (cond ((and (equal warning-summary "Uncertified") (warning-disabled-p "Uncertified")) (value nil)) (t (mv-let (msgs state) (tilde-*-book-check-sums-phrase (unmark-and-delete-local-included-books (cdr post-alist)) actual-alist state) (include-book-er1 full-book-name nil (cons "After including the book ~x0:~|~*3." (list (cons #\3 msgs))) warning-summary ctx state)))))) (t (value certified-p))))) (er-progn ; Now we check that all the subbooks of this one are also compatible with the ; current settings of suspect-book-action-alist. The car of post-alist is the ; part that deals with full-book-name itself. So we deal below with the cdr, ; which lists the subbooks. The cert-obj may be nil, which makes the test ; below a no-op. (chk-cert-annotations-post-alist (cdr post-alist) (and cert-obj (access cert-obj cert-obj :cmds)) full-book-name suspect-book-action-alist ctx state) (let* ((cert-annotations (cadddr (car post-alist))) ; If cert-obj is nil, then cert-annotations is nil. If cert-obj is ; non-nil, then cert-annotations is non-nil. Cert-annotations came ; from a .cert file, and they are always non-nil. But in the ; following, cert-annotations may be nil. (certification-tuple (cond (certified-p ; Below we use the full book name from the certificate, cert-full-book-name, ; rather than full-book-name (from the parse of the user-book-name), in ; certification-tuple, Intuitively, cert-full-book-name is the unique ; representative of the class of all legal full book names (including those ; that involve soft links). Before Version_2.7 we used full-book-name rather ; than cert-full-book-name, and this led to problems as shown in the example ; below. ;;; % ls temp*/*.lisp ;;; temp1/a.lisp temp2/b.lisp temp2/c.lisp ;;; % cat temp1/a.lisp ;;; (in-package "ACL2") ;;; (defun foo (x) x) ;;; % cat temp2/b.lisp ;;; (in-package "ACL2") ;;; (defun goo (x) x) ;;; % cat temp2/c.lisp ;;; (in-package "ACL2") ;;; (defun hoo (x) x) ;;; % ;;; ;;; Below, two absolute pathnames are abbreviated as and . ;;; ;;; In temp2/ we LD a file with the following forms. ;;; ;;; (certify-book "/a") ;;; :u ;;; (include-book "../temp1/a") ;;; (certify-book "b" 1) ;;; :ubt! 1 ;;; (include-book "b") ;;; (certify-book "c" 1) ;;; ;;; We then see the following error. The problem is that involved symbolic ;;; links, and hence did not match up with the entry in the world's ;;; include-book-alist made by (include-book "../temp1/a") which expanded to an ;;; absolute pathname that did not involve symbolic links. ;;; ;;; ACL2 Error in (CERTIFY-BOOK "c" ...): During Step 3 , we loaded different ;;; books than were loaded by Step 2! Perhaps some other user of your ;;; file system was editing the books during our Step 3? You might think ;;; that some other job is recertifying the books (or subbooks) and has ;;; deleted the certificate files, rendering uncertified some of the books ;;; needed here. But more has happened! Some file has changed! ;;; ;;; Here is the include-book-alist as of the end of Step 2: ;;; (("/temp2/c.lisp" ;;; "c" "c" ((:SKIPPED-PROOFSP) (:AXIOMSP)) ;;; . 48180423) ;;; ("/temp2/b.lisp" ;;; "b" "b" ((:SKIPPED-PROOFSP) (:AXIOMSP)) ;;; . 46083312) ;;; (LOCAL ("/a.lisp" ;;; "/a" ;;; "a" ((:SKIPPED-PROOFSP) (:AXIOMSP)) ;;; . 43986201))). ;;; ;;; And here is the alist as of the end of Step 3: ;;; (("/temp2/c.lisp" ;;; "c" "c" ((:SKIPPED-PROOFSP) (:AXIOMSP)) ;;; . 48180423) ;;; ("/temp2/b.lisp" ;;; "b" "b" ((:SKIPPED-PROOFSP) (:AXIOMSP)) ;;; . 46083312) ;;; ("/temp1/a.lisp" ;;; "/temp1/a" ;;; "a" ((:SKIPPED-PROOFSP) (:AXIOMSP)) ;;; . 43986201)). ;;; ;;; Frequently, the former has more entries than the latter because the ;;; former includes LOCAL books. So compare corresponding entries, focusing ;;; on those in the latter. Each entry is of the form (name1 name2 name3 ;;; alist . chk-sum). Name1 is the full name, name2 is the name as written ;;; in an include-book event, and name3 is the ``familiar'' name of the ;;; file. The alist indicates the presence or absence of problematic forms ;;; in the file, such as DEFAXIOM events. For example, (:AXIOMSP . T) ;;; means there were defaxiom events; (:AXIOMSP . NIL) -- which actually ;;; prints as (:AXIOMSP) -- means there were no defaxiom events. Finally, ;;; chk-sum is either an integer check sum on the contents of the file ;;; at the time it was certified or else chk-sum is nil indicating that ;;; the file is not certified. Note that if the chk-sum is nil, the entry ;;; prints as (name1 name2 name3 alist). Go figure. ; ; ;;; Summary ;;; Form: (CERTIFY-BOOK "c" ...) ;;; Rules: NIL ;;; Warnings: Guards ;;; Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) ; ;;; ******** FAILED ******** See :DOC failure ******** FAILED ******** ;;; :ERROR ;;; ACL2 !> (list* cert-full-book-name user-book-name familiar-name cert-annotations ev-lst-chk-sum)) (t ; The certification tuple below is marked as uncertified because the ; ev-lst-chk-sum is nil. What about cert-annotations? It may or may ; not correctly characterize the file, it may even be nil. Is that ; bad? No, the check sum will always save us. (list* full-book-name user-book-name familiar-name cert-annotations nil))))) (er-progn #-acl2-loop-only (cond ((eq load-compiled-file :comp) (compile-for-include-book full-book-name certified-p ctx state)) (t (value nil))) (pprogn (redefined-warning redef ctx state) (f-put-global 'ttags-allowed ttags-allowed1 state) (er-let* ((declaim-list (get-declaim-list state)) (pcert-p (cond ((and cert-obj (access cert-obj cert-obj :pcert-info)) (pprogn (cond ((or (pcert-op-p (cert-op state)) (warning-off-p "Provisionally certified" state)) state) (t (mv-let (erp pcert-envp state) (getenv! "ACL2_PCERT" state) (assert$ (not erp) (cond (pcert-envp state) (t (warning$ ctx ("Provisionally certified") "The book ~s0 was only ~ provisionally certified (proofs ~ ~s1)." full-book-name (if (eq (access cert-obj cert-obj :pcert-info) :proved) "completed" "skipped")))))))) (value t))) (t (value nil))))) (install-event (if behalf-of-certify-flg declaim-list (or cert-full-book-name full-book-name)) (list* 'include-book ; We use the the unique representative of the full book name provided by the ; one in the .cert file, when the certificate is valid before execution of this ; event), namely, cert-full-book-name; otherwise, we use the full-book-name ; parsed from what the user supplied. Either way, we have an absolute path ; name, which is useful for the :puff and :puff* commands. These could fail ; before Version_2.7 because the relative path name stored in the event was not ; sufficient to find the book at :puff/:puff* time. (or cert-full-book-name full-book-name) cddr-event-form) 'include-book full-book-name nil nil t ctx (let* ((wrld4 (update-pcert-books full-book-name pcert-p (global-set 'include-book-path old-include-book-path (update-doc-database full-book-name doc doc-pair (global-set 'certification-tuple certification-tuple (global-set 'include-book-alist (add-to-set-equal certification-tuple (global-val 'include-book-alist wrld3)) (global-set 'include-book-alist-all (add-to-set-equal certification-tuple (accumulate-post-alist (cdr post-alist) (global-val 'include-book-alist-all wrld3))) wrld3))))))) (wrld5 (if ttags-info ; hence certified-p (global-set? 'ttags-seen (cdr ttags-info) wrld4 old-ttags-seen) wrld4)) (wrld6 (if (equal (table-alist 'acl2-defaults-table wrld3) saved-acl2-defaults-table) wrld5 (putprop 'acl2-defaults-table 'table-alist saved-acl2-defaults-table wrld5))) (wrld7 (cond ((or old-skip-proofs-seen (null cert-obj)) wrld6) (t (let ((full-book-name (if cert-obj-skipped-proofsp ; We prefer that an error report about skip-proofs in certification world be ; about a non-local event. full-book-name (skipped-proofsp-in-post-alist post-alist)))) (if full-book-name (global-set 'skip-proofs-seen (list :include-book full-book-name) wrld6) wrld6)))))) wrld7) state)))))))))))))))))))))))))) (defun include-book-fn (user-book-name state load-compiled-file expansion-alist uncertified-okp defaxioms-okp skip-proofs-okp ttags doc dir event-form) ; Note that the acl2-defaults-table is initialized when raising the portcullis. ; As of this writing, this happens by way of a call of chk-certificate-file in ; include-book-fn1, as chk-certificate-file calls chk-certificate-file1, which ; calls chk-raise-portcullis, etc. ; Expansion-alist is an expansion-alist generated from make-event calls if is ; called by certify-book-fn. Otherwise, it is :none. (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'include-book user-book-name)) (pprogn (cond ((and (not (eq load-compiled-file :default)) (not (eq load-compiled-file nil)) (not (f-get-global 'compiler-enabled state))) (warning$ ctx "Compiled file" "Ignoring value ~x0 supplied for include-book keyword ~ parameter :LOAD-COMPILED-FILE, treating it as ~x1 ~ instead, because of an earlier evaluation of ~x2; see ~ :DOC compilation." load-compiled-file nil '(set-compiler-enabled nil state))) (t state)) (er-let* ((dir-value (cond (dir (include-book-dir-with-chk soft ctx dir)) (t (value (cbd)))))) (mv-let (full-book-name directory-name familiar-name) (parse-book-name dir-value user-book-name ".lisp" ctx state) (let* ((behalf-of-certify-flg (not (eq expansion-alist :none))) (load-compiled-file0 load-compiled-file) (load-compiled-file (and (f-get-global 'compiler-enabled state) load-compiled-file)) (cddr-event-form (if (and event-form (eq load-compiled-file0 load-compiled-file)) (cddr event-form) (append (if (not (eq load-compiled-file :default)) (list :load-compiled-file load-compiled-file) nil) (if (not (eq uncertified-okp t)) (list :uncertified-okp uncertified-okp) nil) (if (not (eq defaxioms-okp t)) (list :defaxioms-okp defaxioms-okp) nil) (if (not (eq skip-proofs-okp t)) (list :skip-proofs-okp skip-proofs-okp) nil) (if doc (list :doc doc) nil))))) (cond ((or behalf-of-certify-flg #-acl2-loop-only *hcomp-book-ht* (null load-compiled-file)) ; So, *hcomp-book-ht* was previously bound by certify-book-fn or in the other ; case, below. (include-book-fn1 user-book-name state load-compiled-file expansion-alist uncertified-okp defaxioms-okp skip-proofs-okp ttags doc ; The following were bound above: ctx full-book-name directory-name familiar-name behalf-of-certify-flg cddr-event-form)) (t (let #+acl2-loop-only () #-acl2-loop-only ((*hcomp-book-ht* (make-hash-table :test 'equal))) ; Populate appropriate hash tables; see the Essay on Hash Table Support for ; Compilation. #-acl2-loop-only (include-book-raw-top full-book-name directory-name load-compiled-file dir ctx state) (include-book-fn1 user-book-name state load-compiled-file expansion-alist uncertified-okp defaxioms-okp skip-proofs-okp ttags doc ; The following were bound above: ctx full-book-name directory-name familiar-name behalf-of-certify-flg cddr-event-form)))))))))) (defun spontaneous-decertificationp1 (ibalist alist files) ; Ibalist is an include-book alist, while alist is the strip-cddrs of ; an include-book alist. Thus, an entry in ibalist is of the form ; (full-book-name user-book-name familiar-name cert-annotations ; . ev-lst-chk-sum), while an entry in alist is (familiar-name ; cert-annotations . ev-lst-chk-sum). We know, from context, that ; (subsetp-equal (strip-cddrs ibalist) alist) fails. Thus, there are ; entries in ibalist that are not ``in'' alist, where ``in'' compares ; (familiar-name cert-annotations . ev-lst-chk-sum) tuples. We ; determine whether each such entry fails only because the chk-sum in ; the ibalist is nil while that in a corresponding entry in the alist ; is non-nil. If so, then the most likely explanation is that a ; concurrent process is recertifying certain books and deleted their ; .cert files. We return the list of all files which have been ; decertified. (cond ((endp ibalist) files) (t (let* ((familiar-name1 (caddr (car ibalist))) (cert-annotations1 (cadddr (car ibalist))) (ev-lst-chk-sum1 (cddddr (car ibalist))) (temp (assoc-equal familiar-name1 alist)) (cert-annotations2 (cadr temp)) (ev-lst-chk-sum2 (cddr temp))) (cond (temp (cond ((equal (cddr (car ibalist)) temp) ; This entry is identical to its mate in alist. So we keep ; looking. (spontaneous-decertificationp1 (cdr ibalist) alist files)) ((and (or (null cert-annotations1) (equal cert-annotations1 cert-annotations2)) (equal ev-lst-chk-sum1 nil) ev-lst-chk-sum2) ; The full-book-name (car (car ibalist)) spontaneously decertified. ; So we collect it and keep looking. (spontaneous-decertificationp1 (cdr ibalist) alist (cons (car (car ibalist)) files))) (t nil))) (t nil)))))) (defun spontaneous-decertificationp (alist1 alist2) ; We know that alist1 is not an include-book-alist-subset of alist2. ; We check whether this is precisely because some files which were ; certified in alist2 are not certified in alist1. If so, we return ; the list of all such files. But if we find any other kind of ; discrepancy, we return nil. (spontaneous-decertificationp1 alist1 (strip-cddrs alist2) nil)) (defun remove-duplicates-equal-from-end (lst acc) (cond ((endp lst) (reverse acc)) ((member-equal (car lst) acc) (remove-duplicates-equal-from-end (cdr lst) acc)) (t (remove-duplicates-equal-from-end (cdr lst) (cons (car lst) acc))))) (defun include-book-alist-subsetp-failure-witnesses (alist1 strip-cddrs-alist2 acc) ; We accumulate into acc all members of alist1 that serve as counterexamples to ; (include-book-alist-subsetp alist1 alist2), where strip-cddrs-alist2 = ; (strip-cddrs alist2). (cond ((endp alist1) acc) (t (include-book-alist-subsetp-failure-witnesses (cdr alist1) strip-cddrs-alist2 (if (member-equal (cddr (car alist1)) strip-cddrs-alist2) acc (cons (car alist1) acc)))))) ; Essay on Guard Checking ; We bind the state global variable guard-checking-on to nil in certify-book-fn ; and in include-book-fn (using state-global-let*), and also in ; pc-single-step-primitive. We bind guard-checking-on to t in prove, ; translate-in-theory-hint, and value-triple. We do not bind guard-checking-on ; in defconst-fn. Here we explain these decisions. ; We prefer to bind guard-checking-on to a predetermined fixed value when ; certifying or including books. Why? Book certification is a logical act. ; :Set-guard-checking is intended to be extra-logical, giving the user control ; over evaluation in the interactive loop, and hence we do not want it to ; affect how books are processed, either during certification or during ; inclusion. ; So the question now is whether to bind guard-checking-on to t or to nil for ; book certification and for book inclusion. (We reject :none and :all because ; they can be too inefficient.) ; We want it to be the case that if a book is certified, then subsequently it ; can be included. In particular, it would be unfortunate if certification is ; done in an environment with guard checking off, and then later we get a guard ; violation when including the book with guard checking on. So if we bind ; guard-checking-on to nil in certify-book, then we should also bind it to nil ; in include-book. ; We argue now for binding guard-checking-on to nil in certify-book-fn (and ; hence, as argued above, in include-book-fn as well). Note that we already ; allow book certification without requiring guard verification, which drives ; home the position that guards are extra-logical. Thus, a high-level argument ; for binding guard-checking-on to nil during certification is that if we bind ; instead to t, then that position is diluted. Now we give a more practical ; argument for binding guard-checking-on to nil during certification. Suppose ; someone writes a macro with a guard, where that guard enforces some ; intention. Do we want to enforce that guard, and when? We already have ; safe-mode to enforce the guard as necessary for Common Lisp. If a user ; executes :set-guard-checking nil in the interactive loop, then function ; guards are not checked, and thus it is reasonable not to check macro guards ; either. Now suppose the same user attempts to certify a book that contains a ; top-level guard-violating macro call. What a rude surprise it would be if ; certification fails due to a guard violation during expansion of that macro ; call, when the interactive evaluation of that same call had just succeeded! ; (One might argue that it is a sophisticated act to turn off guard checking in ; the interactive loop, hence a user who does that should be able to handle ; that rude surprise. But that argument seems weak; even a beginner could find ; out how to turn off guard checking after seeing a guard violation.) ; We have argued for turning off guard checking during certify-book. But a ; concern remains. Suppose one user has written a macro with a guard, and now ; suppose a second user creates a book containing a top-level call of that ; macro with a guard violation. Safe-mode will catch any Common Lisp guard ; violation. But the macro writer may have attached the guard in order to ; enforce some intention that is not related to Common Lisp. In the case of ; functions, one can ensure one's compliance with existing guards by verifying ; all guards, for example with (set-verify-guards-eagerness 2) at the top of ; the file. A similar "complete guard checking" mechanism could enforce one's ; compliance with macro guards as well, say, (set-verify-guards-eagerness 3). ; The same concern applies to defconst, not only for macro expansion but also ; for function calls, and could be handled in the case of complete guard ; checking in the same way as for top-level macro expansion, by binding ; guard-checking-on to t with state-global-let*. In practice, users may not ; demand the capability for complete guard checking, so it might not be ; important to provide this capability. ; Having decided to bind guard-checking-on to nil in certify-book-fn and ; (therefore) include-book-fn, let us turn to the other cases in which we bind ; guard-checking-on. ; We discussed defconst briefly above. We note that raw Lisp evaluation should ; never take place for the body of a defconst form (outside the boot-strap), ; because the raw Lisp definition of defconst avoids such evaluation when the ; name is already bound, which should be the case from prior evaluation of the ; defconst form in the ACL2 loop. Value-triple also is not evaluated in raw ; Lisp, where it is defined to return nil. ; We bind guard-checking-on to nil in prove, because proofs can use evaluation ; and such evaluation should be done in the logic, without regard to guards. ; It can be important to check guards during theory operations like ; union-theory, not only during certify-book but in the interactive loop. For ; example, with guard checking off in Version_2.9, one gets a hard Lisp error ; upon evaluation of the following form. ; (in-theory (union-theories '((:rewrite no-such-rule)) ; (current-theory 'ground-zero))) ; (Aside. One does not get such an error in Version_2.8, because *1* functions ; checked guards of system functions regardless of the value of ; guard-checking-on; but we have abandoned that aggressive approach, relying ; instead on safe-mode.) Our solution is to bind guard-checking-on to t in ; translate-in-theory-hint, which calls simple-translate-and-eval and hence ; causes the guards to be checked. ; Note that guard-checking-on is bound to nil in pc-single-step-primitive. We ; no longer recall why, but we may as well preserve that binding. (defun expansion-filename (full-book-name convert-to-os-p state) ; We use a .lsp suffix instead of .lisp for benefit of the makefile system, ; which by default looks for .lisp files to certify. ; Full-book-name is expected to be a Unix-style filename. We return an OS ; filename. (let* ((file (if convert-to-os-p (pathname-unix-to-os full-book-name state) full-book-name)) (len (length file))) (assert$ (equal (subseq file (- len 5) len) ".lisp") (concatenate 'string (subseq file 0 (- len 5)) "@expansion.lsp")))) (defun write-expansion-file (portcullis-cmds declaim-list new-fns-exec expansion-filename expansion-alist expansion-alist-pkg-names ev-lst known-package-alist ctx state) ; Expansion-filename is the expansion file for a certified book (or, a book ; whose certification is nearly complete) that has been through ; include-book-fn. (We call set-current-package below instead of the ; corresponding f-put-global as a partial check that this inclusion has taken ; place.) We write out that expansion file, instead causing an error if we ; cannot open it. #+acl2-loop-only (declare (ignore new-fns-exec expansion-alist-pkg-names known-package-alist)) (with-output-object-channel-sharing ch expansion-filename (cond ((null ch) (er soft ctx "We cannot open expansion file ~s0 for output." expansion-filename)) (t (with-print-defaults ((current-package "ACL2") (print-circle (f-get-global 'print-circle-files state))) (pprogn (io? event nil state (expansion-filename) (fms "Writing book expansion file, ~s0." (list (cons #\0 expansion-filename)) (proofs-co state) state nil)) ; Note: We replace the in-package form at the top of the original file, because ; we want to print in the ACL2 package. See the Essay on Hash Table Support ; for Compilation. (print-object$ '(in-package "ACL2") ch state) ; The next forms introduce packages so that ensuing defparameter forms can be ; read in. The form (maybe-introduce-empty-pkg-1 name) generates defpackage ; forms for name, which are no-ops when the packages already exist. For GCL it ; seems important to put all the defpackage forms at the top of any file to ; compile, immediately after the initial in-package form; otherwise we have ; seen scary warnings in GCL 2.6.7. So we lay down these defpackage forms ; first, and then we lay down maybe-introduce-empty-pkg-2 calls in order to ; tell ACL2 that any such packages not already known to ACL2 are acceptable, ; provided they have no imports. (If they have imports then they must have ; been defined in raw Lisp, and ACL2 should complain. They might even have ; been defined in raw Lisp if they do not have imports, of course, but there ; are limits to how hard we will work to protect the user who traffics in raw ; Lisp evaluation.) #-acl2-loop-only (let ((ans1 nil) (ans2 nil)) (dolist (entry known-package-alist) (let ((pkg-name (package-entry-name entry))) (when (not (member-equal pkg-name ; from initial known-package-alist '("ACL2-USER" "ACL2-PC" "ACL2-INPUT-CHANNEL" "ACL2-OUTPUT-CHANNEL" "ACL2" "COMMON-LISP" "KEYWORD"))) (push `(maybe-introduce-empty-pkg-1 ,pkg-name) ans1) (push `(maybe-introduce-empty-pkg-2 ,pkg-name) ans2)))) (dolist (pkg-name expansion-alist-pkg-names) ; To see why we need these forms, consider the following book. ; (in-package "ACL2") ; (local (include-book "arithmetic/equalities" :dir :system)) ; (make-event (list 'defun (intern$ "FOO" "ACL2-ASG") '(x) 'x)) ; Without these forms, we get a hard Lisp error when include-book attempts to ; load the compiled file, because *hcomp-fn-alist* is defined using the symbol ; acl2-asg::foo, which is in a package not yet known at the time of the load. (push `(maybe-introduce-empty-pkg-1 ,pkg-name) ans1) (push `(maybe-introduce-empty-pkg-2 ,pkg-name) ans2)) (print-objects ans1 ch state) (print-objects ans2 ch state)) #-acl2-loop-only (mv-let (fn-alist const-alist macro-alist) (hcomp-alists-from-hts) (pprogn (print-object$ `(setq *hcomp-fn-alist* ',fn-alist) ch state) (print-object$ `(setq *hcomp-const-alist* ',const-alist) ch state) (print-object$ `(setq *hcomp-macro-alist* ',macro-alist) ch state))) (print-object$ '(hcomp-init) ch state) (newline ch state) (cond (declaim-list (pprogn (princ$ ";;; Declaim forms:" ch state) (newline ch state) (princ$ (concatenate 'string "#+" (symbol-name (f-get-global 'host-lisp state))) ch state) (print-object$ (cons 'progn (reverse declaim-list)) ch state))) (t (princ$ ";;; Note: There are no declaim forms to print." ch state))) ; We print a single progn for all top-level events in order to get maximum ; sharing with compact printing. This trick isn't necessary of course for the ; non-hons version, but it seems simplest to do this the same way for both the ; hons and non-hons versions. (mv-let (erp val state) (state-global-let* ((fmt-hard-right-margin 10000 set-fmt-hard-right-margin) (fmt-soft-right-margin 10000 set-fmt-soft-right-margin)) (pprogn (fms ";;; Printing ~x0 portcullis command~#1~[~/s~] followed by ~ book contents,~%;;; with make-event expansions." (list (cons #\0 (length portcullis-cmds)) (cons #\1 portcullis-cmds)) ch state nil) (value nil))) (declare (ignore erp val)) state) (print-object$ (cons 'progn (append portcullis-cmds (subst-by-position expansion-alist (cdr ev-lst) 1))) ch state) (newline ch state) #-acl2-loop-only (progn (when new-fns-exec (princ$ ";;; *1* function definitions to compile:" ch state) ; No newline is needed here, as compile-uncompiled-*1*-defuns uses ; print-object$, which starts by printing a newline. ; We untrace functions before attempting any compilation, in case there is any ; inlining or other use of symbol-functions. But first we save the traced ; symbol-functions, and then we restore them immediately afterwards. We don't ; use untrace$ and trace$ because trace$ may require a trust tag that is no ; longer available, for example if (break-on-error) has been invoked. (let ((trace-specs (f-get-global 'trace-specs state)) retrace-alist) (unwind-protect (dolist (spec trace-specs) (let* ((fn (car spec)) (*1*fn (*1*-symbol fn)) (old-fn (get fn 'acl2-trace-saved-fn)) (old-*1*fn (get *1*fn 'acl2-trace-saved-fn))) (when old-fn (push (cons fn (symbol-function fn)) retrace-alist) (setf (symbol-function fn) old-fn)) (when old-*1*fn (push (cons *1*fn (symbol-function *1*fn)) retrace-alist) (setf (symbol-function *1*fn) old-*1*fn)))) (compile-uncompiled-*1*-defuns "" ; irrelevant filename new-fns-exec nil ch)) (dolist (pair retrace-alist) (let ((fn (car pair)) (val (cdr pair))) (setf (symbol-function fn) val)))) (newline ch state)) state) (close-output-channel ch state) (value expansion-filename))))))) (defun collect-ideal-user-defuns1 (tl wrld ans) (cond ((or (null tl) (and (eq (caar tl) 'command-landmark) (eq (cadar tl) 'global-value) (equal (access-command-tuple-form (cddar tl)) '(exit-boot-strap-mode)))) ans) ((and (eq (caar tl) 'cltl-command) (eq (cadar tl) 'global-value) (equal (caddar tl) 'defuns)) (collect-ideal-user-defuns1 (cdr tl) wrld (cond ((null (cadr (cddar tl))) ; Defun-mode-flg = nil means encapsulate or :non-executable. In this case we ; do not pick up the function, but that's OK because we don't care if it is ; executed efficiently. Warning: If we decide to pick it up after all, then ; make sure that the symbol-class is not :program, since after Version_4.1 we ; allow non-executable :program mode functions. ans) ((eq (symbol-class (caar (cdddr (cddar tl))) wrld) :ideal) (append (strip-cars (cdddr (cddar tl))) ans)) (t ans)))) (t (collect-ideal-user-defuns1 (cdr tl) wrld ans)))) (defun collect-ideal-user-defuns (wrld) ; We scan wrld down to command 0 (but not into prehistory), collecting those ; fns which were (a) introduced with defun or defuns and (b) are :ideal. (collect-ideal-user-defuns1 wrld wrld nil)) (defun set-difference-eq-sorted (lst1 lst2 ans) ; Lst1 and lst2 are sorted by symbol-<. If ans is nil, then we return the ; difference of lst1 and lst2, sorted by symbol-<. (cond ((null lst1) (reverse ans)) ((null lst2) (revappend ans lst1)) ((eq (car lst1) (car lst2)) (set-difference-eq-sorted (cdr lst1) (cdr lst2) ans)) ((symbol-< (car lst1) (car lst2)) (set-difference-eq-sorted (cdr lst1) lst2 (cons (car lst1) ans))) (t (set-difference-eq-sorted lst1 (cdr lst2) ans)))) (defun expansion-alist-pkg-names0 (x base-kpa acc) (cond ((consp x) (expansion-alist-pkg-names0 (cdr x) base-kpa (expansion-alist-pkg-names0 (car x) base-kpa acc))) ((and x ; optimization (symbolp x)) (let ((name (symbol-package-name x))) (cond ((or (member-equal name acc) (find-package-entry name base-kpa)) acc) (t (cons name acc))))) (t acc))) #+(and hons (not acl2-loop-only)) (defun hons-union-ordered-string-lists (x y) (cond ((null x) y) ((null y) x) ((hons-equal x y) x) ((hons-equal (car x) (car y)) (hons (car x) (hons-union-ordered-string-lists (cdr x) (cdr y)))) ((string< (car x) (car y)) (hons (car x) (hons-union-ordered-string-lists (cdr x) y))) (t ; (string< (car y) (car x)) (hons (car y) (hons-union-ordered-string-lists x (cdr y)))))) #+(and hons (not acl2-loop-only)) (save-def (defun expansion-alist-pkg-names-memoize (x) (cond ((consp x) (hons-union-ordered-string-lists (expansion-alist-pkg-names-memoize (car x)) (expansion-alist-pkg-names-memoize (cdr x)))) ((and x (symbolp x)) (hons (symbol-package-name x) nil)) (t nil))) ) (defun expansion-alist-pkg-names (x base-kpa) ; For an explanation of the point of this function, see the comment at the call ; of expansion-alist-pkg-names in certify-book-fn. ; X is an expansion-alist and base-kpa is the known-package-alists of the ; certification world. ; We return a list including package names of symbols supporting (the tree) x. ; We do *not* take any sort of transitive closure; that is, for the name of a ; package pkg1 in the returned list and the name of a package pkg2 for a symbol ; imported into pkg1, it does not follow that the name of pkg2 is in the ; returned list. (Note: The transitive closure operation performed by ; new-defpkg-list will take care of this closure for us.) #+(and hons (not acl2-loop-only)) ; Here we use a more efficient but equivalent version of this function that ; memoizes, contributed initially by Sol Swords. This version is only more ; efficient when fast alists are available; otherwise the memo table will be a ; linear list ultimately containing every cons visited, resulting in quadratic ; behavior because of the membership tests against it. (return-from expansion-alist-pkg-names (loop for name in (expansion-alist-pkg-names-memoize x) when (not (find-package-entry name base-kpa)) collect name)) (merge-sort-lexorder ; sort this small list, to agree with hons result above (expansion-alist-pkg-names0 x base-kpa nil))) (defun delete-names-from-kpa (names kpa) (cond ((endp kpa) nil) ((member-equal (package-entry-name (car kpa)) names) (delete-names-from-kpa names (cdr kpa))) (t (cons (car kpa) (delete-names-from-kpa names (cdr kpa)))))) (defun print-certify-book-step-2 (ev-lst expansion-alist pcert0-file acl2x-file state) (io? event nil state (ev-lst expansion-alist pcert0-file acl2x-file) (fms "* Step 2: There ~#0~[were no forms in the file. Why are you ~ making such a silly book?~/was one form in the file.~/were ~n1 ~ forms in the file.~] We now attempt to establish that each ~ form, whether local or non-local, is indeed an admissible ~ embedded event form in the context of the previously admitted ~ ones.~@2~%" (list (cons #\0 (zero-one-or-more ev-lst)) (cons #\1 (length ev-lst)) (cons #\2 (cond (expansion-alist (msg " Note that we are substituting ~n0 ~ ~#1~[form~/forms~], as specified in ~ file~#2~[~x2~/s ~&2~], for ~#1~[a ~ corresponding top-level ~ form~/corresponding top-level forms~] in ~ the book." (length expansion-alist) expansion-alist (if pcert0-file (if acl2x-file (list pcert0-file acl2x-file) (list pcert0-file)) (list acl2x-file)))) (t "")))) (proofs-co state) state nil))) (defun print-certify-book-step-3 (state) (io? event nil state nil (fms "* Step 3: That completes the admissibility check. Each form ~ read was an embedded event form and was admissible. We now ~ retract back to the initial world and try to include the book. ~ This may expose local incompatibilities.~%" nil (proofs-co state) state nil))) (defun print-certify-book-guards-warning (full-book-name new-bad-fns all-bad-fns k ctx state) (let* ((new-bad-fns (sort-symbol-listp new-bad-fns)) (all-bad-fns (sort-symbol-listp all-bad-fns)) (extra-bad-fns (set-difference-eq-sorted all-bad-fns new-bad-fns nil))) (warning$ ctx ("Guards") "~#1~[~/The book ~x0 defines the function~#2~[ ~&2, which has ~ not had its~/s ~&2, which have not had their~] guards ~ verified. ~]~#3~[~/~#1~[For the book ~x0, its~/Moreover, this ~ book's~] included sub-books ~#4~[~/and/or its certification ~ world ~]define function~#5~[ ~&5, which has not had its~/s ~ ~&5, which have not had their~] guards verified. ~]See :DOC ~ guards." full-book-name (if new-bad-fns 1 0) new-bad-fns (if extra-bad-fns 1 0) (if (eql k 0) 0 1) extra-bad-fns))) (defun chk-certify-book-step-3 (post-alist2 post-alist1 ctx state) (cond ((not (include-book-alist-subsetp post-alist2 post-alist1)) (let ((files (spontaneous-decertificationp post-alist2 post-alist1))) (cond (files (er soft ctx "During Step 3, we loaded the uncertified ~#0~[book ~&0. This ~ book was certified when we looked at it~/books ~&0. These books ~ were certified when we looked at them~] in Step 2! The most ~ likely explanation is that some concurrent job, possibly by ~ another user of your file system, is currently recertifying ~ ~#0~[this book~/these books~] (or subbooks of ~#0~[it~/them~]). ~ That hypothetical job might have deleted the certificate files ~ of the books in question, rendering ~#0~[this one~/these~] ~ uncertified. If this explanation seems likely, we recommend ~ that you identify the other job and wait until it has ~ successfully completed." files)) (t (er soft ctx "During Step 3, we loaded different books than were loaded by ~ Step 2! Sometimes this happens when the meaning of ``:dir ~ :system'' for include-book has changed, usually because some ~ included books were previously certified with an ACL2 image ~ whose filename differs from that of the current ACL2 image. ~ Here are the tuples produced by Step 3 of the form ~X04 whose ~ CDDRs are not in the list of tuples produced by Step ~ 2:~|~%~X14~|~%Perhaps some other user of your file system was ~ editing the books during our Step 3? You might think that some ~ other job is recertifying the books (or subbooks) and has ~ deleted the certificate files, rendering uncertified some of the ~ books needed here. But more has happened! Some file has ~ changed (as indicated above)!~%~%DETAILS. Here is the ~ include-book-alist as of the end of Step 2:~%~X24.~|~%And here ~ is the alist as of the end of Step 3:~%~X34.~|~%Frequently, the ~ former has more entries than the latter because the former ~ includes LOCAL books. So compare corresponding entries, focusing ~ on those in the latter. Each entry is of the form (name1 name2 ~ name3 alist . chk-sum). Name1 is the full name, name2 is the ~ name as written in an include-book event, and name3 is the ~ ``familiar'' name of the file. The alist indicates the presence ~ or absence of problematic forms in the file, such as DEFAXIOM ~ events. For example, (:AXIOMSP . T) means there were defaxiom ~ events; (:AXIOMSP . NIL) -- which actually prints as (:AXIOMSP) ~ -- means there were no defaxiom events. Finally, chk-sum is ~ either an integer check sum based on the contents of the file at ~ the time it was certified or else chk-sum is nil indicating that ~ the file is not certified. Note that if the chk-sum is nil, the ~ entry prints as (name1 name2 name3 alist). Go figure." '(:full-book-name :user-book-name :familiar-name :cert-annotations . :chk-sum-for-events) (include-book-alist-subsetp-failure-witnesses post-alist2 (strip-cddrs post-alist1) nil) post-alist1 post-alist2 nil))))) (t (value nil)))) (defun print-certify-book-step-4 (full-book-name expansion-filename cert-op state) (io? event nil state (full-book-name expansion-filename cert-op) (fms "* Step 4: Write the certificate for ~x0 in ~x1~@2.~%" (list (cons #\0 full-book-name) (cons #\1 (convert-book-name-to-cert-name full-book-name cert-op)) (cons #\2 (if expansion-filename (msg ", and compile the expansion file, ~s0" expansion-filename) ""))) (proofs-co state) state nil))) (defun print-certify-book-step-5 (full-book-name state) (io? event nil state (full-book-name) (fms "* Step 5: Compile the functions defined in ~x0.~%" (list (cons #\0 full-book-name)) (proofs-co state) state nil))) (defun hcomp-build-from-portcullis (trips state) #+acl2-loop-only (declare (ignore trips)) #+acl2-loop-only (read-acl2-oracle state) #-acl2-loop-only (hcomp-build-from-portcullis-raw trips state)) ; Essay on .acl2x Files (Double Certification) ; Sometimes make-event expansion requires a trust tag, but the final event does ; not, in which case we may want a "clean" certificate that does not depend on ; a trust tag. For example, a make-event form might call an external tool to ; generate an ordinary ACL2 event. Certify-book solves this problem by ; supporting a form of "double certification" that can avoid putting trust tags ; into the certificate. This works by saving the expansion-alist from a first ; certification of foo.lisp into file foo.acl2x, and then certifying in a way ; that first reads foo.acl2x to avoid redoing make-event expansions, thus ; perhaps avoiding the need for trust tags. One could even certify on a ; separate machine first in order to generate foo.acl2x, for added security. ; Key to the implementation of this ``double certification'' is a new state ; global, write-acl2x, which is set in order to enable writing of the .acl2x ; file. Also, a new certify-book keyword argument, :ttagsx, overrides :ttags ; if write-acl2x is true. So the flow is as follows, where a single ; certify-book command is used in both certifications, with :ttagsx specifying ; the ttags used in the first certification and :ttags specifying the ttags ; used in the second certification (perhaps nil). ; ; First certification: (set-write-acl2x t state) and certify, writing out ; foo.acl2x. Second certification: Replace forms as per foo.acl2x; write out ; foo.cert. ; Why do we use a state global, rather than adding a keyword option to ; certify-book? The reason is that it's easier this way to provide makefile ; support: the same .acl2 file can be used for each of the two certifications ; if the makefile sends an extra set-write-acl2x form before the first ; certification. (And, that is what is done in community books file ; books/Makefile-generic.) ; Note that include-book is not affected by this proposal, because foo.acl2x is ; not consulted: its effect is already recorded in the .cert file produced by ; the second certify-book call. However, after that certification, the ; certificate is not polluted by ttags that were needed only for make-event ; expansion (assuming :check-expansion has its default value of nil in each ; case). ; Some details: ; - If write-acl2x has value t, then we overwrite an existing .acl2x file. (If ; there is demand we could cause an error instead; maybe we'll support value ; :overwrite for that. But we don't have any protection against overwriting ; .cert files, so we'll start by not providing any for .acl2x files, either.) ; If write-acl2x has value nil, then certify-book will use the .acl2x file if ; it exists and is not older than the .lisp file; but it will never insist on ; a .acl2x file (though the Makefile could do that). We could consider ; adding an argument to certify-book that insists on having an up-to-date ; .acl2x file. ; - If write-acl2x has value t, we exit as soon as the .acl2x file is written. ; Not only does this avoid computation necessary for writing a .cert file, ; but also it avoids potential confusion with makefiles, so that presence of ; a .cert file indicates that certification is truly complete. ; - When foo.acl2x exists and write-acl2x has value nil, we check that the form ; read is suitable input to subst-by-position: an alist with increasing posp ; keys, whose last key does not exceed the number of events to process. ; - Consider the input expansion-alist used by the second certify-book call, ; taken from the .acl2x file (to substitute for top-level forms in the book), ; and consider an arbitrary entry (index . form) from that input ; expansion-alist such that index doesn't appear in the generated ; expansion-alist written to the .cert file. Before writing that generated ; expansion-alist to the .cert file, we first add every such (index . form) ; to the generated expansion-alist, to make complete the recording of all ; replacements of top-level forms from the source book. Note that in this ; case form is not subject to make-event expansion, or else index would have ; been included already in the generated expansion-alist. (Even when an ; event is ultimately local and hence is modified by elide-locals, a ; record-expansion form is put into the expansion-alist.) ; - Note that one could create the .acl2x file manually to contain any forms ; one likes, to be processed in place of forms in the source book. There is ; no problem with that. ; - The same use of *print-circle* will be made in writing out the .acl2x file ; as is used when writing the :expansion-alist to the .cert file. ; One might think that one would have to incorporate somehow the checksum of ; the .acl2x file. But the logical content of the certified book depends only ; on the .lisp file and the expansion-alist recorded in the .cert file, not on ; the .acl2x file (which was only used to generate that recorded ; expansion-alist). We already have a mechanism to check those: in particular, ; chk-raise-portcullis (called by chk-certificate-file1) checks the checksum of ; the certificate object against the final value in the .cert file. ; Makefile support is available; see community books file ; books/Makefile-generic. (defstub acl2x-expansion-alist (expansion-alist state) ; Users are welcome to attach their own function to acl2x-expansion-alist, ; because it is only called (by write-acl2x-file) to write out a .acl2x file, ; not to write out a .cert file. We pass in state because some users might ; want to read from the state, for example, obtaining values of state globals. ; Indeed, for this reason, Jared Davis and Sol Swords requested the addition of ; state as a parameter. t) (defun hons-copy-with-state (x state) (declare (xargs :guard (state-p state))) (declare (ignore state)) (hons-copy x)) (defun identity-with-state (x state) (declare (xargs :guard (state-p state))) (declare (ignore state)) x) (defattach (acl2x-expansion-alist ; User-modifiable; see comment in the defstub just above. ; At one time we used hons-copy-with-state here, but we are concerned that this ; will interfere with fast-alists in the #+hons version. See the ; Remark on Fast-alists in install-for-add-trip-include-book. identity-with-state) :skip-checks t) (defun write-acl2x-file (expansion-alist acl2x-file ctx state) (with-output-object-channel-sharing ch acl2x-file (cond ((null ch) (er soft ctx "We cannot open file ~x0 for output." acl2x-file)) (t (with-print-defaults ((current-package "ACL2") (print-circle (f-get-global 'print-circle-files state))) (pprogn (io? event nil state (acl2x-file) (fms "* Step 3: Writing file ~x0 and exiting certify-book.~|" (list (cons #\0 acl2x-file)) (proofs-co state) state nil)) (print-object$ (acl2x-expansion-alist expansion-alist state) ch state) (close-output-channel ch state) (value acl2x-file))))))) (defun merge-into-expansion-alist1 (acl2x-expansion-alist computed-expansion-alist acc) (declare (xargs :measure (+ (len acl2x-expansion-alist) (len computed-expansion-alist)))) (cond ((endp acl2x-expansion-alist) (revappend acc computed-expansion-alist)) ((endp computed-expansion-alist) (revappend acc acl2x-expansion-alist)) ((eql (caar acl2x-expansion-alist) (caar computed-expansion-alist)) (merge-into-expansion-alist1 (cdr acl2x-expansion-alist) (cdr computed-expansion-alist) (cons (car computed-expansion-alist) acc))) ((< (caar acl2x-expansion-alist) (caar computed-expansion-alist)) (merge-into-expansion-alist1 (cdr acl2x-expansion-alist) computed-expansion-alist (cons (car acl2x-expansion-alist) acc))) (t ; (> (caar acl2x-expansion-alist) (caar computed-expansion-alist)) (merge-into-expansion-alist1 acl2x-expansion-alist (cdr computed-expansion-alist) (cons (car computed-expansion-alist) acc))))) (defun acl2x-alistp-domains-subsetp (x y) ; WARNING: each of x and y should be an acl2x-alistp (for suitable lengths). (cond ((null x) t) ((endp y) nil) ((eql (caar x) (caar y)) (acl2x-alistp-domains-subsetp (cdr x) (cdr y))) ((< (caar x) (caar y)) nil) (t ; (> (caar x) (caar y)) (acl2x-alistp-domains-subsetp x (cdr y))))) (defun merge-into-expansion-alist (acl2x-expansion-alist computed-expansion-alist) ; Note: Computed expansion-alist can be a value for the :pcert-info field of a ; cert-obj that represents the empty expansion-alist (:unproved or :proved). ; Each argument is an expansion-alist, i.e., an alist whose keys are increasing ; positive integers (see acl2x-alistp). We return the expansion-alist whose ; domain is the union of the domains of the two inputs, mapping each index to ; its value in computed-expansion-alist if the index keys into that alist, and ; otherwise to its value in acl2x-expansion-alist. ; We optimize for the common case that every key of acl2x-expansion-alist is a ; key of computed-expansion-alist. ; See the Essay on .acl2x Files (Double Certification). (cond ((atom computed-expansion-alist) ; see comment above acl2x-expansion-alist) ((acl2x-alistp-domains-subsetp acl2x-expansion-alist computed-expansion-alist) computed-expansion-alist) (t (merge-into-expansion-alist1 acl2x-expansion-alist computed-expansion-alist nil)))) (defun restrict-expansion-alist (index expansion-alist) ; Return the subsequence of expansion-alist that eliminates all indices smaller ; than index. It is assumed that expansion-alist has numeric keys in ascending ; order. (cond ((endp expansion-alist) nil) ((< (caar expansion-alist) index) (restrict-expansion-alist index (cdr expansion-alist))) (t expansion-alist))) (defun elide-locals-from-expansion-alist (alist acc) ; Call this function on an expansion-alist that was not created by provisional ; certification, and hence has already had elide-locals applied to encapsulate ; events (hence strongp=nil in the call below of elide-locals-rec). (cond ((endp alist) (reverse acc)) (t (elide-locals-from-expansion-alist (cdr alist) (cons (cons (caar alist) (mv-let (changedp form) (elide-locals-rec (cdar alist) nil) (declare (ignore changedp)) form)) acc))))) (defun write-port-file (full-book-name cmds ctx state) (let ((port-file (convert-book-name-to-port-name full-book-name))) (with-output-object-channel-sharing ch port-file (cond ((null ch) (er soft ctx "We cannot open file ~x0 for output." port-file)) (t (pprogn (io? event nil state (port-file) (fms "Note: Writing .port file, ~s0.~|" (list (cons #\0 port-file)) (proofs-co state) state nil)) (with-print-defaults ((current-package "ACL2") (print-circle (f-get-global 'print-circle-files state))) (pprogn (print-object$ '(in-package "ACL2") ch state) (print-objects ; We could apply hons-copy to cmds here, but we don't. See the ; Remark on Fast-alists in install-for-add-trip-include-book. cmds ch state) (close-output-channel ch state) (value port-file))))))))) (defmacro save-parallelism-settings (form) #-acl2-par form #+acl2-par `(state-global-let* ((waterfall-parallelism (f-get-global 'waterfall-parallelism state)) (waterfall-printing (f-get-global 'waterfall-printing state)) (total-parallelism-work-limit (f-get-global 'total-parallelism-work-limit state)) (total-parallelism-work-limit-error (f-get-global 'total-parallelism-work-limit-error state))) ,form)) (defun include-book-alist-equal-modulo-local (old-post-alist new-post-alist) ; This check is a stricter one than is done by include-book-alist-subsetp. It ; is appropriate for the Convert procedure of provisional certification, where ; old-post-alist comes from the .pcert0 file and new-post-alist results from ; the proof pass of the Convert procedure, since there is no reason for those ; two alists to differ (other than the fact that some members of the old ; post-alist were marked as local at the end of the include-book pass of the ; Pcertify procedure). (cond ((atom old-post-alist) (atom new-post-alist)) ((atom new-post-alist) nil) ((and (consp (car old-post-alist)) (eq (car (car old-post-alist)) 'local)) (and (equal (cadr (car old-post-alist)) (car new-post-alist)) (include-book-alist-equal-modulo-local (cdr old-post-alist) (cdr new-post-alist)))) ((equal (car old-post-alist) (car new-post-alist)) (include-book-alist-equal-modulo-local (cdr old-post-alist) (cdr new-post-alist))) (t nil))) (defun copy-object-channel-until-marker (marker ch-from ch-to state) (mv-let (eofp obj state) (read-object ch-from state) (cond ((or eofp (eq obj marker)) state) (t (pprogn (print-object$ obj ch-to state) (copy-object-channel-until-marker marker ch-from ch-to state)))))) (defun copy-pcert0-to-pcert1 (from to ctx state) ; Warning: The use of with-output-object-channel-sharing and ; with-print-defaults below should be kept in sync with analogous usage in ; make-certificate-file1. (mv-let (ch-from state) (open-input-channel from :object state) (cond ((null ch-from) (er soft ctx "Unable to open file ~x0 for input (to copy to file ~x1)." from to)) (t (with-output-object-channel-sharing ch-to to (with-print-defaults ((current-package "ACL2") (print-circle (f-get-global 'print-circle-files state))) (cond ((null ch-to) (pprogn (close-input-channel ch-from state) (er soft ctx "Unable to open file ~x0 for output (to copy ~ into from file ~x1)." to from))) (t (pprogn (copy-object-channel-until-marker :pcert-info ch-from ch-to state) (close-input-channel ch-from state) (close-output-channel ch-to state) (value :invisible)))))))))) (defun touch? (filename old-filename ctx state) ; If old-filename is present, then filename must exist and be at least as ; recent as old-filename in order to touch filename. ; The present implementation uses the Unix/Linux utility, "touch". Windows ; environments might or might not have this utility. If not, then a clean ; error should occur. It should be easy enough to create Windows-only code for ; this function, for example that copies filename to a temporary, deletes ; filename, and then moves the temporary to filename. ; Note: We should perhaps either require that the input filenames are as ; expected for the underlying OS, or else convert them with ; pathname-unix-to-os. But we see (March 2012) that file-write-date$ does not ; take care of this issue. So we will defer consideration of that issue here, ; especially since touch? already requires the Unix "touch" utility. (mv-let (old-filename-date state) (file-write-date$ old-filename state) (mv-let (filename-date state) (file-write-date$ filename state) (cond ((and old-filename-date filename-date (<= old-filename-date filename-date)) (prog2$ (sys-call "touch" (list filename)) (mv-let (status state) (sys-call-status state) (cond ((zerop status) (value nil)) (t (er soft ctx "Obtained non-zero exit status ~x0 ~ when attempting to touch file ~x0 ." status filename)))))) (t (value nil)))))) (defun convert-book-name-to-compiled-name (full-book-name state) ; The given full-book-name can either be a Unix-style or an OS-style pathname. (let ((rev-filename-list (reverse (coerce full-book-name 'list)))) (coerce (append (reverse (cddddr rev-filename-list)) (coerce (f-get-global 'compiled-file-extension state) 'list)) 'string))) (defun certify-book-finish-convert (new-post-alist old-post-alist full-book-name ctx state) ; Here we check that the post-alists correspond, as explained in the error ; message below. See also cert-obj-for-convert for a check on the pre-alists ; and portcullis commands and certify-book-fn for a check on the ; expansion-alists. (cond ((include-book-alist-equal-modulo-local old-post-alist new-post-alist) (let ((pcert0-name (convert-book-name-to-cert-name full-book-name :create-pcert)) (pcert1-name (convert-book-name-to-cert-name full-book-name :convert-pcert)) (compiled-name (convert-book-name-to-compiled-name full-book-name state))) (er-progn (copy-pcert0-to-pcert1 pcert0-name pcert1-name ctx state) ; Arrange that compiled file is not older than new certificate file. (touch? compiled-name pcert0-name ctx state) (value pcert1-name)))) (t (er soft ctx "Two sequences of included books unexpectedly differ: one from ~ the first pass of the Pcertify procedure, and one at the end ~ of the Convert procedure. Here is the include-book-alist as ~ of the end of the first pass of the Pcertify ~ procedure:~%~X02.~|~%And here is the include-book-alist at ~ the end of Convert procedure:~%~X12." old-post-alist new-post-alist nil)))) #-acl2-loop-only (defun delete-cert-files (full-book-name) (loop for cert-op in '(:create-pcert :convert-pcert t) do (let ((cert-file (pathname-unix-to-os (convert-book-name-to-cert-name full-book-name cert-op) *the-live-state*))) (when (probe-file cert-file) (delete-file cert-file))))) (defun include-book-alist-uncertified-books (alist acc state) ; Alist is a post-alist from a certificate file, which was constructed from the ; "proof" pass of certify-book, even if proofs were actually skipped in the ; Pcertify step of provisional certification. We use that alist to do a ; lightweight check for uncertified books, collecting all that we find. That ; check is simply that for each entry in the alist, the included sub-book from ; that entry (even if locally included) has a .cert file with a write date at ; least as recent as that sub-book. ; It is clear by induction on the tree of books that if no uncertified book is ; found this way, then assuming that all .cert files were created by ACL2 in ; the proper way, all books in the alist are indeed certified. (cond ((endp alist) (value acc)) (t (let* ((entry0 (car alist)) (entry (if (eq (car entry0) 'local) (cadr entry0) entry0)) (full-book-name (car entry)) (cert-name (convert-book-name-to-cert-name full-book-name t))) (mv-let (book-date state) (file-write-date$ full-book-name state) (mv-let (cert-date state) (file-write-date$ cert-name state) (include-book-alist-uncertified-books (cdr alist) (cond ((and book-date cert-date (<= book-date cert-date)) acc) (t (cons full-book-name acc))) state))))))) (defun count-forms-in-channel (ch state n) (mv-let (eofp state) (read-object-suppress ch state) (cond (eofp (mv n state)) (t (count-forms-in-channel ch state (1+ n)))))) (defun skip-forms-in-channel (n ch state) (cond ((zp n) (mv nil state)) (t (mv-let (eofp state) (read-object-suppress ch state) (cond (eofp (mv eofp state)) (t (skip-forms-in-channel (1- n) ch state))))))) (defun post-alist-from-pcert1-1 (n first-try-p pcert1-file msg ctx state) ; The post-alist is at zero-based position n or, if first-try-p is true, ; position n-2. (mv-let (chan state) (open-input-channel pcert1-file :object state) (cond ((null chan) (er soft ctx "~@0" msg)) (t (mv-let (eofp state) (skip-forms-in-channel n chan state) (cond (eofp ; How can this be? We just read n forms! (pprogn (close-input-channel chan state) (er soft ctx "Implementation error: Unexpected end of file, reading ~x0 forms ~ from file ~x1. Please contact the ACL2 implementors." n pcert1-file))) (t (mv-let (eofp post-alist state) (read-object chan state) (cond (eofp (er soft ctx "Implementation error: Unexpected end of file, reading ~x0 forms ~ and then one more form from file ~x1. Please contact the ACL2 ~ implementors." n pcert1-file)) ((eq post-alist :PCERT-INFO) ; then try again (pprogn (close-input-channel chan state) (cond (first-try-p (post-alist-from-pcert1-1 (- n 2) nil pcert1-file msg ctx state)) (t (er soft ctx "Implementation error: Unexpectedly we appear to have two ~ occurrences of :PCERT-INFO at the top level of file ~x0, ~ at positions ~x1 and ~x2." pcert1-file (+ n 2) n))))) (t (pprogn (close-input-channel chan state) (value post-alist)))))))))))) (defun post-alist-from-pcert1 (pcert1-file msg ctx state) (mv-let (chan state) (open-input-channel pcert1-file :object state) (cond ((null chan) (er soft ctx "~@0" msg)) (t (mv-let (len state) (count-forms-in-channel chan state 0) (pprogn (close-input-channel chan state) (assert$ (<= 2 len) ; len should even be at least 7 (post-alist-from-pcert1-1 (- len 2) t pcert1-file msg ctx state)))))))) (defun certificate-post-alist (pcert1-file cert-file full-book-name ctx state) (er-let* ((post-alist (post-alist-from-pcert1 pcert1-file (msg "Unable to open file ~x0 for input, hence cannot complete ~ its renaming to ~x1." pcert1-file cert-file) ctx state))) (cond ((equal (caar post-alist) full-book-name) (value post-alist)) (t (er soft ctx "Ill-formed post-alist encountered: expected its caar ~ to be the full-book-name ~x0, but the post-alist ~ encountered was ~x1." full-book-name post-alist))))) (defun certify-book-finish-complete (full-book-name ctx state) ; Wart: Perhaps we should convert compiled-file and expansion-file to OS-style ; pathnames in some places below, as for some other files. But we discovered ; this issue just before the Version_5.0 release, so we prefer not to do such a ; thing at this point. (let ((pcert0-file (convert-book-name-to-cert-name full-book-name :create-pcert)) (pcert1-file (convert-book-name-to-cert-name full-book-name :convert-pcert)) (cert-file (convert-book-name-to-cert-name full-book-name t)) (compiled-file (convert-book-name-to-compiled-name full-book-name state)) (expansion-file (expansion-filename full-book-name nil ; don't convert to OS, since we didn't above state))) (er-let* ((post-alist (certificate-post-alist pcert1-file cert-file full-book-name ctx state)) (uncertified-books (include-book-alist-uncertified-books (cdr post-alist) ; car is for full-book-name nil ; accumulator state))) (cond (uncertified-books (er soft ctx "Unable to complete the renaming of ~x0 to ~x1, because ~ ~#2~[~/each of ~]the following included book~#2~[~/s~] does not ~ have a .cert file that is at least as recent as that included ~ book: ~&2." pcert1-file cert-file uncertified-books)) (t #-acl2-loop-only (let ((pcert1-file-os (pathname-unix-to-os pcert1-file state)) (cert-file-os (pathname-unix-to-os cert-file state))) (when (probe-file cert-file-os) (delete-file cert-file-os)) (rename-file pcert1-file-os cert-file-os)) (pprogn (fms "Note: Renaming file ~x0 to ~x1.~|" (list (cons #\0 pcert1-file) (cons #\1 cert-file)) (standard-co state) state nil) (er-progn (touch? cert-file pcert0-file ctx state) (touch? compiled-file pcert0-file ctx state) (touch? expansion-file pcert0-file ctx state) (value cert-file)))))))) (defun expansion-alist-conflict (acl2x-expansion-alist elided-expansion-alist) ; Returns (mv bad-entry expected), where bad-entry is an entry in ; acl2x-expansion-alist that, when locally elided, does not correspond to an ; entry in elided-expansion-alist, either because its index does not exist in ; elided-expansion-alist -- in which case expected is nil -- or because the ; corresponding entry (i.e., with same index) in elided-expansion-alist differs ; from its local elision -- in which case expected is that corresponding entry. (cond ((endp acl2x-expansion-alist) (mv nil nil)) ((endp elided-expansion-alist) (mv (car acl2x-expansion-alist) nil)) ((< (caar acl2x-expansion-alist) (caar elided-expansion-alist)) (mv (car acl2x-expansion-alist) nil)) ((eql (caar acl2x-expansion-alist) (caar elided-expansion-alist)) (cond ((equal (mv-let (changedp val) (elide-locals-rec (cdar acl2x-expansion-alist) t) (declare (ignore changedp)) val) (cdar elided-expansion-alist)) (expansion-alist-conflict (cdr acl2x-expansion-alist) (cdr elided-expansion-alist))) (t (mv (car acl2x-expansion-alist) (car elided-expansion-alist))))) (t ; (< (caar elided-expansion-alist) (caar acl2x-expansion-alist)) (expansion-alist-conflict (cdr acl2x-expansion-alist) elided-expansion-alist)))) (defun chk-absstobj-invariants (extra-msg state) (declare (xargs :stobjs state ; If this were in :logic mode: ; :guard-hints (("Goal" :in-theory (enable read-acl2-oracle))) )) (er-let* ((msg #+acl2-loop-only (read-acl2-oracle state) #-acl2-loop-only (let ((temp (svref *inside-absstobj-update* 0))) (cond ((or (null temp) (eql temp 0)) (value nil)) (t (let ((msg (msg "Possible invariance violation for an abstract ~ stobj! See :DOC set-absstobj-debug, and ~ PROCEED AT YOUR OWN RISK.~@0" (cond ((natp temp) "") (t (msg " Evaluation was aborted under a call of ~ abstract stobj export ~x0.~@1" (cond ((symbolp temp) temp) (t (cdr (last temp)))) (cond ((symbolp temp) "") (t (msg " Moreover, it appears that ~ evaluation was aborted within the ~ following stack of stobj updater ~ calls (innermost call appearing ~ first): ~x0." (let ((y nil)) (loop (if (atom temp) (return (nreverse (cons temp y))) (push (pop temp) y))))))))))))) (pprogn (f-put-global 'illegal-to-certify-message msg state) (progn (setf (svref *inside-absstobj-update* 0) (if (natp temp) 0 nil)) (value msg))))))))) (cond (msg (er soft 'chk-absstobj-invariants "~@0~@1" msg (if extra-msg (msg " ~@0" extra-msg) ""))) (t (value nil))))) (defun certify-book-fn (user-book-name k compile-flg defaxioms-okp skip-proofs-okp ttags ttagsx ttagsxp acl2x write-port pcert state) (with-ctx-summarized (if (output-in-infixp state) (list* 'certify-book user-book-name (if (and (equal k 0) (eq compile-flg :default)) nil '(irrelevant))) (cons 'certify-book user-book-name)) (save-parallelism-settings (let ((wrld0 (w state))) (cond ((not (eq (caar wrld0) 'COMMAND-LANDMARK)) ; If we remove this restriction, then we need to change get-portcullis-cmds (at ; the least) so as not to look only for command markers. (er soft ctx "Certify-book can only be run at the top-level, either directly ~ in the top-level loop or at the top level of LD.")) ((and (stringp user-book-name) (let ((len (length user-book-name))) (and (<= 10 len) ; 10 = (length "@expansion") (equal (subseq user-book-name (- len 10) len) "@expansion")))) (er soft ctx "Book names may not end in \"@expansion\".")) ((not (booleanp acl2x)) ; also checked in certify-book guard (er soft ctx "The argument :ACL2X for certify-book must take on the value of T ~ or NIL. The value ~x0 is thus illegal. See :DOC certify-book." acl2x)) (t (er-let* ((pcert-env (cond ((eq pcert :default) (getenv! "ACL2_PCERT_ARG" state)) (t (value nil)))) (pcert (cond ((not pcert-env) (value (if (eq pcert :default) nil pcert))) ; For the remaining cases we know pcert-env is not nil, hence pcert = :default. ((string-equal pcert-env "T") (value t)) (t (value (intern (string-upcase pcert-env) "KEYWORD")))))) (mv-let (full-book-name directory-name familiar-name) (parse-book-name (cbd) user-book-name ".lisp" ctx state) (cond ((eq pcert :complete) (certify-book-finish-complete full-book-name ctx state)) (t (er-let* ((write-port (cond ((member-eq write-port '(t nil)) (value write-port)) ((eq write-port :default) (getenv! "ACL2_WRITE_PORT" state)) (t (er soft ctx "Illegal :write-port argument, ~x0. See ~ :DOC certify-book.")))) (write-acl2x (cond (acl2x (value (f-get-global 'write-acl2x state))) ((f-get-global 'write-acl2x state) (er soft ctx "Apparently set-write-acl2x has been ~ evaluated with argument value ~x0, yet ~ certify-book is being called without ~ supplying keyword argument :ACL2X T. ~ This is illegal. See :DOC ~ set-write-acl2x. If you do not intend to ~ write a .acl2x file, you may wish to ~ evaluate ~x1." (f-get-global 'write-acl2x state) '(set-write-acl2x nil state))) (t (value nil)))) (cert-op (cond ((and write-acl2x pcert) (er soft ctx "It is illegal to specify the ~ writing of a .acl2x file when a ~ non-nil value for :pcert (here, ~ ~x1) is specified~@0." pcert (cond (pcert-env " (even when the :pcert ~ argument is supplied, as ~ in this case, by an ~ environment variable)") (t "")))) (write-acl2x (value (if (consp write-acl2x) :write-acl2xu :write-acl2x))) (t (case pcert (:create (value :create-pcert)) (:convert (value :convert-pcert)) ((t) (value :create+convert-pcert)) ((nil) (value t)) (otherwise (er soft ctx "Illegal value of :pcert, ~ ~x0~@1. See :DOC ~ certify-book." pcert (cond (pcert-env (msg " (from environment ~ variable ~ ACL2_PCERT_ARG=~x0" pcert-env)) (t "")))))))) (skip-proofs-okp (value (cond ((eq skip-proofs-okp :default) (consp write-acl2x)) (t skip-proofs-okp)))) (uncertified-okp (value (consp write-acl2x))) (ttagsx (value (convert-non-nil-symbols-to-keywords (if ttagsxp ttagsx ttags)))) (ttags (cond ((and ttagsxp (not acl2x)) (er soft ctx "The :TTAGSX argument for ~ certify-book may only be supplied ~ if :ACL2X is T. See :DOC ~ set-write-acl2x.")) (t (chk-well-formed-ttags (convert-non-nil-symbols-to-keywords (cond (write-acl2x ttagsx) (t ttags))) (cbd) ctx state)))) (pair0 (chk-acceptable-ttags1 ; We check whether the ttags in the certification world are legal for the given ; ttags, and if so we refine ttags, as described in chk-acceptable-ttag1. (global-val 'ttags-seen wrld0) nil ; correct active-book-name, but irrelevant ttags nil ; irrelevant value for ttags-seen :quiet ; ttags in certif. world were already reported ctx state))) (state-global-let* ((certify-book-info (make certify-book-info :full-book-name full-book-name :cert-op cert-op)) (match-free-error nil) (defaxioms-okp-cert defaxioms-okp) (skip-proofs-okp-cert skip-proofs-okp) (guard-checking-on ; see Essay on Guard Checking nil)) (er-let* ((env-compile-flg (getenv! "ACL2_COMPILE_FLG" state)) (compile-flg (cond ((or (and env-compile-flg (string-equal env-compile-flg "ALL")) (eq compile-flg :all)) (value t)) ((or (eq cert-op :convert-pcert) (null (f-get-global 'compiler-enabled state))) (value nil)) ((not (eq compile-flg :default)) (value compile-flg)) ((or (null env-compile-flg) (string-equal env-compile-flg "T")) (value t)) ((string-equal env-compile-flg "NIL") (value nil)) (t (er soft ctx "Illegal value, ~x0, for environment ~ variable ACL2_COMPILE_FLG. The legal ~ values are (after converting to ~ uppercase) \"\", \"T\", \"NIL\", \"\", ~ and \"ALL\"." env-compile-flg)))) (saved-acl2-defaults-table (value (table-alist 'acl2-defaults-table (w state)))) ; If you add more keywords to this list, make sure you do the same to the list ; constructed by include-book-fn. (suspect-book-action-alist (value (list (cons :uncertified-okp uncertified-okp) (cons :defaxioms-okp defaxioms-okp) (cons :skip-proofs-okp skip-proofs-okp)))) (cert-obj ; The following call can modify (w state) by evaluating portcullis commands ; from an existing certificate file. (chk-acceptable-certify-book user-book-name full-book-name directory-name suspect-book-action-alist cert-op k ctx state)) (portcullis-cmds0 (value (access cert-obj cert-obj :cmds))) (ignore (cond (write-port (write-port-file full-book-name portcullis-cmds0 ctx state)) (t (value nil))))) (let* ((wrld1 ; from chk-acceptable-certify-book (w state)) (wrld1-known-package-alist (global-val 'known-package-alist wrld1)) (acl2x-file (convert-book-name-to-acl2x-name full-book-name)) (bad-chksum-str ; too wide to use in place "The file ~x0 is not a legal list of embedded event ~ forms because it contains an object, ~x1, that ~ check sum was unable to handle. This may be an ~ implementation error; feel free to contact the ~ ACL2 implementors.")) (pprogn (io? event nil state (full-book-name cert-op) (fms "CERTIFICATION ATTEMPT~@0 FOR ~x1~%~s2~%~%*~ ~ Step 1: Read ~x1 and compute its check sum.~%" (list (cons #\0 (case cert-op ((:write-acl2xu :write-acl2x) " (for writing .acl2x file)") (:create-pcert " (for writing .pcert0 file)") (:create+convert-pcert " (for writing .pcert0 and ~ .pcert1 files)") (:convert-pcert " (for writing .pcert1 file)") (t ""))) (cons #\1 full-book-name) (cons #\2 (f-get-global 'acl2-version state))) (proofs-co state) state nil)) (er-let* ((ev-lst (read-object-file full-book-name ctx state)) (acl2x-expansion-alist ; See the Essay on .acl2x Files (Double Certification). (cond (write-acl2x (value nil)) (t (read-acl2x-file acl2x-file full-book-name (length ev-lst) acl2x ctx state)))) (expansion-alist0 (cond ((eq cert-op :convert-pcert) (let ((elided-expansion-alist (access cert-obj cert-obj :expansion-alist))) (mv-let (bad-entry elided-entry) (expansion-alist-conflict acl2x-expansion-alist elided-expansion-alist) (cond (bad-entry (er soft ctx "The following expansion-alist ~ entry from file ~x0 is ~ unexpected:~|~x1~|~@2" acl2x-file bad-entry (cond (elided-entry (msg "It was expected to ~ correspond to the ~ following entry from the ~ :expansion-alist in file ~ ~x0:~|~x1" (convert-book-name-to-cert-name full-book-name :create-pcert) elided-entry)) (t "")))) (t (value (merge-into-expansion-alist (merge-into-expansion-alist elided-expansion-alist acl2x-expansion-alist) (access cert-obj cert-obj :pcert-info)))))))) (t (value acl2x-expansion-alist))))) (pprogn (print-certify-book-step-2 ev-lst expansion-alist0 (and (eq cert-op :convert-pcert) (convert-book-name-to-cert-name full-book-name :create-pcert)) acl2x-file state) (er-let* ((pass1-result (state-global-let* ((ttags-allowed (car pair0)) (user-home-dir ; We disallow ~/ in subsidiary include-book forms, because its meaning will be ; different when the superior book is included if the user changes (see :doc ; pathname). We do not make a similar binding in Step 3, because it calls ; include-book-fn and we do want to allow the argument to certify-book to start ; with ~/. Step 3 presumably doesn't call any include-book forms not already ; considered in Step 2, so this decision should be OK. nil) ; We will accumulate into the flag axiomsp whether any axioms have been added, ; starting with those in the portcullis. We can identify axioms in the ; portcullis by asking if the current nonconstructive axioms are different from ; those at the end of boot-strap. (axiomsp (not (equal (global-val ; axioms as of boot-strap 'nonconstructive-axiom-names (scan-to-landmark-number 'event-landmark (global-val 'event-number-baseline wrld1) wrld1)) (global-val ; axioms now 'nonconstructive-axiom-names wrld1)))) (ld-redefinition-action nil) (connected-book-directory directory-name)) (revert-world-on-error (er-let* ((portcullis-skipped-proofsp (value (and (global-val 'skip-proofs-seen (w state)) t))) (expansion-alist-and-index ; The fact that we are under 'certify-book means that all calls of ; include-book will insist that the :uncertified-okp action is nil, meaning ; errors will be caused if uncertified books are read. (process-embedded-events 'certify-book saved-acl2-defaults-table (or (eq cert-op :create-pcert) (and (consp write-acl2x) (car write-acl2x))) (cadr (car ev-lst)) (list 'certify-book full-book-name) (subst-by-position expansion-alist0 ; See the Essay on .acl2x Files (Double Certification). (cdr ev-lst) 1) 1 nil 'certify-book state)) (ignore (chk-absstobj-invariants "Your certify-book command ~ is therefore aborted." state)) (expansion-alist (value (cond (write-acl2x (assert$ ; disallowed pcert (null expansion-alist0) (car expansion-alist-and-index))) ((eq cert-op :convert-pcert) :irrelevant) ; not used (t (merge-into-expansion-alist expansion-alist0 (car expansion-alist-and-index))))))) (cond (write-acl2x (assert$ (not (eq cert-op :convert-pcert)) ; See the Essay on .acl2x Files (Double Certification). Below we will exit ; certify-book-fn, so the value returned here for pass1-result will be ; ignored. (write-acl2x-file expansion-alist acl2x-file ctx state))) (t (let ((expansion-alist (cond ((or (eq cert-op :create-pcert) (eq cert-op :convert-pcert)) ; The value here is irrelevant for :convert-pcert. We avoid eliding locals for ; :create-pcert (except when pcert = t, since then we are doing just what we ; would do for ordinary certification without pcert), hence we elide along the ; way); we'll take care of that later, after dealing with ; expansion-alist-pkg-names to support reading the unelided expansion-alist ; members from the .pcert0 file during the Convert procedure. expansion-alist) (t (elide-locals-from-expansion-alist expansion-alist nil))))) (value ; pass1-result: (list (or ; We are computing whether proofs may have been skipped. If k is a symbol with ; name "T", then we are using an existing certificate. If proofs were skipped ; during that previous certification, then perhaps they were skipped during ; evaluation of a portcullis command after setting ld-skip-proofsp to a non-nil ; value. So we are conservative here, being sure that in such a case, we set ; :SKIPPED-PROOFSP to T in the annotations for the present book. See the ; example in a comment in the deflabel note-5-0 pertaining to "Fixed a ; soundness bug based on the use of ~ilc[skip-proofs] ...." (and (symbol-name-equal k "T") cert-obj ; always true? (let ((cert-ann (cadddr (car (access cert-obj cert-obj :post-alist))))) (cdr (assoc-eq :SKIPPED-PROOFSP cert-ann)))) (let ((val (global-val 'skip-proofs-seen (w state)))) (and val ; Here we are trying to record whether there was a skip-proofs form in the ; present book or its portcullis commands, not merely on behalf of an included ; book. The post-alist will record such information for included books, and is ; consulted by skipped-proofsp-in-post-alist. See the comment about this ; comment in install-event. (not (eq (car val) :include-book))))) portcullis-skipped-proofsp (f-get-global 'axiomsp state) (global-val 'ttags-seen (w state)) (global-val 'include-book-alist-all (w state)) expansion-alist ; The next form represents the part of the expansion-alist that needs to be ; checked for new packages, in the sense described above the call below of ; expansion-alist-pkg-names. (let ((index (cdr expansion-alist-and-index))) (cond ((eq cert-op :convert-pcert) ; Presumably the packages defined in the portcullis commands of the .pcert0 ; file, as computed by chk-acceptable-certify-book1, are sufficient for reading ; the expansion-alist. nil) ((integerp index) (restrict-expansion-alist index expansion-alist)) (t expansion-alist))))))))))))) (cond (write-acl2x ; early exit (value acl2x-file)) (t (let* ((pass1-known-package-alist (global-val 'known-package-alist (w state))) (skipped-proofsp (nth 0 pass1-result)) (portcullis-skipped-proofsp (nth 1 pass1-result)) (axiomsp (nth 2 pass1-result)) (ttags-seen (nth 3 pass1-result)) (new-include-book-alist-all (nth 4 pass1-result)) (expansion-alist (nth 5 pass1-result)) (expansion-alist-to-check (nth 6 pass1-result)) (expansion-alist-pkg-names ; Warning: If the following comment is modified or deleted, visit its reference ; in expansion-alist-pkg-names. Also see the comments at the top of :doc ; note-3-2 for a discussion of this issue. ; We may need to create a defpkg in the certification world in order to read ; the expansion-alist from the certificate before evaluating events from the ; book. As long as there have been no new defpkg events since the end of the ; portcullis command evaluation, there is no problem. (Note that make-event-fn ; calls bad-lisp-objectp to check that the expansion is readable after ; evaluating the make-event call.) But once we get a new package, any ; subsequent form in the expansion-alist may need that new package to be ; defined in order for ACL2 to read the expansion-alist from the .cert file. ; Here we take the first step towards finding those packages. (expansion-alist-pkg-names expansion-alist-to-check wrld1-known-package-alist)) (cert-annotations (list ; We set :skipped-proofsp in the certification annotations to t or nil ; according to whether there were any skipped proofs in either the ; portcullis or the body of this book (not subbooks). (cons :skipped-proofsp skipped-proofsp) ; We similarly set :axiomsp to t or nil. As above, subbooks are not considered ; here. (cons :axiomsp axiomsp) (cons :ttags ttags-seen))) (post-alist1 new-include-book-alist-all)) (er-progn (chk-cert-annotations cert-annotations portcullis-skipped-proofsp portcullis-cmds0 full-book-name suspect-book-action-alist ctx state) (cond ((eq cert-op :convert-pcert) (let* ((chk-sum (check-sum-cert portcullis-cmds0 (access cert-obj cert-obj :expansion-alist) ev-lst)) (extra-entry (list* full-book-name user-book-name familiar-name cert-annotations chk-sum))) (certify-book-finish-convert (cons extra-entry post-alist1) (access cert-obj cert-obj :post-alist) full-book-name ctx state))) (t (pprogn ; Start include-book. (print-certify-book-step-3 state) (set-w 'retraction wrld1 state) #+(and gcl (not acl2-loop-only)) ; In GCL, object code (from .o files) may be stored in read-only memory, which ; is not collected by sgc. In particular, such code just loaded from ; include-book forms (during the admissibility check pass) is now garbage but ; may stay around awhile. Ultimately one would expect GCL to do a full garbage ; collect when relocating the hole, but by then it may have allocated many ; pages unnecessarily; and pages are never deallocated. By collecting garbage ; now, we may avoid the need to allocate many pages during this coming ; (include-book) pass of certification. ; However, it is far from clear that we are actually reclaiming the space we ; intend to reclaim. So we may want to delete this code. It seems to cost ; about 1/4 second per book certification for the ACL2 regression suite (as of ; 5/07). (progn (cond ((and (fboundp 'si::sgc-on) (funcall 'si::sgc-on)) (funcall 'si::sgc-on nil) (si::gbc t) (funcall 'si::sgc-on t)) (t (si::gbc t))) state) (with-hcomp-bindings compile-flg ; It may seem strange to call with-hcomp-bindings here -- after all, we call ; include-book-fn below, and we may think that include-book-fn will ultimately ; call load-compiled-book, which calls with-hcomp-bindings. However, when ; include-book-fn is called on behalf of certify-book, it avoids calling ; include-book-raw and hence avoids calling load-compiled-book; it processes ; events without first doing a load in raw Lisp. It is up to us to bind the ; *hcomp-xxx* variables here, so that add-trip can use them as it is processing ; events on behalf of the call below of include-book-fn, where ; *inside-include-book-fn* is 'hcomp-build. (mv-let (expansion-alist pcert-info) (cond ((eq cert-op :create-pcert) (elide-locals-and-split-expansion-alist expansion-alist acl2x-expansion-alist nil nil)) (t (mv expansion-alist (if (eq cert-op :create+convert-pcert) :proved nil)))) (er-let* ((defpkg-items (defpkg-items pass1-known-package-alist ctx wrld1 state)) (declaim-list (state-global-let* ((ld-redefinition-action nil)) ; Note that we do not bind connected-book-directory before calling ; include-book-fn, because it will bind it for us. We leave the directory set ; as it was when we parsed user-book-name to get full-book-name, so that ; include-book-fn will parse user-book-name the same way again. (er-progn (hcomp-build-from-portcullis (reverse (global-val 'top-level-cltl-command-stack wrld1)) state) (include-book-fn user-book-name state nil expansion-alist uncertified-okp defaxioms-okp skip-proofs-okp ttags-seen nil nil nil))))) (let* ((wrld2 (w state)) (new-defpkg-list (new-defpkg-list defpkg-items (delete-names-from-kpa expansion-alist-pkg-names (global-val 'known-package-alist wrld2)) wrld1-known-package-alist)) (new-fns (and (or (not (warning-disabled-p "Guards")) compile-flg) ; The test above is an optimization; we only need new-fns if the test holds. (newly-defined-top-level-fns wrld1 wrld2 full-book-name))) (os-expansion-filename (and compile-flg (expansion-filename full-book-name t state))) (post-alist2 (cdr (global-val 'include-book-alist wrld2)))) ; The cdr above removes the certification tuple stored by the include-book-fn ; itself. That pair is guaranteed to be the car because it is the most ; recently added one (with add-to-set-equal) and we know it was not already a ; member of the list because chk-acceptable-certify-book1 checked that. Could ; a file include itself? It could try. But if (include-book file) is one of ; the events in file, then the attempt to (include-book file) will cause ; infinite recursion -- because we don't put file on the list of files we've ; included (and hence recognize as redundant) until after we've completed the ; processing. (pprogn (mv-let (new-bad-fns all-bad-fns) (cond ((not (warning-disabled-p "Guards")) (mv (collect-ideals new-fns wrld2 nil) (collect-ideal-user-defuns wrld2))) (t (mv nil nil))) (cond ((or new-bad-fns all-bad-fns) (print-certify-book-guards-warning full-book-name new-bad-fns all-bad-fns k ctx state)) (t state))) (er-progn (chk-certify-book-step-3 post-alist2 post-alist1 ctx state) (state-global-let* ((connected-book-directory ; This binding is for the call of compile-certified-file below, though perhaps ; there will be other uses. directory-name)) (pprogn ; Write certificate. (print-certify-book-step-4 full-book-name os-expansion-filename cert-op state) (let* ((portcullis-cmds (append? portcullis-cmds0 new-defpkg-list)) (chk-sum (check-sum-cert portcullis-cmds expansion-alist ev-lst)) (extra-entry (list* full-book-name user-book-name familiar-name cert-annotations chk-sum))) (cond ((not (integerp chk-sum)) ; This really shouldn't happen! After all, we already called read-object-file ; above, which calls read-object, which calls chk-bad-lisp-object. (er soft ctx bad-chksum-str full-book-name chk-sum)) (t ; It is important to write the compiled file before installing the certificate ; file, since "make" dependencies look for the .cert file, whose existence ; should thus imply the existence of an intended compiled file. However, we ; need the compiled file to have a later write date (see load-compiled-book). ; So our approach if compile-flg is true is to write the certificate file ; first, but with a temporary name, and then move it to its final name after ; compilation (if any) has completed. (er-let* ((temp-alist (make-certificate-files full-book-name (cons portcullis-cmds (access cert-obj cert-obj :pre-alist)) (cons extra-entry post-alist1) (cons extra-entry post-alist2) expansion-alist pcert-info cert-op ctx state))) (er-progn (cond (compile-flg (pprogn (print-certify-book-step-5 full-book-name state) (er-progn (write-expansion-file portcullis-cmds declaim-list new-fns (expansion-filename full-book-name nil state) expansion-alist expansion-alist-pkg-names ev-lst pass1-known-package-alist ctx state) #-acl2-loop-only (progn (compile-certified-file os-expansion-filename full-book-name state) (when (not (f-get-global 'save-expansion-file state)) (delete-expansion-file os-expansion-filename state)) (value nil)) (value nil)))) (t #-acl2-loop-only (delete-auxiliary-book-files full-book-name) (value nil))) #-acl2-loop-only (progn ; Install temporary certificate file(s). (delete-cert-files full-book-name) (loop for pair in temp-alist do (rename-file (pathname-unix-to-os (car pair) state) (pathname-unix-to-os (cdr pair) state))) (value nil)) (pprogn (cond (expansion-alist0 ; Note that we are not in the Convert procedure. So we know that ; expansion-alist0 came from a .acl2x file, not a .pcert0 file. (observation ctx "Used expansion-alist ~ obtained from file ~ ~x0." acl2x-file)) (t state)) (value full-book-name)))))))))))))))))))))))))))))))))))))))) #+acl2-loop-only (defmacro certify-book (user-book-name &optional (k '0) (compile-flg ':default) &key (defaxioms-okp 'nil) (skip-proofs-okp ':default) (ttags 'nil) (ttagsx 'nil ttagsxp) (acl2x 'nil) (write-port ':default) (pcert ':default)) ":Doc-Section Books how to produce a ~il[certificate] for a book~/ ~bv[] Examples: (certify-book \"my-arith\") ; certify in a world with 0 commands (certify-book \"my-arith\" 3) ; ... in a world with 3 commands (certify-book \"my-arith\" ?) ; ... in a world without checking the ; number of commands (certify-book \"my-arith\" 0 nil) ; ... without compilation (certify-book \"my-arith\" 0 t) ; ... with compilation (default) (certify-book \"my-arith\" 0 t :ttags (foo)) ; ... allowing trust tag (ttag) foo (certify-book \"my-arith\" 0 t :ttags :all) ; ... allowing all trust tags (ttags) (certify-book \"my-arith\" t) ; ... from world of old certificate (certify-book \"my-arith\" 0 nil :acl2x t) ; ... writing or reading a .acl2x file~/ General Form: (certify-book book-name k ; [default 0] compile-flg ; [default t] :defaxioms-okp t/nil ; [default nil] :skip-proofs-okp t/nil ; [default nil] :ttags ttags ; [default nil] :acl2x t/nil ; [default nil] :ttagsx ttags ; [default nil] :write-port t/nil ; [default t] :pcert pcert ; [default nil] ) ~ev[] where ~c[book-name] is a book name (~pl[book-name]), ~c[k] is used to indicate your approval of the ``certification ~il[world],'' and ~c[compile-flg] can control whether the book is to be compiled. The defaults for ~c[compile-flg], ~c[skip-proofs-okp], ~c[acl2x], ~c[write-port], and ~c[pcert] can be affected by environment variables. All of these arguments are described in detail below, except for ~c[:pcert]. (We assume below that the value of ~c[:pcert] is ~c[nil] (and environment variable ~c[ACL2_PCERT_ARG] is unset or the empty string). For a discussion of this argument, ~pl[provisional-certification].) Certification occurs in some logical ~il[world], called the ``certification ~il[world].'' That ~il[world] must contain the ~ilc[defpkg]s needed to read and execute the forms in the book. The ~il[command]s necessary to recreate that ~il[world] from the ACL2 initial ~il[world] are called the ``~il[portcullis] commands,'' and will be copied into the ~il[certificate] created for the book. Those ~il[command]s will be re-executed whenever the book is included, to ensure that the appropriate packages (and all other names used in the certification ~il[world]) are correctly defined. The certified book will be more often usable if the certification ~il[world] is kept to a minimal extension of the ACL2 initial ~il[world] (for example, to prevent name clashes with functions defined in other books). Thus, before you call ~c[certify-book] for the first time on a book, you may wish to get into the initial ACL2 ~il[world] (e.g., with ~c[:ubt 1] or just starting a new version of ACL2), ~ilc[defpkg] the desired packages, and then invoke ~c[certify-book]. The ~c[k] argument to ~c[certify-book] must be either a nonnegative integer or else one of the symbols ~c[t] or ~c[?] in any package. If ~c[k] is an integer, then it must be the number of ~il[command]s that have been executed after the initial ACL2 ~il[world] to create the ~il[world] in which ~c[certify-book] was called. One way to obtain this number is by doing ~c[:pbt :start] to see all the ~il[command]s back to the first one. If ~c[k] is ~c[t] (or any symbol whose ~ilc[symbol-name] is ~c[\"T\"]), it means that ~c[certify-book] should use the same ~il[world] used in the last certification of this book. ~c[K] may have such a value only if you call ~c[certify-book] in the initial ACL2 ~il[world] and there is a ~il[certificate] on file for the book being certified. (Of course, the ~il[certificate] is probably invalid.) In this case, ~c[certify-book] reads the old ~il[certificate] to obtain the ~il[portcullis] ~il[command]s and executes them to recreate the certification ~il[world]. Finally, ~c[k] may be ~c[?] (or any symbol whose ~ilc[symbol-name] is ~c[\"?\"]), in which case there is no check made on the certification world. That is, if ~c[k] is such a value then no action related to the preceding two paragraphs is performed, which can be a nice convenience but at the cost of eliminating a potentially valuable check that the certification ~il[world] may be as expected. We next describe the meaning of ~c[compile-flg] and how it defaults. If explicit compilation has been suppressed by ~c[(set-compiler-enabled nil state)], then ~c[compile-flg] is coerced to ~c[nil]; ~pl[compilation]. Otherwise ~c[compile-flg] may be given the value of ~c[t] (or ~c[:all], which is equivalent to ~c[t] except during provisional certification; ~pl[provisional-certification]), indicating that the book is to be compiled, or else ~c[nil]. (Note that compilation initially creates a compiled file with a temporary file name, and then moves that temporary file to the final compiled file name obtained by adding a suitable extension to the book name. Thus, a compiled file will appear atomically in its intended location.) Finally, suppose that ~c[compile-flg] is not supplied (or is ~c[:default]). If environment variable ~c[ACL2_COMPILE_FLG] is defined and not the empty string, then its value should be ~c[T], ~c[NIL], or ~c[ALL] after converting to upper case, in which case ~c[compile-flg] is considered to have value ~c[t], ~c[nil], or ~c[:all] (respectively). Otherwise ~c[compile-flg] defaults to ~c[t]. Note that the value ~c[:all] is equivalent to ~c[t] except for during the Convert procedure of provisional certification; ~pl[provisional-certification]. Two keyword arguments, ~c[:defaxioms-okp] and ~c[:skip-proofs-okp], determine how the system handles the inclusion of ~ilc[defaxiom] events and ~ilc[skip-proofs] events, respectively, in the book. The value ~c[t] allows such events, but prints a warning message. The value ~c[nil] causes an error if such an event is found. ~c[Nil] is the default unless keyword argument ~c[:acl2x t] is provided and state global ~c['write-acl2x] is a cons (~pl[set-write-acl2x]), in which case the default is ~c[t]. The keyword argument ~c[:ttags] may normally be omitted. A few constructs, used for example if you are building your own system based on ACL2, may require it. ~l[defttag] for an explanation of this argument. When book ~c[B] is certified with value ~c[t] (the default) for keyword argument ~c[:write-port], a file ~c[B.port] is written by certification process. This file contains all of the ~il[portcullis] ~il[command]s for ~c[B], i.e., all user commands present in the ACL2 logical ~il[world] at the time ~c[certify-book] is called. if ~c[B.lisp] later becomes uncertified, say because ~il[events] from that file or an included book have been edited, then ~c[(include-book \"B\")] will consult ~c[B.port] to evaluate forms in that file before evaluating the events in ~c[B.lisp]. On the other hand, ~c[B.port] is ignored when including ~c[B] if ~c[B] is certified. If you use ~il[guard]s, please note ~c[certify-book] is executed as though ~c[(set-guard-checking nil)] has been evaluated; ~pl[set-guard-checking]. If you want guards checked, consider using ~c[ld] instead, or in addition; ~pl[ld]. For a general discussion of books, ~pl[books]. ~c[Certify-book] is akin to what we have historically called a ``proveall'': all the forms in the book are ``proved'' to guarantee their admissibility. More precisely, ~c[certify-book] (1) reads the forms in the book, confirming that the appropriate packages are defined in the certification ~il[world]; (2) does the full admissibility checks on each form (proving termination of recursive functions, proving theorems, etc.), checking as it goes that each form is an embedded event form (~pl[embedded-event-form]); (3) rolls the ~il[world] back to the initial certification ~il[world] and does an ~ilc[include-book] of the book to check for ~ilc[local] incompatibilities (~pl[local-incompatibility]); (4) writes a ~il[certificate] recording not only that the book was certified but also recording the ~il[command]s necessary to recreate the certification ~il[world] (so the appropriate packages can be defined when the book is included in other ~il[world]s) and the check sums of all the ~il[books] involved (~pl[certificate]); (5) compiles the book if so directed (and then loads the object file in that case). The result of executing a ~c[certify-book] ~il[command] is the creation of a single new event, which is actually an ~ilc[include-book] event. If you don't want its included ~il[events] in your present ~il[world], simply execute ~c[:]~ilc[ubt] ~c[:here] afterwards. A utility is provided to assist in debugging failures of ~c[certify-book]; ~pl[redo-flat].) ~c[Certify-book] requires that the default ~il[defun-mode] (~pl[default-defun-mode]) be ~c[:]~ilc[logic] when certification is attempted. If the mode is not ~c[:]~ilc[logic], an error is signalled. An error will occur if ~c[certify-book] has to deal with any uncertified book other than the one on which it was called. For example, if the book being certified includes another book, that subbook must already have been certified. If you have a certified book that has remained unchanged for some time you might well not remember the appropriate ~ilc[defpkg]s for it, though they are stored in the ~il[certificate] file and (by default) also in the ~c[.port] file. If you begin to change the book, don't throw away its ~il[certificate] file just because it has become invalid! It is an important historical document until the book is re-certified. More important, don't throw away the ~c[.port] file, as it will provide the ~il[portcullis] commands when including the book as an uncertified book; ~pl[include-book]. When ~c[certify-book] is directed to produce a compiled file, it calls the Common Lisp function ~c[compile-file] on the original source file. This creates a compiled file with an extension known to ACL2, e.g., if the book is named ~c[\"my-book\"] then the source file is ~c[\"my-book.lisp\"] and the compiled file under GCL will be ~c[\"my-book.o\"] while under SBCL it will be ~c[\"my-book.fasl\"]. The compiled file is then loaded. When ~ilc[include-book] is used later on ~c[\"my-book\"] it will automatically load the compiled file, provided the compiled file has a later write date than the source file. The only effect of such ~il[compilation] and loading is that the functions defined in the book execute faster. ~l[guard] for a discussion of the issues, and if you want more details about ~il[books] and compilation, ~pl[book-compiled-file]. When ~c[certify-book] is directed not to produce a compiled file, it will delete any existing compiled file for the book, so as not to mislead ~ilc[include-book] into loading the now outdated compiled file. Otherwise, ~c[certify-book] will create a temporary ``expansion file'' to compile, obtained by appending the string \"@expansion.lsp\" to the end of the book name. Remark: Users may ignore that file, which is automatically deleted unless ~il[state] global variable ~c['save-expansion-file] has been set, presumably by a system developer, to a non-~c[nil] value; ~pl[book-compiled-file] for more information about hit issue, including the role of environment variable ~c[ACL2_SAVE_EXPANSION]. After execution of a ~c[certify-book] form, the value of ~ilc[acl2-defaults-table] is restored to what it was immediately before that ~c[certify-book] form was executed. ~l[acl2-defaults-table]. Those who use the relatively advanced features of trust tags (~pl[defttag]) and ~ilc[make-event] may wish to know how to create a ~il[certificate] file that avoids dependence on trust tags that are used only during ~ilc[make-event] expansion. For this, including documentation of the ~c[:acl2x] and ~c[:ttagsx] keyword arguments for ~c[certify-book], ~pl[set-write-acl2x]. This completes the tour through the ~il[documentation] of ~il[books].~/ :cited-by other :cited-by Programming" (declare (xargs :guard (and (booleanp acl2x) (member-eq compile-flg '(nil t :all ; We allow :default as a way for generated certify-book commands to specify ; explicitly that they take compile-flg from environment variable ; ACL2_COMPILE_FLG. :default))))) (list 'certify-book-fn (list 'quote user-book-name) (list 'quote k) (list 'quote compile-flg) (list 'quote defaxioms-okp) (list 'quote skip-proofs-okp) (list 'quote ttags) (list 'quote ttagsx) (list 'quote ttagsxp) (list 'quote acl2x) (list 'quote write-port) (list 'quote pcert) 'state)) (defmacro certify-book! (user-book-name &optional (k '0) (compile-flg 't compile-flg-supplied-p) &rest args) (declare (xargs :guard (and (integerp k) (<= 0 k)))) ":Doc-Section Other a variant of ~ilc[certify-book]~/ ~bv[] Examples: (certify-book! \"my-arith\" 3) ;Certify in a world with 3 ; commands, starting in a world ; with at least 3 commands. (certify-book! \"my-arith\") ;Certify in the initial world.~/ General Form: (certify-book! book-name k compile-flg) ~ev[] where ~c[book-name] is a book name (~pl[book-name]), ~c[k] is a nonnegative integer used to indicate the ``certification ~il[world],'' and ~c[compile-flg] indicates whether you wish to compile the (functions in the) book. This ~il[command] is identical to ~ilc[certify-book], except that the second argument ~c[k] may not be ~c[t] in ~c[certify-book!] and if ~c[k] exceeds the current ~il[command] number, then an appropriate ~ilc[ubt!] will be executed first. ~l[certify-book] and ~pl[ubt!].~/" `(er-progn (ubt! ,(1+ k)) ,(if compile-flg-supplied-p `(certify-book ,user-book-name ,k ,compile-flg ,@args) `(certify-book ,user-book-name ,k)))) (defdoc provisional-certification ; Here we put random remarks about provisional certification that are not fully ; covered in the :doc topic (which serves as a prerequisite for these remarks). ; Let us consider the :pcert-info field of a cert-obj, which comes from the ; :pcert-info field of a .pcert0 file. The value is :PROVED when the .pcert0 ; file was created by the Pcertify+ procedure, but now we consider the value ; when the .pcert0 file was created by the Pcertify procedure. The value is an ; expansion-alist that associates indices with full expansions, before applying ; elide-locals or elide-locals-rec. When there is no such elision in the ; regular :expansion-alist, no entry is made in the :pcert-info field. We do ; not elide locals during the Pcertify procedure; then when writing out the ; .pcert0 file, we use a strong version of ; elide-locals-and-split-expansion-alist (essentially, a strong version of ; elide-locals-from-expansion-alist) that dives inside encapsulates to elide ; locals. Note that the :pcert-info field does not contribute to the checksum ; of a cert-obj (see check-sum-cert-obj and check-sum-cert), the reason being ; that we want the computed checksums not to change between a .pcert0 file and ; the .pcert1 file derived from it. ; Next, we discuss issues related to include-book and provisionally certified ; books. ; ACL2 users sometimes arrange to include books to build a certification world, ; as a precursor to a call of certify-book. When using provisional ; certification, the .cert files might not exist for those books. Of course, ; we allow .pcert0 and .pcert1 files to serve as certificate files when inside ; a call of certify-book with a :pcert argument of :create or :convert. But ; the include-book forms mentioned above are executed before calling ; certify-book. How can we support the use of .pcert0 and .pcert1 files in ; such cases? ; Our solution is to allow .pcert0 and .pcert1 files to serve as certificate ; files in all circumstances, with the following two kinds of special handling. ; First, and most important, we do the following two things: (a) we make a note ; in the world (in world global pcert-books) when cert-op is not :create-pcert, ; :create+convert-pcert or :convert-pcert, and an include-book has taken place ; that uses a .pcert0 or .pcert1 file as a certificate, and (b) we disallow ; certify-book in any such world unless the :pcert argument has value :create, ; t, or :convert (where this argument might, of course, come from environment ; variable ACL2_PCERT_ARG). Second, we print a warning when using a .pcert0 or ; .pcert1 file unless we are in an environment with ACL2_PCERT set to a ; non-empty string. (Of course, that warning may also be inhibited by ; set-inhibit-warnings or set-inhibit-output-lst.) ":Doc-Section Books certify a book in stages for improved parallelism~/ Provisional certification is a process that can increase parallelism at the system level, typically using `make', when certifying a collection of ~il[books]. We got this idea from Jared Davis, who developed rudimentary provisional certification schemes first at Rockwell Collins and later for his `Milawa' project. Our design has also benefited from conversations with Sol Swords. To invoke provisional certification, ~pl[books-certification]. For example, you could issue the following command. ~bv[] ACL2_PCERT=t cert.pl -j 4 `find . -name '*.lisp'` ~ev[] Alternatively, ~pl[books-certification-classic] for a discussion of classic ACL2 `make'-based certification (which may disappear in a future ACL2 release); here we extend those instructions to show how to use provisional certification. (Also, you may wish to look at community books file ~c[books/system/pcert/Makefile] for an example.) We begin by describing a few ways to do that. A simple way is to add the line `~c[ACL2_PCERT=t]' to a `make' command that you use for book certification, for example as follows. ~bv[] make -j 4 ACL2_PCERT=t ~ev[] The following works too, in a bash shell. ~bv[] (export ACL2_PCERT=t ; time make -j 4) ~ev[] Alternatively, add the line ~bv[] ACL2_PCERT ?= t ~ev[] to the ~c[Makefile] residing in the directory, placed above the line that specifies the `~c[include]' of file ~c[Makefile-generic]. A successful `make' will result in creating the desired ~il[certificate] (~c[.cert]) files. Warning: If you put the line ``~c[ACL2_PCERT ?= t]'' below the include of ~c[Makefile-generic], it might have no effect. For example, try editing community books file ~c[books/system/pcert/Makefile] by moving the line ``~c[ACL2_PCERT ?= t]'' to the bottom of the file, and watch ``~c[make top.cert]'' fail to invoke provisional certification. The description above may be sufficient for you to use provisional certification. We provide additional documentation below for the reader who wants to understand more about this process, for example when not using `make'. Below we assume prior familiarity with ~il[books], in particular ~ilc[certify-book] and ~ilc[include-book]. The remainder of this ~il[documentation] topic is divided into sections: Summary, Correctness Claim and Issues, Combining Pcertify and Convert into Pcertify+, and Further Information.~/ ~st[Summary] Recall that certification of a book, ~c[bk], produces a ~il[certificate] file ~c[bk.cert]. The provisional certification process produces this file as well, but as the last of the following three steps. All of these steps are carried out by calls of ~ilc[certify-book] using its ~c[:pcert] keyword argument. We typically call these steps ``procedures'', to distinguish them from the steps of an individual call of ~ilc[certify-book]. ~bq[] o The ``Pcertify'' procedure (sometimes called the ``Create'' procedure) is invoked by calling ~ilc[certify-book] with keyword argument ~c[:pcert :create]. It produces a file ~c[bk.pcert0], sometimes called the ``~c[.pcert0]'' file (pronounced ``dot pee cert zero''). Proofs are skipped during this procedure, which can be viewed as an elaborate syntax check, augmented by compilation if specified (as it is by default). o The ``Convert'' procedure is invoked by calling ~ilc[certify-book] with keyword argument ~c[:pcert :convert]. It creates file ~c[bk.pcert1] from ~c[bk.pcert0], by doing proofs for all events in ~c[bk.lisp]. Note that the third argument (the `~c[compile-flg]' argument) is ignored by such a call of ~c[certify-book] unless its value is ~c[:all] (either explicitly or by way of environment variable ~c[ACL2_COMPILE_FLG]). A subtlety is that if there is a compiled file at least as recent as the corresponding ~c[.pcert0] file, then that compiled file's write date will be updated to the current time at the end of this procedure. The usual ~il[local-incompatibility] check at the end of ~ilc[certify-book] is omitted for the Convert procedure, since it was performed towards the end of the Create procedure. o The ``Complete'' procedure is invoked by calling ~ilc[certify-book] with keyword argument ~c[:pcert :complete]. It checks that every included book (including every one that is ~ilc[local]ly included) has a ~c[.cert] file that is at least as recent as the corresponding book. The effect is to move ~c[bk.pcert1] to ~c[bk.cert]. Note that all arguments of ~c[certify-book] other than the ~c[:pcert] argument are ignored for this procedure, other than for some trivial argument checking.~eq[] You can combine the Pcertify and Convert procedures into a single procedure, Pcertify+, which may be useful for books that contain expensive ~ilc[include-book] ~il[events] but do few proofs. We defer discussion of that feature to the section below, ``Combining Pcertify and Convert into Pcertify+''. The main idea of provisional certification is to break sequential dependencies caused by ~ilc[include-book], that is, so that a book's proofs are carried out even when it includes books (sometimes called ``sub-books'') that have not themselves been fully certified. For example, suppose that a proof development consists of books ~c[A.lisp], ~c[B.lisp], and ~c[C.lisp], where file ~c[A.lisp] contains the form ~c[(include-book \"B\")] and file ~c[B.lisp] contains the form ~c[(include-book \"C\")]. Normally one would first certify ~c[C], then ~c[B], and finally, ~c[A]. However, the provisional certification process can speed up the process on a multi-core machine, as follows: the Pcertify (pronounced ``pee certify'') procedure respects this order but (one hopes) is fast since proofs are skipped; the Convert procedure essentially completes the certification in parallel by doing proofs and creating ~c[.pcert1] files based on ~c[.pcert0] files; and the Complete procedure respects book order when quickly renaming ~c[.pcert1] files to ~c[.cert] files. In our example, the steps would be as follows, but note that we need not finish all Pcertify steps before starting some Convert steps, nor need we finish all Convert steps before starting some Complete steps, as explained further below. ~bq[] o Pcertify books \"C\", \"B\",and then \"A\", sequentially, skipping proofs but doing compilation. o Do proofs in parallel for the books using the Convert procedure, where each book relies on the existence of its own ~c[.pcert0] file as well as a ~c[.cert], ~c[.pcert0], or ~c[.pcert1] file for each of its included sub-books. Write out a ~c[.pcert1] file for the book when the proof succeeds. o Rename the ~c[.pcert1] file to a corresponding ~c[.cert] file when a ~c[.cert] file exists and is up-to-date for each included book.~eq[] The Convert step can begin for ~c[bk.lisp] any time after ~c[bk.pcert0] is built. The Complete step can begin for this book any time after ~c[bk.pcert1] and every ~c[sub-bk.cert] are built, for ~c[sub-bk] a sub-book of ~c[bk]. The new procedures ~-[] Pcertify, Convert, and Complete ~-[] are invoked by supplying a value for the keyword argument ~c[:pcert] of ~ilc[certify-book], namely ~c[:create], ~c[:convert], or ~c[:complete], respectively. Typically, and by default, the ~c[compile-flg] argument of ~ilc[certify-book] is ~c[t] for the Pcertify procedure, so that ~il[compilation] can take full advantage of parallelism. This argument is treated as ~c[nil] for the other procedures except when its value is ~c[:all] in the Convert procedure, as mentioned above. For those who use ~ilc[make-event], we note that expansion is done in the Pcertify procedure; the later steps use the expansion resulting from that procedure. The reason is that although a call of ~ilc[make-event] is similar to a macro call, a difference is that the expansion of a ~c[make-event] form can depend on the ~il[state]. Therefore, we insist on doing such an expansion only once, so that all books involved agree on the expansion. We say more about ~c[make-event] below. ~st[Correctness Claim and Issues] The Basic Claim for certification is the same whether or not the provisional certification process is employed: all books should be certified from scratch, with no files written to the directories of the books except by ACL2. Moreover, no trust tags should be used (~pl[defttag]), or else it is the responsibility of the user to investigate every occurrence of ``~c[TTAG NOTE]'' that is printed to standard output. But common practice is to certify a set of books in stages: certify a few books, fix some books, re-certify changed books, certify some more books, and so on. In practice, we expect this process to be sound even though it does not meet the preconditions for the Basic Claim above. In particular, we expect that the use of checksums in ~il[certificate]s will make it exceedingly unlikely that a book is still treated as certified after any events in the book or any sub-book, or any ~il[portcullis] ~il[command]s of the book or any sub-book, have been modified. Provisional certification makes it a bit easier for a determined user to subvert correctness. For example, the Complete procedure only checks write dates to ensure that each sub-book's ~c[.cert] file is no older than the corresponding ~c[.lisp] file, but it does not look inside ~c[.cert] files of sub-books; in particular it does not look at their checksum information. Of course, the automatic dependency analysis provided by classic ACL2 `make'-based certification avoids accidental problems of this sort. And, checksum information will indeed be applied at ~ilc[include-book] time, at least for sub-books included non-~il[local]ly. In short: while we believe that the provisional certification process can be trusted, we suggest that for maximum trust, it is best for all books in a project to be certified from scratch without the provisional certification process. ~st[Combining Pcertify and Convert into Pcertify+] You can combine the Pcertify and Convert procedure into a single procedure, Pcertify+, which may be useful for books that contain expensive ~ilc[include-book] ~il[events] but do few proofs. If you are using `make' to do provisional certification as described above, just set `make' variable ~c[ACL2_BOOKS_PCERT_ARG_T] to the list of books for which you want the Pcertify+ procedure performed instead of separate Pcertify and Convert procedures. Either of two common methods may be used to set this variable, as illustrated below for the case that books ~c[sub.lisp] and ~c[mid.lisp] are the ones on which you want Pcertify+ performed. One method is to add the following to your directory's Makefile, above the ~c[include] of ~c[Makefile-generic]. ~bv[] ACL2_BOOKS_PCERT_ARG_T = sub mid ~ev[] Alternatively, you can specify the desired books on the command line, for example as follows. ~bv[] make -j 4 ACL2_BOOKS_PCERT_ARG_T='sub mid' ~ev[] Note that the books are given without their ~c[.lisp] extensions. At the ACL2 level, the Pcertify+ procedure is performed when the value ~c[t] is supplied to the ~c[:pcert] keyword argument of ~ilc[certify-book]. Thus, ~c[:pcert t] can be thought of as a combination of ~c[:pcert :create] and ~c[:pcert :convert]. However, what ACL2 actually does is to perform the Pcertify step without skipping proofs, and at the end of the ~c[certify-book] run, it writes out both the ~c[.pcert0] and ~c[.pcert1] file, with essentially the same contents. (We say ``essentially'' because the implementation writes ~c[:PCERT-INFO :PROVED] to the end of the ~c[.pcert0] file, but not to the ~c[.pcert1] file.) ~st[Further Information] Some errors during provisional certification cannot be readily solved. For example, if there are circular directory dependencies (for example, some book in directory D1 includes some book in directory D2 and vice-versa), then classic ACL2 `make'-based certification will quite possibly fail. For another example, perhaps your directory's Makefile is awkward to convert to one with suitable dependencies. When no fix is at hand, it might be best simply to avoid provisional certification. If you are using classic ACL2 `make'-based certification, you can simply add the following line to your directory's ~c[Makefile], or use ``~c[ACL2_PCERT= ]'' on the `make' command line, to avoid provisional certification. ~bv[] override ACL2_PCERT = ~ev[] We invite anyone who has troubleshooting tips to contact the ACL2 developers with suggestions for adding such tips to this section. Our next remark is relevant only to users of ~ilc[make-event], and concerns the interaction of that utility with state global ~ilc[ld-skip-proofsp]. Normally, the global value of ~ilc[ld-skip-proofsp] is unchanged during ~c[make-event] expansion, except that it is bound to ~c[nil] when the ~c[make-event] form has a non-~c[nil] ~c[:check-expansion] argument. But during the Pcertify procedure (not the Pcertify+ procedure), ~ilc[ld-skip-proofsp] is always bound to ~c[nil] at the start of ~c[make-event] expansion. To see why, consider for example the community book ~c[books/make-event/proof-by-arith.lisp]. This book introduces a macro, ~c[proof-by-arith], that expands to a call of ~ilc[make-event]. This ~c[make-event] form expands by trying to prove a given theorem using a succession of included arithmetic books, until the proof succeeds. Now proofs are skipped during the Pcertify procedure, and if proofs were also skipped during ~c[make-event] expansion within that procedure, the first arithmetic book's ~ilc[include-book] form would always be saved because the theorem's proof ``succeeded'' (as it was skipped!). Of course, the theorem's proof could then easily fail during the Convert step. If you really want to inhibit proofs during ~c[make-event] expansion in the Pcertify step, consider using a form such as the following: ~c[(state-global-let* ((ld-skip-proofsp nil)) ...)]. Finally, we describe what it means for there to be a valid ~il[certificate] file for including a certified book. Normally, this is a file with extension ~c[.cert]. However, if that ~c[.cert] file does not exist, then ACL2 looks for a ~c[.pcert0] file instead; and if that also does not exist, it looks for a ~c[.pcert1] file. (To see why does the ~c[.pcert0] file take priority over the ~c[.pcert1] file, note that the Convert procedure copies a ~c[.pcert0] file to a ~c[.pcert1] file, so both might exist ~-[] but the ~c[.pcert1] file might be incomplete if copying is in progress.) Once the candidate certificate file is thus selected, it must be valid in order for the book to be considered certified (~pl[certificate]). For the certificate file as chosen above, then in order for a compiled file to be loaded, it must be at least as recent as that certificate file. Again, as discussed above, a ~c[.pcert0] or ~c[.pcert1] file may serve as a valid certificate file when the ~c[.cert] file is missing. But when that happens, a warning may be printed that a ``provisionally certified'' book has been included. No such warning occurs if environment variable ~c[ACL2_PCERT] has a non-empty value, or if that warning is explicitly inhibited (~pl[set-inhibit-warnings] and ~pl[set-inhibit-output-lst]).") (deflabel pathname :doc ":Doc-Section acl2::Books introduction to filename conventions in ACL2~/ The notion of pathname objects from Common Lisp is not supported in ACL2, nor is the function ~c[pathname]. However, ACL2 supports file operations, using conventions for naming files based on those of the Unix (trademark of AT&T) operating system, so that the character ~c[/] is used to terminate directory names. Some file names are ``absolute'' (complete) descriptions of a file or directory; others are ``relative'' to the current working directory or to the connected book directory (~pl[cbd]). We emphasize that even for users of Windows-based systems or Macintosh computers, ACL2 file names are in the Unix style. We will call these ~em[ACL2 pathnames], often omitting the ``ACL2.''~/ Pathnames starting with the directory separator (~c[/]) or the tilde character (~c[~~]) are absolute pathnames. All other pathnames are relative pathnames. An exception is in the Microsoft Windows operating system, where it is illegal for the pathname to start with a tilde character but the drive may be included, e.g., ~c[\"c:/home/smith/acl2/book-1.lisp\"]. In fact, the drive ~em[must] be included in the portcullis of a book; ~pl[portcullis]. Note also that some host Common Lisps will not support pathnames starting with ~c[\"~~\"], for example ~c[~~smith], though ACL2 will generally support those starting with ~c[\"~~/\"] regardless of the host Common Lisp. Consider the following examples. The filename string ~bv[] \"/home/smith/acl2/book-1.lisp\" ~ev[] is an absolute pathname, with top-level directory ~c[\"home\"], under that the directory ~c[\"smith\"] and then the directory ~c[\"acl2\"], and finally, within that directory the file ~c[\"book-1.lisp\"]. If the connected book directory is ~c[\"/home/smith/\"] (~pl[cbd]), then the filename string above also corresponds to the relative filename string \"acl2/book1.lisp\". Finally, we note that (on non-Windows systems) the pathname ~c[\"~~\"] and pathnames starting with ~c[\"~~/\"] are illegal in books being certified. Otherwise, a subsidiary ~ilc[include-book] form would have a different meaning at certification time than at a later time when the certified book is included by a different user.~/") (deflabel book-example :Doc ":Doc-Section Books how to create, certify, and use a simple book~/ Suppose you have developed a sequence of admissible ~il[events] which you want to turn into a book. We call this ``publishing'' the book. This note explains how to do that.~/ A key idea of ~il[books] is that they are ``incremental'' in the sense that when you include a book in a host logical ~il[world], the ~il[world] is incrementally extended by the results established in that book. This is allowed only if every name defined by the incoming book is either new or is already identically defined. ~l[redundant-events]. This is exactly the same problem faced by a programmer who wishes to provide a utility to other people: how can he make sure he doesn't create name conflicts? The solution, in Common Lisp, is also the same: use packages. While ~il[books] and packages have a very tenuous formal connection (every book must start with an ~ilc[in-package]), the creation of a book is intimately concerned with the package issue. Having motivated what would otherwise appear as an unnecessary fascination with packages below, we now proceed with a description of how to publish a book. Just to be concrete, let's suppose you have already gotten ACL2 to accept the following sequence of ~il[command]s, starting in the ACL2 initial ~il[state]. ~bv[] (defpkg \"ACL2-MY-BOOK\" (union-eq *common-lisp-symbols-from-main-lisp-package* *acl2-exports*)) (in-package \"ACL2-MY-BOOK\") (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y)) (defun rev (x) (if (consp x) (app (rev (cdr x)) (list (car x))) nil)) (defthm rev-app-hack (equal (rev (app a (list x))) (cons x (rev a)))) (defthm rev-rev (implies (acl2::true-listp x) (equal (rev (rev x)) x))) ~ev[] Observe that the first form above defines a package (which imports the symbols defined in CLTL such as ~ilc[if] and ~ilc[cons] and the symbols used to ~il[command] ACL2 such as ~ilc[defun] and ~ilc[defthm]). The second form selects that package as the current one. All subsequent forms are read into that package. The remaining forms are just event forms: ~ilc[defun]s and ~ilc[defthm]s in this case. Typically you would have created a file with Emacs containing these forms and you will have submitted each of them interactively to ACL2 to confirm that they are all admissible. That interactive verification should start in ACL2's initial ~il[world] ~-[] although you might, of course, start your sequence of ~il[events] with some ~ilc[include-book]s to build a more elaborate ~il[world]. The first step towards publishing a book containing the results above is to create a file that starts with the ~ilc[in-package] and then contains the rest of the forms. Let's call that file ~c[\"my-book.lisp\"]. The name is unimportant, except it must end with ~c[\".lisp\"]. If there are ~il[events] that you do not wish to be available to the user of the book ~-[] e.g., lemmas you proved on your way toward proving the main ones ~-[] you may so mark them by enclosing them in ~ilc[local] forms. ~l[local]. Let us suppose you wish to hide ~c[rev-app-hack] above. You may also add standard Lisp comments to the file. The final content of ~c[\"my-book.lisp\"] might be: ~bv[] ; This book contains my app and rev functions and the theorem ; that rev is its own inverse. (in-package \"ACL2-MY-BOOK\") (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y)) (defun rev (x) (if (consp x) (app (rev (cdr x)) (list (car x))) nil)) ; The following hack is not exported. (local (defthm rev-app-hack (equal (rev (app a (list x))) (cons x (rev a))))) (defthm rev-rev (implies (acl2::true-listp x) (equal (rev (rev x)) x))) ~ev[] The file shown above ~st[is] the book. By the time this note is done you will have seen how to certify that the book is correct, how to compile it, and how to use it in other host ~il[world]s. Observe that the ~ilc[defpkg] is not in the book. It cannot be: Common Lisp compilers disagree on how to treat new package definitions appearing in files to be compiled. Since a book is just a source file typed by the user, ACL2 provides a mechanism for checking that the ~il[events] are all admissible and then marking the file as checked. This is called certification. To certify ~c[\"my-book.lisp\"] you should first get into ACL2 with an initial ~il[world]. Then, define the package needed by the book, by typing the following ~ilc[defpkg] to the ACL2 ~il[prompt]: ~bv[] ACL2 !>(defpkg \"ACL2-MY-BOOK\" (union-eq *common-lisp-symbols-from-main-lisp-package* *acl2-exports*)) ~ev[] Then execute the ~il[command]: ~bv[] ACL2 !>(certify-book \"my-book\" 1 t) ; the `t' is in fact the default ~ev[] Observe that you do not type the ~c[\".lisp\"] part of the file name. For purposes of ~il[books], the book's name is ~c[\"my-book\"] and by the time all is said and done, there will be several extensions in addition to the ~c[\".lisp\"] extension associated with it. The ~c[1] tells ~ilc[certify-book] that you acknowledge that there is one command in this ``certification ~il[world]'' (namely the ~ilc[defpkg]). To use the book, any prospective host ~il[world] must be extended by the addition of whatever ~il[command]s occurred before certification. It would be a pity to certify a book in a ~il[world] containing junk because that junk will become the ``~il[portcullis]'' guarding entrance to the book. The ~c[t] above tells ~ilc[certify-book] that you wish to compile ~c[\"my-book.lisp\"] also (but ~pl[compilation] for an exception). ~ilc[Certify-book] makes many checks but by far the most important and time-consuming one is that it ``proves'' every event in the file. When ~ilc[certify-book] is done it will have created two new files. The first will be called ~c[\"my-book.cert\"] and contains the ``~il[certificate]'' attesting to the admissibility of the ~il[events] in ~c[\"my-book.lisp\"]. The ~il[certificate] contains the ~ilc[defpkg] and any other forms necessary to construct the certification ~il[world]. It also contains various check sums used to help you keep track of which version of ~c[\"my-book.lisp\"] was certified. The second file that may be created by ~ilc[certify-book] is the compiled version of ~c[\"my-book.lisp\"] and will have a name that is assigned by the host compiler (e.g., ~c[\"my-book.o\"] in GCL, ~c[\"my-book.fasl\"] in SBCL). ~ilc[Certify-book] will also load this object file. When ~ilc[certify-book] is done, you may throw away the logical ~il[world] it created, for example by executing the ~il[command] ~c[:u]. To use the book later in any ACL2 session, just execute the event ~c[(include-book \"my-book\")]. This will do the necessary ~ilc[defpkg], load the non-~ilc[local] ~il[events] in ~c[\"my-book.lisp\"] and then may load the compiled code for the non-local functions defined in that file. Checks are made to ensure that the ~il[certificate] file exists and describes the version of ~c[\"my-book.lisp\"] that is read. The compiled code is loaded if and only if it exists and has a later write date than the source file (but ~pl[compilation] for an exception). Since ~ilc[include-book] is itself an event, you may put such forms into other ~il[books]. Thus it is possible for the inclusion of a single book to lead to the inclusion of many others. The check sum information maintained in ~il[certificate]s helps deal with the version control problem of the referenced ~il[books]. I.e., if this version of ~c[\"my-book\"] is used during the certification of ~c[\"your-book\"], then the ~il[certificate] for ~c[\"your-book\"] includes the check sum of this version of ~c[\"my-book\"]. If a later ~c[(include-book \"your-book\")] finds a version of ~c[\"my-book\"] with a different check sum, an error is signalled. But check sums are not perfect and the insecurity of the host file system prevents ACL2 from guaranteeing the logical soundness of an ~ilc[include-book] event, even for a book that appears to have a valid ~il[certificate] (they can be forged, after all). (~l[certificate] for further discussion.) This concludes the example of how to create, certify and use a book. If you wish, you could now review the ~il[documentation] for book-related topics (~pl[books]) and browse through them. They'll probably make sense in this context. Alternatively, you could continue the ``guided tour'' through the rest of the ~il[documentation] of ~il[books]. ~l[book-name], following the pointer given at the conclusion.") (deflabel full-book-name :doc ":Doc-Section Books book naming conventions assumed by ACL2~/ For this discussion we assume that the resident operating system is Unix (trademark of AT&T), but analogous remarks apply to other operating systems supported by ACL2, in particular, the Macintosh operating system where `~c[:]' plays roughly the role of `~c[/]' in Unix; ~pl[pathname]. ACL2 defines a ``full book name'' to be an ``absolute filename string,'' that may be divided into contiguous sections: a ``directory string'', a ``familiar name'' and an ``extension''. ~l[pathname] for the definitions of ``absolute,'' ``filename string,'' and other notions pertaining to naming files. Below we exhibit the three sections of one such string: ~bv[] \"/usr/home/smith/project/arith.lisp\" \"/usr/home/smith/project/\" ; directory string \"arith\" ; familiar name \".lisp\" ; extension~/ ~ev[] The sections are marked by the rightmost slash and rightmost dot, as shown below. ~bv[] \"/usr/home/smith/project/arith.lisp\" | | slash dot | | \"/usr/home/smith/project/\" ; directory string \"arith\" ; familiar name \".lisp\" ; extension ~ev[] The directory string includes (and terminates with) the rightmost slash. The extension includes (and starts with) the rightmost dot. The dot must be strictly to the right of the slash so that the familiar name is well-defined and nonempty. If you are using ACL2 on a system in which file names do not have this form, please contact the authors and we'll see what we can do about generalizing ACL2's conventions.") (deflabel book-name :doc ":Doc-Section Books conventions associated with book names~/ ~bv[] Examples: \"list-processing\" \"/usr/home/smith/my-arith\" ~ev[] Book names are string constants that can be elaborated into file names. We elaborate book names by concatenating the ``connected book directory'' (~pl[cbd]) string on the left and some ``extension,'' such as ~c[\".lisp\"], on the right. However, the connected book directory is not added if the book name itself already represents an absolute file name. Furthermore, ~ilc[include-book] and ~ilc[certify-book] temporarily reset the connected book directory to be the directory of the book being processed. This allows ~ilc[include-book] forms to use file names without explicit mention of the enclosing book's directory. This in turn allows ~il[books] (together with those that they include, using ~ilc[include-book]) to be moved between directories while maintaining their certification and utility. You may wish to read elsewhere for details of ACL2 file name conventions (~pl[pathname]), for a discussion of the filename that is the result of the elaboration described here (~pl[full-book-name]), and for details of the concept of the connected book directory (~pl[cbd]). For details of how ~ilc[include-book] (~pl[include-book]) and ~ilc[certify-book] (~pl[certify-book]) use these concepts, see below.~/ Often a book name is simply the familiar name of the file. (~l[full-book-name] for discussion of the notions of ``directory string,'' ``familiar name,'' and ``extension''. These concepts are not on the guided tour through ~il[books] and you should read them separately.) However, it is permitted for book names to include a directory or part of a directory name. Book names never include the extension, since ACL2 must routinely tack several different extensions onto the name during ~ilc[include-book]. For example, ~ilc[include-book] uses the ~c[\".lisp\"], ~c[\".cert\"] and possibly the ~c[\".o\"] or ~c[\".lbin\"] extensions of the book name. Book names are elaborated into full file names by ~ilc[include-book] and ~ilc[certify-book]. This elaboration is sensitive to the ``connected book directory.'' The connected book directory is an absolute filename string (~pl[pathname]) that is part of the ACL2 ~ilc[state]. (You may wish to ~pl[cbd] and to ~pl[set-cbd] ~-[] note that these are not on the guided tour). If a book name is an absolute filename string, ACL2 elaborates it simply by appending the desired extension to the right. If a book name is a relative filename string, ACL2 appends the connected book directory on the left and the desired extension on the right. Note that it is possible that the book name includes some partial specification of the directory. For example, if the connected book directory is ~c[\"/usr/home/smith/\"] then the book name ~c[\"project/task-1/arith\"] is a book name that will be elaborated to ~bv[] \"/usr/home/smith/project/task-1/arith.lisp\". ~ev[] Observe that while the ~il[events] in this ~c[\"arith\"] book are being processed the connected book directory will temporarily be set to ~bv[] \"/usr/home/smith/project/task-1/\". ~ev[] Thus, if the book requires other ~il[books], e.g., ~bv[] (include-book \"naturals\") ~ev[] then it is not necessary to specify the directory on which they reside provided that directory is the same as the superior book. This inheritance of the connected book directory and its use to elaborate the names of inferior ~il[books] makes it possible to move ~il[books] and their inferiors to new directories, provided they maintain the same relative relationship. It is even possible to move with ease whole collections of ~il[books] to different filesystems that use a different operating system than the one under which the original certification was performed. The ~c[\".cert\"] extension of a book, if it exists, is presumed to contain the most recent ~il[certificate] for the book. ~l[certificate] (or, if you are on the guided tour, wait until the tour gets there). ~l[book-contents] to continue the guided tour.") (deflabel book-contents :doc ":Doc-Section Books restrictions on the forms inside ~il[books]~/ ~bv[] Example Book: ; This book defines my app function and the theorem that it is ; associative. One irrelevant help lemma is proved first but ; it is local and so not seen by include-book. I depend on the ; inferior book \"weird-list-primitives\" from which I get ; definitions of hd and tl. (in-package \"MY-PKG\") (include-book \"weird-list-primitives\") (defun app (x y) (if (consp x) (cons (hd x) (app (tl x) y)) y)) (local (defthm help-lemma (implies (true-listp x) (equal (app x nil) x)))) (defthm app-is-associative (equal (app (app a b) c) (app a (app b c))))~/ ~ev[] The first form in a book must be ~c[(in-package \"pkg\")] where ~c[\"pkg\"] is some package name known to ACL2 whenever the book is certified. The rest of the forms in a book are embedded event forms, i.e., ~ilc[defun]s, ~ilc[defthm]s, etc., some of which may be marked ~ilc[local]. ~l[embedded-event-form]. The usual Common Lisp commenting conventions are provided. Note that since a book consists of embedded event forms, we can talk about the ``~ilc[local]'' and ``non-local'' ~il[events] of a book. Because ~ilc[in-package] is not an embedded event form, the only ~ilc[in-package] in a book is the initial one. Because ~ilc[defpkg] is not an embedded event form, a book can never contain a ~ilc[defpkg] form. Because ~ilc[include-book] is an embedded event form, ~il[books] may contain references to other ~il[books]. This makes ~il[books] structured objects. When the forms in a book are read from the file, they are read with ~ilc[current-package] set to the package named in the ~ilc[in-package] form at the top of the file. The effect of this is that all symbols are ~il[intern]ed in that package, except those whose packages are given explicitly with the ``::'' notation. For example, if a book begins with ~c[(in-package \"ACL2-X\")] and then contains the form ~bv[] (defun fn (x) (acl2::list 'car x)) ~ev[] then ~ilc[defun], ~c[fn], ~c[x], and ~ilc[car] are all ~il[intern]ed in the ~c[\"ACL2-X\"] package. I.e., it is as though the following form were read instead: ~bv[] (acl2-x::defun acl2-x::fn (acl2-x::x) (acl2::list 'acl2-x::car acl2-x::x)). ~ev[] Of course, ~c[acl2-x::defun] would be the same symbol as ~c[acl2::defun] if the ~c[\"ACL2-X\"] package imported ~c[acl2::defun]. If each book has its own unique package name and all the names defined within the book are in that package, then name clashes between ~il[books] are completely avoided. This permits the construction of useful logical ~il[world]s by the successive inclusion of many ~il[books]. Although it is often too much trouble to manage several packages, their judicious use is a way to minimize name clashes. Often, a better way is to use ~c[local]; ~pl[local]. How does ~ilc[include-book] know the definitions of the packages used in a book, since ~ilc[defpkg]s cannot be among the forms? More generally, how do we know that the forms in a book will be admissible in the host logical ~il[world] of an ~ilc[include-book]? ~l[certificate] for answers to these questions.") (deflabel certificate :doc ":Doc-Section Books how a book is known to be admissible and where its ~ilc[defpkg]s reside~/ A book, say ~c[\"arith\"], is said to have a ``certificate'' if there is a file named ~c[\"arith.cert\"]. Certificates are created by the function ~ilc[certify-book] and inspected by ~ilc[include-book]. Check sums are used to help ensure that certificates are legitimate and that the corresponding book has not been modified since certification. But because the file system is insecure and check sums are not perfect it is possible for the inclusion of a book to cause inconsistency even though the book carries an impeccable certificate. The certificate includes the version number of the certifying ACL2. A book is considered uncertified if it is included in an ACL2 with a different ~il[version] number.~/ The presence of a ``valid'' certificate file for a book attests to two things: all of the ~il[events] of the book are admissible in a certain extension of the initial ACL2 logic, and the non-~ilc[local] ~il[events] of the book are independent of the ~ilc[local] ones (~pl[local-incompatibility]). In addition, the certificate contains the ~il[command]s used to construct the ~il[world] in which certification occurred. Among those ~il[command]s, of course, are the ~ilc[defpkg]s defining the packages used in the book. When a book is included into a host ~il[world], that ~il[world] is first extended by the ~il[command]s listed in the certificate for the book. Unless that causes an error due to name conflicts, the extension ensures that all the packages used by the book are identically defined in the host ~il[world]. ~em[Security:] Because the host file system is insecure, there is no way ACL2 can guarantee that the contents of a book remain the same as when its certificate was written. That is, between the time a book is certified and the time it is used, it may be modified. Furthermore, certificates can be counterfeited. Check sums (~pl[check-sum]) are used to help detect such problems. But check sums provide imperfect security: two different files can have the same check sum. Therefore, from the strictly logical point of view, one must consider even the inclusion of certified ~il[books] as placing a burden on the user:~bq[] The non-erroneous inclusion of a certified book is consistency preserving provided (a) the objects read by ~ilc[include-book] from the certificate were the objects written there by a ~ilc[certify-book] and (b) the forms read by ~ilc[include-book] from the book itself are the forms read by the corresponding ~ilc[certify-book]. ~eq[]We say that a given execution of ~ilc[include-book] is ``certified'' if a certificate file for the book is present and well-formed and the check sum information contained within it supports the conclusion that the ~il[events] read by the ~ilc[include-book] are the ones checked by ~ilc[certify-book]. When an uncertified ~ilc[include-book] occurs, warnings are printed or errors are caused. But even if no warning is printed, you must accept burdens (a) and (b) if you use ~il[books]. These burdens are easier to live with if you protect your ~il[books] so that other users cannot write to them, you abstain from running concurrent ACL2 jobs, and you abstain from counterfeiting certificates. But even on a single user uniprocessor, you can shoot yourself in the foot by using the ACL2 ~il[io] primitives to fabricate an inconsistent book and the corresponding certificate. Note that part (a) of the burden described above implies, in particular, that there are no guarantees when a certificate is copied. When ~il[books] are renamed (as by copying them), it is recommended that their certificates be removed and the ~il[books] be recertified. The expectation is that recertification will go through without a hitch if relative ~il[pathname]s are used. ~l[pathname], which is not on the guided tour. Certificates essentially contain two parts, a ~il[portcullis] and a ~il[keep]. There is a third part, an ~c[expansion-alist], in order to record expansions if ~ilc[make-event] has been used, but the user need not be concerned with that level of detail. ~l[portcullis] to continue the guided tour through ~il[books].") (deflabel portcullis ; This documentation string formerly concluded (just before "~l[keep] to ; continue...") with the following discussion, until Version 2.6. Now that we ; change include-book forms in the portcuillis to use absolute pathnames, we do ; not need this. ; Recall that we disallow ~ilc[include-book] ~il[events] from the portcullis ; unless the included book's name is an absolute filename ; (~l[pathname]). Thus, for example, under the Unix operating ; system it is impossible to certify a book if the certification ; ~il[world] was created with ; ~bv[] ; ACL2 !>(~il[include-book] \"arith\") ; ~ev[] ; The problem here is that the file actually read on behalf of such ; an ~ilc[include-book] depends upon the then current setting of the ; connected book directory (~pl[cbd]). That setting could be ; changed before the certification occurs. If we were to copy ; ~c[(include-book \"arith\")] into the portcullis of the book being ; certified, there is no assurance that the ~c[\"arith\"] book included ; would come from the correct directory. However, by requiring that ; the ~ilc[include-book]s in the certification ~il[world] give book names ; that begin with slash we effectively require you to specify the full ; file name of each book involved in creating your certification ; ~il[world]. Observe that the execution of ; ~bv[] ; (~il[include-book] \"/usr/local/src/acl2/library/arith\") ; ~ev[] ; does not depend on the current book directory. On the other hand, ; this requirement ~-[] effectively that absolute file names be used in ; the certification ~il[world] ~-[] means that a book that requires ; another book in its certification ~il[world] will be rendered ; uncertified if the required book is removed to another directory. ; If possible, any ~ilc[include-book] ~il[command] required for a book ought ; to be placed in the book itself and not in the certification ; ~il[world]. The only time this cannot be done is if the required ; book is necessary to some ~ilc[defpkg] required by your book. Of ; course, this is just the same advice we have been giving: keep the ; certification ~il[world] as elementary as possible. :doc ":Doc-Section Books the gate guarding the entrance to a certified book~/ The certificate (~pl[certificate] for general information) of a certified file is divided into two parts, a portcullis and a ~il[keep]. These names come from castle lore. The portcullis of a castle is an iron grate that slides up through the ceiling of the tunnel-like entrance. The portcullis of a book ensures that ~ilc[include-book] does not start to read the book until the appropriate context has been created.~/ Technically, the portcullis consists of the ~il[version] number of the certifying ACL2, a list of ~il[command]s used to create the ``certification ~il[world]'' and an alist specifying the check sums of all the ~il[books] included in that ~il[world]. The portcullis is constructed automatically by ~ilc[certify-book] from the ~il[world] in which ~ilc[certify-book] is called, but that ~il[world] must have certain properties described below. After listing the properties we discuss the issues in a more leisurely manner. Each ~il[command] in the portcullis must be either a ~ilc[defpkg] form or an embedded event form (~pl[embedded-event-form]). Consider a book to be certified. The book is a file containing event forms. Suppose the file contains references to such symbols as ~c[my-pkg::fn] and ~c[acl2-arith::cancel], but that the book itself does not create the packages. Then a hard Lisp error would be caused merely by the attempt to read the expressions in the book. The corresponding ~ilc[defpkg]s cannot be written into the book itself because the book must be compilable and Common Lisp compilers differ on the rules concerning the inline definition of new packages. The only safe course is to make all ~ilc[defpkg]s occur outside of compiled files. More generally, when a book is certified it is certified within some logical ~il[world]. That ``certification ~il[world]'' contains not only the necessary ~ilc[defpkg]s but also, perhaps, function and constant definitions and maybe even references to other ~il[books]. When ~ilc[certify-book] creates the ~il[certificate] for a file it recovers from the certification ~il[world] the ~il[command]s used to create that ~il[world] from the initial ACL2 ~il[world]. Those ~il[command]s become part of the portcullis for the certified book. In addition, ~ilc[certify-book] records in the portcullis the check sums (~pl[check-sum]) of all the ~il[books] included in the certification ~il[world]. ~ilc[Include-book] presumes that it is impossible even to read the contents of a certified book unless the portcullis can be ``raised.'' To raise the portcullis we must be able to execute (possibly redundantly, but certainly without error), all of the ~il[command]s in the portcullis and then verify that the ~il[books] thus included were identical to those used to build the certification ~il[world] (up to check sum). This raising of the portcullis must be done delicately since ~ilc[defpkg]s are present: we cannot even read a ~il[command] in the portcullis until we have successfully executed the previous ones, since packages are being defined. Clearly, a book is most useful if it is certified in the most elementary extension possible of the initial logic. If, for example, your certification ~il[world] happens to contain a ~ilc[defpkg] for ~c[\"MY-PKG\"] and the function ~c[foo], then those definitions become part of the portcullis for the book. Every time the book is included, those names will be defined and will have to be either new or redundant (~pl[redundant-events]). But if those names were not necessary to the certification of the book, their presence would unnecessarily restrict the utility of the book. ~l[keep] to continue the guided tour of ~il[books].") (deflabel version :doc ":Doc-Section Miscellaneous ACL2 Version Number~/ To determine the version number of your copy of ACL2, evaluate the form ~c[(@ acl2-version)]. The value will be a string. For example, ~bv[] ACL2 !>(@ acl2-version) \"ACL2 Version 3.4\" ~ev[] ~/ The part of the string after ~c[\"ACL2 Version \"] is of the form ~c[x.y] or ~c[x.y.z], optionally followed by a succession of values in parentheses, where ~c[x], ~c[y], and ~c[z] are natural numbers. If ~c[z] is omitted then it is implicitly 0. We refer to ~c[X], ~c[y], and ~c[z] as the ``major'', ``minor'', and ``incrl'' fields, respectively. The incrl field is used for incremental releases. The discussion just below assumes that incremental releases are not employed at the user's site, i.e., the incrl fields are always 0. We remove this assumption when we discuss incremental releases at the end of this documenttation topic. ~il[Books] are considered certified only in the same version of ACL2 in which the certification was done. The ~il[certificate] file records the version number of the certifying ACL2 and ~il[include-book] considers the book uncertified if that does not match the current version number. Thus, each time we release a new version of ACL2, previously certified books should be recertified. Note that there are over 150 constants in the system, most having to do with the fact that ACL2 is coded in ACL2. Many of these, for example ~c[*common-lisp-specials-and-constants*] and ~c[*acl2-exports*], may change from version to version, and this can cause unsoundness. For example, the symbol ~c['set-difference-eq] was added to ~c[*acl2-exports*] in Version_2.9, so we can certify a book in Version_2.8 containing the following theorem, which is false in Version_2.9. ~bv[] (null (member 'set-difference-eq *acl2-exports*)) ~ev[] Therefore, we need to disallow inclusion of such a book in a Version_2.9 session, which otherwise would allow us to prove ~c[nil]. Furthermore, it is possible that from one version of the system to another we might change, say, the default values on some system function or otherwise make ``intentional'' changes to the axioms. It is even possible one version of the system is discovered to be unsound and we release a new version to correct our error. Therefore we adopted the draconian policy that books are certified by a given version of ACL2 and ``must'' be recertified to be used in other versions. We put ``must'' in quotes because in fact, ACL2 allows a book that was certified in one ACL2 version to be included in a later version, using ~ilc[include-book]. But ACL2 does not allow ~ilc[certify-book] to succeed when such an ~ilc[include-book] is executed on its behalf. Also, you may experience undesirable behavior if you avoid recertification when moving to a different version. Hence we recommend that you stick to the draconion policy of recertifying books when updating to a new ACL2 version. The string ~c[(@ acl2-version)] can contain implementation-specific information in addition to the version number. For example, in Macintosh Common Lisp (MCL) ~c[(char-code #\Newline)] is 13, while as far as we know, it is 10 in every other Common Lisp. Our concern is that one could certify a book in an MCL-based ACL2 with the theorem ~bv[] (equal (char-code #\Newline) 13) ~ev[] and then include this book in another Lisp and thereby prove ~c[nil]. So, when a book is certified in an MCL-based ACL2, the book's ~il[certificate] mentions ``MCL'' in its version string. Moreover, ~c[(@ acl2-version)] similarly mentions ``MCL'' when the ACL2 image has been built on top of MCL. Thus, an attempt to include a book in an MCL-based ACL2 that was certified in a non-MCL-based ACL2, or vice-versa, will be treated like an attempt to include an uncertified book. ~em[Incremental releases.] From time to time, so-called ``incremental releases'' of ACL2 are made available. These releases are thoroughly tested on at least two platforms; ``normal'' releases, on the other hand, are thoroughly tested on many more platforms (perhaps a dozen or so) and are accompanied by updates to the ACL2 home page. We provide incremental releases in order to provide timely updates for ACL2 users who want them, without imposing unnecessary burdens on either on the ACL2 implementors or on ACL2 users who prefer to update less frequently. The implementors expect users to update their copies of ACL2 when normal releases are made available, but not necessarily when incremental releases are made available. Incremental releases are accompanied by a bump in the incrl field of the version field, while normal releases are accompanied by a bump in the minor or (much less frequently) major field and zeroing out of the incrl field. Note that incremental releases are full-fledged releases.~/") (deflabel keep :doc ":Doc-Section Books how we know if ~ilc[include-book] read the correct files~/ The certificate (~pl[certificate] for general information) of a certified file is divided into two parts, a ~il[portcullis] and a keep. These names come from castle lore. The keep is the strongest and usually tallest tower of a castle from which the entire courtyard can be surveyed by the defenders. The keep of a book is a list of file names and check sums used after the book has been included, to determine if the files read were (up to check sum) those certified.~/ Once the ~il[portcullis] is open, ~ilc[include-book] can enter the book and read the event forms therein. The non-~ilc[local] event forms are in fact executed, extending the host theory. That may read in other ~il[books]. When that has been finished, the keep of the ~il[certificate] is inspected. The keep is a list of the book names which are included (hereditarily through all subbooks) in the certified book (including the certified book itself) together with the check sums of the objects in those ~il[books] at the time of certification. We compare the check sums of the ~il[books] just included to the check sums of the ~il[books] stored in the keep. If differences are found then we know that the book or one of its subbooks has been changed since certification. ~l[include-book] to continue the guided tour through ~il[books].") ; The documentation for include-book is in axioms.lisp, where the ; include-book event is defined. (deflabel uncertified-books :doc ":Doc-Section Books invalid ~il[certificate]s and uncertified ~il[books]~/ For relevant background ~pl[books], ~pl[certificate], and ~pl[portcullis]. ~ilc[Include-book] has a special provision for dealing with an uncertified book, i.e., a file with no ~il[certificate] or an invalid ~il[certificate] (i.e., one whose check sums describe files other than the ones actually read). In this case, a warning is printed and the book is otherwise processed much as though it were certified and had an open ~il[portcullis]. If a book ~c[B.lisp] is uncertified and a file ~c[B.port] exists, then the forms in ~c[B.port] are evaluated before the forms in ~c[B.lisp]. Such a file ~c[B.port] is typically created calling ~ilc[certify-book] on book ~c[\"B\"] with argument ~c[:write-port t], so that ~c[B.port] contains the ~il[portcullis] ~il[command]s for ~c[B] (the commands present in the ~il[world] when that certification was attempted). Inclusion of uncertified books can be handy, but it can have disastrous consequences.~/ The provision allowing uncertified ~il[books] to be included can have disastrous consequences, ranging from hard lisp errors, to damaged memory, to quiet logical inconsistency. It is possible for the inclusion of an uncertified book to render the logic inconsistent. For example, one of its non-~ilc[local] ~il[events] might be ~c[(defthm t-is-nil (equal t nil))]. It is also possible for the inclusion of an uncertified book to cause hard errors or ~il[breaks] into raw Common Lisp. For example, if the file has been edited since it was certified, it may contain too many open parentheses, causing Lisp to read past ``end of file.'' Similarly, it might contain non-ACL2 objects such as ~c[3.1415] or ill-formed event forms that cause ACL2 code to break. Even if a book is perfectly well formed and could be certified (in a suitable extension of ACL2's initial ~il[world]), its uncertified inclusion might cause Lisp errors or inconsistencies! For example, it might mention packages that do not exist in the host ~il[world], especially if the ~c[.port] file (discussed above) does not exist from an earlier certification attempt. The ~il[portcullis] of a certified book ensures that the correct ~ilc[defpkg]s have been admitted, but if a book is read without actually raising its ~il[portcullis], symbols in the file, e.g., ~c[acl2-arithmetic::fn], could cause ``unknown package'' errors in Common Lisp. Perhaps the most subtle disaster occurs if the host ~il[world] does have a ~ilc[defpkg] for each package used in the book but the host ~ilc[defpkg] imports different symbols than those required by the ~il[portcullis]. In this case, it is possible that formulas which were theorems in the certified book are non-theorems in the host ~il[world], but those formulas can be read without error and will then be quietly assumed. In short, if you include an uncertified book, ~st[all bets are off] regarding the validity of the future behavior of ACL2. That said, it should be noted that ACL2 is pretty tough and if errors don't occur, the chances are that deductions after the inclusion of an uncertified book are probably justified in the (possibly inconsistent) logical extension obtained by assuming the admissibility and validity of the definitions and conjectures in the book.") (deflabel books-certification-classic :Doc ":Doc-Section Books classic ACL2 `make'-based certification of ~il[books]~/ This ~il[documentation] topic explains an approach to certifying directories of books, which we call ``classic ACL2 `make'-based certification''. Warning: The capability described in this section might be replaced at any time by a capability based on corresponding support for community books (~pl[books-certification]). If you think that would be a hardship, please contact the ACL2 implementors. This topic discusses a way to certify a directory of books other than the ACL2 community books. ~l[books-certification] for how to certify the set of ACL2 community ~il[books]. There is also a section in that ~il[documentation] topic, ``Restricting to Specific Directories and Books'', that provides an alternative to classic ACL2 `make'-based certification (as discussed in the present topic) for certifying specified sets of books. We assume here a familiarity with Unix/Linux `make'. We also assume that you are using GNU `make' rather than some other flavor of `make'. And finally, we assume, as is typically the case by following the standard installation instructions, that you install the ACL2 community books in the ~c[books/] subdirectory of your ACL2 distribution. We will refer below to that directory as ~c[BOOKS]. In summary: to use `make' to certify ~il[books] under a given directory, you may create a simple Makefile in that directory (as explained below) so that when you stand in that directory, you can submit the command, `make', to certify those books. If you have a multi-processor machine or the like, then you can use the `~c[-j] flag `make'-level parallelism by specifying the number of concurrent processes. For example: ~bv[] make -j 4 ~ev[] For each book ~c[foo.lisp], a file ~c[foo.out] in the same directory as ~c[foo.lisp] will contain the output from the corresponding certification attempt. If you have previously executed such a command, then you might first want to delete ~il[certificate] files and other generated files by executing the following command. ~bv[] make clean ~ev[] Note that when you run `make', then by default, the first error will cause the process to stop. You can use ~c[make -i] to force `make' to ignore errors, thus continuing past them. Or, use ~c[make -k] to keep going, but skipping certification for any book that includes another whose certification has failed. By default, your acl2-customization file (~pl[acl2-customization]) is ignored by such `make' commands. However, you can specify the use of an acl2-customization file by setting the value of environment variable ~c[ACL2_CUSTOMIZATION] to the empty string, indicating a default such file, or to the desired absolute pathname. For example: ~bv[] make ACL2_CUSTOMIZATION='' make ACL2_CUSTOMIZATION='~~/acl2-customization.lisp' ~ev[] We now discuss how to create makefiles to support `make' commands as discussed above.~/ First we give five steps for creating a ~c[Makefile] to support certification of a directory of books, without subdirectories. For examples of such Makefiles you can look in community book directories (which, however, might disappear in future versions of ACL2). ~bq[] 1. Include the file ~c[Makefile-generic] from the ~c[books/] subdirectory of your ACL2 sources directory, but first perhaps define the variable `~c[ACL2]'. Consider the following example. ~bv[] ACL2 ?= /Users/john_doe/acl2/acl2-sources/saved_acl2 include /Users/john_doe/acl2/acl2-sources/books/Makefile-generic ~ev[] In this example, you can omit the first line, because the default ACL2 executable is file ~c[saved_acl2] in the directory immediately above the directory of the specified ~c[Makefile-generic] file. Indeed, that is the common case. Note the use of ~c[?=] instead of ~c[=] or ~c[:=], so that ~c[ACL2] can instead be defined by the environment or provided on the command line as part of the `make' command. 2. (Optional; usually skipped.) Set the ~c[INHIBIT] variable if you want to see more than the summary output. For example, if you want to see the same output as you would normally see at the terminal, put this line in your Makefile after the `~c[include]' lines. ~bv[] INHIBIT = (assign inhibit-output-lst (list (quote proof-tree))) ~ev[] For other values to use for ~c[INHIBIT], ~pl[set-inhibit-output-lst] and see the original setting of ~c[INHIBIT] in ~c[books/Makefile-generic]. 3. Specify the books to be certified. Normally, every file with extension ~c[.lisp] will be a book that you want to certify, in which case you can skip this step. Otherwise, put a line in your ~c[Makefile] after the ones above that specifies the books to be certified. The following example, from an old version of community books file ~c[books/finite-set-theory/osets/Makefile], should make this clear. ~bv[] BOOKS = computed-hints fast instance map membership outer primitives \\ quantify set-order sets sort ~ev[] But better yet, use the extension ~c[.lsp] for any Lisp or ACL2 files that are not to be certified, so that the definition of ~c[BOOKS] can be omitted. 4. Create ~c[.acl2] files for books that are to be certified in other than the initial ACL2 world (~pl[portcullis]). For example, if you look in community books file ~c[books/arithmetic/equalities.acl2] you will see ~ilc[defpkg] forms followed by a ~ilc[certify-book] command, because it was determined that ~ilc[defpkg] forms were necessary in the certification world in order to certify the ~c[equalities] book. In general, for each ~c[.lisp] whose certification requires a non-initial certification world, you will need a corresponding ~c[.acl2] file that ends with the appropriate ~ilc[certify-book] command. You also have the option of creating a file ~c[cert.acl2] that has a special role. When file ~c[.lisp] is certified, if there is no file ~c[.acl2] but there is a file ~c[cert.acl2], then ~c[cert.acl2] will be used as ~c[.acl2] would have been used, as described in the preceding paragraph, except that the appropriate ~ilc[certify-book] command will be generated automatically. Thus, no ~c[certify-book] command should occur in ~c[cert.acl2]. It is actually allowed to put raw lisp forms in a ~c[.acl2] file (presumably preceded by ~c[:q] or ~c[(value :q)] and followed by ~c[(lp)]). But this is not recommended; we make no guarantees about certification performed any time after raw Lisp has been entered in the ACL2 session. 5. Generally, the next step is to include the following line after the `~c[include]' of ~c[Makefile-generic] (see the first step above). ~bv[] -include Makefile-deps ~ev[] This will cause `make' to create and then include a file ~c[Makefile-deps] that contains ``dependency'' lines needed by `make'. If those dependencies are somehow flawed, it may be because you have ~ilc[include-book] forms that are not truly including books, for example in multi-line comments (~c[#|..|#]). These will be ignored if preceded by a semicolon (~c[;]), or if you add a line break after ``~c[include-book].'' But instead of adding the `~c[-include]' line above, you can create dependency lines yourself by running the command ~bv[] make dependencies ~ev[] and pasting the result into the end of your ~c[Makefile], and editing as you see fit.~eq[] This concludes the basic instructions for creating a ~c[Makefile] in a directory including books. Here are some other capabilities offered by community books file ~c[books/Makefile-subdirs]. Not included below is a discussion of how to increase parallelism by avoiding the need to certify included books before certifying a given book; ~pl[provisional-certification]. ~st[Subdirectory Support] There is support for using `make' to certify books in subdirectories. Consider the following example. ~bv[] DIRS = pass1 bind-free floor-mod include ../Makefile-subdirs ~ev[] This indicates that we are to run `make' in subdirectories ~c[pass1/], ~c[bind-free/], and ~c[floor-mod/] of the current directory. You can combine this subdirectory support with the support already discussed for certifying books in the top-level directory. Here is an example, which as of this writing is in community books file ~c[books/arithmetic-3/Makefile] contains the following lines. ~bv[] arith-top: top all all: top DIRS = pass1 bind-free floor-mod include ../Makefile-subdirs include ../Makefile-generic -include Makefile-deps ~ev[] The `~c[top]' target is defined in ~c[../Makefile-subdirs] to call `make' in each subdirectory specified in ~c[DIRS]. We have set the default target in the example above to a new name, ~c[arith-top], that makes that ~c[top] target before making the `~c[all]' target which, in turn, is the default target in any ~c[Makefile-generic], and is responsible for certifying books in the current directory as discussed in the five steps displayed above. Use ~c[Makefile-psubdirs] instead of ~c[Makefile-subdirs] if certification of a book in a subdirectory never depends on certification of a book in a different subdirectory, because then the ~c[-j] option of `make' can allow subdirectories to be processed in parallel. ~st[Cleaning Up] We note that there is a ~c[clean] target. Thus, ~bv[] make clean ~ev[] will remove generated files including ~c[.cert], ~c[.out] files, and compiled files. ~st[System Books] An environment variable ~c[ACL2_SYSTEM_BOOKS] is generally set automatically, so you can probably skip reading the following paragraph unless your attempt to certify books fails to locate those books properly. The environment variable ~c[ACL2_SYSTEM_BOOKS] can be set to the top-level directory of the ACL2 community books. A Unix-style pathname, typically ending in ~c[books/] or ~c[books], is permissible. In most cases, your ACL2 executable is a small script in which you can set this environment variable just above the line on which the actual ACL2 image is invoked, for example: ~bv[] export ACL2_SYSTEM_BOOKS ACL2_SYSTEM_BOOKS=/home/acl2/v3-2/acl2-sources/books ~ev[] However, you can also set ~c[ACL2_SYSTEM_BOOKS] as a `make' variable, by setting it in your ~c[Makefile] before the first target definition, e.g.: ~bv[] ACL2_SYSTEM_BOOKS ?= /home/acl2/v3-2/acl2-sources/books ~ev[] ~st[Compilation Support] The file ~c[books/Makefile-generic] provides support for compiling books that are already certified (but ~pl[compilation] for an exception). For example, suppose that you have certified books using GCL as the host Lisp, resulting in compiled files with the ~c[.o] extension. Now suppose you would like to compile the books for Allegro Common Lisp, whose compiled files have the ~c[.fasl] extension. The following command will work if you have included ~c[books/Makefile-generic] in your ~c[Makefile]. ~bv[] make fasl ~ev[] In general, the compiled file extension for a Lisp supported by ACL2 will be a target name for building compiled files for all your books (after certifying the books, if not already up-to-date on certification). If you run into problems, you can get help by joining the ~c[acl2-help] email list (follow the link from the ACL2 home page) and sending a message to that list. Also consider trying another version of GNU `make'; for example, we have found that versions 3.81 and 3.82 sometimes cause errors on Linux where version 3.80 does not.") (defdoc books-certification ":Doc-Section Books certifying ACL2 community ~il[books]~/ For background on the ACL2 community books, ~pl[community-books]. Here we explain how to certify those books, or some of those books, with ACL2. We thank Bishop Brock, Jared Davis, and Sol Swords for their substantial contributions to this methodology. See ~c[books/Makefile], in the community books, for more about ``Credits and History'', and for additional technical details not covered in this topic (for example, how to build a local copy of the xdoc manual for those who may wish to do that). For more information about installing ACL2, see the installation instructions, either by following a link from the ACL2 home page or by going directly to the page ~url[http://www.cs.utexas.edu/users/moore/acl2/current/installation/installation.html]. For information about so-called ``classic ACL2 `make'-based certification'', which provides support for certifying directories of books but may disappear in a future ACL2 release, ~pl[books-certification-classic]. ~st[The Basics] We make the following assumptions. ~bq[] o Gnu `make' is available on your system via the `make' command (rather than some other flavor of `make'). (Execute `~c[make --version] to verify this.) o You have built or obtained an ACL2 executable. o The ACL2 community books are installed in the ~c[books/] subdirectory of your ACL2 distribution, as is the case when you have followed the standard installation instructions. o All commands shown below are issued in the top-level (ACL2 sources) directory of your ACL2 distribution.~eq[] By default the ACL2 executable is file ~c[saved_acl2] in your ACL2 sources directory, and you can issue the following command to the shell in order to do a ``regression run'' that certifies all of the community books using that executable. ~bv[] make regression ~ev[] Better yet, save a log file in case there are problems, for example as follows. ~bv[] (make regression) >& make-regression.log ~ev[] or perhaps better yet: ~bv[] (time nice make regression) >& make-regression.log ~ev[] For the sake of brevity, below we'll skip mentioning any of `~c[time]', `~c[nice]', or `~c[>& make-regression.log]'. But saving a log file, in particular, is useful in case you encounter problems to report. If you fetched the community books using svn, then you will have a directory ~c[books/workshops/] that is not necessary for certifying the other books. If you want to skip certification of the books under ~c[books/workshops/], use target `~c[certify-books]' instead of target `~c[regression]', for example as follows. ~bv[] (time nice make certify-books) >& make-certify-books.log ~ev[] Whether you use target `~c[regression]' or target `~c[certify-books]', then for each book ~c[foo.lisp] whose certification is attempted, a file ~c[foo.cert.out] in the same directory will contain the output from the book's certification attempt. A regression run may take a few hours, but if you have a multiprocessing computer, you can speed it up by certifying some books in parallel, by providing a value for `make' option ~c[-j]. For example, if you have 8 hardware threads then you might want to issue the following command. ~bv[] make regression -j 8 ~ev[]~/ ~st[Specifying the ACL2 Executable] If your ACL2 executable is not file ~c[saved_acl2] in the ACL2 sources directory, then you will need to specify that executable. You can do that by setting variable ~c[ACL2], either as an environment variable or, as displayed below, as a `make' variable. Either way, you will need to avoid relative pathnames. For example, the first two forms below are legal, but the third is not, assuming that ~c[my-acl2] is on your ~c[PATH] in a Unix-like environment (e.g., linux or MacOS) and that ~c[my-saved_acl2] is just a pathname relative to your ACL2 sources directory, which is not on your path. ~bv[] make regression -j 8 ACL2=my-acl2 make regression -j 8 ACL2=/u/smith/bin/acl2 # The following only works if my-saved_acl2 is on your path (see above). make regression -j 8 ACL2=my-saved_acl2 ~ev[] ~st[Cleaning] You can delete files generated by book certification (including ~c[.cert] files, ~c[.out] files, compiled files, and more) by issuing the following command (again, in your ACL2 sources directory). ~bv[] make clean-books ~ev[] If you want to cause such deletion and then do a regression, simply replace the `~c[regression]' or `~c[certify-books]' target by `~c[regression-fresh]' or `~c[certify-books-fresh]', respectively, for example as follows. follows. ~bv[] make -j 4 regression-fresh make -j 4 certify-books-fresh ~ev[] If however you only want to clean up generated files residing under a given directory (or its subdirectories, and recursively), you can issue the following command while standing in that directory, where ~c[DIR] is a pathname of your ~c[books] directory. ~bv[] DIR/clean.pl ~ev[] For example, to clean up generated files under ~c[books/arithmetic], you could do the following. ~bv[] cd books/arithmetic ../clean.pl cd - # to return to the ACL2 sources directory, if you wish to do so ~ev[] ~st[Restricting to Specific Directories and Books] You can specify which books you want certified by using any or all of the variables ~c[EXCLUDED_PREFIXES], ~c[ACL2_BOOK_CERTS], or ~c[ACL2_BOOK_DIRS]. First, the set of desired ~c[.cert] files is restricted to those that do not start with any string that is one of the words in the value of ~c[EXCLUDED_PREFIXES]. Then ~c[ACL2_BOOK_CERTS] and ~c[ACL2_BOOK_DIRS], if supplied, specify which books should be certified, as illustrated by the following example. ~bv[] make -j 8 regression-fresh \\ ACL2_BOOK_DIRS=\"symbolic paco\" \\ ACL2_BOOK_CERTS=\" \\ workshops/2006/cowles-gamboa-euclid/Euclid/ed6a.cert \\ workshops/2006/cowles-gamboa-euclid/Euclid/ed4bb.cert \\ \" ~ev[] Then all book in directories ~c[symbolic] and ~c[paco] will be certified, as will the books ~c[workshops/2006/cowles-gamboa-euclid/Euclid/ed6a.lisp] and ~c[workshops/2006/cowles-gamboa-euclid/Euclid/ed4bb.lisp]. Note that all pathnames should be relative to your community books directory; in particular, they should not be absolute pathnames. Also notice the ~c[.cert] extension used in files supplied for ~c[ACL2_BOOK_CERTS]. Alternatively, you may wish to invoke ~c[books/cert.pl] while standing in a directory under which you want to certify books. This will certify not only those books, but all supporting books ~-[] even those not under the current directory ~-[] that do not have up-to-date ~c[.cert] files. The following is a simple command to invoke that will certify all books in the current directory, where if the ~c[books/] directory is not on your path, you will need to provide a suitable filename, e.g. ~c[../../cert.pl] or ~c[~~/acl2/books/cert.pl]. ~bv[] cert.pl -j 4 *.lisp ~ev[] Here is a more complex command, which illustrates a way to certify books in subdirectories (as well as the current directory), the use of provisional certification (~pl[provisional-certification]), and `make'-level parallelism (in this case specifying four parallel processes). ~bv[] ACL2_PCERT=t cert.pl -j 4 `find . -name '*.lisp'` ~ev[] Note that with this approach, unlike classic ACL2 `make'-based certification (~pl[books-certification-classic], out-of-date ~c[.cert] files that are not under the current directory will also be built. For documentation of ~c[cert.pl] invoke: ~bv[] cert.pl -h ~ev[] See the top of ~c[cert.pl] for authorship and copyright information. Finally, we give a brief summary of how to use so-called ``classic ACL2 `make'-based certification'' for community books; ~pl[books-certification-classic] for details. Note that support for this approach might be eliminated in a future ACL2 release. We welcome comments from the ACL2 community about whether or not that would be a good thing to do. See the discussion above about ~c[ACL2_BOOK_DIRS] for the ``modern'' way to accomplish the same thing. Many community book directories have a ~c[Makefile]. If you modify books only in such a directory, you can recertify by standing in that directory and issuing a `make' command. This command can optionally specify an ACL2 executable as well as parallelism, for example as follows, where the first line (~c[make clean]) is optional. ~bv[] make clean (time nice make -j 8 ACL2=my-acl2) ~ev[] ~st[ACL2 Customization Files] By default, your acl2-customization file (~pl[acl2-customization]) is ignored by all flavors of ``~c[make regression]''. However, you can specify the use of an acl2-customization file by setting the value of environment variable ~c[ACL2_CUSTOMIZATION] to the empty string, indicating a default such file, or to the desired absolute pathname. For example: ~bv[] make regression ACL2_CUSTOMIZATION='' make regression ACL2_CUSTOMIZATION='~~/acl2-customization.lisp' ~ev[] ~st[Regressions for Experimental Extensions of ACL2] The instructions are unchanged if you are using ACL2(h) (~pl[hons-and-memoization]). However, note that the default executable (when ~c[ACL2] is not specified) remains ~c[saved_acl2] in your ACL2 sources directory, not ~c[saved_acl2h] as one might expect for ACL2(h)(p), respectively. So probably you'll want to supply ``~c[ACL2=]'' explicitly with your `make' command. The comments above also pertain pertain to using ACL2(p) (~pl[parallel]); the default is ~c[saved_acl2] rather than ~c[saved_acl2p]. However, we recommend that you use ACL2, not ACL2(p), for your regression. Then you can use ACL2(p) for your own proof developments. (The analogous comment applies to ACL2(hp), which combines capabilities of ACL2(h) and ACL2(p), i.e., we recommend that you use ACl2(h) for your regression in that case.) However, if you want to use ACL2(p) or ACL2(hp) for your regression, ~pl[waterfall-parallelism-for-book-certification]. If you intend to certify books in the ~c[nonstd] subdirectory of the community books, you will want to use ACL2(r) (~pl[real]). In that case, substitute target ~c[regression-nonstd] for target ~c[regression]. The default executable (when ~c[ACL2] is not specified) will be file ~c[saved_acl2r] in your ACL2 sources directory, rather than ~c[saved_acl2]. ~st[Provisional Certification] To use provisional certification (~pl[provisional-certification]), supply ~c[ACL2_PCERT=t] with your `make' command. Here is an example. ~bv[] time nice make regression -j 4 ACL2_BOOK_DIRS=deduction ACL2_PCERT=t ~ev[] ~st[Miscellany] Other control of the certification process may be found by perusing community books file ~c[books/make_cert]. In particular, the ~c[INHIBIT] variable may be set to a call of ~ilc[set-inhibit-output-lst], for example as follows to obtain the output one would get by default in an (interactive) ACL2 session. ~bv[] time nice make regression -j 4 ACL2_BOOK_DIRS=arithmetic \\ INHIBIT='(set-inhibit-output-lst proof-tree)' ~ev[] ~st[Troubleshooting] If you run into problems, you can get help by joining the ~c[acl2-help] email list (follow the link from the ACL2 home page) and sending a message to that list. Also consider trying another version of GNU `make'; for example, we have found that versions 3.81 and 3.82 sometimes cause errors on Linux where version 3.80 does not. Note however that Version 3.80 does not print certain informational messages that are printed by later versions.~/") (link-doc-to regression books books-certification) ; Next we implement defchoose and defun-sk. (defun redundant-defchoosep (name event-form wrld) (let* ((old-ev (get-event name wrld))) (and old-ev (case-match old-ev (('defchoose !name old-bound-vars old-free-vars old-body . old-rest) (case-match event-form (('defchoose !name new-bound-vars new-free-vars new-body . new-rest) (and (equal old-bound-vars new-bound-vars) (equal old-free-vars new-free-vars) (equal old-body new-body) (eq (cadr (assoc-keyword :strengthen old-rest)) (cadr (assoc-keyword :strengthen new-rest))))))))))) (defun chk-arglist-for-defchoose (args bound-vars-flg ctx state) (cond ((arglistp args) (value nil)) ((not (true-listp args)) (er soft ctx "The ~#0~[bound~/free~] variables of a DEFCHOOSE event must be a ~ true list but ~x1 is not." (if bound-vars-flg 0 1) args)) (t (mv-let (culprit explan) (find-first-bad-arg args) (er soft ctx "The ~#0~[bound~/free~] variables of a DEFCHOOSE event ~ must be a true list of distinct, legal variable names. ~ ~x1 is not such a list. The element ~x2 violates the ~ rules because it ~@3." (if bound-vars-flg 0 1) args culprit explan))))) (defun defchoose-constraint-basic (fn bound-vars formals tbody ctx wrld state) ; It seems a pity to translate tbody, since it's already translated, but that ; seems much simpler than the alternatives. (cond ((null (cdr bound-vars)) (er-let* ((consequent (translate `(let ((,(car bound-vars) ,(cons fn formals))) ,tbody) t t t ctx wrld state))) (value (fcons-term* 'implies tbody consequent)))) (t (er-let* ((consequent (translate `(mv-let ,bound-vars ,(cons fn formals) ,tbody) t t t ctx wrld state))) (value (fcons-term* 'if ; We originally needed the following true-listp conjunct in order to prove ; guard conjectures generated by mv-nth in defun-sk. After v4-1, we tried ; removing it, but regression failed at lemma Bezout1-property in community ; book books/workshops/2006/cowles-gamboa-euclid/Euclid/ed3.lisp. So we have ; avoided making a change here after v4-1, after all. (fcons-term* 'true-listp (cons-term fn formals)) (fcons-term* 'implies tbody consequent) *nil*)))))) (defun generate-variable-lst-simple (var-lst avoid-lst) ; This is a simple variant of generate-variable-lst, to apply to a list of ; variables. (cond ((null var-lst) nil) (t (let ((old-var (car var-lst))) (mv-let (str n) (strip-final-digits (symbol-name old-var)) (let ((new-var (genvar (find-pkg-witness old-var) str (1+ n) avoid-lst))) (cons new-var (generate-variable-lst-simple (cdr var-lst) (cons new-var avoid-lst))))))))) (defun defchoose-constraint-extra (fn bound-vars formals body) ; WARNING: If the following comment is removed, then eliminate the reference to ; it in :doc defchoose. ; Note that :doc conservativity-of-defchoose contains an argument showing that ; we may assume that there is a definable enumeration, enum, of the universe. ; Thus, for any definable property that is not always false, there is a "least" ; witness, i.e., a least n for which (enum n) satisfies that property. Thus, a ; function defined with defchoose is definable: pick the least witness if there ; is one, else nil. From this definition it is clear that the following ; formula holds, where formals2 is a copy of formals that is disjoint both from ; formals and from bound-vars, and where tbody2 is the result of replacing ; formals by formals2 in tbody, the translated body of the defchoose. (If ; bound-vars is a list of length 1, then we use let rather than mv-let in this ; formula.) ; (or (equal (fn . formals) ; (fn . formals2)) ; (mv-let (bound-vars (fn . formals)) ; (and tbody ; (not tbody2))) ; (mv-let (bound-vars (fn . formals2)) ; (and tbody2 ; (not tbody1)))) ; We now outline an argument for the :non-standard-analysis case, which in fact ; provides justification for both defchoose axioms. The idea is to assume that ; there is a suitable well-ordering for the ground-zero theory and that the ; ground-zero theory contains enough "invisible" functions so that this ; property is preserved by extensions (as discussed in the JAR paper "Theory ; Extensions in ACL2(r) by Gamboa and Cowles). Here is a little more detail, ; but a nice challenge is to work this out completely. ; The idea of the proof is first to start with what the above paper calls an ; "r-complete" GZ: basically, a ground-zero theory satisfying induction and ; transfer that contains a function symbol for each defun and defun-std. We ; can preserve r-completeness as we add defun, defun-std, encapsulate, and ; defchoose events (again, as in the above paper). The key idea for defchoose ; is that GZ should also have a binary symbol, <|, that is axiomatized to be a ; total order. That is, <| is a "definable well order", in the sense that ; there are axioms that guarantee for each phi(x) that (exists x phi) implies ; that (exists <|-least x phi). The trick is to add the well-ordering after ; taking a nonstandard elementary extension of the standard reals MS, where ; every function over the reals is represented in MS as the interpretation of a ; function symbol. ; Still as in the above paper, there is a definable fn for the above defchoose, ; obtained by picking the least witness. Moreover, if body is classical then ; we can first conjoin it with (standard-p bound-var), choose the <|-least ; bound-var with a classical function using defun-std, and then show by ; transfer that this function witnesses the original defchoose. (let* ((formals2 (generate-variable-lst-simple formals (append bound-vars formals))) (body2 `(let ,(pairlis$ formals (pairlis$ formals2 nil)) ,body)) (equality `(equal (,fn ,@formals) (,fn ,@formals2)))) (cond ((null (cdr bound-vars)) (let ((bound-var (car bound-vars))) `(or ,equality (let ((,bound-var (,fn ,@formals))) (and ,body (not ,body2))) (let ((,bound-var (,fn ,@formals2))) (and ,body2 (not ,body)))))) (t `(or ,equality (mv-let (,@bound-vars) (,fn ,@formals) (and ,body (not ,body2))) (mv-let (,@bound-vars) (,fn ,@formals2) (and ,body2 (not ,body)))))))) (defun defchoose-constraint (fn bound-vars formals body tbody strengthen ctx wrld state) (er-let* ((basic (defchoose-constraint-basic fn bound-vars formals tbody ctx wrld state))) (cond (strengthen (er-let* ((extra (translate (defchoose-constraint-extra fn bound-vars formals body) t t t ctx wrld state))) (value (conjoin2 basic extra)))) (t (value basic))))) (defun defchoose-fn (def state event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (declare (xargs :guard (true-listp def))) ; def comes from macro call (when-logic "DEFCHOOSE" (with-ctx-summarized (if (output-in-infixp state) event-form (cons 'defchoose (car def))) (let* ((wrld (w state)) (event-form (or event-form (cons 'defchoose def))) (raw-bound-vars (cadr def)) (valid-keywords '(:doc :strengthen)) (ka (nthcdr 4 def)) ; def is the argument list of a defchoose call (doc (cadr (assoc-keyword :doc ka))) (strengthen (cadr (assoc-keyword :strengthen def)))) (er-progn (chk-all-but-new-name (car def) ctx 'constrained-function wrld state) (cond ((not (and (keyword-value-listp ka) (null (strip-keyword-list valid-keywords ka)))) (er soft ctx "Defchoose forms must have the form (defchoose fn bound-vars ~ formals body), with optional keyword arguments ~&0. However, ~ ~x1 does not have this form. See :DOC defchoose." valid-keywords event-form)) ((and doc (not (doc-stringp doc))) (er soft ctx "Illegal doc string has been supplied in ~x0. See :DOC ~ doc-string." event-form)) ((not (booleanp strengthen)) (er soft ctx "The :strengthen argument of a defchoose event must be t or nil. ~ The event ~x0 is thus illegal." event-form)) ((redundant-defchoosep (car def) event-form wrld) (stop-redundant-event ctx state)) (t (enforce-redundancy event-form ctx wrld (cond ((null raw-bound-vars) (er soft ctx "The bound variables of a defchoose form must be non-empty. ~ The form ~x0 is therefore illegal." event-form)) (t (let ((fn (car def)) (bound-vars (if (atom raw-bound-vars) (list raw-bound-vars) raw-bound-vars)) (formals (caddr def)) (body (cadddr def))) (er-progn (chk-arglist-for-defchoose bound-vars t ctx state) (chk-arglist-for-defchoose formals nil ctx state) (er-let* ((tbody (translate body t t t ctx wrld state)) (wrld (chk-just-new-name fn 'function nil ctx wrld state)) (doc-pair (translate-doc fn doc ctx state))) (cond ((intersectp-eq bound-vars formals) (er soft ctx "The bound and free variables of a defchoose form must ~ not intersect, but their intersection for the form ~x0 ~ is ~x1." event-form (intersection-eq bound-vars formals))) (t (let* ((body-vars (all-vars tbody)) (bound-and-free-vars (append bound-vars formals)) (diff (set-difference-eq bound-and-free-vars body-vars)) (ignore-ok (cdr (assoc-eq :ignore-ok (table-alist 'acl2-defaults-table wrld))))) (cond ((not (subsetp-eq body-vars bound-and-free-vars)) (er soft ctx "All variables in the body of a defchoose form must ~ appear among the bound or free variables supplied ~ in that form. However, the ~#0~[variable ~x0 ~ does~/variables ~&0 do~] not appear in the bound or ~ free variables of the form ~x1, even though ~#0~[it ~ appears~/they appear~] in its body." (set-difference-eq body-vars (append bound-vars formals)) event-form)) ((and diff (null ignore-ok)) (er soft ctx "The variable~#0~[ ~&0~ occurs~/s ~&0 occur~] in the ~ body of the form ~x1. However, ~#0~[this variable ~ does~/these variables do~] not appear either in the ~ bound variables or the formals of that form. In ~ order to avoid this error, see :DOC set-ignore-ok." diff event-form)) (t (pprogn (cond ((eq ignore-ok :warn) (warning$ ctx "Ignored-variables" "The variable~#0~[ ~&0 occurs~/s ~&0 ~ occur~] in the body of the following ~ defchoose form:~|~x1~|However, ~#0~[this ~ variable does~/these variables do~] not ~ appear either in the bound variables or ~ the formals of that form. In order to ~ avoid this warning, see :DOC set-ignore-ok." diff event-form)) (t state)) (let* ((stobjs-in (compute-stobj-flags formals nil wrld)) (stobjs-out (compute-stobj-flags bound-vars nil wrld)) (wrld #+:non-standard-analysis (putprop fn 'classicalp (classical-fn-list-p (all-fnnames tbody) wrld) wrld) #-:non-standard-analysis wrld) (wrld (putprop fn 'constrainedp t (putprop fn 'hereditarily-constrained-fnnames (list fn) (putprop fn 'symbol-class :common-lisp-compliant (putprop-unless fn 'stobjs-out stobjs-out nil (putprop-unless fn 'stobjs-in stobjs-in nil (putprop fn 'formals formals (update-doc-database fn doc doc-pair wrld))))))))) (er-let* ((constraint (defchoose-constraint fn bound-vars formals body tbody strengthen ctx wrld state))) (install-event fn event-form 'defchoose fn nil `(defuns nil nil ; Keep the following in sync with intro-udf-lst2. (,fn ,formals ,(null-body-er fn formals nil))) :protect ctx (putprop fn 'defchoose-axiom constraint wrld) state)))))))))))))))))))))) (defun non-acceptable-defun-sk-p (name args body doc quant-ok rewrite exists-p) ; Since this is just a macro, we only do a little bit of vanilla checking, ; leaving it to the real events to implement the most rigorous checks. (declare (ignore doc)) (let ((bound-vars (and (true-listp body) ;this is to guard cadr (cadr body) (if (atom (cadr body)) (list (cadr body)) (cadr body))))) (cond ((and rewrite exists-p) (msg "It is illegal to supply a :rewrite argument for a defun-sk form ~ that uses the exists quantifier. See :DOC defun-sk.")) ((and (keywordp rewrite) (not (member-eq rewrite '(:direct :default)))) (msg "The only legal keyword values for the :rewrite argument of a ~ defun-sk are :direct and :default. ~x0 is thus illegal." rewrite)) ((not (true-listp args)) (msg "The second argument of DEFUN-SK must be a true list of legal ~ variable names, but ~x0 is not a true-listp." args)) ((not (arglistp args)) (mv-let (culprit explan) (find-first-bad-arg args) (msg "The formal parameters (second argument) of a DEFUN-SK form must ~ be a true list of distinct, legal variable names. ~x0 is not ~ such a list. The element ~x1 violates the rules because it ~@2." args culprit explan))) ((not (and (true-listp body) (equal (length body) 3) (symbolp (car body)) (member-equal (symbol-name (car body)) '("FORALL" "EXISTS")) (true-listp bound-vars) (null (collect-non-legal-variableps bound-vars)))) (msg "The body (last argument) of a DEFUN-SK form must be a true list of ~ the form (Q vars term), where Q is FORALL or EXISTS and vars is a ~ variable or a true list of variables. The body ~x0 is therefore ~ illegal." body)) ((member-eq 'state bound-vars) (msg "The body (last argument) of a DEFUN-SK form must be a true list of ~ the form (Q vars term), where vars represents the bound ~ variables. The bound variables must not include STATE. The body ~ ~x0 is therefore illegal." body)) ((null (cadr body)) (msg "The variables of the body of a DEFUN-SK, following the quantifier ~ EXISTS or FORALL, must be a non-empty list. However, in DEFUN-SK ~ of ~x0, they are empty." name)) ((intersectp-eq bound-vars args) (msg "The formal parameters of a DEFUN-SK form must be disjoint from ~ the variables bound by its body. However, the ~#0~[variable ~x0 ~ belongs~/variables ~&0 belong~] to both the formal parameters, ~ ~x1, and the bound variables, ~x2." (intersection-eq bound-vars args) args bound-vars)) ((and (not quant-ok) (or (symbol-name-tree-occur 'forall (caddr body)) (symbol-name-tree-occur 'exists (caddr body)))) (msg "The symbol ~x0 occurs in the term you have supplied to DEFUN-SK, ~ namely, ~x1. By default, this is not allowed. Perhaps you ~ believe that DEFUN-SK can appropriately handle quantifiers other ~ than one outermost quantifier; sadly, this is not yet the case ~ (though you are welcome to contact the implementors and request ~ this capability). If however you really intend this DEFUN-SK form ~ to be executed (because, for example, ~x0 is in the scope of a ~ macro that expands it away), simply give a non-nil :quant-ok ~ argument. See :DOC defun-sk." (if (symbol-name-tree-occur 'forall (caddr body)) 'forall 'exists) body)) (t nil)))) (defmacro defun-sk (name args body &key doc quant-ok skolem-name thm-name rewrite strengthen #+:non-standard-analysis (classicalp 't classicalp-p) (witness-dcls '((declare (xargs :non-executable t))))) ":Doc-Section Events define a function whose body has an outermost quantifier~/ ~bv[] Examples: (defun-sk exists-x-p0-and-q0 (y z) (exists x (and (p0 x y z) (q0 x y z)))) (defun-sk exists-x-p0-and-q0 (y z) ; equivalent to the above (exists (x) (and (p0 x y z) (q0 x y z)))) (defun-sk forall-x-y-p0-and-q0 (z) (forall (x y) (and (p0 x y z) (q0 x y z))) :strengthen t)~/ General Form: (defun-sk fn (var1 ... varn) body &key rewrite doc quant-ok skolem-name thm-name witness-dcls strengthen) ~ev[] where ~c[fn] is the symbol you wish to define and is a new symbolic name (~pl[name]), ~c[(var1 ... varn)] is its list of formal parameters (~pl[name]), and ~c[body] is its body, which must be quantified as described below. The ~c[&key] argument ~ilc[doc] is an optional ~il[documentation] string to be associated with ~c[fn]; for a description of its form, ~pl[doc-string]. In the case that ~c[n] is 1, the list ~c[(var1)] may be replaced by simply ~c[var1]. The other arguments are explained below. For a simple example, ~pl[defun-sk-example]. For a more elaborate example, ~pl[Tutorial4-Defun-Sk-Example]. ~l[quantifier-tutorial] for a careful beginner's introduction that takes you through typical kinds of quantifier-based reasoning in ACL2. Also ~pl[quantifiers] for an example illustrating how the use of recursion, rather than explicit quantification with ~c[defun-sk], may be preferable. Below we describe the ~c[defun-sk] event precisely. First, let us consider the examples above. The first example, again, is: ~bv[] (defun-sk exists-x-p0-and-q0 (y z) (exists x (and (p0 x y z) (q0 x y z)))) ~ev[] It is intended to represent the predicate with formal parameters ~c[y] and ~c[z] that holds when for some ~c[x], ~c[(and (p0 x y z) (q0 x y z))] holds. In fact ~c[defun-sk] is a macro that adds the following two ~il[events], as shown just below. The first event guarantees that if this new predicate holds of ~c[y] and ~c[z], then the term shown, ~c[(exists-x-p0-and-q0-witness y z)], is an example of the ~c[x] that is therefore supposed to exist. (Intuitively, we are axiomatizing ~c[exists-x-p0-and-q0-witness] to pick a witness if there is one. We comment below on the use of ~ilc[defun-nx]; for now, consider ~c[defun-nx] to be ~ilc[defun].) Conversely, the second event below guarantees that if there is any ~c[x] for which the term in question holds, then the new predicate does indeed hold of ~c[y] and ~c[z]. ~bv[] (defun-nx exists-x-p0-and-q0 (y z) (let ((x (exists-x-p0-and-q0-witness y z))) (and (p0 x y z) (q0 x y z)))) (defthm exists-x-p0-and-q0-suff (implies (and (p0 x y z) (q0 x y z)) (exists-x-p0-and-q0 y z))) ~ev[] Now let us look at the third example from the introduction above: ~bv[] (defun-sk forall-x-y-p0-and-q0 (z) (forall (x y) (and (p0 x y z) (q0 x y z)))) ~ev[] The intention is to introduce a new predicate ~c[(forall-x-y-p0-and-q0 z)] which states that the indicated conjunction holds of all ~c[x] and all ~c[y] together with the given ~c[z]. This time, the axioms introduced are as shown below. The first event guarantees that if the application of function ~c[forall-x-y-p0-and-q0-witness] to ~c[z] picks out values ~c[x] and ~c[y] for which the given term ~c[(and (p0 x y z) (q0 x y z))] holds, then the new predicate ~c[forall-x-y-p0-and-q0] holds of ~c[z]. Conversely, the (contrapositive of) the second axiom guarantees that if the new predicate holds of ~c[z], then the given term holds for all choices of ~c[x] and ~c[y] (and that same ~c[z]). ~bv[] (defun-nx forall-x-y-p0-and-q0 (z) (mv-let (x y) (forall-x-y-p0-and-q0-witness z) (and (p0 x y z) (q0 x y z)))) (defthm forall-x-y-p0-and-q0-necc (implies (not (and (p0 x y z) (q0 x y z))) (not (forall-x-y-p0-and-q0 z)))) ~ev[] The examples above suggest the critical property of ~c[defun-sk]: it indeed does introduce the quantified notions that it claims to introduce. Notice that the ~ilc[defthm] event just above, ~c[forall-x-y-p0-and-q0-necc], may not be of optimal form as a rewrite rule. Users sometimes find that when the quantifier is ~c[forall], it is useful to state this rule in a form where the new quantified predicate is a hypothesis instead. In this case that form would be as follows: ~bv[] (defthm forall-x-y-p0-and-q0-necc (implies (forall-x-y-p0-and-q0 z) (and (p0 x y z) (q0 x y z)))) ~ev[] ACL2 will turn this into one ~c[:]~ilc[rewrite] rule for each conjunct, ~c[(p0 x y z)] and ~c[(q0 x y z)], with hypothesis ~c[(forall-x-y-p0-and-q0 z)] in each case. In order to get this effect, use ~c[:rewrite :direct], in this case as follows. ~bv[] (defun-sk forall-x-y-p0-and-q0 (z) (forall (x y) (and (p0 x y z) (q0 x y z))) :rewrite :direct) ~ev[] We now turn to a detailed description of ~c[defun-sk], starting with a discussion of its arguments as shown in the \"General Form\" above. The third argument, ~c[body], must be of the form ~bv[] (Q bound-vars term) ~ev[] where: ~c[Q] is the symbol ~ilc[forall] or ~ilc[exists] (in the \"ACL2\" package), ~c[bound-vars] is a variable or true list of variables disjoint from ~c[(var1 ... varn)] and not including ~ilc[state], and ~c[term] is a term. The case that ~c[bound-vars] is a single variable ~c[v] is treated exactly the same as the case that ~c[bound-vars] is ~c[(v)]. The result of this event is to introduce a ``Skolem function,'' whose name is the keyword argument ~c[skolem-name] if that is supplied, and otherwise is the result of modifying ~c[fn] by suffixing \"-WITNESS\" to its name. The following definition and one of the following two theorems (as indicated) are introduced for ~c[skolem-name] and ~c[fn] in the case that ~c[bound-vars] (see above) is a single variable ~c[v]. The name of the ~ilc[defthm] event may be supplied as the value of the keyword argument ~c[:thm-name]; if it is not supplied, then it is the result of modifying ~c[fn] by suffixing \"-SUFF\" to its name in the case that the quantifier is ~ilc[exists], and \"-NECC\" in the case that the quantifier is ~ilc[forall]. ~bv[] (defun-nx fn (var1 ... varn) (let ((v (skolem-name var1 ... varn))) term)) (defthm fn-suff ;in case the quantifier is EXISTS (implies term (fn var1 ... varn))) (defthm fn-necc ;in case the quantifier is FORALL (implies (not term) (not (fn var1 ... varn)))) ~ev[] In the ~c[forall] case, however, the keyword pair ~c[:rewrite :direct] may be supplied after the body of the ~c[defun-sk] form, in which case the contrapositive of the above form is used instead: ~bv[] (defthm fn-necc ;in case the quantifier is FORALL (implies (fn var1 ... varn) term)) ~ev[] This is often a better choice for the \"-NECC\" rule, provided ACL2 can parse ~c[term] as a ~c[:]~ilc[rewrite] rule. A second possible value of the ~c[:rewrite] argument of ~c[defun-sk] is ~c[:default], which gives the same behavior as when ~c[:rewrite] is omitted. Otherwise, the value of ~c[:rewrite] should be the term to use as the body of the ~c[fn-necc] theorem shown above; ACL2 will attempt to do the requisite proof in this case. If that term is weaker than the default, the properties introduced by ~c[defun-sk] may of course be weaker than they would be otherwise. Finally, note that the ~c[:rewrite] keyword argument for ~c[defun-sk] only makes sense if the quantifier is ~c[forall]; it is thus illegal if the quantifier is ~c[exists]. Enough said about ~c[:rewrite]! In the case that ~c[bound-vars] is a list of at least two variables, say ~c[(bv1 ... bvk)], the definition above (with no keywords) is the following instead, but the theorem remains unchanged. ~bv[] (defun-nx fn (var1 ... varn) (mv-let (bv1 ... bvk) (skolem-name var1 ... varn) term)) ~ev[] In order to emphasize that the last element of the list, ~c[body], is a term, ~c[defun-sk] checks that the symbols ~ilc[forall] and ~ilc[exists] do not appear anywhere in it. However, on rare occasions one might deliberately choose to violate this convention, presumably because ~ilc[forall] or ~ilc[exists] is being used as a variable or because a macro call will be eliminating ``calls of'' ~ilc[forall] and ~ilc[exists]. In these cases, the keyword argument ~c[quant-ok] may be supplied a non-~c[nil] value. Then ~c[defun-sk] will permit ~ilc[forall] and ~ilc[exists] in the body, but it will still cause an error if there is a real attempt to use these symbols as quantifiers. The use of ~ilc[defun-nx] above, rather than ~ilc[defun], disables certain checks that are required for evaluation, in particular the single-threaded use of ~ilc[stobj]s. However, there is a price: calls of these defined functions cannot be evaluated; ~pl[defun-nx]. Normally that is not a problem, since these notions involve quantifiers. But you are welcome to replace this ~ilc[declare] form with your own, as follows: if you supply a list of ~c[declare] forms to keyword argument ~c[:witness-dcls], these will become the declare forms in the generated ~ilc[defun]. Note that if your value of ~c[witness-dcls] does not contain the form ~c[(declare (xargs :non-executable t))], then the appropriate wrapper for non-executable functions will not be added automatically, i.e., ~ilc[defun] will be used in place of ~c[defun-nx]. Note also that if ~il[guard] verification is attempted, then it will likely fail with an error message complaining that ``guard verification may depend on local properties.'' In that case, you may wish to delay guard verification, as in the following example. ~bv[] (encapsulate () (defun-sk foo (x) (exists n (and (integerp n) (< n x))) :witness-dcls ((declare (xargs :guard (integerp x) :verify-guards nil)))) (verify-guards foo)) ~ev[] ~c[Defun-sk] is a macro implemented using ~ilc[defchoose]. Hence, it should only be executed in ~il[defun-mode] ~c[:]~ilc[logic]; ~pl[defun-mode] and ~pl[defchoose]. Advanced feature: If argument ~c[:strengthen t] is passed to ~c[defun-sk], then ~c[:strengthen t] will generate the extra constraint that that is generated for the corresponding ~c[defchoose] event; ~pl[defchoose]. You can use the command ~c[:]~ilc[pcb!] to see the event generated by a call of the ~c[defun-sk] macro. If you find that the rewrite rules introduced with a particular use of ~c[defun-sk] are not ideal, even when using the ~c[:rewrite] keyword discussed above (in the ~c[forall] case), then at least two reasonable courses of action are available for you. Perhaps the best option is to prove the ~ilc[rewrite] rules you want. If you see a pattern for creating rewrite rules from your ~c[defun-sk] events, you might want to write a macro that executes a ~c[defun-sk] followed by one or more ~ilc[defthm] events. Another option is to write your own variant of the ~c[defun-sk] macro, say, ~c[my-defun-sk], for example by modifying a copy of the definition of ~c[defun-sk] from the ACL2 sources. If you want to represent nested quantifiers, you can use more than one ~c[defun-sk] event. For example, in order to represent ~bv[] (forall x (exists y (p x y z))) ~ev[] you can use ~c[defun-sk] twice, for example as follows. ~bv[] (defun-sk exists-y-p (x z) (exists y (p x y z))) (defun-sk forall-x-exists-y-p (z) (forall x (exists-y-p x z))) ~ev[] Some distracting and unimportant warnings are inhibited during ~c[defun-sk]. Note for ACL2(r) users (~pl[real]): In ACL2(r), the keyword ~c[:CLASSICALP] is also supported. Its legal values are ~c[t] (the default) and ~c[nil], and it determines whether or not (respectively) ACL2(r) will consider ~c[fn] to be a classical function. It must be the case that the value is ~c[t] (perhaps implicitly, by default) if and only if ~c[body] is classical. Note that this way of implementing quantifiers is not a new idea. Hilbert was certainly aware of it 60 years ago! Also ~pl[conservativity-of-defchoose] for a technical argument that justifies the logical conservativity of the ~ilc[defchoose] event in the sense of the paper by Kaufmann and Moore entitled ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203).~/" (let* ((exists-p (and (true-listp body) (symbolp (car body)) (equal (symbol-name (car body)) "EXISTS"))) (bound-vars (and (true-listp body) (or (symbolp (cadr body)) (true-listp (cadr body))) (cond ((atom (cadr body)) (list (cadr body))) (t (cadr body))))) (body-guts (and (true-listp body) (caddr body))) (defchoose-body (if exists-p body-guts `(not ,body-guts))) (skolem-name (or skolem-name (intern-in-package-of-symbol (concatenate 'string (symbol-name name) "-WITNESS") name))) (thm-name (or thm-name (intern-in-package-of-symbol (concatenate 'string (symbol-name name) (if exists-p "-SUFF" "-NECC")) name))) (msg (non-acceptable-defun-sk-p name args body doc quant-ok rewrite exists-p))) (if msg `(er soft '(defun-sk . ,name) "~@0" ',msg) `(encapsulate () (logic) (set-match-free-default :all) (set-inhibit-warnings "Theory" "Use" "Free" "Non-rec" "Infected") (encapsulate ((,skolem-name ,args ,(if (= (length bound-vars) 1) (car bound-vars) (cons 'mv bound-vars)) #+:non-standard-analysis ,@(and classicalp-p `(:classicalp ,classicalp)))) (local (in-theory '(implies))) (local (defchoose ,skolem-name ,bound-vars ,args ,defchoose-body ,@(and strengthen '(:strengthen t)))) ,@(and strengthen `((defthm ,(packn (list skolem-name '-strengthen)) ,(defchoose-constraint-extra skolem-name bound-vars args defchoose-body) :hints (("Goal" :use ,skolem-name :in-theory (theory 'minimal-theory))) :rule-classes nil))) (,(if (member-equal '(declare (xargs :non-executable t)) witness-dcls) 'defun-nx 'defun) ,name ,args ,@witness-dcls ,(if (= (length bound-vars) 1) `(let ((,(car bound-vars) (,skolem-name ,@args))) ,body-guts) `(mv-let (,@bound-vars) (,skolem-name ,@args) ,body-guts))) (in-theory (disable (,name))) (defthm ,thm-name ,(cond (exists-p `(implies ,body-guts (,name ,@args))) ((eq rewrite :direct) `(implies (,name ,@args) ,body-guts)) ((member-eq rewrite '(nil :default)) `(implies (not ,body-guts) (not (,name ,@args)))) (t rewrite)) :hints (("Goal" :use (,skolem-name ,name) :in-theory (theory 'minimal-theory)))) ,@(if doc `((defdoc ,name ,doc)) nil)))))) (deflabel forall :doc ":Doc-Section Defun-sk universal quantifier~/~/ The symbol ~c[forall] (in the ACL2 package) represents universal quantification in the context of a ~ilc[defun-sk] form. ~l[defun-sk] and ~pl[exists]. ~l[quantifiers] for an example illustrating how the use of recursion, rather than explicit quantification with ~ilc[defun-sk], may be preferable.") (deflabel exists :doc ":Doc-Section Defun-sk existential quantifier~/~/ The symbol ~c[exists] (in the ACL2 package) represents existential quantification in the context of a ~ilc[defun-sk] form. ~l[defun-sk] and ~pl[forall]. ~l[quantifiers] for an example illustrating how the use of recursion, rather than explicit quantification with ~ilc[defun-sk], may be preferable.") (deflabel defun-sk-example :doc ":Doc-Section Defun-sk a simple example using ~ilc[defun-sk]~/~/ For a more through, systematic beginner's introduction to quantification in ACL2, ~pl[quantifier-tutorial]. The following example illustrates how to do proofs about functions defined with ~ilc[defun-sk]. The events below can be put into a certifiable book (~pl[books]). The example is contrived and rather silly, in that it shows how to prove that a quantified notion implies itself, where the antecedent and conclusion are defined with different ~ilc[defun-sk] events. But it illustrates the formulas that are generated by ~ilc[defun-sk], and how to use them. Thanks to Julien Schmaltz for presenting this example as a challenge. ~bv[] (in-package \"ACL2\") (encapsulate (((p *) => *) ((expr *) => *)) (local (defun p (x) x)) (local (defun expr (x) x))) (defun-sk forall-expr1 (x) (forall (y) (implies (p x) (expr y)))) (defun-sk forall-expr2 (x) (forall (y) (implies (p x) (expr y))))) ; We want to prove the theorem my-theorem below. What axioms are there that ; can help us? If you submit the command ; :pcb! forall-expr1 ; then you will see the following two key events. (They are completely ; analogous of course for FORALL-EXPR2.) ; (DEFUN FORALL-EXPR1 (X) ; (LET ((Y (FORALL-EXPR1-WITNESS X))) ; (IMPLIES (P X) (EXPR Y)))) ; ; (DEFTHM FORALL-EXPR1-NECC ; (IMPLIES (NOT (IMPLIES (P X) (EXPR Y))) ; (NOT (FORALL-EXPR1 X))) ; :HINTS ; ((\"Goal\" :USE FORALL-EXPR1-WITNESS))) ; We see that the latter has value when FORALL-EXPR1 occurs negated in a ; conclusion, or (therefore) positively in a hypothesis. A good rule to ; remember is that the former has value in the opposite circumstance: negated ; in a hypothesis or positively in a conclusion. ; In our theorem, FORALL-EXPR2 occurs positively in the conclusion, so its ; definition should be of use. We therefore leave its definition enabled, ; and disable the definition of FORALL-EXPR1. ; (thm ; (implies (and (p x) (forall-expr1 x)) ; (forall-expr2 x)) ; :hints ((\"Goal\" :in-theory (disable forall-expr1)))) ; ; ; which yields this unproved subgoal: ; ; (IMPLIES (AND (P X) (FORALL-EXPR1 X)) ; (EXPR (FORALL-EXPR2-WITNESS X))) ; Now we can see how to use FORALL-EXPR1-NECC to complete the proof, by ; binding y to (FORALL-EXPR2-WITNESS X). ; We use defthmd below so that the following doesn't interfere with the ; second proof, in my-theorem-again that follows. (defthmd my-theorem (implies (and (p x) (forall-expr1 x)) (forall-expr2 x)) :hints ((\"Goal\" :use ((:instance forall-expr1-necc (x x) (y (forall-expr2-witness x))))))) ; The following illustrates a more advanced technique to consider in such ; cases. If we disable forall-expr1, then we can similarly succeed by having ; FORALL-EXPR1-NECC applied as a :rewrite rule, with an appropriate hint in how ; to instantiate its free variable. See :doc hints. (defthm my-theorem-again (implies (and (P x) (forall-expr1 x)) (forall-expr2 x)) :hints ((\"Goal\" :in-theory (disable forall-expr1) :restrict ((forall-expr1-necc ((y (forall-expr2-witness x)))))))) ~ev[]") (defdoc quantifier-tutorial ":Doc-Section Defun-sk A Beginner's Guide to Reasoning about Quantification in ACL2~/ The initial version of this tutorial was written by Sandip Ray. Additions and revisions are welcome. Sandip has said: ~bq[] ``This is a collection of notes that I wrote to remind myself of how to reason about quantifiers when I just started. Most users after they have gotten the hang of quantifiers probably will not need this and will be able to use their intuitions to guide them in the process. But since many ACL2 users are not used to quantification, I am hoping that this set of notes might help them to think clearly while reasoning about quantifiers in ACL2.''~eq[] Many ACL2 papers start with the sentence ``ACL2 is a quantifier-free first-order logic of recursive functions.'' It is true that the ~em[syntax] of ACL2 is quantifier-free; every formula is assumed to be universally quantified over all free variables in the formula. But the ~em[logic] in fact does afford arbitrary first-order quantification. This is obtained in ACL2 using a construct called ~c[defun-sk]. ~l[defun-sk]. Many ACL2 users do not think in terms of ~il[quantifiers]. The focus is almost always on defining recursive functions and reasoning about them using induction. That is entirely justified, in fact, since proving theorems about recursive functions by induction plays to the strengths of the theorem prover. Nevertheless there are situations where it is reasonable and often useful to think in terms of quantifiers. However, reasoning about quantifiers requires that you get into the mindset of thinking about theorems in terms of quantification. This note is about how to do this effectively given ACL2's implementation of quantification. This does not discuss ~ilc[defun-sk] in detail, but merely shows some examples. A detailed explanation of the implementation is in the ACL2 ~il[documentation] (~pl[defun-sk]); also ~pl[conservativity-of-defchoose]. [Note: Quantifiers can be used for some pretty cool things in ACL2. Perhaps the most interesting example is the way of using quantifiers to introduce arbitrary tail-recursive equations; see the paper ``Partial Functions in ACL2'' by Panagiotis Manolios and J Strother Moore. This note does not address applications of quantifiers, but merely how you would reason about them once you think you want to use them.]~/ Assume that you have some function ~c[P]. I have just left ~c[P] as a unary function stub below, since I do not care about what ~c[P] is. ~bv[] (defstub P (*) => *) ~ev[] Now suppose you want to specify the concept that ``there exists some ~c[x] such that ~c[(P x)] holds''. ACL2 allows you to write that directly using quantifiers. ~bv[] (defun-sk exists-P () (exists x (P x))) ~ev[] If you submit the above form in ACL2 you will see that the theorem prover specifies two functions ~c[exists-p] and ~c[exists-p-witness], and exports the following constraints: ~bv[] 1. (defun exists-P () (P (exists-P-witness))) 2. (defthm exists-P-suff (implies (p x) (exists-p))) ~ev[] Here ~c[exists-P-witness] is a new function symbol in the current ACL2 theory. What do the constraints above say? Notice the constraint ~c[exists-p-suff]. It says that if you can provide any ~c[x] such that ~c[(P x)] holds, then you know that ~c[exists-p] holds. Think of the other constraint (definition of ~c[exists-p]) as going the other way. That is, it says that if ~c[exists-p] holds, then there is some ~c[x], call it ~c[(exists-p-witness)], for which ~c[P] holds. Notice that nothing else is known about ~c[exists-p-witness] than the two constraints above. [Note: ~c[exists-p-witness] above is actually defined in ACL2 using a special form called ~c[defchoose]. ~l[defchoose]. This note does not talk about ~c[defchoose]. So far as this note is concerned, think of ~c[exists-p-witness] as a new function symbol that has been generated somehow in ACL2, about which nothing other than the two facts above is known.] Similarly, you can talk about the concept that ``for all ~c[x] ~c[(P x)] holds.'' This can be specified in ACL2 by the form: ~bv[] (defun-sk forall-P () (forall x (P x))) ~ev[] This produces the following two constraints: ~bv[] 1. (defun forall-P () (P (forall-p-witness))) 2. (defthm forall-p-necc (implies (not (P x)) (not (forall-p)))) ~ev[] To understand these, think of ~c[for-all-p-witness] as producing some ~c[x] which does not satisfy ~c[P], if such a thing exists. The constraint ~c[forall-p-necc] merely says that if ~c[forall-p] holds then ~c[P] is satisfied for every ~c[x]. (To see this more clearly, just think of the contrapositive of the formula shown.) The other constraint (definition of ~c[forall-p]) implies that if ~c[forall-p] does not hold then there is some ~c[x], call it ~c[(forall-p-witness)], which does not satisfy ~c[P]. To see this, just consider the following formula which is immediately derivable from the definition. ~bv[] (implies (not (forall-p)) (not (P (forall-witness)))) ~ev[] The description above suggests that to reason about quantifiers, the following Rules of Thumb, familiar to most any student of logic, are useful. ~bq[] RT1: To prove ~c[(exists-p)], construct some object ~c[A] such that ~c[P] holds for ~c[A] and then use ~c[exists-P-suff]. RT2: If you assume ~c[exists-P] in your hypothesis, use the definition of ~c[exists-p] to know that ~c[P] holds for ~c[exists-p-witness]. To use this to prove a theorem, you must be able to derive the theorem based on the hypothesis that ~c[P] holds for something, whatever the something is. RT3: To prove ~c[forall-P], prove the theorem ~c[(P x)] (that is, that ~c[P] holds for an arbitrary ~c[x]), and then simply instantiate the definition of ~c[forall-p], that is, show that ~c[P] holds for the witness. RT4: If you assume ~c[forall-p] in the hypothesis of the theorem, see how you can prove your conclusion if indeed you were given ~c[(P x)] as a theorem. Possibly for the conclusion to hold, you needed that ~c[P] holds for some specific set of ~c[x] values. Then use the theorem ~c[forall-p-necc] by instantiating it for the specific ~c[x] values you care about.~eq[] Perhaps the above is too terse. In the remainder of the note, we will consider several examples of how this is done to prove theorems in ACL2 that involve quantified notions. Let us consider two trivial theorems. Assume that for some unary function ~c[r], you have proved ~c[(r x)] as a theorem. Let us see how you can prove that (1) there exists some x such that ~c[(r x)] holds, and (2) for all ~c[x] ~c[(r x)] holds. We first model these things using ~ilc[defun-sk]. Below, ~c[r] is simply some function for which ~c[(r x)] is a theorem. ~bv[] (encapsulate (((r *) => *)) (local (defun r (x) (declare (ignore x)) t)) (defthm r-holds (r x))) (defun-sk exists-r () (exists x (r x))) (defun-sk forall-r () (forall x (r x))) ~ev[] ACL2 does not have too much reasoning support for quantifiers. So in most cases, one would need ~c[:use] hints to reason about quantifiers. In order to apply ~c[:use] ~il[hints], it is preferable to keep the function definitions and theorems disabled. ~bv[] (in-theory (disable exists-r exists-r-suff forall-r forall-r-necc)) ~ev[] Let us now prove that there is some ~c[x] such that ~c[(r x)] holds. Since we want to prove ~c[exists-r], we must use ~c[exists-r-suff] by RT1. We do not need to construct any instance here since ~c[r] holds for all ~c[x] by the theorem above. ~bv[] (defthm exists-r-holds (exists-r) :hints ((\"Goal\" :use ((:instance exists-r-suff))))) ~ev[] Let us now prove the theorem that for all ~c[x], ~c[(r x)] holds. By RT3, we must be able to prove it by definition of ~c[forall-r]. ~bv[] (defthm forall-r-holds (forall-r) :hints ((\"Goal\" :use ((:instance (:definition forall-r)))))) ~ev[] [Note: Probably no ACL2 user in his or her right mind would prove the theorems ~c[exists-r-holds] and ~c[forall-r-holds] above. The theorems shown are only for demonstration purposes.] For the remainder of this note we will assume that we have two stubbed out unary functions ~c[M] and ~c[N], and we will look at proving some quantified properties of these functions. ~bv[] (defstub M (*) => *) (defstub N (*) => *) ~ev[] Let us now define the predicates ~c[all-M], ~c[all-N], ~c[ex-M], and ~c[ex-N] specifying the various quantifications. ~bv[] (defun-sk all-M () (forall x (M x))) (defun-sk all-N () (forall x (N x))) (defun-sk some-M () (exists x (M x))) (defun-sk some-N () (exists x (N x))) (in-theory (disable all-M all-N all-M-necc all-N-necc)) (in-theory (disable some-M some-N some-M-suff some-N-suff)) ~ev[] Let us prove the classic distributive properties of quantification: the distributivity of universal quantification over conjunction, and the distributivity of existential quantification over disjunction. We can state these properties informally in ``pseudo ACL2'' notation as follows: ~bv[] 1. (exists x: (M x)) or (exists x: (N x)) <=> (exists x: (M x) or (N x)) 2. (forall x: (M x)) and (forall: x (N x)) <=> (forall x: (M x) and (N x)) ~ev[] To make these notions formal we of course need to define the formulas at the right-hand sides of 1 and 2. So we define ~c[some-MN] and ~c[all-MN] to capture these concepts. ~bv[] (defun-sk some-MN () (exists x (or (M x) (N x)))) (defun-sk all-MN () (forall x (and (M x) (N x)))) (in-theory (disable all-MN all-MN-necc some-MN some-MN-suff)) ~ev[] First consider proving property 1. The formal statement of this theorem would be: ~c[(iff (some-MN) (or (some-M) (some-N)))]. How do we prove this theorem? Looking at RT1-RT4 above, note that they suggest how one should reason about quantification when one has an ``implication''. But here we have an ``equivalence''. This suggests another rule of thumb. ~bq[] RT5: Whenever possible, prove an equivalence involving quantifiers by proving two implications.~eq[] Let us apply RT5 to prove the theorems above. So we will first prove: ~c[(implies (some-MN) (or (some-M) (some-N)))] How can we prove this? This involves assuming a quantified predicate ~c[(some-MN)], so we must use RT2 and apply the definition of ~c[some-MN]. Since the conclusion involves a disjunction of two quantified predicates, by RT1 we must be able to construct two objects ~c[A] and ~c[B] such that either ~c[M] holds for ~c[A] or ~c[N] holds for ~c[B], so that we can then invoke ~c[some-M-suff] and ~c[some-N-suff] to prove the conclusion. But now notice that if ~c[some-MN] is true, then there is already an object, in fact ~c[some-MN-witness], such that either ~c[M] holds for it, or ~c[N] holds for it. And we know this is the case from the definition of ~c[some-MN]! So we will simply prove the theorem instantiating ~c[some-M-suff] and ~c[some-N-suff] with this witness. The conclusion is that the following event will go through with ACL2. ~bv[] (defthm le1 (implies (some-MN) (or (some-M) (some-N))) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition some-MN)) (:instance some-M-suff (x (some-MN-witness))) (:instance some-N-suff (x (some-MN-witness))))))) ~ev[] This also suggests the following rule of thumb: ~bq[] RT6: If a conjecture involves assuming an existentially quantified predicate in the hypothesis from which you are trying to prove an existentially quantified predicate, use the witness of the existential quantification in the hypothesis to construct the witness for the existential quantification in the conclusion.~eq[] Let us now try to prove the converse of le1, that is: ~c[(implies (or (some-M) (some-N)) (some-MN))] Since the hypothesis is a disjunction, we will just prove each case individually instead of proving the theorem by a :~c[cases] hint. So we prove the following two lemmas. ~bv[] (defthm le2 (implies (some-M) (some-MN)) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition some-M)) (:instance some-MN-suff (x (some-M-witness))))))) (defthm le3 (implies (some-N) (some-MN)) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition some-N)) (:instance some-MN-suff (x (some-N-witness))))))) ~ev[] Note that the hints above are simply applications of RT6 as in ~c[le1]. With these lemmas, of course the main theorem is trivial. ~bv[] (defthmd |some disjunction| (iff (some-MN) (or (some-M) (some-N))) :hints ((\"Goal\" :use ((:instance le1) (:instance le2) (:instance le3))))) ~ev[] Let us now prove the distributivity of universal quantification over conjunction, that is, the formula: ~c[(iff (all-MN) (and (all-M) (all-N)))] Applying RT5, we will again decompose this into two implications. So consider first the one-way implication: ~c[(implies (and (all-M) (all-N)) (all-MN))]. Here we get to assume ~c[all-M] and ~c[all-N]. Thus by RT4 we can use ~c[all-M-necc] and ~c[all-N-necc] to think as if we are given the formulas ~c[(M x)] and ~c[(N x)] as theorems. The conclusion here is also a universal quantification, namely we have to prove ~c[all-MN]. Then RT3 tells us to proceed as follows. Take any object ~c[y]. Try to find an instantiation ~c[z] of the hypothesis that implies ~c[(and (M y) (N y))]. Then instantiate ~c[y] with ~c[all-MN-witness]. Note that the hypothesis lets us assume ~c[(M x)] and ~c[(N x)] to be theorems. Thus to justify we need to instantiate ~c[x] with ~c[y], and in this case, therefore, with ~c[all-MN-witness]. To make the long story short, the following event goes through with ACL2: ~bv[] (defthm lf1 (implies (and (all-M) (all-N)) (all-MN)) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition all-MN)) (:instance all-M-necc (x (all-MN-witness))) (:instance all-N-necc (x (all-MN-witness))))))) ~ev[] This suggests the following rule of thumb which is a dual of RT6: ~bq[] RT7: If a conjecture assumes some universally quantified predicate in the hypothesis and its conclusion asserts a universallly quantified predicate, then instantiate the ``necessary condition'' (~c[forall-mn-necc]) of the hypothesis with the witness of the conclusion to prove the conjecture.~eq[] Applying RT7 now we can easily prove the other theorems that we need to show that universal quantification distributes over conjunction. Let us just go through this motion in ACL2. ~bv[] (defthm lf2 (implies (all-MN) (all-M)) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition all-M)) (:instance all-MN-necc (x (all-M-witness))))))) (defthm lf3 (implies (all-MN) (all-N)) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition all-N)) (:instance all-MN-necc (x (all-N-witness))))))) (defthmd |all conjunction| (iff (all-MN) (and (all-M) (all-N))) :hints ((\"Goal\" :use ((:instance lf1) (:instance lf2) (:instance lf3))))) ~ev[] The rules of thumb for universal and existential quantification should make you realize the duality of their use. Every reasoning method about universal quantification can be cast as a way of reasoning about existential quantification, and vice versa. Whether you reason using universal and existential quantifiers depends on what is natural in a particular context. But just for the sake of completeness let us prove the duality of universal and existential quantifiers. So what we want to prove is the following: ~bv[] 3. (forall x (not (M x))) = (not (exists x (M x))) ~ev[] We first formalize the notion of ~c[(forall x (not (M x)))] as a quantification. ~bv[] (defun-sk none-M () (forall x (not (M x)))) (in-theory (disable none-M none-M-necc)) ~ev[] So we now want to prove: ~c[(equal (none-M) (not (some-M)))]. As before, we should prove this as a pair of implications. So let us prove first: ~c[(implies (none-M) (not (some-M)))]. This may seem to assert an existential quantification in the conclusion, but rather, it asserts the ~em[negation] of an existential quantification. We are now trying to prove that something does not exist. How do we do that? We can show that nothing satisfies ~c[M] by just showing that ~c[(some-M-witness)] does not satisfy ~c[M]. This suggests the following rule of thumb: ~bq[] RT8: When you encounter the negation of an existential quantification think in terms of a universal quantification, and vice-versa. ~eq[] Ok, so now applying RT8 and RT3 you should be trying to apply the definition of ~c[some-M]. The hypothesis is just a pure (non-negated) universal quantification so you should apply RT4. A blind application lets us prove the theorem as below. ~bv[] (defthm nl1 (implies (none-M) (not (some-M))) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition some-M)) (:instance none-M-necc (x (some-M-witness))))))) ~ev[] How about the converse implication? I have deliberately written it as ~c[(implies (not (none-M)) (some-M))] instead of switching the left-hand and right-hand sides of ~c[nl1], which would have been equivalent. Again, RH8 tells us how to reason about it, in this case using RH2, and we succeed. ~bv[] (defthm nl2 (implies (not (none-M)) (some-M)) :rule-classes nil :hints ((\"Goal\" :use ((:instance (:definition none-M)) (:instance some-M-suff (x (none-M-witness))))))) ~ev[] So finally we just go through the motions of proving the equality. ~bv[] (defthmd |forall not = not exists| (equal (none-M) (not (some-M))) :hints ((\"Goal\" :use ((:instance nl1) (:instance nl2))))) ~ev[] Let us now see if we can prove a slightly more advanced theorem which can be stated informally as: If there is a natural number ~c[x] which satisfies ~c[M], then there is a least natural number ~c[y] that satisfies ~c[M]. [Note: Any time I have had to reason about existential quantification I have had to do this particular style of reasoning and state that if there is an object satisfying a predicate, then there is also a ``minimal'' object satisfying the predicate.] Let us formalize this concept. We first define the concept of existence of a natural number satisfying ~c[x]. ~bv[] (defun-sk some-nat-M () (exists x (and (natp x) (M x)))) (in-theory (disable some-nat-M some-nat-M-suff)) ~ev[] We now talk about what it means to say that ~c[x] is the least number satisfying ~c[M]. ~bv[] (defun-sk none-below (y) (forall r (implies (and (natp r) (< r y)) (not (M r)))))) (in-theory (disable none-below none-below-necc)) (defun-sk min-M () (exists y (and (M y) (natp y) (none-below y)))) (in-theory (disable min-M min-M-suff)) ~ev[] The predicate ~c[none-below] says that no natural number less than ~c[y] satisfies ~c[M]. The predicate ~c[min-M] says that there is some natural number ~c[y] satisfying ~c[M] such that ~c[none-below] holds for ~c[y]. So the formula we want to prove is: ~c[(implies (some-nat-M) (min-M))]. Since the formula requires that we prove an existential quantification, RT1 tells us to construct some object satisfying the predicate over which we are quantifying. We should then be able to instantiate ~c[min-M-suff] with this object. That predicate says that the object must be the least natural number that satisfies ~c[M]. Since such an object is uniquely computable if we know that there exists some natural number satisfying ~c[M], let us just write a recursive function to compute it. This function is ~c[least-M] below. ~bv[] (defun least-M-aux (i bound) (declare (xargs :measure (nfix (- (1+ bound) i)))) (cond ((or (not (natp i)) (not (natp bound)) (> i bound)) 0) ((M i) i) (t (least-M-aux (+ i 1) bound)))) (defun least-M (bound) (least-M-aux 0 bound)) ~ev[] Let us now reason about this function as one does typically. So we prove that this object is indeed the least natural number that satisfies ~c[M], assuming that ~c[bound] is a natural number that satisfies ~c[M]. ~bv[] (defthm least-aux-produces-an-M (implies (and (natp i) (natp bound) (<= i bound) (M bound)) (M (least-M-aux i bound)))) (defthm least-<=bound (implies (<= 0 bound) (<= (least-M-aux i bound) bound))) (defthm least-aux-produces-least (implies (and (natp i) (natp j) (natp bound) (<= i j) (<= j bound) (M j)) (<= (least-M-aux i bound) j))) (defthm least-aux-produces-natp (natp (least-M-aux i bound))) (defthmd least-is-minimal-satisfying-m (implies (and (natp bound) (natp i) (< i (least-M bound))) (not (M i))) :hints ((\"Goal\" :in-theory (disable least-aux-produces-least least-<=bound) :use ((:instance least-<=bound (i 0)) (:instance least-aux-produces-least (i 0) (j i)))))) (defthm least-has-m (implies (and (natp bound) (m bound)) (M (least-M bound)))) (defthm least-is-natp (natp (least-M bound))) ~ev[] So we have done that, and hopefully this is all that we need about ~c[least-M]. So we disable everything. ~bv[] (in-theory (disable least-M natp)) ~ev[] Now of course we note that the statement of the conjecture we are interested in has two quantifiers, an inner ~c[forall] (from ~c[none-below]) and an outer ~c[exists] (from ~c[min-M]). Since ACL2 is not very good with quantification, we hold its hands to reason with the quantifier part. So we will first prove something about the ~c[forall] and then use it to prove what we need about the ~c[exists]. ~bq[] RT9: When you face nested quantifiers, reason about each nesting separately.~eq[] So what do we want to prove about the inner quantifier? Looking carefully at the definition of ~c[none-below] we see that it is saying that for all natural numbers ~c[r] < ~c[y], ~c[(M r)] does not hold. Well, how would we want to use this fact when we want to prove our final theorem? We expect that we will instantiate ~c[min-M-suff] with the object ~c[(least-M bound)] where we know (via the outermost existential quantifier) that ~c[M] holds for ~c[bound], and we will then want to show that ~c[none-below] holds for ~c[(least-M bound)]. So let us prove that for any natural number (call it ~c[bound]), ~c[none-below] holds for ~c[(least-M bound)]. For the final theorem we only need it for natural numbers satisfying ~c[M], but note that from the lemma ~c[least-is-minimal-satisfying-m] we really do not need that ~c[bound] satisfies ~c[M]. So we are now proving: ~c[(implies (natp bound) (none-below (least-M bound)))]. Well since this is a standard case of proving a universally quantified predicate, we just apply RT3. We have proved that for all naturals ~c[i] < ~c[(least-M bound)], ~c[i] does not satisfy ~c[M] (lemma ~c[least-is-minimal-satisfying-M]), so we merely need the instantiation of that lemma with ~c[none-below-witness] of the thing we are trying to prove, that is, ~c[(least-M bound)]. The theorem below thus goes through. ~bv[] (defthm least-is-minimal (implies (natp bound) (none-below (least-M bound))) :hints ((\"Goal\" :use ((:instance (:definition none-below) (y (least-M bound))) (:instance least-is-minimal-satisfying-m (i (none-below-witness (least-M bound)))))))) ~ev[] Finally we are in the outermost existential quantifier, and are in the process of applying ~c[min-M-suff]. What object should we instantiate it with? We must instantiate it with ~c[(least-M bound)] where ~c[bound] is an object which must satisfy ~c[M] and is a natural. We have such an object, namely ~c[(some-nat-M-witness)] which we know have all these qualities given the hypothesis. So the proof now is just RT1 and RT2. ~bv[] (defthm |minimal exists| (implies (some-nat-M) (min-M)) :hints ((\"Goal\" :use ((:instance min-M-suff (y (least-M (some-nat-M-witness)))) (:instance (:definition some-nat-M)))))) ~ev[] If you are comfortable with the reasoning above, then you are comfortable with quantifiers and probably will not need these notes any more. In my opinion, the best way of dealing with ACL2 is to ask yourself why you think something is a theorem, and the rules of thumb above are simply guides to the questions that you need to ask when you are dealing with quantification. Here are a couple of simple exercises for you to test if you understand the reasoning process. ~st[Exercise 1]. Formalize and prove the following theorem. Suppose there exists ~c[x] such that ~c[(R x)] and suppose that all ~c[x] satisfy ~c[(P x)]. Then prove that there exists ~c[x] such that ~c[(P x) & (R x)]. (See ~url[http://www.cs.utexas.edu/users/moore/acl2/contrib/quantifier-exercise-1-solution.html] for a solution.) ~st[Exercise 2]. Recall the example just before the preceding exercise, where we showed that if there exists a natural number ~c[x] satisfying ~c[M] then there is another natural number ~c[y] such that ~c[y] satisfies ~c[M] and for every natural number ~c[z] < ~c[y], ~c[z] does not. What would happen if we remove the restriction of ~c[x], ~c[y], and ~c[z] being naturals? Of course, we will not talk about ~c[<] any more, but suppose you use the total order on all ACL2 objects (from community book ~c[\"books/misc/total-order\"]). More concretely, consider the definition of ~c[some-M] above. Let us now define two other functions: ~bv[] (include-book \"misc/total-order\" :dir :system) (defun-sk none-below-2 (y) (forall r (implies (<< r y) (not (M r))))) (defun-sk min-M2 () (exists y (and (M y) (none-below-2 y)))) ~ev[] The question is whether ~c[(implies (some-M) (min-M2))] is a theorem. Can you prove it? Can you disprove it?~/") (deflabel quantifiers :doc ":Doc-Section Defun-sk issues about quantification in ACL2~/ ACL2 supports first-order quantifiers ~ilc[exists] and ~ilc[forall] by way of the ~ilc[defun-sk] event. However, proof support for quantification is quite limited. Therefore, you may prefer using recursion in place of ~c[defun-sk] when possible (following common ACL2 practice).~/ For example, the notion ``every member of ~c[x] has property ~c[p]'' can be defined either with recursion or explicit quantification, but proofs may be simpler when recursion is used. We illustrate this point with two proofs of the same informal claim, one of which uses recursion which the other uses explicit quantification. Notice that with recursion, the proof goes through fully automatically; but this is far from true with explicit quantification (especially notable is the ugly hint). The informal claim for our examples is: If every member ~c[a] of each of two lists satisfies the predicate ~c[(p a)], then this holds of their ~ilc[append]; and, conversely. ~l[quantifiers-using-recursion] for a solution to this example using recursion. ~l[quantifiers-using-defun-sk] for a solution to this example using ~ilc[defun-sk]. Also ~l[quantifiers-using-defun-sk-extended] for an elaboration on that solution. But perhaps first, ~pl[defun-sk] for an ACL2 utility to introduce first-order quantification in a direct way. Examples of the use of ~c[defun-sk] are also available: ~pl[defun-sk-example] and ~pl[Tutorial4-Defun-Sk-Example] for basic examples, and ~pl[quantifier-tutorial] for a more complete, careful beginner's introduction that takes you through typical kinds of quantifier-based reasoning in ACL2.~/") (deflabel quantifiers-using-recursion :doc ":Doc-Section Quantifiers recursion for implementing quantification~/ The following example illustrates the use of recursion as a means of avoiding proof difficulties that can arise from the use of explicit quantification (via ~ilc[defun-sk]). ~l[quantifiers] for more about the context of this example.~/ ~bv[] (in-package \"ACL2\") ; We prove that if every member A of each of two lists satisfies the ; predicate (P A), then this holds of their append; and, conversely. ; Here is a solution using recursively-defined functions. (defstub p (x) t) (defun all-p (x) (if (atom x) t (and (p (car x)) (all-p (cdr x))))) (defthm all-p-append (equal (all-p (append x1 x2)) (and (all-p x1) (all-p x2)))) ~ev[]") (deflabel quantifiers-using-defun-sk :doc ":Doc-Section Quantifiers quantification example~/ ~l[quantifiers] for the context of this example. It should be compared to a corresponding example in which a simpler proof is attained by using recursion in place of explicit quantification; ~pl[quantifiers-using-recursion].~/ ~bv[] (in-package \"ACL2\") ; We prove that if every member A of each of two lists satisfies the ; predicate (P A), then this holds of their append; and, conversely. ; Here is a solution using explicit quantification. (defstub p (x) t) (defun-sk forall-p (x) (forall a (implies (member a x) (p a)))) (defthm member-append (iff (member a (append x1 x2)) (or (member a x1) (member a x2)))) (defthm forall-p-append (equal (forall-p (append x1 x2)) (and (forall-p x1) (forall-p x2))) :hints ((\"Goal\" ; ``should'' disable forall-p-necc, but no need :use ((:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x1))) (:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x2))) (:instance forall-p-necc (x x1) (a (forall-p-witness (append x1 x2)))) (:instance forall-p-necc (x x2) (a (forall-p-witness (append x1 x2)))))))) ~ev[] Also ~pl[quantifiers-using-defun-sk-extended] for an elaboration on this example.") (deflabel quantifiers-using-defun-sk-extended :doc ":Doc-Section Quantifiers quantification example with details~/ ~l[quantifiers-using-defun-sk] for the context of this example.~/ ~bv[] (in-package \"ACL2\") ; We prove that if every member A of each of two lists satisfies the ; predicate (P A), then this holds of their append; and, conversely. ; Here is a solution using explicit quantification. (defstub p (x) t) (defun-sk forall-p (x) (forall a (implies (member a x) (p a)))) ; The defun-sk above introduces the following axioms. The idea is that ; (FORALL-P-WITNESS X) picks a counterexample to (forall-p x) if there is one. ; (DEFUN FORALL-P (X) ; (LET ((A (FORALL-P-WITNESS X))) ; (IMPLIES (MEMBER A X) (P A)))) ; ; (DEFTHM FORALL-P-NECC ; (IMPLIES (NOT (IMPLIES (MEMBER A X) (P A))) ; (NOT (FORALL-P X))) ; :HINTS ((\"Goal\" :USE FORALL-P-WITNESS))) ; The following lemma seems critical. (defthm member-append (iff (member a (append x1 x2)) (or (member a x1) (member a x2)))) ; The proof of forall-p-append seems to go out to lunch, so we break into ; directions as shown below. (defthm forall-p-append-forward (implies (forall-p (append x1 x2)) (and (forall-p x1) (forall-p x2))) :hints ((\"Goal\" ; ``should'' disable forall-p-necc, but no need :use ((:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x1))) (:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x2))))))) (defthm forall-p-append-reverse (implies (and (forall-p x1) (forall-p x2)) (forall-p (append x1 x2))) :hints ((\"Goal\" :use ((:instance forall-p-necc (x x1) (a (forall-p-witness (append x1 x2)))) (:instance forall-p-necc (x x2) (a (forall-p-witness (append x1 x2)))))))) (defthm forall-p-append (equal (forall-p (append x1 x2)) (and (forall-p x1) (forall-p x2))) :hints ((\"Goal\" :use (forall-p-append-forward forall-p-append-reverse)))) ~ev[]") ; Here is the defstobj event. ; We start with the problem of finding the arguments to the defstobj event. ; The form looks likes ; (defstobj name ... field-descri ... ; :renaming alist ; :doc string) ; :inline flag) ; where the :renaming, :doc, and :inline keyword arguments are ; optional. This syntax is not supported by macros because you can't ; have an &REST arg and a &KEYS arg without all the arguments being in ; the keyword style. So we use &REST and implement the new style of ; argument recovery. ; Once we have partitioned the args for defstobj, we'll have recovered ; the field-descriptors, a renaming alist, and a doc string. Our next ; step is to check that the renaming alist is of the correct form. (defun doublet-style-symbol-to-symbol-alistp (x) (cond ((atom x) (equal x nil)) (t (and (consp (car x)) (symbolp (caar x)) (consp (cdar x)) (symbolp (cadar x)) (null (cddar x)) (doublet-style-symbol-to-symbol-alistp (cdr x)))))) ; Then, we can use the function defstobj-fnname to map the default ; symbols in the defstobj to the function names the user wants us to ; use. (It is defined elsewhere because it is needed by translate.) (defun chk-legal-defstobj-name (name state) (cond ((eq name 'state) (er soft (cons 'defstobj name) "STATE is an illegal name for a user-declared ~ single-threaded object.")) ((legal-variablep name) (value nil)) (t (er soft (cons 'defstobj name) "The symbol ~x0 may not be declared as a single-threaded object ~ name because it is not a legal variable name." name)))) (defun chk-unrestricted-guards-for-user-fns (names wrld ctx state) (cond ((null names) (value nil)) ((or (acl2-system-namep (car names) wrld) (equal (guard (car names) nil wrld) *t*)) (chk-unrestricted-guards-for-user-fns (cdr names) wrld ctx state)) (t (er soft ctx "The guard for ~x0 is ~p1. But in order to use ~x0 in the ~ type-specification of a single-threaded object it must ~ have a guard of T." (car names) (untranslate (guard (car names) nil wrld) t wrld))))) (defconst *expt2-28* (expt 2 28)) (defun fix-stobj-array-type (type wrld) ; Note: Wrld may be a world or nil. If wrld is nil and we are in raw Lisp, ; then this function should be called in a context where the symbol-value is ; available for any symbol introduced by a previous defconst event. Our ; intended use case meets that criterion: evaluation of a defstobj form during ; loading of the compiled file for a book. (let* ((max (car (caddr type))) (n (cond ((consp wrld) (let ((qc (defined-constant max wrld))) (and qc (unquote qc)))) #-acl2-loop-only ((eq wrld nil) (and (symbolp max) (symbol-value max))) (t nil)))) (cond (n (list (car type) (cadr type) (list n))) (t type)))) (defun chk-stobj-field-descriptor (name field-descriptor ctx wrld state) ; See the comment just before chk-acceptable-defstobj1 for an explanation of ; our handling of Common Lisp compliance. (cond ((symbolp field-descriptor) (value nil)) (t (er-progn (cond ((and (consp field-descriptor) (symbolp (car field-descriptor)) (keyword-value-listp (cdr field-descriptor)) (member-equal (length field-descriptor) '(1 3 5 7)) (let ((keys (odds field-descriptor))) (and (no-duplicatesp keys) (subsetp-eq keys '(:type :initially :resizable))))) (value nil)) (t (er soft ctx "The field descriptors of a single-threaded object ~ definition must be a symbolic field-name or a list of the ~ form (field-name :type type :initially val), where ~ field-name is a symbol. The :type and :initially keyword ~ assignments are optional and their order is irrelevant. ~ The purported descriptor ~x0 for a field in ~x1 is not of ~ this form." field-descriptor name))) (let* ((field (car field-descriptor)) (type (if (assoc-keyword :type (cdr field-descriptor)) (cadr (assoc-keyword :type (cdr field-descriptor))) t)) (initp (assoc-keyword :initially (cdr field-descriptor))) (init (if initp (cadr initp) nil)) (resizable (if (assoc-keyword :resizable (cdr field-descriptor)) (cadr (assoc-keyword :resizable (cdr field-descriptor))) nil))) (cond ((and resizable (not (eq resizable t))) (er soft ctx "The :resizable value in the ~x0 field of ~x1 is illegal: ~x2. ~ The legal values are t and nil." field name resizable)) ((and (consp type) (eq (car type) 'array)) (cond ((not (and (true-listp type) (equal (length type) 3) (true-listp (caddr type)) (equal (length (caddr type)) 1))) (er soft ctx "When a field descriptor specifies an ARRAY :type, the type ~ must be of the form (ARRAY etype (n)). Note that we only ~ support single-dimensional arrays. The purported ARRAY :type ~ ~x0 for the ~x1 field of ~x2 is not of this form." type field name)) (t (let* ((type0 (fix-stobj-array-type type wrld)) (etype (cadr type0)) (stobjp (stobjp etype t wrld)) (etype-term ; used only when (not stobjp) (and (not stobjp) ; optimization (translate-declaration-to-guard etype 'x wrld))) (n (car (caddr type0))) (etype-error-string "The element type specified for the ~x0 field of ~x1, ~ namely ~x2, is not recognized by ACL2 as a type-spec ~ (see :DOC type-spec) or as a user-defined stobj name.")) (cond ((not (natp n)) (er soft ctx "An array dimension must be a non-negative integer or a ~ defined constant whose value is a non-negative integer. ~ ~ The :type ~x0 for the ~x1 field of ~x2 is thus ~ illegal." type0 field name)) (stobjp ; Defstobj-raw-init-fields depends on this check. (cond ((eq etype 'state) (er soft ctx etype-error-string field name etype)) ((null initp) (value nil)) (t (er soft ctx "The :initially keyword must be omitted for a ~ :type specified as an array of stobjs. But ~ for :type ~x0, :initially is specified as ~x1 ~ for the ~x2 field of ~x3." type init field name)))) ((null etype-term) (er soft ctx etype-error-string field name etype)) (t (er-let* ((pair (simple-translate-and-eval etype-term (list (cons 'x init)) nil (msg "The type ~x0" etype-term) ctx wrld state nil))) ; pair is (tterm . val), where tterm is a term and val is its value ; under x<-init. (er-progn (chk-common-lisp-compliant-subfunctions nil (list field) (list (car pair)) wrld "auxiliary function" ctx state) (chk-unrestricted-guards-for-user-fns (all-fnnames (car pair)) wrld ctx state) (cond ((not (cdr pair)) (er soft ctx "The value specified by the :initially ~ keyword, namely ~x0, fails to satisfy the ~ declared type ~x1 in the array ~ specification for the ~x2 field of ~x3." init etype field name)) (t (value nil))))))))))) ((assoc-keyword :resizable (cdr field-descriptor)) (er soft ctx "The :resizable keyword is only legal for array types, hence is ~ illegal for the ~x0 field of ~x1." field name)) (t (let* ((stobjp (stobjp type t wrld)) (type-term ; used only when (not stobjp) (and (not stobjp) ; optimization (translate-declaration-to-guard type 'x wrld))) (type-error-string "The :type specified for the ~x0 field of ~x1, namely ~x2, ~ is not recognized by ACL2 as a type-spec (see :DOC ~ type-spec) or as a user-defined stobj name.")) (cond (stobjp ; Defstobj-raw-init-fields depends on this check. (cond ((eq type 'state) (er soft ctx type-error-string field name type)) ((null initp) (value nil)) (t (er soft ctx "The :initially keyword must be omitted for a ~ :type specified as a stobj. But for :type ~x0, ~ :initially is specified as ~x1 for the ~x2 field ~ of ~x3." type init field name)))) ((null type-term) (er soft ctx type-error-string field name type)) (t (er-let* ((pair (simple-translate-and-eval type-term (list (cons 'x init)) nil (msg "The type ~x0" type-term) ctx wrld state nil))) ; pair is (tterm . val), where tterm is a term and val is its value ; under x<-init. (er-progn (chk-common-lisp-compliant-subfunctions nil (list field) (list (car pair)) wrld "body" ctx state) (chk-unrestricted-guards-for-user-fns (all-fnnames (car pair)) wrld ctx state) (cond ((not (cdr pair)) (er soft ctx "The value specified by the :initially keyword, ~ namely ~x0, fails to satisfy the declared :type ~x1 ~ for the ~x2 field of ~x3." init type field name)) (t (value nil))))))))))))))) (defun chk-acceptable-defstobj-renaming (name field-descriptors renaming ctx state default-names) ; We collect up all the default names and then check that the domain ; of renaming contains no duplicates and is a subset of the default ; names. We already know that field-descriptors is well-formed and ; that renaming is a doublet-style symbol-to-symbol alist. (cond ((endp field-descriptors) (let ((default-names (list* (defstobj-fnname name :recognizer :top nil) (defstobj-fnname name :creator :top nil) (reverse default-names))) (domain (strip-cars renaming))) (cond ((null renaming) ; In this case, the default-names are the names the user intends us to use. (cond ((not (no-duplicatesp default-names)) (er soft ctx "The field descriptors are illegal because they require ~ the use of the same name for two different functions. ~ The duplicated name~#0~[ is~/s are~] ~&0. You must ~ change the component names so that no conflict occurs. ~ ~ You may then wish to use the :RENAMING option to ~ introduce your own names for these functions. See ~ :DOC defstobj." (duplicates default-names))) (t (value nil)))) ((not (no-duplicatesp default-names)) (er soft ctx "The field descriptors are illegal because they require ~ the use of the same default name for two different ~ functions. The duplicated default name~#0~[ is~/s are~] ~ ~&0. You must change the component names so that no ~ conflict occurs. Only then may you use the :RENAMING ~ option to rename the default names." (duplicates default-names))) ((not (no-duplicatesp domain)) (er soft ctx "No two entries in the :RENAMING alist may mention the ~ same target symbol. Your alist, ~x0, contains ~ duplications in its domain." renaming)) ((not (subsetp domain default-names)) (er soft ctx "Your :RENAMING alist, ~x0, mentions ~#1~[a function ~ symbol~/function symbols~] in its domain which ~ ~#1~[is~/are~] not among the default symbols to be ~ renamed. The offending symbol~#1~[ is~/s are~] ~&1. ~ The default defstobj names for this event are ~&2." renaming (set-difference-equal domain default-names) default-names)) (t (value nil))))) (t (let* ((field (if (atom (car field-descriptors)) (car field-descriptors) (car (car field-descriptors)))) (type (if (consp (car field-descriptors)) (or (cadr (assoc-keyword :type (cdr (car field-descriptors)))) t) t)) (key2 (if (and (consp type) (eq (car type) 'array)) :array :non-array))) (chk-acceptable-defstobj-renaming name (cdr field-descriptors) renaming ctx state (list* (defstobj-fnname field :updater key2 nil) (defstobj-fnname field :accessor key2 nil) (defstobj-fnname field :recognizer key2 nil) (cond ((eq key2 :array) (list* (defstobj-fnname field :length key2 nil) (defstobj-fnname field :resize key2 nil) default-names)) (t default-names)))))))) ; The functions introduced by defstobj are all defined with ; :VERIFY-GUARDS T. This means we must ensure that their guards and ; bodies are compliant. Most of this stuff is mechanically generated ; by us and is guaranteed to be compliant. But there is a way that a ; user defined function can sneak in. The user might use a type-spec ; such as (satisfies foo), where foo is a user defined function. ; To discuss the guard issue, we name the functions introduced by ; defstobj, following the convention used in the comment in ; defstobj-template. The recognizer for the stobj itself will be ; called namep, and the creator will be called create-name. For each ; field, the following names are introduced: recog-name - recognizer ; for the field value; accessor-name - accessor for the field; ; updater-name - updater for the field; length-name - length of array ; field; resize-name - resizing function for array field. ; We are interested in determining the conditions we must check to ; ensure that each of these functions is Common Lisp compliant. Both ; the guard and the body of each function must be compliant. ; Inspection of defstobj-axiomatic-defs reveals the following. ; Namep is defined in terms of primitives and the recog-names. The ; guard for namep is T. The body of namep is always compliant, if the ; recog-names are compliant and have guards of T. ; Create-name is a constant with a guard of T. Its body is always ; compliant. ; Recog-name has a guard of T. The body of recog-name is interesting ; from the guard verification perspective, because it may contain ; translated type-spec such as (satisfies foo) and so we must check ; that foo is compliant. We must also check that the guard of foo is ; T, because the guard of recog-name is T and we might call foo on ; anything. ; Accessor-name is not interesting: its guard is namep and its body is ; primitive. We will have checked that namep is compliant. ; Updater-name is not interesting: its guard may involve translated ; type-specs and will involve namep, but we will have checked their ; compliance already. ; Length-name and resize-name have guards that are calls of namep, and ; their bodies are known to satisfy their guards. ; So it all boils down to checking the compliance of the body of ; recog-name, for each component. Note that we must check both that ; the type-spec only involves compliant functions and that every ; non-system function used has a guard of T. (defun defconst-name (name) (intern-in-package-of-symbol (concatenate 'string "*" (symbol-name name) "*") name)) (defun chk-acceptable-defstobj1 (name field-descriptors ftemps renaming ctx wrld state names const-names) ; We check whether it is legal to define name as a single-threaded ; object with the description given in field-descriptors. We know ; name is a legal (and new) stobj name and we know that renaming is a ; symbol to symbol doublet-style alist. But we know nothing else. We ; either signal an error or return the world in which the event is to ; be processed (thus implementing redefinitions). Names is, in ; general, the actual set of names that the defstobj event will ; introduce. That is, it contains the images of the default names ; under the renaming alist. We accumulate the actual names into it as ; we go and check that it contains no duplicates at the termination of ; this function. All of the names in names are to be defined as ; functions with :VERIFY-GUARDS T. See the comment above about ; Common Lisp compliance. (cond ((endp ftemps) (let* ((recog-name (defstobj-fnname name :recognizer :top renaming)) (creator-name (defstobj-fnname name :creator :top renaming)) (names (list* recog-name creator-name names))) (er-progn (chk-all-but-new-name recog-name ctx 'function wrld state) (chk-all-but-new-name creator-name ctx 'function wrld state) (chk-acceptable-defstobj-renaming name field-descriptors renaming ctx state nil) ; Note: We insist that all the names be new. In addition to the ; obvious necessity for something like this, we note that this does ; not permit us to have redundantly defined any of these names. For ; example, the user might have already defined a field recognizer, ; PCP, that is identically defined to what we will lay down. But we ; do not allow that. We basically insist that we have control over ; every one of these names. (chk-just-new-names names 'function nil ctx wrld state) (chk-just-new-names const-names 'const nil ctx wrld state)))) (t ; An element of field-descriptors (i.e., of ftemps) is either a symbolic field ; name, field, or else of the form (field :type type :initially val), where ; either or both of the keyword fields can be omitted. Val must be an evg, ; i.e., an unquoted constant like t, nil, 0 or undef (the latter meaning the ; symbol 'undef). :Type defaults to the unrestricted type t and :initially ; defaults to nil. Type is either a primitive type, as recognized by ; translate-declaration-to-guard, or a stobj name, or else is of the form ; (array ptype (n)), where ptype is a primitive type or stobj name and n is an ; positive integer constant. If type is a stobj name or an array of such, then ; :initially must be omitted. (er-progn (chk-stobj-field-descriptor name (car ftemps) ctx wrld state) (let* ((field (if (atom (car ftemps)) (car ftemps) (car (car ftemps)))) (type (if (consp (car ftemps)) (or (cadr (assoc-keyword :type (cdr (car ftemps)))) t) t)) (key2 (if (and (consp type) (eq (car type) 'array)) :array :non-array)) (fieldp-name (defstobj-fnname field :recognizer key2 renaming)) (accessor-name (defstobj-fnname field :accessor key2 renaming)) (accessor-const-name (defconst-name accessor-name)) (updater-name (defstobj-fnname field :updater key2 renaming)) (length-name (defstobj-fnname field :length key2 renaming)) (resize-name (defstobj-fnname field :resize key2 renaming))) (er-progn (chk-all-but-new-name fieldp-name ctx 'function wrld state) (chk-all-but-new-name accessor-name ctx 'function wrld state) (chk-all-but-new-name updater-name ctx 'function wrld state) (chk-all-but-new-name accessor-const-name ctx 'const wrld state) (if (eq key2 :array) (er-progn (chk-all-but-new-name length-name ctx 'function wrld state) (chk-all-but-new-name resize-name ctx 'function wrld state)) (value nil)) (chk-acceptable-defstobj1 name field-descriptors (cdr ftemps) renaming ctx wrld state (list* fieldp-name accessor-name updater-name (if (eq key2 :array) (list* length-name resize-name names) names)) (cons accessor-const-name const-names)))))))) (defconst *defstobj-keywords* '(:renaming :doc :inline :congruent-to)) (defun defstobj-redundancy-bundle (args) ; See redundant-defstobjp to see how this is used. ; The treatment of erp below is justified as follows. If this function is used ; to compute a redundancy bundle for a new purported but ill-formed defstobj, ; the bundle will contain the symbol 'error in the field-descriptors slot, ; which will cause it not to match any correct redundancy bundle. Thus, the ; purported defstobj will not be considered redundant and the error will be ; detected by the admissions process. (mv-let (erp field-descriptors key-alist) (partition-rest-and-keyword-args args *defstobj-keywords*) (list* (if erp 'error field-descriptors) (cdr (assoc-eq :renaming key-alist)) ; We include the :congruent-to field, for example to avoid errors like the ; following. ; (defstobj st1 fld1) ; ; (encapsulate ; () ; (local (defstobj st2 fld2 fld3)) ; (defstobj st2 fld2 fld3 :congruent-to st1)) ; ; ; Raw lisp error! ; (fld3 st1) (cdr (assoc-eq :congruent-to key-alist))))) (defun old-defstobj-redundancy-bundle (name wrld) ; Name has a (non-nil) 'stobj property in the given world. We return data ; relevant for redundancy from the event associated with name in wrld. (assert$ (getprop name 'stobj nil 'current-acl2-world wrld) (let ((ev (get-event name wrld))) (and ev (assert$ (and (member-eq (car ev) '(defstobj defabsstobj)) (eq (cadr ev) name)) (defstobj-redundancy-bundle (cddr ev))))))) (defun redundant-defstobjp (name args wrld) ; Note: At one time we stored the defstobj template on the property ; list of a defstobj name and we computed the new template from args ; and compared the two templates to identify redundancy. To make this ; possible without causing runtime errors we had to check, here, that ; the arguments -- which have not yet been checked for well-formedness ; -- were at least of the right basic shape, e.g., that the renaming ; is a doublet-style-symbol-to-symbol-alistp and that each ; field-descriptor is either a symbol or a true-list of length 1, 3, ; or 5 with :type and :initially fields. But this idea suffered the ; unfortunate feature that an illegal defstobj event could be ; considered redundant. For example, if the illegal event had a ; renaming that included an unnecessary function symbol in its domain, ; that error was not caught. The bad renaming produced a good ; template and if a correct version of that defstobj had previously ; been executed, the bad one was recognized as redundant. ; Unfortunately, if one were to execute the bad one first, an error ; would result. ; So we have changed this function to be extremely simple. (and (getprop name 'stobj nil 'current-acl2-world wrld) (equal (old-defstobj-redundancy-bundle name wrld) (defstobj-redundancy-bundle args)))) (defun congruent-stobj-fields (fields1 fields2) (cond ((endp fields1) (null fields2)) (t (let ((x1 (car fields1)) (x2 (car fields2))) (and (if (symbolp x1) (symbolp x2) (and (consp x1) (consp x2) (equal (cdr x1) (cdr x2)))) (congruent-stobj-fields (cdr fields1) (cdr fields2))))))) (defun chk-acceptable-defstobj (name args ctx wrld state) ; We check that (defstobj name . args) is well-formed and either ; signal an error or return nil. (mv-let (erp field-descriptors key-alist) (partition-rest-and-keyword-args args *defstobj-keywords*) (cond (erp (er soft ctx "The keyword arguments to the DEFSTOBJ event must appear ~ after all field descriptors. The allowed keyword ~ arguments are ~&0, and these may not be duplicated, and ~ must be followed by the corresponding value of the keyword ~ argument. Thus, ~x1 is ill-formed." *defstobj-keywords* (list* 'defstobj name args))) (t (let ((renaming (cdr (assoc-eq :renaming key-alist))) (doc (cdr (assoc-eq :doc key-alist))) (inline (cdr (assoc-eq :inline key-alist))) (congruent-to (cdr (assoc-eq :congruent-to key-alist)))) (cond ((redundant-defstobjp name args wrld) (value 'redundant)) ((not (booleanp inline)) (er soft ctx "DEFSTOBJ requires the :INLINE keyword argument to have a Boolean ~ value. See :DOC defstobj.")) ((and congruent-to (not (stobjp congruent-to t wrld))) (er soft ctx "The :CONGRUENT-TO field of a DEFSTOBJ must either be nil or the ~ name of an existing stobj, but the value ~x0 is neither. See ~ :DOC defstobj." congruent-to)) ((and congruent-to ; hence stobjp holds, hence symbolp holds (getprop congruent-to 'absstobj-info nil 'current-acl2-world wrld)) (er soft ctx "The symbol ~x0 is the name of an abstract stobj in the current ~ ACL2 world, so it is not legal for use as the :CONGRUENT-TO ~ argument of DEFSTOBJ." congruent-to)) ((and congruent-to (not (congruent-stobj-fields field-descriptors (car (old-defstobj-redundancy-bundle congruent-to wrld))))) (er soft ctx "A non-nil :CONGRUENT-TO field of a DEFSTOBJ must be the name of ~ a stobj that has the same shape as the proposed new stobj. ~ However, the proposed stobj named ~x0 does not have the same ~ shape as the existing stobj named ~x1. See :DOC defstobj." name congruent-to)) (t (er-progn ; The defstobj name itself is not subject to renaming. So we check it ; before we even bother to check the well-formedness of the renaming alist. (chk-all-but-new-name name ctx 'stobj wrld state) (cond ((or (eq name 'I) (eq name 'V)) (er soft ctx "DEFSTOBJ does not allow single-threaded objects with ~ the names I or V because those symbols are used as ~ formals, along with the new stobj name itself, in ~ ``primitive'' stobj functions that will be ~ defined.")) (t (value nil))) (chk-legal-defstobj-name name state) (cond ((not (doublet-style-symbol-to-symbol-alistp renaming)) (er soft ctx "The :RENAMING argument to DEFSTOBJ must be an ~ alist containing elements of the form (sym ~ sym), where each element of such a doublet is a ~ symbol. Your argument, ~x0, is thus illegal." renaming)) (t (value nil))) ; We use translate-doc here just to check the string. We throw away ; the section-symbol and citations returned. We'll repeat this later. (translate-doc name doc ctx state) (er-let* ((wrld1 (chk-just-new-name name 'stobj nil ctx wrld state)) (wrld2 (chk-just-new-name (the-live-var name) 'stobj-live-var nil ctx wrld1 state))) (chk-acceptable-defstobj1 name field-descriptors field-descriptors renaming ctx wrld2 state nil nil)))))))))) ; Essay on Defstobj Definitions ; Consider the following defstobj: ; (defstobj $st ; (flag :type t :initially run) ; (pc :type (integer 0 255) :initially 128) ; (mem :type (array (integer 0 255) (256)) :initially 0) ; :renaming ((pc pcn))) ; If you call (defstobj-template '$st '((flag ...) ...)) you will get ; back a ``template'' which is sort of a normalized version of the ; event with the renaming applied and all the optional slots filled ; appropriately. (See the definition of defstobj-template for details.) ; Let template be that template. ; To see the logical definitions generated by this defstobj event, invoke ; (defstobj-axiomatic-defs '$st template (w state)) ; To see the raw lisp definitions generated, invoke ; (defstobj-raw-defs '$st template nil (w state)) ; The *1* functions for the functions are all generated by oneifying ; the axiomatic defs. ; To see the deconsts generated, invoke ; (defstobj-defconsts (strip-accessor-names (caddr template)) 0) ; It is important the guard conjectures for these functions be ; provable! They are assumed by the admission process! To prove ; the guards for the defstobj above, it helped to insert the following ; lemma after the defun of memp but before the definition of memi. ; (defthm memp-implies-true-listp ; (implies (memp x) ; (true-listp x))) ; Even without this lemma, the proof succeeded, though it took much ; longer and involved quite a few generalizations and inductions. ; If you change any of the functions, I recommend generating the axiomatic ; defs for a particular defstobj such as that above and proving the guards. ; Up through v2-7 we also believed that we ensured that the guards in the ; axiomatic defs are sufficient for the raw defs. However, starting with v2-8, ; this became moot because of the following claim: the raw Lisp functions are ; only called on live stobjs (this change, and others involving :inline, were ; contributed by Rob Sumners). We believe this claim because of the following ; argument. Note that there is an exception for the recognizer, which can be ; applied to an ordinary object, but we do not consider this exception here. ; ; a) The *1* function now has an additional requirement that not only does ; guard checking pass, but also, all of the stobjs arguments passed in ; must be the live stobjs in order to execute raw Common Lisp. ; b) Due to the syntactic restrictions that ACL2 enforces, we know that the ; direct correspondence between live stobjs and stobj arguments in the ; raw Common Lisp functions will persist throughout evaluation. ; -- This can be proven by induction over the sequence of function calls ; in any evaluation. ; -- The base case is covered by the binding of stobj parameters to ; the global live stobj in the acl2-loop, or by the restrictions ; placed upon with-local-stobj and stobj-let. ; -- The induction step is proven by the signature requirements of ; functions that access and/or update stobjs. ; A reasonable question is: Should the guard for resize-name be ; strengthened so as to disallow sizes of at least (1- (expt 2 28))? ; Probably there is no need for this. Logically, there is no such ; restriction; it is OK for the implementation to insist on such a ; bound when actually executing. ; Now we introduce the idea of the "template" of a defstobj, which ; includes a normalized version of the field descriptors under the ; renaming. (defun defstobj-fields-template (field-descriptors renaming wrld) ; Note: Wrld may be a world or nil. See fix-stobj-array-type. (cond ((endp field-descriptors) nil) (t (let* ((field (if (atom (car field-descriptors)) (car field-descriptors) (car (car field-descriptors)))) (type (if (consp (car field-descriptors)) (or (cadr (assoc-keyword :type (cdr (car field-descriptors)))) t) t)) (init (if (consp (car field-descriptors)) (cadr (assoc-keyword :initially (cdr (car field-descriptors)))) nil)) (resizable (if (consp (car field-descriptors)) (cadr (assoc-keyword :resizable (cdr (car field-descriptors)))) nil)) (key2 (if (and (consp type) (eq (car type) 'array)) :array :non-array)) (fieldp-name (defstobj-fnname field :recognizer key2 renaming)) (accessor-name (defstobj-fnname field :accessor key2 renaming)) (updater-name (defstobj-fnname field :updater key2 renaming)) (resize-name (defstobj-fnname field :resize key2 renaming)) (length-name (defstobj-fnname field :length key2 renaming))) (cons (list fieldp-name (cond ((and (consp type) (eq (car type) 'array)) (fix-stobj-array-type type wrld)) (t type)) init accessor-name updater-name length-name resize-name resizable) (defstobj-fields-template (cdr field-descriptors) renaming wrld)))))) (defun defstobj-template (name args wrld) ; Note: Wrld may be a world or nil. See fix-stobj-array-type. ; We unpack the args to get the renamed field descriptors. We return a list of ; the form (namep create-name fields doc inline congruent-to), where: namep is ; the name of the recognizer for the single-threaded object; create-name is the ; name of the constructor for the stobj; fields is a list corresponding to the ; field descriptors, but normalized with respect to the renaming, types, etc.; ; doc is the doc string, or nil if no doc string is supplied; inline is t if ; :inline t was specified in the defstobj event, else nil; and congruent-to is ; the :congruent-to field of the defstobj event (default: nil). A field in ; fields is of the form (recog-name type init accessor-name updater-name ; length-name resize-name resizable). The last three fields are nil unless ; type has the form (ARRAY ptype (n)), in which case ptype is a primitive type ; and n is a positive integer. Init is the evg of a constant term, i.e., ; should be quoted to be a treated as a term. Doc is the value of the :doc ; keyword arg in args. (mv-let (erp field-descriptors key-alist) (partition-rest-and-keyword-args args *defstobj-keywords*) (cond (erp ; If the defstobj has been admitted, this won't happen. (er hard 'defstobj "The keyword arguments to the DEFSTOBJ event must appear ~ after all field descriptors. The allowed keyword ~ arguments are ~&0, and these may not be duplicated. Thus, ~ ~x1 is ill-formed." *defstobj-keywords* (list* 'defstobj name args))) (t (let ((renaming (cdr (assoc-eq :renaming key-alist))) (doc (cdr (assoc-eq :doc key-alist))) (inline (cdr (assoc-eq :inline key-alist))) (congruent-to (cdr (assoc-eq :congruent-to key-alist)))) (list (defstobj-fnname name :recognizer :top renaming) (defstobj-fnname name :creator :top renaming) (defstobj-fields-template field-descriptors renaming wrld) doc inline congruent-to)))))) (defun defstobj-component-recognizer-calls (ftemps n var ans) ; Warning: See the guard remarks in the Essay on Defstobj Definitions. ; Given a list of field templates, e.g., ((regp ...) (pcp ...) ...), ; where n is one less than the number of fields and var is some ; symbol, v, we return ((regp (nth 0 v)) (pcp (nth 1 v)) ...). Except, ; if field represents a non-resizable array then we also include a ; corresponding length statement in the list. (cond ((endp ftemps) (reverse ans)) (t (defstobj-component-recognizer-calls (cdr ftemps) (+ n 1) var (let* ((type (cadr (car ftemps))) (nonresizable-ar (and (consp type) (eq (car type) 'array) (not (nth 7 (car ftemps))))) (pred-stmt `(,(car (car ftemps)) (nth ,n ,var)))) (if nonresizable-ar (list* `(equal (len (nth ,n ,var)) ,(car (caddr type))) pred-stmt ans) (cons pred-stmt ans))))))) (defun defstobj-component-recognizer-axiomatic-defs (name template ftemps wrld) ; Warning: See the guard remarks in the Essay on Defstobj Definitions. ; It is permissible for wrld to be nil, as this merely defeats additional ; checking by translate-declaration-to-guard. ; We return a list of defs (see defstobj-axiomatic-defs) for all the ; recognizers for the single-threaded resource named name with the given ; template. The answer contains the top-level recognizer as well as the ; definitions of all component recognizers. The answer contains defs for ; auxiliary functions used in array component recognizers. The defs are listed ; in an order suitable for processing (components first, then top-level). (cond ((endp ftemps) (let* ((recog-name (car template)) (field-templates (caddr template)) (n (length field-templates))) ; Rockwell Addition: See comment below. ; Note: The recognizer for a stobj must be Boolean! That is why we ; conclude the AND below with a final T. The individual field ; recognizers need not be Boolean and sometimes are not! For example, ; a field with :TYPE (MEMBER e1 ... ek) won't be Boolean, nor with ; certain :TYPE (OR ...) involving MEMBER. The reason we want the ; stobj recognizer to be Boolean is so that we can replace it by T in ; guard conjectures for functions that have been translated with the ; stobj syntactic restrictions. See optimize-stobj-recognizers. (list `(,recog-name (,name) (declare (xargs :guard t :verify-guards t)) (and (true-listp ,name) (= (length ,name) ,n) ,@(defstobj-component-recognizer-calls field-templates 0 name nil) t))))) (t (let ((recog-name (nth 0 (car ftemps))) (type (nth 1 (car ftemps)))) ; Below we simply append the def or defs for this field to those for ; the rest. We get two defs for each array field and one def for each ; of the others. (cons (cond ((and (consp type) (eq (car type) 'array)) (let ((etype (cadr type))) `(,recog-name (x) (declare (xargs :guard t :verify-guards t)) (if (atom x) (equal x nil) (and ,(translate-stobj-type-to-guard etype '(car x) wrld) (,recog-name (cdr x))))))) (t (let ((type-term (translate-stobj-type-to-guard type 'x wrld))) ; We might not use x in the type-term and so have to declare it ignorable. `(,recog-name (x) (declare (xargs :guard t :verify-guards t) (ignorable x)) ,type-term)))) (defstobj-component-recognizer-axiomatic-defs name template (cdr ftemps) wrld)))))) (defun defstobj-field-fns-axiomatic-defs (top-recog var n ftemps wrld) ; Wrld is normally a logical world, but it can be nil when calling this ; function from raw Lisp. ; Warning: Keep the formals in the definitions below in sync with corresponding ; formals defstobj-field-fns-raw-defs. Otherwise trace$ may not work ; correctly; we saw such a problem in Version_5.0 for a resize function. ; Warning: See the guard remarks in the Essay on Defstobj Definitions. ; We return a list of defs (see defstobj-axiomatic-defs) for all the accessors, ; updaters, and optionally, array resizing and length, of a single-threaded ; resource. ; Warning: Each updater definition should immediately follow the corresponding ; accessor definition, so that this is the case for the list of definitions ; returned by defstobj-axiomatic-defs. That list of definitions becomes the ; 'stobj property laid down by defstobj-fn, and function ; chk-stobj-let/updaters1 assumes that it will find each updater definition in ; that property immediately after the corresponding accessor definition. (cond ((endp ftemps) nil) (t (let* ((field-template (car ftemps)) (type (nth 1 field-template)) (init (nth 2 field-template)) (arrayp (and (consp type) (eq (car type) 'array))) (type-term ; used in guard (and (not arrayp) ; else type-term is not used (if (null wrld) ; called from raw Lisp, so guard is ignored t (translate-stobj-type-to-guard type 'v wrld)))) (array-etype (and arrayp (cadr type))) (array-etype-term ; used in guard (and arrayp ; else array-etype-term is not used (if (null wrld) ; called from raw Lisp, so guard is ignored t (translate-stobj-type-to-guard array-etype 'v wrld)))) (array-length (and arrayp (car (caddr type)))) (accessor-name (nth 3 field-template)) (updater-name (nth 4 field-template)) (length-name (nth 5 field-template)) (resize-name (nth 6 field-template)) (resizable (nth 7 field-template))) (cond (arrayp (append `((,length-name (,var) (declare (xargs :guard (,top-recog ,var) :verify-guards t) ,@(and (not resizable) `((ignore ,var)))) ,(if resizable `(len (nth ,n ,var)) `,array-length)) (,resize-name (i ,var) (declare (xargs :guard (,top-recog ,var) :verify-guards t) ,@(and (not resizable) '((ignore i)))) ,(if resizable `(update-nth ,n (resize-list (nth ,n ,var) i ',init) ,var) `(prog2$ (hard-error ',resize-name "The array field corresponding to accessor ~x0 of ~ stobj ~x1 was not declared :resizable t. ~ Therefore, it is illegal to resize this array." (list (cons #\0 ',accessor-name) (cons #\1 ',var))) ,var))) (,accessor-name (i ,var) (declare (xargs :guard (and (,top-recog ,var) (integerp i) (<= 0 i) (< i (,length-name ,var))) :verify-guards t)) (nth i (nth ,n ,var))) (,updater-name (i v ,var) (declare (xargs :guard (and (,top-recog ,var) (integerp i) (<= 0 i) (< i (,length-name ,var)) ,@(if (eq array-etype-term t) nil (list array-etype-term))) :verify-guards t)) (update-nth-array ,n i v ,var))) (defstobj-field-fns-axiomatic-defs top-recog var (+ n 1) (cdr ftemps) wrld))) (t (append `((,accessor-name (,var) (declare (xargs :guard (,top-recog ,var) :verify-guards t)) (nth ,n ,var)) (,updater-name (v ,var) (declare (xargs :guard ,(if (eq type-term t) `(,top-recog ,var) `(and ,type-term (,top-recog ,var))) :verify-guards t)) (update-nth ,n v ,var))) (defstobj-field-fns-axiomatic-defs top-recog var (+ n 1) (cdr ftemps) wrld)))))))) (defun defstobj-axiomatic-init-fields (ftemps wrld) ; Keep this in sync with defstobj-raw-init-fields. (cond ((endp ftemps) nil) (t (let* ((field-template (car ftemps)) (type (nth 1 field-template)) (arrayp (and (consp type) (eq (car type) 'array))) (array-size (and arrayp (car (caddr type)))) (init0 (nth 2 field-template)) (creator (get-stobj-creator (if arrayp (cadr type) type) wrld)) (init (if creator `(non-exec (,creator)) (kwote init0)))) (cond (arrayp (cons `(make-list ,array-size :initial-element ,init) (defstobj-axiomatic-init-fields (cdr ftemps) wrld))) (t ; whether the type is given or not is irrelevant (cons init (defstobj-axiomatic-init-fields (cdr ftemps) wrld)))))))) (defun defstobj-creator-fn (creator-name field-templates wrld) ; This function generates the logic initialization code for the given stobj ; name. `(,creator-name () (declare (xargs :guard t :verify-guards t)) (list ,@(defstobj-axiomatic-init-fields field-templates wrld)))) (defun defstobj-axiomatic-defs (name template wrld) ; Warning: See the guard remarks in the Essay on Defstobj Definitions. ; Template is the defstobj-template for name and args and thus ; corresponds to some (defstobj name . args) event. We generate the ; #+acl2-loop-only defs for that event and return a list of defs. For ; each def it is the case that (defun . def) is a legal defun; and ; these defs can be executed in the order returned. ; These defs are processed to axiomatize the recognizer, accessor and ; updater functions for the single-threaded resource. They are also ; oneified when we process the defstobj CLTL-COMMAND to define the *1* ; versions of the functions. Finally, parts of them are re-used in ; raw lisp code when the code is applied to an object other than the ; live one. ; WARNING: If you change the formals of these generated axiomatic defs, be sure ; to change the formals of the corresponding raw defs. ; Warning: Each updater definition in the list returned should immediately ; follow the corresponding accessor definition, as guaranteed by the call of ; defstobj-field-fns-axiomatic-defs, below. This is important because ; defstobj-axiomatic-defs provides the 'stobj property laid down by ; defstobj-fn, and the function chk-stobj-let/updaters1 assumes that it will ; find each updater definition in that property immediately after the ; corresponding accessor definition. ; See the Essay on Defstobj Definitions. (let ((field-templates (caddr template))) (append (defstobj-component-recognizer-axiomatic-defs name template field-templates wrld) (cons (defstobj-creator-fn (cadr template) field-templates wrld) (defstobj-field-fns-axiomatic-defs (car template) name 0 field-templates wrld))))) (defun simple-array-type (array-etype dimensions) (declare (ignore dimensions)) (cond ((eq array-etype t) `(simple-vector *)) ((eq array-etype '*) (er hard 'simple-array-type "Implementation error: We had thought that * is an invalid type-spec! ~ ~ Please contact the ACL2 implementors.")) (t `(simple-array ,array-etype (*))))) #-acl2-loop-only (defun-one-output stobj-copy-array-aref (a1 a2 i n) (declare (type (unsigned-byte 29) i n)) ; Copy the first n elements of array a1 into array a2, starting with index i, ; and then return a2. See also copy-array-svref and stobj-copy-array-fix-aref. ; Note that this copying does not copy substructures, so in the case that a1 is ; an array of stobjs, if 0 <= i < n then the ith element of a1 will be EQ to ; the ith element of a2 after the copy is complete. (cond ((>= i n) a2) (t (setf (aref a2 i) (aref a1 i)) (stobj-copy-array-aref a1 a2 (the (unsigned-byte 29) (1+ i)) (the (unsigned-byte 29) n))))) #-acl2-loop-only (defun-one-output stobj-copy-array-svref (a1 a2 i n) (declare (type (unsigned-byte 29) i n) (type simple-vector a1 a2)) ; This is a variant of copy-array-aref for simple vectors a1 and a2. (cond ((>= i n) a2) (t (setf (svref a2 i) (svref a1 i)) (stobj-copy-array-svref a1 a2 (the (unsigned-byte 29) (1+ i)) (the (unsigned-byte 29) n))))) #-acl2-loop-only (defun-one-output stobj-copy-array-fix-aref (a1 a2 i n) #+gcl ; declaration causes errors in cmucl and sbcl and may not be necessary ; except in gcl (to avoid boxing) (declare (type (unsigned-byte 29) i n) (type (simple-array (signed-byte 29) (*)) a1 a2)) ; This is a variant of copy-array-aref for arrays of fixnums a1 and a2. We ; need this special version to avoid fixnum boxing in GCL during resizing. (cond ((>= i n) a2) (t (setf (aref a2 i) (aref a1 i)) (stobj-copy-array-fix-aref a1 a2 (the (unsigned-byte 29) (1+ i)) (the (unsigned-byte 29) n))))) (defmacro live-stobjp (name) ; Through Version_4.3, this macro was called the-live-stobj, and its body was ; `(eq ,name ,(the-live-var name)). However, we need a more permissive ; definition in support of congruent stobjs (and perhaps local stobjs and stobj ; fields of nested stobjs). Note that no ACL2 object is a simple-vector; in ; particular, a string is a vector but not a simple-vector. `(typep ,name 'simple-vector)) (defun array-etype-is-fixnum-type (array-etype) (declare (xargs :guard (implies (consp array-etype) (true-listp array-etype)))) (and (consp array-etype) (case (car array-etype) (integer (let* ((e1 (cadr array-etype)) (int1 (if (integerp e1) e1 (and (consp e1) (integerp (car e1)) (1- (car e1))))) (e2 (caddr array-etype)) (int2 (if (integerp e2) e2 (and (consp e2) (integerp (car e2)) (1- (car e2)))))) (and int1 int2 (>= int1 (- *expt2-28*)) (< int2 *expt2-28*)))) (mod (and (integerp (cadr array-etype)) (< (cadr array-etype) *expt2-28*))) (unsigned-byte (and (integerp (cadr array-etype)) (<= (cadr array-etype) 29))) (signed-byte (and (integerp (cadr array-etype)) (<= (cadr array-etype) 30)))))) (defun defstobj-field-fns-raw-defs (var flush-var inline n ftemps) ; Warning: Keep the formals in the definitions below in sync with corresponding ; formals defstobj-field-fns-raw-defs. Otherwise trace$ may not work ; correctly; we saw such a problem in Version_5.0 for a resize function. ; Warning: See the guard remarks in the Essay on Defstobj Definitions. #-hons (declare (ignorable flush-var)) ; irrelevant var without hons (cond ((endp ftemps) nil) (t (append (let* ((field-template (car ftemps)) (type (nth 1 field-template)) (init (nth 2 field-template)) (arrayp (and (consp type) (eq (car type) 'array))) (array-etype0 (and arrayp (cadr type))) (stobj-creator (get-stobj-creator (if arrayp array-etype0 type) nil)) (scalar-type (if stobj-creator t type)) ; only used when (not arrayp) (array-etype (and arrayp (if stobj-creator ; Stobj-creator is non-nil when array-etype is a stobj. The real element type, ; then, is simple-array rather than a simple-array-type, so we might say that ; the parent stobj array is not simple. But we will assume that the advantage ; of having a simple-vector for the parent stobj outweighs the advantage of ; having a simple-vector element type declaration. t array-etype0))) (simple-type (and arrayp (simple-array-type array-etype (caddr type)))) (array-length (and arrayp (car (caddr type)))) (vref (and arrayp (if (eq (car simple-type) 'simple-vector) 'svref 'aref))) (fix-vref (and arrayp (if (array-etype-is-fixnum-type array-etype) 'fix-aref vref))) (accessor-name (nth 3 field-template)) (updater-name (nth 4 field-template)) (length-name (nth 5 field-template)) (resize-name (nth 6 field-template)) (resizable (nth 7 field-template))) (cond (arrayp `((,length-name (,var) ,@(and inline (list *stobj-inline-declare*)) ,@(if (not resizable) `((declare (ignore ,var)) ,array-length) `((the (and fixnum (integer 0 *)) (length (svref ,var ,n)))))) (,resize-name (i ,var) ,@(if (not resizable) `((declare (ignore i)) (prog2$ (er hard ',resize-name "The array field corresponding to accessor ~x0 of ~ stobj ~x1 was not declared :resizable t. ~ Therefore, it is illegal to resize this array." ',accessor-name ',var) ,var)) `((if (not (and (integerp i) (>= i 0) (< i array-dimension-limit))) (hard-error ',resize-name "Attempted array resize failed because the requested ~ size ~x0 was not a nonnegative integer less than the ~ value of Common Lisp constant array-dimension-limit, ~ which is ~x1. These bounds on array sizes are fixed ~ by ACL2." (list (cons #\0 i) (cons #\1 array-dimension-limit))) (let* ((var ,var) (old (svref var ,n)) (min-index (min i (length old))) (new (make-array$ i ; The :initial-element below is probably not necessary in the case ; that we are downsizing the array. At least, CLtL2 does not make any ; requirements about specifying an :initial-element, even when an ; :element-type is supplied. However, it seems harmless enough to go ; ahead and specify :initial-element even for downsizing: resizing is ; not expected to be fast, we save a case split here (at the expense ; of this comment!), and besides, we are protecting against the ; possibility that some Common Lisp will fail to respect the spec and ; will cause an error by trying to initialize a fixnum array (say) ; with NILs. :initial-element ',init :element-type ',array-etype))) #+hons (memoize-flush ,flush-var) (setf (svref var ,n) (,(pack2 'stobj-copy-array- fix-vref) old new 0 min-index)) ,@(and stobj-creator `((when (< (length old) i) (loop for j from (length old) to (1- i) do (setf (svref new j) (,stobj-creator)))))) var))))) (,accessor-name (i ,var) (declare (type (and fixnum (integer 0 *)) i)) ,@(and inline (list *stobj-inline-declare*)) (the ,array-etype (,vref (the ,simple-type (svref ,var ,n)) (the (and fixnum (integer 0 *)) i)))) (,updater-name (i v ,var) (declare (type (and fixnum (integer 0 *)) i) (type ,array-etype v)) ,@(and inline (list *stobj-inline-declare*)) (progn #+hons (memoize-flush ,flush-var) ; See the long comment below for the updater in the scalar case, about ; supporting *1* functions. (setf (,vref (the ,simple-type (svref ,var ,n)) (the (and fixnum (integer 0 *)) i)) (the ,array-etype v)) ,var)))) ((eq scalar-type t) `((,accessor-name (,var) ,@(and inline (list *stobj-inline-declare*)) (svref ,var ,n)) (,updater-name (v ,var) ,@(and inline (list *stobj-inline-declare*)) (progn #+hons (memoize-flush ,flush-var) ; For the case of a stobj field, we considered causing an error here since the ; raw Lisp code for stobj-let avoids calling updaters because there is no need: ; updates for fields that are stobjs have already updated destructively. ; However, a raw Lisp updater can be called by a *1* function, say *1*f, ; applied to live stobjs, when guard checking does not pass control to the raw ; Lisp function, f. Perhaps we could optimize to avoid this, but there is no ; need; this setf is fast and is only called on behalf of executing *1* ; function calls. See the comment referencing "defstobj-field-fns-raw-defs" in ; community book misc/nested-stobj-tests.lisp. To see this point in action, ; evaluate the forms under that comment after modifying this definition by ; uncommenting the following line of code. ; ,@(when stobj-creator '((break$))) ; see just above (setf (svref ,var ,n) v) ,var)))) (t (assert$ (not stobj-creator) ; scalar-type is t for stobj-creator `((,accessor-name (,var) ,@(and inline (list *stobj-inline-declare*)) (the ,scalar-type (aref (the (simple-array ,scalar-type (1)) (svref ,var ,n)) 0))) (,updater-name (v ,var) (declare (type ,scalar-type v)) ,@(and inline (list *stobj-inline-declare*)) (progn #+hons (memoize-flush ,flush-var) (setf (aref (the (simple-array ,scalar-type (1)) (svref ,var ,n)) 0) (the ,scalar-type v)) ,var))))))) (defstobj-field-fns-raw-defs var flush-var inline (1+ n) (cdr ftemps)))))) (defun defstobj-raw-init-fields (ftemps) ; Keep this in sync with defstobj-axiomatic-init-fields. (cond ((endp ftemps) nil) (t (let* ((field-template (car ftemps)) (type (nth 1 field-template)) (arrayp (and (consp type) (eq (car type) 'array))) (array-etype0 (and arrayp (cadr type))) (array-size (and arrayp (car (caddr type)))) (stobj-creator (get-stobj-creator (if arrayp array-etype0 type) nil)) (array-etype (and arrayp ; See comment for this binding in defstobj-field-fns-raw-defs. (if stobj-creator t array-etype0))) (init (nth 2 field-template))) (cond (arrayp (cons (cond (stobj-creator (assert$ (null init) ; checked by chk-stobj-field-descriptor (assert$ ; We expect array-size to be a natural number, as this is checked by ; chk-stobj-field-descriptor (using fix-stobj-array-type). It is important ; that array-size not be a Lisp form that references the variable AR, even ; after macroexpasion, in order to avoid capture by the binding of AR below. (natp array-size) `(let ((ar (make-array$ ,array-size ; Do not be tempted to use :initial-element (,stobj-creator) here, because that ; would presumably share structure among all the created stobjs. :element-type ',array-etype))) (loop for i from 0 to ,(1- array-size) do (setf (svref ar i) (,stobj-creator))) ar)))) (t `(make-array$ ,array-size :element-type ',array-etype :initial-element ',init))) (defstobj-raw-init-fields (cdr ftemps)))) ((eq type t) (cons (kwote init) (defstobj-raw-init-fields (cdr ftemps)))) (stobj-creator (cons `(,stobj-creator) (defstobj-raw-init-fields (cdr ftemps)))) (t (cons `(make-array$ 1 :element-type ',type :initial-element ',init) (defstobj-raw-init-fields (cdr ftemps))))))))) (defun defstobj-raw-init (template) ; This function generates the initialization code for the live object ; representing the stobj name. (let ((field-templates (caddr template))) `(vector ,@(defstobj-raw-init-fields field-templates)))) (defun defstobj-raw-defs (name template congruent-stobj-rep wrld) ; Warning: See the guard remarks in the Essay on Defstobj Definitions. ; This function generates a list of defs. Each def is such that ; (defun . def) is a well-formed raw Lisp definition. The defuns can ; be executed in raw lisp to define the versions of the recognizers, ; accessors, and updaters (and for array fields, length and resize ; functions) that are run when we know the guards are satisfied. Many ; of these functions anticipate application to the live object itself. ; It is permissible for wrld to be nil, as this merely defeats additional ; checking by translate-declaration-to-guard. If wrld is nil, then ; congruent-stobj-rep should be the result of calling congruent-stobj-rep on ; name and the world where the corresponding defstobj is executed. If wrld is ; non-nil, then it should be an ACL2 world and congruent-stobj-rep is ; irrelevant. ; WARNING: If you change the formals of these generated raw defs be ; sure to change the formals of the corresponding axiomatic defs. (let* ((recog (first template)) (creator (second template)) (field-templates (third template)) (inline (fifth template))) (append (all-but-last (defstobj-component-recognizer-axiomatic-defs name template field-templates wrld)) `((,recog (,name) (cond ((live-stobjp ,name) t) (t (and (true-listp ,name) (= (length ,name) ,(length field-templates)) ,@(defstobj-component-recognizer-calls field-templates 0 name nil))))) (,creator () ,(defstobj-raw-init template)) ,@(defstobj-field-fns-raw-defs name (if wrld (congruent-stobj-rep (or (sixth template) name) wrld) congruent-stobj-rep) inline 0 field-templates))))) (defun put-stobjs-in-and-outs1 (name ftemps wrld) ; See put-stobjs-in-and-outs for a table that explains what we're doing. (cond ((endp ftemps) wrld) (t (let ((type (nth 1 (car ftemps))) (acc-fn (nth 3 (car ftemps))) (upd-fn (nth 4 (car ftemps))) (length-fn (nth 5 (car ftemps))) (resize-fn (nth 6 (car ftemps)))) (put-stobjs-in-and-outs1 name (cdr ftemps) (cond ((and (consp type) (eq (car type) 'array)) (let* ((etype (cadr type)) (stobj-flg (and (stobjp etype t wrld) etype))) (putprop length-fn 'stobjs-in (list name) (putprop resize-fn 'stobjs-in (list nil name) (putprop resize-fn 'stobjs-out (list name) (putprop acc-fn 'stobjs-in (list nil name) (putprop-unless acc-fn 'stobjs-out (list stobj-flg) '(nil) (putprop upd-fn 'stobjs-in (list nil stobj-flg name) (putprop upd-fn 'stobjs-out (list name) wrld))))))))) (t (let ((stobj-flg (and (stobjp type t wrld) type))) (putprop acc-fn 'stobjs-in (list name) (putprop-unless acc-fn 'stobjs-out (list stobj-flg) '(nil) (putprop upd-fn 'stobjs-in (list stobj-flg name) (putprop upd-fn 'stobjs-out (list name) wrld)))))))))))) (defun put-stobjs-in-and-outs (name template wrld) ; We are processing a (defstobj name . args) event for which template ; is the template. Wrld is a world containing the definitions of the ; accessors, updaters and recognizers of the stobj -- all of which ; were processed before we declared that name is a stobj. Wrld now ; also contains the belated declaration that name is a stobj. We now ; put the STOBJS-IN and STOBJS-OUT properties for the appropriate ; names. ; Relevant functions and their settings: ; fn stobjs-in stobjs-out ; topmost recognizer (name) (nil) ; creator () (name) ; field recogs (nil ...) (nil) ; simple accessor (name) (nil) ; array accessor (nil name) (nil) ; simple updater (nil name) (name) ; array updater (nil nil name) (name) ; The entries above not involving name were correctly computed before ; we knew that name was a stobj and hence are correct in wrld now. ; It is important to realize, in the case of the topmost recognizer ; and the accessors -- which do not return stobjs, that the appearance ; of name in the stobjs-in setting can be interpreted to mean ``the ; stobj name MAY be supplied here'' as opposed to ``MUST be supplied ; here.'' (let ((recog-name (car template)) (creator-name (cadr template)) (field-templates (caddr template))) ; Each element of field templates is of the form: ; 0 1 2 3 4 5 ; (field-recog field-recog-helper-fn type init accessor updater) ; or, for arrays, ; (field-recog field-recog-helper-fn type init accessor updater length-name ; resize-name) ; and we know if the field is simple or an array according to whether ; (car type) is ARRAY. (put-stobjs-in-and-outs1 name field-templates (putprop creator-name 'STOBJS-OUT (list name) (putprop recog-name 'STOBJS-IN (list name) wrld))))) (defun defconst-name-alist (lst n) (if (endp lst) nil (cons (cons n (defconst-name (car lst))) (defconst-name-alist (cdr lst) (1+ n))))) (defun accessor-array (name field-names) (let ((len (length field-names))) (compress1 name (cons `(:HEADER :DIMENSIONS (,len) :MAXIMUM-LENGTH ,(+ 1 len) :DEFAULT nil ; should be ignored :NAME ,name :ORDER :none) (defconst-name-alist field-names 0))))) (defun strip-accessor-names (x) ; This could just as well be called strip-cadddrs. X is the caddr of a ; defstobj template; see defstobj-template. (if (endp x) nil (cons (cadddr (car x)) (strip-accessor-names (cdr x))))) (defun defstobj-defconsts (names index) (if (endp names) nil (cons `(defconst ,(defconst-name (car names)) ,index) (defstobj-defconsts (cdr names) (1+ index))))) (defun defstobj-fn (name args state event-form) ; Warning: If this event ever generates proof obligations (other than those ; that are always skipped), remove it from the list of exceptions in ; install-event just below its "Comment on irrelevance of skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (msg "( DEFSTOBJ ~x0 ...)" name)) (let ((event-form (or event-form (list* 'defstobj name args))) (wrld0 (w state))) (er-let* ((wrld1 (chk-acceptable-defstobj name args ctx wrld0 state))) (cond ((eq wrld1 'redundant) (stop-redundant-event ctx state)) (t (enforce-redundancy event-form ctx wrld0 (let* ((template (defstobj-template name args wrld1)) (field-names (strip-accessor-names (caddr template))) (defconsts (defstobj-defconsts field-names 0)) (field-const-names (strip-cadrs defconsts)) (ax-def-lst (defstobj-axiomatic-defs name template wrld1)) (raw-def-lst (defstobj-raw-defs name template nil wrld1)) (recog-name (car template)) (creator-name (cadr template)) (names ; Warning: Each updater should immediately follow the corresponding accessor -- ; and, this is guaranteed by the call of defstobj-axiomatic-defs, above) -- so ; that the 'stobj property laid down below puts each updater immediately after ; the corresponding accessor, as assumed by function chk-stobj-let/updaters1. (strip-cars ax-def-lst)) (the-live-var (the-live-var name)) (doc (fourth template)) (congruent-to (sixth template))) (er-progn (cond ((set-equalp-equal names (strip-cars raw-def-lst)) (value nil)) (t (value (er hard ctx "Defstobj-axiomatic-defs and defstobj-raw-defs are ~ out of sync! They should each define the same set ~ of names. Here are the functions with axiomatic ~ defs that have no raw defs: ~x0. And here are ~ the with raw defs but no axiomatic ones: ~x1." (set-difference-equal names (strip-cars raw-def-lst)) (set-difference-equal (strip-cars raw-def-lst) names))))) (revert-world-on-error (pprogn (set-w 'extension wrld1 state) (er-progn (process-embedded-events 'defstobj (table-alist 'acl2-defaults-table wrld1) (or (ld-skip-proofsp state) t) (current-package state) (list 'defstobj name names) (append ; See the comments about defstobj in process-embedded-events for dealing with ; (set-ignore-ok t) and (set-irrelevant-formals-ok t). (pairlis-x1 'defun ax-def-lst) defconsts ; We disable the executable counterpart of the creator function since its *1* ; function always does a throw, which is not useful during proofs. `((encapsulate () (set-inhibit-warnings "theory") (in-theory (disable (:executable-counterpart ,creator-name)))))) 0 t ; might as well do make-event check ctx state) ; The processing above will define the functions in the logic, using ; defun, and that, in turn, will define their *1* counterparts in ; Lisp. But because of code in defuns-fn, the processing above will ; not define the raw Lisp versions of the functions themselves ; (normally that would be derived from the axiomatic defs just ; processed). Instead, we will store a CLTL-COMMAND below that ; handles the raw Lisp defs only. ; What follows is hard to follow and rather arcane. Why do we include ; name in the ee-entry computed above, (defstobj name names)? That ; entry will be added to the embedded-event-lst by ; process-embedded-events and be inspected by the individual defuns ; done. Those defuns will recognize their fn name, fn, among names, ; to detect that they are being done as part of a defstobj. The defun ; will pick up the stobj name, name, from the ee-entry and build it ; into the ignorep entry of the defun CLTL-COMMAND, to be processed by ; add-trip. In add-trip, the stobj name, name, will find its way into ; the oneify-cltl-code that generates the *1* body for fn. That body ; contains a throw upon detection of a guard error. The object thrown ; contains the stobjs-in of the offensive expression, so we will know ; how to print it. But the stobjs-in of fn is incorrectly set in the ; world right now -- more accurately, will be incorrectly set in the ; world in which the defun is done and the throw form is constructed ; -- because we have not yet declared name to be a stobj. Indeed, we ; cannot declare it to be a stobj yet since we are defining functions ; that treat it as an ordinary list. This is the stobj version of the ; super-defun-wart problem. (er-let* ((doc-pair (translate-doc name doc ctx state))) (let* ((wrld2 (w state)) (wrld3 (update-doc-database name doc doc-pair (putprop name 'congruent-stobj-rep (and congruent-to (congruent-stobj-rep congruent-to wrld2)) (putprop ; Here I declare that name is Common Lisp compliant. Below I similarly declare ; the-live-var. All elements of the namex list of an event must have the same ; symbol-class. name 'symbol-class :common-lisp-compliant (put-stobjs-in-and-outs name template ; Rockwell Addition: It is convenient for the recognizer to be in a ; fixed position in this list, so I can find out its name. (putprop name 'stobj (cons the-live-var (list* recog-name creator-name (append (remove1-eq creator-name (remove1-eq recog-name ; See the comment in the binding of names above. names)) field-const-names))) (putprop-x-lst1 names 'stobj-function name (putprop-x-lst1 field-const-names 'stobj-constant name (putprop the-live-var 'stobj-live-var name (putprop the-live-var 'symbol-class :common-lisp-compliant (putprop name 'accessor-names (accessor-array name field-names) wrld2)))))))))))) ; The property 'stobj marks a single-threaded object name. Its value is a ; non-nil list containing all the names associated with this object. The car ; of the list is always the live variable name for the object. The cadr and ; caddr of the list (for all user-defined stobjs, i.e., all but our STATE) are ; the stobj recognizer and creator for the stobj, respectively. The remaining ; elements are the names of the other events introduced, including definitions ; of the accessors and the updaters. ; Every supporting function is marked with the property ; 'stobj-function, whose value is the object name. The live var name ; is marked with 'stobj-live-var, whose value is the object name. ; CHEAT: I ought, at this point, ; (pprogn ; (update-user-stobj-alist ; (cons (cons name (create-stobj name template)) ; (user-stobj-alist state)) ; state) ; That is, I should add to the user-stobj-alist in state an entry for ; this new stobj, binding its name to its initial value. But I don't ; want to create the logical counterpart of its initial value -- the ; function create-stobj cannot be used this way (only uses ; resulting from with-local-stobj will pass translate), and we do ; not want to hack our way through the admission of this function ; which is apparently consing a stobj into an alist. Instead, I rely ; on the live object representing the stobj. This live object is ; created when the CLTL-COMMAND below is processed by add-trip. ; Add-trip evals the init form in raw lisp to create the live object ; and assign it to global variables. It also creates array-based ; accessors and updaters. It then stores this live object in the ; user-stobj-alist of the state just as suggested above, provided this ; is not a redefinition. (For a redefinition of the stobj, it does a ; put-assoc-eq rather than a cons.) ; The down-side to this cheat is that this only works while ; defstobj-fn is a :program mode function called on the live state, ; where the raw code operates. If I admitted this function to the ; logic and then called it on the live state, I would get an effect on ; the live state not explained by the code. Furthermore, if I called ; it on a fake state, I would get a new fake state in which the new ; stobj was not on the user-stobj-alist. ; It will be a while before these discrepancies bother me enough to ; fix. As long as this is a :program mode function, we won't be able ; to prove that its effect on state is contrary to its semantics as ; expressed here. (install-event name event-form 'defstobj ; Note: The namex generated below consists of the single-threaded ; object name, the live variable name, and then the names of all the ; functions introduced. Big-d-little-d-event knows it can cdr past ; the first two elements of the namex of a defstobj to find the list ; of functions involved. (list* name the-live-var names) nil `(defstobj ,name ,the-live-var ,(defstobj-raw-init template) ,raw-def-lst ,template ,ax-def-lst) t ctx wrld3 state))))))))))))))) ; Essay on the Correctness of Abstract Stobjs ; In this Essay we provide a semantic foundation for abstract stobjs that shows ; the critical role of :CORRESPONDENCE, :PRESERVED, and :GUARD-THM lemmas. Our ; motivation is to understand why the standard logical definition of evaluation ; is correctly implemented by how evaluation really works in Lisp, using live ; stobjs. ; Below, we use the term ``stobj primitive (for s)'' to indicate a function ; introduced by a defstobj or (more often) defabsstobj event (for stobj s). In ; the case of defabsstobj, an ``:EXEC function'' or ``:LOGIC function'' is a ; stobj primitive associated with an :EXEC or :LOGIC keyword (perhaps by ; default), respectively. ; Informally, we wish to relate two kinds of evaluation, one using :LOGIC ; primitives and one using corresponding :EXEC primitives, in corresponding ; environments where each abstract stobj is bound to an object satisfying its ; :LOGIC recognizer in the first environment and its :EXEC recognizer in the ; second. Such evaluation will enforce guards before making calls of stobj ; primitives at the :LOGIC level and (as we will see, by the :GUARD-THM ; theorems) guarantee that guards thus hold for calls of stobj primitives at ; the :EXEC level. Because of the latter, we can even imagine passing live ; stobjs around for the :EXEC evaluation. But the replacement of ACL2 objects ; by live stobjs is not what's new for abstract stobjs, so we do not address it ; here. Thus in the remainder of this Essay, we deal only with ACL2 objects, ; without any consideration of raw Lisp evaluation using live stobjs. We ; imagine two sorts of logical evaluation using either :LOGIC or :EXEC ; primitives, with the goal of showing that they keep the corresponding states ; (latches) in sync. ; Fix an ACL2 world, and let A be a set of abstract stobj names. We introduce ; a variant of EV, A-evaluation, which models evaluation using :EXEC functions ; for abstract stobjs in A. (But note that throughout, we ignore the EV ; arguments of state, hard-error-returns-nilp, and aok, which aren't of ; interest for our exposition. See also the Essay on EV for some relevant ; background.) As is the case for EV, A-evaluation maps a term, alist, and ; latches to a (possibly multiple) value and new latches, but for A-evaluation ; there is a new wrinkle: for each function symbol f introduced for an abstract ; stobj in A that is bound in latches, the :EXEC definition of f is used ; (instead of the logical definition, which invokes the :LOGIC function). If A ; is the empty set, then A-evaluation reduces to EV, and we call it ; pure-evaluation. As with EV, A-evaluation comprehends guards and can return ; an error indication when there is a guard violation; and in that case, as ; with actual ACL2 evaluation, it must return such an error when latched stobjs ; are involved (even with guard-checking nil or :NONE). ; It is tempting to show a direct correspondence between pure-evaluation and ; A-evaluation, where A is the set of abstract stobj names. But we will ; instead define a sort of "dual-rail" evaluator that does both of these ; together, because we need those two evaluations to stop at the same time in ; order to compare their returned latches. Now, it is possible to show that ; A-evaluation continues for at _least_ as long as pure-evaluation, by applying ; the :GUARD-THM theorems when comparing a :LOGIC function call during ; pure-evaluation with a corresponding :EXEC function call during A-evaluation. ; But A-evaluation could run longer, so we need a way to stop it at the point ; that pure-evaluation stops, in order to compare the returned latches for ; each. Before we define our dual-rail evaluator, we need a few definitions. ; For a set A of abstract stobj names, an alist S is A-valid if for every stobj ; name s0 in the domain of S: if s0 is in A then S(s0) satisfies the :EXEC ; recognizer for s0, and otherwise S(s0) satisfies the recognizer for s0 ; (hence, the :LOGIC recognizer for s0 if s0 is an abstract stobj). We may say ; ``valid'' in place of ``A-valid'' if A is clear from context or ; ``pure-valid'' if A is the empty set. ; For a given abstract stobj s0, two ACL2 objects x$c and x$a are said to ; s0-correspond (or, just "correspond" if s0 is clear from context) if ; corr(x$c,x$a) holds, where corr is the (ordinary logical interpretation of ; the) :CORR-FN for s0. Let A be a set of abstract stobj names and let S$c and ; S$a be alists. We say that S$c and S$a A-correspond if they have the same ; domain and for every x in their domain: if x is in A then S$c(x) and S$a(x) ; correspond, and otherwise S$c(x) = S$a(x). In the context of an expected ; stobjs-out, two results A-correspond if they have the same number of values ; and for each position n, if the nth element of the stobjs-out is some s0 in A ; then the respective nth values s0-correspond, and otherwise the respective ; nth values are equal. ; We are ready to model dual-rail evaluation with a function ev5. ; (ev5 term alist$a alist$c latches$a latches$c A) ; = ; (mv erp result$c result$a latches$c' latches$a') ; The definition of EV5 (details omitted here) is, with two exceptions ; discussed below, a straightforward modification of EV for a dual-rail ; semantics, i.e., returning the results of A-evaluation (result$c and ; latches$c') and pure-evaluation (result$a and latches$a'). The first ; exception is that guards are checked only for pure-evaluation. The second ; exception pertains to any call of a primitive for an abstract stobj in A that ; is bound in latches$c (or equivalently, in latches$a). In that case, ; A-evaluation and pure-evaluation are used independently on the :EXEC and ; :LOGIC functions for the primitive, respectively, to compute the result pairs ; and , respectively). We argue ; below that both of these evaluations occur without guard violations. ; The following correctness claim justifies the use of concrete stobjs to ; simulate evaluation with abstract stobjs. ; Claim. Let A be a set of abstract stobj names, and let u be a term. Also ; let S$c and S$a be A-corresponding alists that are A-valid and pure-valid, ; respectively, with a common domain that includes all free variables of u. ; Let L$c and L$a be the respective restrictions of S$c and S$a to a common ; set of stobj names, and assume that A is a subset of the common domain of ; L$c and L$a. Assume either of the following, where EV_A is the variant of ; EV that evaluates using :EXEC functions in the case of stobj primitives for ; members of A. ; (a) (ev_A u S$c L$c) = (mv erp r$c L$c') ; AND ; (ev u S$a L$a) = (mv nil r$a L$a') ; OR ; (b) (ev5 u S$c S$a L$c L$a A) = (mv erp r$c r$a L$c' L$a'). ; Then the following properties hold. ; (1) In case (b) with erp nil, and in case (a) (for any erp): ; (i) r$c A-corresponds to r$a; and ; (ii) L$c' and L$a' are the respective updates of L$c and L$a ; according to r$c, r$a, and the stobjs-out of u, in the obvious ; sense. ; (2) L$c', L$a', L$c, and (thus) L$a all have the same domain. ; (3) L$c' A-corresponds to L$a'. ; (4) L$c' is A-valid and L$a' is pure-valid. ; (5) In case (a), erp is nil. ; Remark. The subset restriction does not impair the relevance of this Claim. ; In an actual evaluation in the top-level loop, consider the latches as ; including a binding for every known stobj name that occurs in the term to be ; evaluated. Local stobjs and stobj-let-bound stobjs don't present a problem, ; since each will add a binding to the latches; but we ignore these for the ; proof. ; Proof. We give only an outline of the proof, first dealing with (a) by ; itself, then using (a) in the proof sketch for (b). ; The proof of (a) is by induction on A. First consider the base case, where A ; is empty. Clearly (1)(i), (3), and (5) hold vacuously. Parts (1)(ii) and ; (2) are just facts about EV. Finally, (4) reduces to showing that L$a' is ; pure-valid. This follows from the :PRESERVED theorems, each of which has a ; stobj primitive guard as a hypothesis, and the fact that EV returns an error ; (mv non-nil ...) when the guard doesn't hold for a call of a stobj primitive. ; We omit details. ; So assume that A is non-empty. Let s0 be the abstract stobj in A that was ; last introduced in the world. Hence we may write A = A' U {s0} where by the ; inductive hypothesis, the claim holds for A'. ; We now proceed by computational induction. We consider only the interesting ; case, leaving the rest to the reader: u is (f ... s0 ...), where f is a stobj ; primitive for s0 (whose arguments may include members of A'), and where the ; arguments evaluate without error. Thus, we may assume (a), since (b) clearly ; implies (a) in the non-error case. Let f$a and f$c be the :LOGIC and :EXEC ; functions for f (respectively). For notational simplicity we consider only ; the case that f returns a single value; the general case is really the same, ; except for focusing on a particular position's result. Let r' be the result ; of pure-evaluation of (f$c ... s0 ...). We make the following two ; observations, each followed by a justification. ; (*) r' {s0}-corresponds to r$a. ; To see this, first note that since we are assuming that the evaluation (ev u ; S$a L$a) does not result in an error, i.e., returns (mv nil ...), we know ; that the guard of f is met for the call of f in the term u. We may therefore ; apply the :CORRESPONDENCE theorem for f, to show that (f$c ... s0 ...) and ; (f$a ... s0 ...) {s0}-correspond. But these are respectively equal to r' and ; r$a because pure-evaluation (i.e., EV) returns a result provably equal to its ; input. ; (**) r$c A'-corresponds to r'. ; This holds by the inductive hypothesis on A', since A'-evaluation of the body ; of f$c produces the same result as A-evaluation of the body of f$c (namely, ; r$c), as no primitive function for s0 is ancestral in f$c (because f$c was ; defined before the introduction of abstract stobj s0). ; From (*) and (**), a case analysis (on the type of result returned by f) ; yields that r$c A-corresponds to r$a. Conclusion (1)(i) follows, and ; (1)(ii), (2), and (3) then follow by usual arguments for EV. For (5), we ; observe that since the guard holds for the call of f (as argued for (*) ; above)), then by the :GUARD-THM theorems, the guard holds for the ; corresponding call of f$c; hence since f$c is guard-verified, its call's ; A-evaluation concludes without error. For (4): the preservation of stobj ; recognizers for other than abstract stobjs, and thus for :EXEC recognizers ; for abstract stobjs in A, is a well-known property of well-guarded stobj ; computations (and the f$c is indeed well-guarded, as argued for (5) above); ; and for :LOGIC recognizers of stobjs in A it follows from the :PRESERVED ; theorems. Note that the :PRESERVED theorems again require that the guard is ; met, which was argued above. ; That concludes the proof of case (a), so we now consider case (b). The proof ; is by computational induction. The interesting case is the same one we dealt ; with in the proof of case (a). In case (b), EV5 first checks the guard for ; pure evaluation before passing control to EV_A and EV, passing up (mv erp ; ...) with erp non-nil if the check fails; and in that case we simply appeal ; to the inductive hypothesis. But if the guard-check succeeds, then since f ; is guard-verified, we know that there will be no guard violation, and ; pure-evaluation (EV) will return with erp = nil. So we can apply case (a), ; completing the proof. ; Note: the argument above probably works if we allow an arbitrary guard for a ; function exported by defabsstobj instead of using the guard of its :logic ; function. If the need for such flexibility arises (presumably in the form of ; a new :guard keyword for defabsstobj :exports), we should revisit this Essay ; in order to be sure that the argument holds together. But careful: allowing ; an arbitrary guard might not be feasible! A comment in update-guard-post ; explains that substituting exported functions for their :logic versions has ; the property that guard proof obligations are essentially preserved. But the ; use of user-supplied guards destroys that argument, and as a result, we no ; longer can trust evaluation of the guard in raw Lisp. (defun absstobj-name (name type) ; Warning: The (absstobj-name name :CREATOR) should equal (defstobj-fnname name ; :CREATOR :TOP nil), because of the use of the latter in ; parse-with-local-stobj. (declare (type symbol name type)) (mv-let (prefix suffix) (case type (:A (mv nil "$A")) ; abstract (:C (mv nil "$C")) ; concrete (:CREATOR (mv "CREATE-" nil)) (:RECOGNIZER (mv nil "P")) (:RECOGNIZER-LOGIC (mv nil "$AP")) (:RECOGNIZER-EXEC (mv nil "$CP")) (:CORR-FN (mv nil "$CORR")) (:CORRESPONDENCE (mv nil "{CORRESPONDENCE}")) (:PRESERVED (mv nil "{PRESERVED}")) (:GUARD-THM (mv nil "{GUARD-THM}")) (otherwise (mv (er hard 'absstobj-name "Unrecognized type, ~x0." type) nil))) (let* ((s (symbol-name name)) (s (if prefix (concatenate 'string prefix s) s)) (s (if suffix (concatenate 'string s suffix) s))) (intern-in-package-of-symbol s name)))) #-acl2-loop-only (defmacro defabsstobj (&whole event-form name &key concrete recognizer creator exports protect-default &allow-other-keys) ; Warning: If you change this definition, consider the possibility of making ; corresponding changes to the #-acl2-loop-only definition of defstobj. ; This function is run when we evaluate (defabsstobj name . args) in raw lisp. (let* ((the-live-name (the-live-var name)) (recognizer (or recognizer (absstobj-name name :RECOGNIZER))) (st$c (cond ((null concrete) (absstobj-name name :C)) ((consp concrete) (car concrete)) (t concrete))) (creator (or creator (absstobj-name name :CREATOR))) (creator-name (if (consp creator) (car creator) creator)) (fields (list* recognizer ; Recognizer must be first and creator second: the call below of ; simple-translate-absstobj-fields returns methods that are passed to ; defabsstobj-raw-defs, which requires the first two methods to be for the ; recognizer and creator, respectively. creator exports))) (mv-let (erp methods) ; Each method has only the :NAME, :LOGIC, :EXEC, and :PROTECT fields filled in ; (the others are nil). But that suffices for the present purposes. (simple-translate-absstobj-fields name st$c ; See the comment above about the first two fields of the computed methods ; being for the recognizer and creator. fields '(:RECOGNIZER :CREATOR) ; other types are nil protect-default nil ; safe value, probably irrelevant in raw Lisp ) (cond (erp (interface-er "~@0" methods)) (t `(progn ; We place the defvar above the subsequent let*, in order to avoid ; warnings in Lisps such as CCL that compile on-the-fly. (defvar ,the-live-name) ; For defstobj, we lay down a defg form for the variable (st-lst name). Here, ; we do not do so, because memoize-fn collects st-lst values based on ; (congruent-stobj-rep values for) corresponding concrete stobjs. To see why ; this is appropriate, consider what happens when a stobj primitive is called ; for an abstract stobj that updates that stobj. That primitive is defined as ; a macro that expands to a call of the :exec function for that stobj ; primitive. Any memoized function call made on behalf of calling that :exec ; function will take responsibility for flushing memo tables; see the ; discussion of abstract stobjs in comments in memoize-fn. So there is no defg ; form to lay down here. ,@(mapcar (function (lambda (def) (cons 'DEFMACRO def))) ; See the comment above in the binding of fields, about a guarantee that the ; first two methods must be for the recognizer and creator, respectively. (defabsstobj-raw-defs name methods)) (let* ((boundp (boundp ',the-live-name)) (d (and boundp (get ',the-live-name 'redundant-raw-lisp-discriminator))) (ok-p (and boundp (equal d ',event-form)))) (cond (ok-p ',name) ((and boundp (not (raw-mode-p *the-live-state*))) (interface-er "Illegal attempt to redeclare the (abstract) single-threaded ~ object ~s0." ',name)) (t (setf ,the-live-name ,(defabsstobj-raw-init creator-name methods)) (setf (get ',the-live-name 'redundant-raw-lisp-discriminator) ',event-form) (let ((old (and boundp ; optimization (as for defstobj) (assoc-eq ',name *user-stobj-alist*)))) (cond (old ; hence raw-mode (fms "Note: Redefining and reinitializing (abstract) stobj ~ ~x0 in raw mode.~%" (list (cons #\0 ',name)) (standard-co *the-live-state*) *the-live-state* nil) (setf (cdr old) (symbol-value ',the-live-name))) (t (assert$ (not (assoc-eq ',name *user-stobj-alist*)) (setq *user-stobj-alist* (cons (cons ',name (symbol-value ',the-live-name)) *user-stobj-alist*)))))) ',name))))))))) #+acl2-loop-only (defmacro defabsstobj (&whole event-form name &key concrete recognizer creator corr-fn exports protect-default congruent-to missing-only doc) ":Doc-Section Events define a new abstract single-threaded object ~/ We assume familiarity with single-threaded objects; ~pl[stobj] and ~pl[defstobj]. The event ~c[defabsstobj] defines a so-called ``abstract stobj'', a notion we introduce briefly now and then explain in more depth below. The evaluation of a ~il[defstobj] event produces logical definitions for several functions: a recognizer, which characterizes the ~il[stobj] in terms of lists; a creator, which produces an initial suitable list structure; and field accessors and updators, defined in terms of ~ilc[nth] and ~ilc[update-nth]. ~c[Defabsstobj] provides a way to define alternate definitions for ``stobj primitives'' for a corresponding single-threaded object. These stobj primitives include a recognizer, a creator, and other ``exported'' functions. In essence, ~c[defabsstobj] establishes interface functions, or ``exports'', on a new stobj that is a copy of an indicated ``concrete'' stobj that already exists. We begin below with an introduction to abstract ~il[stobj]s. We then explain the ~ilc[defabsstobj] event by way of an example. We conclude by giving summary documentation for the ~c[defabsstobj] event. For another introduction to abstract stobjs, see the paper ``Abstract Stobjs and Their Application to ISA Modeling'' by Shilpi Goel, Warren A. Hunt, Jr., and Matt Kaufmann, in the proceedings of ACL2 Workshop 2013, ~url[http://www.cs.uwyo.edu/~~ruben/acl2-13].~/ ~st[INTRODUCTION] We start with a brief review of ~il[stobj]s and some potential problems with them, followed by an introduction to abstract stobjs and how they can avoid these problems. Prior experience with stobjs will probably help the reader to absorb the ideas below. Recall that single-threaded objects, or ~il[stobj]s, provide a way for ACL2 users to stay within the ACL2 logic, where every data object is an atom or a ~ilc[cons] of data objects, while obtaining the benefits of fast evaluation through destructive updates. Consider for example this very simple event. ~bv[] (defstobj st fld) ~ev[] This event introduces a recognizer, ~c[stp], and a creator, ~c[create-st], for a data structure consisting of a single field accessed and updated by functions ~c[fld] and ~c[update-fld], respectively. Each of these four primitive functions has both a logical definition, which is used when the prover reasons about the function, and an executable definition, which is used in raw Lisp. In the logic, ~c[stp] recognizes objects that have the requisite fields. In raw Lisp, there is a ``live stobj'', which is an array object whose fields correspond to those specified by the ~ilc[defstobj] event, implemented as Lisp arrays. Here are the logical definition and the executable definition, respectively, that are introduced for the field accessor, ~c[fld], introduced above. Notice that since a stobj is represented in raw Lisp using an array, the raw Lisp accessor uses a raw Lisp array accessor, ~c[svref]. (You can see all the logical and executable definitions by evaluating the form ~c[(trace$ defstobj-axiomatic-defs defstobj-raw-defs)] before evaluating the ~ilc[defstobj] form.) ~bv[] ; logical definition (defun fld (st) (declare (xargs :guard (stp st) :verify-guards t)) (nth 0 st)) ; executable (raw Lisp) definition (defun fld (st) (svref st 0)) ~ev[] Sophisticated programming with stobjs can provide efficient implementations of algorithms, but may require the preservation of a complex invariant. One can, of course, define a function to implement such an invariant after introducing the stobj, as follows. ~bv[] ; Introduce a stobj. (defstobj st fld1 ... fldk) ; Define an invariant on that stobj. (defun good-stp (st) (declare (xargs :stobjs st)) ...) ; Define some basic functions that update the stobj and preserve the ; invariant. (defun update-st (... st ...) (declare (xargs :stobjs st :guard (and (good-stp st) ...))) ...) ... ; Prove that the invariant is indeed preserved by those basic functions. (defthm good-stp-update-st (implies (and (good-stp st) ...) (good-stp (update-st ... st ...)))) ... ; Implement algorithms built on the basic functions. (defun foo (... st ...) (declare (xargs :stobjs st :guard (and (good-stp st) ...))) ... (update-st ... st ...) ...) ; Prove invariance theorems about these algorithms. (defthm good-stp-foo (implies (and (good-stp st) ...) (good-stp (foo ... st ...)))) ... ; Prove other properties of these algorithms. (defthm foo-is-correct (implies (and (good-stp st) ...) (some-property (foo ... st ...)))) ... ~ev[] But there are at least two potential difficulties in using stobjs as described above. ~bq[] 1. When ~c[foo] is executed on concrete data in the ACL2 loop, the guard check may be expensive because ~c[(good-stp st)] is expensive. 2. Reasoning about ~c[foo] (using rules like ~c[foo-is-correct] above) involves proving hypotheses of invariance theorems, which may be complicated for the user to manage or slow for the theorem prover.~eq[] The ~c[defabsstobj] event offers an opportunity to address these issues. It introduces a new stobj, which we call an ``abstract stobj'', which is associated with a corresponding ``concrete stobj'' introduced by an earlier ~ilc[defstobj] event. The ~c[defabsstobj] event specifies a logical (~c[:LOGIC]) and an executable (~c[:EXEC]) definition for each primitive operation, or ``stobj primitive'', involving that stobj. As is the case for ~ilc[defstobj], the logical definition is what ACL2 reasons about, and is appropriate to apply to an ACL2 object satisfying the logical definition of the recognizer function for the stobj. The executable definition is applied in raw Lisp to a live stobj, which is an array object associated with the given stobj name. We can picture a sequence of updates to corresponding abstract and concrete stobjs as follows. Initially in this picture, ~c[st$a0] and ~c[st$c0] are a corresponding abstract and concrete stobj (respectively). Then an update, ~c[u1], is applied with ~c[:LOGIC] and ~c[:EXEC] functions ~c[u$a1] and ~c[u$c1], respectively. The resulting abstract and concrete stobj, ~c[st$a1] and ~c[st$c1], correspond as before. Then a second update, ~c[u2], is applied with ~c[:LOGIC] and ~c[:EXEC] functions ~c[u$a2] and ~c[u$c2], respectively ~-[] again preserving the correspondence. And so on. ~bv[] Abstract u$a1 u$a2 u$a3 (:logic) st$a0 --> st$a1 --> st$a2 --> ... ^ ^ ^ ^ Correspondence | | | ... | v v v v u$c1 u$c2 u$c3 Concrete st$c0 --> st$c1 --> st$c2 --> ... (:exec) ~ev[] We conclude this introduction with some remarks about implementation. Consider an abstract stobj ~c[st] with corresponding concrete stobj ~c[st$c]. The live stobjs for ~c[st] and ~c[st$c] have the same structure, but are distinct arrays. Indeed, the raw Lisp creator function for ~c[st$c] is called to create a new initial live stobj for ~c[st]. As we will see below, reads and writes in raw Lisp to the live stobj for ~c[st] are ultimately performed using the primitive accessors and updaters defined for ~c[st$c]. One might think of the live stobjs for ~c[st] and ~c[st$c] as being congruent stobjs (~pl[defstobj]), except that the stobjs themselves are not congruent: the stobj primitives introduced for ~c[st] may be applied to ~c[st] but not arbitrary field updaters of ~c[st$c], for example. As one might expect, the ~c[:EXEC] function for an exported function is applied to the live stobj for ~c[st] in raw Lisp. ~st[EXAMPLE] We present examples, with detailed comments intended to explain abstract stobjs, in two community books: ~c[books/misc/defabsstobj-example-1.lisp] and ~c[books/misc/defabsstobj-example-2.lisp]. In this section we outline the first of these. We suggest that after you finish this ~il[documentation] topic, you read through those two books. Here is the first of two closely related ~c[defabsstobj] ~il[events] from the book ~c[defabsstobj-example-1.lisp], but in expanded form. We will show the abbreviated form later, which omits most of data in the form that is immediately below. Thus most of the information shown here is default information. We believe that the comments below explain most or all of what you need to know in order to start using ~c[defabsstobj], and that you will learn the remainder when you see error messages. For example, we do not say in the comments below that every ~c[:LOGIC] and ~c[:EXEC] function must be ~il[guard]-verified, but that is indeed a requirement. ~bv[] (defabsstobj st ; The new abstract stobj is named st. ; The concrete stobj corresponding to st is st$c: :concrete st$c ; The recognizer for the new abstract stobj is stp, which is defined to be ; st$ap in the logic, and is executed on the live stobj in raw Lisp using ; st$cp. :recognizer (stp :logic st$ap :exec st$cp) ; The initial stobj is defined as create-st (a function of no arguments), ; which is defined logically as create-st$a, though create-st$c is invoked to ; create the initial live stobj for st. The :correspondence and :preserved ; keywords refer to proof obligations, discussed below. :creator (create-st :logic create-st$a :exec create-st$c :correspondence create-st{correspondence} :preserved create-st{preserved}) ; Proof obligations are generated that involve a correspondence between the ; new abstract stobj and corresponding concrete stobj. The function ; st$corr, which need not be executable (see :DOC defun-nx), takes two ; arguments, a concrete stobj and an abstract stobj. This function symbol is ; used in the statements of the proof obligations. :corr-fn st$corr ; In this example we have four exports. In each case a new function is ; introduced that has the same signature as its :EXEC function, except that ; st$c is replaced by st. The :LOGIC and :EXEC functions are as specified, ; and the other keywords refer to proof obligations that we discuss below. :exports ((lookup :logic lookup$a :exec mem$ci :correspondence lookup{correspondence} :guard-thm lookup{guard-thm}) (update :logic update$a :exec update-mem$ci :correspondence update{correspondence} :preserved update{preserved} :guard-thm update{guard-thm}) (misc :logic misc$a :exec misc$c :correspondence misc{correspondence}) (update-misc :logic update-misc$a :exec update-misc$c :correspondence update-misc{correspondence} :preserved update-misc{preserved})) :doc nil) ~ev[] Note that all stobj primitives (recognizer, creator, and exported functions) are defined in the ACL2 loop in terms of their ~c[:LOGIC] functions and in raw Lisp in terms of their ~c[:EXEC] functions. In the ACL2 loop, a ~ilc[defun] form defines a function, while in raw Lisp, a ~ilc[defmacro] form defines a macro (for efficiency). We first illustrate how that works for the recognizer. (You can see all the logical and executable definitions by evaluating the form ~c[(trace$ defabsstobj-axiomatic-defs defabsstobj-raw-defs)] before evaluating the ~ilc[defstobj] form.) ~bv[] ; In the ACL2 loop: (defun stp (st) (declare (xargs :guard 't)) (st$ap st)) ; In raw Lisp: (defmacro stp (&rest args) (cons 'st$cp args)) ~ev[] The definitions are made similarly for exported functions, with ~il[guard]s derived from their ~c[:LOGIC] functions as follows. Consider the exported function ~c[update] in our example. Its ~c[:LOGIC] function, ~c[update$a], has formals ~c[(k val st$a)] and the following guard. ~bv[] (and (and (integerp k) (<= 0 k) (<= k 49)) (and (integerp val) (<= 0 val)) (st$ap st$a) (mem$c-entryp val)) ~ev[] The formals of ~c[update] are obtained by starting with the formals of its ~c[:EXEC] function, ~c[update-mem$ci] ~-[] which are ~c[(i v st$c)] ~-[] and replacing the concrete stobj name ~c[st$c] by the new stobj name ~c[st]. The formals of ~c[update] are thus ~c[(i v st)]. The guard for ~c[update] is obtained in two steps. The first step is to substitute the formals of ~c[update] for the formals of ~c[update$a] in the guard for ~c[update$a], to obtain the following. ~bv[] (and (and (integerp i) (<= 0 i) (<= i 49)) (and (integerp v) (<= 0 v)) (st$ap st) (mem$c-entryp v)) ~ev[] The second step is to replace, for each new stobj primitive ~c[p], the ~c[:LOGIC] function for ~c[p] by ~c[p] itself. The only ~c[:LOGIC] function occurring in the formula just above is ~c[st$ap], which is the ~c[:LOGIC] funcction for ~c[stp]. The guard for ~c[update] is thus as follows. ~bv[] (and (and (integerp i) (<= 0 i) (<= i 49)) (and (integerp v) (<= 0 v)) (stp st) (mem$c-entryp v)) ~ev[] We turn now to the proof obligations, as promised above. There are three types: ~c[:CORRESPONDENCE], ~c[:PRESERVED], and ~c[:GUARD-THM]. All required lemmas may be printed simply by defining the necessary ~c[:LOGIC] and ~c[:EXEC] functions and then submitting the ~c[defabsstobj] event. (To advanced users: also ~pl[defabsstobj-missing-events] for a utility that returns the required formulas in translated form.) Although the ~c[defabsstobj] event will fail if the required lemmas have not been proved, first it will print the ~ilc[defthm] forms that must be admitted in order to complete submission of the ~c[defabsstobj] event. The detailed theory explaining the need for these lemmas may be found in a comment in ACL2 source file ~c[other-events.lisp], in a comment entitled ``Essay on the Correctness of Abstract Stobjs''. Here, we give an informal sense of the importance of these lemmas as we present examples of them. Fundamental is the notion of evaluation in the logic versus evaluation using live stobjs, where one imagines tracking the current value of each abstract stobj during each of these two evaluations. We start with the ~c[:CORRESPONDENCE] lemmas. These guarantee that evaluation in the logic agrees with evaluation using live stobjs, in the sense that the only difference is between a logical stobj and a live stobj, where the two correspond in the sense of the function specified by ~c[:CORR-FN]. We start with the ~c[:CREATOR] function where the statement is quite simple, stating that the ~c[:CORR-FN] holds initially. ~bv[] (defthm create-st{correspondence} (st$corr (create-st$c) (create-st$a))) ~ev[] For the exported functions, there are essentially two cases. If an exported function returns other than the new abstract stobj, then the theorem asserts the equality of the results of applying the ~c[:LOGIC] and ~c[:EXEC] functions for the exported function. Hypotheses include the ~c[:CORR-FN] correspondence followed by the ~il[guard] for the ~c[:LOGIC] function, which is stated in terms of the formal parameters of the ~c[:EXEC] function except using the abstract stobj (here, ~c[st]) in place of the concrete stobj (here, ~c[st$c]). The conclusion uses the ~c[:EXEC] formals, modified in the call of the ~c[:LOGIC] function (here, ~c[lookup$a]) to use the abstract stobj, as in the hypotheses. ~bv[] (defthm lookup{correspondence} (implies (and (st$corr st$c st) (integerp i) (<= 0 i) (<= i 49) (st$ap st)) (equal (mem$ci i st$c) (lookup$a i st))) :rule-classes nil) ~ev[] By contrast, if the exported function returns the new abstract stobj, then the conclusion uses the correspondence function insted of ~c[EQUAL], as in the following. ~bv[] (defthm update{correspondence} (implies (and (st$corr st$c st) (integerp i) (<= 0 i) (<= i 49) (integerp v) (<= 0 v) (st$ap st) (mem$c-entryp v)) (st$corr (update-mem$ci i v st$c) (update$a i v st))) :rule-classes nil) ~ev[] For exported functions that return multiple values, such conclusions are conjoined together over the returned values. The ~c[:PRESERVED] lemmas guarantee that updates to the abstract stobj preserve its recognizer. The fact that every exported function has this property provides justification for an optimization performed by ACL2 during generation of proof obligations for ~il[guard] verification, by assuming that the recognizer always holds. The ~c[:PRESERVED] lemma for the ~c[:CREATOR] shows that the recognizer holds initially. ~bv[] (defthm create-st{preserved} (st$ap (create-st$a))) ~ev[] Here is a typical such lemma, for the exported function ~c[update]. Note that there is no such lemma for ~c[lookup], since ~c[lookup] does not return ~c[st]. ~bv[] (defthm update{preserved} (implies (and (integerp i) (<= 0 i) (<= i 49) (integerp v) (<= 0 v) (st$ap st) (mem$c-entryp v)) (st$ap (update$a i v st)))) ~ev[] Finally, we consider the ~c[:GUARD-THM] lemmas. These serve to guarantee that the ~il[guard] holds for each call of an ~c[:EXEC] function. During guard verification, logical definitions are used; in particular, since each exported function is defined in the logic as the corresponding call of its ~c[:LOGIC] function, guard verification shows that each call of the ~c[:LOGIC] function for an exported function satisfies that function's guard. But why is this true for raw Lisp evaluation using live stobjs, where the ~c[:EXEC] function is called for an exported function? The ~c[:GUARD-THM] lemmas provide the answer, as they state that if the ~c[:LOGIC] function's guard holds, then the ~c[:EXEC] function's guard holds. Here is an example. Note that the hypotheses come from the correspondence of the concrete and abstract function as guaranteed by the ~c[:CORR] function, together with the guard of the ~c[:LOGIC] function; and the conclusion comes from the guard of the ~c[:EXEC] function. ~bv[] (defthm lookup{guard-thm} (implies (and (st$corr st$c c) (integerp i) (<= 0 i) (<= i 49) (st$ap st)) (and (integerp i) (<= 0 i) (< i (mem$c-length st$c)))) :rule-classes nil) ~ev[] We conclude this EXAMPLE section by showing a short form for the ~c[defabsstobj] form displayed above. ~bv[] (defabsstobj st :exports ((lookup :exec mem$ci) (update :exec update-mem$ci) misc update-misc)) ~ev[] ~st[SUMMARY DOCUMENTATION] The General Form is as shown below, where the order of keywords is unimportant. Duplicate keywords are discouraged; while permitted, only the first (leftmost) occurrence of a given keyword is used. Only the ~c[:exports] keyword is required. ~bv[] (defabsstobj st :concrete concrete :recognizer recognizer :creator creator :corr-fn corr-fn :congruent-to congruent-to :protect-default protect-default :exports (e1 ... ek) :doc doc) ~ev[] The keyword argument ~c[:EXPORTS] must be supplied, and missing or ~c[nil] keyword arguments have defaults as indicated below. All arguments must satisfy the conditions below. Before we describe the arguments, we define a notion of a ``function spec'' and its ``completion''. A function spec is either a symbol or else a list of the form ~bv[] (fn ~c[:kwd1] val1 ... ~c[:kwdn] valn), ~ev[] that is, a symbol followed by a ~ilc[keyword-value-listp]. We view the case of a symbol, ~c[s], as the function spec ~c[(s)], with no keywords. There must be no duplicate keywords. In each case that we expect a function spec, the context provides a set of valid keywords for that function spec; it is an error to provide any other keyword in the function spec. Each function spec is interpreted as its ``completion'', obtained by extending the function spec with a default value for each valid keyword as indicated below. With that interpretation, the ``exported function'' of a function spec is its ~c[car], and that function symbol and each keyword value must be a guard-verified function symbol; and moreover, the ~c[:EXEC] function must not include the new abstract stobj name, ~c[st], among its formals. We are ready to describe the arguments of ~c[defabsstobj]. ~bq[] ~c[St] is a symbol, which names the new abstract stobj. ~c[Concrete] is the name of an existing stobj that is not an abstract stobj, i.e., was introduced with ~ilc[defstobj] (not ~ilc[defabsstobj]). ~c[Recognizer] is a function spec (for the recognizer function). The valid keywords are ~c[:LOGIC] and ~c[:EXEC]. The default for ~c[recognizer] is obtained by adding the suffix ~c[\"P\"] to ~c[name]. The default value for ~c[:LOGIC] is formed by adding the suffix ~c[\"$AP\"] to ~c[recognizer]; for ~c[:EXEC], by adding the suffix ~c[\"$CP\"]. The ~c[:EXEC] function must be the recognizer for the specified ~c[:CONCRETE] stobj. ~c[Creator] is a function spec (for the creator function). The valid keywords are ~c[:LOGIC] and ~c[:EXEC]. The default for ~c[creator] is obtained by adding the prefix ~c[\"CREATE-\"] to ~c[name]. The default value for ~c[:LOGIC] is formed by adding the suffix ~c[\"$A\"] to ~c[creator]; for ~c[:EXEC], by adding the suffix ~c[\"$C\"]. The ~c[:CREATOR] function must be the creator for the specified ~c[:CONCRETE] stobj, as ACL2 checks that the ~c[:CREATOR] function takes no arguments and returns the ~c[:CONCRETE] stobj. ~c[Corr-fn] is a known function symbol that takes two arguments (for the correspondence theorems). The default for ~c[corr-fn] is obtained by adding the suffix ~c[\"$CORR\"] to ~c[name]. ~c[Congruent-to] should either be ~c[nil] (the default) or the name of an abstract stobj previously introduced (by ~ilc[defabsstobj]). In the latter case, the current and previous abstract stobj should have the same concrete stobj (not merely congruent concrete stobjs), and their ~c[:EXPORTS] fields should have the same length and also correspond, as follows: the ith export of each should have the same ~c[:LOGIC] and ~c[:EXEC] symbols. ~l[defstobj] for more about congruent stobjs. Note that if two names are congruent, then they are either both ordinary stobjs or both abstract stobjs. ~c[Protect-default] should either be ~c[nil] (the default) or ~c[t]. It provides the value of keyword ~c[:PROTECT] for each member of ~c[exports] that does not explicitly specify ~c[:PROTECT]. See the discussion of ~c[exports] below. An important aspect of the ~c[congruent-to] parameter is that if it is not ~c[nil], then the checks for lemmas ~-[] ~c[{CORRESPONDENCE}], ~c[{GUARD-THM}], and ~c[{PRESERVED}] ~-[] are omitted. Thus, the values of keyword ~c[:CORR-FN], and the values of keywords ~c[:CORRESPONDENCE], ~c[:GUARD-THM], and ~c[:PRESERVED] in each export (as we discuss next), are irrelevant; they are not inferred and they need not be supplied. The value of ~c[:EXPORTS] is a non-empty true list. Each ~c[ei] is a function spec (for an exported function). The valid keywords are ~c[:LOGIC], ~c[:EXEC], ~c[:CORRESPONDENCE], and ~c[:GUARD-THM], ~c[:PROTECT], and also ~c[:PRESERVED] if and only if the specified ~c[:EXEC] function returns the ~c[:CONCRETE] stobj. The default values for all of these keywords except ~c[:PROTECT] are obtained by respectively adding the suffix ~c[\"$A\"] ~c[\"$C\"], ~c[\"{CORRESPONDENCE}\"], ~c[\"{GUARD-THM}\"], or ~c[\"{PRESERVED}\"]. For ~c[:PROTECT], the default is ~c[nil] unless the ~c[defabsstobj] event specifies ~c[:PROTECT-DEFAULT t]. ~c[Doc], if non-~c[nil], is a ~il[documentation] string (~pl[doc-string]).~eq[] Not shown is the keyword, ~c[:MISSING]; the effect of ~c[:missing t] is to turn the call of ~c[defabsstobj] into a corresponding call of ~ilc[defabsstobj-missing-events]. Note that a ~c[defabsstobj] event will fail if the required lemmas ~-[] that is, those for valid keywords ~c[:CORRESPONDENCE], ~c[:GUARD-THM], and ~c[:PRESERVED] ~-[] have not been proved, unless proofs are being skipped. The exemption when skipping proofs allows the supporting lemmas to be ~ilc[local] to ~il[books] and ~ilc[encapsulate] ~il[events]. If the ~ilc[ld] special ~ilc[ld-skip-proofsp] is ~c[t], then the missing ~il[events] are printed with a warning before the ~c[defabsstobj] event is admitted; but if ~c[ld-skip-proofsp] is the symbol ~c[INCLUDE-BOOK], then that warning is omitted. (Also ~pl[skip-proofs] and ~pl[ld-skip-proofsp].) If however proofs are not being skipped, then the ~c[defabsstobj] event will fail after printing the missing events. Advanced users may wish to ~pl[defabsstobj-missing-events] for a utility that returns a data structure containing the missing lemmas. Let ~c[st] be an abstract stobj with corresponding concrete stobj ~c[st$c]. let ~c[f] be an exported function for ~c[st] and let ~c[f$a] and ~c[f$c] be the corresponding ~c[:LOGIC] and ~c[:EXEC] functions, respectively. The formals of ~c[f] are obtained by taking the formals of ~c[f$c] and replacing ~c[st$c] by ~c[st]. The ~il[guard] for ~c[f] is derived as follows from the guard of ~c[f$a]. First, the formals of ~c[f$a] are replaced by the formals of ~c[f] in the guard of ~c[f$a], to obtain a term we denote here as ~c[guard-pre]. Now for each exported function symbol ~c[g] of ~c[st] with corresponding ~c[:LOGIC] function ~c[g$a], form a functional substitution by consing ~c[g$a] with ~c[g]. Finally, apply that functional substitution to ~c[guard-pre]; the result is the guard of ~c[f]. That guard must satisfy the usual conditions of a guard: thus, it must return a single non-~il[stobj] value and satisfy normal syntactic restrictions, including single-threadedness in its handling of stobjs. ~c[Remark]. Because of how guards are created for exported functions, and in particular because ~c[:LOGIC] functions are replaced as discussed above, a good discipline is to define ~c[:LOGIC] functions that are not intended for general use, but are intended only for use as ~c[:LOGIC] functions of corresponding stobj primitives. For example, suppose that you use ~c[length] as the ~c[:LOGIC] function for some stobj primitive, ~c[f] (as opposed to using your own function, say, ~c[foo-length] or ~c[foo$a]). Then every call of ~c[length] will be replaced by ~c[f] when creating the guard of a stobj primitive from the guard of its ~c[:LOGIC] function. This might not be what you intended if you were using ~c[length] in that guard simply to compute the length of an ordinary list. There are a few additional restrictions, as follows. ~bq[] All exported function names must be new (unless redefinition is on; ~pl[ld-redefinition-action]), and there must be no duplicates among them. The ~c[:CONCRETE] stobj name must be a formal parameter of the ~c[:EXEC] fn of every function spec, except for the ~c[:CREATOR] function spec. Also the input signatures of the ~c[:LOGIC] and ~c[:EXEC] function for a function spec must agree, except perhaps at the position of that ~c[:CONCRETE] formal. For function specs other than the ~c[:CREATOR] function spec, the output signatures of the ~c[:LOGIC] and ~c[:EXEC] functions must have the same length and must agree, except perhaps at position ~c[p_out] of the ~c[:CONCRETE] stobj in the ~c[:EXEC] function's output. If ~c[p_in] is the position of the ~c[:CONCRETE] stobj in the ~c[:EXEC] function's formals, then the ~c[:LOGIC] function's output at position ~c[p_out] should match the ~c[:LOGIC] function's formal at position ~c[p_in]. The ~c[:PROTECT] keyword is something that you should ignore unless you get an error message about it, pertaining to modifying the concrete stobj non-atomically. In that case, you can eliminate the error by providing ~c[:PROTECT t] in the function spec, or by providing ~c[defabsstobj] keyword argument ~c[:PROTECT-DEFAULT t] at the top level. The above explanation is probably all you need to know about ~c[:PROTECT], but just below is a more complete explanation for those who desire it. Further information is also available if you need it; ~pl[set-absstobj-debug], and see the example uses of these keywords in community book ~c[books/misc/defabsstobj-example-2.lisp].~eq[] For those who are interested, here is a more detailed discussion of ~c[:PROTECT] and ~c[:PROTECT-DEFAULT], as promised above. It applies to any function spec for an export (hence not to the ~c[:CREATOR] function spec). If the ~c[:EXEC] function is a stobj primitive, then clearly the following property holds: any execution of a call of that function can only update the concrete stobj at most once ~-[] i.e., modification of the concrete stobj is atomic. ACL2 can deduce this property not only for stobj primitives but for many other functions as well. However, if ACL2 cannot deduce this property, then it will cause an error saying that the ~c[:EXEC] function ``appears capable of modifying the concrete stobj, ~c[], non-atomically.'' That message also explains how to eliminate this error: provide ~c[:PROTECT t] for the function spec. Alternatively, all function specs without an explicit ~c[:PROTECT] keyword can be implicitly supplied ~c[:PROTECT t] by supplying the value ~c[t] for the ~c[:PROTECT-DEFAULT] keyword parameter of the ~c[defabsstobj] event. However, beware that when ~c[:PROTECT] is ~c[t], the generated raw Lisp code runs slightly less efficiently ~-[] though perhaps with negligible efficiency loss if the ~c[:EXEC] function is not trivial. Community books ~c[books/misc/defabsstobj-example-3.lisp] and ~c[books/misc/defabsstobj-example-4.lisp] provide related information. We conclude with some remarks. Unlike ~ilc[defstobj], there is no ~c[:renaming] argument. Instead, the scheme described above provides a flexible way to assign names. Those who use the experimental extension ACL2(h), which includes function memoization (~pl[memoize]), may be aware that the memo table for a function is flushed whenever it is the case that one of its stobj inputs is updated. In fact, such flushing happens even when a stobj that is congruent to one of its stobj inputs is updated. For that purpose, an abstract stobj is considered to be congruent to its corresponding concrete stobj. ~/" (declare (xargs :guard (and (symbolp name) (booleanp protect-default)))) (list 'defabsstobj-fn (list 'quote name) (list 'quote concrete) (list 'quote recognizer) (list 'quote creator) (list 'quote corr-fn) (list 'quote exports) (list 'quote protect-default) (list 'quote congruent-to) (list 'quote missing-only) (list 'quote doc) 'state (list 'quote event-form))) (defun concrete-stobj (st wrld) (let ((absstobj-info (getprop st 'absstobj-info nil 'current-acl2-world wrld))) (and absstobj-info (access absstobj-info (getprop st 'absstobj-info nil 'current-acl2-world wrld) :st$c)))) (defmacro defabsstobj-missing-events (&whole event-form name &key concrete recognizer creator corr-fn exports protect-default congruent-to doc) ":Doc-Section Events obtain the ~il[events] needed to admit a ~ilc[defabsstobj] event~/ We assume familiarity with ~ilc[defabsstobj]. ~c[Defabsstobj-missing-events] is a macro is for advanced users (who, for example, understand the role of the ~c[translate] and ~c[untranslate] functions), who want programmatic access to the ~ilc[defthm] events required to admit a specific ~c[defabsstobj] event.~/ This macro has the same syntax as ~ilc[defabsstobj] ~-[] to use it, just replace a call of ~ilc[defabsstobj] by a call of ~c[defabsstobj-missing-events] on the same arguments. The result is an error triple ~c[(mv erp val state)]. If ~c[erp] is ~c[nil], then ~c[val] is the list of all objects ~c[(name formula . old-formula)], where a ~ilc[defthm] event named ~c[name] remains to be admitted whose translated formula is ~c[formula], and where ~c[old-formula] is ~c[nil] unless the indicated event already exists (hence with a different formula), in which case ~c[old-formula] is the existing translated formula. To build a ~ilc[defthm] event from the above value, ~c[val], we suggest evaluating a form like ~c[(untranslate formula t (w state))].~/" (declare (xargs :guard (symbolp name))) (list 'defabsstobj-fn1 (list 'quote name) (list 'quote concrete) (list 'quote recognizer) (list 'quote creator) (list 'quote corr-fn) (list 'quote exports) (list 'quote protect-default) (list 'quote congruent-to) (list 'quote t) ; missing-only (list 'quote doc) (list 'quote (msg "( DEFABSSTOBJ-MISSING-EVENTS ~x0 ...)" name)) ; ctx 'state (list 'quote event-form))) (defun redundant-defabsstobjp (name event-form wrld) (and (getprop name 'stobj nil 'current-acl2-world wrld) (equal event-form (get-event name wrld)))) (defun absstobj-correspondence-concl-lst (stobjs-out i st$c corr-fn) (cond ((endp stobjs-out) nil) (t (cons (let ((qi (kwote i))) (fcons-term* (if (eq (car stobjs-out) st$c) corr-fn 'equal) (fcons-term* 'mv-nth qi 'lhs) (fcons-term* 'mv-nth qi 'rhs))) (absstobj-correspondence-concl-lst (cdr stobjs-out) (1+ i) st$c corr-fn))))) (defun absstobj-correspondence-formula (f$a f$c corr-fn formals guard-pre st st$c wrld) ; F$A and f$c are the abstract and concrete versions of some exported function ; whose formals are the given formals. If f$c returns a single non-stobj ; value, then the formula looks as follows, where guard-pre is the result of ; restating the guard on f$a in terms of formals (but still using st$ap rather ; than stp). ; (IMPLIES (AND (corr-fn st$c st) ; guard-pre) ; (EQUAL (f$c ... st$c ...) ; (f$c . formals) ; (f$a ... st ...))) ; However, if f$c returns a single stobj value, st$c, then the formula looks as ; follows instead, the only difference being the use of the correspondence ; predicate, corr-fn, in the conclusion. ; (IMPLIES (AND (corr-fn st$c st) ; guard-pre) ; (corr-fn (f$c ... st$c ...) ; (f$a ... st ...))) ; We make suitable adjustments if f$c returns multiple values. (cond ((null formals) ; Note that translate-absstobj-field guarantees that except for the creator ; function, the formals of an exec function must include the concrete stobj. ; Thus, f$c is the exec creator function. `(,corr-fn (,f$c) (,f$a))) (t (let* ((stobjs-out (stobjs-out f$c wrld)) (lhs (fcons-term f$c (formals f$c wrld))) (rhs (fcons-term f$a formals))) (fcons-term* 'implies (conjoin (cons (fcons-term* corr-fn st$c st) (flatten-ands-in-lit guard-pre))) (cond ((null (cdr stobjs-out)) (fcons-term* (if (eq (car stobjs-out) st$c) corr-fn 'equal) lhs rhs)) (t (fcons-term* (make-lambda '(lhs rhs) (conjoin (absstobj-correspondence-concl-lst stobjs-out 0 st$c corr-fn))) lhs rhs)))))))) (defun absstobj-preserved-formula (f$a f$c formals guard-pre st st$c st$ap wrld) ; F$A and f$c are the abstract and concrete versions of some exported function. ; If these return a single stobj value, then the formula looks as follows, ; where guard-pre is the result of restating the guard on f$a in terms of ; formals (but still using st$ap rather than stp). Although guard-pre may ; often include the conjunct (st$ap st), we do not enforce that expectation ; here. ; (IMPLIES guard-pre ; (st$ap (f$a ... st ...))) (cond ((null formals) ; Note that translate-absstobj-field guarantees that except for the creator ; function, the formals of an exec function must include the concrete stobj. ; So in this case, f$c is the exec creator function. (fcons-term* st$ap (fcons-term* f$a))) (t (let ((stobjs-out (stobjs-out f$c wrld)) (updated-st-term (fcons-term f$a formals))) (fcons-term* 'implies (conjoin (add-to-set-equal (fcons-term* st$ap st) (flatten-ands-in-lit guard-pre))) ; Note that the :preserved theorem is only generated if st$c is returned by the ; exec function. (cond ((null (cdr stobjs-out)) (assert$ (eq (car stobjs-out) st$c) (fcons-term* st$ap updated-st-term))) (t (let ((posn (position st$c stobjs-out))) (assert$ (and posn ; We expect translate to disallow returning st$c more than once; if that ; changes, we should collect all such terms and conjoin them. (not (member-eq st$c (cdr (nthcdr posn stobjs-out))))) (fcons-term* st$ap (fcons-term* 'mv-nth (kwote posn) updated-st-term))))))))))) (defrec absstobj-method ; WARNING: We use assoc-eq to test a symbol against a list of methods, which ; assumes that (access absstobj-method method :name) is (car method). Do not ; change the cheap flag to nil or move name without revisiting such uses! (name ; see warning above before changing position formals ; formals of name: formals of exec but with st substituted for st$c guard-pre ; result of restating the guard on f$a in terms of formals guard-post ; restating guard-pre using stp instead of st$ap guard-thm guard-thm-p stobjs-in-posn stobjs-in-exec stobjs-out logic exec correspondence preserved protect) t ; see warning above before changing to nil ) (mutual-recursion (defun fn-stobj-updates-p (st fn wrld) ; See stobj-updates-p for background. We assume (member-eq st (stobjs-out fn ; wrld)). (cond ((eq st (getprop fn 'stobj-function nil 'current-acl2-world wrld)) :once) ((getprop fn 'recursivep nil 'current-acl2-world wrld) ; We can't predict how many updates fn will make to st. t) ((getprop fn 'constrainedp nil 'current-acl2-world wrld) ; Fn might be attachable, so we can't predict how many updates fn will make to ; st. t) (t (let ((body (getprop fn 'unnormalized-body nil 'current-acl2-world wrld))) (assert$ body (stobj-updates-p st body wrld)))))) (defun stobj-updates-p (st term wrld) ; It is always sound for this function to return t. If it returns :once, then ; st is updated at most once by the execution of term. If it returns nil, then ; st is not updated by the execution of term. ; Consider for example: ; (defstobj st fld) ; (defun foo (a st) ; (declare (xargs :stobjs st)) ; (let* ((b (cons a a)) ; (st (update-fld b st))) ; (mv b st))) ; Then we have: ; ACL2 !>(getprop 'foo 'unnormalized-body nil 'current-acl2-world (w state)) ; ((LAMBDA (B ST) ; ((LAMBDA (ST B) (CONS B (CONS ST 'NIL))) ; (UPDATE-FLD B ST) ; B)) ; (CONS A A) ; ST) ; ACL2 !> ; Notice that for the inner lambda application, the unique update is in an ; argument, and for the the outer lambda, it's in the lambda-body. ; We rely on the following claim, which we believe to be true: if a term can ; make more than one update to st, then this will be observed in our algorithm, ; which uses the result of translating the term. (cond ((or (variablep term) (fquotep term)) nil) ((flambdap (ffn-symb term)) (flet ((or! (x y) ; If x and y are both true, then t; else (or x y). (if x (if y t x) y))) (or! (stobj-updates-listp st (fargs term) wrld) (stobj-updates-p st (lambda-body (ffn-symb term)) wrld)))) ((member-eq (ffn-symb term) '(if return-last)) ; We are conservative here for return-last, avoiding assumptions about whether ; its logic or exec body will be run. (let ((temp1 (stobj-updates-p st (fargn term 1) wrld)) (temp2 (stobj-updates-p st (fargn term 2) wrld))) (cond (temp1 (er hard! 'stobj-updates-p "Please contact the ACL2 implementors. Unexpected true ~ result for first argument of ~x0." term)) ((eq temp2 t) t) (t (let ((temp3 (stobj-updates-p st (fargn term 3) wrld))) (cond ((eq temp3 t) t) (t (or temp2 temp3)))))))) (t ; The assertion just below should hold, because the output of translate on a ; function body won't allow stobj modification in args of a function call. (assert$ (null (stobj-updates-listp st (fargs term) wrld)) (and (member-eq st (stobjs-out (ffn-symb term) wrld)) ; We recur into the body of fn. If this process runs too slowly, we may decide ; on a sort of memoization obtained by storing a suitable property for fn. (fn-stobj-updates-p st (ffn-symb term) wrld)))))) (defun stobj-updates-listp (st x wrld) (cond ((endp x) nil) (t (flet ((or! (x y) ; If x and y are both true, then t; else (or x y). (if x (if y t x) y))) (or! (stobj-updates-p st (car x) wrld) (stobj-updates-listp st (cdr x) wrld)))))) ) (defun unprotected-export-p (st$c name wrld) (and (member-eq st$c (stobjs-out name wrld)) (eq t (fn-stobj-updates-p st$c name wrld)))) (defun translate-absstobj-field (st st$c field type protect-default ld-skip-proofsp see-doc ctx wrld) ; Field is a member of the :exports field of a defabsstobj event if type is ; nil; otherwise type is :recognizer or :creator and field is the recognizer or ; creator argument to defabsstobj. We return an error triple such that if ; there is no error, then the value component is an appropriate absstobj-method ; record. ; If wrld is nil, then we take a shortcut, returning a record with only the ; :NAME, :LOGIC, :EXEC, and :PROTECT fields filled in (the others are nil), ; which are sufficient for handling a defabsstobj form in raw lisp. Otherwise, ; this function does all necessary checks except for the presence of suitable ; :correspondence, :preserved, and :guard formulas. For that, see ; chk-defabsstobj-method. (let* ((field0 field) (field (if (atom field) (list field) field)) (name (car field)) (keyword-lst (cdr field))) (cond ((not (and (symbolp name) (keyword-value-listp keyword-lst))) (er-cmp ctx "Each field of a DEFABSSTOBJ event must be a symbol or a list ~ of the form (symbol :KWD1 val1 :KWD2 val2 ...), but the field ~ ~x0 is not of this form. ~@1" field0 see-doc)) (t (mv-let (exec exec-p) (let ((exec (cadr (assoc-keyword :EXEC keyword-lst)))) (cond (exec (mv exec t)) ((eq type :recognizer) (mv (absstobj-name st :RECOGNIZER-EXEC) nil)) (t (mv (absstobj-name name :C) nil)))) (let* ((protect-tail (assoc-keyword :PROTECT keyword-lst)) (protect (if protect-tail (cadr protect-tail) protect-default))) (cond ((and protect-tail ; optimization (not (member-eq protect '(t nil)))) (er-cmp ctx "Illegal value of :PROTECT, ~x0, in the field for ~x1. ~@2" protect name see-doc)) (t (mv-let (logic logic-p) (let ((logic (cadr (assoc-keyword :LOGIC keyword-lst)))) (cond (logic (mv logic t)) ((eq type :recognizer) (mv (absstobj-name st :RECOGNIZER-LOGIC) nil)) (t (mv (absstobj-name name :A) nil)))) (cond ((null wrld) ; shortcut for raw Lisp definition of defabsstobj (value-cmp (make absstobj-method :NAME name :LOGIC logic :EXEC exec :PROTECT protect))) ((strip-keyword-list '(:LOGIC :EXEC :CORRESPONDENCE :PRESERVED :GUARD-THM :PROTECT) keyword-lst) (er-cmp ctx "Unexpected keyword~#0~[~/s~], ~&0, in field ~x1. ~@2" (evens (strip-keyword-list '(:LOGIC :EXEC :CORRESPONDENCE :PRESERVED :GUARD-THM) keyword-lst)) field0 see-doc)) ((duplicate-key-in-keyword-value-listp keyword-lst) (er-cmp ctx "Duplicate keyword~#0~[~/s~] ~&0 found in field ~x1.~|~@2" (duplicates (evens keyword-lst)) field0 see-doc)) ((not (and (symbolp exec) (function-symbolp exec wrld))) (er-cmp ctx "The :EXEC field ~x0, specified~#1~[~/ (implicitly)~] for ~ ~#2~[defabsstobj :RECOGNIZER~/defabsstobj ~ :CREATOR~/exported~] symbol ~x3, is not a function symbol ~ in the current ACL2 logical world. ~@4" exec (if exec-p 0 1) (case type (:RECOGNIZER 0) (:CREATOR 1) (otherwise 2)) name see-doc)) ((and (null protect) (not (member-eq type '(:RECOGNIZER :CREATOR))) (not (member-eq ld-skip-proofsp ; optimization '(include-book include-book-with-locals))) (unprotected-export-p st$c exec wrld)) (er-cmp ctx "The :EXEC field ~x0, specified~#1~[~/ (implicitly)~] for ~ defabsstobj field ~x2, appears capable of modifying the ~ concrete stobj, ~x3, non-atomically; yet :PROTECT T was ~ not specified for this field. ~@4" exec (if exec-p 0 1) name st$c see-doc)) (t (mv-let (guard-thm guard-thm-p) (let ((guard-thm (cadr (assoc-keyword :GUARD-THM keyword-lst)))) (cond (guard-thm (mv guard-thm t)) (t (mv (absstobj-name name :GUARD-THM) nil)))) (let* ((exec-formals (formals exec wrld)) (posn-exec (position-eq st$c exec-formals)) (stobjs-in-logic (stobjs-in logic wrld)) (stobjs-in-exec (stobjs-in exec wrld)) (stobjs-out-logic (stobjs-out logic wrld)) (stobjs-out-exec (stobjs-out exec wrld)) (posn-exec-out (position-eq st$c stobjs-out-exec)) (correspondence-required (not (eq type :RECOGNIZER))) (preserved-required (and (not (eq type :RECOGNIZER)) (member-eq st$c stobjs-out-exec)))) (mv-let (correspondence correspondence-p) (let ((corr (cadr (assoc-keyword :CORRESPONDENCE keyword-lst)))) (cond (corr (mv corr t)) (t (mv (and correspondence-required (absstobj-name name :CORRESPONDENCE)) nil)))) (mv-let (preserved preserved-p) (let ((pres (cadr (assoc-keyword :PRESERVED keyword-lst)))) (cond (pres (mv pres t)) (t (mv (and preserved-required (absstobj-name name :PRESERVED)) nil)))) (cond ((or (and (eq type :RECOGNIZER) (or correspondence-p preserved-p guard-thm-p (not logic-p) (not exec-p))) (and (eq type :CREATOR) guard-thm-p)) (er-cmp ctx "The keyword ~x0 for the ~@1. ~@2" type (cond (guard-thm-p ":GUARD-THM field is not allowed") (correspondence-p ":CORRESPONDENCE field is not allowed") (preserved-p ":PRESERVED field is not allowed") ((not logic-p) ":LOGIC field is required") (t ; (not exec-p) ":EXEC field is required")) see-doc)) ((not (and (symbolp logic) (function-symbolp logic wrld))) (er-cmp ctx "The :LOGIC field ~x0, specified~#1~[~/ ~ (implicitly)~] for ~#2~[defabsstobj ~ :RECOGNIZER~/defabsstobj :CREATOR~/exported~] ~ symbol ~x3, is not a function symbol in the ~ current ACL2 logical world. ~@4" logic (if logic-p 0 1) (case type (:RECOGNIZER 0) (:CREATOR 1) (otherwise 2)) name see-doc)) ((or (not (eq (symbol-class exec wrld) :COMMON-LISP-COMPLIANT)) (not (eq (symbol-class logic wrld) :COMMON-LISP-COMPLIANT))) (let* ((lp (not (eq (symbol-class logic wrld) :COMMON-LISP-COMPLIANT))) (implicit-p (if lp logic-p exec-p)) (fn (if lp logic exec))) (er-cmp ctx "The~#0~[~/ (implicit)~] ~x1 component of field ~ ~x2, ~x3, is a function symbol but its guards ~ have not yet been verified. ~@4" (if implicit-p 0 1) (if lp :LOGIC :EXEC) field0 fn see-doc))) ((and (eq type :RECOGNIZER) (not (eq exec (get-stobj-recognizer st$c wrld)))) ; We use the concrete recognizer in the definition of the recognizer returned ; by defabsstobj-raw-defs. (er-cmp ctx "The~#0~[~/ (implicit)~] :EXEC component, ~x1, of ~ the specified :RECOGNIZER, ~x2, is not the ~ recognizer of the :CONCRETE stobj ~x3. ~@4" (if exec-p 0 1) exec name st$c see-doc)) ((and preserved-p (not preserved-required)) (er-cmp ctx "It is illegal to specify :PRESERVED for a field ~ whose :EXEC does not return the concrete stobj. ~ In this case, :PRESERVED ~x0 has been specified ~ for an :EXEC of ~x1, which does not return ~x2. ~ ~@3" preserved exec st$c see-doc)) ((member-eq st exec-formals) ; We form the formals of name by replacing st$c by st in exec-formals. If st ; is already a formal parameter of exec-formals then this would create a ; duplicate, provided st$c is in exec-formals, as we expect it to be in that ; case (since we are presumably not looking at a creator). The ensuing defun ; would catch this duplication, but it seems most robust and friendly to cause ; a clear error here. This check could probably be eliminated by doing ; suitable renaming; but that could be awkward, and it seems quite unlikely ; that anyone will need such an enhancement. In the worst case one can of ; course define a wrapper for the :EXEC function that avoids the new stobj name, ; st. (er-cmp ctx "We do not allow the use of the defabsstobj name, ~ ~x0, in the formals of the :EXEC function of a ~ field, in particular, the :EXEC function ~x1 for ~ field ~x2. ~@3" st exec field0 see-doc)) ((and (eq type :CREATOR) (not (and (null stobjs-in-logic) (null stobjs-in-exec) (null (cdr stobjs-out-exec)) (eq (car stobjs-out-exec) st$c) (null (cdr stobjs-in-exec)) (eql (length stobjs-out-logic) 1)))) (cond ((or stobjs-in-logic stobjs-in-exec) (er-cmp ctx "The :LOGIC and :EXEC versions of the ~ :CREATOR function must both be functions ~ of no arguments but ~&0 ~#0~[is not such a ~ function~/xare not such functions~]. ~@1" (append (and stobjs-in-logic (list logic)) (and stobjs-in-exec (list exec))) see-doc)) ((or (not (eql (length stobjs-out-logic) 1)) (not (eql (length stobjs-out-exec) 1))) (er-cmp ctx "The :LOGIC and :EXEC versions of the ~ :CREATOR function must both be functions ~ that return a single value, but ~&0 ~ ~#0~[is not such a function~/are not such ~ functions~]. ~@1" (append (and (not (eql (length stobjs-out-logic) 1)) (list logic)) (and (not (eql (length stobjs-out-exec) 1)) (list exec))) see-doc)) (t ; (not (eq (car stobjs-out-exec) st$c)) (er-cmp ctx "The :EXEC version of the :CREATOR function ~ must return a single value that is the ~ stobj ~x0, but ~x1 does not have that ~ property. ~@2" st$c exec see-doc)))) ((and (not (eq type :CREATOR)) (not posn-exec)) ; Warning: before weakening this test, consider how it is relied upon in ; absstobj-correspondence-formula. Also, note that stobj-creatorp relies on ; empty formals, so this check guarantees that stobj-creatorp returns nil for ; functions other than the creator. (er-cmp ctx "The :CONCRETE stobj name, ~x0, is not a known ~ stobj parameter of :EXEC function ~x1 for field ~ ~x2.~|~@3" st$c exec field0 see-doc)) ((and (not (eq type :CREATOR)) (not (and (equal (length stobjs-in-logic) (length stobjs-in-exec)) (equal (update-nth posn-exec nil stobjs-in-logic) (update-nth posn-exec nil stobjs-in-exec))))) (er-cmp ctx "The input signatures of the :LOGIC and :EXEC ~ functions for a field must agree except perhaps ~ at the position of the concrete stobj (~x0) in ~ the :EXEC function (which is zero-based position ~ ~x1). However, this agreement fails for field ~ ~x2, as the input signatures are as ~ follows.~|~%~x3 (:LOGIC):~|~X47~|~%~x5 ~ (:EXEC):~|~X67~|~%~@8" st$c posn-exec field0 logic (prettyify-stobj-flags stobjs-in-logic) exec (prettyify-stobj-flags stobjs-in-exec) nil see-doc)) ((and (not (eq type :CREATOR)) ; handled elsewhere (not (and (equal (length stobjs-out-logic) (length stobjs-out-exec)) (equal stobjs-out-exec (if posn-exec-out (update-nth posn-exec-out (assert$ posn-exec (nth posn-exec stobjs-in-exec)) stobjs-out-logic) stobjs-out-logic))))) (er-cmp ctx "The output signatures of the :LOGIC and :EXEC ~ functions for a field must have the same length ~ and must agree at each position, except for the ~ position of concrete stobj (~x0) in the outputs ~ of the :EXEC function. For that position, the ~ :LOGIC function should return the type of the ~ object (stobj or not) that is at the position of ~ ~x0 in the inputs of the :EXEC function. ~ However, the criteria above are not all met for ~ field ~x1, as the output signatures are as ~ follows.~|~%~x2 (:LOGIC):~|~X36~|~%~x4 ~ (:EXEC):~|~X56~|~%~@7" st$c field0 logic (prettyify-stobj-flags stobjs-out-logic) exec (prettyify-stobj-flags stobjs-out-exec) nil see-doc)) (t (let* ((formals (if (eq type :CREATOR) nil (update-nth posn-exec st exec-formals))) (guard-pre (subcor-var (formals logic wrld) formals (guard logic nil wrld)))) (value-cmp (make absstobj-method :NAME name :FORMALS formals :GUARD-PRE guard-pre :GUARD-POST nil ; to be filled in later :GUARD-THM guard-thm :GUARD-THM-P (if type :SKIP guard-thm-p) :STOBJS-IN-POSN posn-exec :STOBJS-IN-EXEC (stobjs-in exec wrld) :STOBJS-OUT (substitute st st$c stobjs-out-exec) :LOGIC logic :EXEC exec :CORRESPONDENCE correspondence :PRESERVED preserved :PROTECT protect)))))))))))))))))))) (defun simple-translate-absstobj-fields (st st$c fields types protect-default ld-skip-proofsp) ; Warning: Return methods in the same order as fields. See the comments about ; simple-translate-absstobj-fields in the #-acl2-loop-only definition of ; defabsstobj. Each returned method has only the :NAME, :LOGIC, :EXEC, and ; :PROTECT fields filled in (the others are nil). (cond ((endp fields) (mv nil nil)) (t (er-let*-cmp ((method (translate-absstobj-field st st$c (car fields) (car types) protect-default ld-skip-proofsp "" 'defabsstobj nil)) (rest (simple-translate-absstobj-fields st st$c (cdr fields) (cdr types) protect-default ld-skip-proofsp))) (value-cmp (cons method rest)))))) (defun one-way-unify-p (pat term) ; Returns true when term2 is an instance of term1. (or (equal pat term) ; optimization (mv-let (ans unify-subst) (one-way-unify pat term) (declare (ignore unify-subst)) ans))) (defun obviously-iff-equiv-terms (x y) ; Warning: It would be best to keep this in sync with untranslate1, ; specifically, giving similar attention in both to functions like implies, ; iff, and not, which depend only on the propositional equivalence class of ; each argument. ; Here we code a weak version of Boolean equivalence of x and y, for use in ; chk-defabsstobj-method-lemmas or other places where we expect this to be ; sufficient. For example, in the lambda case we could weaken the requirement ; that the args are equal by beta-reducing x and y, but that would be less ; efficient so we don't bother. (or (equal x y) ; common case (cond ((or (variablep x) (fquotep x) (variablep y) (fquotep y)) nil) ((flambda-applicationp x) (and (flambda-applicationp y) (equal (lambda-formals x) (lambda-formals y)) (obviously-iff-equiv-terms (lambda-body x) (lambda-body y)) (equal (fargs x) (fargs y)))) ((not (eq (ffn-symb x) (ffn-symb y))) nil) ((member-eq (ffn-symb x) '(implies iff)) (and (obviously-iff-equiv-terms (fargn x 1) (fargn y 1)) (obviously-iff-equiv-terms (fargn x 2) (fargn y 2)))) ((eq (ffn-symb x) 'not) (obviously-iff-equiv-terms (fargn x 1) (fargn y 1))) ((eq (ffn-symb x) 'if) (and (obviously-iff-equiv-terms (fargn x 1) (fargn y 1)) (obviously-iff-equiv-terms (fargn x 3) (fargn y 3)) (or (obviously-iff-equiv-terms (fargn x 2) (fargn y 2)) ; Handle case that a term is of the form (or u v). (cond ((equal (fargn x 2) *t*) (equal (fargn y 2) (fargn y 1))) ((equal (fargn y 2) *t*) (equal (fargn x 2) (fargn x 1))) (t nil))))) (t nil)))) (defun chk-defabsstobj-method-lemmas (method st st$c st$ap corr-fn missing wrld state) (let ((correspondence (access absstobj-method method :CORRESPONDENCE)) (preserved (access absstobj-method method :PRESERVED))) (cond ((null correspondence) ; recognizer method (assert$ (null preserved) (value (cons missing wrld)))) (t (let* ((formals (access absstobj-method method :FORMALS)) (guard-pre (access absstobj-method method :GUARD-PRE)) (logic (access absstobj-method method :LOGIC)) (exec (access absstobj-method method :EXEC)) (expected-corr-formula (absstobj-correspondence-formula logic exec corr-fn formals guard-pre st st$c wrld)) (old-corr-formula (formula correspondence nil wrld)) (tuple (cond ((null old-corr-formula) `(,correspondence ,expected-corr-formula)) ((obviously-iff-equiv-terms expected-corr-formula old-corr-formula) ; We will be printing formulas with untranslate using t for its iff-flg, for ; readability. But imagine what happens if the printed, untranslated formula ; has a call (or x y) that came from translated formula (if x 't y). ; When the user submits a version with (or x y), it will translate to (if x x ; y), and we will have a mismatch! Thus, we allow obviously-iff-equiv-terms ; rather than requiring equality. ; Why not consider it sufficient for the two formulas to untranslate, using ; iff-flg = t, to the same user-level formula? The problem is that utilities ; like untranslate, untranslate*, and even untranslate1 depend on inputs that ; can destroy any meaningful semantics for these functions. In particular, ; (untrans-table wrld) is important for getting pretty results from ; untranslate, but we cannot trust it to produce meaningful results because the ; user gets to decide what goes into this table. nil) ((one-way-unify-p old-corr-formula expected-corr-formula) nil) (t `(,correspondence ,expected-corr-formula ,@old-corr-formula)))) (missing (cond (tuple (cons tuple missing)) (t missing))) (guard-thm-p (access absstobj-method method :GUARD-THM-P)) (tuple (cond ((eq guard-thm-p :SKIP) nil) (t (let* ((expected-guard-thm-formula (make-implication (cons (fcons-term* corr-fn st$c st) (flatten-ands-in-lit guard-pre)) (conjoin (flatten-ands-in-lit (guard exec t wrld))))) (taut-p (and (null guard-thm-p) (tautologyp expected-guard-thm-formula wrld))) (guard-thm (access absstobj-method method :GUARD-THM)) (old-guard-thm-formula (and (not taut-p) ; optimization (formula guard-thm nil wrld)))) (cond (taut-p nil) ((null old-guard-thm-formula) `(,guard-thm ,expected-guard-thm-formula)) ((obviously-iff-equiv-terms expected-guard-thm-formula old-guard-thm-formula) ; See the comment at the first call of obviously-iff-equiv-terms above. nil) ((one-way-unify-p old-guard-thm-formula expected-guard-thm-formula) nil) (t `(,guard-thm ,expected-guard-thm-formula ,@old-guard-thm-formula))))))) (missing (cond (tuple (cons tuple missing)) (t missing)))) (cond ((null preserved) (value (cons missing wrld))) (t (let* ((expected-preserved-formula (absstobj-preserved-formula logic exec formals guard-pre st st$c st$ap wrld)) (old-preserved-formula (formula preserved nil wrld)) (tuple (cond ((null old-preserved-formula) `(,preserved ,expected-preserved-formula)) ((obviously-iff-equiv-terms expected-preserved-formula old-preserved-formula) ; See the comment at the first call of obviously-iff-equiv-terms above. nil) ((one-way-unify-p old-preserved-formula expected-preserved-formula) nil) (t `(,preserved ,expected-preserved-formula ,@old-preserved-formula)))) (missing (cond (tuple (cons tuple missing)) (t missing)))) (value (cons missing wrld)))))))))) (defun chk-defabsstobj-method (method st st$c st$ap corr-fn congruent-to missing ctx wrld state) ; The input, missing, is a list of tuples (name expected-event . old-event), ; where old-event may be nil; see chk-acceptable-defabsstobj. We return a pair ; (missing1 . wrld1), where missing1 extends missing as above and wrld1 extends ; wrld as necessary for redefinition. (let ((name (access absstobj-method method :name))) (er-let* ((wrld (er-progn (chk-all-but-new-name name ctx 'function wrld state) (chk-just-new-name name 'function nil ctx wrld state)))) (cond ((or congruent-to (member-eq (ld-skip-proofsp state) '(include-book include-book-with-locals))) ; We allow the :correspondence, :preserved, and :guard-thm theorems to be ; local. (value (cons missing wrld))) (t (chk-defabsstobj-method-lemmas method st st$c st$ap corr-fn missing wrld state)))))) (defun chk-acceptable-defabsstobj1 (st st$c st$ap corr-fn fields types protect-default congruent-to see-doc ctx wrld state methods missing) ; See chk-acceptable-defabsstobj (whose return value is computed by the present ; function) for the form of the result. Note that fields begins with the ; recognizer and then the creator; see the comments about ; chk-acceptable-defabsstobj1 in defabsstobj-fn1 and ; chk-acceptable-defabsstobj. (cond ((endp fields) (value (list* (reverse missing) (reverse methods) wrld))) (t (mv-let (erp method) (translate-absstobj-field st st$c (car fields) (car types) protect-default (ld-skip-proofsp state) see-doc ctx wrld) (cond (erp ; erp is ctx, method is a msg (er soft erp "~@0" method)) (t (er-let* ((missing/wrld (chk-defabsstobj-method method st st$c st$ap corr-fn congruent-to missing ctx wrld state))) (let ((missing (car missing/wrld)) (wrld (cdr missing/wrld))) (cond ((assoc-eq (access absstobj-method method :name) methods) (er soft ctx "The name ~x0 is introduced more than once by a ~ DEFABSSTOBJ event. ~@1" (access absstobj-method method :name) see-doc)) (t (chk-acceptable-defabsstobj1 st st$c st$ap corr-fn (cdr fields) (cdr types) protect-default congruent-to see-doc ctx wrld state (cons method methods) missing))))))))))) (defun first-keyword (lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((keywordp (car lst)) (car lst)) (t (first-keyword (cdr lst))))) (defun chk-acceptable-defabsstobj (name st$c recognizer st$ap creator corr-fn exports protect-default congruent-to doc see-doc ctx wrld state event-form) ; We return an error triple such that when there is no error, the value ; component is either 'redundant or is a tuple of the form (missing methods ; . wrld1). Missing is always nil if we are including a book; otherwise, ; missing is a list of tuples (name event . old-event), where event must be ; proved and old-event is an existing event of the same name that ; (unfortunately) differs from event, if such exists, and otherwise old-event ; is nil. Methods is a list of absstobj-method records corresponding to the ; recognizer, creator, and exports. Wrld1 is an extension of the given world, ; wrld, that deals with redefinition. (cond ((atom exports) (er soft ctx "~x0 requires at least one export. ~@1" 'defabsstobj see-doc)) ((redundant-defabsstobjp name event-form wrld) (value 'redundant)) ((not (stobjp st$c t wrld)) (er soft ctx "The symbol ~x0 is not the name of a stobj in the current ACL2 world. ~ ~ ~@1" st$c see-doc)) ((getprop st$c 'absstobj-info nil 'current-acl2-world wrld) (er soft ctx "The symbol ~x0 is the name of an abstract stobj in the current ACL2 ~ world, so it is not legal for use as the :CONCRETE argument of ~ DEFABSSTOBJ. ~@1" st$c see-doc)) ((not (true-listp exports)) (er soft ctx "DEFABSSTOBJ requires the value of its :EXPORTS keyword argument to ~ be a non-empty true list. ~@0" see-doc)) ((first-keyword exports) ; early error here, as a courtesy (er soft ctx "The keyword ~x0 is being specified as an export. This may indicate ~ a parenthesis error, since keywords cannot be exports. ~@1" (first-keyword exports) see-doc)) ((and congruent-to (not (and (symbolp congruent-to) (getprop congruent-to 'absstobj-info nil 'current-acl2-world wrld)))) ; Here, we only check that congruent-to is a candidate for a congruent abstract ; stobj. The check is elsewhere that it is truly congruent to the proposed ; abstract stobj. But at least we will know that congruent-to, if non-nil, ; does name some abstract stobj; see the binding of old-absstobj-info in ; defabsstobj-fn1. (er soft ctx "The :CONGRUENT-TO parameter of a DEFABSSTOBJ must either be nil or ~ the name of an existing abstract stobj, but the value ~x0 is ~ neither. ~@1." congruent-to see-doc)) (t (er-progn (chk-all-but-new-name name ctx 'stobj wrld state) (chk-legal-defstobj-name name state) ; We use translate-doc here just to check the string. We throw away ; the section-symbol and citations returned. We'll repeat this later. (translate-doc name doc ctx state) (er-let* ((wrld1 (chk-just-new-name name 'stobj nil ctx wrld state)) (wrld2 (chk-just-new-name (the-live-var name) 'stobj-live-var nil ctx wrld1 state))) (chk-acceptable-defabsstobj1 name st$c st$ap corr-fn ; Keep the recognizer and creator first and second in our call to ; chk-acceptable-defabsstobj1. See the comment about ; chk-acceptable-defabsstobj1 in defabsstobj-fn1, and also note that the first ; two methods must be for the recognizer and creator in defabsstobj-raw-defs, ; which is called in defabsstobj-fn1, where it consumes the methods we return ; here. (list* recognizer creator exports) (list* :RECOGNIZER :CREATOR nil) protect-default congruent-to see-doc ctx wrld2 state nil nil)))))) (defun defabsstobj-axiomatic-defs (st$c methods) (cond ((endp methods) nil) (t (cons (let ((method (car methods))) (mv-let (name formals guard-post logic stobjs) (mv (access absstobj-method method :NAME) (access absstobj-method method :FORMALS) (access absstobj-method method :GUARD-POST) (access absstobj-method method :LOGIC) (remove1 st$c (collect-non-x nil (access absstobj-method method :STOBJS-IN-EXEC)))) `(,name ,formals (declare (xargs ,@(and stobjs `(:STOBJS ,stobjs)) :GUARD ,guard-post)) (,logic ,@formals)))) (defabsstobj-axiomatic-defs st$c (cdr methods)))))) (defun defabsstobj-raw-def (method) ; Warning: Method, which is an absstobj-method record, might only have valid ; :NAME, :LOGIC, :EXEC, and :PROTECT fields filled in. Do not use other fields ; unless you adjust how methods is passed in. (let* ((name (access absstobj-method method :NAME)) (exec (access absstobj-method method :EXEC)) (protect (access absstobj-method method :PROTECT)) (body (cond ((null protect) `(cons ',exec args)) (t ``(let* ((temp *inside-absstobj-update*) (saved (svref temp 0))) (declare (type simple-array temp)) (cond ((eql saved 0) (setf (svref temp 0) 1) (our-multiple-value-prog1 ,(cons ',exec args) (setf (svref temp 0) 0))) ((typep saved 'fixnum) (setf (svref temp 0) (1+ (the fixnum saved))) (our-multiple-value-prog1 ,(cons ',exec args) (decf (the fixnum (svref temp 0))))) (t ; If saved_var is a number, then it is bounded by the number of calls of ; abstract stobj exports on the stack. But surely the length of the stack is a ; fixnum! So if saved_var is not a fixnum, then it is not a number, and hence ; it must be a symbol or a list of symbols with a non-nil final cdr. (let ((sym ',',name)) (declare (type symbol sym)) (cond ((eq nil saved) (setf (svref temp 0) (the symbol sym)) (our-multiple-value-prog1 ,(cons ',exec args) (setf (svref temp 0) nil))) (t (push (the symbol sym) saved) (our-multiple-value-prog1 ,(cons ',exec args) (pop (svref temp 0))))))))))))) `(,name (&rest args) ,body))) (defun defabsstobj-raw-defs-rec (methods) ; See defabsstobj-raw-defs. (cond ((endp methods) nil) (t (cons (defabsstobj-raw-def (car methods)) (defabsstobj-raw-defs-rec (cdr methods)))))) (defun defabsstobj-raw-defs (st-name methods) ; Warning: Each method, which is an absstobj-method record, might only have ; valid :NAME, :LOGIC, :EXEC, and :PROTECT fields filled in. Do not use other ; fields unless you adjust how methods is passed in. ; Warning: The first two methods in methods should be for the recognizer and ; creator, respectively. See comments about that where defabsstobj-raw-defs is ; called. ; We define the bodies of macros. By defining macros instead of functions, not ; only do we get better runtime efficiency, but also we avoid having to grab ; formals for the :EXEC function from the world. ; We pass in st-name because when we call defabsstobj-raw-defs from the ; #-acl2-loop-only definition of defabsstobj, we have methods that have nil for ; their :LOGIC components, and we need st-name to generate the :LOGIC ; recognizer name. (list* (let* ((method (car methods)) ; for the recognizer (name (access absstobj-method method :NAME)) (logic (or (access absstobj-method method :LOGIC) (absstobj-name st-name :RECOGNIZER-LOGIC)))) `(,name (x) ; recognizer definition (list 'let (list (list 'y x)) '(cond ((live-stobjp y) t) (t (,logic y)))))) (let* ((method (cadr methods)) ; for the creator (name (access absstobj-method method :NAME)) (exec (access absstobj-method method :EXEC))) (assert$ (not (eq exec 'args)) ; ACL2 built-in `(,name (&rest args) (cons ',exec args)))) (defabsstobj-raw-defs-rec (cddr methods)))) (defun expand-recognizer (st-name recognizer see-doc ctx state) (cond ((null recognizer) (value (list (absstobj-name st-name :RECOGNIZER) :LOGIC (absstobj-name st-name :RECOGNIZER-LOGIC) :EXEC (absstobj-name st-name :RECOGNIZER-EXEC)))) ((and (consp recognizer) (keyword-value-listp (cdr recognizer)) (assoc-keyword :LOGIC (cdr recognizer)) (assoc-keyword :EXEC (cdr recognizer)) (null (cddddr (cdr recognizer)))) (value recognizer)) (t (er soft ctx "Illegal :RECOGNIZER field. ~@0" see-doc)))) (defun put-absstobjs-in-and-outs (st methods wrld) (cond ((endp methods) wrld) (t (put-absstobjs-in-and-outs st (cdr methods) (mv-let (name posn stobjs-in-exec stobjs-out) (let ((method (car methods))) (mv (access absstobj-method method :name) (access absstobj-method method :stobjs-in-posn) (access absstobj-method method :stobjs-in-exec) (access absstobj-method method :stobjs-out))) (putprop name 'stobjs-in (if posn (update-nth posn st stobjs-in-exec) stobjs-in-exec) (putprop name 'stobjs-out stobjs-out wrld))))))) (defun method-exec (name methods) (cond ((endp methods) (er hard 'method-exec "Name ~x0 not found in methods, ~x1." name methods)) ((eq name (access absstobj-method (car methods) :name)) (access absstobj-method (car methods) :exec)) (t (method-exec name (cdr methods))))) (defun defabsstobj-raw-init (creator-name methods) `(,(method-exec creator-name methods))) (defun defabsstobj-missing-msg (missing wrld) ; We are given missing, a list of tuples (name expected-event . old-event), ; where old-event may be nil; see chk-acceptable-defabsstobj. We return a ; message for ~@ fmt printing that indicates the events remaining to be proved ; in support of a defabsstobj event. (assert$ missing (let* ((tuple (car missing)) (name (car tuple)) (expected-formula (untranslate (cadr tuple) t wrld)) (old-formula (untranslate (cddr tuple) t wrld)) (expected-defthm `(defthm ,name ,expected-formula :rule-classes nil)) (msg (cond (old-formula (msg "~%~Y01[Note discrepancy with existing ~ formula named ~x2:~| ~Y31~|]~%" expected-defthm nil name old-formula)) (t (msg "~%~Y01" expected-defthm nil name old-formula))))) (cond ((endp (cdr missing)) msg) (t (msg "~@0~@1" msg (defabsstobj-missing-msg (cdr missing) wrld))))))) (defun update-guard-post (logic-subst methods) ; Note that the original :guard-pre term is the guard of a guard-verified ; function; hence its guard proof obligations are provable. The guard proof ; obligations for the new :guard-post (created below using sublis-fn-simple) by ; replacing some functions with equal functions, and hence are also provable. ; Thus, the guard of the guard of an exported function, which comes from the ; :guard-post field of the corresponding method, has provable guard proof ; obligations, as we would expect for guard-of-the-guard, which is important ; for avoiding guard violations while checking the guard for a function call. (cond ((endp methods) nil) (t (cons (change absstobj-method (car methods) :guard-post (sublis-fn-simple logic-subst (access absstobj-method (car methods) :guard-pre))) (update-guard-post logic-subst (cdr methods)))))) (defun defabsstobj-logic-subst (methods) (cond ((endp methods) nil) (t (acons (access absstobj-method (car methods) :logic) (access absstobj-method (car methods) :name) (defabsstobj-logic-subst (cdr methods)))))) (defun chk-defabsstobj-guard (method ctx wrld state-vars) ; Warning: Keep this call of translate in sync with the call of ; translate-term-lst in chk-acceptable-defuns1. (mv-let (ctx msg) (translate-cmp (access absstobj-method method :guard-post) '(nil) ; stobjs-out t ; logic-modep = t because we expect :logic mode here (stobjs-in (access absstobj-method method :name) wrld) ctx wrld state-vars) (cond (ctx (er-cmp ctx "The guard for exported function ~x0 fails to ~ pass a test for being suitably single-threaded. ~ ~ Here is that guard (derived from the guard ~ for function ~x1).~| ~x2~|And here is the ~ error message for the failed test.~| ~@3" (access absstobj-method method :name) (access absstobj-method method :logic) (access absstobj-method method :guard-post) msg)) (t (value-cmp nil))))) (defun chk-defabsstobj-guards1 (methods msg ctx wrld state-vars) (cond ((endp methods) msg) (t (mv-let (ctx0 msg0) (chk-defabsstobj-guard (car methods) ctx wrld state-vars) (chk-defabsstobj-guards1 (cdr methods) (cond (ctx0 (assert$ msg0 (cond (msg (msg "~@0~|~%~@1" msg msg0)) (t msg0)))) (t msg)) ctx wrld state-vars))))) (defun chk-defabsstobj-guards (methods congruent-to ctx wrld state) (cond (congruent-to (value nil)) ; no need to check! (t (let ((msg (chk-defabsstobj-guards1 methods nil ctx wrld (default-state-vars t)))) (cond (msg (er soft ctx "At least one guard of an exported function fails to ~ obey single-threadedness restrictions. See :DOC ~ defabsstobj. See below for details.~|~%~@0~|~%" msg)) (t (value nil))))))) (defun make-absstobj-logic-exec-pairs (methods) (cond ((endp methods) nil) (t (cons (cons (access absstobj-method (car methods) :logic) (access absstobj-method (car methods) :exec)) (make-absstobj-logic-exec-pairs (cdr methods)))))) (defun defabsstobj-fn1 (st-name st$c recognizer creator corr-fn exports protect-default congruent-to missing-only doc ctx state event-form) (let* ((wrld0 (w state)) (see-doc "See :DOC defabsstobj.") (st$c (or st$c (absstobj-name st-name :C))) (creator (or creator (absstobj-name st-name :CREATOR))) (creator-name (if (consp creator) (car creator) creator)) (corr-fn (or corr-fn (absstobj-name st-name :CORR-FN)))) (er-let* ((recognizer (expand-recognizer st-name recognizer see-doc ctx state)) (st$ap (value (cadr (assoc-keyword :logic (cdr recognizer))))) (missing/methods/wrld1 (chk-acceptable-defabsstobj st-name st$c recognizer st$ap creator corr-fn exports protect-default congruent-to doc see-doc ctx wrld0 state event-form))) (cond ((eq missing/methods/wrld1 'redundant) (stop-redundant-event ctx state)) ((and missing-only (not congruent-to)) ; else do check before returning missing (value (car missing/methods/wrld1))) (t (let* ((missing (car missing/methods/wrld1)) (methods0 (cadr missing/methods/wrld1)) (old-absstobj-info ; Note that if old-absstobj-info is non-nil, then because of the congruent-to ; check in chk-acceptable-defabsstobj, congruent-to is a symbol and the getprop ; below returns a non-nil value (which must then be an absstobj-info record). ; See the comment about this in chk-acceptable-defabsstobj. (and congruent-to (getprop congruent-to 'absstobj-info nil 'current-acl2-world wrld0))) (logic-exec-pairs (make-absstobj-logic-exec-pairs methods0))) (cond ((and congruent-to (not (equal st$c (access absstobj-info old-absstobj-info :st$c)))) (er soft ctx "The value provided for :congruent-to, ~x0, is illegal, ~ because the concrete stobj associated with ~x0 is ~x1, while ~ the concrete stobj proposed for ~x2 is ~x3. ~@4" congruent-to (access absstobj-info old-absstobj-info :st$c) st-name st$c see-doc)) ((and congruent-to (not (equal logic-exec-pairs (access absstobj-info old-absstobj-info :logic-exec-pairs)))) (er soft ctx "The value provided for :congruent-to, ~x0, is illegal. ACL2 ~ requires that the :LOGIC and :EXEC functions match up ~ perfectly (in the same order), for stobj primitives ~ introduced by the proposed new abstract stobj, ~x1 and the ~ existing stobj to which it is supposed to be congruent, ~x0. ~ Here are the lists of pairs (:LOGIC . :EXEC) for ~ each.~|~%For ~x1 (proposed):~|~Y24~%For ~x0:~|~Y34~%~|~@5" congruent-to st-name logic-exec-pairs (access absstobj-info old-absstobj-info :logic-exec-pairs) nil see-doc)) (missing-only (value missing)) (t (er-progn (cond ((or (null missing) (member-eq (ld-skip-proofsp state) '(include-book include-book-with-locals))) (value nil)) ((ld-skip-proofsp state) (pprogn (warning$ ctx "defabsstobj" "The following events would have to be ~ admitted, if not for proofs currently being ~ skipped (see :DOC ld-skip-proofsp), before ~ the given defabsstobj event. ~@0~|~@1" see-doc (defabsstobj-missing-msg missing wrld0)) (value nil))) (t (er soft ctx "The following events must be admitted before the given ~ defabsstobj event. ~@0~|~@1" see-doc (defabsstobj-missing-msg missing wrld0)))) (enforce-redundancy event-form ctx wrld0 (let* ((methods (update-guard-post (defabsstobj-logic-subst methods0) methods0)) (wrld1 (cddr missing/methods/wrld1)) (ax-def-lst (defabsstobj-axiomatic-defs st$c methods)) (raw-def-lst ; The first method in methods is for the recognizer, as is guaranteed by ; chk-acceptable-defabsstobj (as explained in a comment there that refers to ; the present function, defabsstobj-fn1). (defabsstobj-raw-defs st-name methods)) (names (strip-cars ax-def-lst)) (the-live-var (the-live-var st-name))) (er-progn (cond ((equal names (strip-cars raw-def-lst)) (value nil)) (t (value (er hard ctx "Defabsstobj-axiomatic-defs and ~ defabsstobj-raw-defs are out of sync! We ~ expect them to define the same list of names. ~ ~ Here are the strip-cars of the axiomatic ~ defs: ~x0. And here are the strip-cars of ~ the raw defs: ~x1." names (strip-cars raw-def-lst))))) (revert-world-on-error (pprogn (set-w 'extension wrld1 state) (er-progn (process-embedded-events 'defabsstobj (table-alist 'acl2-defaults-table wrld1) (or (ld-skip-proofsp state) t) (current-package state) (list 'defstobj st-name names) ; ee-entry (append (pairlis-x1 'defun ax-def-lst) `((encapsulate () (set-inhibit-warnings "theory") (in-theory (disable (:executable-counterpart ,creator-name)))))) 0 t ; might as well do make-event check ctx state) ; The processing above will install defun events but defers installation of raw ; Lisp definitions, just as for defstobj. (er-let* ((doc-pair (translate-doc st-name doc ctx state))) (let* ((wrld2 (w state)) (wrld3 (update-doc-database st-name doc doc-pair (putprop st-name 'congruent-stobj-rep (and congruent-to (congruent-stobj-rep congruent-to wrld2)) (putprop st-name 'absstobj-info (make absstobj-info :st$c st$c :logic-exec-pairs logic-exec-pairs) (putprop st-name 'symbol-class :common-lisp-compliant (put-absstobjs-in-and-outs st-name methods (putprop st-name 'stobj (cons the-live-var ; Names is in the right order; it does not need adjustment as is the case for ; corresponding code in defstobj-fn. See the comment about ; chk-acceptable-defabsstobj1 in chk-acceptable-defabsstobj. names) (putprop-x-lst1 names 'stobj-function st-name (putprop the-live-var 'stobj-live-var st-name (putprop the-live-var 'symbol-class :common-lisp-compliant wrld2))))))))))) (pprogn (set-w 'extension wrld3 state) (er-progn (chk-defabsstobj-guards methods congruent-to ctx wrld3 state) ; The call of install-event below follows closely the corresponding call in ; defstobj-fn. In particular, see the comment in defstobj-fn about a "cheat". (install-event st-name event-form 'defstobj (list* st-name the-live-var names) ; namex nil `(defabsstobj ,st-name ,the-live-var ,(defabsstobj-raw-init creator-name methods) ,raw-def-lst ,event-form ,ax-def-lst) t ctx wrld3 state)))))))))))))))))))) (defun defabsstobj-fn (st-name st$c recognizer creator corr-fn exports protect-default congruent-to missing-only doc state event-form) ; This definition shares a lot of code and ideas with the definition of ; defstobj-fn. See the comments there for further explanation. Note that we ; use the name "defstobj" instead of "defabsstobj" in some cases where defstobj ; and defabsstobj are handled similarly. For example, install-event-defuns ; uses (cons 'defstobj (defstobj-functionsp ...)) for the ignorep field of its ; cltl-command because we look for such a cons in add-trip, and ; defstobj-functionsp looks for 'defstobj in the embedded-event-lst, which is ; why the ee-entry argument of process-embedded-events below uses 'defstobj. (with-ctx-summarized (if (output-in-infixp state) event-form (msg "( DEFABSSTOBJ ~x0 ...)" st-name)) (defabsstobj-fn1 st-name st$c recognizer creator corr-fn exports protect-default congruent-to missing-only doc ctx state event-form))) (deflabel stobj :doc ":Doc-Section stobj single-threaded objects or ``von Neumann bottlenecks''~/ In ACL2, a ``single-threaded object'' is a data structure whose use is so syntactically restricted that only one instance of the object need ever exist and its fields can be updated by destructive assignments. Note: Novices are advised to avoid using single-threaded objects, perhaps instead using community book ~c[books/data-structures/structures.lisp]. At the least, consider using ~c[(]~ilc[set-verify-guards-eagerness]~c[ 0)] to avoid ~il[guard] verification. The documentation in this section is laid out in the form of a tour that visits the documented topics in a reasonable order. We recommend that you follow the tour the first time you read about stobjs. The list of all stobj topics is shown below. The tour starts immediately afterwards. Also ~pl[defstobj] and, for so-called abstract stobjs, ~pl[defabsstobj].~/ As noted, a ``single-threaded object'' is a data structure whose use is so syntactically restricted that only one instance of the object need ever exist. Updates to the object must be sequentialized. This allows us to update its fields with destructive assignments without wrecking the axiomatic semantics of update-by-copy. For this reason, single-threaded objects are sometimes called ``von Neumann bottlenecks.'' From the logical perspective, a single-threaded object is an ordinary ACL2 object, e.g., composed of integers and conses. Logically speaking, ordinary ACL2 functions are defined to allow the user to ``access'' and ``update'' its fields. Logically speaking, when fields in the object, obj, are ``updated'' with new values, a new object, obj', is constructed. But suppose that by syntactic means we could ensure that there were no more references to the ``old'' object, obj. Then we could create obj' by destructively modifying the memory locations involved in the representation of obj. The syntactic means is pretty simple but draconian: the only reference to obj is in the variable named ~c[OBJ]. The consequences of this simple rule are far-reaching and require some getting used to. For example, if ~c[OBJ] has been declared as a single-threaded object name, then the following consequences ensue (but see the discussion of congruent stobjs below for a slight relaxation). o ~c[OBJ] is a top-level global variable that contains the current object, obj. o If a function uses the formal parameter ~c[OBJ], the only ``actual expression'' that can be passed into that slot is the variable ~c[OBJ], not merely a term that ``evaluates to an obj''; thus, such functions can only operate on the current object. So for example, instead of ~c[(FOO (UPDATE-FIELD1 3 ST))] write ~c[(LET ((ST (UPDATE-FIELD1 3 ST))) (FOO ST))]. o The accessors and updaters have a formal parameter named ~c[OBJ], so by the rule just above, those functions can only be applied to the current object. The recognizer is the one exception to the rule: it may be applied either the ~c[OBJ] or to an ordinary (non-stobj) object. o The ACL2 primitives, such as ~c[CONS], ~c[CAR] and ~c[CDR], may not be applied to the variable ~c[OBJ]. Thus, for example, obj may not be consed into a list (which would create another pointer to it) or accessed or copied via ``unapproved'' means. o The updaters return a ``new ~c[OBJ] object'', i.e., obj'; thus, when an updater is called, the only variable which can hold its result is ~c[OBJ]. o If a function calls an ~c[OBJ] updater, it must return an ~c[OBJ] object (either as the sole value returned, or in ~c[(mv ... OBJ ...)]; ~pl[mv]). o When a top-level expression involving ~c[OBJ] returns an ~c[OBJ] object, that object becomes the new current value of ~c[OBJ]. There are other functional languages supporting single-threadedness, for example Haskell's ``monads'' and Clean's ``uniqueness type system''. Of course, ACL2 provides a theorem prover that can prove theorems that involve such constructs. Note that the syntactic restrictions noted above are enforced only when single-threaded objects are encountered directly in the top-level loop or are used in function definitions; the accessor and update functions for single-threaded objects may be used without restriction in formulas to be proved. Since function evaluation is sometimes necessary during proofs, ACL2 must be able to evaluate these functions on logical constants representing the object, even when the constant is not ``the current object.'' Thus, ACL2 supports both the efficient von Neumann semantics and the clean applicative semantics, and uses the first in contexts where execution speed is paramount and the second during proofs. ~ilc[Defstobj] and ~ilc[defabsstobj] ~il[events] introduce stobjs. ~l[defstobj] for more details about stobjs. In particular, a relatively advanced notion of ``congruent stobjs'' is discussed there. The idea is to allow a stobj, ~c[st2], of the same ``shape'' as a given stobj, ~c[st1], to be used in place of ~c[st1]. Other ~ilc[defstobj] keywords allow inlining and renaming of stobj accessors and updaters. But we are getting ahead of ourselves. To start the stobj tour, ~pl[stobj-example-1].~/ :cite defstobj :cite defabsstobj") (deflabel stobj-example-1 :doc ":Doc-Section stobj an example of the use of single-threaded objects~/ Suppose we want to sweep a tree and (1) count the number of interior nodes, (2) count the number of tips and (3) keep a record of every tip we encounter that is an integer. We could use a single-threaded object as our ``accumulator''. Such an object would have three fields, one holding the number of nodes seen so far, one holding the number of tips, and one holding all the integer tips seen.~/ The following event declares ~c[counters] to be a single-threaded object. ~bv[] (defstobj counters (NodeCnt :type integer :initially 0) (TipCnt :type integer :initially 0) (IntTipsSeen :type t :initially nil)) ~ev[] It has three fields, ~c[NodeCnt], ~c[TipCnt], and ~c[IntTipsSeen]. (As always in ACL2, capitalization is irrelevant in simple symbol names, so the first name could be written ~c[nodecnt] or ~c[NODECNT], etc.) Those are the name of the accessor functions for the object. The corresponding update functions are named ~c[update-NodeCnt], ~c[update-TipCnt] and ~c[update-IntTipsSeen]. If you do not like the default function names chosen above, there is a feature in the ~ilc[defstobj] event that allows you to specify other names. If you want to see the ACL2 definitions of all the functions defined by this event, look at ~il[stobj-example-1-defuns]. If, after this event, we evaluate the top-level ``global variable'' ~c[counters] in the ACL2 read-eval-print loop we get: ~bv[] ACL2 !>counters ~ev[] Note that the value printed is ``~c[]''. Actually, the value of ~c[counters] in the logic is ~c[(0 0 NIL)]. But ACL2 always prints single-threaded objects in this non-informative way because they are usually so big that to do otherwise would be unpleasant. Had you tried to evaluate the ``global variable'' ~c[counters] before declaring it a single-threaded object, ACL2 would have complained that it does not support global variables. So a lesson here is that once you have declared a new single-threaded object your top-level forms can reference it. In versions of ACL2 prior to Version 2.4 the only variable enjoying this status was ~c[STATE]. single-threaded objects are a straightforward generalization of the long-implemented von Neumann ~ilc[state] feature of ACL2. We can access the fields of ~c[counters] as with: ~bv[] ACL2 !>(NodeCnt counters) 0 ACL2 !>(IntTipsSeen counters) NIL ~ev[] and we can set the fields of ~c[counters] as with: ~bv[] ACL2 !>(update-NodeCnt 3 counters) ACL2 !>(NodeCnt counters) 3 ~ev[] Observe that when we evaluate an expression that returns a counter object, that object becomes the ``current value'' of ~c[counters]. Here is a function that ``converts'' the ~c[counters] object to its ``ordinary'' representation: ~bv[] (defun show-counters (counters) (declare (xargs :stobjs (counters))) (list (NodeCnt counters) (TipCnt counters) (IntTipsSeen counters))) ~ev[] Observe that we ~em[must] declare, at the top of the ~c[defun], that we mean to use the formal parameter ~c[counters] as a single-threaded object! If we did not make this declaration, the body of ~c[show-counters] would be processed as though ~c[counters] were an ordinary object. An error would be caused because the accessors used above cannot be applied to anything but the single-threaded object ~c[counters]. If you want to know why we insist on this declaration, ~pl[declare-stobjs]. When ~c[show-counters] is admitted, the following message is printed: ~bv[] Since SHOW-COUNTERS is non-recursive, its admission is trivial. We observe that the type of SHOW-COUNTERS is described by the theorem (AND (CONSP (SHOW-COUNTERS COUNTERS)) (TRUE-LISTP (SHOW-COUNTERS COUNTERS))). We used primitive type reasoning. (SHOW-COUNTERS COUNTERS) => *. The guard conjecture for SHOW-COUNTERS is trivial to prove. SHOW-COUNTERS is compliant with Common Lisp. ~ev[] The line above containing the ``=>'' is called the ``signature'' of ~c[show-counters]; it conveys the information that the first argument is the single-threaded object ~c[counters] and the only result is an ordinary object. Here is an example of another signature: ~bv[] (PROCESSOR * * COUNTERS) => (MV * COUNTERS) ~ev[] which indicates that the function ~c[PROCESSOR] (which we haven't shown you) takes three arguments, the third of which is the ~c[COUNTERS] stobj, and returns two results, the second of which is the modified ~c[COUNTERS]. Returning to the admission of ~c[show-counters] above, the last sentence printed indicates that the ~ilc[guard] conjectures for the function were proved. When some argument of a function is declared to be a single-threaded object via the ~c[xargs] ~c[:stobj], we automatically add (conjoin) to the guard the condition that the argument satisfy the recognizer for that single-threaded object. In the case of ~c[show-counters] the guard is ~c[(countersp counters)]. Here is an example of ~c[show-counters] being called: ~bv[] ACL2 !>(show-counters counters) (3 0 NIL) ~ev[] This is what we would see had we set the ~c[NodeCnt] field of the initial value of ~c[counters] to ~c[3], as we did earlier in this example. We next wish to define a function to reset the ~c[counters] object. We could define it this way: ~bv[] (defun reset-counters (counters) (declare (xargs :stobjs (counters))) (let ((counters (update-NodeCnt 0 counters))) (let ((counters (update-TipCnt 0 counters))) (update-IntTipsSeen nil counters)))) ~ev[] which ``successively'' sets the ~c[NodeCnt] field to ~c[0], then the ~c[TipCnt] field to ~c[0], and then the ~c[IntTipsSeen] field to ~c[nil] and returns the resulting object. However, the nest of ~c[let] expressions is tedious and we use this definition instead. This definition exploits a macro, here named ``~c[seq]'' (for ``sequentially'') which evaluates each of the forms given, binding their results successively to the stobj name given. ~bv[] (defun reset-counters (counters) (declare (xargs :stobjs (counters))) (seq counters (update-NodeCnt 0 counters) (update-TipCnt 0 counters) (update-IntTipsSeen nil counters))) ~ev[] This definition is syntactically identical to the one above, after macro expansion. Our definition of ~c[seq] is shown below and is not part of native ACL2. ~bv[] (defmacro seq (stobj &rest rst) (cond ((endp rst) stobj) ((endp (cdr rst)) (car rst)) (t `(let ((,stobj ,(car rst))) (seq ,stobj ,@(cdr rst)))))) ~ev[] The signature printed for ~c[reset-counters] is ~bv[] (RESET-COUNTERS COUNTERS) => COUNTERS. ~ev[] Here is an example. ~bv[] ACL2 !>(show-counters counters) (3 0 NIL) ACL2 !>(reset-counters counters) ACL2 !>(show-counters counters) (0 0 NIL) ~ev[] Here finally is a function that uses ~c[counters] as a single-threaded accumulator to collect the desired information about the tree ~c[x]. ~bv[] (defun sweep-tree (x counters) (declare (xargs :stobjs (counters))) (cond ((atom x) (seq counters (update-TipCnt (+ 1 (TipCnt counters)) counters) (if (integerp x) (update-IntTipsSeen (cons x (IntTipsSeen counters)) counters) counters))) (t (seq counters (update-NodeCnt (+ 1 (NodeCnt counters)) counters) (sweep-tree (car x) counters) (sweep-tree (cdr x) counters))))) ~ev[] We can paraphrase this definition as follows. If ~c[x] is an atom, then increment the ~c[TipCnt] field of ~c[counters] and ~em[then], if ~c[x] is an integer, add ~c[x] to the ~c[IntTipsSeen] field, and return ~c[counters]. On the other hand, if ~c[x] is not an atom, then increment the ~c[NodeCnt] field of ~c[counters], and ~em[then] sweep the ~c[car] of ~c[x] and ~em[then] sweep the ~c[cdr] of ~c[x] and return the result. Here is an example of its execution. We have displayed the input tree in full dot notation so that the number of interior nodes is just the number of dots. ~bv[] ACL2 !>(sweep-tree '((((a . 1) . (2 . b)) . 3) . (4 . (5 . d))) counters) ACL2 !>(show-counters counters) (7 8 (5 4 3 2 1)) ACL2 !>(reset-counters counters) ACL2 !>(show-counters counters) (0 0 NIL) ~ev[] The ~c[counters] object has two integer fields and a field whose type is unrestricted. single-threaded objects support other types of fields, such as arrays. We deal with that in the ~il[stobj-example-2]. But we recommend that you first consider the implementation issues for the ~c[counters] example (in ~il[stobj-example-1-implementation]) and then consider the proof issues (in ~il[stobj-example-1-proofs]). To continue the stobj tour, ~pl[stobj-example-2].~/") (deflabel declare-stobjs :doc ":Doc-Section stobj declaring a formal parameter name to be a single-threaded object~/ When a ~ilc[defun] uses one of its formals as a single-threaded object (~il[stobj]), the ~c[defun] ~em[must] include a declaration that the formal is to be so used. An exception is the formal ``~ilc[state],'' which if not declared as explained below, may still be used provided an appropriate global ``declaration'' is issued: ~pl[set-state-ok].~/ If the formal in question is ~c[counters] then an appropriate declaration is ~bv[] (declare (xargs :stobjs counters)) ~ev[] or, more generally, ~bv[] (declare (xargs :stobjs (... counters ...))) ~ev[] where all the single-threaded formals are listed. For such a declaration to be legal it must be the case that all the names have previously been defined as single-threaded objects with ~ilc[defstobj]. When an argument is declared to be single-threaded the guard of the function is augmented by conjoining to it the condition that the argument satisfy the recognizer for the single-threaded object. Furthermore, the syntactic checks done to enforce the legal use of single-threaded objects are also sufficient to allow these guard conjuncts to be automatically proved. The obvious question arises: Why does ACL2 insist that you declare stobj names before using them in ~c[defun]s if you can only declare names that have already been defined with ~c[defstobj]? What would go wrong if a formal were treated as a single-threaded object if and only if it had already been so defined? Suppose that one user, say Jones, creates a book in which ~c[counters] is defined as a single-threaded object. Suppose another user, Smith, creates a book in which ~c[counters] is used as an ordinary formal parameter. Finally, suppose a third user, Brown, wishes to use both books. If Brown includes Jones' book first and then Smith's, then Smith's function treats ~c[counters] as single-threaded. But if Brown includes Smith's book first, the argument is treated as ordinary. ACL2 insists on the declaration to ensure that the definition is processed the same way no matter what the context.~/") (deflabel stobj-example-1-defuns :doc ":Doc-Section stobj the defuns created by the ~c[counters] stobj~/ Consider the event shown in ~il[stobj-example-1]: ~bv[] (defstobj counters (NodeCnt :type integer :initially 0) (TipCnt :type integer :initially 0) (IntTipsSeen :type t :initially nil)) ~ev[] Here is a complete list of the defuns added by the event.~/ The careful reader will note that the ~c[counters] argument below is ~em[not] declared with the ~c[:stobjs] ~c[xarg] even though we insist that the argument be a stobj in calls of these functions. This ``mystery'' is explained below. ~bv[] (defun NodeCntp (x) ;;; Recognizer for 1st field (declare (xargs :guard t :verify-guards t)) (integerp x)) (defun TipCntp (x) ;;; Recognizer for 2nd field (declare (xargs :guard t :verify-guards t)) (integerp x)) (defun IntTipsSeenp (x) ;;; Recognizer for 3rd field (declare (xargs :guard t :verify-guards t) (ignore x)) t) (defun countersp (counters) ;;; Recognizer for object (declare (xargs :guard t :verify-guards t)) (and (true-listp counters) (= (length counters) 3) (NodeCntp (nth 0 counters)) (TipCntp (nth 1 counters)) (IntTipsSeenp (nth 2 counters)) t)) (defun create-counters () ;;; Creator for object (declare (xargs :guard t :verify-guards t)) (list '0 '0 'nil)) (defun NodeCnt (counters) ;;; Accessor for 1st field (declare (xargs :guard (countersp counters) :verify-guards t)) (nth 0 counters)) (defun update-NodeCnt (v counters) ;;; Updater for 1st field (declare (xargs :guard (and (integerp v) (countersp counters)) :verify-guards t)) (update-nth 0 v counters)) (defun TipCnt (counters) ;;; Accessor for 2nd field (declare (xargs :guard (countersp counters) :verify-guards t)) (nth 1 counters)) (defun update-TipCnt (v counters) ;;; Updater for 2nd field (declare (xargs :guard (and (integerp v) (countersp counters)) :verify-guards t)) (update-nth 1 v counters)) (defun IntTipsSeen (counters) ;;; Accessor for 3rd field (declare (xargs :guard (countersp counters) :verify-guards t)) (nth 2 counters)) (defun update-IntTipsSeen (v counters) ;;; Updater for 3rd field (declare (xargs :guard (countersp counters) :verify-guards t)) (update-nth 2 v counters)) ~ev[] Observe that there is a recognizer for each of the three fields and then a recognizer for the ~c[counters] object itself. Then, for each field, there is an accessor and an updater. Observe also that the functions are guarded so that they expect a ~c[countersp] for their ~c[counters] argument and an appropriate value for the new field values. You can see all of the ~c[defuns] added by a ~c[defstobj] event by executing the event and then using the ~c[:pcb!] command on the stobj name. E.g., ~bv[] ACL2 !>:pcb! counters ~ev[] will print the defuns above. We now clear up the ``mystery'' mentioned above. Note, for example in ~c[TipCnt], that the formal ~c[counters] is used. From the discussion in ~il[stobj-example-1] it has been made clear that ~c[TipCnt] can only be called on the ~c[counters] object. And yet, in that same discussion it was said that an argument is so treated only if it it declared among the ~c[:stobjs] in the definition of the function. So why doesn't ~c[TipCnt] include something like ~c[(declare (xargs :stobjs (counters)))]? The explanation of this mystery is as follows. At the time ~c[TipCnt] was defined, during the introduction of the ~c[counters] stobj, the name ``~c[counters]'' was not yet a single-threaded object. The introduction of a new single-threaded object occurs in three steps: (1) The new primitive recognizers, accessors, and updaters are introduced as ``ordinary functions,'' producing their logical axiomatizations. (2) The executable counterparts are defined in raw Lisp to support destructive updating. (3) The new name is declared a single-threaded object to ensure that all future use of these primitives respects the single-threadedness of the object. The functions defined as part of the introduction of a new single-threaded object are the only functions in the system that have undeclared stobj formals other than ~c[state]. You may return to ~il[stobj-example-1] here.~/") (deflabel stobj-example-1-implementation :doc ":Doc-Section stobj the implementation of the ~c[counters] stobj~/ the event ~bv[] (defstobj counters (NodeCnt :type integer :initially 0) (TipCnt :type integer :initially 0) (IntTipsSeen :type t :initially nil)) ~ev[] discussed in ~il[stobj-example-1], creates a Common Lisp object to represent the current value of ~c[counters]. That object is created by evaluating either of the following ``raw'' (non-ACL2) Common Lisp forms: ~bv[] (create-counters) (vector (make-array 1 :element-type 'integer :initial-element '0) (make-array 1 :element-type 'integer :initial-element '0) 'nil) ~ev[] and the value is stored in the Common Lisp global variable named ~c[*the-live-counters*]. ~/ Thus, the ~c[counters] object is an array of length three. The first two elements are arrays of size 1 and are used to hold the ~c[NodeCnt] and ~c[TipCnt] fields. The third element is the ~c[IntTipsSeen] field. The first two fields are represented by arrays so that we can implement the ~c[integer] type specification efficiently. Generally, integers are ``boxed'' in some Common Lisp implementations, for example, GCL. Creating a new integer requires creating a new box to put it in. But in some lisps, including GCL, the integers inside arrays of integers are not boxed. The function ~c[NodeCnt] is defined in raw Lisp as: ~bv[] (defun NodeCnt (counters) (the integer (aref (the (simple-array integer (1)) (svref counters 0)) 0))) ~ev[] Observe that the form ~c[(svref counters 0)] is evaluated to get an array of size 1, which is followed by a call of ~c[aref] to access the 0th element of that array. The function ~c[update-NodeCnt] is defined in raw Lisp as: ~bv[] (defun update-NodeCnt (v counters) (declare (type integer v)) (progn (setf (aref (the (simple-array integer (1)) (svref counters 0)) 0) (the integer v)) counters)) ~ev[] Note that when this function is called, it does not create a new vector of length three, but ``smashes'' the existing one. One way to see all the raw Lisp functions defined by a given ~c[defstobj] is to evaluate the ~c[defstobj] event and then evaluate, in the ACL2 loop, the expression ~c[(nth 4 (global-val 'cltl-command (w state)))]. Those functions that contain ~c[(DECLARE (STOBJ-INLINE-FN T))] will generate ~ilc[defabbrev] forms because the ~c[:inline] keyword of ~ilc[defstobj] was supplied the value ~c[t]. The rest will generate ~ilc[defun]s. We now recommend that you look at ~il[stobj-example-1-proofs].~/") (deflabel stobj-example-1-proofs :doc ":Doc-Section stobj some proofs involving the ~c[counters] stobj~/ Consider again the event ~bv[] (defstobj counters (NodeCnt :type integer :initially 0) (TipCnt :type integer :initially 0) (IntTipsSeen :type t :initially nil)) ~ev[] discussed in ~il[stobj-example-1], followed by the definition ~bv[] (defun reset-counters (counters) (declare (xargs :stobjs (counters))) (seq counters (update-NodeCnt 0 counters) (update-TipCnt 0 counters) (update-IntTipsSeen nil counters))) ~ev[] which, because of the ~c[seq] macro in ~il[stobj-example-1], is just syntactic sugar for ~bv[] (defun reset-counters (counters) (declare (xargs :stobjs (counters))) (let ((counters (update-NodeCnt 0 counters))) (let ((counters (update-TipCnt 0 counters))) (update-IntTipsSeen nil counters)))). ~ev[] Here is a simple theorem about ~c[reset-counters]. ~bv[] (defthm reset-counters-is-constant (implies (countersp x) (equal (reset-counters x) '(0 0 nil)))) ~ev[] ~/ Before we talk about how to prove this theorem, note that the theorem is unusual in two respects. First, it calls ~c[reset-counters] on an argument other than the variable ~c[counters]! That is allowed in theorems; logically speaking, the stobj functions are indistinguishable from ordinary functions. Their use is syntactically restricted only in ~c[defun]s, which might be compiled and run in raw Lisp. Those restrictions allow us to implement stobj modification destructively. But logically speaking, ~c[reset-counters] and other stobj ``modifying'' functions just create new objects, constructively. Second, the theorem above explicitly provides the hypothesis that ~c[reset-counters] is being applied to an object satisfying ~c[countersp]. Such a hypothesis is not always required: ~c[reset-counters] is total and will do something no matter what ~c[x] is. But in this particular case, the result is not ~c['(0 0 nil)] unless ~c[x] is, at least, a true-list of length three. To make a long story short, to prove theorems about stobj functions you behave in exactly the way you would to prove the same theorems about the same functions defined without the stobj features. How can we prove the above theorem? Unfolding the definition of ~c[reset-counters] shows that ~c[(reset-counters x)] is equal to ~bv[] (update-IntTipsSeen nil (update-TipCnt 0 (update-NodeCnt 0 x))) ~ev[] which in turn is ~bv[] (update-nth 2 nil (update-nth 1 0 (update-nth 0 0 x))). ~ev[] Opening up the definition of ~c[update-nth] reduces this to ~bv[] (list* 0 0 nil (cdddr x)). ~ev[] This is clearly equal to ~c['(0 0 nil)], provided we know that ~c[(cdddr x)] is ~c[nil]. Unfortunately, that last fact requires a lemma. The most specific lemma we could provide is ~bv[] (defthm special-lemma-for-counters (implies (countersp x) (equal (cdddr x) nil))) ~ev[] but if you try to prove that lemma you will find that it requires some reasoning about ~c[len] and ~c[true-listp]. Furthermore, the special lemma above is of interest only for ~c[counters]. The following lemma about ~c[len] is the one we prefer. ~bv[] (defthm equal-len-n (implies (syntaxp (quotep n)) (equal (equal (len x) n) (if (integerp n) (if (< n 0) nil (if (equal n 0) (atom x) (and (consp x) (equal (len (cdr x)) (- n 1))))) nil)))) ~ev[] This lemma will simplify any equality in which a ~c[len] expression is equated to any explicitly given constant ~em[n], e.g., ~c[3], reducing the equation to a conjunction of ~c[consp] terms about the first ~em[n] ~c[cdr]s. If the above lemma is available then ACL2 immediately proves ~bv[] (defthm reset-counters-is-constant (implies (countersp x) (equal (reset-counters x) '(0 0 nil)))) ~ev[] The point is presumably well made: proving theorems about single-threaded object accessors and updaters is no different than proving theorems about other recursively defined functions on lists. As we have seen, operations on ~il[stobj]s turn into definitions involving ~ilc[nth] and ~ilc[update-nth] in the logic. Here are two lemmas that are useful for simplifying terms involving ~c[nth] and ~c[update-nth], which are therefore useful in reasoning about single-threaded objects. ~bv[] (defthm update-nth-update-nth-same (implies (equal (nfix i1) (nfix i2)) (equal (update-nth i1 v1 (update-nth i2 v2 l)) (update-nth i1 v1 l)))) (defthm update-nth-update-nth-diff (implies (not (equal (nfix i1) (nfix i2))) (equal (update-nth i1 v1 (update-nth i2 v2 l)) (update-nth i2 v2 (update-nth i1 v1 l)))) :rule-classes ((:rewrite :loop-stopper ((i1 i2))))) ~ev[] These lemmas are due to Matt Wilding. ~l[nu-rewriter] for a discussion of the efficient simplification of terms of the form ~c[(nth n (update-nth key val lst))], which can be critical in settings involving sequential bindings that commonly arise in operations involving stobjs. We now recommend that you ~pl[stobj-example-2].~/") (deflabel stobj-example-2 :doc ":Doc-Section stobj an example of the use of arrays in single-threaded objects~/ The following event ~bv[] (defstobj ms (pcn :type integer :initially 0) (mem :type (array integer (100000)) :initially -1) (code :type t :initially nil)) ~ev[] introduces a single-threaded object named ~c[ms] (which stands for ``machine state''). The object has three fields, a ~c[pcn] or program counter, a ~c[mem] or memory, and a ~c[code] field. The ~c[mem] field is occupied by an object initially of type ~c[(array integer (100000))]. Logically speaking, this is a list of length ~c[100000], each element of which is an integer. But in the underlying implementation of the ~c[ms] object, this field is occupied by a raw Lisp array, initially of size 100000.~/ You might expect the above ~c[defstobj] to define the accessor function ~c[mem] and the updater ~c[update-mem]. ~em[That does not happen!]. The above event defines the accessor function ~c[memi] and the updater ~c[update-memi]. These functions do not access/update the ~c[mem] field of the ~c[ms] object; they access/update the individual elements of the array in that field. In particular, the logical definitions of the two functions are: ~bv[] (defun memi (i ms) (declare (xargs :guard (and (msp ms) (integerp i) (<= 0 i) (< i (mem-length ms))))) (nth i (nth 1 ms))) (defun update-memi (i v ms) (declare (xargs :guard (and (msp ms) (integerp i) (<= 0 i) (< i (mem-length ms)) (integerp v)))) (update-nth-array 1 i v ms)) ~ev[] For example, to access the 511th (0-based) memory location of the current ~c[ms] you could evaluate: ~bv[] ACL2 !>(memi 511 ms) -1 ~ev[] The answer is ~c[-1] initially, because that is the above-specified initial value of the elements of the ~c[mem] array. To set that element you could do ~bv[] ACL2 !>(update-memi 511 777 ms) ACL2 !>(memi 511 ms) 777 ~ev[] The raw Lisp implementing these two functions is shown below. ~bv[] (defun memi (i ms) (declare (type (and fixnum (integer 0 *)) i)) (the integer (aref (the (simple-array integer (*)) (svref ms 1)) (the (and fixnum (integer 0 *)) i)))) (defun update-memi (i v ms) (declare (type (and fixnum (integer 0 *)) i) (type integer v)) (progn (setf (aref (the (simple-array integer (*)) (svref ms 1)) (the (and fixnum (integer 0 *)) i)) (the integer v)) ms)) ~ev[] If you want to see the raw Lisp supporting a ~c[defstobj], execute the ~c[defstobj] and then evaluate the ACL2 form ~c[(nth 4 (global-val 'cltl-command (w state)))]. To continue the stobj tour, ~pl[stobj-example-3].~/") (deflabel stobj-example-3 :doc ":Doc-Section stobj another example of a single-threaded object~/ The event ~bv[] (defstobj $s (x :type integer :initially 0) (a :type (array (integer 0 9) (3)) :initially 9 :resizable t)) ~ev[] introduces a stobj named ~c[$S]. The stobj has two fields, ~c[X] and ~c[A]. The ~c[A] field is an array. The ~c[X] field contains an integer and is initially 0. The ~c[A] field contains a list of integers, each between 0 and 9, inclusively. (Under the hood, this ``list'' is actually implemented as an array.) Initially, the ~c[A] field has three elements, each of which is 9.~/ This event introduces the following sequence of function definitions: ~bv[] (DEFUN XP (X) ...) ; recognizer for X field (DEFUN AP (X) ...) ; recognizer of A field (DEFUN $SP ($S) ...) ; top-level recognizer for stobj $S (DEFUN CREATE-$S NIL ...) ; creator for stobj $S (DEFUN X ($S) ...) ; accessor for X field (DEFUN UPDATE-X (V $S) ...) ; updater for X field (DEFUN A-LENGTH ($S) ...) ; length of A field (DEFUN RESIZE-A (K $S) ...) ; resizer for A field (DEFUN AI (I $S) ...) ; accessor for A field at index I (DEFUN UPDATE-AI (I V $S) ...) ; updater for A field at index I ~ev[] Here is the definition of ~c[$SP]: ~bv[] (DEFUN $SP ($S) (DECLARE (XARGS :GUARD T :VERIFY-GUARDS T)) (AND (TRUE-LISTP $S) (= (LENGTH $S) 2) (XP (NTH 0 $S)) (AP (NTH 1 $S)) T)) ~ev[] This reveals that in order to satisfy ~c[$SP] an object must be a true list of length 2 whose first element satisfies ~c[XP] and whose second satisfies ~c[AP]. By printing the definition of ~c[AP] one learns that it requires its argument to be a true list, each element of which is an integer between 0 and 9. The initial value of stobj ~c[$S] is given by zero-ary ``creator'' function ~c[CREATE-$S]. Creator functions may only be used in limited contexts. ~l[with-local-stobj]. Here is the definition of ~c[UPDATE-AI], the updater for the ~c[A] field at index ~c[I]: ~bv[] (DEFUN UPDATE-AI (I V $S) (DECLARE (XARGS :GUARD (AND ($SP $S) (INTEGERP I) (<= 0 I) (< I (A-LENGTH $S)) (AND (INTEGERP V) (<= 0 V) (<= V 9))) :VERIFY-GUARDS T)) (UPDATE-NTH-ARRAY 1 I V $S)) ~ev[] By definition ~c[(UPDATE-NTH-ARRAY 1 I V $S)] is ~c[(UPDATE-NTH 1 (UPDATE-NTH I V (NTH 1 $S)) $S)]. This may be a little surprising but should be perfectly clear. First, ignore the guard, since it is irrelevant in the logic. Reading from the inside out, ~c[(UPDATE-AI I V $S)] extracts ~c[(NTH 1 $S)], which is array ~c[a] of ~c[$S]. (Recall that ~ilc[NTH] is 0-based.) The next higher expression in the definition above, ~c[(UPDATE-NTH I V a)], ``modifies'' ~c[a] by setting its ~c[I]th element to ~c[V]. Call this ~c[a']. The next higher expression, ~c[(UPDATE-NTH 1 a' $S)], ``modifies'' ~c[$S] by setting its 1st component to ~c[a']. Call this result ~c[$s']. Then ~c[$s'] is the result returned by ~c[UPDATE-AI]. So the first useful observation is that from the perspective of the logic, the type ``restrictions'' on stobjs are irrelevant. They are ``enforced'' by ACL2's guard mechanism, not by the definitions of the updater functions. As one might also imagine, the accessor functions do not really ``care,'' logically, whether they are applied to well-formed stobjs or not. For example, ~c[(AI I $S)] is defined to be ~c[(NTH I (NTH 1 $S))]. Thus, you will not be able to prove that (AI 2 $S) is an integer. That is, ~bv[] (integerp (AI 2 $S)) ~ev[] is not a theorem, because ~c[$S] may not be well-formed. Now ~c[(integerp (AI 2 $S))] will always evaluate to ~c[T] in the top-level ACL2 command loop, because we insist that the current value of the stobj ~c[$S] always satisfies ~c[$SP] by enforcing the guards on the updaters, independent of whether guard checking is on or off; ~pl[set-guard-checking]. But in a theorem ~c[$S] is just another variable, implicitly universally quantified. So ~c[(integerp (AI 2 $S))] is not a theorem because it is not true when the variable ~c[$S] is instantiated with, say, ~bv[] '(1 (0 1 TWO)) ~ev[] because, logically speaking, ~c[(AI 2 '(1 (0 1 TWO)))] evaluates to the symbol ~c[TWO]. That is, ~bv[] (equal (AI 2 '(1 (0 1 TWO))) 'TWO) ~ev[] is true. However, ~bv[] (implies (and ($SP $S) (< 2 (A-LENGTH $S))) (integerp (AI 2 $S))) ~ev[] is a theorem. To prove it, you will have to prove a lemma about ~c[AP]. The following will do: ~bv[] (defthm ap-nth (implies (and (AP x) (integerp i) (<= 0 i) (< i (len x))) (integerp (nth i x)))). ~ev[] Similarly, ~bv[] (implies (and (integerp i) (<= 0 i) (< i (A-LENGTH $S)) (integerp v) (<= 0 v) (<= v 9)) ($SP (UPDATE-AI i v $S))) ~ev[] is not a theorem until you add the additional hypothesis ~c[($SP $S)]. To prove the resulting theorem, you will need a lemma such as the following. ~bv[] (defthm ap-update-nth (implies (and (AP a) (integerp v) (<= 0 v) (<= v 9) (integerp i) (<= 0 i) (< i (len a))) (AP (update-nth i v a)))) ~ev[] The moral here is that from the logical perspective, you must provide the hypotheses that, as a programmer, you think are implicit on the structure of your stobjs, and you must prove their invariance. This is a good area for further support, perhaps in the form of a library of macros. ~em[Resizing Array Fields] Recall the specification of the array field, ~c[A] for the stobj ~c[$S] introduced above: ~bv[] (a :type (array (integer 0 9) (3)) :initially 9 :resizable t) ~ev[] Logically, this field is a list, initially of length 3. Under the hood, this field is implemented using a Common Lisp array with 3 elements. In some applications, one may wish to lengthen an array field, or even (to reclaim space) to shrink an array field. The ~ilc[defstobj] event provides functions to access the current length of an array field and to change the array field, with default names obtained by suffixing the field name with ``~c[LENGTH-]'' or prefixing it with ``~c[RESIZE-],'' respectively. The following log shows the uses of these fields in the above example. ~bv[] ACL2 !>(A-LENGTH $S) 3 ACL2 !>(RESIZE-A 10 $S) ; change length of A to 10 <$s> ACL2 !>(A-LENGTH $S) 10 ACL2 !>(AI 7 $S) ; new elements get value from :initially 9 ACL2 !>(RESIZE-A 2 $S) ; truncate A down to first 2 elements <$s> ACL2 !>(A-LENGTH $S) 2 ACL2 !>(AI 7 $S) ; error: access past array bound ACL2 Error in TOP-LEVEL: The guard for the function symbol AI, which is (AND ($SP $S) (INTEGERP I) (<= 0 I) (< I (A-LENGTH $S))), is violated by the arguments in the call (AI 7 $S). ACL2 !> ~ev[] Here are the definitions of the relevant functions for the above example; also ~pl[resize-list]. ~bv[] (DEFUN A-LENGTH ($S) (DECLARE (XARGS :GUARD ($SP $S) :VERIFY-GUARDS T)) (LEN (NTH 1 $S))) (DEFUN RESIZE-A (K $S) (DECLARE (XARGS :GUARD ($SP $S) :VERIFY-GUARDS T)) (UPDATE-NTH 1 (RESIZE-LIST (NTH 1 $S) K 9) $S)) ~ev[] It is important to note that the implementation of array resizing in ACL2 involves copying the entire array into a newly allocated space and thus can be quite costly if performed often. This approach was chosen in order to make array access and update as efficient as possible, with the suspicion that for most applications, array access and update are considerably more frequent than resizing (especially if the programmer is aware of the relative costs beforehand). It should also be noted that computations of lengths of stobj array fields should be fast (constant-time) in all or most Common Lisp implementations. Finally, if ~c[:resizable t] is not supplied as shown above, then an attempt to resize the array will result in an error. If you do not intend to resize the array, it is better to omit the ~c[:resizable] option (or to supply ~c[:resizable nil]), since then the length function will be defined to return a constant, namely the initial length, which can simplify guard proofs (compare with the definition of ~c[A-LENGTH] above). This completes the tour through the documentation of ~il[stobj]s. However, you may now wish to read the documentation for the event that introduces a new single-threaded object; ~pl[defstobj].~/") (defdoc resize-list ":Doc-Section Stobj list resizer in support of stobjs~/ ~c[(Resize-list lst n default-value)] takes a list, ~c[lst], and a desired length, ~c[n], for the result list, as well as a ~c[default-value] to use for the extra elements if ~c[n] is greater than the length of ~c[lst].~/ ~c[Resize-list] has a guard of ~c[t]. This function is called in the body of function, ~c[resize-] where ~c[] is an array field of a ~il[stobj]. ~l[stobj] and ~pl[defstobj].~/") #-acl2-loop-only (defun-one-output mv-let-for-with-local-stobj (mv-let-form st creator flet-fns w) ; If w is not nil, then it is the current ACL2 world and we are to oneify the ; appropriate subforms. ; It was tempting to have an acl2-loop-only version of the body below as well, ; which would omit the binding of the live var. But if someone were to ; verify-termination of this function, we could presumably prove nil using the ; discrepancy between the two versions. So we take the attitude that ; with-local-stobj is a special form, like let, that is not defined. ; In the case that st is STATE, this form does not take responsibility for ; restoring state, for example by restoring values of state global variables ; and by closing channels that may have been created during evaluation of the ; producer form. A with-local-state form thus needs to take responsibility for ; restoring state; see for example the definition of channel-to-string. (let ((producer (caddr mv-let-form)) (rest (cdddr mv-let-form))) `(mv-let ,(cadr mv-let-form) (let* (,@(and (not (eq st 'state)) `((,st (,creator)))) ; We bind the live var so that user-stobj-alist-safe can catch misguided ; attempts to use functions like trans-eval in inappropriate contexts. ,@(cond ((eq st 'state) '((*file-clock* *file-clock*) (*t-stack* *t-stack*) (*t-stack-length* *t-stack-length*) (*32-bit-integer-stack* *32-bit-integer-stack*) (*32-bit-integer-stack-length* *32-bit-integer-stack-length*))) (t `((,(the-live-var st) ,st))))) ,(let ((p (if w (oneify producer flet-fns w) producer))) (if (eq st 'state) ; We should lock this computation when #+acl2-par, even though special ; variables that are let-bound (including those bound above) are thread-local. `(if (f-get-global 'parallel-execution-enabled *the-live-state*) ; Parallelism wart: this isn't really the right check for ACL2(p), because ; we've effectively disallowed the use of with-local-state, even when we're not ; executing in parallel! This bothers Rager, because he wants to use ; with-local-state in code that isn't executing in parallel (in his ; dissertation's supporting evidence, for reading in files that contain ; performance results). Instead, we should be calling ; warn-about-parallelism-hazard (similar to what we do in the definition of ; state-global-let*). (er hard! 'with-local-state "The use of with-local-state ~ (or, with-local-stobj where STATE is the ~ stobj) is disallowed with parallelism enabled.") ,p) p))) (declare (ignore ,st)) ,@(if w (if (cdr rest) ; rest is ((declare (ignore ...)) body) (list (car rest) (oneify (cadr rest) flet-fns w)) (list (oneify (car rest) flet-fns w))) rest)))) #-acl2-loop-only ; see the comment in mv-let-for-with-local-stobj (defmacro with-local-stobj (&rest args) ; Below are some tests of local stobjs. ; (defstobj foo bar xxx) ; ; (thm (equal (create-foo) '(nil nil))) ; succeeds ; ; (defun up1 (x foo) ; (declare (xargs :stobjs foo)) ; (update-bar x foo)) ; ; (bar foo) ; nil ; ; (up1 3 foo) ; ; ; (bar foo) ; 3 ; ; (defun test (x) ; should fail; must use with-local-stobj explicitly ; (mv-let (a b foo) ; (let ((foo (create-foo))) ; (let ((foo (up1 (1+ x) foo))) ; (mv (bar foo) (xxx foo) foo))) ; (declare (ignore foo)) ; (mv a b x))) ; ; (defun test (x) ; (declare (xargs :guard (acl2-numberp x) :verify-guards nil)) ; (with-local-stobj ; foo ; (mv-let (a b foo) ; (let ((foo (up1 (1+ x) foo))) ; (mv (bar foo) (xxx foo) foo)) ; (mv a b x)))) ; ; (test 17) ; (18 NIL 17) ; ; (bar foo) ; 3 ; ; (thm (equal (test x) (list (1+ x) nil x))) ; succeeds ; ; (thm (equal (test x) (list (1+ x) nil x)) ; succeeds ; :hints (("Goal" ; :in-theory ; (enable ; (:executable-counterpart create-foo))))) ; ; (thm (equal (test x) (list (1+ x) nil x)) ; fails, creating (NOT (NTH 1 (HIDE (CREATE-FOO)))) ; :hints (("Goal" ; :in-theory ; (set-difference-theories ; (enable ; (:executable-counterpart create-foo)) ; '(create-foo))))) ; ; (verify-guards test) ; ; (test 17) ; (18 nil 17) ; ; (bar foo) ; 3 ; ; (defun test2 (x) ; (with-local-stobj ; foo ; (mv-let (a foo) ; (let ((foo (up1 (1+ x) foo))) (mv (bar foo) foo)) ; (mv a x)))) ; ; (test2 12) ; (13 12) ; ; (bar foo) ; 3 ; ; (thm (equal (test x) (mv-let (x y) (test2 x) (mv x nil y)))) ; succeeds ; ; (create-foo) ; should get graceful error ; ; (defun test3 (x) ; Should be OK. ; (with-local-stobj ; foo ; (mv-let (a foo) ; (let ((foo (up1 (1+ x) foo))) (mv (bar foo) foo)) ; a))) ; ; (test3 11) ; 12 ; ; (bar foo) ; 3 ; ; (defun test4 (x foo) ; Should be OK. ; (declare (xargs :stobjs foo ; :verify-guards nil)) ; (let* ((x+1 ; (with-local-stobj ; foo ; (mv-let (a foo) ; (let ((foo (up1 (1+ x) foo))) (mv (bar foo) foo)) ; a))) ; (foo (up1 92 foo))) ; (mv x+1 foo))) ; ; (test4 19 foo) ; (20 ) ; ; (bar foo) ; 92 ; ; (defun test5 (x foo) ; Should be OK. ; (declare (xargs :stobjs foo ; :verify-guards nil)) ; (let* ((foo (up1 23 foo)) ; (x+1 ; (with-local-stobj ; foo ; (mv-let (a foo) ; (let ((foo (up1 (1+ x) foo))) (mv (bar foo) foo)) ; a)))) ; (mv x+1 foo))) ; ; (test5 35 foo) ; (36 ) ; ; (bar foo) ; 23 ; ; (with-local-stobj ; should get macroexpansion error or the equivalent ; foo ; (mv foo 3)) ; ; (defun trans-eval-test (x foo state) ; this part is ok ; (declare (xargs :stobjs (foo state) ; :mode :program)) ; (mv-let (erp val state) ; (trans-eval '(update-bar (cons 3 (bar foo)) foo) 'top state t) ; (declare (ignore erp val)) ; (mv x foo state))) ; ; (with-local-stobj ; should fail; cannot use with-local-stobj in top level loop ; foo ; (mv-let (x foo state) ; (trans-eval-test 3 foo state t) ; (mv x state))) ; ; (pprogn ; (with-local-stobj ; should fail with create-foo error ; foo ; (mv-let (x foo state) ; (trans-eval-test 3 foo state t) ; (declare (ignore x)) ; state)) ; (mv 3 state)) ; ; (defun test6 (a state) ; (declare (xargs :mode :program :stobjs state)) ; (with-local-stobj ; foo ; (mv-let (x foo state) ; (trans-eval-test a foo state t) ; (mv x state)))) ; ; (test6 100 state) ; should get trans-eval error: user-stobj-alist mismatch ; ; (bar foo) ; 23, still -- trans-eval did not affect global state ; Below are some more tests, contributed by Rob Sumners. ; (defstobj foo foo-fld) ; (defstobj bar bar-fld) ; ; (defun test-wls1 (x) ; (with-local-stobj ; foo ; (mv-let (result foo) ; (let ((foo (update-foo-fld 2 foo))) ; (mv (with-local-stobj ; bar ; (mv-let (result bar) ; (let ((bar (update-bar-fld 3 bar))) ; (mv x bar)) ; result)) ; foo)) ; result))) ; ; (test-wls1 129) ; 129 ; ; :comp t ; ; (test-wls1 '(adjka 202)) ; '(ADJKA 202) ; ; (thm (equal (test-wls1 x) x)) ; ; (defun test-wls2 (x) ; (with-local-stobj ; foo ; (mv-let (result foo) ; (let ((foo (update-foo-fld 2 foo))) ; (mv (with-local-stobj ; foo ; (mv-let (result foo) ; (let ((foo (update-foo-fld 3 foo))) ; (mv x foo)) ; result)) ; foo)) ; result))) ; ; (test-wls2 129) ; 129 ; ; :comp t ; ; (test-wls2 '(adjka 202)) ; (ADJKA 202) ; ; (thm (equal (test-wls2 x) x)) ; ; (defun test-wls3 (x) ; (if (atom x) x ; (with-local-stobj ; foo ; (mv-let (result foo) ; (mv (cons (car x) ; (test-wls3 (cdr x))) ; foo) ; (let ((x result)) ; (if (atom x) x (cons (car x) (cdr x)))))))) ; ; (test-wls3 129) ; 129 ; ; :comp t ; ; (test-wls3 '(adjka 202)) ; (ADJKA 202) ; ; (thm (equal (test-wls3 x) x)) (mv-let (erp st mv-let-form creator) (parse-with-local-stobj args) (if (or erp (not (and (true-listp mv-let-form) (<= 3 (length mv-let-form))))) (er hard 'with-local-stobj "Macroexpansion of a with-local-stobj call caused an error. ~ See :DOC with-local-stobj.") (mv-let-for-with-local-stobj mv-let-form st creator nil nil)))) (deflabel with-local-stobj :doc ":Doc-Section Stobj locally bind a single-threaded object~/ ~l[stobj] for an introduction to single-threaded objects. ~bv[] Example Form: (with-local-stobj st (mv-let (result st) (compute-with-st x st) result)) ~ev[] ~c[With-local-stobj] can be thought of as a macro, where the example form above expands as follows. ~bv[] (mv-let (result st) (let ((st (create-st))) (compute-with-st x st)) (declare (ignore st)) result) ~ev[] However, ACL2 expects you to use ~c[with-local-stobj], not its expansion. More precisely, stobj creator functions are not allowed except (implicitly) via ~c[with-local-stobj] and in logic-only situations (like theorems and hints). Moreover, neither ~c[with-local-stobj] nor its expansions are legal when typed directly at the top-level loop. ~l[top-level] for a way to use ~c[with-local-stobj] in the top-level loop.~/ ~bv[] General Forms: (with-local-stobj stobj-name mv-let-form) (with-local-stobj stobj-name mv-let-form creator-name) ~ev[] where ~c[stobj-name] is the name of a ~il[stobj], ~c[mv-let-form] is a call of ~ilc[mv-let], and if ~c[creator-name] is supplied then it should be the name of the creator function for ~c[stobj-name]; ~pl[defstobj]. For the example form above, its expansion would use ~c[creator-name], if supplied, in place of ~c[create-st]. Note that ~c[stobj-name] must not be ~ilc[state] (the ACL2 state), except in special situations probably of interest only to system developers; ~pl[with-local-state]. ~c[With-local-stobj] can be useful when a stobj is used to memoize intermediate results during a computation, yet it is desired not to make the ~c[stobj] a formal parameter for the function and its callers. ACL2 can reason about these ``local stobjs,'' and in particular about stobj creator functions. For technical reasons, ACL2 will not allow you to enable the ~c[:EXECUTABLE-COUNTERPART] ~il[rune] of a stobj creator function. Finally, here is a small example concocted in order to illustrate that ~c[with-local-stobj] calls can be nested. ~bv[] (defstobj st fld1) (defun foo () (with-local-stobj st ; Let us call this the ``outer binding of st''. (mv-let (val10 val20 st) (let ((st (update-fld1 10 st))) ;; At this point the outer binding of st has fld1 = 10. (let ((result (with-local-stobj st ; Let us call this the ``inner binding of st''. (mv-let (val st) (let ((st (update-fld1 20 st))) ;; Now fld1 = 20 for the inner binding of st. (mv (fld1 st) st)) val)))) ;; So result has been bound to 20 above, but here we are once again ;; looking at the outer binding of st, where fld1 is still 10. (mv (fld1 st) result st))) (mv val10 val20)))) (thm (equal (foo) (mv 10 20))) ; succeeds ~ev[]~/") (defun create-state () (declare (xargs :guard t)) (coerce-object-to-state *default-state*)) (defmacro with-local-state (mv-let-form) ":Doc-Section Stobj locally bind state~/ This is an advanced topic, probably of interest only to system developers. Consider the following example form: ~bv[] (with-local-state (mv-let (result state) (compute-with-state x state) result)) ~ev[] This is equivalent to the following form. ~bv[] (with-local-stobj state (mv-let (result state) (compute-with-state x state) result)) ~ev[] By default, this form is illegal, because ACL2 does not have a way to unwind all changes to the ACL2 ~il[state]; we say more on this issue below. There may however be situations where you are willing to manage or overlook this issue. In that case you may execute the following form to enable the use of ~c[with-local-state], by enabling the use of ~ilc[with-local-stobj] on ~c[state]; but note that it requires an active trust tag (~pl[defttag]). ~bv[] (remove-untouchable create-state t) ~ev[] Please be aware that no local ~il[state] is actually created, however! In particular, users of ~c[with-local-state] need either to ensure that channels are closed and state global variables are returned to their original values, or else be willing to live with changes made to state that are not justified by the code that has been evaluated. You are welcome to look in the the ACL2 source code at the definition of macro ~c[channel-to-string], which employs ~c[with-local-state] to create a local ~il[state] for the purpose of creating a string.~/ Here is an example use of ~c[with-local-state]. Notice the use of ~ilc[defttag] ~-[] and indeed, please understand that we are just hacking here, and in general it takes significant effort to be sure that one is using ~c[with-local-state] correctly! ~bv[] (defttag t) (remove-untouchable create-state t) (set-state-ok t) (defun foo (state) (declare (xargs :mode :program)) (mv-let (channel state) (open-input-channel \"my-file\" :object state) (mv-let (eofp obj state) (read-object channel state) (declare (ignore eofp)) (let ((state (close-input-channel channel state))) (mv obj state))))) (defun bar () (declare (xargs :mode :program)) (with-local-state (mv-let (result state) (foo state) result))) ; Multiple-value return version: (defun foo2 (state) (declare (xargs :mode :program)) (mv-let (channel state) (open-input-channel \"my-file\" :object state) (mv-let (eofp obj state) (read-object channel state) (let ((state (close-input-channel channel state))) (mv eofp obj state))))) (defun bar2 () (declare (xargs :mode :program)) (with-local-state (mv-let (eofp result state) (foo2 state) (mv eofp result)))) ~ev[]~/" `(with-local-stobj state ,mv-let-form)) ; Essay on Nested Stobjs ; After Version_6.1 we introduced a new capability: allowing fields of stobjs ; to themselves be stobjs or arrays of stobjs. Initially we resisted this idea ; because of an aliasing problem, which we review now, as it is fundamental to ; understanding our implementation. ; Consider the following events. ; (defstobj st fld) ; (defstobj st2 (fld2 :type st)) ; Now suppose we could evaluate the following code, to be run immediately after ; admitting the two defstobj events above. ; (let* ((st (fld2 st2)) ; (st (update-fld 3 st))) ; (mv st st2)) ; A reasonable raw-Lisp implementation of nested stobjs, using destructive ; updates, could be expected to have the property that for the returned st and ; st2, st = (fld2 st2) and thus (fld (fld2 st2)) = (fld st) = 3. However, ; under an applicative semantics, st2 has not changed and thus, logically, it ; follows that (fld (fld2 st2)) has its original value of nil, not 3. ; In summary, a change to st can cause a logically-inexplicable change to st2. ; But this problem can also happen in reverse: a change to st2 can cause a ; logically-inexplicable change to st. Consider evaluation of the following ; code, to be run immediately after admitting the two defstobj events above. ; (let ((st2 (let* ((st (fld2 st2)) ; (st (update-fld 3 st))) ; (update-fld2 st st2)))) ; (mv st st2)) ; With destructive updates in raw Lisp, we expect that st = (fld2 st2) for the ; returned st and st2, and thus (fld st) = (fld (fld2 st2)) = 3. But ; logically, the returned st is as initially created, and hence (fld st) = ; nil. ; One can imagine other kinds of aliasing problems; imagine putting a single ; stobj into two different slots of a parent stobj. ; Therefore, we carefully control access to stobj fields of stobjs by ; introducing a new construct, stobj-let. Consider for example the following ; events. ; (defstobj st1 ...) ; (defstobj st2 ...) ; (defstobj st3 ...) ; (defstobj st+ ; (fld1 :type st1) ; (fld2 :type st2) ; (fld3 :type (array st3 (8)))) ; If producer and consumer are functions, then we can write the following ; form. Note that stobj-let takes four "arguments": bindings, producer ; variables, a producer form, and a consumer form. ; (stobj-let ; ((st1 (fld1 st+)) ; (st2 (fld2 st+)) ; (st3 (fld3i 4 st+))) ; (x st1 y st3 ...) ; producer variables ; (producer st1 st2 st3 ...) ; (consumer st+ x y ...)) ; Updater names need to be supplied if not the default. Thus, the form above ; is equivalent to the following. ; (stobj-let ; ((st1 (fld1 st+) update-fld1) ; (st2 (fld2 st+) update-fld2) ; (st3 (fld3i 4 st+) update-fld3i)) ; (x st1 y st3 ...) ; producer variables ; (producer st1 st2 st3 ...) ; (consumer st+ x y ...)) ; The form above expands as follows in the logic (or at least, essentially so). ; The point is that we avoid the aliasing problem: there is no direct access to ; the parent stobj when running the producer, which is updated to stay in sync ; with updates to the child stobjs; and there is no direct access to the child ; stobjs when running the consumer. Note that since st2 is not among the ; producer variables, fld2 is not updated. ; (let ((st1 (fld1 st+)) ; (st2 (fld2 st+)) ; (st3 (fld3i 4 st+))) ; (declare (ignorable st1 st2 st3)) ; since user has no way to do this ; (mv-let (x st1 y st3 ...) ; producer variables ; (check-vars-not-free (st+) ; (producer st1 st2 st3 ...)) ; (let* ((st+ (update-fld1 st1 st+)) ; (st+ (update-fld3i 4 st3 st+))) ; (check-vars-not-free (st1 st2 st3) ; (consumer st+ x y ...))))) ; We consider next whether the use of check-vars-not-free truly prevents access ; to a stobj named in its variable list (first argument). For example, if ; is the stobj-let form displayed above, might we let-bind foo to st+ ; above and then reference foo in the producer? Such aliasing is ; prevented (or had better be!) by our implementation; in general, we cannot ; have two variables bound to the same stobj, or there would be logical ; problems: changes to a stobj not explained logically (because they result ; from destructive changes to a "copy" of the stobj that is really EQ to the ; original stobj). On first glance, one might wonder if support for congruent ; stobjs could present a problem, for example as follows. Suppose that ; function h is the identity function that maps stobj st1 to itself, suppose ; that st2 is a stobj congruent to st1, and consider the form (let ((st2 (h ; st1))) ). If such a form could be legal when translating for execution ; (which is the only way live stobjs can be introduced), then the aliasing ; problem discussed above could arise, because st2 is bound to st1 and ; could mention both st1 and st2. But our handling of congruent stobjs allows ; h to map st1 to st2 and also to map st2 to st2, but not to map st1 to st2. ; There are comments about aliasing in the definitions of translate11-let and ; translate11-mv-let. ; In the bindings, an index in an array access must be a symbol or a (possibly ; quoted) natural number -- after all, there would be a waste of computation ; otherwise, since we do updates at the end. For each index that is a ; variable, it must not be among the producer variables, to prevent its capture ; in the generated updater call. ; Of course, we make other checks too: for example, all of the top-level ; let-bound stobj fields must be distinct stobj variables that suitably ; correspond to distinct field calls on the same concrete (not abstract) stobj. ; (If we want to relax that restriction, we need to think very carefully about ; capture issues.) ; In raw Lisp, the expansion avoids the expense of binding st+ to the updates, ; or even updating st+ at all, since the updates to its indicated stobj fields ; are all destructive. IGNORE declarations take the place of those updates. ; And the update uses let* instead of let at the top level, since (at least in ; CCL) that adds efficiency. ; (let* ((st1 (fld1 st+)) ; (st2 (fld2 st+)) ; (st3 (fld3i 4 st+))) ; (declare (ignorable st1 st2 st3)) ; (mv-let (x st1 y st3 ...) ; (producer st1 st2 st3 ...) ; (declare (ignore st1 st3)) ; (consumer st+ x y ...))) ; Note that bound variables of a stobj-let form must be unique. Thus, if the ; parent stobj has two fields of the same stobj type, then they cannot be bound ; by the same stobj-let form unless different variables are used. This may be ; possible, since stobj-let permits congruent stobjs in the bindings. For ; example, suppose that we started instead with these defstobj events. ; (defstobj st1 ...) ; (defstobj st2 ... :congruent-to st1) ; (defstobj st3 ...) ; (defstobj st+ ; (fld1 :type st1) ; (fld2 :type st1) ; (fld3 :type (array st3 (8)))) ; Then we can write the same stobj-let form as before. ACL2 will check that ; st2 is congruent to the type of fld2, which is st1. ; The discussion above assumes that there are at least two producer variables ; for a stobj-let. However, one producer variable is permitted, which ; generates a LET in place of an MV-LET. For example, consider the following. ; (stobj-let ; ((st1 (fld1 st+)) ; (st2 (fld2 st+)) ; (st3 (fld3i 4 st+))) ; (st1) ; producer variable ; (producer st1 st2 st3 ...) ; (consumer st+ x y ...)) ; Here is the translation in the logic. ; (let ((st1 (fld1 st+)) ; (st2 (fld2 st+)) ; (st3 (fld3 st+))) ; (declare (ignorable st1 st2 st3)) ; (let ((st1 (check-vars-not-free (st+) ; (producer st1 st2 st3 ...)))) ; (let* ((st+ (update-fld1 st1 st+))) ; (check-vars-not-free (st1 st2 st3) ; (consumer st+ x y ...))))) ; For simplicity, each binding (in the first argument of stobj-let) should be ; for a stobj field. ; We had the following concern about *1* code generated for updaters of stobjs ; with stobj fields. Oneify-cltl-code generates a check for *1* updater calls, ; for whether a stobj argument is live. But should we consider the possibility ; that one stobj argument is live and another stobj argument is not? ; Fortunately, that's not an issue: if one stobj argument is live, then we are ; running code, in which case translate11 ensures that all stobj arguments are ; live. ; End of Essay on Nested Stobjs (defdoc nested-stobjs ; WARNING: This documentation contains examples from community book ; misc/nested-stobj-tests.lisp. If you change those examples here, change them ; also in that book. ":Doc-Section stobj using ~il[stobj]s that contain stobjs~/ For this topic we assume that you already understand the basics of single-threaded objects in ACL2. ~l[stobj], and in particular, ~pl[defstobj]. The latter topic defers discussion of the ability to specify a stobj field that is itself a stobj or an array of stobjs. That discussion is the subject of the present ~il[documentation] topic. Our presentation is in four sections. First we augment the documentation for ~ilc[defstobj] by explaining how stobjs may be specified for fields in a new stobj definition. Then we explain an aliasing problem, which accounts for a prohibition against making direct calls to accessors and updaters involving stobj fields of stobjs. Next, we introduce an ACL2 primitive, ~c[stobj-let], which provides the only way to read and write stobj components of stobjs. The final section provides precise documentation for ~c[stobj-let]. See also ACL2 community book ~c[demos/modeling/nested-stobj-toy-isa.lisp] for a worked example, which applies nested stobj structures to defining interpreters. A variety of small additional examples may be found in ACL2 community book ~c[misc/nested-stobj-tests.lisp]. For further discussion, you are welcome to read the ``Essay on Nested Stobjs'', a long comment in ACL2 source file ~c[other-events.lisp]. However, this documentation topic is intended to be self-contained for those familiar with ~il[stobj]s. SECTION: Extension of ~ilc[defstobj] to permit ~il[stobj]s within stobjs Recall that the ~c[:type] keyword of a ~ilc[defstobj] field descriptor can be a ``type-indicator'' that specifies the type of the field as a type-spec (~pl[type-spec]). For example, the following specifies an integer field and a field that is an array of bytes. ~bv[] (defstobj st (int-field :type integer :initially 0) (ar-field :type (array unsigned-byte (10)) :initially 0)) ~ev[] But the ~c[:type] of a stobj field descriptor may instead be based on a stobj. For example, the following sequence of three ~il[events] is legal. The first field descriptor of ~c[top], named ~c[sub1-field], illustrates one new kind of value for ~c[:type]: the name of a previously-introduced stobj, which here is ~c[sub1]. The second field descriptor of ~c[top], named ~c[sub2-ar-field], illustrates the other new kind of value for ~c[:type]: an array whose elements are specified by the name of a previously-introduced stobj, in this case, the stobj ~c[sub2]. ~bv[] (defstobj sub1 fld1) (defstobj sub2 fld2) (defstobj top (sub1-field :type sub1) (sub2-ar-field :type (array sub2 (10)))) ~ev[] The ~c[:initially] keyword is illegal for fields whose ~c[:type] is a stobj or an array of stobjs. Each such initial value is provided by a corresponding call of the stobj creator for that stobj. In particular, in the case of an array of stobjs, the stobj creator is called once for each element of the array, so that the array elements are distinct. For example, each element of ~c[sub2-ar-field] in the example above is initially provided by a separate call of ~c[create-sub2]. Each initial element is thus unique, and in particular is distinct from the initial global value of the stobj. Similarly, the initial global stobj for ~c[sub1] is distinct from the initial ~c[sub1-field] field of the global stobj for ~c[top], as these result from separate calls of ~c[create-sub1]. When a stobj is used in a field of another stobj, we may refer to the former field as a ``child stobj'' and the latter stobj as a ``parent stobj''. So in the example above, ~c[sub1-field] is a child stobj of type ~c[sub1] for parent stobj ~c[top], and ~c[sub2-ar-field] is an array of child stobjs of type ~c[sub2] for parent stobj ~c[top]. A child stobj has the same structural shape as the global stobj of its type, but as explained above, these are distinct structures. We follow standard terminology by saying ``isomorphic'' to indicate the same structural shape. So for example, (the value of) ~c[sub1-field] is isomorphic to ~c[sub1], though these are distinct structures. SECTION: An aliasing problem Before introducing ~c[stobj-let] below, we provide motivatation for this ACL2 primitive. Consider the following ~il[events]. ~bv[] (defstobj child fld) (defstobj parent (fld2 :type child)) ~ev[] Now suppose we could evaluate the following code, to be run immediately after admitting the two ~ilc[defstobj] events above. ~bv[] (let* ((child (fld2 parent)) (child (update-fld 3 child))) (mv child parent)) ~ev[] Now logically there is no change to ~c[parent]: ~c[parent] is passed through unchanged. We can indeed prove that fact! ~bv[] (thm (equal (mv-nth 1 (let* ((child (fld2 parent)) (child (update-fld 3 child))) (mv child parent))) parent)) ~ev[] But recall that stobjs are updated with destructive assignments. That is, we really are updating ~c[(fld2 parent)] to be the new value of ~c[child], whether this is explained logically or not. Thus, evaluation of the above ~ilc[let*] form does in fact change the actual global stobj, ~c[parent]. (Aside: Here is an explanation involving raw Lisp, for those who might find this useful. We escape to raw Lisp and execute the following; note that ~c[*the-live-parent*] is the Lisp variable representing the global value of ~c[parent]. ~bv[] (let ((parent *the-live-parent*)) (let* ((child (fld2 parent)) (child (update-fld 4 child))) (mv child parent))) ~ev[] Then, in raw Lisp, ~c[(fld (fld2 *the-live-parent*))] evaluates to ~c[4], illustrating the destructive update. End of Aside.) Such aliasing can permit a change to a child stobj to cause a logically-inexplicable change to the parent stobj. Similarly, unfettered accessing of stobj fields can result in logically inexplicable changes to the child stobj when the parent stobj is changed. Thus, ACL2 disallows direct calls of stobj accessors and updaters for fields whose ~c[:type] is a stobj or an array of stobjs. Instead, ACL2 provides ~c[stobj-let] for reading and writing such fields in a sound manner. SECTION: Accessing and updating stobj fields of stobjs using ~c[stobj-let] ACL2 provides a primitive, ~c[stobj-let], to access and update stobj fields of stobjs, in a manner that avoids the aliasing problem discussed above. In this section we provide an informal introduction to ~c[stobj-let], using examples, to be followed in the next section by precise documentation. We begin by returning to a slight variant of the example above. ~bv[] (defstobj child fld) (defstobj parent (fld2 :type child) fld3) ~ev[] The following form returns the result of updating the ~c[fld2] field of ~c[parent], which is a stobj isomorphic to ~c[child], to have a value of 3. Below we explain the terms ``bindings'', ``producer variables'', ``producer'', and ``consumer'', as well as how to understand this form. ~bv[] (stobj-let ((child (fld2 parent))) ; bindings (child) ; producer variable(s) (update-fld 3 child) ; producer (update-fld3 'a parent)) ; consumer ~ev[] The four lines under ``~c[stobj-let]'' just above can be understood as follows. ~bf[] o Bindings: Bind ~c[child] to ~c[(fld2 parent)]. o Producer variable(s) and producer: Then bind the variable, ~c[child], to the value of the producer, ~c[(update-fld 3 child)]. o Implicit update of parent: Update ~c[fld2] of ~c[parent] with the producer variable, ~c[child]. o Consumer: Finally, return ~c[(update-fld3 'a parent)]. ~ef[] Thus, the logical expansion of the ~c[stobj-let] form above is the following expression, though this is approximate (see below). ~bv[] (let ((child (fld2 parent))) ; bindings (let ((child (update-fld 3 child))) ; bind producer vars to producer (let ((parent (update-fld2 child parent))) ; implicit update of parent (update-fld3 'a parent)))) ~ev[] The bindings always bind distinct names to child stobjs of a unique parent stobj, where the child stobj corresponds to the ~c[:type] associated with the indicated accessor in the ~ilc[defstobj] form for the parent stobj. Thus in this case, for the unique binding, variable ~c[child] is bound to the stobj of `type' ~c[child] for accessor ~c[fld2] of the parent stobj, ~c[parent]. We refer to ~c[child] from the bindings as a ``stobj-let-bound variable''. Note also that the ``implicit extra step'' mentioned above is generated by macroexpansion of ~c[stobj-let]; it logically updates the parent with new child values, just before calling the consumer. Implementation note: Destructive updating in raw Lisp lets us omit this implicit extra step. The form above is equivalent to the form displayed just below, which differs only in specifying an explicit stobj updater corresponding to the stobj accessor, ~c[fld2]. Here we show the default updater name, whose name has ~c[\"UPDATE-\"] prepended to the name of the accessor. But if the ~c[:RENAMING] field of the ~c[defstobj] event specified a different updater name corresponding to ~c[fld2], then that would need to be included where we have added ~c[update-fld2] below. ~bv[] (stobj-let ((child (fld2 parent) update-fld2)) ; bindings, including updater(s) (child) ; producer variables (update-fld 3 child) ; producer (update-fld3 'a parent)) ; consumer ~ev[] You can experiment using ~c[:]~ilc[trans1] to see the single-step macroexpansion of a ~c[stobj-let] form in the logic. For example, here is how that works for a ~c[stobj-let] form that binds three fields and updates two of them. Notice that because more than one field is updated, an ~ilc[mv-let] form is generated to bind the two fields to their values returned by the producer, rather than a ~ilc[let] form as previously generated. First, let's introduce some events. ~bv[] (defstobj child1 child1-fld) (defstobj child2 child2-fld) (defstobj child3 child3-fld) (defstobj mom (fld1 :type child1) (fld2 :type child2) (fld3 :type child3)) ; Silly stub: (defun update-last-op (op mom) (declare (xargs :stobjs mom)) (declare (ignore op)) mom) (defun new-mom (mom) (declare (xargs :stobjs mom)) (stobj-let ((child1 (fld1 mom)) (child2 (fld2 mom)) (child3 (fld3 mom))) (child1 child3) (let* ((child1 (update-child1-fld 'one child1)) (child3 (update-child3-fld 'three child3))) (mv child1 child3)) (update-last-op 'my-compute mom))) ~ev[] Now let's look at the single-step macroexpansion of the above ~c[stobj-let] form. ~bv[] ACL2 !>:trans1 (stobj-let ((child1 (fld1 mom)) (child2 (fld2 mom)) (child3 (fld3 mom))) (child1 child3) (let* ((child1 (update-child1-fld 'one child1)) (child3 (update-child3-fld 'three child3))) (mv child1 child3)) (update-last-op 'my-compute mom)) (PROGN$ (LET ((CHILD1 (FLD1 MOM)) (CHILD2 (FLD2 MOM)) (CHILD3 (FLD3 MOM))) (DECLARE (IGNORABLE CHILD1 CHILD2 CHILD3)) (MV-LET (CHILD1 CHILD3) (CHECK-VARS-NOT-FREE (MOM) (LET* ((CHILD1 (UPDATE-CHILD1-FLD 'ONE CHILD1)) (CHILD3 (UPDATE-CHILD3-FLD 'THREE CHILD3))) (MV CHILD1 CHILD3))) (LET* ((MOM (UPDATE-FLD1 CHILD1 MOM)) (MOM (UPDATE-FLD3 CHILD3 MOM))) (CHECK-VARS-NOT-FREE (CHILD1 CHILD2 CHILD3) (UPDATE-LAST-OP 'MY-COMPUTE MOM)))))) ACL2 !> ~ev[] If you try to evaluate a ~c[stobj-let] form directly in the top-level loop, rather than from within a function body, you will get an error. The example above illustrates how ~c[stobj-let] may be used in function bodies; here is another example, presented using an edited log. ~bv[] ACL2 !>(defstobj child fld) Summary Form: ( DEFSTOBJ CHILD ...) Rules: NIL Time: 0.02 seconds (prove: 0.00, print: 0.00, other: 0.02) CHILD ACL2 !>(defstobj parent (fld2 :type child) fld3) Summary Form: ( DEFSTOBJ PARENT ...) Rules: NIL Time: 0.02 seconds (prove: 0.00, print: 0.00, other: 0.02) PARENT ACL2 !>(defun f (parent) (declare (xargs :stobjs parent)) (stobj-let ((child (fld2 parent))) ; bindings (child) ; producer variables (update-fld 3 child) ; producer (update-fld3 'a parent))) ; consumer [[output omitted]] F ACL2 !>(f parent) ACL2 !>(defun check-f (parent) ; returns the value of the field of the child stobj (declare (xargs :stobjs parent)) (stobj-let ((child (fld2 parent))) ; bindings (val) ; producer variables (fld child) ; producer val)) ; consumer [[output omitted]] CHECK-F ACL2 !>(check-f parent) 3 ACL2 !> ~ev[] Notice that the second function defined above, ~c[check-f], uses a ~c[stobj-let] form that returns an ordinary value: it reads a value from a child stobj, but does not write to the child stobj, as indicated by the lack of a child stobj among the producer variables. So for that ~c[stobj-let] form, there is no implicit extra step. We labeled a ~c[stobj-let] expansion above as ``approximate'' for two reasons, which we give here informally. (Now you know how to apply ~c[:]~ilc[trans1] to that ~c[stobj-let] call to see the precise expansion.) First, ~c[stobj-let] declares the stobj-let-bound variables to be ~c[ignorable] for the top ~c[let] bindings. Second, and more importantly, ~c[stobj-let] imposes the following restrictions on the producer and consumer, to avoid the aliasing problem: it disallows references to the parent stobj in the producer and it also disallows references to any bound stobj (i.e., bound in the bindings) in the consumer. We conclude this section with examples based on a slight variation of the nested stobj example from the first section above. These events can also be found in ACL2 community book ~c[misc/nested-stobj-tests.lisp], immediately under the following comment: ~bv[] ; As promised in :doc stobj-let, we begin with an example from that :doc. ~ev[] Note that some lemmas were needed in order to complete the ~il[guard] proof for the function ~c[update-top], which may be found in the above file; they are omitted below. First we introduce three stobjs. ~bv[] (defstobj kid1 fld1) (defstobj kid2 fld2) (defstobj mom (kid1-field :type kid1) (kid2-ar-field :type (array kid2 (5))) last-op) ~ev[] The next function takes a given index and a ~c[mom] stobj, and swaps the value stored in the stobj in ~c[mom]'s ~c[kid2-ar-field] array at that index with the value stored in the stobj in ~c[mom]'s ~c[kid1-field] field. ~bv[] (defun mom-swap-fields (index mom) (declare (xargs :stobjs mom :guard (and (natp index) (< index (kid2-ar-field-length mom))))) (stobj-let ((kid1 (kid1-field mom)) (kid2 (kid2-ar-fieldi index mom))) (kid1 kid2) (let* ((val1 (fld1 kid1)) (val2 (fld2 kid2)) (kid1 (update-fld1 val2 kid1)) (kid2 (update-fld2 val1 kid2))) (mv kid1 kid2)) (update-last-op 'swap mom))) ~ev[] Function ~c[mom.kid1-fld1] stores a given value in the given ~c[mom]'s ~c[kid1-fld1] field. ~bv[] (defun mom.kid1-fld1 (val mom) (declare (xargs :stobjs mom)) (stobj-let ((kid1 (kid1-field mom))) (kid1) (update-fld1 val kid1) (update-last-op val mom))) ~ev[] We next combine the two functions above, according to an ~c[op] argument, as indicated by the following definition. ~bv[] (defun update-mom (op mom) (declare (xargs :stobjs mom)) (cond ((and (consp op) (eq (car op) 'swap) (natp (cdr op)) (< (cdr op) (kid2-ar-field-length mom))) (mom-swap-fields (cdr op) mom)) (t (mom.kid1-fld1 op mom)))) ~ev[] The following checker function uses a ~c[stobj-let] form like the ones above, a major difference being that the producer variable is not a stobj, since it does not modify the input stobj, ~c[mom]. ~bv[] (defun check-update-mom (index val1 val2 last-op mom) (declare (xargs :stobjs mom :mode :program :guard (or (null index) (and (natp index) (< index (kid2-ar-field-length mom)))))) (and (equal (last-op mom) last-op) (stobj-let ((kid1 (kid1-field mom)) (kid2 (kid2-ar-fieldi index mom))) (val) ; producer variables (and (equal val1 (fld1 kid1)) (equal val2 (fld2 kid2))) val))) ~ev[] Now let us run our update function to populate some fields within the ~c[mom] stobj. ~bv[] (let* ((mom ; set mom to (3 (x0 x1 x2 x3 x4)) (update-mom 3 mom)) (mom ; set mom to (x1 (x0 3 x2 x3 x4)) (update-mom '(swap . 1) mom)) (mom ; set mom to (7 (x0 3 x2 x3 x4)) (update-mom 7 mom)) (mom ; set mom to (x0 (7 3 x2 x3 x4)) (update-mom '(swap . 0) mom)) (mom ; set mom to (5 (7 3 x2 x3 x4)) (update-mom 5 mom)) (mom ; set mom to (7 (5 3 x2 x3 x4)) (update-mom '(swap . 0) mom))) mom) ~ev[] Are the above values of 7, 5, and 3 as expected, with a last operation being a swap? Yes! ~bv[] ACL2 !>(and (check-update-mom 0 7 5 'swap mom) (check-update-mom 1 7 3 'swap mom)) T ACL2 !> ~ev[] Notice that above, we never tried to access two different entries of the array. This can be done, but we need to bind two different stobjs to those fields. Fortunately, congruent stobjs make this possible; ~pl[defstobj], in particular the discussion of congruent stobjs. Since we want to bind two stobjs to values in the array that are isomorphic to the stobj ~c[kid2], we introduce a stobj congruent to ~c[kid2]. ~bv[] (defstobj kid2a fld2a :congruent-to kid2) ~ev[] Then we can define our swapping function as follows. The ~il[guard] proof obligation includes the requirement that the two indices be distinct, again to avoid an aliasing problem. ~bv[] (defun mom-swap-indices (i1 i2 mom) (declare (xargs :stobjs mom :guard (and (natp i1) (< i1 (kid2-ar-field-length mom)) (natp i2) (< i2 (kid2-ar-field-length mom)) (not (equal i1 i2))))) (stobj-let ((kid2 (kid2-ar-fieldi i1 mom)) (kid2a (kid2-ar-fieldi i2 mom))) (kid2 kid2a) (let* ((val2 (fld2 kid2)) (val2a (fld2 kid2a)) (kid2 (update-fld2 val2a kid2)) (kid2a (update-fld2 val2 kid2a))) (mv kid2 kid2a)) mom)) ~ev[] The aforementioned community book, ~c[misc/nested-stobj-tests.lisp], contains a corresponding checker immediately following this definition. SECTION: Precise documentation for ~c[stobj-let] ~bv[] General Form: (stobj-let BINDINGS PRODUCER-VARIABLES PRODUCER CONSUMER) ~ev[] where ~c[PRODUCER-VARIABLES] is a non-empty true list of legal variable names without duplicates, ~c[PRODUCER] and ~c[CONSUMER] are expressions, and ~c[BINDINGS] is a list subject to the following requirements. ~c[BINDINGS] is a non-empty true list of tuples, each of which has the form ~c[(VAR ACCESSOR)] or ~c[(VAR ACCESSOR UPDATER)]. There is a stobj name, ~c[ST], previously introduced by ~ilc[defstobj] (not ~ilc[defabsstobj]), such that each ~c[accessor] is of the form ~c[(ACC ST)] or ~c[(ACCi I ST)], with the same stobj name (~c[ST]) for each binding. In the case ~c[(ACC ST)], ~c[ACC] is the accessor for a non-array field of ~c[ST]. In the case ~c[(ACCi I ST)], ~c[ACCi] is the accessor for an array field of ~c[ST], and ~c[I] is either a variable, a natural number, a list ~c[(quote N)] where ~c[N] is a natural number, or a symbol introduced by ~ilc[defconst]. If ~c[UPDATER] is supplied, then it is a symbol that is the name of the stobj updater for the field of ~c[ST] accessed by ~c[ACCESSOR]. If ~c[UPDATER] is not supplied, then for the discussion below we consider it to be, implicitly, the symbol in the same package as the function symbol of ~c[ACCESSOR] (i.e., ~c[ACC] or ~c[ACCi]), obtained by prepending the string ~c[\"UPDATE-\"] to the ~ilc[symbol-name] of that function symbol. Finally, ~c[ACCESSOR] has a ~il[signature] specifying a return value that is either ~c[ST] or is stobj that is congruent to ~c[ST]. If the conditions above are met, then the General Form expands to the one of the following expressions, depending on whether the list ~c[PRODUCER-VARIABLES] has one member or more than one member, respectively. (But see below for extra code that may be inserted if there are stobj array accesses in ~c[BINDINGS].) Here we write ~c[STOBJ-LET-BOUND-VARS] for the list of variables ~c[VAR] discussed above, i.e., for ~c[(strip-cars BINDINGS)]. And, we write ~c[UPDATES] for the result of mapping through ~c[PRODUCER-VARIABLES] and, for each variable ~c[VAR] that has a binding ~c[(VAR ACCESSOR UPDATER)] in ~c[BINDINGS] (where ~c[UPDATER] may be implicit, as discussed above), collect into ~c[UPDATES] the tuple ~c[(ST (UPDATER VAR ST))]. For ~c[PRODUCER-VARIABLES] = ~c[(PRODUCER-VAR)]: ~bv[] (let BINDINGS (declare (ignorable . STOBJ-LET-BOUND-VARIABLES)) (let ((PRODUCER-VAR PRODUCER)) (let* UPDATES CONSUMER))) ~ev[] Otherwise: ~bv[] (let BINDINGS (declare (ignorable . STOBJ-LET-BOUND-VARIABLES)) (mv-let PRODUCER-VARS PRODUCER (let* UPDATES CONSUMER))) ~ev[] Moreover, ACL2 places restrictions on the resulting expression: ~c[ST] must not occur free in ~c[PRODUCER], and every variable in ~c[STOBJ-LET-BOUND-VARIABLES] must not occur free in ~c[CONSUMER]. ~c[Stobj-let] forms can be evaluated using ordinary objects in theorem contexts, much as any form. They can also, of course, appear in function bodis. However, a ~c[stobj-let] form cannot be evaluated directly in the top-level loop or other top-level contexts for execution (such as during ~ilc[make-event] expansion). Finally, let ~c[FORM] denote the form displayed above (either case). We explain how ~c[FORM] is actually replaced by an expression of the form ~c[(PROGN$ ... FORM)]. This expression generates an extra ~il[guard] proof obligation, which guarantees that no aliasing occurs from binding two stobj-let-bound variables to the same array access. So fix a stobj array accessor ~c[ACCi] for which some stobj is bound to ~c[(ACCi I ST)] in ~c[BINDINGS]; we define an expression ~c[ACCi-CHECK] as follows. Collect up all such index expressions ~c[I], where if ~c[I] is of the form ~c[(quote N)] then replace ~c[I] by ~c[N]. If the resulting list of index expressions for ~c[ACCi] consists solely of distinct numbers, or if it is of length 1, then no extra check is generated for ~c[ACCi]. Otherwise, let ~c[ACCi-CHECK] be the form ~c[(chk-no-duplicatesp (list I1 ... Ik))], where ~c[I1], ..., ~c[Ik] are the index expressions for ~c[ACCi]. Note: ~c[chk-no-duplicatesp] is a function that returns nil, but has a ~il[guard] that its argument is an ~ilc[eqlable-listp] that satisfies ~ilc[no-duplicatesp]. Finally, ~c[FORM] is replaced by ~c[(PROGN$ CHK1 ... CHKn FORM)], where the ~c[CHKm] range over all of the above ~c[ACCi-CHECK].~/~/") (defmacro stobj-let (&whole x &rest args) (declare (ignore args)) #+acl2-loop-only (stobj-let-fn x) #-acl2-loop-only (stobj-let-fn-raw x)) (link-doc-to stobj-let stobj nested-stobjs) (defun push-untouchable-fn (name fn-p state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cond ((symbolp name) (cond ((null doc) (msg "( PUSH-UNTOUCHABLE ~x0 ~x1)" name fn-p)) (t (msg "( PUSH-UNTOUCHABLE ~x0 ~x1 ...)" name fn-p)))) ((null doc) "( PUSH-UNTOUCHABLE ...)") (t "( PUSH-UNTOUCHABLE ... ...)"))) (let ((wrld (w state)) (event-form (or event-form (list* 'push-untouchable name fn-p (if doc (list :doc doc) nil)))) (names (if (symbolp name) (list name) name)) (untouchable-prop (cond (fn-p 'untouchable-fns) (t 'untouchable-vars)))) (er-let* ((doc-pair (translate-doc nil doc ctx state))) ; With no name to hang it on, we don't permit a formatted doc string. ; So the above causes an error if the string is formatted. Otherwise, ; we ignore doc-pair. (cond ((not (symbol-listp names)) (er soft ctx "The argument to push-untouchable must be either a non-nil symbol or a non-empty true list of symbols and ~x0 is ~ neither." name)) ((subsetp-eq names (global-val untouchable-prop wrld)) (stop-redundant-event ctx state)) (t (install-event name event-form 'push-untouchable 0 nil nil nil nil (global-set untouchable-prop (union-eq names (global-val untouchable-prop wrld)) wrld) state))))))) (defun remove-untouchable-fn (name fn-p state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cond ((symbolp name) (cond ((null doc) (msg "( REMOVE-UNTOUCHABLE ~x0 ~x1)" name fn-p)) (t (msg "( REMOVE-UNTOUCHABLE ~x0 ~x1 ...)" name fn-p)))) ((null doc) "( REMOVE-UNTOUCHABLE ...)") (t "( REMOVE-UNTOUCHABLE ... ...)"))) (let ((wrld (w state)) (event-form (or event-form (list* 'remove-untouchable name fn-p (if doc (list :doc doc) nil)))) (names (if (symbolp name) (list name) name)) (untouchable-prop (cond (fn-p 'untouchable-fns) (t 'untouchable-vars)))) (er-let* ((doc-pair (translate-doc nil doc ctx state))) ; With no name to hang it on, we don't permit a formatted doc string. ; So the above causes an error if the string is formatted. Otherwise, ; we ignore doc-pair. (cond ((not (symbol-listp names)) (er soft ctx "The argument to remove-untouchable must be either a non-nil symbol or a non-empty true list of symbols and ~x0 is neither." name)) ((not (intersectp-eq names (global-val untouchable-prop wrld))) (stop-redundant-event ctx state)) (t (let ((old-untouchable-prop (global-val untouchable-prop wrld))) (install-event name event-form 'remove-untouchable 0 nil nil nil nil (global-set untouchable-prop (set-difference-eq old-untouchable-prop names) wrld) state)))))))) (defun def-body-lemmas (def-bodies lemmas) (cond ((endp def-bodies) nil) (t (cons (find-runed-lemma (access def-body (car def-bodies) :rune) lemmas) (def-body-lemmas (cdr def-bodies) lemmas))))) (defmacro show-bodies (fn) ":Doc-Section Miscellaneous show the potential definition bodies~/ ~bv[] Examples: (show-bodies foo) :show-bodies foo ~ev[] A definition made using ~ilc[defun] installs a so-called ``body'' of a function symbol, as do certain ~c[:]~ilc[definition] rules. Such bodies are used in a number of ways, including the application of ~c[:expand] ~il[hints]; ~pl[definition], in particular the discussion of ``body'' there, and ~pl[hints] for a discussion of the ~c[:expand] hint. Also ~pl[set-body] for how to change which of the available definitions (among the original definition and appropriate ~c[:]~ilc[definition] rules) is the one that provides the body. The ~c[show-bodies] command displays the available such bodies in an appropriate format, starting with the one that is currently used as the body. ~bv[] General Forms: (show-bodies function-symbol) :show-bodies function-symbol ~ev[]~/~/" (declare (xargs :guard (or (symbolp fn) (and (true-listp fn) (eql (length fn) 2) (eq (car fn) 'quote) (symbolp (cadr fn)))))) (let ((fn (if (symbolp fn) fn (cadr fn)))) `(let* ((wrld (w state)) (fn (deref-macro-name ',fn (macro-aliases wrld))) (lemmas (def-body-lemmas (getprop fn 'def-bodies nil 'current-acl2-world wrld) (getprop fn 'lemmas nil 'current-acl2-world wrld)))) (cond (lemmas (pprogn (fms "Definitional bodies available for ~x0, current ~ one listed first:~|" (list (cons #\0 fn)) (standard-co state) state nil) (print-info-for-rules (info-for-lemmas lemmas t (ens state) wrld) (standard-co state) state))) (t (er soft 'show-bodies "There are no definitional bodies for ~x0." fn)))))) (defun set-body-fn1 (rune def-bodies acc) (cond ((null def-bodies) ; error nil) ((equal rune (access def-body (car def-bodies) :rune)) (cons (car def-bodies) (revappend acc (cdr def-bodies)))) (t (set-body-fn1 rune (cdr def-bodies) (cons (car def-bodies) acc))))) (defun set-body-fn (fn name-or-rune state event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (if (output-in-infixp state) event-form (cond ((symbolp fn) (msg "( SET-BODY ~x0)" fn)) (t "( SET-BODY ...)"))) (let* ((wrld (w state)) (rune (if (symbolp name-or-rune) ; We don't yet know that name-or-rune is a function symbol in the current ; world, so we do not call fn-rune-nume here. (list :definition name-or-rune) name-or-rune)) (fn (and (symbolp fn) (deref-macro-name fn (macro-aliases wrld)))) (old-def-bodies (getprop fn 'def-bodies nil 'current-acl2-world wrld)) (def-bodies (and fn old-def-bodies (cond ((equal rune (access def-body (car old-def-bodies) :rune)) :redundant) (t (set-body-fn1 rune old-def-bodies nil)))))) (cond ((null def-bodies) (er soft ctx "No definitional body was found for function ~x0 with rune ~ ~x1. See :DOC set-body." fn rune)) ((eq def-bodies :redundant) (stop-redundant-event ctx state)) (t (install-event rune event-form 'set-body 0 nil nil nil ctx (putprop fn 'def-bodies def-bodies wrld) state)))))) ; Section: trace/untrace (defdoc trace ":Doc-Section Trace tracing functions in ACL2~/ ACL2 provides a trace utility, ~ilc[trace$], with corresponding reverse operation ~ilc[untrace$]. These can be used without any dependence on the underlying Lisp utility, and are the tracing utilities of choice in ACL2; ~pl[trace$] and ~pl[untrace$]. However, for advanced users we note that the underlying host Lisp may also provide a trace utility, ~c[trace], and corresponding ~c[untrace]. Moreover, these have been modified in the case that the host Lisp is GCL, Allegro CL, or CCL (OpenMCL), to provide limited support for ~c[:entry], ~c[:exit], and perhaps ~c[:cond] keywords, to hide certain large data structures (world, enabled structure, rewrite constant), and to trace executable counterparts. See source files ~c[*-trace.lisp]. For the above Lisps, you can invoke the original ~c[trace] and ~c[untrace] by invoking ~c[old-trace] and ~c[old-untrace], respectively, in raw Lisp rather than in the normal ACL2 loop.~/~/ :cite acl2::time-tracker :cited-by acl2::other :cited-by acl2::programming") #-acl2-loop-only (progn (defparameter *trace-evisc-tuple* nil) (defparameter *trace-evisc-tuple-world* nil) (defun trace-evisc-tuple () (cond ((and *trace-evisc-tuple-world* (not (eq *trace-evisc-tuple-world* (w *the-live-state*)))) (set-trace-evisc-tuple t *the-live-state*) *trace-evisc-tuple*) (t *trace-evisc-tuple*))) ) (defun trace-multiplicity (name state) ; Returns nil for functions unknown to ACL2. (let ((stobjs-out ; Return-last cannot be traced, so it is harmless to get the stobjs-out here ; without checking if name is return-last. (getprop name 'stobjs-out nil 'current-acl2-world (w state)))) (and stobjs-out (length stobjs-out)))) (defun first-trace-printing-column (state) ; This returns the first column after the trace prompt ("n> " or " " *trace-output*)) (2 (princ " 2> " *trace-output*)) (3 (princ " 3> " *trace-output*)) (4 (princ " 4> " *trace-output*)) (5 (princ " 5> " *trace-output*)) (6 (princ " 6> " *trace-output*)) (7 (princ " 7> " *trace-output*)) (8 (princ " 8> " *trace-output*)) (9 (princ " 9> " *trace-output*)) (t (princ (format nil " ~s> " trace-level) *trace-output*)))) (t (case trace-level (1 (princ "<1 " *trace-output*)) (2 (princ " <2 " *trace-output*)) (3 (princ " <3 " *trace-output*)) (4 (princ " <4 " *trace-output*)) (5 (princ " <5 " *trace-output*)) (6 (princ " <6 " *trace-output*)) (7 (princ " <7 " *trace-output*)) (8 (princ " <8 " *trace-output*)) (9 (princ " <9 " *trace-output*)) (t (princ (format nil " <~s " trace-level) *trace-output*)))))) (cond ((eq evisc-tuple :print) (format *trace-output* "~s~%" x)) (t (trace-ppr x evisc-tuple msgp *the-live-state*))) (when (not (eq direction :in)) (f-put-global 'trace-level (1-f trace-level) *the-live-state*)) (finish-output *trace-output*)))) (defun *1*defp (trace-spec wrld) (let ((fn (car trace-spec))) (not (eq (getprop fn 'formals t 'current-acl2-world wrld) t)))) (defun trace$-er-msg (fn) (msg "Ignoring request to trace function ~x0, because" fn)) (defun decls-and-doc (forms) (cond ((endp forms) nil) ((or (stringp (car forms)) (and (consp (car forms)) (eq (caar forms) 'declare))) (cons (car forms) (decls-and-doc (cdr forms)))) (t nil))) (defun trace$-when-gcond (gcond form) (if gcond `(when ,gcond ,form) form)) (defun stobj-evisceration-alist (user-stobj-alist state) (cond ((endp user-stobj-alist) (list (cons (coerce-state-to-object state) *evisceration-state-mark*))) (t (cons (cons (cdar user-stobj-alist) (evisceration-stobj-mark (caar user-stobj-alist) nil)) (stobj-evisceration-alist (cdr user-stobj-alist) state))))) (defun trace-evisceration-alist (state) (append (world-evisceration-alist state nil) (stobj-evisceration-alist (user-stobj-alist state) state))) (defun set-trace-evisc-tuple (val state) ":Doc-Section Trace set the trace evisc tuple~/ A trace evisc-tuple, which is set by this utility, provides a means to restrict printing during tracing. ~l[evisc-tuple] for an introduction to evisc-tuples; also ~pl[set-evisc-tuple] and ~pl[set-iprint]. By default the ACL2 ~il[trace] mechanism, ~ilc[trace$], automatically deals with stobjs, the logical world, and certain other large structures. ~l[trace$], in particular the documentation of ~c[trace$] option ~c[:hide]. However, even with that default behavior you may want to restrict what is printed according to the print-level and print-length of an evisc-tuple; ~pl[evisc-tuple]. ~bv[] Examples: ; Set trace evisc tuple to a standard value, using current Lisp *print-level* ; and *print-length* variables: (set-trace-evisc-tuple t state) ; Set trace evisc tuple back to its default: (set-trace-evisc-tuple nil state) ; Set trace evisc tuple to restrict print-level to 3 and print-length to 4, ; while hiding the logical world and suitably printing stobjs even if trace$ ; option ``:hide nil'' is used. (Note: calling ~c[trace-evisceration-alist] ; directly requires removing this function as `untouchable', which requires a ; trust tag; ~pl[remove-untouchable].) (set-trace-evisc-tuple (evisc-tuple 3 4 (trace-evisceration-alist state) nil) state)~/ General Forms: (set-trace-evisc-tuple nil state) ; trace evisc-tuple set to standard value (set-trace-evisc-tuple t state) ; trace evisc-tuple set to hide the logical ; world and deal with stobjs even when ; trace$ option ``:hide nil'' is supplied (set-trace-evisc-tuple evisc-tuple state) ; tracing set to use indicated evisc-tuple ~ev[] ~l[trace$] for a discussion of ACL2 tracing. The ~il[evisc-tuple] used by that trace utility is the one last installed by ~c[set-trace-evisc-tuple] (or by ~ilc[set-evisc-tuple] for the trace-evisc-tuple) ~-[] initially to the default of ~c[nil] ~-[] unless overriden by trace option ~c[:evisc-tuple]. ~st[Remark]. If you use value ~c[t], then ACL2 will ensure that the logical world and stobjs are kept up-to-date in the trace evisc-tuple." #+acl2-loop-only (declare (ignore val)) #-acl2-loop-only (cond ((null val) (setq *trace-evisc-tuple-world* nil) (setq *trace-evisc-tuple* nil)) ((eq val t) (setq *trace-evisc-tuple-world* (w *the-live-state*)) (setq *trace-evisc-tuple* (list (trace-evisceration-alist *the-live-state*) *print-level* *print-length* nil))) ((standard-evisc-tuplep val) (setq *trace-evisc-tuple-world* nil) (setq *trace-evisc-tuple* val)) (t (er hard 'set-trace-evisc-tuple "Illegal evisc tuple, ~x0" val))) state) (defun chk-trace-options-aux (form kwd formals ctx wrld state) ; Check that the indicated form returns a single, non-stobj value, and that ; term has no unexpected free variables. (er-let* ((term (translate form '(nil) nil '(state) ctx wrld state))) (let ((vars (set-difference-eq (all-vars term) (append (case kwd ((:entry :cond) '(traced-fn arglist state)) (:exit '(traced-fn arglist value values state)) (:hide nil) (otherwise '(state))) formals)))) (cond (vars (er soft ctx "Global variables, such as ~&0, are not allowed for ~ tracing option ~x1, especially without a trust tag. ~ See :DOC trace$." vars kwd)) (t (value nil)))))) (defun trace$-value-msgp (x) (and (consp x) (keywordp (car x)) (or (and (member-eq (car x) '(:fmt :fmt!)) (consp (cdr x)) (null (cddr x))) (er hard 'trace$ "Illegal :ENTRY value. A legal :ENTRY value starting with a ~ keyword must be of the form (:FMT x). The :ENTRY value ~x0 ~ is therefore illegal." x)) (car x))) (defun chk-trace-options (fn predefined trace-options formals ctx wrld state) (let ((notinline-tail (assoc-keyword :notinline trace-options)) (multiplicity-tail (assoc-keyword :multiplicity trace-options))) (cond ((and notinline-tail (not (member-eq (cadr notinline-tail) '(t nil :fncall)))) ; We are tempted to use a hard error here so that we don't see the message ; about trace! printed by trace$-fn-general. But then instead we see a message ; suggesting the use of trace, which is very odd here, since we are trying to ; trace! So we'll just live with seeing a not very helpful message about ; trace!. (er soft ctx "The only legal values for trace option :NOTINLINE are ~&0. The ~ value ~x1 is thus illegal." '(t nil :fncall) (cadr notinline-tail))) ((and multiplicity-tail (not (natp (cadr multiplicity-tail)))) (er soft ctx "The value of trace option :MULTIPLICITY must be a non-negative ~ integer value. The value ~x0 is thus illegal." (cadr multiplicity-tail))) ((and predefined (or (eq fn 'return-last) (and notinline-tail (not (eq (cadr notinline-tail) :fncall)) (or (member-eq fn (f-get-global 'program-fns-with-raw-code state)) (member-eq fn (f-get-global 'logic-fns-with-raw-code state)) (not (ttag wrld)))))) (cond ((eq fn 'return-last) (er soft ctx "Due to its special nature, tracing of ~x0 is not allowed." fn)) ((or (member-eq fn (f-get-global 'program-fns-with-raw-code state)) (member-eq fn (f-get-global 'logic-fns-with-raw-code state))) ; We could probably just arrange not to trace the *1* function in this case. ; But for now we'll cause an error. (er soft ctx "The ACL2 built-in function ~x0 has special code that will not be ~ captured properly when creating code for its traced executable ~ counterpart. It is therefore illegal to specify a value for ~ :NOTINLINE other than :FNCALL unless there is an active trust ~ tag. There may be an easy fix, so contact the ACL2 implementors ~ if this error presents a hardship." fn)) (t (er soft ctx "The function ~x0 is built into ACL2. It is therefore illegal to ~ specify a value for :NOTINLINE other than :FNCALL unless there ~ is an active trust tag." fn)))) ((ttag wrld) (value nil)) (t (let* ((cond-tail (assoc-keyword :cond trace-options)) (entry-tail (assoc-keyword :entry trace-options)) (exit-tail (assoc-keyword :exit trace-options)) (evisc-tuple-tail (assoc-keyword :evisc-tuple trace-options))) (er-progn (if cond-tail (chk-trace-options-aux (cadr cond-tail) :cond formals ctx wrld state) (value nil)) (if entry-tail (chk-trace-options-aux (if (trace$-value-msgp (cadr entry-tail)) (cadr (cadr entry-tail)) (cadr entry-tail)) :entry formals ctx wrld state) (value nil)) (if exit-tail (chk-trace-options-aux (if (trace$-value-msgp (cadr exit-tail)) (cadr (cadr exit-tail)) (cadr exit-tail)) :exit formals ctx wrld state) (value nil)) (if (and evisc-tuple-tail (not (member-eq (cadr evisc-tuple-tail) '(:print :no-print)))) (chk-trace-options-aux (cadr evisc-tuple-tail) :evisc-tuple formals ctx wrld state) (value nil)))))))) (defun memoize-off-trace-error (fn ctx) (er hard ctx "Memoized function ~x0 is to be traced or untraced, but its ~ symbol-function differs from the :MEMOIZED-FN field of its memoization ~ hash-table entry. Perhaps the trace or untrace request occurred in ~ the context of ~x1; at any rate, it is illegal." fn 'memoize-off)) (defun untrace$-fn1 (fn state) #-acl2-loop-only (let* ((old-fn (get fn 'acl2-trace-saved-fn)) (*1*fn (*1*-symbol? fn)) (old-*1*fn (get *1*fn 'acl2-trace-saved-fn)) #+hons (memo-entry (memoizedp-raw fn))) #+hons (when (and memo-entry (not (eq (symbol-function fn) (access memoize-info-ht-entry memo-entry :memoized-fn)))) ; See comment about this "strange state of affairs" in trace$-def. (memoize-off-trace-error fn 'untrace$)) ; We do a raw Lisp untrace in case we traced with :native. We use eval here ; because at the time we evaluate this definition, untrace might not yet have ; been modified (e.g., by allegro-acl2-trace.lisp). (eval `(maybe-untrace ,fn)) (when old-fn ; Warning: Do not print an error or warning here. See the comment about ; "silent no-op" below and in trace$-fn-general. (setf (symbol-function fn) old-fn) #+hons (when memo-entry (setf (gethash fn *memoize-info-ht*) (change memoize-info-ht-entry memo-entry :memoized-fn old-fn))) (setf (get fn 'acl2-trace-saved-fn) nil)) (when old-*1*fn ; Warning: Do not print an error or warning here. See the comment about ; "silent no-op" below and in trace$-fn-general. (setf (symbol-function *1*fn) old-*1*fn) (setf (get *1*fn 'acl2-trace-saved-fn) nil))) ; If we interrupt before completing update of the global below, then we may leave a ; trace-spec in that global even though the function is partially or totally ; untraced in the sense of the two forms above. That's perfectly OK, however, ; because if the function is not actually traced then the corresponding WHEN ; form above will be a silent no-op. (f-put-global 'trace-specs (delete-assoc-eq fn (f-get-global 'trace-specs state)) state)) (defun untrace$-rec (fns ctx state) (cond ((endp fns) (value nil)) (t (let ((trace-spec (assoc-eq (car fns) (f-get-global 'trace-specs state)))) (cond (trace-spec (pprogn (untrace$-fn1 (car fns) state) (er-let* ((fnlist (untrace$-rec (cdr fns) ctx state))) (value (cons (car fns) fnlist))))) (t (pprogn (warning$ ctx "Trace" "The function ~x0 is not currently traced. Ignoring ~ attempt to apply untrace$ to it." (car fns)) (untrace$-rec (cdr fns) ctx state)))))))) (defun untrace$-fn (fns state) (let ((ctx 'untrace$)) (cond ((null fns) (untrace$-rec (strip-cars (f-get-global 'trace-specs state)) ctx state)) ((symbol-listp fns) (untrace$-rec fns ctx state)) (t (er soft ctx "Untrace$ may only be applied to a list of symbols, hence not to~s." fns))))) (defun maybe-untrace$-fn (fn state) (prog2$ (or (symbolp fn) (er hard 'untrace$ "Illegal attempt to untrace non-symbol: ~x0" fn)) (if (assoc-eq fn (f-get-global 'trace-specs state)) (untrace$-fn1 fn state) state))) (defmacro maybe-untrace$ (fn) `(maybe-untrace$-fn ',fn state)) #-acl2-loop-only (defmacro maybe-untrace (fn) ; We use eval here because at the time we evaluate this definition, untrace ; might not yet have been modified (e.g., by allegro-acl2-trace.lisp). `(when (member-eq ',fn (trace)) (eval '(untrace ,fn)) t)) #-acl2-loop-only (defun maybe-untrace! (fn &optional verbose) ; WART: Calling this in raw Lisp changes the state without an in-the-logic ; explanation, because it modifies state global variable 'trace-specs. ; Consider using the oracle of the state within the logic to explain this ; wart. ; Inline maybe-untrace$-fn: (let ((state *the-live-state*)) (when (assoc-eq fn (f-get-global 'trace-specs state)) (untrace$-fn1 fn state) (when verbose (observation "untracing" "Untracing ~x0." fn))) (when (and (eval `(maybe-untrace ,fn)) verbose) (observation "untracing" "Raw-Lisp untracing ~x0." fn)) nil)) #-acl2-loop-only (defun increment-trace-level () (f-put-global 'trace-level (1+f (f-get-global 'trace-level *the-live-state*)) *the-live-state*)) #-acl2-loop-only (defun trace$-def (arglist def trace-options predefined multiplicity ctx) #-hons (declare (ignore ctx)) (let* ((state-bound-p (member-eq 'state arglist)) (fn (car def)) (cond-tail (assoc-keyword :cond trace-options)) (cond (cadr cond-tail)) (hide-tail (assoc-keyword :hide trace-options)) (hide (or (null hide-tail) ; default is t (cadr hide-tail))) (entry (or (cadr (assoc-keyword :entry trace-options)) (list 'cons (kwote fn) 'arglist))) (entry-msgp (trace$-value-msgp entry)) (entry (if entry-msgp (cadr entry) entry)) (exit (or (cadr (assoc-keyword :exit trace-options)) (list 'cons (kwote fn) 'values))) (exit-msgp (trace$-value-msgp exit)) (exit (if exit-msgp (cadr exit) exit)) (notinline-tail (assoc-keyword :notinline trace-options)) (notinline-nil (and notinline-tail (null (cadr notinline-tail)))) #+hons (memo-entry (memoizedp-raw fn)) (notinline-fncall (cond (notinline-tail #+hons (or (eq (cadr notinline-tail) :fncall) (and memo-entry (er hard ctx "It is illegal to specify a value for ~ trace$ option :NOTINLINE other than ~ :FNCALL for a memoized function. The ~ suggested trace spec for ~x0, which ~ specifies :NOTINLINE ~x0, is thus ~ illegal." fn (cadr notinline-tail)))) #-hons (eq (cadr notinline-tail) :fncall)) #+hons (memo-entry ; Memoization installs its own symbol-function for fn, so we do not want to ; insert the body of fn into the traced definition; instead, we want to call ; the traced version of fn to call the "old" (memoized) fn. Note that we ; always remove any trace when memoizing or unmemoizing, so we don't have the ; symmetric problem of figuring out how to make a memoized function call a ; traced function. t) ((or (not def) ; then no choice in the matter! predefined (member-eq fn (f-get-global 'program-fns-with-raw-code *the-live-state*)) (member-eq fn (f-get-global 'logic-fns-with-raw-code *the-live-state*))) t) (t nil))) (gcond (and cond-tail (acl2-gentemp "COND"))) (garglist (acl2-gentemp "ARGLIST")) (evisc-tuple-tail (assoc-keyword :evisc-tuple trace-options)) (evisc-tuple (if evisc-tuple-tail (cadr evisc-tuple-tail) '(trace-evisc-tuple))) (gevisc-tuple (and evisc-tuple-tail (acl2-gentemp "EVISC-TUPLE"))) (decls-and-doc (and def ; optimization (decls-and-doc (cddr def)))) (body (and def ; optimization (nthcdr (length decls-and-doc) (cddr def)))) (new-body (if notinline-fncall `(funcall (get ',fn 'acl2-trace-saved-fn) ,@arglist) `(block ,fn (progn ,@body))))) #+hons (when (and memo-entry (not (eq (symbol-function fn) (access memoize-info-ht-entry memo-entry :memoized-fn)))) ; This is a strange state of affairs that we prefer not to try to support. For ; example, it is not clear how things would work out after we installed the ; traced symbol-function as the :memoized-fn. ; Note by the way that under memoize-off, tracing will be defeated. If this ; presents a big problem then we can reconsider the design of how tracing and ; memoization interact. (memoize-off-trace-error fn ctx)) `(defun ,fn ,(if state-bound-p arglist (append arglist '(&aux (state *the-live-state*)))) ,@(if state-bound-p nil '((declare (ignorable state)))) ; At one time we included declarations and documentation here: ; ,@(and (not notinline-fncall) ; else just lay down fncall; skip decls ; decls-and-doc) ; But then we saw compiler warnings, for example: ; (defun foo (x y) (declare (ignore x)) y) ; (trace$ (foo :compile t)) ; could give: ; ; While compiling FOO: ; Warning: variable X is used yet it was declared ignored ; When tracing, it seems needless to install documentation or to keep ; declarations (as tracing can't be expected to be fast), so we keep things ; simple and just throw away the declarations and documentation. Notice that ; because of ,@arglist below, none of the formals is ignored. ,@(and (not notinline-nil) `((declare (notinline ,fn)))) ,@(and predefined `((when *inside-trace$* (return-from ,fn (funcall (get ',fn 'acl2-trace-saved-fn) ,@arglist))))) (let ((,garglist (list ,@arglist)) ,@(and gevisc-tuple `((,gevisc-tuple ,evisc-tuple)))) (let ,(and gcond `((,gcond (let ((arglist ,garglist) (traced-fn ',fn)) (declare (ignorable traced-fn arglist)) ,cond)))) ,(trace$-when-gcond gcond `(let ((arglist ,garglist) (traced-fn ',fn)) (declare (ignorable traced-fn arglist)) (custom-trace-ppr :in ,(if hide `(trace-hide-world-and-state ,entry) entry) ,(or gevisc-tuple evisc-tuple) ,entry-msgp))) (let* ((values ; The use of block below is critical for *1* functions, so that a return-from ; doesn't pass control all the way out and we can exit the remaining call of ; custom-trace-ppr below. It is unnecessary for user-defined ACL2 functions, ; but is presumably harmless. ; Also note that it is important that ARGLIST and TRACED-FN be bound in the ; right order. For example, if we bind ARGLIST before VALUES but ARGLIST is a ; formal, then the a reference to ARGLIST in new-body will be a reference to ; the entire arglist instead of what it should be: a reference to the formal ; parameter, ARGLIST. #+acl2-mv-as-values (multiple-value-list ,new-body) #-acl2-mv-as-values (cons ,new-body ,(cond ((eql multiplicity 1) nil) (t `(mv-refs ,(1- multiplicity)))))) ; Warning: It may be tempting to eliminate value, since it is not used below. ; But we deliberately generate a binding of value here so that users can refer ; to it in their :exit conditions (see :DOC trace$). (value ,(if (eql multiplicity 1) '(car values) 'values)) (arglist ,garglist) (traced-fn ',fn)) (declare (ignorable value values traced-fn arglist)) ,(trace$-when-gcond gcond `(custom-trace-ppr :out ,(if hide `(trace-hide-world-and-state ,exit) exit) ,(or gevisc-tuple evisc-tuple) ,exit-msgp)) #+acl2-mv-as-values (values-list values) #-acl2-mv-as-values (mv ,@(mv-nth-list 'values 0 multiplicity)))))))) #-acl2-loop-only (defun trace$-install (fn formals def trace-options predefined multiplicity ctx) ; We redefine the given function after saving the existing symbol-function. ; Note that fn can be a function defined in the ACL2 loop, or the *1* function ; of such, or a function defined directly in raw Lisp. (when (get fn 'acl2-trace-saved-fn) (er hard ctx "Implementation error: attempted to call trace$-install on a ~ function, ~x0, that already has a saved 'acl2-trace-saved-fn ~ property." fn)) (let* ((compile-tail (assoc-keyword :compile trace-options)) (compile-option (cadr compile-tail)) (do-compile (cond ((or (null compile-tail) (eq compile-option :same)) (compiled-function-p! fn)) (t compile-option)))) (setf (get fn 'acl2-trace-saved-fn) (symbol-function fn)) (eval (trace$-def formals def trace-options predefined multiplicity ctx)) #+hons (let ((memo-entry (memoizedp-raw fn))) (when memo-entry (setf (gethash fn *memoize-info-ht*) (change memoize-info-ht-entry memo-entry :memoized-fn (symbol-function fn))))) (when do-compile (compile fn)))) #-acl2-loop-only (defun oneified-def (fn wrld &optional trace-rec-for-none) (let* ((stobj-function (getprop fn 'stobj-function nil 'current-acl2-world wrld)) (form (cltl-def-from-name1 fn stobj-function t wrld))) (oneify-cltl-code (cond ((or (getprop fn 'constrainedp nil 'current-acl2-world wrld) (getprop fn 'non-executablep nil 'current-acl2-world wrld)) nil) ((eq (symbol-class fn wrld) :program) :program) ; see oneify-cltl-code (t :logic)) (cdr form) stobj-function wrld trace-rec-for-none))) (defun trace$-fn-general (trace-spec ctx state) (let* ((fn (car trace-spec)) (trace-options (cdr trace-spec)) (native (cadr (assoc-keyword :native trace-options))) (wrld (w state)) (stobj-function (and (not (assoc-keyword :def trace-options)) ; optimization (getprop fn 'stobj-function nil 'current-acl2-world wrld))) #-acl2-loop-only (*inside-trace$* t) (def (or (cadr (assoc-keyword :def trace-options)) (let ((defun+def (cltl-def-from-name1 fn stobj-function nil wrld))) (cond (defun+def (cdr defun+def)) ((and stobj-function (cltl-def-from-name1 fn stobj-function t wrld)) :macro) (t nil))) (and (getprop fn 'constrainedp nil 'current-acl2-world wrld) (let ((formals (getprop fn 'formals t 'current-acl2-world wrld))) (assert$ (not (eq formals t)) (list fn formals (null-body-er fn formals t))))))) (formals-tail (assoc-keyword :formals trace-options)) (formals-default (and (not formals-tail) (atom def) (not native) ; else formals doesn't much matter (getprop fn 'formals t 'current-acl2-world wrld))) (formals (cond (formals-tail (cadr formals-tail)) ((consp def) (cadr def)) (t formals-default))) (evisc-tuple (cadr (assoc-keyword :evisc-tuple trace-options))) (compile (cadr (assoc-keyword :compile trace-options))) (predefined ; (acl2-system-namep fn wrld) (getprop fn 'predefined nil 'current-acl2-world wrld))) (cond ((eq def :macro) (assert$ stobj-function (cond ((getprop stobj-function 'absstobj-info nil 'current-acl2-world wrld) (er very-soft ctx "~x0 cannot be traced, because it is a macro in raw Lisp, ~ introduced with the defabsstobj event for abstract stobj ~x1." fn stobj-function)) (t (er very-soft ctx "~x0 cannot be traced, because it is a macro in raw Lisp: its ~ introducing defstobj event (for stobj ~x1) was supplied with ~ :INLINE T." fn stobj-function))))) ((eq formals-default t) (er very-soft ctx "~@0 this symbol does not have an ACL2 function definition. ~ Consider using option :native, :def, or :formals. See :DOC trace$." (trace$-er-msg fn))) ((and def (not (equal (cadr def) formals))) (er very-soft ctx "~@0 the formals list, ~x1, does not match the definition's formals ~ ~x2." (trace$-er-msg fn) formals (cadr def))) ((not (symbol-listp formals)) (er very-soft ctx "~@0 the provided formals is not a true list of symbols." (trace$-er-msg fn))) ((and (keywordp evisc-tuple) (not (member-eq evisc-tuple '(:print :no-print)))) (er very-soft ctx "~@0 the only legal keyword values for option :evisc-tuple are ~ :print and :no-print." (trace$-er-msg fn))) ((member-eq fn '(wormhole-eval)) (er very-soft ctx "~@0 it is illegal (for ACL2 implementation reasons) to trace ~x1." (trace$-er-msg fn) fn)) ((and (not native) (equal (symbol-package-name fn) *main-lisp-package-name*)) (er very-soft ctx "~@0 the ACL2 trace$ utility must be used with option :native for ~ function symbols in the main Lisp package, ~x1. See :DOC trace$." (trace$-er-msg fn) *main-lisp-package-name*)) ((and compile native) (er very-soft ctx "~@0 we do not support compilation in trace specs (via keyword ~ :compile) when :native is present, as in trace spec ~x1. Consider ~ removing :compile and performing compilation separately." (trace$-er-msg fn) trace-spec)) (t (mv-let (erp val state) (chk-trace-options fn predefined trace-options formals ctx wrld state) (declare (ignore val)) (if erp (if (or (ttag wrld) (eq fn 'return-last)) (value nil) (er very-soft ctx "It is possible that you can use TRACE! to avoid the above ~ error (but consider that only with great care!). See :DOC ~ trace!.")) (let* ((state ; this handles *1* function if appropriate (maybe-untrace$-fn fn state)) (new-trace-specs (cons trace-spec (f-get-global 'trace-specs state)))) (cond ((and (not native) (null def)) (er very-soft ctx "ACL2 found no definition for ~x0. Consider supplying the ~ :def trace option. See :DOC trace$." fn)) (t (pprogn ; We update the value of 'trace-specs before modifying symbol-functions, since ; if we reverse the order then it is possible that we would interrupt between ; compilation of the raw Lisp and *1* function, after which (untrace$) would ; not untrace the raw Lisp function. Note that untrace$ is a silent no-op if ; 'trace-specs has entries that are not truly traced -- see untrace$-fn1 -- so ; better that 'trace-specs have a value that is too large than too small. (f-put-global 'trace-specs new-trace-specs state) (cond (native #-acl2-loop-only (let* ((trace-options-1 (remove-keyword :multiplicity (remove-keyword :native trace-options))) (new-trace-options ; ACL2 has redefined the underlying Lisp trace for GCL and Allegro CL so that ; they recognize the :exit keyword, and we take advantage of that here. ACL2 ; has also redefined the :exit keyword for CCL, but since CCL uses feature ; acl2-mv-as-values, there is no need to specify :exit here. #+(or gcl allegro) (let ((multiplicity (or (cadr (assoc-keyword :multiplicity trace-options)) (trace-multiplicity fn state)))) (cond ((and multiplicity (not (assoc-keyword :exit trace-options))) (append `(:exit (cons (car values) (mv-refs ,(1- multiplicity)))) trace-options-1)) (t trace-options-1))) #-(or gcl allegro) (pprogn (when (assoc-keyword :multiplicity trace-options) (let ((state *the-live-state*)) (with-output :on (warning) (warning$ ctx "Trace" "The :multiplicity option of ~ trace$ has no effect in this ~ Lisp. Only one value will be ~ passed to trace printing by ~ function ~x0." fn)))) trace-options-1))) (if new-trace-options (eval `(trace (,fn ,@new-trace-options))) (eval `(trace ,fn)))) (value trace-spec)) (t #-acl2-loop-only (let ((multiplicity (or (cadr (assoc-keyword :multiplicity trace-options)) (trace-multiplicity fn state)))) (assert$ multiplicity (trace$-install fn formals def trace-options predefined multiplicity ctx)) (when (*1*defp trace-spec wrld) (trace$-install (*1*-symbol fn) formals (oneified-def fn wrld t) trace-options predefined multiplicity ctx))) (value trace-spec))))))))))))) (defun trace$-fn-simple (trace-spec ctx state) (trace$-fn-general (list trace-spec) ctx state)) (defconst *trace-keywords* ; WARNING: If you add a keyword here, consider also adding it to ; *trace-keywords-needing-ttag*. '(:cond :entry :exit :compile :def :multiplicity :evisc-tuple :formals :hide :native :notinline)) (defconst *trace-keywords-needing-ttag* ; We omit options here that can need a trust tag, such as :entry with a term ; that does not translate, as these are managed in chk-trace-options. '(:native :def :multiplicity)) (defun all-keywords-p (keywords) (if (consp keywords) (and (keywordp (car keywords)) (all-keywords-p (cdr keywords))) (null keywords))) (defun first-assoc-keyword (keys x) (declare (xargs :guard (and (keyword-value-listp x) (all-keywords-p keys)))) (cond ((endp keys) nil) (t (or (assoc-keyword (car keys) x) (first-assoc-keyword (cdr keys) x))))) (defconst *illegal-trace-spec-fmt-string* "A trace spec must be a symbol or a symbol consed onto an alternating list ~ of the form (:kwd1 val1 :kwd2 val2 ...). The trace spec ~x0 is thus ~ illegal. See :DOC trace$.") (defun trace$-fn (trace-spec ctx state) (cond ((symbolp trace-spec) (trace$-fn-simple trace-spec ctx state)) ((and (consp trace-spec) (symbolp (car trace-spec)) (keyword-value-listp (cdr trace-spec))) (cond ((and (not (assoc-keyword :native (cdr trace-spec))) (strip-keyword-list *trace-keywords* (cdr trace-spec))) (let ((bad-keywords (evens (strip-keyword-list *trace-keywords* (cdr trace-spec))))) (er very-soft ctx "The keyword~#0~[~/s~] ~&0 ~#0~[is~/are~] illegal for ~ trace specs. See :DOC trace." bad-keywords))) ((and (not (f-get-global 'retrace-p state)) (first-assoc-keyword *trace-keywords-needing-ttag* (cdr trace-spec)) (not (ttag (w state)))) (er very-soft ctx "The keyword ~x0 cannot be used in a trace spec unless ~ there is an active trust tag. The trace spec ~x1 is ~ thus illegal. Consider using trace! instead. The ~ complete list of keywords that require a trust tag for ~ use in a trace spec is: ~x2." (car (first-assoc-keyword *trace-keywords-needing-ttag* (cdr trace-spec))) trace-spec *trace-keywords-needing-ttag*)) (t (trace$-fn-general trace-spec ctx state)))) (t (er very-soft ctx *illegal-trace-spec-fmt-string* trace-spec)))) (defun trace$-lst (trace-spec-lst ctx state) (cond ((endp trace-spec-lst) (value nil)) (t (er-let* ((tspec (trace$-fn (car trace-spec-lst) ctx state)) (tspecs (trace$-lst (cdr trace-spec-lst) ctx state))) (value (if tspec (cons tspec tspecs) tspecs)))))) (defmacro trace$ (&rest trace-specs) ":Doc-Section Trace trace function evaluations~/ ~bv[] Examples: (trace$ foo bar) ; trace foo and bar (trace$) ; return current trace info (no new tracing specified) (trace$ (foo :entry ; trace foo, printing first actual parameter upon entry (car arglist))) (trace$ (foo :exit ; trace foo, using fmt to print upon exit (:fmt (msg \"Exiting FOO with ~~x0\" value)))) (trace$ (foo :native t)) General Forms: (trace$ spec1 spec2 ... specn) ; n >= 1 (trace$) ~ev[] where the ~c[speci] are trace specs, as described below. ~c[Trace$] installs alternate code for the indicated functions that prints information upon entry to, and exit from, calls of the functions. For an alternate tracing utility used for educational purposes in ACL2s (~url[http://acl2s.ccs.neu.edu/acl2s/doc/]), see community book ~c[books/misc/trace-star.lisp]. From a logical perspective all trace printing is a fiction. (But ~pl[trace!] for a way to get around this and modify ~il[state].) For a related fiction, ~pl[cw]. ~c[(Trace$)] returns the list of currently-active trace specs, while the application of ~c[trace$] to at least one argument returns the list of its arguments that are successfully acted on. Output from ~ilc[trace$] normally goes to the screen, i.e., to ~ilc[standard-co]. But it can be redirected to a file; ~pl[open-trace-file]. ~l[untrace$] for how to undo the effect of ~ilc[trace$]. Also ~pl[trace] for mention of modifications made to raw Lisp trace, which is accessible (as described below) using the ~c[:native] keyword. Note that when ~c[trace$] is applied to a function without option ~c[:native], that function's declarations and documentation are discarded.~/ Next, we introduce tracing with some examples. After that, we provide reference documentation for individual trace options allowed in a trace spec. Note that although our example focuses on user-defined functions, ~c[trace$] can also be applied to built-in functions, though perhaps only system hackers should take advantage of this observation. We begin by illustrating the simplest sort of trace spec: a function symbol. For example, the form ~c[(trace$ foo bar)] directs the tracing of functions ~c[foo] and ~c[bar] by virtue of the two trace specs ~c[foo] and ~c[bar]. We can see tracing in action by first defining: ~bv[] (defun f (x) (cons x x)) (defun g (x) (list (f x) 3)) ~ev[] The following log then illustrates tracing of these two functions. Notice that before ~il[guard]s have been verified, the so-called ``*1*'' functions (sometimes called ``executable counterpart functions'' or ``logic functions'') are called but the corresponding raw Lisp functions are not; but after guard verification of ~c[f], the raw Lisp counterpart of ~c[f] is indeed called. (~l[guard] and ~pl[guard-evaluation-examples-log].) ~bv[] ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(g 7) 1> (ACL2_*1*_ACL2::G 7) 2> (ACL2_*1*_ACL2::F 7) <2 (ACL2_*1*_ACL2::F (7 . 7)) <1 (ACL2_*1*_ACL2::G ((7 . 7) 3)) ((7 . 7) 3) ACL2 !>(verify-guards f) Computing the guard conjecture for F.... The guard conjecture for F is trivial to prove. F is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS F) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) F ACL2 !>(g 7) 1> (ACL2_*1*_ACL2::G 7) 2> (ACL2_*1*_ACL2::F 7) 3> (F 7) <3 (F (7 . 7)) <2 (ACL2_*1*_ACL2::F (7 . 7)) <1 (ACL2_*1*_ACL2::G ((7 . 7) 3)) ((7 . 7) 3) ACL2 !> ~ev[] The following example introduces trace specs other than function symbols. Consider the following definition. ~bv[] (defun fact (n) (declare (xargs :guard (natp n))) (if (zp n) 1 (* n (fact (1- n))))) ~ev[] The following log illustrates the use of trace options ~c[:cond] (condition for entering trace), ~c[:entry] (what to print on entry), and ~c[:exit] (what to print on exit). The reason for two calls on argument 4 is that we are seeing such calls for the executable counterpart of ~c[fact] and also its raw Lisp function. ~bv[] ACL2 !>(trace$ (fact :cond (evenp (car arglist)) :entry (cons 'factorial-call arglist) :exit (car values))) ((FACT :COND (EVENP (CAR ARGLIST)) :ENTRY (CONS 'FACTORIAL-CALL ARGLIST) :EXIT (CAR VALUES))) ACL2 !>(fact 4) 1> (FACTORIAL-CALL 4) 2> (FACTORIAL-CALL 4) 3> (FACTORIAL-CALL 2) 4> (FACTORIAL-CALL 0) <4 1 <3 2 <2 24 <1 24 24 ACL2 !> ~ev[] Notice that ~c[VALUES] above is the list of all values returned, which is a one-element list unless ~ilc[mv] return is used, as illustrated in the following example, after defining: ~c[(defun two-vals (x) (mv x 7))]. ~bv[] ACL2 !>(trace$ two-vals) ((TWO-VALS)) ACL2 !>(two-vals 3) 1> (ACL2_*1*_ACL2::TWO-VALS 3) <1 (ACL2_*1*_ACL2::TWO-VALS 3 7) (3 7) ACL2 !>(verify-guards two-vals) Computing the guard conjecture for TWO-VALS.... The guard conjecture for TWO-VALS is trivial to prove, given the :executable- counterpart of CONS. TWO-VALS is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS TWO-VALS) Rules: ((:EXECUTABLE-COUNTERPART CONS)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) TWO-VALS ACL2 !>(two-vals 3) 1> (ACL2_*1*_ACL2::TWO-VALS 3) 2> (TWO-VALS 3) <2 (TWO-VALS 3 7) <1 (ACL2_*1*_ACL2::TWO-VALS 3 7) (3 7) ACL2 !> ~ev[] We now document all of the options that may appear in a trace spec. A trace spec with options is of the form ~bv[] (fn :kwd1 val1 :kwd2 val2 ... :kwdn valn) ~ev[] and here we document each legal keyword ~c[:kwdi] and corresponding expected value ~c[vali]. Note that ~c[trace$] is intended primarily for functions defined in the ACL2 command loop (~pl[lp]). If you want to trace a function that defined in raw Lisp, then you can use option ~c[:native] (see below), but then many other ~c[trace$] options will not be available to you: all of them except ~c[:multiplicity] and ~c[:native] itself will be passed directly to the ~c[trace] utility of the underlying Common Lisp. ~c[:COND], ~c[:ENTRY], and ~c[:EXIT] ~st[Introduction]. For each of these three options, the value is a (user-level) term, except that for ~c[:entry] and ~c[:exit] the value can be of the form ~c[(:fmt u)] or ~c[(:fmt! u)], where ~c[u] is a user-level term. We skip these two latter cases for now and return to them later. Then the indicated term is evaluated as indicated in the next paragraph, and if the ~c[:cond] term is omitted or evaluates to non-~c[nil], then the value of the ~c[:entry] term is printed on entry and the value of the ~c[:exit] term is printed on exit. By default, where ~c[:entry] is omitted or is specified as ~c[nil], the value printed for ~c[:entry] is the list obtained by consing the calling function symbol onto the list of actual parameters: in the notation described below, this is ~c[(cons TRACED-FN ARGLIST)]. Similarly, the default for printing at the exit of the function call, i.e. where ~c[:exit] is omitted or is specified as ~c[nil], is ~c[(cons TRACED-FN VALUES)] where ~c[VALUES] is the list of values returned as described below. In the evaluations of the term described below upon a call of ~c[fn], each formal parameter of the definition of ~c[fn] will be bound to the corresponding actual of the call, the variable ~c[ARGLIST] will be bound to the list of actuals, and the variable ~c[TRACED-FN] will be bound to the function being called (either ~c[fn] or its executable counterpart function; see above). Additionally in the case of ~c[:exit], the variable ~c[VALUES] will be bound to the multiple values returned (thus, a one-element list if ~ilc[mv] is not used in the return). Also for ~c[:exit], we bind ~c[VALUE] to the logical value returned, i.e., to the suitable list of values returned in the ~ilc[mv] case and otherwise to the single value returned. So in the ~c[mv] case, ~c[VALUE] is the same as ~c[VALUES], and otherwise ~c[VALUE] is ~c[(car VALUES)]. Other than these variables and ~ilc[STATE], no other variable may occur in the term, whose value must be a single non-~ilc[stobj] value, unless there is an active trust tag (~pl[defttag]). Now suppose ~c[fn] is called. First: If ~c[:cond] is supplied and the result of evaluating the ~c[:cond] term is ~c[nil], then no tracing is done. Otherwise tracing proceeds as follows. First the ~c[:entry] form is evaluated, and the result is printed. Then the call of ~c[fn] is evaluated. Finally the ~c[:exit] term is evaluated and the result is printed. As indicated above, the default for the ~c[:entry] term if omitted or explicitly ~c[nil] is ~c[(cons TRACED-FN ARGLIST)], and the default for the ~c[:exit] term if omitted or explicitly ~c[nil] is ~c[(cons TRACED-FN VALUES)]. Note that if the function has a formal named ~c[ARGLIST], then ~c[ARGLIST] will nevertheless refer to the entire list of formals, not the single formal named ~c[ARGLIST]; similarly for ~c[TRACED-FN], and additionally for ~c[VALUE] and ~c[VALUES] in the case of ~c[:exit]. As mentioned above, for each of ~c[:entry] and ~c[:exit], a value of ~c[nil] specifies the default behavior. If you really want a value of ~c[nil], use a non-~c[nil] form that evaluates to ~c[nil], for example ~c[(car nil)] or ~c['nil]. However, for ~c[:cond] a value of ~c[nil] means what it says: do not evaluate the ~c[:entry] or ~c[:exit] forms. Finally we discuss the case that the ~c[:entry] or ~c[:exit] term is of the form ~c[(:fmt u)] or ~c[(:fmt! u)]. In these cases, the term ~c[u] is evaluated as described above to produce a value, say ~c[msg], but instead of printing ~c[msg] directly, ACL2 calls ~c[fmt1] using the string ~c[\"~~@0\"] and the alist that binds just character ~c[#\\0] to ~c[msg]. The following example illustrates this point, where ~c[fact] is defined as above. Also ~pl[fmt]. Note that ~c[(msg string . vals)] produces a value suitable for a ~c[\"~~@\"] directive to the ~c[fmt] family of print functions. ~bv[] ACL2 !>(trace$ (fact :entry (:fmt (msg \"Tracing ~~x0 on ~~x1\" traced-fn arglist)) :exit (car values))) ((FACT :ENTRY (:FMT (MSG \"Tracing ~~x0 on ~~x1\" TRACED-FN ARGLIST)) :EXIT (CAR VALUES))) ACL2 !>(fact 3) 1> Tracing ACL2_*1*_ACL2::FACT on (3) 2> Tracing FACT on (3) 3> Tracing FACT on (2) 4> Tracing FACT on (1) 5> Tracing FACT on (0) <5 1 <4 1 <3 2 <2 6 <1 6 6 ACL2 !> ~ev[] If ~c[:fmt!] is used instead of ~c[:fmt], then indentation as is the prefix string, ~c[\"n> \"] or ~c[\"(trace$ (fact :entry (:fmt! (msg \"Tracing ~~x0 on ~~x1\" traced-fn arglist)) :exit (:fmt! (msg \"From input ~~x0: ~~x1\" (car arglist) (car values))))) ((FACT :ENTRY (:FMT! (MSG \"Tracing ~~x0 on ~~x1\" TRACED-FN ARGLIST)) :EXIT (:FMT! (MSG \"From input ~~x0: ~~x1\" (CAR ARGLIST) (CAR VALUES))))) ACL2 !>(fact 3) Tracing ACL2_*1*_ACL2::FACT on (3) Tracing FACT on (3) Tracing FACT on (2) Tracing FACT on (1) Tracing FACT on (0) From input 0: 1 From input 1: 1 From input 2: 2 From input 3: 6 From input 3: 6 6 ACL2 !> ~ev[] Here is the same example, with user-managed indentation. ~bv[] ACL2 !>(trace$ (fact :entry (:fmt! (msg \"~~t0Tracing ~~x1 on ~~x2\" (+ 3 (* 2 (@ trace-level))) traced-fn arglist)) :exit (:fmt! (msg \"~~t0From input ~~x1: ~~x2\" (1+ (* 2 (@ trace-level))) (car arglist) (car values))))) ((FACT :ENTRY (:FMT! (MSG \"~~t0Tracing ~~x1 on ~~x2\" (+ 3 (* 2 (@ TRACE-LEVEL))) TRACED-FN ARGLIST)) :EXIT (:FMT! (MSG \"~~t0From input ~~x1: ~~x2\" (1+ (* 2 (@ TRACE-LEVEL))) (CAR ARGLIST) (CAR VALUES))))) ACL2 !>(fact 3) Tracing ACL2_*1*_ACL2::FACT on (3) Tracing FACT on (3) Tracing FACT on (2) Tracing FACT on (1) Tracing FACT on (0) From input 0: 1 From input 1: 1 From input 2: 2 From input 3: 6 From input 3: 6 6 ACL2 !> ~ev[] ~st[ADVANCED OPTIONS] (alphabetical list) ~c[:COMPILE] The tracing of ~c[fn] installs a substitute definition of ~c[fn] that prints trace information. If the ~c[:compile] option is omitted or has value ~c[:same], then the new definition will be compiled if and only if the existing definition is already compiled. Otherwise, the new definition will be compiled exactly when the value of ~c[:compile] is not ~c[nil]. ~c[:DEF], ~c[:MULTIPLICITY] ACL2's ~c[trace$] mechanism often needs to know the number of outputs of a traced function, in the sense of ~ilc[mv]. If you trace a function that was not defined inside the ACL2 loop (hence you are using the ~c[:native] option), or if you provide an alternative definition using option ~c[:def] (see below) and the new definition changes the number of values returned, then a natural number value for ~c[:multiplicity] informs the trace utility of the number of expected outputs of the function being traced. In the case that ~c[:native] is supplied, the effect of a non-~c[nil] ~c[:multiplicity] value depends on the host Lisp. In the case of Lisps for which ACL2 uses the built-in Lisp mechanism for returning multiple values (~pl[mv]), which are CCL and threaded SBCL as of June, 2010, ~c[:multiplicity] is not needed and is ignored with ~c[:native t]. For GCL and Allegro CL, ~c[:multiplicity] is used to generate a suitable ~c[:exit] form if the ~c[:exit] keyword was not already supplied. For the other Lisps, the ~c[:multiplicity] value is treated essentially as 1 whether it is supplied or not, because we do not know how to pass suitable information based on this value to the host Lisp's built-in tracing mechanism. Note that even supplying a ~c[:multiplicity] option does not change the meaning of the variable ~c[values]. See the discussion of ~c[:native] below. A useful option can be to supply a definition as the value of ~c[:def]. (Again, note that if ~c[:native] is used, then all options other than ~c[:multiplicity] are passed directly to the underlying Lisp; in particular, ~c[:def] will have no effect with ~c[:native] except in the unlikely case that the raw Lisp provides some sort of support for ~c[:def].) Note that this definition should be like a ~ilc[defun] form, but without the leading ~c[defun] symbol; and it should define the function symbol being traced, with the same formal parameter list. However, tracing of the so-called ``executable counterpart'' of a function (sometimes referred to as the ``*1* function'', for evaluation in the ACL2 loop; ~pl[guards] for related discussion) is not sensitive to the ~c[:def] option; rather, if a function has an executable counterpart then that executable counterpart is traced. ~c[:EVISC-TUPLE] The printing described above is, by default, done using the current default trace evisc-tuple, which can be set using ~ilc[set-trace-evisc-tuple] (for the shape of this tuple, ~pl[evisc-tuple]); ~pl[set-trace-evisc-tuple]. This tuple is based by default on the raw Lisp variables ~c[*print-level*] and ~c[*print-length*], and will hide the ACL2 ~c[world] and handle ~il[stobj]s appropriately. You may override this default by supplying an evisc tuple with the ~c[:evisc-tuple] argument in your trace spec. Be careful to supply a valid evisc-tuple, or you may get a raw Lisp error! A special value, ~c[:print], is useful if you are doing system hacking that can produce objects that are not valid ACL2 objects, such as raw Lisp arrays or objects in supporting packages not visible in the ACL2 read-eval-print loop. If you supply ~c[:evisc-tuple :print], then the printing described above will be done with raw Lisp printing rather than ACL2 printing: specifically, with ~c[(format *trace-output* \"~s~%\" x)], where ~c[x] is the value to be printed. A second special value for ~c[:evisc-tuple], ~c[:no-print], avoids printing the values of the ~c[:entry] and ~c[:exit] forms (or their defaults, if not specified). This option is of use for side effects; for an example see community book ~c[books/misc/wet.lisp]. Note that if ~c[:evisc-tuple X] is supplied, then the form ~c[X] will be evaluated before the function body is entered. You can thus pull some tricks to print extra information before the ~c[:entry] form is evaluated, for example as follows for a factorial function, ~c[fact]. ~bv[] ACL2 !>(trace$ (fact :evisc-tuple (prog2$ (cw \"~~|**** HERE IS CW ****~~|\") nil))) ((FACT :EVISC-TUPLE (PROG2$ (CW \"~~|**** HERE IS CW ****~~|\") NIL))) ACL2 !>(fact 3) **** HERE IS CW **** 1> (ACL2_*1*_ACL2::FACT 3) **** HERE IS CW **** 2> (ACL2_*1*_ACL2::FACT 2) **** HERE IS CW **** 3> (ACL2_*1*_ACL2::FACT 1) **** HERE IS CW **** 4> (ACL2_*1*_ACL2::FACT 0) <4 (ACL2_*1*_ACL2::FACT 1) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 2) <1 (ACL2_*1*_ACL2::FACT 6) 6 ACL2 !> ~ev[] ~c[:FORMALS] Normally ACL2 can figure out the formals for a given function. This is always the case for functions defined in the ACL2 command loop and when option ~c[:def] is supplied. If neither of these cases applies then you can still trace a function (even without using the ~c[:native] option) by supplying option ~c[:notinline :fncall], but you will still need to supply the list of formal parameters. The value of the ~c[:formals] option should be the list of formals in this case. ~c[:HIDE] The default value for this advanced option is ~c[t], which causes ~il[stobj]s and the logical ~il[world] to be printed as single symbols, along with certain large structures of interest to developers (rewrite contants, enabled structures, and event and command index structures). If however the value ~c[nil] is supplied, then this default behavior is defeated. In that case, you can still arrange to print the logical world as a symbol and to print ~il[stobj]s without breaking the trace printing: ~pl[set-trace-evisc-tuple] for how to do this globally, or similarly use the ~c[:evisc-tuple] option to ~c[trace$] to do this with a single trace spec. Note however that with value ~c[nil] specified for ~c[:hide], such use of an evisc-tuple will not deal properly with local stobjs (~pl[with-local-stobj]) or stobjs bound by ~ilc[stobj-let], or with the aforementioned large structures other than the logical ~il[world]. ~c[:NATIVE] If ~c[:native] is supplied with a non-~c[nil] value, then the trace spec is passed to the native Lisp trace (after removing the ~c[:native] option). A trust tag (~pl[defttag]) is required in order to use this option, because no syntactic check is made on the ~c[:cond], ~c[:entry], or ~c[:exit] forms -- arbitrary raw Lisp may occur in them! Note that by ``native Lisp trace'' we mean the currently installed ~c[trace]. As discussed briefly elsewhere (~pl[trace]), ACL2 has modified that trace to be more useful if the underlying host Lisp is GCL, Allegro CL, or CCL (OpenMCL). If you need the original trace utility supplied for those Lisps, quit the ACL2 loop with ~c[:q] and call ~c[old-trace] and ~c[old-untrace] in raw Lisp where you would otherwise call ~c[trace] and ~c[untrace]. Note that the original trace utility supplied with a given Lisp will not hide the ACL2 logical ~il[world] or give special treatment to ~il[stobj]s. It is important to understand that if ~c[:native t] is specified, then all other options are interpreted by the native Lisp trace. For example, that trace probably has no understanding of the use of ~c[:fmt] described above for ~c[:entry] or ~c[:exit]. Indeed, the native trace may not even accept any of ~c[:cond], ~c[:entry] or ~c[:exit], let alone any of the advanced options! Moreover, if ~c[:native t] is specified, then even a ~c[:multiplicity] option does not provide the meaning of the variable ~c[values] that one might desire. In GCL for example, in the case of an ~ilc[mv] return of a function defined only in raw Lisp (not in ACL2), this variable will be bound to a list containing only the first result. ~c[:NOTINLINE] By default, a new definition installed by ~c[trace$] will include a ~c[notinline] declaration so that recursive calls will always be traced. To avoid this declaration, supply value ~c[nil]. A special value for ~c[:notinline], ~c[:fncall], will cause the traced function to call its original definition. Without this special value, the new installed definition for the traced function will include the body of the original definition. This ~c[:fncall] behavior is the default only in the following cases: ~bq[] o for functions whose definitions are built into ACL2; o for functions that have been added (using a trust tag, an advanced feature, so most users can probably ignore this case) to either of the ~ilc[state] global variables ~c[program-fns-with-raw-code] or ~c[logic-fns-with-raw-code]; o (`HONS' extension only; ~pl[hons-and-memoization]) for ~ilc[memoize]d functions.~eq[] The legal values for ~c[:notinline] are ~c[t] (the default for other than the cases displayed above), ~c[nil], and ~c[:fncall]. (Except: For the 'HONS' extension, only ~c[:fncall] is legal.) ~st[Remarks]. (1) If some of the given trace specs have errors, then ~c[trace$] will generally print error messages for those but will still process those that do not have errors. The value returned will indicate the trace specs that were processed successfully. (2) If you certify or include a book that redundantly defines a function that is currently traced, then tracing behavior may disappear if a compiled definition is installed for the function or its in-the-logic (so-called `*1*') counterpart. (3) Some predefined functions are called during tracing. In order to avoid infinite loops, such calls of traced predefined functions will be made using the original predefined functions, not using their code installed by ~c[trace$].~/" (cond ((null trace-specs) '(value (f-get-global 'trace-specs state))) (t `(pprogn (if (equal (f-get-global 'trace-co state) *standard-co*) state (fms "**NOTE**: Trace output will continue to go to a file.~|~%" nil *standard-co* state nil)) (if (eql 0 (f-get-global 'ld-level state)) (ld '((trace$-lst ',trace-specs 'trace$ state)) :ld-verbose nil) (trace$-lst ',trace-specs 'trace$ state)))))) (defmacro with-ubt! (form) (let ((label 'with-ubt!-label)) `(er-progn (with-output :stack :push :off :all (ld '((deflabel ,label) (with-output :stack :pop ,form) (state-global-let* ((inhibit-output-lst ; eliminate output from :ubt! (add-to-set-eq 'temporary (f-get-global 'inhibit-output-lst state)))) (ubt! ',label))) :ld-verbose nil :ld-prompt nil :ld-post-eval-print nil :ld-error-action :error)) (value :invisible)))) (defmacro trace! (&rest fns) ":Doc-Section Trace trace the indicated functions after creating an active trust tag~/ ~bv[] Example: (trace! (fact :native t :entry *foo*))~/ General Form: (trace! spec1 ... specn) ~ev[] where the ~c[fni] are suitable arguments to ~ilc[trace$]. ~c[Trace!] is a version of ~ilc[trace$] that avoids the need for an already-active trust tag (or ``ttag''; ~pl[defttag]), as explained below. ~l[trace$] for when a trust tag can be necessary. ~l[untrace$] for how to undo the effect of ~ilc[trace!]. The evaluation of a ~c[trace!] form causes temporary creation of an active trust tag, ~c[:trace!], followed by the corresponding ~c[trace$] form. The trust tag will disappear when the call to ~c[trace!] completes. Even though ~c[trace!] will remove its temporary ttag, it will still print a ``~c[TTAG NOTE]'', which indicates that the session is suspect. ~l[defttag] and ~pl[ttags-seen] for further remarks on this issue. Because of the active trust tag, it is possible to do things with ~c[trace!] that are useful but without logical justification. Below is an example of how to use ~c[trace!] to cause a function call to change ~il[state], even though the function does not take ~ilc[state] as a parameter. ~bv[] ACL2 !>(defun fact (n) (declare (xargs :guard (natp n) :verify-guards nil)) (if (zp n) 1 (* n (fact (1- n))))) The admission of FACT is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT N). We observe that the type of FACT is described by the theorem (AND (INTEGERP (FACT N)) (< 0 (FACT N))). We used the :compound- recognizer rule ZP-COMPOUND-RECOGNIZER and primitive type reasoning. Summary Form: ( DEFUN FACT ...) Rules: ((:COMPOUND-RECOGNIZER ZP-COMPOUND-RECOGNIZER) (:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 !>(defun update-foo (n value state) (declare (xargs :stobjs state :verify-guards nil)) (assign foo (cons (cons n value) (@ foo)))) Since UPDATE-FOO is non-recursive, its admission is trivial. We observe that the type of UPDATE-FOO is described by the theorem (AND (CONSP (UPDATE-FOO N VALUE STATE)) (TRUE-LISTP (UPDATE-FOO N VALUE STATE))). We used primitive type reasoning. (UPDATE-FOO * * STATE) => (MV * * STATE). Summary Form: ( DEFUN UPDATE-FOO ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) UPDATE-FOO ACL2 !>(trace! (fact :exit (update-foo n value state))) TTAG NOTE: Adding ttag :TRACE! from the top level loop. ((FACT :EXIT (UPDATE-FOO N VALUE STATE))) ACL2 !>(assign foo nil) NIL ACL2 !>(fact 7) 1> (ACL2_*1*_ACL2::FACT 7) 2> (ACL2_*1*_ACL2::FACT 6) 3> (ACL2_*1*_ACL2::FACT 5) 4> (ACL2_*1*_ACL2::FACT 4) 5> (ACL2_*1*_ACL2::FACT 3) 6> (ACL2_*1*_ACL2::FACT 2) 7> (ACL2_*1*_ACL2::FACT 1) 8> (ACL2_*1*_ACL2::FACT 0) <8 NIL <7 NIL <6 NIL <5 NIL <4 NIL <3 NIL <2 NIL <1 NIL 5040 ACL2 !>(@ foo) ((7 . 5040) (6 . 720) (5 . 120) (4 . 24) (3 . 6) (2 . 2) (1 . 1) (0 . 1)) ACL2 !>(verify-guards fact) Computing the guard conjecture for FACT.... The guard conjecture for FACT is trivial to prove, given the :compound- recognizer rules NATP-COMPOUND-RECOGNIZER and ZP-COMPOUND-RECOGNIZER, primitive type reasoning and the :type-prescription rule FACT. FACT is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS FACT) Rules: ((:COMPOUND-RECOGNIZER NATP-COMPOUND-RECOGNIZER) (:COMPOUND-RECOGNIZER ZP-COMPOUND-RECOGNIZER) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:TYPE-PRESCRIPTION FACT)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 !>(assign foo nil) NIL ACL2 !>(fact 7) 1> (ACL2_*1*_ACL2::FACT 7) 2> (FACT 7) 3> (FACT 6) 4> (FACT 5) 5> (FACT 4) 6> (FACT 3) 7> (FACT 2) 8> (FACT 1) 9> (FACT 0) <9 NIL <8 NIL <7 NIL <6 NIL <5 NIL <4 NIL <3 NIL <2 NIL <1 NIL 5040 ACL2 !>(@ foo) ((7 . 5040) (7 . 5040) (6 . 720) (5 . 120) (4 . 24) (3 . 6) (2 . 2) (1 . 1) (0 . 1)) ACL2 !>(trace! (fact :exit (progn (update-foo n value state) (cons traced-fn values)))) TTAG NOTE: Adding ttag :TRACE! from the top level loop. ((FACT :EXIT (PROGN (UPDATE-FOO N VALUE STATE) (CONS TRACED-FN VALUES)))) ACL2 !>(assign foo nil) NIL ACL2 !>(fact 7) 1> (ACL2_*1*_ACL2::FACT 7) 2> (FACT 7) 3> (FACT 6) 4> (FACT 5) 5> (FACT 4) 6> (FACT 3) 7> (FACT 2) 8> (FACT 1) 9> (FACT 0) <9 (FACT 1) <8 (FACT 1) <7 (FACT 2) <6 (FACT 6) <5 (FACT 24) <4 (FACT 120) <3 (FACT 720) <2 (FACT 5040) <1 (ACL2_*1*_ACL2::FACT 5040) 5040 ACL2 !>(@ foo) ((7 . 5040) (7 . 5040) (6 . 720) (5 . 120) (4 . 24) (3 . 6) (2 . 2) (1 . 1) (0 . 1)) ACL2 !> ~ev[] Finally, we remark that the use ~c[trace!] can cause errors in situations where tracing is automatically suspended and re-introduced. This is likely to be a rare occurrence, but consider the following example. ~bv[] (trace! (lexorder :native t :multiplicity 1)) (certify-book \"foo\" 0 t) ~ev[] If the certify-book causes compilation, you may see an error such as the following. ~bv[] ACL2 Error in (CERTIFY-BOOK \"foo\" ...): The keyword :NATIVE cannot be used in a trace spec unless there is an active trust tag. The trace spec (LEXORDER :NATIVE T :MULTIPLICITY 1) is thus illegal. Consider using trace! instead. The complete list of keywords that require a trust tag for use in a trace spec is: (:NATIVE :DEF :MULTIPLICITY). ~ev[] This error is harmless. The function will appear, when calling ~c[(trace$)], to remain traced, but in fact there will be no tracing behavior, so you may want to call ~ilc[untrace$] on the function symbol in question. ~/" (let ((form `(with-ubt! (with-output :off :all (with-output :on (error warning warning!) (make-event (progn (defttag :trace!) (progn! (er-let* ((specs (trace$ ,@fns))) (value (list 'value-triple (kwote specs)))))))))))) #-acl2-loop-only ; We use ld so that this can work in raw Lisp. We allow a different ; macroexpansion in raw Lisp because we know that no function will ever call ; this macro, since with-output is prohibited inside code. With this raw Lisp ; code, one can call trace! in raw Lisp, which is handy for example when ; calling break-on-error. Of course, no trust tag note will be printed in raw ; Lisp -- but all bets are off anyhow in raw Lisp! `(ld '(,form)) #+acl2-loop-only form)) (defmacro untrace$ (&rest fns) ":Doc-Section Trace untrace functions~/ ~bv[] Examples: (untrace$) ; untrace all functions previously ; traced (e.g. with trace$ or trace!) (untrace$ foo bar) ; as above, except only untrace foo and bar~/ General Forms: (untrace$) ; untrace all (as noted above) (untrace$ fn1 fn2 ... fnk) ; untrace the indicated functions ~ev[] where the ~c[fni] were previously traced (e.g. with ~ilc[trace$] or ~ilc[trace!]). ~c[Untrace$] undoes the effect of ~ilc[trace$]. ~l[trace$]. The value returned by ~c[untrace$] gives the list of functions for which tracing is being removed.~/" `(untrace$-fn ',fns state)) (defun open-trace-file-fn (filename state) ; Logically, this function opens a channel to the given file. But there is no ; logical accounting for subsequent writes to that channel on behalf of ; tracing. We view those subsequent writes as being to the file, but not the ; channel, in analogy to how cw prints to the screen but does not modify the ; contents of *standard-co*. (mv-let (chan state) (open-output-channel filename :character state) (cond (chan #-acl2-loop-only (setq *trace-output* (get-output-stream-from-channel chan)) (pprogn (if (equal (f-get-global 'trace-co state) *standard-co*) state (close-output-channel (f-get-global 'trace-co state) state)) (f-put-global 'trace-co chan state))) (t (prog2$ (er hard 'open-trace-file "Unable to open file ~s0 for trace output." filename) state))))) (defmacro open-trace-file (filename) ":Doc-Section Trace redirect trace output to a file~/ ~bv[] Example: (open-trace-file \"foo\") ; trace output will go to file foo~/ General Form: (open-trace-file filename) ; trace output will go to file filename ~ev[] Output from ~ilc[trace$] normally goes to the screen, i.e., ~ilc[standard-co]. But it can be redirected to a file as shown above. ~l[close-trace-file] for how to send trace output back to the screen.~/" (declare (xargs :guard (stringp filename))) `(pprogn (close-trace-file-fn t state) (open-trace-file-fn ,filename state))) (defun close-trace-file-fn (quiet-p state) #-acl2-loop-only (setq *trace-output* (get-output-stream-from-channel *standard-co*)) (if (equal (f-get-global 'trace-co state) *standard-co*) (if quiet-p state (prog2$ (er hard 'close-trace-file "No change: trace is already written to standard output.~%") state)) (pprogn (close-output-channel (f-get-global 'trace-co state) state) (f-put-global 'trace-co *standard-co* state)))) (defmacro close-trace-file () ":Doc-Section Trace stop redirecting trace output to a file~/ ~bv[] General Form: (close-trace-file) ; trace output is no longer redirected to a file~/ ~ev[] Output from ~ilc[trace$] normally goes to the screen, or more precisely, ~ilc[standard-co]. It can be redirected to a file; ~pl[open-trace-file]. Use ~c[close-trace-file] to redirect trace output to ~ilc[standard-co].~/" '(close-trace-file-fn nil state)) (defmacro break-on-error (&optional (on 't)) ; It should suffice to trace error1 and not illegal, since raw-ev-fncall turns ; hard errors into soft errors (mv t msg latches), which in turn cause calls of ; error1 in trans-eval. We trace on exit so that the error message will be ; printed. ":Doc-Section Trace break when encountering a hard or soft error caused by ACL2~/ ~bv[] General forms: (break-on-error t) ; installs a trace causing a continuable error (break) ; when an error is invoked by ACL2. (break-on-error) ; same as above (break-on-error :all) ; same as above, but even when inside the prover (break-on-error nil) ; uninstall any above trace ~ev[] ~c[(Break-on-error)] generates a suitable trace of error functions. Evaluate ~c[(trace$)] after ~c[(break-on-error)] if you want to see the specific trace forms (which you can modify and then submit directly to ~c[trace$], if you wish). This ~il[trace] should cause entry to the Lisp debugger whenever ACL2 calls its error routines, except for certain errors when inside the theorem prover, and also at those times if option :all is supplied. NOTE: For technical reasons, you may see some error messages more than once. Finally, note that you are welcome to define your own version of ~c[break-on-error] by modifying a copy of the source definition (search for ``~c[(defmacro break-on-error]'' in ACL2 source file other-events.lisp). Please feel free to send your version of ~c[break-on-error] to the ACL2 implementors, for possible inclusion into ACL2. ~c[Break-on-error] is implmented using ACL2 ~ilc[trace$]. ~l[trace!] if you want an explanation of the ``~c[TTAG NOTE]'' that is printed. The argument, if supplied, is evaluated and must evaluate to ~c[t], ~c[nil], or ~c[:all]. Also ~pl[set-debugger-enable] for how to get raw-Lisp backtrace information when an error occurs as a result of ~c[break-on-error], or even of a raw Lisp error, by calling ~c[set-debugger-enable] with argument ~c[:bt], ~c[:bt-break], or ~c[:break-bt]. Note that for ACL2 errors (as opposed to raw Lisp errors), i.e. errors affected by ~c[break-on-error], all three of those keyword values are treated equivalently (and, all are ignored for non-ANSI GCL; ~pl[set-debugger-enable]).~/~/" (let* ((error1-trace-form '(error1 :entry (:fmt (msg "[Breaking on error:]")) :exit (prog2$ (maybe-print-call-history state) (break$)) :compile nil)) (er-cmp-fn-trace-form '(er-cmp-fn :entry ; body of error1, to avoid second break on error1 (pprogn (io? error nil state (ctx msg) (error-fms nil ctx "~|[Breaking on cmp error:]~|~@0" (list (cons #\0 msg)) state)) (mv :enter-break nil state)) :exit (prog2$ (maybe-print-call-history state) (break$)) :compile nil)) (throw-raw-ev-fncall-string "[Breaking on error (entry to ev-fncall-msg)]") (throw-raw-ev-fncall-trace-form `(throw-raw-ev-fncall :def (throw-raw-ev-fncall (val) (throw 'raw-ev-fncall val)) :multiplicity 1 :entry (progn (fmt-abbrev "~%ACL2 Error ~@0: ~@1" (list (cons #\0 ,throw-raw-ev-fncall-string) (cons #\1 (ev-fncall-msg (car arglist) (w *the-live-state*) (user-stobj-alist *the-live-state*)))) 0 *standard-co* *the-live-state* "~|~%") (maybe-print-call-history *the-live-state*) (break$))))) `(let ((on ,on)) (er-progn (case on (:all (trace! ,error1-trace-form ,er-cmp-fn-trace-form ,throw-raw-ev-fncall-trace-form)) ((t) (trace! ,error1-trace-form ,er-cmp-fn-trace-form (,@throw-raw-ev-fncall-trace-form :cond (not (f-get-global 'in-prove-flg *the-live-state*))))) ((nil) (with-output :off warning (untrace$ error1 er-cmp-fn throw-raw-ev-fncall))) (otherwise (er soft 'break-on-error "Illegal argument value for break-on-error: ~x0." on))) (value :invisible))))) (defun defexec-extract-key (x keyword result result-p) ; X is a keyword-value-listp from an xargs declaration, and result-p indicates ; whether we expect to see no further value of the indicated keyword (in which ; case we should return result and result-p unchanged if erp, below, is nil). ; We return (mv erp result result-p), where if erp is nil, result-p is nil ; coming in, and x contains (keyword result), then we return (mv nil result t). (declare (xargs :guard (and (keywordp keyword) (keyword-value-listp x)))) (cond ((endp x) (mv nil result result-p)) (t (mv-let (erp result result-p) (defexec-extract-key (cddr x) keyword result result-p) (cond (erp (mv erp nil nil)) ((eq (car x) keyword) (cond (result-p (mv "more than one ~x0 has been specified" nil nil)) (t (mv nil (cadr x) t)))) (t (mv nil result result-p))))))) (defun parse-defexec-dcls-1 (alist guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default acc) ; We return (mv nil declare-form ...) as suggested in the first (endp) case ; below, where exec-xargs has been removed from alist in creating the declare ; form (the second returned value). (declare (xargs :guard (symbol-alistp alist))) (cond ((endp alist) (mv nil (cons 'declare (reverse acc)) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default)) (t (let* ((decl (car alist)) (sym (car decl)) (x (cdr decl))) (cond ((eq sym 'xargs) (cond ((keyword-value-listp x) (mv-let (erp guard guard-p) (defexec-extract-key x :GUARD guard guard-p) (cond (erp (mv erp nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) (t (mv-let (erp hints hints-p) (defexec-extract-key x :HINTS hints hints-p) (cond (erp (mv erp nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) (t (mv-let (erp measure measure-p) (defexec-extract-key x :MEASURE measure measure-p) (cond (erp (mv erp nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) (t (mv-let (erp ruler-extenders ruler-extenders-p) (defexec-extract-key x :RULER-EXTENDERS ruler-extenders ruler-extenders-p) (cond (erp (mv erp nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) (t (mv-let (erp wfrel wfrel-p) (defexec-extract-key x :WELL-FOUNDED-RELATION wfrel wfrel-p) (cond (erp (mv erp nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) (t (mv-let (erp stobjs stobjs-p) (defexec-extract-key x :STOBJS stobjs stobjs-p) (cond (erp (mv erp nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) (t (parse-defexec-dcls-1 (cdr alist) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default (cons decl acc))))))))))))))))))))) (t (mv "we found (XARGS . x) where x is not a keyword-value-listp" nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)))) ((eq sym 'exec-xargs) (cond ((or exec-xargs exec-test exec-default) (mv "more than one EXEC-XARGS has been specified" nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)) ((and (keyword-value-listp x) x) (let* ((exec-test (cadr (assoc-keyword :test x))) (x (if exec-test (remove-keyword :test x) x)) (exec-default (cadr (assoc-keyword :default-value x))) (x (if exec-default (remove-keyword :default-value x) x))) (parse-defexec-dcls-1 (cdr alist) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p x exec-test exec-default acc))) (t (mv "we found declaration (EXEC-XARGS . x) where x is not a ~ non-empty keyword-value-listp" nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil)))) (t (parse-defexec-dcls-1 (cdr alist) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p x exec-test exec-default (cons (car alist) acc)))))))) (defun fix-exec-xargs (exec-xargs hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p) (declare (xargs :guard (keyword-value-listp exec-xargs))) ; Update exec-xargs to incorporate the hints, measure, and stobjs extracted ; from the xargs (if any). (let* ((x (if (and hints-p (not (assoc-keyword :HINTS exec-xargs))) (list* :HINTS hints exec-xargs) exec-xargs)) (x (if (and measure-p (not (assoc-keyword :MEASURE exec-xargs))) (list* :MEASURE measure x) x)) (x (if (and ruler-extenders-p (not (assoc-keyword :RULER-EXTENDERS exec-xargs))) (list* :RULER-EXTENDERS ruler-extenders x) x)) (x (if (and wfrel-p (not (assoc-keyword :WELL-FOUNDED-RELATION exec-xargs))) (list* :WELL-FOUNDED-RELATION wfrel x) x)) (x (if (and stobjs-p (not (assoc-keyword :STOBJS exec-xargs))) (list* :STOBJS stobjs x) x))) x)) (defun parse-defexec-dcls (dcls-and-strings final guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default) ; We return the following values. Note that input guard-p is true if we have ; encountered a guard on an earlier call. ; erp - nil or a string that indicates an error ; final - what is left of dcls-and-strings after (exec-xargs ...) is ; removed ; guard - the guard from (xargs ... :guard ...) ; exec-xargs - the cdr of (exec-xargs ...) from input ; exec-test - from (exec-xargs ... :test ...) if present, else guard ; exec-default - from (exec-xargs ... :default-value ...), else nil (cond ((endp dcls-and-strings) (cond ((null guard-p) (mv "no :GUARD has been specified in the XARGS. The MBE proof ~ obligation is actually a guard condition -- we have to prove that ~ the guard ensures that the :LOGIC and :EXEC terms are equivalent ~ and that the guards are satisfied for the :EXEC term. Please ~ specify a :GUARD. Note also that you can delay the verification ~ of the MBE conditions by delaying guard verification, as with ~ :VERIFY-GUARDS NIL" nil nil nil nil nil)) (t (mv nil (reverse final) guard (fix-exec-xargs exec-xargs hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p) (or exec-test guard) exec-default)))) (t (let ((x (car dcls-and-strings))) (cond ((stringp x) (parse-defexec-dcls (cdr dcls-and-strings) (cons x final) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default)) ((and (consp x) (eq (car x) 'declare) (symbol-alistp (cdr x))) (mv-let (erp decl guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default) (parse-defexec-dcls-1 (cdr x) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default nil) (cond (erp (mv erp nil nil nil nil nil)) (t (parse-defexec-dcls (cdr dcls-and-strings) (cons decl final) guard guard-p hints hints-p measure measure-p ruler-extenders ruler-extenders-p wfrel wfrel-p stobjs stobjs-p exec-xargs exec-test exec-default))))) (t (mv (msg "the form ~x0 is neither a string nor a form (declare . x) ~ where x is a symbol-alistp" x) nil nil nil nil nil))))))) (defmacro defexec (&whole whole fn formals &rest rest) ":Doc-Section Events attach a terminating executable function to a definition~/ Suppose you define a function ~c[(fn x)] with a ~il[guard] of ~c[(good-input-p x)], and you know that when the guard holds, the measure decreases on each recursive call. Unfortunately, the definitional principle (~pl[defun]) ignores the guard. For example, if the definition has the form ~bv[] (defun fn (x) (declare (xargs :guard (good-input-p x))) (if (not-done-yet x) (... (fn (destr x)) ...) ...)) ~ev[] then in order to admit this definition, ACL2 must prove the appropriate formula asserting that ~c[(destr x)] is ``smaller than'' ~c[x] under the assumption ~c[(not-done-yet x)] but without the assumption ~c[(good-input-p x)], even if ~c[(not-done-yet x)] is true. In essence, it may be necessary to submit instead the following definition. ~bv[] (defun fn (x) (declare (xargs :guard (good-input-p x))) (if (good-input-p x) (if (not-done-yet x) (... (fn (destr x)) ...) ...) nil) ~ev[] But it is unfortunate that when calls of ~c[fn] are evaluated, for example when ~c[fn] is applied to an explicit constant during a proof, then a call of ~c[good-input-p] must now be evaluated on each recursive call. Fortunately, ~c[defexec] provides a way to keep the execution efficient. For the example above we could use the following form. ~bv[] (defexec fn (x) (declare (xargs :guard (good-input-p x))) (mbe :logic (if (good-input-p x) (if (not-done-yet x) (... (fn (destr x)) ...) ...) nil) :exec (if (not-done-yet x) (... (fn (destr x)) ...) ...))) ~ev[] Here ``~ilc[mbe]'' stands for ``must be equal'' and, roughly speaking, its call above is logically equal to the ~c[:logic] form but is evaluated using the ~c[:exec] form when the guard holds. ~l[mbe]. The effect is thus to define ~c[fn] as shown in the ~ilc[defun] form above, but to cause execution of ~c[fn] using the ~c[:exec] body. The use of ~c[defexec] instead of ~ilc[defun] in the example above causes a termination proof to be performed, in order to guarantee that evaluation always theoretically terminates, even when using the ~c[:exec] form for evaluation. ~bv[] Example: ; Some of the keyword arguments in the declarations below are irrelevant or ; unnecessary, but they serve to illustrate their use. (defexec f (x) (declare (xargs :measure (+ 15 (acl2-count x)) :ruler-extenders :basic :hints ((\"Goal\" :in-theory (disable nth))) :guard-hints ((\"Goal\" :in-theory (disable last))) :guard (and (integerp x) (<= 0 x) (< x 25))) (exec-xargs :test (and (integerp x) (<= 0 x)) :default-value 'undef ; defaults to nil :measure (nfix x) :ruler-extenders :basic :well-founded-relation o<)) (mbe :logic (if (zp x) 1 (* x (f (- x 1)))) :exec (if (= x 0) 1 (* x (f (- x 1)))))) ~ev[] The above example macroexpands to the following. ~bv[] (ENCAPSULATE () (LOCAL (ENCAPSULATE () (SET-IGNORE-OK T) (SET-IRRELEVANT-FORMALS-OK T) (LOCAL (DEFUN F (X) (DECLARE (XARGS :VERIFY-GUARDS NIL :HINTS ((\"Goal\" :IN-THEORY (DISABLE NTH))) :MEASURE (NFIX X) :RULER-EXTENDERS :BASIC :WELL-FOUNDED-RELATION O<)) (IF (AND (INTEGERP X) (<= 0 X)) (IF (= X 0) 1 (* X (F (- X 1)))) 'UNDEF))) (LOCAL (DEFTHM F-GUARD-IMPLIES-TEST (IMPLIES (AND (INTEGERP X) (<= 0 X) (< X 25)) (AND (INTEGERP X) (<= 0 X))) :RULE-CLASSES NIL)))) (DEFUN F (X) (DECLARE (XARGS :MEASURE (+ 15 (ACL2-COUNT X)) :RULER-EXTENDERS :BASIC :HINTS ((\"Goal\" :IN-THEORY (DISABLE NTH))) :GUARD-HINTS ((\"Goal\" :IN-THEORY (DISABLE LAST))) :GUARD (AND (INTEGERP X) (<= 0 X) (< X 25)))) (MBE :LOGIC (IF (ZP X) 1 (* X (F (- X 1)))) :EXEC (IF (= X 0) 1 (* X (F (- X 1))))))) ~ev[] Notice that in the example above, the ~c[:]~ilc[hints] in the ~ilc[local] definition of ~c[F] are inherited from the ~c[:hints] in the ~ilc[xargs] of the ~c[defexec] form. We discuss such inheritance below. ~sc[Caveat]: Termination is not considered for calls of ~ilc[mbe] under the top-level call. Moreover, the ~c[:exec] part of an ~ilc[mbe] call under the ~c[:logic] part of any superior ~c[mbe] call is completely ignored. ~bv[] General Form: (defexec fn (var1 ... varn) doc-string dcl ... dcl (mbe :LOGIC logic-body :EXEC exec-body)) ~ev[] where the syntax is identical to the syntax of ~ilc[defun] where the body is a call of ~c[mbe], with the exceptions described below. Thus, ~c[fn] is the symbol you wish to define and is a new symbolic name and ~c[(var1 ... varn)] is its list of formal parameters (~pl[name]). The first exception is that at least one ~c[dcl] (i.e., ~ilc[declare] form) must specify a ~c[:guard], ~c[guard]. The second exception is that one of the ~c[dcl]s is allowed to contain an element of the form ~c[(exec-xargs ...)]. The ~c[exec-xargs] form, if present, must specify a non-empty ~ilc[keyword-value-listp] each of whose keys is one of ~c[:test], ~c[:default-value], or one of the standard ~ilc[xargs] keys of ~c[:measure], ~c[:ruler-extenders], ~c[:well-founded-relation], ~c[:hints], or ~c[:stobjs]. Any of these five standard ~c[xargs] keys that is present in an ~c[xargs] of some ~c[dcl] but is not specified in the (possibly nonexistent) ~c[exec-xargs] form is considered to be specified in the ~c[exec-xargs] form, as illustrated in the example above for ~c[:hints]. (So for example, if you want ~c[:hints] in the final, non-local definition but not in the local definition, then specify the ~c[:hints] in the ~c[xargs] but specify ~c[:hints nil] in the ~c[exec-xargs].) If ~c[:test] is specified and not ~c[nil], let ~c[test] be its value; otherwise let ~c[test] default to ~c[guard]. If ~c[:default-value] is specified, let ~c[default-value] be its value; else ~c[default-value] is ~c[nil]. ~c[Default-value] should have the same ~il[signature] as ~c[exec-body]; otherwise the ~c[defexec] form will fail to be admitted. The above General Form's macroexpansion is of the form ~c[(PROGN encap final-def)], where ~c[encap] and ~c[final-def] are as follows. ~c[Final-def] is simply the result of removing the ~c[exec-xargs] declaration (if any) from its ~ilc[declare] form, and is the result of evaluating the given ~c[defexec] form, since ~c[encap] is of the following form. ~bv[] ; encap (ENCAPSULATE () (set-ignore-ok t) ; harmless for proving termination (set-irrelevant-formals-ok t) ; harmless for proving termination (local local-def) (local local-thm)) ~ev[] The purpose of ~c[encap] is to ensure the the executable version of ~c[name] terminates on all arguments. Thus, ~c[local-def] and ~c[local-thm] are as follows, where the ~c[xargs] of the ~ilc[declare] form are the result of adding ~c[:VERIFY-GUARDS NIL] to the result of removing the ~c[:test] and (optional) ~c[:default-value] from the ~c[exec-xargs]. ~bv[] ; local-def (DEFUN fn formals (DECLARE (XARGS :VERIFY-GUARDS NIL ...)) (IF test exec-body default-value)) ; local-thm (DEFTHM fn-EXEC-GUARD-HOLDS (IMPLIES guard test) :RULE-CLASSES NIL) ~ev[] We claim that if the above ~c[local-def] and ~c[local-thm] are admitted, then all evaluations of calls of ~c[fn] terminate. The concern is that the use of ~ilc[mbe] in ~c[final-def] allows for the use of ~c[exec-body] for a call of ~c[fn], as well as for subsequent recursive calls, when ~c[guard] holds and assuming that the guards have been verified for ~c[final-def]. However, by ~c[local-thm] we can conclude in this case that ~c[test] holds, in which case the call of ~c[fn] may be viewed as a call of the version of ~c[fn] defined in ~c[local-def]. Moreover, since guards have been verified for ~c[final-def], then guards hold for subsequent evaluation of ~c[exec-body], and in particular for recursive calls of ~c[fn], which can thus continue to be viewed as calls using ~c[local=def].~/~/ :cited-by mbe" (let ((dcls-and-strings (butlast rest 1)) (body (car (last rest)))) (mv-let (erp exec-body) (case-match body (('mbe ':logic & ':exec exec-body) (mv nil exec-body)) (('mbe ':exec exec-body ':logic &) (mv nil exec-body)) (('mbe . &) (mv 'mbe nil)) (& (mv t nil))) (cond (erp `(er soft 'defexec "A defexec form must have a body that is a valid call of mbe. ~ See :DOC ~s0." ,(if (eq erp 'mbe) "mbe" "defexec"))) ((not (symbolp fn)) `(er soft 'defexec "The first argument of defexec must be a symbol, but ~x0 is not." ',fn)) ((not (arglistp formals)) `(er soft 'defexec "The second argument of defexec must be legal list of formals, ~ but ~x0 is not." ',formals)) (t (mv-let (erp final-dcls-and-strings guard exec-xargs exec-test exec-default) (parse-defexec-dcls dcls-and-strings nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil nil) (cond (erp `(er soft 'defexec "Macroexpansion of ~x0 has failed because ~@1." ',whole ',erp)) (t `(encapsulate () (local (encapsulate () (set-ignore-ok t) (set-irrelevant-formals-ok t) (local (defun ,fn ,formals (declare (xargs :verify-guards nil ,@exec-xargs)) (if ,exec-test ,exec-body ,exec-default))) (local (defthm ,(packn (list fn '-GUARD-IMPLIES-TEST)) (implies ,guard ,exec-test) :rule-classes nil)))) (defun ,fn ,formals ,@final-dcls-and-strings ,body)))))))))) ; Start code for :pl and proof-checker show-rewrites command. (defrec sar ; single-applicable-rewrite ((lemma . alist) (index . equiv)) nil) ; Here's the idea. Both showing and using of rewrites benefits from knowing ; which hypotheses are irrelevant. But when rewriting in the proof-checker, we ; will try to do more, namely relieve all the hyps by instantiating free ; variables. So we avoid doing any instantiation in forming the sar record. ; Actually, if we knew that rewriting were to be done with the empty ; substitution, then we'd go ahead and store the result of trying to relieve ; hypotheses at this point; but we don't. Nevertheless, we should have a ; function that takes the fields of an sar record and returns an appropriate ; structure representing the result of trying to relieve the hyps (possibly ; starting with a unify-subst extending the one that was originally produced). (defun applicable-rewrite-rules1 (term geneqv lemmas current-index target-name-or-rune target-index wrld) ; Warning: If you change this function, consider changing related function ; applicable-linear-rules1. ; Call this initially with current-index equal to 1. (declare (xargs :guard (or (null target-index) (integerp target-index)))) (cond ((consp lemmas) (let ((lemma (car lemmas))) (cond ((and (or (null target-name-or-rune) (if (symbolp target-name-or-rune) (equal target-name-or-rune (cadr (access rewrite-rule lemma :rune))) (equal target-name-or-rune (access rewrite-rule lemma :rune)))) (member (access rewrite-rule lemma :subclass) '(backchain abbreviation definition)) (or (eq geneqv :none) (geneqv-refinementp (access rewrite-rule lemma :equiv) geneqv wrld))) (mv-let (flg alist) (one-way-unify (access rewrite-rule lemma :lhs) term) (cond (flg (if target-index (if (eql target-index current-index) (list (make sar :index current-index :lemma lemma :alist alist :equiv (access rewrite-rule lemma :equiv))) (applicable-rewrite-rules1 term geneqv (cdr lemmas) (1+ current-index) target-name-or-rune target-index wrld)) (cons (make sar :index (if target-name-or-rune nil current-index) :lemma lemma :alist alist :equiv (access rewrite-rule lemma :equiv)) (applicable-rewrite-rules1 term geneqv (cdr lemmas) (1+ current-index) target-name-or-rune target-index wrld)))) (t (applicable-rewrite-rules1 term geneqv (cdr lemmas) current-index target-name-or-rune target-index wrld))))) (t (applicable-rewrite-rules1 term geneqv (cdr lemmas) current-index target-name-or-rune target-index wrld))))) (t nil))) (defun applicable-linear-rules1 (term lemmas current-index target-name-or-rune target-index) ; Warning: If you change this function, consider changing related function ; applicable-rewrite-rules1. ; Call this initially with current-index equal to 1. (declare (xargs :guard (or (null target-index) (integerp target-index)))) (cond ((consp lemmas) (let ((lemma (car lemmas))) (cond ((or (null target-name-or-rune) (if (symbolp target-name-or-rune) (equal target-name-or-rune (cadr (access linear-lemma lemma :rune))) (equal target-name-or-rune (access linear-lemma lemma :rune)))) (mv-let (flg alist) (one-way-unify (access linear-lemma lemma :max-term) term) (cond (flg (cond (target-index (cond ((eql target-index current-index) (list (make sar ; omit :equiv, which is not needed :index current-index :lemma lemma :alist alist))) (t (applicable-linear-rules1 term (cdr lemmas) (1+ current-index) target-name-or-rune target-index)))) (t (cons (make sar ; omit :equiv, which is not needed :index (if target-name-or-rune nil current-index) :lemma lemma :alist alist) (applicable-linear-rules1 term (cdr lemmas) (1+ current-index) target-name-or-rune target-index))))) (t (applicable-linear-rules1 term (cdr lemmas) current-index target-name-or-rune target-index))))) (t (applicable-linear-rules1 term (cdr lemmas) current-index target-name-or-rune target-index))))) (t nil))) (defun pc-relieve-hyp (rune hyp unify-subst type-alist wrld state ens ttree) ; This function is adapted from ACL2 function relieve-hyp, but without ; rewriting. Notice that there are no arguments for obj, equiv, fnstack, ; ancestors, or simplify-clause-pot-lst. Also notice that rcnst has been ; replaced by ens (an enable structure). ; We return t or nil indicating whether we won, an extended unify-subst and a ; new ttree, with one exception: we can return (mv :unify-subst-list lst ; new-ttree), where lst is a list of binding alists, as for relieve-hyp. This ; function is a No-Change Loser. (cond ((and (nvariablep hyp) (not (fquotep hyp)) (eq (ffn-symb hyp) 'synp)) (mv-let (wonp failure-reason unify-subst ttree) (relieve-hyp-synp rune hyp unify-subst (rewrite-stack-limit wrld) type-alist wrld state nil ; fnstack nil ; ancestors nil ; backchain-limit nil ; simplify-clause-pot-lst (make-rcnst ens wrld :force-info 'weak) ; conservative nil ; gstack ttree nil ; bkptr ) (declare (ignore failure-reason)) (mv wonp unify-subst ttree))) (t (mv-let (forcep bind-flg) (binding-hyp-p hyp unify-subst wrld) (let ((hyp (if forcep (fargn hyp 1) hyp))) (cond (bind-flg (mv t (cons (cons (fargn hyp 1) (sublis-var unify-subst (fargn hyp 2))) unify-subst) ttree)) (t (mv-let (lookup-hyp-ans unify-subst ttree) (lookup-hyp hyp type-alist wrld unify-subst ttree) (cond (lookup-hyp-ans (mv t unify-subst ttree)) ((free-varsp hyp unify-subst) (search-ground-units hyp unify-subst type-alist ens (ok-to-force-ens ens) wrld ttree)) (t (let ((inst-hyp (sublis-var unify-subst hyp))) (mv-let (knownp nilp nilp-ttree) (known-whether-nil inst-hyp type-alist ens (ok-to-force-ens ens) nil ; dwp wrld ttree) (cond (knownp (mv (not nilp) unify-subst nilp-ttree)) (t (mv-let (not-flg atm) (strip-not hyp) ; Again, we avoid rewriting in this proof-checker code. (cond (not-flg (if (equal atm *nil*) (mv t unify-subst ttree) (mv nil unify-subst ttree))) (t (if (if-tautologyp atm) (mv t unify-subst ttree) (mv nil unify-subst ttree))))))))))))))))))) (mutual-recursion (defun pc-relieve-hyps1-iter (rune hyps unify-subst-lst unify-subst unify-subst0 ttree0 type-alist keep-unify-subst wrld state ens ttree) ; This function is adapted from ACL2 function relieve-hyps1-iter. (mv-let (relieve-hyps1-ans unify-subst1 ttree1) (pc-relieve-hyps1 rune hyps (extend-unify-subst (car unify-subst-lst) unify-subst) unify-subst0 ttree0 type-alist keep-unify-subst wrld state ens ttree) (cond ((or (endp (cdr unify-subst-lst)) relieve-hyps1-ans) (mv relieve-hyps1-ans unify-subst1 ttree1)) (t (pc-relieve-hyps1-iter rune hyps (cdr unify-subst-lst) unify-subst unify-subst0 ttree0 type-alist keep-unify-subst wrld state ens ttree))))) (defun pc-relieve-hyps1 (rune hyps unify-subst unify-subst0 ttree0 type-alist keep-unify-subst wrld state ens ttree) ; This function is adapted from ACL2 function relieve-hyp. Notice that there ; are no arguments for obj, equiv, fnstack, ancestors, or ; simplify-clause-pot-lst. Also notice that rcnst has been replaced by ens (an ; enable structure). ; When keep-unify-subst is non-nil, we run through all of the hyps in order to ; find extensions of unify-subst that bind free variables in order to make hyps ; true. Keep-unify-subst is true at the top level, but when we get a failure, ; we set it to :FAILED so that we can return nil at the end. ; This function a No-Change Loser when keep-unify-subst is nil. In order to ; accomplish this without requiring it have to test the answer to its own ; recursive calls, we have to pass down the original unify-subst and ttree so ; that when it fails it can return them instead of the accumulated versions. (cond ((null hyps) (mv (not (eq keep-unify-subst :FAILED)) unify-subst ttree)) (t (mv-let (relieve-hyp-ans new-unify-subst ttree) ; We avoid rewriting in this proof-checker code, so new-ttree = ttree. (pc-relieve-hyp rune (car hyps) unify-subst type-alist wrld state ens ttree) (cond ((eq relieve-hyp-ans :unify-subst-list) ; The hypothesis (car hyps) is a call of bind-free that has produced a list of ; unify-substs. (pc-relieve-hyps1-iter rune (cdr hyps) new-unify-subst ; a list of alists unify-subst unify-subst0 ttree0 type-alist keep-unify-subst wrld state ens ttree)) ((or relieve-hyp-ans keep-unify-subst) (pc-relieve-hyps1 rune (cdr hyps) new-unify-subst unify-subst0 ttree0 type-alist (if (and (eq keep-unify-subst t) (not relieve-hyp-ans)) :FAILED keep-unify-subst) wrld state ens ttree)) (t (mv nil unify-subst0 ttree0))))))) ) (defun pc-relieve-hyps (rune hyps unify-subst type-alist keep-unify-subst wrld state ens ttree) ; Adapted from ACL2 function relieve-hyp. Notice that there are no arguments ; for obj, equiv, fnstack, ancestors, or simplify-clause-pot-lst. Also notice ; that rcnst has been replaced by ens (an enable structure). ; We return t or nil indicating success, an extended unify-subst and ; a new ttree. This function is a No-Change Loser. (pc-relieve-hyps1 rune hyps unify-subst unify-subst ttree type-alist keep-unify-subst wrld state ens ttree)) (defun remove-trivial-lits (lst type-alist alist wrld ens ttree) ; Removes trivially true lits from lst. However, we don't touch elements of ; lst that contain free variables. We apply the substitution at this point ; because we need to know whether a lit contains a free variable (one not bound ; by alist) that might get bound later, thus changing its truth value. (if (consp lst) (mv-let (rest-list ttree) (remove-trivial-lits (cdr lst) type-alist alist wrld ens ttree) (let ((new-lit (sublis-var alist (car lst)))) (if (free-varsp (car lst) alist) (mv (cons new-lit rest-list) ttree) (mv-let (knownp nilp nilp-ttree) (known-whether-nil new-lit type-alist ens (ok-to-force-ens ens) nil ; dwp wrld ttree) (if (and knownp (not nilp)) (mv rest-list nilp-ttree) (mv (cons new-lit rest-list) ttree)))))) (mv nil ttree))) (defun unrelieved-hyps (rune hyps unify-subst type-alist keep-unify-subst wrld state ens ttree) ; Returns unrelieved hyps (with the appropriate substitution applied), an ; extended substitution, and a new tag-tree. Note: the substitution really has ; been applied already to the returned hyps, even though we also return the ; extended substitution. ; If keep-unify-subst is true, then we allow unify-subst to extend even if we ; do not relieve all of the hypotheses. (mv-let (success-flg new-unify-subst new-ttree) (pc-relieve-hyps rune hyps unify-subst type-alist keep-unify-subst wrld state ens ttree) (if success-flg (mv nil new-unify-subst new-ttree) (mv-let (unify-subst ttree) (if keep-unify-subst (mv new-unify-subst new-ttree) (mv unify-subst ttree)) (mv-let (lits ttree) (remove-trivial-lits hyps type-alist unify-subst wrld ens ttree) (mv lits unify-subst ttree)))))) (defun untranslate-subst-abb (sub abbreviations state) (declare (xargs :guard (symbol-alistp sub))) (if (consp sub) (cons (list (caar sub) (untrans0 (cdar sub) nil abbreviations)) (untranslate-subst-abb (cdr sub) abbreviations state)) nil)) (defun show-rewrite-linear (caller index col rune nume show-more subst-hyps subst-hyps-2 unify-subst unify-subst-2 free free-2 rhs abbreviations term-id-iff ens enabled-only-flg equiv pl-p state) ; Pl-p is true when we are calling this function on behalf of :pl, and is false ; when we are calling it on behalf of the proof-checker. (let ((enabledp (enabled-numep nume ens)) (subst-rhs (sublis-var unify-subst rhs)) (term-id-iff (and (eq caller 'show-rewrites) term-id-iff))) (if (and enabled-only-flg (not enabledp)) state (pprogn (fms "~|~#a~[~/~c0. ~/ ~]~x1~#2~[~/ (disabled)~]" (list (cons #\a (if pl-p 0 (if index 1 2))) (cons #\0 (cons index col)) (cons #\1 (cond (pl-p rune) ((cddr rune) rune) ; just print name if rune seems unique for it (t (base-symbol rune)))) (cons #\2 (if enabledp 0 1))) (standard-co state) state nil) (let ((fmt-string "~ ~ ~#c~[New term~/Conclusion~]: ~Y3t~|~ ~ ~ Hypotheses: ~#b~[~/~Y4t~]~|~ ~#c~[~ ~ Equiv: ~ye~|~/~]~ ~#s~[~/~ ~ Substitution: ~Yat~|~]~ ~#5~[~/~ ~ ~ Remaining free variable: ~&6~/~ ~ ~ Remaining free variables: ~&6~sn~]~ ~#7~[~/ WARNING: One of the hypotheses is (equivalent to) NIL, ~ and hence will apparently be impossible to relieve.~]~|")) (pprogn (cond ((and show-more pl-p) ; then just show more state) (t (fms fmt-string (list (cons #\c (if (eq caller 'show-rewrites) 0 1)) (cons #\3 (untrans0 subst-rhs term-id-iff abbreviations)) (cons #\s (if pl-p 1 0)) (cons #\a (untranslate-subst-abb unify-subst abbreviations state)) (cons #\b (if subst-hyps 1 0)) (cons #\e equiv) (cons #\4 (untrans0-lst subst-hyps t abbreviations)) (cons #\5 (zero-one-or-more (length free))) (cons #\6 free) (cons #\n "") (cons #\7 (if (member-eq nil subst-hyps) 1 0)) (cons #\t (term-evisc-tuple nil state))) (standard-co state) state nil))) (cond (show-more (pprogn (cond (pl-p state) (t (fms0 " -- IF ~#c~[REWRITE~/APPLY-LINEAR~]: is ~ called with a third argument of t: --" (list (cons #\c (if (eq caller 'show-rewrites) 0 1)))))) (fms fmt-string (list (cons #\c (if (eq caller 'show-rewrites) 0 1)) (cons #\3 (untrans0 (sublis-var unify-subst-2 rhs) term-id-iff abbreviations)) (cons #\s (if pl-p 1 0)) (cons #\a (untranslate-subst-abb unify-subst-2 abbreviations state)) (cons #\b (if subst-hyps-2 1 0)) (cons #\e equiv) (cons #\4 (untrans0-lst subst-hyps-2 t abbreviations)) (cons #\5 (if (eql (length free-2) 1) 1 2)) (cons #\6 free-2) (cons #\n (if (null free-2) "[none]" "")) (cons #\7 (if (member-eq nil subst-hyps-2) 1 0)) (cons #\t (term-evisc-tuple nil state))) (standard-co state) state nil))) (t state)))))))) (defun show-rewrites-linears (caller app-rules col abbreviations term-id-iff ens type-alist enabled-only-flg pl-p w state) ; Pl-p is true when we are calling this function on behalf of :pl, and is false ; when we are calling it on behalf of the proof-checker. (cond ((null app-rules) state) (t (pprogn (let ((sar (car app-rules))) (let ((lemma (access sar sar :lemma)) (alist (access sar sar :alist)) (index (access sar sar :index))) (mv-let (hyps result rune) (cond ((eq caller 'show-rewrites) (mv (access rewrite-rule lemma :hyps) (access rewrite-rule lemma :rhs) (access rewrite-rule lemma :rune))) (t (mv (access linear-lemma lemma :hyps) (access linear-lemma lemma :concl) (access linear-lemma lemma :rune)))) (mv-let (subst-hyps unify-subst ttree) (unrelieved-hyps rune hyps alist type-alist nil w state ens nil) (declare (ignore ttree)) (let* ((result-and-hyps-vars (union-eq (all-vars result) (all-vars1-lst hyps nil))) (free (set-difference-assoc-eq result-and-hyps-vars unify-subst))) (mv-let (show-more subst-hyps-2 unify-subst-2) (cond ((and free subst-hyps) ; Then we try to find at least a partial extension of unify-subst that ; eliminates some hypotheses. (mv-let (subst-hyps-2 unify-subst-2 ttree) (unrelieved-hyps rune hyps alist type-alist t w state ens nil) (declare (ignore ttree)) (cond ((equal unify-subst-2 unify-subst) (assert$ (equal subst-hyps-2 subst-hyps) (mv nil subst-hyps unify-subst))) (t (mv t subst-hyps-2 unify-subst-2))))) (t (mv nil subst-hyps unify-subst))) (show-rewrite-linear caller index col rune (if (eq caller 'show-rewrites) (access rewrite-rule lemma :nume) (access linear-lemma lemma :nume)) show-more subst-hyps subst-hyps-2 unify-subst unify-subst-2 free (set-difference-assoc-eq result-and-hyps-vars unify-subst-2) result abbreviations term-id-iff ens enabled-only-flg (and (eq caller 'show-rewrites) (access sar sar :equiv)) pl-p state))))))) (show-rewrites-linears caller (cdr app-rules) col abbreviations term-id-iff ens type-alist enabled-only-flg pl-p w state))))) (defun expand-assumptions-1 (term) (case-match term (('if a b ''nil) (append (expand-assumptions-1 a) (expand-assumptions-1 b))) ((equality-p a b) (if (or (and (eq equality-p 'eq) (or (and (consp a) (eq (car a) 'quote) (symbolp (cadr a))) (and (consp b) (eq (car b) 'quote) (symbolp (cadr b))))) (and (eq equality-p 'eql) (or (and (consp a) (eq (car a) 'quote) (eqlablep (cadr a))) (and (consp b) (eq (car b) 'quote) (eqlablep (cadr b)))))) (list term (mcons-term* 'equal a b)) (list term))) (& (list term)))) (defun expand-assumptions (x) ; If x is (and a b) then we get (list a b), etc. (declare (xargs :guard (true-listp x))) (if x (append (expand-assumptions-1 (car x)) (expand-assumptions (cdr x))) nil)) (defun hyps-type-alist (assumptions ens wrld state) ; Note that the force-flg arg to type-alist-clause is nil here, so we shouldn't ; wind up with any assumptions in the returned tag-tree. Also note that we ; return (mv contradictionp type-alist fc-pair-lst), where actually fc-pair-lst ; is a ttree if contradictionp holds; normally we ignore fc-pair-lst otherwise. (forward-chain-top 'show-rewrites (dumb-negate-lit-lst (expand-assumptions assumptions)) nil (ok-to-force-ens ens) nil ; do-not-reconsiderp wrld ens (match-free-override wrld) state)) (defun show-rewrites-linears-fn (caller rule-id enabled-only-flg ens current-term abbreviations term-id-iff all-hyps geneqv pl-p state) ; Pl-p is true when we are calling this function on behalf of :pl, and is false ; when we are calling it on behalf of the proof-checker. (let ((name (and (symbolp rule-id) rule-id)) (index (and (integerp rule-id) (< 0 rule-id) rule-id)) (rune (and (consp rule-id) (if pl-p (keywordp (car rule-id)) (member-eq (car rule-id) (cond ((eq caller 'show-rewrites) '(:rewrite :definition)) (t :linear)))) rule-id)) (w (w state))) (cond ((and (not pl-p) ; optimization -- check is already made by pl2-fn rule-id (not (or name index rune))) (fms "The rule-id argument to ~s0 must be a name, a positive ~ integer, or a rune representing a rewrite or definition rule, but ~ ~x1 is none of these.~|" (list (cons #\0 (symbol-name caller)) (cons #\1 rule-id)) (standard-co state) state nil)) ((and (not pl-p) ; optimization -- check is already made by pl2-fn (or (variablep current-term) (fquotep current-term) (flambdap (ffn-symb current-term)))) (fms "It is only possible to apply ~#0~[rewrite rules to terms~/linear ~ rules for triggers~] that are not variables, (quoted) constants, ~ or applications of lambda expressions. However, the current term ~ is:~%~ ~ ~y1.~|" (list (cons #\0 (if (eq caller 'show-rewrites) 0 1)) (cons #\1 current-term)) (standard-co state) state (term-evisc-tuple nil state))) ((and (not pl-p) ; optimization -- check is already made by pl2-fn (eq (ffn-symb current-term) 'if)) (fms "It is only possible to apply ~#0~[rewrite rules to terms~/linear ~ rules for triggers~] that are applications of function symbols ~ other than IF. However, the current term is~|~ ~ ~y0.~|" (list (cons #\0 (if (eq caller 'show-rewrites) 0 1)) (cons #\1 current-term)) (standard-co state) state (term-evisc-tuple nil state))) (t (mv-let (flg hyps-type-alist ttree) (hyps-type-alist all-hyps ens w state) (declare (ignore ttree)) (cond (flg ; contradiction in hyps, so we are in the proof-checker (assert$ (not pl-p) (fms "*** Contradiction in the hypotheses! ***~%The S command ~ should complete this goal.~|" nil (standard-co state) state nil))) (t (let ((app-rules (cond ((eq caller 'show-rewrites) (applicable-rewrite-rules1 current-term geneqv (getprop (ffn-symb current-term) 'lemmas nil 'current-acl2-world w) 1 (or name rune) index w)) (t (applicable-linear-rules1 current-term (getprop (ffn-symb current-term) 'linear-lemmas nil 'current-acl2-world w) 1 (or name rune) index))))) (cond ((null app-rules) (cond (pl-p state) ((and index (> index 1)) (fms "~|*** There are fewer than ~x0 applicable ~s1 ~ rules. ***~%" (list (cons #\0 index) (cons #\1 (if (eq caller 'show-rewrites) "rewrite" "linear"))) (standard-co state) state nil)) (t (fms "~|*** There are no applicable ~s0 rules. ***~%" (list (cons #\0 (if (eq caller 'show-rewrites) "rewrite" "linear"))) (standard-co state) state nil)))) (t (show-rewrites-linears caller app-rules (floor (length app-rules) 10) abbreviations term-id-iff ens hyps-type-alist enabled-only-flg pl-p w state))))))))))) (defun show-meta-lemmas1 (lemmas rule-id term wrld ens state) (cond ((endp lemmas) state) (t (pprogn (let* ((lemma (car lemmas)) (rune (and (eq (access rewrite-rule lemma :subclass) 'meta) (access rewrite-rule lemma :rune)))) (cond ((and rune ; hence lemma is a meta lemma (or (null rule-id) (if (symbolp rule-id) (eq rule-id (base-symbol rune)) (equal rule-id rune)))) (let* ((fn (access rewrite-rule lemma :lhs)) (extendedp (access rewrite-rule lemma :rhs)) (args (meta-fn-args term extendedp ens state))) (mv-let (erp new-term latches) (ev-fncall-meta fn args state) (declare (ignore latches)) (cond ((or erp (equal new-term term) (not (termp new-term wrld))) state) (t (let ((hyp-fn (access rewrite-rule lemma :hyps))) (mv-let (erp hyp latches) (if hyp-fn (ev-fncall-meta hyp-fn (meta-fn-args term extendedp ens state) state) (mv nil *t* nil)) (declare (ignore latches)) (cond ((or erp (not (termp hyp wrld))) state) (t (fms "~Y01~|~ ~ ~ New term: ~Y2t~|~ ~ ~ Hypothesis: ~Y3t~|~ ~ ~ Equiv: ~y4~|" (list (cons #\0 rune) (cons #\1 nil) (cons #\2 new-term) (cons #\3 (untranslate hyp nil wrld)) (cons #\4 (access rewrite-rule lemma :equiv)) (cons #\t (term-evisc-tuple nil state))) (standard-co state) state nil)))))))))) (t state))) (show-meta-lemmas1 (cdr lemmas) rule-id term wrld ens state))))) (defun show-meta-lemmas (term rule-id state) (cond ((and (nvariablep term) (not (fquotep term)) (not (flambdap (ffn-symb term)))) (let ((wrld (w state))) (show-meta-lemmas1 (getprop (ffn-symb term) 'lemmas nil 'current-acl2-world wrld) rule-id term wrld (ens state) state))) (t state))) (defun decoded-type-set-from-tp-rule (tp unify-subst wrld ens) (mv-let (ts type-alist ttree) (type-set-with-rule1 unify-subst (access type-prescription tp :vars) (ok-to-force-ens ens) nil ; dwp, as in known-whether-nil (see relieve-hyp) nil ; type-alist nil ; ancestors ens wrld (access type-prescription tp :basic-ts) nil ; ttree nil ; pot-lst nil ; pt nil ; backchain-limit ) (declare (ignore type-alist ttree)) (decode-type-set ts))) (defun show-type-prescription-rule (rule unify-subst type-alist abbreviations wrld ens state) (let ((rune (access type-prescription rule :rune)) (nume (access type-prescription rule :nume)) (hyps (access type-prescription rule :hyps))) (pprogn (fms "~x1~#2~[~/ (disabled)~]" (list (cons #\1 rune) (cons #\2 (if (enabled-numep nume ens) 0 1))) (standard-co state) state nil) (let ((fmt-string "~ ~ Type: ~Y01~|~ ~ ~ Hypotheses: ~#b~[~/~Y4t~]~|~ ~ ~ Substitution: ~Yat~|~ ~#5~[~/~ ~ ~ Remaining free variable: ~&6~/~ ~ ~ Remaining free variables: ~&6~sn~]~ ~#7~[~/ WARNING: One of the hypotheses is (equivalent to) NIL, ~ and hence will apparently be impossible to relieve.~]~|")) (mv-let (subst-hyps unify-subst ttree) (unrelieved-hyps rune hyps unify-subst type-alist nil wrld state ens nil) (declare (ignore ttree)) (let ((free (set-difference-assoc-eq (all-vars1-lst hyps nil) unify-subst))) (fms fmt-string (list (cons #\a (untranslate-subst-abb unify-subst abbreviations state)) (cons #\b (if subst-hyps 1 0)) (cons #\0 (decoded-type-set-from-tp-rule rule unify-subst wrld ens)) (cons #\1 nil) (cons #\4 (untrans0-lst subst-hyps t abbreviations)) (cons #\5 (zero-one-or-more (length free))) (cons #\6 free) (cons #\n "") (cons #\7 (if (member-eq nil subst-hyps) 1 0)) (cons #\t (term-evisc-tuple nil state))) (standard-co state) state nil))))))) (defun show-type-prescription-rules1 (rules term rule-id type-alist abbreviations wrld ens state) (cond ((endp rules) state) (t (pprogn (mv-let (unify-ans unify-subst) (cond ((or (null rule-id) (let ((rune (access type-prescription (car rules) :rune))) (if (symbolp rule-id) (eq rule-id (base-symbol rune)) (equal rule-id rune)))) (one-way-unify (access type-prescription (car rules) :term) term)) (t (mv nil nil))) (cond (unify-ans (show-type-prescription-rule (car rules) unify-subst type-alist abbreviations wrld ens state)) (t state))) (show-type-prescription-rules1 (cdr rules) term rule-id type-alist abbreviations wrld ens state))))) (defun show-type-prescription-rules (term rule-id abbreviations all-hyps ens state) (cond ((and (nvariablep term) (not (fquotep term)) (not (flambdap (ffn-symb term))) (not (eq (ffn-symb term) 'if))) (let ((wrld (w state))) (mv-let (flg hyps-type-alist ttree) (hyps-type-alist all-hyps ens wrld state) (declare (ignore ttree)) (cond (flg ; contradiction, so hyps is non-nil: we are in proof-checker (fms "*** Contradiction in the hypotheses! ***~%The S command ~ should complete this goal.~|" nil (standard-co state) state nil)) (t (show-type-prescription-rules1 (getprop (ffn-symb term) 'type-prescriptions nil 'current-acl2-world wrld) term rule-id hyps-type-alist abbreviations wrld ens state)))))) (t ; Presumably we are inside the proof-checker, since pl2-fn has already checked ; term. (fms "Type-prescription rules are associated with function symbols ~ (other than IF). The current term, ~x0, is therefore not ~ suitable for listing associated type-prescription rules.~|" (list (cons #\0 term)) (standard-co state) state nil)))) (defun pl2-fn (form rule-id caller state) (let ((ens (ens state))) (er-let* ((term (translate form t t nil caller (w state) state))) (cond ((not (or (symbolp rule-id) (and (consp rule-id) (keywordp (car rule-id))))) (er soft caller "The rule-id supplied to ~x0 must be a symbol or a rune, but ~x1 ~ is neither. See :DOC ~x0." caller rule-id)) (t (mv-let (flg term1) (cond ((or (variablep term) (fquotep term) (flambdap (ffn-symb term)) (eq (ffn-symb term) 'if)) (mv t (remove-guard-holders term))) (t (mv nil term))) (cond ((or (variablep term1) (fquotep term1) (flambdap (ffn-symb term1)) (eq (ffn-symb term1) 'if)) (er soft caller "~@0 must represent a term that is not a variable or a ~ constant, which is not a LET (or LAMBDA application), ~ and whose function symbol is not IF. But ~x1 does not ~ meet this requirement." (case caller (pl (msg "A non-symbol argument of ~x0" caller)) (pl2 (msg "The first argument of ~x0" caller)) (otherwise (er hard 'pl2-fn "Implementation error: Unexpected case! ~ ~ Please contact the ACL2 implementors."))) form)) (t (let ((term term1)) (pprogn (cond (flg (fms "+++++++++~%**NOTE**:~%Instead showing ~ rules for the following term, which is ~ much more likely to be encountered ~ during proofs:~|~% ~y0+++++++++~%" (list (cons #\0 (untranslate term1 nil (w state)))) (standard-co state) state nil)) (t state)) (show-rewrites-linears-fn 'show-rewrites rule-id nil ens term nil nil nil :none t state) (show-rewrites-linears-fn 'show-linears rule-id nil ens term nil nil nil :none t state) (show-meta-lemmas term rule-id state) (show-type-prescription-rules term rule-id nil nil ens state) (value :invisible))))))))))) (defun pl-fn (name state) (cond ((symbolp name) (let* ((wrld (w state)) (ens (ens state)) (name (deref-macro-name name (macro-aliases wrld)))) (cond ((function-symbolp name wrld) (print-info-for-rules (append (info-for-lemmas (getprop name 'lemmas nil 'current-acl2-world wrld) t ens wrld) (info-for-linear-lemmas (getprop name 'linear-lemmas nil 'current-acl2-world wrld) t ens wrld) (info-for-type-prescriptions (getprop name 'type-prescriptions nil 'current-acl2-world wrld) t ens wrld) (info-for-forward-chaining-rules (getprop name 'forward-chaining-rules nil 'current-acl2-world wrld) t ens wrld) (let ((elim-rule (getprop name 'eliminate-destructors-rule nil 'current-acl2-world wrld))) (and elim-rule (info-for-eliminate-destructors-rule elim-rule t ens wrld))) (info-for-induction-rules (getprop name 'induction-rules nil 'current-acl2-world wrld) t ens wrld)) (standard-co state) state)) (t (er soft 'pl "If the argument to PL is a symbol, then it must be a function ~ symbol in the current world or else a macro that is associated ~ with a function symbol (see :DOC add-macro-alias)."))))) (t (pl2-fn name nil 'pl state)))) (defmacro pl (name) ":Doc-Section History print the rules for the given name or term~/ ~bv[] Examples: :pl foo ; prints rules that rewrite some call of foo :pl (+ x y) ; prints rules that rewrite (+ x y) ~ev[]~/ Also ~pl[pl2], which restricts output to rules that you specify for a given term. ~c[Pl] takes one argument, which should be a symbol or a term. First suppose that the argument is a symbol. Then it should be either a function symbol or else a macro alias for a function symbol (~pl[macro-aliases-table]), which is treated as the corresponding function symbol. In this case ~c[:pl] displays rules that apply to terms whose top function symbol is the one specified, specifically, rules of class ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], ~c[:]~ilc[meta], ~c[:]~ilc[linear], ~c[:]~ilc[type-prescription], ~c[:]~ilc[forward-chaining], ~c[:]~ilc[elim], and ~c[:]~ilc[induction]. Otherwise the argument should be a term (in user syntax, so that for example macros are permitted). In this case, ~c[:pl] displays the ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], and ~c[:meta] rules that rewrite the specified term, followed by the applicable ~c[:]~ilc[type-prescription] rules. Each rule is displayed with additional information, such as the hypotheses that remain after applying some simple techniques to discharge them that are likely to apply in any context. Note that for ~c[:]~ilc[meta] rules, only those are displayed that meet two conditions: the application of the metafunction returns a term different from the input term, and if there is a hypothesis metafunction then it also returns a term. (A subtlety: In the case of extended metafunctions (~pl[extended-metafunctions]), a trivial metafunction context is used for the application of the metafunction.) Note that some rule classes are not handled by ~c[:pl]. In particular, if you want to see all ~c[:]~ilc[clause-processor] rules, issue the command ~c[:print-clause-processor-rules], and for trusted clause-processors, ~c[(table trusted-clause-processor-table)]; ~pl[clause-processor] and ~pl[define-trusted-clause-processor].~/" (list 'pl-fn name 'state)) (defmacro pl2 (form rule-id) ":Doc-Section History print rule(s) for the given form~/ ~bv[] Examples: :pl2 (+ x y) nil ; prints rules that apply to (+ x y) :pl2 (+ x y) foo ; prints rules named foo that apply to (+ x y) :pl2 (+ x y) (:rewrite foo) ; if the rule with rune (:rewrite foo) applies ; to (+ x y), then print it :pl2 (+ x y) (:type-prescription foo) ; as above, but for the indicated ; type-prescription rule ~ev[]~/ ~c[Pl2] takes two arguments. The first is a term. The second is either ~c[nil] or a ``rule-id'' that is either a symbol or a ~il[rune]. The result is to print rules of class ~c[:]~ilc[rewrite], ~c[:]~ilc[definition], ~c[:meta], ~c[:]~ilc[linear], and ~c[:]~ilc[type-prescription] that apply to the given term. Indeed, ~c[:pl2] prints exactly what is printed by applying ~c[:]~ilc[pl] to the first argument ~-[] ~pl[pl] ~-[] except that if the second argument is not ~c[nil] then it is used to filter the rules printed, as follows.~bq[] If the rule-id is a symbol, then only rules whose name is that symbol will be printed. If the rule-id is a ~il[rune], then at most one rule will be printed: the rule named by that rune (if the rule would be printed by ~c[:]~ilc[pl]).~eq[]~/" (list 'pl2-fn form rule-id ''pl2 'state)) ; Essay on Include-book-dir-alist ; The :include-book-dir-alist field of the acl2-defaults-table supports the ; :dir argument of include-book and ld, by associating keywords (used as values ; of :dir) with absolute directory pathnames. The macro add-include-book-dir ; provides a way to extend that field. Up through ACL2 Version_3.6.1, when ; add-include-book-dir was executed in raw Lisp it would be ignored, because it ; macroexpanded to a table event. But consider a file loaded in raw Lisp, say ; when we are in raw-mode and are executing an include-book command with a :dir ; argument. If that :dir value were defined by an add-include-book-dir event ; also evaluated in raw Lisp, and hence ignored, then that :dir value would not ; really be defined after all and the include-book would fail. ; The above problem with raw-mode could be explained away by saying that ; raw-mode is a hack, and you get what you get. But Version_4.0 introduced the ; loading of compiled files before corresponding event processing, which causes ; routine evaluation of add-include-book-dir and include-book in raw Lisp. ; Therefore we maintain for raw Lisp a variant of the acl2-defaults-table's ; include-book-dir-alist, namely state global 'raw-include-book-dir-alist. The ; value of this variable is initially :ignore, meaning that we are to use the ; acl2-defaults-table's include-book-dir-alist. But when the value is not ; :ignore, then it is an alist that is used as the include-book-dir-alist. We ; guarantee that every embedded event form that defines handling of :dir values ; for include-book does so in a manner that works when loading compiled files, ; and we weakly extend this guarantee to raw-mode as well (weakly, since we ; cannot perfectly control raw-mode but anyhow, a trust tag is necessary to ; enter raw-mode so our guarantee need not be ironclad). The above :ignore ; value must then be set to a legitimate include-book-alist when inside ; include-book or (generally) raw-mode, and should remain :ignore when not in ; those contexts. When the value is not :ignore, then execution of ; add-include-book-dir will extend the value of 'raw-include-book-dir-alist ; instead of modifying the acl2-defaults-table. And whenever we execute ; include-book in raw Lisp, we use this value instead of the one in the ; acl2-defaults-table, by binding it to nil upon entry. Thus, any ; add-include-book-dir will be local to the book, which respects the semantics ; of include-book. We bind it to nil because that is also how include-book ; works: the acl2-defaults-table initially has an empty :include-book-dir-alist ; field (see process-embedded-events). ; In order to be able to rely on the above scheme, we disallow any direct table ; update of the :include-book-dir-alist field of the acl2-defaults-table. We ; use the state global 'modifying-include-book-dir-alist for this purpose, ; which is globally nil but is bound to t by add-include-book-dir and ; delete-include-book-dir. We insist that it be non-nil in chk-table-guard. ; We considered doing it in chk-embedded-event-form, but that would have been ; more awkward, and more importantly, it would have allowed such direct updates ; when developing a book interactively but not when certifying the book, which ; could provide a rude surprise to the user at certification time. ; End of Essay on Include-book-dir-alist (defun acl2-defaults-table-local-ctx-p (state) (let ((wrld (w state))) (or (global-val 'include-book-path wrld) (f-get-global 'certify-book-info state) (in-encapsulatep (global-val 'embedded-event-lst wrld) nil)))) (defun add-include-book-dir-fn (keyword dir state) ; See the Essay on Include-book-dir-alist. (declare (xargs :guard (state-p state) :mode :program)) (let ((ctx 'add-include-book-dir)) (cond ((or (not (keywordp keyword)) (eq keyword :SYSTEM)) (er soft ctx "The first argument of add-include-book-dir must be a keyword ~ (see :DOC keywordp) other than :SYSTEM, but ~x0 is not." keyword)) ((not (stringp dir)) (er soft ctx "The second argument of add-include-book-dir must be a ~ string, but ~x0 is not." dir)) (t (state-global-let* ((inhibit-output-lst (cons 'summary (@ inhibit-output-lst))) (modifying-include-book-dir-alist t)) (let* ((dir (maybe-add-separator (extend-pathname (cbd) dir state))) (raw-p (not (eq (f-get-global 'raw-include-book-dir-alist state) :ignore))) (old (cond (raw-p (f-get-global 'raw-include-book-dir-alist state)) (t (cdr (assoc-eq :include-book-dir-alist (table-alist 'acl2-defaults-table (w state))))))) (pair (assoc-eq keyword old)) (new (acons keyword dir old))) (cond ((not (absolute-pathname-string-p dir t (os (w state)))) ; The call above of maybe-add-separator should make this branch dead code, but ; we leave it here for robustness, e.g., in case we change that call. (er soft ctx "The second argument of add-include-book-dir must ~ represent a directory, in particular ending with ~ character '~s0', but ~x1 does not." *directory-separator-string* dir)) (pair (cond ((equal (cdr pair) dir) (stop-redundant-event ctx state)) (t (er soft ctx "The keyword ~x0 is already bound to directory ~ ~x1. If you intend to override the old setting ~ with directory ~x2, first evaluate ~x3." keyword (cdr pair) dir `(delete-include-book-dir ,keyword))))) (raw-p (pprogn (cond ((or (acl2-defaults-table-local-ctx-p state) (not (eq (f-get-global 'raw-include-book-dir-alist state) :ignore))) ; We skip the warning below when in a context for which acl2-defaults-table ; will ultimately be discarded, since the effect of add-include-book-dir would ; disappear even without raw-mode. We also skip the warning if we are in a ; context where we have previously transitioned the value of ; 'raw-include-book-dir-alist from :ignore, as either we have already given a ; warning, or else we deemed that transition not worthy of a warning, as in the ; state-global-let* binding of raw-include-book-dir-alist in ; load-compiled-book. state) (t (warning$ ctx "Raw-mode" "The effect of add-include-book-dir will ~ disappear if you exit raw-mode."))) (f-put-global 'raw-include-book-dir-alist new state) (value new))) (t (er-progn (table-fn 'acl2-defaults-table (list :include-book-dir-alist (list 'quote new)) state (list 'table 'acl2-defaults-table ':include-book-dir-alist (list 'quote new))) (value new)))))))))) (defun delete-include-book-dir-fn (keyword state) ; See the Essay on Include-book-dir-alist. (declare (xargs :guard (state-p state) :mode :program)) (let ((ctx 'delete-include-book-dir)) (cond ((or (not (keywordp keyword)) (eq keyword :SYSTEM)) (er soft ctx "The argument of delete-include-book-dir must be a keyword ~ (see :DOC keywordp) other than :SYSTEM, but ~x0 is not." keyword)) (t (state-global-let* ((inhibit-output-lst (cons 'summary (@ inhibit-output-lst))) (modifying-include-book-dir-alist t)) (let* ((raw-p (not (eq (f-get-global 'raw-include-book-dir-alist state) :ignore))) (old (cond (raw-p (f-get-global 'raw-include-book-dir-alist state)) (t (cdr (assoc-eq :include-book-dir-alist (table-alist 'acl2-defaults-table (w state))))))) (found (assoc-eq keyword old)) (new (if found (delete-assoc-eq keyword old) old))) (cond ((not found) (pprogn (warning$ ctx "Table" "There is no binding of keyword ~x0 to delete!" keyword) (stop-redundant-event ctx state))) (raw-p (pprogn (f-put-global 'raw-include-book-dir-alist new state) (warning$ ctx "Raw-mode" "The effect of add-include-book-dir will ~ disappear if you exit raw-mode.") (value new))) (t (er-progn (table-fn 'acl2-defaults-table (list :include-book-dir-alist (list 'quote new)) state (list 'table 'acl2-defaults-table ':include-book-dir-alist (list 'quote new))) (value new)))))))))) (defun add-custom-keyword-hint-fn (key uterm1 uterm2 state) ; We translate uterm1 and uterm2 to check the syntactic requirements and we ; cause errors if we don't like what we see. BUT we store the untranslated ; uterm1 and uterm2 in the custom-keywords-table! The reason is that the ; invariant on the table cannot insure that the terms there meet the ; requirements -- translated, single-threaded, error-triple signatured terms. ; So when we use the terms we find in the table we have to use trans-eval to ; (re-)translate and evaluate them. Thus, we might as well store the pretty ; versions of the terms in case the user ever looks at them. ; Note: The new entry on the custom-keyword-hints-alist will be of the form ; (key uterm1 uterm2). Uterm1 is the untranslated generator term and uterm2 is ; the untranslated checker term. (declare (xargs :guard (state-p state) :mode :program)) (let ((world (w state)) (ctx 'add-custom-keyword-hint) (allowed-gvars '(val keyword-alist id clause world stable-under-simplificationp hist pspv ctx state)) (allowed-cvars '(val world ctx state))) (er-let* ((term1 (translate-simple-or-error-triple uterm1 ctx world state)) (term2 (translate uterm2 *error-triple-sig* nil '(state) ctx world state))) (cond ((not (keywordp key)) (er soft ctx "The first argument of add-custom-keyword-hint must be a keyword ~ and ~x0 is not!" key)) ((member-eq key *hint-keywords*) (er soft ctx "It is illegal to use the name of a primitive hint, ~e.g., ~x0, as ~ a custom keyword hint." key)) ((assoc-eq key (table-alist 'custom-keywords-table (w state))) (er soft ctx "It is illegal to use the name of an existing custom keyword hint, ~ e.g., ~x0. Use remove-custom-keyword-hint first to remove the ~ existing custom keyword hint of that name." key)) ((not (subsetp-eq (all-vars term1) allowed-gvars)) (er soft ctx "The second argument of add-custom-keyword-hint must be a term ~ whose free variables are among ~%~Y01, but you provided the term ~ ~x2, whose variables include~%~Y31." allowed-gvars nil uterm1 (set-difference-eq (all-vars term1) allowed-gvars))) ((not (subsetp-eq (all-vars term2) allowed-cvars)) (er soft ctx "The :checker argument of add-custom-keyword-hint must be a term ~ whose free variables are among ~%~Y01, but you provided the term ~ ~x2, whose variables include~%~Y31." allowed-cvars nil uterm2 (set-difference-eq (all-vars term2) allowed-cvars))) (t (state-global-let* ((inhibit-output-lst (cons 'summary (@ inhibit-output-lst)))) (let ((val (list uterm1 uterm2))) ; WARNING: Each term is UNtranslated (er-progn (table-fn 'custom-keywords-table (list (kwote key) (kwote val)) state (list 'table 'custom-keywords-table (kwote key) (kwote val))) (table-fn 'custom-keywords-table 'nil state '(table custom-keywords-table)))))))))) (link-doc-to set-non-linear switches-parameters-and-modes set-non-linearp) (link-doc-to set-let*-abstraction switches-parameters-and-modes set-let*-abstractionp) (link-doc-to set-ld-skip-proofs switches-parameters-and-modes set-ld-skip-proofsp) #-acl2-loop-only (defmacro reset-prehistory (&rest args) (declare (ignore args)) nil) #+acl2-loop-only (defmacro reset-prehistory (&whole event-form &optional permanent-p doc) ; Warning: See the Important Boot-Strapping Invariants before modifying! ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". ":Doc-Section switches-parameters-and-modes reset the prehistory~/ ~bv[] Examples: (reset-prehistory) ; restart command numbering at 0 (reset-prehistory nil) ; same as above (reset-prehistory t) ; as above except also disable ubt-prehistory~/ General Forms: (reset-prehistory) (reset-prehistory permanent-p) (reset-prehistory permanent-p doc-string) ~ev[] where ~c[permanent-p] is ~c[t] or ~c[nil], and ~c[doc-string] is an optional ~il[documentation] string not beginning with ``~c[:Doc-Section] ...''. After execution of this command, ACL2 will change the numbering provided by its ~il[history] utilities so that this ~c[reset-prehistory] command (or the top-level compound ~il[command] containing it, which for example might be an ~ilc[include-book]) is assigned the number 0. The only way to undo this command is with command ~ilc[ubt-prehistory]. However, even that is disallowed if ~c[permanent-p] is ~c[t]. Note that the second argument of ~ilc[certify-book], which specifies the number of commands in the certification world (i.e., since ground-zero), is not sensitive to ~c[reset-prehistory]; rather, it expects the number of commands since ground-zero. To see such commands, ~c[:]~ilc[pbt]~c[ :start]. A ~c[reset-prehistory] event with value ~c[nil] for ~c[permanent-p] (the default) is always skipped during ~ilc[certify-book] and ~ilc[include-book] (indeed, whenever ~ilc[ld-skip-proofsp] is ~c[t]). Otherwise one would find the history numbering reset to 0 just by virtue of including (or certifying) a book ~-[] probably not what was intended. ~l[ubt-prehistory] for how to undo a ~c[reset-prehistory] command that does not have a ~c[permanent-p] of ~c[t].~/" ; Warning: See the Important Boot-Strapping Invariants before modifying! (declare (xargs :guard (member-eq permanent-p '(t nil)))) (list 'reset-prehistory-fn (list 'quote permanent-p) 'state (list 'quote doc) (list 'quote event-form))) #+acl2-loop-only (defun checkpoint-world (flushp state) (declare (ignore flushp state)) nil) #-acl2-loop-only (progn (defvar *checkpoint-world-len-and-alist-stack* nil) (defmacro checkpoint-world-len-and-alist () '(car *checkpoint-world-len-and-alist-stack*)) (defun checkpoint-world1 (flushp wrld state) ; When flushp is true, we are promising never to undo back past wrld. For ; example, we could be calling checkpoint-world1 when finishing the boot-strap ; or executing (reset-prehistory t), but not when executing (reset-prehistory ; nil). (let (saved-alist) (loop for entry in (known-package-alist state) do (let ((pkg (find-package (package-entry-name entry)))) (assert pkg) (when flushp (do-symbols (sym pkg) (when (get sym '*undo-stack*) (setf (get sym '*undo-stack*) nil)))) (do-symbols (sym pkg) (let ((alist (get sym *current-acl2-world-key* :unfound))) (when (not (eq alist :unfound)) (push (cons sym (copy-alist alist)) saved-alist)))))) (let ((new (list* wrld (length wrld) saved-alist))) (cond (flushp (setq *checkpoint-world-len-and-alist-stack* (list new))) (t (push new *checkpoint-world-len-and-alist-stack*)))))) (defun checkpoint-world (flushp state) (revert-world-on-error (let* ((wrld0 (w state)) (wrld (scan-to-command wrld0))) (set-w 'retraction wrld state) (checkpoint-world1 flushp wrld state) (set-w 'extension wrld0 state) (value nil))) nil) ) (defun reset-kill-ring (n state) ":Doc-Section History save memory by resetting and perhaps resizing the kill ring used by ~ilc[oops]~/ By default, ACL2 holds on to old logical ~il[world]s when you undo ~il[command]s (~pl[ubt]), as documented elswhere; ~pl[oops]. You can free up memory by deleting those old worlds using ~c[reset-kill-ring]. ~bv[] Examples: (reset-kill-ring t state) ; replace each element of the kill ring by nil (reset-kill-ring 2 state) ; create a new kill ring of '(nil nil) (reset-kill-ring 0 state) ; create a new kill ring that is empty (reset-kill-ring nil state) ; just return the length of the kill ring General form: (reset-kill-ring n state) ~ev[] where ~c[n] evaluates either to ~c[t], to ~c[nil], or to a nonnegative integer (a ~ilc[natp]). If ~c[n] evaluates to ~c[t], it is treated as the length of the current kill ring. If ~c[n] is ~c[nil], then the length ~c[k] of the current kill ring is returned as a value triple ~c[(mv nil k state)]. If ~c[n] is a ~ilc[natp], then the kill ring is replaced with a list of ~c[n] ~c[nil]s. In particular, use ~c[(reset-kill-ring 0 state)] to avoid saving any old logical ~il[world]s, at the cost of disabling the effect of the ~ilc[oops] ~il[command].~/~/" (declare (xargs :guard (or (eq n t) (natp n)))) (let ((n (if (eq n t) (length (f-get-global 'undone-worlds-kill-ring state)) n))) (if n (pprogn (f-put-global 'undone-worlds-kill-ring (make-list n) state) (value :invisible)) (value (f-get-global 'undone-worlds-kill-ring state))))) (defun reset-prehistory-fn (permanent-p state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (with-ctx-summarized (cond ((output-in-infixp state) event-form) ((null doc) (msg "( RESET-PREHISTORY ~x0)" permanent-p)) (t (msg "( RESET-PREHISTORY ~x0 ...)" permanent-p))) (cond ((and (not permanent-p) (or (f-get-global 'certify-book-info state) (eq (f-get-global 'ld-skip-proofsp state) 'include-book))) (pprogn (observation ctx "~x0 events with permanent-p=t are skipped ~ when ~s1 books. See :DOC reset-prehistory." 'reset-prehistory (if (f-get-global 'certify-book-info state) "certifying" "including")) (value :skipped))) (t (let* ((wrld (w state)) (event-form (or event-form (list* 'reset-prehistory permanent-p (if doc (list :doc doc) nil)))) (next-absolute-command-number (next-absolute-command-number wrld))) (er-let* ((val (install-event :new-prehistory-set event-form 'reset-prehistory 0 nil nil nil ctx (global-set 'command-number-baseline-info (change command-number-baseline-info (global-val 'command-number-baseline-info wrld) :permanent-p permanent-p :current next-absolute-command-number) wrld) state))) (er-progn (reset-kill-ring t state) (prog2$ (checkpoint-world permanent-p state) (value val))))))))) ; Next we develop memoization table support. We defer this way past hons.lisp ; because some functions we call are defined relatively late (at least that was ; the case when cltl-def-from-name was defined here). (defun memoize-table-chk-commutative (str fn val ctx wrld) ; Function memoize-table-chk does a full check that the given fn/val pair is a ; legal entry for memoize-table in the given world. Here we check in ; particular that if the :commutativity value is supplied in val, then it names ; a lemma in wrld stating the commutativity of fn. This function always ; returns t or causes a hard error using the same string (str) as is used by ; memoize-table-chk. (declare (xargs :guard (and (symbolp fn) (not (eq (getprop fn 'formals t 'current-acl2-world wrld) t)) (symbol-alistp val)))) (let ((commutative (cdr (assoc-eq :commutative val)))) (cond ((null commutative) t) ((not (eql (len (getprop fn 'formals t 'current-acl2-world wrld)) 2)) (er hard ctx "~@0~x1 is not a binary function symbol, so it is illegal to ~ specify a non-nil value of :commutative (here, ~x2) for ~ memoization of this function." str fn commutative)) ((not (symbolp commutative)) (er hard ctx "~@0Attempted to memoize ~x1 with a non-symbolp value of ~ :commutative, ~x2." str fn commutative)) (t (let ((thm (getprop commutative 'theorem nil 'current-acl2-world wrld))) (cond ((null thm) (er hard ctx "~@0The theorem ~x1 specified for :commutative ~ memoization of ~x2 does not exist." str commutative fn)) (t (or (case-match thm (('equal (!fn x y) (!fn y x)) (cond ((and (variablep x) (variablep y) (not (eq x y))) t))) (& nil)) (er hard ctx "~@0The theorem ~x1 specified for :commutative ~ memoization of ~x2 does not have the expected ~ form. See :DOC memoize." str commutative fn))))))))) (defun memoize-table-chk (key val wrld) ; Although this function is generally only called with #+hons, nevertheless we ; define it independently of #+hons so that it has the same definition in the ; hons and non-hons versions of ACL2. ; The usual table guard mechanism provides crude error messages when there is a ; violation. We avoid that problem by causing a hard error. We rely on the ; fact that illegal and hard-error return nil. ; The memoize-table maps :common-lisp-compliant function symbols (to be ; memoized or unmemoized) to nil (unmemoized) or to a non-empty alist that ; stores relevant information, such as the condition (see memoize-form). The ; guard requirement then ensures that when we call the raw Lisp version of fn, ; then since the guard for fn must hold in that case, so does the guard for ; condition-fn. The body of condition-fn can therefore be called in raw Lisp ; on the arguments of any call of fn made in raw Lisp from the ACL2 ; read-eval-print loop. This is important because the memoized function body ; includes code from the body of condition-fn. (let ((ctx '(table . memoize-table)) (str "Illegal attempt to set memoize-table: ") (memoize-table (table-alist 'memoize-table wrld)) (key-formals (if (symbolp key) (getprop key 'formals t 'current-acl2-world wrld) t)) (key-class (symbol-class key wrld)) (condition (and val (cdr (assoc-eq :condition-fn val))))) (let ((result (cond ((eq key-formals t) (er hard ctx "~@0~x1 is not a function symbol." str key)) ((member-eq 'state (stobjs-in key wrld)) (er hard ctx "~@0~x1 takes ACL2's STATE as an argument." str key)) ((and condition (not (all-nils (stobjs-out key wrld)))) (let ((stobj (find-first-non-nil (stobjs-out key wrld)))) (er hard ctx "~@0~x1 returns a stobj, ~x2." str key stobj))) ((member-eq key *hons-primitive-fns*) (er hard ctx "~@0~x1 is a HONS primitive." str key)) ((not (cltl-def-from-name key wrld)) (er hard ctx "~@0~x1 is not a defined ACL2 function." str key)) ((getprop key 'constrainedp nil 'current-acl2-world wrld) (er hard ctx "~@0~x1 is constrained. You may instead wish to memoize a ~ caller or to memoize its attachment (see :DOC defattach)." str key)) ((if (eq key-class :program) (member-eq key *primitive-program-fns-with-raw-code*) (member-eq key *primitive-logic-fns-with-raw-code*)) (er hard ctx "~@0The built-in function symbol ~x1 has associated raw-Lisp ~ code, hence is illegal to memoize." str key)) ((not (symbol-alistp val)) (er hard ctx "~@0Function symbol ~x1 must be associated with a ~ symbol-alistp, unlike ~x2." str key val)) ((let ((pair (assoc-eq :memo-table-init-size val))) (and pair (not (posp (cdr pair))))) (er hard ctx "~@0The :memo-table-init-size must be a positive integer, ~ unlike ~x1." str (cdr (assoc-eq :memo-table-init-size val)))) ((not (memoize-table-chk-commutative str key val ctx wrld)) nil) ; an error was presumably already caused ; The next two checks require that we do not memoize or unmemoize a function ; that is already memoized or unmemoized, respectively. The function ; maybe-push-undo-stack relies on this check. ((and val (cdr (assoc-eq key memoize-table))) (er hard ctx "~@0Function ~x1 is already memoized." str key)) ((and (null val) (null (cdr (assoc-eq key memoize-table)))) (er hard ctx "~@0Cannot unmemoize function ~x1 because it is not ~ currently memoized." str key)) ((and (eq key-class :ideal) val ; memoize, not unmemoize (let* ((pair (assoc-eq :ideal-okp val)) (okp (if pair (cdr pair) (cdr (assoc-eq :memoize-ideal-okp (table-alist 'acl2-defaults-table wrld)))))) (cond ((eq okp t) nil) ((not okp) (er hard ctx "~@0The function symbol ~x1 is in :logic mode ~ but has not had its guards verified. Either ~ run ~x2, or specify :IDEAL-OKP ~x3 in your ~ ~x4 call, or else evaluate ~x5 or ~x6." str key 'verify-guards t 'memoize '(table acl2-defaults-table :memoize-ideal-okp t) '(table acl2-defaults-table :memoize-ideal-okp :warn))) (t ; okp is :warn (prog2$ (warning$-cw 'memoize-table-chk "The function ~x0 to be memoized is in ~ :logic mode but has not had its guards ~ verified. Memoization might therefore ~ not take place; see :DOC memoize." key) nil)))))) ; Finally, check conditions on the memoization condition function. (t (let ((val-formals (and condition (if (symbolp condition) (getprop condition 'formals t 'current-acl2-world wrld) t))) (val-guard (and condition (if (symbolp condition) (getprop condition 'guard *t* 'current-acl2-world wrld) t)))) (cond ((or (eq val nil) (member-eq condition '(t nil))) t) ((eq val-formals t) (er hard ctx "~@0The proposed memoization condition function, ~x1, is ~ neither T, NIL, nor a function symbol known to ACL2." str condition)) ((not (and (symbolp condition) (or (eq key-class :program) (eq (symbol-class condition wrld) :common-lisp-compliant)))) (er hard ctx "~@0Function ~x1 cannot serve as a memoization condition ~ function for function ~x2, because unlike ~x2, ~x1 is ~ not common-lisp-compliant (a logic-mode function that ~ has had its guards verified)." str condition key)) ((not (equal key-formals val-formals)) (er hard ctx "~@0Function ~x1 cannot serve as a memoization condition ~ function for ~x2, because the two functions have ~ different formal parameter lists." str condition key)) ((not (equal (getprop key 'guard *t* 'current-acl2-world wrld) val-guard)) (er hard ctx "~@0Function ~x1 cannot serve as a memoization condition ~ function for ~x2, because the two functions have ~ different guards." str condition key)) (t t))))))) (progn$ (or (global-val 'hons-enabled wrld) (warning$-cw (if val 'memoize 'unmemoize) "The ~#0~[un~/~]memoization request for ~x1 is being ~ ignored because this ACL2 executable is not ~ hons-enabled." (if val 1 0) key)) (and val (let ((stobjs-in (stobjs-in key wrld))) (cond ((and condition (find-first-non-nil stobjs-in)) (let ((input-stobjs (collect-non-x nil stobjs-in))) (observation-cw ctx "The function ~x0 has input stobj~#1~[~/s~] ~&1. The ~ memoization table for ~x0 will be cleared whenever ~ ~#2~[this stobj is~/either of these stobjs is~/any of ~ these stobjs is~] updated. Any update of a stobj may ~ therefore be significantly slower, perhaps by a factor of ~ 5 or 10, when it is an input of a memoized function." key input-stobjs (zero-one-or-more (cdr input-stobjs))))) (t nil)))) result)))) (table memoize-table nil nil :guard (memoize-table-chk key val world)) ; The following code supports print-gv. (defun remove-stobjs-in-by-position (lst stobjs-in) (declare (xargs :guard (and (true-listp lst) (true-listp stobjs-in) (eql (length lst) (length stobjs-in))))) (cond ((endp lst) nil) ((car stobjs-in) (remove-stobjs-in-by-position (cdr lst) (cdr stobjs-in))) (t (cons (car lst) (remove-stobjs-in-by-position (cdr lst) (cdr stobjs-in)))))) (defun alist-to-doublets (alist) (declare (xargs :guard (alistp alist))) (cond ((endp alist) nil) (t (cons (list (caar alist) (cdar alist)) (alist-to-doublets (cdr alist)))))) (defun print-gv1 (fn-guard-stobjsin-args state) (let* ((wrld (w state)) (fn (nth 0 fn-guard-stobjsin-args)) (guard (nth 1 fn-guard-stobjsin-args)) (args (apply-user-stobj-alist-or-kwote (user-stobj-alist state) (nth 3 fn-guard-stobjsin-args) nil)) (formals (formals fn wrld)) (guard-fn (intern-in-package-of-symbol (concatenate 'string (symbol-name fn) "{GUARD}") fn))) ; Note: (nth 2 fn-guard-stobjsin-args) is the stobjs-in of fn, but we don't ; need it. `(flet ((,guard-fn ,formals (declare (ignorable ,@formals)) ,guard)) (,guard-fn ,@args)))) (defun print-gv-fn (evisc-tuple state) (prog2$ (wormhole 'ev-fncall-guard-er-wormhole '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) nil `(pprogn (let ((info ; see save-ev-fncall-guard-er (wormhole-data (f-get-global 'wormhole-status state)))) (cond (info (fms "~x0~|~%" (list (cons #\0 (print-gv1 info state))) (standard-co state) state ',evisc-tuple)) (t (fms "There is no guard violation to debug.~|~%" nil (standard-co state) state nil)))) (value :q)) :ld-prompt nil :ld-missing-input-ok nil :ld-pre-eval-filter :all :ld-pre-eval-print nil :ld-post-eval-print :command-conventions :ld-evisc-tuple nil :ld-error-triples t :ld-error-action :error :ld-query-control-alist nil :ld-verbose nil) (value :invisible))) (defmacro print-gv (&key (evisc-tuple '(evisc-tuple nil ; print-level nil ; print-length (world-evisceration-alist state nil) nil ; hiding-cars ))) ":Doc-Section Other print a form whose evaluation caused a guard violation~/ By default, ACL2 checks input constraints on functions, known as ~il[guard]s. When guards are violated, an informative message is printed; but sometimes it is useful to investigate why the guard check fails. The utility ~c[print-gv] is provided for that purpose. (Alternatively, you may prefer to avoid guards entirely with ~c[(set-guard-checking :none)]; ~pl[set-guard-checking].) ~bv[] Example Forms: (print-gv) (print-gv :evisc-tuple (evisc-tuple 4 4 nil nil)) (print-gv :evisc-tuple (evisc-tuple 4 ; print-level 5 ; print-length (world-evisceration-alist state nil) nil ; hiding-cars )) ~ev[]~/ ~bv[] General Form: (print-gv :evisc-tuple x) ~ev[] where the ~c[:evisc-tuple] argument is optional and defaults to one that hides only the ACL2 logical ~il[world], by printing ~c[] instead of a very large structure. ~l[evisc-tuple] for a discussion of evisc-tuples. To see how one might use ~c[print-gv], consider the following definition. ~bv[] (defun foo (x) (declare (xargs :guard (and (my-first-predicate x) (my-second-predicate x)))) (cdr x)) ~ev[] Now suppose we evaluate a call of ~c[foo] and obtain a guard violation. ~bv[] ACL2 !>(foo 3) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X), which is (AND (MY-FIRST-PREDICATE X) (MY-SECOND-PREDICATE X)), is violated by the arguments in the call (FOO 3). To debug: See :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> ~ev[] We can obtain (in essence) the guard form that was false, as follows. ~bv[] ACL2 !>(print-gv) (LET ((X '3)) (DECLARE (IGNORABLE X)) (AND (MY-FIRST-PREDICATE X) (MY-SECOND-PREDICATE X))) ACL2 !> ~ev[] But which conjunct is to blame? With a bit of editing we can try each conjunct in turn. ~bv[] ACL2 !>(LET ((X '3)) (DECLARE (IGNORABLE X)) (MY-FIRST-PREDICATE X)) T ACL2 !>(LET ((X '3)) (DECLARE (IGNORABLE X)) (MY-SECOND-PREDICATE X)) NIL ACL2 !> ~ev[] Aha! Now we can investigate why the second predicate fails for 3. The following hack will give you access in raw Lisp to the form printed by ~c[(print-gv)]. After a guard violation, just submit this form to raw Lisp: ~bv[] (print-gv1 (wormhole-data (cdr (assoc 'ev-fncall-guard-er-wormhole *wormhole-status-alist*))) state) ~ev[] If you use local ~il[stobj]s (~pl[with-local-stobj]) or stobj fields of stobjs, you may need to edit the output of ~c[print-gv] in order to evaluate it. Consider the following example. ~bv[] (defstobj st fld) (defun g (x st) (declare (xargs :guard (consp x) :stobjs st) (ignore x)) (fld st)) (defun test () (with-local-stobj st (mv-let (result st) (mv (g 3 st) st) result))) (test) ~ev[] Then ~c[:print-gv] yields the result shown below. ~bv[] (FLET ((G{GUARD} (X ST) (DECLARE (IGNORABLE X ST)) (AND (STP ST) (CONSP X)))) (G{GUARD} '3 ||)) ~ev[] In this example you could replace ``~c[||]'' by ``~c[st]'' to obtain a result of ~c[nil]. But similar cases may require the use of a local stobj that is no longer available, in which case you may need to be creative in order to take advantage of ~c[:print-gv]. Here is such an example. ~bv[] (defstobj st2 fld2) (defun g2 (st2) (declare (xargs :guard (null (fld2 st2)) :stobjs st2)) (mv 0 st2)) (defun test2 () (with-local-stobj st2 (mv-let (result st2) (let ((st2 (update-fld2 17 st2))) (g2 st2)) result))) (test2) ~ev[] In this case, ~c[:print-gv] yields the following. ~bv[] (FLET ((G2{GUARD} (ST2) (DECLARE (IGNORABLE ST2)) (AND (ST2P ST2) (NULL (FLD2 ST2))))) (G2{GUARD} ||)) ~ev[] But if you replace ``~c[||]'' by ``~c[st]'', the guard holds; it is only the local stobj, which is no longer available, that produced a guard violation (because its field had been updated to a cons). ~bv[] ACL2 !>(FLET ((G2{GUARD} (ST2) (DECLARE (IGNORABLE ST2)) (AND (ST2P ST2) (NULL (FLD2 ST2))))) (G2{GUARD} st2)) T ACL2 !> ~ev[] Finally, we note that while ~c[print-gv] is a utility for debugging guard violations, in contrast, ~pl[guard-debug] for a utility to assist in debugging failed proofs arising from guard verification.~/" `(print-gv-fn ,evisc-tuple state)) (defun disable-iprint-ar (state) (cond ((iprint-enabledp state) ; A comment in rollover-iprint-ar explains conditions that allow a certain ; multiplier of 4 to enable maintainance of the invariant that the ; maximum-length of the iprint-ar is always at least four times the dimension. ; We support that reasoning here by making sure that we do not create ; successive entries with index 0. Note that compress1 does not change the ; array's alist since the :order in its header is :none. (let* ((iprint-ar (f-get-global 'iprint-ar state)) (last-index (aref1 'iprint-ar iprint-ar 0))) (pprogn (f-put-global 'iprint-ar (compress1 'iprint-ar (acons 0 (list last-index) (if (eql (caar iprint-ar) 0) (cdr iprint-ar) iprint-ar))) state) (mv t state)))) (t (mv nil state)))) (defun enable-iprint-ar (state) (cond ((not (iprint-enabledp state)) ; See the comment in disable-iprint-ar about an invariant. (let* ((iprint-ar (f-get-global 'iprint-ar state)) (last-index (car (aref1 'iprint-ar iprint-ar 0)))) (pprogn (f-put-global 'iprint-ar (compress1 'iprint-ar (acons 0 last-index (if (eql (caar iprint-ar) 0) (cdr iprint-ar) iprint-ar))) state) (mv t state)))) (t (mv nil state)))) (defconst *iprint-actions* '(t nil :reset :reset-enable :same)) (defun set-iprint-fn1 (x state) (cond ((eq x :same) (mv nil state)) ((null x) (mv-let (result state) (disable-iprint-ar state) (cond (result (mv "Iprinting has been disabled." state)) (t (mv "Iprinting remains disabled." state))))) ((eq x t) (mv-let (result state) (enable-iprint-ar state) (cond (result (mv "Iprinting has been enabled." state)) (t (mv "Iprinting remains enabled." state))))) ((member-eq x '(:reset :reset-enable)) (pprogn (f-put-global 'iprint-ar (compress1 'iprint-ar (init-iprint-ar (f-get-global 'iprint-hard-bound state) (eq x :reset-enable))) state) (mv (cond ((eq x :reset-enable) "Iprinting has been reset and enabled.") (t "Iprinting has been reset and disabled.")) state))) (t (mv t state)))) (defun set-iprint-fn (x state) (let ((ctx 'set-iprint)) (mv-let (msg state) (set-iprint-fn1 x state) (cond ((eq msg t) (er soft ctx "Unknown option, ~x0. The legal iprint actions are ~&1." x *iprint-actions*)) (msg (pprogn (observation ctx "~@0" msg) (value :invisible))) (t (value :invisible)))))) (defun set-iprint-hard-bound (n ctx state) (cond ((posp n) (pprogn (f-put-global 'iprint-hard-bound n state) (observation ctx "The hard-bound for iprinting has been set ~ to ~x0." n) (value :invisible))) (t (er soft ctx "The hard-bound for iprinting must be a positive integer, but ~ ~x0 is not." n)))) (defun set-iprint-soft-bound (n ctx state) (cond ((posp n) (pprogn (f-put-global 'iprint-soft-bound n state) (observation ctx "The soft-bound for iprinting has been set ~ to ~x0." n) (value :invisible))) (t (er soft ctx "The soft-bound for iprinting must be a positive integer, but ~ ~x0 is not." n)))) (defmacro set-iprint (&optional (action ':RESET ; default ignored action-p) &key (soft-bound '1 ; default ignored soft-bound-p) (hard-bound '1 ; default ignored hard-bound-p)) ":Doc-Section IO control whether abbreviated output can be read back in~/ When ACL2 pretty-prints large expressions using formatted printing (~pl[fmt]), it may save time and space by printing tokens `~c[#]' or `~c[...]' in place of certain subexpressions. By default this only happens for a few settings such as error and warning messages; ~pl[set-evisc-tuple] for controlling such elision in general. The full expression is unavailable to the user when `~c[#]' or `~c[...]' is printed, but that is easily solved by evaluating the form ~bv[] (set-iprint t) ~ev[] to enable a mode called ``iprinting''. Then, instead of printing `~c[#]' or `~c[...]', ACL2 prints `~c[#@i#]' for ~c[i] = 1,2,3,... ~-[] all in base 10. ACL2 can read back in such `~c[#@i#]' because under the hood, ~c[i] is associated with its corresponding elided form. Thus the term ``iprint'' can be viewed as suggesting ``interactive print'' or ``index print''. We also think of ``iprint'' as suggesting ``~c[i] printing'', to suggest the printing of token `~c[#@i#]'. We call ~c[i] the ``iprint index'' of that token. The following example should suffice to illustrate how to recover elided subexpressions. (Below this example we provide details that may be of interest only to advanced users.) Here we cause an error by defining a macro of one argument and then calling it with two arguments. By default, error messages abbreviate subexpressions deeper than level 5 with `~c[#]' and past the 7th element of a list with `~c[...]'. We see this behavior below. ~bv[] ACL2 !>(defmacro foo (x) x) Summary Form: ( DEFMACRO FOO ...) Rules: NIL Warnings: None Time: 0.02 seconds (prove: 0.00, print: 0.00, other: 0.02) FOO ACL2 !>(foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n)) ACL2 Error in macro expansion: Wrong number of args in macro expansion of (FOO ARG1 (A (B (C (D #))) H I J K L ...)). (See :DOC set-iprint to be able to see elided values in this message.) ACL2 !>(set-iprint t) ACL2 Observation in SET-IPRINT: Iprinting has been enabled. ACL2 !>(foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n)) ACL2 Error in macro expansion: Wrong number of args in macro expansion of (FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#)). ACL2 !>(acl2-count '(FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#))) 23 ACL2 !> ~ev[] Sometimes you may want to supply the abbreviated form not to compute with it, as in the ~ilc[acl2-count] example just above, but so that you can see it. The macro ~ilc[without-evisc] eliminates elision during printing. Below we show two ways to use this utility: first, we use it to print the elided form, and then, we use it instead on the original input form to print the entire error message. ~bv[] ACL2 !>(without-evisc '(FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#))) (FOO ARG1 (A (B (C (D (E (F (G)))))) H I J K L M N)) ACL2 !>(without-evisc (foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n))) ACL2 Error in macro expansion: Wrong number of args in macro expansion of (FOO ARG1 (A (B (C (D (E (F (G)))))) H I J K L M N)). ACL2 !> ~ev[] The documentation above probably suffices for most users. For those who want more details, below we detail all the ways to use the ~c[set-iprint] utility.~/ ~bv[] Example Forms: (set-iprint t) ; enable iprinting (elision with #@i@) (set-iprint nil) ; disable iprinting General Form: (set-iprint action ; t, nil, :reset, :reset-enable, or :same :soft-bound s ; initially 1000 :hard-bound h ; initially 10000) ~ev[] where all arguments are optional, but ACL2 queries for ~c[action] if it is omitted. We defer the explanations of ~c[:soft-bound] and ~c[:hard-bound]. The values for ~c[action] are as follows: ~bq[] ~c[T] ~-[] Enable iprinting. ~c[NIL] ~-[] Disable iprinting. ~c[:RESET] ~-[] Reset iprinting to its initial disabled state, so that when enabled, the first index ~c[i] for which `~c[#@i#] is printed will be 1. Note that all stored information for existing iprint indices will be erased. ~c[:RESET-ENABLE] ~-[] Reset iprinting as with ~c[:reset], and then enable iprinting. ~c[:SAME] ~-[] Make no change to the iprinting state (other than setting the ~c[:soft-bound] and/or ~c[:hard-bound] if specified, as explained below).~eq[] Immediately after a top-level form is read, hence before it is evaluated, a check is made for whether the latest iprint index exceeds a certain bound, ~c[(iprint-soft-bound state)] ~-[] 1000, by default. If so, then the ~c[(iprint-last-index state)] is set back to 0. This soft bound can be changed to any positive integer ~c[k] by calling ~c[set-iprint] with ~c[:soft-bound k], typically ~c[(set-iprint :same :soft-bound k])]. The above ``soft bound'' is applied once for each top-level form, but you may want finer control by applying a bound after the pretty-printing of each individual form (since many forms may be pretty-printed between successive evaluations of top-level forms). That bound is ~c[(iprint-hard-bound state)], and can be set with the ~c[:hard-bound] argument in analogy to how ~c[:soft-bound] is set, as described above. A ``rollover'' is the detection that the soft or hard bound has been exceeded, along with a state update so that the next iprint index will be 1. When a rollover occurs, any index beyond the latest iprint index is no longer available for reading. At the top level of the ACL2 read-eval-print loop, this works as follows: ACL2 reads the next top-level form according to the current iprint state, then handles a rollover if the latest iprint index exceeded the current soft bound. The following log illustrates a rollover, which follows the description above. ~bv[] ACL2 !>(set-iprint t :soft-bound 3) ACL2 Observation in SET-IPRINT: The soft-bound for iprinting has been set to 3. ACL2 Observation in SET-IPRINT: Iprinting has been enabled. ACL2 !>(set-evisc-tuple (evisc-tuple 2 3 nil nil) :sites :ld) (:LD) ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g)) ((A B C . #@1#) (A B C . #@2#) (A B C . #@3#)) ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g)) ((A B C . #@4#) (A B C . #@5#) (A B C . #@6#)) ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))) ((A B C D E F G) (A B C D E F G) (A B C D E F G)) ACL2 !>'(1 2 3 4 5) (1 2 3 . #@1#) ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g)) ((A B C . #@2#) (A B C . #@3#) (A B C . #@4#)) ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))) ((A B C D E F G) (A B C D E F G) (A B C D E F G)) ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))) *********************************************** ************ ABORTING from raw Lisp *********** Error: Out-of-bounds index in #@5#. See :DOC set-iprint. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !> ~ev[] We conclude by mentioning two cases where iprinting and evisc-tuples are ignored. (1) This is typically the case when printing results in raw Lisp outside the ACL2 loop. To use iprinting and evisc-tuples in raw Lisp, use raw-mode; ~pl[set-raw-mode]. In raw-mode, results that are ACL2 objects will be printed in the same way that ACL2 would print those results if not in raw-mode. (2) Iprinting and evisc-tuples are ignored by ~ilc[print-object$], which however is sensitive to many settings that do not affect formatted (~ilc[fmt] etc.) printing; ~pl[print-control]. The reader interested in design and implementation issues considered during the addition of iprinting to ACL2 is invited to read the paper ``Abbreviated Output for Input in ACL2: An Implementation Case Study''; see the proceedings of ACL2 Workshop 2009, ~url[http://www.cs.utexas.edu/users/moore/acl2/workshop-2009/]." (declare (xargs :guard ; the setters deal with illegal values t)) `(er-progn ,@(and hard-bound-p `((set-iprint-hard-bound ,hard-bound 'set-iprint state))) ,@(and soft-bound-p `((set-iprint-soft-bound ,soft-bound 'set-iprint state))) ,(cond (action-p `(set-iprint-fn ,action state)) (t '(er-let* ((ans (acl2-query :set-iprint '("Action" :t t :nil nil :reset :reset :reset-enable :reset-enable :same :same :q :q :? ("reply with :Q to quit, or else with one of the ~ options to set-iprint, which are ~&0 (see :DOC ~ set-iprint)" :t t :nil nil :reset :reset :reset-enable :reset-enable :same :same :q :q)) (list (cons #\0 *iprint-actions*)) state))) (cond ((eq ans :q) (silent-error state)) (t (set-iprint-fn ans state)))))))) ; We develop code for setting evisc-tuples. (defconst *evisc-tuple-sites* '(:TERM :LD :TRACE :ABBREV :GAG-MODE)) (defun set-site-evisc-tuple (site evisc-tuple ctx state) ; This function is untouchable because it assumes that evisc-tuple is legal. ; Note that the special case where site is :trace and evisc-tuple is t is ; handled specially by set-evisc-tuple-fn; set-site-evisc-tuple is not called ; in that case. (declare (xargs :guard (and (member-eq site *evisc-tuple-sites*) (or (null evisc-tuple) (eq evisc-tuple :default) (and (eq site :gag-mode) (eq evisc-tuple t)) (standard-evisc-tuplep evisc-tuple)) (state-p state)))) (case site (:TERM (f-put-global 'term-evisc-tuple evisc-tuple state)) (:ABBREV (f-put-global 'abbrev-evisc-tuple evisc-tuple state)) (:GAG-MODE (f-put-global 'gag-mode-evisc-tuple evisc-tuple state)) (:LD (f-put-global 'ld-evisc-tuple (if (eq evisc-tuple :default) nil evisc-tuple) state)) (:TRACE (set-trace-evisc-tuple (if (eq evisc-tuple :default) nil evisc-tuple) state)) (otherwise (prog2$ (er hard ctx "Implementation Error: Unrecognized keyword, ~x0. ~ Expected evisc-tuple site: ~v1" site *evisc-tuple-sites*) state)))) (defun chk-evisc-tuple (evisc-tuple ctx state) (cond ((or (null evisc-tuple) (eq evisc-tuple :default) (standard-evisc-tuplep evisc-tuple)) (value nil)) (t (er soft ctx "Illegal evisc-tuple argument, ~x0. See :DOC set-evisc-tuple." evisc-tuple)))) (defun set-evisc-tuple-lst (keys evisc-tuple acc ctx state) ; This function is untouchable because it assumes that evisc-tuple is legal. (cond ((endp keys) (value (reverse acc))) (t (pprogn (set-site-evisc-tuple (car keys) evisc-tuple ctx state) (set-evisc-tuple-lst (cdr keys) evisc-tuple (cons (car keys) acc) ctx state))))) (defun set-evisc-tuple-fn1 (keys all-keys evisc-tuple acc ctx state) ; This function is untouchable because it assumes that evisc-tuple is legal. (declare (xargs :guard (and (symbol-listp keys) (symbol-listp all-keys) (standard-evisc-tuplep evisc-tuple) (symbol-listp acc) (state-p state)))) (cond ((endp keys) (let ((lst (reverse acc))) (set-evisc-tuple-lst lst evisc-tuple nil ctx state))) (t (er-let* ((ans (acl2-query :set-evisc-tuple '("Do you wish to set ~s0?" :y t :n nil :all :all :rest :rest :q :q :abort :abort :? ("reply with REST to set ~s0 and all remaining ~ evisc-tuples, ALL to set all evisc-tuples, Q to set only ~ the evisc-tuples already specified, or ABORT to quit ~ without setting any evisc-tuples at all; or reply with Y ~ or N to set or not to set (respectively) ~s0 before ~ considering whether to set other evisc-tuples" :y t :n nil :all :all :rest :rest :q :q :abort :abort)) (list (cons #\0 (string-append (symbol-name (car keys)) "-EVISC-TUPLE"))) state))) (case ans ((:REST :ALL :Q) (let ((lst (case ans (:REST keys) (:ALL all-keys) (:Q (reverse acc))))) (set-evisc-tuple-lst lst evisc-tuple nil ctx state))) (:ABORT (value nil)) (otherwise (set-evisc-tuple-fn1 (cdr keys) all-keys evisc-tuple (if ans (cons (car keys) acc) acc) ctx state))))))) (defun iprint-virginp (state) (and (not (iprint-enabledp state)) (let* ((iprint-ar (f-get-global 'iprint-ar state)) (bound (default 'iprint-ar iprint-ar))) (and (null bound) (int= 0 (iprint-last-index* iprint-ar)))))) (defun set-evisc-tuple-fn (evisc-tuple iprint iprint-p sites sites-p state) ; This function checks standard-evisc-tuplep, so it need not be untouchable. (let ((ctx 'set-evisc-tuple) (fail-string "The legal values for :SITES are :ALL and either members ~ or subsets of the list ~x0. The :SITES ~x1 is thus ~ illegal. See :DOC set-evisc-tuple.")) (cond ((eq evisc-tuple t) (cond ((null sites) (er soft ctx "The :SITES argument is required for set-evisc-tuple when a ~ value of T is specified, in which case :SITES should ~ specify :TRACE and/or :GAG-MODE.~ ~ See :DOC ~ set-evisc-tuple.")) ((not (or (and (true-listp sites) (subsetp-eq sites *evisc-tuple-sites*)) (member-eq sites *evisc-tuple-sites*))) (er soft ctx fail-string *evisc-tuple-sites* sites)) (t (let ((sites (if (symbolp sites) (list sites) sites))) (cond ((not (subsetp-eq sites '(:trace :gag-mode))) (er soft ctx "You have called set-evisc-tuple with an ~ `evisc-tuple' of T. The only :SITES for which ~ this is legal are :TRACE and :GAG-MODE, but you ~ have supplied ~&0." sites)) (t (pprogn (cond ((member-eq :TRACE sites) (set-trace-evisc-tuple t state)) (t state)) (cond ((member-eq :GAG-MODE sites) (f-put-global 'gag-mode-evisc-tuple t state)) (t state)) (value sites)))))))) (t (er-progn (chk-evisc-tuple evisc-tuple ctx state) (cond (iprint-p (set-iprint-fn iprint state)) ((not (iprint-virginp state)) (value nil)) (t (set-iprint))) (cond ((null sites-p) (set-evisc-tuple-fn1 *evisc-tuple-sites* *evisc-tuple-sites* evisc-tuple nil ctx state)) ((eq sites :ALL) (set-evisc-tuple-lst *evisc-tuple-sites* evisc-tuple nil ctx state)) ((and (true-listp sites) (subsetp-eq sites *evisc-tuple-sites*)) (set-evisc-tuple-lst sites evisc-tuple nil ctx state)) ((member-eq sites *evisc-tuple-sites*) (set-evisc-tuple-lst (list sites) evisc-tuple nil ctx state)) (t (er soft ctx fail-string *evisc-tuple-sites* sites)))))))) (defmacro set-evisc-tuple (evisc-tuple &key (iprint 'nil ; irrelevant default iprint-p) (sites 'nil ; irrelevant default sites-p)) ":Doc-Section IO control suppression of details when printing~/ ACL2 output is generally printed in full. However, ACL2 can be directed to abbreviate, or ``eviscerate'', objects before printing them, though the use of a so-called ``evisc-tuple''. ~l[evisc-tuple] for a discussion of evisc-tuples. The utility ~c[set-evisc-tuple] modifies certain global evisc-tuples, as explained below, to affect the extent to which ACL2 eviscerates objects during printing, for example during proof output or when printing top-level evaluation results. ~bv[] General Form: (set-evisc-tuple evisc-tuple ; a legal evisc-tuple, or :DEFAULT :iprint val ; one of *iprint-actions* :sites sites ; either :ALL, or an element or a subset of the ; list (:TERM :LD :TRACE :ABBREV) ~ev[] where the value of ~c[:iprint] is passed to ~ilc[set-iprint], ~c[:sites :all] abbreviates ~c[:sites '(:TERM :LD :TRACE :ABBREV)], and other documentation is provided below. Note that all arguments are evaluated. ~l[without-evisc] for how to avoid evisceration for ACL2 output. The following example illustrates an invocation of ~c[set-evisc-tuple] that limits the print-level to 3 ~-[] only three descents into list structures are permitted before eviscerating a subterm ~-[] and limits the print-length to 4 ~-[] only the first four elements of any list structure will be printed. ~bv[] ACL2 !>(set-evisc-tuple (evisc-tuple 3 ; print-level 4 ; print-length nil ; alist nil ; hiding-cars ) :iprint :same ; better yet, T :sites :all) (:TERM :LD :TRACE :ABBREV) ACL2 !>'((a b ((c d)) e f g) u v w x y) ((A B (#) E ...) U V W ...) ACL2 !> ~ev[] We recommend however using ~c[:iprint t] so that eviscerated terms may be read back in; ~pl[set-iprint]. Indeed, the ~c[:iprint] argument is required as a reminder to the user to consider that issue, unless iprinting has been enabled at least once. If ~c[:sites] or a required ~c[:iprint] argument is omitted, however, ACL2 will query the user for the missing arguments rather than causing an error. ACL2 eviscerates by default only in a few cases, primarily in informational messages for errors, warnings, and queries (i.e., in the ~c[:EVISC] case below). Users can modify the default behavior by supplying a suitable argument to ~c[set-evisc-tuple]. The argument may be ~c[:default], which denotes the evisceration provided when ACL2 starts up. Otherwise that argument is an evisc-tuple, which is either ~c[nil] (no evisceration) or as described above. Moreover, there are four evisc-tuple ``evisceration contexts'', each with its own evisceration control. The value returned by ~c[set-evisc-tuple] indicates the evisceration contexts whose evisc-tuple has been set. The evisceration contexts are as follows, all of which use a default value of ~c[nil] for the hiding-cars. Accessors are also shown for retrieving the corresponding evisc-tuple. ~bq[] o ~c[:TERM] ~-[] used for printing terms. The accessor is ~c[(term-evisc-tuple flg state)], where ~c[flg] is ignored if ~c[set-evisc-tuple] has been called for ~c[:term] with value other than ~c[:default], and otherwise (hence initially): a ~c[flg] of ~c[nil] indicates an evisc-tuple of ~c[nil], and otherwise the term-evisc-tuple has a print-level of 3 and print-length of 4. o ~c[:ABBREV] ~-[] used for printing informational messages for errors, warnings, and queries. Initially, the alist abbreviates the ACL2 ~c[world], print-level is 5, and print-level is 7. The accessor is ~c[(abbrev-evisc-tuple state)]. o ~c[:GAG-MODE] ~-[] used for printing induction schemes (and perhaps, in the future, for other printing) when ~il[gag-mode] is on. If gag-mode is off, the value used for this ~il[evisc-tuple] is ~c[(term-evisc-tuple nil state)]. But if gag-mode is on (i.e., ~c[(gag-mode)] evaluates to a non-~c[nil] value), then with one exception, the value is an evisc-tuple or ~c[nil], to be used in gag-mode for printing of induction schemes and, during proofs, the ``The non-trivial part of the guard conjecture''. The exceptional value is ~c[t], which indicates that in gag-mode, no printing of induction schemes should be and the guard conjecture should be printed using ~c[(term-evisc-tuple t state)]. The accessor is ~c[(gag-mode-evisc-tuple state)]. o ~c[:LD] ~-[] used by the ACL2 read-eval-print loop. The accessor is ~c[(]~ilc[ld-evisc-tuple]~c[ state)]. o ~c[:TRACE] ~-[] used for printing ~il[trace] output. No accessor is available (though in raw Lisp, ~c[(trace-evisc-tuple)] returns the trace-evisc-tuple).~eq[] Each context ~c[ectx] also has an updater, ~c[(set-ectx-evisc-tuple val state)], where ~c[val] is a legal value for ~c[set-evisc-tuple] as described above: ~c[:default] or an ~il[evisc-tuple] (possibly ~c[nil]).~/ Note that the ~il[break-rewrite] commands and the ~il[proof-checker] generally do their printing using the term-evisc-tuple.~/" `(set-evisc-tuple-fn ,evisc-tuple ,iprint ,iprint-p ,sites ,sites-p state)) (defmacro top-level (form &rest declares) ":Doc-Section Other evaluate a top-level form as a function body~/ Some forms, such as calls of ~ilc[with-local-stobj], are illegal when supplied directly to the ACL2 top-level loop. The macro ~c[top-level] provides a workaround in such cases, by defining a temporary ~c[:]~ilc[program]-mode function named ~c[top-level-fn] whose only argument is ~c[state] and whose body is the form to be evaluated. When the call of ~c[top-level] returns there is no change to the existing ACL2 logical ~il[world]. The following edited log illustrates all of the above points. ~bv[] ACL2 !>:pbt 0 0 (EXIT-BOOT-STRAP-MODE) ACL2 !>(defstobj st fld) Summary Form: ( DEFSTOBJ ST ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) ST ACL2 !>(top-level (with-local-stobj st (mv-let (result st) (let ((st (update-fld 17 st))) (mv (fld st) st)) result))) 17 ACL2 !>(top-level (with-local-stobj st (mv-let (result st) (let ((st (update-fld 17 st))) (mv (fld st) st)) (mv nil result state)))) 17 ACL2 !>(top-level (with-local-stobj st (mv-let (result st) (let ((st (update-fld 17 st))) (mv (fld st) st)) (mv result state)))) (17 ) ACL2 !>:pbt 0 0 (EXIT-BOOT-STRAP-MODE) d 1:x(DEFSTOBJ ST FLD) ACL2 !> ~ev[]~/ Each argument of ~c[top-level] after the first should be a ~ilc[declare] form or ~il[documentation] string, as the list of these extra arguments will be placed before the first argument when forming the definition of ~c[top-level-fn]. ~ilc[Top-level] generates a call of ~ilc[ld], so that the value returned is printed in the normal way. The call of ~ilc[top-level] itself actually evaluates to ~c[(mv erp :invisible state)], where ~c[erp] is ~c[t] if and only evaluation of the call of ~c[top-level-fn] caused an error, which normally results in no additional output. (For details about ``caused an error'', see the definition of ~c[top-level] in the ACL2 source code, and ~pl[ld-error-action].)~/" `(mv-let (erp val state) (ld '((pprogn ; ensure initialization (f-put-global 'top-level-errorp nil state) (value :invisible)) (with-output :off :all :on error (defun top-level-fn (state) (declare (xargs :mode :program :stobjs state) (ignorable state)) ,@declares ,form)) (ld '((pprogn (f-put-global 'top-level-errorp t state) (value :invisible)) (top-level-fn state) (pprogn (f-put-global 'top-level-errorp nil state) (value :invisible))) :ld-post-eval-print :command-conventions :ld-error-action :return :ld-error-triples t) (with-output :off :all :on error (ubt! 'top-level-fn))) :ld-pre-eval-print nil :ld-post-eval-print nil :ld-error-action :error ; in case top-level-fn fails :ld-error-triples t :ld-verbose nil :ld-prompt nil) (declare (ignore erp val)) (mv (@ top-level-errorp) :invisible state))) ; Essay on Defattach ; In this essay we discuss both the implementation of defattach and its logical ; characterization. ; We begin by introducing some terminology. We refer to the "substitution of" ; a defattach event, i.e. its "attachment substitution" or "attachment-alist", ; as the functional substitution specified in the event. An "attachment pair" ; is an ordered pair of function symbols f and g such that g is the ; "current attachment" of f: that is, is a pair from the substitution of ; a defattach event active in the current ACL2 world. If is an ; attachment pair, then the corresponding "attachment axiom" is defined to be ; the formula (equal (f x1 ... xk) (g x1 ... xk)), where the xi are the formals ; of f. When we treat this attachment axiom as an event, we consider it ; logically to be a defun event that introduces f, in an "evaluation history" ; as described below. Note that we only admit attachment pairs with the ; same signatures (up to renaming of formals). ; In what follows we allow defaxiom events. However, we insist on the ; following Defaxiom Restriction for Defattach: no ancestor (according to the ; transitive closure of the immediate-supporter relation) of a defaxiom event ; has an attachment. ; Most of the focus in this essay is on logical issues related to defattach, ; but we first say a few words about the implementation. ; In one sense we must disallow attachments to functions introduced by defun, ; since we need to generate our own defuns for attachable functions. We can ; allow such attachments for logical purposes, however. Consider the following ; silly example. ; (defstub f (x) t) ; (defun g (x) ; (f x)) ; (encapsulate ((h (x) t)) ; (local (defun h (x) (g x))) ; (defthm h-prop ; (equal (h x) (g x)))) ; Imagine attaching acl2-numberp to f and also to h. Our proof obligations are ; obtained by applying the attachment substitution to the conjunction of the ; constraints on f and h, in this case (equal (h x) (g x)). Thus we must prove ; (equal (acl2-numberp x) (g x)) in the current ACL2 world. But in that world ; we do not know that g is acl2-numberp. The solution is to attach ; acl2-numberp to g as well. Defattach has a syntax, :attach nil, that ; specifies that an attachment is not to be installed for execution; and that ; syntax would need to be used in the case of g. ; Thus, we only allow attachments for execution to functions introduced in ; signatures of encapsulate events. (We do not expect attachments to defchoose ; functions, which can be simulated anyhow by using suitable wrappers. But if ; such support becomes necessary, then we can probably provide it.) ; Of course, users can experiment with defattach without concern for proof ; obligations, by wrapping skip-proofs around defattach events. ; Since ACL2 supports two theories -- the current theory and its corresponding ; evaluation theory (described below) -- we add an argument to all ev functions ; (ev-w, ev-fncall, etc.), aok, specifying whether or not to use attachments. ; We will tend to use attachments at the top level but not in logical contexts, ; hence not under waterfall-step except in subroutines where it is again ; appropriate, such as evaluation of syntaxp expressions (ev-synp), meta rules, ; and clause-processor rules (see the Essay on Correctness of Meta Reasoning). ; Note that ev-fncall-rec-logical really needs this information. With this ; fine-grained control of which of the two theories to use, we can for example ; arrange that mfc-rewrite behave the same inside syntaxp as does rewrite (at ; the top level), as suggested by Robert Krug. ; We next describe the implementation of defattach as an event. In particular: ; Why is it a separate event type, rather than a macro that generates some ; other kind of event? ; We first considered following the precedent of :induction rules, using ; defthm. But the user might find it unintuitive if defattach were skipped in ; :program mode, yet defthm is so skipped. Moreover, we expected to generate ; Lisp code to do the attachment, but defthm doesn't generate any 'cltl-command ; property. ; We thus tentatively decided to implement defattach as a table event. As a ; bonus, implementing defattach as a table event meant that we could follow the ; precedent of memoize/unmemoize. ; However, using a table event would be awkward. If several attachments are ; made together (say, because several functions share a single constraint), ; then in order to remove one of those attachments, soundness demands that we ; remove them all. (For, why did we require them all to be attached together ; in the first place? Perhaps because we needed all those instantiations to be ; made together in order to prove the instantiated constraint.) So undoing ; can't be done one function at a time, really; it needs to be atomic. That ; could be managed, but it "feels" like it could potentially be awkward with ; table events. ; A bigger concern is that through Version_3.6.1, table events have not ; involved proof obligations, as indicated by a comment at the top of table-fn. ; Even merely expressing proof obligations in the language of tables seems ; awkward, unless perhaps we explicitly name each theorem that must be proved ; and refer to it by name in the table event. And note that although we can do ; some fancy things within the context of table events (for example memoize ; generates a 'cltl-command), it seems much simpler not to have to do so in ; this case. ; It seems more natural, then, to invent a new event to handle attachment, ; rather than having defattach be a macro that expands to an existing event. ; At the time we started our implementation of defattach, there seemed to be ; fewer than 40 occurrences of set-body in the code, as determined by: ; fgrep -i set-body /projects/acl2/devel/saved/*.lisp | wc -l ; So, we used set-body as a model for adding a new event. ; In general, we pick a canonical representative for the set of function ; symbols to which a defattach event is making attachments; call that ; representative f. Then the 'attachment property of f is an alist matching ; function symbols to their attachments (including f); but the other function ; symbols in the nest have an 'attachment property of f -- reminiscent of ; handling of the 'constraint-lst property. We maintain the invariant that for ; every function symbol g with an attachment h to its code, g is associated ; with h using the lookup method described above: if g is associated with an ; alist then h is the result of looking up g in that alist, else g is ; associated with a "canonical" symbol g' that is associated with an alist ; associating g with h. (Note that this notion of "canonical" is not related ; to the notion of "canonical sibling", described elsewhere.) ; Turning now to logical issues: ; The fundamental guiding notion is that the attachment pairs extend the ; session's set of axioms to form what we call the "evaluation theory". We say ; more about this theory (and its consistency) below. But we also want to ; support the use of defattach for specifying refinements, with the following ; understanding: "refinement" means that the new function definitions, together ; with the attachment axioms, extend the current theory (to the evaluation ; theory), and this extension remains consistent, indeed with a standard model ; if there are no defaxiom events. Moreover, it would be a kindness to the ; user if whenever an attachment substitution is subsequently used for ; functional instantiation, the resulting proof obligations generated are all ; dispatched without proof because they were cached at the time the defattach ; event was admitted. However, we do not restrict the design to ensure ; definitively that this "kindness" must take place. ; Our logical characterization is based on the idea of an "evaluation theory", ; obtained by extending the current theory with the attachment axiom for each ; attachment pair. We show that evaluation in the top-level loop is performed ; with respect to the evaluation theory. Below we also show -- and this is ; where most of our effort lies -- that the evaluation theory is consistent, ; assuming that there are no defaxiom events in the current history. More ; generally, we show that the evaluation theory is contained in the theory of a ; "evaluation history" containing no new defaxiom events, which replaces ; constraints in the original history by attachment axioms. Consider the ; following example. ; (encapsulate ((f1 (x) t)) ; (local (defun f1 (x) x))) ; (defthm f1-property ; (consp (f1 (cons x x))))) ; (defun g1 (x) ; (if (consp x) x (cons x x))) ; (defattach f1 g1) ; Then the corresponding evaluation history could be as follows, where we ; ignore the defattach event itself. ; (defun g1 (x) ; (if (consp x) x (cons x x))) ; (defun f1 (x) ; (g1 x)) ; Of course, we could then prove that the original constraint holds for f1. ; (defthm f1-property ; (consp (f1 (cons x x)))) ; Indeed, that is critical: in order to show that the theory of the evaluation ; history extends that of the original history, we must guarantee that the ; original constraints on attached functions (hence on f1 in the example above) ; are provable in the evaluation history. In the example above, notice that we ; have replaced the constraint of f1 by its definition using the attachment ; axiom. The property exported from the original encapsulation on f1 now ; becomes a top-level theorem, which is provable because (consp (g1 (cons x ; x))) is provable, as this is the proof obligation generated by the defattach, ; and g1 and f1 are provably equal by the attachment axiom. ; But we must be careful. Consider for example: ; (encapsulate ((f1 (x) t)) ; (local (defun f1 (x) x))) ; (defun g1 (x) ; (not (f1 x))) ; (defattach f1 g1) ; Of course, g1 satisfies the constraint on f1, which is trivial. Also notice ; that we can prove (thm (not (equal (f1 x) (g1 x)))) before the defattach ; event. Yet after the defattach, (equal (f1 x) (g1 x)) will always compute to ; t, not nil! ; This example motivates an acylicity condition on functions and their ; attachments, explained below, that is sufficient for supporting a ; characterization of defattach events in terms of evaluation histories. We ; next provide some motivation, before presenting the appropriate foundational ; theory. ; Consider first a defattach event that attaches certain gi to fi, where the fi ; are all the functions introduced by some individual encapsulate event and the ; gi are all defined before that encapsulate. Imagine replacing the ; encapsulate by the attachment axioms discussed above, i.e., defining each fi ; to be the corresponding gi. Before such replacement, the encapsulate's ; constraint is guaranteed to hold of the gi by our conditions for admitting ; the defattach event; therefore the original constraint on the fi trivially ; holds when we replace the encapsulate by these attachment axioms. Thus, the ; replacement of the encapsulate by the attachment axioms gives us a stronger ; theory than the original theory; so it's pretty clear that the events after ; the encapsulate remain admissible after that replacement -- not that the ACL2 ; prover could actually dispatch all proof obligations, but that all proof ; obligations are indeed theorems, since the history's axiom base has been ; strengthened. ; But in general, the gi might be defined *after* introduction of the ; corresponding fi. For example, one can imagine that f1 is the :logic mode ; built-in function symbol too-many-ifs and g1 is defined by a user (much ; later) to be an efficient implementation. Notice that in the first example ; displayed above, the definition of g1 could presumably have been made before ; f1, while that is not the case in the second (problematic) example above. ; Thus, although we need not insist that each gi be introduced before fi, we do ; insist that the definition of gi can be relocated to occur before the ; introduction of fi. We thus provide a criterion that considers gi to be an ; "extended ancestor" of fi, the motivating idea being that gi is an ancestor ; (supporter) of fi in the evaluation history (because gi supports the ; definition of fi in the attachment axiom for ) and to insist that this ; extended ancestor relation is cycle-free. More details are provided in the ; foundational theory presented below and considered at length in the Essay on ; Merging Attachment Records. ; Of course, if the acyclicity check succeeds then it continues to succeed when ; we remove attachment pairs. But suppose one defattach event attaches to f1 ; and f2 while a second attaches to f1 and f3. It would be a mistake to leave ; the attachment of f2 in place when attaching to f1 and f3, as illustrated by ; the following example. For simplicity, we abuse notation a little, sometimes ; writing a zero-ary function symbol, f, to denote a constant, (f), and ; sometimes writing a constant, e.g. 0, to denote a zero-ary function returning ; that value. ; (defstub f1 ...) ; constrain f2=f1 ; constrain f3=f1 ; (defattach ((f1 0) (f2 0))) ; (defattach ((f1 1) (f3 1))) ; If we don't eliminate the attachment to f2 when evaluating the second ; defattach, then we are left with the following, which violates the contraint ; f2=f1! ; f1=1 ; f2=0 ; f3=1 ; We now make a more careful argument that the evaluation theory is contained ; in the theory of some so-called evaluation history. To recap: We have ; specified that in order for a defattach event to be admissible, then if C is ; the conjunction of the constraints on the attached functions, the functional ; instance C\s must be a theorem, where s is the set of attachment pairs ; specified by the event. We have also discussed the requirement for ; acyclicity of an extended ancestors relation; so next, we turn to specifying ; that relation precisely. We begin by making a couple of remarks. Note first ; that our arguments below will involve permuting top-level events. We thus ; prefer not to think about events other than those at the top level, and we ; disallow defattach events except at the top-level; they are illegal inside ; non-trivial encapsulates. We also assume the absence of trivial encapsulates ; and include-book forms, which can be replaced by the individual events ; within. ; Note that acyclicity checking is necessary even during the include-book pass ; of certify-book (and similarly by any include-book, though we restrict ; attention here to the former case). Consider the following example. ; (defattach f ...) ; first defattach ; (local (defattach f ...)) ; second defattach ; (defattach ...) ; third defattach ; If the first and third defattach events interact to form a loop, but the ; second and third do not, then the first pass of book certification would not ; catch a loop, but the include-book pass would encounter a loop that needs to ; be detected. ; Acyclicity checking will be unnecessary when including a certified book in a ; world where there are no defattach events outside the boot-strap world, ; because the necessary acyclicity checks for the book's defattach events were ; already made when certifying the book, during its include-book pass. (At the ; time of this writing, however, no such optimization is implemented.) But ; otherwise, we need to do an acyclicity check for the defattach events ; encountered while including a book. The following example illustrates why ; this is necessary. Imagine that we have two books, each containing the ; following events. ; (defstub g () t) ; (defun h () (not (g))) ; (defstub f () t) ; Now suppose one of the books concludes with the event ; (defattach f h) ; while the other concludes with the event ; (defattach g f). ; Although each book is individually certifiable, it is logically unsound to ; include both in the same session, since the resulting evaluation theory would ; equate g and h (as both are equated to f), and hence be inconsistent. Of ; course, the extended ancestors relation in this case has a cycle: h supports ; f supports g supports h. ; Our acyclicity check will be made for the following binary relation. Recall ; the immediate ancestors relation between a function symbol f1 introduced by ; event E1 and a function symbol f2 introduced by a later event E2: f1 occurs ; in the formula of E2. In this case we also say that E1 is an immediate ; ancestor of E2. For purposes of our acyclicity check, we are also interested ; in the pair if is an (existing, or about-to-be-introduced) ; attachment pair. (The intuition is that ultimately we will define f to be g, ; hence f will depend on g.) We will refer to this relation as the "extended ; immediate ancestors relation". (Note that the discussion about events above ; refers to defun, defattach, and non-trivial encapsulate events; in ; particular, we assume that E1 and E2 are not defattach events.) ; But we really want an ancestor relation on events. So to be more precise, we ; consider f and g to be related by either relation above if they have siblings ; f' and g' (respectively) that are so related, where two function symbols are ; siblings if they are introduced in the same (encapsulate or mutual-recursion) ; event. We mainly ignore this issue of siblings here, but it is central in ; the Essay on Merging Attachment Records. In that essay we also discuss in ; some detail the extended ancestor relation, which is the transitive closure ; of the union of the ordinary and extended immediate ancestor relations. In ; particular, we defer to that essay how we check for cycles. In summary: our ; ancestor relation is essentially on encapsulate and definitional events. ; This is important for the argument below, in which we complete our ancestor ; relation on events to a total order, essentially rearranging events so that ; for attachment pair , g is introduced before f. Because we want to ; rearrange entire events, our notion of event ancestor incorporates mbe when ; relevant to evaluation (i.e., in defun bodies) and guards. We could perhaps ; avoid considering guards, actually, were it not for the issue related to ; guards raised at the end of the Essay on Correctness of Meta Reasoning. ; Before leaving the notion of extended ancestor relation, we make the ; following claim. ; Defaxiom Acyclicity Claim. If the extended ancestor relation is acyclic ; without considering defaxiom events, then it remains acyclic when taking ; the transitive closure after including defaxiom events in the following ; sense: every event depends on every earlier defaxiom event, and every ; defaxiom event depends on all its immediate ancestors. ; Because of this claim, we check for acyclicity of the extended ancestor ; relation, R, without considering defaxiom events, but when we assume ; acyclicity of R, we include the extra dependencies for defaxiom events ; mentioned in the claim above. We may say "(without defaxioms" to refer to ; the version of the extended ancestor relation that does not include defaxiom ; events as above. ; To prove the claim, suppose for a contradiction that C is a cycle in the ; indicated extension for defaxiom events. Since the extension is assumed ; acyclic before extending by defaxiom events, then C must go through an edge ; linking a defaxiom to a supporting function symbol, f. But when we restrict ; C to start with f, we stay in the original ancestor relation, since no ; attached function is ancestral in any defaxiom, by the Defaxiom Restriction ; for Defattach (stated above). Thus we have a cycle starting with f in the ; original ancestor relation (without extending for defaxiom events), a ; contradiction. ; We turn now to the promised logical characterization of defattach, deferring ; for several paragraphs the proof that it actually holds for admissible ; defattach events. ; The defattach events of a history extend its theory, T, to its corresponding ; "evaluation theory", T', whose axioms include T together with the attachment ; axioms: again, these are the definitions (f x1 ... xn) = (g x1 ... xn), as ; ranges over all current attachment pairs. We assume that the defattach ; events produce an acyclic extended ancestors relation. A key desired ; property is as follows (see also the Evaluation History Theorem below for a ; sort of generalization). ; Proposition (Attachment Consistency). If a history is defaxiom-free, ; then its evaluation theory is consistent and in fact has a standard model. ; (Note: For ACL2(r), we expect that the argument for the proposition is ; essentially unchanged except that a final step, providing a standard model, ; is of course removed. Specifically, this proposition follows from the ; Evaluation History Theorem, as explained below, whose proof we expect goes ; through unchanged for ACL2(r).) ; What is the significance of the above proposition? There are several ; answers, depending on our intended application of defattach. If our ; motivation is testing, then we want to test in a model of the original ; theory; and that will be the case if we evaluate in an extension of that ; theory, which as explained in the next paragraph is the evaluation theory (as ; we evaluate f using g for each attachment pair ). If our motivation is ; attachment to system code, then it's really the same answer: Our system code ; defines a theory of prover functions, such that every model of that theory is ; intended to be correct. Finally, if the motivation is refinement, the idea ; is that our goal is to refine (extend) the original theory with increasingly ; strong axioms about functions; and each defattach will provide such an ; extension, so that the refinement provided by the final defattach event does ; so as well. In all cases, the idea is that we originally specified a class ; of models, and defattach events merely shrink that class of models without ; making it empty (indeed, still including a standard model). ; We characterize the effect of defattach for evaluation in the ACL2 loop as ; follows: it takes place relative to the definitions in the current evaluation ; theory, where for each attachment pair , the guard of f is defined to be ; the guard of g. (Implementation note: *1*f may directly call *1*g, and f may ; directly call g, if f has attachment g.) In particular, as with evaluation ; in the ACL2 loop in general: (a) there is never a guard violation in raw ; Lisp; and (b) if evaluation of a ground term u in the ACL2 loop returns a ; value v, then u=v is a theorem of the evaluation theory. To see that the ; implementation guarantees this behavior, first note that for every attachment ; pair , ACL2 insists that the guard of f logically implies the guard of ; g; hence if we replace the guard of f by the guard of g in every guard proof ; obligation, provability of the original guard proof obligation implies ; provability of this weaker, new guard proof obligation. Another way of ; thinking about evaluation is that it takes place in the evaluation chronology ; but with the guard of f set to the guard of g; thus guard verification for ; the new definition of f (i.e., equating f with g) is trivial. ; To be more precise: The actual guard proof obligation is obtained from the ; basic one, described above, by applying the attachment substitution. The ; argument above goes through unchanged, however, because for each attachment ; pair , f and g are equal in the evaluation history. In most cases we ; imagine that this remark doesn't apply, i.e., the attachment substitution ; does not hit the basic guard implications described above. However, consider ; the case of a generic interpreter using function generic-good-statep as a ; guard, and a concrete interpreter (say, for the JVM) using function ; concrete-good-statep as a guard. There is no reason to expect that ; generic-good-statep is stronger than (or in any way related to) ; concrete-good-statep; but the implication (implies (generic-good-statep x) ; (concrete-good-statep x)) becomes trivial when applying a functional ; substitution mapping generic-good-statep to concrete-good-statep. ; We refer below to the replacement of an encapsulate using attachment ; equations. This refers to the process, for each function symbol being ; provided an attachment, of replacing the encapsulate with a sequence ; consisting first of attachment equations (as defun events) for each function ; f that is both attached and is introduced by the encapsulate, and then ; modifying the encapsulate by eliminating a part of it for each such symbol f ; as follows: remove f from the signature if it's present there, and otherwise ; remove the definition of f from the encapsulate. (If the definition is via a ; sub-encapsulate, then this removal occurs recursively.) If what is left of ; the encapsulate introduces any functions, then we apply the same argument ; showing that the constraint of the original encapsulate is provable in the ; evaluation theory, to conclude that the first pass of that encapsulate is ; provable; hence the remainder of that encapsulate is admissible in the ; evaluation chronology. ; The Attachment Consistency Proposition, above, is immediate from the ; following theorem. Here, we say that a sequence of axiomatic events respects ; a binary relation on function symbols if whenever is in the relation, ; the axiomatic event for f is introduced before the axiomatic event for g. ; Theorem (Evaluation History). Let h1 be a history, and fix legal ; attachments for h1, that is: the extended ancestors relation is acyclic, ; and no attached function is ancestral in any defaxiom. Then there is a ; permutation h2 of h1 that respects the extended dependency relation for h1. ; Let h3 be obtained from any such h2 by replacing each attached encapsulate ; using the attachment equations of an admissible defattach event. Then h3 ; is a history whose theory is the evaluation theory for h1 with respect to ; the given attachments, and whose syntactic dependency relation is the ; extended dependency relation (without defaxioms) of h1 with respect to the ; given attachments. ; Proof. The existence of h2 is immediate by completing the extended dependency ; relation to a total order. So we focus on the claim that h3 is a history ; whose theory is the evaluation theory for h1. Since h2 respects the extended ; dependency relation for h1, h3 is a weak history: roughly speaking, each ; function is introduced before it is used. So our task is to show that all ; proof obligations are met for h3 and that its theory is the evaluation theory ; for h1 with respect to the given attachments. ; Since h2 respects the extended ancestors relation, h2 is a weak history. To ; see that h2 is a history, it suffices to show that all proof obligations are ; met. This is clear if there are no defaxiom events, because each initial ; segment of h2 forms a subset of (the events in) h1 that is closed under ; ancestors in h1. The argument is similar even if there are defaxiom events, ; as follows. Fix an event A in h2; we show that the proof obligations are met ; for A. Let B be the predecessor of A in h2 (possibly A itself) that occurs ; latest in h1, and let let h1' be the predecessors of B (including B) in h1, ; and let h2' be the predecessors of A (including A) in h2. It's easy to see ; that h2' is closed under ancestors in h1'; here we use the fact that h2 is ; closed under the version of the extended ancestors relation that includes ; defaxiom events (see the discussion around the Defaxiom Acyclicity Claim, ; above). Since h1' is a history (as it is an initial segment of h1), then it ; conservatively extends h2', and thus the proof obligations are met for A as ; it sits in h2. ; We return now to the task of showing that proof obligations are met for ; events in h3. It suffices to show that each replacement of an encapsulate by ; corresponding attachment equations, as described above, can only strengthen ; the theory. So for E = E(f1,f2,...) an encapsulate replaced by the use of ; attachment equations A = {fi=gi}, we must show that for E' = E(g1,g2,...), ; the first pass of E' is provable from A together with the predecessors of E' ; in h3. We induct on the length of h3, so it suffices by the inductive ; hypothesis to show that the first pass of E' is provable from A together with ; the predecessors of E in h2. But this is obvious, since the first passes of ; E and E' are provably equal under A. ; We have shown that h3 is a history. Clearly the theory of h3 is axiomatized ; by the theory of h1 together with the attachment equations, so the remaining ; properties of h3 are obvious. -| ; Note that since each defattach event's proof obligations are done without ; taking into account any attachment pairs outside those specified by the ; event, then we can delete any set of defattach events from the world and the ; result is still a legal set of defattach events (as acyclicity is of course ; preserved when we shrink a relation). But we can do something else: we can ; restrict the set of attachments to any set closed under ancestors, and hence ; under that restriction we can even remove pairs within a single defattach ; event. (As of this writing we don't support such removal, however, and ; perhaps we won't ever support it since the implementation is simpler for ; removing entire defattach events when we erase.) A long comment in function ; chk-evaluator-use-in-rule takes advantage of this fact, which we now state ; and prove. ; Attachment Restriction Lemma. Let s0 be a set of attachment pairs and let ; s1 be a subset of s0, such that every function symbol in the domain of s0 ; that is ancestral (including siblings, but ignoring defaxioms) in the ; domain of s1 is also in the domain of s1. Let C0 and C1 be the respective ; lists of constraints on the domains of s0 and s1, and similarly G0 and G1 ; for guard proof obligations, and assume that the functional instances C0\s0 ; and G0\s0 are provable in the current history. Then C1\s1 and G1\s1 are ; also provable in the current history. ; Proof: Note that every function symbol occurring in the constraint or guard ; obligation for attaching to a function symbol, f, is ancestral in f ; (syntactically ancestral, ignoring defaxioms, as defaxioms are not considered ; when gathering such constraints; see function defattach-constraint). ; Therefore any function symbol h in the domain of s0 that occurs in C1 or G1 ; is ancestral in some element of the domain of s1, and therefore h is in the ; domain of s1, by the assumption about ancestors. It follows trivially that ; C1\s1 is a subset of C0\s0 and that G1\s1 is a subset of G0\s0. -| ; We conclude this essay with some remarks. ; Our first implementation of defattach, in Version_4.0, went through ; considerable contortions to support what had seemed a relatively simple ; attachment for too-many-ifs-post-rewrite (then called ; too-many-ifs-post-rewrite-wrapper). The problem was that during the first ; pass of the boot-strap, the functions pseudo-termp and pseudo-term-listp were ; in :program mode, yet were needed for the :guard of the encapsulated ; function. Our solution was to introduce (in Version_4.2) the notion of a ; ``proxy'': a non-executable program-mode function, which can be easily ; introduced using the defproxy macro, and to add keyword argument :skip-checks ; to defattach; see :DOC defproxy. ; There are circumstances where we prohibit attachments to a constrained ; function, by associating a value (:ATTACHMENT-DISALLOWED . msg) with the ; 'attachment property of the function, where msg may be used in error ; messages. This association enforces some restrictions on receiving ; attachments in the cases of meta functions and clause-processor functions; ; see the Essay on Correctness of Meta Reasoning. (A different mechanism ; prohibits the functions in *unattachable-primitives* from receiving ; attachments.) ; We discuss a few aspects of how we handle this :ATTACHMENT-DISALLOWED case. ; Source function redefinition-renewal-mode disallows redefinition when there ; is an attachment, but allows redefinition in the :ATTACHMENT-DISALLOWED case. ; Thus, function renew-name/overwrite preserves the 'attachment property in ; order to preserve the prohibition of attachments; and since redefinition is ; disallowed when there is an attachment, this is actually the only case ; encountered in renew-name/overwrite for which there is a non-nil 'attachment ; property. Finally, note that while the :ATTACHMENT-DISALLOWED case can be ; expected never to hold for a proxy, nevertheless we check this in ; redefinition-renewal-mode before attaching to a proxy. ; End of Essay on Defattach (defun translate-defattach-helpers (kwd-value-lst name-tree ctx wrld state) ; Warning: Keep this in sync with *defattach-keys*. ; We have already checked that kwd-value-lst is a keyword-value-listp without ; duplicate keys each of whose keys is among *defattach-keys*. (cond ((endp kwd-value-lst) (value nil)) (t (let ((key (car kwd-value-lst)) (val (cadr kwd-value-lst))) (er-let* ((rest (translate-defattach-helpers (cddr kwd-value-lst) name-tree ctx wrld state)) (tval (cond ((assoc-eq key rest) (er soft ctx "The key ~x0 occurs more than once in the same context ~ for a defattach event." key)) ((or (and (member-eq key '(:OTF-FLG :HINTS)) (assoc-eq :INSTRUCTIONS rest)) (and (eq key :INSTRUCTIONS) (or (assoc-eq :OTF-FLG rest) (assoc-eq :HINTS rest)))) (er soft ctx "The combination of :INSTRUCTIONS and either :HINTS or ~ :OTF-FLG is illegal for the same context in a defattach ~ event.")) (t (case key (:HINTS (translate-hints+ name-tree val (default-hints wrld) ctx wrld state)) (:INSTRUCTIONS (translate-instructions name-tree val ctx wrld state)) (:OTF-FLG (value val)) (:ATTACH (cond ((member-eq val '(t nil)) (value val)) (t (er soft ctx "The only legal values for keyword :ATTACH in ~ a defattach event are ~&0. The value ~x1 is ~ thus illegal." '(t nil) val)))) (otherwise (value (er hard ctx "Implementation error: Should already have ~ checked keys in process-defattach-args1.")))))))) (value (cons (cons key tval) rest))))))) (defconst *defattach-keys* ; Warning: Keep this in sync with translate-defattach-helpers. '(:hints :instructions :otf-flg :attach)) (defun defattach-unknown-constraints-error (name wrld ctx state) (er soft ctx "Attachment is disallowed in this context, because the function ~x0 has ~ unknown constraints provided by the dependent clause-processor ~x1. ~ See :DOC define-trusted-clause-processor." name (getprop name 'constrainedp '(:error "See defattach-unknown-constraints-error: expected to find ~ a 'constrainedp property where we did not.") 'current-acl2-world wrld))) (defun intersection-domains (a1 a2) (declare (xargs :guard (and (symbol-alistp a1) (symbol-alistp a2)))) (if (consp a1) (if (assoc-eq (caar a1) a2) (cons (caar a1) (intersection-domains (cdr a1) a2)) (intersection-domains (cdr a1) a2)) nil)) (defun process-defattach-args1 (args ctx wrld state erasures explicit-erasures attachment-alist helper-alist-lst skip-checks) ; We accumulate into four arguments as follows: ; - erasures: existing attachment pairs that need to be removed (perhaps before ; reattachment to the cars of some of these pairs) ; - explicit-erasures: functions associated with nil, for explicit de-attachment ; - attachment-alist: list of pairs (f . g) where g is to be attached to f ; - helper-alist-lst: list of alists corresponding positionally to ; attachment-alist, where each element is used for the corresponding proof ; that the guard of f implies the guard of g, and/or to specify :attach nil, ; and is an alist created by translate-defattach-helpers. ; We return an error triple (mv erp val state), where either erp is non-nil to ; signal an error, or else val is of the form (list* erasures attachment-alist ; helper-alist-lst). ; Args is known to be a true-listp. (cond ((endp args) (value (list erasures explicit-erasures attachment-alist helper-alist-lst))) (t (let ((arg (car args)) (see-doc " See :DOC defattach.") (ld-skip-proofsp (ld-skip-proofsp state)) (skip-checks-t (eq skip-checks t)) (unless-ttag (msg " (unless :SKIP-CHECKS T is specified with an active trust ~ tag)"))) (case-match arg ((f g . kwd-value-lst) (er-let* ((helper-alist (cond ((or (eq ld-skip-proofsp 'include-book) (eq ld-skip-proofsp 'include-book-with-locals) (eq ld-skip-proofsp 'initialize-acl2)) (value nil)) ((or (not (keyword-value-listp kwd-value-lst)) (strip-keyword-list *defattach-keys* kwd-value-lst)) (er soft ctx "Each specified attachment must be of the form (F ~ G . LST), where LST is an alternating list of ~ keywords and values (see :DOC ~ keyword-value-listp) whose keys are without ~ duplicates, such that each key is ~v1. The LST ~ specified for the attachment to ~x0 is not of ~ this form.~@2" f *defattach-keys* see-doc)) (t (translate-defattach-helpers kwd-value-lst (cons "DEFATTACH guard obligation for attaching to" f) ctx wrld state))))) (cond ((not (function-symbolp f wrld)) (er soft ctx "Attachment is only legal for function symbols, but ~x0 ~ is not a known function symbol.~@1~@2" f see-doc (let ((f1 (deref-macro-name f (macro-aliases wrld)))) (cond ((not (eq f1 f)) ; We cannot soundly allow attachment to macro-aliases. For, imagine a book ; with non-local macro-alias m1 for f1 followed by local macro-alias m1 for f2, ; followed by a defattach attaching to m1. The proofs during pass 1 of this ; book's certification would be based on attaching to f2, but a later ; include-book would attach to f1. (msg " NOTE: You may have intended to use ~x0 ~ instead of ~x1, which is a macro alias ~ for the function symbol ~x0." f1 f)) ((getprop f 'macro-body nil 'current-acl2-world wrld) (msg " NOTE: ~x0 is a macro, not a function ~ symbol." f)) (t ""))))) ((let ((fns (global-val 'untouchable-fns wrld))) (or (member-eq f fns) (member-eq g fns))) (er soft ctx "The argument~#0~[ ~&0 has~/s ~&0 have~] been placed on ~ untouchable-fns. See :DOC remove-untouchable." (intersection-eq (list f g) (global-val 'untouchable-fns wrld)))) ((and (not skip-checks-t) (not (logicalp f wrld))) (cond ((null g) (er soft ctx "You must specify :SKIP-CHECKS T in order to use ~ defattach with :PROGRAM mode functions, such as ~ ~x0.~@1" f see-doc)) (t (er soft ctx "Only function symbols in :LOGIC mode may have ~ attachments~@0, but ~x1 is in :PROGRAM mode.~@2" unless-ttag f see-doc)))) ((and (member-eq f *unattachable-primitives*) (not (global-val 'boot-strap-flg wrld))) (er soft ctx "It is illegal to add or remove an attachment to the ~ function symbol ~x0 because it is given special ~ treatment by the ACL2 implementation." f)) (t (let ((at-alist (attachment-alist f wrld))) (cond ((eq (car at-alist) :attachment-disallowed) ; Perhaps we should allow this case if skip-checks is true. But let's wait and ; see if there is a reason to consider doing so. (er soft ctx "It is illegal to attach to the function symbol ~x0 ~ because ~@1.~@2" f (cdr at-alist) see-doc)) (t ; at-alist is a legitimate attachment alist (let* ((erasures (cond ((consp at-alist) (append at-alist erasures)) (t erasures))) (constraint-lst (getprop f 'constraint-lst t 'current-acl2-world wrld)) (attach-pair (assoc-eq :ATTACH helper-alist))) (cond ((and (not skip-checks-t) (eq constraint-lst *unknown-constraints*)) (defattach-unknown-constraints-error f wrld ctx state)) ((null g) (cond (helper-alist (er soft ctx "The function symbol ~x0 has been associated ~ with NIL in a defattach event, yet keyword ~ argument~#1~[ ~&1 has~/s ~&1 have~] been ~ supplied for this association, which is ~ illegal.~@2" f (strip-cars helper-alist) see-doc)) (t (pprogn (cond ((null at-alist) (warning$ ctx "Attachment" "The function symbol ~x0 does not ~ currently have an attachment, so the ~ directive to remove its attachment ~ will have no effect." f)) (t (assert$ (consp at-alist) state))) (process-defattach-args1 (cdr args) ctx wrld state erasures ; updated above (cons f explicit-erasures) attachment-alist helper-alist-lst skip-checks))))) ((and (or (null attach-pair) (cdr attach-pair)) ; attaching for execution (not (and skip-checks-t ; If skip-checks is tand we have a non-executable program-mode function, then ; it is legal to attach for execution, so we can move on to the next COND ; branch. (eq (getprop f 'non-executablep nil 'current-acl2-world wrld) :program))) ; Is it legal to attach for execution? A 'constraint-lst property alone isn't ; enough, because a defined function can have a constraint-lst; for example, g ; has a Common Lisp defun (so we can't attach to it) yet it also has a non-nil ; 'constraint-lst property. ; (encapsulate ; ((f (x) t)) ; (local (defun f (x) x)) ; (defun g (x) (f x)) ; (defthm some-prop (equal (g x) (f x)))) ; A 'constrainedp property alone isn't enough either, because defchoose ; introduces a 'constrainedp property of t but doesn't provide a ; 'constraints-lst property. That might be OK, depending on how we gather ; constraints; but defchoose is an unusual case and for now we won't consider ; it. (or (eq constraint-lst t) ; property is missing (not (getprop f 'constrainedp nil 'current-acl2-world wrld)))) ; We cause an error: the function is not permitted an executable attachment. ; The only challenge is to provide a useful error message. (er soft ctx "It is illegal to attach to function symbol ~x0, ~ because it was introduced with ~x1.~@2" f (if (getprop f 'defchoose-axiom nil 'current-acl2-world wrld) 'defchoose 'defun) see-doc)) ((not (symbolp g)) (er soft ctx "Only a function symbol may be attached to a ~ function symbol. The proposed attachment of ~ ~x0 to ~x1 is thus illegal, since ~x0 is not a ~ symbol.~@2" g f see-doc)) ((not (function-symbolp g wrld)) (er soft ctx "Only a function symbol may be attached to a ~ function symbol. The proposed attachment of ~ ~x0 to ~x1 is thus illegal, since ~x0 is not a ~ known function symbol.~@2~@3" g f see-doc (let ((g1 (deref-macro-name g (macro-aliases wrld)))) ; See the comment above explaining why we cannot soundly allow attachment to ; macro-aliases. (cond ((not (eq g1 g)) (msg " NOTE: You may have intended to ~ use ~x0 instead of ~x1, which is ~ a macro alias for the function ~ symbol ~x0." g1 g)) ((getprop g 'macro-body nil 'current-acl2-world wrld) (msg " NOTE: ~x0 is a macro, not a ~ function symbol." g)) (t ""))))) ((and (not skip-checks-t) (not (logicalp g wrld))) (er soft ctx "Attachments must be function symbols in :LOGIC ~ mode~@0, but ~x1 is in :PROGRAM mode.~@2" unless-ttag g see-doc)) ((and (not skip-checks-t) (not (eq (symbol-class g wrld) :common-lisp-compliant))) (er soft ctx "Attachments must be guard-verified function ~ symbols~@0, but ~x1 has not had its guard ~ verified. You may wish to use the macro ~x2 in ~ community book books/misc/defattach-bang.~@3" unless-ttag g 'defattach! see-doc)) ((not (and (equal (stobjs-in f wrld) (stobjs-in g wrld)) (equal (stobjs-out f wrld) (stobjs-out g wrld)))) (er soft ctx "Attachments must preserve signatures, but the ~ signatures differ for ~x0 and ~x1.~@2" f g see-doc)) ((eq f g) (er soft ctx "It is illegal to attach a function to itself, ~ such as ~x0.~@1" f see-doc)) ((and (not skip-checks-t) (eq (canonical-sibling f wrld) (canonical-sibling g wrld))) ; Perhaps we should avoid causing an error if skip-checks is :cycles. But that ; will require some thought, so we'll wait for a complaint. (er soft ctx "The function ~x0 is an illegal attachment for ~ ~x1~@2, because the two functions were ~ introduced in the same event.~@3" g f unless-ttag see-doc)) (t (process-defattach-args1 (cdr args) ctx wrld state erasures ; updated above explicit-erasures (cons (cons f g) attachment-alist) (cons helper-alist helper-alist-lst) skip-checks))))))))))) (& (er soft ctx "Each tuple supplied to a defattach event must be of the ~ form (f g . kwd-value-lst). The tuple ~x0 is thus ~ illegal.~@1" arg see-doc))))))) (defun duplicate-keysp-eq (alist) ; As with duplicate-keysp, return the first pair whose key is bound twice. (declare (xargs :guard (symbol-alistp alist))) (cond ((endp alist) nil) ((assoc-eq (caar alist) (cdr alist)) (car alist)) (t (duplicate-keysp-eq (cdr alist))))) (defun split-at-first-keyword (args) ; Return (mv x y), where args is (append x y), x contains no keywords, and if ; args contains a keyword then (car y) is a keyword. (declare (xargs :guard (true-listp args))) (cond ((endp args) (mv nil nil)) ((keywordp (car args)) (mv nil args)) (t (mv-let (alist kwd-value-lst) (split-at-first-keyword (cdr args)) (mv (cons (car args) alist) kwd-value-lst))))) (defun filter-for-attachment (attachment-alist helpers-lst attach-by-default aa hl) ; We remove pairs from attachment-alist for which no attachment will be made, ; returning the ones that are left together with the corresponding elements of ; helpers-lst. (cond ((endp attachment-alist) (mv (reverse aa) (reverse hl))) (t (let ((pair (assoc-eq :ATTACH (car helpers-lst)))) (cond ((if pair (cdr pair) attach-by-default) (filter-for-attachment (cdr attachment-alist) (cdr helpers-lst) attach-by-default (cons (car attachment-alist) aa) (cons (car helpers-lst) hl))) (t (filter-for-attachment (cdr attachment-alist) (cdr helpers-lst) attach-by-default aa hl))))))) (defconst *defattach-keys-plus-skip-checks* (cons :skip-checks *defattach-keys*)) (defun process-defattach-args (args ctx state) ; Args is known to be a true-listp, as it comes from a macro call. (let ((msg "Illegal arguments for defattach. See :DOC defattach. Note ~ that if the first argument is a symbol, then there should be ~ only two arguments, both of them symbols. Consider instead ~ executing ")) (cond ((null args) (er soft ctx "Defattach must specify at least one attachment. See :DOC ~ defattach.")) ((symbolp (car args)) ; (defattach f ...) (cond ((and (not (keywordp (car args))) (consp (cdr args)) (symbolp (cadr args)) (not (keywordp (cadr args)))) ; (defattach f g ...) (cond ((null (cddr args)) (process-defattach-args `((,(car args) ,(cadr args))) ctx state)) ((and (true-listp args) (eql (length args) 4) (eq (caddr args) :SKIP-CHECKS)) (er soft ctx "~@0the form:~|~%~y1." msg `(defattach (,(car args) ,(cadr args)) ,@(cddr args)))) ((and (true-listp args) (eql (length args) 4) (eq (caddr args) :ATTACH)) (er soft ctx "~@0the form:~|~%~y1." msg `(defattach (,@args)))) (t (er soft ctx "~@0one of the following two forms:~|~%~y1~ ~ or~|~y2." msg `(defattach (,(car args) ,(cadr args)) ,@(cddr args)) `(defattach (,(car args) ,(cadr args) ,@(cddr args))))))) (t (er soft ctx "Illegal defattach form. If the first argument is a symbol, then ~ there must be exactly two arguments, both of which are ~ non-keyword symbols. See :DOC defattach.")))) (t (mv-let (args constraint-kwd-alist) (split-at-first-keyword args) (cond ((not (symbol-alistp args)) (er soft ctx "Illegal arguments for defattach, ~x0. See :DOC defattach." args)) ((duplicate-keysp-eq args) (er soft ctx "A defattach event must specify attachments for distinct ~ function symbols, but ~x0 is associated with a value more than ~ once. See :DOC defattach." (car (duplicate-keysp-eq args)))) ((or (not (keyword-value-listp constraint-kwd-alist)) (strip-keyword-list *defattach-keys-plus-skip-checks* constraint-kwd-alist)) (er soft ctx "Illegal defattach argument list. The tail following the ~ specified pairs of function symbols should be an alternating ~ list of keywords and values (see :DOC keyword-value-listp) ~ whose keys are without duplicates and all belong to the list ~ ~x0. That tail is, however, ~x1. See :DOC defattach." *defattach-keys-plus-skip-checks* constraint-kwd-alist)) (t (let* ((wrld (w state)) (ld-skip-proofsp (ld-skip-proofsp state)) (skip-checks (cadr (assoc-keyword :skip-checks constraint-kwd-alist))) (constraint-kwd-alist (if skip-checks (remove-keyword :skip-checks constraint-kwd-alist) constraint-kwd-alist))) (cond ((and skip-checks (not (eq skip-checks t)) (not (eq skip-checks :cycles))) (er soft ctx "Illegal value for :SKIP-CHECKS (must be ~x0, ~x1, or ~ ~x2): ~x3." t nil :cycles skip-checks)) ((and skip-checks (not (or (global-val 'boot-strap-flg wrld) (ttag wrld)))) (er soft ctx "It is illegal to specify a non-nil value of :SKIP-CHECKS ~ for defattach unless there is an active trust tag.")) (t (er-let* ((tuple (process-defattach-args1 args ctx wrld state nil nil nil nil skip-checks)) (constraint-helpers (cond ((or (eq ld-skip-proofsp 'include-book) (eq ld-skip-proofsp 'include-book-with-locals) (eq ld-skip-proofsp 'initialize-acl2)) (value nil)) (t (translate-defattach-helpers constraint-kwd-alist "DEFATTACH constraint proof obligation" ctx wrld state))))) (let ((erasures (nth 0 tuple)) (explicit-erasures (nth 1 tuple)) (attachment-alist (nth 2 tuple)) (helper-alist-lst (nth 3 tuple)) (attach-by-default (let ((pair (assoc-eq :ATTACH constraint-helpers))) (if pair (cdr pair) t)))) (mv-let (attachment-alist-exec helper-alist-lst-exec) (filter-for-attachment attachment-alist helper-alist-lst attach-by-default nil nil) (value (list constraint-helpers erasures explicit-erasures attachment-alist attachment-alist-exec helper-alist-lst-exec skip-checks))))))))))))))) (defun prove-defattach-guards1 (i n attachment-alist-tail attachment-alist helpers-lst ctx ens wrld state ttree) ; This function is similar to prove-corollaries1, but for the proof obligations ; arising from a defattach stating that for each attachment pair , the ; guard of f implies the guard of g. See prove-defattach-guards for further ; comments. We are currently working on the ith our of n such proofs. (cond ((null attachment-alist-tail) (pprogn (io? event nil state (n) (fms "This concludes the ~#0~[guard proof~/~n1 guard proofs~].~%" (list (cons #\0 (cond ((= n 1) 0) (t 1))) (cons #\1 n)) (proofs-co state) state nil)) (value ttree))) (t (let* ((f (caar attachment-alist-tail)) (g (cdar attachment-alist-tail)) (goal (sublis-fn-simple attachment-alist (fcons-term* 'implies (sublis-var (pairlis$ (formals f wrld) (formals g wrld)) (guard f nil wrld)) (guard g nil wrld)))) (helper-alist (car helpers-lst)) (otf-flg (cdr (assoc-eq :OTF-FLG helper-alist))) (hints (cdr (assoc-eq :HINTS helper-alist))) (instructions (cdr (assoc-eq :INSTRUCTIONS helper-alist))) (ugoal (untranslate goal t wrld))) (pprogn (io? event nil state (ugoal n i) (fms "The~#0~[~/ ~n1 (and last)~/ ~n1~] guard proof obligation ~ is~|~%~y2." (list (cons #\0 (cond ((int= n 1) (assert$ (= i 1) 0)) ((int= i n) 1) (t 2))) (cons #\1 (list i)) (cons #\2 ugoal)) (proofs-co state) state (term-evisc-tuple nil state))) (er-let* ((ttree1 (cond (instructions (proof-checker nil ugoal goal nil instructions wrld state)) (t (prove goal (make-pspv ens wrld :displayed-goal ugoal :otf-flg otf-flg) hints ens wrld ctx state))))) (prove-defattach-guards1 (1+ i) n (cdr attachment-alist-tail) attachment-alist (cdr helpers-lst) ctx ens wrld state (cons-tag-trees ttree1 ttree)))))))) (defun prove-defattach-guards (attachment-alist helpers-lst ctx ens wrld state) ; This function is based on prove-corollaries, but instead of being given ; corollaries, we are given an attachment-alist with pairs (f . g) such that ; the guard of f must be proved to imply the guard of g. Helpers-lst is a list ; of alists corresponding positionally to attachment-alist, each of which binds ; elements of *defattach-keys* to values to help with the respective proof. ; Like prove, we return an error triple; the non-erroneous value is a ttree ; signalling the successful proof of all the goals. ; Note that filter-for-attachment has been applied before calling this ; function. (let ((n (length attachment-alist))) (assert$ (and attachment-alist (int= n (length helpers-lst))) (pprogn (cond ((int= n 1) state) (t (io? event nil state (n) (fms "~%We first consider the ~n0 guard proof ~ obligations.~%" (list (cons #\0 n)) (proofs-co state) state nil)))) (prove-defattach-guards1 1 n attachment-alist attachment-alist helpers-lst ctx ens wrld state nil))))) (defun defattach-constraint-rec (alist full-alist proved-fnl-insts-alist constraint event-names new-entries seen wrld) ; This function is patterned after relevant-constraints1. See the comments ; there. ; Alist is a tail of full-alist, an attachment-alist. (cond ((null alist) (mv constraint event-names new-entries)) (t (mv-let (name x) (constraint-info (caar alist) wrld) ; Note that if x is not *unknown-constraints*, then x is a single constraint if ; name is nil and otherwise x is a list of constraints. (cond ((eq x *unknown-constraints*) (mv x name nil)) ; the nil is irrelevant (t (let ((key (or name (caar alist)))) (cond ((member-eq key seen) (defattach-constraint-rec (cdr alist) full-alist proved-fnl-insts-alist constraint event-names new-entries seen wrld)) (t (let* ((ev (and x ; optimization (event-responsible-for-proved-constraint key full-alist proved-fnl-insts-alist))) (instantiable-fns (and x ; optimization (cond (name (instantiable-ffn-symbs-lst x wrld nil nil)) (t (instantiable-ffn-symbs x wrld nil nil))))) (constraint-alist (and x ; optimization (restrict-alist instantiable-fns full-alist))) (seen (cons key seen))) (cond ((null x) (defattach-constraint-rec (cdr alist) full-alist proved-fnl-insts-alist constraint event-names new-entries seen wrld)) (ev (defattach-constraint-rec (cdr alist) full-alist proved-fnl-insts-alist constraint (add-to-set ev event-names) new-entries seen wrld)) (t (defattach-constraint-rec (cdr alist) alist proved-fnl-insts-alist (if name (conjoin (cons constraint (sublis-fn-lst-simple constraint-alist x))) (conjoin2 constraint (sublis-fn-simple constraint-alist x))) event-names (cons (make proved-functional-instances-alist-entry :constraint-event-name key :restricted-alist constraint-alist :behalf-of-event-name 0) new-entries) seen wrld))))))))))))) (defun defattach-constraint (attachment-alist proved-fnl-insts-alist wrld ctx state) (mv-let (goal event-names new-entries) (defattach-constraint-rec attachment-alist attachment-alist proved-fnl-insts-alist *t* nil nil nil wrld) (cond ((eq goal *unknown-constraints*) (defattach-unknown-constraints-error event-names wrld ctx state)) (t (value (list* goal event-names new-entries)))))) (defun prove-defattach-constraint (goal event-names attachment-alist helper-alist ctx ens wrld state) (assert$ (not (eq goal *unknown-constraints*)) (let ((constraint-bypass-string " Note that we are bypassing constraints that have been proved ~ when processing ~#0~[previous events~/events including ~&1~/the ~ event~#1~[~/s~] ~&1~].")) (cond ((equal goal *t*) (pprogn (io? event nil state (attachment-alist event-names constraint-bypass-string) (fms "~%The attachment~#0~[ trivially satisfies~/s trivially ~ satisfy~] the required constraints.~@1~|~%" (list (cons #\0 attachment-alist) (cons #\1 (cond ((null event-names) "") ((member 0 event-names) (cond ((null (cdr event-names)) (msg constraint-bypass-string 0 event-names)) (t (msg constraint-bypass-string 1 (remove 0 event-names))))) (t (msg constraint-bypass-string 2 event-names))))) (proofs-co state) state nil)) (value nil))) (t (let ((ugoal (untranslate goal t wrld)) (otf-flg (cdr (assoc-eq :OTF-FLG helper-alist))) (hints (cdr (assoc-eq :HINTS helper-alist))) (instructions (cdr (assoc-eq :INSTRUCTIONS helper-alist)))) (pprogn (io? event nil state (attachment-alist event-names constraint-bypass-string ugoal) (fms "~%We now prove that the attachment~#0~[ satisfies~/s ~ satisfy~] the required constraint.~@1~|The goal to prove ~ is~|~%~y2." (list (cons #\0 attachment-alist) (cons #\1 (cond ((null event-names) "") ((member 0 event-names) (cond ((null (cdr event-names)) (msg constraint-bypass-string 0 event-names)) (t (msg constraint-bypass-string 1 (remove 0 event-names))))) (t (msg constraint-bypass-string 2 event-names)))) (cons #\2 ugoal)) (proofs-co state) state nil)) (er-let* ((ttree (cond (instructions (proof-checker nil ugoal goal nil instructions wrld state)) (t (prove goal (make-pspv ens wrld :displayed-goal ugoal :otf-flg otf-flg) hints ens wrld ctx state))))) (value ttree))))))))) ; Essay on Merging Attachment Records ; See the Essay on Defattach for relevant background. Our goal in this Essay ; is to describe the process whereby we check for loops in the extended ; ancestor relation and, more generally, compute that relation as a transitive ; closure. ; Let S be the siblings relation: thus S(f1,f2) holds if f1 and f2 are ; introduced in the same event. Clearly S is an equivalence relation, and we ; will write S(f) to denote the canonical representative of that equivalence ; class, i.e., the canonical sibling of f. A function f is canonical if S(f) ; is f. ; Recall from the Essay on Defattach that our goal is to reorder events in a ; manner that respects not only the original ancestor relation but also a new ; relation containing the ordered pair for each attachment pair , ; which we'll call the "immediate extended ancestor relation". (Motivation: ; The < relation implements the use of an attachment equation in the evaluation ; chronology to define f in terms of g.) However, for each such added to ; that relation, we also add for each sibling g' of g and sibling f' of ; f. We denote the ordinary ancestors relation as <|, which we also consider ; to respect ancestors: we include in this relation whenever f1' and ; f2' are siblings of f1 and f2 (respectively) and f1 is an ordinary ancestor ; of f2. Finally, we define <+ as the transitive closure of the union of < and ; <|. ; The reordering argument in the Essay on Defattach provides the proof ; obligation that <+ must not contain loops. Since f1 <+ f2 if and only if ; S(f1) <+ S(f2), we will use canonical siblings in our algorithms. Indeed, it ; is convenient to think of all of these relations (<, <|, and <+) as being ; defined only on canonical function symbols. Below, we denote as "f-canonical ; and "g-canonical" the function symbols S(f0) and S(g0), respectively, where ; ranges over attachment pairs. ; We turn now to describe our transitive closure algorithm for computing <+. ; We create a data structure for each g-canonical function, g. Each such g is ; associated with its canonical predecessors in each of the relations <| and <, ; and we use notation below such as "FNS" for either of these. Then, we apply ; a "merge" operation to build up to the transitive closure. If we encounter a ; loop, as described below, then we stop and report the loop. ; We present our algorithm using examples. Assume that g1 initially has FNS1 ; as its set of predecessors in either <| or <, and that g2 is similarly ; related to FNS2, where g1 < f1 and g2 < f2. Thus there are attachment pairs ; and such that S(fi') = fi and S(gi') = gi; and we may ; write FNS1 <+ g1 and FNS2 <+ g2. Now suppose f2 is in FNS1. So we have this ; picture: ; FNS1 <+ g1 ; FNS2 <+ g2 < f2 ; We thus associate with g1 a structure exhibiting the following path, obtained ; by merging the paths above using the attachment pair . ; FNS2 <+ g2 < f2 <+ g1 ; More generally, we may or may not involve the "f field" (first component) of ; an attachment pair . Consider the following paths P0 and P1. ; P0: FNS2 <+ g2 < f2 <+ g1 ; P1: FNS4 <+ g4 <+ ... <+ x <+ g3 < f3 ; We could merge using f3 if it is in FNS2, as in our previous example, or ; using g3 if it is in FNS2. Respectively, the resulting merges of P1 into P0 ; are: ; FNS4 <+ g4 <+ ... <+ x <+ g3 < f3 <+ g2 < f2 <+ g1 ; FNS4 <+ g4 <+ ... <+ x <+ g3 <+ g2 < f2 <+ g1 ; Let us call these an "f-merge" and a "g-merge", respectively, of P1 into P0. ; As an optimization, for the case FNS2 <| g2 we only allow a g-merge if P1 ; consists entirely of FNS4 <+ g3 < f3. To justify this optimization, suppose ; that P1 is as above where g4 is not g3. We may restrict ourselves to ; constructing minimal paths. We can thus rule out the case x <| g3, since ; otherwise we have y <+ x <| g3 <| g2, which by minimality and transitivity of ; <| implies that we have y < x <| g2. So x is in FNS2, and we can form a path ; from FNS4 to g1 by doing an f-merge after stripping g3 from P1 (with y such that S(g0) = g; thus, the ; :ext-succ field is the set of S(f0) for all in the :pairs field ; Note that the pairs field is used only by the code that erases attachment ; pairs, not by the code that merges paths. ; The notion of owner, introduced above, is realized in the following ; definition. (defun attachment-component-owner (g path) ; Return the owner of an attachment-component, with :path field of path, that ; is in the :components field of an attachment record with :g field of g. (if path (car path) g)) ; The initial attachment record for a given g has a unique component, with ; owner g and empty path, and whose ext-anc field (denoted fns. (cons 'attachment (append erasures alist))) (defun defattach-fn (args state event-form) (with-ctx-summarized (if (output-in-infixp state) event-form (case-match args (((x y)) (msg "( DEFATTACH (~x0 ~x1))" x y)) (((x y . &)) (msg "( DEFATTACH (~x0 ~x1 ...))" x y)) (((x y) . &) (msg "( DEFATTACH (~x0 ~x1) ...)" x y)) (((x y . &) . &) (msg "( DEFATTACH (~x0 ~x1 ...) ...)" x y)) ((x y) (msg "( DEFATTACH ~x0 ~x1)" x y)) ((x y . &) (msg "( DEFATTACH ~x0 ~x1 ...)" x y)) (& (msg "( DEFATTACH ...)")))) (let* ((wrld (w state)) (proved-fnl-insts-alist (global-val 'proved-functional-instances-alist wrld))) (er-let* ((tuple (chk-acceptable-defattach args proved-fnl-insts-alist ctx wrld state))) (let ((erasures (strip-cars (nth 0 tuple))) (explicit-erasures (nth 1 tuple)) (attachment-alist (nth 2 tuple)) (attachment-alist-exec (nth 3 tuple)) (new-entries (nth 4 tuple)) (ttree (nth 5 tuple)) (records (nth 6 tuple)) (skip-checks (nth 7 tuple))) (let* ((attachment-fns (strip-cars attachment-alist)) (wrld1 (putprop-x-lst1 erasures 'attachment nil wrld)) (wrld2 (cond (attachment-fns (putprop-x-lst1 (cdr attachment-fns) 'attachment (car attachment-fns) (putprop (car attachment-fns) 'attachment attachment-alist wrld1))) (t wrld1))) (wrld3 (cond (new-entries (global-set 'proved-functional-instances-alist (append new-entries proved-fnl-insts-alist) wrld2)) (t wrld2))) (wrld4 (cond (skip-checks wrld3) ; for skip-checks t or :cycles (t (global-set 'attachment-records records wrld3)))) (cltl-cmd (attachment-cltl-cmd (set-difference-assoc-eq erasures attachment-alist-exec) attachment-alist-exec))) (pprogn (let ((implicit-erasures (set-difference-eq erasures explicit-erasures))) (cond (implicit-erasures (observation ctx "The pre-existing attachment~#0~[ ~ is~/s are~] being removed for ~ function~#0~[~/s~] ~&0~@1~@2." implicit-erasures (cond (explicit-erasures (msg ", in addition to the ~ association~#0~[~/s~] ~ with nil provided ~ explicitly for ~&0" explicit-erasures)) (t "")) (cond (attachment-fns (msg ", before adding the ~ requested ~ attachment~#0~[~/s~]" attachment-fns)) (t "")))) (t state))) (install-event :attachments-recorded event-form 'defattach 0 ttree cltl-cmd nil ctx wrld4 state)))))))) ; We now provide support for return-last. (defun chk-return-last-entry (key val wrld) ; Key is a symbol such as prog2$ that has a macro definition in raw Lisp that ; takes two arguments. Val is either nil, denoting that key is not in the ; table; the name of a macro known to ACL2; or (list m), where m is the name of ; a macro known to ACL2. The latter case causes ACL2 to disallow corresponding ; return-last calls at the top level (as opposed to inside function bodies). (declare (xargs :guard (plist-worldp wrld) :mode :program)) (cond ((or (ttag wrld) (global-val 'boot-strap-flg wrld)) (and (symbolp key) key (or (symbolp val) (and (consp val) (symbolp (car val)) (car val) (null (cdr val)))) (or (not (member-eq key '(progn mbe1-raw ec-call1-raw with-guard-checking1-raw))) ; Keep the list above in sync with the comment about these macros in ; *initial-return-last-table* and with return-last-lookup. (er hard! 'chk-return-last-entry "The proposed key ~x0 for ~x1 is illegal because it is ~ given special treatment. See :DOC return-last." key 'return-last-table)) (or (null val) (let ((val2 (if (symbolp val) val (car val)))) (or (getprop val2 'macro-body nil 'current-acl2-world wrld) (er hard! 'chk-return-last-entry "The proposed value ~x0 for key ~x1 in ~x2 is ~ illegal because ~x3 is not the name of a macro ~ known to ACL2. See :DOC return-last and (for the ~ above point made explicitly) see :DOC ~ return-last-table." val key 'return-last-table val2)))) #-acl2-loop-only (or (fboundp key) ; Note that fboundp holds for functions, macros, and special operators. (er hard! 'chk-return-last-entry "The proposed key ~x0 for ~x1 is illegal because it is ~ does not have a Common Lisp definition. See :DOC ~ return-last and (for the above point made explicitly) ~ see :DOC return-last-table." key 'return-last-table)) t)) (t (er hard! 'chk-return-last-entry "It is illegal to modify the table, ~x0, unless there is an ~ active trust tag. See :DOC return-last and see :DOC ~ return-last-table." 'return-last-table)))) (table return-last-table nil nil :guard (chk-return-last-entry key val world)) (defdoc return-last-table ":Doc-Section switches-parameters-and-modes install special raw Lisp behavior~/ Please first ~pl[return-last] for relevant background. This ~il[table] is for advanced users only, and requires an active trust tag (~pl[defttag]). We recommend that you do not modify this table directly, but instead use the macro ~c[defmacro-last]. Here we augment that discussion with some highly technical observations that can probably be ignored if you use ~c[defmacro-last].~/ This ~il[table] has a ~c[:guard] requiring that each key be a symbol defined in raw Lisp, generally as a macro, and requiring that each non-~c[nil] value be associated either with a symbol that names a macro defined in ACL2, or else with a list of one element containing such a symbol. The table can only be modified when there is an active trust tag; ~pl[defttag]. If a key is associated with the value ~c[nil], then that key is treated as though it were not in the table. Note that keys of this table are not eligible to be bound by ~ilc[flet]. The current value of this table may be obtained by evaluating the form ~c[(table-alist 'return-last-table (w state))]. The built-in constant ~c[*initial-return-last-table*] holds the initial value of this table.~/") (defmacro defmacro-last (fn &key raw (top-level-ok 't)) ":Doc-Section Events define a macro that returns its last argument, but with side effects~/ This is an advanced feature that requires a trust tag. For explanation, including an example, ~pl[return-last].~/~/" (declare (xargs :guard (and (symbolp fn) (symbolp raw)))) (let ((raw (or raw (intern-in-package-of-symbol (concatenate 'string (symbol-name fn) "-RAW") fn)))) `(progn (defmacro ,fn (x y) (list 'return-last (list 'quote ',raw) x y)) (table return-last-table ',raw ' ,(if top-level-ok fn (list fn)))))) ; Formatted printing to strings requires local stobjs (actually ; with-local-state), so we place the relevant code below. It could certainly ; go in later source files if that is desired. (defdoc printing-to-strings ":Doc-Section IO printing to strings instead of files or standard output~/ Each of ACL2's formatted printing functions, ~c[FM*], has an analoguous macro ~c[FM*-TO-STRING] indicated below. These functions do not include a channel or ~ilc[state] as an argument, and ~c[FM*-TO-STRING] returns the string that ~c[FM*] would print to the state, in place of ~c[state]; ~pl[fmt]. The legal keyword arguments are described below. ~bv[] General Forms: result (fms-to-string string alist &key ...) ; string (fmt-to-string string alist &key ...) ; (mv col string) (fmt1-to-string string alist column &key ...) ; (mv col string) (fms!-to-string string alist &key ...) ; string (fmt!-to-string string alist &key ...) ; (mv col string) (fmt1!-to-string string alist column &key ...) ; (mv col string) ~ev[] The legal keyword arguments are as follows. They are all optional with a default of ~c[nil]. ~bq[] ~c[Evisc-tuple] is evaluated, and corresponds exactly to the ~c[evisc-tuple] argument of the corresponding ~c[FM*] function; ~pl[fmt]. ~c[Fmt-control-alist] should typically evaluate to an alist that maps print-control variables to values; ~pl[print-control]. Any alist mapping variables to values is legal, however. By default the print controls are set according to the value of constant ~c[*fmt-control-defaults*]; ~c[fmt-control-alist] overrides these defaults. For example, ~c[*fmt-control-defaults*] sets the right margin just as it is set in the initial ACL2 ~il[state], by binding ~c[fmt-soft-right-margin] and ~c[fmt-hard-right-margin] to their respective defaults of ~c[*fmt-soft-right-margin-default*] and ~c[*fmt-hard-right-margin-default*]. The following example shows how you can override those defaults, in this case arranging to print flat by setting the right margin to a large number. ~bv[] (fmt-to-string \"~~x0\" (list (cons #\\0 (make-list 30))) :fmt-control-alist `((fmt-soft-right-margin . 10000) (fmt-hard-right-margin . 10000))) ~ev[] ~c[Iprint] is typically ~c[nil], but ~c[t] is also a legal value. ~l[set-iprint] for the effect of value ~c[t], which however is local to this call of a ~c[FM*-TO-STRING] function; the behavior if iprinting afterwards is not affected by this call. In particular, you will not be able to look at the values of iprint tokens printed by this call, so the value ~c[t] is probably of limited utility at best.~eq[] Also ~pl[io] for a discussion of the utility ~c[get-output-stream-string$], which allows for accumulating the results of more than one printing call into a single string but requires the use of ~ilc[state].~/~/") (defconst *fmt-control-defaults* ; This constant should set up a state-global-let* binding for every state ; global variable that can have an effect on evaluation of a call of fms, fmt, ; or fmt1 (or their "!" versions), which are the functions on which we apply ; the macro channel-to-string. The values for the margins are simply ; convenient large values. (append *print-control-defaults* `((write-for-read t) (fmt-hard-right-margin ,*fmt-hard-right-margin-default* set-fmt-hard-right-margin) (fmt-soft-right-margin ,*fmt-soft-right-margin-default* set-fmt-soft-right-margin) (iprint-soft-bound ,*iprint-soft-bound-default*) (iprint-hard-bound ,*iprint-hard-bound-default*) (ppr-flat-right-margin ,(cdr (assoc-eq 'ppr-flat-right-margin *initial-global-table*))) ; Values not to be modified; keep in sync with *fixed-fmt-controls*. (iprint-ar (f-get-global 'iprint-ar state) set-iprint-ar) (evisc-hitp-without-iprint nil)))) (defconst *fixed-fmt-controls* ; These are the state global variables that have bindings in ; *fmt-control-defaults* but must not have those bindings overridden by the ; user (because they are managed by ACL2). '(iprint-ar evisc-hitp-without-iprint)) (defun fmt-control-bindings1 (alist fmt-control-defaults-tail) ; Alist is a variable whose value is an alist used to modify ; fmt-control-defaults-tail, which is a tail of *fmt-control-defaults*. (cond ((endp fmt-control-defaults-tail) nil) (t (cons (let* ((trip (car fmt-control-defaults-tail)) (var (car trip))) (list* var `(let ((pair (assoc-eq ',var ,alist))) (cond (pair ,(cond ((member-eq var *fixed-fmt-controls*) `(er hard 'fmt-control-bindings "The binding of ~x0 is illegal in ~ this context." ',var)) (t '(cdr pair)))) (t ,(cadr trip)))) (cddr trip))) (fmt-control-bindings1 alist (cdr fmt-control-defaults-tail)))))) (defun fmt-control-bindings (alist) (cond (alist (fmt-control-bindings1 alist *fmt-control-defaults*)) (t ; optimization *fmt-control-defaults*))) (defun set-iprint-ar (iprint-ar state) ; This function, which is untouchable, assumes that iprint-ar is well-formed. ; It is used when restoring a valid iprint-ar. (prog2$ (compress1 'iprint-ar iprint-ar) (f-put-global 'iprint-ar iprint-ar state))) (defmacro channel-to-string (form channel-var &optional extra-var fmt-controls iprint-action) ; Form is a call of fms, fmt, or fmt1 (or their "!" versions) on variables. To ; see why we make this restriction, consider the following form: ; (channel-to-string ; (f-put-global 'xxx (f-get-global 'term-evisc-tuple state) state) ; chan) ; If you evaluate this form in raw-mode and then evaluate (@ xxx), you'll ; initially get :default. But if then evaluate the form ; (set-term-evisc-tuple (evisc-tuple 4 5 nil nil) state) ; and then evaluate the above channel-to-string call again, this time (@ xxx) ; evaluates to (NIL 4 5 NIL). Thus, state changed even though ; channel-to-string generates a with-local-state call, which should not change ; state! ; Note that fmt-controls and iprint-action are evaluated, but channel-var and ; extra-var are not evaluated. ; Any non-nil value of iprint-action is coerced to t before being passed to ; (a function underneath) set-iprint. ; This macro is not recommended for users, as it has been designed specifically ; for the fmt family of functions. If one wishes to use this or a similar ; macro outside the boot-strap then one will need to avoid issues with ; untouchables; here is an example. ; (defttag t) ; (remove-untouchable temp-touchable-fns nil) ; (set-temp-touchable-fns t state) ; (remove-untouchable temp-touchable-vars nil) ; (set-temp-touchable-vars t state) ; (defun fms-to-string-fn-again ; (str alist evisc-tuple fmt-control-alist iprint-action) ; (declare (xargs :mode :program)) ; (channel-to-string ; (fms str alist chan-do-not-use-elsewhere state evisc-tuple) ; chan-do-not-use-elsewhere nil fmt-control-alist iprint-action)) ; (defmacro fmt-to-string-again ; (str alist &key evisc-tuple fmt-control-alist iprint) ; (declare (xargs :guard (member-eq iprint '(t nil)))) ; `(fmt-to-string-fn ,str ,alist ,evisc-tuple ,fmt-control-alist ,iprint)) ; If you now evaluate ; (fmt-to-string-again "Hello, ~s0." (list (cons #\0 "World"))) ; you will get the cons of 13 with "\nHello, World." (here we write \n to ; indicate a newline). (declare (xargs :guard (and (symbol-listp form) ; see "on variables" above (symbolp channel-var) (symbolp extra-var) (symbolp fmt-controls) (symbolp iprint-action) (not (eq 'result extra-var)) (not (eq 'state extra-var))))) (let* ((body0 ; error triple (mv nil val state), where val may cons extra-var `(mv?-let (,@(and extra-var (list extra-var)) state) ,form (mv-let (erp result state) (get-output-stream-string$ ,channel-var state) (mv nil (and (not erp) ,(if extra-var `(cons ,extra-var result) 'result)) state)))) (body1 ; bind fmt controls and clean up around body0 `(acl2-unwind-protect ; We use acl2-unwind-protect to guarantee that the new channel is finally ; closed. See the comment about channels in mv-let-for-with-local-stobj. "channel-to-string" (state-global-let* ,(fmt-control-bindings fmt-controls) (mv-let (msg state) (set-iprint-fn1 (if ,iprint-action :reset-enable :reset) state) (declare (ignore msg)) ,body0)) (cond ((open-output-channel-p ,channel-var :character state) (close-output-channel ,channel-var state)) (t state)) state)) (body ; open a string output channel and then evaluate body1 `(mv-let (,channel-var state) (open-output-channel :string :character state) (cond (,channel-var ,body1) (t (er soft 'channel-to-string "Implementation error: Unable to open a channel to a ~ string.")))))) `(with-local-state (mv-let (erp result state) (with-live-state ,body) (declare (ignore erp)) ,(cond (extra-var `(mv (car result) (cdr result))) (t 'result)))))) (defun fms-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action) (channel-to-string (fms str alist chan-do-not-use-elsewhere state evisc-tuple) chan-do-not-use-elsewhere nil fmt-control-alist iprint-action)) (defmacro fms-to-string (str alist &key evisc-tuple fmt-control-alist iprint) (declare (xargs :guard (member-eq iprint '(t nil)))) `(fms-to-string-fn ,str ,alist ,evisc-tuple ,fmt-control-alist ,iprint)) (defun fms!-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action) (channel-to-string (fms! str alist chan-do-not-use-elsewhere state evisc-tuple) chan-do-not-use-elsewhere nil fmt-control-alist iprint-action)) (defmacro fms!-to-string (str alist &key evisc-tuple fmt-control-alist iprint) (declare (xargs :guard (member-eq iprint '(t nil)))) `(fms!-to-string-fn ,str ,alist ,evisc-tuple ,fmt-control-alist ,iprint)) (defun fmt-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action) (channel-to-string (fmt str alist chan-do-not-use-elsewhere state evisc-tuple) chan-do-not-use-elsewhere col fmt-control-alist iprint-action)) (defmacro fmt-to-string (str alist &key evisc-tuple fmt-control-alist iprint) (declare (xargs :guard (member-eq iprint '(t nil)))) `(fmt-to-string-fn ,str ,alist ,evisc-tuple ,fmt-control-alist ,iprint)) (defun fmt!-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action) (channel-to-string (fmt! str alist chan-do-not-use-elsewhere state evisc-tuple) chan-do-not-use-elsewhere col fmt-control-alist iprint-action)) (defmacro fmt!-to-string (str alist &key evisc-tuple fmt-control-alist iprint) (declare (xargs :guard (member-eq iprint '(t nil)))) `(fmt!-to-string-fn ,str ,alist ,evisc-tuple ,fmt-control-alist ,iprint)) (defun fmt1-to-string-fn (str alist col evisc-tuple fmt-control-alist iprint-action) (channel-to-string (fmt1 str alist col chan-do-not-use-elsewhere state evisc-tuple) chan-do-not-use-elsewhere col fmt-control-alist iprint-action)) (defmacro fmt1-to-string (str alist col &key evisc-tuple fmt-control-alist iprint) (declare (xargs :guard (member-eq iprint '(t nil)))) `(fmt1-to-string-fn ,str ,alist ,col ,evisc-tuple ,fmt-control-alist ,iprint)) (defun fmt1!-to-string-fn (str alist col evisc-tuple fmt-control-alist iprint-action) (channel-to-string (fmt1! str alist col chan-do-not-use-elsewhere state evisc-tuple) chan-do-not-use-elsewhere col fmt-control-alist iprint-action)) (defmacro fmt1!-to-string (str alist col &key evisc-tuple fmt-control-alist iprint) (declare (xargs :guard (member-eq iprint '(t nil)))) `(fmt1!-to-string-fn ,str ,alist ,col ,evisc-tuple ,fmt-control-alist ,iprint)) (link-doc-to fms-to-string acl2-built-ins printing-to-strings) (link-doc-to fms!-to-string acl2-built-ins printing-to-strings) (link-doc-to fmt-to-string acl2-built-ins printing-to-strings) (link-doc-to fmt!-to-string acl2-built-ins printing-to-strings) (link-doc-to fmt1-to-string acl2-built-ins printing-to-strings) (link-doc-to fmt1!-to-string acl2-built-ins printing-to-strings) (defdoc dead-events ":Doc-Section Other using proof supporters to identify dead code and unused theorems~/ Below, when we talk about ``an event ~c[A]'', we mean an event whose name is ~c[A]. When event ~c[A] is used in a proof performed to admit event ~c[B] that you submit to ACL2, we say that ~c[A] is a ``proof-supporter'' of ~c[B]. ACL2 stores an association list such that for every event ~c[B] with at least one proof-supporter, ~c[B] is associated with a list of all of its proof-supporters, sorted by ~ilc[symbol-<]. The following form evaluates to that alist, which is called the ``proof-supporters-alist''. ~bv[] (global-val 'proof-supporters-alist (w state)) ~ev[] By ``used in a proof'' above, we mean: applied as a rule or supplied explicitly via ~il[hints] of type ~c[:use], ~c[:by], or ~c[:clause-processor]. That is, the ~il[events] ``used in a proof'' for admitting an event ~c[E] are those listed in the summary printed at the conclusion of admitting ~c[E].~/ Note that if proofs are skipped when admitting event ~c[E], say because the last admission of ~c[E] was done by ~ilc[include-book] (or ~c[certify-book], which ends with an ~ilc[include-book]), then there will be no entry in that alist for ~c[E]. (An exception is made however for ~ilc[encapsulate] ~il[events], where proof-supporters are remembered from the first pass; see below.) So if you want the proof-supporters-alist to include supporters for events in a book, use ~ilc[ld] rather than ~ilc[include-book] or ~ilc[certify-book] to process the events in that book. If however you are interested in the proof-supporters FROM a book that support a later event, then it is fine to include that book. The case for ~ilc[encapsulate] is slightly tricky. Consider an example of the following form. ~bv[] A ; event preceding the encapsulate (encapsulate () B (local C) ; uses A and B in a proof D ; uses C in a proof ) ~ev[] At the conclusion of this ~ilc[encapsulate] event, the proof-supporters-alist associates ~c[D] with ~c[A] and ~c[B], but not ~c[C] (which has disappeared, since it is ~il[local]). Note that this sort of ``transitive closure'' operation is only performed when necessary due to the disappearance of ~il[local] ~il[events]. For example, if we replace ~c[(local C)] above by just ~c[C], then ~c[D] is associated in the proof-supporters-alist only with ~c[C], not with ~c[A] or ~c[B]. If you want the transitive closure of the relation computed by the proof-supporters-alist, you have to compute it yourself. (This was a deliberate design decision, in order to avoid slowing down event processing.) However, there is help available on how to do such a computation: A community book, ~c[books/misc/dead-events.lisp], does such a transitive closure, and moreover uses that information to find ``dead events'' relative to a list of ``desired'' events. For example, suppose you use ~ilc[LD] to process the events, with proofs, in a book intended to prove theorems ~c[MAIN-1] and ~c[MAIN-2]. (Remember, ~ilc[certify-book] will not save such information.) Suppose furthermore that the book begins with some ~ilc[include-book] forms followed by ~c[(deflabel book-start)]. You could evaluate this form: ~bv[] (dead-events '(main-1 main-2) :start 'book-start) ~ev[] The result is a list of events that you probably can delete from the book without causing any proofs to fail. See the ~c[dead-events.lisp] book for further documentation. You might also find the code in the above book to be helpful for writing your own utilities based on the proof-supporters-alist.~/") (link-doc-to proof-supporters-alist miscellaneous dead-events) ; Essay on Memoization with Attachments (relevant for #+hons version only) ; We maintain the invariant that every stored value in a memo table is valid. ; The main idea is to ensure that if a function m is memoized with :aok t, then ; for every possible call of m, every function called has the same attachment ; now as it did when the value was stored. To do this, we maintain a stronger ; invariant, described in the next paragraph, that is based on the acyclic ; "extended ancestor" relation introduced in the the Essay on Defattach. ; Roughly speaking, this relation is the transitive closure of the immediate ; ancestor relation, where g is an immediate ancestor of f if it either g is an ; ordinary ancestor of f or else is an attachment pair (think: f is ; redefined to be g). We say "roughly speaking" primarily because we traffic ; entirely in "canonical" function symbols, as explained in the Essay on ; Defattach. Morover, for our defattach implementation, we include guards in ; the calculation of canonical ancestors. Guards are relevant in the sense ; that changing or (especially) removing an attachment used in a guard could ; invalidate a stored value, not logically, but in the sense that its ; computation should now cause a guard violation error and thus we don't want ; to return such a value. ; Let m a memoized function symbol. If m was memoized with :aok nil (the ; default), then the invariant maintained is simply that the ; :ext-anc-attachments field of the memoize-info-ht-entry record for m is nil. ; This implies the property we desire, that all stored entries for m are valid, ; because defattach events do not destroy the validity of stored results. But ; in the case that f was memoized with :aok t, the :ext-anc-attachments field ; of the memoize-info-ht-entry record for m is a non-null fast alist whose keys ; are exactly the (canonical) extended ancestors of m, including the canonical ; sibling of m. We maintain the invariant that the value of key f in this fast ; alist is itself an alist associating each sibling f' of f with its ; attachment, for each sibling f' that has an attachment. ; To summarize: in the :aok t case, we maintain the :ext-anc-attachments field ; to have the value described above, and every value stored in the memo table ; is correct with respect to the current attachments, which are those indicated ; in the :ext-anc-attachments field. Thus, if a defattach event changes the ; attachment of (some sibling of) an extended ancestor, then the ; :ext-anc-attachments field is recalculated and stored anew. If the only ; changes are to add new attachments, without changing or removing any existing ; attachments, then the memo table is not cleared; otherwise, it is. The ; analogous actions are taken when we undo. ; For efficiency, we implement extend-world1, retract-world1, and recover-world ; so that they do not update such fields or clear memo-tables until all trips ; have been processed. At that point we see whether any defattach event has ; been installed or undone, and then we see whether any memo-table's ; :ext-anc-attachments field needs to be recalculated, and whether furthermore ; the table needs to be invalidated, as discussed above. For efficiency, we ; set a variable to a list L of canonical siblings of all functions whose ; attachment may have been installed, eliminated, or changed. We then restrict ; our check on :ext-anc-attachments fields to check attachments for siblings of ; functions in L. In particular, if L is empty then nothing needs to be done. ;;; Going backward, when we see that an extended ancestor attachment has ;;; changed or been added for the memo table of fn, then add fn to a list of ;;; functions that will eventually have their memo tables strongly cleared when ;;; we finish retract-world1. (How about recover-world? Perhaps just strongly ;;; clear all memo tables unless I find a better idea.) ; Start code supporting extended-ancestors. (defun attachment-pairs (fns wrld acc) ; Accumulate into acc all attachment pairs (f . g) for f in fns. (cond ((endp fns) acc) (t (attachment-pairs (cdr fns) wrld (let ((pair (attachment-pair (car fns) wrld))) (cond (pair (cons pair acc)) (t acc))))))) (defun sibling-attachments (f wrld) ; We return all attachment pairs (f0 . g0) for which f0 is a sibling of f. (attachment-pairs (siblings f wrld) wrld nil)) (defun extended-ancestors4 (fns wrld fal) (cond ((endp fns) fal) (t (extended-ancestors4 (cdr fns) wrld (cond ((hons-get (car fns) fal) fal) (t (hons-acons (car fns) (sibling-attachments (car fns) wrld) fal))))))) (defun extended-ancestors3 (components wrld fal) (cond ((endp components) fal) (t (extended-ancestors3 (cdr components) wrld (let ((anc (access attachment-component (car components) :ord-anc)) (path (access attachment-component (car components) :path))) (extended-ancestors4 (if path (cons (car path) ; attachment-component-owner anc) anc) wrld fal)))))) (defun extended-ancestors2 (canon-gs arfal wrld canon-gs-fal fal) (cond ((endp canon-gs) fal) (t (let ((g (car canon-gs))) (cond ((hons-get g canon-gs-fal) (extended-ancestors2 (cdr canon-gs) arfal wrld canon-gs-fal fal)) (t (let ((rec (cdr (hons-get g arfal)))) (extended-ancestors2 (cdr canon-gs) arfal wrld (hons-acons g fal canon-gs-fal) (let ((fal (hons-acons g (sibling-attachments (car canon-gs) wrld) fal))) (cond (rec (extended-ancestors3 (access attachment rec :components) wrld fal)) (t ; :skip-checks was used fal))))))))))) (defun canonical-cdrs (alist wrld acc) (cond ((endp alist) acc) (t (canonical-cdrs (cdr alist) wrld (cons (canonical-sibling (cdar alist) wrld) acc))))) (defun extended-ancestors1 (fns canon-gs arfal wrld fal) ; Arfal is a fast alist mapping g-canonical function symbols to attachment ; records. We accumulate ordinary ancestors of members of fns, including those ; functions, into fal, as we accumulate immediate extended ancestors of members ; of fns into canon-gs. Once fns is empty, however, we accumulate all extended ; ancestors of members of canon-gs (including those functions) into fal. (cond ((endp fns) (extended-ancestors2 canon-gs arfal wrld 'extended-ancestors2 fal)) ((hons-get (car fns) fal) (extended-ancestors1 (cdr fns) canon-gs arfal wrld fal)) (t (let* ((alist (sibling-attachments (car fns) wrld)) (canon-gs (cond ((null alist) ; optimization canon-gs) (t (append (canonical-cdrs alist wrld nil) canon-gs))))) (extended-ancestors1 (append (canonical-ancestors (car fns) wrld nil) (cdr fns)) canon-gs arfal wrld (hons-acons (car fns) alist fal)))))) (defun attachment-records-fal (attachment-records fal) (cond ((endp attachment-records) fal) (t (attachment-records-fal (cdr attachment-records) (hons-acons (access attachment (car attachment-records) :g) (car attachment-records) fal))))) (defun extended-ancestors (f wrld) ; The implementation of this function uses hons-acons, so might only be ; efficient when #+hons (which was its intended use when written). (extended-ancestors1 (cons (canonical-sibling f wrld) (canonical-ancestors f wrld nil)) nil (attachment-records-fal (global-val 'attachment-records wrld) :attachment-records-fal) wrld f)) (defun ext-anc-attachment-missing (alist wrld) ; See ext-anc-attachments-valid-p. (cond ((endp alist) nil) ((eq (cdar alist) (cdr (attachment-pair (caar alist) wrld))) (ext-anc-attachment-missing (cdr alist) wrld)) (t (caar alist)))) (defun ext-anc-attachments-valid-p-1 (fns alist wrld) ; See ext-anc-attachments-valid-p. We assume that for every pair (f . g) in ; alist, g is the attachment of f in wrld. (cond ((endp fns) t) ((or (assoc-eq (car fns) alist) (not (attachment-pair (car fns) wrld))) (ext-anc-attachments-valid-p-1 (cdr fns) alist wrld)) (t nil))) (defun ext-anc-attachments-valid-p (fns ext-anc-attachments wrld acc) ; Each member of the fast alist ext-ancestor-attachments associates a function ; symbol f with an alist. That alist is intended to have as its keys the ; siblings of f that have an attachment, associating each such key with its ; attachment. This function returns t if that spec currently holds. ; Otherwise, if some such key is no longer attached to its value, return that ; key. The other possibility is that some key is missing, in which case we ; return nil to indicate that we need to grow. ; Acc is initially t, but is nil when we find that an alist needs to grow. (cond ((endp fns) acc) (t (let* ((f (car fns)) (siblings (siblings f wrld)) (alist (cdr (hons-get f ext-anc-attachments))) (missing (ext-anc-attachment-missing alist wrld))) (or missing (ext-anc-attachments-valid-p (cdr fns) ext-anc-attachments wrld (and acc (ext-anc-attachments-valid-p-1 siblings alist wrld)))))))) ; Start definitions related to defun-inline. (defconst *inline-suffix* "$INLINE") (defconst *inline-suffix-len-minus-1* (1- (length *inline-suffix*))) (defconst *notinline-suffix* "$NOTINLINE") (defconst *notinline-suffix-len-minus-1* (1- (length *notinline-suffix*))) (defconst *non-stobj-var-root* (coerce (symbol-name 'non-stobj-var) 'list)) (defun defun-inline-form (name formals lst defun-type suffix) ; Defun-type is DEFUN or DEFUND; suffix is *inline-suffix* or ; *notinline-suffix*. (declare (xargs :mode :program :guard (and (symbolp name) (symbol-listp formals) lst (<= (number-of-strings (butlast lst 1)) 1) (or (eq defun-type 'defun) (eq defun-type 'defund)) (or (equal suffix *inline-suffix*) (equal suffix *notinline-suffix*))))) (let* ((name$inline (intern-in-package-of-symbol (concatenate 'string (symbol-name name) suffix) name)) (dcls-and-strings (butlast lst 1)) (strings (get-string dcls-and-strings)) (dcls (remove-strings dcls-and-strings)) (body (car (last lst))) (macro-formals formals)) `(progn (defmacro ,name ,macro-formals ,@strings (list ',name$inline ,@macro-formals)) (add-macro-fn ,name ,name$inline) (,defun-type ,name$inline ,formals ,@dcls ,body)))) (defmacro defun-inline (name formals &rest lst) ":Doc-Section acl2::Events define a potentially inlined function symbol and associated macro~/ You may be able to improve performance by replacing an event ~c[(defun f ...)] with a corresponding event ~c[(defun-inline f ...)], in order to encourage the host Lisp compiler to inline calls of ~c[f]. ~bv[] Example Form: (defun-inline lng (x) (declare (xargs :guard (true-listp x))) (if (endp x) 0 (1+ (lng (cdr x)))))~/ General Form: (defun-inline fn (var1 ... varn) doc-string dcl ... dcl body) ~ev[] satisfying the same requirements as in the General Form for ~ilc[defun]. The effect is to define a macro ~c[fn] and a function ~c[fn$inline] (i.e., a symbol in the same package as ~c[fn] but whose ~ilc[symbol-name] has the suffix ~c[\"$INLINE\"], such that each call of ~c[fn] expands to a call of the function symbol ~c[fn$inline] on the same arguments. If ~c[doc-string] is supplied, then it is associated with ~c[fn], not with ~c[fn$inline]. Moreover, ~ilc[table] ~il[events] are generated that allow the use of ~c[fn] in ~il[theory] expressions to represent ~c[fn$inline] and that cause any untranslated (user-level) call of ~c[fn$inline] to be printed as the corresponding call of ~c[fn]. A form ~c[(defun-inline f ...)] actually defines a function named ~c[f$inline] and a corresponding macro named ~c[f] whose calls expand to calls of ~c[f$inline], while providing the illusion that there is just the ``function'' ~c[f]. For example, the Example Form above macroexpands in one step to the following form. ~bv[] (progn (defmacro lng (non-stobj-var0) (list 'lng$inline non-stobj-var0)) (add-macro-fn lng lng$inline) (defun lng$inline (x) (declare (xargs :guard (true-listp x))) (if (endp x) 0 (1+ (lng (cdr x)))))) ~ev[] Note that the above call of ~ilc[add-macro-fn] generates the aforementioned two table events (~pl[add-macro-fn]), which provide the illusion that we are just defining a function ~c[lng], as you can see in the following log: ~c[lng] appears rather than ~c[lng$inline]. ~bv[] ACL2 !>(set-gag-mode nil) ACL2 !>(thm (equal (lng (append x y)) (+ (lng x) (lng y))) :hints ((\"Goal\" :in-theory (enable lng)))) [.. output omitted ..] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (LNG (APPEND (CDR X) Y)) (+ (LNG (CDR X)) (LNG Y)))) (EQUAL (LNG (APPEND X Y)) (+ (LNG X) (LNG Y)))). [.. output omitted ..] ~ev[] Under the hood, ACL2 arranges that every function symbol with suffix ~c[\"$INLINE\"] is presented to the compiler as one whose calls we would prefer to inline. Technically: the Common Lisp form ~c[(declaim (inline f$inline))] is generated for a function symbol ~c[f$inline] before that symbol's definition is submitted. However, the Common Lisp spec explicitly avoids requiring that the compiler respect this ~c[declaim] form. Fortunately, Common Lisp implementations often do respect it. Also ~pl[defund-inline], ~pl[defun-notinline], and ~pl[defund-notinline]. ~em[Remarks]. (1) None of these macros (including ~c[defun-inline]) is supported for use inside a ~ilc[mutual-recursion]. (2) Every function symbol defined in ACL2 whose ~ilc[symbol-name] has the suffix ~c[\"$INLINE\"] is proclaimed to be inline; similarly for ~c[\"$NOTINLINE\"] and notinline. (3) No special treatment for inlining (or notinlining) is given for function symbols locally defined by ~ilc[flet], with two exceptions: when explicitly declared ~c[inline] or ~c[notinline] by the ~c[flet] form, and for symbols discussed in (1) and (2) above that, at some point in the current ACL2 session, were defined as function symbols in ACL2 (even if not currently defined because of undoing or being ~il[local]). (4) The function symbol actually being defined by ~c[(defun-inline foo ...)] is ~c[foo$inline]. As mentioned above, one can be oblivious to this fact when writing ~il[theory] expressions or perusing prover output. However, for other purposes (for example, ~ilc[verify-guards] and ~ilc[defabsstobj] ~c[:exports]) you will need to supply the name of the function symbol rather than the name of the macro; e.g., for the above form ~c[(defun-inline foo ...)], you may subsequently issue the event ~c[(verify-guards foo$inline)] but not ~c[(verify-guards foo)]. (5) Obscure Remark. Suppose that you certify a book with compilation (the default) in one host Lisp, saving the expansion file. Suppose that you then compile in another host Lisp by using ~ilc[include-book] with argument ~c[:load-compiled-file :comp]. Then in subsequent sessions, including that book with the second host Lisp will not result in any inline or notinline behavior for functions defined in the book. This may be fixed in a future release if someone complains.~/" ; Implementor hint for (5) above: Search for ";;; Declaim forms:" in ; write-expansion-file, and notice the printing just below it of a readtime ; conditional for the host Lisp, so that declaim forms are restricted to that ; Lisp. This mechanism was probably put into place so that declaiming for GCL ; didn't result in declaiming for Allegro CL, or something along those lines. (defun-inline-form name formals lst 'defun *inline-suffix*)) (defmacro defund-inline (name formals &rest lst) ; Warning: Keep this in sync with defun-inline. ":Doc-Section acl2::Events define a potentially disabled, inlined function symbol and associated macro~/ ~c[Defund-inline] is a variant of ~ilc[defun-inline], the difference being that ~c[defund-inline] disables the newly-defined function symbol. ~l[defun-inline].~/~/" (defun-inline-form name formals lst 'defund *inline-suffix*)) (defmacro defun-notinline (name formals &rest lst) ; Warning: Keep this in sync with defund-notinline. ":Doc-Section acl2::Events define a not-to-be-inlined function symbol and associated macro~/ ~l[defun-inline] for an analogous utility that supports inlining. The present utility is probably far less useful; it tells the compiler ~em[not] to inline calls of the function being defined. Also ~pl[defund-notinline] for a variant of this event that disables the newly-defined function symbol. Under the hood, ~c[(defun-inline f ...)] and ~c[(defun-notinline f ...)] cause evaluation of Common Lisp forms ~c[(declaim (inline f$inline))] and ~c[(declaim (notinline f$notinline))], respectively. According to the Common Lisp spec, the compiler need not respect the first of these (for ~c[inline]), but it must respect the second of these (for ~c[notinline]). Fortunately, Common Lisp implementations often do respect the first of these as well.~/~/" (defun-inline-form name formals lst 'defun *notinline-suffix*)) (defmacro defund-notinline (name formals &rest lst) ; Warning: Keep this in sync with defun-inline. ":Doc-Section acl2::Events define a disabled, not-to-be-inlined function symbol and associated macro~/ ~c[Defund-notinline] is a variant of ~ilc[defun-notinline], the difference being that ~c[defund-notinline] disables the newly-defined function symbol. ~l[defun-notinline].~/~/" (defun-inline-form name formals lst 'defund *notinline-suffix*)) (defun regenerate-tau-database-fn0 (user-auto-modep auto-modep ens trips ctx wrld state) ; Tau will visit each triple in trips and extend the tau database using the ; given auto-modep and ens. Trips is a list of the tau-relevant triples in the ; current world, in chronological order from the earliest relevant boot-strap ; triple. Auto-modep is the setting of the tau auto modep flag to use during ; the visit of each triple. It is presumably T, since that is how the system ; boots. However, when we pass the EXIT-BOOT-STRAP-MODE triple, we switch the ; auto-modep to user-auto-modep. (cond ((endp trips) (value wrld)) ((eq (cadr (car trips)) 'formals) (regenerate-tau-database-fn0 user-auto-modep auto-modep ens (cdr trips) ctx (tau-visit-function-introduction (car (car trips)) wrld) state)) ((and (eq (car (car trips)) 'event-landmark) (eq (cadr (car trips)) 'global-value)) (cond ((eq (access-event-tuple-type (cddr (car trips))) 'EXIT-BOOT-STRAP-MODE) (regenerate-tau-database-fn0 user-auto-modep user-auto-modep ens (cdr trips) ctx wrld state)) (t (er-let* ((wrld1 (tau-visit-event nil (access-event-tuple-type (cddr (car trips))) (access-event-tuple-namex (cddr (car trips))) auto-modep ens ctx wrld state))) (regenerate-tau-database-fn0 user-auto-modep auto-modep ens (cdr trips) ctx wrld1 state))))) (t (value (er hard 'regenerate-tau-database-fn0 "Collect-tau-relevant-triples collected an unrecognized ~ property! We expected to see fn FORMALS and EVENT-LANDMARK ~ GLOBAL-VALUE triples, but we see the triple ~x0." (car trips)))))) (defun regenerate-tau-database-fn1 (boot-strap-auto-modep user-auto-modep ens ctx wrld state) ; Collect all the tau-relevant triples in the world, in chronological order, ; then re-initialize the tau globals, and then visit each triple in turn and ; regenerate the tau database. We start with the tau auto mode set to ; boot-strap-auto-mode (presumably t), and switch to the user-auto-mode setting ; when we get out of the boot strap region. ; Tau Todo: It might be worthwhile trying to compress the world we get from ; this event. See how big it is and think about it. (regenerate-tau-database-fn0 user-auto-modep boot-strap-auto-modep ens (collect-tau-relevant-triples wrld nil) ctx (initialize-tau-preds *primitive-monadic-booleans* (initialize-tau-globals wrld)) state)) ; Essay on Regenerate-tau-database ; Regenerate-tau-database is motivated by the desire to provide the user with ; some facility for adjusting the tau database to reflect a theory, since ; otherwise there would be no way to achieve that end short of removing certain ; theorems from the script! We think this will be a rarely-used event simply ; because tau is designed to prove goals, not to simplify or rewrite them. How ; often will the user wish a goal NOT to be proved? This is an admittedly ; naive view of the situation, since as of this writing no one has ever used ; the tau system! It is still being developed. ; Regenerate-tau-database recomputes the database but only considers those ; runes enabled by the current global theory. But this represents a design ; choice: why not allow the user to specify the theory governing the ; regeneration process? For example, why not provide: ; (regenerate-tau-database (disable lemma55)) ; We call this the ``tau theory alternative'' and rejected it because we think ; it will confuse the user moving forward. That is, while a particular tau ; theory might be used to regenerate the database when the regeneration event ; is processed, the subsequent incremental extension of the database with ; :tau-system or implicit (auto mode) rules is done under the global theory. ; This could be confusing if the user thinks that the tau theory governs what ; is in the tau database (instead of what was put in it ``initially'' by the ; regeneration event). ; One solution would be to provide a completely separate theory to be used by ; tau. Setting the tau theory to some new theory would actually recompute the ; tau database. As new events are added, both the current theory and the tau ; theory are explicitly extended. All queries to enabled status by the tau ; system, including its use of type-set, would refer to the tau theory. In a ; situation similar to in-arithmetic-theory we would either have to provide ; versions of enable and disable that are relative to current-tau-theory or ; else warn the user not to use those macros. All things considered this seems ; like a lot of infrastructure for a rarely used event. ; The design we actually implement doesn't allow for a distinct tau theory. ; The global theory is always used. If the user wants to regenerate the ; database he or she must reset the global theory appropriately and set it back ; afterwards. This has the advantage of forcing the user to acknowledge which ; theory is being used. ; In a similar vein, we use the global (acl2-defaults-table) setting of ; tau-auto-modep during the regeneration. ; Tau Todo: see the install-event comment below! (defun regenerate-tau-database-fn (state doc event-form) ; Warning: If this event ever generates proof obligations, remove it from the ; list of exceptions in install-event just below its "Comment on irrelevance of ; skip-proofs". (when-logic "REGENERATE-TAU-DATABASE" (with-ctx-summarized (if (output-in-infixp state) event-form (if doc "( REGENERATE-TAU-DATABASE ...)" "( REGENERATE-TAU-DATABASE)")) (let* ((wrld (w state)) (event-form (or event-form `(REGENERATE-TAU-DATABASE ,@(if doc (list :doc doc) nil)))) (boot-strap-auto-modep (cdar *tau-status-boot-strap-settings*)) (user-auto-modep (tau-auto-modep wrld)) (ens (ens state))) ; Note: We do not permit REGENERATE-TAU-DATABASE events to be redundant. If ; this is changed, change the text of the :doc for redundant-events. (er-let* ((doc-pair (translate-doc nil doc ctx state)) (wrld1 (regenerate-tau-database-fn1 boot-strap-auto-modep user-auto-modep ens ctx wrld state)) (val (install-event t event-form 'regenerate-tau-database 0 nil nil :protect nil wrld1 state))) (value t)))))) ; Next comes support for time-tracker (but see axioms.lisp for ; time-tracker-fn). (defun rational-to-decimal-string (x state) (declare (xargs :mode :program :stobjs state :guard (rationalp x))) (mv-let (channel state) (open-output-channel :string :character state) (pprogn (print-rational-as-decimal x channel state) (er-let* ((str (get-output-stream-string$ channel state nil))) (pprogn (close-output-channel channel state) (value str)))))) #-acl2-loop-only (progn (defvar *time-tracker-alist* nil) (defvar *time-tracker-disabled-p* nil) (defstruct time-tracker ; When this structure is created for a given tag, Init is set to the current ; run-time. Tracking is on when Latest is non-nil, in which case Latest marks ; the run-time at the time time-tracker was turned on (for an implicitly ; associated tag) with option :init. Elapsed marks the total elapsed run-time ; accumulated with tracking active, except that if tracking is currently ; inactive (i.e., :latest is non-nil), then Elapsed does not include the ; run-time since Latest. Times and Interval come from the :init option of ; time-tracker, though Times may have been cdr'ed; so these are a true-list of ; rationals and either a rational or nil, respectively. ; The Msg field is marked as :read-only simply because we currently see no ; reason to update that field. (init ; reset by :init (get-internal-time) :type rational :read-only t) (latest ; reset by :init, :stop, and :start nil :type (or null rational)) (elapsed ; total time tracked, updated when updating Latest 0 :type rational) (msg nil :read-only t) (times ; can be updated by time-tracker with option :print? nil :type (satisfies rational-listp)) (interval ; if non-nil, used for updating an an empty Times nil :type (or null rational) :read-only t) ) (defun tt-print-msg (tag msg total) (assert msg) (let* ((state *the-live-state*) ; local state (*file-clock* *file-clock*) (seconds (/ total internal-time-units-per-second))) (mv-let (erp seconds-as-decimal-string state) (rational-to-decimal-string seconds state) (assert$ (null erp) (fms "TIME-TRACKER-NOTE [~x0]: ~@1~|" (list (cons #\0 tag) (cons #\1 msg) (cons #\t seconds-as-decimal-string)) (proofs-co state) state nil))))) (defun tt-init (tag times interval msg) (cond ((null times) (er hard 'time-tracker "Illegal :INIT option (tag ~x0): the :TIMES keyword is required, with ~ a value that is a non-empty list of rational numbers." tag)) ((not (rational-listp times)) (er hard 'time-tracker "Illegal value of :TIMES for :INIT (tag ~x0): ~x1 is not a true list ~ of rationals. See :DOC time-tracker." tag times)) ((not (or (null interval) (rationalp interval))) (er hard 'time-tracker "Illegal value of :INTERVAL for :INIT (tag ~x0): ~x1 is neither NIL ~ nor a rational number. See :DOC time-tracker." tag interval)) ((and msg (not (msgp msg))) (er hard 'time-tracker "Illegal value of :MSG for :INIT (tag ~x0): ~x1 is not a string or a ~ true list whose first element is a string. See :DOC time-tracker." tag msg)) ((assoc-eq tag *time-tracker-alist*) (er hard 'time-tracker "It is illegal to specify :INIT for tag ~x0, because this tag is ~ already being tracked. Specify :END first to solve this problem. ~ See :DOC time-tracker." tag)) (t (setq *time-tracker-alist* (acons tag (make-time-tracker :msg (or msg "~st s") :times (mapcar (lambda (x) (* x internal-time-units-per-second)) times) :interval (and interval (* internal-time-units-per-second interval))) *time-tracker-alist*))))) (defun tt-end (tag) ; We allow :end to run without error even when tag is not being tracked, so ; that we can run :end in case an anticipated earlier :end was not run because ; of an interceding interrupt. (when (assoc-eq tag *time-tracker-alist*) (setq *time-tracker-alist* (delete-assoc-eq tag *time-tracker-alist*)))) (defun tt-print? (tag min-time msg) ; When we print based on the first of time-tracker-times (because min-time ; isn't supplied), we update time-tracker-times, taking the cdr but if the ; result is empty and the :interval is not nil, then leaving an empty singleton ; list containing the interval. If min-time is supplied, then ; time-tracker-times is not updated. (cond ((not (or (null min-time) (rationalp min-time))) (er hard 'time-tracker "Illegal value of :MIN-TIME for :PRINT? (tag ~x0): ~x1 is not a ~ rational number or nil. See :DOC time-tracker." tag min-time)) ((and msg (not (msgp msg))) (er hard 'time-tracker "Illegal value of :MSG for :PRINT? (tag ~x0): ~x1 is not a string or ~ a true list whose first element is a string. See :DOC time-tracker." tag msg)) (t (let ((tt (cdr (assoc-eq tag *time-tracker-alist*)))) (when tt (let* ((min-time (and min-time (* internal-time-units-per-second min-time))) (times (time-tracker-times tt)) (time (or min-time (car times)))) (when time (let* ((current-internal-time (get-internal-time)) (total (let ((latest (time-tracker-latest tt))) (if latest (+ (time-tracker-elapsed tt) (- current-internal-time latest)) (time-tracker-elapsed tt))))) (when (>= total time) (let ((msg (or msg (time-tracker-msg tt)))) (when msg (tt-print-msg tag msg total))) (when (not min-time) ; see comment above discussing this test (pop times) (loop (cond ((or (null times) (< total (car times))) (return)) (t (pop times)))) (setf (time-tracker-times tt) (if (null times) (let ((interval (time-tracker-interval tt))) (if interval (list (+ total interval)) nil)) times)))))))))))) (defun tt-stop (tag) (let* ((tt (cdr (assoc-eq tag *time-tracker-alist*))) (latest (and tt (time-tracker-latest tt)))) (cond ((not tt) (er hard 'time-tracker "It is illegal to specify :STOP for tag ~x0, because this tag is ~ not being tracked. Specify :INIT first to solve this problem. ~ See :DOC time-tracker." tag)) ((not latest) (er hard 'time-tracker "It is illegal to specify :STOP for tag ~x0, because tracking for ~ this tag is already in an inactive state. Specify :START first to ~ solve this problem. See :DOC time-tracker." tag)) (t (setf (time-tracker-elapsed tt) (+ (time-tracker-elapsed tt) (- (get-internal-time) latest))) (setf (time-tracker-latest tt) nil))))) (defun tt-start (tag) (let ((tt (cdr (assoc-eq tag *time-tracker-alist*)))) (cond ((not tt) (er hard 'time-tracker "It is illegal to specify :START for tag ~x0, because this tag is ~ not being tracked. Specify :INIT first to solve this problem. ~ See :DOC time-tracker." tag)) ((time-tracker-latest tt) (er hard 'time-tracker "It is illegal to specify :START for tag ~x0, because tracking for ~ this tag is already in an active state. Specify :STOP first to ~ solve this problem. See :DOC time-tracker." tag)) (t (setf (time-tracker-latest tt) (get-internal-time)))))) ) ; We originally defined defund in axioms.lisp, but now that its definition ; depends on remove-strings and other functions defined after axioms.lisp, we ; define it here. (defun program-declared-p1 (dcls) (cond ((endp dcls) nil) ((and (consp (car dcls)) (eq (caar dcls) 'xargs) (keyword-value-listp (cdr (car dcls))) (eq (cadr (assoc-keyword :mode (cdr (car dcls)))) :program)) t) (t (program-declared-p1 (cdr dcls))))) (defun program-declared-p (def) ; Def is a definition with the initial DEFUN or DEFUND stripped off. We return ; t if the declarations in def are minimally well-formed and there is an xargs ; declaration of :mode :program. (mv-let (erp dcls) (collect-dcls (remove-strings (butlast (cddr def) 1)) 'ignored-ctx) (cond (erp nil) (t (program-declared-p1 dcls))))) #+acl2-loop-only (defmacro defund (&rest def) ":Doc-Section acl2::Events define a function symbol and then disable it~/~/ Use ~c[defund] instead of ~ilc[defun] when you want to disable a function immediately after its definition in ~c[:]~ilc[logic] mode. This macro has been provided for users who prefer working in a mode where functions are only enabled when explicitly directed by ~c[:]~ilc[in-theory]. Specifically, the form ~bv[] (defund NAME FORMALS ...) ~ev[] expands to: ~bv[] (progn (defun NAME FORMALS ...) (with-output :off summary (in-theory (disable NAME))) (value-triple '(:defund NAME))). ~ev[] Only the ~c[:]~ilc[definition] rule (and, for recursively defined functions, the ~c[:]~ilc[induction] rule) for the function are disabled. In particular, ~c[defund] does not disable either the ~c[:]~ilc[type-prescription] or the ~c[:]~ilc[executable-counterpart] rule. Also, the summary for the ~ilc[in-theory] event is suppressed. If the function is defined in ~c[:]~ilc[program] mode, either because the ~il[default-defun-mode] is ~c[:]~ilc[program] or because ~c[:mode :program] has been specified in an ~ilc[xargs] form of a ~ilc[declare] form, then no ~ilc[in-theory] event is executed. (More precisely, ~ilc[in-theory] events are ignored when the ~il[default-defun-mode] is ~c[:]~ilc[program], and if ~c[:mode :program] is specified then ~c[defund] does not generate an ~ilc[in-theory] event.) Note that ~c[defund] commands are never redundant (~pl[redundant-events]) when the ~il[default-defun-mode] is ~c[:]~ilc[logic], because the ~ilc[in-theory] event will always be executed. ~l[defun] for documentation of ~c[defun]." (declare (xargs :guard (and (true-listp def) (symbolp (car def)) (symbol-listp (cadr def))))) `(progn (defun ,@def) ,@(and (not (program-declared-p def)) `((with-output :off summary (in-theory (disable ,(car def)))))) (value-triple ',(xd-name 'defund (car def)) :on-skip-proofs t))) #-acl2-loop-only (defmacro defund (&rest def) (cons 'defun def)) ; The next three events define a :logic mode version of ev-fncall that has ; unknown constraints. We originally put this in boot-strap-pass-2.lisp, but ; it didn't work there, because add-trip doesn't give special treatment for ; defun-overrides in pass 2 of the boot-strap, which is the only time that the ; events in boot-strap-pass-2.lisp are evaluated. (defun magic-ev-fncall-cl-proc (x) ; This function is a sort of placeholder, used in a ; define-trusted-clause-processor event for noting that magic-ev-fncall has ; unknown constraints. (declare (xargs :guard t)) (list x)) #+acl2-loop-only (encapsulate () (define-trusted-clause-processor magic-ev-fncall-cl-proc (magic-ev-fncall) :partial-theory (encapsulate (((magic-ev-fncall * * state * *) => (mv * *))) (logic) (local (defun magic-ev-fncall (fn args state hard-error-returns-nilp aok) (declare (xargs :mode :logic) (ignore fn args state hard-error-returns-nilp aok)) (mv nil nil)))))) #-acl2-loop-only (defun-overrides magic-ev-fncall (fn args state hard-error-returns-nilp aok) (let ((wrld (w state))) (cond ((and (symbolp fn) (true-listp args) (let ((formals (getprop fn 'formals t 'current-acl2-world wrld))) (and (not (eq formals t)) ; (function-symbolp fn wrld) (eql (length args) (length formals)))) (logicalp fn wrld)) (ev-fncall fn args state nil ; latches hard-error-returns-nilp aok)) (t (mv t (cw "~%~%Meta-level function Problem: Magic-ev-fncall ~ attempted to apply ~X02 to argument list ~X12. This is ~ illegal because ~@3. The meta-level function ~ computation was ignored.~%~%" fn args (abbrev-evisc-tuple *the-live-state*) (cond ((not (symbolp fn)) (msg "~x0 is not a symbol" fn)) ((not (true-listp args)) (msg "that argument list is not a true list")) ((eq (getprop fn 'formals t 'current-acl2-world wrld) t) (msg "~x0 is not a known function symbol in the ~ current ACL2 logical world" fn)) ((not (eql (length args) (length (getprop fn 'formals t 'current-acl2-world wrld)))) (msg "The length of that args is ~x0, but ~x1 takes ~ ~x2 arguments" (length args) fn (length (getprop fn 'formals t 'current-acl2-world wrld)))) (t (assert (not (logicalp fn wrld))) (msg "~x0 is not a logic-mode function symbol" fn))))))))) acl2-sources/other-processes.lisp0000666002132200015000000032162212222115527016600 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; Our top-level function for generating variables attempts to feed ; genvar roots that generate names suggestive of the term being ; replaced by the variable. We now develop the code for generating ; these roots. It involves a recursive descent through a term. At ; the bottom, we see variable symbols, e.g., ABC123, and we wish to ; generate the root '("ABC" . 124). (defun strip-final-digits1 (lst) ; See strip-final-digits. (cond ((null lst) (mv "" 0)) ((member (car lst) '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) (mv-let (str n) (strip-final-digits1 (cdr lst)) (mv str (+ (let ((c (car lst))) (case c (#\0 0) (#\1 1) (#\2 2) (#\3 3) (#\4 4) (#\5 5) (#\6 6) (#\7 7) (#\8 8) (otherwise 9))) (* 10 n))))) (t (mv (coerce (reverse lst) 'string) 0)))) (defun strip-final-digits (str) ; Given a string, such as "ABC123", we strip off the final digits in it, ; and compute the number they represent. We return two things, ; the string and the number, e.g., "ABC" and 123. (strip-final-digits1 (reverse (coerce str 'list)))) ; For non-variable, non-quote terms we try first the idea of ; generating a name based on the type of the term. The following ; constant associates with selected type sets the names of some ; variables that we find pleasing and suggestive of the type. When we ; generalize a term we look at its type and if it is a subtype of one ; of those listed we prefer to use the variables given. The first ; variable in each family is additionally used as the root for a ; gensym, should it come to that. This list can be extended ; arbitarily without affecting soundness, as long as (a) the car of ; each pair below is a type set and (b) the cdr is a true-list of ; symbols. Arbitrary overlaps between the types and between the ; symbols are permitted. ;; RAG - I changed rational to real and complex-rational to complex in ;; the list below, since the new types are supersets of the old types, ;; so it should be harmless. (defconst *var-families-by-type* (list (cons *ts-integer* '(I J K L M N)) (cons #+:non-standard-analysis *ts-real* #-:non-standard-analysis *ts-rational* '(R S I J K L M N)) (cons #+:non-standard-analysis *ts-complex* #-:non-standard-analysis *ts-complex-rational* '(Z R S I J K L M N)) (cons *ts-cons* '(L LST)) (cons *ts-boolean* '(P Q R)) (cons *ts-symbol* '(A B C D E)) (cons *ts-string* '(S STR)) (cons *ts-character* '(C CH)))) ; The following function is used to find the family of vars, given the ; type set of a term: (defun assoc-ts-subsetp (ts alist) ; Like assoc except we compare with ts-subsetp. (cond ((null alist) nil) ((ts-subsetp ts (caar alist)) (car alist)) (t (assoc-ts-subsetp ts (cdr alist))))) ; And here is how we look for an acceptable variable. (defun first-non-member-eq (lst1 lst2) ; Return the first member of lst1 that is not a member-eq of lst2. (cond ((null lst1) nil) ((member-eq (car lst1) lst2) (first-non-member-eq (cdr lst1) lst2)) (t (car lst1)))) ; If the above techniques don't lead to a choice we generate a string ; from the term by abbreviating the first symbol in the term. Here is ; how we abbreviate: (defun abbreviate-hyphenated-string1 (str i maximum prev-c) ; We return a list of characters that, when coerced to a string, ; abbreviates string str from position i to (but not including) maximum. ; Currently, it returns the first character after each block of "hyphens" ; and the last character. Thus, "PITON-TEMP-STK" is abbreviated ; "PTSK". ; If prev-char is T it means we output the character we last saw. ; If prev-char is NIL it means the character we last saw was a "hyphen". ; Otherwise, prev-char is the previous character. "Hyphen" here means ; any one of several commonly used "word separators" in symbols. ; This function can be changed arbitrarily as long as it returns a ; list of characters. (cond ((< i maximum) (let ((c (char str i))) (cond ((member c '(#\- #\_ #\. #\/ #\+)) (abbreviate-hyphenated-string1 str (1+ i) maximum nil)) ((null prev-c) (cons c (abbreviate-hyphenated-string1 str (1+ i) maximum t))) (t (abbreviate-hyphenated-string1 str (1+ i) maximum c))))) ((characterp prev-c) (list prev-c)) (t nil))) (defun abbreviate-hyphenated-string (str) ; The function scans a string and collects the first and last character ; and every character immediately after a block of "hyphens" as defined ; above. (let ((lst (abbreviate-hyphenated-string1 str 0 (length str) nil))) (coerce (cond ((or (null lst) (member (car lst) *suspiciously-first-numeric-chars*)) (cons #\V lst)) (t lst)) 'string))) ; Just as strip-final-digits produces the genvar root for a variable, ; the following function produces the genvar root for a nonvariable term. (defun generate-variable-root1 (term avoid-lst type-alist ens wrld) ; Term is a nonvariable, non-quote term. This function returns two ; results, str and n, such that (str . n) is a "root" for genvar. ; In fact, it tries to return a root that when fed to genvar will ; create a variable symbol that is suggestive of term and which does ; not occur in avoid-lst. But the function is correct as long as it ; returns any root, which could be any string. (mv-let (ts ttree) (type-set term t nil type-alist ens wrld nil nil nil) ; Note: We don't really know that the guards have been checked and we ; don't split on the 'assumptions we have forced. But our only use of ; type set here is heuristic. This also explains why we just use the ; global enabled structure and ignore the ttree. (declare (ignore ttree)) (let* ((family (cdr (assoc-ts-subsetp ts *var-families-by-type*))) (var (first-non-member-eq family avoid-lst))) (cond (var ; If the type set of term is one of those for which we have a var family ; and some member of that family does not occur in avoid-lst, then we will ; use the symbol-name of var as the root from which to generate a ; variable symbol for term. This will almost certainly result in the ; generation of the symbol var by genvar. The only condition under which ; this won't happen is if var is an illegal variable symbol, in which case ; genvar will suffix it with some sufficiently large natural. (mv (symbol-name var) nil)) (family ; If we have a family for this type of term but all the members are ; to be avoided, we'll genvar from the first member of the family and ; we might as well start suffixing immediately (from 0) because we ; know the unsuffixed var is in avoid-lst. (mv (symbol-name (car family)) 0)) (t ; Otherwise, we will genvar from an abbreviated version of the "first ; symbol" in term. (mv (abbreviate-hyphenated-string (symbol-name (cond ((variablep term) 'z) ; never happens ((fquotep term) 'z) ; never happens ((flambda-applicationp term) 'z) (t (ffn-symb term))))) nil)))))) ; And here we put them together with one last convention. The ; root for (CAR ...) is just the root for ..., except we force ; there to be a suffix. Thus, the root for (CAR X4) is going to be ; ("X" . 5). (defun generate-variable-root (term avoid-lst type-alist ens wrld) (cond ((variablep term) (mv-let (str n) (strip-final-digits (symbol-name term)) (mv str (1+ n)))) ((fquotep term) (mv "CONST" 0)) ((eq (ffn-symb term) 'car) (mv-let (str n) (generate-variable-root (fargn term 1) avoid-lst type-alist ens wrld) (mv str (or n 0)))) ((eq (ffn-symb term) 'cdr) (mv-let (str n) (generate-variable-root (fargn term 1) avoid-lst type-alist ens wrld) (mv str (or n 0)))) (t (generate-variable-root1 term avoid-lst type-alist ens wrld)))) (defun generate-variable (term avoid-lst type-alist ens wrld) ; We generate a legal variable symbol that does not occur in avoid-lst. We use ; term, type-alist, ens, and wrld in a heuristic way to suggest a preferred ; name for the symbol. Generally speaking, the symbol we generate will be used ; to replace term in some conjecture, so we try to generate a symbol that we ; think "suggests" term. (mv-let (str n) (generate-variable-root term avoid-lst type-alist ens wrld) (genvar (find-pkg-witness term) str n avoid-lst))) (defun generate-variable-lst (term-lst avoid-lst type-alist ens wrld) ; And here we generate a list of variable names sequentially, one for ; each term in term-lst. ; See also generate-variable-lst-simple, which only requires the first two of ; the formals above. (cond ((null term-lst) nil) (t (let ((var (generate-variable (car term-lst) avoid-lst type-alist ens wrld))) (cons var (generate-variable-lst (cdr term-lst) (cons var avoid-lst) type-alist ens wrld)))))) ; That completes the code for generating new variable names. ; An elim-rule, as declared below, denotes a theorem of the form ; (implies hyps (equal lhs rhs)), where rhs is a variable symbol and ; lhs involves the terms destructor-terms, each of which is of the ; form (dfn v1 ... vn), where the vi are distinct variables and {v1 ; ... vn} are all the variables in the formula. We call rhs the ; "crucial variable". It is the one we will "puff up" to eliminate ; the destructor terms. For example, in (CONSP X) -> (CONS (CAR X) ; (CDR X)) = X, X is the crucial variable and puffing it up to (CONS A ; B) we can eliminate (CAR X) and (CDR X). We store an elim-rule ; under the function symbol, dfn, of each destructor term. The rule ; we store for (dfn v1 ... vn) has that term as the car of destructor- ; terms and has crucial-position j where (nth j '(v1 ... vn)) = rhs. ; (Thus, the crucial-position is the position in the args at which the ; crucial variable occurs and for these purposes we enumerate the args ; from 0 (as by nth) rather than from 1 (as by fargn).) (defrec elim-rule (((nume . crucial-position) . (destructor-term . destructor-terms)) (hyps . equiv) (lhs . rhs) . rune) nil) (defun occurs-nowhere-else (var args c i) ; Index the elements of args starting at i. Scan all args except the ; one with index c and return nil if var occurs in one of them and t ; otherwise. (cond ((null args) t) ((int= c i) (occurs-nowhere-else var (cdr args) c (1+ i))) ((dumb-occur var (car args)) nil) (t (occurs-nowhere-else var (cdr args) c (1+ i))))) (defun first-nomination (term votes nominations) ; See nominate-destructor-candidate for an explanation. (cons (cons term (cons term votes)) nominations)) (defun second-nomination (term votes nominations) ; See nominate-destructor-candidate for an explanation. (cond ((null nominations) nil) ((equal term (car (car nominations))) (cons (cons term (union-equal votes (cdr (car nominations)))) (cdr nominations))) (t (cons (car nominations) (second-nomination term votes (cdr nominations)))))) (defun some-hyp-probably-nilp (hyps type-alist ens wrld) ; The name of this function is meant to limit its use to heuristics. ; In fact, if this function says some hyp is probably nil then in fact ; some hyp is known to be nil under the given type-alist, wrld and ; some forced 'assumptions. ; Since the function actually ignores 'assumptions generated, its use ; must be limited to heuristic situations. When it says "yes, some ; hyp is probably nil" we choose not to pursue the establishment of ; those hyps. (cond ((null hyps) nil) (t (mv-let (knownp nilp ttree) (known-whether-nil (car hyps) type-alist ens (ok-to-force-ens ens) nil ; dwp wrld nil) (declare (ignore ttree)) (cond ((and knownp nilp) t) (t (some-hyp-probably-nilp (cdr hyps) type-alist ens wrld))))))) (mutual-recursion (defun sublis-expr (alist term) ; Alist is of the form ((a1 . b1) ... (ak . bk)) where the ai and bi are ; all terms. We substitute bi for each occurrence of ai in term. ; Thus, if the ai are distinct variables, this function is equivalent to ; sublis-var. We do not look for ai's properly inside of quoted objects. ; Thus, ; (sublis-expr '(('3 . x)) '(f '3)) = '(f x) ; but ; (sublis-expr '(('3 . x)) '(f '(3 . 4))) = '(f '(3 . 4)). (let ((temp (assoc-equal term alist))) (cond (temp (cdr temp)) ((variablep term) term) ((fquotep term) term) (t (cons-term (ffn-symb term) (sublis-expr-lst alist (fargs term))))))) (defun sublis-expr-lst (alist lst) (cond ((null lst) nil) (t (cons (sublis-expr alist (car lst)) (sublis-expr-lst alist (cdr lst)))))) ) (defun nominate-destructor-candidate (term eliminables type-alist clause ens wrld votes nominations) ; This function recognizes candidates for destructor elimination. It ; is assumed that term is a non-variable, non-quotep term. To be a ; candidate the term must not be a lambda application and the function ; symbol of the term must have an enabled destructor elimination rule. ; Furthermore, the crucial argument position of the term must be ; occupied by a variable symbol that is a member of the eliminables, ; that occurs only in equiv-hittable positions within the clause, ; and that occurs nowhere else in the arguments of the term, or else ; the crucial argument position must be occupied by a term that itself ; is recursively a candidate. (Note that if the crucial argument is ; an eliminable term then when we eliminate it we will introduce a ; suitable distinct var into the crucial argument of this term and ; hence it will be eliminable.) Finally, the instantiated hypotheses ; of the destructor elimination rule must not be known nil under the ; type-alist. ; Votes and nominations are accumulators. Votes is a list of terms ; that contain term and will be candidates if term is eliminated. ; Nominations are explained below. ; If term is a candidate we either "nominate" it, by adding a ; "nomination" for term to the running accumulator nominations, or ; else we "second" a prior nomination for it. A nomination of a term ; is a list of the form (dterm . votes) where dterm is the innermost ; eliminable candidate in term and votes is a list of all the terms ; that will be eliminable if dterm is eliminated. To "second" a ; nomination is simply to add yourself as a vote. ; For example, if X is eliminable then (CAR (CAR (CAR X))) is a ; candidate. If nominations is initially nil then at the conclusion ; of this function it will be ; (((CAR X) (CAR X) (CAR (CAR X)) (CAR (CAR (CAR X))))). ; We always return a nominations list. (cond ((flambda-applicationp term) nominations) (t (let ((rule (getprop (ffn-symb term) 'eliminate-destructors-rule nil 'current-acl2-world wrld))) (cond ((or (null rule) (not (enabled-numep (access elim-rule rule :nume) ens))) nominations) (t (let ((crucial-arg (nth (access elim-rule rule :crucial-position) (fargs term)))) (cond ((variablep crucial-arg) ; Next we wish to determine that every occurrence of the crucial ; argument -- outside of the destructor nests themselves -- is equiv ; hittable. For example, for car-cdr-elim, where we have A as the ; crucial arg (meaning term, above, is (CAR A) or (CDR A)), we wish to ; determine that every A in the clause is equal-hittable, except those ; A's occurring inside the (CAR A) and (CDR A) destructors. Suppose ; the clause is p(A,(CAR A),(CDR A)). The logical explanation of what ; elim does is to replace the A's not in the destructor nests by (CONS ; (CAR A) (CDR A)) and then generalize (CAR A) to HD and (CDR A) to ; TL. This will produce p((CONS HD TL), HD, TL). Observe that we do ; not actually hit the A's inside the CAR and CDR. So we do not ; require that they be equiv-hittable. (This situation actually ; arises in the elim rule for sets, where equiv tests equality on the ; canonicalizations. In this setting, equiv is not a congruence for ; the destructors.) So the question then is how do we detect that all ; the ``naked'' A's are equiv-hittable? We ``ought'' to generalize ; away the instantiated destructor terms and then ask whether all the ; A's are equiv-hittable. But we do not want to pay the price of ; generating n distinct new variable symbols. So we just replace ; every destructor term by NIL. This creates a ``pseudo-clause;'' the ; ``terms'' in it are not really legal -- NIL is not a variable ; symbol. We only use this pseudo-clause to answer the question of ; whether the crucial variable, which certainly isn't NIL, is ; equiv-hittable in every occurrence. (let* ((alist (pairlis$ (fargs (access elim-rule rule :destructor-term)) (fargs term))) (inst-destructors (sublis-var-lst alist (cons (access elim-rule rule :destructor-term) (access elim-rule rule :destructor-terms)))) (pseudo-clause (sublis-expr-lst (pairlis$ inst-destructors nil) clause))) (cond ((not (every-occurrence-equiv-hittablep-in-clausep (access elim-rule rule :equiv) crucial-arg pseudo-clause ens wrld)) nominations) ((assoc-equal term nominations) (second-nomination term votes nominations)) ((member crucial-arg eliminables) (cond ((occurs-nowhere-else crucial-arg (fargs term) (access elim-rule rule :crucial-position) 0) (let* ((inst-hyps (sublis-var-lst alist (access elim-rule rule :hyps)))) (cond ((some-hyp-probably-nilp inst-hyps type-alist ens wrld) nominations) (t (first-nomination term votes nominations))))) (t nominations))) (t nominations)))) (t (nominate-destructor-candidate crucial-arg eliminables type-alist clause ens wrld (cons term votes) nominations)))))))))) (mutual-recursion (defun nominate-destructor-candidates (term eliminables type-alist clause ens wrld nominations) ; We explore term and accumulate onto nominations all the nominations. (cond ((variablep term) nominations) ((fquotep term) nominations) (t (nominate-destructor-candidates-lst (fargs term) eliminables type-alist clause ens wrld (nominate-destructor-candidate term eliminables type-alist clause ens wrld nil nominations))))) (defun nominate-destructor-candidates-lst (terms eliminables type-alist clause ens wrld nominations) (cond ((null terms) nominations) (t (nominate-destructor-candidates-lst (cdr terms) eliminables type-alist clause ens wrld (nominate-destructor-candidates (car terms) eliminables type-alist clause ens wrld nominations))))) ) ; We next turn to the problem of choosing which candidate we will eliminate. ; We want to eliminate the most complicated one. We measure them with ; max-level-no, which is also used by the defuns principle to store the ; level-no of each fn. Max-level-no was originally defined here, but it is ; mutually recursive with get-level-no, a function we call earlier in the ACL2 ; sources, in sort-approved1-rating1. (defun sum-level-nos (lst wrld) ; Lst is a list of non-variable, non-quotep terms. We sum the ; level-no of the function symbols of the terms. For the level no of ; a lambda expression we use the max level no of its body, just as ; would be done if a non-recursive function with the same body were ; being applied. (cond ((null lst) 0) (t (+ (if (flambda-applicationp (car lst)) (max-level-no (lambda-body (ffn-symb (car lst))) wrld) (or (getprop (ffn-symb (car lst)) 'level-no nil 'current-acl2-world wrld) 0)) (sum-level-nos (cdr lst) wrld))))) (defun pick-highest-sum-level-nos (nominations wrld dterm max-score) ; Nominations is a list of pairs of the form (dterm . votes), where ; votes is a list of terms. The "score" of a dterm is the ; sum-level-nos of its votes. We scan nominations and return a dterm ; with maximal score, assuming that dterm and max-score are the ; winning dterm and its score seen so far. (cond ((null nominations) dterm) (t (let ((score (sum-level-nos (cdr (car nominations)) wrld))) (cond ((> score max-score) (pick-highest-sum-level-nos (cdr nominations) wrld (caar nominations) score)) (t (pick-highest-sum-level-nos (cdr nominations) wrld dterm max-score))))))) (defun select-instantiated-elim-rule (clause type-alist eliminables ens wrld) ; Clause is a clause to which we wish to apply destructor elimination. ; Type-alist is the type-alist obtained by assuming all literals of cl nil. ; Eliminables is the list of legal "crucial variables" which can be ; "puffed up" to do an elim. For example, to eliminate (CAR X), X ; must be puffed up to (CONS A B). X is the crucial variable in (CAR ; X). Upon entry to the destructor elimination process we consider ; all the variables eliminable (except the ones historically ; introduced by elim). But once we get going within the elim process, ; the only eliminable variables are the ones we introduce ourselves ; (because they won't be eliminable by subsequent processes since they ; were introduced by elim). ; If there is at least one nomination for an elim, we choose the one ; with maximal score and return an instantiated version of the ; elim-rule corresponding to it. Otherwise we return nil. (let ((nominations (nominate-destructor-candidates-lst clause eliminables type-alist clause ens wrld nil))) (cond ((null nominations) nil) (t (let* ((dterm (pick-highest-sum-level-nos nominations wrld nil -1)) (rule (getprop (ffn-symb dterm) 'eliminate-destructors-rule nil 'current-acl2-world wrld)) (alist (pairlis$ (fargs (access elim-rule rule :destructor-term)) (fargs dterm)))) (change elim-rule rule :hyps (sublis-var-lst alist (access elim-rule rule :hyps)) :lhs (sublis-var alist (access elim-rule rule :lhs)) :rhs (sublis-var alist (access elim-rule rule :rhs)) :destructor-term (sublis-var alist (access elim-rule rule :destructor-term)) :destructor-terms (sublis-var-lst alist (access elim-rule rule :destructor-terms)))))))) ; We now take a break from elim and develop the code for the generalization ; that elim uses. We want to be able to replace terms by variables ; (sublis-expr, above), we want to be able to restrict the new variables by ; noting type-sets of the terms replaced, and we want to be able to use ; generalization rules provided in the database. (defun type-restriction-segment (cl terms vars type-alist ens wrld) ; Warning: This function calls clausify using the sr-limit from the world, not ; from the rewrite-constant. Do not call this function from the simplifier ; without thinking about passing in the sr-limit. ; Cl is a clause. Terms is a list of terms and is in 1:1 ; correspondence with vars, which is a list of vars. Type-alist is ; the result of assuming false every literal of cl. This function ; returns three results. The first is a list of literals that can be ; disjoined to cl without altering the validity of cl. The second is ; a subset of vars. The third is an extension of ttree. Technically ; speaking, this function may return any list of terms with the ; property that every term in it is false (under the assumptions in ; type-alist) and any subset of vars, provided the ttree returned is ; an extension of ttree and justifies the falsity of the terms ; returned. The final ttree must be 'assumption-free and is if the ; initial ttree is also. ; As for motivation, we are about to generalize cl by replacing each ; term in terms by the corresponding var in vars. It is sound, of ; course, to restrict the new variable to have whatever properties the ; corresponding term has. This function is responsible for selecting ; the restrictions we want to place on each variable, based on ; type-set reasoning alone. Thus, if t is known to have properties h1 ; & ... & hk, then we can include (not h1), ..., (not hk) in our first ; answer to restrict the variable introduced for t. We will include ; the corresponding var in our second answer to indicate that we have ; a type restriction on that variable. ; We do not want our type restrictions to cause the new clause to ; explode into cases. Therefore, we adopt the following heuristic. ; We convert the type set of each term t into a term (hyp t) known to ; be true of t. We negate (hyp t) and clausify the result. If that ; produces a single clause (segment) then that segment is added to our ; answer. Otherwise, we add no restriction. There are probably ; better ways to do this than to call the full-blown ; convert-type-set-to-term and clausify. But this is simple, elegant, ; and lets us take advantage of improvements to those two utilities. (cond ((null terms) (mv nil nil nil)) (t (mv-let (ts ttree1) (type-set (car terms) nil nil type-alist ens wrld nil nil nil) (mv-let (term ttree1) (convert-type-set-to-term (car terms) ts ens wrld ttree1) (let ((clauses (clausify (dumb-negate-lit term) nil t ; Notice that we obtain the sr-limit from the world; see Warning above. (sr-limit wrld)))) (mv-let (lits restricted-vars ttree) (type-restriction-segment cl (cdr terms) (cdr vars) type-alist ens wrld) (cond ((null clauses) ; If the negation of the type restriction term clausifies to the empty set ; of clauses, then the term is nil. Since we get to assume it, we're done. ; But this can only happen if the type-set of the term is empty. We don't think ; this will happen, but we test for it nonetheless, and toss a nil hypothesis ; into our answer literals if it happens. (mv (add-to-set-equal *nil* lits) (cons (car vars) restricted-vars) (cons-tag-trees ttree1 ttree))) ((and (null (cdr clauses)) (not (null (car clauses)))) ; If there is only one clause and it is not the empty clause, we'll ; assume everything in it. (If the clausify above produced '(NIL) ; then the type restriction was just *t* and we ignore it.) It is ; possible that the literals we are about to assume are already in cl. ; If so, we are not fooled into thinking we've restricted the new var. (cond ((subsetp-equal (car clauses) cl) (mv lits restricted-vars ttree)) (t (mv (disjoin-clauses (car clauses) lits) (cons (car vars) restricted-vars) (cons-tag-trees ttree1 ttree))))) (t ; There may be useful type information we could extract, but we don't. ; It is always sound to exit here, giving ourselves no additional ; assumptions. (mv lits restricted-vars ttree)))))))))) (mutual-recursion (defun subterm-one-way-unify (pat term) ; This function searches pat for a non-variable non-quote subterm s such that ; (one-way-unify s term) returns t and a unify-subst. If it finds one, it ; returns t and the unify-subst. Otherwise, it returns two nils. (cond ((variablep pat) (mv nil nil)) ((fquotep pat) (mv nil nil)) (t (mv-let (ans alist) (one-way-unify pat term) (cond (ans (mv ans alist)) (t (subterm-one-way-unify-lst (fargs pat) term))))))) (defun subterm-one-way-unify-lst (pat-lst term) (cond ((null pat-lst) (mv nil nil)) (t (mv-let (ans alist) (subterm-one-way-unify (car pat-lst) term) (cond (ans (mv ans alist)) (t (subterm-one-way-unify-lst (cdr pat-lst) term))))))) ) (defrec generalize-rule (nume formula . rune) nil) (defun apply-generalize-rule (gen-rule term ens) ; Gen-rule is a generalization rule, and hence has a name and a ; formula component. Term is a term which we are intending to ; generalize by replacing it with a new variable. We return two ; results. The first is either t or nil indicating whether gen-rule ; provides a useful restriction on the generalization of term. If the ; first result is nil, so is the second. Otherwise, the second result ; is an instantiation of the formula of gen-rule in which term appears. ; Our heuristic for deciding whether to use gen-rule is: (a) the rule ; must be enabled, (b) term must unify with a non-variable subterm of ; the formula of the rule, (c) the unifying substitution must leave no ; free vars in that formula, and (d) the function symbol of term must ; not occur in the instantiation of the formula except in the ; occurrences of term itself. (cond ((not (enabled-numep (access generalize-rule gen-rule :nume) ens)) (mv nil nil)) (t (mv-let (ans unify-subst) (subterm-one-way-unify (access generalize-rule gen-rule :formula) term) (cond ((null ans) (mv nil nil)) ((free-varsp (access generalize-rule gen-rule :formula) unify-subst) (mv nil nil)) (t (let ((inst-formula (sublis-var unify-subst (access generalize-rule gen-rule :formula)))) (cond ((ffnnamep (ffn-symb term) (subst-expr 'x term inst-formula)) (mv nil nil)) (t (mv t inst-formula)))))))))) (defun generalize-rule-segment1 (generalize-rules term ens) ; Given a list of :GENERALIZE rules and a term we return two results: ; the list of instantiated negated formulas of those applicable rules ; and the runes of all applicable rules. The former list is suitable ; for splicing into a clause to add the formulas as hypotheses. (cond ((null generalize-rules) (mv nil nil)) (t (mv-let (ans formula) (apply-generalize-rule (car generalize-rules) term ens) (mv-let (formulas runes) (generalize-rule-segment1 (cdr generalize-rules) term ens) (cond (ans (mv (add-literal (dumb-negate-lit formula) formulas nil) (cons (access generalize-rule (car generalize-rules) :rune) runes))) (t (mv formulas runes)))))))) (defun generalize-rule-segment (terms vars ens wrld) ; Given a list of terms and a list of vars in 1:1 correspondence, we ; return two results. The first is a clause segment containing the ; instantiated negated formulas derived from every applicable ; :GENERALIZE rule for each term in terms. This segment can be spliced ; into a clause to restrict the range of a generalization of terms. ; The second answer is an alist pairing some of the vars in vars to ; the runes of all :GENERALIZE rules in wrld that are applicable to the ; corresponding term in terms. The second answer is of interest only ; to output routines. (cond ((null terms) (mv nil nil)) (t (mv-let (segment1 runes1) (generalize-rule-segment1 (global-val 'generalize-rules wrld) (car terms) ens) (mv-let (segment2 alist) (generalize-rule-segment (cdr terms) (cdr vars) ens wrld) (cond ((null runes1) (mv segment2 alist)) (t (mv (disjoin-clauses segment1 segment2) (cons (cons (car vars) runes1) alist))))))))) (defun generalize1 (cl type-alist terms vars ens wrld) ; Cl is a clause. Type-alist is a type-alist obtained by assuming all ; literals of cl false. Terms and vars are lists of terms and ; variables, respectively, in 1:1 correspondence. We assume no var in ; vars occurs in cl. We generalize cl by substituting vars for the ; corresponding terms. We restrict the variables by using type-set ; information about the terms and by using :GENERALIZE rules in wrld. ; We return four results. The first is the new clause. The second ; is a list of the variables for which we added type restrictions. ; The third is an alist pairing some variables with the runes of ; generalization rules used to restrict them. The fourth is a ttree ; justifying our work; it is 'assumption-free. (mv-let (tr-seg restricted-vars ttree) (type-restriction-segment cl terms vars type-alist ens wrld) (mv-let (gr-seg alist) (generalize-rule-segment terms vars ens wrld) (mv (sublis-expr-lst (pairlis$ terms vars) (disjoin-clauses tr-seg (disjoin-clauses gr-seg cl))) restricted-vars alist ttree)))) ; This completes our brief flirtation with generalization. We now ; have enough machinery to finish coding destructor elimination. ; However, it might be noted that generalize1 is the main subroutine ; of the generalize-clause waterfall processor. (defun apply-instantiated-elim-rule (rule cl type-alist avoid-vars ens wrld) ; This function takes an instantiated elim-rule, rule, and applies it to a ; clause cl. Avoid-vars is a list of variable names to avoid when we generate ; new ones. See eliminate-destructors-clause for an explanation of that. ; An instantiated :ELIM rule has hyps, lhs, rhs, and destructor-terms, all ; instantiated so that the car of the destructor terms occurs somewhere in the ; clause. To apply such an instantiated :ELIM rule to a clause we assume the ; hyps (adding their negations to cl), we generalize away the destructor terms ; occurring in the clause and in the lhs of the rule, and then we substitute ; that generalized lhs for the rhs into the generalized cl to obtain the final ; clause. The generalization step above may involve adding additional ; hypotheses to the clause and using generalization rules in wrld. ; We return three things. The first is the clause described above, which ; implies cl when the hyps of the rule are known to be true, the second is the ; set of elim variables we have just introduced into it, and the third is a ; list describing this application of the rune of the rule, as explained below. ; The list returned as the third value will become an element in the ; 'elim-sequence list in the ttree of the history entry for this elimination ; process. The "elim-sequence element" we return has the form: ; (rune rhs lhs alist restricted-vars var-to-runes-alist ttree) ; and means "use rune to replace rhs by lhs, generalizing as specified by alist ; (which maps destructors to variables), restricting the restricted-vars ; variables by type (as justified by ttree) and restricting the ; var-to-runes-alist variables by the named generalize rules." The ttree is ; 'assumption-free. (let* ((rune (access elim-rule rule :rune)) (hyps (access elim-rule rule :hyps)) (lhs (access elim-rule rule :lhs)) (rhs (access elim-rule rule :rhs)) (dests (access elim-rule rule :destructor-terms)) (negated-hyps (dumb-negate-lit-lst hyps))) (mv-let (contradictionp type-alist0 ttree0) (type-alist-clause negated-hyps nil nil type-alist ens wrld nil nil) ; Before Version_2.9.3, we just punted when contradictionp is true here, and ; this led to infinite loops reported by Sol Swords and then (shortly ; thereafter) Doug Harper, who both sent examples. Our initial fix was to punt ; without going into the infinite loop, but then we implemented the current ; scheme in which we simply perform the elimination without generating clauses ; for the impossible "pathological" cases corresponding to falsity of each of ; the instantiated :elim rule's hypotheses. Both fixes avoid the infinite loop ; in both examples. We kept the present fix because at the time it actually ; proved the latter example (shown here) without induction: ; (include-book "ihs/@logops" :dir :system) ; (thm (implies (integerp (* 1/2 n)) (equal (mod n 2) 0))) ; However, the fix was buggy. When we fixed those bugs after Version_3.6.1, ; the thm above no longer proved; but we still avoided the infinite loop. That ; loop is easily seen in the following example sent by Eric Smith, which proved ; from Versions 2.9.3 through 3.6.1 by exploiting that bug and looped in ; Versions before 2.9.3: ; (defthmd true-listp-of-cdr ; (implies (true-listp (cdr x)) ; (true-listp x)) ; :hints (("Goal" :in-theory (disable true-listp)))) (let* ((type-alist (if contradictionp type-alist type-alist0)) (cl-with-hyps (disjoin-clauses negated-hyps cl)) (elim-vars (generate-variable-lst dests (all-vars1-lst cl-with-hyps avoid-vars) type-alist ens wrld)) (alist (pairlis$ dests elim-vars)) (generalized-lhs (sublis-expr alist lhs))) (cond (contradictionp ; The negation of the clause implies that the type-alist holds, and thus one of ; the negated-hyps holds. Then since contradictionp is true, the conjunction ; of the hyps implies the clause. That is, *true-clause* implies cl when the ; hyps of the rule are known to be true. (mv *true-clause* nil ; actual-elim-vars (list rune rhs generalized-lhs alist nil ; restricted-vars nil ; var-to-runes-alist ttree0))) (t (let* ((cl-with-hyps (disjoin-clauses negated-hyps cl)) (elim-vars (generate-variable-lst dests (all-vars1-lst cl-with-hyps avoid-vars) type-alist ens wrld))) (mv-let (generalized-cl-with-hyps restricted-vars var-to-runes-alist ttree) (generalize1 cl-with-hyps type-alist dests elim-vars ens wrld) (let* ((final-cl (subst-var-lst generalized-lhs rhs generalized-cl-with-hyps)) (actual-elim-vars (intersection-eq elim-vars (all-vars1-lst final-cl nil)))) (mv final-cl actual-elim-vars (list rune rhs generalized-lhs alist restricted-vars var-to-runes-alist (cons-tag-trees ttree0 ttree)))))))))))) (defun eliminate-destructors-clause1 (cl eliminables avoid-vars ens wrld top-flg) ; Cl is a clause we are trying to prove. Eliminables is the set of variables ; on which we will permit a destructor elimination. Avoid-vars is a list of ; variable names we are to avoid when generating new names. In addition, we ; avoid the variables in cl. We look for an eliminable destructor, select the ; highest scoring one and get its instantiated rule, split on the hyps of that ; rule to produce a "pathological" case of cl for each hyp and apply the rule ; to cl to produce the "normal" elim case. Then we iterate until there is ; nothing more to eliminate. ; The handling of the eliminables needs explanation however. At the top-level ; (when top-flg is t) eliminables is the set of all variables occurring in cl ; except those historically introduced by destructor elimination. It is with ; respect to that set that we select our first elimination rule. Thereafter ; (when top-flg is nil) the set of eliminables is always just the set of ; variables we have introduced into the clauses. We permit these variables to ; be eliminated by this elim and this elim only. For example, the top-level ; entry might permit elimination of (CAR X) and of (CAR Y). Suppose we choose ; X, introducing A and B. Then on the second iteration, we'll allow ; eliminations of A and B, but not of Y. ; We return three things. The first is a set of clauses to prove instead of ; cl. The second is the set of variable names introduced by this destructor ; elimination step. The third is an "elim-sequence list" that documents this ; step. If the list is nil, it means we did nothing. Otherwise it is a list, ; in order, of the "elim-sequence elements" described in ; apply-instantiated-elim-rule above. This list should become the ; 'elim-sequence entry in the ttree for this elim process. ; Historical Remark on Nqthm. ; This code is spiritually very similar to that of Nqthm. However, it is much ; more elegant and easy to understand. Nqthm managed the "iteration" with a ; "todo" list which grew and then shrank. In addition, while we select the ; best rule on each round from scratch, Nqthm kept an ordered list of ; candidates (which it culled appropriately when eliminations removed some of ; them from the clause or when the crucial variables were no longer among ; eliminables). Finally, and most obscurely, Nqthm used an incrutable test on ; the "process history" (related to our elim-sequence) and a subtle invariant ; about the candidates to switch from our top-flg t mode to top-flg nil mode. ; We have spent about a week coding destructor elimination in ACL2 and we have ; thrown away more code that we have kept as we at first transcribed and then ; repeatedly refined the Nqthm code. We are much happier with the current code ; than Nqthm's and believe it will be much easier to modify in the future. Oh, ; one last remark: Nqthm's destructor elimination code had almost no comments ; and everything was done in a single big function with lots of ITERATEs. It ; is no wonder it was so hard to decode. ; Our first step is to get the type-alist of cl. It is used in two different ; ways: to identify contradictory hypotheses of candidate :ELIM lemmas and to ; generate names for new variables. (mv-let (contradictionp type-alist ttree) (type-alist-clause cl nil ; The force-flg must be nil, or else apply-instantiated-elim-rule may call ; generalize1 with a type-alist whose ttrees are not all assumption-free, ; resulting in the return of such a ttree by generalize1 (contrary to its ; specification). The following example was admitted in Version_2.4 and ; Version_4.1, and presumably versions inbetween and perhaps older. ; (progn ; (defun app (x y) ; (if (consp x) ; (cons (car x) (app (cdr x) y)) ; y)) ; (defun rev (x) ; (if (consp x) ; (app (rev (cdr x)) (cons (car x) nil)) ; x)) ; (defthm rev-type ; (implies (force (true-listp x)) ; (true-listp (rev x))) ; :rule-classes :type-prescription) ; (defthm false ; (equal (rev (rev x)) x) ; :rule-classes nil) ; (defthm true ; (equal (rev (rev '(a b . c))) ; '(a b)) ; :rule-classes nil) ; (defthm bug ; nil ; :hints (("Goal" :use (true (:instance false (x '(a b . c)))))) ; :rule-classes nil)) nil ; force-flg; see comment above nil ens wrld nil nil) (declare (ignore ttree)) (cond (contradictionp ; This is unusual. We don't really expect to find a contradiction here. We'll ; return an answer indicating that we didn't do anything. We ignore the ; possibly non-nil ttree here, which is valid given that we are returning the ; same goal clause rather than actually relying on the contradiction. We thus ; ignore ttree everywhere because it is nil when contradictionp is nil. (mv (list cl) nil nil)) (t (let ((rule (select-instantiated-elim-rule cl type-alist eliminables ens wrld))) (cond ((null rule) (mv (list cl) nil nil)) (t (mv-let (new-clause elim-vars1 ele) (apply-instantiated-elim-rule rule cl type-alist avoid-vars ens wrld) (let ((clauses1 (split-on-assumptions (access elim-rule rule :hyps) cl nil))) ; Clauses1 is a set of clauses obtained by splitting on the instantiated hyps ; of the rule. It contains n clauses, each obtained by adding one member of ; inst-hyps to cl. (If any of these new clauses is a tautology, it will be ; deleted, thus there may not be as many clauses as there are inst-hyps.) ; Because these n clauses are all "pathological" wrt the destructor term, e.g., ; we're assuming (not (consp x)) in a clause involving (car x), we do no ; further elimination down those paths. Note the special case where ; contradictionp is true, meaning that we have ascertained that the ; pathological cases are all impossible. (cond ((equal new-clause *true-clause*) (mv clauses1 elim-vars1 (list ele))) (t (mv-let (clauses2 elim-vars2 elim-seq) (eliminate-destructors-clause1 new-clause (if top-flg elim-vars1 (union-eq elim-vars1 (remove1-eq (access elim-rule rule :rhs) eliminables))) avoid-vars ens wrld nil) (mv (conjoin-clause-sets clauses1 clauses2) (union-eq elim-vars1 elim-vars2) (cons ele elim-seq)))))))))))))) (defun owned-vars (process mine-flg history) ; This function takes a process name, e.g., 'eliminate-destructors- ; clause, a flag which must be either nil or t, and a clause history. ; If the flag is t, it returns all of the variables introduced into ; the history by the given process. If the flag is nil, it returns ; all of the variables introduced into the history by any other ; process. Note: the variables returned may not even occur in the ; clause whose history we scan. ; For example, if the only two processes that introduce variables are ; destructor elimination and generalization, then when given ; 'eliminate-destructors-clause and mine-flg nil this function will ; return all the variables introduced by 'generalize-clause. ; In order to work properly, a process that introduces variables must ; so record it by adding a tagged object to the ttree of the process. ; The tag should be 'variables and the object should be a list of the ; variables introduced at that step. There should be at most one ; occurrence of that tag in the ttree. ; Why are we interested in this concept? Destructor elimination is ; controlled by a heuristic meant to prevent indefinite elim loops ; involving simplification. For example, suppose you eliminate (CDR ; X0) by introducing (CONS A X1) for X0, and then open a recursive ; function so as to produce (CDR X1). It is easy to cause a loop if ; you then eliminate (CDR X1) by replacing X1 it with (CONS B X2), ; etc. To prevent this, we do not allow destructor elimination to ; work on a variable that was introduced by destructor elimination ; (except within the activation of the elim process that introduces ; that variable). ; That raises the question of telling how a variable was introduced ; into a clause. In ACL2 we adopt the convention described above and ; follow the rule that no process shall introduce a variable into a ; clause that has been introduced by a different process in the ; history of that clause. Thus, if X1 is introduced by elim into the ; history, then X1 cannot also be introduced by generalization, even ; if X1 is new for the clause when generalization occurs. By ; following this rule we know that if a variable is in a clause and ; that variable was introduced into the history of the clause by elim ; then that variable was introduced into the clause by elim. If ; generalize could "re-use" a variable that was already "owned" by ; elim in the history, then we could not accurately determine by ; syntactic means the elim variables in the clause. ; Historical Remark on Nqthm: ; Nqthm solved this problem by allocating a fixed set of variable names ; to elim and a disjoint set to generalize. At the top of the waterfall it ; removed from those two fixed sets the variables that occurred in the ; input clause. Thereafter, if a variable was found to be in the (locally) ; fixed sets, it was known to be introduced by the given process. The ; limitation to a fixed set caused the famous set-dif-n error message ; when the set was exhausted: ; FATAL ERROR: SET-DIFF-N called with inappropriate arguments. ; In the never-released xnqthm -- the "book version" of Nqthm that was ; in preparation when we began work on ACL2 -- we generated a more ; informative error message and increased the size of the fixed sets ; from 18 to over 600. But that meant copying a list of length 600 at ; the top of the waterfall. But the real impetus to the current ; scheme was the irritation over there being a fixed set and the ; attraction of being able to generate mnemonic names from terms. (It ; remains to be seen whether we like the current algorithms. E.g., is ; AENI really a good name for (EXPLODE-NONNEGATIVE-INTEGER N 10 A)? ; In any case, now we are free to experiment with name generation.) (cond ((null history) nil) ((eq mine-flg (eq (access history-entry (car history) :processor) process)) (union-eq (tagged-object 'variables (access history-entry (car history) :ttree)) (owned-vars process mine-flg (cdr history)))) (t (owned-vars process mine-flg (cdr history))))) (defun eliminate-destructors-clause (clause hist pspv wrld state) ; This is the waterfall processor that eliminates destructors. ; Like all waterfall processors it returns four values: 'hit or 'miss, ; and, if 'hit, a set of clauses, a ttree, and a possibly modified pspv. (declare (ignore state)) (mv-let (clauses elim-vars elim-seq) (eliminate-destructors-clause1 clause (set-difference-eq (all-vars1-lst clause nil) (owned-vars 'eliminate-destructors-clause t hist)) (owned-vars 'eliminate-destructors-clause nil hist) (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :current-enabled-structure) wrld t) (cond (elim-seq (mv 'hit clauses (add-to-tag-tree! 'variables elim-vars (add-to-tag-tree! 'elim-sequence elim-seq nil)) pspv)) (t (mv 'miss nil nil nil))))) ; We now develop the code to describe the destructor elimination done, ; starting with printing clauses prettily. (defun prettyify-clause1 (cl wrld) (cond ((null (cdr cl)) nil) (t (cons (untranslate (dumb-negate-lit (car cl)) t wrld) (prettyify-clause1 (cdr cl) wrld))))) (defun prettyify-clause2 (cl wrld) (cond ((null cl) nil) ((null (cdr cl)) (untranslate (car cl) t wrld)) ((null (cddr cl)) (list 'implies (untranslate (dumb-negate-lit (car cl)) t wrld) (untranslate (cadr cl) t wrld))) (t (list 'implies (cons 'and (prettyify-clause1 cl wrld)) (untranslate (car (last cl)) t wrld))))) ; Rockwell Addition: Prettyify-clause now has a new arg to control ; whether we abstract away common subexprs. This will show up many ; times in a compare-windows. (defun prettyify-clause (cl let*-abstractionp wrld) (if let*-abstractionp (mv-let (vars terms) (maximal-multiples (cons 'list cl) let*-abstractionp) (cond ((null vars) (prettyify-clause2 cl wrld)) (t `(let* ,(listlis vars (untranslate-lst (all-but-last terms) nil wrld)) ,(prettyify-clause2 (cdr (car (last terms))) wrld))))) (prettyify-clause2 cl wrld))) (defun prettyify-clause-lst (clauses let*-abstractionp wrld) (cond ((null clauses) nil) (t (cons (prettyify-clause (car clauses) let*-abstractionp wrld) (prettyify-clause-lst (cdr clauses) let*-abstractionp wrld))))) (defun prettyify-clause-set (clauses let*-abstractionp wrld) (cond ((null clauses) t) ((null (cdr clauses)) (prettyify-clause (car clauses) let*-abstractionp wrld)) (t (cons 'and (prettyify-clause-lst clauses let*-abstractionp wrld))))) (defun tilde-*-elim-phrase/alist1 (alist wrld) (cond ((null alist) nil) (t (cons (msg "~p0 by ~x1" (untranslate (caar alist) nil wrld) (cdar alist)) (tilde-*-elim-phrase/alist1 (cdr alist) wrld))))) (defun tilde-*-elim-phrase/alist (alist wrld) ; Alist is never nil, except in the unusual case that ; apply-instantiated-elim-rule detected a tautology where we claim ; none could occur. If that happens we print the phrase "generalizing ; nothing". This is documented simply because it is strange to put ; anything in the 0 case below. (list* "" " and ~@*" ", ~@*" ", ~@*" (tilde-*-elim-phrase/alist1 alist wrld) nil)) (defun tilde-*-elim-phrase3 (var-to-runes-alist) (cond ((null var-to-runes-alist) nil) (t (cons (msg "noting the condition imposed on ~x0 by the ~ generalization rule~#1~[~/s~] ~&1" (caar var-to-runes-alist) (strip-base-symbols (cdar var-to-runes-alist))) (tilde-*-elim-phrase3 (cdr var-to-runes-alist)))))) (defun tilde-*-elim-phrase2 (alist restricted-vars var-to-runes-alist ttree wrld) (list* "" "~@*" "~@* and " "~@*, " (append (list (msg "~*0" (tilde-*-elim-phrase/alist alist wrld))) (cond (restricted-vars (let ((simp-phrase (tilde-*-simp-phrase ttree))) (cond ((null (cdr restricted-vars)) (list (msg "restrict the type of the new ~ variable ~&0 to be that of the term ~ it replaces~#1~[~/, as established ~ by ~*2~]" restricted-vars (if (nth 4 simp-phrase) 1 0) simp-phrase))) (t (list (msg "restrict the types of the new ~ variables ~&0 to be those of the ~ terms they replace~#1~[~/, as ~ established by ~*2~]" restricted-vars (if (nth 4 simp-phrase) 1 0) simp-phrase)))))) (t nil)) (tilde-*-elim-phrase3 var-to-runes-alist)) nil)) (defun tilde-*-elim-phrase1 (lst i already-used wrld) (cond ((null lst) nil) (t (cons (cons "(~xi) ~#f~[Use~/Finally, use~] ~#a~[~x0~/~x0, again,~] to ~ replace ~x1 by ~p2~*3. " (list (cons #\i i) (cons #\f (if (and (null (cdr lst)) (> i 2)) 1 0)) (cons #\a (if (member-equal (nth 0 (car lst)) already-used) (if (member-equal (nth 0 (car lst)) (cdr (member-equal (nth 0 (car lst)) already-used))) 0 1) 0)) (cons #\0 (base-symbol (nth 0 (car lst)))) (cons #\1 (nth 1 (car lst))) (cons #\2 (untranslate (nth 2 (car lst)) nil wrld)) (cons #\3 (tilde-*-elim-phrase2 (nth 3 (car lst)) (nth 4 (car lst)) (nth 5 (car lst)) (nth 6 (car lst)) wrld)))) (tilde-*-elim-phrase1 (cdr lst) (1+ i) (cons (nth 0 (car lst)) already-used) wrld))))) (defun tilde-*-elim-phrase (lst wrld) ; Lst is the 'elim-sequence list of the ttree of the elim process, ; i.e., it is the third result of eliminate-destructors-clause1 above, ; the third result of apply-instantiated-elim-rule, i.e., a list of ; elements of the form ; (rune rhs lhs alist restricted-vars var-to-runes-alist ttree). ; We generate an object suitable for giving to the tilde-* fmt directive ; that will cause each element of the list to print out a phrase ; describing that step. (list* "" "~@*" "~@*" "~@*" (tilde-*-elim-phrase1 lst 1 nil wrld) nil)) (defun tilde-*-untranslate-lst-phrase (lst flg wrld) (list* "" "~p*" "~p* and " "~p*, " (untranslate-lst lst flg wrld) nil)) (defun eliminate-destructors-clause-msg1 (signal clauses ttree pspv state) ; The arguments to this function are the standard ones for an output ; function in the waterfall. See the discussion of the waterfall. (declare (ignore signal pspv)) (let ((lst (tagged-object 'elim-sequence ttree)) (n (length clauses)) (wrld (w state))) (cond ((null (cdr lst)) (fms "The destructor term~#p~[~/s~] ~*0 can be eliminated by using ~x1 ~ to replace ~p2 by ~p3~*4. ~#5~[All the clauses produced are ~ tautological.~/This produces the following goal.~/This produces ~ the following ~n6 goals.~]~|" (list (cons #\p (nth 3 (car lst))) (cons #\0 (tilde-*-untranslate-lst-phrase (strip-cars (nth 3 (car lst))) nil wrld)) (cons #\1 (base-symbol (nth 0 (car lst)))) (cons #\2 (nth 1 (car lst))) (cons #\3 (untranslate (nth 2 (car lst)) nil wrld)) (cons #\4 (tilde-*-elim-phrase2 (nth 3 (car lst)) (nth 4 (car lst)) (nth 5 (car lst)) (nth 6 (car lst)) wrld)) (cons #\5 (zero-one-or-more n)) (cons #\6 n)) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "The destructor term~#p~[~/s~] ~*0 can be eliminated. Furthermore, ~ ~#p~[that term is~/those terms are~] at the root of a ~ chain of ~n1 rounds of destructor elimination. ~*2 ~ These steps produce ~#3~[no nontautological goals~/the ~ following goal~/the following ~n4 goals~].~|" (list (cons #\p (nth 3 (car lst))) (cons #\0 (tilde-*-untranslate-lst-phrase (strip-cars (nth 3 (car lst))) nil wrld)) (cons #\1 (length lst)) (cons #\2 (tilde-*-elim-phrase lst wrld)) (cons #\3 (zero-one-or-more n)) (cons #\4 n)) (proofs-co state) state (term-evisc-tuple nil state)))))) ; We now develop the cross-fertilization process. (mutual-recursion (defun almost-quotep1 (term) (cond ((variablep term) t) ((fquotep term) t) ((flambda-applicationp term) (and (almost-quotep1 (lambda-body term)) (almost-quotep1-listp (fargs term)))) ((eq (ffn-symb term) 'cons) (and (almost-quotep1 (fargn term 1)) (almost-quotep1 (fargn term 2)))) (t nil))) (defun almost-quotep1-listp (terms) (cond ((null terms) t) (t (and (almost-quotep1 (car terms)) (almost-quotep1-listp (cdr terms)))))) ) (defun almost-quotep (term) ; A term is "almost a quotep" if it is a non-variablep term that ; consists only of variables, explicit values, and applications of ; cons. Lambda-applications are permitted provided they have ; almost-quotep bodies and args. ; Further work: See equal-x-cons-x-yp. (and (nvariablep term) (almost-quotep1 term))) (defun destructor-applied-to-varsp (term ens wrld) ; We determine whether term is of the form (destr v1 ... vn) ; where destr has an enabled 'eliminate-destructors-rule ; and all the vi are variables. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) nil) (t (and (all-variablep (fargs term)) (let ((rule (getprop (ffn-symb term) 'eliminate-destructors-rule nil 'current-acl2-world wrld))) (and rule (enabled-numep (access elim-rule rule :nume) ens))))))) (defun dumb-occur-lst-except (term lst lit) ; Like dumb-occur-lst except that it does not look into the first ; element of lst that is equal to lit. If you think of lst as a ; clause and lit as a literal, we ask whether term occurs in some ; literal of clause other than lit. In Nqthm we looked for an eq ; occurrence of lit, which we can't do here. But if there are two ; occurrences of lit in lst, then normally in Nqthm they would not ; be eq and hence we'd look in one of them. Thus, here we look in ; all the literals of lst after we've seen lit. This is probably ; unnecessarily complicated. (cond ((null lst) nil) ((equal lit (car lst)) (dumb-occur-lst term (cdr lst))) (t (or (dumb-occur term (car lst)) (dumb-occur-lst-except term (cdr lst) lit))))) (defun fertilize-feasible (lit cl hist term ens wrld) ; Lit is a literal of the form (not (equiv term val)) (or the commuted ; form). We determine if it is feasible to substitute val for term in ; clause cl. By that we mean that term is neither a near constant nor ; a destructor, term does occur elsewhere in the clause, every ; occurrence of term is equiv-hittable, and we haven't already ; fertilized with this literal. (and (not (almost-quotep term)) (not (destructor-applied-to-varsp term ens wrld)) (dumb-occur-lst-except term cl lit) (every-occurrence-equiv-hittablep-in-clausep (ffn-symb (fargn lit 1)) term cl ens wrld) (not (already-used-by-fertilize-clausep lit hist t)))) (mutual-recursion (defun fertilize-complexity (term wrld) ; The fertilize-complexity of (fn a1 ... an) is the level number of fn ; plus the maximum fertilize complexity of ai. (cond ((variablep term) 0) ((fquotep term) 0) (t (+ (get-level-no (ffn-symb term) wrld) (maximize-fertilize-complexity (fargs term) wrld))))) (defun maximize-fertilize-complexity (terms wrld) (cond ((null terms) 0) (t (max (fertilize-complexity (car terms) wrld) (maximize-fertilize-complexity (cdr terms) wrld))))) ) (defun first-fertilize-lit (lst cl hist ens wrld) ; We find the first literal lst of the form (not (equiv lhs1 rhs1)) ; such that a fertilization of one side for the other into cl is ; feasible. We return six values. The first is either nil, meaning ; no such lit was found, or a direction of 'left-for-right or ; 'right-for-left. The second is the literal found. The last three are ; the equiv, lhs, and rhs of the literal, and the length of the tail of ; cl after lit. (cond ((null lst) (mv nil nil nil nil nil nil)) (t (let ((lit (car lst))) (case-match lit (('not (equiv lhs rhs)) (cond ((equivalence-relationp equiv wrld) (cond ((fertilize-feasible lit cl hist lhs ens wrld) (cond ((fertilize-feasible lit cl hist rhs ens wrld) (cond ((< (fertilize-complexity lhs wrld) (fertilize-complexity rhs wrld)) (mv 'left-for-right lit equiv lhs rhs (len (cdr lst)))) (t (mv 'right-for-left lit equiv lhs rhs (len (cdr lst)))))) (t (mv 'right-for-left lit equiv lhs rhs (len (cdr lst)))))) ((fertilize-feasible lit cl hist rhs ens wrld) (mv 'left-for-right lit equiv lhs rhs (len (cdr lst)))) (t (first-fertilize-lit (cdr lst) cl hist ens wrld)))) (t (first-fertilize-lit (cdr lst) cl hist ens wrld)))) (& (first-fertilize-lit (cdr lst) cl hist ens wrld))))))) (defun cross-fertilizep/c (equiv cl direction lhs1 rhs1) ; See condition (c) of cross-fertilizep. (cond ((null cl) nil) ((and (nvariablep (car cl)) (not (fquotep (car cl))) (equal (ffn-symb (car cl)) equiv) (if (eq direction 'left-for-right) (dumb-occur rhs1 (fargn (car cl) 2)) (dumb-occur lhs1 (fargn (car cl) 1)))) t) (t (cross-fertilizep/c equiv (cdr cl) direction lhs1 rhs1)))) (defun cross-fertilizep/d (equiv cl direction lhs1 rhs1) ; See condition (d) of cross-fertilizep. (cond ((null cl) nil) ((and (nvariablep (car cl)) (not (fquotep (car cl))) (equal (ffn-symb (car cl)) equiv) (if (eq direction 'left-for-right) (dumb-occur rhs1 (fargn (car cl) 1)) (dumb-occur lhs1 (fargn (car cl) 2)))) t) (t (cross-fertilizep/d equiv (cdr cl) direction lhs1 rhs1)))) (defun cross-fertilizep (equiv cl pspv direction lhs1 rhs1) ; We have found a literal, (not (equiv lhs1 rhs1)), of cl such that a ; fertilization is feasible in the indicated direction. We want to ; know whether this will be a cross-fertilization or not. Suppose, ; without loss of generality, that the direction is 'left-for-right, ; i.e., we are going to substitute lhs1 for rhs1. A cross- ; fertilization is performed only if (a) neither lhs1 nor rhs1 is an ; explicit value, (b) we are under an induction (thus our interest in ; pspv), (c) there is some equiv literal, (equiv lhs2 rhs2), in the ; clause such that rhs1 occurs in rhs2 (thus we'll hit rhs2) and (d) ; there is some equiv literal such that rhs1 occurs in lhs2 (thus, ; cross fertilization will actually prevent us from hitting something ; massive substitution would hit). Note that since we know the ; fertilization is feasible, every occurrence of the target is in an ; equiv-hittable slot. Thus, we can use equivalence-insensitive occur ; checks rather than being prissy. (and (not (quotep lhs1)) (not (quotep rhs1)) (assoc-eq 'being-proved-by-induction (access prove-spec-var pspv :pool)) (cross-fertilizep/c equiv cl direction lhs1 rhs1) (cross-fertilizep/d equiv cl direction lhs1 rhs1))) (defun delete-from-ttree (tag val ttree) (let ((objects (tagged-objects tag ttree))) (cond (objects (cond ((member-equal val objects) (let ((new-objects (remove1-equal val objects)) (new-ttree (remove-tag-from-tag-tree! tag ttree))) (cond (new-objects (extend-tag-tree tag new-objects new-ttree)) (t new-ttree)))) (t ttree))) (t ttree)))) (defun fertilize-clause1 (cl lit1 equiv lhs1 rhs1 direction cross-fert-flg delete-lit-flg ens wrld state ttree) ; Cl is a clause we are fertilizing with lit1, which is one of its ; literals and which is of the form (not (equiv lhs1 rhs1)). Direction is ; 'left-for-right or 'right-for-left, indicating which way we're to ; substitute. Cross-fert-flg is t if we are to hit only (equiv lhs2 ; rhs2) and do it in a cross-fertilize sort of way (left for right ; into right or right for left into left); otherwise we substitute for ; all occurrences. Delete-lit-flg is t if we are to delete the first ; occurrence of lit when we see it. We return two things: the new ; clause and a ttree indicating the congruences used. (cond ((null cl) (mv nil ttree)) (t (let* ((lit2 (car cl)) (lit2-is-lit1p (equal lit2 lit1))) ; First, we substitute into lit2 as appropriate. We obtain new-lit2 ; and a ttree. We ignore the hitp result always returned by ; subst-equiv-expr. ; What do we mean by "as appropriate"? We consider three cases on ; lit2, the literal into which we are substituting: lit2 is (equiv lhs ; rhs), lit2 is (not (equiv lhs rhs)), or otherwise. We also consider ; whether we are cross fertilizing or just substituting for all ; occurrences. Here is a table that explains our actions below. ; lit2 (equiv lhs rhs) (not (equiv lhs rhs)) other ; xfert xfert subst no action ; subst subst subst subst ; The only surprising part of this table is that in the case of ; cross-fertilizing into (not (equiv lhs rhs)), i.e., into another ; equiv hypothesis, we do a full-fledged substitution rather than a ; cross-fertilization. I do not give an example of why we do this. ; However, it is exactly what Nqthm does (in the only comparable case, ; namely, when equiv is EQUAL). (mv-let (hitp new-lit2 ttree) (cond (lit2-is-lit1p (mv nil lit2 ttree)) ((or (not cross-fert-flg) (case-match lit2 (('not (equiv-sym & &)) (equal equiv-sym equiv)) (& nil))) (cond ((eq direction 'left-for-right) (subst-equiv-expr equiv lhs1 rhs1 *geneqv-iff* lit2 ens wrld state ttree)) (t (subst-equiv-expr equiv rhs1 lhs1 *geneqv-iff* lit2 ens wrld state ttree)))) (t ; Caution: There was once a bug below. We are cross fertilizing. ; Suppose we see (equiv lhs2 rhs2) and want to substitute lhs1 for ; rhs1 in rhs2. What geneqv do we maintain? The bug, which was ; completely nonsensical, was that we maintained *geneqv-iff*, just as ; above. But in fact we must maintain whatever geneqv maintains ; *geneqv-iff* in the second arg of equiv. Geneqv-lst returns a list ; of geneqvs, one for each argument position of equiv. We select the ; one in the argument position corresponding to the side we are ; changing. Actually, the two geneqvs for an equivalence relation ; ought to be the identical, but it would be confusing to exploit ; that. ; In the days when this bug was present there was another problem! We ; only substituted into (equal lhs2 rhs2)! (That is, the case-match ; below was on ('equal lhs2 rhs2) rather than (equiv-sym lhs2 rhs2).) ; So here is an example of a screwy substitution we might have done: ; Suppose (equiv a b) is a hypothesis and (equal (f a) (f b)) is a ; conclusion and that we are to do a cross-fertilization of a for b. ; We ought not to substitute into equal except maintaining equality. ; But we actually would substitute into (f b) maintaining iff! Now ; suppose we knew that (equiv x y) -> (iff (f x) (f y)). Then we ; could derive (equal (f a) (f a)), which would be t and unsound. The ; preconditions for this screwy situation are exhibited by: ; (defun squash (x) ; (cond ((null x) nil) ; ((integerp x) 1) ; (t t))) ; ; (defun equiv (x y) ; (equal (squash x) (squash y))) ; ; (defequiv equiv) ; ; (defun f (x) x) ; ; (defcong equiv iff (f x) 1) ; ; In particular, (implies (equiv a b) (equal (f a) (f b))) is not a ; theorem (a=1 and b=2 are counterexamples), but this function, if ; called with that input clause, ((not (equiv a b)) (equal (f a) (f ; b))), and the obvious lit1, etc., would return (equal (f a) (f a)), ; which is T. (Here we are substituting left for right, a for b.) So ; there was a soundness bug in the old version of this function. ; But it turns out that this bug could never be exploited. The bug ; can be provoked only if we are doing cross-fertilization. And ; cross-fertilization is only done if the fertilization is "feasible". ; That means that every occurrence of b in the clause is equiv ; hittable, as per every-occurrence-equiv-hittablep-in-clausep. In ; our example, the b in (f b) is not equiv hittable. Indeed, if every ; occurrence of b is equiv hittable then no matter what braindamaged ; geneqv we use below, the result will be sound! A braindamaged ; geneqv might prevent us from hitting some, but any hit it allowed is ; ok. ; This bug was first noticed by Bill McCune (September, 1998), who ; reported an example in which the system io indicated that a was ; substituted for b but in fact no substitution occurred. No ; substitution occurred because we didn't have the congruence theorem ; shown above -- not a surprising lack considering the random nature ; of the problem. At first I was worried about soundness but then saw ; the argument above. (case-match lit2 ((equiv-sym lhs2 rhs2) (cond ((not (equal equiv-sym equiv)) (mv nil lit2 ttree)) ((eq direction 'left-for-right) (mv-let (hitp new-rhs2 ttree) (subst-equiv-expr equiv lhs1 rhs1 (cadr (geneqv-lst equiv *geneqv-iff* ens wrld)) rhs2 ens wrld state ttree) (declare (ignore hitp)) (mv nil (mcons-term* equiv lhs2 new-rhs2) ttree))) (t (mv-let (hitp new-lhs2 ttree) (subst-equiv-expr equiv rhs1 lhs1 (car (geneqv-lst equiv *geneqv-iff* ens wrld)) lhs2 ens wrld state ttree) (declare (ignore hitp)) (mv nil (mcons-term* equiv new-lhs2 rhs2) ttree))))) (& (mv nil lit2 ttree))))) (declare (ignore hitp)) ; Second, we recursively fertilize appropriately into the rest of the clause. (mv-let (new-tail ttree) (fertilize-clause1 (cdr cl) lit1 equiv lhs1 rhs1 direction cross-fert-flg (if lit2-is-lit1p nil delete-lit-flg) ens wrld state ttree) ; Finally, we combine the two, deleting the lit if required. (cond (lit2-is-lit1p (cond (delete-lit-flg (mv new-tail (cond ((eq direction 'left-for-right) (add-binding-to-tag-tree rhs1 lhs1 ttree)) (t (add-binding-to-tag-tree lhs1 rhs1 ttree))))) (t (mv-let (not-flg atm) (strip-not lit2) (prog2$ (or not-flg (er hard 'fertilize-clause1 "We had thought that we only ~ fertilize with negated literals, ~ unlike ~x0!" new-lit2)) (prog2$ (or (equal lit2 new-lit2) ; should be eq (er hard 'fertilize-clause1 "Internal error in ~ fertilize-clause1!~|Old lit2: ~ ~x0.~|New lit2: ~x1" lit2 new-lit2)) (mv (cons (mcons-term* 'not (mcons-term* 'hide atm)) new-tail) ttree))))))) (t (mv (cons new-lit2 new-tail) ttree))))))))) (defun fertilize-clause (cl-id cl hist pspv wrld state) ; A standard clause processor of the waterfall. ; We return 4 values. The first is a signal that is either 'hit, or ; 'miss. When the signal is 'miss, the other 3 values are irrelevant. ; When the signal is 'hit, the second result is the list of new ; clauses, the third is a ttree that will become that component of the ; history-entry for this fertilization, and the fourth is an ; unmodified pspv. (We return the fourth thing to adhere to the ; convention used by all clause processors in the waterfall (q.v.).) ; The ttree we return has seven tagged objects in it plus a bunch ; of 'lemmas indicating the :CONGRUENCE rules used. ; 'literal - the literal from cl we used, guaranteed to be of ; the form (not (equiv lhs rhs)). ; 'clause-id - the current clause-id ; 'hyp-phrase - a tilde-@ phrase that describes literal in cl. ; 'equiv - the equivalence relation ; 'bullet - the term we substituted ; 'target - the term we substituted for ; 'cross-fert-flg - whether we did a cross fertilization ; 'delete-lit-flg - whether we deleted literal from the ; clause. (mv-let (direction lit equiv lhs rhs len-tail) (first-fertilize-lit cl cl hist (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :current-enabled-structure) wrld) (cond ((null direction) (mv 'miss nil nil nil)) (t (let ((cross-fert-flg (cross-fertilizep equiv cl pspv direction lhs rhs)) (delete-lit-flg (and (not (quotep lhs)) (not (quotep rhs)) (assoc-eq 'being-proved-by-induction (access prove-spec-var pspv :pool))))) (mv-let (new-cl ttree) (fertilize-clause1 cl lit equiv lhs rhs direction cross-fert-flg delete-lit-flg (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :current-enabled-structure) wrld state nil) (mv 'hit (list new-cl) (add-to-tag-tree! 'literal lit (add-to-tag-tree! 'hyp-phrase (tilde-@-hyp-phrase len-tail cl) (add-to-tag-tree! 'cross-fert-flg cross-fert-flg (add-to-tag-tree! 'equiv equiv (add-to-tag-tree! 'bullet (if (eq direction 'left-for-right) lhs rhs) (add-to-tag-tree! 'target (if (eq direction 'left-for-right) rhs lhs) (add-to-tag-tree! 'clause-id cl-id (add-to-tag-tree! 'delete-lit-flg delete-lit-flg (if delete-lit-flg ttree (push-lemma (fn-rune-nume 'hide nil nil wrld) ttree)))))))))) pspv))))))) (defun fertilize-clause-msg1 (signal clauses ttree pspv state) ; The arguments to this function are the standard ones for an output ; function in the waterfall. See the discussion of the waterfall. (declare (ignore signal pspv clauses)) (let* ((hyp-phrase (tagged-object 'hyp-phrase ttree)) (wrld (w state)) (ttree ; We can get away with eliminating the :definition of hide from the ttree ; because fertilize-clause1 only pushes lemmas by way of subst-equiv-expr, ; which are either about geneqvs (from geneqv-refinementp) or are executable ; counterparts (from scons-term). If we do not delete the definition of hide ; from ttree here, we get a bogus "validity of this substitution relies upon ; the :definition HIDE" message. (delete-from-ttree 'lemma (fn-rune-nume 'hide nil nil wrld) ttree))) (fms "We now use ~@0 by ~#1~[substituting~/cross-fertilizing~] ~p2 for ~p3 ~ and ~#4~[hiding~/throwing away~] the ~@5.~#6~[~/ The validity of ~ this substitution relies upon ~*7.~] This produces~|" (list (cons #\0 hyp-phrase) (cons #\1 (if (tagged-object 'cross-fert-flg ttree) 1 0)) (cons #\2 (untranslate (tagged-object 'bullet ttree) nil wrld)) (cons #\3 (untranslate (tagged-object 'target ttree) nil wrld)) (cons #\4 (if (tagged-object 'delete-lit-flg ttree) 1 0)) (cons #\5 (if (and (consp hyp-phrase) (null (cdr hyp-phrase))) "conclusion" "hypothesis")) (cons #\6 (if (tagged-objectsp 'lemma ttree) 1 0)) (cons #\7 (tilde-*-simp-phrase ttree))) (proofs-co state) state (term-evisc-tuple nil state)))) ; And now we do generalization... (defun collectable-fnp (fn ens wrld) ; A common collectable term is a non-quoted term that is an ; application of a collectable-fnp. Most functions are common ; collectable. The ones that are not are cons, open lambdas, and the ; (enabled) destructors of wrld. (cond ((flambdap fn) nil) ((eq fn 'cons) nil) (t (let ((rule (getprop fn 'eliminate-destructors-rule nil 'current-acl2-world wrld))) (cond ((and rule (enabled-numep (access elim-rule rule :nume) ens)) nil) (t t)))))) (mutual-recursion (defun smallest-common-subterms1 (term1 term2 ens wrld ans) ; This is the workhorse of smallest-common-subterms, but the arguments are ; arranged so that we know that term1 is the smaller. We add to ans ; every subterm x of term1 that (a) occurs in term2, (b) is ; collectable, and (c) has no collectable subterms in common with ; term2. ; We return two values. The first is the modified ans. The second is ; t or nil according to whether term1 occurs in term2 but neither it ; nor any of its subterms is collectable. This latter condition is ; said to be the ``potential'' of term1 participating in a collection ; vicariously. What does that mean? Suppose a1, ..., an, all have ; potential. Then none of them are collected (because they aren't ; collectable) but each occurs in term2. Thus, a term such as (fn a1 ; ... an) might actually be collected because it may occur in term2 ; (all of its args do, at least), it may be collectable, and none of ; its subterms are. So those ai have the potential to participate ; vicariously in a collection. (cond ((or (variablep term1) (fquotep term1)) ; Since term1 is not collectable, we don't add it to ans. But we return ; t as our second value if term1 occurs in term2, i.e., term1 has ; potential. (mv ans (occur term1 term2))) (t (mv-let (ans all-potentials) (smallest-common-subterms1-lst (fargs term1) term2 ens wrld ans) (cond ((null all-potentials) ; Ok, some arg did not have potential. Either it did not occur or it ; was collected. In either case, term1 should not be collected and ; furthermore, has no potential for participating later. (mv ans nil)) ((not (occur term1 term2)) ; Every arg of term1 had potential but term1 doesn't occur in ; term2. That means we don't collect it and it hasn't got ; potential. (mv ans nil)) ((collectable-fnp (ffn-symb term1) ens wrld) ; So term1 occurs, none of its subterms were collected, and term1 ; is collectable. So we collect it, but it no longer has potential ; (because it got collected). (mv (add-to-set-equal term1 ans) nil)) (t ; Term1 occurs, none of its subterms were collected, and term1 ; was not collected. So it has potential to participate vicariously. (mv ans t))))))) (defun smallest-common-subterms1-lst (terms term2 ens wrld ans) ; We accumulate onto ans every subterm of every element of terms ; that (a) occurs in term2, (b) is collectable, and (c) has no ; collectable subterms in common with term2. We return the modified ; ans and the flag indicating whether all of the terms have potential. (cond ((null terms) (mv ans t)) (t (mv-let (ans car-potential) (smallest-common-subterms1 (car terms) term2 ens wrld ans) (mv-let (ans cdr-potential) (smallest-common-subterms1-lst (cdr terms) term2 ens wrld ans) (mv ans (and car-potential cdr-potential))))))) ) (defun dumb-fn-count-1 (flg x acc) (declare (xargs :guard (and (if flg (pseudo-term-listp x) (pseudo-termp x)) (natp acc)))) (cond (flg (cond ((null x) acc) (t (dumb-fn-count-1 t (cdr x) (dumb-fn-count-1 nil (car x) acc))))) ((or (variablep x) (fquotep x)) acc) (t (dumb-fn-count-1 t (fargs x) (1+ acc))))) (defun dumb-fn-count (x) ; Originally we had this upside-down call tree, where cons-count was a function ; that counts the number of conses in an object. ; cons-count ; smallest-common-subterms ; generalizable-terms-across-relations ; generalizable-terms ; generalizable-terms-across-literals1 ; generalizable-terms-across-literals ; generalizable-terms ; generalize-clause ; But the role of evgs disappears if we use dumb-occur instead of occur in our ; algorithm for finding common subterms, which seems anyhow like the right ; thing to do if the point is to generalize common subterms to variables. ; Evg-occur is called by occur but not by dumb-occur, and evg-occur is ; potentially expensive on galactic objects. So we no longer use cons-count to ; compute the smallest-common-subterms; we use fn-count-dumb. (dumb-fn-count-1 nil x 0)) (defun smallest-common-subterms (term1 term2 ens wrld ans) ; We accumulate onto ans and return the list of every subterm x of ; term1 that is also a subterm of term2, provided x is ``collectable'' ; and no subterm of x is collectable. A term is a collectable if it ; is an application of a collectable-fnp and is not an explicit value. ; Our aim is to collect the ``innermost'' or ``smallest'' collectable ; subterms. (mv-let (ans potential) (cond ((> (dumb-fn-count term1) (dumb-fn-count term2)) (smallest-common-subterms1 term2 term1 ens wrld ans)) (t (smallest-common-subterms1 term1 term2 ens wrld ans))) (declare (ignore potential)) ans)) (defun generalizing-relationp (term wrld) ; Term occurs as a literal of a clause. We want to know whether ; we should generalize common subterms occurring in its arguments. ; Right now the answer is geared to the special case that term is ; a binary relation -- or at least that only two of the arguments ; encourage generalizations. We return three results. The first ; is t or nil indicating whether the other two are important. ; The other two are the two terms we should explore for common ; subterms. ; For example, for (equal lhs rhs), (not (equal lhs rhs)), (< lhs ; rhs), and (not (< lhs rhs)), we return t, lhs, and rhs. We also ; generalize across any known equivalence relation, but this code has ; built into the assumption that all such relations have arity at ; least 2 and just returns the first two args. For (member x y), we ; return three nils. (mv-let (neg-flg atm) (strip-not term) (declare (ignore neg-flg)) (cond ((or (variablep atm) (fquotep atm) (flambda-applicationp atm)) (mv nil nil nil)) ((or (eq (ffn-symb atm) 'equal) (eq (ffn-symb atm) '<) (equivalence-relationp (ffn-symb atm) wrld)) (mv t (fargn atm 1) (fargn atm 2))) (t (mv nil nil nil))))) (defun generalizable-terms-across-relations (cl ens wrld ans) ; We scan clause cl for each literal that is a generalizing-relationp, ; e.g., (equal lhs rhs), and collect into ans all the smallest common ; subterms that occur in each lhs and rhs. We return the final ans. (cond ((null cl) ans) (t (mv-let (genp lhs rhs) (generalizing-relationp (car cl) wrld) (generalizable-terms-across-relations (cdr cl) ens wrld (if genp (smallest-common-subterms lhs rhs ens wrld ans) ans)))))) (defun generalizable-terms-across-literals1 (lit1 cl ens wrld ans) (cond ((null cl) ans) (t (generalizable-terms-across-literals1 lit1 (cdr cl) ens wrld (smallest-common-subterms lit1 (car cl) ens wrld ans))))) (defun generalizable-terms-across-literals (cl ens wrld ans) ; We consider each pair of literals, lit1 and lit2, in cl and ; collect into ans the smallest common subterms that occur in ; both lit1 and lit2. We return the final ans. (cond ((null cl) ans) (t (generalizable-terms-across-literals (cdr cl) ens wrld (generalizable-terms-across-literals1 (car cl) (cdr cl) ens wrld ans))))) (defun generalizable-terms (cl ens wrld) ; We return the list of all the subterms of cl that we will generalize. ; We look for common subterms across equalities and inequalities, and ; for common subterms between the literals of cl. (generalizable-terms-across-literals cl ens wrld (generalizable-terms-across-relations cl ens wrld nil))) (defun generalize-clause (cl hist pspv wrld state) ; A standard clause processor of the waterfall. ; We return 4 values. The first is a signal that is either 'hit, or 'miss. ; When the signal is 'miss, the other 3 values are irrelevant. When the signal ; is 'hit, the second result is the list of new clauses, the third is a ttree ; that will become that component of the history-entry for this generalization, ; and the fourth is an unmodified pspv. (We return the fourth thing to adhere ; to the convention used by all clause processors in the waterfall (q.v.).) ; The ttree we return is 'assumption-free. (declare (ignore state)) (cond ((not (assoc-eq 'being-proved-by-induction (access prove-spec-var pspv :pool))) (mv 'miss nil nil nil)) (t (let* ((ens (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :current-enabled-structure)) (terms (generalizable-terms cl ens wrld))) (cond ((null terms) (mv 'miss nil nil nil)) (t (mv-let (contradictionp type-alist ttree) (type-alist-clause cl nil ; The force-flg probably needs to be nil, to avoid an inappropriate call of ; generalize1. See the comment about a similar call of type-alist-clause in ; eliminate-destructors-clause1. nil ; force-flg nil ens wrld nil nil) (declare (ignore ttree)) (cond (contradictionp ; We compute the type-alist of the clause to allow us to generate nice variable ; names and to restrict the coming generalization. A contradiction will not ; arise if this clause survived simplification (which it has, unless :do-not ; hints specified that simplification was not to be used). However, we will ; return an accurate answer just to be rugged. We'll report that we couldn't ; do anything! That's really funny. We just proved our goal and we're saying ; we can't do anything. But if we made this fn sometimes return the empty set ; of clauses we'd want to fix the io handler for it and we'd have to respect ; the 'assumptions in the ttree and we don't. Do we? As usual, we ignore the ; ttree in this case, and hence we ignore it totally since it is known to be ; nil when contradictionp is nil. (mv 'miss nil nil nil)) (t (let ((gen-vars (generate-variable-lst terms (all-vars1-lst cl (owned-vars 'generalize-clause nil hist)) type-alist ens wrld))) (mv-let (generalized-cl restricted-vars var-to-runes-alist ttree) (generalize1 cl type-alist terms gen-vars ens wrld) (mv 'hit (list generalized-cl) (add-to-tag-tree! 'variables gen-vars (add-to-tag-tree! 'terms terms (add-to-tag-tree! 'restricted-vars restricted-vars (add-to-tag-tree! 'var-to-runes-alist var-to-runes-alist (add-to-tag-tree! 'ts-ttree ttree nil))))) pspv)))))))))))) (defun tilde-*-gen-phrase/alist1 (alist wrld) (cond ((null alist) nil) (t (cons (msg "~p0 by ~x1" (untranslate (caar alist) nil wrld) (cdar alist)) (tilde-*-gen-phrase/alist1 (cdr alist) wrld))))) (defun tilde-*-gen-phrase/alist (alist wrld) ; Alist is never nil (list* "" "~@*" "~@* and " "~@*, " (tilde-*-gen-phrase/alist1 alist wrld) nil)) (defun tilde-*-gen-phrase (alist restricted-vars var-to-runes-alist ttree wrld) (list* "" "~@*" "~@* and " "~@*, " (append (list (msg "~*0" (tilde-*-gen-phrase/alist alist wrld))) (cond (restricted-vars (let* ((runes (tagged-objects 'lemma ttree)) (primitive-type-reasoningp (member-equal *fake-rune-for-type-set* runes)) (symbols (strip-base-symbols (remove1-equal *fake-rune-for-type-set* runes)))) (cond ((member-eq nil symbols) (er hard 'tilde-*-gen-phrase "A fake rune other than ~ *fake-rune-for-type-set* was found in the ~ ts-ttree generated by generalize-clause. ~ The list of runes in the ttree is ~x0." runes)) ((null (cdr restricted-vars)) (list (msg "restricting the type of the new ~ variable ~&0 to be that of the term ~ it replaces~#1~[~/, as established ~ by primitive type reasoning~/, as ~ established by ~&2~/, as established ~ by primitive type reasoning and ~&2~]" restricted-vars (cond ((and symbols primitive-type-reasoningp) 3) (symbols 2) (primitive-type-reasoningp 1) (t 0)) symbols))) (t (list (msg "restricting the types of the new ~ variables ~&0 to be those of the ~ terms they replace~#1~[~/, as ~ established by primitive type ~ reasoning~/, as established by ~ ~&2~/, as established by primitive ~ type reasoning and ~&2~]" restricted-vars (cond ((and symbols primitive-type-reasoningp) 3) (symbols 2) (primitive-type-reasoningp 1) (t 0)) symbols)))))) (t nil)) (tilde-*-elim-phrase3 var-to-runes-alist)) nil)) (defun generalize-clause-msg1 (signal clauses ttree pspv state) ; The arguments to this function are the standard ones for an output ; function in the waterfall. See the discussion of the waterfall. (declare (ignore signal pspv clauses)) (fms "We generalize this conjecture, replacing ~*0. This produces~|" (list (cons #\0 (tilde-*-gen-phrase (pairlis$ (tagged-object 'terms ttree) (tagged-object 'variables ttree)) (tagged-object 'restricted-vars ttree) (tagged-object 'var-to-runes-alist ttree) (tagged-object 'ts-ttree ttree) (w state)))) (proofs-co state) state (term-evisc-tuple nil state))) ; The elimination of irrelevance is defined in the same file as induct ; because elim uses m&m. acl2-sources/other-releases.html0000664002132200015000000001027012222117210016351 0ustar kaufmannacl2 ACL2 Releases

ACL2 Releases

The current ACL2 release on the
ACL2 home page is Version 6.3 (October, 2013).

This page contains:

  • Incremental releases since Version 6.3 (if any)

  • Past releases


Incremental releases since Version 6.3:

None

Past releases:









acl2-sources/parallel.lisp0000664002132200015000000035041112222115527015243 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We thank David L. Rager for contributing an initial version of this file. (in-package "ACL2") ; Section: To Consider. The following might be good to address as time ; permits. ; Change the piece of work list to an array (perhaps result in a faster ; library because of less garbage. ; Make removing closures from the queue destructive, in particular with ; regard to early termination. ; Recycle locks, perhaps for example in wait-on-condition-variable-lockless. ; See this same comment in parallel-raw.lisp. ; Provide a way for the user to modify *core-count*, including inside the ; ACL2 loop. If we allow for changing *core-count*, then we need to think ; about allowing for changing variables that depend on it, e.g., ; *unassigned-and-active-work-count-limit* (perhaps by changing them to ; zero-ary functions). ; Modify the coefficient (currently 2) in the definition of ; *unassigned-and-active-work-count-limit*. Evaluate such modifications with ; testing, of course. ; End of Section "To Consider". (defdoc deflock ":Doc-Section ACL2::Parallel-programming define a wrapper macro that provides mutual exclusion in ACL2(p)~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel evaluation and proof; ~pl[parallelism]. ~bv[] Example Form: (deflock *my-lock*) General Form: (deflock *symbol*) ~ev[] where ~c[*symbol*] is a symbol whose first and last characters are both the character ~c[#\\*]. A call of this macro generates a definition of another macro, named ~c[with-], where ~c[] is the given symbol with the leading and trailing ~c[*] characters removed. This newly defined macro will guarantee mutually exclusive execution when called in the body of the raw Lisp definition of a function, as is typically the case for ~il[guard]-verified functions, for ~c[:]~ilc[program] mode functions, and for calls of macro ~ilc[top-level]. (~l[guard-evaluation-table] for details of how raw Lisp code might not be invoked when guard-checking (~pl[set-guard-checking]) has value ~c[:none] or ~c[:all].) To see how mutual exclusion is guaranteed, consider the raw Lisp code generated for the macro, ~c[with-], that is introduced by a call of ~c[deflock]. This code uses a lock (with the given ~c[*symbol*] as its name), which guarantees that for any two forms that are each in the scope of a call of ~c[with-], the forms do not execute concurrently. Note that a call of ~c[deflock] expands into the application of ~c[progn] to two events, as illustrated below. ~bv[] ACL2 !>:trans1 (deflock *my-cw-lock*) (PROGN (TABLE LOCK-TABLE '*MY-CW-LOCK* T) (DEFMACRO WITH-MY-CW-LOCK (&REST ARGS) (LIST* 'WITH-LOCK '*MY-CW-LOCK* ARGS))) ACL2 !> ~ev[] Thus, ~c[deflock] forms are legal embedded event forms (~pl[embedded-event-form]) for ~il[books] as well as ~ilc[encapsulate] and ~ilc[progn] ~il[events]. The following log shows a lock in action. Recall that locks work as expected in ~il[guard]-verified and ~c[:]~ilc[program] mode functions; they do not, however, work in ~c[:]~ilc[logic] mode functions that have not been guard-verified, as illustrated below. ~bv[] ACL2 !>(deflock *my-cw-lock*) [[.. output omitted ..]] WITH-MY-CW-LOCK ACL2 !>(defun foo (n) (declare (xargs :guard (natp n) :verify-guards nil)) (plet ((x1 (with-my-cw-lock (cw \"~~x0\" (make-list n)))) (x2 (with-my-cw-lock (cw \"~~x0\" (make-list n))))) (and (null x1) (null x2)))) [[.. output omitted ..]] FOO ACL2 !>(foo 20) (NIL NIL NIL NIL( NIL NIL NIL NIL NIL NILNIL NIL NILNIL NIL NILNIL NIL NILNIL NIL NIL NILNIL NIL NIL NIL NILNIL NIL NILNIL ) NIL NIL NIL NIL NIL NIL NIL NIL) T ACL2 !>(verify-guards foo) [[.. output omitted ..]] FOO ACL2 !>(foo 20) (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) T ACL2 !> ~ev[]~/~/") (defdoc compiling-acl2p ; Keep this documentation in sync with comments above the error in ; acl2-init.lisp about it being "illegal to build the parallel ; version", and also with the error message about supported Lisps in ; set-parallel-execution-fn. ":Doc-Section ACL2::Parallelism compiling ACL2(p)~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~l[parallelism-tutorial] for an introduction to parallel programming in ACL2. You can build an experimental version of ACL2 that supports parallel execution in the following host Common Lisp implementations: ~bq[] * CCL (OpenMCL) * Lispworks 6.0 * SBCL with threads (feature ~c[:sb-thread])~eq[] The command below will compile ACL2 to support parallel execution, including parallel execution during proofs. Any non-empty string may be used in place of ~c[t], and the value of ~c[LISP] (shown here as ~c[ccl]) is any Lisp executable on which one can build ACL2(p) (~pl[parallelism]). ~bv[] make ACL2_PAR=t LISP=ccl ~ev[] So for example, to make an executable image and also documentation (which will appear in subdirectories ~c[doc/EMACS] and ~c[doc/HTML]), using the Lisp executable ~c[ccl]: ~bv[] make large DOC ACL2_PAR=t LISP=ccl ~ev[]~/~/") (defdoc parallel ; Just in case someone types :doc parallel. ":Doc-Section Miscellaneous evaluating forms in parallel~/ ~l[parallelism].~/~/") (defdoc parallelism-build ":Doc-Section Miscellaneous building an ACL2 executable with parallel execution enabled~/ ~l[compiling-acl2p].~/~/") (defun set-parallel-execution-fn (val ctx state) (declare (xargs :guard (member-eq val '(t nil :bogus-parallelism-ok)))) (cond ((eq (f-get-global 'parallel-execution-enabled state) val) (pprogn (observation ctx "No change in enabling of parallel execution.") (value nil))) (t #-acl2-par (er soft ctx "Parallelism can only be enabled in CCL, threaded SBCL, or Lispworks. ~ ~ Additionally, the feature :ACL2-PAR must be set when compiling ~ ACL2 (for example, by using `make' with argument `ACL2_PAR=t'). ~ Either the current Lisp is neither CCL nor threaded SBCL nor ~ Lispworks, or this feature is missing. Consequently, parallelism ~ will remain disabled. Note that you can submit parallelism ~ primitives at the top level when parallel execution is disabled, ~ although they will not result in any parallel execution.~%") #+acl2-par (let ((observation-string (case val ((nil) "Disabling parallel execution. Parallelism primitives may ~ still be used, but during execution they will degrade to ~ their serial equivalents.") ((t) "Parallel execution is enabled, but parallelism primitives may ~ only be called within function definitions or macro top-level, ~ not at the top level of the ACL2 read-eval-print loop. See ~ :DOC parallelism-at-the-top-level.") (otherwise ; :bogus-parallelism-ok "Parallel execution is enabled. Parallelism primitives may be ~ called directly in the top-level loop, but without use of the ~ macro top-level, they will execute serially. See :DOC ~ parallelism-at-the-top-level.")))) (pprogn (f-put-global 'parallel-execution-enabled val state) (observation ctx observation-string) (value val)))))) (defmacro set-parallel-execution (value) ; Parallelism blemish: cause an error if the user tries to go into a state ; where waterfall-parallelism is enabled but parallel-execution is disabled. ":Doc-Section switches-parameters-and-modes for ACL2(p): enabling parallel execution for four parallelism primitives~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~l[parallelism-tutorial] for an introduction to parallel execution in ACL2. ~bv[] General Forms: (set-parallel-execution nil) ; default for images not built for parallelism (set-parallel-execution t) ; default for images built for parallelism (set-parallel-execution :bogus-parallelism-ok) ~ev[] ~/ ~c[Set-parallel-execution] takes an argument that specifies the enabling or disabling of ~il[parallel] execution for the primitives ~ilc[pand], ~ilc[por], ~ilc[plet], and ~ilc[pargs] (but not ~ilc[spec-mv-let], whose parallel execution remains enabled). However, without using ~ilc[top-level], calls of parallelism primitives made explicitly in the ACL2 top-level loop, as opposed to inside function bodies, will never cause parallel execution; ~pl[parallelism-at-the-top-level]. Parallel execution is determined by the value of the argument to ~c[set-parallel-execution], as follows. Value ~c[t]:~nl[] All parallelism primitives used in bodies of function definitions are given the opportunity to execute in parallel. However, the use of parallelism primitives directly in the ACL2 top-level loop causes an error. Value ~c[:bogus-parallelism-ok]:~nl[] Parallel execution is enabled, as for value ~c[t]. However, the use of parallelism primitives directly in the ACL2 top-level loop does not cause an error, but rather, simply results in serial execution for these primitives. Value ~c[nil]:~nl[] All parallelism primitives degrade to their serial equivalents, including their calls made directly in the ACL2 top-level loop. Thus, uses of parallelism primitives do not in themselves cause errors.~/ :cited-by parallel-programming" (declare (xargs :guard (member-equal value '(t 't nil 'nil :bogus-parallelism-ok ':bogus-parallelism-ok)))) `(let ((val ,value) (ctx 'set-parallel-execution)) (set-parallel-execution-fn (cond ((consp val) (cadr val)) (t val)) ctx state))) (defdoc parallel-execution ":Doc-Section Parallel-programming for ACL2(p): configure parallel execution~/ ~l[set-parallel-execution] for how to configure parallel execution for calls of ~ilc[plet], ~ilc[pargs], ~ilc[pand], ~ilc[por] (but not ~ilc[spec-mv-let]).~/~/") (defun waterfall-printing-value-for-parallelism-value (value) ; Warning: We assume the the value of this function on input nil is :full. If ; that changes, then we will need to replace the definition of ; waterfall-printing-full in front of the defproxy event below for ; waterfall-printing, as well as the corresponding defattach event in ; boot-strap-pass-2.lisp. (declare (xargs :guard (member-eq value *waterfall-parallelism-values*))) (cond ((eq value nil) :full) ((eq value :full) :very-limited) ((eq value :top-level) :very-limited) ((eq value :resource-based) :very-limited) ((eq value :resource-and-timing-based) :very-limited) (t (assert$ (eq value :pseudo-parallel) :very-limited)))) ; Parallelism wart: figure out if :bdd hints are supported. Given the call of ; error-in-parallelism-mode@par in waterfall-step, it seems that they might not ; be; yet, regressions may have passed with them. One possible outcome: If ; tests fail for contributed book directory books/bdd/, you might just modify ; translate-bdd-hint to cause a nice error if watefall parallelism is enabled, ; and also mention that (once again) in :doc ; unsupported-waterfall-parallelism-features. Note that bdd-clause might be ; the function that actually performs the bdd hint, and that bdd-clause doesn't ; return state. So, aside from the place in waterfall-step, bdd hints might be ; fine. (defdoc unsupported-waterfall-parallelism-features ; For a discussion of the wormhole issue referenced in the :doc string below, ; see waterfall-print-clause-id@par. ; Parallelism no-fix: the problem below related to interrupts is potentially ; somewhat serious, but probably quite rare. Moreover, it seems potentially ; quite difficult to fix, as it would likely involve multi-threaded Lisp issues ; as well as acl2-unwind-protect issues. ":Doc-Section ACL2::Parallel-proof proof features not supported with waterfall-parallelism enabled~/ For a general introduction to ACL2(p), an experimental extension of ACL2 that supports parallel execution and proof, ~pl[parallelism]. Please note that although this extension is usable and, we hope, robust in its behavior, there are still known issues to address beyond those listed explicitly below. While we expect ACL2(p) to perform correctly, it may never have the same level of attention to correctness as is given to ACL2; ~pl[parallelism], specifically the ``IMPORTANT NOTE'' there. Below we list proof features of ACL2 that are not yet supported when parallel execution is enabled for the primary ACL2 proof process, generally known as ``the waterfall'', typically by calling ~ilc[set-waterfall-parallelism]. Please note that this topic is limited to the case that such waterfall parallelism is enabled. We believe that all ACL2 proof procedures are supported when waterfall parallelism is disabled, even in executables that support parallelism (~pl[compiling-acl2p]). Without a trust tag (~pl[defttag]): We support ~il[clause-processor]s, ~il[computed-hints], and ~il[custom-keyword-hints] that do not modify ~il[state], but we do not permit ~il[override-hints], regardless of whether they modify state. With a trust tag, the user can use ~il[clause-processor]s that modify state and can also use ~il[override-hints] (~pl[set-waterfall-parallelism-hacks-enabled] for a convenient mechanism for adding a trust tag). ~l[error-triples-and-parallelism] for a discussion of how to avoid modifying state in such situations. Regardless of whether a trust tag is active: We do not support checkers of ~il[custom-keyword-hints] to be anything but the default checker. GNU Make versions 3.81 and 3.82 formerly caused a lot of problems (version 3.80 somewhat less so), at least on Linux, when certifying books with ACL2 built on a host Lisp of CCL using `make'. CCL was updated around March 23, 2011 to fix this problem, so if you get segfaults (for example) with CCL, try updating your CCL installation. Book certification should generally work but may present some issues, including the following. ~bq[] o The standard `make'-based process for book certification will not use ~il[waterfall-parallelism], which is disabled by default (even when ~il[compiling-acl2p] by using the ~c[ACL2_PAR] flag). ~l[books-certification] and ~pl[books-certification-classic], which explain that ~il[acl2-customization] files are ignored during that process unless specified explicitly on the command line or in the environment. o A book certified with ACL2(p) might subsequently cause an error when included with ACL2. As of this writing, however, we have only seen this for a book in which ~ilc[deftheory-static] is used. o In general, ACL2(p) is primarily intended to support more rapid interactive development. While we are unaware of an unsoundness likely to affect an ACL2(p) user, we suggest using ACL2 for final book certification, rather than ACL2(p), to lower the risk of unsound book certification. ~eq[] Proof output can contain repeated printing of the same subgoal name. ~il[Gag-mode] isn't officially supported, although it has proved helpful to use ACL2(p) in conjunction with ~c[(set-gag-mode t)] (because this setting suppresses the output that occurs outside the waterfall). This being said, ACL2(p) also prints key checkpoints (for example ~pl[introduction-to-key-checkpoints]), but with a notion of ``key checkpoint'' that does not take into account whether the goal is later proved by induction. ~l[acl2p-key-checkpoints] for further explanation of these key checkpoints. Note that ~ilc[pso] is also not supported. The ~c[:]~ilc[brr] utility is not supported. The ~ilc[accumulated-persistence] utility is not supported. Tracking for ~il[forward-chaining-reports] is not supported (~pl[set-fc-criteria]). Time limits (~pl[with-prover-time-limit]) aren't supported. The timing information printed at the end of a proof attempt, which is intended to represent cpu time (not wall-clock time), may be somewhat inaccurate when ~il[waterfall-parallelism] is non-~c[nil]. Consider using ~ilc[time$] to obtain timing information. The use of ~ilc[wormhole]s is not recommended, as there may be race conditions. Output specific to ~c[:OR] ~il[hints] is disabled. Proof trees are likely not to work as originally designed. The use of ~ilc[set-inhibit-output-lst] may not fully inhibit proof output. Reporting of ~il[splitter] rules is currently unsupported when waterfall-parallelism is on. Interrupting a proof attempt is not yet properly supported. At a minimum, interrupts are trickier with waterfall parallelism enabled. For one, the user typically needs to issue the interrupt twice before the proof attempt is actually interrupted. Additionally, on rare occasions the theorem is registered as proved, even though the prover did not finish the proof. If this occurs, issue a ~c[:u] (~pl[ubt]) and you will likely be at a stable state. Also with regards to interrupting a proof attempt, sometimes the user may need to issue a ~c[:q] and ~c[lp] to reset properly the parallelism implementation to a stable state. The primary symptom that the user is experiencing this issue is that threads will continue to compute in the background, even though there should be no proof attempt in progress. The user can observe this symptom by examining the CPU utilization of their ACL2 process, for example on Linux/Unix with the shell process ~c[top]. Lisp usage greater than a few percent is indicative of this problem. Because of how ACL2 ~il[arrays] are designed, the user may find that, in practice, ACL2 arrays work (but perhaps with some ~il[slow-array-warning] messages). However, we are aware of race conditions that can cause problems. Instead of dynamically monitoring rewrites, ~il[dmr] instead dynamically outputs information helpful for debugging the performance of proof parallelism. The instructions concerning how to see this debugging information are the same as the instructions for enabling ~il[dmr] mode. If you are working with LispWorks 6.0 or 6.0.1, then you may see messages about misaligned conses. The state of the system may be corrupted after such a message has been printed. This LispWorks bug is fixed in LispWorks 6.1. The waterfall parallelism mode ~c[:resource-and-timing-based] is not fully supported when the host Lisp is other than CCL. It may work, but we have not attempted to address a potential race condition. Proof output for splitter rules (~pl[splitter]) is currently unsupported when waterfall-parallelism is enabled. (Comment for ACL2(h) users; ~pl[hons-and-memoization].) Memoization may not work as intended when executing in parallel (including the waterfall). In an effort to be helpful to the user, the functions automatically memoized by ACL2(h) are unmemoized when setting waterfall parallelism to anything but ~c[nil]. Those exact functions are again memoized once waterfall parallelism is disabled. Additionally, any functions memoized within the ACL2 loop (by a call of ~ilc[memoize]) are also unmemoized when enabling waterfall parallelism and once again memoized when disabling waterfall parallelism. This is implemented by returning the memoization state to what it was before enabling waterfall parallelism. As such, the user should be aware that any changes made to the memoization state while waterfall parallelism is enabled will be lost once waterfall parallelism is disabled.~/~/") (defdoc unsupported-parallelism-features ":Doc-Section ACL2::Parallelism ACL2 features not supported in ACL2(p)~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~l[parallelism-tutorial] for an introduction to parallel programming in ACL2. For proof features of ACL2 that are not yet supported when parallel execution is enabled for the primary ACL2 proof process, generally known as ``the waterfall'', ~pl[unsupported-waterfall-parallelism-features]. Please note that this topic discusses ACL2 features that are disabled when using ACL2(p) (~pl[compiling-acl2p]). These features are disabled regardless of whether waterfall parallelism is enabled. Calls of ~ilc[observation-cw] simply convert to calls of ~ilc[cw], so suppressing ~ilc[observation]s (~pl[set-inhibit-output-lst]) will not suppress these messages. Memoization is not supported when executing in parallel. ~l[Unsupported-waterfall-parallelism-features] for memoization details related to waterfall parallelism. Since, as of April 2012, garbage collection is inherently sequential, ACL2(p) minimizes the use of garbage collection by setting a high garbage collection threshold. As a result, ACL2(p) is not expected to perform well on machines with less memory than this threshold (1 gigabyte, as of April 2012). In CCL, the underlying parallel execution engine is tuned for the number of CPU cores (or hardware threads) actually available in the machine. SBCL and LispWorks are tuned for a machine with 16 CPU cores. CCL is considered to be the ``flagship Lisp'' for parallel execution in ACL2. The SBCL and LispWorks implementations are thought to be generally stable. However, due to their relatively less common use, the SBCL and LispWorks implementations are likely less robust than the CCL implementation. The ~ilc[time-tracker] utility is a no-op for ACL2(p).~/~/") (defdoc waterfall-printing ":Doc-Section Parallel-proof for ACL2(p): configuring the printing within the parallelized waterfall~/ ~l[set-waterfall-printing].~/~/") (defdoc waterfall-parallelism ":Doc-Section Parallel-proof for ACL2(p): configuring the parallel execution of the waterfall~/ ~l[set-waterfall-parallelism].~/~/") (defun print-set-waterfall-parallelism-notice (val print-val state) ; Warning: This function should only be called inside the ACL2 loop, because of ; the calls of observation-cw. (declare (xargs :guard (and (member-eq val *waterfall-parallelism-values*) (keywordp print-val)))) (let ((str (case val ((nil) "Disabling parallel execution of the waterfall.") (:full "Parallelizing the proof of every subgoal.") (:top-level "Parallelizing the proof of top-level subgoals only.") (:pseudo-parallel "Running the version of the waterfall prepared for parallel ~ execution (stateless). However, we will execute this version of ~ the waterfall serially.") (:resource-and-timing-based "Parallelizing the proof of every subgoal that was determined to ~ take a non-trivial amount of time in a previous proof attempt.") (otherwise ; :resource-based "Parallelizing the proof of every subgoal, as long as CPU core ~ resources are available.")))) ; Keep the following ending "~%" in sync with set-waterfall-parallelism. (observation nil "~@0 Setting waterfall-parallelism to ~s1. Setting ~ waterfall-printing to ~s2 (see :DOC ~ set-waterfall-printing).~%" str (symbol-name val) (symbol-name print-val)))) (defun check-for-no-override-hints (ctx state) ; Although this macro is intended for #+acl2-par, we need it unconditionally ; because it is called in set-waterfall-parallelism, which might be called ; outside ACL2(p); see the note about a call of observation in ; set-waterfall-parallelism-fn. (let ((wrld (w state))) (cond ((and (not (cdr (assoc-eq 'hacks-enabled (table-alist 'waterfall-parallelism-table wrld)))) (cdr (assoc-eq :override (table-alist 'default-hints-table wrld)))) (er soft ctx ; Override hints must be removed because set-waterfall-parallelism performs a ; defattach, which spawns some proof effort. If there are override-hints ; available for use during this proof, apply-override-hints will see them and ; attempt to use them. Since override-hints are not permitted without enabling ; waterfall-parallelism-hacks, in this case, we must cause an error. "Before changing the status of waterfall-parallelism, either (1) ~ override hints must be removed from the default-hints-table or (2) ~ waterfall-parallelism hacks must be enabled. (1) can be achieved ~ by calling ~x0. (2) can be achived by calling ~x1." '(set-override-hints nil) '(set-waterfall-parallelism-hacks-enabled t))) (t (value nil))))) (defun set-waterfall-parallelism-fn (val ctx state) (prog2$ (and val ; We avoid a possible hard error, e.g. from (mini-proveall), when parallelism ; and accumulated-persistence are both turned on. A corresponding bit of code ; is in accumulated-persistence. We do similarly, just to be safe, for ; forward-chaining-reports; see also set-fc-criteria-fn. ; Warning: Keep the following two wormhole-eval calls in sync with the ; definitions of accumulated-persistence and set-fc-criteria-fn. (prog2$ (wormhole-eval 'accumulated-persistence '(lambda (whs) (set-wormhole-data whs nil)) nil) (wormhole-eval 'fc-wormhole '(lambda (whs) (set-wormhole-data whs (put-assoc-eq :CRITERIA nil (wormhole-data whs)))) nil))) (cond ((eq val (f-get-global 'waterfall-parallelism state)) (pprogn (observation ctx "Ignoring call to set-waterfall-parallelism ~ since the new value is the same as the ~ current value.~%~%") (value :ignored))) ((member-eq val *waterfall-parallelism-values*) (let ((val (if (eq val t) ; t is a alias for :resource-based :resource-based val))) #+acl2-par (cond ((null (f-get-global 'parallel-execution-enabled state)) (er soft ctx "Parallel execution must be enabled before enabling ~ waterfall parallelism. See :DOC set-parallel-execution")) (t (pprogn #+(and hons (not acl2-loop-only)) (progn (cond ((null val) (acl2h-init-memoizations)) (t (acl2h-init-unmemoizations))) state) (f-put-global 'waterfall-parallelism val state) (progn$ #-acl2-loop-only (funcall ; avoid undefined function warning 'initialize-dmr-interval-used) (value val))))) #-acl2-par ; Once upon a time we issued an error here instead of an observation. In ; response to feedback from Dave Greve, we have changed it to an observation so ; that users can call set-waterfall-parallelism inside books (presumably via ; make-event) without causing their certification to stop when using #-acl2-par ; builds of ACL2. (pprogn (observation ctx ; We make this an observation instead of a warning, because it's probably ; pretty obvious to the user whether they're using an image that was built with ; the acl2-par feature. "Parallelism can only be enabled in CCL, threaded ~ SBCL, or Lispworks. Additionally, the feature ~ :ACL2-PAR must be set when compiling ACL2 (for ~ example, by using `make' with argument ~ `ACL2_PAR=t'). ~ Either the current Lisp is ~ neither CCL nor threaded SBCL nor Lispworks, or ~ this feature is missing. Consequently, this ~ attempt to set waterfall-parallelism to ~x0 will ~ be ignored.~%~%" val) (value :ignored)))) (t (er soft ctx "Illegal value for set-waterfall-parallelism: ~x0. The legal ~ values are ~&1." val *waterfall-parallelism-values*))))) ; Parallelism blemish: make a macro via deflast called ; with-waterfall-parallelism that enables waterfall parallelism for a given ; form, in particular an event form like calls of defun and defthm. It's low ; priority, since it can easily be added as a book later -- though maybe it ; would be nice to have this as an event constructor, like with-output. But ; while doing proofs with ACL2(hp), Rager would have found this convenient. (defmacro set-waterfall-parallelism1 (val) `(let* ((val ,val) (ctx 'set-waterfall-parallelism)) (er-progn (check-for-no-override-hints ctx state) (er-let* ((val (set-waterfall-parallelism-fn val ctx state))) (cond ((eq val :ignored) (value val)) (t (let ((print-val (waterfall-printing-value-for-parallelism-value val))) (pprogn (print-set-waterfall-parallelism-notice val print-val state) (er-progn (set-waterfall-printing-fn print-val ctx state) (value (list val print-val))))))))))) (table saved-memoize-table nil nil :guard ; It is tempting to install a table guard of (memoize-table-chk key val world). ; However, that won't work, for example because it will prohibit adding an ; entry to this table for a function that is currently memoized -- an act that ; is the point of this table! So instead we rely solely on the checks done ; when putting entries in memoize-table. t) (defmacro save-memo-table () '(with-output :off (summary event) (table saved-memoize-table nil (table-alist 'memoize-table world) :clear))) (defun clear-memo-table-events (alist acc) (declare (xargs :guard (true-list-listp alist))) (cond ((endp alist) acc) (t (clear-memo-table-events (cdr alist) (cons `(table memoize-table ',(caar alist) nil) acc))))) (defmacro clear-memo-table () `(with-output :off (summary event) (make-event (let ((alist (table-alist 'memoize-table (w state)))) (cons 'progn (clear-memo-table-events alist nil)))))) (defmacro save-and-clear-memoization-settings () ":Doc-Section Events save and remove the current memoization settings~/ For background on memoization, ~pl[memoize]. ~bv[] General Form: (save-and-clear-memoization-settings) ~ev[] Calls of this macro achieve two changes. The first copies the current memoization settings into an ACL2 ~il[table], and the second unmemoizes all functions that were memoized by calls of ~ilc[memoize]. Also ~pl[restore-memoization-settings].~/~/ :cite hons-and-memoization :cited-by hons-and-memoization" '(with-output :off (summary event) (progn (save-memo-table) (clear-memo-table)))) (defun set-memo-table-events (alist acc) (declare (xargs :guard (true-list-listp alist))) (cond ((endp alist) acc) (t (set-memo-table-events (cdr alist) (cons `(table memoize-table ',(caar alist) ',(cdar alist)) acc))))) (defmacro restore-memoization-settings () ":Doc-Section Events restore the saved memoization settings~/ For background on memoization, ~pl[memoize]. ~bv[] General Form: (restore-memoization-settings) ~ev[] Calls of this macro restore the memoization settings saved by ~ilc[save-and-clear-memoization-settings].~/~/ :cite hons-and-memoization :cited-by hons-and-memoization" `(with-output :off (summary event) (make-event (let ((alist (table-alist 'saved-memoize-table (w state)))) (cons 'progn (set-memo-table-events alist nil)))))) (defmacro set-waterfall-parallelism (val) ":Doc-Section switches-parameters-and-modes for ACL2(p): configuring the parallel execution of the waterfall~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] General Forms: (set-waterfall-parallelism nil) ; never parallelize (serial execution) (set-waterfall-parallelism :full) ; always parallelize (set-waterfall-parallelism :top-level) ; parallelize top-level subgoals (set-waterfall-parallelism ; parallelize if sufficient resources :resource-based) ; (recommended setting) (set-waterfall-parallelism t) ; alias for :resource-based (set-waterfall-parallelism ; parallelize if sufficient resources :resource-and-timing-based ; and suggested by prior attempts (set-waterfall-parallelism ; never parallelize but use parallel :pseudo-parallel) ; code base (a debug mode) ~ev[] ~/ ~c[Set-waterfall-parallelism] evaluates its argument, which specifies the enabling or disabling of the ~il[parallel] execution of ACL2's main proof process, the waterfall. It also sets ~il[state] global ~c[waterfall-printing] to an appropriate value. ~l[set-waterfall-printing]. Note that not all ACL2 features are supported when waterfall-parallelism is set to non-~c[nil] (~pl[unsupported-waterfall-parallelism-features]). A value of ~c[t] is treated the same as a value of ~c[:resource-based] and is provided for user convenience. ~c[:Resource-based] waterfall parallelism typically achieves the best performance in ACL2(p), while maintaining system stability, so ~c[:resource-based] (or equivalently, ~c[t]) is the recommended value. A value of ~c[nil] indicates that ACL2(p) should never prove subgoals in parallel. A value of ~c[:full] indicates that ACL2(p) should always prove independent subgoals in parallel. A value of ~c[:top-level] indicates that ACL2(p) should prove each of the top-level subgoals in parallel but otherwise prove subgoals in a serial manner. This mode is useful when the user knows that there are enough top-level subgoals, many of which take a non-trivial amount of time to be proved, such that proving them in parallel will result in a useful reduction in overall proof time. A value of ~c[:resource-based] (or equivalently, ~c[t]) indicates that ACL2(p) should use its built-in heuristics to determine whether CPU core resources are available for parallel execution. Note that ACL2(p) does not hook into the operating system to determine the workload on the machine. ACL2(p) works off the assumption that it is the only process using significant CPU resources, and it optimizes the amount of parallelism based on the number of CPU cores in the system. (Note that ACL2(p) knows how to obtain the number of CPU cores from the operating system in CCL, but that, in SBCL and in Lispworks, a constant is used instead). During the first proof attempt of a given conjecture, a value of ~c[:resource-and-timing-based] results in the same behavior as with ~c[:resource-based]. However, on subsequent proof attempts, the time it took to prove each subgoal will be considered when deciding whether to parallelize execution. If a particular theorem's proof is already achieving satisfactory speedup via ~c[:resource-based] parallelism, there is no reason to try this setting. However, if the user wishes to experiment, the ~c[:resource-and-timing-based] setting may improve performance. Note that since the initial run does not have the subgoal proof times available, this mode will never be better than the ~c[:resource-based] setting for non-interactive theorem proving. A value of ~c[:pseudo-parallel] results in using the parallel waterfall code, but with serial execution. This setting is useful for debugging the code base that supports parallel execution of the waterfall. For example, you may wish to use this mode if you are an ``ACL2 Hacker'' who would like to see comprehensible output from tracing (~pl[trace$]) the ~c[@par] versions of the waterfall functions. The following remark pertains to those using the `HONS' experimental extension of ACL2 (~pl[hons-and-memoization]; in particular, ~pl[memoize]). Since memoization is not supported when waterfall parallelism is enabled (~pl[unsupported-waterfall-parallelism-features]), then when ~c[set-waterfall-parallelism] is called with a non-~c[nil] value, all memoized functions are unmemoized. When ~c[set-waterfall-parallelism] is again called with a ~c[nil] value, those memoization settings are restored. ~c[Set-waterfall-parallelism] is an embedded event form. However, a call of this macro will not affect waterfall-parallelism when including a certified book that contains that call. For such an effect, you may use the following ~ilc[make-event] form. ~bv[] (make-event (er-progn (set-waterfall-parallelism :full) (value '(value-triple nil))) :check-expansion t) ~ev[] To enable waterfall parallelism for book certification using ACL2(p), ~pl[waterfall-parallelism-for-book-certification].~/ :cited-by parallel-proof" `(with-output :off (summary event) (make-event (let ((old-val (f-get-global 'waterfall-parallelism state))) (declare (ignorable old-val)) (er-let* ((new-val (set-waterfall-parallelism1 ,val))) (cond ((eq new-val :IGNORED) (value '(value-triple :IGNORED))) #+hons ((and (null old-val) (car new-val)) (pprogn (observation 'set-waterfall-parallelism ; Here and below, we start with a "~%" so that the messages printed when ; enabling and disabling waterfall parallelism have the same amount of space ; between the messages and the return value. This "~%" is paired with the one ; at the end of the observation in print-set-waterfall-parallelism-notice. "~%Unmemoizing the functions that are memoized by default as part ~ of ACL2(h) and all that have been memoized by calling memoize ~ (see :DOC unsupported-waterfall-parallelism-features).~%") ; The functions that are memoized by default as part of hons are ; memoized/unmemoized inside set-waterfall-parallelism-fn. We do it there, ; instead of as part of this macro, because those memoizations only occur in ; raw Lisp and have nothing to do with table events. Since this macro is an ; ACL2-loop macro, it does not have access to acl2h-init-memoizations and ; acl2h-init-unmemoizations. (value '(save-and-clear-memoization-settings)))) #+hons ((and old-val (null (car new-val))) (pprogn (observation 'set-waterfall-parallelism "~%Memoizing the functions that are memoized by default as part ~ of ACL2(h) and that were memoized before disabling ~ waterfall-parallelism (see :DOC ~ unsupported-waterfall-parallelism-features).~%") (value'(restore-memoization-settings)))) (t (value '(value-triple nil))))))))) (defdoc waterfall-parallelism-for-book-certification ":Doc-Section Parallelism for ACL2(p): using waterfall parallelism during book certification~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. There are books whose certification can be sped up significantly by using waterfall parallelism. (~l[parallelism], including the caveat in its \"IMPORTANT NOTE\".) One such example in the ACL2 community books is ~c[models/jvm/m5/apprentice.lisp], which is typically excluded from regressions because of how long it takes to certify. In order to use waterfall parallelism during certification of a book ~c[.lisp] using `make' (~pl[books-certification] and ~pl[books-certification-classic]), we recommend creating a file ~c[.acl2] that includes the following forms. ~bv[] #+acl2-par (set-waterfall-parallelism t) (certify-book ? t) ; other arguments may be preferable ~ev[] Note that there are books that will not certify when waterfall-parallelism is enabled. See file ~c[acl2-customization-files/README] for more information, including how to certify essentially all books using waterfall parallelism.~/~/") (defun set-waterfall-printing-fn (val ctx state) (cond ((member-eq val *waterfall-printing-values*) #+acl2-par (pprogn (f-put-global 'waterfall-printing val state) (value val)) #-acl2-par ; See note about making this an observation instead of an error inside ; set-waterfall-parallelism. (pprogn (observation ctx "Customizing waterfall printing only makes ~ sense in the #+acl2-par builds of ACL2. ~ Consequently, this attempt to set ~ waterfall-printing to ~x0 will be ignored.~%~%" val) (value :invisible))) (t (er soft ctx "Illegal value for set-waterfall-printing: ~x0. The legal ~ values are ~&1." val *waterfall-printing-values*)))) (defmacro set-waterfall-printing (val) ":Doc-Section switches-parameters-and-modes for ACL2(p): configuring the printing that occurs within the parallelized waterfall~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] General Forms: (set-waterfall-printing :full) ; print everything (set-waterfall-printing :limited) ; print a subset that's thought to be useful (set-waterfall-printing :very-limited) ; print an even smaller subset ~ev[] ~/ ~c[Set-waterfall-printing] evaluates its argument, which indicates how much printing should occur when executing ACL2 with the parallelized version of the waterfall. It only affects the printing that occurs when parallelism mode is enabled for the waterfall (~pl[set-waterfall-parallelism]). A value of ~c[:full] is intended to print the same output as in serial mode. This output will be interleaved unless the waterfall-parallelism mode is one of ~c[nil] or ~c[:pseudo-parallel]. A value of ~c[:limited] omits most of the output that occurs in the serial version of the waterfall. Instead, the proof attempt prints key checkpoints (~pl[acl2p-key-checkpoints]). The value of ~c[:limited] also prints messages that indicate which subgoal is currently being proved, along with the wall-clock time elapsed since the theorem began its proof; and if state global ~c['waterfall-printing-when-finished] has a non-~c[nil] value, then such a message will also be printed at the completion of each subgoal. The function ~c[print-clause-id-okp] may receive an attachment to limit such printing; ~pl[set-print-clause-ids]. Naturally, these subgoal numbers can appear out of order, because the subgoals can be proved in parallel. A value of ~c[:very-limited] is treated the same as ~c[:limited], except that instead of printing subgoal numbers, the proof attempt prints a period (`~c[.]') each time it starts a new subgoal. Note that this form cannot be used at the top level of a book, or of a ~ilc[progn] or ~ilc[encapsulate] event. Here is a workaround for use in such contexts; of course, you may replace ~c[:very-limited] with any other legal argument for ~c[set-waterfall-printing]. ~bv[] (make-event (er-progn (set-waterfall-printing :very-limited) (value '(value-triple nil)))) ~ev[] (For more about event contexts and the use of ~c[make-event], ~pl[make-event], in particular the section ``Restriction to Event Contexts.'') The following form has the effect described above, except that it will affect waterfall-printing even when including a certified book that contains it. ~bv[] (make-event (er-progn (set-waterfall-printing :very-limited) (value '(value-triple nil))) :check-expansion t) ~ev[] Note that ~c[set-waterfall-printing] is automatically called by ~ilc[set-waterfall-parallelism]. To enable the printing of information when a subgoal is finished, assign a non-~c[nil] value to global ~c[waterfall-printing-when-finished]. This can be accomplished by entering the following at the top level: ~bv[] (f-put-global 'waterfall-printing-when-finished t state) ~ev[] ~/ :cited-by parallel-proof" `(set-waterfall-printing-fn ,val 'set-waterfall-printing state)) (defun set-waterfall-parallelism-hacks-enabled-guard (wrld) (or (ttag wrld) (er hard nil "Using waterfall parallelism hacks requires an active trust-tag. ~ Consider using (set-waterfall-parallelism-hacks-enabled! t). See ~ :DOC set-waterfall-parallelism-hacks-enabled for~ more~ ~ information."))) (table waterfall-parallelism-table nil nil :guard (set-waterfall-parallelism-hacks-enabled-guard world)) (defmacro set-waterfall-parallelism-hacks-enabled (val) ; One might consider using a state global to implement ; set-waterfall-parallelism-hacks-enabled. But as David Rager points out, this ; macro can change whether or not a proof completes. So, we want this macro ; tied into the undoing mechanism; hence we use a table event. ":Doc-Section switches-parameters-and-modes for ACL2(p): enable waterfall-parallelism hacks~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] General Forms: (set-waterfall-parallelism-hacks-enabled t) (set-waterfall-parallelism-hacks-enabled nil) ~ev[]~/ Some features (e.g., ~il[override-hints] and ~il[clause-processor]s) of serial ACL2 are by default not available in ACL2(p) with waterfall parallelism enabled, because they offer a mechanism to modify ~il[state] that is unsound. To allow or (once again) disallow the use the these features in ACL2(p), call ~c[set-waterfall-parallelism-hacks-enabled] with argument ~c[t] or ~c[nil], respectively. ~c[Set-waterfall-parallelism-hacks-enabled] requires the use of a trust tag (~pl[defttag]). One can call ~ilc[set-waterfall-parallelism-hacks-enabled!] instead, which will automatically install a trust tag named ~c[:waterfall-parallelism-hacks]. ~l[error-triples-and-parallelism] for further related discussion.~/ :cited-by parallel-proof" (declare (xargs :guard (or (equal val t) (null val)))) `(table waterfall-parallelism-table 'hacks-enabled ,val)) (defmacro set-waterfall-parallelism-hacks-enabled! (val) ":Doc-Section switches-parameters-and-modes for ACL2(p): enabling waterfall parallelism hacks~/ ~l[set-waterfall-parallelism-hacks-enabled].~/~/ :cited-by parallel-proof" `(encapsulate () ; Parallelism blemish: the following installation of ttag ; :waterfall-parallelism-hacks should probably be conditionalized upon val ; being equal to t. Furthermore, perhaps the installation should also be ; conditionalized upon the non-existence of a prior ttag. (defttag :waterfall-parallelism-hacks) (set-waterfall-parallelism-hacks-enabled ,val))) (defdoc parallelism-at-the-top-level ":Doc-Section Parallel-programming parallel execution in the ACL2 top-level loop~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. Calls of parallelism primitives made explicitly in the ACL2 top-level loop, as opposed to inside function bodies, will never cause parallel execution. Such calls will either execute with serial execution or will cause an error; ~pl[set-parallel-execution]. For a way around this restriction, ~pl[top-level].~/ Consider for example the following call of ~ilc[pargs] in the ACL2 top-level loop. Instead of executing ~c[pargs], ACL2 macroexpands away this call, leaving us with serial execution of the arguments to the ~ilc[cons] call, or else causes an error (~pl[set-parallel-execution]). If there is no error, then ~bv[] (pargs (cons (expensive-fn-1 4) (expensive-fn-2 5))) ~ev[] expands into: ~bv[] (cons (expensive-fn-1 4) (expensive-fn-2 5)) ~ev[] One trivial way to enable parallel execution of a form is to surround it with a call to macro ~il[top-level]. Consider the following example. ~bv[] (top-level (pargs (cons (expensive-fn-1 4) (expensive-fn-2 5)))) ~ev[] Then in an executable image that supports parallel execution ~-[] ~pl[compiling-acl2p] for instructions on how to build such an executable ~-[] ~c[(expensive-fn-1 4)] and ~c[(expensive-fn-2 5)] can evaluate in parallel. A second way to enable parallel execution of a form is to place it inside a function body. For example, consider the following definition. ~bv[] (defun foo (x y) (pargs (cons (expensive-fn-1 x) (expensive-fn-2 y)))) ~ev[] Then in an executable image that supports parallel execution, submission of the form ~c[(foo 4 5)] can cause parallel execution of ~c[(expensive-fn-1 4)] and ~c[(expensive-fn-2 5)]. Note that ~il[guard]s need not be verified in order to obtain ~il[parallel] execution. The only restrictions on parallel execution are to use an executable supporting it, to avoid calling parallelism primitives directly in the top-level loop, to have sufficient resources (especially, threads) available, and to avoid explicitly disabling parallel execution (~pl[set-parallel-execution]).~/") (defdoc parallelism-tutorial ":Doc-Section Parallel-programming a tutorial on how to use the parallelism library.~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. In this topic we introduce the ACL2 parallelism primitives using the example of a doubly-recursive Fibonacci function, whose basic definition is as follows. ~l[parallelism] for a very high-level summary of the parallelism capability described here, and ~pl[compiling-acl2p] for how to build an executable image that supports parallel execution. Here, we assume that such an executable is being used.~/ ~b[Serial Fibonacci] ~bv[] (defun fib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (+ (fib (- x 1)) (fib (- x 2)))))) ~ev[] ~b[Introducing] ~ilc[Pargs] A simple way to introduce parallelism into this function definition is to wrap the addition expression with a call of ~ilc[pargs], and the arguments to the addition will be computed in parallel whenever resources are available. As a result, we end up with a very similar and thus intuitive function definition. Note that we replaced ~ilc[+] by ~ilc[binary-+], since ~ilc[pargs] expects a function call, not a macro call. ~bv[] (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (pargs (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) ~ev[] ~b[Introducing the Granularity Problem] After you submit the above two versions of the Fibonacci function, test them with the following forms. ~bv[] (time$ (fib 10)) (time$ (pfib 10)) ~ev[] Now increase the argument by increments of 5 to 10 until you find your curiosity satisfied or your patience wearing thin. You can interrupt evaluation if necessary and return to the ACL2 loop. You will immediately notice that you have not increased execution speed, at least not by much, by introducing parallelism. First, consider the computation of ~c[(pfib 4)]. Assuming resources are available, ~c[(pfib 4)] will create a thread for computing ~c[(pfib 3)] and another thread for ~c[(pfib 2)]. It is easy to imagine that setting up each thread takes much longer than the entire computation of ~c[(fib 4)]. Second, we must realize that if we have two threads available for computing ~c[(fib 10)], then the evaluation of ~c[(fib 8)] will probably complete before the evaluation of ~c[(fib 9)]. Once ~c[(fib 8)] finishes, parallelism resources will become available for the next recursive call made on behalf of ~c[(fib 9)]. If for example that call is ~c[(fib 3)], we will waste a lot of cycles just handing work to the thread that does this relatively small computation. We need a way to ensure that parallelism resources are only used on problems of a \"large\" size. Ensuring that only \"large\" problems are spawned is called the ``granularity problem.'' In summary: We want to tell ACL2 that it can evaluate the arguments of ~ilc[pargs] in parallel only when the parameter of ~c[pfib] is greater than some threshold. Our tests using CCL have suggested that 27 is a reasonable threshold. ~b[Explicit Programming for the Granularity Problem] One way to avoid the granularity problem is to duplicate code as follows. ~bv[] (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (if (> x 27) ; the granularity test (pargs (binary-+ (pfib (- x 1)) (pfib (- x 2)))) (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) ~ev[] Duplicating code is fundamentally a bad design principle, because it can double the work for future maintenance. A ``granularity form'' is an expression ~bv[] (declare (granularity )) ~ev[] that can allow threads to be spawned (without duplicating code) whenever the evaluation of ~c[] results in a non-~c[nil] value. It may be placed inside any call of a parallelism primitive, in a position documentated separately for each primitive. Here is a definition of ~c[pfib] using this feature for a call of the parallelism primitive ~ilc[pargs]. ~bv[] (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (pargs (declare (granularity (> x 27))) (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) ~ev[] Test each version as follows (or substitute your own natural number for 33). ~bv[] (time$ (fib 33)) (time$ (pfib 33)) ~ev[] ~b[Another Granularity Issue Related to Thread Limitations] Our implementation of parallelism primitives has the property that once a thread is assigned a computation, that assignment stays in effect until the computation is complete. In particular, if a thread encounters a parallelism primitive that spawns child threads, the parent thread stays assigned, waiting until the child computations complete before it can continue its own computation. In the meantime, the parent thread reduces the number of additional threads that Lisp can provide by 1, rather than being reassigned to do other work. How can this lack of reassignment affect the user? Consider, for example, the application of a recursive function to a long list. Imagine that this function is written so that the body contains a recursive call, for example as ~c[(pargs (process (car x)) (recur (cdr x)))]. Each such ~ilc[pargs] call that spawns child work must wait for its children, one of which is the work of evaluating ~c[(recur (cdr x))], to complete. There is an ACL2 limit on how much pending work can be in the system, limiting the number of ~ilc[pargs] calls that can result in parallel execution. For example, if the ACL2 limit is k and each call of ~ilc[pargs] actually spawns threads for evaluating its arguments, then after k ~c[cdr]s there will be no further parallel execution. Possible solutions may include reworking of algorithms (for example to be tree-based rather than list-based) or using appropriate granularity forms. We hope that future implementations will allow thread ``re-deployment'' in order to mitigate this problem. This problem applies to ~ilc[plet], ~ilc[pargs], ~ilc[pand], ~ilc[por], and ~ilc[spec-mv-let]. ~b[Introducing] ~ilc[Plet] We can use a ~ilc[let] binding to compute the recursive calls of ~c[fib] and then add the bound variables together, as follows. ~bv[] (defun fib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (let ((a (fib (- x 1))) (b (fib (- x 2)))) (+ a b))))) ~ev[] By using the parallelism primitive ~ilc[plet], we can introduce parallelism much as we did using ~ilc[pargs], with an optional granularity form, as follows. ~bv[] (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (plet (declare (granularity (> x 27))) ((a (pfib (- x 1))) (b (pfib (- x 2)))) (+ a b))))) ~ev[] Notice that this time, we were able to use ~c[+] rather than being forced to use ~c[binary-+]. Unlike ~ilc[pargs], which expects a function call (not a macro call), ~ilc[plet] allows macros at the top level of its body. ~b[Introducing] ~ilc[Pand] (By Way of a Tree Validation Example) Consider ``genetic trees'' that contains leaves of DNA elements, in the sense that each tip is one of the symbols ~c[A], ~c[G], ~c[C], or ~c[T]. First we define the function ~c[valid-tip] which recognizes whether a tip contains one of these symbols. ~bv[] (defun valid-tip (tip) (declare (xargs :guard t)) (or (eq tip 'A) (eq tip 'G) (eq tip 'C) (eq tip 'T))) ~ev[] Now we define a function that traverses the tree, checking that every tip is valid. ~bv[] (defun valid-tree-serial (tree) (declare (xargs :guard t)) (if (atom tree) (valid-tip tree) (and (valid-tree-serial (car tree)) (valid-tree-serial (cdr tree))))) ~ev[] We also define a parallel version. ~bv[] (defun valid-tree-parallel (tree) (declare (xargs :guard t)) (if (atom tree) (valid-tip tree) (pand (valid-tree-parallel (car tree)) (valid-tree-parallel (cdr tree))))) ~ev[] Before we can time the functions, we need to create test trees. We have found that test trees need to be approximately 1 gigabyte before we clearly see speedup, and we make them asymmetric to demonstrate the ability of our implementation to adapt to asymmetric data. We can create the trees with the execution of the following forms. ~bv[] (defun make-valid-binary-tree (x) (declare (xargs :mode :program)) (if (< x 0) (cons (cons 'C 'G) (cons 'A 'T)) (cons (make-valid-binary-tree (- x 2)) ; 2 to make asymmetric (make-valid-binary-tree (- x 1))))) (defconst *valid-tree* (make-valid-binary-tree 30)) ; takes awhile (defconst *invalid-tree* (cons *valid-tree* nil)) ; nil is invalid tip ~ev[] We can time our functions with the forms: ~bv[] (time$ (valid-tree-serial *valid-tree*)) (time$ (valid-tree-parallel *valid-tree*)) ~ev[] Unfortunately, the serial version runs faster than the parallelism version; however, there is more to this story. ~b[Demonstrating Early Termination with an Invalid Tree] Now observe this magic: ~bv[] (time$ (valid-tree-serial *invalid-tree*)) (time$ (valid-tree-parallel *invalid-tree*)) ~ev[] The serial version took awhile, while the parallel version finished almost immediately. The test for validation was split into testing the ~ilc[car] and the ~ilc[cdr] of the ~c[*invalid-tree*] root, and since the ~c[cdr] was equal to ~c[nil], its test returned immediately. This immediate return quickly interrupted the computation associated with the ~c[car], and returned the result. ~b[Granularity with] ~ilc[Pand] We can also define a parallel version with a granularity form: ~bv[] (defun valid-tree-parallel (tree depth) (declare (xargs :guard (natp depth))) (if (atom tree) (valid-tip tree) (pand (declare (granularity (< depth 5))) (valid-tree-parallel (car tree) (1+ depth)) (valid-tree-parallel (cdr tree) (1+ depth))))) ~ev[] We can test this form by executing our previous forms. You will probably find some speedup on a machine with several cores available, but more speedup can likely be obtained with an expensive test on the leaves in place of ~c[valid-tip]. ~bv[] (time$ (valid-tree-serial *valid-tree*)) (time$ (valid-tree-parallel *valid-tree* 0)) ~ev[] ~b[Introducing] ~ilc[Por] ~ilc[Por] can be used in the same way as ~ilc[pand], but with early termination occurring when an argument evaluates to a non-~c[nil] value, in which case the value returned is ~c[t]. Finally, ~ilc[por] also supports the use of a ~il[granularity] form.~/") (defdoc granularity ":Doc-Section Parallel-programming limit the amount of parallelism~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. Some function calls are on arguments whose evaluation time is long enough to warrant parallel execution, while others are not. A granularity form can be used to make appropriate restrictions on the use of parallelism.~/ For example, consider the Fibonacci function. Experiments have suggested that execution time can be reduced if whenever the argument is less than 27, a serial version of the Fibonacci function is called. One way to utilize this information is to write two definitions of the Fibonacci function, one serial and one parallel. ~bv[] (defun fib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (binary-+ (fib (- x 1)) (fib (- x 2)))))) (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) ((< x 27) (binary-+ (fib (- x 1)) (fib (- x 2)))) (t (pargs (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) ~ev[] We realize quickly that writing both of these function definitions is cumbersome and redundant. This problem can be avoided by using a ~c[granularity] declaration with a parallelism primitive. This form ensures that a call is parallelized only if resources are available and the granularity form evaluates to a non-~c[nil] value at the time of the call. Below is a definition of the Fibonacci function using a granularity form. ~bv[] (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (pargs (declare (granularity (>= x 27))) (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) ~ev[] A granularity form can reference an extra formal parameter that describes the call depth of the function the user is parallelizing. Consider for example the following parallel ~c[mergesort] function, based on Davis's Ordered Sets library. It splits the data into symmetric chunks for computation, so we increment the ~c[depth] argument during the recursive call on both the ~c[car] and ~c[cdr]. ~bv[] (include-book \"finite-set-theory/osets/sets\" :dir :system) (defun sets::pmergesort-exec (x depth) (declare (xargs :mode :program)) (cond ((endp x) nil) ((endp (cdr x)) (sets::insert (car x) nil)) (t (mv-let (part1 part2) (sets::split-list x nil nil) (pargs (declare (granularity (< depth 2))) (sets::union (sets::pmergesort-exec part1 (1+ depth)) (sets::pmergesort-exec part2 (1+ depth)))))))) ~ev[] A less intrusive method (i.e., not requiring an extra formal parameter like the ~c[depth] argument just above), which however can be less efficient, involves analyzing the data itself for structural properties. For example: ~bv[] (defun some-depth-exceeds (x n) (declare (xargs :guard (natp n))) (if (atom x) nil (if (zp n) t (or (some-depth-exceeds (car x) (1- n)) (some-depth-exceeds (cdr x) (1- n)))))) (defun valid-tip (x) (declare (xargs :guard t)) (or (eq x 'A) (eq x 'T) (eq x 'C) (eq x 'G))) (defun pvalid-tree (x) (declare (xargs :guard t)) (if (atom x) (valid-tip x) (pand (declare (granularity (some-depth-exceeds x 3))) (pvalid-tree (car x)) (pvalid-tree (cdr x))))) ~ev[] If you experiment with calls of ~c[pvalid-tree], you are likely to find that the ``speedup'' it provides over a corresponding serial version is, in fact, a slowdown! The problem is likely that ~c[some-depth-exceeds] is an expensive function to run repeatedly. Instead of the approach above, it is often handy to add an extra formal parameter in order to allow for more efficient granularity forms, as we have done above in the definition of ~c[SETS::pmergesort-exec]. ~/") (defdoc parallelism-performance ":Doc-Section Parallel-programming performance issues for parallel execution~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~l[granularity] for an important construct that limits the spawning of parallel computations, which can be important when a computation is too short-lived to warrant a separate thread. There are times in which parallelism provides no speedup because of garbage collection in the underlying Lisp implementation. The following example illustrates this phenomenon. If you change the ~ilc[granularity] declaration so that the depth bound is 3, 4, or larger instead of 2, you may still find no speedup. In all cases you may find that parallelism results in a significantly greater time spent in garbage collection. ~bv[] (include-book \"finite-set-theory/osets/sets\" :dir :system) (defun sets::pmergesort-exec (x depth) (declare (xargs :mode :program)) (cond ((endp x) nil) ((endp (cdr x)) (sets::insert (car x) nil)) (t (mv-let (part1 part2) (sets::split-list x nil nil) (pargs (declare (granularity (< depth 2))) (sets::union (sets::pmergesort-exec part1 (1+ depth)) (sets::pmergesort-exec part2 (1+ depth)))))))) (defconst *x* (reverse (fromto 1 400000))) (time$ (length (sets::pmergesort-exec *x* 0))) (set-parallel-execution nil) (time$ (length (sets::pmergesort-exec *x* 0))) ~ev[]~/~/") (defdoc early-termination ":Doc-Section Parallel-programming early termination for ~ilc[pand] and ~ilc[por].~/~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. The evaluation of ~c[(and expr1 expr2)] returns ~c[nil] if ~c[expr1] evaluates to ~c[nil], avoiding the evaluation of ~c[expr2]. More generally, the evaluation of ~c[(and expr1 expr2 ... exprk)] terminates with a return value of ~c[nil] as soon as any ~c[expri] evaluates to ~c[nil] ~-[] no ~c[exprj] is evaluated in this case for ~c[j > i]. This so-called ``lazy evaluation'' of ~ilc[and] terms can thus save some computation; roughly speaking, the smaller the ~c[i], the more computation is saved. If the above call of ~ilc[and] is replaced by its parallel version, ~ilc[pand], then there can be even more opportunity for skipping work. The arguments to ~ilc[pand] can be evaluated in parallel, in which case the first such evaluation that returns with a value of ~c[nil], if any, causes the remaining such evaluations to abort. Consider the following functions that compute whether a tree is valid (~pl[granularity] for a discussion of the granularity form). ~bv[] (defun valid-tip (x) (declare (xargs :guard t)) (or (eq x 'A) (eq x 'T) (eq x 'C) (eq x 'G))) (defun pvalid-tree (x depth) (declare (xargs :guard (natp depth))) (if (atom x) (valid-tip x) (pand (declare (granularity (< depth 10))) (pvalid-tree (car x) (1+ depth)) (pvalid-tree (cdr x) (1+ depth))))) ~ev[] We would like to stop execution as soon as any tip is found to be invalid. So, when computing the conjunction of terms by using ~ilc[pand], once one of those terms evaluates to ~c[nil], the computations for the other terms are aborted and the ~ilc[pand] call returns ~c[nil]. By using ~ilc[pand], we can in principle attain a speedup factor greater than the number of available cores. The concept of early termination also applies to ~ilc[por], except that early termination occurs when an argument evaluates to non-~c[nil].~/") (defdoc parallel-pushing-of-subgoals-for-induction ":Doc-Section Parallel-proof consequences of how parallelized proofs of subgoals are pushed for induction~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. The following discussion, concerning the naming of subgoals pushed for proof by induction and the timeliness of aborting when two or more goals are pushed for proof by induction, only applies when waterfall parallelism is enabled (~pl[set-waterfall-parallelism]).~/ When two sibling subgoals (e.g. 4.5 and 4.6) both push goals to be proved by induction (e.g., 4.6 pushes *1 and 4.5 pushes *2), a name is assigned to the second pushed subgoal (e.g., *2) as if the first push hasn't happened (e.g., *2 is mistakenly called *1). In such a case, we say what the name _could_ be. The following non-theorem illustrates how this works. ~bv[] (set-waterfall-parallelism :full) (thm (equal (append (car (cons x x)) y z) (append x x y))) ~ev[] There is another consequence of the way the parallelized waterfall pushes subgoals for proof by induction. Without waterfall parallelism enabled, ACL2 sometimes decides to abort instead of pushing a goal for later proof by induction, preferring instead to induct on the original conjecture. But with waterfall parallelism enabled, the prover no longer necessarily immediately aborts to prove the original conjecture. Suppose for example that sibling subgoals, Subgoal 4.6 and Subgoal 4.5, each push a subgoal for induction. If the waterfall is performing the proof of each of these subgoals in parallel, the proof will no longer abort immediately after the second push occurs, that is at Subgoal 4.5. As a result, the prover will continue through Subgoal 4.4, Subgoal 4.3, and beyond. It is not until the results of combining the proof results of Subgoal 4.6 with the results from the remaining sibling subgoals (4.5, 4.4, and so on), that the proof attempt will abort and revert to prove the original conjecture by induction. This example illustrates behavior that is rather like the case that ~c[:]~ilc[otf-flg] is ~c[t], in the sense that the abort does not happen immediately, but also rather like the case that ~c[:]~ilc[otf-flg] is ~c[nil], in the sense that the abort does occur before getting to Subgoal 3.~/") (defun caar-is-declarep (x) ; Recognizer for expressions x for which (car x) is of the form (declare ...). (declare (xargs :guard t)) (and (consp x) (consp (car x)) (eq (caar x) 'declare))) (defun declare-granularity-p (x) ; We return true when x is of the form (declare (granularity )). (declare (xargs :guard t)) (and (true-listp x) (eql (length x) 2) (eq (car x) 'declare) (let ((gran-form (cadr x))) (and (true-listp gran-form) (eql (length gran-form) 2) (eq (car gran-form) 'granularity))))) (defun check-and-parse-for-granularity-form (x) ; X is a list of forms that may begin with a granularity declaration such as ; (declare (granularity (< depth 5))). The return signature is (erp msg ; granularity-form-exists granularity-form remainder-forms). If there is no ; declaration then we return (mv nil nil nil nil x). If there is error then we ; return (mv t an-error-message nil nil x). Otherwise we return (mv nil nil t ; granularity-form (cdr x)). ; It is necessary to return whether the granularity form exists. If we did not ; do so, there would be no mechanism for distinguishing between a non-existent ; granularity form and one that was nil. ; A granularity form declaration is the only acceptable form of declaration. ; Some examples of unaccepted declarations are type and ignore declarations. ; We use this function in both the raw and acl2-loop definitions of plet to ; macroexpand away our granularity form, as part of our effort to ensure that ; pargs is logically the identity function. (cond ((not (caar-is-declarep x)) (mv nil nil nil nil x)) ((declare-granularity-p (car x)) (let* ((granularity-declaration (cadar x)) (granularity-form (cadr granularity-declaration))) (mv nil nil t granularity-form (cdr x)))) (t (mv t "Within a parallelism primitive, a granularity form declaration ~ is the only acceptable form of declaration. Some examples of ~ unaccepted declarations are type and ignore declarations. See ~ :DOC granularity." nil nil x)))) #+(or acl2-loop-only (not acl2-par)) (defmacro pargs (&rest forms) ":Doc-Section Parallel-programming parallel evaluation of arguments in a function call~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] Example Forms: (pargs (binary-+ (fibonacci (- x 1)) (fibonacci (- x 2)))) (pargs (declare (granularity (> x 35))) (binary-+ (fibonacci (- x 1)) (fibonacci (- x 2))))~/ General Form: (pargs (declare (granularity expr)) ; optional granularity declaration (f arg1 ... argN)) ~ev[] where ~c[N >= 0] and each ~c[argi] and ~c[expr] are arbitrary terms. Logically, ~c[pargs] is just the identity macro, in the sense that the above forms can logically be replaced by ~c[(f arg1 ... argN)]. However, this ~c[pargs] form may parallelize the evaluation of arguments ~c[arg1] through ~c[argN] before applying function ~c[f] to those results. If the above ~ilc[granularity] declaration is present, then its expression (~c[expr] above) is first evaluated, and if the result is ~c[nil], then such parallelism is avoided. Even if parallelism is not thus avoided, parallelism may be limited by available resources. Since macros can change expressions in unexpected ways, it is illegal to call ~c[pargs] on a form that is a macro call. To parallelize computation of arguments to a macro, ~pl[plet]. ~l[parallelism-at-the-top-level] for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop.~/" (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (cond (erp (er hard 'pargs msg)) ((or (and (equal (length forms) 1) (not gran-form-exists)) (and (equal (length forms) 2) gran-form-exists)) (let ((function-call (car remainder-forms))) (if gran-form-exists `(prog2$ ,gran-form ,function-call) function-call))) (t (er hard 'pargs "Pargs was passed the wrong number of arguments. Without a ~ granularity declaration, pargs takes one argument. With a ~ granularity declaration, pargs requires two arguments, the ~ first of which must be of the form ~x0. See :DOC pargs." '(declare (granularity expr))))))) #+(or acl2-loop-only (not acl2-par)) (defmacro plet (&rest forms) ":Doc-Section Parallel-programming parallel version of ~ilc[let]~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] Example Forms: (plet ((a (fibonacci (- x 1))) (b (fibonacci (- x 2)))) (+ a b)) (plet (declare (granularity (> x 35))) ((a (fibonacci (- x 1))) (b (fibonacci (- x 2)))) (+ a b))~/ General Form: (plet (declare (granularity expr)) ; optional granularity declaration ((var1 val1) ... (varN valN)) (declare ...) ... (declare ...) ; optional declarations body) ~ev[] The syntax of ~c[plet] is identical to the syntax of ~ilc[let], except that ~c[plet] permits an optional granularity declaration in the first argument position; ~pl[granularity]. In the logic a call of ~c[plet] macroexpands to the corresponding call of ~ilc[let], where the granularity declaration (if any) is dropped. ~c[Plet] cause the evaluation of each ~c[vali] above to be done in parallel before processing the body. If the above ~ilc[granularity] declaration is present, then its expression (~c[expr] above) is first evaluated, and if the result is ~c[nil], then such parallelism is avoided. Even if parallelism is not thus avoided, parallelism may be limited by available resources. ~l[parallelism-at-the-top-level] for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop.~/" (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (cond (erp (er hard 'plet msg)) (gran-form-exists `(prog2$ ,gran-form (let ,@remainder-forms))) (t `(let ,@remainder-forms))))) (defun binary-pand (x y) ; Booleanized binary and. (declare (xargs :guard t :mode :logic)) (and x y t)) #+(or acl2-loop-only (not acl2-par)) (defmacro pand (&rest forms) ; We Booleanize pand so that it is consistent with por, which must be ; Booleanized (see :DOC por). Another nice thing about this Booleanization is ; that it emphasizes that PAND differs from AND logically, which can raise ; awareness of a guard-related difference based on the impact of lazy ; evaluation. ":Doc-Section Parallel-programming parallel, Boolean version of ~ilc[and]~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] Example Forms: (pand (subsetp-equal x y) (subsetp-equal y x)) (pand (declare (granularity (and (> (length x) 500) (> (length y) 500)))) (subsetp-equal x y) (subsetp-equal y x)) ~ev[]~/ ~bv[] General Form: (pand (declare (granularity expr)) ; optional granularity declaration arg1 ... argN) ~ev[] where ~c[N >= 0] and each ~c[argi] and ~c[expr] are arbitrary terms. ~c[Pand] evaluates its arguments in parallel. It returns a Boolean result: ~c[nil] if any argument evaluates to ~c[nil], else ~c[t]. Note that ~c[pand] always returns a Boolean result, even though ~c[and] can return a non-~c[nil] value other than ~c[t], namely the value of its last argument. (A moment's reflection will make it clear that in order for ~ilc[por] to parallelize efficiently, it needs to return a Boolean value; so ~c[pand] returns a Boolean value for consistency with ~ilc[por].) Another difference between ~c[pand] and ~ilc[and] is that for a call of ~c[pand], even if an argument evaluates to ~c[nil], a subsequent argument may be evaluated. Consider the following example (where ~c[cw] prints a string; ~pl[cw]). ~bv[] (defun bar () (pand (equal (make-list 100000) nil) ; evaluates to nil (cw \"hello world~~%\"))) ~ev[] When ~c[(bar)] is evaluated, the above arguments of ~c[pand] can execute in parallel, causing ``~c[hello world]'' to be printed to the terminal. If we had used ~c[and] rather than ~c[pand], then since ~c[(equal (make-list 100000) nil)] evaluates to ~c[nil], the above call of ~ilc[cw] would be avoided and no such printing would take place. Even with ~c[pand], such printing ~em[might] not take place, depending on resources, timing of thread creation, and whether or not parallel execution is enabled (~pl[set-parallel-execution]). Note that unlike the case for ~ilc[and], the definition of ~c[pand] does not provide ~c[(consp x)] as a ~il[guard] to ~c[(car x)] in the call of ~c[pand] below: ~bv[] (defun h (x) (declare (xargs :guard t)) (pand (consp x) (equal (car x) 'foo))) ~ev[] As a result, ~il[guard] verification will fail for the above definition. If ~c[pand] were replaced by ~c[and], then ~il[guard] verification would succeed. ~l[parallelism-tutorial] for another example. Also ~pl[parallelism-at-the-top-level] for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop. Finally ~pl[early-termination] to read how ~c[pand] can offer more efficiency than ~ilc[and] by avoiding evaluation of some of its arguments.~/" ; Since we use &rest, we know forms is a true-list. (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (cond (erp (er hard 'pand msg)) ((atom remainder-forms) t) ; (pand) == t ((null (cdr remainder-forms)) ; same as length == 1 (list 'if (car remainder-forms) t nil)) ; booleanize (gran-form-exists (list 'prog2$ gran-form (xxxjoin 'binary-pand remainder-forms))) (t (xxxjoin 'binary-pand remainder-forms))))) (defun binary-por (x y) ; Booleanized binary or. (declare (xargs :guard t :mode :logic)) (if x t (if y t nil))) #+(or acl2-loop-only (not acl2-par)) (defmacro por (&rest forms) ; Note that por must be Booleanized if we are to support early termination, ; i.e., so that any non-nil value can cause por to return. ":Doc-Section Parallel-programming parallel, Boolean version of ~ilc[or]~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] Example Forms: (por (subsetp-equal x y) (subsetp-equal y x)) (por (declare (granularity (and (> (length x) 500) (> (length y) 500)))) (subsetp-equal x y) (subsetp-equal y x)) ~ev[]~/ ~bv[] General Form: (por (declare (granularity expr)) ; optional granularity declaration arg1 ... argN) ~ev[] where ~c[N >= 0] and each ~c[argi] and ~c[expr] are arbitrary terms. ~c[Por] evaluates its arguments in parallel. It returns a Boolean result: ~c[t] if any argument evaluates to non-~c[nil], else ~c[nil]. Note that while ~ilc[or] returns the first non-~c[nil] value from evaluating its arguments left-to-right (if any such value is not ~c[nil]) ~ilc[por] always returns a Boolean result, in support of efficiency (~pl[early-termination]) in light of the nondeterministic order in which argument values are returned. Another difference between ~c[por] and ~ilc[or] is that for a call of ~c[por], even if the an argument's value is not ~c[nil], a subsequent argument may be evaluated. ~l[pand] for a discussion of the analogous property of ~c[pand]. In particular, ~il[guard]s generated from calls of ~c[por] may not assume for an argument that the preceding arguments evaluated to ~c[nil]. ~l[parallelism-tutorial] for another example. Also ~pl[parallelism-at-the-top-level] for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop. Finally ~pl[early-termination] to read how ~c[por] can offer more efficiency than ~ilc[or] by avoiding evaluation of some of its arguments.~/" (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (cond (erp (er hard 'por msg)) ((atom remainder-forms) nil) ; (por) == nil ((null (cdr remainder-forms)) ; same as length == 1 (list 'if (car remainder-forms) t nil)) (gran-form-exists (list 'prog2$ gran-form (xxxjoin 'binary-por remainder-forms))) (t (xxxjoin 'binary-por remainder-forms))))) (defun or-list (x) (declare (xargs :guard (true-listp x) :mode :logic)) (if (endp x) nil (if (car x) t (or-list (cdr x))))) (defun and-list (x) (declare (xargs :guard (true-listp x) :mode :logic)) (if (endp x) t (and (car x) (and-list (cdr x))))) (defun cpu-core-count (state) ":Doc-Section ACL2::ACL2-built-ins the number of cpu cores~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. Unless the ACL2 executable supports parallel execution (~pl[parallelism]), this function returns ~c[(mv 1 state)]. Otherwise: ~c[(Cpu-core-count state)] returns ~c[(mv core-count state)], where ~c[core-count] is the number of cpu cores if ACL2 can get that information from the underlying Common Lisp implementation. Otherwise an error occurs, unless global ~c['cpu-core-count] is assigned to a positive integer value (~pl[assign]), in which case that value is returned as the ~c[core-count]. ~bv[] Example: (cpu-core-count state) ==> (mv 4 state) ~ev[].~/~/" (declare (xargs :stobjs state :guard t)) #+(and (not acl2-loop-only) (not acl2-par)) (when (live-state-p state) (return-from cpu-core-count (mv 1 state))) #+(and (not acl2-loop-only) acl2-par) (when (live-state-p state) (return-from cpu-core-count (mv (if (and (f-boundp-global 'cpu-core-count state) (posp (f-get-global 'cpu-core-count state))) (core-count-raw nil (f-get-global 'cpu-core-count state)) (core-count-raw 'core-count)) state))) (mv-let (nullp val state) (read-acl2-oracle state) (declare (ignore nullp)) (mv val state))) ; Preliminary code for parallelizing the rewriter ; ; We now develop code for parallelizing calls to the arguments of a call of ; ; rewrite. ; ; ; WARNING! We believe that this approach has the following bug. If ; ; with-prover-time-limit is used, then the main thread (which is the one ; ; calling waterfall-step) has a catch (implemented by the call there of ; ; catch-time-limit5) that will only catch throws to that tag from the SAME ; ; thread. We will get in trouble if a spawned thread's call of rewrite does ; ; such a throw. ; ; ; Warning: Moreover, if we use this code, consider modifying the ; ; rewrite-constant to store the value of :limit in ; ; rewrite-args-granularity-table. Otherwise, we have to go to the world with a ; ; potentially slow getprop every time we call rewrite-args-par-big-enough. ; ; Maybe that's just noise, but maybe it's expensive. ; ; ; We initially set the value of (the unique key) :limit to nil in ; ; rewrite-args-granularity-table, so that in fact we do not do such ; ; parallelization. But we leave this infrastructure in place (see comment "or ; ; try :limit" below) in case we want to experiment with such parallelization in ; ; the future. ; ; #+acl2-par ; (table rewrite-args-granularity-table nil nil ; :guard (and (eq key :limit) ; (or (null val) (natp val)))) ; ; #+acl2-par ; (table rewrite-args-granularity-table :limit nil) ; or try :limit = 10 ; ; #+acl2-par ; (defun rewrite-args-par-big-enough-rec (flg x bound acc) ; ; ; Flg is true when x is a list of terms; else x is a term. Returns a number by ; ; accumulating into acc, or t if that number would exceed bound. We assume ; ; that acc is <= bound. ; ; (cond (flg ; x is a list ; (cond ((null x) ; acc) ; (t ; (let ((new-acc (rewrite-args-par-big-enough-rec ; nil (car x) bound acc))) ; (if (eq new-acc t) ; t ; (rewrite-args-par-big-enough-rec ; flg (cdr x) bound new-acc)))))) ; ((variablep x) ; acc) ; ((fquotep x) ; acc) ; ((eql bound acc) ; t) ; ((flambdap (ffn-symb x)) ; (let ((new-acc (rewrite-args-par-big-enough-rec ; nil (lambda-body (ffn-symb x)) bound (1+ acc)))) ; (if (eq new-acc t) ; t ; (rewrite-args-par-big-enough-rec t (fargs x) bound new-acc)))) ; (t (rewrite-args-par-big-enough-rec t (fargs x) bound (1+ acc))))) ; ; #+acl2-par ; (defun rewrite-args-par-big-enough (x wrld) ; ; ; If the limit is set to nil, the function returns nil. This allows the ; ; enabling and disabling of rewriting args in parallel. ; ; (let ((limit (cdr (assoc-eq :limit ; (table-alist ; 'rewrite-args-granularity-table ; wrld))))) ; (and limit (equal t (rewrite-args-par-big-enough-rec nil x limit 0))))) ; ; ; With the additions above, we can contemplate adding something like the ; ; following to the rewrite nest below. If we do that, then replace the call of ; ; rewrite-args in rewrite by the following: ; ; ; #-acl2-par ; ; rewrite-args ; ; #+acl2-par ; ; rewrite-args-par ; ; #+acl2-par ; (defun rewrite-args-par (args alist bkptr ; &extra formals ; rdepth ; type-alist obj geneqv wrld state fnstack ; ancestors backchain-limit ; simplify-clause-pot-lst rcnst gstack ttree) ; (let ((pair (rewrite-entry (rewrite-args-par-rec args alist bkptr)))) ; (mv (car pair) (cdr pair)))) ; ; #+acl2-par ; (defun rewrite-args-par-rec (args alist bkptr ; &extra formals ; rdepth ; type-alist obj geneqv wrld state fnstack ; ancestors backchain-limit ; simplify-clause-pot-lst rcnst gstack ttree) ; ; ; Note: In this function, the extra formal geneqv is actually a list of geneqvs ; ; or nil denoting a list of nil geneqvs. ; ; ; Unlike rewrite-args, we return (cons rewritten-args ttree) instead of ; ; (mv rewritten-args ttree). ; ; (declare (type (unsigned-byte 29) rdepth)) ; (cond ((f-big-clock-negative-p state) ; (cons (sublis-var-lst alist args) ; ttree)) ; ((null args) ; (cons nil ttree)) ; (t (plet ; (declare (granularity t)) ; should call rewrite-args-par-big-enough ; ((pair1 ; (mv-let (term ttree1) ; (rewrite-entry (rewrite (car args) alist bkptr) ; :geneqv (car geneqv) ; :ttree nil) ; (cons term ttree1))) ; (pair2 (rewrite-entry ; (rewrite-args-par-rec (cdr args) alist (1+ bkptr)) ; :geneqv (cdr geneqv)))) ; (let* ((term (car pair1)) ; (ttree1 (cdr pair1)) ; (rewritten-args (car pair2)) ; (ttree2 (cdr pair2))) ; (cons (cons term rewritten-args) ; (cons-tag-trees ttree1 ttree2))))))) ; Preliminary code for parallelizing the rewriter for ACL2 version 6.3. ; Note that the following code treats step-limits a little differently from how ; they are treated in the sequential version. If we keep this treatment, we ; should add a comment here and in decrement-step-limit suggesting that if we ; change either, then we should consider changing the other. Also note the ; commented out declare forms, which would be good to include (especially ; important for GCL) once spec-mv-let accepts them. And finally, note that as ; of v6-2, it is necessary to unmemoize the rewriter functions when running the ; rewriter in parallel in ACL2(hp) because memoization is not thread-safe. ; This unmemoization can perhaps be done by issuing a call of (unmemoize-all) ; in raw Lisp). ; (defun rewrite-args (args alist bkptr; &extra formals ; rdepth step-limit ; type-alist obj geneqv wrld state fnstack ancestors ; backchain-limit ; simplify-clause-pot-lst rcnst gstack ttree) ; ; ; Note: In this function, the extra formal geneqv is actually a list of geneqvs ; ; or nil denoting a list of nil geneqvs. ; ; (declare (type (unsigned-byte 29) rdepth) ; (type (signed-byte 30) step-limit)) ; (the-mv ; 3 ; (signed-byte 30) ; (cond ((null args) ; (mv step-limit nil ttree)) ; (t (spec-mv-let ; (step-limit1 rewritten-arg ttree1) ; ; (declare (type (signed-byte 30) step-limit1)) ; (rewrite-entry (rewrite (car args) alist bkptr) ; :geneqv (car geneqv)) ; (mv-let ; (step-limit2 rewritten-args ttree2) ; (rewrite-entry (rewrite-args (cdr args) alist (1+ bkptr)) ; :geneqv (cdr geneqv)) ; ; (declare (type (signed-byte 30) step-limit2)) ; (if t ; (mv (let* ((steps1 (- step-limit step-limit1)) ; (step-limit (- step-limit2 steps1))) ; (declare (type (signed-byte 30) steps1 step-limit)) ; (cond ((>= step-limit 0) ; step-limit) ; ((step-limit-strictp state) ; (step-limit-error nil)) ; (t -1))) ; (cons rewritten-arg rewritten-args) ; (cons-tag-trees ttree1 ttree2)) ; (mv 0 ; nil ; nil)))))))) #+(or acl2-loop-only (not acl2-par)) (defmacro spec-mv-let (bindings computation body) ":Doc-Section Parallel-programming modification of ~ilc[mv-let] supporting speculative and parallel execution~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] Example Form: (defun pfib-with-step-count (x) (declare (xargs :mode :program)) (if (or (zp x) (< x 33)) (fib-with-step-count x) (spec-mv-let (a cnt1) (pfib-with-step-count (- x 1)) (mv-let (b cnt2) (pfib-with-step-count (- x 2)) (if t (mv (+ a b) (+ 1 cnt1 cnt2)) (mv \"speculative result is always needed\" -1))))))~/ General Form: (spec-mv-let (v1 ... vn) ; bind distinct variables ; evaluate speculatively; return n values (mv-let ; or, use mv?-let if k=1 below (w1 ... wk) ; bind distinct variables ; evaluate eagerly (if ; use results from if true ; may mention v1 ... vn ))) ; does not mention v1 ... vn ~ev[] Our design of ~c[spec-mv-let] is guided by its use in ACL2 source code to parallelize part of ACL2's proof process, in the experimental parallel extension of ACL2. The user can think of ~c[spec-mv-let] as a speculative version of ~ilc[mv-let]. (In ordinary ACL2, the semantics agree with this description but without speculative or parallel execution.) Evaluation of the above general form proceeds as suggested by the comments. First, ~c[] is executed speculatively. Control then passes immediately to the ~ilc[mv-let] call, without waiting for the result of evaluating ~c[]. The variables ~c[(w1 ... wk)] are bound to the result of evaluating ~c[], and then ~c[] is evaluated. If the value of ~c[] is true, then the values of ~c[(v1 ... vn)] are needed, and ~c[] blocks until they are available. If the value of ~c[] is false, then the values of ~c[(v1 ... vn)] are not needed, and the evaluation of ~c[] may be aborted. The calls to ~c[mv-let] and to ~c[if] displayed above in the General Form are an essential part of the design of ~c[spec-mv-let], and are thus required. The following definition of ~c[fib-with-step-count] completes the example above: ~bv[] (defun fib-with-step-count (x) (declare (xargs :mode :program)) (cond ((<= x 0) (mv 0 1)) ((= x 1) (mv 1 1)) (t (mv-let (a cnt1) (fib-with-step-count (- x 1)) (mv-let (b cnt2) (fib-with-step-count (- x 2)) (mv (+ a b) (+ 1 cnt1 cnt2))))))) ~ev[]~/" (assert$ (and (true-listp body) (equal (length body) 4) (or (equal (car body) 'mv-let@par) (equal (car body) 'mv-let) (equal (car body) 'mv?-let))) (let* ((inner-let (car body)) (inner-bindings (cadr body)) (inner-body (caddr body)) (ite (cadddr body))) (assert$ (and (true-listp ite) (equal (length ite) 4) (equal (car ite) 'if)) (let* ((test (cadr ite)) (true-branch (caddr ite)) (false-branch (cadddr ite))) `(check-vars-not-free ; Keep the check for variable name "the-very-obscure-feature" in sync with the ; variable name in the raw Lisp version. (the-very-obscure-future) (,inner-let ,inner-bindings ,inner-body (if (check-vars-not-free ,bindings ,test) (mv?-let ,bindings ,computation ,true-branch) (check-vars-not-free ,bindings ,false-branch))))))))) ; Parallelism wart: when set-verify-guards-eagerness is 0, and there is a guard ; violation in subfunctions that are evaluating in the non-main-thread, we get ; errors that aren't user friendly (the errors occur in the non-main-threads). ; I think that the solution to this problem might necessitate catching the ; errors and re-causing them. Hitting ctrl+c causes the main thread to abort ; waiting on the result from those threads, and allows the interactive session ; to resume. David says that he may have already fixed this for spec-mv-let, ; and for the other parallelism primitives, the solution may be for the closure ; to bind *ld-level* to the value inherited from each thread's parent. As of ; this writing (1/13/2012), we can see the unfortunate need for control-c in ; the following example: ; (defun f (x) (declare (xargs :guard (integerp x))) (+ x x)) ; (defun g () ; (declare (xargs :guard t :verify-guards nil)) ; (plet ((a (f (car (make-list 1000000)))) ; (b (f (car (make-list 1000000))))) ; (+ a b))) ; (g) (defdoc error-triples-and-parallelism ":Doc-Section Parallel-programming how to avoid error triples in ACL2(p)~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ACL2 supports the use of error triples in many features; e.g., ~ilc[computed-hints]. (For background on error triples, ~pl[programming-with-state].) However, ACL2(p) does not support the use of error triples in some of these features (e.g., ~ilc[computed-hints]) while ~il[waterfall-parallelism] is enabled.~/ You may see an error message like the following when running ACL2(p) with ~il[waterfall-parallelism] enabled: ~bv[] ACL2 Error in ( THM ...): Since we are translating a form in ACL2(p) intended to be executed with waterfall parallelism enabled, the form (MY-STATE-MODIFYING-COMPUTED-HINT ID STATE) was expected to represent an ordinary value, not an error triple (mv erp val state), as would be acceptable in a serial execution of ACL2. Therefore, the form returning a tuple of the form (* * STATE) is an error. See :DOC unsupported- waterfall-parallelism-features and :DOC error-triples-and-parallelism for further explanation. ~ev[] In this particular example, the cause of the error was trying to use a computed hint that returned state, which is not allowed when executing the waterfall in parallel (~pl[unsupported-waterfall-parallelism-features] for other related information). Often, the only reason users need to return state is so they can perform some output during the proof process. In this case, we suggest using one of the state-free output functions, like ~ilc[cw] or ~ilc[observation-cw]. If the user is concerned about the interleaving of their output with other output, these calls can be surrounded with the macro ~ilc[with-output-lock]. Another frequent reason users return state is so they can cause a ~c[soft] error and halt the proof process. In this case, we suggest instead calling ~ilc[er] with the ~c[hard] or ~c[hard?] severity. By using these mechanisms, the user avoids modifying ~ilc[state], a requirement for much of the code written in ACL2(p). You may encounter other similar error messages when using ~il[computed-hints], ~il[custom-keyword-hints], or ~il[override-hints]. Chances are that you are somehow returning an error triple when an ordinary value is needed. If this turns out not to be the case, please let the ACL2 implementors know.~/") (defdoc with-output-lock ; Note: If you're looking for the definition of with-output-lock, you can find ; it as (deflock *output-lock*) in axioms.lisp. ":Doc-Section Parallel-programming provides a mutual-exclusion mechanism for performing output in parallel~/ This documentation topic relates to an experimental extension of ACL2, ACL2(p), created initially by David L. Rager. ~l[compiling-acl2p] for how to build an executable image that supports parallel execution. Also see community books directory ~c[books/parallel/] for examples.~/ One may wish to perform output while executing code in parallel. If threads are allowed to print concurrently, the output will be interleaved and often unreadable. To avoid this, the user can surround forms that perform output with the ~c[with-output-lock] macro. Take the following definition of ~c[pfib] as an example. ~bv[] (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((mbe :logic (or (zp x) (<= x 0)) :exec (<= x 0)) 0) ((= x 1) 1) (t (plet (declare (granularity t)) ((a (prog2$ (cw \"Computing pfib ~~x0~~%\" (- x 1)) (pfib (- x 1)))) (b (prog2$ (cw \"Computing pfib ~~x0~~%\" (- x 2)) (pfib (- x 2))))) (+ a b))))) ~ev[] With ~il[parallel-execution] enabled, a call of ~c[(pfib 5)]results in non-deterministically interleaved output, for example as follows. ~bv[] ACL2 !>(pfib 5) CComputing pfib 4 omputing pfib 3 ComCpuotmipnugt ipnfgi bp fib3 2 Computing pCfiobm put2i ng pfib 1 Computing pfib Co1mp uting pfib 0 CCoommppuuttiinngg ppffiibb 12 ComCpuotmipnugt ipnfgi bp fib1 0 CoCmopmuptuitnign gp fpifbi b 1 0 5 ACL2 !> ~ev[] If the user instead surrounds the calls to ~ilc[cw] with the macro ~c[with-output-lock], as in the following session, the output will no longer be interleaved. ~bv[] ACL2 !> (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((mbe :logic (or (zp x) (<= x 0)) :exec (<= x 0)) 0) ((= x 1) 1) (t (plet (declare (granularity t)) ((a (prog2$ (with-output-lock (cw \"Computing pfib ~~x0~~%\" (- x 1))) (pfib (- x 1)))) (b (prog2$ (with-output-lock (cw \"Computing pfib ~~x0~~%\" (- x 2))) (pfib (- x 2))))) (+ a b))))) ACL2 !>(pfib 5) Computing pfib 4 Computing pfib 3 Computing pfib 3 Computing pfib 2 Computing pfib 2 Computing pfib 1 Computing pfib 2 Computing pfib 1 Computing pfib 1 Computing pfib 0 Computing pfib 1 Computing pfib 0 Computing pfib 1 Computing pfib 0 5 ACL2 !> ~ev[] ~/") (defdoc acl2p-key-checkpoints ":Doc-Section Parallel-proof key checkpoints in ACL2(p)~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. For printing output, the parallel version of the waterfall follows the precedent of ~ilc[gag-mode]. The idea behind gag mode is to print only the subgoals most relevant to debugging a failed proof attempt. These subgoals are called 'key checkpoints' (~pl[set-gag-mode] for the definition of ``key'' and ``checkpoint''), and we restrict the default output mode for the parallel version of the waterfall to printing checkpoints similar to these key checkpoints.~/ As of this writing, we are aware of exactly one discrepancy between gag mode's key checkpoints and the parallel version of the waterfall's checkpoints. This discrepancy occurs when using ``by'' hints (~pl[hints]). As an example, take the following form, which attempts to prove a non-theorem: ~bv[] (thm (equal (append x y z) (append z (append y x))) :hints ((\"Subgoal *1/2'''\" :by nil))) ~ev[] With waterfall parallelism enabled, ~c[Subgoal *1/2''] will be printed as a key checkpoint. This is different from using ~ilc[gag-mode] while running the serial version of the waterfall, which skips printing the subgoal as a checkpoint. For those familiar with the ACL2 waterfall, we note that that the parallel version of the waterfall prints key checkpoints that are unproved in the following sense: a subgoal is a key checkpoint if it leads, in the current call of the waterfall, to a goal that is pushed for induction.~/ :cite set-waterfall-printing") ; Parallelism wart: it is still possible in ACL2(p) to receive an error at the ; Lisp-level when CCL cannot "create thread". An example of a user (Kaufmann) ; encountering this error is shown below, with community book ; concurrent-programs/bakery/stutter2. In March 2012, Kaufmann's laptop could ; sometimes exhibit this problem (a 2-core machine with 4 hardware threads). ; There are two possible ways to fix this problem. The first is to set the ; default-total-parallelism-work-limit to a lower number so that it never ; occurs (but this costs performance). Kaufmann suggests that we should also ; catch this particular Lisp error and instead cause an ACL2 error, similar to ; the error in function not-too-many-futures-already-in-existence. This may be ; harder than one might intially think, because our current mechanism for ; catching errors in child threads involves catching thrown tags and then ; rethrowing them in the thread who is that child's parent. ; The error looks like the following: ;; ;; ;;............................................................. ;; *********************************************** ;; ************ ABORTING from raw Lisp *********** ;; Error: .Can't create thread ;; *********************************************** ;; The message above might explain the error. If not, and ;; if you didn't cause an explicit interrupt (Control-C), ;; then the root cause may be call of a :program mode ;; function that has the wrong guard specified, or even no ;; guard specified (i.e., an implicit guard of t). ;; See :DOC guards. ;; To enable breaks into the debugger (also see :DOC acl2-customization): ;; (SET-DEBUGGER-ENABLE T) ;; . ;; *********************************************** ;; ************ ABORTING from raw Lisp *********** ;; Error: Can't create thread ;; *********************************************** ;; The message above might explain the error. If not, and ;; if you didn't cause an explicit interrupt (Control-C), ;; then the root cause may be call of a :program mode ;; function that has the wrong guard specified, or even no ;; guard specified (i.e., an implicit guard of t). ;; See :DOC guards. ;; To enable breaks into the debugger (also see :DOC acl2-customization): ;; (SET-DEBUGGER-ENABLE T) ;; ........................................................... ;; ..................Here is the current pstack [see :DOC pstack]: ;; (CLAUSIFY REWRITE-ATM ;; SIMPLIFY-CLAUSE SIMPLIFY-CLAUSE ;; REWRITE-ATM SIMPLIFY-CLAUSE REWRITE-ATM ;; PREPROCESS-CLAUSE PREPROCESS-CLAUSE ;; SETUP-SIMPLIFY-CLAUSE-POT-LST ;; SIMPLIFY-CLAUSE ;; EV-FNCALL-META REWRITE-ATM ;; EV-FNCALL-META EV-FNCALL-META ;; EV-FNCALL-META REWRITE-ATM ;; EV-FNCALL EV-FNCALL EV-FNCALL-META ;; REWRITE-ATM REWRITE-ATM SIMPLIFY-CLAUSE ;; FORWARD-CHAIN1 SIMPLIFY-CLAUSE ;; PREPROCESS-CLAUSE EV-FNCALL-META ;; REWRITE-ATM REWRITE-ATM REWRITE-ATM ;; FORWARD-CHAIN1 FORWARD-CHAIN1 ;; SIMPLIFY-CLAUSE SIMPLIFY-CLAUSE ;; SIMPLIFY-CLAUSE PREPROCESS-CLAUSE ;; SIMPLIFY-CLAUSE SIMPLIFY-CLAUSE ;; SIMPLIFY-CLAUSE SIMPLIFY-CLAUSE ;; REWRITE-ATM PREPROCESS-CLAUSE ;; SIMPLIFY-CLAUSE PREPROCESS-CLAUSE ;; SIMPLIFY-CLAUSE PREPROCESS-CLAUSE ;; ;; (defun set-total-parallelism-work-limit-fn (val state) (declare (xargs :guard (or (equal val :none) (integerp val)))) (f-put-global 'total-parallelism-work-limit val state)) (defmacro set-total-parallelism-work-limit (val) ":Doc-Section switches-parameters-and-modes for ACL2(p): set thread limit for parallelism primitives~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. While the most common use of the limit described below is in parallel proof (~pl[parallel-proof]), it also applies to all parallelism primitives (~pl[parallel-programming]) except ~ilc[spec-mv-let] ~-[] though we expect that rather few programming applications will encouter this limit. ~bv[] General Forms: (set-total-parallelism-work-limit :none) ; disable the limit (set-total-parallelism-work-limit ) ; set limit to ~ev[] ~l[parallelism-tutorial], Section ``Another Granularity Issue Related to Thread Limitations'', for an explanation of how the host Lisp can run out of threads. Also ~pl[set-total-parallelism-work-limit-error].~/ If the underlying runtime system (the Operating System, the host Lisp, etc.) is unable to provide enough threads to finish executing the parallelism work given to it, the runtime system can crash in a very unpleasant manner (ranging from a Lisp error to completely freezing the machine). To avoid this unpleasantness, ACL2(p) will attempt to avoid creating so much parallelism that the runtime system crashes. ACL2 initially uses a conservative estimate to limit the number of threads. To tell ACL2(p) to use a different limit, call ~c[set-total-parallelism-work-limit] with the new limit. For example, if the current default limit is 10,000, then to allow 13,000 threads to exist, issue the following form at the top level. ~bv[] (set-total-parallelism-work-limit 13000) ~ev[] To disable this limit altogether, evaluate the following form: ~bv[] (set-total-parallelism-work-limit :none) ~ev[] The default value of total-parallelism-work-limit can be found by calling function ~c[default-total-parallelism-work-limit]. If the default value is too high for your system please notify the ACL2 maintainers with a limit that does work for your system, as they might then lower the default limit.~/ :cited-by parallel-proof :cited-by parallel-programming" (declare (xargs :guard (or (equal val :none) (integerp val)))) `(set-total-parallelism-work-limit-fn ,val state)) (defun set-total-parallelism-work-limit-error-fn (val state) (declare (xargs :guard (or (equal val t) (null val)))) (f-put-global 'total-parallelism-work-limit-error val state)) (defmacro set-total-parallelism-work-limit-error (val) ; Parallelism blemish: explain something about how, unlike ; set-total-parallelism-work-limit, this one only applies to proof (not ; programming). ":Doc-Section switches-parameters-and-modes for ACL2(p): control the action taken when the thread limit is exceeded~/ This ~il[documentation] topic relates to the experimental extension of ACL2 supporting parallel execution and proof; ~pl[parallelism]. ~bv[] General Forms: (set-total-parallelism-work-limit-error t) ; cause error (default) (set-total-parallelism-work-limit-error nil) ; disable error and ; continue serially ~ev[] ~l[parallelism-tutorial], Section ``Another Granularity Issue Related to Thread Limitations'', for an explanation of how the host Lisp can run out of threads. ~l[set-total-parallelism-work-limit] for further details, including an explanation of how to manage the limit that triggers this error.~/ The value of state global ~c[total-parallelism-work-limit-error] dictates what occurs when the underlying runtime system runs reaches a limit on the number of threads for parallel computation. By default, when this limit is reached, the ACL2(p) user will receive an error and computation will halt. At this point, the ACL2(p) user has the following options. (1) Remove the limit by evaluating the following form. ~bv[] (set-total-parallelism-work-limit :none) ~ev[] (2) Disable the error so that execution continues serially once the available thread resources have been exhausted. ~bv[] (set-total-parallelism-work-limit-error nil) ~ev[] (3) Increase the limit on number of threads that ACL2(p) is willing to create, in spite of potential risk (~pl[set-total-parallelism-work-limit]). In this case, the following query returns the current limit. ~bv[] (f-get-global 'total-parallelism-work-limit) ~ev[] Then to increase that limit, evaluate the following form: ~bv[] (set-total-parallelism-work-limit ) ~ev[] For example, suppose that the value of ~c[total-parallelism-work-limit] was originally 10,000. Then evaluation of the following form increases that limit to 13,000. ~bv[] (set-total-parallelism-work-limit 13000) ~ev[] ~/ :cited-by parallel-proof" (declare (xargs :guard (or (equal val t) (null val)))) `(set-total-parallelism-work-limit-error-fn ,val state)) acl2-sources/parallel-raw.lisp0000666002132200015000000016625012222115527016042 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; We thank David L. Rager for contributing an initial version of this file. ; This file is divided into the following sections. ; Section: Parallelism Basis ; Section: Work Consumer Code ; Section: Work Producer Code ; Section: Parallelism Primitives ; In particular, see the Essay on Parallelism Definitions and the Essay on ; Parallelism Strategy for overviews on this implementation of parallel ; evaluation. (in-package "ACL2") ;--------------------------------------------------------------------- ; Section: Parallelism Basis ; In this section we outline definitions and strategies for parallel evaluation ; and define constants, structures, variables, and other basic parallelism ; infrastructure. ; Essay on Parallelism Definitions ; Core ; ; A core is a unit inside a computer that can do useful work. It has its own ; instruction pointers and usually accesses shared memory. In the old days, we ; had "dual processors." This is an example of a two core system. A ; 2006-vintage example of a four core system is "dual sockets" with "dual core ; technology." ; Process ; ; We generally use the term "process" as a verb, meaning: run a set of ; instructions. For example, the system can process a closure. ; Thread ; ; We use the OS definition of a thread as a lightweight process that shares ; memory with other threads in the same process. A thread in our system is in ; one of the following three states. ; ; 1. Idle - The thread is waiting until both a piece of work (see below) and ; a core are available. ; ; 2. Active - The thread has been allocated a core and is processing some ; work. ; ; 3. Pending - This state occurs iff the thread in this state is associated ; with a parent piece of work, and it is waiting for the children of that ; piece of work to complete and for sufficient CPU core resources. A thread ; in this state is often waiting on a signaling mechanism. ; Closure ; ; We use the term "closure" in the Lisp sense: a function that captures values ; of variables from the lexical environment in which it is formed. A closure ; thus contains enough information to be applied to a list of arguments. We ; create closures in the process of saving work to be performed. ; Work ; ; A piece of work contains all the data necessary for a worker thread to ; process one closure, save its result somewhere that a parent can read it, and ; communicate that it is finished. It also contains some data necessary to ; implement features like the early termination of parallel and/or. Comments ; at parallelism-piece give implementation details. ; Roughly, work can be in any of four states: unassigned, starting, pending, or ; resumed. A piece of work will be processed by a single worker thread (not ; including, of course, child work, which will be processed by other worker ; threads). When a core becomes available, a thread can grab an unassigned ; piece of work, at which time the thread and the piece of work leave their ; initial states together. From that point forward until the piece of work is ; complete, the piece of work and its associated worker thread are considered ; to be in corresponding states (active/started,resumed or pending). Initially ; they are in their active/started states. Later, if child work is created, ; then at that time the thread and its associated piece of work both enter the ; pending state. When all child work terminates and either a CPU core becomes ; available or a heuristic allows an exception to that requirement, the piece ; of work enteres the resumed state and its associated worker thread re-enters ; the active state. This heuristic (implemented in ; wait-for-resumptive-parallelism-resources) gives priority to such resumptions ; over starting new pieces of work. ; Parallelism Primitive ; ; A macro that enables the user to introduce parallelism into a computation: ; one of plet, pargs, pand, and por. ; End of Essay on Parallelism Definitions ; Essay on Parallelism Strategy ; Whenever a parallelism primitive is used, the following steps occur. The ; text between the < and > describes the state after the previous step ; finishes. ; 1. If there is a granularity form, the form is evaluated. If the form ; returns nil, the parallelism primitive expands to the serial equivalent; ; otherwise we continue. ; < granularity form has returned true or was omitted - the system was given a ; "large" amount of work > ; 2. If we heuristically determine that the system is already overwhelmed with ; too much work (see parallelism-resources-available for details), then the ; primitive expands to its serial equivalent; otherwise we continue. ; 3. Create closures for each primitive's arguments, as follows. ; - Plet: one closure for each form assigned to a bound variable ; - Pargs: one closure for each argument to the function call ; - Pand/Por: one closure for each argument ; < have closures in memory representing computation to parallelize > ; 4. Create the data structures for pieces of work that worker threads are to ; process. One such data structure (documented in *work-queue* below) is ; created for each computation to be spawned. Among the fields of each ; such structure is a closure that represents that computation. Siblings ; have data structures that share some fields, such as a result-array that ; is to contain the values returned by the sibling computations. ; ; The system then adds these pieces of work to the global *work-queue* for ; worker threads to pop off the queue and process. ; ; Note that Step 2 avoids creating undesirably many pieces of work. ; (Actually the heuristics used in Step 2 don't provide exact guarantees, ; since two computations that reach Step 2 simultaneously might both ; receive the go-ahead even though together, they create work that exceeds ; the heuristic work limit). ; < now have unassigned work in the work-queue > ; 5. After the parent thread adds the work to the queue, it will check to see ; if more worker threads are needed and spawn them if necessary. Note ; however that if there are more threads than cores, then any newly spawned ; thread will wait on a semaphore, and only begins evaluating the work when ; a core becomes available. Each core is assigned to at most one thread at ; any time (but if this decision is revisited, then it should be documented ; here and in the Parallelism Variables section. Note that this decision ; is implemented by setting *idle-core-count* to (1- *core-count*) in ; reset-parallelism-variables). ; Note that by limiting the amount of work in the system at Step 2, we ; avoid creating more threads than the system can handle. ; < now have enough worker threads to process the work > ; 6. The parent thread waits for its children to signal their completion. It ; is crucial for efficiency that this waiting be implemented using a ; signaling mechanism rather than as busy waiting. ; < the parent is waiting for the worker threads to process the work > ; 7. At this point, the child threads begin processing the work on the queue. ; As they are allocated resources, they each pull off a piece of work from ; the work queue and save their results in the associated result-array. ; After a child thread finishes a piece of work, it will check to see if ; its siblings' computations are still necessary. If not, the child will ; remove these computations from the work queue and interrupt each of its ; running sibling threads with a primitive that supplies a function for ; that thread to execute. This function throws to the tag ; :result-no-longer-needed, causing the interrupted sibling to abort ; evaluation of that piece of work, signal the parent (in an ; unwind-protect's cleanup form on the way out to the catch), catch that ; tag, and finally reenter the stalled state (where the controlling loop ; will find it something new to do). We take care to guarantee that this ; mechanism works even if a child receives more than one interrupt. Note ; that when a child is interrupted in this manner, the value stored for the ; child is a don't-care. ; < all of the children are done computing, the required results are in the ; results-array, and the parent has been signaled a number of times equal to ; the number of children > ; 8. The parent thread (from steps 1-6) resumes. It finds the results stored ; in its results array. If the primitive is a: ; - Plet: it executes the body of the plet with the calculated bindings ; - Pargs: it applies the called function to the calculated arguments ; - Pand, Por: it applies a functionalized "and" or "or" to the calculated ; arguments. The result is Booleanized. ; End of Essay on Parallelism Strategy ; Parallelism Structures ; If the shape of parallelism-piece changes, update the *work-queue* ; documentation in the section "Parallelism Variables." (defstruct parallelism-piece ; piece of work ; A data item in the work queue has the following contents, and we often call ; each a "piece of work." ; thread-array - the array that holds the threads spawned for that closure's ; particular parent ; result-array - the array that holds the results for that closure's particular ; parent, where each value is either nil (no result yet) or a cons whose cdr is ; the result ; array-index - the index into the above two arrays for this particular closure ; semaphore-to-signal-as-last-act - the semaphore to signal right before the ; spawned thread dies ; closure - the closure to process by spawning a thread ; throw-siblings-when-function - the function to funcall on the current ; thread's result to see if its siblings should be terminated. The function ; will also remove work from the work-queue and throw the siblings if ; termination should occur. (thread-array nil) (result-array nil) (array-index -1) (semaphore-to-signal-as-last-act nil) (closure nil) (throw-siblings-when-function nil)) ; Parallelism Variables (progn ; Keep this progn in sync with reset-parallelism-variables, which resets the ; variables defined here. Note that none of the variables are initialized ; here, so reset-parallelism-variables must be called before evaluating ; parallelism primitives (an exception is *throwable-worker-thread* since it is ; first called in reset-parallelism-variables). ; *Idle-thread-count* is updated both when a thread is created and right before ; it expires. It is also updated when a worker thread gets some work to do and ; after it is done with that work. (define-atomically-modifiable-counter *idle-thread-count* 0) ; *Idle-core-count* is only used to estimate resource availability. The number ; itself is always kept accurate using atomic writes. Since atomic increments ; also stall reads, the value read is no longer only an estimate. But since we ; don't perform the action associated with a test of the read result while ; holding a lock, it's as if the number read is just an estimate. It defaults ; to (1- *core-count*), because the current thread is considered active. ; There are two pairs of places that *idle-core-count* is updated. First, ; whenever a worker thread begins processing work, the count is decremented. ; This decrement is paired with the increment that occurs after a worker thread ; finishes work. It is also incremented and decremented in ; eval-and-save-result, before and after a parent waits for its children. ; Note: At different stages of development we have contemplated having a ; "*virtual-core-count*", exceeding the number of CPU cores, that bounds the ; number of active threads. Since our initial tests did not show a performance ; improvement by using this trick, we have not employed a *virtual-core-count*. ; If we later do employ this trick, the documentation in step 5 of the Essay on ; Parallelism Strategy will need to be updated. (define-atomically-modifiable-counter *idle-core-count* 0) ; *Unassigned-and-active-work-count* tracks the amount of parallelism work in ; the system, other than pending work. It is increased when a parallelism ; primitive adds work to the system. This increase is paired with the final ; decrease in consume-work-on-work-queue-when-there, which occurs when a ; piece of work finishes. It is decremented and incremented (respectively) ; when a parent waits on children and when it resumes after waiting on ; children. (define-atomically-modifiable-counter *unassigned-and-active-work-count* 0) ; *Total-work-count* tracks the total amount of parallelism work. This ; includes unassigned, started, pending, and resumed work. (define-atomically-modifiable-counter *total-work-count* 0) ; We maintain a queue of work to process. See parallelism-piece for ; documentation on pieces of work. Even though *work-queue* is a list, we ; think of it as a structure that can be destructively modified -- so beware ; sharing any structure with *work-queue*! (defvar *work-queue*) (deflock *work-queue-lock*) ; An idle thread waits for the signaling mechanism ; *check-work-and-core-availability* to be signaled, at which time it looks for ; work on the *work-queue* and an idle core to use. This condition can be ; signaled by the addition of new work or by the availabilty of a CPU core. ; Warning: In the former case, a parent thread must always signal this ; semaphore *after* it has already added the work to the queue. Otherwise, a ; child can attempt to acquire work, fail, and then go wait on the signaling ; mechanism again. Since the parent has already signaled, there is no ; guarantee that the work they place on the queue will ever be processed. (The ; latter case also requires analogous care.) ; Why are there two distinct signaling mechanisms, one for idle threads and one ; for resuming threads? Suppose that idle and resuming threads waited on the ; same mechanism. We would then have no guarantee that resuming threads would ; be signaled before the idle threads (which is necessary to establish the ; priority explained in wait-for-resumptive-parallelism-resources). Using ; separate signaling mechanisms allows both an idle and resuming thread to be ; signaled. Then whichever thread's heuristics allow it to execute will claim ; access to the CPU core. There is no problem if both their heuritistics allow ; them to continue. ; We omit the suffix "sem" from the following two variable names, because we do ; not want to think about the counter that resides inside semaphores. Our ; intent is only to use them as a lockless signaling mechanism. (defvar *check-work-and-core-availability*) (defvar *check-core-availability-for-resuming*) ; *total-parallelism-piece-historical-count* tracks the total number of pieces ; of parallelism work processed over the lifetime of the ACL2 session. It is ; reset whenever the parallelism variables are reset. It is only used for ; informational purposes, and the system does not depend on its accuracy in any ; way. We therefore perform no locking/synchronization when modifying its ; value. (defvar *total-parallelism-piece-historical-count*) ) ; end of parallelism variables ; Following are definitions of functions that help us restore the ; parallelism system to a stable state after an interrupt occurs. (defparameter *reset-parallelism-variables* nil) (defparameter *reset-core-count-too* ; This variable has a relatively unsophisticated use: When Rager runs his ; dissertation performance test scripts, sometimes he adjusts the number of ; cpu-cores to be a factor of the actual cpu-core count. In this case we are ; just testing, and, to avoid resetting the core count variable every time we ; reset the parallelism system, we will want to set this variable to nil. t) (defun reset-parallelism-variables () ; We use this function (a) to kill all worker threads, (b) to reset "most" of ; the parallelism variables, and (c) to reset the lock and semaphore recycling ; systems. Keep (b) in sync with the progn above that declares the variables ; reset here, in the sense that this function assigns values to exactly those ; variables. ; If a user kills threads directly from raw Lisp, for example using functions ; above, then they should call reset-parallelism-variables. Note that ; reset-parallelism-variables is called automatically on any top-level call of ; LD (i.e., a call with *ld-level* = 0), as well as any time we return to the ; prompt after entering a raw Lisp break (using ; *reset-parallelism-variables*). ; This function is not to be confused with reset-future-parallelism-variables ; (although it is similar in nature). ; (a) Kill all worker threads. (send-die-to-worker-threads) ; (b) Reset "most" of the parallelism variables. (when *reset-core-count-too* ; We reset *core-count* and related variable(s) in case the current platform ; has a different number of CPU cores than the compilation platform had. (setf *core-count* (core-count-raw)) (setf *unassigned-and-active-work-count-limit* (* 4 *core-count*))) (setf *idle-thread-count* (make-atomically-modifiable-counter 0)) (setf *idle-core-count* (make-atomically-modifiable-counter (1- *core-count*))) (setf *unassigned-and-active-work-count* (make-atomically-modifiable-counter 1)) (setf *total-work-count* (make-atomically-modifiable-counter 1)) (setf *work-queue* nil) (reset-lock *work-queue-lock*) (setf *check-work-and-core-availability* (make-semaphore)) (setf *check-core-availability-for-resuming* (make-semaphore)) (setf *throwable-worker-thread* nil) (setf *total-parallelism-piece-historical-count* 0) (setf *reset-parallelism-variables* nil) t ; return t ) ;--------------------------------------------------------------------- ; Section: Work Consumer Code ; We develop functions that assign threads to process work. (defun eval-and-save-result (work) ; Work is a piece of parallelism work. Among its fields are a closure and an ; array. We evaluate this closure and save the result into this array. No ; lock is required because no other thread will be writing to the same position ; in the array. ; Keep this in sync with the comment in parallelism-piece, where we explain ; that the result is the cdr of the cons stored in the result array at the ; appropriate position. (assert work) (let ((result-array (parallelism-piece-result-array work)) (array-index (parallelism-piece-array-index work)) (closure (parallelism-piece-closure work))) (setf (aref result-array array-index) (cons t (funcall closure))))) (defun pop-work-and-set-thread () ; Once we exit the without-interrupts that must enclose a call to ; pop-work-and-set-thread, our siblings can interrupt us so that we execute a ; throw to the tag :result-no-longer-needed. The reason they can access us is ; that they will have a pointer to us in the thread array. ; There is a race condition between when work is popped from the *work-queue* ; and when the current thread is stored in the thread-array. This race ; condition could be eliminated by holding *work-queue-lock* during the ; function's entire execution. Since (1) we want to minimize the duration ; locks are held, (2) the chance of this race condition occuring is small and ; (3) there is no safety penalty when this race condition occurs (instead an ; opportunity for early termination is missed), we only hold the lock for the ; amount of time it takes to read and modify the *work-queue*. (let ((work (with-lock *work-queue-lock* (when (consp *work-queue*) (pop *work-queue*)))) (thread (current-thread))) (when work (assert thread) (assert (parallelism-piece-thread-array work)) ; Record that the current thread is the one assigned to do this piece of work: (setf (aref (parallelism-piece-thread-array work) (parallelism-piece-array-index work)) thread)) work)) (defun consume-work-on-work-queue-when-there () ; This function is an infinite loop. However, the thread running it can be ; waiting on a condition variable and will expire if it waits too long. ; Each iteration through the main loop will start by trying to grab a piece of ; work to process. When it succeeds, then it will process that piece of work ; and wait again on a condition variable before starting the next iteration. ; But ideally, if it has to wait too long for a piece of work to grab then we ; return from this function (with expiration of the current thread); see below. (catch :worker-thread-no-longer-needed (let ((*throwable-worker-thread* t) ; If #+hons is set, we must bind *default-hs* to NIL so that each thread will ; get its own hons space whenever it uses honsing code. We could alternately ; call (hl-hspace-init) here, but using NIL allows us to avoid the overhead of ; initializing a hons space unless honsing is used in this thread. See also ; the notes in hons-raw.lisp. #+hons (*default-hs* nil)) #+hons (declare (ignorable *default-hs*)) ; not yet used (loop ; "forever" - really until :worker-thread-no-longer-needed thrown ; Wait until there are both a piece of work and an idle core. In CCL, if ; the thread waits too long, it throws to the catch above and returns from this ; function. (loop while (not (and *work-queue* (< 0 (atomically-modifiable-counter-read *idle-core-count*)))) ; We can't grab work yet, so we wait until somebody signals us to try again, by ; returning a non-nil value to the call of not, just below. If however nobody ; signals us then ideally (and in CCL but not SBCL) a timeout occurs that ; returns nil to this call of not, so we give up with a throw. do (when (not (wait-on-semaphore *check-work-and-core-availability* :timeout 15)) (throw :worker-thread-no-longer-needed nil))) ; Now very likely there are both a piece of work and an idle core to process ; it. But a race condition allows either of these to disappear before we can ; claim a piece of work and a CPU core, which explains the use of `when' ; below. (unwind-protect-disable-interrupts-during-cleanup (when (<= 0 ; allocate CPU core ; We will do a corresponding increment of *idle-core-count* in the cleanup form ; of this unwind-protect. Note that the current thread cannot be interrupted ; (except by direct user intervention, for which we may provide only minimal ; protection) until the call of pop-work-and-set-thread below (see long comment ; above that call), because no other thread has a pointer to this one until ; that time. (atomic-decf *idle-core-count*)) (catch :result-no-longer-needed (let ((work nil)) (unwind-protect-disable-interrupts-during-cleanup (progn (without-interrupts (setq work ; The following call has the side effect of putting the current thread into a ; thread array, such that this presence allows the current thread to be ; interrupted by another (via interrupt-thread, in throw-threads-in-array). So ; until this point, the current thread will not be told to do a throw. ; We rely on the following claim: If any state has been changed by this call of ; pop-work-and-set-thread, then that call completes and work is set to a ; non-nil value. This claim guarantees that if any state has been changed, ; then the cleanup form just below will be executed and will clean up properly. ; For example, we would have a problem if pop-work-and-set-thread were ; interrupted after the decrement of *idle-thread-count*, but before work is ; set, since then the matching increment in the cleanup form below would be ; skipped. For another example, if we complete the call of ; pop-work-and-set-thread but not the enclosing setq for work, then we miss the ; semaphore signaling in the cleanup form below. (pop-work-and-set-thread)) (when work (atomic-decf *idle-thread-count*))) (when work ; The consumer now has a core (see the <= test above) and a piece of work. (eval-and-save-result work) (let* ((thread-array (parallelism-piece-thread-array work)) (result-array (parallelism-piece-result-array work)) (array-index (parallelism-piece-array-index work)) (throw-siblings-when-function (parallelism-piece-throw-siblings-when-function work))) (setf (aref thread-array array-index) nil) ; The nil setting just above guarantees that the current thread doesn't ; interrupt itself by way of the early termination function. (when throw-siblings-when-function (funcall throw-siblings-when-function (aref result-array array-index)))))) (when work ; process this cleanup form if we acquired work (let* ((semaphore-to-signal-as-last-act (parallelism-piece-semaphore-to-signal-as-last-act work)) (thread-array (parallelism-piece-thread-array work)) (array-index (parallelism-piece-array-index work))) ; We don't use a thread-safe increment because we don't care if it's off by a ; few. The variable is just used for debugging. (incf *total-parallelism-piece-historical-count*) (setf (aref thread-array array-index) nil) ; Above we argued that if *idle-thread-count* is decremented, then work is set ; and hence we get to this point so that we can do the corresponding ; increment. In the other direction, if we get here, then how do we know that ; *idle-thread-count* was decremented? We know because if we get here, then ; work is non-nil and hence pop-work-and-set-thread must have completed. (atomic-incf *idle-thread-count*) ; Each of the following two decrements undoes the corresponding increment done ; when the piece of work was first created and queued. (atomic-decf *total-work-count*) (atomic-decf *unassigned-and-active-work-count*) (assert (semaphorep semaphore-to-signal-as-last-act)) (signal-semaphore semaphore-to-signal-as-last-act))))) ) ; end catch :result-no-longer-needed ) ; end when CPU core allocation (atomic-incf *idle-core-count*) (signal-semaphore *check-work-and-core-availability*) (signal-semaphore *check-core-availability-for-resuming*)))) ) ; end catch :worker-thread-no-longer-needed ; The current thread is about to expire because all it was given to do was to ; run this function. (atomic-decf *idle-thread-count*)) (defun spawn-worker-threads-if-needed () ; This function must be called with interrupts disabled. Otherwise it is ; possible for the *idle-thread-count* to be incremented even though no new ; worker thread is spawned. (loop while (< (atomically-modifiable-counter-read *idle-thread-count*) *max-idle-thread-count*) ; Note that the above test could be true, yet *idle-thread-count* could be ; incremented before we get to the lock just below. But we want as little ; bottleneck as possible for scaling later, and the practical worst consequence is ; that we spawn extra threads here. ; Another possibility is that we spawn too few threads here, because the final ; decrement of *idle-thread-count* in consume-work-on-work-queue-when-there ; has not occurred even though a worker thread has decided to expire. If this ; occurs, then we may not have the expected allotment of idle threads for ; awhile, but we expect the other idle threads (if any) and the active threads ; to suffice. Eventually a new parallelism primitive call will invoke this ; function again, at a time when the about-to-expire threads have already ; updated *idle-thread-count*, which will allow this function to create the ; expected number of threads. The chance of any of this kind of issue arising ; is probably extremely small. ; NOTE: Consider coming up with a design that's easier to understand. do (progn (atomic-incf *idle-thread-count*) ;(format t "param parent thread ~a: ~s~%" (current-thread) acl2::*param*) (run-thread "Worker thread" 'consume-work-on-work-queue-when-there)))) ;--------------------------------------------------------------------- ; Section: Work Producer Code ; We develop functions that create work, to be later processed by threads. Our ; main concern is to keep the work queue sufficiently populated so as to keep ; CPU cores busy, while limiting the total amount of work so that the number of ; threads necessary to evaluate that work does not execede the number of ; threads that the underlying Lisp supports creating. (See also comments in ; default-total-parallelism-work-limit.) (defun add-work-list-to-queue (work-list) ; Call this function inside without-interrupts, in order to maintain the ; invariant that when this function exits, the counts are accurate. ; WARNING! This function destructively modifies *work-queue*. (let ((work-list-length (length work-list))) (with-lock *work-queue-lock* ; In naive performance tests using a parallel version of Fibonacci, we found ; that (pfib 45) took about 19.35 seconds with (nconc *work-queue* work-list), ; as opposed to 19.7 seconds when we reversed the argument order. We have ; other evidence that suggests switching the argument order. But we follow ; Halstead's 1989 paper "New Ideas in Parallel Lisp: Language Design, ; Implementation, and Programming Tools", by doing the oldest work first. (setf *work-queue* (nconc *work-queue* work-list))) (atomic-incf-multiple *total-work-count* work-list-length) (atomic-incf-multiple *unassigned-and-active-work-count* work-list-length) (dotimes (i work-list-length) (signal-semaphore *check-work-and-core-availability*)))) (defun combine-array-results-into-list (result-array current-position acc) (if (< current-position 0) acc (combine-array-results-into-list result-array (1- current-position) (cons (cdr ; entry is a cons whose cdr is the result (aref result-array current-position)) acc)))) (defun remove-thread-array-from-work-queue-rec (work-queue thread-array array-positions-left) ; The function calling remove-thread-array-from-work-queue must hold the lock ; *work-queue-lock*. ; This function must be called with interrupts disabled. (cond ((eql array-positions-left 0) work-queue) ((atom work-queue) nil) ((eq thread-array (parallelism-piece-thread-array (car work-queue))) (progn (atomic-decf *total-work-count*) (atomic-decf *unassigned-and-active-work-count*) ; we must signal the parent (assert (semaphorep (parallelism-piece-semaphore-to-signal-as-last-act (car work-queue)))) (signal-semaphore (parallelism-piece-semaphore-to-signal-as-last-act (car work-queue))) (remove-thread-array-from-work-queue-rec (cdr work-queue) thread-array (1- array-positions-left)))) (t (cons (car work-queue) (remove-thread-array-from-work-queue-rec (cdr work-queue) thread-array (1- array-positions-left)))))) (defun remove-thread-array-from-work-queue (thread-array) (without-interrupts (with-lock *work-queue-lock* (setf *work-queue* (remove-thread-array-from-work-queue-rec *work-queue* thread-array (length thread-array)))))) (defun terminate-siblings (thread-array) ; This function supports early termination by eliminating further computation ; by siblings. Siblings not yet assigned a thread are removed from the work ; queue. Siblings that are already active are interrupted to throw with tag ; :result-no-longer-needed. The order of these two operations is important: if ; we do them in the other order, then we could miss a sibling that is assigned ; a thread (and removed from the work queue) just inbetween the two ; operations. (remove-thread-array-from-work-queue thread-array) (throw-threads-in-array thread-array (1- (length thread-array)))) (defun generate-work-list-from-closure-list-rec (thread-array result-array children-done-semaphore closure-list current-position &optional throw-siblings-when-function) (if (atom closure-list) (assert (equal current-position (length thread-array))) ; returns nil (cons (make-parallelism-piece :thread-array thread-array :result-array result-array :array-index current-position :semaphore-to-signal-as-last-act children-done-semaphore :closure (car closure-list) :throw-siblings-when-function throw-siblings-when-function) (generate-work-list-from-closure-list-rec thread-array result-array children-done-semaphore (cdr closure-list) (1+ current-position) throw-siblings-when-function)))) (defun generate-work-list-from-closure-list (closure-list &optional terminate-early-function) ; Given a list of closures, we need to generate a list of work data structures ; that are in a format ready for the work queue. Via mv, we also return the ; pointers to the thread, result, and semaphore arrays. (let* ((closure-count (length closure-list)) (thread-array (make-array closure-count :initial-element nil)) (result-array (make-array closure-count :initial-element nil)) (children-done-semaphore (make-semaphore))) (progn ; warning: avoid prog2 as we need to return multiple value (assert (semaphorep children-done-semaphore)) (mv (generate-work-list-from-closure-list-rec thread-array result-array children-done-semaphore closure-list 0 (if terminate-early-function (lambda (x) ; x is (t . result) (when (funcall terminate-early-function (cdr x)) (terminate-siblings thread-array))) nil)) thread-array result-array children-done-semaphore)))) (defun pargs-parallelism-buffer-has-space-available () (< (atomically-modifiable-counter-read *unassigned-and-active-work-count*) *unassigned-and-active-work-count-limit*)) (defun not-too-many-pieces-of-parallelism-work-already-in-existence () ; Parallelism no-fix: we could fix the plet, pargs, pand, and por parallel ; execution system to cause an error when this limit is exceeded. However, ; since there is no notion of ":full" parallel execution (like in the ACL2 ; waterfall) for these primitives (because these primitives only parallelize ; when resources are avaiable), such an error would be meaningless. (< (atomically-modifiable-counter-read *total-work-count*) (f-get-global 'total-parallelism-work-limit *the-live-state*))) (defun parallelism-resources-available () ; This function is our attempt to guess when resources are available. When ; this function returns true, then resources are probably available, and a ; parallelism primitive call will opt to parallelize. We say "probably" ; because correctness does not depend on our answering exactly. For ; performance, we prefer that this function is reasonably close to an accurate ; implementation that would use locks. Perhaps even more important for ; performance, however, is that we avoid the cost of locks to try to remove ; bottlenecks. ; In summary, it is unneccessary to acquire a lock, because we just don't care ; if we miss a few chances to parallelize, or parallelize a few extra times. (and (f-get-global 'parallel-execution-enabled *the-live-state*) (pargs-parallelism-buffer-has-space-available) (not-too-many-pieces-of-parallelism-work-already-in-existence))) (defun throw-threads-in-array (thread-array current-position) ; Call this function to terminate computation for every thread in the given ; thread-array from position current-position down to position 0. We expect ; that thread-array was either created by the current thread's parent or was ; created by the current thread (for its children). ; We require that the current thread not be in thread-array. This requirement ; prevents the current thread from interrupting itself, which could conceivably ; abort remaining recursive calls of this function, or cause a hang in some ; Lisps since we may be operating with interrupts disabled (for example, inside ; the cleanup form of an unwind-protect in CCL (OpenMCL 1.1pre or later)). (assert thread-array) (when (<= 0 current-position) (let ((current-thread (aref thread-array current-position))) (when current-thread (interrupt-thread current-thread ; The delayed evaluation of (aref thread-array...) below is crucial to keep a ; thread from throwing :result-no-longer-needed outside of the catch for that tag. ; Consume-work-on-work-queue-when-there will set the (aref thread-array...) ; to nil when the thread should not be thrown. (lambda () (when (aref thread-array current-position) (throw :result-no-longer-needed nil)))))) (throw-threads-in-array thread-array (1- current-position)))) (defun decrement-children-left (children-left-ptr semaphore-notification-obj) ; This function should be called with interrupts disabled. (when (semaphore-notification-status semaphore-notification-obj) (decf (aref children-left-ptr 0)) (clear-semaphore-notification-status semaphore-notification-obj))) (defun wait-for-children-to-finish (semaphore children-left-ptr semaphore-notification-obj) ; This function is called both in the normal case and in the early-termination ; case. (assert children-left-ptr) (when (<= 1 (aref children-left-ptr 0)) (assert (not (semaphore-notification-status semaphore-notification-obj))) (unwind-protect-disable-interrupts-during-cleanup (wait-on-semaphore semaphore :notification semaphore-notification-obj) (decrement-children-left children-left-ptr semaphore-notification-obj)) (wait-for-children-to-finish semaphore children-left-ptr semaphore-notification-obj))) (defun wait-for-resumptive-parallelism-resources () ; A thread resuming execution after its children finish has a higher priority ; than a thread just beginning execution. As such, resuming threads are ; allowed to "borrow" up to *core-count* CPU cores. That is implemented by ; waiting until *idle-core-count* is greater than the negation of the ; *core-count*. This is different from a thread just beginning execution, ; which waits for *idle-core-count* to be greater than 0. (loop while (<= (atomically-modifiable-counter-read *idle-core-count*) (- *core-count*)) ; So, *idle-core-count* is running a deficit that is at least the number of ; cores: there are already *core-count* additional active threads beyond the ; normal limit of *core-count*. do (wait-on-semaphore *check-core-availability-for-resuming*)) (atomic-incf *unassigned-and-active-work-count*) (atomic-decf *idle-core-count*)) (defun early-terminate-children-and-rewait (children-done-semaphore children-left-ptr semaphore-notification-obj thread-array) ; This function performs three kinds of actions. ; A. It signals children-done-semaphore once for each child that is unassigned ; (i.e. still on the work queue) and removes that child from the work queue. ; B. It interrups each assigned child's thread with a throw that terminates ; processing of its work. Note that we must do Step B after Step A: otherwise ; threads might grab work after Step B but before Step A, resulting in child ; work that is no longer available to terminate unless we call this function ; again. ; C. The above throw from Step B eventually causes the interrupted threads to ; signal children-done-semaphore. The current thread waits for those remaining ; signals. (when (< 0 (aref children-left-ptr 0)) (remove-thread-array-from-work-queue ; A ; Signal children-done-semaphore, which is in each piece of work in ; closure-list. thread-array) (throw-threads-in-array thread-array ; B (1- (length thread-array))) (wait-for-children-to-finish ; C children-done-semaphore children-left-ptr semaphore-notification-obj))) (defun prepare-to-wait-for-children () ; This function should be executed with interrupts disabled, after all child ; work is added to the work queue but before the current thread waits on such ; work to finish. ; First, since we are about to enter the pending state, we must free CPU core ; resources and notify other threads. (atomic-incf *idle-core-count*) (signal-semaphore *check-work-and-core-availability*) (signal-semaphore *check-core-availability-for-resuming*) ; Second, record that we are no longer active. (Note: We could avoid the ; following form (thus saving a lock) by incrementing ; *unassigned-and-active-work-count* by one less in add-work-list-to-queue.) (atomic-decf *unassigned-and-active-work-count*)) (defun parallelize-closure-list (closure-list &optional terminate-early-function) ; Given a list of closures, we: ; 1. Create a list of pieces of work (see defstruct parallelism-piece). ; 2. If there aren't enough idle worker threads, we spawn a reasonably ; sufficient number of new worker threads, so that CPU cores are kept busy but ; without the needless overhead of useless threads. Note that when a thread ; isn't already assigned work, it is waiting for notification to look for work ; to do. ; 3. Add the work to the work queue, which notifies the worker threads of the ; additional work. ; 4. Free parallelism resources (specifically, a CPU core), since we are about ; to become idle as we wait children to finish. Issue the proper notifications ; (via condition variables) so that other threads are aware of the freed ; resources. ; 5. Wait for the children to finish. In the event of receiving an early ; termination from our parent (a.k.a. the grandparent of our children) or our ; sibling (a.k.a. the uncle of our children), we signal our children to ; terminate early, and we wait again. ; Note that if the current thread's children decide the remaining child results ; are irrelevant, that the current thread will never know it. The children ; will terminate early amongst themselves without any parent intervention. ; 6. Resume when resources become available, reclaiming parallelism resources ; (see wait-for-resumptive-parallelism-resources). ; 7. Return the result. ; It's silly to parallelize just 1 (or no!) thing. The definitions of pargs, ; plet, pand, and por should prevent this assertion from failing, but we have ; it here as a check that this is true. (assert (and (consp closure-list) (cdr closure-list))) (let ((work-list-setup-p nil) (semaphore-notification-obj (make-semaphore-notification)) (children-left-ptr (make-array 1 :initial-element (length closure-list)))) ; 1. Create a list of pieces of work. (mv-let (work-list thread-array result-array children-done-semaphore) (generate-work-list-from-closure-list closure-list terminate-early-function) (assert (semaphorep children-done-semaphore)) (unwind-protect-disable-interrupts-during-cleanup (progn (without-interrupts ; 2. Spawn worker threads so that CPU cores are kept busy. (spawn-worker-threads-if-needed) ; 3. Add the work to the work queue. (setq work-list-setup-p (progn (add-work-list-to-queue work-list) t)) ; 4. Free parallelism resources. (prepare-to-wait-for-children)) ; 5a. Wait for children to finish. But note that we may be interrupted by our ; sibling or our parent before this wait is completed. ; Now that the two operations under the above without-interrupts are complete, ; it is once again OK to be interrupted with a function that throws to the tag ; :results-no-longer-needed. Note that wait-for-children-to-finish is called ; again in the cleanup form below, so we don't have to worry about dangling ; child threads even if we don't complete evaluation of the following form. (wait-for-children-to-finish children-done-semaphore children-left-ptr semaphore-notification-obj)) ; We are entering the cleanup form, which we always need to run (in particular, ; so that we can resume and return a result). But why must we run without ; interrupts? Suppose for example we have been interrupted (to do a throw) by ; the terminate-early-function of one of our siblings or by our parent. Then ; we must wait for all child pieces of work to terminate (see ; early-terminate-children-and-rewait) before we return. And this waiting must ; be non-abortable; otherwise, for example, we could be left (after Control-c ; and an abort) with orphaned child threads. (progn (when work-list-setup-p ; children were added to *work-queue* ; If we were thrown by a sibling or parent, it's possible that our children ; didn't finish. We now throw our children and wait for them. ; 5b. Complete processing of our children in case we were interrupted when we ; were waiting the first time. (early-terminate-children-and-rewait children-done-semaphore children-left-ptr semaphore-notification-obj thread-array) ; AS OF *HERE*, ALL OF THIS PARENT'S CHILD WORK IS "DONE" ; 6. Resume when resources become available. (wait-for-resumptive-parallelism-resources) (assert (eq (aref children-left-ptr 0) 0))))) ; 7. Return the result. (combine-array-results-into-list result-array (1- (length result-array)) nil)))) (defun parallelize-fn (parent-fun-name arg-closures &optional terminate-early-function) ; Parallelize-fn Booleanizes the results from pand/por. ; It's inefficient to parallelize just one (or no!) computation. The ; definitions of pargs, plet, pand, and por should prevent this assertion from ; failing, but we have it here as a check that this is true. (assert (cdr arg-closures)) (let ((parallelize-closures-res (parallelize-closure-list arg-closures terminate-early-function))) (if (or (equal parent-fun-name 'and-list) (equal parent-fun-name 'or-list)) (funcall parent-fun-name parallelize-closures-res) (apply parent-fun-name parallelize-closures-res)))) (defmacro closure-for-expression (x) `(function (lambda () ,x))) (defmacro closure-list-for-expression-list (x) (if (atom x) nil `(cons (closure-for-expression ,(car x)) (closure-list-for-expression-list ,(cdr x))))) ;--------------------------------------------------------------------- ; Section: Parallelism Primitives (defun parallelism-condition (gran-form-exists gran-form) (if gran-form-exists `(and (parallelism-resources-available) ; We check availability of resources before checking the granularity form, ; since the latter can be arbitrarily expensive. ,gran-form) '(parallelism-resources-available))) (defmacro pargs (&rest forms) ; This is the raw lisp version for threaded Lisps. (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (declare (ignore msg)) (assert (not erp)) (let ((function-call (car remainder-forms))) (if (null (cddr function-call)) ; whether there are two or more arguments function-call (list 'if (parallelism-condition gran-form-exists gran-form) (list 'parallelize-fn (list 'quote (car function-call)) (list 'closure-list-for-expression-list (cdr function-call))) function-call))))) (defun plet-doublets (bindings bsym n) (cond ((endp bindings) nil) (t (cons (list (caar bindings) (list 'nth n bsym)) (plet-doublets (cdr bindings) bsym (1+ n)))))) (defun make-closures (bindings) ; We return a list of forms (function (lambda () )), each of which ; evaluates to a closure, as ranges over the expression components of ; the given plet bindings. Note that this function is only called on the ; bindings of a plet expression that has passed translate -- so we know that ; bindings has the proper shape. (if (endp bindings) nil (cons `(function (lambda () ,(cadar bindings))) (make-closures (cdr bindings))))) (defun identity-list (&rest rst) rst) (defun make-list-until-non-declare (remaining-list acc) (if (not (caar-is-declarep remaining-list)) (mv (reverse acc) remaining-list) (make-list-until-non-declare (cdr remaining-list) (cons (car remaining-list) acc)))) (defun parse-additional-declare-forms-for-let (x) ; X is a list of forms from a well-formed plet, with the plet and optional ; granularity form removed. It thus starts with bindings and is followed by ; any finite number of valid declare forms, and finally a body. (mv-let (declare-forms body) (make-list-until-non-declare (cdr x) nil) (mv (car x) declare-forms body))) (defmacro plet (&rest forms) ; This is the raw Lisp version for threaded Lisps. (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (declare (ignore msg)) (assert (not erp)) (mv-let (bindings declare-forms body) (parse-additional-declare-forms-for-let remainder-forms) (cond ((null (cdr bindings)) ; at most one binding `(let ,bindings ,@declare-forms ,@body)) (t (list 'if (parallelism-condition gran-form-exists gran-form) (let ((bsym (acl2-gentemp "plet"))) `(let ((,bsym (parallelize-fn 'identity-list (list ,@(make-closures bindings))))) (let ,(plet-doublets bindings bsym 0) ,@declare-forms ,@body))) `(let ,bindings ,@declare-forms ,@body))))))) (defmacro pand (&rest forms) ; This is the raw Lisp version for threaded Lisps. (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (declare (ignore msg)) (assert (not erp)) (if (null (cdr remainder-forms)) ; whether pand has only one argument (list 'if (car remainder-forms) t nil) (let ((and-early-termination-function '(lambda (x) (null x)))) (list 'if (parallelism-condition gran-form-exists gran-form) (list 'parallelize-fn ''and-list (list 'closure-list-for-expression-list remainder-forms) and-early-termination-function) (list 'if (cons 'and remainder-forms) t nil)))))) (defmacro por (&rest forms) ; This is the raw Lisp version for threaded Lisps. (mv-let (erp msg gran-form-exists gran-form remainder-forms) (check-and-parse-for-granularity-form forms) (declare (ignore msg)) (assert (not erp)) (if (null (cdr remainder-forms)) ; whether por has one argument (list 'if (car remainder-forms) t nil) (let ((or-early-termination-function '(lambda (x) x))) (list 'if (parallelism-condition gran-form-exists gran-form) (list 'parallelize-fn ''or-list (list 'closure-list-for-expression-list remainder-forms) or-early-termination-function) (list 'if (cons 'or remainder-forms) t nil)))))) (defun signal-semaphores (sems) (cond ((endp sems) nil) (t (signal-semaphore (car sems)) (signal-semaphores (cdr sems))))) (defmacro spec-mv-let (bindings computation body) (assert (and (true-listp body) (equal (length body) 4) ; We could easily also allow "mv?-let," but we don't for now, because we don't ; have a need. With work, we could also allow "let." We don't do so for now, ; because we don't have a need, and we also don't want to a non-trivial amount ; of work. (or (equal (car body) 'mv-let@par) (equal (car body) 'mv-let) (equal (car body) 'mv?-let)))) (let* ((inner-let (car body)) (inner-bindings (cadr body)) (inner-body (caddr body)) (ite (cadddr body))) (assert (and (true-listp ite) (equal (length ite) 4) (equal (car ite) 'if))) (let* ((test (cadr ite)) (true-branch (caddr ite)) (false-branch (cadddr ite))) ; Keep variable name "the-very-obscure-feature" in sync with ; check-vars-not-free in logical definition. `(let ((the-very-obscure-feature (future ,computation))) (,inner-let ,inner-bindings ,inner-body (if ,test (mv?-let ,bindings (future-read the-very-obscure-feature) ,true-branch) (progn (future-abort the-very-obscure-feature) ,false-branch))))))) (defun number-of-active-threads-aux (threads acc) #-ccl (declare (ignore threads acc)) #-ccl 0 #+ccl (cond ((atom threads) acc) ((equal (ccl:process-whostate (car threads)) "Active") (number-of-active-threads-aux (cdr threads) (1+ acc))) (t (number-of-active-threads-aux (cdr threads) acc)))) (defun number-of-active-threads () (number-of-active-threads-aux (all-threads) 0)) (defun number-of-threads-waiting-on-a-child-aux (threads acc) #-ccl (declare (ignore threads acc)) #-ccl 0 #+ccl (cond ((atom threads) acc) ((equal (ccl:process-whostate (car threads)) "semaphore wait") (number-of-threads-waiting-on-a-child-aux (cdr threads) (1+ acc))) (t (number-of-threads-waiting-on-a-child-aux (cdr threads) acc)))) (defun number-of-threads-waiting-on-a-child () (number-of-threads-waiting-on-a-child-aux (all-threads) 0)) (defun future-queue-length () ; At one point this was simply the difference between the *last-slot-saved* and ; the *last-slot-taken*. However, since we grab work from the work queue ; before actually processing it with an idle cpu core, it is also necessary to ; include the number of threads taht are waiting for a starting core. (+ (- *last-slot-saved* *last-slot-taken*) *threads-waiting-for-starting-core* ; I intentionally ignore the threads that have incremented *last-slot-taken* ; but not yet entered claim-starting-core. I could come up with some mechanism ; to track these, but there should be an insignificant number of them (less ; than the total number of hardware threads, as of 2012-07), and it's not worth ; it right now. )) (defun total-number-of-threads () (length (all-threads))) (defvar *refresh-rate-indicator* 0) (defmacro value-of-symbol (var) (when (not (or (fboundp var) (symbolp var))) (error "value-of-symbol requires a symbol or function name as its argument")) (cond ((constantp var) `(format nil " Constant ~s is ~s~% " ,(symbol-name var) ,var)) ((fboundp var) `(format nil " Stat ~s is ~s~% " ,(symbol-name var) (,var))) ((boundp-global var *the-live-state*) `(format nil " Stat ~s is ~s~% " ,(symbol-name var) ,(f-get-global var *the-live-state*))) (t `(format nil " Variable ~s is ~s~% " ,(symbol-name var) ,var)))) (defun acl2p-sum-list1 (lst acc) (cond ((endp lst) acc) (t (acl2p-sum-list1 (cdr lst) (+ (car lst) acc))))) (defun acl2p-sum-list (lst) ; An arcane name is chosen so that we don't conflict with other implementations ; of "sum-list". (acl2p-sum-list1 lst 0)) (defun average-future-queue-size () (* 1.0 (/ (acl2p-sum-list *future-queue-length-history*) (length *future-queue-length-history*)))) (defun print-interesting-parallelism-variables-str () (incf *refresh-rate-indicator*) (setf *future-queue-length-history* ; Note that this setf isn't thread safe, but if we lose one entry in the ; history, we don't really care -- it's just a debugging tool anyway. (cons (future-queue-length) *future-queue-length-history*)) (concatenate 'string (format nil " Printing stats related to executing proofs in parallel.~% ") (value-of-symbol *idle-future-core-count*) (value-of-symbol *idle-future-resumptive-core-count*) (value-of-symbol *idle-future-thread-count*) (value-of-symbol *threads-waiting-for-starting-core*) (value-of-symbol number-of-idle-threads-and-threads-waiting-for-a-starting-core) (value-of-symbol total-number-of-threads) (format nil "~% ") (value-of-symbol *unassigned-and-active-future-count*) (value-of-symbol *unassigned-and-active-work-count-limit*) (value-of-symbol *total-future-count*) (value-of-symbol total-parallelism-work-limit) (format nil "~% ") (value-of-symbol number-of-active-threads) (value-of-symbol number-of-threads-waiting-on-a-child) (format nil "~% ") (value-of-symbol *last-slot-taken*) (value-of-symbol *last-slot-saved*) (value-of-symbol future-queue-length) (value-of-symbol average-future-queue-size) (format nil "~% ") (value-of-symbol *resource-based-parallelizations*) (value-of-symbol *resource-based-serializations*) (value-of-symbol *resource-and-timing-based-parallelizations*) (value-of-symbol *resource-and-timing-based-serializations*) (value-of-symbol *futures-resources-available-count*) (value-of-symbol *futures-resources-unavailable-count*) (format nil "~% ") (format nil " Printing stats related to aborting futures.~% ") (value-of-symbol *aborted-futures-total*) (value-of-symbol *aborted-futures-via-throw*) (value-of-symbol *aborted-futures-via-flag*) (value-of-symbol *almost-aborted-future-count*) (format nil "~% ") (value-of-symbol *refresh-rate-indicator*))) (defun print-interesting-parallelism-variables () (format t (print-interesting-parallelism-variables-str))) acl2-sources/proof-checker-a.lisp0000666002132200015000000025664612222115527016435 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; PC globals are those that can be changed from inside the proof-checker's ; interactive loop, and whose values we want saved. Note that state-stack can ; also be changed outside the interactive loop (by use of :instruction), so we ; need to be careful. We'll manage this by keeping state-stack as a PC global, ; updating pc-output upon entry to reflect the latest value of state-stack. (defmacro pc-value (sym) (cond ((eq sym 'ss-alist) '(f-get-global 'pc-ss-alist state)) (t `(cdr (assoc-eq ',sym (f-get-global 'pc-output state)))))) (defmacro pc-assign (key val) (cond ((eq key 'ss-alist) `(f-put-global 'pc-ss-alist ,val state)) (t `(f-put-global 'pc-output (put-assoc-eq ',key ,val (f-get-global 'pc-output state)) state)))) (defun initialize-pc-acl2 (state) (er-progn (assign pc-output nil) (pprogn (pc-assign ss-alist nil) (pc-assign old-ss nil) (pc-assign state-stack nil) (pc-assign next-pc-enabled-array-suffix 0) (pc-assign pc-depth 0) ; for the proof-checker-cl-proc clause-processor (assign in-verify-flg nil)))) (defmacro state-stack () '(pc-value state-stack)) (defmacro old-ss () '(pc-value old-ss)) ; The entries in ss-alist are of the form (name state-stack . old-ss). (defmacro ss-alist () '(pc-value ss-alist)) (defun define-global-name (var) (intern-in-package-of-symbol (string-append (symbol-name var) "-FN") var)) (defmacro define-global (var) (let ((var-fn (define-global-name var))) `(progn (defun ,var-fn (state) (f-get-global ',var state)) (defmacro ,var () '(,var-fn state))))) (define-global pc-prompt) (define-global pc-prompt-depth-prefix) (define-global pc-print-macroexpansion-flg) ; Turn the following on for debugging macro commands. (define-global pc-print-prompt-and-instr-flg) ; We will maintain an invariant that there are no unproved goals hanging around ; in the pc-state. Moreover, for simplicity, we leave it up to each command to ; ensure that no newly-created goal has a conclusion with a non-NIL explicit ; value. The function remove-proved-goal-from-pc-state will be applied to ; remove the current goal if it has been proved. ; The pc-ens component of the state is either an enabled structure or else is ; NIL, which indicates that we should use the global enabled structure. (defrec pc-state (instruction (goals . abbreviations) local-tag-tree pc-ens . tag-tree) nil) (defconst *pc-state-fields-for-primitives* '(instruction goals abbreviations tag-tree local-tag-tree pc-ens)) (defmacro instruction (&optional state-stack-supplied-p) `(access pc-state (car ,(if state-stack-supplied-p 'state-stack '(state-stack))) :instruction)) (defmacro goals (&optional state-stack-supplied-p) `(access pc-state (car ,(if state-stack-supplied-p 'state-stack '(state-stack))) :goals)) (defmacro abbreviations (&optional state-stack-supplied-p) `(access pc-state (car ,(if state-stack-supplied-p 'state-stack '(state-stack))) :abbreviations)) (defmacro local-tag-tree (&optional state-stack-supplied-p) `(access pc-state (car ,(if state-stack-supplied-p 'state-stack '(state-stack))) :local-tag-tree)) (defmacro pc-ens (&optional state-stack-supplied-p) `(access pc-state (car ,(if state-stack-supplied-p 'state-stack '(state-stack))) :pc-ens)) (defmacro tag-tree (&optional state-stack-supplied-p) `(access pc-state (car ,(if state-stack-supplied-p 'state-stack '(state-stack))) :tag-tree)) ; A state-stack is a list of goal records. The goal contains explicit hyps, ; and also (via current-addr) implicit if-term governors. Depends-on is the ; first suffix available for subgoals of the current goal; so, (goal-name . n) ; has been used at some point for exactly those positive integers n for which n ; < depends-on. (defrec goal (conc depends-on (hyps . current-addr) goal-name) t) (defconst *goal-fields* '(conc hyps current-addr goal-name depends-on)) (defmacro conc (&optional ss-supplied-p) `(access goal (car (goals ,ss-supplied-p)) :conc)) (defmacro hyps (&optional ss-supplied-p) `(access goal (car (goals ,ss-supplied-p)) :hyps)) (defmacro current-addr (&optional ss-supplied-p) `(access goal (car (goals ,ss-supplied-p)) :current-addr)) (defmacro goal-name (&optional ss-supplied-p) `(access goal (car (goals ,ss-supplied-p)) :goal-name)) (defmacro depends-on (&optional ss-supplied-p) `(access goal (car (goals ,ss-supplied-p)) :depends-on)) (defmacro make-official-pc-command (sym) `(intern-in-package-of-symbol (symbol-name ,sym) 'acl2-pc::acl2-pkg-witness)) (defun intern-in-keyword-package (sym) (declare (xargs :guard (symbolp sym))) (intern (symbol-name sym) "KEYWORD")) (defun make-pretty-pc-command (x) (declare (xargs :guard (symbolp x))) ;; Returns the user-and-stored version of the command x. (intern-in-keyword-package x)) (defun make-pretty-pc-instr (instr) (declare (xargs :guard (or (symbolp instr) (and (consp instr) (symbolp (car instr)))))) (if (atom instr) (make-pretty-pc-command instr) (if (null (cdr instr)) (make-pretty-pc-command (car instr)) (cons (make-pretty-pc-command (car instr)) (cdr instr))))) (defmacro change-pc-state (pc-s &rest args) (list* 'change 'pc-state pc-s args)) (defun make-official-pc-instr (instr) ; This function always returns a syntactically legal instruction, i.e., a true ; list whose car is a symbol in the ACL2-PC package (if (consp instr) (if (and (symbolp (car instr)) (true-listp (cdr instr))) (cons (make-official-pc-command (car instr)) (cdr instr)) (list (make-official-pc-command 'illegal) instr)) (if (symbolp instr) (list (make-official-pc-command instr)) (if (and (integerp instr) (> instr 0)) (list (make-official-pc-command 'dv) instr) (list (make-official-pc-command 'illegal) instr))))) (defun check-formals-length (formals args fn ctx state) (declare (xargs :guard (and (symbol-listp formals) (true-listp args)))) (let ((max-length (if (member-eq '&rest formals) 'infinity (length (remove '&optional formals)))) (min-length (let ((k (max (length (member-eq '&rest formals)) (length (member-eq '&optional formals))))) (- (length formals) k))) (n (length args))) (if (and (<= min-length n) (or (eq max-length 'infinity) (<= n max-length))) (value t) (if (equal min-length max-length) (er soft ctx "Wrong number of arguments in argument list ~x0 to ~x1. There should ~ be ~n2 argument~#3~[s~/~/s~] to ~x1." args fn min-length (zero-one-or-more min-length)) (if (equal max-length 'infinity) (er soft ctx "Wrong number of arguments in argument list ~x0 to ~x1. There should ~ be at least ~n2 argument~#3~[s~/~/s~] to ~x1." args fn min-length (min min-length 2)) (er soft ctx "Wrong number of arguments in argument list ~x0 to ~x1. There should ~ be between ~n2 and ~n3 arguments to ~x1." args fn min-length max-length)))))) (defun check-&optional-and-&rest (formals state) (cond ((not (true-listp formals)) (er soft 'check-&optional-and-&rest "The formals are supposed to be a true list, but they are ~x0." formals)) ;; &optional can only occur at most once ((member-eq '&optional (cdr (member-eq '&optional formals))) (er soft 'check-&optional-and-&rest "The &optional keywords occurs more than once in ~x0." formals)) ;; &rest can only occur next to the end (t (let ((r-formals (reverse formals))) (if (or (eq (car r-formals) '&optional) (eq (car r-formals) '&rest)) (er soft 'check-&optional-and-&rest "The &optional and &rest keywords may not occur as the last element of ~ the formals list, ~x0." formals) (if (member-eq '&rest (cddr r-formals)) (er soft 'check-&optional-and-&rest "The &rest keyword may not occur except as the next-to-last ~ member of the formals list, which is not the case for ~x0." formals) (value t))))))) (defun make-let-pairs-from-formals (formals arg) ;; e.g. (make-let-pairs-from-formals '(a b c) 'x) = ;; ((a (car x)) (b (car (cdr x))) (c (car (cdr (cdr x))))) (if (consp formals) (if (eq (car formals) '&optional) (make-let-pairs-from-formals (cdr formals) arg) (if (eq (car formals) '&rest) (list (list (cadr formals) arg)) (cons (list (car formals) (list 'car arg)) (make-let-pairs-from-formals (cdr formals) (list 'cdr arg))))) nil)) ;; The following are like all-vars, but heuristic in that they deal with untranslated forms. (mutual-recursion (defun all-symbols (form) (cond ((symbolp form) (list form)) ((atom form) nil) ((eq (car form) (quote quote)) nil) (t ;; used to have just (all-symbols-list (cdr form)) below, but ;; then (cond (current-addr ...) ...) messed up (union-eq (all-symbols (car form)) (all-symbols-list (cdr form)))))) (defun all-symbols-list (x) (if (consp x) (union-eq (all-symbols (car x)) (all-symbols-list (cdr x))) nil)) ) (defun make-access-bindings (record-name record fields) (if (consp fields) (cons `(,(car fields) (access ,record-name ,record ,(intern-in-keyword-package (car fields)))) (make-access-bindings record-name record (cdr fields))) nil)) (defun let-form-for-pc-state-vars (form) (let ((vars (all-symbols form))) (let* ((goal-vars (intersection-eq *goal-fields* vars)) (pc-state-vars (if goal-vars (intersection-eq *pc-state-fields-for-primitives* (cons 'goals vars)) (intersection-eq *pc-state-fields-for-primitives* vars)))) `(let ,(make-access-bindings 'pc-state 'pc-state pc-state-vars) (let ,(make-access-bindings 'goal '(car goals) goal-vars) ,form))))) (defun check-field-names (formals ctx state) (let ((bad-formals (intersection-eq formals (append *goal-fields* *pc-state-fields-for-primitives*)))) (if bad-formals (er soft ctx "It is illegal to use names of pc-state or goal fields as formals to~ define commands with ~x0, in this case ~&1." ctx bad-formals) (value t)))) (defmacro print-no-change (&optional str alist (col '0)) `(print-no-change-fn ,str ,alist ,col state)) (defmacro print-no-change2 (&rest args) `(pprogn ,(cons 'print-no-change args) (mv nil state))) (defun print-no-change-fn (str alist col state) (declare (xargs :guard (or (stringp str) (null str)))) (io? proof-checker nil state (col alist str) (mv-let (col state) (let ((channel (proofs-co state))) (mv-let (col state) (fmt1 "~|*** NO CHANGE ***" nil col channel state nil) (if str (mv-let (col state) (fmt1 " -- " nil col channel state nil) (mv-let (col state) (fmt1 str alist col channel state (term-evisc-tuple nil state)) (fmt1 "~|" nil col channel state nil))) (fmt1 "~|" nil col channel state nil)))) (declare (ignore col)) state))) (defmacro maybe-update-instruction (instr pc-state-and-state) `(mv-let (pc-state state) ,pc-state-and-state (mv (and pc-state ; in case the instruction failed! (if (access pc-state pc-state :instruction) pc-state (change-pc-state pc-state :instruction (make-pretty-pc-instr ,instr)))) state))) (defun add-pc-doc-header (command-type str) (declare (xargs :guard (and (stringp command-type) (stringp str)))) (string-append ":Doc-Section ACL2::Proof-checker-commands " (string-append (string-append command-type " ") str))) (defun remove-doc (command-type body) ;; puts in doc if there isn't any, and puts the appropriate header on (declare (xargs :guard (stringp command-type))) (if (and (consp body) (consp (cdr body)) (stringp (car body))) (mv (add-pc-doc-header command-type (car body)) (cdr body)) (mv nil body))) (defun pc-primitive-defun-form (raw-name name formals doc body) `(defun ,name (args state) ;; notice that args aren't ignored, since even if they're nil, they're ;; used for arity checking ,@(and doc (list doc)) (mv-let ;; can't use er-progn because we return (mv nil state) for errors (erp v state) (check-formals-length ',formals args ',raw-name ',name state) (declare (ignore v)) (if erp (mv nil state) (let ((pc-state (change pc-state (car (state-stack)) :instruction nil)) ,@(make-let-pairs-from-formals formals 'args)) ;; in case we have (declare (ignore pc-state)) ,@(butlast body 1) (maybe-update-instruction (cons ',raw-name args) ,(let-form-for-pc-state-vars (car (last body))))))))) (defun pc-command-table-guard (key val wrld) ; We wrap the pc-command-table guard into this function so that we can redefine ; it when modifying the ACL2 system. (and (function-symbolp key wrld) (or (eq val 'macro) (eq val 'atomic-macro) (eq val 'meta) (and (eq val 'primitive) (global-val 'boot-strap-flg wrld))))) (table pc-command-table nil nil :guard ; Before adding this table guard after Version_4.3, we were able to certify the ; following book. ; (in-package "ACL2") ; (program) ; (set-state-ok t) ; (define-pc-primitive foo (&rest rest-args) ; (declare (ignore rest-args)) ; (mv (change-pc-state pc-state :goals (cdr goals)) ; state)) ; (logic) ; (defthm bug ; nil ; :instructions (:foo) ; :rule-classes nil) (pc-command-table-guard key val world)) (defmacro add-pc-command (name command-type) `(table pc-command-table ',name ,command-type)) (defmacro pc-command-type (name) `(cdr (assoc-equal ,name (table-alist 'pc-command-table (w state))))) (defmacro print-no-change3 (&optional str alist (col '0)) `(pprogn (print-no-change-fn ,str ,alist ,col state) (value nil))) (defun add-pc-command-1 (name command-type state) (table-fn 'pc-command-table `(',name ',command-type) state (list 'table 'pc-command-table (list 'quote name) (list 'quote command-type)))) (defun toggle-pc-macro-fn (name new-tp state) (let ((tp (pc-command-type name))) (if (null tp) (print-no-change3 "The command ~x0 is not a proof-checker command." (list (cons #\0 name))) (case tp (macro (if (or (null new-tp) (equal (symbol-name new-tp) "ATOMIC-MACRO")) (add-pc-command-1 name 'atomic-macro state) (if (equal (symbol-name new-tp) "MACRO") (print-no-change3 "~x0 is already a non-atomic macro." (list (cons #\0 name))) (print-no-change3 "You can't change a proof-checker macro ~ to have type ~x0." (list (cons #\0 new-tp)))))) (atomic-macro (if (or (null new-tp) (equal (symbol-name new-tp) "MACRO")) (add-pc-command-1 name 'macro state) (if (equal (symbol-name new-tp) "ATOMIC-MACRO") (print-no-change3 "~x0 is already an atomic macro." (list (cons #\0 name))) (print-no-change3 "You can't change a proof-checker atomic macro ~ to have type ~x0." (list (cons #\0 new-tp)))))) (otherwise (print-no-change3 "You can't change the type of a proof-checker ~x0 command." (list (cons #\0 tp)))))))) (defun pc-meta-or-macro-defun (raw-name name formals doc body) `(defun ,name (args state) ;; notice that args aren't ignored, since even if they're nil, they're ;; used for arity checking (declare (xargs :mode :program :stobjs state)) ,@(and doc (list doc)) (er-progn (check-formals-length ',formals args ',raw-name ',name state) (let ((state-stack (state-stack)) ,@(make-let-pairs-from-formals formals 'args)) ;; in case we have a doc-string and/or declare forms ,@(butlast body 1) (let ((very-silly-copy-of-state-stack state-stack)) ; This silly trick ensures that we don't have to declare state-stack ignored. (declare (ignore very-silly-copy-of-state-stack)) ,(car (last body))))))) (defun goal-names (goals) (if (consp goals) (cons (access goal (car goals) :goal-name) (goal-names (cdr goals))) nil)) (defun instructions-of-state-stack (ss acc) (if (consp ss) (instructions-of-state-stack (cdr ss) (cons (access pc-state (car ss) :instruction) acc)) ;; at the end we cdr the accumulator to get rid of the `start' instruction (cdr acc))) (defmacro fms0 (str &optional alist col (evisc-tuple 'nil evisc-tuple-p)) ;; This should only be called when the cursor is on the left margin, or when ;; a fresh line or new line indicator starts the string, unless col is ;; supplied. `(mv-let (new-col state) (fmt1 ,str ,alist ,(or col 0) (proofs-co state) state ,(if evisc-tuple-p evisc-tuple '(term-evisc-tuple nil state))) (declare (ignore new-col)) state)) (defmacro with-output-forced (output-chan signature code) ; Use this to force output to output-chan after executing the give code. See ; print-pc-prompt and print-prompt for examples that make the usage pretty ; obvious. (cond ((or (not (true-listp signature)) (member-eq output-chan signature)) (er hard 'with-output-forced "Ill-formed call: ~x0" `(with-output-forced ,output-chan ,signature ,code))) (t #+acl2-loop-only code #-acl2-loop-only `(mv-let ,signature ,code #-acl2-loop-only (progn (force-output (get-output-stream-from-channel ,output-chan)) (mv ,@signature)) #+acl2-loop-only (mv ,@signature))))) (defun print-pc-prompt (state) ;; Does NOT print a new line before or after, but assumes that we're in column 0. (let ((chan (proofs-co state))) (with-output-forced chan (col state) (io? proof-checker nil (mv col state) (chan) (fmt1 (pc-prompt) nil 0 chan state nil) :default-bindings ((col 0)))))) (defun pc-macroexpand (raw-instr state) ; We assume that instr has already been "parsed", so that it's a list whose car ; is in the ACL2-PC package. This function repeatedly expands instr until we ; have an answer. At one time we intended not to allow state to be returned by ; macroexpansion, but now we want to take a more general view that all kinds of ; what used to be called "help" commands are implemented by macro commands. ; Notice that unlike Lisp macros, the global Lisp state is available for the ; expansion. Hence we can query the ACL2 database etc. (let ((instr (make-official-pc-instr raw-instr))) ; Notice that instr is syntactically valid, i.e. is a true-listp headed by a ; symbol in the acl2-pc package -- even if raw-instr isn't of this form. (if (member-eq (pc-command-type (car instr)) '(macro atomic-macro)) (er-let* ((val (xtrans-eval (list (car instr) (list 'quote (cdr instr)) 'state) nil t t 'pc-macroexpand state t))) (pc-macroexpand val state)) ; So, now we have an instruction that is primitive or meta. (value instr)))) (defun find-goal (name goals) (if (consp goals) (if (equal name (access goal (car goals) :goal-name)) (car goals) (find-goal name (cdr goals))) nil)) (defun print-all-goals-proved-message (state) (io? proof-checker nil state nil (pprogn (print-no-change "There are no unproved goals!") (if (f-get-global 'in-verify-flg state) (fms0 "You may wish to exit.~%") state)))) (defmacro when-goals (form) `(if (goals t) ,form (print-all-goals-proved-message state))) (defmacro when-goals-trip (form) `(if (goals t) ,form (pprogn (print-all-goals-proved-message state) (value 'skip)))) (defun current-immediate-deps (goal-name goal-names) ;; Returns all names in goal-names that are immediate dependents of goal-name. (if (consp goal-names) (if (and (consp (car goal-names)) (equal goal-name (caar goal-names))) (cons (car goal-names) (current-immediate-deps goal-name (cdr goal-names))) (current-immediate-deps goal-name (cdr goal-names))) nil)) (defun goal-dependent-p (parent name) ;; says whether parent is a proper ancestor of name (if (consp name) (if (equal parent (car name)) t (goal-dependent-p parent (car name))) nil)) (defun current-all-deps (goal-name goal-names) ;; Returns all names in goal-names that are proper dependents (not necessarily ;; immediate) of goal-name. (if (consp goal-names) (if (goal-dependent-p goal-name (car goal-names)) (cons (car goal-names) (current-immediate-deps goal-name (cdr goal-names))) (current-immediate-deps goal-name (cdr goal-names))) nil)) (defun maybe-print-proved-goal-message (goal old-goals goals state) ; Here goal is a goal in the existing pc-state and goals is the goals in the ; new pc-state. old-goals is the goals in the existing pc-state. ; Warning: This function should be called under (io? proof-checker ...). (let* ((name (access goal goal :goal-name)) (new-names (goal-names goals)) (names (set-difference-equal new-names (goal-names old-goals)))) (pprogn (if names (fms0 "~|~%Creating ~n0 new ~#1~[~/goal~/goals~]: ~&2.~%" (list (cons #\0 (length names)) (cons #\1 (zero-one-or-more (length names))) (cons #\2 names)) 0 nil) state) (if (find-goal name goals) state (let ((unproved-deps (current-all-deps name new-names))) (if unproved-deps (fms0 "~|~%The proof of the current goal, ~x0, has been ~ completed. However, the following subgoals remain ~ to be proved:~%~ ~ ~&1.~%Now proving ~x2.~%" (list (cons #\0 name) (cons #\1 unproved-deps) (cons #\2 (access goal (car goals) :goal-name))) 0 nil) (if goals (fms0 "~|~%The proof of the current goal, ~x0, has been ~ completed, as have all of its subgoals.~%Now proving ~x1.~%" (list (cons #\0 name) (cons #\1 (access goal (car goals) :goal-name))) 0 nil) (pprogn (fms0 "~|*!*!*!*!*!*!* All goals have been proved! ~ *!*!*!*!*!*!*~%") (if (f-get-global 'in-verify-flg state) (fms0 "You may wish to exit.~%") state))))))))) (defun accumulate-ttree-in-pc-state (pc-state state) (er-let* ((ttree (accumulate-ttree-and-step-limit-into-state (access pc-state pc-state :tag-tree) :skip state))) (value (change-pc-state pc-state :tag-tree ttree)))) (defun pc-process-assumptions (pc-ens ttree wrld state) ; Like process-assumptions, but returns (mv clauses known-assumptions ttree ; state). (let ((n (count-assumptions ttree))) (pprogn (cond ((< n 101) state) (t (io? prove nil state (n) (fms "~%Note: processing ~x0 forced hypotheses which we now ~ collect)~%" (list (cons #\0 n)) (proofs-co state) state nil)))) (mv-let (n0 assns pairs ttree1) (extract-and-clausify-assumptions nil ttree nil pc-ens wrld (splitter-output)) (cond ((= n0 0) (mv nil nil ttree state)) (t (mv (strip-cdrs pairs) assns ttree1 state))))))) (defun make-implication (assumptions concl) (cond (assumptions (fcons-term* (quote implies) (conjoin assumptions) concl)) (t concl))) (defun cl-set-to-implications (cl-set) (if (null cl-set) nil (cons (make-implication (butlast (car cl-set) 1) (car (last (car cl-set)))) (cl-set-to-implications (cdr cl-set))))) (defun known-assumptions (type-alist assns) ; Here assns is a list of cleaned-up assumptions. We want to collect those ; assumptions whose hypotheses are clearly true under the given type-alist. ; There seems to be no point in trying to add the ones that don't have this ; property, since they'd only introduce case splits. In fact, though, probably ; most of the assumptions we encounter will have this property. (cond ((null assns) nil) ((dumb-type-alist-implicationp type-alist (access assumption (car assns) :type-alist)) (cons (access assumption (car assns) :term) (known-assumptions type-alist (cdr assns)))) (t (known-assumptions type-alist (cdr assns))))) (defun add-assumptions-to-top-goal (goal-unproved-p known-assumptions forced-goals remaining-goals) (if forced-goals (if goal-unproved-p (cons (if known-assumptions (if forced-goals (change goal (car remaining-goals) :hyps (append (access goal (car remaining-goals) :hyps) known-assumptions) :depends-on (+ (access goal (car remaining-goals) :depends-on) (length forced-goals))) (change goal (car remaining-goals) :hyps (append (access goal (car remaining-goals) :hyps) known-assumptions))) (car remaining-goals)) (append forced-goals (cdr remaining-goals))) (append forced-goals remaining-goals)) ; Otherwise, we assume that since forced-goals is nil, assns is nil. ; This saves us the cons above. remaining-goals)) (defun unproved-goals (pc-state) (let ((goals (access pc-state pc-state :goals))) (if (and goals (equal (access goal (car goals) :conc) *t*)) (cdr goals) goals))) (defun make-pc-ens (pc-ens state) (if (null pc-ens) (ens state) pc-ens)) (defun initial-rcnst-from-ens (ens wrld splitter-output) (change rewrite-constant *empty-rewrite-constant* :splitter-output splitter-output :current-enabled-structure ens :oncep-override (match-free-override wrld) :force-info t :nonlinearp (non-linearp wrld) :backchain-limit-rw (backchain-limit wrld :rewrite) :rw-cache-state (rw-cache-state wrld))) (defun make-new-goals-fixed-hyps (termlist hyps goal-name start-index) ;; similar to make-new-goals (if (consp termlist) (cons (make goal :conc (car termlist) :hyps hyps :current-addr nil :goal-name (cons goal-name start-index) :depends-on 1) (make-new-goals-fixed-hyps (cdr termlist) hyps goal-name (1+ start-index))) nil)) (defun pc-single-step-primitive (instr state) (state-global-let* ((guard-checking-on nil)) ; see the Essay on Guard Checking (let* ((goals (goals)) (wrld (w state)) (old-tag-tree (tag-tree))) (cond ((null goals) (pprogn (print-all-goals-proved-message state) (mv nil nil state))) (t (mv-let (erp stobjs-out/vals state) (trans-eval (list (car instr) (list 'quote (cdr instr)) 'state) 'pc-single-step state t) (let ((vals (cdr stobjs-out/vals))) ; Vals is (x replaced-state), where x is a pc-state or nil. (cond (erp (pprogn (print-no-change ; We used to say "Very odd" here, but it is perfectly natural to get such an ; error if there is an rdepth-error. "An error occurred in executing ~X01." (list (cons #\0 instr) (cons #\1 (abbrev-evisc-tuple state)))) (mv 'pc-single-step-error-primitive nil state))) (t (assert$ (equal (car stobjs-out/vals) '(nil state)) (cond ((car vals) ;so, there is a new state (let ((pc-ens (make-pc-ens (pc-ens) state))) (mv-let (step-limit bad-ass ttree) (resume-suspended-assumption-rewriting (access pc-state (car vals) :local-tag-tree) nil ;ancestors nil ;gstack nil ;simplify-clause-pot-lst (initial-rcnst-from-ens pc-ens wrld (splitter-output)) wrld state (initial-step-limit wrld state)) (declare (ignore step-limit)) (cond (bad-ass (pprogn (let ((assumnote ; Is the assumnotes field always non-empty? (car (access assumption bad-ass :assumnotes)))) (print-no-change "A false assumption was encountered from applying the ~ rune ~x0 to the target ~x1." (list (cons #\0 (access assumnote assumnote :rune)) (cons #\1 (access assumnote assumnote :target))))) (mv nil nil state))) (t (let* ((returned-pc-state (car vals)) (remaining-goals (unproved-goals returned-pc-state)) (goal-name (goal-name)) ; original goal-name (goal-unproved-p (and remaining-goals (equal goal-name (access goal (car remaining-goals) :goal-name)))) (hyps (hyps)) ; original hyps (returned-goal (let* ((goals (access pc-state returned-pc-state :goals))) (and goals (equal goal-name (access goal (car goals) :goal-name)) (car goals)))) (depends-on (cond (returned-goal (access goal returned-goal :depends-on)) (t ; goal has disappeared; use old depends-on (depends-on))))) (mv-let (cl-set assns ttree state) (pc-process-assumptions pc-ens ttree wrld state) (mv-let (contradictionp hyps-type-alist ttree0) (cond ((and assns goal-unproved-p) (type-alist-clause (dumb-negate-lit-lst hyps) nil nil nil pc-ens wrld nil nil)) (t ; else don't bother (mv nil nil nil))) (cond (contradictionp (er-let* ((new-pc-state (let ((local-ttree (cons-tag-trees ttree ttree0))) (accumulate-ttree-in-pc-state (change-pc-state (car vals) :goals (cdr goals) :tag-tree (cons-tag-trees local-ttree old-tag-tree) :local-tag-tree local-ttree) state)))) (pprogn (io? proof-checker nil state (instr goal-name) (fms0 "~|AHA! A contradiction has ~ been discovered in the ~ hypotheses of goal ~x0 in the ~ course of executing ~ instruction ~x1, in the ~ process of preparing to deal ~ with forced assumptions.~|" (list (cons #\0 goal-name) (cons #\0 instr)) 0 nil)) (io? proof-checker nil state (goals) (maybe-print-proved-goal-message (car goals) goals (cdr goals) state)) (pc-assign state-stack (cons new-pc-state (state-stack))) (value new-pc-state)))) (t (let* ((termlist (cl-set-to-implications cl-set)) (forced-goals (make-new-goals-fixed-hyps termlist hyps goal-name depends-on)) (new-goals (add-assumptions-to-top-goal goal-unproved-p (known-assumptions hyps-type-alist assns) forced-goals remaining-goals)) (pc-state-1 (change-pc-state (car vals) :goals new-goals :tag-tree (cons-tag-trees ttree old-tag-tree) :local-tag-tree ttree))) (er-let* ((new-pc-state (accumulate-ttree-in-pc-state pc-state-1 state))) (pprogn (cond (forced-goals (io? proof-checker nil state (forced-goals) (fms0 "~|~%NOTE (forcing): Creating ~ ~n0 new ~#1~[~/goal~/goals~] ~ due to FORCE or CASE-SPLIT ~ hypotheses of rules.~%" (list (cons #\0 (length forced-goals)) (cons #\1 (zero-one-or-more (length forced-goals))))))) (t state)) (io? proof-checker nil state (new-goals goals) (maybe-print-proved-goal-message (car goals) goals new-goals state)) (pc-assign state-stack (cons new-pc-state (state-stack))) (value new-pc-state)))))))))))))) (t (mv nil nil state))))))))))))) (defun maybe-print-macroexpansion (instr raw-instr state) (let ((pc-print-macroexpansion-flg (pc-print-macroexpansion-flg))) (if (and pc-print-macroexpansion-flg (not (eq (car instr) (make-official-pc-command 'lisp))) (not (equal instr (make-official-pc-instr raw-instr)))) (io? proof-checker nil state (pc-print-macroexpansion-flg instr) (fms0 ">> ~x0~|" (list (cons #\0 instr)) 0 (if (and (consp pc-print-macroexpansion-flg) (integerp (car pc-print-macroexpansion-flg)) (integerp (cdr pc-print-macroexpansion-flg)) (> (car pc-print-macroexpansion-flg) 0) (> (cdr pc-print-macroexpansion-flg) 0)) (evisc-tuple (car pc-print-macroexpansion-flg) (cdr pc-print-macroexpansion-flg) nil nil) nil))) state))) (defun pc-single-step-1 (raw-instr state) ; Returns a triple (signal value new-state). Among other things, new-state ; contains the new value of the state-stack. Value is thought of as ; determining "success" or failure of raw-instr -- in particular, if raw-instr ; is primitive (or expands to a primitive instruction) then value is the new ; state (upon success) or nil (upon failure). Except, signal is handy for ; reporting errors. Signals are to be used only for simulating THROW and ; CATCH, unless one really wants to throw to the top-level loop in case of a ; "really bad" error. (mv-let (erp instr state) (pc-macroexpand raw-instr state) (if erp (pprogn (io? proof-checker nil state (raw-instr) (fms0 "~%Macroexpansion of instruction ~x0 failed!~%" (list (cons #\0 raw-instr)))) (mv erp nil state)) (case (pc-command-type (car instr)) (primitive (pprogn (maybe-print-macroexpansion instr raw-instr state) (pc-single-step-primitive instr state))) (meta (cond ((and (not (f-get-global 'in-verify-flg state)) (not (getprop (car instr) 'predefined nil 'current-acl2-world (w state)))) (er soft 'proof-checker "You may only invoke a user-defined proof-checker meta ~ command, such as ~x0, when you are inside the ~ interactive ~x1 loop." (car instr) 'verify)) (t (pprogn (maybe-print-macroexpansion instr raw-instr state) (mv-let (erp stobjs-out/vals state) ; Vals is a list (er x replaced-state), where er is to be passed as the error ; flag in the triple returned by pc-single-step. We need to call trans-eval ; here, rather than xtrans-eval, so that the effects of meta commands are not ; erased. But then we have to disallow meta commands during replay. (trans-eval (list (car instr) (list 'quote (cdr instr)) 'state) 'pc-single-step state t) (assert$ (equal (car stobjs-out/vals) *error-triple-sig*) (if erp ; impossible case? (pprogn (print-no-change "Very odd -- an error ~ occurred in executing ~x0." (list (cons #\0 instr))) (mv 'pc-single-step-error-meta nil state)) (let ((vals (cdr stobjs-out/vals))) (mv (car vals) (cadr vals) state))))))))) ((macro atomic-macro) (value (er hard 'pc-single-step "Encountered instruction ~x0 whose pc-macroexpansion ~ produced ~x1, which is headed by a macro command!" raw-instr instr))) (otherwise (pprogn (print-no-change "Undefined instruction, ~x0." (list (cons #\0 (make-pretty-pc-instr instr)))) ;; maybe I should cause an error below -- but then I should handle it too (value nil))))))) (defun union-lastn-pc-tag-trees (n ss acc) ; Union together the most recent n local-tag-tree fields of states in the ; state-stack ss. (if (zp n) acc (union-lastn-pc-tag-trees (1- n) (cdr ss) (cons-tag-trees (access pc-state (car ss) :local-tag-tree) acc)))) (defun pc-single-step (raw-instr state) ;; We assume that raw-instr is an "official" instr. ;; same as pc-single-step-1, except that we deal with atomic macro commands (declare (xargs :guard (consp raw-instr))) (let ((tp (pc-command-type (car raw-instr)))) (if (eq tp 'atomic-macro) (let* ((saved-ss (state-stack)) (old-len (length saved-ss))) (mv-let (erp val state) (pc-single-step-1 raw-instr state) (let* ((new-ss (state-stack)) (new-len (length new-ss)) (diff (- new-len old-len))) (if (and (< old-len new-len) (equal saved-ss (nthcdr diff new-ss))) (pprogn (pc-assign state-stack (cons (change pc-state (car new-ss) :instruction (make-pretty-pc-instr raw-instr) :local-tag-tree (union-lastn-pc-tag-trees diff new-ss nil)) saved-ss)) ;; Notice that atomic macros can "return errors" ;; even when they "fail". (mv erp val state)) (mv erp val state))))) (pc-single-step-1 raw-instr state)))) (defconst *pc-complete-signal* 'acl2-pc-complete) (defmacro catch-throw-to-local-top-level (form) ; Form should evaluate to (mv erp val state) or else throw to ; 'local-top-level. #+acl2-loop-only `(mv-let (cttltl-erp cttltl-val state) (read-acl2-oracle state) (cond ((or cttltl-erp cttltl-val) (mv 'thrown-to-local-top-level (or cttltl-erp cttltl-val) state)) (t (check-vars-not-free (cttltl-erp cttltl-val) ,form)))) #-acl2-loop-only (let ((thrown-var (gensym))) `(let* ((,thrown-var t) (trip (catch 'local-top-level (prog1 (mv-list 3 ,form) (setq ,thrown-var nil))))) (cond (,thrown-var (mv 'thrown-to-local-top-level trip state)) (t (assert$ (eq *the-live-state* (caddr trip)) (mv (car trip) (cadr trip) state))))))) (defun pc-main-loop (instr-list quit-conditions last-value pc-print-prompt-and-instr-flg state) ; Returns an error triple whose state has the new state-stack "installed". ; Here instr-list is a (true) list of instructions or else is a non-NIL atom, ; probably *standard-oi*, from which the instructions are to be read. Notice ; that by taking (append instrs ), one is able to get the system to ; read from the instr-list input until there are no more instructions, and then ; to read from the stream. ; Quit-conditions indicates when we want to quit; it is a list of atoms. ; 'signal means that we quit when there's a signal, while 'value means that we ; quit when the value is nil. If quit-conditions is empty (nil) then we keep ; going, no matter what. However, a signal to quit (i.e. *pc-complete-signal*) ; is always obeyed if 'exit is a quit-condition. ; This only returns non-nil if we exit successfully, or if all instructions ; succeed (null erp, non-nil value) without error. (if (null instr-list) (mv nil last-value state) (mv-let (col state) (if pc-print-prompt-and-instr-flg (print-pc-prompt state) (mv 0 state)) (mv-let (erp instr state) (if (consp instr-list) (pprogn (if pc-print-prompt-and-instr-flg (io? proof-checker nil state (col instr-list) (fms0 "~y0~|" (list (cons #\0 (car instr-list))) col)) state) (value (car instr-list))) (state-global-let* ((infixp nil)) (catch-throw-to-local-top-level (read-object instr-list state)))) (cond (erp ; read error (pprogn (io? proof-checker nil state nil (fms0 "~|~%~ /----------------------------------------------------\\~%~ | NOTE: Read error -- input discarded. |~%~ | Submit EXIT if you want to exit the proof-checker. |~%~ \\----------------------------------------------------/~%")) (pc-main-loop instr-list quit-conditions last-value pc-print-prompt-and-instr-flg state))) (t (mv-let (signal val state) (catch-throw-to-local-top-level (pc-single-step (make-official-pc-instr instr) state)) (cond ((and signal (or (member-eq 'signal quit-conditions) (and (eq signal *pc-complete-signal*) (member-eq 'exit quit-conditions)))) (mv signal val state)) ((and (null val) (member-eq 'value quit-conditions)) (mv signal val state)) (t (let ((new-last-value ; We ultimately "succeed" if and only if every instruction "succeeds". We use ; a let-binding here in order to avoid an Allegro CL compiler bug (found using ; Allegro CL 8.0, but told by Franz support that it still exists in Allegro CL ; 9.0). (and last-value (null signal) val))) (pc-main-loop (if (consp instr-list) (cdr instr-list) instr-list) quit-conditions new-last-value pc-print-prompt-and-instr-flg state))))))))))) (defun make-initial-goal (term) (make goal :conc term :hyps nil :current-addr nil :goal-name 'main :depends-on 1)) (defun initial-state-stack (term raw-term event-name rule-classes pc-ens) (list (make pc-state :instruction (list :start (list event-name rule-classes raw-term)) :goals (list (make-initial-goal term)) :local-tag-tree nil :tag-tree nil :abbreviations nil :pc-ens pc-ens))) (defun event-name-and-types-and-raw-term (state-stack) (cadr (access pc-state (car (last state-stack)) :instruction))) (defmacro install-initial-state-stack (term raw-term event-name rule-classes) `(pprogn (pc-assign state-stack (initial-state-stack ,term ,raw-term ,event-name ,rule-classes ;; the initial enabled structure is nil, meaning ;; that we should use the global enabled structure nil)) (pc-assign old-ss nil))) (defun pc-main1 (instr-list quit-conditions pc-print-prompt-and-instr-flg state) (with-prover-step-limit! :start (pc-main-loop instr-list quit-conditions t pc-print-prompt-and-instr-flg state))) (defun pc-main (term raw-term event-name rule-classes instr-list quit-conditions pc-print-prompt-and-instr-flg state) (pprogn (install-initial-state-stack term raw-term event-name rule-classes) (pc-main1 instr-list quit-conditions pc-print-prompt-and-instr-flg state))) (defun pc-top (raw-term event-name rule-classes instr-list quit-conditions state) ;; Here instr-list can have a non-nil last cdr, meaning "proceed ;; interactively". (declare (xargs :guard (symbolp event-name))) (mv-let (erp term state) (translate raw-term t t t 'pc-top (w state) state) ; known-stobjs = t (stobjs-out = t) ; Translate, above, does not enforce the mv-let or stobj signature rules. ; It does insist that the translation contain no :program mode functions. (if erp (mv t nil state) (pc-main term raw-term event-name rule-classes instr-list quit-conditions t state)))) (mutual-recursion ; Keep this in sync with termp. (defun illegal-fnp (x w) (cond ((atom x) nil) ((eq (car x) 'quote) nil) ((symbolp (car x)) (let ((arity (arity (car x) w))) (if (and arity (eql (length (cdr x)) arity)) (illegal-fnp-list (cdr x) w) (car x)))) ((consp (car x)) (illegal-fnp-list (cdr x) w)) (t nil))) (defun illegal-fnp-list (x w) (cond ((endp x) nil) (t (or (illegal-fnp (car x) w) (illegal-fnp-list (cdr x) w))))) ) (defun verify-fn (raw-term raw-term-supplied-p event-name rule-classes instructions state) (cond ((f-get-global 'in-verify-flg state) (er soft 'verify "You are apparently already inside the VERIFY interactive loop. It ~ is illegal to enter such a loop recursively.")) (t (mv-let (erp val state) (cond (raw-term-supplied-p (state-global-let* ((in-verify-flg t) (print-base 10) (print-radix nil) (inhibit-output-lst (remove1-eq 'proof-checker (f-get-global 'inhibit-output-lst state)))) (pc-top raw-term event-name rule-classes (append instructions *standard-oi*) (list 'exit) state))) ((null (state-stack)) (er soft 'verify "There is no interactive verification to re-enter!")) (t (let ((bad-fn (illegal-fnp (access goal (car (access pc-state (car (last (state-stack))) :goals)) :conc) (w state)))) (cond (bad-fn (er soft 'verify "The current proof-checker session was begun in an ACL2 world ~ with function symbol ~x0, but that function symbol no longer ~ exists." bad-fn)) (t (state-global-let* ((in-verify-flg t) (print-base 10) (print-radix nil) (inhibit-output-lst (remove1-eq 'proof-checker (f-get-global 'inhibit-output-lst state)))) (pc-main1 (append instructions *standard-oi*) (list 'exit) t state))))))) (cond ((equal erp *pc-complete-signal*) (value val)) (t (mv erp val state))))))) (defun print-unproved-goals-message (goals state) (io? proof-checker nil state (goals) (fms0 "~%There ~#0~[is~/are~] ~x1 unproved goal~#0~[~/s~] from replay ~ of instructions. To enter the proof-checker state that exists ~ at this point, type (VERIFY).~%" (list (cons #\0 goals) (cons #\1 (length goals)))))) (defun state-stack-from-instructions (raw-term event-name rule-classes instructions replay-flg quit-conditions state) (if replay-flg (pprogn (io? proof-checker nil state nil (fms0 "~|~%Entering the proof-checker....~%~%")) (er-progn (pc-top raw-term event-name rule-classes instructions quit-conditions state) (value (state-stack)))) (value (state-stack)))) (defun state-from-instructions (raw-term event-name rule-classes instructions quit-conditions state) (mv-let (erp val state) (pc-top raw-term event-name rule-classes instructions quit-conditions state) (declare (ignore erp val)) state)) (defun print-pc-defthm (ev state) (let ((ldd (make-ldd 'event nil #\Space 0 t ev))) (io? proof-checker nil state (ldd) (fms0 "~|~y0" (list (cons #\0 (print-ldd-full-or-sketch (access-ldd-fullp ldd) (access-ldd-form ldd)))))))) (defmacro print-pc-goal (&optional goal) `(let ((goal ,(or goal '(car (access pc-state (car (state-stack)) :goals))))) (io? proof-checker nil state (goal) (if goal (fms0 "~%------- ~x3 -------~|~ Conc: ~q0~|~ Hyps: ~q1~|~ Addr: ~Y2n~|~ Deps: ~Y4n~|" (list (cons #\0 (untranslate (access goal goal :conc) t (w state))) (cons #\1 (let ((hyps (access goal goal :hyps))) (cond ((null hyps) t) ((null (cdr hyps)) (untranslate (car hyps) t (w state))) (t (cons 'and (untranslate-lst hyps t (w state))))))) (cons #\2 (access goal goal :current-addr)) (cons #\3 (access goal goal :goal-name)) (cons #\4 (access goal goal :depends-on)) (cons #\n nil))) (fms0 "~%No goal in CAR of state-stack.~|"))))) (defmacro print-pc-state (&optional pc-state) `(let ((pc-state ,(or pc-state '(car (state-stack))))) (io? proof-checker nil state (pc-state) (if pc-state (fms0 "~%Instr: ~y0~|~ Goals: ~y1~|~ Abbrs: ~y2~|~ Local ttree: ~y3~|~ Ttree: ~y4~|" (list (cons #\0 (access pc-state pc-state :instruction)) (cons #\1 (access pc-state pc-state :goals)) (cons #\2 (access pc-state pc-state :abbreviations)) (cons #\3 (access pc-state pc-state :local-tag-tree)) (cons #\4 (access pc-state pc-state :tag-tree)))) (fms0 "~%No state in CAR of state-stack.~|"))))) (defun proof-checker (event-name raw-term term rule-classes instructions wrld state) ;; I'm only including wrld in the arglist because J has it there. ;; **** Be sure that in-verify-flg is untouchable, for soundness here (or ;; is that really an issue?). ":Doc-Section Proof-checker support for low-level interaction~/ Call this up with ~c[(verify ...)]. ~/ This is an interactive system for checking ACL2 theorems, or at least exploring their proofs. One enters it using the ~c[VERIFY] command (~pl[verify]), and then invokes commands at the resulting prompt to operate on a stack of goals, starting with the single goal that was supplied to ~c[VERIFY]. The final command (or ``instruction'') can be an ~c[exit] command, which can print out a ~ilc[defthm] event if the goal stack is empty; ~pl[proof-checker-commands], in particular the ~c[exit] command. That resulting ~c[defthm] event includes an ~c[:]~ilc[instructions] parameter, which directs replay of the proof-checker commands (for example during certification of a book containing that event; ~pl[books]). If you exit the proof-checker interactive loop, you may re-enter that session at the same point using the command ~c[(verify)], i.e., with no arguments. The commands ~c[save] and ~c[retrieve] may be invoked to manage more than one session. The proof-checker can be invoked on a specific subgoal, and the resulting ~c[:instructions] can be given as a hint to the theorem prover for that subgoal. ~l[instructions]. A tutorial is available on the world-wide web:~nl[] ~url[http://www.cs.utexas.edu/users/kaufmann/tutorial/rev3.html].~nl[] The tutorial illustrates more than just the proof-checker. The portion relevant to the proof-checker may be accessed directly:~nl[] ~url[http://www.cs.utexas.edu/users/kaufmann/tutorial/rev3.html#slide29] ~l[set-evisc-tuple] for how to arrange that output is printed in abbreviated form. In general, the proof-checker uses the ~c[:TERM] ~il[evisc-tuple] described in that documentation. Individual proof-checker commands are documented in subsection ~il[proof-checker-commands]. When inside the interactive loop (i.e., after executing ~ilc[verify]), you may use the ~ilc[help] command to get a list of legal instructions and ~c[(help instr)] to get help for the instruction ~c[instr]." (declare (ignore term wrld)) (cond ((and (not (f-get-global 'in-verify-flg state)) (ld-skip-proofsp state)) ; Thus, we are not in an interactive loop, and we are to skip proofs. (value nil)) (t (mv-let (erp state-stack state) (state-stack-from-instructions raw-term event-name rule-classes instructions (not (f-get-global 'in-verify-flg state)) '(signal value) state) ;; could perhaps (declare (ignore erp)), but for now I'll abort upon error (if erp (pprogn (io? proof-checker nil state nil (fms0 "~%~%Replay of proof-checker instructions ~ aborted.~%")) (if (f-get-global 'in-verify-flg state) (mv *pc-complete-signal* nil state) (silent-error state))) (let ((goals (access pc-state (car state-stack) :goals))) (if (null goals) (value (access pc-state (car state-stack) :tag-tree)) (pprogn ;; could print the goals here instead of just the number of goals. (print-unproved-goals-message goals state) (if (f-get-global 'in-verify-flg state) (mv *pc-complete-signal* nil state) (silent-error state)))))))))) (deflabel proof-checker-commands :doc ":Doc-Section Proof-checker list of commands for the proof-checker~/ This documentation section contains documentation for individual commands that can be given inside the interactive ~il[proof-checker] loop that is entered using ~ilc[verify].~/~/") (deflabel macro-command :doc ":Doc-Section Proof-checker compound command for the proof-checker~/ The proof-checker (~pl[proof-checker]) allows the user to supply interactive commands. Compound commands, called macro commands, may be defined; these expand into zero or more other commands. Some of these are ``atomic'' macro commands; these are viewed as a single command step when completed successfully.~/ More ~il[documentation] will be written on the ~il[proof-checker]. For now, we simply point out that there are lots of examples of the use of ~c[define-pc-macro] and ~c[define-pc-atomic-macro] in the ACL2 source file ~c[\"proof-checker-b.lisp\"]. The former is used to create macro commands, which can be submitted to the interactive loop (~pl[verify]) and will ``expand'' into zero or more commands. The latter is similar, except that the undoing mechanism (~pl[acl2-pc::undo]) understands atomic macro commands to represent single interactive commands. Also ~pl[acl2-pc::comm] and ~pl[acl2-pc::commands] for a discussion of the display of interactive commands. Also ~pl[toggle-pc-macro] for how to change a macro command to an atomic macro command, and vice versa.") (defmacro verify (&optional (raw-term 'nil raw-term-supplied-p) &key event-name (rule-classes '(:rewrite)) instructions) ":Doc-Section Proof-checker enter the interactive proof checker~/ For proof-checker command summaries, ~pl[proof-checker].~/ ~bv[] Examples: (VERIFY (implies (and (true-listp x) (true-listp y)) (equal (append (append x y) z) (append x (append y z))))) -- Attempt to prove the given term interactively. (VERIFY (p x) :event-name p-always-holds :rule-classes (:rewrite :generalize) :instructions ((rewrite p-always-holds-lemma) change-goal)) -- Attempt to prove (p x), where the intention is to call the resulting DEFTHM event by the name p-always-holds, with rule-classes as indicated. The two indicated instructions will be run immediately to start the proof. (VERIFY) -- Re-enter the proof-checker in the state at which is was last left. General Form: (VERIFY &OPTIONAL raw-term &KEY event-name rule-classes instructions) ~ev[] ~c[Verify] is the function used for entering the ~il[proof-checker]'s interactive loop." (if (and raw-term-supplied-p (eq raw-term nil)) '(pprogn (io? proof-checker nil state nil (fms0 "It is not permitted to enter the interactive proof-checker ~ with a goal of NIL! If you really MEANT to do such a ~ thing, (VERIFY 'NIL).~%")) (value :invisible)) `(verify-fn ',raw-term ',raw-term-supplied-p ',event-name ',rule-classes ',instructions state))) (deflabel instructions :doc ":Doc-Section Proof-checker instructions to the proof checker~/ ~l[proof-checker] for an introduction to the interactive ``proof-checker'' goal manager, which supports much more direct control of the proof process than is available by direct calls to the prover (as are normally made using ~ilc[defthm] or ~ilc[thm]). In brief, typical use is to evaluate the form ~c[(verify SOME-GOAL)], where ~c[SOME-GOAL] is a formula (i.e., term) that you would like to prove. Various commands (instructions) are available at the resulting prompt; ~pl[proof-checker-commands]. When the proof is completed, suitable invocation of the ~c[exit] command will print out a form containing an ~c[:instructions] field that provides the instructions that you gave interactively, so that this form can be evaluated non-interactively. Thus, also ~pl[defthm] for the role of ~c[:instructions] in place of ~c[:]~ilc[hints]. As illustrated by the following example, the value associated with ~c[:instructions] is a list of ~il[proof-checker] commands. ~bv[] Example: (defthm associativity-of-append (equal (append (append x y) z) (append x (append y z))) :instructions (:induct (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2) :top (:dv 2) :x :top :s :bash)) ~ev[] When you are inside the interactive loop (i.e., after executing ~ilc[verify]), you may invoke ~ilc[help] to get a list of legal instructions and ~c[(help instr)] to get help for the instruction ~c[instr]. Below, we describe a capability for supplying ~c[:instructions] as ~c[:]~ilc[hints].~/ The most basic utilities for directing the discharge of a proof obligation are ~c[:]~ilc[hints] and (less commonly) ~c[:instructions]. Individual instructions may call the prover with ~c[:hints]; in that sense, prover hints may occur inside instructions. We now describe how, on the other hand, instructions may occur inside hints. ACL2 supports ~c[:instructions] as a hints keyword. The following example forms the basis for our running example. This example does not actually need hints, but imagine that the inductive step ~-[] which is \"Subgoal *1/2\" ~-[] was difficult. You could submit that goal to ~ilc[verify], do an interactive proof, submit ~c[(exit t)] to obtain the list of ~c[:instructions], and then paste in those instructions. When you submit the resulting event, you might see the following. Below we'll explain the hint processing. ~bv[] ACL2 !>(thm (equal (append (append x y) z) (append x (append y z))) :hints ((\"Subgoal *1/2\" :instructions (:promote (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2) :top (:dv 2) :x :top :s)))) Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. Subsumption reduces that number to two. However, one of these is flawed and so we are left with one viable candidate. We will induct according to a scheme suggested by (BINARY-APPEND X Y). This suggestion was produced using the :induction rule BINARY-APPEND. If we let (:P X Y Z) denote *1 above then the induction scheme we'll use is (AND (IMPLIES (AND (NOT (ENDP X)) (:P (CDR X) Y Z)) (:P X Y Z)) (IMPLIES (ENDP X) (:P X Y Z))). This induction is justified by the same argument used to admit BINARY-APPEND. When applied to the goal at hand the above induction scheme produces two nontautological subgoals. [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC replaces this goal by T. Subgoal *1/1 (IMPLIES (ENDP X) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). By the simple :definition ENDP we reduce the conjecture to Subgoal *1/1' (IMPLIES (NOT (CONSP X)) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But simplification reduces this to T, using the :definition BINARY-APPEND and primitive type reasoning. That completes the proof of *1. Q.E.D. Summary Form: ( THM ...) Rules: ((:DEFINITION BINARY-APPEND) (:DEFINITION ENDP) (:DEFINITION NOT) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:INDUCTION BINARY-APPEND)) Time: 0.02 seconds (prove: 0.01, print: 0.01, other: 0.00) Proof succeeded. ACL2 !> ~ev[] To understand how the ~c[:instructions] supplied above were processed, observe proof-checker instruction interpreter may be viewed as a ``clause-processor'': a function that takes an input goal and returns a list of goals (which can be the empty list). Such a function has the property that if all goals in that returned list are theorems, then so is the input goal. This view of the proof-checker instruction interpreter as a clause-processor leads to the following crucial observation. ~st[IMPORTANT!]. Each call of the proof-checker instruction interpreter is treated as a standalone clause-processor that is insensitive to the surrounding prover environment. In particular:~bq[] o The proof-checker's theory is not sensitive to ~c[:in-theory] ~il[hints] already processed in the surrounding proof. Indeed, the current theory for which proof-checker commands are processed is just the current theory of the ACL2 logical ~il[world], i.e., the value of ~c[(current-theory :here)]. Moreover, references to ~c[(current-theory :here)] in a proof-checker ~c[in-theory] command, even implicit references such as provided by ~ilc[enable] and ~ilc[disable] expressions, are also references to the current theory of the ACL2 logical ~il[world]. o The ~il[rune]s used during an ~c[:instructions] hint are not tracked beyond that hint, hence may not show up in the summary of the overall proof. Again, think of the ~c[:instructions] hint as a ~il[clause-processor] call, which has some effect not tracked by the surrounding proof other than for the child goals that it returns.~eq[] We continue now with our discussion of the proof-checker instruction interpreter as a clause-processor. In the example above, the input goal (~c[\"Subgoal *1/2\"]) was processed by the proof-checker instruction interpreter. The result was the empty goal stack, therefore proving the goal, as reported in the output, which we repeat here. ~bv[] [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC replaces this goal by T. ~ev[] ~st[Remark.] This brief remark can probably be ignored, but we include it for completeness. The ~c[:CLAUSE-PROCESSOR] message above may be surprising, since the hint attached to ~c[\"Subgoal *1/2\"] is an ~c[:instructions] hint, not a ~c[:clause-processor] hint. But ~c[:instructions] is actually a custom keyword hint (~pl[custom-keyword-hints]), and may be thought of as a macro that expands to a ~c[:]~ilc[clause-processor] hint, one that specifies ~c[proof-checker-cl-proc] as the clause-processor function. The keen observer may notice that the clause-processor is referred to as ``trusted'' in the above output. Normally one needs a trust tag (~pl[defttag]) to install a trusted clause-processor, but that is not the case for the built-in clause-processor, ~c[proof-checker-cl-proc]. Finally, we note that ~c[:instructions] ~il[hints] are ``spliced'' into the hints as follows: the appropriate ~c[:]~ilc[clause-processor] hint replaces the ~c[:instructions] hint, and the other hints remain intact. It may seems surprising that one can thus, for example, use ~c[:instructions] and ~c[:in-theory] together; but although the ~c[:in-theory] hint will have no effect on execution of the ~c[:instructions] (see first bullet above), the ~c[:in-theory] hint will apply in the usual manner to any child goals (~pl[hints-and-the-waterfall]). End of Remark. Now consider the case that the supplied instructions do not prove the goal. That is, suppose that the execution of those instructions results in a non-empty goal stack. In that case, the resulting goals become children of the input goals. The following edited log provides an illustration using a modification of the above example, this time with a single instruction that splits into two cases. ~bv[] ACL2 !>(thm (equal (append (append x y) z) (append x (append y z))) :hints ((\"Subgoal *1/2\" :instructions ((:casesplit (equal x y)))))) [[ ... output omitted ... ]] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). We now apply the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC to produce two new subgoals. Subgoal *1/2.2 [[ ... output omitted ... ]] Subgoal *1/2.1 [[ ... output omitted ... ]] ~ev[] We have seen that an ~c[:instructions] hint may produce zero or more subgoals. There may be times where you wish to insist that it produce zero subgoals, i.e., that it prove the desired goal. The proof-checker `~c[finish]' command works nicely for this purpose. For example, the following form is successfully admitted, but if you delete some of the commands (for example, the ~c[:s] command at the end), you will see an informative error message. ~bv[] (thm (equal (append (append x y) z) (append x (append y z))) :hints ((\"Subgoal *1/2\" :instructions ((finish :promote (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2) :top (:dv 2) :x :top :s))))) ~ev[] If an :instructions hint of the form ~c[((finish ...))] fails to prove the goal, the clause-processor is deemed to have caused an error. Indeed, any ``failure'' of a supplied proof-checker instruction will be deemed to cause an error. In this case, you should see an error message such as the following: ~bv[] Saving proof-checker error state; see :DOC instructions. To retrieve: (RETRIEVE :ERROR1) ~ev[] In this case, you can evaluate the indicated ~ilc[retrieve] command in the ACL2 read-eval-print loop, to get to the point of failure. You may have noticed that there is no output from the proof-checker in the examples above. This default behavior prevents confusion that could arise from use of proof-checker commands that call the theorem prover such as ~c[prove], ~c[bash], ~c[split], and ~c[induct]. These commands produce output for what amounts to a fresh proof attempt, which could confuse attempts to understand the surrounding proof log. You can override the default behavior by providing a command of the form ~bv[] ~c[(comment inhibit-output-lst VAL)] ~ev[] where ~c[VAL] is either the keyword ~c[:SAME] (indicating that no change should be made to which output is inhibited) or else is a legal value for inhibited output; ~pl[set-inhibit-output-lst]. The following two variants of the immediately preceding ~c[THM] form will each produce output from the proof-checker commands, assuming in the first variant that output hasn't already been inhibited. ~bv[] (thm (equal (append (append x y) z) (append x (append y z))) :hints ((\"Subgoal *1/2\" :instructions ((comment inhibit-output-lst :same) (:casesplit (equal x y)))))) (thm (equal (append (append x y) z) (append x (append y z))) :hints ((\"Subgoal *1/2\" :instructions ((comment inhibit-output-lst (proof-tree)) (:casesplit (equal x y)))))) ~ev[] Note that such a ~c[comment] instruction must be provided explicitly (i.e., not by way of a proof-checker ~il[macro-command]) as the first instruction, in order to have the effect on inhibited output that is described above. The following contrived example gives a sense of how one might want to use ~c[:instructions] within ~c[:]~ilc[hints]. If you submit the following theorem ~bv[] (thm (implies (true-listp x) (equal (reverse (reverse x)) x))) ~ev[] then you will see the following checkpoint printed with the summary. ~bv[] Subgoal *1/3'' (IMPLIES (AND (CONSP X) (EQUAL (REVAPPEND (REVAPPEND (CDR X) NIL) NIL) (CDR X)) (TRUE-LISTP (CDR X))) (EQUAL (REVAPPEND (REVAPPEND (CDR X) (LIST (CAR X))) NIL) X)) ~ev[] This suggests proving the following theorem. Here we state it using ~ilc[defthmd], so that it is immediately disabled. Normally disabling would be unnecessary, but for our contrived example it is useful to imagine disabling it, say because we are following a methodology that tends to keep ~il[rewrite] rules disabled. ~bv[] (defthmd revappend-revappend (equal (revappend (revappend x y) z) (revappend y (append x z)))) ~ev[] We might then enter the ~il[proof-checker] to prove the original theorem interactively, as follows. ~bv[] ACL2 !>(verify (implies (true-listp x) (equal (reverse (reverse x)) x))) ->: bash ***** Now entering the theorem prover ***** Goal' ([ A key checkpoint: Goal' (IMPLIES (TRUE-LISTP X) (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL) X)) Goal' is subsumed by a goal yet to be proved. ]) Q.E.D. Creating one new goal: (MAIN . 1). The proof of the current goal, MAIN, has been completed. However, the following subgoals remain to be proved: (MAIN . 1). Now proving (MAIN . 1). ->: th ; show current goal (\"th\" for \"theorem\") *** Top-level hypotheses: 1. (TRUE-LISTP X) The current subterm is: (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL) X) ->: p ; show current subterm only (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL) X) ->: 1 ; dive to first argument ->: p (REVAPPEND (REVAPPEND X NIL) NIL) ->: sr ; show-rewrites 1. REVAPPEND-REVAPPEND (disabled) New term: (REVAPPEND NIL (APPEND X NIL)) Hypotheses: Equiv: EQUAL 2. REVAPPEND New term: (AND (CONSP (REVAPPEND X NIL)) (REVAPPEND (CDR (REVAPPEND X NIL)) (LIST (CAR (REVAPPEND X NIL))))) Hypotheses: Equiv: EQUAL ->: (r 1) ; rewrite with rule #1 above Rewriting with REVAPPEND-REVAPPEND. ->: p (REVAPPEND NIL (APPEND X NIL)) ->: top ; move to the top of the conclusion, making it the current subterm ->: p (EQUAL (REVAPPEND NIL (APPEND X NIL)) X) ->: prove ; finish the proof ***** Now entering the theorem prover ***** Q.E.D. *!*!*!*!*!*!* All goals have been proved! *!*!*!*!*!*!* You may wish to exit. ->: (exit t) ; the argument, t, causes :instructions to be printed (DEFTHM T (IMPLIES (TRUE-LISTP X) (EQUAL (REVERSE (REVERSE X)) X)) :INSTRUCTIONS (:BASH (:DV 1) (:REWRITE REVAPPEND-REVAPPEND) :TOP :PROVE)) NIL ACL2 !>(thm (IMPLIES (TRUE-LISTP X) (EQUAL (REVERSE (REVERSE X)) X)) :hints ((\"Goal\" :INSTRUCTIONS ; copy what was printed above: (:BASH (:DV 1) (:REWRITE REVAPPEND-REVAPPEND) :TOP :PROVE)))) Goal' Q.E.D. Q.E.D. Q.E.D. Summary Form: ( THM ...) Rules: NIL Hint-events: ((:CLAUSE-PROCESSOR PROOF-CHECKER-CL-PROC)) Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !> ~ev[] Finally we present an even more contrived example, based on the one above. This example illustrates that there is actually no limit imposed on the nesting of ~c[:instructions] within ~c[:]~ilc[hints] within ~c[:instructions], and so on. Notice the indication of nesting levels: ``~c[1>]'' to ``~c[<1]'' for output from nesting level 1, and ``~c[2>]'' to ``~c[<2]'' for output from nesting level 2. ~bv[] (thm (implies (true-listp x) (equal (reverse (reverse x)) x)) :hints ((\"Goal\" :instructions ((comment inhibit-output-lst :same) (:prove :hints ((\"Goal\" :in-theory (disable append)) (\"Subgoal *1/3''\" :instructions ((comment inhibit-output-lst :same) :bash (:dv 1) (:rewrite revappend-revappend))))))))) ~ev[] Here is an edited version of the resulting log. ~bv[] [Note: A hint was supplied for our processing of the goal above. Thanks!] [[1> Executing proof-checker instructions]] ->: (COMMENT INHIBIT-OUTPUT-LST :SAME) ->: (:PROVE :HINTS ((\"Goal\" :IN-THEORY (DISABLE APPEND)) (\"Subgoal *1/3''\" :INSTRUCTIONS ((COMMENT INHIBIT-OUTPUT-LST :SAME) :BASH (:DV 1) (:REWRITE REVAPPEND-REVAPPEND))))) ***** Now entering the theorem prover ***** [[ ... output omitted ... ]] [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/3'' (IMPLIES (AND (CONSP X) (EQUAL (REVAPPEND (REVAPPEND (CDR X) NIL) NIL) (CDR X)) (TRUE-LISTP (CDR X))) (EQUAL (REVAPPEND (REVAPPEND (CDR X) (LIST (CAR X))) NIL) X)). [[2> Executing proof-checker instructions]] ->: (COMMENT INHIBIT-OUTPUT-LST :SAME) ->: :BASH ***** Now entering the theorem prover ***** [Note: A hint was supplied for our processing of the goal above. Thanks!] But we have been asked to pretend that this goal is subsumed by the yet-to-be-proved |PROOF-CHECKER Goal|. Q.E.D. Creating one new goal: (MAIN . 1). The proof of the current goal, MAIN, has been completed. However, the following subgoals remain to be proved: (MAIN . 1). Now proving (MAIN . 1). ->: (:DV 1) ->: (:REWRITE REVAPPEND-REVAPPEND) Rewriting with REVAPPEND-REVAPPEND. [[<2 Completed proof-checker instructions]] We now apply the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC to produce one new subgoal. Subgoal *1/3''' [[ ... output omitted ... ]] [[<1 Completed proof-checker instructions]] ~ev[] The nesting levels are independent of whether or not output is enabled; for example, if the first ~c[(comment ...)] form below is omitted, then we will see only the output bracketed by ``~c[2>]'' to ``~c[<2]''. Note also that these levels are part of the error states saved for access by ~ilc[retrieve] (as indicated above); for example, a failure at level 1 would be associated with symbol ~c[:ERROR1] as indicated above, while a failure at level 2 would be associated with symbol ~c[:ERROR2].~/") ; Finally, here is some stuff that is needed not only for the proof-checker but ; also for :pl. (mutual-recursion (defun sublis-expr-non-quoteps (alist term) ;; Same as ACL2's function sublis-expr, except that it doesn't take a ;; world argument. However, for correctness it may be necessary that ;; every CDR in ALIST is non-quotep, so that we can guarantee that ;; non-quotep's are mapped to non-quotep's. (let ((temp (assoc-equal term alist))) (cond (temp (cdr temp)) ((variablep term) term) ((fquotep term) term) (t (let ((new-args (sublis-expr-non-quoteps-lst alist (fargs term)))) (if (quote-listp new-args) ;; then no substitution was actually made term ;; otherwise, cons-term becomes simply cons (cons (ffn-symb term) new-args))))))) (defun sublis-expr-non-quoteps-lst (alist lst) (cond ((null lst) nil) (t (cons (sublis-expr-non-quoteps alist (car lst)) (sublis-expr-non-quoteps-lst alist (cdr lst)))))) ) (defun invert-abbreviations-alist (alist) (declare (xargs :guard (alistp alist))) (if (null alist) nil (cons (cons (cdr (car alist)) (list '? (car (car alist)))) (invert-abbreviations-alist (cdr alist))))) (defun abbreviate (term abbreviations) (if (null abbreviations) term (sublis-expr-non-quoteps (invert-abbreviations-alist abbreviations) term))) (defmacro untrans0 (term &optional iff-flg abbreviations) ; Note that state should always be bound where this is called. `(untranslate (abbreviate ,term ,abbreviations) ,iff-flg (w state))) (defun untrans0-lst-fn (termlist iff-flg abbreviations state) (if (consp termlist) (cons (untrans0 (car termlist) iff-flg abbreviations) (untrans0-lst-fn (cdr termlist) iff-flg abbreviations state)) nil)) (defmacro untrans0-lst (termlist &optional iff-flg abbreviations) `(untrans0-lst-fn ,termlist ,iff-flg ,abbreviations state)) acl2-sources/proof-checker-b.lisp0000664002132200015000000117434012222115527016423 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") (defmacro install-new-pc-meta-or-macro (command-type raw-name name formals doc body) `(progn ,(pc-meta-or-macro-defun raw-name name formals doc body) (add-pc-command ,name ',command-type))) (defun define-pc-meta-or-macro-fn (command-type raw-name formals body) (let ((name (make-official-pc-command raw-name)) ) (mv-let (doc body) (remove-doc (case command-type (meta " (meta)") (macro " (macro)") (atomic-macro " (atomic macro)") (otherwise "")) body) `(install-new-pc-meta-or-macro ,command-type ,raw-name ,name ,formals ,doc ,body)))) (defmacro define-pc-meta (raw-name formals &rest body) ":Doc-Section Proof-checker define a proof-checker meta command~/ Built-in proof-checker meta commands include ~c[undo] and ~c[restore], and others (~c[lisp], ~c[exit], and ~c[sequence]); ~pl[proof-checker-commands]. The advanced proof-checker user can define these as well. See ACL2 source file ~c[proof-checker-b.lisp] for examples, and contact the ACL2 implementors if those examples do not provide sufficient documentation.~/~/" (define-pc-meta-or-macro-fn 'meta raw-name formals body)) (defmacro define-pc-macro (raw-name formals &rest body) ":Doc-Section Proof-checker define a proof-checker macro command~/ ~bv[] Example: (define-pc-macro ib (&optional term) (value (if term `(then (induct ,term) bash) `(then induct bash)))) ~ev[] The example above captures a common paradigm: one attempts to prove the current goal by inducting and then simplifying the resulting goals. (~pl[proof-checker-commands] for documentation of the command ~c[then], which is itself a pc-macro command, and commands ~c[induct] and ~c[bash].) Rather than issuing ~c[(then induct bash)], or worse yet issuing ~c[induct] and then issuing ~c[bash] for each resulting goals, the above definition of ~c[ib] would let you issue ~c[ib] and get the same effect.~/ ~bv[] General Form: (define-pc-macro cmd args doc-string dcl ... dcl body) ~ev[] where ~c[cmd] is the name of the pc-macro than you want to define, ~c[args] is its list of formal parameters. ~c[Args] may include lambda-list keywords ~c[&optional] and ~c[&rest]; ~pl[macro-args], but note that here, ~c[args] may not include ~c[&key] or ~c[&whole]. The value of ~c[body] should be an error triple (~pl[error-triples]), of the form ~c[(mv erp xxx state)] for some ~c[erp] and ~c[xxx]. If ~c[erp] is ~c[nil], then ~c[xxx] is handed off to the proof-checker's instruction interpreter. Otherwise, evaluation typically halts. We may write more on the full story later if there is interest in reading it.~/" (define-pc-meta-or-macro-fn 'macro raw-name formals body)) (defmacro define-pc-atomic-macro (raw-name formals &rest body) (define-pc-meta-or-macro-fn 'atomic-macro raw-name formals body)) (defmacro toggle-pc-macro (name &optional new-tp) (declare (xargs :guard (and (symbolp new-tp) (or (null new-tp) (member-equal (symbol-name new-tp) '("MACRO" "ATOMIC-MACRO")))))) ":Doc-Section Proof-checker change an ordinary macro command to an atomic macro, or vice-versa~/ ~bv[] Example: (toggle-pc-macro pro) ~ev[] Change ~c[pro] from an atomic macro command to an ordinary one (or vice-versa, if ~c[pro] happens to be an ordinary macro command)~/ ~bv[] General Form: (toggle-pc-macro name &optional new-tp) ~ev[] If name is an atomic macro command then this turns it into an ordinary one, and vice-versa. However, if ~c[new-tp] is supplied and not ~c[nil], then it should be the new type (the symbol ~c[macro] or ~c[atomic-macro], in any package), or else there is no change." `(toggle-pc-macro-fn ',(make-official-pc-command name) ',new-tp state)) (defmacro define-pc-primitive (raw-name formals &rest body) ; Define-pc-primitive defines a new primitive for the proof-checker. That ; primitive is always a function returning (mv pc-state state), where the ; (pc-value state-stack) has not been changed for state. ; Primitive command definitions should never look at the instruction field of ; the current state; see pc-primitive-defun-form. ; We generally rely in pc-single-step-primitive on the following property: a ; primitive leaves the top goal on the top of the :goals stack of the pc-state, ; adjusted as necessary, with its depends-on field reflecting all new subgoals ; added to that stack. However, if the top goal is proved and no forced ; hypotheses are stored in the tag tree (see pc-single-step-primitive), then we ; may drop a proved goal. (let ((name (make-official-pc-command raw-name))) (mv-let (doc body) (remove-doc " (primitive)" body) `(progn ,(pc-primitive-defun-form raw-name name formals doc body) (add-pc-command ,name 'primitive))))) (define-pc-primitive comment (&rest x) "insert a comment~/ ~bv[] Example: (comment now begin difficult final goal)~/ General Form: (comment &rest x) ~ev[] This instruction makes no change in the state except to insert the ~c[comment] instruction. Some comments can be used to improve the display of commands; see documentation for ~c[comm]." (declare (ignore x)) (mv pc-state state)) (defun non-bounded-nums (nums lower upper) (declare (xargs :guard (and (rationalp lower) (rationalp upper) (true-listp nums)))) (if (consp nums) (if (and (integerp (car nums)) (<= lower (car nums)) (<= (car nums) upper)) (non-bounded-nums (cdr nums) lower upper) (cons (car nums) (non-bounded-nums (cdr nums) lower upper))) nil)) (defun delete-by-position (lst current-index nums) (declare (xargs :guard (and (true-listp nums) (integerp current-index)))) (if (consp lst) (if (member current-index nums) (delete-by-position (cdr lst) (1+ current-index) nums) (cons (car lst) (delete-by-position (cdr lst) (1+ current-index) nums))) nil)) (define-pc-primitive drop (&rest nums) "drop top-level hypotheses~/ ~bv[] Examples: (drop 2 3) -- drop the second and third hypotheses drop -- drop all top-level hypotheses~/ General Forms: (drop n1 n2 ...) -- Drop the hypotheses with the indicated indices. drop -- Drop all the top-level hypotheses. ~ev[] ~st[Remark:] If there are no top-level hypotheses, then the instruction ~c[drop] will fail. If any of the indices is out of range, i.e. is not an integer between one and the number of top-level hypotheses ~c[(inclusive)], then ~c[(drop n1 n2 ...)] will fail." (if nums (let ((bad-nums (non-bounded-nums nums 1 (length hyps)))) (if bad-nums (print-no-change2 "The following are not in-range hypothesis numbers: ~&0." (list (cons #\0 bad-nums))) (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :hyps (delete-by-position hyps 1 nums)) (cdr goals))) state))) (if hyps (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :hyps nil) (cdr goals))) state) (print-no-change2 "There are no hypotheses to drop!")))) (define-pc-meta lisp (form) "evaluate the given form in Lisp~/ ~bv[] Example: (lisp (assign xxx 3))~/ General Form: (lisp form) ~ev[] Evaluate ~c[form]. The ~c[lisp] command is mainly of interest for side effects. See also ~c[print], ~c[skip], and ~c[fail]. The rest of the documentation for ~c[lisp] is of interest only to those who use it in macro commands. If the Lisp evaluation (by ~c[trans-eval]) of form returns an error triple (~pl[error-triples]) of the form ~c[(mv erp ((NIL NIL STATE) . (erp-1 val-1 &)) state)], then the ~c[lisp] command returns the appropriate error triple ~bv[] (mv (or erp erp-1) val-1 state) . ~ev[] Otherwise, the ~c[trans-eval] of form must return an error triple of the form ~c[(mv erp (cons stobjs-out val) &)], and the ~c[lisp] command returns the appropriate error triple ~bv[] (mv erp val state). ~ev[] Note that the output signature of the form has been lost. The user must know the signature in order to use the output of the ~c[lisp] command. Trans-eval, which is undocumented except by comments in the ACL2 source code, has replaced, in ~c[val], any occurrence of the current state or the current values of stobjs by simple symbols such as ~c[REPLACED-STATE]. The actual values of these objects may be recovered, in principle, from the ~c[state] returned and the ~c[user-stobj-alist] within that state. However, in practice, the stobjs cannot be recovered because the user is denied access to ~c[user-stobj-alist]. The moral is: do not try to write macro commands that manipulate stobjs. Should the returned ~c[val] contain ~c[REPLACED-STATE] the value may simply be ignored and ~c[state] used, since that is what ~c[REPLACED-STATE] denotes." (cond ((not (f-get-global 'in-verify-flg state)) (er soft 'acl2-pc::lisp "You may only invoke the proof-checker LISP command when ~ you are inside the interactive loop.")) ((and (symbolp form) (or (eq form t) (eq form nil) (keywordp form))) (value form)) (t (mv-let (erp stobjs-out/vals state) (trans-eval form :lisp state t) (let ((stobjs-out (car stobjs-out/vals)) (vals (cdr stobjs-out/vals))) (if (equal stobjs-out *error-triple-sig*) (mv (or erp (car vals)) (cadr vals) state) (mv erp vals state))))))) (define-pc-primitive fail-primitive () (declare (ignore pc-state)) (mv nil state)) (define-pc-macro fail (&optional hard) "cause a failure~/ ~bv[] Examples: fail (fail t)~/ General Form: (fail &optional hard) ~ev[] This is probably only of interest to writers of macro commands. The only function of ~c[fail] is to fail to ``succeed''. The full story is that ~c[fail] and ~c[(fail nil)] simply return ~c[(mv nil nil state)], while ~c[(fail hard)] returns ~c[(mv hard nil state)] if ~c[hard] is not ~c[nil]. See also ~c[do-strict], ~c[do-all], and ~c[sequence]." (if hard (value '(lisp (mv hard nil state))) (value 'fail-primitive))) (define-pc-macro illegal (instr) "illegal instruction~/ ~bv[] Example: (illegal -3)~/ General Form: (illegal instruction) ~ev[] Probably not of interest to most users; always ``fails'' since it expands to the ~c[fail] command. The ~c[illegal] command is used mainly in the implementation. For example, the instruction ~c[0] is ``read'' as ~c[(illegal 0)], since ~c[dive] expects positive integers.~/" (pprogn (print-no-change "Illegal interactive instruction, ~x0.~% An instruction must be a ~ symbol or a proper list headed by a symbol." (list (cons #\0 instr))) (value :fail))) (defun chk-assumption-free-ttree-1 (ttree ctx) ;; Same as chk-assumption-free-ttree, but returns a value. (cond ((tagged-objectsp 'assumption ttree) (er hard ctx "The 'assumption ~x0 was found in the final ttree!" (car (tagged-objects 'assumption ttree)))) ((tagged-objectsp 'fc-derivation ttree) (er hard ctx "The 'fc-derivation ~x0 was found in the final ttree!" (car (tagged-objects 'fc-derivation ttree)))) (t t))) (defun put-cdr-assoc-query-id (id val alist) (cond ((atom alist) (cons (cons id val) alist)) ((eq id (caar alist)) (cons (cons id val) (cdr alist))) (t (cons (car alist) (put-cdr-assoc-query-id id val (cdr alist)))))) (defun set-query-val (id val state) ;; If val is 'toggle, then a NIL default is changed to T and every ;; other default is changed to NIL. Otherwise, VAL is the new default. (let ((alist (ld-query-control-alist state))) (set-ld-query-control-alist (put-cdr-assoc-query-id id (if (eq val 'toggle) (not (cdr-assoc-query-id id alist)) val) alist) state))) (defmacro query-on-exit (&optional (val 'toggle)) `(set-query-val 'acl2-pc::exit ',val state)) (defun replay-query (state) ;; Returns a state-stack, T or NIL. A T value means we should replay instructions ;; in order to create the state-stack. A value of NIL means that we should exit ;; without creating the event (by making the state-stack nil). ;; In fact, the only time we return other than the current ;; state-stack is if we're inside verify and ;; either the query flag is off or the response is other than "Y". (acl2-query 'acl2-pc::exit '("~%Do you want to submit this event? Possible replies are:~%~ Y (Yes), R (yes and Replay commands), N (No, but exit), A (Abort exiting).~|~ " :y :y :r :r :n :n :a :a) nil state)) (define-pc-meta exit (&optional event-name rule-classes do-it-flg) "exit the interactive proof-checker~/ ~bv[] Examples: exit -- exit the interactive proof-checker (exit t) -- exit after printing a bogus defthm event (exit append-associativity) -- exit and create a defthm event named append-associativity~/ General Forms: exit -- Exit without storing an event. (exit t) -- Exit after printing a bogus defthm event, showing :INSTRUCTIONS. (exit event-name &optional rule-classes do-it-flg) -- Exit, and perhaps store an event ~ev[] The command ~c[exit] returns you to the ACL2 loop. At a later time, ~c[(verify)] may be executed to get back into the same proof-checker state, as long as there hasn't been an intervening use of the proof-checker (otherwise see ~c[save]). When given one or more arguments as shown above, ~c[exit] still returns you to the ACL2 loop, but first, if the interactive proof is complete, then it attempts create a ~c[defthm] event with the specified ~c[event-name] and ~c[rule-classes] (which defaults to ~c[(:rewrite)] if not supplied). The event will be printed to the terminal, and then normally the user will be queried whether an event should really be created. However, if the final optional argument ~c[do-it-flg] is supplied and not ~c[nil], then an event will be made without a query. For example, the form ~bv[] (exit top-pop-elim (:elim :rewrite) t) ~ev[] causes a ~c[defthm] event named ~c[top-pop-elim] to be created with rule-classes ~c[(:elim :rewrite)], without a query to the user (because of the argument ~c[t]). ~st[Remark:] it is permitted for ~c[event-name] to be ~c[nil]. In that case, the name of the event will be the name supplied during the original call of ~c[verify]. (See the documentation for ~c[verify] and ~c[commands].) Also in that case, if ~c[rule-classes] is not supplied then it defaults to the rule-classes supplied in the original call of ~c[verify]. ~c[Comments] on ``success'' and ``failure''. An ~c[exit] instruction will always ``fail'', so for example, if it appears as an argument of a ~c[do-strict] instruction then none of the later (instruction) arguments will be executed. Moreover, the ``failure'' will be ``hard'' if an event is successfully created or if the instruction is simply ~c[exit]; otherwise it will be ``soft''. See the documentation for ~c[sequence] for an explanation of hard and soft ``failures''. An obscure but potentially important fact is that if the ``failure'' is hard, then the error signal is a special signal that the top-level interactive loop can interpret as a request to exit. Thus for example, a sequencing command that turns an error triple ~c[(mv erp val state)] into ~c[(mv t val state)] would never cause an exit from the interactive loop. If the proof is not complete, then ~c[(exit event-name ...)] will not cause an exit from the interactive loop. However, in that case it will print out the original user-supplied goal (the one that was supplied with the call to ~c[verify]) and the current list of instructions." ; We allow (exit .. nil ..) to indicate that information is to be picked up ; from the initial pc-state. (if (not (f-get-global 'in-verify-flg state)) (er soft 'acl2-pc::exit "You may not invoke the EXIT command unless inside the ~ interactive loop.") (if args ; so it's not just a command to exit (let* ((event-name-and-types-and-raw-term (event-name-and-types-and-raw-term state-stack)) (event-name (or event-name (car event-name-and-types-and-raw-term))) (instructions (instructions-of-state-stack state-stack nil))) (er-let* ((event-name (if event-name (value event-name) (pprogn (io? proof-checker nil state nil (fms0 "Please supply an event name (or :A to ~ abort)~%>> ")) (state-global-let* ((infixp nil)) (read-object *standard-oi* state)))))) (if (eq event-name :a) (pprogn (io? proof-checker nil state nil (fms0 "~|Exit aborted.~%")) (mv nil nil state)) (if (null (goals t)) (let* ((rule-classes (if (consp (cdr args)) rule-classes (if (and (consp args) (eq (car args) nil)) (cadr event-name-and-types-and-raw-term) '(:rewrite)))) (event-form `(defthm ,event-name ,(caddr event-name-and-types-and-raw-term) ,@(if (equal rule-classes '(:rewrite)) nil (list :rule-classes rule-classes)) :instructions ,instructions))) (mv-let (erp stobjs-out/vals state) (pprogn (print-pc-defthm event-form state) (mv-let (erp ans state) (cond (do-it-flg (value :y)) ((eq event-name t) (value :n)) (t (replay-query state))) (declare (ignore erp)) (case ans (:y (trans-eval event-form 'acl2-pc::exit state t)) (:r (pprogn (state-from-instructions (caddr event-form) event-name rule-classes instructions '(signal value) state) (trans-eval event-form 'acl2-pc::exit state t))) (:a (mv t '(nil . t) state)) (otherwise (mv t '(nil . nil) state))))) ; We assume here that if DEFTHM returns without error, then it succeeds. (if (or erp (null (car stobjs-out/vals))) (if (eq (cdr stobjs-out/vals) t) (pprogn (io? proof-checker nil state nil (fms0 "~|Exit aborted.~%")) (mv nil nil state)) (mv *pc-complete-signal* nil state)) (mv *pc-complete-signal* event-name state)))) ; Otherwise, we have an incomplete proof. (pprogn (io? proof-checker nil state (instructions event-name-and-types-and-raw-term state-stack) (fms0 "~%Not exiting, as there remain unproved ~ goals: ~&0.~%The original goal is:~%~ ~ ~ ~ ~ ~y1~| Here is the current instruction ~ list, starting with the first:~%~ ~ ~ ~ ~ ~y2~|" (list (cons #\0 (goal-names (goals t))) (cons #\1 (caddr event-name-and-types-and-raw-term)) (cons #\2 instructions)))) (mv nil nil state)))))) (pprogn (io? proof-checker nil state nil (fms0 "~|Exiting....~%")) (mv *pc-complete-signal* nil state))))) (define-pc-meta undo (&optional n) "undo some instructions~/ ~bv[] Examples: (undo 7) undo~/ General Forms: (undo n) -- Undo the last n instructions. The argument n should be a positive integer. undo -- Same as (undo 1). ~ev[] ~st[Remark:] To remove the effect of an ~c[undo] command, use ~c[restore]. See the documentation for details. ~st[Remark:] If the argument ~c[n] is greater than the total number of interactive instructions in the current session, then ~c[(undo n)] will simply take you back to the start of the session. The ~c[undo] meta command always ``succeeds''; it returns ~c[(mv nil t state)] unless its optional argument is supplied and of the wrong type (i.e. not a positive integer) or there are no instructions to undo." (if (and args (not (and (integerp n) (< 0 n)))) (pprogn (print-no-change "The optional argument to undo must be a positive integer.") (mv nil nil state)) (let ((m (min (or n 1) (1- (length state-stack))))) (if (null (cdr state-stack)) (pprogn (print-no-change "Already at the start.") (mv nil nil state)) (pprogn (pc-assign old-ss state-stack) (io? proof-checker nil state (state-stack m) (fms0 "~|Undoing: ~y0~|" (list (cons #\0 (access pc-state (car (nthcdr (1- m) state-stack)) :instruction))))) (pc-assign state-stack (nthcdr m state-stack)) (if (consp (cdr (state-stack))) state (io? proof-checker nil state nil (fms0 "Back to the start.~%"))) (mv nil t state)))))) (define-pc-meta restore () "remove the effect of an UNDO command~/ ~bv[] Example and General Form: restore ~ev[]~/ ~c[Restore] removes the effect of an ~c[undo] command. This always works as expected if ~c[restore] is invoked immediately after ~c[undo], without intervening instructions. However, other commands may also interact with ~c[restore], notably ``sequencing'' commands such as ~c[do-all], ~c[do-strict], ~c[protect], and more generally, ~c[sequence]. ~st[Remark:] Another way to control the saving of proof-checker state is with the ~c[save] command; see the documentation for ~c[save]. The ~c[restore] command always ``succeeds''; it returns ~c[(mv nil t state)]." (let ((old-ss (pc-value old-ss))) (if (null old-ss) (pprogn (io? proof-checker nil state nil (fms0 "~%Nothing to restore from!~%")) (mv nil nil state)) (let ((saved-ss state-stack)) (pprogn (pc-assign state-stack old-ss) (pc-assign old-ss saved-ss) (mv nil t state)))))) (defun print-commands (indexed-instrs state) (if (null indexed-instrs) state (if (null (caar indexed-instrs)) (io? proof-checker nil state (indexed-instrs) (fms0 (car (cdar indexed-instrs)) (cdr (cdar indexed-instrs)))) (pprogn (io? proof-checker nil state (indexed-instrs) (fms0 "~|~x0. ~y1~|" (list (cons #\0 (caar indexed-instrs)) (cons #\1 (cdar indexed-instrs))))) (print-commands (cdr indexed-instrs) state))))) (defun make-pretty-start-instr (state-stack) (let* ((triple (event-name-and-types-and-raw-term state-stack)) (name (car triple)) (types (cadr triple))) (if name (list "~|[started with (~x0 ~x1 ...)]~%" (cons #\0 name) (cons #\1 types)) (list "~|<< no event name specified at start >>~%")))) (defun raw-indexed-instrs (start-index finish-index state-stack) (declare (xargs :guard (and (integerp start-index) (integerp finish-index) (<= start-index finish-index) (true-listp state-stack) ;; It's tempting to add the following guard, but ;; since state-stack keeps shrinking, it can get violated ;; on recursive calls. ;; (<= finish-index (length state-stack)) ))) (if (< start-index finish-index) (cons (cons start-index (access pc-state (car state-stack) :instruction)) (raw-indexed-instrs (1+ start-index) finish-index (cdr state-stack))) (if (cdr state-stack) (list (cons start-index (access pc-state (car state-stack) :instruction))) (list (cons nil (make-pretty-start-instr state-stack)))))) (define-pc-macro sequence-no-restore (instr-list) (value `(sequence ,instr-list nil nil nil nil t))) (define-pc-macro skip () "``succeed'' without doing anything~/ ~bv[] Example and General Form: skip ~ev[]~/ Make no change in the state-stack, but ``succeed''. Same as ~c[(sequence nil)]." (value '(sequence-no-restore nil))) (defmacro define-pc-help (name args &rest body) ":Doc-Section Proof-checker define a macro command whose purpose is to print something~/ ~bv[] Example: (define-pc-help pp () (if (goals t) (io? proof-checker nil state (state-stack) (fms0 \"~~|~~y0~~|\" (list (cons #\0 (fetch-term (conc t) (current-addr t)))))) (print-all-goals-proved-message state)))~/ General Form: (define-pc-help name args &rest body) ~ev[] This defines a macro command named ~c[name], as explained further below. The ~c[body] should (after removing optional declarations) be a form that returns ~c[state] as its single value. Typically, it will just print something. What ~c[(define-pc-help name args &rest body)] really does is to create a call of ~c[define-pc-macro] that defines ~c[name] to take arguments ~c[args], to have the declarations indicated by all but the last form in ~c[body], and to have a body that (via ~c[pprogn]) first executes the form in the last element of body and then returns a call to the command ~c[skip] (which will return ~c[(mv nil t state)])." `(define-pc-macro ,name ,args ,@(butlast body 1) (pprogn ,(car (last body)) (value 'skip)))) (defun evisc-indexed-instrs-1 (name rev-indexed-instrs) (if (consp rev-indexed-instrs) (let ((instr (cdr (car rev-indexed-instrs)))) (case-match instr ((comm ':end x . &) (if (and (eq comm (make-pretty-pc-command :comment)) (equal x name)) rev-indexed-instrs (evisc-indexed-instrs-1 name (cdr rev-indexed-instrs)))) (& (evisc-indexed-instrs-1 name (cdr rev-indexed-instrs))))) nil)) (defun evisc-indexed-instrs-rec (rev-indexed-instrs) (if (consp rev-indexed-instrs) (let ((instr (cdr (car rev-indexed-instrs))) (evisc-cdr (evisc-indexed-instrs-rec (cdr rev-indexed-instrs)))) (case-match instr ((comm ':begin name . &) (if (eq comm (make-pretty-pc-command :comment)) (let ((rst (evisc-indexed-instrs-1 name evisc-cdr))) (if rst (cons (cons (car (car rev-indexed-instrs)) (cons "***HIDING***" instr)) (cdr rst)) (cons (car rev-indexed-instrs) evisc-cdr))) (cons (car rev-indexed-instrs) evisc-cdr))) (& (cons (car rev-indexed-instrs) evisc-cdr)))) nil)) (defun mark-unfinished-instrs (indexed-instrs) ;; any "begin" in here was not matched with an "end" (if (consp indexed-instrs) (let ((instr (cdr (car indexed-instrs)))) (case-match instr ((comm ':begin & . &) (if (eq comm (make-pretty-pc-command :comment)) (cons (cons (car (car indexed-instrs)) (cons "***UNFINISHED***" instr)) (mark-unfinished-instrs (cdr indexed-instrs))) (cons (car indexed-instrs) (mark-unfinished-instrs (cdr indexed-instrs))))) (& (cons (car indexed-instrs) (mark-unfinished-instrs (cdr indexed-instrs)))))) nil)) (defun evisc-indexed-instrs (indexed-instrs) ;; for now, returns a new state stack in which we drop bookends ;; (comment (begin ) ...) ;; (comment (end ) ...) (mark-unfinished-instrs (reverse (evisc-indexed-instrs-rec (reverse indexed-instrs))))) (define-pc-help commands (&optional n evisc-p) "display instructions from the current interactive session~/ ~bv[] Examples: commands (commands 10 t)~/ General Forms: commands or (commands nil) Print out all the instructions (in the current state-stack) in reverse order, i.e. from the most recent instruction to the starting instruction. (commands n) [n a positive integer] Print out the most recent n instructions (in the current state-stack), in reverse order. (commands x abbreviate-flag) Same as above, but if abbreviate-flag is non-NIL, then do not display commands between ``matching bookends''. See documentation for comm for an explanation of matching bookends. ~ev[] ~st[Remark]: If there are more than ~c[n] instructions in the state-stack, then ~c[(commands n)] is the same as ~c[commands] (and also, ~c[(commands n abb)] is the same as ~c[(commands nil abb)])." (if (and n (not (and (integerp n) (> n 0)))) (io? proof-checker nil state (n) (fms0 "*** The first optional argument to the COMMANDS command must ~ be a positive integer, but ~x0 is not.~|" (list (cons #\0 n)))) (let* ((indexed-instrs (raw-indexed-instrs 1 (if n (min n (length state-stack)) (length state-stack)) state-stack))) (print-commands (if evisc-p (evisc-indexed-instrs indexed-instrs) indexed-instrs) state)))) (define-pc-macro comm (&optional n) "display instructions from the current interactive session~/ ~bv[] Examples: comm (comm 10)~/ General Form: (comm &optional n) ~ev[] Prints out instructions in reverse order. This is actually the same as ~c[(commands n t)] ~-[] or, ~c[(commands nil t)] if ~c[n] is not supplied. As explained in the documentation for ~c[commands], the final argument of ~c[t] causes suppression of instructions occurring between so-called ``matching bookends,'' which we now explain. A ``begin bookend'' is an instruction of the form ~bv[] (COMMENT :BEGIN x . y). ~ev[] Similarly, an ``end bookend'' is an instruction of the form ~bv[] (COMMENT :END x' . y'). ~ev[] The ``name'' of the first bookend is ~c[x] and the ``name'' of the second bookend is ~c[x']. When such a pair of instructions occurs in the current state-stack, we call them ``matching bookends'' provided that they have the same name (i.e. ~c[x] equals ~c[x']) and if no other begin or end bookend with name ~c[x] occurs between them. The idea now is that ~c[comm] hides matching bookends together with the instructions they enclose. Here is a more precise explanation of this ``hiding''; probably there is no value in reading on! A ~c[comm] instruction hides bookends in the following manner. (So does a ~c[comment] instruction when its second optional argument is supplied and non-~c[nil].) First, if the first argument ~c[n] is supplied and not ~c[nil], then we consider only the last ~c[n] instructions from the state-stack; otherwise, we consider them all. Now the resulting list of instructions is replaced by the result of applying the following process to each pair of matching bookends: the pair is removed, together with everything in between the begin and end bookend of the pair, and all this is replaced by the ``instruction'' ~bv[] (\"***HIDING***\" :COMMENT :BEGIN name ...) ~ev[] where ~c[(comment begin name ...)] is the begin bookend of the pair. Finally, after applying this process to each pair of matching bookends, each begin bookend of the form ~c[(comment begin name ...)] that remains is replaced by ~bv[] (\"***UNFINISHED***\" :COMMENT :BEGIN name ...) . ~ev[]" (value (list 'commands n t))) (defun promote-guts (pc-state goals hyps x y no-flatten-flg) (change-pc-state pc-state :goals (cons (change goal (car goals) :hyps (append hyps (if no-flatten-flg (list x) (flatten-ands-in-lit x))) :conc y) (cdr goals)))) (define-pc-primitive promote (&optional do-not-flatten-flag) "move antecedents of conclusion's ~c[implies] term to top-level hypotheses~/ ~bv[] Examples: promote (promote t) ~ev[] For example, if the conclusion is ~c[(implies (and x y) z)], then after execution of ~c[promote], the conclusion will be ~c[z] and the terms ~c[x] and ~c[y] will be new top-level hypotheses.~/ ~bv[] General Form: (promote &optional do-not-flatten-flag) ~ev[] Replace conclusion of ~c[(implies hyps exp)] or ~c[(if hyps exp t)] with simply ~c[exp], adding ~c[hyps] to the list of top-level hypotheses. Moreover, if ~c[hyps] is viewed as a conjunction then each conjunct will be added as a separate top-level hypothesis. An exception is that if ~c[do-not-flatten-flag] is supplied and not ~c[nil], then only one top-level hypothesis will be added, namely ~c[hyps]. ~st[Remark]: You must be at the top of the conclusion in order to use this command. Otherwise, first invoke ~c[top]." (if current-addr (print-no-change2 "You must be at the top ~ of the goal in order to promote the ~ antecedents of an implication. Try TOP first.") (case-match conc (('implies x y) (mv (promote-guts pc-state goals hyps x y do-not-flatten-flag) state)) (('if x y *t*) (mv (promote-guts pc-state goals hyps x y do-not-flatten-flag) state)) (& (print-no-change2 "The goal must be of the form ~x0 or ~x1." (list (cons #\0 '(IMPLIES P Q)) (cons #\1 '(IF P Q T)))))))) (defun remove-by-indices (m indices lst) ;; (declare (xargs :guard (null (non-bounded-nums indices m (length lst))))) ;; this was ok for the original entry, but it's not preserved (if (consp lst) (if (member-equal m indices) (remove-by-indices (1+ m) indices (cdr lst)) (cons (car lst) (remove-by-indices (1+ m) indices (cdr lst)))) nil)) ;;; **** Should improve the following so that if form outputs a state or ;;; does return just one result, then fms0 isn't even called but instead ;;; an appropriate error message is printed. (define-pc-macro print (form &optional without-evisc) "print the result of evaluating the given form~/ ~bv[] Example: (print (append '(a b) '(c d))) Print the list (a b c d) to the terminal~/ General Forms: (print form) (print form t) ~ev[] Prettyprints the result of evaluating form. The evaluation of ~c[form] should return a single value that is not ~ilc[state] or a single-threaded object (~pl[stobj]). The optional second argument causes printing to be done without elision (so-called ``evisceration''; ~pl[evisc-tuple]). If the form you want to evaluate does not satisfy the criterion above, you should create an appropriate call of the ~c[lisp] command instead. Notice that this command always returns ~c[(mv nil nil state)] where the second result will always be ~c[REPLACED-STATE]." ; NOTE: The saved-output mechanism described in the Essay on Saved-output won't ; work here, because there is no call of io?. We can't call io? because form ; is arbitrary and hence we cannot check its variables. (let ((print-form `(fms0 "~|~y0~|" (list (cons #\0 ,form))))) (value `(lisp ,(if without-evisc `(without-evisc ,print-form) print-form))))) (defun bounded-integer-listp (i j lst) ;; If i is a non-integer, then it's -infinity. ;; If j is a non-integer, then it's +infinity. (if (consp lst) (and (integerp (car lst)) (if (integerp i) (if (integerp j) (and (<= i (car lst)) (<= (car lst) j)) (<= i (car lst))) (<= (car lst) j))) (null lst))) (defun fetch-term-and-cl (term addr cl) ;; Returns the subterm of TERM at address ADDR paired with a list ;; containing the tests governing that occurrence of the subterm plus ;; the literals of the input CL. However, if CL is T then we simply ;; return (mv nil t) (see also below). ;; I've assumed that the address is a list of positive integers. If ;; the address is not valid for diving into TERM according to ADDR, ;; then we return (mv nil t). Notice that ADDR is expected to be in ;; the correct order, while CL is in reverse order and the extension ;; of CL returned in the second position is also in reverse order. ;; For the funny contrapositive subcase of IMPLIES, note that ;; (implies (implies (and u (not x)) (equal y1 y2)) ;; (implies u (equal (implies y1 x) (implies y2 x)))) ;; is a tautology. However, the corresponding fact does not hold in ;; general for IF; it depends on x being boolean. (declare (xargs :guard (bounded-integer-listp 1 'infinity addr))) (cond ((eq cl t) (mv nil t)) ((null addr) (mv term cl)) ((or (variablep term) (fquotep term)) ;; can't dive any further (mv nil t)) ((and (integerp (car addr)) (< 0 (car addr)) (< (car addr) (length term))) (case-match term (('if t1 t2 t3) (cond ((= 1 (car addr)) (fetch-term-and-cl t1 (cdr addr) cl)) ((= 2 (car addr)) (fetch-term-and-cl t2 (cdr addr) (cons t1 cl))) (t (fetch-term-and-cl t3 (cdr addr) (cons (dumb-negate-lit t1) cl))))) (('implies t1 t2) (cond ((= 1 (car addr)) (fetch-term-and-cl t1 (cdr addr) (cons (dumb-negate-lit t2) cl))) (t (fetch-term-and-cl t2 (cdr addr) (cons t1 cl))))) (& (fetch-term-and-cl (nth (1- (car addr)) (fargs term)) (cdr addr) cl)))) (t (mv nil t)))) (defun fetch-term (term addr) ;; causes hard error when appropriate (mv-let (term cl) (fetch-term-and-cl term addr nil) (if (eq cl t) (er hard 'fetch-term "FETCH-TERM-AND-CL did not find a subterm of ~x0 at address ~x1." term addr) term))) (defun governors (term addr) (mv-let (term cl) (fetch-term-and-cl term addr nil) (declare (ignore term)) ;; note that cl could be T rather than a list of governors cl)) ;;;;;;!!!!!!! I should generalize the following to arbitrary equivalence stuff. (defun term-id-iff (term address iff-flg) ;; The property we want is that if one substitutes an equivalent subterm ;; of TERM at the given address (equivalent modulo the flag returned by ;; this function, that is), then the resulting term is equivalent modulo ;; the IFF-FLG argument to the original TERM. We assume that address is ;; a valid address for term. (*** This should really be a guard.) (if (null address) iff-flg ;; so, the term is a function application (term-id-iff (nth (car address) term) (cdr address) (cond ((eq (ffn-symb term) (quote if)) (if (= (car address) 1) t iff-flg)) ((member-eq (ffn-symb term) (quote (implies iff not))) t) (t nil))))) ;; The way abbreviations will work is as follows. For input, an ;; abbreviation variable is to be thought of as a placeholder for ;; literal substitution (*before* translation!). It was tempting to ;; think of abbreviation variables as standing for something else only ;; when they're in variable position, but the problem with that ;; approach is that we can't tell about the position until we've done ;; the translation (consider macro calls that look at the first ;; character, say, for example). On a pragmatic (implementation) ;; level, it's hard to see how to implement a translator that ;; substitutes for abbreviation variables only when they're in ;; variable position, except by modifying translate. On the other ;; hand, for untranslation the specification is only that ;; (trans (untrans x)) = x, where here translation is with respect ;; to abbreviations. Notice though that this spec messes things ;; up, because if x is (quote &v) then untrans of that is still ;; (quote &v) but then trans would remove the &v, if we use sublis ;; to deal with abbreviations. ;; So, I think I'll implement abbreviations as follows. There will ;; be a new "macro": ;; (defmacro ? (x) ;; (cdr (assoc-eq x (abbreviations)))) ;; Notice however that (abbreviations) generates a reference to ;; state, which isn't compatible with ? being a macro. So, I'll ;; stub it out: (defmacro ? (x) `(?-fn ',x)) (defstub ?-fn (x) t) ;; Now, translation will be followed by an appropriate substitution. ;; For convenience, abbreviations will be turned into an alist whose ;; pairs are of the form ((&-fn 'var) . term). (defun abbreviations-alist (abbreviations) (if (consp abbreviations) (cons (cons (fcons-term* '?-fn (kwote (caar abbreviations))) (cdar abbreviations)) (abbreviations-alist (cdr abbreviations))) nil)) (mutual-recursion (defun chk-?s (term ctx state) ;; There shouldn't be any ?-fns in term. (cond ((or (variablep term) (fquotep term)) (value nil)) ((eq (ffn-symb term) '?-fn) (case-match term ((& ('quote var)) (if (variablep var) (er soft ctx "The variable ~x0 is not among the current abbreviations." var) (er soft ctx "Expected a variable in place of ~x0." var))) (& (value (er hard ctx "Bad call of ?-FN, ~x0. ?-FN must be called on the quotation of ~ a variable." term))))) ((flambdap (ffn-symb term)) (er-progn (chk-?s (lambda-body (ffn-symb term)) ctx state) (chk-?s-lst (fargs term) ctx state))) (t (chk-?s-lst (fargs term) ctx state)))) (defun chk-?s-lst (term-lst ctx state) (if (consp term-lst) (er-progn (chk-?s (car term-lst) ctx state) (chk-?s-lst (cdr term-lst) ctx state)) (value nil))) ) (defun remove-?s (term abbreviations-alist ctx state) (let ((newterm (sublis-expr abbreviations-alist term))) (er-progn (chk-?s newterm ctx state) (value newterm)))) (defun translate-abb (x abbreviations ctx state) (mv-let (erp term state) (translate x t ; Since we only use this function in a logical context, we set ; logic-modep to t. t t ctx (w state) state) (if erp (mv erp term state) (remove-?s term (abbreviations-alist abbreviations) ctx state)))) (defmacro trans0 (x &optional abbreviations ctx) `(translate-abb ,x ,abbreviations ,(or ctx ''trans0) state)) (defun p-body (conc current-addr abbreviations state) (io? proof-checker nil state (abbreviations current-addr conc) (fms0 "~|~y0~|" (list (cons #\0 (untrans0 (fetch-term conc current-addr) (term-id-iff conc current-addr t) abbreviations)))))) (define-pc-help p () "prettyprint the current term~/ ~bv[] Example and General Form: p ~ev[]~/ Prettyprint the current term. The usual user syntax is used, so that for example one would see ~c[(and x y)] rather than ~c[(if x y 'nil)]. (See also ~c[pp].) Also, abbreviations are inserted where appropriate; see ~c[add-abbreviation]. The ``current term'' is the entire conclusion unless ~c[dive] commands have been given, in which case it may be a subterm of the conclusion. If all goals have been proved, a message saying so will be printed (as there will be no current ~c[term]!)." (when-goals (p-body (conc t) (current-addr t) (abbreviations t) state))) (define-pc-help pp () "prettyprint the current term~/ ~bv[] Example and General Form: pp ~ev[]~/ This is the same as ~c[p] (see its documentation), except that raw syntax (internal form) is used. So for example, one would see ~c[(if x y 'nil)] rather than ~c[(and x y)]. Abbreviations are however still inserted, as with ~c[p].~/" (when-goals (io? proof-checker nil state (state-stack) (fms0 "~|~y0~|" (list (cons #\0 (fetch-term (conc t) (current-addr t)))))))) (defun take-by-indices (m indices lst) ;; (declare (xargs :guard (null (non-bounded-nums indices m (length lst))))) ;; this was ok for the original entry, but it's not preserved (if (consp lst) (if (member-equal m indices) (cons (car lst) (take-by-indices (1+ m) indices (cdr lst))) (take-by-indices (1+ m) indices (cdr lst))) nil)) (defun print-hyps (indexed-hyps ndigits abbreviations state) (declare (xargs :guard (and (eqlable-alistp indexed-hyps) (integerp ndigits) (> ndigits 0)))) (if (null indexed-hyps) state (pprogn (io? proof-checker nil state (abbreviations ndigits indexed-hyps) (fms0 "~c0. ~y1~|" (list (cons #\0 (cons (caar indexed-hyps) ndigits)) (cons #\1 (untrans0 (cdar indexed-hyps) t abbreviations))))) (print-hyps (cdr indexed-hyps) ndigits abbreviations state)))) (defun some-> (lst n) ;; says whether some element of lst exceeds n (declare (xargs :guard (and (rational-listp lst) (rationalp n)))) (if lst (or (> (car lst) n) (some-> (cdr lst) n)) nil)) (defun print-hyps-top (indexed-hyps abbreviations state) (declare (xargs :guard (eqlable-alistp indexed-hyps))) (if (null indexed-hyps) (io? proof-checker nil state nil (fms0 "~|There are no top-level hypotheses.~|")) (print-hyps indexed-hyps (if (some-> (strip-cars indexed-hyps) 9) 2 1) abbreviations state))) (defun print-governors-top (indexed-hyps abbreviations state) (declare (xargs :guard (eqlable-alistp indexed-hyps))) (if (null indexed-hyps) (io? proof-checker nil state nil (fms0 "~|There are no governors.~|")) (print-hyps indexed-hyps (if (some-> (strip-cars indexed-hyps) 9) 2 1) abbreviations state))) (defun pair-indices (seed indices lst) ;; Returns a list of indices paired with the corresponding (1-based) element of ;; lst when in range. Seed is a starting integer; we do things this way ;; because we want the result sorted (and hence want to recurse on lst). (declare (xargs :guard (and (integerp seed) (true-listp lst) (bounded-integer-listp 1 (length lst) indices)))) (if lst (let ((rest-lst (pair-indices (1+ seed) indices (cdr lst)))) (if (member seed indices) (cons (cons seed (car lst)) rest-lst) rest-lst)) nil)) (define-pc-macro hyps (&optional hyps-indices govs-indices) "print the hypotheses~/ ~bv[] Examples: hyps -- print all (top-level) hypotheses (hyps (1 3) (2 4)) -- print hypotheses 1 and 3 and governors 2 and 4 (hyps (1 3) t) -- print hypotheses 1 and 3 and all governors~/ General Form: (hyps &optional hyps-indices govs-indices) ~ev[] Print the indicated top-level hypotheses and governors. (The notion of ``governors'' is defined below.) Here, ~c[hyps-indices] and ~c[govs-indices] should be lists of indices of hypotheses and governors (respectively), except that the atom ~c[t] may be used to indicate that one wants all hypotheses or governors (respectively). The list of ``governors'' is defined as follows. Actually, we define here the notion of the governors for a pair of the form ~c[]; we're interested in the special case where the term is the conclusion and the address is the current address. If the address is ~c[nil], then there are no governors, i.e., the list of governors is ~c[nil]. If the term is of the form ~c[(if x y z)] and the address is of the form ~c[(2 . rest)] or ~c[(3 . rest)], then the list of governors is the result of ~c[cons]ing ~c[x] or its negation (respectively) onto the list of governors for the pair ~c[] or the pair ~c[] (respectively). If the term is of the form ~c[(implies x y)] and the address is of the form ~c[(2 . rest)], then the list of governors is the result of ~c[cons]ing ~c[x] onto the list of governors for the pair ~c[]. Otherwise, the list of governors for the pair ~c[] is exactly the list of governors for the pair ~c[] where ~c[argn] is the ~c[n]th argument of ~c[term]. If all goals have been proved, a message saying so will be printed. (as there will be no current hypotheses or governors!). The ~c[hyps] command never causes an error. It ``succeeds'' (in fact its value is ~c[t]) if the arguments (when supplied) are appropriate, i.e. either ~c[t] or lists of indices of hypotheses or governors, respectively. Otherwise it ``fails'' (its value is ~c[nil])." (when-goals-trip (let* ((hyps (hyps t)) (len-hyps (length hyps)) (govs (and govs-indices;; for efficiency (governors (conc t) (current-addr t)))) (len-govs (length govs)) (abbs (abbreviations t)) (hyps-indices (or hyps-indices (null args)))) (cond ((not (or (eq hyps-indices t) (bounded-integer-listp 1 len-hyps hyps-indices))) (pprogn (io? proof-checker nil state (len-hyps hyps-indices) (fms0 "~|Bad hypothesis-list argument to HYPS, ~X0n. The ~ hypothesis-list argument should either be T or should be a ~ list of integers between 1 and the number of top-level ~ hypotheses, ~x1.~%" (list (cons #\0 hyps-indices) (cons #\n nil) (cons #\1 len-hyps)))) (value :fail))) ((not (or (eq govs-indices t) (bounded-integer-listp 1 len-govs govs-indices))) (pprogn (io? proof-checker nil state (len-govs govs-indices) (fms0 "~|Bad governors-list argument to HYPS,~% ~X0n.~%The ~ governors-list argument should either be T or should be a ~ list of integers between 1 and the number of top-level ~ governors, ~x1." (list (cons #\0 govs-indices) (cons #\n nil) (cons #\1 len-govs)))) (value :fail))) ((and (null hyps-indices) (null govs-indices)) (pprogn (io? proof-checker nil state nil (fms0 "~|You have specified no printing of either hypotheses or ~ governors! Perhaps you should read the documentation for ~ the HYPS command.~|")) (value :fail))) (t (let ((hyps-to-print (if (eq hyps-indices t) (count-off 1 hyps) (pair-indices 1 hyps-indices hyps))) (govs-to-print (if (eq govs-indices t) (count-off 1 govs) (pair-indices 1 govs-indices govs)))) (pprogn (if hyps-indices (pprogn (if (eq hyps-indices t) (io? proof-checker nil state nil (fms0 "~|*** Top-level hypotheses:~|")) (io? proof-checker nil state nil (fms0 "~|*** Specified top-level hypotheses:~|"))) (print-hyps-top hyps-to-print abbs state)) state) (if govs-indices (pprogn (if (eq govs-indices t) (io? proof-checker nil state nil (fms0 "~|~%*** Governors:~|")) (io? proof-checker nil state nil (fms0 "~|~%*** Specified governors:~|"))) (print-governors-top govs-to-print abbs state)) state) (value 'skip)))))))) (define-pc-primitive demote (&rest rest-args) "move top-level hypotheses to the conclusion~/ ~bv[] Examples: demote -- demote all top-level hypotheses (demote 3 5) -- demote hypotheses 3 and 5 ~ev[] For example, if the top-level hypotheses are ~c[x] and ~c[y] and the conclusion is ~c[z], then after execution of ~c[demote], the conclusion will be ~c[(implies (and x y) z)] and there will be no (top-level) hypotheses.~/ ~bv[] General Form: (demote &rest hyps-indices) ~ev[] Eliminate the indicated (top-level) hypotheses, but replace the conclusion ~c[conc] with ~c[(implies hyps conc)] where ~c[hyps] is the conjunction of the hypotheses that were eliminated. If no arguments are supplied, then all hypotheses are demoted, i.e. ~c[demote] is the same as ~c[(demote 1 2 ... n)] where ~c[n] is the number of top-level hypotheses. ~st[Remark]: You must be at the top of the conclusion in order to use this command. Otherwise, first invoke ~c[top]. Also, ~c[demote] fails if there are no top-level hypotheses or if indices are supplied that are out of range.~/" (cond (current-addr (print-no-change2 "You must be at the top of the conclusion in order to ~ demote hypotheses. Try TOP first.")) ((null hyps) (print-no-change2 "There are no top-level hypotheses.")) (t (let ((badindices (non-bounded-nums rest-args 1 (length hyps)))) (if badindices (print-no-change2 "The arguments to DEMOTE ~ must be indices of active top-level hypotheses, ~ but the following are not: ~&0." (list (cons #\0 badindices))) (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :hyps (if rest-args (remove-by-indices 1 rest-args hyps) nil) :conc (make-implication (if rest-args (take-by-indices 1 rest-args hyps) hyps) conc)) (cdr goals))) state)))))) (defun pair-keywords (keywords lst) (declare (xargs :guard (and (all-keywords-p keywords) (keyword-value-listp lst)))) ;; returns (mv alist rst) (if (consp keywords) (mv-let (alist rst) (pair-keywords (cdr keywords) lst) (let ((tail (assoc-keyword (car keywords) rst))) (if tail (mv (cons (cons (car tail) (cadr tail)) alist) ;; could use a remove1 version of the following, but who cares? (remove-keyword (car keywords) rst)) (mv alist rst)))) (mv nil lst))) (defun null-pool (pool) (cond ((null pool) t) ((eq (access pool-element (car pool) :tag) 'being-proved-by-induction) (null-pool (cdr pool))) (t nil))) (defun initial-pspv (term displayed-goal otf-flg ens wrld splitter-output) (change prove-spec-var *empty-prove-spec-var* :rewrite-constant (initial-rcnst-from-ens ens wrld splitter-output) :user-supplied-term term :displayed-goal displayed-goal :otf-flg otf-flg)) (defun pc-prove (term displayed-goal hints otf-flg ens wrld ctx state) ; This is exactly the same as the ACL2 PROVE function, except that we allow ; :bye objects in the tag-tree, there is no checking of the load mode, and the ; warning above. (prog2$ (initialize-brr-stack state) (er-let* ((ttree (let ((pspv (initial-pspv term displayed-goal otf-flg ens wrld (splitter-output))) (clauses (list (list term)))) (if (f-get-global 'in-verify-flg state) ;interactive (state-global-let* ((saved-output-p t) (saved-output-token-lst :all)) (pprogn (f-put-global 'saved-output-reversed nil state) (prove-loop clauses pspv hints ens wrld ctx state))) (prove-loop clauses pspv hints ens wrld ctx state))))) (er-progn (chk-assumption-free-ttree ttree ctx state) (value ttree))))) (defun sublis-equal (alist tree) (declare (xargs :guard (alistp alist))) (let ((pair (assoc-equal tree alist))) (if pair (cdr pair) (if (atom tree) tree (cons (sublis-equal alist (car tree)) (sublis-equal alist (cdr tree))))))) (defun abbreviations-alist-? (abbreviations) ;; Same as abbreviations-alist, except that we assume that we ;; haven't translated yet, and hence we use ? instead of ?-fn ;; and we don't quote the variable. (if (consp abbreviations) (cons (cons (fcons-term* '? (caar abbreviations)) (cdar abbreviations)) (abbreviations-alist-? (cdr abbreviations))) nil)) (defun find-?-fn (x) ;; x is not necessarily a term. Heuristically though it's useful ;; to be able to find all (?-fn var) subexpressions of x. (if (atom x) nil (if (eq (car x) '?-fn) (list (cadr x)) (union-equal (find-?-fn (car x)) (find-?-fn (cdr x)))))) (defun unproved-pc-prove-clauses (ttree) (reverse-strip-cdrs (tagged-objects :bye ttree) nil)) (defun prover-call (comm term-to-prove rest-args pc-state state) ;; We assume that the :otf-flg and :hints "hints" are locally inside ;; a variable called rest-args, which in fact are the arguments to the ;; instruction being processed. ;; Returns an error triple (mv erp-flg ttree state). (declare (xargs :guard (keywordp comm))) (let ((prover-call-abbreviations (access pc-state pc-state :abbreviations)) (prover-call-wrld (w state))) (let ((prover-call-pc-ens (make-pc-ens (access pc-state pc-state :pc-ens) state))) (mv-let (prover-call-pairs prover-call-tail) (pair-keywords '(:otf-flg :hints) rest-args) (if prover-call-tail (pprogn (print-no-change "The only keywords allowed in the arguments to the ~x0 command ~ are :otf-flg and :hints. Your ~ instruction ~x1 violates this requirement." (list (cons #\0 comm) (cons #\1 (make-pretty-pc-instr (cons comm rest-args))))) (mv t nil state)) (mv-let (prover-call-erp prover-call-hints state) (let ((un-?-hints (sublis-equal ;; *** someday I should do this all right (abbreviations-alist-? prover-call-abbreviations) (cdr (assoc-eq :hints prover-call-pairs))))) (let ((?-exprs (find-?-fn un-?-hints))) (if ?-exprs (pprogn (print-no-change "You appear to have attempted to use the following ~ abbreviation variable~#0~[~/~/s~], which however ~ ~#0~[~/is~/are~] not among ~ the current abbreviation variables (see SHOW-ABBREVIATIONS): ~&1." (list (cons #\0 (zero-one-or-more (length ?-exprs))) (cons #\1 ?-exprs))) (mv t nil state)) (pprogn (io? proof-checker nil state nil (fms0 "~|***** Now entering the theorem ~ prover *****~|")) (translate-hints+ 'proof-checker un-?-hints (default-hints prover-call-wrld) comm prover-call-wrld state))))) (if prover-call-erp (pprogn (print-no-change "Failed to translate hints successfully.") (mv t nil state)) (let ((prover-call-otf-flg (cdr (assoc-eq :otf-flg prover-call-pairs)))) (mv-let (prover-call-erp prover-call-ttree state) (pc-prove term-to-prove (untranslate term-to-prove t prover-call-wrld) prover-call-hints prover-call-otf-flg prover-call-pc-ens prover-call-wrld comm state) (pprogn (io? proof-checker nil state nil (fms0 "~%")) (if prover-call-erp (pprogn (print-no-change "Proof failed.") (mv t nil state)) (mv nil prover-call-ttree state)))))))))))) (defun make-new-goals (cl-set goal-name start-index) ;; assumes that every member of CL-SET is a non-empty true list (should be a guard) (if (consp cl-set) (cons (make goal :conc (car (last (car cl-set))) :hyps (dumb-negate-lit-lst (butlast (car cl-set) 1)) :current-addr nil :goal-name (cons goal-name start-index) :depends-on 1) (make-new-goals (cdr cl-set) goal-name (1+ start-index))) nil)) (defun same-goal (goal1 goal2) (and (equal (access goal goal1 :hyps) (access goal goal2 :hyps)) (equal (access goal goal1 :conc) (access goal goal2 :conc)))) (defun remove-byes-from-tag-tree (ttree) (remove-tag-from-tag-tree :bye ttree)) (define-pc-primitive prove (&rest rest-args) "call the ACL2 theorem prover to prove the current goal~/ ~bv[] Examples: prove -- attempt to prove the current goal (prove :otf-flg t :hints ((\"Subgoal 2\" :by foo) (\"Subgoal 1\" :use bar))) -- attempt to prove the current goal, with the indicated hints and with OTF-FLG set~/ General Form: (prove &rest rest-args) ~ev[] Attempt to prove the current goal, where ~c[rest-args] is as in the keyword arguments to ~c[defthm] except that only ~c[:hints] and ~c[:otf-flg] are allowed. The command succeeds exactly when the corresponding ~c[defthm] would succeed, except that it is all right for some goals to be given ``bye''s. Each goal given a ``bye'' will be turned into a new subgoal. (~l[hints] for an explanation of ~c[:by] hints.) ~st[Remark:] Use ~c[(= t)] instead if you are not at the top of the conclusion. Also note that if there are any hypotheses in the current goal, then what is actually attempted is a proof of ~c[(implies hyps conc)], where ~c[hyps] is the conjunction of the top-level hypotheses and ~c[conc] is the goal's conclusion. ~st[Remark:] It is allowed to use abbreviations in the hints." (cond (current-addr (print-no-change2 "The PROVE command should only be used at ~ the top. Use (= T) if that is what you want.")) ((not (keyword-value-listp rest-args)) (print-no-change2 "The argument list for the PROVE command should ~ be empty or a list of even length with keywords in the odd ~ positions. See the documentation for examples and details.")) (t (mv-let (erp ttree state) (prover-call :prove (make-implication hyps conc) rest-args pc-state state) (cond (erp (mv nil state)) (t (let* ((new-clauses (unproved-pc-prove-clauses ttree)) (new-goals (make-new-goals new-clauses goal-name depends-on)) (len-new-goals (length new-goals))) (cond ((and (equal len-new-goals 1) (same-goal (car new-goals) (car goals))) (print-no-change2 "Exactly one new goal was created by your PROVE ~ command, and it has exactly the same hypotheses ~ and conclusion as did the current goal.")) ((tagged-objects 'assumption ttree) ; See the comment in define-pc-primitive about leaving the top goal on the top ; of the :goals stack. (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :conc *t* :depends-on (+ (access goal (car goals) :depends-on) len-new-goals)) (append new-goals (cdr goals))) :local-tag-tree (remove-byes-from-tag-tree ttree)) state)) (t (mv (change-pc-state pc-state :goals (append new-goals (cdr goals)) :local-tag-tree (remove-byes-from-tag-tree ttree)) state)))))))))) (defun add-string-val-pair-to-string-val-alist (key key1 val alist) ; Key is a string (typically a goal name) and key1 is a keyword (presumably a ; hint keyword). Alist associates keys (strings) with keyword alists. ; Associate key1 with val in the keyword alist associated with key, unless key1 ; is already bound in that keyword alist in which case just return alist. (cond ((null alist) (list (list key key1 val))) ((and (stringp (caar alist)) (string-equal key (caar alist))) (if (assoc-keyword key1 (cdar alist)) alist (cons (list* (caar alist) key1 val (cdar alist)) (cdr alist)))) (t (cons (car alist) (add-string-val-pair-to-string-val-alist key key1 val (cdr alist)))))) (defconst *bash-skip-forcing-round-hints* '(("[1]Goal" :by nil) ("[1]Subgoal 1" :by nil) ("[1]Subgoal 2" :by nil) ("[1]Subgoal 3" :by nil) ("[1]Subgoal 4" :by nil) ("[1]Subgoal 5" :by nil) ("[1]Subgoal 6" :by nil) ("[1]Subgoal 7" :by nil) ("[1]Subgoal 8" :by nil) ("[1]Subgoal 9" :by nil) ("[1]Subgoal 10" :by nil) ("[1]Subgoal 11" :by nil) ("[1]Subgoal 12" :by nil) ("[1]Subgoal 13" :by nil) ("[1]Subgoal 14" :by nil) ("[1]Subgoal 15" :by nil))) (define-pc-atomic-macro bash (&rest hints) "call the ACL2 theorem prover's simplifier~/ ~bv[] Examples: bash -- attempt to prove the current goal by simplification alone (bash (\"Subgoal 2\" :by foo) (\"Subgoal 1\" :use bar)) -- attempt to prove the current goal by simplification alone, with the indicated hints~/ General Form: (bash &rest hints) ~ev[] Call the theorem prover's simplifier, creating a subgoal for each resulting goal. Notice that unlike ~c[prove], the arguments to ~c[bash] are spread out, and are all hints. ~c[Bash] is similar to ~c[reduce] in that neither of these allows induction. But ~c[bash] only allows simplification, while ~c[reduce] allows processes ~c[eliminate-destructors], ~c[fertilize], ~c[generalize], and ~c[eliminate-irrelevance]. ~st[Remark:] All forcing rounds will be skipped (unless there are more than 15 subgoals generated in the first forcing round, an injustice that should be rectified, but might remain unless there is pressure to fix it)." (if (alistp hints) (value (list :prove :hints (append *bash-skip-forcing-round-hints* (add-string-val-pair-to-string-val-alist "Goal" ;; only preprocess and simplify are allowed :do-not (list 'quote '(generalize eliminate-destructors fertilize eliminate-irrelevance)) (add-string-val-pair-to-string-val-alist "Goal" :do-not-induct 'proof-checker hints))) :otf-flg t)) (pprogn (print-no-change "A BASH instruction must be of the form~%~ ~ ~ (:BASH (goal_name_1 ...) ... (goal_name_n ...)),~%and hence ~ your instruction,~%~ ~ ~x0,~%is not legal." (list (cons #\0 (cons :bash hints)))) (value :fail)))) (define-pc-primitive dive (n &rest rest-addr) "move to the indicated subterm~/ ~bv[] Examples: (DIVE 1) -- assign the new current subterm to be the first argument of the existing current subterm (DIVE 1 2) -- assign the new current subterm to be the result of first taking the 1st argument of the existing current subterm, and then the 2nd argument of that ~ev[] For example, if the current subterm is ~bv[] (* (+ a b) c), ~ev[] then after ~c[(dive 1)] it is ~bv[] (+ a b). ~ev[] If after that, then ~c[(dive 2)] is invoked, the new current subterm will be ~bv[] b. ~ev[] Instead of ~c[(dive 1)] followed by ~c[(dive 2)], the same current subterm could be obtained by instead submitting the single instruction ~c[(dive 1 2)].~/ ~bv[] General Form: (dive &rest naturals-list) ~ev[] If ~c[naturals-list] is a non-empty list ~c[(n_1 ... n_k)] of natural numbers, let the new current subterm be the result of selecting the ~c[n_1]-st argument of the current subterm, and then the ~c[n_2]-th subterm of that, ..., finally the ~c[n_k]-th subterm. ~st[Remark:] ~c[Dive] is related to the command ~c[pp], in that the diving is done according to raw (translated, internal form) syntax. Use the command ~c[dv] if you want to dive according to the syntax displayed by the command ~c[p]. Note that ~c[(dv n)] can be abbreviated by simply ~c[n]." (if (not (bounded-integer-listp 1 'infinity args)) (print-no-change2 "The arguments to DIVE must all be positive integers.") (mv-let (subterm cl) (fetch-term-and-cl (fetch-term conc current-addr) args nil) (declare (ignore subterm)) (if (eq cl t) (print-no-change2 "Unable to DIVE according to the address~%~ ~ ~y0." (list (cons #\0 (cons n rest-addr)))) (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :current-addr (append (access goal (car goals) :current-addr) args)) (cdr goals))) state))))) ; Keep this in sync with translate-in-theory-hint. (define-pc-atomic-macro split () "split the current goal into cases~/ ~bv[] Example: split ~ev[] For example, if the current goal has one hypothesis ~c[(or x y)] and a conclusion of ~c[(and a b)], then ~c[split] will create four new goals: ~bv[] one with hypothesis X and conclusion A one with hypothesis X and conclusion B one with hypothesis Y and conclusion A one with hypothesis Y and conclusion B.~/ General Form: SPLIT ~ev[] Replace the current goal by subgoals whose conjunction is equivalent (primarily by propositional reasoning) to the original goal, where each such goal cannot be similarly split. ~st[Remark:] The new goals will all have their hypotheses promoted; in particular, no conclusion will have a top function symbol of ~c[implies]. Also note that ~c[split] will fail if there is exactly one new goal created and it is the same as the existing current goal. The way ~c[split] really works is to call the ACL2 theorem prover with only simplification (and preprocessing) turned on, and with only a few built-in functions (especially, propositional ones) enabled, namely, the ones in the list ~c[(theory 'minimal-theory)]. However, because the prover is called, type-set reasoning can be used to eliminate some cases. For example, if ~c[(true-listp x)] is in the hypotheses, then probably ~c[(true-listp (cdr x))] will be reduced to ~c[t]." (value '(:prove :hints (("Goal" :do-not-induct proof-checker :do-not '(generalize eliminate-destructors fertilize eliminate-irrelevance) :in-theory (theory 'minimal-theory)))))) (define-pc-primitive add-abbreviation (var &optional raw-term) "add an abbreviation~/ Example: ~c[(add-abbreviation v (* x y))] causes future occurrences of ~c[(* x y)] to be printed as ~c[(? v)], until (unless) a corresponding invocation of ~c[remove-abbreviations] occurs. In this case we say that ~c[v] ``abbreviates'' ~c[(* x y)].~/ ~bv[] General Form: (add-abbreviation var &optional raw-term) ~ev[] Let ~c[var] be an abbreviation for ~c[raw-term], if ~c[raw-term] is supplied, else for the current subterm. Note that ~c[var] must be a variable that does not already abbreviate some term. A way to think of abbreviations is as follows. Imagine that whenever an abbreviation is added, say ~c[v] abbreviates ~c[expr], an entry associating ~c[v] to ~c[expr] is made in an association list, which we will call ``~c[*abbreviations-alist*]''. Then simply imagine that ~c[?] is a function defined by something like: ~bv[] (defun ? (v) (let ((pair (assoc v *abbreviations-alist*))) (if pair (cdr pair) (error ...)))) ~ev[] Of course the implementation isn't exactly like that, since the ``constant'' ~c[*abbreviations-alist*] actually changes each time an ~c[add-abbreviation] instruction is successfully invoked. Nevertheless, if one imagines an appropriate redefinition of the ``constant'' ~c[*abbreviations-alist*] each time an ~c[add-abbreviation] is invoked, then one will have a clear model of the meaning of such an instruction. The effect of abbreviations on output is that before printing a term, each subterm that is abbreviated by a variable ~c[v] is first replaced by ~c[(? v)]. The effect of abbreviations on input is that every built-in proof-checker command accepts abbreviations wherever a term is expected as an argument, i.e., accepts the syntax ~c[(? v)] whenever ~c[v] abbreviates a term. For example, the second argument of ~c[add-abbreviation] may itself use abbreviations that have been defined by previous ~c[add-abbreviation] instructions. See also ~c[remove-abbreviations] and ~c[show-abbreviations]." (mv-let (erp term state) (if (cdr args) (trans0 raw-term abbreviations :add-abbreviation) (value (fetch-term conc current-addr))) (cond (erp (mv nil state)) ((variablep var) (if (assoc-eq var abbreviations) (print-no-change2 "The abbreviation ~x0 has already been used, and stands for ~x1." (list (cons #\0 var) (cons #\1 (untrans0 (cdr (assoc-eq var abbreviations)))))) (mv (change-pc-state pc-state :abbreviations (cons (cons var term) abbreviations)) state))) (t (print-no-change2 "An abbreviation must be a variable, but ~x0 is not." (list (cons #\0 var))))))) (defun not-in-domain-eq (lst alist) (declare (xargs :guard (if (symbol-listp lst) (alistp alist) (symbol-alistp alist)))) (if (consp lst) (if (assoc-eq (car lst) alist) (not-in-domain-eq (cdr lst) alist) (cons (car lst) (not-in-domain-eq (cdr lst) alist))) nil)) (define-pc-primitive remove-abbreviations (&rest vars) "remove one or more abbreviations~/ ~bv[] Examples: remove-abbreviations -- remove all abbreviations (remove-abbreviations v w) -- assuming that V and W currently abbreviate terms, then they are ``removed'' in the sense that they are no longer considered to abbreviate those terms~/ General Forms: (remove-abbreviations &rest vars) ~ev[] If vars is not empty (i.e., not ~c[nil]), remove the variables in ~c[vars] from the current list of abbreviations, in the sense that each variable in ~c[vars] will no longer abbreviate a term. ~st[Remark:] The instruction fails if at least one of the arguments fails to be a variable that abbreviates a term. See also the documentation for ~c[add-abbreviation], which contains a discussion of abbreviations in general, and ~c[show-abbreviations]." (if (null abbreviations) (print-no-change2 "There are currently no abbreviations.") (let ((badvars (and args (not-in-domain-eq vars abbreviations)))) (if (and args badvars) (print-no-change2 "The variable~#0~[~/~/s~] ~&1 ~ ~#0~[~/is not currently an abbreviation variable~/~ are not currently abbreviation variables~]." (list (cons #\0 (zero-one-or-more (length badvars))) (cons #\1 badvars))) (mv (change-pc-state pc-state :abbreviations (if args (delete-assoc-eq-lst vars abbreviations) nil)) state))))) (defun print-abbreviations (vars abbreviations state) ;; Here abbreviations can contain junky pairs. (declare (xargs :guard (and (true-listp vars) (symbol-alistp abbreviations)))) (if (null vars) state (pprogn (io? proof-checker nil state nil (fms0 "~%")) (let ((pair (assoc-equal (car vars) abbreviations))) (if (null pair) ;; then this pair is junk (io? proof-checker nil state (vars) (fms0 "*** ~x0 does not abbreviate a term.~|" (list (cons #\0 (car vars))))) (let ((untrans-1 (untrans0 (cdr pair))) (untrans-2 (untrans0 (cdr pair) nil (delete-assoc-eq (car pair) abbreviations)))) (pprogn (io? proof-checker nil state (pair) (fms0 "(? ~x0) is an abbreviation for:~%~ ~ " (list (cons #\0 (car pair))))) (io? proof-checker nil state (untrans-1) (fms0 "~y0~|" (list (cons #\0 untrans-1)) 2)) (if (equal untrans-1 untrans-2) state (pprogn (io? proof-checker nil state nil (fms0 "i.e. for~%~ ~ ")) (io? proof-checker nil state (untrans-2) (fms0 "~y0~|" (list (cons #\0 untrans-2)) 2)))))))) (print-abbreviations (cdr vars) abbreviations state)))) (define-pc-help show-abbreviations (&rest vars) "display the current abbreviations~/ ~bv[] Examples: (show-abbreviations v w) -- assuming that v and w currently abbreviate terms, then this instruction displays them together with the terms they abbreviate show-abbreviations -- display all abbreviations ~ev[] See also ~c[add-abbreviation] and ~c[remove-abbreviations]. In particular, the documentation for ~c[add-abbreviation] contains a general discussion of abbreviations.~/ ~bv[] General Form: (show-abbreviations &rest vars) ~ev[] Display each argument in ~c[vars] together with the term it abbreviates (if any). If there are no arguments, i.e. the instruction is simply ~c[show-abbreviations], then display all abbreviations together with the terms they abbreviate. If the term abbreviated by a variable, say ~c[v], contains a proper subterm that is also abbreviate by (another) variable, then both the unabbreviated term and the abbreviated term (but not using ~c[(? v)] to abbreviate the term) are displayed with together with ~c[v]." (if (null (abbreviations t)) (io? proof-checker nil state nil (fms0 "~|There are currently no abbreviations.~%")) (print-abbreviations (or vars (strip-cars (abbreviations t))) (abbreviations t) state))) (defun drop-from-end (n l) (declare (xargs :guard (and (integerp n) (not (< n 0)) (true-listp l) (<= n (length l))))) (take (- (length l) n) l)) (define-pc-primitive up (&optional n) "move to the parent (or some ancestor) of the current subterm~/ ~bv[] Examples: if the conclusion is (= x (* (- y) z)) and the current subterm is y, then we have: up or (up 1) -- the current subterm becomes (- y) (up 2) -- the current subterm becomes (* (- y) z) (up 3) -- the current subterm becomes the entire conclusion (up 4) -- no change; can't go up that many levels~/ General Form: (up &optional n) ~ev[] Move up ~c[n] levels in the conclusion from the current subterm, where ~c[n] is a positive integer. If ~c[n] is not supplied or is ~c[nil], then move up 1 level, i.e., treat the instruction as ~c[(up 1)]. See also ~c[dive], ~c[top], ~c[nx], and ~c[bk]." (let ((n (or n 1))) (cond ((null current-addr) (print-no-change2 "Already at the top.")) ((not (and (integerp n) (> n 0))) (print-no-change2 "If UP is supplied with an argument, it must be ~ a positive integer or NIL, unlike ~x0." (list (cons #\0 n)))) ((<= n (length current-addr)) (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :current-addr (drop-from-end n current-addr)) (cdr goals))) state)) (t (print-no-change2 "Can only go up ~x0 level~#1~[~/~/s~]." (list (cons #\0 (length current-addr)) (cons #\1 (zero-one-or-more (length current-addr))))))))) (define-pc-atomic-macro top () "move to the top of the goal~/ ~bv[] Example and General Form: top ~ev[] For example, if the conclusion is ~c[(= x (* (- y) z))] and the current subterm is ~c[y], then after executing ~c[top], the current subterm will be the same as the conclusion, i.e., ~c[(= x (* (- y) z))].~/ ~c[Top] is the same as ~c[(up n)], where ~c[n] is the number of times one needs to execute ~c[up] in order to get to the top of the conclusion. The ~c[top] command fails if one is already at the top of the conclusion. See also ~c[up], ~c[dive], ~c[nx], and ~c[bk]." (when-goals-trip (let ((current-addr (current-addr t))) (value (list :up (length current-addr)))))) (defmacro expand-address-recurse (&key (ans '(cons (car addr) rest-addr)) (new-addr '(cdr addr)) (new-raw-term '(nth (car addr) raw-term)) (new-term '(nth (car addr) term)) (new-iff-flg 'nil) (new-accumulated-addr-r '(cons (car addr) accumulated-addr-r))) `(mv-let (erp rest-addr) (expand-address ,new-addr ,new-raw-term ,new-term abbreviations ,new-iff-flg ,new-accumulated-addr-r wrld) (if erp (mv erp rest-addr) (mv nil ,ans)))) (defmacro dive-once-more-error () '(mv "When diving to subterm ~x0 using address ~x1, ~ the additional dive to ~x2 was impossible." (list (cons #\0 raw-term) (cons #\1 (reverse accumulated-addr-r)) (cons #\2 (car addr))))) (defun abbreviation-raw-term-p (x) (and (consp x) (eq (car x) '?))) (defmacro addr-recur (pos b) `(if (integerp ,pos) (mv-let (addr new-term new-iff-flg not-flg) ,b (if (stringp addr) (mv addr nil nil nil) (mv (cons ,pos addr) new-term new-iff-flg not-flg))) (if (eq ,pos 'not) ,(case-match b (('mv 'nil x y 'nil) `(mv nil ,x ,y t)) (& '(mv "a NOT term unexpected by the code; sorry" nil nil nil))) (mv ,pos nil nil nil)))) (defun or-addr (n term iff-flg) ; Warning: Keep this in sync with untranslate-or and its use in untranslate1. ; See and-addr, which has a corresponding spec except that it is applied to ; terms that untranslate as AND calls, where the present function is for OR ; instead. (case-match term (('if x1 x1 x2) ; see untranslate1 (prog2$ x1 ; otherwise we get a "not used" complaint (cond ((int= n 1) (mv "an ambiguous dive to first arg of an OR" nil nil nil)) ((int= n 2) (addr-recur 3 (or-addr (1- n) x2 iff-flg))) (t (mv "an index that is out of range" nil nil nil))))) (('if x1 x2 *t*) ; see untranslate1 (cond ((int= n 1) (cond ((and (nvariablep x1) (not (fquotep x1)) (eq (ffn-symb x1) 'not)) (mv '(1) x1 t t)) (t (mv "an unexpected case of diving to first argument: for ~ an if-then-else term with THEN branch of nil, the ~ TEST was expected to be a call of NOT." nil nil nil)))) (t (addr-recur 2 (or-addr (1- n) x2 iff-flg))))) (('if x1 *t* x2) ; see untranslate1 (cond ((int= n 1) (mv '(1) x1 t nil)) (t (addr-recur 3 (or-addr (1- n) x2 iff-flg))))) (& (cond ((int= n 1) ; presumably in a recursive call of this function (mv nil term iff-flg nil)) (t (mv "a non-IF term encountered when diving to the first argument ~ (perhaps because your DV argument was greater than the ~ number of disjuncts)." nil nil nil)))))) (defun and-addr (n term iff-flg) ; Warning: Keep this in sync with untranslate-and and its use in untranslate1. ; We assume that term has already been abbreviated. To dive via n into the ; given translated term, which untranslates to an AND expression, we use the ; address returned by this function, dive-addr. This value is the first in the ; multiple values that we return: (mv dive-addr new-term new-iff-flg ; finish-not-p), where new-term and new-iff-flg are the term after the dive by ; addr, and finish-not-p is t if an additional dive into a NOT call is required ; for the corresponding untranslated term so that the result matches up with ; using dive-addr on the translated term. (That is, user should provide a next ; address of 1 after n so that the dive can be completed. The new-term ; returned here "assumes" that this further dive has already been done.) (case-match term (('if *t* x2 *nil*) ; see untranslate-and (addr-recur 2 (and-addr n x2 iff-flg))) (('if x1 x2 *nil*) (cond ((and iff-flg (equal x2 *t*)) ; see untranslate-and (addr-recur 1 (and-addr n x1 t))) ((int= n 1) (mv '(1) x1 t nil)) (t (addr-recur 2 (and-addr (1- n) x2 iff-flg))))) (('if x1 *nil* x2) (cond ((int= n 1) (cond ((and (nvariablep x1) (not (fquotep x1)) (eq (ffn-symb x1) 'not)) (mv '(1) x1 t t)) (t (mv "an unexpected case of diving to first argument: for ~ an if-then-else term with THEN branch of nil, the ~ TEST was expected to be a call of NOT" nil nil nil)))) (t (addr-recur 3 (and-addr (1- n) x2 iff-flg))))) (& (cond ((int= n 1) ; presumably in a recursive call of this function (mv nil term iff-flg nil)) (t (mv "a non-IF term encountered when diving to the first argument ~ (perhaps because your DV argument was greater than the ~ number of conjuncts)" nil nil nil)))))) (table dive-into-macros-table nil nil :guard (and (symbolp key) (or (symbolp val) ; a function to call (integerp val) (null val)))) (defmacro add-dive-into-macro (name val) ":Doc-Section switches-parameters-and-modes associate ~il[proof-checker] diving function with macro name~/ ~bv[] Examples: (add-dive-into-macro cat expand-address-cat) ~ev[] This feature is used so that the ~il[proof-checker]'s ~c[DV] command and numeric diving commands (e.g., ~c[3]) will dive properly into subterms. Please ~pl[dive-into-macros-table].~/~/" `(table dive-into-macros-table ',name ',val)) (defmacro remove-dive-into-macro (name) ":Doc-Section switches-parameters-and-modes removes association of ~il[proof-checker] diving function with macro name~/ ~bv[] Example: (remove-dive-into-macro logand) ~ev[] This feature undoes the effect of ~ilc[add-dive-into-macro], which is used so that the ~il[proof-checker]'s ~c[DV] command and numeric diving commands (e.g., ~c[3]) will dive properly into subterms. Please ~pl[add-dive-into-macro] and especially ~pl[dive-into-macros-table].~/~/" `(table dive-into-macros-table ',name nil)) (defun dive-into-macros-table (wrld) ":Doc-Section switches-parameters-and-modes right-associated function information for the ~il[proof-checker]~/ ~bv[] Examples: ACL2 !>(dive-into-macros-table (w state)) ((CAT . EXPAND-ADDRESS-CAT) (LXOR . EXPAND-ADDRESS-LXOR) ~ev[] This table associates macro names with functions used by the ~il[proof-checker]'s ~c[DV] and numeric diving commands (e.g., ~c[3]) in order to dive properly into subterms. ~l[proof-checker], in particular the documentation for ~c[DV]. This table can be extended easily. ~l[add-dive-into-macro] and also ~pl[remove-dive-into-macro]. The symbol associated with a macro should be a function symbol taking four arguments, in this order: ~bf[] ~c[car-addr] ; the first number in the list given to the ~il[proof-checker]'s ~c[DV] command ~c[raw-term] ; the untranslated term into which we will dive ~c[term] ; the translated term into which we will dive ~c[wrld] ; the current ACL2 logical ~il[world] ~ef[] The function will normally return a list of positive integers, representing the (one-based) address for diving into ~c[term] that corresponds to the single-address dive into ~c[raw-term] by ~c[car-address]. However, it can return ~c[(cons str alist)], where ~c[str] is a string suitable for ~ilc[fmt] and ~c[args] is the corresponding alist for ~ilc[fmt]. Referring to the example above, ~c[expand-address-cat] would be such a function, which will be called on ~c[raw-term] values that are calls of ~c[cat]. See the community book ~c[books/misc/rtl-untranslate.lisp] for the definition of such a function. ~l[table] for a general discussion of tables.~/~/" (declare (xargs :guard (plist-worldp wrld))) (table-alist 'dive-into-macros-table wrld)) (defun rassoc-eq-as-car (key alist) (cond ((endp alist) nil) ((eq key (car (cdr (car alist)))) (car alist)) (t (rassoc-eq-as-car key (cdr alist))))) (defun expand-address (addr raw-term term abbreviations iff-flg accumulated-addr-r wrld) ; This definition roughly parallels the definition of untranslate. It normally ; returns (mv nil new-addr), where new-addr is an address appropriate for ; diving into term that corresponds (in a translated setting) to use of the ; given addr to dive into raw-term (in an untranslated setting). However, this ; function can return (mv string fmt-alist) or (mv t hard-error) when there is ; an error. We keep accumulated-addr-r as the raw address already traversed, ; in reverse order, only for error messages. ; It's tempting to have a guard of (equal raw-term (untrans0 term iff-flg ; abbreviations)). We make some attempt to maintain this invariant. (cond ((or (null addr) (equal addr '(0))) (mv nil nil)) ((abbreviation-raw-term-p raw-term) ; The car of addr should be 0 or 1, but we forgivingly strip off whatever it ; is. By the way, it doesn't make a whole lot of sense for the cdr of addr to ; be anything other than nil (else why is dv being used?), but we won't enforce ; that here. (let ((pair (assoc-eq (cadr raw-term) abbreviations))) (if pair (expand-address (cdr addr) (cdr pair) term (remove1-equal pair abbreviations) iff-flg (cons (car addr) accumulated-addr-r) wrld) (mv t (er hard 'expand-address "Found abbreviation variable ~x0 that is not in the ~ current abbreviations alist, ~x1." (cadr raw-term) abbreviations))))) ((not (and (integerp (car addr)) (< 0 (car addr)))) (mv "All members of an address must be positive integers (except ~ that 0 is allowed in circumstances involving CASE, COND, and ~ abbreviations, which do not apply here). ~x0 violates this ~ requirement." (list (cons #\0 (car addr))))) ((or (variablep raw-term) (fquotep raw-term) (not (< (car addr) (length raw-term)))) (dive-once-more-error)) ((flambda-applicationp term) ; Raw-term is of the form ; (let ((var_0 term_0) ... (var_k-1 term_k-1)) body) ; and term is of the form ; ((lambda (var_0 ... var_k-1) body') term_0' ... term_k-1') ; where body' and termi' are the translation of body through termi, ; respectively. We cannot dive into the lambda, but we can dive into some ; term_i. So the DV command must be of the form (DV 1 n 1 . rest) for ; 0<=n<=k-1, which tells us to apply (DV . rest) after diving to term_n, which ; corresponds to position n+1 of the translated term. (cond ((eql (car addr) 2) (mv "Unable to dive to the body of a LET, which is really part of ~ the function symbol of the translated LAMBDA term." nil)) ((and (consp raw-term) (eq (car raw-term) 'let) ; so we assume raw-term is well-formed (>= (length addr) 3) (eql (car addr) 1) (natp (cadr addr)) (< (cadr addr) (length (cadr raw-term))) (member (caddr addr) '(0 1))) (cond ((eql (caddr addr) 0) (mv "Unable to dive to a variable of a LET." nil)) (t (expand-address-recurse :ans (cons (1+ (cadr addr)) rest-addr) :new-addr (cdddr addr) :new-raw-term (nth 1 (nth (cadr addr) (nth 1 raw-term))) :new-term (nth (1+ (car addr)) term) :new-accumulated-addr-r (cons (1+ (car addr)) accumulated-addr-r))))) (t (mv "Unable to expand LAMBDA (LET) term." nil)))) ((atom raw-term) (mv t (er hard 'expand-address "Surprise! Found an unexpected raw-term atom, ~x0." raw-term))) (t (let ((dive-fn (cdr (assoc-eq (car raw-term) (dive-into-macros-table wrld))))) (cond (dive-fn (mv-let (erp val) (ev-fncall-w dive-fn (list (car addr) raw-term term wrld) wrld nil nil t nil t) (cond ((or erp (stringp (car val))) (mv (car val) (cdr val))) (t (expand-address-recurse :ans (append val rest-addr) :new-iff-flg nil :new-term (fetch-term term val)))))) ((or (eq (car term) 'list) (let ((pair (rassoc-eq-as-car (car raw-term) (untrans-table wrld)))) (and pair (eql (arity (car pair) wrld) 2)))) ; E.g., (append a b c d) is (binary-append a (binary-append b (binary-append c ; d))), so diving 3 into this (to c) generates address (2 2 1), but diving 4 ; generates address (2 2 2), not (2 2 2 1). (let* ((lst (cond ((= (car addr) (1- (length raw-term))) (make-list (1- (car addr)) :initial-element 2)) (t (append (make-list (1- (car addr)) :initial-element 2) '(1))))) (subterm (fetch-term term lst))) (if subterm (expand-address-recurse :ans (append lst rest-addr) :new-iff-flg nil :new-term subterm) (dive-once-more-error)))) (t (case (car raw-term) (list* (let* ((lst (make-list (1- (car addr)) :initial-element 2)) (subterm (fetch-term term lst))) (if subterm (expand-address-recurse :ans (append lst rest-addr) :new-iff-flg nil :new-term subterm) (dive-once-more-error)))) (<= ; Note that (<= x y) translates to (not (< y x)). (cond ((not (member (car addr) '(1 2))) (dive-once-more-error)) ((= (car addr) 1) (expand-address-recurse :ans (cons 1 (cons 2 rest-addr)) :new-iff-flg nil :new-term (nth 2 (nth 1 term)))) (t ; (= (car addr) 2) (expand-address-recurse :ans (cons 1 (cons 1 rest-addr)) :new-iff-flg nil :new-term (nth 1 (nth 1 term)))))) ((and or) (mv-let (and-or-addr new-term new-iff-flg finish-not-p) (if (eq (car raw-term) 'and) (and-addr (car addr) (abbreviate term abbreviations) iff-flg) (or-addr (car addr) (abbreviate term abbreviations) iff-flg)) (cond ((stringp and-or-addr) (mv "The dive via address ~x0 brings us to the ~x4 ~ term~%~ ~ ~y1,~|~%which translates to~%~ ~ ~ ~y2.~|~%The requested dive into this ~x4 term ~ is problematic, because of ~@3. Try using ~ DIVE instead (after using PP to find the ~ appropriate address)." (list (cons #\0 (reverse accumulated-addr-r)) (cons #\1 raw-term) (cons #\2 term) (cons #\3 and-or-addr) (cons #\4 (car raw-term))))) (finish-not-p (cond ((and (cdr addr) (int= (cadr addr) 1)) (expand-address-recurse :ans (append and-or-addr rest-addr) :new-addr (cddr addr) :new-term new-term :new-iff-flg new-iff-flg :new-accumulated-addr-r (cons 1 (cons (car addr) accumulated-addr-r)))) (t (mv "The dive via address ~x0 apparently brings ~ us to the NOT term ~x1, which does not ~ actually exist in the internal syntax of the ~ current term, namely:~%~x2. Try using DIVE ~ instead (after using PP to find the ~ appropriate address)." (list (cons #\0 (reverse (cons (car addr) accumulated-addr-r))) (cons #\1 (nth (car addr) raw-term)) (cons #\2 term)))))) (t (expand-address-recurse :ans (append and-or-addr rest-addr) :new-term new-term :new-iff-flg new-iff-flg))))) (case ; For example, ; (case a (b c) (d e) ((f g) h) (otherwise k)) ; translates to ; (IF (EQL A 'B) ; C ; (IF (EQL A 'D) ; E ; (IF (MEMBER A '(F G)) H K))) . ; So, we can only dive to addresses of the form (n 1 ...) ; and (n 0 ...), though the latter cases aren't too interesting. ; In the example above, ; (2 1 ...) gets us to c, which should generate (2) ; (3 1 ...) gets us to e, which should generate (3 2) ; (4 1 ...) gets us to h, which should generate (3 3 2) ; (5 1 ...) gets us to k, which should generate (3 3 3). ; (2 0 ...) gets us to b, which should generate (1 2) ; (3 0 ...) gets us to d, which should generate (3 1 2) ; (4 0 ...) gets us to (f g), which should generate (3 3 1 2) ; (5 0 ...) gets us to "otherwise", which is an error (cond ((= (car addr) 1) (mv "The dive via address ~x0 brings us to the CASE ~ term~%~ ~ ~x1,~%which corresponds to the translated ~ term~%~ ~ ~x2.~%A further dive to the first argument ~ doesn't really make sense here." (list (cons #\0 (reverse accumulated-addr-r)) (cons #\1 raw-term) (cons #\2 term)))) ((not (and (consp (cdr addr)) (member-equal (cadr addr) '(0 1)))) (mv "The dive via address ~x0 brings us to the CASE ~ term~%~ ~ ~x1,~%A further dive past argument number ~ ~x2 to the zeroth or first ``argument'' is required ~ at this point.~%" (list (cons #\0 (reverse accumulated-addr-r)) (cons #\1 raw-term) (cons #\2 (car addr))))) ((and (= (cadr addr) 0) (= (car addr) (1- (length raw-term)))) (mv "The dive via address ~x0 brings us to the CASE ~ term~%~ ~ ~x1,~%A further dive to the ``otherwise'' ~ expression is not allowed." (list (cons #\0 (reverse accumulated-addr-r)) (cons #\1 raw-term)))) (t (let* ((lst (if (= (cadr addr) 1) (if (= (car addr) (1- (length raw-term))) (make-list (- (car addr) 2) :initial-element 3) (append (make-list (- (car addr) 2) :initial-element 3) '(2))) ;; otherwise (car addr) is 0 and ;; (car addr) < (1- (length raw-term)) (append (make-list (- (car addr) 2) :initial-element 3) '(1 2)))) (subterm (fetch-term term lst))) (if subterm (expand-address-recurse :ans (append lst rest-addr) :new-addr (cddr addr) :new-raw-term (cadr (nth (1+ (car addr)) raw-term)) :new-term subterm :new-iff-flg iff-flg :new-accumulated-addr-r (cons (car addr) (cons (cadr addr) accumulated-addr-r))) (mv t (er hard 'expand-address "Surprise! Unable to dive into raw-term ~x0, which is term ~x1, using list ~x2. So far we have DV-ed using ~x3." raw-term term lst (reverse accumulated-addr-r)))))))) (cond ; For example, ; (cond (a b) (c d) (e f) (t g)) ; translates to ; (if a b (if c d (if e f g))) ; So, we can dive to addresses of the form (n 0 ...) ; and (n 1 ...). ; (1 0 ...) gets us to a, which should generate (1) ; (2 0 ...) gets us to c, which should generate (3 1) ; (3 0 ...) gets us to e, which should generate (3 3 1) ; (4 0 ...) gets us to t, which is not allowed. ; (1 1 ...) gets us to b, which should generate (2) ; (2 1 ...) gets us to d, which should generate (3 2) ; (3 1 ...) gets us to f, which should generate (3 3 2) ; (4 1 ...) gets us to g, which should generate (3 3 3) (cond ((not (and (consp (cdr addr)) (member-equal (cadr addr) '(0 1)))) (mv "The dive via address ~x0 brings us to the COND term~%~ ~ ~ ~x1,~%A further dive past argument number ~x2 to the ~ zeroth or first ``argument'' is required at this ~ point.~%" (list (cons #\0 (reverse accumulated-addr-r)) (cons #\1 raw-term) (cons #\2 (car addr))))) ((and (= (cadr addr) 0) (= (car addr) (1- (length raw-term)))) (mv "The dive via address ~x0 brings us to the COND term~%~ ~ ~ ~x1,~%A further dive to the ``T'' expression is not ~ allowed." (list (cons #\0 (reverse accumulated-addr-r)) (cons #\1 raw-term)))) (t (let* ((lst (if (= (cadr addr) 1) (if (= (car addr) (1- (length raw-term))) (make-list (1- (car addr)) :initial-element 3) (append (make-list (1- (car addr)) :initial-element 3) '(2))) ;; otherwise (cadr addr) is 0 and (car addr) < (1- (length raw-term)) (append (make-list (1- (car addr)) :initial-element 3) '(1)))) (subterm (fetch-term term lst))) (if subterm (expand-address-recurse :ans (append lst rest-addr) :new-addr (cddr addr) :new-raw-term (cadr (nth (1+ (car addr)) raw-term)) :new-term subterm :new-iff-flg iff-flg :new-accumulated-addr-r (cons (car addr) (cons (cadr addr) accumulated-addr-r))) (mv t (er hard 'expand-address "Surprise! Unable to dive into raw-term ~x0, ~ which is term ~x1, using list ~x2. So far we ~ have DV-ed using ~x3." raw-term term lst (reverse accumulated-addr-r)))))))) (if (expand-address-recurse :new-iff-flg (if (= (car addr) 1) t iff-flg))) ((implies iff) (expand-address-recurse :new-iff-flg t)) (t (mv-let (fn guts) (car-cdr-nest term) (cond (fn (expand-address-recurse :ans (append (make-list (- (length (symbol-name fn)) 2) :initial-element 1) rest-addr) :new-term guts)) (t (expand-address-recurse)))))))))))) (defmacro dv-error (str alist) `(pprogn (print-no-change (string-append "Unable to perform the requested dive.~|~%" ,str) ; We could print the current term using ~xt in the string above, but that ; appears to be distracting in the error message. (cons (cons #\t current-term) ,alist)) (mv t nil state))) (define-pc-atomic-macro dv (&rest rest-args) "move to the indicated subterm~/ ~bv[] Examples: (dv 1) -- assign the new current subterm to be the first argument of the existing current subterm (dv 1 2) -- assign the new current subterm to be the result of first taking the 1st argument of the existing current subterm, and then the 2nd argument of that ~ev[] For example, if the current subterm is ~bv[] (* (+ a b) c), ~ev[] then after ~c[(dv 1)] it is ~bv[] (+ a b). ~ev[] If after that, then ~c[(dv 2)] is invoked, the new current subterm will be ~bv[] b. ~ev[] Instead of ~c[(dv 1)] followed by ~c[(dv 2)], the same current subterm could be obtained by instead submitting the single instruction ~c[(dv 1 2)].~/ ~bv[] General Form: (dv &rest naturals-list) ~ev[] If ~c[naturals-list] is a non-empty list ~c[(n_1 ... n_k)] of natural numbers, let the new current subterm be the result of selecting the ~c[n_1]-st argument of the current subterm, and then the ~c[n_2]-th subterm of that, ..., finally the ~c[n_k]-th subterm. ~st[Remark:] ~c[(dv n)] may be abbreviated by simply ~c[n], so we could have typed ~c[1] instead of ~c[(dv 1)] in the first example above. ~st[Remark:] See also ~c[dive], which is related to the command ~c[pp], in that the diving is done according to raw (translated, internal form) syntax. Use the command ~c[dv] if you want to dive according to the syntax displayed by the command ~c[p]. Thus, the command ``~c[up]'' is the inverse of ~c[dive], not of ~c[dv]. The following example illustrates this point. ~bv[] ACL2 !>(verify (equal (* a b c) x)) ->: p ; print user-level term (EQUAL (* A B C) X) ->: pp ; print internal-form (translated) term (EQUAL (BINARY-* A (BINARY-* B C)) X) ->: exit Exiting.... NIL ACL2 !>(verify (equal (* a b c) x)) ->: p (EQUAL (* A B C) X) ->: 1 ; same as (dv 1) ->: p ; print user-level term (* A B C) ->: pp ; print internal-form (translated) term (BINARY-* A (BINARY-* B C)) ->: 3 ; dive to third argument of (* A B C) ->: p C ->: up ; go up one level in (BINARY-* A (BINARY-* B C)) ->: p (* B C) ->: pp (BINARY-* B C) ->: ~ev[]" (let* ((conc (conc t)) (current-addr (current-addr t)) (abbreviations (abbreviations t)) (current-term (fetch-term conc current-addr)) (term-id-iff (term-id-iff conc current-addr t))) (mv-let (erp addr) ;; If erp is not nil, then it's a string explaining why DV failed, ;; and then addr is a list of args for that string (except #\t is ;; associated with 'current-term). (expand-address rest-args (untrans0 current-term term-id-iff abbreviations) current-term abbreviations term-id-iff nil (w state)) (if erp (dv-error erp addr) (mv nil (cons ':dive addr) state))))) (mutual-recursion (defun deposit-term (term addr subterm) ;; Puts subterm at address addr in term. Assumes that error checking is ;; not necessary, i.e. that addr is given correctly relative to term, (cond ((null addr) subterm) (t (cons-term (ffn-symb term) (deposit-term-lst (fargs term) (car addr) (cdr addr) subterm))))) (defun deposit-term-lst (lst n addr subterm) ;; This simply puts (deposit-term term addr subterm) in place of the nth ;; element, term, of lst, but avoids consing up the unchanged tail. (cond ((= 1 n) (cons (deposit-term (car lst) addr subterm) (cdr lst))) (t (cons (car lst) (deposit-term-lst (cdr lst) (1- n) addr subterm))))) ) ;; Suppose that we want to make congruence-based substitution. Here ;; is the plan. Then (unless the congruence is equality) we need to ;; make sure that wherever the substitution is to be made, the ;; congruence relation is enough to preserve the geneqv at the current ;; subterm. The following function actually returns a list of congruence ;; rules. (defun geneqv-at-subterm (term addr geneqv ens wrld) ;; The property we want is that if one substitutes an equivalent ;; subterm of TERM at the given address (equivalent modulo the ;; generated equivalence relation returned by this function, that ;; is), then the resulting term is equivalent modulo geneqv to the ;; original TERM. We assume that address is a valid address for ;; term. (*** This should really be a guard.) As usual, we may ;; return NIL for 'equal. (if (null addr) geneqv (geneqv-at-subterm (nth (1- (car addr)) (fargs term)) (cdr addr) (nth (1- (car addr)) ;; ***** seems inefficient to do all the computing just below (geneqv-lst (ffn-symb term) geneqv ens wrld)) ens wrld))) (defun geneqv-at-subterm-top (term addr ens wrld) (geneqv-at-subterm term addr *geneqv-iff* ens wrld)) ;; In the following we want to know if every occurrence of old in term ;; is at a position at which substitution by something EQUIV to old ;; will guarantee a result that is GENEQV to term. ; (mutual-recursion ; ; (defun subst-expr1-okp (old term equiv geneqv ens wrld) ; (cond ((equal term old) ; (geneqv-refinementp equiv geneqv wrld)) ; ((variablep term) t) ; ((fquotep term) t) ; (t (subst-expr1-ok-listp old (fargs term) ; (geneqv-lst (ffn-symb term) geneqv ens wrld) ; equiv ens wrld)))) ; ; (defun subst-expr1-ok-listp (old args equiv geneqv-lst ens wrld) ; (cond ((null args) nil) ; (t (and (subst-expr1-okp ; old (car args) equiv (car geneqv-lst) ens wrld) ; (subst-expr1-ok-listp ; old (cdr args) equiv (cdr geneqv-lst) ens wrld))))) ; ; ; ) ;; **** Need to think about what happens if we, e.g., substitute T for X ;; inside (equal X T). Probably that's OK -- but also, consider allowing ;; an equivalence relation as an argument. One would have to check that ;; the relation is OK in at the current address, and then one would use ;; that relation instead of equal to create the proof obligation. Also, ;; consider special handling for IFF in the case that it's (IFF ... T), ;; so that we can simulate pc-nqthm's PUSH command. ;; ****** give a warning if the term to be replaced doesn't occur in the ;; current subterm ;; The following are adapted from ACL2 definitions of subst-expr1 and ;; subst-expr1-lst. Note that the parameter `new' has been dropped, ;; but the given and current equivalence relations have been added. (defun maybe-truncate-current-address (addr term orig-addr acc state) ;; Truncates the current address if it tries to dive into a quotep. ;; Here orig-addr is the original address (used for the warning message) ;; and acc is the accumulated new address (in reverse order). (declare (xargs :guard (true-listp addr))) (if addr (cond ((variablep term) (mv (er hard 'maybe-truncate-current-address "Found variable with non-NIL address!") state)) ((fquotep term) (let ((new-addr (reverse acc))) (pprogn (io? proof-checker nil state (new-addr orig-addr) (fms0 "~|NOTE: truncating current address from ~x0 to ~ ~x1. See explanation at end of help for X ~ command.~|" (list (cons #\0 orig-addr) (cons #\1 new-addr)) 0 nil)) (mv new-addr state)))) (t (maybe-truncate-current-address (cdr addr) (nth (1- (car addr)) (fargs term)) orig-addr (cons (car addr) acc) state))) (mv (reverse acc) state))) (defun deposit-term-in-goal (given-goal conc current-addr new-term state) ;; state is passed in so that maybe-truncate-current-address can ;; print a warning message if appropriate (let ((new-conc (deposit-term conc current-addr new-term))) (if (quotep new-term) (mv-let (new-current-addr state) (maybe-truncate-current-address current-addr new-conc current-addr nil state) (mv (change goal given-goal :conc new-conc :current-addr new-current-addr) state)) (mv (change goal given-goal :conc new-conc) state)))) (defun split-implies (term) ;; returns hyps and conc for term, e.g. ;; (implies x y) --> (mv (list x) y), ;; (implies x (implies (and y z)) w) --> (mv (list x y z) w), and ;; (foo 3) --> (mv nil (foo 3)) (if (or (variablep term) (fquotep term) (not (eq (ffn-symb term) 'implies))) (mv nil term) (mv-let (h c) (split-implies (fargn term 2)) (mv (append (flatten-ands-in-lit (fargn term 1)) h) c)))) (defun equiv-refinementp (equiv1 equiv2 wrld) (member-eq equiv2 (getprop equiv1 'coarsenings nil 'current-acl2-world wrld))) (defun find-equivalence-hyp-term (term hyps target equiv w) ;; allows backchaining through IMPLIES (if (consp hyps) (mv-let (h c) (split-implies (car hyps)) (if (or (variablep c) (fquotep c) (not (symbolp (ffn-symb c))) (not (equiv-refinementp (ffn-symb c) equiv w))) (find-equivalence-hyp-term term (cdr hyps) target equiv w) (let ((x (fargn c 1)) (y (fargn c 2))) (or (and (subsetp-equal h hyps) (or (and (equal x term) (equal y target)) (and (equal y term) (equal x target)))) (find-equivalence-hyp-term term (cdr hyps) target equiv w))))) nil)) (defun flatten-ands-in-lit-lst (x) (if (endp x) nil (append (flatten-ands-in-lit (car x)) (flatten-ands-in-lit-lst (cdr x))))) (define-pc-primitive equiv (x y &optional equiv) "attempt an equality (or congruence-based) substitution~/ ~bv[] Examples: (equiv (* x y) 3) -- replace (* x y) by 3 everywhere inside the current subterm, if their equality is among the top-level hypotheses or the governors (equiv x t iff) -- replace x by t everywhere inside the current subterm, where only propositional equivalence needs to be maintained at each occurrence of x~/ General form: (equiv old new &optional relation) ~ev[] Substitute new for old everywhere inside the current subterm, provided that either (relation old new) or (relation new old) is among the top-level hypotheses or the governors (possibly by way of backchaining and/or refinement; see below). If relation is ~c[nil] or is not supplied, then it defaults to ~c[equal]. See also the command ~c[=], which is much more flexible. Note that this command fails if no substitution is actually made. ~st[Remark:] No substitution takes place inside explicit values. So for example, the instruction ~c[(equiv 3 x)] will cause ~c[3] to be replaced by ~c[x] if the current subterm is, say, ~c[(* 3 y)], but not if the current subterm is ~c[(* 4 y)] even though ~c[4 = (1+ 3)]. The following remarks are quite technical and mostly describe a certain weak form of ``backchaining'' that has been implemented for ~c[equiv] in order to support the ~c[=] command. In fact neither the term ~c[(relation old new)] nor the term ~c[(relation new old)] needs to be ~st[explicitly] among the current ``assumptions'', i.e., the top-level hypothesis or the governors. Rather, there need only be such an assumption that ``tells us'' ~c[(r old new)] or ~c[(r new old)], for ~st[some] equivalence relation ~c[r] that ~st[refines] ~c[relation]. Here, ``tells us'' means that either one of the indicated terms is among those assumptions, or else there is an assumption that is an implication whose conclusion is one of the indicated terms and whose hypotheses (gathered up by appropriately flattening the first argument of the ~c[implies] term) are all among the current assumptions." (mv-let (current-term governors) (fetch-term-and-cl conc current-addr nil) (cond ((eq governors t) (mv (er hard ':= "Found governors of T inside command ~x0!" (cons := args)) state)) (t (let* ((assumptions (append hyps governors)) (w (w state)) (pc-ens (make-pc-ens pc-ens state))) (mv-let (erp new-pc-state state) (er-let* ((old (trans0 x abbreviations :equiv)) (new (trans0 y abbreviations :equiv)) (equiv (if (null equiv) (value 'equal) (if (equivalence-relationp equiv w) (value equiv) (er soft :equiv "The name ~x0 is not currently the name of an ACL2 ~ equivalence relation. The current list of ~ ACL2 equivalence relations is ~x1." equiv (getprop 'equal 'coarsenings nil 'current-acl2-world w)))))) (if (find-equivalence-hyp-term old (flatten-ands-in-lit-lst assumptions) new equiv w) (mv-let (hitp new-current-term new-ttree) (subst-equiv-expr1 equiv new old (geneqv-at-subterm-top conc current-addr pc-ens w) current-term pc-ens w state nil) (if hitp (mv-let (new-goal state) (deposit-term-in-goal (car goals) conc current-addr new-current-term state) (value (change-pc-state pc-state :local-tag-tree new-ttree :goals (cons new-goal (cdr goals))))) (pprogn (print-no-change "The equivalence relation that you specified, namely ~x0, is ~ not appropriate at any occurrence of the ``old'' term ~x1 ~ inside the current term, and hence no substitution has ~ been made." (list (cons #\0 equiv) (cons #\1 x))) (value nil)))) (pprogn (print-no-change "The ~#2~[equivalence~/equality~] of the terms ~x0 and ~x1~#2~[ with respect ~ to the equivalence relation ~x3~/~] is not known at the ~ current subterm from the current hypotheses and governors." (list (cons #\0 x) (cons #\1 y) (cons #\2 (if (eq equiv 'equal) 1 0)) (cons #\3 equiv))) (value nil)))) (if erp (print-no-change2 "EQUIV failed.") (mv new-pc-state state)))))))) (define-pc-primitive casesplit (expr &optional use-hyps-flag do-not-flatten-flag) "split into two cases~/ ~bv[] Example: (casesplit (< x y)) -- assuming that we are at the top of the conclusion, add (< x y) as a new top-level hypothesis in the current goal, and create a subgoal identical to the current goal except that it has (not (< x y)) as a new top-level hypothesis~/ General Form: (casesplit expr &optional use-hyps-flag do-not-flatten-flag) ~ev[] When the current subterm is the entire conclusion, this instruction adds ~c[expr] as a new top-level hypothesis, and create a subgoal identical to the existing current goal except that it has the negation of ~c[expr] as a new top-level hypothesis. See also ~c[claim]. The optional arguments control the use of governors and the ``flattening'' of new hypotheses, as we now explain. The argument ~c[use-hyps-flag] is only of interest when there are governors. (To read about governors, see the documentation for the command ~c[hyps]). In that case, if ~c[use-hyps-flag] is not supplied or is ~c[nil], then the description above is correct; but otherwise, it is not ~c[expr] but rather it is ~c[(implies govs expr)] that is added as a new top-level hypothesis (and whose negation is added as a top-level hypothesis for the new goal), where ~c[govs] is the conjunction of the governors. If ~c[do-not-flatten-flag] is supplied and not ~c[nil], then that is all there is to this command. Otherwise (thus this is the default), when the claimed term (first argument) is a conjunction (~c[and]) of terms and the ~c[claim] instruction succeeds, then each (nested) conjunct of the claimed term is added as a separate new top-level hypothesis. Consider the following example, assuming there are no governors. ~bv[] (casesplit (and (and (< x y) (integerp a)) (equal r s)) t) ~ev[] Three new top-level hypotheses are added to the current goal, namely ~c[(< x y)], ~c[(integerp a)], and ~c[(equal r s)]. In that case, only one hypothesis is added to create the new goal, namely the negation of ~c[(and (< x y) (integerp a) (equal r s))]. If the negation of this term had been ~c[claim]ed, then it would be the other way around: the current goal would get a single new hypothesis while the new goal would be created by adding three hypotheses. ~st[Remark:] It is allowed to use abbreviations in the hints." (mv-let (erp term state) (trans0 expr abbreviations :casesplit) (if erp (print-no-change2 "~x0 failed." (list (cons #\0 (cons :casesplit args)))) (let ((claimed-term (if use-hyps-flag (mv-let (current-term governors) (fetch-term-and-cl conc current-addr nil) (declare (ignore current-term)) (cond ((eq governors t) (er hard ':casesplit "Found governors of T inside command ~x0!" (cons :casesplit args))) (governors (fcons-term* 'implies (conjoin governors) term)) (t term))) term))) (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :hyps (append hyps (if do-not-flatten-flag (list claimed-term) (flatten-ands-in-lit claimed-term))) :depends-on (1+ depends-on)) (cons (change goal (car goals) :hyps (append hyps (if do-not-flatten-flag (list (dumb-negate-lit claimed-term)) (flatten-ands-in-lit (dumb-negate-lit claimed-term)))) :goal-name (cons goal-name depends-on) :depends-on 1) (cdr goals)))) state))))) ;;(defconst *pc-catch-all-tag* :pc-catch-all-tag) (define-pc-macro top? () (when-goals-trip (if (current-addr t) (value 'top) (value 'skip)))) (define-pc-macro contrapose-last () (when-goals-trip (let ((hyps (hyps))) (if (null hyps) (pprogn (print-no-change "CONTRAPOSE-LAST failed -- no top-level hypotheses!") (value :fail)) (value (list :contrapose (length hyps))))))) (define-pc-macro drop-last () (when-goals-trip (let ((hyps (hyps))) (if (null hyps) (pprogn (print-no-change "DROP-LAST failed -- no top-level hypotheses!") (value :fail)) (value (list :drop (length hyps))))))) (define-pc-macro drop-conc () (value `(do-strict top? contrapose-last drop-last))) (define-pc-atomic-macro claim (expr &rest rest-args) "add a new hypothesis~/ ~bv[] Examples: (claim (< x y)) -- attempt to prove (< x y) from the current top-level hypotheses and if successful, then add (< x y) as a new top-level hypothesis in the current goal (claim (< x y) :otf-flg t :hints ((\"Goal\" :induct t))) -- as above, but call the prover using the indicated values for the otf-flg and hints (claim (< x y) 0) -- as above, except instead of attempting to prove (< x y), create a new subgoal with the same top-level hypotheses as the current goal that has (< x y) as its conclusion (claim (< x y) :hints :none) -- same as immediately above~/ General Form: (claim expr &rest rest-args) ~ev[] This command creates a new subgoal with the same top-level hypotheses as the current goal but with a conclusion of ~c[expr]. If ~c[rest-args] is a non-empty list headed by a non-keyword, then there will be no proof attempted for the new subgoal. With that possible exception, ~c[rest-args] should consist of keyword arguments. The keyword argument ~c[:do-not-flatten] controls the ``flattening'' of new hypotheses, just as with the ~c[casesplit] command (as described in its documentation). The remaining ~c[rest-args] are used with a call the ~c[prove] command on the new subgoal, except that if ~c[:hints] is a non-~c[nil] atom, then the prover is not called ~-[] rather, this is the same as the situation described above, where ~c[rest-args] is a non-empty list headed by a non-keyword. ~st[Remarks:] (1) Unlike the ~c[casesplit] command, the ~c[claim] command is completely insensitive to governors. (2) It is allowed to use abbreviations in the hints. (3) The keyword :~c[none] has the special role as a value of :~c[hints] that is shown clearly in an example above." (when-goals-trip (value (let ((rest-args-1 (if (and rest-args (car rest-args) (not (keywordp (car rest-args)))) (list* :hints :none (cdr rest-args)) rest-args))) (mv-let (pairs remaining-rest-args) (pair-keywords '(:do-not-flatten) rest-args-1) (let ((do-not-flatten-flag (cdr (assoc-eq :do-not-flatten pairs))) (temp (cadr (member-eq :hints rest-args-1)))) (if (and temp (atom temp)) `(protect (casesplit ,expr nil ,do-not-flatten-flag) change-goal drop-conc pro change-goal) `(protect (casesplit ,expr nil ,do-not-flatten-flag) change-goal drop-conc (prove ,@remaining-rest-args))))))))) (define-pc-atomic-macro induct (&optional raw-term) "generate subgoals using induction~/ ~bv[] Examples: induct, (induct t) -- induct according to a heuristically-chosen scheme, creating a new subgoal for each base and induction step (induct (append (reverse x) y)) -- as above, but choose an induction scheme based on the term (append (reverse x) y) rather than on the current goal~/ General Form: (induct &optional term) ~ev[] Induct as in the corresponding ~c[:induct] hint given to the theorem prover, creating new subgoals for the base and induction steps. If term is ~c[t] or is not supplied, then use the current goal to determine the induction scheme; otherwise, use that term. ~st[Remark:] As usual, abbreviations are allowed in the term. ~st[Remark:] ~c[Induct] actually calls the ~c[prove] command with all processes turned off. Thus, you must be at top of the goal for an ~c[induct] instruction." (when-goals-trip (if (and (goals t) (current-addr t)) (pprogn (print-no-change "You must be at the top of the goal in order to use the ~ INDUCT command. Try TOP first.") (value :fail)) (let ((raw-term (or raw-term t))) (value `(prove :hints (("Goal" :induct ,raw-term :do-not-induct proof-checker :do-not *do-not-processes*)))))))) (defun print-on-separate-lines (vals evisc-tuple chan state) (declare (xargs :guard (true-listp vals))) (if (null vals) (newline chan state) (pprogn (io? proof-checker nil state (evisc-tuple chan vals) (fms "~x0" (list (cons #\0 (car vals))) chan state evisc-tuple)) (print-on-separate-lines (cdr vals) evisc-tuple chan state)))) (define-pc-help goals () "list the names of goals on the stack~/ ~bv[] Example and General Form: goals ~ev[]~/ ~c[Goals] lists the names of all goals that remain to be proved. They are listed in the order in which they appear on the stack of remaining goals, which is relevant for example to the effect of a ~c[change-goal] instruction." (io? proof-checker nil state (state-stack) (when-goals (print-on-separate-lines (goal-names (goals t)) nil (proofs-co state) state)))) (defun modified-error-triple-for-sequence (erp val success-expr state) (mv-let (new-erp stobjs-out-and-vals state) (state-global-let* ((pc-erp erp) (pc-val val)) (trans-eval success-expr :sequence state t)) ; Note: Success-expr is typically an expression involving STATE, which ; accesses erp and val via (@ erp) and (@ val). It may modify STATE. ; It may, indeed, talk about single-threaded objects! It may even ; modify them, leaving their modified values in the modified state. ; But it is expected to return at least two results, and the first two ; must not be stobjs. (let ((stobjs-out (car stobjs-out-and-vals)) (vals (cdr stobjs-out-and-vals))) (if new-erp (mv new-erp nil state) (if (or (< (length stobjs-out) 2) (car stobjs-out) (cadr stobjs-out)) (pprogn (io? proof-checker nil state (vals success-expr) (fms0 "~|WARNING -- evaluation of ~ `success-expr' argument to ~ :SEQUENCE, ~x0, has been ~ ignored because it returned a ~ single-threaded object in one ~ of its first two values or ~ returned fewer than two values. ~ The value(s) returned was ~ (were):~%~ ~ ~x1.~%" (list (cons #\0 success-expr) (cons #\2 vals)))) (mv erp val state)) (mv (car vals) (cadr vals) state)))))) (define-pc-meta sequence (instr-list &optional strict-flg protect-flg success-expr no-prompt-flg no-restore-flg) ;; Note: the reason I use state globals instead of a lexical LET for ;; the success-expr argument is that I don't want to worry about the ;; translator failing because erp and val aren't declared ignored when ;; they should be. "run the given list of instructions according to a multitude of options~/ ~bv[] Example: (sequence (induct p prove) t) ~ev[] See also the definitions of commands ~c[do-all], ~c[do-strict], ~c[protect], and ~c[succeed].~/ ~bv[] General Form: (sequence instruction-list &optional strict-flg protect-flg success-expr no-prompt-flg no-restore-flg) ~ev[] Each instruction in the list ~c[instruction-list] is run, and the instruction ``succeeds'' if every instruction in ~c[instruction-list] ``succeeds''. However, it might ``succeed'' even if some instructions in the list ``fail''; more generally, the various arguments control a number of aspects of the running of the instructions. All this is explained in the paragraphs below. First we embark on a general discussion of the instruction interpreter, including the notions of ``succeed'' and ``fail''. ~st[Remark:] The arguments are ~st[not] evaluated, except (in a sense) for ~c[success-expr], as described below. Each primitive and meta instruction can be thought of as returning an error triple, say ~c[(erp val state)]; ~pl[error-triples]. An instruction (primitive or meta) ``succeeds'' if ~c[erp] is ~c[nil] and ~c[val] is not ~c[nil]; otherwise it ``fails''. (When we use the words ``succeed'' or ``fail'' in this technical sense, we'll always include them in double quotes.) If an instruction ``fails,'' we say that that the failure is ``soft'' if ~c[erp] is ~c[nil]; otherwise the failure is ``hard''. The ~c[sequence] command gives the user control over how to treat ``success'' and ``failure'' when sequencing instructions, though we have created a number of handy macro commands for this purpose, notably ~c[do-all], ~c[do-strict] and ~c[protect]. Here is precisely what happens when a ~c[sequence] instruction is run. The instruction interpreter is run on the instructions supplied in the argument ~c[instruction-list] (in order). The interpreter halts the first time there is a hard ``failure.'' except that if ~c[strict-flg] is supplied and not ~c[nil], then the interpreter halts the first time there is any ``failure.'' The error triple ~c[(erp val state)] returned by the ~c[sequence] instruction is the triple returned by the last instruction executed (or, the triple ~c[(nil t state)] if ~c[instruction-list] is ~c[nil]), except for the following provision. If ~c[success-expr] is supplied and not ~c[nil], then it is evaluated with the state global variables ~c[pc-erp] and ~c[pc-val] (in the \"ACL2\" package) bound to the corresponding components of the error triple returned (as described above). At least two values should be returned, and the first two of these will be substituted for ~c[erp] and ~c[val] in the triple finally returned by ~c[sequence]. For example, if ~c[success-expr] is ~c[(mv erp val)], then no change will be made to the error triple, and if instead it is ~c[(mv nil t)], then the ~c[sequence] instruction will ``succeed''. That concludes the description of the error triple returned by a ~c[sequence] instruction, but it remains to explain the effects of the arguments ~c[protect-flg] and ~c[no-prompt-flg]. If ~c[protect-flg] is supplied and not ~c[nil] and if also the instruction ``fails'' (i.e., the error component of the triple is not ~c[nil] or the value component is ~c[nil]), then the state is reverted so that the proof-checker's state (including the behavior of ~c[restore]) is set back to what it was before the ~c[sequence] instruction was executed. Otherwise, unless ~c[no-restore-flg] is set, the state is changed so that the ~c[restore] command will now undo the effect of this ~c[sequence] instruction (even if there were nested calls to ~c[sequence]). Finally, as each instruction in ~c[instruction-list] is executed, the prompt and that instruction will be printed, unless the global state variable ~c[pc-print-prompt-and-instr-flg] is unbound or ~c[nil] and the parameter ~c[no-prompt-flg] is supplied and not ~c[nil]." ;; This is the only place where the pc-prompt gets lengthened. ;; Also note that we always lengthen the prompt, but we only do the printing ;; if no-prompt-flg is nil AND pc-print-prompt-and-instr-flg is non-nil. (if (not (true-listp instr-list)) (pprogn (print-no-change "The first argument to the SEQUENCE command must be ~ a true list, but~%~ ~ ~x0~| is not." (list (cons #\0 instr-list))) (mv t nil state)) (state-global-let* ((pc-prompt (string-append (pc-prompt-depth-prefix) (pc-prompt)))) (let ((saved-old-ss (old-ss)) (saved-ss (state-stack))) (mv-let (erp val state) (pc-main-loop instr-list (if strict-flg '(signal value) '(signal)) t (and (null no-prompt-flg) (pc-print-prompt-and-instr-flg)) state) (mv-let (erp val state) (if success-expr (modified-error-triple-for-sequence erp val success-expr state) (mv erp val state)) (if (and protect-flg (or erp (null val))) (pprogn (print-no-change "SEQUENCE failed, with protection on. ~ Reverting back to existing state of the ~ proof-checker.~|") ;; **** consider improving message above, say by printing ;; entire instruction with appropriate evisceration (pc-assign state-stack saved-ss) (pc-assign old-ss saved-old-ss) (mv erp val state)) (pprogn (if no-restore-flg state (pc-assign old-ss saved-ss)) (mv erp val state))))))))) (define-pc-macro negate (&rest instr-list) "run the given instructions, and ``succeed'' if and only if they ``fail''~/ Example: (negate prove)~/ ~bv[] General form: (negate &rest instruction-list) ~ev[] Run the indicated instructions exactly in the sense of ~c[do-all], and ``succeed'' if and only if they ``fail''. ~st[Remark:] ~c[Negate] instructions will never produce hard ``failures''." (value (list :sequence instr-list nil nil '(mv nil (if (or (f-get-global 'pc-erp state) (null (f-get-global 'pc-val state))) t nil))))) (define-pc-macro succeed (&rest instr-list) ;; I won't make this atomic, since I view this as just a sequencer ;; command that should ultimately "disappear" in favor of its arguments. "run the given instructions, and ``succeed''~/ ~bv[] Example: (succeed induct p prove)~/ General Form: (succeed &rest instruction-list) ~ev[] Run the indicated instructions until there is a hard ``failure'', and ``succeed''. (See the documentation for ~c[sequence] for an explanation of ``success'' and ``failure''.)" (mv nil (list :sequence instr-list nil nil '(mv nil t)) state)) (define-pc-macro do-all (&rest instr-list) "run the given instructions~/ ~bv[] Example: (do-all induct p prove)~/ General Form: (do-all &rest instruction-list) ~ev[] Run the indicated instructions until there is a hard ``failure''. The instruction ``succeeds'' if and only if each instruction in ~c[instruction-list] does. (See the documentation for ~c[sequence] for an explanation of ``success'' and ``failure.'') As each instruction is executed, the system will print the usual prompt followed by that instruction, unless the global state variable ~c[pc-print-prompt-and-instr-flg] is ~c[nil]. ~st[Remark:] If ~c[do-all] ``fails'', then the failure is hard if and only if the last instruction it runs has a hard ``failure''. Obscure point: For the record, ~c[(do-all ins_1 ins_2 ... ins_k)] is the same as ~c[(sequence (ins_1 ins_2 ... ins_k))]." (mv nil (list :sequence instr-list) state)) (define-pc-macro do-strict (&rest instr-list) "run the given instructions, halting once there is a ``failure''~/ ~bv[] Example: (do-strict induct p prove)~/ General Form: (do-strict &rest instruction-list) ~ev[] Run the indicated instructions until there is a (hard or soft) ``failure''. In fact ~c[do-strict] is identical in effect to ~c[do-all], except that ~c[do-all] only halts once there is a hard ``failure''. See the documentation for ~c[do-all]." (mv nil (list :sequence instr-list t) state)) (define-pc-macro do-all-no-prompt (&rest instr-list) "run the given instructions, halting once there is a ``failure''~/ ~bv[] Example: (do-all-no-prompt induct p prove)~/ General Form: (do-all-no-prompt &rest instruction-list) ~ev[] ~c[Do-all-no-prompt] is the same as ~c[do-all], except that the prompt and instruction are not printed each time, regardless of the value of ~c[pc-print-prompt-and-instr-flg]. Also, restoring is disabled. See the documentation for ~c[do-all]." (mv nil (list :sequence instr-list nil nil nil t t) state)) (define-pc-macro th (&optional hyps-indices govs-indices) "print the top-level hypotheses and the current subterm~/ ~bv[] Examples: th -- print all (top-level) hypotheses and the current subterm (th (1 3) (2 4)) -- print hypotheses 1 and 3 and governors 2 and 4, and the current subterm (th (1 3) t) -- print hypotheses 1 and 3 and all governors, and the current subterm~/ General Form: (th &optional hyps-indices govs-indices) ~ev[] Print hypotheses and the current subterm. The printing of hypotheses (and perhaps governors) are controlled as in the ~c[hyps] command; see its documentation. Historical note: The name ~c[th] is adapted from the Gypsy Verification Environment, where ~c[th] abbreviates the command ~c[theorem], which says to print information on the current goal." (declare (ignore hyps-indices govs-indices)) (when-goals-trip (value `(do-all-no-prompt (hyps ,@args) (lisp (io? proof-checker nil state nil (fms0 "~%The current subterm is:~%"))) p)))) (define-pc-macro protect (&rest instr-list) "run the given instructions, reverting to existing state upon failure~/ ~bv[] Example: (protect induct p prove)~/ General Form: (protect &rest instruction-list) ~ev[] ~c[Protect] is the same as ~c[do-strict], except that as soon as an instruction ``fails'', the state-stack reverts to what it was before the ~c[protect] instruction began, and ~c[restore] is given the same meaning that it had before the ~c[protect] instruction began. See the documentation for ~c[do-strict]." (mv nil (list :sequence instr-list t t) state)) (defun extract-goal (name goals) ;; returns (goal rest-goals) if goal is found, else (nil ...). (if (consp goals) (if (equal (access goal (car goals) :goal-name) name) (mv (car goals) (cdr goals)) (mv-let (goal rest-goals) (extract-goal name (cdr goals)) (mv goal (cons (car goals) rest-goals)))) (mv nil goals))) (define-pc-primitive change-goal (&optional name end-flg) "change to another goal.~/ ~bv[] Examples: (change-goal (main . 1)) -- change to the goal (main . 1) change-goal -- change to the next-to-top goal~/ General Form: (change-goal &optional goal-name end-flg) ~ev[] Change to the goal with the name ~c[goal-name], i.e. make it the current goal. However, if ~c[goal-name] is ~c[nil] or is not supplied, then it defaults to the next-to-top goal, i.e., the second goal in the stack of goals. If ~c[end-flg] is supplied and not ~c[nil], then move the current goal to the end of the goal stack; else merely swap it with the next-to-top goal. Also see documentation for ~c[cg]." (cond ((null goals) (pprogn (print-all-goals-proved-message state) (mv nil state))) ((null (cdr goals)) (print-no-change2 "The current goal is the only unproved goal.")) ((null name) (pprogn (io? proof-checker nil state (goals) (fms0 "~|Now proving ~X0n.~%" (list (cons #\0 (access goal (cadr goals) :goal-name)) (cons #\n nil)))) (mv (change-pc-state pc-state :goals (if end-flg (cons (cadr goals) (append (cddr goals) (list (car goals)))) (list* (cadr goals) (car goals) (cddr goals)))) state))) ((equal name goal-name) (print-no-change2 "The current goal is already ~x0." (list (cons #\0 name)))) (t (mv-let (gl rest-goals) (extract-goal name (cdr goals)) (if gl (mv (change-pc-state pc-state :goals (if end-flg (cons gl (append rest-goals (list (car goals)))) (cons gl (cons (car goals) rest-goals)))) state) (print-no-change2 "There is no unproved goal named ~x0." (list (cons #\0 name)))))))) (define-pc-macro cg (&optional name) "change to another goal.~/ ~bv[] Examples: (cg (main . 1)) -- change to the goal (main . 1) cg -- change to the next-to-top goal~/ General Form: (CG &OPTIONAL goal-name) ~ev[] Same as ~c[(change-goal goal-name t)], i.e. change to the indicated and move the current goal to the end of the goal stack." (value `(change-goal ,name t))) (defun change-by-position (lst index new) (declare (xargs :guard (and (true-listp lst) (integerp index) (<= 0 index) (< index (length lst))))) (if (equal index 0) (cons new (cdr lst)) (cons (car lst) (change-by-position (cdr lst) (1- index) new)))) (define-pc-primitive contrapose (&optional n) "switch a hypothesis with the conclusion, negating both~/ ~bv[] Example: (contrapose 3)~/ General Form: (contrapose &optional n) ~ev[] The (optional) argument ~c[n] should be a positive integer that does not exceed the number of hypotheses. Negate the current conclusion and make it the ~c[n]th hypothesis, while negating the current ~c[n]th hypothesis and making it the current conclusion. If no argument is supplied then the effect is the same as for ~c[(contrapose 1)]. ~st[Remark:] By ``negate'' we mean an operation that replaces ~c[nil] by ~c[t], ~c[x] by ~c[nil] for any other explicit value ~c[x], ~c[(not x)] by ~c[x], and any other ~c[x] by ~c[(not x)]." (let ((n (if args n 1))) (if hyps (if current-addr (print-no-change2 "You must be at the top of the conclusion to apply ~ the CONTRAPOSE command. Try TOP first.") (if (and (integerp n) (< 0 n) (<= n (length hyps))) (mv (change-pc-state pc-state :goals (cons (change goal (car goals) :hyps (change-by-position hyps (1- n) (dumb-negate-lit conc)) :conc (dumb-negate-lit (nth (1- n) hyps))) (cdr goals))) state) (print-no-change2 "The argument to CONTRAPOSE must be a positive integer ~ that does not exceed the length of the list of top-level ~ hypotheses. The argument ~x0 fails to meet this requirement." (list (cons #\0 n))))) (print-no-change2 "There are no top-level hypotheses.")))) (define-pc-macro contradict (&optional n) "same as ~c[contrapose]~/ see documentation for ~c[contrapose]~/ " (declare (ignore n)) (value (cons :contrapose args))) (define-pc-atomic-macro pro () "repeatedly apply promote~/ ~bv[] Example and General Form: pro ~ev[]~/ Apply the ~c[promote] command until there is no change. This command ``succeeds'' exactly when at least one call of ~c[promote] ``succeeds''. In that case, only a single new proof-checker state will be created." (value '(quiet (repeat promote)))) (define-pc-atomic-macro nx () "move forward one argument in the enclosing term~/ ~bv[] Example and General Form: nx ~ev[] For example, if the conclusion is ~c[(= x (* (- y) z))] and the current subterm is ~c[x], then after executing ~c[nx], the current subterm will be ~c[(* (- y) z)].~/ This is the same as ~c[up] followed by ~c[(dive n+1)], where ~c[n] is the position of the current subterm in its parent term in the conclusion. Thus in particular, the ~c[nx] command fails if one is already at the top of the conclusion. See also ~c[up], ~c[dive], ~c[top], and ~c[bk]." (when-goals-trip (let ((current-addr (current-addr t))) (if current-addr (value `(protect up ,(1+ (car (last current-addr))))) (pprogn (print-no-change "NX failed: already at the top!") (value :fail)))))) (define-pc-atomic-macro bk () "move backward one argument in the enclosing term~/ ~bv[] Example and General Form: bk ~ev[] For example, if the conclusion is ~c[(= x (* (- y) z))] and the current subterm is ~c[(* (- y) z)], then after executing ~c[bk], the current subterm will be ~c[x].~/ Move to the previous argument of the enclosing term. This is the same as ~c[up] followed by ~c[(dive n-1)], where ~c[n] is the position of the current subterm in its parent term in the conclusion. Thus in particular, the ~c[nx] command fails if one is already at the top of the conclusion. See also ~c[up], ~c[dive], ~c[top], and ~c[bk]." (when-goals-trip (let ((current-addr (current-addr t))) (if current-addr (let ((n (car (last current-addr)))) (if (equal n 1) (pprogn (print-no-change "BK failed: already at the first argument!") (value :fail)) (value `(do-strict up ,(1- n))))) (pprogn (print-no-change "BK failed: already at the top!") (value :fail)))))) (define-pc-help p-top () "prettyprint the conclusion, highlighting the current term~/ ~bv[] Example and General Form: p-top ~ev[] For example, if the conclusion is ~c[(equal (and x (p y)) (foo z))] and the current subterm is ~c[(p y)], then ~c[p-top] will print ~c[(equal (and x (*** (p y) ***)) (foo z))].~/ Prettyprint the the conclusion, highlighting the current term. The usual user syntax is used, as with the command ~c[p] (as opposed to ~c[pp]). This is illustrated in the example above, where one would ~c[*not*] see ~c[(equal (if x (*** (p y) ***) 'nil) (foo z))]. ~st[Remark] (obscure): In some situations, a term of the form ~c[(if x t y)] occurring inside the current subterm will not print as ~c[(or x y)], when ~c[x] isn't a call of a boolean primitive. There's nothing incorrect about this, however." (when-goals (let ((conc (conc t)) (current-addr (current-addr t)) (stars (intern$ "***" (f-get-global 'current-package state)))) (io? proof-checker nil state (state-stack current-addr conc stars) (fms0 "~|~y0~|" (list (cons #\0 (untrans0 (deposit-term conc current-addr (list stars (fetch-term conc current-addr) stars)) t (abbreviations t))))))))) (define-pc-macro repeat (instr) "repeat the given instruction until it ``fails''~/ ~bv[] Example: (repeat promote)~/ General Form: (repeat instruction) ~ev[] The given ~c[instruction] is run repeatedly until it ``fails''. ~st[Remark:] There is nothing here in general to prevent the instruction from being run after all goals have been proved, though this is indeed the case for primitive instructions." (value `(succeed (repeat-rec ,instr)))) (define-pc-macro repeat-rec (instr) "auxiliary to ~c[repeat]~/ See documentation for ~c[repeat].~/ " (value `(do-strict ,instr (repeat-rec ,instr)))) (defmacro define-pc-bind (name args &optional doc-string declare-form) (mv-let (doc-string declare-form) (if (and (null declare-form) (not (stringp doc-string))) (mv nil doc-string) (mv doc-string declare-form)) `(define-pc-meta ,name (&rest instr-list) ,@ (and doc-string (list doc-string)) ,@(and declare-form (list declare-form)) (state-global-let* (,args) (pc-main-loop instr-list nil t (pc-print-prompt-and-instr-flg) state))))) ;; ****** Fix the documentation and code below once I can turn off ;; prover IO. (define-pc-bind quiet (inhibit-output-lst (union-eq '(prove proof-checker proof-tree warning observation) (f-get-global 'inhibit-output-lst state))) "run instructions without output~/ ~bv[] Example: (quiet induct prove)~/ General Form: (quiet &rest instruction-list) ~ev[] Run the ~c[instruction-list] through the top-level loop with no output. See also ~c[noise]." ) (define-pc-bind noise (inhibit-output-lst nil) "run instructions with output~/ ~bv[] Example: (noise induct prove)~/ General Form: (noise &rest instruction-list) ~ev[] Run the ~c[instruction-list] through the top-level loop with output. In fact, having output is the default. ~c[Noise] is useful inside a surrounding call of ~c[quiet], when one temporarily wants output. For example, if one wants to see output for a ~c[prove] command immediately following an ~c[induct] command but before an ~c[s] command, one may want to submit an instruction like ~c[(quiet induct (noise prove) s)]. See also ~c[quiet].") (defun find-equivalence-hyp-term-no-target (index term hyps equiv w) ;; Allows backchaining through IMPLIES. Returns an appropriate target. ;; Thus we are being rather silly here computationally, since we have ;; to do the work twice after generating an :equiv command. But so what? (if (consp hyps) (mv-let (h c) (split-implies (car hyps)) (if (or (variablep c) (fquotep c) (not (symbolp (ffn-symb c))) (not (equiv-refinementp (ffn-symb c) equiv w))) (find-equivalence-hyp-term-no-target (1+ index) term (cdr hyps) equiv w) (let* ((x (fargn c 1)) (y (fargn c 2)) (hyp-to-use (and (subsetp-equal h hyps) (or (and (equal x term) y) (and (equal y term) x))))) (if hyp-to-use (mv index hyp-to-use) (find-equivalence-hyp-term-no-target (1+ index) term (cdr hyps) equiv w))))) (mv nil nil))) (define-pc-atomic-macro if-not-proved (goal-name cmd) ; Requires the current goal to be named goal-name if it isn't already proved. (if (member-equal goal-name (goal-names (goals t))) (if (equal goal-name (goal-name t)) (value cmd) (mv-let (erp val state) (er soft 'if-not-proved "The proof-checker's atomic macro IF-NOT-PROVED requires the ~ indicated goal to be the current goal. However, the current ~ goal is ~p0 while the first argument to IF-NOT-PROVED is ~p1." (goal-name t) goal-name) (declare (ignore erp val)) (value 'fail))) (value :skip))) (define-pc-atomic-macro = (&optional x y &rest rest-args) "attempt an equality (or equivalence) substitution~/ ~bv[] Examples: = -- replace the current subterm by a term equated to it in one of the hypotheses (if such a term exists) (= x) -- replace the current subterm by x, assuming that the prover can show that they are equal (= (+ x y) z) -- replace the term (+ x y) by the term z inside the current subterm, assuming that the prover can prove (equal (+ x y) z) from the current top-level hypotheses or that this term or (equal z (+ x y)) is among the current top-level hypotheses or the current governors (= & z) -- exactly the same as above, if (+ x y) is the current subterm (= (+ x y) z :hints :none) -- same as (= (+ x y) z), except that a new subgoal is created with the current goal's hypotheses and governors as its top-level hypotheses and (equal (+ x y) z) as its conclusion (= (+ x y) z 0) -- exactly the same as immediately above (= (p x) (p y) :equiv iff :otf-flg t :hints ((\"Subgoal 2\" :BY FOO) (\"Subgoal 1\" :use bar))) -- same as (= (+ x y) z), except that the prover uses the indicated values for otf-flg and hints, and only propositional (iff) equivalence is used (however, it must be that only propositional equivalence matters at the current subterm)~/ General Form: (= &optional x y &rest keyword-args) ~ev[] If terms ~c[x] and ~c[y] are supplied, then replace ~c[x] by ~c[y] inside the current subterm if they are ``known'' to be ``equal''. Here ``known'' means the following: the prover is called as in the ~c[prove] command (using ~c[keyword-args]) to prove ~c[(equal x y)], except that a keyword argument ~c[:equiv] is allowed, in which case ~c[(equiv x y)] is proved instead, where ~c[equiv] is that argument. (See below for how governors are handled.) Actually, ~c[keyword-args] is either a single non-keyword or is a list of the form ~c[((kw-1 x-1) ... (kw-n x-n))], where each ~c[kw-i] is one of the keywords ~c[:equiv], ~c[:otf-flg], ~c[:hints]. Here ~c[:equiv] defaults to ~c[equal] if the argument is not supplied or is ~c[nil], and otherwise should be the name of an ACL2 equivalence relation. ~c[:Otf-flg] and ~c[:hints] give directives to the prover, as explained above and in the documentation for the ~c[prove] command; however, no prover call is made if ~c[:hints] is a non-~c[nil] atom or if ~c[keyword-args] is a single non-keyword (more on this below). ~em[Remarks on defaults] (1) If there is only one argument, say ~c[a], then ~c[x] defaults to the current subterm, in the sense that ~c[x] is taken to be the current subterm and ~c[y] is taken to be ~c[a]. (2) If there are at least two arguments, then ~c[x] may be the symbol ~c[&], which then represents the current subterm. Thus, ~c[(= a)] is equivalent to ~c[(= & a)]. (Obscure point: actually, ~c[&] can be in any package, except the keyword package.) (3) If there are no arguments, then we look for a top-level hypothesis or a governor of the form ~c[(equal c u)] or ~c[(equal u c)], where ~c[c] is the current subterm. In that case we replace the current subterm by ~c[u]. As with the ~c[prove] command, we allow goals to be given ``bye''s in the proof, which may be generated by a ~c[:hints] keyword argument in ~c[keyword-args]. These result in the creation of new subgoals. A proof is attempted unless the ~c[:hints] argument is a non-~c[nil] atom other than :~c[none], or unless there is one element of ~c[keyword-args] and it is not a keyword. In that case, if there are any hypotheses in the current goal, then what is attempted is a proof of the implication whose antecedent is the conjunction of the current hypotheses and governors and whose conclusion is the appropriate ~c[equal] term. ~st[Remarks:] (1) It is allowed to use abbreviations in the hints. (2) The keyword :~c[none] has the special role as a value of :~c[hints] that is shown clearly in an example above. (3) If there are governors, then the new subgoal has as additional hypotheses the current governors." (when-goals-trip (let ((conc (conc t)) (hyps (hyps t)) (current-addr (current-addr t)) (abbreviations (abbreviations t)) (w (w state)) (rest-args-1 (if (and rest-args (car rest-args) (not (keywordp (car rest-args)))) '(:hints :none) rest-args))) (if (not (keyword-value-listp rest-args-1)) (pprogn (print-no-change "The ``rest-args'' arguments for the = command should be ~ empty or a list, either (i) containing one element, an ~ atom, or else (ii) of even length with keywords in the odd ~ positions. Thus your command ~p0 is not legal. See the ~ documentation for examples and details." (list (cons #\0 (make-pretty-pc-instr (cons := args))))) (value :fail)) (mv-let (equiv-alist rest-args-1) (if (keyword-value-listp rest-args-1) (pair-keywords '(:equiv) rest-args-1) (mv nil rest-args-1)) (let ((equiv (or (cdr (assoc-eq :equiv equiv-alist)) 'equal))) (mv-let (current-term governors) (fetch-term-and-cl conc current-addr nil) (cond ((eq governors t) (value (er hard ':= "Found governors of T inside command ~p0!" (cons := args)))) ((eq x :&) (pprogn (print-no-change "We do not allow the first argument of the = command ~ to be the keyword :&, because no other symbol with ~ print-name & can be a term (and hence we use it to ~ represent the current subterm), but :& is a ~ legitimate term and -- we can't be really sure ~ whether you intended it to represent the term :& or ~ the current subterm.") (value :fail))) ((not (member-eq equiv (getprop 'equal 'coarsenings nil 'current-acl2-world w))) (pprogn (print-no-change "The ``equivalence relation'' that you supplied, ~p0, is not known to ACL2 as an equivalence relation." (list (cons #\0 equiv))) (value :fail))) ((null args) (mv-let (found-hyp new) (find-equivalence-hyp-term-no-target 1 current-term (flatten-ands-in-lit-lst (append hyps governors)) equiv w) (if found-hyp (pprogn (io? proof-checker nil state (found-hyp) (fms0 "Using hypothesis #~x0.~%" (list (cons #\0 found-hyp)))) (value (list :equiv current-term new))) (pprogn (print-no-change "There is no hypothesis or governor that equates ~ the current term ~#0~[under the equivalence ~ relation ~p1 ~/~]with anything." (list (cons #\0 (if (eq equiv 'equal) 1 0)) (cons #\1 equiv))) (value :fail))))) (t ;; so, we have a valid equivalence relation and at least one argument (mv-let (rest-args-alist tail) (pair-keywords '(:otf-flg :hints) rest-args-1) (declare (ignore rest-args-alist)) (if tail (pprogn (print-no-change "The only keywords allowed in the arguments to the = ~ command are :otf-flg, :hints, and :equiv. Your ~ instruction ~p1 violates this requirement." (list (cons #\1 (make-pretty-pc-instr (cons := args))))) (value :fail)) (er-let* ((old (if (or (null (cdr args)) (and (symbolp x) (eq (intern-in-keyword-package x) :&))) (value current-term) (trans0 x abbreviations ':=))) (new (if (null (cdr args)) (trans0 x abbreviations ':=) (trans0 y abbreviations ':=)))) (value (list :protect (list* :claim (if governors (fcons-term* 'implies (conjoin governors) (list equiv old new)) (list equiv old new)) :do-not-flatten t rest-args-1) (list :equiv old new equiv) (list :if-not-proved (goal-name t) :drop-last))))))))))))))) (define-pc-macro set-success (instr form) (value `(sequence (,instr) nil nil ,form))) (define-pc-macro orelse (instr1 instr2) "run the first instruction; if (and only if) it ``fails'', run the second~/ ~bv[] Example: (orelse top (print \"Couldn't move to the top\"))~/ General form: (orelse instr1 instr2) ~ev[] Run the first instruction. Then if it ``fails'', run the second instruction also; otherwise, stop after the first. This instruction ``succeeds'' if and only if either ~c[instr1] ``succeeds'', or else ~c[instr2] ``succeeds''. If it ``fails'', then the failure is soft." (value `(negate (do-strict (negate ,instr1) (negate ,instr2))))) (defun applicable-rewrite-rules (current-term conc current-addr target-name-or-rune target-index ens wrld) ; Returns a list of sar records. This list represents rules that can rewrite ; the current-term, each paired with the appropriate substitution and index, ; but filtered so that only those corresponding to target-name-or-rune are ; included (if non-NIL). If target-index is NIL then we get all such rules; ; otherwise we get a list with at most one rule, namely the one corresponding ; to that index. (declare (xargs :guard (not (or (variablep current-term) (fquotep current-term) (flambdap (ffn-symb current-term)))))) (applicable-rewrite-rules1 current-term (geneqv-at-subterm-top conc current-addr ens wrld) (getprop (ffn-symb current-term) 'lemmas nil 'current-acl2-world wrld) 1 target-name-or-rune target-index wrld)) (define-pc-help show-rewrites (&optional rule-id enabled-only-flg) "display the applicable ~il[rewrite] rules~/ ~bv[] Example: show-rewrites~/ General Form: (show-rewrites &optional rule-id enabled-only-flg) ~ev[] This command displays ~il[rewrite] rules whose left-hand side matches the current subterm, and shows how that command can be applied. For each rule displayed, hypotheses are shown that would need to be proved after the rule is applied. Note that hypotheses are omitted from the display when the system can trivially verify that they hold; to see all hypotheses for each rule in a display that is independent of the arguments of the current subterm, use the ~c[pl] or ~c[pr] command. Here are details on the arguments and the output. If ~c[rule-id] is supplied and is a name (non-~c[nil] symbol) or a ~c[:]~ilc[rewrite] or ~c[:]~ilc[definition] ~il[rune], then only the corresponding rewrite rule(s) will be displayed, while if ~c[rule-id] is a positive integer ~c[n], then only the ~c[n]th rule that would be in the list is displayed. In each case, the display will point out when a rule is currently disabled (in the interactive environment), except that if ~c[enabled-only-flg] is supplied and not ~c[nil], then disabled rules will not be displayed at all. Finally, among the free variables of any rule (~pl[free-variables]), those that would remain free if the rule were applied will be displayed. Also ~pl[rewrite]." (when-goals (let ((conc (conc t)) (current-addr (current-addr t)) (w (w state))) (let ((ens (make-pc-ens (pc-ens t) state)) (current-term (fetch-term conc current-addr)) (abbreviations (abbreviations t)) (term-id-iff (term-id-iff conc current-addr t)) (all-hyps (union-equal (hyps t) (governors conc current-addr)))) (show-rewrites-linears-fn 'show-rewrites rule-id enabled-only-flg ens current-term abbreviations term-id-iff all-hyps (geneqv-at-subterm-top conc current-addr ens w) nil state))))) (define-pc-macro sr (&rest args) "same as SHOW-REWRITES~/ ~bv[] Example: sr~/ General Form: (sr &optional rule-id enabled-only-flg) ~ev[] See the documentation for ~c[show-rewrites], as ~c[sr] and ~c[show-rewrites] are identical." (value (cons :show-rewrites args))) (define-pc-help show-linears (&optional rule-id enabled-only-flg) "display the applicable ~il[linear] rules~/ ~bv[] Example: show-linears~/ General Form: (show-linears &optional rule-id enabled-only-flg) ~ev[] This command displays ~il[linear] rules with a trigger term that matches the current subterm, and shows how they can be applied. This command is analogous to the ~c[show-rewrites] ~il[proof-checker] command; see its ~il[documentation] for details. Also see the ~il[documentation] for proof-checker command ~c[apply-linear] for how to apply linear rules." (when-goals (let ((conc (conc t)) (current-addr (current-addr t)) (w (w state))) (let ((ens (make-pc-ens (pc-ens t) state)) (current-term (fetch-term conc current-addr)) (abbreviations (abbreviations t)) (term-id-iff (term-id-iff conc current-addr t)) ; irrelevant? (all-hyps (union-equal (hyps t) (governors conc current-addr)))) (show-rewrites-linears-fn 'show-linears rule-id enabled-only-flg ens current-term abbreviations term-id-iff all-hyps (geneqv-at-subterm-top conc current-addr ens w) ; irrelevant? nil state))))) (define-pc-macro sls (&rest args) "same as SHOW-LINEARS~/ ~bv[] Example: srs~/ General Form: (srs &optional rule-id enabled-only-flg) ~ev[] See the documentation for ~c[show-linears], as ~c[sls] and ~c[show-linears] are identical. NOTE: In analogy to the ~c[sr] abbreviation for ~c[show-rewrites], one might expect this command to be ~c[sl]; but that name was taken (``simplify with lemmas'') before ~c[sls] was implemented.~/" (value (cons :show-linears args))) (define-pc-macro pl (&optional x) "print the rules for a given name~/ ~bv[] Examples: pl (pl foo)~/ General Form: (pl &optional x) ~ev[] This command simply invokes the corresponding command of the top-level ACL2 loop; ~pl[pl]. If no argument is given, or if the argument is ~c[nil], then the current subterm should be a call of a function symbol, and the argument is taken to be that symbol. If you want information about applying rewrite rules to the current subterm, consider the ~c[show-rewrites] (or equivalently, ~c[sr]) command." (cond (x (value `(lisp (pl ',x)))) ((null (goals)) (pprogn (print-all-goals-proved-message state) (value 'skip))) (t (let* ((conc (conc t)) (current-addr (current-addr t)) (current-term (fetch-term conc current-addr))) (cond ((or (variablep current-term) (fquotep current-term) (flambda-applicationp current-term)) (er soft 'pl "The current subterm is not the application of a ~ function symbol.")) (t (value `(lisp (pl ',(ffn-symb current-term)))))))))) (define-pc-macro pr (&optional x) "print the rules for a given name~/ ~bv[] Examples: pr (pr foo)~/ General Form: (pr &optional x) ~ev[] This command simply invokes the corresponding command of the top-level ACL2 loop; ~pl[pr]. If no argument is given, or if the argument is ~c[nil], then the current subterm should be a call of a function symbol, and the argument is taken to be that symbol. If you want information about applying rewrite rules to the current subterm, consider the ~c[show-rewrites] (or equivalently, ~c[sr]) command." (cond (x (value `(lisp (pr ',x)))) ((null (goals)) (pprogn (print-all-goals-proved-message state) (value 'skip))) (t (let* ((conc (conc t)) (current-addr (current-addr t)) (current-term (fetch-term conc current-addr))) (cond ((or (variablep current-term) (fquotep current-term) (flambda-applicationp current-term)) (er soft 'pr "The current subterm is not the application of a ~ function symbol.")) (t (value `(lisp (pr ',(ffn-symb current-term)))))))))) (define-pc-help show-type-prescriptions (&optional rule-id) "display the applicable ~il[type-prescription] rules~/ ~bv[] Example: show-type-prescriptions~/ General Form: (show-type-prescriptions &optional rule-id) ~ev[] Display ~il[type-prescription] rules that apply to the current subterm. If ~c[rule-id] is supplied and is a name (non-~c[nil] symbol) or a ~c[:]~ilc[rewrite] or ~c[:]~ilc[definition] ~il[rune], then only the corresponding rewrite rule(s) will be displayed. In each case, the display will point out when a rule is currently disabled (in the interactive environment). Also ~pl[type-prescription]." (when-goals (let ((conc (conc t)) (current-addr (current-addr t))) (let ((ens (make-pc-ens (pc-ens t) state)) (current-term (fetch-term conc current-addr)) (abbreviations (abbreviations t)) (all-hyps (union-equal (hyps t) (governors conc current-addr)))) (show-type-prescription-rules current-term rule-id abbreviations all-hyps ens state))))) (define-pc-macro st (&rest args) "same as SHOW-TYPE-PRESCRIPTIONS~/ ~bv[] Example: sr~/ General Form: (st &optional rule-id) ~ev[] See the documentation for ~c[show-type-prescriptions], as ~c[st] and ~c[show-type-prescriptions] are identical." (value (cons :show-type-prescriptions args))) (defun translate-subst-abb1 (sub abbreviations state) ;; Here sub is a list of doublets (variable form) ;; and we return a triple (erp val state). If the erp is non-nil then ;; we use it to decode the message returned in the value component. ;; We'll assume that #\s is bound to the original substitution. ;; We should check somewhere else that sub is an alistp. ;; We have to pass in and return state because of the call to translate. (declare (xargs :guard (symbol-alistp sub))) (if (consp sub) (mv-let (erp term state) (trans0 (cadar sub) abbreviations 'translate-subst-abb1) (if erp (mv "~|Translation error for ~x0 caused error in ~ translating ~xs.~|" (list (cons #\0 (cadar sub))) state) (mv-let (erp val state) (translate-subst-abb1 (cdr sub) abbreviations state) (if erp (mv erp val state) (mv nil (cons (cons (caar sub) term) val) state))))) (mv nil nil state))) (defun single-valued-symbolp-alistp (alist) (declare (xargs :guard (symbol-alistp alist))) (if alist (and (not (assoc-eq (caar alist) (cdr alist))) (single-valued-symbolp-alistp (cdr alist))) t)) (defun check-cars-are-variables (alist state) ;; return T if there's a problem (declare (xargs :guard (symbol-alistp alist))) (if alist (mv-let (erp val state) (trans0 (caar alist) nil) (if (or erp (not (eq val (caar alist)))) (pprogn (io? proof-checker nil state (alist) (fms0 "~|A substitution must be an alist whose CARs ~ are variables, but the entry ~x0 violates this ~ property.~|" (list (cons #\0 (caar alist))))) (mv t state)) (check-cars-are-variables (cdr alist) state))) (mv nil state))) (defun translate-subst-abb (sub abbreviations state) (cond ((not (true-listp sub)) (pprogn (io? proof-checker nil state (sub) (fms0 "~|A substitution must be a true (null-terminated) ~ list, but~%~x0 is not.~|" (list (cons #\0 sub)))) (mv t nil state))) ((not (and (symbol-alistp sub) (single-valued-symbolp-alistp sub))) (pprogn (io? proof-checker nil state (sub) (fms0 "~|A substitution must be an alist of pairs without ~ duplicate keys, but ~x0 is not.~|" (list (cons #\0 sub)))) (mv t nil state))) (t (mv-let (erp state) (check-cars-are-variables sub state) (if erp (mv t nil state) (mv-let (erp val state) (translate-subst-abb1 sub abbreviations state) (if erp (pprogn (io? proof-checker nil state (val sub erp) (fms0 erp (cons (cons #\s sub) val))) (mv t nil state)) (mv nil val state)))))))) (defun make-rewrite-instr (lemma-id raw-subst instantiate-free) (list* (make-pretty-pc-command :rewrite) lemma-id (cond (instantiate-free (list raw-subst instantiate-free)) (raw-subst (list raw-subst)) (t nil)))) (define-pc-primitive rewrite (&optional rule-id raw-sub instantiate-free) ; Warning: Keep this in sync with the proof-checker apply-linear command. "apply a rewrite rule~/ ~bv[] Examples: (rewrite reverse-reverse) -- apply the rewrite rule `reverse-reverse' (rewrite (:rewrite reverse-reverse)) -- same as above (rewrite 2) -- apply the second rewrite rule, as displayed by show-rewrites rewrite -- apply the first rewrite rule, as displayed by show-rewrites (rewrite transitivity-of-< ((y 7))) -- apply the rewrite rule transitivity-of-< with the substitution that associates 7 to the ``free variable'' y (rewrite foo ((x 2) (y 3)) t) -- apply the rewrite rule foo by substituting 2 and 3 for free variables x and y, respectively, and also binding all other free variables possible by using the current context (hypotheses and governors)~/ General Form: (rewrite &optional rule-id substitution instantiate-free) ~ev[] Replace the current subterm with a new term by applying a ~il[rewrite] or ~il[definition] rule. The replacement will be done according to the information provided by the ~c[show-rewrites] (~c[sr]) command. See also the ~c[linear] command, for an analogous command to use with ~il[linear] rules. If ~c[rule-id] is a positive integer ~c[n], then the ~c[n]th rule as displayed by ~c[show-rewrites] is the one that is applied. If ~c[rule-id] is ~c[nil] or is not supplied, then it is treated as the number 1. Otherwise, ~c[rule-id] should be either a symbol or else a ~c[:rewrite] or ~c[:definition] ~il[rune]. If a symbol is supplied, then any (~c[:rewrite] or ~c[:definition]) rule of that name may be used. We say more about this, and describe the other optional arguments, below. Consider first the following example. Suppose that the current subterm is ~c[(reverse (reverse y))] and that there is a ~il[rewrite] rule called ~c[reverse-reverse] of the form ~bv[] (implies (true-listp x) (equal (reverse (reverse x)) x)) . ~ev[] Then the instruction ~c[(rewrite reverse-reverse)] causes the current subterm to be replaced by ~c[y] and creates a new goal with conclusion ~c[(true-listp y)]. An exception is that if the top-level hypotheses imply ~c[(true-listp y)] using only ``trivial reasoning'' (more on this below), then no new goal is created. If the ~c[rule-id] argument is a number or is not supplied, then the system will store an instruction of the form ~c[(rewrite name ...)], where ~c[name] is the name of a rewrite rule; this is in order to make it easier to replay instructions when there have been changes to the history. Except: instead of the name (whether the name is supplied or calculated), the system stores the ~il[rune] if there is any chance of ambiguity. (Formally, ``ambiguity'' here means that the rune being applied is of the form ~c[(:rewrite name . index)], where index is not ~c[nil].) Speaking in general, then, a ~c[rewrite] instruction works as follows: First, a ~il[rewrite] or ~il[definition] rule is selected according to the arguments of the ~c[rewrite] instruction. The selection is made as explained under ``General Form'' above. Next, the left-hand side of the rule is matched with the current subterm, i.e., a substitution ~c[unify-subst] is found such that if one instantiates the left-hand side of the rule with ~c[unify-subst], then one obtains the current subterm. If this match fails, then the instruction fails. Next, an attempt is made to relieve (discharge) the hypotheses, much as the theorem prover relieves hypotheses except that there is no call to the rewriter. First, the substitution ~c[unify-subst] is extended with the ~c[substitution] argument, which may bind free variables (~pl[free-variables]). Each hypothesis of the rule is then considered in turn, from first to last. For each hypothesis, first the current substitution is applied, and then the system checks whether the hypothesis is ``clearly'' true in the current context. If there are variables in the hypotheses of the rule that are not bound by the current substitution, then a weak attempt is made to extend that substitution so that the hypothesis is present in the current context (see the documentation for the proof-checker ~c[hyps] command under ~il[proof-checker-commands]), much as would be done by the theorem prover's rewriter. If in the process above there are free variables (~pl[free-variables]), but the proof-checker can see how to bind them to relieve all hypotheses, then it will do so in both the ~c[show-rewrites] (~c[sr]) and ~c[rewrite] commands. But normally, if even one hypothesis remains unrelieved, then no automatic extension of the substitution is made. Except, if ~c[instantiate-free] is not ~c[nil], then that extension to the substitution is kept. (Technical note: in the case of an unrelieved hypothesis and a non-~c[nil] value of ~c[instantiate-free], if a ~ilc[bind-free] hypothesis produces a list of binding alists, then the last of those alists is the one that is used to extend the substitution.) Finally, the instruction is applied as follows. The current subterm is replaced by applying the final substitution described above to the right-hand side of the selected rule. And, one new subgoal is created for each unrelieved hypothesis of the rule, whose top-level hypotheses are the governors and top-level hypotheses of the current goal and whose conclusion and current subterm are the instance, by that same final substitution, of that unrelieved hypothesis. ~st[Remark:] The substitution argument should be a list whose elements have the form ~c[(variable term)], where ~c[term] may contain abbreviations." (mv-let (erp sub state) (translate-subst-abb raw-sub abbreviations state) (if erp (print-no-change2 "~x0 failed." (list (cons #\0 (cons :rewrite args)))) (let ((name (and (symbolp rule-id) rule-id)) (rune (and (consp rule-id) (member-eq (car rule-id) '(:rewrite :definition)) rule-id)) (index (if (and (integerp rule-id) (< 0 rule-id)) rule-id (if rule-id nil 1))) (pc-ens (make-pc-ens pc-ens state)) (w (w state)) (current-term (fetch-term conc current-addr)) (assumptions (union-equal hyps (governors conc current-addr)))) (cond ((or (variablep current-term) (fquotep current-term) (flambdap (ffn-symb current-term))) (print-no-change2 "It is only possible to apply rewrite rules to terms that are not ~ variables, (quoted) constants, or applications of lambda ~ expressions. However, the current term is:~%~ ~ ~y0.~|" (list (cons #\0 current-term)))) ((not (or name index rune)) (print-no-change2 "The rule-id argument to REWRITE must be a name, a positive ~ integer, or a :rewrite or :definition rune, but ~x0 is none of ~ these.~|" (list (cons #\0 rule-id)))) (t (mv-let (flg hyps-type-alist ttree) (hyps-type-alist assumptions pc-ens w state) (declare (ignore ttree)) (if flg (print-no-change2 "Contradiction in the hypotheses!~%The S command should ~ complete this goal.~|") (let ((app-rewrite-rules (applicable-rewrite-rules current-term conc current-addr (or name rune) index pc-ens w))) (if (null app-rewrite-rules) (if (and index (> index 1)) (print-no-change2 "There are fewer than ~x0 applicable rewrite rules.~%" (list (cons #\0 index))) (print-no-change2 "There are no applicable rewrite rules.~%")) (let* ((sar (car app-rewrite-rules)) (lemma (access sar sar :lemma)) (start-alist (access sar sar :alist)) (alist (append start-alist sub)) (rhs (access rewrite-rule lemma :rhs)) (lemma-hyps (access rewrite-rule lemma :hyps)) (lemma-rune (access rewrite-rule lemma :rune)) (lemma-name (base-symbol lemma-rune)) (lemma-id (if (cddr lemma-rune) lemma-rune lemma-name)) (non-free (union-eq (intersection-domains sub start-alist) (set-difference-eq (strip-cars sub) (append (all-vars rhs) (all-vars1-lst lemma-hyps nil)))))) (if non-free (print-no-change2 "The variable~#0~[~/~/s~] ~&1 supplied by the ~ substitution in this instruction ~#0~[~/is~/are~] not ~ free for instantiation in the current context.~|" (list (cons #\0 (zero-one-or-more (length non-free))) (cons #\1 non-free))) (mv-let (subst-hyps unify-subst ttree2) (unrelieved-hyps lemma-rune lemma-hyps alist hyps-type-alist instantiate-free w state pc-ens nil) (pprogn (let ((extra-alist (alist-difference-eq unify-subst alist))) (if extra-alist (io? proof-checker nil state (abbreviations extra-alist sub lemma-id) (fms0 "~|Rewriting with ~x0 under the ~ following extension of the the ~ substitution generated by matching ~ that rewrite rule with the current ~ term ~#1~[ (after extending it with ~ the substitution ~x2 supplied in the ~ instruction)~/~]: ~x3.~|" (list (cons #\0 lemma-id) (cons #\1 (if sub 0 1)) (cons #\2 sub) (cons #\3 (untranslate-subst-abb extra-alist abbreviations state))))) (io? proof-checker nil state (lemma-id) (fms0 "~|Rewriting with ~x0.~|" (list (cons #\0 lemma-id)))))) (let ((runes (all-runes-in-ttree ttree2 nil))) (if runes (io? proof-checker nil state (runes) (fms0 "~|--NOTE-- Using the following runes ~ in addition to the indicated rule:~% ~ ~x0.~|" (list (cons #\0 runes)))) state)) (let ((new-goals (make-new-goals-fixed-hyps subst-hyps assumptions goal-name depends-on))) (mv-let (changed-goal state) (deposit-term-in-goal (car goals) conc current-addr (sublis-var unify-subst (access rewrite-rule lemma :rhs)) state) (mv (change-pc-state pc-state :instruction (make-rewrite-instr lemma-id raw-sub instantiate-free) :goals (cons (change goal changed-goal :depends-on (+ depends-on (length new-goals))) (append new-goals (cdr goals))) :local-tag-tree (push-lemma lemma-rune ttree2)) state))))))))))))))))) (defun applicable-linear-rules (current-term target-name-or-rune target-index wrld) ; See applicable-rewrite-rules for the analogous function for rewrite rules. (declare (xargs :guard (not (or (variablep current-term) (fquotep current-term) (flambdap (ffn-symb current-term)))))) (applicable-linear-rules1 current-term (getprop (ffn-symb current-term) 'linear-lemmas nil 'current-acl2-world wrld) 1 target-name-or-rune target-index)) (defun make-linear-instr (lemma-id raw-subst instantiate-free) (list* (make-pretty-pc-command :linear) lemma-id (cond (instantiate-free (list raw-subst instantiate-free)) (raw-subst (list raw-subst)) (t nil)))) (define-pc-primitive apply-linear (&optional rule-id raw-sub instantiate-free) ; Warning: Keep this in sync with the proof-checker rewrite command. "apply a linear rule~/ ~bv[] Examples: (apply-linear foo) -- apply the linear rule `foo' (apply-linear (:linear foo)) -- same as above (apply-linear 2) -- apply the second linear rule, as displayed by show-linears rewrite -- apply the first rewrite rule, as displayed by show-rewrites (apply-linear foo ((y 7))) -- apply the linear rule foo with the substitution that associates 7 to the ``free variable'' y (apply-linear foo ((x 2) (y 3)) t) -- apply the linear rule foo by substituting 2 and 3 for free variables x and y, respectively, and also binding all other free variables possible by using the current context (hypotheses and governors)~/ General Form: (apply-linear &optional rule-id substitution instantiate-free) ~ev[] Add a new top-level hypothesis by applying a ~il[linear] rule to the current subterm. The new hypothesis will be created according to the information provided by the ~c[show-linears] (~c[sls]) command. A short name for this command is ~c[al]. We assume familiarity with the ~il[proof-checker]'s ~c[rewrite] (~c[r]) command. In brief, the ~c[apply-linear] command is an analogue of the ~c[rewrite] command, but for ~il[linear] rules in place of ~il[rewrite] rules. There is a significant difference: for the ~c[apply-linear] command, instead of rewriting the current subterm as is done by the ~c[rewrite] command, the conclusion of the applicable linear rule, suitably instantiated, is added as a new (and last) top-level hypothesis of the goal. There is another significant difference: the automatic application of ~il[linear] rules in the theorem prover is somewhat more complex than the automatic application of ~il[rewrite] rules, so the ~c[apply-linear] command may not correspond as closely to the prover's automatic use of a linear rule as the ~c[rewrite] command corresponds to the prover's automatic use of a rewrite rule. Below, we refer freely to the ~il[documentation] for the proof-checker's ~c[rewrite] command. The ~c[rule-id] is treated just as it is by the ~c[rewrite] command. If ~c[rule-id] is a positive integer ~c[n], then the ~c[n]th rule as displayed by ~c[show-linears] is the one that is applied. If ~c[rule-id] is ~c[nil] or is not supplied, then it is treated as the number 1. Otherwise, ~c[rule-id] should be either a symbol or else a ~c[:linear] ~il[rune]. If a symbol is supplied, then any ~il[linear] rule of that name may be used. Consider the following example. Suppose that the current subterm is ~c[(< (g (h y)) y)] and that ~c[foo] is the name of the following linear rule. ~bv[] (implies (true-listp x) (< (g x) 15)) ~ev[] Then the instruction ~c[(apply-linear foo)] applies ~c[foo] by adding a new hypothesis ~c[(< (g (h y)) 15)]. In addition, a new goal with conclusion ~c[(true-listp y)] is created unless the current context (top-level hypotheses and governors) implies ~c[(true-listp y)] using only ``trivial reasoning'', just as for the ~c[rewrite] command. If the ~c[rule-id] argument is a number or is not supplied, then the system will store an instruction of the form ~c[(apply-linear name ...)], where ~c[name] is the name of a ~il[linear] rule; this is in order to make it easier to replay instructions when there have been changes to the history. Except: instead of the name (whether the name is supplied or calculated), the system stores the ~il[rune] if there is any chance of ambiguity. (Formally, ``ambiguity'' here means that the rune being applied is of the form ~c[(:rewrite name . index)], where index is not ~c[nil].) Speaking in general, then, an ~c[apply-linear] instruction works as follows. First, a ~il[linear] rule is selected according to the arguments of the instruction. The selection is made as explained under ``General Form'' above. Next, a trigger term of the rule (~pl[linear]) is matched with the current subterm, i.e., a substitution ~c[unify-subst] is found such that if one instantiates that trigger term of the rule with ~c[unify-subst], then one obtains the current subterm. If this match fails, then the instruction fails. Next, an attempt is made to relieve (discharge) the hypotheses, possibly handling free variables (~pl[free-variables]), exactly as is done with hypotheses when applying the ~il[proof-checker] command, ~c[rewrite] (~c[r]). Finally, the instruction is applied exactly as the ~c[rewrite] instruction is applied, except instead of replacing the current subterm, the rule's instantiated conclusion is added to the end of the list of top-level hypotheses of the goal. Note that as for the ~c[rewrite] command, the substitution argument should be a list whose elements have the form ~c[(variable term)], where ~c[term] may contain abbreviations." (mv-let (erp sub state) (translate-subst-abb raw-sub abbreviations state) (if erp (print-no-change2 "~x0 failed." (list (cons #\0 (cons :rewrite args)))) (let ((name (and (symbolp rule-id) rule-id)) (rune (and (consp rule-id) (member-eq (car rule-id) '(:linear)) rule-id)) (index (if (and (integerp rule-id) (< 0 rule-id)) rule-id (if rule-id nil 1))) (pc-ens (make-pc-ens pc-ens state)) (w (w state)) (current-term (fetch-term conc current-addr)) (assumptions (union-equal hyps (governors conc current-addr)))) (cond ((or (variablep current-term) (fquotep current-term) (flambdap (ffn-symb current-term))) (print-no-change2 "It is only possible to apply linear rules to terms that are not ~ variables, (quoted) constants, or applications of lambda ~ expressions. However, the current term is:~%~ ~ ~y0.~|" (list (cons #\0 current-term)))) ((not (or name index rune)) (print-no-change2 "The rule-id argument to REWRITE must be a name, a positive ~ integer, or a :linear rune, but ~x0 is none of these.~|" (list (cons #\0 rule-id)))) (t (mv-let (flg hyps-type-alist ttree) (hyps-type-alist assumptions pc-ens w state) (declare (ignore ttree)) (if flg (print-no-change2 "Contradiction in the hypotheses!~%The S command should ~ complete this goal.~|") (let ((app-linear-rules (applicable-linear-rules current-term (or name rune) index w))) (if (null app-linear-rules) (if (and index (> index 1)) (print-no-change2 "There are fewer than ~x0 applicable linear rules.~%" (list (cons #\0 index))) (print-no-change2 "There are no applicable linear rules.~%")) (let* ((sar (car app-linear-rules)) (lemma (access sar sar :lemma)) (start-alist (access sar sar :alist)) (alist (append start-alist sub)) (lemma-concl (access linear-lemma lemma :concl)) (lemma-hyps (access linear-lemma lemma :hyps)) (lemma-rune (access linear-lemma lemma :rune)) (lemma-name (base-symbol lemma-rune)) (lemma-id (if (cddr lemma-rune) lemma-rune lemma-name)) (non-free (union-eq (intersection-domains sub start-alist) (set-difference-eq (strip-cars sub) (append (all-vars lemma-concl) (all-vars1-lst lemma-hyps nil)))))) (if non-free (print-no-change2 "The variable~#0~[~/~/s~] ~&1 supplied by the ~ substitution in this instruction ~#0~[~/is~/are~] not ~ free for instantiation in the current context.~|" (list (cons #\0 (zero-one-or-more (length non-free))) (cons #\1 non-free))) (mv-let (subst-hyps unify-subst ttree2) (unrelieved-hyps lemma-rune lemma-hyps alist hyps-type-alist instantiate-free w state pc-ens nil) (pprogn (let ((extra-alist (alist-difference-eq unify-subst alist))) (if extra-alist (io? proof-checker nil state (abbreviations extra-alist sub lemma-id) (fms0 "~|Apply linear rule ~x0 under the ~ following extension of the the ~ substitution generated by matching ~ that rule's trigger term with the ~ current term ~#1~[ (after extending ~ it with the substitution ~x2 supplied ~ in the instruction)~/~]: ~x3.~|" (list (cons #\0 lemma-id) (cons #\1 (if sub 0 1)) (cons #\2 sub) (cons #\3 (untranslate-subst-abb extra-alist abbreviations state))))) (io? proof-checker nil state (lemma-id) (fms0 "~|Applying linear rule ~x0.~|" (list (cons #\0 lemma-id)))))) (let ((runes (all-runes-in-ttree ttree2 nil))) (if runes (io? proof-checker nil state (runes) (fms0 "~|--NOTE-- Using the following runes ~ in addition to the indicated rule:~% ~ ~x0.~|" (list (cons #\0 runes)))) state)) (let ((new-goals (make-new-goals-fixed-hyps subst-hyps assumptions goal-name depends-on))) (let ((changed-goal (change goal (car goals) :hyps (append hyps (list (sublis-var unify-subst lemma-concl))) :depends-on (+ depends-on (length new-goals))))) (mv (change-pc-state pc-state :instruction (make-linear-instr lemma-id raw-sub instantiate-free) :goals (cons changed-goal (append new-goals (cdr goals))) :local-tag-tree (push-lemma lemma-rune ttree2)) state))))))))))))))))) (define-pc-macro al (&rest args) "same as apply-linear~/ ~bv[] Example: (al 3)~/ ~ev[] See the documentation for ~c[apply-linear], as ~c[al] and ~c[apply-linear] are identical." (value (cons :apply-linear args))) (defun pc-help-fn (name state) ;; Adapted in part from doc-fn. (declare (xargs :guard (and (symbolp name) (equal (symbol-package-name name) "ACL2-PC")))) (let ((name (if (equal (symbol-name name) "ALL") 'proof-checker-commands name))) (cond ((not (or (eq name 'proof-checker-commands) (pc-command-type name))) (pprogn (io? proof-checker nil state (name) (fms0 "~%*** Undefined command, ~x0.~%" (list (cons #\0 (make-pretty-pc-command name))))) (value :invisible))) (t (let ((channel (proofs-co state)) (doc-tuple (access-doc-string-database name state))) (cond ((null doc-tuple) (pprogn (io? proof-checker nil state (channel name) (fms "No help is available for ~s0.~|" (list (cons #\0 (symbol-name name))) channel state nil)) (value nil))) (t (state-global-let* ((print-doc-start-column nil)) (pprogn (print-doc (cons (if (eq name 'proof-checker-commands) name (intern-in-keyword-package name)) (cdr doc-tuple)) 0 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (print-doc doc-tuple 1 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (newline channel state) (end-doc channel state)))))))))) (defmacro state-only (triple) `(mv-let (erp val state) ,triple (declare (ignore erp val)) state)) (define-pc-help help (&optional instr) "proof-checker help facility~/ ~bv[] Examples: (help all) -- list all proof-checker commands (help rewrite) -- partial documentation on the rewrite command; the rest is available using more or more! (help! rewrite) -- full documentation on the rewrite command help, help! -- this documentation (in part, or in totality, respectively)~/ General Forms: (help &optional command) (help! &optional command) more more! ~ev[] The proof checker supports the same kind of documentation as does ACL2 proper. The main difference is that you need to type ~bv[] (help command) ~ev[] in a list rather than ~c[:doc command]. So, to get all the documentation on ~c[command], type ~c[(help! command)] inside the interactive loop, but to get only a one-line description of the command together with some examples, type ~c[(help command)]. In the latter case, you can get the rest of the help by typing ~c[more!]; or type ~c[more] if you don't necessarily want all the rest of the help at once. (Then keep typing ~c[more] if you want to keep getting more of the help for that command.) An exception is ~c[(help all)], which prints the documentation topic ~c[proof-checker-commands], to show you all possible proof-checker commands. So for example, when you see ~c[ACL2-PC::USE] in that list, you can then submit ~c[(help use)] or ~c[(help! use)] to get documentation for the proof-checker ~c[use] command. But summarizing for other than the case of ~c[all]: as with ACL2, you can type either of the following: ~bv[] more, more! -- to obtain more (or, all the rest of) the documentation last requested by help (or, outside the proof-checker's loop, :doc) ~ev[] It has been arranged that the use of ~c[(help command)] will tell you just about everything you could want to know about ~c[command], almost always by way of examples. For more details about a command, use ~c[help!], ~c[more], or ~c[more!]. We use the word ``command'' to refer to the name itself, e.g. ~c[rewrite]. We use the word ``instruction'' to refer to an input to the interactive system, e.g. ~c[(rewrite foo)] or ~c[(help split)]. Of course, we allow commands with no arguments as instructions in many cases, e.g. ~c[rewrite]. In such cases, ~c[command] is treated identically to ~c[(command)]." (let ((comm (make-official-pc-command (if args (if (consp instr) (car instr) instr) 'help)))) (state-only (pc-help-fn comm state)))) (defun pc-help!-fn (name state) ;; Adapted in part from doc-fn. (declare (xargs :guard (and (symbolp name) (equal (symbol-package-name name) "ACL2-PC")))) (cond ((equal (symbol-name name) "ALL") (pc-help-fn name state)) ((not (pc-command-type name)) (pprogn (io? proof-checker nil state (name) (fms0 "~%*** Undefined command, ~x0.~%" (list (cons #\0 (make-pretty-pc-command name))))) (value :invisible))) (t (let ((channel (proofs-co state)) (doc-tuple (access-doc-string-database name state))) (cond ((null doc-tuple) (pprogn (io? proof-checker nil state (channel name) (fms "No help is available for ~s0.~|" (list (cons #\0 (symbol-name name))) channel state nil)) (value nil))) (t (state-global-let* ((print-doc-start-column nil)) (pprogn (print-doc (cons (intern-in-keyword-package name) (cdr doc-tuple)) 0 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (print-doc doc-tuple 1 (doc-prefix state) (doc-markup-table state) (doc-char-subst-table state) (doc-fmt-alist state) channel state) (princ-prefix (doc-prefix state) channel state) (newline channel state) (more-fn t state))))))))) (define-pc-help help! (&optional instr) "proof-checker help facility~/ Same as ~c[help], except that the entire help message is printed without any need to invoke ~c[more!] or ~c[more].~/ Invoke ~c[help] for documentation about the proof-checker help facility." (let ((comm (make-official-pc-command (if instr (if (consp instr) (car instr) instr) 'help)))) (state-only (pc-help!-fn comm state)))) (define-pc-macro help-long (&rest args) "same as ~c[help!]~/ See the documentation for ~c[help!].~/ ~c[Help-long] has been included in addition to ~c[help!] for historical reasons. (Such a command is included in Pc-Nqthm)." (value (cons 'help! args))) (define-pc-help more () "proof-checker help facility~/ Continues documentation of last proof-checker command visited with ~c[help].~/ Invoke ~c[help] for documentation about the proof-checker help facility." (state-only (more-fn 0 state))) (define-pc-help more! () "proof-checker help facility~/ Continues documentation of last proof-checker command visited with ~c[help], until all documentation on that command is printed out.~/ Invoke ~c[help] for documentation about the proof-checker help facility." (state-only (more-fn t state))) (defun pc-rewrite*-1 (term type-alist geneqv iff-flg wrld rcnst old-ttree pot-lst normalize-flg rewrite-flg ens state repeat backchain-limit step-limit) ; This function may be called with a pot-lst of nil in the proof-checker, but ; need not be can figure out a good way to do linear there. Also, note that ; rcnst can be anything (and is ignored) if rewrite-flg is not set. (mv-let (nterm old-ttree) (if normalize-flg (normalize term iff-flg type-alist ens wrld old-ttree) (mv term old-ttree)) (sl-let (newterm ttree) (if rewrite-flg (let ((gstack nil)) (rewrite-entry (rewrite nterm nil 'proof-checker) :rdepth (rewrite-stack-limit wrld) :step-limit step-limit :obj '? :fnstack nil :ancestors nil :simplify-clause-pot-lst pot-lst :rcnst (change rewrite-constant rcnst :current-literal (make current-literal :atm nterm :not-flg nil)) :gstack gstack :ttree old-ttree)) (mv 0 ; irrelevant step-limit nterm old-ttree)) (declare (ignorable step-limit)) (cond ((equal newterm nterm) (mv step-limit newterm old-ttree state)) ((<= repeat 0) (mv step-limit newterm ttree state)) (t (pc-rewrite*-1 newterm type-alist geneqv iff-flg wrld rcnst ttree pot-lst normalize-flg rewrite-flg ens state (1- repeat) backchain-limit step-limit)))))) (defun pc-rewrite* (term type-alist geneqv iff-flg wrld rcnst old-ttree pot-lst normalize-flg rewrite-flg ens state repeat backchain-limit step-limit) (sl-let (newterm ttree state) (catch-step-limit (pc-rewrite*-1 term type-alist geneqv iff-flg wrld rcnst old-ttree pot-lst normalize-flg rewrite-flg ens state repeat backchain-limit step-limit)) (cond ((eql step-limit -1) (mv step-limit term old-ttree state)) (t (mv step-limit newterm ttree state))))) (defun make-goals-from-assumptions (assumptions conc hyps current-addr goal-name start-index) (if assumptions (cons (make goal :conc conc :hyps (cons (dumb-negate-lit (car assumptions)) hyps) :current-addr current-addr :goal-name (cons goal-name start-index) :depends-on 1) (make-goals-from-assumptions (cdr assumptions) conc hyps current-addr goal-name (1+ start-index))) nil)) (defun make-new-goals-from-assumptions (assumptions goal) (and assumptions (make-goals-from-assumptions assumptions (access goal goal :conc) (access goal goal :hyps) (access goal goal :current-addr) (access goal goal :goal-name) (access goal goal :depends-on)))) (defconst *default-s-repeat-limit* 10) (define-pc-primitive s (&rest args) "simplify the current subterm~/ ~bv[] Examples: S -- simplify the current subterm (S :backchain-limit 2 :normalize t :expand (append x z)) -- simplify the current subterm, but during the rewriting process first ``normalize'' it by pushing IFs to the top-level, and also force the term (append x z) to be expanded during the rewriting process~/ General Form: (s &key rewrite normalize backchain-limit repeat in-theory hands-off expand) ~ev[] Simplify the current subterm according to the keyword parameters supplied. First if-normalization is applied (unless the ~c[normalize] argument is ~c[nil]), i.e., each subterm of the form ~c[(f ... (if test x y) ...)] is replaced by the term ~c[(if test (f ... x ...) (f ... y ...))] except, of course, when ~c[f] is ~c[if] and the indicated ~c[if] subterm is in the second or third argument position. Then rewriting is applied (unless the rewrite argument is ~c[nil]). Finally this pair of actions is repeated ~-[] until the rewriting step causes no change in the term. A description of each parameter follows. ~bv[] :rewrite -- default t ~ev[] When non-~c[nil], instructs the system to use ACL2's rewriter (or, something close to it) during simplification. ~bv[] :normalize -- default t ~ev[] When non-~c[nil], instructs the system to use if-normalization (as described above) during simplification. ~bv[] :backchain-limit -- default 0 ~ev[] Sets the number of recursive calls to the rewriter that are allowed for backchaining. Even with the default of 0, some reasoning is allowed (technically speaking, type-set reasoning is allowed) in the relieving of hypotheses. The value should be ~c[nil] or a non-negative integer, and limits backchaining only for rewriting, not for type-set reasoning. ~bv[] :repeat -- default 0 ~ev[] Sets the number of times the current term is to be rewritten. If this value is ~c[t], then the default is used (as specified by the constant ~c[*default-s-repeat-limit*]). ~bv[] :in-theory, :hands-off, :expand ~ev[] These have their usual meaning; ~pl[hints]. ~st[Remark:] if conditional rewrite rules are used that cause case splits because of the use of ~c[force], then appropriate new subgoals will be created, i.e., with the same current subterm (and address) but with each new (forced) hypothesis being negated and then used to create a corresponding new subgoal. In that case, the current goal will have all such new hypotheses added to the list of top-level hypotheses." (cond ((not (keyword-value-listp args)) (print-no-change2 "The argument list to S must be a KEYWORD-VALUE-LISTP, i.e. a list of ~ the form (:kw-1 val-1 ... :kw-n val-n), where each of the arguments ~ :kw-i is a keyword. Your argument list ~x0 does not have this ~ property. Try (HELP S)." (list (cons #\0 args)))) (t (let ((comm (make-official-pc-command 's)) (w (w state)) (current-term (fetch-term conc current-addr)) (assumptions (union-equal hyps (governors conc current-addr)))) (let ((pc-ens (make-pc-ens pc-ens state))) (mv-let (bcl-alist rst) (pair-keywords '(:backchain-limit :normalize :rewrite :repeat) args) (let ((local-backchain-limit (or (cdr (assoc-eq :backchain-limit bcl-alist)) 0)) ; IF-normalization and rewriting will happen by default (normalize (let ((pair (assoc-eq :normalize bcl-alist))) (if pair (cdr pair) t))) (rewrite (let ((pair (assoc-eq :rewrite bcl-alist))) (if pair (cdr pair) t))) (repeat (let ((pair (assoc-eq :repeat bcl-alist))) (if pair (if (equal (cdr pair) t) *default-s-repeat-limit* (cdr pair)) 0)))) (cond ((not (or normalize rewrite)) (print-no-change2 "You may not specify in the S command that ~ neither IF normalization nor rewriting is to ~ take place.")) ((and (null rewrite) (or (assoc-eq :backchain-limit bcl-alist) (assoc-eq :repeat bcl-alist) rst)) (print-no-change2 "When the :REWRITE NIL option is specified, it ~ is not allowed to provide arguments other than ~ :NORMALIZE T. The argument list ~x0 violates ~ this requirement." (list (cons #\0 args)))) (t (mv-let (key-alist new-rst) (pair-keywords '(:in-theory :hands-off :expand) rst) (declare (ignore key-alist)) (cond (new-rst (print-no-change2 "The arguments to the S command must all be &KEY arguments, ~ which should be among ~&0. Your argument list ~x1 violates ~ this requirement." (list (cons #\0 '(:rewrite :normalize :backchain-limit :repeat :in-theory :hands-off :expand)) (cons #\1 args)))) (t (mv-let (erp hint-settings state) (translate-hint-settings comm "Goal" rst (if args (cons comm (car args)) comm) w state) (cond (erp (print-no-change2 "S failed.")) (t (mv-let (flg hyps-type-alist ttree) (hyps-type-alist assumptions pc-ens w state) (cond (flg (cond ((or (null current-addr) ; optimization (equal assumptions hyps) (mv-let (flg hyps-type-alist ttree) (hyps-type-alist hyps pc-ens w state) (declare (ignore hyps-type-alist ttree)) flg)) (pprogn (io? proof-checker nil state nil (fms0 "~|Goal proved: Contradiction in the ~ hypotheses!~|")) (mv (change-pc-state pc-state :goals (cond ((tagged-objects 'assumption ttree) ; See the comment in define-pc-primitive about leaving the top goal on the top ; of the :goals stack. (cons (change goal (car goals) :conc *t*) (cdr goals))) (t (cdr goals))) :local-tag-tree ttree) state))) (t (print-no-change2 "A contradiction was found in the current context ~ using both the top-level hypotheses and the IF ~ tests governing the current term, but not using the ~ top-level hypotheses alone. You may want to issue ~ the TOP command and then issue s-prop to prune some ~ branches of the conclusion.")))) (t (let* ((base-rcnst (and rewrite (change rewrite-constant *empty-rewrite-constant* :current-enabled-structure pc-ens :force-info t)))) (mv-let (erp local-rcnst state) (if rewrite (load-hint-settings-into-rcnst hint-settings base-rcnst nil w 'acl2-pc::s state) (value nil)) (pprogn (if erp (io? proof-checker nil state nil (fms0 "~|Note: Ignoring the above theory ~ invariant error. Proceeding...~|")) state) (if rewrite (maybe-warn-about-theory-from-rcnsts base-rcnst local-rcnst :s pc-ens w state) state) (sl-let (new-term new-ttree state) (pc-rewrite* current-term hyps-type-alist (geneqv-at-subterm-top conc current-addr pc-ens w) (term-id-iff conc current-addr t) w local-rcnst nil nil normalize rewrite pc-ens state repeat local-backchain-limit (initial-step-limit w state)) (pprogn (f-put-global 'last-step-limit step-limit state) (if (equal new-term current-term) (print-no-change2 "No simplification took place.") (pprogn (mv-let (new-goal state) (deposit-term-in-goal (car goals) conc current-addr new-term state) (mv (change-pc-state pc-state :goals (cons new-goal (cdr goals)) :local-tag-tree new-ttree) state))))))))))))))))))))))))))) ;; The proof-checker's enabled state will be either the global enabled ;; state or else a local one. The proof-checker command :IN-THEORY ;; takes zero or one arguments, the former specifying ``use the global ;; enabled state'' and the latter specifying ``create a local enabled ;; state from the current proof-checker enabled state by evaluating ;; the theory form that is given.'' This is an easy design to ;; implement: we'll use NIL in the pc-ens component of the pc-state ;; to mean that we should use the global state, and otherwise we'll ;; store an enabled structure with a root name particular to Pc-ACL2. ;; A subtlety is that (in-theory (current-theory :here)) is not quite ;; equivalent to (in-theory). The difference is that the former ;; stores a copy of the current global enabled state in the current ;; proof-checker state, and that's what will stay there even if the ;; global state is changed, while the latter stores NIL in the current ;; proof-checker state, which means that we'll use whatever is the ;; current global enabled state at the time. ;; I expect that this design will be pretty robust, in the sense that ;; it won't cause hard errors even when the user makes global changes ;; to the ACL2 world and then re-enters an interactive verification. ;; That's because the index-of-last-enabling component of an enabled ;; structure always protects it against inappropriate AREF1 calls ;; in ENABLED-NUMEP. (defun build-pc-enabled-structure-from-ens (new-suffix ens) (let* ((new-name-root '(#\P #\C #\- #\E #\N #\A #\B #\L #\E #\D #\- #\A #\R #\R #\A #\Y #\-)) (new-name (intern (coerce (append new-name-root (explode-nonnegative-integer new-suffix 10 nil)) 'string) "ACL2")) (old-name (access enabled-structure ens :array-name)) (old-alist (access enabled-structure ens :theory-array))) (change enabled-structure ens :theory-array (cons (list :header :dimensions (dimensions old-name old-alist) :maximum-length (maximum-length old-name old-alist) :default (default old-name old-alist) :name new-name) (cdr old-alist)) :array-name new-name :array-length (access enabled-structure ens :array-length) :array-name-root new-name-root :array-name-suffix new-suffix))) (define-pc-primitive in-theory (&optional theory-expr) "set the current proof-checker theory~/ ~bv[] Example: (in-theory (union-theories (theory 'minimal-theory) '(true-listp binary-append)))~/ General Form: (in-theory &optional atom-or-theory-expression) ~ev[] If the argument is not supplied, then this command sets the current proof-checker theory (see below for explanation) to agree with the current ACL2 theory. Otherwise, the argument should be a theory expression, and in that case the proof-checker theory is set to the value of that theory expression. The current proof-checker theory is used in all calls to the ACL2 theorem prover and rewriter from inside the proof-checker. Thus, the most recent ~c[in-theory] instruction in the current ~c[state-stack] has an effect in the proof-checker totally analogous to the effect caused by an ~c[in-theory] hint or event in ACL2. All ~c[in-theory] instructions before the last are ignored, because they refer to the current theory in the ACL2 ~ilc[state], not to the existing proof-checker theory. For example: ~bv[] ACL2 !>:trans1 (enable bar) (UNION-THEORIES (CURRENT-THEORY :HERE) '(BAR)) ACL2 !>:trans1 (CURRENT-THEORY :HERE) (CURRENT-THEORY-FN :HERE WORLD) ACL2 !> ~ev[] Thus ~c[(in-theory (enable bar))] modifies the current theory of the current ACL2 world. So for example, suppose that ~c[foo] is disabled outside the proof checker and you execute the following instructions, in this order. ~bv[] (in-theory (enable foo)) (in-theory (enable bar)) ~ev[] Then after the second of these, ~c[bar] will be enabled in the proof-checker, but ~c[foo] will be disabled. The reason is that ~c[(in-theory (enable bar))] instructs the proof-checker to modify the current theory (from outside the proof-checker, not from inside the proof-checker) by enabling ~c[bar]. Note that ~c[in-theory] instructions in the proof-checker have no effect outside the proof-checker's interactive loop. If the most recent ~c[in-theory] instruction in the current state of the proof-checker has no arguments, or if there is no ~c[in-theory] instruction in the current state of the proof-checker, then the proof-checker will use the current ACL2 theory. This is true even if the user has interrupted the interactive loop by exiting and changing the global ACL2 theory. However, if the most recent ~c[in-theory] instruction in the current state of the proof-checker had an argument, then global changes to the current theory will have no effect on the proof-checker state." (let ((w (w state)) (ens (ens state))) (if args (mv-let (erp hint-setting state) (translate-in-theory-hint theory-expr t 'acl2-pc::in-theory w state) (if erp (print-no-change2 "bad theory expression.") (let* ((new-suffix (pc-value next-pc-enabled-array-suffix)) (new-pc-ens1 (build-pc-enabled-structure-from-ens new-suffix ens))) (mv-let (erp new-pc-ens2 state) (load-theory-into-enabled-structure ;; this call compresses the appropriate array theory-expr hint-setting nil new-pc-ens1 nil nil w 'acl2-pc::in-theory state) (cond (erp (print-no-change2 "bad theory expression.")) (t (pprogn (pc-assign next-pc-enabled-array-suffix (1+ new-suffix)) (maybe-warn-about-theory-simple ens new-pc-ens2 :in-theory w state) (mv (change-pc-state pc-state :pc-ens new-pc-ens2) state)))))))) (if (null pc-ens) (print-no-change2 "The proof-checker enabled/disabled state is ~ already set to agree with the global state, so ~ your IN-THEORY command is redundant.") (mv (change-pc-state pc-state :pc-ens nil) state))))) (define-pc-atomic-macro s-prop (&rest names) "simplify propositionally~/ ~bv[] Example: s-prop~/ General Form: (s-prop &rest names) ~ev[] Simplify, using the default settings for ~c[s] (which include if-normalization and rewriting without real backchaining), but with respect to a theory in which only basic functions and rules (the ones in ~c[(theory 'minimal-theory)]), together with the names (or parenthesized names) in the ~c[&rest] argument ~c[names], are enabled. See also the documentation for ~c[s]." (value `(s :in-theory ,(if names `(union-theories ',names (theory 'minimal-theory)) '(theory 'minimal-theory))))) (define-pc-atomic-macro x (&rest args) "expand and (maybe) simplify function call at the current subterm~/ ~bv[] Examples: x -- expand and simplify. ~ev[] For example, if the current subterm is (append a b), then after ~c[x] the current subterm will probably be (cons (car a) (append (cdr a) b)) if (consp a) and (true-listp a) are among the top-level hypotheses and governors. If there are no top-level hypotheses and governors, then after ~c[x] the current subterm will probably be: ~bv[] (if (true-listp x) (if x (cons (car x) (append (cdr x) y)) y) (apply 'binary-append (list x y))).~/ General Form: (X &key rewrite normalize backchain-limit in-theory hands-off expand) ~ev[] Expand the function call at the current subterm, and simplify using the same conventions as with the ~c[s] command (see documentation for ~c[s]). Unlike ~c[s], it is permitted to set both ~c[:rewrite] and ~c[:normalize] to ~c[nil], which will result in no simplification; see ~c[x-dumb]. ~st[Remark] (obscure): On rare occasions the current address may be affected by the use of ~c[x]. For example, suppose we have the definition ~bv[] (defun g (x) (if (consp x) x 3)) ~ev[] and then we enter the proof-checker with ~bv[] (verify (if (integerp x) (equal (g x) 3) t)) . ~ev[] Then after invoking the instruction ~c[(dive 2 1)], so that the current subterm is ~c[(g x)], followed by the instruction ~c[x], we would expect the conclusion to be ~c[(if (integerp x) (equal 3 3) t)]. However, the system actually replaces ~c[(equal 3 3)] with ~c[t] (because we use the ACL2 term-forming primitives), and hence the conclusion is actually ~c[(if (integerp x) t t)]. Therefore, the current address is put at ~c[(2)] rather than ~c[(2 1)]. In such cases, a warning ``~c[NOTE]'' will be printed to the terminal. The other primitive commands to which the above ``truncation'' note applies are ~c[equiv], ~c[rewrite], and ~c[s]." (value `(do-strict (expand t) (succeed (s ,@args))))) ;; It was tempting to use the rewrite command to implement expand, but ;; this didn't really allow for expanding to keep lambdas or for the ;; issue of how to deal with guards. So I'll keep :definition rules ;; separate from :rewrite rules. (define-pc-primitive expand (&optional ;; nil means eliminate the lambda: do-not-expand-lambda-flg) "expand the current function call without simplification~/ ~bv[] Examples: expand -- expand and do not simplify. ~ev[] For example, if the current subterm is ~c[(append a b)], then after ~c[(expand t)] the current subterm will be the term: ~bv[] (if (true-listp x) (if x (cons (car x) (append (cdr x) y)) y) (apply 'binary-append (list x y))) ~ev[] regardless of the top-level hypotheses and the governors.~/ ~bv[] General Form: (expand &optional do-not-expand-lambda-flg) ~ev[] Expand the function call at the current subterm, and do not simplify. The options have the following meanings: ~bv[] do-not-expand-lambda-flg: default is nil; otherwise, the result should be a lambda expression ~ev[] See also ~c[x], which allows simplification." (let ((w (w state)) (term (fetch-term conc current-addr))) (cond ((or (variablep term) (fquotep term)) (print-no-change2 "It is impossible to expand a variable or a constant.")) ((and do-not-expand-lambda-flg (flambdap (ffn-symb term))) (print-no-change2 "Expansion of lambda terms is disabled when do-not-expand-lambda-flg = ~ t.")) (t (let* ((fn (ffn-symb term)) (def-body (and (not (flambdap fn)) (def-body fn w))) (formals (access def-body def-body :formals)) (body (if (flambdap fn) (lambda-body fn) (and def-body (latest-body (fcons-term fn formals) (access def-body def-body :hyp) (access def-body def-body :concl)))))) (if (null body) (prog2$ (if (flambdap fn) (er hard 'acl2-pc::expand "Found null body for lambda in term ~x0~|Please ~ contact the ACL2 implementors." term) t) (print-no-change2 "Expansion failed. Apparently function ~x0 is ~ constrained, not defined." (list (cons #\0 fn)))) (let ((new-term (cond (do-not-expand-lambda-flg ; hence not (flambdap fn) (fcons-term (make-lambda formals body) (fargs term))) (t (subcor-var (if (flambdap fn) (lambda-formals fn) formals) (fargs term) body))))) (mv-let (new-goal state) (deposit-term-in-goal (car goals) conc current-addr new-term state) (mv (change-pc-state pc-state :goals (cons new-goal (cdr goals)) :local-tag-tree (if (flambdap fn) nil (push-lemma? (access def-body def-body :rune) nil))) state))))))))) (define-pc-atomic-macro x-dumb () "expand function call at the current subterm, without simplifying~/ ~bv[] General Form: x-dumb: expand without simplification. ~ev[]~/ Same as ~c[(expand t new-goals-flg keep-all-guards-flg)]. See documentation for ~c[expand]. See also ~c[x], which allows simplification." (value `(expand t))) ;; **** consider unwinding the effect if there is no change (define-pc-macro bookmark (tag &rest instr-list) "insert matching ``bookends'' comments~/ ~bv[] Example: (bookmark final-goal)~/ General Form: (bookmark name &rest instruction-list) ~ev[] Run the instructions in ~c[instruction-list] (as though this were a call of ~c[do-all]; see the documentation for ~c[do-all]), but first insert a begin bookend with the given name and then, when the instructions have been completed, insert an end bookend with that same name. See the documentation of ~c[comm] for an explanation of bookends and how they can affect the display of instructions." (value `(do-all (comment :begin ,tag) ,@instr-list (comment :end ,tag)))) (defun change-last (lst val) (if (consp lst) (if (consp (cdr lst)) (cons (car lst) (change-last (cdr lst) val)) (list val)) lst)) (defun assign-event-name-and-rule-classes (event-name rule-classes state) (let* ((state-stack (state-stack)) (triple (event-name-and-types-and-raw-term state-stack)) (old-event-name (car triple)) (old-rule-classes (cadr triple)) (old-raw-term (caddr triple))) (pc-assign state-stack (change-last state-stack (change pc-state (car (last state-stack)) :instruction (list :start (list (or event-name old-event-name) (or rule-classes old-rule-classes) old-raw-term))))))) (defun save-fn (name ss-alist state) (pprogn (assign-event-name-and-rule-classes name nil state) (pc-assign ss-alist (put-assoc-eq name (cons (state-stack) (old-ss)) ss-alist)))) (define-pc-macro save (&optional name do-it-flg) "save the proof-checker state (state-stack)~/ ~bv[] Example: (save lemma3-attempt)~/ General Form: (save &optional name do-it-flg) ~ev[] Saves the current proof-checker state by ``associating'' it with the given name. Submit ~c[(retrieve name)] to Lisp to get back to this proof-checker state. If ~c[verify] was originally supplied with an event name, then the argument can be omitted in favor of that name as the default. ~st[Remark] that if a ~c[save] has already been done with the indicated name (or the default event name), then the user will be queried regarding whether to go ahead with the save ~-[] except, if ~c[do-it-flg] is supplied and not ~c[nil], then there will be no query and the ~c[save] will be effected. See also the documentation for ~c[retrieve] and ~c[unsave]." (let ((name (or name (car (event-name-and-types-and-raw-term state-stack)))) (ss-alist (ss-alist))) (if name (mv-let (erp reply state) (if (and (assoc-eq name ss-alist) (null do-it-flg)) (acl2-query 'acl2-pc::save '("The name ~x0 is already associated with a state-stack. Do ~ you really want to overwrite that existing value?" :y t :n nil) (list (cons #\0 name)) state) (mv nil t state)) (declare (ignore erp)) (if reply (pprogn (save-fn name ss-alist state) (value :succeed)) (pprogn (print-no-change "save aborted.") (value :fail)))) (pprogn (print-no-change "You can't SAVE with no argument, because you didn't ~ originally enter VERIFY using an event name. Try ~ (SAVE ) instead.") (value :fail))))) (defmacro retrieve (&optional name) ":Doc-Section Proof-checker re-enter a (specified) ~il[proof-checker] state~/ ~bv[] Examples: (retrieve associativity-of-permutationp) retrieve~/ General Form: (retrieve &optional name) ~ev[] ~l[acl2-pc::retrieve], or use ~c[(help retrieve)] inside the interactive ~il[proof-checker] loop. Also ~pl[unsave]." `(retrieve-fn ',name state)) (define-pc-macro retrieve () "re-enter the proof-checker~/ ~bv[] Examples: (retrieve associativity-of-permutationp) retrieve~/ General Form: (retrieve &optional name) ~ev[] Must be used from ~c[outside] the interactive proof-checker loop. If name is supplied and not ~c[nil], this causes re-entry to the interactive proof-checker loop in the state at which ~c[save] was last executed for the indicated name. (See documentation for ~c[save].) If ~c[name] is ~c[nil] or is not supplied, then the user is queried regarding which proof-checker state to re-enter. The query is omitted, however, if there only one proof-checker state is present that was saved with ~c[save], in which case that is the one that is used. See also ~c[unsave]." (pprogn (print-no-change "RETRIEVE can only be used ouside the ~ interactive loop. Please exit first. To ~ save your state upon exit, use EX rather than EXIT.") (value :fail))) (defun unsave-fn (name state) (pc-assign ss-alist (delete-assoc-eq name (ss-alist)))) (defmacro unsave (name) ":Doc-Section Proof-checker remove a ~il[proof-checker] state~/ ~bv[] Example: (unsave assoc-of-append)~/ General Form: (unsave name) ~ev[] Eliminates the association of a ~il[proof-checker] state with ~c[name]. ~l[unsave] or ~pl[acl2-pc::unsave]. Also ~pl[acl2-pc::save] and ~pl[retrieve]." `(unsave-fn ',name state)) (define-pc-help unsave (&optional name) "remove a proof-checker state~/ ~bv[] Example: (unsave assoc-of-append)~/ General Form: (unsave &optional name) ~ev[] Eliminates the association of a proof-checker state with ~c[name], if ~c[name] is supplied and not ~c[nil]. The name may be ~c[nil] or not supplied, in which case it defaults to the event name supplied with the original call to ~c[verify] (if there is one ~-[] otherwise, the instruction ``fails'' and there is no change). The ACL2 function ~c[unsave] may also be executed outside the interactive loop, with the same syntax. See also documentation for ~c[save] and ~c[retrieve]." (let ((name (or name (car (event-name-and-types-and-raw-term state-stack))))) (if (null name) (print-no-change "You must specify a name to UNSAVE, because you didn't ~ originally enter VERIFY using an event name.") (if (assoc-eq name (ss-alist)) (pprogn (unsave-fn name state) (io? proof-checker nil state (name) (fms0 "~|~x0 removed from saved state-stack alist.~%" (list (cons #\0 name))))) (print-no-change "~|~x0 is does not have a value on the saved ~ state-stack alist.~%" (list (cons #\0 name))))))) (defun show-retrieved-goal (state-stack state) (let ((raw-term (caddr (event-name-and-types-and-raw-term state-stack)))) (assert$ raw-term (fmt-abbrev "~|~%Resuming proof attempt for~|~y0." (list (cons #\0 raw-term)) 0 (proofs-co state) state "~%")))) (defun retrieve-fn (name state) (let ((ss-alist (ss-alist))) (cond ((f-get-global 'in-verify-flg state) (er soft 'verify "You are apparently already inside the VERIFY interactive loop. It is ~ illegal to enter such a loop recursively.")) ((null ss-alist) (pprogn (io? proof-checker nil state nil (fms0 "Sorry -- there is no saved interactive proof to ~ re-enter! Perhaps you meant (VERIFY) rather than ~ (RETRIEVE).~|")) (value :invisible))) ((null name) (if (equal (length ss-alist) 1) (retrieve-fn (caar ss-alist) state) (pprogn (io? proof-checker nil state (ss-alist) (fms0 "Please specify an interactive verification to ~ re-enter. Your options are ~&0.~%(Pick one of the ~ above:) " (list (cons #\0 (strip-cars ss-alist))))) (mv-let (erp val state) (state-global-let* ((infixp nil)) (read-object *standard-oi* state)) (declare (ignore erp)) (retrieve-fn val state))))) (t (let* ((ss-pair (cdr (assoc-eq name ss-alist))) (saved-ss (car ss-pair)) (saved-old-ss (cdr ss-pair))) (if saved-ss (pprogn (pc-assign old-ss saved-old-ss) (pc-assign state-stack saved-ss) (show-retrieved-goal saved-ss state) (verify)) (pprogn (io? proof-checker nil state (name) (fms0 "~|Sorry -- There is no interactive proof saved ~ under the name ~x0.~|" (list (cons #\0 name)))) (value :invisible)))))))) (defun print-all-goals (goals state) (if (null goals) state (pprogn (print-pc-goal (car goals)) (print-all-goals (cdr goals) state)))) (define-pc-help print-all-goals () "print all the (as yet unproved) goals~/ Example and General Form: print-all-goals~/ Prints all the goals that remain to be proved, in a pleasant format. See also the proof-checker command ~c[print-all-concs]." (print-all-goals (goals t) state)) (defmacro print-conc (&optional acl2::goal) `(let ((goal ,(or goal '(car (access pc-state (car (state-stack)) :goals))))) (io? proof-checker nil state (goal) (if goal (fms0 "~%------- ~x3 -------~|~q0~|" (list (cons #\0 (untranslate (access goal goal :conc) t (w state))) (cons #\3 (access goal goal :goal-name)))) (fms0 "~%No goal in CAR of state-stack.~|"))))) (defun print-all-concs (goals state) (declare (xargs :mode :program :stobjs state)) (if (null goals) state (pprogn (print-conc (car goals)) (print-all-concs (cdr goals) state)))) (define-pc-help print-all-concs () "print all the conclusions of (as yet unproved) goals~/ Example and General Form: print-all-concs~/ Prints all the conclusions of goals that remain to be proved, in a pleasant format. See also the proof-checker command ~c[print-all-goals]." (print-all-concs (acl2::goals t) state)) (defun gen-var-marker (x) (or (null x) (and (integerp x) (>= x 0)))) (defun translate-generalize-alist-1 (alist state-vars abbreviations state) ;; Takes an alist with doublets of the form (term var) and ;; returns an alist of the form (translated-term . var). ;; Returns an error triple. However, no attempt is made in this ;; pass to generate new variable names for "variables" that are ;; actually natural numbers or NIL. We'll wait to collect the new ;; variable names first. ;; We'll wait to check for duplicate variables till after this phase. (cond ((null alist) (value nil)) ((and (true-listp (car alist)) (eql (length (car alist)) 2)) (er-let* ((term (translate-abb (caar alist) abbreviations 'translate-generalize-alist state)) (var (if (gen-var-marker (cadar alist)) (value (cadar alist)) ;; I could call translate directly here (translate-abb (cadar alist) nil 'translate-generalize-alist state)))) (cond ((member-eq var state-vars) (er soft :generalize "The variable ~x0 already appears in the current goals of ~ the proof-checker state, and hence is not legal as a ~ generalization variable." var)) ((or (variablep var) (gen-var-marker var)) ;; The second disjunct above is actually subsumed by the first, ;; but I'll leave it in for clarity. (mv-let (erp val state) (translate-generalize-alist-1 (cdr alist) state-vars abbreviations state) (if erp (mv erp val state) (value (cons (cons term var) val))))) (t (er soft :generalize "The second element of each doublet ~ given to the GENERALIZE command must be a variable or ~ natural number, but ~x0 is neither." (cadar alist)))))) (t (er soft :generalize "Each argument to the GENERALIZE command must be a list of ~ length 2, but ~x0 is not." (car alist))))) (defun non-gen-var-markers (alist) ;; gets all the non-gen-var-markers from the cdrs of alist (if (consp alist) (if (gen-var-marker (cdar alist)) (non-gen-var-markers (cdr alist)) (cons (cdar alist) (non-gen-var-markers (cdr alist)))) nil)) (defun find-duplicate-generalize-entries (alist var) (declare (xargs :guard (true-listp alist))) (if alist (if (eq (cadar alist) var) (cons (car alist) (find-duplicate-generalize-entries (cdr alist) var)) (find-duplicate-generalize-entries (cdr alist) var)) nil)) (defun translate-generalize-alist-2 (alist avoid-list) (declare (xargs :guard (true-listp alist))) (if alist (if (gen-var-marker (cdar alist)) (let ((new-var (genvar 'genvar "_" (cdar alist) avoid-list))) (cons (cons (caar alist) new-var) (translate-generalize-alist-2 (cdr alist) (cons new-var avoid-list)))) (cons (car alist) (translate-generalize-alist-2 (cdr alist) avoid-list))) nil)) (defun translate-generalize-alist (alist state-vars abbreviations state) (er-let* ((alist1 (translate-generalize-alist-1 alist state-vars abbreviations state))) (let ((new-vars (non-gen-var-markers alist1))) (if (no-duplicatesp-equal new-vars) (value (translate-generalize-alist-2 alist1 (append new-vars state-vars))) (let* ((bad-var (car (duplicates new-vars))) (dup-entries (find-duplicate-generalize-entries alist bad-var))) (if (cdr dup-entries) (er soft 'acl2-pc::generalize "The pairs ~&0 have the same variable, ~x1, and hence your ~ GENERALIZE instruction is illegal." dup-entries bad-var) (value (er hard 'acl2-pc::generalize "Bad call to translate-generalize-alist on ~% ~x0." (list alist state-vars abbreviations))))))))) (defun all-vars-goals (goals) (if (consp goals) (union-eq (all-vars (access goal (car goals) :conc)) (union-eq (all-vars1-lst (access goal (car goals) :hyps) nil) (all-vars-goals (cdr goals)))) nil)) (defun pc-state-vars (pc-state) (union-eq (all-vars1-lst (strip-cdrs (access pc-state pc-state :abbreviations)) nil) (all-vars-goals (access pc-state pc-state :goals)))) (define-pc-primitive generalize (&rest args) "perform a generalization~/ ~bv[] Example: (generalize ((and (true-listp x) (true-listp y)) 0) ((append x y) w))~/ General Form: (generalize &rest substitution) ~ev[] Generalize using the indicated substitution, which should be a non-empty list. Each element of that list should be a two-element list of the form ~c[(term variable)], where ~c[term] may use abbreviations. The effect of the instruction is to replace each such term in the current goal by the corresponding variable. This replacement is carried out by a parallel substitution, outside-in in each hypothesis and in the conclusion. More generally, actually, the ``variable'' (second) component of each pair may be ~c[nil] or a number, which causes the system to generate a new name of the form ~c[_] or ~c[_n], with ~c[n] a natural number; more on this below. However, when a variable is supplied, it must not occur in any goal of the current proof-checker state. When the ``variable'' above is ~c[nil], the system will treat it as the variable ~c[|_|] if that variable does not occur in any goal of the current proof-checker state. Otherwise it treats it as ~c[|_0|], or ~c[|_1|], or ~c[|_2|], and so on, until one of these is not among the variables of the current proof-checker state. If the ``variable'' is a non-negative integer ~c[n], then the system treats it as ~c[|_n|] unless that variable already occurs among the current goals, in which case it increments n just as above until it obtains a new variable. ~st[Remark:] The same variable may not occur as the variable component of two different arguments (though ~c[nil] may occur arbitrarily many times, as may a positive integer)." (cond (current-addr (print-no-change2 "Generalization may only be applied at the top of the current goal. Try TOP first.")) ((null args) (print-no-change2 "GENERALIZE requires at least one argument.")) (t (mv-let (erp alist state) (translate-generalize-alist args (pc-state-vars pc-state) abbreviations state) (if erp (print-no-change2 "GENERALIZE failed.") (mv (change-pc-state pc-state ;; perhaps we should also adjust abbreviations, but I think that's ;; too complicated (for the user) -- it's simpler to tell him that ;; abbreviations are to be taken literally :goals (cons (change goal (car goals) :hyps (sublis-expr-lst alist (access goal (car goals) :hyps)) :conc (sublis-expr alist (access goal (car goals) :conc))) (cdr goals))) state)))))) (define-pc-atomic-macro use (&rest args) "use a lemma instance~/ ~bv[] Example: (USE true-listp-append (:instance assoc-of-append (x a) (y b) (z c))) -- Add two top-level hypotheses, one the lemma called true-listp-append, and the other an instance of the lemma called assoc-of-append by the substitution in which x is assigned a, y is assigned b, and z is assigned c.~/ General Form: (use &rest args) ~ev[] Add the given lemma instances to the list of top-level hypotheses. ~l[hints] for the syntax of ~c[:use] hints in ~c[defthm], which is essentially the same as the syntax here (see the example above). This command calls the ~c[prove] command, and hence should only be used at the top of the conclusion." (value `(prove :hints (("Goal" :use ,args :do-not-induct proof-checker :do-not *do-not-processes*)) :otf-flg t))) (define-pc-atomic-macro clause-processor (&rest cl-proc-hints) "use a clause-processor~/ ~bv[] Example: (cl-proc :function note-fact-clause-processor :hint '(equal a a)) -- Invoke the indicated clause processor function with the indicated hint argument (see the beginning of community book ~c[books/clause-processors/basic-examples.lisp].~/ General Form: (cl-proc &rest cl-proc-args) ~ev[] Invoke a clause-processor as indicated by cl-proc-args, which is a list of arguments that can serve as the value of a ~c[:]~ilc[clause-processor] hint; ~pl[hints]. This command calls the ~c[prove] command, and hence should only be used at the top of the conclusion." (value `(:prove :hints (("Goal" :clause-processor (,@cl-proc-hints) :do-not-induct proof-checker :do-not *do-not-processes*)) :otf-flg t))) (define-pc-macro cl-proc (&rest cl-proc-hints) "same as ~c[clause-processor]~/ See the documentation for ~ilc[proof-checker] command ~c[clause-processor], which is identical to ~c[cl-proc].~/~/" (value `(:clause-processor ,@cl-proc-hints))) (defun fromto (i j) (declare (xargs :guard (and (rationalp i) (rationalp j)))) (if (< j i) nil (cons i (fromto (1+ i) j)))) (define-pc-atomic-macro retain (arg1 &rest rest-args) "drop all ~st[but] the indicated top-level hypotheses~/ ~bv[] Example: (RETAIN 2 3) -- keep the second and third hypotheses, and drop the rest~/ General Form: (retain &rest args) ~ev[] Drop all top-level hypotheses ~st[except] those with the indicated indices. There must be at least one argument, and all must be in range (i.e. integers between one and the number of top-level hypotheses, inclusive)." (declare (ignore arg1 rest-args)) (when-goals-trip (let* ((hyps (hyps t)) (bad-nums (non-bounded-nums args 1 (length hyps)))) (if bad-nums (pprogn (print-no-change "The following are not in-range hypothesis numbers: ~&0." (list (cons #\0 bad-nums))) (mv t nil state)) (let ((retained-hyps (set-difference-eq (fromto 1 (length hyps)) args))) (if retained-hyps (value (cons :drop retained-hyps)) (pprogn (print-no-change "All hypotheses are to be retained.") (mv t nil state)))))))) (define-pc-atomic-macro reduce (&rest hints) "call the ACL2 theorem prover's simplifier~/ ~bv[] Examples: reduce -- attempt to prove the current goal without using induction (reduce (\"Subgoal 2\" :by foo) (\"Subgoal 1\" :use bar)) -- attempt to prove the current goal without using induction, with the indicated hints~/ General Form: (reduce &rest hints) ~ev[] Attempt to prove the current goal without using induction, using the indicated hints (if any). A subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof. Notice that unlike ~c[prove], the arguments to ~c[reduce] are spread out, and are all hints. ~c[Reduce] is similar to ~c[bash] in that neither of these allows induction. But ~c[bash] only allows simplification, while ~c[reduce] allows processes ~c[eliminate-destructors], ~c[fertilize], ~c[generalize], and ~c[eliminate-irrelevance]. ~st[Remark:] Induction will be used to the extent that it is ordered explicitly in the hints." (if (alistp hints) (value (list :prove :hints (add-string-val-pair-to-string-val-alist "Goal" :do-not-induct 'proof-checker hints) :otf-flg t)) (pprogn (print-no-change "A REDUCE instruction must be of the form~%~ ~ ~ (:REDUCE (goal_name_1 ...) ... (goal_name_n ...)),~%and hence ~ your instruction,~%~ ~ ~x0,~%is not legal." (list (cons #\0 (cons :reduce hints)))) (value :fail)))) (define-pc-macro run-instr-on-goal (instr goal-name) "auxiliary to THEN~/ See documentation for ~c[then].~/ " (when-goals-trip (if (equal goal-name (goal-name t)) (value instr) (value `(protect (change-goal ,goal-name) ,instr))))) (defun run-instr-on-goals-guts (instr goal-names) (declare (xargs :guard (true-listp goal-names))) (if goal-names (cons `(run-instr-on-goal ,instr ,(car goal-names)) (run-instr-on-goals-guts instr (cdr goal-names))) nil)) (define-pc-macro run-instr-on-new-goals (instr existing-goal-names &optional must-succeed-flg) "auxiliary to ~c[then]~/ See documentation for ~c[then].~/ " (value (cons 'do-strict (run-instr-on-goals-guts (if must-succeed-flg instr (list :succeed instr)) (set-difference-equal (goal-names (goals t)) existing-goal-names))))) (define-pc-macro then (instr &optional completion must-succeed-flg) "apply one instruction to current goal and another to new subgoals~/ ~bv[] Example: (then induct prove)~/ General Form: (then first-instruction &optional completion must-succeed-flg) ~ev[] Run ~c[first-instruction], and then run ~c[completion] (another instruction) on each subgoal created by ~c[first-instruction]. If ~c[must-succeed-flg] is supplied and not ~c[nil], then halt at the first ``failure'' and remove the effects of the invocation of ~c[completion] that ``failed''. The default for completion is ~c[reduce]." (value (list 'do-strict instr (list 'run-instr-on-new-goals (or completion :reduce) (goal-names (goals t)) must-succeed-flg)))) (defun print-help-separator (state) (io? proof-checker nil state nil (fms0 "~%==============================~%"))) (defun print-pc-help-rec (lst state) (declare (xargs :guard (true-listp lst))) (if (null lst) (value t) (mv-let (erp val state) (doc!-fn (car lst) state) (declare (ignore erp val)) (pprogn (print-help-separator state) (print-pc-help-rec (cdr lst) state))))) (defun print-all-pc-help-fn (filename state) (mv-let (chan state) (open-output-channel filename :character state) (state-global-let* ((proofs-co chan) (standard-co chan)) (pprogn (io? proof-checker nil state nil (fms0 "~|***** Complete documentation of proof-checker ~ commands *****~%")) (print-help-separator state) (print-pc-help-rec (merge-sort-alpha-< (caddr (access-doc-string-database 'pc-acl2 state))) state))))) (defmacro print-all-pc-help (&optional filename) `(print-all-pc-help-fn ,(or filename "pc-help.out") state)) (define-pc-macro nil () "used for interpreting ~c[control-d]~/ ~bv[] Example and General form: nil ~ev[] (or, ~c[control-d]).~/ The whole point of this command is that in some Lisps (including akcl), if you type ~c[control-d] then it seems, on occasion, to get interpreted as ~c[nil]. Without this command, one seems to get into an infinite loop." (value 'exit)) ;; OK, here's a plan for free variables. When the user thinks that ;; maybe he wants to introduce a free variable, he declares the ;; variable to be free at the time he wants to introduce it. What ;; this really does is to introduce an abbreviation &v for (hide x), ;; where x is that variable. Then if later in the proof he wants to ;; instantiate x with trm, then what happens is that the ;; add-abbreviation command is changed so that &v instead abbreviates ;; (hide trm). The instructions are then replayed (or not, if the ;; user wants to cheat at this point -- or perhaps there's a fast ;; heuristic test on suitability of the PUT). (define-pc-atomic-macro free (var) "create a ``free variable''~/ ~bv[] Example: (free x)~/ General Form: (free var) ~ev[] Mark ~c[var] as a ``free variable''. Free variables are only of interest for the ~c[put] command; see its documentation for an explanation." (er-let* ((var (trans0 var nil :free))) (if (variablep var) (value `(add-abbreviation ,var (hide ,var))) (pprogn (print-no-change "The FREE command requires an argument that is a variable, ~ which ~x0 is not." (list (cons #\0 var))) (value :fail))))) (define-pc-macro replay (&optional n replacement-instr) "replay one or more instructions~/ ~bv[] Examples: REPLAY -- replay all instructions in the current session (i.e., state-stack) (REPLAY 5) -- replay the most recent 5 instructions (REPLAY 5 (COMMENT deleted dive command here)) -- replace the 5th most recent instruction with the indicated comment instruction, and then replay it followed by the remaining 4 instructions~/ General Form: (REPLAY &OPTIONAL n replacement-instruction) ~ev[] Replay the last ~c[n] instructions if ~c[n] is a positive integer; else ~c[n] should be ~c[nil] or not supplied, and replay all instructions. However, if ~c[replacement-instruction] is supplied and not ~c[nil], then before the replay, replace the ~c[nth] instruction (from the most recent, as shown by ~c[commands]) with ~c[replacement-instruction]. If this command ``fails'', then the ~c[restore] command will revert the state-stack to its value present before the ~c[replay] instruction was executed." ;; So that I can use instructions-of-state-stack, I'll make ;; n 1-bigger than it ought to be. (if (or (null n) (and (integerp n) (> n 0))) (let* ((len (length state-stack)) (n (and n (min (1+ n) len))) (instrs (instructions-of-state-stack (if n (take n state-stack) state-stack) nil))) (value `(do-strict (undo ,(1- (or n len))) ,@(if replacement-instr (cons replacement-instr (cdr instrs)) instrs)))) (pprogn (print-no-change "The optional argument to the REPLAY command ~ must be a positive integer, but ~x0 is not!" (list (cons #\0 n))) (value :fail)))) (defun instr-name (instr) ;; assumes that instr is an official (stored) instruction (if (atom instr) instr (car instr))) (defun pc-free-instr-p (var pc-state) (let ((instr (access pc-state pc-state :instruction))) (and (eq (instr-name instr) :free) (eq (cadr instr) var)))) (defun find-possible-put (var state-stack) ;; ***** Should beef this up sometime with heuristics for catching ;; when GENERALIZE or PROVE, for example, makes var "non-free" after all. ;; Attempts to find index (for undoing) of FREE command that introduced var, and if ;; it can't, then returns nil. (if state-stack (if (pc-free-instr-p var (car state-stack)) 1 (let ((n (find-possible-put var (cdr state-stack)))) (and n (1+ n)))) nil)) (define-pc-macro put (var expr) "substitute for a ``free variable''~/ ~bv[] Example: (put x 17)~/ General Form: (put var expr) ~ev[] Substitute ~c[expr] for the ``free variable'' ~c[var], as explained below. A ``free variable'' is, for our purposes, a variable ~c[var] such that the instruction ~c[(free var)] has been executed earlier in the state-stack. What ~c[(free var)] really does is to let ~c[var] be an abbreviation for the term ~c[(hide var)] (see documentation for ~c[add-abbreviation]). What ~c[(put var expr)] really does is to unwind the state-stack, replacing that ~c[free] instruction with the instruction ~c[(add-abbreviation var expr)], so that future references to ~c[(? var)] become reference to ~c[expr] rather than to ~c[(hide var)], and then to replay all the other instructions that were unwound. Because ~c[hide] was used, the expectation is that in most cases, the instructions will replay successfully and ~c[put] will ``succeed''. However, if any replayed instruction ``fails'', then the entire replay will abort and ``fail'', and the state-stack will revert to its value before the ~c[put] instruction was executed. If ~c[(put var expr)] ``succeeds'', then ~c[(remove-abbreviation var)] will be executed at the end. ~st[Remark]: The ~c[restore] command will revert the state-stack to its value present before the ~c[put] instruction was executed." (let ((n (find-possible-put var state-stack))) (if n (value `(do-strict (replay ,n (add-abbreviation ,var ,expr)) (remove-abbreviations ,var))) (pprogn (print-no-change "There is no FREE command for ~x0." (list (cons #\0 var))) (value :fail))))) (define-pc-macro reduce-by-induction (&rest hints) "call the ACL2 prover without induction, after going into induction~/ ~bv[] Examples: reduce-by-induction -- attempt to prove the current goal after going into induction, with no further inductions (reduce-by-induction (\"Subgoal 2\" :by foo) (\"Subgoal 1\" :use bar)) -- attempt to prove the current goal after going into induction, with no further inductions, using the indicated hints~/ General Form: (reduce-by-induction &rest hints) ~ev[] A subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof, except that the proof begins with a top-level induction. Notice that unlike ~c[prove], the arguments to ~c[reduce-by-induction] are spread out, and are all hints. See also ~c[prove], ~c[reduce], and ~c[bash]. ~st[Remark]: Induction and the various processes will be used to the extent that they are ordered explicitly in the ~c[:induct] and ~c[:do-not] hints." (if (alistp hints) (value (cons :reduce (add-string-val-pair-to-string-val-alist "Goal" :induct t hints))) (pprogn (print-no-change "A REDUCE-BY-INDUCTION instruction must be of the form~%~ ~ ~ (:REDUCE-BY-INDUCTION (goal_name_1 ...) ... (goal_name_n ...)),~%and hence ~ your instruction,~%~ ~ ~x0,~%is not legal." (list (cons #\0 (cons :reduce-by-induction hints)))) (value :fail)))) (define-pc-macro r (&rest args) "same as rewrite~/ ~bv[] Example: (r 3)~/ ~ev[] See the documentation for ~c[rewrite], as ~c[r] and ~c[rewrite] are identical." (value (cons :rewrite args))) (define-pc-atomic-macro sl (&optional backchain-limit) "simplify with lemmas~/ ~bv[] Examples: sl (sl 3)~/ General Form: (sl &optional backchain-limit) ~ev[] Simplify, but with all function definitions disabled (~pl[function-theory] in the top-level ACL2 loop), except for a few basic functions (the ones in ~c[(theory 'minimal-theory)]). The ~c[backchain-limit] has a default of 0, but if is supplied and not ~c[nil], then it should be a nonnegative integer; see the documentation for ~c[s]. WARNING: This command completely ignores ~c[in-theory] commands that are executed inside the proof-checker." (value (if backchain-limit `(s :backchain-limit ,backchain-limit :in-theory (union-theories (theory 'minimal-theory) (set-difference-theories (current-theory :here) (function-theory :here)))) `(s :in-theory (union-theories (theory 'minimal-theory) (set-difference-theories (current-theory :here) (function-theory :here))))))) (define-pc-atomic-macro elim () "call the ACL2 theorem prover's elimination process~/ ~bv[] Example and General Form: elim ~ev[]~/ Upon running the ~c[elim] command, the system will create a subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof, where ~st[only] elimination is used; not even simplification is used!" (value (list :prove :otf-flg t :hints '(("Goal" :do-not-induct proof-checker :do-not (set-difference-eq *do-not-processes* '(eliminate-destructors))))))) (define-pc-macro ex () "exit after possibly saving the state~/ ~bv[] Example and General Form: ex ~ev[]~/ Same as ~c[exit], except that first the instruction ~c[save] is executed. If ~c[save] queries the user and is answered negatively, then the exit is aborted." (value '(do-strict save exit))) (defun save-fc-report-settings () (declare (xargs :guard t)) (wormhole-eval 'fc-wormhole '(lambda (whs) (let* ((data (wormhole-data whs)) (criteria (cdr (assoc-eq :CRITERIA data))) (flyp (cdr (assoc-eq :REPORT-ON-THE-FLYP data)))) (set-wormhole-data whs (put-assoc-eq :CRITERIA-SAVED criteria (put-assoc-eq :REPORT-ON-THE-FLYP-SAVED flyp data))))) nil)) (defun restore-fc-report-settings () (declare (xargs :guard t)) (wormhole-eval 'fc-wormhole '(lambda (whs) (let* ((data (wormhole-data whs)) (criteria-saved (cdr (assoc-eq :CRITERIA-SAVED data))) (flyp-saved (cdr (assoc-eq :REPORT-ON-THE-FLYP-SAVED data)))) (set-wormhole-data whs (put-assoc-eq :CRITERIA criteria-saved (put-assoc-eq :REPORT-ON-THE-FLYP flyp-saved data))))) nil)) (define-pc-help type-alist (&optional concl-flg govs-flg fc-report-flg) "display the type-alist from the current context~/ ~bv[] Examples: (type-alist t t) ; display type-alist based on conclusion and governors (type-alist t t t) ; as above, but also display forward-chaining report type-alist ; same as (type-alist nil t) -- governors only (type-alist nil) ; same as (type-alist nil t) -- governors only (type-alist t) ; same as (type-alist t nil) -- conclusion only (type-alist nil nil) ; display type-alist without considering ; conclusion or governors~/ General Form: (type-alist &optional concl-flg govs-flg fc-report-flg) ~ev[] where if ~c[govs-flg] is omitted then it defaults to ~c[(not concl-flg)], and ~c[concl-flg] and ~c[fc-report-flg] default to ~c[nil]. Display the current assumptions as a type-alist. Note that this display includes the result of forward chaining. When ~c[fc-report-flg] is supplied a non-~c[nil] value, the display also includes a forward-chaining report; otherwise,the presence or absence of such a report is controlled by the usual global settings (~pl[forward-chaining-reports]). There are two basic reasons contemplated for using this command. 1. The theorem prover has failed (either outside the proof-checker or using a proof-checker command such as ~c[bash] or ~c[reduce] and you want to debug by getting an idea of what the prover knows about the context.~bq[] a. You really are interested in the context for the current term. Include hypotheses and governors (i.e., accounting for tests of surrounding ~c[if]-expressions that must be true or false) but not the current conclusion (which the theorem prover's heuristics would generally ignore for contextual information). Command:~nl[] ~c[(type-alist nil t)] ; equivalently, ~c[type-alist] or ~c[(type-alist nil)] b. You are not thinking in particular about the current term; you just want to get an idea of the context that the prover would build at the top-level, for forward-chaining. Incorporate the conclusion but not the governors. Command:~nl[] ~c[(type-alist t nil)] ; equivalently, ~c[(type-alist t)]~eq[] 2. You intend to use one of the ~il[proof-checker-commands] that does simplification, such as ~c[s] or ~c[x], and you want to see the context. Then include the surrounding ~c[if]-term governors but not the goal's conclusion. Command:~nl[] ~c[(type-alist nil t)] ; equivalently, ~c[type-alist] or ~c[(type-alist nil)] ~l[type-set] (also ~pl[type-prescription]) for information about ACL2's type system, which can assist in understanding the output of the ~c[type-alist] command." (when-goals (let ((conc (conc t)) (current-addr (current-addr t)) (w (w state)) (govs-flg (if (cdr args) govs-flg (not concl-flg)))) (prog2$ (and fc-report-flg (prog2$ (save-fc-report-settings) (prog2$ (wormhole-eval ; (set-fc-criteria t) without state 'fc-wormhole '(lambda (whs) (set-wormhole-data whs (put-assoc-eq :CRITERIA '((t t t)) (wormhole-data whs)))) nil) (set-fc-report-on-the-fly t)))) (mv-let (flg hyps-type-alist ttree) (hyps-type-alist (cond (concl-flg (union-equal (hyps t) (cond (govs-flg (add-to-set-equal (dumb-negate-lit conc) (governors conc current-addr))) (t (list (dumb-negate-lit conc)))))) (govs-flg (union-equal (hyps t) (governors conc current-addr))) (t (hyps t))) (make-pc-ens (pc-ens t) state) w state) (declare (ignore ttree)) (prog2$ (and fc-report-flg (restore-fc-report-settings)) (if flg (io? proof-checker nil state nil (fms0 "*** Contradiction in the hypotheses! ***~%The S ~ command should complete this goal.~|")) (io? proof-checker nil state (hyps-type-alist w) (pprogn (fms0 "~|Current type-alist, including forward chaining:~%") (prog2$ (print-type-alist hyps-type-alist w) state)))))))))) (define-pc-help print-main () "print the original goal~/ ~bv[] Example and General Form: print-main ~ev[]~/ Print the goal as originally entered." (print-pc-goal (car (access pc-state (car (last state-stack)) :goals)))) (define-pc-macro pso () "print the most recent proof attempt from inside the proof-checker~/ ~bv[] Example and General Form: pso ~ev[]~/ Print the most recent proof attempt from inside the proof-checker assuming you are in ~ilc[gag-mode] or have saved output (~pl[set-saved-output]). This includes all calls to the prover, including for example ~il[proof-checker] commands ~c[induct], ~c[split], and ~c[bash], in addition to ~c[prove]. So for example, you can follow ~c[(quiet prove)] with ~c[pso] to see the proof, including ~il[proof-tree] output, if it failed. See also documentation for related ~il[proof-checker] commands ~c[psog] and ~c[pso!]." (value '(lisp (pso)))) (define-pc-macro psog () "print the most recent proof attempt from inside the proof-checker~/ ~bv[] Example and General Form: psog ~ev[]~/ Print the most recent proof attempt from inside the proof-checker, including goal names, assuming you are in ~ilc[gag-mode] or have saved output (~pl[set-saved-output]). This includes all calls to the prover, including for example ~il[proof-checker] commands ~c[induct], ~c[split], and ~c[bash], in addition to ~c[prove]. So for example, you can follow ~c[(quiet prove)] with ~c[psog] to see the proof, including ~il[proof-tree] output, if it failed. See also documentation for related ~il[proof-checker] commands ~c[pso] and ~c[pso!]." (value '(lisp (psog)))) (define-pc-macro pso! () "print the most recent proof attempt from inside the proof-checker~/ ~bv[] Example and General Form: pso! ~ev[]~/ Print the most recent proof attempt from inside the proof-checker, including ~il[proof-tree] output, assuming you are in ~ilc[gag-mode] or have saved output (~pl[set-saved-output]). This includes all calls to the prover, including for example ~il[proof-checker] commands ~c[induct], ~c[split], and ~c[bash], in addition to ~c[prove]. So for example, you can follow ~c[(quiet prove)] with ~c[pso!] to see the proof, including ~il[proof-tree] output, if it failed. See also documentation for related ~il[proof-checker] commands ~c[pso] and ~c[psog]." (value '(lisp (pso!)))) (define-pc-macro acl2-wrap (x) "same as ~c[(lisp x)]~/ ~bv[] Example: (acl2-wrap (pe :here))~/ General Form: (acl2-wrap form) ~ev[] Same as ~c[(lisp form)]. This is provided for interface tools that want to be able to execute the same form in raw Lisp, in the proof-checker, or in the ACL2 top-level loop ~c[(lp)]." (value `(lisp ,x))) (defmacro acl2-wrap (x) ; This is provided for compatibility with an interface of the same name, ; provided for evaluating forms in raw Lisp. x) (define-pc-macro check-proved-goal (goal-name cmd) (if (member-equal goal-name (goal-names (goals))) (er soft 'check-proved "The command ~x0 failed to prove the goal ~x1." cmd goal-name) (value 'succeed))) (define-pc-macro check-proved (x) (when-goals-trip (let ((goal-name (goal-name))) (value `(do-all ,x (quiet (check-proved-goal ,goal-name ,x))))))) (define-pc-atomic-macro forwardchain (hypn &optional hints quiet-flg) "forward chain from an implication in the hyps~/ ~bv[] Example: (forwardchain 2) ; Second hypothesis should be of the form ; (IMPLIES hyp concl), and the result is to replace ; that hypothesis with concl. ~/ General Forms: (forwardchain hypothesis-number) (forwardchain hypothesis-number hints) (forwardchain hypothesis-number hints quiet-flg) ~ev[] This command replaces the hypothesis corresponding to given index, which should be of the form ~c[(IMPLIES hyp concl)], with its consequent ~c[concl]. In fact, the given hypothesis is dropped, and the replacement hypothesis will appear as the final hypothesis after this command is executed. The prover must be able to prove the indicated hypothesis from the other hypotheses, or else the command will fail. The ~c[:hints] argument is used in this prover call, and should have the usual syntax of hints to the prover. Output is suppressed if ~c[quiet-flg] is supplied and not ~c[nil]." (when-goals-trip (let* ((hyps (hyps)) (len (length hyps))) (cond ((null hyps) (mv-let (erp val state) (er soft 'forwardchain "The are no top-level hypotheses. Hence it makes no sense to ~ forward chain here.") (declare (ignore erp val)) (value 'fail))) ((and (integerp hypn) (< 0 hypn) (<= hypn len)) (let ((hyp (nth (1- hypn) hyps))) (case-match hyp (('implies ant consequent) (let ((instr `(protect (claim ,consequent 0 :do-not-flatten t) (drop ,hypn) ;; Now prove the consequent, leaving the original goal ;; unproved (as the new hypothesis is not necessarily ;; expected to match the conclusion). change-goal (demote ,hypn) (claim ,ant ,@(if hints '(:hints hints) nil)) (demote ,len) (check-proved (s :backchain-limit 0 :in-theory (theory 'minimal-theory)))))) (if quiet-flg (value (list 'quiet instr)) (value instr)))) (& (mv-let (erp val state) (er soft 'forwardchain "The ~n0 hypothesis~| ~x1~|is not of the form (implies x ~ y)." (list hypn) (untrans0 (nth (1- hypn) hyps) t (abbreviations))) (declare (ignore erp val)) (value 'fail)))))) (t (mv-let (erp val state) (er soft 'forwardchain "The index ~x0 is not a valid index into the hypothesis list. ~ The valid indices are the integers from 1 to ~x1." hypn len) (declare (ignore erp val)) (value 'fail))))))) (define-pc-atomic-macro bdd (&rest kw-listp) "prove the current goal using bdds~/ ~bv[] Examples: bdd (bdd :vars nil :bdd-constructors (cons) :prove t :literal :all) ~ev[] ~/ The general form is as shown in the latter example above, but with any keyword-value pairs omitted and with values as described for the ~c[:]~ilc[bdd] hint; ~pl[hints]. This command simply calls the theorem prover with the indicated bdd hint for the top-level goal. Note that if ~c[:prove] is ~c[t] (the default), then the proof will succeed entirely using bdds or else it will fail immediately. ~l[bdd]." (let ((bdd-hint (if (assoc-keyword :vars kw-listp) kw-listp (list* :vars nil kw-listp)))) (value `(:prove :hints (("Goal" :bdd ,bdd-hint)))))) (define-pc-macro runes (&optional flg) "print the runes (definitions, lemmas, ...) used~/ ~bv[] Examples and general forms: (runes t) ; print all ~il[rune]s used during this interactive proof (runes nil) ; print all ~il[rune]s used by the most recent command (runes) ; same as (runes nil) runes ; same as (runes nil) ~ev[]~/ This command does not change the ~il[proof-checker] state. Rather, it simply reports runes (~pl[rune]) that have participated in the interactive proof. Note that ~c[(runes nil)] will show the ~il[rune]s used by the most recent primitive or macro command (as displayed by ~c[:comm])." (value `(print (merge-sort-runes (all-runes-in-ttree (,(if flg 'tag-tree 'local-tag-tree)) nil))))) (define-pc-macro lemmas-used (&optional flg) "print the runes (definitions, lemmas, ...) used~/ This is just an alias for ~c[runes].~/~/" (value `(runes ,flg))) (defun goal-terms (goals) ; Initially terms is empty, and we return the list of terms represented by ; goals. (if (endp goals) nil (cons (make-implication (access goal (car goals) :hyps) (access goal (car goals) :conc)) (goal-terms (cdr goals))))) (defun wrap1-aux1 (kept-goal-names all-goals kept-goals removed-goals) ; Initially, accumulators removed-goals and kept-goals are empty. We partition ; all-goals into those goals whose names are in kept-goal-names and the rest, ; returning (mv kept-goals1 removed-goals1) where removed-goals1 and ; kept-goals1 extend removed-goals and kept-goals, respectively. The goals in ; all-goals are returned in the same order as they appear in all-goals. (cond ((endp all-goals) (mv (reverse kept-goals) (reverse removed-goals))) ((member-equal (access goal (car all-goals) :goal-name) kept-goal-names) (wrap1-aux1 kept-goal-names (cdr all-goals) (cons (car all-goals) kept-goals) removed-goals)) (t (wrap1-aux1 kept-goal-names (cdr all-goals) kept-goals (cons (car all-goals) removed-goals))))) (defun wrap1-aux2 (sym index goals kept-goals removed-goals) (if (endp goals) (mv (reverse kept-goals) (reverse removed-goals)) (let* ((goal (car goals)) (goal-name (access goal goal :goal-name))) (if (and (consp goal-name) (eq sym (car goal-name)) (<= index (cdr goal-name))) (wrap1-aux2 sym index (cdr goals) kept-goals (cons (car goals) removed-goals)) (wrap1-aux2 sym index (cdr goals) (cons (car goals) kept-goals) removed-goals))))) (define-pc-primitive wrap1 (&optional kept-goal-names) "combine goals into a single goal~/ ~bv[] Examples: ; Keep (main . 1) and (main . 2) if they exist, as well as the current goal; ; and for each other goal, conjoin it into the current goal and delete it: (wrap1 ((main . 1) (main . 2))) ; As explained below, conjoin all subsequent siblings of the current goal ; into the current goal, and then delete them: (wrap1)~/ General Form: (wrap1 &optional kept-goal-names) ~ev[] If ~c[kept-goal-names] is not ~c[nil], the current goal is replaced by conjoining it with all goals other than the current goal and those indicated by ~c[kept-goal-names], and those other goals are deleted. If ~c[kept-goal-names] is omitted, then the the current goal must be of the form ~c[(name . n)], and the goals to conjoin into the current goal (and delete) are those with names of the form ~c[(name . k)] for ~c[k] >= ~c[n]. NOTE: ~c[Wrap1] always ``succeeds'', even if there are no other goals to conjoin into the current goal (a message is printed in that case), and it always leaves you with no hypotheses at the top of the current goal's conclusion (as though ~c[top] and ~c[demote] had been executed, if necessary). Also see proof-checker documentation for ~c[wrap] (~pl[proof-checker-commands])." (let* ((current-goal (car goals)) (current-goal-name (access goal current-goal :goal-name))) (cond ((not (true-listp kept-goal-names)) (print-no-change2 "The (optional) argument to wrap1 must be a true list of goal names. ~ ~x0 is thus illegal." (list (cons #\0 kept-goal-names)))) ((and (null kept-goal-names) (not (and (consp current-goal-name) (symbolp (car current-goal-name)) (integerp (cdr current-goal-name))))) (print-no-change2 "The current goal's name, ~x0, is not of the form (SYMBOL . N) for ~ integer N." (list (cons #\0 current-goal-name)))) (t (mv-let (kept-goals removed-goals) (if kept-goal-names (wrap1-aux1 kept-goal-names (cdr goals) nil nil) (wrap1-aux2 (car current-goal-name) (cdr current-goal-name) (cdr goals) nil nil)) (pprogn (io? proof-checker nil state (current-goal-name removed-goals) (if removed-goals (fms0 "~|Conjoining the following goals into the current ~ goal, ~x0:~| ~X1n~%" (list (cons #\0 current-goal-name) (cons #\1 (goal-names removed-goals)) (cons #\n nil))) (fms0 "~|NOTE (wrap1): There are no goals to conjoin into the ~ current goal, but we proceed anyhow.~%"))) (mv (change-pc-state pc-state :goals (cons (change goal current-goal :conc (conjoin (goal-terms (cons current-goal removed-goals))) :hyps nil :current-addr nil) kept-goals)) state))))))) (define-pc-atomic-macro wrap (&rest instrs) "execute the indicated instructions and combine all the new goals~/ ~bv[] Example: (wrap induct) ; induct, then replace first new goal by the conjunction of all ; the new goals, and drop all new goals after the first~/ General Form: (wrap &rest instrs) ~ev[] First the instructions in ~c[instrs] are executed, as in ~c[do-all]. If this ``fails'' then no additional action is taken. Otherwise, the current goal after execution of ~c[instrs] is conjoined with all ``new'' goals, in the sense that their names are not among the names of goals at the time ~c[instrs] was begun. This conjunction becomes the new current goal and those ``new'' goals are dropped. See the code for the proof-checker command wrap-induct for an example of the use of ~c[wrap]." (cond ((null instrs) (pprogn (print-no-change "Wrap takes at least one argument.") (value :fail))) (t (let ((goal-names (goal-names (goals t)))) (value `(sequence ((do-all ,@instrs) (quiet (wrap1 ,goal-names)) (lisp (io? proof-checker nil state (state-stack) (let ((new-current-goal-name (access goal (car (goals)) :goal-name))) (when-goals (fms0 (if (member-equal new-current-goal-name ',goal-names) "~|~%NOTE: Created no new goals. Current ~ goal:~% ~X0n~|" "~|~%NOTE: Created ONLY one new goal, which is ~ the current goal:~% ~X0n~|") (list (cons #\0 new-current-goal-name) (cons #\n nil)))))))) t nil nil t)))))) (define-pc-atomic-macro wrap-induct (&optional raw-term) "same as induct, but create a single goal~/ ~bv[] Examples: wrap-induct (wrap-induct t) (wrap-induct (append (reverse x) y))~/ General Form: (wrap-induct &optional term) ~ev[] The ~c[wrap-induct] command is identical to the ~ilc[proof-checker] ~c[induct] command, except that only a single goal is created: the conjunction of the base and induction steps. Note: The output will generally indicate that more than goal has been created, e.g.: ~bv[] Creating two new goals: (MAIN . 1) and (MAIN . 2). ~ev[] However, ~c[wrap-induct] always creates a unique goal (when it succeeds). A subsequent message clarifies this, for example: ~bv[] NOTE: Created ONLY one new goal, which is the current goal: (MAIN . 1) ~ev[]" (value (if raw-term `(wrap (induct ,raw-term)) `(wrap induct)))) (define-pc-macro finish-error (instrs) (er soft 'finish "~%The following instruction list created at least one subgoal:~|~x0~|" instrs)) (define-pc-macro finish (&rest instrs) "require completion of instructions; save error if inside ~c[:]~ilc[hints]~/ ~bv[] Example: (finish induct prove bash)~/ General Form: (finish &rest instructions) ~ev[] Run the indicated instructions, stopping at the first failure. If there is any failure, or if any new goals are created and remain at the end of the indicated instructions, then consider the call of ~c[finish] to be a failure. ~l[proof-checker-commands] and visit the documentation for ~c[sequence] for a discussion of the notion of ``failure'' for proof-checker commands." (value `(then (check-proved (do-strict ,@instrs)) (finish-error ,instrs) t))) (defun show-geneqv (x with-runes-p) (cond ((endp x) nil) (t (cons (if with-runes-p (list (access congruence-rule (car x) :equiv) (access congruence-rule (car x) :rune)) (access congruence-rule (car x) :equiv)) (show-geneqv (cdr x) with-runes-p))))) (define-pc-macro geneqv (&optional with-runes-p) "show the generated equivalence relation maintained at the current subterm~/ ~bv[] General Forms: geneqv ; show list of equivalence relations being maintained (geneqv t) ; as above, but pair each relation with a justifying rune~/ ~ev[] This is an advanced command, whose effect is to print the so-called ``generated equivalence relation'' (or ``geneqv'') that is maintained at the current subterm of the conclusion. That structure is a list of equivalence relations, representing the transitive closure ~c[E] of the union of those relations, such that it suffices to maintain ~c[E] at the current subterm: if that subterm, ~c[u], is replaced in the goal's conclusion, ~c[G], by another term equivalent to ~c[u] with respect to ~c[E], then the resulting conclusion is Boolean equivalent to ~c[G]. Also ~pl[defcong]. The command `~c[geneqv]' prints the above list of equivalence relations, or more precisely, the list of function symbols for those relations. If however ~c[geneqv] is given a non-~c[nil] argument, then a list is printed whose elements are each of the form ~c[(s r)], where ~c[s] is the symbol for an equivalence relation and ~c[r] is a ~c[:]~ilc[congruence] ~il[rune] justifying the inclusion of ~c[s] in the list of equivalence relations being maintained at the current subterm." (value `(print (show-geneqv (geneqv-at-subterm-top (conc) (current-addr) (pc-ens) (w state)) ',with-runes-p)))) ; Support for :instructions as hints (defun goals-to-clause-list (goals) (if (endp goals) nil (cons (append (dumb-negate-lit-lst (access goal (car goals) :hyps)) (list (access goal (car goals) :conc))) (goals-to-clause-list (cdr goals))))) (defun proof-checker-clause-list (state) (goals-to-clause-list (goals))) (defun proof-checker-cl-proc (cl instr-list state) (let ((ctx 'proof-checker-cl-proc)) (cond ((null cl) (er soft ctx "There is no legal way to prove a goal of NIL!")) (t (let ((term (make-implication (dumb-negate-lit-lst (butlast cl 1)) (car (last cl)))) (wrld (w state)) (new-pc-depth (1+ (pc-value pc-depth)))) (er-let* ((new-inhibit-output-lst (cond ((and (consp instr-list) (true-listp (car instr-list)) (eq (make-pretty-pc-command (caar instr-list)) :COMMENT) (eq (cadar instr-list) 'inhibit-output-lst)) (cond ((eq (caddar instr-list) :same) (value (f-get-global 'inhibit-output-lst state))) (t (chk-inhibit-output-lst (caddar instr-list) :instructions state)))) (t (value (union-eq '(prove proof-tree proof-checker) (f-get-global 'inhibit-output-lst state)))))) (outputp (value (not (subsetp-eq '(prove proof-checker proof-tree) new-inhibit-output-lst))))) (state-global-let* ((inhibit-output-lst new-inhibit-output-lst) (pc-output (f-get-global 'pc-output state))) (mv-let (erp clause-list state) (pprogn (pc-assign pc-depth new-pc-depth) (cond (outputp (io? prove nil state (new-pc-depth) (fms0 "~|~%[[~x0> Executing ~ proof-checker instructions]]~%~%" (list (cons #\0 new-pc-depth))))) (t state)) (pc-assign next-pc-enabled-array-suffix (1+ (pc-value next-pc-enabled-array-suffix))) (mv-let (erp pc-val state) (pc-main term (untranslate term t wrld) nil ; event-name nil ; rule-classes instr-list '(signal value) ; quit-conditions t ; pc-print-prompt-and-instr-flg, suitable for :pso state) (pprogn (cond (outputp (io? prove nil state (new-pc-depth) (fms0 "~|~%[[<~x0 Completed ~ proof-checker ~ instructions]]~%" (list (cons #\0 new-pc-depth))))) (t state)) (cond ((or erp (null pc-val)) (let ((name (intern (concatenate 'string "ERROR" (coerce (explode-atom new-pc-depth 10) 'string)) "KEYWORD"))) (pprogn (io? error nil state (name) (fms0 "~%Saving proof-checker error ~ state; see :DOC instructions. To ~ retrieve:~|~x0" (list (cons #\0 `(retrieve ,name))))) (save-fn name (ss-alist) state) (er soft ctx "The above :INSTRUCTIONS hint failed. ~ For a discussion of ``failed'', follow ~ the link to the SEQUENCE command under ~ :DOC proof-checker-commands.")))) (t (value (proof-checker-clause-list state))))))) (cond (erp (silent-error state)) (t (value clause-list))))))))))) #+acl2-loop-only (define-trusted-clause-processor proof-checker-cl-proc nil) #+acl2-loop-only (add-custom-keyword-hint :instructions (splice-keyword-alist :instructions (list :clause-processor (list :function 'proof-checker-cl-proc :hint (kwote val))) keyword-alist)) acl2-sources/proof-checker-pkg.lisp0000666002132200015000000000161012222115527016751 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") (defpkg "ACL2-PC" nil) acl2-sources/prove.lisp0000666002132200015000000156713312222115527014620 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; Section: PREPROCESS-CLAUSE ; The preprocessor is the first clause processor in the waterfall when ; we enter from prove. It contains a simple term rewriter that expands ; certain "abbreviations" and a gentle clausifier. ; We first develop the simple rewriter, called expand-abbreviations. ; Rockwell Addition: We are now concerned with lambdas, where we ; didn't used to treat them differently. This extra argument will ; show up in several places during a compare-windows. (mutual-recursion (defun abbreviationp1 (lambda-flg vars term2) ; This function returns t if term2 is not an abbreviation of term1 ; (where vars is the bag of vars in term1). Otherwise, it returns the ; excess vars of vars. If lambda-flg is t we look out for lambdas and ; do not consider something an abbreviation if we see a lambda in it. ; If lambda-flg is nil, we treat lambdas as though they were function ; symbols. (cond ((variablep term2) (cond ((null vars) t) (t (cdr vars)))) ((fquotep term2) vars) ((and lambda-flg (flambda-applicationp term2)) t) ((member-eq (ffn-symb term2) '(if not implies)) t) (t (abbreviationp1-lst lambda-flg vars (fargs term2))))) (defun abbreviationp1-lst (lambda-flg vars lst) (cond ((null lst) vars) (t (let ((vars1 (abbreviationp1 lambda-flg vars (car lst)))) (cond ((eq vars1 t) t) (t (abbreviationp1-lst lambda-flg vars1 (cdr lst)))))))) ) (defun abbreviationp (lambda-flg vars term2) ; Consider the :REWRITE rule generated from (equal term1 term2). We ; say such a rule is an "abbreviation" if term2 contains no more ; variable occurrences than term1 and term2 does not call the ; functions IF, NOT or IMPLIES or (if lambda-flg is t) any LAMBDA. ; Vars, above, is the bag of vars from term1. We return non-nil iff ; (equal term1 term2) is an abbreviation. (not (eq (abbreviationp1 lambda-flg vars term2) t))) (mutual-recursion (defun all-vars-bag (term ans) (cond ((variablep term) (cons term ans)) ((fquotep term) ans) (t (all-vars-bag-lst (fargs term) ans)))) (defun all-vars-bag-lst (lst ans) (cond ((null lst) ans) (t (all-vars-bag-lst (cdr lst) (all-vars-bag (car lst) ans))))) ) (defun find-abbreviation-lemma (term geneqv lemmas ens wrld) ; Term is a function application, geneqv is a generated equivalence ; relation and lemmas is the 'lemmas property of the function symbol ; of term. We find the first (enabled) abbreviation lemma that ; rewrites term maintaining geneqv. A lemma is an abbreviation if it ; is not a meta-lemma, has no hypotheses, has no loop-stopper, and has ; an abbreviationp for the conclusion. ; If we win we return t, the rune of the :CONGRUENCE rule used, the ; lemma, and the unify-subst. Otherwise we return four nils. (cond ((null lemmas) (mv nil nil nil nil)) ((and (enabled-numep (access rewrite-rule (car lemmas) :nume) ens) (eq (access rewrite-rule (car lemmas) :subclass) 'abbreviation) (geneqv-refinementp (access rewrite-rule (car lemmas) :equiv) geneqv wrld)) (mv-let (wonp unify-subst) (one-way-unify (access rewrite-rule (car lemmas) :lhs) term) (cond (wonp (mv t (geneqv-refinementp (access rewrite-rule (car lemmas) :equiv) geneqv wrld) (car lemmas) unify-subst)) (t (find-abbreviation-lemma term geneqv (cdr lemmas) ens wrld))))) (t (find-abbreviation-lemma term geneqv (cdr lemmas) ens wrld)))) (mutual-recursion (defun expand-abbreviations-with-lemma (term geneqv fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state ttree) (mv-let (wonp cr-rune lemma unify-subst) (find-abbreviation-lemma term geneqv (getprop (ffn-symb term) 'lemmas nil 'current-acl2-world wrld) ens wrld) (cond (wonp (with-accumulated-persistence (access rewrite-rule lemma :rune) ((the (signed-byte 30) step-limit) term ttree) t (expand-abbreviations (access rewrite-rule lemma :rhs) unify-subst geneqv fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state (push-lemma cr-rune (push-lemma (access rewrite-rule lemma :rune) ttree))))) (t (mv step-limit term ttree))))) (defun expand-abbreviations (term alist geneqv fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state ttree) ; This function is essentially like rewrite but is more restrictive in ; its use of rules. We rewrite term/alist maintaining geneqv and ; avoiding the expansion or application of lemmas to terms whose fns ; are in fns-to-be-ignored-by-rewrite. We return a new term and a ; ttree (accumulated onto our argument) describing the rewrite. We ; only apply "abbreviations" which means we expand lambda applications ; and non-rec fns provided they do not duplicate arguments or ; introduce IFs, etc. (see abbreviationp), and we apply those ; unconditional :REWRITE rules with the same property. ; It used to be written: ; Note: In a break with Nqthm and the first four versions of ACL2, in ; Version 1.5 we also expand IMPLIES terms here. In fact, we expand ; several members of *expandable-boot-strap-non-rec-fns* here, and ; IFF. The impetus for this decision was the forcing of impossible ; goals by simplify-clause. As of this writing, we have just added ; the idea of forcing rounds and the concommitant notion that forced ; hypotheses are proved under the type-alist extant at the time of the ; force. But if the simplifer sees IMPLIES terms and rewrites their ; arguments, it does not augment the context, e.g., in (IMPLIES hyps ; concl) concl is rewritten without assuming hyps and thus assumptions ; forced in concl are context free and often impossible to prove. Now ; while the user might hide propositional structure in other functions ; and thus still suffer this failure mode, IMPLIES is the most common ; one and by opening it now we make our context clearer. See the note ; below for the reason we expand other ; *expandable-boot-strap-non-rec-fns*. ; This is no longer true. We now expand the IMPLIES from the original ; theorem in preprocess-clause before expand-abbreviations is called, ; and do not expand any others here. These changes in the handling of ; IMPLIES (as well as several others) are caused by the introduction ; of assume-true-false-if. See the mini-essay at ; assume-true-false-if. (cond ((zero-depthp rdepth) (rdepth-error (mv step-limit term ttree) t)) ((time-limit5-reached-p ; nil, or throws "Out of time in preprocess (expand-abbreviations).") (mv step-limit nil nil)) (t (let ((step-limit (decrement-step-limit step-limit))) (cond ((variablep term) (let ((temp (assoc-eq term alist))) (cond (temp (mv step-limit (cdr temp) ttree)) (t (mv step-limit term ttree))))) ((fquotep term) (mv step-limit term ttree)) ((and (eq (ffn-symb term) 'return-last) ; We avoid special treatment for return-last when the first argument is progn, ; since the user may have intended the first argument to be rewritten in that ; case; for example, the user might want to see a message printed when the term ; (prog2$ (cw ...) ...) is encountered. But it is useful in the other cases, ; in particular for calls of return-last generated by calls of mbe, to avoid ; spending time simplifying the next-to-last argument. (not (equal (fargn term 1) ''progn))) (expand-abbreviations (fargn term 3) alist geneqv fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state (push-lemma (fn-rune-nume 'return-last nil nil wrld) ttree))) ((eq (ffn-symb term) 'hide) (mv step-limit (sublis-var alist term) ttree)) (t (sl-let (expanded-args ttree) (expand-abbreviations-lst (fargs term) alist (geneqv-lst (ffn-symb term) geneqv ens wrld) fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state ttree) (let* ((fn (ffn-symb term)) (term (cons-term fn expanded-args))) ; If term does not collapse to a constant, fn is still its ffn-symb. (cond ((fquotep term) ; Term collapsed to a constant. But it wasn't a constant before, and so ; it collapsed because cons-term executed fn on constants. So we record ; a use of the executable counterpart. (mv step-limit term (push-lemma (fn-rune-nume fn nil t wrld) ttree))) ((member-equal fn fns-to-be-ignored-by-rewrite) (mv step-limit (cons-term fn expanded-args) ttree)) ((and (all-quoteps expanded-args) (enabled-xfnp fn ens wrld) (or (flambda-applicationp term) (not (getprop fn 'constrainedp nil 'current-acl2-world wrld)))) (cond ((flambda-applicationp term) (expand-abbreviations (lambda-body fn) (pairlis$ (lambda-formals fn) expanded-args) geneqv fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state ttree)) ((programp fn wrld) ; Why is the above test here? We do not allow :program mode fns in theorems. ; However, the prover can be called during definitions, and in particular we ; wind up with the call (SYMBOL-BTREEP NIL) when trying to admit the following ; definition. ; (defun symbol-btreep (x) ; (if x ; (and (true-listp x) ; (symbolp (car x)) ; (symbol-btreep (caddr x)) ; (symbol-btreep (cdddr x))) ; t)) (mv step-limit (cons-term fn expanded-args) ttree)) (t (mv-let (erp val latches) (pstk (ev-fncall fn (strip-cadrs expanded-args) state nil t nil)) (declare (ignore latches)) (cond (erp ; We following a suggestion from Matt Wilding and attempt to simplify the term ; before applying HIDE. (let ((new-term1 (cons-term fn expanded-args))) (sl-let (new-term2 ttree) (expand-abbreviations-with-lemma new-term1 geneqv fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state ttree) (cond ((equal new-term2 new-term1) (mv step-limit (mcons-term* 'hide new-term1) (push-lemma (fn-rune-nume 'hide nil nil wrld) ttree))) (t (mv step-limit new-term2 ttree)))))) (t (mv step-limit (kwote val) (push-lemma (fn-rune-nume fn nil t wrld) ttree)))))))) ((flambdap fn) (cond ((abbreviationp nil (lambda-formals fn) (lambda-body fn)) (expand-abbreviations (lambda-body fn) (pairlis$ (lambda-formals fn) expanded-args) geneqv fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state ttree)) (t ; Once upon a time (well into v1-9) we just returned (mv term ttree) ; here. But then Jun Sawada pointed out some problems with his proofs ; of some theorems of the form (let (...) (implies (and ...) ...)). ; The problem was that the implies was not getting expanded (because ; the let turns into a lambda and the implication in the body is not ; an abbreviationp, as checked above). So we decided that, in such ; cases, we would actually expand the abbreviations in the body ; without expanding the lambda itself, as we do below. This in turn ; often allows the lambda to expand via the following mechanism. ; Preprocess-clause calls expand-abbreviations and it expands the ; implies into IFs in the body without opening the lambda. But then ; preprocess-clause calls clausify-input which does another ; expand-abbreviations and this time the expansion is allowed. We do ; not imagine that this change will adversely affect proofs, but if ; so, well, the old code is shown on the first line of this comment. (sl-let (body ttree) (expand-abbreviations (lambda-body fn) nil geneqv fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state ttree) ; Rockwell Addition: ; Once upon another time (through v2-5) we returned the fcons-term ; shown in the t clause below. But Rockwell proofs indicate that it ; is better to eagerly expand this lambda if the new body would make ; it an abbreviation. (cond ((abbreviationp nil (lambda-formals fn) body) (expand-abbreviations body (pairlis$ (lambda-formals fn) expanded-args) geneqv fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state ttree)) (t (mv step-limit (mcons-term (list 'lambda (lambda-formals fn) body) expanded-args) ttree))))))) ((member-eq fn '(iff synp mv-list return-last wormhole-eval force case-split double-rewrite)) ; The list above is an arbitrary subset of *expandable-boot-strap-non-rec-fns*. ; Once upon a time we used the entire list here, but Bishop Brock complained ; that he did not want EQL opened. So we have limited the list to just the ; propositional function IFF and the no-ops. ; Note: Once upon a time we did not expand any propositional functions ; here. Indeed, one might wonder why we do now? The only place ; expand-abbreviations was called was from within preprocess-clause. ; And there, its output was run through clausify-input and then ; remove-trivial-clauses. The latter called tautologyp on each clause ; and that, in turn, expanded all the functions above (but discarded ; the expansion except for purposes of determining tautologyhood). ; Thus, there is no real case to make against expanding these guys. ; For sanity, one might wish to keep the list above in sync with ; that in tautologyp, where we say about it: "The list is in fact ; *expandable-boot-strap-non-rec-fns* with NOT deleted and IFF added. ; The main idea here is to include non-rec functions that users ; typically put into the elegant statements of theorems." But now we ; have deleted IMPLIES from this list, to support the assume-true-false-if ; idea, but we still keep IMPLIES in the list for tautologyp because ; if we can decide it's a tautology by expanding, all the better. (with-accumulated-persistence (fn-rune-nume fn nil nil wrld) ((the (signed-byte 30) step-limit) term ttree) t (expand-abbreviations (body fn t wrld) (pairlis$ (formals fn wrld) expanded-args) geneqv fns-to-be-ignored-by-rewrite (adjust-rdepth rdepth) step-limit ens wrld state (push-lemma (fn-rune-nume fn nil nil wrld) ttree)))) ; Rockwell Addition: We are expanding abbreviations. This is new treatment ; of IF, which didn't used to receive any special notice. ((eq fn 'if) ; There are no abbreviation (or rewrite) rules hung on IF, so coming out ; here is ok. (let ((a (car expanded-args)) (b (cadr expanded-args)) (c (caddr expanded-args))) (cond ((equal b c) (mv step-limit b ttree)) ((quotep a) (mv step-limit (if (eq (cadr a) nil) c b) ttree)) ((and (equal geneqv *geneqv-iff*) (equal b *t*) (or (equal c *nil*) (and (nvariablep c) (not (fquotep c)) (eq (ffn-symb c) 'HARD-ERROR)))) ; Some users keep HARD-ERROR disabled so that they can figure out ; which guard proof case they are in. HARD-ERROR is identically nil ; and we would really like to eliminate the IF here. So we use our ; knowledge that HARD-ERROR is nil even if it is disabled. We don't ; even put it in the ttree, because for all the user knows this is ; primitive type inference. (mv step-limit a ttree)) (t (mv step-limit (mcons-term 'if expanded-args) ttree))))) ; Rockwell Addition: New treatment of equal. ((and (eq fn 'equal) (equal (car expanded-args) (cadr expanded-args))) (mv step-limit *t* ttree)) (t (expand-abbreviations-with-lemma term geneqv fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state ttree))))))))))) (defun expand-abbreviations-lst (lst alist geneqv-lst fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state ttree) (cond ((null lst) (mv step-limit nil ttree)) (t (sl-let (term1 new-ttree) (expand-abbreviations (car lst) alist (car geneqv-lst) fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state ttree) (sl-let (terms1 new-ttree) (expand-abbreviations-lst (cdr lst) alist (cdr geneqv-lst) fns-to-be-ignored-by-rewrite rdepth step-limit ens wrld state new-ttree) (mv step-limit (cons term1 terms1) new-ttree)))))) ) (defun and-orp (term bool) ; We return t or nil according to whether term is a disjunction ; (if bool is t) or conjunction (if bool is nil). (case-match term (('if & c2 c3) (if bool (or (equal c2 *t*) (equal c3 *t*)) (or (equal c2 *nil*) (equal c3 *nil*)))))) (defun find-and-or-lemma (term bool lemmas ens wrld) ; Term is a function application and lemmas is the 'lemmas property of ; the function symbol of term. We find the first enabled and-or ; (wrt bool) lemma that rewrites term maintaining iff. ; If we win we return t, the :CONGRUENCE rule name, the lemma, and the ; unify-subst. Otherwise we return four nils. (cond ((null lemmas) (mv nil nil nil nil)) ((and (enabled-numep (access rewrite-rule (car lemmas) :nume) ens) (or (eq (access rewrite-rule (car lemmas) :subclass) 'backchain) (eq (access rewrite-rule (car lemmas) :subclass) 'abbreviation)) (null (access rewrite-rule (car lemmas) :hyps)) (null (access rewrite-rule (car lemmas) :heuristic-info)) (geneqv-refinementp (access rewrite-rule (car lemmas) :equiv) *geneqv-iff* wrld) (and-orp (access rewrite-rule (car lemmas) :rhs) bool)) (mv-let (wonp unify-subst) (one-way-unify (access rewrite-rule (car lemmas) :lhs) term) (cond (wonp (mv t (geneqv-refinementp (access rewrite-rule (car lemmas) :equiv) *geneqv-iff* wrld) (car lemmas) unify-subst)) (t (find-and-or-lemma term bool (cdr lemmas) ens wrld))))) (t (find-and-or-lemma term bool (cdr lemmas) ens wrld)))) (defun expand-and-or (term bool fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) ; We expand the top-level fn symbol of term provided the expansion produces a ; conjunction -- when bool is nil -- or a disjunction -- when bool is t. We ; return four values: the new step-limit, wonp, the new term, and a new ttree. ; This fn is a No-Change Loser. ; Note that preprocess-clause calls expand-abbreviations; but also ; preprocess-clause calls clausify-input, which calls expand-and-or, which ; calls expand-abbreviations. But this is not redundant, as expand-and-or ; calls expand-abbreviations after expanding function definitions and using ; rewrite rules when the result is a conjunction or disjunction (depending on ; bool) -- even when the rule being applied is not an abbreviation rule. Below ; are event sequences that illustrate this extra work being done. In both ; cases, evaluation of (getprop 'foo 'lemmas nil 'current-acl2-world (w state)) ; shows that we are expanding with a rewrite-rule structure that is not of ; subclass 'abbreviation. ; (defstub bar (x) t) ; (defun foo (x) (and (bar (car x)) (bar (cdr x)))) ; (trace$ expand-and-or expand-abbreviations clausify-input preprocess-clause) ; (thm (foo x) :hints (("Goal" :do-not-induct :otf))) ; (defstub bar (x) t) ; (defstub foo (x) t) ; (defaxiom foo-open (equal (foo x) (and (bar (car x)) (bar (cdr x))))) ; (trace$ expand-and-or expand-abbreviations clausify-input preprocess-clause) ; (thm (foo x) :hints (("Goal" :do-not-induct :otf))) (cond ((variablep term) (mv step-limit nil term ttree)) ((fquotep term) (mv step-limit nil term ttree)) ((member-equal (ffn-symb term) fns-to-be-ignored-by-rewrite) (mv step-limit nil term ttree)) ((flambda-applicationp term) (cond ((and-orp (lambda-body (ffn-symb term)) bool) (sl-let (term ttree) (expand-abbreviations (subcor-var (lambda-formals (ffn-symb term)) (fargs term) (lambda-body (ffn-symb term))) nil *geneqv-iff* fns-to-be-ignored-by-rewrite (rewrite-stack-limit wrld) step-limit ens wrld state ttree) (mv step-limit t term ttree))) (t (mv step-limit nil term ttree)))) (t (let ((def-body (def-body (ffn-symb term) wrld))) (cond ((and def-body (null (access def-body def-body :recursivep)) (null (access def-body def-body :hyp)) (enabled-numep (access def-body def-body :nume) ens) (and-orp (access def-body def-body :concl) bool)) (sl-let (term ttree) (with-accumulated-persistence (access def-body def-body :rune) ((the (signed-byte 30) step-limit) term ttree) t (expand-abbreviations (subcor-var (access def-body def-body :formals) (fargs term) (access def-body def-body :concl)) nil *geneqv-iff* fns-to-be-ignored-by-rewrite (rewrite-stack-limit wrld) step-limit ens wrld state (push-lemma? (access def-body def-body :rune) ttree))) (mv step-limit t term ttree))) (t (mv-let (wonp cr-rune lemma unify-subst) (find-and-or-lemma term bool (getprop (ffn-symb term) 'lemmas nil 'current-acl2-world wrld) ens wrld) (cond (wonp (sl-let (term ttree) (with-accumulated-persistence (access rewrite-rule lemma :rune) ((the (signed-byte 30) step-limit) term ttree) t (expand-abbreviations (sublis-var unify-subst (access rewrite-rule lemma :rhs)) nil *geneqv-iff* fns-to-be-ignored-by-rewrite (rewrite-stack-limit wrld) step-limit ens wrld state (push-lemma cr-rune (push-lemma (access rewrite-rule lemma :rune) ttree)))) (mv step-limit t term ttree))) (t (mv step-limit nil term ttree)))))))))) (defun clausify-input1 (term bool fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) ; We return three things: a new step-limit, a clause, and a ttree. If bool is ; t, the (disjunction of the literals in the) clause is equivalent to term. If ; bool is nil, the clause is equivalent to the negation of term. This function ; opens up some nonrec fns and applies some rewrite rules. The final ttree ; contains the symbols and rules used. (cond ((equal term (if bool *nil* *t*)) (mv step-limit nil ttree)) ((and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'if)) (let ((t1 (fargn term 1)) (t2 (fargn term 2)) (t3 (fargn term 3))) (cond (bool (cond ((equal t3 *t*) (sl-let (cl1 ttree) (clausify-input1 t1 nil fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (sl-let (cl2 ttree) (clausify-input1 t2 t fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (mv step-limit (disjoin-clauses cl1 cl2) ttree)))) ((equal t2 *t*) (sl-let (cl1 ttree) (clausify-input1 t1 t fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (sl-let (cl2 ttree) (clausify-input1 t3 t fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (mv step-limit (disjoin-clauses cl1 cl2) ttree)))) (t (mv step-limit (list term) ttree)))) ((equal t3 *nil*) (sl-let (cl1 ttree) (clausify-input1 t1 nil fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (sl-let (cl2 ttree) (clausify-input1 t2 nil fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (mv step-limit (disjoin-clauses cl1 cl2) ttree)))) ((equal t2 *nil*) (sl-let (cl1 ttree) (clausify-input1 t1 t fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (sl-let (cl2 ttree) (clausify-input1 t3 nil fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (mv step-limit (disjoin-clauses cl1 cl2) ttree)))) (t (mv step-limit (list (dumb-negate-lit term)) ttree))))) (t (sl-let (wonp term ttree) (expand-and-or term bool fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (cond (wonp (clausify-input1 term bool fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit)) (bool (mv step-limit (list term) ttree)) (t (mv step-limit (list (dumb-negate-lit term)) ttree))))))) (defun clausify-input1-lst (lst fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) ; This function is really a subroutine of clausify-input. It just ; applies clausify-input1 to every element of lst, accumulating the ttrees. ; It uses bool=t. (cond ((null lst) (mv step-limit nil ttree)) (t (sl-let (clause ttree) (clausify-input1 (car lst) t fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (sl-let (clauses ttree) (clausify-input1-lst (cdr lst) fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) (mv step-limit (conjoin-clause-to-clause-set clause clauses) ttree)))))) (defun clausify-input (term fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) ; This function converts term to a set of clauses, expanding some non-rec ; functions when they produce results of the desired parity (i.e., we expand ; AND-like functions in the hypotheses and OR-like functions in the ; conclusion.) AND and OR themselves are, of course, already expanded into ; IFs, but we will expand other functions when they generate the desired IF ; structure. We also apply :REWRITE rules deemed appropriate. We return three ; results: a new step-limit, the set of clauses, and a ttree documenting the ; expansions. (sl-let (neg-clause ttree) (clausify-input1 term nil fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit) ; Neg-clause is a clause that is equivalent to the negation of term. That is, ; if the literals of neg-clause are lit1, ..., litn, then (or lit1 ... litn) ; <-> (not term). Therefore, term is the negation of the clause, i.e., (and ; (not lit1) ... (not litn)). We will form a clause from each (not lit1) and ; return the set of clauses, implicitly conjoined. (clausify-input1-lst (dumb-negate-lit-lst neg-clause) fns-to-be-ignored-by-rewrite ens wrld state ttree step-limit))) (defun expand-some-non-rec-fns-in-clauses (fns clauses wrld) ; Warning: fns should be a subset of functions that ; This function expands the non-rec fns listed in fns in each of the clauses ; in clauses. It then throws out of the set any trivial clause, i.e., ; tautologies. It does not normalize the expanded terms but just leaves ; the expanded bodies in situ. See the comment in preprocess-clause. (cond ((null clauses) nil) (t (let ((cl (expand-some-non-rec-fns-lst fns (car clauses) wrld))) (cond ((trivial-clause-p cl wrld) (expand-some-non-rec-fns-in-clauses fns (cdr clauses) wrld)) (t (cons cl (expand-some-non-rec-fns-in-clauses fns (cdr clauses) wrld)))))))) (defun no-op-histp (hist) ; We say a history, hist, is a "no-op history" if it is empty or its most ; recent entry is a to-be-hidden preprocess-clause or apply-top-hints-clause ; (possibly followed by a settled-down-clause). (or (null hist) (and hist (member-eq (access history-entry (car hist) :processor) '(apply-top-hints-clause preprocess-clause)) (tag-tree-occur 'hidden-clause t (access history-entry (car hist) :ttree))) (and hist (eq (access history-entry (car hist) :processor) 'settled-down-clause) (cdr hist) (member-eq (access history-entry (cadr hist) :processor) '(apply-top-hints-clause preprocess-clause)) (tag-tree-occur 'hidden-clause t (access history-entry (cadr hist) :ttree))))) (mutual-recursion ; This pair of functions is copied from expand-abbreviations and ; heavily modified. The idea implemented by the caller of this ; function is to expand all the IMPLIES terms in the final literal of ; the goal clause. This pair of functions actually implements that ; expansion. One might think to use expand-some-non-rec-fns with ; first argument '(IMPLIES). But this function is different in two ; respects. First, it respects HIDE. Second, it expands the IMPLIES ; inside of lambda bodies. The basic idea is to mimic what ; expand-abbreviations used to do, before we added the ; assume-true-false-if idea. (defun expand-any-final-implies1 (term wrld) (cond ((variablep term) term) ((fquotep term) term) ((eq (ffn-symb term) 'hide) term) (t (let ((expanded-args (expand-any-final-implies1-lst (fargs term) wrld))) (let* ((fn (ffn-symb term)) (term (cons-term fn expanded-args))) (cond ((flambdap fn) (let ((body (expand-any-final-implies1 (lambda-body fn) wrld))) ; Note: We could use a make-lambda-application here, but if the ; original lambda used all of its variables then so does the new one, ; because IMPLIES uses all of its variables and we're not doing any ; simplification. This remark is not soundness related; there is no ; danger of introducing new variables, only the inefficiency of ; keeping a big actual which is actually not used. (fcons-term (make-lambda (lambda-formals fn) body) expanded-args))) ((eq fn 'IMPLIES) (subcor-var (formals 'implies wrld) expanded-args (body 'implies t wrld))) (t term))))))) (defun expand-any-final-implies1-lst (term-lst wrld) (cond ((null term-lst) nil) (t (cons (expand-any-final-implies1 (car term-lst) wrld) (expand-any-final-implies1-lst (cdr term-lst) wrld))))) ) (defun expand-any-final-implies (cl wrld) ; Cl is a clause (a list of ACL2 terms representing a goal) about to ; enter preprocessing. If the final term contains an 'IMPLIES, we ; expand those IMPLIES here. This change in the handling of IMPLIES ; (as well as several others) is caused by the introduction of ; assume-true-false-if. See the mini-essay at assume-true-false-if. ; Note that we fail to report the fact that we used the definition ; of IMPLIES. ; Note also that we do not use expand-some-non-rec-fns here. We want ; to preserve the meaning of 'HIDE and expand an 'IMPLIES inside of ; a lambda. (cond ((null cl) ; This should not happen. nil) ((null (cdr cl)) (list (expand-any-final-implies1 (car cl) wrld))) (t (cons (car cl) (expand-any-final-implies (cdr cl) wrld))))) (defun rw-cache-state (wrld) (let ((pair (assoc-eq t (table-alist 'rw-cache-state-table wrld)))) (cond (pair (cdr pair)) (t *default-rw-cache-state*)))) (defmacro make-rcnst (ens wrld &rest args) ; (Make-rcnst w) will make a rewrite-constant that is the result of filling in ; *empty-rewrite-constant* with a few obviously necessary values, such as the ; global-enabled-structure as the :current-enabled-structure. Then it ; additionally loads user supplied values specified by alternating keyword/value ; pairs to override what is otherwise created. E.g., ; (make-rcnst w :expand-lst lst) ; will make a rewrite-constant that is like the default one except that it will ; have lst as the :expand-lst. ; Note: Wrld and ens are used in the "default" setting of certain fields. `(change rewrite-constant (change rewrite-constant *empty-rewrite-constant* :current-enabled-structure ,ens :oncep-override (match-free-override ,wrld) :case-split-limitations (case-split-limitations ,wrld) :nonlinearp (non-linearp ,wrld) :backchain-limit-rw (backchain-limit ,wrld :rewrite) :rw-cache-state (rw-cache-state ,wrld)) ,@args)) ; We now finish the development of tau-clause... To recap our story so far: In ; the file tau.lisp we defined everything we need to implement tau-clause ; except for its connection to type-alist and the linear pot-lst. Now we can ; define tau-clause. (defun cheap-type-alist-and-pot-lst (cl ens wrld state) ; Given a clause cl, we build a type-alist and linear pot-lst with all of the ; literals in cl assumed false. The pot-lst is built with the cheap-linearp ; flag on, which means we do not rewrite terms before turning them into polys ; and we add no linear lemmas. We insure that the type-alist has no ; assumptions or forced hypotheses. FYI: Just to be doubly sure that we are ; not ignoring assumptions and forced hypotheses, you will note that in ; relieve-dependent-hyps, after calling type-set, we check that no such entries ; are in the returned ttree. We return (mv contradictionp type-alist pot-lst) (mv-let (contradictionp type-alist ttree) (type-alist-clause cl nil nil nil ens wrld nil nil) (cond ((or (tagged-objectsp 'assumption ttree) (tagged-objectsp 'fc-derivation ttree)) (mv (er hard 'cheap-type-alist-and-pot-lst "Assumptions and/or fc-derivations were found in the ~ ttree constructed by CHEAP-TYPE-ALIST-AND-POT-LST. This ~ is supposedly impossible!") nil nil)) (contradictionp (mv t nil nil)) (t (mv-let (new-step-limit provedp pot-lst) (setup-simplify-clause-pot-lst1 cl nil type-alist (make-rcnst ens wrld :force-info 'weak :cheap-linearp t) wrld state *default-step-limit*) (declare (ignore new-step-limit)) (cond (provedp (mv t nil nil)) (t (mv nil type-alist pot-lst)))))))) (defconst *tau-ttree* (add-to-tag-tree 'lemma '(:executable-counterpart tau-system) nil)) (defun tau-clausep (clause ens wrld state calist) ; This function returns (mv flg ttree), where if flg is t then clause is true. ; The ttree, when non-nil, is just the *tau-ttree*. ; If the executable-counterpart of tau is disabled, this function is a no-op. (cond ((enabled-numep *tau-system-xnume* ens) (mv-let (contradictionp type-alist pot-lst) (cheap-type-alist-and-pot-lst clause ens wrld state) (cond (contradictionp (mv t *tau-ttree* calist)) (t (let ((triples (merge-sort-car-< (annotate-clause-with-key-numbers clause (len clause) 0 wrld)))) (mv-let (flg calist) (tau-clause1p triples nil type-alist pot-lst ens wrld calist) (cond ((eq flg t) (mv t *tau-ttree* calist)) (t (mv nil nil calist))))))))) (t (mv nil nil calist)))) (defun tau-clausep-lst-rec (clauses ens wrld ans ttree state calist) ; We return (mv clauses' ttree) where clauses' are provably equivalent to ; clauses under the tau rules and ttree is either the tau ttree or nil ; depending on whether anything changed. Note that this function knows that if ; tau-clause returns non-nil ttree it is *tau-ttree*: we just OR the ttrees ; together, not CONS-TAG-TREES them! (cond ((endp clauses) (mv (revappend ans nil) ttree calist)) (t (mv-let (flg1 ttree1 calist) (tau-clausep (car clauses) ens wrld state calist) (prog2$ ; If the time-tracker call below is changed, update :doc time-tracker ; accordingly. (time-tracker :tau :print?) (tau-clausep-lst-rec (cdr clauses) ens wrld (if flg1 ans (cons (car clauses) ans)) (or ttree1 ttree) state calist)))))) (defun tau-clausep-lst (clauses ens wrld ans ttree state calist) ; If the time-tracker calls below are changed, update :doc time-tracker ; accordingly. (prog2$ (time-tracker :tau :start) (mv-let (clauses ttree calist) (tau-clausep-lst-rec clauses ens wrld ans ttree state calist) (prog2$ (time-tracker :tau :stop) (mv clauses ttree calist))))) (defun preprocess-clause (cl hist pspv wrld state step-limit) ; This is the first "real" clause processor (after a little remembered ; apply-top-hints-clause) in the waterfall. Its arguments and values are the ; standard ones, except that it takes a step-limit and returns a new step-limit ; in the first position. We expand abbreviations and clausify the clause cl. ; For mainly historic reasons, expand-abbreviations and clausify-input operate ; on terms. Thus, our first move is to convert cl into a term. (let ((rcnst (access prove-spec-var pspv :rewrite-constant))) (mv-let (built-in-clausep ttree) (cond ((or (eq (car (car hist)) 'simplify-clause) (eq (car (car hist)) 'settled-down-clause)) ; If the hist shows that cl has just come from simplification, there is no ; need to check that it is built in, because the simplifier does that. (mv nil nil)) (t (built-in-clausep 'preprocess-clause cl (access rewrite-constant rcnst :current-enabled-structure) (access rewrite-constant rcnst :oncep-override) wrld state))) ; Ttree is known to be 'assumption free. (cond (built-in-clausep (mv step-limit 'hit nil ttree pspv)) (t ; Here is where we expand the "original" IMPLIES in the conclusion but ; leave any IMPLIES in the hypotheses. These IMPLIES are thought to ; have been introduced by :USE hints. (let ((term (disjoin (expand-any-final-implies cl wrld)))) (sl-let (term ttree) (expand-abbreviations term nil *geneqv-iff* (access rewrite-constant rcnst :fns-to-be-ignored-by-rewrite) (rewrite-stack-limit wrld) step-limit (access rewrite-constant rcnst :current-enabled-structure) wrld state nil) (sl-let (clauses ttree) (clausify-input term (access rewrite-constant rcnst :fns-to-be-ignored-by-rewrite) (access rewrite-constant rcnst :current-enabled-structure) wrld state ttree step-limit) ;;; (let ((clauses ;;; (expand-some-non-rec-fns-in-clauses ;;; '(iff implies) ;;; clauses ;;; wrld))) ; Previous to Version_2.6 we had written: ; ; Note: Once upon a time (in Version 1.5) we called "clausify-clause-set" here. ; ; That function called clausify on each element of clauses and unioned the ; ; results together, in the process naturally deleting tautologies as does ; ; expand-some-non-rec-fns-in-clauses above. But Version 1.5 caused Bishop a ; ; lot of pain because many theorems would explode into case analyses, each of ; ; which was then dispatched by simplification. The reason we used a full-blown ; ; clausify in Version 1.5 was that in was also into that version that we ; ; introduced forcing rounds and the liberal use of force-flg = t. But if we ; ; are to force that way, we must really get all of our hypotheses out into the ; ; open so that they can contribute to the type-alist stored in each assumption. ; ; For example, in Version 1.4 the concl of (IMPLIES hyps concl) was rewritten ; ; first without the hyps being manifest in the type-alist since IMPLIES is a ; ; function. Not until the IMPLIES was opened did the hyps become "governers" ; ; in this sense. In Version 1.5 we decided to throw caution to the wind and ; ; just clausify the clausified input. Well, it bit us as mentioned above and ; ; we are now backing off to simply expanding the non-rec fns that might ; ; contribute hyps. But we leave the expansions in place rather than normalize ; ; them out so that simplification has one shot on a small set (usually ; ; singleton set) of clauses. ; But the comment above is now irrelevant to the current situation. ; Before commenting on the current situation, however, we point out that ; in (admittedly light) testing the original call to ; expand-some-non-rec-fns-in-clauses in its original context acted as ; the identity. This seems reasonable because 'iff and 'implies were ; expanded in expand-abbreviations. ; We now expand the 'implies from the original theorem (but not the ; implies from a :use hint) in the call to expand-any-final-implies. ; This performs the expansion whose motivations are mentioned in the ; old comments above, but does not interfere with the conclusions ; of a :use hint. See the mini-essay ; Mini-Essay on Assume-true-false-if and Implies ; or ; How Strengthening One Part of a Theorem Prover Can Weaken the Whole. ; in type-set-b for more details on this latter criterion. (let ((tau-completion-alist (access prove-spec-var pspv :tau-completion-alist))) (mv-let (clauses1 ttree1 new-tau-completion-alist) (if (or (null hist) ; If (null (cdr hist)) and (null (cddr hist)) are tested in this disjunction, ; then tau is tried during the first three simplifications and then again when ; the clause settles down. Call this the ``more aggressive'' approach. If ; they are not tested, tau is tried only on the first simplification and upon ; settling down. Call this ``less aggressive.'' There are, of course, proofs ; where the more aggressive use of tau speeds things up. But of course it ; slows down many more proofs. Overall, experiments on the regression suggest ; that the more aggressive approach slows total reported book certification ; time down by about 1.5% compared to the less agressive approach. However, we ; think it might be worth it as more tau-based proofs scripts are developed. (null (cdr hist)) (null (cddr hist)) (eq (car (car hist)) 'settled-down-clause)) (let ((ens (access rewrite-constant rcnst :current-enabled-structure))) (if (enabled-numep *tau-system-xnume* ens) (tau-clausep-lst clauses ens wrld nil nil state tau-completion-alist) (mv clauses nil tau-completion-alist))) (mv clauses nil tau-completion-alist)) (let ((pspv (if (equal tau-completion-alist new-tau-completion-alist) pspv (change prove-spec-var pspv :tau-completion-alist new-tau-completion-alist)))) (cond ((equal clauses1 (list cl)) ; In this case, preprocess-clause has made no changes to the clause. (mv step-limit 'miss nil nil nil)) ((and (consp clauses1) (null (cdr clauses1)) (no-op-histp hist) (equal (prettyify-clause (car clauses1) (let*-abstractionp state) wrld) (access prove-spec-var pspv :displayed-goal))) ; In this case preprocess-clause has produced a singleton set of ; clauses whose only element will be displayed exactly like what the ; user thinks is the input to prove. For example, the user might have ; invoked defthm on (implies p q) and preprocess has managed to to ; produce the singleton set of clauses containing {(not p) q}. This ; is a valuable step in the proof of course. However, users complain ; when we report that (IMPLIES P Q) -- the displayed goal -- is ; reduced to (IMPLIES P Q) -- the prettyification of the output. ; We therefore take special steps to hide this transformation from the ; user without changing the flow of control through the waterfall. In ; particular, we will insert into the ttree the tag ; 'hidden-clause with (irrelevant) value t. In subsequent places ; where we print explanations and clauses to the user we will look for ; this tag. (mv step-limit 'hit clauses1 (add-to-tag-tree 'hidden-clause t (cons-tag-trees ttree1 ttree)) pspv)) (t (mv step-limit 'hit clauses1 (cons-tag-trees ttree1 ttree) pspv)))))))))))))) ; And here is the function that reports on a successful preprocessing. (defun tilde-*-preprocess-phrase (ttree) ; This function is like tilde-*-simp-phrase but knows that ttree was ; constructed by preprocess-clause and hence is based on abbreviation ; expansion rather than full-fledged rewriting. ; Warning: The function apply-top-hints-clause-msg1 knows ; that if the (car (cddddr &)) of the result is nil then nothing but ; case analysis was done! (mv-let (message-lst char-alist) (tilde-*-simp-phrase1 (extract-and-classify-lemmas ttree '(implies not iff) nil) ; Note: The third argument to extract-and-classify-lemmas is the list ; of forced runes, which we assume to be nil in preprocessing. If ; this changes, see the comment in fertilize-clause-msg1. t) (list* "case analysis" "~@*" "~@* and " "~@*, " message-lst char-alist))) (defun tilde-*-raw-preprocess-phrase (ttree punct) ; See tilde-*-preprocess-phrase. Here we print for a non-nil value of state ; global 'raw-proof-format. (let ((runes (all-runes-in-ttree ttree nil))) (mv-let (message-lst char-alist) (tilde-*-raw-simp-phrase1 runes nil ; forced-runes punct '(implies not iff) ; ignore-lst "" ; phrase nil) (list* (concatenate 'string "case analysis" (case punct (#\, ",") (#\. ".") (otherwise ""))) "~@*" "~@* and " "~@*, " message-lst char-alist)))) (defun preprocess-clause-msg1 (signal clauses ttree pspv state) ; This function is one of the waterfall-msg subroutines. It has the ; standard arguments of all such functions: the signal, clauses, ttree ; and pspv produced by the given processor, in this case ; preprocess-clause. It produces the report for this step. (declare (ignore signal pspv)) (let ((raw-proof-format (f-get-global 'raw-proof-format state))) (cond ((tag-tree-occur 'hidden-clause t ttree) ; If this preprocess clause is to be hidden, e.g., because it transforms ; (IMPLIES P Q) to {(NOT P) Q}, we print no message. Note that this is ; just part of the hiding. Later in the waterfall, when some other processor ; has successfully hit our output, that output will be printed and we ; need to stop that printing too. state) ((and raw-proof-format (null clauses)) (fms "But preprocess reduces the conjecture to T, by ~*0~|" (list (cons #\0 (tilde-*-raw-preprocess-phrase ttree #\.))) (proofs-co state) state (term-evisc-tuple nil state))) ((null clauses) (fms "But we reduce the conjecture to T, by ~*0.~|" (list (cons #\0 (tilde-*-preprocess-phrase ttree))) (proofs-co state) state (term-evisc-tuple nil state))) (raw-proof-format (fms "Preprocess reduces the conjecture to ~#1~[~x2~/the ~ following~/the following ~n3 conjectures~], by ~*0~|" (list (cons #\0 (tilde-*-raw-preprocess-phrase ttree #\.)) (cons #\1 (zero-one-or-more clauses)) (cons #\2 t) (cons #\3 (length clauses))) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "By ~*0 we reduce the conjecture to~#1~[~x2.~/~/ the ~ following ~n3 conjectures.~]~|" (list (cons #\0 (tilde-*-preprocess-phrase ttree)) (cons #\1 (zero-one-or-more clauses)) (cons #\2 t) (cons #\3 (length clauses))) (proofs-co state) state (term-evisc-tuple nil state)))))) ; Section: PUSH-CLAUSE and The Pool ; At the opposite end of the waterfall from the preprocessor is push-clause, ; where we actually put a clause into the pool. We develop it now. (defun more-than-simplifiedp (hist) ; Return t if hist contains a process besides simplify-clause (and its ; mates settled-down-clause and preprocess-clause), where we don't count ; certain top-level hints: :OR, or top-level hints that create hidden clauses. (cond ((null hist) nil) ((member-eq (caar hist) '(settled-down-clause simplify-clause preprocess-clause)) (more-than-simplifiedp (cdr hist))) ((eq (caar hist) 'apply-top-hints-clause) (if (or (tagged-objectsp 'hidden-clause (access history-entry (car hist) :ttree)) (tagged-objectsp ':or (access history-entry (car hist) :ttree))) (more-than-simplifiedp (cdr hist)) t)) (t t))) (defun delete-assoc-eq-lst (lst alist) (declare (xargs :guard (or (symbol-listp lst) (symbol-alistp alist)))) (if (consp lst) (delete-assoc-eq-lst (cdr lst) (delete-assoc-eq (car lst) alist)) alist)) (defun delete-assumptions-1 (recs only-immediatep) ; See comment for delete-assumptions. This function returns (mv changedp ; new-recs), where if changedp is nil then new-recs equals recs. (cond ((endp recs) (mv nil nil)) (t (mv-let (changedp new-cdr-recs) (delete-assumptions-1 (cdr recs) only-immediatep) (cond ((cond ((eq only-immediatep 'non-nil) (access assumption (car recs) :immediatep)) ((eq only-immediatep 'case-split) (eq (access assumption (car recs) :immediatep) 'case-split)) ((eq only-immediatep t) (eq (access assumption (car recs) :immediatep) t)) (t t)) (mv t new-cdr-recs)) (changedp (mv t (cons (car recs) new-cdr-recs))) (t (mv nil recs))))))) (defun delete-assumptions (ttree only-immediatep) ; We delete the assumptions in ttree. We give the same interpretation to ; only-immediatep as in collect-assumptions. (let ((objects (tagged-objects 'assumption ttree))) (cond (objects (mv-let (changedp new-objects) (delete-assumptions-1 objects only-immediatep) (cond ((null changedp) ttree) (new-objects (extend-tag-tree 'assumption new-objects (remove-tag-from-tag-tree! 'assumption ttree))) (t (remove-tag-from-tag-tree! 'assumption ttree))))) (t ttree)))) #+acl2-par (defun save-and-print-acl2p-checkpoint (cl-id prettyified-clause old-pspv-pool-lst forcing-round state) ; We almost note that we are changing the global state of the program by ; returning a modified state. However, we manually ensure that this global ; change is thread-safe by calling with-acl2p-checkpoint-saving-lock, and so ; instead, we give ourselves the Okay to call f-put-global@par. (declare (ignorable cl-id prettyified-clause state)) (let* ((new-pair (cons cl-id prettyified-clause)) (newp (with-acl2p-checkpoint-saving-lock (cond ((member-equal new-pair (f-get-global 'acl2p-checkpoints-for-summary state)) nil) (t (prog2$ (f-put-global@par 'acl2p-checkpoints-for-summary (cons new-pair (f-get-global 'acl2p-checkpoints-for-summary state)) state) t)))))) (and newp (with-output-lock (progn$ (cw "~%~%([ An ACL2(p) key checkpoint:~%~%~s0~%" (string-for-tilde-@-clause-id-phrase cl-id)) (cw "~x0" prettyified-clause) ; Parallelism no-fix: we are encountering a problem that we've known about from ; within the first few months of looking at parallelizing the waterfall. When ; two sibling subgoals both push for induction, the second push doesn't know ; about the first proof's push in parallel mode. So, the number of the second ; proof (e.g., *1.2) gets printed as if the first push hasn't happened (e.g., ; *1.2 gets mistakenly called *1.1). Rather than fix this (the problem is ; inherent to the naming scheme of ACL2), we punt and say what the name _could_ ; be (e.g., we print *1.1 for what's really *1.2). The following non-theorem ; showcases this problem. See :doc topic set-waterfall-printing. ; (thm (equal (append (car (cons x x)) y z) (append x x y))) ; The sentence in the following cw call concerning the halting of the proof ; attempt is motivated by the following example -- which is relevant because ; ACL2(p) with :limited waterfall-printing does not print a message that says ; the :do-not-induct hint causes the proof to abort. ; (thm (equal (append x (append y z)) (append (append x y) z)) ; :hints (("Goal" :do-not-induct t))) (cw "~%~%The above subgoal may cause a goal to be pushed for proof by ~ induction. The pushed goal's new name might be ~@0. Note that ~ we may instead decide (either now or later) to prove the original ~ conjecture by induction. Also note that if a hint indicates that ~ this subgoal or the original conjecture should not be proved by ~ induction, the proof attempt will halt.~%~%])~%~%" (tilde-@-pool-name-phrase forcing-round old-pspv-pool-lst))))))) #+acl2-par (defun find-the-first-checkpoint (h checkpoint-processors) ; "H" is the history reversed, which really means h is in the order that the ; entries were added. E.g. the history entry for subgoal 1.2 is before the ; entry for 1.1.4. To remind us that this is not the "standard ACL2 history" ; (which is often in the other order), we name the variable "h" instead of ; "hist." (cond ((atom h) ; occurs when we are at the top-level goal nil) ((atom (cdr h)) (car h)) ; maybe this should also be an error ((member (access history-entry (cadr h) :processor) checkpoint-processors) (car h)) ; Parallelism blemish: we haven't thought through how specious entries affect ; this function. The following code is left as a hint at what might be needed. ; ((or (and (consp (access history-entry (cadr h) :processor)) ; (equal (access history-entry (cadr h) :processor) ; 'specious)) (t (find-the-first-checkpoint (cdr h) checkpoint-processors)))) #+acl2-par (defun acl2p-push-clause-printing (cl hist pspv wrld state) (cond ((null cl) ; The following non-theorem illustrates the case where we generate the clause ; nil, and instead of printing the associated key checkpoint, we inform the ; user that nil was generated from that checkpoint. ; (thm (equal (append (car (cons x x)) y z) (append x x y))) (cw "~%~%A goal of ~x0 has been generated! Obviously, the proof attempt ~ has failed.~%" cl)) (t (let* ((hist-entry (find-the-first-checkpoint (reverse hist) (f-get-global 'checkpoint-processors state))) (checkpoint-clause (or (access history-entry hist-entry :clause) ; We should be able to add an assertion that, if the hist-entry is nil (and ; thus, the :clause field of hist-entry is also nil), cl always has the same ; printed representation as the original conjecture. However, since we do not ; have access to the original conjecture in this function, we avoid such an ; assertion. cl)) (cl-id (access history-entry hist-entry :cl-id)) (cl-id (if cl-id cl-id *initial-clause-id*)) (forcing-round (access clause-id cl-id :forcing-round)) (old-pspv-pool-lst (pool-lst (cdr (access prove-spec-var pspv :pool)))) (prettyified-clause (prettyify-clause checkpoint-clause (let*-abstractionp state) wrld))) (save-and-print-acl2p-checkpoint cl-id prettyified-clause old-pspv-pool-lst forcing-round state))))) (defun@par push-clause (cl hist pspv wrld state) ; Roughly speaking, we drop cl into the pool of pspv and return. ; However, we sometimes cause the waterfall to abort further ; processing (either to go straight to induction or to fail) and we ; also sometimes choose to push a different clause into the pool. We ; even sometimes miss and let the waterfall fall off the end of the ; ledge! We make this precise in the code below. ; The pool is actually a list of pool-elements and is treated as a ; stack. The clause-set is a set of clauses and is almost always a ; singleton set. The exception is when it contains the clausification ; of the user's initial conjecture. ; The expected tags are: ; 'TO-BE-PROVED-BY-INDUCTION - the clause set is to be given to INDUCT ; 'BEING-PROVED-BY-INDUCTION - the clause set has been given to INDUCT and ; we are working on its subgoals now. ; Like all clause processors, we return four values: the signal, ; which is either 'hit, 'miss or 'abort, the new set of clauses, in this ; case nil, the ttree for whatever action we take, and the new ; value of pspv (containing the new pool). ; Warning: Generally speaking, this function either 'HITs or 'ABORTs. ; But it is here that we look out for :DO-NOT-INDUCT name hints. For ; such hints we want to act like a :BY name-clause-id was present for ; the clause. But we don't know the clause-id and the :BY handling is ; so complicated we don't want to reproduce it. So what we do instead ; is 'MISS and let the waterfall fall off the ledge to the nil ledge. ; See waterfall0. This function should NEVER return a 'MISS unless ; there is a :DO-NOT-INDUCT name hint present in the hint-settings, ; since waterfall0 assumes that it falls off the ledge only in that ; case. (declare (ignorable state wrld)) ; actually ignored in #-acl2-par (prog2$ ; Every branch of the cond below, with the exception of when cl is null, ; results in an ACL2(p) key checkpoint. As such, it is reasonable to print the ; checkpoint at the very beginning of this function. ; Acl2p-push-clause-printing contains code that handles the case where cl is ; nil. ; Parallelism blemish: create a :doc topic on ACL2(p) checkpoints and reference ; it in the above comment. (parallel-only@par (acl2p-push-clause-printing cl hist pspv wrld state)) (let ((pool (access prove-spec-var pspv :pool)) (do-not-induct-hint-val (cdr (assoc-eq :do-not-induct (access prove-spec-var pspv :hint-settings))))) (cond ((null cl) ; The empty clause was produced. Stop the waterfall by aborting. Produce the ; ttree that explains the abort. Drop the clause set containing the empty ; clause into the pool so that when we look for the next goal we see it and ; quit. (mv 'abort nil (add-to-tag-tree! 'abort-cause 'empty-clause nil) (change prove-spec-var pspv :pool (cons (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set '(nil) :hint-settings nil) pool)))) ((and (or (and (not (access prove-spec-var pspv :otf-flg)) (eq do-not-induct-hint-val t)) (eq do-not-induct-hint-val :otf-flg-override)) (not (assoc-eq :induct (access prove-spec-var pspv :hint-settings)))) ; We need induction but can't use it. Stop the waterfall by aborting. Produce ; the ttree that expains the abort. Drop the clause set containing the empty ; clause into the pool so that when we look for the next goal we see it and ; quit. Note that if :otf-flg is specified, then we skip this case because we ; do not want to quit just yet. We will see the :do-not-induct value again in ; prove-loop1 when we return to the goal we are pushing. (mv 'abort nil (add-to-tag-tree! 'abort-cause (if (eq do-not-induct-hint-val :otf-flg-override) 'do-not-induct-otf-flg-override 'do-not-induct) nil) (change prove-spec-var pspv :pool (cons (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set '(nil) :hint-settings nil) pool)))) ((and do-not-induct-hint-val (not (member-eq do-not-induct-hint-val '(t :otf :otf-flg-override))) (not (assoc-eq :induct (access prove-spec-var pspv :hint-settings)))) ; In this case, we have seen a :DO-NOT-INDUCT name hint (where name isn't t) ; that is not overridden by an :INDUCT hint. We would like to give this clause ; a :BY. We can't do it here, as explained above. So we will 'MISS instead. (mv 'miss nil nil nil)) ((and (not (access prove-spec-var pspv :otf-flg)) (not (eq do-not-induct-hint-val :otf)) (or (and (null pool) ;(a) (more-than-simplifiedp hist) (not (assoc-eq :induct (access prove-spec-var pspv :hint-settings)))) (and pool ;(b) (not (assoc-eq 'being-proved-by-induction pool)) (not (assoc-eq :induct (access prove-spec-var pspv :hint-settings)))))) ; We have not been told to press Onward Thru the Fog and ; either (a) this is the first time we've ever pushed anything and we have ; applied processes other than simplification to it and we have not been ; explicitly instructed to induct for this formula, or (b) we have already put ; at least one goal into the pool but we have not yet done our first induction ; and we are not being explicitly instructed to induct for this formula. ; Stop the waterfall by aborting. Produce the ttree explaining the abort. ; Drop the clausification of the user's input into the pool in place of ; everything else in the pool. ; Note: We once reverted to the output of preprocess-clause in prove. However, ; preprocess (and clausify-input) applies unconditional :REWRITE rules and we ; want users to be able to type exactly what the system should go into ; induction on. The theorem that preprocess-clause screwed us on was HACK1. ; It screwed us by distributing * and GCD. (mv 'abort nil (add-to-tag-tree! 'abort-cause 'revert nil) (change prove-spec-var pspv ; Before Version_2.6 we did not modify the tag-tree here. The result was that ; assumptions created by forcing before reverting to the original goal still ; generated forcing rounds after the subsequent proof by induction. When this ; bug was discovered we added code below to use delete-assumptions to remove ; assumptions from the tag-tree. Note that we are not modifying the ; 'accumulated-ttree in state, so these assumptions still reside there; but ; since that ttree is only used for reporting rules used and is intended to ; reflect the entire proof attempt, this decision seems reasonable. ; Version_2.6 was released on November 29, 2001. On January 18, 2002, we ; received email from Francisco J. Martin-Mateos reporting a soundness bug, ; with an example that is included after the definition of push-clause. The ; problem turned out to be that we did not remove :use and :by tagged values ; from the tag-tree here. The result was that if the early part of a ; successful proof attempt had involved a :use or :by hint but then the early ; part was thrown away and we reverted to the original goal, the :use or :by ; tagged value remained in the tag-tree. When the proof ultimately succeeded, ; this tagged value was used to update (global-val ; 'proved-functional-instances-alist (w state)), which records proved ; constraints so that subsequent proofs can avoid proving them again. But ; because the prover reverted to the original goal rather than taking advantage ; of the :use hint, those constraints were not actually proved in this case and ; might not be valid! ; So, we have decided that rather than remove assumptions and :by/:use tags ; from the :tag-tree of pspv, we would just replace that tag-tree by the empty ; tag-tree. We do not want to get burned by a third such problem! :tag-tree nil :pool (list (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set ; At one time we clausified here. But some experiments suggested that the ; prover can perhaps do better by simply doing its thing on each induction ; goal, starting at the top of the waterfall. So, now we pass the same clause ; to induction as it would get if there were a hint of the form ("Goal" :induct ; term), where term is the user-supplied-term. (list (list (access prove-spec-var pspv :user-supplied-term))) ; Below we set the :hint-settings for the input clause, doing exactly what ; find-applicable-hint-settings does. Unfortunately, we haven't defined that ; function yet. Fortunately, it's just a simple assoc-equal. In addition, ; that function goes on to compute a second value we don't need here. So ; rather than go to the bother of moving its definition up to here we just open ; code the part we need. We also remove top-level hints that were supposed to ; apply before we got to push-clause. :hint-settings (delete-assoc-eq-lst (cons ':reorder *top-hint-keywords*) ; We could also delete :induct, but we know it's not here! (cdr (assoc-equal *initial-clause-id* (access prove-spec-var pspv :orig-hints))))))))) #+acl2-par ((and (serial-first-form-parallel-second-form@par nil t) (not (access prove-spec-var pspv :otf-flg)) (not (eq do-not-induct-hint-val :otf)) (null pool) ;; (not (more-than-simplifiedp hist)) ; implicit to the cond (not (assoc-eq :induct (access prove-spec-var pspv :hint-settings)))) (mv 'hit nil (add-to-tag-tree! 'abort-cause 'maybe-revert nil) (change prove-spec-var pspv ; Parallelism blemish: there may be a bug in ACL2(p) related to the comment ; above (in this function's definition) that starts with "Before Version_2.6 we ; did not modify the tag-tree here." To fix this (likely) bug, don't reset the ; tag-tree here -- just remove the ":tag-tree nil" -- and instead do it when we ; convert a maybe-to-be-proved-by-induction to a to-be-proved-by-induction. :tag-tree nil :pool (append (list (list 'maybe-to-be-proved-by-induction (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set (list cl) :hint-settings (access prove-spec-var pspv :hint-settings)) (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set ; See above comment that starts with "At one time we clausified here." (list (list (access prove-spec-var pspv :user-supplied-term))) ; See above comment that starts with "Below we set the :hint-settings for..." :hint-settings (delete-assoc-eq-lst (cons ':reorder *top-hint-keywords*) ; We could also delete :induct, but we know it's not here! (cdr (assoc-equal *initial-clause-id* (access prove-spec-var pspv :orig-hints))))))) pool)))) (t (mv 'hit nil nil (change prove-spec-var pspv :pool (cons (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set (list cl) :hint-settings (access prove-spec-var pspv :hint-settings)) pool)))))))) ; Below is the soundness bug example reported by Francisco J. Martin-Mateos. ; ;;;============================================================================ ; ; ;;; ; ;;; A bug in ACL2 (2.5 and 2.6). Proving "0=1". ; ;;; Francisco J. Martin-Mateos ; ;;; email: Francisco-Jesus.Martin@cs.us.es ; ;;; Dpt. of Computer Science and Artificial Intelligence ; ;;; University of SEVILLE ; ;;; ; ;;;============================================================================ ; ; ;;; I've found a bug in ACL2 (2.5 and 2.6). The following events prove that ; ;;; "0=1". ; ; (in-package "ACL2") ; ; (encapsulate ; (((g1) => *)) ; ; (local ; (defun g1 () ; 0)) ; ; (defthm 0=g1 ; (equal 0 (g1)) ; :rule-classes nil)) ; ; (defun g1-lst (lst) ; (cond ((endp lst) (g1)) ; (t (g1-lst (cdr lst))))) ; ; (defthm g1-lst=g1 ; (equal (g1-lst lst) (g1))) ; ; (encapsulate ; (((f1) => *)) ; ; (local ; (defun f1 () ; 1))) ; ; (defun f1-lst (lst) ; (cond ((endp lst) (f1)) ; (t (f1-lst (cdr lst))))) ; ; (defthm f1-lst=f1 ; (equal (f1-lst lst) (f1)) ; :hints (("Goal" ; :use (:functional-instance g1-lst=g1 ; (g1 f1) ; (g1-lst f1-lst))))) ; ; (defthm 0=f1 ; (equal 0 (f1)) ; :rule-classes nil ; :hints (("Goal" ; :use (:functional-instance 0=g1 ; (g1 f1))))) ; ; (defthm 0=1 ; (equal 0 1) ; :rule-classes nil ; :hints (("Goal" ; :use (:functional-instance 0=f1 ; (f1 (lambda () 1)))))) ; ; ;;; The theorem F1-LST=F1 is not proved via functional instantiation but it ; ;;; can be proved via induction. So, the constraints generated by the ; ;;; functional instantiation hint has not been proved. But when the theorem ; ;;; 0=F1 is considered, the constraints generated in the functional ; ;;; instantiation hint are bypassed because they ".. have been proved when ; ;;; processing the event F1-LST=F1", and the theorem is proved !!!. Finally, ; ;;; an instance of 0=F1 can be used to prove 0=1. ; ; ;;;============================================================================ ; We now develop the functions for reporting what push-clause did. Except, ; pool-lst has already been defined, in support of proof-trees. (defun push-clause-msg1-abort (cl-id ttree pspv state) ; Ttree has exactly one object associated with the tag 'abort-cause. (let ((temp (tagged-object 'abort-cause ttree)) (cl-id-phrase (tilde-@-clause-id-phrase cl-id)) (gag-mode (gag-mode))) (case temp (empty-clause (if gag-mode (msg "A goal of NIL, ~@0, has been generated! Obviously, the ~ proof attempt has failed.~|" cl-id-phrase) "")) ((do-not-induct do-not-induct-otf-flg-override) (msg "Normally we would attempt to prove ~@0 by induction. However, a ~ :DO-NOT-INDUCT hint was supplied to abort the proof attempt.~|" cl-id-phrase (if (eq temp 'do-not-induct) t :otf-flg-override))) (otherwise (msg "Normally we would attempt to prove ~@0 by induction. However, ~ we prefer in this instance to focus on the original input ~ conjecture rather than this simplified special case. We ~ therefore abandon our previous work on this conjecture and ~ reassign the name ~@1 to the original conjecture. (See :DOC ~ otf-flg.)~#2~[~/ [Note: Thanks again for the hint.]~]~|" cl-id-phrase (tilde-@-pool-name-phrase (access clause-id cl-id :forcing-round) (pool-lst (cdr (access prove-spec-var pspv :pool)))) (if (and (not gag-mode) (access prove-spec-var pspv :hint-settings)) 1 0)))))) (defun push-clause-msg1 (cl-id signal clauses ttree pspv state) ; Push-clause was given a clause and produced a signal and ttree. We ; are responsible for printing out an explanation of what happened. ; We look at the ttree to determine what happened. We return state. (declare (ignore clauses)) (cond ((eq signal 'abort) (fms "~@0" (list (cons #\0 (push-clause-msg1-abort cl-id ttree pspv state))) (proofs-co state) state nil)) (t (fms "Name the formula above ~@0.~|" (list (cons #\0 (tilde-@-pool-name-phrase (access clause-id cl-id :forcing-round) (pool-lst (cdr (access prove-spec-var pspv :pool)))))) (proofs-co state) state nil)))) (deflabel otf-flg :doc ":Doc-Section Miscellaneous allow more than one initial subgoal to be pushed for induction~/ The value of this flag is normally ~c[nil]. If you want to prevent the theorem prover from abandoning its initial work upon pushing the second subgoal, set ~c[:otf-flg] to ~c[t].~/ Suppose you submit a conjecture to the theorem prover and the system splits it up into many subgoals. Any subgoal not proved by other methods is eventually set aside for an attempted induction proof. But upon setting aside the second such subgoal, the system chickens out and decides that rather than prove n>1 subgoals inductively, it will abandon its initial work and attempt induction on the originally submitted conjecture. The ~c[:otf-flg] (Onward Thru the Fog) allows you to override this chickening out. When ~c[:otf-flg] is ~c[t], the system will push all the initial subgoals and proceed to try to prove each, independently, by induction. Even when you don't expect induction to be used or to succeed, setting the ~c[:otf-flg] is a good way to force the system to generate and display all the initial subgoals. For ~ilc[defthm] and ~ilc[thm], ~c[:otf-flg] is a keyword argument that is a peer to ~c[:]~ilc[rule-classes] and ~c[:]~ilc[hints]. It may be supplied as in the following examples; also ~pl[defthm]. ~bv[] (thm (my-predicate x y) :rule-classes nil :otf-flg t) (defthm append-assoc (equal (append (append x y) z) (append x (append y z))) :hints ((\"Goal\" :induct t)) :otf-flg t) ~ev[] The ~c[:otf-flg] may be supplied to ~ilc[defun] via the ~ilc[xargs] declare option. When you supply an ~c[:otf-flg] hint to ~c[defun], the flag is effective for the termination proofs and the guard proofs, if any.~/") ; Section: Use and By hints (defun clause-set-subsumes-1 (init-subsumes-count cl-set1 cl-set2 acc) ; We return t if the first set of clauses subsumes the second in the sense that ; for every member of cl-set2 there exists a member of cl-set1 that subsumes ; it. We return '? if we don't know (but this can only happen if ; init-subsumes-count is non-nil); see the comment in subsumes. (cond ((null cl-set2) acc) (t (let ((temp (some-member-subsumes init-subsumes-count cl-set1 (car cl-set2) nil))) (and temp ; thus t or maybe, if init-subsumes-count is non-nil, ? (clause-set-subsumes-1 init-subsumes-count cl-set1 (cdr cl-set2) temp)))))) (defun clause-set-subsumes (init-subsumes-count cl-set1 cl-set2) ; This function is intended to be identical, as a function, to ; clause-set-subsumes-1 (with acc set to t). The first two disjuncts are ; optimizations that may often apply. (or (equal cl-set1 cl-set2) (and cl-set1 cl-set2 (null (cdr cl-set2)) (subsetp-equal (car cl-set1) (car cl-set2))) (clause-set-subsumes-1 init-subsumes-count cl-set1 cl-set2 t))) (defun preprocess-clause? (cl hist pspv wrld state step-limit) (cond ((member-eq 'preprocess-clause (assoc-eq :do-not (access prove-spec-var pspv :hint-settings))) (mv step-limit 'miss nil nil nil)) (t (preprocess-clause cl hist pspv wrld state step-limit)))) (defun apply-use-hint-clauses (temp clauses pspv wrld state step-limit) ; Note: There is no apply-use-hint-clause. We just call this function ; on a singleton list of clauses. ; Temp is the result of assoc-eq :use in a pspv :hint-settings and is ; non-nil. We discuss its shape below. But this function applies the ; given :use hint to each clause in clauses and returns (mv 'hit ; new-clauses ttree new-pspv). ; Temp is of the form (:USE lmi-lst (hyp1 ... hypn) constraint-cl k ; event-names new-entries) where each hypi is a theorem and ; constraint-cl is a clause that expresses the conjunction of all k ; constraints. Lmi-lst is the list of lmis that generated these hyps. ; Constraint-cl is (probably) of the form {(if constr1 (if constr2 ... ; (if constrk t nil)... nil) nil)}. We add each hypi as a hypothesis ; to each goal clause, cl, and in addition, create one new goal for ; each constraint. Note that we discard the extended goal clause if ; it is a tautology. Note too that the constraints generated by the ; production of the hyps are conjoined into a single clause in temp. ; But we hit that constraint-cl with preprocess-clause to pick out its ; (non-tautologial) cases and that code will readily unpack the if ; structure of a typical conjunct. We remove the :use hint from the ; hint-settings so we don't fire the same :use again on the subgoals. ; We return (mv new-step-limit 'hit new-clauses ttree new-pspv). ; The ttree returned has at most two tags. The first is :use and has ; ((lmi-lst hyps constraint-cl k event-names new-entries) ; . non-tautp-applications) as its value, where non-tautp-applications ; is the number of non-tautologous clauses we got by adding the hypi ; to each clause. However, it is possible the :use tag is not ; present: if clauses is nil, we don't report a :use. The optional ; second tag is the ttree produced by preprocess-clause on the ; constraint-cl. If the preprocess-clause is to be hidden anyway, we ; ignore its tree (but use its clauses). (let* ((hyps (caddr temp)) (constraint-cl (cadddr temp)) (new-pspv (change prove-spec-var pspv :hint-settings (remove1-equal temp (access prove-spec-var pspv :hint-settings)))) (A (disjoin-clause-segment-to-clause-set (dumb-negate-lit-lst hyps) clauses)) (non-tautp-applications (length A))) ; In this treatment, the final set of goal clauses will the union of ; sets A and C. A stands for the "application clauses" (obtained by ; adding the use hyps to each clause) and C stands for the "constraint ; clauses." Non-tautp-applications is |A|. (cond ((null clauses) ; In this case, there is no point in generating the constraints! We ; anticipate this happening if the user provides both a :use and a ; :cases hint and the :cases hint (which is applied first) proves the ; goal completely. If that were to happen, clauses would be output of ; the :cases hint and pspv would be its output pspv, from which the ; :cases had been deleted. So we just delete the :use hint from that ; pspv and call it quits, without reporting a :use hint at all. (mv step-limit 'hit nil nil new-pspv)) (t (sl-let (signal C ttree irrel-pspv) (preprocess-clause? constraint-cl nil pspv wrld state step-limit) (declare (ignore irrel-pspv)) (cond ((eq signal 'miss) (mv step-limit 'hit (conjoin-clause-sets A (conjoin-clause-to-clause-set constraint-cl nil)) (add-to-tag-tree! :use (cons (cdr temp) non-tautp-applications) nil) new-pspv)) ((or (tag-tree-occur 'hidden-clause t ttree) (and C (null (cdr C)) (equal (list (prettyify-clause (car C) (let*-abstractionp state) wrld)) constraint-cl))) (mv step-limit 'hit (conjoin-clause-sets A C) (add-to-tag-tree! :use (cons (cdr temp) non-tautp-applications) nil) new-pspv)) (t (mv step-limit 'hit (conjoin-clause-sets A C) (add-to-tag-tree! :use (cons (cdr temp) non-tautp-applications) (add-to-tag-tree! 'preprocess-ttree ttree nil)) new-pspv)))))))) (defun apply-cases-hint-clause (temp cl pspv wrld) ; Temp is the value associated with :cases in a pspv :hint-settings ; and is non-nil. It is thus of the form (:cases term1 ... termn). ; For each termi we create a new clause by adding its negation to the ; goal clause, cl, and in addition, we create a final goal by adding ; all termi. As with a :use hint, we remove the :cases hint from the ; hint-settings so that the waterfall doesn't loop! ; We return (mv 'hit new-clauses ttree new-pspv). (let ((new-clauses (remove-trivial-clauses (conjoin-clause-to-clause-set (disjoin-clauses (cdr temp) cl) (split-on-assumptions ; We reverse the term-list so the user can see goals corresponding to the ; order of the terms supplied. (dumb-negate-lit-lst (reverse (cdr temp))) cl nil)) wrld))) (mv 'hit new-clauses (add-to-tag-tree! :cases (cons (cdr temp) new-clauses) nil) (change prove-spec-var pspv :hint-settings (remove1-equal temp (access prove-spec-var pspv :hint-settings)))))) (defun term-list-listp (l w) (declare (xargs :guard t)) (if (atom l) (equal l nil) (and (term-listp (car l) w) (term-list-listp (cdr l) w)))) (defun non-term-listp-msg (x w) ; Perhaps ~Y01 should be ~y below. If someone complains about a large term ; being printed, consider making that change. (declare (xargs :guard t)) (cond ((atom x) (assert$ x "that fails to satisfy true-listp.")) ((not (termp (car x) w)) (msg "that contains the following non-termp (see :DOC term):~|~% ~Y01" (car x) nil)) (t (non-term-listp-msg (cdr x) w)))) (defun non-term-list-listp-msg (l w) ; Perhaps ~Y01 should be ~y below. If someone complains about a large term ; being printed, consider making that change. (declare (xargs :guard t)) (cond ((atom l) (assert$ l "which fails to satisfy true-listp.")) ((not (term-listp (car l) w)) (msg "which has a member~|~% ~Y01~|~%~@2" (car l) nil (non-term-listp-msg (car l) w))) (t (non-term-list-listp-msg (cdr l) w)))) (defun eval-clause-processor (clause term stobjs-out ctx state) ; Should we do our evaluation in safe-mode? For a relevant discussion, see the ; comment in protected-eval about safe-mode. ; Keep in sync with eval-clause-processor@par. (revert-world-on-error (let ((original-wrld (w state)) (cl-term (subst-var (kwote clause) 'clause term))) (protect-system-state-globals (pprogn (mv-let (erp trans-result state) (ev-for-trans-eval cl-term (all-vars cl-term) stobjs-out ctx state ; See chk-evaluator-use-in-rule for a discussion of how we restrict the use of ; evaluators in rules of class :meta or :clause-processor, so that we can use ; aok = t here. t) (cond (erp (mv (msg "Evaluation failed for the :clause-processor hint.") nil state)) (t (assert$ (equal (car trans-result) stobjs-out) (let* ((result (cdr trans-result)) (eval-erp (and (cdr stobjs-out) (car result))) (val (if (cdr stobjs-out) (cadr result) result))) (cond ((stringp eval-erp) (mv (msg "~s0" eval-erp) nil state)) ((tilde-@p eval-erp) ; a message (mv (msg "Error in clause-processor hint:~|~% ~@0" eval-erp) nil state)) (eval-erp (mv (msg "Error in clause-processor hint:~|~% ~Y01" term nil) nil state)) (t (pprogn (set-w! original-wrld state) (cond ((not (term-list-listp val original-wrld)) (mv (msg "The :CLAUSE-PROCESSOR hint~|~% ~ ~Y01~%did not evaluate to a list ~ of clauses, but instead to~|~% ~ ~Y23~%~@4" term nil val nil (non-term-list-listp-msg val original-wrld)) nil state)) (t (value val)))))))))))))))) #+acl2-par (defun eval-clause-processor@par (clause term stobjs-out ctx state) ; Keep in sync with eval-clause-processor. (cond ((and ; Note that potential conjunct (f-get-global 'waterfall-parallelism state) is ; not needed, since we are in an @par definition. Also note that a ; clause-processor returns (as per :doc clause-processor) either a list cl-list ; of clauses, or else (mv erp cl-list st_i1 ... st_in) where erp and cl-list ; are not stobjs and the st_ik are all stobjs. Since we want to rule out ; stobjs, we therefore check that stobjs-out is (nil) or (nil nil). (not (or (equal stobjs-out '(nil)) (equal stobjs-out '(nil nil)))) (not (cdr (assoc-eq 'hacks-enabled (table-alist 'waterfall-parallelism-table (w state)))))) (mv (msg "Clause-processors that do not return exactly one or two values are ~ not officially supported when waterfall parallelism is enabled. If ~ you wish to use such a clause-processor anyway, you can call ~x0. ~ See :DOC set-waterfall-parallelism-hacks-enabled for more ~ information. " '(set-waterfall-parallelism-hacks-enabled t)) nil)) (t (let ((wrld (w state)) (cl-term (subst-var (kwote clause) 'clause term))) (mv-let (erp trans-result) ; Parallelism blemish: we could consider making an ev@par, which calls ev-w, ; and tests that the appropriate preconditions for ev-w are upheld (like state ; not being in the alist). (ev-w-for-trans-eval cl-term (all-vars cl-term) stobjs-out ctx state ; See chk-evaluator-use-in-rule for a discussion of how we restrict the use of ; evaluators in rules of class :meta or :clause-processor, so that we can use ; aok = t here. t) (cond (erp (mv (msg "Evaluation failed for the :clause-processor hint.") nil)) (t (assert$ (equal (car trans-result) stobjs-out) (let* ((result (cdr trans-result)) (eval-erp (and (cdr stobjs-out) (car result))) (val (if (cdr stobjs-out) (cadr result) result))) (cond ((stringp eval-erp) (mv (msg "~s0" eval-erp) nil)) ((tilde-@p eval-erp) ; a message (mv (msg "Error in clause-processor hint:~|~% ~@0" eval-erp) nil)) (eval-erp (mv (msg "Error in clause-processor hint:~|~% ~Y01" term nil) nil)) (t (cond ((not (term-list-listp val wrld)) (mv (msg "The :CLAUSE-PROCESSOR hint~|~% ~ ~Y01~%did not evaluate to a list of ~ clauses, but instead to~|~% ~Y23~%~@4" term nil val nil (non-term-list-listp-msg val wrld)) nil)) (t (value@par val)))))))))))))) (defun apply-top-hints-clause1 (temp cl-id cl pspv wrld state step-limit) ; See apply-top-hints-clause. This handles the case that we found a ; hint-setting, temp, for a top hint other than :clause-processor or :or. (case (car temp) (:use ; temp is a non-nil :use hint. (let ((cases-temp (assoc-eq :cases (access prove-spec-var pspv :hint-settings)))) (cond ((null cases-temp) (apply-use-hint-clauses temp (list cl) pspv wrld state step-limit)) (t ; In this case, we have both :use and :cases hints. Our ; interpretation of this is that we split clause cl according to the ; :cases and then apply the :use hint to each case. By the way, we ; don't have to consider the possibility of our having a :use and :by ; or :bdd. That is ruled out by translate-hints. (mv-let (signal cases-clauses cases-ttree cases-pspv) (apply-cases-hint-clause cases-temp cl pspv wrld) (declare (ignore signal)) ; We know the signal is 'HIT. (sl-let (signal use-clauses use-ttree use-pspv) (apply-use-hint-clauses temp cases-clauses cases-pspv wrld state step-limit) (declare (ignore signal)) ; Despite the names, use-clauses and use-pspv both reflect the work we ; did for cases. However, use-ttree was built from scratch as was ; cases-ttree and we must combine them. (mv step-limit 'HIT use-clauses (cons-tag-trees use-ttree cases-ttree) use-pspv))))))) (:by ; If there is a :by hint then it is of one of the two forms (:by . name) or ; (:by lmi-lst thm constraint-cl k event-names new-entries). The first form ; indicates that we are to give this clause a bye and let the proof fail late. ; The second form indicates that the clause is supposed to be subsumed by thm, ; viewed as a set of clauses, but that we have to prove constraint-cl to obtain ; thm and that constraint-cl is really a conjunction of k constraints. Lmi-lst ; is a singleton list containing the lmi that generated this thm-cl. (cond ((symbolp (cdr temp)) ; So this is of the first form, (:by . name). We want the proof to fail, but ; not now. So we act as though we proved cl (we hit, produce no new clauses ; and don't change the pspv) but we return a tag-tree containing the tag ; :bye with the value (name . cl). At the end of the proof we must search ; the tag-tree and see if there are any :byes in it. If so, the proof failed ; and we should display the named clauses. (mv step-limit 'hit nil (add-to-tag-tree! :bye (cons (cdr temp) cl) nil) pspv)) (t (let ((lmi-lst (cadr temp)) ; a singleton list (thm (remove-guard-holders ; We often remove guard-holders without tracking their use in the tag-tree. ; Other times we record their use (but not here). This is analogous to our ; reporting of the use of (:DEFINITION NOT) in some cases but not in others ; (e.g., when we use strip-not). (caddr temp))) (constraint-cl (cadddr temp)) (sr-limit (car (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :case-split-limitations))) (new-pspv (change prove-spec-var pspv :hint-settings (remove1-equal temp (access prove-spec-var pspv :hint-settings))))) ; We remove the :by from the hint-settings. Why do we remove the :by? ; If we don't the subgoals we create from constraint-cl will also see ; the :by! ; We insist that thm-cl-set subsume cl -- more precisely, that cl be ; subsumed by some member of thm-cl-set. ; WARNING: See the warning about the processing in translate-by-hint. (let* ((cl (remove-guard-holders-lst cl)) (cl (remove-equal *nil* cl)) (easy-winp (cond ((null cl) ; very weird case! (equal thm *nil*)) ((null (cdr cl)) (equal (car cl) thm)) (t (equal thm (implicate (conjoin (dumb-negate-lit-lst (butlast cl 1))) (car (last cl))))))) (cl1 (if (and (not easy-winp) (ffnnamep-lst 'implies cl)) (expand-some-non-rec-fns-lst '(implies) cl wrld) cl)) (cl-set (if (not easy-winp) ; Before Version_2.7 we only called clausify here when (and (null hist) cl1 ; (null (cdr cl1))). But Robert Krug sent an example in which a :by hint was ; given on a subgoal that had been produced from "Goal" by destructor ; elimination. That subgoal was identical to the theorem given in the :by ; hint, and hence easy-winp is true; but before Version_2.7 we did not look for ; the easy win. So, what happened was that thm-cl-set was the result of ; clausifying the theorem given in the :by hint, but cl-set was a singleton ; containing cl1, which still has IF terms. (clausify (disjoin cl1) nil t sr-limit) (list cl1))) (thm-cl-set (if easy-winp (list (list thm)) ; WARNING: Below we process the thm obtained from the lmi. In particular, we ; expand certain non-rec fns and we clausify it. For heuristic sanity, the ; processing done here should exactly duplicate that done above for cl-set. ; The reason is that we want it to be the case that if the user gives a :by ; hint that is identical to the goal theorem, the subsumption is guaranteed to ; succeed. If the processing of the goal theorem is slightly different than ; the processing of the hint, that guarantee is invalid. (clausify (expand-some-non-rec-fns '(implies) thm wrld) nil t sr-limit))) (val (list* (cadr temp) thm-cl-set (cdddr temp))) (subsumes (and (not easy-winp) ; otherwise we don't care (clause-set-subsumes nil ; We supply nil just above, rather than (say) *init-subsumes-count*, because ; the user will be able to see that if the subsumption check goes out to lunch ; then it must be because of the :by hint. For example, it takes 167,997,825 ; calls of one-way-unify1 (more than 2^27, not far from the fixnum limit in ; many Lisps) to do the subsumption check for the following, yet in a feasible ; time (26 seconds on Allegro CL 7.0, on a 2.6GH Pentium 4). So we prefer not ; to set a limit. ; (defstub p (x) t) ; (defstub s (x1 x2 x3 x4 x5 x6 x7 x8) t) ; (defaxiom ax ; (implies (and (p x1) (p x2) (p x3) (p x4) ; (p x5) (p x6) (p x7) (p x8)) ; (s x1 x2 x3 x4 x5 x6 x7 x8)) ; :rule-classes nil) ; (defthm prop ; (implies (and (p x1) (p x2) (p x3) (p x4) ; (p x5) (p x6) (p x7) (p x8)) ; (s x8 x7 x3 x4 x5 x6 x1 x2)) ; :hints (("Goal" :by ax))) thm-cl-set cl-set))) (success (or easy-winp subsumes))) ; Before the full-blown subsumption check we ask if the two sets are identical ; and also if they are each singleton sets and the thm-cl-set's clause is a ; subset of the other clause. These are fast and commonly successful checks. (cond (success ; Ok! We won! To produce constraint-cl as our goal we first ; preprocess it as though it had come down from the top. See the ; handling of :use hints below for some comments on this. This code ; was copied from that historically older code. (sl-let (signal clauses ttree irrel-pspv) (preprocess-clause? constraint-cl nil pspv wrld state step-limit) (declare (ignore irrel-pspv)) (cond ((eq signal 'miss) (mv step-limit 'hit (conjoin-clause-to-clause-set constraint-cl nil) (add-to-tag-tree! :by val nil) new-pspv)) ((or (tag-tree-occur 'hidden-clause t ttree) (and clauses (null (cdr clauses)) (equal (list (prettyify-clause (car clauses) (let*-abstractionp state) wrld)) constraint-cl))) ; If preprocessing produced a single clause that prettyifies to the ; clause we had, then act as though it didn't do anything (but use its ; output clause set). This is akin to the 'hidden-clause ; hack of preprocess-clause, which, however, is intimately tied to the ; displayed-goal input to prove and not to the input to prettyify- ; clause. We look for the 'hidden-clause tag just in case. (mv step-limit 'hit clauses (add-to-tag-tree! :by val nil) new-pspv)) (t (mv step-limit 'hit clauses (add-to-tag-tree! :by val (add-to-tag-tree! 'preprocess-ttree ttree nil)) new-pspv))))) (t (mv step-limit 'error (msg "When a :by hint is used to supply a lemma-instance ~ for a given goal-spec, the formula denoted by the ~ lemma-instance must subsume the goal. This did not ~ happen~@1! The lemma-instance provided was ~x0, ~ which denotes the formula ~p2 (when converted to a ~ set of clauses and then printed as a formula). This ~ formula was not found to subsume the goal clause, ~ ~p3.~|~%Consider a :use hint instead ; see :DOC ~ hints." (car lmi-lst) ; The following is not possible, because we are not putting a limit on the ; number of one-way-unify1 calls in our subsumption check (see above). But we ; leave this code here in case we change our minds on that. (if (eq subsumes '?) " because our subsumption heuristics were unable ~ to decide the question" "") (untranslate thm t wrld) (prettyify-clause-set cl-set (let*-abstractionp state) wrld)) nil nil)))))))) (:cases ; Then there is no :use hint present. See the comment in *top-hint-keywords*. (prepend-step-limit 4 (apply-cases-hint-clause temp cl pspv wrld))) (:bdd (prepend-step-limit 4 (bdd-clause (cdr temp) cl-id cl (change prove-spec-var pspv :hint-settings (remove1-equal temp (access prove-spec-var pspv :hint-settings))) wrld state))) (t (mv step-limit (er hard 'apply-top-hints-clause "Implementation error: Missing case in apply-top-hints-clause.") nil nil nil)))) (defun@par apply-top-hints-clause (cl-id cl hist pspv wrld ctx state step-limit) ; This is a standard clause processor of the waterfall. It is odd in that it ; is a no-op unless there is a :use, :by, :cases, :bdd, :clause-processor, or ; :or hint in the :hint-settings of pspv. If there is, we remove it and apply ; it. By implementing these hints via this special-purpose processor we can ; take advantage of the waterfall's already-provided mechanisms for handling ; multiple clauses and output. ; We return five values. The first is a new step-limit and the sixth is state. ; The second is a signal that is either 'hit, 'miss, or 'error. When the ; signal is 'miss, the remaining three values are irrelevant. When the signal ; is 'error, the third result is a pair of the form (str . alist) which allows ; us to give our caller an error message to print. In this case, the remaining ; two values are irrelevant. When the signal is 'hit, the third result is the ; list of new clauses, the fourth is a ttree that will become that component of ; the history-entry for this process, and the fifth is the modified pspv. ; We need cl-id passed in so that we can store it in the bddnote, in the case ; of a :bdd hint. (declare (ignore hist)) (let ((temp (first-assoc-eq *top-hint-keywords* (access prove-spec-var pspv :hint-settings)))) (cond ((null temp) (mv@par step-limit 'miss nil nil nil state)) ((eq (car temp) :or) ; If there is an :or hint then it is the only hint present and (in the ; translated form found here) it is of the form (:or . ((user-hint1 ; . hint-settings1) ...(user-hintk . hint-settingsk))). We simply signal an ; or-hit and let the waterfall process the hints. We remove the :or hint from ; the :hint-settings of the pspv. (It may be tempting simply to set the ; :hint-settings to nil. But there may be other :hint-settings, say from a ; :do-not hint on a superior clause id.) ; The value, val, tagged with :or in the ttree is of the form (parent-cl-id NIL ; uhs-lst), where the parent-cl-id is the cl-id of the clause to which this :OR ; hint applies, the uhs-lst is the list of dotted pairs (... (user-hinti ; . hint-settingsi)...) and the NIL signifies that no branches have been ; created. Eventually we will replace the NIL in the ttree of each branch by ; an integer i indicating which branch. If that slot is occupied by an integer ; then user-hinti was applied to the corresponding clause. See ; change-or-hit-history-entry. (mv@par step-limit 'or-hit (list cl) (add-to-tag-tree! :or (list cl-id nil (cdr temp)) nil) (change prove-spec-var pspv :hint-settings (delete-assoc-eq :or (access prove-spec-var pspv :hint-settings))) state)) ((eq (car temp) :clause-processor) ; special case as state can be returned ; Temp is of the form (clause-processor-hint . stobjs-out), as returned by ; translate-clause-processor-hint. (mv-let@par (erp new-clauses state) (eval-clause-processor@par cl (access clause-processor-hint (cdr temp) :term) (access clause-processor-hint (cdr temp) :stobjs-out) ctx state) (cond (erp (mv@par step-limit 'error erp nil nil state)) (t (mv@par step-limit 'hit new-clauses (cond ((and new-clauses (null (cdr new-clauses)) (equal (car new-clauses) cl)) (add-to-tag-tree! 'hidden-clause t nil)) (t (add-to-tag-tree! :clause-processor (cons (cdr temp) new-clauses) nil))) (change prove-spec-var pspv :hint-settings (remove1-equal temp (access prove-spec-var pspv :hint-settings))) state))))) (t (sl-let (signal clauses ttree new-pspv) (apply-top-hints-clause1 temp cl-id cl pspv wrld state step-limit) (mv@par step-limit signal clauses ttree new-pspv state)))))) (defun tilde-@-lmi-phrase (lmi-lst k event-names) ; Lmi-lst is a list of lmis. K is the number of constraints we have to ; establish. Event-names is a list of names of events that justify the ; omission of certain proof obligations, because they have already been proved ; on behalf of those events. We return an object suitable for printing via ~@ ; that will print the phrase ; can be derived from ~&0 via instantiation and functional ; instantiation, provided we can establish the ~n1 constraints ; when event-names is nil, or else ; can be derived from ~&0 via instantiation and functional instantiation, ; bypassing constraints that have been proved when processing the events ..., ; [or: instead of ``the events,'' use ``events including'' when there ; is at least one unnamed event involved, such as a verify-guards ; event] ; provided we can establish the remaining ~n1 constraints ; Of course, the phrase is altered appropriately depending on the lmis ; involved. There are two uses of this phrase. When :by reports it ; says "As indicated by the hint, this goal is subsumed by ~x0, which ; CAN BE ...". When :use reports it says "We now add the hypotheses ; indicated by the hint, which CAN BE ...". (let* ((seeds (lmi-seed-lst lmi-lst)) (lemma-names (filter-atoms t seeds)) (thms (filter-atoms nil seeds)) (techs (lmi-techs-lst lmi-lst))) (cond ((null techs) (cond ((null thms) (msg "can be obtained from ~&0" lemma-names)) ((null lemma-names) (msg "can be obtained from the ~ ~#0~[~/constraint~/~n1 constraints~] generated" (zero-one-or-more k) k)) (t (msg "can be obtained from ~&0 and the ~ ~#1~[~/constraint~/~n2 constraints~] ~ generated" lemma-names (zero-one-or-more k) k)))) ((null event-names) (msg "can be derived from ~&0 via ~*1~#2~[~/, provided we can ~ establish the constraint generated~/, provided we can ~ establish the ~n3 constraints generated~]" seeds (list "" "~s*" "~s* and " "~s*, " techs) (zero-one-or-more k) k)) (t (msg "can be derived from ~&0 via ~*1, bypassing constraints that ~ have been proved when processing ~#2~[events ~ including ~/previous events~/the event~#3~[~/s~]~ ~ ~]~&3~#4~[~/, provided we can establish the constraint ~ generated~/, provided we can establish the ~n5 constraints ~ generated~]" seeds (list "" "~s*" "~s* and " "~s*, " techs) ; Recall that an event-name of 0 is really an indication that the event in ; question didn't actually have a name. See install-event. (if (member 0 event-names) (if (cdr event-names) 0 1) 2) (if (member 0 event-names) (remove 0 event-names) event-names) (zero-one-or-more k) k))))) (defun or-hit-msg (gag-mode-only-p cl-id ttree) ; We print the opening part of the :OR disjunction message, in which we alert ; the reader to the coming disjunctive branches. If the signal is 'OR-HIT, ; then clauses just the singleton list contain the same clause the :OR was ; attached to. But ttree contains an :or tag with value (parent-cl-id nil ; ((user-hint1 . hint-settings1)...)) indicating what is to be done to the ; clause. Eventually the nil we be replaced, on each branch, by the number of ; that branch. See change-or-hit-history-entry. The number of branches is the ; length of the third element. The parent-cl-id in the value is the same as ; the cl-id passed in. (let* ((val (tagged-object :or ttree)) (branch-cnt (length (nth 2 val)))) (msg "The :OR hint for ~@0 gives rise to ~n1 disjunctive ~ ~#2~[~/branch~/branches~]. Proving any one of these branches would ~ suffice to prove ~@0. We explore them in turn~#3~[~@4~/~].~%" (tilde-@-clause-id-phrase cl-id) branch-cnt (zero-one-or-more branch-cnt) (if gag-mode-only-p 1 0) ", describing their derivations as we go"))) (defun apply-top-hints-clause-msg1 (signal cl-id clauses speciousp ttree pspv state) ; This function is one of the waterfall-msg subroutines. It has the standard ; arguments of all such functions: the signal, clauses, ttree and pspv produced ; by the given processor, in this case preprocess-clause (except that for bdd ; processing, the ttree comes from bdd-clause, which is similar to ; simplify-clause, which explains why we also pass in the argument speciousp). ; It produces the report for this step. ; Note: signal and pspv are really ignored, but they don't appear to be when ; they are passed to simplify-clause-msg1 below, so we cannot declare them ; ignored here. (cond ((tagged-objectsp :bye ttree) ; The object associated with the :bye tag is (name . cl). We are interested ; only in name here. (fms "But we have been asked to pretend that this goal is ~ subsumed by the yet-to-be-proved ~x0.~|" (list (cons #\0 (car (tagged-object :bye ttree)))) (proofs-co state) state nil)) ((tagged-objectsp :by ttree) (let* ((obj (tagged-object :by ttree)) ; Obj is of the form (lmi-lst thm-cl-set constraint-cl k event-names ; new-entries). (lmi-lst (car obj)) (thm-cl-set (cadr obj)) (k (car (cdddr obj))) (event-names (cadr (cdddr obj))) (ttree (tagged-object 'preprocess-ttree ttree))) (fms "~#0~[But, as~/As~/As~] indicated by the hint, this goal is ~ subsumed by ~x1, which ~@2.~#3~[~/ By ~*4 we reduce the ~ ~#5~[constraint~/~n6 constraints~] to ~#0~[T~/the following ~ conjecture~/the following ~n7 conjectures~].~]~|" (list (cons #\0 (zero-one-or-more clauses)) (cons #\1 (prettyify-clause-set thm-cl-set (let*-abstractionp state) (w state))) (cons #\2 (tilde-@-lmi-phrase lmi-lst k event-names)) (cons #\3 (if (int= k 0) 0 1)) (cons #\4 (tilde-*-preprocess-phrase ttree)) (cons #\5 (if (int= k 1) 0 1)) (cons #\6 k) (cons #\7 (length clauses))) (proofs-co state) state (term-evisc-tuple nil state)))) ((tagged-objectsp :use ttree) (let* ((use-obj (tagged-object :use ttree)) ; The presence of :use indicates that a :use hint was applied to one ; or more clauses to give the output clauses. If there is also a ; :cases tag in the ttree, then the input clause was split into to 2 ; or more cases first and then the :use hint was applied to each. If ; there is no :cases tag, the :use hint was applied to the input ; clause alone. Each application of the :use hint adds literals to ; the target clause(s). This generates a set, A, of ``applications'' ; but A need not be the same length as the set of clauses to which we ; applied the :use hint since some of those applications might be ; tautologies. In addition, the :use hint generated some constraints, ; C. The set of output clauses, say G, is (C U A). But C and A are ; not necessarily disjoint, e.g., some constraints might happen to be ; in A. Once upon a time, we reported on the number of non-A ; constraints, i.e., |C'|, where C' = C\A. Because of the complexity ; of the grammar, we do not reveal to the user all the numbers: how ; many non-tautological cases, how many hypotheses, how many ; non-tautological applications, how many constraints generated, how ; many after preprocessing the constraints, how many overlaps between ; C and A, etc. Instead, we give a fairly generic message. But we ; have left (as comments) the calculation of the key numbers in case ; someday we revisit this. ; The shape of the use-obj, which is the value of the :use tag, is ; ((lmi-lst (hyp1 ...) cl k event-names new-entries) ; . non-tautp-applications) where non-tautp-applications is the number ; of non-tautologies created by the one or more applications of the ; :use hint, i.e., |A|. (But we do not report this.) (lmi-lst (car (car use-obj))) (hyps (cadr (car use-obj))) (k (car (cdddr (car use-obj)))) ;;; |C| (event-names (cadr (cdddr (car use-obj)))) ; (non-tautp-applications (cdr use-obj)) ;;; |A| (preprocess-ttree (tagged-object 'preprocess-ttree ttree)) ; (len-A non-tautp-applications) ;;; |A| (len-G (len clauses)) ;;; |G| (len-C k) ;;; |C| ; (len-C-prime (- len-G len-A)) ;;; |C'| (cases-obj (tagged-object :cases ttree)) ; If there is a cases-obj it means we had a :cases and a :use; the ; form of cases-obj is (splitting-terms . case-clauses), where ; case-clauses is the result of splitting on the literals in ; splitting-terms. We know that case-clauses is non-nil. (Had it ; been nil, no :use would have been reported.) Note that if cases-obj ; is nil, i.e., there was no :cases hint applied, then these next two ; are just nil. But we'll want to ignore them if cases-obj is nil. ; (splitting-terms (car cases-obj)) ; (case-clauses (cdr cases-obj)) ) (fms "~#0~[But we~/We~] ~#x~[split the goal into the cases specified ~ by the :CASES hint and augment each case~/augment the goal~] ~ with the ~#1~[hypothesis~/hypotheses~] provided by the :USE ~ hint. ~#1~[The hypothesis~/These hypotheses~] ~@2~#3~[~/; the ~ constraint~#4~[~/s~] can be simplified using ~*5~]. ~#6~[This ~ reduces the goal to T.~/We are left with the following ~ subgoal.~/We are left with the following ~n7 subgoals.~]~%" (list (cons #\x (if cases-obj 0 1)) (cons #\0 (if (> len-G 0) 1 0)) ;;; |G|>0 (cons #\1 hyps) (cons #\2 (tilde-@-lmi-phrase lmi-lst k event-names)) (cons #\3 (if (> len-C 0) 1 0)) ;;; |C|>0 (cons #\4 (if (> len-C 1) 1 0)) ;;; |C|>1 (cons #\5 (tilde-*-preprocess-phrase preprocess-ttree)) (cons #\6 (if (equal len-G 0) 0 (if (equal len-G 1) 1 2))) (cons #\7 len-G)) (proofs-co state) state (term-evisc-tuple nil state)))) ((tagged-objectsp :cases ttree) (let* ((cases-obj (tagged-object :cases ttree)) ; The cases-obj here is of the form (term-list . new-clauses), where ; new-clauses is the result of splitting on the literals in term-list. ; (splitting-terms (car cases-obj)) (new-clauses (cdr cases-obj))) (cond (new-clauses (fms "We now split the goal into the cases specified by ~ the :CASES hint to produce ~n0 new non-trivial ~ subgoal~#1~[~/s~].~|" (list (cons #\0 (length new-clauses)) (cons #\1 (if (cdr new-clauses) 1 0))) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "But the resulting goals are all true by case reasoning." nil (proofs-co state) state nil))))) ((eq signal 'OR-HIT) (fms "~@0" (list (cons #\0 (or-hit-msg nil cl-id ttree))) (proofs-co state) state nil)) ((tagged-objectsp 'hidden-clause ttree) state) ((tagged-objectsp :clause-processor ttree) (let* ((clause-processor-obj (tagged-object :clause-processor ttree)) ; The clause-processor-obj here is produced by apply-top-hints-clause, and is ; of the form (clause-processor-hint . new-clauses), where new-clauses is the ; result of splitting on the literals in term-list and hint is the translated ; form of a :clause-processor hint. (verified-p-msg (cond ((access clause-processor-hint (car clause-processor-obj) :verified-p) "verified") (t "trusted"))) (new-clauses (cdr clause-processor-obj)) (cl-proc-fn (ffn-symb (access clause-processor-hint (car clause-processor-obj) :term)))) (cond (new-clauses (fms "We now apply the ~@0 :CLAUSE-PROCESSOR function ~x1 to ~ produce ~n2 new subgoal~#3~[~/s~].~|" (list (cons #\0 verified-p-msg) (cons #\1 cl-proc-fn) (cons #\2 (length new-clauses)) (cons #\3 (if (cdr new-clauses) 1 0))) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "But the ~@0 :CLAUSE-PROCESSOR function ~x1 replaces this goal ~ by T.~|" (list (cons #\0 verified-p-msg) (cons #\1 cl-proc-fn)) (proofs-co state) state nil))))) (t ; Normally we expect (tagged-object 'bddnote ttree) in this case, but it is ; possible that forward-chaining after trivial equivalence removal proved ; the clause, without actually resorting to bdd processing. (simplify-clause-msg1 signal cl-id clauses speciousp ttree pspv state)))) (defun previous-process-was-speciousp (hist) ; NOTE: This function has not been called since Version_2.5. However, ; we reference the comment below in a comment in settled-down-clause, ; so for now we keep this comment, if for no other other reason than ; historical. ; Context: We are about to print cl-id and clause in waterfall-msg. ; Then we will print the message associated with the first entry in ; hist, which is the entry for the processor which just hit clause and ; for whom we are reporting. However, if the previous entry in the ; history was specious, then the cl-id and clause were printed when ; the specious hit occurred and we should not reprint them. Thus, our ; job here is to decide whether the previous process in the history ; was specious. ; There are complications though, introduced by the existence of ; settled-down-clause. In the first place, settled-down-clause ALWAYS ; produces a set of clauses containing the input clause and so ought ; to be considered specious every time it hits! We avoid that in ; waterfall-step and never mark a settled-down-clause as specious, so ; we can assoc for them. More problematically, consider the ; possibility that the first simplification -- the one before the ; clause settled down -- was specious. Recall that the ; pre-settled-down-clause simplifications are weak. Thus, it is ; imaginable that after settling down, other simplifications may ; happen and allow a non-specious simplification. Thus, ; settled-down-clause actually does report its "hit" (and thus add its ; mark to the history so as to enable the subsequent simplify-clause ; to pull out the stops) following even specious simplifications. ; Thus, we must be prepared here to see a non-specious ; settled-down-clause which followed a specious simplification. ; Note: It is possible that the first entry on hist is specious. That ; is, if the process on behalf of which we are about to print is in ; fact specious, it is so marked right now in the history. But that ; is irrelevant to our question. We don't care if the current guy ; specious, we want to know if his "predecessor" was. For what it is ; worth, as of this writing, it is thought to be impossible for two ; adjacent history entries to be marked 'SPECIOUS. Only ; simplify-clause, we think, can produce specious hits. Whenever a ; specious simplify-clause occurs, it is treated as a 'miss and we go ; on to the next process, which is not simplify-clause. Note that if ; elim could produce specious 'hits, then we might get two in a row. ; Observe also that it is possible for two successive simplifies to be ; specious, but that they are separated by a non-specious ; settled-down-clause. (Our code doesn't rely on any of this, but it ; is sometimes helpful to be able to read such thoughts later as a ; hint of what we were thinking when we made some terrible coding ; mistake and so this might illuminate some error we're making today.) (cond ((null hist) nil) ((null (cdr hist)) nil) ((consp (access history-entry (cadr hist) :processor)) t) ((and (eq (access history-entry (cadr hist) :processor) 'settled-down-clause) (consp (cddr hist)) (consp (access history-entry (caddr hist) :processor))) t) (t nil))) ; Section: WATERFALL ; The waterfall is a simple finite state machine (whose individual ; state transitions are very complicated). Abstractly, each state ; contains a "processor" and two neighbor states, the "hit" state and ; the "miss" state. Roughly speaking, when we are in a state we apply ; its processor to the input clause and obtain either a "hit" signal ; (and some new clauses) or "miss" signal. We then transit to the ; appropriate state and continue. ; However, the "hit" state for every state is that point in the falls, ; where 'apply-top-hints-clause is the processor. ; apply-top-hints-clause <------------------+ ; | | ; preprocess-clause ----------------------->| ; | | ; simplify-clause ------------------------->| ; | | ; settled-down-clause---------------------->| ; | | ; ... | ; | | ; push-clause ----------------------------->+ ; WARNING: Waterfall1-lst knows that 'preprocess-clause follows ; 'apply-top-hints-clause! ; We therefore represent a state s of the waterfall as a pair whose car ; is the processor for s and whose cdr is the miss state for s. The hit ; state for every state is the constant state below, which includes, by ; successive cdrs, every state below it in the falls. ; Because the word "STATE" has a very different meaning in ACL2 than we have ; been using thus far in this discussion, we refer to the "states" of the ; waterfall as "ledges" and basically name them by the processors on each. (defconst *preprocess-clause-ledge* '(apply-top-hints-clause preprocess-clause simplify-clause settled-down-clause eliminate-destructors-clause fertilize-clause generalize-clause eliminate-irrelevance-clause push-clause)) ; Observe that the cdr of the 'simplify-clause ledge, for example, is the ; 'settled-down-clause ledge, etc. That is, each ledge contains the ; ones below it. ; Note: To add a new processor to the waterfall you must add the ; appropriate entry to the *preprocess-clause-ledge* and redefine ; waterfall-step and waterfall-msg, below. ; If we are on ledge p with input cl and pspv, we apply processor p to ; our input and obtain signal, some cli, and pspv'. If signal is ; 'abort, we stop and return pspv'. If signal indicates a hit, we ; successively process each cli, starting each at the top ledge, and ; accumulating the successive pspvs starting from pspv'. If any cli ; aborts, we abort; otherwise, we return the final pspv. If signal is ; 'miss, we fall to the next lower ledge with cl and pspv. If signal ; is 'error, we return abort and propagate the error message upwards. ; Before we resume development of the waterfall, we introduce functions in ; support of gag-mode. (defmacro initialize-pspv-for-gag-mode (pspv) `(if (gag-mode) (change prove-spec-var ,pspv :gag-state *initial-gag-state*) ,pspv)) ; For debug only: ; (progn ; ; (defun show-gag-info-pushed (pushed state) ; (if (endp pushed) ; state ; (pprogn (let ((cl-id (caar pushed))) ; (fms "~@0 (~@1) pushed for induction.~|" ; (list (cons #\0 (tilde-@-pool-name-phrase ; (access clause-id cl-id :forcing-round) ; (cdar pushed))) ; (cons #\1 (tilde-@-clause-id-phrase cl-id))) ; *standard-co* state nil)) ; (show-gag-info-pushed (cdr pushed) state)))) ; ; (defun show-gag-info (info state) ; (pprogn (fms "~@0:~%~Q12~|~%" ; (list (cons #\0 (tilde-@-clause-id-phrase ; (access gag-info info :clause-id))) ; (cons #\1 (access gag-info info :clause)) ; (cons #\2 nil)) ; *standard-co* state nil) ; (show-gag-info-pushed (access gag-info info :pushed) ; state))) ; ; (defun show-gag-stack (stack state) ; (if (endp stack) ; state ; (pprogn (show-gag-info (car stack) state) ; (show-gag-stack (cdr stack) state)))) ; ; (defun show-gag-state (cl-id gag-state state) ; (let* ((top-stack (access gag-state gag-state :top-stack)) ; (sub-stack (access gag-state gag-state :sub-stack)) ; (clause-id (access gag-state gag-state :active-cl-id)) ; (printed-p (access gag-state gag-state ; :active-printed-p))) ; (pprogn (fms "********** Gag state from handling ~@0 (active ~ ; clause id: ~#1~[~/~@2~])~%" ; (list (cons #\0 (tilde-@-clause-id-phrase cl-id)) ; (cons #\1 (if clause-id 1 0)) ; (cons #\2 (and clause-id (tilde-@-clause-id-phrase ; clause-id)))) ; *standard-co* state nil) ; (fms "****** Top-stack:~%" nil *standard-co* state nil) ; (show-gag-stack top-stack state) ; (fms "****** Sub-stack:~%" nil *standard-co* state nil) ; (show-gag-stack sub-stack state) ; (fms "****** Active-printed-p: ~x0" ; (list (cons #\0 (access gag-state gag-state ; :active-printed-p))) ; *standard-co* state nil) ; (fms "****** Forcep: ~x0" ; (list (cons #\0 (access gag-state gag-state ; :forcep))) ; *standard-co* state nil) ; (fms "******************************~|" nil *standard-co* state ; nil)))) ; ; (defun maybe-show-gag-state (cl-id pspv state) ; (if (and (f-boundp-global 'gag-debug state) ; (f-get-global 'gag-debug state)) ; (show-gag-state cl-id ; (access prove-spec-var pspv :gag-state) ; state) ; state)) ; ) (defun waterfall-update-gag-state (cl-id clause proc signal ttree pspv state) ; We are given a clause-id, cl-id, and a corresponding clause. Processor proc ; has operated on this clause and returned the given signal (either 'abort or a ; hit indicator), ttree, and pspv. We suitably extend the gag-state of ; the pspv and produce a message to print before any normal prover output that ; is allowed under gag-mode. ; Thus, we return (mv gagst msg), where gagst is either nil or a new gag-state ; obtained by updating the :gag-state field of pspv, and msg is a message to be ; printed or else nil. If msg is not nil, then its printer is expected to ; insert a newline before printing msg. (let* ((msg-p (not (output-ignored-p 'prove state))) (gagst0 (access prove-spec-var pspv :gag-state)) (pool-lst (access clause-id cl-id :pool-lst)) (forcing-round (access clause-id cl-id :forcing-round)) (stack (cond (pool-lst (access gag-state gagst0 :sub-stack)) (t (access gag-state gagst0 :top-stack)))) (active-cl-id (access gag-state gagst0 :active-cl-id)) (abort-p (eq signal 'abort)) (push-or-bye-p (or (eq proc 'push-clause) (and (eq proc 'apply-top-hints-clause) (eq signal 'hit) (tagged-objectsp :bye ttree)))) (new-active-p ; true if we are to push a new gag-info frame (and (null active-cl-id) (null (cdr pool-lst)) ; not in a sub-induction (or push-or-bye-p ; even if the next test fails (member-eq proc (f-get-global 'checkpoint-processors state))))) (new-frame (and new-active-p (make gag-info :clause-id cl-id :clause clause :pushed nil))) (new-stack (cond (new-active-p (cons new-frame stack)) (t stack))) (gagst (cond (new-active-p (cond (pool-lst (change gag-state gagst0 :sub-stack new-stack :active-cl-id cl-id)) (t (change gag-state gagst0 :top-stack new-stack :active-cl-id cl-id)))) (t gagst0))) (new-forcep (and (not abort-p) (not (access gag-state gagst :forcep)) (tagged-objectsp 'assumption ttree))) (gagst (cond (new-forcep (change gag-state gagst :forcep t)) (t gagst))) (forcep-msg (and new-forcep msg-p (msg "Forcing Round ~x0 is pending (caused first by ~ ~@1)." (1+ (access clause-id cl-id :forcing-round)) (tilde-@-clause-id-phrase cl-id))))) (cond (push-or-bye-p (let* ((top-ci (assert$ (consp new-stack) (car new-stack))) (old-pushed (access gag-info top-ci :pushed)) (top-goal-p (equal cl-id *initial-clause-id*)) (print-p ; We avoid gag's key checkpoint message if we are in a sub-induction or if we ; are pushing the initial goal for proof by induction. The latter case is ; handled similarly in the call of waterfall1-lst under waterfall. (not (or (access gag-state gagst :active-printed-p) (cdr pool-lst) top-goal-p))) (gagst (cond (print-p (change gag-state gagst :active-printed-p t)) (t gagst))) (top-stack (access gag-state gagst0 :top-stack)) (msg0 (cond ((and print-p msg-p) (assert$ (null old-pushed) (msg "~@0~|~%~@1~|~Q23~|~%" (gag-start-msg (and pool-lst (assert$ (consp top-stack) (access gag-info (car top-stack) :clause-id))) (and pool-lst (tilde-@-pool-name-phrase forcing-round pool-lst))) (tilde-@-clause-id-phrase (access gag-info top-ci :clause-id)) (prettyify-clause (access gag-info top-ci :clause) (let*-abstractionp state) (w state)) (term-evisc-tuple nil state)))) (t nil)))) (cond (abort-p (mv (cond ((equal (tagged-objects 'abort-cause ttree) '(revert)) (change gag-state gagst :abort-stack new-stack)) ((equal (tagged-objects 'abort-cause ttree) '(empty-clause)) (change gag-state gagst :abort-stack 'empty-clause)) (t gagst)) (and msg-p (msg "~@0~@1" (or msg0 "") (push-clause-msg1-abort cl-id ttree pspv state))))) (t (let* ((old-pspv-pool-lst (pool-lst (cdr (access prove-spec-var pspv :pool)))) (newer-stack (and (assert$ (or (cdr pool-lst) ;sub-induction; no active chkpt (equal (access gag-state gagst :active-cl-id) (access gag-info top-ci :clause-id))) (if (eq proc 'push-clause) (cons (change gag-info top-ci :pushed (cons (cons cl-id old-pspv-pool-lst) old-pushed)) (cdr new-stack)) new-stack))))) (mv (cond (pool-lst (change gag-state gagst :sub-stack newer-stack)) (t (change gag-state gagst :top-stack newer-stack))) (and msg-p (or msg0 forcep-msg (gag-mode)) (msg "~@0~#1~[~@2~|~%~/~]~@3" (or msg0 "") (if forcep-msg 0 1) forcep-msg (cond ((null (gag-mode)) "") (t (let ((msg-finish (cond ((or pool-lst ; pushed for sub-induction (null active-cl-id)) ".") (t (msg ":~|~Q01." (prettyify-clause clause (let*-abstractionp state) (w state)) (term-evisc-tuple nil state)))))) (cond ((eq proc 'push-clause) (msg "~@0 (~@1) is pushed for proof by ~ induction~@2" (tilde-@-pool-name-phrase forcing-round old-pspv-pool-lst) (if top-goal-p "the initial Goal, a key checkpoint" (tilde-@-clause-id-phrase cl-id)) msg-finish)) (t (msg "~@0 is subsumed by a goal yet to be ~ proved~@1" (tilde-@-clause-id-phrase cl-id) msg-finish)))))))))))))) (t (assert$ (not abort-p) ; we assume 'abort is handled above (mv (cond ((or new-active-p new-forcep) gagst) (t nil)) forcep-msg)))))) #+acl2-par (defun waterfall-update-gag-state@par (cl-id clause proc signal ttree pspv state) (declare (ignore cl-id clause proc signal ttree pspv state)) ; Parallelism blemish: consider causing an error when the user tries to enable ; gag mode. At the moment I'm unsure of the effects of returning two nils in ; this case. (mv nil nil)) (defun@par record-gag-state (gag-state state) (declare (ignorable gag-state state)) (serial-first-form-parallel-second-form@par (f-put-global 'gag-state gag-state state) nil)) (defun@par gag-state-exiting-cl-id (signal cl-id pspv state) ; If cl-id is the active clause-id for the current gag-state, then we ; deactivate it. We also eliminate the corresponding stack frame, if any, ; provided no goals were pushed for proof by induction. (declare (ignorable signal cl-id pspv state)) (serial-first-form-parallel-second-form@par (let* ((gagst0 (access prove-spec-var pspv :gag-state)) (active-cl-id (access gag-state gagst0 :active-cl-id))) (cond ((equal cl-id active-cl-id) (let* ((pool-lst (access clause-id cl-id :pool-lst)) (stack (cond (pool-lst (access gag-state gagst0 :sub-stack)) (t (access gag-state gagst0 :top-stack)))) (ci (assert$ (consp stack) (car stack))) (current-cl-id (access gag-info ci :clause-id)) (printed-p (access gag-state gagst0 :active-printed-p)) (gagst1 (cond (printed-p (change gag-state gagst0 :active-cl-id nil :active-printed-p nil)) (t (change gag-state gagst0 :active-cl-id nil)))) (gagst2 (cond ((eq signal 'abort) (cond ((equal (tagged-objects 'abort-cause (access prove-spec-var pspv :tag-tree)) '(revert)) (change gag-state gagst1 ; save abort info :active-cl-id nil :active-printed-p nil :forcep nil :sub-stack nil :top-stack (list (make gag-info :clause-id *initial-clause-id* :clause (list ') :pushed (list (cons *initial-clause-id* '(1))))))) (t gagst1))) ((and (equal cl-id current-cl-id) (null (access gag-info ci :pushed))) (cond (pool-lst (change gag-state gagst1 :sub-stack (cdr stack))) (t (change gag-state gagst1 :top-stack (cdr stack))))) (t gagst1)))) (pprogn (record-gag-state gagst2 state) (cond (printed-p (io? prove nil state nil (pprogn (increment-timer 'prove-time state) (cond ((gag-mode) (fms "~@0" (list (cons #\0 *gag-suffix*)) (proofs-co state) state nil)) (t state)) (increment-timer 'print-time state)))) (t state)) (mv (change prove-spec-var pspv :gag-state gagst2) state)))) (t (mv pspv state)))) (mv@par pspv state))) (defun remove-pool-lst-from-gag-state (pool-lst gag-state state) #-acl2-par (declare (ignore state)) (cond #+acl2-par ((f-get-global 'waterfall-parallelism state) ; This function contains an assertion that fails when executing the waterfall ; in parallel. The assertion fails because parallelism mode doesn't save the ; data required to make gag-mode work, and the assertion tests the gag-mode ; state for being in a reasonable condition. ; Based upon a simple test using :mini-proveall, it appears that switching ; gag-mode on and off, and switching between different waterfall parallelism ; modes does not result in a system breakage. (mv nil nil)) (t ; The proof attempt for the induction goal represented by pool-lst has been ; completed. We return two values, (mv gagst cl-id), as follows. Gagst is the ; result of removing pool-lst from the given gag-state. Cl-id is nil unless ; pool-lst represents the final induction goal considered that was generated ; under a key checkpoint, in which case cl-id is the clause-id of that key ; checkpoint. (let* ((sub-stack (access gag-state gag-state :sub-stack)) (stack (or sub-stack (access gag-state gag-state :top-stack)))) (assert$ (consp stack) (let* ((ci (car stack)) (pushed (access gag-info ci :pushed)) (pop-car-p (null (cdr pushed)))) (assert$ (and (consp pushed) (equal (cdar pushed) pool-lst) (not (access gag-state gag-state :active-cl-id))) (let ((new-stack (if pop-car-p (cdr stack) (cons (change gag-info ci :pushed (cdr pushed)) (cdr stack))))) (mv (cond (sub-stack (change gag-state gag-state :sub-stack new-stack)) (t (change gag-state gag-state :top-stack new-stack))) (and pop-car-p (access gag-info ci :clause-id))))))))))) (defun pop-clause-update-gag-state-pop (pool-lsts gag-state msgs msg-p state) ; Pool-lsts is in reverse chronological order. (cond ((endp pool-lsts) (mv gag-state msgs)) (t (mv-let (gag-state msgs) (pop-clause-update-gag-state-pop (cdr pool-lsts) gag-state msgs msg-p state) (mv-let (gagst cl-id) (remove-pool-lst-from-gag-state (car pool-lsts) gag-state state) (mv gagst (if (and msg-p cl-id) (cons (msg "~@0" (tilde-@-clause-id-phrase cl-id)) msgs) msgs))))))) (defun gag-mode-jppl-flg (gag-state) (let ((stack (or (access gag-state gag-state :sub-stack) (access gag-state gag-state :top-stack)))) (cond (stack (let* ((pushed (access gag-info (car stack) :pushed)) (pool-lst (and pushed (cdar pushed)))) ; Notice that pool-lst is nil if pushed is nil, as can happen when we abort due ; to encountering an empty clause. (and (null (cdr pool-lst)) ; sub-induction goal was not printed pool-lst))) (t nil)))) ; That completes basic support for gag-mode. We now resume mainline ; development of the waterfall. ; The waterfall also manages the output, by case switching on the processor. ; The function waterfall-msg1 handles the printing of the formula and the ; output for those processes that hit. (defmacro splitter-output () ":Doc-Section Miscellaneous status for reporting of ~il[splitter] rules~/ ~l[splitter] for a discussion of splitter rules. ~l[set-splitter-output] for how to turn off, or on, the reporting of splitter rules. When splitter-output is off, because either ~c[prove] output is inhibited (~pl[set-inhibit-output-lst]) or ~c[(]~ilc[set-splitter-output]~c[ nil)] has been invoked, then the value of ~c[(splitter-output)] is ~c[nil]. Otherwise, such reporting is on and the value is non-~c[nil].~/~/" `(and (f-get-global 'splitter-output state) (not (member-eq 'prove (f-get-global 'inhibit-output-lst state))))) (defdoc splitter ":Doc-Section Miscellaneous reporting of rules whose application may have caused case splits~/ The application of a rule to a term may cause a goal to simplify to more than one subgoal. A rule with such an application is called a ``splitter''. Here, we explain the output produced for splitters when proof output is enabled (~pl[set-inhibit-output-lst]) and such reporting is turned on (as it is by default) ~-[] that is, when the value of ~c[(]~ilc[splitter-output]~c[)] is true. ~l[set-splitter-output] for how to turn off, or on, the reporting of splitters. Also ~pl[set-case-split-limitations] for information on how to control case splits. We begin by describing three types of splitters.~bq[] ~c[if-intro]: The rule application may have introduced a call of ~c[IF], in the sense discussed at the end below. ~c[case-split]: For the application of a rule with hypothesis of the form ~c[(case-split )], ~c[] did not simplify to true or false. ~c[immed-forced]: For the application of a rule with hypothesis of the form ~c[(force )], ~c[] did not simplify to true or false, where immediate-force-modep is enabled (~pl[immediate-force-modep]).~eq[] These three annotations ~-[] ~c[if-intro], ~c[case-split], and ~c[immed-forced] ~-[] may be used in proof output and summaries for describing rule applications, as discussed below. A fourth annotation, ~c[forced], maybe also be used in proof output to indicate the application of a rule with hypothesis of the form ~c[(force )] when ~c[] did not simplify to true or false, where immediate-force-modep is disabled (~pl[immediate-force-modep]). We don't consider such uses of ~ilc[force] to be splitters, because they do not cause case splits (though they do produce goals to prove after lower-case ``q.e.d.'' is printed); ~pl[force]. There are three kinds of output affected by splitters, illustrated in turn below using examples. ~bq[] (a) During the proof, ~il[gag-mode] off~nl[] (b) During the proof, ~il[gag-mode] on~nl[] (c) Summary ~eq[] Of course, (a) and (b) are skipped if proof output is inhibited, which (c) is skipped if summary output is inhibited; ~pl[set-inhibit-output-lst]. (a) During the proof, ~il[gag-mode] off With ~il[gag-mode] off (or when using ~c[:]~ilc[pso], ~c[:]~ilc[psof], or ~c[:]~ilc[psog]) one normally gets an English commentary. The following output indicates that at least one application of each rule ~c[F] and ~c[G] is of type ~c[if-intro], at least one application of rules ~c[G] and ~c[R1] are of type ~c[case-split], and at least one application of rule ~c[R3] is of type ~c[immed-forced]. If ~il[immediate-force-modep] is off then ``~c[immed-forced]'' would be replaced by ``~c[forced]''. ~bv[] This simplifies, using the :definitions F (if-intro), G (case-split and if-intro) and H and the :rewrite rules R1, R2 (case-split), and R3 (immed-forced), to the following two conjectures. ~ev[] Note that any such printing of ``~c[forced]'' is done even if ~c[(splitter-output)] is false. Such forcing indication is also made when raw proof format is used ~-[] ~pl[set-raw-proof-format] ~-[] but in that case, no indication is made for splitters in the proof output. (b) During the proof, ~il[gag-mode] on With ~il[gag-mode] on the proof output is greatly abbreviated. However, ``Splitter Notes'' are printed so that even with ~il[gag-mode] on, one can get important information to help control large case splits, by disabling splitter rules as appropriate. These are printed at the point when a goal splits into subgoals. Here, for example, is the Splitter Note that corresponds to the output shown in (a) above. It shows the goal whose simplification has produced a split into more than one subgoal, and it shows how many subgoals have been created. ~bv[] Splitter note (see :DOC splitter) for Subgoal *1/2.2.1' (2 subgoals). case-split: ((:DEFINITION G) (:REWRITE R2)) immed-forced: ((:REWRITE R3)) if-intro: ((:DEFINITION G) (:DEFINITION F)) ~ev[] No such splitter notes are printed for the use of ~ilc[force] (when ~il[immediate-force-modep] is off). (c) Summary Here is a possible summary corresponding to our running example. In the summary, ``Splitter rules'' is omitted if there are no splitter rules, and a splitter type is only mentioned if there is at least one corresponding splitter rule. ~bv[] Summary Form: ( THM ...) Rules: ((:DEFINITION F) (:DEFINITION G) (:DEFINITION H) (:REWRITE R1) (:REWRITE R2) (:REWRITE R3)) Splitter rules (see :DOC splitter): case-split: ((:DEFINITION G) (:REWRITE R2)) immed-forced: ((:REWRITE R3)) if-intro: ((:DEFINITION G) (:DEFINITION F)) Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.00) Prover steps counted: 145 ~ev[] No indication for ``~c[forced]'' is given for ``Splitter rules''. (As discussed earlier above, the ~ilc[force]d hypotheses are not considered relevant for determining splitter rule applications unless ~il[immediate-force-modep] is on.) We conclude by giving the criteria for a ~il[rewrite] or ~il[definition] rule application to be a splitter of type ~c[if-intro].~bq[] o Reporting of splitter rules is on, i.e., the value of ~c[(]~ilc[splitter-output]~c[)] is true. o At least two subgoals are created, even before considering subgoals generated by hypotheses that are calls of ~ilc[case-split] or ~ilc[force]. o The term to which the rule is applied is at the top level, rather than being encountered when trying to establish the hypothesis of a rule. o The rule is a ~il[rewrite] rule, a ~il[definition] rule, or a ~il[meta] rule. o There is a call of the function symbol ~c[IF] in the right-hand side of the ~il[rewrite] rule; or, in the case of a ~il[definition] rule, in the body of the definition; or, in the case of a ~il[meta] rule, in the result of applying the metafunction. o There is a call of the function symbol ~c[IF] in the result of rewriting: the right-hand side (for a ~il[rewrite] rule), the definition body (for a ~il[definition] rule), or the metafunction application (for a ~il[meta] rule). ~eq[] Any rule application meeting the above criteria will be considered a splitter of type ~c[if-intro], even if the call does not actually cause a case split. For example, if you are proving ~c[(implies (hyp x) (conc x))] and rule R rewrites ~c[(hyp x)] to ~c[(if (h1 x) (h2 x) nil)], which is really the term ~c[(and (h1 x) (h2 x))], then R may be labelled as a splitter rule. If you want to find the causes of case-splitting, the list of ~c[if-intro] splitters can help you narrow your search, but may include irrelevant rules as well. Finally, note that you may see splits not attributed to splitters. We believe that this will be uncommon during simplification, though it can occur for example when a call of ~c[IF] is in the body of a ~ilc[LET] expression, i.e., in a call of a ~ilc[LAMBDA] expression. But splits caused by other processes, notably destructor elimination (~pl[elim]), will typically not be attributed to splitters.~/~/") (defmacro set-splitter-output (val) ":Doc-Section switches-parameters-and-modes turn on or off reporting of rules that may have caused case splits~/ ~bv[] Examples: (set-splitter-output t) ; enable reports of ``splitter'' rules (default) (set-splitter-output nil) ; disable reports of ``splitter'' rules ~ev[]~/ After evaluation of the form ~c[(set-splitter-output t)] (the default), then whenever ~c[prove] output is not inhibited (~pl[set-inhibit-output-lst]), ACL2 will report ~il[rewrite] and ~il[definition] rules that may have reduced a goal to more than one subgoal. ~l[splitter] for how to interpret such reports. We call such rules ``splitter rules'' for the goal that is being split. This information can be useful in deciding how to eliminate large splits, for example of Goal into Subgoal 1000 through Subgoal 1, by disabling some splitter rules. If you want to avoid the printing of such information, you can put the form ~c[(set-splitter-output t)] in your customization file; ~pl[acl2-customization]. Note that this command does not change the ACL2 ~il[world]; it only modifies the ~il[state]. More precisely, it sets a state global to the indicated value. (~l[state] for discussion of the ``global-table'' of the state.) When ~c[prove] output is enabled (~pl[set-inhibit-output-lst]), the value of that state global is the value of the form ~c[(]~ilc[splitter-output]~c[)]; otherwise the value of that form is ~c[nil]. Again, ~pl[splitter] for the effects of turning on the reporting of splitter rules." `(f-put-global 'splitter-output ,val state)) (defun waterfall-msg1 (processor cl-id signal clauses new-hist msg ttree pspv state) (pprogn ; (maybe-show-gag-state cl-id pspv state) ; debug (cond ; Suppress printing for :OR splits; see also other comments with this header. ; ((and (eq signal 'OR-HIT) ; (gag-mode)) ; (fms "~@0~|~%~@1~|" ; (list (cons #\0 (or msg "")) ; (cons #\1 (or-hit-msg t cl-id ttree))) ; (proofs-co state) state nil)) ((and msg (gag-mode)) (fms "~@0~|" (list (cons #\0 msg)) (proofs-co state) state nil)) (t state)) (cond ((gag-mode) (print-splitter-rules-summary cl-id clauses ttree (proofs-co state) state)) (t (case processor (apply-top-hints-clause ; Note that the args passed to apply-top-hints-clause, and to ; simplify-clause-msg1 below, are nonstandard. This is what allows the ; simplify message to detect and report if the just performed simplification ; was specious. (apply-top-hints-clause-msg1 signal cl-id clauses (consp (access history-entry (car new-hist) :processor)) ttree pspv state)) (preprocess-clause (preprocess-clause-msg1 signal clauses ttree pspv state)) (simplify-clause (simplify-clause-msg1 signal cl-id clauses (consp (access history-entry (car new-hist) :processor)) ttree pspv state)) (settled-down-clause (settled-down-clause-msg1 signal clauses ttree pspv state)) (eliminate-destructors-clause (eliminate-destructors-clause-msg1 signal clauses ttree pspv state)) (fertilize-clause (fertilize-clause-msg1 signal clauses ttree pspv state)) (generalize-clause (generalize-clause-msg1 signal clauses ttree pspv state)) (eliminate-irrelevance-clause (eliminate-irrelevance-clause-msg1 signal clauses ttree pspv state)) (otherwise (push-clause-msg1 cl-id signal clauses ttree pspv state))))))) (defmacro io?-prove-cw (vars body &rest keyword-args) ; This macro is a version of io?-prove that prints to the comment window using ; wormholes. ; Keep in sync with io?-prove. `(io? prove t state ,vars (if (gag-mode) state ,body) ,@keyword-args)) #+acl2-par (defmacro io?-prove@par (&rest rst) ; This macro is the approved way to produce proof output with ; waterfall-parallelism enabled. `(io?-prove-cw ,@rst)) (defun waterfall-print-clause-body (cl-id clause state) (pprogn (increment-timer 'prove-time state) (fms "~@0~|~q1.~|" (list (cons #\0 (tilde-@-clause-id-phrase cl-id)) (cons #\1 (prettyify-clause clause (let*-abstractionp state) (w state)))) (proofs-co state) state (term-evisc-tuple nil state)) (increment-timer 'print-time state))) (defmacro waterfall-print-clause-id-fmt1-call (cl-id) ; Keep in sync with waterfall-print-clause-id-fmt1-call@par. `(mv-let (col state) (fmt1 "~@0~|" (list (cons #\0 (tilde-@-clause-id-phrase ,cl-id))) 0 (proofs-co state) state nil) (declare (ignore col)) state)) #+acl2-par (defmacro waterfall-print-clause-id-fmt1-call@par (cl-id) ; Keep in sync with waterfall-print-clause-id-fmt1-call. `(with-output-lock (mv-let (col state) (fmt1 "~@0~|" (list (cons #\0 (tilde-@-clause-id-phrase ,cl-id))) 0 (proofs-co state) state nil) (declare (ignore col state)) nil))) (defmacro waterfall-print-clause-id (cl-id) `(pprogn (increment-timer 'prove-time state) (waterfall-print-clause-id-fmt1-call ,cl-id) (increment-timer 'print-time state))) #+acl2-par (defmacro waterfall-print-clause-id@par (cl-id) ; Parallelism wart: wormhole printing isn't reliable. (When this wart is ; removed, then remove the references to it in ; unsupported-waterfall-parallelism-features and ; waterfall1-wrapper@par-before.) We lock wormholes at a very high level, so ; we thought they might be thread safe. However, in practice, when we enable ; printing through wormholes, there are problems symptomatic of race ; conditions. We think these problems are related to the ld-special variables. ; Specifically, a thread tries to read from the prompt upon entering the ; wormhole, even if there isn't supposed to be any interaction with the prompt. ; A possible solution to this problem might involve implementing all of the ; ld-specials with global variables (as opposed to propsets), and then ; rebinding those global variables in each worker thread. Long story short: ; wormholes might be thread-safe, but we have lots of reasons to believe they ; aren't. ; Therefore, we have different versions of the present macro for the ; #+acl2-loop-only and #-acl2-loop-only cases. To see why, first note that ; waterfall-print-clause-id-fmt1-call does printing, hence returns state. As ; such, the #+acl2-loop-only code (where state is not available) performs the ; printing inside a wormhole. However, because of the parallelism wart above, ; we avoid the wormhole call in the #-acl2-loop-only case, which is the ; actually executed inside the prover. #+acl2-loop-only `(wormhole 'comment-window-io '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) (list ,cl-id) '(mv-let (col state) (waterfall-print-clause-id-fmt1-call ,cl-id) (declare (ignore col)) (value :q)) :ld-error-action :return! ; might cause problems :ld-verbose nil :ld-pre-eval-print nil :ld-prompt nil) #-acl2-loop-only `(waterfall-print-clause-id-fmt1-call@par ,cl-id)) (defproxy print-clause-id-okp (*) => *) (defun print-clause-id-okp-builtin (cl-id) (declare (ignore cl-id) (xargs :guard (clause-id-p cl-id))) t) (defattach (print-clause-id-okp print-clause-id-okp-builtin) :skip-checks t) (defun@par waterfall-print-clause (suppress-print cl-id clause state) (cond ((or suppress-print (equal cl-id *initial-clause-id*)) (state-mac@par)) ((serial-first-form-parallel-second-form@par nil (member-equal (f-get-global 'waterfall-printing state) '(:limited :very-limited))) (state-mac@par)) (t (pprogn@par (if (and (or (gag-mode) (member-eq 'prove (f-get-global 'inhibit-output-lst state))) (f-get-global 'print-clause-ids state) (print-clause-id-okp cl-id)) (waterfall-print-clause-id@par cl-id) (state-mac@par)) (io?-prove@par (cl-id clause) (waterfall-print-clause-body cl-id clause state)))))) #+acl2-par (defun some-parent-is-checkpointp (hist state) (cond ((endp hist) nil) ((member (access history-entry (car hist) :processor) (f-get-global 'checkpoint-processors state)) t) (t (some-parent-is-checkpointp (cdr hist) state)))) (defun@par waterfall-msg (processor cl-id clause signal clauses new-hist ttree pspv state) ; This function prints the report associated with the given processor on some ; input clause, clause, with output signal, clauses, ttree, and pspv. The code ; below consists of two distinct parts. First we print the message associated ; with the particular processor. Then we return three results: a "jppl-flg", a ; new pspv with the gag-state updated, and the state. ; The jppl-flg is either nil or a pool-lst. When non-nil, the jppl-flg means ; we just pushed a clause into the pool and assigned it the name that is the ; value of the flag. "Jppl" stands for "just pushed pool list". This flag is ; passed through the waterfall and eventually finds its way to the pop-clause ; after the waterfall, where it is used to control the optional printing of the ; popped clause. If the jppl-flg is non-nil when we pop, it means we need not ; re-display the clause because it was just pushed and we can refer to it by ; name. ; This function increments timers. Upon entry, the accumulated time is charged ; to 'prove-time. The time spent in this function is charged to 'print-time. (declare (ignorable new-hist clauses)) (pprogn@par (increment-timer@par 'prove-time state) (serial-only@par (io? proof-tree nil state (pspv signal new-hist clauses processor ttree cl-id) (pprogn (increment-proof-tree cl-id ttree processor (length clauses) new-hist signal pspv state) (increment-timer 'proof-tree-time state)))) (mv-let (gagst msg) (waterfall-update-gag-state@par cl-id clause processor signal ttree pspv state) (declare (ignorable msg)) (mv-let@par (pspv state) (cond (gagst (pprogn@par (record-gag-state@par gagst state) (mv@par (change prove-spec-var pspv :gag-state gagst) state))) (t (mv@par pspv state))) (pprogn@par (serial-first-form-parallel-second-form@par (io? prove nil state (pspv ttree new-hist clauses signal cl-id processor msg) (waterfall-msg1 processor cl-id signal clauses new-hist msg ttree pspv state)) ; Parallelism wart: consider replacing print-splitter-rules-summary below. A ; version of printing that does not involve wormholes will be required. See ; book parallel/proofs/stress-waterfall-parallelism.lsp. Note that it is ; unclear to Rager whether the :limited (or nil) version of waterfall-printing ; should print splitter-rules. :Limited waterfall-printing should probably ; follow whatever gag-mode does. ; We could similarly comment out the :full case just below, since it also uses ; wormholes. But we prefer to leave it, noting that :full is primarily used by ; developers. (cond ((equal (f-get-global 'waterfall-printing state) :full) (io? prove t state (pspv ttree new-hist clauses signal cl-id processor msg) (waterfall-msg1 processor cl-id signal clauses new-hist msg ttree pspv state))) (t 'nothing-to-print ; (io? prove t ; state ; (cl-id ttree clauses) ; (print-splitter-rules-summary ; cl-id clauses ttree (proofs-co state) state)) ))) (increment-timer@par 'print-time state) (mv@par (cond ((eq processor 'push-clause) ; Keep the following in sync with the corresponding call of pool-lst in ; waterfall0-or-hit. See the comment there. (pool-lst (cdr (access prove-spec-var pspv :pool)))) (t nil)) pspv state)))))) ; The waterfall is responsible for storing the ttree produced by each ; processor in the pspv. That is done with: (defun put-ttree-into-pspv (ttree pspv) (change prove-spec-var pspv :tag-tree (cons-tag-trees ttree (access prove-spec-var pspv :tag-tree)))) (defun set-cl-ids-of-assumptions1 (recs cl-id) (cond ((endp recs) nil) (t (cons (change assumption (car recs) :assumnotes (list (change assumnote (car (access assumption (car recs) :assumnotes)) :cl-id cl-id))) (set-cl-ids-of-assumptions1 (cdr recs) cl-id))))) (defun set-cl-ids-of-assumptions (ttree cl-id) ; We scan the tag-tree ttree, looking for 'assumptions. Recall that each has a ; :assumnotes field containing exactly one assumnote record, which contains a ; :cl-id field. We assume that :cl-id field is empty. We put cl-id into it. ; We return a copy of ttree. (let ((recs (tagged-objects 'assumption ttree))) (cond (recs (extend-tag-tree 'assumption (set-cl-ids-of-assumptions1 recs cl-id) (remove-tag-from-tag-tree! 'assumption ttree))) (t ttree)))) ; We now develop the code for proving the assumptions that are forced during ; the first part of the proof. These assumptions are all carried in the ttree ; on 'assumption tags. (Delete-assumptions was originally defined just below ; collect-assumptions, but has been moved up since it is used in push-clause.) (defun collect-assumptions1 (recs only-immediatep ans) (cond ((endp recs) ans) (t (collect-assumptions1 (cdr recs) only-immediatep (cond ((cond ((eq only-immediatep 'non-nil) (access assumption (car recs) :immediatep)) ((eq only-immediatep 'case-split) (eq (access assumption (car recs) :immediatep) 'case-split)) ((eq only-immediatep t) (eq (access assumption (car recs) :immediatep) t)) (t t)) (add-to-set-equal (car recs) ans)) (t ans)))))) (defun collect-assumptions (ttree only-immediatep) ; We collect the assumptions in ttree and accumulate them onto ans. ; Only-immediatep determines exactly which assumptions we collect: ; * 'non-nil -- only collect those with :immediatep /= nil ; * 'case-split -- only collect those with :immediatep = 'case-split ; * t -- only collect those with :immediatep = t ; * nil -- collect ALL assumptions (collect-assumptions1 (tagged-objects 'assumption ttree) only-immediatep nil)) ; We are now concerned with trying to shorten the type-alists used to ; govern assumptions. We have two mechanisms. One is ; ``disguarding,'' the throwing out of any binding whose term ; requires, among its guard clauses, the truth of the term we are ; trying to prove. The second is ``disvaring,'' the throwing out of ; any binding that does not mention any variable linked to term. ; First, disguarding... We must first define the fundamental process ; of generating the guard clauses for a term. This "ought" to be in ; the vicinity of our definition of defun and verify-guards. But we ; need it now. (defun sublis-var-lst-lst (alist clauses) (cond ((null clauses) nil) (t (cons (sublis-var-lst alist (car clauses)) (sublis-var-lst-lst alist (cdr clauses)))))) (defun add-segments-to-clause (clause segments) (cond ((null segments) nil) (t (conjoin-clause-to-clause-set (disjoin-clauses clause (car segments)) (add-segments-to-clause clause (cdr segments)))))) (defun split-initial-extra-info-lits (cl hyps-rev) (cond ((endp cl) (mv hyps-rev cl)) ((extra-info-lit-p (car cl)) (split-initial-extra-info-lits (cdr cl) (cons (car cl) hyps-rev))) (t (mv hyps-rev cl)))) (defun conjoin-clause-to-clause-set-extra-info1 (tags-rev cl0 cl cl-set cl-set-all) ; Roughly speaking, we want to extend cl-set-all by adding cl = (revappend ; tags-rev cl0), where tags-rev is the reversed initial prefix of negated calls ; of *extra-info-fn*. But the situation is a bit more complex: ; Cl is (revappend tags-rev cl0) and cl-set is a tail of cl-set-all. Let cl1 ; be the first member of cl-set, if any, such that removing its initial negated ; calls of *extra-info-fn* yields cl0. We replace the corresponding occurrence ; of cl1 in cl-set-all by the result of adding tags-rev (reversed) in front of ; cl0, except that we drop each tag already in cl1; otherwise we return ; cl-set-all unchanged. If there is no such cl1, then we return the result of ; consing cl on the front of cl-set-all. (cond ((endp cl-set) (cons cl cl-set-all)) (t (mv-let (initial-extra-info-lits-rev cl1) (split-initial-extra-info-lits (car cl-set) nil) (cond ((equal cl0 cl1) (cond ((not tags-rev) ; seems unlikely cl-set-all) (t (cond ((subsetp-equal tags-rev initial-extra-info-lits-rev) cl-set-all) (t (append (take (- (length cl-set-all) (length cl-set)) cl-set-all) (cons (revappend initial-extra-info-lits-rev (mv-let (changedp new-tags-rev) (set-difference-equal-changedp tags-rev initial-extra-info-lits-rev) (cond (changedp (revappend new-tags-rev cl0)) (t cl)))) (cdr cl-set)))))))) (t (conjoin-clause-to-clause-set-extra-info1 tags-rev cl0 cl (cdr cl-set) cl-set-all))))))) (defun conjoin-clause-to-clause-set-extra-info (cl cl-set) ; Cl, as well as each clause in cl-set, may start with zero or more negated ; calls of *extra-info-fn*. Semantically (since *extra-info-fn* always returns ; T), we return the result of conjoining cl to cl-set, as with ; conjoin-clause-to-clause-set. However, we view a prefix of negated ; *extra-info-fn* calls in a clause as a set of tags indicating a source of ; that clause, and we want to preserve that view when we conjoin cl to cl-set. ; In particular, if a member cl1 of cl-set agrees with cl except for the ; prefixes of negated calls of *extra-info-fn*, it is desirable for the merge ; to be achieved simply by adding the prefix of negated calls of ; *extra-info-fn* in cl to the prefix of such terms in cl1. This function ; carries out that desire. (cond ((member-equal *t* cl) cl-set) (t (mv-let (tags-rev cl0) (split-initial-extra-info-lits cl nil) (conjoin-clause-to-clause-set-extra-info1 tags-rev cl0 cl cl-set cl-set))))) (defun conjoin-clause-sets-extra-info (cl-set1 cl-set2) ; Keep in sync with conjoin-clause-sets. ; It is unfortunatel that each clause in cl-set2 is split into a prefix (of ; negated *extra-info-fn* calls) and the rest for EACH member of cl-set1. ; However, we expect the sizes of clause-sets to be relatively modest; ; otherwise presumably the simplifier would choke. So even though we could ; preprocess by splitting cl-set2 into a list of pairs (prefix . rest), for now ; we'll avoid thus complicating the algorithm (which also could perhaps ; generate extra garbage as it reconstitutes cl-set2 from such pairs). (cond ((null cl-set1) cl-set2) (t (conjoin-clause-to-clause-set-extra-info (car cl-set1) (conjoin-clause-sets-extra-info (cdr cl-set1) cl-set2))))) (defun maybe-add-extra-info-lit (debug-info term clause wrld) (cond (debug-info (cons (fcons-term* 'not (fcons-term* *extra-info-fn* (kwote debug-info) (kwote (untranslate term nil wrld)))) clause)) (t clause))) (defun conjoin-clause-sets+ (debug-info cl-set1 cl-set2) (cond (debug-info (conjoin-clause-sets-extra-info cl-set1 cl-set2)) (t (conjoin-clause-sets cl-set1 cl-set2)))) (defconst *equality-aliases* ; This constant should be a subset of *definition-minimal-theory*, since we do ; not track the corresponding runes in simplify-tests and related code below. '(eq eql =)) (defun term-equated-to-constant (term) (case-match term ((rel x y) (cond ((or (eq rel 'equal) (member-eq rel *equality-aliases*)) (cond ((quotep x) (mv y x)) ((quotep y) (mv x y)) (t (mv nil nil)))) (t (mv nil nil)))) (& (mv nil nil)))) (defun simplify-clause-for-term-equal-const-1 (var const cl) ; This is the same as simplify-tests, but where cl is a clause: here we are ; considering their disjunction, rather than the disjunction of their negations ; (i.e., an implication where all elements are considered true). (cond ((endp cl) (mv nil nil)) (t (mv-let (changedp rest) (simplify-clause-for-term-equal-const-1 var const (cdr cl)) (mv-let (var2 const2) (term-equated-to-constant (car cl)) (cond ((and (equal var var2) (not (equal const const2))) (mv t rest)) (changedp (mv t (cons (car cl) rest))) (t (mv nil cl)))))))) (defun simplify-clause-for-term-equal-const (var const cl) ; See simplify-clause-for-term-equal-const. (mv-let (changedp new-cl) (simplify-clause-for-term-equal-const-1 var const cl) (declare (ignore changedp)) new-cl)) (defun add-literal-smart (lit cl at-end-flg) ; This version of add-literal can remove literals from cl that are known to be ; false, given that lit is false. (mv-let (term const) (cond ((and (nvariablep lit) ; (not (fquotep lit)) (eq (ffn-symb lit) 'not)) (term-equated-to-constant (fargn lit 1))) (t (mv nil nil))) (add-literal lit (cond (term (simplify-clause-for-term-equal-const term const cl)) (t cl)) at-end-flg))) (mutual-recursion (defun guard-clauses (term debug-info stobj-optp clause wrld ttree) ; See also guard-clauses+, which is a wrapper for guard-clauses that eliminates ; ground subexpressions. ; We return two results. The first is a set of clauses whose conjunction ; establishes that all of the guards in term are satisfied. The second result ; is a ttree justifying the simplification we do and extending ttree. ; Stobj-optp indicates whether we are to optimize away stobj recognizers. Call ; this with stobj-optp = t only when it is known that the term in question has ; been translated with full enforcement of the stobj rules. Clause is the list ; of accumulated, negated tests passed so far on this branch. It is maintained ; in reverse order, but reversed before we return it. ; We do not add the definition rune for *extra-info-fn* in ttree. The caller ; should be content with failing to report that rune. Prove-guard-clauses is ; ultimately the caller, and is happy not to burden the user with mention of ; that rune. ; Note: Once upon a time, this function took an additional argument, alist, and ; was understood to be generating the guards for term/alist. Alist was used to ; carry the guard generation process into lambdas. (cond ((variablep term) (mv nil ttree)) ((fquotep term) (mv nil ttree)) ((flambda-applicationp term) (mv-let (cl-set1 ttree) (guard-clauses-lst (fargs term) debug-info stobj-optp clause wrld ttree) (mv-let (cl-set2 ttree) (guard-clauses (lambda-body (ffn-symb term)) debug-info stobj-optp ; We pass in the empty clause here, because we do not want it involved in ; wrapping up the lambda term that we are about to create. nil wrld ttree) (let* ((term1 (make-lambda-application (lambda-formals (ffn-symb term)) (termify-clause-set cl-set2) (remove-guard-holders-lst (fargs term)))) (cl (reverse (add-literal-smart term1 clause nil))) (cl-set3 (if (equal cl *true-clause*) cl-set1 (conjoin-clause-sets+ debug-info cl-set1 ; Instead of cl below, we could use (maybe-add-extra-info-lit debug-info term ; cl wrld). But that can cause a large number of lambda (let) terms in the ; output that are probabably more unhelpful (as noise) than helpful. (list cl))))) (mv cl-set3 ttree))))) ((eq (ffn-symb term) 'if) (let ((test (remove-guard-holders (fargn term 1)))) (mv-let (cl-set1 ttree) ; Note: We generate guards from the original test, not the one with guard ; holders removed! (guard-clauses (fargn term 1) debug-info stobj-optp clause wrld ttree) (mv-let (cl-set2 ttree) (guard-clauses (fargn term 2) debug-info stobj-optp ; But the additions we make to the two branches is based on the simplified ; test. (add-literal-smart (dumb-negate-lit test) clause nil) wrld ttree) (mv-let (cl-set3 ttree) (guard-clauses (fargn term 3) debug-info stobj-optp (add-literal-smart test clause nil) wrld ttree) (mv (conjoin-clause-sets+ debug-info cl-set1 (conjoin-clause-sets+ debug-info cl-set2 cl-set3)) ttree)))))) ((eq (ffn-symb term) 'wormhole-eval) ; Because of translate, term is necessarily of the form ; (wormhole-eval ' '(lambda () ) ) ; or ; (wormhole-eval ' '(lambda ( ) ) ) ; the only difference being whether the lambda has one or no formals. The ; of the lambda has been translated despite its occurrence inside a ; quoted lambda. The is always of the form 'NIL or a ; variable symbol or a PROG2$ nest of variable symbols and thus has a guard of ; T. Furthermore, translate insures that the free variables of the lambda are ; those of the . Thus, all the variables we encounter in ; are variables known in the current context, except . By the way, ; ``whs'' stands for ``wormhole status'' and if it is the lambda formal we know ; it occurs in . ; The raw lisp macroexpansion of the first wormhole-eval form above is (modulo ; the name of var) ; (let* (( (cdr (assoc-equal ' *wormhole-status-alist*))) ; (val )) ; (or (equal val) ; (put-assoc-equal ' val *wormhole-status-alist*)) ; nil) ; ; We wish to make sure this form is Common Lisp compliant. We know that ; *wormhole-status-alist* is an alist satisfying the guard of assoc-equal and ; put-assoc-equal. The raw lisp macroexpansion of the second form of ; wormhole-eval is also like that above. Thus, the only problematic form in ; either expansion is , which necessarily mentions the variable symbol ; if it's mentioned in the lambda. Furthermore, at runtime we know ; absolutely nothing about the last wormhole status of . So we need to ; generate a fresh variable symbol to use in place of in our guard ; clauses. (let* ((whs (car (lambda-formals (cadr (fargn term 2))))) (body (lambda-body (cadr (fargn term 2)))) (name-dropper-term (fargn term 3)) (new-var (if whs (genvar whs (symbol-name whs) nil (all-vars1-lst clause (all-vars name-dropper-term))) nil)) (new-body (if (eq whs new-var) body (subst-var new-var whs body)))) (guard-clauses new-body debug-info stobj-optp clause wrld ttree))) ((throw-nonexec-error-p term :non-exec nil) ; It would be sound to replace the test above by (throw-nonexec-error-p term ; nil nil). However, through Version_4.3 we have always generated guard proof ; obligations for defun-nx, and indeed for any form (prog2$ ; (throw-nonexec-error 'fn ...) ...). So we restrict this special treatment to ; forms (prog2$ (throw-nonexec-error :non-exec ...) ...), as may be generated ; by calls of the macro, non-exec. The corresponding translated term is of the ; form (return-last 'progn (throw-non-exec-error ':non-exec ...) targ3); then ; only the throw-non-exec-error call needs to be considered for guard ; generation, not targ3. (guard-clauses (fargn term 2) debug-info stobj-optp clause wrld ttree)) ; At one time we optimized away the guards on (nth 'n MV) if n is an integerp ; and MV is bound in (former parameter) alist to a call of a multi-valued ; function that returns more than n values. Later we changed the way mv-let is ; handled so that we generated calls of mv-nth instead of nth, but we ; inadvertently left the code here unchanged. Since we have not noticed ; resulting performance problems, and since this was the only remaining use of ; alist when we started generating lambda terms as guards, we choose for ; simplicity's sake to eliminate this special optimization for mv-nth. (t ; Here we generate the conclusion clauses we must prove. These clauses ; establish that the guard of the function being called is satisfied. We first ; convert the guard into a set of clause segments, called the ; guard-concl-segments. ; We optimize stobj recognizer calls to true here. That is, if the function ; traffics in stobjs (and is not non-executablep!), then it was so translated ; (except in cases handled by the throw-nonexec-error-p case above), and we ; know that all those stobj recognizer calls are true. ; Once upon a time, we normalized the 'guard first. Is that important? (let ((guard-concl-segments (clausify (guard (ffn-symb term) stobj-optp wrld) ; Warning: It might be tempting to pass in the assumptions of clause into the ; second argument of clausify. That would be wrong! The guard has not yet ; been instantiated and so the variables it mentions are not the same ones in ; clause! nil ; Should we expand lambdas here? I say ``yes,'' but only to be conservative ; with old code. Perhaps we should change the t to nil? t ; We use the sr-limit from the world, because we are above the level at which ; :hints or :guard-hints would apply. (sr-limit wrld)))) (mv-let (cl-set1 ttree) (guard-clauses-lst (cond ((and (eq (ffn-symb term) 'return-last) (quotep (fargn term 1))) (case (unquote (fargn term 1)) (mbe1-raw ; Since (return-last 'mbe1-raw exec logic) macroexpands to exec in raw Common ; Lisp, we need only verify guards for the :exec part of an mbe call. (list (fargn term 2))) (ec-call1-raw ; Since (return-last 'ec-call1-raw ign (fn arg1 ... argk)) macroexpands to ; (*1*fn arg1 ... argk) in raw Common Lisp, we need only verify guards for the ; argi. (fargs (fargn term 3))) (otherwise ; Consider the case that (fargn term 1) is not syntactically equal to 'mbe1-raw ; or 'ec-call1-raw but reduces to one of these. Even then, return-last is a ; macro in Common Lisp, so we shouldn't produce the reduced obligations for ; either of the two cases above. But this is a minor issue anyhow, because ; it's certainly safe to avoid those reductions, so in the worst case we would ; still be sound, even if producing excessively strong guard obligations. (fargs term)))) (t (fargs term))) debug-info stobj-optp clause wrld ttree) (mv (conjoin-clause-sets+ debug-info cl-set1 (add-segments-to-clause (maybe-add-extra-info-lit debug-info term (reverse clause) wrld) (add-each-literal-lst (and guard-concl-segments ; optimization (nil for ec-call) (sublis-var-lst-lst (pairlis$ (formals (ffn-symb term) wrld) (remove-guard-holders-lst (fargs term))) guard-concl-segments))))) ttree)))))) (defun guard-clauses-lst (lst debug-info stobj-optp clause wrld ttree) (cond ((null lst) (mv nil ttree)) (t (mv-let (cl-set1 ttree) (guard-clauses (car lst) debug-info stobj-optp clause wrld ttree) (mv-let (cl-set2 ttree) (guard-clauses-lst (cdr lst) debug-info stobj-optp clause wrld ttree) (mv (conjoin-clause-sets+ debug-info cl-set1 cl-set2) ttree)))))) ) ; And now disvaring... (defun linked-variables1 (vars direct-links changedp direct-links0) ; We union into vars those elements of direct-links that overlap its ; current value. When we have done them all we ask if anything ; changed and if so, start over at the beginning of direct-links. (cond ((null direct-links) (cond (changedp (linked-variables1 vars direct-links0 nil direct-links0)) (t vars))) ((and (intersectp-eq (car direct-links) vars) (not (subsetp-eq (car direct-links) vars))) (linked-variables1 (union-eq (car direct-links) vars) (cdr direct-links) t direct-links0)) (t (linked-variables1 vars (cdr direct-links) changedp direct-links0)))) (defun linked-variables (vars direct-links) ; Vars is a list of variables. Direct-links is a list of lists of ; variables, e.g., '((X Y) (Y Z) (A B) (M)). Let's say that one ; variable is "directly linked" to another if they both appear in one ; of the lists in direct-links. Thus, above, X and Y are directly ; linked, as are Y and Z, and A and B. This function returns the list ; of all variables that are linked (directly or transitively) to those ; in vars. Thus, in our example, if vars is '(X) the answer is '(X Y ; Z), up to order of appearance. ; Note on Higher Order Definitions and the Inconvenience of ACL2: ; Later in these sources we will define the "mate and merge" function, ; m&m, which computes certain kinds of transitive closures. We really ; wish we had that function now, because this function could use it ; for the bulk of this computation. But we can't define it here ; without moving up some of the data structures associated with ; induction. Rather than rip our code apart, we define a simple ; version of m&m that does the job. ; This suggests that we really ought to support the idea of defining a ; function before all of its subroutines are defined -- a feature that ; ultimately involves the possibility of implicit mutual recursion. ; It should also be noted that the problem with moving m&m is not so ; much with the code for the mate and merge process as it is with the ; pseudo functional argument it takes. M&m naturally is a higher ; order function that compute the transitive closure of an operation ; supplied to it. Because ACL2 is first order, our m&m doesn't really ; take a function but rather a symbol and has a finite table mapping ; symbols to functions (m&m-apply). It is only that table that we ; can't move up to here! So if ACL2 were higher order, we could ; define m&m now and everything would be neat. Of course, if ACL2 ; were higher order, we suspect some other aspects of our coding ; (perhaps efficiency and almost certainly theorem proving power) ; would be degraded. (linked-variables1 vars direct-links nil direct-links)) ; Part of disvaring a type-alist to is keep type-alist entries about ; constrained constants. This goes to a problem that Eric Smith noted. ; He had constrained (thebit) to be 0 or 1 and had a type-alist entry ; stating that (thebit) was not 0. In a forcing round he needed that ; (thebit) was 1. But disvaring had thrown out of the type-alist the ; entry for (thebit) because it did not mention any of the relevant ; variables. So, in a change for Version_2.7 we now keep entries that ; mention constrained constants. We considered the idea of keeping ; entries that mention any constrained function, regardless of arity. ; But that seems like overkill. Had Eric constrained (thebit x) to ; be 0 or 1 and then had a hypothesis that it was not 0, it seems ; unlikely that the forcing round would need to know (thebit x) is 1 ; if x is not among the relevant vars. That is, one assumes that if a ; constrained function has arguments then the function's behavior on ; those arguments does not determine the function's behavior on other ; arguments. This need not be the case. One can constrain (thebit x) ; so that if it is 0 on some x then it is 0 on all x. ; (implies (equal (thebit x) 0) (equal (thebit y) 0)) ; But this seems unlikely. (mutual-recursion (defun contains-constrained-constantp (term wrld) (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (contains-constrained-constantp-lst (fargs term) wrld) (contains-constrained-constantp (lambda-body (ffn-symb term)) wrld))) ((and (getprop (ffn-symb term) 'constrainedp nil 'current-acl2-world wrld) (null (getprop (ffn-symb term) 'formals t 'current-acl2-world wrld))) t) (t (contains-constrained-constantp-lst (fargs term) wrld)))) (defun contains-constrained-constantp-lst (lst wrld) (cond ((null lst) nil) (t (or (contains-constrained-constantp (car lst) wrld) (contains-constrained-constantp-lst (cdr lst) wrld)))))) ; So now we can define the notion of ``disvaring'' a type-alist. (defun disvar-type-alist1 (vars type-alist wrld) (cond ((null type-alist) nil) ((or (intersectp-eq vars (all-vars (caar type-alist))) (contains-constrained-constantp (caar type-alist) wrld)) (cons (car type-alist) (disvar-type-alist1 vars (cdr type-alist) wrld))) (t (disvar-type-alist1 vars (cdr type-alist) wrld)))) (defun collect-all-vars (lst) (cond ((null lst) nil) (t (cons (all-vars (car lst)) (collect-all-vars (cdr lst)))))) (defun disvar-type-alist (type-alist term wrld) ; We throw out of type-alist any binding that does not involve a ; variable linked by type-alist to those in term. Thus, if term ; involves only the variables X and Y and type-alist binds a term that ; links Y to Z (and nothing else is linked to X, Y, or Z), then the ; resulting type-alist only binds terms containing X, Y, and/or Z. ; We actually keep entries about constrained constants. ; As we did for ``disguard'' we apologize for (but stand by) the ; non-word ``disvar.'' (let* ((vars (all-vars term)) (direct-links (collect-all-vars (strip-cars type-alist))) (vars* (linked-variables vars direct-links))) (disvar-type-alist1 vars* type-alist wrld))) ; Finally we can define the notion of ``unencumbering'' a type-alist. ; But as pointed out below, we no longer use this notion. (defun unencumber-type-alist (type-alist term rewrittenp wrld) ; We wish to prove term under type-alist. If rewrittenp is non-nil, ; it is also a term, namely the unrewritten term from which we ; obtained term. Generally, term (actually its unrewritten version) ; is some conjunct from a guard. In many cases we expect term to be ; something very simple like (RATIONALP X). But chances are high that ; type- alist talks about many other variables and many irrelevant ; terms. We wish to throw out irrelevant bindings from type-alist and ; return a new type-alist that is weaker but, we believe, as ; sufficient as the original for proving term. We call this ; ``unencumbering'' the type-alist. ; The following paragraph is inaccurate because we no longer use ; disguarding. ; Historical Comment: ; We apply two different techniques. The first is ``disguarding.'' ; Roughly, the idea is to throw out the binding of any term that ; requires the truth of term in its guard. Since we are trying to ; prove term true we will assume it false. If a hypothesis in the ; type-alist requires term to get past the guard, we'll never do it. ; This is not unlikely since term is (probably) a forced guard from ; the very clause from which type-alist was created. ; End of Historical Comment ; The second technique, applied after disguarding, is to throw out any ; binding of a term that is not linked to the variables used by term. ; For example, if term is (RATIONALP X) then we won't keep a ; hypothesis about (PRIMEP Y) unless some kept hypothesis links X and ; Y. This is called ``disvaring'' and is applied after diguarding ; because the terms thrown out by disguarding are likely to link ; variables in a bogus way. For example, (< X Y) would link X and Y, ; but is thrown out by disguarding since it requires (RATIONALP X). ; While disvaring, we actually keep type-alist entries about constrained ; constants. (declare (ignore rewrittenp)) (disvar-type-alist type-alist term wrld)) (defun unencumber-assumption (assn wrld) ; We no longer unencumber assumptions. An example from Jared Davis prompted ; this change, in which he had contradictory hypotheses for which the ; contradiction was not yet evident after a round of simplification, leading to ; premature forcing -- and the contradiction was in hypotheses about variables ; irrelevant to what was forced, and hence was lost in the forcing round! Here ; is a much simpler example of that phenomenon. ; (defstub p1 (x) t) ; (defstub p2 (x) t) ; (defstub p3 (x) t) ; (defstub p4 (x) t) ; ; (defaxiom p1->p2 ; (implies (p1 x) ; (p2 x))) ; ; (defun foo (x y) ; (implies x y)) ; ; (defaxiom p3->p4{forced} ; (implies (force (p3 x)) ; (p4 x))) ; ; ; When we unencumber the type-alist upon forcing, the following THM fails with ; ; the following forcing round. The problem is that the hypothesis about x is ; ; dropped because it is deemed (by unencumber-type-alist) to be irrelevant to ; ; the sole variable y of the forced hypothesis. ; ; ; We now undertake Forcing Round 1. ; ; ; ; [1]Goal ; ; (P3 Y). ; ; (thm (if (not (foo (p1 x) (p2 x))) ; (p4 y) ; t) ; :hints (("Goal" :do-not '(preprocess) ; :in-theory (disable foo)))) ; ; ; But with unencumber-assumption defined to return its first argument, the THM ; ; produces a forced goal that includes the contradictory hypotheses: ; ; ; [1]Goal ; ; (IMPLIES (NOT (FOO (P1 X) (P2 X))) ; ; (P3 Y)). ; ; (thm (if (not (foo (p1 x) (p2 x))) ; (p4 y) ; t) ; :hints (("Goal" :do-not '(preprocess) ; :in-theory (disable foo)))) ; Old comment and code: ; Given an assumption we try to unencumber (i.e., shorten) its ; :type-alist. We return an assumption that may be proved in place of ; assn and is supposedly simpler to prove. ; (change assumption assn ; :type-alist ; (unencumber-type-alist (access assumption assn :type-alist) ; (access assumption assn :term) ; (access assumption assn :rewrittenp) ; wrld)) (declare (ignore wrld)) assn) (defun unencumber-assumptions (assumptions wrld ans) ; This is just a glorified list reverse function! At one time we actually did ; unencumber assumptions, but now, unencumber-assumption is defined simply to ; return nil, as explained in a comment in its definition. A more elegant fix ; is to redefine the present function to return assumptions unchanged, to avoid ; consing up a reversed list. However, we continue to reverse the input ; assumptions, for backward compatibility (otherwise forcing round goal names ; will change). Reversing a small list is cheap, so this is not a big deal. ; Old comments: ; We unencumber every assumption in assumptions and return the ; modified list, accumulated onto ans. ; Note: This process is mentioned in :DOC forcing-round. So if we change it, ; update the documentation. (cond ((null assumptions) ans) (t (unencumber-assumptions (cdr assumptions) wrld (cons (unencumber-assumption (car assumptions) wrld) ans))))) ; We are now concerned, for a while, with the idea of deleting from a ; set of assumptions those implied by others. We call this ; assumption-subsumption. Each assumption can be thought of as a goal ; of the form type-alist -> term. Observe that if you have two ; assumptions with the same term, then the first implies the second if ; the type-alist of the second implies the type-alist of the first. ; That is, ; (thm (implies (implies ta2 ta1) ; (implies (implies ta1 term) (implies ta2 term)))) ; First we develop the idea that one type-alist implies another. (defun dumb-type-alist-implicationp1 (type-alist1 type-alist2 seen) (cond ((null type-alist1) t) ((member-equal (caar type-alist1) seen) (dumb-type-alist-implicationp1 (cdr type-alist1) type-alist2 seen)) (t (let ((ts1 (cadar type-alist1)) (ts2 (or (cadr (assoc-equal (caar type-alist1) type-alist2)) *ts-unknown*))) (and (ts-subsetp ts1 ts2) (dumb-type-alist-implicationp1 (cdr type-alist1) type-alist2 (cons (caar type-alist1) seen))))))) (defun dumb-type-alist-implicationp2 (type-alist1 type-alist2) (cond ((null type-alist2) t) (t (and (assoc-equal (caar type-alist2) type-alist1) (dumb-type-alist-implicationp2 type-alist1 (cdr type-alist2)))))) (defun dumb-type-alist-implicationp (type-alist1 type-alist2) ; NOTE: This function is intended to be dumb but fast. One can ; imagine that we should be concerned with the types deduced by ; type-set under these type-alists. For example, instead of asking ; whether every term bound in type-alist1 is bound to a bigger type ; set in type-alist2, we should perhaps ask whether the term has a ; bigger type-set under type-alist2. Similarly, if we find a term ; bound in type-alist2 we should make sure that its type-set under ; type-alist1 is smaller. If we need the smarter function we'll write ; it. That's why we call this one "dumb." ; We say type-alist1 implies type-alist2 if (1) for every ; "significant" entry in type-alist1, (term ts1 . ttree1) it is the ; case that either term is not bound in type-alist2 or term is bound ; to some ts2 in type-alist2 and (ts-subsetp ts1 ts2), and (2) every ; term bound in type-alist2 is bound in type-alist1. The case where ; term is not bound in type-alist2 can be seen as the natural ; treatment of the equivalent situation in which term is bound to ; *ts-unknown* in type-set2. An entry (term ts . ttree) is ; "significant" if it is the first binding of term in the alist. ; We can treat a type-alist as a conjunction of assumptions about the ; terms it binds. Each relevant entry gives rise to an assumption ; about its term. Call the conjunction the "assumptions" encoded in ; the type-alist. If type-alist1 implies type-alist2 then the ; assumptions of the first imply those of the second. Consider an ; assumption of the first. It restricts its term to some type. But ; the corresponding assumption about term in the second type-alist ; restricts term to a larger type. Thus, each assumption of the first ; type-alist implies the corresponding assumption of the second. ; The end result of all of this is that if you need to prove some ; condition, say g, under type-alist1 and also under type-alist2, and ; you can determine that type-alist1 implies type-alist2, then it is ; sufficient to prove g under type-alist2. ; Here is an example. Let type-alist1 be ; ((x *ts-t*) (y *ts-integer*) (z *ts-symbol*)) ; and type-alist2 be ; ((x *ts-boolean*)(y *ts-rational*)). ; Observe that type-alist1 implies type-alist2: *ts-t* is a subset of ; *ts- boolean*, *ts-integer* is a subset of *ts-rational*, and ; *ts-symbol* is a subset of *ts-unknown*, and there are no terms ; bound in type-alist2 that aren't bound in type-alist1. If we needed ; to prove g under both of these type-alists, it would suffice to ; prove it under type-alist2 (the weaker) because we must ultimately ; prove g under type-alist2 and the proof of g under type-alist1 ; follows from that for free. ; Observe also that if we added to type-alist2 the binding (u ; *ts-cons*) then condition (1) of our definition still holds but (2) ; does not. Further, if we mistakenly regarded type-alist2 as the ; weaker then proving (consp u) under type-alist2 would not ensure a ; proof of (consp u) under type-alist1. (and (dumb-type-alist-implicationp1 type-alist1 type-alist2 nil) (dumb-type-alist-implicationp2 type-alist1 type-alist2))) ; Now we arrange to partition a bunch of assumptions into pots ; according to their :terms, so we can do the type-alist implication ; work just on those assumptions that share a :term. (defun partition-according-to-assumption-term (assumptions alist) ; We partition assumptions into pots, where the assumptions in a ; single pot all share the same :term. The result is an alist whose ; keys are the :terms and whose values are the assumptions which have ; those terms. (cond ((null assumptions) alist) (t (partition-according-to-assumption-term (cdr assumptions) (put-assoc-equal (access assumption (car assumptions) :term) (cons (car assumptions) (cdr (assoc-equal (access assumption (car assumptions) :term) alist))) alist))))) ; So now imagine we have a bunch of assumptions that share a term. We ; want to delete from the set any whose type-alist implies any one ; kept. See dumb-keep-assumptions-with-weakest-type-alists. (defun exists-assumption-with-weaker-type-alist (assumption assumptions i) ; If there is an assumption, assn, in assumptions whose type-alist is ; implied by that of the given assumption, we return (mv pos assn), ; where pos is the position in assumptions of the first such assn. We ; assume i is the position of the first assumption in assumptions. ; Otherwise we return (mv nil nil). (cond ((null assumptions) (mv nil nil)) ((dumb-type-alist-implicationp (access assumption assumption :type-alist) (access assumption (car assumptions) :type-alist)) (mv i (car assumptions))) (t (exists-assumption-with-weaker-type-alist assumption (cdr assumptions) (1+ i))))) (defun add-assumption-with-weak-type-alist (assumption assumptions ans) ; We add assumption to assumptions, deleting any member of assumptions ; whose type-alist implies that of the given assumption. When we ; delete an assumption we union its :assumnotes field into that of the ; assumption we are adding. We accumulate our answer onto ans to keep ; this tail recursive; we presume that there will be a bunch of ; assumptions when this stuff gets going. (cond ((null assumptions) (cons assumption ans)) ((dumb-type-alist-implicationp (access assumption (car assumptions) :type-alist) (access assumption assumption :type-alist)) (add-assumption-with-weak-type-alist (change assumption assumption :assumnotes (union-equal (access assumption assumption :assumnotes) (access assumption (car assumptions) :assumnotes))) (cdr assumptions) ans)) (t (add-assumption-with-weak-type-alist assumption (cdr assumptions) (cons (car assumptions) ans))))) (defun dumb-keep-assumptions-with-weakest-type-alists (assumptions kept) ; We return that subset of assumptions with the property that for ; every member, a, of assumptions there is one, b, among those ; returned such that (dumb-type-alist-implicationp a b). Thus, we keep ; all the ones with the weakest hypotheses. If we can prove all the ; ones kept, then we can prove them all, because each one thrown away ; has even stronger hypotheses than one of the ones we'll prove. ; (These comments assume that kept is initially nil and that all of ; the assumptions have the same :term.) Whenever we throw out a in ; favor of b, we union into b's :assumnotes those of a. (cond ((null assumptions) kept) (t (mv-let (i assn) (exists-assumption-with-weaker-type-alist (car assumptions) kept 0) (cond (i (dumb-keep-assumptions-with-weakest-type-alists (cdr assumptions) (update-nth i (change assumption assn :assumnotes (union-equal (access assumption (car assumptions) :assumnotes) (access assumption assn :assumnotes))) kept))) (t (dumb-keep-assumptions-with-weakest-type-alists (cdr assumptions) (add-assumption-with-weak-type-alist (car assumptions) kept nil)))))))) ; And now we can write the top-level function for dumb-assumption-subsumption. (defun dumb-assumption-subsumption1 (partitions ans) ; Having partitioned the original assumptions into pots by :term, we ; now simply clean up the cdr of each pot -- which is the list of all ; assumptions with the given :term -- and append the results of all ; the pots together. (cond ((null partitions) ans) (t (dumb-assumption-subsumption1 (cdr partitions) (append (dumb-keep-assumptions-with-weakest-type-alists (cdr (car partitions)) nil) ans))))) (defun dumb-assumption-subsumption (assumptions) ; We throw out of assumptions any assumption implied by any of the others. Our ; notion of "implies" here is quite weak, being a simple comparison of ; type-alists. Briefly, we partition the set of assumptions into pots by :term ; and then, within each pot throw out any assumption whose type-alist is ; stronger than some other in the pot. When we throw some assumption out in ; favor of another we combine its :assumnotes into that of the one we keep, so ; we can report the cases for which each final assumption accounts. (dumb-assumption-subsumption1 (partition-according-to-assumption-term assumptions nil) nil)) ; Now we move on to the problem of converting an unemcumbered and subsumption ; cleansed assumption into a clause to prove. (defun clausify-type-alist (type-alist cl ens w seen ttree) ; Consider a type-alist such as ; `((x ,*ts-cons*) (y ,*ts-integer*) (z ,(ts-union *ts-rational* *ts-symbol*))) ; and some term, such as (p x y z). We wish to construct a clause ; that represents the goal of proving the term under the assumption of ; the type-alist. A suitable clause in this instance is ; (implies (and (consp x) ; (integerp y) ; (or (rationalp z) (symbolp z))) ; (p x y z)) ; We return (mv clause ttree), where clause is the clause constructed. (cond ((null type-alist) (mv cl ttree)) ((member-equal (caar type-alist) seen) (clausify-type-alist (cdr type-alist) cl ens w seen ttree)) (t (mv-let (term ttree) (convert-type-set-to-term (caar type-alist) (cadar type-alist) ens w ttree) (clausify-type-alist (cdr type-alist) (cons (dumb-negate-lit term) cl) ens w (cons (caar type-alist) seen) ttree))))) (defun clausify-assumption (assumption ens wrld) ; We convert the assumption assumption into a clause. ; Note: If you ever change this so that the assumption :term is not the last ; literal of the clause, change the printer process-assumptions-msg1. (clausify-type-alist (access assumption assumption :type-alist) (list (access assumption assumption :term)) ens wrld nil nil)) (defun clausify-assumptions (assumptions ens wrld pairs ttree) ; We clausify every assumption in assumptions. We return (mv pairs ttree), ; where pairs is a list of pairs, each of the form (assumnotes . clause) where ; the assumnotes are the corresponding field of the clausified assumption. (cond ((null assumptions) (mv pairs ttree)) (t (mv-let (clause ttree1) (clausify-assumption (car assumptions) ens wrld) (clausify-assumptions (cdr assumptions) ens wrld (cons (cons (access assumption (car assumptions) :assumnotes) clause) pairs) (cons-tag-trees ttree1 ttree)))))) (defun strip-assumption-terms (lst) ; Given a list of assumptions, return the set of their terms. (cond ((endp lst) nil) (t (add-to-set-equal (access assumption (car lst) :term) (strip-assumption-terms (cdr lst)))))) (defun add-splitters-to-ttree1 (assumnotes tag ttree) (cond ((endp assumnotes) ttree) (t (add-splitters-to-ttree1 (cdr assumnotes) tag (add-to-tag-tree tag (access assumnote (car assumnotes) :rune) ttree))))) (defun add-splitters-to-ttree (immediatep tag assumptions ttree) (cond ((endp assumptions) ttree) (t (add-splitters-to-ttree immediatep tag (cdr assumptions) (cond ((eq immediatep (access assumption (car assumptions) :immediatep)) (add-splitters-to-ttree1 (access assumption (car assumptions) :assumnotes) tag ttree)) (t ttree)))))) (defun maybe-add-splitters-to-ttree (splitter-output immediatep tag assumptions ttree) (cond (splitter-output (add-splitters-to-ttree immediatep tag assumptions ttree)) (t ttree))) (defun extract-and-clausify-assumptions (cl ttree only-immediatep ens wrld splitter-output) ; WARNING: This function is overloaded. Only-immediatep can take only only two ; values in this function: 'non-nil or nil. The interpretation is as in ; collect-assumptions. Cl is irrelevant if only-immediatep is nil. We always ; return four results. But when only-immediatep = 'non-nil, the first and part ; of the third result are irrelevant. We know that only-immediatep = 'non-nil ; is used only in waterfall-step to do CASE-SPLITs and immediate FORCEs. We ; know that only-immediatep = nil is used for forcing-round applications and in ; the proof checker. When CASE-SPLIT type assumptions are collected with ; only-immediatep = nil, then they are given the semantics of FORCE rather ; than CASE-SPLIT. This could happen in the proof checker, but it is thought ; not to happen otherwise. ; In the case that only-immediatep is nil: we strip all assumptions out of ; ttree, obtaining an assumption-free ttree, ttree'. We then cleanup the ; assumptions, by removing subsumed ones. (Formerly we also unencumbered their ; type-alists of presumed irrelevant bindings first, but we no longer do so; ; see unencumber-assumption.) We then convert each kept assumption into a ; clause encoding the implication from the cleaned up type-alist to the ; assumed term. We pair each clause with the :assumnotes of the assumptions ; for which it accounts, to produce a list of pairs, which is among the things ; we return. Each pair is of the form (assumnotes . clause). We return four ; results, (mv n a pairs ttree'), where n is the number of assumptions in the ; tree, a is the cleaned up assumptions we have to prove, whose length is the ; same as the length of pairs. ; In the case that only-immediatep is 'non-nil: we strip out of ttree only ; those assumptions with non-nil :immediatep flags. As before, we generate a ; clause for each, but those with :immediatep = 'case-split we handle ; differently now: the clause for such an assumption is the one that encodes ; the implication from the negation of cl to the assumed term, rather than the ; one involving the type-alist of the assumption. The assumnotes paired with ; such a clause is nil. We do not really care about the assumnotes in ; case-splits or immediatep = t cases (e.g., they are ignored by the ; waterfall-step processing). The final ttree, ttree', may still contain ; non-immediatep assumptions. ; To keep the definition simpler, we split into just the two cases outlined ; above. (cond ((eq only-immediatep nil) (let* ((raw-assumptions (collect-assumptions ttree only-immediatep)) (cleaned-assumptions (dumb-assumption-subsumption (unencumber-assumptions raw-assumptions wrld nil)))) (mv-let (pairs ttree1) (clausify-assumptions cleaned-assumptions ens wrld nil nil) ; We check below that ttree1 is 'assumption free, so that when we add it to the ; result of cleansing 'assumptions from ttree we get an assumption-free ttree. ; If ttree1 contains assumptions we believe it must be because the bottom-most ; generator of those ttrees, namely convert-type-set-to-term, was changed to ; force assumptions. But if that happens, we will have to rethink a lot here. ; How can we ensure that we get rid of all assumptions if we make assumptions ; while trying to express our assumptions as clauses? (mv (length raw-assumptions) cleaned-assumptions pairs (cons-tag-trees (cond ((tagged-objectsp 'assumption ttree1) (er hard 'extract-and-clausify-assumptions "Convert-type-set-to-term apparently returned a ttree that ~ contained an 'assumption tag. This violates the ~ assumption in this function.")) (t ttree1)) (delete-assumptions ttree only-immediatep)))))) ((eq only-immediatep 'non-nil) (let* ((case-split-assumptions (collect-assumptions ttree 'case-split)) (assumed-terms (strip-assumption-terms case-split-assumptions)) (case-split-clauses (split-on-assumptions assumed-terms cl nil)) (case-split-pairs (pairlis2 nil case-split-clauses)) (raw-assumptions (collect-assumptions ttree t)) (cleaned-assumptions (dumb-assumption-subsumption (unencumber-assumptions raw-assumptions wrld nil)))) (mv-let (pairs ttree1) (clausify-assumptions cleaned-assumptions ens wrld nil nil) ; We check below that ttree1 is 'assumption free, so that when we add it to the ; result of cleansing 'assumptions from ttree we get an assumption-free ttree. ; If ttree1 contains assumptions we believe it must be because the bottom-most ; generator of those ttrees, namely convert-type-set-to-term, was changed to ; force assumptions. But if that happens, we will have to rethink a lot here. ; How can we ensure that we get rid of all assumptions if we make assumptions ; while trying to express our assumptions as clauses? (mv 'ignored assumed-terms (append case-split-pairs pairs) (maybe-add-splitters-to-ttree splitter-output 'case-split 'splitter-case-split case-split-assumptions (maybe-add-splitters-to-ttree splitter-output t 'splitter-immed-forced raw-assumptions (cons-tag-trees (cond ((tagged-objectsp 'assumption ttree1) (er hard 'extract-and-clausify-assumptions "Convert-type-set-to-term apparently returned a ttree ~ that contained an 'assumption tag. This violates the ~ assumption in this function.")) (t ttree1)) (delete-assumptions ttree 'non-nil)))))))) (t (mv 0 nil (er hard 'extract-and-clausify-assumptions "We only implemented two cases for only-immediatep: 'non-nil ~ and nil. But you now call it on ~p0." only-immediatep) nil)))) ; Finally, we put it all together in the primitive function that ; applies a processor to a clause. (defun@par waterfall-step1 (processor cl-id clause hist pspv wrld state step-limit) ; Note that apply-top-hints-clause is handled in waterfall-step. (case processor (simplify-clause (pstk (simplify-clause clause hist pspv wrld state step-limit))) (preprocess-clause (pstk (preprocess-clause clause hist pspv wrld state step-limit))) (otherwise (prepend-step-limit 4 (case processor (settled-down-clause (pstk (settled-down-clause clause hist pspv wrld state))) (eliminate-destructors-clause (pstk (eliminate-destructors-clause clause hist pspv wrld state))) (fertilize-clause (pstk (fertilize-clause cl-id clause hist pspv wrld state))) (generalize-clause (pstk (generalize-clause clause hist pspv wrld state))) (eliminate-irrelevance-clause (pstk (eliminate-irrelevance-clause clause hist pspv wrld state))) (otherwise (pstk (push-clause@par clause hist pspv wrld state)))))))) (defun@par process-backtrack-hint (cl-id clause clauses processor new-hist new-pspv ctx wrld state) ; A step of the indicated clause with cl-id through the waterfall, via ; waterfall-step, has tentatively returned the indicated clauses, new-hist, and ; new-pspv. If the original pspv contains a :backtrack hint-setting, we replace ; the hint-settings with the computed hint that it specifies. (let ((backtrack-hint (cdr (assoc-eq :backtrack (access prove-spec-var new-pspv :hint-settings))))) (cond (backtrack-hint (assert$ (eq (car backtrack-hint) 'eval-and-translate-hint-expression) (mv-let@par (erp val state) (eval-and-translate-hint-expression@par (cdr backtrack-hint) ; tuple of the form (name-tree flg term) cl-id clause wrld :OMITTED ; stable-under-simplificationp, unused in :backtrack hints new-hist new-pspv clauses processor :OMITTED ; keyword-alist, unused in :backtrack hints 'backtrack (override-hints wrld) ctx state) (cond (erp (mv@par t nil nil state)) ((assert$ (listp val) (eq (car val) :computed-hint-replacement)) (mv@par nil (cddr val) (assert$ (consp (cdr val)) (case (cadr val) ((t) (list backtrack-hint)) ((nil) nil) (otherwise (cadr val)))) state)) (t (mv@par nil val nil state)))))) (t (mv@par nil nil nil state))))) ; Before we can can complete the definition of waterfall-step, we need support ; for rw-cache operations (see the Essay on Rw-cache) at the pspv level. (defun set-rw-cache-state-in-pspv (pspv val) (declare (xargs :guard (member-eq val *legal-rw-cache-states*))) (change prove-spec-var pspv :rewrite-constant (change rewrite-constant (access prove-spec-var pspv :rewrite-constant) :rw-cache-state val))) (defun maybe-set-rw-cache-state-disabled (pspv) (cond ((eq (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :rw-cache-state) t) (set-rw-cache-state-in-pspv pspv :disabled)) (t pspv))) (defun maybe-set-rw-cache-state-enabled (pspv) (cond ((eq (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :rw-cache-state) :disabled) (set-rw-cache-state-in-pspv pspv t)) (t pspv))) (defun accumulate-rw-cache-into-pspv (processor ttree pspv) ; This function is called during waterfall-step before modifying the pspv, in ; order to accumulate the rw-cache of ttree into the ttree of pspv. This need ; only happen when the processor can put significant entries into the rw-cache, ; so this function is a no-op unless the processor is simplify-clause. This ; need not happen when simplify-clause produces a hit, since the ttree will ; accumulated into the pspv elsewhere (so that runes are reported, forcing ; takes place, etc.); so it should suffice to call this function when there is ; a miss. If the ttree is empty or if the rw-cache is not active, there is ; nothing to accumulate. Also, as we clear the rw-cache for every call of ; rewrite-atm, there is no need to accumulate when the rw-cache-state is ; :atom. (cond ((and (eq processor 'simplify-clause) ttree (eq (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :rw-cache-state) t)) (let ((new-ttree-or-nil (accumulate-rw-cache? nil ttree (access prove-spec-var pspv :tag-tree)))) (cond (new-ttree-or-nil (change prove-spec-var pspv :tag-tree new-ttree-or-nil)) (t pspv)))) (t pspv))) (defun erase-rw-cache-from-pspv (pspv) ; Erase all rw-cache tagged objects from the ttree of pspv. We could call ; erase-rw-cache, but since we have the opportunity to call ; remove-tag-from-tag-tree! to save assoc-eq calls, we do so. (let ((ttree (access prove-spec-var pspv :tag-tree))) (cond ((tagged-objectsp 'rw-cache-any-tag ttree) (change prove-spec-var pspv :tag-tree (remove-tag-from-tag-tree 'rw-cache-nil-tag (remove-tag-from-tag-tree! 'rw-cache-any-tag ttree)))) ((tagged-objectsp 'rw-cache-nil-tag ttree) (change prove-spec-var pspv :tag-tree (remove-tag-from-tag-tree! 'rw-cache-nil-tag ttree))) (t pspv)))) (defconst *simplify-clause-ledge* (member-eq 'simplify-clause *preprocess-clause-ledge*)) (defconst *simplify-clause-ledge-complement* (set-difference-eq *preprocess-clause-ledge* *simplify-clause-ledge*)) (defun@par waterfall-step-cleanup (processor cl-id clause hist wrld state signal clauses ttree pspv new-pspv step-limit) ; Signal here can be either some form of HIT (hit, hit-rewrite, ; hit-rewrite2, or-hit) or ABORT. ; Imagine that the indicated waterfall processor produced some form of ; hit and returned signal, clauses, ttree, and new-pspv. We have to ; do certain cleanup on these things (e.g., add cl-id to all the ; assumnotes) and we do all that cleanup here. ; The actual cleanup is ; (1) add the cl-id to each assumnote in ttree ; (2) accumulate the modified ttree into state ; (3) extract the assumptions we are to handle immediately ; (4) compute the resulting case splits and modify clauses appropriately ; (5) make a new history entry ; (6) adjust signal to account for a specious application ; The result is (mv signal' clauses' ttree' hist' pspv' state) where ; each of the primed things are (possibly) modifications of their ; input counterparts. ; Here is blow-by-blow description of the cleanup. ; We update the :cl-id field (in the :assumnote) of every 'assumption. ; We accumulate the modified ttree into state. (declare (ignorable cl-id step-limit state)) (let ((ttree (set-cl-ids-of-assumptions ttree cl-id))) ; We extract the assumptions we are to handle immediately. (mv-let (n assumed-terms pairs ttree) (extract-and-clausify-assumptions clause ttree 'non-nil ; collect CASE-SPLIT and immediate FORCE assumptions (access rewrite-constant (access prove-spec-var new-pspv :rewrite-constant) :current-enabled-structure) wrld (access rewrite-constant (access prove-spec-var new-pspv :rewrite-constant) :splitter-output)) (declare (ignore n)) ; Note below that we throw away the cars of the pairs, which are ; typically assumnotes. We keep only the clauses themselves. ; We perform the required splitting, augmenting the previously ; generated clauses with the assumed terms. (let* ((split-clauses (strip-cdrs pairs)) (clauses (if (and (null split-clauses) (null assumed-terms) (member-eq processor '(preprocess-clause apply-top-hints-clause))) clauses (remove-trivial-clauses (union-equal split-clauses (disjoin-clause-segment-to-clause-set (dumb-negate-lit-lst assumed-terms) clauses)) wrld))) (ttree (cond ((cdr clauses) ttree) (t (remove-tag-from-tag-tree 'splitter-if-intro ttree)))) ; We create the history entry for this step. We have to be careful about ; specious hits to prevent a loop described below. (new-hist (cons (make history-entry :signal signal ; indicating the type of "hit" :processor ; We here detect specious behavior. The basic idea is that a hit ; is specious if the input clause is among the output clauses. But ; there are two exceptions: when the process is settled-down-clause or ; apply-top-hints-clause, such apparently specious output is ok. ; We mark a specious hit by setting the :processor of the history-entry ; to the cons (SPECIOUS . processor). (if (and (not (member-eq processor '(settled-down-clause ; The obvious example of apparently specious behavior by ; apply-top-hints-clause that is not really specious is when it signals ; an OR-HIT and returns the input clause (to be processed by further hints). ; But the inclusion of apply-top-hints-clause in this list of exceptions ; was originally made in Version_2.7 because of :by hints. Consider ; what happens when a :by hint produces a subgoal that is identical to the ; current goal. If the subgoal is labeled as 'SPECIOUS, then we will 'MISS ; below. This was causing the waterfall to apply the :by hint a second time, ; resulting in output such as the following: ; As indicated by the hint, this goal is subsumed by (EQUAL (F1 X) (F0 X)), ; which can be derived from LEMMA1 via functional instantiation, provided ; we can establish the constraint generated. ; ; As indicated by the hint, this goal is subsumed by (EQUAL (F1 X) (F0 X)), ; which can be derived from LEMMA1 via functional instantiation, provided ; we can establish the constraint generated. ; The following example reproduces the above output. The top-level hints ; (i.e., *top-hint-keywords*) should never be 'SPECIOUS anyhow, because the ; user will more than likely prefer to see the output before the proof ; (probably) fails. ; (defstub f0 (x) t) ; (defstub f1 (x) t) ; (defstub f2 (x) t) ; ; (defaxiom lemma1 ; (equal (f2 x) (f1 x))) ; ; (defthm main ; (equal (f1 x) (f0 x)) ; :hints (("Goal" :by (:functional-instance lemma1 (f2 f1) (f1 f0))))) apply-top-hints-clause))) (member-equal clause clauses)) (cons 'SPECIOUS processor) processor) :clause clause :ttree ttree :cl-id ; only needed for #+acl2-par, but harmless cl-id) hist))) (mv-let@par (erp ttree state) (accumulate-ttree-and-step-limit-into-state@par ttree step-limit state) (declare (ignore erp)) (cond ((consp (access history-entry ; (SPECIOUS . processor) (car new-hist) :processor)) (mv@par 'MISS nil ttree new-hist (accumulate-rw-cache-into-pspv processor ttree pspv) state)) (t (mv@par signal clauses ttree new-hist (cond ((or (member-eq processor *simplify-clause-ledge-complement*) (eq processor 'settled-down-clause)) (put-ttree-into-pspv ttree new-pspv)) ((eq processor 'simplify-clause) (put-ttree-into-pspv ttree (maybe-set-rw-cache-state-enabled new-pspv))) (t (put-ttree-into-pspv (erase-rw-cache ttree) (maybe-set-rw-cache-state-disabled (erase-rw-cache-from-pspv new-pspv))))) state)))))))) (defun@par waterfall-step (processor cl-id clause hist pspv wrld ctx state step-limit) ; Processor is one of the known waterfall processors. This function applies ; processor and returns seven results: step-limit, signal, clauses, new-hist, ; new-pspv, jppl-flg, and state. ; All processor functions take as input a clause, its hist, a pspv, wrld, and ; state. They all deliver four values (but apply-top-hints-clause, ; preprocess-clause, and simplify-clause also take a step-limit input and ; deliver a new step-limit as an additional value, in the first position): a ; signal, some clauses, a ttree, and a new pspv. The signal delivered by such ; processors is one of 'error, 'miss, 'abort, or else indicates a "hit" via the ; signals 'hit, 'hit-rewrite, 'hit-rewrite2 and 'or-hit. We identify the first ; three kinds of hits. 'Or-hits indicate that a set of disjunctive branches ; has been produced. ; If the returned signal is 'error or 'miss, we immediately return with that ; signal. But if the signal is a "hit" or 'abort (which in this context means ; "the processor did something but it has demanded the cessation of the ; waterfall process"), we add a new history entry to hist, store the ttree into ; the new pspv, print the message associated with this processor, and then ; return. ; When a processor "hit"s, we check whether it is a specious hit, i.e., whether ; the input is a member of the output. If so, the history entry for the hit is ; marked specious by having the :processor field '(SPECIOUS . processor). ; However, we report the step as a 'miss, passing back the extended history to ; be passed. Specious processors have to be recorded in the history so that ; waterfall-msg can detect that they have occurred and not reprint the formula. ; Mild Retraction: Actually, settled-down-clause always produces ; specious-appearing output but we never mark it as 'SPECIOUS because we want ; to be able to assoc for settled-down-clause and we know it's specious anyway. ; We typically return (mv step-limit signal clauses new-hist new-pspv jppl-flg ; state). ; Signal Meaning ; 'error Halt the entire proof attempt with an error. We ; print out the error message to the returned state. ; In this case, clauses, new-hist, new-pspv, and jppl-flg ; are all irrelevant (and nil). ; 'miss The processor did not apply or was specious. Clauses, ; and jppl-flg are irrelevant and nil, but new-hist has the ; specious processor recorded in it, and new-pspv records ; rw-cache modifications to the :tag-tree of the input pspv. ; State is unchanged. ; 'abort Like a "hit", except that we are not to continue with ; the waterfall. We are to use the new pspv as the ; final pspv produced by the waterfall. ; 'hit The processor applied and produced the new set of ; 'hit-rewrite clauses returned. The appropriate new history and ; 'hit-rewrite2 new pspv are returned. Jppl-flg is either nil ; (indicating that the processor was not push-clause) ; or is a pool lst (indicating that a clause was pushed ; and assigned that lst). The jppl-flg of the last executed ; processor should find its way out of the waterfall so ; that when we get out and pop a clause we know if we ; just pushed it. Finally, the message describing the ; transformation has been printed to state. ; 'or-hit The processor applied and has requested a disjunctive ; split determined by hints. The results are actually ; the same as for 'hit. The processor did not actually ; produce the case split because some of the hints ; (e.g., :in-theory) are related to the environment ; in which we are to process the clause rather than the ; clause itself. ; 'top-of-waterfall-hint ; A :backtrack hint was applicable, and suitable results are ; returned for handling it. (sl-let@par (erp signal clauses ttree new-pspv state) (catch-time-limit5@par (cond ((eq processor 'apply-top-hints-clause) ; this case returns state (apply-top-hints-clause@par cl-id clause hist pspv wrld ctx state step-limit)) (t (sl-let (signal clauses ttree new-pspv) (waterfall-step1@par processor cl-id clause hist pspv wrld state step-limit) (mv@par step-limit signal clauses ttree new-pspv state))))) (cond (erp ; from out-of-time or clause-processor failure; treat as 'error signal (mv-let@par (erp val state) (er@par soft ctx "~@0" erp) (declare (ignore erp val)) (mv@par step-limit 'error nil nil nil nil state))) (t (pprogn@par ; account for bddnote in case we do not have a hit (cond ((and (eq processor 'apply-top-hints-clause) (member-eq signal '(error miss)) ttree) ; a bddnote; see bdd-clause (error-in-parallelism-mode@par ; Parallelism blemish: we disable the following addition of BDD notes to the ; state. Until a user requests it, we don't see a need to implement this. (state-mac@par) (f-put-global 'bddnotes (cons ttree (f-get-global 'bddnotes state)) state))) (t (state-mac@par))) (mv-let@par (signal clauses new-hist new-pspv jppl-flg state) (cond ((eq signal 'error) ; As of this writing, the only processor which might cause an error is ; apply-top-hints-clause. But processors can't actually cause errors in the ; error/value/state sense because they don't return state and so can't print ; their own error messages. We therefore make the convention that if they ; signal error then the "clauses" value they return is in fact a pair ; (fmt-string . alist) suitable for giving error1. Moreover, in this case ; ttree is an alist assigning state global variables to values. (mv-let@par (erp val state) (error1@par ctx (car clauses) (cdr clauses) state) (declare (ignore erp val)) (mv@par 'error nil nil nil nil state))) ((eq signal 'miss) (mv@par 'miss nil hist (accumulate-rw-cache-into-pspv processor ttree pspv) nil state)) (t (mv-let@par (signal clauses ttree new-hist new-pspv state) (waterfall-step-cleanup@par processor cl-id clause hist wrld state signal clauses ttree pspv new-pspv step-limit) (mv-let@par (erp new-hint-settings new-hints state) (cond ((or (eq signal 'miss) ; presumably specious (eq processor 'settled-down-clause)) ; not user-visible (mv@par nil nil nil state)) (t (process-backtrack-hint@par cl-id clause clauses processor new-hist new-pspv ctx wrld state))) (cond (erp (mv@par 'error nil nil nil nil state)) (new-hint-settings (mv@par 'top-of-waterfall-hint new-hint-settings processor :pspv-for-backtrack new-hints state)) (t (mv-let@par (jppl-flg new-pspv state) (waterfall-msg@par processor cl-id clause signal clauses new-hist ttree new-pspv state) (mv@par signal clauses new-hist new-pspv jppl-flg state)))))))) (mv@par step-limit signal clauses new-hist new-pspv jppl-flg state))))))) ; Section: FIND-APPLICABLE-HINT-SETTINGS ; Here we develop the code that recognizes that some user-supplied ; hint settings are applicable and we develop the routine to use ; hints. It all comes together in waterfall1. (defun@par find-applicable-hint-settings1 (cl-id clause hist pspv ctx hints hints0 wrld stable-under-simplificationp override-hints state) ; See translate-hints1 for "A note on the taxonomy of hints", which explains ; hint settings. Relevant background is also provided by :doc topic ; hints-and-the-waterfall, which links to :doc override-hints (also providing ; relevant background). ; We scan down hints looking for the first one that matches cl-id and clause. ; If we find none, we return nil. Otherwise, we return a pair consisting of ; the corresponding hint-settings and hints0 modified as directed by the hint ; that was applied. By "match" here, of course, we mean either ; (a) the hint is of the form (cl-id . hint-settings), or ; (b) the hint is of the form (eval-and-translate-hint-expression name-tree flg ; term) where term evaluates to a non-erroneous non-nil value when ID is bound ; to cl-id, CLAUSE to clause, WORLD to wrld, STABLE-UNDER-SIMPLIFICATIONP to ; stable-under-simplificationp, HIST to hist, PSPV to pspv, CTX to ctx, and ; STATE to state. In this case the corresponding hint-settings is the ; translated version of what the term produced. We know that term is ; single-threaded in state and returns an error triple. ; This function is responsible for interpreting computed hints, including the ; meaning of the :computed-hint-replacement keyword. It also deals ; appropriately with override-hints. Note that override-hints is ; (override-hints wrld). ; Stable-under-simplificationp is t when the clause has been found not to ; change when simplified. In particular, it is t if we are about to transition ; to destructor elimination. ; Optimization: By convention, when this function is called with ; stable-under-simplificationp = t, we know that the function returned nil when ; called earlier with the change: stable-under-simplificationp = nil. That is, ; if we know the clause is stable under simplification, then we have already ; tried and failed to find an applicable hint for it before we knew it was ; stable. So when stable-under-simplificationp is t, we avoid some work and ; just eval those hints that might be sensitive to ; stable-under-simplificationp. The flg component of (b)-style hints indicates ; whether the term contains the free variable stable-under-simplificationp. (cond ((null hints) (cond ((or (null override-hints) ; optimization for common case stable-under-simplificationp) ; avoid loop (value@par nil)) (t ; no applicable hint-settings, so apply override-hints to nil (er-let*@par ((new-keyword-alist (apply-override-hints@par override-hints cl-id clause hist pspv ctx wrld stable-under-simplificationp :OMITTED ; clause-list :OMITTED ; processor nil ; keyword-alist state)) (pair (cond (new-keyword-alist (translate-hint@par 'override-hints ; name-tree (cons (string-for-tilde-@-clause-id-phrase cl-id) new-keyword-alist) nil ; hint-type ctx wrld state)) (t (value@par nil))))) (value@par (cond (pair (cons (cdr pair) hints0)) (t nil))))))) ((eq (car (car hints)) 'eval-and-translate-hint-expression) (cond ((and stable-under-simplificationp (not (caddr (car hints)))) ; flg (find-applicable-hint-settings1@par cl-id clause hist pspv ctx (cdr hints) hints0 wrld stable-under-simplificationp override-hints state)) (t (er-let*@par ((hint-settings ; The following call applies the override-hints to the computed hint. (eval-and-translate-hint-expression@par (cdr (car hints)) cl-id clause wrld stable-under-simplificationp hist pspv :OMITTED ; clause-list :OMITTED ; processor :OMITTED ; keyword-alist nil override-hints ctx state))) (cond ((null hint-settings) (find-applicable-hint-settings1@par cl-id clause hist pspv ctx (cdr hints) hints0 wrld stable-under-simplificationp override-hints state)) ((eq (car hint-settings) :COMPUTED-HINT-REPLACEMENT) (value@par (cond ((eq (cadr hint-settings) nil) (cons (cddr hint-settings) (remove1-equal (car hints) hints0))) ((eq (cadr hint-settings) t) (cons (cddr hint-settings) hints0)) (t (cons (cddr hint-settings) (append (cadr hint-settings) (remove1-equal (car hints) hints0))))))) (t (value@par (cons hint-settings (remove1-equal (car hints) hints0))))))))) ((and (not stable-under-simplificationp) (consp (car hints)) (equal (caar hints) cl-id)) (cond ((null override-hints) (value@par (cons (cdar hints) (remove1-equal (car hints) hints0)))) ; Override-hints is (override-hints wrld). If override-hints is non-nil, then ; we originally translated the hint as (list* cl-id (:KEYWORD-ALIST ; . keyword-alist) (:NAME-TREE . name-tree) . hint-settings. We apply the ; override-hints to get a new keyword-alist. If the keyword-alist has not ; changed, then hint-settings is still the correct translation. Otherwise, we ; need to translate the new keyword-alist. The result could be a computed ; hint, in which case we simply replace the hint with that computed hint and ; call this function recursively. But if the result is not a computed hint, ; then it is a fully-translated hint with the same clause-id, and we have our ; result. ((not (let ((hint-settings (cdar hints))) (and (consp hint-settings) (consp (car hint-settings)) (eq (car (car hint-settings)) :KEYWORD-ALIST) (consp (cdr hint-settings)) (consp (cadr hint-settings)) (eq (car (cadr hint-settings)) :NAME-TREE)))) (er@par soft ctx "Implementation error: With override-hints present, we ~ expected an ordinary translated hint-settings to start ~ with ((:KEYWORD-ALIST . keyword-alist) (:NAME-TREE . ~ name-tree)) but instead the translated hint-settings was ~ ~x0. Please contact the ACL2 implementors." (cdar hints))) (t (let* ((full-hint-settings (cdar hints)) (keyword-alist (cdr (car full-hint-settings))) (name-tree (cdr (cadr full-hint-settings))) (hint-settings (cddr full-hint-settings))) (er-let*@par ((new-keyword-alist (apply-override-hints@par override-hints cl-id clause hist pspv ctx wrld stable-under-simplificationp :OMITTED ; clause-list :OMITTED ; processor keyword-alist state)) (hint-settings (cond ((equal new-keyword-alist keyword-alist) (value@par hint-settings)) ((null new-keyword-alist) ; optimization (value@par nil)) (t (er-let*@par ((pair (translate-hint@par name-tree (cons (string-for-tilde-@-clause-id-phrase cl-id) new-keyword-alist) nil ; hint-type ctx wrld state))) (assert$ (equal (car pair) cl-id) (value@par (cdr pair)))))))) (value@par (cond ((null new-keyword-alist) nil) (t (cons hint-settings (remove1-equal (car hints) hints0)))))))))) (t (find-applicable-hint-settings1@par cl-id clause hist pspv ctx (cdr hints) hints0 wrld stable-under-simplificationp override-hints state)))) (defun@par find-applicable-hint-settings (cl-id clause hist pspv ctx hints wrld stable-under-simplificationp state) ; First, we find the applicable hint-settings (with ; find-applicable-hint-settings1) from the explicit and computed hints. Then, ; we modify those hint-settings by using the override-hints; see :DOC ; override-hints. (find-applicable-hint-settings1@par cl-id clause hist pspv ctx hints hints wrld stable-under-simplificationp (override-hints wrld) state)) (defun@par thanks-for-the-hint (goal-already-printed-p hint-settings state) ; This function prints the note that we have noticed the hint. We have to ; decide whether the clause to which this hint was attached was printed out ; above or below us. We return state. Goal-already-printed-p is either t, ; nil, or a pair (cons :backtrack processor) where processor is a member of ; *preprocess-clause-ledge*. (declare (ignorable state)) (cond ((cdr (assoc-eq :no-thanks hint-settings)) (mv@par (delete-assoc-eq :no-thanks hint-settings) state)) ((alist-keys-subsetp hint-settings '(:backtrack)) (mv@par hint-settings state)) (t (pprogn@par (cond ((serial-first-form-parallel-second-form@par nil (member-equal (f-get-global 'waterfall-printing state) '(:limited :very-limited))) (state-mac@par)) (t (io?-prove@par (goal-already-printed-p) (fms "[Note: A hint was supplied for our processing of the goal ~ ~#0~[above~/below~/above, provided by a :backtrack hint ~ superseding ~@1~]. Thanks!]~%" (list (cons #\0 (case goal-already-printed-p ((t) 0) ((nil) 1) (otherwise 2))) (cons #\1 (and (consp goal-already-printed-p) (case (cdr goal-already-printed-p) (apply-top-hints-clause "the use of a :use, :by, :cases, :bdd, ~ :clause-processor, or :or hint") (preprocess-clause "preprocessing (the use of simple rules)") (simplify-clause "simplification") (eliminate-destructors-clause "destructor elimination") (fertilize-clause "the heuristic use of equalities") (generalize-clause "generalization") (eliminate-irrelevance-clause "elimination of irrelevance") (push-clause "the use of induction") (otherwise (er hard 'thanks-for-the-hint "Implementation error: Unrecognized ~ processor, ~x0." (cdr goal-already-printed-p))))))) (proofs-co state) state nil)))) (mv@par hint-settings state))))) ; We now develop the code for warning users about :USEing enabled ; :REWRITE and :DEFINITION rules. (defun lmi-name-or-rune (lmi) ; See also lmi-seed, which is similar except that it returns a base ; symbol where we are happy to return a rune, and when it returns a ; term we return nil. (cond ((atom lmi) lmi) ((eq (car lmi) :theorem) nil) ((or (eq (car lmi) :instance) (eq (car lmi) :functional-instance)) (lmi-name-or-rune (cadr lmi))) (t lmi))) (defun enabled-lmi-names1 (ens pairs) ; Pairs is the runic-mapping-pairs for some symbol, and hence each of ; its elements looks like (nume . rune). We collect the enabled ; :definition and :rewrite runes from pairs. (cond ((null pairs) nil) ((and (or (eq (cadr (car pairs)) :definition) (eq (cadr (car pairs)) :rewrite)) (enabled-numep (car (car pairs)) ens)) (add-to-set-equal (cdr (car pairs)) (enabled-lmi-names1 ens (cdr pairs)))) (t (enabled-lmi-names1 ens (cdr pairs))))) (defun enabled-lmi-names (ens lmi-lst wrld) (cond ((null lmi-lst) nil) (t (let ((x (lmi-name-or-rune (car lmi-lst)))) ; x is either nil, a name, or a rune (cond ((null x) (enabled-lmi-names ens (cdr lmi-lst) wrld)) ((symbolp x) (union-equal (enabled-lmi-names1 ens (getprop x 'runic-mapping-pairs nil 'current-acl2-world wrld)) (enabled-lmi-names ens (cdr lmi-lst) wrld))) ((enabled-runep x ens wrld) (add-to-set-equal x (enabled-lmi-names ens (cdr lmi-lst) wrld))) (t (enabled-lmi-names ens (cdr lmi-lst) wrld))))))) (defdoc using-enabled-rules ":Doc-Section Miscellaneous avoiding ~c[:use] ~il[hints] for ~il[enable]d ~c[:]~ilc[rewrite] rules~/ Consider the following (admittedly silly) example. ~bv[] (thm (equal (append (append x y) z) (append x y z)) :hints ((\"Subgoal *1/1\" :use cdr-cons))) ~ev[] ACL2's output includes the following warning. ~bv[] ACL2 Warning [Use] in ( THM ...): It is unusual to :USE an enabled :REWRITE or :DEFINITION rule, so you may want to consider disabling (:REWRITE CDR-CONS) in the hint provided for Subgoal *1/1. ~ev[] The warning is saying that if you leave the rewrite rule enabled, ACL2 may simplify away the hypothesis added by the ~c[:use] hint. We now explain this danger in more detail and show how disabling the rule can solve this problem.~/ Recall (~pl[hints]) how ~c[:use] ~il[hints] work. Such a hint specifies a formula, ~c[F], which is based on an existing lemma. Then the indicated goal, ~c[G], is replaced by the implication ~c[(implies F G)]. The intention is that the truth of ~c[F] will help in the simplification of ~c[G] to ~c[T] (true). The ``[Use]'' warning shown above is telling us of the danger that ~c[F] may be rewritten to ~c[T], reducing the implication above to ~c[(implies T G)] ~-[] thus, sadly, ~c[F] has disappeared and is not available to help with the simplification of ~c[G]. Consider the following tiny example. ~bv[] (defun p (x) (cons x x)) (defthm car-p (equal (car (p x)) x)) (in-theory (disable p (:type-prescription p))) (thm (implies (equal (p x1) (p x2)) (equal x1 x2)) :hints ((\"Goal\" :use ((:instance car-p (x x1)) (:instance car-p (x x2)))))) ~ev[] The proof of the final ~ilc[thm] form fails, because the new hypotheses are rewritten to ~c[t] using the ~c[:]~ilc[rewrite] rule ~c[CAR-P], in the manner described above. The following proof log shows the new hypotheses and their disappearance via rewriting. ~bv[] We augment the goal with the hypotheses provided by the :USE hint. These hypotheses can be derived from CAR-P via instantiation. We are left with the following subgoal. Goal' (IMPLIES (AND (EQUAL (CAR (P X1)) X1) (EQUAL (CAR (P X2)) X2)) (IMPLIES (EQUAL (P X1) (P X2)) (EQUAL X1 X2))). By the simple :rewrite rule CAR-P we reduce the conjecture to Goal'' (IMPLIES (EQUAL (P X1) (P X2)) (EQUAL X1 X2)). ~ev[] When we disable the rule ~c[CAR-P] as follows, the proof succeeds. ~bv[] (thm (implies (equal (p x1) (p x2)) (equal x1 x2)) :hints ((\"Goal\" :use ((:instance car-p (x x1)) (:instance car-p (x x2))) :in-theory (disable car-p)))) ~ev[] In general, then, a solution is to disable the rewrite rule that you are supplying in a ~c[:use] hint.~/") (defun@par maybe-warn-for-use-hint (pspv cl-id ctx wrld state) (cond ((warning-disabled-p "Use") (state-mac@par)) (t (let ((enabled-lmi-names (enabled-lmi-names (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :current-enabled-structure) (cadr (assoc-eq :use (access prove-spec-var pspv :hint-settings))) wrld))) (cond (enabled-lmi-names (warning$@par ctx ("Use") "It is unusual to :USE an enabled :REWRITE or :DEFINITION rule, so ~ you may want to consider disabling ~&0 in the hint provided for ~ ~@1. See :DOC using-enabled-rules." enabled-lmi-names (tilde-@-clause-id-phrase cl-id))) (t (state-mac@par))))))) (defun@par maybe-warn-about-theory-simple (ens1 ens2 ctx wrld state) ; We may use this function instead of maybe-warn-about-theory when we know that ; ens1 contains a compressed theory array (and so does ens2, but that should ; always be the case). (let ((force-xnume-en1 (enabled-numep *force-xnume* ens1)) (imm-xnume-en1 (enabled-numep *immediate-force-modep-xnume* ens1))) (maybe-warn-about-theory@par ens1 force-xnume-en1 imm-xnume-en1 ens2 ctx wrld state))) (defun@par maybe-warn-about-theory-from-rcnsts (rcnst1 rcnst2 ctx ens wrld state) (declare (ignore ens)) (let ((ens1 (access rewrite-constant rcnst1 :current-enabled-structure)) (ens2 (access rewrite-constant rcnst2 :current-enabled-structure))) (cond ((equal (access enabled-structure ens1 :array-name) (access enabled-structure ens2 :array-name)) ; We want to avoid printing a warning in those cases where we have not really ; created a new enabled structure. In this case, the enabled structures could ; still in principle be different, in which case we are missing some possible ; warnings. In practice, this function is only called when ens2 is either ; identical to ens1 or is created from ens1 by a call of ; load-theory-into-enabled-structure that creates a new array name, in which ; case the eql test above will fail. ; Warning: Through Version_4.1 we compared :array-name-suffix fields. But now ; that the waterfall can be parallelized, the suffix might not change when we ; install a new theory array; consider load-theory-into-enabled-structure in ; the case that its incrmt-array-name-info argument is a clause-id. (state-mac@par)) (t ; The new theory is being constructed from the user's hint and the ACL2 world. ; The most coherent thing to do is contruct the warning in an analogous manner, ; which is why we use ens below rather than ens1. (maybe-warn-about-theory-simple@par ens1 ens2 ctx wrld state))))) ; Essay on OR-HIT Messages ; When we generate an OR-HIT, we print a message saying it has ; happened and that we will sweep across the disjuncts and consider ; each in turn. That message is printed in ; apply-top-hints-clause-msg1. ; As we sweep, we print three kinds of messages: ; a: ``here is the next disjunct to consider'' ; b: ``this disjunct has succeeded and we won't consider the others'' ; c: ``we've finished the sweep and now we choose the best result'' ; Each is printed by a waterfall-or-hit-msg- function, labeled a, b, ; or c. (defun waterfall-or-hit-msg-a (cl-id user-hinti d-cl-id i branch-cnt state) ; We print out the message associated with one disjunctive branch. The special ; case of when there is exactly one branch is handled by ; apply-top-hints-clause-msg1. (cond ((gag-mode) ; Suppress printing for :OR splits; see also other comments with this header. ; In the case where we are only printing for gag-mode, we want to keep the ; message short. Our message relies on the disjunctive goal name starting with ; the word "Subgoal" so that the English is sensible. ; (fms "---~|Considering disjunctive ~@0 of ~@1.~|" ; (list (cons #\0 (tilde-@-clause-id-phrase d-cl-id)) ; (cons #\1 (tilde-@-clause-id-phrase cl-id))) ; (proofs-co state) ; state ; nil) state) (t (fms "---~%~@0~%( same formula as ~@1 ).~%~%The ~n2 disjunctive branch ~ (of ~x3) for ~@1 can be created by applying the hint:~%~y4.~%" (list (cons #\0 (tilde-@-clause-id-phrase d-cl-id)) (cons #\1 (tilde-@-clause-id-phrase cl-id)) (cons #\2 (list i)) (cons #\3 branch-cnt) (cons #\4 (cons (string-for-tilde-@-clause-id-phrase d-cl-id) user-hinti))) (proofs-co state) state nil)))) (defun waterfall-or-hit-msg-b (cl-id d-cl-id branch-cnt state) ; We print out the message that d-cl-id (and thus cl-id) has succeeded ; and that we will cut off the search through the remaining branches. (cond ((gag-mode) ; Suppress printing for :OR splits; see also other comments with this header. state) (t (fms "---~%~@0 has succeeded! All of its subgoals have been proved ~ (modulo any forced assumptions). Recall that it was one of ~ ~n1 disjunctive branches generated by the :OR hint to prove ~ ~@2. We therefore abandon work on the other branches.~%" (list (cons #\0 (tilde-@-clause-id-phrase d-cl-id)) (cons #\1 branch-cnt) (cons #\2 (tilde-@-clause-id-phrase cl-id))) (proofs-co state) state nil)))) (defun tilde-*-or-hit-summary-phrase1 (summary) (cond ((endp summary) nil) (t (let ((cl-id (car (car summary))) (subgoals (cadr (car summary))) (difficulty (caddr (car summary)))) (cons (msg "~@0~t1 ~c2 ~c3~%" (tilde-@-clause-id-phrase cl-id) 20 (cons subgoals 5) (cons difficulty 10)) (tilde-*-or-hit-summary-phrase1 (cdr summary))))))) (defun tilde-*-or-hit-summary-phrase (summary) ; Each element of summary is of the form (cl-id n d), where n is the ; number of subgoals pushed by the processing of cl-id and d is their ; combined difficulty. We prepare a ~* message that will print as ; a series of lines: ; disjunct subgoals estimated difficulty ; cl-id1 n d (list "" "~@*" "~@*" "~@*" (tilde-*-or-hit-summary-phrase1 summary))) (defun waterfall-or-hit-msg-c (parent-cl-id results revert-d-cl-id cl-id summary state) ; This message is printed when we have swept all the disjunctive ; branches and have chosen cl-id as the one to pursue. If results is ; empty, then cl-id and summary are all irrelevent (and probably nil). (cond ((null results) (cond (revert-d-cl-id (fms "---~%None of the branches we tried for ~@0 led to a plausible set ~ of subgoals, and at least one, ~@1, led to the suggestion that we ~ focus on the original conjecture. We therefore abandon our ~ previous work on this conjecture and reassign the name *1 to the ~ original conjecture. (See :DOC otf-flg.)~%" (list (cons #\0 (tilde-@-clause-id-phrase parent-cl-id)) (cons #\1 (tilde-@-clause-id-phrase revert-d-cl-id))) (proofs-co state) state nil)) (t (fms "---~%None of the branches we tried for ~@0 led to a plausible set ~ of subgoals. The proof attempt has failed.~|" (list (cons #\0 (tilde-@-clause-id-phrase parent-cl-id))) (proofs-co state) state nil)))) ((endp (cdr results)) (fms "---~%Even though we saw a disjunctive split for ~@0, it ~ turns out there is only one viable branch to pursue, the ~ one named ~@1.~%" (list (cons #\0 (tilde-@-clause-id-phrase parent-cl-id)) (cons #\1 (tilde-@-clause-id-phrase cl-id))) (proofs-co state) state nil)) (t (let* ((temp (assoc-equal cl-id summary)) (n (cadr temp)) (d (caddr temp))) (fms "---~%After simplifying every branch of the disjunctive ~ split for ~@0 we choose to pursue the branch named ~@1, ~ which gave rise to ~x2 *-named formula~#3~[s~/~/s~] ~ with total estimated difficulty of ~x4. The complete ~ list of branches considered is shown below.~%~%~ clause id subgoals estimated~%~ ~ pushed difficulty~%~*5" (list (cons #\0 (tilde-@-clause-id-phrase parent-cl-id)) (cons #\1 (tilde-@-clause-id-phrase cl-id)) (cons #\2 n) (cons #\3 (zero-one-or-more n)) (cons #\4 d) (cons #\5 (tilde-*-or-hit-summary-phrase summary))) (proofs-co state) state nil))))) ; Next we define a notion of the difficulty of a term and then grow it ; to define the difficulty of a clause and of a clause set. The ; difficulty of a term is (almost) the sum of all the level numbers of ; the functions in the term, plus the number of function applications. ; That is, suppose f and g have level-nos of i and j. Then the ; difficulty of (f (g x)) is i+j+2. However, the difficulty of (NOT ; (g x)) is just i+1 because we pretend the NOT (and its function ; application) is invisible. (mutual-recursion (defun term-difficulty1 (term wrld n) (cond ((variablep term) n) ((fquotep term) n) ((flambda-applicationp term) (term-difficulty1-lst (fargs term) wrld (term-difficulty1 (lambda-body term) wrld (1+ n)))) ((eq (ffn-symb term) 'not) (term-difficulty1 (fargn term 1) wrld n)) (t (term-difficulty1-lst (fargs term) wrld (+ 1 (get-level-no (ffn-symb term) wrld) n))))) (defun term-difficulty1-lst (lst wrld n) (cond ((null lst) n) (t (term-difficulty1-lst (cdr lst) wrld (term-difficulty1 (car lst) wrld n))))) ) (defun term-difficulty (term wrld) (term-difficulty1 term wrld 0)) ; The difficulty of a clause is the sum of the complexities of the ; literals. Note that we cannot have literals of difficulty 0 (except ; for something trivial like (NOT P)), so this measure will assign ; lower difficulty to shorter clauses, all other things being equal. (defun clause-difficulty (cl wrld) (term-difficulty1-lst cl wrld 0)) ; The difficulty of a clause set is similar. (defun clause-set-difficulty (cl-set wrld) (cond ((null cl-set) 0) (t (+ (clause-difficulty (car cl-set) wrld) (clause-set-difficulty (cdr cl-set) wrld))))) ; The difficulty of a pspv is the sum of the difficulties of the ; TO-BE-PROVED-BY-INDUCTION clause-sets in the pool of the pspv down ; (and not counting) the element element0. (defun pool-difficulty (element0 pool wrld) (cond ((endp pool) 0) ((equal (car pool) element0) 0) ((not (eq (access pool-element (car pool) :tag) 'TO-BE-PROVED-BY-INDUCTION)) 0) (t (+ (clause-set-difficulty (access pool-element (car pool) :clause-set) wrld) (pool-difficulty element0 (cdr pool) wrld))))) (defun how-many-to-be-proved (element0 pool) ; We count the number of elements tagged TO-BE-PROVED-BY-INDUCTION in ; pool until we get to element0. (cond ((endp pool) 0) ((equal (car pool) element0) 0) ((not (eq (access pool-element (car pool) :tag) 'TO-BE-PROVED-BY-INDUCTION)) 0) (t (+ 1 (how-many-to-be-proved element0 (cdr pool)))))) (defun pick-best-pspv-for-waterfall0-or-hit1 (results element0 wrld ans ans-difficulty summary) ; Results is a non-empty list of elements, each of which is of the ; form (cl-id . pspv) where pspv is a pspv corresponding to the ; complete waterfall processing of a single disjunct (of conjuncts). ; Inside the :pool of the pspv are a bunch of ; TO-BE-PROVED-BY-INDUCTION pool-elements. We have to decide which of ; the pspv's is the "best" one, i.e., the one to which we will commit ; to puruse by inductions. We choose the one that has the least ; difficulty. We measure the difficulty of the pool-elements until we ; get to element0. ; Ans is the least difficult result so far, or nil if we have not seen ; any yet. Ans-difficulty is the difficulty of ans (or nil). Note ; that ans is of the form (cl-id . pspv). ; We accumulate into summary some information that is used to report ; what we find. For each element of results we collect (cl-id n d), ; where n is the number of subgoals pushed by the cl-id processing and ; d is their combined difficulty. We return (mv choice summary), ; where choice is the final ans (cl-id . pspv). (cond ((endp results) (mv ans summary)) (t (let* ((cl-id (car (car results))) (pspv (cdr (car results))) (new-difficulty (pool-difficulty element0 (access prove-spec-var pspv :pool) wrld)) (new-summary (cons (list cl-id (how-many-to-be-proved element0 (access prove-spec-var pspv :pool)) new-difficulty) summary))) (if (or (null ans) (< new-difficulty ans-difficulty)) (pick-best-pspv-for-waterfall0-or-hit1 (cdr results) element0 wrld (car results) new-difficulty new-summary) (pick-best-pspv-for-waterfall0-or-hit1 (cdr results) element0 wrld ans ans-difficulty new-summary)))))) (defun pick-best-pspv-for-waterfall0-or-hit (results pspv0 wrld) ; Results is a non-empty list of elements, each of which is of the ; form (cl-id . pspv) where pspv is a pspv corresponding to the ; complete waterfall processing of a single disjunct (of conjuncts). ; Inside the :pool of the pspv are a bunch of ; TO-BE-PROVED-BY-INDUCTION pool-elements. We have to decide which of ; the pspv's is the "best" one, i.e., the one to which we will commit ; to puruse by inductions. We choose the one that has the least ; difficulty. ; We return (mv (cl-id . pspv) summary) where cl-id and pspv are the ; clause id and pspv of the least difficult result in results and ; summary is a list that summarizes all the results, namely a list of ; the form (cl-id n difficulty), where cl-id is the clause id of a ; disjunct, n is the number of subgoals the processing of that ; disjunct pushed into the pool, and difficulty is the difficulty of ; those subgoals. (pick-best-pspv-for-waterfall0-or-hit1 results ; We pass in the first element of the original pool as element0. This ; may be a BEING-PROVED-BY-INDUCTION element but is typically a ; TO-BE-PROVED-BY-INDUCTION element. In any case, we don't consider ; it or anything after it as we compute the difficulty. (car (access prove-spec-var pspv0 :pool)) wrld nil ; ans - none yet nil ; ans-difficulty - none yet nil ; summary )) (defun change-or-hit-history-entry (i hist cl-id) ; The first entry in hist is a history-entry of the form ; (make history-entry ; :signal 'OR-HIT ; :processor 'APPLY-TOP-HINTS-CLAUSE ; :clause clause ; :ttree ttree) ; where ttree contains an :OR tag with the value, val, ; (parent-cl-id NIL ((user-hint1 . hint-settings1) ...)) ; We smash the NIL to i. This indicates that along this branch of the ; history, we are dealing with user-hinti. Note that numbering starts ; at 1. ; While apply-top-hints-clause generates a ttree with a :or tagged ; object containing a nil, this function is used to replace that nil ; in the history of every branch by the particular i generating the ; branch. ; In the histories maintained by uninterrupted runs, you should never ; see an :OR tag with a nil in that slot. (Note carefully the use of ; the word "HISTORIES" above! It is possible to see ttrees with :OR ; tagged objects containing nil instead of a branch number. They get ; collected into the ttree inside the pspv that is following a clause ; around. But it is silly to inspect that ttree to find out the ; history of the clause, since you need the history for that.) ; The basic rule is that in a history recovered from an uninterupted ; proof attempt the entries with :signal OR-HIT will have a ttree with ; a tag :OR on an object (parent-cl-id i uhs-lst). (let* ((val (tagged-object :or (access history-entry (car hist) :ttree))) (parent-cl-id (nth 0 val)) (uhs-lst (nth 2 val))) (cons (make history-entry :signal 'OR-HIT :processor 'apply-top-hints-clause :clause (access history-entry (car hist) :clause) :ttree (add-to-tag-tree! :or (list parent-cl-id i uhs-lst) nil) :cl-id ; only needed for #+acl2-par, but harmless cl-id) (cdr hist)))) (defun@par pair-cl-id-with-hint-setting (cl-id hint-settings) ; Background: An :OR hint takes a list of n translated hint settings, ; at a clause with a clause id. It essentially copies the clause n ; times, gives it a new clause id (with a "Dk" suffix), and arranges ; for the waterfall to apply each hint settings to one such copy of ; the clause. The way it arranges that is to change the hints in the ; pspv so that the newly generated "Dk" clause ids are paired with ; their respective hints. But computed hints are different! A ; computed hint isn't paired with a clause id. It has it built into ; the form somewhere. Now generally the user created the form and we ; assume the user saw the subgoal with the "Dk" id of interest and ; entered it into his form. But we generate some computed hint forms ; -- namely custom keyword hints. And we're responsible for tracking ; the new clause ids to which they apply. ; Hint-settings is a translated hint-settings. That is, it is either ; a dotted alist pairing common hint keywords to their translated ; values or it is a computed hint form starting with ; eval-and-translate-hint-expression. In the former case, we produce ; (cl-id . hint-settings). In the latter case, we look for the ; possibility that the term we are to eval is a call of the ; custom-keyword-hint-interpreter and if so smash its cl-id field. (cond ((eq (car hint-settings) 'eval-and-translate-hint-expression) (cond ((custom-keyword-hint-in-computed-hint-form hint-settings) (put-cl-id-of-custom-keyword-hint-in-computed-hint-form@par hint-settings cl-id)) (t hint-settings))) (t (cons cl-id hint-settings)))) (defun apply-reorder-hint-front (indices len clauses acc) (cond ((endp indices) acc) (t (apply-reorder-hint-front (cdr indices) len clauses (cons (nth (- len (car indices)) clauses) acc))))) (defun apply-reorder-hint-back (indices current-index clauses acc) (cond ((endp clauses) acc) (t (apply-reorder-hint-back indices (1- current-index) (cdr clauses) (if (member current-index indices) acc (cons (car clauses) acc)))))) (defun filter-> (lst max) (cond ((endp lst) nil) ((> (car lst) max) (cons (car lst) (filter-> (cdr lst) max))) (t (filter-> (cdr lst) max)))) (defun@par apply-reorder-hint (pspv clauses ctx state) (let* ((hint-settings (access prove-spec-var pspv :hint-settings)) (hint-setting (assoc-eq :reorder hint-settings)) (indices (cdr hint-setting)) (len (length clauses))) (cond (indices (cond ((filter-> indices len) (mv-let@par (erp val state) (er@par soft ctx "The following subgoal indices given by a :reorder ~ hint exceed the number of subgoals, which is ~x0: ~ ~ ~&1." len (filter-> indices len)) (declare (ignore val)) (mv@par erp nil nil state))) (t (mv@par nil hint-setting (reverse (apply-reorder-hint-back indices len clauses (apply-reorder-hint-front indices len clauses nil))) state)))) (t (mv@par nil nil clauses state))))) ; This completes the preliminaries for hints and we can get on with the ; waterfall itself soon. But first we provide additional rw-cache support (see ; the Essay on Rw-cache). #+acl2-par (defun pspv-equal-except-for-tag-tree-and-pool (x y) ; Warning: Keep this in sync with prove-spec-var. The only fields not ; addressed here should be the :tag-tree and :pool fields. (and (equal (access prove-spec-var x :rewrite-constant) (access prove-spec-var y :rewrite-constant)) (equal (access prove-spec-var x :induction-hyp-terms) (access prove-spec-var y :induction-hyp-terms)) (equal (access prove-spec-var x :induction-concl-terms) (access prove-spec-var y :induction-concl-terms)) (equal (access prove-spec-var x :hint-settings) (access prove-spec-var y :hint-settings)) (equal (access prove-spec-var x :gag-state) (access prove-spec-var y :gag-state)) (equal (access prove-spec-var x :user-supplied-term) (access prove-spec-var y :user-supplied-term)) (equal (access prove-spec-var x :displayed-goal) (access prove-spec-var y :displayed-goal)) (equal (access prove-spec-var x :orig-hints) (access prove-spec-var y :orig-hints)) (equal (access prove-spec-var x :otf-flg) (access prove-spec-var y :otf-flg)) ; One can uncomment the following equal test to witness that the comparison is ; indeed actually occurring and causing a hard error upon failure. ; (equal (access prove-spec-var x :tag-tree) ; (access prove-spec-var y :tag-tree)) )) #+acl2-par (defun list-extensionp-aux (rev-base rev-extension) (assert$ (<= (length rev-base) (length rev-extension)) (if (atom rev-base) t (and (equal (car rev-base) (car rev-extension)) (list-extensionp-aux (cdr rev-base) (cdr rev-extension)))))) #+acl2-par (defun list-extensionp (base extension) (cond ((> (length base) (length extension)) nil) (t (list-extensionp-aux (reverse base) (reverse extension))))) #+acl2-par (defun find-list-extensions (base extension acc) (if (or (endp extension) (equal extension base)) (reverse acc) (find-list-extensions base (cdr extension) (cons (car extension) acc)))) #+acl2-par (defun combine-pspv-pools (base x y debug-pspv) (prog2$ (if debug-pspv (with-output-lock (cw "Combining base: ~x0 with x: ~%~x1 and with y: ~%~x2~%" base x y)) nil) (append (find-list-extensions base y nil) (find-list-extensions base x nil) base))) #+acl2-par (defun combine-pspv-tag-trees (x y) ; We reverse the arguments, because y was generated after x in time (in the ; serial case). And since accumulating into a tag-tree is akin to pushing onto ; the front of a list, y is the first argument to the "cons". (cons-tag-trees-rw-cache y x)) #+acl2-par (defun print-pspvs (base x y debug-pspv) (if debug-pspv (progn$ (cw "Base is:~% ~x0~%" base) (cw "X is:~% ~x0~%" X) (cw "Y is:~% ~x0~%" Y)) nil)) #+acl2-par (defun combine-prove-spec-vars (base x y ctx debug-pspv signal1 signal2) ; X and Y should be extensions of the base. That is, every member of base ; should be in both x and y. Also, note that switching the order of x and y ; returns a result that means something different. The order with which we ; combine pspv's matters. (assert$ ; We check that the signals aren't abort. This way we know that we are in case ; (1), as described in "Essay on prove-spec-var pool modifications". We also ; know that this assertion is always true from just examining the code. (and (not (equal signal1 'abort)) (not (equal signal2 'abort))) (cond ; We first test to make sure that the pspv's were only changed in the two ; fields that we know how to combine. ((not (pspv-equal-except-for-tag-tree-and-pool x y)) (prog2$ (print-pspvs base x y debug-pspv) (er hard ctx "Implementation error: waterfall1 returns the pspv changed in a way ~ other than the :tag-tree and :pool fields."))) (t (change prove-spec-var x :tag-tree (combine-pspv-tag-trees (access prove-spec-var x :tag-tree) (access prove-spec-var y :tag-tree)) :pool (combine-pspv-pools (access prove-spec-var base :pool) (access prove-spec-var x :pool) (access prove-spec-var y :pool) debug-pspv)))))) ; The following form may be helpful for tracing waterfall1-lst in an effort to ; understand how the pspv's :pool is modified. ; (trace$ ; (waterfall1-lst ; :entry (list 'waterfall1-lst ; 'clauses= ; clauses ; 'pspv-pool= ; (access prove-spec-var pspv :pool)) ; :exit (list 'waterfall1-lst ; 'signal= ; (cadr values) ; 'pspv-pool= ; (access prove-spec-var (caddr values) :pool)))) #+acl2-par (defun speculative-execution-valid (x y) ; This function aids in determining whether a speculative evaluation of the ; waterfall is valid. Typically, X is the pspv given to the current call of ; waterfall1-lst, and Y is the pspv returned from calling waterfall1 on the ; first clause. ; For now, if anything but the tag-tree or pool is different, we want to ; immediately return nil, because we don't know how to handle the combining of ; such pspv's. (assert$ (pspv-equal-except-for-tag-tree-and-pool x y) t)) #+acl2-par (defun abort-will-occur-in-pool (pool) ; Returns t if the given pool requires that we abort the current set of subgoal ; proof attempts and revert to prove the original conjecture by induction. The ; function must only consider the case where 'maybe-to-be-proved-by-induction ; tags are present, because push-clause[@par] handles all other cases. ; If you change this function, evaluate the following form. If the result is ; an error, then either modify the form below or fix the change. ; (assert$ ; (and ; (not (abort-will-occur-in-pool '((maybe-to-be-proved-by-induction sub orig)))) ; (abort-will-occur-in-pool '((maybe-to-be-proved-by-induction sub orig) ; (to-be-proved-by-induction) ; (to-be-proved-by-induction))) ; (not (abort-will-occur-in-pool '((to-be-proved-by-induction)))) ; (not (abort-will-occur-in-pool '((to-be-proved-by-induction) ; (maybe-to-be-proved-by-induction sub orig)))) ; (not (abort-will-occur-in-pool '((maybe-to-be-proved-by-induction sub orig)))) ; (not (abort-will-occur-in-pool '((to-be-proved-by-induction) ; (to-be-proved-by-induction) ; (to-be-proved-by-induction)))) ; (abort-will-occur-in-pool '((maybe-to-be-proved-by-induction sub orig) ; (to-be-proved-by-induction) ; (to-be-proved-by-induction) ; (maybe-to-be-proved-by-induction sub2 orig2))) ; (abort-will-occur-in-pool '((to-be-proved-by-induction a) ; (maybe-to-be-proved-by-induction sub orig) ; (to-be-proved-by-induction b) ; (to-be-proved-by-induction c) ; (maybe-to-be-proved-by-induction sub2 orig2)))) ; "abort-will-occur-in-pool tests passed") (cond ((atom pool) nil) ((and (equal (caar pool) 'maybe-to-be-proved-by-induction) (consp (cdr pool))) t) (t (abort-will-occur-in-pool (cdr pool))))) #+acl2-par (defrec maybe-to-be-proved-by-induction ; Important Note: This record is laid out this way so that we can use assoc-eq ; on the pspv pool to detect the presence of a maybe-to-be-proved-by-induction ; tag. Do not move the key field! (key subgoal original) t) #+acl2-par (defun convert-maybes-to-tobe-subgoals (pool) ; This function converts all 'maybe-to-be-proved-by-induction records to ; 'to-be-proved-by-induction pool-elements. Since this function is only called ; in the non-abort case, it uses the :subgoal field of the record. (cond ((atom pool) nil) ((equal (caar pool) 'maybe-to-be-proved-by-induction) (cons (access maybe-to-be-proved-by-induction (car pool) :subgoal) (convert-maybes-to-tobe-subgoals (cdr pool)))) (t (cons (car pool) (convert-maybes-to-tobe-subgoals (cdr pool)))))) #+acl2-par (defun convert-maybes-to-tobes (pool) ; This function converts a pool that contains 'maybe-to-be-proved-by-induction ; records to a pool that either (1) aborts and proves the :original conjecture ; or (2) replaces all such clauses with their :subgoal ; 'to-be-proved-by-induction pool-element. This function outsources all ; thinking about whether we are in an abort case to the function ; abort-will-occur-in-pool. ; If you change this function, evaluate the following form. If the result is ; an error, then either modify the form below or fix the change. ; (assert$ ; (and ; (equal (convert-maybes-to-tobes '((maybe-to-be-proved-by-induction sub orig))) ; '(sub)) ; (equal ; (convert-maybes-to-tobes '((maybe-to-be-proved-by-induction sub orig) ; (to-be-proved-by-induction) ; (to-be-proved-by-induction))) ; '(orig)) ; (equal ; (convert-maybes-to-tobes '((to-be-proved-by-induction))) ; '((to-be-proved-by-induction))) ; (equal (convert-maybes-to-tobes '((to-be-proved-by-induction) ; (maybe-to-be-proved-by-induction sub orig))) ; '((to-be-proved-by-induction) ; sub)) ; (equal (convert-maybes-to-tobes '((maybe-to-be-proved-by-induction sub orig))) ; '(sub)) ; (equal (convert-maybes-to-tobes '((maybe-to-be-proved-by-induction sub orig) ; (to-be-proved-by-induction) ; (to-be-proved-by-induction) ; (maybe-to-be-proved-by-induction sub2 orig2))) ; '(orig)) ; (equal (convert-maybes-to-tobes '((to-be-proved-by-induction a) ; (maybe-to-be-proved-by-induction sub orig) ; (to-be-proved-by-induction b) ; (to-be-proved-by-induction c) ; (maybe-to-be-proved-by-induction sub2 ; orig2))) ; '(orig)) ; (equal (convert-maybes-to-tobes ; '((maybe-to-be-proved-by-induction sub1 orig) ; (to-be-proved-by-induction a) ; (maybe-to-be-proved-by-induction sub2 orig) ; (to-be-proved-by-induction b) ; (to-be-proved-by-induction c) ; (maybe-to-be-proved-by-induction sub3 orig))) ; '(orig)) ; ) ; "convert-maybes-to-tobes tests worked." ; ) (cond ((atom pool) nil) ((abort-will-occur-in-pool pool) (list (access maybe-to-be-proved-by-induction ; It doesn't matter whether we use the first 'maybe-to-be-proved-by-induction ; tag to cause an abort, because the :original field will be the same for all ; of them. (assoc-eq 'maybe-to-be-proved-by-induction pool) :original))) (t (convert-maybes-to-tobe-subgoals pool)))) #+acl2-par (defun convert-maybes-to-tobes-in-pspv (pspv) (change prove-spec-var pspv :pool (convert-maybes-to-tobes (access prove-spec-var pspv :pool)))) ; This completes the preliminaries for hints and we can get on with the ; waterfall itself soon. But first we provide additional rw-cache support (see ; the Essay on Rw-cache). (defun erase-rw-cache-any-tag-from-pspv (pspv) ; We maintain the invariant that the "nil" cache is contained in the "any" ; cache. (let ((ttree (access prove-spec-var pspv :tag-tree))) (cond ((tagged-objectsp 'rw-cache-any-tag ttree) (change prove-spec-var pspv :tag-tree (rw-cache-enter-context ttree))) (t pspv)))) (defun restore-rw-cache-state-in-pspv (new-pspv old-pspv) (let* ((old-rcnst (access prove-spec-var old-pspv :rewrite-constant)) (old-rw-cache-state (access rewrite-constant old-rcnst :rw-cache-state)) (new-rcnst (access prove-spec-var new-pspv :rewrite-constant)) (new-rw-cache-state (access rewrite-constant new-rcnst :rw-cache-state))) (cond ((eq old-rw-cache-state new-rw-cache-state) new-pspv) (t (change prove-spec-var new-pspv :rewrite-constant (change rewrite-constant new-rcnst :rw-cache-state old-rw-cache-state)))))) #+(and acl2-par (not acl2-loop-only)) (defvar *waterfall-parallelism-timings-ht-alist* nil "Association list of hashtables, where the key is the name of a theorem attempted with a defthm, and the value is a hashtable mapping from clause-ids to the time it took to prove that clause.") #+(and acl2-par (not acl2-loop-only)) (defvar *waterfall-parallelism-timings-ht* nil "This variable supports the :resource-and-timing-based mode for waterfall parallelism. It can contain the hashtable that associates waterfall-parallelism timings with each clause-id. This variable should always be nil unless ACL2(p) is in the middle of attempting a proof initiated by the user with a defthm.") #+acl2-par (defun setup-waterfall-parallelism-ht-for-name (name) #-acl2-loop-only (let ((curr-ht (assoc-eq name *waterfall-parallelism-timings-ht-alist*))) (cond ((null curr-ht) (let ((new-ht (make-hash-table :test 'equal :size (expt 2 13) ; Parallelism blemish: CCL locks these hashtable operations automatically ; because of the argument :shared t below. However in SBCL and LispWorks, we ; should really lock these hashtable operations ourselves. Note that the SBCL ; documentation at http://www.sbcl.org/manual/Hash-Table-Extensions.html ; describes a keyword, :synchronized, that is like CCL's :shared but is labeled ; as "experimental". At any rate, we are willing to take our chances for now ; with SBCL and Lispworks. #+ccl :shared #+ccl t))) (setf *waterfall-parallelism-timings-ht-alist* (acons name new-ht (take 4 *waterfall-parallelism-timings-ht-alist*))) (setf *waterfall-parallelism-timings-ht* new-ht))) (t (setf *waterfall-parallelism-timings-ht* (cdr curr-ht))))) name) #+acl2-par (defun clear-current-waterfall-parallelism-ht () #-acl2-loop-only (setf *waterfall-parallelism-timings-ht* nil) t) #+acl2-par (defun flush-waterfall-parallelism-hashtables () #-acl2-loop-only (progn (setf *waterfall-parallelism-timings-ht-alist* nil) (setf *waterfall-parallelism-timings-ht* nil)) t) #+(and acl2-par (not acl2-loop-only)) (defun save-waterfall-timings-for-cl-id (key value) (when *waterfall-parallelism-timings-ht* (setf (gethash key *waterfall-parallelism-timings-ht*) value)) value) #+(and acl2-par (not acl2-loop-only)) (defun lookup-waterfall-timings-for-cl-id (key) (mv-let (val found) (cond (*waterfall-parallelism-timings-ht* (gethash key *waterfall-parallelism-timings-ht*)) (t (mv nil nil))) (declare (ignore found)) (or val 0))) (defmacro waterfall1-wrapper (form) ; We create a non-@par version of waterfall1-wrapper that is the identity, so ; we can later have an @par version that does something important for the ; parallel case. In the #-acl2-par case, or the serial case, ; waterfall1-wrapper will have no effect, returning its argument unchanged. ; For a discussion about such matters, see the long comment in *@par-mappings*. form) #+(and acl2-par (not acl2-loop-only)) (defparameter *acl2p-starting-proof-time* 0.0d0) #+acl2-par (defun waterfall1-wrapper@par-before (cl-id state) (case (f-get-global 'waterfall-printing state) (:limited (and (print-clause-id-okp cl-id) (with-output-lock ; Parallelism blemish: Kaufmann suggests that we need to skip printing ; clause-ids that have already been printed. Note that using the printing of ; clause-ids to show that the prover is still making progress is no longer the ; default setting (see :doc set-waterfall-printing). This is a low-priority ; blemish, because as of 2012-07, the main ACL2 users use the :very-limited ; setting for waterfall-printing -- this setting only prints periods, not ; clause-ids. Example: ; (set-waterfall-parallelism :pseudo-parallel) ; (set-waterfall-printing :limited) ; (thm (implies (equal x y) (equal u v))) ; Parallelism blemish: here, and at many other ACL2(p)-specific places, consider ; using observation-cw or printing that can be inhibited, instead of cw. We ; have not tried this so far because observation-cw calls wormhole, which is ; problematic (see the comment in waterfall-print-clause-id@par). #+acl2-loop-only nil #-acl2-loop-only (format t "At time ~,6f sec, starting: ~a~%" (/ (- (get-internal-real-time) *acl2p-starting-proof-time*) internal-time-units-per-second) (string-for-tilde-@-clause-id-phrase cl-id))))) (:very-limited (with-output-lock (cw "."))) (otherwise nil))) #+acl2-par (defun waterfall1-wrapper@par-after (cl-id start-time state) #+acl2-loop-only (declare (ignore start-time cl-id)) #-acl2-loop-only (save-waterfall-timings-for-cl-id cl-id (- (get-internal-real-time) ; end time start-time)) (cond ((f-get-global 'waterfall-printing-when-finished state) (cond ((equal (f-get-global 'waterfall-printing state) :very-limited) (with-output-lock (cw ","))) ((equal (f-get-global 'waterfall-printing state) :limited) (with-output-lock #+acl2-loop-only nil #-acl2-loop-only (format t "At time ~,6f sec, finished: ~a~%" (/ (- (get-internal-real-time) *acl2p-starting-proof-time*) internal-time-units-per-second) (string-for-tilde-@-clause-id-phrase cl-id)))) (t nil))) (t nil))) #+acl2-par (defmacro waterfall1-wrapper@par (&rest form) `(let ((start-time #+acl2-loop-only 'ignored-value #-acl2-loop-only (get-internal-real-time))) (prog2$ (waterfall1-wrapper@par-before cl-id state) (mv-let@par (step-limit signal pspv jppl-flg state) ,@form (prog2$ (waterfall1-wrapper@par-after cl-id start-time state) (mv@par step-limit signal pspv jppl-flg state)))))) #+acl2-par (defun increment-waterfall-parallelism-counter (abbreviated-symbol) (case abbreviated-symbol ((resource-and-timing-parallel) #-acl2-loop-only (incf *resource-and-timing-based-parallelizations*) 'parallel) ((resource-and-timing-serial) #-acl2-loop-only (incf *resource-and-timing-based-serializations*) 'serial) ((resource-parallel) #-acl2-loop-only (incf *resource-based-parallelizations*) 'parallel) ((resource-serial) #-acl2-loop-only (incf *resource-based-serializations*) 'serial) (otherwise (er hard 'increment-waterfall-parallelism-counter "Illegal value ~x0 was given to ~ increment-waterfall-parallelism-counter" abbreviated-symbol)))) #+acl2-par (defun halves-with-length (clauses) ; Returns (mv first-half second-half len), where clauses is split into the ; indicated halves (maintaining the order of the input list), and len is the ; length of the first half. (declare (xargs :guard (true-listp clauses))) (let* ((len (length clauses)) (split-point (ceiling (/ len 2) 1)) (first-half (take split-point clauses)) (second-half (nthcdr split-point clauses))) (mv first-half second-half split-point))) (mutual-recursion@par (defun@par waterfall1 (ledge cl-id clause hist pspv hints suppress-print ens wrld ctx state step-limit) ; ledge - In general in this mutually recursive definition, the ; formal "ledge" is any one of the waterfall ledges. But ; by convention, in this function, waterfall1, it is ; always either the 'apply-top-hints-clause ledge or ; the next one, 'preprocess-clause. Waterfall1 is the ; place in the waterfall that hints are applied. ; Waterfall0 is the straightforward encoding of the ; waterfall, except that every time it sends clauses back ; to the top, it send them to waterfall1 so that hints get ; used again. ; cl-id - the clause id for clause. ; clause - the clause to process ; hist - the history of clause ; pspv - an assortment of special vars that any clause processor might ; change ; hints - an alist mapping clause-ids to hint-settings. ; wrld - the current world ; state - the usual state ; step-limit - the new step-limit. ; We return 5 values. The first is a new step-limit. The second is a "signal" ; and is one of 'abort, 'error, or 'continue. The last three returned values ; are the final values of pspv, the jppl-flg for this trip through the falls, ; and state. The 'abort signal is used by our recursive processing to ; implement aborts from below. When an abort occurs, the clause processor that ; caused the abort sets the pspv and state as it wishes the top to see them. ; When the signal is 'error, the error message has been printed. The returned ; pspv is irrelevant (and typically nil). (mv-let@par (erp pair state) (find-applicable-hint-settings@par cl-id clause hist pspv ctx hints wrld nil state) ; If no error occurs and pair is non-nil, then pair is of the form ; (hint-settings . hints') where hint-settings is the hint-settings ; corresponding to cl-id and clause and hints' is hints with the appropriate ; element removed. (cond (erp ; This only happens if some hint function caused an error, e.g., by ; generating a hint that would not translate. The error message has been ; printed and pspv is irrelevant. We pass the error up. (mv@par step-limit 'error nil nil state)) (t (sl-let@par (signal new-pspv jppl-flg state) (cond ((null pair) ; There was no hint. (pprogn@par ; In the #+acl2-par version of the waterfall, with global waterfall-printing ; set to :limited, the need to print the clause on a checkpoint is taken care ; of inside waterfall-msg@par. (waterfall-print-clause@par suppress-print cl-id clause state) (waterfall0@par ledge cl-id clause hist pspv hints ens wrld ctx state step-limit))) (t (waterfall0-with-hint-settings@par (car pair) ledge cl-id clause hist pspv (cdr pair) suppress-print ens wrld ctx state step-limit))) (let ((pspv (cond ((null pair) (restore-rw-cache-state-in-pspv new-pspv pspv)) (t new-pspv)))) (mv-let@par (pspv state) (cond ((or (eq signal 'miss) (eq signal 'error)) (mv@par pspv state)) (t (gag-state-exiting-cl-id@par signal cl-id pspv state))) (mv@par step-limit signal pspv jppl-flg state)))))))) (defun@par waterfall0-with-hint-settings (hint-settings ledge cl-id clause hist pspv hints goal-already-printedp ens wrld ctx state step-limit) ; We ``install'' the hint-settings given and call waterfall0 on the ; rest of the arguments. (mv-let@par (hint-settings state) (thanks-for-the-hint@par goal-already-printedp hint-settings state) (pprogn@par (waterfall-print-clause@par goal-already-printedp cl-id clause state) (mv-let@par (erp new-pspv-1 state) (load-hint-settings-into-pspv@par t hint-settings pspv cl-id wrld ctx state) (cond (erp (mv@par step-limit 'error pspv nil state)) (t (pprogn@par (maybe-warn-for-use-hint@par new-pspv-1 cl-id ctx wrld state) (maybe-warn-about-theory-from-rcnsts@par (access prove-spec-var pspv :rewrite-constant) (access prove-spec-var new-pspv-1 :rewrite-constant) ctx ens wrld state) ; If there is no :INDUCT hint, then the hint-settings can be handled by ; modifying the clause and the pspv we use subsequently in the falls. (sl-let@par (signal new-pspv new-jppl-flg state) (waterfall0@par (cond ((assoc-eq :induct hint-settings) '(push-clause)) (t ledge)) cl-id clause hist new-pspv-1 hints ens wrld ctx state step-limit) (mv@par step-limit signal (restore-hint-settings-in-pspv new-pspv pspv) new-jppl-flg state))))))))) (defun@par waterfall0 (ledge cl-id clause hist pspv hints ens wrld ctx state step-limit) (sl-let@par (signal clauses new-hist new-pspv new-jppl-flg state) (cond ((null ledge) ; The only way that the ledge can be nil is if the push-clause at the ; bottom of the waterfall signalled 'MISS. This only happens if ; push-clause found a :DO-NOT-INDUCT name hint. That being the case, ; we want to act like a :BY name' hint was attached to that clause, ; where name' is the result of extending the supplied name with the ; clause id. This fancy call of waterfall-step is just a cheap way to ; get the standard :BY name' processing to happen. All it will do is ; add a :BYE (name' . clause) to the tag-tree of the new-pspv. We ; know that the signal returned will be a "hit". Because we had to smash ; the hint-settings to get this to happen, we'll have to restore them ; in the new-pspv. (waterfall-step@par 'apply-top-hints-clause cl-id clause hist (change prove-spec-var pspv :hint-settings (list (cons :by (convert-name-tree-to-new-name (cons (cdr (assoc-eq :do-not-induct (access prove-spec-var pspv :hint-settings))) (string-for-tilde-@-clause-id-phrase cl-id)) wrld)))) wrld ctx state step-limit)) ((eq (car ledge) 'eliminate-destructors-clause) (mv-let@par (erp pair state) (find-applicable-hint-settings@par ; stable-under-simplificationp=t cl-id clause hist pspv ctx hints wrld t state) (cond (erp ; A hint generated an error. The error message has been printed and ; we pass the error up. The other values are all irrelevant. #+acl2-par (assert$ ; At one time, the waterfall returned Context Message Pairs. This assertion ; was subsequently added to check that we no longer do so. Since it's an ; inexpensive check, we leave it here. (not pair) (mv@par step-limit 'error nil nil nil nil state)) #-acl2-par (mv@par step-limit 'error nil nil nil nil state)) (pair ; A hint was found. The car of pair is the new hint-settings and the ; cdr of pair is the new value of hints. We need to arrange for ; waterfall0-with-hint-settings to be called. But we are inside ; mv-let binding signal, etc., above. We generate a fake ``signal'' ; to get out of here and handle it below. (mv@par step-limit 'top-of-waterfall-hint (car pair) hist (cdr pair) nil state)) ; Otherwise no hint was applicable. We do exactly the same thing we would have ; done had (car ledge) not been 'eliminate-destructors-clause, after checking ; whether we should make a desperate final attempt to simplify, with caching ; turned off. Keep these two calls of waterfall-step in sync! ((eq (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :rw-cache-state) t) ; We return an updated pspv, together with a bogus signal indicating that we ; are to make a "desperation" run through the simplifier with the rw-cache ; disabled. The nil values returned below will be ignored. (mv@par step-limit 'top-of-waterfall-avoid-rw-cache nil nil (set-rw-cache-state-in-pspv (erase-rw-cache-from-pspv pspv) :disabled) nil state)) ((member-eq (car ledge) (assoc-eq :do-not (access prove-spec-var pspv :hint-settings))) (mv@par step-limit 'miss nil hist pspv nil state)) (t (waterfall-step@par (car ledge) cl-id clause hist pspv wrld ctx state step-limit))))) ((member-eq (car ledge) (assoc-eq :do-not (access prove-spec-var pspv :hint-settings))) (mv@par step-limit 'miss nil hist pspv nil state)) (t (waterfall-step@par (car ledge) cl-id clause hist pspv wrld ctx state step-limit))) (cond ((eq signal 'OR-HIT) ; A disjunctive, hint-driven split has been requested by an :OR hint. ; Clauses is a singleton containing the clause to which we are to ; apply all of the hints. The hints themselves are recorded in the ; first entry of the new-hist, which necessarily has the form ; (make history-entry ; :signal 'OR-HIT ; :processor 'APPLY-TOP-HINTS-CLAUSE ; :clause clause ; :ttree ttree) ; where ttree contains an :OR tag with the value, val, ; (parent-cl-id NIL ((user-hint1 . hint-settings1) ...)) ; Note that we are guaranteed here that (nth 1 val) is NIL. That is ; because that's what apply-top-hints-clause puts into its ttree. ; It will be replaced along every history by the appropriate i. ; We recover this crucial data first. (let* ((val (tagged-object :or (access history-entry (car new-hist) :ttree))) ; (parent-cl-id (nth 0 val)) ;;; same as our cl-id! (uhs-lst (nth 2 val)) (branch-cnt (length uhs-lst))) ; Note that user-hinti is what the user wrote and hint-settingsi is ; the corresponding translation. For each i we are going to act just ; like the user supplied the given hint for the parent. Thus the ; waterfall will act like it saw parent n times, once for each ; user-hinti. ; For example, if the original :or hint was ; ("Subgoal 3" :OR ((:use lemma1 :in-theory (disable lemma1)) ; (:use lemma2 :in-theory (disable lemma2)))) ; ; then we will act just as though we saw "Subgoal 3" twice, ; once with the hint ; ("Subgoal 3" :use lemma1 :in-theory (disable lemma1)) ; and then again with the hint ; ("Subgoal 3" :use lemma2 :in-theory (disable lemma2)). ; except that we give the two occurrences of "Subgoal 3" different ; names for sanity. (waterfall0-or-hit@par ledge cl-id (assert$ (and (consp clauses) (null (cdr clauses))) (car clauses)) new-hist new-pspv hints ens wrld ctx state uhs-lst 1 branch-cnt nil nil step-limit))) (t (let ((new-pspv (if (and (null ledge) (not (eq signal 'error))) ; If signal is 'error, then new-pspv is nil (e.g., see the error ; behavior of waterfall step) and we wish to avoid manipulating a ; bogus pspv. (restore-hint-settings-in-pspv new-pspv pspv) new-pspv))) (cond ((eq signal 'top-of-waterfall-hint) ; This fake signal just means that either we have found an applicable hint for ; a clause that was stable under simplification (stable-under-simplificationp = ; t), or that we have found an applicable :backtrack hint. (mv-let (hint-settings hints pspv goal-already-printedp) (cond ((eq new-pspv :pspv-for-backtrack) ; The variable named clauses is holding the hint-settings. (mv clauses (append new-jppl-flg ; new-hints hints) ; We will act as though we have just discovered the hint-settings and leave it ; up to waterfall0-with-hint-settings to restore the pspv if necessary after ; trying those hint-settings. Note that the rw-cache is restored (as part of ; the tag-tree, which is part of the rewrite-constant of the pspv). (change prove-spec-var pspv :hint-settings (delete-assoc-eq :backtrack (access prove-spec-var pspv :hint-settings))) (cons :backtrack new-hist) ; see thanks-for-the-hint )) (t ; The variables named clauses and new-pspv are holding the hint-settings and ; hints in this case. We reenter the top of the falls with the new hint ; setting and hints. (mv clauses new-pspv pspv t))) (waterfall0-with-hint-settings@par hint-settings *preprocess-clause-ledge* cl-id clause ; Simplify-clause contains an optimization that lets us avoid resimplifying ; the clause if the most recent history entry is settled-down-clause and ; the induction hyp and concl terms don't occur in it. We short-circuit that ; short-circuit by removing the settled-down-clause entry if it is the most ; recent. (cond ((and (consp hist) (eq (access history-entry (car hist) :processor) 'settled-down-clause)) (cdr hist)) (t hist)) pspv hints goal-already-printedp ens wrld ctx state step-limit))) ((eq signal 'top-of-waterfall-avoid-rw-cache) ; New-pspv already has the rw-cache disabled. Pop up to simplify-clause. The ; next waterfall-step, which will be a simplify-clause step unless a :do-not ; hint prohibits that, will re-enable the rw-cache. (waterfall0@par *simplify-clause-ledge* cl-id clause hist new-pspv hints ens wrld ctx state step-limit)) ((eq signal 'error) (mv@par step-limit 'error nil nil state)) ((eq signal 'abort) (mv@par step-limit 'abort new-pspv new-jppl-flg state)) ((eq signal 'miss) (if ledge (waterfall0@par (cdr ledge) cl-id clause new-hist ; used because of specious entries new-pspv hints ens wrld ctx state step-limit) (mv@par step-limit (er hard 'waterfall0 "The empty ledge signalled 'MISS! This can only ~ happen if we changed APPLY-TOP-HINTS-CLAUSE so that ~ when given a single :BY name hint it fails to hit.") nil nil state))) (t ; Signal is one of the flavors of 'hit, 'hit-rewrite, or 'hit-rewrite2. (mv-let@par (erp hint-setting clauses state) (apply-reorder-hint@par pspv clauses ctx state) (cond (erp (mv@par step-limit 'error nil nil state)) (t (let ((new-pspv (if (cddr clauses) ; We erase the "any" cache if there are at least two children, much as we erase ; it (more accurately, replace it by the smaller "nil" cache) when diving into ; a branch of an IF term. Actually, we needn't erase the "any" cache if the ; rw-cache is inactive. But rather than consider carefully when the cache ; becomes active and inactive due to hints, we simply go ahead and do the cheap ; erase operation here. (erase-rw-cache-any-tag-from-pspv new-pspv) new-pspv))) (waterfall1-lst@par (cond ((eq (car ledge) 'settled-down-clause) 'settled-down-clause) ((null clauses) 0) ((null (cdr clauses)) nil) (t (length clauses))) cl-id clauses new-hist (if hint-setting (change prove-spec-var new-pspv :hint-settings (remove1-equal hint-setting (access prove-spec-var new-pspv :hint-settings))) new-pspv) new-jppl-flg hints (eq (car ledge) 'settled-down-clause) ens wrld ctx state step-limit)))))))))))) (defun@par waterfall0-or-hit (ledge cl-id clause hist pspv hints ens wrld ctx state uhs-lst i branch-cnt revert-info results step-limit) ; Cl-id is the clause id of clause, of course, and we are to disjunctively ; apply each of the hints in ush-lst to it. Uhs-lst is of the form ; (...(user-hinti . hint-settingsi)...) and branch-cnt is the length of that ; list initially, i.e., the maximum value of i. ; We map over uhs-lst and pursue each branch, giving each its own "D" clause id ; and changing the ttree in its history entry to indicate that it is branch i. ; We collect the results as we go into results. The results are each of the ; form (d-cl-id . new-pspv), where new-pspv is the pspv that results from ; processing the branch. If the :pool in any one of these new-pspv's is equal ; to that in pspv, then we have succeeded (nothing was pushed) and we stop. ; Otherwise, when we have considered all the hints in uhs-lst, we inspect ; results and choose the best (least difficult looking) one to pursue further. ; Revert-info is nil unless we have seen a disjunctive subgoal that generated a ; signal to abort and revert to the original goal. In that case, revert-info ; is a pair (revert-d-cl-id . pspv) where revert-d-cl-id identifies that ; disjunctive subgoal (the first one, in fact) and pspv is the corresponding ; pspv returned for that subgoal. #+acl2-par (declare (ignorable branch-cnt)) ; irrelevant in @par definition (cond ((endp uhs-lst) ; Results is the result of doing all the elements of uhs-lst. If it is empty, ; all branches aborted. Otherwise, we have to choose between the results. (cond ((endp results) ; In this case, every single disjunct aborted. That means each failed in one ; of three ways: (a) it set the goal to nil, (b) it needed induction but found ; a hint prohibiting it, or (c) it chose to revert to the original input. We ; will cause the whole proof to abort. We choose to revert if revert-d-cl-id ; is non-nil, indicating that (c) occurred for at least one disjunctive branch, ; namely one with a clause-id of revert-d-cl-id. (pprogn@par (serial-only@par (io? prove nil state (cl-id revert-info) (waterfall-or-hit-msg-c cl-id nil (car revert-info) nil nil state))) (mv@par step-limit 'abort (cond (revert-info (cdr revert-info)) (t (change prove-spec-var pspv :pool (cons (make pool-element :tag 'TO-BE-PROVED-BY-INDUCTION :clause-set '(nil) :hint-settings nil) (access prove-spec-var pspv :pool)) :tag-tree (add-to-tag-tree 'abort-cause 'empty-clause (access prove-spec-var pspv :tag-tree))))) (and revert-info ; Keep the following in sync with the corresponding call of pool-lst in ; waterfall-msg. That call assumes that the pspv was returned by push-clause, ; which is also the case here. (pool-lst (cdr (access prove-spec-var (cdr revert-info) :pool)))) state))) (t (mv-let (choice summary) (pick-best-pspv-for-waterfall0-or-hit results pspv wrld) #+acl2-par (declare (ignorable summary)) (pprogn@par (serial-only@par (io? proof-tree nil state (choice cl-id) (pprogn (increment-timer 'prove-time state) (install-disjunct-into-proof-tree cl-id (car choice) state) (increment-timer 'proof-tree-time state)))) (serial-only@par (io? prove nil state (cl-id results choice summary) (waterfall-or-hit-msg-c cl-id ; parent-cl-id results nil (car choice) ; new goal cl-id summary state))) (mv@par step-limit 'continue (cdr choice) ; chosen pspv ; Through Version_3.3 we used a jppl-flg here instead of nil. But to the ; extent that this value controls whether we print the goal before starting ; induction, we prefer to print it: for the corresponding goal pushed for ; induction under one of the disjunctive subgoals, the connection might not be ; obvious to the user. nil state)))))) (t (let* ((user-hinti (car (car uhs-lst))) (hint-settingsi (cdr (car uhs-lst))) (d-cl-id (make-disjunctive-clause-id cl-id (length uhs-lst) (current-package state)))) #+acl2-par (declare (ignorable user-hinti)) (pprogn@par (serial-only@par ; Wormholes are known to be a problem in the @par version of the waterfall. As ; such, we skip the following call of waterfall-or-hit-msg-a (also for some ; similar calls further down), which we have determined through runs of the ; regression suite (specifically with community book ; arithmetic-5/lib/floor-mod/floor-mod-basic.lisp) to cause problems. (io? prove nil state (cl-id user-hinti d-cl-id i branch-cnt) (pprogn (increment-timer 'prove-time state) (waterfall-or-hit-msg-a cl-id user-hinti d-cl-id i branch-cnt state) (increment-timer 'print-time state)))) (sl-let@par (d-signal d-new-pspv d-new-jppl-flg state) (waterfall1-wrapper@par (waterfall1@par ledge d-cl-id clause (change-or-hit-history-entry i hist cl-id) pspv (cons (pair-cl-id-with-hint-setting@par d-cl-id hint-settingsi) hints) t ;;; suppress-print ens wrld ctx state step-limit)) (declare (ignore d-new-jppl-flg)) ; Here, d-signal is one of 'error, 'abort or 'continue. We pass 'error up ; immediately and we filter 'abort out. (cond ((eq d-signal 'error) ; Errors shouldn't happen and we stop with an error if one does. (mv@par step-limit 'error nil nil state)) ((eq d-signal 'abort) ; Aborts are normal -- the proof failed somehow; we just skip it and continue ; with its peers. (waterfall0-or-hit@par ledge cl-id clause hist pspv hints ens wrld ctx state (cdr uhs-lst) (+ 1 i) branch-cnt (or revert-info (and (equal (tagged-objects 'abort-cause (access prove-spec-var d-new-pspv :tag-tree)) '(revert)) (cons d-cl-id d-new-pspv))) results step-limit)) ((equal (access prove-spec-var pspv :pool) (access prove-spec-var d-new-pspv :pool)) ; We won! The pool in the new pspv is the same as the pool in the old, which ; means all the subgoals generated in the branch were proved (modulo any forced ; assumptions, etc., in the :tag-tree). In this case we terminate the sweep ; across the disjuncts. ; Parallelism wart: you'll get a runtime error if pprogn@par forms are ; evaluated that have state returned by other than the last form, such as the ; call below of waterfall-or-hit-msg-b. Example: (WORMHOLE1 'COMMENT-WINDOW-IO ; 'NIL '(PPROGN (PRINC$ 17 *STANDARD-CO* STATE) 17) 'NIL) (pprogn@par (serial-only@par (io? proof-tree nil state (d-cl-id cl-id) (pprogn (increment-timer 'prove-time state) (install-disjunct-into-proof-tree cl-id d-cl-id state) (increment-timer 'proof-tree-time state)))) (serial-only@par (io? prove nil state (cl-id d-cl-id branch-cnt) (pprogn (increment-timer 'prove-time state) (waterfall-or-hit-msg-b cl-id d-cl-id branch-cnt state) (increment-timer 'print-time state)))) (mv@par step-limit 'continue d-new-pspv nil ; could probably use jppl-flg, but nil is always OK state))) (t ; Otherwise, we collect the result into results and continue with the others. (waterfall0-or-hit@par ledge cl-id clause hist pspv hints ens wrld ctx state (cdr uhs-lst) (+ 1 i) branch-cnt revert-info (cons (cons d-cl-id d-new-pspv) results) step-limit))))))))) (defun waterfall1-lst (n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit) ; N is either 'settled-down-clause, nil, or an integer. 'Settled- ; down-clause means that we just executed settled-down-clause and so ; should pass the parent's clause id through as though nothing ; happened. Nil means we produced one child and so its clause-id is ; that of the parent with the primes field incremented by 1. An ; integer means we produced n children and they each get a clause-id ; derived by extending the parent's case-lst. ; Keep the main body of waterfall1-lst in sync with waterfall1-lst@par-serial ; and waterfall1-tree@par-parallel. (cond ((null clauses) (mv step-limit 'continue pspv jppl-flg state)) (t (let ((cl-id ; Keep this binding in sync with the binding of cl-id in waterfall1-lst@par. (cond ((and (equal parent-cl-id *initial-clause-id*) (no-op-histp hist)) parent-cl-id) ((eq n 'settled-down-clause) parent-cl-id) ((null n) (change clause-id parent-cl-id :primes (1+ (access clause-id parent-cl-id :primes)))) (t (change clause-id parent-cl-id :case-lst (append (access clause-id parent-cl-id :case-lst) (list n)) :primes 0))))) (sl-let (signal new-pspv new-jppl-flg state) (waterfall1 *preprocess-clause-ledge* cl-id (car clauses) hist pspv hints suppress-print ens wrld ctx state step-limit) (cond ((eq signal 'error) (mv step-limit 'error nil nil state)) ((eq signal 'abort) (mv step-limit 'abort new-pspv new-jppl-flg state)) (t (waterfall1-lst (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t (1- n))) parent-cl-id (cdr clauses) hist new-pspv new-jppl-flg hints nil ens wrld ctx state step-limit)))))))) #+acl2-par (defun waterfall1-lst@par-serial (n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit) ; Keep the main body of waterfall1-lst in sync with waterfall1-lst@par-serial, ; waterfall1-tree@par-parallel, and waterfall1-tree@par-pseudo-parallel. Keep ; the calculation of cl-id in sync with waterfall1-lst@par. (cond ((null clauses) (mv@par step-limit 'continue pspv jppl-flg state)) (t (let ((cl-id (cond ((and (equal parent-cl-id *initial-clause-id*) (no-op-histp hist)) parent-cl-id) ((eq n 'settled-down-clause) parent-cl-id) ((null n) (change clause-id parent-cl-id :primes (1+ (access clause-id parent-cl-id :primes)))) (t (change clause-id parent-cl-id :case-lst (append (access clause-id parent-cl-id :case-lst) (list n)) :primes 0))))) (sl-let@par (signal new-pspv new-jppl-flg state) (waterfall1-wrapper@par (waterfall1@par *preprocess-clause-ledge* cl-id (car clauses) hist pspv hints suppress-print ens wrld ctx state step-limit)) (cond ((eq signal 'error) (mv@par step-limit 'error nil nil state)) ((eq signal 'abort) (mv@par step-limit 'abort new-pspv new-jppl-flg state)) (t (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t (1- n))) parent-cl-id (cdr clauses) hist new-pspv new-jppl-flg hints nil ens wrld ctx state step-limit)))))))) #+acl2-par (defun waterfall1-tree@par-pseudo-parallel (n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit) ; Keep the main body of waterfall1-lst in sync with waterfall1-lst@par-serial, ; waterfall1-tree@par-parallel, and waterfall1-tree@par-pseudo-parallel. Keep ; the calculation of cl-id in sync with waterfall1-lst@par. ; Since waterfall1-tree@par-pseudo-parallel is just a refactoring of ; waterfall1-tree@par-parallel, I remove many comments from this defintion. So, ; see waterfall1-tree@par-parallel for a more complete set of comments. (declare (ignorable ens)) (cond ((null clauses) (mv@par step-limit 'continue pspv jppl-flg state)) (t (let ((cl-id (cond ((and (equal parent-cl-id *initial-clause-id*) (no-op-histp hist)) parent-cl-id) ((eq n 'settled-down-clause) parent-cl-id) ((null n) (change clause-id parent-cl-id :primes (1+ (access clause-id parent-cl-id :primes)))) (t (change clause-id parent-cl-id :case-lst (append (access clause-id parent-cl-id :case-lst) (list n)) :primes 0))))) (mv-let (first-half-clauses second-half-clauses len-first-half) (halves-with-length clauses) (mv-let@par (step-limit1 signal1 pspv1 jppl-flg1 state) (cond ((assert$ (consp clauses) (null (cdr clauses))) ; just one clause, call waterfall1 (waterfall1-wrapper@par (waterfall1@par *preprocess-clause-ledge* cl-id (car clauses) hist pspv hints suppress-print ens wrld ctx state step-limit))) (t (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t n)) ;(1- n))) parent-cl-id first-half-clauses hist pspv jppl-flg hints nil ens wrld ctx state step-limit))) (if ; Conditions that must be true for the speculative call to be valid: (and (not (eq signal1 'error)) (not (eq signal1 'abort)) (speculative-execution-valid pspv pspv1)) (mv-let ; Here, we perform the speculative call of waterfall1-lst@par, which is the ; recursion on the cdr of clauses. As such, this code matches the code at the ; end of waterfall1-lst. (step-limit2 signal2 pspv2 jppl-flg2) (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t (- n len-first-half))) parent-cl-id second-half-clauses hist pspv jppl-flg hints nil ens wrld ctx state step-limit) (cond ((eq signal2 'error) (mv@par step-limit2 'error nil nil state)) ((eq signal2 'abort) (mv@par step-limit2 'abort pspv2 jppl-flg2 state)) (t (let ((combined-step-limit (- (- step-limit (- step-limit step-limit1)) (- step-limit step-limit2))) (combined-prove-spec-vars (combine-prove-spec-vars pspv pspv1 pspv2 ctx (f-get-global 'debug-pspv state) signal1 signal2))) (if (abort-will-occur-in-pool (access prove-spec-var combined-prove-spec-vars :pool)) (prog2$ (with-output-lock (cw "Normally we would attempt to prove two or ~ more of the previously printed subgoals by ~ induction. However, we prefer in this ~ instance to focus on the original input ~ conjecture rather than those simplified ~ special cases. We therefore abandon our ~ previous work on these conjectures and ~ reassign the name *1 to the original ~ conjecture.")) (mv@par combined-step-limit 'abort combined-prove-spec-vars jppl-flg2 state)) (mv@par combined-step-limit signal2 combined-prove-spec-vars jppl-flg2 state)))))) (cond ((eq signal1 'error) (mv@par step-limit1 'error nil nil state)) ((eq signal1 'abort) (mv@par step-limit1 'abort pspv1 jppl-flg1 state)) (t ; we need to recompute the recursive call (prog2$ (cond ((member-eq 'prove (f-get-global 'inhibit-output-lst state)) nil) (t (with-output-lock (cw "Invalid speculation for children of subgoal ~ ~x0~%" (string-for-tilde-@-clause-id-phrase cl-id))))) (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t (- n len-first-half))) parent-cl-id second-half-clauses hist pspv1 jppl-flg1 hints nil ens wrld ctx state step-limit1))))))))))) #+acl2-par (defun waterfall1-tree@par-parallel (n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit) ; Keep the main body of waterfall1-lst in sync with waterfall1-lst@par-serial, ; waterfall1-tree@par-parallel, and waterfall1-tree@par-pseudo-parallel. Keep ; the calculation of cl-id in sync with waterfall1-lst@par. ; Once upon a time, we took a "list-based" approach to parallelizing the proofs ; of clauses. We now take a "tree-based" approach. ; Originally waterfall1-tree@par-parallel would "cdr" down the list of clauses ; and spawn a thread for each of those recursive calls. However, that approach ; required too many threads (we attempted to mitigate this problem with ; set-total-parallelism-work-limit, but it was just a bandage on a more glaring ; problem). As of April, 2012, we now take a "tree-based" approach and split ; the list of clauses into halves, and then call waterfall1-lst@par again, ; twice, each with its own half. We eventually call waterfall1-lst@par with a ; clause list of length 1, and then that clause is proven. ; Note that splitting the list like this is a reasonable thing to do -- we do ; not reorder any subgoals, and we increment the variable that keeps track of ; the current subgoal number (n) by the length of the first half of clauses. (declare (ignorable ens)) (cond ((null clauses) (mv@par step-limit 'continue pspv jppl-flg state)) (t (let ((cl-id (cond ((and (equal parent-cl-id *initial-clause-id*) (no-op-histp hist)) parent-cl-id) ((eq n 'settled-down-clause) parent-cl-id) ((null n) (change clause-id parent-cl-id :primes (1+ (access clause-id parent-cl-id :primes)))) (t (change clause-id parent-cl-id :case-lst (append (access clause-id parent-cl-id :case-lst) (list n)) :primes 0))))) (mv-let (first-half-clauses second-half-clauses len-first-half) (halves-with-length clauses) (spec-mv-let ; Here, we perform the speculative call of waterfall1-lst@par, which is the ; recursion on the cdr of clauses. As such, this code matches the code at the ; end of waterfall1-lst. ; Variable names that end with "1" (as in signal1) store results from proving ; the first half of the clauses (the part of the spec-mv-let that is always ; peformed), and variable names that end with "2" (as in signal2) store results ; from speculatively proving the second half of the clauses. (step-limit2 signal2 pspv2 jppl-flg2) (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t (- n len-first-half))) parent-cl-id second-half-clauses hist pspv jppl-flg hints nil ens wrld ctx state step-limit) (mv-let@par (step-limit1 signal1 pspv1 jppl-flg1 state) (cond ((assert$ (consp clauses) (null (cdr clauses))) ; just one clause, call waterfall1 (waterfall1-wrapper@par (waterfall1@par *preprocess-clause-ledge* cl-id (car clauses) hist pspv hints suppress-print ens wrld ctx state step-limit))) (t (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t n)) parent-cl-id first-half-clauses hist pspv jppl-flg hints nil ens wrld ctx state step-limit))) (if ; Conditions that must be true for the speculative call to be valid: (and (not (eq signal1 'error)) (not (eq signal1 'abort)) (speculative-execution-valid pspv pspv1)) (cond ((eq signal2 'error) (mv@par step-limit2 'error nil nil state)) ((eq signal2 'abort) ; It is okay to just return pspv2, because if there is an abort, any clauses ; pushed for induction into pspv1 would be discarded anyway. See Essay on ; prove-spec-var pool modifications for further discussion. (mv@par step-limit2 'abort pspv2 jppl-flg2 state)) (t (let ((combined-step-limit (- (- step-limit (- step-limit step-limit1)) (- step-limit step-limit2))) (combined-prove-spec-vars (combine-prove-spec-vars pspv pspv1 pspv2 ctx (f-get-global 'debug-pspv state) signal1 signal2))) (if (abort-will-occur-in-pool (access prove-spec-var combined-prove-spec-vars :pool)) (prog2$ ; Parallelism wart: maybe this call to cw should be inside waterfall instead of ; here. The potential problem with printing the message here is that printing ; can still occur after we say that we are "focus[sing] on the original ; conjecture". ; For example, suppose we are Subgoal 3.2.4, and we know we need to abort. ; Subgoal 3.3.4 could still be proving and print a checkpoint, even though this ; call of Subgoal 3.2.4 knows we need to abort. It is not until control ; returns to the waterfall1-lst@par call on Subgoal 3.3 that the 'abort from ; Subgoal 3.2.4 will be seen, and that we will then know that all such calls ; that might print have already returned (because Subgoal 3.3.4 must be ; finished before the call of waterfall1 on Subgoal 3.3 returns). (with-output-lock (cw "Normally we would attempt to prove two or ~ more of the previously printed subgoals by ~ induction. However, we prefer in this ~ instance to focus on the original input ~ conjecture rather than those simplified ~ special cases. We therefore abandon our ~ previous work on these conjectures and ~ reassign the name *1 to the original ~ conjecture.")) (mv@par combined-step-limit 'abort ; We do not adjust the pspv's pool here. Instead, we rely upon waterfall to ; correctly convert the 'maybe-to-be-proved-by-induction tag to a ; 'to-be-proved-by-induction and discard the other clauses. combined-prove-spec-vars jppl-flg2 state)) (mv@par combined-step-limit signal2 combined-prove-spec-vars jppl-flg2 state))))) (cond ((eq signal1 'error) (mv@par step-limit1 'error nil nil state)) ((eq signal1 'abort) (mv@par step-limit1 'abort pspv1 jppl-flg1 state)) (t ; we need to recompute the recursive call (prog2$ ; Parallelism wart: improve message just below (maybe even eliminate it?). ; Also, consider avoiding this direct use of inhibit-output-lst (it seemed that ; io? didn't work because we don't use state, as it requires). ; And finally, deal the same way with all cw printing done on behalf of the ; prover; consider searching for with-output-lock to find those. ; Parallelism wart: due to the definition of speculative-execution-valid, this ; code should no longer be reachable. We leave it for now because it is an ; example use of 'inhibit-output-lst (also see parallelism wart immediately ; above). (cond ((member-eq 'prove (f-get-global 'inhibit-output-lst state)) nil) (t (with-output-lock (cw "Invalid speculation for children of subgoal ~ ~x0~%" (string-for-tilde-@-clause-id-phrase cl-id))))) (waterfall1-lst@par (cond ((eq n 'settled-down-clause) n) ((null n) nil) (t (- n len-first-half))) parent-cl-id second-half-clauses hist pspv1 jppl-flg1 hints nil ens wrld ctx state step-limit1)))))))))))) #+acl2-par (defun waterfall1-lst@par (n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit) ; Keep the main body of waterfall1-lst in sync with waterfall1-lst@par-serial ; and waterfall1-tree@par-parallel. Keep the calculation of cl-id in sync with ; waterfall1-lst@par. (let ((primes-subproof (cond ((and (equal parent-cl-id *initial-clause-id*) (no-op-histp hist)) nil) ((eq n 'settled-down-clause) nil) ((null n) t) (t nil))) (cl-id ; Keep this binding in sync with the binding of cl-id in waterfall1-lst. (cond ((and (equal parent-cl-id *initial-clause-id*) (no-op-histp hist)) parent-cl-id) ((eq n 'settled-down-clause) parent-cl-id) ((null n) (change clause-id parent-cl-id :primes (1+ (access clause-id parent-cl-id :primes)))) (t (change clause-id parent-cl-id :case-lst (append (access clause-id parent-cl-id :case-lst) (list n)) :primes 0))))) (declare (ignorable primes-subproof cl-id)) (let ((call-type (cond (primes-subproof 'serial) (t (case (f-get-global 'waterfall-parallelism state) ((nil) 'serial) ((:full) (cond #-acl2-loop-only ((not-too-many-futures-already-in-existence) 'parallel) (t 'serial))) ((:pseudo-parallel) 'pseudo-parallel) ((:top-level) (cond ((equal parent-cl-id '((0) NIL . 0)) 'parallel) (t 'serial))) ((:resource-and-timing-based) ; Here, and in the :resource-based branch below, we have an unusual functional ; discrepancy between code in the #+acl2-loop-only and #-acl2-loop-only cases. ; But the alternative we have considered would involve some complicated use of ; the acl2-oracle, which seems unjustified for this #+acl2-par code. (cond #-acl2-loop-only ((and ; We could test to see whether doing the lookup or testing for resource ; availability is faster. It probably doesn't matter since they're both ; supposed to be "lock free." Since we control the lock-freeness for the ; resource availability test in the definition of futures-resources-available ; (as opposed to relying upon the underlying CCL implementation), we call that ; first. (futures-resources-available) (> (or (lookup-waterfall-timings-for-cl-id cl-id) 0) (f-get-global 'waterfall-parallelism-timing-threshold state))) (increment-waterfall-parallelism-counter 'resource-and-timing-parallel)) (t (increment-waterfall-parallelism-counter 'resource-and-timing-serial)))) ((:resource-based) ; See comment above about discrepancy between #+acl2-loop-only and ; #-acl2-loop-only code. (cond #-acl2-loop-only ((futures-resources-available) (increment-waterfall-parallelism-counter 'resource-parallel)) (t (increment-waterfall-parallelism-counter 'resource-serial)))) (otherwise (er hard 'waterfall1-lst@par "Waterfall-parallelism type is not what it's supposed to ~ be. Please contact the ACL2 authors."))))))) (case call-type ; There are three modes of execution available to the waterfall in ACL2(p). We ; describe each mode inline, below. ((serial) ; The serial mode cdrs down the list of clauses, just like waterfall1-lst. (waterfall1-lst@par-serial n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit)) ((parallel) ; The parallel mode will call waterfall1-lst@par on the first half of clauses ; in the current thread and call waterfall1-lst@par on the second half of ; clauses in another thread. Once upon a time, we took a "list-based" approach ; to proving the list of clauses -- where we would prove the (car clauses) in ; the current thread and call (waterfall1-lst@par (cdr clauses)) in another ; thread. We now take a "tree-based" approach, hence the difference in name ; ("tree" vs. "lst"). (waterfall1-tree@par-parallel n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit)) ((pseudo-parallel) ; The psuedo-parallel mode is just like parallel mode, except both calls occur ; in the current thread. (waterfall1-tree@par-pseudo-parallel n parent-cl-id clauses hist pspv jppl-flg hints suppress-print ens wrld ctx state step-limit)) (otherwise (prog2$ (er hard 'waterfall1-lst@par "Implementation error in waterfall1-lst@par. Please ~ contact the ACL2 authors.") (mv@par nil nil nil nil state))))))) ) ; And here is the waterfall: (defun waterfall (forcing-round pool-lst x pspv hints ens wrld ctx state step-limit) ; Here x is a list of clauses, except that when we are beginning a forcing ; round other than the first, x is really a list of pairs (assumnotes . ; clause). ; Pool-lst is the pool-lst of the clauses and will be used as the first field ; in the clause-id's we generate for them. We return the five values: a new ; step-limit, an error flag, the final value of pspv, the jppl-flg, and the ; final state. (let ((parent-clause-id (cond ((and (= forcing-round 0) (null pool-lst)) ; Note: This cond is not necessary. We could just do the make clause-id ; below. We recognize this case just to avoid the consing. *initial-clause-id*) (t (make clause-id :forcing-round forcing-round :pool-lst pool-lst :case-lst nil :primes 0)))) (clauses (cond ((and (not (= forcing-round 0)) (null pool-lst)) (strip-cdrs x)) (t x))) (pspv (maybe-set-rw-cache-state-disabled (erase-rw-cache-from-pspv pspv)))) (pprogn (cond ((output-ignored-p 'proof-tree state) state) (t (initialize-proof-tree parent-clause-id x ctx state))) (sl-let (signal new-pspv new-jppl-flg state) #+acl2-par (if (f-get-global 'waterfall-parallelism state) (with-ensured-parallelism-finishing (with-parallelism-hazard-warnings (mv-let (step-limit signal new-pspv new-jppl-flg) (waterfall1-lst@par (cond ((null clauses) 0) ((null (cdr clauses)) 'settled-down-clause) (t (length clauses))) parent-clause-id clauses nil pspv nil hints (and (eql forcing-round 0) (null pool-lst)) ; suppress-print ens wrld ctx state step-limit) (mv step-limit signal (convert-maybes-to-tobes-in-pspv new-pspv) new-jppl-flg state)))) (sl-let (signal new-pspv new-jppl-flg state) (waterfall1-lst (cond ((null clauses) 0) ((null (cdr clauses)) 'settled-down-clause) (t (length clauses))) parent-clause-id clauses nil pspv nil hints (and (eql forcing-round 0) (null pool-lst)) ; suppress-print ens wrld ctx state step-limit) (mv step-limit signal new-pspv new-jppl-flg state))) #-acl2-par (waterfall1-lst (cond ((null clauses) 0) ((null (cdr clauses)) 'settled-down-clause) (t (length clauses))) parent-clause-id clauses nil pspv nil hints (and (eql forcing-round 0) (null pool-lst)) ; suppress-print ens wrld ctx state step-limit) (cond ((eq signal 'error) ; If the waterfall signalled an error then it printed the message and we ; just pass the error up. (mv step-limit t nil nil state)) (t ; Otherwise, the signal is either 'abort or 'continue. But 'abort here ; was meant as an internal signal only, used to get out of the recursion ; in waterfall1. We now simply fold those two signals together into the ; non-erroneous return of the new-pspv and final flg. (mv step-limit nil new-pspv new-jppl-flg state))))))) ; After the waterfall has finished we have a pool of goals. We ; now develop the functions to extract a goal from the pool for ; induction. It is in this process that we check for subsumption ; among the goals in the pool. (defun some-pool-member-subsumes (pool clause-set) ; We attempt to determine if there is a clause set in the pool that subsumes ; every member of the given clause-set. If we make that determination, we ; return the tail of pool that begins with that member. Otherwise, no such ; subsumption was found, perhaps because of the limitation in our subsumption ; check (see subsumes), and we return nil. (cond ((null pool) nil) ((eq (clause-set-subsumes *init-subsumes-count* (access pool-element (car pool) :clause-set) clause-set) t) pool) (t (some-pool-member-subsumes (cdr pool) clause-set)))) (defun add-to-pop-history (action cl-set pool-lst subsumer-pool-lst pop-history) ; Extracting a clause-set from the pool is called "popping". It is ; complicated by the fact that we do subsumption checking and other ; things. To report what happened when we popped, we maintain a "pop-history" ; which is used by the pop-clause-msg fn below. This function maintains ; pop-histories. ; A pop-history is a list that records the sequence of events that ; occurred when we popped a clause set from the pool. The pop-history ; is used only by the output routine pop-clause-msg. ; The pop-history is built from nil by repeated calls of this ; function. Thus, this function completely specifies the format. The ; elements in a pop-history are each of one of the following forms. ; All the "lst"s below are pool-lsts. ; (pop lst1 ... lstk) finished the proofs of the lstd goals ; (consider cl-set lst) induct on cl-set ; (subsumed-by-parent cl-set lst subsumer-lst) ; cl-set is subsumed by lstd parent ; (subsumed-below cl-set lst subsumer-lst) ; cl-set is subsumed by lstd peer ; (qed) pool is empty -- but there might be ; assumptions or :byes yet to deal with. ; and has the property that no two pop entries are adjacent. When ; this function is called with an action that does not require all of ; the arguments, nils may be provided. ; The entries are in reverse chronological order and the lsts in each ; pop entry are in reverse chronological order. (cond ((eq action 'pop) (cond ((and pop-history (eq (caar pop-history) 'pop)) (cons (cons 'pop (cons pool-lst (cdar pop-history))) (cdr pop-history))) (t (cons (list 'pop pool-lst) pop-history)))) ((eq action 'consider) (cons (list 'consider cl-set pool-lst) pop-history)) ((eq action 'qed) (cons '(qed) pop-history)) (t (cons (list action cl-set pool-lst subsumer-pool-lst) pop-history)))) (defun pop-clause1 (pool pop-history) ; We scan down pool looking for the next 'to-be-proved-by-induction ; clause-set. We mark it 'being-proved-by-induction and return six ; things: one of the signals 'continue, 'win, or 'lose, the pool-lst ; for the popped clause-set, the clause-set, its hint-settings, a ; pop-history explaining what we did, and a new pool. (cond ((null pool) ; It looks like we won this one! But don't be fooled. There may be ; 'assumptions or :byes in the ttree associated with this proof and ; that will cause the proof to fail. But for now we continue to just ; act happy. This is called denial. (mv 'win nil nil nil (add-to-pop-history 'qed nil nil nil pop-history) nil)) ((eq (access pool-element (car pool) :tag) 'being-proved-by-induction) (pop-clause1 (cdr pool) (add-to-pop-history 'pop nil (pool-lst (cdr pool)) nil pop-history))) ((equal (access pool-element (car pool) :clause-set) '(nil)) ; The empty set was put into the pool! We lose. We report the empty name ; and clause set, and an empty pop-history (so no output occurs). We leave ; the pool as is. So we'll go right out of pop-clause and up to the prover ; with the 'lose signal. (mv 'lose nil nil nil nil pool)) (t (let ((pool-lst (pool-lst (cdr pool))) (sub-pool (some-pool-member-subsumes (cdr pool) (access pool-element (car pool) :clause-set)))) (cond ((null sub-pool) (mv 'continue pool-lst (access pool-element (car pool) :clause-set) (access pool-element (car pool) :hint-settings) (add-to-pop-history 'consider (access pool-element (car pool) :clause-set) pool-lst nil pop-history) (cons (change pool-element (car pool) :tag 'being-proved-by-induction) (cdr pool)))) ((eq (access pool-element (car sub-pool) :tag) 'being-proved-by-induction) (mv 'lose nil nil nil (add-to-pop-history 'subsumed-by-parent (access pool-element (car pool) :clause-set) pool-lst (pool-lst (cdr sub-pool)) pop-history) pool)) (t (pop-clause1 (cdr pool) (add-to-pop-history 'subsumed-below (access pool-element (car pool) :clause-set) pool-lst (pool-lst (cdr sub-pool)) pop-history)))))))) ; Here we develop the functions for reporting on a pop. (defun make-defthm-forms-for-byes (byes wrld) ; Each element of byes is of the form (name . clause) and we create ; a list of the corresponding defthm events. (cond ((null byes) nil) (t (cons (list 'defthm (caar byes) (prettyify-clause (cdar byes) nil wrld) :rule-classes nil) (make-defthm-forms-for-byes (cdr byes) wrld))))) (defun pop-clause-msg1 (forcing-round lst jppl-flg prev-action gag-state msg-p state) ; Lst is a reversed pop-history. Since pop-histories are in reverse ; chronological order, lst is in chronological order. We scan down ; lst, printing out an explanation of each action. Prev-action is the ; most recently explained action in this scan, or else nil if we are ; just beginning. Jppl-flg, if non-nil, means that the last executed ; waterfall process was 'push-clause; the pool-lst of the clause pushed is ; in the value of jppl-flg. (cond ((null lst) (pprogn (increment-timer 'print-time state) (mv gag-state state))) (t (let ((entry (car lst))) (mv-let (gag-state state) (case-match entry (('pop . pool-lsts) (mv-let (gagst msgs) (pop-clause-update-gag-state-pop pool-lsts gag-state nil msg-p state) (pprogn (io? prove nil state (prev-action pool-lsts forcing-round msgs) (pprogn (fms (cond ((gag-mode) (assert$ pool-lsts "~*1 ~#0~[is~/are~] COMPLETED!~|")) ((null prev-action) "That completes the proof~#0~[~/s~] of ~*1.~|") (t "That, in turn, completes the proof~#0~[~/s~] of ~ ~*1.~|")) (list (cons #\0 pool-lsts) (cons #\1 (list "" "~@*" "~@* and " "~@*, " (tilde-@-pool-name-phrase-lst forcing-round (reverse pool-lsts))))) (proofs-co state) state nil) (cond ((and msgs (gag-mode)) (mv-let (col state) (fmt1 "Thus key checkpoint~#1~[~ ~*0 is~/s ~*0 are~] ~ COMPLETED!~|" (list (cons #\0 (list "" "~@*" "~@* and " "~@*, " (reverse msgs))) (cons #\1 msgs)) 0 (proofs-co state) state nil) (declare (ignore col)) state)) (t state)))) (mv gagst state)))) (('qed) ; We used to print Q.E.D. here, but that is premature now that we know ; there might be assumptions or :byes in the pspv. We let ; process-assumptions announce the definitive completion of the proof. (mv gag-state state)) (& ; Entry is either a 'consider or one of the two 'subsumed... actions. For all ; three we print out the clause we are working on. Then we print out the ; action specific stuff. (let ((pool-lst (caddr entry))) (mv-let (gagst cl-id) (cond ((eq (car entry) 'consider) (mv gag-state nil)) (t (remove-pool-lst-from-gag-state pool-lst gag-state state))) (pprogn (io? prove nil state (prev-action forcing-round pool-lst entry cl-id jppl-flg gag-state) (let* ((cl-set (cadr entry)) (jppl-flg (if (gag-mode) (gag-mode-jppl-flg gag-state) jppl-flg)) (push-pop-flg (and jppl-flg (equal jppl-flg pool-lst)))) ; The push-pop-flg is set if the clause just popped is the same as the one we ; just pushed. It and its name have just been printed. There's no need to ; identify it here unless we are in gag-mode and we are in a sub-induction, ; since in that case we never printed the formula. (We could take the attitude ; that the user doesn't deserve to see any sub-induction formulas in gag-mode; ; but we expect there to be very few of these anyhow, since probably they'll ; generally fail.) (pprogn (cond (push-pop-flg state) (t (fms (cond ((eq prev-action 'pop) "We therefore turn our attention to ~@1, ~ which is~|~%~q0.~|") ((null prev-action) "So we now return to ~@1, which is~|~%~q0.~|") (t "We next consider ~@1, which is~|~%~q0.~|")) (list (cons #\0 (prettyify-clause-set cl-set (let*-abstractionp state) (w state))) (cons #\1 (tilde-@-pool-name-phrase forcing-round pool-lst))) (proofs-co state) state (term-evisc-tuple nil state)))) (case-match entry (('subsumed-below & & subsumer-pool-lst) (pprogn (fms "But the formula above is subsumed by ~@1, ~ which we'll try to prove later. We therefore ~ regard ~@0 as proved (pending the proof of ~ the more general ~@1).~|" (list (cons #\0 (tilde-@-pool-name-phrase forcing-round pool-lst)) (cons #\1 (tilde-@-pool-name-phrase forcing-round subsumer-pool-lst))) (proofs-co state) state nil) (cond ((and cl-id (gag-mode)) (fms "~@0 COMPLETED!~|" (list (cons #\0 (tilde-@-clause-id-phrase cl-id))) (proofs-co state) state nil)) (t state)))) (('subsumed-by-parent & & subsumer-pool-lst) (fms "The formula above is subsumed by one of its ~ parents, ~@0, which we're in the process of ~ trying to prove by induction. When an ~ inductive proof pushes a subgoal for induction ~ that is less general than the original goal, ~ it may be a sign that either an inappropriate ~ induction was chosen or that the original goal ~ is insufficiently general. In any case, our ~ proof attempt has failed.~|" (list (cons #\0 (tilde-@-pool-name-phrase forcing-round subsumer-pool-lst))) (proofs-co state) state nil)) (& ; (consider cl-set pool-lst) state))))) (mv gagst state)))))) (pop-clause-msg1 forcing-round (cdr lst) jppl-flg (caar lst) gag-state msg-p state)))))) (defun pop-clause-msg (forcing-round pop-history jppl-flg pspv state) ; We print the messages explaining the pops we did. ; This function increments timers. Upon entry, the accumulated time is ; charged to 'prove-time. The time spent in this function is charged ; to 'print-time. (pprogn (increment-timer 'prove-time state) (mv-let (gag-state state) (let ((gag-state0 (access prove-spec-var pspv :gag-state))) (pop-clause-msg1 forcing-round (reverse pop-history) jppl-flg nil gag-state0 (not (output-ignored-p 'prove state)) state)) (pprogn (record-gag-state gag-state state) (increment-timer 'print-time state) (mv (change prove-spec-var pspv :gag-state gag-state) state))))) (defun subsumed-clause-ids-from-pop-history (forcing-round pop-history) (cond ((endp pop-history) nil) ((eq (car (car pop-history)) 'subsumed-below) (cons (make clause-id :forcing-round forcing-round :pool-lst (caddr (car pop-history)) ; see add-to-pop-history :case-lst nil :primes 0) (subsumed-clause-ids-from-pop-history forcing-round (cdr pop-history)))) (t (subsumed-clause-ids-from-pop-history forcing-round (cdr pop-history))))) (defun increment-proof-tree-pop-clause (forcing-round pop-history state) (let ((old-proof-tree (f-get-global 'proof-tree state)) (dead-clause-ids (subsumed-clause-ids-from-pop-history forcing-round pop-history))) (if dead-clause-ids (pprogn (f-put-global 'proof-tree (prune-proof-tree forcing-round dead-clause-ids old-proof-tree) state) (print-proof-tree state)) state))) (defun pop-clause (forcing-round pspv jppl-flg state) ; We pop the first available clause from the pool in pspv. We print ; out an explanation of what we do. If jppl-flg is non-nil ; then it means the last executed waterfall processor was 'push-clause ; and the pool-lst of the clause pushed is the value of jppl-flg. ; We return 7 results. The first is a signal: 'win, 'lose, or ; 'continue and indicates that we have finished successfully (modulo, ; perhaps, some assumptions and :byes in the tag-tree), arrived at a ; definite failure, or should continue. If the first result is ; 'continue, the second, third and fourth are the pool name phrase, ; the set of clauses to induct upon, and the hint-settings, if any. ; The remaining results are the new values of pspv and state. (mv-let (signal pool-lst cl-set hint-settings pop-history new-pool) (pop-clause1 (access prove-spec-var pspv :pool) nil) (mv-let (new-pspv state) (pop-clause-msg forcing-round pop-history jppl-flg pspv state) (pprogn (io? proof-tree nil state (forcing-round pop-history) (pprogn (increment-timer 'prove-time state) (increment-proof-tree-pop-clause forcing-round pop-history state) (increment-timer 'proof-tree-time state))) (mv signal pool-lst cl-set hint-settings (change prove-spec-var new-pspv :pool new-pool) state))))) (defun tilde-@-assumnotes-phrase-lst (lst wrld) ; Warning :If you change this function, consider also changing ; tilde-@-assumnotes-phrase-lst-gag-mode. ; WARNING: Note that the phrase is encoded twelve times below, to put ; in the appropriate noise words and punctuation! ; Note: As of this writing it is believed that the only time the :rune of an ; assumnote is a fake rune, as in cases 1, 5, and 9 below, is when the ; assumnote is in the impossible assumption. However, we haven't coded this ; specially because such an assumption will be brought immediately to our ; attention in the forcing round by its *nil* :term. (cond ((null lst) nil) (t (cons (cons (cond ((null (cdr lst)) (cond ((and (consp (access assumnote (car lst) :rune)) (null (base-symbol (access assumnote (car lst) :rune)))) " ~@0~% by primitive type reasoning about~% ~q2.~|") ((eq (access assumnote (car lst) :rune) 'equal) " ~@0~% by the linearization of~% ~q2.~|") ((symbolp (access assumnote (car lst) :rune)) " ~@0~% by assuming the guard for ~x1 in~% ~q2.~|") (t " ~@0~% by applying ~x1 to~% ~q2.~|"))) ((null (cddr lst)) (cond ((and (consp (access assumnote (car lst) :rune)) (null (base-symbol (access assumnote (car lst) :rune)))) " ~@0~% by primitive type reasoning about~% ~q2,~| and~|") ((eq (access assumnote (car lst) :rune) 'equal) " ~@0~% by the linearization of~% ~q2,~| and~|") ((symbolp (access assumnote (car lst) :rune)) " ~@0~% by assuming the guard for ~x1 in~% ~q2,~| and~|") (t " ~@0~% by applying ~x1 to~% ~q2,~| and~|"))) (t (cond ((and (consp (access assumnote (car lst) :rune)) (null (base-symbol (access assumnote (car lst) :rune)))) " ~@0~% by primitive type reasoning about~% ~q2,~|") ((eq (access assumnote (car lst) :rune) 'equal) " ~@0~% by the linearization of~% ~q2,~|") ((symbolp (access assumnote (car lst) :rune)) " ~@0~% by assuming the guard for ~x1 in~% ~q2,~|") (t " ~@0~% by applying ~x1 to~% ~q2,~|")))) (list (cons #\0 (tilde-@-clause-id-phrase (access assumnote (car lst) :cl-id))) (cons #\1 (access assumnote (car lst) :rune)) (cons #\2 (untranslate (access assumnote (car lst) :target) nil wrld)))) (tilde-@-assumnotes-phrase-lst (cdr lst) wrld))))) (defun tilde-*-assumnotes-column-phrase (assumnotes wrld) ; We create a tilde-* phrase that will print a column of assumnotes. (list "" "~@*" "~@*" "~@*" (tilde-@-assumnotes-phrase-lst assumnotes wrld))) (defun tilde-@-assumnotes-phrase-lst-gag-mode (lst acc) ; Warning: If you change this function, consider also changing ; tilde-@-assumnotes-phrase-lst. See also that function definition. (cond ((null lst) (cond ((null acc) acc) ((null (cdr acc)) (list (msg "in~@0.~|" (car acc)))) (t (reverse (list* (msg "in~@0.~|" (car acc)) (msg "in~@0, and " (cadr acc)) (pairlis-x1 "in~@0, ~|" (pairlis$ (pairlis-x1 #\0 (cddr acc)) nil))))))) (t (tilde-@-assumnotes-phrase-lst-gag-mode (cdr lst) (let* ((cl-id-phrase (tilde-@-clause-id-phrase (access assumnote (car lst) :cl-id))) (x (cond ((and (consp (access assumnote (car lst) :rune)) (null (base-symbol (access assumnote (car lst) :rune)))) (list " ~@0 by primitive type reasoning" (cons #\0 cl-id-phrase))) ((eq (access assumnote (car lst) :rune) 'equal) (list " ~@0 by linearization" (cons #\0 cl-id-phrase))) ((symbolp (access assumnote (car lst) :rune)) (list " ~@0 by assuming the guard for ~x1" (cons #\0 cl-id-phrase) (cons #\1 (access assumnote (car lst) :rune)))) (t (list " ~@0 by applying ~x1" (cons #\0 cl-id-phrase) (cons #\1 (access assumnote (car lst) :rune))))))) (add-to-set-equal x acc)))))) (defun tilde-*-assumnotes-column-phrase-gag-mode (assumnotes) ; We create a tilde-* phrase that will print a column of assumnotes. (list "" "~@*" "~@*" "~@*" (tilde-@-assumnotes-phrase-lst-gag-mode assumnotes nil))) (defun process-assumptions-msg1 (forcing-round n pairs state) ; N is either nil (meaning the length of pairs is 1) or n is the length of ; pairs. (cond ((null pairs) state) (t (pprogn (let ((cl-id-phrase (tilde-@-clause-id-phrase (make clause-id :forcing-round (1+ forcing-round) :pool-lst nil :case-lst (if n (list n) nil) :primes 0)))) (cond ((gag-mode) (fms "~@0 was forced ~*1" (list (cons #\0 cl-id-phrase) (cons #\1 (tilde-*-assumnotes-column-phrase-gag-mode (car (car pairs))))) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "~@0, below, will focus on~%~q1,~|which was forced in~%~*2" (list (cons #\0 cl-id-phrase) (cons #\1 (untranslate (car (last (cdr (car pairs)))) t (w state))) (cons #\2 (tilde-*-assumnotes-column-phrase (car (car pairs)) (w state)))) (proofs-co state) state (term-evisc-tuple nil state))))) (process-assumptions-msg1 forcing-round (if n (1- n) nil) (cdr pairs) state))))) (defun process-assumptions-msg (forcing-round n0 n pairs state) ; This function is called when we have completed the given forcing-round and ; are about to begin the next one. Forcing-round is an integer, r. Pairs is a ; list of n pairs, each of the form (assumnotes . clause). It was generated by ; cleaning up n0 assumptions. We are about to pour all n clauses into the ; waterfall, where they will be given clause-ids of the form [r+1]Subgoal i, ; for i from 1 to n, or, if there is only one clause, [r+1]Goal. ; The list of assumnotes associated with each clause explain the need for the ; assumption. Each assumnote is a record of that class, containing the cl-id ; of the clause we were working on when we generated the assumption, the rune ; (a symbol as per force-assumption) generating the assumption, and the target ; term to which the rule was being applied. We print a table explaining the ; derivation of the new goals from the old ones and then announce the beginning ; of the next round. (io? prove nil state (n0 forcing-round n pairs) (pprogn (fms "Modulo~#g~[ the following~/ one~/~]~#0~[~/ ~n1~]~#2~[~/ newly~] ~ forced goal~#0~[~/s~], that completes ~#2~[the proof of the input ~ Goal~/Forcing Round ~x3~].~#4~[~/ For what it is worth, the~#0~[~/ ~ ~n1~] new goal~#0~[ was~/s were~] generated by cleaning up ~n5 ~ forced hypotheses.~] See :DOC forcing-round.~%" (list (cons #\g (if (gag-mode) (if (cdr pairs) 2 1) 0)) (cons #\0 (if (cdr pairs) 1 0)) (cons #\1 n) (cons #\2 (if (= forcing-round 0) 0 1)) (cons #\3 forcing-round) (cons #\4 (if (= n0 n) 0 1)) (cons #\5 n0) (cons #\6 (1+ forcing-round))) (proofs-co state) state nil) (process-assumptions-msg1 forcing-round (if (= n 1) nil n) pairs state) (fms "We now undertake Forcing Round ~x0.~%" (list (cons #\0 (1+ forcing-round))) (proofs-co state) state nil)))) (deflabel forcing-round ; Up through Version_3.3, unencumber-assumption removed hypotheses about ; irrelevant variables at the application of force. Since that is no longer ; true, we have eliminated the following paragraph after the paragraph ; mentioning the ``clean them up'' process. ; For example, suppose the main goal is about some term ; ~c[(pred (xtrans i) i)] and that some rule rewriting ~c[pred] contains a ; ~il[force]d hypothesis that the first argument is a ~c[good-inputp]. ; Suppose that during the proof of Subgoal 14 of the main goal, ; ~c[(good-inputp (xtrans i))] is ~il[force]d in a context in which ~c[i] is ; an ~ilc[integerp] and ~c[x] is a ~ilc[consp]. (Note that ~c[x] is ; irrelevant.) Suppose finally that during the proof of Subgoal 28, ; ~c[(good-inputp (xtrans i))] is ~il[force]d ``again,'' but this time in a ; context in which ~c[i] is a ~ilc[rationalp] and ~c[x] is a ~ilc[symbolp]. ; Since the ~il[force]d hypothesis does not mention ~c[x], we deem the ; contextual information about ~c[x] to be irrelevant and discard it ; from both contexts. We are then left with two ~il[force]d assumptions: ; ~c[(implies (integerp i) (good-inputp (xtrans i)))] from Subgoal 14, ; and ~c[(implies (rationalp i) (good-inputp (xtrans i)))] from Subgoal ; 28. Note that if we can prove the assumption required by Subgoal 28 ; we can easily get that for Subgoal 14, since the context of Subgoal ; 28 is the more general. Thus, in the next forcing round we will ; attempt to prove just ; ~bv[] ; (implies (rationalp i) (good-inputp (xtrans i))) ; ~ev[] ; and ``blame'' both Subgoal 14 and Subgoal 28 of the previous round ; for causing us to prove this. :doc ":Doc-Section Miscellaneous a section of a proof dealing with ~il[force]d assumptions~/ If ACL2 ``~il[force]s'' some hypothesis of some rule to be true, it is obliged later to prove the hypothesis. ~l[force]. ACL2 delays the consideration of ~il[force]d hypotheses until the main goal has been proved. It then undertakes a new round of proofs in which the main goal is essentially the conjunction of all hypotheses ~il[force]d in the preceding proof. Call this round of proofs the ``Forcing Round.'' Additional hypotheses may be ~il[force]d by the proofs in the Forcing Round. The attempt to prove these hypotheses is delayed until the Forcing Round has been successfully completed. Then a new Forcing Round is undertaken to prove the recently ~il[force]d hypotheses and this continues until no hypotheses are ~il[force]d. Thus, there is a succession of Forcing Rounds.~/ The Forcing Rounds are enumerated starting from 1. The Goals and Subgoals of a Forcing Round are printed with the round's number displayed in square brackets. Thus, ~c[\"[1~]Subgoal 1.3\"] means that the goal in question is Subgoal 1.3 of the 1st forcing round. To supply a hint for use in the proof of that subgoal, you should use the goal specifier ~c[\"[1~]Subgoal 1.3\"]. ~l[goal-spec]. When a round is successfully completed ~-[] and for these purposes you may think of the proof of the main goal as being the 0th forcing round ~-[] the system collects all of the assumptions ~il[force]d by the just-completed round. Here, an assumption should be thought of as an implication, ~c[(implies context hyp)], where context describes the context in which hyp was assumed true. Before undertaking the proofs of these assumptions, we try to ``clean them up'' in an effort to reduce the amount of work required. This is often possible because the ~il[force]d assumptions are generated by the same rule being applied repeatedly in a given context. By delaying and collecting the ~c[forced] assumptions until the completion of the ``main goal'' we gain two advantages. First, the user gets confirmation that the ``gist'' of the proof is complete and that all that remains are ``technical details.'' Second, by delaying the proofs of the ~il[force]d assumptions ACL2 can undertake the proof of each assumption only once, no matter how many times it was ~il[force]d in the main goal. In order to indicate which proof steps of the previous round were responsible for which ~il[force]d assumptions, we print a sentence explaining the origins of each newly ~il[force]d goal. For example, ~bv[] [1]Subgoal 1, below, will focus on (GOOD-INPUTP (XTRANS I)), which was forced in Subgoal 14, above, by applying (:REWRITE PRED-CRUNCHER) to (PRED (XTRANS I) I), and Subgoal 28, above, by applying (:REWRITE PRED-CRUNCHER) to (PRED (XTRANS I) I). ~ev[] In this entry, ``[1]Subgoal 1'' is the name of a goal which will be proved in the next forcing round. On the next line we display the ~il[force]d hypothesis, call it ~c[x], which is ~c[(good-inputp (xtrans i))] in this example. This term will be the conclusion of the new subgoal. Since the new subgoal will be printed in its entirety when its proof is undertaken, we do not here exhibit the context in which ~c[x] was ~il[force]d. The sentence then lists (possibly a succession of) a goal name from the just-completed round and some step in the proof of that goal that ~il[force]d ~c[x]. In the example above we see that Subgoals 14 and 28 of the just-completed proof ~il[force]d ~c[(good-inputp (xtrans i))] by applying ~c[(:rewrite pred-cruncher)] to the term ~c[(pred (xtrans i) i)]. If one were to inspect the theorem prover's description of the proof steps applied to Subgoals 14 and 28 one would find the word ``~il[force]d'' (or sometimes ``forcibly'') occurring in the commentary. Whenever you see that word in the output, you know you will get a subsequent forcing round to deal with the hypotheses ~il[force]d. Similarly, if at the beginning of a forcing round a ~il[rune] is blamed for causing a ~il[force] in some subgoal, inspection of the commentary for that subgoal will reveal the word ``~il[force]d'' after the rule name blamed. Most ~il[force]d hypotheses come from within the prover's simplifier. When the simplifier encounters a hypothesis of the form ~c[(force hyp)] it first attempts to establish it by rewriting ~c[hyp] to, say, ~c[hyp']. If the truth or falsity of ~c[hyp'] is known, forcing is not required. Otherwise, the simplifier actually ~il[force]s ~c[hyp']. That is, the ~c[x] mentioned above is ~c[hyp'], not ~c[hyp], when the ~il[force]d subgoal was generated by the simplifier. Once the system has printed out the origins of the newly ~il[force]d goals, it proceeds to the next forcing round, where those goals are individually displayed and attacked. At the beginning of a forcing round, the ~il[enable]d structure defaults to the global ~il[enable]d structure. For example, suppose some ~il[rune], ~c[rune], is globally ~il[enable]d. Suppose in some event you ~il[disable] the ~il[rune] at ~c[\"Goal\"] and successfully prove the goal but ~il[force] ~c[\"[1~]Goal\"]. Then during the proof of ~c[\"[1~]Goal\"], ~il[rune] is ~il[enable]d ``again.'' The right way to think about this is that the ~il[rune] is ``still'' ~il[enable]d. That is, it is ~il[enable]d globally and each forcing round resumes with the global ~il[enable]d structure.") (deflabel failure :doc ":Doc-Section Miscellaneous how to deal with a proof failure~/ When ACL2 gives up it does not mean that the submitted conjecture is invalid, even if the last formula ACL2 printed in its proof attempt is manifestly false. Since ACL2 sometimes ~il[generalize]s the goal being proved, it is possible it adopted an invalid subgoal as a legitimate (but doomed) strategy for proving a valid goal. Nevertheless, conjectures submitted to ACL2 are often invalid and the proof attempt often leads the careful reader to the realization that a hypothesis has been omitted or that some special case has been forgotten. It is good practice to ask yourself, when you see a proof attempt fail, whether the conjecture submitted is actually a theorem.~/ If you think the conjecture is a theorem, then you must figure out from ACL2's output what you know that ACL2 doesn't about the functions in the conjecture and how to impart that knowledge to ACL2 in the form of rules. The ``key checkpoint'' information printed at the end of the summary provides a fine place to start. ~l[the-method] for a general discussion of how to prove theorems with ACL2, and ~pl[introduction-to-the-theorem-prover] for a more detailed tutorial. Also ~pl[set-gag-mode] for discussion of key checkpoints and an abbreviated output mode that focuses attention on them. You may find it most useful to start by focusing on key checkpoints that are not under a proof by induction, if any, both because these are more likely to suggest useful lemmas and because they are more likely to be theorems; for example, generalization may have occurred before a proof by induction has begun. If you need more information than is provided by the key checkpoints ~-[] although this should rarely be necessary ~-[] then you can look at the full proof, perhaps with the aid of certain utilities: ~pl[proof-tree], ~pl[set-gag-mode], and ~pl[set-saved-output]. For information on a tool to help debug failures of ~ilc[encapsulate] and ~ilc[progn] events, as well as ~ilc[certify-book] failures, ~pl[redo-flat]. Again, ~pl[the-method] for a general discussion of how to prove theorems with ACL2, and ~pl[introduction-to-the-theorem-prover] for a more detailed tutorial. See also the book ``Computer-Aided Reasoning: An Approach'' (Kaufmann, Manolios, Moore), as well as the discussion of how to read Nqthm proofs and how to use Nqthm rules in ``A Computational Logic Handbook'' by Boyer and Moore (Academic Press, 1988). If the failure occurred during a forcing round, ~pl[failed-forcing].") (deflabel failed-forcing :doc ":Doc-Section Miscellaneous how to deal with a proof ~il[failure] in a forcing round~/ ~l[forcing-round] for a background discussion of the notion of forcing rounds. When a proof fails during a forcing round it means that the ``gist'' of the proof succeeded but some ``technical detail'' failed. The first question you must ask yourself is whether the ~il[force]d goals are indeed theorems. We discuss the possibilities below.~/ If you believe the ~il[force]d goals are theorems, you should follow the usual methodology for ``fixing'' failed ACL2 proofs, e.g., the identification of key lemmas and their timely and proper use as rules. ~l[failure], ~pl[gag-mode], and ~pl[proof-tree]. The rules designed for the goals of forcing rounds are often just what is needed to prove the ~il[force]d hypothesis at the time it is ~il[force]d. Thus, you may find that when the system has been ``taught'' how to prove the goals of the forcing round no forcing round is needed. This is intended as a feature to help structure the discovery of the necessary rules. If a hint must be provided to prove a goal in a forcing round, the appropriate ``goal specifier'' (the string used to identify the goal to which the hint is to be applied) is just the text printed on the line above the formula, e.g., ~c[\"[1~]Subgoal *1/3''\"]. ~l[goal-spec]. If you solve a forcing problem by giving explicit ~il[hints] for the goals of forcing rounds, you might consider whether you could avoid forcing the assumption in the first place by giving those ~il[hints] in the appropriate places of the main proof. This is one reason that we print out the origins of each ~il[force]d assumption. An argument against this style, however, is that an assumption might be ~il[force]d in hundreds of places in the main goal and proved only once in the forcing round, so that by delaying the proof you actually save time. We now turn to the possibility that some goal in the forcing round is not a theorem. There are two possibilities to consider. The first is that the original theorem has insufficient hypotheses to ensure that all the ~il[force]d hypotheses are in fact always true. The ``fix'' in this case is to amend the original conjecture so that it has adequate hypotheses. A more difficult situation can arise and that is when the conjecture has sufficient hypotheses but they are not present in the forcing round goal. This can be caused by what we call ``premature'' forcing. Because ACL2 rewrites from the inside out, it is possible that it will ~il[force] hypotheses while the context is insufficient to establish them. Consider trying to prove ~c[(p x (foo x))]. We first rewrite the formula in an empty context, i.e., assuming nothing. Thus, we rewrite ~c[(foo x)] in an empty context. If rewriting ~c[(foo x)] ~il[force]s anything, that ~il[force]d assumption will have to be proved in an empty context. This will likely be impossible. On the other hand, suppose we did not attack ~c[(foo x)] until after we had expanded ~c[p]. We might find that the value of its second argument, ~c[(foo x)], is relevant only in some cases and in those cases we might be able to establish the hypotheses ~il[force]d by ~c[(foo x)]. Our premature forcing is thus seen to be a consequence of our ``over eager'' rewriting. Here, just for concreteness, is an example you can try. In this example, ~c[(foo x)] rewrites to ~c[x] but has a ~il[force]d hypothesis of ~c[(rationalp x)]. ~c[P] does a case split on that very hypothesis and uses its second argument only when ~c[x] is known to be rational. Thus, the hypothesis for the ~c[(foo x)] rewrite is satisfied. On the false branch of its case split, ~c[p] simplies to ~c[(p1 x)] which can be proved under the assumption that ~c[x] is not rational. ~bv[] (defun p1 (x) (not (rationalp x))) (defun p (x y)(if (rationalp x) (equal x y) (p1 x))) (defun foo (x) x) (defthm foo-rewrite (implies (force (rationalp x)) (equal (foo x) x))) (in-theory (disable foo)) ~ev[] The attempt then to do ~c[(thm (p x (foo x)))] ~il[force]s the unprovable goal ~c[(rationalp x)]. Since all ``formulas'' are presented to the theorem prover as single terms with no hypotheses (e.g., since ~ilc[implies] is a function), this problem would occur routinely were it not for the fact that the theorem prover expands certain ``simple'' definitions immediately without doing anything that can cause a hypothesis to be ~il[force]d. ~l[simple]. This does not solve the problem, since it is possible to hide the propositional structure arbitrarily deeply. For example, one could define ~c[p], above, recursively so that the test that ~c[x] is rational and the subsequent first ``real'' use of ~c[y] occurred arbitrarily deeply. Therefore, the problem remains: what do you do if an impossible goal is ~il[force]d and yet you know that the original conjecture was adequately protected by hypotheses? One alternative is to disable forcing entirely. ~l[disable-forcing]. Another is to ~il[disable] the rule that caused the ~il[force]. A third alternative is to prove that the negation of the main goal implies the ~il[force]d hypothesis. For example, ~bv[] (defthm not-p-implies-rationalp (implies (not (p x (foo x))) (rationalp x)) :rule-classes nil) ~ev[] Observe that we make no rules from this formula. Instead, we merely ~c[:use] it in the subgoal where we must establish ~c[(rationalp x)]. ~bv[] (thm (p x (foo x)) :hints ((\"Goal\" :use not-p-implies-rationalp))) ~ev[] When we said, above, that ~c[(p x (foo x))] is first rewritten in an empty context we were misrepresenting the situation slightly. When we rewrite a literal we know what literal we are rewriting and we implicitly assume it false. This assumption is ``dangerous'' in that it can lead us to simplify our goal to ~c[nil] and give up ~-[] we have even seen people make the mistake of assuming the negation of what they wished to prove and then via a very complicated series of transformations convince themselves that the formula is false. Because of this ``tail biting'' we make very weak use of the negation of our goal. But the use we make of it is sufficient to establish the ~il[force]d hypothesis above. A fourth alternative is to weaken your desired theorem so as to make explicit the required hypotheses, e.g., to prove ~bv[] (defthm rationalp-implies-main (implies (rationalp x) (p x (foo x))) :rule-classes nil) ~ev[] This of course is unsatisfying because it is not what you originally intended. But all is not lost. You can now prove your main theorem from this one, letting the ~ilc[implies] here provide the necessary case split. ~bv[] (thm (p x (foo x)) :hints ((\"Goal\" :use rationalp-implies-main))) ~ev[]") (defun count-assumptions (ttree) ; The soundness of the system depends on this function returning 0 only if ; there are no assumptions. (length (tagged-objects 'assumption ttree))) (defun add-type-alist-runes-to-ttree1 (type-alist runes) (cond ((endp type-alist) runes) (t (add-type-alist-runes-to-ttree1 (cdr type-alist) (all-runes-in-ttree (cddr (car type-alist)) runes))))) (defun add-type-alist-runes-to-ttree (type-alist ttree) (let* ((runes0 (tagged-objects 'lemma ttree)) (runes1 (add-type-alist-runes-to-ttree1 type-alist runes0))) (cond ((null runes1) ttree) ((null runes0) (extend-tag-tree 'lemma runes1 ttree)) (t (extend-tag-tree 'lemma runes1 (remove-tag-from-tag-tree! 'lemma ttree)))))) (defun process-assumptions-ttree (assns ttree) ; Assns is a list of assumptions records. We extend ttree with all runes in ; assns. (cond ((endp assns) ttree) (t (process-assumptions-ttree (cdr assns) (add-type-alist-runes-to-ttree (access assumption (car assns) :type-alist) ttree))))) (defun process-assumptions (forcing-round pspv wrld state) ; This function is called when prove-loop1 appears to have won the ; indicated forcing-round, producing pspv. We inspect the :tag-tree ; in pspv and determines whether there are forced 'assumptions in it. ; If so, the "win" reported is actually conditional upon the ; successful relieving of those assumptions. We create an appropriate ; set of clauses to prove, new-clauses, each paired with a list of ; assumnotes. We also return a modified pspv, new-pspv, ; just like pspv except with the assumptions stripped out of its ; :tag-tree. We do the output related to explaining all this to the ; user and return (mv new-clauses new-pspv state). If new-clauses is ; nil, then the proof is really done. Otherwise, we are obliged to ; prove new-clauses under new-pspv and should do so in another "round" ; of forcing. (let ((n (count-assumptions (access prove-spec-var pspv :tag-tree)))) (pprogn (cond ((= n 0) (pprogn ; We normally print "Q.E.D." for a successful proof done in gag-mode even if ; proof output is inhibited. However, if summary output is also inhibited, ; then we guess that the user probably would prefer not to be bothered seeing ; the "Q.E.D.". (if (and (saved-output-token-p 'prove state) (member-eq 'prove (f-get-global 'inhibit-output-lst state)) (not (member-eq 'summary (f-get-global 'inhibit-output-lst state)))) (fms "Q.E.D.~%" nil (proofs-co state) state nil) state) (io? prove nil state nil (fms "Q.E.D.~%" nil (proofs-co state) state nil)))) (t (io? prove nil state (n) (fms "q.e.d. (given ~n0 forced ~#1~[hypothesis~/hypotheses~])~%" (list (cons #\0 n) (cons #\1 (if (= n 1) 0 1))) (proofs-co state) state nil)))) (mv-let (n0 assns pairs ttree1) (extract-and-clausify-assumptions nil ;;; irrelevant with only-immediatep = nil (access prove-spec-var pspv :tag-tree) nil ;;; all assumptions, not only-immediatep ; Note: We here obtain the enabled structure. Because the rewrite-constant of ; the pspv is restored after being smashed by hints, we know that this enabled ; structure is in fact the one in the pspv on which prove was called, which is ; the global enabled structure if prove was called by defthm. (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :current-enabled-structure) wrld (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :splitter-output)) (cond ((= n0 0) (mv nil pspv state)) (t (pprogn (process-assumptions-msg forcing-round n0 (length assns) pairs state) (mv pairs (change prove-spec-var pspv :tag-tree (process-assumptions-ttree assns ttree1) ; Note: In an earlier version of this code, we failed to set :otf-flg here and ; that caused us to backup and try to prove the original thm (i.e., "Goal") by ; induction. :otf-flg t) state)))))))) (defun do-not-induct-msg (forcing-round pool-lst state) ; We print a message explaining that because of :do-not-induct, we quit. ; This function increments timers. Upon entry, the accumulated time is ; charged to 'prove-time. The time spent in this function is charged ; to 'print-time. (io? prove nil state (forcing-round pool-lst) (pprogn (increment-timer 'prove-time state) ; It is probably a good idea to keep the following wording in sync with ; push-clause-msg1. (fms "Normally we would attempt to prove ~@0 by induction. However, a ~ :DO-NOT-INDUCT hint was supplied to abort the proof attempt.~|" (list (cons #\0 (tilde-@-pool-name-phrase forcing-round pool-lst))) (proofs-co state) state nil) (increment-timer 'print-time state)))) (defun prove-loop2 (forcing-round pool-lst clauses pspv hints ens wrld ctx state step-limit) ; We are given some clauses to prove. Forcing-round and pool-lst are the first ; two fields of the clause-ids for the clauses. The pool of the prove spec ; var, pspv, in general contains some more clauses to work on, as well as some ; clauses tagged 'being-proved-by-induction. In addition, the pspv contains ; the proper settings for the induction-hyp-terms and induction-concl-terms. ; Actually, when we are beginning a forcing round other than the first, clauses ; is really a list of pairs (assumnotes . clause). ; We pour all the clauses over the waterfall. They tumble into the pool in ; pspv. If the pool is then empty, we are done. Otherwise, we pick one to ; induct on, do the induction and repeat. ; We return a tuple (mv new-step-limit error value state). Either we cause an ; error (i.e., return a non-nil error as the second result), or else the value ; result is the final tag-tree. That tag-tree might contain some byes, ; indicating that the proof has failed. ; WARNING: A non-erroneous return is not equivalent to success! (sl-let (erp pspv jppl-flg state) (pstk (waterfall forcing-round pool-lst clauses pspv hints ens wrld ctx state step-limit)) (cond (erp (mv step-limit t nil state)) (t (mv-let (signal pool-lst clauses hint-settings pspv state) (pstk (pop-clause forcing-round pspv jppl-flg state)) (cond ((eq signal 'win) (mv-let (pairs new-pspv state) (pstk (process-assumptions forcing-round pspv wrld state)) (mv-let (erp ttree state) (accumulate-ttree-and-step-limit-into-state (access prove-spec-var new-pspv :tag-tree) step-limit state) (assert$ (null erp) (cond ((null pairs) (mv step-limit nil ttree state)) (t (prove-loop2 (1+ forcing-round) nil pairs (initialize-pspv-for-gag-mode new-pspv) hints ens wrld ctx state step-limit))))))) ; The following case can probably be removed. It is probably left over from ; some earlier implementation of pop-clause. The earlier code for the case ; below returned (value (access prove-spec-var pspv :tag-tree)), this case, and ; was replaced by the hard error on 5/5/00. ((eq signal 'bye) (mv step-limit t (er hard ctx "Surprising case in prove-loop2; please contact the ACL2 ~ implementors!") state)) ((eq signal 'lose) (mv step-limit t nil state)) ((and (cdr (assoc-eq :do-not-induct hint-settings)) (not (assoc-eq :induct hint-settings))) ; There is at least one goal left to prove, yet :do-not-induct is currently in ; force. How can that be? The user may have supplied :do-not-induct t while ; also supplying :otf-flg t. In that case, push-clause will return a "hit". We ; believe that the hint-settings current at this time will reflect the ; appropriate action if :do-not-induct t is intended here, i.e., the test above ; will put us in this case and we will abort the proof. (pprogn (do-not-induct-msg forcing-round pool-lst state) (mv step-limit t nil state))) (t (mv-let (signal clauses pspv state) (pstk (induct forcing-round pool-lst clauses hint-settings pspv wrld ctx state)) ; We do not call maybe-warn-about-theory-from-rcnsts below, because we already ; made such a call before the goal was pushed for proof by induction. (cond ((eq signal 'lose) (mv step-limit t nil state)) (t (prove-loop2 forcing-round pool-lst clauses pspv hints ens wrld ctx state step-limit))))))))))) (defun prove-loop1 (forcing-round pool-lst clauses pspv hints ens wrld ctx state) (sl-let (erp val state) (catch-step-limit (prove-loop2 forcing-round pool-lst clauses pspv hints ens wrld ctx state (initial-step-limit wrld state))) (pprogn (f-put-global 'last-step-limit step-limit state) (mv erp val state)))) (defun print-pstack-and-gag-state (state) ; When waterfall parallelism is enabled, and the user has to interrupt a proof ; twice before it quits, the prover will attempt to print the gag state and ; pstack. Based on observation by Rager, the pstack tends to be long and ; irrelevant in this case. So, we disable the printing of the pstack when ; waterfall parallelism is enabled and waterfall-printing is something other ; than :full. We considered not involving the current value for ; waterfall-printing, but using the :full setting is a strange thing to begin ; with. So, we make the decision that if a user goes to the effort to use the ; :full waterfall-printing mode, that maybe they'd like to see the pstack after ; all. ; The below #+acl2-par change in definition also results in not printing ; gag-state under these conditions. However, this is effectively a no-op, ; because the parallel waterfall does not save anything to gag-state anyway. (cond #+acl2-par ((and (f-get-global 'waterfall-parallelism state) (not (eql (f-get-global 'waterfall-printing state) :full))) state) (t (prog2$ (cw "Here is the current pstack [see :DOC pstack]:") (mv-let (erp val state) (pstack) (declare (ignore erp val)) (print-gag-state state)))))) (defun prove-loop0 (clauses pspv hints ens wrld ctx state) ; Warning: This function assumes that *acl2-time-limit* has already been ; let-bound in raw Lisp by bind-acl2-time-limit. ; The perhaps unusual structure below is intended to invoke ; print-pstack-and-gag-state only when there is a hard error such as an ; interrupt. In the normal failure case, the pstack is not printed and the ; key checkpoint summary (from the gag-state) is printed after the summary. (state-global-let* ((guard-checking-on nil) ; see the Essay on Guard Checking (in-prove-flg t)) (mv-let (interrupted-p erp-val state) (acl2-unwind-protect "prove-loop" (mv-let (erp val state) (prove-loop1 0 nil clauses pspv hints ens wrld ctx state) (mv nil (cons erp val) state)) (print-pstack-and-gag-state state) state) (cond (interrupted-p (mv t nil state)) (t (mv (car erp-val) (cdr erp-val) state)))))) (defmacro bind-acl2-time-limit (form) ; The raw Lisp code for this macro arranges that *acl2-time-limit* is restored ; to its global value (presumably nil) after we exit its top-level call. ; Consider the following key example of how this can work. Suppose ; *acl2-time-limit* is set to 0 by our-abort because of an interrupt. ; Inspection of the code for our-abort shows that *acl2-time-limit-boundp* must ; be true in that case; but then we must be in the dynamic scope of ; bind-acl2-time-limit, as that is the only legal way for ; *acl2-time-limit-boundp* to be bound or set. But inside bind-acl2-time-limit ; we are only modifying a let-bound *acl2-time-limit*, not its global value. ; In summary, setting *acl2-time-limit* to 0 by our-abort will not change the ; global value of *acl2-time-limit*. #-acl2-loop-only `(if *acl2-time-limit-boundp* ,form (let ((*acl2-time-limit-boundp* t) (*acl2-time-limit* *acl2-time-limit*)) ,form)) #+acl2-loop-only form) (defun prove-loop (clauses pspv hints ens wrld ctx state) ; We either cause an error or return a ttree. If the ttree contains ; :byes, the proof attempt has technically failed, although it has ; succeeded modulo the :byes. #-acl2-loop-only (setq *deep-gstack* nil) ; in case we never call initial-gstack #+(and hons (not acl2-loop-only)) (when (memoizedp-raw 'worse-than-builtin) (clear-memoize-table 'worse-than-builtin)) (prog2$ (clear-pstk) (pprogn (increment-timer 'other-time state) (f-put-global 'bddnotes nil state) (if (gag-mode) (pprogn (f-put-global 'gag-state *initial-gag-state* state) (f-put-global 'gag-state-saved nil state)) state) (mv-let (erp ttree state) (bind-acl2-time-limit ; make *acl2-time-limit* be let-bound (prove-loop0 clauses pspv hints ens wrld ctx state)) (progn$ #+(and hons (not acl2-loop-only)) (when (memoizedp-raw 'worse-than-builtin) (clear-memoize-table 'worse-than-builtin)) (pprogn (increment-timer 'prove-time state) (cond (erp (mv erp nil state)) (t (value ttree))))))))) (defmacro make-pspv (ens wrld &rest args) ; This macro is similar to make-rcnst, which is a little easier to understand. ; (make-pspv ens w) will make a pspv that is just *empty-prove-spec-var* except ; that the rewrite constant is (make-rcnst ens w). More generally, you may use ; args to supply a list of alternating keyword/value pairs to override the ; default settings. E.g., ; (make-pspv w :rewrite-constant rcnst :displayed-goal dg) ; will make a pspv that is like the empty one except for the two fields ; listed above. ; Note: Ens and wrld are only used in the default setting of the ; :rewrite-constant. If you supply a :rewrite-constant in args, then ens and ; wrld are actually irrelevant. `(change prove-spec-var (change prove-spec-var *empty-prove-spec-var* :rewrite-constant (make-rcnst ,ens ,wrld :splitter-output (splitter-output))) ,@args)) (defun chk-assumption-free-ttree (ttree ctx state) ; Let ttree be the ttree about to be returned by prove. We do not want this ; tree to contain any 'assumption tags because that would be a sign that an ; assumption got ignored. For similar reasons, we do not want it to contain ; any 'fc-derivation tags -- assumptions might be buried therein. This ; function checks these claimed invariants of the final ttree and causes an ; error if they are violated. ; This check is stronger than necessary, of course, since an fc-derivation ; object need not contain an assumption. See also contains-assumptionp for a ; slightly more expensive, but more precise, check. ; A predicate version of this function is assumption-free-ttreep and it should ; be kept in sync with this function, as should chk-assumption-free-ttree-1. ; While this function causes a hard error, its functionality is that of a soft ; error because it is so like our normal checkers. (cond ((tagged-objectsp 'assumption ttree) (mv t (er hard ctx "The 'assumption ~x0 was found in the final ttree!" (car (tagged-objects 'assumption ttree))) state)) ((tagged-objectsp 'fc-derivation ttree) (mv t (er hard ctx "The 'fc-derivation ~x0 was found in the final ttree!" (car (tagged-objects 'fc-derivation ttree))) state)) (t (value nil)))) #+(and write-arithmetic-goals (not acl2-loop-only)) (when (not (boundp '*arithmetic-goals-fns*)) (defconstant *arithmetic-goals-fns* '(< = abs acl2-numberp binary-* binary-+ case-split complex-rationalp denominator equal evenp expt fix floor force if iff ifix implies integerp mod natp nfix not numerator oddp posp rationalp synp unary-- unary-/ zerop zip zp signum booleanp nonnegative-integer-quotient rem truncate ash lognot binary-logand binary-logior binary-logxor))) #+(and write-arithmetic-goals (not acl2-loop-only)) (when (not (boundp '*arithmetic-goals-filename*)) (defconstant *arithmetic-goals-filename* ; Be sure to delete ~/write-arithmetic-goals.lisp before starting a regression. ; (This is done by GNUmakefile.) (let ((home (our-user-homedir-pathname))) (cond (home (merge-pathnames home "write-arithmetic-goals.lisp")) (t (error "Unable to determine (user-homedir-pathname).")))))) (defun prove (term pspv hints ens wrld ctx state) ; Term is a translated term. Displayed-goal is any object and is ; irrelevant except for output purposes. Hints is a list of pairs ; as returned by translate-hints. ; We try to prove term using the given hints and the rules in wrld. ; Note: Having prove use hints is a break from nqthm, where only ; prove-lemma used hints. ; This function returns the traditional three values of an error ; producing/output producing function. The first value is a Boolean ; that indicates whether an error occurred. We cause an error if we ; terminate without proving term. Hence, if the first result is nil, ; term was proved. The second is a ttree that describes the proof, if ; term is proved. The third is the final value of state. ; Displayed-goal is relevant only for output purposes. We assume that ; this object was prettyprinted to the user before prove was called ; and is, in the user's mind, what is being proved. For example, ; displayed-goal might be the untranslated -- or pre-translated -- ; form of term. The only use made of displayed-goal is that if the ; very first transformation we make produces a clause that we would ; prettyprint as displayed-goal, we hide that transformation from the ; user. ; Commemorative Plaque: ; We began the creation of the ACL2 with an empty GNU Emacs buffer on ; August 14, 1989. The first few days were spent writing down the ; axioms for the most primitive functions. We then began writing ; experimental applicative code for macros such as cond and ; case-match. The first weeks were dizzying because of the confusion ; in our minds over what was in the logic and what was in the ; implementation. On November 3, 1989, prove was debugged and ; successfully did the associativity of append. During that 82 days ; we worked our more or less normal 8 hours, plus an hour or two on ; weekday nights. In general we did not work weekends, though there ; might have been two or three where an 8 hour day was put in. We ; worked separately, "contracting" with one another to do the various ; parts and meeting to go over the code. Bill Schelter was extremely ; helpful in tuning akcl for us. Several times we did massive ; rewrites as we changed the subset or discovered new programming ; styles. During that period Moore went to the beach at Rockport one ; weekend, to Carlsbad Caverns for Labor Day, to the University of ; Utah for a 4 day visit, and to MIT for a 4 day visit. Boyer taught ; at UT from September onwards. These details are given primarily to ; provide a measure of how much effort it was to produce this system. ; In all, perhaps we have spent 60 8 hour days each on ACL2, or about ; 1000 man hours. That of course ignores totally the fact that we ; have thought about little else during the past three months, whether ; coding or not. ; The system as it stood November 3, 1989, contained the complete ; nqthm rewriter and simplifier (including metafunctions, compound ; recognizers, linear and a trivial cut at congruence relations that ; did not connect to the user-interface) and induction. It did not ; include destructor elimination, cross-fertilization, generalization ; or the elimination of irrelevance. It did not contain any notion of ; hints or disabledp. The system contained full fledged ; implementations of the definitional principle (with guards and ; termination proofs) and defaxiom (which contains all of the code to ; generate and store rules). The system did not contain the ; constraint or functional instantiation events or books. We have not ; yet had a "code walk" in which we jointly look at every line. There ; are known bugs in prove (e.g., induction causes a hard error when no ; candidates are found). ; Matt Kaufmann officially joined the project in August, 1993. He had ; previously generated a large number of comments, engaged in a number of ; design discussions, and written some code. ; Bob Boyer requested that he be removed as a co-author of ACL2 in April, 1995, ; because, in his view, he has worked so much less on the project in the last ; few years than Kaufmann and Moore. ; End of Commemorative Plaque ; This function increments timers. Upon entry, the accumulated time is ; charged to 'other-time. The time spent in this function is divided ; between both 'prove-time and to 'print-time. (cond ((ld-skip-proofsp state) (value nil)) (t #+(and write-arithmetic-goals (not acl2-loop-only)) (when (ffnnames-subsetp term *arithmetic-goals-fns*) (with-open-file (str *arithmetic-goals-filename* :direction :output :if-exists :append :if-does-not-exist :create) (let ((*print-pretty* nil) (*package* (find-package-fast "ACL2")) (*readtable* *acl2-readtable*) (*print-escape* t) *print-level* *print-length*) (prin1 term str) (terpri str) (force-output str)))) (progn$ (initialize-brr-stack state) (initialize-fc-wormhole-sites) (er-let* ((ttree1 (prove-loop (list (list term)) (change prove-spec-var pspv :user-supplied-term term :orig-hints hints) hints ens wrld ctx state))) (er-progn (chk-assumption-free-ttree ttree1 ctx state) (let ((byes (tagged-objects :bye ttree1))) (cond (byes (pprogn ; The use of ~*1 below instead of just ~&1 forces each of the defthm forms ; to come out on a new line indented 5 spaces. As is already known with ~&1, ; it can tend to scatter the items randomly -- some on the left margin and others ; indented -- depending on where each item fits flat on the line first offered. (io? prove nil state (wrld byes) (fms "To complete this proof you could try to admit the ~ following event~#0~[~/s~]:~|~%~*1~%See the discussion ~ of :by hints in :DOC hints regarding the ~ name~#0~[~/s~] displayed above." (list (cons #\0 byes) (cons #\1 (list "" "~|~ ~q*." "~|~ ~q*,~|and~|" "~|~ ~q*,~|~%" (make-defthm-forms-for-byes byes wrld)))) (proofs-co state) state (term-evisc-tuple nil state))) (silent-error state))) (t (value ttree1)))))))))) acl2-sources/rewrite.lisp0000664002132200015000000321500412222115527015131 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; We start our development of the rewriter by coding one-way-unify and the ; substitution fns. ; Essay on Equivalence, Refinements, and Congruence-based Rewriting ; (Note: At the moment, the fact that fn is an equivalence relation is ; encoded merely by existence of a non-nil 'coarsenings property. No ; :equivalence rune explaining why fn is an equivalence relation is to ; be found there -- though such a rune does exist and is indeed found ; among the 'congruences of fn itself. We do not track the use of ; equivalence relations, we just use them anonymously. It would be ; good to track them and report them. When we do that, read the Note ; on Tracking Equivalence Runes in subst-type-alist1.) ; (Note: Some of the parenthetical remarks in this code are extremely ; trite observations -- to the ACL2 afficionado -- added when I sent ; this commented code off to friends to read.) ; We will allow the user to introduce new equivalence relations. ; At the moment, they must be functions of two arguments only. ; Parameterized equivlence relations, e.g., x == y (mod n), are ; interesting and may eventually be implemented. But in the spirit of ; getting something done right and working, we start simple. ; An equivalence relation here is any two argument function that has ; been proved to be Boolean, symmetric, reflexive, and transitive. ; The rule-class :EQUIVALENCE indicates that a given theorem ; establishes that equiv is an equivalence relation. (In the ; tradition of Nqthm, the ACL2 user tells the system how to use a ; theorem when the theorem is submitted by the user. These instructions ; are called "rule classes". A typical "event" might therefore be: ; (defthm set-equal-is-an-equivalence-rel ; (and (booleanp (set-equal x y)) ; (set-equal x x) ; (implies (set-equal x y) (set-equal y x)) ; (implies (and (set-equal x y) ; (set-equal y z)) ; (set-equal x z))) ; :rule-classes :EQUIVALENCE) ; The rule class :EQUIVALENCE just alerts the system that this ; formula states that something is an equivalence relation. If ; the formula is proved, the system identifies set-equal as the ; relation and adds to the database certain information that ; enables the processing described here.) ; The Boolean requirement is imposed for coding convenience. In ; assume-true-false, for example, when we assume (equiv x y) true, we ; simply give it the type-set *ts-t*, rather than something ; complicated like its full type-set take away *ts-nil*. In addition, ; the Boolean requirement means that (equiv x y) is equal to (equiv y ; x) (not just propositionally) and hence we can commute it at will. ; The other three requirements are the classic ones for an equivalence ; relation. All three are exploited. Symmetry is used to justify ; commutativity, which currently shows up in assume-true-false when we ; put either (equiv x y) or (equiv y x) on the type-alist -- depending ; on term-order -- and rely on it to assign the value of either. ; Reflexivity is used to eliminate (equiv x term) as a hypothesis when ; x does not occur in term or elsewhere in the clause. Transitivity ; is used throughout the rewriting process. These are not guaranteed ; to be all the places these properties are used! ; Note: Some thought has been given to the idea of generalizing our ; work to non-symmetric reflexive and transitive relations. We have ; seen occasional utility for the idea of rewriting with such a monotonic ; relation, replacing a term by a stronger or more defined one. But to ; implement that we feel it should be done in a completely independent ; second pass in which monotonic relations are considered. Equivalence ; relations are of such importance that we did not want to risk doing them ; weakly just to allow this esoteric variant. ; Note: We explicitly check that an equivalence relation has no guard ; because we never otherwise consider their guards. (The ; "guard" on an ACL2 function definition is a predicate that must be ; true of the actuals in order for the defining equation to hold. It ; can be thought of as a "precondition" or a characterization of the ; domain of the function definition. In Common Lisp (and ACL2 is just ; a subset of Common Lisp) many functions, e.g., car and cdr, are not ; defined everywhere and guards are our way of taking note of this. ; Equivalence relations have "no" guard, meaning their guard is t, ; i.e., they are defined everywhere.) ; The motivation behind equivalence relations is to allow their use ; as :REWRITE rules. For example, after set-equal has been proved to be ; an equivalence relation and union-eq, say, has been proved to be ; commutative (wrt set-equal), ; (implies (and (symbol-listp a) ; (true-listp a) ; (symbol-listp b) ; (true-listp b)) ; (set-equal (union-eq a b) (union-eq b a))) ; then we would like to be able to use the above rule as a rewrite ; rule to commute union-eq expressions. Of course, this is only ; allowed in situations in which it is sufficient to maintain ; set-equality as we rewrite. Implicit in this remark is the idea ; that the rewriter is given an equivalence relation to maintain as it ; rewrites. This is a generalization of id/iff flag in Nqthm's ; rewriter; that flag indicates whether the rewriter is maintaining ; identity or propositional equivalence. :CONGRUENCE lemmas, ; discussed later, inform the rewriter of the appropriate relations to ; maintain as it steps from (fn a1 ... an) to the ai. But given a ; relation to maintain and a term to rewrite, the rewriter looks at ; all the :REWRITE rules available and applies those that maintain the ; given relation. ; For example, suppose the rewriter is working on (memb x (union-eq b ; a)), where memb is a function that returns t or nil according to ; whether its first argument is an element of its second. Suppose the ; rewriter is to maintain identity during this rewrite, i.e., it is to ; maintain the equivalence relation equal. Suppose a :CONGRUENCE rule ; informs us that equal can be preserved on memb expressions by ; maintaining set-equal on the second argument. Then when rewriting ; the second argument to the memb, rewrite shifts from maintaining ; equal to maintaining set-equal. This enables it to use the above ; theorem as a rewrite rule, replacing (union-eq b a) by (union-eq a ; b), just as Nqthm would had the connecting relation been equal ; instead of set-equal. ; This raises the problem of refinements. For example, we may have ; some rules about union-eq that are expressed with equal rather than ; set-equal. For example, the definition of union-eq is an equality! ; It is clear that a rule may be tried if its connecting equivalence ; relation is a refinement of the one we wish to maintain. By ; ``equiv1 is a refinement of equiv2'' we mean ; (implies (equiv1 x y) (equiv2 x y)). ; Such rules are called :REFINEMENT rules and are a distinguished ; rule-class, named :REFINEMENT. Every equivalence relation is a ; refinement of itself. Equal is a refinement of every equivalence ; relation and no other relation is a refinement of equal. ; Every equivalence relation, fn, has a non-nil value for the property ; 'coarsenings. The value of the property is a list of all ; equivalence relations (including fn itself) known to admit fn as a ; refinement. This list is always closed under the transitivity of ; refinement. That is, if e1 is a refinement of e2 and e2 is a ; refinement of e3, then the 'coarsenings for e1 includes e1 (itself), ; e2 (of course), and e3 (surprise!). This makes it easier to answer ; quickly the question of who is a refinement of whom. ; Equivalence relations are the only symbols with non-nil 'coarsenings ; properties, thus this is the way they are recognized. Furthermore, ; the 'coarsenings property of 'equal will always list all known ; equivalence relations. ; When we are rewriting to maintain equiv we use any rule that is a known ; refinement of equiv. Thus, while rewriting to maintain set-equal we can ; use both set-equal rules and equal rules. ; Now we move on to the heart of the matter: knowing what relation to maintain ; at each step. This is where :CONGRUENCE rules come in. ; The key idea in congruence-based rewriting is that lemmas of the form: ; (implies (equiv1 x y) ; (equiv2 (fn a1 ... x ... an) ; (fn a1 ... y ... an))), ; where equiv1 and equiv2 are equivalence relations, the ai, x, and y ; are distinct variables and x and y occur in the kth argument ; position of the n-ary function fn, can be used to rewrite ; fn-expressions, maintaining equiv2, by rewriting the kth argument ; position maintaining equiv1. ; We call such a lemma a ``congruence lemma'' and say that it ; establishes that ``equiv2 is maintained by equiv1 in the kth ; argument of fn.'' The rule-class :CONGRUENCE indicates when a lemma ; is to be so used. ; An example :CONGRUENCE lemma is ; (implies (set-equal a b) (iff (member x a) (member x b))). ; (In my previous example I used memb. Here I use member, the Common ; Lisp function. When member succeeds, it returns the tail of its ; second arg that starts with its first. Thus, (member x a) is not ; necessary equal to (member x b), even when a and b are set-equal. ; But they are propositionally equivalent, i.e., mutually nil or ; non-nil. Iff is just another equivalence relation.) ; That is, iff is maintained by set-equal in the second argument of ; member. Thus, when rewriting a member expression while trying to ; maintain iff it is sufficient merely to maintain set-equivalence on ; the second argument of member. In general we will sweep across the ; arguments of a function maintaining an appropriate equivalence ; relation for each argument as a function of the relation we wish to ; maintain outside. ; A literal interpretation of the lemma above suggests that one ; must maintain identity on the first argument of member in order to ; rely on the lemma in the second argument. What then justifies our ; independent use of :CONGRUENCE lemmas in distict argument positions? ; Congruence Theorem 1. :CONGRUENCE lemmas for different argument ; positions of the same function can be used independently. In ; particular, suppose equiv is maintained by e1 in the kth argument of ; fn and equiv is maintained by e2 in the jth argument of fn, where j ; is not k. Suppose a is e1 to a' and b is e2 to b'. Then (fn ; ...a...b...) is equiv to (fn ...a'...b'...), where a and b occur in ; the kth and jth arguments, respectively. ; Proof. By the :CONGRUENCE lemma for equiv and e1 we know that ; (fn ...a...b...) is equiv (fn ...a'...b...). By the :CONGRUENCE ; lemma for equiv and e2 we know that (fn ...a'...b...) is equiv to ; (fn ...a'...b'...). The desired result is then obtained via the ; transitivity of equiv. Q.E.D. ; While we require the user to formulate :CONGRUENCE lemmas as shown ; above we actually store them in a data structure, called the ; 'congruences property of fn, in which lemmas for different slots ; have been combined. Indeed, we ``generalize'' still further and ; allow for more than one way to rewrite a given argument position. If fn ; has arity n, then the 'congruences property of fn is a list of tuples, ; each of which is of the form (equiv slot1 slot2 ... slotn), where equiv ; is some equivalence relation and each slotk summarizes our knowledge ; of what is allowed in each argument slot of fn while maintaining ; equiv. The entire n+1 tuple is assembled from many different ; :CONGRUENCE lemmas. Indeed, it is modified each time a new ; :CONGRUENCE lemma is proved about fn and equiv. Without discussing ; yet the structure of slotk, such a tuple means: ; (implies (and (or (equiv1.1 x1 y1) ; ... ; (equiv1.i x1 y1)) ; ... ; (or (equivn.1 xn yn) ; ... ; (equivn.j xn yn))) ; (equiv (fn x1 ... xn) ; (fn y1 ... yn))). ; Thus, to rewrite (fn x1 ... xn) maintaining equiv we sweep across ; the arguments rewriting each in turn, maintaining any one of the ; corresponding equivk,l's, which are encoded in the structure of ; slotk. ; Note that each equivk,l above is attributable to one and only one ; :CONGRUENCE lemma. Since the ors cause searching, we allow the user ; to control the search by disabling :CONGRUENCE lemmas. We only ; pursue paths introduced by enabled lemmas. ; The structure of slotk is a list of ``congruence-rules'', which are ; instances of the record (defrec congruence-rule (nume equiv . rune) t) ; The :equiv field is the function symbol of an equivalence relation ; which, if maintained in argument k, is sufficient to maintain equiv ; for the fn-expression, :rune (it stands for "rule name") is the name ; of the :CONGRUENCE lemma that established this link between equiv, ; :equiv, fn, and k, and :nume is the nume of the rune (a "nume" is a ; unique natural number corresponding to a rune, used only to speed up ; the answer to the question: "is the named rule enabled -- i.e., ; among those the user permits us to apply automatically?") allowing ; us to query the enabled structure directly. ; Because we allow more than one :CONGRUENCE rule per argument, we ; have a problem. If we are trying to maintain equiv for fn and are ; rewriting an argument whose slot contains (equivk.1 ... equivk.l), ; what equivalence relation do we try to maintain while rewriting the ; argument? We could iteratively try them each, rewriting the ; argument l times. This suffers because some rules would be tried ; many times due to our use of refinements. For example, all of the ; equality rules would be tried for each equivk.i tried. ; It is desirable to eliminate the need for more than one pass through ; rewrite. We would like to rewrite once. But if we pass the whole ; set in, with the understanding that any refinement of any of them ; can be used, we are not assured that the result of rewrite is ; equivalent in any of those senses to the input. The reason is that ; rewrite may recursively rewrite its intermediate answer. (If our ; rewriter simplifies a to a' it may then rewrite a' to a''.) Thus, a ; may rewrite to a' maintaining equivk.1 and then a' may rewrite to ; a'' maintaining equivk.2 and it may be that a is not equivalent to ; a'' in either the equivk.1 or equivk.2 sense. However, note that ; there exists an equivalence relation of which equivk.1 and equivk.2 ; are refinements, and that is the relation being maintained. Call ; that the ``generated relation.'' Numerous questions arise. Is the ; generated relation definable in the logic, for if so, perhaps we ; could allow only one equivalence relation per slot per fn and equiv ; and force the user to invent the necessary generalization of the ; several relations he wants to use. Furthermore, if both equivk.1 ; and equivk.2 maintain equiv in the kth slot of fn, does their ; generated relation maintain it? We need to know that the answer is ; ``yes'' if we are going to replace a by a'' (which are equivalent ; only in the generated sense) and still maintain the goal relation. ; We have taken the tack of allowing more than one :CONGRUENCE rule per ; slot by automatically (indeed, implicitly) dealing with the generated ; equivalence relations. To justify our code, we need a variety of ; theorems about generated relations. We state and prove those now. ; Let e1 and e2 be two binary relations. We define the relation s ; ``generated by e1 and e2,'' denoted {e1 e2}, as follows. Because ; order is unimportant below, our set notation {e1 e2} is acceptable. ; (s x y) iff there exists a finite sequence x1, x2, ..., xn such that ; x = x1, y = xn, and for all i, ((e1 xi xi+1) or (e2 xi xi+1)). We ; read this as saying ``(s x y) iff there is a chain connecting x to y ; composed entirely of e1 and/or e2 links.'' ; Congruence Theorem 2. If e1 and e2 are equivalence relations, so is ; {e1 e2}. ; Proof. Let s be {e1 e2}. Then s is reflexive, symmetric, and ; transitive, as shown below. ; Reflexive. To show that (s x x) holds we must exhibit a sequence ; linking x to x via e1 and/or e2. The sequence x,x suffices. ; Symmetric. If (s x y) holds, then there exists a sequence linking x ; to y via e1 and/or e2 steps. Let that sequence be x, x2, ..., xk, ; y. By definition, either e1 or e2 links each pair. Since e1 is ; symmetric, if a pair, xi, xj, is linked by e1 then the pair xj, xi ; is also linked by e1. Similarly for e2. Thus, the sequence ; obtained by reversing that above, y, xk, ..., x2, x, has the desired ; property: each pair is linked by e1 or e2. Therefore, (s y x). ; Transitive. If (s x y) holds, then there exists a sequence linking x ; to y, say x, x2, ..., xk, y. If (s y z) holds, there exists a ; sequence linking y to z, say, y, y1, ..., yk, z. Consider the ; concatenation of those two sequences, x, x2, ..., xk, y, y, y1, ..., ; yk, z. It links x and z and every pair is linked by either e1 or ; e2. Thus, (s x z). ; Q.E.D. ; Thus, the relation generated by two equivalence relations is an ; equivalence relation. ; Congruence Theorem 3. If e1 and e2 are equivalence relations, they ; are both refinements of {e1 e2}. ; Proof. Let s be {e1 e2}. We wish to prove ; (implies (e1 x y) (s x y)) and (implies (e2 x y) (s x y)). ; We consider the first goal only. The second is symmetric. ; But clearly, if x is linked to y by e1 then (s x y) holds, ; as witnessed by the sequence x,y. Q.E.D. ; Congruence Theorem 4. Let equiv, e1 and e2 be equivalence ; relations. Suppose equiv is preserved by e1 in the kth argument of ; fn. Suppose equiv is also preserved by e2 in the kth argument of ; fn. Then equiv is preserved by {e1 e2} in the kth argument of fn. ; Proof. Let s be {e1 e2}. Without loss of generality we restrict our ; attention to a function, fn, of one argument. We have ; (implies (e1 x y) (equiv (fn x) (fn y))) ; and ; (implies (e2 x y) (equiv (fn x) (fn y))) ; We wish to prove ; (implies (s x y) (equiv (fn x) (fn y))) ; The hypothesis (s x y) establishes that there is a chain linking ; x to y via e1 and/or e2. Let that chain be x, x2, ..., xk, y. ; Since each adjacent pair is linked via e1 or e2, and both preserve ; equiv, we get that (equiv (fn x) (fn x2)), (equiv (fn x2) (fn x3)), ; ... (equiv (fn xk) (fn y)). By the transitivity of equiv, therefore, ; (equiv (fn x) (fn y)). Q.E.D. ; Lemma. If e1 is preserved by e in the kth argument of fn then ; so is {e1 e2}, for any relation e2. ; Proof. We have that (e a b) implies (e1 (f ...a...) (f ...b...)). ; Let s be {e1 e2}. We wish to prove that (e a b) implies ; (s (f ...a...) (f ...b...)). But by Congruence Theorem 3 above, ; e1 is a refinement of s. Hence, (e1 (f ...a...) (f ...b...)) ; implies (s (f ...a...) (f ...b...)). Q.E.D. ; Congruence Theorem 5. Let e1, ..., e4 be equivalence relations. ; Then if e2 is preserved by e1 in the kth argument of fn and e4 is ; preserved by e3 in the kth argument of fn, then {e2 e4} is preserved ; by {e1 e3} in the kth argument of fn. ; Proof. By the above lemma, we know {e2 e4} is preserved by e1 in ; the kth argument of fn. Similarly, {e2 e4} is preserved by e3 in ; the kth argument of fn. Thus, the hypotheses of Theorem 4 are ; satisfied and we have that {e2 e4} is preserved by {e1 e3} in the ; kth argument of fn. Q.E.D. ; We generalize the notion of the relation generated by two relations ; to that generated by n relations, {e1, e2, ..., en}. By the above ; results, {e1, ..., en} is an equivalence relation if each ei is, ; each ei is a refinement of it, and it supports any congruence that ; all ei support. We adopt the convention that the relation generated ; by {} is EQUAL and the relation denoted by {e1} is e1. ; In our code, generated equivalence relations are represented by ; lists of congruence-rules. Thus, if cr1 and cr2 are two ; instances of the congruence-rule record having :equivs e1 and e2 ; respectively, then {e1 e2} can be represented by '(cr1 cr2). ; The equivalence relation to be maintained by rewrite is always ; represented as a generated equivalence relation. In our code we ; follow the convention of always using a variant of the name ; ``geneqv'' for such an equivalence relation. When a variable ; contains (or is expected to contain) the name of an equivalence ; relation rather than a :CONGRUENCE rule or geneqv, we use a variant ; of the name ``equiv'' or even ``fn''. ; The geneqv denoting EQUAL is nil. The geneqv denoting IFF is: (defconst *geneqv-iff* (list (make congruence-rule :rune *fake-rune-for-anonymous-enabled-rule* :nume nil :equiv 'iff))) ; This completes our general essay on the subject. The theorems ; proved above are mentioned by name elsewhere in our code. In ; addition, various details are discussed elsewhere. For a simple ; example of how all of this works together, see the function ; subst-equiv-expr which implements substitution of new for old in ; term to produce term', where it is given tha new is equiv1 old and ; term is to be equiv2 term'. ; We now turn to the most primitive functions for manipulating ; equivalences and generated equivalences. We deal with refinements ; first and then with the question of congruences. (defun refinementp (equiv1 equiv2 wrld) ; Note: Keep this function in sync with refinementp below. ; (ACL2 is an applicative subset of Common Lisp. When this ; function, refinementp, is called, its third argument, wrld, will be ; the current "property list world" which is just an association ; list binding symbols and property names to values. The lookup of ; a symbol's property in wrld is via the ACL2 function getprop. ; Getprop is coded in a clever way so that in the case that the ; world is in fact that implicit in the global property list ; structure of Common Lisp, then getprop is just Common Lisp's ; non-applicative get. In our code, wrld is always that world, ; but the code works correctly -- if somewhat more slowly -- if ; called on a different world.) ; Both equiv1 and equiv2 are function symbols. We determine whether ; equiv1 is a known refinement of equiv2, given wrld. If we return t ; we must be correct. Nil means ``maybe not.'' For an explanation of ; why our database contains the 'coarsenings property instead of the ; inverse 'refinements property, see the discussion of ; geneqv-refinements below. (cond ((eq equiv1 'equal) ; Equal is a refinement of all equivalence relations. t) ((eq equiv2 'equal) ; No other relation is a refinement of equal. nil) ((eq equiv1 equiv2) ; Every equivalence relation is a refinement of itself. t) (t ; Otherwise, look for equiv2 among the known coarsenings of equiv1. ; The database must be kept so that the transitive property of ; refinement is manifested explicitly. This function is called very ; often and we do not want to go searching through the transitive ; closure of refinementhood or coarseninghood. So if e1 is a known ; refinement of e2 and e2 is a known refinement of e3, then the ; 'coarsenings property of e1 must include not just e2 but also e3. ; We know the first element in the 'coarsenings of equiv1 is equiv1 ; -- which isn't equiv2 -- so we skip it. (member-eq equiv2 (cdr (getprop equiv1 'coarsenings nil 'current-acl2-world wrld)))))) ; The above function determines if one equivalence symbol is a ; refinement of another. More often we want to know whether a symbol ; is a refinement of a generated equivalence relation. That is, is e1 ; a refinement of {e2 e3}? The most common occurrence of this ; question is when we are maintaining {e2 e3} and want to know if we ; can apply a :REWRITE rule about e1. (defun geneqv-refinementp1 (coarsenings geneqv) ; We determine whether any name in coarsenings is the :equiv of any ; :CONGRUENCE rule in geneqv. If so, we return the :rune of the rule ; found. (cond ((null geneqv) nil) ((member-eq (access congruence-rule (car geneqv) :equiv) coarsenings) (access congruence-rule (car geneqv) :rune)) (t (geneqv-refinementp1 coarsenings (cdr geneqv))))) (defun geneqv-refinementp (equiv geneqv wrld) ; We determine whether the equivalence relation symbol equiv is a ; known refinement of the generated relation geneqv. If so, we return ; the rune of the :CONGRUENCE rule in geneqv used, or ; *fake-rune-for-anonymous-enabled-rule* if equality was used. ; Otherwise we return nil. ; This function is used both as a function and a predicate. Its ; primary use is as a predicate, typically to determine whether it is ; permitted to use a :REWRITE rule whose top-level equivalence is ; equiv. If the function reports success and the rewrite in fact ; succeeds, the caller will typically use the value of the function as ; the rune of the :CONGRUENCE rule used, adding it into the tag-tree of ; the term being rewritten. ; Note: If the database contained only a 'refinements property for e2 ; and e3, we would have to access both of them to determine whether e1 ; was among the known refinements. But if the database contains a ; 'coarsenings property for e1 we can access just that and then look ; for e2 or e3 in it. This saves us doing unnecessary getprops. ; Historical Note: Once we passed around geneqvs that contained ; possibly disabled :CONGRUENCE rules and this function got, as an ; additional argument, the current enabled structure and had the job ; of ignoring those :CONGRUENCE rules. This proved cumbersome and we ; adopted the idea of passing around geneqvs that are fully enabled. ; It means, of course, filtering out the disabled components when we ; form new geneqvs from those in the database. In any case, this ; function does not get the enabled structure and takes no note of the ; status of any rule. (cond ((eq equiv 'equal) *fake-rune-for-anonymous-enabled-rule*) ((null geneqv) nil) (t (geneqv-refinementp1 (getprop equiv 'coarsenings nil 'current-acl2-world wrld) geneqv)))) ; We now define the function which constructs the list of generated ; equivalences to be maintained across the arguments of fn, as a ; function of the generated equivalence to be maintained overall and ; the current enabled structure. Our main concern, technically, here ; is to avoid consing. Most often, we expect that the list of geneqvs ; stored a given fn will be the list we are to return, because we will ; be trying to maintain just one primitive equivalence and we will ; know at most one way to do it for each arg, and none of the ; :CONGRUENCE rules are disabled. So we start with the function that ; filters out of the geneqv stored in slot k all of the disabled ; congruences -- and we code it so as to first check to see whether ; anything needs to be removed. Then we move up to the corresponding ; operation on a stored list of geneqvs. Finally, we consider the ; problem of unioning together the slot k's for all of the primitive ; equivalences to be maintained. (defun some-congruence-rule-disabledp (geneqv ens) (cond ((null geneqv) nil) ((enabled-numep (access congruence-rule (car geneqv) :nume) ens) (some-congruence-rule-disabledp (cdr geneqv) ens)) (t t))) (defun filter-geneqv1 (geneqv ens) (cond ((null geneqv) nil) ((enabled-numep (access congruence-rule (car geneqv) :nume) ens) (cons (car geneqv) (filter-geneqv1 (cdr geneqv) ens))) (t (filter-geneqv1 (cdr geneqv) ens)))) (defun filter-geneqv (geneqv ens) ; Geneqv is a set (list) of :CONGRUENCE rules, generally retrieved from ; slot k of some equiv entry on some function's 'congruences. We ; return the subset consisting of the enabled ones. We avoid consing ; if they are all enabled. (cond ((some-congruence-rule-disabledp geneqv ens) (filter-geneqv1 geneqv ens)) (t geneqv))) ; Now we repeat this exercise one level higher, where we are dealing with ; a list of geneqvs. (defun some-geneqv-disabledp (lst ens) (cond ((null lst) nil) ((some-congruence-rule-disabledp (car lst) ens) t) (t (some-geneqv-disabledp (cdr lst) ens)))) (defun filter-geneqv-lst1 (lst ens) (cond ((null lst) nil) (t (cons (filter-geneqv (car lst) ens) (filter-geneqv-lst1 (cdr lst) ens))))) (defun filter-geneqv-lst (lst ens) ; It is handy to allow ens to be nil, indicating that nothing is disabled. (cond ((null ens) lst) ((some-geneqv-disabledp lst ens) (filter-geneqv-lst1 lst ens)) (t lst))) ; Next we must union together two lists of :CONGRUENCE rules. To keep ; the lists from getting large we will eliminate refinements. That ; is, if we have {e1 e2} U {e3 e4}, and e1 is a refinement of e3, but ; there is no refinement relation between e2, e3 and e4, then the ; answer will be {e2 e3 e4}. In general, we will assume the two lists ; are free of internal refinement relations and we will generate such ; a list. It is a little messy because e3 may be a refinement of e2, ; say. In which case the answer is {e2 e4}. (defun refinementp1 (equiv1 coarsenings1 equiv2) ; Both equiv1 and equiv2 are function symbols and coarsenings1 is the ; cdr of the 'coarsenings property of equiv1 (the car of that property ; is equiv1 itself). We determine whether equiv1 is a known ; refinement of equiv2. This function should be kept in sync with the ; more general refinementp. (cond ((eq equiv1 'equal) t) ((eq equiv2 'equal) nil) ((eq equiv1 equiv2) t) (t (member-eq equiv2 coarsenings1)))) (defun pair-congruence-rules-with-coarsenings (geneqv wrld) ; We pair each congruence rule in geneqv with non-id coarsenings, ; i.e., the cdr of the 'coarsenings property of its :equiv. (cond ((null geneqv) nil) (t (cons (cons (car geneqv) (cdr (getprop (access congruence-rule (car geneqv) :equiv) 'coarsenings nil 'current-acl2-world wrld))) (pair-congruence-rules-with-coarsenings (cdr geneqv) wrld))))) (defun add-to-cr-and-coarsenings (new-cr new-cr-coarsenings old-crs-and-coarsenings both-tests-flg) ; New-cr is a congruence rule and new-cr-coarsenings is the ; 'coarsenings property of its :equiv. Note that the car of ; new-cr-coarsenings is thus the :equiv name. Old-crs-and-coarsenings ; is a list of pairs of the form (congruence-rule . non-id-coarsenings). ; We assume no member of the old list refines any other member. ; We ``add'' the new pair (new-cr . non-id-new-cr-coarsenings) to the old ; list. However, if new-cr is a refinement of any equiv in the old ; list, we do nothing. Furthermore, if any member of the old list is ; a refinement of new-cr, we delete that member. (cond ((null old-crs-and-coarsenings) ; Add the new-cr and its non-id coarsenings to the list. (list (cons new-cr (cdr new-cr-coarsenings)))) ((and both-tests-flg (refinementp1 (car new-cr-coarsenings) ; new-equiv (cdr new-cr-coarsenings) ; new-equiv's non-id coarsenings (cadr (car (car old-crs-and-coarsenings))))) ; first old-equiv ; The new equiv is a refinement of the first old one. Nothing to do. old-crs-and-coarsenings) ((refinementp1 (cadr (car (car old-crs-and-coarsenings))) ; first old-equiv (cdr (car old-crs-and-coarsenings)) ; first old-equiv's ; non-id coarsenings (car new-cr-coarsenings)) ; new-equiv ; The first old equiv is a refinement of the new one. Delete the old ; one. Continue inserting the new one -- it may cause other ; refinements to be deleted. But there is no possibility that it will ; be dropped because any old cr which it refines would have been ; refined by the one we just dropped. So we can henceforth only test for ; this case. (add-to-cr-and-coarsenings new-cr new-cr-coarsenings (cdr old-crs-and-coarsenings) nil)) (t (cons (car old-crs-and-coarsenings) (add-to-cr-and-coarsenings new-cr new-cr-coarsenings (cdr old-crs-and-coarsenings) both-tests-flg))))) (defun union-geneqv1 (geneqv1 old-crs-and-coarsenings wrld) ; Geneqv1 is a geneqv and old-crs-and-coarsenings is a list of pairs ; of the form (congurence-rule . coarsenings), where the coarsenings ; are the non-id coarsenings of the :equiv of the corresponding ; congruence-rule. This data structure makes it possible to answer ; refinement questions without going back to the world. We scan down ; geneqv1 and augment old-crs-and-coarsenings, adding a new ; (congruence-rule . non-id-coarsenings) pair for each congruence rule in ; geneqv1 that is not a refinement of any rule already in the old set. ; In addition, if we find an old rule that is a refinement of some new ; one, we drop it from the old set, replacing it with the new one. (cond ((null geneqv1) old-crs-and-coarsenings) (t (union-geneqv1 (cdr geneqv1) (add-to-cr-and-coarsenings (car geneqv1) (getprop (access congruence-rule (car geneqv1) :equiv) 'coarsenings nil 'current-acl2-world wrld) old-crs-and-coarsenings t) wrld)))) (defun union-geneqv (geneqv1 geneqv2 wrld) ; We union together the congruence rules in the two geneqv's, forming ; a set with the property that no element in it is a refinement of any ; other. Roughly speaking we simply add the equivs of geneqv1 into ; those of geneqv2, not adding any that is a refinement and deleting ; any that is refined by a new one. To make this process faster we ; first annotate genquv2 by pairing each congruence rule in it with ; the non-id 'coarsenings property of its :equiv. Union-geneqv1 does the ; work and returns such an annotated list of congruence rules. We ; convert that back into a geneqv by stripping out the annotations. (strip-cars (union-geneqv1 geneqv1 (pair-congruence-rules-with-coarsenings geneqv2 wrld) wrld))) ; And now we do slotwise union. (defun pairwise-union-geneqv (lst1 lst2 wrld) ; Lst1 and lst2 are lists of geneqvs that are in 1:1 correspondence. ; We pairwise union their elements. (cond ((null lst1) nil) (t (cons (union-geneqv (car lst1) (car lst2) wrld) (pairwise-union-geneqv (cdr lst1) (cdr lst2) wrld))))) ; That brings us to the main function we've been wanting: the one that ; determines what generated equivalence relations must be maintained ; across the the arguments of fn in order to maintain a given ; generated equivlence relation for the fn-expression itself. Because ; we form new geneqvs from stored ones in the database, we have to ; have the enabled structure so we filter out disabled congruence ; rules. (defun geneqv-lst1 (congruences geneqv ens wrld) ; Congruences is the list of congruences of a certain function, fn. ; Geneqv is a list of congruence-rules whose :equiv relations we are ; trying to maintain as we sweep across the args of fn. For each ; element of congruences, (equiv slot1 ... slotn), such that equiv is ; an element of geneqv we filter disabled rules out of each slot and ; then union together corresponding slots. ; In coding this, the following question arose. ``Should we include ; those equiv that are refinements of elements of geneqv or just those ; that are literally elements of geneqv?'' Our answer is ``include ; refinements.'' Suppose geneqv is {set-equal}. Suppose list-equal ; is a known refinement of set-equal. Suppose that for the fn in ; question we know a :CONGRUENCE rule that preserves list-equal but we ; know no rules that preserve set-equal. Then if we do not include ; refinements we will be fooled into thinking that the only way to ; preserve set-equal for the fn is to preserve equal across the args. ; But if we do include refinements we will know that we can admit ; whatever relations are known to maintain list-equal across the args. (cond ((null congruences) ; This is a little subtle. We return nil where we ought to return a ; list of n nils. But it is ok. An optimization below (in which we ; avoid pairwise-union-geneqv when the second arg is nil) makes it ; clearly ok. But even without the optimization it is ok because ; pairwise-union-geneqv is controlled by its first arg! nil) (t (let ((ans (geneqv-lst1 (cdr congruences) geneqv ens wrld))) (cond ((geneqv-refinementp (caar congruences) geneqv wrld) (cond ((null ans) (filter-geneqv-lst (cdar congruences) ens)) (t (pairwise-union-geneqv (filter-geneqv-lst (cdar congruences) ens) ans wrld)))) (t ans)))))) ; On the Optimization of Geneqv-lst ; Once upon a time we suspected that geneqv-lst might be causing a significant ; slowdown of ACL2 compared to Nqthm. So we tried the following experiment. ; First we ran the code on the Nqthm package and learned that geneqv-lst is ; called a total of 876843 times. The entire series of proofs took 1654 ; seconds (on Rana, a Sparc 2). Then we recoded the function so that it saved ; every input and output and reran it on the proof of the Nqthm package to ; collect all io pairs. Analyzing the io pairs showed that we could reproduce ; the behavior of geneqv-lst on that series of proofs with the following code. ; Note that this does does not look at the property lists nor at the enabled ; structure. Nor does it do any consing. ; (defun geneqv-lst (fn geneqv ens wrld) ; (declare (ignore ens wrld)) ; ; (setq genquv-cnt (1+ genquv-cnt)) ; (cond ; ((and (eq fn 'IFF) ; (equal geneqv *geneqv-iff*)) ; '(((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)) ; ((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)))) ; ((and (eq fn 'IMPLIES) ; (equal geneqv *geneqv-iff*)) ; '(((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)) ; ((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)))) ; ((eq fn 'IF) ; (cond ; ((null geneqv) ; '(((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)) ; nil nil)) ; ((equal geneqv *geneqv-iff*) ; '(((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)) ; ((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)) ; ((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)))) ; (t nil))) ; ((and (eq fn 'NOT) ; (equal geneqv *geneqv-iff*)) ; '(((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)))) ; (t nil))) ; (Note: ((NIL IFF :FAKE-RUNE-FOR-ANONYMOUS-ENABLED-RULE NIL)) is just ; *geneqv-iff*.) ; Then we recompiled the entire ACL2 system with this definition in place (to ; ensure that the calls were all fast) and reran the Nqthm package proofs. The ; result was that it took 1668 seconds! ; Not wanting to believe these results (it seems so obvious that this function ; is inefficient!) we tried redefining geneqv-lst so that always returned nil. ; This is not the same behavior as the geneqv-lst below, but at least it is ; fast. The resulting proofs took 1780 seconds but investigation showed that ; some proofs followed different paths, so this experiment was discounted. ; Next, we simply remembered the complete sequence of answers generated by the ; code below (all 876843 of them) and then redefined the function to feed back ; those very answers in the same sequence. The answers were pushed into a ; stack during one run, the stack was reversed, and the answers were popped off ; during the second run. The code for geneqv-lst was simply (pop ; geneqv-stack). We cannot imagine a faster implementation. The second ; run took 1685 seconds. ; The conclusion of these experiments is that geneqv-lst is not likely to be ; optimized! (defun geneqv-lst (fn geneqv ens wrld) ; Suppose we are attempting to rewrite a term whose function is fn while ; maintaining the generated equivalence relation geneqv. Fn may be a lambda ; expression. We return the list of generated equivalence relations to be ; maintained at each argument position. See the essay above for some ; experiments on the optimization of this function. ; For example, while rewriting a MEMBER expression, (MEMBER x s) to ; maintain IFF we should rewrite x maintaining EQUAL and rewrite s ; maintaining SET-EQUAL. That is, given MEMBER and IFF (for fn and ; geneqv) we wish to return (EQUAL SET-EQUAL), a list in 1:1 ; correspondence with the formals of fn giving the equivalence ; relations that must be maintained for the arguments in order to ; maintain geneqv. However, rather than (EQUAL SET-EQUAL) we return a ; list of two geneqvs, namely '(nil (cr)), where cr is the congruence ; rule which establishes that IFF is maintained by SET-EQUAL in the ; 2nd arg of MEMBER. ; The fact that nil denotes the equivalence generated by 'EQUAL, ; combined with the facts that the car and cdr of nil are nil, allows ; us to return nil to denote a list of a suitable number of generated ; equalities. Thus, the answer nil is always correct and is in fact ; the answer returned for all those functions for which we know no ; :CONGRUENCE rules. ; If fn is a lambda-expression, we return nil. Otherwise, the ; 'congruences property of the symbol fn is an alist. The entries of ; the alist are of the form (equiv geneqv1 ... geneqvn). Consider the ; entry for each refinement of some :equiv in the goal geneqv, after ; filtering out the disabled rules from each: ; (equiv1 geneqv1,1 ... geneqv1,n) ; (equiv2 geneqv2,1 ... geneqv2,n) ; ... ; (equivk geneqvk,1 ... geneqvk,n) ; The union down the first column is geneqv. Let the union down ; subsequent columns be geneqv1, ... geneqvn. Then by Congruence ; Theorem 5, we have that geneqv is maintained by geneqvi in the ith ; argument of fn. Thus, we return (geneqv1 ... geneqvn). ; Observe that if some equivj in geneqv is not mentioned in the ; known congruences then we have, implicitly, the entry ; (equivj {} ... {}) and so its contribution to the union is ; justifiably ignored. ; Observe that if we throw away a disabled rule from a geneqvi,j we ; are just strengthening the equivalence relation to be maintained ; in that slot. Thus, our heuristic use of ens is sound. ; We allow ens to be nil, to signify that all rules are to be considered as ; enabled. (cond ((flambdap fn) nil) ((eq fn 'if) ; IF is an unusual function symbol vis-a-vis congruence. We know that ; equality is preserved by iff in the 1st argument of IF. But more ; significantly, for every equivalence relation, equiv, we have that ; equiv is preserved by equiv in the 2nd and 3rd arguments of if. ; Thus, we could store a lot of congruences under IF, one for each ; equivalence relation: (equiv iff equiv equiv). Instead, we just ; manufacture it when we are asked. This is inefficient in that we ; may cons up the same structure repeatedly. But we do not suffer ; as much as one might think because the really heavy-duty users of ; geneqv-lst, e.g., rewrite, build in their handling of IF anyway and ; never call geneqv-lst on 'IF. (list *geneqv-iff* geneqv geneqv)) (t (let ((congruences (getprop fn 'congruences nil 'current-acl2-world wrld))) (cond ((null congruences) nil) ((null geneqv) ; This is a special case. If we are trying to maintain equality ; then the likelihood is that we have to maintain equality across ; the args, i.e., return nil. But it is possible that the congruences ; for fn lists 'equal explicitly. If so, we use those. Otherwise nil. ; But we have to filter for disabled rules. (filter-geneqv-lst (cdr (assoc-eq 'equal congruences)) ens)) (t ; This is the general case in which the function has some known congruence ; relations and the equivalence relation we are trying to maintain is not just ; equality. In this case, we are prepared to to do some consing. (geneqv-lst1 congruences geneqv ens wrld))))))) ; As an exercise in the use of the equivalence and congruence stuff, we ; now code the function that substitutes one term for another maintaining ; a given generated equivalence. We begin with elementary substitution ; because it illustrates the fundamental notion of substitution. ; Elementary Expression Substitution (``Equals for Equals'') ; Students of our code might find it helpful to look at subst-var ; before looking at the following. ; We show how to substitute one term, new, for another term, old, ; in a term. The presumption is that new and old are known to be ; equal. This might be used, for example, to substitute ; A for (CAR (CONS A B)) in (FOO (CAR (CONS A B))) to produce ; (FOO A). (mutual-recursion (defun subst-expr1 (new old term) (declare (xargs :guard (and (pseudo-termp new) (pseudo-termp old) (pseudo-termp term)))) (cond ((equal term old) new) ((variablep term) term) ((fquotep term) term) (t (cons-term (ffn-symb term) (subst-expr1-lst new old (fargs term)))))) (defun subst-expr1-lst (new old args) (declare (xargs :guard (and (pseudo-termp new) (pseudo-termp old) (pseudo-term-listp args)))) (cond ((endp args) nil) (t (cons (subst-expr1 new old (car args)) (subst-expr1-lst new old (cdr args)))))) ) (defun subst-expr-error (const) (declare (xargs :guard nil)) (er hard 'subst-expr-error "An attempt was made to substitute for the explicit value ~x0. ~ The substitution functions were optimized to disallow this." const)) (defun subst-expr (new old term) (declare (xargs :guard (and (pseudo-termp new) (pseudo-termp old) (not (quotep old)) (pseudo-termp term)))) (cond ((variablep old) (subst-var new old term)) ((fquotep old) (subst-expr-error old)) (t (subst-expr1 new old term)))) ; Congruence-Based Substitution: ; Below we develop the function that substitutes new for old into ; term, where new is equiv to old and we are supposed to produce an ; answer that is geneqv to term. The main reason we're developing ; this function is to solidify our ideas on congruence rewriting. ; Note: The relation between new and old is some primitive ; equivalence, i.e., equiv is a function symbol. But the relation we ; are trying to maintain is a generated equivalencd, i.e., a set of ; primitive equivs. We could pursue the idea of generalizing equiv to ; a generated equivalence. However, we don't, at the moment, see the ; value in that. In the first place, this function is meant as a ; model of how rewrite should handle geneqvs and each :REWRITE rule is ; about a single primitive equivalence, not a generated equivalence. ; In the second place, everywhere this function is used, e.g., when we ; eliminate a (set-equal a b) hyp in the conjecture by substituting a ; for b, we have a primitive equiv relating the two. Now we will need ; the generalized version of this function if we ever obtain b, say, ; by rewriting a under some generated equivalence. The resulting a ; and b are not related by a primitive equiv. But we will wait until ; we need that to implement it. ; Here is an example of the kind of substitution we implement. Let ; list-equal be the equivalence relation that is element by element ; equality on lists (ignoring the final cdr). Let set-equal be ; permutationp. Suppose that if a is set-equal to b then (listify a) ; is list-equal to (listify b). A model of listify is that it removes ; duplicates and sorts with some total ordering, but preserves the ; final cdr just to prevent (listify a) from being equal to (listify ; b). Suppose further that if x is list-equal to y then (member e x) ; iff (member e y). ; Given the foregoing, we have three equivalence relations, ; list-equal, set-equal, and iff, and two congruences. ; Under the 'congruences property of listify we have the congruence ; (list-equal ((nume set-equal . rune))) which means that list-equal ; is preserved by set-equal in the first argument of listify. ; Under the 'congruences property of member we have (iff nil ((nume ; list-equal . rune))) which means that iff is preserved by list-equal ; in the second argument of member. The nil implicitly says ``iff is ; preserved by equal in the first argument of member.'' ; Now suppose we want to substitute a for b (which are known to be ; set-equal) into (member e (listify b)) maintaining iff. Then we see ; that iff can be maintained on the member expression if we substitute ; a for b in (listify b) maintaining list-equal. Then we see that ; list-equal can be maintained on the listify expression if we ; substitute a for b in b maintaining set-equal. But a is set-equal ; to b. So we get (member e (listify a)). ; Now let us refine this slightly. What does it mean for one ; equivalence relation, e1, to be a refinement of another, e2? It ; means that (implies (e1 a b) (e2 a b)). That is, if a and b are ; in a refinement of e2 they are in e2. So for example, EQUAL is a ; refinement of every equivalence relation because (implies (equal a ; b) (e2 a b)) is the same as (e2 a a), which is just reflexivity. ; So suppose a is equiv1 to b and we want to substitute a for b in b ; maintaining equiv2. What is a sufficient condition on equiv1 and ; equiv2? Equiv1 must be a refinement of equiv2. That is, they must ; be ``even more alike'' than equiv2 requires, in the sense of being ; in a smaller equivalence class. ; In our actual implementation equiv2 is generalized to a generated ; equivalence relation. (defun scons-term (fn args ens wrld state ttree) ; This function is (cons-term fn args) except that we evaluate any enabled ; fn on quoted arguments and may do any other replacements that preserve ; equality (e.g., (equal x x) = t). In addition, we report the executable ; counterparts we use by adding them to ttree. We return (mv hitp term ; ttree'), hitp is t iff term is something different than (fn . args), term is ; equal to (fn . args) and ttree' is an extension of ttree. (cond ((and (all-quoteps args) (or (flambdap fn) (and (enabled-xfnp fn ens wrld) ; We don't mind disallowing constrained functions that have attachments, ; because the call of ev-fncall below disallows the use of attachments (last ; parameter, aok, is nil). (not (getprop fn 'constrainedp nil 'current-acl2-world wrld))))) ; Note: This code is supposed to be the same as in rewrite. Keep them in sync ; and see the comment there for explanations. (cond ((flambdap fn) ; This is a problematic case. At first sight, we could just create the term ; (fn . args) and then evaluate it with ev. (We can't use ev-fncall as we do ; below because it doesn't handle lambdas.) But this ignores some problems. ; How do we avoid evaluating :program fns that occur in the body? How do ; we avoid evaluating disabled fns in the body? How do we report the ; executable counterparts we use? Problems, problems. We punt. (mv nil (cons-term fn args) ttree)) ((eq fn 'if) (mv t (if (cadr (car args)) (cadr args) (caddr args)) ttree)) ((programp fn wrld) ; this test is needed; see the comment in rewrite (mv t (cons-term fn args) ttree)) (t (mv-let (erp val latches) (pstk (ev-fncall fn (strip-cadrs args) state nil t nil)) (declare (ignore latches)) (cond (erp ; There is a guard violation, probably -- or perhaps there's some other kind of ; error. We'll just hide this term so we don't see it again. (mv t (fcons-term* 'hide (cons-term fn args)) ttree)) (t (mv t (kwote val) (push-lemma (fn-rune-nume fn nil t wrld) ttree)))))))) ((and (eq fn 'equal) (equal (car args) (cadr args))) (mv t *t* ttree)) (t (mv nil (cons-term fn args) ttree)))) (mutual-recursion (defun subst-equiv-expr1 (equiv new old geneqv term ens wrld state ttree) ; This function substitutes new for old (which are known to be in the ; equivalence relation equiv) into term (maintaining the generated ; equivalence relation geneqv). We assume that geneqv contains only ; enabled :CONGRUENCE rules. We use only enabled :CONGRUENCE rules. ; We return three values: a flag indicating whether we changed term, ; the new term, and a ttree recording the :CONGRUENCE rules used. ; When we create new terms we run enabled fns on constant args. The ; executable counterparts used are reported in the ttree. ; (The (mv a b c) expressions below mean we are returning "multiple ; values", in this case, triples consisting of a, b, and c. ; Logically speaking (mv a b c) is just (list a b c), but ACL2's ; syntactic rules ensure that the list structure is never seen, i.e., ; the three values are immediately plucked out of the structure. ; Analogously, in (mv-let (a b c) term1 term2) term1 evaluates to a ; triple, the three variables a, b, and c are bound to the three items ; of that triple, and then term2 is evaluated under those bindings. ; ACL2 uses mv and mv-let in place of Common Lisp's multiple value ; mechanism because the Common Lisp mechanism is too flexible. It ; allows a function to return varying numbers of things. Ours is also ; faster.) ; NOTE: We ignore occurrences of old inside arguments to HIDE. (cond ((and (equal term old) (geneqv-refinementp equiv geneqv wrld)) (mv t new (push-lemma (geneqv-refinementp equiv geneqv wrld) ttree))) ((or (variablep term) (fquotep term) (eq (ffn-symb term) 'hide)) (mv nil term ttree)) (t (mv-let (hitp1 args ttree) (subst-equiv-expr1-lst equiv new old (geneqv-lst (ffn-symb term) geneqv ens wrld) (fargs term) ens wrld state ttree) ; Note: Observe that we are relying on the IF hack in geneqv-lst here, ; asking that function to generate (iff geneqv geneqv) to control our ; calls. If we thought this function would see a lot of action on ; IF's it would be better to special-case the substitution into IF ; expressions. (mv-let (hitp2 new-term ttree) (scons-term (ffn-symb term) args ens wrld state ttree) (mv (or hitp1 hitp2) new-term ttree)))))) (defun subst-equiv-expr1-lst (equiv new old geneqv-lst args ens wrld state ttree) ; Here geneqv-lst is in 1:1 correspondence with args. We substitute ; into each arg. (cond ((null args) (mv nil nil ttree)) (t (mv-let (hitp1 arg ttree) (subst-equiv-expr1 equiv new old (car geneqv-lst) (car args) ens wrld state ttree) (mv-let (hitp2 args ttree) (subst-equiv-expr1-lst equiv new old (cdr geneqv-lst) (cdr args) ens wrld state ttree) (mv (or hitp1 hitp2) (cons arg args) ttree)))))) ) (defun subst-equiv-expr (equiv new old geneqv term ens wrld state ttree) (cond ((and (nvariablep old) (fquotep old)) (mv (subst-expr-error old) term ttree)) (t (subst-equiv-expr1 equiv new old geneqv term ens wrld state ttree)))) ; This completes the definition of congruence-based substitution. ; Next we develop clausify, the function that reduces a term to a set ; of clauses. (mutual-recursion (defun ffnnamesp (fns term) ; We determine whether some function fn (possibly a lambda-expression) ; in fns is used as a function in term. So this function is: ; (exists fn in fns s.t. (ffnamep fn term)). (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (member-equal (ffn-symb term) fns) (ffnnamesp fns (lambda-body (ffn-symb term))) (ffnnamesp-lst fns (fargs term)))) ((member-eq (ffn-symb term) fns) t) (t (ffnnamesp-lst fns (fargs term))))) (defun ffnnamesp-lst (fns l) (if (null l) nil (or (ffnnamesp fns (car l)) (ffnnamesp-lst fns (cdr l))))) ) (mutual-recursion (defun collect-ffnnames (fns term ans) ; We collect onto ans those members of fns used as functions in term. ; If ffnnamesp returns non-nil, then this function returns the non-nil ; subset of fns responsible. (cond ((variablep term) ans) ((fquotep term) ans) ((flambda-applicationp term) (collect-ffnnames fns (lambda-body (ffn-symb term)) (collect-ffnnames-lst fns (fargs term) (if (member-equal (ffn-symb term) fns) (add-to-set-equal (ffn-symb term) ans) ans)))) (t (collect-ffnnames-lst fns (fargs term) (if (member-eq (ffn-symb term) fns) (add-to-set-eq (ffn-symb term) ans) ans))))) (defun collect-ffnnames-lst (fns l ans) (cond ((null l) ans) (t (collect-ffnnames-lst fns (cdr l) (collect-ffnnames fns (car l) ans))))) ) (defun comm-equal (fn lhs rhs term) ; This function is equivalent to ; (or (equal `(,fn ,lhs ,rhs) term) ; (equal `(,fn ,rhs ,lhs) term)) (and (nvariablep term) (not (fquotep term)) (eq fn (ffn-symb term)) (if (equal rhs (fargn term 2)) (equal lhs (fargn term 1)) (and (equal rhs (fargn term 1)) (equal lhs (fargn term 2)))))) (defun member-term2 (fn lhs rhs cl) ; We determine whether either `(,fn ,lhs ,rhs) or `(,fn ,rhs ,lhs) is ; a member of cl. ; Note on Nomenclature: This is a subroutine of member-term. It ought ; to be named member-term1, but in symmetry with ; member-complement-term, we named it member-term2. Member-equal ; plays the role of member-term1. (cond ((null cl) nil) ((comm-equal fn lhs rhs (car cl)) cl) (t (member-term2 fn lhs rhs (cdr cl))))) (defun member-complement-term2 (fn lhs rhs cl) (cond ((null cl) nil) ((and (nvariablep (car cl)) (not (fquotep (car cl))) (eq (ffn-symb (car cl)) 'not) (comm-equal fn lhs rhs (fargn (car cl) 1))) cl) (t (member-complement-term2 fn lhs rhs (cdr cl))))) (defun member-complement-term1 (lit cl) ; Lit is known not to begin with not and not to be an equality or iff. ; This fn is equivalent to (member-equal `(not ,lit) cl). (cond ((null cl) nil) ((and (nvariablep (car cl)) (not (fquotep (car cl))) (eq (ffn-symb (car cl)) 'not) (equal lit (fargn (car cl) 1))) cl) (t (member-complement-term1 lit (cdr cl))))) (mutual-recursion (defun member-term (lit cl) ; We determine whether lit is a member-equal of cl, except that if the ; atom of lit is an equality or iff term, we also look for its ; commuted version. (cond ((variablep lit) (member-eq lit cl)) ((fquotep lit) (member-equal lit cl)) ((or (eq (ffn-symb lit) 'equal) (eq (ffn-symb lit) 'iff)) (member-term2 (ffn-symb lit) (fargn lit 1) (fargn lit 2) cl)) ((eq (ffn-symb lit) 'not) (member-complement-term (fargn lit 1) cl)) (t (member-equal lit cl)))) (defun member-complement-term (lit cl) ; We determine whether the complement of lit is a member-equal of cl, ; except that if the atom of lit is an equality or iff we recognize ; its commuted version. (cond ((variablep lit) (member-complement-term1 lit cl)) ((fquotep lit) (member-complement-term1 lit cl)) ((or (eq (ffn-symb lit) 'equal) (eq (ffn-symb lit) 'iff)) (member-complement-term2 (ffn-symb lit) (fargn lit 1) (fargn lit 2) cl)) ((eq (ffn-symb lit) 'not) (member-term (fargn lit 1) cl)) (t (member-complement-term1 lit cl)))) ) (defun instr-listp (l) (cond ((atom l) (equal l nil)) (t (and (or (integerp (car l)) (let ((carl (car l))) (case-match carl (('push . x) (pseudo-termp x)) (('push-local . n) (integerp n)) (('push-frame-ptr) t) (('go . x) (integerp x)) (('test . x) (integerp x)) (('call . term) (pseudo-termp term)) (('ret . lst) (pseudo-term-listp lst))))) (instr-listp (cdr l)))))) (defun spliced-instr-listp (l) (cond ((atom l) (equal l nil)) (t (and (let ((carl (car l))) (case-match carl (('push . x) (pseudo-termp x)) (('push-local . n) (integerp n)) (('push-frame-ptr) t) (('test . x) (spliced-instr-listp x)) (('call . term) (pseudo-termp term)) (('ret . lst) (pseudo-term-listp lst)))) (spliced-instr-listp (cdr l)))))) (defun next-tag (l) (declare (xargs :guard (instr-listp l))) (cond ((null l) 1) ((and (consp (car l)) (eq (caar l) 'test)) (+ 2 (cdr (car l)))) (t (next-tag (cdr l))))) (defun if-compile-formal (var rformals i) (declare (xargs :guard (and (symbolp var) (true-listp rformals) (member-eq var rformals)))) (cond ((eq var (car rformals)) i) (t (if-compile-formal var (cdr rformals) (1+ i))))) ; Rockwell Addition: Repeatedly in this new code we will be concerned ; with the question of whether we look inside of lambdas or not. Many ; functions have an additional lambda-exp arg, telling them whether to ; go inside lambda applications. These extra args will show up in a ; window comparison but aren't commented upon henceforth. (mutual-recursion (defun ffnnamep-hide (fn term lambda-exp) ; We determine whether the function fn (possibly a lambda-expression) ; is used as a function in term, without diving inside calls of HIDE. ; If lambda-exp is t we look inside of lambda bodies. Otherwise we ; don't. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (equal fn (ffn-symb term)) (and lambda-exp (ffnnamep-hide fn (lambda-body (ffn-symb term)) lambda-exp)) (ffnnamep-hide-lst fn (fargs term) lambda-exp))) ((eq (ffn-symb term) fn) t) ((eq (ffn-symb term) 'hide) nil) (t (ffnnamep-hide-lst fn (fargs term) lambda-exp)))) (defun ffnnamep-hide-lst (fn l lambda-exp) (declare (xargs :guard (and (symbolp fn) (pseudo-term-listp l)))) (if (null l) nil (or (ffnnamep-hide fn (car l) lambda-exp) (ffnnamep-hide-lst fn (cdr l) lambda-exp)))) ) (mutual-recursion (defun if-compile (term lambda-exp ac rformals) ; We compile term. If lambda-exp is t, we expand lambda applications. ; Otherwise, we don't. Rformals is the list of formal parameters that ; occur in term. It is nil outside of lambdas. It MIGHT be nil ; inside of a lambda: ((lambda nil ...)). ; Here is the target language of our compiler: ; (push . term) push term onto the stack. ; (push-local . n) push the nth local onto the stack, where we ; enumerate the locals 0-based, starting from ; the right-most! That is, in the compiled ; code for body in ; ((lambda (x y z) body) a b c) ; z is the 0th local, y is the 1st, and x is the ; 2nd. ; (push-frame-ptr) the current stack represents a complete frame; ; think of this as marking this point on the stack ; so that (push-local . n) fetches from here, offset ; back by n. ; (go . n) transfer control to the instruction labelled n ; (test . n) pop and test the top of the stack and if nil, ; transfer control to the instruction labelled n, ; else execute the next instruction. ; (call fn . lst) Lst is a list that is completely irrelevant ; except for its length, n. Pop n things off ; the stack, apply fn to them (top-most item ; on the stack being the last arg to fn), and ; push the result. ; (ret . lst) Lst is a list that is irrelevant except for ; its length, n. Pop one value off the stack and ; hold it as the returned value of the lambda ; expression just evaluated, then pop n things ; off the stack, clearing the most recent frame, ; and finally push the returned value. (declare (xargs :guard (pseudo-termp term))) (cond ((variablep term) ; Note: What if rformals is nil? Then we couldn't have hit a variable ; and we aren't in a lambda. (cond (rformals (cons (cons 'push-local (if-compile-formal term rformals 0)) ac)) (t (cons (cons 'push term) ac)))) ((or (fquotep term) (eq (ffn-symb term) 'hide)) (cons (cons 'push term) ac)) ((flambdap (ffn-symb term)) ; This is a lambda application. If we are supposed to expand lambdas ; and there is an IF inside the body of this one, we compile the body. ; Otherwise we treat it the same way we do ordinary function symbol ; applications. (cond ((and lambda-exp (ffnnamep-hide 'if (lambda-body (ffn-symb term)) lambda-exp)) (cons (cons 'ret (lambda-formals (ffn-symb term))) (if-compile (lambda-body (ffn-symb term)) lambda-exp (cons '(push-frame-ptr) (if-compile-lst (fargs term) lambda-exp ac rformals)) (revappend (lambda-formals (ffn-symb term)) nil)))) ((or (ffnnamep-hide-lst 'if (fargs term) lambda-exp) rformals) (cons (cons 'call term) (if-compile-lst (fargs term) lambda-exp ac rformals))) (t (cons (cons 'push term) ac)))) ((eq (ffn-symb term) 'if) (let* ((test-seg (if-compile (fargn term 1) lambda-exp ac rformals)) (n (next-tag test-seg))) (cons (+ n 1) (if-compile (fargn term 3) lambda-exp (cons n (cons (cons 'go (+ n 1)) (if-compile (fargn term 2) lambda-exp (cons (cons 'test n) test-seg) rformals))) rformals)))) ((or (ffnnamep-hide-lst 'if (fargs term) lambda-exp) rformals) ; If there is an IF in some arg, we compile the args to get rid of the ; IFs. Alternatively, if we are compiling a lambda body (with ; rformals), we must compile the args to deref them via the stack. (cons (cons 'call term) (if-compile-lst (fargs term) lambda-exp ac rformals))) (t (cons (cons 'push term) ac)))) (defun if-compile-lst (l lambda-exp ac rformals) (declare (xargs :guard (pseudo-term-listp l))) (cond ((null l) ac) (t (if-compile-lst (cdr l) lambda-exp (if-compile (car l) lambda-exp ac rformals) rformals)))) ) ; The following code is a little weird. We implement a data structure called ; "assumptions" for representing assumed terms. In particular, we can add to ; the data structure to assume a term true and then we can quickly convert that ; structure to one in which the term is assumed false. The pair of these ; assumptions always costs exactly two conses, total: either the first costs 1 ; cons and the next does also, or the first costs 2 and the next is free. Our ; representation of assumptions relies upon the fact that the keywords :NOT and ; :IGNORE-WHEN-CONVERTING-TO-CLAUSE are not legal variable symbols. Our ; machinery for manipulating assumptions also exploits the fact that we never ; assume a quoted term -- we simply decide the issue. Thus, (nvariablep x) ; means (ffn-symb x) is a function symbol or lambda expression. ; To assume an atm true, we add it to the list (one cons). To assume an atom ; false, we add it to the list and then add :NOT in front of it (two conses). ; To negate the last assumption you either add a :NOT (one cons) or delete a ; :NOT (no conses). The element :IGNORE-WHEN-CONVERTING-TO-CLAUSE plays no ; special role in determining the value of an atom -- it looks like a variable ; symbol assumed true. We never "negate" it though! That is, the process of ; "negating the last assumption" must be done in a disciplined way in which you ; negate an assumption that you were responsible for adding in the first place. ; Below we first write the function for recovering from this structure the ; assumed value of a term, getting the answer t (for assumed true), 'f (for ; assumed false) or nil (for unassumed). Like member-term and ; member-complement-term this recovering process knows about the commutativity ; of equal and iff. But this is faster than those two, both because ; assumptions cost fewer conses and because we get the answer to the question ; "is it assumed and if so which way?" in the same time we can use member-term ; or member-complement-term to get only half the answer. ; Then we write the function for converting an assumptions structure into a ; clause. All assumptions after the :IGNORE-WHEN-CONVERTING-TO-CLAUSE marker ; are ignored during the process. Thus, it is possible to load into an initial ; assumption a bunch of literals that will be known true or false appropriately ; during the clausification process but which will not be transferred into the ; answer clauses produced. ; Finally we write the function that converts a clause into an initial set of ; assumptions, marked :IGNORE-WHEN-CONVERTING-TO-CLAUSE. ; All of this is in support of our fast clausifier. The whole idea, here ; exposed at the very end of this long comment, is to make it fast to explore ; and recognize tautologies, paying the price for creating the clause only when ; we have to. (defun if-interp-assume-true (not-flg term assumptions) ; Adds the assumption that term is true/false according to whether ; not-flg is nil/t. Thus, to assume term true, use not-flg nil. ; These assumptions must be used in a propositional setting. That is, ; if p is assumed true in assumptions then (if-interp-assumed-value p ; assumption) will be t, but this doesn't mean that p equals t, it ; means (iff p t). Note that term should not be a quotep. (cond ((variablep term) (if not-flg (cons :not (cons term assumptions)) (cons term assumptions))) ((eq (ffn-symb term) 'not) (if-interp-assume-true (not not-flg) (fargn term 1) assumptions)) (t (if not-flg (cons :not (cons term assumptions)) (cons term assumptions))))) (defun if-interp-switch (assumptions) ; Converts assumptions to the opposite parity on the most recent ; assumption. I.e., if assumptions was created by assuming term true, ; the after this switch, the assumptions assume term false. (cond ((eq (car assumptions) :not) (cdr assumptions)) (t (cons :not assumptions)))) ; We now start the development of the lookup functions. See ; if-interp-assumed-value for the top-level function. All the others ; are just subroutines of that one. (defun if-interp-assumed-value0 (var assumptions) ; Look up the assumed value of a variable symbol. (cond ((null assumptions) nil) ((eq (car assumptions) :not) (cond ((eq var (cadr assumptions)) 'f) (t (if-interp-assumed-value0 var (cddr assumptions))))) ((eq (car assumptions) var) 't) (t (if-interp-assumed-value0 var (cdr assumptions))))) (defun if-interp-assumed-value1 (term assumptions) ; Look up the assumed value of an arbitrary non-NOT term -- i.e., just ; like the variable case but using equal instead of eq to compare. (cond ((null assumptions) nil) ((eq (car assumptions) :not) (cond ((equal term (cadr assumptions)) 'f) (t (if-interp-assumed-value1 term (cddr assumptions))))) ((equal (car assumptions) term) 't) (t (if-interp-assumed-value1 term (cdr assumptions))))) (defun if-interp-assumed-value2-equal-constant (arg const1 assumptions) ; This function is an optimization of if-interp-assumed-value2, which looks up ; the assumed value of an EQUAL or IFF term. However, here, we know the term ; is of the form (EQUAL arg const1), where const1 is a quoted constant. The ; key difference between this situation and the more general one is that if ; assumptions contains (EQUAL arg const2), where const2 is different from ; const1, we know the answer is 'f. (cond ((null assumptions) nil) ((eq (car assumptions) :not) (let ((term (cadr assumptions))) (cond ((variablep term) (if-interp-assumed-value2-equal-constant arg const1 (cddr assumptions))) ((and (eq 'EQUAL (ffn-symb term)) (or (and (equal arg (fargn term 1)) (equal const1 (fargn term 2))) (and (equal arg (fargn term 2)) (equal const1 (fargn term 1))))) 'f) (t (if-interp-assumed-value2-equal-constant arg const1 (cddr assumptions)))))) (t (let ((term (car assumptions))) (cond ((variablep term) (if-interp-assumed-value2-equal-constant arg const1 (cdr assumptions))) (t (let ((term-fn (ffn-symb term))) ; Term-fn is either a function symbol or a lambda expression. (cond ((eq term-fn 'EQUAL) (cond ((or (and (equal arg (fargn term 1)) (equal const1 (fargn term 2))) (and (equal arg (fargn term 2)) (equal const1 (fargn term 1)))) 't) ((or (and (equal arg (fargn term 1)) (quotep (fargn term 2)) (not (equal const1 (fargn term 2)))) (and (equal arg (fargn term 2)) (quotep (fargn term 1)) (not (equal const1 (fargn term 1))))) 'f) (t (if-interp-assumed-value2-equal-constant arg const1 (cdr assumptions))))) (t (if-interp-assumed-value2-equal-constant arg const1 (cdr assumptions))))))))))) (defun if-interp-assumed-value2 (fn arg1 arg2 assumptions) ; Look up the assumed value of (fn arg1 arg2), where fn is a function ; symbol (e.g., EQUAL or IFF) that is known to be commutative. This is ; like (or (if-interp-assumed-value1 `(,fn ,arg1 ,arg2) assumptions) ; (if-interp-assumed-value1 `(,fn ,arg2 ,arg1) assumptions)). (cond ((null assumptions) nil) ((eq (car assumptions) :not) (let ((term (cadr assumptions))) (cond ((variablep term) (if-interp-assumed-value2 fn arg1 arg2 (cddr assumptions))) ((and (eq fn (ffn-symb term)) (or (and (equal arg1 (fargn term 1)) (equal arg2 (fargn term 2))) (and (equal arg1 (fargn term 2)) (equal arg2 (fargn term 1))))) 'f) (t (if-interp-assumed-value2 fn arg1 arg2 (cddr assumptions)))))) ((let* ((term (car assumptions)) (term-fn (and (nvariablep term) (ffn-symb term)))) ; Term-fn is either nil (in case term is a variable), or else a function symbol ; or a lambda expression. Once upon a time, the (and (nvariablep term) ...) ; above included the conjunct (not (fquotep term)). That is unnecessary. If ; (nvariablep term), then we know (ffn-symb term) is a function symbol or ; lambda expression. Thus, term could not be a quotep. (and (eq fn term-fn) ;nil is not a function symbol (or (and (equal arg1 (fargn term 1)) (equal arg2 (fargn term 2))) (and (equal arg1 (fargn term 2)) (equal arg2 (fargn term 1)))))) 't) (t (if-interp-assumed-value2 fn arg1 arg2 (cdr assumptions))))) (defun if-interp-assumed-value3 (term assumptions) ; Look up the assumed value of an arbitrary non-NOT (RATIONALP x) term. ; This function is very similar to if-interp-assumed-value1 except that ; if we find (INTEGERP x) assumed true, we know (RATIONALP x) is true. (cond ((null assumptions) nil) ((eq (car assumptions) :not) (cond ((equal term (cadr assumptions)) 'f) (t (if-interp-assumed-value3 term (cddr assumptions))))) ((equal (car assumptions) term) 't) ((and (not (variablep (car assumptions))) (eq (ffn-symb (car assumptions)) 'INTEGERP) (equal (fargn term 1) (fargn (car assumptions) 1))) 't) (t (if-interp-assumed-value3 term (cdr assumptions))))) (defun if-interp-assumed-value4 (term assumptions) ; Look up the assumed value of an arbitrary non-NOT (INTEGERP x) term. ; This function is very similar to if-interp-assumed-value1 except that ; if we find (RATIONALP x) assumed false, we know (INTEGERP x) is false. (cond ((null assumptions) nil) ((eq (car assumptions) :not) (cond ((equal term (cadr assumptions)) 'f) ((and (not (variablep (cadr assumptions))) (eq (ffn-symb (cadr assumptions)) 'RATIONALP) (equal (fargn term 1) (fargn (cadr assumptions) 1))) 'f) (t (if-interp-assumed-value4 term (cddr assumptions))))) ((equal (car assumptions) term) 't) (t (if-interp-assumed-value4 term (cdr assumptions))))) (defun if-interp-assumed-value-x (term assumptions) ; Look up the assumed value of an arbitrary non-NOT term, treating ; EQUAL and IFF as commutative and recognizing that INTEGERP ; implies RATIONALP. (cond ((variablep term) (if-interp-assumed-value0 term assumptions)) ((eq (ffn-symb term) 'EQUAL) (cond ((quotep (fargn term 1)) (if-interp-assumed-value2-equal-constant (fargn term 2) (fargn term 1) assumptions)) ((quotep (fargn term 2)) (if-interp-assumed-value2-equal-constant (fargn term 1) (fargn term 2) assumptions)) (t (if-interp-assumed-value2 (ffn-symb term) (fargn term 1) (fargn term 2) assumptions)))) ((eq (ffn-symb term) 'IFF) (if-interp-assumed-value2 (ffn-symb term) (fargn term 1) (fargn term 2) assumptions)) ((eq (ffn-symb term) 'RATIONALP) (if-interp-assumed-value3 term assumptions)) ((eq (ffn-symb term) 'INTEGERP) (if-interp-assumed-value4 term assumptions)) (t (if-interp-assumed-value1 term assumptions)))) (defun if-interp-assumed-value (term assumptions) ; Look up the assumed value of an arbitrary term, treating EQUAL and ; IFF as commutative. This function returns t, f, or nil. The last ; means that no assumptions about term are available. The other ; indicate that term has been assumed true or false, respectively. ; Note that a value of t does not mean (EQUAL term T) but (IFF term ; T), under the assumptions. (cond ((variablep term) (if-interp-assumed-value0 term assumptions)) ((eq (ffn-symb term) 'NOT) (let ((temp (if-interp-assumed-value-x (fargn term 1) assumptions))) (cond ((eq temp t) 'f) ((eq temp 'f) t) (t nil)))) (t (if-interp-assumed-value-x term assumptions)))) (defun convert-assumptions-to-clause-segment (assumptions ans known-constants) ; We convert an assumptions structure to a clause segment, a list of disjoined ; literals to use as the hypothesis. Assumptions represents a conjunction of ; assumptions. E.g., (A :NOT B C D) represents (AND A (NOT B) C D). Observe ; that this is the same as (NOT (OR (NOT A) B (NOT C) (NOT D))). Thus, the ; clause segment is ((NOT A) B (NOT C) (NOT D)). We reverse it as we create ; it. When we get to the special marker :ignore-when-converting-to-clause we ; stop and act as though assumptions were nil. This allows us to load up ; assumptions with some initial implicit literals that do not get transferred ; into the clauses we generate. ; We implement the optimization that if one of our assumptions is ; (EQUAL x 'const1) then any subsequent (NOT (EQUAL x 'const2)) is T and ; can be omitted, where const1 and const2 are different constants. ; That is, if assumptions is ; ((EQUAL x 'const1) :NOT (equal x 'const2) P Q) ; we return ; ((NOT (EQUAL x 'const1)) (NOT P) (NOT Q)) ; instead of ; ((NOT (EQUAL x 'const1)) (EQUAL x 'const2) (NOT P) (NOT Q)). ; (Actually, our answer is reversed.) ; We always know that the positive equality precedes the negative one in ; the input assumptions. That is, we will never see ; (:NOT (equal x 'const2) (EQUAL x 'const1) P Q) ; as our assumptions. The reason is that if (EQUAL x 'const1) is already ; assumed, then when if-interp gets the value of (equal x 'const2) under the ; assumptions it will come back 'f. ; Thus, whenever we see a positive equality with a constant, (EQUAL x 'const1), we ; add the pair (x . const1) onto known-constants. Whenever we see a negative ; equality with a constant, we ask if we know what the value is. (cond ((or (null assumptions) (eq (car assumptions) :ignore-when-converting-to-clause)) ans) ((eq (car assumptions) :not) (let ((test (cadr assumptions))) ; Everything in the first branch of the cond below is devoted to the optimization ; of (NOT (EQUAL x 'const2)) when x is known to be a different 'const1. To see ; the simple case of this function, skip to the T clause of this cond. (cond ((and (nvariablep test) (eq (ffn-symb test) 'equal) (or (quotep (fargn test 1)) (quotep (fargn test 2)))) (cond ((quotep (fargn test 1)) (let* ((x (fargn test 2)) (const2 (fargn test 1)) (temp (assoc-equal x known-constants))) ; We are processing (NOT (EQUAL x const2)), where const2 is a quoted constant. ; If x is bound on known-constants to a different object, this assumption is ; trivially T and can be omitted from our answer. (cond ((and temp (not (equal const2 (cdr temp)))) (convert-assumptions-to-clause-segment (cddr assumptions) ans known-constants)) (t (convert-assumptions-to-clause-segment (cddr assumptions) (cons test ans) known-constants))))) ((quotep (fargn test 2)) (let* ((x (fargn test 1)) (const2 (fargn test 2)) (temp (assoc-equal x known-constants))) ; We are processing (NOT (EQUAL x const2)), where const2 is a quoted constant. ; If x is bound on known-constants to a different object, this assumption is ; trivially T and can be omitted from our answer. (cond ((and temp (not (equal const2 (cdr temp)))) (convert-assumptions-to-clause-segment (cddr assumptions) ans known-constants)) (t (convert-assumptions-to-clause-segment (cddr assumptions) (cons test ans) known-constants))))) (t (convert-assumptions-to-clause-segment (cddr assumptions) (cons test ans) known-constants)))) (t (convert-assumptions-to-clause-segment (cddr assumptions) (cons test ans) known-constants))))) (t (let ((test (car assumptions))) (cond ((and (nvariablep test) (eq (ffn-symb test) 'equal) (or (quotep (fargn test 1)) (quotep (fargn test 2)))) (cond ((quotep (fargn test 1)) (convert-assumptions-to-clause-segment (cdr assumptions) (cons (list 'not test) ans) (cons (cons (fargn test 2) (fargn test 1)) known-constants))) ((quotep (fargn test 2)) (convert-assumptions-to-clause-segment (cdr assumptions) (cons (list 'not test) ans) (cons (cons (fargn test 1) (fargn test 2)) known-constants))) (t (convert-assumptions-to-clause-segment (cdr assumptions) (cons (list 'not test) ans) known-constants)))) (t (convert-assumptions-to-clause-segment (cdr assumptions) (cons (list 'not test) ans) known-constants))))))) (defun convert-clause-to-assumptions (clause ans) ; The falsity of each literal in clause is encoded into our assumptions format. ; We then cover the entire list of assumptions with the mark ; :ignore-when-converting-to-clause. The function if-interp-assumed-value ; properly recovers from these assumptions the values of the literals assumed ; false here. The :ignore-when-converting-to-clause marker is innocuous since ; it looks like a variable assumed true, but we will never ask about a keyword ; "variable". As if-interp explores its term to construct clauses it will ; extend our assumptions and if-interp-assumed-value continues to recover ; values of new and old assumptions. But when if-interp finally builds a ; clause from assumptions, it ignores the ones stemming from clause. (cond ((null clause) (cons :ignore-when-converting-to-clause ans)) (t (convert-clause-to-assumptions (cdr clause) (if-interp-assume-true t (car clause) ans))))) ; Rockwell Addition: Minor change. We used to create the instantiation ; sublis-var. Now I chase vars. (defun simplifiable-mv-nth1 (n cons-term alist) ; N is a natural number. If cons-term/alist is of the form ; (cons v0 ... (cons vn ...)), we return (mv vn alist'), where alist' is the ; alist under which to interpret vi. Cons-term may, of course, be ; a variable or may contain variables, bound in alist. We return ; (mv nil nil) if we do not like what we see. (cond ((variablep cons-term) (let ((temp (assoc-eq cons-term alist))) (cond (temp (simplifiable-mv-nth1 n (cdr temp) nil)) (t (mv nil nil))))) ((fquotep cons-term) ; If the guts of this quote is a true-list of sufficient length, we ; return the correct answer. Otherwise, we indicate that we cannot ; determine the value. We could, always, determine the value in this ; case, but we are lazy and there seems little point. (cond ((and (true-listp (cadr cons-term)) (> (length (cadr cons-term)) n)) (mv (kwote (nth n (cadr cons-term))) nil)) (t (mv nil nil)))) ((eq (ffn-symb cons-term) 'cons) (if (= n 0) (mv (fargn cons-term 1) alist) (simplifiable-mv-nth1 (1- n) (fargn cons-term 2) alist))) (t (mv nil nil)))) (defun simplifiable-mv-nth (term alist) ; Term/alist must be a term of the form (mv-nth & &), i.e., the ; ffn-symb of term is known to be 'mv-nth. We determine whether we ; can simplify this and is so return (mv term' alist') as the ; simplification. If we cannot, we return (mv nil nil). We look for ; (mv-nth 'i (cons v1 ... (cons vi ...))), but we allow the two ; arguments of term to be variable symbols that are looked up. That ; is, we allow (MV-NTH I V) where I is bound in alist to a quoted ; constant and V is bound to a CONS term. (let ((arg1 (cond ((variablep (fargn term 1)) (let ((temp (assoc-eq (fargn term 1) alist))) (cond (temp (cdr temp)) (t (fargn term 1))))) (t (fargn term 1))))) (cond ((and (quotep arg1) (integerp (cadr arg1)) (>= (cadr arg1) 0)) (mv-let (term1 alist1) (simplifiable-mv-nth1 (cadr arg1) (fargn term 2) alist) (cond (term1 (mv term1 alist1)) (t (mv nil nil))))) (t (mv nil nil))))) (defun simplifiable-mv-nthp (term alist) ; Here is a predicate version of the above. (mv-let (term alist) (simplifiable-mv-nth term alist) (declare (ignore alist)) (if term t nil))) (defun call-stack (fn lst stack assumptions ac) (declare (xargs :guard (and (true-listp lst) (true-listp stack) (>= (length stack) (length lst))))) (cond ((null lst) (cons (cond ((eq fn 'not) (let ((x (car ac))) (cond ((quotep x) (if (eq (cadr x) nil) *t* *nil*)) (t (let ((temp (if-interp-assumed-value x assumptions))) (cond ((eq temp t) *nil*) ((eq temp 'f) *t*) ; ((variablep x) (list 'not x)) ; Note: In Version_2.7 it was noticed by Qiang Zhang that the there ; was an unsoundness which we traced to the two lines commented out ; below. This unsoundness goes fairly far back into the history of ; ACL2 and allowed us to prove (equal (and p q) (not (or (not p) (not ; q)))). If it is found important to simplify (not (not x)) to x, as ; is done here, it will be necessary to determine whether we are in a ; propositional context, e.g., IFF-FLG = T or geneqv = *geneqv-iff*. ; But we have no such contextual information in the compiled code ; being traversed by if-interp. It would be necessary to change the ; if-compile to insert some kind of iff-flg into the instructions ; generated to code the fact that this value is destined to be used in ; a propositional way. If we restore the lines below, then we will ; need to restore the line commented out above (with the variablep ; test). ; ((eq (ffn-symb x) 'not) ; (fargn x 1)) (t (list 'not x)))))))) ((eq fn 'equal) (cond ((equal (car ac) (cadr ac)) *t*) ((and (quotep (car ac)) (quotep (cadr ac))) *nil*) ((and (equal (car ac) *t*) (nvariablep (cadr ac)) ; (not (fquotep (cadr ac))) (eq (ffn-symb (cadr ac)) 'equal)) ; Note: (equal t (equal a b)) = (equal a b). (cadr ac)) ((and (equal (cadr ac) *t*) (nvariablep (car ac)) (eq (ffn-symb (car ac)) 'equal)) (car ac)) (t (fcons-term fn ac)))) ; Rockwell Addition: Now during clausification we know that (< x x) is ; nil and (< 'i 'j) can be decided when i and j are rationals. ((eq fn '<) (cond ((equal (car ac) (cadr ac)) *nil*) ((and (quotep (car ac)) (quotep (cadr ac)) (rationalp (cadr (car ac))) (rationalp (cadr (cadr ac)))) (if (< (cadr (car ac)) (cadr (cadr ac))) *t* *nil*)) (t (cons-term fn ac)))) ((eq fn 'iff) (let ((arg1 (car ac)) (arg2 (cadr ac))) (cond ((equal arg1 arg2) *t*) (t (let ((temp1 (if (quotep arg1) (if (eq (cadr arg1) nil) 'f t) (if-interp-assumed-value arg1 assumptions))) (temp2 (if (quotep arg2) (if (eq (cadr arg2) nil) 'f t) (if-interp-assumed-value arg2 assumptions)))) (cond ((and temp1 temp2) (if (eq temp1 temp2) *t* *nil*)) ; There is a temptation here to simplify (iff t x) to x, which ; preserves iff but not equal. But this call of IFF might be in a ; equal-preserving slot, e.g., (CONS (IFF T (IF A X Y)) TL). (t (fcons-term fn ac)))))))) ((eq fn 'mv-nth) ; This optimization of clausify is slightly tainted by the fact that it is ; using the definition of mv-nth without reporting it in a ttree (there is no ; ttree). (let ((term (fcons-term fn ac))) (if (simplifiable-mv-nthp term nil) ; Alist1 below must be nil since we used nil above. (mv-let (term1 alist1) (simplifiable-mv-nth term nil) (declare (ignore alist1)) term1) term))) (t (cons-term fn ac))) stack)) (t (call-stack fn (cdr lst) (cdr stack) assumptions (cons (car stack) ac))))) (defun ret-stack (lst stack) (cond ((null lst) stack) (t (ret-stack (cdr lst) (cdr stack))))) (defun extra-info-lit-p (lit) (and (nvariablep lit) ; (not (fquotep lit)) (eq (ffn-symb lit) 'not) (let ((atm (fargn lit 1))) (and (nvariablep atm) (eq (ffn-symb atm) *extra-info-fn*))))) (defun subsetp-equal-mod-extra-info-lits (x y) (declare (xargs :guard (and (true-listp y) (true-listp x)))) (cond ((endp x) t) ((or (extra-info-lit-p (car x)) (member-equal (car x) y)) (subsetp-equal-mod-extra-info-lits (cdr x) y)) (t nil))) (defun quick-and-dirty-subsumption-replacement-step1 (cl1 cl2) (cond ((null cl1) 'subsumed2) ((extra-info-lit-p (car cl1)) (quick-and-dirty-subsumption-replacement-step1 (cdr cl1) cl2)) ((null cl2) 'subsumed1) ((extra-info-lit-p (car cl2)) (quick-and-dirty-subsumption-replacement-step1 cl1 (cdr cl2))) ((equal (car cl1) (car cl2)) (let ((ans (quick-and-dirty-subsumption-replacement-step1 (cdr cl1) (cdr cl2)))) (cond ((symbolp ans) ; Experiments show that (symbolp ans) is marginally faster than (or (null ans) ; (eq ans 'subsumed2) (eq ans 'subsumed1)). ans) (t (cons (car cl1) ans))))) ((and (complementaryp (car cl1) (car cl2)) (subsetp-equal-mod-extra-info-lits (cdr cl1) (cdr cl2))) (cdr cl2)) (t nil))) (defun quick-and-dirty-subsumption-replacement-step (cl1 lst) ; Aka The Satriani Hack (Note on the Derivation of the Name: The theme music of ; this exercise was Joe Satriani's "Motorcycle Driver" on The Extremist album. ; That track was not just what I was listening to while this code was written; ; the structure of the music sort of inspired the code. The music starts out ; boringly repetitive and "slow." A fairly decent guitar solo at 2:02 doesn`t ; do the job, in the sense that after this attempted speedup the plodding drums ; still dominate and the repetitive theme reemerges. But then, at 3:33 the ; guitar, spewing frustration, breaks out into a really wild solo that persists ; into the next reoccurrence of the theme and ends the song. The sense I get ; while listening to that solo is that the guitarist simply abandoned the ; structure and did whatever it took. That is the theme of the Satriani Hack, ; which actually is not localized here but involves little tweaks and patches ; in several places, to get the speedup I wanted. JSM.) ; This function is akin to subsumption-replacement-loop except that it only ; takes one step and is much more limited in its detection of the ; subsumption/replacement conditions. Let lst be a set of clauses we have to ; prove. Imagine that we are going to add cl1 to that set. If cl1 is subsumed ; by any clause in lst, we needn't add it. Among other things, this function ; checks a limited form of that condition; if we return 'subsumed1 then cl1 is ; subsumed by some clause in lst. Otherwise, suppose that cl1 can be resolved ; against some clause, cl2, of lst to produce a clause cl3 that subsumes cl2. ; We call this a "replacement resolution." For example, suppose ; cl1 = {a b c d e} ; cl2 = {a b -c d f e g} ; cl3 = {a b d f e g} ; Then when we add cl1 to the set of clauses to prove we can delete cl2 from ; the set and replace it with cl3. In addition, if cl1 simply subsumes some ; cl2, we can delete cl2 from the set. If this function does not return ; 'subsumed1 then it returns a new set of clauses in which some of those ; subsumed by cl1 have been deleted and some of those that participate in ; replacement resolution with cl1 have been appropriately replaced. Thus, if ; this function does not return 'subsumed1 it is sound to add cl1 to the output ; of this function and attack that set of clauses. ; The "quick and dirty" part of this is that we do not consider all possible ; literals upon which to do replacement resolution but rather only consider ; resolving on the first literal in cl1 that differs from the corresponding ; literal of cl2, and we insist that the corresponding literal of cl2 be the ; required complement. The "step" part comes from the fact that we don't ; consider every possible pair of cl1 and cl2 but only the about-to-be-added ; cl1 against the already added cl2s. ; This rather draconian restriction is judged heuristically important because ; of the order in which clauses are generated. The motivating example was of ; the form ; (clausify ; '(not (if A ; (if (if E1 ; 't ; (if E2 ; 't ; E3)) ; B ; 'nil) ; 'nil)) ; nil ; t ; or nil, no lambdas here. ; (sr-limit (w state))) ; Before we added this quick and dirty test, we created ; {-a -e1 -b} ; {-a e1 -e2 -b} ; {-a e1 e2 -e3 -b} ; The general-purpose subsumption-replacement-loop would work this down to ; {-a -e1 -b} ; {-a -e2 -b} ; {-a -e3 -b} ; But that was slow because it considers all possible ways of resolving and ; subsuming. After a couple of days of Satriani and some false starts, it was ; realized (in the shower, no less) that the clauses were probably generated in ; just the right order to let us detect this condition quickly on the fly. ; Another motivating example comes from clausifying the opened up version of ; (not (member x '(1 2 ... 128))). This arises when the member term is used as ; a hypothesis. The problem becomes: ; (clausify '(not (if e1 't (if e2 't (if e3 't ...(if e127 't e128)...)))) ; nil t (sr-limit (w state))) ; which is like the (if e1 ...) nest above. In Nqthm the clausifier had ; special purpose rules for handling a negated disjunction, but that is harder ; in ACL2 because the compiled form of the term hides the negation. But the ; Satriani hack takes care of it, by cleaning up the clause set locally as it ; is produced, leaving the quadratic general-purpose ; subsumption-replacement-loop with nothing to do. ; To see this hack in action, first define the function that maps ; the list of standard chars into the list of standard codes: ; (defun make-standard-codes (lst) ; (if (endp lst) ; nil ; (cons (char-code (car lst)) (make-standard-codes (cdr lst))))) ; and use it to define the appropriate constant ; (defconst *standard-codes* (make-standard-codes *standard-chars*)) ; Then prove ; (thm (implies (member x *standard-chars*) ; (member (char-code x) *standard-codes*))) ; With the Satriani hack in place, the proof takes 3.87 seconds. With the ; Satriani hack omitted, it takes 431.92 seconds! (Note: to omit the Satriani ; hack from these sources redefine the function if-interp-add-clause below so ; that ans is bound to ac rather than to the call of ; quick-and-dirty-subsumption-replacement-step.) ":Doc-Section Other (advanced topic) controlling a heuristic in the prover's clausifier~/ This topic is probably only of interest to those who are undertaking particularly complex proof developments. The ACL2 prover's handling of propositional logic uses a heuristic that we believe might cause the prover to slow down significantly or even to trigger a stack overflow. However, we believe these negative effects are only felt on very large formulas ~-[] formulas that are likely to cause similar degradation of many other parts of ACL2. The following two events turn off that heuristic (by eliminating calls to source function ~c[quick-and-dirty-subsumption-replacement-step], after which this ~il[documentation] topic is named). ~bv[] (defun quick-and-dirty-srs-off (cl1 ac) (declare (ignore cl1 ac) (xargs :mode :logic :guard t)) nil) (defattach quick-and-dirty-srs quick-and-dirty-srs-off) ~ev[] However, if you feel the need to try this out, please remember that the proof is likely to fail anyway since other parts of ACL2 will probably be stressed. A more basic problem with your proof strategy may exist: the formulas are getting extraordinarily large. A common approach for avoiding such problem include disabling functions (~pl[in-theory]). Other less common approaches might help if you are sufficiently desperate, such as defining your own ~c[IF] function to use in place of the built-in ~c[IF]. If you do find an example for which the above two events provide significant benefit, we (the ACL2 implementors) would be interested in hearing about it. To turn the heuristic back on: ~bv[] (defattach quick-and-dirty-srs quick-and-dirty-srs-builtin) ~ev[]~/~/" (cond ((null lst) nil) ((time-limit5-reached-p ; nil, or throws "Out of time in subsumption ~ (quick-and-dirty-subsumption-replacement-step).") nil) (t (let ((cl3 (quick-and-dirty-subsumption-replacement-step1 cl1 (car lst)))) (cond ((eq cl3 'subsumed1) 'subsumed1) (t (let ((ans (quick-and-dirty-subsumption-replacement-step cl1 (cdr lst)))) (cond ((eq cl3 'subsumed2) ans) ((eq ans 'subsumed1) ans) ((null cl3) (cons (car lst) ans)) (t (cons cl3 ans)))))))))) (defstub quick-and-dirty-srs (cl1 ac) t) (defun quick-and-dirty-srs-builtin (cl1 ac) (declare (ignore cl1 ac) (xargs :mode :logic :guard t)) t) (defattach quick-and-dirty-srs quick-and-dirty-srs-builtin) (defun if-interp-add-clause (assumptions cl ac pflg) ; This is how we add a new clause to if-interp's accumulator, ac. The clause ; we add is the one recovered from the current assumptions, starting with the ; clause cl. If pflg is t then the caller is not interested in the set of ; clauses but just whether the set is empty or not. In that case, we return t ; if the set of clauses is non-empty and nil if it is empty. (cond (pflg t) (t (let ((cl1 (convert-assumptions-to-clause-segment assumptions cl nil))) (cond ((quick-and-dirty-srs cl1 ac) (let ((ans (quick-and-dirty-subsumption-replacement-step cl1 ac))) (cond ((eq ans 'subsumed1) ac) (t (cons cl1 ans))))) (t (cons cl1 ac))))))) (defun if-interp (instrs stack frame-ptr-stack assumptions ac pflg) ; First consider the case that pflg is nil. Then we return the set of clauses ; extracted from instrs, together with those already in ac. ; Otherwise pflg is a natural number, and we quit as soon as we know that there ; will be at least one clause. When we so quit, we return t. Otherwise we ; return pflg, which counts down as steps are taken. Thus if we return 0, then ; there might or might not be at least one clause, but if we return a positive ; integer, then the term encoded in instrs is a tautology. (declare (type (or null (unsigned-byte 29)) pflg)) (cond ((null instrs) (let ((v (car stack))) (or (cond ((quotep v) (cond ((equal v *nil*) (if-interp-add-clause assumptions nil ac pflg)) (t ac))) (t (let ((assumed-val (if-interp-assumed-value v assumptions))) (cond ((eq assumed-val t) ac) ((eq assumed-val 'f) (if-interp-add-clause assumptions nil ac pflg)) (t (if-interp-add-clause assumptions (list v) ac pflg)))))) pflg))) ((and pflg (zpf pflg)) 0) (t (let ((caarinstrs (caar instrs)) (pflg (and pflg (1-f pflg)))) (declare (type (or null (unsigned-byte 29)) pflg)) (case caarinstrs (push (if-interp (cdr instrs) (cons (cdr (car instrs)) stack) frame-ptr-stack assumptions ac pflg)) (push-local (if-interp (cdr instrs) (cons (nth (cdr (car instrs)) (car frame-ptr-stack)) stack) frame-ptr-stack assumptions ac pflg)) (push-frame-ptr (if-interp (cdr instrs) stack (cons stack frame-ptr-stack) assumptions ac pflg)) (ret (if-interp (cdr instrs) (cons (car stack) (ret-stack (cdr (car instrs)) (cdr stack))) (cdr frame-ptr-stack) assumptions ac pflg)) (call (if-interp (cdr instrs) (call-stack (cadr (car instrs)) (cddr (car instrs)) stack assumptions nil) frame-ptr-stack assumptions ac pflg)) (test (let* ((v (car stack)) (stack (cdr stack))) (cond ((quotep v) (cond ((equal v *nil*) (if-interp (cdr (car instrs)) stack frame-ptr-stack assumptions ac pflg)) (t (if-interp (cdr instrs) stack frame-ptr-stack assumptions ac pflg)))) (t (let ((temp (if-interp-assumed-value v assumptions))) (cond ((eq temp 'f) (if-interp (cdr (car instrs)) stack frame-ptr-stack assumptions ac pflg)) ((eq temp t) (if-interp (cdr instrs) stack frame-ptr-stack assumptions ac pflg)) (pflg (let ((assumptions (if-interp-assume-true nil v assumptions))) (let ((pflg (if-interp (cdr instrs) stack frame-ptr-stack assumptions ac pflg))) (cond ((eq pflg t) t) (t (if-interp (cdr (car instrs)) stack frame-ptr-stack (if-interp-switch assumptions) ac pflg)))))) (t (let ((assumptions (if-interp-assume-true nil v assumptions))) (if-interp (cdr instrs) stack frame-ptr-stack assumptions (if-interp (cdr (car instrs)) stack frame-ptr-stack (if-interp-switch assumptions) ac pflg) pflg)))))))))))))) (defun splice-instrs1 (instrs ans alist) (declare (xargs :guard (instr-listp instrs))) (cond ((null instrs) ans) ((atom (car instrs)) (splice-instrs1 (cdr instrs) ans (cons (cons (car instrs) ans) alist))) (t (let ((caarinstrs (caar instrs))) (case caarinstrs ((push push-local push-frame-ptr call ret) (splice-instrs1 (cdr instrs) (cons (car instrs) ans) alist)) (test (splice-instrs1 (cdr instrs) (cons (cons 'test (cdr (assoc (cdr (car instrs)) alist))) ans) alist)) (go (splice-instrs1 (cdr instrs) (cdr (assoc (cdr (car instrs)) alist)) alist))))))) (defun splice-instrs (instrs) (declare (xargs :guard (instr-listp instrs))) (splice-instrs1 instrs nil nil)) (defun strip-branches (term assumptions lambda-exp) ; We return a set of clauses whose conjunction is equivalent to term in the context ; of the assumptions given. See clausify. (declare (xargs :guard (pseudo-termp term))) (cond ((and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'if) (equal (fargn term 3) *nil*)) ; Term is of the form (if p q 'nil). We will strip the branches of each in ; isolation and union them together. The original justification of this was ; so that when we clausify the translation of (and (implies p q) r) we get ; back two clauses, {~p q} and {r}. Without this modification, we get back ; three clauses, {p r}, {~p q}, and {~q r}. Except for here, strip-branches ; is not recursive and this special treatment of conjuncts is not done in ; other contexts. (union-equal (strip-branches (fargn term 1) assumptions lambda-exp) (strip-branches (fargn term 2) assumptions lambda-exp))) (t (if-interp (splice-instrs (if-compile term lambda-exp nil nil)) nil nil assumptions nil nil)))) (defun merge-length (l1 l2) (declare (xargs :guard (and (true-list-listp l1) (true-list-listp l2)))) (cond ((null l1) l2) ((null l2) l1) ((<= (length (car l1)) (length (car l2))) (cons (car l1) (merge-length (cdr l1) l2))) (t (cons (car l2) (merge-length l1 (cdr l2)))))) (defun merge-sort-length (l) (declare (xargs :guard (true-list-listp l))) (cond ((null (cdr l)) l) (t (merge-length (merge-sort-length (evens l)) (merge-sort-length (odds l)))))) (defun member-equal-+- (lit clause) ; We return '+ if lit is a member of clause. We return '- if the complement of ; lit is a member of clause. Otherwise we return nil. If both conditions are ; met, we return either '+ or '- depending on which occurs first. For example, ; let clause be '(A (NOT B)). Then if lit is A we return '+. If lit is (NOT ; A) we return '-. We also return '- when lit is B. If lit is C we return ; nil. (cond ((null clause) nil) ((equal lit (car clause)) '+) ((complementaryp lit (car clause)) '-) (t (member-equal-+- lit (cdr clause))))) (defun arg1-almost-subsumes-arg2 (arg1 arg2) (declare (xargs :guard (and (pseudo-term-listp arg1) (pseudo-term-listp arg2)))) ; We are interested in ``throwing away,'' or at least shortening, the ; clause arg2. We return 'subsumed, a cons, or nil. ; If the clause arg1 subsumes (i.e. is a subset of) arg2, then ; 'subsumed is returned. This means we can ``throw away arg2'', ; because arg1 <-> (arg1 & arg2) since if arg1 is true, so is arg2, ; whereas if arg1 is false, so is the conjunction. ; If arg1 is a subset of arg2 except for one literal of arg1 which occurs ; complemented in arg2, we return a cons whose car is that literal. ; Note that the resolvent of arg1 and arg2 on this literal produces a ; clause that subsumes arg2: the clause obtained by deleting the ; complement of the literal in question. ; Here is a more careful argument that we can delete the complement. ; If the subsumption fails but arg1 has the form {x} u arg1' (x not ; in arg1'), arg1' subsumes arg2, and -x occurs in arg2, then the ; tail of arg1 starting at x (which will be non-nil, of course) is ; returned. In this case, we can REPLACE arg2 with arg2 - {-x}, ; which has one less literal. This replacement is justified by the ; fact that arg1 & arg2 <-> arg1 & (arg2 - {-x}). Proof. If arg1 is ; false, both sides are false. If arg1 is true, then the equivalence ; reduces to arg2 <-> arg2 - {-x}. But if arg1 is true, either x or ; arg1' is true. If arg1' is true, then so is arg2 and arg2 - {-x}. ; On the otherhand, if x is true, then -x is false, so the ; equivalence is the observation that we can throw out false ; disjuncts. (cond ((null arg1) 'subsumed) ((extra-info-lit-p (car arg1)) (arg1-almost-subsumes-arg2 (cdr arg1) arg2)) (t (let ((sign (member-equal-+- (car arg1) arg2))) ; Sign is +, -, or nil, meaning (car arg1) occurs in arg2, the complement of ; (car arg1) occurs in arg2, or neither occur. (cond ((null sign) nil) ((eq sign '+) (arg1-almost-subsumes-arg2 (cdr arg1) arg2)) ((subsetp-equal-mod-extra-info-lits (cdr arg1) arg2) arg1) (t nil)))))) (defun find-subsumer-replacement (cl l) (declare (xargs :guard (and (pseudo-term-listp cl) (pseudo-term-list-listp l)))) ; We return (mv val cl0), where val is nil to indicate that no subsumer or ; replacer was found, or 'subsumed to indicate cl is subsumed by clause cl0 in ; l, or if neither of these cases applies, then a pair (indicating that the ; complement of the car of the pair may be removed from cl). The last case ; means that somewhere in l we found a clause that when resolved with cl ; produces a resolvent that subsumes cl. (cond ((null l) (mv nil nil)) (t (let ((here (arg1-almost-subsumes-arg2 (car l) cl))) (cond ((eq here 'subsumed) (mv here (car l))) (t (mv-let (rst cl0) (find-subsumer-replacement cl (cdr l)) (cond ((eq rst 'subsumed) (mv rst cl0)) (t (mv (or here rst) nil)))))))))) (defun remove-one-complement (lit cl) (declare (xargs :guard (and (pseudo-termp lit) (pseudo-term-listp cl)))) (cond ((null cl) nil) ((complementaryp lit (car cl)) (cdr cl)) (t (cons (car cl) (remove-one-complement lit (cdr cl)))))) (defun weak-disc-tree (x) (and (or (consp x) (equal x nil)) (cond ((equal (car x) 'node) (and (true-listp x) (equal (length x) 4) (pseudo-termp (cadr x)) (weak-disc-tree (caddr x)) (weak-disc-tree (cadddr x)))) (t (pseudo-term-list-listp (cdr x)))))) (defun sweep-clauses1 (tree ac) (declare (xargs :guard (weak-disc-tree tree))) (cond ((eq (car tree) 'node) (sweep-clauses1 (caddr tree) (sweep-clauses1 (cadddr tree) ac))) (t (append (cdr tree) ac)))) (defun sweep-clauses (tree) (declare (xargs :guard (weak-disc-tree tree))) (sweep-clauses1 tree nil)) (defun filter-with-and-without (x l with-lst without-lst) ; L is a list of clauses. X is a literal. We partition l into two sets: the ; with-lst contains those clauses with x as a (positive or negative) literal; ; the without-lst are all others. Then we return (mv with-lst without-lst). ; We consider a negated call of EXTRA-INFO to belong to every clause! (cond ((null l) (mv with-lst without-lst)) ((or (extra-info-lit-p x) (member-equal-+- x (car l))) (filter-with-and-without x (cdr l) (cons (car l) with-lst) without-lst)) (t (filter-with-and-without x (cdr l) with-lst (cons (car l) without-lst))))) (defun disc-tree (x) (and (or (consp x) (equal x nil)) (cond ((equal (car x) 'node) (and (true-listp x) (equal (length x) 4) (pseudo-termp (cadr x)) (disc-tree (caddr x)) (disc-tree (cadddr x)) (mv-let (with-lst without-lst) (filter-with-and-without (cadr x) (sweep-clauses (caddr x)) nil nil) (declare (ignore without-lst)) (equal (sweep-clauses (caddr x)) with-lst)) (mv-let (with-lst without-lst) (filter-with-and-without (cadr x) (sweep-clauses (cadddr x)) nil nil) (declare (ignore with-lst)) (equal (sweep-clauses (cadddr x)) without-lst)))) (t (pseudo-term-list-listp (cdr x)))))) (defun find-clauses1 (clause tree ac) (declare (xargs :guard (and (disc-tree tree) (pseudo-term-listp clause) (pseudo-term-list-listp ac)))) ; We compute a superset of all those clauses stored in tree which ; could subsume clause or which, when resolved with clause, could ; produce a new clause that subsumed clause. If the key of a node ; does not occur+- in clause, then none of the clauses on the yes ; branch of the node can be relevant because all of the clauses ; on the yes branch contain+- the key. (cond ((eq (car tree) 'node) (cond ((or (extra-info-lit-p (cadr tree)) (member-equal-+- (cadr tree) clause)) (find-clauses1 clause (caddr tree) (find-clauses1 clause (cadddr tree) ac))) (t (find-clauses1 clause (cadddr tree) ac)))) (t (append (cdr tree) ac)))) (defun find-clauses (clause tree) (find-clauses1 clause tree nil)) (defun remove-one-+- (x l) (cond ((null l) nil) ((equal x (car l)) (cdr l)) ((complementaryp x (car l)) (cdr l)) (t (cons (car l) (remove-one-+- x (cdr l)))))) (defun store-clause1 (clause undisc-lits tree) (declare (xargs :guard (and (pseudo-term-listp clause) (pseudo-term-listp undisc-lits) (disc-tree tree)))) (cond ((eq (car tree) 'node) (cond ((extra-info-lit-p (cadr tree)) (list 'node (cadr tree) (store-clause1 clause undisc-lits (caddr tree)) (cadddr tree))) ((member-equal-+- (cadr tree) clause) (list 'node (cadr tree) (store-clause1 clause (remove-one-+- (cadr tree) undisc-lits) (caddr tree)) (cadddr tree))) (t (list 'node (cadr tree) (caddr tree) (store-clause1 clause undisc-lits (cadddr tree)))))) ((null undisc-lits) (cons 'tip (cons clause (cdr tree)))) ((extra-info-lit-p (car undisc-lits)) (store-clause1 clause (cdr undisc-lits) tree)) (t (mv-let (with-lst without-lst) (filter-with-and-without (car undisc-lits) (cdr tree) nil nil) (store-clause1 clause undisc-lits (list 'node (car undisc-lits) (cons 'tip with-lst) (cons 'tip without-lst))))))) (defun store-clause (cl tree) ; Store-clause implements a specialized discrimination network for ; storing clauses during the subsumption/replacement phase of ; clausify. Here the tree is either of the form: ; (NODE lit with-tree without-tree) ; or ; (TIP . clauses) ; A tree is said to contain a clause if that clause is a member of the clause ; list at some TIP in the tree. Every clause in the with-tree of a NODE ; contains the node's lit either positively or negatively as an element. No ; clause in the without-tree of a NODE contains the lit. (store-clause1 cl cl tree)) (defun substitute1-ac (new old seq acc) (declare (xargs :guard (and (true-listp acc) (true-listp seq) (member-equal old seq)))) (cond ((endp seq) (er hard 'substitute "Attempted to substitute ~x0 for ~x1 into a sequence in which the ~ latter was not an element." new old)) ((equal old (car seq)) (revappend acc (cons new (cdr seq)))) (t (substitute1-ac new old (cdr seq) (cons (car seq) acc))))) (defun substitute1 (new old seq) (declare (xargs :guard (and (true-listp seq) (member-equal old seq)))) (substitute1-ac new old seq nil)) (defun replace-clause1 (clause undisc-lits new-clause tree) (declare (xargs :guard (and (pseudo-term-listp clause) (pseudo-term-listp undisc-lits) (disc-tree tree)))) (cond ((eq (car tree) 'node) (cond ((member-equal-+- (cadr tree) clause) (list 'node (cadr tree) (replace-clause1 clause (remove-one-+- (cadr tree) undisc-lits) new-clause (caddr tree)) (cadddr tree))) (t (list 'node (cadr tree) (caddr tree) (replace-clause1 clause undisc-lits new-clause (cadddr tree)))))) ((member-equal clause (cdr tree)) (cons (car tree) ; 'tip (substitute1 new-clause clause (cdr tree)))) (t tree))) (defun replace-clause (clause new-clause tree) (declare (xargs :guard (and (pseudo-term-listp clause) (disc-tree tree)))) (replace-clause1 clause clause new-clause tree)) (defun extra-info-lits (cl acc) (cond ((endp cl) acc) ((extra-info-lit-p (car cl)) (extra-info-lits (cdr cl) (cons (car cl) acc))) (t (extra-info-lits (cdr cl) acc)))) (defun rev-union-equal (x y) (declare (xargs :guard (and (true-listp x) (true-listp y)))) (cond ((endp x) y) ((member-equal (car x) y) (rev-union-equal (cdr x) y)) (t (rev-union-equal (cdr x) (cons (car x) y))))) (defun merge-extra-info-lits (cl cl0 tree) ; cl0 is in tree. We want to merge the extra-info-lit elements of cl into cl0. (let ((lits (extra-info-lits cl nil))) (cond (lits (replace-clause cl0 (rev-union-equal lits cl0) tree)) (t tree)))) (defun subsumption-replacement-loop (todo done-tree again-flg) (declare (xargs :guard (and (pseudo-term-list-listp todo) (disc-tree done-tree)))) ; Precondition: todo should have the shortest clauses first in order for this ; code to catch all possible subsumptions. Use merge-sort-length to sort the ; input todo. ; Caution: If there are tautologies in the input clause set, todo, then the ; output clause set may not be propositionally equivalent. The output clause ; set will imply the input. For example, let todo be ; ((A (NOT B) B) ; c1 ; (A B)) ; c2 ; Then c1 is a tautology. However, it is used to replace c2 by (A), which ; then subsumes c1. The output is thus ((A)). But the input set is ; propositionally equivalent to ((A B)). (cond ((null todo) (cond (again-flg (cond ((time-limit5-reached-p ; nil, or throws "Out of time in subsumption (subsumption-replacement-loop).") nil) (t (subsumption-replacement-loop (merge-sort-length (sweep-clauses done-tree)) nil nil)))) (t (sweep-clauses done-tree)))) (t (mv-let (x cl0) (find-subsumer-replacement (car todo) (find-clauses (car todo) done-tree)) (cond ((null x) (subsumption-replacement-loop (cdr todo) (store-clause (car todo) done-tree) again-flg)) ((eq x 'subsumed) (subsumption-replacement-loop (cdr todo) (merge-extra-info-lits (car todo) cl0 done-tree) again-flg)) (t (subsumption-replacement-loop (cdr todo) (store-clause (remove-one-complement (car x) (car todo)) done-tree) t))))))) ; Rockwell Addition: Same old lambda-exp arg. Clausify is called in ; many places and now has a new last arg. This will show up many ; times. (defun clausify (term assumptions lambda-exp sr-limit) ; We return a conjunction of clauses equivalent to term under the assumptions ; given. Assumptions must be nil (meaning no assumptions) or something ; generated by convert-clause-to-assumptions. In the latter case, assumptions ; will start with the mark :ignore-when-converting-to-clause, which means that ; the assumptions in assumptions do not get transferred into the clauses built. ; If context is nil, then (bar (if test a b)) would clausify to two clauses, ; ((not test) (bar a)) and (test (bar b)). But if (bar a) is assumed true in ; assumptions, e.g., assumptions is (:ignore-when-converting-to-clause (bar a)) ; then the first clause above is recognized as true. While the initial ; assumptions filter out literals and clauses they do not otherwise contribute; ; in particular, our answer is not a set of clauses representing context -> ; term. ; It would be nice for clausify to know all sorts of things, like type-set and ; the removal of trivial equivalences. The trouble is that if we do that, we ; need to track what was done with ttrees. But if clausify returns a ttree ; many of its callers have great difficulty accomodating it. For example, in ; the translation of :by hints, there is no provision for recording or ; reporting the rules used to "translate" the hint into a clause. For this ; reason, we've left clausify "dumb." ; Lambda-exp indicates whether we should go inside of lambdas. (declare (xargs :guard (pseudo-termp term))) (let ((clauses (pstk (strip-branches term assumptions lambda-exp)))) (cond ((or (null sr-limit) (<= (length clauses) sr-limit)) (pstk (subsumption-replacement-loop (merge-sort-length clauses) nil nil))) (t clauses)))) ; Now we get into the immediate subroutines of rewrite itself. (defun find-rewriting-equivalence (lhs type-alist geneqv wrld ttree) ; We search type-alist for a binding to *ts-t* of a term of the form ; (equiv lhs rhs), where equiv is a refinement of geneqv and lhs is as ; given in the arguments. If we find it, we return the entire binding ; and a ttree in which we have added the name of the :CONGRUENCE rule ; as a 'lemma. Equiv is known to be an equivalence relation and as ; such we know that lhs is bigger than rhs in the term-order. ; A heuristic question arises. Suppose we have several such ; equiv-terms for lhs, all different refinements of geneqv. What do ; we do? Well, we will chose the first we find. Ugh. But suppose ; they are refinements of each other. E.g., we have three hypotheses, ; (set-equal b a1), (list-equal b a2) and (equal b a3), where ; list-equal is a refinement of set-equal. Then because we know, for ; every equivalence relation equiv, that iff is preserved by equiv in ; both slots of equiv, we will eventually rewrite the b in each of the ; hypotheses above, maintaining the equivalence relation concerned. ; Thus, in (set-equal b a1) we will rewrite b maintaining set-equal ; and will choose either to replace b by a2 or a3, since list-equal ; and equal are both refinements. The point is that ultimately in the ; rewriting process the three hypotheses will become (set-equal b a3), ; (list-equal b a3) and (equal b a3) because the finest refinement ; will ultimately get to rewrite each of the others. ; No Change Loser on the ttree (cond ((null type-alist) (mv nil nil ttree)) (t (let ((entry (car type-alist))) (cond ((not (variablep (car entry))) ; This code is a bit contorted because we have found (specifically, in ; (verify-guards exec-send ...) in community book ; books/workshops/2000/lusk-mccune/lusk-mccune-final/stepproc1.lisp) that the ; ts= call below is noticeably more efficient than the (ts-disjointp ...). (let ((rw-equivp (cond ((and (eq (ffn-symb (car entry)) 'hide) (not (variablep (fargn (car entry) 1))) (eq (ffn-symb (fargn (car entry) 1)) 'rewrite-equiv)) (car entry))))) (cond ((if rw-equivp (ts-disjointp (cadr entry) *ts-nil*) (ts= (cadr entry) *ts-t*)) (let* ((equiv-term (cond (rw-equivp (fargn (fargn (car entry) 1) 1)) (t (car entry)))) (rune (and (not (flambdap (ffn-symb equiv-term))) (geneqv-refinementp (ffn-symb equiv-term) geneqv wrld)))) (cond ((and rune (equal (fargn equiv-term 1) lhs)) (mv rw-equivp equiv-term (cons-tag-trees (cddr entry) (push-lemma rune ttree)))) (t (find-rewriting-equivalence lhs (cdr type-alist) geneqv wrld ttree))))) (t (find-rewriting-equivalence lhs (cdr type-alist) geneqv wrld ttree))))) (t (find-rewriting-equivalence lhs (cdr type-alist) geneqv wrld ttree))))))) (defun obj-table (term ts ts-ttree obj geneqv wrld ttree) ; This function is (mv term' ttree'), where term' is equivalent modulo ; geneqv (see the essay on Equivalence, Refinements and Congruence- ; based Rewriting) to term and ttree' includes ttree and may include ; additional stuff. Depending on ts, the type-set of term (which is ; supported by the ts-ttree), we may coerce term to 0, t, or nil. ; Note: This function used to depend on the objective, obj, of the ; rewrite. When obj was nil, that dependency prevented obj-table from ; reducing term to t when term was known to have non-nil type-set. ; That, in turn, caused relieve-hyp to force (not term), even though ; (not term) was known nil. We now reduce term to t, nil or 0 as ; appropriate by the geneqv and ts, regardless of obj. However, we have ; left the obj parameter in place, in case we someday want to restore ; dependency on it. (declare (ignore obj)) (cond ((ts= ts *ts-t*) (mv *t* ; At one time we tested whether (equal term *t*), so that when this holds we ; can avoid a call of cons-tag-trees. However, we only call obj-table on ; non-quotep terms, so we know that this test will be false. (cons-tag-trees ts-ttree ttree))) ((ts= ts *ts-nil*) (mv *nil* (cons-tag-trees ts-ttree ttree))) ((ts= ts *ts-zero*) (mv *0* (cons-tag-trees ts-ttree ttree))) (t (let ((rune (geneqv-refinementp 'iff geneqv wrld))) (cond (rune (cond ((ts-subsetp *ts-nil* ts) (mv term ttree)) (t (mv *t* (push-lemma rune (cons-tag-trees ts-ttree ttree)))))) (t (mv term ttree))))))) (defun rewrite-solidify-rec (bound term type-alist obj geneqv ens wrld ttree pot-lst pt) (declare (type (unsigned-byte 29) bound)) (cond ((quotep term) (cond ((equal term *nil*) (mv *nil* ttree)) (t (let ((rune (geneqv-refinementp 'iff geneqv wrld))) (cond (rune (mv *t* (push-lemma rune ttree))) (t (mv term ttree))))))) ((and (nvariablep term) (eq (ffn-symb term) 'if)) ; Is this case important? It doesn't seem so, and we were tempted to delete it ; when we modified find-rewriting-equivalence after Version_3.0.1 to look for ; calls of (hide ('rewrite-equiv ..)). But at one time, deletion caused ; failure of lemma lop3-34 in community book ; books/rtl/rel5/support/lop3-proofs.lisp, so we leave this case for backward ; compatibility. (mv term ttree)) ((and (nvariablep term) (eq (ffn-symb term) 'hide) (let ((e (fargn term 1))) (case-match e (('rewrite-equiv (equiv x x)) (prog2$ x ; avoid "not used" error (equivalence-relationp equiv wrld))) (& nil)))) ; Here we rewrite terms of the form (hide (rewrite-equiv (equiv x x))) to true, ; where equiv is a known equivalence relation. This is clearly sound. It ; avoids some loops. The following example, based closely on one sent by Dave ; Greve, loops in ACL2 Version_3.2 but not in later versions (which have this ; fix). If you trace rewrite and rewrite-solidify in TEST below, you'll see ; that where formerly (HIDE (REWRITE-EQUIV (EQUAL RES (GOO X)))) rewrote (with ; RES bound to (GOO X), and thanks to running BETA-REDUCE-HIDE-WRAPPER), to ; (HIDE (REWRITE-EQUIV (EQUAL (GOO X) (GOO X)))) now instead it rewrites to ; *T*. ; (DEFEVALUATOR UNHIDE-EVAL UNHIDE-EVAL-LIST ; ((IF X Y Z) (HIDE X))) ; ; (DEFUN BETA-REDUCE-HIDE-WRAPPER (X) ; (IF (EQUAL X '(HIDE ((LAMBDA (RES X) ; (REWRITE-EQUIV (EQUAL RES (GOO X)))) ; (GOO X) ; X))) ; '(HIDE (REWRITE-EQUIV (EQUAL (GOO X) (GOO X)))) ; X)) ; ; (DEFTHM ; *META*-BETA-REDUCE-HIDE ; (IMPLIES (PSEUDO-TERMP TERM) ; (EQUAL (UNHIDE-EVAL TERM A) ; (UNHIDE-EVAL (BETA-REDUCE-HIDE-WRAPPER TERM) ; A))) ; :HINTS (("Goal" :EXPAND (:FREE (X) (HIDE X)) ; :IN-THEORY (ENABLE UNHIDE-EVAL-CONSTRAINT-0))) ; :RULE-CLASSES ((:META :TRIGGER-FNS (HIDE)))) ; ; (DEFUN GOO (X) X) ; (DEFUN FOO (X) (GOO X)) ; (IN-THEORY (DISABLE FOO GOO)) ; ; (DEFUN CONCLUSION (X) ; (LET ((RES (FOO X))) ; (AND ; (HIDE (REWRITE-EQUIV (EQUAL RES (GOO X)))) ; (INTEGERP RES)))) ; ; (DEFTHM TEST ; (IMPLIES ; (HIDE (REWRITE-EQUIV (EQUAL (FOO X) (GOO X)))) ; (CONCLUSION X)) ; :HINTS (("GOAL" :IN-THEORY (DISABLE CONCLUSION) ; :DO-NOT '(PREPROCESS)) ; (AND STABLE-UNDER-SIMPLIFICATIONP ; '(:IN-THEORY (ENABLE CONCLUSION))))) (mv *t* (push-lemma (fn-rune-nume 'hide nil nil wrld) (push-lemma (fn-rune-nume 'rewrite-equiv nil nil wrld) ; We do not track the use of equivalence relations; see comment in ; equivalence-relationp. ttree)))) (t (mv-let (rw-equivp eterm ttree) (find-rewriting-equivalence term type-alist geneqv wrld ttree) (cond (eterm ; If rw-equivp is true, then the equivalence is from a call of rewrite-equiv. ; The following recursive call is guaranteed to be made on a term that is ; smaller according to term-order, by the Third invariant on type-alists. See ; the Essay on the Invariants on Type-alists, and Canonicality. (let ((new-bound (cond ((not rw-equivp) bound) ((zpf bound) (prog2$ (er hard 'rewrite-solidify "You appear to have hit the unusual case ~ of a loop in the replacement of terms by ~ equivalent terms using rewrite-equiv. ~ The term ~x0 is involved in the loop." rw-equivp) 0)) (t (1-f bound))))) (declare (type (unsigned-byte 29) new-bound)) (rewrite-solidify-rec new-bound (fargn eterm 2) type-alist obj geneqv ens wrld ttree pot-lst pt))) (t (mv-let (ts ts-ttree) ; See the comment just after rewrite-solidify for some historical waffling. (cond ((not (eq obj '?)) (type-set term nil t type-alist ens wrld nil pot-lst pt)) (t (assoc-type-alist term type-alist wrld))) (if (null ts) (mv term ttree) (obj-table term ts ts-ttree obj geneqv wrld ttree))))))))) (defconst *rewrite-equiv-solidify-iteration-bound* ; The number below is pretty generous, since it bounds the number of recursive ; calls of rewrite-solidify-rec on behalf of rewrite-equiv. 100) (defun rewrite-solidify (term type-alist obj geneqv ens wrld ttree pot-lst pt) (rewrite-solidify-rec *rewrite-equiv-solidify-iteration-bound* term type-alist obj geneqv ens wrld ttree pot-lst pt)) ; Comment on Historical Waffling over Calling Type-Set in Rewrite-Solidify ; ; Back in v1-7 we called ; (type-set term nil force-flg type-alist nil ens wrld nil) ; here, where force-flg was passed into rewrite-solidify. ; ; We abandoned that in v1-8 and most of v1-9 and replaced it with a simple ; lookup of term in the type-alist, ; ; (assoc-type-alist term type-alist wrld) ; ; and marked the occasion by writing the following comment: ; ; ; At one time we called type-set here. As a result, the prover could simplify ; ; ; ; (thm (implies (and (not (< y 0)) ; ; (rationalp y) ; ; (not (equal 0 y))) ; ; (equal aaa (< 0 y)))) ; ; ; ; to ; ; ; ; (implies (and (not (< y 0)) ; ; (rationalp y) ; ; (not (equal 0 y))) ; ; (equal aaa t)) ; ; ; ; However, in the interest of performance we have decided to avoid a full-blown ; ; call of type-set here. You get what you pay for, perhaps. ; ; However, then Rich Cohen observed that if we are trying to relieve a hypothesis ; in a lemma and the hyp rewrites to an explicit cons expression we fail to ; recognize that it is non-nil! Here is a thm that fails for that reason: ; ; (defstub foo (x a) t) ; (defaxiom lemma ; (implies (member x a) (equal (foo x a) x))) ; (thm (equal (foo x (cons x y)) x)) ; ; We have decided to revert to the use of type-set in rewrite-solidify, but ; only when we have an objective of t or nil. Under this condition we use ; force-flg nil and dwp t. We tried the div proofs with force-flg t here ; and found premature forcing killed us. ; (defun rewrite-if11 (term type-alist geneqv wrld ttree) (mv-let (ts ts-ttree) (look-in-type-alist term type-alist wrld) (cond ((ts= ts *ts-nil*) (mv *nil* (cons-tag-trees ts-ttree ttree))) ((and (equal geneqv *geneqv-iff*) (ts-disjointp ts *ts-nil*)) (mv *t* (cons-tag-trees ts-ttree ttree))) (t (mv term ttree))))) (defun rewrite-if1 (test left right type-alist geneqv ens ok-to-force wrld ttree) ; Test, left and right are rewritten terms. They were rewritten under ; appropriate extensions of type-alist. We implement the following ; rules here: ; (if x y y) = y ; (if x x nil) = x ; (if x t nil) = x, if x is Boolean ; Note: In Version 2-5 days, the following comment was in type-set: ; Note: Because IF's are not bound on the type-alist, we need not .... ; This was not true then, nor is it true now (Version 2-7). Therefore, ; when the above three rules fail we try looking up `(if ,test ,left ,right) ; on the type-alist. This is done in rewrite-if11. ; Once upon a time we used known-whether-nil to determine if right was ; nil under type-alist and wrld. But since right is the result of ; rewriting, we claim that if it is known to be nil then it is in fact ; *nil* because of rewrite-solidify. So we no longer use ; known-whether-nil here. (cond ((equal left right) (mv left ttree)) ((equal right *nil*) (cond ((equal test left) (mv test ttree)) ((equal left *t*) (mv-let (ts ts-ttree) (type-set test ok-to-force nil type-alist ens wrld ttree nil nil) (cond ((ts-subsetp ts *ts-boolean*) (mv test ts-ttree)) (t (rewrite-if11 (mcons-term* 'if test left right) type-alist geneqv wrld ttree))))) (t (rewrite-if11 (mcons-term* 'if test left right) type-alist geneqv wrld ttree)))) (t (rewrite-if11 (mcons-term* 'if test left right) type-alist geneqv wrld ttree)))) ; Rockwell Addition: In the not-to-be-rewritten test below, we used to ; create an instantiation with sublis-var. Now we chase var bindings. ; But there is a subtlety with constants created by sublis-var. (mutual-recursion (defun equal-mod-alist (term1 alist1 term2) ; We determine whether (sublis-var alist1 term1) is equal to term2. ; We just chase vars in term1 and use equal at the tips. There is ; one subtlety. Consider ; (equal-mod-alist '(foo x z (cons x y)) ; '((x . '1) (y . '2)) ; '(foo '1 z '(1 . 2))) ; The idea is that if term2 is a quoted constant and term1 is some ; function application, then it is possible that the sublis-var will ; convert term1 to a quoted constant. We know that only happens if ; the top-most function symbol in term1 is a primitive, so we check ; that and do the sublis-var if we have to. But it only happens on ; the ``tips.'' (cond ((variablep term1) (let ((temp (assoc-eq term1 alist1))) (cond (temp (equal (cdr temp) term2)) (t (equal term1 term2))))) ((fquotep term1) (equal term1 term2)) ((variablep term2) nil) ((fquotep term2) (cond ((and (not (flambdap (ffn-symb term1))) (assoc-eq (ffn-symb term1) *primitive-formals-and-guards*)) (equal term2 (sublis-var alist1 term1))) (t nil))) ((equal (ffn-symb term1) (ffn-symb term2)) ; may be lambdas. (equal-mod-alist-lst (fargs term1) alist1 (fargs term2))) (t nil))) (defun equal-mod-alist-lst (term1-lst alist1 term2-lst) (cond ((endp term1-lst) t) (t (and (equal-mod-alist (car term1-lst) alist1 (car term2-lst)) (equal-mod-alist-lst (cdr term1-lst) alist1 (cdr term2-lst)))))) ) (defun member-equal-mod-alist (term1 alist1 term2-lst) (cond ((endp term2-lst) nil) ((equal-mod-alist term1 alist1 (car term2-lst)) t) (t (member-equal-mod-alist term1 alist1 (cdr term2-lst))))) (defun not-to-be-rewrittenp1 (fn lst) ; This function determines whether fn is the ffn-symb of any term on ; lst. We assume lst is a true list of non-variablep non-quotep ; terms. (cond ((null lst) nil) ((equal fn (ffn-symb (car lst))) ; Both may be LAMBDAs. t) (t (not-to-be-rewrittenp1 fn (cdr lst))))) (defun not-to-be-rewrittenp (term alist terms-to-be-ignored-by-rewrite) ; We assume term is a nonvariable non-quotep and that ; terms-to-be-ignored-by-rewrite contains no vars or quoteps. Let ; term' be (sublis-var alist term). If term' is a member of ; terms-to-be-ignored-by-rewrite we return term' else nil. We have ; a faster preliminary check, namely, whether terms-to-be-ignored- ; by-rewrite contains any terms with the same top-level function ; symbol as term. (cond ((not-to-be-rewrittenp1 (ffn-symb term) terms-to-be-ignored-by-rewrite) (member-equal-mod-alist term alist terms-to-be-ignored-by-rewrite)) (t nil))) (defun rewrite-recognizer (recog-tuple arg type-alist ens force-flg wrld ttree pot-lst pt) ; This function returns (mv term' ttree'), where term' is equivalent ; to (fn arg), where fn is the fn field of recog-tuple, and ttree' is ; an extension of ttree that supports whatever was done to reduce (fn ; arg) to term'. (We use ``ttree+'' for ttree' below. Observe that we ; sometimes return ttree+ and other times return ttree.) (mv-let (ts ttree+) (type-set arg force-flg nil type-alist ens wrld ttree pot-lst pt) (cond ((ts-intersectp ts (access recognizer-tuple recog-tuple :true-ts)) (cond ((ts-intersectp ts (access recognizer-tuple recog-tuple :false-ts)) (mv (mcons-term* (access recognizer-tuple recog-tuple :fn) arg) ttree)) (t (mv *t* (push-lemma (access recognizer-tuple recog-tuple :rune) ttree+))))) ; Once upon a time we had: ; ((ts-intersectp ts (access recognizer-tuple recog-tuple :false-ts)) ; (mv *nil* ttree+)) ; (t ; (mv (mcons-term* (access recognizer-tuple recog-tuple :fn) ; arg) ; ttree)) ; here. But we noticed that if the type-set of arg, ts, does not ; intersect true-ts then we know that (not (fn arg)): either (fn arg) ; or (not (fn arg)) and we know the former implies that ts a subset of ; true-ts. Since it is not, the latter must hold. A consequence of ; this insight is that we can see that if ts does not intersect ; true-ts then it MUST intersect false-ts. (t (mv *nil* (push-lemma (access recognizer-tuple recog-tuple :rune) ttree+)))))) ; In a departure from Nqthm, we use a lexicographic order on lists of ; terms for the loop-stopping algorithm. This change was motivated by ; an example in which there were two variables involved in the ; loop-stopper, and one of the corresponding actuals was unchanged. ; Consider for example a rewrite rule like ; (equal ; (variable-update var1 ; val1 (variable-update var2 val2 vs)) ; (variable-update var2 ; val2 (variable-update var1 val1 vs))) ; which has a loop-stopper of ((val1 . val2) (var1 . var2)), and could ; be applied where val1 and val2 are both x but var2 is instantiated ; by a term that precedes the instantiation of var1 in the term-order. ; Nqthm's loop stopper would prevent this application of the rule, but ; the implementation below allows it. (defun remove-invisible-fncalls (term invisible-fns) ; Given a term and a list of unary function symbols considered invisible, ; strip off all the invisible outermost function symbols from the term. (cond ((or (variablep term) (fquotep term) (flambda-applicationp term)) term) ((member-eq (ffn-symb term) invisible-fns) (remove-invisible-fncalls (fargn term 1) invisible-fns)) (t term))) (defun term-order+ (x1 x2 invisible-fns) ; See the doc string for loop-stopper to find an implicit description ; of this function. See the comment below for a proof that this ; function is a total order, provided term-order is a total order. (let ((x1-guts (remove-invisible-fncalls x1 invisible-fns)) (x2-guts (remove-invisible-fncalls x2 invisible-fns))) (cond ((equal x1-guts x2-guts) (term-order x1 x2)) (t (term-order x1-guts x2-guts))))) ; We wish to prove that term-order+ is a total ordering on terms, which, ; recall, means that it is antisymmetric, transitive, and enjoys the trichotomy ; property. However, because term-order+ and its main subroutine, term-order, ; are :program functions we cannot do this directly without reclassifying them. ; In addition, we would first need to prove the lemma that term-order is a ; total ordering. Rather than undertake such a large proof effort, we attack a ; slightly different problem. The basic idea is to constrain the new functions ; xtermp, xterm-order, and xremove-invisible-fncalls to have the properties we ; are willing to assume about the corresponding :program functions. In ; particular, we assume that xterm-order is a total ordering on xtermps and ; that xremove-invisible-fncalls preserves xtermp. Then we define xterm-order+ ; analogously to the definition above of term-order+ and we prove that ; xterm-order+ is a total ordering on xterms. ; Introduce xtermp, xterm-order and xremove-invisible-fncalls by constraint. ; Observe that in the three properties characterizing xterm-order as a total ; ordering we restrict our claims to the cases where only xtermps are involved. ; We also require that xremove-invisible-fncalls preserve xtermp. ; (encapsulate (((xtermp *) => *) ; ((xterm-order * *) => *) ; ((xremove-invisible-fncalls * *) => *)) ; We witness xtermp with rationalp, xterm-order with <= on the rationals, ; and xremove-invisible-fncalls by the identify function. ; (local (defun xtermp (x) (rationalp x))) ; (local (defun xterm-order (x y) ; (and (xtermp x) (xtermp y) (<= x y)))) ; (local (defun xremove-invisible-fncalls (x lst) (declare (ignore lst)) x)) ; Here we establish that xremove-invisible-fncalls preserves xtermp. ; (defthm xtermp-xremove-invisible-fncalls ; (implies (xtermp x) (xtermp (xremove-invisible-fncalls x lst)))) ; We now prove the three total ordering properties. In each case we ; state the property elegantly and then store it as an effective ; rewrite rule. ; (defthm antisymmetry-of-xterm-order ; (implies (and (xtermp x) ; (xtermp y) ; (xterm-order x y) ; (xterm-order y x)) ; (equal x y)) ; ; :rule-classes ; ((:rewrite :corollary ; (implies (and (xtermp x) ; (xtermp y) ; (xterm-order x y) ; (xterm-order y x)) ; (equal (equal x y) t))))) ; ; (defthm transitivity-of-xterm-order ; (implies (and (xtermp x) ; (xtermp y) ; (xtermp z) ; (xterm-order x y) ; (xterm-order y z)) ; (xterm-order x z)) ; ; :rule-classes ; ((:rewrite :corollary ; (implies (and (xtermp x) ; (xterm-order x y) ; (xtermp y) ; (xtermp z) ; (xterm-order y z)) ; (xterm-order x z))))) ; ; (defthm trichotomy-of-xterm-order ; (implies (and (xtermp x) ; (xtermp y)) ; (or (xterm-order x y) (xterm-order y x))) ; ; :rule-classes ; ((:rewrite :corollary ; (implies (and (xtermp x) ; (xtermp y) ; (not (xterm-order x y))) ; (xterm-order y x)))))) ; Introduce the derived order, xterm-order+, that transduces with ; xremove-invisible-fncalls. This is exactly analogous to the definition ; of term-order+ above. ; (defun xterm-order+ (x1 x2 invisible-fns) ; (let ((x1-guts (xremove-invisible-fncalls x1 invisible-fns)) ; (x2-guts (xremove-invisible-fncalls x2 invisible-fns))) ; (cond ; ((equal x1-guts x2-guts) ; (xterm-order x1 x2)) ; (t ; (xterm-order x1-guts x2-guts))))) ; Prove the three properties of xterm-order+, restricted to the xtermp cases. ; (defthm antisymmetry-of-xterm-order+ ; (implies (and (xtermp x) ; (xtermp y) ; (xterm-order+ x y invisible-fns) ; (xterm-order+ y x invisible-fns)) ; (equal x y)) ; :rule-classes nil) ; ; (defthm transitivity-of-xterm-order+ ; (implies (and (xtermp x) ; (xtermp y) ; (xtermp z) ; (xterm-order+ x y invisible-fns) ; (xterm-order+ y z invisible-fns)) ; (xterm-order+ x z invisible-fns))) ; ; (defthm trichotomy-of-xterm-order+ ; (implies (and (xtermp x) ; (xtermp y)) ; (or (xterm-order+ x y invisible-fns) ; (xterm-order+ y x invisible-fns))) ; :rule-classes nil) (defun invisible-fns (fns alist acc) ; Fns is a list of function symbols. Alist is an alist that maps each function ; symbol to a (possibly empty) list of corresponding invisible unary function ; symbols. Acc should be t initially. We return the intersection of the lists ; of invisible functions associated with each function in the list fns. ; We understand "intersection" to mean NIL when intersecting the empty list of ; lists; recall the set-theoretic definition of the intersection of a family of ; sets as containing those elements of the union of that family that belong to ; every set in that family. (declare (xargs :guard (and (symbol-listp fns) (or (true-listp acc) (eq acc t))))) (cond ((null fns) (if (eq acc t) nil acc)) ((eq acc t) (invisible-fns (cdr fns) alist (cdr (assoc-eq (car fns) alist)))) ((null acc) ; This case is a minor optimization that could be omitted. nil) (t (invisible-fns (cdr fns) alist (intersection-eq (cdr (assoc-eq (car fns) alist)) acc))))) (defun loop-stopperp-rec (loop-stopper sbst wrld) ; Only call this at the top level when loop-stopper is non-nil. (cond ((null loop-stopper) nil) (t (let ((pre (cdr (assoc-eq (car (car loop-stopper)) sbst))) (post (cdr (assoc-eq (cadr (car loop-stopper)) sbst)))) (cond ((equal pre post) (loop-stopperp-rec (cdr loop-stopper) sbst wrld)) (t (term-order+ post pre (invisible-fns (cddr (car loop-stopper)) (invisible-fns-table wrld) t)))))))) (defun loop-stopperp (loop-stopper sbst wrld) (or (null loop-stopper) (loop-stopperp-rec loop-stopper sbst wrld))) (defrec rewrite-rule (rune nume hyps equiv lhs rhs subclass heuristic-info ; The backchain-limit-lst must be nil, a natp, or a list of these of the same ; length as hyps. For subclass 'meta, only the first two of these is legal. ; Otherwise, only the first and third of these are legal. backchain-limit-lst ; For subclass 'backchain or 'abbreviation, var-info is t or nil according to ; whether or not there are free variables on the left-hand side of the rule. ; For subclass 'definition, var-info is a list that positionally associates ; each argument of lhs with the number of its occurrences in rhs. Var-info is ; ignored for subclass 'meta. var-info . ; The match-free field should be :all or :once if there are free variables in ; the hypotheses, else nil. match-free) nil) ; There are four subclasses of rewrite rule, distinguished by the :subclass slot. ; 'backchain - the traditional rewrite rule. In this case, :heuristic-info is ; the loop-stopper for the rule: a list of elements of the form (x y . fns), ; indicating that in replacing lhs by rhs (the binding of) y moves forward to ; the spot occupied by (the binding of) x, and that x and y only appear on ; the left-hand side as arguments to functions in fns. Thus, to prevent ; loops we adopt the heuristic convention of replacing lhs by rhs only if ; each y is smaller than the corresponding x, with respect to functions that ; are considered "invisible" if they are invisible with respect to every ; function in fns. ; 'abbreviation - the special case where there are no hyps, a nil loop-stopper, ; and the rhs satisfies the abbreviationp predicate. Heuristic-info is ; irrelevant here. Non-recursive definitions whose bodies are abbreviationps ; are stored this way rather than as :subclass 'definition. ; 'meta - a rule justified by a metatheorem. In this case, the lhs is the ; the metafunction symbol to be applied, and hyps is a function of one (term) ; argument that generates a hypothesis for the metatheorem. ; Rockwell Addition: The recursivep property used to be the fn name if the ; fn in question was singly recursive. Now it is a singleton list (fn). ; 'definition - a rule implementing a non-abbreviational definitional equation. ; In this case :heuristic-info is the pair (recursivep . controller-alist) ; where recursivep is nil (if this is a nonrec definition) or a truelist of ; symbols naming all the fns in the ``clique'' (singly recursive functions have ; a singleton list as their recursivep property); and controller-alist is an ; alist pairing each fn named in recursivep to a mask of t's and nil's in 1:1 ; correspondence with the formals of the fn and indicating with t's which ; arguments control the recursion for this definition. (defun relevant-ground-lemmas (hyp wrld) (mv-let (not-flg hyp) (strip-not hyp) (declare (ignore not-flg)) (cond ((variablep hyp) nil) ((fquotep hyp) nil) ((flambda-applicationp hyp) nil) (t (getprop (ffn-symb hyp) 'lemmas nil 'current-acl2-world wrld))))) (defun search-ground-units1 (hyp unify-subst lemmas type-alist ens force-flg wrld ttree) (cond ((null lemmas) (mv nil unify-subst ttree nil)) ((and (enabled-numep (access rewrite-rule (car lemmas) :nume) ens) (not (eq (access rewrite-rule (car lemmas) :subclass) 'meta)) (null (access rewrite-rule (car lemmas) :hyps)) (not (access rewrite-rule (car lemmas) :var-info)) (geneqv-refinementp (access rewrite-rule (car lemmas) :equiv) *geneqv-iff* wrld)) ; The tests above select enabled, non-meta, unconditional lemmas of ; the form (equiv lhs rhs), where equiv is a refinement of iff and lhs ; has no variables in it. We do not know that rhs has no variables in ; it, but if it does, they can clearly be instantiated to whatever we ; wish and we will act as though they are instantiated with the ; corresponding variables of our current problem. We now want to know ; if rhs is non-nil. If it is, this lemma may be a way to establish ; hyp. (mv-let (knownp nilp nilp-ttree) (known-whether-nil (access rewrite-rule (car lemmas) :rhs) type-alist ens force-flg nil ; dwp wrld ttree) ; Observe that nilp-ttree extends ttree. We may use either, depending on ; how things work out. (cond ((and knownp (not nilp)) (mv-let (ans unify-subst) (one-way-unify1 hyp (access rewrite-rule (car lemmas) :lhs) unify-subst) (cond (ans (mv t unify-subst (push-lemma (geneqv-refinementp (access rewrite-rule (car lemmas) :equiv) *geneqv-iff* wrld) (push-lemma (access rewrite-rule (car lemmas) :rune) nilp-ttree)) (cdr lemmas))) (t (search-ground-units1 hyp unify-subst (cdr lemmas) type-alist ens force-flg wrld ttree))))) (t (search-ground-units1 hyp unify-subst (cdr lemmas) type-alist ens force-flg wrld ttree))))) (t (search-ground-units1 hyp unify-subst (cdr lemmas) type-alist ens force-flg wrld ttree)))) (defun search-ground-units (hyp unify-subst type-alist ens force-flg wrld ttree) ; This function is like lookup-hyp except we search through the ground unit ; rewrite lemmas. We are a No-Change Loser with three values: the win flag, ; the new unify-subst, and a new ttree. (let ((lemmas (relevant-ground-lemmas hyp wrld))) (mv-let (winp unify-subst ttree rest-lemmas) (search-ground-units1 hyp unify-subst lemmas type-alist ens force-flg wrld ttree) (declare (ignore rest-lemmas)) (mv winp unify-subst ttree)))) (defun if-tautologyp (term) (declare (xargs :guard (pseudo-termp term))) ; This function returns T or NIL according to whether TERM is or is ; not an if-tautologyp. A term is an if-tautology provided that under ; all (a) assignments of functions to the non-IF function symbols in ; the term and (b) assignments of objects to the variables in the ; term, the value of the term, (using the usual interpretation of IF ; and QUOTE and any Boolean commutative interpretations for EQUAL and ; IFF) is non-NIL. Every if-tautology is true, but one cannot conclude ; from the fact that a term is not an if-tautologyp that it is not ; true! Note that we do not attach any ``semantics'' to the built-ins ; besides IF, QUOTEd objects, and the little we know about EQUAL and ; IFF. For example, (IF (EQUAL A B) (EQUAL B A) 'T) is an ; if-tautology, but (IF (equiv A B) (equiv B A) 'T) for any symbol ; equiv other than EQUAL and IFF is not. (posp (if-interp (splice-instrs (if-compile term t nil nil)) nil nil nil nil ; The choice of 100000 below is rather arbitrary, determined by ; experimentation. It is the limit for the number of if-interp steps. It is ; probably fair to view this limit as a hack, but after all, Boolean ; decidability is NP-hard. 100000))) (mutual-recursion ; Warning: For both functions in this nest, fns should be a subset of ; *definition-minimal-theory*. See the error related to ; *definition-minimal-theory* in chk-acceptable-definition-install-body. (defun expand-some-non-rec-fns (fns term wrld) ; We forcibly expand all calls in term of the fns in fns. They better ; all be non-recursive or this may take a while. (cond ((variablep term) term) ((fquotep term) term) (t (let ((args (expand-some-non-rec-fns-lst fns (fargs term) wrld))) (cond ((member-equal (ffn-symb term) fns) (subcor-var (formals (ffn-symb term) wrld) args (body (ffn-symb term) t wrld))) (t (cons-term (ffn-symb term) args))))))) (defun expand-some-non-rec-fns-lst (fns lst wrld) (cond ((null lst) nil) (t (cons (expand-some-non-rec-fns fns (car lst) wrld) (expand-some-non-rec-fns-lst fns (cdr lst) wrld))))) ) (defun tautologyp (term wrld) ; If this function returns t, then term is a theorem. With the intended ; application in mind, namely the recognition of "trivial corollaries" while ; processing rule classes, we check for the "most common" tautology, (implies p ; p). Otherwise, we expand certain non-recursive fns and see if the result is ; an if-tautology. This function can be made as fancy as you want, as long as ; it recognizes theorems. (cond ((and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'implies) (equal (fargn term 1) (fargn term 2))) t) (t (if-tautologyp (expand-some-non-rec-fns ; The list of functions expanded is arbitrary, but they must all be ; non-recursively defined. Guards are permitted but of course it is the ; guarded body that we substitute. The IF tautology checker doesn't know ; anything about any function symbol besides IF and NOT (and QUOTEd constants). ; The list below pretty obviously has to include IMPLIES and IFF. It should ; not include NOT. ; The list is in fact *expandable-boot-strap-non-rec-fns* with NOT deleted and ; IFF added. The main idea here is to include non-rec functions that users ; typically put into the elegant statements of theorems. If functions are ; added to this list, consider changing the quoted constant in ; expand-abbreviations and, if the functions are not also added to ; *expandable-boot-strap-non-rec-fns*, the constant ; *definition-minimal-theory*, used in translate-in-theory-hint. Consider also ; preprocess-clause and the error pertaining to *definition-minimal-theory* in ; chk-acceptable-definition-install-body. '(iff ;not implies eq atom eql = /= null ; If we ever make 1+ and 1- functions again, they should go back on this list. zerop synp return-last plusp minusp listp mv-list wormhole-eval force case-split double-rewrite) term wrld))))) (defun make-true-list-cons-nest (term-lst) (cond ((null term-lst) *nil*) (t (cons-term 'cons (list (car term-lst) (make-true-list-cons-nest (cdr term-lst))))))) ; Rockwell Addition: The reason we changed the recursivep property is ; that we frequently ask whether there is a recursive fn on the ; fnstack and now we don't have to go to the property list to answer. ; Read the comment below. (defun being-openedp-rec (fn fnstack) ; The fnstack used by the rewriter is a list. Each element is a ; function symbol, a list of function symbols, or of the form (:term ; . term) for some term, term. The first case means we are expanding ; a definition of that symbol and the symbol is non-recursively ; defined. The second means we are expanding a singly or mutually ; recursive function. (In fact, the fnstack element is the recursivep ; flag of the function we're expanding.) The third means that we are ; rewriting the indicated term (through the recursive dive in the ; rewriter that rewrites the just-rewritten term). Lambda-expressions ; are not pushed onto the fnstack, though fn may be a ; lambda-expression. We determine whether fn is on fnstack (including ; being a member of a mutually recursive clique). (cond ((null fnstack) nil) ((consp (car fnstack)) (or (eq fn (caar fnstack)) ; and hence (not (eq (caar fnstack) :term)) (being-openedp-rec fn (cdr fnstack)))) (t (or (eq fn (car fnstack)) (being-openedp-rec fn (cdr fnstack)))))) (defmacro being-openedp (fn fnstack clique) ; We found a 1.8% slowdown when we modified the code, in a preliminary cut at ; Version_2.7, to improve the speed of being-openedp when large cliques are on ; the fnstack by looking up the representative of fn on the fnstack, rather ; than looking up fn itself. Presumably that slowdown resulted from the new ; calls to getprop to get the 'recursivep property (back when we used it for ; this purpose, through Version_2.9.4). Here we avoid computing that getprop ; (in the case that clique is a getprop expression) in a case we suspect is ; pretty common: fnstack is empty. The fnstack argument will always be a ; symbolp expression, so we do not need to let-bind it below. (declare (xargs :guard (symbolp fnstack))) `(and ,fnstack (let ((clique ,clique)) (being-openedp-rec (if clique (car clique) ,fn) ,fnstack)))) (defun recursive-fn-on-fnstackp (fnstack) ; We return t iff there is an element of fnstack that is recursively ; defined. We assume that any mutually recursive clique on the stack ; is truly indicative of mutual recursion. See the description of the ; fnstack in being-openedp. (cond ((null fnstack) nil) ((and (consp (car fnstack)) (not (eq (caar fnstack) :term))) t) (t (recursive-fn-on-fnstackp (cdr fnstack))))) (defun fnstack-term-member (term fnstack) ; If we are not careful, the call (rewrite rewritten-body ...) in ; rewrite-fncall can cause an infinite loop. Here we describe a mechanism for ; avoiding such loops. This mechanism is enforced by the call to ; fnstack-term-member in rewrite-fncall, which must return nil before opening ; up a function call. ; The problem is the interaction between opening up function definitions and ; use of equalities on the type-alist. Suppose that (foo x) is defined to be ; (bar (foo (cdr x))) in a certain case. But imagine that on the type-alist we ; have (foo (cdr x)) = (foo x). Then rewritten-body, here, is (bar (foo x)). ; Because it contains a rewriteable call we rewrite it again. If we do so with ; the old fnstack, we will open (foo x) to (bar (foo x)) again and infinitely ; regress. ; The following event list illustrates the problem we wish to avoid. ; (defun bar (x) (declare (ignore x)) 7) ; (in-theory (disable bar)) ; (defun foo (x) ; (if (consp x) (bar (foo (cdr x))) t)) ; :brr t ; :monitor (:definition foo) t ; (thm (implies (and (consp x) (equal (foo x) (foo uuu))) (not (equal (foo (cdr x)) (foo x))))) ; :eval ; :eval ; :eval ; ... ; Doing a :path after the :evals shows an infinite regress rewriting (foo x). ; The problem is that lit 3 is on the type-alist and causes (foo (cdr x)) to ; rewrite to (foo x). Thus, when (foo x) in lit 2 is rewritten it first goes ; to (bar (foo (cdr x))) and thence to (bar (foo x)). ; This same loop occurs in Nqthm, though it has never been fired in anger, as ; far as we know. ; In Version 2.5 and before we handled this rare loop in a very non-rugged way, ; using fnstack unchanged in the aforementioned recursive call (rewrite ; rewritten-body ...): If the term we're expanding reoccurs in the rewritten ; body, we won't rewrite the rewritten body. In that approach, if we're ; expanding (foo x a) and it rewrites to (bar (foo (cdr x) a)) and thence to ; (bar (foo x a)), we'll break the loop. BUT if it goes instead to (bar (foo x ; a')), we'll just naively go around the loop. ; Starting with Version_2.6, we extended fnstack with (:term . term) in that ; recursive call to rewrite. Through Version_2.8, before making that recursive ; call we first checked the fnstack to see if an entry (:term . x) was already ; there for some subterm x of rewritten-body. This was the only place that we ; paid attention to elements of fnstack of the form (:term . x). ; Starting with Version_2.9, we do a simpler check for membership of (:term ; . term) in the fnstack. (The present function implements that membership ; check without the need to cons up (:term . term).) The unique such check is ; done where it makes the most sense: just before we open up a function call in ; rewrite-fncall. ; Here is an example based on a script sent by Andrew Feist that causes an ; infinite loop in Version 2.5 but not in Version 2.6 (but using :dir :system ; as introduced in 2.8). ; (include-book "arithmetic/top-with-meta" :dir :system) ; ; (defun a (x) ; (cond ; ((not (integerp x)) nil) ; ((< x 1) nil) ; ((= x 1) 1) ; ((= x 2) 2) ; ((= x 3) 24) ; (t (/ (- (* 6 (expt (a (1- x)) 2) (a (- x 3))) ; (* 8 (a (1- x)) (expt (a (- x 2)) 2))) ; (* (a (- x 2)) (a (- x 3))))))) ; ; (defun e (x) ; product from i=1 to x-1 of 2^i - 1 ; (if (not (integerp x)) ; 0 ; (if (< x 2) ; 1 ; (* (+ (expt 2 x) (- 1)) (e (1- x)))))) ; ; (defun d (x) ; (cond ; ((not (integerp x)) nil) ; ((< x 1) nil) ; (t (* (expt 2 (/ (* x (1- x)) 2)) (e (1- x)))))) ; ; ; Added to Andrew's script: ; (in-theory (disable exponents-add)) ; ; (defthm lemma-a-is-d ; doesn't prove, but at least it avoids the loop ; (= (a x) (d x))) ; We can execute the following trace forms if in GCL, in which case we should see ; the trace output shown below in Version 2.5 and before. ; (trace (rewrite-fncall ; :cond (eq (cadr (access rewrite-rule (car si::arglist) :rune)) 'expt) ; :entry (list (cadr si::arglist) (nth 7 si::arglist)) ; :exit (car si::values))) ; (trace (rewrite ; :entry (list (car si::arglist) (nth 8 si::arglist)) ; :exit (car si::values))) ; ; 114> (REWRITE-FNCALL (EXPT '2 (BINARY-+ '-2 X)) ; (E))> ; 115> (REWRITE ; (IF (ZIP I) ; '1 ; (IF (EQUAL (FIX R) '0) ; '0 ; (IF (< '0 I) ; (BINARY-* R (EXPT R (BINARY-+ I '-1))) ; (BINARY-* (UNARY-/ R) ; (EXPT R (BINARY-+ I '1)))))) ; (EXPT E))> ; ............................... ; 120> (REWRITE-FNCALL (EXPT '2 (BINARY-+ '-1 X)) ; (EXPT E))> ; <120 (REWRITE-FNCALL EXPT '2 ; (BINARY-+ '-1 X))> ; ............................... ; <115 (REWRITE BINARY-* '1/2 ; (EXPT '2 (BINARY-+ '-1 X)))> ; 115> (REWRITE (BINARY-* '1/2 ; (EXPT '2 (BINARY-+ '-1 X))) ; (E))> ; [never returns from this final 115, hence never returns from 114] ; But our solution at that point (described above for Version_2.6) did not ; prevent an infinite loop in Version_2.8 for the following example, sent by ; Fares Fraij. ; (defun get-constant (n classfile) ; (let ((temp (assoc n classfile))) ; (cond ((null temp) nil) ; ((stringp (cadr temp)) (cadr temp)) ; ((or (not (natp n)) ; (not (natp (cadr temp))) ; (<= n (cadr temp))) ; nil) ; (t (get-constant (cadr temp) classfile))))) ; (defun get-constant-path (n classfile) ; (let ((temp (assoc n classfile))) ; (cond ((null temp) nil) ; (t (if (or (stringp (cadr temp)) ; (not (natp n)) ; (not (natp (cadr temp))) ; (<= n (cadr temp))) ; (list n) ; (cons n (get-constant-path (cadr temp) classfile))))))) ; (defthm member-position-path-get-constant-n-1 ; (implies (member position (get-constant-path n classfile)) ; (equal (get-constant n classfile) ; (get-constant position classfile)))) ; The final defthm above caused an infinite loop. The fnstack had plenty of ; copies of (:TERM GET-CONSTANT N CLASSFILE), yet the loop was caused by ; repeated opening up of (GET-CONSTANT N CLASSFILE)! How could this happen? ; The rewritten-body was (GET-CONSTANT POSITION CLASSFILE), so our test for ; membership in fnstack returned nil, and we went ahead and rewrote the ; rewritten-body. That rewrite was in a context where POSITION is known to ; equal N, so POSITION rewrote to N, and we found ourselves with a new call of ; (GET-CONSTANT N CLASSFILE). ; So now we do the fnstack check for (:term . term) even before opening up the ; function call. (cond ((null fnstack) nil) ((and (consp (car fnstack)) (eq (caar fnstack) :term) (equal (cdar fnstack) term)) t) (t (fnstack-term-member term (cdr fnstack))))) ; Essay on Too-many-ifs ; The discussion below applies to a long-standing "too-many-ifs" heuristic that ; is used only for nonrecursive function applications when no recursive ; function application is on the stack. Up through Version_3.6.1, we always ; rewrote the body of nonrecursive function calls and then applied this ; heuristic. After Version_3.6.1, we modified this heuristic to avoid ; rewriting the bodies of some such calls, by calling a version of the function ; first on unrewritten bodies and then, possibly again, after rewriting. This ; gives rise to two functions, too-many-ifs-pre-rewrite and ; too-many-ifs-post-rewrite. ; Let args be the list of actuals to a nonrec fn. We wish to determine whether ; the expansion of the fn call introduces too many IFs all at once into the ; rewritten body of fn. Our motivation comes from an example like (M2 (ZTAK & ; & &) (ZTAK & & &) (ZTAK & & &)) where the careless opening up of everybody ; produces a formula with several hundred IFs in it because of M2's duplication ; of the IFs coming from the simplification of the ZTAKs. An early thought was ; never to expand a nonrec fn -- at the top level of the clause -- if it had ; some IFs in its args and to wait till CLAUSIFY has cleaned things up. That ; slowed a proveall down by a factor of 2 -- and by a factor of 13 in ; PRIME-LIST-TIMES-LIST -- because of the ridiculously slow expansion of such ; basic nonrec fns as AND, OR, NOT, and NLISTP. ; This heuristic originally took ARGS and the rewritten right-hand side of fn, ; VAL, and computed something like ; (> (ITERATE FOR ARG IN ARGS SUM (* (COUNT-IFS ARG) (OCCUR-CNT ARG VAL))) ; (ITERATE FOR ARG IN ARGS SUM (COUNT-IFS ARG))) ; where the OCCUR-CNT counted the number of times ARG occured in VAL. The ; heuristic was slightly optimized by observing that if no IFs occur in any arg ; then there is no point in doing the OCCUR-CNTs and that once the left hand ; side has been pushed beyond the right there is no point in continuing. (We ; say "something like" because the code, at least as of Version_3.6.1, ; double-counted an ARG when it was a subterm of some other arg in ARGS.) ; However, when Sol Swords profiled some book certification typically done at ; Centaur, his results suggested that nearly half of the rewriting and 15% of ; the total time (where 45% of the total time seemed to be in include-book-fn) ; was spent in too-many-ifs. It turns out that we can save most of the ; too-many-ifs time by doing a preliminary check, before rewriting the ; right-hand-side, to see if it is "expected" (in some very inexact sense) that ; the right-hand-side would have too-many-ifs. The function ; too-many-ifs-pre-rewrite does this check using the unrewritten body, which ; not only saves potential rewriting but also can be faster because the unrewritten ; body is often much smaller than the rewritten body. ; At one point we avoided too-many-ifs-post-rewrite entirely, which pushed our ; savings above 20%. But we had failures in the regression suite: ; collect-times-1d in books/arithmetic-2/meta/common-meta.lisp and ; sum-pp4-reduce-to in books/rtl/rel7/support/lib1.delta1/mult-proofs.lisp. In ; these cases, the proof failed because the new heuristic stopped fix from ; opening up, while the original heuristic allowed (fix x) to open up for the ; particular x at hand because (acl2-numberp x) simplified to t. We solved ; that problem: at first we made an exception for fix, but now we simply ; ignored occurrences in test positions of calls of IF when counting argument ; occurrences in right-hand-sides of definition rules (see var-counts). ; Lemma make-shared-variables-dag-as-term-l-lemma in community book ; books/defexec/dag-unification/terms-as-dag.lisp is a good test case: it ; proves using the old heuristic but seems difficult to prove using the new ; heuristic (too-many-ifs-pre-rewrite) alone. It is also notable in that if ; memory serves, the new heuristic specifically fails on lambdas. We are ; pretty happy with our current implementation, which is a compromise: Use ; too-many-ifs-pre-rewrite to avoid opening up the right-hand side of a ; definition at all in some cases, but even if we do open it up, use ; too-many-ifs-post-rewrite to apply the old too-many-ifs heuristic. (mutual-recursion (defun var-counts1 (arg rhs acc) ; See the comment in var-counts. (declare (xargs :guard (and (pseudo-termp rhs) (natp acc)) :verify-guards nil)) (cond ((equal arg rhs) (1+ acc)) ((variablep rhs) acc) ((fquotep rhs) acc) ((eq (ffn-symb rhs) 'if) (max (var-counts1 arg (fargn rhs 2) acc) (var-counts1 arg (fargn rhs 3) acc))) (t (var-counts1-lst arg (fargs rhs) acc)))) (defun var-counts1-lst (arg lst acc) (declare (xargs :guard (and (pseudo-term-listp lst) (natp acc)))) (cond ((endp lst) acc) (t (var-counts1-lst arg (cdr lst) (var-counts1 arg (car lst) acc))))) ) (defun var-counts (lhs-args rhs) ; Return a list of natural numbers that corresponds positionally to lhs-args, ; where the nth element of the returned list is an approximation to the number ; of occurrences of the nth element of lhs-args in rhs. Normally lhs-args will ; be a list of variables -- hence the name -- though it can be the arguments to ; any call on the left-hand side of a definition rule. ; More precisely, the return value is used in the too-many-ifs-pre-rewrite ; heuristic, as a list of possible occurrences of each arg (formal) in the rhs ; of a given definition. Larger elements of var-counts make it more likely ; that the given definition will not be opened up (or if it is, then that it ; will be closed back up again). ; Our algorithm ignores occurrences of elements of lhs-args in test positions ; of calls of IF, and for such calls, it takes maxima for the true and false ; branches; see var-counts1. These decisions are merely heuristic, and might ; benefit from further experimentation, though we are pretty happy with current ; performance based on tests to date. But our decisions deserve some remarks: ; Note that the var-counts are used before attempting to rewrite the rhs. If ; we wished, var-counts could return a trivial result consisting of a list of ; zeroes from var-counts; as a result we will always rewrite the rhs. But we ; want to short-circuit that rewrite when it seems reasonable to do so, such as ; when we have pretty good reason to believe that the too-many-ifs heuristic ; used _after_ rewriting would reject opening up the definition anyhow. ; For us to have good reason, we should be careful not to have the returned ; var-counts be too large, which could make it too easy to reject the ; opening-up. For this reason, we ignore occurrences in test positions of ; calls of IF, since we can imagine those may disappear after the instantiated ; rhs is simplified. But we don't want the var-counts to be too small, since ; then we might miss opportunities for efficiencies in early termination. We ; might for example get all zeroes if we always take the minimum of var-counts ; in the two branches of any IF call, since it could often be the case that a ; formal parameter only occurs in one of the two branches. ; So, we take the maximum of two branches of any IF call. In an early ; experiment we had good results taking the sum rather than the maximum: only a ; couple of proofs failed during ACL2 regression, and we got a 20% speed-up on ; a test provided by Sol Swords on certification done at Centaur. But the sum ; is too large if we really imagine the IF tests simplifying away, so we take ; the maximum as a sort of compromise between the sum and the minimum (which ; could easily be too small, as explained above). (declare (xargs :guard (and (true-listp lhs-args) (pseudo-termp rhs)))) (cond ((endp lhs-args) nil) (t (cons (var-counts1 (car lhs-args) rhs 0) (var-counts (cdr lhs-args) rhs))))) (mutual-recursion (defun count-ifs (term) (declare (xargs :guard (pseudo-termp term))) (cond ((variablep term) 0) ((fquotep term) 0) ((eq (ffn-symb term) 'hide) 0) ((eq (ffn-symb term) 'if) (+ 1 (count-ifs (fargn term 1)) (count-ifs (fargn term 2)) (count-ifs (fargn term 3)))) (t (count-ifs-lst (fargs term))))) (defun count-ifs-lst (lst) (declare (xargs :guard (pseudo-term-listp lst))) (cond ((endp lst) 0) (t (+ (count-ifs (car lst)) (count-ifs-lst (cdr lst)))))) ) ; We originally defined nat-listp here and used it in the guards of ; too-many-ifs0 and too-many-ifs-pre-rewrite, but several community books had ; conflicts with this definition of nat-listp, as follows: ; workshops/2004/ruiz-et-al/support/terms-as-dag.lisp ; workshops/2003/sumners/support/n2n.lisp ; workshops/2009/kaufmann-kornerup-reitblatt/support/preliminaries.lisp ; concurrent-programs/bakery/measures.lisp ; unicode/nat-listp.lisp ; defexec/dag-unification/terms-as-dag.lisp ; So we have commented out this definition. If we decide to use it after all, ; change integer-listp to nat-listp in the two guards mentioned above and also ; in community book books/system/too-many-ifs.lisp, as indicated there. ; (defun nat-listp (x) ; (declare (xargs :guard t)) ; (cond ((atom x) ; (equal x nil)) ; (t (and (natp (car x)) ; (nat-listp (cdr x)))))) (defun too-many-ifs0 (args counts diff ctx) ; See also too-many-ifs-pre-rewrite. ; Diff is (- dot-product count-ifs), where count-ifs is the sum of the ; count-ifs of the args already processed and dot-product is the dot-product of ; the vector of those count-ifs and the counts already processed. (declare (type (signed-byte 30) diff) (xargs :guard (and (pseudo-term-listp args) (integer-listp counts) (equal (len args) (len counts))))) (cond ((endp args) (> diff 0)) ((eql (car counts) 1) ; Then (count-ifs (car args)) will contribute nothing to diff. (too-many-ifs0 (cdr args) (cdr counts) diff ctx)) (t (let ((count1 (the-fixnum! (count-ifs (car args)) ctx))) (declare (type (unsigned-byte 29) count1)) (too-many-ifs0 (cdr args) (cdr counts) (the-fixnum! (+ (the-fixnum! (* count1 (1- (car counts))) ctx) diff) ctx) ctx))))) (defproxy too-many-ifs-pre-rewrite (* *) => *) (defun too-many-ifs-pre-rewrite-builtin (args counts) ; See the Essay on Too-many-ifs. ; Args is the left-hand-side of a definition rule, hence most commonly the ; formal parameters of some function. Counts is a list that corresponds ; positionally to args, and represents the number of occurrences of each ; element of args in the right-hand-side of the implicit definition rule. ; (For details on how counts is computed, see var-counts.) (declare (xargs :guard (and (pseudo-term-listp args) (integer-listp counts) (equal (len args) (len counts))))) (too-many-ifs0 args counts 0 'too-many-ifs-pre-rewrite)) (defattach (too-many-ifs-pre-rewrite too-many-ifs-pre-rewrite-builtin) :skip-checks t) ; This dead code could be deleted, but we leave it as documentation for ; occur-cnt-bounded. ; (mutual-recursion ; ; (defun occur-cnt-rec (term1 term2 acc) ; ; ; Return a lower bound on the number of times term1 occurs in term2. ; ; We do not go inside of quotes. ; ; (cond ((equal term1 term2) (1+ acc)) ; ((variablep term2) acc) ; ((fquotep term2) acc) ; (t (occur-cnt-lst term1 (fargs term2) acc)))) ; ; (defun occur-cnt-lst (term1 lst acc) ; (cond ((null lst) acc) ; (t (occur-cnt-rec term1 ; (car lst) ; (occur-cnt-lst term1 (cdr lst) acc))))) ; ) ; ; (defun occur-cnt (term1 term2) ; (occur-cnt-rec term1 term2 0)) (mutual-recursion (defun occur-cnt-bounded (term1 term2 a m bound-m) ; Let bound = (+ m bound-m). Return (+ a (* m (occur-cnt term1 term2))) unless ; it exceeds bound, in which case return -1. We assume (<= a bound). ; Occur-cnt is no longer defined, but was defined (as is this function) so as ; not to go inside of quotes, returning a lower bound on the number of times ; term1 occurs in term2. (declare (type (signed-byte 30) a m bound-m) (xargs :measure (acl2-count term2) :ruler-extenders (:lambdas) :guard (and (pseudo-termp term2) (signed-byte-p 30 (+ bound-m m)) (<= 0 a) (<= 0 m) (<= 0 bound-m) (<= a (+ bound-m m))) :verify-guards nil)) (the-fixnum (cond ((equal term1 term2) (if (<= a bound-m) (the-fixnum (+ a m)) -1)) ((variablep term2) a) ((fquotep term2) a) (t (occur-cnt-bounded-lst term1 (fargs term2) a m bound-m))))) (defun occur-cnt-bounded-lst (term1 lst a m bound-m) (declare (type (signed-byte 30) a m bound-m) (xargs :measure (acl2-count lst) :ruler-extenders (:lambdas) :guard (and (pseudo-term-listp lst) (signed-byte-p 30 (+ bound-m m)) (<= 0 a) (<= 0 m) (<= 0 bound-m) (<= a (+ bound-m m))))) (the-fixnum (cond ((endp lst) a) (t (let ((new (occur-cnt-bounded term1 (car lst) a m bound-m))) (declare (type (signed-byte 30) new)) (if (eql new -1) -1 (occur-cnt-bounded-lst term1 (cdr lst) new m bound-m))))))) ) (defun too-many-ifs1 (args val lhs rhs ctx) ; See also too-many-ifs-post-rewrite-builtin. ; We assume (<= lhs rhs). (declare (type (signed-byte 30) lhs rhs) (xargs :guard (and (pseudo-term-listp args) (pseudo-termp val) (<= 0 lhs) (<= lhs rhs) (<= (count-ifs-lst args) rhs)))) (cond ((endp args) nil) (t (let ((x (the-fixnum! (count-ifs (car args)) ctx))) (declare (type (signed-byte 30) x)) (cond ((eql x 0) (too-many-ifs1 (cdr args) val lhs rhs ctx)) (t (let ((lhs (occur-cnt-bounded (car args) val lhs x (the-fixnum (- rhs x))))) (declare (type (signed-byte 30) lhs)) (if (eql lhs -1) -1 (too-many-ifs1 (cdr args) val lhs rhs ctx))))))))) (defproxy too-many-ifs-post-rewrite (* *) => *) (defun too-many-ifs-post-rewrite-builtin (args val) ; This function implements the part of the too-many-ifs heuristic after the ; right-hand-side of a definition has been rewritten, to see if that expansion ; is to be kept or thrown away. See the Essay on Too-many-ifs. (declare (xargs :guard (and (pseudo-term-listp args) (pseudo-termp val)))) (let* ((ctx 'too-many-ifs-post-rewrite-builtin) (rhs (the-fixnum! (count-ifs-lst args) ctx))) (cond ((int= rhs 0) nil) (t (too-many-ifs1 args val 0 rhs ctx))))) (defattach (too-many-ifs-post-rewrite too-many-ifs-post-rewrite-builtin) :skip-checks t) (defun all-args-occur-in-top-clausep (args top-clause) (cond ((null args) t) (t (and (dumb-occur-lst (car args) top-clause) (all-args-occur-in-top-clausep (cdr args) top-clause))))) (defun cons-count-bounded-ac (x i) ; We accumulate into i the number of conses in x, bounding our result by ; (fn-count-evg-max-val), which should not be less than i. We choose ; (fn-count-evg-max-val) as our bound simply because that bound is used in the ; similar computation of fn-count-evg. (declare (type (signed-byte 30) i)) (the (signed-byte 30) (cond ((atom x) i) (t (let ((i (cons-count-bounded-ac (cdr x) i))) (declare (type (signed-byte 30) i)) (cond ((>= i (fn-count-evg-max-val)) (fn-count-evg-max-val)) (t (cons-count-bounded-ac (car x) (1+f i))))))))) (defun cons-count-bounded (x) (the (signed-byte 30) (cons-count-bounded-ac x 0))) (mutual-recursion (defun max-form-count (term) ; This function is used in the control of recursive fn expansion. Many years ; ago, we used the fn count part of var-fn-count in this role. Then we decided ; that for controlling expansion we should not count (IF x y z) to have size ; 1+|x|+|y|+|z| because the IF will be distributed and the y or the z will rest ; in the argument position of the recursive call. So we started to compute the ; maximum fn count in the branches. Then we added explicit values (this really ; was years ago!) and decided not to consider 1000 to be better than 999, since ; otherwise (< x 1000) would open. So we measure quoted constants by their ; Lisp size. ; But with the advent of the HONS version of ACL2, our concern mounted about ; the ability of ACL2 to handle very large ("galactic") objects. Consider the ; following example, which caused ACL2 Version_3.4 to hang. ; (defun big (n) ; (cond ((posp n) (let ((x (big (1- n)))) ; (cons x x))) ; (t nil))) ; ; (defun foo (x) (if (consp x) (foo (cdr x)) x)) ; ; (set-gag-mode nil) ; (set-inhibit-output-lst '(prove proof-tree summary)) ; ; (thm (consp (foo (big 50))) ; :hints (("Goal" ; :in-theory ; (disable (foo) (:type-prescription foo))))) ; ; Our solution is to bound the computation of size of explicit values, unlike ; the unbounded computation done through ACL2 Version_3.4. There, we used a ; function, cons-count, that ignored the sizes of numeric explicit values, ; counting only conses. ; But just how should we bound the size computation for explicit values? ; It seems odd that the existing approach only counted conses, since there ; seems to be no obvious reason to treat the number of conses in a list ; differently from the number of (implicit) successor calls in a natural ; number. Our first change was to ignore completely the sizes of explicit ; values, returning 0 in the fquotep case below. Unfortunately, we then ; observed a failure in the event (verify-guards meta-integerp ...) in ; community book books/arithmetic-3/bind-free/integerp-meta.lisp. We have ; extracted the following from that failure: This succeeded when using ; (cons-count (cadr term)) in the case (fquotep term) below, but not when using ; 0 in that case instead. ; (thm (IMPLIES ; (AND (PSEUDO-TERM-LISTP (CDR TERM)) ; (MEMBER-EQ (CAADR TERM) ; '(BINARY-+ BINARY-*))) ; (PSEUDO-TERM-LISTP (LEAVES (CADDAR (CDR TERM)) ; (CAADR TERM))))) ; Our first fix was simply to count size of explicit values just as we do in ; some other places, using fn-count-evg in the fquotep case. Unfortunately we ; got a failure in (verify-guards subtract-bag ...) in the same file as above, ; apparently because (mv-nth 1 x) now opens up to (cadr x). ; So for backward compatibility we now define a bounded version of cons-count. ; Notice that our bounded size computation can cause the "wrong" term to be ; viewed as the smaller, so we need to be confident that this is not a problem, ; and indeed it is not when we call max-form-count in smallest-common-subterms. (the (signed-byte 30) (cond ((variablep term) 0) ((fquotep term) (cons-count-bounded (cadr term))) ((eq (ffn-symb term) 'if) (max (max-form-count (fargn term 2)) (max-form-count (fargn term 3)))) (t (max-form-count-lst (fargs term) 1))))) (defun max-form-count-lst (lst acc) (declare (type (signed-byte 30) acc)) (the (signed-byte 30) (cond ((>= acc (fn-count-evg-max-val)) (fn-count-evg-max-val)) ((null lst) acc) (t (max-form-count-lst (cdr lst) (+f acc (max-form-count (car lst)))))))) ) (defun controller-complexity1 (flg args controller-pocket) ; Flg is either t (meaning we measure the controllers) or nil ; (meaning we measure the non-controllers). Args is the arg list ; to a call of a fn with the given controller pocket. ; In this implementation a controller pocket is a list of ; Booleans in 1:1 correspondence with the formals. A t in an ; argument position indicates that the formal is a controller. ; We sum the max-form-counts of the arguments in controller (or ; non-controller, according to flg) positions. (cond ((null args) 0) ((eq (car controller-pocket) flg) (+ (max-form-count (car args)) (controller-complexity1 flg (cdr args) (cdr controller-pocket)))) (t (controller-complexity1 flg (cdr args) (cdr controller-pocket))))) (defun controller-complexity (flg term controller-alist) ; Term is a call of some recursive fn in a mutually recursive clique. ; Controller-alist is an alist that assigns to each fn in the clique a ; controller-pocket. We compute the controller complexity (or ; non-controller complexity, according to flg being t or nil) of term ; for the controller pocket assigned fn in the alist. (controller-complexity1 flg (fargs term) (cdr (assoc-eq (ffn-symb term) controller-alist)))) (defun controller-pocket-simplerp (call result controller-alist) ; Call has rewritten to something involving result. Both call and ; result are applications of functions in the same mutually recursive ; clique. ; Controller-alist associates a fn in the clique to a controller ; pocket. A controller pocket is a list in 1:1 correspondence with ; the formals of the fn with a t in those slots that are controllers ; and a nil in the others. Thus, this alist assigns a complexity to ; both call and to result. ; We determine whether there controller-alist assigns a lower ; complexity to result than to call. (< (controller-complexity t result controller-alist) (controller-complexity t call controller-alist))) (defun constant-controller-pocketp1 (args controller-pocket) (cond ((null args) t) ((car controller-pocket) (and (quotep (car args)) (constant-controller-pocketp1 (cdr args) (cdr controller-pocket)))) (t (constant-controller-pocketp1 (cdr args) (cdr controller-pocket))))) (defun constant-controller-pocketp (term controller-alist) ; Term is a call of some fn in the clique for which controller-alist is ; a controller alist. That alist assigns a controller-pocket to fn. ; We determine whether the controller arguments to fn in term are all ; quoted. (constant-controller-pocketp1 (fargs term) (cdr (assoc-eq (ffn-symb term) controller-alist)))) (defun some-controller-pocket-constant-and-non-controller-simplerp (call result controller-alist) ; Call and result are both applications of functions in the same ; mutually recursive clique. Controller-alist is an alistthat assigns ; to each fn in the clique a controller pocket. We determine whether ; that alist assigns controllers in such a way that the controllers of ; result are constant and the complexity of the non-controllers in ; result is less than that of the non-controllers in call. (and (constant-controller-pocketp result controller-alist) (< (controller-complexity nil result controller-alist) (controller-complexity nil call controller-alist)))) (mutual-recursion (defun rewrite-fncallp (call result cliquep top-clause current-clause controller-alist) ; Call has rewritten to (some term involving) result. We want to know ; if we should replace call by result or leave the call unopened. The ; ffn-symb of call is known to be a recursive function symbol, fn. It ; is not a lambda-expression. Cliquep is nil if fn is singly ; recursive and is the list of functions in fn's clique if it is ; mutually recursive. Top-clause and current-clause are two clauses ; from simplify-clause0 (the input clause there and the result of ; removing trivial equations). Controller-alist is the ; :controller-alist field of the def-body of fn. ; Controller-alist pairs every function in fn's mutually recursive ; clique with a controller pocket. Thus, if fn is singly recursive, ; controller-alist looks like this: ; ((fn . controller-pocket)). ; But if fn is mutually recursive with clique fn1...fnm, then this ; alist assigns a controller pocket to each fni. (cond ((variablep result) t) ((fquotep result) t) ((flambda-applicationp result) ; This should not normally happen. The only time we refuse to open a ; lambda-application is (a) we are at the top level of the clause and ; it has too many ifs, or (b) we were told not to open it by the user. ; But (a) can't have happened while we were constructing result ; because we were opening up a recursive fn. Of course, the worry is ; that the body of this lambda-expression contains a recursive call ; that will somehow get loose and we will indefinitely recur. But if ; the only way we get here is via case (b) above, we won't ever open ; this lambda and so we're safe. We therefore act as though this ; lambda were just some ordinary function symbol. (rewrite-fncallp-listp call (fargs result) cliquep top-clause current-clause controller-alist)) ((if cliquep (member-eq (ffn-symb result) cliquep) (eq (ffn-symb result) (ffn-symb call))) (and (or (all-args-occur-in-top-clausep (fargs result) top-clause) (dumb-occur-lst result current-clause) (controller-pocket-simplerp call result controller-alist) (some-controller-pocket-constant-and-non-controller-simplerp call result controller-alist)) (rewrite-fncallp-listp call (fargs result) cliquep top-clause current-clause controller-alist))) (t (rewrite-fncallp-listp call (fargs result) cliquep top-clause current-clause controller-alist)))) (defun rewrite-fncallp-listp (call lst cliquep top-clause current-clause controller-alist) (cond ((null lst) t) (t (and (rewrite-fncallp call (car lst) cliquep top-clause current-clause controller-alist) (rewrite-fncallp-listp call (cdr lst) cliquep top-clause current-clause controller-alist))))) ) (mutual-recursion (defun contains-rewriteable-callp (fn term cliquep terms-to-be-ignored-by-rewrite) ; This function scans the non-quote part of term and determines ; whether it contains a call, t, of any fn in the mutually recursive ; clique of fn, such that t is not on terms-to-be-ignored-by-rewrite. ; Fn is known to be a symbol, not a lambda-expression. If cliquep is ; nil, fn is singly recursive. Otherwise, cliquep is the list of ; functions in the clique (including fn). (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) ; If term is a lambda-application then we know that it contains no recursive ; calls of fns in the clique, as described in the comment on the subject ; in rewrite-fncallp above. (contains-rewriteable-callp-lst fn (fargs term) cliquep terms-to-be-ignored-by-rewrite)) ((and (if cliquep (member-eq (ffn-symb term) cliquep) (eq (ffn-symb term) fn)) (not (member-equal term terms-to-be-ignored-by-rewrite))) t) (t (contains-rewriteable-callp-lst fn (fargs term) cliquep terms-to-be-ignored-by-rewrite)))) (defun contains-rewriteable-callp-lst (fn lst cliquep terms-to-be-ignored-by-rewrite) (cond ((null lst) nil) (t (or (contains-rewriteable-callp fn (car lst) cliquep terms-to-be-ignored-by-rewrite) (contains-rewriteable-callp-lst fn (cdr lst) cliquep terms-to-be-ignored-by-rewrite))))) ) (defrec linear-lemma ((nume . hyps) max-term concl backchain-limit-lst rune . ; The match-free field should be :all or :once if there are free variables in ; the hypotheses, else nil. match-free) nil) ; Finally the Rewriter (defrec current-literal (not-flg . atm) t) (defrec rewrite-constant ; WARNING: If you change the layout of the rewrite-constant in a way that ; affects the position of :current-clause -- e.g., add a field -- you MUST ; change the definition in axioms.lisp of the function |Access REWRITE-CONSTANT ; record field CURRENT-CLAUSE|. If you don't, however, the build will fail ; loudly (via a redefinition error). ; WARNING: If you change the layout of the rewrite-constant in a way that ; affects the position on :nonlinearp, you must change the guard on the ; definitions of nonlinearp-default-hint in (at least) the following ; community books: ; books/arithmetic-5/lib/basic-ops/default-hint.lisp -- one occurrence ; books/hints/basic-tests.lisp -- two occurrences ; WARNING: The name "rewrite-constant" is a misnomer because it is not ; really constant during rewriting. The active-theory is frequently ; toggled. ; The Rewriter's Constant Argument -- rcnst ; In nqthm the rewriter accessed many "special variables" -- variables ; bound outside the rewriter. Some of these were true specials in the ; rewriter, in the sense that the rewriter sometimes re-bound them in its ; recursion. An example of such a variable is fnstack, which is nil ; outside the rewriter and re-bound inside the rewriter only when we ; tentatively expand a function call. But other nqthm special variables ; were just constants -- as far as the rewriter was concerned. For example, ; current-lit, the literal on which rewrite-clause called rewrite, is ; set outside the call of rewrite and read but never written inside. ; We package up these "rewrite constants" as a single record so that ; we can pass all of them in one argument. ; We list below some of the "constants" in question and where they are set. We ; then give the meaning of each field. ; field where set soundness ; pt rewrite-clause * ; current-literal not-flg rewrite-clause ; current-literal atm rewrite-clause ; top-clause simplify-clause1 ; current-clause simplify-clause1 ; terms-to-be-ignored-by-rewrite simplify-clause ; expand-lst simplify-clause ; fns-to-be-ignored-by-rewrite prove ; The fields marked with *'s are involved in the soundness of the result ; of rewriting. The rest are of heuristic use only. ; The current-literal not-flg and atm are always used together so we bundle ; them so we can extract them both at once: ((active-theory . (backchain-limit-rw . rw-cache-state)) current-enabled-structure (pt restrictions-alist . expand-lst) (force-info fns-to-be-ignored-by-rewrite . terms-to-be-ignored-by-rewrite) (top-clause . current-clause) ((splitter-output . current-literal) . oncep-override) (nonlinearp . cheap-linearp) . case-split-limitations) t) ; Active-theory is either :standard or :arithmetic. (It was added first to ; Version_2.7.) It is used to determine whether we are in the middle of ; rewriting arithmetic expressions in support of non-linear arithmetic. This ; field is toggled during rewriting. Thus, we put it at the front of the data ; structure. ; Current-enabled-structure is an enabled-structure that contains the theory ; which specifies which rules are to be considered enabled. ; Pt -- a parent tree (see Essay on Parent Trees) denoting a set of literals in ; current-clause and containing the one we are working on in rewrite-clause and ; all the others that have rewritten to false. Any poly in the ; simplify-clause-pot-lst that depends on one of these literals is considered ; "inactive." To avoid tail biting we do not use inactive polys. ; Restrictions-alist is used for :restrict hints. (Someday we should flesh out ; this explanation.) ; Expand-lst -- a list of expand-hint structures used heuristically. We ; automatically expand any term on this list when encountered. It is set from ; the user's hint settings and by simplify-clause to force the expansion of the ; induction conclusion in post-induction, pre-settled down rewriting. ; Case-split-limitations -- typically (sr-limit (w state)), but can be set with ; a :case-split-limitations hint to override that default in the simplifier. ; Force-info -- t if there are no calls of IF in the :top-clause, else 'weak. ; Fns-to-be-ignored-by-rewrite -- a list of function symbols used ; heuristically. If a term begins with one of these, we do not rewrite it. ; This is set from the user's hint settings. ; Terms-to-be-ignored-by-rewrite -- a list of terms used heuristically. We do ; not rewrite any term on this list. Simplify-clause sets it during the ; initial post-induction rewriting to prevent us from looking prematurely at ; the induction hypotheses (see simplify-clause for details). ; Top-clause -- the clause on which simplify-clause was called. This is used ; heuristically only, to decide whether to expand function calls. The ; difference between top-clause and current-clause is that current-clause has ; been subjected to remove-trivial-equations. ; Current-clause -- Top-clause with remove-trivial-equations. This is used ; heuristically only. ; Current-literal -- a pair containing the not-flg and atm of the literal on ; which rewrite-clause is currently working. It is used to avoid biting our ; tail (see below). When we are adding a term to the pot-lst, we refuse to add ; the negation of the current literal. ; Nonlinearp -- A boolean indicating whether nonlinear arithmetic should be ; considered to be active. ; Cheap-linearp -- A boolean indicating whether linear arithmetic should avoid ; rewriting terms to turn into polys and avoid adding linear lemmas. ; We always obtain our rewrite-constant by loading relevant information into ; the following empty constant. Warning: The constant below is dangerously ; useless less the current-enabled-structure is set to an enabled-structure. (defconst *default-rw-cache-state* :atom) (defconst *empty-rewrite-constant* (make rewrite-constant :active-theory :standard :backchain-limit-rw nil :case-split-limitations nil :splitter-output t ; initial value of state global splitter-output :current-clause nil :current-enabled-structure nil :current-literal nil :expand-lst nil :fns-to-be-ignored-by-rewrite nil :force-info nil :nonlinearp nil :cheap-linearp nil :oncep-override :clear :pt nil :restrictions-alist nil :rw-cache-state *default-rw-cache-state* :terms-to-be-ignored-by-rewrite nil :top-clause nil)) ; So much for the rcnst. (defrec metafunction-context ; WARNING: If you change the layout of this record you must change the PROGN in ; axioms.lisp that defines |Access METAFUNCTION-CONTEXT record field ; TYPE-ALIST| and the other record functions, because that form comes about by ; macroexpanding this defrec. But if you don't change that PROGN, however, the ; build will fail loudly (via a redefinition error). ; See the Essay on Metafunction Support, Part 1 for an explanation of the use ; of this record. (rdepth type-alist obj geneqv wrld fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree unify-subst) t) (defun ok-to-force (rcnst) ; We normally use the rewrite constant to determine whether forcing is enabled. ; At one time we experimented with a heuristic that allows the "force-flg" to ; be 'weak, meaning: do not force if the resulting assumption has a variable ; that does not appear in its type-alist. (Recall that its type-alist is used ; for the hypotheses of the corresponding goal in the forcing round.) We still ; allow 'weak to be stored in the rewrite constant, and at the time of this ; writing, the heuristic just described is still implemented in ; force-assumption. However, we found an example where this heuristic is too ; weak: the presence of IF terms in the top-level clause is enough to cause ; bad assumptions to be forced, even though our heuristic permits does not ; filter out those bad assumptions. So we have decided for now that the value ; 'weak from the :force-info field of the rewrite-constant, which is generated ; when there is an IF in the top-level clause, means: do not force, period. ; (Note that forcing may still be used heuristically, for example by ; type-alist-clause; but, assumptions will not "get out" of such uses.) (let ((force-info (access rewrite-constant rcnst :force-info))) (cond ((eq force-info t) (and (enabled-numep *force-xnume* (access rewrite-constant rcnst :current-enabled-structure)) t)) ((eq force-info 'weak) ; See comment above. nil) (t (er hard 'ok-to-force "OK-TO-FORCE called on apparently uninitialized rewrite constant, ~ ~x0." rcnst))))) ; The next major concern is the fact that rewrite takes so many ; arguments. ; Rewrite takes so many arguments that we introduce a macro for ; calling it. Many functions that call rewrite also take a lot of ; rewrite-type arguments and this macro can be used to call them too. ; Because all of these functions are mutually recursive with rewrite, ; we consider the use of this macro as an indication that we are ; entering the rewriter and have given it the name "rewrite-entry". ; For example, if you write: ; (rewrite-entry (rewrite-if test left right alist)) ; you get ; (rewrite-if test left right alist type-alist ... rcnst ttree) ; And if you write: ; (rewrite-entry (rewrite left alist 2) ; :ttree new-ttree) ; you get ; (rewrite left alist 2 ... rcnst new-ttree) ; Note that in specifying which extra arguments you wish to set ; you must use the keyword form of the formal. This implementation ; decision was made just to bring rewrite-entry into the same style ; as CLTL keyword args, which it resembles. ; The macro extends the given call by adding 12 extra arguments. ; The arguments used are the "extra formals" of rewrite, namely ; ; &extra formals ; rdepth type-alist obj geneqv wrld state fnstack ancestors ; backchain-limit step-limit simplify-clause-pot-lst rcnst gstack ttree ; Important Note: The string "&extra formals" is included where ever ; this list has been copied. ; However, for every extra formal for which the macro invocation ; specifies a value, that value is used instead. Any function to be ; called via rewrite-entry should include the extra formals above ; explicitly in its defun, as the last 12 formals. ; Convention: Not every function uses all 12 of the extra formals. ; Ignored formals are so declared. It is our convention when calling ; a function with an ignored formal to pass it nil in that slot. That ; explains some (rewrite-entry (add-poly...) :obj nil...). We could have ; just passed obj's current value, but that suffers from making the ; caller look like it uses obj when in fact obj might be ignored by it ; too. This convention means that if one of these functions does ; begin to use a currently ignored formal, it will be necessary to ; remove the formal from the (declare (ignore ...)) and might cause us ; to think about the incoming value. (defun plist-to-alist (lst) ; Convert '(key1 val1 key2 val2 ...) to '((key1 . val1) (key2 . val2) ...). ; In use here, the keys are all in the keyword package. (cond ((null lst) nil) (t (cons (cons (car lst) (cadr lst)) (plist-to-alist (cddr lst)))))) (defmacro adjust-rdepth (rdepth) ; Keep the following in sync with zero-depthp. #+acl2-rewrite-meter ; for stats on rewriter depth `(1+f ,rdepth) #-acl2-rewrite-meter ; normal case (no stats) `(1-f ,rdepth)) (defun add-rewrite-args (extra-formals keyword-extra-formals alist) ; extra-formals is '(type-alist ...) ; keyword-extra-formals is '(:type-alist ...) ; alist pairs keyword extra formals to terms ; We return a list in 1:1 correspondence with extra-formals. The ; element corresponding to an extra-formal is the value specified by ; the alist if one is so specified, otherwise it is the extra-formal ; itself. (cond ((null extra-formals) nil) (t (cons (let ((pair (assoc-eq (car keyword-extra-formals) alist))) (cond (pair (cdr pair)) (t (car extra-formals)))) (add-rewrite-args (cdr extra-formals) (cdr keyword-extra-formals) alist))))) (defrec step-limit-record ; See the Essay on Step-limits. ; The state global 'step-limit-record is bound to one of these records at the ; start of an event by with-ctx-summarized (specifically, by the call of ; with-prover-step-limit in save-event-state-globals). Then, :start is the ; initial value of state global 'last-step-limit for that event; :strictp ; indicates whether an error should occur if the step-limit is exceeded; and ; :sub-limit is the step-limit to use for sub-events, if any, where nil ; indicates that the sub-limit should be limited by the current step-limit. (start strictp . sub-limit) t) (defun step-limit-start (state) ; Return the starting value of step-limit in the present context. See defrec ; step-limit-record. (let ((rec (f-get-global 'step-limit-record state))) (cond (rec (access step-limit-record rec :start)) (t (step-limit-from-table (w state)))))) (defun step-limit-strictp (state) ; Warning: Keep this in sync with code in with-prover-step-limit-fn near the ; comment there about step-limit-strictp. ; Return true if in the present context, we are to cause an error if the ; step-limit is exceeded. See defrec step-limit-record. (let ((rec (f-get-global 'step-limit-record state))) (cond (rec (access step-limit-record rec :strictp)) (t nil)))) (defun initial-step-limit (wrld state) ; Warning: Keep this in sync with code in with-prover-step-limit-fn near the ; comment there about initial-step-limit. ; See the Essay on Step-limits. ; This function returns the current step limit. If 'step-limit-record has a ; non-nil value (see defrec step-limit-record), then we are already tracking ; step-limits in the state, so we return the value of 'last-step-limit. ; Otherwise the acl2-defaults-table is consulted for the step-limit. (declare (xargs :guard ; also needs rec, bound below, to be suitable (and (plist-worldp wrld) (alistp (table-alist 'acl2-defaults-table wrld)) (let ((val (cdr (assoc-eq :step-limit (table-alist 'acl2-defaults-table wrld))))) (or (null val) (and (natp val) (<= val *default-step-limit*)))) (state-p state) (boundp-global 'step-limit-record state) (boundp-global 'last-step-limit state)))) (let ((rec (f-get-global 'step-limit-record state))) (cond (rec (or (access step-limit-record rec :sub-limit) (f-get-global 'last-step-limit state))) (t (step-limit-from-table wrld))))) (defun step-limit-error1 (ctx str start where state) (declare (ignorable state)) ; only used in raw Lisp #-acl2-loop-only (when *step-limit-error-p* (er soft ctx str start where) (setq *step-limit-error-p* 'error) (throw 'step-limit-tag ; irrelevant value t)) (the (signed-byte 30) (prog2$ (er hard? ctx str start where) -1))) (defmacro step-limit-error (superior-context-p) ; If superior-context-p is t then we return an error triple; if it is nil, we ; return -1, possibly causing a hard error or a throw. (let ((str "The prover step-limit, which is ~x0 in the ~@1, has been ~ exceeded. See :DOC set-prover-step-limit.") (ctx ''step-limit)) (cond (superior-context-p `(er soft ,ctx ,str (step-limit-start state) "context immediately above the one just completed")) (t `(the-fixnum (step-limit-error1 ,ctx ,str (step-limit-start state) "current context" state)))))) (defmacro decrement-step-limit (step-limit) ; We make this event a macro for improved performance. (declare (xargs :guard ; By insisting that the formal is a symbol, we guarantee that its repeated ; reference below does not result in repeated evaluation of other than the ; current binding of a symbol. (symbolp step-limit))) `(the (signed-byte 30) (cond ((< 0 (the-fixnum ,step-limit)) (1-f ,step-limit)) ((eql -1 (the-fixnum ,step-limit)) -1) (t (assert$ (eql 0 (the-fixnum ,step-limit)) (cond ((step-limit-strictp state) (step-limit-error nil)) (t -1))))))) (defmacro rewrite-entry (&rest args) (declare (xargs :guard (and (true-listp args) (consp (car args)) (keyword-value-listp (cdr args))))) (let* ((call0 (append (car args) (add-rewrite-args '( ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) '( ; &extra formals -- keyword versions :rdepth :step-limit :type-alist :obj :geneqv :wrld :state :fnstack :ancestors :backchain-limit :simplify-clause-pot-lst :rcnst :gstack :ttree) (plist-to-alist (if (eq (caar args) 'rewrite) (remove-keyword :step-limit ; dealt with below (cdr args)) (cdr args)))))) (call (cond ((not (eq (caar args) 'rewrite)) call0) (t (let ((call1 `(let ((step-limit (decrement-step-limit step-limit))) (declare (type (signed-byte 30) step-limit)) ,call0)) (step-limit-tail (assoc-keyword :step-limit (cdr args)))) (cond (step-limit-tail `(let ((step-limit ,(cadr step-limit-tail))) ,call1)) (t call1))))))) #+acl2-loop-only call #-acl2-loop-only (if (member-eq (caar args) '(rewrite rewrite-with-lemma add-terms-and-lemmas non-linear-arithmetic)) ; We restore *deep-gstack* to its value from before the call. We really only ; need to do that for dmr monitoring, so that there aren't stale frames on ; *deep-gstack* when printing both the gstack and pstk (see dmr-string). But ; the prog1 and setq seem cheap so we clean up after ourselves in all cases. ; WARNING: Gstack must be bound where rewrite-entry is called for the above ; values of (caar args). `(cond ((or (f-get-global 'gstackp state) (f-get-global 'dmrp state)) ; We could call our-multiple-value-prog1 instead of multiple-value-prog1 in the ; #+cltl2 case below, which would avoid the need for a separate #-cltl2 case. ; However, for non-ANSI GCL we want to take advantage of the fact that all ; functions in the rewrite nest return a first argument (the new step-limit) ; that is a fixnum, but the compiler doesn't use that information when a prog1 ; call is used. So we manage the non-ANSI case (including non-ANSI GCL) ; ourselves. #+cltl2 (multiple-value-prog1 ,call (setq *deep-gstack* gstack)) #-cltl2 ,(let ((var (gensym))) `(let ((,var ,call)) (declare (type (signed-byte 30) ,var)) (setq *deep-gstack* gstack) ,var))) (t ,call)) call))) (deflabel free-variables :doc ":Doc-Section Rule-Classes free variables in rules~/ As described elsewhere (~pl[rule-classes]), ACL2 rules are treated as implications for which there are zero or more hypotheses ~c[hj] to prove. In particular, rules of class ~c[:]~ilc[rewrite] may look like this: ~bv[] (implies (and h1 ... hn) (fn lhs rhs)) ~ev[] Variables of ~c[hi] are said to occur ~em[free] in the above ~c[:rewrite] rule if they do not occur in ~c[lhs] or in any ~c[hj] with ~c[j:args oncep-tp Function ONCEP-TP Formals: (RUNE WRLD) Signature: (ONCEP-TP * *) => * Guard: (AND (PLIST-WORLDP WRLD) (CONSP RUNE) (CONSP (CDR RUNE)) (SYMBOLP (CADR RUNE))) Guards Verified: T Defun-Mode: :logic Type: built-in (or unrestricted) ONCEP-TP ACL2 !> ~ev[]~/~/") (deflabel free-variables-examples :doc ":Doc-Section Free-Variables examples pertaining to free variables in rules~/ The examples in the two sub-topics of this topic illustrate the handling of free variables in rules of class ~c[:]~ilc[rewrite] (~pl[free-variables-examples-rewrite]) and of class ~c[:]~ilc[forward-chaining] (~pl[free-variables-examples-forward-chaining]), respectively. These implicitly illustrate ~il[free-variables] handling in rules of class ~c[:]~ilc[linear] as well. Also ~pl[free-variables] and ~pl[rule-classes].~/~/") (deflabel free-variables-examples-rewrite ; The second example below could have been given as follows instead, though ; this one is kind of weird since there are free variables on the right-hand ; side of the ground unit rules. ; (encapsulate ; (((p1 *) => *) ; ((p2 * *) => *) ; ((p3 *) => *) ; ((a) => *) ; ((b) => *)) ; (local (defun p1 (x) x)) ; (local (defun p2 (x y) (list x y))) ; (local (defun p3 (x) x)) ; (local (defun a () 0)) ; (local (defun b () 0))) ; ; ; Allow default of :match-free :all (form may be omitted). ; (set-match-free-error nil) ; ; (defaxiom ax1 ; (implies (and (p1 x) ; (p2 x y)) ; (p3 y))) ; ; (defaxiom p1-a-equals-p2-a-y ; (equal (p1 (a)) (p2 (a) y))) ; ; (defaxiom p1-u-equals-p2-a-y ; (equal (p1 (b)) (p2 (a) y))) ; ; ; Succeeds. ; (thm (implies (p2 (a) y) ; (p3 y))) ; ; (add-match-free-override :once t) ; ; ; Fails. ; (thm (implies (p2 (a) y) ; (p3 y))) ; ; (add-match-free-override :clear) ; ; ; Succeeds. ; (thm (implies (p2 (a) y) ; (p3 y))) ; ; (add-match-free-override :once (:rewrite string<-l-trichotomy)) ; ; ; Succeeds. ; (thm (implies (p2 (a) y) ; (p3 y))) :doc ":Doc-Section Free-Variables-Examples examples pertaining to free variables in ~il[rewrite] rules~/ The following examples illustrate ACL2's handling of free variables in ~il[rewrite] rules, as well as user control over how such free variables are handled. ~l[free-variables] for a background discussion.~/ ~bv[] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Example 1 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defstub p2 (x y) t) ; introduce unconstrained function ; Get warning because of free variable. This would be an error if you had ; first executed (set-match-free-error t) in order to force yourself to ; specify :match-free (illustrated later, below). (defaxiom p2-trans (implies (and (p2 x y) (p2 y z)) (p2 x z))) ; Succeeds. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) ; The following causes an error because p2-trans is not a rune. (add-match-free-override :once p2-trans) ; After the following, the rewrite rule p2-trans will only allow one ; attempt per hypothesis to bind free variables. (add-match-free-override :once (:rewrite p2-trans)) ; Now this same theorem fails to be proved. Here's why. The ; context for proving (p2 a d) happens to include the hypotheses in ; reverse order. So when the first hypothesis of p2-trans, namely ; (p2 x y), is relieved, where x is bound to a (as we are attempting ; to rewrite the current literal (p2 a d)), we find (p2 a b) in the ; context before (p2 a c) and hence y is bound to b. The ; instantiated second hypothesis of p2-trans is thus (p2 b d), and ; the proof fails. Before the add-match-free-override form above, ; the proof succeeded because the rewriter was allowed to backtrack ; and find the other binding for the first hypothesis of p2-trans, ; namely, y bound to c. Then the instantiated second hypothesis of ; p2-trans is (p2 c d), which is known to be true in the current ; context. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) ; Return to original behavior for binding free variables. (add-match-free-override :all t) ; Succeeds once again. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) (u) ; undo (add-match-free-override :all t) ; This is an error, since no further arguments should appear after ; :clear. (add-match-free-override :clear t) ; Return all rules to original behavior for binding free variables, ; regardless of which previous add-match-free-override forms have ; been executed. (add-match-free-override :clear) ; This succeeds just as it did originally. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) (ubt! 'p2-trans) ; back to the start, except retain the defstub ; Require that :match-free be specified for :linear and :rewrite rules with ; free variables. (set-match-free-error t) ; Fails because :match-free is missing. (defaxiom p2-trans (implies (and (p2 x y) (p2 y z)) (p2 x z))) ; Fails because :match-free must be followed by :once or :all. (defaxiom p2-trans (implies (and (p2 x y) (p2 y z)) (p2 x z)) :rule-classes ((:rewrite :match-free nil))) ; Succeeds, this time with no warning at all. (defaxiom p2-trans (implies (and (p2 x y) (p2 y z)) (p2 x z)) :rule-classes ((:rewrite :match-free :once))) ; Fails because we only bind once (see earlier long comment). (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) ; Treat p2-trans as though `:match-free :all' had been specified. (add-match-free-override :all (:rewrite p2-trans)) ; Succeeds since more than one binding is allowed for p2-trans. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) (u) (u) ; Specify that future :linear and :rewrite rules with free variables ; that do not have :match-free specified are treated as though ; `:match-free :once' were specified. (set-match-free-default :once) ; Succeeds without error since `:match-free' is specified, as described ; above. But there is a warning, since :match-free is not specified for this ; :rewrite rule. (defaxiom p2-trans (implies (and (p2 x y) (p2 y z)) (p2 x z))) ; Fails since only single bindings are allowed for p2-trans. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) ; Treat p2-trans as though `:match-free :all' had been specified. (add-match-free-override :all t) ; Succeeds. (thm (implies (and (p2 a c) (p2 a b) (p2 c d)) (p2 a d))) ; Test searching of ground units, i.e. rewrite rules without variables on the ; left side of the conclusion, for use in relieving hypotheses with free ; variables. This is a very contrived example. (ubt! 1) ; back to the start (encapsulate (((p1 *) => *) ((p2 * *) => *) ((p3 *) => *) ((a) => *) ((b) => *)) (local (defun p1 (x) x)) (local (defun p2 (x y) (list x y))) (local (defun p3 (x) x)) (local (defun a () 0)) (local (defun b () 0))) ; Allow default of :match-free :all (form may be omitted). (set-match-free-error nil) (defaxiom ax1 (implies (and (p2 x y) (p1 y)) (p3 x))) (defaxiom p2-a-b (p2 (a) (b))) (defaxiom p2-a-a (p2 (a) (a))) (defaxiom p1-b (p1 (b))) ; Succeeds; see long comment below on next attempt to prove this ; theorem. (thm (implies (p2 (a) y) (p3 (a)))) ; Now ax1 will only relieve hypothesis (p2 x y) for one binding of y: (add-match-free-override :once t) ; Fails when ax1 attempts to rewrite the conclusion to true, because ; the most recent ground unit for hypothesis (p2 x y) with x bound ; to (a) is rule p2-a-a, which binds y to (a). If more than one ground ; unit could be used then we would backtrack and apply rule p2-a-b, ; which binds y to (b) and hence hypothesis (p1 y) of ax1 is ; relieved by rule p1-b. (thm (implies (p2 (a) y) (p3 (a)))) ; Return rules to original :match-free behavior. (add-match-free-override :clear) ; Succeeds once again. (thm (implies (p2 (a) y) (p3 (a)))) ; Just for kicks, change the behavior of a built-in rule irrelevant ; to the proof at hand. (add-match-free-override :once (:rewrite string<-l-trichotomy)) ; Still succeeds. (thm (implies (p2 (a) y) (p3 (a)))) ~ev[] ~bv[] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Example 2 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ~ev[] The next example illustrates the use of the ~il[break-rewrite] facility to get information about handling of free variables by the rewriter. Explanation is given after this (edited) transcript. Input begins on lines with a prompt (search for ``ACL2''); the rest is output. ~bv[] ACL2 !>(encapsulate ((p1 (u x) t) (bad (x) t) (p2 (x y z) t) (bar (x y) t) (foo (x y) t) (poo (x y) t) (prop (u) t)) (local (defun p1 (u x) (declare (ignore u x)) nil)) (local (defun bad (x) (declare (ignore x)) nil)) (local (defun p2 (x y z) (declare (ignore x y z)) nil)) (local (defun bar (x y) (declare (ignore x y)) nil)) (local (defun foo (x y) (declare (ignore x y)) nil)) (local (defun poo (x y) (declare (ignore x y)) nil)) (local (defun prop (u) (declare (ignore u)) t)) (defthm foo-poo (implies (syntaxp (equal y 'y3)) (equal (foo x y) (poo x y)))) (defthm lemma-1 (implies (and (p1 u x) (bad x) (p2 x y z) (bar x y) (equal x x) ; admittedly silly! (foo x y)) (prop u)) :rule-classes ((:rewrite :match-free :all)))) ; [[ output omitted ]] Summary Form: ( ENCAPSULATE ((P1 ...) ...) ...) Rules: NIL Warnings: Subsume and Non-rec Time: 0.08 seconds (prove: 0.00, print: 0.01, other: 0.06) T ACL2 !>:brr t The monitored runes are: NIL T ACL2 !>:monitor (:rewrite lemma-1) t (((:REWRITE LEMMA-1) 'T)) ACL2 !>(thm (implies (and (p1 u0 x1) (bad x1) (bad x3) (bar x3 y1) (bar x3 y3) (p1 u0 x2) (p1 u0 x3) (p2 x3 y1 z1) (p2 x3 y3 z1)) (prop u0))) (1 Breaking (:REWRITE LEMMA-1) on (PROP U0): 1 ACL2 >:eval 1x (:REWRITE LEMMA-1) failed because :HYP 1 contains free variables. The following display summarizes the attempts to relieve hypotheses by binding free variables; see :DOC free-variables. [1] X : X1 Failed because :HYP 3 contains free variables Y and Z, for which no suitable bindings were found. [1] X : X2 Failed because :HYP 2 rewrote to (BAD X2). [1] X : X3 [3] Z : Z1 Y : Y1 Failed because :HYP 6 rewrote to (FOO X3 Y1). [3] Z : Z1 Y : Y3 Failed because :HYP 6 rewrote to (POO X3 Y3). 1 ACL2 >:unify-subst U : U0 1 ACL2 > ~ev[] The ~c[:eval] command above asks the rewriter to attempt to apply the rewrite rule ~c[lemma-1] to the term ~c[(prop u0)], shown just above the line with ~c[:eval]. As we can see at the end, the variable ~c[u] in the conclusion of ~c[lemma-1] is being bound to the variable ~c[u0] in the conjecture. The first hypothesis of ~c[lemma-1] is ~c[(p1 u x)], so the rewriter looks for some ~c[x] for which ~c[(p1 u0 x)] is known to be true. It finds ~c[x1], and then goes on to consider the second hypothesis, ~c[(bad x)]. Since the theorem we are proving has ~c[(bad x1)] in the hypothesis and ~c[x] is currently bound to ~c[x1], the rewriter is satisfied and moves on to the third hypothesis of ~c[lemma-1], ~c[(p2 x y z)]. However, ~c[x] is bound to ~c[x1] and there are no instances of ~c[y] and ~c[z] for which ~c[(p2 x1 y z)] is known in the current context. All of the above analysis is summarized in the first part of the output from ~c[:eval] above: ~bv[] [1] X : X1 Failed because :HYP 3 contains free variables Y and Z, for which no suitable bindings were found. ~ev[] Thus, the binding of ~c[x] to ~c[x1] on behalf of the first hypothesis has failed. The rewriter now backs up to look for other values of ~c[x] that satisfy the first hypothesis, and finds ~c[x2] because our current theorem has a hypothesis of ~c[(p1 u0 x2)]. But this time, the second hypothesis of ~c[lemma-1], ~c[(bad x)], is not known to be true for ~c[x]; that is, ~c[(bad x2)] does not rewrite to ~c[t]; in fact, it rewrites to itself. That explains the next part of the output from ~c[:eval] above: ~bv[] [1] X : X2 Failed because :HYP 2 rewrote to (BAD X2). ~ev[] The rewriter now backs up again to look for other values of ~c[x] that satisfy the first hypothesis, and finds ~c[x3] because our current theorem has a hypothesis of ~c[(p1 u0 x3)]. This time, the second hypothesis of ~c[lemma-1] is not a problem, and moreover, the rewriter is able to bind ~c[y] and ~c[z] to ~c[y1] and ~c[z1], respectively, in order to satisfy the third hypothesis, ~c[(p2 x y z)]: that is, ~c[(p2 x2 y1 z1)] is known in the current context. That explains more of the above output from ~c[:eval]: ~bv[] [1] X : X3 [3] Z : Z1 Y : Y1 ~ev[] Unfortunately, the sixth hypothesis, ~c[(foo x y)], rewrites to itself under the above bindings: ~bv[] Failed because :HYP 6 rewrote to (FOO X3 Y1). ~ev[] So the rewriter looks for other bindings to satisfy the third hypothesis and finds these. ~bv[] [3] Z : Z1 Y : Y3 ~ev[] This time, the sixth hypothesis can be rewritten under the above bindings, from ~c[(foo x3 y3)] to ~c[(poo x3 y3)] by lemma ~c[foo-poo], but still not to ~c[t]. ~bv[] Failed because :HYP 6 rewrote to (POO X3 Y3). ~ev[] There are no more free variable bindings to try, so this concludes the output from ~c[:eval]. ~bv[] ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Example 3 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ~ev[] The next pair of examples illustrates so-called ``binding hypotheses'' (~pl[free-variables]) and explores some of their subtleties. The first shows binding hypotheses in action on a simple example. The second shows how binding hypotheses interact with equivalence relations and explains the role of ~ilc[double-rewrite]. Our first example sets up a theory with two user-supplied rewrite rules, one of which has a binding hypothesis. Below we explain how that binding hypothesis contributes to the proof. ~bv[] ; Define some unary functions. (defun f (x) (declare (ignore x)) t) (defun g (x) x) (defun h (x) x) (defun k (x) x) ; Prove some simple lemmas. Note the binding hypothesis in g-rewrite. (defthm f-k-h (f (k (h x)))) (defthm g-rewrite (implies (and (equal y (k x)) ; binding hypothesis (f y)) (equal (g x) y))) ; Restrict to a theory that includes the above lemmas but avoids the above ; definitions. (in-theory (union-theories (theory 'minimal-theory) '(f-k-h g-rewrite))) ; Prove a theorem. (thm (equal (g (h a)) (k (h a)))) ~ev[] Let us look at how ACL2 uses the above binding hypothesis in the proof of the preceding ~c[thm] form. The rewriter considers the term ~c[(g (h a))] and finds a match with the left-hand side of the rule ~c[g-rewrite], binding ~c[x] to ~c[(h a)]. The first hypothesis binds ~c[y] to the result of rewriting ~c[(k x)] in the current context, where the variable ~c[x] is bound to the term ~c[(h a)]; thus ~c[y] is bound to ~c[(k (h a))]. The second hypothesis, ~c[(f y)], is then rewritten under this binding, and the result is ~c[t] by application of the rewrite rule ~c[f-k-h]. The rule ~c[g-rewrite] is then applied under the already-mentioned binding of ~c[x] to ~c[(h a)]. This rule application triggers a recursive rewrite of the right-hand side of ~c[g-rewrite], which is ~c[y], in a context where ~c[y] is bound (as discussed above) to ~c[(k (h a))]. The result of this rewrite is that same term, ~c[(k (h a))]. The original call of ~c[equal] then trivially rewrites to ~c[t]. We move on now to our second example, which is similar but involves a user-defined equivalence relation. You may find it helpful to review ~c[:equivalence] rules; ~pl[equivalence]. Recall that when a hypothesis is a call of an equivalence relation other than ~c[equal], the second argument must be a call of ~ilc[double-rewrite] in order for the hypothesis to be treated as a binding hypothesis. That is indeed the case below; an explanation follows. ~bv[] ; Define an equivalence relation. (defun my-equiv (x y) (equal x y)) (defequiv my-equiv) ; introduces rule MY-EQUIV-IS-AN-EQUIVALENCE ; Define some unary functions (defun f (x) (declare (ignore x)) t) (defun g (x) x) (defun h1 (x) x) (defun h2 (x) x) ; Prove some simple lemmas. Note the binding hypothesis in lemma-3. (defthm lemma-1 (my-equiv (h1 x) (h2 x))) (defthm lemma-2 (f (h2 x))) (defthm lemma-3 (implies (and (my-equiv y (double-rewrite x)) ; binding hypothesis (f y)) (equal (g x) y))) ; Restrict to a theory that includes the above lemmas but avoids the above ; definitions. (in-theory (union-theories (theory 'minimal-theory) '(lemma-1 lemma-2 lemma-3 my-equiv-is-an-equivalence))) ; Prove a theorem. (thm (equal (g (h1 a)) (h2 a))) ~ev[] The proof succeeds much as in the first example, but the following observation is key: when ACL2 binds ~c[y] upon considering the first hypothesis of ~c[lemma-3], it rewrites the term ~c[(double-rewrite x)] in a context where it need only preserve the equivalence relation ~c[my-equiv]. At this point, ~c[x] is bound by applying ~c[lemma-3] to the term ~c[(g (h1 a))]; so, ~c[x] is bound to ~c[(h1 a)]. The rule ~c[lemma-1] then applies to rewrite this occurrence of ~c[x] to ~c[(h2 a)], but only because it suffices to preserve ~c[my-equiv]. Thus ~c[y] is ultimately bound to ~c[(h2 a)], and the proof succeeds as one would expect. If we tweak the above example slightly by disabling the user's ~il[equivalence] ~il[rune], then the proof of the ~ilc[thm] form fails because the above rewrite of ~c[(double-rewrite x)] is done in a context where it no longer suffices to preserve ~c[my-equiv] as we dive into the second argument of ~c[my-equiv] in the first hypothesis of ~c[lemma-3]; so, ~c[lemma-1] does not apply this time. ~bv[] (in-theory (union-theories (theory 'minimal-theory) '(lemma-1 lemma-2 lemma-3))) ; Proof fails in this case! (thm (equal (g (h1 a)) (h2 a))) ~ev[] ~/") (deflabel free-variables-examples-forward-chaining ; Below is a useful comment contributed by Erik Reeber, who also wrote the ; first version of this documentation topic. ; I've heard that the worst cases of forward-chaining blow up occur when one ; forward-chaining rule is feeding another one. These got worse when ; :match-free :all showed up. Previously, such situations were kept in check ; by putting restrictions on the number of times a forward chaining rule ; could use a term that was generated by another forward-chaining rule. This ; worked pretty well since no forward-chaining rule could generate more than ; one new term. However, the match-free :all code allows forward-chaining ; rules to produce lots of terms. Thus, such we can generate a lot more ; rules now. ; I should probably create such a case since it's a more realisitic situation ; for slowdown. I can also imagine some ways to be more restrictive about ; such cases. Perhaps, we should consider the number of siblings of a term ; as well as the number of ancestors it has before we allow new terms to be ; generated from it. Or perhaps only the first round should allow match-free ; :all. ; The problem with any such solution is that it would make our ; forward-chaining even harder to explain to new users. :doc ":Doc-Section Free-Variables-Examples examples pertaining to free variables in ~il[forward-chaining] rules~/ The following examples illustrate ACL2's handling of free variables in ~il[forward-chaining] rules, as well as user control over how such free variables are handled. ~l[free-variables] for a background discussion.~/ ~bv[] ; First let us introduce a transitive operation, op, and prove a ; forward-chaining rule stating the transitivity of op. (encapsulate (((op * *) => *)) (local (defun op (x y) (< x y))) (defthm transitivity-of-op (implies (and (op x y) (op y z)) (op x z)) :rule-classes :forward-chaining)) ; The following theorem is proved by forward chaining, using the above rule. (thm (implies (and (op u v) (op v w) (op v a)) (op u w))) ; The proof of the theorem just above succeeds because the term (op u v) ; triggers the application of forward-chaining rule transitivity-of-op, ; binding x to u and y to v. Free variable z of that rule is bound to both w ; and to a, resulting in the addition of both (op u w) and (op u a) to the ; context. However, (op v a) happens to be at the front of the context, so ; if only one free-variable binding had been allowed, then z would have only ; been bound to a, not to w, as we now illustrate. (add-match-free-override :once (:forward-chaining transitivity-of-op)) (thm ; FAILS! (implies (and (op u v) (op v w) (op v a)) (op u w))) :ubt! 1 ; Starting over, this time we prove transitivity-of-op as a :match-free :once ; forward-chaining rule. Note that the presence of :match-free eliminates ; the free-variables warning that we got the first time. (encapsulate (((op * *) => *)) (local (defun op (x y) (< x y))) (defthm transitivity-of-op (implies (and (op x y) (op y z)) (op x z)) :rule-classes ((:forward-chaining :match-free :once)))) (thm ; FAILS! (implies (and (op u v) (op v w) (op v a)) (op u w))) ; Notice that if we swap the order of the last two hypotheses the theorem ; goes through, because this time (op v w) is first in the context. (thm ; SUCCEEDS! (implies (and (op u v) (op v a) (op v w)) (op u w))) :u ; Now let's try setting the default to :once. (set-match-free-default :once) ; We still get a free-variables warning when we admit this forward-chaining rule. (encapsulate (((op * *) => *)) (local (defun op (x y) (< x y))) (defthm transitivity-of-op (implies (and (op x y) (op y z)) (op x z)) :rule-classes ((:forward-chaining)))) ; This theorem fails--as it should. (thm ; FAILS! (implies (and (op u v) (op v w) (op v a)) (op u w))) ; But if we convert this rule (or here, all possible rules) to :all rules, ; then the proof succeeds. (add-match-free-override :all t) (thm ; SUCCEEDS! (implies (and (op u v) (op v w) (op v a)) (op u w))) ; Now let's test a relatively slow :all case (the next thm below). :ubt! 1 (encapsulate (((op1 *) => *) ((op3 * * *) => *)) (local (defun op1 (x) (declare (ignore x)) t)) (local (defun op3 (x0 x1 x2) (declare (ignore x0 x1 x2)) t)) (defthm op1-op3-property (implies (and (op1 x0) (op1 x1) (op1 x2)) (op3 x0 x1 x2)) :rule-classes ((:forward-chaining :match-free :all)))) ; The following succeeds, but takes a little time (about a second in one run). (thm (implies (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5) (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11) (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16) (op1 a17) (op1 a18) (op1 a19) (op1 a20)) (op3 a5 a6 a0))) (add-match-free-override :once t) ; The same theorem now fails because of the add-match-free-override, but is ; more than an order of magnitude faster. (thm (implies (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5) (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11) (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16) (op1 a17) (op1 a18) (op1 a19) (op1 a20)) (op3 a5 a6 a0))) ; A slight variant succeeds in a negligible amount of time (still with the ; :once override above). (thm (implies (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5) (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11) (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16) (op1 a17) (op1 a18) (op1 a19) (op1 a20)) (op3 a5 a20 a20))) ; Reality check: This shouldn't give a free-variables warning, and everything ; should work great since there are no free variables with this trigger term. :ubt! 1 (encapsulate (((op1 *) => *) ((op7 * * * * * * *) => *)) (local (defun op1 (x) (declare (ignore x)) t)) (local (defun op7 (x0 x1 x2 x3 x4 x5 x6) (declare (ignore x0 x1 x2 x3 x4 x5 x6)) t)) (defthm op1-op7-property (implies (and (op1 x0) (op1 x1) (op1 x2) (op1 x3) (op1 x4) (op1 x5) (op1 x6)) (op7 x0 x1 x2 x3 x4 x5 x6)) :rule-classes ((:forward-chaining :trigger-terms ((op7 x0 x1 x2 x3 x4 x5 x6)))))) ; The following then succeeds, and very quickly. (thm (implies (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5) (op1 a6)) (op7 a4 a6 a5 a6 a6 a6 a6))) ~ev[]~/" ) ; The following object, a fake rune, will be pushed as a 'lemma to ; indicate that the "linear arithmetic rule" was used. (defconst *fake-rune-for-linear* '(:FAKE-RUNE-FOR-LINEAR nil)) ; We now develop the code used in path maintenance and monitor. ; The goal stack is a list of frames, each of the form (defrec gframe (sys-fn bkptr . args) t) ; where sys-fn is a system function name, e.g., REWRITE, bkptr is an ; arbitrary object supplied by the caller to the sys-fn that indicates ; why the call was made (and must be interpreted by the caller, not ; the called sys-fn), and args are some subset of the args to sys-fn. ; WARNING: We use bkptr as a "hash index" uniquely identifying a hypothesis ; among the hypotheses of a rewrite rule when we are memoizing relieve-hyp. ; Thus, bkptr is a positive integer inside the functions relieve-hyps1 and ; relieve-hyp and their peers. ; Note: Nqthm included a count in each frame which was the number of ; frames generated so far and could be used to determine the ; "persistence" of each frame. I am skipping that for the present ; because it means linearizing the code to pass the incremented count ; across args, etc., unless it is done in an extra-logical style. A ; better idea would be to connect the goal stack to the comment window ; and actually display it so that persistence became visual again. ; The uses of acl2-loop-only below are simply to give us a debugging ; tool for stack overflows. When a stack overflow occurs, set :brr t. ; Then provoke the overflow. Exit from the break and from LP. In raw ; lisp type ; (cw-gstack) ; to see the gstack near the point of overflow. ; In addition, if one then reenters LP and does ; :monitor (:rewrite car-cons) t ; :q ; then one can do ; (brkpt1 '(:rewrite car-cons) nil nil nil nil nil *deep-gstack* *the-live-state*) ; to walk around the stack. ; Furthermore, one can interrupt ACL2 anytime with ctrl-c and do ; (cw-gstack) ; from within the Raw Lisp Break to see what is happening. Continue with :r. #-acl2-loop-only (defparameter *deep-gstack* nil) (defmacro push-gframe (sys-fn bkptr &rest args) ; This macro allows us to write ; (let ((gstack (push-gframe 'rewrite bkptr term alist obj))) ; ...) ; without actually doing any conses if we are not maintaining the goal stack. ; Notice that it conses the new frame onto the value of the variable gstack, so ; to use this macro that variable must be the gstack. ; Observe the use of list* below. Thus, the :args component of the frame built ; is a DOTTED list of the args provided, i.e., the last arg is in the final ; cdr, not the final cadr. Thus, (push-gframe 'rewrite 3 'a 'b 'c 'd) builds a ; frame with :args '(a b c . d). Note in particular the effect when only one ; arg is provided: (push-gframe 'rewrite 3 'a) builds a frame with :args 'a. ; One might wish in this case that the field name were :arg. #+acl2-loop-only `(cond ((or (f-get-global 'gstackp state) (f-get-global 'dmrp state)) (cons (make gframe :sys-fn ,sys-fn :bkptr ,bkptr :args (list* ,@args)) gstack)) (t nil)) #-acl2-loop-only `(progn (when (or (f-get-global 'gstackp state) (f-get-global 'dmrp state)) (setq *deep-gstack* (cons (make gframe :sys-fn ,sys-fn :bkptr ,bkptr :args (list* ,@args)) gstack)) (when (f-get-global 'dmrp state) (dmr-display)) *deep-gstack*))) #-acl2-loop-only (defparameter *saved-deep-gstack* nil) (defmacro initial-gstack (sys-fn bkptr &rest args) ; This macro is just (push-gframe sys-fn bkptr ,@args) except it is done on an ; empty gstack. Thus, it builds an initial gstack with the top-most frame as ; specified. The frame is built by push-gframe, so all frames are built by ; that macro. ; This is also a convenient place to reset *add-polys-counter*, which is used ; by dmr-string. `(let ((gstack nil)) #-acl2-loop-only (setq *saved-deep-gstack* nil) #-acl2-loop-only (setq *add-polys-counter* 0) (push-gframe ,sys-fn ,bkptr ,@args))) (defun tilde-@-bkptr-phrase (calling-sys-fn called-sys-fn bkptr) ; Warning: Keep this in sync with tilde-@-bkptr-string. ; This function builds a ~@ phrase explaining how two adjacent frames ; are related, given the calling sys-fn, the called sys-fn and the ; bkptr supplied by the caller. See cw-gframe for the use of this ; phrase. (case called-sys-fn (rewrite (cond ((integerp bkptr) (cond ((eq calling-sys-fn 'rewrite-with-lemma) (msg " the atom of the ~n0 hypothesis" (list bkptr))) ((eq calling-sys-fn 'simplify-clause) (msg " the atom of the ~n0 literal" (list bkptr))) (t (msg " the ~n0 argument" (list bkptr))))) ((consp bkptr) (msg " the rhs of the ~n0 hypothesis" (list (cdr bkptr)))) ((symbolp bkptr) (case bkptr (body " the body") (lambda-body " the lambda body") (rewritten-body " the rewritten body") (expansion " the expansion") (equal-consp-hack-car " the equality of the cars") (equal-consp-hack-cdr " the equality of the cdrs") (rhs " the rhs of the conclusion") (meta " the result of the metafunction") (nth-update " the result of the nth/update rewriter") (multiply-alists2 " the product of two polys") (forced-assumption " a forced assumption") (proof-checker " proof-checker top level") (otherwise (er hard 'tilde-@-bkptr-phrase "When ~x0 calls ~x1 we get an unrecognized ~ bkptr, ~x2." calling-sys-fn called-sys-fn bkptr)))) (t (er hard 'tilde-@-bkptr-phrase "When ~x0 calls ~x1 we get an unrecognized bkptr, ~x2." calling-sys-fn called-sys-fn bkptr)))) ((rewrite-with-lemma setup-simplify-clause-pot-lst simplify-clause add-terms-and-lemmas non-linear-arithmetic synp) "") (t (er hard 'tilde-@-bkptr-phrase "When ~x0 calls ~x1 we get an unrecognized bkptr, ~x2." calling-sys-fn called-sys-fn bkptr)))) (defun cw-gframe (i calling-sys-fn frame evisc-tuple) ; Warning: Keep this in sync with dmr-interp. ; This prints a gframe, frame, which is known to be frame number i and ; was called by calling-sys-fn. (case (access gframe frame :sys-fn) (simplify-clause ; We are tempted to ignore evisc-tuple in this case and print the whole clause. ; We have seen situations where we print ellipses after the 4th literal of the ; clause and then say that the next frame is simplifying the "fifth literal." ; On the other hand, we have seen huge clauses bring cw-gframe to its knees. ; So we compromise by using the evisc-tuple supplied. (cw "~x0. Simplifying the clause~% ~Y12" i (access gframe frame :args) evisc-tuple)) (setup-simplify-clause-pot-lst (cw "~x0. Setting up the linear pot list for the clause~% ~Y12" i (access gframe frame :args) evisc-tuple)) (rewrite (let ((term (car (access gframe frame :args))) (alist (cadr (access gframe frame :args))) (obj (cddr (access gframe frame :args)))) (cw "~x0. Rewriting (to ~@6)~@1,~% ~Y23,~#4~[~/ under the substitution~%~*5~]" i (tilde-@-bkptr-phrase calling-sys-fn 'rewrite (access gframe frame :bkptr)) term evisc-tuple (if alist 1 0) (tilde-*-alist-phrase alist evisc-tuple 5) (cond ((eq obj nil) "falsify") ((eq obj t) "establish") (t "simplify"))))) (rewrite-with-lemma (cw "~x0. Attempting to apply ~F1 to~% ~Y23" i (access rewrite-rule (cdr (access gframe frame :args)) :rune) (car (access gframe frame :args)) evisc-tuple)) (add-terms-and-lemmas (cw "~x0. Attempting to apply linear arithmetic to ~@1 the term ~ list~% ~Y23" i (let ((obj (cdr (access gframe frame :args)))) (cond ((eq obj nil) "falsify") ((eq obj t) "establish") (t "simplify"))) (car (access gframe frame :args)) evisc-tuple)) (non-linear-arithmetic (cw "~x0. Attempting to apply non-linear arithmetic to the list of ~ ~x1 var~#2~[~/s~]:~% ~Y23" i (length (access gframe frame :args)) (access gframe frame :args) evisc-tuple)) (synp (let ((synp-fn (access gframe frame :args))) (cw "~x0. Entering ~x1 for hypothesis ~x2~%" i synp-fn (access gframe frame :bkptr)))) (otherwise (er hard 'cw-gframe "Unrecognized sys-fn, ~x0" (access gframe frame :sys-fn))))) (defun cw-gstack1 (i calling-sys-fn lst evisc-tuple) (cond ((null lst) nil) (t (prog2$ (cw-gframe i calling-sys-fn (car lst) evisc-tuple) (cw-gstack1 (1+ i) (access gframe (car lst) :sys-fn) (cdr lst) evisc-tuple))))) (defun cw-gstack-fn (evisc-tuple frames) ; And here is how we print the whole goal stack to the comment window. ; Note: I am unhappy about the use of the comment window here. It pre-dates ; the invention of wormhole and its undoable changes to state. I sometimes ; think I should make this function just print the stack to an arbitrary ; channel and in wormhole that can be *standard-co*. But I have bigger fish to ; fry right now, namely the use of wormhole to implement an apparently (but not ; actually) recursive break-lemma. So I'm leaving this little wart to think ; about later. ; Since this function is a hack anyhow, we implicitly refer to *deep-gstack* ; without passing it in. (let ((gstack #-acl2-loop-only *deep-gstack* #+acl2-loop-only nil) (ctx 'cw-gstack)) (cond ((null gstack) (cw "There is no gstack to print. If you have enabled stack monitoring ~ with ``:BRR t'' this is likely due to the loop you wish to ~ investigate occurring in so-called preprocessing, where monitoring ~ is not done, rather than in the rewriter proper. You may obtain ~ better results by replaying the problematic event with a hint ~ of:~%((\"Goal\" :DO-NOT '(preprocess)).~%See :DOC hints, in ~ particular the discussion of :DO-NOT.~%")) ((and evisc-tuple (not (standard-evisc-tuplep evisc-tuple))) (er hard ctx "Illegal :evisc-tuple argument to ~x0: ~x1. See :DOC cw-gstack." 'cw-gstack evisc-tuple)) ((not (or (null frames) (and (integerp frames) (< 0 frames)) (and (true-listp frames) (eql (length frames) 2) (natp (car frames)) (natp (cadr frames)) (<= (car frames) (cadr frames))))) (er hard ctx "Illegal :frames argument to ~x0: ~x1. See :DOC cw-gstack." 'cw-gstack frames)) (t (let ((start (cond ((or (null frames) (integerp frames)) 1) ((<= (car frames) (length gstack)) (car frames)) (t (length gstack))))) (cw-gstack1 start nil (cond ((null frames) (reverse gstack)) (t (let* ((rev-gstack (reverse gstack)) (len (length gstack)) (n (min (if (integerp frames) frames (cadr frames)) len))) (nthcdr (1- start) (take n rev-gstack))))) evisc-tuple)))))) (defmacro cw-gstack (&key (evisc-tuple 'nil evisc-tuplep) (frames 'nil)) (declare (xargs :guard t)) ":Doc-Section Other debug a rewriting loop or stack overflow~/ ~bv[] Example Forms: (cw-gstack) (cw-gstack :frames 10) ; show only the top 10 frames (cw-gstack :frames '(1 10)) ; same as above: show only frames 1 through 10 (cw-gstack :frames '(10 20)) ; show only frames 10 through 20 (cw-gstack :evisc-tuple (evisc-tuple 3 4 nil nil)) ; print with print-level 3 and print-length 4 (cw-gstack :evisc-tuple nil) ; print using default ``evisceration'', ; essentially the same as just above (cw-gstack :evisc-tuple '(nil 3 4 (hide))) ; same as just above~/ General Form: (cw-gstack :frames frames :evisc-tuple evisc-tuple) ~ev[] where ~c[:frames] and ~c[:evisc-tuple] are optional, but if they are supplied, their values are evaluated. The value of ~c[frames] should be either a natural number or a list of two natural numbers, the first less than the second; and the value of ~c[evisc-tuple] should be an evisc-tuple (~pl[evisc-tuple]). If ~c[:evisc-tuple] is omitted, then substructures deeper than 3 are replaced by ``~c[#]'' and those longer than 4 are replaced by ``~c[...]'', and terms of the form ~c[(hide ...)] are printed as ~c[]. Also ~pl[set-iprint] for an alternative to printing ``~c[#]'' and ``~c[...]''. Stack overflows may occur, perhaps caused by looping rewrite rules. In some Lisps, stack overflows may manifest themselves as segmentation faults, causing the entire ACL2 image to crash. Finding looping rewrite rules can be tricky, especially if you are using books supplied by other people. (However, ~pl[set-rewrite-stack-limit] for a way to avoid stack overflows caused by rewriter loops.) Normally, a stack overflow will cause the printing of an error message that suggests how to proceed. Just follow those instructions, and you will generally be able to see what is causing the loop. Suggestion: Once you have found the loop and fixed it, you should execute the ACL2 command ~c[:]~ilc[brr]~c[ nil], so that you don't slow down subsequent proof attempts.~/" `(cw-gstack-fn ,(if evisc-tuplep evisc-tuple '(term-evisc-tuple t state)) ,frames)) ; Essay on "Break-Rewrite" ; Essay on BRR ; We wish to develop the illusion of a recursive function we will call ; "break-rewrite". In particular, when a rule is to be applied by ; rewrite-with-lemma and that rule is monitored (i.e., its rune is on ; brr-monitored-runes) then we imagine the rule is actually applied by ; "break-rewrite", which is analogous to rewrite-with-lemma but instrumented to ; allow the user to watch the attempt to apply the rule. Rewrite-fncall is ; similarly affected. Because we find "break-rewrite" a tedious name (in ; connection with user-available macros for accessing context sensitive ; information) we shorten it to simply brr when we need a name that is ; connected with the implementation of "break-rewrite." There is no ; "break-rewrite" function -- its presence is an illusion -- and we reserve ; the string "break-rewrite" to refer to this mythical function. ; Rather than actually implement "break-rewrite" we sprinkle "break points" ; through the various rewrite functions. These break points are the functions ; brkpt1 and brkpt2. The reason we do this is so that we don't have to ; maintain two parallel versions of rewrite. It is not clear this is ; justification for what is a surprisingly complicated alternative. But since ; we haven't pursued any other approach, it is not clear that the complications ; are isolated in this one. ; The main complication is that if we really had a recursive "break-rewrite" ; then we could have local variables associated with each attempt to apply a ; given rule. This would allow us, for example, to set a variable early in ; "break-rewrite" and then test it late, without having to worry that recursive ; calls of "break-rewrite" in between will see the setting. An additional ; complication is that to interact with the user we must enter a wormhole and ; thus have no effect on the state. ; Our first step is to implement a slightly different interface to wormholes that ; will provide us with global variables that retain their values from one exit to ; the next entrance but that can be overwritten conveniently upon entrance. See ; brr-wormhole below. Assume that we have such a wormhole interface providing ; what we call "brr-globals." ; We use the notion of brr-globals to implement "brr-locals." Of course, what ; we implement is a stack. That stack is named brr-stack and it is a ; brr-global. By virtue of being a brr-global it retains its value from one ; call of brr-wormhole to the next. ; Imagine then that we have this stack. Its elements are frames. Each frame ; specifies the local bindings of various variables. Inside brkpt1 and brkpt2 ; we access these "brr-locals" via the top-most frame on the stack. Brkpt1 ; pushes a new frame, appropriately binding the locals. brkpt2 pops that frame ; when it exits "break-rewrite". ; For sanity, each frame will contain the gstack for the brkpt1 that built it. ; Any function accessing a brr-local will present its own gstack as proof that ; it is accessing the right frame. One might naively assume that the presented ; gstack will always be equal to the gstack in the top-most frame and that ; failure of this identity check might as well signal a hard error. How might ; this error occur? The most obvious route is that we have neglected to pop a ; frame upon exit from the virtual "break-rewrite", i.e., we have forgotten to ; call brkpt2 on some exit of rewrite-with-lemma. More devious is the ; possibility that brkpt2 was called but failed to pop because we have ; misinterpreted our various flags and locally monitored runes. These routes ; argue for a hard error because they ought never to occur and the error ; clearly indicates a coding mistake. But it is possible for the stack to get ; "out of sync" in an entirely user controlled way! ; Suppose we are in brkpt1. It has pushed a frame with the current gstack. ; The user, typing to "break-rewrite" (the brr-wormhole in brkpt1) invokes the ; theorem prover and we enter another brkpt1. It pushes its frame. The user ; issues the command to proceed (i.e., to attempt to establish the hypotheses). ; The inner brkpt1 is terminated and control returns to rewrite. Note that we ; are still in the inner "break-rewrite" because we are pursuing the hyps of ; the inner rule. Consistent with this note is the fact that the stack ; contains two frames, the top-most one being that pushed by the inner brkpt1. ; Control is flowing toward the inner brkpt2 where, normally, the user would ; see the results of trying to establish the inner hyps. But then the user ; aborts. Control is thrown to the outer brkpt1, because all of this action ; has occurred in response to a recursive invocation of the theorem prover from ; within that wormhole. But now the stack at that brkpt1 is out of sync: the ; gstack of the wormhole is different from the gstack in the top-most frame. ; So we see that this situation is unavoidable and must be handled gracefully. ; Therefore, to access the value of a brr-local we use a function which ; patiently looks up the stack until it finds the right frame. It simply ; ignores "dead" frames along the way. We could pop them off, but then we ; would have to side-effect state to update the stack. The way a frame binds ; local variables is simply in an alist. If a variable is not bound at the ; right frame we scan on up the stack looking for the next binding. Thus, ; variables inherit their bindings from higher levels of "break-rewrite" as ; though the function opened with (let ((var1 var1) (var2 var2) ...) ...). ; When we "pop a frame" we actually pop all the frames up to and including the ; one for the gstack presented to pop. Finally, we need the function that ; empties the stack. ; So much for the overview. We begin by implementing brr-wormholes and ; brr-globals. ; While a normal wormhole provides one "global variable" that persists over ; entries and exits (namely, in the wormhole data field of the ; wormhole-status), the brr-wormhole provides several. These are called ; "brr-globals." The implementation of brr-globals is in two places: entry to ; and exit from the wormhole. The entry modification is to alter the supplied ; form so that it first moves the variable values from the wormhole-input and ; previous wormhole-status vectors into true state global variables. See ; brr-wormhole. The exit modification is to provide exit-brr-wormhole which ; moves the final values of the globals to the wormhole-status vector to be ; preserved for the next entrance. ; NOTE: To add a new brr-global, look for all the functions containing the ; string "Note: To add a new brr-global" and change them appropriately. No ; other changes are necessary (except, of course, passing in the desired values ; for the new global and using it). (defun restore-brr-globals1 (name new-alist old-alist) ; Retrieve the value of name under new-alist, if a value is specified; ; otherwise use the value of name under old-alist. See brr-wormhole. (let ((pair (assoc-eq name new-alist))) (cond (pair (cdr pair)) (t (cdr (assoc-eq name old-alist)))))) (defun restore-brr-globals (state) ; We assign incoming values to the brr-globals. When brr-wormhole ; enters a wormhole, this function is the first thing that is done. See ; brr-wormhole. ; NOTE: To add a new brr-global, this function must be changed. (let ((new-alist (f-get-global 'wormhole-input state)) (old-alist (wormhole-data (f-get-global 'wormhole-status state)))) (pprogn (f-put-global 'brr-monitored-runes (restore-brr-globals1 'brr-monitored-runes new-alist old-alist) state) (f-put-global 'brr-stack (restore-brr-globals1 'brr-stack new-alist old-alist) state) (f-put-global 'brr-gstack (restore-brr-globals1 'brr-gstack new-alist old-alist) state) (f-put-global 'brr-alist (restore-brr-globals1 'brr-alist new-alist old-alist) state)))) (defun save-brr-globals (state) ; We collect into an alist all of the brr-globals and their current values and ; store that alist into the wormhole data field of (@ wormhole-status). When ; exiting from a brr-wormhole, this is the last thing that ought to be done. ; See exit-brr-wormhole. ; NOTE: To add a new brr-global, this function must be changed. (f-put-global 'wormhole-status (make-wormhole-status (f-get-global 'wormhole-status state) :ENTER (list (cons 'brr-monitored-runes (f-get-global 'brr-monitored-runes state)) (cons 'brr-stack (f-get-global 'brr-stack state)) (cons 'brr-gstack (f-get-global 'brr-gstack state)) (cons 'brr-alist (f-get-global 'brr-alist state)))) state)) (defun get-brr-global (var state) ; This function should be used whenever we wish to access a brr-global. That ; is, we should write (get-brr-global 'brr-stack state) instead of either ; (f-get-global 'brr-stack state) or (@ brr-stack), even those these ; alternative forms are equivalent when we are in a brr-wormhole. But if we ; are not in a brr-wormhole, these alternative forms might cause arbitrary lisp ; errors because the brr-globals are not (generally) bound outside of wormholes ; (though there is nothing to prevent us from using the same symbols as ; "normal" state globals -- their values would just be unavailable to us from ; within brr-wormholes because they get over-written upon entry to the ; wormhole.) Thus, this function checks that the variable really is bound and ; causes a hard error if it is not. That is generally an indication that a ; function intended to be used only inside wormholes is being called outside. ; NOTE: To add a new brr-global, this function must be changed. (cond ((eq (f-get-global 'wormhole-name state) 'brr) (case var (brr-monitored-runes (f-get-global 'brr-monitored-runes state)) (brr-stack (f-get-global 'brr-stack state)) (brr-gstack (f-get-global 'brr-gstack state)) (brr-alist (f-get-global 'brr-alist state)) (otherwise (illegal 'get-brr-global "Unrecognized brr-global, ~x0." (list (cons #\0 var)))))) (t (illegal 'get-brr-global "It is illegal to call get-brr-global unless you are ~ under break-rewrite and you are not. The argument to ~ get-brr-global was ~x0." (list (cons #\0 var)))))) (defun exit-brr-wormhole (state) ; This function should be called on every exit from a brr-wormhole. It saves ; the brr-globals into the wormhole-status to be preserved for future entries ; and then it returns (value :q) which will cause us to exit the wormhole. (pprogn (save-brr-globals state) (value :q))) (defmacro brr-wormhole (entry-lambda input-alist test-form aliases) ; A brr-wormhole is a particular kind of wormhole. A quick summary of the ; differences: ; (0) while our normal convention is that the entry code for all wormholes ; should be :ENTER, brr-wormholes really do use the :SKIP option and ; toggle between :SKIP and :ENTER frequently; the status of the ; brr-wormhole is thus (:key data), where data is the alist mapping ; brr-globals to their values as described below ; (1) brr-wormhole implements brr-global variables which are set ; from input-alist (or else retain their values from the ; last exit of the 'brr wormhole). ; (2) test-form returns (value t) or (value nil) indicating whether ; a break is to occur. ; (3) the LD specials are manipulated so that no output appears before ; test-form is eval'd and an error in the test-form throws you out of ; the wormhole. If the test-form returns (value nil), the wormhole ; entry/exit are entirely silent. `(wormhole 'brr ,entry-lambda ,input-alist `(pprogn (restore-brr-globals state) (er-progn (set-ld-keyword-aliases! ,,aliases) (set-ld-prompt 'brr-prompt state) ; The above reference to the function symbol brr-prompt is a little startling ; because we haven't defined it yet. But we will define it before we use this ; macro. (mv-let (erp val state) ,,test-form (cond (erp (exit-brr-wormhole state)) (val (er-progn (set-ld-error-action :continue state) ; The aliases had better ensure that every exit is via exit-brr-wormhole. (value :invisible))) (t (exit-brr-wormhole state)))))) :ld-prompt nil :ld-missing-input-ok nil :ld-pre-eval-filter :all :ld-pre-eval-print nil :ld-post-eval-print :command-conventions :ld-evisc-tuple nil :ld-error-triples t :ld-error-action :error :ld-query-control-alist nil :ld-verbose nil)) (defun initialize-brr-stack (state) ; This is a no-op that returns nil. But it has the secret side effect of ; setting the brr-global brr-stack to nil. We don't want to reset all the ; brr-globals: brr-monitored-runes should persist. The others are irrelevant ; because they will be assigned before they are read. (and (f-get-global 'gstackp state) (brr-wormhole '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) '((brr-stack . nil)) '(value nil) nil))) ; This completes the implementation of brr-wormholes (except that we must be sure to ; exit via exit-brr-wormhole always). ; We now move on to the implementation of brr-locals. (defun lookup-brr-stack (var-name stack) ; See the Essay on "Break-Rewrite". Stack is a list of frames. Each frame is ; of the form (gstack' . alist). We assoc-eq up the alists of successive ; frames until we find one binding var-name. We return the value with which ; var-name is paired, or else nil if no binding is found. (cond ((null stack) nil) (t (let ((temp (assoc-eq var-name (cdar stack)))) (cond (temp (cdr temp)) (t (lookup-brr-stack var-name (cdr stack)))))))) (defun clean-brr-stack1 (gstack stack) (cond ((null stack) nil) ((equal gstack (caar stack)) stack) (t (clean-brr-stack1 gstack (cdr stack))))) (defun clean-brr-stack (gstack stack) ; See the Essay on "Break-Rewrite". Stack is a list of frames. Each frame is ; of the form (gstack' . alist), where the frames are ordered so that each ; successive gstack' is at a higher level than the previous one. (But note ; that they do not ascend in increments of one. That is, suppose the ; top frame of stack is marked with gstack' and the next-to-top frame is ; marked with gstack''. Then gstack' is an extension of gstack'', i.e., ; some cdr of gstack' is gstack''. We sweep down stack looking for ; the frame marked by gstack. We return the stack that has this frame on ; top, or else we return nil. ; We used (Version_2.7 and earlier) to cause a hard error if we did ; not find a suitable frame because we thought it indicated a coding ; error. Now we just return the empty stack because this situation ; can arise through interrupt processing. Suppose we are in rewrite ; and push a new frame with brkpt1. We're supposed to get to brkpt2 ; eventually and pop it. An interrupt could prevent that, leaving the ; frame unpopped. Suppose that is the last time a brkpt occurs in ; that simplification. Then the old stack survives. Suppose the ; waterfall carries out an elim and then brings us back to ; simplification. Now the gstack is completely different but the ; preserved brr-stack in *wormhole-status-alist* is still the old one. ; Clearly, we should ignore it -- had no interrupt occurred it would ; have been popped down to nil. (let ((cleaned-stack (clean-brr-stack1 gstack stack))) (prog2$ (if (not (equal cleaned-stack stack)) (cw "~%~%Cryptic BRR Message 1: Sweeping dead frames off ~ brr-stack. If this occurs in a reproducible way ~ without your having to cause a console interrupt or ~ otherwise break into Lisp, please send a script to ~ the authors of ACL2. If, on the other hand, during ~ this proof you've caused a console interrupt and aborted ~ from the resulting Lisp break, it is likely that this is ~ a bit of routine BRR housekeeping.~%~%") ; If anybody ever reports the problem described above, it indicates ; that frames are being left on the brr-stack as though the ; pop-brr-stack-frame supposedly performed by brkpt2 is not being ; executed. This could leave the brr-stack arbitrarily wrong, as a ; non-nil stack could survive into the simplification of a subsequent, ; independent subgoal sharing no history at all with brr-gstack. nil) cleaned-stack))) (defun get-brr-local (var state) ; This function may be used inside code executed under "break-rewrite". It is ; NOT for use in general purpose calls of wormhole because it is involved with ; the local variable illusion associated with "break-rewrite". A typical use ; is (get-brr-local 'unify-subst state) which fetches the local binding of ; 'unify-subst in the frame of brr-stack that is labelled with the current ; brr-gstack. (let ((clean-stack (clean-brr-stack (get-brr-global 'brr-gstack state) (get-brr-global 'brr-stack state)))) (lookup-brr-stack var clean-stack))) (defun put-brr-local1 (gstack var val stack) ; See the Essay on "Break-Rewrite" and the comment in brr-@ above. We assign ; val to var in the frame labelled by gstack. This function returns the ; resulting stack but does not side-effect state (obviously). Dead frames at ; the top of the stack are removed by this operation. (let ((clean-stack (clean-brr-stack gstack stack))) (cons (cons gstack (put-assoc-eq var val (cdar clean-stack))) (cdr clean-stack)))) (defun put-brr-local (var val state) ; This function may be used inside code executed within "break-rewrite". It is ; NOT for use in general purpose calls of wormhole because it is involved with ; the local variable illusion associated with "break-rewrite". A typical use ; is (put-brr-local 'unify-subst val state) which stores val as the local ; binding of 'unify-subst in the frame of brr-stack that is labelled with the ; current brr-gstack. (f-put-global 'brr-stack (put-brr-local1 (get-brr-global 'brr-gstack state) var val (get-brr-global 'brr-stack state)) state)) (defun put-brr-local-lst (alist state) (cond ((null alist) state) (t (pprogn (put-brr-local (caar alist) (cdar alist) state) (put-brr-local-lst (cdr alist) state))))) (defun some-cdr-equalp (little big) ; We return t if some cdr of big, including big itself, is equal to little. (cond ((equal little big) t) ((null big) nil) (t (some-cdr-equalp little (cdr big))))) (defun push-brr-stack-frame (state) ; This function may be used inside code executed within "break-rewrite". It ; pushes the new frame, (gstack . alist) on the brr-stack, where gstack is the ; current value of (get-brr-global 'brr-gstack state) and alist is ; (get-brr-global 'brr-alist state). (let ((gstack (get-brr-global 'brr-gstack state)) (brr-stack (get-brr-global 'brr-stack state))) (cond ((or (null brr-stack) (and (not (equal (caar brr-stack) gstack)) (some-cdr-equalp (caar brr-stack) gstack))) (f-put-global 'brr-stack (cons (cons gstack (get-brr-global 'brr-alist state)) brr-stack) state)) (t (prog2$ (cw "~%~%Cryptic BRR Message 2: Discarding dead brr-stack. ~ If this occurs in a reproducible way without your having ~ to cause a console interrupt or otherwise break into Lisp, ~ please send a script to the authors of ACL2. If, on the ~ other hand, during this proof you've caused a console ~ interrupt and aborted from the resulting Lisp break, it is ~ likely that this is a bit of routine BRR housekeeping.~%~%") (f-put-global 'brr-stack (cons (cons gstack (get-brr-global 'brr-alist state)) nil) state)))))) (defun pop-brr-stack-frame (state) ; This function may be used inside code executed within "break-rewrite". It ; pops the top-most frame off the brr-stack. Actually, it pops all the frames ; up through the one labelled with the current brr-gstack. (f-put-global 'brr-stack (cdr (clean-brr-stack (get-brr-global 'brr-gstack state) (get-brr-global 'brr-stack state))) state)) (defun decode-type-alist (type-alist) ; Once upon a type we untranslated (caar type-alist) below. But ; tilde-*-substitution-phrase, which is the only function which sees the output ; of this function in our sources, does an untranslate. (cond ((null type-alist) nil) (t (cons (cons (caar type-alist) (decode-type-set (cadar type-alist))) (decode-type-alist (cdr type-alist)))))) (defun translate-break-condition (xterm ctx state) (er-let* ((term (translate xterm '(nil) nil t ctx (w state) state))) ; known-stobjs = t (user interface) (let* ((used-vars (all-vars term)) (bad-vars (set-difference-eq used-vars '(state)))) (cond (bad-vars (er soft ctx "The only variable allowed in a break condition ~ is STATE. Your form, ~x0, contains the ~ variable~#1~[~/s~] ~&2." xterm (if (cdr bad-vars) 1 0) bad-vars)) (t (value term)))))) (defun eval-break-condition (rune term ctx state) (cond ((equal term *t*) (value t)) (t (mv-let (erp okp latches) (ev term (list (cons 'state (coerce-state-to-object state))) state nil nil t) (declare (ignore latches)) (cond (erp (pprogn (error-fms nil ctx (car okp) (cdr okp) state) (er soft ctx "The break condition installed on ~x0 could not be ~ evaluated." rune))) (t (value okp))))))) (defconst *default-free-vars-display-limit* 30) (defmacro set-free-vars-display-limit (n) `(let ((n ,n)) (prog2$ (or (natp n) (er hard 'set-free-vars-display-limit "The argument to set-free-vars-display-limit should ~ evaluate to a natural number, but it was given an ~ argument that evaluated to ~x0." n)) (f-put-global 'free-vars-display-limit n state)))) (defun free-vars-display-limit (state) (if (f-boundp-global 'free-vars-display-limit state) (let ((val (f-get-global 'free-vars-display-limit state))) (if (or (natp val) (null val)) val *default-free-vars-display-limit*)) *default-free-vars-display-limit*)) (mutual-recursion (defun limit-failure-reason (failures-remaining failure-reason elided-p) (declare (xargs :guard (natp failures-remaining))) (case-match failure-reason ((hyp 'free-vars . alist) (cond ((zp failures-remaining) (mv 0 (list hyp 'free-vars 'elided) t)) ((eq (car alist) 'hyp-vars) (mv (1- failures-remaining) failure-reason elided-p)) (t (mv-let (new-failures-remaining new-alist elided-p) (limit-failure-reason-alist (1- failures-remaining) alist elided-p) (cond ((eql failures-remaining new-failures-remaining) ;optimization (mv failures-remaining failure-reason elided-p)) (t (mv new-failures-remaining (list* hyp 'free-vars new-alist) elided-p))))))) (& (mv (if (zp failures-remaining) failures-remaining (1- failures-remaining)) failure-reason elided-p)))) (defun limit-failure-reason-alist (failures-remaining alist elided-p) (cond ((null alist) (mv failures-remaining alist elided-p)) (t (mv-let (failures-remaining-1 failure-reason elided-p) (limit-failure-reason failures-remaining (cdar alist) elided-p) (mv-let (failures-remaining-2 rest-alist elided-p) (limit-failure-reason-alist failures-remaining-1 (cdr alist) elided-p) (mv failures-remaining-2 (cond ((and (not (zp failures-remaining)) (eql failures-remaining failures-remaining-2)) alist) ;optimization (t (cons (cond ((and (not (zp failures-remaining)) (eql failures-remaining failures-remaining-1)) (car alist)) ;optimization (t (cons (caar alist) failure-reason))) rest-alist))) elided-p)))))) ) (mutual-recursion (defun fix-free-failure-reason (failure-reason) ; See tilde-@-failure-reason-phrase. (case-match failure-reason ((& 'free-vars 'hyp-vars . &) failure-reason) ((bkptr 'free-vars . failure-reason-lst) (list* bkptr 'free-vars (fix-free-failure-reason-alist failure-reason-lst nil))) (& failure-reason))) (defun fix-free-failure-reason-alist (x acc) ; We deliberately reverse x as we fix it; see tilde-@-failure-reason-phrase. (cond ((endp x) acc) (t ; x is (cons (cons unify-subst failure-reason) &) (fix-free-failure-reason-alist (cdr x) (cons (cons (caar x) (fix-free-failure-reason (cdar x))) acc))))) ) (mutual-recursion (defun tilde-@-failure-reason-free-phrase (hyp-number alist level unify-subst evisc-tuple) ; Alist is a list of pairs (unify-subst . failure-reason). Level is initially ; 0 and increases as we dive into failure-reason. (cond ((null alist) "") (t (let ((new-unify-subst (caar alist)) (new-failure-reason (cdar alist))) (msg "~t0[~x1]~*2~|~@3~@4~@5" (if (< hyp-number 10) (* 4 level) (1- (* 4 level))) hyp-number (tilde-*-alist-phrase (alist-difference-eq new-unify-subst unify-subst) evisc-tuple (+ 4 (* 4 level))) (if (let ((fr (if (and (consp new-failure-reason) (eq (car new-failure-reason) 'cached)) (cdr new-failure-reason) new-failure-reason))) (and (consp fr) (integerp (car fr)) (or (not (and (consp (cdr fr)) (eq (cadr fr) 'free-vars))) (and (consp (cdr fr)) (consp (cddr fr)) (member-eq (caddr fr) '(hyp-vars elided)))))) "Failed because " "") (tilde-@-failure-reason-phrase1 new-failure-reason (1+ level) new-unify-subst evisc-tuple nil) (tilde-@-failure-reason-free-phrase hyp-number (cdr alist) level unify-subst evisc-tuple)))))) (defun tilde-@-failure-reason-phrase1 (failure-reason level unify-subst evisc-tuple free-vars-display-limit) (cond ((eq failure-reason 'time-out) "we ran out of time.") ((eq failure-reason 'loop-stopper) "it permutes a big term forward.") ((eq failure-reason 'too-many-ifs-pre-rewrite) "the unrewritten :RHS contains too many IFs for the given args.") ((eq failure-reason 'too-many-ifs-post-rewrite) "the rewritten :RHS contains too many IFs for the given args.") ((eq failure-reason 'rewrite-fncallp) "the :REWRITTEN-RHS is judged heuristically unattractive.") ((and (consp failure-reason) (integerp (car failure-reason))) (let ((n (car failure-reason))) (case (cdr failure-reason) (time-out (msg "we ran out of time while processing :HYP ~x0." n)) (ancestors (msg ":HYP ~x0 is judged more complicated than its ~ ancestors (type :ANCESTORS to see the ~ ancestors and :PATH to see how we got to this ~ point)." n)) (known-nil (msg ":HYP ~x0 is known nil by type-set." n)) (otherwise (cond ((eq (cadr failure-reason) 'free-vars) (mv-let (failures-remaining failure-reason elided-p) (if free-vars-display-limit (limit-failure-reason free-vars-display-limit failure-reason nil) (mv nil failure-reason nil)) (declare (ignore failures-remaining)) (cond ((eq (caddr failure-reason) 'hyp-vars) (msg ":HYP ~x0 contains free variables ~&1, for which no ~ suitable bindings were found." n (set-difference-equal (cdddr failure-reason) (strip-cars unify-subst)))) ((eq (caddr failure-reason) 'elided) (msg ":HYP ~x0 contains free variables (further reasons ~ elided, as noted above)." n)) (t (msg "~@0~@1" (if (eql level 1) (msg ":HYP ~x0 contains free variables. The ~ following display summarizes the attempts to ~ relieve hypotheses by binding free variables; ~ see :DOC free-variables.~|~@1~%" n (if elided-p (msg " Also, if you want to avoid ~ ``reasons elided'' notes below, then ~ evaluate (assign free-vars-display-limit ~ k) for larger k (currently ~x0, default ~ ~x1); then :failure-reason will show the ~ first k or so failure sub-reasons before ~ eliding. Note that you may want to do ~ this evaluation outside break-rewrite, ~ so that it persists.~|" free-vars-display-limit *default-free-vars-display-limit*) "")) "") (tilde-@-failure-reason-free-phrase n (cddr failure-reason) level unify-subst evisc-tuple)))))) ((eq (cadr failure-reason) 'backchain-limit) ; (cddr failure-reason) is the backchain-limit at the point of ; failure. But that number was calculated by successive additions of ; backchain limits for individual rules and we have no record of which ; rules were involved in this calculation. (msg "a backchain limit was reached while processing :HYP ~x0 ~ (but we cannot tell you which rule imposed the limit)" n)) ((eq (cadr failure-reason) 'rewrote-to) (msg ":HYP ~x0 rewrote to ~X12." n (cddr failure-reason) evisc-tuple)) ((member-eq (cadr failure-reason) '(syntaxp syntaxp-extended bind-free bind-free-extended)) (let ((synp-fn (case (cadr failure-reason) (syntaxp-extended 'syntaxp) (bind-free-extended 'bind-free) (otherwise (cadr failure-reason))))) (cond ((caddr failure-reason) (msg "the evaluation of the ~x0 test in :HYP ~x1 ~ produced the error ``~@2''" synp-fn n (cadddr failure-reason))) (t (msg "the ~x0 test in :HYP ~x1 evaluated to NIL." synp-fn n))))) (t (er hard 'tilde-@-failure-reason-phrase1 "Unrecognized failure reason, ~x0." failure-reason))))))) ((and (consp failure-reason) (eq (car failure-reason) 'cached)) (msg "~@0~|*NOTE*: This failure was cached earlier. Use the hint ~ :RW-CACHE-STATE ~x1 to disable failure caching." (tilde-@-failure-reason-phrase1 (cdr failure-reason) level unify-subst evisc-tuple free-vars-display-limit) nil)) (t (er hard 'tilde-@-failure-reason-phrase1 "Unrecognized failure reason, ~x0." failure-reason)))) ) (defun tilde-@-failure-reason-phrase (failure-reason level unify-subst evisc-tuple free-vars-display-limit) ; In relieve-hyps1 we store a 'free-vars failure reason in which we formerly ; reversed a "failure-reason-lst", which is really an alist mapping extended ; unify-substs to failure reasons. Now, we save consing by delaying such ; reversal until the relatively rare times that we are ready to display the ; failure reason. (tilde-@-failure-reason-phrase1 (fix-free-failure-reason failure-reason) level unify-subst evisc-tuple free-vars-display-limit)) (defun stuff-standard-oi (cmds state) ; This function appends cmds (which must be a true list) onto standard-oi. We ; act as though the entire system maintains the invariant that when standard-oi ; is a symbol ld-pre-eval-print is nil and when it is a list ld-pre-eval-print ; is t. We maintain it here. This has the convenient effect that -- if the ; condition is true now -- then the commands in cmds will be printed before ; they are executed and that when we get back down to *standard-oi* printing ; will be shut off. There is no guarantee that this condition is invariant. ; The user might set ld-pre-eval-print at will. The worse that will happen is ; undesirable pre-eval print behavior. (declare (xargs :guard (true-listp cmds))) (cond ((null cmds) state) (t (pprogn (f-put-global 'ld-pre-eval-print t state) (f-put-global 'standard-oi (append cmds (cond ((symbolp (f-get-global 'standard-oi state)) (cons '(set-ld-pre-eval-print nil state) (f-get-global 'standard-oi state))) (t (f-get-global 'standard-oi state)))) state))))) (deflabel break-rewrite :doc ":Doc-Section Break-Rewrite the read-eval-print loop entered to ~il[monitor] rewrite rules~/ ACL2 allows the user to ~il[monitor] the application of rewrite rules. When ~il[monitor]ed rules are about to be tried by the rewriter, an interactive break occurs and the user is allowed to watch and, in a limited sense, control the attempt to apply the rule. This interactive loop, which is technically just a call of the standard top-level ACL2 read-eval-print loop, ~ilc[ld], on a ``~il[wormhole] ~il[state]'' (~pl[wormhole]), is called ``break-rewrite.'' While in break-rewrite, certain keyword commands are available for accessing information about the context in which the lemma is being tried. These keywords are called break-rewrite ``commands.'' Also ~pl[dmr] (Dynamically Monitor Rewrites) for a related utility, which allows you to watch progress of the rewriter in real time. To abort from inside break-rewrite at any time, execute For further information, see the related ~c[:]~ilc[doc] topics listed below. ~terminal[For broad information on the behavior of break-rewrite, type :more.]~/ As explained in the documentation for ~ilc[monitor], it is possible to cause the ACL2 rewriter to ~il[monitor] the attempted application of selected rules. When such a rule is about to be tried, the rewriter evaluates its break condition and if the result is non-~c[nil], break-rewrite is entered. Break-rewrite permits the user to inspect the current ~il[state] by evaluating break-rewrite commands. Type ~c[:]~ilc[help] in break-rewrite to see what the break-rewrite commands are. However, break-rewrite is actually just a call of the general ACL2 read-eval-print loop, ~ilc[ld], on a certain ~il[state] and the break-rewrite commands are simply aliases provided by ~c[ld-keyword-aliases] ~il[table] (~pl[ld-keyword-aliases]). ~l[ld] for details about this read-eval-print loop. Thus, with a few exceptions, anything you can do at the ACL2 top-level can be done within break-rewrite. For example, you can evaluate arbitrary expressions, use the keyword command hack, access ~il[documentation], print ~il[events], and even define functions and prove theorems. However, the ``certain ~il[state]'' upon which ~ilc[ld] was called is a ``~il[wormhole] ~il[state]'' (~pl[wormhole]) because break-rewrite is not allowed to have any effect upon the behavior of rewrite. What this means, very roughly but understandably, is that break-rewrite operates on a copy of the ~il[state] being used by rewrite and when break-rewrite exits the ~il[wormhole] closes and the ~il[state] ``produced'' by break-rewrite disappears. Thus, break-rewrite lets you query the state of the rewriter and even do experiments involving proofs, etc., but these experiments have no effect on the ongoing proof attempt. In particular: Note that the output from break-rewrite is sometimes abbreviated by default, such as for the term causing the break. This can be controlled by setting the ~c[:term] evisc-tuple; ~pl[set-evisc-tuple]. (Another option: use iprinting. ~l[set-iprint].) But as noted above, if you use ~c[set-evisc-tuple] from inside the break-rewrite ~il[wormhole], its effect will disappear when you exit the break. So you might want to issue a ~c[set-evisc-tuple] command from the top level, outside break-rewrite. When you first enter break-rewrite a simple herald is printed such as: ~bv[] (3 Breaking (:rewrite lemma12) on (delta a (+ 1 j)): ~ev[] The integer after the open parenthesis indicates the depth of nested break-rewrite calls. In this discussion we use ~c[3] consistently for this integer. Unless you abort or somehow enter unbalanced parentheses into the script, the entire session at a given depth will be enclosed in balanced parentheses, making it easy to skip over them in Emacs. You then will see the break-rewrite ~il[prompt]: ~bv[] 3 ACL2 !> ~ev[] The leading integer is, again, the depth. Because breaks often occur recursively it is convenient always to know the level with which you are interacting. You may type arbitrary commands as in the top-level ACL2 loop. For example, you might type: ~bv[] 3 ACL2 !>:help ~ev[] or ~bv[] 3 ACL2 !>:pe lemma12 ~ev[] More likely, upon entering break-rewrite you will determine the context of the attempted application. Here are some useful commands: ~bv[] 3 ACL2 >:target ; the term being rewritten 3 ACL2 >:unify-subst ; the unifying substitution 3 ACL2 >:path ; the stack of goals pursued by the rewriter ; starting at the top-level clause being simplified ; and ending with the current application ~ev[] At this point in the interaction the system has not yet tried to apply the ~il[monitor]ed rule. That is, it has not tried to establish the hypotheses, considered the heuristic cost of backchaining, rewritten the right-hand side of the conclusion, etc. When you are ready for it to try the rule you can type one of several different ``proceed'' commands. The basic proceed commands are ~c[:ok], ~c[:go], and ~c[:eval]. ~bv[] :ok ~ev[] exits break-rewrite without further interaction. When break-rewrite exits it prints ``~c[3)]'', closing the parenthesis that opened the level ~c[3] interaction. ~bv[] :go ~ev[] exits break-rewrite without further interaction, but prints out the result of the application attempt, i.e., whether the application succeeded, if so, what the ~c[:target] term was rewritten to, and if not why the rule was not applicable. ~bv[] :eval ~ev[] causes break-rewrite to attempt to apply the rule but interaction at this level of break-rewrite resumes when the attempt is complete. When control returns to this level of break-rewrite a message indicating the result of the application attempt (just as in ~c[:go]) is printed, followed by the ~il[prompt] for additional user input. Generally speaking, ~c[:ok] and ~c[:go] are used when the break in question is routine or uninteresting and ~c[:eval] is used when the break is one that the user anticipates is causing trouble. For example, if you are trying to determine why a lemma isn't being applied to a given term and the ~c[:target] of the current break-rewrite is the term in question, you would usually ~c[:eval] the rule and if break-rewrite reports that the rule failed then you are in a position to determine why, for example by carefully inspecting the ~c[:type-alist] of governing assumptions or why some hypothesis of the rule could not be established. It is often the case that when you are in break-rewrite you wish to change the set of ~il[monitor]ed ~il[rune]s. This can be done by using ~c[:]~ilc[monitor] and ~c[:]~ilc[unmonitor] as noted above. For example, you might want to ~il[monitor] a certain rule, say ~c[hyp-reliever], just when it is being used while attempting to apply another rule, say ~c[main-lemma]. Typically then you would ~il[monitor] ~c[main-lemma] at the ACL2 top-level, start the proof-attempt, and then in the break-rewrite in which ~c[main-lemma] is about to be tried, you would install a ~il[monitor] on ~c[hyp-reliever]. If during the ensuing ~c[:eval] ~c[hyp-reliever] is broken you will know it is being used under the attempt to apply ~c[main-lemma]. However, once ~c[hyp-reliever] is being ~il[monitor]ed it will be ~il[monitor]ed even after ~c[main-lemma] has been tried. That is, if you let the proof attempt proceed then you may see many other breaks on ~c[hyp-reliever], breaks that are not ``under'' the attempt to apply ~c[main-lemma]. One way to prevent this is to ~c[:eval] the application of ~c[main-lemma] and then ~c[:]~ilc[unmonitor] ~c[hyp-reliever] before exiting. But this case arises so often that ACL2 supports several additional ``flavors'' of proceed commands. ~c[:Ok!], ~c[:go!], and ~c[:eval!] are just like their counterparts (~c[:ok], ~c[:go], and ~c[:eval], respectively), except that while processing the rule that is currently broken no ~il[rune]s are ~il[monitor]ed. When consideration of the current rule is complete, the set of ~il[monitor]ed ~il[rune]s is restored to its original setting. ~c[:Ok$], ~c[:go$], and ~c[:eval$] are similar but take an additional argument which must be a list of ~il[rune]s. An example usage of ~c[:eval$] is ~bv[] 3 ACL2 !>:eval$ ((:rewrite hyp-reliever)) ~ev[] These three commands temporarily install unconditional breaks on the ~il[rune]s listed, proceed with the consideration of the currently broken rule, and then restore the set of ~il[monitor]ed rules to its original setting. Thus, there are nine ways to proceed from the initial entry into break-rewrite although we often speak as though there are two, ~c[:ok] and ~c[:eval], and leave the others implicit. We group ~c[:go] with ~c[:ok] because in all their flavors they exit break-rewrite without further interaction (at the current level). All the flavors of ~c[:eval] require further interaction after the rule has been tried. To abort a proof attempt and return to the top-level of ACL2 you may at any time type ~c[(a!)] followed by a carriage return. If you are not in a raw Lisp break, you may type ~c[:a!] instead. The utility ~c[p!] is completely analogous to ~c[a!] except that it pops up only one ~ilc[ld] level. If you have just entered the break-rewrite loop, this will pop you out of that loop, back to the proof. ~l[a!] and ~pl[p!]. We now address ourselves to the post-~c[:eval] interaction with break-rewrite. As noted, that interaction begins with break-rewrite's report on the results of applying the rule: whether it worked and either what it produced or why it failed. This information is also printed by certain keyword commands available after ~c[:eval], namely ~c[:wonp], ~c[:rewritten-rhs], and ~c[:failure-reason]. In addition, by using ~ilc[brr@] (~pl[brr@]) you can obtain this information in the form of ACL2 data objects. This allows the development of more sophisticated ``break conditions''; ~pl[monitor] for examples. In this connection we point out the macro form ~c[(ok-if term)]. ~l[ok-if]. This command exits break-rewrite if ~c[term] evaluates to non-~c[nil] and otherwise does not exit. Thus it is possible to define macros that provide other kinds of exits from break-rewrite. The only way to exit break-rewrite after ~c[:eval] is ~c[:ok] (or, equivalently, the use of ~ilc[ok-if]). ACL2 users who wish to know more about break-rewrite so that they can develop more convenient ways to ~il[monitor] rules are encouraged to speak to J Moore. The rest of this ~il[documentation] discusses a few implementation details of break-rewrite and may not be interesting to the typical user. There is no ACL2 function named break-rewrite. It is an illusion created by appropriate calls to two functions named ~c[brkpt1] and ~c[brkpt2]. As previously noted, break-rewrite is ~ilc[ld] operating on a ~il[wormhole] ~il[state]. One might therefore wonder how break-rewrite can apply a rule and then communicate the results back to the rewriter running in the external ~il[state]. The answer is that it cannot. Nothing can be communicated through a ~il[wormhole]. In fact, ~c[brkpt1] and ~c[brkpt2] are each calls of ~ilc[ld] running on ~il[wormhole] ~il[state]s. ~c[Brkpt1] implements the pre-~c[:eval] break-rewrite and ~c[brkpt2] implements the post-~c[:eval] break-rewrite. The rewriter actually calls ~c[brkpt1] before attempting to apply a rule and calls ~c[brkpt2] afterwards. In both cases, the rewriter passes into the ~il[wormhole] the relevant information about the current context. Logically ~c[brkpt1] and ~c[brkpt2] are no-ops and ~ilc[rewrite] ignores the ~c[nil] they return. But while control is in them the execution of ~ilc[rewrite] is suspended and cannot proceed until the break-rewrite interactions complete. This design causes a certain anomoly that might be troubling. Suppose that inside break-rewrite before ~c[:evaling] a rule (i.e., in the ~c[brkpt1] ~il[wormhole] ~il[state]) you define some function, ~c[foo]. Suppose then you ~c[:eval] the rule and eventually control returns to break-rewrite (i.e., to ~c[brkpt2] on a ~il[wormhole] ~il[state] with the results of the application in it). You will discover that ~c[foo] is no longer defined! That is because the ~il[wormhole] ~il[state] created during your ~c[pre-:eval] interaction is lost when we exit the ~il[wormhole] to resume the proof attempt. The post-~c[:eval] ~il[wormhole] ~il[state] is in fact identical to the initial pre-~c[:eval] ~il[state] (except for the results of the application) because ~ilc[rewrite] did not change the external ~il[state] and both ~il[wormhole] ~il[state]s are copies of it. A similar issue occurs with the use of ~il[trace] utilities: all effects of calling ~ilc[trace$] and ~ilc[untrace$] are erased when you proceed from a break in the break-rewrite loop. There is a lot more to know about break-rewrite, most of which is fairly easy to learn from looking at the code, since it is all expressed in ACL2. Feel free to ask questions of J Moore.") (deflabel break-lemma :doc ":Doc-Section Break-Rewrite a quick introduction to breaking rewrite rules in ACL2~/ ~bv[] Example: :brr t ; if you haven't done that yet :monitor (:rewrite lemma12) t ; to install a break point on the ; rule named (:rewrite lemma12) ~ev[]~/ ACL2 does not support Nqthm's ~c[break-lemma] but supports a very similar and more powerful break facility. Suppose some proof is failing; apparently some particular rule is not being used and you wish to learn why. Then you need the ACL2 ~il[break-rewrite] facility. ~l[break-rewrite] and all of its associated ~c[:]~ilc[doc] topics for details. The following basic steps are required. (1) To enable the ``break rewrite'' feature, you must first execute ~bv[] ACL2 !>:brr t ~ev[] at the top-level of ACL2. Equivalently, evaluate ~c[(brr t)]. ~il[Break-rewrite] stays enabled until you disable it with ~c[(brr nil)]. When ~il[break-rewrite] is enabled the ACL2 rewriter will run slower than normal but you will be able to ~il[monitor] the attempts to apply specified rules. (2) Decide what ~il[rune]s (~pl[rune]) you wish to ~il[monitor]. For example, you might want to know why ~c[(:rewrite lemma12 . 2)] is not being used in the attempted proof. That, by the way, is the name of the second rewrite rule generated from the event named ~c[lemma12]. The command ~bv[] ACL2 !>:monitor (:rewrite lemma12 . 2) t ~ev[] will install an ``unconditional'' break point on that rule. The ``~c[t]'' at the end of the command means it is unconditional, i.e., a break will occur every time the rule is tried. ACL2 supports conditional breaks also, in which case the ~c[t] is replaced by an expression that evaluates to non-~c[nil] when you wish for a break to occur. ~l[monitor]. The above keyword command is, of course, equivalent to ~bv[] ACL2 !>(monitor '(:rewrite lemma12 . 2) t) ~ev[] which you may also type. You may install breaks on as many rules as you wish. You must use ~ilc[monitor] on each rule. You may also change the break condition on a rule with ~ilc[monitor]. Use ~ilc[unmonitor] (~pl[unmonitor]) to remove a rule from the list of ~il[monitor]ed rules. (3) Then try the proof again. When a ~il[monitor]ed rule is tried by the rewriter you will enter an interactive break, called ~il[break-rewrite]. ~l[break-rewrite] for a detailed description. Very simply, ~il[break-rewrite] lets you inspect the context of the attempted application both before and after the attempt. When ~il[break-rewrite] is entered it will print out the ``target'' term being rewritten. If you type ~c[:go] ~il[break-rewrite] will try the rule and then exit, telling you (a) whether the rule was applied, (b) if so, how the target was rewritten, and (c) if not, why the rule failed. There are many other commands. ~l[brr-commands]. (4) When you have finished using the ~il[break-rewrite] feature you should disable it to speed up the rewriter. You can disable it with ~bv[] ACL2 !>:brr nil ~ev[] The list of ~il[monitor]ed rules and their break conditions persists but is ignored. If you enable ~il[break-rewrite] later, the list of ~il[monitor]ed rules will be displayed and will be used again by rewrite. You should disable the ~il[break-rewrite] feature whenever you are not intending to use it, even if the list of ~il[monitor]ed rules is empty, because the rewriter is slowed down as long as ~il[break-rewrite] is enabled. If you get a stack overflow, ~pl[cw-gstack].") (deflabel brr-commands :doc ":Doc-Section Break-Rewrite ~il[Break-Rewrite] Commands~/ ~bv[] :a! abort to ACL2 top-level :p! pop one level (exits a top-level break-rewrite loop) :target term being rewritten :unify-subst substitution making :lhs equal :target :hyps hypotheses of the rule :hyp i ith hypothesis of the rule :lhs left-hand side of rule's conclusion :rhs right-hand side of rule's conclusion :type-alist type assumptions governing :target :initial-ttree ttree before :eval (~pl[ttree]) :ancestors negations of backchaining hypotheses being pursued :wonp indicates if application succeed (after :eval) :rewritten-rhs rewritten :rhs (after :eval) :final-ttree ttree after :eval (~pl[ttree]) :failure-reason reason rule failed (after :eval) :path rewrite's path from top clause to :target :frame i ith frame in :path :top top-most frame in :path :ok exit break :go exit break, printing result :eval try rule and re-enter break afterwards :ok! :ok but no recursive breaks :go! :go but no recursive breaks :eval! :eval but no recursive breaks :ok$ runes :ok with runes monitored during recursion :go$ runes :go with runes monitored during recursion :eval$ runes :eval with runes monitored during recursion :help this message :standard-help :help message from ACL2 top-level ~ev[]~/ ~il[Break-rewrite] is just a call of the standard ACL2 read-eval-print loop, ~ilc[ld], on a ``~il[wormhole]'' ~il[state]. Thus, you may execute most commands you might normally execute at the top-level of ACL2. However, all ~il[state] changes you cause from within ~il[break-rewrite] are lost when you exit or ~c[:eval] the rule. You cannot modify ~il[stobj]s from within the break. ~l[break-rewrite] for more details and ~pl[ld] for general information about the standard ACL2 read-eval-print loop.") (defdoc dmr ":Doc-Section Other dynamically monitor rewrites and other prover activity~/ In addition to utilities that allow you to set breakpoints or print rewriting information to the screen ~-[] ~pl[break-rewrite] ~-[] ACL2 provides a utility for watching the activity of the rewriter and some other proof processes, in real time. This utility is called ``dmr'', which is an acronym for ``dynamically monitor rewrites''. The utility comes in two parts: an ACL2 component that frequently updates a file (the ``dmr file'') containing the relevant information, and an Emacs component that frequently updates the an Emacs buffer (the ``dmr buffer'') with the contents of that file. Other editors could, in principle, be programmed to display that file; anyone developing such a capability is invited to contribute it to the ACL2 community. The dmr utility can be extremely helpful for expensive proofs, especially when ACL2 is not providing any output to the terminal. The ~il[break-rewrite] and ~ilc[accumulated-persistence] utilities may be a bit easier to use, so you might want to try those first. But the dmr utility can be a very helpful debugging aide, as it can visually give you a sense of where ACL2 is spending its time. The Emacs portion of this utility is already loaded if you load the distributed Emacs file ~c[emacs/emacs-acl2.el]. Otherwise, invoke the following Emacs command, say by typing ~c[Control-X Control-E] after the right parenthesis, where ~c[DIR] is the directory of your ACL2 distribution. ~bv[] (load \"/emacs/monitor.el\") ; absolute pathnames might work best ~ev[] You only need to do that once. Then each time you want to observe the rewriter in action, invoke the following to see it displayed in a buffer, which we call the ``dmr buffer'': ~bv[] Control-t 1 ~ev[] But first you will need to enable monitoring at the ACL2 level: ~bv[] (dmr-start) ~ev[] Monitoring has some cost. So if you have started it, then at some point you may want to turn it off when not using it. Any time the dmr buffer (generally called ~c[\"acl2-dmr-\"]) is not visible, Emacs monitoring is turned off. You can also turn off Emacs monitoring explicitly, with: ~bv[] Control-t 2 ~ev[] At the ACL2 level you can disable monitoring as follows: ~bv[] (dmr-stop) ~ev[]~/ ~em[Interpreting the dmr buffer display.] We explain the dmr buffer display by way of the following example. It is a snapshot of a dmr buffer taken from one of the community books, ~c[books/workshops/2004/legato/support/proof-by-generalization-mult.lisp]. ~bv[] 0. (DEFTHM . WP-ZCOEF-G-MULTIPLIES) 1. SIMPLIFY-CLAUSE 2. Rewriting (to simplify) the atom of literal 18; argument(s) 1 4. Rewriting (to simplify) the expansion; argument(s) 3|2 7. Applying (:DEFINITION WP-ZCOEF-G) * 8. Rewriting (to simplify) the rewritten body; argument(s) 2|1|2|2 * 13. Applying (:REWRITE MOD-ZERO . 2) * 14. Rewriting (to establish) the atom of hypothesis 4 * 15. Applying (:META META-INTEGERP-CORRECT) ~ev[] Each line indicates an ACL2 activity that leads to the activity shown on the line just below it. Moreover, lines are sometimes collapsed to make the display more compact. Consider for example the first few lines. Above, we are proving a theorem named ~c[WP-ZCOEF-G-MULTIPLIES]. Lines 1 and 2 show the clause simplification process invokes the rewriter on the 18th literal. (Recall that a clause is a disjunction of literals; for example the clause ~c[{(NOT A), (NOT B), C}] would be displayed as ~c[(IMPLIES (AND A B) C)].) This 18th literal mentioned on line 2 is a function call ~c[(f arg1 ...)], and ``~c[argument(s) 1]'' indicates that the rewriter, which works inside-out, is considering the first argument (``~c[arg1]''). Thus the display could instead have shown the following. ~bv[] 2. Rewriting (to simplify) the atom of literal 18 3. Rewriting (to simplify) the first argument 4. Rewriting (to simplify) the expansion; argument(s) 3|2 ~ev[] But it saved space to combine lines 2 and 3. Line 4 suggests that the above ~c[arg1] is a function call that has been opened up because of an ~c[:expand] hint or, perhaps, an expansion directed explicitly by the prover (as can happen during induction). The annotation ``~c[argument(s) 3|2]'' then indicates that the rewriter is diving into the third argument of the expansion, and then into the second argument of that. Let us call the result ~c[term7] (since it is the one to be considered on line 7). Now consider the next two lines: ~bv[] 7. Applying (:DEFINITION WP-ZCOEF-G) * 8. Rewriting (to simplify) the rewritten body; argument(s) 2|1|2|2 ~ev[] Line 7 is saying that ~c[term7] (defined above) is modified by applying the definition of ~c[WP-ZCOEF-G] to it. Line 8 then says that the body of this definition has been rewritten (with its formals bound to the actuals from ~c[term7]) and the rewriter is diving into the subterms of that rewritten body, as indicated. Notice also that line 8 is the first line marked with an asterisk (``~c[*]'') in the margin. This line is the first that is different from what was shown the previous time the display was updated (about 1/10 second earlier, by default). When a line is marked with an asterisk, so are all the lines below it; so the lines without an asterisk are those that have been stable since the last display. In this example we may see line 7 marked without an asterisk for a while, which suggests that the rule ~c[(:DEFINITION WP-ZCOEF-G)] is expensive. (Also ~pl[accumulated-persistence].) In general, a line that persists for awhile without a leading asterisk can suggest why the proof is taking a long time. Finally, note the indentation of line 14 relative to line 13. Extra indentation occurs when an attempt is being made to relieve a hypothesis (i.e., rewrite it to ~c[t]). In particular, rewrites that will be incorporated directly into a (top-level) literal are all indented just two spaces, starting with the first rewrite directly under a process such as ~c[SIMPLIFY-CLAUSE] (shown line 1 above). If the indentation is at least the value of raw Lisp variable ~c[*dmr-indent-max*] (by default, 20), then indenttion is restricted to that column, but ACL2 prints ~c[{n}] where n is the column that would have been used for indentation if there were no maximum. You can move the cursor around in the dmr buffer even while it is being updated. But emacs will attempt to keep the cursor no later than the first asterisk (``~c[*]'') in the margin. Thus, you can move the cursor around in the stable part of the display, and emacs will keep the cursor in that stable part. WARNING: Things could go terribly wrong if the same user runs two different ACL2 sessions with dmr active, because the same file will be written by two different ACL2 processes. WARNING: For dmr to work, emacs and ACL2 need to be run on the same machine because the file used to communicate between ACL2 and emacs is under ~c[/tmp]. Except, you can probably hack around that restriction by changing ~c[*dmr-file-name*] in ACL2 (in raw Lisp) and correspondingly in Emacs file ~c[monitor.el]. More generally, advanced users are welcome to search for the string ~bv[] User-settable dmr variables ~ev[] in ACL2 files ~c[interface-raw.lisp] and ~c[emacs/monitor.el] in order to customize their dmr environments. In order to update the dmr file with the latest stack information, interrupt ACL2 and then evaluate: ~c[(dmr-flush)]. In order to support resumption of the interrupted proof (assuming your host Common Lisp supports resumption), evaluation of ~c[(dmr-start)] will automatically enable the debugger if it is not already enabled and not fully disabled with value ~c[:never] (~pl[set-debugger-enable]). If such automatic enabling takes place, then ~c[(dmr-stop)] will restore the old setting of the debugger unless, after ~c[(dmr-start)] enables the debugger but before ~c[(dmr-stop)] is called, you call ~ilc[set-debugger-enable] (or more precisely: function ~c[set-debugger-enable-fn] is called). Note for users of the experimental extension ACL2(p) (~pl[parallelism]): when waterfall-parallelism has been set to a non-~c[nil] value (~pl[set-waterfall-parallelism]), statistics about parallel execution are printed instead of the usual information.~/ :cited-by break-rewrite :cited-by accumulated-persistence") (defun raw-mode-p (state) (f-get-global 'acl2-raw-mode-p state)) (defun defun-mode-prompt-string (state) (if (raw-mode-p state) "P" (case (default-defun-mode (w state)) (:logic (if (gc-off state) (if (ld-skip-proofsp state) "s" "") (if (ld-skip-proofsp state) "!s" "!"))) (otherwise ; :program (if (gc-off state) (if (ld-skip-proofsp state) "ps" "p") (if (ld-skip-proofsp state) "p!s" "p!")))))) (defun brr-prompt (channel state) (the2s (signed-byte 30) (fmt1 "~F0 ~s1~sr ~@2>" (list (cons #\0 (get-brr-local 'depth state)) (cons #\1 (f-get-global 'current-package state)) (cons #\2 (defun-mode-prompt-string state)) (cons #\r #+:non-standard-analysis "(r)" #-:non-standard-analysis "")) 0 channel state nil))) ; We now develop code to display type-alists nicely. (defun ts< (x y) ; This is just a heuristic order for the type-alist command (proof-checker and ; break-rewrite). First comes t, then non-nil, then nil, and finally we sort ; by type inclusion. (cond ((ts= x y) nil) ((ts= x *ts-t*) t) ((ts= y *ts-t*) nil) ((ts= x *ts-non-nil*) t) ((ts= y *ts-non-nil*) nil) ((ts= x *ts-nil*) t) ((ts= y *ts-nil*) nil) ((ts-subsetp x y) t) (t nil))) (defun add-to-type-alist-segments (ts term segs) (cond ((or (endp segs) (ts< ts (caar segs))) (cons (cons ts (list term)) segs)) ((ts= ts (caar segs)) (cons (cons ts (cons term (cdar segs))) (cdr segs))) (t (cons (car segs) (add-to-type-alist-segments ts term (cdr segs)))))) (defun merge-term-order (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((term-order (car l1) (car l2)) (cons (car l1) (merge-term-order (cdr l1) l2))) (t (cons (car l2) (merge-term-order l1 (cdr l2)))))) (defun merge-sort-term-order (l) (cond ((null (cdr l)) l) (t (merge-term-order (merge-sort-term-order (evens l)) (merge-sort-term-order (odds l)))))) (defun sort-type-alist-segments (segs) (if (endp segs) nil (cons (cons (caar segs) ; Unfortunately, term-order does not do a particularly great job from the point ; of view of displaying terms. However, we use it anyhow here, if for no other ; reason so that the display order is predictable. (merge-sort-term-order (cdar segs))) (sort-type-alist-segments (cdr segs))))) (defun type-alist-segments (type-alist acc) (if (endp type-alist) (sort-type-alist-segments acc) (type-alist-segments (cdr type-alist) (add-to-type-alist-segments (cadar type-alist) (caar type-alist) acc)))) (defun print-terms (terms iff-flg wrld) ; Print untranslations of the given terms with respect to iff-flg, following ; each with a newline. ; We use cw instead of the fmt functions because we want to be able to use this ; function in print-type-alist-segments (used in brkpt1), which does not return ; state. (if (endp terms) terms (prog2$ (cw "~q0" (untranslate (car terms) iff-flg wrld)) (print-terms (cdr terms) iff-flg wrld)))) (defun print-type-alist-segments (segs wrld) ; We use cw instead of the fmt functions because we want to be able to use this ; function in brkpt1, which does not return state. (if (endp segs) segs (prog2$ (cw "-----~%Terms with type ~x0:~%" (decode-type-set (caar segs))) (prog2$ (print-terms (cdar segs) (member (caar segs) (list *ts-t* *ts-non-nil* *ts-nil* *ts-boolean*)) wrld) (print-type-alist-segments (cdr segs) wrld))))) (defun print-type-alist (type-alist wrld) (print-type-alist-segments (type-alist-segments type-alist nil) wrld)) ; End of code for printing type-alists. (defun tilde-*-ancestors-stack-msg1 (i ancestors wrld evisc-tuple) (cond ((endp ancestors) nil) ((ancestor-binding-hyp-p (car ancestors)) (cons (msg "~c0. Binding Hyp: ~Q12~|~ Unify-subst: ~Q32~%" (cons i 2) (untranslate (dumb-negate-lit (ancestor-binding-hyp/hyp (car ancestors))) t wrld) evisc-tuple (ancestor-binding-hyp/unify-subst (car ancestors))) (tilde-*-ancestors-stack-msg1 (+ 1 i) (cdr ancestors) wrld evisc-tuple))) (t (cons (msg "~c0. Hyp: ~Q12~|~ Runes: ~x3~%" (cons i 2) (untranslate (dumb-negate-lit (access ancestor (car ancestors) :lit)) t wrld) evisc-tuple (access ancestor (car ancestors) :tokens)) (tilde-*-ancestors-stack-msg1 (+ 1 i) (cdr ancestors) wrld evisc-tuple))))) (defun tilde-*-ancestors-stack-msg (ancestors wrld evisc-tuple) (list "" "~@*" "~@*" "~@*" (tilde-*-ancestors-stack-msg1 0 ancestors wrld evisc-tuple))) (defun brkpt1 (lemma target unify-subst type-alist ancestors initial-ttree gstack state) ; #+ACL2-PAR note: since we lock the use of wormholes, brr might be usable ; within the parallelized waterfall. However, since locks can serialize ; computation, we leave brr disabled for now. Kaufmann has the following ; reaction to using brr and waterfall parallelism at the same time: ;;; "My immediate reaction is that if someone wants to use brr, they should ;;; turn off parallelism. I'd probably even make it illegal to have both ;;; waterfall-parallelism enabled and :brr t at the same time." ; Parallelism blemish: cause an error when a user tries to enable parallelism ; and brr is enabled. Also cause an error when enabling brr and ; waterfall-parallism is enabled. We do not label this a "wart", because we ; have documented this lack of feature in ; unsupported-waterfall-parallelism-features. (cond #+acl2-par ; test is always false anyhow when #-acl2-par ((f-get-global 'waterfall-parallelism state) nil) ((not (f-get-global 'gstackp state)) nil) (t (brr-wormhole '(lambda (whs) (set-wormhole-entry-code whs (if (assoc-equal (access rewrite-rule lemma :rune) (cdr (assoc-eq 'brr-monitored-runes (wormhole-data whs)))) :ENTER :SKIP))) `((brr-gstack . ,gstack) (brr-alist . ((lemma . ,lemma) (target . ,target) (unify-subst . ,unify-subst) (type-alist . ,type-alist) (ancestors . ,ancestors) (initial-ttree . ,initial-ttree)))) '(pprogn (push-brr-stack-frame state) (put-brr-local 'depth (1+ (or (get-brr-local 'depth state) 0)) state) (let ((pair (assoc-equal (access rewrite-rule (get-brr-local 'lemma state) :rune) (get-brr-global 'brr-monitored-runes state)))) ; We know pair is non-nil because of the entrance test on wormhole above (er-let* ((okp (eval-break-condition (car pair) (cadr pair) 'wormhole state))) (cond (okp (pprogn (cond ((true-listp okp) (stuff-standard-oi okp state)) (t state)) (prog2$ (cw "~%(~F0 Breaking ~F1 on ~X23:~|" (get-brr-local 'depth state) (access rewrite-rule (get-brr-local 'lemma state) :rune) (get-brr-local 'target state) (term-evisc-tuple t state)) (value t)))) (t (pprogn (pop-brr-stack-frame state) (value nil))))))) '( ; If you add commands, change the deflabel brr-commands. (:ok 0 (lambda nil ; Note: Proceed-from-brkpt1 is not defined in this file! It is here used ; in a constant, fortunately, because it cannot yet be defined. The problem ; is that it involves chk-acceptable-monitors, which in turn must look at ; the rules named by given runes, which is a procedure we can define only ; after introducing certain history management utilities. (proceed-from-brkpt1 'silent t :ok state))) (:go 0 (lambda nil (proceed-from-brkpt1 'print t :go state))) (:eval 0 (lambda nil (proceed-from-brkpt1 'break t :eval state))) (:ok! 0 (lambda nil (proceed-from-brkpt1 'silent nil :ok! state))) (:go! 0 (lambda nil (proceed-from-brkpt1 'print nil :go! state))) (:eval! 0 (lambda nil (proceed-from-brkpt1 'break nil :eval! state))) (:ok$ 1 (lambda (runes) (proceed-from-brkpt1 'silent runes :ok$ state))) (:go$ 1 (lambda (runes) (proceed-from-brkpt1 'print runes :go$ state))) (:eval$ 1 (lambda (runes) (proceed-from-brkpt1 'break runes :eval$ state))) (:q 0 (lambda nil (prog2$ (cw "Proceed with some flavor of :ok, :go, or :eval, or use ~ :p! to pop or :a! to abort.~%") (value :invisible)))) (:target 0 (lambda nil (prog2$ (cw "~x0~|" (get-brr-local 'target state)) (value :invisible)))) (:hyps 0 (lambda nil (prog2$ (cw "~x0~|" (access rewrite-rule (get-brr-local 'lemma state) :hyps)) (value :invisible)))) (:hyp 1 (lambda (n) (cond ((and (integerp n) (>= n 1) (<= n (length (access rewrite-rule (get-brr-local 'lemma state) :hyps)))) (prog2$ (cw "~X01~|" (nth (1- n) (access rewrite-rule (get-brr-local 'lemma state) :hyps)) nil) (value :invisible))) (t (er soft :HYP ":HYP must be given an integer argument between 1 and ~x0." (length (access rewrite-rule (get-brr-local 'lemma state) :hyps))))))) (:lhs 0 (lambda nil (prog2$ (cw "~x0~|" (access rewrite-rule (get-brr-local 'lemma state) :lhs)) (value :invisible)))) (:rhs 0 (lambda nil (prog2$ (cw "~x0~|" (access rewrite-rule (get-brr-local 'lemma state) :rhs)) (value :invisible)))) (:unify-subst 0 (lambda nil (prog2$ (cw "~*0" (tilde-*-alist-phrase (get-brr-local 'unify-subst state) (term-evisc-tuple t state) 5)) (value :invisible)))) (:type-alist 0 (lambda nil (prog2$ (cw "~%Decoded type-alist:~%") (prog2$ (print-type-alist-segments (type-alist-segments (get-brr-local 'type-alist state) nil) (w state)) (prog2$ (cw "~%==========~%Use ~x0 to see actual type-alist.~%" '(get-brr-local 'type-alist state)) (value :invisible)))))) (:ancestors 0 (lambda nil (prog2$ (cw "Ancestors stack (from first backchain (0) to ~ current):~%~*0~%Use ~x1 to see actual ancestors stack.~%" (tilde-*-ancestors-stack-msg (get-brr-local 'ancestors state) (w state) (term-evisc-tuple t state)) '(get-brr-local 'ancestors state)) (value :invisible)))) (:initial-ttree 0 (lambda nil (prog2$ (cw "~x0~|" (get-brr-local 'initial-ttree state)) (value :invisible)))) (:final-ttree 0 (lambda nil (prog2$ (cw "~F0 has not yet been :EVALed.~%" (access rewrite-rule (get-brr-local 'lemma state) :rune)) (value :invisible)))) (:rewritten-rhs 0 (lambda nil (prog2$ (cw "~F0 has not yet been :EVALed.~%" (access rewrite-rule (get-brr-local 'lemma state) :rune)) (value :invisible)))) (:failure-reason 0 (lambda nil (prog2$ (cw "~F0 has not yet been :EVALed.~%" (access rewrite-rule (get-brr-local 'lemma state) :rune)) (value :invisible)))) (:wonp 0 (lambda nil (prog2$ (cw "~F0 has not yet been :EVALed.~%" (access rewrite-rule (get-brr-local 'lemma state) :rune)) (value :invisible)))) (:path 0 (lambda nil (prog2$ (cw-gstack) (value :invisible)))) (:frame 1 (lambda (n) (let ((rgstack (reverse (get-brr-global 'brr-gstack state)))) (cond ((and (integerp n) (>= n 1) (<= n (length rgstack))) (prog2$ (cw-gframe n (if (= n 1) nil (access gframe (nth (- n 2) rgstack) :sys-fn)) (nth (- n 1) rgstack) nil) (value :invisible))) (t (er soft :frame ":FRAME must be given an integer argument between 1 and ~x0." (length rgstack))))))) (:top 0 (lambda nil (prog2$ (cw-gframe 1 nil (car (reverse (get-brr-global 'brr-gstack state))) nil) (value :invisible)))) (:help 0 (lambda nil (doc 'brr-commands))) (:standard-help 0 help)))))) (defun brkpt2 (wonp failure-reason unify-subst gstack rewritten-rhs final-ttree rcnst state) ; #+ACL2-PAR note: see brkpt1. (cond #+acl2-par ; test is always false anyhow when #-acl2-par ((f-get-global 'waterfall-parallelism state) nil) ((not (f-get-global 'gstackp state)) nil) (t (brr-wormhole '(lambda (whs) (set-wormhole-entry-code whs (if (assoc-equal gstack (cdr (assoc-eq 'brr-stack (wormhole-data whs)))) :ENTER :SKIP))) `((brr-gstack . ,gstack) (brr-alist . ((wonp . ,wonp) (failure-reason . ,failure-reason) (unify-subst . ,unify-subst) ; maybe changed (rewritten-rhs . ,rewritten-rhs) (rcnst . ,rcnst) (final-ttree . ,final-ttree)))) '(cond ((eq (get-brr-local 'action state) 'silent) (prog2$ (cw "~F0)~%" (get-brr-local 'depth state)) (pprogn (f-put-global 'brr-monitored-runes (get-brr-local 'saved-brr-monitored-runes state) state) (pop-brr-stack-frame state) (value nil)))) ((eq (get-brr-local 'action state) 'print) (pprogn (put-brr-local-lst (f-get-global 'brr-alist state) state) (prog2$ (if (get-brr-local 'wonp state) (cw "~%~F0 ~F1 produced ~X23.~|~F0)~%" (get-brr-local 'depth state) (access rewrite-rule (get-brr-local 'lemma state) :rune) (get-brr-local 'rewritten-rhs state) (term-evisc-tuple t state)) (cw "~%~F0x ~F1 failed because ~@2~|~F0)~%" (get-brr-local 'depth state) (access rewrite-rule (get-brr-local 'lemma state) :rune) (tilde-@-failure-reason-phrase (get-brr-local 'failure-reason state) 1 (get-brr-local 'unify-subst state) (term-evisc-tuple t state) (free-vars-display-limit state)))) (pprogn (f-put-global 'brr-monitored-runes (get-brr-local 'saved-brr-monitored-runes state) state) (pop-brr-stack-frame state) (value nil))))) (t (pprogn (put-brr-local-lst (f-get-global 'brr-alist state) state) (er-progn (set-standard-oi (get-brr-local 'saved-standard-oi state) state) (cond ((consp (f-get-global 'standard-oi state)) (set-ld-pre-eval-print t state)) (t (value nil))) (pprogn (f-put-global 'brr-monitored-runes (get-brr-local 'saved-brr-monitored-runes state) state) (prog2$ (if (get-brr-local 'wonp state) (cw "~%~F0! ~F1 produced ~X23.~|~%" (get-brr-local 'depth state) (access rewrite-rule (get-brr-local 'lemma state) :rune) (get-brr-local 'rewritten-rhs state) (term-evisc-tuple t state)) (cw "~%~F0x ~F1 failed because ~@2~|~%" (get-brr-local 'depth state) (access rewrite-rule (get-brr-local 'lemma state) :rune) (tilde-@-failure-reason-phrase (get-brr-local 'failure-reason state) 1 (get-brr-local 'unify-subst state) (term-evisc-tuple t state) (free-vars-display-limit state)))) (value t))))))) '( ; If you add commands, change the deflabel brr-commands. (:ok 0 (lambda nil ; Note: Exit-brr is not yet defined because it calls proceed-from-brkpt1. ; See the note above about that function. (exit-brr state))) (:eval 0 (lambda nil (prog2$ (cw "You already have run some flavor of :eval.~%") (value :invisible)))) (:eval! 0 (lambda nil (prog2$ (cw "You already have run some flavor of :eval.~%") (value :invisible)))) (:eval$ 0 (lambda nil (prog2$ (cw "You already have run some flavor of :eval.~%") (value :invisible)))) (:go 0 (lambda nil ; Like :ok, :man. (exit-brr state))) (:go! 0 (lambda nil (exit-brr state))) (:go$ 0 (lambda nil (exit-brr state))) (:q 0 (lambda nil (prog2$ (cw "Exit with :ok or use :p! to pop or :a! to abort.~%") (value :invisible)))) (:target 0 (lambda nil (prog2$ (cw "~x0~|" (get-brr-local 'target state)) (value :invisible)))) (:hyps 0 (lambda nil (prog2$ (cw "~x0~|" (access rewrite-rule (get-brr-local 'lemma state) :hyps)) (value :invisible)))) (:hyp 1 (lambda (n) (cond ((and (integerp n) (>= n 1) (<= n (length (access rewrite-rule (get-brr-local 'lemma state) :hyps)))) (prog2$ (cw "~X01~|" (nth (1- n) (access rewrite-rule (get-brr-local 'lemma state) :hyps)) nil) (value :invisible))) (t (er soft :HYP ":HYP must be given an integer argument between 1 and ~x0." (length (access rewrite-rule (get-brr-local 'lemma state) :hyps))))))) (:lhs 0 (lambda nil (prog2$ (cw "~x0~|" (access rewrite-rule (get-brr-local 'lemma state) :lhs)) (value :invisible)))) (:rhs 0 (lambda nil (prog2$ (cw "~x0~|" (access rewrite-rule (get-brr-local 'lemma state) :rhs)) (value :invisible)))) (:unify-subst 0 (lambda nil (prog2$ (cw "~*0" (tilde-*-alist-phrase (get-brr-local 'unify-subst state) (term-evisc-tuple t state) 5)) (value :invisible)))) (:type-alist 0 (lambda nil (prog2$ (cw "~%Decoded type-alist:~%") (prog2$ (print-type-alist-segments (type-alist-segments (get-brr-local 'type-alist state) nil) (w state)) (prog2$ (cw "~%==========~%Use ~x0 to see actual type-alist.~%" '(get-brr-local 'type-alist state)) (value :invisible)))))) (:ancestors 0 (lambda nil (prog2$ (cw "Ancestors stack (from first backchain (0) to ~ current):~%~*0~%Use ~x1 to see actual ancestors stack.~%" (tilde-*-ancestors-stack-msg (get-brr-local 'ancestors state) (w state) (term-evisc-tuple t state)) '(get-brr-local 'ancestors state)) (value :invisible)))) (:initial-ttree 0 (lambda nil (prog2$ (cw "~x0~|" (get-brr-local 'initial-ttree state)) (value :invisible)))) (:final-ttree 0 (lambda nil (prog2$ (cw "~x0~|" (get-brr-local 'final-ttree state)) (value :invisible)))) (:rewritten-rhs 0 (lambda nil (prog2$ (cond ((or (get-brr-local 'wonp state) (member-eq (get-brr-local 'failure-reason state) '(too-many-ifs rewrite-fncallp))) (cw "~x0~|" (get-brr-local 'rewritten-rhs state))) (t (cw "? ~F0 failed.~%" (access rewrite-rule (get-brr-local 'lemma state) :rune)))) (value :invisible)))) (:failure-reason 0 (lambda nil (prog2$ (if (get-brr-local 'wonp state) (cw "? ~F0 succeeded.~%" (access rewrite-rule (get-brr-local 'lemma state) :rune)) (cw "~@0~|" (tilde-@-failure-reason-phrase (get-brr-local 'failure-reason state) 1 (get-brr-local 'unify-subst state) (term-evisc-tuple t state) (free-vars-display-limit state)))) (value :invisible)))) (:path 0 (lambda nil (prog2$ (cw-gstack) (value :invisible)))) (:frame 1 (lambda (n) (let ((rgstack (reverse (get-brr-global 'brr-gstack state)))) (cond ((and (integerp n) (>= n 1) (<= n (length rgstack))) (prog2$ (cw-gframe n (if (= n 1) nil (access gframe (nth (- n 2) rgstack) :sys-fn)) (nth (- n 1) rgstack) nil) (value :invisible))) (t (er soft :frame ":FRAME must be given an integer argument between 1 and ~x0." (length rgstack))))))) (:top 0 (lambda nil (prog2$ (cw-gframe 1 nil (car (reverse (get-brr-global 'brr-gstack state))) nil) (value :invisible)))) (:help 0 (lambda nil (doc 'brr-commands))) (:standard-help 0 help)))))) ; We now develop some of the code for an implementation of an idea put ; forward by Diederik Verkest, namely, that patterns should be allowed ; in :expand hints. (defrec expand-hint ((equiv . alist) ; :none, or a partial unify-subst for matching term against actual . (pattern . ((rune ; nil for a lambda application . hyp) ; nil if there are no hypotheses of rule, else their conjunction . (lhs ; left-hand side of rule, for matching against actual term . rhs) ))) t) (defun binds-to-constants-p (unify-subst) (cond ((endp unify-subst) t) (t (let ((pair (car unify-subst))) (and (or (eq (car pair) (cdr pair)) (quotep (cdr pair))) (binds-to-constants-p (cdr unify-subst))))))) (defun expand-permission-result1 (term expand-lst geneqv wrld) ; This is a generalized version of member-equal that asks whether expand-lst ; gives term permission to be expanded, as described in :DOC hints. Here, term ; is a function application. We return (mv new-term hyp unify-subst rune k), ; where if new-term is not nil, and assuming hyp if hyp is non-nil, then ; new-term is provably equal to the application of unify-subst to term and, if ; non-nil, rune justifies this equality. If new-term is not nil then k is the ; length of the tail of expand-lst whose car justifies the expansion of ; new-term, but only if we want to remove that member of expand-lst for ; heuristic purposes; otherwise k is nil. See expand-permission-result. (if expand-lst (let ((x (car expand-lst))) (cond ((eq x :lambdas) (cond ((flambda-applicationp term) (mv (lambda-body (ffn-symb term)) nil (pairlis$ (lambda-formals (ffn-symb term)) (fargs term)) nil nil)) (t (expand-permission-result1 term (cdr expand-lst) geneqv wrld)))) ((not (geneqv-refinementp (access expand-hint x :equiv) geneqv wrld)) (expand-permission-result1 term (cdr expand-lst) geneqv wrld)) (t (let* ((alist (access expand-hint x :alist)) (alist-none-p (eq alist :none)) (alist-constants-p (and (not alist-none-p) (eq (car alist) :constants))) (alist (if alist-constants-p (cdr alist) alist))) (mv-let (flg unify-subst0) (cond (alist-none-p (mv (equal (access expand-hint x :pattern) term) nil)) (t (one-way-unify1 (access expand-hint x :pattern) term alist))) (let ((flg (and flg (if alist-constants-p ; We require that unify-subst0 bind each variable to itself or to a constant. ; See the long comment in filter-disabled-expand-terms for further discussion. (binds-to-constants-p unify-subst0) t)))) (cond (flg (mv-let (flg unify-subst) (one-way-unify (access expand-hint x :lhs) term) (cond (flg (mv (access expand-hint x :rhs) (access expand-hint x :hyp) unify-subst (access expand-hint x :rune) (and (or alist-none-p ; For the example in a comment in expand-permission-result, looping occurs if ; we do not remove the expand hint in the alist-constants-p case. See the long ; comment in filter-disabled-expand-terms for further discussion. alist-constants-p) (length expand-lst)))) (t (expand-permission-result1 term (cdr expand-lst) geneqv wrld))))) (t (expand-permission-result1 term (cdr expand-lst) geneqv wrld))))))))) (mv nil nil nil nil nil))) (defun remove1-by-position (target-index lst acc) (declare (xargs :guard (and (true-listp lst) (true-listp acc) (natp target-index) (< target-index (len lst))))) (cond ((zp target-index) (revappend acc (cdr lst))) (t (remove1-by-position (1- target-index) (cdr lst) (cons (car lst) acc))))) (defun expand-permission-result (term rcnst geneqv wrld) ; This is a generalized version of member-equal that asks whether rcnst gives ; term permission to be expanded, as described in :DOC hints. Here, term is a ; function application. We return (mv new-term hyp unify-subst rune ; new-rcnst), where if new-term is not nil: ; - if hyp is non-nil, then assuming hyp, term is provably equal to the ; application of unify-subst to new-term; ; - if rune is non-nil, rune justifies the above claim; and ; - new-rcnst is either rcnst or an update of it that removes the reason for ; expansion of term from the :expand-lst (see long comment below). (let ((expand-lst (access rewrite-constant rcnst :expand-lst))) (mv-let (new-term hyp unify-subst rune posn-from-end) (expand-permission-result1 term expand-lst geneqv wrld) (cond (posn-from-end ; In this case new-term is non-nil; so term will be expanded, and we want to ; remove the reason for this expansion in order to avoid looping. The thm ; below did indeed cause a rewriting loop through Version_4.3. (It is OK for ; it to fail, but not with looping.) ; (defun first-nondecrease (lst) ; (cond ((endp lst) nil) ; ((endp (cdr lst)) (list (car lst))) ; ((> (car lst) (cadr lst)) (list (car lst))) ; (t (cons (car lst) (first-nondecrease (cdr lst)))))) ; ; (defun removeN (lst n) ; (cond ((endp lst) nil) ; ((zp n) lst) ; (t (removeN (cdr lst) (1- n))))) ; ; (defthm len-removen ; Needed to admint next fn. If you disable this ; (implies (natp n) ; lemma, the overflow no longer occurs. ; (equal (len (removen lst n)) ; (if (>= n (len lst)) ; 0 ; (- (len lst) n))))) ; ; (defun longest-nondecrease (lst) ; (declare (xargs :measure (len lst))) ; (if (or (endp lst) (not (true-listp lst))) nil ; (let* ((first (first-nondecrease lst)) ; (n (len first))) ; (let ((remain (longest-nondecrease (removeN lst n)))) ; (if (>= n (len remain)) first remain))))) ; ; ; This is an arithmetic lemma that may seem benign. ; (defthm equality-difference-hack ; (implies (and (acl2-numberp x) ; (acl2-numberp y)) ; (equal (equal (+ x (- y)) x) ; (equal y 0)))) ; ; ; Loops: ; (thm (implies (true-listp lst) ; (equal (equal (len (longest-nondecrease lst)) (len lst)) ; (equal (longest-nondecrease lst) lst)))) (assert$ new-term (mv new-term hyp unify-subst rune (let ((expand-lst (access rewrite-constant rcnst :expand-lst))) (change rewrite-constant rcnst :expand-lst (remove1-by-position (- (length expand-lst) posn-from-end) expand-lst nil)))))) (t (mv new-term hyp unify-subst rune rcnst)))))) (defun expand-permission-p (term rcnst geneqv wrld) ; Returns nil if we do not have permission from :expand hints to expand, else ; returns rcnst possibly updated by removing term from the :expand-lst field ; (see comments about that in expand-permission-result). It may be more ; appropriate to use expand-permission-result instead. (mv-let (new-term hyp unify-subst rune new-rcnst) (expand-permission-result term rcnst geneqv wrld) (declare (ignore hyp unify-subst rune)) (and new-term new-rcnst))) (defun one-way-unify-restrictions1 (pat term restrictions) (cond ((null restrictions) (mv nil nil)) (t (mv-let (unify-ans unify-subst) (one-way-unify1 pat term (car restrictions)) (cond (unify-ans (mv unify-ans unify-subst)) (t (one-way-unify-restrictions1 pat term (cdr restrictions)))))))) (defun one-way-unify-restrictions (pat term restrictions) (cond ((null restrictions) (one-way-unify pat term)) (t (one-way-unify-restrictions1 pat term restrictions)))) (defun ev-fncall! (fn args state latches aok) ; This function is logically equivalent to ev-fncall. However, it is ; much faster because it can only be used for certain fn and args: fn ; is a Common Lisp compliant function, not trafficking in stobjs, ; defined as a function in raw Lisp. The args satisfy the guard of fn. ; Note that return-last is not defined as a function in raw Lisp, so fn should ; not be return-last. That is also important so that we do not take the ; stobjs-out of return-last, which causes an error. (declare (xargs :guard (let ((wrld (w state))) (and (symbolp fn) (not (eq fn 'return-last)) (function-symbolp fn wrld) (all-nils (stobjs-in fn wrld)) (equal (stobjs-out fn wrld) '(nil)) (eq (symbol-class fn wrld) :common-lisp-compliant) (mv-let (erp val latches) (ev (guard fn nil wrld) (pairlis$ (formals fn wrld) args) state nil t aok) ; Formerly, here we had (declare (ignore latches)). But CCL complained ; about unused lexical variable LATCHES when defining/compiling the *1* ; function. So instead we use LATCHES in a trivial way. (prog2$ latches ; fool CCL; see comment above (and (null erp) val))))))) #+(and (not acl2-loop-only) lucid) (declare (ignore state)) #-acl2-loop-only (return-from ev-fncall! (mv nil (apply fn args) latches)) (ev-fncall fn args state latches nil aok)) (defun ev-fncall-meta (fn args state) (declare (xargs :guard (and (symbolp fn) (function-symbolp fn (w state))))) ; Fn is a metafunction and args is its list of arguments. Extended ; metafunctions have three arguments, term, mfc, and state. Thanks to the ; power of coerce-state-to-object, we actually find the live state in args. ; The args of a vanilla metafunction is just the list containing the term. Our ; first interest below is to bind the Lisp special *metafunction-context* to ; the context if we are calling an extended metafunction. This will allow the ; metafunction's subroutines to authenticate their arguments. We assume that ; the context was correctly constructed by our caller, e.g., rewrite. Our ; second concern is to avoid guard checking if possible. (let (#-acl2-loop-only (*metafunction-context* (if (cdr args) (cadr args) nil)) ) (cond ((eq (symbol-class fn (w state)) :common-lisp-compliant) ; Since the guard of the meta function fn is implied by pseudo-termp of its ; arg, and since fn is only applied to terms by our meta facility, and finally ; because we check that fn does not traffic in stobjs (see ; chk-acceptable-meta-rule), we know that it is safe to call the raw Lisp ; version of fn. ; See chk-evaluator-use-in-rule for a discussion of how we restrict the use of ; evaluators in rules of class :meta or :clause-processor, so that we can use ; aok = t in the calls of ev-fncall! and ev-fncall just below. (ev-fncall! fn args state nil t)) (t (ev-fncall fn args state nil nil t))))) (defun get-evg (q ctx) ; Q is a quotep, or at least we expect it to be. We cause a hard error if not, ; else we return the "explicit value guts". (if (quotep q) (cadr q) (er hard ctx "We expected a quotep in this context, variables, but ~x0 is not a ~ quotep!" q))) (defun ev-synp (synp-term unify-subst mfc state) ; Synp-term is the quotation of the term to be evaluated. Unify-subst is the ; unifying substitution presently in force, and mfc is the meta-level context ; (formerly referred to as "metafunction-context"). This function has been ; modeled (weakly) on ev-fncall-meta. ; This call to synp is the result of the macro-expansion of a syntaxp or ; bind-free hyothesis. Or at least it might as well be; we check in ; bad-synp-hyp-msg (called in chk-acceptable-rewrite-rule2) that synp-term has ; a form that we know how to handle. (let* (#-acl2-loop-only (*metafunction-context* mfc) (unify-subst1 (if mfc (cons (cons 'mfc mfc) unify-subst) unify-subst)) (unify-subst2 (if mfc (cons (cons 'state (coerce-state-to-object state)) unify-subst1) unify-subst))) ; It is tempting to bind the state global safe-mode to t here, using ; state-global-let*. However, we cannot do that without returning state, which ; we want to avoid since the caller, relieve-hyp, does not return state. Since ; synp is only used heuristically, it really isn't necessary to use safe mode, ; although it would have been nice for avoiding hard errors (e.g., from car of ; a non-nil atom). (ev (get-evg synp-term 'ev-synp) unify-subst2 state nil t t))) (defun bad-synp-alist1 (alist unify-subst vars-to-be-bound wrld) ; We return nil if the alist is legal, else a string or message suitable for ; printing with ~@. (declare (xargs :guard (alistp alist))) (if (null alist) nil (or (let ((key (caar alist)) (value (cdar alist))) (cond ((not (legal-variablep key)) (msg "the key ~x0 is not a legal variable" key)) ((assoc-eq key unify-subst) (msg "the key ~x0 is already bound in the unifying ~ substitution, ~x1" key unify-subst)) ((not (termp value wrld)) (msg "the value ~x0 bound to key ~x1 is not a legal term ~ (translated into ACL2 internal form) in the current ~ ACL2 world" value key)) ((and (not (eq vars-to-be-bound t)) (not (member-eq key vars-to-be-bound))) (msg "the key ~x0 is not a member of the specified list of ~ variables to be bound, ~x1" key vars-to-be-bound)) (t nil))) (bad-synp-alist1 (cdr alist) unify-subst vars-to-be-bound wrld)))) (defun bad-synp-alist1-lst (alist-lst unify-subst vars-to-be-bound wrld) (cond ((endp alist-lst) nil) (t (or (bad-synp-alist1 (car alist-lst) unify-subst vars-to-be-bound wrld) (bad-synp-alist1-lst (cdr alist-lst) unify-subst vars-to-be-bound wrld))))) (defun bind-free-info (x unify-subst vars-to-be-bound wrld) ; X is a value returned by a bind-free synp hypothesis, known not to be t or ; nil; unify-subst is an alist containing the unifying substitution gathered so ; far; and vars-to-be-bound is either t or a quoted list of variables. We ; check that alist is indeed an alist, that it does not bind any variables ; already bound in unify-subst, and that it only binds variables to ACL2 terms. ; If vars-to-be-bound is anything other than t, we also require that alist only ; binds vars present in vars-to-be-bound. ; We return nil if x is a legal alist, t if x is a legal list of alists, and ; otherwise a string or message suitable for printing with ~@. (cond ((and (true-listp x) (alistp (car x))) (or (bad-synp-alist1-lst x unify-subst (get-evg vars-to-be-bound 'bad-synp-alist) wrld) t)) ((alistp x) (bad-synp-alist1 x unify-subst (get-evg vars-to-be-bound 'bad-synp-alist) wrld)) (t "it is not an alist"))) (defun evgs-or-t (lst alist) ; Consider the result, lst', of substituting alist into the list of ; terms, lst. Is every term in lst' a quoted constant? (For example, ; lst might be (x '23) and alist might be '((x . '7) (y . a)), in ; which case, the answer is "yes, they're all quoted constants.") If ; so, we return the true-list containing the evgs of the elements of ; lst'; else we return t. (cond ((endp lst) nil) ((variablep (car lst)) (let ((temp (assoc-eq (car lst) alist))) (if (and temp (quotep (cdr temp))) (let ((rest (evgs-or-t (cdr lst) alist))) (cond ((eq rest t) t) (t (cons (cadr (cdr temp)) rest)))) t))) ((fquotep (car lst)) (let ((rest (evgs-or-t (cdr lst) alist))) (cond ((eq rest t) t) (t (cons (cadr (car lst)) rest))))) (t t))) ; Essay on Correctness of Meta Reasoning ; Below, we sketch a proof of a theorem asserting the correctness of ACL2's ; meta reasoning, starting with meta rules and then handling clause processor ; rules. We state correctness for extended metafunctions, but correctness for ; ordinary metafunctions follows trivially by adding mfc and state as ignored ; arguments. We assume a call of hyp-fn in the meta rule, but of course this ; too is fully general; just define hyp-fn to return 't if it is not already ; present. We also assume that the metatheorem includes hypotheses of ; (pseudo-termp term) and (alistp a), but of course the metatheorem then ; applies if it omits these hypotheses -- just weaken it by adding them back ; in! And of course, the mention of meta-extract-hyps is harmless if there are ; no meta-extract hypotheses; in that case, meta-extract-hyps is the empty ; conjunction. ; Let *mfc* be a metafunction context, and let {*mfc*} denote the formula ; asserting the validity of *mfc*, as based on its type-alist. For example, if ; *mfc* has only one entry in its type-alist, and that entry binds (foo x) to ; (ts-complement *ts-integer*), then {*mfc*} is (not (integerp (foo x))). For ; notational convenience, we write "ev" below for a function symbol that is ; definitely not the predefined ACL2 ev function! ; Theorem. Suppose that the following is a theorem, where the only axioms for ; ev are evaluator axioms, where term, a, mfc, and state are variables with ; those exact names (clearly this theorem then generalizes to more arbitrary ; variables) and META-EXTRACT-HYPS is explained below. ; (implies (and (pseudo-termp term) ; (alistp a) ; META-EXTRACT-HYPS ; see below ; (ev (hyp-fn term mfc state) a)) ; (equal (ev term a) ; (ev (meta-fn term mfc state) a))) ; Suppose in addition that LHS, HYP, and RHS are terms, and that in an ; environment where term is bound to 'LHS, mfc is bound to *mfc* (the current ; metafunction context), and state is bound to the live ACL2 state, the ; following conditions hold, where evaluation may use attachments. ; (hyp-fn term mfc state) evaluates to 'HYP; ; (meta-fn term mfc state) evaluates to 'RHS; and ; META-EXTRACT-HYPS is a conjunction of meta-extract hypotheses, ; as recognized by remove-meta-extract-contextual-hyps and ; remove-meta-extract-global-hyps ; Let EXTRA-FNS be a set of 0, 1, or 2 symbols consisting of ; meta-extract-contextual-fact, meta-extract-global-fact+, or both, according ; to which have top-level calls among meta-extract-hyps. ; Finally, assume the following: ev is not ancestral in any defaxiom, in ; meta-fn, in hyp-fn, or in EXTRA-FNS; no ancestor of ev or EXTRA-FNS with an ; attachment is ancestral in meta-fn or hyp-fn; and no ancestor of any defaxiom ; has an attachment. (See chk-evaluator-use-in-rule for enforcement.) ; Then the following is a theorem of (mfc-world *mfc*), or equivalently (since ; the worlds have the same logical theory), (w *the-live-state*): ; (implies (and {*mfc*} ; HYP) ; (equal LHS RHS)). ; The proof of the theorem above uses the following lemma. ; Evaluator Elimination Lemma. Assume that u is a term, ev is an evaluator for ; the function symbols in u, and a0 is a term of the form (list (cons 'v1 t1) ; ... (cons 'vn tn)) where (v1 ... vn) includes all variables occurring free in ; u and each ti is a term. Let s be the substitution mapping vi to ti (1 <= i ; <= n). Then the following is a theorem: ; (ev 'u a0) = u/s ; Proof: An easy induction on the structure of term u. Q.E.D. ; As a warmup, we first prove the theorem in the special case that ; META-EXTRACT-HYPS is the empty conjunction and there are no attachments ; involved. Let (v1 .. vn) be the variables occurring free in lhs, rhs, or ; hyp. Let A0 be the term ; (list (cons 'v1 v1) ... (cons 'vn vn)). ; We instantiate the assumed theorem ; (implies (and (pseudo-termp term) ; (alistp a) ; (ev (hyp-fn term mfc state) a)) ; (equal (ev term a) (ev (meta-fn term mfc state) a))) ; replacing term by 'LHS, a by A0, mfc by *mfc*, and state by the live state, ; to obtain the following. ; (implies (and (pseudo-termp 'LHS) ; (alistp A0) ; (ev (hyp-fn 'LHS *mfc* *the-live-state*) A0)) ; (equal (ev 'LHS A0) ; (ev (meta-fn 'LHS *mfc* *the-live-state*) A0))) ; which is provably equal, by computation, to the following (assuming no ; attachments are used in the computation; we consider attachments later): ; (implies (ev 'HYP A0) ; (equal (ev 'LHS A0) (ev 'RHS A0))) ; By functional instantiation, we may replace ev in the hypotheses of the ; theorem by an "extended" evaluator for a set of function symbols including ; all those that occur in hyp, lhs, or rhs. (A long comment in ; defaxiom-supporters justifies this use of functional instantiation.) Then by ; the Evaluator Elimination Lemma the formula above simplifies to ; (implies HYP ; (equal LHS RHS)) ; as desired. ; We next consider the general case, where there may be meta-extract hypotheses ; and attachments may be used. To start, note that the following is a theorem, ; as it results from the assumed theorem by strengthening hypotheses. (Here we ; write obj1, obj2, st, and aa for variables not occurring elsewhere in the ; formula.) ; (implies ; (and (pseudo-termp term) ; (alistp a) ; (forall (obj1) ; (ev (meta-extract-contextual-fact obj1 mfc state) a)) ; (forall (obj2 st2 aa) ; (ev (meta-extract-global-fact+ obj2 st2 state) aa)) ; (ev (hyp-fn term mfc state) a)) ; (equal (ev term a) (ev (meta-fn term mfc state) a))) ; We instantiate as before, to obtain: ; (implies ; (and (pseudo-termp 'LHS) ; (alistp A0) ; (forall (obj1) ; (ev (meta-extract-contextual-fact obj1 *mfc* *the-live-state*) ; A0)) ; (forall (obj2 st2 aa) ; (ev (meta-extract-global-fact+ obj2 st2 *the-live-state*) aa)) ; (ev (hyp-fn 'LHS *mfc* *the-live-state*) A0)) ; (equal (ev 'LHS A0) ; (ev (meta-fn 'LHS *mfc* *the-live-state*) A0))) ; As before, this reduces by computation to the following theorem. ; (implies ; (and (forall (obj1) ; (ev (meta-extract-contextual-fact obj1 *mfc* *the-live-state*) ; A0)) ; (forall (obj2 st2 aa) ; (ev (meta-extract-global-fact+ obj2 st2 *the-live-state*) aa)) ; (ev 'hyp A0)) ; (equal (ev 'LHS A0) (ev 'RHS A0))) ; We now deal with attachments; feel free to skip this paragraph on a first ; read. If attachments are used, then the formula displayed just above is ; actually a theorem in the current evaluation theory, because of the use of ; computation; we now argue that it is also a theorem of the current logical ; world. Consider the evaluation history h_e obtained from the current logical ; world by considering only attachment pairs for which f is ancestral in ; hyp-fn or meta-fn. The Attachment Restriction Lemma in the Essay on ; Defattach justifies that h_e is indeed an evaluation history. The ; computations above use only attachments in h_e, because it is closed under ; ancestors (also see the comment about mbe in constraint-info). So the ; formula displayed just above is a theorem of h_e. But by hypothesis, no ; ancestor of ev or EXTRA-FNS with an attachment occurs in h_e. So for the ; history h1 obtained by closing ev and EXTRA-FNS under ancestors in h_e ; (including defaxioms, which never have ancestors with attachments, by the ; Defaxiom Restriction for Defattach; see the Essay on Defattach), h1 contains ; no attachments. But h_e is conservative over h1 (a standard property of ; histories), so by definition of conservativity, the formula displayed above ; is a theorem of h1. Since h1 is contained in the current logical world, that ; formula is also a theorem of the current logical world. So we justifiably ; ignore attachments for the remainder of this discussion. ; Now we functionally instantiate as before, this time after introducing an ; evaluator ev' that includes all currently known function symbols, thus ; obtaining a world w' extending the current logical world, w. ; (implies ; (and (forall (obj1) ; (ev' (meta-extract-contextual-fact obj1 *mfc* *the-live-state*) ; A0)) ; (forall (obj2 st2 aa) ; (ev' (meta-extract-global-fact+ obj2 st2 *the-live-state*) aa)) ; (ev' 'HYP A0)) ; (equal (ev' 'LHS A0) (ev' 'RHS A0))) ; As before, the Evaluator Elimination Lemma yields that the following is a ; theorem of w'. ; (implies ; (and (forall (obj1) ; (ev' (meta-extract-contextual-fact obj1 *mfc* *the-live-state*) ; A0)) ; (forall (obj2 st2 aa) ; (ev' (meta-extract-global-fact+ obj2 st2 *the-live-state*) aa)) ; HYP) ; (equal LHS RHS)) ; Thus, it remains only to modify the rest of the original argument by dealing ; with the two universally quantified hypotheses. ; Our first step is to show that the second universally quantified hypothesis, ; where we may as well ignore the forall quantifier, is a theorem of w'. Let ; term0 be the value returned by (meta-extract-global-fact+ obj2 st2 ; *the-live-state*). Since (ev' *t* aa) is provably equal to *t*, let us ; assume without loss of generality that term0 is not *t*, . The first case we ; consider is that obj2 is not of the form (:FNCALL fn arglist). Then we ; claim, without proof (but by appeal to plausibility!), that term0 is provably ; a member of the finite list ('THM1 'THM2 ...), where (THM1 THM2 ...) ; enumerates the theorems of w that can be returned by rewrite-rule-term and ; meta-extract-formula when called by meta-extract-global-fact+. We thus need ; to show that for each member 'THM of this list, (ev' 'THM aa) is a theorem of ; w'. By the (argument of the) Evaluator Elimination Lemma, (ev' 'THM aa) is ; provably equal to the instance of THM obtained by replacing each variable x ; by the term (cdr (assoc 'x aa)). Since THM is a theorem of w and hence w', ; so is this instance. It remains to consider the other case, i.e., to show ; that for obj2 = (:FNCALL fn arglist), (ev' term0 aa) is a theorem of w'. ; Since we are assuming that term0 is not *t*, we know that (w st2) = (w ; *the-live-state*), which is w, and we also know (by inspection of the ; definition of fncall-term) that term0 = (fncall-term fn arglist st2) for a ; logic-mode function symbol fn of w whose input arity is the length of ; arglist. But (fncall-term fn arglist st2) is the term (equal (fn . arglist) ; 'val) where (magic-ev-fncall fn arglist st2 ...) = (mv nil val). We arrange ; that magic-ev-fncall has unknown constraints, but we conceive of it as being ; axiomatized using clocked, logic mode definitions that follow the definitions ; supporting ev-fncall -- in particular, a clocked, logic-mode version of ; ev-fncall-rec-logical -- such that (mv t nil) is returned when the clock ; expires. (All of those functions are conceptually in the ground-zero theory, ; but they need not be defined in the ACL2 system implementation.) Then the ; top-level recursive function is called with a clock that is greater than all ; clocks that would ever be needed for termination under this story for actual ; calls made. Thus, every value returned by ev-fncall is provably equal to the ; value returned by magic-ev-fncall. ; Thus, we now know that the following is a theorem of w': ; (*) ; (implies ; (and (forall (obj1) ; (ev' (meta-extract-contextual-fact obj1 *mfc* *the-live-state*) ; A0)) ; HYP) ; (equal LHS RHS)) ; Recall that we are trying to show that the following is a theorem of w. ; (implies ; (and {*mfc*} ; HYP) ; (equal LHS RHS)) ; Since the introduction of ev' makes w' a conservative extension of w, it ; suffices to show that the formula just above is a theorem of w'. Since (*) ; has been shown to be a theorem of w', then it suffices to show that the ; following is a theorem of w'. ; (+) ; (implies ; {*mfc*} ; (forall (obj1) ; (ev' (meta-extract-contextual-fact obj1 *mfc* *the-live-state*) A0))) ; But we now argue that this is indeed a theorem. Informally, we think of it ; as a way to formalize the spec for meta-extract-contextual-fact: that it only ; produces terms that evaluate to true. To see why (+) is a theorem, we focus ; on the case that obj has the form (:rw term obj nil). Then the above call of ; meta-extract-contextual-fact is equal to a term of the form (equal lhs0 ; rhs0), where rhs0 is the result of applying mfc-rw-fn to lhs0, *mfc*, and a ; state whose world is w, the world of *mfc*. The key is that in such a case, ; mfc-rw-fn rewrites a term to one that is equal to it with respect to the ; hypotheses of *mfc* including its world, w. A little more precisely, we ; arrange that mfc-rw-fn -- and mfc-ts-fn, and so on -- all have ; unknown-constraints, but we conceive of those constraints as coming from ; clocked, logic mode versions of corresponding prover routines. For example, ; we conceive of the definition of mfc-rw-fn as following the definition of ; rewrite, but with a clock and using analogous logic-mode supporting functions ; (just as discussed above for magic-ev-fncall), so that the original term is ; returned if the clock expires. That clock has an initial value that is ; greater than all clocks that could be needed for termination in support of ; all calls ever actually made, in the sense of this story. This approach ; guarantees that any value computed by rewrite can be legitimately used as a ; value returned by mfc-rw-fn; that is, the returned value is provably equal to ; the call of mfc-rw-fn on its inputs. But by the (conceived) definition of ; mfc-rw-fn as a logic mode function, the proof obligations pertaining to ; mfc-rw-fn for (+) are provable. By extending this argument to other mfc- ; functions, we see that (+) is a theorem. ; It remains to modify the arguments above in the case of clause-processors. ; The terms in META-EXTRACT-HYPS are then all calls of ; meta-extract-global-fact+, not meta-extract-contextual-fact. The argument ; then proceeds in analogy to how it went before, thus for example replacing ; (ev' 'HYP A0) by (forall aa (ev' 'CLAUSES-RESULT aa)), where CLAUSES-RESULT ; is the formal conjunction of the (disjunctions of the) clauses returned by ; the clause-processor. This hypothesis is a theorem (by the Evaluator ; Elimination Lemma), however, because by hypothesis, these clauses are all ; theorems. ; We remark briefly on the relation between guards and ancestors in our ; criterion for using attachments in meta-level reasoning. Above, we argue ; that we can restrict to attachments to functions ancestral in metafunctions ; or clause-processors. But how do we know that evaluation produces theorems ; in the resulting evaluation history? If raw-Lisp functions installed by ACL2 ; involve mbe, then we need to know that their guards hold. Thus we need to ; know that the guard proof obligation holds when a function is calling its ; attachment. This was in essence proved when the defattach event was ; admitted, but after applying the entire functional substitution of that ; event. Thus, we include guards in our notion of ancestor so that this guard ; obligation clearly holds; see the calls of canonical-ancestors-lst in ; function chk-evaluator-use-in-rule. ; So, we enrich the notion of ancestor to include guards. However, we can ; weaker our notion of ancestor to avoid the next-to-last argument of ; return-last, except when it is used to implement mbe (see function ; canonical-ffn-symbs). This weakening was inspired by an example sent to us ; by Sol Swords, who derived it from his own experience, and is justified by ; imagining that all such calls of return-last are expanded away before storing ; events. The parameter rlp passed to our functions is true when this special ; handling of return-last is to be performed. ; End of Essay on Correctness of Meta Reasoning ; Rockwell Addition: This is beginning of the nu-rewriter. It extends ; right through the beginning of the rewrite clique. ; The NU-Rewriter ; In this section we define the nth/update-nth rewriter, aka ; nu-rewriter, which provides fast simplification of terms of the form ; (nth i (update-nth j v s)) and (nth i (update-nth-array j k v s)). ; Someday we hope to generalize this to arbitrary rules. ; This work was inspired and driven by Matt Wilding and Dave Greve at ; Rockwell Advanced Technology Center. The abstract algorithm is ; described in "Rewriting for Symbolic Execution of State Machines" by ; J Strother Moore. That description is referred to in some of the ; comments below as "the paper". (deflabel nu-rewriter :doc ":Doc-Section Miscellaneous rewriting ~c[NTH]/~c[UPDATE-NTH] expressions~/ The rewriter contains special provisions for rewriting expressions composed of ~c[nth], ~c[update-nth], ~c[update-nth-array], together with ~c[let] expressions and other applications of non-recursive functions or ~c[lambda] expressions. For details see the paper ``Rewriting for Symbolic Execution of State Machine Models'' by J Strother Moore. Also ~pl[set-nu-rewriter-mode].~/ The ``nu-rewriter'' is a recent addition to the main rewrite engine in ACL2. Consider the expression ~bv[] (let ((s (update-nth 1 (new-a x s) s))) (let ((s (update-nth 2 (new-b x s) s))) (let ((s (update-nth 3 (new-c x s) s))) s))) ~ev[] If the ~c[let]s in this expression are expanded, a very large expression results because of the duplicate occurrences of ~c[s]: ~bv[] (update-nth 3 (new-c x (update-nth 2 (new-b x (update-nth 1 (new-a x s) s)) (update-nth 1 (new-a x s) s))) (update-nth 2 (new-b x (update-nth 1 (new-a x s) s)) (update-nth 1 (new-a x s) s))). ~ev[] This expansion of the ~c[let] expression can be very expensive in space and time. In particular, the ~c[(new-a x s)] expression might be rewritten many times. Now imagine asking what 2nd component of the structure is. That is, consider ~bv[] (nth 2 (let ((s (update-nth 1 (new-a x s) s))) (let ((s (update-nth 2 (new-b x s) s))) (let ((s (update-nth 3 (new-c x s) s))) s)))) ~ev[] The normal ACL2 rewrite engine would answer this question by first rewriting the arguments to the ~c[nth] expression; in particular, it would expand the nested ~c[let] expression to the large nested ~c[update-nth] expression and then, using rules such as ~bv[] (defthm nth-update-nth (equal (nth m (update-nth n val l)) (if (equal (nfix m) (nfix n)) val (nth m l)))) ~ev[] would reduce the expression to ~c[(new-b x (update-nth 1 (new-a x s) s))]. The purpose of the nu-rewriter is to allow simplifications like this without first expanding the ~c[let]s. The ``nu'' in the name is an acronym for ``~c[nth/update-nth]''. The nu-rewriter knows how to move an ~c[nth] into a ~c[let] without expanding the ~c[let] and how to simplify it if it nestles up against an ~c[update-nth]. There are four characteristics of this problem: the presence of ~c[nth], the presence of ~c[update-nth], the use of ~c[let] to provide ``sequential'' updates, and the use of constant indices. ~c[Nth] and ~c[update-nth] need not occur explicitly; they may be used inside of definitions of ``wrapper'' functions. Because the nu-rewriter changes the order in which things are rewritten, its routine use can make ACL2 unable to reproduce old proofs. It is therefore switched off by default. If your application exhibits the characteristics above, you might wish to switch the nu-rewriter on using ~ilc[set-nu-rewriter-mode]. More will eventually be written about the nu-rewriter. However, it is described in detail in the paper cited at the beginning of this documentation topic.~/") ; We first present all of the raw Lisp definitions we will need. ; All of this is concerned with memoization, but some of the memoization ; code is not raw. ; Rockwell Addition: A major problem with the nu-rewriter implementation ; is that I do not express it 100% in ACL2 but use a lot of raw Lisp in the ; cache maintenance. The defparameters below are, of course, only used in ; code that is protected by the acl2-loop-only read conditional for the ; non-logic case. But the logic case of that code doesn't usually correspond ; to the "real" code. That is, I will often write ; #-acl2-loop-only ; Rockwell Addition ; (raw lisp code) ; #+acl2-loop-only ; Rockwell Addition ; (acl2 code) ; but the two codes are not equivalent under any stretch of the imagination. ; Every such read conditional ought to be inspected and I would like, ; eventually, to replace them all by ACL2. ; Here are all the defparameters associated with the nu-rewriter. #-acl2-loop-only ; Rockwell Addition (defparameter *nth-update-tracingp* nil) #-acl2-loop-only ; Rockwell Addition (defparameter *lambda-abstractp* t) #-acl2-loop-only ; Rockwell Addition (defparameter *nu-memos* (make-array$ '(65535))) #-acl2-loop-only ; Rockwell Addition (defparameter *nu-memos-ens* nil) #-acl2-loop-only ; Rockwell Addition (defparameter *nu-memos-wrld* nil) #-acl2-loop-only ; Rockwell Addition (defparameter *ring-buffer-size* (the (integer 0 1000) 5)) ; The cache is an array, named *nu-memos*, with indices 0 through ; 65534 (inclusive). Accessing the array at location i returns a pair ; (n . ring), where ring is a ``ring buffer'' described below and n is ; the number of items in the ring. That number is never larger than ; *ring-buffer-size*. ; Note: The simple-vector forms below cause failure when running interpreted in ; GCL 2.6.6, 2.6.7, and a preliminary version of 2.6.8. But they are fine in ; GCL 2.7.0 as of April 2006, and when running compiled, so we leave them as ; is. #-acl2-loop-only ; Rockwell Addition (defun-one-output initialize-nu-memos (i) ; Call this with i = 65534. (declare (type (integer -1 65534) i)) (cond ((= i -1) nil) (t (setf (svref (the (simple-vector 65535) *nu-memos*) (the (integer 0 65534) i)) (cons 0 nil)) (initialize-nu-memos (- i 1))))) #-acl2-loop-only ; Rockwell Addition (defun-one-output clear-nu-memos1 (i) ; Call this with i = 65534. (declare (type (integer -1 65534) i)) (cond ((= i -1) nil) (t (let ((pair (svref (the (simple-vector 65535) *nu-memos*) (the (integer 0 65534) i)))) (setf (car pair) 0) (setf (cdr pair) nil)) (clear-nu-memos1 (- i 1))))) ; The answers stored in the cache are sensitive to ens and wrld. But ; we do not store those objects in the k-tuple of inputs. Instead, we ; associate them implicitly with the cache. Every item in the cache ; was computed with ens *nu-memos-ens* and wrld *nu-memos-wrld*. When ; we clear the cache we actually let the old items persist if the ens ; and wrld are the same. #-acl2-loop-only ; Rockwell Addition (defun-one-output clear-nu-memos (reallyp ens wrld) (cond ((or reallyp (not (and (equal ens *nu-memos-ens*) (eq wrld *nu-memos-wrld*)))) (clear-nu-memos1 65534) (setq *nu-memos-ens* ens) (setq *nu-memos-wrld* wrld) t) (t nil))) ; A ring buffer is a linked list of records. Each record is actually ; a triple. The three fields are accessed by the macros below. These ; macros are used both in raw lisp code and regular code, so they are ; not raw. A triple is of the form (item next . prev) where item is ; the data item stored in the record and next and prev are pointers to ; the next and previous record in the ring. This structure is ; circular. Recall that the number of items in the ring is given ; outside the ring. (defmacro this-item (x) `(car ,x)) (defmacro next-ptr (x) `(cadr ,x)) (defmacro prev-ptr (x) `(cddr ,x)) ; Given two records ; x: (xitem xnext . xprev) ; y: (yitem ynext . yprev) ; the following function links them so that y is the next record after ; x (and x is the previous record to y). ; x: (xitem y . xprev) ; y: (yitem ynext . x) #-acl2-loop-only ; Rockwell Addition (defun-one-output link-em (x y) (setf (next-ptr x) y) (setf (prev-ptr y) x)) #-acl2-loop-only ; Rockwell Addition (defun-one-output destructive-get-memo1 (d1 d2 d3 ptr0 ptr1 ptr2) ; In general, we do not know (at this level) what the items of our ; ring buffers hold. But we assume here that we can access the car, ; cadr, and caddr of an item. This function searches down the link ; structure of ptr2 for an item whose first three elements are d1, d2, ; and d3 respectively. (We don't want to waste the time and space of ; consing up the key to look for it.) We fail when ptr2 reaches ptr0, ; and we return nil. Ptr1 is always the prev record from ptr2. ; ptr0 -> itema <-+ ; ... | ; ptr1 -> itemi | ; ptr2 -> itemj | ; itemk | ; ... | ; itemz | ; |____| ; Suppose we find that itemj is the one we're looking for (its first ; three elements are d1, d2, and d3). Then we move it to the front ; so that the ring looks like: ; ptr0 -> itema <-+ ; ... | ; ptr1 -> itemi | ; itemk | ; ... | ; itemz | ; ans -> itemj | ; |____| ; and we return ans as the new ring. (cond ((eq ptr2 ptr0) nil) ((and (eq d1 (car (this-item ptr2))) (equal d2 (cadr (this-item ptr2))) (equal d3 (caddr (this-item ptr2)))) (link-em ptr1 (next-ptr ptr2)) (link-em (prev-ptr ptr0) ptr2) (link-em ptr2 ptr0) ptr2) (t (destructive-get-memo1 d1 d2 d3 ptr0 ptr2 (next-ptr ptr2))))) #-acl2-loop-only ; Rockwell Addition (defun-one-output destructive-get-memo (d1 d2 d3 rbuff) ; We search rbuff for the item whose first three elements are d1, d2, ; and d3. We return nil if we don't find it. If we find it, we ; rearrange the ring so that the found item is at the front and return ; that ring. (cond ((null rbuff) nil) ((and (eq d1 (car (this-item rbuff))) (equal d2 (cadr (this-item rbuff))) (equal d3 (caddr (this-item rbuff)))) rbuff) (t (destructive-get-memo1 d1 d2 d3 rbuff rbuff (next-ptr rbuff))))) (defun get-memo (recursively term stack memo-alist) ; We ignore memo-alist and use the *nu-memos* as our cache. Memo-alist is ; a fiction to help us keep our story straight. We search the cache ; for an item whose first three elements are the first three args above, ; recursively, term, and stack. If we find it, it will look like: ; (recursively term stack flg term2 stack2 ttree2) ; and means that nth-update-rewrite1 returned (mv flg term2 stack2 ttree2) ; as the answer for ; (nth-update-rewrite1 recursivelyp term stack ; *nu-memos-ens* *nu-memos-wrld* state memo-alist) ; In that case, we return (mv t flg term2 stack2 ttree2). Otherwise we return ; (mv nil nil nil nil nil). #-acl2-loop-only ; Rockwell Addition (declare (ignore memo-alist)) ; Rockwell Addition: Non-equivalent read conditionals! #-acl2-loop-only ; Rockwell Addition (let* ((pair (svref (the (simple-vector 65535) *nu-memos*) (the (integer 0 65534) (mk term stack)))) ; (car pair) is a natural number and (cdr pair) is a ring buffer. ; The number is how many items are in the ring buffer. (temp (destructive-get-memo recursively term stack (cdr pair)))) (cond (temp (setf (cdr pair) temp) (let ((temp1 (cdddr (this-item temp)))) (mv t (car temp1) (cadr temp1) (caddr temp1) (cadddr temp1)))) (t (mv nil nil nil nil nil)))) #+acl2-loop-only ; Rockwell Addition ; Lies. All lies... But I can imagine making memo-alist behave this ; way... (let ((temp (assoc-equal (list recursively term stack) memo-alist))) (cond (temp (mv t (car (cdr temp)) (cadr (cdr temp)) (caddr (cdr temp)) (cadddr (cdr temp)))) (t (mv nil nil nil nil nil))))) #-acl2-loop-only ; Rockwell Addition (defun-one-output destructive-retire-memo (item rbuff) ; Rbuff is a ring buffer whose first item is the most recently hit and ; whose length is equal to the maximum ring buffer size. We add item ; into the ring, bumping out the last item (which is the prev of the ; first one) and rotate so that that record is the first. (let ((new (prev-ptr rbuff))) (setf (this-item new) item) new)) #-acl2-loop-only ; Rockwell Addition (defun-one-output destructive-insert-memo (item rbuff) ; Rbuff is a ring buffer whose size is less than the allowed maximum. ; We add a record for item and make it the first. (cond ((null rbuff) (let ((temp (cons item (cons nil nil)))) (setf (next-ptr temp) temp) (setf (prev-ptr temp) temp) temp)) (t (let ((temp (cons item (cons nil nil)))) (link-em (prev-ptr rbuff) temp) (link-em temp rbuff) temp)))) ; These stat functions are only used to print stats about the cache. ; I don't document them. #-acl2-loop-only ; Rockwell Addition (defun-one-output nu-memo-stats1 (i buckets items full-buckets) (declare (type (integer -1 65534) i)) (cond ((= i -1) (list buckets items full-buckets)) (t (let ((n (car (svref (the (simple-vector 65535) *nu-memos*) (the (integer 0 65534) i))))) (if (equal n 0) (nu-memo-stats1 (- i 1) buckets items full-buckets) (nu-memo-stats1 (- i 1) (+ 1 buckets) (+ n items) (cond ((equal n *ring-buffer-size*) (+ 1 full-buckets)) (t full-buckets)))))))) #-acl2-loop-only ; Rockwell Addition (defun-one-output nu-memo-stats () (let* ((triple (nu-memo-stats1 65534 0 0 0)) (buckets (car triple)) (items (cadr triple)) (full-buckets (caddr triple))) (cw " Memo Stats (*ring-buffer-size* = ~x0)~%~ ~ Items: ~x1~%~ ~ Buckets: ~x2~%~ ~ Full: ~x3~%" *ring-buffer-size* items buckets full-buckets))) (defun memo-key1 (term) ; We compute a hash key for term. This is an ACL2 function, not just a ; raw lisp function. See memo-key. (the (unsigned-byte 14) (cond ((variablep term) 1) ((quotep term) (if (integerp (cadr term)) ; Rockwell Addition: I think these two read conditionals are equivalent. #-acl2-loop-only ; Rockwell Addition (if (eq (type-of (cadr term)) 'fixnum) (mod (the fixnum (cadr term)) 16384) (mod (the integer (cadr term)) 16384)) ; 2^14 #+acl2-loop-only ; Rockwell Addition (mod (the integer (cadr term)) 16384) 2)) ((null (fargs term)) 3) ((null (cdr (fargs term))) (memo-key1 (fargn term 1))) (t (mod (the (unsigned-byte 16) (+ (the (unsigned-byte 14) (memo-key1 (fargn term 1))) (the (unsigned-byte 15) (* 2 (the (unsigned-byte 14) (memo-key1 (fargn term 2))))))) 16384))))) (defun bounded-length (lst ans max) (declare (type (signed-byte 30) ans max)) ; We assume ans <= max. (the-fixnum (cond ((null lst) ans) ((= ans max) max) (t (bounded-length (cdr lst) (+ 1 ans) max))))) (defun memo-key (term stack) ; This is how we compute a hash key for term and stack. ; Experiments suggest that with the hash function below the optimal ; bucket size is 3-4. But if you change the *s to +s, and the bucket ; size to 5-7, you get slightly better performance even though there ; are many fewer items in the table. (the (unsigned-byte 29) (* (the (unsigned-byte 14) (mod (the (unsigned-byte 28) (* (the (unsigned-byte 14) (memo-key1 term)) (the (unsigned-byte 14) (+ 1 (the (unsigned-byte 13) (bounded-length stack 0 8191)))))) 16384)) (the (unsigned-byte 15) (+ 1 (the (unsigned-byte 14) (memo-key1 (cadr (car stack))))))))) (defun mk (term stack) (the (integer 0 65534) (mod (the (unsigned-byte 29) (memo-key term stack)) 65535))) ; These show functions are just used for debugging... (defun show-rbuff1 (r s) (if (eq r s) nil (cons (this-item s) (show-rbuff1 r (next-ptr s))))) (defun show-rbuff (r) (cond ((null r) nil) ((eq (next-ptr r) r) (list (this-item r))) (t (cons (this-item r) (show-rbuff1 r (next-ptr r)))))) ; As noted, this implementation of memoization is in raw lisp. But we ; act as though the memo hash array is an ACL2 object and generally ; pass it around in the variable memo-alist. Think of memo-alist as ; an alist that really does associate our inputs and outputs in just ; the way the hash array does. Of course, to add something to it we ; would have to cons and we don't do that. In fact, the memo-alist ; starts as nil and stays nil and is totally irrelevant except that it ; provides a fiction that explains what we're doing. The logical code ; grows the alist but that never, in fact, happens. (defun memo-exit (recursivelyp term stack flg term2 stack2 ttree2 memo-alist) ; We know that term/stack (with recursivelyp) doesn't occur in ; memo-alist. If flg is non-nil, we modify memo-alist so that it does ; memoize this call. In any case, we return the appropriate result. (mv flg term2 stack2 ttree2 ; Rockwell Addition: Non-equivalent read conditionals! #-acl2-loop-only ; Rockwell Addition (if flg (let* ((k (the (integer 0 65534) (mk term stack))) (pair (svref (the (simple-vector 65535) *nu-memos*) (the (integer 0 65534) k))) (item (list recursivelyp term stack flg term2 stack2 ttree2))) (declare (type (integer 0 65534) k)) (cond ((= (the (integer 0 1000) (car pair)) (the (integer 0 1000) *ring-buffer-size*)) (setf (cdr pair) (destructive-retire-memo item (cdr pair)))) (t (setf (car pair) (the (integer 0 1000) (+ 1 (the (integer 0 999) (car pair))))) (setf (cdr pair) (destructive-insert-memo item (cdr pair))))) memo-alist) memo-alist) #+acl2-loop-only ; Rockwell Addition ; Lies... We ought to modify memo-alist so that old entries that ; hash to the same key get deleted if there are too many... (cons (cons (list recursivelyp term stack) (list flg term2 stack2 ttree2)) memo-alist))) ; Now we get to the ACL2 code. (defun nfix-quote (x) ; X is known to be a quotep. We return the result of nfixing it. (if (and (integerp (cadr x)) (<= 0 (cadr x))) x *0*)) ; Stack is a list of frames. Each frame is of the form (vars . terms) ; and denotes the substitution carried out by subcor-vars. We will write ; term/stack to mean the instance of term obtained by successively ; applying each substitution in the stack, starting with the top-most. ; Alternatively, term/stack is just a lambda application, e.g., ; term/((vars1 . args1) ... (varsk . argsk)) is ; ((lambda varsk ; ... ; ((lambda vars1 term) ; args1)) ; argsk) (defun bound-in-framep (var vars terms) ; We exploit the fact that nil is not a term. We return the term to which ; var is bound or else we return nil. (cond ((endp vars) nil) ((eq var (car vars)) (car terms)) (t (bound-in-framep var (cdr vars) (cdr terms))))) (defun non-rec-defun1 (lemmas ens ttree) (cond ((endp lemmas) (mv nil nil ttree)) (t (let* ((lemma (car lemmas)) (subclass (access rewrite-rule lemma :subclass))) ; Note: It is assumed (twice) below that a rune whose base symbol is ; the name of the function the rule rewrites is the definition of the ; function. That is suppose the rule of some rune (:REWRITE fn) is ; (equal (fn ...) ...) Then we assume that this is the introduction ; of fn. The introduction of a function symbol can only be by ; constraint (which must have a different name than the function ; symbol itself) or by DEFUN, which uses the convention assumed here. ; The impact of our assumption is that if the rune is so named it is ; not necessary to check that the args of the lhs are distinct ; variables: that is ensured by DEFUN. (cond ((and (eq subclass 'definition) (eq (access rewrite-rule lemma :equiv) 'equal) (null (access rewrite-rule lemma :hyps)) (null (car (access rewrite-rule lemma :heuristic-info))) (enabled-numep (access rewrite-rule lemma :nume) ens) (or (eq (ffn-symb (access rewrite-rule lemma :lhs)) (cadr (access rewrite-rule lemma :rune))) (and (symbol-listp (fargs (access rewrite-rule lemma :lhs))) (no-duplicatesp (fargs (access rewrite-rule lemma :lhs)))))) (mv (fargs (access rewrite-rule lemma :lhs)) (access rewrite-rule lemma :rhs) (push-lemma (access rewrite-rule lemma :rune) ttree))) ((and (eq subclass 'abbreviation) (eq (access rewrite-rule lemma :equiv) 'equal) (enabled-numep (access rewrite-rule lemma :nume) ens) (or (eq (ffn-symb (access rewrite-rule lemma :lhs)) (cadr (access rewrite-rule lemma :rune))) (and (symbol-listp (fargs (access rewrite-rule lemma :lhs))) (no-duplicatesp (fargs (access rewrite-rule lemma :lhs)))))) (mv (fargs (access rewrite-rule lemma :lhs)) (access rewrite-rule lemma :rhs) (push-lemma (access rewrite-rule lemma :rune) ttree))) (t (non-rec-defun1 (cdr lemmas) ens ttree))))))) (defun non-rec-defun (fn ens wrld ttree) ; Fn must be a function symbol or lambda expression. We return (mv ; formals body ttree'), where, if body is non-nil, it is a theorem that: ; (equal (fn . formals) body) ; and formals is a list of distinct variable names. Ttree' is an ; extension of ttree that records the rule we used. Furthermore, the ; rule is enabled, body is non-recursive in fn, and fn is neither HIDE ; nor NTH-UPDATE-ARRAY (which are treated here as though they were ; undefined). ; If fn is an enabled non-recursive function (other than HIDE, etc), ; we return the standard formals and body from its def-body. ; Otherwise, we look for the most recently proved enabled ; non-recursive unconditional EQUALity :definition of fn with distinct ; vars on the lhs. ; Note: If you prove a non-recursive unconditional :definition of a ; function and you want it used by the nu-rewriter as though it were ; the definition, you must DISABLE the function. Otherwise, we just ; use the standard formals and body as above. This decision was made ; for efficiency. It is expensive to go to the property list and scan ; all the lemmas looking for an enabled non-recursive unconditional ; EQUALity :definition of fn with distinct vars on the lhs -- ; especially if we do it every time that deref recurs! (cond ((flambdap fn) (mv (lambda-formals fn) (lambda-body fn) ttree)) ((or (eq fn 'if) (eq fn 'hide) (eq fn 'update-nth-array)) (mv nil nil ttree)) (t (let ((def-body (def-body fn wrld))) (cond ((or (null def-body) (access def-body def-body :recursivep)) (mv nil nil ttree)) ((enabled-numep (access def-body def-body :nume) ens) (let ((formals (access def-body def-body :formals))) (mv formals (latest-body (fcons-term fn formals) (access def-body def-body :hyp) (access def-body def-body :concl)) (push-lemma (access def-body def-body :rune) ttree)))) (t ; (print (list 'checking fn 'props)) (non-rec-defun1 (getprop fn 'lemmas nil 'current-acl2-world wrld) ens ttree))))))) ; The paper talks about ``facets.'' But we never create facets as ; objects in this code. Instead, we keep pairs of variables, e.g., ; term and stack, that together represent the facet in question. (mutual-recursion (defun deref-var (var stack ens wrld ttree) ; We return (mv term' stack' ttree'), where is the ; preferred facet of and ttree' is an extension of ttree ; that records the runes used. (cond ((endp stack) (mv var nil ttree)) (t (let ((temp (bound-in-framep var (car (car stack)) (cdr (car stack))))) (cond (temp (deref temp (cdr stack) ens wrld ttree)) (t (deref-var var (cdr stack) ens wrld ttree))))))) (defun deref (term stack ens wrld ttree) (cond ((variablep term) (deref-var term stack ens wrld ttree)) ((fquotep term) (mv term nil ttree)) (t (let ((fn (ffn-symb term))) (mv-let (formals body ttree) (non-rec-defun fn ens wrld ttree) (cond (body (deref body (cons (cons formals (fargs term)) stack) ens wrld ttree)) (t (mv term stack ttree)))))))) ) ; Once upon a time we used the following code to determine whether two ; facets are equal. But in Rockwell trials it never did anything ; different than the faster version used below. ; (mutual-recursion ; ; (defun equal-derefs (term1 stack1 term2 stack2 ens wrld) ; (mv-let ; (term1 stack1) ; (deref term1 stack1 ens wrld) ; (mv-let ; (term2 stack2) ; (deref term2 stack2 ens wrld) ; (cond ; ((and (equal term1 term2) ; (equal stack1 stack2)) ; t) ; ((variablep term1) ; ; ; When deref returns a var, it is unbound. If both are vars, they are ; ; denote equal terms precisely if they are the same (unbound) var. If ; ; one is a var and the other isn't, we don't know if they denote equal ; ; terms. ; ; (if (equal term1 term2) ; t ; '?)) ; ((variablep term2) '?) ; ((fquotep term1) ; (if (fquotep term2) ; (equal term1 term2) ; '?)) ; ((fquotep term2) '?) ; ((eq (ffn-symb term1) (ffn-symb term2)) ; (equal-derefs-lst (fargs term1) stack1 ; (fargs term2) stack2 ; ens wrld)) ; (t '?))))) ; ; (defun equal-derefs-lst (term-lst1 stack1 term-lst2 stack2 ens wrld) ; (cond ((endp term-lst1) t) ; (t (let ((flg (equal-derefs (car term-lst1) stack1 ; (car term-lst2) stack2 ; ens wrld))) ; (cond ; ((eq flg t) ; (equal-derefs-lst (cdr term-lst1) stack1 ; (cdr term-lst2) stack2 ; ens wrld)) ; (t '?))))))) (defun equal-derefs (term1 stack1 term2 stack2) (cond ((and (quotep term1) (quotep term2)) (equal (cadr term1) (cadr term2))) ((and (equal term1 term2) (equal stack1 stack2)) t) (t '?))) ; Next we develop the notion of lambda abstraction, i.e., how to ; create the lambda application corresponding to the facet ; . The interesting part is creating simple, correct ; lambda applications. (defun filter-args (formals vars actuals) (cond ((endp formals) nil) ((member-eq (car formals) vars) (cons (car actuals) (filter-args (cdr formals) vars (cdr actuals)))) (t (filter-args (cdr formals) vars (cdr actuals))))) (mutual-recursion (defun foccurrences (term1 term2 ans) ; We ``count'' the number of occurrences of term1 in term2, ; ``summing'' the result into ans to be tail recursive, except: ; ans = nil means we've seen 0 ; ans = t means we've seen 1 ; ans = > means we've seen 2 or more ; Thus, nil + 1 = t ; t + 1 = > ; > + 1 = > ; so (+ ans 1) is (if ans '> t) and we can short-circuit whenever ans ; is >. ; Observe that if (eq (foccurrences term1 term2 t) '>) is t, then term1 ; occurs at least once in term2. This could also be tested by asking ; whether (foccurrences term1 term2 nil) is non-nil, but that idiom is ; less efficient because the former short-circuits as soon as the ; first occurrence is found, while the latter doesn't short-circuit ; until the second occurrence (if any) is found. (cond ((equal term1 term2) (if ans '> t)) ((eq ans '>) '>) ((variablep term2) ans) ((fquotep term2) ans) (t (foccurrences-lst term1 (fargs term2) ans)))) (defun foccurrences-lst (term lst ans) (cond ((endp lst) ans) ((eq ans '>) '>) (t (foccurrences-lst term (cdr lst) (foccurrences term (car lst) ans)))))) (defun every-var-at-most-oncep (vars body) ; Return t iff every var in vars occurs at most once in body. That ; is, if no var in vars occurs more than once. (cond ((endp vars) t) ((eq (foccurrences (car vars) body nil) '>) nil) (t (every-var-at-most-oncep (cdr vars) body)))) (defun every-actual-simple (args) (cond ((endp args) t) ((or (variablep (car args)) (fquotep (car args))) (every-actual-simple (cdr args))) (t nil))) (defun make-clean-lambda-application (formals body args) ; Build a lambda application ((lambda formals body) . args), except ; clean it up. For example ((lambda (s) s) a) becomes just a, ; ((lambda (s) '123) a) becomes just '123, and ((lambda (x y) (foo x)) ; a b) becomes ((lambda (x) (foo x)) a). ; WARNING: This function invites incorrect improvements! Don't mess ; with body! For example one might be tempted to substitute into body ; for some of the formals (e.g., those occurring only once) while ; leaving the duplicated formals. This would be wrong. It might ; introduce a var into body that is not bound in the formals. It ; might also cause a var in the body to be captured by the remaining ; formals. Just don't mess with body unless you're clear headed! (let ((body-vars (all-vars body))) (cond ((not (subsetp-eq body-vars formals)) (er hard 'make-clean-lambda-application "It was supposedly impossible to derive a lambda body ~ containing variables not bound in the lambda formals. ~ But the proposed lambda body ~p0 contains variables not ~ in ~x1." body formals)) ((null body-vars) ; ((lambda (v1 ... vk) 'evg) a1 ... ak) => 'evg. However, we also deal here ; with the case that the body is a non-quoted constant involving no vars, ; like (F (G) (H)). body) ((every-var-at-most-oncep body-vars body) ; ((lambda (v1 ... vk) vi) a1 ... ak) => ai ; ((lambda (v1 ... vk) (f vi vj)) a1 ... ak) => (f ai aj). ; We could, in general, always return the following, it is just an ; explosive idea if some var occurs more than once. (subcor-var formals args body)) (t (let ((minimal-formals (filter-args formals body-vars formals)) (minimal-args (filter-args formals body-vars args))) (cond ((every-actual-simple minimal-args) (subcor-var minimal-formals minimal-args body)) ((subsetp minimal-formals body-vars) (fcons-term (make-lambda minimal-formals body) minimal-args)) (t (fcons-term (make-lambda minimal-formals body) minimal-args)))))))) (defun lambda-stack (stack term) ; Build the lambda application denoted by term/stack. (cond ((endp stack) term) (t (lambda-stack (cdr stack) (make-clean-lambda-application (car (car stack)) term (cdr (car stack))))))) ; We next deal with reconciliaton. The version described in the paper ; is quite simple and is implemented below. But for many weeks we ; experimented with a different version, based on a notion called ; ``widgets.'' That version was ultimately unsound. But it might ; actually be better than the simple version and if you are interested ; in improving reconcilliation, look for the file ; /v/hank/v104/rockwell/acl2x/v2-5/rewrite.lisp and find the first ; occurrence of the word "widget." ; The function that reconciles a list of stacks is strange in that it ; ignores those stacks corresponding to quoted constants. So most of ; these functions take a list of terms and skip the stack in question ; if the term is quoted. To understand what is going on, see ; reconcile-stacks. (defun shorten-each-stack-to-len (n terms stacks) ; We shorten each stack in stacks to be length n, except those that ; correspond to quoted terms, which are simply set to nil. (cond ((endp stacks) nil) (t (cons (cond ((quotep (car terms)) nil) (t (nthcdr (- (len (car stacks)) n) (car stacks)))) (shorten-each-stack-to-len n (cdr terms) (cdr stacks)))))) (defun all-equal-stacks (the-stack terms stacks) (cond ((endp stacks) t) ((quotep (car terms)) (all-equal-stacks the-stack (cdr terms) (cdr stacks))) ((eq the-stack t) (all-equal-stacks (car stacks) (cdr terms) (cdr stacks))) (t (and (equal the-stack (car stacks)) (all-equal-stacks the-stack (cdr terms) (cdr stacks)))))) (defun non-quoted-stack (terms stacks) (cond ((endp stacks) nil) ((quotep (car terms)) (non-quoted-stack (cdr terms) (cdr stacks))) (t (car stacks)))) (defun cdr-all (x) (cond ((endp x) nil) (t (cons (cdr (car x)) (cdr-all (cdr x)))))) (defun len-common-tail (n terms stacks) (cond ((zp n) 0) ((all-equal-stacks t terms stacks) n) (t (len-common-tail (- n 1) terms (cdr-all stacks))))) (defun butlast-all-stacks (terms stacks n) (cond ((endp stacks) nil) (t (cons (if (quotep (car terms)) nil (butlast (car stacks) n)) (butlast-all-stacks (cdr terms) (cdr stacks) n))))) (defun min-stack-len (terms stacks) (cond ((endp stacks) nil) ((quotep (car terms)) (min-stack-len (cdr terms) (cdr stacks))) (t (let ((x (len (car stacks))) (temp (min-stack-len (cdr terms) (cdr stacks)))) (cond (temp (min x temp)) (t x)))))) (defun reconcile-stacks (terms stacks) ; For the moment, forget about terms. Stacks is a list of n stacks, ; (stack1 ... stackn). We return (mv (list ext1 ... extn) stack), ; where stack is the greatest common ancestor of all the stacki and ; each exti has the property stacki = (append exti stack). ; For example, if stacks is: ; ((a b c d e f g) ; (x d e f g) ; (y y d e f g) ; (z d e f g) ; (u u u d e f g)) ; ^ ; then we return (mv '((a b c) (x) (y y) (z) (u u u)) '(d e f g)). ; (The above are not really stacks, but function doesn't look at the ; structure beyond what is shown above.) We do this by first ; computing the length of the shortest stack, in this case 5. We then ; shorten every stack to that length. Then we compare them all. If ; they're all the same, that's our common ancestor. Else, we cdr each ; and repeat. Once we find the common ancestor, we use butlast to ; compute the initial part of each stack, where (butlast '(y y d e f ; g) 4) is (y y). ; Now what about terms? It is a list of n terms, in 1:1 ; correspondence with stacks. Each termi is interpreted under stacki. ; But if a term is a quoted constant, its interpretation is fixed ; regardless of the stack. So we do not let the stacks of the quoted ; terms influence our answer. (let ((min-len (min-stack-len terms stacks))) (cond ((null min-len) ; Then all the terms are quoted. (mv (butlast-all-stacks terms stacks 0) ; just a list of nils nil)) (t (let ((n (len-common-tail min-len terms (shorten-each-stack-to-len min-len terms stacks)))) (if (zp n) (mv stacks nil) (mv (butlast-all-stacks terms stacks n) (let ((stack (non-quoted-stack terms stacks))) (nthcdr (- (len stack) n) stack))))))))) (defun reconcile-terms (terms exts) (cond ((endp terms) nil) (t (cons (lambda-stack (car exts) (car terms)) (reconcile-terms (cdr terms) (cdr exts)))))) (defun all-equal (x lst) (declare (xargs :guard t)) (cond ((atom lst) t) (t (and (equal x (car lst)) (all-equal x (cdr lst)))))) (defun recon (terms stacks) ; Terms and stacks are in 1:1 correspondence. We return (mv terms' stack). ; Terms' and terms are in 1:1 correspondence, where, for corresponding ; termi, stacki, and termi', we have termi/stacki = termi'/stack. ; A correct answer is always delivered by the t clause below. The other ; two clauses are just special cases. One experiment with actual data ; suggests that the second special case below is almost always the one we ; see. Out of 82514 calls of this function on actual data (TOS0-TEST), ; we saw 81380 calls of the form handled by that case! The remaining ; cases were also pathological in some sense. (cond ((null (cdr terms)) (mv terms (car stacks))) ((and (quotep (car terms)) (all-equal (cadr stacks) (cddr stacks))) (mv terms (cadr stacks))) (t (mv-let (exts stack) (reconcile-stacks terms stacks) (mv (reconcile-terms terms exts) stack))))) (defun with-reconciliation-let-bindings (terms var) (cond ((endp terms) nil) (t (cons (list (car terms) `(car ,var)) (with-reconciliation-let-bindings (cdr terms) `(cdr ,var)))))) (defmacro with-reconciliation (terms stacks common-stack body) ; This macro allows us to write: ; (with-reconciliation (i j v s) ; (is js vs ss) ; stack ; ) ; and mean: ; (mv-let (temp stack) ; (recon (list i j v s) (list is js vs ss)) ; (let ((i (car temp)) ; (j (cadr temp)) ; (v (caddr temp)) ; (s (cadddr temp))) ; )) ; That is, we reconcile i/is, j/js, ..., to get new terms i, j, ... and a ; common stack stack, which we then do with as we please in the body. `(mv-let (with-reconciliation-temp ,common-stack) (recon (list ,@terms) (list ,@stacks)) (let ,(with-reconciliation-let-bindings terms 'with-reconciliation-temp) (check-vars-not-free (with-reconciliation-temp) ,body)))) ; Now we develop a special-purpose rule applier. (mutual-recursion (defun lambda-stack-one-way-unify1 (pat term stack alist ens wrld ttree) ; Alist binds vars in pat to term1/stack1 pairs: (var . (term1 ; . stack1)). No Change Loser on alist and ttree. (cond ((variablep pat) (let ((trip (assoc-eq pat alist))) (cond (trip (cond ((eq (equal-derefs (cadr trip) (cddr trip) term stack) t) (mv t alist ttree)) (t (mv nil alist ttree)))) (t (mv t (cons (cons pat (cons term (if (quotep term) nil stack))) alist) ttree))))) ((fquotep pat) (cond ((equal pat term) (mv t alist ttree)) (t (mv nil alist ttree)))) (t (mv-let (term stack ttree1) (cond ((variablep term) (deref-var term stack ens wrld ttree)) ((flambda-applicationp term) (deref term stack ens wrld ttree)) (t (mv term stack ttree))) (cond ((variablep term) (mv nil alist ttree)) ((fquotep term) ; This case is handled far more elaborately in one-way-unify1. ; Suppose term is '5 and pat is (+ 1 ...). Then in one-way-unify1 we ; recursively unify ... with 4. But here we just fail. (mv nil alist ttree)) ((eq (ffn-symb pat) (ffn-symb term)) (mv-let (ans alist1 ttree1) (lambda-stack-one-way-unify1-lst (fargs pat) (fargs term) stack alist ens wrld ttree1) (cond (ans (mv ans alist1 ttree1)) (t (mv nil alist ttree))))) (t (mv nil alist ttree))))))) (defun lambda-stack-one-way-unify1-lst (pat-lst term-lst stack alist ens wrld ttree) ; Warning: This function is NOT a no change loser! When it fails, ; the returned alist and ttree might not be the original ones. (cond ((endp pat-lst) (mv t alist ttree)) (t (mv-let (flg alist ttree) (lambda-stack-one-way-unify1 (car pat-lst) (car term-lst) stack alist ens wrld ttree) (cond (flg (lambda-stack-one-way-unify1-lst (cdr pat-lst) (cdr term-lst) stack alist ens wrld ttree)) (t (mv nil alist ttree))))))) ) (defun lambda-stack-one-way-unify (pat term stack ens wrld ttree) ; We one-way-unify pat with term/stack, returning (mv t alist stack' ttree') ; if we win and (mv nil nil nil ttree) otherwise. When we win, alist is an ; alist pairing variables of pat with terms, each of which is to be ; interpreted under stack'. That is, ((pat/alist)/stack') is ; term/stack. The returned ttree is an extension of ttree if we win and ; is ttree if we lose. ; No change loser on ttree. (mv-let (ans alist ttree) (lambda-stack-one-way-unify1 pat term stack nil ens wrld ttree) (cond (ans (let ((vars (strip-cars alist)) (terms (strip-cadrs alist)) (stacks (strip-cddrs alist))) (mv-let (terms common-stack) (recon terms stacks) (mv t (pairlis$ vars terms) common-stack ttree)))) (t (mv nil nil nil ttree))))) ; Here is how we apply certain rewrite rules to term/stack. (defun apply-abbrevs-to-lambda-stack1 (term stack ens wrld lemmas ttree) ; Term is known to be a function symbol application and lemmas are the ; rewrite lemmas for that symbol. We apply the first enabled ; abbreviation lemma (with equiv EQUAL) to term/stack. We restrict ; our attention to lemmas for which the variables on the rhs are a ; subset of those on the lhs. The subset restriction prevents the ; introduction of an unbound variable into the lambda expressions ; implicit in term'/stack'. We return (mv hitp term' stack' ttree'), where, ; if hitp is t, term/stack is equal to term'/stack' and ttree' is an ; extension of ttree. (cond ((endp lemmas) (mv nil term stack ttree)) ((and (enabled-numep (access rewrite-rule (car lemmas) :nume) ens) (equal (access rewrite-rule (car lemmas) :equiv) 'equal) (eq (access rewrite-rule (car lemmas) :subclass) 'abbreviation) (subsetp-eq (all-vars (access rewrite-rule (car lemmas) :rhs)) (all-vars (access rewrite-rule (car lemmas) :lhs)))) (mv-let (flg alist stack1 ttree) (lambda-stack-one-way-unify (access rewrite-rule (car lemmas) :lhs) term stack ens wrld ttree) (cond (flg #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (cw "Abbrev: ~x0~%" (access rewrite-rule (car lemmas) :rune)))) (let ((term1 (sublis-var alist (access rewrite-rule (car lemmas) :rhs)))) (mv t term1 (if (quotep term1) nil stack1) (push-lemma (access rewrite-rule (car lemmas) :rune) ttree)))) (t (apply-abbrevs-to-lambda-stack1 term stack ens wrld (cdr lemmas) ttree))))) (t (apply-abbrevs-to-lambda-stack1 term stack ens wrld (cdr lemmas) ttree)))) (defun apply-abbrevs-to-lambda-stack (hitp term stack ens wrld state ttree) ; We try to eval term/stack, if possible. Otherwise, we apply abbrev ; rules. If any rule hits, we repeat on the output of the rule. We ; return (mv hitp' term' stack' ttree'), where term/stack is equal to ; term'/stack' and hitp' is t if either hitp is t initially or we did ; something. Ttree' is an extension of ttree. (cond ((or (variablep term) (fquotep term) (flambda-applicationp term)) (mv hitp term stack ttree)) ; I once added this because we see expt's a lot. But I only gained one second ; out of 100, and it means expt can't be disabled. So I don't think this is ; worth it. ; ((and (eq (ffn-symb term) 'expt) ; (quotep (fargn term 1)) ; (quotep (fargn term 2))) ; (mv t ; (kwote (expt (fix (cadr (fargn term 1))) (nfix (cadr (fargn term 2))))) ; nil ; ttree)) ((and (null stack) (logicalp (ffn-symb term) wrld) (all-quoteps (fargs term)) (enabled-xfnp (ffn-symb term) ens wrld) ; We don't mind disallowing constrained functions that have attachments, ; because the call of ev-fncall below disallows the use of attachments (last ; parameter, aok, is nil). (not (getprop (ffn-symb term) 'constrainedp nil 'current-acl2-world wrld))) (mv-let (erp val latches) (pstk (ev-fncall (ffn-symb term) (strip-cadrs (fargs term)) state nil t nil)) (declare (ignore latches)) (cond (erp (mv-let (hitp1 term1 stack1 ttree) (apply-abbrevs-to-lambda-stack1 term stack ens wrld (getprop (ffn-symb term) 'lemmas nil 'current-acl2-world wrld) ttree) (cond (hitp1 (apply-abbrevs-to-lambda-stack t term1 stack1 ens wrld state ttree)) (t (mv hitp term stack ttree))))) (t #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (cw "Eval: ~x0~%" term))) (mv t (kwote val) nil (push-lemma (fn-rune-nume (ffn-symb term) nil t wrld) ttree)))))) (t (mv-let (hitp1 term1 stack1 ttree) (apply-abbrevs-to-lambda-stack1 term stack ens wrld (getprop (ffn-symb term) 'lemmas nil 'current-acl2-world wrld) ttree) (cond (hitp1 (apply-abbrevs-to-lambda-stack t term1 stack1 ens wrld state ttree)) ((eq (ffn-symb term) 'equal) (let ((p (equal-derefs (fargn term 1) stack (fargn term 2) stack))) (cond ((eq p '?) (mv hitp term stack ttree)) (p (mv t *t* nil ttree)) (t (mv t *nil* nil ttree))))) (t (mv hitp term stack ttree))))))) ; We create a fake rune (see the Essay on Fake-Runes) for the ; nu-rewriter. This allows us to record that something was done. It ; would probably be sufficient to add (:rewrite nth-update-nth) or ; (:rewrite nth-update-nth-array) to the ttree, as appropriate, to ; report a hit. But what if one of those runes is disabled? We did ; not want to use a disabled rune. And we did not want to give the ; user three ways to shut off the nu-rewriter (by using the ; nu-rewriter-mode or disabling one of those runes). So we use this ; fake rune instead and simply don't are whether the nth-update-nth ; runes are enabled or not. It is as though this were a metafunction ; justified by the existence of those runes but independent of them in ; application. (defconst *fake-rune-for-nu-rewriter* '(:FAKE-RUNE-FOR-NU-REWRITER nil)) ; And here is the special-purpose rewriter itself. (mutual-recursion (defun nth-update-rewriter1-continue (recursivelyp term stack flg1 term1 stack1 ttree1 try-abbrevsp ens wrld state memo-alist) ; This function is used to memoize the fact that on the argument ; triple (recursivelyp, term, stack) nth-update-rewriter1 returned ; (flg1, term1, stack1, ttree1) -- EXCEPT that we continue to simplify by ; applying abbreviations to term1/stack1 -- which also evaluates ; ground function applications -- and recursively calling ; nth-update-rewriter1 on the result. We actually memoize the final ; result, not the intermediate one. (cond (try-abbrevsp (mv-let (flg2 term2 stack2 ttree2) (apply-abbrevs-to-lambda-stack nil term1 stack1 ens wrld state ttree1) (cond (flg2 ; (format t "Abbreves hit!~%") (mv-let (flg3 term3 stack3 ttree3 memo-alist) (nth-update-rewriter1 recursivelyp term2 stack2 ens wrld state memo-alist) (declare (ignore flg3)) (memo-exit recursivelyp term stack t term3 stack3 (cons-tag-trees ttree2 ttree3) memo-alist))) (t ; If we were told to try abbrevs and none fired, then we are done. We ; assume that the input has already had nth-update-rewriter1 applied ; to it, if we're trying abbrevs. To do otherwise is risk a loop. (memo-exit recursivelyp term stack flg1 term1 stack1 ttree1 memo-alist))))) (flg1 ; We're not trying abbrevs and the original input changed from term to ; term1. So try again. (mv-let (flg3 term3 stack3 ttree3 memo-alist) (nth-update-rewriter1 recursivelyp term1 stack1 ens wrld state memo-alist) (declare (ignore flg3)) (memo-exit recursivelyp term stack t term3 stack3 (cons-tag-trees ttree1 ttree3) memo-alist))) (t (memo-exit recursivelyp term stack nil term1 stack1 ttree1 memo-alist)))) (defun nth-update-rewriter1 (recursivelyp term stack ens wrld state memo-alist) ; We return (mv flg term' stack' ttree) where ; (1) (EQUAL term/stack term'/stack) ; (2) term' is a quote, if possible ; (3) flg is T iff we applied NTH-UPDATE-NTH or NTH-UPDATE-NTH-ARRAY ; to some part of term', and ; (4) ttree lists all the rules we used. ; Warning: We might change term to a quote by lookup in stack, without ; applying a rule. Hence flg = nil does NOT mean that term = term'. ; Stack is a stack of frames. Each frame is a pair of the form ; (vars . actuals). We determine whether ; (equal (nth i (update-nth j v s)) ; lhs ; (if (equal (nfix i) (nfix j)) v (nth i s))) ; rhs ; applies to term/stack. If so, we return (mv t new-term new-stack) ; such that new-term/new-stack is equal to term/stack. Else, ; we return (mv nil term stack). That is, no matter what, we ; return a term and stack equal to the original one. ; This function also deals with (nth i (update-nth-array ...)) ; in a similar fashion. ; Nomenclature. The patterns shown below indicate the names we will ; use for the various parts of term/stack. We look at term through ; the lens of the stack, without applying the full substitution to it. ; We initially try to see it as ; (NTH i r) ; and then, by dereferencing r, as ; (NTH i (UPDATE-NTH j v s)), ; where each piece is represented by a term and a stack. Thus we ; have ; term/stack = (NTH i/is (UPDATE-NTH j/js v/vs s/ss)) ; Now, if term was bound in stack then term1 is now its ultimate ; binding. Of course, term/stack is equal to term1/stack1. (mv-let (xx1 xx2 xx3 xx4 xx5) (get-memo recursivelyp term stack memo-alist) (cond (xx1 (mv xx2 xx3 xx4 xx5 memo-alist)) (t (mv-let (term1 stack1 ttree1) (deref term stack ens wrld nil) (cond ((variablep term1) ; If term1 is a variable, then stack1 is nil. (mv nil term1 nil ttree1 memo-alist)) ((fquotep term1) (mv nil term1 nil ttree1 memo-alist)) ; We used to have the following code. But it blocked a (at least one) ; proof in the fp books (LOGAND-DEF). So I eliminated it. But it was ; added for a reason. Other changes we're now running seem to have ; rendered that reason no longer relevant... This deleted code does not ; track ttrees, so if it is reinstated the ttree issues will have to be ; thought out. ;;; ((and (nvariablep term) ;;; (flambdap (ffn-symb term)) ;;; (eq (ffn-symb term1) 'IF) ;;; stack1) ;;; ;;; ; If the ORIGINAL term, the one we deref'd above, is a LAMBDA that contains ;;; ; an IF at the top of its body, we lift the IF out of the LAMBDA. ;;; ;;; (mv-let ;;; (aflg a as memo-alist) ;;; (nth-update-rewriter1 recursivelyp? ; When this was done, I didn't ;;; ; have the recursivelyp flag. ;;; (fargn term1 1) stack1 ;;; ens wrld state memo-alist) ;;; (declare (ignore aflg)) ;;; (mv t `(IF ,(lambda-stack as a) ;;; ,(lambda-stack stack1 (fargn term1 2)) ;;; ,(lambda-stack stack1 (fargn term1 3))) ;;; nil ;;; memo-alist))) ((not (eq (ffn-symb term1) 'nth)) ; So term/stack isn't an (nth ...). We can't do anything with it, unless we ; are doing this recursively or else an abbreviation lemma clears the way. (cond ((or (not recursivelyp) (eq (ffn-symb term1) 'hide)) (nth-update-rewriter1-continue recursivelyp term stack nil term1 stack1 ttree1 t ens wrld state memo-alist)) ; In the next two clauses we know that recursivelyp is t. ((eq (ffn-symb term1) 'IF) (mv-let (aflg a as attree memo-alist) (nth-update-rewriter1 t (fargn term1 1) stack1 ens wrld state memo-alist) ; On this IF we use the ``abc'' approach: simplify the test first and ; then the two branches. But on (NTH i (IF a b c)) we use the ``bca'' ; approach of working first on (NTH i b) and (NTH i c) in hopes that ; they come out equal. We tried the bca approach on this IF and tried ; it on one of the standard Rockwell problems and it gave marginally worse ; performance than the abc approach. (cond ((quotep a) (cond ((equal a *nil*) (nth-update-rewriter1-continue recursivelyp term stack t (fargn term1 3) stack1 (cons-tag-trees ttree1 attree) nil ens wrld state memo-alist)) (t (nth-update-rewriter1-continue recursivelyp term stack t (fargn term1 2) stack1 (cons-tag-trees ttree1 attree) nil ens wrld state memo-alist)))) (t (mv-let (bflg b bs bttree memo-alist) (nth-update-rewriter1 t (fargn term1 2) stack1 ens wrld state memo-alist) (mv-let (cflg c cs cttree memo-alist) (nth-update-rewriter1 t (fargn term1 3) stack1 ens wrld state memo-alist) (cond ((eq (equal-derefs b bs c cs) t) (memo-exit t term stack t b bs (cons-tag-trees bttree cttree) memo-alist)) (t (with-reconciliation (a b c) (as bs cs) common-stack ; There are no abbrev rules on IF and a, at least, is not quoted so ; we cannot evaluate the IF below. Thus, we're done. (memo-exit t term stack (or aflg bflg cflg) `(IF ,a ,b ,c) common-stack (cons-tag-trees attree (cons-tag-trees bttree cttree)) memo-alist)))))))))) (t (mv-let (aflg args astacks attree memo-alist) (nth-update-rewriter1-lst t (fargs term1) stack1 ens wrld state memo-alist) (cond (aflg (mv-let (args astack) (recon args astacks) (let ((aterm (cons-term (ffn-symb term1) args))) (nth-update-rewriter1-continue recursivelyp term stack t aterm astack (cons-tag-trees ttree1 attree) t ens wrld state memo-alist)))) (t (nth-update-rewriter1-continue t term stack nil term1 stack1 ttree1 t ens wrld state memo-alist))))))) (t ; We start by rewriting the first argument to the nth expression. ; It is not unheard-of for this argument to be an nth expression ; itself. That is, we might be looking at (nth (nth & &) &). (mv-let (iflg i is ittree memo-alist) (nth-update-rewriter1 t (fargn term1 1) stack1 ens wrld state memo-alist) (mv-let (r rs irttree) ; includes ittree! (deref (fargn term1 2) stack1 ens wrld ittree) (cond ((or (variablep r) (fquotep r)) ; So we are looking at an expression like (NTH i r), where r is a variable ; or a quoted constant. (with-reconciliation (i r) (is rs) common-stack (nth-update-rewriter1-continue recursivelyp term stack iflg `(NTH ,i ,r) common-stack irttree t ens wrld state memo-alist))) ((eq (ffn-symb r) 'IF) ; We are looking at (NTH i (IF a b c)). We turn it into (IF a (NTH i ; b) (NTH i c)), provided at least one of the branches simplifies or ; we can decide the test. The code below may duplicate i! (let* ((a (fargn r 1)) (as (if (quotep a) nil rs)) (b (fargn r 2)) (bs (if (quotep b) nil rs)) (c (fargn r 3)) (cs (if (quotep c) nil rs))) (with-reconciliation (i b) (is bs) inner-bs (mv-let (xflg x xs xttree memo-alist) (nth-update-rewriter1 t `(NTH ,i ,b) inner-bs ens wrld state memo-alist) (with-reconciliation (i c) (is cs) inner-cs (mv-let (yflg y ys yttree memo-alist) (nth-update-rewriter1 t `(NTH ,i ,c) inner-cs ens wrld state memo-alist) (cond ((or xflg yflg) ; Equal-derefs is not perfect; it sometimes says nil when the two ; derefs are recursively equal. We tried the fully recursive, ; complete equal-derefs and found it too slow. We also tried ; reconciling a, x and y and then asking whether x is y, but found ; that slower than this. (cond ((eq (equal-derefs x xs y ys) t) (memo-exit recursivelyp term stack t x xs (cons-tag-trees irttree (cons-tag-trees xttree yttree)) memo-alist)) (t (mv-let (aflg a as attree memo-alist) (nth-update-rewriter1 t a as ens wrld state memo-alist) (declare (ignore aflg)) (cond ((quotep a) (cond ((equal a *nil*) (memo-exit recursivelyp term stack t y ys (cons-tag-trees irttree (cons-tag-trees attree yttree)) memo-alist)) (t (memo-exit recursivelyp term stack t x xs (cons-tag-trees irttree (cons-tag-trees attree xttree)) memo-alist)))) (t (with-reconciliation (a x y) (as xs ys) common-stack ; There are no abbrev rules on IF and a, at least, is not a constant, so ; we can't eval the IF below. We're done. (memo-exit recursivelyp term stack t `(IF ,a ,x ,y) common-stack (cons-tag-trees irttree (cons-tag-trees attree (cons-tag-trees xttree yttree))) memo-alist)))))))) ((null iflg) ; If iflg is off, we haven't changed term/stack at all and avoid the ; reconciliation otherwise necessary. (nth-update-rewriter1-continue recursivelyp term stack nil term stack nil t ens wrld state memo-alist)) (t (with-reconciliation (i r) (is rs) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,r) common-stack irttree t ens wrld state memo-alist)))))))))) ((eq (ffn-symb r) 'UPDATE-NTH) ; We are looking at (NTH i (UPDATE-NTH j v s)). (mv-let (jflg j js jttree memo-alist) (nth-update-rewriter1 t (fargn r 1) rs ens wrld state memo-alist) (declare (ignore jflg)) (let* ((i (if (quotep i) (nfix-quote i) i)) (j (if (quotep j) (nfix-quote j) j)) (irjttree (push-lemma *fake-rune-for-nu-rewriter* (cons-tag-trees irttree jttree))) (i=j (equal-derefs i is j js)) (v (fargn r 2)) (vs (if (quotep v) nil rs)) (s (fargn r 3)) (ss (if (quotep s) nil rs))) (cond ((eq i=j '?) (with-reconciliation (i j) (is js) ps (let ((nfixi (if (quotep i) i `(NFIX ,i))) (nfixj (if (quotep j) j `(NFIX ,j)))) (mv-let (pflg p ps pttree) (apply-abbrevs-to-lambda-stack nil `(EQUAL ,nfixi ,nfixj) ps ens wrld state irjttree) (declare (ignore pflg)) (cond ((quotep p) (cond ((equal p *nil*) (with-reconciliation (i s) (is ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,s) common-stack pttree nil ens wrld state memo-alist))) (t (nth-update-rewriter1-continue recursivelyp term stack t v vs pttree nil ens wrld state memo-alist)))) (t (with-reconciliation (p i v s) (ps is vs ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(IF ,p ,v (NTH ,i ,s)) common-stack pttree nil ens wrld state memo-alist)))))))) ((eq i=j t) (nth-update-rewriter1-continue recursivelyp term stack t v vs irjttree nil ens wrld state memo-alist)) (t (with-reconciliation (i s) (is ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,s) common-stack irjttree nil ens wrld state memo-alist))))))) ((eq (ffn-symb r) 'UPDATE-NTH-ARRAY) ; We are looking at (NTH i (UPDATE-NTH-ARRAY j k v s)). ; The operative rule is ; (equal (NTH i (UPDATE-NTH-ARRAY j k v s)) ; (if (equal (nfix i) (nfix j)) ; (update-nth k v (nth i s)) ; (nth i s)))) (mv-let (jflg j js jttree memo-alist) (nth-update-rewriter1 t (fargn r 1) rs ens wrld state memo-alist) (declare (ignore jflg)) (let* ((i (if (quotep i) (nfix-quote i) i)) (j (if (quotep j) (nfix-quote j) j)) (irjttree (push-lemma *fake-rune-for-nu-rewriter* (cons-tag-trees irttree jttree))) (i=j (equal-derefs i is j js)) (k (fargn r 2)) (ks (if (quotep k) nil rs)) (v (fargn r 3)) (vs (if (quotep v) nil rs)) (s (fargn r 4)) (ss (if (quotep s) nil rs))) (cond ((eq i=j '?) (with-reconciliation (i j) (is js) ps (let ((nfixi (if (quotep i) i `(NFIX ,i))) (nfixj (if (quotep j) j `(NFIX ,j)))) (mv-let (pflg p ps pttree) (apply-abbrevs-to-lambda-stack nil `(EQUAL ,nfixi ,nfixj) ps ens wrld state irjttree) (declare (ignore pflg)) (cond ((quotep p) (cond ((equal p *nil*) (with-reconciliation (i s) (is ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,s) common-stack pttree nil ens wrld state memo-alist))) (t (with-reconciliation (i k v s) (is ks vs ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(UPDATE-NTH ,k ,v (NTH ,i ,s)) common-stack pttree nil ens wrld state memo-alist))))) (t (with-reconciliation (p i k v s) (ps is ks vs ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(IF ,p (UPDATE-NTH ,k ,v (NTH ,i ,s)) (NTH ,i ,s)) common-stack pttree nil ens wrld state memo-alist)))))))) ((eq i=j t) (with-reconciliation (i k v s) (is ks vs ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(UPDATE-NTH ,k ,v (NTH ,i ,s)) common-stack irjttree nil ens wrld state memo-alist))) (t (with-reconciliation (i s) (is ss) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,s) common-stack irjttree nil ens wrld state memo-alist))))))) ((eq (ffn-symb r) 'NTH) ; We are looking at (NTH i (NTH ...)). We first simplify the inner ; NTH. It could simplify to an UPDATE-NTH. So after rewriting it we ; recur. (mv-let (rflg1 r1 r1s rttree memo-alist) (nth-update-rewriter1 t r rs ens wrld state memo-alist) (cond (rflg1 (with-reconciliation (i r1) (is r1s) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,r1) common-stack (cons-tag-trees irttree rttree) nil ens wrld state memo-alist))) (t ; We are looking at (NTH i (NTH ...)) but we couldn't simplify the inner ; NTH. So we behave just as we do in the clause below, when r isn't ; even an NTH. (cond ((null iflg) (nth-update-rewriter1-continue recursivelyp term stack nil term stack nil t ens wrld state memo-alist)) (t (with-reconciliation (i r) (is rs) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,r) common-stack (cons-tag-trees irttree rttree) t ens wrld state memo-alist)))))))) ((and (eq (ffn-symb r) 'CONS) (quotep i) (integerp (cadr i)) (<= 0 (cadr i)) (mv-let (un temp-alist) (simplifiable-mv-nth1 (cadr i) r nil) (declare (ignore temp-alist)) un)) ; We are looking at (NTH 'n (CONS u0 (CONS u1 ... (CONS un ...)...))) ; where r is the cons nest and we know there there are enough ; elements. The code above is a little strange because of its use of ; simplifiable-mv-nth1. That function actually has nothing to do with ; MV-NTH and just asks whether its second arg is a cons-term of ; sufficient depth, as given by its first arg. The last arg is an ; alist for use in chasing variables that occur in the cons-term. We ; supply nil, which means we insist that r be a suitable cons-term at ; the ``top-level'' of its structure. We ignore the returned alist ; because it is nil. (mv-let (un temp-alist) (simplifiable-mv-nth1 (cadr i) r nil) (declare (ignore temp-alist)) (nth-update-rewriter1-continue recursivelyp term stack t un rs irttree t ens wrld state memo-alist))) (t ; We are looking at (NTH i r), where r is a function or lambda ; application but not an IF, UPDATE-NTH or UPDATE-NTH-ARRAY. (cond ((null iflg) (nth-update-rewriter1-continue recursivelyp term stack nil term stack nil t ens wrld state memo-alist)) (t (with-reconciliation (i r) (is rs) common-stack (nth-update-rewriter1-continue recursivelyp term stack t `(NTH ,i ,r) common-stack irttree t ens wrld state memo-alist))))))))))))))) (defun nth-update-rewriter1-lst (recursivelyp args stack ens wrld state memo-alist) (cond ((endp args) (mv nil nil nil nil memo-alist)) (t (mv-let (aflg1 arg1 astack1 attree1 memo-alist) (nth-update-rewriter1 recursivelyp (car args) stack ens wrld state memo-alist) (mv-let (aflg2 args1 astacks1 attree2 memo-alist) (nth-update-rewriter1-lst recursivelyp (cdr args) stack ens wrld state memo-alist) (mv (or (or aflg1 (and (not (quotep (car args))) (quotep arg1))) aflg2) (cons arg1 args1) (cons astack1 astacks1) (cons-tag-trees attree1 attree2) memo-alist)))))) ) (mutual-recursion (defun nth-update-rewriter-targetp (term wrld) ; We determine whether the function NTH is used as a function in term ; or in the body of any non-recursive fn used in term. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (nth-update-rewriter-targetp (lambda-body (ffn-symb term)) wrld) (nth-update-rewriter-target-lstp (fargs term) wrld))) (t (or (getprop (ffn-symb term) 'nth-update-rewriter-targetp nil 'current-acl2-world wrld) (nth-update-rewriter-target-lstp (fargs term) wrld))))) (defun nth-update-rewriter-target-lstp (lst wrld) (declare (xargs :guard (pseudo-term-listp lst))) (if (null lst) nil (or (nth-update-rewriter-targetp (car lst) wrld) (nth-update-rewriter-target-lstp (cdr lst) wrld)))) ) (defun make-stack-from-alist (term alist) ; We wish to make a stack representing alist, so that term/stack is ; term/alist. The stack will consist of a single frame. We used to ; do this with ; (if alist (list (cons (strip-cars alist) (strip-cdrs alist))) nil). ; But that was incorrect. The free variables of term must be among ; the vars bound by the frame. (That is, we must imagine that term is ; the body of a lambda expression whose formals are the vars of the ; frame.) So if term contains a variable not bound in alist then we ; must capture that variable and bind it to itself. (if alist (let* ((vars-of-term (all-vars term)) (formals (strip-cars alist)) (actuals (strip-cdrs alist)) (free (set-difference-eq vars-of-term formals))) (list (cons (append free formals) (append free actuals)))) nil)) (defun nth-update-rewriter (recursivep term alist ens wrld state) ; Term is a function or lambda application. This function may rewrite ; term/alist and returns (mv hitp term' ttree), where either hitp is nil (in ; which case term' is irrelevant) or term' is equal to term/alist and ttree ; justifies the equivalence. ; Note: Consistent with the conventions inside the rewriter, alist may ; not bind every variable of term. That is, we might see the term (fn ; x y) and an alist that binds x to a but does not mention y. ; This function applies two rules, looking for the two targets: ; (nth i (update-nth j v s)) ; and ; (nth i (update-nth-array j k v s)) ; and rewriting in the obvious way. But it looks through IFs and LAMBDAs ; and proceeds in an outside-in manner. ; If recursivep is t, we explore term/alist to the tips. If ; recursivep is nil, we only proceed if term/alist is an NTH ; expression. However, term/alist is an NTH expression either because ; term is an NTH expression or because term is a variable bound to an ; NTH expression in alist. But term is not a variable. (cond ((not (nu-rewriter-mode wrld)) (mv nil nil nil)) ((not (if recursivep (nth-update-rewriter-targetp term wrld) (eq (ffn-symb term) 'NTH))) ; This exit is always sound. The idea of the test above is this: If ; we are to explore the term recursively and it does not contain any ; targets, then we don't bother. Alternatively, if we are not to ; explore it recursively and it is not an NTH expression, we don't ; bother. We look at term rather than term/alist because everything ; in alist has been simplified already, so if we do work it will be ; because of stuff in term. ; Tests have shown that it is worth the cost of looking for targets ; before proceeding. This is because we call this function with ; recursivep = t more often than we might (namely, on every rewrite ; during backchaining and recursive function expansion, and whenever ; the arguments to a non-rec fn application change when rewritten). ; If these calls are changed to recursivep = nil, then things speed up ; by not looking ahead for targets. But suppose that an argument, a, ; has changed from (compute st) to some huge expression, expr, and the ; term being rewritten is (if (test a) (nth 1 a) (nth 1 st)), and ; suppose that we can tell that (nth 1 expr) is (nth 1 a) by ; nth-update reasoning. Then we would win bigtime. Therefore, I ; believe it is important to use recursivep = t agressively when it ; might help. (mv nil nil nil)) (t #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (cw "(nth-update-rewriter:~%~ ~ recp: ~x0~%~ ~ term: ~x1~%~ ~ alist: ~x2~%~ ~ (..." recursivep (untranslate term nil wrld) alist))) #-acl2-loop-only ; Rockwell Addition (clear-nu-memos nil ens wrld) (mv-let (flg term1 stack1 ttree1 memo-alist) ; Rockwell Addition: Non-equivalent read conditionals! Well, they ; may be equivalent if you take the view that TIME's output is to the ; comment window. This could be done in straight ACL2. #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (time (nth-update-rewriter1 recursivep term (make-stack-from-alist term alist) ens wrld state nil))) (t (nth-update-rewriter1 recursivep term (make-stack-from-alist term alist) ens wrld state nil))) #+acl2-loop-only ; Rockwell Addition (nth-update-rewriter1 recursivep term (make-stack-from-alist term alist) ens wrld state nil) (declare (ignore memo-alist)) #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (cw "...)~%") (nu-memo-stats))) (cond ((null flg) #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (cw " output: no change)~%"))) (mv nil nil nil)) (t (let ((ans (lambda-stack stack1 term1))) #-acl2-loop-only ; Rockwell Addition (cond (*nth-update-tracingp* (cw " output: ~x0)~%" (untranslate ans nil wrld)))) (mv t ans ttree1)))))))) ; Here is how we create a lambda application. (defun collect-by-position (sub-domain full-domain full-range) ; Full-domain and full-range are lists of the same length, where ; full-domain is a list of symbols. Collect into a list those members ; of full-range that correspond (positionally) to members of ; full-domain that belong to sub-domain. (if (endp full-domain) nil (if (member-eq (car full-domain) sub-domain) (cons (car full-range) (collect-by-position sub-domain (cdr full-domain) (cdr full-range))) (collect-by-position sub-domain (cdr full-domain) (cdr full-range))))) (defun make-lambda-application (formals body actuals) ; Example: ; (make-lambda-application '(x y z) ; '(foo x z) ; '((x1 a b) (y1 a b) (z1 a b))) ; equals ; ((lambda (x z) (foo x z)) (x1 a b) (z1 a b)) ; ; Note that the irrelevant formal y has been eliminated. (let ((vars (all-vars body))) (cond ((null vars) body) ((equal formals actuals) body) ((set-difference-eq vars formals) (er hard 'make-lambda-application "Unexpected unbound vars ~x0" (set-difference-eq vars formals))) (t ; The slightly tricky thing here is to avoid using all the formals, ; since some might be irrelevant. Note that the call of ; intersection-eq below is necessary rather than just using vars, even ; though it is a no-op when viewed as a set operation (as opposed to a ; list operation), in order to preserve the order of the formals. (fcons-term (make-lambda (intersection-eq formals vars) body) (collect-by-position vars formals actuals)))))) ; The following two functions help us implement lambda-hide commuting, ; e.g., ((LAMBDA (x) (HIDE body)) arg) => (HIDE ((LAMBDA (x) body) arg)). (defun lambda-nest-hidep (term) ; We return t iff term is a lambda nest with a HIDE as the inner-most ; body. E.g., ; (let ((st ...)) ; (let ((st ...)) ; (let ((st ...)) ; (HIDE ...)))) (and (lambda-applicationp term) (let ((body (lambda-body (ffn-symb term)))) (cond ((variablep body) nil) ((fquotep body) nil) ((eq (ffn-symb body) 'hide) t) (t (lambda-nest-hidep body)))))) (defun lambda-nest-unhide (term) ; We remove the HIDE from a lambda-nest-hidep term. (if (lambda-applicationp term) (make-lambda-application (lambda-formals (ffn-symb term)) (lambda-nest-unhide (lambda-body (ffn-symb term))) (fargs term)) (fargn term 1))) (defun search-type-alist+ (term typ type-alist unify-subst ttree wrld) ; Keep this in sync with search-type-alist. One difference between this ; function and search-type-alist is that the present function returns one ; additional argument: the remainder of type-alist to be searched. Another is ; that we assume here that term has at least one variable not bound by ; unify-subst. ; No-change loser except for type-alist. (mv-let (term alt-term) (cond ((or (variablep term) (fquotep term) (not (equivalence-relationp (ffn-symb term) wrld))) (mv term nil)) (t ; we know there are free vars in term (mv term (fcons-term* (ffn-symb term) (fargn term 2) (fargn term 1))))) (search-type-alist-rec term alt-term typ type-alist unify-subst ttree))) (defun oncep (nume-runes match-free rune nume) ; We are given a oncep-override value (e.g., from the :oncep-override value of ; a rewrite constant), nume-runes; a rune, rune and its corresponding nume; and a ; value :once or :all from the match-free field of the rule corresponding to ; that rune. We want to determine whether we should try only one binding when ; relieving a hypothesis in order to relieve subsequent hypotheses, and return ; non-nil in that case, else nil. (if (or (eq nume-runes :clear) (<= (car nume-runes) nume)) (eq match-free :once) (member-equal rune (cdr nume-runes)))) (defabbrev memo-activep (memo) (or (eq memo :start) (consp memo))) (defabbrev activate-memo (memo) (if (eq memo t) :start memo)) (defmacro zero-depthp (depth) ; We use this macro instead of zpf for two reasons. For one, we have not (as ; of this writing) made zpf a macro, and we want efficiency. For another, we ; want to be able to experiment to see what sort of stack depth is used for ; a given event. Use the first body below for that purpose, but use the second ; body for normal operation. #+acl2-rewrite-meter ; for stats on rewriter depth `(prog2$ #+acl2-loop-only ,depth #-acl2-loop-only (setq *rewrite-depth-max* (max ,depth *rewrite-depth-max*)) nil) #-acl2-rewrite-meter ; normal stats (no stats) `(eql (the-fixnum ,depth) 0)) (defmacro rdepth-error (form &optional preprocess-p) (if preprocess-p (let ((ctx ''preprocess)) `(prog2$ (er hard ,ctx "The call depth limit of ~x0 has been exceeded in the ~ ACL2 preprocessor (a sort of rewriter). There is ~ probably a loop caused by some set of enabled simple ~ rules. To see why the limit was exceeded, ~@1retry the ~ proof with :hints~% :do-not '(preprocess)~%and then ~ follow the directions in the resulting error message. ~ See :DOC rewrite-stack-limit." (rewrite-stack-limit wrld) (if (f-get-global 'gstackp state) "" "execute~% :brr t~%and next ")) ,form)) (let ((ctx ''rewrite)) `(prog2$ (er hard ,ctx "The call depth limit of ~x0 has been exceeded in the ACL2 ~ rewriter. To see why the limit was exceeded, ~@1execute ~ the form (cw-gstack) or, for less verbose output, instead ~ try (cw-gstack :frames 30). You will then probably ~ notice a loop caused by some set of enabled rules, some ~ of which you can then disable; see :DOC disable. Also ~ see :DOC rewrite-stack-limit." (rewrite-stack-limit wrld) (if (f-get-global 'gstackp state) "" "first execute~% :brr ~ t~%and then try the proof again, and then ")) ,form)))) (defun bad-synp-hyp-msg1 (hyp bound-vars all-vars-bound-p wrld) ; A hyp is a "good synp hyp" if either it does not mention SYNP as a function ; symbol or else it is a call of SYNP that we know how to handle in our ; processing of rewrite and linear rules. We return nil in this case, or else ; an appropriate message explaining the problem. See bad-synp-hyp-msg. (if (ffnnamep 'synp hyp) (cond ((not (eq (ffn-symb hyp) 'synp)) (mv (cons "a call of syntaxp or bind-free can occur only ~ at the top level of a hypothesis, but in ~x0 it ~ appears elsewhere." (list (cons #\0 (untranslate hyp t wrld)))) bound-vars all-vars-bound-p)) ; Note that we check for the well-formedness of a call to synp in ; translate, so the following bindings should be safe. (t (let* ((term-to-be-evaluated (get-evg (fargn hyp 3) 'bad-synp-hyp-msg1-arg3)) (vars (all-vars term-to-be-evaluated)) (saved-term (get-evg (fargn hyp 2) 'bad-synp-hyp-msg1-arg2)) (vars-to-be-bound (get-evg (fargn hyp 1) 'bad-synp-hyp-msg1-arg1))) (cond ((not (termp term-to-be-evaluated wrld)) (mv (cons "the term to be evaluated by the syntaxp or ~ bind-free hypothesis must be an ACL2 term, but ~ this is not the case in ~x0. The term's internal ~ (translated) form is ~x1." (list (cons #\0 (untranslate hyp nil wrld)) (cons #\1 term-to-be-evaluated))) bound-vars all-vars-bound-p)) ((or (variablep saved-term) (fquotep saved-term) (not (member-eq (ffn-symb saved-term) '(syntaxp bind-free)))) (mv (cons "a synp hyp has been found which does not appear to ~ have come from a syntaxp or bind-free hypothesis: ~ ~x0. This is not, at present, allowed. If we are ~ in error or you believe we have been otherwise too ~ restrictive, please contact the maintainers of ~ ACL2." (list (cons #\0 (untranslate hyp nil wrld)))) bound-vars all-vars-bound-p)) ((and (not (equal vars-to-be-bound nil)) ; not syntaxp (not (equal vars-to-be-bound t)) (or (collect-non-legal-variableps vars-to-be-bound) all-vars-bound-p (intersectp-eq vars-to-be-bound bound-vars))) (mv (cons "the vars to be bound by a bind-free hypothesis ~ must be either t or a list of variables which ~ are not already bound. This is not the case in ~ ~x0. The vars to be bound are ~x1 and the vars ~ already bound are ~x2." (list (cons #\0 (untranslate hyp t wrld)) (cons #\1 vars-to-be-bound) (cons #\2 (if all-vars-bound-p ' bound-vars)))) bound-vars all-vars-bound-p)) ((and (not all-vars-bound-p) (not (subsetp-eq (set-difference-eq vars '(state mfc)) bound-vars))) (mv (cons "any vars, other than ~x2 and ~x3, used in ~ the term to be evaluated by a ~ syntaxp or bind-free hypothesis must already be ~ bound. This does not appear to be the case ~ in ~x0. The vars already bound are ~x1." (list (cons #\0 (untranslate hyp t wrld)) (cons #\1 bound-vars) (cons #\2 'mfc) (cons #\3 'state))) bound-vars all-vars-bound-p)) ((or (member-eq 'state vars) (member-eq 'mfc vars)) (cond ((or (member-eq 'state bound-vars) (member-eq 'mfc bound-vars) all-vars-bound-p) ; The point here is that if state or mfc is already bound, then the user may be ; confused as to whether the present uses are intended to refer to the "real" ; state and mfc or whether they are intended to refer to the variables already ; bound. (mv (cons "we do not allow the use of state or mfc ~ in a syntaxp or bind-free hypothesis ~ in a context where either state or ~ mfc is already bound. This restriction ~ is violated in ~x0. The vars already ~ bound are ~x1." (list (cons #\0 (untranslate hyp nil wrld)) (cons #\1 (if all-vars-bound-p ' bound-vars)))) bound-vars all-vars-bound-p)) ((or (not (eq 'state (car vars))) (member-eq 'state (cdr vars)) (not (eq 'mfc (cadr vars))) (member-eq 'mfc (cddr vars)) (and (not all-vars-bound-p) (not (subsetp-eq (cddr vars) bound-vars)))) (mv (cons "if either state or mfc is a member of the ~ vars of the term to be evaluated, we ~ require that both mfc and state be present ~ and that they be the last two args of the ~ term, in that order. We also require that ~ the remaining vars be already bound. This ~ does not appear to be the case in ~x0. The ~ vars already bound are ~x1." (list (cons #\0 (untranslate hyp nil wrld)) (cons #\1 (if all-vars-bound-p ' bound-vars)))) bound-vars all-vars-bound-p)) (t (mv nil (cond ((eq vars-to-be-bound nil) bound-vars) ((eq vars-to-be-bound t) bound-vars) (t (union-eq vars-to-be-bound bound-vars))) (or all-vars-bound-p (equal vars-to-be-bound t)))))) (t (mv nil (cond ((equal vars-to-be-bound nil) bound-vars) ((equal vars-to-be-bound t) bound-vars) (t (union-eq vars-to-be-bound bound-vars))) (or all-vars-bound-p (equal vars-to-be-bound t)))))))) ; We do not have a synp hyp. (mv nil (union-eq (all-vars hyp) bound-vars) all-vars-bound-p))) (defun bad-synp-hyp-msg (hyps bound-vars all-vars-bound-p wrld) ; We check hyps for any bad synp hyps and return either nil, if there ; were none found, or an error message suitable for use with ~@. This ; message will describe what is wrong with the first (and only) bad ; synp hyp found and will be used in chk-acceptable-rewrite-rule2 ; or chk-acceptable-linear-rule2, or in rewrite-with-lemma. ; Hyps is a list of hypotheses we are to check, bound-vars is an ; accumulator of all the vars known to be bound (initially set to the ; vars in the lhs of the rewrite rule or the trigger term of a linear ; rule), and all-vars-bound-p is a boolean which indicates whether all ; vars are potentially bound (due to the presence of a 't var-list in ; an earlier synp hyp) and is initially nil. ; See bad-synp-hyp-msg1 for the checks we perform. Crudely, we ; check that a synp hyp looks like it came from the expansion of a ; syntaxp or bind-free hyp and that it does not appear to rebind any ; vars that are already bound. (if (null hyps) nil (mv-let (bad-synp-hyp-msg bound-vars all-vars-bound-p) (bad-synp-hyp-msg1 (car hyps) bound-vars all-vars-bound-p wrld) (or bad-synp-hyp-msg (bad-synp-hyp-msg (cdr hyps) bound-vars all-vars-bound-p wrld))))) (defmacro sl-let (vars form &rest rest) ; Keep in sync with sl-let@par. (let ((new-vars (cons 'step-limit vars))) `(mv-let ,new-vars ,form (declare (type (signed-byte 30) step-limit)) ,@rest))) #+acl2-par (defmacro sl-let@par (vars form &rest rest) ; Keep in sync with sl-let. (declare (xargs :guard ; sanity check inherited from mv-let@par (member-eq 'state vars))) (let ((new-vars (cons 'step-limit vars))) `(mv-let@par ,new-vars ,form (declare (type (signed-byte 30) step-limit)) ,@rest))) (defmacro rewrite-entry-extending-failure (unify-subst failure-reason form &rest args) `(mv-let (step-limitxx relieve-hyps-ansxx failure-reason-lstxx unify-substxx ttreexx allpxx rw-cache-alist-newxx) (rewrite-entry ,form ,@args) (mv step-limitxx relieve-hyps-ansxx (and (null relieve-hyps-ansxx) (cons (check-vars-not-free (step-limitxx relieve-hyps-ansxx failure-reason-lstxx unify-substxx ttreexx allpxx rw-cache-alist-newxx) (cons ,unify-subst ,failure-reason)) failure-reason-lstxx)) unify-substxx ttreexx allpxx rw-cache-alist-newxx))) (defun set-difference-assoc-eq (lst alist) (declare (xargs :guard (and (true-listp lst) (alistp alist) (or (symbol-listp lst) (symbol-alistp alist))))) (cond ((endp lst) nil) ((assoc-eq (car lst) alist) (set-difference-assoc-eq (cdr lst) alist)) (t (cons (car lst) (set-difference-assoc-eq (cdr lst) alist))))) (defun extend-unify-subst (alist unify-subst) ; We attempt to keep all terms in quote-normal form, which explains the ; modification of val just below. (append (pairlis$ (strip-cars alist) (sublis-var-lst nil (strip-cdrs alist))) unify-subst)) (defun relieve-hyp-synp (rune hyp0 unify-subst rdepth type-alist wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree bkptr) ; Hyp0 is a call of synp. This special case of relieve-hyp returns some of the ; same values as does relieve-hyp, namely the following ; where wonp is t, nil, or :unify-subst-list: ; (mv wonp failure-reason unify-subst' ttree'') (let* ((synp-fn (car (get-evg (fargn hyp0 2) 'relieve-hyp))) (mfc (if (member-eq 'state (all-vars (get-evg (fargn hyp0 3) 'relieve-hyp))) (make metafunction-context :rdepth rdepth :type-alist type-alist ; The user-supplied term for synp may use the mfc in arbitrary ways, so we ; don't have a clear :obj and we cannot do better than equality for :geneqv. :obj '? :geneqv nil :wrld wrld :fnstack fnstack :ancestors ancestors :backchain-limit backchain-limit :simplify-clause-pot-lst simplify-clause-pot-lst :rcnst rcnst :gstack (if bkptr ; Bkptr is nil when we turn off tracking, e.g. for show-rewrites. (push-gframe 'synp bkptr (if (eq synp-fn 'syntaxp) synp-fn 'bind-free)) gstack) :ttree ttree :unify-subst unify-subst) nil))) (mv-let (erp val latches) (ev-synp (fargn hyp0 3) unify-subst mfc state) (declare (ignore latches)) #-acl2-loop-only (setq *deep-gstack* gstack) (cond ((or erp (null val)) (let ((sym (cond ((null mfc) synp-fn) ((eq synp-fn 'syntaxp) 'syntaxp-extended) ((eq synp-fn 'bind-free) 'bind-free-extended) (t ; impossible? synp-fn)))) (mv nil (list sym erp val) unify-subst ttree))) ((eq synp-fn 'syntaxp) (cond ((eq val t) (mv t nil unify-subst (push-lemma (fn-rune-nume 'synp nil nil wrld) ; It is tempting to record the following: ; (definition-runes ; (all-fnnames (get-evg (fargn hyp0 3) 'relieve-hyp)) ; t wrld)) ; However, some of the functions in question may be :program mode functions, in ; which case they will not have executable-counterpart runes. It is fine not ; to track these, even if they are in logic mode, since these functions ; contribute only heuristically to the proof, not logically; and besides, it ; would be confusing to report runes that are disabled, which they may well be. ttree))) (t (mv (er hard 'relieve-hyp "The evaluation of the SYNTAXP test in :HYP ~x0 of ~ rule ~x1 produced something other than t or nil, ~ ~x2. This was unexpected and is illegal. Please ~ contact the maintainers of ACL2 with a description ~ of the situation that led to this message." (get-evg (fargn hyp0 1) 'relieve-hyp) rune val) nil unify-subst ttree)))) (t (let ((info (bind-free-info val unify-subst (fargn hyp0 1) wrld))) (cond ((eq info nil) (mv t nil (extend-unify-subst val unify-subst) (push-lemma (fn-rune-nume 'synp nil nil wrld) ; see comment above ttree))) ((eq info t) (mv :unify-subst-list nil val ; a list of alists with which to extend unify-subst (push-lemma (fn-rune-nume 'synp nil nil wrld) ; see comment above ttree))) (t (mv (er hard 'relieve-hyp "The evaluation of the BIND-FREE form in ~ hypothesis ~p0 of rule ~x1 produced the result ~ ~x2, which is illegal because ~@3." (untranslate hyp0 t wrld) rune val info) nil unify-subst ttree))))))))) (defun push-lemma? (rune ttree) (if rune (push-lemma rune ttree) ttree)) (defmacro push-lemma+ (rune ttree rcnst ancestors rhs rewritten-rhs) ; Warning: Keep this in sync with push-splitter?; see the comment there for how ; these two macros differ. `(cond ((and (null ,ancestors) (access rewrite-constant ,rcnst :splitter-output) (ffnnamep 'if ,rhs) (ffnnamep 'if ,rewritten-rhs)) (let ((rune ,rune) (ttree ,ttree)) (add-to-tag-tree 'splitter-if-intro rune (push-lemma rune ttree)))) (t (push-lemma ,rune ,ttree)))) (defmacro push-splitter? (rune ttree rcnst ancestors rhs rewritten-rhs) ; Warning: Keep this in sync with push-lemma+, which differs in three ways: ; that macro does not require that rune is bound to a symbol, it does not allow ; the value of rune to be nil, and it also adds a 'lemma tag. ; We could easily remove the guard below, which simply avoids the need to bind ; rune and hence ttree. (declare (xargs :guard (symbolp rune))) `(cond ((and ,rune (null ,ancestors) (access rewrite-constant ,rcnst :splitter-output) (ffnnamep 'if ,rhs) (ffnnamep 'if ,rewritten-rhs)) (add-to-tag-tree 'splitter-if-intro ,rune ,ttree)) (t ,ttree))) (defmacro prepend-step-limit (n form) (let ((vars (if (consp n) n (make-var-lst 'x n)))) `(mv-let ,vars ,form (mv step-limit ,@vars)))) ; We are almost ready to define the rewrite mutual-recursion nest. But first ; we provide support for the rw-cache; see the Essay on Rw-cache. (defrec rw-cache-entry ; This structure is a record of a failed attempt at relieve-hyps. The ; :step-limit is set to the step-limit upon entry to the failed relieve-hyps ; call. ; There are two cases, which we call the "normal-failure" case and the ; "free-failure" case. In the free-failure case, a preceding hypothesis bound ; a free variable without using bind-free or being a binding hypothesis; ; otherwise, we are in the normal-failure case. ; Consider first the normal-failure case. Then the :unify-subst is the ; restriction of a failed attempt to rewrite the nth hypothesis, stored in ; :hyp-info, to true, where the :failure-reason has the form (n . &), and the ; indexing is one-based. ; In the free-failure case, failure-reason is a structure satisfying ; free-failure-p, i.e. of the form (:RW-CACHE-ALIST . alist), where each key ; of alist is a unify-subst and each value is a failure reason (either ; normal-failure or recursively of this form). We sometimes call alist an ; "rw-cache-alist". The :hyp-info field contains the :hyps field of the ; rewrite-rule, and the :step-limit is as above. The following example ; illustrates the form of the :failure-reason. Suppose we have a rewrite rule ; whose left-hand side has variables x1 and x2, such that hypthesis 2 binds ; free variable y and hypothesis 6 binds free variable z. Suppose that when ; binding x1 to a1 and x2 to a2 we find: ; - bind y to b1 ; - obtained failure-reason-1 at hypothesis 4 ; - bind y to b2 ; - bind z to c1 ; - obtained failure-reason-2 at hypothesis 8 ; - bind z to c2 ; - obtained failure-reason-3 at hypothesis 8 ; Then the :unify-subst is ((x1 . a1) (x2 . a2)), and the corresponding ; :failure-reason looks as follows. ; (:RW-CACHE-ALIST ; (((y . b1) (x1 . a1) (x2 . a2)) ; unify-subst ; . failure-reason-1) ; (((y . b2) (x1 . a1) (x2 . a2)) ; unify-subst ; . (:RW-CACHE-ALIST ; (((z . c1) (y . b2) (x1 . a1) (x2 . a2)) ; unify-subst ; . failure-reason-2) ; (((z . c2) (y . b2) (x1 . a1) (x2 . a2)) ; unify-subst ; . failure-reason-3)))) ; Note that if for example we bind y to b3 at hypothesis 2 and fail by finding ; no binding of z at hypothesis 6, then we do not store a failure-reason; and ; this is reasonable, because maybe a later context will find a binding of z. ; Another way to look at this case is to notice that above, we are storing a ; failure reason for each binding of z; so if there are no bindings of z, then ; there is nothing to store! ; We use lexorder a lot, so we put the step-limit field first. ((step-limit . failure-reason) . (unify-subst . hyp-info)) t) (defmacro free-failure-p (r) `(eq (car ,r) :RW-CACHE-ALIST)) (defabbrev combine-free-failure-reasons (r1 r2) ; See the Essay on Rw-cache. ; R1 and r2 are failure reasons satisfying free-failure-p. We return (mv flg ; r), where r is a merge of the given failure reasons and if flg is t, then r ; is equal (in fact eq) to r2. (mv-let (flg alist) (combine-free-failure-alists (cdr r1) (cdr r2)) (cond (flg (mv t r2)) (t (mv nil (cons :RW-CACHE-ALIST alist)))))) (defun combine-free-failure-alists (a1 a2) ; A1 and a2 are rw-cache-alists, as described in (defrec rw-cache-entry ...). (cond ((endp a1) (mv t a2)) (t (let ((pair (assoc-equal (caar a1) a2))) (cond (pair ; then first update a2 with (car a1) (let ((failure-reason-1 (cdar a1)) (failure-reason-2 (cdr pair))) (mv-let (flg a2) (cond ((not (free-failure-p failure-reason-2)) ; keep normal-failure reason (mv t a2)) ((not (free-failure-p failure-reason-1)) (mv nil (put-assoc-equal (caar a1) failure-reason-1 a2))) (t (mv-let (flg2 new-reason) (combine-free-failure-reasons failure-reason-1 failure-reason-2) (cond (flg2 (mv t a2)) (t (mv nil (put-assoc-equal (caar a1) new-reason a2))))))) (cond (flg (combine-free-failure-alists (cdr a1) a2)) (t ; a2 has been updated, so returned flag must be nil (mv-let (flg alist) (combine-free-failure-alists (cdr a1) a2) (declare (ignore flg)) (mv nil alist))))))) (t ; (null pair); in this case, a2 has not yet been updated (mv-let (flg alist) (combine-free-failure-alists (cdr a1) a2) (declare (ignore flg)) (mv nil (cons (car a1) alist))))))))) (defun combine-sorted-rw-cache-lists1 (l1 l2) ; We are given two rw-cache-lists l1 and l2, where each element is an ; rw-cache-entry record (not t) and the lists are sorted by lexorder. We ; return (mv flg lst), where lst is a sorted list that suitably combines l1 and ; l2, and if flg is true then lst is l2. Note that t is not a member of the ; result. (cond ((endp l1) (mv t l2)) ((endp l2) (mv nil l1)) ((and (equal (access rw-cache-entry (car l1) :unify-subst) (access rw-cache-entry (car l2) :unify-subst)) (equal (access rw-cache-entry (car l1) :hyp-info) (access rw-cache-entry (car l2) :hyp-info))) (mv-let (flg lst) (combine-sorted-rw-cache-lists1 (cdr l1) (cdr l2)) (let ((r1 (access rw-cache-entry (car l1) :failure-reason)) (r2 (access rw-cache-entry (car l2) :failure-reason))) (cond ((and (free-failure-p r1) (free-failure-p r2)) (mv-let (flg2 failure-reason) (combine-free-failure-reasons r1 r2) (cond ((and flg flg2) (mv t l2)) (t (mv nil (cons (change rw-cache-entry (car l2) :failure-reason failure-reason) lst)))))) ; Otherwise we prefer r2 to r1, at least if flg is true (so that we return a ; true flg). If r2 is a free-failure-p and r1 is not, then r1 would actually ; be preferable. But we expect that case to be virtually impossible, both ; because the failure that produced r1 would presumably have produced r2 as ; well, and because the :hyp-info field of r1 would be a single hypothesis but ; for r2 it would be a list of hypotheses. (flg (mv flg l2)) (t (mv nil (cons (car l2) lst))))))) ((lexorder (car l1) (car l2)) (mv-let (flg lst) (combine-sorted-rw-cache-lists1 (cdr l1) l2) (declare (ignore flg)) (mv nil (cons (car l1) lst)))) (t (mv-let (flg lst) (combine-sorted-rw-cache-lists1 l1 (cdr l2)) (cond (flg (mv t l2)) (t (mv nil (cons (car l2) lst)))))))) (defun split-psorted-list1 (lst acc) (cond ((endp lst) (mv acc nil)) ((eq (car lst) t) (assert$ (not (member-eq t (cdr lst))) (mv acc (cdr lst)))) (t (split-psorted-list1 (cdr lst) (cons (car lst) acc))))) (defun split-psorted-list (lst) ; Lst is a list with at most one occurrence of t, the idea being that the tail ; after T is sorted. We return the list of elements of lst preceding that ; occurrence of T if any, in any order, together with the list of elements ; after the T (possibly empty, if there is no such T), in their given order. ; We assume that (car lst) is not t. (cond ((member-eq t (cdr lst)) (split-psorted-list1 (cdr lst) (list (car lst)))) (t (mv lst nil)))) (defun merge-lexorder-fast (l1 l2) (declare (xargs :guard (and (true-listp l1) (true-listp l2)) :measure (+ (len l1) (len l2)))) (cond ((endp l1) (mv t l2)) ((endp l2) (mv nil l1)) ((lexorder (car l1) (car l2)) (mv-let (flg x) (merge-lexorder-fast (cdr l1) l2) (declare (ignore flg)) (mv nil (cons (car l1) x)))) (t ; (lexorder (car l2) (car l1)) (mv-let (flg x) (merge-lexorder-fast l1 (cdr l2)) (cond (flg (mv t l2)) (t (mv nil (cons (car l2) x)))))))) (defun merge-sort-lexorder-fast (l) ; We have considered calling merge-lexorder below instead of ; merge-lexorder-fast. However, the realtime of a one-processor regression ; increased by nearly 1% when we tried that -- not a lot, but enough to keep ; using merge-lexorder-fast, especially since it might generate less garbage ; (which could be useful for ACL2(p)). Note: The above experiment took place ; before adding the cddr case, and before removing the equal case from ; merge-lexorder-fast, which should be an impossible case for our application ; of sorting the "front" (unsorted) part of a psorted list. But we did a ; second experiment with a later version, on an "insert-proof" example from ; Dave Greve. ; Using merge-lexorder-fast: ; ; 387.18 seconds realtime, 297.43 seconds runtime ; ; (19,564,695,712 bytes allocated). ; Total GC time: 44573873 T ; Using merge-lexorder: ; ; 388.84 seconds realtime, 298.74 seconds runtime ; ; (19,739,620,816 bytes allocated). ; Total GC time: 44831695 T ; So, we'll use merge-lexorder-fast. (declare (xargs :guard (true-listp l) :measure (len l))) (cond ((endp (cdr l)) l) ((endp (cddr l)) ; avoid the cons built by calling take below (cond ((lexorder (car l) (cadr l)) l) (t (list (cadr l) (car l))))) (t (let* ((n (length l)) (a (ash n -1))) (mv-let (flg x) (merge-lexorder-fast (merge-sort-lexorder-fast (take a l)) (merge-sort-lexorder-fast (nthcdr a l))) (declare (ignore flg)) x))))) (defun sort-rw-cache-list (lst) ; See the Essay on Rw-cache. ; Lst is an rw-cache-list. We return a corresponding sorted list of ; rw-cache-entry records, without t as a member. (cond ((eq (car lst) t) (cdr lst)) ((null (cdr lst)) lst) (t (mv-let (front back) (split-psorted-list lst) (mv-let (flg ans) (combine-sorted-rw-cache-lists1 (merge-sort-lexorder-fast front) back) (declare (ignore flg)) ans))))) (defun combine-rw-cache-lists (lst1 lst2) ; See the Essay on Rw-cache. ; Lst1 and lst2 are rw-cache-lists. We return a suitable combination of the ; two, together with a flag which, when true, implies that the result is equal ; (in fact, eq) to lst2. (cond ((null lst1) (mv t lst2)) ((null lst2) (mv nil lst1)) ((eq (car lst2) t) (mv-let (flg ans) (combine-sorted-rw-cache-lists1 (sort-rw-cache-list lst1) (cdr lst2)) (cond (flg (mv t lst2)) (t (mv nil (cons t ans)))))) (t (mv nil (cons t (mv-let (flg ans) (combine-sorted-rw-cache-lists1 (sort-rw-cache-list lst1) (sort-rw-cache-list lst2)) (declare (ignore flg)) ans)))))) (defun merge-rw-caches (alist1 alist2) ; Each of alist1 and alist2 is a symbol-alist sorted by car according to ; symbol-<. The value of each key is a sorted-rw-cache-list. We return a ; symbol-alist, sorted that same way, such that each key's value is the ; suitable combination of its values in the two alists. We avoid some consing ; by returning an additional value: a flag which, if true, implies that the ; result is equal (in fact, eq) to alist2. (cond ((endp alist1) (mv t alist2)) ((endp alist2) (mv nil alist1)) ((eq (caar alist1) (caar alist2)) (mv-let (flg rest) (merge-rw-caches (cdr alist1) (cdr alist2)) (mv-let (flg2 objs) (combine-rw-cache-lists (cdar alist1) (cdar alist2)) (cond ((and flg flg2) (mv t alist2)) (flg2 (mv nil (cons (car alist2) rest))) (t (mv nil (acons (caar alist2) objs rest))))))) ((symbol-< (caar alist1) (caar alist2)) (mv-let (flg rest) (merge-rw-caches (cdr alist1) alist2) (declare (ignore flg)) (mv nil (cons (car alist1) rest)))) (t ; (symbol-< (caar alist2) (caar alist1)) (mv-let (flg rest) (merge-rw-caches alist1 (cdr alist2)) (cond (flg (mv t alist2)) (t (mv nil (cons (car alist2) rest)))))))) (defmacro sorted-rw-cache-p (cache) ; WARNING: This macro assumes that the given rw-cache is non-empty. `(eq (car ,cache) t)) (defun merge-symbol-alistp (a1 a2) (cond ((endp a1) a2) ((endp a2) a1) ((symbol-< (caar a1) (caar a2)) (cons (car a1) (merge-symbol-alistp (cdr a1) a2))) (t (cons (car a2) (merge-symbol-alistp a1 (cdr a2)))))) (defun merge-sort-symbol-alistp (alist) (cond ((endp (cdr alist)) alist) ((endp (cddr alist)) (cond ((symbol-< (car (car alist)) (car (cadr alist))) alist) (t (list (cadr alist) (car alist))))) (t (let* ((n (length alist)) (a (ash n -1))) (merge-symbol-alistp (merge-sort-symbol-alistp (take a alist)) (merge-sort-symbol-alistp (nthcdr a alist))))))) (defun cdr-sort-rw-cache (cache) ; We sort the given rw-cache. (assert$ cache (cond ((sorted-rw-cache-p cache) (cdr cache)) (t (mv-let (front back) (split-psorted-list cache) (mv-let (flg ans) (merge-rw-caches (merge-sort-symbol-alistp front) back) (declare (ignore flg)) ans)))))) (defun combine-rw-caches (c1 c2) ; See the Essay on Rw-cache. ; C1 and c2 are rw-caches, typically the respective values in two caches of ; either 'rw-cache-any-tag or 'rw-cache-nil-tag. Thus, they are psorted ; symbol-alists. We return a suitable combination of c1 and c2, together with ; a flag implying that the result is equal (in fact eq) to c2. (cond ((null c1) (mv t c2)) ((null c2) (mv nil c1)) (t (mv-let (flg x) (merge-rw-caches (cdr-sort-rw-cache c1) (cdr-sort-rw-cache c2)) (cond ((and flg (sorted-rw-cache-p c2)) (mv t c2)) (t (mv nil (cons t x)))))))) (defun unify-subst-subsetp (a1 a2) ; Both a1 and a2 satisfy symbol-alistp. We assume that if a1 is a subset of ; a2, then their keys occur in the same order. (cond ((endp a1) t) ((endp a2) nil) ((eq (caar a1) (caar a2)) (and (equal (cdar a1) (cdar a2)) (unify-subst-subsetp (cdr a1) (cdr a2)))) (t (unify-subst-subsetp a1 (cdr a2))))) (defun rw-cache-list-lookup (unify-subst hyps recs) (cond ((endp recs) nil) ((eq (car recs) t) (rw-cache-list-lookup unify-subst hyps (cdr recs))) ((let* ((rec (car recs)) (failure-reason (access rw-cache-entry rec :failure-reason)) (hyp-info (access rw-cache-entry rec :hyp-info))) (and (cond ((free-failure-p failure-reason) (and (equal hyps hyp-info) (equal (access rw-cache-entry rec :unify-subst) unify-subst))) (t (and (equal hyp-info ; We test the stored hypothesis against the corresponding current hypothesis ; because the same rune can correspond to several different rules. Theorem ; mod-completion in community book arithmetic-2/floor-mod/floor-mod.lisp ; fails if we cache a failure for one rule stored under (:rewrite ; mod-completionxxx) and then decide not to fire the other rule because we come ; across the same unify-subst. (nth (1- (car failure-reason)) hyps)) (unify-subst-subsetp (access rw-cache-entry rec :unify-subst) unify-subst)))) rec))) (t (rw-cache-list-lookup unify-subst hyps (cdr recs))))) (defstub relieve-hyp-failure-entry-skip-p (rune unify-subst hyps ttree step-limit) t) (defun relieve-hyp-failure-entry-skip-p-builtin (rune unify-subst hyps ttree step-limit) (declare (ignore rune unify-subst hyps ttree step-limit) (xargs :mode :logic :guard t)) nil) (defattach (relieve-hyp-failure-entry-skip-p relieve-hyp-failure-entry-skip-p-builtin)) (defmacro rw-cache-active-p (rcnst) `(member-eq (access rewrite-constant ,rcnst :rw-cache-state) '(t :atom))) (defun assoc-rw-cache (key alist) (cond ((endp alist) nil) ((eq (car alist) t) (assoc-eq key (cdr alist))) ((eql key (caar alist)) (car alist)) (t (assoc-rw-cache key (cdr alist))))) (defun put-assoc-rw-cache1 (key val alist) ; Alist is a psorted-alist (see the Essay on Rw-cache) and key is a key of ; alist. We return the result of replacing the value of key with val in alist. (cond ((atom alist) (list (cons key val))) ((eq (car alist) t) (cons (car alist) (put-assoc-eq key val (cdr alist)))) ((eq key (caar alist)) (cons (cons key val) (cdr alist))) (t (cons (car alist) (put-assoc-rw-cache1 key val (cdr alist)))))) (defun put-assoc-rw-cache (key val alist) ; Alist is a psorted-alist (see the Essay on Rw-cache). We return a ; psorted-alist that associates key with val. (cond ((assoc-rw-cache key alist) (put-assoc-rw-cache1 key val alist)) (t (acons key val alist)))) (defun relieve-hyp-failure-entry (rune unify-subst hyps ttree step-limit) ; We return either nil or else an rw-cache-entry from the rw-cache of the ; ttree. (let* ((cache (tagged-objects 'rw-cache-any-tag ttree)) (entry (and cache ; optimization (rw-cache-list-lookup unify-subst hyps (cdr (assoc-rw-cache (base-symbol rune) cache)))))) ; We could do our check with relieve-hyp-failure-entry-skip-p before even ; looking up the entry, above. Instead, we optimize for the common case that ; relieve-hyp-failure-entry-skip-p returns nil, hence only calling it when ; necessary. This way, the user's attachment to ; relieve-hyp-failure-entry-skip-p could print (with cw or observation-cw, say) ; when an entry is found but skipped. (cond ((null entry) nil) ((relieve-hyp-failure-entry-skip-p rune unify-subst hyps ttree step-limit) nil) (t entry)))) (defun maybe-extend-tag-tree (tag vals ttree) ; Warning: We assume that tag is not a key of ttree. (cond ((null vals) ttree) (t (extend-tag-tree tag vals ttree)))) (defun accumulate-rw-cache1 (replace-p tag new-ttree old-ttree) ; This function is intended to return an extension of the rw-cache of old-ttree ; according to new-ttree, or else nil if the "extension" would not actually ; change old-ttree. Below we describe more precisely what we mean by ; "extension", hence specifying the tag-tree returned in the non-nil case. ; If replace-p is true, then replace the caches tagged by the rw-cache tag in ; old-ttree with those tagged by tag in new-ttree, the expectation being that ; the value of tag in new-ttree extends its value in old-ttree. If replace-p ; is false, then instead of replacing, combine the two caches. In the case ; that replace-p is nil, performance may be best if the value of tag in ; new-ttree is more likely to be contained in its value in old-ttree, than the ; other way around (given our use below of combine-rw-caches). (let ((new-vals (tagged-objects tag new-ttree)) (old-vals (tagged-objects tag old-ttree))) (cond ((and replace-p ; restrict optimization (else equality is unlikely) (equal new-vals old-vals)) ; It's not clear to us whether this COND branch is helpful or harmful. It can ; avoid modifying the tag-tree, but only to save at most a few conses, and at ; the cost of the above equality check. nil) (old-vals (cond (replace-p (assert$ new-vals ; extends non-nil old-vals (extend-tag-tree tag new-vals (remove-tag-from-tag-tree! tag old-ttree)))) (t (mv-let (flg objs) (combine-rw-caches new-vals old-vals) (assert$ objs (cond (flg old-ttree) (t (extend-tag-tree tag objs (remove-tag-from-tag-tree! tag old-ttree))))))))) (new-vals (extend-tag-tree tag new-vals old-ttree)) (t nil)))) (defun accumulate-rw-cache (replace-p new-ttree old-ttree) ; Keep this in sync with accumulate-rw-cache?, which is similar but may (and ; usually will) return nil if old-ttree is unchanged. ; New-ttree is an extension of old-ttree. We incorporate the rw-cache from ; new-ttree into old-ttree, generally because new-ttree is to be discarded ; after a failure but we want to save its cached failures to relieve ; hypotheses. If replace-p is true then we actually ignore the list of values ; of the relevant tags in old-ttree, assuming (and perhaps checking with an ; assert$) that this list forms a tail of the corresponding list of values in ; new-ttree. (let ((ttree1 (or (accumulate-rw-cache1 replace-p 'rw-cache-nil-tag new-ttree old-ttree) old-ttree))) (or (accumulate-rw-cache1 replace-p 'rw-cache-any-tag new-ttree ttree1) ttree1))) (defun accumulate-rw-cache? (replace-p new-ttree old-ttree) ; Keep this in sync with accumulate-rw-cache, which is similar; see comments ; there. However, that function always returns a tag-tree, while the present ; function may (and usually will) return nil if old-ttree is unchanged. (let* ((ttree1-or-nil (accumulate-rw-cache1 replace-p 'rw-cache-nil-tag new-ttree old-ttree)) (ttree1 (or ttree1-or-nil old-ttree)) (ttree2-or-nil (accumulate-rw-cache1 replace-p 'rw-cache-any-tag new-ttree ttree1))) (or ttree2-or-nil ttree1-or-nil))) (mutual-recursion (defun dumb-occur-var (var term) ; This function determines if variable var occurs in the given term. This is ; the same as dumb-occur, but optimized for the case that var is a variable. (cond ((eq var term) t) ((variablep term) nil) ((fquotep term) nil) (t (dumb-occur-var-lst var (fargs term))))) (defun dumb-occur-var-lst (var lst) (cond ((null lst) nil) (t (or (dumb-occur-var var (car lst)) (dumb-occur-var-lst var (cdr lst)))))) ) (defun restrict-alist-to-all-vars1 (alist term) ; Return the result of restricting alist to those pairs whose key is a variable ; occurring free in term, together with a flag that, if nil, implies that the ; result is equal (in fact eq) to alist. (declare (xargs :guard (and (symbol-alistp alist) (pseudo-termp term)))) (cond ((endp alist) (mv nil nil)) (t (mv-let (changedp rest) (restrict-alist-to-all-vars1 (cdr alist) term) (cond ((dumb-occur-var (caar alist) term) (cond (changedp (mv t (cons (car alist) rest))) (t (mv nil alist)))) (t (mv t rest))))))) (mutual-recursion (defun all-vars-boundp (term alist) (declare (xargs :guard (and (pseudo-termp term) (symbol-alistp alist)))) (cond ((variablep term) (assoc-eq term alist)) ((fquotep term) t) (t (all-vars-lst-boundp (fargs term) alist)))) (defun all-vars-lst-boundp (lst alist) (declare (xargs :guard (and (pseudo-term-listp lst) (symbol-alistp alist)))) (cond ((endp lst) t) (t (and (all-vars-boundp (car lst) alist) (all-vars-lst-boundp (cdr lst) alist))))) ) (defun restrict-alist-to-all-vars (alist term) ; We return a subset of alist, with the order of elements unchanged. In our ; intended application of this function, alist is a unify-subst obtained by ; matching the lhs of a rewrite-rule, and term is a hypothesis of that rule ; that has generated a failure reason other than a free-failure. The return ; value is then intended to capture enough of the unify-subst such that for any ; extension of it encountered subsequently, we can reasonably expect the same ; hypothesis to fail again. (cond ((all-vars-boundp term alist) (mv-let (changedp result) (restrict-alist-to-all-vars1 alist term) (declare (ignore changedp)) result)) (t ; This case can happen when we have a binding hypothesis. If we pass in the ; list of all hypotheses in our intended application (see above), we could ; compute which variables bound by alist are really relevant to term. alist))) (defun push-rw-cache-entry (entry tag rune ttree) ; Add entry, an rw-cache-entry record that corresponds to rune, to the records ; associated with tag (which is 'rw-cache-any-tag or 'rw-cache-nil-tag) in ; ttree. (let* ((cache (tagged-objects tag ttree)) (base (base-symbol rune)) (recs (and cache ; optimization (cdr (assoc-rw-cache base cache))))) (cond ((null cache) (extend-tag-tree tag (list (cons base (list entry))) ttree)) (t (extend-tag-tree tag (put-assoc-rw-cache base (cons entry recs) cache) (remove-tag-from-tag-tree tag ttree)))))) (defstub rw-cache-debug (rune target unify-subst relieve-hyp-failure-reason step-limit) t) (defstub rw-cache-debug-action (rune target unify-subst relieve-hyp-failure-reason step-limit) t) (defun rw-cache-debug-builtin (rune target unify-subst failure-reason step-limit) (declare (ignore rune target unify-subst failure-reason step-limit) (xargs :guard t)) nil) (defun rw-cache-debug-action-builtin (rune target unify-subst failure-reason step-limit) (declare (xargs :guard t)) (cw "@@ rw-cache-debug:~|~x0~|" (list :step-limit step-limit :rune rune :target target :unify-subst unify-subst :relieve-hyp-failure-reason failure-reason))) (encapsulate (((rw-cacheable-failure-reason *) => * :formals (failure-reason) :guard (and (consp failure-reason) (posp (car failure-reason))))) (local (defun rw-cacheable-failure-reason (failure-reason) failure-reason))) (defun rw-cacheable-failure-reason-builtin (failure-reason) ; This function recognizes non-free-failure reasons. The guard is important ; for note-relieve-hyp-failure, as noted in a comment in its definition. (declare (xargs :guard (and (consp failure-reason) (posp (car failure-reason))))) (and (consp (cdr failure-reason)) (member-eq (cadr failure-reason) '(rewrote-to syntaxp bind-free)))) (defattach (rw-cacheable-failure-reason rw-cacheable-failure-reason-builtin) :skip-checks t) (defun rw-cacheable-nil-tag (failure-reason) ; Failure-reason is assumed to satisfy rw-cacheable-failure-reason. We return ; true if it is a reason we want to put into the "nil" cache, i.e., one that we ; generally expect to remain suitable when we strengthen the original context ; of the failure. (and (consp (cdr failure-reason)) (cond ((eq (cadr failure-reason) 'rewrote-to) (equal (cddr failure-reason) *nil*)) (t (assert$ (member-eq (cadr failure-reason) '(syntaxp bind-free)) ; Quoting :doc bind-free (and similarly for syntaxp): "every variable occuring ; freely in term occurs freely in lhs or in some hypi, i c." The args are tagged with h and c according to how ; they are involved in this spec. ; The Rewrite Assumption: the conjunction of (a) the assumptions in type-alist, ; (b) the assumptions in ancestors, (c) the assumption of every "active" poly ; in simplify-clause-pot-lst (where a poly is inactive iff its tag-tree ; contains a 'pt containing some literal number that occurs in the :pt field of ; rcnst), and (d) the 'assumptions in the final tag-tree ttree'. ; Observe that if there are 'assumptions in the incoming ttree they are unioned ; into those made by this rewrite. Thus, unless you want the assumptions to ; accumulate across many rewrites, you must use the empty initial tag-tree. It ; would be incorrect to attempt to split on the "new" assumptions in the new ; tag-tree because of the unioning. ":Doc-Section Rule-Classes make some ~c[:rewrite] rules (possibly conditional ones)~/ ~l[rule-classes] for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description. This doc topic discusses the rule-class ~c[:rewrite]. If you want a general discussion of how rewriting works in ACL2 and some guidance on how to construct effective rewrite rules, ~pl[introduction-to-rewrite-rules-part-1] and then ~pl[introduction-to-rewrite-rules-part-2]. ~bv[] Examples: (defthm plus-commutes ; Replace (+ a b) by (+ b a) provided (equal (+ x y) (+ y x))) ; certain heuristics approve the ; permutation. (defthm plus-commutes ; equivalent to the above (equal (+ x y) (+ y x)) :rule-classes ((:rewrite :corollary (equal (+ x y) (+ y x)) :loop-stopper ((x y binary-+)) :match-free :all))) (defthm append-nil ; Replace (append a nil) by a, if (implies (true-listp x) ; (true-listp a) rewrites to t. (equal (append x nil) x))) (defthm append-nil ; as above, but with defaults and (implies (true-listp x) ; a backchain limit (equal (append x nil) x)) :rule-classes ((:rewrite :corollary (implies (true-listp x) (equal (append x nil) x)) :backchain-limit-lst (3) ; or equivalently, 3 :match-free :all))) (defthm member-append ; Replace (member e (append b c)) by (implies ; (or (member e b) (member e c) in (and ; contexts in which propositional (true-listp x) ; equivalence is sufficient, provided (true-listp y)) ; b and c are true-lists. (iff (member e (append x y)) (or (member e x) (member e y)))))~/ General Form: (and ... (implies (and ...hi...) (implies (and ...hk...) (and ... (equiv lhs rhs) ...))) ...) ~ev[] Note: One ~c[:rewrite] rule class object might create many rewrite rules from the ~c[:]~ilc[corollary] formula. To create the rules, we first translate the formula, expanding all macros (~pl[trans]) and also expanding away calls of all so-called ``guard holders,'' ~ilc[mv-list] and ~ilc[return-last] (the latter resulting for example from calls of ~ilc[prog2$], ~ilc[mbe], or ~ilc[ec-call]), as well as expansions of the macro `~ilc[the]'. Next, we eliminate all ~c[lambda]s; one may think of this step as simply substituting away every ~ilc[let], ~ilc[let*], and ~ilc[mv-let] in the formula. We then flatten the ~ilc[AND] and ~ilc[IMPLIES] structure of the formula; for example, if the hypothesis or conclusion is of the form ~c[(and (and term1 term2) term3)], then we replace that by the ``flat'' term ~c[(and term1 term2 term3)]. (The latter is actually an abbreviation for the right-associated term ~c[(and term1 (and term2 term3))].) The result is a conjunction of formulas, each of the form ~bv[] (implies (and h1 ... hn) concl) ~ev[] where no hypothesis is a conjunction and ~c[concl] is neither a conjunction nor an implication. If necessary, the hypothesis of such a conjunct may be vacuous. We then further coerce each ~c[concl] into the form ~c[(equiv lhs rhs)], where ~c[equiv] is a known ~il[equivalence] relation, by replacing any ~c[concl] not of that form by ~c[(iff concl t)]. A ~c[concl] of the form ~c[(not term)] is considered to be of the form ~c[(iff term nil)]. By these steps we reduce the given ~c[:]~ilc[corollary] to a sequence of conjuncts, each of which is of the form ~bv[] (implies (and h1 ... hn) (equiv lhs rhs)) ~ev[] where ~c[equiv] is a known ~il[equivalence] relation. ~l[equivalence] for a general discussion of the introduction of new ~il[equivalence] relations. At this point, we check whether ~c[lhs] and ~c[rhs] are the same term; if so, we cause an error, since this rule will loop. (But this is just a basic check; the rule could loop in other cases, for example if ~c[rhs] is an instance of ~c[lhs]; ~pl[loop-stopper].) We create a ~c[:rewrite] rule for each such conjunct, if possible, and otherwise cause an error. It is possible to create a rewrite rule from such a conjunct provided ~c[lhs] is not a variable, a quoted constant, a ~ilc[let]-expression, a ~c[lambda] application, or an ~ilc[if]-expression. A ~c[:rewrite] rule is used when any instance of the ~c[lhs] occurs in a context in which the ~il[equivalence] relation is an admissible ~il[congruence] relation. First, we find a substitution that makes ~c[lhs] equal to the target term. Then we attempt to relieve the instantiated hypotheses of the rule. Hypotheses that are fully instantiated are relieved by recursive rewriting. Hypotheses that contain ``free variables'' (variables not assigned by the unifying substitution) are relieved by attempting to guess a suitable instance so as to make the hypothesis equal to some known assumption in the context of the target. If the hypotheses are relieved, and certain restrictions that prevent some forms of infinite regress are met (~pl[loop-stopper]), the target is replaced by the instantiated ~c[rhs], which is then recursively rewritten. ACL2's rewriting process has undergone some optimization. In particular, when a term ~c[t1] is rewritten to a new term ~c[t2], the rewriter is then immediately applied to ~c[t2]. On rare occasions you may find that you do not want this behavior, in which case you may wish to use a trick involving ~ilc[hide]; ~pl[meta], near the end of that documentation. In another optimization, when the hypotheses and right-hand side are rewritten, ACL2 does not really first apply the substitution and then rewrite; instead, it as it rewrites those terms it looks up the ~i[already rewritten] values of the bound variables. Sometimes you may want those bindings rewritten again, e.g., because the variables occur in slots that admit additional equivalence relations. See ~i[double-rewrite]. ~l[introduction-to-rewrite-rules-part-1] and ~pl[introduction-to-rewrite-rules-part-2] for an extended discussion of how to create effective rewrite rules.~/ :cite free-variables :cite force :cite case-split :cite double-rewrite :cite syntaxp :cite bind-free" ; The first value is the rewritten term. The second is the final ; value of ttree. (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (let ((gstack (push-gframe 'rewrite bkptr term alist obj)) (rdepth (adjust-rdepth rdepth))) (declare (type (unsigned-byte 29) rdepth)) (cond ((zero-depthp rdepth) (rdepth-error (mv step-limit (sublis-var alist term) ttree))) ((time-limit5-reached-p "Out of time in the rewriter (rewrite).") ; nil, or throws (mv step-limit nil nil)) ((variablep term) (rewrite-entry (rewrite-solidify-plus (let ((temp (assoc-eq term alist))) (cond (temp (cdr temp)) (t term)))))) ((fquotep term) (mv step-limit term ttree)) ((eq (ffn-symb term) 'if) ; Normally we rewrite (IF a b c) by rewriting a and then one or both ; of b and c, depending on the rewritten a. But in the special case ; (IF a b b) we just rewrite and return b. We have seen examples ; where this comes up, e.g., nth-update-rewriter can produce such IFs. (cond ((equal (fargn term 2) (fargn term 3)) (rewrite-entry (rewrite (fargn term 2) alist 2))) (t (sl-let (rewritten-test ttree) (rewrite-entry (rewrite (fargn term 1) alist 1) ; When we rewrite the test of the if we use geneqv iff. What about ; obj. Mostly we'll use '?. But there are a few special cases. ; Suppose you are rewriting (if t1 'nil 't) with the objective t. ; Then you should rewrite t1 with the objective nil. This actually ; comes up in the handling of (<= x y). That term opens to (if (< y ; x) 'nil 't). If we had an obj of t initially, and we don't look ; into the if to see which way the branches go, then we rewrite the (< ; y x) with obj '? and miss an opportunity to use linear arithmetic. ; After Version_3.2.1 we added some more special cases. Consider the ; following example supplied by Robert Krug. ; (defstub quux (x) t) ; ; (defaxiom quux-thm-1 ; (<= x (quux x)) ; :rule-classes :linear) ; ; (defaxiom quux-thm-2 ; (integerp (quux x))) ; ; ; Good ; ; (defstub foo-1 (x) t) ; ; (defun bar-1 (x) ; (or (not (integerp x)) ; (< 4 x))) ; ; (defaxiom foo-1-thm ; (implies (bar-1 (quux x)) ; (foo-1 x))) ; ; (thm ; good ; (implies (and (integerp x) ; (integerp y) ; (< 2 x) ; (< 2 y)) ; (foo-1 (+ x y)))) ; Robert pointed out that if instead we switched the order of ; disjuncts in bar-1, the thm fails: (< 4 x) has moved to a test ; position and we had only passed a t or nil :obj down to the true and ; false branches. ; (defstub foo-2 (x) t) ; ; (defun bar-2 (x) ; (or (< 4 x) ; (not (integerp x)))) ; ; (defaxiom foo-2-thm ; (implies (bar-2 (quux x)) ; (foo-2 x))) ; ; (thm ; bad ; (implies (and (integerp x) ; (integerp y) ; (< 2 x) ; (< 2 y)) ; (foo-2 (+ x y)))) ; Our goal, then, is to recognize the symmetry of OR, AND, and the ; like. But if we do that naively then we miss the proof of the thm ; in the following case, because (or u v) expands to (if u u v) rather than to ; (if u t v). ; (defstub foo-3 (x) t) ; ; (defstub bar-3 (x) t) ; ; (defaxiom bar-3-open ; (equal (bar-3 x) ; (or (< 4 x) ; (foo-3 (append x x)) ; optional extra challenge, since this ; ; doesn't rewrite to a consant ; (not (integerp x))))) ; ; (defaxiom foo-3-thm ; (implies (bar-3 (quux x)) ; (foo-3 x))) ; ; (thm ; bad ; (implies (and (integerp x) ; (integerp y) ; (< 2 x) ; (< 2 y)) ; (foo-3 (+ x y)))) ; Therefore, we treat (if u u v) the same as (if u t v) for purposes ; of establishing the :obj. :obj (cond ((eq obj '?) '?) (t (let ((arg2 (if (equal (fargn term 1) (fargn term 2)) *t* (fargn term 2)))) (cond ((quotep arg2) ; Since (if u t v) is essentially (or u v), :obj is same for u and v ; Since (if u nil v) is essentially (and (not u) v), :obj flips for u and v (if (unquote arg2) obj (not obj))) (t (let ((arg3 (fargn term 3))) (cond ((quotep arg3) ; Since (if u v t ) is essentially (or (not u) v), :obj flips for u and v ; Since (if u v nil) is essentially (and u v), :obj is same for u and v (if (unquote arg3) (not obj) obj)) (t '?)))))))) :geneqv *geneqv-iff*) (rewrite-entry (rewrite-if rewritten-test (fargn term 1) (fargn term 2) (fargn term 3) alist)))))) ((and (eq (ffn-symb term) 'return-last) ; We avoid special treatment for a return-last term when the first argument is ; 'progn, since the user may have intended the first argument to be rewritten ; in that case; consider for example (prog2$ (cw ...) ...). But it is useful ; in the other cases, in particular for calls of return-last generated by calls ; of mbe, to avoid spending time rewriting the next-to-last argument. (not (equal (fargn term 1) ''progn))) (rewrite-entry (rewrite (fargn term 3) alist 2) :ttree (push-lemma (fn-rune-nume 'return-last nil nil wrld) ttree))) ((eq (ffn-symb term) 'hide) ; We are rewriting (HIDE x). Recall the substitution alist. We must ; stuff it into x. That is, if the term is (HIDE (fn u v)) and alist ; is ((u . a) (v . b)), then we must return something equal to (HIDE ; (fn a b)). We used to sublis-var the alist into the term. But that ; may duplicate large terms. So as of Version 2.6 we actually create ; (HIDE ((lambda (u v) x) a b)) or, equivalently, (HIDE (LET ((u a) (v ; b)) x)). ; Care must be taken to ensure that there are no free vars in the ; lambda. We therefore use make-stack-from-alist to create a stack. ; This stack contains (at most) a single frame consisting of the ; appropriate formals and actuals. ; Also recall :EXPAND hints. We must check whether we have been told ; to expand this guy. But which guy? (HIDE (fn a b)) or (HIDE (LET ; ((u a) (v b)) x))? We actually ask about the latter because the ; former may be prohibitive to compute. The fact that HIDEs are ; changed a little may make it awkward for the user to formulate ; :EXPAND or HIDE-rewrite hints without waiting to see what comes out. (let* ((stack (make-stack-from-alist (fargn term 1) alist)) (inst-term (if alist (fcons-term* 'hide (make-lambda-application (caar stack) (fargn term 1) (cdar stack))) term)) (new-rcnst (expand-permission-p inst-term rcnst geneqv wrld))) (cond (new-rcnst ; We abandon inst-term and rewrite the hidden part under the alist. (rewrite-entry (rewrite (fargn term 1) alist 1) :ttree (push-lemma (fn-rune-nume 'hide nil nil wrld) ttree) :rcnst new-rcnst)) (t (rewrite-entry (rewrite-with-lemmas inst-term)))))) ((lambda-nest-hidep term) ; This clause of rewrite implements ``lambda-hide commuting''. The ; idea is that ((LAMBDA (x) (HIDE body)) actual) can be rewritten to ; (HIDE ((LAMBDA (x) body) actual)). But, as above, we must be ; careful with the free vars. (Note: the term is a well-formed lambda ; application, so we know the obvious about the free vars of its body ; versus its formals. But that is not the question! The question is: ; what variables are bound in alist? There is no a priori ; relationship between term and alist.) (let* ((new-body (lambda-nest-unhide term)) (stack (make-stack-from-alist new-body alist)) (inst-term (fcons-term* 'HIDE (if alist (make-lambda-application (caar stack) new-body (cdar stack)) new-body))) (new-rcnst (expand-permission-p inst-term rcnst geneqv wrld))) (cond (new-rcnst ; We rewrite the ``instantiated'' term under the empty substitution. (rewrite-entry (rewrite (fargn inst-term 1) nil 1) :ttree (push-lemma (fn-rune-nume 'hide nil nil wrld) ttree) :rcnst new-rcnst)) (t (rewrite-entry (rewrite-with-lemmas inst-term)))))) ((eq (ffn-symb term) 'IMPLIES) ; We handle IMPLIES specially. We rewrite both the hyps and the ; concl under the original type-alist, and then immediately return the ; resulting expansion. This prevents the concl from being rewritten ; under the (presumably) more powerful type-alist gotten from assuming ; the hyps true until after any normalization has occurred. See the ; mini-essay at assume-true-false-if. ; It is possible that this rewriting will force some hypotheses in a ; ``context free'' way, i.e., forcing might occur while rewriting the ; concl but the forced assumption won't record the hypotheses that ; might actually be necessary to establish the assumption. This is ; not supposed to happen because the only IMPLIES we should see ; (barring any introduced by user supplied rewrite rules) are in :USE ; hyps, and their hyps are normally provable under the hyps of the ; original theorem -- and those original hyps are in the type-alist ; defining this context. (sl-let (rewritten-test ttree) (rewrite-entry (rewrite (fargn term 1) alist 1) :obj '? :geneqv *geneqv-iff*) (sl-let (rewritten-concl ttree) (rewrite-entry (rewrite (fargn term 2) alist 1) :obj '? :geneqv *geneqv-iff*) (mv step-limit (subcor-var ; It seems reasonable to keep this in sync with the corresponding use of ; subcor-var in rewrite-atm. (formals 'IMPLIES wrld) (list rewritten-test rewritten-concl) (body 'IMPLIES t wrld)) ttree)))) ((eq (ffn-symb term) 'double-rewrite) (sl-let (term ttree) (rewrite-entry (rewrite (fargn term 1) alist 1)) (rewrite-entry (rewrite term nil bkptr) :ttree (push-lemma (fn-rune-nume 'double-rewrite nil nil wrld) ttree)))) ((not-to-be-rewrittenp term alist (access rewrite-constant rcnst :terms-to-be-ignored-by-rewrite)) (prepend-step-limit 2 (rewrite-solidify (sublis-var alist term) type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))) (t (let ((fn (ffn-symb term))) (cond ((and (eq fn 'mv-nth) (simplifiable-mv-nthp term alist)) ; This is a special case. We are looking at a term/alist of the form ; (mv-nth 'i (cons x0 (cons x1 ... (cons xi ...)...))) and we immediately ; rewrite it to xi and proceed to rewrite that. Before we did this, we would ; rewrite x0, x1, etc., all of which are irrelevant. This code is helpful ; because of the way (mv-let (v0 v1 ... vi ...) (foo ...) (p v0 ...)) ; is translated. Note however that the bkptr we report in the rewrite entry ; below is 2, i.e., we say we are rewriting the 2nd arg of the mv-nth, when ; in fact we are rewriting a piece of it (namely xi). (mv-let (term1 alist1) (simplifiable-mv-nth term alist) (rewrite-entry (rewrite term1 alist1 2) :ttree (push-lemma (fn-rune-nume 'mv-nth nil nil wrld) ttree)))) (t (mv-let (flg term1 ttree1) ; Rockwell Addition (cond ((eq (nu-rewriter-mode wrld) :literals) (mv nil nil nil)) (t (nth-update-rewriter (cond (ancestors t) ; see below ((recursive-fn-on-fnstackp fnstack) t) (t nil)) term alist (access rewrite-constant rcnst :current-enabled-structure) wrld state))) ; Note about the handling of the recursivelyp flag above: If ancestors ; is non-nil or if there is a recursive function on the fnstack, then ; this term was not analyzed by the call of nth-update-rewriter in ; rewrite-atm. Therefore, we explore it recursively to get rid of ; huge irrelevant parts. But if ancestors is nil and there are no ; recursive functions in sight, then this term was seen earlier. It ; is possible that it was seen under a different context -- our alist ; here contains rewritten terms. So it is possible the term can be ; further simplified now. But rewrite is now recursing through the ; main goal and there is no need for nth-update-rewriter to do that. ; So we need not look at it recursively. (cond (flg (rewrite-entry (rewrite term1 nil 'nth-update) :ttree (cons-tag-trees ttree1 ttree))) (t (sl-let (rewritten-args ttree) (rewrite-entry (rewrite-args (fargs term) alist 1) :obj '? :geneqv (geneqv-lst fn geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld)) (cond ((and (or (flambdap fn) (logicalp fn wrld)) (all-quoteps rewritten-args) (or (flambda-applicationp term) (and (enabled-xfnp fn (access rewrite-constant rcnst :current-enabled-structure) wrld) ; We don't mind disallowing constrained functions that have attachments, ; because the call of ev-fncall below disallows the use of attachments (last ; parameter, aok, is nil). Indeed, we rely on this check in chk-live-state-p. (not (getprop fn 'constrainedp nil 'current-acl2-world wrld))))) ; Note: The test above, if true, leads here where we execute the ; executable counterpart of the fn (or just go into the lambda ; expression if it's a lambda application). The test however is ; obscure. What it says is "run the function if (a) it is either a ; lambda or a :logic function symbol, (b) all of its args are quoted ; constants, and either (c1) the fn is a lambda expression, or (c2) ; the fn is enabled and fn is not a constrained fn." Thus, ; constrained fns fail the test. Defined functions pass the test ; provided such functions are currently toggled. Undefined functions ; (e.g., car) pass the test. (cond ((flambda-applicationp term) (rewrite-entry (rewrite (lambda-body fn) (pairlis$ (lambda-formals fn) rewritten-args) 'lambda-body))) (t (mv-let (erp val latches) (pstk (ev-fncall fn (strip-cadrs rewritten-args) state nil t nil)) (declare (ignore latches)) (cond (erp ; We following a suggestion from Matt Wilding and attempt to rewrite the term ; before applying HIDE. This is really a heuristic choice; we could choose ; always to apply HIDE, as we did before v2-8. So we do not apply ; nth-update-rewriter (as in the next next COND clause, below), nor do we apply ; rewrite-primitive (as in the last COND clause, below) as this would only ; apply in the rare case that the current function symbol (whose evaluation has ; errored out) is a compound recognizer. (let ((new-term1 (cons-term fn rewritten-args))) (sl-let (new-term2 ttree) (rewrite-entry (rewrite-with-lemmas new-term1)) (cond ((equal new-term1 new-term2) (mv step-limit (fcons-term* 'hide new-term1) (push-lemma (fn-rune-nume 'hide nil nil wrld) ttree))) (t (mv step-limit new-term2 ttree)))))) (t (mv step-limit (kwote val) (push-lemma (fn-rune-nume fn nil t wrld) ttree)))))))) ((and (or (equal fn 'NTH) (flambdap fn) (not (recursivep fn wrld))) (not (member-eq (nu-rewriter-mode wrld) '(nil :literals))) (not (equal-mod-alist-lst (fargs term) alist rewritten-args))) ; If this is an application of NTH, a lambda expression, or a ; non-recursive function, and the arguments changed in the rewrite ; above, then we will try nth-update-rewriter again. We do so ; recursively. (mv-let (hitp term1 ttree1) ; Rockwell Addition (nth-update-rewriter t (cons-term fn rewritten-args) nil (access rewrite-constant rcnst :current-enabled-structure) wrld state) (cond (hitp (rewrite-entry (rewrite term1 nil 'nth-update) :ttree (cons-tag-trees ttree1 ttree))) (t ; Note: This code is the same as that below. Keep them in sync! (sl-let (rewritten-term ttree) (rewrite-entry (rewrite-primitive fn rewritten-args)) (rewrite-entry (rewrite-with-lemmas rewritten-term))))))) (t (sl-let (rewritten-term ttree) (rewrite-entry (rewrite-primitive fn rewritten-args)) (rewrite-entry (rewrite-with-lemmas rewritten-term))))))))))))))))) (defun rewrite-solidify-plus (term ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; This function allows us one more try at relieving a hypothesis by rewriting ; with lemmas when rewrite-solidify isn't sufficient. The call of ; rewrite-with-lemmas1 below can allow a hypothesis to be relieved when the ; term in question was previously rewritten in an equality context, rather than ; the more generous propositional context that we have available when relieving ; a hypothesis. ; For a motivating example, see the item in note-2-9 (proofs) starting with: ; "The rewriter has been modified to work slightly harder in relieving ; hypotheses." (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (mv-let (new-term new-ttree) (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)) (cond ((or (eq obj '?) ; Keep the next four conditions in sync with those in rewrite-with-lemmas. (variablep new-term) (fquotep new-term) (member-equal (ffn-symb new-term) (access rewrite-constant rcnst :fns-to-be-ignored-by-rewrite)) (flambda-applicationp term) (not (equal geneqv *geneqv-iff*)) (not (equal term new-term))) (mv step-limit new-term new-ttree)) (t (sl-let (rewrittenp term1 ttree) (rewrite-entry ; We are tempted to call rewrite here. But the point of this call is to handle ; the case that term was the result of looking up a variable in an alist, where ; the term has already been rewritten but perhaps not under *geneqv-iff*. All ; we really want to do here is to make another pass through the lemmas in case ; one of them applies this time. (rewrite-with-lemmas1 term (getprop (ffn-symb new-term) 'lemmas nil 'current-acl2-world wrld))) (declare (ignore rewrittenp)) (mv step-limit term1 ttree))))))) (defun rewrite-if (test unrewritten-test left right alist ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Test is the result of rewriting unrewritten-test under the same alist and ; extra formals. Except, unrewritten-test can be nil, in which case we of ; course make no such claim. ; Warning: If you modify this function, consider modifying the code below a ; comment mentioning rewrite-if in rewrite-with-lemmas. (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (cond ((and (nvariablep test) (not (fquotep test)) (eq (ffn-symb test) 'if) (equal (fargn test 2) *nil*) (equal (fargn test 3) *t*)) ; Note: In Nqthm the equality test against *t* was a known-whether-nil check. ; But unrewritten-test has been rewritten under equiv = 'iff. Hence, its two ; branches were rewritten under 'iff. Thus, if one of them is known non-nil ; under the type-alist then it was rewritten to *t*. (rewrite-entry (rewrite-if (fargn test 1) nil right left alist))) ((quotep test) ; It often happens that the test rewrites to *t* or *nil* and we can ; avoid the assume-true-false below. (if (cadr test) (if (and unrewritten-test ; optimization (see e.g. rewrite-if above) (geneqv-refinementp 'iff geneqv wrld) (equal unrewritten-test left)) ; We are in the process of rewriting a term of the form (if x x y), which ; presumably came from an untranslated term of the form (or x y). We do not ; want to rewrite x more than once if we can get away with it. We are using ; the fact that the following is a theorem: (iff (if x x y) (if x t y)). ; We will use this observation later in the body of this function as well. (mv step-limit *t* ttree) (rewrite-entry (rewrite left alist 2))) (rewrite-entry (rewrite right alist 3)))) (t (let ((ens (access rewrite-constant rcnst :current-enabled-structure))) (mv-let (must-be-true must-be-false true-type-alist false-type-alist ts-ttree) ; Once upon a time, the call of assume-true-false below was replaced by a call ; of repetitious-assume-true-false. See the Essay on Repetitive Typing. This ; caused a terrible slowdown in the proof of the Nqthm package theorems (e.g., ; the proof of AX-20-2 seemed never to complete but was not apparently ; looping). It was apprently due to the opening of MEMBER on a long constant ; list and each time doing a repetition on an increasingly long type-alist (but ; this is just speculation). For a simple example of a problem that arises if ; repetition is used here, consider the example problem shown with the Satriani ; hack above. (Search for make-standard-codes.) Try that thm both with an ; assume-true-false and a repetitious-assume-true-false here. The former takes ; 3.87 seconds; the latter takes about 13.37 seconds. The problem is that we ; keep assuming tests of the form (EQUAL X '#\a) on a type-alist that contains ; a litany of all the chars X is not equal to, i.e., a type-alist containing ; such triples as ((EQUAL X '#\b) 64 ; (*ts-nil*)) for lots of different #\b's. ; On the true branch, we add the pair that X is of type *ts-character* and then ; reconsider every one of the (EQUAL X '#\b) assumptions previously posted. ; Note: Running that example will also illustrate another oddity. You will see ; successive duplicate calls of assume-true-false on the (EQUAL X '#\a)'s. ; What is happening? In opening (MEMBER X '(#\a ...)) in rewrite-fncall we ; rewrite the body of member, producing the first call of assume-true-false ; when we consider (equal x (car lst)). The result of rewriting the body is ; essentially an instance of the body; the recursive call within it is unopened ; because member is recursive (!). Then we decide to keep the rewrite and ; rewrite the body again. So we again assume-true-false the instance of the ; just produced (EQUAL X '#\a). ; If ancestors is non-nil, ACL2 is backchaining to relieve the hypothesis of ; some rule. Conversely, if ancestors is nil, ACL2 is rewriting a term in the ; current clause. As of v2_8 if ACL2 is backchaining, we use the new and ; stronger assume-true-false capability of milking the linear pot. We apply ; the extra power when backchaining because ACL2's operations are largely ; invisible to the user when backchaining. The main effect of using ; assume-true-false this way is to cause recursive definitions to open up a ; little more aggressively. (Since the simplify-clause-pot-lst is passed in, ; linear arithmetic --- via type-reasoning --- can decide the truth or falsity ; of more inequalities than otherwise, causing more if expressions to ; collapse. This may eliminate recursive calls that would otherwise be passed ; up to rewrite-fncallp and have to be accepted as heuristically simpler. It ; could also change the too-many-ifs situation.) We do not apply the extra ; power when rewriting the current clause, because it is potentially expensive ; and the user can see (and therefore change) what is going on. (if ancestors (assume-true-false test nil (ok-to-force rcnst) nil type-alist ens wrld simplify-clause-pot-lst (access rewrite-constant rcnst :pt) nil) (assume-true-false test nil (ok-to-force rcnst) nil type-alist ens wrld nil nil nil)) (cond (must-be-true (if (and unrewritten-test (geneqv-refinementp 'iff geneqv wrld) (equal unrewritten-test left)) (mv step-limit *t* (cons-tag-trees ts-ttree ttree)) (rewrite-entry (rewrite left alist 2) :type-alist true-type-alist :ttree (cons-tag-trees ts-ttree ttree)))) (must-be-false (rewrite-entry (rewrite right alist 3) :type-alist false-type-alist :ttree (cons-tag-trees ts-ttree ttree))) (t (let ((ttree (normalize-rw-any-cache ttree))) (sl-let (rewritten-left ttree) (if (and unrewritten-test (geneqv-refinementp 'iff geneqv wrld) (equal unrewritten-test left)) (mv step-limit *t* ttree) (sl-let (rw-left ttree1) (rewrite-entry (rewrite left alist 2) :type-alist true-type-alist :ttree (rw-cache-enter-context ttree)) (mv step-limit rw-left (rw-cache-exit-context ttree ttree1)))) (sl-let (rewritten-right ttree1) (rewrite-entry (rewrite right alist 3) :type-alist false-type-alist :ttree (rw-cache-enter-context ttree)) (let ((ttree (rw-cache-exit-context ttree ttree1))) (prepend-step-limit 2 (rewrite-if1 test rewritten-left rewritten-right type-alist geneqv ens (ok-to-force rcnst) wrld ttree)))))))))))))) (defun rewrite-args (args alist bkptr; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Note: In this function, the extra formal geneqv is actually a list of geneqvs ; or nil denoting a list of nil geneqvs. (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (cond ((null args) (mv step-limit nil ttree)) (t (sl-let (rewritten-arg ttree) (rewrite-entry (rewrite (car args) alist bkptr) :geneqv (car geneqv)) (sl-let (rewritten-args ttree) (rewrite-entry (rewrite-args (cdr args) alist (1+ bkptr)) :geneqv (cdr geneqv)) (mv step-limit (cons rewritten-arg rewritten-args) ttree))))))) (defun rewrite-primitive (fn args ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) (declare (ignore geneqv obj) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (cond ((flambdap fn) (mv step-limit (fcons-term fn args) ttree)) ((eq fn 'equal) (rewrite-entry (rewrite-equal (car args) (cadr args) nil nil) :obj '? ; don't-care :geneqv nil)) (t (let* ((ens (access rewrite-constant rcnst :current-enabled-structure)) (recog-tuple (most-recent-enabled-recog-tuple fn (global-val 'recognizer-alist wrld) ens))) (cond (recog-tuple (prepend-step-limit 2 (rewrite-recognizer recog-tuple (car args) type-alist ens (ok-to-force rcnst) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))) (t (mv step-limit (cons-term fn args) ttree)))))))) (defun rewrite-equal (lhs rhs lhs-ancestors rhs-ancestors ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We rewrite and return a term equivalent to (EQUAL lhs rhs), plus a ttree. ; We keep lists lhs-ancestors and rhs-ancestors of lhs and rhs parameters from ; superior calls, in order to break loops as explained below. (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((equal lhs rhs) (mv step-limit *t* (puffert ttree))) ((and (quotep lhs) (quotep rhs)) (mv step-limit *nil* (puffert ttree))) (t (mv-let (ts-lookup ttree-lookup) (assoc-type-alist (fcons-term* 'equal lhs rhs) type-alist wrld) (cond ((and ts-lookup (ts= ts-lookup *ts-t*)) (mv step-limit *t* (cons-tag-trees ttree-lookup ttree))) ((and ts-lookup (ts= ts-lookup *ts-nil*)) (mv step-limit *nil* (cons-tag-trees ttree-lookup ttree))) (t (let ((ens (access rewrite-constant rcnst :current-enabled-structure)) (ok-to-force (ok-to-force rcnst))) (mv-let (ts-lhs ttree-lhs) (type-set lhs ok-to-force nil type-alist ens wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)) (mv-let (ts-rhs ttree+) (type-set rhs ok-to-force nil type-alist ens wrld ttree-lhs simplify-clause-pot-lst (access rewrite-constant rcnst :pt)) (mv-let (ts-equality ttree-equality) (type-set-equal ts-lhs ts-rhs ttree+ ttree) (cond ((ts= ts-equality *ts-t*) (mv step-limit *t* ttree-equality)) ((ts= ts-equality *ts-nil*) (mv step-limit *nil* ttree-equality)) ; The commented-out case just below, here explicitly before we added the above ; call of type-set-equalo, is handled by that call. ; ((ts-disjointp ts-lhs ts-rhs) ; (mv *nil* (puffert ttree+))) ((equal-x-cons-x-yp lhs rhs) ; Recall that the correctness of a positive answer by equal-x-cons-x-yp doesn't ; rely on type-set knowledge. (mv step-limit *nil* (puffert ttree))) ((and (ts-subsetp ts-lhs *ts-boolean*) (equal rhs *t*)) (mv step-limit lhs (puffert ttree-lhs))) ((and (ts-subsetp ts-rhs *ts-boolean*) (equal lhs *t*)) (mv step-limit rhs (puffert ttree+))) ((equal lhs *nil*) (mv step-limit (mcons-term* 'if rhs *nil* *t*) (puffert ttree))) ((equal rhs *nil*) (mv step-limit (mcons-term* 'if lhs *nil* *t*) (puffert ttree))) ((equalityp lhs) (mv step-limit (mcons-term* 'if lhs (mcons-term* 'equal rhs *t*) (mcons-term* 'if rhs *nil* *t*)) (puffert ttree))) ((equalityp rhs) (mv step-limit (mcons-term* 'if rhs (mcons-term* 'equal lhs *t*) (mcons-term* 'if lhs *nil* *t*)) (puffert ttree))) ((and (ts-subsetp ts-lhs *ts-cons*) (ts-subsetp ts-rhs *ts-cons*) (not (member-equal lhs lhs-ancestors)) (not (member-equal rhs rhs-ancestors))) ; If lhs and rhs are both of type cons, we (essentially) recursively rewrite ; the equality of their cars and then of their cdrs. If either of these two ; tests fails, this equality is nil. If both succeed, this one is t. ; Otherwise, we don't rewrite term. ; Before attempting to add complete equality we did not do anything like this ; and relied solely on elim to do it for us. In the first attempt to add it to ; rewrite we just rewrote all such (EQUAL lhs rhs) to the conjunction of the ; equalities of the components. That was unsatisfactory because it caused such ; equalities as (EQUAL (ADDTOLIST X L) B) to be torn up all the time. That ; caused us to fail to prove thms like SORT-OF-ORDERED-NUMBER-LIST because weak ; subgoals are pushed -- subgoals about (CAR (ADDTOLIST X L)) and (CDR ; (ADDTOLIST X L)) instead about (ADDTOLIST X L) itself. ; In Version_3.3 and earlier (even as far back as Version_2.2) we rewrote ; equality terms (equal (car lhs) (car rhs)) and (equal (cdr lhs) (cdr rhs)), ; with variables lhs and rhs bound to the parameters lhs and rhs. But now we ; instead call the rewriter separately on the car and cdr of lhs and rhs (hence ; "essentially" in a paragraph above). Then to check equality we finish using ; a recursive call of rewrite-equal with lhs and rhs pushed on to the stacks ; lhs-ancestors and rhs-ancestors (respectively). We avoid making a recursive ; call if we see that we have looped back to a call with the same lhs or rhs, ; which indicates a potential infinite loop. When we formerly called the full ; rewriter on (equal (car lhs) (car rhs)) and (equal (cdr lhs) (cdr rhs)), We ; did not make such a check and we found an infinite loop in the following ; example (a slight simplification of one Sol Swords sent to us); see just ; below for analysis. ; (thm (implies (and (consp y) ; (consp (car y)) ; (equal (caar y) y)) ; (equal y (car y)))) ; If you try the following trace on the above example using Version_3.3, where ; we called rewrite on applications of equal to the two cars and the two cdrs ; (trace$ (rewrite :entry (list 'rewrite term alist type-alist)) ; (rewrite-equal :entry (list 'r-e lhs rhs type-alist))) ; then you will see a loop as follows. ; 98> (R-E Y ; (CAR Y) ; (((CAR (CAR Y)) 1536) ; ((EQUAL (CAR (CAR Y)) Y) 128) ; 128 = *ts-t* ; ((CAR Y) 1536) ; (Y 1536))) ; 99> (REWRITE (EQUAL (CAR LHS) (CAR RHS)) ; ((LHS . Y) (RHS CAR Y)) ; (((CAR (CAR Y)) 1536) ; ((EQUAL (CAR (CAR Y)) Y) 128) ; ((CAR Y) 1536) ; (Y 1536))) ; .... (CAR LHS) rewrites to (CAR Y) and (CAR RHS) rewrites to Y .... ; .... Then: .... ; 100> (R-E (CAR Y) ; Y ; (((CAR (CAR Y)) 1536) ; ((EQUAL (CAR (CAR Y)) Y) 128) ; ((CAR Y) 1536) ; (Y 1536))) ; The calls of rewrite-equal keep toggling between argument list (Y (CAR Y)) ; and ((CAR Y) Y), because when we take the CAR, Y becomes (CAR Y), but (CAR Y) ; becomes (CAR (CAR Y)) which simplifies to Y. Our loop-breaking mechanism ; clearly avoids this problem. (An elim is still needed to finish the proof, ; but that's fine.) (let ((alist (list (cons 'lhs lhs) (cons 'rhs rhs)))) (sl-let (equal-cars new-ttree) (sl-let (cars ttree0) (rewrite-entry (rewrite-args '((car lhs) (car rhs)) alist 1) :obj '? :geneqv nil :ttree ttree+) (rewrite-entry (rewrite-equal (car cars) (cadr cars) ; We considered an alternative to adding the lhs-ancestors and rhs-ancestors ; arguments, namely adding a flag saying whether we could move into this branch ; at all (in place of the member-equal tests above). With that alternative we ; considered calling rewrite-equal here with that flag set to nil. However, ; the following example failed when we attempted to make such a restriction on ; making recursive calls. ; (progn (defstub fn (x) t) ; (defthm test ; (implies (and (consp (fn x)) ; (consp (car (fn x))) ; (null (cdar (fn x)))) ; (equal (cons (cons (caar (fn x)) ; nil) ; (cdr (fn x))) ; (fn x))))) (cons lhs lhs-ancestors) (cons rhs rhs-ancestors)) :obj '? :geneqv nil :ttree ttree0)) ; Note that we pass ttree+ (which includes ttree) into the rewrite of ; the car equality and getting back new-ttree. We will pass new-ttree ; into the rewrite of the cdr equality and get back new-ttree. If we ; succeed, we'll return new-ttree, which includes ttree, ttree+, and ; the rewriting; otherwise, we'll stick with the original ttree. (cond ((equal equal-cars *t*) (sl-let (equal-cdrs new-ttree) (sl-let (cdrs ttree0) (rewrite-entry (rewrite-args '((cdr lhs) (cdr rhs)) alist 1) :obj '? :geneqv nil :ttree new-ttree) (rewrite-entry (rewrite-equal (car cdrs) (cadr cdrs) (cons lhs lhs-ancestors) (cons rhs rhs-ancestors)) :obj '? :geneqv nil :ttree ttree0)) (cond ((equal equal-cdrs *t*) (mv step-limit *t* (puffert new-ttree))) ((equal equal-cdrs *nil*) (mv step-limit *nil* (puffert new-ttree))) (t (mv step-limit (mcons-term* 'equal lhs rhs) (accumulate-rw-cache t new-ttree ttree)))))) ((equal equal-cars *nil*) (mv step-limit *nil* (puffert new-ttree))) (t (let ((ttree (accumulate-rw-cache t new-ttree ttree))) ; If we fail to get a definitive answer then we still might be able to ; answer negatively by rewriting the cdrs. We have been asymmetric ; for a long time without knowing it; at this point we used to simply ; return (mcons-term* 'equal lhs rhs). In fact, the following theorem ; didn't prove -- ; (implies (equal (cons a b) (cons x y)) ; (equal b y)) ; even though the analogous one for the cars did prove: ; (implies (equal (cons a b) (cons x y)) ; (equal a x)) ; If the cdrs aren't known to be different, then we do simply return ; the obvious equality. That is what we would have done had lhs or ; rhs not been of type *ts-cons* -- see the (t (mv (mcons-term* ...) ; ttree)) clause at the very end of this function. The explicit ; returning of the equality forces us to consider the (and (ts-subsetp ; ts-lhs *ts-cons*) (ts-subsetp ts-rhs *ts-cons*)) case as the second ; to last case in the main cond. We could have coded the and above ; differently so that if both were conses and the rewrites decide it ; then we return appropriately and otherwise we fall through to ; whatever other rewrites we consider. But we didn't. (sl-let (equal-cdrs new-ttree) (sl-let (cdrs ttree0) (rewrite-entry (rewrite-args '((cdr lhs) (cdr rhs)) alist 1) :obj '? :geneqv nil :ttree ttree) (rewrite-entry (rewrite-equal (car cdrs) (cadr cdrs) (cons lhs lhs-ancestors) (cons rhs rhs-ancestors)) :obj '? :geneqv nil :ttree ttree0)) (cond ((equal equal-cdrs *nil*) (mv step-limit *nil* (puffert new-ttree))) (t (mv step-limit (mcons-term* 'equal lhs rhs) (accumulate-rw-cache t new-ttree ttree))))))))))) (t (mv step-limit (mcons-term* 'equal lhs rhs) ttree)))))))))))))) (defun relieve-hyp (rune target hyp0 unify-subst bkptr memo ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We are trying to rewrite hyp0 to true, where hyp0 is the hypothesis of rune ; at (one-based) position bkptr, and target is an instantiated term to which ; rune is being applied. ; We return six results. Most often they are interpreted as indicated by the ; names: ; (mv step-limit wonp failure-reason unify-subst' ttree' memo'). ; Here wonp is t, nil, :unify-subst-list, or a term. If it is t, nil, or ; :unify-subst-list, then interpretation of the results is as hinted above: ; wonp indicates whether hyp0 was relieved, failure-reason is nil or else a ; token indicating why we failed, and the rest are extended versions of the ; corresponding inputs except for the case :unify-subst-list, where ; unify-subst' is actually a list of unifying substitutions, each of which is ; sufficient for relieving the remaining hypotheses. ; But there is a special case where they are interpreted quite differently: if ; wonp is a term then it means that hyp0 contains free-vars, it was not ; relieved, and the six results are to be interpreted as follows, ; where the last three are unchanged. ; (mv step-limit term typ unify-subst ttree memo) ; This signals that the caller of relieve-hyp is responsible for relieving the ; hypothesis and may do so in either of two ways: Extend unify-subst to make ; term have typ in the original type-alist or extend unify-subst to make hyp0 ; true via ground units. This is called the SPECIAL CASE. ; This function is a No-Change Loser modulo rw-cache: only the values of ; 'rw-cache-any-tag and 'rw-cache-nil-tag may differ between the input and ; output ttrees. ; Below we describe the memo argument, but first, here is an example that ; illustrates how it is used. ; (defstub p1 (x) t) ; (defstub p2 (x) t) ; (defstub p3 (x) t) ; (defaxiom ax (implies (and (p1 x) (p2 y) (consp x) (symbolp y)) (p3 x))) ; (thm (implies (and (p1 a) (p2 b) (p2 c) (consp a) (symbolp b)) (p3 a))) ; In the proof of thm, a rewrite of (p3 a) triggers application of ax. Note ; that (p2 c) is in front of (p2 b) on the type-alist. So, the second ; hypothesis of ax first binds y to c. Since (symbolp y) fails with this ; binding, we backtrack in the relieving of hyps for ax, and now bind y to b. ; But note that we encounter (consp x) again. Rather than have to rewrite ; (consp x) again, we save the fact that it was relieved when that happened the ; first time, when y was bound to c. How do we do this? ; Memo (called "allp" in other functions in this nest) can be an alist with ; entries of the form (n vars (subst0 . ttree0) ... (substk . ttreek)), where n ; is a bkptr, vars is (all-vars hyp0), and ttreei is the result of succesfully ; calling relieve-hyp with the following arguments: ttree=nil; bkptr=n; ; unify-subst is some substitution whose restriction to vars is substi; and the ; other arguments are the same. In these cases substi should bind all the free ; variables of hyp0. The other legal values of memo are nil, t and :start. If ; memo is nil or t then we do not memoize, though in the case of t we may start ; memoizing in later calls because we have a free variable. If memo is :start ; or an alist then we return an extended memo (where :start is viewed as the ; empty memo) if this call of relieve-hyp succeeds and all variables of hyp0 ; are bound in unify-subst. ; Note that unlike some other functions in the rewrite clique, here we really ; do care that bkptr is a number representing the hypothesis. (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 6 (signed-byte 30) (cond ((and (nvariablep hyp0) (not (fquotep hyp0)) (eq (ffn-symb hyp0) 'synp)) (mv-let (wonp failure-reason unify-subst ttree) (relieve-hyp-synp rune hyp0 unify-subst rdepth type-alist wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree bkptr) (mv step-limit wonp failure-reason unify-subst ttree memo))) (t (mv-let (forcep1 bind-flg) (binding-hyp-p hyp0 unify-subst wrld) (let ((hyp (if forcep1 (fargn hyp0 1) hyp0))) (cond (bind-flg (sl-let (rewritten-rhs ttree) (rewrite-entry (rewrite (fargn hyp 2) unify-subst (if (or (f-get-global 'gstackp state) (f-get-global 'dmrp state)) (cons 'rhs bkptr) nil)) :obj '? :ancestors (cons (make-ancestor-binding-hyp hyp unify-subst) ancestors) :geneqv (and (not (eq (ffn-symb hyp) 'equal)) (cadr (geneqv-lst (ffn-symb hyp) *geneqv-iff* (access rewrite-constant rcnst :current-enabled-structure) wrld)))) (mv step-limit t nil (cons (cons (fargn hyp 1) rewritten-rhs) unify-subst) ttree memo))) ((free-varsp hyp unify-subst) ; See comment above about "SPECIAL CASE". (mv-let (term typ) (term-and-typ-to-lookup hyp wrld) (mv step-limit term typ unify-subst ttree memo))) (t (let* ((memo-active (memo-activep memo)) (memo-entry (and (consp memo) (cdr (assoc bkptr memo)))) (hyp-vars (if memo-entry (car memo-entry) (and memo-active ; optimization (all-vars hyp0)))) (restricted-unify-subst (and memo-active ; optimization (restrict-alist hyp-vars unify-subst))) (old-entry (and memo-entry (assoc-equal restricted-unify-subst (cdr memo-entry))))) (cond (old-entry (mv step-limit t nil unify-subst (cons-tag-trees-rw-cache (cdr old-entry) ttree) memo)) (t (sl-let (relieve-hyp-ans failure-reason unify-subst ttree0) (let ((ttree (if memo-active ; If memo-active is true, we may be storing a ttree from the work done below, ; and we do not want to accumulate the existing ttree into that one. Later ; below, if memo-active is true, then we will cons ttree0 (bound above) with ; ttree. (rw-cache ttree) ttree))) (mv-let (lookup-hyp-ans unify-subst ttree) (lookup-hyp hyp type-alist wrld unify-subst ttree) ; We know that unify-subst is not extended, since (free-varsp hyp unify-subst) ; is false, but it still seems appropriate to use the existing code in ; one-way-unify1 under search-type-alist (under lookup-hyp). (cond (lookup-hyp-ans (mv step-limit t nil unify-subst ttree)) (t (let* ((inst-hyp (sublis-var unify-subst hyp)) (forcer-fn (and forcep1 (ffn-symb hyp0))) (force-flg (ok-to-force rcnst)) (forcep (and forcep1 force-flg))) (mv-let (knownp nilp nilp-ttree) (known-whether-nil inst-hyp type-alist (access rewrite-constant rcnst :current-enabled-structure) force-flg nil ; dwp wrld ttree) (cond (knownp (cond (nilp (mv step-limit nil 'known-nil unify-subst ttree)) (t (mv step-limit t nil unify-subst nilp-ttree)))) (t (mv-let (on-ancestorsp assumed-true) (ancestors-check inst-hyp ancestors (list rune)) (cond ((and on-ancestorsp assumed-true) (mv step-limit t nil unify-subst ttree)) ((or on-ancestorsp ; and (not assumed-true) (backchain-limit-reachedp backchain-limit ancestors)) (mv-let (force-flg ttree) (cond ((not forcep) (mv nil ttree)) (t (force-assumption rune target inst-hyp type-alist nil (immediate-forcep forcer-fn (access rewrite-constant rcnst :current-enabled-structure)) force-flg ttree))) (cond (force-flg (mv step-limit t nil unify-subst ttree)) (t (mv step-limit nil (if on-ancestorsp 'ancestors (cons 'backchain-limit backchain-limit)) unify-subst ttree))))) (t (mv-let (not-flg atm) (strip-not hyp) (sl-let (rewritten-atm new-ttree) (rewrite-entry (rewrite atm unify-subst bkptr) :obj (if not-flg nil t) :geneqv *geneqv-iff* :ancestors (push-ancestor (dumb-negate-lit inst-hyp) (list rune) ancestors)) (cond (not-flg (if (equal rewritten-atm *nil*) (mv step-limit t nil unify-subst new-ttree) (mv-let (force-flg new-ttree) (if (and forcep ; Since we rewrote under *geneqv-iff*, the only way that rewritten-atm ; is known not to be nil is if it's t. (not (equal rewritten-atm *t*))) (force-assumption rune target (mcons-term* 'not rewritten-atm) type-alist ; Note: :rewrittenp = instantiated unrewritten term. (mcons-term* 'not (sublis-var unify-subst atm)) (immediate-forcep forcer-fn (access rewrite-constant rcnst :current-enabled-structure)) force-flg new-ttree) (mv nil new-ttree)) (cond (force-flg (mv step-limit t nil unify-subst new-ttree)) (t (mv step-limit nil (cons 'rewrote-to (dumb-negate-lit rewritten-atm)) unify-subst (accumulate-rw-cache t new-ttree ttree))))))) ((if-tautologyp rewritten-atm) (mv step-limit t nil unify-subst new-ttree)) (t (mv-let (force-flg new-ttree) (cond ((and forcep (not (equal rewritten-atm *nil*))) (force-assumption rune target rewritten-atm type-alist ; Note: :rewrittenp = instantiated unrewritten term. (sublis-var unify-subst atm) (immediate-forcep forcer-fn (access rewrite-constant rcnst :current-enabled-structure)) force-flg new-ttree)) (t (mv nil new-ttree))) (cond (force-flg (mv step-limit t nil unify-subst new-ttree)) (t (mv step-limit nil (cons 'rewrote-to rewritten-atm) unify-subst (accumulate-rw-cache t new-ttree ttree)))))))))))))))))))) (cond (relieve-hyp-ans (mv step-limit relieve-hyp-ans failure-reason unify-subst (if memo-active (cons-tag-trees-rw-cache-first ttree ttree0) ttree0) (cond (memo-entry (put-assoc-eql bkptr (list* hyp-vars (cons (cons restricted-unify-subst ttree0) (cdr memo-entry))) memo)) (memo-active (put-assoc-eql bkptr (list* hyp-vars (cons (cons restricted-unify-subst ttree0) nil)) (if (eq memo :start) nil memo))) (t memo)))) (t (mv step-limit relieve-hyp-ans failure-reason unify-subst (accumulate-rw-cache t ttree0 ttree) memo))))))))))))))) (defun relieve-hyps1-iter (rune target hyps backchain-limit-lst unify-subst-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; This function calls relieve-hyps1 on each alist in unify-subst-list (which is ; non-empty) until the hypotheses are relieved, extending the given unify-subst ; by that alist for each such call. Note that if this function fails, then the ; failure-reason will be reported based on the last one tried. That seems the ; simplest approach both for this implementation and for reporting to the ; user. If there are user complaints about that, we can consider a more ; elaborate form of failure reporting. (sl-let (relieve-hyps1-ans failure-reason unify-subst1 ttree1 allp rw-cache-alist-new) (rewrite-entry (relieve-hyps1 rune target hyps backchain-limit-lst (extend-unify-subst (car unify-subst-lst) unify-subst) bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new)) (cond ((or (endp (cdr unify-subst-lst)) relieve-hyps1-ans) (mv step-limit relieve-hyps1-ans failure-reason unify-subst1 ttree1 allp rw-cache-alist-new)) (t (rewrite-entry (relieve-hyps1-iter rune target hyps backchain-limit-lst (cdr unify-subst-lst) unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new)))))) (defun relieve-hyps1 (rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; In order to make relieve-hyps a No-Change Loser (modulo rw-cache) without ; making it have to test the answer to its own recursive calls, we have to pass ; down the original unify-subst and ttree so that when it fails it can return ; them instead of the accumulated ones it otherwise would have. ; Parameter allp is nil iff rune has behavior :match-free :once (as opposed to ; :match-free :all). Its legal non-nil values are explained in a comment in ; relieve-hyp (where it is called memo). NOTE: if allp is not nil or t then ; allp does not change if we fail, but if allp is :start or an alist then its ; returned value can change even if relieve-hyps1 fails, in order for it to ; serve its memoization purpose. ; We accumulate updates to make to rw-cache-alist into parameter ; rw-cache-alist-new, which is ultimately returned. Note that ; relieve-hyps1-free-1 and relieve-hyps1-free-2 take responsibility for ; extending rw-cache-alist-new. Note that rw-cache-alist-new contains only new ; entries, rather than extending rw-cache-alist. (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 7 (signed-byte 30) (cond ((null hyps) (mv step-limit t nil unify-subst ttree allp rw-cache-alist-new)) (t (sl-let (relieve-hyp-ans failure-reason new-unify-subst new-ttree allp) (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) relieve-hyp-ans failure-reason new-unify-subst new-ttree allp) ; Even in the "special case" for relieve-hyp, we can mark this as a success ; because it will ultimately be counted as a failure if the surrounding call of ; relieve-hyps fails. relieve-hyp-ans (rewrite-entry (relieve-hyp rune target (car hyps) unify-subst bkptr allp) :backchain-limit (new-backchain-limit (car backchain-limit-lst) backchain-limit ancestors) :obj nil :geneqv nil) bkptr) (cond ((eq relieve-hyp-ans t) (rewrite-entry (relieve-hyps1 rune target (cdr hyps) (cdr backchain-limit-lst) new-unify-subst (1+ bkptr) unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new) :obj nil :geneqv nil :ttree new-ttree)) ((eq relieve-hyp-ans :unify-subst-list) ; The hypothesis (car hyps) is a call of bind-free that has produced a list of ; unify-substs. (rewrite-entry (relieve-hyps1-iter rune target (cdr hyps) (cdr backchain-limit-lst) new-unify-subst ; a list of alists unify-subst (1+ bkptr) unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new) :obj nil :geneqv nil :ttree new-ttree)) (relieve-hyp-ans ; As explained in the "SPECIAL CASE" comment in relieve-hyp, relieve-hyp ; returned (mv step-limit term typ unify-subst ttree allp). We enter a loop in ; which we try to relieve the current hypothesis and subsequent hypotheses by ; instantiating the variables in term that are free with respect to ; unify-subst. (let* ((hyp (car hyps)) (forcep1 (and (nvariablep hyp) (not (fquotep hyp)) (or (eq (ffn-symb hyp) 'force) (eq (ffn-symb hyp) 'case-split)))) (forcer-fn (and forcep1 (ffn-symb hyp))) (hyp (if forcep1 (fargn hyp 1) (car hyps))) (force-flg (ok-to-force rcnst)) (forcep (and forcep1 force-flg))) ; The following call of relieve-hyps1-free-1 will return an "activated" allp ; structure even if the current allp is t. But if the current allp is t, then ; we are just now seeing our first free variable as we work our way through the ; hyps. Since there is no search above us, there will be no further calls of ; relieve-hyps1 under the call of relieve-hyps that we are inside. So, the ; returned value for allp is irrelevant if the current allp is t. (sl-let (relieve-hyps-ans failure-reason-lst unify-subst ttree allp rw-cache-alist-new) (rewrite-entry (relieve-hyps1-free-1 relieve-hyp-ans ; term failure-reason ; typ hyp type-alist forcer-fn forcep force-flg rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 (activate-memo allp) rw-cache-alist rw-cache-alist-new) :obj nil :geneqv nil) (mv step-limit relieve-hyps-ans (and (null relieve-hyps-ans) (cond ((null (f-get-global 'gstackp state)) nil) ; save some conses (failure-reason-lst (list* bkptr 'free-vars failure-reason-lst)) (t ; There were no variable bindings. (list* bkptr 'free-vars 'hyp-vars (reverse (set-difference-assoc-eq (all-vars hyp) unify-subst)))))) unify-subst ttree allp rw-cache-alist-new)))) (t (mv step-limit nil (cons bkptr failure-reason) unify-subst0 (accumulate-rw-cache t new-ttree ttree0) allp rw-cache-alist-new)))))))) (defun relieve-hyps1-free-1 (term typ hyp rest-type-alist forcer-fn forcep force-flg rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We search the type-alist in order to extend unify-subst so that a ; corresponding instance of term has type typ. Then (with a call to ; relieve-hyps1-free-2) we search ground units in an attempt to extend ; unify-subst to make term true. ; We return seven values: a new step-limit, a relieve-hyps-ans, a ; failure-reason-lst that is a list of pairs (cons extended-unify-subst_i ; failure-reason_i), a unify-subst extending the given unify-subst, a ttree, a ; resulting allp, and an alist extending rw-cache-alist-new that will ; ultimately (in relieve-hyps) be merged into rw-cache-alist (and a ; corresponding alist for the "nil" cache). Each failure-reason_i corresponds ; to the attempt to relieve hyps using extended-unify-subst_i, an extension of ; unify-subst. The failure-reason-lst is used in ; tilde-@-failure-reason-free-phrase to explain why each attempt at extending ; the unify-subst failed to succeed, except if this list is empty, then a ; 'hyp-vars token is used in its place (see relieve-hyps1). (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 7 (signed-byte 30) (mv-let (ans new-unify-subst new-ttree new-rest-type-alist) (search-type-alist+ term typ rest-type-alist unify-subst ttree wrld) (cond (ans (mv-let (cached-failure-reason-free cached-failure-reason) (rw-cached-failure-pair new-unify-subst rw-cache-alist) (sl-let (relieve-hyps-ans failure-reason unify-subst1 ttree1 allp inferior-rw-cache-alist-new) (cond (cached-failure-reason (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (cons 'cached cached-failure-reason)) unify-subst ttree allp nil)) (t (rewrite-entry (relieve-hyps1 rune target (cdr hyps) (cdr backchain-limit-lst) new-unify-subst (1+ bkptr) unify-subst0 ttree0 allp (cdr cached-failure-reason-free) nil) :obj nil :geneqv nil :ttree new-ttree))) (let ((rw-cache-alist-new (extend-rw-cache-alist-free rcnst new-unify-subst inferior-rw-cache-alist-new rw-cache-alist-new))) (cond (relieve-hyps-ans (mv step-limit relieve-hyps-ans nil unify-subst1 ttree1 allp rw-cache-alist-new)) (t (let ((rw-cache-alist-new ; add normal-failure reason (rw-cache-add-failure-reason rcnst new-unify-subst failure-reason rw-cache-alist-new))) (cond ((not allp) ; hence original allp is nil (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (list (cons new-unify-subst failure-reason))) unify-subst0 (accumulate-rw-cache t ttree1 ttree0) nil ; allp rw-cache-alist-new)) (t ; look for the next binding in the type-alist (rewrite-entry-extending-failure new-unify-subst failure-reason (relieve-hyps1-free-1 term typ hyp new-rest-type-alist forcer-fn forcep force-flg rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new) :obj nil :geneqv nil :ttree (accumulate-rw-cache t ttree1 ttree))))))))))) (t ; failed to relieve hyp using rest-type-alist (rewrite-entry (relieve-hyps1-free-2 hyp (relevant-ground-lemmas hyp wrld) forcer-fn forcep (access rewrite-constant rcnst :current-enabled-structure) force-flg rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new) :obj nil :geneqv nil)))))) (defun relieve-hyps1-free-2 (hyp lemmas forcer-fn forcep ens force-flg rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We search ground units in an attempt to extend unify-subst to make term true, ; As with relieve-hyps1-free-1, we return a relieve-hyps-ans, a ; failure-reason-lst that is a list of pairs (cons new-unify-subst ; failure-reason), a unify-subst extending the given unify-subst, a ttree, and ; a resulting allp. (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 7 (signed-byte 30) (cond ((endp lemmas) ; If we have to force this hyp, we make sure all its free vars are bound by ; fully-bound-unify-subst, an extension of unify-subst. (let ((fully-bound-unify-subst (if force-flg (bind-free-vars-to-unbound-free-vars (all-vars hyp) unify-subst) unify-subst))) (mv-let (force-flg ttree) (cond ((not forcep) (mv nil ttree)) (t (force-assumption rune target (sublis-var fully-bound-unify-subst hyp) type-alist nil (immediate-forcep forcer-fn (access rewrite-constant rcnst :current-enabled-structure)) force-flg ttree))) (cond (force-flg (mv-let (cached-failure-reason-free cached-failure-reason) (rw-cached-failure-pair fully-bound-unify-subst rw-cache-alist) (cond (cached-failure-reason (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (list ; failure-reason-lst (cons fully-bound-unify-subst (cons 'cached cached-failure-reason)))) unify-subst0 (accumulate-rw-cache t ttree ttree0) allp rw-cache-alist-new)) (t (sl-let (relieve-hyps-ans failure-reason unify-subst1 ttree1 allp inferior-rw-cache-alist-new) (rewrite-entry (relieve-hyps1 rune target (cdr hyps) (cdr backchain-limit-lst) fully-bound-unify-subst (1+ bkptr) unify-subst0 ttree0 allp (cdr cached-failure-reason-free) nil) :obj nil :geneqv nil) (let ((rw-cache-alist-new (extend-rw-cache-alist-free rcnst fully-bound-unify-subst inferior-rw-cache-alist-new rw-cache-alist-new))) (cond (relieve-hyps-ans (mv step-limit relieve-hyps-ans nil ; failure-reason-lst unify-subst1 ttree1 allp rw-cache-alist-new)) (t (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (list (cons fully-bound-unify-subst failure-reason))) unify-subst0 (accumulate-rw-cache t ttree1 ttree0) allp (rw-cache-add-failure-reason rcnst fully-bound-unify-subst failure-reason rw-cache-alist-new)))))))))) (t (mv step-limit nil nil ; failure-reason-lst unify-subst0 (accumulate-rw-cache t ttree ttree0) allp rw-cache-alist-new)))))) (t (mv-let (winp new-unify-subst new-ttree rest-lemmas) (search-ground-units1 hyp unify-subst lemmas type-alist ens force-flg wrld ttree) (cond (winp (mv-let (cached-failure-reason-free cached-failure-reason) (rw-cached-failure-pair new-unify-subst rw-cache-alist) (sl-let (relieve-hyps-ans failure-reason unify-subst1 ttree1 allp inferior-rw-cache-alist-new) (cond (cached-failure-reason (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (list ; failure-reason-lst (cons new-unify-subst (cons 'cached cached-failure-reason)))) unify-subst ttree allp nil)) (t (rewrite-entry (relieve-hyps1 rune target (cdr hyps) (cdr backchain-limit-lst) new-unify-subst (1+ bkptr) unify-subst0 ttree0 allp (cdr cached-failure-reason-free) nil) :obj nil :geneqv nil :ttree new-ttree))) (let ((rw-cache-alist-new (extend-rw-cache-alist-free rcnst new-unify-subst inferior-rw-cache-alist-new rw-cache-alist-new))) (cond (relieve-hyps-ans (mv step-limit relieve-hyps-ans nil unify-subst1 ttree1 allp rw-cache-alist-new)) (t (let ((rw-cache-alist-new ; add normal-failure reason (rw-cache-add-failure-reason rcnst new-unify-subst failure-reason rw-cache-alist-new))) (cond ((not allp) ; hence original allp is nil (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (list ; failure-reason-lst (cons new-unify-subst failure-reason))) unify-subst0 (accumulate-rw-cache t ttree1 ttree0) nil rw-cache-alist-new)) (t (rewrite-entry-extending-failure new-unify-subst failure-reason (relieve-hyps1-free-2 hyp rest-lemmas forcer-fn forcep ens force-flg rune target hyps backchain-limit-lst unify-subst bkptr unify-subst0 ttree0 allp rw-cache-alist rw-cache-alist-new) :obj nil :geneqv nil :ttree (accumulate-rw-cache t ttree1 ttree))))))))))) (t (mv step-limit nil nil ; failure-reason-lst unify-subst0 ; We believe that new-ttree is unlikely to have rw-cache entries that are not ; already in ttree0, as they would generally (always?) come from type-set ; computations. But we expect the following call to be cheap, so we make it. (accumulate-rw-cache t new-ttree ttree0) allp rw-cache-alist-new)))))))) (defun relieve-hyps (rune target hyps backchain-limit-lst unify-subst allp ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We return t or nil indicating success, a token indicating why we failed (or ; nil if we succeeded), an extended unify-subst and a new ttree. Allp is ; either t or nil, according to whether or not we are to attempt all free ; variable matches until we succeed. ; This function is a No-Change Loser modulo rw-cache: only the values of ; 'rw-cache-any-tag and 'rw-cache-nil-tag may differ between the input and ; output ttrees. (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 5 (signed-byte 30) (let* ((ttree-saved ttree) (rw-cache-active-p (rw-cache-active-p rcnst)) (cached-failure-entry (and rw-cache-active-p (relieve-hyp-failure-entry rune unify-subst hyps ttree step-limit))) (cached-failure-reason-raw (and cached-failure-entry (access rw-cache-entry cached-failure-entry :failure-reason))) (cached-failure-reason-free-p (and (consp cached-failure-reason-raw) (free-failure-p cached-failure-reason-raw))) (cached-failure-reason-free (and cached-failure-reason-free-p (equal (access rw-cache-entry cached-failure-entry :hyp-info) hyps) cached-failure-reason-raw)) (cached-failure-reason (and (not cached-failure-reason-free-p) cached-failure-reason-raw)) (debug (and cached-failure-reason (rw-cache-debug rune target unify-subst cached-failure-reason step-limit)))) (cond ((and cached-failure-reason (not debug)) (mv step-limit nil (and (f-get-global 'gstackp state) ; cons optimization (cons 'cached cached-failure-reason)) unify-subst ttree)) (t (let ((step-limit-saved step-limit) (unify-subst-saved unify-subst) (old-rw-cache-alist (cdr cached-failure-reason-free))) (sl-let (relieve-hyps-ans failure-reason unify-subst ttree allp new-rw-cache-alist) (rewrite-entry (relieve-hyps1 rune target hyps backchain-limit-lst unify-subst 1 unify-subst ttree allp old-rw-cache-alist nil) :obj nil :geneqv nil ; If we are doing non-linear arithmetic, we will be rewriting linear ; terms under a different theory than the standard one. However, when ; relieving hypotheses, we want to use the standard one, so we make ; sure that that is what we are using. :rcnst (if (eq (access rewrite-constant rcnst :active-theory) :standard) rcnst (change rewrite-constant rcnst :active-theory :standard))) (declare (ignore allp)) (cond ((and debug relieve-hyps-ans) (prog2$ (rw-cache-debug-action rune target unify-subst-saved cached-failure-reason step-limit-saved) (mv step-limit nil cached-failure-reason unify-subst-saved ttree-saved))) (t (mv step-limit relieve-hyps-ans failure-reason unify-subst (cond ((or relieve-hyps-ans backchain-limit (not rw-cache-active-p)) ttree) (new-rw-cache-alist ; free vars case (note-relieve-hyps-failure-free rune unify-subst hyps ttree cached-failure-entry old-rw-cache-alist new-rw-cache-alist ; At one time we only saved the step-limit in debug mode, so that when we merge ; rw-caches after calls of cons-tag-trees, we avoid essentially duplicated ; rw-cache-entry records, differing only in their :step-limit fields. However, ; we now save the step-limit unconditionally, because we may be calling ; merge-lexorder-fast a lot and the :step-limit field of a rw-cache-entry ; record can give a quick result. The potential for rare duplication seems ; harmless. step-limit-saved)) (t ; We cache the rewriting failure into the ttree. It would be a mistake to ; extend the rw-cache if there is a backchain-limit, because a later lookup ; might be made with a different backchain-limit. This may be why ; Prime-property-lemma, in community book ; workshops/2006/cowles-gamboa-euclid/Euclid/ed3.lisp, fails with ; :rw-cache-state :atom. (note-relieve-hyp-failure rune unify-subst failure-reason ttree hyps ; See comment above about regarding our formerly saving the step-limit only in ; debug mode. step-limit-saved))))))))))))) (defun rewrite-with-lemma (term lemma ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; The four values returned by this function are: a new step-limit, t or nil ; indicating whether lemma was used to rewrite term, the rewritten version of ; term, and the final version of ttree. ; This function is a No-Change Loser modulo rw-cache: only the values of ; 'rw-cache-any-tag and 'rw-cache-nil-tag may differ between the input and ; output ttrees. (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 4 (signed-byte 30) (let ((gstack (push-gframe 'rewrite-with-lemma nil term lemma)) (rdepth (adjust-rdepth rdepth))) (declare (type (unsigned-byte 29) rdepth)) (cond ((zero-depthp rdepth) (rdepth-error (mv step-limit nil term ttree))) ((eq (access rewrite-rule lemma :subclass) 'meta) ; See the Essay on Correctness of Meta Reasoning, above, and :doc meta. (cond ((geneqv-refinementp (access rewrite-rule lemma :equiv) geneqv wrld) ; We assume that the meta function has defun-mode :logic. How could it ; be :program if we proved it correct? ; Metafunctions come in two flavors. Vanilla metafunctions take just ; one arg, the term to be rewritten. Extended metafunctions take ; three args. We cons up the args here and use this list of args ; twice below, once to eval the metafunction and once to eval the hyp ; fn. The :rhs of the rewrite-rule is the special flag 'extended ; if we are in the extended case; otherwise, :rhs is nil. We must ; manufacture a context in the former case. (let ((args (cond ((eq (access rewrite-rule lemma :rhs) 'extended) (list term (make metafunction-context :rdepth rdepth :type-alist type-alist :obj obj :geneqv geneqv :wrld wrld :fnstack fnstack :ancestors ancestors :backchain-limit backchain-limit :simplify-clause-pot-lst simplify-clause-pot-lst :rcnst rcnst :gstack gstack :ttree ttree :unify-subst nil) (coerce-state-to-object state))) (t (list term)))) (rune (access rewrite-rule lemma :rune))) (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) flg term ttree) flg (mv-let (erp val latches) (pstk (ev-fncall-meta (access rewrite-rule lemma :lhs) args state)) (declare (ignore latches)) (cond (erp (mv step-limit nil term ttree)) ((equal term val) (mv step-limit nil term ttree)) ((termp val wrld) (let ((hyp-fn (access rewrite-rule lemma :hyps))) (mv-let (erp evaled-hyp latches) (if (eq hyp-fn nil) (mv nil *t* nil) (pstk (ev-fncall-meta hyp-fn args state))) (declare (ignore latches)) (cond (erp (mv step-limit nil term ttree)) ((termp evaled-hyp wrld) (let* ((vars (all-vars term)) (hyps (flatten-ands-in-lit ; Note: The sublis-var below normalizes the explicit constant ; constructors in evaled-hyp, e.g., (cons '1 '2) becomes '(1 . 2). (sublis-var nil evaled-hyp))) (rule-backchain-limit (access rewrite-rule lemma :backchain-limit-lst)) (bad-synp-hyp-msg (bad-synp-hyp-msg hyps vars nil wrld))) (cond (bad-synp-hyp-msg (mv step-limit (er hard 'rewrite-with-lemma "The hypothesis metafunction ~x0, when ~ applied to the input term ~x1, produced ~ a term whose use of synp is illegal ~ because ~@2" hyp-fn term bad-synp-hyp-msg) term ttree)) (t (sl-let (relieve-hyps-ans failure-reason unify-subst ttree) (rewrite-entry (relieve-hyps ; The next argument of relieve-hyps is a rune on which to "blame" a ; possible force. We could blame such a force on a lot of things, but ; we'll blame it on the metarule and the term that it's applied to. rune term hyps (and rule-backchain-limit (assert$ (natp rule-backchain-limit) (make-list (length hyps) :initial-element rule-backchain-limit))) ; The meta function has rewritten term to val and has generated a ; hypothesis called evaled-hyp. Now ignore the metafunction and just ; imagine that we have a rewrite rule (implies evaled-hyp (equiv term ; val)). The unifying substitution just maps the vars of term to ; themselves. There may be additional vars in both evaled-hyp and in ; val. But they are free at the time we do this relieve-hyps. (pairlis$ vars vars) nil ; allp=nil for meta rules ) :obj nil :geneqv nil) ; If relieve hyps succeeds we get back a unifying substitution that extends ; the identity substitution above. This substitution might bind free vars ; in the evaled-hyp. ; Why are we ignoring failure-reason? Do we need to be calling one of the ; brkpt functions? No, because we don't break on meta rules. But perhaps we ; should consider allowing breaks on meta rules. (declare (ignore failure-reason)) (cond (relieve-hyps-ans (sl-let (rewritten-rhs ttree) (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) rewritten-rhs ttree) ; This rewrite of the body is considered a success unless the parent with-acc-p ; fails. t (rewrite-entry (rewrite ; Note: The sublis-var below normalizes the explicit constant ; constructors in val, e.g., (cons '1 '2) becomes '(1 . 2). (sublis-var nil val) ; At one point we ignored the unify-subst constructed above and used a ; nil here. That was unsound if val involved free vars bound by the ; relief of the evaled-hyp. We must rewrite val under the extended ; substitution. Often that is just the identity substitution. unify-subst 'meta)) :conc hyps) (mv step-limit t rewritten-rhs ; Should we be pushing executable counterparts into ttrees when we applying ; metafunctions on behalf of meta rules? NO: We should only do that if the ; meta-rule's use is sensitive to whether or not they're enabled, and it's not ; -- all that matters is if the rule itself is enabled. (push-lemma (geneqv-refinementp (access rewrite-rule lemma :equiv) geneqv wrld) (push-lemma+ rune ttree rcnst ancestors val rewritten-rhs))))) (t (mv step-limit nil term ttree)))))))) (t (mv step-limit (er hard 'rewrite-with-lemma "The hypothesis function ~x0 produced the ~ non-termp ~x1 on the input term ~x2. Our ~ implementation requires that ~x0 produce ~ a term." hyp-fn evaled-hyp term (access rewrite-rule lemma :lhs)) term ttree)))))) (t (mv step-limit (er hard 'rewrite-with-lemma "The metafunction ~x0 produced the non-termp ~ ~x1 on the input term ~x2. The proof of the ~ correctness of ~x0 establishes that the ~ quotations of these two s-expressions have the ~ same value, but our implementation ~ additionally requires that ~x0 produce a term." (access rewrite-rule lemma :lhs) val term) term ttree))))))) (t (mv step-limit nil term ttree)))) ((not (geneqv-refinementp (access rewrite-rule lemma :equiv) geneqv wrld)) (mv step-limit nil term ttree)) ((eq (access rewrite-rule lemma :subclass) 'definition) (sl-let (rewritten-term ttree) (rewrite-entry (rewrite-fncall lemma term)) (mv step-limit (not (equal term rewritten-term)) rewritten-term ttree))) ((and (or (null (access rewrite-rule lemma :hyps)) (not (eq obj t)) (not (equal (access rewrite-rule lemma :rhs) *nil*))) (or (flambdap (ffn-symb term)) ; hence not on fnstack (not (being-openedp (ffn-symb term) fnstack (recursivep (ffn-symb term) wrld))) (not (ffnnamep (ffn-symb term) (access rewrite-rule lemma :rhs))))) (let ((lhs (access rewrite-rule lemma :lhs)) (rune (access rewrite-rule lemma :rune))) (mv-let (unify-ans unify-subst) (one-way-unify-restrictions lhs term (cdr (assoc-equal rune (access rewrite-constant rcnst :restrictions-alist)))) (cond ((and unify-ans (null (brkpt1 lemma term unify-subst type-alist ancestors ttree gstack state))) (cond ((null (loop-stopperp (access rewrite-rule lemma :heuristic-info) unify-subst wrld)) (prog2$ (brkpt2 nil 'loop-stopper unify-subst gstack nil nil rcnst state) (mv step-limit nil term ttree))) (t (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) flg term ttree) flg (sl-let (relieve-hyps-ans failure-reason unify-subst ttree) (rewrite-entry (relieve-hyps rune term (access rewrite-rule lemma :hyps) (access rewrite-rule lemma :backchain-limit-lst) unify-subst (not (oncep (access rewrite-constant rcnst :oncep-override) (access rewrite-rule lemma :match-free) rune (access rewrite-rule lemma :nume)))) :obj nil :geneqv nil) (cond (relieve-hyps-ans (sl-let (rewritten-rhs ttree) (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) rewritten-rhs ttree) ; This rewrite of the body is considered a success unless the parent with-acc-p ; fails. t (rewrite-entry (rewrite (access rewrite-rule lemma :rhs) unify-subst 'rhs)) :conc (access rewrite-rule lemma :hyps)) (prog2$ (brkpt2 t nil unify-subst gstack rewritten-rhs ttree rcnst state) (mv step-limit t rewritten-rhs (push-lemma (geneqv-refinementp (access rewrite-rule lemma :equiv) geneqv wrld) (push-lemma+ rune ttree rcnst ancestors (access rewrite-rule lemma :rhs) rewritten-rhs)))))) (t (prog2$ (brkpt2 nil failure-reason unify-subst gstack nil nil rcnst state) (mv step-limit nil term ttree))))))))) (t (mv step-limit nil term ttree)))))) (t (mv step-limit nil term ttree)))))) (defun rewrite-with-lemmas1 (term lemmas ;;; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Try to rewrite term with the lemmas in lemmas. Return t or nil indicating ; success, the rewritten term, and the final ttree. ; This function is a No-Change Loser modulo rw-cache: only the values of ; 'rw-cache-any-tag and 'rw-cache-nil-tag may differ between the input and ; output ttrees. (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 4 (signed-byte 30) (cond ((null lemmas) (mv step-limit nil term ttree)) ; When we are doing non-linear we will be rewriting linear terms ; under a different theory than the standard one. The :active-theory ; field of the rcnst keeps track of which theory we are using. ((if (eq (access rewrite-constant rcnst :active-theory) :standard) (not (enabled-numep (access rewrite-rule (car lemmas) :nume) (access rewrite-constant rcnst :current-enabled-structure))) (not (enabled-arith-numep (access rewrite-rule (car lemmas) :nume) (global-val 'global-arithmetic-enabled-structure wrld)))) (rewrite-entry (rewrite-with-lemmas1 term (cdr lemmas)))) (t (sl-let (rewrittenp rewritten-term ttree) (rewrite-entry (rewrite-with-lemma term (car lemmas))) (cond (rewrittenp (mv step-limit t rewritten-term ttree)) (t (rewrite-entry (rewrite-with-lemmas1 term (cdr lemmas)))))))))) (defun rewrite-fncall (rule term ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Rule is a :REWRITE rule of subclass DEFINITION or else it is nil. Rule is ; nil iff term is a lambda application. The three values returned by this ; function are the new step-limit, the (possibly) rewritten term, and the new ; ttree. We assume rule is enabled. ; Term is of the form (fn . args). ; Nqthm Discrepancy: In nqthm, the caller of rewrite-fncall, ; rewrite-with-lemmas, would ask whether the result was different from term and ; whether it contained rewriteable calls. If so, it called the rewriter on the ; result. We have changed that here so that rewrite-fncall, in the case that ; it is returning the expanded body, asks about rewriteable calls and possibly ; calls rewrite again. In the implementation below we ask about rewriteable ; calls only for recursively defined fns. The old code asked the question on ; all expansions. It is possible the old code sometimes found a rewriteable ; call of a non-recursive fn in the expansion of that fn's body because of uses ; of that fn in the arguments. So this is a possible difference between ACL2 ; and nqthm, although we have no reason to believe it is significant and we do ; it only for recursive fns simply because the non-recursive case seems ; unlikely. (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (let* ((fn (ffn-symb term)) (args (fargs term)) (body (if (null rule) (or (lambda-body fn) (er hard 'rewrite-fncall "We had thought that a lambda function symbol ~ always has a non-nil lambda-body, but the ~ following lambda does not: ~x0" fn)) (or (access rewrite-rule rule :rhs) "We had thought that a rewrite-rule always has a non-nil ~ :rhs, but the following rewrite rule does not: ~x0"))) (recursivep (and rule ; it's a don't-care if (flambdap fn) (car (access rewrite-rule rule :heuristic-info))))) (cond ((and (not (flambdap fn)) (or (being-openedp fn fnstack recursivep) (fnstack-term-member term fnstack))) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))) ((null rule) ; i.e., (flambdap fn) (cond ((and (not (recursive-fn-on-fnstackp fnstack)) (too-many-ifs-pre-rewrite args (var-counts (lambda-formals fn) body))) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))) (t (sl-let (rewritten-body ttree1) (rewrite-entry (rewrite body (pairlis$ (lambda-formals fn) args) 'lambda-body) :fnstack fnstack) ; Observe that we do not put the lambda-expression onto the fnstack. ; We act just as though we were rewriting a term under a substitution. ; But we do decide on heuristic grounds whether to keep the expansion. ; See the handling of non-recursive functions below for some comments ; relating to the too-many-ifs code. ; Note: If the handling of lambda-applications is altered, consider ; their handling in both rewrite-fncallp (where we take advantage of ; the knowledge that lambda-expressions will not occur in rewritten ; bodies unless the user has explicitly prevented us from opening ; them) and contains-rewriteable-callp. (cond ((and (not (recursive-fn-on-fnstackp fnstack)) (too-many-ifs-post-rewrite args rewritten-body)) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld (accumulate-rw-cache t ttree1 ttree) simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))) (t (mv step-limit rewritten-body ttree1))))))) (t (let* ((new-fnstack (cons (or recursivep fn) fnstack)) (rune (access rewrite-rule rule :rune))) (mv-let (unify-ans unify-subst) (one-way-unify-restrictions (access rewrite-rule rule :lhs) term (cdr (assoc-equal rune (access rewrite-constant rcnst :restrictions-alist)))) (cond ((and unify-ans (null (brkpt1 rule term unify-subst type-alist ancestors ttree gstack state))) (with-accumulated-persistence (access rewrite-rule rule :rune) ((the (signed-byte 30) step-limit) term-out ttree) ; The following mis-guarded use of eq instead of equal implies that we could be ; over-counting successes at the expense of failures. (not (eq term term-out)) (cond ((and (null recursivep) (not (recursive-fn-on-fnstackp fnstack)) (too-many-ifs-pre-rewrite args (access rewrite-rule rule :var-info))) ; We are dealing with a nonrecursive fn. If we are at the top-level of the ; clause but the expanded body has too many IFs in it compared to the number ; in the args, we do not use the expanded body. We know the IFs in ; the args will be clausified out soon and then this will be permitted to ; open. (prog2$ (brkpt2 nil 'too-many-ifs-pre-rewrite unify-subst gstack :rewriten-rhs-avoided ttree rcnst state) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt))))) (t (sl-let (relieve-hyps-ans failure-reason unify-subst ttree1) (cond ((and (eq fn (base-symbol rune)) ; There may be alternative definitions of fn. "The" definition is the one ; whose rune is of the form (:DEFINITION fn); its hyps is nil, at least in the ; standard case; but: #+:non-standard-analysis ; In the non-standard case, we may be attempting to open up a call of a ; function defined by defun-std. Hence, there may be one or more hypotheses. (not (access rewrite-rule rule :hyps))) (mv step-limit t nil unify-subst ttree)) (t (rewrite-entry (relieve-hyps rune term (access rewrite-rule rule :hyps) nil ; backchain-limit-lst unify-subst nil ; allp=nil for definitions ) :obj nil :geneqv nil))) (cond (relieve-hyps-ans (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) term-out ttree) t ; considered a success unless the parent with-acc-p fails (sl-let (rewritten-body new-ttree1) (rewrite-entry (rewrite body unify-subst 'body) :fnstack new-fnstack :ttree ttree1) ; Again, we use ttree1 to accumulate the successful rewrites and we'll ; return it in our answer if we like our answer. (let ((ttree1 (restore-rw-cache-any-tag new-ttree1 ttree1))) (cond ((null recursivep) ; We are dealing with a nonrecursive fn. If we are at the top-level of the ; clause but the expanded body has too many IFs in it compared to the number ; in the args, we do not use the expanded body. We know the IFs in ; the args will be clausified out soon and then this will be permitted to ; open. (cond ((and (not (recursive-fn-on-fnstackp fnstack)) (too-many-ifs-post-rewrite args rewritten-body)) (prog2$ (brkpt2 nil 'too-many-ifs-post-rewrite unify-subst gstack rewritten-body ttree1 rcnst state) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld (accumulate-rw-cache t ttree1 ttree) simplify-clause-pot-lst (access rewrite-constant rcnst :pt))))) (t (prog2$ (brkpt2 t nil unify-subst gstack rewritten-body ttree1 rcnst state) (mv step-limit rewritten-body (push-lemma+ rune ttree1 rcnst ancestors body rewritten-body)))))) ((rewrite-fncallp term rewritten-body (if (cdr recursivep) recursivep nil) (access rewrite-constant rcnst :top-clause) (access rewrite-constant rcnst :current-clause) (cdr (access rewrite-rule rule :heuristic-info))) (cond ; Once upon a time, before we were heavily involved with ACL2 proofs, we had ; the following code here. Roughly speaking this code forced recursive ; functions to open one step at a time if they introduced any IFs. ; ((ffnnamep 'if rewritten-body) ; Nqthm Discrepancy: This clause is new to ACL2. Nqthm always rewrote the ; rewritten body if it contained rewriteable calls. This allows Nqthm to open ; up (member x '(a b c d e)) to a 5-way case split in "one" apparent rewrite. ; In an experiment I have added the proviso above, which avoids rewriting the ; rewritten body if it contains an IF. This effectively slows down the opening ; of member, forcing the whole theorem back through the simplifier on each ; opening. Eventually it will open completely, even under this rule. The ; thought, though, is that often the case splits introduced by such openings ; seems to be irrelevant. Under this new rule, (length (list a b c d e)) will ; expand in one step to '5, but the member expression above will expand more ; slowly because the expansion introduces a case split. An experiment was done ; with Nqthm-1992 in which this change was introduced and examples/basic/ ; proveall.events was replayed without any trouble and with no apparent ; performance change. There are undoubtedly example files where this change ; will slow things down. But it was motivated by an example in which it speeds ; things up by a factor of 10 because the opening is totally irrelevant to the ; proof. The problem -- which was illustrated in the guard proofs for the ; function ascii-code-lst in the nqthm.lisp events -- is that (member x ; *standard-chars*) opens into a 96-way case split in a situation in which it ; could as well have been disabled. This happens more in ACL2 than in Nqthm ; because of the presence of defconsts which permit big constants to be fed ; to recursive functions. It is not clear whether this change is an improvement ; or not. ; (prog2$ ; (brkpt2 t nil unify-subst gstack rewritten-body ; ttree1 rcnst state) ; (mv rewritten-body ; (push-lemma rune ttree1)))) ; With further experience, I've decided it is clear that this change is not an ; improvement! I really like Nqthm's behavior. The example cited above is ; still a problem. In particular, ; (defun ascii-code-lst (lst) ; ; ; This function converts a standard char list into the list of their ; ; ascii codes, terminated by a 0. ; ; (declare (xargs :guard (standard-char-listp lst) ; :hints (("Goal" :in-theory (disable member))) ; :guard-hints (("Goal" :in-theory (disable member))))) ; (if (null lst) ; 0 ; (cons (ascii-code (car lst)) ; (ascii-code-lst (cdr lst))))) ; takes forever unless you give the two disable hints shown above. ((contains-rewriteable-callp fn rewritten-body (if (cdr recursivep) recursivep nil) (access rewrite-constant rcnst :terms-to-be-ignored-by-rewrite)) ; Ok, we are prepared to rewrite the once rewritten body. But beware! There ; is an infinite loop lurking here. It can be broken by using :fnstack ; new-fnstack. While the loop can be broken by using new-fnstack, that ; approach has a bad side-effect: (member x '(a b c)) is not runout. It opens ; to (if (equal x 'a) (member x '(b c))) and because new-fnstack mentions ; member, we don't expand the inner call. See the comment in ; fnstack-term-member for a discussion of loop avoidance (which involved code ; that was here before Version_2.9). (sl-let (rewritten-body ttree2) (rewrite-entry (rewrite rewritten-body nil 'rewritten-body) :fnstack ; See the reference to fnstack in the comment above. (cons (cons :TERM term) fnstack) :ttree ttree1) (let ((ttree2 (restore-rw-cache-any-tag (push-lemma+ rune ttree2 rcnst ancestors body rewritten-body) ttree1))) (prog2$ (brkpt2 t nil unify-subst gstack rewritten-body ttree2 rcnst state) (mv step-limit rewritten-body ttree2))))) (t (prog2$ (brkpt2 t nil unify-subst gstack rewritten-body ttree1 rcnst state) (mv step-limit rewritten-body (push-lemma+ rune ttree1 rcnst ancestors body rewritten-body)))))) (t (prog2$ (brkpt2 nil 'rewrite-fncallp unify-subst gstack rewritten-body ttree1 rcnst state) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld (accumulate-rw-cache t ttree1 ttree) simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))))))) :conc (access rewrite-rule rule :hyps))) (t (prog2$ (brkpt2 nil failure-reason unify-subst gstack nil nil rcnst state) (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld (accumulate-rw-cache t ttree1 ttree) simplify-clause-pot-lst (access rewrite-constant rcnst :pt))))))))))) (t (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))))))))))) (defun rewrite-with-lemmas (term ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) (declare (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) (the-mv 3 (signed-byte 30) (cond ((variablep term) (rewrite-entry (rewrite-solidify-plus term))) ((fquotep term) (mv step-limit term ttree)) ((member-equal (ffn-symb term) (access rewrite-constant rcnst :fns-to-be-ignored-by-rewrite)) (mv step-limit term ttree)) ((flambda-applicationp term) (mv-let (new-term hyp unify-subst rune rcnst) (expand-permission-result term rcnst geneqv wrld) (cond (new-term (assert$ (and (null rune) (null hyp)) (rewrite-entry (rewrite new-term unify-subst 'expansion)))) (t (rewrite-entry (rewrite-fncall nil term)))))) (t (sl-let (rewrittenp rewritten-term ttree) (rewrite-entry (rewrite-with-linear term) :geneqv nil) (cond (rewrittenp (mv step-limit rewritten-term ttree)) (t (sl-let (rewrittenp rewritten-term ttree) (rewrite-entry (rewrite-with-lemmas1 term (getprop (ffn-symb term) 'lemmas nil 'current-acl2-world wrld))) (cond (rewrittenp (mv step-limit rewritten-term ttree)) (t (mv-let (new-term hyp alist rune rcnst) (expand-permission-result term rcnst geneqv wrld) (cond ((and hyp new-term) ; We want to rewrite something like (if hyp new-term term). But hyp and ; new-term are to be understood (and rewritten) in the context of the unifying ; substitution, while term is to be understood in the context of the empty ; substitution. So we lay down code customized to this situation, adapted from ; the definition of rewrite-if. (sl-let (rewritten-test ttree) ; We could save the original ttree to use below when we don't use ; rewritten-test. But this way we record runes that participated even in a ; failed expansion, which could be of use for those who want to use that ; information for constructing a theory in which the proof may replay. (rewrite-entry (rewrite hyp alist 'expansion) :ttree (push-lemma? rune ttree)) (let ((ens (access rewrite-constant rcnst :current-enabled-structure))) (mv-let (must-be-true must-be-false true-type-alist false-type-alist ts-ttree) (assume-true-false rewritten-test nil (ok-to-force rcnst) nil type-alist ens wrld nil nil :fta) (declare (ignore false-type-alist)) (cond (must-be-true (sl-let (rewritten-new-term ttree) (rewrite-entry (rewrite new-term alist 'expansion) :type-alist true-type-alist :ttree (cons-tag-trees ts-ttree ttree)) (mv step-limit rewritten-new-term (push-splitter? rune ttree rcnst ancestors new-term rewritten-new-term)))) (must-be-false (mv step-limit (fcons-term* 'hide term) (push-lemma (fn-rune-nume 'hide nil nil wrld) (cons-tag-trees ts-ttree ttree)))) (t ; We are tempted to bind ttree here to (normalize-rw-any-cache ttree), as we do ; in a similar situation in rewrite-if. But limited experiments suggest that ; we may get better performance without doing so. (sl-let (rewritten-left ttree1) (rewrite-entry (rewrite new-term alist 2) :type-alist true-type-alist :ttree (rw-cache-enter-context ttree)) (mv-let (final-term ttree) (rewrite-if11 (fcons-term* 'if rewritten-test rewritten-left (fcons-term* 'hide term)) type-alist geneqv wrld (push-lemma (fn-rune-nume 'hide nil nil wrld) (rw-cache-exit-context ttree ttree1))) (mv step-limit final-term ; We avoid push-lemma+ just below, because ttree already incorporates a call of ; push-lemma? on rune. (push-splitter? rune ttree rcnst ancestors new-term final-term)))))))))) (new-term (sl-let (final-term ttree) (rewrite-entry (rewrite new-term alist 'expansion) :ttree (push-lemma? rune ttree)) (mv step-limit final-term (push-splitter? rune ttree rcnst ancestors new-term final-term)))) (t (prepend-step-limit 2 (rewrite-solidify term type-alist obj geneqv (access rewrite-constant rcnst :current-enabled-structure) wrld ttree simplify-clause-pot-lst (access rewrite-constant rcnst :pt)))))))))))))))) (defun rewrite-linear-term (term alist ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We desire to rewrite the instantiated conclusion of :LINEAR lemmas ; before adding them to the linear pot. (We also rewrite with this ; function the hypotheses being added while building the initial pot ; list, when the non-linear package is turned on via set-non-linearp.) ; To avoid tail biting we adopted the policy of rewriting just the ; args of the conclusion. It is not known whether this is still ; necessary. ; Historical Plaque from Nqthm: ; However, because all of the literals of the clause being proved are on the ; TYPE-ALIST as false, it is possible -- say when proving an instance of an ; already proved :LINEAR lemma -- to rewrite the conclusion to F! We could ; avoid this by either not putting the linear-like literals on the type alist ; in the first place, or by not rewriting the entire conclusion, just the ; args. We took the latter approach because it was simplest. It does suffer ; from the possibility that the whole (< lhs rhs) of the conclusion might ; rewrite to something else, possibly a better <. ; End of Plaque. ; Note that it is not the case that all of the literals of the clause are on ; type-alist! In rewrite-clause we do not put the current literal on. During ; the computation of the initial pot-lst in setup-simplify-clause-pot-lst, the ; type-alist is nil. ; We return two things, the rewritten term and the new ttree. (declare (ignore obj geneqv) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (mv-let (not-flg atm) (strip-not term) (cond ((and (nvariablep atm) (not (fquotep atm)) (or (eq (ffn-symb atm) '<) (eq (ffn-symb atm) 'equal))) (let ((rcnst1 (if (access rewrite-constant rcnst :nonlinearp) (change rewrite-constant rcnst :active-theory :arithmetic) rcnst))) (sl-let (lhs ttree) (rewrite-entry (rewrite (fargn atm 1) alist 1) :obj '? :geneqv nil ; geneqv equal ; If we have enabled non-linear arithmetic, we change theories here, ; so that we can have a different normal form for polys and linear- and ; non-linear-arithmetic than when rewriting. :rcnst rcnst1) (sl-let (rhs ttree) (rewrite-entry (rewrite (fargn atm 2) alist 2) :obj '? :geneqv nil ; geneqv equal ; We change theories here also. :rcnst rcnst1) (cond (not-flg (mv step-limit (mcons-term* 'not (mcons-term* (ffn-symb atm) lhs rhs)) ttree)) (t (mv step-limit (mcons-term* (ffn-symb atm) lhs rhs) ttree))))))) (t (mv step-limit (sublis-var alist term) ttree)))))) (defun rewrite-linear-term-lst (term-lst ttrees ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We wish to be able to have a different normal form when doing ; linear and non-linear arithmetic than when doing normal rewriting. ; Therefore, before seeding the linear pot with a poly, we rewrite it ; under the theory prevailing in rewrite-linear-term. ; Term-lst is a list of terms as received by add-terms-and-lemmas, and ; ttrees is its corresponding list of tag-trees. We simply call ; rewrite-linear-term (nee rewrite-linear-concl in ACL2 Version_2.6) ; on each member of term-lst and return two lists --- the rewritten ; terms and their ttrees. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (if (null term-lst) (mv step-limit nil nil) (sl-let (term1 ttree1) (rewrite-entry (rewrite-linear-term (car term-lst) nil) :obj nil :geneqv nil :type-alist (cleanse-type-alist type-alist (collect-parents (car ttrees))) :ttree (car ttrees)) (sl-let (term-lst ttree-lst) (rewrite-entry (rewrite-linear-term-lst (cdr term-lst) (cdr ttrees)) :obj nil :geneqv nil :ttree nil) (mv step-limit (cons term1 term-lst) (cons ttree1 ttree-lst))))))) (defun add-linear-lemma (term lemma ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We investigate the application of lemma to term and the ; simplify-clause-pot-lst. If term unifies with the max-term of lemma and we ; can relieve the hypotheses, we add the polynomial produced from lemma's ; conclusion to the pot-lst. We return three values. The second is the ; standard contradictionp. The third is a possibly modified ; simplify-clause-pot-lst. ; PATCH: We use a new field in the linear pots to catch potential loops. ; Loop-stopper-value is initially 0 in all the linear pots. Let value be the ; loop-stopper-value associated with term in simplify-clause-pot-lst. When we ; return a new linear-pot-list, we check to see if there are any new pots. Let ; pot be such a new pot. If the largest var in pot is term order greater than ; term, we set the loop-stopper-value of pot to value + 1. Otherwise, we set ; it to value. ; Now, before we actually add any polys to simplify-clause-pot-lst, we call ; no-new-and-ugly-linear-varsp on the list of polys to be added. This function ; (among other things) checks whether the new vars would have a ; loop-stopper-value which exceeds *max-linear-pot-loop-stopper-value*. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (mv-let (unify-ans unify-subst) (one-way-unify (access linear-lemma lemma :max-term) term) (cond (unify-ans (let ((rune (access linear-lemma lemma :rune))) (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) contradictionp pot-lst) (or contradictionp ; The following mis-guarded use of eq instead of equal implies that we could be ; over-counting successes at the expense of failures. (not (eq pot-lst simplify-clause-pot-lst))) (sl-let (relieve-hyps-ans failure-reason unify-subst ttree1) (rewrite-entry (relieve-hyps rune term (access linear-lemma lemma :hyps) (access linear-lemma lemma :backchain-limit-lst) unify-subst (not (oncep (access rewrite-constant rcnst :oncep-override) (access linear-lemma lemma :match-free) rune (access linear-lemma lemma :nume)))) :obj nil :geneqv nil :ttree nil) (declare (ignore failure-reason)) (cond (relieve-hyps-ans (sl-let (rewritten-concl ttree2) (with-accumulated-persistence rune ((the (signed-byte 30) step-limit) rewritten-concl ttree2) t ; considered a success unless the parent with-acc-p fails (rewrite-entry (rewrite-linear-term (access linear-lemma lemma :concl) unify-subst) :obj nil :geneqv nil :ttree ttree1) :conc (access linear-lemma lemma :hyps)) ; Previous to Version_2.7, we just went ahead and used the result of ; (linearize rewritten-concl ...). This had long been known to be ; problematic (see the comments in linearize1 beginning ``This is a ; strange one.'') but nothing had been done about it. Then Eric Smith ; sent the following example to us and wanted to know what was going ; wrong. ; (defstub bitn (x n) t) ; extract bit n of x ; ; (skip-proofs ; (defthm bitn-non-negative-integer ; (and (integerp (bitn x n)) ; (<= 0 (bitn x n))) ; :rule-classes (:rewrite :type-prescription))) ; ; (skip-proofs ; (defthm bits-upper-bound-linear ; (< (bits x i j) (expt 2 (+ i 1 (- j)))) ; :rule-classes ((:linear :trigger-terms ((bits x i j)))))) ; ; ;goes through (using the two :linear rules above) ; (thm ; (< (+ (BITN x 32) ; (BITN x 58)) ; 2)) ; ; ;the problem rule. ; (skip-proofs ; (defthm bitn-known-not-0-replace-with-1 ; (implies (not (equal (bitn x n) 0)) ; (equal (bitn x n) ; 1)))) ; ; ;same thm; now fails --- the rule above causes linear arithmetic to fail. ; ; (thm ; (< (+ (BITN x 32) ; (BITN x 58)) ; 2)) ; If one uses the following trace and replays the above script, one ; can see what was happening (In a nutshell, ACL2 rewrites the (BITN B ; Z) in the instantiated conclusion of bitn-upper-bound-linear, (<= ; (BITN B Z) 1), to 1 yielding (<= 1 1), which is trivially true but ; not very useful. ; (defun show-type-alist (type-alist) ; (cond ((endp type-alist) nil) ; (t (cons (list (car (car type-alist)) ; (decode-type-set (cadr (car type-alist)))) ; (show-type-alist (cdr type-alist)))))) ; SHOW-TYPE-ALIST ; ACL2(3): (trace (add-polys ; :entry (list (list 'new-polys (show-poly-lst (nth 0 arglist))) ; (list 'pot-lst (show-pot-lst (nth 1 arglist))) ; (list 'type-alist (show-type-alist (nth 3 arglist)))) ; :exit (list (list 'contradictionp (nth 0 values)) ; (list 'new-pot-lst (show-pot-lst (nth 1 values))))) ; (add-linear-lemma ; :entry (list (list 'term (nth 0 arglist)) ; (list 'lemma (nth 1 arglist))) ; :exit (list (list 'contradictionp (nth 0 values)) ; (list 'new-pot-lst (show-pot-lst (nth 1 values))))) ; (rewrite-linear-term ; :entry (list (list 'term (sequential-subst-var-term (nth 1 arglist) ; (nth 0 arglist)))) ; :exit (list (list 'rewritten-term (nth 0 values)) ; (list 'ttree (nth 1 arglist))))) ; (REWRITE-LINEAR-TERM ACL2_*1*_ACL2::REWRITE-LINEAR-TERM ADD-LINEAR-LEMMA ; ACL2_*1*_ACL2::ADD-LINEAR-LEMMA ADD-POLYS ; ACL2_*1*_ACL2::ADD-POLYS) ; The best solution would probably be to not rewrite the instantiated ; trigger term in rewrite-linear-term, but that has its own problems ; and is much more work to implement. By just reverting to the ; un-rewritten concl we catch the ``obvious'' cases such as ; illustrated above. Note that the un-rewritten concl may also ; linearize to nil, but a regression test using the community books ; actually shows a slight improvement in speed (about a ; minute and a half, out of 158 and a half minutes), so we conclude ; that this is not a problem in practice. ; We thank Robert Krug for providing this improvement. (let* ((force-flg (ok-to-force rcnst)) (temp-lst (linearize rewritten-concl t type-alist (access rewrite-constant rcnst :current-enabled-structure) force-flg wrld (push-lemma rune ttree2) state)) (lst (or temp-lst (linearize (sublis-var unify-subst (access linear-lemma lemma :concl)) t type-alist (access rewrite-constant rcnst :current-enabled-structure) force-flg wrld (push-lemma rune (accumulate-rw-cache t ttree2 ttree1)) state)))) (cond ((and (null (cdr lst)) (not (new-and-ugly-linear-varsp (car lst) (<= *max-linear-pot-loop-stopper-value* (loop-stopper-value-of-var term simplify-clause-pot-lst)) term))) (mv-let (contradictionp new-pot-lst) (add-polys (car lst) simplify-clause-pot-lst (access rewrite-constant rcnst :pt) (access rewrite-constant rcnst :nonlinearp) type-alist (access rewrite-constant rcnst :current-enabled-structure) force-flg wrld) (cond (contradictionp (mv step-limit contradictionp nil)) (t (mv step-limit nil (set-loop-stopper-values (new-vars-in-pot-lst new-pot-lst simplify-clause-pot-lst nil) new-pot-lst term (loop-stopper-value-of-var term simplify-clause-pot-lst))))))) (t (mv step-limit nil simplify-clause-pot-lst)))))) (t (mv step-limit nil simplify-clause-pot-lst))))))) (t (mv step-limit nil simplify-clause-pot-lst)))))) (defun add-linear-lemmas (term linear-lemmas ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Linear-lemmas is a list of linear-lemmas. We look for those lemmas ; in linear-lemmas that match term and, if their hyps can be relieved ; and the resulting polys don't contain new and ugly vars, add them to ; the simplify-clause-pot-lst. ; We return two values. The first is the standard contradictionp. ; The second is the possibly new pot-lst. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((null linear-lemmas) (mv step-limit nil simplify-clause-pot-lst)) ((not (enabled-numep (access linear-lemma (car linear-lemmas) :nume) (access rewrite-constant rcnst :current-enabled-structure))) (rewrite-entry (add-linear-lemmas term (cdr linear-lemmas)) :obj nil :geneqv nil :ttree nil)) (t (sl-let (contradictionp new-pot-lst) (rewrite-entry (add-linear-lemma term (car linear-lemmas)) :obj nil :geneqv nil :ttree nil) (cond (contradictionp (mv step-limit contradictionp nil)) (t (rewrite-entry (add-linear-lemmas term (cdr linear-lemmas)) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst)))))))) (defun multiply-alists2 (alist-entry1 alist-entry2 poly ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We are in the middle of multiplying two polys. Poly is the result ; so far. Alist-entry1 is an alist entry from the first poly, and ; alist-entry2 is an alist entry from the second poly. See multiply-alists. ; Here, we perform the actual multiplication of the two alist entries ; and add the result to poly. Note that each entry is of the form ; (term . coeff). (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (let* ((leaves1 (binary-*-leaves (car alist-entry1))) (leaves2 (binary-*-leaves (car alist-entry2))) (leaves (merge-arith-term-order leaves1 leaves2)) (tree (binary-*-tree leaves)) (coeff (* (cdr alist-entry1) (cdr alist-entry2))) (temp-entry (mcons-term* 'BINARY-* (kwote coeff) tree))) (sl-let (new-entry new-ttree) (rewrite-entry (rewrite temp-entry nil 'multiply-alists2) :obj nil :geneqv nil ; We change theories here, so that we can have a different normal form ; for the terms in polys than when rewriting in general. :rcnst (change rewrite-constant rcnst :active-theory :arithmetic) :ttree nil) (let ((new-poly (add-linear-term new-entry 'rhs poly))) (mv step-limit (change poly new-poly :ttree (cons-tag-trees-rw-cache new-ttree (access poly new-poly :ttree)) :parents (marry-parents (collect-parents new-ttree) (access poly new-poly :parents))))))))) (defun multiply-alists1 (alist-entry alist2 poly ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We are in the middle of multiplying two polys. Poly is the result ; so far. Alist-entry is an alist entry from the first poly, and ; alist2 is the alist from the second poly. See multiply-alists. ; Here, we cdr down alist2 multiplying alist-entry by each entry in ; alist2 and adding the result to poly. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (cond ((null alist2) (mv step-limit poly)) (t (sl-let (temp-poly) (rewrite-entry (multiply-alists2 alist-entry (car alist2) poly) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-alists1 alist-entry (cdr alist2) temp-poly) :obj nil :geneqv nil :ttree nil)))))) (defun multiply-alists (alist1 alist2 poly ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We are in the middle of multiplying two polys. Poly is the result ; so far. Initially, it has a partially filled alist and we need to ; finish filling it in. Alist1 is the alist from the first poly, ; and alist2 is the alist from the second poly. ; If one thinks of the initial polys as ; 0 < const1 + alist1 and 0 < const2 + alist2, ; poly initially contains ; 0 < const1*const2 + const1*alist2 + const2*alist1 + () ; and our job is to successively add things to the (). ; In particular, we wish to form alist1*alist2. Here, we cdr down ; alist1 multiplying each entry by alist2 and adding the result to poly. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (cond ((null alist1) (mv step-limit poly)) (t (sl-let (temp-poly) (rewrite-entry (multiply-alists1 (car alist1) alist2 poly) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-alists (cdr alist1) alist2 temp-poly) :obj nil :geneqv nil :ttree nil)))))) (defun multiply-polys1 (alist1 const1 rel1 alist2 const2 rel2 poly ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We are in the middle of multiplying two polys. Poly is the result so far. ; Initially, it has an empty alist which we need to fill in. Alist1 and const1 ; are the alist and constant from the first poly, and alist2 and const2 are ; from the second poly. We assume that at least one of these two polys is ; rational-poly-p. Here we constuct the alist for poly, finishing the process. ; If one thinks of the initial polys as ; 0 < const1 + alist1 and 0 < const2 + alist2, ; poly initially contains 0 < const1*const2 + () and our job is to successively ; add things to the (). We wish to form const1*alist2 + const2*alist1 + ; alist1*alist2. The first two steps are performed by the successive ; multiply-alist-and-consts in the let* below, accumulating their answers ; into the growing alist. We finish with multiply-alists. (declare (ignore obj geneqv ttree rel1 rel2) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. ; Warning: It may be tempting to add the following optimization, as was done up ; through Version_3.3. Don't do it! The optimization is that under suitable ; hypotheses (see optimization code below): when given 0 < a1 + b1x and 0 < a2 ; + b2y, first let a1' = -a1 and b1' = -b1 and then multiply a1' < b1x by a2' < ; b2 y to get a1'a2' < b1b2xy. ; Consider the following example that illustrates the problem with this ; optimization. ; (set-non-linearp t) ; (thm (implies (and (rationalp x) (< 3 x) ; (rationalp y) (< 4 y)) ; (< 0 (+ 12 (* -4 x) (* -3 y) (* x y))))) ; With the optimization shown below, the proof fails, because the hypotheses ; only generate the weaker inequality (< 0 (+ -12 (* x y))). This inequality, ; which we will name In0, is weaker than the thm's conclusion above because ; under the thm's hypotheses, we have (< (* -4 x) -12) and (< (* -3 x) -12), ; and adding these inequalities to the thm's conclusion yields In0. In0 is ; stricly weaker than the thm's conclusion: consider x=13 and y=1, which makes ; In0 true but makes the thm's conclusion false. Of course, that example ; doesn't take into account the hypotheses on x and y above, so the following ; example may be more persuasive. Consider abstracting (* x y) to a new ; variable z, and consider whether the weaker inequality implies the stronger ; -- if so, then we would expect linear arithmetic reasoning to be able to ; derive the stronger from the weaker when necessary. ; (implies (and (rationalp x) (< 3 x) ; (rationalp y) (< 4 y) ; (rationalp z) (< 12 z)) ; (< 0 (+ 12 (* -4 x) (* -3 y) z))) ; But this is not a theorem -- consider x = y = z = 100. ; Here, then, is the optimization code to avoid: ; (if (and (rationalp const1) ; (rationalp const2) ; (< const1 0) ; (< const2 0)) ; (let ((temp-poly (if (and (eq (access poly poly :relation) '<=) ; (or (eq rel1 '<) ; (eq rel2 '<))) ; (change poly poly ; :constant ; (- (access poly poly :constant)) ; :relation ; '<) ; (change poly poly ; :constant ; (- (access poly poly :constant)))))) ; (rewrite-entry ; (multiply-alists alist1 alist2 ; temp-poly) ; :obj nil ; :geneqv nil ; :ttree nil)) ; The following examples from Robert Krug illustrate issues pertaining to the ; above "optimization". First note that the following fails with the ; optimization. We have labeled interesting hypotheses for an explanation ; below. ; (set-non-linearp t) ; (thm ; (implies (and (rationalp i) ; (rationalp n) ; (rationalp r) ; (<= 1 i) ; [1] ; (<= 1 n) ; [2] ; (< r 0) ; [3] ; (< (- i) r)) ; [4] ; (<= 0 (+ r (* i n))))) ; However, if in this formula we change r to a, and/or if we comment out the ; hypothesis (<= 1 i), then we succeed with or without the optimization, ; i.e. in Version_3.3 or beyond. ; First, consider how commenting out [1] can help. ACL2 can add hypotheses [3] ; and [4] about r to generate 0 < i. This can be multiplied by [2] (in the ; form 0 <= -1 + n) to generate an i*n term. This product -- performed without ; the optimization, since 0 < i has a constant of zero -- is 0 < -i + i*n. ; This adds to [4] to yield the conclusion. BUT if [1] is around, it subsumes ; generated inequality 0 < i, and then with the optimization, hypotheses [1] ; and [2] generate 1 <= i*n, and we claim that the conclusion no longer follows ; by linear reasoning. To verify this claim, treat i*n as a variable by ; replacing it with z, and then notice that the following evaluates to NIL: ; (let ((i 10) (n 10) (r -5) (z 1)) ; (implies (and (rationalp i) ; (rationalp n) ; (rationalp r) ; (rationalp z) ; (<= 1 i) ; [1] ; (<= 1 n) ; [2] ; (< r 0) ; [3] ; (< (- i) r) ; [4] ; (<= 1 z)) ; generated, with i*n abstracted ; (<= 0 (+ r z)))) ; Second, consider how changing r to a can help. We have the following. ; (thm ; (implies (and (rationalp i) ; (rationalp n) ; (rationalp a) ; (<= 1 i) ; [1] ; (<= 1 n) ; [2] ; (< a 0) ; [3] ; (< (- i) a)) ; [4] ; (<= 0 (+ a (* i n))))) ; This time, [4] is about i, not r. So in order to obtain an i*n term, we can ; multiply [4] (actually 0 < i + a) by [2] (actually 0 <= -1 + n), and what's ; more, there is no "optimization" since [4] has a constant of 0. ; Multiplication gives us: 0 < -i + i*n - a + a*n. We add this to the negated ; conclusion, 0 < -a - i*n, to obtain 0 < -i - 2*a + a*n. We combine this with ; [4] to obtain 0 < -a + a*n. We then generate an inequality about a*n by ; multiplying [2] and [3] (without optimization, since [3] has a constant of 0) ; to obtain 0 < a - a*n. Adding this to the previous result yields a ; contradiction. (the-mv 2 (signed-byte 30) (let* ((temp-poly1 (if (eql const2 0) poly (multiply-alist-and-const alist1 const2 poly))) (temp-poly2 (if (eql const1 0) temp-poly1 (multiply-alist-and-const alist2 const1 temp-poly1)))) (rewrite-entry (multiply-alists alist1 alist2 temp-poly2) :obj nil :geneqv nil :ttree nil)))) (defun multiply-polys (poly1 poly2 ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We are to multiply the two polys, poly1 and poly2. Roughly speaking this ; function implements the lemma: ; (implies (and (rationalp terms1) ; (< 0 terms1) ; (< 0 terms2)) ; (< 0 (* terms1 terms2))) ; We assume that either poly1 or poly2 is rational-poly-p. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (let ((alist1 (access poly poly1 :alist)) (ttree1 (access poly poly1 :ttree)) (const1 (access poly poly1 :constant)) (rel1 (access poly poly1 :relation)) (parents1 (access poly poly1 :parents)) (ratp1 (access poly poly1 :rational-poly-p)) (alist2 (access poly poly2 :alist)) (ttree2 (access poly poly2 :ttree)) (const2 (access poly poly2 :constant)) (rel2 (access poly poly2 :relation)) (parents2 (access poly poly2 :parents)) (ratp2 (access poly poly2 :rational-poly-p))) (let ((pre-poly (make poly :alist nil :ttree (cons-tag-trees-rw-cache ttree1 ttree2) :parents (marry-parents parents1 parents2) :constant (* const1 const2) :relation (if (and (eq rel1 '<) (eq rel2 '<)) '< '<=) :rational-poly-p (and ratp1 ratp2)))) (sl-let (poly) (rewrite-entry (multiply-polys1 alist1 const1 rel1 alist2 const2 rel2 pre-poly) :obj nil :geneqv nil :ttree nil) (mv step-limit (normalize-poly poly))))))) (defun multiply-pots2 (poly big-poly-list new-poly-list ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Poly is a poly and we are to multiply it by the polys in ; big-poly-list, accumulating the answer into new-poly-list. We ; assume that poly is a rational-poly-p. Every poly in big-poly-list ; is assumed to be a rational-poly-p. ; We return a list of polys. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (cond ((null big-poly-list) (mv step-limit new-poly-list)) ((or (access poly poly :rational-poly-p) (access poly (car big-poly-list) :rational-poly-p)) ; If at least one of poly and (car big-poly-list) are rational, multiplication ; preserves sign. See the comments in multiply-polys. (sl-let (new-poly) (rewrite-entry (multiply-polys poly (car big-poly-list)) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-pots2 poly (cdr big-poly-list) (cons new-poly new-poly-list)) :obj nil :geneqv nil :ttree nil))) (t ; Since neither poly is known to be rational, we skip this multiplication. (rewrite-entry (multiply-pots2 poly (cdr big-poly-list) new-poly-list) :obj nil :geneqv nil :ttree nil))))) (defun multiply-pots1 (poly-list big-poly-list new-poly-list ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Both poly-list and big-poly-list are lists of polys. We are to ; multiply the polys in poly-list by those in big-poly-list. ; New-poly-list is initially nil, and is where we accumulate our ; answer. We assume every element of big-poly-lst is a ; rational-poly-p. ; We return a list of polys. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (cond ((null poly-list) (mv step-limit new-poly-list)) (t (sl-let (new-new-poly-list) (rewrite-entry (multiply-pots2 (car poly-list) big-poly-list new-poly-list) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-pots1 (cdr poly-list) big-poly-list new-new-poly-list) :obj nil :geneqv nil :ttree nil)))))) (defun multiply-pots-super-filter (var-list pot-lst-to-look-in ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; This function is similar to multiply-pots, which see, except that we ; only multiply the bounds polys of the pots labeled by the vars in var-list. ; We return a list of polys. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (cond ((null var-list) (mv step-limit nil)) ((null (cdr var-list)) (mv step-limit (shortest-polys-with-var (car var-list) pot-lst-to-look-in (access rewrite-constant rcnst :pt)))) (t (sl-let (big-poly-list) (rewrite-entry (multiply-pots-super-filter (cdr var-list) pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-pots1 (shortest-polys-with-var (car var-list) pot-lst-to-look-in (access rewrite-constant rcnst :pt)) big-poly-list nil) :obj nil :geneqv nil :ttree nil)))))) (defun multiply-pots-filter (var-list pot-lst-to-look-in ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; This function is similar to multiply-pots except that we assume ; var-list is of length two, and we multiply only some of the polys. ; in particular, we multiply the bounds polys of one pot by the polys ; in the other pot, and vice-versa. ; We return a list of polys. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (sl-let (poly-list1) (rewrite-entry (multiply-pots1 (bounds-polys-with-var (car var-list) pot-lst-to-look-in (access rewrite-constant rcnst :pt)) (polys-with-var (cadr var-list) pot-lst-to-look-in) nil) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-pots1 (bounds-polys-with-var (cadr var-list) pot-lst-to-look-in (access rewrite-constant rcnst :pt)) (polys-with-var (car var-list) pot-lst-to-look-in) poly-list1) :obj nil :geneqv nil :ttree nil)))) (defun multiply-pots (var-list pot-lst-to-look-in ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Var-list is a list of pot-labels in pot-lst-to-look-in. We are ; about to multiply the polys of the labeled pots. We recur down ; var-list and as we unwind the recursion we multiply the polys ; corresponding to the first pot-label in var-list by the result ; of multiplying the polys corresponding to the rest of the pot-labels. ; Multiply-pots1 is responsible for carrying out the actual multiplication. ; We return a list of polys. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 2 (signed-byte 30) (cond ((null var-list) ; How can we multiply 0 things? (mv step-limit nil)) ((null (cdr var-list)) (mv step-limit (polys-with-var (car var-list) pot-lst-to-look-in))) (t (sl-let (big-poly-list) (rewrite-entry (multiply-pots (cdr var-list) pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil) (rewrite-entry (multiply-pots1 (polys-with-var (car var-list) pot-lst-to-look-in) big-poly-list nil) :obj nil :geneqv nil :ttree nil)))))) (defun add-multiplied-polys-filter (var-list products-already-tried pot-lst-to-look-in ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; This function assumes that var-list is of length two. It is similar to ; add-multiply-pots, which see, except that we only multiply some of the polys ; corresponding to the pots labeled by the vars in var-list. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 4 (signed-byte 30) (cond ((product-already-triedp var-list products-already-tried) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) (t (sl-let (poly-list1) (rewrite-entry (multiply-pots-filter var-list pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil) ; By filtering the polys so that we avoid creating new pots, we can ; dramatically speed up proofs, for example the failure of the following. (The ; result is reversed, but no matter.) Robert Krug contributed this ; modification, and expresses the opinoion that the extra consing done by ; polys-with-pots is quite likely less expensive in general than the effort it ; would take to see if any filtering actually occurs, especially since ; filtering probably does occur most of the time. ; (include-book "arithmetic-3/bind-free/top" :dir :system) ; (set-default-hints '((nonlinearp-default-hint stable-under-simplificationp ; hist pspv))) ; (defstub f (x) t) ; (thm ; (implies (and (rationalp (f r)) ; (integerp (f i)) ; (< (f i) 0) ; (not (integerp (* (f r) (/ (f y))))) ; (< (f r) (f y)) ; (< (/ (f r) (f y)) 1) ; (< 0 (f r)) ; (< (+ (f r) (* (f i) (f y))) -1) ; (rationalp (f y)) ; (<= 2 (f y))) ; (< (+ (f r) (* (f i) (f y))) (f i)))) (let ((poly-list2 (polys-with-pots poly-list1 simplify-clause-pot-lst nil))) (mv-let (contradictionp new-pot-lst) (add-polys poly-list2 simplify-clause-pot-lst (access rewrite-constant rcnst :pt) (access rewrite-constant rcnst :nonlinearp) type-alist (access rewrite-constant rcnst :current-enabled-structure) (ok-to-force rcnst) wrld) (mv step-limit contradictionp new-pot-lst (cons (sort-arith-term-order var-list) products-already-tried))))))))) (defun add-multiplied-polys (var-list products-already-tried pot-lst-to-look-in ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Var-list is a list of pot labels. If we have not yet multiplied ; the polys corresponding to those labels, we do so and add them to the ; the simplify-clause-pot-lst. Products-already-tried is a list of the ; factors we have already tried, and pot-lst-to-look-in is the pot-lst ; from which we get our polys. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 4 (signed-byte 30) (cond ((null (cdr var-list)) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) ((product-already-triedp var-list products-already-tried) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) ((or (too-many-polysp var-list pot-lst-to-look-in 1) (< 4 (length var-list))) ; If we went ahead and naively multiplied all the polys corresponding ; to the pot labels in var-list, we would get too many of them and ; be overwhelmed. So, we will only multiply some of the polys. (sl-let (poly-list) (rewrite-entry (multiply-pots-super-filter var-list pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil) (mv-let (contradictionp new-pot-lst) (add-polys poly-list simplify-clause-pot-lst (access rewrite-constant rcnst :pt) (access rewrite-constant rcnst :nonlinearp) type-alist (access rewrite-constant rcnst :current-enabled-structure) (ok-to-force rcnst) wrld) (mv step-limit contradictionp new-pot-lst (cons (sort-arith-term-order var-list) products-already-tried))))) (t (sl-let (poly-list) (rewrite-entry (multiply-pots var-list pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil) (mv-let (contradictionp new-pot-lst) (add-polys poly-list simplify-clause-pot-lst (access rewrite-constant rcnst :pt) (access rewrite-constant rcnst :nonlinearp) type-alist (access rewrite-constant rcnst :current-enabled-structure) (ok-to-force rcnst) wrld) (mv step-limit contradictionp new-pot-lst (cons (sort-arith-term-order var-list) products-already-tried)))))))) (defun deal-with-product1 (part-of-new-var var-list pot-lst-to-look-in pot-lst-to-step-down products-already-tried ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Pot-lst-to-look-in is the pot-lst we keep around to extract polys for ; multiplication from (see non-linear-arithmetic), and pot-lst-to-step-down ; is the pot-lst we cdr down as we recurse through this function. They ; are initially the same. Products-already-tried is an accumulator which ; keeps track of which pots we have already tried multiplying the polys from. ; We are here because we wish to find a set of polys to multiply together. ; Part-of-new-var is an ACL2-term and var-list is a list of pot-labels. ; If part-of-new-var is '1, we have found our set of polys, and we will ; proceed to multiply the polys corresponding to those pot-labels and add ; them to the simplify-clause-pot-lst. Otherwise, we attempt to find ; some pot labels whose product will form part-of-new-var, adding them ; to var-list as we go. ; All the deal-with-xxx functions return four values: a new step-limit, the ; standard contradictionp, a potentially augmented pot-lst (or nil if ; contradictionp is true), and the accumulated list of products we have already ; tried. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 4 (signed-byte 30) (cond ((equal part-of-new-var *1*) (if (null (cdr var-list)) (mv step-limit nil simplify-clause-pot-lst products-already-tried) (rewrite-entry (add-multiplied-polys var-list products-already-tried pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil))) ((null pot-lst-to-step-down) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) (t ; Is the label of the pot we are standing on a factor of part-of-new-var? ; If so, we proceed in two ways --- try using the factor, and try without ; using the factor. (let ((new-part-of-new-var (part-of (access linear-pot (car pot-lst-to-step-down) :var) part-of-new-var))) (cond (new-part-of-new-var (sl-let (contradictionp new-pot-list products-already-tried) (rewrite-entry (deal-with-product1 new-part-of-new-var (cons (access linear-pot (car pot-lst-to-step-down) :var) var-list) pot-lst-to-look-in ; Once upon a time, we used (cdr pot-lst-to-step-down) below. But ; that introduces an asymmetry in handling (* a a) v (* a a a a) when ; one is new and the other is old. For example, if (* a a) is a new ; var and (* a a a a) is an old pot label, deal-with-factor would ; recognize that we could square the former. But if (* a a a a) is ; the new var and (* a a) is the old one -- and we use (cdr ; pot-lst-to-step-down) below -- then deal-with-product would not find ; an opportunity to square (* a a). In particular, it would recognize ; (* a a) as a part of (* a a a a) and generate the subgoal of finding ; polys about (* a a), but it would do so in a shorter pot list in ; which the pot containing (* a a) was now cdr'd past. pot-lst-to-look-in products-already-tried) :obj nil :geneqv nil :ttree nil) (cond (contradictionp (mv step-limit contradictionp nil products-already-tried)) (t (rewrite-entry (deal-with-product1 part-of-new-var var-list pot-lst-to-look-in (cdr pot-lst-to-step-down) products-already-tried) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-list))))) (t (rewrite-entry (deal-with-product1 part-of-new-var var-list pot-lst-to-look-in (cdr pot-lst-to-step-down) products-already-tried) :obj nil :geneqv nil :ttree nil)))))))) (defun deal-with-product (new-var pot-lst-to-look-in pot-lst-to-step-down products-already-tried ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; If new-var is a product, we try to find a set of pots whose labels, ; when multiplied together, form new-var. If we are succesful at ; gathering such a set of pot labels, we will multiply the polys in those ; pots and add them to the simplify-clause-pot-lst. ; All the deal-with-xxx functions return four values: a new step-limit, the ; standard contradictionp, a potentially augmented pot-lst (or nil if ; contradictionp is true), and the accumulated list of products we have already ; tried. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 4 (signed-byte 30) (cond ((eq (fn-symb new-var) 'BINARY-*) (rewrite-entry (deal-with-product1 new-var nil pot-lst-to-look-in pot-lst-to-step-down products-already-tried) :obj nil :geneqv nil :ttree nil)) (t (mv step-limit nil simplify-clause-pot-lst products-already-tried))))) (defun deal-with-factor (new-var pot-lst-to-look-in pot-lst-to-step-down products-already-tried ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Pot-lst-to-look-in is the pot-lst we keep around to extract polys for ; multiplication from (see non-linear-arithmetic), and pot-lst-to-step-down ; is the pot-lst we cdr down as we recurse through this function. They ; are initially the same. Products-already-tried is an accumulator which ; keeps track of which pots we have already tried multiplying the polys from. ; In this function, we cdr down pot-lst-to-step-down to see whether ; new-var is a factor of any of its pot labels. If so, we attempt to ; find a set of other pots (in pot-lst-to-look-in) whose labels are the ; remaining factors of the pot label found in pot-lst-to-step-down. ; If we are succesful at gathering such a set of pot labels, we will ; multiply the polys in those pots and add them to the simplify-clause-pot-lst. ; All the deal-with-xxx functions return four values: a new step-limit, the ; standard contradictionp, a potentially augmented pot-lst (or nil if ; contradictionp is true), and the accumulated list of products we have already ; tried. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 4 (signed-byte 30) (cond ((null pot-lst-to-step-down) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) (t (let ((part-of-pot-var (part-of new-var (access linear-pot (car pot-lst-to-step-down) :var)))) (cond ((and part-of-pot-var (not (equal new-var (access linear-pot (car pot-lst-to-step-down) :var)))) (sl-let (contradictionp new-pot-list products-already-tried) (rewrite-entry (deal-with-product1 part-of-pot-var (list new-var) pot-lst-to-look-in pot-lst-to-look-in products-already-tried) :obj nil :geneqv nil :ttree nil) (cond (contradictionp (mv step-limit contradictionp nil products-already-tried)) (t (rewrite-entry (deal-with-factor new-var pot-lst-to-look-in (cdr pot-lst-to-step-down) products-already-tried) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-list))))) (t (rewrite-entry (deal-with-factor new-var pot-lst-to-look-in (cdr pot-lst-to-step-down) products-already-tried) :obj nil :geneqv nil :ttree nil)))))))) (defun deal-with-division (new-var inverse-var pot-lst-to-look-in pot-lst-to-step-down products-already-tried ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Inverse-var is the multiplicative inverse of new-var, ; pot-lst-to-look-in is the pot-lst we keep around to extract polys ; for multiplication from (see non-linear-arithmetic), and ; pot-lst-to-step-down is the pot-lst we cdr down as we recurse ; through this function. They are initially the same pot ; list. Products-already-tried is an accumulator which keeps track of ; which pots we have already tried multiplying the polys from. ; Division can cause problems. For a simple example, consider: ; p1: 0 < b ; p2: b < a ; and imagine we are trying to prove ; p: 1 < a/b. ; by adding its negation and looking for a contradiction. ; The presence of the /b in the pot will cause inverse-polys to give us ; p3: 0 < 1/b ; But deal-with-factors and deal-with-products will not have a poly ; ``about'' a to multiply p3 by, because a is not the heaviest term in ; any poly. Rather, what we want to do is multiply p3 and p2 since ; b/b = 1. (Note that before we invoke deal-with-division, we insure ; that we have good bounds for b in the pot. This insures that b/b ; disappears without a case split.) ; Another example is that ; p1: 0 < a ; p2: a < b ; imply ; p: 1 < b/a. ; The last will be stored as ; p3: b/a <= 1. ; If we multiply p1 and p3 and cancel we get ; p4: 0 <= a - b ; or ; p4: b <= a ; which contradicts p2. ; So, what we do here is see if there is a pot whose label has inverse-var ; as a factor, and, if so, multiply two sets of polys and add the ; resultant polys to the pot-lst. The two sets of polys we multiply are: ; (1) The bounds polys of new-var with the polys of the found pot, and ; (2) the polys of new-var with the bounds polys of the found pot. ; All the deal-with-xxx functions return four values: a new step-limit, the ; standard contradictionp, a potentially augmented pot-lst (or nil if ; contradictionp is true), and the accumulated list of products we have already ; tried. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 4 (signed-byte 30) (cond ((null pot-lst-to-step-down) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) (t ; The part-of expression asks the question, ``Is inverse-var a factor ; of the first pot label in pot-lst-to-step-down?'' It returns either ; nil, meaning no, or the naive result of dividing the pot label by ; inverse-var. (let ((part-of (part-of inverse-var (access linear-pot (car pot-lst-to-step-down) :var)))) (cond (part-of (sl-let (contradictionp new-pot-lst products-already-tried) (rewrite-entry (add-multiplied-polys-filter (list new-var (access linear-pot (car pot-lst-to-step-down) :var)) products-already-tried pot-lst-to-look-in) :obj nil :geneqv nil :ttree nil) (cond (contradictionp (mv step-limit contradictionp nil nil)) (t (rewrite-entry (deal-with-division new-var inverse-var pot-lst-to-look-in (cdr pot-lst-to-step-down) products-already-tried) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst))))) (t (rewrite-entry (deal-with-division new-var inverse-var pot-lst-to-look-in (cdr pot-lst-to-step-down) products-already-tried) :obj nil :geneqv nil :ttree nil)))))))) (defun non-linear-arithmetic1 (new-vars pot-lst ;;; to look-in/step-down products-already-tried ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; This is the recursive version of function non-linear-arithmetic. See the ; comments and documentation there. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((null new-vars) (mv step-limit nil simplify-clause-pot-lst)) (t (let ((inverted-var (invert-var (car new-vars)))) (sl-let (contradictionp new-pot-lst1 products-already-tried) ; Inverse-var is the multiplicative inverse of var. Within deal-with-division ; we are going multiply var and inverse-var in order to cancel them with ; each other. There are two cases in which this cancellation can occur: ; (1) We know that var and inverse-var are non-zero so their product is ; one. (2) We know that var and inverse var are zero so their product is ; zero. Good-bounds-in-pot determines this for us and allows us to avoid ; case-splits. (if (good-bounds-in-pot inverted-var pot-lst (access rewrite-constant rcnst :pt)) (rewrite-entry (deal-with-division (car new-vars) inverted-var pot-lst ; to-look-in pot-lst ; to-step-down products-already-tried) :obj nil :geneqv nil :ttree nil) (mv step-limit nil simplify-clause-pot-lst products-already-tried)) (cond (contradictionp (mv step-limit contradictionp nil)) (t (sl-let (contradictionp new-pot-lst2 products-already-tried) (rewrite-entry (deal-with-product (car new-vars) pot-lst ; to-look-in pot-lst ; to-step-down products-already-tried) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst1) (cond (contradictionp (mv step-limit contradictionp nil)) (t (sl-let (contradictionp new-pot-lst3 products-already-tried) (rewrite-entry (deal-with-factor (car new-vars) pot-lst ; to-look-in pot-lst ; to-step-down products-already-tried) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst2) (cond (contradictionp (mv step-limit contradictionp nil)) (t (rewrite-entry (non-linear-arithmetic1 (cdr new-vars) pot-lst ; to look-in/step-down products-already-tried) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst3))))))))))))))) (defun non-linear-arithmetic (new-vars pot-lst ;;; to look-in/step-down products-already-tried ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; New-vars is a list of pot labels or factors thereof. We think of it ; as the labels of newly added pots, analogous to new-vars in ; add-polys-and-lemmas1. ; We cdr down the list of new-vars, calling the deal-with-xxx functions ; on the way. The basic idea is that if a new var is a product and we have ; polys about both factors, then we can multiply those polys together to ; form polys about the new var. We are thus using the lemma ; 0 < a & 0 < b -> 0 < a*b (for rational a and b) ; We ``deal with'' new vars of the form a*b, a/b. Analogously, if we ; have a new var of the form a we look to see whether we have an old ; pot about a*b and if so, look for a pot about b, etc. That is, we try ; not to be sensitive to the order in which the pots a, b, and a*b are ; added. ; We do not handle terms like (* a (* a (* a a))) very well. We ; anticipate that such terms will be normalized into expt expressions ; anyway. So handling them here may not be too helpful. ; Unfortunately, we do not handle (expt a i) very well either. We do ; know that (expt a -2) is the inverse of (expt a 2). But we do not ; know that (expt a 2) is a*a or any of the analogous higher-order ; facts. This is an obvious subject for future work. ; Note that we keep around the original pot-lst. We have found this ; heuristic useful to prevent excessive effort on the part of ; non-linear arithmetic. After running a large number of tests, we ; have found that the polys which we wished to multiply were almost ; always present in the original pot-lst and that much time can be ; saved this way. Perhaps in a few more years when computers are even ; faster than they are now (2002) this should be revisited. ; Products-already-tried is an accumulator which keeps track of which pots ; we have already tried multiplying the polys from. ":Doc-Section Miscellaneous Non-linear Arithmetic~/~/ This documentation topic is divided into two parts. We first discuss the practical aspect of how to use the non-linear arithmetic extension to ACL2, and then the theory behind it. We assume that the reader is familiar with the material in ~ilc[linear-arithmetic] and that on ~c[:]~ilc[linear] rules. We begin our discussion of how to use non-linear arithmetic with a simple example. Assume that we wish to prove: ~bv[] (thm (implies (and (rationalp x) (rationalp y) (rationalp z) (< 0 y) (< x (* y z))) (< (floor x y) z))) ~ev[] Note that ~c[(floor x y) <= (/ x y)]. Note also that if we divide both sides of ~c[x < (* y z)] by ~c[y], since ~c[0 < y], we obtain ~c[(/ x y) < z]. By chaining these two inequalities together, we get the inequality we desired to prove. We now proceed with our example session: ~bv[] (skip-proofs (progn ; Since the truth of this theorem depends on the linear properties ; of floor, we will need the linear lemma: (defthm floor-bounds-1 (implies (and (rationalp x) (rationalp y)) (and (< (+ (/ x y) -1) (floor x y)) (<= (floor x y) (/ x y)))) :rule-classes ((:linear :trigger-terms ((floor x y))))) ; We now disable floor, so that the linear lemma will be used. (in-theory (disable floor)) ; We create five rewrite rules which we will use during non-linear ; arithmetic. The necessity for these is due to one of the differences in ; ACL2's behaviour when non-linear arithmetic is turned on. Although ; the conclusions of linear lemmas have always been rewritten before ; they are used, now, when non-linear arithmetic is turned on, the ; conclusions are rewritten under a different theory than under ``normal'' ; rewriting. This theory is also used in other, similar, circumstances ; described below. (defthm |arith (* -1 x)| (equal (* -1 x) (- x))) (defthm |arith (* 1 x)| (equal (* 1 x) (fix x))) (defthm |arith (* x (/ x) y)| (equal (* x (/ x) y) (if (equal (fix x) 0) 0 (fix y)))) (defthm |arith (* y x)| (equal (* y x) (* x y))) (defthm |arith (fix x)| (implies (acl2-numberp x) (equal (fix x) x)))) ) ; End skip-proofs. ; We disable the above rewrite rules from normal use. (in-theory (disable |arith (* -1 x)| |arith (* 1 x)| |arith (* x (/ x) y)| |arith (* y x)| |arith (fix x)|)) ; We create an arithmetic-theory. Note that we must give a quoted ; constant for the theory ~-[] none of the normal ~ilc[theory-functions] ; are applicable to in-arithmetic-theory. (in-arithmetic-theory '(|arith (* -1 x)| |arith (* 1 x)| |arith (* x (/ x) y)| |arith (* y x)| |arith (fix x)|)) ; We turn non-linear arithmetic on. (set-non-linearp t) ; We can now go ahead and prove our theorem. (thm (implies (and (rationalp x) (rationalp y) (rationalp z) (< 0 y) (< x (* y z))) (< (floor x y) z))) ~ev[] The above example illustrates the two practical requirements for using non-linear arithmetic in ACL2. First, one must set up an arithmetic-theory. Usually, one would not set up an arithmetic-theory on one's own but would instead load a library book or books which do so. Second, one must turn the non-linear arithmetic extension on. This one must do explicitly ~-[] no book can do this for you. For a brief discussion of why this is so, even though ~c[(set-non-linearp t)] is an embeddable event, ~pl[acl2-defaults-table] (in particular, the final paragraph). (Note that ~c[(set-non-linearp t)] modifies the ~c[acl2-defaults-table].) Also ~pl[set-non-linearp], ~pl[embedded-event-form], and ~pl[events]. You can also enable non-linear arithmetic with the hint ~c[:nonlinearp t]. ~l[hints]. We, in fact, recommend the use of a hint which will enable nonlinear arithmetic only when the goal has stabilized under rewriting. Using ~ilc[default-hints] can make this easier. ~bv[] (defun nonlinearp-default-hint (stable-under-simplificationp hist pspv) (cond (stable-under-simplificationp (if (not (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp)) '(:computed-hint-replacement t :nonlinearp t) nil)) ((access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp) (if (not (equal (caar hist) 'SETTLED-DOWN-CLAUSE)) '(:computed-hint-replacement t :nonlinearp nil) nil)) (t nil))) (set-default-hints '((nonlinearp-default-hint stable-under-simplificationp hist pspv))) ~ev[] This has proven to be a helpful strategy which allows faster proof times. We now proceed to briefly describe the theory behind the non-linear extension to ACL2. In ~ilc[linear-arithmetic] it was stated that, ``[L]inear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third inequality....'' That is, if ~bv[] 0 < poly1, 0 < poly2, ~ev[] and ~c[c] and ~c[d] are positive rational constants, then ~bv[] 0 < c*poly1 + d*poly2. ~ev[] Similarly, given the above, ~bv[] 0 < poly1*poly2. ~ev[] In the linear arithmetic case, we are taking advantage of the facts that multiplication by a positive rational constant does not change the sign of a polynomial and that the sum of two positive polynomials is itself positive. In the non-linear arithmetic case, we are using the fact that the product of two positive polynomials is itself positive. For example, suppose we have the three assumptions: ~bv[] p1: 3*x*y + 7*a < 4 p2: 3 < 2*x or p2': 0 < -3 + 2*x p3: 1 < y or p3': 0 < -1 + y, ~ev[] and we wish to prove that ~c[a < 0]. As described elsewhere (~pl[linear-arithmetic]), we proceed by assuming the negation of our goal: ~bv[] p4: 0 <= a, ~ev[] and looking for a contradiction. There are no cancellations which can be performed by linear arithmetic in the above situation. (Recall that two polynomials are cancelled against each other only when they have the same largest unknown.) However, ~c[p1] has a product as its largest unknown, and for each of the factors of that product there is a poly that has that factor as a largest unknown. When non-linear arithmetic is enabled, ACL2 will therefore multiply ~c[p1'] and ~c[p2'] obtaining ~bv[] p5: 0 < 3 + -2*x + -3*y + 2*x*y. ~ev[] The addition of this polynomial will allow cancelation to continue and, in this case, we will prove our goal. Thus, just as ACL2 adds two polynomials together when they have the same largest unknown of opposite signs in order to create a new ``smaller'' polynomial; so ACL2 multiplies polynomials together when the product of their largest unknowns is itself the largest unknown of another polynomial. As the use of ~c[:]~ilc[linear] lemmas to further seed the arithmetic database may allow cancellation to proceed, so may the use of non-linear arithmetic. This multiplication of polynomials is the motivation for an arithmetic-theory distinct from than the normal one. Because this may be done so often, and because the individual factors have presumably already been rewritten, it is important that this be done in an efficient way. The use of a small, specialized, theory helps avoid the repeated application of rewrite rules to already stabilized terms.~/" (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((null new-vars) (mv step-limit nil simplify-clause-pot-lst)) (t (let ((gstack (push-gframe 'non-linear-arithmetic nil new-vars)) (rdepth (adjust-rdepth rdepth))) (declare (type (unsigned-byte 29) rdepth)) (rewrite-entry (non-linear-arithmetic1 new-vars pot-lst products-already-tried) :obj nil ; ignored :geneqv nil ; ignored :ttree nil ; ignored )))))) (defun add-polys-and-lemmas2-nl (new-vars old-pot-lst ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; In add-polys-and-lemmas1, it is said that: ; To the simplify-clause-pot-lst, we add lemmas for every var ; in new-vars, generating a new pot-lst. Then if that new pot-lst has ; new vars in it (relative to old-pot-lst) we repeat for those vars. ; We return the standard contradictionp and a new pot-lst. ; This is analogous to add-polys-and-lemmas1, but we also add ; polys gleaned from other sources than add-linear-lemmas, namely ; from the type-alist and ``inverse'' polys (which picks up facts about ; division). (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((null new-vars) (let ((new-vars (expanded-new-vars-in-pot-lst simplify-clause-pot-lst old-pot-lst))) (cond ((null new-vars) (mv step-limit nil simplify-clause-pot-lst)) (t (rewrite-entry (add-polys-and-lemmas2-nl new-vars simplify-clause-pot-lst) :obj nil :geneqv nil :ttree nil))))) (t (mv-let (contradictionp new-pot-lst) (add-polys-from-type-set (car new-vars) simplify-clause-pot-lst type-alist (access rewrite-constant rcnst :pt) (ok-to-force rcnst) (access rewrite-constant rcnst :current-enabled-structure) wrld) (cond (contradictionp (mv step-limit contradictionp nil)) (t (sl-let (contradictionp new-pot-lst) (if (and (nvariablep (car new-vars)) (not (flambda-applicationp (car new-vars))) (not (access rewrite-constant rcnst :cheap-linearp))) (rewrite-entry (add-linear-lemmas (car new-vars) (getprop (ffn-symb (car new-vars)) 'linear-lemmas nil 'current-acl2-world wrld)) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst) (mv step-limit nil new-pot-lst)) (cond (contradictionp (mv step-limit contradictionp nil)) (t (mv-let (contradictionp new-pot-lst) (add-inverse-polys (car new-vars) type-alist wrld new-pot-lst (ok-to-force rcnst) (access rewrite-constant rcnst :current-enabled-structure) (access rewrite-constant rcnst :pt)) (cond (contradictionp (mv step-limit contradictionp nil)) (t (rewrite-entry (add-polys-and-lemmas2-nl (cdr new-vars) old-pot-lst) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst)))))))))))))) (defun add-polys-and-lemmas1-nl (old-pot-lst cnt ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; When doing non-linear arithmetic, we use this function rather than ; the add-polys-and-lemmas1. It is a wrapper for add-polys-and-lemmas2-nl ; which is similar in function to add-polys-and-lemmas1. ; We start by calling add-polys-and-lemmas2-nl with an expanded list of pot ; vars which are new to the simplify-clause-pot-lst (relative to old-pot-lst). ; Add-polys-and-lemmas2-nl augments simplify-clause-pot-lst, creating ; new-pot-lst1. ; We next call non-linear-arithmetic with a list of all the pot vars which are ; new to new-pot-lst1 (relative, again, to old-pot-lst). Non-linear-arithmetic ; augments new-pot-lst1, creating new-pot-lst2. ; Finally, we recursively call ourselves, replacing the ; simplify-clause-pot-lst with new-pot-lst2 and old-pot-lst with new-pot-lst1. ; We thereby avoid calling add-polys-and-lemmas1 with any of the vars which ; it has already seen. ; When we recursively call ourselves we also increment the value of the ; variable cnt, and then check its value upon entry. If it is greater than ; or equal to *non-linear-rounds-value*, we return rather than proceeding. ; This heuristic has proved an easy way to prevent excessive effort in ; non-linear arithmetic. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((<= *non-linear-rounds-value* cnt) (mv step-limit nil simplify-clause-pot-lst)) (t ; Since we are doing non-linear arithmetic, we want to gather information not ; only on the new-vars, but also on the factors of any new-vars which are ; products. Expanded-new-vars-in-pot-lst does this for us. Note that the list ; of new-vars returned by expanded-new-vars-in-pot-lst may include variable ; symbols, unlike the list returned by new-vars-in-pot-lst with ; include-variableps = nil. (let ((new-vars (expanded-new-vars-in-pot-lst simplify-clause-pot-lst old-pot-lst))) (sl-let (contradictionp new-pot-lst1) (cond ((null new-vars) (mv step-limit nil simplify-clause-pot-lst)) ; We used to test for (null new-vars) in the outer cond, and simply return if ; it was true. See also the comment following the call to new-vars-in-pot-lst ; below. (t ; This call to add-polys-and-lemmas2-nl is stronger than a corresponding call ; to add-polys-and-lemmas1, in the sense that it may add additional facts to ; simplify-clause-pot-lst. (rewrite-entry (add-polys-and-lemmas2-nl new-vars old-pot-lst) :obj nil :geneqv nil :ttree nil))) (cond (contradictionp (mv step-limit contradictionp nil)) (t (let ((new-vars (new-vars-in-pot-lst new-pot-lst1 old-pot-lst t))) ; By using include-variableps = t in our call of new-vars-in-pot-lst, and ; moving the test above for (null new-vars) to its present location, we pick up ; theorems such as the following. ; (include-book "arithmetic-3/bind-free/top" :dir :system) ; (set-default-hints '((nonlinearp-default-hint stable-under-simplificationp ; hist pspv))) ; (thm ; (implies (and (rationalp a) ; (rationalp b) ; (rationalp c) ; (< 0 a) ; (< b 0) ; (< 0 (* a c)) ; (< 0 (* b c))) ; (equal c 0)) ; :hints (("Goal" :in-theory (disable |(< 0 (* x y))|)))) (cond ((null new-vars) (mv step-limit nil new-pot-lst1)) (t (sl-let (contradictionp new-pot-lst2) (rewrite-entry (non-linear-arithmetic new-vars new-pot-lst1 nil) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst1) (cond (contradictionp (mv step-limit contradictionp nil)) (t (rewrite-entry (add-polys-and-lemmas1-nl new-pot-lst1 (1+ cnt)) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst2))))))))))))))) (defun add-polys-and-lemmas1 (new-vars old-pot-lst ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; To the simplify-clause-pot-lst, we add lemmas for every var ; in new-vars, generating a new pot-lst. Then if that new pot-lst has ; new vars in it (relative to old-pot-lst) we repeat for those vars. ; We return the standard contradictionp and a new pot-lst. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (cond ((null new-vars) (let ((new-vars (new-vars-in-pot-lst simplify-clause-pot-lst old-pot-lst nil))) (cond ((null new-vars) (mv step-limit nil simplify-clause-pot-lst)) (t (rewrite-entry (add-polys-and-lemmas1 new-vars simplify-clause-pot-lst) :obj nil :geneqv nil :ttree nil))))) (t (sl-let (contradictionp new-pot-lst) (cond ((or (flambda-applicationp (car new-vars)) (access rewrite-constant rcnst :cheap-linearp)) (mv step-limit nil simplify-clause-pot-lst)) (t (rewrite-entry (add-linear-lemmas (car new-vars) (getprop (ffn-symb (car new-vars)) 'linear-lemmas nil 'current-acl2-world wrld)) :obj nil :geneqv nil :ttree nil))) (cond (contradictionp (mv step-limit contradictionp nil)) (t (rewrite-entry (add-polys-and-lemmas1 (cdr new-vars) old-pot-lst) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst)))))))) (defun add-polys-and-lemmas (lst disjunctsp ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We add all the polys in lst to the simplify-clause-pot-lst ; and then add the lemmas triggered by all the new variables. ; We return two results: the standard contradictionp and a new pot-lst. ; Important Observation about Applicative Programming: In nqthm, this ; function was called add-equations-to-pot-lst. Isn't this a better ; name? The advantage to rewriting a megabyte of code applicatively ; is that you get to think of better names for everything! (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (mv-let (contradictionp new-pot-lst) (add-polys lst simplify-clause-pot-lst (access rewrite-constant rcnst :pt) (access rewrite-constant rcnst :nonlinearp) type-alist (access rewrite-constant rcnst :current-enabled-structure) (ok-to-force rcnst) wrld) (cond (contradictionp (mv step-limit contradictionp nil)) ; The defthm below used to fail. This failure was caused by our use of the ; test (and (access rewrite-constant rcnst :nonlinearp) (not disjunctsp)) to ; determine when to use nonlinear arithmetic. This prevented the use of ; nonlinear arithmetic whenever there were disjunctive polys, but this was too ; restrictive. We now use nonlinear arithmetic on disjunct polys that are ; derived from the goal, but not those that arise while backchaining. Some ; type of limitation is needed as we have seen much thrashing in the arithmetic ; procedures when we were too liberal. (Thanks to Robert Krug for providing ; this modification.) ; ; This example was supplied by Julien Schmaltz. ; ; (include-book "arithmetic-3/bind-free/top" :dir :system) ; (include-book "arithmetic-3/floor-mod/floor-mod" :dir :system) ; (set-non-linearp t) ; (defthm foo ; (implies (and (integerp a) (integerp b) ; (< 0 a) (< 0 b) ; (equal (len l) (* a b))) ; (equal (floor (len l) a) ; b)) ; :hints (("GOAL" ; :do-not '(eliminate-destructors generalize fertilize) ; :do-not-induct t)) ; :rule-classes nil) ; We can get here by two routes. We could have been called by ; add-terms-and-lemmas or add-disjunct-polys-and-lemmas. In the ; latter case we are "speculatively" trying to get a contradiction ; from one disjunct so we can simplify things to the other disjunct. ; But non-linear is very expensive. We choose not to try it in this ; "speculative" case during backchaining even if non-linear is ; otherwise enabled. ((and (access rewrite-constant rcnst :nonlinearp) (or (not disjunctsp) (null ancestors))) (rewrite-entry (add-polys-and-lemmas1-nl simplify-clause-pot-lst 0) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst)) (t (rewrite-entry (add-polys-and-lemmas1 (new-vars-in-pot-lst new-pot-lst simplify-clause-pot-lst nil) new-pot-lst) :obj nil :geneqv nil :ttree nil :simplify-clause-pot-lst new-pot-lst)))))) (defun add-disjunct-polys-and-lemmas (lst1 lst2 ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; We try to construct a pot-lst from the simplify-clause-pot-lst ; by assuming the disjunction of the polys in lst1 and lst2. But since ; pot lists can only represent conjunctions, we are forced to take a weak ; approach: we can assume lst1 if the assumption of lst2 produces a ; contradiction and vice versa. If both are contradictory, we return ; the standard contradiction result. Otherwise we return a (possibly) new ; pot-lst. ; The hard part of this procedure is keeping track of dependencies. ; If lst1 is contradictory, we must infect lst2 with the ttree of the ; contradiction, since the assumption of lst2 is dependent upon the ; proof that lst1 is contradictory. We must do the symmetric thing if ; lst2 proves to be contradictory. But here we are in an efficiency ; bind. We have already created the assumption of ; simplify-clause-pot-lst and lst1 and do not want to re-create it ; after infecting lst1 with the ttree from the refutation of lst2. So ; we visit the modified pot-lst after the fact, if lst2 is contradictory, ; and add the appropriate ttree. ; Historical Note: In nqthm we handled this problem by infecting the ; polys of lst1 with a special mark (a fresh cons) in the lemmas field ; of the poly before we added them to te pot-lst. If lst2 gave a ; contradiction, we scanned the pot-lst produced by lst1 looking for ; all polys containing that (eq) cons. During the initial attempts to ; code linear applicatively we tried to mimic this by using a 'mark ; tag in the tag-tree and inventing a "new" mark, such as an integer ; that was associated with the simplify-clause-pot-lst and was ; increased here when we obtained the mark. We could not find a ; convincing way to generate a new mark. The problem is due to the ; recursive rewriting done to add :LINEAR lemmas. How do we know a ; mark generated now will still be new when it needs to be? How do we ; know that a term rewritten in an extension of this pot-lst under us, ; doesn't have some marks in its tag-tree that will come back to haunt ; us? These questions may have cut and dried answers that make marks ; viable. But we decided not to pursue them and just identify the new ; polys as done here. This exercise does point to the convenience of ; being able to use cons to generate a unique object. (declare (ignore obj geneqv ttree) (type (unsigned-byte 29) rdepth) (type (signed-byte 30) step-limit)) ; Convention: It is our convention to pass nils into ignored &extra formals. ; Do not change the (ignore ...) declaration above without looking at the ; callers. That is, if you change this function so that it uses the formals ; declared ignored above, you are making a mistake because all callers of this ; function pass nils into them. (the-mv 3 (signed-byte 30) (sl-let (contradictionp new-pot-lst1) (rewrite-entry (add-polys-and-lemmas lst1 t) :obj nil :geneqv nil :ttree nil) (cond (contradictionp ; So the first disjunct, lst1, has led to a contradiction. We will ; infect the polys in lst2 with the ttree of that contradiction and ; and add them to the original pot list. (rewrite-entry (add-polys-and-lemmas (infect-polys lst2 (access poly contradictionp :ttree) (collect-parents (access poly contradictionp :ttree))) t) :obj nil :geneqv nil :ttree nil)) (t ; The first disjunct did not lead to a contradiction. Perhaps the ; second one will... (sl-let (contradictionp new-pot-lst2) (rewrite-entry (add-polys-and-lemmas lst2 t) :obj nil :geneqv nil :ttree nil) (declare (ignore new-pot-lst2)) (cond (contradictionp ; So the second disjunct, lst2, has led to a contradiction and we may ; use new-pot-lst1, the result of assuming lst1, as the result of ; assuming their disjunction. But we must infect, with the ttree from ; the contradiction, all the polys in new-pot-lst1 derived from lst1. ; That set is just all the polys in new-pot-lst1 that are not in ; simplify-clause-pot-lst. (mv step-limit nil (infect-new-polys new-pot-lst1 simplify-clause-pot-lst (access poly contradictionp :ttree)))) (t (mv step-limit nil simplify-clause-pot-lst))))))))) (defun add-disjuncts-polys-and-lemmas (split-lst to-do-later pot-lst0 ; &extra formals rdepth step-limit type-alist obj geneqv wrld state fnstack ancestors backchain-limit simplify-clause-pot-lst rcnst gstack ttree) ; Each element of split-lst is a doublet, (lst1 lst2). Logically, we wish to ; conjoin to the simplify-clause-pot-lst the conjunction across split-lst of ; the disjunctions of each lst1 and lst2. I.e., we wish to assume (and ... (or ; lst1 lst2) ...) and we wish to express this assumption as a pot-lst. No way ; Jose. Pot-lsts represent conjunctions of assumptions. So instead we'll ; conjoin lst1 into the pot list and lst2 into the pot list and hope one or the ; other gives a contradiction. If not, we'll just discard that doublet and try ; the others. But if one gives a contradiction, then we can go with the ; assumption of the other as the assumption of their disjunction. There is a ; subtlety here however: the assumption of lst2 in place of (or lst1 lst2) ; depends upon the refutation of lst1 and hence we must infect the polys from ; lst2 with the ttree arising from the refutation of lst1. And vice versa. ; See add-disjunct-polys-and-lemma. ; We return two values, the standard contradictionp, and a new pot-lst. ; The to-do-later list was first present in Version 1.6, and represents an ; attempt to make the order of the split-lst irrelevant. The idea is that if a ; doublet in the split-lst must be "discarded" as noted above, then we actually ; save that doublet on to-do-later and try it again after processing the ; others. Here is a long message that explains the problem; the message was ; sent to Bishop Brock, who first reported the problem, on March 31, 1994, ; I have fixed the "bug" that prevented us from proving ; (thm ; (IMPLIES ; (AND (INTEGERP N) ; (NOT (< N 0)) ; (NOT (< 4 N)) ; (NOT (EQUAL N 2)) ; (NOT (EQUAL N 0)) ; (NOT (EQUAL N 1)) ; (NOT (EQUAL N 3))) ; (EQUAL N 4))) ; To understand what I did, consider a proof that works, e.g., ; (IMPLIES (AND (INTEGERP N) ; (NOT (< N 0)) ; (NOT (< 4 N)) ; (NOT (EQUAL N 0)) ; (NOT (EQUAL N 1)) ; (NOT (EQUAL N 2)) ; (NOT (EQUAL N 3))) ; (EQUAL N 4)) ; The arithmetic hyps are stored in the linear inequalities database by the ; linear arithmetic package. That database represents a conjunction of ; inequalities. The first two inequalities give us ; 0 <= N <= 4 ; Now we come to the hard part. In general, we cannot represent (NOT (EQUAL x ; y)) as a conjunction of inequalities. It turns into a DISjunction, namely, ; either x < y or y < x. Thus, if we are asked to add (NOT (EQUAL x y)) to the ; linear database we try adding x < y. If that gives us a contradiction, then ; we know y < x and we add that. Alternatively, if x < y doesn't give us a ; contradiction, but y < x does, we can assume x < y. If neither gives us a ; contradiction, we simply can't represent (NOT (EQUAL x y)) in the linear ; database. Note that to get any linear information out of (NOT (EQUAL x y)) ; we must get a contradiction from one of the two disjuncts. ; When you process the hypotheses in the "wrong" order, you don't always get a ; contradiction and so we effectively drop one or more of the inequalities and ; lose. ; Consider one of the many "right" orders first, in particular the proof that ; works above. The first NOT EQUAL we process is (NOT (EQUAL N 0)). Because N ; is an integer, this is equivalent to either N <= -1 or 1 <= N. The linear ; database we have initially is ; 0 <= N <= 4. ; When we add N <= -1 we get a contradiction, by clashing 0 <= N with N <= -1 ; and deriving 0 <= -1. Since we got a contradiction on one disjunct we can ; assume the other. Adding 1 <= N to the above database gives us ; 1 <= N <= 4. ; Note that we are now in a position to successfully process (NOT (EQUAL N 1)), ; because it becomes either N <= 0 (contradiction) or 2 <= N, and thus we get ; 2 <= N <= 4. ; As you can see, we can keep narrowing the known interval as long as the hyp ; we process is beyond the current known endpoints. We can work at either ; endpoint and so there are many "right" orders. (In the case of the 5-way ; case split on N=0,1,2,3,4, there are 90 right orders and 30 wrong ones out of ; the 120 permutations.) ; Now consider one of the "wrong" orders. If we know ; 0 <= N <= 4 ; and we first process (NOT (EQUAL N 1)) then we must get a contradiction from ; either N <= 0 or from 2 . (in-package "ACL2") (defdoc serialize ":Doc-Section serialize routines for saving ACL2 objects to files, and later restoring them~/ This documentation topic relates to an experimental extension of ACL2 that supports ~ilc[hons], memoization, and fast alists. ~l[hons-and-memoization]. Thanks to Jared Davis for contributing the ``serialization'' routines for saving ACL2 objects in files for later loading. We implement some routines for writing arbitrary ACL2 objects to files, and for loading those files later. We usually call these \".sao\" files, which stands for (S)erialized (A)CL2 (O)bject. Our serialization scheme uses a compact, binary format that preserves structure sharing in the original object. We optimize for read performance.~/~/") (defmacro serialize-write (filename obj &key verbosep) ":Doc-Section serialize write an ACL2 object into a file~/ General form: ~bv[] (serialize-write filename obj [:verbosep {t, nil}]) ; nil by default --> state ~ev[] In the logic this carries out an oracle read. Under the hood, we try to save ~c[obj] into the file indicated by ~c[filename], which must be a string. The object can later be recovered with ~ilc[serialize-read]. We just return ~c[state], and any failures (e.g., file not openable) will result in a hard Lisp error. Writing objects to disk is generally slower than reading them back in since some analysis is required to convert an object into our ~il[serialize]d object format. The ~c[verbosep] flag just says whether to print some low-level details related to timing and memory usage as the file is being read.~/~/" `(serialize-write-fn ,filename ,obj ,verbosep state)) (defun serialize-write-fn (filename obj verbosep state) (declare (xargs :guard (and (stringp filename) (booleanp verbosep) (state-p state)) :stobjs state) (ignorable filename obj verbosep)) #-acl2-loop-only (cond ((live-state-p state) #-hons (er hard? 'serialize-write-fn "Serialization routines are currently only available in the HONS ~ version of ACL2.") #+hons (with-open-file (stream filename :direction :output :if-exists :supersede) (let* ((*ser-verbose* verbosep)) (ser-encode-to-stream obj stream))) (return-from serialize-write-fn state)) (t ; We fall through to the logic code if we are doing a proof, where ; *hard-error-returns-nilp* is true. Otherwise, we throw here with an error ; message. (er hard? 'serialize-write-fn "Serialization requires a live state."))) (mv-let (erp val state) (read-acl2-oracle state) (declare (ignore erp val)) state)) (defmacro serialize-read (filename &key (hons-mode ':smart) verbosep) ":Doc-Section serialize read a serialized ACL2 object from a file~/ General form: ~bv[] (serialize-read filename [:hons-mode {:always, :never, :smart}] ; :smart by default [:verbosep {t, nil}]) ; nil by default --> (mv obj state) ~ev[] In the logic this is an oracle read. Under the hood, we try to read and return a serialized object from a file that was presumably created by ~ilc[serialize-write]. On success we return the contents of the file. Any failures (e.g., file not found, bad file contents, etc.) will result in a hard Lisp error. The ~c[filename] should be a string that gives the path to the file. The ~c[hons-mode] controls how whether to use ~ilc[hons] or ~ilc[cons] to restore the object. The default mode is ~c[:smart], which means that conses that were ~il[normed] at the time of the file's creation should be restored with ~c[hons]. But you can override this and insist that ~c[hons] is to ~c[:always] or ~c[:never] be used, instead. Why would you use ~c[:never]? If your object previously had a lot of honses, but you no longer have any need for them to be normed, then using ~c[:never] may sometimes be a lot faster since it can avoid ~c[hons] calls. On the other hand, if you are going to ~ilc[hons-copy] some part of the file's contents, then it is likely faster to use ~c[:smart] or ~c[:always] instead of first creating normal conses and then copying them to build honses. The ~c[:verbosep] flag just controls whether to print some low-level details related to timing and memory usage as the file is being read.~/~/" `(serialize-read-fn ,filename ,hons-mode ,verbosep state)) (defun serialize-read-fn (filename hons-mode verbosep state) (declare (xargs :guard (and (stringp filename) (member hons-mode '(:never :always :smart)) (booleanp verbosep) (state-p state)) :stobjs state) (ignorable filename hons-mode verbosep)) #-acl2-loop-only (cond ((live-state-p state) (return-from serialize-read-fn #-hons (progn (er hard? 'serialize-read-fn "Serialization routines are currently only available in the ~ HONS version of ACL2.") (mv nil state)) #+hons (with-open-file (stream filename :direction :input) (let* ((*ser-verbose* verbosep) (val (ser-decode-from-stream t hons-mode stream))) (mv val state))))) (t ; We fall through to the logic code if we are doing a proof, where ; *hard-error-returns-nilp* is true. Otherwise, we throw here with an error ; message. (er hard? 'serialize-read-fn "Serialization requires a live state."))) (mv-let (erp val state) (read-acl2-oracle state) (declare (ignore erp)) (mv val state))) (defdoc serialize-alternatives ":Doc-Section serialize alternatives to the ~il[serialize] routines~/ ~il[Hons] users could previously use the routines ~c[compact-print-file] and ~c[compact-read-file]. These are deprecated and are no longer built into ACL2. However, they are still available by loading the new community book, ~c[serialize/compact-print]. Note that loading this book requires a ttag, and these routines are still only available in raw lisp. Another predecessor of the serialization routines were hons archives, which are still available in the ~c[hons-archive] library. The serialization routines are generally better and we recommend against using hons archives for new projects.~/~/") (defdoc serialize-in-books ":Doc-Section serialize using serialization efficiently in books~/ Our serialize scheme was developed in order to allow very large ACL2 objects to be loaded into books. Ordinarily this is carried out using ~ilc[serialize-read] within a ~ilc[make-event], e.g., ~bv[] (make-event (mv-let (obj state) (serialize-read \"my-file\") (value `(defconst *my-file* ',obj)))) ~ev[] But this scheme is not particularly efficient. During ~ilc[certify-book], the actual call of ~c[serialize-read] is carried out, and this is typically pretty fast. But then a number of possibly inefficient things occur.~bq[] - The ACL2 function ~c[bad-lisp-object] is run on the resulting object. This is memoized for efficiency, but may still take considerable time when the file is very large. - The checksum of the resulting object is computed. This is also memoized, but as before may still take some time. - The object that was just read is then written into book.cert, essentially with ~ilc[serialize-write]. This can take some time, and results in large certifiate files.~eq[] Then, during ~il[include-book], the ~c[make-event] expansion of is loaded. This is now basically just a ~c[serialize-read]. The moral of the story is that using serialize will only help your ~c[certify-book] time, and it only impacts a portion of the overall time. To avoid this overhead, we have developed an UNSOUND alternative to ~c[serialize-read], which is available only by loading an additional book. So, if the above scheme is not performing well for you, you may wish to see the community book ~c[serialize/unsound-read].~/~/") (defmacro with-serialize-character (val form) (declare (xargs :guard (member val '(nil #\Y #\Z)))) ":Doc-Section serialize control output mode for ~c[print-object$]~/ This documentation topic relates to an experimental extension of ACL2 that supports ~ilc[hons], memoization, and fast alists. ~l[hons-and-memoization]. ~l[serialize] for a discussion of ``serialization'' routines, contributed by Jared Davis for saving ACL2 objects in files for later loading. The expression ~c[(with-serialize-character char form)] evaluates to the value of ~c[form], but with the serialize-character of the ~ilc[state] assigned to ~c[char], which should be one of ~c[nil], ~c[#\\Y], or ~c[#\\Z]. We describe the effect of that assignment below. But note that if you are doing this because of one or more specific calls of ~c[print-object$], such as ~c[(print-object$ x channel state)], then you may wish instead to evaluate ~c[(print-object$-ser x serialize-character channel state)], in which case you will not need to use ~c[with-serialize-character]. (Note however that ~c[serialize-character] is treated as ~c[nil] for other than a HONS version.) ~bv[] General forms: (with-serialize-character nil form) (with-serialize-character #\Y form) (with-serialize-character #\Z form) ~ev[] where ~c[form] should evaluate to an error triple (~pl[error-triples]). Note that if you prefer to obtain the same behavior (as described below) globally, rather than only within the scope of ~c[with-serialize-character], then use ~c[set-serialize-character] in a corresponding manner: ~bv[] (set-serialize-character nil state) (set-serialize-character #\Y state) (set-serialize-character #\Z state) ~ev[] In each case above, calls of ~c[print-object$] (~pl[io]) in ~c[form] will produce an object that can be read by the HONS version of ACL2. In the first case, that object is printed as one might expect at the terminal, as an ordinary Lisp s-expression. But in the other cases, the object is printed by first laying down either ~c[#Y] or ~c[#Z] (as indicated) and then calling ~ilc[serialize-write] (or more precisely, the underlying function called by ~c[serialize-write] that prints to a stream). Consider what happens when the ACL2 reader encounters an object produced as described above (in the ~c[#Y] or ~c[#Z] case). When the object was written, information was recorded on whether that object was a ~il[hons]. In the case of ~c[#Z], the object will be read as a hons if and only if it was originally written as a hons. But in the case of ~c[#Y], it will never be read as a hons. Thus, ~c[#Y] and ~c[#Z] will behave the same if the original written object was not a hons, creating an object that is not a hons. For an equivalent explanation and a bit more discussion, ~pl[serialize-read], in particular the discussion of the hons-mode. The value ~c[:smart] described there corresponds to ~c[#Z], while ~c[:never] corresponds to ~c[#Y].~/~/" `(state-global-let* ((serialize-character ,val set-serialize-character)) ,form)) acl2-sources/serialize-raw.lisp0000666002132200015000000017730712222115527016242 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Regarding authorship of ACL2 in general: ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; serialize-raw.lisp -- a scheme for serializing ACL2 objects to disk. ; This file was developed and contributed by Jared Davis on behalf of ; Centaur Technology. ; Note: this file is currently only included as part of ACL2(h). But ; it is independent of the remainder of the Hons extension and might ; some day become part of ordinary ACL2. ; Please direct correspondence about this file to Jared Davis ; . (in-package "ACL2") ; INTRODUCTION ; ; We now develop a serialization scheme that allows ACL2 objects to be saved to ; disk using a compact, structure-shared, binary encoding. ; ; We configure ACL2's print-object$ function so that it writes objects with our ; encoding scheme when writing certificate files. This allows large objects ; produced by make-event to be written efficiently. ; ; We extend the ACL2 readtable so that serialized objects can be read at any ; time, using the extended reader macros #z[...] and #Z[...]. These macros are ; almost identical, but ; ; #z rebuilds the object entirely with CONS and does not restore any ; fast alists, whereas ; ; #Z uses HONS for the parts of the structure that were originally normed, ; and rebuilds the hash tables for fast alists. ; ; We provide routines for reading and writing ACL2 objects as individual files, ; typically with a ".sao" extension for "Serialized ACL2 Object". For ; bootstrapping reasons, these are introduced in hons.lisp and hons-raw.lisp ; instead of here in serialize-raw.lisp. ; ESSAY ON BAD OBJECTS AND SERIALIZE ; ; When we decode serialized objects, must we ensure that the object returned is ; a valid ACL2 object, i.e., not something that BAD-LISP-OBJECTP would reject? ; ; Matt and Jared think the answer is "no" for the reader macros. Why? These ; macros are just extensions of certain readtables like the *acl2-readtable*, ; which are used by READ to interpret input. But there are already any number ; of other ways for READ to produce bad objects, for instance it might produce a ; floating point numbers, symbols in a foreign package, vectors, structures, ; etc. The "Remarks on *acl2-readtable*" in acl2.lisp has more discussion of ; these matters. At any rate, whenever ACL2 is using READ, it already needs to ; be defending against bad objects, so it should be okay if the serialize reader ; macros generate bad objects. ; ; However, Jared thinks that the serialize-read-fn function *is* responsible for ; ensuring that only good objects are read, because it is a "new" way for input ; to enter the system. ; ; Well, the serialized object format is considerably more restrictive than the ; Common Lisp reader, and does not provide any way to encode floats, circular ; objects, etc. Jared thinks the only bad objects that can be produced from ; serialized reading are symbols in unknown packages. So, in ; serialize-read-file we pass in a flag that makes sure we check whether ; packages are known. We think this is sufficient to justify not checking ; BAD-LISP-OBJECTP explicitly. ; ESSAY ON THE SERIALIZE OBJECT FORMAT ; ; Our scheme involves encoding ACL2 objects into a fairly simple, byte-based, ; binary format. ; ; There are actually three different formats of serialized objects, named V1, ; V2 and V3. For compatibility with previously written files, we support ; reading from all three file formats. But we always write files using the ; newest, V3 format. ; ; Why these different versions? ; ; V1. We originally developed our serialization scheme as ttag-based ; library in :dir :system, but this left us with no way to tightly ; integrate it into ACL2's certificate printing/reading routines. ; ; V2. When we moved serialization into ACL2 proper, we noticed a few things ; that we could improve, and tweaked the serialization scheme. The new ; scheme, V2, wasn't compatible with community book books/serialize, but ; we already had many files written in the old V1 format. ; ; V3. Later, we realized that it would be easy to restore fast alists within ; serialized objects, and that this would allow the fast alists defined ; in a book to still be fast after including the book. The new format, ; V3, added this feature, but again we had lots of V2 files that we ; still wanted to be able to read. ; ; Eventually we should be able to drop support for the old versions, but the ; formats are all very similar so supporting them is not that much work. Since ; all of the versions are very similar, we begin with the V1 format: ; ; OBJECT ::= MAGIC ; marker for sanity checking ; LEN ; total number of objects ; NATS RATS COMPLEXES CHARS ; object data ; STRS SYMBOLS CONSES ; ; MAGIC ; marker for sanity checking ; ; NATS ::= LEN NAT* ; number of nats, followed by that many nats ; RATS ::= LEN RAT* ; number of rats, followed by that many rats ; COMPLEXES ::= LEN COMPLEX* ; etc. ; CHARS ::= LEN CHAR* ; STRS ::= LEN STR* ; PACKAGES ::= LEN PACKAGE* ; CONSES ::= LEN CONS* ; ; RAT ::= NAT NAT NAT ; sign (0 or 1), numerator, denominator ; COMPLEX ::= RAT RAT ; real, imaginary parts ; CHAR ::= byte ; the character code for this character ; STR ::= LEN CHAR* ; length and then its characters ; PACKAGE ::= STR LEN STR* ; package name, number of symbols, symbol names ; CONS ::= NAT NAT ; "index" of its car and cdr (see below) ; ; LEN ::= NAT ; just to show when we're referring to a length ; ; MAGIC ::= #xAC120BC7 ; also see the discussion below ; ; NAT ::= [see below] ; ; ; Magic Numbers. The magic number, #xAC120BC7, is a 32-bit integer that sort ; of looks like "ACL2 OBCT", i.e., "ACL2 Object." This use of magic numbers is ; probably silly, but may have some advantages: ; ; (1) It lets us do a basic sanity check. ; ; (2) When serialize is used to write out whole files, helps to ensure the ; file doesn't start with valid ASCII characters. This *might* help ; protect these files from tampering by programs that convert newline ; characters in text files (e.g., FTP programs). ; ; (3) It gives us the option of tweaking our encoding. Today we use distinct ; magic numbers to identify V1, V2, and V3 files, and in the future we ; could add additional encodings by adding other magic numbers. ; ; ; Naturals. Our representation of NAT is slightly involved since we need to ; support arbitrary-sized natural numbers. We use a variable-length encoding ; where the most significant bit of each byte is 0 if this is the last piece of ; the number, or 1 if there are additional bytes, and the other 7 bits are data ; bits. The bytes are kept in little-endian order. For example: ; ; 0 is encoded as #x00 (continue bit: 0, data: 0) ; 2 is encoded as #x02 (continue bit: 0, data: 2) ; ... ; 127 is encoded as #x7F (continue bit: 0, data: 127) ; 128 is encoded as #x80 #x01 [(continue bit: 1, data: 1) = 1] + 127*[(c: 0, d: 1) = 1] ; 129 is encoded as #x81 #x01 [(continue bit: 1, data: 2) = 2] + 127*[(c: 0, d: 1) = 1] ; ... ; ; ; Negative Integers. Negative integers aren't mentioned in the file format ; because we encode them as rationals with denominator 1. This only requires 2 ; bytes of overhead (for the sign and denominator) beyond the magnitude of the ; integer, which seems acceptable since negative integers aren't especially ; frequent. ; ; ; Conses. Every object in the file has an (implicit) index, determined by its ; position in the file. The naturals are given the smallest indexes 0, 1, 2, ; and so on. Supposing there are N naturals, the rationals will have indexes ; N, N+1, etc. After that we have the complexes, then the characters, strings, ; symbols, and finally the conses. ; ; We encode conses using these indices. For instance, suppose the first two ; natural numbers in our file are 37 and 55. Since we start our indexing with ; the naturals, 37 will have index 0 and 55 will have index 1. Then, we can ; encode the cons (37 . 55) by just writing down these two indices, e.g., #x00 ; #x01. ; ; We insist that sub-trees of conses come first in the file, so as we are ; decoding the file, whenever we construct a cons we can make sure its indices ; refer to already-constructed conses. ; ; The object with the maximal index in the "the object" that has been saved, ; and is returned by the #z and #Z readers, or by the whole-file reader. ; ; ; The V2 format. The V2 format is almost the same as the V1 format, but with ; the following changes that allow us to restore the normedness of conses. ; ; (1) The magic number changes to #xAC120BC8, so we know which format is ; being used, ; ; (2) We tweak the way indices are handled so that NIL and T are implicitly ; given index 0 and 1, respectively, which can sometimes slightly improve ; compression for cons structures that have lots of NILs and Ts within ; them. ; ; (3) We change the way conses are represented so we can mark which conses ; were normed. Instead of recording a cons by writing down its car-index ; and cdr-index verbatim, we now instead write down: ; ; (car-index << 1) | (if honsp 1 0), cdr-index ; ; Because of the way we encode naturals, this neatly only costs an extra ; byte if the car-index happens to have an integer length that is a ; multiple of 7. ; ; (4) Instead of the total number of objects, we replace LEN with the maximum ; index of the object to be read. Usually this just means that instead of ; LEN we record LEN-1. But it allows us to detect the special case of NIL ; where the object being encoded is not necessarily at LEN - 1. ; ; ; The V3 format. The V3 format is almost the same as the V2 format, but with ; the following changes that allow us to restore fast alists. ; ; (1) The magic number changes to #xAC120BC9, and ; ; (2) We extend the OBJECT format with an extra FALS field, that comes ; after the conses. Note that LEN is unchanged and does not count ; the FALS. ; ; OBJECT ::= MAGIC ; marker for sanity checking ; LEN ; total number of objects ; NATS RATS COMPLEXES CHARS ; object data ; STRS SYMBOLS CONSES ; ; FALS ; MAGIC ; marker for sanity checking ; ; FALS ::= FAL* 0 ; zero-terminated list ; ; FAL ::= NAT NAT ; index and hash-table-count ; ; (3) We change the encoding of STR so that we can mark which strings were ; normed. Instead of recording the string's LEN, we record: ; (len << 1) | (if honsp 1 0) ; ----------------------------------------------------------------------------- ; ; PRELIMINARIES ; ; ----------------------------------------------------------------------------- (defparameter *ser-verbose* nil) (defmacro ser-time? (form) `(if *ser-verbose* (time ,form) ,form)) (defmacro ser-print? (msg &rest args) `(when *ser-verbose* (format t ,msg . ,args))) ; To make it easy to switch the kind of input/output stream being used, all of ; our stream reading/writing is done with the following macros. ; ; In previous versions of serialize we used binary streams and wrote/read from ; them with write/read-byte on most Lisps; on CCL we used memory-mapped files ; for better performance while reading. But we had to switch to using ordinary ; character streams to get compatibility with the Common Lisp reader. (defmacro ser-write-char (x stream) `(write-char (the character ,x) ,stream)) (defmacro ser-write-byte (x stream) `(ser-write-char (code-char (the (unsigned-byte 8) ,x)) ,stream)) (defmacro ser-read-char (stream) ;; Note that Lisp's read-char causes an end-of-file error if EOF is reached, ;; so we don't have to detect unexpected EOFs in our decoding routines. `(the character (read-char ,stream))) (defmacro ser-read-byte (stream) `(the (unsigned-byte 8) (char-code (ser-read-char ,stream)))) (defun ser-encode-magic (stream) ;; We only write V3 files now, so we write AC120BC9 instead of C8 or C7. (ser-write-byte #xAC stream) (ser-write-byte #x12 stream) (ser-write-byte #x0B stream) (ser-write-byte #xC9 stream)) (defun ser-decode-magic (stream) ;; Returns :V1, :V2, or :V3, or causes an error. (let* ((magic-1 (ser-read-byte stream)) (magic-2 (ser-read-byte stream)) (magic-3 (ser-read-byte stream)) (magic-4 (ser-read-byte stream))) (declare (type (unsigned-byte 8) magic-1 magic-2 magic-3 magic-4)) (let ((version (and (= magic-1 #xAC) (= magic-2 #x12) (= magic-3 #x0B) (cond ((= magic-4 #xC7) :v1) ((= magic-4 #xC8) :v2) ((= magic-4 #xC9) :v3) (t nil))))) (unless version (error "Invalid serialized object, magic number incorrect: ~X ~X ~X ~X" magic-1 magic-2 magic-3 magic-4)) version))) ; ----------------------------------------------------------------------------- ; ; ENCODING AND DECODING NATURALS ; ; ----------------------------------------------------------------------------- ; WHY DO WE USE 8-BIT BLOCKS? ; ; Originally I tried using 64-bit blocks. I thought this would mean only 1/64 ; of the bits would be "overhead" for continue-bits, and surely this would be ; better than using 8-bit blocks, where 1/8 of the bits would be continue-bit ; overhead. ; ; This thinking is totally wrong. It ignores another important source of ; overhead: the unnecessary data-bits in the final block. To make this very ; concrete, think about encoding the number 5. We only "need" 3 bits. In an ; 8-bit encoding, we use 8 bits so the overhead is 5/8 = 62%. But in a 64-bit ; encoding we would need 64 bits for an overhead of 61/64 = 95%. So the 8-bit ; encoding is much more efficient for small integers. ; ; Of course, there are cases where 64-bit blocks win. For instance, 2^62 ; nicely fits into a single 64-bit block, but requires 9 8-bit blocks (at 7 ; data bits apiece), i.e., 72 bits. But on some other larger numbers, 8-bit ; blocks can still be more efficient. Take 2^64. Here, we need either 2 ; 64-bit blocks (at 63 data bits apiece) for 128 bits, or 10 8-bit blocks for ; 80 bits. In short, the wider encoding only wins when the numbers are very ; long, or when there aren't very many unnecessary data bits in the final ; block. ; WHY ALL THESE OPTIMIZATIONS? ; ; The performance of natural number encoding/decoding is especially important ; because we have to (1) encode/decode two naturals for every cons, and (2) ; encode/decode naturals all over the place for string lengths, symbol name ; lengths, and the representation of any number. These optimizations are a big ; deal: on one example benchmark, they improve CCL's decoding performance by ; almost 20%. (defun ser-encode-nat-fixnum (n stream) ; Optimized encoder that assumes N is a fixnum. (declare (type fixnum n)) (loop while (>= n 128) do (ser-write-byte (the fixnum (logior ;; The 7 low data bits (the fixnum (logand (the fixnum n) #x7F)) ;; A continue bit #x80)) stream) (setq n (the fixnum (ash n -7)))) (ser-write-byte n stream)) (defun ser-encode-nat-large (n stream) ; Safe encoder that doesn't assume how large N is. (declare (type integer n)) (loop until (typep n 'fixnum) do ;; Fixnums are at least (signed-byte 16) in Common Lisp, so we must ;; be in the large case, i.e., n > 128. (ser-write-byte (the fixnum (logior ;; The 7 low data bits (the fixnum (logand (the integer n) #x7F)) ;; A continue bit #x80)) stream) (setq n (the integer (ash n -7)))) (ser-encode-nat-fixnum n stream)) (defmacro ser-encode-nat (n stream) ; This is kind of silly, but it lets us avoid the function overhead of calling ; ser-encode-nat-large in the very common case that N is a fixnum. `(let ((n ,n)) (if (typep n 'fixnum) (ser-encode-nat-fixnum n ,stream) (ser-encode-nat-large n ,stream)))) (defun ser-decode-nat-large (shift value stream) ; Simple (but unoptimized) natural number decoder that doesn't assume anything ; is a fixnum. Shift is 7 times the current block number we are reading, and ; represents how much we need to shift over the next 7 bits we read. Value is ; the already-summed value of the previous blocks we have read. (declare (type integer value shift)) (let ((x1 (ser-read-byte stream))) (declare (type fixnum x1)) (loop while (>= x1 128) do (incf value (ash (- x1 128) shift)) (incf shift 7) (setf x1 (ser-read-byte stream))) (incf value (ash x1 shift)) value)) (defmacro ser-decode-nat-body (shift) ; See SER-NAT-DECODE; this is accounting for different fixnum sizes across Lisps ; by unrolling the loop with a recursive macro. SHIFT is a constant that is ; being incremented by 7 on each "iteration". An invariant that is important to ; the fixnum optimizations is that VALUE is always less than 2^SHIFT. (if (> (expt 2 (+ 7 shift)) most-positive-fixnum) ;; Can't unroll any further because we've reached the fixnum size, so fall ;; back to using the large decoder. `(ser-decode-nat-large ,shift value stream) `(progn (setq x1 (ser-read-byte stream)) ;; Reusing X1 is kind of goofy, but seems to result in better code on ;; CCL. (cond ((< x1 128) ;; The returned VALUE + (X1 << SHIFT) is a fixnum since it is less than ;; 2^(7+SHIFT), which above we checked is a fixnum. (setq x1 (the fixnum (ash (the fixnum x1) ,shift))) (the fixnum (+ value x1))) (t ;; Else, we increment value by (x1 - 128) << SHIFT. This is still a ;; fixnum because (x1 - 128) < 2^7, so the sum is under 2^(7+SHIFT). (setq x1 (the fixnum (- x1 128))) (setq x1 (the fixnum (ash x1 ,shift))) (setq value (the fixnum (+ value x1))) ;; Recursive macro expansion to unroll further. (ser-decode-nat-body ,(+ 7 shift))))))) (defun ser-decode-nat (stream) ; Optimized natural-number decoder. For small enough integers (under 128) we ; don't need any shifting nonsense or even an accumulator. For anything larger, ; we set up the initial VALUE accumulator and use our macro to write an ; unrolled, fixnum-optimized loop for us. (let ((x1 (ser-read-byte stream))) (declare (type fixnum x1)) (when (< (the fixnum x1) 128) (return-from ser-decode-nat x1)) (setq x1 (the fixnum (- x1 128))) (let ((value x1)) (declare (fixnum value)) (ser-decode-nat-body 7)))) ; ----------------------------------------------------------------------------- ; ; ENCODING AND DECODING OTHER BASIC OBJECTS ; ; ----------------------------------------------------------------------------- ; RAT ::= NAT NAT NAT ; sign (0 or 1), numerator, denominator (declaim (inline ser-encode-rat ser-decode-rat)) (defun ser-encode-rat (x stream) (declare (type rational x)) (ser-encode-nat (if (< x 0) 1 0) stream) (ser-encode-nat (abs (numerator x)) stream) (ser-encode-nat (denominator x) stream)) (defun ser-decode-rat (stream) (let* ((sign (ser-decode-nat stream)) (numerator (ser-decode-nat stream)) (denominator (ser-decode-nat stream))) (declare (type integer sign numerator denominator)) (cond ((= sign 1) (setq numerator (- numerator))) ((= sign 0) ;; Fine, but there is nothing to do. nil) (t ;; This check probably isn't necessary; we could just assume that the ;; sign is zero. But it seems cheap enough and basically reasonable. (error "Trying to decode rational, but the sign is invalid."))) (when (= denominator 0) ;; This check probably isn't necessary since the Lisp should probably an ;; error if we try to divide by zero, but it seems cheap enough and is ;; probably basically reasonable. (error "Trying to decode rational, but the denominator is zero.")) (the rational (/ numerator denominator)))) ; COMPLEX ::= RAT RAT ; real, imaginary parts (declaim (inline ser-encode-complex ser-decode-complex)) (defun ser-encode-complex (x stream) (declare (type complex x)) (ser-encode-rat (realpart x) stream) (ser-encode-rat (imagpart x) stream)) (defun ser-decode-complex (stream) (let* ((realpart (ser-decode-rat stream)) (imagpart (ser-decode-rat stream))) (declare (type rational realpart imagpart)) (when (= imagpart 0) ;; Hrmn. This check is probably unnecessary. (complex 3 0) is just 3. ;; Our encoder should never encode natural numbers as complexes, but it ;; wouldn't necessarily be wrong to do so. (error "Trying to decode complex, but the imagpart is zero.")) (complex realpart imagpart))) ; (v1/v2): STR ::= LEN CHAR* ; length and then its characters ; (v3): STR ::= [(LEN << 1) | (if normedp 1 0)] CHAR* ; Note that our symbol encoding/decoding stuff piggy-backs on our string stuff, ; so we care about string encoding/decoding performance a bit. ; ; A very minor note is that in Common Lisp, the length of a string must be a ; fixnum (a string is a specialized vector, which is a one-dimensional array, ; and hence its size must be less than the array-dimension-limit, which is a ; fixnum.) (declaim (inline ser-encode-str ser-decode-str)) (defun ser-encode-str (x stream) (declare (type string x)) (let* ((len (length x)) (normedp (hl-hspace-normedp-wrapper x)) (header (logior (ash len 1) (if normedp 1 0)))) (ser-encode-nat header stream) (loop for n fixnum from 0 below (the fixnum len) do (ser-write-char (char x n) stream)))) (defun ser-decode-str (version hons-mode stream) (let* ((header (ser-decode-nat stream)) (len (if (eq version :v3) (ash header -1) header)) (normp (and (eq version :v3) (= (the bit (logand header 1)) 1) (not (eq hons-mode :never))))) (unless (and (typep len 'fixnum) (< (the fixnum len) array-dimension-limit)) (error "Trying to decode a string, but the length is too long.")) (let ((str (make-string (the fixnum len)))) (declare (type vector str)) (loop for i fixnum from 0 below (the fixnum len) do (setf (schar str i) (ser-read-char stream))) (if normp (hons-copy str) str)))) ; ----------------------------------------------------------------------------- ; ; ENCODING AND DECODING BASIC OBJECT LISTS ; ; ----------------------------------------------------------------------------- ; We now build upon our encoders/decoders for individual elements, and write ; versions to deal with lists of naturals, rationals, etc. (defstruct ser-decoder ; The decoder's state mainly consists of an ARRAY and a FREE index. As the file ; is decoded, ARRAY gets populated from zero on up, with FREE always pointing to ; the next available slot. Since array sizes are always fixnums, we know that ; FREE is always a fixnum. (array (make-array 0) :type simple-vector) (free 0 :type fixnum) ; The decoder also knows which file format we are decoding (i.e., :v1, :v2, ; :v3). This is set based on the magic number from the start of the file. (version nil)) ; NATS ::= LEN NAT* ; number of nats, followed by that many nats (defun ser-encode-nats (x stream) (let ((len (length x))) (ser-print? "; Encoding ~a naturals.~%" len) (ser-encode-nat len stream) (dolist (elem x) (ser-encode-nat elem stream)))) (defun ser-decode-and-load-nats (decoder stream) (declare (type ser-decoder decoder)) (let* ((len (ser-decode-nat stream)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a naturals.~%" len) (unless (<= stop (length arr)) ;; Note that we need just one bounds check for the whole list of naturals. (error "Invalid serialized object, too many naturals.")) (loop until (= (the fixnum stop) free) do (setf (svref arr free) (ser-decode-nat stream)) (incf free)) (setf (ser-decoder-free decoder) stop))) ; RATS ::= LEN RAT* ; number of rats, followed by that many rats (defun ser-encode-rats (x stream) (let ((len (length x))) (ser-print? "; Encoding ~a rationals.~%" len) (ser-encode-nat len stream) (dolist (elem x) (ser-encode-rat elem stream)))) (defun ser-decode-and-load-rats (decoder stream) (declare (type ser-decoder decoder)) (let* ((len (ser-decode-nat stream)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a rationals.~%" len) (unless (<= stop (length arr)) (error "Invalid serialized object, too many rationals.")) (loop until (= (the fixnum stop) free) do (setf (svref arr free) (ser-decode-rat stream)) (incf free)) (setf (ser-decoder-free decoder) stop))) ; COMPLEXES ::= LEN COMPLEX* ; number of complexes, followed by that many complexes (defun ser-encode-complexes (x stream) (let ((len (length x))) (ser-print? "; Encoding ~a complexes.~%" len) (ser-encode-nat len stream) (dolist (elem x) (ser-encode-complex elem stream)))) (defun ser-decode-and-load-complexes (decoder stream) (declare (type ser-decoder decoder)) (let* ((len (ser-decode-nat stream)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a complexes.~%" len) (unless (<= stop (length arr)) (error "Invalid serialized object, too many complexes.")) (loop until (= (the fixnum stop) free) do (setf (svref arr free) (ser-decode-complex stream)) (incf free)) (setf (ser-decoder-free decoder) stop))) ; CHARS ::= LEN CHAR* ; number of characters, followed by that many chars (defun ser-encode-chars (x stream) (let ((len (length x))) (ser-print? "; Encoding ~a characters.~%" len) (ser-encode-nat len stream) (dolist (elem x) (ser-write-char elem stream)))) (defun ser-decode-and-load-chars (decoder stream) (declare (type ser-decoder decoder)) (let* ((len (ser-decode-nat stream)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a characters.~%" len) (unless (<= stop (length arr)) (error "Invalid serialized object, too many characters.")) (loop until (= (the fixnum stop) free) do (setf (svref arr free) (ser-read-char stream)) (incf free)) (setf (ser-decoder-free decoder) stop))) ; STRS ::= LEN STR* ; number of strings, followed by that many strs (defun ser-encode-strs (x stream) (let ((len (length x))) (ser-print? "; Encoding ~a strings.~%" len) (ser-encode-nat len stream) (dolist (elem x) (ser-encode-str elem stream)))) (defun ser-decode-and-load-strs (hons-mode decoder stream) (declare (type ser-decoder decoder)) (let* ((len (ser-decode-nat stream)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) (version (ser-decoder-version decoder)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a strings.~%" len) (unless (<= stop (length arr)) (error "Invalid serialized object, too many strings.")) (loop until (= (the fixnum stop) free) do (setf (svref arr free) (ser-decode-str version hons-mode stream)) (incf free)) (setf (ser-decoder-free decoder) stop))) ; ----------------------------------------------------------------------------- ; ; ENCODING AND DECODING SYMBOLS ; ; ----------------------------------------------------------------------------- ; We don't want to pay the price of writing down the package for every symbol ; individually, since most of the time an object will probably contain lots of ; symbols from the same package. So, our basic approach is to organize the ; symbols into groups by their package names, and then for each package we write ; out: the name of the package, and the list of symbol names. ; ; See also the Essay on Bad Objects and Serialize. When we are decoding, we ; optionally check that packages are known to ACL2 by calling pkg-witness, which ; causes an error if it the package isn't known. Note that we only have to do ; this once per package, so this is a very low-cost check. ; ; If checking packages is so cheap, why not just check packages all the time? ; We tried that originally, but sometimes ACL2 actually DOES read in bad ; objects, e.g., foo@expansion.lsp may have *1* symbols in it, etc. So we need ; to not complain if ACL2 is using the #z readers when reading these files. ; PACKAGE ::= STR LEN STR* ; package name, number of symbols, symbol names (defun ser-encode-package (pkg x stream) (declare (type string pkg)) (let ((len (length x))) (ser-print? "; Encoding ~a symbols for ~a package.~%" len pkg) (ser-encode-str pkg stream) (ser-encode-nat (length x) stream) (dolist (elem x) (ser-encode-str (symbol-name elem) stream)))) (defun ser-decode-and-load-package (check-packagesp decoder stream) (declare (type ser-decoder decoder)) (let* ((version (ser-decoder-version decoder)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) ;; We always use hons-mode :never here, because there's no need to ;; the package or symbol names since we're going to intern them and ;; not return them (pkg-name (ser-decode-str version :never stream)) (len (ser-decode-nat stream)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a symbols for ~a package.~%" len pkg-name) (unless (<= stop (length arr)) (error "Invalid serialized object, too many symbols.")) (when check-packagesp (acl2::pkg-witness pkg-name)) (loop until (= (the fixnum stop) free) do (setf (svref arr free) ; (intern (ser-decode-str version :never stream) pkg-name)) ; Change by Matt K. to avoid package errors when *read-suppress* is t: (let ((temp (ser-decode-str version :never stream))) (if *read-suppress* nil (intern temp pkg-name)))) (incf free)) (setf (ser-decoder-free decoder) stop))) ; PACKAGES ::= LEN PACKAGE* ; number of packages, followed by that many packages (defun ser-encode-packages (alist stream) ;; Alist maps package-names to the lists of their symbols (let ((len (length alist))) (ser-print? "; Encoding symbols for ~a packages.~%" len) (ser-encode-nat (length alist) stream) (dolist (entry alist) (ser-encode-package (car entry) (cdr entry) stream)))) (defun ser-decode-and-load-packages (check-packagesp decoder stream) (declare (type ser-decoder decoder)) (let ((len (ser-decode-nat stream))) (ser-print? "; Decoding symbols for ~a packages.~%" len) (loop for i from 1 to len do (ser-decode-and-load-package check-packagesp decoder stream)))) ; ----------------------------------------------------------------------------- ; ; PREPARING OBJECTS FOR ENCODING ; ; ----------------------------------------------------------------------------- (defun ser-hashtable-init (size test) ; For good performance, it is critical that we aggressively resize the hash ; tables that are used in the atom-gathering phase of encoding. This is just a ; wrapper for making hash tables with more aggressive rehash sizes. (make-hash-table :size size :test test :rehash-size 2.2 #+Clozure :shared #+Clozure nil )) (defstruct ser-encoder ; This object bundles the state of the encoder. ; ; The first phase of encoding is SER-GATHER-ATOMS. The goal is to quickly ; collect all of the atoms in the object, without duplication, and partition ; them into lists by their types. ; ; To avoid repeatedly collecting the same atoms, we use four "seen" tables that ; keep track of which objects we have explored. As we gather atoms, we mark the ; objects we have seen by binding them to T in the appropriate hash table. ; ; Every symbol we have seen is in the SYM hash table, and every ; number/character we have seen is in the EQL hash table. But the string and ; cons tables are only EQ hash tables. Because of this, EQUAL-but-not-EQ ; strings and conses may be bound in their seen tables. ; ; We make no effort to avoid "redundantly" writing EQUAL-but-not-EQ conses or ; strings. Of course, a HONS user could first hons-copy their object to ; achieve full structure sharing. This would perhaps improve read time at the ; cost of write time. (seen-sym (ser-hashtable-init 1000 'eq) :type hash-table) (seen-eql (ser-hashtable-init 1000 'eql) :type hash-table) (seen-str (ser-hashtable-init 1000 'eq) :type hash-table) (seen-cons (ser-hashtable-init 2000 'eq) :type hash-table) ; In addition to the above seen tables, the encoder has several accumulators ; which collect the atoms it finds during the GATHER-ATOMS phase. The basic ; idea here is to separate these objects by their types, so that we can then ; write them out using our encoders for lists of naturals, rationals, etc. ; ; The accumulators for naturals, rationals, complexes, strings, and characters ; are simple lists that we push new values into. Because of our seen-tables, we ; can guarantee that the accumulators for naturals, rationals, complexes, and ; characters have no duplicates. However, the strings accumulator may contain ; duplicates in the sense of EQUAL. (naturals nil :type list) (rationals nil :type list) (complexes nil :type list) (chars nil :type list) (strings nil :type list) ; The symbol accumulator is more complex. The SYMBOL-HT is a hash table that ; associates packages with the lists of symbols found in that package. Once we ; are done gathering atoms, we map over this hash table to convert it into an ; alist (SYMBOL-AL). This conversion is cheap; it only requires one cons per ; package. (symbol-ht (ser-hashtable-init 60 'eq) :type hash-table) (symbol-al nil :type list) ; The free-index here is only used in ser-encode-conses. Bundling it with the ; encoder's state is beneficial in two ways for ser-encode-conses: it reduces ; stack size requirements by eliminating a parameter, and simplifies the flow ; because we don't need to return multiple values. (free-index 0 :type fixnum) ; The stream that we are writing into. Bundling this into the encoder instead ; of passing it as an extra argument helps to reduce the stack size ; requirements for ser-encode-conses. (stream nil) ) (defmacro ser-see-obj (x table) ;; Mark X as seen, and return T/NIL based on whether it was previously seen `(let ((x ,x) (tbl ,table)) (if (gethash x tbl) t (progn (setf (gethash x tbl) t) nil)))) (defun ser-gather-atoms (x encoder) ; Gathering atoms is particularly performance critical, so we have looked into ; making it faster. We assume X is a valid ACL2 object. We do some typep ; checks in a few cases where using ordinary recognizers seems to be slower. ; But this does not gain us much, because almost all of the time seems to be ; spent on hashing. ; ; Sol uses a destructive hashing scheme in his AIGER writer which we could ; probably adapt for use here, and it would probably lead to significant ; performance gains. However, anything destructive is scary with respect to ; multithreaded code, and we don't want to use it unless we really have no ; other choice. (declare (type ser-encoder encoder)) (cond ((consp x) (unless (ser-see-obj x (ser-encoder-seen-cons encoder)) (ser-gather-atoms (car x) encoder) (ser-gather-atoms (cdr x) encoder))) ((symbolp x) ;; V2 change: do not gather T and NIL into the accumulator for ;; symbols. They are implicit in the v2 format. (unless (or (eq x t) (eq x nil) (ser-see-obj x (ser-encoder-seen-sym encoder))) (push x (gethash (symbol-package x) (ser-encoder-symbol-ht encoder))))) ((typep x 'fixnum) ;; This is probably common enough to check explicitly even though with ;; our fast check. (unless (ser-see-obj x (ser-encoder-seen-eql encoder)) (if (< (the fixnum x) 0) (push x (ser-encoder-rationals encoder)) (push x (ser-encoder-naturals encoder))))) ((typep x 'array) ; <-- (stringp x), but twice as fast in CCL. (unless (ser-see-obj x (ser-encoder-seen-str encoder)) (push x (ser-encoder-strings encoder)))) ;; Performance is probably already pretty bad at this point. (t (unless (ser-see-obj x (ser-encoder-seen-eql encoder)) (cond ((typep x 'character) (push x (ser-encoder-chars encoder))) ((typep x 'integer) (if (< x 0) (push x (ser-encoder-rationals encoder)) (push x (ser-encoder-naturals encoder)))) ((rationalp x) (push x (ser-encoder-rationals encoder))) ((complex-rationalp x) (push x (ser-encoder-complexes encoder))) (t (error "ser-gather-atoms-types given non-ACL2 object."))))))) ; ESSAY ON HOW WE HANDLE EQUAL-BUT-NOT-EQ STRINGS ; ; A subtle change in V3 is that we no longer make any effort to avoid ; redundantly encoding EQUAL-but-not-EQ or strings. ; ; When I first developed serialize, I wanted to use it to save the models of ; our processor. My Verilog translator produced objects with lots of strings, ; and in many cases these strings could be different, e.g., if you parsed a ; module with: ; ; module m ( ..., foo, ... ); ; input foo; ; assign ... = foo; ; ... ; endmodule ; ; Then you could end up with lots of different strings that all said "foo". ; Note also that in this context, I really wanted to optimize for read time ; instead of write time (a script made the processor models at night, where ; time is irrelevant, but I needed to read them in while developing proofs.) ; At any rate, for these reasons, I really wanted to avoid writing out ; redundant strings. ; ; My first idea was to just use an EQUAL hash table for seen-str, but this ; turned out to be far too slow. ; ; Instead, I developed a fancy scheme. First, the seen-str was only an EQ hash ; table. But when encoding the collected strings, I (1) sorted them using ; Lisp's SORT function, which is a stable sort, and (2) assigned their indices ; using a tricky function that looked for adjacent EQUAL strings, and reused ; the index in this case. The net effect was that all strings would be ; canonicalized to some representative. This was probably a performance win ; because, instead of having to EQUAL-hash at each occurrence of the string, we ; only have to EQ hash and then do one global sort at the end. (It probably ; would have worked just as well to use separate EQ and EQUAL hash tables.) ; ; This scheme was used in V1 and V2. However, in V3, where we started to ; record whether strings were normed, and restore them to their normed status, ; we ran into a problem. ; ; Suppose there are two EQUAL-but-not-EQ strings, ; - X (which is normed) and ; - Y (which is not). ; ; Under our canonicalization scheme, we would choose one of these as the ; canonical form "at random." (Actually the winner just depended on which we ; first encountered, but you can think of that as basically random.) At any ; rate, this could two bad outcomes: ; ; (1) Y could be canonicalized to X, and hence become normed. This seems ; basically fine and is probably nothing to be worried about unless some ; TTAG code is depending on it being different from Y, which wouldn't be ; sound anyway). ; ; (2) X could be canonicalized to Y, and hence lose its normed status. This ; is bad because if X is being used as a fast alist key, we will have ; trouble restoring that alist. ; ; It seems easy enough to fix this by norming the string whenever ANY of its ; EQUAL-but-not-EQ partners are normed. But instead, we now just do not try to ; avoid writing out redundant strings. Why not? ; ; - It was somewhat complicated and ugly, and the fix seems kind of gross. ; ; - Sorting the strings slows down writing, and write speed is now more ; important than it used to be. We used to only write out our processor ; models with an automated script. But now serialize is used to print ; certificates, etc., and while write speed is certainly still less ; important than read speed, it is at least somewhat important. ; ; - EQUAL-but-not-EQ strings don't seem that prevalent anyway, e.g., we now ; judiciously use HONS-COPY to normalize strings in the Verilog parser, ; etc., and other users can do the same if they think EQUAL-but-not-EQ ; copies of a string are likely to occur. (defun ser-make-atom-map (encoder) ; After the atoms have been gathered we want to assign them unique indexes. ; These indexes will need to agree with the implicit order of the indexes in ; the serialized file. So, we need to assign indexes to the naturals first, ; then the rationals, etc. ; ; In earlier versions of serialize, we constructed "atom map" structures that ; were hash tables binding atoms to their indices. These atom maps were new ; structures that were unrelated to the seen-tables above. ; ; But now, for considerably better performance and memory efficiency, we ; instead smash the seen-tables and convert them into index mappings. That is, ; during SER-GATHER-ATOMS above, the seen tables just bound the objects we had ; seen to T. Now we are going to smash these bindings and replace them with ; their indices. This is especially efficient because their hash tables have ; already been grown to the proper sizes. ; ; Before we this smashing process, we check that the maximum index we will ever ; need is going to be a fixnum. Because of this, throughout this code we can ; assume that all indices are always fixnums. ; ; Future optimization potential. It might be possible to do the index ; assignment inline with atom gathering, by keeping separate track of how many ; naturals we have seen, how many characters, etc., and storing only ; type-relative offsets into the atom maps instead of absolute indices. This ; might be worthwhile: it should significantly reduce the amount of hashing we ; need to do. ;; Note: this order must agree with ser-encode-main. (let ((free-index 2) ;; In v2, the first free index is 2 (nil and t are implicitly 0 and 1) (seen-sym (ser-encoder-seen-sym encoder)) (seen-eql (ser-encoder-seen-eql encoder)) (seen-str (ser-encoder-seen-str encoder))) (declare (fixnum free-index) (type hash-table seen-sym seen-eql seen-str)) (dolist (elem (ser-encoder-naturals encoder)) (setf (gethash elem seen-eql) free-index) (incf free-index)) (dolist (elem (ser-encoder-rationals encoder)) (setf (gethash elem seen-eql) free-index) (incf free-index)) (dolist (elem (ser-encoder-complexes encoder)) (setf (gethash elem seen-eql) free-index) (incf free-index)) (dolist (elem (ser-encoder-chars encoder)) (setf (gethash elem seen-eql) free-index) (incf free-index)) (dolist (elem (ser-encoder-strings encoder)) (setf (gethash elem seen-str) free-index) (incf free-index)) ;; Turn the hash table of symbols into an alist so that they're in the same ;; order now and when we encode. This might not be necessary, but it's ;; probably very cheap in practice because there's only one entry per ;; package. (let ((al nil)) (maphash (lambda (key val) (push (cons (package-name key) val) al)) (ser-encoder-symbol-ht encoder)) (setf (ser-encoder-symbol-al encoder) al)) (dolist (elem (ser-encoder-symbol-al encoder)) (dolist (sym (cdr elem)) ;; We don't have to check for T and NIL because we didn't accumulate ;; them into the symbol table. (setf (gethash sym seen-sym) free-index) (incf free-index))) ;; V2 change: explicitly assign nil and t indices 0 and 1 (setf (gethash nil seen-sym) 0) (setf (gethash t seen-sym) 1) ;; Finally, update the encoder with the free index we've arrived at. (setf (ser-encoder-free-index encoder) free-index))) ; ----------------------------------------------------------------------------- ; ; ENCODING AND DECODING CONSES ; ; ----------------------------------------------------------------------------- ; After the atoms have been assigned their indices as above, we are going to ; write out a list of instructions for reassembling the conses in the object. ; We keep incrementing the free-index as we go so that the atoms and conses end ; up in a shared index-space. Just as we smashed the seen-tables for the ; atoms, we also smash the seen-cons table to reuse its space for the indices. ; ; In earlier versions of serialize, we separated the act of generating ; instructions from writing them. But now for greater efficiency we fuse the ; two operations so that we never need to record the instructions anywhere ; except in the stream. ; ; We call ser-encode-conses only after generating all of the atom maps, ; writing out all the atoms in the file, and writing the number of conses that ; we are about to build (which is available as the count of the seen-cons ; table.) (defun ser-encode-conses (x ; the object we are encoding, which we are recurring through encoder ; the encoder's state (so we can look at all the tables) ) "Returns X-INDEX" (declare (type ser-encoder encoder)) (if (atom x) ;; Atoms already have their indices assigned, so there's nothing ;; to do but look them up. (cond ((symbolp x) (gethash x (ser-encoder-seen-sym encoder))) ((stringp x) (gethash x (ser-encoder-seen-str encoder))) (t (gethash x (ser-encoder-seen-eql encoder)))) (let* ((seen-cons (ser-encoder-seen-cons encoder)) (idx (gethash x seen-cons))) ;; At this point you might expect to see something like, "(if idx ...)". ;; But since we are reusing the seen-cons table, every cons that does not ;; already have its index assigned is bound to T, not unbound. To see if ;; an index has been assigned, then, we have to check if it is a number. ;; Since all indices are fixnums, I check whether it's a fixnum, which is ;; very fast (just looking at type bits), at least on CCL. (if (typep idx 'fixnum) idx (let* ((car-index (ser-encode-conses (car x) encoder)) (cdr-index (ser-encode-conses (cdr x) encoder)) ;; At this point, we've assigned indices to the car and cdr. ;; We've also written out all of the instructions needed to ;; generate them in the stream. We can now assign an index to X ;; and write the instruction for rebuilding it: (free-index (ser-encoder-free-index encoder)) (stream (ser-encoder-stream encoder)) ;; V2 change: we now write (car-index << 1) | (if honsp 1 0) ;; instead of just car-index. Note that these fixnum ;; declarations are justified by the checking we do in ;; ser-encode-to-stream (v2-car-index (if (hl-hspace-honsp-wrapper x) (the fixnum (logior (the fixnum (ash car-index 1)) 1)) (the fixnum (ash car-index 1))))) (setf (gethash x seen-cons) free-index) (ser-encode-nat v2-car-index stream) (ser-encode-nat cdr-index stream) (setf (ser-encoder-free-index encoder) (the fixnum (+ 1 free-index))) free-index))))) (defmacro ser-decode-loop (version hons-mode) `(loop until (= (the fixnum stop) free) do (let ((first-index (ser-decode-nat stream))) (unless (typep first-index 'fixnum) (error "Consing instruction has non-fixnum first-index.")) (let ((car-index ,(if (eq version :v1) 'first-index '(the fixnum (ash (the fixnum first-index) -1)))) (honsp ,(cond ((eq hons-mode :always) 't) ((and (eq hons-mode :smart) (not (eq version :v1))) '(logbitp 0 (the fixnum first-index))) (t nil))) (cdr-index (ser-decode-nat stream))) ;; Performance testing suggests these bounds checks are ;; almost free. (unless (and (typep cdr-index 'fixnum) (< (the fixnum car-index) free) (< (the fixnum cdr-index) free)) (error "Consing instruction has index out of bounds.")) (let ((car-obj (svref arr (the fixnum car-index))) (cdr-obj (svref arr (the fixnum cdr-index)))) (setf (svref arr free) (if honsp (hons car-obj cdr-obj) (cons car-obj cdr-obj))) (incf free)))))) (defun ser-decode-and-load-conses (hons-mode decoder stream) ;; The valid hons modes are: ;; :always - always hons regardless of hons bits ;; :never - never hons regardless of hons bits ;; :smart - hons only when hons bits are set (v2/3 only) ;; smart does no honsing for v1 files (declare (type ser-decoder decoder)) (let* ((len (ser-decode-nat stream)) (arr (ser-decoder-array decoder)) (free (ser-decoder-free decoder)) (version (ser-decoder-version decoder)) (stop (+ free len))) (declare (fixnum free)) (ser-print? "; Decoding ~a consing instructions.~%" len) (unless (<= stop (length arr)) ;; Like our other decoders, we only need a single bounds check to make ;; sure we won't overflow the array as we decode the conses. (error "Invalid serialized object, too many conses.")) ;; This is a gross hack so that we have five different loops, optimized for ;; the different cases of hons-mode and file version. (if (eq version :v1) (if (eq hons-mode :always) (ser-decode-loop :v1 :always) (ser-decode-loop :v1 :never)) ;; v2/3 are the same here (cond ((eq hons-mode :always) (ser-decode-loop :v2 :always)) ((eq hons-mode :never) (ser-decode-loop :v2 :never)) (t (ser-decode-loop :v2 :smart)))) (setf (ser-decoder-free decoder) stop))) ; ----------------------------------------------------------------------------- ; ; FAST ALISTS ; ; ----------------------------------------------------------------------------- ; See also the Essay on Fast Alists in hons-raw.lisp. The FAL-HT binds some ; alists to hash tables. Some of these alists might be conses that we've just ; written out. For each such alist, we just want to record its index. ; ; Our basic approach to restoring fast alists is as follows. First, we encode ; the whole object -- we gather its atoms, assign indices to them, and write ; out all the consing instructions -- without any regard to which alists inside ; of it are fast. Then we tack on some fast-alist information. ; ; Once the whole object has been encoded, we look at the FALTABLE from the Hons ; Space. Recall that the FALTABLE binds alists to their backing hash tables. ; For each entry in the FALTABLE, we check whether the alist has been assigned ; an index in the encoder's SEEN-CONS table. I.e., we check whether the alist ; was part of the object we just encoded. If so, we write down a FAL ; instruction that describes how to rebuild the hash table. ; ; This seems pretty efficient: each FAL instruction is only two natural ; numbers, so the real added cost to encoding is just N hash table lookups, ; where N is the size of the FALTABLE, and the cost of encoding 2M naturals ; where M is the number of fast alists actually in the object. ; ; An encoded FAL instruction is a pair of natural numbers, INDEX and COUNT. ; The INDEX is just the looked up index of the alist, itself. The COUNT is the ; hash-table-count of the backing hash table, so that when we recreate the hash ; table we can initialize it with approximately the correct size. ; ; So how does decoding work? Since the FAL instructions come at the end of the ; object, we already have restored the whole object by the time we get to them. ; In other words, the INDEX that we read tells us where, in the decode array, ; we can find the alist that was fast. Assuming we are using smart honsing or ; always honsing, then the keys of the ALIST should already be honsed, because ; it was a fast alist and so its keys had to be honses. So all we need to do ; is rebuild the hash table for this alist and install it into the FAL table, ; which is very easy. ; ; We zero-terminate the FALS instead of writing down the number of FALS first. ; This is legitimate because 0 is always the index of NIL, and NIL cannot be ; bound in the FALTABLE, so there is no ambiguity. It is desirable because as ; we encode, we do not know how many FAL instructions we will actually need to ; write out. Likewise, as we decode we do not need to know how many FAL ; instructions will be processed. (defun ser-encode-fals (encoder) (declare (type ser-encoder encoder)) ; Q: Why include the hash table's count, when the decoder could just take the ; length of the alist instead? ; ; A: Since the alist can have shadowed pairs, so its length may not be a very ; good indication of the size we should use. We could end up with a much ; larger hash table than we really need. ; ; Q: Why use the hash-table-count instead of the hash-table-size? ; ; A: If a fast alist has been saved in a book, we think it is relatively ; unlikely that it is going to be modified by a sub-book. It seems more likely ; that it is some kind of lookup table that you intend to refer to over and ; over. (let* ((stream (ser-encoder-stream encoder)) (seen-cons (ser-encoder-seen-cons encoder)) (fn (lambda (alist backing-hash-table) (let ((idx (gethash alist seen-cons))) (when idx (ser-encode-nat idx stream) (ser-encode-nat (hash-table-count backing-hash-table) stream)))))) (hl-faltable-maphash fn (hl-hspace-faltable-wrapper)) (ser-encode-nat 0 stream))) (defun ser-decode-and-restore-fals (decoder hons-mode stream) (declare (type ser-decoder decoder)) ; Q: Why don't we restore when hons-mode is never? ; ; A: The keys of a fast alist need to be honsed. If we didn't smartly (or ; dumbly) make them honses when we built the conses, then we may not be able to ; convert the alist into a fast alist. Sure, we could build a new alist that ; is EQUAL to the alist and make it fast, but then how would we install it? It ; wouldn't work to smash the entry in the decoder array -- the other conses ; that rely on it have already been built. The only way would be to walk ; through the object and replace all uses of the alist with the new alist, and ; that seems horribly slow. At any rate, it doesn't seem unreasonable to say, ; if you're reading the file with no honses, you get no fast alists either. (let* ((array (ser-decoder-array decoder)) (max (length array)) (index (ser-decode-nat stream))) (loop until (= index 0) do (unless (< index max) (error "FAL index to restore is too large!")) (unless (eq hons-mode :never) (let ((alist (svref array index)) (count (ser-decode-nat stream))) (hl-restore-fal-for-serialize alist count))) (setq index (ser-decode-nat stream))))) (defun ser-encode-atoms (encoder) (declare (type ser-encoder encoder)) ; It's sort of silly for this to be its own function, but it makes a convenient ; target for timing. (let ((stream (ser-encoder-stream encoder))) (ser-encode-nats (ser-encoder-naturals encoder) stream) (ser-encode-rats (ser-encoder-rationals encoder) stream) (ser-encode-complexes (ser-encoder-complexes encoder) stream) (ser-encode-chars (ser-encoder-chars encoder) stream) (ser-encode-strs (ser-encoder-strings encoder) stream) (ser-encode-packages (ser-encoder-symbol-al encoder) stream))) (defun ser-encode-to-stream (obj stream) ; Serialize the OBJ and write it to the stream. This writes "everything from ; magic number to magic number." Note that it does NOT include the #Z prefix, ; which is needed if you're going to read the object back in. (let ((encoder (make-ser-encoder :stream stream)) starting-free-index-for-conses total-number-of-objects max-index nconses) (declare (dynamic-extent encoder)) ;; Make sure the hons space is initialized (hl-maybe-initialize-default-hs-wrapper) (ser-time? (ser-gather-atoms obj encoder)) (setq nconses (hash-table-count (ser-encoder-seen-cons encoder))) (unless (typep (ash (+ 2 ;; to account for T and NIL (hash-table-count (ser-encoder-seen-sym encoder)) (hash-table-count (ser-encoder-seen-eql encoder)) (hash-table-count (ser-encoder-seen-str encoder)) nconses) 1) 'fixnum) ;; This check ensures that all indexes will be fixnums. The sum above ;; may actually exceed the actual maximum index we will have, because ;; EQUAL-but-not-EQ strings will be removed. But it is at least as large ;; as the maximum index, so if it is a fixnum then all indexes are ;; fixnums. In V1 we just checked if the sum was a fixnum. In V2 we ;; need to shift it since indices get shifted in the file. (error "Maximum index exceeded.")) (ser-time? (ser-make-atom-map encoder)) (setq starting-free-index-for-conses (ser-encoder-free-index encoder)) (ser-encode-magic stream) (setq total-number-of-objects (the fixnum (+ starting-free-index-for-conses nconses))) (ser-encode-nat (cond ((eq obj nil) 0) (t (- total-number-of-objects 1))) stream) (ser-time? (ser-encode-atoms encoder)) (ser-encode-nat nconses stream) (setq max-index (ser-time? (ser-encode-conses obj encoder))) (ser-time? (ser-encode-fals encoder)) (unless (and (equal (ser-encoder-free-index encoder) total-number-of-objects) (or (equal max-index (- (ser-encoder-free-index encoder) 1)) ;; in v2, max-index can be 0 and 1 also, for nil or t. ;; if it happens to be t, then it's still going to be one ;; less than the max free index. (equal max-index 0))) (error "Sanity check failed in ser-encode-to-stream!~% ~ - final-free-index is ~a~% ~ - total-number-of-objects is ~a~% ~ - max-index is ~a~%" (ser-encoder-free-index encoder) total-number-of-objects max-index)) (ser-encode-magic stream))) (defun ser-decode-and-load-atoms (check-packagesp hons-mode decoder stream) (declare (type ser-decoder decoder)) (ser-decode-and-load-nats decoder stream) (ser-decode-and-load-rats decoder stream) (ser-decode-and-load-complexes decoder stream) (ser-decode-and-load-chars decoder stream) (ser-decode-and-load-strs hons-mode decoder stream) (ser-decode-and-load-packages check-packagesp decoder stream)) (defun ser-decode-from-stream (check-packagesp hons-mode stream) ; Read a serialized object from the stream. This reads "everything from magic ; number to magic number." Note that it does NOT expect there to be a #Z ; prefix. (let* ((version (ser-decode-magic stream)) (size/idx (ser-decode-nat stream)) (arr-size (if (or (eq version :v2) (eq version :v3)) (cond ((eq size/idx 0) 2) (t (+ size/idx 1))) size/idx)) (final-obj (if (or (eq version :v2) (eq version :v3)) size/idx (- arr-size 1)))) (unless (typep arr-size 'fixnum) (error "Serialized object is too large.")) (let* ((arr (make-array arr-size)) (decoder (make-ser-decoder :array arr :free 0 :version version))) (declare (dynamic-extent arr decoder) (type ser-decoder decoder)) (when (or (eq version :v2) (eq version :v3)) (setf (aref arr 0) nil) (setf (aref arr 1) t) (setf (ser-decoder-free decoder) 2)) (ser-print? "; Decoding serialized object of size ~a.~%" arr-size) (ser-time? (ser-decode-and-load-atoms check-packagesp hons-mode decoder stream)) (ser-time? (ser-decode-and-load-conses hons-mode decoder stream)) (when (eq version :v3) (ser-decode-and-restore-fals decoder hons-mode stream)) (unless (eq (ser-decode-magic stream) version) (error "Invalid serialized object, magic number mismatch.")) (unless (= (ser-decoder-free decoder) arr-size) (error "Invalid serialized object.~% ~ - Decode-free is ~a~% - Arr-size is ~a." (ser-decoder-free decoder) arr-size)) ;; Return the top object. (svref arr final-obj)))) acl2-sources/simplify.lisp0000666002132200015000000151337712222115527015322 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; A quick sketch of the three main functions here ; We renamed these functions because their nqthm names were confusing to ; one of us. ; ACL2 Nqthm ; simplify-clause SIMPLIFY-CLAUSE ; simplify-clause1 SIMPLIFY-CLAUSE0 ; rewrite-clause SIMPLIFY-CLAUSE1 ; Simplify-clause is the top-level clause simplifier but it does ; relatively little work. It merely determines what to expand and ; what not to, taking into account induction as described in comments ; in simplify-clause. The real workhorse of simplify-clause is its ; subroutine, simplify-clause1. ; Simplify-clause1 is non-recursive. It does an enormous amount of ; clause level work: removing trivial equations, detecting ; propositional tautologies with type-set, setting up the ; simplify-clause-pot-lst for the later literal-by-literal-rewriting, ; detecting linear arithmetic tautologies, and retrieving useful ; equalities from the linear arithmetic pot-lst. Once all that has ; happened, it calls rewrite-clause which begins the classic sweep of ; the clause rewriting each literal. ; Rewrite-clause is concerned only with rewriting the literals of a ; clause. It does not do any clause level work aside from that ; necessary to avoid tail biting. It rewrites each lit in turn, ; clausifies the result into a bunch of segments and splices them into ; the evolving set of answers. ; In this section we develop rewrite-clause. ; Note: The following two functions are no longer called. The were ; called before we made type-set track dependencies. However, after ; that change, we found the burden of passing up the ttrees generated ; below to be so off-putting that we eliminated their calls in favor ; of dumb-negate-lit and a no-op. It is our belief that these changes ; do not seriously weaken the system. Comments indicating the changes ; contain calls of the two functions so these decisions can be ; reconsidered. (defun negate-lit (term type-alist ens force-flg wrld) ; This function returns a term equivalent to (not term) under the ; given type-alist and wrld. It also returns a ttree justifying this ; result. ; Note Added After This Function Became Obsolete: Because ; known-whether-nil now may generate 'assumptions, negate-lit may ; generate 'assumptions. Thus, use of this function is even more ; problematic since the ttrees not only must be tracked but the ; necessary case splits done. (mv-let (knownp nilp ttree) (known-whether-nil term type-alist ens force-flg nil ; dwp wrld nil) (cond (knownp (cond (nilp (mv *t* ttree)) (t (mv *nil* ttree)))) (t (mv (dumb-negate-lit term) nil))))) (defun pegate-lit (term type-alist ens force-flg wrld) ; Like negate-lit but returns a term equivalent to term, and a ttree. ; Note Added After This Function Became Obsolete: Because ; known-whether-nil now may generate 'assumptions, negate-lit may ; generate 'assumptions. Thus, use of this function is even more ; problematic since the ttrees not only must be tracked but the ; necessary case splits done. (mv-let (knownp nilp ttree) (known-whether-nil term type-alist ens force-flg nil ; dwp wrld nil) (cond (knownp (cond (nilp (mv *nil* ttree)) (t (mv *t* ttree)))) (t (mv term nil))))) ; Rockwell Addition: Now we know hard-error returns nil too. (defun add-literal (lit cl at-end-flg) ; We add lit to clause cl, optionally at the end as per the flag. ; We assume that lit has been subjected to rewriting modulo the geneqv ; iff. Therefore, though we check lit against *t* and *nil* we do ; not do more powerful type-set reasoning. In addition, we know that ; (hard-error ctx str alist) is logically nil. (cond ((quotep lit) (cond ((equal lit *nil*) cl) (t *true-clause*))) ((equal cl *true-clause*) *true-clause*) ((member-complement-term lit cl) *true-clause*) ((variablep lit) (cond ((member-term lit cl) cl) (at-end-flg (append cl (list lit))) (t (cons lit cl)))) ; Now we can take the ffn-symb of lit. ((eq (ffn-symb lit) 'hard-error) ; (Hard-error ctx str alist) = nil. cl) ((and (eq (ffn-symb lit) 'rationalp) (member-complement-term1 (fcons-term 'integerp (fargs lit)) cl)) *true-clause*) ((and (eq (ffn-symb lit) 'not) (nvariablep (fargn lit 1)) (not (fquotep (fargn lit 1))) (eq (ffn-symb (fargn lit 1)) 'integerp) (member-equal (fcons-term 'rationalp (fargs (fargn lit 1))) cl)) *true-clause*) ((member-term lit cl) cl) (at-end-flg (append cl (list lit))) (t (cons lit cl)))) (defun add-each-literal (cl) (cond ((null cl) nil) (t (add-literal (car cl) (add-each-literal (cdr cl)) nil)))) ; By definition, clause cl1 subsumes clause cl2 provided some instance of cl1 ; is a subset of cl2. Operationally, we think of finding a suitable ; substitution, alist. But this involves search since a given literal, lit1, ; of cl1 might be instantiated so that it becomes one of several literals in ; cl2, and which instantiation we choose depends on how we can similarly get ; the rest of cl1's literals ``absorbed.'' ; We augment subsumption to handle the special case of clauses containing ; (EQUAL x 'const1) atoms. First, note that cl1 subsumes cl2 below: ; cl1: ((equal x 'const1) p...) ; cl2: ((not (equal x 'const2)) p... q...) ; In particular, modulo the instantiation done for subsumption, subsumption ; just checks the truth of (IMPLIES (OR . cl1) (OR . cl2)). But cl2 may be ; thought of as (IMPLIES (equal x 'const2) (OR p... q...)) and thus we are ; checking ; (IMPLIES (AND (equal x 'const2) (OR (equal x 'const1) p...)) ; (OR p... q...)) ; which is the same as ; (IMPLIES (AND (equal x 'const2) (OR p...)) ; (OR p... q...)) ; and hence true. ; To check this thinking for sanity, consider a specific application. Suppose ; we have proved cl1: (or (equal x 'const1) (p x)), and we are later confronted ; by cl2: (or (not (equal A 'const2)) (p A) (q A)). Are we justified in saying ; that the proved theorem establishes cl2? Yes. Think of cl1 as a rewrite ; rule: (implies (not (equal x 'const1)) (iff (p x) t)). Now consider ; rewriting (p A) in cl2. You may assume the falsity of the other literals of ; cl2. So we have (equal A 'const2). Backchain with cl1. We msut prove (not ; (equal A 'const1)), which is true because A is 'const2. ; So how extend subsumption to handle instantiation of an ; ``equality-with-a-constant''? First recall the basic subsumption algorithm. ; We think of a literal lit2 from cl2 as ``absorbing'' a literal lit1 of cl1 if ; there is an extension of the current unify substitution alist such that ; lit1/alist is lit2. Then we say that cl1 subsumes cl2 if for every literal ; lit1 of cl1 there is a literal lit2 of cl2 that absorbs it so that the rest ; of the literals of cl1 are subsumed. To extend this basic idea to handle ; equality-with-constants we extend the notion of absorption. We say (NOT ; (EQUAL a const2)) absorbs (EQUAL x const1) if const1 and const2 are distinct ; constants and x unifies with a. This is implemented in the function ; subsumes!1-equality-with-const below. ; We code two versions of subsumption. One, subsumes-rec fails after a certain ; specified number of unification calls. The other, subsumes!-rec has no such ; limit. They must be kept in sync. Both handle the special case of ; equalities with constants and of the dummy EXTRA-INFO literal. (mutual-recursion (defun subsumes-rec (count cl1 cl2 alist) ; Keep this nest in sync with the subsumes!-rec nest, which is similar except ; that there is no restriction (count) on the number of one-way-unify1 calls. ; We return a positive or negative integer, according to whether or not ; (respectively) some instance of cl1 via an extension of alist is a subset of ; clause cl2. In either case, the absolute value n of that integer is at most ; count, and (- count n) is the number of one-way-unify1 calls that were made. ; Otherwise we return 0, indicating that we could not determine subsumption ; using fewer than count such calls. ; Here is why subsumes-rec and subsumes1 take a "count" argument to limit the ; number of calls: ; Note that in the worst case, checking whether clause2 of length len2 is an ; instance of clause1 of length len1 is roughly on the order of len2^len1. For ; suppose every term in each clause is (integerp x) for a distinct x, except ; that the last term in the first clause is not a match for any member of the ; second clause. Then each (integerp x) in clause1 can be matched against any ; (integerp y) in clause2, so we have len2*len2*...*len2, len1-1 times. (declare (type (signed-byte 30) count)) (the (signed-byte 30) (cond ((eql count 0) 0) ((null cl1) count) ((extra-info-lit-p (car cl1)) (subsumes-rec count (cdr cl1) cl2 alist)) ((and (nvariablep (car cl1)) (not (fquotep (car cl1))) (eq (ffn-symb (car cl1)) 'EQUAL)) (cond ((quotep (fargn (car cl1) 1)) (subsumes1-equality-with-const count (car cl1) (fargn (car cl1) 2) (fargn (car cl1) 1) (cdr cl1) cl2 cl2 alist)) ((quotep (fargn (car cl1) 2)) (subsumes1-equality-with-const count (car cl1) (fargn (car cl1) 1) (fargn (car cl1) 2) (cdr cl1) cl2 cl2 alist)) (t (subsumes1 count (car cl1) (cdr cl1) cl2 cl2 alist)))) (t (subsumes1 count (car cl1) (cdr cl1) cl2 cl2 alist))))) (defun subsumes1-equality-with-const (count lit x const1 tl1 tl2 cl2 alist) (cond ((eql count 0) 0) ((null tl2) (-f count)) ((extra-info-lit-p (car tl2)) (subsumes1-equality-with-const count lit x const1 tl1 (cdr tl2) cl2 alist)) ((and (nvariablep (car tl2)) (not (fquotep (car tl2))) (eq (ffn-symb (car tl2)) 'NOT) (nvariablep (fargn (car tl2) 1)) (not (fquotep (fargn (car tl2) 1))) (eq (ffn-symb (fargn (car tl2) 1)) 'EQUAL)) (let ((arg1 (fargn (fargn (car tl2) 1) 1)) (arg2 (fargn (fargn (car tl2) 1) 2))) (cond ((and (quotep arg1) (not (equal arg1 const1))) (mv-let (wonp alist1) (one-way-unify1 x arg2 alist) (cond ((not wonp) (subsumes1-equality-with-const (1-f count) lit x const1 tl1 (cdr tl2) cl2 alist)) (t (let ((new-count (subsumes-rec (1-f count) tl1 cl2 alist1))) (cond ((<= 0 new-count) new-count) (t (subsumes1-equality-with-const (-f new-count) lit x const1 tl1 (cdr tl2) cl2 alist)))))))) ((and (quotep arg2) (not (equal arg2 const1))) (mv-let (wonp alist1) (one-way-unify1 x arg1 alist) (cond ((not wonp) (subsumes1-equality-with-const (1-f count) lit x const1 tl1 (cdr tl2) cl2 alist)) (t (let ((new-count (subsumes-rec (1-f count) tl1 cl2 alist1))) (cond ((<= 0 new-count) new-count) (t (subsumes1-equality-with-const (-f new-count) lit x const1 tl1 (cdr tl2) cl2 alist)))))))) (t (subsumes1-equality-with-const count lit x const1 tl1 (cdr tl2) cl2 alist))))) (t (mv-let (wonp alist1) (one-way-unify1 lit (car tl2) alist) (cond ((not wonp) (subsumes1-equality-with-const (1-f count) lit x const1 tl1 (cdr tl2) cl2 alist)) (t (let ((new-count (subsumes-rec (1-f count) tl1 cl2 alist1))) (cond ((<= 0 new-count) new-count) (t (subsumes1-equality-with-const (-f new-count) lit x const1 tl1 (cdr tl2) cl2 alist)))))))))) (defun subsumes1 (count lit tl1 tl2 cl2 alist) ; Keep this nest in sync with the subsumes!-rec nest, which is similar except ; that there is no restriction (count) on the number of one-way-unify1 calls. ; If we can extend alist to an alist1 so that lit/alist1 is a member of tl2 and ; tl1/alist1 is a subset of cl2, we return a positive integer obtained by ; decreasing count by the number of one-way-unify1 calls. If we determine that ; there is no such alist, we return a negative integer whose absolute value is ; obtained by decreasing count as above. But, if the number of one-way-unify1 ; calls necessary is not less than count, we return 0. (declare (type (signed-byte 30) count)) (the (signed-byte 30) (cond ((eql count 0) 0) ((null tl2) (-f count)) ((extra-info-lit-p (car tl2)) (subsumes1 count lit tl1 (cdr tl2) cl2 alist)) (t (mv-let (wonp alist1) (one-way-unify1 lit (car tl2) alist) (cond ((not wonp) (subsumes1 (1-f count) lit tl1 (cdr tl2) cl2 alist)) (t (let ((new-count (subsumes-rec (1-f count) tl1 cl2 alist1))) (declare (type (signed-byte 30) new-count)) (cond ((<= 0 new-count) new-count) (t (subsumes1 (-f new-count) lit tl1 (cdr tl2) cl2 alist))))))))))) ) (mutual-recursion (defun subsumes!-rec (cl1 cl2 alist) ; Keep this nest in sync with the subsumes1 nest, which is similar except that ; there is a restriction (count) on the number of one-way-unify1 calls. ; We return t if some instance of cl1 via an extension of alist is a subset of ; clause cl2, otherwise nil. (cond ((null cl1) t) ((extra-info-lit-p (car cl1)) (subsumes!-rec (cdr cl1) cl2 alist)) ((and (nvariablep (car cl1)) (not (fquotep (car cl1))) (eq (ffn-symb (car cl1)) 'EQUAL)) (cond ((quotep (fargn (car cl1) 1)) (subsumes!1-equality-with-const (car cl1) (fargn (car cl1) 2) (fargn (car cl1) 1) (cdr cl1) cl2 cl2 alist)) ((quotep (fargn (car cl1) 2)) (subsumes!1-equality-with-const (car cl1) (fargn (car cl1) 1) (fargn (car cl1) 2) (cdr cl1) cl2 cl2 alist)) (t (subsumes!1 (car cl1) (cdr cl1) cl2 cl2 alist)))) (t (subsumes!1 (car cl1) (cdr cl1) cl2 cl2 alist)))) (defun subsumes!1-equality-with-const (lit x const1 tl1 tl2 cl2 alist) (cond ((null tl2) nil) ((extra-info-lit-p (car tl2)) (subsumes!1-equality-with-const lit x const1 tl1 (cdr tl2) cl2 alist)) ((and (nvariablep (car tl2)) (not (fquotep (car tl2))) (eq (ffn-symb (car tl2)) 'NOT) (nvariablep (fargn (car tl2) 1)) (not (fquotep (fargn (car tl2) 1))) (eq (ffn-symb (fargn (car tl2) 1)) 'EQUAL)) (let ((arg1 (fargn (fargn (car tl2) 1) 1)) (arg2 (fargn (fargn (car tl2) 1) 2))) (cond ((and (quotep arg1) (not (equal arg1 const1))) (mv-let (wonp alist1) (one-way-unify1 x arg2 alist) (cond ((and wonp (subsumes!-rec tl1 cl2 alist1)) t) (t (subsumes!1-equality-with-const lit x const1 tl1 (cdr tl2) cl2 alist))))) ((and (quotep arg2) (not (equal arg2 const1))) (mv-let (wonp alist1) (one-way-unify1 x arg1 alist) (cond ((and wonp (subsumes!-rec tl1 cl2 alist1)) t) (t (subsumes!1-equality-with-const lit x const1 tl1 (cdr tl2) cl2 alist))))) (t (subsumes!1-equality-with-const lit x const1 tl1 (cdr tl2) cl2 alist))))) (t (mv-let (wonp alist1) (one-way-unify1 lit (car tl2) alist) (cond ((and wonp (subsumes!-rec tl1 cl2 alist1)) t) (t (subsumes!1-equality-with-const lit x const1 tl1 (cdr tl2) cl2 alist))))))) (defun subsumes!1 (lit tl1 tl2 cl2 alist) ; Keep this nest in sync with the subsumes1 nest, which is similar except that ; there is a restriction (count) on the number of one-way-unify1 calls. ; If we can extend alist to an alist1 so that lit/alist1 is a member of tl2 and ; tl1/alist1 is a subset of cl2, we return t; otherwise, nil. (cond ((null tl2) nil) ((extra-info-lit-p (car tl2)) (subsumes!1 lit tl1 (cdr tl2) cl2 alist)) (t (mv-let (wonp alist1) (one-way-unify1 lit (car tl2) alist) (cond ((and wonp (subsumes!-rec tl1 cl2 alist1)) t) (t (subsumes!1 lit tl1 (cdr tl2) cl2 alist))))))) ) (defconst *init-subsumes-count* (the (signed-byte 30) ; The following value is rather arbitrary, determined by experimentation so ; that subsumes doesn't run for more than a small fraction of a second on a ; 2.6GH P4 (depending on the underlying Lisp). The following takes about 0.04 ; seconds to return '? (signalling that we have done 1,000,000 calls of ; one-way-unify1) on that machine using GCL 2.6.7 and about 0.17 seconds using ; Allegro CL 7.0. ; (subsumes 1000000 ; '((integerp x1) (integerp x2) (integerp x3) (integerp x4) ; (integerp x5) (integerp x6) (integerp x7) (integerp x8) ; (foo a)) ; '((integerp x1) (integerp x2) (integerp x3) (integerp x4) ; (integerp x5) (integerp x6) (integerp x7) (integerp x8)) ; nil) 1000000)) (defun subsumes (init-subsumes-count cl1 cl2 alist) ; If init-subsumes-count is not nil, then it is a nonnegative integer ; specifying a strict upper bound on the number of one-way-unify1 calls. See ; the comment in subsumes-rec for an explanation of why we may want this bound. ; If the return value is t, then we can extend alist to a substitution s such ; that cl1/s is a subset of cl2. If the return value is nil, then we cannot ; thus extend alist. Otherwise (only possible if init-subsumes-count is not ; nil), the return value is '?, in which case we could not make such a ; determination with fewer than init-subsumes-count one-way-unify1 calls. (cond ((time-limit5-reached-p "Out of time in subsumption (subsumes).") ; nil, or throws nil) ((null init-subsumes-count) (subsumes!-rec cl1 cl2 alist)) (t (let ((temp (subsumes-rec init-subsumes-count cl1 cl2 alist))) (cond ((eql temp 0) '?) (t (< 0 temp))))))) (defun some-member-subsumes (init-subsumes-count cl-set cl acc) ; Returns t if some member of cl-set subsumes cl, acc if no member of cl-set ; subsumes cl, and '? (only possible if init-subsumes-count is non-nil) if we ; don't know. (cond ((null cl-set) acc) (t (let ((temp (subsumes init-subsumes-count (car cl-set) cl nil))) (cond ((eq temp t)) (t (some-member-subsumes init-subsumes-count (cdr cl-set) cl (or temp acc)))))))) (defun conjoin-clause-to-clause-set (cl cl-set) ; Once upon a time, in particular, in the two weeks before January 25, ; 1990, we did a subsumption check here. The idea was that if cl was ; subsumed by some member of cl-set, don't add it and if it subsumes ; some member of cl-set, delete that member. That caused unsoundness. ; The reason is that cl-set is not a set of clauses that is ; necessarily going to be proved. For example, cl-set may contain a ; collection of clause segments to which we will eventually add some ; additional hypotheses. If cl-set contains the clause segment ((P ; I)) and then we conjoin the clause segment ((P (F X))) to it, we ; don't want the first segment to subsume the second because we may ; eventually add the additional hypothesis (INTEGERP I) to all the ; segments. (cond ((member-equal *t* cl) cl-set) ((member-equal cl cl-set) cl-set) (t (cons cl cl-set)))) (defun add-each-literal-lst (cl-set) (cond ((null cl-set) nil) (t (conjoin-clause-to-clause-set (add-each-literal (car cl-set)) (add-each-literal-lst (cdr cl-set)))))) (defun conjoin-clause-sets (cl-set1 cl-set2) (cond ((null cl-set1) cl-set2) (t (conjoin-clause-to-clause-set (car cl-set1) (conjoin-clause-sets (cdr cl-set1) cl-set2))))) (defun some-element-member-complement-term (lst1 lst2) (cond ((null lst1) nil) ((member-complement-term (car lst1) lst2) t) (t (some-element-member-complement-term (cdr lst1) lst2)))) ; Rockwell Addition: We used to just stick them together. Now we add ; each literal to catch cases like hard-error. (defun disjoin-clauses1 (cl1 cl2) ; This is equivalent to (append cl1 (set-difference-equal cl2 cl1)) ; except that we add each literal with add-literal to check for ; complementary pairs, etc. ; Note: This function repeatedly adds literals from cl2 to cl1, at the ; end. So it copies cl1's spine as many times as there are literals ; to add. We used to use the append formulation above but found that ; complementary pairs were being missed once we extended the notion of ; complementary to include rational v integer. (cond ((endp cl2) cl1) (t (disjoin-clauses1 (add-literal (car cl2) cl1 t) (cdr cl2))))) (defun disjoin-clauses (cl1 cl2) (cond ((or (equal cl1 *true-clause*) (equal cl2 *true-clause*)) *true-clause*) ((null cl1) cl2) ((null cl2) cl1) (t (disjoin-clauses1 cl1 cl2)))) ; See comment in disjoin-clause-segment-to-clause-set. ; (defun disjoin-clause-segment-to-clause-set-rec (segment cl-set acc) ; (cond ((null cl-set) acc) ; (t (disjoin-clause-segment-to-clause-set-rec ; segment ; (cdr cl-set) ; (conjoin-clause-to-clause-set ; (disjoin-clauses segment (car cl-set)) ; acc))))) (defun disjoin-clause-segment-to-clause-set (segment cl-set) ; This code is not tail-recursive, but it could be. At one time it caused ; stack overflow problems in LispWorks 4.2.0. Below is some alternate code, ; with a reverse call in order to provide unchanged functionality. Would we ; gain efficiency by eliminating tail recursion at the cost of that reverse ; call? Maybe. A clearer win would be to avoid the reverse call, which should ; be logically OK but could change the prover's behavior, thus invaliding huge ; portions of the regression suite. ; The alternate code is simply the following line, in place of all that ; follows: ; (disjoin-clause-segment-to-clause-set-rec segment (reverse cl-set) nil) (cond ((null cl-set) nil) (t (conjoin-clause-to-clause-set (disjoin-clauses segment (car cl-set)) (disjoin-clause-segment-to-clause-set segment (cdr cl-set)))))) (defun split-on-assumptions (assumptions cl ans) ; Cl is a clause and ans is a set of clauses that will be our answer. ; Assumptions is a list of literals. For each lit in assumptions ; we add a new clause to ans, obtained by adding lit to cl. (cond ((null assumptions) ans) (t (split-on-assumptions (cdr assumptions) cl (conjoin-clause-to-clause-set (add-literal (car assumptions) cl nil) ans))))) (defun rewrite-clause-action (lit branches) ; Lit is a term. Branches is the result of clausifying the result of ; rewriting lit. We want to know if anything has happened. The table ; below indicates our result: ; branches result meaning ; {} 'shown-true Lit was rewritten and clausified to ; the empty set, i.e., lit is T under our ; assumptions. ; {NIL} 'shown-false Lit was rewritten and clausified to ; the set containing the empty clause, i.e., ; lit is NIL under our assumptions. ; {{lit}} 'no-change Neither rewrite nor clausify did anything. ; otherwise 'change (cond ((consp branches) (cond ((null (cdr branches)) (cond ((null (car branches)) 'shown-false) ((and (null (cdr (car branches))) (equal lit (car (car branches)))) 'no-change) (t 'change))) (t 'change))) (t 'shown-true))) ; Forward Chaining ; ACL2 implements a rudimentary form of forward chaining -- though it is ; getting less rudimentary as time goes on! Its primary use is at the ; top-level of clause simplification (simplify-clause1), where before we begin ; to rewrite the literals of the clause (and in the same place we set up the ; simplify-clause-pot-lst), we forward chain from the negations of the literals ; of the clause and construct a list of all the (heuristically approved) ; conclusions we can derive. Each concl is paired with a tree that contains ; the 'lemma and 'pt dependencies. That list of pairs is passed down to the ; rewrite-clause level, where it is used to augment the type-alist before ; rewriting any given literal. ; This is the fourth (or fifth, depending on how you count) version of forward ; chaining. For an extensive comment on version II, see the historical plaque ; after the definition of rewrite-clause-type-alist. (There are also ; historical plaques elsewhere in this code.) ; The top-level interface to forward chaining is the function named ; forward-chain. However, forward-chain just calls forward-chain-top with one ; more argument, a token identifying the caller. We tend to use ; forward-chain-top in our code so that a sensible caller is known. But we ; provide forward-chain, with caller 'miscellaneous, mainly for builders of ; tools. ; Besides its use in simplify-clause1, forward-chain-top is called in several ; other places, including built-in-clausep (which is used in preprocess-clause ; and indirectly in defun's prove-termination), bdd-clause (which is used when ; we apply :bdd hints), get-induction-cands-from-cl-set1 (used in firing ; induction rules while computing induction schemes), and hyps-type-alist (used ; in show-rewrites). All of these provide sensible caller tokens. The caller ; is only relevant to the trace-like reporting facility. ; Basic Ideas and Terminology ; Forward chaining is implemented by the function forward-chain-top. At the ; highest level, think of forward chaining as ``activating'' all the forward ; chaining rules triggered by the terms in the problem and then ``advancing'' ; each activation in the context of a type-alist that tells us all the things ; we know. An activation is actually an object with fields such as the ; instantiated hypothesis we're trying to relieve, the remaining hyps, the ; unify substitution, etc. To advance an activation we check each ; (instantiated) hyp successively against the type-alist with type-set. If we ; reach the end of the hyps, we know the conclusions of the rule are all true. ; We record these facts in ``fc-derivations''. If we reach a hyp whose truth ; is not known under the current type-alist, we ``suspend'' the activation. Of ; course, if a hyp is found to be false, we simply drop the activation. We ; must also handle the branching caused by free vars in a hyp -- which causes ; an activation to split into several different activations under each of the ; possible matches for the free vars plus to remain suspended in case ; additional matches arise later. ; When we have advanced all the activations we have a list of still-suspended ; activations and a list of forward-chaining derivations deduced so far. We ; then heuristically decide which of the derivations to keep. This is called ; ``approving'' the derivations and is imposed to prevent pumps like (implies ; (p x) (p (f x))) from causing infinite forward chaining. A key heuristic is ; that the derived conclusion should not be worse than any conclusion used in ; its derivation. This means we must be able to determine which conclusions ; were used in the derivation of another one. We do that rather cheaply by ; embedding ttrees in fc-derivations. These ttrees are tainted from the ; perspective of the rest of the code, because they have dependencies buried ; inside them. We discuss this when we introduce them but it gives rise to ; such notions as an ``fcd-free'' ttree -- that is, a normal ttree as opposed ; to one with fc-derivations containing other ttrees in it -- and ``expunging'' ; the fc-derivations from a non-fcd-free ttree to get an fcd-free ttree. The ; forward chaining module traffics in non-fcd-free ttrees but ultimately ; returns fcd-free ttrees and type-alists that may be used in the rest of the ; prover. ; Once we have collected the approved derivations, we assume them all obtaining ; a new type-alist. Since the newly added conclusions may contain new terms, ; we add more activations to our list of suspended activations. Then we start ; another ``round'' in which we try again to advance the suspended activations ; in the context of the new type-alist. ; The notion of a round is implemented by forward-chain1. The top-level ; forward chain just sets up the initial type-alist and the initial activations ; and calls forward-chain1, and then expunges and elaborates the type-alist a ; little. The notion of advancing an activation is implemented by a nest of ; three functions named advance-fc-activation1, 2, and 3, which roughly put, ; are designed to relieve a single hyp, relieve a list of hyps, and relieve ; hyps under a multiplicity of matches for free-vars. Advancing an ; fc-activation introduces the notion of a ``virtual activation'' to avoid ; consing up activation objects as we move from hyp to hyp, for example. A ; virtual activation, v, is an ordinary activation object, o, together with ; values held in certain local variables of the ``advance-fc-activation'' ; functions; the actual activation represented by v could be obtained by ; writing those values to their respective fields in o. But we don't do that ; until it is time to suspend the virtual activation because we get blocked. ; Finally, we also squirrel away certain data in a wormhole, named ; ``fc-wormhole'' to allow us to create a ``report'' on what happened in ; forward chaining. Because forward chaining is called in several places and ; is an algorithm (like resolution) in which things are happening on many ; fronts (activations) at once, rather than a real-time trace-like facility we ; provide an after-the-fact reporting facility. ; We repeat some of this introductory material as we develop the code. We also ; provide prettyify-fc-activation and prettyify-fc-derivation for debugging ; purposes even though they are not used in this code. Prettyify-fc-derivation ; is particularly useful because it builds a decent representation of the ; derivation tree of conclusion produced by forward chaining and thus can help ; you understand fc-derivations and what has actually happened in a proof ; attempt. ; A forward chaining rule is (defrec forward-chaining-rule ((rune . nume) trigger hyps concls . match-free) nil) ; One of the main inefficiencies in our earlier forward chaining schemes ; was that if a rule began to fire but misfired because some hyp could ; not be relieved, we would reconsider firing it later and redo the work ; associated with the misfire. We avoid that by storing what we call a ; "forward chaining activation" record which permits us to suspend the ; attempt to fire a rule and resume it later. (defrec fc-activation (inst-hyp (hyps . ttree) unify-subst inst-trigger . rule) t) ; Warning: Despite the name, inst-hyp is not necessarily a term! ; See below. ; Warning: If you reorder the fields or add new ones, reconsider ; suspend-fc-activation, which is designed to save conses by exploiting ; the layout. Suspend-fc-activation is correct independently of the ; order of the fields, but may not actually save conses if they're ; rearranged. ; An fc-activation represents an attempt to apply the given ; forward-chaining-rule. Suppose a term of interest unifies with the ; trigger term of some rule. Then we try to relieve the hypotheses of ; that rule, using the current set of assumptions coded in a type-alist. ; Imagine that we relieve all those up to but not including hypn, ; producing an extended unify substitution and a ttree recording the ; dependencies so far. But then we learn that hypn is not yet ; definitely nil or definitely non-nil. Since the list of assumptions ; is growing, we may be able eventually to establish hypn. Therefore, ; instead of just quitting and starting over we suspend the attempted ; application of rule by producing an fc-activation record containing ; our current state. ; The current :unify-subst, :ttree and :rule are stored in the slots of ; those names. The :inst-trigger is the term in the current problem ; that fired this rule. What about :inst-hyps and :hyps? What do they ; hold? There are two cases of interest depending on whether the hyp we ; are stuck on (hypn above) contains free variables under unify-subst. ; :Inst-hyp is just hypn/unify-subst if hypn contains no free variables ; wrt unify-subst. In that case, :hyps is the cdr of the rule's hyps ; starting immediately after hypn. Furthermore, :inst-hyp is never an ; evaluable ground term (or else we would have evaluated it) or a FORCE ; or CASE-SPLIT (or else we would have forced or split on it). That is, ; :inst-hyp is a term that must be true under type-alist to proceed, and ; :hyps contains the hyps we must relieve after relieving inst-hyp. (We ; cannot get stuck on a hypothesis that is forced or split unless it ; contains free variables. So we never build an activation stuck on ; such a hyp.) ; :Inst-hyp is a special marker, called the :FC-FREE-VARS marker, if ; hypn contains free variables wrt unify-subst. In this case, :hyps is ; the cdr of the rule's hyps starting with the problematic hypn. The ; :FC-FREE-VARS marker looks like this and so is not a term: ; (:FC-FREE-VARS forcer-fn . last-keys-seen) ; Forcer-fn is nil if the hyp is not to be forced, and is either FORCE ; or CASE-SPLIT otherwise. (By providing for forcer-fn we can isolate ; the handling of free vars into one piece of code. Observe how ; advance-fc-activation2 calls advance-fc-activation1 when the hyp in ; question has free vars.) The FORCE or CASE-SPLIT annotation will have ; been stripped off of the car of :hyps, so that what is there is what ; must be found. Last-keys-seen is a list of all the keys ever ; used to create matches up to now -- and thus those keys should be ; avoided in the future. ; Summary: :inst-hyp is either a term or a non-term starting with ; :FC-FREE-VARS. If the former, it is the fully instantiated term that ; must be true under the current type-alist to proceed, it is not an ; evaluable ground term (except for possibly a constant like *t*) or a ; FORCE or CASE-SPLIT, and :hyps is the rest of the hyps. If the ; latter, the marker tells us how to match it without reproducing ; matches already created and whether to force or split on it. Note ; that we consider it very odd and rare to see a forced or split ; free-var hypothesis since it is either matched right away or ; introduces UNBOUND-FREE-vars into the proof. ; Historical Plaque: Forward chaining was first coded before type-set ; could force 'assumptions. Thus, splitting on 'assumptions was ; uncommon, indeed, it was only done for the output of linear ; arithmetic, where we first used the idea in the late 70's. Thus, the ; forward chaining mechanism was designed so as not to produce any ; 'assumptions, i.e., so as not to call rewrite. When type-set was ; extended, assumption generation and handling became more wide-spread. ; In particular, this function can generate assumptions due to the call ; of type-set below and those assumptions are handled by the caller of ; the forward-chaining module. So now, except for these historical ; comments, there is no rationale behind this function's abstinence from ; rewrite. Mixing forward and backward chaining so intimately might be ; interesting. It might also be a can of worms. It might also be ; inevitable. It just isn't the most important thing to do just yet. ; Historical Plaque: As of Version_4.1 we had a heuristic oversight in ; forward chaining that allowed the presence of one (irrelevant) forward ; chaining rule to thwart the application of a relevant forward chaining ; rule. Here I describe how. Suppose we have a relevant rule whose ; activation is blocked because it needs (FOOP x) where x is free. ; Suppose (FOOP (A)) is derived by some irrelevant rule. Then the ; relevant activation advances, choosing (A) for x. Eventually that ; activation terminates, e.g., because we can't prove the next hyp about ; x when x is (A). In Version_4.1 and before, all traces of the ; relevant activation are lost when it is advanced over (FOOP x). So if ; a subsequent rule derives (FOOP (B)) for us, we never make that choice ; for x. In summary: the irrelevant rule derives a spurious guess for x ; and we never try the relevant rule with the right choice of x, even ; though the choice is suggested on the eventual type-alist. This ; actually happened in an example distilled by Dave Greve. The obvious ; problem with leaving the relevant activation around, still blocked on ; (FOOP x), is that we'll repeatedly re-discover the possiblity that x ; is (A). In discussing how to avoid such redundancy, Dave suggested ; searching only the ``new'' part of each type-alist (new since the last ; attempt to guess free vars). However, that idea doesn't work because ; we cannot determine what part of the type-alist is ``new'' since we do ; not necessary just add pairs to a type-alist. End of Plaque. ; When we advance an activation we keep the inst-hyp, hyp, unify-subst, ; and ttree fields in variables and only put them back into the activation ; record when we decide to suspend it. They may or may not have changed. (defun suspend-fc-activation (act inst-hyp hyps unify-subst ttree) ; This function is equivalent to ; (change fc-activation act ; :inst-hyp inst-hyp ; :hyps hyps ; :unify-subst unify-subst ; :ttree ttree) ; This would take 4 conses given the layout: ; (defrec fc-activation ; (inst-hyp (hyps . ttree) ; unify-subst inst-trigger . rule) t) ; But, for example, if only inst-hyp changes, then it could be done in 1 cons. ; So we optimize three cases: (a) where none of the fields change, (b) where ; :unify-subst didn't change, and (c) where only :inst-hyp changed. These ; cases are chosen both for their estimated frequency and the fact that the ; data structure actually permits conses to be saved. Case (a) is perhaps most ; common, when we make no progress relieving the hypothesis we're stuck on and ; free variables are not involved at all. Case (b) is when we've made progress ; but not selected any new free variables. Case (c) probably cannot occur -- ; if inst-hyp changed then hyps changes too -- but we coded it because it was ; straightforward to do and because in past versions of this code it was ; possible for inst-hyp alone to change and thus it may become possible again. ; The only sense in which this function depends on the shape of ; fc-activation records is that if the shape were rearranged these ; optimizations might not save any conses. The correctness of the ; function (given its arguments) is independent of the shape of the ; record. (cond ((equal unify-subst (access fc-activation act :unify-subst)) (cond ((and (equal hyps (access fc-activation act :hyps)) (equal ttree (access fc-activation act :ttree))) (cond ((equal inst-hyp (access fc-activation act :inst-hyp)) ; Case (a) -- 0 conses act) (t ; Case (c) -- 1 cons (change fc-activation act :inst-hyp inst-hyp)))) (t ; Case (b) -- 3 conses (change fc-activation act :inst-hyp inst-hyp :hyps hyps :ttree ttree)))) (t ; Otherwise -- 4 conses (change fc-activation act :inst-hyp inst-hyp :hyps hyps :unify-subst unify-subst :ttree ttree)))) (defun prettyify-fc-activation (act level) ; This function converts an fc-activation act into a readable form and level is ; either 1 or 2 that specifies how much detail you want to see. What you ; get is: ; level result ; 1: (name (trigger: inst-trigger) ; (:blocked-hyp k) ; (:reason inst-hyp) | (:reason :FREE inst-hyp' seen) ; ) ; 2: (rune (trigger: inst-trigger) ; (:blocked-hyp k) ; (:reason inst-hyp) | (:reason :FREE inst-hyp' seen) ; (:unify-subst unify-subst)) ; where k is the number of the hyp that is currently blocking our ; progress and inst-hyp' is the hyp instantiated with the unbound-free ; extension of unify-subst and seen is the list of terms ; already used to bind the free vars in this hyp. As with ; prettyify-fc-derivation, name is the basic symbol of rune or else a ; pair of that symbol and the nat that makes this rune unique. ; To see how to read a rule, look at the level 2 code. (let* ((rune (access forward-chaining-rule (access fc-activation act :rule) :rune)) (name (if (null (cddr rune)) (cadr rune) (cdr rune))) (inst-trigger (access fc-activation act :inst-trigger)) (inst-hyp (access fc-activation act :inst-hyp)) (hyps (access fc-activation act :hyps)) (unify-subst (access fc-activation act :unify-subst)) (pretty-subst (pairlis$ (strip-cars unify-subst) (pairlis-x2 (strip-cdrs unify-subst) nil))) (k (+ 1 (- (len (access forward-chaining-rule (access fc-activation act :rule) :hyps)) (if (and (consp inst-hyp) (eq (car inst-hyp) :FC-FREE-VARS)) (len hyps) (+ 1 (len hyps))))))) (case level (1 `(,name (:TRIGGER ,inst-trigger) (:BLOCKED-HYP ,k) ,(if (and (consp inst-hyp) (eq (car inst-hyp) :FC-FREE-VARS)) `(:REASON :FREE ,(sublis-var (bind-free-vars-to-unbound-free-vars (all-vars (car hyps)) unify-subst) (car hyps)) ,(len (cddr inst-hyp))) `(:REASON ,inst-hyp)))) (otherwise `( ; This forward-chaining rule: ,rune ; was triggered by this term in the problem: (:TRIGGER ,inst-trigger) ; but is currently blocked waiting for hyp number k: (:BLOCKED-HYP ,k) ; which (either) contains a free var as shown or ; which is this when fully instantiated: ,(if (and (consp inst-hyp) (eq (car inst-hyp) :FC-FREE-VARS)) `(:REASON :FREE ,(sublis-var (bind-free-vars-to-unbound-free-vars (all-vars (car hyps)) unify-subst) (car hyps)) ,(cddr inst-hyp)) `(:REASON ,inst-hyp)) ; with the current unify-sustitution: (:UNIFY-SUBST ,pretty-subst)))))) (defun prettyify-fc-activations (acts level) (cond ((endp acts) nil) (t (cons (prettyify-fc-activation (car acts) level) (prettyify-fc-activations (cdr acts) level))))) (defun make-fc-activation (term rule ttree ens) ; If rule is enabled and the trigger of rule can be instantiated with ; some substitution unify-subst to be term, then we make an ; fc-activation for this pair, otherwise we return nil. Activations ; have rather difficult-to-enforce rules on :inst-hyp and :hyps. For ; example, if the hyp upon which we're stuck contains no free vars, then ; :inst-hyp is supposed to be the instance for which we're looking -- ; but we want to make sure that :inst-hyp cannot be settled by ; evaluation and is not supposed to be forced or split upon. Therefore, ; rather than try to enforce the invariants here we just start every ; activation with an :inst-hyp of *t*. This way we can add new methods ; of establishing a hyp without having to reproduce the code here. ; The initial ttree of the activation is ttree. When we are building an ; activation for a term in the initial clause, this ttree will be nil. ; When we are building an activation for a term derived by some earlier ; round, the ttree will contain its derivation, tagged 'fc-derivation as ; described below. The presence of that derivation in this activation ; will mean that the conclusion we eventually derive must not be worse ; than the conclusion of the derivation from which this term sprang. ; Once upon a time this function did not take the ttree arg and just ; used nil. But that gave rise to infinite loops that were not stopped ; by our worse-than hacks because the terms from which the bad terms ; were derived were not logically dependent on their parents. (cond ((not (enabled-numep (access forward-chaining-rule rule :nume) ens)) nil) (t (mv-let (unify-ans unify-subst) (one-way-unify (access forward-chaining-rule rule :trigger) term) ; Note: We do not start accumulating the persistence of this rule until we ; advance the fc-activation we create below. (cond ((null unify-ans) nil) (t (let ((rule-hyps (access forward-chaining-rule rule :hyps))) (make fc-activation :inst-hyp *t* :hyps rule-hyps :ttree ttree :unify-subst unify-subst :inst-trigger term :rule rule)))))))) (defun make-fc-activations (term rules ttree ens activations) (cond ((endp rules) activations) (t (let ((act (make-fc-activation term (car rules) ttree ens))) (make-fc-activations term (cdr rules) ttree ens (if act (cons act activations) activations)))))) (mutual-recursion (defun collect-terms-and-activations (term ttree wrld ens trigger-terms activations) ; We sweep term and collect (a) every subterm starting with a function ; symbol having forward chaining rules -- whether or not the subterm ; triggers any activations, and (b) every activation of every forward ; chaining rule triggered. We accumulate those two results onto our ; last two arguments and return (mv trigger-terms activations). We do not ; collect activations for the same subterm twice. (cond ((variablep term) (mv trigger-terms activations)) ((fquotep term) (mv trigger-terms activations)) ((or (flambda-applicationp term) (eq (ffn-symb term) 'not)) ; We do not sweep the bodies of lambda expressions nor do we allow NOT ; to trigger forward-chaining rules because printed clauses contain NOTs ; that aren't really there and it would confuse the user. ; Until Version_4.1 we swept the bodies of lambda expressions for ; triggering terms, but we see no point in doing that since the variable ; environment is different. Anything we derived triggered by such a ; term is true (since we only use assumptions from the original clause ; and true derivations) but would very likely be irrelevant because the ; triggering term doesn't actually occur in the problem. (collect-terms-and-activations-lst (fargs term) ttree wrld ens trigger-terms activations)) (t (let ((rules (getprop (ffn-symb term) 'forward-chaining-rules nil 'current-acl2-world wrld))) ; If the term has rules, we collect it and add any activations it ; triggers (though there may be none). But first we see whether we've ; already collected this term and don't do anything if we have. If the ; term doesn't have rules, we don't collect it. In any case, unless ; we've seen the term before, we sweep its args. (cond (rules (cond ((member-equal term trigger-terms) (mv trigger-terms activations)) (t (collect-terms-and-activations-lst (fargs term) ttree wrld ens (cons term trigger-terms) (make-fc-activations term rules ttree ens activations))))) (t (collect-terms-and-activations-lst (fargs term) ttree wrld ens trigger-terms activations))))))) (defun collect-terms-and-activations-lst (terms ttree wrld ens trigger-terms activations) (cond ((endp terms) (mv trigger-terms activations)) (t (mv-let (trigger-terms activations) (collect-terms-and-activations (car terms) ttree wrld ens trigger-terms activations) (collect-terms-and-activations-lst (cdr terms) ttree wrld ens trigger-terms activations))))) ) (defun collect-terms-and-activations-from-fcd-lst (fcd-lst wrld ens trigger-terms activations) ; We map over a list of fc-derivations and treat each :concl as a source ; of trigger terms, each subterm being marked with the fc-derivation tag ; containing its derivation. We accumulate all our changes onto the ; last two arguments and return the extended values of those two lists. (cond ((endp fcd-lst) (mv trigger-terms activations)) (t (mv-let (trigger-terms activations) (collect-terms-and-activations (access fc-derivation (car fcd-lst) :concl) (add-to-tag-tree! 'fc-derivation (car fcd-lst) nil) wrld ens trigger-terms activations) (collect-terms-and-activations-from-fcd-lst (cdr fcd-lst) wrld ens trigger-terms activations))))) ; Now we develop the code to try to advance an activation. We will ; advance each activation as far as possible and then suspend it. Of ; course, many times re-suspending it is a no-op because we will have ; made no progress at all. (mutual-recursion ; These two functions return non-nil when sublis-var (respectively, ; sublis-var-lst) can return a term (resp. list of terms) different from the ; input. (defun sublis-varp (alist term) (declare (xargs :guard (and (symbol-alistp alist) (pseudo-termp term)))) (cond ((variablep term) (assoc-eq term alist)) ((fquotep term) nil) (t (sublis-var-lstp alist (fargs term))))) (defun sublis-var-lstp (alist l) (declare (xargs :guard (and (symbol-alistp alist) (pseudo-term-listp l)))) (if (null l) nil (or (sublis-varp alist (car l)) (sublis-var-lstp alist (cdr l))))) ) (defun mult-search-type-alist (rest-hyps concls term typ type-alist unify-subst ttree oncep keys-seen) ; This function is a variant of search-type-alist that searches for ; all instances of term (other than those listed in keys-seen) bound to a ; subset of type-set typ. It returns three lists in 1:1 correspondence: ; a list of substitutions (which produce those instances), a list of ; tag-trees each extending ttree, and a list of the instances themselves ; (actually EQ to the terms from the type-alist upon which ; one-way-unify1 was called). (cond ((null type-alist) (mv nil nil nil)) ((and (ts-subsetp (cadr (car type-alist)) typ) (not (member-equal (car (car type-alist)) keys-seen))) (mv-let (ans new-unify-subst) (one-way-unify1 term (car (car type-alist)) unify-subst) (cond (ans (let ((diff-alist (alist-difference-eq new-unify-subst unify-subst))) (cond ((or oncep (not (or (sublis-var-lstp diff-alist rest-hyps) (sublis-var-lstp diff-alist concls)))) ; We aren't going to look for additional bindings either because we're not ; supposed to (i.e. oncep is true) or there is no point. In the latter ; case the newly-bound variables do not occur free in the remaining hyps or the ; conclusions of the forward-chaining rule under consideration. So, there is ; no point to looking for additional bindings. (mv (list new-unify-subst) (list (cons-tag-trees (cddr (car type-alist)) ttree)) (list (car (car type-alist))))) ; We found a new unify-subst but there may be additional interesting ones out ; there. (t (mv-let (other-unifies other-ttrees other-instances) (mult-search-type-alist rest-hyps concls term typ (cdr type-alist) unify-subst ttree oncep keys-seen) (mv (cons new-unify-subst other-unifies) (cons (cons-tag-trees (cddr (car type-alist)) ttree) other-ttrees) (cons (car (car type-alist)) other-instances))))))) ; We didn't find any new substitutions; try again. (t (mult-search-type-alist rest-hyps concls term typ (cdr type-alist) new-unify-subst ttree oncep keys-seen))))) (t (mult-search-type-alist rest-hyps concls term typ (cdr type-alist) unify-subst ttree oncep keys-seen)))) (defun mult-lookup-hyp (hyp rest-hyps concls type-alist wrld unify-subst ttree oncep last-keys-seen) ; This function basically takes a hyp and a type-alist. It returns (mv ; new-unify-substs new-ttrees new-last-keys-seen), in which extensions of ; unify-subst that make hyp true under type-alist are listed in 1:1 ; correspondence with extensions of ttree. The function does not consider ; type-alist entries on the keys last-keys-seen and its third result is the ; keys it used this time. ; This function is basically a variant of lookup-hyp. (mv-let (term typ) (term-and-typ-to-lookup hyp wrld) (mult-search-type-alist rest-hyps concls term typ type-alist unify-subst ttree oncep last-keys-seen))) (mutual-recursion (defun ev-respecting-ens (form alist state latches ttree ens wrld) ; This is a variant of ev (see also ev-rec) that avoids calling functions whose ; executable counterparts are disabled. Thus, here we return (mv erp val ; latches ttree), where ev would return (mv erp val latches) and ttree extends ; the given ttree by adding executable-counterpart runes justifying the ; evaluation. If erp is non-nil then val and ttree are to be taken as ; meaningless. (cond ((or (variablep form) (fquotep form)) (mv-let (erp val latches) (ev form alist state latches t nil) (mv erp val latches ttree))) (t (let ((fn (ffn-symb form))) (cond ((or (flambdap fn) (enabled-xfnp fn ens wrld)) (cond ((eq fn 'if) (mv-let (test-er test latches ttree) (ev-respecting-ens (fargn form 1) alist state latches ttree ens wrld) (cond (test-er (mv t test latches ttree)) (test (ev-respecting-ens (fargn form 2) alist state latches (push-lemma '(:EXECUTABLE-COUNTERPART if) ttree) ens wrld)) (t (ev-respecting-ens (fargn form 3) alist state latches (push-lemma '(:EXECUTABLE-COUNTERPART if) ttree) ens wrld))))) (t (mv-let (args-er args latches ttree) (ev-lst-respecting-ens (fargs form) alist state latches ttree ens wrld) (cond (args-er (mv t args latches ttree)) (t (cond ((flambdap fn) (ev-respecting-ens (lambda-body (ffn-symb form)) (pairlis$ (lambda-formals (ffn-symb form)) args) state latches ttree ens wrld)) (t (mv-let (erp val latches) (ev-fncall fn args state latches t nil) (mv erp val latches (push-lemma `(:EXECUTABLE-COUNTERPART ,fn) ttree))))))))))) (t (mv t nil latches ttree))))))) (defun ev-lst-respecting-ens (lst alist state latches ttree ens wrld) (cond ((endp lst) (mv nil nil latches ttree)) (t (mv-let (erp val latches ttree) (ev-respecting-ens (car lst) alist state latches ttree ens wrld) (cond (erp (mv erp val latches ttree)) (t (mv-let (erp rst latches ttree) (ev-lst-respecting-ens (cdr lst) alist state latches ttree ens wrld) (cond (erp (mv erp rst latches ttree)) (t (mv nil (cons val rst) latches ttree)))))))))) ) ; Forward Chaining Derivations - fc-derivations - fcds ; To implement forward chaining, especially to implement the heuristic controls ; on which derived conclusions to keep, we have to use ttrees in a rather ; subtle way that involves embedding a ttree in a tagged object in another ; ttree. These tagged objects holding ttrees are called "fc-derivations" and a ; ttree that (may) contain fc-derivation tags is said to be ``not fcd-free'' ; (i.e., not free of fc-derivation). We speak of type-alists as being fcd-free ; in the obvious way. We motivate and discuss fc-derivation here. However, no ; fc-derivation gets out of the forward chaining module. That is, once ; forward-chain-top has done its job, its returned ttrees are fcd-free. ; When we finally relieve all the hyps we will create the instantiated ; conclusion, concl. After heuristic filtering, approved concls will find ; their way into the type-alist by being assumed true. But within the forward ; chaining module we must be able to track dependencies for two reasons. The ; first reason concerns the ultimate use of such derived conclusions: when we ; have finished all our forward chaining and go into the rewriting of literals ; we will need to choose from among the available forward chained concls those ; that don't depend upon the literal we are rewriting. For this it is ; sufficient to have the ttree of the conclusion with its parent tree markers. ; But the second reason is entirely internal to forward chaining: we need loop ; stopping heuristics and the one we use is that no conclusion is worse than ; any of its immediate supporters (which, transitively means that no conclusion ; is worse than any of its supporters). ; So, associated with each derived conclusion is a derivation. To keep things ; as efficient as possible we don't make these derivations as clean as we ; might! Instead, we basically just store the ttree of each concl together ; with the concl and other information in a record. All such records at the ; "top level" of a ttree are the immediate supporters and one must descend ; recursively into the ttrees of the derivations to get the whole tree. ; This is odd because it results in a ttree being a component of an object ; stored in a ttree. Those interior ttrees are actually hidden from our ttree ; scanners. Before we leave forward chaining we must lift out any important ; information. But within forward chaining this structure is sufficient and ; reasonably efficient. ; An "fc-derivation" is a structure of the form: ; (defrec fc-derivation ; (((concl . ttree) . (fn-cnt . p-fn-cnt)) ; . ; ((inst-trigger . rune) . (fc-round . unify-subst))) ; t) ; Note: This is just an 8-tipped perfectly symmetric tree. We ; contemplated optimizing it for access time to the pieces. Informally, ; we suspect concl, fn-cnt, p-fn-cnt, and ttree, are the most critical ; because of their use in fcd-worse-than-or-equal. We also contemplated ; replacing ttree, inst-trigger, rune, and unify-subst by the ; fc-activation that gave rise to this conclusion, thereby saving the ; time of consing up so much in this record. But (a) the activation is ; not already consed up at the time we build this fc-derivation ``from'' ; it -- we are only holding its pieces in advance-fc-activation2. (b) ; To do that would slow down access to those buried pieces. (c) And ; risk having move the declaration of fc-activations into linear-a.lisp ; too. So we just tear the activation apart and put the pieces into the ; derivation. ; Rune is the name of the rule applied, concl is the instantiated ; conclusion. Fn-cnt is the function symbol count of concl (as computed ; by fn-count) and p-fn-cnt is the pseudo-function count (see ; term-order). These are used in our heuristic for deciding whether to ; keep a concl, as are rune, concl, and inst-trigger. Ttree is the ; ttree that derived concl from name. Inst-trigger is the term in the ; current problem that fired this rule. And fc-round is the number of ; the forward chaining round in which this concl was derived. ; If we decide to keep concl then we make a ttree that contains its ; fc-derivation as its only object, tagged 'fc-derivation. That ttree is ; attached to the assumption of concl in the new type-alist and will ; attach itself to all uses of concl. Given an fc-derivation we can ; reconstruct the derivation of its concl as follows: concl was derived ; by applying name to all of the derived concls in all of the ; 'fc-derivations in its ttree. ; When the forward chaining algorithm is complete we convert the ; recursively nested ttrees in 'fc-derivations to standard ttrees. This ; destroys the information about exactly how concl was derived from its ; supporters but it lifts out and makes visible the 'lemmas and 'pt upon ; which the concl is based. ; Here ends the essay on fc-derivations. Now we develop the code. (defun add-fc-derivations (rune concls unify-subst inst-trigger fc-round ens wrld state ttree fcd-lst) ; Suppose concls is the instantiated concls of a successful forward ; chaining rule. Here we convert each concl in it into an fc-derivation ; We add each fc-derivation to the list fcd-lst and return the final ; fcd-lst. (cond ((null concls) fcd-lst) (t (mv-let (flg concl new-ttree) (eval-ground-subexpressions (car concls) ens wrld state ttree) (declare (ignore flg)) (mv-let (fn-cnt p-fn-cnt) (fn-count concl) (add-fc-derivations rune (cdr concls) unify-subst inst-trigger fc-round ens wrld state ttree (cons (make fc-derivation :fc-round fc-round :rune rune :concl concl :fn-cnt fn-cnt :p-fn-cnt p-fn-cnt :inst-trigger inst-trigger :unify-subst unify-subst :ttree new-ttree) fcd-lst))))))) ; The following function is not used in forward chaining except as a ; trace/debugging tool. Given an fc-derivation, it produces a human ; readable (at least for some humans) form of the derivation. (mutual-recursion (defun prettyify-fc-derivation (fcd level) ; Level is a natural specifying how much detail we want. ``Name'' below ; is just the event name of the rune if there is only one ; forward-chaining rune with that name, e.g., rune is (:FORWARD-CHAINING ; name), or the cdr of the rune otherwise, e.g., (:FORWARD-CHAINING ; name . 3). The idea is to keep the prettyified version short and ; all the runes are :FORWARD-CHAINING ones, while being unambiguous. ; 1: (fc-round concl name) ; 2: (fc-round concl name (:literals ...) . level-0-supporters) ; 3: (fc-round concl name (:literals ...) . level-3-supporters) ; 4: (fc-round concl rune (:unify-subst ...) ; (:literals ...) . level-4-supporters) ; Look at the code for level 4 to see how you read these things. (let* ((fc-round (access fc-derivation fcd :fc-round)) (concl (access fc-derivation fcd :concl)) (rune (access fc-derivation fcd :rune)) (name (if (null (cddr rune)) (cadr rune) (cdr rune))) (unify-subst (access fc-derivation fcd :unify-subst)) (pretty-subst (pairlis$ (strip-cars unify-subst) (pairlis-x2 (strip-cdrs unify-subst) nil)))) (case level (1 `(,fc-round ,concl ,name)) (2 `(,fc-round ,concl ,name (:LITERALS ,@(collect-parents (access fc-derivation fcd :ttree))) ,@(prettyify-fc-derivations (tagged-objects 'fc-derivation (access fc-derivation fcd :ttree)) 0))) (3 `(,fc-round ,concl ,name (:LITERALS ,@(collect-parents (access fc-derivation fcd :ttree))) ,@(prettyify-fc-derivations (tagged-objects 'fc-derivation (access fc-derivation fcd :ttree)) 3))) (otherwise `( ; Forward chaining round: ,fc-round ; produced the new fact: ,concl ; via the rule ,rune ; and unify-subst: (:UNIFY-SUBST ,@pretty-subst) ; relying on these literals from the original clause to relieve some of ; the hyps: (:LITERALS ,@(collect-parents (access fc-derivation fcd :ttree))) ; and relying on these facts from earlier rounds for the other hyps: ,@(prettyify-fc-derivations (tagged-objects 'fc-derivation (access fc-derivation fcd :ttree)) 4)))))) (defun prettyify-fc-derivations (fcd-lst level) (cond ((null fcd-lst) nil) (t (cons (prettyify-fc-derivation (car fcd-lst) level) (prettyify-fc-derivations (cdr fcd-lst) level))))) ) (mutual-recursion (defun expunge-fc-derivations-lst (fc-derivation-lst ttree) (cond ((endp fc-derivation-lst) ttree) (t (push-lemma (access fc-derivation (car fc-derivation-lst) :rune) (cons-tag-trees (expunge-fc-derivations (access fc-derivation (car fc-derivation-lst) :ttree)) (expunge-fc-derivations-lst (cdr fc-derivation-lst) ttree)))))) (defun expunge-fc-derivations (ttree) ; Ttree is a not fcd-free and we make it fcd-free. In particular, we ; copy ttree, replacing each 'fc-derivation in it by a new node which ; tags the rule name with 'lemma and lifts out the interior ttrees and ; expunges them. Thus, when we are done we have a ttree with no ; 'fc-derivation tags, but which has 'lemma tags on the set of names in ; the 'fc-derivations and which has all of the 'pt objects and ; 'assumptions (for example) that were recursively embedded in ; 'fc-derivations. ; Note: This function must be able to find 'fc-derivations anywhere within the ; ttree. In particular, before we removed ttrees from the type-alists in ; 'assumptions, we had to expunge the fc-derivations within the type-alists. ; See the comment in force-assumptions. Remember that 'fc-derivations are for ; heuristic use only, except that they may contain 'pt and 'assumption objects ; that we must lift out. So we should be ruthless about finding and expunging ; all 'fc-derivations. ; Once upon a time we detected an 'fc-derivation at the end of prove. It ; slipped into the final proof tree as follows: Forward chaining made two ; rounds. During the first, hyp1 was concluded. During the second, hyp2 was ; concluded and forced an assumption. That assumption contained the type-alist ; produced from the first round, which had the 'fc-derivation for hyp1. Now if ; forward-chaining had proved the theorem, we would be in good shape. But ; suppose it doesn't prove the theorem and we start rewriting. Suppose the ; rewriter appeals to hyp2. That causes it to raise the assumption. We then ; try, at the end of rewrite-clause, to relieve the assumption by rewriting it ; under its type-alist. Suppose that we use hyp1 during that successful ; relieving of the assumption: its 'fc-derivation then enters our final proof ; tree. Here is a script that used to provoke this bug. The fix, below, is to ; expunge fc-derivations from the :type-alists of assumptions. We keep this ; script simply because it took a while to find the path down which the ; 'fc-derivation migrated out of forward-chaining. ; (er-progn ; (defstub hyp1 (x) t) ; (defstub hyp2 (x) t) ; (defstub trig (x) t) ; (defstub assumptionp (x) t) ; (defstub concl (x) t) ; ; (defaxiom fc-to-hyp1 ; (hyp1 (trig x)) ; :rule-classes ((:forward-chaining :trigger-terms ((trig X))))) ; ; (defaxiom then-fc-to-hyp2 ; (implies (and (hyp1 x) (force (assumptionp x))) ; (hyp2 x)) ; :rule-classes :forward-chaining) ; ; (defaxiom in-rewrite-use-hyp2-thus-raising-the-assumption ; (implies (hyp2 x) (concl x))) ; ; (defaxiom and-relieve-the-assumption-by-appeal-to-hyp1-sucking-in-the-fc-deriv ; (implies (hyp1 x) (assumptionp x))) ; ; (thm (concl (trig a)))) (let ((objects (tagged-objects 'fc-derivation ttree))) (cond (objects (expunge-fc-derivations-lst objects (remove-tag-from-tag-tree! 'fc-derivation ttree))) (t ttree)))) ) ; A Reporting Facility for Forward Chaining ; We now describe our design for a reporting facility for forward chaining. ; The facility is designed to help answer the question ``What happens with the ; attempt to use ?'' where the rules of interest ; are described with some ``criteria'' defined below. ; What should be displayed as the answer? ; (1) The clause being worked on (once we thought the clause-id was a good ; idea, but not every clause given to forward-chain-top has a clause-id). ; (2) The final status of every rule activated that met the criteria. ; By ``final status'' we mean the rune, instantiated trigger, full ; unify-substitution, and disposition of every rule that meets the criteria. ; By ``disposition'' we mean one of these tuples: ; (a) SUCCESS ADDED -- successfully fired and gave us ; (b) SUCCESS REJECTED -- successfully fired but conclusion was ; disapproved ; (c) BLOCKED UNRELIEVED-HYPx -- unable to relieve ; ; UNRELIEVED-HYPx is either UNRELIEVED-HYP or ; UNRELIEVED-HYP-FREE to indicate whether hyp has ; free vars. But hyp is printed with the ; unify-subst applied and with UNBOUND-FREE-vars ; in place of the free vars. We include the ; UNRELIEVED-HYP-FREE tag just to make it easier ; to mechanically recognize the presence of free ; vars. (d) BLOCKED FALSE -- hyp shown ; false ; The ``criteria'' is a list of triples, sometimes called a ``criterion.'' ; Each criterion consist of a rune, an inst-trigger, and a concl. All three ; parts of a criterion are optional and we use t to indicate the absence of a ; part. An activation satisfies the criteria if it satisfies one of the ; criterion. An activation satisfies a criterion if it satisfies each of the ; provided (non-t) parts. An activation satisfies the rune (or inst-trigger) ; part if the activation's rune (or inst-trigger) is the criterion's rune (or ; inst-trigger). However, because of free variables we cannot always know if a ; still-active activation will produce the conclusion of the activation we ; seek. If an activation has free variables in it, the best we can do is ; determine whether conclusion of the rule, under the unify-subst, can be made ; to match the concl we seek. Therefore, an activation satisfies the concl ; part if the concl of its rule matches (with one-way-unify1 extending ; unify-subst) the concl we seek. (To be precise, fc rules have :concls and we ; wish to know whether some member of the :concls of the rule matches the concl ; we seek.) This is as good as an equality check if the unify-subst is ; complete on the variables in the concl. ; How can we collect and display this information? ; We will work inside a wormhole named the ``fc-wormhole''. ; The wormhole-data shall consist of an alist with the following keys: ; :CRITERIA - a list of triples ; :REPORT-ON-THE-FLYP - t if we are to print reports every time ; forward-chain-top is called, nil if we are to just save the data for browsing ; later. ; :FORWARD-CHAIN-CALLS - an alist pairing a ``call number'' n to an alist with ; the following keys. The order of the keys in this alist is not necessarily ; that shown below. We manipulate the alist only with assoc-eq and ; put-assoc-eq, but we initialize it with the keys in an ``optimized'' order. ; :INPUT - all of the arguments of this call of forward-chain-top, except ; for WRLD, ENS, STATE. We omit the first two because they make it hard to ; print the wormhole state. We omit the last for obvious reasons. The ; omission of ens makes the stored data actually inadequate to reproduce ; the call, since the ens used might be a locally installed :in-theory. The ; arguments include caller, so these are really calls of forward-chain-top! ; :ROUNDS - how many forward-chaining rounds were used ; :OUTPUT - the output values returned by this call, as a triple: (flg ; type-alist ttree-or-fc-pairs). The semantics of this triple is that if ; flg is t, then forward-chain-top found a contradiction, type-alist is nil ; and ttree-or-fc-pairs is an fcd-free ttree explaining the contradiction; ; on the other hand, if flg is nil, then forward-chain-top did not find a ; contradiction, type-alist is an fcd-free type-alist extending the ; original one with what we know and ttree-or-fc-pairs is a list of pairs ; of the form (concl . ttree) where each concl is a derived conclusion and ; its ttree is fcd-free. See forward-chain-top. ; If the :OUTPUT value is nil instead of a triple, it means the call was ; interrupted before we stored the final values. ; (1) :BLOCKED-FALSE-SATISFYING-ACTIVATIONS - every time we abandon a ; satisfying activation because its hyp is false, we add it to this list; ; note that we will have to do some work to install inst-hyp, etc. into ; the activation act0 just detected by advance-fc-activationi. ; (2) :ALL-SATISFYING-FC-DERIVATIONS - every time we make an fc derivation from a ; satisfying activation, we save the fc-derivation here. ; (3) :APPROVED-SATISFYING-FC-DERIVATIONS - every time we approve a satisfying ; fc-derivation we save the fc-derivation here. ; (4) :LEFTOVER-ACTIVATIONS - all activations still suspended at the ; termination of of forward chaining ; (5) :REDUNDANT-APPROVED-FC-DERIVATIONS - every time we assume an approved ; derived conclusion true, we check to see whether it changes the ; type-alist. If not, we put the fc-derivation on this list. ; For brevity we sometimes call the last five lists ``sites'' and number them ; as seen. For example, we'll ask whether an fc-derivation ``is a member of ; site (3).'' ; Note that there are three levels of alist here. We call the top one ``the ; fc-wormhole data.'' We call the second level one, the ``calls alist'', and ; we call the third level one the ``call alist.'' That is, the fc-wormhole ; data is an alist with two keys, one of which is :FORWARD-CHAIN-CALLS. The ; value of that particular key is the calls alist, which is an alist with n ; numeric keys. There is a key for each time forward-chain-top has been ; called. The calls alist is ordered with the largest key first. Suppose k is ; the call number of the most recent call of forward chain. Then the value of ; k in the calls alist is a call alist, which has :INPUT, :ROUNDS, :OUTPUT, and ; the four sites as its keys. (defun current-fc-call-number (data) ; See paragraph above. (car (car (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))))) (defun current-fc-call-alist (data) ; See paragraph above. (cdr (car (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))))) (defun put-current-fc-call-alist (call-alist data) ; See paragraph above. (let* ((calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (temp (car calls-alist)) (k (car temp))) ; current-fc-call-number (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons k call-alist) (cdr calls-alist)) data))) ; When prove is first called, we initialize the fc-wormhole data by clearing ; the calls-alist but leaving the :CRITERIA and :REPORT-ON-THE-FLYP settings as ; is. The user is responsible for them. All of our code is written to ; optimize the case where the :CRITERIA is nil. In that case, we come as close ; as we can to doing nothing at all about tracking forward-chaining. ; To allow the user maintain the criteria and reporting flag, we provide these ; very basic primitives. (defun initialize-fc-wormhole-sites () ; This function initializes the fc-wormhole and is called in prove. (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((data (wormhole-data whs))) (set-wormhole-data whs `((:CRITERIA ,@(cdr (assoc-eq :criteria data))) (:REPORT-ON-THE-FLYP . ,(cdr (assoc-eq :REPORT-ON-THE-FLYP data))) (:FORWARD-CHAIN-CALLS . nil))))) nil)) (deflabel forward-chaining-reports :doc ":Doc-Section forward-chaining-reports to see reports about the forward chaining process~/ Debugging forward-chaining rules can be hard because their effects are not directly visible on the goal. In this documentation we tell you how to get reports on the forward chaining activity occurring in your proof attempts. This documentation is written in several parts. The first part is an introduction for the first-time user of forward chaining reports. The next two parts describe how to read reports. The last part describes how to monitor forward chaining activity only for selected runes, etc. We recommend the new user of these reports read everything!~/ ~em[A Quick Introduction to Forward Chaining Reports] Caution: The reporting mechanism maintains some state and if you have already used forward chaining reporting in a session the directions below may not work as advertised! To return to the default forward chaining reporting state, execute this form at the top level: ~bv[] (reset-fc-reporting) ~ev[] You can get a report about all forward chaining activity in subsequent proofs by doing: ~bv[] (set-fc-criteria t) ~ev[] Options will be discussed later that allow you to monitor the activity caused by particular ~c[:forward-chaining] rules or terms. Then do a proof that is expected to cause some forward chaining. In the proof output you will see lines like this: ~bv[] (Forward Chaining on behalf of PREPROCESS-CLAUSE: (FC-Report 1)) ~ev[] This is the only difference you should see in the proof output. After the proof attempt has terminated, you can execute: ~bv[] (fc-report k) ~ev[] for any ~c[k] printed during the immediately preceding proof attempt. That will print a much longer report describing the activity that occurred during the ~c[k]th use of forward chaining in that proof attempt. If you want to see these reports in real time (embedded in the proof output), do this before invoking the prover: ~bv[] (set-fc-report-on-the-fly t) ~ev[] Collecting the data used to generate these reports slows down the prover. If you no longer wish to see such reports, do ~bv[] (set-fc-criteria nil) ~ev[] ~em[How To Read FC Reports] The report printed by ~c[(fc-report k)] is of the form: ~bv[] Forward Chaining Report ~em[k]: Caller: ~em[token] Clause: (~em[lit1] ~em[lit2] ... ~em[litn]) Number of Rounds: ~em[m] Contradictionp: ~em[bool] Activations: (~em[act1] ~em[act2] ...) ~ev[] This report means that the ~em[k]th use of forward chaining in the most recent proof attempt was done on behalf of ~em[token] (see below). The initial context (set of assumptions) consisted of the negations of the literals listed in the clause shown and the initial candidate trigger terms are all those appearing in that clause. This invocation of forward chaining proceeded to do ~em[m] rounds of successive extensions of the initial context and ultimately either reached a contradiction (~em[bool] = ~c[T]) or returned an extended context (~em[bool] = ~c[NIL]). Note that reaching a contradiction from the negations of all the literals in a clause is ``good'' because it means the clause is true. The report concludes with the final status of all the forward chaining rules fired during the process. We explain how to read one of these activation reports in the next section. Forward chaining is done on behalf of many proof techniques in the system. Each is associated with a ~em[token]. The main proof technique that uses forward chaining is ~c[SIMPLIFY-CLAUSE]. This is the call of forward chaining that sets up the context used by the rewriter to relieve hypotheses during backchaining. Another common caller of forward chaining is ~c[PREPROCESS-CLAUSE], the first process in the ACL2 waterfall (~pl[hints-and-the-waterfall]). Forward chaining often proves ``near propositional'' goals (those depending just on boolean implications between basic predicates). Other tokens you may see include ~c[INDUCT], which uses forward chaining to set up a context for applying ~c[:]~ilc[induction] rules, and the definitional principle (and related utilities such as ~ilc[verify-termination] and ~ilc[verify-guards]) which uses forward chaining during the construction of both measure conjectures and guard conjectures. When used this way, the ~em[token] is ~c[defun-or-guard-verification]. ~em[How to Read Activation Reports] The forward chaining report concludes with a list of activation reports. ~bv[] Activations: (~em[act1] ~em[act2] ...) ~ev[] Each ~em[acti] is of the form: ~bv[] (~em[rune] (:TRIGGER ~em[inst-trig]) ((:UNIFY-SUBST ~em[subst]) (:DISPOSITION ~em[outcome-part1] ~em[outcome-part2] ~em[inst-term])) ...) ~ev[] where the ~c[...] indicates that the rest of the report consists of more of those tuples listing a ~c[:UNIFY-SUBST] and ~c[:DISPOSITION]. We call each tuple a ~em[disposition] of the activation and each disposition describes a substitution ~em[subst] identifying the final instantiation of the rule and how the activation fared. Suppose there are ~em[n] dispositions. (If the rule in question contains no free variables, ~em[n] will be 1.) This activation report means that during the forward chaining process in question, the ~c[:]~ilc[forward-chaining] ~il[rune] ~em[rune] was fired due to the presence in the evolving context of the trigger term ~em[inst-trig]. (Note that ~em[inst-trig] is an instantiation of the trigger term of the named rule. That is, the variable symbols you see in ~em[inst-trig] are those of the clause printed in the forward chaining report.) The activation of ~em[rune] by ~em[inst-trig] proceeded to split ~em[n] ways as different choices were made for the ~il[free-variables] occuring among the hypotheses. Each of those ~em[n] choices gave rise to a different substitution ~em[subst], and each succeeded or failed as described by the corresponding ~c[:DISPOSITION]. The ~c[:DISPOSITION] of an activation is described in three parts, ~em[outcome-part1], ~em[outcome-part2], and ~em[inst-term]. ~em[Outcome-part1] is either ~c[SUCCESS] or ~c[BLOCKED], meaning that the instance given by ~em[subst] either succeeded in the sense that all of its instantiated hypotheses were found in the context, or failed because some instantiated hypothesis was not found. If ~c[outcome-part1] is ~c[SUCCESS] then ~em[inst-term] is the instantiated conclusion produced by the rule. ~em[Outcome-part2] is ~c[APPROVED] if the instantiated conclusion was acceptable to our heuristics designed to prevent looping and not already known in the evolving context. ~em[Outcome-part2] is ~c[REJECTED] if the instantiated conclusion was not approved by our heuristics. ~em[Outcome-part2] is ~c[REDUNDANT] if the instantiated conclusion was approved by the heuristics but already known true in the current evolving context. If ~c[APPROVED], the truth of the instantiated conclusion is added to the evolving context. Otherwise, it is not. If ~c[outcome-part1] is ~c[BLOCKED] then ~c[outcome-part2] is one of three possible things: ~c[FALSE], in which case ~em[inst-term] is an instantiated hypothesis of the rule that is assumed false in the current context, ~c[UNRELIEVED-HYP], in which case ~em[inst-term] is an instantiated hypothesis whose truthvalue is not determined by the context, or ~c[UNRELIEVED-HYP-FREE], in which case ~em[inst-term] is an oddly instantiated hypothesis whose truthvalue is not determined by the context and which also contains free variables. In the last case, the ``odd'' instantiation was by the substitution ~em[subst] but extended so that free variables in the hypothesis are renamed to start with the prefix ~c[UNBOUND-FREE-] to draw your attention to them. Note: All of the terms printed in the report are instantiated with the relevant unifying substitution (possibly extended to bind free variables). ~em[Specifying the Tracking Criteria] During a proof attempt, the forward chaining module stores information about the activations satisfying certain criteria. The ~em[criteria] is a list of triples. Each triple consists of a forward chaining rune, an instantiated trigger term, and an instantiated conclusion to watch for. However, any or all of the components of such a triple may be ~c[t] and that is given special significance. An activation ~c[satisfies] a criteria if it satisfies at least one of the triples. An activation satisfies a triple if it satisfies all three of the components. Every activation satisfies the component ~c[t]. An activation satisfies a rune if the activation describes a firing of the named rule. An activation satisfies an instantiated trigger term if the activation was created by that trigger being present in the context. An activation satisfies an instantiated conclusion if the activation ~em[could] produce the instantiated conclusion (with the right choice of any free variables). Thus, the criteria is interpreted as a disjunction of conjunctions, making it possible to track a specific set of runes, triggers, and conclusions. For example, here is a triple that might appear in the criteria: ~bv[] ((:FORWARD-CHAINING ALISTP-FORWARD-TO-TRUE-LISTP) t t). ~ev[] This triple would cause every activation of the given rule to be tracked. However, the triple ~bv[] ((:FORWARD-CHAINING ALISTP-FORWARD-TO-TRUE-LISTP) (ALISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S)))) t) ~ev[] would only track activations of that rule fired by the specific term shown as the second element of the triple. Futhermore ~bv[] (t (ALISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S)))) t) ~ev[] would track any forward chaining rule triggered by that term, and ~bv[] (t t (TRUE-LISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S))))) ~ev[] would track any rule fired by any trigger that might lead to the specific term given as the third component above. Note: The condition on how an activation satisfies an instantiated conclusion is a little subtle. Consider the activation of the forward chaining rule ~bv[] (implies (and (symbol-listp x) (equal (len x) (len y))) (true-listp (make-bindings x y))) ~ev[] triggered by ~c[(SYMBOL-LISTP VARS)] arising in the current context. This activation ~em[could] produce the specific conclusion shown in the last triple above, if it just happened that ~c[(TOP-N (OP1 INST) (STACK S))] were chosen as the binding of the free variable ~c[y]. Thus, the activation of this rule triggered by ~c[(SYMBOL-LISTP VARS)] satisfies the last triple above. Observe that the triple ~bv[] (t t t) ~ev[] is satisfied by every activation of any rule by any trigger term producing any conclusion. The function ~c[set-fc-criteria] sets the criteria describing which activations are to be tracked. For example, if you execute: ~bv[] (set-fc-criteria ((:FORWARD-CHAINING LEMMA1) t t) ((:FORWARD-CHAINING LEMMA2) (ALISTP (BASIC-MAPPER A B)) t) (t t (TRUE-LISTP (DLT D)))), ~ev[] the system would track all activations of the forward-chaining rule ~c[LEMMA1], plus those activations of forward-chaining rule ~c[LEMMA2] triggered by the term given in the second triple, plus any activation of any rule that might derive ~c[(TRUE-LISTP (DLT D))]. Because criteria generally mention variable symbols used in a specific conjecture, it is probably best to reconsider your criteria every time you want to track forward chaining. If the criteria is ~c[nil], then nothing is tracked. Setting the criteria to ~c[nil] is the way you turn off tracking and reporting of forward chaining activity. You may do this either by ~c[(set-fc-criteria)] or by ~c[(set-fc-criteria nil)]. (Technically the second form is an odd use of ~c[set-fc-criteria], which expects any supplied arguments to be triples; if the ``triple'' ~c[nil] is the only one supplied, we take it to mean that the entire criteria should be ~c[nil].) To track every forward chaining activation you may set the criteria with either ~c[(set-fc-criteria (t t t))] or use the abbreviation ~c[(set-fc-criteria t)]. If, when you read a forward chaining report, you see no mention of an activation you have in mind, e.g., of a certain rune or deriving a certain conclusion, and you have set the criteria correctly, then the activation never happened. (This is akin to using ~c[:]~ilc[brr] and ~c[:]~ilc[monitor] to monitor the application of a rewrite rule and then seeing no interactive break.) For some relevant functions to help you manage criteria and when the full reports are printed see ~ilc[fc-report], ~ilc[show-fc-criteria], ~ilc[set-fc-criteria], ~ilc[reset-fc-reporting], and ~ilc[set-fc-report-on-the-fly].") (defun show-fc-criteria () ":Doc-Section Forward-Chaining-Reports print the forward-chaining tracking criteria~/ ~bv[] Example: (show-fc-criteria) ~ev[] This function prints the list of triples being used to determine what is tracked during forward chaining.~/ ~l[forward-chaining-reports] for details." (wormhole-eval 'fc-wormhole '(lambda (whs) (prog2$ (cw "Forward Chaining Tracking Criteria:~%~x0~%" (cdr (assoc-eq :CRITERIA (wormhole-data whs)))) whs)) nil)) (defun reset-fc-reporting () ; This user-level function resets the criteria but leaves the on-the-fly flg as ; last set. All data is wiped out. ":Doc-Section Forward-Chaining-Reports reset the forward-chaining tracking state to its initial configuration~/ ~bv[] Example: (reset-fc-reporting) ~ev[] This function erases all forward chaining tracking criteria and sets the on-the-fly reporting flag to ~c[nil.] The next time you set the criteria (~pl[set-fc-criteria]) the short form of reports, in which only the caller and the fc-report number is printed, will appear in your proof logs.~/ ~l[forward-chaining-reports] for details." (wormhole-eval 'fc-wormhole '(lambda (whs) (set-wormhole-data whs '((:CRITERIA . nil) (:REPORT-ON-THE-FLYP . nil) (:FORWARD-CHAIN-CALLS . nil)))) nil)) (defun translate-fc-criterion (x state) (cond ((and (true-listp x) (equal (length x) 3)) (let ((rune (car x)) (inst-trigger (cadr x)) (concl (caddr x))) (cond ((not (or (eq rune t) (and (runep rune (w state)) (eq (car rune) :forward-chaining)))) (er soft 'set-fc-criteria "~x0 is not a :FORWARD-CHAINING rune." rune)) (t (er-let* ((inst-trigger (cond ((eq inst-trigger t) (value t)) (t (translate inst-trigger t t t 'add-fc-criterion (w state) state)))) (concl (cond ((eq concl t) (value t)) (t (translate concl t t t 'add-fc-criterion (w state) state))))) (value (list rune inst-trigger concl))))))) (t (er soft 'set-fc-criteria "Each element of a criteria must be a triple, (rune inst-trigger ~ inst-concl), where rune is a :FORWARD-CHAINING rune or t, ~ inst-trigger is a term or t, and inst-concl is a term or t. ~ But ~x0 is not of this form." x)))) (defun translate-fc-criteria (lst state) ; We either cause an error or return a properly translated forward chaining ; criteria. Recall that a criteria is a true-list of triples, each of the form ; (rune inst-trigger inst-concl), where any of the three components may be nil ; but when a component is not nil, the rune must be a rune, and the other two ; must be terms. (cond ((atom lst) (cond ((equal lst nil) (value nil)) (t (er soft 'set-fc-criteria "The criteria must be a true-list.")))) (t (er-let* ((triple (translate-fc-criterion (car lst) state)) (rest (translate-fc-criteria (cdr lst) state))) (value (cons triple rest)))))) (defun set-fc-criteria-fn (x state) ; Warning: Keep this in syc with set-waterfall-parallelism-fn. (er-let* ((criteria (cond ((equal x '(nil)) (value nil)) #+acl2-par ; the following test is always false when #-acl2-par ((f-get-global 'waterfall-parallelism state) (er soft 'set-fc-criteria "It is illegal to track forward-chaining when ~ waterfall-parallelism is enabled. ")) ((equal x '(t)) (value '((t t t)))) (t (translate-fc-criteria x state))))) (prog2$ (wormhole-eval 'fc-wormhole '(lambda (whs) (set-wormhole-data whs (put-assoc-eq :CRITERIA criteria (wormhole-data whs)))) nil) (value nil)))) (defmacro set-fc-criteria (&rest x) ":Doc-Section Forward-Chaining-Reports to set the tracking criteria for forward chaining reports~/ ~bv[] Examples: (set-fc-criteria) ; shut off all tracking (set-fc-criteria nil) (set-fc-criteria t) ; to track all forward chaining (set-fc-criteria (t t t)) (set-fc-criteria ((:FORWARD-CHAINING LEMMA1) ; track all uses of LEMMA1, t t) ((:FORWARD-CHAINING LEMMA2) ; uses of LEMMA2 triggered (ALISTP (BASIC-MAPPER A B)) ; by this specific ALISTP term t) (t t (TRUE-LISTP (DLT D)))) ; and every rule deriving ; this TRUE-LISTP term. General Forms: (set-fc-criteria nil) (set-fc-criteria t) (set-fc-criteria triple1 triple2 ...) ~ev[] where each triple is of the form ~c[(rune inst-trigger inst-concl)]. If rune is non-~c[t] is must be a forward chaining ~il[rune]. The other two components, ~c[inst-trigger] and ~c[inst-concl], if non-~c[t], must be terms. The list of all the triples supplied is called the ``criteria.'' In the form ~c[(set-fc-criteria nil)], the criteria used is the empty list of triples. (Technically, supplying ~c[nil] as a triple ``ought'' to be an error, but it is a more ``natural'' way to say the list of criteria is empty than to use the correct form ~c[(set-fc-criteria)].) In the form ~c[(set-fc-criteria t)] the criteria used is the list containing the single triple ~c[(t t t)]. This function sets the tracking criteria for forward chaining reporting. ~l[forward-chaining-reports] for a general discussion of tracking and reporting forward chaining activity.~/ The forward chaining criteria is a list of triples. Think of it as representing a disjunction of conjunctions. The activation of a ~c[:]~ilc[forward-chaining] rune by some triggering term in the current context ~em[satisfies] the criteria if it satisfies one of the triples. To satisfy the triple ~c[(rune inst-trigger inst-concl)], the activation must satisfy each component of the triple. Any ~c[t] component is always satisfied. If ~c[rune] is non-~c[t] it is satisfied if the activation is for the given rune. If ~c[inst-trigger] is non-~c[t], it is satisfied if the activation is triggered by the given term. (``~c[Inst-trigger''] stands for ``instantiated trigger.'' It is not the trigger term of the rule but is supposed to be an instance of that term that you believe will arise in some proof attempt you are debugging -- an instance you want to ``watch'' as it fires the rule.) If ~c[inst-concl] is non-~c[t], it is satisfied if the activation could possibly derive the conclusion given. (Again, ``~c[inst-concl''] stands for ``instantiated conclusion'' and shows the term in your problem that you expect to be derived by forward chaining.) Note that if the criteria is empty, it is never satisfied, so tracking is turned off. If the criteria is the singleton list containing just the triple ~c[(t t t)], then every activation satisfies it and so all ~c[:forward chaining] rules are tracked. ~l[forward-chaining-reports] for details." `(set-fc-criteria-fn ',x state)) (defun set-fc-report-on-the-fly (flg) ; This function allows the user to set the flag that determines whether we do ; on-the-fly reporting (flg = t) or not (flg = nil) during forward chaining. ":Doc-Section Forward-Chaining-Reports to determine when forward-chaining reports are printed~/ ~bv[] Examples: (set-fc-report-on-the-fly t) (set-fc-report-on-the-fly nil) ~ev[] If the flag is set to ~c[t], forward chaining tracking reports are printed when forward chaining occurs. If the flag is set to ~c[nil], very short reports (giving just the caller and the report number) are printed during forward chaining but you can use ~ilc[fc-report] to see the full report afterwards. Since nothing is tracked when the criteria is ~c[nil], this function also prints out the current criteria to remind you of what it is. The flag manipulated by this function does not shut off tracking. It only determines whether reports are printed on-the-fly or not. To shut off tracking ~ilc[set-fc-criteria].~/ ~l[forward-chaining-reports] for details." (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((data (wormhole-data whs))) (prog2$ (cond (flg (cond ((cdr (assoc-eq :criteria data)) (cw "On-the-fly reporting of forward-chaining activity is ~ enabled. The criteria being tracked are: ~x0.~%" (cdr (assoc-eq :criteria data)))) (t (cw "On-the-fly reporting of forward-chaining activity is enabled ~ but no data will be collected because there are no criteria.~%")))) ((cdr (assoc-eq :criteria data)) (cw "On-the-fly reporting of forward-chaining activity is disabled. ~ The criteria being tracked are: ~x0.~%" (cdr (assoc-eq :criteria data)))) (t (cw "On-the-fly reporting of forward-chaining activity is disabled ~ but no data will be collected because there are no criteria.~%"))) (set-wormhole-data whs (put-assoc-eq :REPORT-ON-THE-FLYP flg data))))) nil)) ; When forward-chain-top is called, we add a new entry to the calls-alist: (defun new-fc-call (caller cl pts force-flg do-not-reconsiderp wrld ens oncep-override) ; Once upon a time we stored all the arguments (except state) in :INPUT. ; However, that makes it really hard to print whs because it contains many ; copies of world and ens. So we just print those symbols, not their ; values. This is inadequate to reproduce the call, since the ens ; might be local to the goal. (declare (ignore wrld ens)) (wormhole-eval 'fc-wormhole '(lambda (whs) (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data)))) (cond ((cdr (assoc-eq :CRITERIA data)) (set-wormhole-data whs (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons (+ 1 (or (car (car calls-alist)) 0)) ; may be first time `((:BLOCKED-FALSE-SATISFYING-ACTIVATIONS . nil) (:ALL-SATISFYING-FC-DERIVATIONS . nil) (:APPROVED-SATISFYING-FC-DERIVATIONS . nil) (:LEFTOVER-ACTIVATIONS . nil) (:INPUT . ,(list caller cl pts force-flg do-not-reconsiderp 'wrld 'ens oncep-override 'state)) (:ROUNDS . nil) (:OUTPUT . nil))) calls-alist) data))) (t whs)))) :no-wormhole-lock)) ; As forward-chain-top operates, it monitors the activations it creates and ; records certain information. First, we must be able to determine whether an ; activation satisfies the criteria. It is convenient to here develop several ; notions of satisfaction. Bear with us. ; Here is the definition of when an fc-activation satisfies a criterion. It ; uses the notion of whether some concl in the concls of a forward-chaining ; rule matches a given term, e.g., whether the term is a ``member (modulo ; unification)'' of the concls. (defun member-one-way-unify1 (term pat-lst unify-subst) ; We return t or nil to indicate whether some member of pat-lst unifies with ; term, extending unify-subst. (cond ((endp pat-lst) nil) (t (mv-let (flg alist) (one-way-unify1 (car pat-lst) term unify-subst) (declare (ignore alist)) (cond (flg t) (t (member-one-way-unify1 term (cdr pat-lst) unify-subst))))))) (defun satisfying-fc-activation1p (criterion act) (let ((rune (car criterion)) (trig (cadr criterion)) (concl (caddr criterion)) (rule (access fc-activation act :rule))) (and (or (eq rune t) (equal rune (access forward-chaining-rule rule :rune))) (or (eq trig t) (equal trig (access fc-activation act :inst-trigger))) (or (eq concl t) (member-one-way-unify1 concl (access forward-chaining-rule rule :concls) (access fc-activation act :unify-subst)))))) ; And then we can conjoin that across a criteria (list of ``criterions''). (defun satisfying-fc-activationp (criteria act) (cond ((endp criteria) nil) (t (or (satisfying-fc-activation1p (car criteria) act) (satisfying-fc-activationp (cdr criteria) act))))) (defun collect-satisfying-fc-activations (criteria acts ans) ; Accumulate all satisfying fc-activations in acts onto ans. (cond ((endp acts) ans) ((satisfying-fc-activationp criteria (car acts)) (collect-satisfying-fc-activations criteria (cdr acts) (cons (car acts) ans))) (t (collect-satisfying-fc-activations criteria (cdr acts) ans)))) ; The notion of a satisfying fc-activation applies naturally to the ``virtual ; activations'' manipulated in advance-fc-activation1, 2, and 3, where we have ; an activation represented by some initial version of it, act0, together with ; the current values of fields :inst-hyp, :hyps, :unify-subst, and :ttree -- ; but without those values actually deposited in the activation. The only one ; of the virtual fields that is relevant to satisfiability is the unify-subst. (defun satisfying-virtual-fc-activation1p (criterion act0 unify-subst) ; Here we define the analog of satisfying-fc-activationp except that the ; activation we assess is a ``virtual'' one obtained by putting unify-subst ; into the act0. The functions that advance fc-activations traffic in ; ``virtual'' activations represented by some initial act0 and the current ; values intended to occupy the inst-hyp, hyps, unify-subst, and ttree fields. ; But of those ``virtual'' fields, the only one that affects satisfiability is ; unify-subst. (let ((rune (car criterion)) (trig (cadr criterion)) (concl (caddr criterion)) (rule (access fc-activation act0 :rule))) (and (or (eq rune t) (equal rune (access forward-chaining-rule rule :rune))) (or (eq trig t) (equal trig (access fc-activation act0 :inst-trigger))) (or (eq concl t) (member-one-way-unify1 concl (access forward-chaining-rule rule :concls) unify-subst))))) (defun satisfying-virtual-fc-activationp (criteria act0 unify-subst) (cond ((endp criteria) nil) (t (or (satisfying-virtual-fc-activation1p (car criteria) act0 unify-subst) (satisfying-virtual-fc-activationp (cdr criteria) act0 unify-subst))))) ; The notion of a satisfying fc-activation extends naturally to the notion of a ; satisfying fc-derivation. However, by the time we get to fc-derivations we ; can check equality of the instantiated conclusion to the concl sought. (defun satisfying-fc-derivation1p (criterion fcd) (let ((rune (car criterion)) (trig (cadr criterion)) (concl (caddr criterion))) (and (or (eq rune t) (equal rune (access fc-derivation fcd :rune))) (or (eq trig t) (equal trig (access fc-derivation fcd :inst-trigger))) (or (eq concl t) (equal concl (access fc-derivation fcd :concl)))))) (defun satisfying-fc-derivationp (criteria fcd) (cond ((endp criteria) nil) (t (or (satisfying-fc-derivation1p (car criteria) fcd) (satisfying-fc-derivationp (cdr criteria) fcd))))) (defun collect-satisfying-fc-derivations (criteria fcd-lst ans) ; Accumulate all satisfying fc-derivations in fcd-lst onto ans. (cond ((endp fcd-lst) ans) ((satisfying-fc-derivationp criteria (car fcd-lst)) (collect-satisfying-fc-derivations criteria (cdr fcd-lst) (cons (car fcd-lst) ans))) (t (collect-satisfying-fc-derivations criteria (cdr fcd-lst) ans)))) ; We now define the functions that move information into the five fc-wormhole ; sites. We call this ``filtering'' because we only move the objects that ; satisfy the criteria. (defun filter-satisfying-virtual-fc-activation (act0 inst-hyp hyps unify-subst ttree) ; This is the function that adds an activation to the ; :blocked-false-satisfying-activations, aka site (1), of the current ; forward-chain-top call, provided the activation satisfies the criteria. This ; is called from both advance-fc-activation1 and advance-fc-activation2, which ; are the two functions that detect false hypotheses. Those two functions will ; not actually have the realized activation available to them (without consing ; it up). Instead, they have act0, inst-hyp, hyps, unify-subst, and ttree. ; The actual activation being considered is that obtained by putting those ; fields into act0, something that the advance-fc-activation functions don't do ; unnecessarily. But we must create the actual from the virtual if we wish to ; save the actual activation. This function avoids creating the activation if ; it is not satisfying. ; The prefix ``filter'' in this name is a little misleading. We generally use ; that prefix to suggest mapping over a list and extracting the ones satisfying ; some criteria. But here we have just one virtual activation and we either ; save it or not depending on whether it is satisfying. (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((criteria (cdr (assoc-eq :CRITERIA (wormhole-data whs))))) (cond ((null criteria) whs) ((satisfying-virtual-fc-activationp criteria act0 unify-subst) ; At this point we know we need the activation. So we get comfortable. (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (k (car (car calls-alist))) (call-alist (cdr (car calls-alist))) (act (suspend-fc-activation act0 inst-hyp hyps unify-subst ttree))) (set-wormhole-data whs (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons k (put-assoc-eq :BLOCKED-FALSE-SATISFYING-ACTIVATIONS (cons act (cdr (assoc-eq :BLOCKED-FALSE-SATISFYING-ACTIVATIONS call-alist))) call-alist)) (cdr calls-alist)) data)))) (t whs)))) :no-wormhole-lock)) (defun filter-all-satisfying-fc-derivations (fcd-lst) ; This function moves satisfying fcds from fcd-lst into site (2) of the current ; call of forward-chain-top. ; Two of our sites, (2) all-satisfying-fc-derivations and (3) ; approved-satisfying-fc-derivations, contain fc-derivations. The process of ; collecting into these sites is the same: we map over some fcd-lst and cons ; every satisfying fcd onto the appropriate site. One possibly confusing ; difference between the handling of these two sites is that to collect into ; site (2) we must call this function repeatedly during forward chaining, once ; per round, because it is only at the level of a round (forward-chain1) that ; we know all the fc-derivations made in a round. But the top-level ; forward-chain-top process keeps track of all approved fc-derivations, so we ; call the guts of this function just once on the other site at the top-level ; (as we exit forward-chain-top) to filter site (3). (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((criteria (cdr (assoc-eq :CRITERIA (wormhole-data whs))))) (cond ((null criteria) whs) (t (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (k (car (car calls-alist))) (call-alist (cdr (car calls-alist)))) (set-wormhole-data whs (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons k (put-assoc-eq :ALL-SATISFYING-FC-DERIVATIONS (collect-satisfying-fc-derivations criteria fcd-lst (cdr (assoc-eq :ALL-SATISFYING-FC-DERIVATIONS call-alist))) call-alist)) (cdr calls-alist)) data))))))) :no-wormhole-lock)) (defun filter-satisfying-fc-activations (acts) ; Site (4) is leftover-activations. At the termination of forward-chaining we ; are holding a list of all still-suspended fc-activations and this is the ; function that filters that list into site 4 of the current call of ; forward-chain-top. (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((criteria (cdr (assoc-eq :CRITERIA (wormhole-data whs))))) (cond ((null criteria) whs) (t (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (k (car (car calls-alist))) (call-alist (cdr (car calls-alist)))) (set-wormhole-data whs (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons k (put-assoc-eq :LEFTOVER-ACTIVATIONS (collect-satisfying-fc-activations criteria acts (cdr (assoc-eq :LEFTOVER-ACTIVATIONS call-alist))) call-alist)) (cdr calls-alist)) data))))))) :no-wormhole-lock)) (defun filter-redundant-approved-fc-derivation (fcd) ; We move fcd into site (5), :REDUNDANT-APPROVED-FC-DERIVATIONS, provided fcd ; meets the criteria. By calling this function on fcd we are indicating that ; the conclusion of the fcd was already known true under the type-alist. (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((criteria (cdr (assoc-eq :CRITERIA (wormhole-data whs))))) (cond ((null criteria) whs) ((satisfying-fc-derivationp criteria fcd) (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (k (car (car calls-alist))) (call-alist (cdr (car calls-alist)))) (set-wormhole-data whs (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons k (put-assoc-eq :REDUNDANT-APPROVED-FC-DERIVATIONS (cons fcd (cdr (assoc-eq :REDUNDANT-APPROVED-FC-DERIVATIONS call-alist))) call-alist)) (cdr calls-alist)) data)))) (t whs)))) :no-wormhole-lock)) ; So now we have got the machinery to populate sites (1)-(5) of the ; current call of forward-chain-top. ; When forward-chain-top is about to exit, we finish the task of recording the ; results of the current call. fc-exit This consists of three main parts: we ; move some accumulated data into sites 3 and 4 (sites 1 and 2 will be ; accumulated as we go), we generate a report that is either long or short ; depending on the :REPORT-ON-THE-FLYP flag, and we store the returned values. ; We now develop the machinery to report. Reports take two forms, a long and short ; form. The short form is just: ; (Forward-chaining called by caller. See (FC-Report k).) ; where caller is the token indicating the caller of forward-chain-top and k is ; the call-number of the current call of forward-chain-top. ; The long form will summarize all the activity. We will arrange for the ; function fc-report to print the long form after the fact and we'll print the ; long form on the fly if the flag is set. ; A difficulty with reporting is that activations branch as free variables are ; instantiated. Thus, a rule triggered by a given term may have many final ; unify-substs and dispositions. The report therefore lists every rule and ; trigger term and then all the dispositions: ; ( ; (:TRIGGER ) ; ((:UNIFY-SUBST ) ; (:DISPOSITION )) ; ... ; ((:UNIFY-SUBST ) ; (:DISPOSITION ))) ; We prepare this report in its raw form and will just print it. The user may ; want to process it further with some attachment. ; When we begin to create the report we have: ; (1) blocked-false-satisfying-activations - every satisfying fc-activation ; found to have a false hyp ; (2) all-satisfying-fc-derivations - every fc-derivation that satisfies the ; criteria ; (3) approved-satisfying-fc-derivations - the fc-derivations that both satisfy ; the criteria and were approved ; (4) leftover-activations - all activations still suspended at the termination ; of forward chaining ; Recall that the status of an activation consists of its unify-subst (which ; completes the identification of the branch) and the disposition: ; (a) SUCCESS APPROVED -- successfully fired and gave us ; (b) SUCCESS REJECTED -- successfully fired but conclusion was disapproved ; (c) SUCCESS REDUNDANT -- successfully fired and approved but already known ; (d) BLOCKED UNRELIEVED-HYPx -- unable to relieve ; (e) BLOCKED FALSE -- hyp shown false ; Our strategy will be first to collect all (rune . inst-trigger) pairs and ; then, for each such pair, map over each of the sites (1)-(5) to collect the ; status of that pair. ; To collect all (rune . inst-trigger) pairs we have to map over sites (1), ; (2), and (4), i.e., all blocked false activations, all fc-derivations, and ; all still-suspended activations. (defun collect-rune-trigger-pairs-from-fc-activations (acts ans) (cond ((endp acts) ans) (t (collect-rune-trigger-pairs-from-fc-activations (cdr acts) (add-to-set-equal (cons (access forward-chaining-rule (access fc-activation (car acts) :rule) :rune) (access fc-activation (car acts) :inst-trigger)) ans))))) (defun collect-rune-trigger-pairs-from-fc-derivations (fcds ans) (cond ((endp fcds) ans) (t (collect-rune-trigger-pairs-from-fc-derivations (cdr fcds) (add-to-set-equal (cons (access fc-derivation (car fcds) :rune) (access fc-derivation (car fcds) :inst-trigger)) ans))))) ; Once we've collected all the rune-trigger pairs, we can map over each site to ; collect the status information for each pair. (defun prettyify-subst (alist) ; Turn a dotted-pair alist into a doublet alist, e.g., ; ((X CAR A) (Y . B)) into ((X (CAR A)) (Y B)). (cond ((endp alist) nil) (t (cons (list (car (car alist)) (cdr (car alist))) (prettyify-subst (cdr alist)))))) (defun collect-fc-status-site-1 (rune inst-trigger acts) ; Acts is site (1) blocked-false-satisfying-activations - every satisfying ; fc-activation found to have a false hyp. Note that when we store a ; satisfying activation at this site we put the inst-hyp (which was either ; type-set to *ts-nil* or evaluated to *nil*) into the activation. The hyp ; cannot possibly have free vars in it (because we never choose instantiations ; to falsify a hyp). It may have a FORCE or CASE-SPLIT on it, but that's ok ; because type-set and eval handle those functions and accurately determined ; that the inst-hyp is false. (cond ((endp acts) nil) ((and (equal rune (access forward-chaining-rule (access fc-activation (car acts) :rule) :rune)) (equal inst-trigger (access fc-activation (car acts) :inst-trigger))) (cons `((:UNIFY-SUBST ,(prettyify-subst (access fc-activation (car acts) :unify-subst))) (:DISPOSITION BLOCKED FALSE ,(access fc-activation (car acts) :inst-hyp))) (collect-fc-status-site-1 rune inst-trigger (cdr acts)))) (t (collect-fc-status-site-1 rune inst-trigger (cdr acts))))) (defun collect-fc-status-sites-2-3-5 (rune inst-trigger all-fcds approved-fcds redundant-approved-fc-derivations) ; All-fcds is site (2) all-satisfying-fc-derivations - every fc-derivation that ; satisfies the criteria, approved-fcds is site (3) ; approved-satisfying-fc-derivations - the fc-derivations that both satisfy the ; criteria and were approved, and redundant-approved-fc-derivations is site ; (5). We map down all-fcds and use the other two to determine if whether each ; was approved, redundant, or rejected. (cond ((endp all-fcds) nil) ((and (equal rune (access fc-derivation (car all-fcds) :rune)) (equal inst-trigger (access fc-derivation (car all-fcds) :inst-trigger))) (cons `((:UNIFY-SUBST ,(prettyify-subst (access fc-derivation (car all-fcds) :unify-subst))) (:DISPOSITION SUCCESS ,(if (member-equal (car all-fcds) approved-fcds) (if (member-equal (car all-fcds) redundant-approved-fc-derivations) 'REDUNDANT 'APPROVED) 'REJECTED) ,(access fc-derivation (car all-fcds) :concl))) (collect-fc-status-sites-2-3-5 rune inst-trigger (cdr all-fcds) approved-fcds redundant-approved-fc-derivations))) (t (collect-fc-status-sites-2-3-5 rune inst-trigger (cdr all-fcds) approved-fcds redundant-approved-fc-derivations)))) (defun prettyify-blocked-fc-inst-hyp (inst-hyp hyps unify-subst) ; The arguments are those respective fields in some fc-activation. Hence, ; inst-hyp is either the :FC-FREE-VAR marker (which implicitly depends on the ; contents of the hyp and unify-subst fields) or an instantiated hyp. We ; recover the actual (partially) instantiated hyp we're stuck on. (cond ((and (consp inst-hyp) (eq (car inst-hyp) :FC-FREE-VARS)) (let ((hyp (sublis-var (bind-free-vars-to-unbound-free-vars (all-vars (car hyps)) unify-subst) (car hyps)))) (if (cadr inst-hyp) ; then FORCE or CASE-SPLIT should be added `(,(cadr inst-hyp) ,hyp) hyp))) (t inst-hyp))) (defun collect-fc-status-site-4 (rune inst-trigger acts) ; Acts is site (4) leftover-activations - all activations still suspended at ; the termination of forward chaining. (cond ((endp acts) nil) ((and (equal rune (access forward-chaining-rule (access fc-activation (car acts) :rule) :rune)) (equal inst-trigger (access fc-activation (car acts) :inst-trigger))) (let ((inst-hyp (access fc-activation (car acts) :inst-hyp))) (cons `((:UNIFY-SUBST ,(prettyify-subst (access fc-activation (car acts) :unify-subst))) (:DISPOSITION BLOCKED ,(if (and (consp inst-hyp) (eq (car inst-hyp) :FC-FREE-VARS)) 'UNRELIEVED-HYP-FREE 'UNRELIEVED-HYP) ,(prettyify-blocked-fc-inst-hyp inst-hyp (access fc-activation (car acts) :hyps) (access fc-activation (car acts) :unify-subst)))) (collect-fc-status-site-4 rune inst-trigger (cdr acts))))) (t (collect-fc-status-site-4 rune inst-trigger (cdr acts))))) (defun collect-fc-status (rune inst-trigger site1 site2 site3 site4 site5) ; Given a rune and instantiated trigger term we collect the final status of ; every activation of that pair recorded in the sites. Every activation ; (derivation) in the sites is known to satisfy the criteria. `(,rune (:TRIGGER ,inst-trigger) ,@(collect-fc-status-site-1 rune inst-trigger site1) ,@(collect-fc-status-sites-2-3-5 rune inst-trigger site2 site3 site5) ,@(collect-fc-status-site-4 rune inst-trigger site4))) (defun make-fc-activity-report1 (rune-trigger-pairs site1 site2 site3 site4 site5) ; Given a list of (rune . inst-trigger) pairs and the four sites, we ; collect the final status of each pair. (cond ((endp rune-trigger-pairs) nil) (t (cons (collect-fc-status (car (car rune-trigger-pairs)) (cdr (car rune-trigger-pairs)) site1 site2 site3 site4 site5) (make-fc-activity-report1 (cdr rune-trigger-pairs) site1 site2 site3 site4 site5))))) (defun make-fc-activity-report (call-alist) ; Given the data collected in the fc-wormhole by forward-chain-top, we prepare ; the final status reports of every activation satisfying the criteria. (let* ((site1 (cdr (assoc-eq :blocked-false-satisfying-activations call-alist))) (site2 (cdr (assoc-eq :all-satisfying-fc-derivations call-alist))) (site3 (cdr (assoc-eq :approved-satisfying-fc-derivations call-alist))) (site4 (cdr (assoc-eq :leftover-activations call-alist))) (site5 (cdr (assoc-eq :redundant-approved-fc-derivations call-alist))) (rune-trigger-pairs (collect-rune-trigger-pairs-from-fc-activations site1 (collect-rune-trigger-pairs-from-fc-derivations site2 (collect-rune-trigger-pairs-from-fc-activations site4 nil))))) (merge-sort-lexorder (make-fc-activity-report1 rune-trigger-pairs site1 site2 site3 site4 site5)))) (defun fc-report1 (whs k) ; We assume we are in the fc-wormhole when this function is called. It takes ; the wormhole status and an alleged caller number, k, and prints the report ; for the kth call of forward-chain-top. It returns nil. (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (temp (assoc-equal k calls-alist))) (cond ((and temp (cdr (assoc-eq :OUTPUT (cdr temp)))) (let* ((call-alist (cdr temp)) (input (cdr (assoc-eq :INPUT call-alist))) (caller (car input)) (clause (cadr input)) (output (cdr (assoc-eq :OUTPUT call-alist))) (flg (car output)) (rounds (cdr (assoc-eq :ROUNDS call-alist))) (activity (make-fc-activity-report call-alist))) (cw "~%~ -----------------------------------------------------------------~%~ Forward Chaining Report ~x0:~%~ Caller: ~x1~%~ Clause: ~x2~%~ Number of Rounds: ~x3~%~ Contradictionp: ~x4~%~ Activations:~%~ ~x5~%~ -----------------------------------------------------------------~%" k caller clause rounds flg activity))) (t (cw "~%There is no Forward Chaining Report for ~x0.~%" k))))) (defun fc-report (k) ; This function is intended to be called from outside the fc-wormhole, ; by the user. ":Doc-Section Forward-Chaining-Reports to report on the forward chaining activity in the most recent proof~/ ~bv[] Example: (fc-report 15) General Form: (fc-report k) ~ev[] where ~c[k] is the number of some forward chaining report printed in the most recent event.~/ ~l[forward-chaining-reports] for details." (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((criteria (cdr (assoc-eq :CRITERIA (wormhole-data whs))))) (cond ((null criteria) whs) (t (prog2$ (fc-report1 whs k) whs))))) nil)) ; As noted above, when forward-chain-top is about to exit, we finish the task ; of recording the results of the current call. We move some accumulated data ; into sites 3 and 4 (sites 1 and 2 will be accumulated as we go), we generate ; a report that is either long or short depending on the :REPORT-ON-THE-FLYP ; flag, and we store the returned values. (defun fc-exit (flg type-alist ttree-or-fc-pairs caller rounds all-approved-fcds all-leftover-activations) ; We exit forward-chain-top by calling this function. Logically you can think ; of this function as just: ; (mv flg type-alist ttree-or-fc-pairs) ; The other arguments are used to report on forward-chaining. ; At the time this is called we will have already fully loaded sites (1) and ; (2), i.e., the satisfying activations with false hyps and the list of all ; satisfying fc-derivations. We load sites (3) and (4) -- the approved ; satisfying fc-derivations and the (satisfying) leftover activations -- here, ; using the supplied all-approved-fcds and all-leftover-activations arguments. ; Then we generate a report -- long or short as appropriate -- and return. (prog2$ (wormhole-eval 'fc-wormhole '(lambda (whs) (let ((criteria (cdr (assoc-eq :CRITERIA (wormhole-data whs))))) (cond ((null criteria) whs) (t (let* ((data (wormhole-data whs)) (calls-alist (cdr (assoc-eq :FORWARD-CHAIN-CALLS data))) (k (car (car calls-alist))) (call-alist (cdr (car calls-alist))) (new-data (put-assoc-eq :FORWARD-CHAIN-CALLS (cons (cons k (put-assoc-eq :APPROVED-SATISFYING-FC-DERIVATIONS (collect-satisfying-fc-derivations criteria all-approved-fcds nil) (put-assoc-eq :LEFTOVER-ACTIVATIONS (collect-satisfying-fc-activations criteria all-leftover-activations nil) (put-assoc-eq :ROUNDS rounds (put-assoc-eq :OUTPUT (list flg type-alist ttree-or-fc-pairs) call-alist))))) (cdr calls-alist)) data)) (new-whs (set-wormhole-data whs new-data))) (cond ((cdr (assoc-eq :REPORT-ON-THE-FLYP new-data)) (prog2$ (fc-report1 new-whs k) new-whs)) (t (prog2$ (cw "~%(Forward Chaining on behalf of ~x0: (FC-Report ~x1))~%" caller k) new-whs)))))))) :no-wormhole-lock) (mv flg type-alist ttree-or-fc-pairs))) ; Explanation of the Kernel Code for FC Advancement ; The mutual-recursion nest below is the kernel code for advancing ; fc-activations. There is a wrapper defined afterwards. The kernel functions ; advance an activation along all possible threads and return a list of ; suspensions created only when they finally get stuck on some hypothesis. But ; in the mutual recursion keep in mind act0, inst-hyps, hyps, unify-subst, and ; ttree. Initially, act0 is the fc-activation with which we started. ; Initially, the other four were just the obvious fields extracted from this ; activation. But as we recur we may change the other four. When we finally ; get stuck, we suspend act0 by setting all four of the fields because we don't ; know which ones have changed. The function suspend-fc-activation optimizes ; the construction for common cases of unchanged fields. ; The mutual recursion has 3 functions or phases and their names end in 1, 2, ; and 3. Phase (1) works on the inst-hyp, which is either an :FC-FREE-VARS ; marker or the instantiated hyp upon which we were stuck the last time we saw ; this activation. If the inst-hyp is just an instantiated hyp and we find it ; to be true now, we enter phase (2) below to work on the other hyps. If the ; inst-hyp is a :FC-FREE-VARS marker and we find instances of it that are true, ; we enter phase (3) to pursue each possible unify-subst and ttree, but we also ; generally re-suspend in case further instances come along as the type-alist ; grows. Phase (2) just loops down hyps calling itself recursively. However, ; if it sees a hyp containing a free variable, it just manufactures an ; appropriate inst-hyp and calls phase (1) so we don't reproduce that code. ; Finally, Phase (3) just loops through the unify-substs and ttrees generated ; by finding suitable instances and calls phase (2) on the rest of the hyps. ; So the call graph of this nest is: ; (1) calls ; (2) to go on to the rest of the hyps and ; (3) to pursue each choice of free vars ; (2) calls ; (1) to handle free vars and ; (2) to go on to the rest of the hyps ; (3) calls ; (2) to handle a given unify-subst and ; (3) to handle the rest of the unify-substs. ; All of these functions accumulate suspensions of the newly advanced act0 onto ; suspensions and derived conclusions (in the form of fc-derivations) onto ; fcd-lst. It is only in the base case of phase (2), when hyps is nil, that we ; convert successful terminal fc-activations into fc-derivations. ; If we're asked to FORCE or CASE-SPLIT on a hyp that contains free variables ; and we are unable to find a true match for it on the type-alist, we ; immediately force or split on it, binding the free variables to variables ; with "UNBOUND-FREE-" prefixed onto the existing names. In principle we can ; bind the free variables of a hyp to any term. We chose these names in the ; hope that they catch the eye of the user when they appear in failed proofs. ; The user was warned of this possibility when a forward-chaining rule was ; built with a forced or split hyp containing free variables. Also, when ; forcing or splitting on a hypothesis containing free vars we don't produce a ; suspension to find new instances because that would just keep spitting out ; UNBOUND-FREE variables. (mutual-recursion (defun advance-fc-activation1 (act0 inst-hyp hyps unify-subst ttree ; key args fc-round type-alist ens force-flg wrld state oncep-override ; contextual args suspensions fcd-lst) ; answers ; See explanation above the mutual-recursion nest. (cond ((and (consp inst-hyp) (eq (car inst-hyp) :FC-FREE-VARS)) (let ((forcer-fn (cadr inst-hyp)) ; nil, FORCE, or CASE-SPLIT (last-keys-seen (cddr inst-hyp))) ; When inst-hyp is the marker, the hyp we are to relieve is the first one in ; hyps. Any FORCE or CASE-SPLIT has been removed but recorded in the forcer-fn ; field. Last-keys-seen is the list of all type-alist keys from which matches ; have already been produced. (let* ((hyp (car hyps)) (rule (access fc-activation act0 :rule)) (oncep1 (oncep oncep-override (access forward-chaining-rule rule :match-free) (access forward-chaining-rule rule :rune) (access forward-chaining-rule rule :nume)))) ; Hyp is the hypothesis we are stuck on. ; We match hyp/unify-subst against the true terms in type-alist, in all ; possible ways, obtaining lists of the extended unify-substs and their ; respective ttrees, and a list of the key terms from the type-alist ; used to produce these unifications. (mv-let (new-unify-subst-list new-ttree-list new-keys-seen) (mult-lookup-hyp hyp (cdr hyps) (access forward-chaining-rule (access fc-activation act0 :rule) :concls) type-alist wrld unify-subst ttree oncep1 last-keys-seen) (cond (new-unify-subst-list ; We found one or more extensions of unify-subst and pursue all of them. ; Normally we also suspend any activation that is stuck on a free-var hyp in ; case future type-alists permit other matches, but if this rule has explicitly ; been tagged as using the first binding (as now stored in the flag oncep1) or ; if this hyp is to be forced or split upon we don't also suspend it. (advance-fc-activation3 act0 (cdr hyps) new-unify-subst-list new-ttree-list fc-round type-alist ens force-flg wrld state oncep-override (if (or oncep1 (and forcer-fn force-flg)) suspensions (cons (suspend-fc-activation act0 (list* :FC-FREE-VARS forcer-fn (append new-keys-seen last-keys-seen)) hyps unify-subst ttree) suspensions)) fcd-lst)) ((and forcer-fn force-flg) ; In this case, we found no instances of this hyp on type-alist and it ; is supposed to be forced (or case-split). So we must assume something ; to move forward. We replace its free vars with UNBOUND-FREE-vars and ; proceed, without saving a suspension. (let ((fully-bound-unify-subst (bind-free-vars-to-unbound-free-vars (all-vars hyp) unify-subst))) (mv-let (new-force-flg ttree) (force-assumption (access forward-chaining-rule (access fc-activation act0 :rule) :rune) (access fc-activation act0 :inst-trigger) (sublis-var fully-bound-unify-subst hyp) type-alist nil (immediate-forcep forcer-fn ens) force-flg ttree) ; Force-assumption always returns an unchanged force-flg which we just ignore. (declare (ignore new-force-flg)) (advance-fc-activation2 act0 (cdr hyps) unify-subst ttree fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst)))) (t ; In this case, we are stuck on a hyp with free vars, no match is ; available, and we're not supposed to force it. So we create a ; suspension. (mv (cons (suspend-fc-activation act0 (list* :FC-FREE-VARS forcer-fn (append new-keys-seen last-keys-seen)) hyps unify-subst ttree) suspensions) fcd-lst))))))) (t ; In this case, we're stuck on a fully instantiated hyp, ; hypn/unify-subst, where hypn had no free variables and is not an ; evaluable ground term, or a FORCE or CASE-SPLIT. Inst-hyp must be ; true under type-alist to proceed. (mv-let (ts ttree1) (type-set inst-hyp force-flg nil type-alist ens wrld nil nil nil) (cond ((ts= ts *ts-nil*) ; This hyp has been shown false. We just let the activation ; evaporate by not including this suspension of act0 in our answer. (prog2$ (filter-satisfying-virtual-fc-activation ; (FC Report) act0 inst-hyp hyps unify-subst ttree) (mv suspensions fcd-lst))) ((ts-intersectp ts *ts-nil*) ; The value of hyp is indeterminate. We suspend it. It is tempting to ; think of the suspension below as being identical to act0 -- i.e., no ; changes -- but we're in recursion, so who knows? ; Suspend-fc-activation will check if anything changed. (mv (cons (suspend-fc-activation act0 inst-hyp hyps unify-subst ttree) suspensions) fcd-lst)) (t ; Finally! We're past inst-hyp and begin to work our way down hyps. (advance-fc-activation2 act0 hyps unify-subst (cons-tag-trees ttree1 ttree) fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst))))))) (defun advance-fc-activation2 (act0 hyps unify-subst ttree ; key args fc-round type-alist ens force-flg wrld state oncep-override ; contextual args suspensions fcd-lst) ; answers ; See explanation above the mutual-recursion nest. (cond ((null hyps) ; We succeeded in relieving all the hypotheses of this activation. We ; produce the resultant fc-derivations and add them to fcd-lst. (mv suspensions (add-fc-derivations (access forward-chaining-rule (access fc-activation act0 :rule) :rune) (sublis-var-lst unify-subst (access forward-chaining-rule (access fc-activation act0 :rule) :concls)) unify-subst (access fc-activation act0 :inst-trigger) fc-round ens wrld state ttree fcd-lst))) (t (let* ((forcep1 (and (nvariablep (car hyps)) (not (fquotep (car hyps))) (or (eq (ffn-symb (car hyps)) 'force) (eq (ffn-symb (car hyps)) 'case-split)))) (forcer-fn (and forcep1 (ffn-symb (car hyps)))) (hyp (if forcep1 (fargn (car hyps) 1) (car hyps)))) (cond ((free-varsp hyp unify-subst) ; To avoid code duplication we let advance-fc-activation1 handle all ; free var situations: (advance-fc-activation1 act0 (if forcer-fn (if (eq forcer-fn 'FORCE) '(:FC-FREE-VARS FORCE . nil) '(:FC-FREE-VARS CASE-SPLIT . nil)) '(:FC-FREE-VARS nil . nil)) (cons hyp (cdr hyps)) unify-subst ttree fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst)) (t ; Hyp contains no free vars, so we instantiate it and then use any of ; three methods (depending on the instance) to decide if it is true: ; type-set with the current type-alist, ground evaluation, or ; forcing/case splitting. (let ((inst-hyp (sublis-var unify-subst hyp))) (mv-let (ts ttree1) (type-set inst-hyp force-flg nil type-alist ens wrld nil nil nil) ; Note that ttree1 is the ttree associated with the type-set computation ; and that it does not include ttree. If we use the type-set ; information, we must add ttree1 to ttree. (cond ((ts= ts *ts-nil*) ; Inst-hyp is false under the current type-alist, so we just ; abandon this activation. (prog2$ (filter-satisfying-virtual-fc-activation ; (FC Report) act0 inst-hyp hyps unify-subst ttree) (mv suspensions fcd-lst))) ((ts-intersectp ts *ts-nil*) (cond ((not (free-varsp inst-hyp nil)) ; This means that inst-hyp is actually a ground term. We try to ; evaluate it. Note that we do not try to eval or even partially eval ; non-ground hyps. For example, the translation of (OR (NATP '1) (NATP ; A)) will eval non-erroneously to T and the translation of (AND (NATP ; '1) (NATP A)) will eval-ground-subexpressions to (NATP A). So there ; may be some merit in a fancier treatment of evaluation. However, ; rewriting a hyp, even via evaluation, might be problematic in this ; setting since the only way we can decide a non-trivial inst-hyp is via ; type-set, which is often just an assoc-equal. So for the moment we're ; only using evaluation on ground terms where it makes the most sense. (mv-let (erp val latches ttree2) (ev-respecting-ens inst-hyp nil state nil nil ens wrld) (declare (ignore latches)) ; Note that ttree2 is the ttree for the evaluation and it does not ; include ttree or ttree1. We are not using the type-set stuff because ; it only told us that inst-hyp was nil or non-nil. But the evaluation ; ttree should be added to the original ttree if we use the evaluation ; result. (cond (erp ; This hyp cannot be evaluated, e.g., perhaps it contains a constrained ; function. So we must either force it or wait for it to come up on the ; type-alist. Note that in this part be ignore type-set's ttree1 and ; the evaluator's ttree2. (mv-let (force-flg ttree) (cond ((or (not forcep1) (not force-flg)) (mv nil ttree)) (t (force-assumption (access forward-chaining-rule (access fc-activation act0 :rule) :rune) (access fc-activation act0 :inst-trigger) inst-hyp type-alist nil (immediate-forcep forcer-fn ens) force-flg ttree))) (cond (force-flg ; Inst-hyp is ground but cannot be evaluated and is supposed to be forced or ; split upon. So we did that and the result is in ttree. Therefore, we ; just move on. (advance-fc-activation2 act0 (cdr hyps) unify-subst ttree fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst)) (t ; Inst-hyp is ground but cannot be evaluated and is not supposed to be ; forced. So we just suspend it. Note that inst-hyp satisfies our ; invariant on fc-activations: it contains no free vars, is not an ; evaluable ground term, and is not a FORCE or CASE-SPLIT. We just have ; to wait until the type-alist makes it true. (mv (cons (suspend-fc-activation act0 inst-hyp (cdr hyps) unify-subst ttree) suspensions) fcd-lst))))) (val ; Inst-hyp evaluated to non-nil, so we just move on (using the evaluator's ; ttree2) plus the original one. (advance-fc-activation2 act0 (cdr hyps) unify-subst (cons-tag-trees ttree2 ttree) fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst)) (t ; Inst-hyp evaluated to nil, so we just abandon the activation. ; Forcing considerations are irrelevant here. (prog2$ (filter-satisfying-virtual-fc-activation ; (FC Report) act0 inst-hyp hyps unify-subst ttree) (mv suspensions fcd-lst)))))) (t ; Inst-hyp contains variables and so we don't even try evaluation -- ; even though there are expressions containing variables and IFs that ; evaluate to constants. Instead, we just see whether we should force ; it. We ignore type-set's ttree1. (mv-let (force-flg ttree) (cond ((or (not forcep1) (not force-flg)) (mv nil ttree)) (t (force-assumption (access forward-chaining-rule (access fc-activation act0 :rule) :rune) (access fc-activation act0 :inst-trigger) inst-hyp type-alist nil (immediate-forcep forcer-fn ens) force-flg ttree))) (cond (force-flg ; Inst-hyp has been forced. So just move on. (advance-fc-activation2 act0 (cdr hyps) unify-subst ttree fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst)) (t ; Inst-hyp ``cannot'' be evaluated and is not supposed to be ; forced. So we just suspend it. Note that inst-hyp satisfies our ; invariant on fc-activations: it contains no free vars, is not an ; evaluable ground term, and is not a FORCE or CASE-SPLIT. We just have ; to wait until the type-alist makes it true. (mv (cons (suspend-fc-activation act0 inst-hyp (cdr hyps) unify-subst ttree) suspensions) fcd-lst))))))) (t ; Inst-hyp is true under type-alist. We add type-set's ttree1 to ttree ; as we move on. (advance-fc-activation2 act0 (cdr hyps) unify-subst (cons-tag-trees ttree1 ttree) fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst))))))))))) (defun advance-fc-activation3 (act0 hyps unify-subst-lst ttree-lst ; key args fc-round type-alist ens force-flg wrld state oncep-override ; contextual args suspensions fcd-lst) ; answers (cond ((endp unify-subst-lst) (mv suspensions fcd-lst)) (t (mv-let (suspensions1 fcd-lst1) (advance-fc-activation2 act0 hyps (car unify-subst-lst) (car ttree-lst) fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst) (advance-fc-activation3 act0 hyps (cdr unify-subst-lst) (cdr ttree-lst) fc-round type-alist ens force-flg wrld state oncep-override suspensions1 fcd-lst1))))) ) ; The wrapper for the forward chaining kernel: advancing an fc-activation. (defun advance-fc-activation (act fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst) (with-accumulated-persistence (access forward-chaining-rule (access fc-activation act :rule) :rune) (suspensions1 fcd-lst1) t ; Wart: We consider all forward-chaining work to be ``useful'' (advance-fc-activation1 act (access fc-activation act :inst-hyp) (access fc-activation act :hyps) (access fc-activation act :unify-subst) (access fc-activation act :ttree) fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst))) ; Recall the basic data structure of forward chaining, the fc-pot-lst. ; It is a list of fc-pots, each of which is (term act1 ... actn), with a ; pot for every term in the problem pairing all the fc-activations ; triggered by the corresponding term. We want to advance all the ; activations in every pot. We start by advancing all the activations ; listed in a single pot. (defun advance-fc-activations (lst fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst) ; Lst is of the form (act1 ... actn), where each acti is an fc ; activation. Fcd-lst is a list of fc-derivations onto which we ; accumulate any derived conclusions (as fc-derivations). We return two ; results which we build by accumulation onto the last two arguments: a ; new list of possibly advanced suspended activations and the ; accumulated successful derivations. (cond ((null lst) (mv suspensions fcd-lst)) (t (mv-let (suspensions1 fcd-lst1) (advance-fc-activation (car lst) fc-round type-alist ens force-flg wrld state oncep-override suspensions fcd-lst) (advance-fc-activations (cdr lst) fc-round type-alist ens force-flg wrld state oncep-override suspensions1 fcd-lst1))))) (defun fc-pair-lst (fcd-lst) ; We convert a list of fc-derivations to a list of pairs of the form ; (concl . ttree), where each ttree is fcd-free. We call such a pair an ; "fc-pair." These pairs can be sensibly used outside of the ; forward-chaining module. ; Note: It is important that this function return a list in 1:1 correspondence ; with fcd-lst. The reason is that after forming this list (in ; forward-chain-top) we map over it with fc-pair-lst-type-alist (immediately ; below) while mapping in parallel over the original fcd-lst, assuming that the ; concl being dealt with from the first came from the corresponding element of ; the second. (cond ((null fcd-lst) nil) (t (cons (cons (access fc-derivation (car fcd-lst) :concl) (push-lemma (access fc-derivation (car fcd-lst) :rune) (expunge-fc-derivations (access fc-derivation (car fcd-lst) :ttree)))) (fc-pair-lst (cdr fcd-lst)))))) (defun fc-pair-lst-type-alist (fc-pair-lst fcd-lst type-alist force-flg ens wrld) ; Fc-pair-lst is a list of pairs of the form (concl . ttree). Fcd-lst is the ; list from which fc-pair-lst was derived and hence is in 1:1 correspondence ; with it. That is, the (concl . ttree) entry from the first argument came ; from the fcd in the corresponding position of the second argument. ; We extend type-alist by assuming the truth of every concl, tagging each ; type-alist entry with the corresponding ttree, which we assume is fcd-free. ; Assuming the initial type-alist is fcd-free, the final one is too. We return ; three results, (mv flg type-alist ttree). If a contradiction is found, flg ; is t, type-alist is nil, and ttree is the fcd-free ttree explaining it. ; Otherwise, type-alist is the resulting type-alist and ttree is nil. ; At one time we assumed that there was no contradiction, causing a hard error ; if we found one. However, Jared Davis sent the following script that causes ; that hard error, so we changed this function. A relevant comment, from ; before that change, is given below. ; (defstub appealp (* *) => *) ; (defstub appeal-listp (* *) => *) ; (defstub appeal-structurep (*) => *) ; (defstub appeal-structure-listp (*) => *) ; (defstub get-subgoals (*) => *) ; (defstub appeal-provisionally-okp (* * *) => *) ; (defstub proofp (* * *) => *) ; (defstub proof-listp (* * *) => *) ; ; (defaxiom appeal-structure-listp-forward-to-appeal-structurep-of-car ; (implies (appeal-structure-listp x) ; (equal (appeal-structurep (car x)) ; (if x t nil))) ; :rule-classes :forward-chaining) ; ; (defaxiom appealp-listp-forward-to-appealp-of-car ; (implies (appeal-listp x arity-table) ; (equal (appealp (car x) arity-table) ; (if x t nil))) ; :rule-classes :forward-chaining) ; ; (defaxiom appealp-forward-to-appeal-structurep ; (implies (appealp x arity-table) ; (appeal-structurep x)) ; :rule-classes :forward-chaining) ; ; (defaxiom appeal-structure-listp-forward-to-appeal-structure-listp-of-cdr ; (implies (appeal-structure-listp x) ; (appeal-structure-listp (cdr x))) ; :rule-classes :forward-chaining) ; ; (defaxiom appeal-listp-forward-to-appeal-listp-of-cdr ; (implies (appeal-listp x arity-table) ; (appeal-listp (cdr x) arity-table)) ; :rule-classes :forward-chaining) ; ; (defaxiom appeal-listp-forward-to-appeal-structure-listp ; (implies (appeal-listp x arity-table) ; (appeal-structure-listp x)) ; :rule-classes :forward-chaining) ; ; (defaxiom appeal-structure-listp-forward-to-true-listp ; (implies (appeal-structure-listp x) ; (true-listp x)) ; :rule-classes :forward-chaining) ; ; (defaxiom appeal-listp-when-proofp ; (implies (proof-listp x database arity-table) ; (appeal-listp x arity-table)) ; :rule-classes :forward-chaining) ; ; (defaxiom appealp-when-proofp ; (implies (proofp x database arity-table) ; (appealp x arity-table)) ; :rule-classes :forward-chaining) ; ; (defthm hard-error-in-fc-pair-lst-type-alist ; (implies (and (proof-listp xs database arity-table) ; (not (consp xs))) ; (equal (proofp (car xs) database arity-table) ; nil))) ; Historical Comment: ; Note on the Hard Error below: How might this error arise? The intuitive ; argument that it doesn't goes like this: This function is called in ; forward-chain, on something produced by forward-chain1. But inspection of ; forward-chain1 shows that it uses type-alist-fcd-lst to check that approved ; fc derivations are not contradictory. What can go wrong? Well, one thing ; that has gone wrong is that type-alist-fcd-lst looks at the derivations in a ; different order than they are looked at by this function. Hence, the old ; familiar type-alist-clause bugaboo (order of the literals) comes into play. ; We have seen an example where forward-chain1 checked ((< 0 x) (< x 1) ; (integerp x)) and found no contradiction but then passed the reversed list to ; this function which found the contradiction and caused the hard error for the ; first time ever. Our response to that was to put a reconsider-type-alist ; into type-alist-fcd-lst. But our "proof" that this hard error never arises ; is now suspect. (cond ((null fc-pair-lst) (mv nil type-alist nil)) (t (mv-let (mbt mbf tta fta ttree) (assume-true-false (car (car fc-pair-lst)) (cdr (car fc-pair-lst)) force-flg nil type-alist ens wrld nil nil :fta) (declare (ignore fta)) (cond (mbf (mv t nil ttree)) (mbt (prog2$ (filter-redundant-approved-fc-derivation (car fcd-lst)) (fc-pair-lst-type-alist (cdr fc-pair-lst) (cdr fcd-lst) type-alist force-flg ens wrld))) (t (fc-pair-lst-type-alist (cdr fc-pair-lst) (cdr fcd-lst) tta force-flg ens wrld))))))) ; Now we work on the heuristic for approving fc derivations. The ; problem is to avoid infinite forward chaining. So we define a ; predicate that determines whether we wish to keep a given derivation. (defmacro fcd-runep (rune ttree) ; Rune is the name of a forward chaining rule. We want to determine if rune ; has been used in any fc-derivation in ttree. This macro is analogous to ; tag-tree-occur except that it knows that 'fc-derivation tags contain other ; ttrees and it looks recursively into those ttrees too. It is a macro so that ; fcd-runep-lst can be singly recursive (which could conceivably help ; performance, but at any rate seems very unlikely to hurt). `(fcd-runep-lst ,rune (tagged-objects 'fc-derivation ,ttree))) (defun fcd-runep-lst (rune lst) (cond ((endp lst) nil) (t (or (equal rune (access fc-derivation (car lst) :rune)) (fcd-runep rune (access fc-derivation (car lst) :ttree)) (fcd-runep-lst rune (cdr lst)))))) (defmacro fcd-worse-than-or-equal (concl fn-cnt p-fn-cnt ttree) ; Concl is a term and fn-cnt is its function symbol count. If there exists a ; concl' with fn count fn-cnt' in an 'fc-derivation of ttree such that fn-cnt ; >= fn-cnt' and concl is worse-than-or-equal to concl', then we return t. ; Otherwise we return nil. We define a macro so that ; fcd-worse-than-or-equal-lst can be singly recursive (which could conceivably ; help performance, but at any rate seems very unlikely to hurt). `(fcd-worse-than-or-equal-lst ,concl ,fn-cnt ,p-fn-cnt (tagged-objects 'fc-derivation ,ttree))) (defun fcd-worse-than-or-equal-lst (concl fn-cnt p-fn-cnt lst) (cond ((endp lst) nil) (t (or (and (let ((fc-fn-cnt (access fc-derivation (car lst) :fn-cnt))) (or (> fn-cnt fc-fn-cnt) (and (eql fn-cnt fc-fn-cnt) (>= p-fn-cnt (access fc-derivation (car lst) :p-fn-cnt))))) (worse-than-or-equal concl (access fc-derivation (car lst) :concl))) (fcd-worse-than-or-equal concl fn-cnt p-fn-cnt (access fc-derivation (car lst) :ttree)) (fcd-worse-than-or-equal-lst concl fn-cnt p-fn-cnt (cdr lst)))))) ; Once upon a time we had heuristics for keeping concl if there was ; a lit of the current clause that was worse than it or if there was a ; concl already kept that was worse than it. We have ; removed those heuristics and replaced them by the faster check that the ; triggering term occurs in the clause. But we'll keep the ; definitions in case we want to reinstate the heuristics. ; (defun exists-lit-worse-than-or-equal (cl concl fn-cnt) ; (cond ; ((null cl) nil) ; (t (or (and (>= (fn-count (car cl)) fn-cnt) ; (worse-than-or-equal (car cl) concl)) ; (exists-lit-worse-than-or-equal (cdr cl) ; concl ; fn-cnt))))) (defun exists-fcd-worse-than-or-equal (fcd-lst concl fn-cnt p-fn-cnt) (cond ((null fcd-lst) nil) (t (or (and (let ((fcd-fn-cnt (access fc-derivation (car fcd-lst) :fn-cnt))) (or (> fcd-fn-cnt fn-cnt) (and (eql fcd-fn-cnt fn-cnt) (>= (access fc-derivation (car fcd-lst) :p-fn-cnt) p-fn-cnt)))) (worse-than-or-equal (access fc-derivation (car fcd-lst) :concl) concl)) (exists-fcd-worse-than-or-equal (cdr fcd-lst) concl fn-cnt p-fn-cnt))))) (defun all-dumb-occur-lst (args cl) (cond ((endp args) t) (t (and (dumb-occur-lst (car args) cl) (all-dumb-occur-lst (cdr args) cl))))) (defun all-args-occur-after-strip-not (term cl) ; One of our heuristics for approving a derivation is that all of the ; arguments appearing in its conclusion occur in cl. This function ; checks that when term is the :concl of an fc-derivation. Roughly ; speaking, we check that every arg of term occurs in cl. However, we ; first strip off any NOTs that surround term. Rather arbitrarily, if ; the resulting atom is a variable, we return t, and if it is a constant ; we return nil. (cond ((variablep term) t) ((fquotep term) nil) ((eq (ffn-symb term) 'not) (all-args-occur-after-strip-not (fargn term 1) cl)) (t (all-dumb-occur-lst (fargs term) cl)))) (defun approved-fc-derivationp (fcd cl) ; We return t iff we approve fcd as a new fact we will add to fcd-lst ; while forward chaining from clause cl. ; Once upon a time, our heuristic for approving an fc-derivation is ; that one of the following 4 conditions is satisfied. (a) The ; relevant forward-chaining rune has not been used before in this ; derivation. (b) Concl is not worse-than-or-equal any concl in its ; derivation. (c) The triggering term of this fcd is in the current ; clause. (d) All of the args of concl occur in the clause. However, ; after an improvement to the forward-chaining code to extract new ; trigger terms from all approved conclusions, we found that condition ; (c) was unnecessary and, in fact, could cause forward-chaining to ; loop indefinitely. So (c) has been commented out below. (let ((ttree (access fc-derivation fcd :ttree))) (or (not (fcd-runep (access fc-derivation fcd :rune) ttree)) ; (a) (not (fcd-worse-than-or-equal (access fc-derivation fcd :concl) ; (b) (access fc-derivation fcd :fn-cnt) (access fc-derivation fcd :p-fn-cnt) ttree)) ; (dumb-occur-lst (access fc-derivation fcd :inst-trigger) cl) ; (c) ; There is one more condition, (d), below, but first a big comment ; explaining it. If all of the arguments of the conclusion (ignoring ; any leading NOTs) of the forward-chaining rule appear in the clause, ; we approve the result. Dave Greve has encountered cases where this ; extra flexibility is important for making type-like forward-chaining ; derivations, as illustrated by the following example. ; (defstub con (x y) nil) ; (defstub des (x) nil) ; ; (defstub typec (x) nil) ; (defstub typeg (x) nil) ; (defstub typed (x) nil) ; ; (defaxiom typed-implies-typeg ; (implies ; (typed x) ; (typeg x)) ; :rule-classes (:rewrite :forward-chaining)) ; ; (defaxiom typeg-des ; (implies ; (typec x) ; (typed (des x))) ; :rule-classes (:rewrite ; (:forward-chaining :trigger-terms ((des x))))) ; ; (defaxiom typec-con ; (implies ; (and ; (natp n) ; (typeg x)) ; (typec (con x n))) ; :rule-classes (:rewrite ; (:forward-chaining :trigger-terms ((con x n))))) ; ; (defun several (g) ; (let* ((c (con g 1)) ; (g (des c)) ; (c (con g 2)) ; (g (des c)) ; (c (con g 3)) ; (g (des c))) ; (con g 4))) ; ; (in-theory (disable ; (:rewrite typec-con) ; (:rewrite typeg-des) ; (:rewrite typed-implies-typeg) ; )) ; ; ; The following fails without the call below of ; ; all-args-occur-after-strip-not below unless we remove the ; ; in-theory event above. ; (defthm typec-several ; (implies ; (typed g) ; (typec (several g)))) (all-args-occur-after-strip-not (access fc-derivation fcd :concl) ; (d) cl)))) (defun approve-fc-derivations (new-fcd-lst cl approved-this-round all-approved) ; We have just derived the fc-derivations in new-fcd-lst, from the ; negations of the literals in cl. We filter out those new ; fc-derivations that we do not approve. We add the approved ones to ; both approved-this-round and all-approved. The former is initially ; nil within a given round and is thus the approved derivations of that ; round. The latter is cumulative across all rounds. We return both. (cond ((null new-fcd-lst) (mv approved-this-round all-approved)) ((approved-fc-derivationp (car new-fcd-lst) cl) (approve-fc-derivations (cdr new-fcd-lst) cl (cons (car new-fcd-lst) approved-this-round) (cons (car new-fcd-lst) all-approved))) (t (approve-fc-derivations (cdr new-fcd-lst) cl approved-this-round all-approved)))) ; Once we have a batch of approved derivations, we sort them so the ; ``simpler'' ones appear first. We will then assume them in that ; order. The heuristic is that simpler conclusions might strengthen ; what we learn about subsequent ones, as would happen if we assumed ; (integerp x) before we assumed (integerp (foo x)). (mutual-recursion (defun max-level-no (term wrld) ; Each defun'd function, except the ones being defund at the moment, ; has a 'level-no property, which is a non-negative integer. The ACL2 ; primitives have no level-no property, which we treat as though it were ; 0. This function computes the maximum stored level-no of the functions ; appearing in term. Any fn appearing without a level-no is treated ; as though it had level 0, i.e., it is ignored. (cond ((variablep term) 0) ((fquotep term) 0) (t (max (get-level-no (ffn-symb term) wrld) (max-level-no-lst (fargs term) wrld))))) (defun max-level-no-lst (args wrld) (cond ((null args) 0) (t (max (max-level-no (car args) wrld) (max-level-no-lst (cdr args) wrld))))) (defun get-level-no (fn wrld) ; Fn is either a lambda expression or a function symbol. We return ; its level number. (cond ((flambdap fn) (max-level-no (lambda-body fn) wrld)) ((getprop fn 'level-no nil 'current-acl2-world wrld)) (t 0))) ) (mutual-recursion (defun sort-approved1-rating1 (term wrld fc vc) (cond ((variablep term) (mv fc (1+ vc))) ((fquotep term) (mv fc vc)) ((flambda-applicationp term) (mv-let (fc vc) (sort-approved1-rating1 (lambda-body term) wrld fc vc) (sort-approved1-rating1-lst (fargs term) wrld (1+ fc) vc))) ((or (eq (ffn-symb term) 'not) (= (getprop (ffn-symb term) 'absolute-event-number 0 'current-acl2-world wrld) 0)) (sort-approved1-rating1-lst (fargs term) wrld fc vc)) (t (sort-approved1-rating1-lst (fargs term) wrld (+ 1 (get-level-no (ffn-symb term) wrld) fc) vc)))) (defun sort-approved1-rating1-lst (lst wrld fc vc) (cond ((null lst) (mv fc vc)) (t (mv-let (fc vc) (sort-approved1-rating1 (car lst) wrld fc vc) (sort-approved1-rating1-lst (cdr lst) wrld fc vc))))) ) (defun sort-approved1-rating (term wrld) ; In forward-chaining we assume all the derived concls. We sort them by the ; ratings computed here, assuming first those terms with the highest rating. ; Therefore, we wish to give high numbers to very type-like terms such as ; (rationalp x) and (not (< x '0)). Actually, all our ratings are nonpositive ; integers, with 0 thus the highest. The terms pictured above have ratings of ; -1 because they contain a single variable and are otherwise completely ; primitive. If you assume no term contains more than 10 variable occurrences ; then the ordering imposed by these ratings is lexicographic, favoring ; low function count and using variable occurrences to break ties. No ; real consideration has been given this measure beyond that it puts ; the terms above before others! (mv-let (fc vc) (sort-approved1-rating1 term wrld 0 0) (- (+ (* 10 fc) vc)))) (defun sort-approved1 (approved wrld) (cond ((null approved) nil) (t (cons (cons (sort-approved1-rating (access fc-derivation (car approved) :concl) wrld) (car approved)) (sort-approved1 (cdr approved) wrld))))) (defun sort-approved (approved wrld) ; Approved is a list of fc-derivations which have derived certain :concls. ; We sort that list so that those with the higher rated :concls come first. (strip-cdrs (merge-sort-car-> (sort-approved1 approved wrld)))) (defun strip-fcd-concls (fcd-lst) (cond ((null fcd-lst) nil) (t (cons (access fc-derivation (car fcd-lst) :concl) (strip-fcd-concls (cdr fcd-lst)))))) ; Upon obtaining the approved derived conclusions, we need to extend the ; type-alist with them. (defun type-alist-fcd-lst (fcd-lst type-alist do-not-reconsiderp force-flg ens wrld) ; We take a list of fc-derivations and assume the truth of each concl, ; extending type-alist. We return two results. The first is t or nil ; indicating whether a contradiction was found. When a contradiction is ; found, the second result is the ttree of that contradiction. When a ; contradiction is not found, the second is the final type-alist. In ; both cases, the second result is not fcd-free. ; Note that when we finish, (endp fcd-lst), we reconsider the type-alist. This ; is analogous to type-alist-clause-finish. We have seen an example of forward ; chaining where we derived, in order, (< 0 x), (< x 1), (integerp x), and ; failed to recognize the contradiction, just as type-alist-clause-finish1 ; fails to recognize that contradiction. (cond ((endp fcd-lst) (if do-not-reconsiderp (mv nil type-alist) (mv-let (contradictionp xtype-alist ttree) (reconsider-type-alist type-alist type-alist nil ens wrld nil nil) (cond (contradictionp (mv t ttree)) (t (mv nil xtype-alist)))))) (t (mv-let (mbt mbf tta fta ttree) (assume-true-false (access fc-derivation (car fcd-lst) :concl) (add-to-tag-tree! 'fc-derivation (car fcd-lst) nil) force-flg nil type-alist ens wrld nil nil :fta) (declare (ignore fta)) (cond (mbf (mv t ttree)) (mbt (type-alist-fcd-lst (cdr fcd-lst) type-alist do-not-reconsiderp force-flg ens wrld)) (t (type-alist-fcd-lst (cdr fcd-lst) tta do-not-reconsiderp force-flg ens wrld))))))) ; Finally, we have to detect ``stability'' as we repeatedly do rounds of ; forward chaining. One aspect of stability is that every approved ; conclusion is already in the list of trigger terms in the problem. (defun every-concl-member-equalp (fcd-lst trigger-terms) ; Fcd-lst is a list of fc-derivations. We return t if the :concl of ; every element of fcd-lst is a member-equal of trigger-terms. (cond ((endp fcd-lst) t) ((member-equal (access fc-derivation (car fcd-lst) :concl) trigger-terms) (every-concl-member-equalp (cdr fcd-lst) trigger-terms)) (t nil))) ; Now we are ready to define the function that carries out successive ; rounds of a forward chaining. (defun forward-chain1 (cl fc-round trigger-terms activations type-alist force-flg wrld do-not-reconsiderp ens oncep-override state all-approved-fcds) ; Cl is a clause and fc-round is the current forward chaining round ; number. Trigger-terms is the list of every subterm in the problem ; whose top function symbol has forward chaining rules. Activations is ; the list of all (suspended) activations. We first advance every ; activation, obtaining a new list of activations and some derived ; conclusions represented as fcds. We filter the derived conclusions, ; throwing out any that, on heuristic grounds, we don't like. We then ; assume the approved ones, updating the type-alist. Some approved ; conclusions may not give us any new type information, e.g., they are ; already encoded in the type-alist, but we keep track of those ; conclusions anyway because they might give us new trigger terms. We ; then add activations for all the new trigger terms and appropriately ; extend trigger-terms. Then we repeat this process until either we get ; a contradiction or we stabilize. ; We return (mv flg ttree all-approved-fcds fc-round activations). If flg is ; t, then we found a contradiction and ttree is a (not fcd-free) ttree. ; Otherwise, ttree is nil. In both cases, all-approved-fcds is the accumulated ; list of all approved fc-derivations produced during forward-chaining, ; fc-round is the final fc-round number, and activations is the list of ; still-suspended activations at the end of the process. These last two are ; only used in the trace facility for forward-chaining. ; Note: The extended type-alist we build here is of no use outside ; forward chaining because it is full of fc-derivations. We return two ; results. The first is a t or nil indicating whether a contradiction ; was found. The second is a ttree if a contradiction was found and is ; the final fcd-lst otherwise. (mv-let (activations1 fcd-lst1) (advance-fc-activations activations fc-round type-alist ens force-flg wrld state oncep-override nil ; initial new activations nil ; initial new derived concls ) (prog2$ (filter-all-satisfying-fc-derivations fcd-lst1) ; (FC Reporting) (mv-let (approved-this-round all-approved-fcds) (approve-fc-derivations fcd-lst1 cl nil ; initial approved this round all-approved-fcds) (mv-let (contradictionp x) (type-alist-fcd-lst (sort-approved approved-this-round wrld) type-alist do-not-reconsiderp force-flg ens wrld) ; If contradictionp is t, x is a ttree; otherwise, x is a type-alist. ; In any case, x is not fcd-free. (cond (contradictionp ; Note: x, below, is a ttree and is not fcd-free. (mv t x all-approved-fcds fc-round activations1)) ; Note: x, below, is a type-alist and is not fcd-free. ((and (equal x type-alist) (every-concl-member-equalp approved-this-round trigger-terms)) (mv nil nil all-approved-fcds fc-round activations1)) (t (mv-let (trigger-terms1 activations1) (collect-terms-and-activations-from-fcd-lst approved-this-round wrld ens trigger-terms activations1) (forward-chain1 cl (+ 1 fc-round) trigger-terms1 activations1 x ; type-alist force-flg wrld do-not-reconsiderp ens oncep-override state all-approved-fcds))))))))) (defun forward-chain-top (caller cl pts force-flg do-not-reconsiderp wrld ens oncep-override state) ; The only difference between forward-chain-top and forward-chain is that this ; function allows the caller to identify itself; forward-chain just uses the ; 'miscellaneous caller so that tool books that use forward chaining don't have ; to be changed. ; We forward chain in all possible ways from clause cl. We return three ; results, (mv flg type-alist ttree-or-fc-pairs), where type-alist is nil if ; flg is t and the last result is either a ttree (flg=t) or fc-pairs (flg=nil) ; as described below. Thus, the answer is of one of the forms: ; (mv t nil ttree) or (mv nil type-alist fc-pairs). ; Flg is either t or nil indicating whether a contradiction was found. If so, ; the second result is nil and the third is an fcd-free ttree that encodes the ; 'lemmas and literals used (via 'pt tags). If no contradiction is found, the ; second result is an fcd-free type-alist obtained by assuming false all of the ; literals of cl (this type-alist is fully tagged with 'pt tags) plus all of ; the conclusions derived from forward chaining; the third is a list of ; fc-pairs, each of the form (concl . ttree), where concl is a truth derived ; from some subset of the negations of literals of cl and ttree is fcd-free and ; tags the :FORWARD-CHAINING 'lemmas used and all parents (via 'pt tags). ; Note: The type-alist returned assumes the falsity of every literal in ; the clause and thus is not suitable for use by rewrite. We return it ; strictly for the use of setup-simplify-clause-pot-lst and bdd-clause. ; In reading the code below, read (fc-exit a b c ...) as though it ; were (mv a b c). The stuff in ... is just used in the reporting. (prog2$ (new-fc-call caller cl pts force-flg do-not-reconsiderp wrld ens oncep-override) (mv-let (contradictionp type-alist ttree1) (type-alist-clause cl (pts-to-ttree-lst pts) nil nil ens wrld nil nil) ; If a contradiction was found, type-alist is nil and ttree1 is an fcd-free ; tree explaining the contradiction. Otherwise, type-alist is the type-alist ; produced by assuming all the literals false and ttree1 is nil. (cond (contradictionp (mv t nil ttree1)) (t (mv-let (trigger-terms activations) (collect-terms-and-activations-lst cl nil wrld ens nil nil) ; Trigger-terms is the list of all subterms of cl whose top function ; symbols have fc rules and activations is the list of all (suspended) ; activations triggered by those subterms. (mv-let (contradictionp ttree2 all-approved-fcds rounds activations1) (pstk (forward-chain1 cl 1 trigger-terms activations type-alist force-flg wrld do-not-reconsiderp ens oncep-override state nil)) (cond (contradictionp ; If a contradiction was found by forward chaining, ttree2 is the ttree that ; derives it. But it is not fcd-free and we need to make it fcd-free ; before letting it out of the forward-chaining module. (fc-exit t nil (expunge-fc-derivations ttree2) ; We return the three things above but use the following in the report: caller rounds all-approved-fcds activations1)) (t ; If no contradiction was found, ttree2 is nil. We need to convert ; all-approved-fcds to a list of pairs of the form (concl . ttree), where each ; ttree is fcd-free. (let ((fc-pair-lst (fc-pair-lst all-approved-fcds))) (mv-let (contradictionp type-alist3 ttree3) (fc-pair-lst-type-alist fc-pair-lst all-approved-fcds type-alist force-flg ens wrld) (cond (contradictionp (fc-exit t nil ttree3 ; (mv t nil ttree3) ; ... and the stuff we need to do reporting ... caller rounds all-approved-fcds activations1)) (t (mv-let (contradictionp type-alist4 ttree4) (type-alist-equality-loop type-alist3 ens wrld *type-alist-equality-loop-max-depth*) (cond (contradictionp (fc-exit t nil ttree4 ; (mv t nil ttree4) ; ... and the stuff we need to do reporting ... caller rounds all-approved-fcds activations1)) (t (fc-exit nil type-alist4 fc-pair-lst ; (mv nil type-alist4 fc-pair-lst) ; ... and the stuff we need to do reporting ... caller rounds all-approved-fcds activations1))))))))))))))))) (defun forward-chain (cl pts force-flg do-not-reconsiderp wrld ens oncep-override state) ; This is a version of forward-chain that is backwards compatible with the ; Version_4.1 signature, which did not allow the caller to identify itself. It ; is defined so it can be used in books like the expander. (forward-chain-top 'miscellaneous cl pts force-flg do-not-reconsiderp wrld ens oncep-override state)) ; When forward-chain has done its job and produced an fc-pair list, ; we will pass that list to rewrite-clause. Rewrite-clause rewrites ; each literal in turn, under a type-alist constructed from the remaining ; literals (some of which will have been rewritten since forward-chain ; constructed the type-alist returned above) and from the fc-pair list. ; Here is how we construct the type-alist: (defun select-forward-chained-concls-and-ttrees (fc-pair-lst pt lits ttree-lst) ; Fc-pair-lst is a list of pairs of the form (concl . ttree). Each ttree ; contains 'pt tags indicating the parents of concl. Pt is a parent tree. ; Consider those elements of fc-pair-lst, say fc-pair-lst', whose parents are ; disjoint from pt. While working on the literals in pt we are permitted to ; assume the truth of every concl in fc-pair-lst'. This function computes ; fc-pair-lst' and destructures it into two lists which we return in the form ; of (mv lits ttree-lst). Lits and ttree-lst are in 1:1 correspondence. Each ; lit is the negation of a concl in fc-pair-lst' and the corresponding ttree is ; the ttree for concl in fc-pair-lst'. Thus, lits can be thought of as a ; clause segment that can be appended to the other literals we get to assume ; false while working on pt. The ttrees in ttree-lst may have 'assumption tags ; because forwarding chaining may FORCE or CASE-SPLIT. (cond ((null fc-pair-lst) (mv lits ttree-lst)) ((to-be-ignoredp (cdr (car fc-pair-lst)) pt) (select-forward-chained-concls-and-ttrees (cdr fc-pair-lst) pt lits ttree-lst)) (t (select-forward-chained-concls-and-ttrees (cdr fc-pair-lst) pt (cons (dumb-negate-lit (car (car fc-pair-lst))) lits) (cons (cdr (car fc-pair-lst)) ttree-lst))))) ; Essay on the Construction of the Type-alist to Rewrite the Current Literal ; Simplification sweeps across the literals of a clause, rewriting each in turn ; while assuming the others false. After rewriting a literal, we clausify the ; result into n clause segments [extending other already-rewritten segments] ; and rewrite the next literal under (the falsity of each literal in) each of ; those segments together with the remaining literals and any available ; conclusions produced by forward chaining. Thus, to get the type-alist to be ; used while rewriting ``the current literal'' we assume the falsity of three ; lists of literals: new-clause [the clause segement obtained from one path ; through the previously rewritten literals], (cdr tail) [the rest of the ; unrewritten literals], and lits [the literals derived by forward chaining]. ; We use the ordinary type-alist-clause to create the new type-alist. The ; question is: in which order shall we combine these three lists to give to ; type-alist-clause? ; Warning: Note that rearranging the order in which we make these assumptions ; reorders the type-alist! But this can be a Very Big Deal. Different rules ; might fire because one type-alist is actually stronger than another, ; different free variable choices may be available because we run into ; different hypotheses (in different orders) suggesting bindings, and the order ; of literals in forced subgoals may be different because we reconstruct forced ; subgoals from converting the governing type-alist into a conjunction of ; terms. Experimenting with reordering is a costly experiment. ; We have tried three approaches: (append lits new-clause (cdr tail)), (append ; new-clause (cdr tail) lits), and a ``smart'' approach in which we sort the ; literals to put the smaller ones first, thereby allowing their type-sets to ; improve, perhaps, the type-sets computed for larger literals (like disjuctive ; ones (IF a a b)) involving the some of the smaller ones. The code deleted ; below was part of this ``smart'' approach. All of these reordering ; strategies must maintain the correspondence between the forward-chained ; literals and the ttrees that produced them and some of the code below deals ; with how to permute two lists so as to order one by size and keep the result ; in 1:1 correspondence with the permuted other. ; Start Experimental Code ; (mutual-recursion ; (defun term-size (term) ; ; ; This computes the number of conses in a term, down to (but not including) the ; ; quoted constants. This is just an ``arbitrary'' measure with the following ; ; two properties: (a) it is fast to compute, though one might someday try to ; ; speed it up via memoization, and (b) it has the property that if a and b are ; ; two non-constant terms and term a occurs inside of term b, then the size of a ; ; is less than the size of b. This is expoloited to reorder a clause so that ; ; the smaller literals come first during the process of sequentially assuming ; ; their falsity to construct a type-alist to use in the rewriting of some other ; ; literal. See rewrite-clause-type-alist. ; ; (cond ((variablep term) 1) ; ((fquotep term) 1) ; (t (+ 1 (term-size-lst (fargs term)))))) ; (defun term-size-lst (term-lst) ; (cond ((endp term-lst) 0) ; (t (+ (term-size (car term-lst)) ; (term-size-lst (cdr term-lst))))))) ; ; ; Suppose x is some clause and y is some list of ttrees in 1:1 correspondence ; ; with x. We wish to reorder the literals of x according to term-size and to ; ; apply the same permutation to y, so that the correspondence of literals to ; ; ttrees is preserved. We do this by constructing a list of elements (size xi ; ; . yi), where xi and yi are corresponding elements of x and y, sorting that ; ; list by its cars, and then stripping out the xi to get the new x' and the yi ; ; to get the new yi. ; ; (defun pairlis-with-rankings (x y ans) ; ; See comment above. If y is too short, we extend it with nils to match x. ; (cond ((endp x) ans) ; (t (pairlis-with-rankings ; (cdr x) (cdr y) ; (cons (cons (term-size (car x)) (cons (car x) (car y))) ; ans))))) ; ; (defun reorder-lits-and-ttrees-for-type-alist-clause ; (lits1 ttree-lst1 lits2 ttree-lst2 lits3 ttree-lst3) ; (let ((triples ; (merge-sort-car-< ; (pairlis-with-rankings ; lits1 ttree-lst1 ; (pairlis-with-rankings ; lits2 ttree-lst2 ; (pairlis-with-rankings lits3 ttree-lst3 nil)))))) ; (mv (strip-cadrs triples) ; (strip-cddrs triples)))) ; End Experimental Code ; We started (back in 1989) with the Nqthm idea of just concatenating ; new-clause and (cdr tail); at that time, forward chaining lits didn't exist. ; When forward-chaining was introduced, we experimented and ultimately decided ; to try the order (append lits new-clause (cdr tail)). We did not use (or ; even have) the function reorder-lits-and-ttrees-for-type-alist-clause and ; simply appended the ttree lists in the same order. The following comment is ; preserved from versions dating back to the mid-1990s through Version_6.1: ; Historical Comment: ; Observe below that we put the forward-chained concls first. The problem that ; led us to do this was the toy example shown below (extracted from a harder ; failed proof attempt). The thm below fails if you process the literals in ; the order (append new-clause (cdr tail) lits). ; (defstub p (x) t) ; (defstub r (x) t) ; (defaxiom p->r ; (implies (p x) ; (r x)) ; :rule-classes :forward-chaining) ; (defstub my-assoc (name l) t) ; (defaxiom blob ; (implies (r l) ; (or (consp (my-assoc name l)) ; (equal (my-assoc name l) nil))) ; :rule-classes :type-prescription) ; (thm ; (implies (p l) ; (or (consp (my-assoc name l)) ; (equal (my-assoc name l) nil)))) ; As a clause the theorem is ; (implies (and (p l) ; (not (consp (my-assoc name l)))) ; (equal (my-assoc name l) nil)). ; Consider what happens when we rewrite the conclusion assuming the hyps. We ; have (p l) and (not (consp (my-assoc name l))). We do indeed forward chain ; and get (r l) also. But the (not (consp (my-assoc name l))) puts the ; following pair on the type-alist: ; ((my-assoc name l) -1537) ; ts-complement of consp ; Thus, when we go to get the type-set of (my-assoc name l) we don't even look ; at the rules about my-assoc, we just return -1537. ; Three fixes are possible. First, process lits first so that (r l) is ; available when we do the (consp (my-assoc name l)). Second, change type-set ; so that it uses rules about my-assoc even if the term (my-assoc name l) is ; bound on the type-alist. Third, modify type-alist-clause so that it iterates ; as long as the type-alist changes so that it is not important in what order ; the lits are processed. The second alternative seems potentially very slow, ; though we have done no experiments. The third alternative is hard because ; one must ignore known types on the type-alist when reprocessing the lits. ; Feb 9, 1995. We are trying a version of the third alternative, with ; reconsider-type-alist and the double whammy flag. ; End of Historical Comment ; In April, 2013, we experimented with the ``smart'' approach and temporarily ; introduced reorder-lits-and-ttrees-for-type-alist-clause into what would ; become Version_6.2. In all the examples we looked at, the type-alists ; produced by this method were at least as strong as those produced by the ; earlier method. Sometimes they are actually better, especially when the ; conclusions produced by forward-chained are disjunctions, e.g., (IF a a b), ; where earlier assumptions about a or b may give us stronger type-sets about b ; or a. ; The warning above about the effects of changing the order of the type-alist ; came to the fore in this experiment. We found that 500 of the 3100+ books ; failed the regression. (Of course, presumably many failed because they ; merely depended on books that failed for type-alist reasons.) In any case, ; we abandoned the smart approach. ; But then we moved back to the (append new-clause (cdr tail) lits) approach ; dismissed earlier in the Historical Comment above. The reasons for this ; ordering are fairly compelling: if one is to forward-chain to disjunctions ; they ought to be processed last so we can take advantage of known-false ; disjuncts within them. We tried the old example cited in the Historical ; Comment above and it works under this approach -- presumably because in the ; ~20 years since that example was recorded, the system has changed in other ; ways (e.g., the sophistication now in assume-true-false-if and ; reconsider-type-alist). But, not withstanding the Warning above about the ; dangers of reordering the type-alist, only three contemporary (April, 2013) ; books failed due to reordering reasons: ; books/centaur/bitops/congruences.lisp ; books/models/y86/y86-basic/common/read-over-write-proofs.lisp ; books/demos/modeling/network-state.lisp ; We decided to ``patch'' these proof scripts and stay with the ``forward-chained ; lits last'' reordering strategy. ; Search those books for: "; Reordering the rewrite-clause-type-alist" to see ; the three patched events. Only one event in each book had to modified. In ; two of the books one rune had to be disabled in each event (because that rule ; was able to fire in the new reordering but the proof had been designed when ; that rule was not firing). The runes are obscure (if trying to reconstruct ; the proof via The Method) but were obtained simply by determining the runes ; for the failed subgoal under reordering that were not fired by the successful ; proof of that same subgoal. Once the set of such runes was identified we ; could experiment to determine a ``minimal'' sufficient set (in each case a ; set of size 1). In the third book (network-state.lisp) we proved a lemma ; that drastically simplified the affected proof. ; Our decision to change after Version_6.1 to using (append new-clause (cdr ; tail) lits) instead of the former (append lits new-clause (cdr tail)) was ; motivated by the following example from Dave Greve. In this example, Dave ; expected the rewrite rule to suffice, but it did not. It does now. ; (defstub a-p (x) nil) ; (defstub b-p (x) nil) ; (defstub c-p (x) nil) ; (defstub d-p (x) nil) ; ; (defun x-p (x) ; (or (a-p x) ; (b-p x) ; (c-p x) ; (d-p x))) ; ; (defthm forward ; (implies ; (x-p x) ; (or (a-p x) ; (b-p x) ; (c-p x) ; (d-p x))) ; :rule-classes (:forward-chaining)) ; ; (in-theory (disable x-p)) ; ; (defun z-p (x) ; (c-p x)) ; ; (defthm goo ; (implies ; (c-p x) ; (z-p x)) ; :rule-classes (:rewrite :type-prescription)) ; ; (in-theory (disable z-p)) ; ; (in-theory (disable (:type-prescription goo))) ; ; ; Fails, but Dave expected that (:rewrite goo) would suffice. ; ; (defthm zoo ; (implies ; (and ; (x-p x) ; (not (a-p x)) ; (not (b-p x)) ; (not (d-p x))) ; (z-p x))) (defun rewrite-clause-type-alist (tail new-clause fc-pair-lst rcnst wrld pot-lst pt) ; We construct a type alist in which we assume (a) the falsity of every literal ; in tail except the first, (b) the falsity of every literal in new-clause, and ; (c) the truth of every concl in fc-pair-lst that is not dependent upon ; any literal noted in the parent tree (:pt) of rcnst. ; We do this by constructing a clause containing the literals in question ; (negating the concls in fc-pair-lst) and calling our general purpose ; type-alist-clause. As of v2-8, we also pass in the simplify-clause-pot-lst ; to aid in the endeavor since type-set and assume-true-false can now ; (weakly) use linear arithmetic. ; We return a four-tuple, (mv contradictionp type-alist ttree current-clause), ; where contradictionp is t or nil and indicates whether we derived a ; contradiction. Type-alist is the constructed type-alist (or nil if we got a ; contradiction). Ttree is a ttree explaining the contradiction (or nil if got ; no contradiction). Current-clause is the clause used in the computation ; described immediately above. ; Note: The type-alist returned may contain 'assumption tags. In addition, the ; type-alist may contain some 'pt tags -- the conclusions derived by forward ; chaining will have their respective ttrees attached to them and these will ; have 'pt tags and could have 'assumptions. We could throw out the 'pt tags ; if we wanted -- we are allowed to use everything in this type-alist because ; we only put accessible assumptions in it -- but we don't. We must record the ; ttrees because of the possible 'assumption tags. (mv-let (lits ttree-lst) (select-forward-chained-concls-and-ttrees fc-pair-lst (access rewrite-constant rcnst :pt) nil nil) (mv-let (current-clause current-ttree-lst) ; The ``smart'' approach was this: ; (reorder-lits-and-ttrees-for-type-alist-clause new-clause nil ; (cdr tail) nil ; lits ttree-lst) ; See the essay above for explanations. (mv (append new-clause (cdr tail) lits) (make-list-ac (+ (len new-clause) (len (cdr tail))) nil ttree-lst)) (mv-let (contradictionp type-alist ttree) (type-alist-clause current-clause current-ttree-lst nil ; force-flg nil ; initial type-alist (access rewrite-constant rcnst :current-enabled-structure) wrld pot-lst pt) (mv contradictionp type-alist ttree current-clause))))) ; Historical Plaque on Forward Chaining ; General purpose forward chaining was not implemented in Nqthm, although ; the linear arithmetic package and :COMPOUND-RECOGNIZER lemmas were (and ; still are) examples of forward-chaining reasoning. The first two ; implementations of general purpose forward chaining in ACL2 occurred ; last week (April 9-13, 1990). They were both implemented one level ; below where the current forward chaining module sits: we did forward ; chaining just before rewriting each literal of the clause, rather ; than doing all the forward chaining once and tracking dependencies. ; They were both abandoned because of inefficiency. The killer was -- we ; think -- the repeated duplication of forward chaining derivations. For ; example, if the clause to be rewritten was {~a ~b c1 ... ck} and an ; elaborate forward chaining tree can be built from a and b, then that ; tree was built when we began to rewrite c1 and that tree was built ; again when we began to rewrite c2, etc. In addition, the old forward ; chaining scheme did not include the idea of triggers, it forward ; chained off the first hypothesis of a :FORWARD-CHAINING rule. Finally, ; the old scheme used full fledged relieve-hyps to relieve the other hyps ; of the rules -- another potential killer but one that didn't get us ; simply because we had no forward chaining rules with more than one hyp ; in our tests. ; However, in an effort to help software archeologists (not to mention ; the possibility that we might help ourselves avoid repetition of past ; mistakes) we inscribe here an extensive comment written last week: ; The Forward Chaining Essay - Version II (This essay is of at most historic ; interest. For the current version of forward chaining, search for ; Forward Chaining from the top of this file.) ; We are about to start rewriting the current literal under the ; assumption of the negations of the literals in clause-seg. We wish to ; forward chain off of these assumptions to generate a type-alist ; suitable for use during the rewriting. ; We return three values: t or nil indicating whether a contradiction was ; found while forward chaining, a new type-alist, and a ttree recording ; the forward-chaining-rules used. ; The form of a :FORWARD-CHAINING rule is: ; (defrec forward-chaining-rule ; ((rune . nume) key-hyp other-hyps . concls) nil) ; If a lemma such as ; (implies (and hyp1 hyp2 ... hypn) (and concl1 ... conclk)) ; is processed as a :FORWARD-CHAINING rule named name we will generate: ; (make forward-chaining-rule ; :rune rune ; :nume & ; :key-hyp hyp1 ; :other-hyps (hyp2 ... hypn) ; :concls (concl1 ... conclk) ; :match-free once_or_all) ; which is stored under the 'forward-chaining-rules property of the top ; function symbol of hyp1. By "top function symbol" we mean the outer ; most function symbol after stripping away any top-level NOT. ; When we apply a forward-chaining-rule we have a context defined by the ; set of assumptions off of which we are forward chaining (which is ; initially obtained by negating the literals of clause-seg) and a ; type-alist encoding those assumptions. Our main result is, of course, ; the final type-alist. But the set of assumptions is represented ; explicitly (indeed, somewhat elaborately) to support heuristics ; designed to avoid infinite loops while permitting the desired forward ; chaining. ; The list of assumptions is more properly thought of as the history of ; this forward chaining problem and is held in the variable fc-history. ; More on its structure later. ; Roughly speaking, one applies a :FORWARD-CHAINING rule to a term, hyp1', ; as follows: unify :key-hyp with hyp1' and then relieve-hyps the ; :other-hyps. If those two steps do not succeed, the application fails. ; If they work, then make a heuristic decision about whether the ; resulting instance of :concls is worthwhile. If it is not, the ; application fails. If it is, add concl to the fc-history and ; type-alist and say the application succeeded. ; The structure of fc-history sufficient to support our current ; heuristics has evolved from a naive structure that just listed the ; assumptions made so far. Initially, our heuristic decision was simply ; whether the candidate concl was worse-than any existing assumption. ; But imagine that among the initial hypotheses are (ASSOC & &) and ; (STATE-P1 S). And imagine that some forward chaining rule lets you ; pump forward from (STATE-P1 S) to (P (CDR (ASSOC & &))). Then you ; wouldn't get to use that rule because its conclusion is worse than ; (ASSOC & &). This was the first indication that worse-than alone was ; too restrictive. We fixed this by distinguishing the initial ; assumptions from those produced by forward chaining and we did the ; worse-than check only on the newly added ones. ; However, the next problem was illustrated by having two forward ; chaining rules: ; name1: (state-p1 x) -> (p (nth 2 state)) ; name2: (state-p1 x) -> (p (nth 3 state)), ; that can get in eachother's way. If the first is used to add its ; conclusion then the second cannot be used because its conclusion is ; worse than that just added. ; So the structure of fc-history is now a list of pairs, each of the form ; (term . hist), where term is one of our assumptions and hist is the ; history of term. If term is among the initial assumptions, then hist ; is nil. If term was produced by the rule named name from some term' ; with history hist', then hist is (name term' . hist'). ; Thus, another way to view it is that each entry in fc-history is of the ; form (term namek termk ... name2 term2 name1 term1) and means that term ; was produced by a chain of k forward chaining steps: starting with ; term1 (which is in the initial set of assumptions) use name1 to derive ; term2, use name2 to dervie term3, ..., and use namek to derive term. ; Our heuristic for deciding whether to keep a conclusion, concl, is if ; namek has not been used in this chain, keep concl; otherwise, if namek ; has been used, then concl must be worse than nor equal to no termi in ; its chain. ; It is very inefficient to repeatedly hit all the assumptions with all ; the rules until no change occurs. We have therefore taken steps to ; avoid unnecessary work. First, if a rule has been successfully applied ; to a term then there is no need to apply it again (only to learn that ; its conclusion is rejected). Second, if a conclusion has ever been ; produced before, there is no need to add it again (although technically ; it is probably possible to rederive it in a way that permits further ; chaining not permitted by the original derivation). Third, if a rule ; named name is applied to a term term with derivation d and produces a ; concl that is rejected because of its ancestry, then don't apply name ; to term and d again. To support this heuristic we have to keep track ; of the failed applications, which we do in the variable bad-appls. ; End of Historical Plaque ; Rockwell Addition: The nu-rewriter is applied to literals. So ; rewrite-atm is changed below. In addition, we optionally lambda ; abstract the result. We also clean up in certain ways. New code ; extends all the way to the defun of rewrite-atm. ; Essay on Lambda Abstraction ; We will do some lambda abstraction when we rewrite literals. That ; is implemented here. ; The original idea here was to expand lambdas by ordinary rewriting ; and then to fold them back up, removing duplicate occurrences of ; subterms. Consider ; ((lambda (x y) (foo x (car y) x)) ; alpha ; (cons b c)) ; This would normally expand to ; (foo alpha b alpha) ; Suppose alpha is very large. Then this is a problem. I will ; fold it back up, to get: ; (let* ((u alpha)) ; (foo u b u)) ; I have abandoned this idea as far as rewriting goes, though it ; probably still bears a closer look. But I have adopted it as an ; option for prettyprinting clauses. ; The first sub-problem is identifying the common subterms (e.g., ; alpha in (foo alpha b alpha)) to abstract away. I call this the ; multiple subterm problem. ; We say that x is a "multiple subterm" of y if x occurs more than ; once in y. We say x is a "maximal multiple subterm" of y if x is a ; multiple subterm of y and no other multiple subterm of y contains an ; occurrence of x. ; Our interest in maximal subterms is illustrated by (f (g (m x)) (g ; (m x))). (M x) is a multiple subterm. We might abstract this term ; to (let* ((v1 (m x)) (v2 (g v1))) (f v2 v2)). But if (g (m x)) is ; identified as the first multiple subterm, then we get (let ((v1 (g ; (m x)))) (f v1 v1)) and there is only one let-binding, which we ; prefer. So we wish to find a maximal multiple subterm. We will ; eliminate them one at a time. That way we will find smaller ; terms that still appear more than once. For example: ; The term (f (g (m x)) (h (m x)) (g (m x))) may give rise first ; to (let* ((v1 (g (m x)))) (f v1 (h (m x)) v1)), but upon abstracting ; that we get (let* ((v2 (m x)) (v1 (g v2))) (f v1 v2 v1)). ; We are only interested in "non-atomic" multiple subterms, i.e., ; function applications. Our interest in non-atomic subterms is ; because otherwise we will infinitely recur ``eliminating'' multiple ; occurrences of variable symbols by introducing new variable symbols ; that occur multiple times. ; So to do lambda abstraction on term we will find a non-atomic ; maximal multiple subterm, e1, in term. If successful, we will ; replace all occurrences of e1 in term by some new variable, say v1, ; producing, say, term1. Now consider (f e1 term1), where f is some ; irrelevant made-up symbol. This term has one less non-atomic ; multiple subterm, since e1 occurs only once in it and v1 is atomic. ; Repeat the process on this term until no multiple subterms are ; found. The result is (f ek ... (f e1 termk)), which we can abstract ; to (let ((vk ek) ... (v1 e1)) termk). ; We would like to carry out this process without manufacturing the ; irrelevant function symbol f. So we are really interested in ; multiple occurrences of a term in a list of terms. (mutual-recursion (defun maximal-multiple (x term-lst winner) ; In this definition, x is a term, but I am using it as though it were ; just the set of all of its subterms. I wish to find a non-atomic ; subterm, e, of x that is a maximal multiple subterm in the list of ; terms term-lst. Winner is either nil or the maximal multiple found ; so far. (cond ((or (variablep x) (fquotep x)) winner) ((eq (foccurrences-lst x term-lst nil) '>) (cond ((equal winner nil) x) ((eq (foccurrences x winner t) '>) winner) ((eq (foccurrences winner x t) '>) x) (t winner))) (t (maximal-multiple-lst (fargs x) term-lst winner)))) (defun maximal-multiple-lst (x-lst term-lst winner) (cond ((endp x-lst) winner) (t (maximal-multiple-lst (cdr x-lst) term-lst (maximal-multiple (car x-lst) term-lst winner)))))) ; So, to find a non-atomic maximal multiple subterm of a single term, ; term, do (maximal-multiple term (list term) nil). More generally, ; to find a non-atomic maximal multiple in a list of terms, lst, do ; (maximal-multiple lst lst nil). If the result is nil, there is no ; such subterm. Otherwise, the result is one. ; To carry out the algorithm sketched above, we must iteratively ; find and replace the maximal multiples by new variable symbols. (defun maximal-multiples1 (term-lst new-vars avoid-vars pkg-witness) (let ((e (maximal-multiple-lst term-lst term-lst nil))) (cond ((equal e nil) (mv new-vars term-lst)) (t (let ((var (genvar pkg-witness "V" (+ 1 (len new-vars)) avoid-vars))) (maximal-multiples1 (cons e (subst-expr1-lst var e term-lst)) (cons var new-vars) (cons var avoid-vars) pkg-witness)))))) (defun maximal-multiples (term pkg-witness) ; This function returns (mv vars terms), where terms is one longer ; than vars. Suppose vars is (v3 v2 v1) and terms is (e3 e2 e1 ; term3). Then term is equivalent to ; (let* ((v3 e3) (v2 e2) (v1 e1)) term3). ; Observe that if vars is nil there are no multiple subterms and terms ; is the singleton containing term. (maximal-multiples1 (list term) nil (all-vars term) pkg-witness)) (defun lambda-abstract1 (vars terms) (cond ((endp vars) (car terms)) (t (let* ((body (lambda-abstract1 (cdr vars) (cdr terms))) (new-vars (remove1-eq (car vars) (all-vars body)))) (cons-term (make-lambda (cons (car vars) new-vars) body) (cons (car terms) new-vars)))))) (defun lambda-abstract (term pkg-witness) ; Rockwell Addition: Non-equivalent read conditionals! #-acl2-loop-only ; Rockwell Addition (cond (*lambda-abstractp* (mv-let (vars terms) (maximal-multiples term pkg-witness) (lambda-abstract1 vars terms))) (t term)) #+acl2-loop-only ; Rockwell Addition (mv-let (vars terms) (maximal-multiples term pkg-witness) (lambda-abstract1 vars terms))) ; We also will clean up certain IF-expressions. (defun mutually-exclusive-tests (a b) ; We return t if terms (and a b) cannot be true. We just recognize ; the case where each is (EQUAL x 'constant) for different constants. (and (not (variablep a)) (not (fquotep a)) (eq (ffn-symb a) 'equal) (not (variablep b)) (not (fquotep b)) (eq (ffn-symb b) 'equal) (or (and (quotep (fargn a 1)) (quotep (fargn b 1)) (not (equal (cadr (fargn a 1)) (cadr (fargn b 1)))) (equal (fargn a 2) (fargn b 2))) (and (quotep (fargn a 2)) (quotep (fargn b 2)) (not (equal (cadr (fargn a 2)) (cadr (fargn b 2)))) (equal (fargn a 1) (fargn b 1))) (and (quotep (fargn a 1)) (quotep (fargn b 2)) (not (equal (cadr (fargn a 1)) (cadr (fargn b 2)))) (equal (fargn a 2) (fargn b 1))) (and (quotep (fargn a 2)) (quotep (fargn b 1)) (not (equal (cadr (fargn a 2)) (cadr (fargn b 1)))) (equal (fargn a 1) (fargn b 2)))))) (defun mutually-exclusive-subsumptionp (a b c) ; This is a generalized version of (if x y y). Suppose we wish to ; form (if a b c) but that b is c. Then clearly, the result is equal ; to c. Now imagine that c is (if c1 c2 c3) and that a and c1 are ; mutually exclusive. Then we could form (if c1 c2 (if a b c3)) ; instead. This would be a win if it turns out that after rippling ; down we find that b is equal to ck: (if a b c) is just c. (cond ((equal b c) t) ((and (nvariablep c) (not (fquotep c)) (eq (ffn-symb c) 'IF) (mutually-exclusive-tests a (fargn c 1))) (mutually-exclusive-subsumptionp a b (fargn c 3))) (t nil))) (mutual-recursion (defun cleanup-if-expr (x trues falses) (cond ((variablep x) x) ((fquotep x) x) ((eq (ffn-symb x) 'IF) (let ((a (cleanup-if-expr (fargn x 1) trues falses))) (cond ((quotep a) (if (cadr a) (cleanup-if-expr (fargn x 2) trues falses) (cleanup-if-expr (fargn x 3) trues falses))) ((member-equal a trues) (cleanup-if-expr (fargn x 2) trues falses)) ((member-equal a falses) (cleanup-if-expr (fargn x 3) trues falses)) (t (let ((b (cleanup-if-expr (fargn x 2) (cons a trues) falses)) (c (cleanup-if-expr (fargn x 3) trues (cons a falses)))) (cond ((equal b c) b) ((mutually-exclusive-subsumptionp a b c) c) (t (mcons-term* 'if a b c)))))))) (t (mcons-term (ffn-symb x) (cleanup-if-expr-lst (fargs x) trues falses))))) (defun cleanup-if-expr-lst (x trues falses) (cond ((endp x) nil) (t (cons (cleanup-if-expr (car x) trues falses) (cleanup-if-expr-lst (cdr x) trues falses))))) ) (defun all-type-reasoning-tags-p1 (lemmas) (cond ((endp lemmas) t) ((or (eq (car (car lemmas)) :FAKE-RUNE-FOR-TYPE-SET) (eq (car (car lemmas)) :TYPE-PRESCRIPTION)) (all-type-reasoning-tags-p1 (cdr lemmas))) (t nil))) (defun all-type-reasoning-tags-p (ttree) (all-type-reasoning-tags-p1 (tagged-objects 'lemma ttree))) (defun try-clause (atm clause wrld) ; We assume that atm rewrites to t or nil. We return t if we are to keep that ; rewrite, else nil. (cond ((endp clause) nil) ((and (eq (fn-symb (car clause)) 'not) (equal-mod-commuting atm (fargn (car clause) 1) wrld)) t) ((equal-mod-commuting atm (car clause) wrld) t) (t (try-clause atm (cdr clause) wrld)))) (defconst *trivial-non-nil-ttree* (puffert nil)) (defun make-non-nil-ttree (ttree) (or ttree *trivial-non-nil-ttree*)) (defun try-type-set-and-clause (atm ans ttree ttree0 current-clause wrld ens knownp) ; We are finishing off a call to rewrite-atm on atm that is about to return ans ; with associated ttree, which is assumed to extend ttree0. Ans is *t* or ; *nil*, but in a context in which this would produce a removal of ans rather ; than a win. We have found it heuristically useful to disallow such removals ; except when atm is trivially known to be true or false. We return the ; desired rewritten value of atm and associated justifying ttree that extends ; ttree0. (mv-let (ts ttree1) (type-set atm nil nil nil ens wrld nil nil nil) (cond ((ts= ts *ts-nil*) ; Type-set was able to reduce atm to nil, by examining atm in isolation. This ; would happen, for instance to an atm such as (not (acl2-numberp (+ x y))) or ; (not (consp (cons x y))). We want to allow such trivial facts to be removed ; from the clause to reduce clutter. We certainly do not lose anything by ; allowing such removals. (mv *nil* (cons-tag-trees ttree1 ttree0) nil)) ((ts-subsetp ts *ts-non-nil*) (mv *t* (cons-tag-trees ttree1 ttree0) nil)) ((try-clause atm current-clause wrld) (mv ans ttree nil)) (t (mv atm ttree0 (and knownp (make-non-nil-ttree ttree))))))) (mutual-recursion (defun lambda-subtermp (term) ; We determine whether some lambda-expression is used as a function in term. (if (or (variablep term) (fquotep term)) nil (or (flambdap (ffn-symb term)) (lambda-subtermp-lst (fargs term))))) (defun lambda-subtermp-lst (termlist) (if termlist (or (lambda-subtermp (car termlist)) (lambda-subtermp-lst (cdr termlist))) nil)) ) (defun rewrite-atm (atm not-flg bkptr gstack type-alist wrld simplify-clause-pot-lst rcnst current-clause state step-limit ttree0) ; This function rewrites atm with nth-update-rewriter, recursively. Then it ; rewrites the result with rewrite, in the given context, maintaining iff. ; Note that not-flg is only used heuristically, as it is the responsibility of ; the caller to account properly for it. Current-clause is also used only ; heuristically. ; It is used to rewrite the atoms of a clause as we sweep across. It is ; essentially a call of rewrite -- indeed, it didn't exist in Nqthm and rewrite ; was called in its place -- but with a couple of exceptions. For one thing, ; it first gives type-set a chance to decide things. ; But a more complex exception is that instead of the usual result from ; rewrite, (mv step-limit rewritten-atm ttree), we return a fourth value as ; well: when non-nil, it is a ttree justifying the rewriting of atm to *t* or ; *nil* according to not-flg having value t or nil, respectively. We use this ; additional information to rewrite a clause to *false-clause* when every ; literal simplifies to nil even when our heuristics (documented rather ; extensively below) would normally refuse to simplify at least one of those ; literals; see parameter fttree in rewrite-clause. The following example from ; Pete Manolios illustrates this situation: (thm (<= (+ 1 (acl2-count x)) 0)). ; In this case, there is only one literal, which simplifies to nil; and our ; heuristics would normally refuse to take advantage of that simplification. ; But since every literal (i.e., this one) simplifies to nil, then even if we ; wouldn't normally take advantage of that information, we nevertheless rewrite ; the clause to false. As Pete points out, this helps the user to see the ; likely falsehood of the conjecture, which otherwise can trigger a useless but ; distracting proof by induction. ; Another example like the one above, but involving two literals, is: ; (thm (or (<= (+ 1 (acl2-count x)) -1) (< (acl2-count x) 0))). It seems not ; quite trivial to come up with such two-literal examples that generate ; inductions in Version_3.6.1, before this improvement; for example, the thm ; just above fails to be such an example if we switch the order of arguments to ; OR. (mv-let (knownp nilp ttree) (known-whether-nil atm type-alist (access rewrite-constant rcnst :current-enabled-structure) (ok-to-force rcnst) ; The use of dwp = t here, together with the passing of dwp down to ; the calls of type-set-with-rules in type-set-rec, enables the proof of the ; thm below to go through. This example is a distillation of an example that ; arose during a proof attempt by Matt Kaufmann. ; (defstub f1 (x) t) ; (defstub f2 (x) t) ; (defstub f3 (x) t) ; (defaxiom ax1 ; (implies (f1 x) ; (f2 x))) ; (defaxiom ax2 ; (implies (force (f2 x)) ; (natp (f3 x))) ; :rule-classes :type-prescription) ; (thm (implies (and (f1 x) ; (f3 x)) ; (<= 0 (f3 x)))) t ; dwp wrld ttree0) (cond ; Before Version 2.6 we had ; (knownp ; (cond (nilp (mv *nil* ttree)) ; (t (mv *t* ttree)))) ; but this allowed type-set to remove ``facts'' from a theorem which ; may be needed later. The following transcript illustrates the previous ; behavior: ; ACL2 !>(defthm fold-consts-in-+ ; (implies (and (syntaxp (consp c)) ; (syntaxp (eq (car c) 'QUOTE)) ; (syntaxp (consp d)) ; (syntaxp (eq (car d) 'QUOTE))) ; (equal (+ c d x) ; (+ (+ c d) x)))) ; ACL2 !>(defthm helper ; (implies (integerp x) ; (integerp (+ 1 x)))) ; ACL2 !>(thm ; (implies (integerp (+ -1/2 x)) ; (integerp (+ 1/2 x))) ; :hints (("Goal" :use ((:instance helper ; (x (+ -1/2 x))))))) ; ; [Note: A hint was supplied for our processing of the goal above. ; Thanks!] ; ; ACL2 Warning [Use] in ( THM ...): It is unusual to :USE an enabled ; :REWRITE or :DEFINITION rule, so you may want to consider disabling ; (:REWRITE HELPER). ; ; ; We now augment the goal above by adding the hypothesis indicated by ; the :USE hint. The hypothesis can be derived from HELPER via instantiation. ; The augmented goal is shown below. ; ; Goal' ; (IMPLIES (IMPLIES (INTEGERP (+ -1/2 X)) ; (INTEGERP (+ 1 -1/2 X))) ; (IMPLIES (INTEGERP (+ -1/2 X)) ; (INTEGERP (+ 1/2 X)))). ; ; By case analysis we reduce the conjecture to ; ; Goal'' ; (IMPLIES (AND (OR (NOT (INTEGERP (+ -1/2 X))) ; (INTEGERP (+ 1 -1/2 X))) ; (INTEGERP (+ -1/2 X))) ; (INTEGERP (+ 1/2 X))). ; ; This simplifies, using primitive type reasoning, to ; ; Goal''' ; (IMPLIES (INTEGERP (+ -1/2 X)) ; (INTEGERP (+ 1/2 X))). ; ; Normally we would attempt to prove this formula by induction. However, ; we prefer in this instance to focus on the original input conjecture ; rather than this simplified special case. We therefore abandon our ; previous work on this conjecture and reassign the name *1 to the original ; conjecture. (See :DOC otf-flg.) ; ; No induction schemes are suggested by *1. Consequently, the proof ; attempt has failed. ; ; Summary ; Form: ( THM ...) ; Rules: ((:DEFINITION IMPLIES) ; (:DEFINITION NOT) ; (:FAKE-RUNE-FOR-TYPE-SET NIL)) ; Warnings: Use ; Time: 0.03 seconds (prove: 0.02, print: 0.01, other: 0.00) ; ; ******** FAILED ******** See :DOC failure ******** FAILED ******** ; ACL2 !> ; Note that in the transition from Goal'' to Goal''', the needed ; fact --- (INTEGERP (+ 1 -1/2 X)) --- was removed by type reasoning. ; This is not good. We now only use type reasoning at this point if ; it will give us a win. ; One might ask why we only disallow type-set from removing facts here. ; Why not elswhere, and what about rewrite? We do it this way because ; it is only here that the user cannot prevent this removal from ; happening by manipulating the enabled structure. ((and knownp not-flg nilp) ; We have reduced the atm to nil but it occurs negated in the ; clause and so we have reduced the literal to t, proving the clause. ; So we report this reduction. (mv step-limit *nil* ttree nil)) ((and knownp (not not-flg) (not nilp)) (mv step-limit *t* ttree nil)) (t (mv-let (hitp atm1 ttree1) ; Rockwell Addition (cond ((eq (nu-rewriter-mode wrld) :literals) (nth-update-rewriter t atm nil (access rewrite-constant rcnst :current-enabled-structure) wrld state)) (t (mv nil nil nil))) (let ((atm2 (if hitp (lambda-abstract (cleanup-if-expr atm1 nil nil) (pkg-witness (current-package state))) atm)) (lemmas0 (tagged-objects 'lemma ttree0)) (lemmas1 (and hitp (tagged-objects 'lemma ttree1))) (ttree00 (remove-tag-from-tag-tree 'lemma ttree0))) (sl-let (ans1 ans2) (rewrite-entry (rewrite atm2 nil bkptr) :rdepth (rewrite-stack-limit wrld) :step-limit step-limit :type-alist type-alist :obj '? :geneqv *geneqv-iff* :wrld wrld :fnstack nil :ancestors nil :backchain-limit (access rewrite-constant rcnst :backchain-limit-rw) :simplify-clause-pot-lst simplify-clause-pot-lst :rcnst rcnst :gstack gstack :ttree (if hitp (cons-tag-trees (remove-tag-from-tag-tree 'lemma ttree1) ttree00) ttree00)) (let* ((old-lemmas (if lemmas0 (union-equal lemmas1 lemmas0) lemmas1)) (new-lemmas (tagged-objects 'lemma ans2)) (final-lemmas (if old-lemmas (union-equal new-lemmas old-lemmas) new-lemmas)) (ttree (maybe-extend-tag-tree 'lemma final-lemmas (remove-tag-from-tag-tree 'lemma ans2)))) ; But we need to do even more work to prevent type-set from removing ; ``facts'' from the goal. Here is another (edited) transcript: ; ACL2 !>(defun foo (x y) ; (if (acl2-numberp x) ; (+ x y) ; 0)) ; ; ACL2 !>(defthm foo-thm ; (implies (acl2-numberp x) ; (equal (foo x y) ; (+ x y)))) ; ACL2 !>(in-theory (disable foo)) ; ACL2 !>(thm ; (implies (and (acl2-numberp x) ; (acl2-numberp y) ; (equal (foo x y) x)) ; (equal y 0))) ; ; This simplifies, using the :type-prescription rule FOO, to ; ; Goal' ; (IMPLIES (AND (ACL2-NUMBERP Y) ; (EQUAL (FOO X Y) X)) ; (EQUAL Y 0)). ; ; Name the formula above *1. ; ; No induction schemes are suggested by *1. Consequently, the proof ; attempt has failed. ; ; Summary ; Form: ( THM ...) ; Rules: ((:TYPE-PRESCRIPTION FOO)) ; Warnings: None ; Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ; ; ******** FAILED ******** See :DOC failure ******** FAILED ******** ; | ; Note that in the transition from Goal to Goal' we removed the critical fact ; that x was an acl2-numberp. This fact can be derived from the third ; hypothesis --- (equal (foo x y) x) --- via :type-prescription rule FOO as ; indicated. However, when we then go on to rewrite the third hypothesis, we ; are not able to rederive this fact, since the type-alist used at that point ; does not use use the third hypothesis so as to prevent tail biting. ; Robert Krug has seen this sort of behavior in reasoning about floor and mod. ; In fact, that experience motivated him to provide the original version of the ; code below not to remove certain additional facts. ; Finally, note that even before this additional care, the lemma ; (thm ; (implies (and (acl2-numberp y) ; (equal (foo x y) x) ; (acl2-numberp x)) ; (equal y 0))) ; does succeed, since the (acl2-numberp x) hypothesis now appears after the ; (equal (foo x y) x) hypothesis, hence does not get removed until after it has ; been used to relieve the hypothesis of foo-thm. This kind of situation in ; which a proof succeeds or fails depending on the order of hypotheses really ; gets Robert's goat. (cond ((not (or (equal ans1 *nil*) (equal ans1 *t*))) ; We have, presumably, not removed any facts, so we allow this rewrite. (mv step-limit ans1 ttree (and knownp *trivial-non-nil-ttree*))) ((and (nvariablep atm2) (not (fquotep atm2)) (equivalence-relationp (ffn-symb atm2) wrld)) ; We want to blow away equality (and equivalence) hypotheses, because for ; example there may be a :use or :cases hint that is intended to blow away (by ; implication) such hypotheses. (mv step-limit ans1 ttree nil)) ((equal ans1 (if not-flg *nil* *t*)) ; We have proved the original literal from which atm is derived; hence we have ; proved the clause. So we report this reduction. (mv step-limit ans1 ttree nil)) ((all-type-reasoning-tags-p ans2) ; Type-reasoning alone has been used, so we are careful in what we allow. (cond ((lambda-subtermp atm2) ; We received an example from Jared Davis in which a hypothesis of the form ; (not (let ...)) rewrites to true with a tag-tree of nil, and hence was kept ; without this lambda-subtermp case. The problem with keeping that hypothesis ; is that it has calls of IF in a lambda body, which do not get eliminated by ; clausification -- and this presence of IF terms causes the :force-info field ; to be set to 'weak in the rewrite constant generated under simplify-clause. ; That 'weak setting prevented forced simplification from occurring that was ; necessary in order to make progress in Jared's proof! ; A different solution would be to ignore IF calls in lambda bodies when ; determining whether to set :force-info to 'weak. However, that change caused ; a regression suite failure: in community book ; books/symbolic/tiny-fib/tiny-rewrites.lisp, theorem next-instr-pop. The ; problem seemed to be premature forcing, of just the sort we are trying to ; prevent with the above-mentioned check for IF terms. ; Robert Krug points out to us, regarding the efforts here to keep hypotheses ; that rewrote to true, that for him the point is simply not to lose Boolean ; hypotheses like (acl2-numberp x) in the example above. Certainly we do not ; expect terms with lambda calls to be of that sort, or even to make any sorts ; of useful entries in type-alists. If later we find other reasons to keep *t* ; or *nil*, we can probably feel comfortable in adding conditions as we have ; done with the lambda-subtermp test above. (mv step-limit ans1 ttree nil)) ((eq (fn-symb atm2) 'implies) ; We are contemplating throwing away the progress made by the above call of ; rewrite. However, we want to keep progress made by expanding terms of the ; form (IMPLIES x y), so we do that expansion (again) here. It seems ; reasonable to keep this in sync with the corresponding use of subcor-var in ; rewrite. (prepend-step-limit 3 (try-type-set-and-clause (subcor-var (formals 'implies wrld) (list (fargn atm2 1) (fargn atm2 2)) (body 'implies t wrld)) ans1 ttree ttree0 current-clause wrld (access rewrite-constant rcnst :current-enabled-structure) knownp))) (t ; We make one last effort to allow removal of certain ``trivial'' facts from ; the goal. (prepend-step-limit 3 (try-type-set-and-clause atm2 ans1 ttree ttree0 current-clause wrld (access rewrite-constant rcnst :current-enabled-structure) knownp))))) (t (mv step-limit ans1 ttree nil))))))))))) ; Now we develop the functions for finding trivial equivalence hypotheses and ; stuffing them into the clause, transforming {(not (equal n '27)) (p n x)}, ; for example, into {(p '27 x)} and running p if x is constant too. (mutual-recursion (defun every-occurrence-equiv-hittablep1 (equiv old geneqv term in-hide-flg ens wrld) ; This function determines whether every occurrence of old in term is ``equiv ; hittable'' while maintaining geneqv. This is just an optimization of a call ; to subst-equiv-expr followed by an occur check. ; NOTE: We ignore occurrences of old inside arguments to HIDE. (cond ((equal term old) ; If term is old, then we return non-nil or nil according to whether ; equiv refines geneqv. If it does refine geneqv, this occurrence ; will be hit; if not, this occurrence won't be hit. Actually, if ; we are inside a call of hide then this occurrence won't be hit ; either way. (and (not in-hide-flg) (geneqv-refinementp equiv geneqv wrld))) ((or (variablep term) (fquotep term)) ; If term is different from old and doesn't contain old, e.g., term is a ; variable or a quote, then all occurrences of old in term are equiv ; hittable. Hide is handled below. t) (t (every-occurrence-equiv-hittablep1-listp equiv old (geneqv-lst (ffn-symb term) geneqv ens wrld) (fargs term) (or in-hide-flg (eq (ffn-symb term) 'hide)) ens wrld)))) (defun every-occurrence-equiv-hittablep1-listp (equiv old geneqv-lst args in-hide-flg ens wrld) (cond ((null args) t) (t (and (every-occurrence-equiv-hittablep1 equiv old (car geneqv-lst) (car args) in-hide-flg ens wrld) (every-occurrence-equiv-hittablep1-listp equiv old (cdr geneqv-lst) (cdr args) in-hide-flg ens wrld))))) ) (defun every-occurrence-equiv-hittablep (equiv old geneqv term ens wrld) ; This function determines whether every occurrence of old in term is ``equiv ; hittable'' while maintaining geneqv. This means that (subst-equiv-expr equiv ; new old genequv term ens wrld state ttree) will remove all occurrences of old ; from term (assuming there are no occurrences of old in new and old is a ; variable). ; We here enforce the rule that we don't know how to substitute for explicit ; constants. We also build in the fact that everything is equal-hittable ; (i.e., equal refines all equivalence relations). ; NOTE: We ignore occurrences of old inside arguments to HIDE. (cond ((and (nvariablep old) (fquotep old)) (subst-expr-error old)) ((eq equiv 'equal) t) (t (every-occurrence-equiv-hittablep1 equiv old geneqv term nil ens wrld)))) (defun every-occurrence-equiv-hittablep-in-clausep (equiv old cl ens wrld) ; This checks that every occurrence of old in cl is equiv hittable ; while maintaining 'iff on each literal. This is just a special case ; in which we are checking every-occurrence-equiv-hittablep1-listp where ; geneqv-lst is a list, as long as cl, of *geneqv-iff*s. Rather than ; manufacture the suitable geneqv-lst we just supply *geneqv-iff* as ; needed. (cond ((null cl) t) (t (and (every-occurrence-equiv-hittablep1 equiv old *geneqv-iff* (car cl) nil ens wrld) (every-occurrence-equiv-hittablep-in-clausep equiv old (cdr cl) ens wrld))))) (mutual-recursion (defun some-occurrence-equiv-hittablep1 (equiv old geneqv term ens wrld) ; This function determines whether there exists an equiv-hittable occurrence of ; old in term maintaining geneqv. (cond ((equal term old) ; If term is old, then we return non-nil or nil according to whether ; equiv refines geneqv. If it does refine geneqv, this occurrence ; will be hit; if not, this occurrence won't be hit. (geneqv-refinementp equiv geneqv wrld)) ((or (variablep term) (fquotep term) (eq (ffn-symb term) 'hide)) ; If term is different from old and doesn't contain old, e.g., term is ; a variable or a quote, then there is no occurrence of old in term. ; Calls of hide are included, since substitution (subst-equiv-expr) ; does not go inside calls of hide. nil) (t (some-occurrence-equiv-hittablep1-listp equiv old (geneqv-lst (ffn-symb term) geneqv ens wrld) (fargs term) ens wrld)))) (defun some-occurrence-equiv-hittablep1-listp (equiv old geneqv-lst args ens wrld) (cond ((null args) nil) (t (or (some-occurrence-equiv-hittablep1 equiv old (car geneqv-lst) (car args) ens wrld) (some-occurrence-equiv-hittablep1-listp equiv old (cdr geneqv-lst) (cdr args) ens wrld))))) ) (defun some-occurrence-equiv-hittablep (equiv old geneqv term ens wrld) ; This function determines whether some occurrence of old in term is ``equiv ; hittable'' while maintaining geneqv. This means that (subst-equiv-expr equiv ; new old geneqv term ens wrld state ttree) changes term. ; We here enforce the rule that we don't know how to substitute for explicit ; constants. ; NOTE: We ignore occurrences of old inside arguments to HIDE. (cond ((and (nvariablep old) (fquotep old)) (subst-expr-error old)) (t (some-occurrence-equiv-hittablep1 equiv old geneqv term ens wrld)))) (defun equiv-hittable-in-some-other-lit (equiv term n cl i ens wrld) ; We determine whether term occurs in an equiv-hittable slot (maintaining iff) ; in some lit of clause cl other than the nth. The number of the first literal ; of cl is i. (cond ((null cl) nil) ((int= n i) (equiv-hittable-in-some-other-lit equiv term n (cdr cl) (1+ i) ens wrld)) ((some-occurrence-equiv-hittablep equiv term *geneqv-iff* (car cl) ens wrld) t) (t (equiv-hittable-in-some-other-lit equiv term n (cdr cl) (1+ i) ens wrld)))) (defun find-trivial-equivalence1 (not-just-quotep-flg tail i cl ens wrld avoid-lst) ; Cl is a clause. Tail is a tail of cl and i is the position number ; of its first literal, starting from 0 for the first lit in cl. See ; find-trivial-equivalence for the rest of the spec. ; It is important to keep in mind that the clause upon which we are working has ; not necessarily been rewritten. Indeed, it is often the product of previous ; substitutions by the driver of this very function. (Aside: once upon a time, ; the driver did not evaluate literals as they got stuffed with constants. At ; the moment it does evaluate enabled fns on constant args. But that may ; change and so this function is written in a way that assumes the worst: there ; may be reducible terms in the clause.) Thus, for example, a clause like ; {(not (equal x 'a)) (not (equal y 'b)) (not (equal x y)) y ...} ; may first produce ; {(not (equal y 'b)) (not (equal 'a y)) y ...} ; and then ; {(not (equal 'a 'b)) 'b ...} ; which contains two unexpected sorts of literals: an equivalence with constant ; args and a constant literal. We must therefore not be surprised by such ; literals. However, we do not expect them to arise often enough to justify ; making our caller cope with the possibility that we've proved the clause. So ; if we find such a literal and can decide the clause, we will just immediately ; report that there are no more usable equivalences and let the simplifier ; rediscover the literal. If we find such a literal and can't decide the ; clause quickly based on equal and iff facts (we are not going to eval ; user-defined equivs) then we will just continue looking for usable ; equivalences. The idea is that if the discovered lit makes the clause true, ; we don't expect to have screwed it up by continuing to substitute; and if the ; discovered lit just drops out, then our continued substitution is what we ; should have done. (Aside: If we persist in our decision to reduce literals ; when they are suffed with constants, then these cases will not arise and all ; of the above is irrelevant.) ; Recall our output spec from find-trivial-equivalence. The six results we ; return are the name of the condition detected (disposable or keeper), the ; location i of the literal, equiv, lhs, rhs and the literal itself. Otherwise ; we return 6 nils. (When we succeed, the "lhs" of our result is the term for ; which we are to substitute and "rhs" is the term by which we replace lhs. ; They may in fact come from the opposite sides of the equivalence term.) (cond ((null tail) (mv nil nil nil nil nil nil)) ((member-equal (car tail) avoid-lst) (find-trivial-equivalence1 not-just-quotep-flg (cdr tail) (1+ i) cl ens wrld avoid-lst)) ; Handle variable V as though it is the literal (not (equal V nil)). ((quotep (car tail)) ; If the discovered lit is nil, then we just ignore it because it will drop ; out. If the discovered lit is non-nil, this clause is true. So we signal ; that there are no more usable equivs and let the simplifier get its hands ; on the clause to rediscover that it is true. (if (equal (car tail) *nil*) (find-trivial-equivalence1 not-just-quotep-flg (cdr tail) (1+ i) cl ens wrld avoid-lst) (mv nil nil nil nil nil nil))) ((or (variablep (car tail)) (and (eq (ffn-symb (car tail)) 'not) (or (variablep (fargn (car tail) 1)) (and (not (fquotep (fargn (car tail) 1))) (equivalence-relationp (ffn-symb (fargn (car tail) 1)) wrld))))) (let* ((atm (if (variablep (car tail)) (fcons-term* 'equal (car tail) *nil*) (fargn (car tail) 1))) (equiv (if (variablep atm) 'iff (ffn-symb atm))) (lhs (if (variablep atm) atm (fargn atm 1))) (rhs (if (variablep atm) *t* (fargn atm 2)))) ; We have discovered an equiv hyp (not (equiv lhs rhs)) that is not on avoid-lst. (cond ((and (quotep lhs) (quotep rhs)) ; Oh! It has constant args. If equiv is equal we decide which way this lit ; goes and act accordingly, as we did for a quotep lit above. If the equiv is ; not equal then we just assume this lit will eventually drop out (we bet it is ; nil) and go on looking for other usable equivs before giving the result to ; the simplifier to decide. (cond ((eq equiv 'equal) (if (equal lhs rhs) (find-trivial-equivalence1 not-just-quotep-flg (cdr tail) (1+ i) cl ens wrld avoid-lst) (mv nil nil nil nil nil nil))) (t (find-trivial-equivalence1 not-just-quotep-flg (cdr tail) (1+ i) cl ens wrld avoid-lst)))) ; So below we know that if one side is a quotep then the other side is not (but ; we don't yet know that either side is a quotep). Observe that if one side is ; a quotep we are always safe in answering that we can equiv substitute for the ; other side and it is only a question of whether we have a disposable lit or a ; keeper. ((and not-just-quotep-flg (variablep lhs) (every-occurrence-equiv-hittablep-in-clausep equiv lhs cl ens wrld) (not (dumb-occur lhs rhs))) ; The 'disposable condition is met: lhs is an everywhere hittable variable not in rhs. ; But it could be that rhs is also an everywhere hittable variable not in lhs. ; If so, we'll substitute the term-order smaller for the bigger just so the ; user knows which way the substitutions will go. (cond ((and (variablep rhs) (every-occurrence-equiv-hittablep-in-clausep equiv rhs cl ens wrld)) (cond ((term-order lhs rhs) (mv 'disposable i equiv rhs lhs (car tail))) (t (mv 'disposable i equiv lhs rhs (car tail))))) (t (mv 'disposable i equiv lhs rhs (car tail))))) ((and not-just-quotep-flg (variablep rhs) (every-occurrence-equiv-hittablep-in-clausep equiv rhs cl ens wrld) (not (dumb-occur rhs lhs))) ; This is the case symmetric to that above. (mv 'disposable i equiv rhs lhs (car tail))) ((and (quotep rhs) ; thus lhs is a non-quotep (equiv-hittable-in-some-other-lit equiv lhs i cl 0 ens wrld)) ; The 'keeper conditions are met: lhs is a non-quote with at least one ; equiv-hittable occurrence in another lit and rhs is a quote. Note that in ; the case that not-just-quotep-flg is nil, we might be giving the ``wrong'' ; first answer, since if lhs is a variable then 'keeper should be 'disposable. ; However, if not-just-quotep-flg is nil, then we will be ignoring that answer ; anyhow; see the call of subst-equiv-and-maybe-delete-lit in ; remove-trivial-equivalences. (mv 'keeper i equiv lhs rhs (car tail))) ((and (quotep lhs) ; thus rhs is a non-quotep (equiv-hittable-in-some-other-lit equiv rhs i cl 0 ens wrld)) (mv 'keeper i equiv rhs lhs (car tail))) (t (find-trivial-equivalence1 not-just-quotep-flg (cdr tail) (1+ i) cl ens wrld avoid-lst))))) (t (find-trivial-equivalence1 not-just-quotep-flg (cdr tail) (1+ i) cl ens wrld avoid-lst)))) (defun find-trivial-equivalence (not-just-quotep-flg cl ens wrld avoid-lst) ; We look for a literal of cl of the form (not (equiv lhs rhs)) where ; either of two conditions apply. ; name condition ; disposable: lhs is a variable, all occurrences of lhs in cl ; are equiv-hittable, and rhs does not contain lhs. ; keeper: lhs is any non-quotep and rhs is a quotep and lhs has ; an equiv-hittable occurrence in some other literal ; of the clause ; Note that in the keeper case, there may be some non-hittable occurrences of ; lhs in the clause. In addition, we accept commuted version of the equivalence ; and we treat each variablep literal, var, as the trivial equivalence (not ; (equal var 'NIL)). ; If we find such a literal we return 6 values: the name of the condition ; detected, the location i of the literal, equiv, lhs, rhs and the literal ; itself. Otherwise we return 6 nils. ; The driver of this function, remove-trivial-equivalences, will substitute rhs ; for lhs throughout cl, possibly delete the literal, and then call us again to ; look for the next trivial equivalence. This raises a problem. If the driver ; doesn't delete the literal, then we will return the same one again and loop. ; So the driver supplies us with a list of literals to avoid, avoid-lst, and ; will put onto it each of the literals that has been used but not deleted. ; So consider a clause like ; (implies (and (equal (foo b) 'evg) [1] ; (equal a b)) [2] ; (p (foo a) (foo b))) ; The first trivial equivalence is [1]. The driver substitutes 'evg ; for (foo b) but doesn't delete the literal. So we get: ; (implies (and (equal (foo b) 'evg) [1] ; (equal a b)) [2] ; (p (foo a) 'evg)) ; and the admonition against using (equal (foo b) 'evg). But now we see ; [2] and the driver substitutes a for b (because a is smaller) and deletes ; [2]. So we get: ; (implies (equal (foo a) 'evg) [1] ; (p (foo a) 'evg)) ; and the old admotion against using (equal (foo b) 'evg). Here we find [1] ; ``again'' because it is no longer on the list of things to avoid. Indeed, we ; can even use it to good effect. Of course, once it is used both it and the ; old avoided literal are to be avoided. ; So can this loop? No. Every substitution reduces term-order. (find-trivial-equivalence1 not-just-quotep-flg cl 0 cl ens wrld avoid-lst)) (defun add-literal-and-pt1 (cl-tail pt cl pt-lst) ; Imagine that lit is a literal with pt as its parent tree. Cl is a clause and ; the parent tree of each literal is given by the corresponding element of ; pt-lst. We were about to add lit to cl when we noticed that lit (possibly ; commuted) is already an element of cl, namely the one in the car of cl-tail, ; which is a tail of cl. Thus, we wish to update pt-lst so that the ; corresponding parent tree in the new pt-lst includes pt. (cond ((null cl) (er hard 'add-literal-and-pt1 "We failed to find the literal!")) ((equal cl-tail cl) (cond ((null (car pt-lst)) (cons pt (cdr pt-lst))) (t (cons (cons pt (car pt-lst)) (cdr pt-lst))))) (t (cons (car pt-lst) (add-literal-and-pt1 cl-tail pt (cdr cl) (cdr pt-lst)))))) (defun add-literal-and-pt (lit pt cl pt-lst ttree) ; Very roughly speaking, this is just: ; (mv (add-literal lit cl nil) ; add lit to clause cl ; (cons pt pt-lst) ; add lit's parent tnree to pt-lst ; ttree) ; and pass up the ttree ; But it is complicated by the fact that the add-literal might not actually ; cons lit onto cl but reduce the clause to {t} or merge the literal with ; another. If that happened and we had actually used the code above, then the ; pt-lst returned would no longer be in 1:1 correspondence with the new ; clause. (cond ((quotep lit) (cond ((equal lit *nil*) (mv cl pt-lst ttree)) (t (mv *true-clause* nil ttree)))) ((or (equal cl *true-clause*) (member-complement-term lit cl)) (mv *true-clause* nil ttree)) (t (let ((cl0 (member-term lit cl))) (cond ((null cl0) (mv (cons lit cl) (cons pt pt-lst) ttree)) ((null pt) (mv cl pt-lst ttree)) (t (mv cl (add-literal-and-pt1 cl0 pt cl pt-lst) ttree))))))) (defun add-binding-to-tag-tree (var term ttree) ; Note that var need not be a variable; see the call in fertilize-clause1. (let* ((tag 'binding-lst) (binding (cons var term)) (old (tagged-object tag ttree))) (cond (old (extend-tag-tree tag (list (cons binding old)) (remove-tag-from-tag-tree! tag ttree))) (t (extend-tag-tree tag (list (cons binding nil)) ttree))))) (defun subst-equiv-and-maybe-delete-lit (equiv new old n cl i pt-lst delete-flg ens wrld state ttree) ; Substitutes new for old (which are equiv) in every literal of cl (maintaining ; iff) except the nth one. The nth literal is deleted if delete-flg is t and ; is skipped but included in the if delete-flg is nil. Pt-lst is in 1:1 ; correspondence with cl. We return the new clause, a new pt-lst and a ttree ; recording the congruence and executable counterpart rules used. It is ; possible that this fn will return a clause dramatically shorter than cl, ; because lits may evaporate upon evaluation or merge with other literals. We ; may also prove the clause. (cond ((null cl) (mv nil nil ttree)) ((int= n i) (mv-let (cl1 pt-lst1 ttree) (subst-equiv-and-maybe-delete-lit equiv new old n (cdr cl) (1+ i) (cdr pt-lst) delete-flg ens wrld state ttree) (cond (delete-flg (mv cl1 pt-lst1 (add-binding-to-tag-tree old new ttree))) (t (add-literal-and-pt (car cl) (car pt-lst) cl1 pt-lst1 ttree))))) ((dumb-occur old (car cl)) (mv-let (hitp lit ttree) (subst-equiv-expr equiv new old *geneqv-iff* (car cl) ens wrld state ttree) (declare (ignore hitp)) ; Hitp may be nil even though old occurs in the lit, because it may not occur ; in an equiv-hittable place. But we don't really care whether it's t or nil. (mv-let (cl1 pt-lst1 ttree) (subst-equiv-and-maybe-delete-lit equiv new old n (cdr cl) (1+ i) (cdr pt-lst) delete-flg ens wrld state ttree) (add-literal-and-pt lit (car pt-lst) cl1 pt-lst1 ttree)))) (t (mv-let (cl1 pt-lst1 ttree) (subst-equiv-and-maybe-delete-lit equiv new old n (cdr cl) (1+ i) (cdr pt-lst) delete-flg ens wrld state ttree) (add-literal-and-pt (car cl) (car pt-lst) cl1 pt-lst1 ttree))))) (defun remove-trivial-equivalences (cl pt-lst remove-flg ens wrld state ttree hitp avoid-lst) ; This function looks for two kinds of equivalence hypotheses in cl and uses ; them to do substitutions. By "equivalence hypothesis" we mean a literal of ; the form (not (equiv lhs rhs)) that is not on avoid-lst. The two kinds are ; called "trivial var equivalences" and "trivial quote equivalences." If we ; find an equation of the first sort we substitute one side for the other and ; delete the equivalence (provided remove-flg is t). If we find an equation of ; the second sort, we substitute one side for the other but do not delete the ; equivalence. See find-trivial-equivalence for more details, especially ; concerning avoid-lst. Hitp is an accumulator that records whether we did ; anything. ; Pt-lst is a list of parent trees in 1:1 correspondence with cl. Since we ; return a modified version of cl in which some literals may have been deleted, ; we must also return a modified version of pt-lst giving the parent trees for ; the surviving literals. ; The justification for deleting (not (equiv var term)) when var occurs nowhere ; in the clause is (a) it is always sound to throw out a literal, and (b) it is ; heuristically safe here because var is isolated and equiv is reflexive: if ; (implies (equiv var term) p) is a theorem so is p because (equiv term term). ; We return four values: hitp, cl, pt-lst and ttree. ; No Change Loser. ; Note: We have briefly considered the question of whether we should do ; anything with hypotheses of the form (equiv var term), where var does not ; occur in term, and some (but not all) occurrences of var are equiv-hittable. ; Perhaps we should hit those occurrences but not delete the hypothesis? We ; think not. After all, if term is larger than var (as it generally is here), ; why should we replace some occurrences of the small term by the big one? ; They will just be zapped back by rewrite-solidify if the hyp is not deleted. ; However, an exception to this rule is if we see a hypothesis of the form ; (equal lhs 'const) where not every occurrence of lhs is equiv-hittable. ; Such a hyp is not a trivial var equivalence, even if lhs is a variable, ; because of the un-hittable occurrence of var. But we do count it as a ; trivial quote equivalence and hit var where we can (but don't delete the ; hypothesis). (mv-let (condition lit-position equiv lhs rhs lit) (find-trivial-equivalence remove-flg cl ens wrld avoid-lst) (cond (lit-position (mv-let (new-cl new-pt-lst ttree) (subst-equiv-and-maybe-delete-lit equiv rhs lhs lit-position cl 0 pt-lst (and remove-flg (eq condition 'disposable)) ens wrld state ttree) (remove-trivial-equivalences new-cl new-pt-lst remove-flg ens wrld state ttree t (cons lit avoid-lst)))) (t (mv hitp cl pt-lst ttree))))) ; In a break with nqthm, we implement a really trivial theorem prover which ; gets the first shot at any conjecture we have to prove. The idea is to build ; into this function whatever is necessary for boot-strap to work. It will ; also speed up the acceptance of commonly used recursive schemas. The idea is ; simply to recognize instances of a small number of known truths, stored in ; clausal form on the world global 'built-in-clauses, whose initial value is ; set up below. ; To be predictable, we have to include commutative variants of the ; recognized clauses. In addition, because subsumption works by first ; trying to find (an instance of) the first literal and then trying to ; find the rest, it is faster to put the most unusual literal first in ; each built-in clause. (defrec built-in-clause ((nume . all-fnnames) clause . rune) t) ; Note: The :all-fnnames field must be set as it would be by ; all-fnnames-subsumer. This setting cannot be done automatically because we ; do not know the initial world until we have set up the built-in-clauses. But ; we do check, with chk-initial-built-in-clauses which is called and reported ; in check-built-in-constants, that the setting below is correct for the actual ; initial world. When adding new records, it is best to use ; (all-fnnames-subsumer cl (w state)) to get the :all-fnnames field below. ;; RAG - I changed the clauses about e0-ord-< [v2-8 and beyond: o<] reducing on ;; complex-rationalps to reducing on any complexp. (defconst *initial-built-in-clauses* (list ; acl2-count is an ordinal. (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o-p (acl2-count x))) :all-fnnames '(o-p acl2-count)) ; Car and cdr decrease on consps. (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (car x)) (acl2-count x)) (not (consp x))) :all-fnnames '(acl2-count car o< consp not)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (not (consp x))) :all-fnnames '(acl2-count cdr o< consp not)) ; Car and cdr decrease on non-atoms. (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (car x)) (acl2-count x)) (atom x)) :all-fnnames '(acl2-count car o< atom)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (atom x)) :all-fnnames '(acl2-count cdr o< atom)) ; Car and cdr decrease on non-endps. (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (car x)) (acl2-count x)) (endp x)) :all-fnnames '(acl2-count car o< endp)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (endp x)) :all-fnnames '(acl2-count cdr o< endp)) ; 1- decreases on positives and on non-negatives other than 0. But we ; represent (1- x) three different ways: (1- x), (+ x -1) and (+ -1 x). And to ; say "other than 0" we can use (not (zp x)) or (integerp x) together ; with the negations of any one of (equal x 0), (= x 0) or (= 0 x). The ; symmetry of equal is built into unification, but not so =, so we have ; two versions for each =. ; However, in Version 1.8 we made 1- a macro. Therefore, we have deleted the ; two built-in-clauses for 1-. If we ever make 1- a function again, we should ; add them again. (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ x '-1)) (acl2-count x)) (zp x)) :all-fnnames '(acl2-count o< zp)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ '-1 x)) (acl2-count x)) (zp x)) :all-fnnames '(acl2-count o< zp)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ x '-1)) (acl2-count x)) (not (integerp x)) (not (< '0 x))) :all-fnnames '(acl2-count o< integerp < not)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ x '-1)) (acl2-count x)) (not (integerp x)) (< x '0) (= x '0)) :all-fnnames '(acl2-count o< integerp not < =)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ x '-1)) (acl2-count x)) (not (integerp x)) (< x '0) (= '0 x)) :all-fnnames '(acl2-count o< integerp not < =)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ x '-1)) (acl2-count x)) (not (integerp x)) (< x '0) (equal x '0)) :all-fnnames '(acl2-count o< integerp not < equal)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ '-1 x)) (acl2-count x)) (not (integerp x)) (not (< '0 x))) :all-fnnames '(acl2-count o< integerp < not)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ '-1 x)) (acl2-count x)) (not (integerp x)) (< x '0) (= x '0)) :all-fnnames '(acl2-count o< integerp not < =)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ '-1 x)) (acl2-count x)) (not (integerp x)) (< x '0) (= '0 x)) :all-fnnames '(acl2-count o< integerp not < =)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (binary-+ '-1 x)) (acl2-count x)) (not (integerp x)) (< x '0) (equal x '0)) :all-fnnames '(acl2-count o< integerp not < equal)) ; Realpart and imagpart decrease on complexps. #+:non-standard-analysis (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (realpart x)) (acl2-count x)) (not (complexp x))) :all-fnnames '(acl2-count realpart o< complexp not)) #-:non-standard-analysis (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (realpart x)) (acl2-count x)) (not (complex-rationalp x))) :all-fnnames '(acl2-count realpart o< complex-rationalp not)) #+:non-standard-analysis (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (imagpart x)) (acl2-count x)) (not (complexp x))) :all-fnnames '(acl2-count imagpart o< complexp not)) #-:non-standard-analysis (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (imagpart x)) (acl2-count x)) (not (complex-rationalp x))) :all-fnnames '(acl2-count imagpart o< complex-rationalp not)) ; Finally, cdr decreases on non-nil true-listps, but we can say ; "non-nil" as (eq x nil), (eq nil x), (null x) or (equal x nil) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (not (true-listp x)) (eq x 'nil)) :all-fnnames '(acl2-count cdr o< true-listp not eq)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (not (true-listp x)) (null x)) :all-fnnames '(acl2-count cdr o< true-listp not null)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (not (true-listp x)) (eq 'nil x)) :all-fnnames '(acl2-count cdr o< true-listp not eq)) (make built-in-clause :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :clause '((o< (acl2-count (cdr x)) (acl2-count x)) (not (true-listp x)) (equal x 'nil)) :all-fnnames '(acl2-count cdr o< true-listp not equal)))) (defun built-in-clausep2 (bic-lst cl fns ens) (cond ((null bic-lst) nil) ((and (enabled-numep (access built-in-clause (car bic-lst) :nume) ens) (subsetp-eq (access built-in-clause (car bic-lst) :all-fnnames) fns) (eq (subsumes *init-subsumes-count* (access built-in-clause (car bic-lst) :clause) cl nil) t)) (access built-in-clause (car bic-lst) :rune)) (t (built-in-clausep2 (cdr bic-lst) cl fns ens)))) (defun built-in-clausep1 (bic-alist cl fns ens) ; Bic-alist is the alist of built-in clauses, organized via top fnname. Cl is ; a clause and fns is the all-fnnames-lst of cl. This function is akin to ; some-member-subsumes in the sense of some built-in clause subsumes cl. We ; only try subsumption on enabled built-in clauses whose :all-fnnames field is ; a subset of fns. We return the rune of the subsuming clause, or nil. (cond ((null bic-alist) nil) ((or (null (caar bic-alist)) (member-eq (caar bic-alist) fns)) ; All the built-in clauses in this pot have the same top-fnname and that name ; occurs in cl. So these guys are all candidate subsumers. Note: if (car ; bic-alist) is null then this is the special pot into which we have put all ; the built-in clauses that have no "function symbols" in them, as computed by ; all-fnnames-subsumer. I don't see how this can happen, but if it does we're ; prepared! (or (built-in-clausep2 (cdr (car bic-alist)) cl fns ens) (built-in-clausep1 (cdr bic-alist) cl fns ens))) (t (built-in-clausep1 (cdr bic-alist) cl fns ens)))) (defun possible-trivial-clause-p (cl) (if (null cl) nil (mv-let (not-flg atm) (strip-not (car cl)) (declare (ignore not-flg)) ; Keep the following list of function names in sync with those in tautologyp. ; It should be, in fact, just the list in tautologyp plus IF and NOT. Note ; that although tautologyp does not expand NOT, if-tautologyp (and hence ; tautologyp) knows about NOT, so we look for it here. (or (ffnnamesp '(if not iff ;not implies eq atom eql = /= null ; If we ever make 1+ and 1- functions again, they should go back on this list. zerop plusp minusp listp mv-list return-last wormhole-eval force case-split double-rewrite) atm) (possible-trivial-clause-p (cdr cl)))))) (defun trivial-clause-p (cl wrld) (or (member-equal *t* cl) (and (possible-trivial-clause-p cl) (tautologyp (disjoin cl) wrld)))) (defun built-in-clausep (caller cl ens match-free-override wrld state) ; We return two results. The first indicates whether cl is a ``built ; in clause,'' i.e., a known truth. The second is the supporting ; ttree (or nil). This ttree is guaranteed to be assumption-free. ; Caller is just a token that indicates what function (possibly indirectly) is ; responsible for calling built-in-clausep. ; Once upon a time, this function used force-flg = t in the ; type-alist-clause call below. Thus, the callers of this function ; anticipate splitting. We have backed off force-flg = t here because ; it seems likely to cause loops due to assuming literals that are ; explicitly denied later in the clause (see the warning in ; type-alist-clause). But this condition has never been witnessed and ; the change was made without significant testing of the force-flg = t ; case. However, the callers of this function do not now anticipate ; the presence of 'assumption tags in the ttree. Thus, if you make ; this function force or case-split, you must change its callers! ; Starting with Version_2.7, this function uses forward-chaining. This idea ; arose when changing translate-declaration-to-guard to output calls of ; signed-byte-p, unsigned-byte-p, and integer-range-p. Suddenly some guards ; proofs needed to be done that formerly were handled by built-in-clausep. But ; that problem is reduced or eliminated when we forward-chain and have suitable ; forward-chaining rules from those new predicates. ; When this function is allowed to return t, it is also allowed to return nil. ; In particular, the limit on one-way-unify1 calls in the call of subsumes in ; built-in-clausep2 can cause this predicate to fail. (let ((rune (built-in-clausep1 (global-val 'built-in-clauses wrld) cl (all-fnnames-lst cl) ens))) (cond (rune (mv t (push-lemma rune nil))) (t (mv-let (contradictionp type-alist ttree) (forward-chain-top caller cl nil ; pts nil ; ok-to-force nil ; do-not-reconsiderp wrld ens match-free-override state) (declare (ignore type-alist)) (cond ((not contradictionp) ; At one time we checked trivial-clause-p before doing anything else. But Sol ; Swords sent an example defun whose body was a big if-then-else term that ; generated 42 guard obligations, some of which were very expensive to check ; with trivial-clause-p, but all of which were very quickly found contradictory ; by forward-chain. (cond ((trivial-clause-p cl wrld) (mv t nil)) (t (mv nil nil)))) ((tagged-objectsp 'assumption ttree) (mv (er hard 'built-in-clausep "It was thought that the forward-chain call in ~ this function could not produce an ~ 'assumption but it did! Try running ~ forward-chain on ~X01 with ~ match-free-override = ~x2. The ens and wrld ~ used here must be recovered by other means if ~ (ens state) and (w state) don't work." (kwote cl) nil (kwote match-free-override)) nil)) (t (mv t ttree)))))))) (defun crunch-clause-segments1 (seg1 pts1 cl pts) ; This function reverses seg1 and appends it to cl, and does the analogous ; thing to pts1 and pts. However, if a literal in seg1 already occurs in ; cl, it is merged into that literal and its pt is consed onto the ; pt of the literal in cl. ; Note: It is a mystery how the opportunity for this merging should arise. It ; appears to be impossible because seg1 was rewritten under the assumption of ; the falsity of the literals in cl and hence any such literal of seg1 would ; have evaporated. Nevertheless, in the days before we used pts this function ; had been modified from the rev-append approach to a careful use of ; member-equal and hence duplicate literals do, apparently, arise. ; Note: In normal use, the first literal in cl at the beginning will be the ; marker literal dealt with by crunch-clause-segments2 and ; crunch-clause-segments. Observe that the pts of literals occurring after ; that marker in cl are completely irrelevant to the behavior of ; crunch-clause-segment, even though we are here careful to move pts from pts1 ; into that section of pts when merging occurs. They are irrelevant because ; crunch-clause-segments2 just collects the pts up to the marker. It might ; still be important for us to catch merges, since it is possible that two ; literals within seg1 itself will merge and thus we will create a consp pt for ; that literal and that consp pt will be collected by crunch-clause-segments2 ; and find its way into the main computation. Stranger things have happened in ; this code! (cond ((null seg1) (mv cl pts)) (t (mv-let (cl pts ttree) (add-literal-and-pt (car seg1) (car pts1) cl pts nil) ; Add-literal-and-pt just passes its last argument through as the ttree and we ; simply ignore the resulting nil. This is just an easy way to cons the first ; literal of seg1 onto cl and the first pt of pts1 onto pts -- provided the ; literal doesn't already occur in cl -- and to merge the pt into the ; appropriate element of pts if it does. (declare (ignore ttree)) (crunch-clause-segments1 (cdr seg1) (cdr pts1) cl pts))))) (defun crunch-clause-segments2 (cl pts seg1 pts1) ; See crunch-clause-segments. (cond ((null cl) (mv seg1 pts1 nil)) ((and (consp (car cl)) (eq (ffn-symb (car cl)) 'car) (eq (fargn (car cl) 1) :crunch-clause-segments-marker)) (mv seg1 pts1 (cdr cl))) (t (crunch-clause-segments2 (cdr cl) (cdr pts) (cons (car cl) seg1) (cons (car pts) pts1))))) (defun crunch-clause-segments (seg1 pts1 seg2 ens wrld state ttree) ; This function is a special purpose subroutine of rewrite-clause. Seg1 and ; seg2 are just lists of literals. Pts1 is in weak 1:1 correspondence with ; seg1 and enumerates the parent trees of the corresponding literals of seg1. ; Consider the clause obtained by appending these two segments. ; {lit4 ... lit7 lit1' ... lit2' lit3a ... lit3z} ; cl ; | <- seg1 -> | <- seg2 -> | ; unrewritten | rewritten ; Context: The rewriter is sweeping through this clause, rewriting each literal ; and assembling a new clause. It has rewritten none of the seg1 literals and ; all of the seg2 literals. It has just rewritten some literal called lit3. ; After clausifying the result (and getting in this case lit3a ... lit3z) it is ; about to start rewriting the first literal of seg1, lit4. It has already ; rewritten lit1'...lit2'. The rewriter actually keeps the unrewritten part of ; the clause (seg1) separate from the rewritten part (seg2) so that it knows ; when it is done. In the old days, it would just proceed to rewrite the first ; literal of seg1. ; But we are trying something new. Suppose lit3 was something like (not ; (member x '(...))). Then we will get lots of segs, each of the form (equal x ; '...). We are trying to optimize our handling of this by actually stuffing ; the constant into the clause and running any terms we can. We do this in ; what we think is a very elegant way: We actually create cl and call ; remove-trivial-equivalences on it. Then we recover the two parts, ; unrewritten and rewritten. The trick is how we figure out which is which. ; We put a marker literal into the clause, after seg1 and before ; seg2. Remove-trivial-equivalences may do a lot of literal evaluation ; and deletion. But then we find the marker literal and consider everything to ; its left unrewritten and everything else rewritten. ; We return three values: The unrewritten part of cl, the rewritten part of cl, ; and an extension of ttree. (let ((marker '(car :crunch-clause-segments-marker))) (mv-let (cl pts) (crunch-clause-segments1 seg1 pts1 (cons marker seg2) nil) (mv-let (hitp cl pts ttree) (remove-trivial-equivalences cl pts nil ;;; see Note ens wrld state ttree nil nil) ; Note: In the call of remove-trivial-equivalences above we use remove-flg = ; nil. At one time, we used remove-flg = t, thinking that our cl here was the ; entire problem and thus we could delete the literal after using it. However, ; because of the fc-pair-lst and the simplify-clause-pot-lst -- both of which ; may contain terms that mention the "eliminated" variable and both of which ; may introduce such terms into the clause later -- we believe it best to keep ; the equality until we are at the top of the waterfall again. (cond ((null hitp) (mv seg1 pts1 seg2 ttree)) (t (mv-let (seg1 pts1 seg2) (crunch-clause-segments2 cl pts nil nil) (mv seg1 pts1 seg2 ttree)))))))) ; We now develop code to deal with the unrewritten assumptions generated by ; rewriting a literal of a clause. We would like to implement the illusion ; that all 'assumptions produced while rewriting a literal have actually been ; rewritten. We achieve that by stripping such assumptions out of the returned ; ttree, rewriting them, and putting them back. See ; resume-suspended-assumption-rewriting1, below, for the details. (defun strip-non-rewrittenp-assumptions1 (recs acc) ; See strip-non-rewrittenp-assumptions. We move non-rewritten assumptions from ; recs to acc to obtain recs' and acc, and return (mv recs' acc'). (cond ((endp recs) (mv nil acc)) (t (mv-let (rest acc) (strip-non-rewrittenp-assumptions1 (cdr recs) acc) (cond ((access assumption (car recs) :rewrittenp) (cond (acc ; a record was removed: (cdr recs) != rest (mv (cons (car recs) rest) acc)) (t (mv recs nil)))) (t (mv rest (cons (car recs) acc)))))))) (defun strip-non-rewrittenp-assumptions (ttree) ; Strip out all 'assumption records that have :rewrittenp nil and accumulate ; them into ans. Return (mv ttree' ans'), where ttree' is the result of ; removing the records in ans from ttree. (let ((recs (tagged-objects 'assumption ttree))) (cond (recs (let ((ttree0 (remove-tag-from-tag-tree! 'assumption ttree))) (mv-let (rewritten unrewritten) (strip-non-rewrittenp-assumptions1 recs nil) (mv (cond (rewritten (extend-tag-tree 'assumption rewritten ttree0)) (t ttree0)) unrewritten)))) (t (mv ttree nil))))) (defun assumnote-list-to-token-list (assumnote-list) (if (null assumnote-list) nil (cons (access assumnote (car assumnote-list) :rune) (assumnote-list-to-token-list (cdr assumnote-list))))) (defun resume-suspended-assumption-rewriting1 (assumptions ancestors gstack simplify-clause-pot-lst rcnst wrld state step-limit ttree) ; A simple view of this function then is that it rewrites each assumption in ; assumptions and puts the rewritten version into ttree, reporting the first ; false assumption if finds. ; Assumptions is a list of unrewritten assumptions that were generated while ; rewriting with the rewrite arguments given to this function. We return two ; results, (mv bad-ass ttree), where bad-ass is either nil or an assumption ; whose :term can be rewritten to false in the current context and ttree is a ; ttree extending the input tree, justifying all the rewriting done (including ; that to false, if bad-ass), containing 'assumption tags for all the ; assumptions in assumptions, and containing no unrewritten assumptions ; (assuming the initial ttree contained no unrewritten assumptions). ; The complication is that rewriting an assumption generates assumptions which ; we must also rewrite. The process could in principle loop if rewriting an ; assumption can re-generate the assumption. We break this potential loop via ; the use of ancestors. We imagine we are just backchaining. ; It is perhaps worth reminding the reader that these assumptions cannot be ; rewritten before they are forced because they come from type-set, which is ; defined before the rewriter is defined. Thus, we are really implementing a ; kind of delayed mutual recursion: type-set is reporting some assumptions it ; would like rewritten and we are doing it. (cond ((endp assumptions) (mv step-limit nil ttree)) (t (let* ((assn (car assumptions)) (term (access assumption assn :term))) (mv-let (on-ancestorsp assumed-true) (ancestors-check term ancestors (assumnote-list-to-token-list (access assumption assn :assumnotes))) (cond (on-ancestorsp ; If the assumption's term is assumed true, we may omit it from the answer. If ; it is not assumed-true, we don't know that it is false: it might merely be ; worse than some ancestor. We therefore just move the now rewritten ; assumption into the ttree and go on. Once upon a time we considered ; aborting, reporting assn as a bad-ass. Observe that if the complement of ; term is on ancestors, then term is being assumed nil (because (not term) is ; assumed true). Doesn't that mean we coul rewrite term to nil? No. All we ; really know is that term is impossible to prove by rewriting using whatever ; lemmas we did this time. Term might be provable. Consider the fact that ; the user could have proved (implies term term) for any term, even a provable ; one. Then in trying to prove term we'd assume it false by putting (not term) ; on ancestors and backchain to term, which would lead us here, with the ; complement of term on ancestors. That doesn't mean term can't be proved! (resume-suspended-assumption-rewriting1 (cdr assumptions) ancestors gstack simplify-clause-pot-lst rcnst wrld state step-limit (if assumed-true ttree (add-to-tag-tree 'assumption (change assumption assn :rewrittenp term) ttree)))) (t ; We are about to rewrite term, just as in relieve-hyp, and so we add its ; negation to ancestors. This is equivalent to assuming term false. (let ((new-ancestors (push-ancestor (dumb-negate-lit term) (assumnote-list-to-token-list (access assumption assn :assumnotes)) ancestors))) (mv-let (not-flg atm) (strip-not term) (sl-let (val ttree1) (rewrite-entry (rewrite atm nil 'forced-assumption) :rdepth (rewrite-stack-limit wrld) :step-limit step-limit :type-alist (access assumption assn :type-alist) :obj '? :geneqv *geneqv-iff* :wrld wrld :fnstack nil :ancestors new-ancestors :backchain-limit (access rewrite-constant rcnst :backchain-limit-rw) :simplify-clause-pot-lst simplify-clause-pot-lst :rcnst rcnst :gstack gstack :ttree nil) (let ((val (if not-flg (dumb-negate-lit val) val))) (cond ((equal val *nil*) ; If term rewrote to nil, we return assn as a bad assumption. We ; assume the proof attempt is doomed. We accumulate into ttree the ; ttree supporting the final rewrite to nil. This is a little odd. ; The bad-ass returned is the unrewritten assumption generated by ; (force term) or (case-split term). But the ttree returned may ; contain work done on other forces as well as the work done to show ; that term reduces to nil, even though we are returning term, not ; nil. (mv step-limit assn (cons-tag-trees ttree1 ttree))) (t ; If term rewrote to non-nil, we must process the unrewritten assumptions in ; the ttree, ttree1, produced by rewriting term. We do that with a separate ; recursive call of this function, because we must pass in the new-ancestors so ; that we don't loop. Think of us as having assumed term false, rewritten it ; while making certain assumptions, and now -- still in that context of having ; assumed it false -- we will work on those assumptions. (mv-let (ttree1 assumptions1) (strip-non-rewrittenp-assumptions ttree1) ; Observe that if ttree1 contains any assumptions, they are of the rewrittenp t ; variety. We accumulate ttree1 into our answer ttree. Unless term rewrote to ; t, we accumulate the rewritten version of assn into our answer. Note that ; since the :geneqv used above is iff, we can rely on the fact that if val is ; known not to be nil then it is actually t. Finally, we rewrite all of the ; unrewritten assumptions (assumptions1) generated by rewriting term to val ; accumulate them into our answer as well. (sl-let (bad-ass ttree) (resume-suspended-assumption-rewriting1 assumptions1 new-ancestors ; the critical difference gstack simplify-clause-pot-lst rcnst wrld state step-limit (cons-tag-trees ttree1 (if (equal val *t*) ttree (add-to-tag-tree 'assumption (change assumption assn :term val :rewrittenp term) ttree)))) (cond (bad-ass (mv step-limit bad-ass ttree)) (t ; Having taken care of assn and all the unrewritten assumptions generated when ; we rewrote it, we now do the rest of assumptions. (resume-suspended-assumption-rewriting1 (cdr assumptions) ancestors gstack simplify-clause-pot-lst rcnst wrld state step-limit ttree)))))))))))))))))) (defun resume-suspended-assumption-rewriting (ttree ancestors gstack simplify-clause-pot-lst rcnst wrld state step-limit) ; We copy ttree and rewrite all the non-rewrittenp assumptions in it, deleting ; any thus established. We return (mv bad-ass ttree'), where bad-ass is either ; nil or an assumption in ttree whose :term can be rewritten to nil. Ttree' is ; an extension of the result of removing all non-rewrittenp assumptions from ; ttree and then replacing them by their rewritten versions plus the ttrees ; produced by that rewriting. There are no non-rewrittenp assumptions in ; ttree'. (mv-let (ttree assumptions) (strip-non-rewrittenp-assumptions ttree) (resume-suspended-assumption-rewriting1 assumptions ancestors gstack simplify-clause-pot-lst rcnst wrld state step-limit ttree))) ; Essay on Case Limit ; The case-limit component in the case-split-limitations is a number ; used by rewrite-clause to shut down case splitting after a certain ; point is reached. ; The informal heuristic implemented here is ``don't continue to ; rewrite literals once the estimated number of output clauses exceeds ; some limit.'' We call the limit the ``case-limit.'' There are many ; interpretations of this heuristic. We discuss them here. In this ; discussion we abstract away from the particulars of given call of ; rewrite-clause and instead consider all the calls of that function ; on the Common Lisp control stack. Each of those calls can be ; characterized by a picture like this {h1 ... hk ! lit1 ... litn} ; where the hi are already-rewritten terms generated by splitting on ; the literals that we've already rewritten, the ! signifies where we ; are in this case, and the liti are the unrewritten literals from the ; tail of the original clause. Suppose there are now more than ; case-limit of these cases; we will handle each with the same ; approach. Here are the approaches that come to mind. We have ; chosen to implement (1) after some experimentation with (3). ; (0) Stop now and process no further literals. Return the clause ; {h1 ... hk lit1 ... litn}. ; The advantage to (0) is that it is the cheapest thing we could do. ; But it dooms us to revisit each of the hi with the rewriter before ; we even look at their combination or their effects on the liti. The ; other interpretations below all do some work on the liti in hopes ; that we will have less work to do later. ; (1) It is possible that -h1 ... -hk is contradictory, or that -h1 ; ... -hk together with -lit2, ..., -litn, are contradictory. ; Such contradictions will be found by type-set when we try to ; assume all of them false in order to rewrite lit1. So we could ; proceed to do the type-alist work to set up the ``rewrite'' of ; each liti, detect the contradiction if it happens, but ; short-circuit the rewrite at the last minute if no contradiction ; arises. In the short-circuit we would just have the rewrite-atm ; call return liti (i.e., each liti would rewrite to itself). ; This is actually the simplest change to the code. ; As noted above, if we find a type-set contradiction in (1), we won't ; have to rewrite the hi again for this case. Otherwise, we will. ; This kind of observation applies to the other ideas below. ; (2) Build the type-alist and actually rewrite each liti while we have ; all this context in our hands. If rewriting liti generates an IF-free ; term (e.g., T or no change or simple normalization), just proceed. ; But if it generates an IF, pretend we did nothing and rewrite it ; to itself. ; (3) As (2) above, but if it generates an IF, use the IF without clausifying ; it. This has the effect of possibly stripping out of liti all the ; cases that are precluded by the hi, without generating any more cases. ; We will eventually see the IFs in this literal again and split them ; out. ; Once upon a time we implemented a ``creep up to the limit'' ; heuristic here: If we have not yet exceeded the case limit but the ; current literal's clausification does exceed the limit, then we left ; that literal in IF form and went on. We are afraid that when the ; limit is exceeded it is exceeded by some hideous amount, e.g., 2^32 ; clauses are produced. We call such a literal a ``big splitter.'' ; The IF form probably tells the user more about what opened than the ; clauses do. Furthermore, little splitters further on down the ; clause might be allowed to open and may ultimately allow us to ; simplify the big splitter. This heuristic had to be implemented in ; such a way that the big splitter was eventually split out. (The ; user might have set the case limit rather low and might be using it ; to introduce cases slowly.) Our idea was just to allow the split ; when it is the first literal to split. It eventually will be. We ; abandoned this creepy idea because it put unsplit big-splitters on ; the type-alist, where they were essentially useless, and then all ; the downstream literals simplified in the empty context, introducing ; many bogus case splits. (defun helpful-little-ecnt-msg (case-limit ecnt) (cond ((and (null case-limit) (> ecnt 1000)) (prog2$ (cw "~%~%Helpful Little Message: The simplifier is now expected to ~ produce approximately ~n0 subgoals. See :DOC ~ case-split-limitations and see :DOC splitter.~%~%" ecnt) ecnt)) (t ecnt))) (mutual-recursion (defun rewrite-clause (tail pts bkptr gstack new-clause fc-pair-lst wrld simplify-clause-pot-lst rcnst flg ecnt ans ttree fttree splitp state step-limit) ; In nqthm this function was called SIMPLIFY-CLAUSE1. ; We are to rewrite the literals of the clause cl formed by appending tail to ; new-clause. We assume rcnst has the correct top-clause and pt and the ; current-clause is the correct clause. We assume the simplify-clause-pot-lst ; is set up for the current-clause. We assume fc-pair-lst is a list of pairs ; of the form (concl . ttree) of conclusions derived by forward chaining from ; negations of literals in current-clause. The ttrees indicate dependence on ; parents (via 'pt tags) and we may use any concl not dependent upon the ; literals contained in the :pt of rcnst (to which we add the current literal's ; pt). Ecnt is the estimated number of output clauses. We refine it as we go ; and it is ultimately returned and is the length of ans. Fttree (``false ; tag-tree'') is either nil or else is a non-nil tag-tree justifying the ; falsity of every literal in new-clause; see the comment in rewrite-atm about ; the third argument returned by that function. Note that it is always legal ; to return the false clause in place of any other clause, so our use of fttree ; may be viewed as heuristic, i.e., it is clearly sound. ; We return 5 values: a new step-limit; a flag indicating whether anything was ; done; the final ecnt; a set, ans, of clauses whose conjunction implies cl ; under our assumptions; and a ttree that describes what we did. Our answers ; are accumulated onto flg, ecnt, ans, and ttree as we recur through the ; literals of tail. ; Finally, we comment on parameter splitp, which controls the rw-cache in the ; case that the rw-cache-state is t. (See the Essay on Rw-cache.) This ; parameter is true when we are part of a split into two or more clauses, which ; case (if the rw-cache-state is t) we think of the split into children the way ; we think of entering branches of an IF expression, discarding the "any" cache ; since we do not trust its use in a stronger context. If there is just one ; child, our heuristic is to keep the rw-cache, with the (perhaps bold) ; expectation that because there is no case-split, we can continue to trust its ; full rw-cache. While this assumption may be bold, it might be true in many ; cases, and we are willing to make it since the default rw-cache-state is ; :atom, not t. (cond ((null tail) (let ((rune (built-in-clausep1 (global-val 'built-in-clauses wrld) new-clause (all-fnnames-lst new-clause) (access rewrite-constant rcnst :current-enabled-structure)))) (cond (rune (mv step-limit t (- ecnt 1) ans (push-lemma rune ttree))) ((and fttree ; Avoid considering it a "change" to rewrite the empty (false) clause to ; itself. Note that we already know (null tail) in this context. (not (and (null ans) (null new-clause)))) (mv step-limit t 1 (list *false-clause*) (cons-tag-trees fttree ttree))) (t (mv step-limit flg ecnt (cons new-clause ans) ttree))))) (t (mv-let (not-flg atm) (strip-not (car tail)) (let* ((new-pts (cons (car pts) (access rewrite-constant rcnst :pt))) (local-rcnst (change rewrite-constant rcnst :current-literal (make current-literal :not-flg not-flg :atm atm) :pt new-pts)) (case-split-limitations (access rewrite-constant rcnst :case-split-limitations)) ; Warning: Keep the following bindings in sync with the definitions of macros ; case-limit and sr-limit. (case-limit (cadr case-split-limitations)) (sr-limit (car case-split-limitations))) ; Note that in local-rcnst we declared inactive the polys descending ; from the current lit. ; The use of simplify-clause-pot-lst below is new to Version_2.8. This ; is in support of type-set using linear arithmetic --- we use the ; simplify-clause-pot-lst when building the type-alist. Note that we ; also pass in a parent-tree to declare inactive the polys descending ; from the current lit. (mv-let (contradictionp type-alist ttree0 current-clause) (rewrite-clause-type-alist tail new-clause fc-pair-lst local-rcnst wrld simplify-clause-pot-lst new-pts) ; Ttree0 is relevant only if we got a contradiction. (cond (contradictionp (mv step-limit t (- ecnt 1) ans (cons-tag-trees ttree0 ttree))) (t (let ((skip-rewrite-atm (and case-limit (> ecnt case-limit))) (rw-cache-state (access rewrite-constant rcnst :rw-cache-state))) (sl-let (val ttree1 fttree1) ; Note: Nqthm used a call of (rewrite atm ...) here, while we now look on the ; type-alist for atm and then rewrite. See the Nqthm note below. ; Note: Here is our ``short circuit'' implementation of case limit ; interpretation (2). We just bail out if we have exceeded the case ; limit. (if skip-rewrite-atm (mv step-limit atm (add-to-tag-tree! 'case-limit t ttree) nil) (pstk (rewrite-atm atm not-flg bkptr gstack type-alist wrld simplify-clause-pot-lst local-rcnst current-clause state step-limit (cond ((eq rw-cache-state :atom) (erase-rw-cache ttree)) ((and (eq rw-cache-state t) splitp) (rw-cache-enter-context ttree)) (t ttree))))) (let* ((ttree1 (cond (skip-rewrite-atm ttree1) ((eq rw-cache-state :atom) (erase-rw-cache ttree1)) ((and (eq rw-cache-state t) splitp) (rw-cache-exit-context ttree ttree1)) (t ttree1))) (val (if not-flg (dumb-negate-lit val) val)) (branches (pstk (clausify val (convert-clause-to-assumptions (cdr tail) (convert-clause-to-assumptions new-clause nil)) nil sr-limit))) (ttree1 (if (and sr-limit (> (length branches) sr-limit)) (add-to-tag-tree 'sr-limit t ttree1) ttree1)) (action (rewrite-clause-action (car tail) branches)) (segs ; Perhaps we can simply use branches below. But that requires some thought, ; because the form below handles true clauses (including *true-clause*) with ; special care. This issue arose as we removed old-style-forcing from the ; code. (disjoin-clause-segment-to-clause-set nil branches)) (nsegs (length segs))) (sl-let (bad-ass ttree1) (resume-suspended-assumption-rewriting ttree1 nil ;ancestors - the fact that this isn't what it was when ;we pushed the assumption could let rewriting go deeper gstack simplify-clause-pot-lst local-rcnst wrld state step-limit) (cond (bad-ass ; When we rewrote the current literal of the clause we made an assumption ; that we now know to be false. We must abandon that rewrite. We ; act just as though the literal rewrote to itself: we pretend we have just ; done the rewrite-atm above and obtained atm instead of val. We just ; reproduce the code, except we don't worry about assumptions. (let* ((val (if not-flg (dumb-negate-lit atm) atm)) (branches (pstk (clausify val (convert-clause-to-assumptions (cdr tail) (convert-clause-to-assumptions new-clause nil)) nil sr-limit))) (ttree2 (if (and sr-limit (> (length branches) sr-limit)) (add-to-tag-tree 'sr-limit t ttree) ttree)) (action (rewrite-clause-action (car tail) branches)) (segs branches) (nsegs (length segs))) ; For an explanation of the following call of rewrite-clause-lst, see ; the standard call below. This is just like it, except we are ignoring ; ttree1. Note that ttree2 is an extension of ttree. (rewrite-clause-lst segs (1+ bkptr) gstack (cdr tail) (cdr pts) new-clause fc-pair-lst wrld simplify-clause-pot-lst (if (eq action 'shown-false) local-rcnst rcnst) (or flg (not (eq action 'no-change))) (helpful-little-ecnt-msg case-limit (+ ecnt -1 nsegs)) ans ttree2 nil ; literal is not known to be false (cdr segs) ; splitp state step-limit))) ; Here, once upon a time, we implemented the ``creep up on the limit'' ; twist of case limit interpretation (3). Instead of short-circuiting ; above we rewrote the atm. We either clausified the result or just ; turned it into a singleton clause possibly containing IFs, depending ; on whether we were already above the case-limit. We had to handle ; ttree1 appropriately to record the case limit restriction. We then ; continued on to here. ; The following test determines that we're about to exceed the ; case-limit. ; (and case-limit ; (<= ecnt case-limit) ; (< case-limit (+ ecnt -1 nsegs)) ; (< 1 ecnt)) ; It says we are currently at or below the case limit but the segs ; generated for this literal would push us over it. Furthermore, this ; is not the very first literal to produce segs (ecnt exceeds 1). In ; this case, we ignored segs. That is, we just put the un-clausified ; val in as a single literal. We hold ecnt the fixed and show the ; user this rewritten goal in IF form. Eventually this IF would ; become the first literal that produces segs and the (< 1 ecnt) would ; fail, so we would split it out then. ; But as we've abandoned the whole idea of rewriting after the limit ; has been exceeded, we no longer implement this creepy idea. ; Instead, we just blast past the limit and then shut 'er down. (t ; In the case that there is no bad assumption, then ttree1 is a ttree in which ; all assumptions have been rewritten. (rewrite-clause-lst segs (1+ bkptr) gstack (cdr tail) (cdr pts) new-clause fc-pair-lst wrld simplify-clause-pot-lst ; If the current lit rewrote to false, or even if it rewrote at all ; (since critical information may be lost), then we should continue to ; ignore polys and forward-chaining facts that descend from it. We ; therefore pass to lower level calls the local-rcnst, which has the ; current literal's index in its :pt. The current-literal in that ; local-rcnst will be reset and the :pt will be extended locally ; there. If the current lit did not change upon rewrite, then we want ; to restore :pt to what it was at entry, so we pass the original ; rcnst. One could consider this as (change rewrite-constant rcnst ; :pt ...) to add to the old rcnst the pt of the literal just ; rewritten. Before v2-9, we only used local-rcnst when action is ; 'shown-false, which resulted in dropping important information, as ; shown in the following example derived from one provided by Art ; Flatau. Before the change, the goal ("Goal'") produced was ; (IMPLIES (AND (< 30 N) (<= 30 N)) (FOO N)); after the change, the ; (INTEGERP N) hypothesis was preserved. ; (defstub foo (n) t) ; (defthm natp-fc-2 ; (implies (natp x) (integerp x)) ; :rule-classes :forward-chaining) ; (thm (implies (and (not (or (not (natp n)) (<= n 30))) ; (integerp n) ; (<= 30 n)) ; (foo n))) (if (eq action 'no-change) rcnst local-rcnst) (or flg (not (eq action 'no-change))) ; Prior to this literal, we estimated the number of output clauses to ; be ecnt. This literal of this clause rewrote to nsegs segments. So ; now we have ecnt-1+nsegs clauses. This will be correct if no other ; literal (anywhere on the call stack) splits. ; We could estimate differently. We could suppose that this literal ; will split nsegs ways every time it occurs in the call stack. ; Essentially we would let the new ecnt be (* ecnt (max 1 nsegs)). ; (Note that if nsegs is 0, we keep ecnt fixed; the lit rewrote to ; nil.) That estimate will grow faster and probably is an upper bound ; on the actual number that would be created (e.g., some would almost ; certainly be tautologies). If we used such a method, we would start ; to cut off case splitting earlier, we would get more literals with ; IFs in them, and fewer overall clauses because the estimate would be ; too large and kick in even though some of the previous splitting was ; tautologous. (helpful-little-ecnt-msg case-limit (+ ecnt -1 nsegs)) ans (if (eq action 'no-change) (if (eq rw-cache-state :atom) ttree (accumulate-rw-cache t ttree1 ttree)) ttree1) (and fttree1 fttree (cons-tag-trees fttree1 fttree)) (cdr segs) ; splitp state step-limit))))))))))))))) (defun rewrite-clause-lst (segs bkptr gstack cdr-tail cdr-pts new-clause fc-pair-lst wrld simplify-clause-pot-lst rcnst flg ecnt ans ttree fttree splitp state step-limit) ; Fttree is either nil or else is a tag-tree justifying the falsity of every ; literal in segs and every literal in new-clause; see the comment in ; rewrite-atm about the third argument returned by that function. ; Splitp is true when we do not want to trust the "any" cache of ttree; for ; more explanation, see rewrite-clause. (cond ((null segs) (mv step-limit flg ecnt ans ttree)) (t (sl-let (flg1 ecnt1 ans1 ttree1) (rewrite-clause-lst (cdr segs) bkptr gstack cdr-tail cdr-pts new-clause fc-pair-lst wrld simplify-clause-pot-lst rcnst flg ecnt ans ttree fttree splitp state step-limit) (mv-let (unrewritten unwritten-pts rewritten ttree2) (crunch-clause-segments cdr-tail cdr-pts (append new-clause (set-difference-equal (car segs) new-clause)) (access rewrite-constant rcnst :current-enabled-structure) wrld state ttree1) (rewrite-clause unrewritten unwritten-pts bkptr gstack rewritten fc-pair-lst wrld simplify-clause-pot-lst rcnst flg1 ecnt1 ans1 ttree2 fttree splitp state step-limit)))))) ) ; After removing trivial equations, simplify-clause must set up ; the context in which the rewriting of the clause is done. This ; mainly means setting up the simplify-clause-pot-lst. (defun setup-simplify-clause-pot-lst1 (cl ttrees type-alist rcnst wrld state step-limit) (sl-let (contradictionp simplify-clause-pot-lst) (let ((gstack (initial-gstack 'setup-simplify-clause-pot-lst nil cl))) (rewrite-entry (add-terms-and-lemmas cl ;term-lst to assume ttrees ;corresponding tag-trees nil ;positivep (terms assumed false) ) :rdepth (rewrite-stack-limit wrld) :step-limit step-limit :type-alist type-alist :obj nil :geneqv nil :wrld wrld :fnstack nil :ancestors nil :backchain-limit (access rewrite-constant rcnst :backchain-limit-rw) :simplify-clause-pot-lst nil :pot-lst-terms nil :rcnst rcnst :gstack gstack :ttree nil)) (cond (contradictionp #-acl2-loop-only (dmr-flush t) (mv step-limit contradictionp nil)) (t #-acl2-loop-only (dmr-flush t) (mv step-limit nil simplify-clause-pot-lst))))) (defun setup-simplify-clause-pot-lst (cl ttrees fc-pair-lst type-alist rcnst wrld state step-limit) ; We construct the initial value of the simplify-clause-pot-lst in preparation ; for rewriting clause cl. We assume rcnst contains the user's hint settings, ; the correct top-clause and current clause, and a null :pt. ; We return three values. The first is a new step-limit. If the second is ; non-nil it indicates that we have proved cl and the other value is ; irrelevant. In the case that we prove clause the second result is a poly, ; meaning it was proved by linear arithmetic. The assumptions in the ttree of ; that poly must be considered before cl is declared proved. When the second ; answer is nil the third is the constructed simplify-clause-pot-lst. ; As of Version_2.8, we start by adding the (negations of) any forward-chaining ; conclusions to the head of cl and the corresponding ttrees to ttrees. We ; then call the original setup-simplify-clause-pot-lst on the resultant ; expanded clause. This will allow us to use forward-chaining conclusions in ; linear arithmetic. ; Here is one example of why we might want to do this: ; (defun bytep (n) ; (and (integerp n) ; (<= -128 n) ; (< n 128))) ; ; (defthm bytep-thm ; (implies (and (integerp n) ; (<= -128 n) ; (< n 128)) ; (bytep n))) ; ; (defthm bytep-fc-thm ; (implies (bytep n) ; (and (integerp n) ; (<= -128 n) ; (< n 128))) ; :rule-classes :forward-chaining) ; ; (in-theory (disable bytep)) ; ; (defthm tricky ; (implies (and (bytep n) ; (bytep (+ 7 n))) ; (bytep (+ 3 n)))) ; Before linear arithmetic used the conclusions of forward-chaining rules, one ; would have to re-enable the definition of bytep in order to prove tricky. ; But if this appeared in a larger context, in which one had proved a bunch of ; lemmas about bytep, one could find oneself in a pickle. By enabling bytep, ; one loses the ability to use all the lemmas about it. Without enabling ; bytep, tricky is hard to prove. ; And here is another example: ; (defun bvecp (x n) ; (and (integerp x) ; (<= 0 x) ; (< x (expt 2 n)))) ; ; (defthm bvecp-2-<-4 ; (implies (bvecp x 2) ; (and (integerp x) ; (<= 0 x) ; (< x 4))) ; :rule-classes :forward-chaining) ; ; (in-theory (disable bvecp)) ; ; (thm (implies (and (bvecp x 2) ; (not (equal x 0)) ; (not (equal x 1)) ; (not (equal x 2))) ; (equal x 3))) (cond ((null fc-pair-lst) (setup-simplify-clause-pot-lst1 cl ttrees type-alist rcnst wrld state step-limit)) (t (setup-simplify-clause-pot-lst (cons (dumb-negate-lit (caar fc-pair-lst)) cl) (cons (cdar fc-pair-lst) ttrees) (cdr fc-pair-lst) type-alist rcnst wrld state step-limit)))) (defun sequential-subst-var-term (alist term) ; Each element of alist is of the form (vari . termi). We replace ; vari by termi in term and then sequentially apply the remaining ; pairs to the result. (cond ((null alist) term) (t (sequential-subst-var-term (cdr alist) (subst-var (cdar alist) (caar alist) term))))) (defun process-equational-polys (cl hist rcnst type-alist wrld pot-lst flg ttree) ; We deduce from pot-lst all the interesting equations in it and add ; them to cl unless they have already been generated and recorded in ; hist. The flg and ttree are merely accumulators where we construct ; our answers. In the top-level call, flg should be nil and ttree ; should be any ttree we want to infect with our answer. Nil would do. ; We return three results, flg, cl and ttree. The first indicates ; whether we did anything. The second is the final value of cl and ; the third is the final ttree. That ttree will record the equations ; we generated and used in this step. It should become part of the ; history of our output cl so that we do not loop. ; We merely scan down pot-lst. At every pot we find the first ; acceptable equational poly (if any) and change flg, cl and ttree ; appropriately. ; Historical note: Previous to Version_2.7, rather than add certain ; equalities to cl we performed the substitution suggested by that ; equality. This substitution forced us to carry along another ; argument, which was the list of all such substitutions made to date. ; That was called var-term-substs. Here is a Historical Comment that ; deals with the necessity for this now eliminated argument. ; Historical Comment ; The argument var-term-substs is a list of pairs of the form (var ; . term). These represent some of the equations already found, with ; the first pair on the list representing the earliest such equation. ; (That is, the list is in chronology order, not reverse chronological ; order.) When a new equation is found and that equation relates a ; var to a term (instead of two non-var terms), we do not really add ; the equation to the clause but rather just substitute the term for ; the var, eliminating that variable. This can raise problems if, for ; example, we find A = B and replace all the B's by A, and then later ; find B = C. Had we actually added (equal A B) in response to the ; first equation, this would not be a problem. But since we didn't ; add that equation but just eliminated all the B's in favor of A, we ; now find B = C unconnected to A. So every time we find a new ; equation from the pot we first apply each of the substitution pairs ; to it, sequentially. ; Here is an example that failed under Version_2.4 (which did not ; have the var-term-substs argument) but succeeded in Version_2.5 ; (which introduced the argument to fix such problems). ; (defstub bar (x) t) ; ; (thm (implies (and (rationalp a)(rationalp b)(rationalp c) ; (<= a b) (<= b a) ; (<= b c) (<= c b)) ; (equal (bar a) (bar c)))) ; End of Historical Comment ; We think we avoid the need for this argument by eliminating all ; substitutions from this function and instead producing the literal ; equalities we deduce. (cond ((null pot-lst) (mv flg cl ttree)) (t (mv-let (ttree1 lhs rhs) (find-equational-poly (car pot-lst) hist) (if (null ttree1) ; no equation was found (process-equational-polys cl hist rcnst type-alist wrld (cdr pot-lst) flg ttree) ; From lhs <= rhs and rhs <= lhs we can actually derive the equality ; of their fixes, (fix lhs) = (fix rhs). We could generate that ; equation and let the system split on the numericity of the two sides ; by conventional opening of fix. But we don't do that, partly for ; cosmetic reasons but mainly because it is possible the two sides ; have been assumed numeric in ttree1 and rather than force a ; premature split, we just use the existing mechanism to cause the ; split later on below, and thus avoid an identical split. ; The derived-equation, below, is used for two purposes: It is ; advertised as the :target of the assumnote we generate to force an ; assumption, and it is possibly added to the clause. (We say "possibly" ; because the equality may be manifest in some sense. See hitp below.) ; The :target of an assumnote is used just in reporting the force. (let ((derived-equation ; orient the equality (cond ((and (variablep lhs) (not (dumb-occur lhs rhs))) (cons-term 'equal (list lhs rhs))) ((and (variablep rhs) (not (dumb-occur rhs lhs))) (cons-term 'equal (list rhs lhs))) (t (cons-term 'equal (list lhs rhs))))) (ok-to-force (ok-to-force rcnst)) (ens (access rewrite-constant rcnst :current-enabled-structure))) (mv-let (flag1 ttree2) (add-linear-assumption derived-equation (mcons-term* 'acl2-numberp lhs) type-alist ens (immediate-forcep nil ens) ok-to-force wrld ttree1) (mv-let (flag2 ttree3) (cond ((eq flag1 :failed) (mv :failed ttree1)) (t (add-linear-assumption derived-equation (mcons-term* 'acl2-numberp rhs) type-alist ens (immediate-forcep nil ens) ok-to-force wrld ttree2))) ; Note lhs and rhs below are bogus if flag2 is :failed; they should not be ; used. Also, note that up through Version_2.9.3, lhs was set to 0 even when ; (acl2-numberp lhs) was indeterminate with forcing off, but now we do not set ; to 0 in that case (flag1 = :failed); similarly for rhs. (let* ((lhs (if (eq flag1 :known-false) *0* lhs)) (rhs (if (eq flag2 :known-false) *0* rhs)) (new-lit (dumb-negate-lit (mcons-term* 'equal lhs rhs))) ; So at this point, if either side is definitely nonnumeric, it has ; defaulted to 0, just as though we generated (equal (fix lhs) (fix ; rhs)) and then opened the corresponding fix to 0. Furthermore, ; ttree3 contains the assumptions that both are numeric (when those ; assumptions are not trivially true or trivially false). In addition ; ttree3 extends ttree1. ; If hitp, below, is true then we will change the cl we are working on. In ; particular, we will NOT change it if either of our numeric assumptions ; :failed or if both lhs and rhs are trivially 0 -- e.g., as would happen if ; one was 0 and the other was known non-numeric. (hitp (not (or (eq flag2 :failed) ; The following case is new after ACL2_2.9.3. The following example was ; provided by Robert Krug, inspired by an example from Dave Greve. Dave didn't ; want a bogus forcing round in such cases (i.e., cases where we don't know ; that at least one side is numeric). ; (thm (implies (and (equal (foo z) (foo y)) ; (equal (foo x) (foo z))) ; (foo (+ x y z)))) (and (eq flag1 :added) (eq flag2 :added)) (and (equal lhs *0*) (equal rhs *0*)) (member-term new-lit cl))))) ; Note: Robert Krug found a soundness bug in an earlier version of ; this code. We used derived-equation instead of (mcons-term* 'equal ; lhs rhs) below. But derived-equation has the original lhs and rhs ; in them, not the FIXed versions! (process-equational-polys (if hitp (add-literal new-lit cl nil) cl) hist rcnst type-alist wrld (cdr pot-lst) ; We got a hit if either we already had a hit or we hit this time. (or flg hitp) (cons-tag-trees (cond (hitp ttree3) (t ; If we do not change the clause, we do not record a dependence on the ; type-set information recorded in ttree3. However, we still record ; ttree1 because it contains the note that prevents us from rederiving ; this same inequality. Recall that ttree3 includes ttree1. ttree1)) ttree))))))))))) ; We are finally ready to begin the final assault on simplify-clause. (defun enumerate-elements (lst i) (cond ((null lst) nil) (t (cons i (enumerate-elements (cdr lst) (1+ i)))))) (defun already-used-by-fertilize-clausep (lit hist get-clause-id) ; We determine whether the literal lit, which is presumably of the form (not ; (equiv lhs rhs)), has already been used by fertilization in the history hist ; of the current clause. If not, then we return nil. Otherwise, we return the ; clause id of that previous use if get-clause-id is true, else we return t. (cond ((null hist) nil) ((and (eq (access history-entry (car hist) :processor) 'fertilize-clause) (tag-tree-occur 'literal lit (access history-entry (car hist) :ttree))) (or get-clause-id (tagged-object 'clause-id (access history-entry (car hist) :ttree)))) (t (already-used-by-fertilize-clausep lit (cdr hist) get-clause-id)))) (defun unhidden-lit-info (hist clause pos wrld) (cond ((null clause) (mv nil nil nil)) (t (let ((lit (car clause))) (case-match lit (('not ('hide (equiv & &))) ; (not (hide (equiv x y))) (cond ((equivalence-relationp equiv wrld) (let* ((new-lit (fcons-term* 'not (fargn (fargn lit 1) 1))) (cl-id (already-used-by-fertilize-clausep new-lit hist nil))) (cond (cl-id (mv pos new-lit cl-id)) (t (unhidden-lit-info hist (cdr clause) (1+ pos) wrld))))) (t (unhidden-lit-info hist (cdr clause) (1+ pos) wrld)))) (& (unhidden-lit-info hist (cdr clause) (1+ pos) wrld))))))) (defun tilde-@-hyp-phrase (len-tail cl) ; This tilde-@ phrase describes a literal of the given clause, cl, as a ; hypothesis in the prettyification of cl, where len-tail is the length of the ; tail of cl following that literal (but if somehow the literal is in cl, this ; function acts like it is the last element). This phrase will, for example, ; complete the sentence "We now use ~@0." One possible completion is "We now ; use the hypothesis." Another is, "We now use the conclusion." A more common ; one is "We now use the third hypothesis." ; Warning: The function fertilize-clause-msg knows that this function ; either refers to the lit as a "conclusion" or as a "hypothesis" and ; can tell which by asking whether the result here is a consp whose ; cdr is nil! So don't change this function without considering that. (let* ((len (length cl)) (n (- len len-tail))) (cond ((int= n len) ; See the warning above. '("the conclusion")) ((and (int= len 2) (int= n 1)) "the hypothesis") (t (msg "the ~n0 hypothesis" (cons n 'th)))))) (defun simplify-clause1 (top-clause hist rcnst wrld state step-limit) ; In Nqthm, this function was called SIMPLIFY-CLAUSE0. ; Top-clause is a clause with history hist. We assume that rcnst has a null pt ; and contains whatever hint settings the user wants in it, as well as the ; :force-info based on whether there is a call of IF in top-clause. ; We return 4 values. The first is a new step-limit. The next indicates ; whether we changed top-clause and, if so, whether we went through the ; rewriter; it will help determine signal returned by simplify-clause (and ; hence will be used, ultimately, to create the 'simplify-clause hist entry). ; If we did not change top-clause, the third is; otherwise, it is a list of new ; clauses whose conjunction implies top-clause. The last argument is a ttree ; that explains what we did, and is used in the 'simplify-clause hist entry. (mv-let (hitp current-clause pts remove-trivial-equivalences-ttree) (remove-trivial-equivalences top-clause nil t (access rewrite-constant rcnst :current-enabled-structure) wrld state nil nil nil) (declare (ignore pts)) (let ((local-rcnst (change rewrite-constant rcnst :top-clause top-clause :current-clause current-clause)) (current-clause-pts (enumerate-elements current-clause 0))) (mv-let (contradictionp type-alist fc-pair-lst) (forward-chain-top 'simplify-clause current-clause current-clause-pts (ok-to-force local-rcnst) nil ; do-not-reconsiderp wrld (access rewrite-constant rcnst :current-enabled-structure) (access rewrite-constant rcnst :oncep-override) state) ; Either we forward chained to a contradiction, in which case we are ; done, or else we have a type-alist assuming the negation of every ; literal in the current-clause and a list of pairs of the form (concl ; . ttree) indicating that each concl can be derived from the parent ; literals indicated by the 'pt tags. (cond (contradictionp ; Note: When forward-chain returns with contradictionp = t, then the ; "fc-pair-lst" is really a ttree. We must add the remove-trivial- ; equivalences ttree to the ttree returned by forward-chain and we must ; remember our earlier case splits. (mv step-limit 'hit nil (cons-tag-trees remove-trivial-equivalences-ttree fc-pair-lst))) (t ; We next construct the initial simplify-clause-pot-lst. ; The construction might prove current-clause, in which case the ; contradictionp answer is non-nil. (sl-let (contradictionp simplify-clause-pot-lst) (pstk (setup-simplify-clause-pot-lst current-clause (pts-to-ttree-lst current-clause-pts) fc-pair-lst type-alist local-rcnst wrld state step-limit)) (cond (contradictionp ; A non-nil contradictionp is a poly meaning linear proved current-clause ; (modulo the assumptions in the tag-tree of the poly). (mv step-limit 'hit nil (cons-tag-trees remove-trivial-equivalences-ttree (push-lemma *fake-rune-for-linear* (access poly contradictionp :ttree))))) (t (mv-let (flg new-current-clause ttree) (pstk (process-equational-polys current-clause hist local-rcnst type-alist wrld simplify-clause-pot-lst nil remove-trivial-equivalences-ttree)) (cond (flg ; Here is where we now perform the substitutions previously done ; within process-equational-polys. See the historical remarks in that ; function. (mv-let (hitp newest-current-clause pts ttree1) (pstk (remove-trivial-equivalences new-current-clause nil t (access rewrite-constant local-rcnst :current-enabled-structure) wrld state ttree nil nil)) (declare (ignore pts hitp)) (mv step-limit 'hit (list newest-current-clause) (push-lemma *fake-rune-for-linear* ttree1)))) (t ; When we call rewrite-clause, below, we pass in as the initial value ; of its ``did we change anything?'' accumulator the flg, hitp, that ; indicates whether remove-trivial-equations changed anything. Thus, ; this call may answer ``yes, something was changed'' when in fact, ; nothing was done by rewrite-clause itself. Note that since we are ; calling rewrite-clause here, we return 'hit-rewrite rather than 'hit ; if we return a non-nil signal; see the comments in simplify-clause. (sl-let (flg ecnt ans ttree) (rewrite-clause current-clause current-clause-pts 1 (initial-gstack 'simplify-clause nil current-clause) nil fc-pair-lst wrld simplify-clause-pot-lst local-rcnst hitp 1 nil remove-trivial-equivalences-ttree *trivial-non-nil-ttree* nil ; splitp state step-limit) (declare (ignore ecnt)) (cond ((null flg) #-acl2-loop-only (dmr-flush t) (mv-let (pos unhidden-lit old-cl-id) (unhidden-lit-info hist top-clause 0 wrld) (cond (pos (let ((tail (nthcdr (1+ pos) top-clause))) (mv step-limit 'hit-rewrite (list (append (take pos top-clause) (cons unhidden-lit tail))) (add-to-tag-tree! 'hyp-phrase (tilde-@-hyp-phrase (len tail) top-clause) (add-to-tag-tree! 'clause-id old-cl-id (push-lemma (fn-rune-nume 'hide nil nil wrld) (rw-cache ttree))))))) (t (mv step-limit nil ans ttree))))) (t #-acl2-loop-only (dmr-flush t) (mv step-limit 'hit-rewrite ans ttree)))))))))))))))) (defun some-element-dumb-occur-lst (lst1 lst2) (cond ((null lst1) nil) ((dumb-occur-lst (car lst1) lst2) t) (t (some-element-dumb-occur-lst (cdr lst1) lst2)))) ; The Spec Vars of Prove -- pspv ; The theorem prover, prove, uses so many special variables that are rarely ; modified that we bundle them up. Because simplify-clause, below, is a ; clause processor in the waterfall, it is the first function in this ; development that is high enough up in the hierarchy to see prove's ; bundle of variables. So it is time to lay out prove's spec vars: (defrec prove-spec-var ; Warning: Keep this in sync with #+acl2-par function ; pspv-equal-except-for-tag-tree-and-pool. ; WARNING: If you change the layout of the prove-spec-var in a way that affects ; the position on :rewrite-constant you must change the guard on the ; definitions of nonlinearp-default-hint in (at least) the following community ; books: ; books/arithmetic-5/lib/basic-ops/default-hint.lisp -- one occurrence ; books/hints/basic-tests.lisp -- two occurrences ((rewrite-constant induction-hyp-terms . induction-concl-terms) (tag-tree hint-settings . tau-completion-alist) (pool . gag-state) user-supplied-term displayed-goal orig-hints . otf-flg) t) ; The orig-hints setting is the list of clause-ids and hint-settings supplied ; to prove. The hint-settings is the particular hint settings for the current ; clause. The only reason we have the orig-hints field is so that we can ; compute the hints appropriate for the top-level goal if we have to abandon ; the initial work and revert to the user-supplied term. To understand the ; need for the tau-completion-alist read mini-essay On the Tau Completion Alist ; (calist) in tau.lisp. (defrec gag-info ; This record corresponds to a key checkpoint, but not necessarily the active ; checkpoint. Suppose for example we see a goal that is hit by an elim before ; any other checkpoint. Then we push an instance of this record on the ; appropriate stack. When a goal is pushed for induction and this record is ; for the active checkpoint, then we add the clause-id and pool-lst for that ; goal. (clause-id ; could be nil clause ; nil iff clause-id is nil . ; list of pairs (clause-id . pool-lst); see above pushed ) t) (defrec gag-state ((top-stack . sub-stack) ; each a list of gag-info records (active-cl-id ; for active key checkpoint if any, else nil . active-printed-p) ; true when active key checkpoint has been printed forcep ; true after next forcing round has been announced . abort-stack) ; top-stack when reverting; 'empty-clause on abort t) (defconst *initial-gag-state* (make gag-state :top-stack nil :sub-stack nil :active-cl-id nil :active-printed-p nil :forcep nil)) (defconst *empty-prove-spec-var* (make prove-spec-var :rewrite-constant nil :induction-hyp-terms nil :induction-concl-terms nil :tag-tree nil :hint-settings nil :tau-completion-alist nil :orig-hints nil :pool nil :gag-state *initial-gag-state* :user-supplied-term *t* :displayed-goal nil :otf-flg nil)) (defun controller-unify-subst2 (vars acc) (cond ((endp vars) acc) ((assoc-eq (car vars) acc) acc) (t (controller-unify-subst2 (cdr vars) (acons (car vars) (car vars) acc))))) (defun controller-unify-subst1 (actuals controllers acc) (cond ((endp actuals) acc) ((car controllers) (controller-unify-subst2 (all-vars (car actuals)) (controller-unify-subst1 (cdr actuals) (cdr controllers) acc))) (t (controller-unify-subst1 (cdr actuals) (cdr controllers) acc)))) (defun controller-unify-subst (name term def-body) (let* ((controller-alist (access def-body def-body :controller-alist)) (controllers (cdr (assoc-eq name controller-alist)))) (cond (controllers (controller-unify-subst1 (fargs term) controllers nil)) (t :none)))) (defun filter-disabled-expand-terms (terms ens wrld) ; We build expand hint structures, throwing certain terms out of terms. ; Variables and constants are kept (but they should never be there). Lambda ; applications are kept. Function symbol applications are kept provided the ; symbol has a non-nil, enabled def-body. There is no point in keeping on ; :expand-lst a term whose function symbol has no def-body, because it is there ; that we go when we decide to force an expansion (from other than ; user-provided :expand hints). ; Note: It is good that HIDE has a body because we allow HIDE terms to be put ; on :expand-lst and we wouldn't want to throw them off. (cond ((null terms) nil) ((or (variablep (car terms)) (fquotep (car terms))) nil) (t (cond ((flambdap (ffn-symb (car terms))) (cons (make expand-hint :pattern (car terms) :alist :none :rune nil :equiv 'equal :hyp nil :lhs (car terms) :rhs (subcor-var (lambda-formals (ffn-symb (car terms))) (fargs (car terms)) (lambda-body (ffn-symb (car terms))))) (filter-disabled-expand-terms (cdr terms) ens wrld))) (t (let* ((term (car terms)) (name (ffn-symb term)) (def-body (def-body name wrld)) (formals (access def-body def-body :formals))) (cond ((and def-body (enabled-numep (access def-body def-body :nume) ens)) (cons (make expand-hint :pattern term :alist ; Starting after Version_5.0, we use a more generous expansion heuristic during ; induction, in which only actuals in the controller positions must match ; exactly the actuals in induction terms; otherwise the latter may be instances ; of the former. With our first attempt at such a change, 8 proofs failed in ; an ACL2(h) regression, not including possible additional proofs that were not ; attempted because of include-book failures. That attempt didn't remove the ; expand hint when applying it, a heuristic discussed in a long comment in ; expand-permission-result. ; We restored that removal heuristic and the number of failures decreased from ; 8 to 5. But one of those failures was pretty nasty, still with the same ; behavior (as judging by output from :gag-mode :goals) and the same prove ; time: MAIN-LEMMA-3 in community book ; books/data-structures/memories/memtree.lisp. The prove time increased from ; 17 seconds for a successful proof to 20 seconds for (both versions of) this ; failure, with a notably different proof as compared to the successful proof ; (Subgoal *1/2' split into 15 subgoals in the failed proof but only generated ; one subgoal in the successful proof). ; So in addition to restoring the removal heuristic, we now limit the ; application of the expand-hint to instances for which each variable is bound ; either to itself or to a constant (a quotep). That is probably the common ; case in which users had supplied :expand hints because of the formerly weaker ; expand-hint created by the system, say because some non-controller argument ; in the pattern had simplified to 0 or nil. (cons :constants (controller-unify-subst name term def-body)) :rune (access def-body def-body :rune) :equiv 'equal :hyp (access def-body def-body :hyp) :lhs (cons-term name formals) :rhs (access def-body def-body :concl)) (filter-disabled-expand-terms (cdr terms) ens wrld))) (t (filter-disabled-expand-terms (cdr terms) ens wrld))))))))) (defun found-hit-rewrite-hist-entry (hist) ; Hist is a list of history-entry records. We search it to find a history ; entry with 'hit-rewrite or 'hit-rewrite2 signal. Note that if we ; find 'hit-rewrite, we know that no previous entry (i.e., later in ; hist when viewed as a list) has signal 'hit-rewrite2, due to the way ; we return signals in simplify-clause. (if (endp hist) nil (or (car (member-eq (access history-entry (car hist) :signal) '(hit-rewrite hit-rewrite2))) (found-hit-rewrite-hist-entry (cdr hist))))) (defabbrev append? (x y) (cond ((null y) x) (t (append x y)))) (defun simplify-clause (cl hist pspv wrld state step-limit) ; Warning: Keep this in sync with function simplify-clause-rcnst defined in ; community book books/misc/computed-hint-rewrite.lisp. ; This is a "clause processor" of the waterfall. Its input and output spec is ; consistent with that of all such processors. See the waterfall for a general ; discussion. ; Cl is a clause with history hist. We can obtain a rewrite-constant from the ; prove spec var pspv. We assume nothing about the rewrite-constant except ; that it has the user's hint settings in it and that the pt is nil. We ; install our top-clause and current-clause when necessary. ; We return five values. The first is a new step-limit. The second is a ; signal that in general is 'miss, 'abort, 'error, or a "hit". In this case, ; it is always either 'miss or one of 'hit, 'hit-rewrite, or 'hit-rewrite2 (as ; described further below). When the signal is 'hit, the third result is the ; list of new clauses, the fourth is a ttree that will become that component of ; the history-entry for this simplification, and the fifth is the unmodified ; pspv. (We return the fifth thing to adhere to the convention used by all ; clause processors in the waterfall (q.v.).) When the signal is 'miss, the ; third and fifth results are irrelevant, but we return a ttree whose rw-cache ; may extend the ttree of the input pspv. ; If the second result is a "hit" then the conjunction of the new clauses ; returned implies cl. Equivalently, under the assumption that cl is false, cl ; is equivalent to the conjunction of the new clauses. ; On Tail Biting by Simplify-clause: ; Tail biting can occur at the simplify-clause level, i.e., we can return a set ; of clauses that is a generalization of the clause cl, e.g., a set whose ; conjunction is false even though cl is not. This is because of the way we ; manage the simplify-clause-pot-lst and pts. We build a single pot-lst and ; use parent trees to render inactive those polys that we wish to avoid. To ; arrange to bite our own tail, put two slightly different versions of the same ; inequality literal into cl. The poly arising from the second can be used to ; rewrite the first and the poly arising from first can be used to rewrite the ; second. If the first rewrites to false immediately our use of parent trees ; (as arranged by passing local-rcnst to the recursive call of rewrite-clause ; in rewrite-clause) will wisely prevent the use of its poly while rewriting ; the second. But if the first rewrites to some non-linear term (which will be ; rewritten to false later) then we'll permit ourselves to use the first's poly ; while working on the second and we could bite our tail. ; This would not happen if we produced a new linear pot-lst for each literal -- ; a pot-lst in which the literal to be rewritten was not assumed false. Early ; experiments with that approach led us to conclude it was too expensive. ; If the specification of rewrite is correct, then tail biting cannot happen ; except via the involvement of linear arithmetic. To see this, consider the ; assumptions governing the rewriting of each literal in the clause and ask ; whether the literal being rewritten in in rewrite-clause is assumed false via ; any of those assumptions. There are five sources of assumptions in the ; specification of rewrite: (a) the type-alist (which is constructed so as to ; avoid that literal), (b) the assumptions in ancestors (which is initially ; empty), (c) the assumptions in the pot-lst (which we are excepting), and (d) ; 'assumptions in ttree (which we are excepting). Thus, the only place that ; assumption might be found is simplify-clause-pot-lst. If linear is ; eliminated, the only assumptions left are free of the literal being worked ; on. ; This is really an interface function between the rewriter and the rest of the ; prover. It has three jobs. ; The first is to convert from the world of pspv to the world of rcnst. That ; is, from the package of spec vars passed around in the waterfall to the ; package of constants known to the rewriter. ; The second job of this function is to control the expansion of the ; induction-hyp-terms and induction-concl terms (preventing the expansion of ; the former and forcing the expansion of the latter) by possibly adding them ; to terms-to-be-ignored-by-rewrite and expand-lst, respectively. This is done ; as part of the conversion from pspv (where induction hyps and concl are ; found) to rcnst (where terms-to-be-ignored-by-rewrite and expand-lst are ; found). They are so controlled as long as we are in the first simplification ; stages after induction. As soon as the clause has gone through the rewriter ; with some change, with input free of induction-concl-terms, we stop ; interfering. The real work horse of clause level simplification is ; simplify-clause1. ; The third job is to convert the simplify-clause1 answers into the kind ; required by a clause processor in the waterfall. The work horse doesn't ; return an pspv and we do. (prog2$ (initialize-brr-stack state) (cond ((assoc-eq 'settled-down-clause hist) ; The clause has settled down under rewriting with the induction-hyp-terms ; initially ignored and the induction-concl-terms forcibly expanded. In ; general, we now want to stop treating those terms specially and continue ; simplifying. However, there is a special case that will save a little time. ; Suppose that the clause just settled down -- i.e., the most recent hist entry ; is the one we just found. And suppose that none of the specially treated ; terms occurs in the clause we're to simplify. Then we needn't simplify it ; again. (cond ((and (eq 'settled-down-clause (access history-entry (car hist) :processor)) ; If the rw-cache-state is :disabled immediately after a hit from ; settled-down-clause, then we want to do the work of making a "desperation" ; attempt at simplification. (not (eq (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :rw-cache-state) :disabled)) (not (some-element-dumb-occur-lst (access prove-spec-var pspv :induction-hyp-terms) cl))) ; Since we know the induction-concl-terms couldn't occur in the clause -- they ; would have been expanded -- we are done. (Or at least: if they do occur in ; the clause, then still, removing them from the expand-lst should not help the ; rewriter!) This test should speed up base cases and preinduction ; simplification at least. (mv step-limit 'miss nil nil nil)) (t ; We now cease interfering and let the heuristics do their job. (sl-let (changedp clauses ttree) (simplify-clause1 cl hist (change rewrite-constant (access prove-spec-var pspv :rewrite-constant) :force-info (if (ffnnamep-lst 'if cl) 'weak t)) wrld state step-limit) (cond (changedp ; Note: It is possible that our input, cl, is a member-equal of our output, ; clauses! Such simplifications are said to be "specious." But we do not ; bother to detect that here because we want to report the specious ; simplification as though everything were ok and then pretend nothing ; happened. This gives the user some indication of where the loop is. In the ; old days, we just signalled a 'miss if (member-equal cl clauses) and that ; caused a lot of confusion among experienced users, who saw simplifiable ; clauses being passed on to elim, etc. See :DOC specious-simplification. (mv step-limit 'hit clauses ttree pspv)) (t (mv step-limit 'miss nil ttree nil))))))) (t ; The clause has not settled down yet. So we arrange to ignore the ; induction-hyp-terms when appropriate, and to expand the induction-concl-terms ; without question. The local-rcnst created below is not passed out of this ; function. (let* ((rcnst (access prove-spec-var pspv :rewrite-constant)) (new-force-info (if (ffnnamep-lst 'if cl) 'weak t)) (induction-concl-terms (access prove-spec-var pspv :induction-concl-terms)) (hist-entry-hit (found-hit-rewrite-hist-entry hist)) (hit-rewrite2 (or (eq hist-entry-hit 'hit-rewrite2) (and (eq hist-entry-hit 'hit-rewrite) (not (some-element-dumb-occur-lst induction-concl-terms cl))))) ; We arrange to expand the induction-concl-terms and ignore the ; induction-hyp-terms unless hit-rewrite2 above is set. (local-rcnst (cond (hit-rewrite2 ; We have previously passed through the rewriter, and either a predecessor goal ; or this one is free of induction-concl-terms. In that case we stop meddling ; with the rewriter by inhibiting rewriting of induction-hyp-terms and forcing ; expansion of induction-concl-terms. Before Version_2.8 we waited until ; 'settled-down-clause before ceasing our meddling. However, Dave Greve and ; Matt Wilding reported an example in which that heuristic slowed down the ; prover substantially by needlessly delaying the rewriting of ; induction-hyp-terms. Notice that this heuristic nicely matches the induction ; heuristics: both consider only the goal, not the result of rewriting it. (We ; however ignore rewriting by preprocess-clause for the present purpose: we ; want to wait for a full-blown rewrite before allowing rewriting of ; induction-hyp-terms.) ; Initially we attempted to fix the slowdown mentioned above (the one reported ; by Greve and Wilding) by eliminating completely the special treatment of ; induction-hyp-terms. However, lemma psuedo-termp-binary-op_tree in community ; book books/meta/pseudo-termp-lemmas.lisp showed the folly of this attempt. ; The relevant goal was as follows. ; Subgoal *1/5' ; (IMPLIES (AND (CONSP L) ; (CONSP (CDR L)) ; (CONSP (CDDR L)) ; (PSEUDO-TERMP (BINARY-OP_TREE BINARY-OP-NAME ; CONSTANT-NAME FIX-NAME (CDR L))) ; (PSEUDO-TERM-LISTP L) ; (SYMBOLP BINARY-OP-NAME) ; (SYMBOLP FIX-NAME) ; (NOT (EQUAL BINARY-OP-NAME 'QUOTE)) ; (NOT (CONSP CONSTANT-NAME))) ; (PSEUDO-TERMP (BINARY-OP_TREE BINARY-OP-NAME ; CONSTANT-NAME FIX-NAME L))) ; In this case induction-hyp-terms contained the single term (binary-op_tree ; binary-op-name constant-name fix-name (cdr l)). Without special treatment of ; induction-hyp-terms, the above binary-op_tree term was rewritten, and hence ; so was the pseudo-termp hypothesis. The result seemed to give permission to ; the next hypothesis, (pseudo-term-listp l), to be rewritten much more ; agressively than it was formerly, which bogged down the rewriter (perhaps ; even in an infinite loop). ; A later attempt used the simple algorithm that we stop meddling once we have ; made a pass through the rewriter, even if there are still ; induction-concl-terms around. Lemma flip-eq-subst-commute in community book ; books/workshops/1999/ivy/ivy-v2/ivy-sources/flip.lisp showed the problem with ; that approach. Subgoal *1/2' below was produced by preprocess-clause. It ; produces goal Subgoal *1/2.16, which has a new occurrence in the conclusion ; of the induction-concl-term (SUBST-FREE F X TM): ; Subgoal *1/2' ; (IMPLIES (AND (NOT (WFNOT F)) ; (CONSP F) ; (CONSP (CDR F)) ; (CONSP (CDDR F)) ; (NOT (CDDDR F)) ; (OR (EQUAL (CAR F) 'AND) ; (EQUAL (CAR F) 'OR) ; (EQUAL (CAR F) 'IMP) ; (EQUAL (CAR F) 'IFF)) ; (EQUAL (SUBST-FREE (FLIP-EQ (CADR F) (CDR POS)) ; X TM) ; (FLIP-EQ (SUBST-FREE (CADR F) X TM) ; (CDR POS))) ; (EQUAL (SUBST-FREE (FLIP-EQ (CADDR F) (CDR POS)) ; X TM) ; (FLIP-EQ (SUBST-FREE (CADDR F) X TM) ; (CDR POS)))) ; (EQUAL (SUBST-FREE (FLIP-EQ F POS) X TM) ; (FLIP-EQ (SUBST-FREE F X TM) POS))). ; ; This simplifies, using the :definitions FLIP-EQ, LEN, LIST2P, LIST3P, ; SUBST-FREE, TRUE-LISTP, WFBINARY, WFEQ and WFNOT, the :executable- ; counterparts of BINARY-+, EQUAL, LEN and TRUE-LISTP, primitive type ; reasoning and the :rewrite rules CAR-CONS and CDR-CONS, to the following ; 16 conjectures. ; ; Subgoal *1/2.16 ; (IMPLIES (AND (CONSP F) ; (CONSP (CDR F)) ; (CONSP (CDDR F)) ; (NOT (CDDDR F)) ; (EQUAL (CAR F) 'AND) ; (EQUAL (SUBST-FREE (FLIP-EQ (CADR F) (CDR POS)) ; X TM) ; (FLIP-EQ (SUBST-FREE (CADR F) X TM) ; (CDR POS))) ; (EQUAL (SUBST-FREE (FLIP-EQ (CADDR F) (CDR POS)) ; X TM) ; (FLIP-EQ (SUBST-FREE (CADDR F) X TM) ; (CDR POS))) ; (NOT (CONSP POS))) ; (EQUAL (SUBST-FREE F X TM) ; (LIST 'AND ; (SUBST-FREE (CADR F) X TM) ; (SUBST-FREE (CADDR F) X TM)))) ; If we stop meddling after Subgoal *1/2', then hypothesis rewriting in Subgoal ; *1/2.16 is so severe that we are subject to case-split-limitations and never ; reach the conclusion! If memory serves, an attempt to turn off ; case-split-limitations just led the prover off the deep end. (change rewrite-constant rcnst :force-info new-force-info ; We also tried a modification in which we use the same :expand-lst as below, ; thus continuing to meddle with induction-concl-terms even after we are done ; meddling with induction-hyp-terms. However, that caused problems with, for ; example, the proof of exponents-add-1 in community book ; books/arithmetic-2/pass1/expt-helper.lisp. Apparently the forced expansion ; of (EXPT R J) looped with rule exponents-add-2 (rewriting r^(i+j)). At any ; rate, it seems reasonable enough to keep suppression of induction-hyp-terms ; rewriting in sync with forced expansion of induction-concl-terms. ; And we tried one more idea: removing the test on whether the clause had been ; rewritten. We got one failure, on collect-times-3b in v2-8 in community book ; books/arithmetic-2/meta/common-meta.lisp. ; What happens in the proof attempt is that the induction-concl-terms have been ; eliminated in Subgoal *1/7''. If we check for rewriting, no further meddling ; occurs, and the next subgoal (Subgoal *1/7''') is pushed for proof by ; induction. That's what we want in this case. ; But if we don't check for rewriting then when the induction-concl-term (EXPT ; X J) surprisingly reappears in Subgoal *1/7''', we again expand it. It ; continues to appear in every other goal, causing a loop. ; Now, the suggestion was not to check whether the goal was rewritten, and ; we've given that one interpretation. Another interpretation is to record in ; the history the first time we see a disappearance of induction-concl-terms, ; so that we never again try to expand them (or ignore induction-hyp-terms). ; But it seems that the natural way to record such information still involves ; saving extra information (e.g., the signal) in a history entry. So even ; though it may be redundant, we might as well check that we've done some ; rewriting. That way we don't have to rely on the immediate appearance of ; induction-concl-terms, and yet we are still guaranteed at least one pass ; through the rewriter before stopping the "meddling". )) (t (change rewrite-constant rcnst :force-info new-force-info :terms-to-be-ignored-by-rewrite (append (access prove-spec-var pspv :induction-hyp-terms) (access rewrite-constant rcnst :terms-to-be-ignored-by-rewrite)) :expand-lst (append? (access rewrite-constant rcnst :expand-lst) ; We give the user's expand-lst priority, in case it specifies :with for a term ; that is also an enabled call in induction-concl-terms. (filter-disabled-expand-terms induction-concl-terms (access rewrite-constant rcnst :current-enabled-structure) wrld))))))) (sl-let (hitp clauses ttree) (simplify-clause1 cl hist local-rcnst wrld state step-limit) (cond (hitp (mv step-limit (if hit-rewrite2 'hit-rewrite2 hitp) clauses ttree pspv)) (t (mv step-limit 'miss nil ttree nil))))))))) ; Inside the waterfall, the following clause processor immediately follows ; simplify-clause. (defun settled-down-clause (clause hist pspv wrld state) ; This is the processor in the waterfall just after simplify-clause. ; Its presence in the waterfall causes us to add a ; 'settled-down-clause hist-entry to the history of the clause when ; simplify-clause finishes with it. The "hit state" of the waterfall ; leads back to the simplifier, where the code above detects this ; settling down and turns off the handling of the induction hyp and ; concl terms. The "miss state" of the waterfall leads to the next ; processor. ; Note: There has been some consideration given to the question ; ``should this function claim a 'hit after SPECIOUS ; simplify-clauses?'' We say ``yes'' in the comment in ; previous-process-was-speciousp. (declare (ignore wrld state)) (cond ((assoc-eq 'settled-down-clause hist) (mv 'miss nil nil nil)) (t (mv 'hit (list clause) nil pspv)))) ; We now develop the functions for reporting on the output of simplify. (defun member-class-name-runes (class name runes) (cond ((endp runes) nil) ((let ((rune (car runes))) (and (eq (car rune) class) (eq (base-symbol rune) name))) t) (t (member-class-name-runes class name (cdr runes))))) (defun extract-and-classify-lemmas2 (names class ignore-lst if-intro case-split immed-forced forced-runes) (cond ((endp names) nil) ((member-eq (car names) ignore-lst) (extract-and-classify-lemmas2 (cdr names) class ignore-lst if-intro case-split immed-forced forced-runes)) (t (let ((name (car names))) (acons name (append (if (member-class-name-runes class name if-intro) '("if-intro") nil) (if (member-class-name-runes class name case-split) '("case-split") nil) (if (member-class-name-runes class name immed-forced) '("immed-forced") nil) (if (member-class-name-runes class name forced-runes) '("forced") nil)) (extract-and-classify-lemmas2 (cdr names) class ignore-lst if-intro case-split immed-forced forced-runes)))))) (defun extract-and-classify-lemmas1 (class-alist ignore-lst if-intro case-split immed-forced forced-runes) (cond ((endp class-alist) nil) (t (let* ((class (caar class-alist)) (symbol-alist (extract-and-classify-lemmas2 (cdar class-alist) ; names class ignore-lst if-intro case-split immed-forced forced-runes)) (rest (extract-and-classify-lemmas1 (cdr class-alist) ignore-lst if-intro case-split immed-forced forced-runes))) (cond (symbol-alist (acons class symbol-alist rest)) (t rest)))))) (defun runes-to-class-alist1 (runes alist) (cond ((endp runes) alist) (t (let* ((rune (car runes)) (type (car rune)) (sym (base-symbol rune)) (old (cdr (assoc-eq type alist)))) (runes-to-class-alist1 (cdr runes) (put-assoc-eq type (cons sym old) alist)))))) ; We admit the following sorting functions in :logic mode, verify their guards, ; and prove properties of them in community book books/misc/sort-symbols.lisp. (defun strict-merge-symbol-< (l1 l2 acc) ; If l1 and l2 are strictly ordered by symbol-< and above acc, which is also ; thus strictly ordered, then the result is strictly ordered by symbol-<. (declare (xargs :guard (and (symbol-listp l1) (symbol-listp l2) (true-listp acc)) ; We admit this to the logic and prove termination in community book ; books/misc/sort-symbols.lisp. :mode :program)) (cond ((endp l1) (revappend acc l2)) ((endp l2) (revappend acc l1)) ((eq (car l1) (car l2)) (strict-merge-symbol-< (cdr l1) (cdr l2) (cons (car l1) acc))) ((symbol-< (car l1) (car l2)) (strict-merge-symbol-< (cdr l1) l2 (cons (car l1) acc))) (t (strict-merge-symbol-< l1 (cdr l2) (cons (car l2) acc))))) (defun strict-merge-sort-symbol-< (l) ; Produces a result with the same elements as the list l of symbols, but ; strictly ordered by symbol-name. (declare (xargs :guard (symbol-listp l) ; We admit this to the logic and prove termination in community book ; books/misc/sort-symbols.lisp. :mode :program)) (cond ((endp (cdr l)) l) (t (strict-merge-symbol-< (strict-merge-sort-symbol-< (evens l)) (strict-merge-sort-symbol-< (odds l)) nil)))) (defun strict-symbol-<-sortedp (x) (declare (xargs :guard (symbol-listp x))) (cond ((or (endp x) (null (cdr x))) t) (t (and (symbol-< (car x) (cadr x)) (strict-symbol-<-sortedp (cdr x)))))) (defun sort-symbol-listp (x) (declare (xargs :guard (symbol-listp x))) (cond ((strict-symbol-<-sortedp x) x) (t (strict-merge-sort-symbol-< x)))) (defun strict-merge-sort-symbol-<-cdrs (alist) (cond ((endp alist) nil) (t (acons (caar alist) (strict-merge-sort-symbol-< (cdar alist)) (strict-merge-sort-symbol-<-cdrs (cdr alist)))))) (defun runes-to-class-alist (runes) (strict-merge-sort-symbol-<-cdrs (runes-to-class-alist1 runes (pairlis$ (strict-merge-sort-symbol-< (strip-cars runes)) nil)))) (defun extract-and-classify-lemmas (ttree ignore-lst forced-runes) ; We essentially partition the set of runes tagged as 'lemmas in ttree into ; partitions based on the type (i.e., the keyword token) for each rune. In ; addition, we indicate whether the rune was applied as a splitter rune, and if ; so, of which types. In our partitioning we actually throw away the runes and ; just report the corresponding base symbols. ; In particular, scan ttree for all the 'lemma tags and return an alist ; associating each type of rune used in the ttree with an alist associating ; runes with types of splitters, except that we ignore runes whose whose base ; symbols are in ignore-lst. ; An example alist returned is ; '((:DEFINITION (APP) (REV FORCED)) ; (:REWRITE (LEMMA1) (LEMMA2 IF-INTRO FORCED) (LEMMA3 CASE-SPLIT)) ; (:TYPE-PRESCRIPTION (FN1 FORCED) (FN2 FORCED) (FN3))) ; indicating that three :REWRITE runes were used, with base symbols ; LEMMA1, LEMMA2 (which was forced and also introduced a call of IF), and ; LEMMA3, etc. ; The alist is sorted by car. Each value is itself an alist that is itself ; sorted by car. (extract-and-classify-lemmas1 (runes-to-class-alist (tagged-objects 'lemma ttree)) ignore-lst (tagged-objects 'splitter-if-intro ttree) (tagged-objects 'splitter-case-split ttree) (tagged-objects 'splitter-immed-forced ttree) forced-runes)) (deflabel Simple :doc ":Doc-Section Miscellaneous ~c[:]~ilc[definition] and ~c[:]~ilc[rewrite] rules used in preprocessing~/ ~bv[] Example of simple rewrite rule: (equal (car (cons x y)) x) Examples of simple definition: (defun file-clock-p (x) (integerp x)) (defun naturalp (x) (and (integerp x) (>= x 0))) ~ev[]~/ The theorem prover output sometimes refers to ``simple'' definitions and rewrite rules. These rules can be used by the preprocessor, which is one of the theorem prover's ``processes'' understood by the ~c[:do-not] hint; ~pl[hints]. The preprocessor expands certain definitions and uses certain rewrite rules that it considers to be ``fast''. There are two ways to qualify as fast. One is to be an ``abbreviation'', where a rewrite rule with no hypotheses or loop stopper is an ``abbreviation'' if the right side contains no more variable occurrences than the left side, and the right side does not call the functions ~ilc[if], ~ilc[not] or ~ilc[implies]. Definitions and rewrite rules can both be abbreviations; the criterion for definitions is similar, except that the definition must not be recursive. The other way to qualify applies only to a non-recursive definition, and applies when its body is a disjunction or conjunction, according to a perhaps subtle criterion that is intended to avoid case splits.") (defun tilde-*-conjunction-of-possibly-forced-names-phrase1 (alist) (cond ((null alist) nil) (t (cons (let ((name (caar alist)) (splitter-types (cdar alist))) (cond ((null splitter-types) (msg "~x0" name)) (t (msg "~x0 (~*1)" name (list "" "~s*" "~s*," "~s*," splitter-types))))) (tilde-*-conjunction-of-possibly-forced-names-phrase1 (cdr alist)))))) (defun tilde-*-conjunction-of-possibly-forced-names-phrase (lst) ; Lst is a list of pairs of the form (flg . name). We build a tilde-* phrase ; that will print a conjoined list of names, with the parenthetical remark "forced" ; occurring just after those with flg t. ; For example, if lst is ; ((NIL . APP) (T . REV) (NIL . LEN) (T . MEM) (T . SQ)) ; and the output of this function is bound to the fmt variable ; #\D, then ~*D prints "APP, REV (forced), LEN, MEM (forced) and SQ ; (forced)". (list "" "~@*" "~@* and " "~@*, " (tilde-*-conjunction-of-possibly-forced-names-phrase1 lst))) (defconst *fake-rune-alist* ; We use this constant for dealing with fake runes in tag-trees. We ignore ; *fake-rune-for-anonymous-enabled-rule*, because push-lemma is careful not to ; put it into any tag-trees. (list (cons (car *fake-rune-for-linear*) "linear arithmetic") (cons (car *fake-rune-for-type-set*) "primitive type reasoning") (cons (car *fake-rune-for-nu-rewriter*) "the nu-rewriter"))) (defun rune-< (x y) (cond ((eq (car x) (car y)) (or (symbol-< (cadr x) (cadr y)) (and (eq (cadr x) (cadr y)) (cond ((null (cddr x)) (cddr y)) ((null (cddr y)) nil) (t (< (cddr x) (cddr y))))))) ((symbol-< (car x) (car y)) t) (t nil))) (defun merge-runes (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((rune-< (car l1) (car l2)) (cons (car l1) (merge-runes (cdr l1) l2))) (t (cons (car l2) (merge-runes l1 (cdr l2)))))) (defun merge-sort-runes (l) (cond ((null (cdr l)) l) (t (merge-runes (merge-sort-runes (evens l)) (merge-sort-runes (odds l)))))) (defun tilde-*-simp-phrase1 (alist abbreviations-flg) (cond ((null alist) (mv nil nil)) (t (let ((pair (assoc-eq (caar alist) *fake-rune-alist*))) (cond (pair (mv-let (rest-msgs rest-pairs) (tilde-*-simp-phrase1 (cdr alist) abbreviations-flg) (mv (cons (cdr pair) rest-msgs) rest-pairs))) (t (let ((names (tilde-*-conjunction-of-possibly-forced-names-phrase (cdar alist))) ; Note: Names is a tilde-* object that will print a conjoined list of names ; (possibly followed by parenthetical remarks for splitters). We must ; determine whether there is more than one name in the list. The names printe ; are just those in (cdar alist), which we know is a non-empty true list of ; pairs. Below we set pluralp to t if two or more names will be printed and to ; nil if exactly one name will be printed. (pluralp (if (cdr (cdar alist)) t nil))) (mv-let (msg pair) (case (caar alist) (:DEFINITION (mv (if abbreviations-flg (if pluralp "the simple :definitions ~*D" "the simple :definition ~*D") (if pluralp "the :definitions ~*D" "the :definition ~*D")) (cons #\D names))) (:EXECUTABLE-COUNTERPART (mv (if pluralp "the :executable-counterparts of ~*X" "the :executable-counterpart of ~*X") (cons #\X names))) (:REWRITE (mv (if abbreviations-flg (if pluralp "the simple :rewrite rules ~*R" "the simple :rewrite rule ~*R") (if pluralp "the :rewrite rules ~*R" "the :rewrite rule ~*R")) (cons #\R names))) (:LINEAR (mv (if pluralp "the :linear rules ~*L" "the :linear rule ~*L") (cons #\L names))) (:BUILT-IN-CLAUSE (mv (if pluralp "the :built-in-clause rules ~*B" "the :built-in-clause rule ~*B") (cons #\B names))) (:COMPOUND-RECOGNIZER (mv (if pluralp "the :compound-recognizer rules ~*C" "the :compound-recognizer rule ~*C") (cons #\C names))) ; We do not expect the following three cases to arise in the ; simplifier, but this function finds wider use. (:ELIM (mv (if pluralp "the :elim rules ~*e" "the :elim rule ~*e") (cons #\e names))) (:GENERALIZE (mv (if pluralp "the :generalize rules ~*G" "the :generalize rule ~*G") (cons #\G names))) (:INDUCTION (mv (if pluralp "the :induction rules ~*I" "the :induction rule ~*I") (cons #\I names))) (:META (mv (if pluralp "the :meta rules ~*M" "the :meta rule ~*M") (cons #\M names))) (:FORWARD-CHAINING (mv (if pluralp "the :forward-chaining rules ~*F" "the :forward-chaining rule ~*F") (cons #\F names))) (:EQUIVALENCE (mv (if pluralp "the :equivalence rules ~*E" "the :equivalence rule ~*E") (cons #\E names))) (:REFINEMENT (mv (if pluralp "the :refinement rules ~*r" "the :refinement rule ~*r") (cons #\r names))) (:CONGRUENCE (mv (if pluralp "the :congruence rules ~*c" "the :congruence rule ~*c") (cons #\c names))) (:TYPE-PRESCRIPTION (mv (if pluralp "the :type-prescription rules ~*T" "the :type-prescription rule ~*T") (cons #\T names))) (:TYPE-SET-INVERTER (mv (if pluralp "the :type-set-inverter rules ~*t" "the :type-set-inverter rule ~*t") (cons #\t names))) (otherwise (mv (er hard 'tilde-*-simp-phrase1 "We did not expect to see the simplifier report a rune ~ of type ~x0." (caar alist)) nil))) (mv-let (rest-msgs rest-pairs) (tilde-*-simp-phrase1 (cdr alist) abbreviations-flg) (mv (cons msg rest-msgs) (cons pair rest-pairs))))))))))) (defun tilde-*-raw-simp-phrase1 (runes forced-runes punct ignore-lst phrase acc) (cond ((null runes) (cond ((null acc) (mv nil (list (cons #\p (if phrase (msg " " phrase) ""))))) (t (mv (list (concatenate 'string "~@Fthe list of runes,~|~% ~YRe" (case punct (#\, ",~|~%") (#\. ".~|") (otherwise "~|")) "~@p")) (list (cons #\F (if forced-runes (msg "(forcing with ~&0) " forced-runes) "")) (cons #\p (if phrase (msg "~@0~|" phrase) "")) (cons #\R (merge-sort-runes (reverse acc))) (cons #\e nil)))))) (t (let ((pair (assoc-eq (caar runes) *fake-rune-alist*))) (cond (pair (mv-let (rest-msgs rest-pairs) (tilde-*-raw-simp-phrase1 (cdr runes) forced-runes punct ignore-lst phrase acc) (mv (cons (if (null rest-msgs) (concatenate 'string (cdr pair) (case punct (#\, ",") (#\. ".") (otherwise ""))) (cdr pair)) rest-msgs) rest-pairs))) (t (tilde-*-raw-simp-phrase1 (cdr runes) forced-runes punct ignore-lst phrase (if (member-eq (base-symbol (car runes)) ignore-lst) acc (cons (car runes) acc))))))))) (defun recover-forced-runes1 (recs ans) (cond ((endp recs) ans) (t (recover-forced-runes1 (cdr recs) (let ((rune (access assumnote (car (access assumption (car recs) :assumnotes)) :rune))) (cond ((not (symbolp rune)) (add-to-set-equal rune ans)) (t ans))))))) (defun recover-forced-runes (ttree) ; Every assumption in ttree has exactly one assumnote. Let the ":rune" of the ; assumption be the :rune field of the car of its assumnotes. ; We scan the tag-tree ttree for all occurrences of the 'assumption tag and ; collect into ans the :rune of each assumption, when the :rune is a rune. We ; ignore the symbolp :runes because we will be searching the resulting list for ; genuine runes and thus need not clutter it with symbols. (recover-forced-runes1 (tagged-objects 'assumption ttree) nil)) (defun tilde-*-raw-simp-phrase (ttree punct phrase) ; See tilde-*-simp-phrase. But here, we print as specified by value :raw for ; state global 'raw-proof-format. We supply the concluding punctuation msg, ; punct. (let ((forced-runes (recover-forced-runes ttree))) (let ((runes (all-runes-in-ttree ttree nil))) (mv-let (message-lst char-alist) (tilde-*-raw-simp-phrase1 runes forced-runes punct nil phrase nil) (list* (concatenate 'string "trivial ob~-ser~-va~-tions" (case punct (#\, ",") (#\. ".") (otherwise ""))) "~@*" "~@* and " "~@*, " message-lst char-alist))))) (defun tilde-*-simp-phrase (ttree) ; This function determines from ttree whether linear arithmetic and/or ; primitive type reasoning was used and what lemmas and function symbols were ; used. Then it constructs and returns a tuple suitable for giving to the ~* ; fmt directive. I.e., if you fmt the string "The proof depends upon ~*S." ; and #\S is bound to the output of this function, then you will get something ; like: ; v ; The proof depends upon linear arithmetic, the lemma ASSOC-OF-APP ; (forced), and the definitions of APP (forced) and REVERSE. ; ^ ; Note that the msg actually starts at the v above and stops at the ^. ; I.e., no space will be printed at the beginning, and no space or ; punctuation will be printed at the end. ; Note: Several functions know that if (nth 4 output) is nil, where ; output is the result of this function, then essentially nothing was ; done (i.e., "trivial observations" would be printed). (let ((forced-runes (recover-forced-runes ttree))) (mv-let (message-lst char-alist) (tilde-*-simp-phrase1 (extract-and-classify-lemmas ttree nil forced-runes) nil) (list* "trivial ob~-ser~-va~-tions" "~@*" "~@* and " "~@*, " message-lst char-alist)))) (defun tilde-@-pool-name-phrase (forcing-round pool-lst) ; We use this function to create the printed representation from the ; forcing-round and pool-lst. This function actually has two uses. First, ; pool-names are used within a single round to refer to local goals, such as ; when we say "Name the formula above *1." or, more suggestively "Name the ; formula above [2]*1.3.4." In such use, the forcing round is placed just ; before the star, in square brackets. But pool-names also play a role in ; "clause ids" such as [2]Subgoal *1.3.4/1.1'''. Observe that in clause ids ; the pool-name is printed here ^^^^^^ but the forcing-round is ; not printed in the normal place but before the word "Subgoal." Basically, ; the forcing-round is always leading. Thus, we need two versions of this ; function, one that puts the forcing-round in and another that does not. ; Confusingly but conveniently, if the forcing round is 0, we do not display it ; and so the two uses of this function -- to generate stand-alone pool names ; and to generate parts of clause ids -- appear somewhat hidden. But you will ; find calls of this function where the forcing-round supplied is 0 -- ; signallying that we want a pool name to use within a clause id -- even though ; the actual forcing-round at the time of call is non-0. (cond ((= forcing-round 0) ; Notes: ; 1. This asterisk is the one that appears in the printed name. ; 2. If you wanted trailing punctuation, you could put it before ; this close double gritch. ; 3. These two dots are the ones that separate numbers in the name. ; 1 2 3 3 ; ! ! ! ! (cons "*~*0" (list (cons #\0 (list "" "~x*" "~x*." "~x*." pool-lst))))) (t (cons "[~xr]*~*0" (list (cons #\r forcing-round) (cons #\0 (list "" "~x*" "~x*." "~x*." pool-lst))))))) (defun tilde-@-pool-name-phrase-lst (forcing-round lst) (cond ((null lst) nil) (t (cons (tilde-@-pool-name-phrase forcing-round (car lst)) (tilde-@-pool-name-phrase-lst forcing-round (cdr lst)))))) (defun tilde-@-clause-id-phrase (id) ; Warning: Keep this in sync with string-for-tilde-@-clause-id-phrase (and its ; subfunctions). ; Id is a clause-id. This function builds a tilde-@ object that when printed ; will display the clause id in its external form. ; Warning: If this function is changed so as to print differently, change the ; associated parser, parse-clause-id. Also change the clone of ; this function, string-for-tilde-@-clause-id-phrase. ; For example, if id is ; (make clause-id ; :forcing-round 3 ; :pool-lst '(2 1) ; :case-lst '(5 7 9 11) ; :primes 3) ; then the result of a tilde-@ on the returned object will be: ; [3]Subgoal *2.1/5.7.9.11''' ; The parser noted above will parse "[3]Subgoal *2.1/5.7.9.11'''" into the ; clause-id above. Will wonders never cease? Boyer and Moore wrote a parser? ; If the forcing-round is 0, then we do not print the [...] displaying the forcing-round. ; The sequence of id's printed as the primes field goes from 0 to 11 is ; Subgoal *2.1/5.7.9.11 ; Subgoal *2.1/5.7.9.11' ; Subgoal *2.1/5.7.9.11'' ; Subgoal *2.1/5.7.9.11''' ; Subgoal *2.1/5.7.9.11'4' ; Subgoal *2.1/5.7.9.11'5' ; Subgoal *2.1/5.7.9.11'6' ; Subgoal *2.1/5.7.9.11'7' ; Subgoal *2.1/5.7.9.11'8' ; Subgoal *2.1/5.7.9.11'9' ; Subgoal *2.1/5.7.9.11'10' ; Subgoal *2.1/5.7.9.11'11' ; If the pool-lst is nil (which is not a pool list ever produced by ; pool-lst but which is used before we have pushed anything into the ; pool), then we print ; Subgoal 5.7.9.11''' ; or ; [3]Subgoal 5.7.9.11''' ; depending on the forcing-round. ; And if the pool-lst is nil and the case-lst is nil we print ; Goal''' ; or ; [3]Goal''' (cons (cond ((= (access clause-id id :forcing-round) 0) (cond ((null (access clause-id id :pool-lst)) (cond ((null (access clause-id id :case-lst)) "Goal~#q~[~/'~/''~/'''~/'~xn'~]") (t "Subgoal ~@c~#q~[~/'~/''~/'''~/'~xn'~]"))) (t "Subgoal ~@p/~@c~#q~[~/'~/''~/'''~/'~xn'~]"))) (t (cond ((null (access clause-id id :pool-lst)) (cond ((null (access clause-id id :case-lst)) "[~xr]Goal~#q~[~/'~/''~/'''~/'~xn'~]") (t "[~xr]Subgoal ~@c~#q~[~/'~/''~/'''~/'~xn'~]"))) (t "[~xr]Subgoal ~@p/~@c~#q~[~/'~/''~/'''~/'~xn'~]")))) (list (cons #\r (access clause-id id :forcing-round)) (cons #\p (tilde-@-pool-name-phrase 0 (access clause-id id :pool-lst))) (cons #\c (cons "~*0" (list (cons #\0 (list "" "~x*" "~x*." "~x*." (access clause-id id :case-lst)))))) (cons #\q (cond ((> (access clause-id id :primes) 3) 4) (t (access clause-id id :primes)))) (cons #\n (access clause-id id :primes))))) (defrec bddnote (cl-id goal-term mx-id err-string fmt-alist cst term bdd-call-stack ttree) nil) (defun tilde-@-bddnote-phrase (x) ; Bddnote is either a tagged bddnote pair or nil. This function returns a ~@ ; phrase to be used just after "But simplification" or "This simplifies". (cond ((null x) "") (t (msg " with BDDs (~x0 nodes)" (access bddnote x :mx-id))))) ; Clause-ids are typed as strings by the user when he wants to ; identify a clause to which some hint settings are attached. We now ; develop the machinery for parsing the user's strings into clause-id ; records. (defun parse-natural1 (str i maximum ans) ; Starting at the ith position of string str we parse a natural ; number. We return the number read (or nil, if the first char we see ; is not a digit) and the position of the first non-digit. Ans should ; be initially nil. (cond ((>= i maximum) (mv ans maximum)) (t (let* ((c (char str i)) (d (case c (#\0 0) (#\1 1) (#\2 2) (#\3 3) (#\4 4) (#\5 5) (#\6 6) (#\7 7) (#\8 8) (#\9 9) (otherwise nil)))) (cond (d (parse-natural1 str (1+ i) maximum (cond ((null ans) d) (t (+ (* 10 ans) d))))) (t (mv ans i))))))) (defun parse-natural (dflg str i maximum) ; If dflg is nil, this is just parse-natural1, i.e., starting at the ; ith position of string str we parse a natural number. We return the ; number read (or nil, if the first char we see is not a digit) and ; the position of the first non-digit. ; If dflg is non-nil, we allow an initial D, which we add to the final ; answer with packn, thus returning a symbol rather than a natural. ; Thus, if D123 parses as that symbol, if dflg is non-nil. (cond ((>= i maximum) (mv nil maximum)) ((and dflg (eql (char str i) #\D)) (mv-let (ans k) (parse-natural1 str (+ 1 i) maximum nil) (cond ((null ans) (mv nil i)) (t (mv (packn (list 'D ans)) k))))) (t (parse-natural1 str i maximum nil)))) (defun parse-dotted-naturals (dflg str i maximum ans) ; For now, assume dflg is nil. ; Starting at the ith position of string str we parse a list of ; naturals separated by dots. We return the list of naturals (which ; may be nil) and the position of the first character not parsed. ; Here are some examples. In all cases, assume the initial i is 1. ; "*2.1.3 abc..." => (2 1 3) and 6 (which points to the #\Space) ; " Subgoal..." => nil and 1 (which points to the #\S) ; " 5.7.9" => (5 7 9) and 6 (which is just off the end) ; " 5.7ABC" => (5 7) and 4 (which points to the #\A) ; " 5.7.ABC" => (5 7) and 4 (which points to the #\.) ; The last example bears thinking about. ; If dflg is non-nil, we allow Dn where naturals are expected above. ; I.e., "*2.1.D23.4 abc" would parse to (2 1 D23 4). Thus, the ; variable nat below may sometimes hold a symbol, e.g., D23. (cond ((>= i maximum) (mv (reverse ans) maximum)) (t (mv-let (nat j) (parse-natural dflg str i maximum) (cond ((null nat) (mv (reverse ans) i)) ((>= j maximum) (mv (reverse (cons nat ans)) maximum)) ((and (eql (char str j) #\.) (< (1+ j) maximum) (or (member (char str (1+ j)) '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9)) (and dflg (eql (char str (1+ j)) #\D)))) (parse-dotted-naturals dflg str (1+ j) maximum (cons nat ans))) (t (mv (reverse (cons nat ans)) j))))))) (defun parse-match (pat j patmax str i strmax) ; Starting at the ith char of string str we match each against its ; counterpart in pat, starting at j. If we exhaust pat we return the ; position of the first character in str past the match. Otherwise we ; return nil. This matching is case-insensitive. (cond ((>= j patmax) i) ((>= i strmax) nil) ((or (eql (char pat j) (char str i)) (eql (char-downcase (char pat j)) (char-downcase (char str i)))) (parse-match pat (1+ j) patmax str (1+ i) strmax)) (t nil))) (defun parse-primes (str i maximum) ; Starting at the ith char of string str we count the "number of primes." ; ', '', and ''' are 1, 2, and 3, respectively. '4' is 4, '5' is 5, etc. ; We return nil if the string we find is not of this form. We also return ; the index of the first character not parsed. (cond ((>= i maximum) (mv 0 maximum)) ((eql (char str i) #\') (cond ((= (+ 1 i) maximum) (mv 1 maximum)) ((eql (char str (+ 1 i)) #\') (cond ((= (+ 2 i) maximum) (mv 2 maximum)) ((eql (char str (+ 2 i)) #\') (mv 3 (+ 3 i))) (t (mv 2 (+ 2 i))))) (t (mv-let (nat j) (parse-natural nil str (+ 1 i) maximum) (cond ((null nat) (mv 1 (+ 1 i))) ((< nat 4) (mv 1 (+ 1 i))) ((= j maximum) (mv 1 (+ 1 i))) ((eql (char str j) #\') (mv nat (+ 1 j))) (t (mv 1 (+ 1 i)))))))) (t (mv 0 i)))) (defun parse-clause-id2 (forcing-round pool-lst str i maximum) ; Assume that pool-lst is a pool-lst. Suppose that at position i in ; string str there is a case-lst followed by some primes, e.g., ; "...5.7.9.11'''". We parse them out and check that the string ends ; at the end of the primes. We return a clause-id composed of the ; pool-lst supplied above and the parsed case-lst and primes. If the ; parse fails, we return nil. (mv-let (case-lst j) (parse-dotted-naturals t str i maximum nil) ; Allow D's. (cond ((member 0 case-lst) nil) (t ; So we've seen "...5.7.9.11..." where ... may be empty. ; We look for the primes. (mv-let (n j) (parse-primes str j maximum) (cond ((= j maximum) (make clause-id :forcing-round forcing-round :pool-lst pool-lst :case-lst case-lst :primes n)) (t nil))))))) (defun parse-clause-id1 (forcing-round str i maximum) ; This function takes a string, e.g., "...Subgoal *2.1/5.7.9.11'''" and an ; index i into it to indicate the terminal substring of interest. We parse ; that terminal substring into the internal clause id with forcing-round as its ; :forcing-round. For example, if i points to the S in subgoal above, then the ; result is ; (make clause-id ; :forcing-round forcing-round ; :pool-lst '(2 1) ; :case-lst '(5 7 9 11) ; :primes 3) ; We return nil if the substring does not parse. (cond ((< maximum (+ i 4)) nil) ((member (char str i) '(#\G #\g)) ; The only thing this string could be is something of the form "Goal'...". In ; particular, we know that the pool-lst and the case-lst are both nil and it is ; merely a question of counting primes. (let ((k (parse-match "Goal" 0 4 str i maximum))) (cond (k (mv-let (n j) (parse-primes str k maximum) (cond ((= j maximum) (make clause-id :forcing-round forcing-round :pool-lst nil :case-lst nil :primes n)) (t nil)))) (t nil)))) (t (let ((k (parse-match "Subgoal " 0 8 str i maximum))) (cond ((null k) nil) ((>= k maximum) nil) ((eql (char str k) #\*) (mv-let (pool-lst j) (parse-dotted-naturals nil str (1+ k) maximum nil) ; disallow D's (cond ((or (null pool-lst) (member 0 pool-lst) (> (+ 1 j) maximum) (not (eql (char str j) #\/))) ; So we've seen "Subgoal *junk" and we return nil. nil) (t ; So we've seen "Subgoal *2.1/..." where ... is non-empty. We look for the ; case-lst now. (parse-clause-id2 forcing-round pool-lst str (+ 1 j) maximum))))) (t ; So we've seen "Subgoal ..." where ... doesn't begin with #\*. Thus it can ; only be a case-lst followed by primes. (parse-clause-id2 forcing-round nil str k maximum))))))) (defun parse-clause-id (str) ; This function takes a string, e.g., "[3]Subgoal *2.1/5.7.9.11'''" and either ; parses it into an internal clause id or returns nil. For example, on the ; string above the result is ; (make clause-id ; :forcing-round 3 ; :pool-lst '(2 1) ; :case-lst '(5 7 9 11) ; :primes 3) ; We are case insensitive, but totally rigid about whitespace. We ; expect that the user will most often obtain these strings by ; grabbing the fmt output of a tilde-@-clause-id-phrase object. Users ; sometimes use Emacs to lowercase whole regions of events and that is ; why we are case insensitive. ; We recognize two special cases of clause-id's that are never printed ; by prove. "Goal" and the symbol T both denote the top-level ; clause-id. (cond ((stringp str) (let* ((maximum (length str))) (cond ((< maximum 4) nil) ((eql (char str 0) #\[) (mv-let (forcing-round i) (parse-natural nil str 1 maximum) (cond ((and forcing-round (eql (char str i) #\])) (parse-clause-id1 forcing-round str (1+ i) maximum)) (t nil)))) (t (parse-clause-id1 0 str 0 maximum))))) ((eq str t) *initial-clause-id*) (t nil))) (defun tilde-@-case-split-limitations-phrase (sr-flg case-flg prefix) (if (or sr-flg case-flg) (msg "~@0(By the way, the ~@1 affected this analysis. See :DOC ~ case-split-limitations.)" prefix (if sr-flg (if case-flg "subsumption/replacement and case limits" "subsumption/replacement limit") "case limit")) "")) ; And now we can define the output routine for simplify-clause, which is also ; used in apply-top-hints-clause-msg1. (defun simplify-clause-msg1 (signal cl-id clauses speciousp ttree pspv state) ; The arguments to this function are NOT the standard ones for an ; output function in the waterfall because we are prepared to print a ; message about the simplification being specious and as of this ; writing simplification is the only process that may be specious. ; Exception: OBDD processing also uses this function, and can also ; produce specious simplification. Note that our current treatment of ; OBDDs does not create 'assumptions; however, we check for them ; anyhow here, both in order to share this code between ; simplify-clause and OBDD processing and in order to be robust (in ; case forcing enters the realm of OBDD processing later). ; See the discussion of the waterfall for more details about the ; standard arguments for processors. (declare (ignore signal pspv)) (let ((raw-proof-format (f-get-global 'raw-proof-format state))) (cond (speciousp ; At one time had access to the clauses and could print a little more ; information here. But apparently the code was reorganized in Version_3.3 ; such that clauses is nil at this point. It seems unimportant to report how ; many clauses there are in the specious case. (fms "This ~#0~[~/forcibly ~]simplifies~@b, using ~*1~@pto a set of ~ conjectures including ~@3 itself! Therefore, we ignore this ~ specious simp~-li~-fi~-ca~-tion. See :DOC ~ specious-simplification.~@c~|" (list (cons #\0 (if (tagged-objectsp 'assumption ttree) 1 0)) (cons #\1 (if raw-proof-format (tilde-*-raw-simp-phrase ttree #\, "") (tilde-*-simp-phrase ttree))) (cons #\p (if raw-proof-format "" ", ")) (cons #\3 (tilde-@-clause-id-phrase cl-id)) (cons #\b (tilde-@-bddnote-phrase (tagged-object 'bddnote ttree))) (cons #\c (tilde-@-case-split-limitations-phrase (tagged-objects 'sr-limit ttree) (tagged-objects 'case-limit ttree) " "))) (proofs-co state) state (term-evisc-tuple nil state))) ((null clauses) (cond (raw-proof-format (fms "But ~#0~[~/forced ~]simplification~@b reduces this to T, using ~ ~*1~|" (list (cons #\0 (if (tagged-objectsp 'assumption ttree) 1 0)) (cons #\1 (tilde-*-raw-simp-phrase ttree #\. (tilde-@-case-split-limitations-phrase (tagged-objects 'sr-limit ttree) (tagged-objects 'case-limit ttree) ""))) (cons #\b (tilde-@-bddnote-phrase (tagged-object 'bddnote ttree)))) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "But ~#0~[~/forced ~]simplification~@b reduces this to T, using ~ ~*1.~@c~|" (list (cons #\0 (if (tagged-objectsp 'assumption ttree) 1 0)) (cons #\1 (tilde-*-simp-phrase ttree)) (cons #\b (tilde-@-bddnote-phrase (tagged-object 'bddnote ttree))) (cons #\c (tilde-@-case-split-limitations-phrase (tagged-objects 'sr-limit ttree) (tagged-objects 'case-limit ttree) " "))) (proofs-co state) state (term-evisc-tuple nil state))))) (t (let ((hyp-phrase (tagged-object 'hyp-phrase ttree))) (cond (hyp-phrase (fms "We remove HIDE from ~@0, which was used heuristically to ~ transform ~@1 by substituting into the rest of that ~ goal. This produces~|" (list (cons #\0 hyp-phrase) (cons #\1 (tilde-@-clause-id-phrase (tagged-object 'clause-id ttree)))) (proofs-co state) state (term-evisc-tuple nil state))) (raw-proof-format (fms "This ~#0~[~/forcibly ~]simplifies~@b, using ~*1~ to~#2~[~/ the following ~n3 conjectures.~@c~]~|" (list (cons #\0 (if (tagged-objectsp 'assumption ttree) 1 0)) (cons #\1 (tilde-*-raw-simp-phrase ttree #\, "")) (cons #\2 clauses) (cons #\3 (length clauses)) (cons #\b (tilde-@-bddnote-phrase (tagged-object 'bddnote ttree))) (cons #\c (tilde-@-case-split-limitations-phrase (tagged-objectsp 'sr-limit ttree) (tagged-objectsp 'case-limit ttree) " "))) (proofs-co state) state (term-evisc-tuple nil state))) (t (fms "This ~#0~[~/forcibly ~]simplifies~@b, using ~*1, ~ to~#2~[~/ the following ~n3 conjectures.~@c~]~|" (list (cons #\0 (if (tagged-objectsp 'assumption ttree) 1 0)) (cons #\1 (tilde-*-simp-phrase ttree)) (cons #\2 clauses) (cons #\3 (length clauses)) (cons #\b (tilde-@-bddnote-phrase (tagged-object 'bddnote ttree))) (cons #\c (tilde-@-case-split-limitations-phrase (tagged-objectsp 'sr-limit ttree) (tagged-objectsp 'case-limit ttree) " "))) (proofs-co state) state (term-evisc-tuple nil state))))))))) (deflabel specious-simplification :doc ":Doc-Section Miscellaneous nonproductive proof steps~/ Occasionally the ACL2 theorem prover reports that the current goal simplifies to itself or to a set including itself. Such simplifications are said to be ``specious'' and are ignored in the sense that the theorem prover acts as though no simplification were possible and tries the next available proof technique. Specious simplifications are almost always caused by forcing.~/ The simplification of a formula proceeds primarily by the local application of ~c[:]~ilc[rewrite], ~c[:]~ilc[type-prescription], and other rules to its various subterms. If no rewrite rules apply, the formula cannot be simplified and is passed to the next ACL2 proof technique, which is generally the elimination of destructors. The experienced ACL2 user pays special attention to such ``maximally simplified'' formulas; the presence of unexpected terms in them indicates the need for additional rules or the presence of some conflict that prevents existing rules from working harmoniously together. However, consider the following interesting possibility: local rewrite rules apply but, when applied, reproduce the goal as one of its own subgoals. How can rewrite rules apply and reproduce the goal? Of course, one way is for one rule application to undo the effect of another, as when commutativity is applied twice in succession to the same term. Another kind of example is when two rules conflict and undermine each other. For example, under suitable hypotheses, ~c[(length x)] might be rewritten to ~c[(+ 1 (length (cdr x)))] by the ~c[:]~ilc[definition] of ~ilc[length] and then a ~c[:]~ilc[rewrite] rule might be used to ``fold'' that back to ~c[(length x)]. Generally speaking the presence of such ``looping'' rewrite rules causes ACL2's simplifier either to stop gracefully because of heuristics such as that described in the documentation for ~ilc[loop-stopper] or to cause a stack overflow because of indefinite recursion. A more insidious kind of loop can be imagined: two rewrites in different parts of the formula undo each other's effects ``at a distance,'' that is, without ever being applied to one another's output. For example, perhaps the first hypothesis of the formula is simplified to the second, but then the second is simplified to the first, so that the end result is a formula propositionally equivalent to the original one but with the two hypotheses commuted. This is thought to be impossible unless forcing or case-splitting occurs, but if those features are exploited (~pl[force] and ~pl[case-split]) it can be made to happen relatively easily. Here is a simple example. Declare ~c[foo] to be a function of one argument returning one result: ~bv[] (defstub p1 (x) t) ~ev[] Prove the following silly rule: ~bv[] (defthm bad (implies (case-split (p1 x)) (p1 x))) ~ev[] Now suppose we try the following. ~bv[] (thm (p1 x)) ~ev[] The ~il[rewrite] rule ~c[bad] will rewrite ~c[(p1 x)] to ~c[t], but it will be unable to prove the hypothesis ~c[(case-split (p1 x))]. As a result, the prover will spawn a new goal, to prove ~c[(p1 x)]. However, since this new goal is the same as the original goal, ACL2 will recognize the simplification as ~em[specious] and consider the attempted simplification to have failed. In other words, despite the rewriting, no progress was made! In more common cases, the original goal may simplify to a set of subgoals, one of which includes the original goal. If ACL2 were to adopt the new set of subgoals, it would loop indefinitely. Therefore, it checks whether the input goal is a member of the output subgoals. If so, it announces that the simplification is ``specious'' and pretends that no simplification occurred. ``Maximally simplified'' formulas that produce specious simplifications are maximally simplified in a very technical sense: were ACL2 to apply every applicable rule to them, no progress would be made. Since ACL2 can only apply every applicable rule, it cannot make further progress with the formula. But the informed user can perhaps identify some rule that should not be applied and make it inapplicable by disabling it, allowing the simplifier to apply all the others and thus make progress. When specious simplifications are a problem it might be helpful to ~il[disable] all forcing (including ~il[case-split]s) and resubmit the formula to observe whether forcing is involved in the loop or not. ~l[force]. The commands ~bv[] ACL2 !>:disable-forcing and ACL2 !>:enable-forcing ~ev[] ~il[disable] and ~il[enable] the pragmatic effects of both ~c[force] and ~c[case-split]. If the loop is broken when forcing is ~il[disable]d, then it is very likely some ~il[force]d hypothesis of some rule is ``undoing'' a prior simplification. The most common cause of this is when we ~il[force] a hypothesis that is actually false but whose falsity is somehow temporarily hidden (more below). To find the offending rule, compare the specious simplification with its non-specious counterpart and look for rules that were speciously applied that are not applied in the non-specious case. Most likely you will find at least one such rule and it will have a ~ilc[force]d hypothesis. By disabling that rule, at least for the subgoal in question, you may allow the simplifier to make progress on the subgoal.") (defun settled-down-clause-msg1 (signal clauses ttree pspv state) ; The arguments to this function are the standard ones for an output ; function in the waterfall. See the discussion of the waterfall. (declare (ignore signal clauses ttree pspv)) state) acl2-sources/sum-list-example.lisp0000666002132200015000000001007612146476723016674 0ustar kaufmannacl2; ACL2 Version 6.0 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2012, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; This file illustrates Version 1.8 as of Nov 2, 1994. We start in ; the default :logic mode with guard checking on, i.e. ,the prompt ; is ACL2 !>. (defun sum-list (x) (declare (xargs :guard (integer-listp x) :verify-guards nil)) (cond ((endp x) 0) (t (+ (car x) (sum-list (cdr x)))))) ; Note that we disable guard verification above. We can now evaluate ; calls of this function. (sum-list '(1 2 3 4)) ; this call succeeds. (sum-list '(1 2 abc 3 4 . xyz)) ; this one fails because of a guard violation. ; It is surprising, perhaps, to note that the violation is on the guard of ; endp, not the guard of sum-list. Because we have not yet verified the guards ; of sum-list we only have a logical definition. But because endp has had its ; guards verified, we can run either the Common Lisp or the logical version of ; it and with guard checking on we are running the Common Lisp one. ; So we switch to no guard checking... :set-guard-checking nil (sum-list '(1 2 abc 3 4 . xyz)) ; Now this call succeeds. ; We prove an important theorem about sum-list. Note that we do not have ; to restrict it with any hypotheses. (defthm sum-list-append (equal (sum-list (append a b)) (+ (sum-list a) (sum-list b)))) ; Ok, we now move on to the task of showing these results hold in Common Lisp. (verify-guards sum-list) ; this is just like it was in Version 1.7 :comp sum-list ; this compiles both the Common Lisp version of ; sum-list and the logical (Nqthm-like ``*1*'') ; version ; Now we exit the loop and install a trace on the Common Lisp program ; sum-list, and then reenter the loop. :q (trace sum-list) (lp) ; Recall that guard checking is still off. The following call violates the ; guards of sum-list, but since guard checking is off, we get the logical ; answer. It is done without running the Common Lisp version of sum-list. ; That is, no trace output is evident. (sum-list '(1 2 abc 3 4)) ; succeeds (but runs the compiled *1* function) ; But the following call prints a lot of trace output. Because the ; guard is satisfied and we can run sum-list directly in Common Lisp. (sum-list '(1 2 3 4)) ; succeeds (running compiled sum-list) ; Next we turn our attention to verifying that sum-list-append ; holds in Common Lisp. If we try (verify-guards sum-list-append) ; it fails. Among other things, the guard for append is violated since we know ; nothing about a. We need a version of the theorem with some hypotheses ; restricting the variables. We state the theorem with IF so that the ; hypotheses govern the conclusion. (At the moment in Version 1.8 IMPLIES is a ; function, not a macro, and hence in (IMPLIES p q), q is not governed by p.) (defthm common-lisp-version-of-sum-list-append (if (and (integer-listp a) (integer-listp b)) (= (sum-list (append a b)) (+ (sum-list a) (sum-list b))) t) :rule-classes nil) ; The theorem above is trivially proved, because its conclusion is true ; without the hypotheses. But we can now verify that its guards hold. (verify-guards common-lisp-version-of-sum-list-append) ; So that expression will all evaluate to non-nil without error in ; Common Lisp (except for resource errors). acl2-sources/TAGS0000644002132200015000000232653312222333430013242 0ustar kaufmannacl2 acl2.lisp,6270 (defvar *acl2-compiler-enabled*)*acl2-compiler-enabled*67,3198 (defun acl2-set-character-encoding ()acl2-set-character-encoding308,15326 (defun our-file-encoding (pathname ef-spec buffer length)our-file-encoding425,19748 (defconstant acl2::*the-live-state* 'acl2_invisible::|The Live State Itself|)acl2::*the-live-state*706,31094 (defvar acl2::*compiling-certified-file* nil)acl2::*compiling-certified-file*741,33454 (defun acl2::defconst-redeclare-error (name)acl2::defconst-redeclare-error743,33501 (defparameter acl2::*safe-mode-verified-p*acl2::*safe-mode-verified-p*764,34444 (defmacro acl2::defconst (name term &rest rst)acl2::defconst773,34737 (defvar acl2::*copy-of-common-lisp-symbols-from-main-lisp-package*)acl2::*copy-of-common-lisp-symbols-from-main-lisp-package*847,37631 (defvar acl2::*copy-of-common-lisp-specials-and-constants*)acl2::*copy-of-common-lisp-specials-and-constants*848,37699 (defvar acl2::*copy-of-acl2-version*)acl2::*copy-of-acl2-version*849,37759 (defconstant acl2::*acl2-files*acl2::*acl2-files*851,37798 (defparameter *compiled-file-extension**compiled-file-extension*969,41767 (defmacro initialize-state-globals ()initialize-state-globals979,42177 (defconstant *suppress-compile-build-time**suppress-compile-build-time*1039,44898 (defparameter *global-package-prefix* "ACL2_GLOBAL_")*global-package-prefix*1051,45286 (defparameter *1*-package-prefix* "ACL2_*1*_")*1*-package-prefix*1053,45341 (defun make-global-package (x)make-global-package1055,45389 (defun make-*1*-package (x)make-*1*-package1063,45642 (defconstant *main-lisp-package**main-lisp-package*1081,46201 (defconstant *main-lisp-package-name-raw**main-lisp-package-name-raw*1084,46267 (defparameter acl2::*initial-lisp-symbol-mark*acl2::*initial-lisp-symbol-mark*1107,47149 (defconstant *acl2-package* (find-package "ACL2"))*acl2-package*1118,47578 (defmacro with-redefinition-suppressed (&rest forms)with-redefinition-suppressed1148,48739 (defmacro with-warnings-suppressed (&rest forms)with-warnings-suppressed1184,50090 (defmacro with-more-warnings-suppressed (&rest forms)with-more-warnings-suppressed1231,51752 (defmacro with-suppression (&rest forms)with-suppression1266,52887 (defconstant acl2::*acl2-status-file*acl2::*acl2-status-file*1281,53517 (defun acl2::check-suitability-for-acl2 ()acl2::check-suitability-for-acl21285,53624 (defun note-compile-ok ()note-compile-ok1301,54146 (defvar *lisp-extension* "lisp")*lisp-extension*1332,55684 (defmacro our-with-compilation-unit (form)our-with-compilation-unit1368,57211 (defconstant *acl2-read-character-terminators**acl2-read-character-terminators*1397,58533 (defparameter *acl2-readtable**acl2-readtable*1432,59857 (defparameter *host-readtable**host-readtable*1438,60068 (defun set-new-dispatch-macro-character (char subchar fn)set-new-dispatch-macro-character1443,60241 (defun define-sharp-dot ()define-sharp-dot1504,63280 (defun define-sharp-comma ()define-sharp-comma1510,63376 (defun define-sharp-atsign ()define-sharp-atsign1516,63476 (defun define-sharp-bang ()define-sharp-bang1522,63582 (defun define-sharp-u ()define-sharp-u1528,63684 (defvar *old-character-reader**old-character-reader*1534,63780 (defun modify-acl2-readtable (do-all-changes)modify-acl2-readtable1537,63854 (defvar *reckless-acl2-readtable**reckless-acl2-readtable*1626,66601 (defvar *load-compiled-verbose* nil)*load-compiled-verbose*1643,67127 (defun load-compiled (filename &optional verbose)load-compiled1645,67165 (defun non-trivial-acl2-proclaims-file-p ()non-trivial-acl2-proclaims-file-p1896,77147 (defun compile-acl2 (&optional use-acl2-proclaims)compile-acl21912,77809 (defvar user::*fast-acl2-gcl-build* nil)user::*fast-acl2-gcl-build*2023,82445 (defun load-acl2 (&optional fast)load-acl22025,82487 (defparameter *acl2-panic-exit-status* nil)*acl2-panic-exit-status*2129,86611 (defun exit-lisp (&optional (status '0 status-p))exit-lisp2131,86656 (defconstant *slashable-array**slashable-array*2236,91391 (defconstant *suspiciously-first-numeric-array**suspiciously-first-numeric-array*2249,91838 (defconstant *suspiciously-first-hex-array**suspiciously-first-hex-array*2260,92174 (defconstant *base-10-array**base-10-array*2274,92595 (defconstant *hex-array**hex-array*2285,92873 (defconstant *letter-array**letter-array*2298,93222 (defmacro suspiciously-first-numeric-array (print-base)suspiciously-first-numeric-array2312,93701 (defmacro numeric-array (print-base)numeric-array2317,93866 (defconstant *char-code-backslash* (char-code #\\))*char-code-backslash*2322,93974 (defconstant *char-code-slash* (char-code #\/))*char-code-slash*2324,94027 (defconstant *char-code-double-gritch* (char-code #\"))*char-code-double-gritch*2326,94076 (defconstant *big-n-special-object* '(nil . nil))*big-n-special-object*2332,94332 (defconstant *number-of-return-values**number-of-return-values*2334,94383 (defconstant *boole-array**boole-array*2340,94485 (defconstant *mo-f* (make-symbol "F"))*mo-f*2378,95852 (defconstant *mo-h* (make-symbol "H"))*mo-h*2379,95891 (defconstant *mo-o* (make-symbol "O"))*mo-o*2380,95930 (defconstant *mf-old-caller* (make-symbol "OLD-CALLER"))*mf-old-caller*2385,96023 (defconstant *mf-start-hons* (make-symbol "START-HONS"))*mf-start-hons*2386,96080 (defconstant *mf-start-pons* (make-symbol "START-PONS"))*mf-start-pons*2387,96137 (defconstant *mf-start-bytes* (make-symbol "START-BYTES"))*mf-start-bytes*2388,96194 (defconstant *mf-ans* (make-symbol "ANS"))*mf-ans*2389,96253 (defconstant *mf-ans-p* (make-symbol "ANS-P"))*mf-ans-p*2390,96296 (defconstant *mf-ma* (make-symbol "MA"))*mf-ma*2391,96343 (defconstant *mf-args* (make-symbol "ARGS"))*mf-args*2392,96384 (defconstant *mf-2mmf* (make-symbol "MF-2MMF"))*mf-2mmf*2393,96429 (defconstant *mf-2mmf-fnn* (make-symbol "MF-2MMF-FNN"))*mf-2mmf-fnn*2394,96477 (defconstant *mf-count-loc* (make-symbol "MF-COUNT-LOC"))*mf-count-loc*2395,96533 (defconstant *attached-fn-temp* (make-symbol "ATTACHED-FN-TEMP"))*attached-fn-temp*2397,96592 (defvar *debug-prompt-suffix* "")*debug-prompt-suffix*2424,97740 (defun break-level-for-acl2 (at &optional env)break-level-for-acl22426,97854 acl2-check.lisp,0 acl2-fns.lisp,5127 (defmacro qfuncall (fn &rest args)qfuncall27,1067 (defmacro defun-one-output (&rest args)defun-one-output40,1583 (defparameter *package-alist* nil)*package-alist*51,2044 (defun-one-output find-package-fast (string)find-package-fast53,2080 (defvar *global-symbol-key* (make-symbol "*GLOBAL-SYMBOL-KEY*"))*global-symbol-key*59,2287 (defun global-symbol (x)global-symbol61,2353 (defmacro live-state-p (x)live-state-p70,2692 (defun get-global (x state-state)get-global74,2771 (defmacro f-get-global (x st)f-get-global84,3106 (defun acl2-realp (x)acl2-realp117,4489 (defun gcl-version-> (major minor extra &optional weak)gcl-version->133,5032 (defun gcl-version->= (major minor extra)gcl-version->=158,6125 (defvar *do-proclaims**do-proclaims*225,9744 (defun macroexpand-till (form sym)macroexpand-till249,10778 (defun get-type-from-dcls (var dcls)get-type-from-dcls265,11486 (defun arg-declarations (formals dcls)arg-declarations273,11742 (defun collect-types (l)collect-types278,11927 (defun convert-type-to-integer-pair (typ)convert-type-to-integer-pair289,12266 (defvar *acl2-output-type-abort* nil)*acl2-output-type-abort*303,12700 (defun min-integer-* (x y)min-integer-*305,12739 (defun max-integer-* (x y)max-integer-*310,12845 (defun max-output-type-for-declare-form (type1 type2)max-output-type-for-declare-form315,12951 (defun max-output-type-for-declare-form-lst (type-list1 type-list2)max-output-type-for-declare-form-lst395,15907 (defun output-type-for-declare-form-rec (form flet-alist)output-type-for-declare-form-rec430,17321 (defun output-type-for-declare-form-rec-list (forms flet-alist)output-type-for-declare-form-rec-list572,23273 (defun output-type-for-declare-form (fn form)output-type-for-declare-form583,23767 (defun make-defun-declare-form (fn formmake-defun-declare-form629,25516 (defun make-defconst-declare-form (form)make-defconst-declare-form662,26934 (defun make-defstobj-declare-form (form)make-defstobj-declare-form680,27568 (defmacro eval-or-print (form stream)eval-or-print711,28963 (defun proclaim-form (form &optional stream)proclaim-form719,29149 (defun proclaim-file (name &optional stream)proclaim-file765,30747 (defparameter *comma* (make-symbol "COMMA")*comma*817,33359 (defparameter *comma-atsign* (make-symbol "COMMA-ATSIGN")*comma-atsign*821,33525 (defparameter *backquote-counter* 0*backquote-counter*825,33719 (defun backquote (x)backquote832,34051 (defun backquote-lst (l)backquote-lst875,36229 (defun rev1@ (x acc)rev1@900,37100 (defun acl2-read-character-string (s acc)acl2-read-character-string910,37307 (defun acl2-character-reader (s c n)acl2-character-reader982,40390 (defvar *inside-sharp-dot-read* nil)*inside-sharp-dot-read*991,40708 (defvar *inhibit-sharp-comma-warning* nil)*inhibit-sharp-comma-warning*993,40746 (defvar *inside-sharp-u-read* nil)*inside-sharp-u-read*995,40790 (defun sharp-comma-read (stream char n)sharp-comma-read997,40826 (defun sharp-dot-read (stream char n)sharp-dot-read1008,41357 (defun sharp-bang-read (stream char n)sharp-bang-read1044,42891 (defun sharp-u-read (stream char n)sharp-u-read1067,43864 (defmacro sharp-atsign-read-er (str &rest format-args)sharp-atsign-read-er1098,45255 (defun sharp-atsign-read (stream char n)sharp-atsign-read1104,45510 (defvar *sharp-reader-array-size**sharp-reader-array-size*1151,47484 (defvar *sharp-reader-array**sharp-reader-array*1158,47672 (defvar *sharp-reader-array-size-multiplier**sharp-reader-array-size-multiplier*1161,47744 (defconstant *sharp-reader-max-array-size**sharp-reader-max-array-size*1168,47924 (defvar *sharp-reader-max-index**sharp-reader-max-index*1176,48176 (defun update-sharp-reader-max-index (index)update-sharp-reader-max-index1189,48843 (defun reckless-sharp-sharp-read (stream char arg)reckless-sharp-sharp-read1218,50028 (defun reckless-sharp-equal-read (stream char arg)reckless-sharp-equal-read1224,50188 (defmacro with-reckless-read (&rest forms)with-reckless-read1232,50520 (defun symbol-package-name (x)symbol-package-name1254,51402 (defmacro gv (fn args val)gv1286,52851 (defun getenv$-raw (string)getenv$-raw1309,53832 (defun get-os ()get-os1332,54655 (defmacro our-ignore-errors (x)our-ignore-errors1341,54869 (defmacro safe-open (&rest args)safe-open1345,54945 (defun our-truename (filename &optional namestringp)our-truename1348,55017 (defun our-pwd ()our-pwd1418,58112 (defun cancel-dot-dots (full-pathname)cancel-dot-dots1429,58500 (defun unix-full-pathname (name &optional extension)unix-full-pathname1437,58818 (defun our-user-homedir-pathname ()our-user-homedir-pathname1465,59833 (defun ser-cons-reader-macro (stream subchar arg)ser-cons-reader-macro1517,62109 (defun ser-hons-reader-macro (stream subchar arg)ser-hons-reader-macro1523,62386 (defmacro special-form-or-op-p (name)special-form-or-op-p1540,63276 (defvar *startup-package-name* "ACL2")*startup-package-name*1547,63500 (defmacro save-def (def-form)save-def1549,63540 (defmacro defg (&rest r)defg1574,64516 (defmacro defv (&rest r)defv1588,65010 init.lisp,0 acl2-init.lisp,4182 (defconstant *current-acl2-world-key**current-acl2-world-key*306,14371 (defun system-call (string arguments)system-call360,16578 (defun copy-acl2 (dir)copy-acl2435,18977 (defun our-probe-file (filename)our-probe-file452,19492 (defun copy-distribution (output-file source-directory target-directorycopy-distribution473,20234 (defun make-tags ()make-tags643,27955 (defvar *saved-build-date-lst*)*saved-build-date-lst*684,30082 (defvar *saved-mode*)*saved-mode*685,30114 (defun svn-revision-from-line (s)svn-revision-from-line687,30137 (defconstant *acl2-svn-revision-string**acl2-svn-revision-string*698,30541 (defvar *saved-string**saved-string*726,31884 (defun maybe-load-acl2-init ()maybe-load-acl2-init754,32947 (defun chmod-executable (sysout-name)chmod-executable760,33145 (defun saved-build-dates (separator)saved-build-dates763,33233 (defmacro our-with-standard-io-syntax (&rest args)our-with-standard-io-syntax780,33835 (defun user-args-string (inert-args &optional (separator '"--"))user-args-string785,33966 (defmacro write-exec-file (stream prefix string &rest args)write-exec-file801,34631 (defun proclaim-files (&optional outfilename infilename infile-optional-p)proclaim-files829,35770 (defun insert-string (s)insert-string880,37745 (defvar *saved-system-banner**saved-system-banner*885,37841 (defun save-acl2-in-akcl-aux (sysout-name gcl-exec-namesave-acl2-in-akcl-aux905,38678 (defun save-acl2-in-akcl (sysout-name gcl-exec-namesave-acl2-in-akcl957,41023 (defun save-exec-raw (sysout-name host-lisp-args inert-args)save-exec-raw1180,50255 (defvar *acl2-default-restart-complete* nil)*acl2-default-restart-complete*1186,50542 (defun fix-default-pathname-defaults ()fix-default-pathname-defaults1188,50588 (defvar *print-startup-banner**print-startup-banner*1212,51420 (defvar *lp-ever-entered-p* nil)*lp-ever-entered-p*1227,52097 (defun acl2-default-restart ()acl2-default-restart1229,52131 (defun cmulisp-restart ()cmulisp-restart1303,54523 (defun sbcl-restart ()sbcl-restart1310,54655 (defun save-acl2-in-lucid (sysout-name &optional mode)save-acl2-in-lucid1316,54789 (defun lispworks-save-exec-aux (sysout-name eventual-sysout-namelispworks-save-exec-aux1322,54985 (defun save-acl2-in-lispworks (sysout-name mode eventual-sysout-name)save-acl2-in-lispworks1419,59502 (defun save-exec-raw (sysout-name host-lisp-args inert-args)save-exec-raw1429,59871 (defun save-acl2-in-cmulisp-aux (sysout-name core-namesave-acl2-in-cmulisp-aux1438,60142 (defun save-acl2-in-cmulisp (sysout-name &optional mode core-name)save-acl2-in-cmulisp1500,62823 (defun save-exec-raw (sysout-name host-lisp-args inert-args)save-exec-raw1509,63148 (defvar *sbcl-dynamic-space-size**sbcl-dynamic-space-size*1514,63342 (defvar *sbcl-contrib-dir* nil)*sbcl-contrib-dir*1561,65894 (defun save-acl2-in-sbcl-aux (sysout-name core-namesave-acl2-in-sbcl-aux1564,65934 (defun save-acl2-in-sbcl (sysout-name &optional mode core-name)save-acl2-in-sbcl1656,70213 (defun save-exec-raw (sysout-name host-lisp-args toplevel-args inert-args)save-exec-raw1666,70572 (defun save-acl2-in-allegro-aux (sysout-name dxl-namesave-acl2-in-allegro-aux1673,70851 (defun save-acl2-in-allegro (sysout-name &optional mode dxl-name)save-acl2-in-allegro1729,73236 (defun save-exec-raw (sysout-name host-lisp-args inert-args)save-exec-raw1743,73716 (defun rc-filename (dir)rc-filename1747,73903 (defun write-acl2rc (dir)write-acl2rc1750,73968 (defun save-acl2-in-clisp-aux (sysout-name mem-name host-lisp-args inert-args)save-acl2-in-clisp-aux1769,74682 (defun save-acl2-in-clisp (sysout-name &optional mode mem-name)save-acl2-in-clisp1815,76662 (defun save-exec-raw (sysout-name host-lisp-args inert-args)save-exec-raw1824,76982 (defun save-acl2-in-ccl-aux (sysout-name core-namesave-acl2-in-ccl-aux1829,77173 (defun save-acl2-in-ccl (sysout-name &optional mode core-name)save-acl2-in-ccl1933,82211 (defun save-exec-raw (sysout-name host-lisp-args inert-args)save-exec-raw1939,82391 (defun save-acl2 (&optional mode other-infosave-acl21946,82727 (defun generate-acl2-proclaims ()generate-acl2-proclaims2006,84857 akcl-acl2-trace.lisp,663 (defmacro trace (&rest fns)trace36,1430 (defmacro untrace (&rest fns)untrace92,3486 (defun trace-ppr-gcl (direction x &aux (state *the-live-state*))trace-ppr-gcl100,3630 (defun trace-fix-entry-raw (name l)trace-fix-entry-raw132,4972 (defun trace-fix-entry (name l)trace-fix-entry148,5602 (defun trace-values (name)trace-values163,6141 (defun trace-values (name)trace-values173,6430 (defun make-nths (i n var)make-nths178,6515 (defun trace-fix-exit-raw (name l)trace-fix-exit-raw184,6637 (defun trace-fix-exit (name original-name l &aux (state *the-live-state*))trace-fix-exit199,7204 (defun trace-fix-cond (trace-spec)trace-fix-cond220,8176 allegro-acl2-trace.lisp,616 (defvar *trace-arglist*)*trace-arglist*52,2090 (defvar *trace-values*)*trace-values*54,2116 (defconst *trace-sublis* '((values . *trace-values*)*trace-sublis*56,2141 (defun trace-pre-process (lst)trace-pre-process101,4371 (defun trace-entry (name l)trace-entry129,5368 (defun trace-values (name)trace-values158,6478 (defun trace-values (name)trace-values168,6767 (defun make-nths (i n var)make-nths173,6852 (defun trace-exit (name original-name l &aux (state *the-live-state*))trace-exit179,6974 (defun traced-fns-lst (lst)traced-fns-lst215,8449 (defun trace-process (lst)trace-process218,8514 openmcl-acl2-trace.lisp,575 (defvar *trace-arglist*)*trace-arglist*56,2188 (defvar *trace-values*)*trace-values*58,2214 (defparameter *trace-sublis* '((values . *trace-values*)*trace-sublis*60,2239 (defun trace-pre-process (lst &aux (state *the-live-state*))trace-pre-process66,2516 (defun trace-entry (name l)trace-entry101,3869 (defun trace-values (name)trace-values126,5007 (defun trace-exit (name original-name l)trace-exit135,5321 (defun traced-fns-lst (lst)traced-fns-lst164,6651 (defun trace-process (lst)trace-process167,6716 (defun acl2-traced-fns ()acl2-traced-fns187,7620 axioms.lisp,111044 (acl2::defconst acl2::*common-lisp-symbols-from-main-lisp-package*acl2::*common-lisp-symbols-from-main-lisp-package*81,4294 (defconst *common-lisp-specials-and-constants**common-lisp-specials-and-constants*593,26922 (defconst *stobj-inline-declare**stobj-inline-declare*671,31242 (defmacro make-package-entry (&key name imports hidden-p book-pathmake-package-entry914,42484 (defmacro find-package-entry (name known-package-alist)find-package-entry955,44431 (defmacro package-entry-name (package-entry)package-entry-name958,44533 (defmacro package-entry-imports (package-entry)package-entry-imports961,44604 (defmacro package-entry-hidden-p (package-entry)package-entry-hidden-p964,44679 (defmacro package-entry-book-path (package-entry)package-entry-book-path967,44756 (defmacro package-entry-defpkg-event-form (package-entry)package-entry-defpkg-event-form970,44835 (defmacro package-entry-tterm (package-entry)package-entry-tterm973,44928 (defmacro find-non-hidden-package-entry (name known-package-alist)find-non-hidden-package-entry976,45009 (defmacro remove-package-entry (name known-package-alist)remove-package-entry981,45202 (defmacro change-package-entry-hidden-p (entry value)change-package-entry-hidden-p984,45313 (defmacro getprop (symb key default world-name world-alist)getprop994,45683 (defvar *user-stobj-alist* nil)*user-stobj-alist*1012,46394 (defparameter *wormholep* nil)*wormholep*1041,48186 (defun-one-output replace-bad-lisp-object (x)replace-bad-lisp-object1048,48497 (defun-one-output replace-bad-lisp-object-list (x)replace-bad-lisp-object-list1060,48818 (defun-one-output wormhole-er (fn args)wormhole-er1066,48991 (defparameter *wormhole-cleanup-form* nil)*wormhole-cleanup-form*1079,49517 (defun-one-output cloaked-set-w! (x state)cloaked-set-w!1127,52541 (defun-one-output assoc-eq-butlast-2 (x alist)assoc-eq-butlast-21135,52817 (defun-one-output assoc-eq-equal-butlast-2 (x y alist)assoc-eq-equal-butlast-21146,53239 (defun-one-output push-wormhole-undo-formi (op arg1 arg2)push-wormhole-undo-formi1159,53746 (defconstant *open-input-channel-key**open-input-channel-key*1283,60510 (defconstant *open-input-channel-type-key**open-input-channel-type-key*1289,60725 (defconstant *open-output-channel-key**open-output-channel-key*1292,60819 (defconstant *open-output-channel-type-key**open-output-channel-type-key*1295,60905 (defconstant *non-existent-stream**non-existent-stream*1298,61001 (defvar *acl2-error-p* nil)*acl2-error-p*1304,61195 (defun interface-er (&rest args)interface-er1306,61224 (defun-one-output acl2-numberp (x)acl2-numberp1416,65177 (defun-one-output binary-+ (x y) (+ x y))binary-+1419,65228 (defun-one-output binary-* (x y) (* x y))binary-*1421,65271 (defun-one-output unary-- (x) (- x))unary--1423,65314 (defun-one-output unary-/ (x) (/ x))unary-/1425,65352 (defparameter *in-recover-world-flg* nil)*in-recover-world-flg*1431,65567 (defparameter *ever-known-package-alist**ever-known-package-alist*1436,65754 (defvar **1*-symbol-key* (make-symbol "**1*-SYMBOL-KEY*"))**1*-symbol-key*1459,66752 (defun *1*-symbol (x)*1*-symbol1461,66812 (defun *1*-symbol? (x)*1*-symbol?1471,67176 (defmacro defun-*1* (fn &rest args)defun-*1*1483,67657 (defparameter *defun-overrides* nil)*defun-overrides*1486,67730 (defmacro defun-overrides (name formals &rest rest)defun-overrides1488,67768 (defmacro defpkg (&whole event-form name importsdefpkg1503,68340 (defmacro defuns (&rest lst)defuns1516,68821 (defmacro defun-std (name formals &rest args)defun-std1521,68951 (defmacro defuns-std (&rest args)defuns-std1529,69177 (defmacro defthm (&rest args)defthm1532,69232 (defmacro defthmd (&rest args)defthmd1536,69296 (defmacro defthm-std (&rest args)defthm-std1541,69386 (defmacro defaxiom (&rest args)defaxiom1545,69454 (defmacro skip-proofs (arg)skip-proofs1549,69520 (defmacro deflabel (&rest args)deflabel1552,69556 (defmacro defdoc (&rest args)defdoc1556,69622 (defmacro deftheory (&rest args)deftheory1560,69686 (defun-one-output stobj-initial-statep-arr (n i arr init)stobj-initial-statep-arr1564,69753 (defun-one-output stobj-initial-statep-entry (temp entry)stobj-initial-statep-entry1569,69926 (defun-one-output stobj-initial-statep1 (field-templates ndx stobj)stobj-initial-statep11599,70846 (defun-one-output stobj-initial-statep (stobj field-templates)stobj-initial-statep1607,71237 (defun remove-stobj-inline-declare (x)remove-stobj-inline-declare1621,71820 (defun congruent-stobj-rep-raw (name)congruent-stobj-rep-raw1628,72031 (defmacro defstobj (name &rest args)defstobj1640,72416 (defmacro value-triple (&rest args)value-triple1766,77913 (defmacro verify-termination-boot-strap (&rest args)verify-termination-boot-strap1770,77983 (defmacro verify-guards (&rest args)verify-guards1774,78070 (defmacro in-theory (&rest args)in-theory1778,78141 (defmacro in-arithmetic-theory (&rest args)in-arithmetic-theory1782,78208 (defmacro regenerate-tau-database (&rest args)regenerate-tau-database1786,78286 (defmacro push-untouchable (&rest args)push-untouchable1790,78367 (defmacro remove-untouchable (&rest args)remove-untouchable1794,78441 (defmacro set-body (&rest args)set-body1798,78517 (defmacro table (&rest args)table1802,78583 (defmacro encapsulate (signatures &rest lst)encapsulate1813,78960 (defparameter *inside-include-book-fn**inside-include-book-fn*1856,80764 (defmacro include-book (user-book-nameinclude-book1867,81214 (defmacro certify-book (&rest args)certify-book1881,81781 (defmacro local (x)local1892,82282 (defmacro defchoose (&rest args)defchoose1896,82333 (defmacro mutual-recursion (&rest lst)mutual-recursion1906,82748 (defmacro make-event (&whole event-formmake-event1909,82809 (deflabel programmingprogramming1929,83792 (deflabel acl2-built-insacl2-built-ins1946,84413 (deflabel miscellaneousmiscellaneous1962,85124 (defconst *standard-co* 'acl2-output-channel::standard-character-output-0)*standard-co*1976,85499 (defconst *standard-oi* 'acl2-input-channel::standard-object-input-0)*standard-oi*1978,85575 (defconst *standard-ci* 'acl2-input-channel::standard-character-input-0)*standard-ci*1980,85646 (defconst nil 'nilnil2002,86101 (defconst t 'tt2010,86269 (defun insist (x)insist2016,86422 (defun iff (p q)iff2025,86649 (defun xor (p q)xor2040,87041 (defun eq (x y)eq2055,87409 (defun booleanp (x)booleanp2082,88527 (defthm iff-is-an-equivalenceiff-is-an-equivalence2104,89117 (defun implies (p q)implies2112,89332 (defthm iff-implies-equal-implies-1iff-implies-equal-implies-12127,89706 (defthm iff-implies-equal-implies-2iff-implies-equal-implies-22132,89855 (defun not (p)not2138,90021 (defthm iff-implies-equal-notiff-implies-equal-not2155,90437 (defun hide (x)hide2160,90568 (defun rewrite-equiv (x)rewrite-equiv2266,95213 (defmacro real/rationalp (x)real/rationalp2280,95699 (defmacro complex/complex-rationalp (x)complex/complex-rationalp2298,96323 (defun true-listp (x)true-listp2320,97133 (defun list-macro (lst)list-macro2336,97523 (defmacro list (&rest args)list2345,97724 (defun and-macro (lst)and-macro2360,98128 (defmacro and (&rest args)and2371,98360 (defun or-macro (lst)or-macro2387,98803 (defmacro or (&rest args)or2399,99057 (defmacro - (x &optional (y 'nil binary-casep))-2442,100156 (defthm booleanp-compound-recognizerbooleanp-compound-recognizer2487,102017 (defun integer-abs (x)integer-abs2501,102512 (defun xxxjoin (fn args)xxxjoin2507,102621 (defmacro + (&rest rst)+2526,103142 (defun-one-output len2 (x acc)len22537,103401 (defun len1 (x acc)len12542,103511 (defun len (x)len2571,104808 (defun length (x)length2600,105600 (defun-one-output complex-rationalp (x)complex-rationalp2624,106244 (defun acl2-count (x)acl2-count2627,106301 (defun cond-clausesp (clauses)cond-clausesp2682,108448 (defun cond-macro (clauses)cond-macro2691,108704 (defmacro cond (&rest clauses)cond2718,109701 (defun eqlablep (x)eqlablep2740,110426 (defthm eqlablep-recogeqlablep-recog2764,111304 (defun eqlable-listp (l)eqlable-listp2773,111505 (defun eql (x y)eql2791,111988 (defun atom (x)atom2814,112706 (defun make-character-list (x)make-character-list2833,113169 (defun eqlable-alistp (x)eqlable-alistp2855,113752 (defun alistp (l)alistp2873,114302 (defthm alistp-forward-to-true-listpalistp-forward-to-true-listp2889,114695 (defthm eqlable-alistp-forward-to-alistpeqlable-alistp-forward-to-alistp2894,114817 (defun acons (key datum alist)acons2900,114964 (defun endp (x)endp2919,115537 (defmacro caar (x)caar2943,116253 (defmacro cadr (x)cadr2952,116443 (defmacro cdar (x)cdar2961,116633 (defmacro cddr (x)cddr2970,116823 (defmacro caaar (x)caaar2979,117013 (defmacro caadr (x)caadr2988,117206 (defmacro cadar (x)cadar2997,117399 (defmacro caddr (x)caddr3006,117592 (defmacro cdaar (x)cdaar3015,117785 (defmacro cdadr (x)cdadr3024,117978 (defmacro cddar (x)cddar3033,118171 (defmacro cdddr (x)cdddr3042,118364 (defmacro caaaar (x)caaaar3051,118557 (defmacro caaadr (x)caaadr3060,118753 (defmacro caadar (x)caadar3069,118949 (defmacro caaddr (x)caaddr3078,119145 (defmacro cadaar (x)cadaar3087,119341 (defmacro cadadr (x)cadadr3096,119537 (defmacro caddar (x)caddar3105,119733 (defmacro cadddr (x)cadddr3114,119929 (defmacro cdaaar (x)cdaaar3123,120125 (defmacro cdaadr (x)cdaadr3132,120321 (defmacro cdadar (x)cdadar3141,120517 (defmacro cdaddr (x)cdaddr3150,120713 (defmacro cddaar (x)cddaar3159,120909 (defmacro cddadr (x)cddadr3168,121105 (defmacro cdddar (x)cdddar3177,121301 (defmacro cddddr (x)cddddr3186,121497 (defun null (x)null3195,121693 (defun symbol-listp (lst)symbol-listp3213,122205 (defthm symbol-listp-forward-to-true-listpsymbol-listp-forward-to-true-listp3229,122623 (defun symbol-doublet-listp (lst)symbol-doublet-listp3234,122757 (defun reverse-strip-cars (x a)reverse-strip-cars3361,127283 (defun strip-cars (x)strip-cars3367,127472 (defun reverse-strip-cdrs (x a)reverse-strip-cdrs3392,128222 (defun strip-cdrs (x)strip-cdrs3398,128411 (defmacro let-mbe (bindings &key logic exec)let-mbe3422,129152 (defun return-last (fn eager-arg last-arg)return-last3428,129282 (defmacro return-last (qfn arg2 arg3)return-last3942,151401 (defmacro mbe1-raw (exec logic)mbe1-raw3990,153535 (defmacro mbe1 (exec logic)mbe13998,153737 (defmacro must-be-equal (logic exec)must-be-equal4017,154305 (defmacro mbe (&key (exec 'nil exec-p) (logic 'nil logic-p))mbe4045,155567 (defmacro mbt (x)mbt4188,161590 (defdoc equality-variantsequality-variants4291,165761 (defdoc equality-variants-detailsequality-variants-details4404,171416 (defun member-eq-exec (x lst)member-eq-exec4587,178312 (defun member-eql-exec (x lst)member-eql-exec4595,178578 (defun member-equal (x lst)member-equal4603,178849 (defmacro member-eq (x lst)member-eq4612,179151 (defthm member-eq-exec-is-member-equalmember-eq-exec-is-member-equal4615,179211 (defthm member-eql-exec-is-member-equalmember-eql-exec-is-member-equal4619,179311 (defmacro member (x l &key (test ''eql))member4624,179430 (defun subsetp-eq-exec (x y)subsetp-eq-exec4679,181496 (defun subsetp-eql-exec (x y)subsetp-eql-exec4690,181855 (defun subsetp-equal (x y)subsetp-equal4702,182205 (defmacro subsetp-eq (x y)subsetp-eq4713,182560 (defthm subsetp-eq-exec-is-subsetp-equalsubsetp-eq-exec-is-subsetp-equal4716,182618 (defthm subsetp-eql-exec-is-subsetp-equalsubsetp-eql-exec-is-subsetp-equal4720,182722 (defmacro subsetp (x y &key (test ''eql))subsetp4725,182845 (defun symbol-alistp (x)symbol-alistp4776,184799 (defthm symbol-alistp-forward-to-eqlable-alistpsymbol-alistp-forward-to-eqlable-alistp4793,185300 (defun assoc-eq-exec (x alist)assoc-eq-exec4800,185453 (defun assoc-eql-exec (x alist)assoc-eql-exec4808,185740 (defun assoc-equal (x alist)assoc-equal4816,186032 (defmacro assoc-eq (x lst)assoc-eq4825,186348 (defthm assoc-eq-exec-is-assoc-equalassoc-eq-exec-is-assoc-equal4828,186406 (defthm assoc-eql-exec-is-assoc-equalassoc-eql-exec-is-assoc-equal4832,186502 (defmacro assoc (x alist &key (test ''eql))assoc4837,186617 (defun assoc-eq-equal-alistp (x)assoc-eq-equal-alistp4890,188728 (defun assoc-eq-equal (x y alist)assoc-eq-equal4898,188983 (defmacro <= (x y)<=4918,189730 (defun = (x y)=4933,190062 (defun /= (x y)/=4958,190716 (defmacro > (x y)>4982,191419 (defmacro >= (x y)>=4997,191723 (deflabel zero-test-idiomszero-test-idioms5011,192041 (defmacro int= (i j)int=5180,200454 (defun zp (x)zp5202,201199 (defun-one-output zp (x)zp5242,202449 (defthm zp-compound-recognizerzp-compound-recognizer5246,202518 (defthm zp-openzp-open5261,203000 (defun zip (x)zip5288,203862 (defun-one-output zip (x) (= x 0))zip5328,205038 (defthm zip-compound-recognizerzip-compound-recognizer5330,205074 (defthm zip-openzip-open5339,205269 (defun nth (n l)nth5348,205477 (defun char (s n)char5375,206230 (defun proper-consp (x)proper-consp5399,206962 (defun improper-consp (x)improper-consp5414,207355 (defmacro * (&rest rst)*5431,207814 (defun conjugate (x)conjugate5460,208479 (defmacro prog2$ (x y)prog2$5481,209030 (deflabel OtherOther5558,211949 (deflabel acl2-helpacl2-help5568,212251 (defmacro ec-call1-raw (ign x)ec-call1-raw5581,212678 (defmacro ec-call1 (ign x)ec-call15615,214378 (defmacro ec-call (x)ec-call5623,214653 (defmacro non-exec (x)non-exec5770,220482 (defmacro / (x &optional (y 'nil binary-casep))/5868,223535 (defaxiom closureclosure5906,224432 (defaxiom Associativity-of-+Associativity-of-+5913,224589 (defaxiom Commutativity-of-+Commutativity-of-+5916,224658 (defun fix (x)fix5919,224715 (defaxiom Unicity-of-0Unicity-of-05939,225217 (defaxiom Inverse-of-+Inverse-of-+5943,225277 (defaxiom Associativity-of-*Associativity-of-*5946,225326 (defaxiom Commutativity-of-*Commutativity-of-*5949,225395 (defaxiom Unicity-of-1Unicity-of-15952,225452 (defaxiom Inverse-of-*Inverse-of-*5956,225512 (defaxiom DistributivityDistributivity5961,225639 (defaxiom <-on-others<-on-others5965,225719 (defaxiom ZeroZero5970,225808 (defaxiom TrichotomyTrichotomy5974,225861 (defaxiom PositivePositive5986,226131 (defaxiom Rational-implies1Rational-implies15996,226412 (defaxiom Rational-implies2Rational-implies26003,226611 (defaxiom integer-implies-rationalinteger-implies-rational6011,226844 (defaxiom rational-implies-realrational-implies-real6016,226965 (defaxiom complex-implies1complex-implies16022,227114 (defaxiom complex-definitioncomplex-definition6029,227298 (defaxiom nonzero-imagpartnonzero-imagpart6040,227635 (defaxiom realpart-imagpart-elimrealpart-imagpart-elim6045,227766 (defaxiom realpart-complexrealpart-complex6054,228053 (defaxiom imagpart-compleximagpart-complex6062,228283 (defthm complex-equalcomplex-equal6070,228507 (defun force (x)force6089,229215 (defconst *force-xnume**force-xnume*6228,237055 (defun immediate-force-modep ()immediate-force-modep6235,237176 (defconst *immediate-force-modep-xnume**immediate-force-modep-xnume*6284,239081 (defun case-split (x)case-split6287,239145 (defmacro disable-forcing nildisable-forcing6349,241713 (defmacro enable-forcing nilenable-forcing6372,242460 (defmacro disable-immediate-force-modep ()disable-immediate-force-modep6397,243258 (defmacro enable-immediate-force-modep ()enable-immediate-force-modep6423,244196 (defun synp (vars form term)synp6449,245120 (defmacro syntaxp (form)syntaxp6485,246766 (deflabel syntaxp-examplessyntaxp-examples6715,257753 (defmacro bind-free (form &optional (vars))bind-free6918,264265 (deflabel bind-free-examplesbind-free-examples7221,277543 (defun extra-info (x y)extra-info7408,284547 (defconst *extra-info-fn**extra-info-fn*7415,284709 (deflabel rule-classesrule-classes7430,285381 (defun tau-system (x)tau-system7723,303021 (defconst *tau-status-boot-strap-settings**tau-status-boot-strap-settings*8165,323041 (defconst *tau-system-xnume**tau-system-xnume*8184,324024 (defconst *tau-acl2-numberp-pair* '(0 . ACL2-NUMBERP))*tau-acl2-numberp-pair*8188,324149 (defconst *tau-integerp-pair**tau-integerp-pair*8189,324204 (defconst *tau-rationalp-pair**tau-rationalp-pair*8194,324323 (defconst *tau-natp-pair**tau-natp-pair*8199,324445 (defconst *tau-posp-pair**tau-posp-pair*8204,324554 (defconst *tau-minusp-pair**tau-minusp-pair*8209,324663 (defconst *tau-booleanp-pair**tau-booleanp-pair*8214,324778 (defaxiom nonnegative-productnonnegative-product8238,325747 (defaxiom Integer-0Integer-08268,326743 (defaxiom Integer-1Integer-18272,326800 (defaxiom Integer-stepInteger-step8276,326857 (defaxiom Lowest-TermsLowest-Terms8282,326999 (defthm basic-tau-rulesbasic-tau-rules8310,328045 (defaxiom car-cdr-elimcar-cdr-elim8363,329881 (defaxiom car-cons (equal (car (cons x y)) x))car-cons8368,329994 (defaxiom cdr-cons (equal (cdr (cons x y)) y))cdr-cons8370,330042 (defaxiom cons-equalcons-equal8372,330090 (defaxiom booleanp-characterpbooleanp-characterp8385,330495 (defaxiom characterp-pagecharacterp-page8389,330575 (defaxiom characterp-tabcharacterp-tab8393,330645 (defaxiom characterp-ruboutcharacterp-rubout8397,330713 (defun no-duplicatesp-eq-exec (l)no-duplicatesp-eq-exec8403,330805 (defun no-duplicatesp-eql-exec (l)no-duplicatesp-eql-exec8409,330994 (defun no-duplicatesp-equal (l)no-duplicatesp-equal8415,331183 (defmacro no-duplicatesp-eq (x)no-duplicatesp-eq8421,331369 (defthm no-duplicatesp-eq-exec-is-no-duplicatesp-equalno-duplicatesp-eq-exec-is-no-duplicatesp-equal8424,331436 (defthm no-duplicatesp-eql-exec-is-no-duplicatesp-equalno-duplicatesp-eql-exec-is-no-duplicatesp-equal8428,331564 (defmacro no-duplicatesp (x &key (test ''eql))no-duplicatesp8432,331694 (defun chk-no-duplicatesp (lst)chk-no-duplicatesp8484,333593 (defun r-eqlable-alistp (x)r-eqlable-alistp8492,333771 (defun r-symbol-alistp (x)r-symbol-alistp8512,334360 (defun rassoc-eq-exec (x alist)rassoc-eq-exec8531,334904 (defun rassoc-eql-exec (x alist)rassoc-eql-exec8539,335195 (defun rassoc-equal (x alist)rassoc-equal8547,335491 (defmacro rassoc-eq (x alist)rassoc-eq8556,335810 (defthm rassoc-eq-exec-is-rassoc-equalrassoc-eq-exec-is-rassoc-equal8559,335874 (defthm rassoc-eql-exec-is-rassoc-equalrassoc-eql-exec-is-rassoc-equal8563,335982 (defmacro rassoc (x alist &key (test ''eql))rassoc8568,336109 (defconst *standard-chars**standard-chars*8624,338451 (defun standard-char-p (x)standard-char-p8634,338920 (defun standard-char-listp (l)standard-char-listp8660,339706 (defun character-listp (l)character-listp8682,340308 (defthm character-listp-forward-to-eqlable-listpcharacter-listp-forward-to-eqlable-listp8698,340729 (defthm standard-char-listp-forward-to-character-listpstandard-char-listp-forward-to-character-listp8703,340875 (defaxiom coerce-inverse-1coerce-inverse-18708,341033 (defaxiom coerce-inverse-2coerce-inverse-28737,342065 (defaxiom character-listp-coercecharacter-listp-coerce8747,342366 (defun string (x)string8779,343796 (defun alpha-char-p (x)alpha-char-p8817,345081 (defun upper-case-p (x)upper-case-p8845,346041 (defun lower-case-p (x)lower-case-p8874,347084 (defun char-upcase (x)char-upcase8903,348126 (defun char-downcase (x)char-downcase8959,350090 (defthm lower-case-p-char-downcaselower-case-p-char-downcase9014,352105 (defthm upper-case-p-char-upcaseupper-case-p-char-upcase9019,352252 (defthm lower-case-p-forward-to-alpha-char-plower-case-p-forward-to-alpha-char-p9024,352395 (defthm upper-case-p-forward-to-alpha-char-pupper-case-p-forward-to-alpha-char-p9030,352570 (defthm alpha-char-p-forward-to-characterpalpha-char-p-forward-to-characterp9036,352745 (defthm characterp-char-downcasecharacterp-char-downcase9041,352879 (defthm characterp-char-upcasecharacterp-char-upcase9045,352982 (defun string-downcase1 (l)string-downcase19054,353307 (defthm character-listp-string-downcase-1character-listp-string-downcase-19063,353586 (defun string-downcase (x)string-downcase9067,353688 (defun string-upcase1 (l)string-upcase19094,354668 (defthm character-listp-string-upcase1-1character-listp-string-upcase1-19103,354941 (defun string-upcase (x)string-upcase9107,355040 (defun our-digit-char-p (ch radix)our-digit-char-p9128,355755 (defmacro digit-char-p (ch &optional (radix '10))digit-char-p9201,358006 (defun char-equal (x y)char-equal9224,358906 (defun atom-listp (lst)atom-listp9248,359698 (defthm atom-listp-forward-to-true-listpatom-listp-forward-to-true-listp9266,360158 (defthm eqlable-listp-forward-to-atom-listpeqlable-listp-forward-to-atom-listp9271,360288 (defun good-atom-listp (lst)good-atom-listp9276,360424 (defthm good-atom-listp-forward-to-atom-listpgood-atom-listp-forward-to-atom-listp9300,361139 (defthm characterp-nthcharacterp-nth9305,361279 (defun ifix (x)ifix9311,361431 (defun rfix (x)rfix9330,361944 (defun realfix (x)realfix9359,362998 (defun nfix (x)nfix9378,363525 (defun string-equal1 (str1 str2 i maximum)string-equal19399,364089 (defun string-equal (str1 str2)string-equal9424,365012 (defun standard-string-alistp (x)standard-string-alistp9451,365996 (defthm standard-string-alistp-forward-to-alistpstandard-string-alistp-forward-to-alistp9472,366712 (defun assoc-string-equal (str alist)assoc-string-equal9477,366858 (defdoc e0-ordinalpe0-ordinalp9513,368184 (defdoc e0-ord-<e0-ord-<9530,368986 (defun natp (x)natp9547,369802 (defthm natp-compound-recognizernatp-compound-recognizer9566,370456 (defun posp (x)posp9572,370598 (defthm posp-compound-recognizerposp-compound-recognizer9589,371177 (defun o-finp (x)o-finp9595,371318 (defmacro o-infp (x)o-infp9610,371809 (defun o-first-expt (x)o-first-expt9620,372028 (defun o-first-coeff (x)o-first-coeff9640,372715 (defun o-rst (x)o-rst9660,373423 (defun o (x y)o>9764,378142 (defmacro o<= (x y)o<=9773,378338 (defmacro o>= (x y)o>=9782,378555 (defun o-p (x)o-p9791,378775 (defthm o-p-implies-o (x y)char>15529,591888 (defun char<= (x y)char<=15549,592467 (defun char>= (x y)char>=15569,593066 (defun string<-l (l1 l2 i)string<-l15588,593654 (defun string< (str1 str2)string<15602,594081 (defun string> (str1 str2)string>15639,595121 (defun string<= (str1 str2)string<=15660,595748 (defun string>= (str1 str2)string>=15686,596613 (defun symbol-< (x y)symbol-<15711,597461 (defthm string<-l-irreflexivestring<-l-irreflexive15737,598410 (defthm string<-irreflexivestring<-irreflexive15740,598468 (defun substitute-ac (new old seq acc)substitute-ac15743,598520 (defun substitute (new old seq)substitute15757,598966 (defun sublis (alist tree)sublis15793,600310 (defun subst (new old tree)subst15823,601350 (defmacro pprogn (&rest lst)pprogn15848,602175 (defmacro progn$ (&rest rst)progn$15911,604130 (defmacro pprogn@par (&rest rst)pprogn@par15937,604841 (defparameter *acl2-unwind-protect-stack* nil)*acl2-unwind-protect-stack*16066,612457 (defmacro push-car (item place ctx)push-car16069,612522 (defmacro acl2-unwind-protect (expl body cleanup1 cleanup2)acl2-unwind-protect16087,613342 (defun-one-output acl2-unwind (n flg)acl2-unwind16193,618249 (defmacro when-logic (str x)when-logic16369,627978 (defmacro in-package (str)in-package16393,628859 (defmacro defpkg (&whole event-form name form &optional doc book-path hidden-p)defpkg16422,629708 (defdoc managing-acl2-packagesmanaging-acl2-packages16544,635544 (deflabel hidden-defpkghidden-defpkg16553,635819 (deflabel hidden-death-packagehidden-death-package16561,635951 (defmacro defun (&whole event-form &rest def)defun16623,638577 (defmacro defun-std (&whole event-form &rest def)defun-std16868,650539 (defmacro defuns (&whole event-form &rest def-lst)defuns16884,650902 (defmacro defuns-std (&whole event-form &rest def-lst)defuns-std16927,652165 (defmacro verify-termination (&rest lst)verify-termination16942,652539 (defmacro verify-termination-boot-strap (&whole event-form &rest lst)verify-termination-boot-strap17106,660607 (defmacro verify-guards (&whole event-form name &key hints otf-flg guard-debugverify-guards17116,660888 (defmacro verify-guards+ (name &rest rest)verify-guards+17440,676004 (defdoc defpundefpun17518,679780 (defmacro defmacro (&whole event-form &rest mdef)defmacro17553,681135 (defmacro defconst (&whole event-form name form &optional doc)defconst17646,685145 (defmacro defthm (&whole event-formdefthm17717,688175 (defmacro defthmd (&whole event-formdefthmd17798,691218 (defmacro defthm-std (&whole event-formdefthm-std17844,692592 (defmacro defaxiom (&whole event-form name termdefaxiom17872,693334 (defmacro deflabel (&whole event-form name &key doc)deflabel17919,695030 (deflabel theoriestheories17960,696555 (defmacro deftheory (&whole event-form name expr &key doc)deftheory18119,705050 (defmacro deftheory-static (name theory)deftheory-static18204,708362 (defmacro defstobj (&whole event-form name &rest args)defstobj18283,711721 (defmacro in-theory (&whole event-form expr &key doc)in-theory18723,733027 (defmacro in-arithmetic-theory (&whole event-form expr &key doc)in-arithmetic-theory18788,735867 (defmacro regenerate-tau-database (&whole event-form &key doc)regenerate-tau-database18847,738352 (defmacro push-untouchable (&whole event-form name fn-p &key doc)push-untouchable18926,742437 (defmacro remove-untouchable (&whole event-form name fn-p &key doc)remove-untouchable18984,744858 (defmacro set-body (&whole event-form fn name-or-rune)set-body19083,749474 (defmacro table (&whole event-form name &rest args)table19124,751086 (defmacro encapsulate (&whole event-form signatures &rest cmd-lst)encapsulate19341,762547 (defdoc redundant-encapsulateredundant-encapsulate19602,775116 (defconst *load-compiled-file-values**load-compiled-file-values*19695,779137 (defmacro include-book (&whole event-form user-book-nameinclude-book19699,779226 (defmacro make-event (&whole event-formmake-event19902,790111 (defdoc make-event-detailsmake-event-details20641,824716 (defdoc using-tables-efficientlyusing-tables-efficiently20865,836610 (defmacro record-expansion (x y)record-expansion20946,839809 (defmacro skip-proofs (x)skip-proofs21149,850424 (defmacro local (x)local21257,855972 (defmacro defchoose (&whole event-form &rest def)defchoose21317,858438 (deflabel conservativity-of-defchooseconservativity-of-defchoose21478,864638 (defmacro defattach (&whole event-form &rest args)defattach21888,884646 (defun attachment-symbol (x)attachment-symbol22405,908426 (defun set-attachment-symbol-form (fn val)set-attachment-symbol-form22412,908603 (defmacro defattach (&rest args)defattach22415,908696 (deflabel worldp) ; reserving this symbol for later useworldp22496,912477 (defun plist-worldp (alist)plist-worldp22498,912534 (defthm plist-worldp-forward-to-assoc-eq-equal-alistpplist-worldp-forward-to-assoc-eq-equal-alistp22533,913992 (defdoc getpropgetprop22538,914148 (defun putprop (symb key value world-alist)putprop22554,914590 (defparameter meter-maid-cnt 0)meter-maid-cnt22618,916865 (defun meter-maid (fn maximum &optional arg1 arg2 cnt)meter-maid22621,916915 (defconst *acl2-property-unbound* :acl2-property-unbound)*acl2-property-unbound*22639,917731 (defun getprop-default (symb key default)getprop-default22641,917790 (defun-one-output sgetprop1 (symb key default world-alist inst-world-alistsgetprop122657,918299 (defun fgetprop (symb key default world-alist)fgetprop22764,922665 (defun sgetprop (symb key default world-name world-alist)sgetprop22822,925250 (defun ordered-symbol-alistp (x)ordered-symbol-alistp22860,926863 (defthm ordered-symbol-alistp-forward-to-symbol-alistpordered-symbol-alistp-forward-to-symbol-alistp22878,927414 (defun add-pair (key value l)add-pair22883,927572 (defun delete-assoc-eq-exec (key alist)delete-assoc-eq-exec22897,927987 (defun delete-assoc-eql-exec (key alist)delete-assoc-eql-exec22905,928310 (defun delete-assoc-equal (key alist)delete-assoc-equal22913,928638 (defmacro delete-assoc-eq (key lst)delete-assoc-eq22919,928863 (defthm delete-assoc-eq-exec-is-delete-assoc-equaldelete-assoc-eq-exec-is-delete-assoc-equal22922,928939 (defthm delete-assoc-eql-exec-is-delete-assoc-equaldelete-assoc-eql-exec-is-delete-assoc-equal22926,929071 (defmacro delete-assoc (key alist &key (test ''eql))delete-assoc22930,929205 (defun getprops1 (alist)getprops122983,931418 (defun getprops (symb world-name world-alist)getprops22998,932039 (defthm equal-char-codeequal-char-code23043,933828 (defun has-propsp1 (alist exceptions known-unbound)has-propsp123057,934229 (defun has-propsp (symb exceptions world-name world-alist known-unbound)has-propsp23078,935176 (defun extend-world (name wrld)extend-world23110,936542 (defun retract-world (name wrld)retract-world23130,937209 (defun global-val (var wrld)global-val23150,937878 (defun function-symbolp (sym wrld)function-symbolp23167,938547 (defun translate-declaration-to-guard/integer (lo var hi)translate-declaration-to-guard/integer23188,939611 (defun weak-satisfies-type-spec-p (x)weak-satisfies-type-spec-p23234,941509 (defun translate-declaration-to-guard1 (x var wrld)translate-declaration-to-guard123248,942020 (defun translate-declaration-to-guard (x var wrld)translate-declaration-to-guard23434,949556 (defun translate-declaration-to-guard-lst (l var wrld)translate-declaration-to-guard-lst23507,952982 (deflabel declaredeclare23533,953860 (deflabel type-spectype-spec23595,956277 (defun the-check (guard x y)the-check23672,959633 (defun the-fn (x y)the-fn23682,960029 (defmacro the (x y)the23732,962447 (defconst *maximum-positive-32-bit-integer**maximum-positive-32-bit-integer*23859,968623 (defconst *our-array-total-size-limit**our-array-total-size-limit*23863,968705 (defun-one-output chk-make-array$ (dimensions form)chk-make-array$23875,969137 (defmacro make-array$ (&whole form dimensions &rest args)make-array$23908,970609 (defparameter *acl2-array-cache**acl2-array-cache*24004,975423 (defmacro set-acl2-array-property (name prop)set-acl2-array-property24012,975691 (defmacro get-acl2-array-property (name)get-acl2-array-property24061,977492 (defun bounded-integer-alistp (l n)bounded-integer-alistp24089,978482 (defthm bounded-integer-alistp-forward-to-eqlable-alistpbounded-integer-alistp-forward-to-eqlable-alistp24105,979055 (defun keyword-value-listp (l)keyword-value-listp24110,979219 (defthm keyword-value-listp-forward-to-true-listpkeyword-value-listp-forward-to-true-listp24128,979762 (defun assoc-keyword (key l)assoc-keyword24133,979910 (defthm keyword-value-listp-assoc-keywordkeyword-value-listp-assoc-keyword24155,980633 (defthm consp-assoc-equalconsp-assoc-equal24161,980863 (defmacro f-get-global (x st)f-get-global24181,981738 (defun our-import (syms pkg)our-import24219,982850 (defvar *defpkg-virgins* nil)*defpkg-virgins*24238,983868 (defun check-proposed-imports (name package-entry proposed-imports)check-proposed-imports24240,983899 (defun-one-output defpkg-raw1 (name imports book-path event-form)defpkg-raw124299,986872 (defun package-has-no-imports (name)package-has-no-imports24393,990887 (defmacro maybe-make-package (name)maybe-make-package24401,991128 (defmacro maybe-introduce-empty-pkg-1 (name)maybe-introduce-empty-pkg-124485,994181 (defmacro maybe-introduce-empty-pkg-2 (name)maybe-introduce-empty-pkg-224530,996163 (defmacro defpkg-raw (name imports book-path event-form)defpkg-raw24538,996484 (defun-one-output slow-array-warning (fn nm)slow-array-warning24568,997743 (deflabel arraysarrays24585,998434 (deflabel arrays-examplearrays-example25000,1020882 (deflabel slow-array-warningslow-array-warning25080,1023727 (defun array1p (name l)array1p25184,1029157 (defthm array1p-forwardarray1p-forward25235,1031469 (defthm array1p-lineararray1p-linear25255,1032630 (defun bounded-integer-alistp2 (l i j)bounded-integer-alistp225264,1033134 (defun assoc2 (i j l)assoc225283,1033964 (defun array2p (name l)array2p25295,1034269 (defthm array2p-forwardarray2p-forward25338,1035999 (defthm array2p-lineararray2p-linear25363,1037564 (defun header (name l)header25376,1038295 (defun dimensions (name l)dimensions25408,1039285 (defun maximum-length (name l)maximum-length25435,1040375 (defun default (name l)default25459,1041268 (defun aref1 (name l n)aref125507,1043537 (defun compress11 (name l i n default)compress1125558,1045317 (defconstant *invisible-array-mark* 'acl2_invisible::|An Invisible Array Mark|)*invisible-array-mark*25573,1045908 (defun array-order (header)array-order25575,1045989 (defun compress1 (name l)compress125588,1046382 (defthm array1p-consarray1p-cons25855,1059244 (defun aset1 (name l n val)aset125865,1059602 (defun aref2 (name l i j)aref225989,1065698 (defun compress211 (name l i x j default)compress21126036,1067392 (defun compress21 (name l n i j default)compress2126052,1068030 (defun compress2 (name l)compress226065,1068516 (defthm array2p-consarray2p-cons26243,1076477 (defun aset2 (name l i j val)aset226254,1076855 (defun flush-compress (name)flush-compress26367,1081729 (defparameter *return-values**return-values*26528,1088064 (defmacro declare-return-values ()declare-return-values26535,1088253 (defun declare-return-values1 ()declare-return-values126538,1088331 (defun in-akcl-with-mv-set-and-ref ()in-akcl-with-mv-set-and-ref26549,1088553 (defconstant *akcl-mv-ref-and-set-inclusive-upper-bound* 9)*akcl-mv-ref-and-set-inclusive-upper-bound*26552,1088628 (defmacro special-location (i)special-location26554,1088689 (defmacro set-mv (i v)set-mv26564,1089098 (defmacro mv-ref (i)mv-ref26577,1089564 (defun mv-refs-fn (i)mv-refs-fn26589,1089996 (defmacro mv-refs (i)mv-refs26597,1090130 (defun cdrn (x i)cdrn26614,1090553 (defun mv-nth (n l)mv-nth26620,1090719 (defun make-mv-nths (args call i)make-mv-nths26672,1092740 (defun mv-bindings (lst)mv-bindings26680,1093052 (defun mv-set-mvs (bindings i)mv-set-mvs26690,1093376 (defmacro mv (&rest l)mv26695,1093541 (defmacro mv? (&rest l)mv?26843,1099096 (defmacro mv-let (&rest rst)mv-let26914,1101460 (defmacro mv?-let (vars form &rest rst)mv?-let27068,1108086 (defun mv-list (input-arity x)mv-list27124,1109721 (defmacro mv-list (input-arity x)mv-list27169,1111503 (defmacro mv-list (input-arity x)mv-list27174,1111653 (deflabel statestate27177,1111730 (defdoc programming-with-stateprogramming-with-state27352,1120894 (defdoc error-tripleserror-triples27887,1144010 (defun update-nth (key val l)update-nth27915,1145195 (defun update-nth-array (j key val l)update-nth-array27958,1146515 (defmacro maximum-positive-32-bit-integer ()maximum-positive-32-bit-integer27969,1146945 (defmacro maximum-positive-32-bit-integer-minus-1 ()maximum-positive-32-bit-integer-minus-127972,1147028 (defun 32-bit-integerp (x)32-bit-integerp27975,1147130 (defthm 32-bit-integerp-forward-to-integerp32-bit-integerp-forward-to-integerp27981,1147313 (defun acl2-number-listp (l)acl2-number-listp27986,1147449 (defthm acl2-number-listp-forward-to-true-listpacl2-number-listp-forward-to-true-listp28003,1147873 (defun rational-listp (l)rational-listp28008,1148017 (defthm rational-listp-forward-to-acl2-number-listprational-listp-forward-to-acl2-number-listp28025,1148447 (defun real-listp (l)real-listp28033,1148680 (defdoc real-listpreal-listp28040,1148844 (defthm real-listp-forward-to-acl2-number-listpreal-listp-forward-to-acl2-number-listp28052,1149193 (defun integer-listp (l)integer-listp28057,1149337 (defthm integer-listp-forward-to-rational-listpinteger-listp-forward-to-rational-listp28074,1149747 (defun nat-listp (l)nat-listp28079,1149891 (defthm nat-listp-forward-to-integer-listpnat-listp-forward-to-integer-listp28096,1150300 (defthm rational-listp-forward-to-real-listprational-listp-forward-to-real-listp28104,1150527 (defun 32-bit-integer-listp (l)32-bit-integer-listp28109,1150665 (defthm 32-bit-integer-listp-forward-to-integer-listp32-bit-integer-listp-forward-to-integer-listp28115,1150853 (defun open-input-channels (st)open-input-channels28124,1151204 (defun update-open-input-channels (x st)update-open-input-channels28128,1151294 (defun open-output-channels (st)open-output-channels28132,1151402 (defun update-open-output-channels (x st)update-open-output-channels28136,1151493 (defun global-table (st)global-table28140,1151602 (defun update-global-table (x st)update-global-table28144,1151685 (defun t-stack (st)t-stack28148,1151786 (defun update-t-stack (x st)update-t-stack28152,1151864 (defun 32-bit-integer-stack (st)32-bit-integer-stack28156,1151960 (defun update-32-bit-integer-stack (x st)update-32-bit-integer-stack28160,1152051 (defun big-clock-entry (st)big-clock-entry28164,1152160 (defun update-big-clock-entry (x st)update-big-clock-entry28168,1152246 (defun idates (st)idates28172,1152350 (defun update-idates (x st)update-idates28176,1152427 (defun acl2-oracle (st)acl2-oracle28180,1152522 (defun update-acl2-oracle (x st)update-acl2-oracle28184,1152604 (defun file-clock (st)file-clock28188,1152704 (defun update-file-clock (x st)update-file-clock28192,1152785 (defun readable-files (st)readable-files28196,1152884 (defun written-files (st)written-files28200,1152969 (defun update-written-files (x st)update-written-files28204,1153054 (defun read-files (st)read-files28208,1153157 (defun update-read-files (x st)update-read-files28212,1153239 (defun writeable-files (st)writeable-files28216,1153339 (defun list-all-package-names-lst (st)list-all-package-names-lst28220,1153426 (defun update-list-all-package-names-lst (x st)update-list-all-package-names-lst28224,1153524 (defun user-stobj-alist1 (st)user-stobj-alist128232,1153852 (defun update-user-stobj-alist1 (x st)update-user-stobj-alist128236,1153941 (defconst *initial-raw-arity-alist**initial-raw-arity-alist*28241,1154068 (defconst *initial-checkpoint-processors**initial-checkpoint-processors*28267,1154795 (defconst *primitive-program-fns-with-raw-code**primitive-program-fns-with-raw-code*28286,1155533 (defconst *primitive-logic-fns-with-raw-code**primitive-logic-fns-with-raw-code*28399,1159724 (defconst *primitive-macros-with-raw-code**primitive-macros-with-raw-code*28559,1166091 (defmacro with-live-state (form)with-live-state28652,1169692 (defun init-iprint-ar (hard-bound enabledp)init-iprint-ar28761,1173795 (defconst *iprint-soft-bound-default* 1000)*iprint-soft-bound-default*28793,1175188 (defconst *iprint-hard-bound-default* 10000)*iprint-hard-bound-default*28794,1175232 (defdoc parallelismparallelism28796,1175278 (defdoc parallel-programmingparallel-programming28833,1177233 (defdoc parallel-proofparallel-proof28871,1179128 (defun default-total-parallelism-work-limit ()default-total-parallelism-work-limit28884,1179556 (defconst *fmt-soft-right-margin-default* 65)*fmt-soft-right-margin-default*28962,1183525 (defconst *fmt-hard-right-margin-default* 77)*fmt-hard-right-margin-default*28963,1183571 (defconst *initial-global-table**initial-global-table*28965,1183618 (defun all-boundp (alist1 alist2)all-boundp29257,1196385 (defun known-package-alistp (x)known-package-alistp29265,1196656 (defthm known-package-alistp-forward-to-true-list-listp-and-alistpknown-package-alistp-forward-to-true-list-listp-and-alistp29276,1197037 (defun timer-alistp (x)timer-alistp29282,1197241 (defthm timer-alistp-forward-to-true-list-listp-and-symbol-alistptimer-alistp-forward-to-true-list-listp-and-symbol-alistp29294,1197553 (defun typed-io-listp (l typ)typed-io-listp29300,1197755 (defthm typed-io-listp-forward-to-true-listptyped-io-listp-forward-to-true-listp29312,1198206 (defconst *file-types* '(:character :byte :object))*file-types*29317,1198348 (defun open-channel1 (l)open-channel129319,1198401 (defthm open-channel1-forward-to-true-listp-and-conspopen-channel1-forward-to-true-listp-and-consp29333,1198816 (defun open-channel-listp (l)open-channel-listp29339,1198994 (defun open-channels-p (x)open-channels-p29352,1199351 (defthm open-channels-p-forwardopen-channels-p-forward29357,1199473 (defun file-clock-p (x)file-clock-p29363,1199652 (defthm file-clock-p-forward-to-integerpfile-clock-p-forward-to-integerp29367,1199718 (defun readable-file (x)readable-file29372,1199844 (defthm readable-file-forward-to-true-listp-and-conspreadable-file-forward-to-true-listp-and-consp29384,1200206 (defun readable-files-listp (x)readable-files-listp29390,1200384 (defthm readable-files-listp-forward-to-true-list-listp-and-alistpreadable-files-listp-forward-to-true-list-listp-and-alistp29396,1200570 (defun readable-files-p (x)readable-files-p29402,1200774 (defthm readable-files-p-forward-to-readable-files-listpreadable-files-p-forward-to-readable-files-listp29406,1200860 (defun written-file (x)written-file29411,1201022 (defthm written-file-forward-to-true-listp-and-conspwritten-file-forward-to-true-listp-and-consp29424,1201421 (defun written-file-listp (x)written-file-listp29430,1201597 (defthm written-file-listp-forward-to-true-list-listp-and-alistpwritten-file-listp-forward-to-true-list-listp-and-alistp29436,1201778 (defun written-files-p (x)written-files-p29442,1201978 (defthm written-files-p-forward-to-written-file-listpwritten-files-p-forward-to-written-file-listp29446,1202061 (defun read-file-listp1 (x)read-file-listp129451,1202217 (defthm read-file-listp1-forward-to-true-listp-and-conspread-file-listp1-forward-to-true-listp-and-consp29460,1202447 (defun read-file-listp (x)read-file-listp29466,1202631 (defthm read-file-listp-forward-to-true-list-listpread-file-listp-forward-to-true-list-listp29472,1202810 (defun read-files-p (x)read-files-p29477,1202960 (defthm read-files-p-forward-to-read-file-listpread-files-p-forward-to-read-file-listp29481,1203037 (defun writable-file-listp1 (x)writable-file-listp129486,1203181 (defthm writable-file-listp1-forward-to-true-listp-and-conspwritable-file-listp1-forward-to-true-listp-and-consp29494,1203386 (defun writable-file-listp (x)writable-file-listp29500,1203578 (defthm writable-file-listp-forward-to-true-list-listpwritable-file-listp-forward-to-true-list-listp29506,1203769 (defun writeable-files-p (x)writeable-files-p29511,1203927 (defthm writeable-files-p-forward-to-writable-file-listpwriteable-files-p-forward-to-writable-file-listp29515,1204013 (defun state-p1 (x)state-p129520,1204175 (defthm state-p1-forwardstate-p1-forward29555,1205605 (defun state-p (state-state)state-p29598,1207501 (defthm state-p-implies-and-forward-to-state-p1state-p-implies-and-forward-to-state-p129608,1207758 (defmacro build-statebuild-state29701,1213075 (defconst *default-state**default-state*29725,1214008 (defun build-state1 (open-input-channelsbuild-state129730,1214142 (defun coerce-state-to-object (x)coerce-state-to-object29758,1215402 (defun coerce-object-to-state (x)coerce-object-to-state29762,1215471 (defun-one-output strip-numeric-postfix (sym)strip-numeric-postfix29772,1215644 (defun global-table-cars1 (state-state)global-table-cars129780,1215884 (defun global-table-cars (state-state)global-table-cars29805,1217028 (defun boundp-global1 (x state-state)boundp-global129812,1217232 (defun boundp-global (x state-state)boundp-global29821,1217558 (defmacro f-boundp-global (x st)f-boundp-global29829,1217806 (defun makunbound-global (x state-state)makunbound-global29845,1218338 (defun get-global (x state-state)get-global29874,1219397 (defun put-global (key value state-state)put-global29886,1219812 (defmacro f-put-global (key value st)f-put-global29907,1220632 (defmacro f-put-global@par (key value st)f-put-global@par29970,1223231 (defconst *initial-ld-special-bindings**initial-ld-special-bindings*29988,1223833 (defun always-boundp-global (x)always-boundp-global30012,1224688 (defun state-global-let*-bindings-p (lst)state-global-let*-bindings-p30019,1224885 (defun state-global-let*-get-globals (bindings)state-global-let*-get-globals30036,1225516 (defun state-global-let*-put-globals (bindings)state-global-let*-put-globals30065,1227066 (defun state-global-let*-cleanup (bindings index)state-global-let*-cleanup30098,1228793 (defparameter *possible-parallelism-hazards**possible-parallelism-hazards*30149,1231682 (defmacro with-parallelism-hazard-warnings (body)with-parallelism-hazard-warnings30190,1233045 (defmacro warn-about-parallelism-hazard (call body)warn-about-parallelism-hazard30200,1233295 (defmacro with-ensured-parallelism-finishing (form)with-ensured-parallelism-finishing30257,1236118 (defmacro state-global-let* (bindings body)state-global-let*30270,1236516 (defmacro state-free-global-let* (bindings body)state-free-global-let*30390,1241893 (defun integer-range-p (lower upper x)integer-range-p30433,1243916 (defun signed-byte-p (bits x)signed-byte-p30459,1244951 (defun unsigned-byte-p (bits x)unsigned-byte-p30484,1245789 (defthm integer-range-p-forwardinteger-range-p-forward30512,1246682 (defthm signed-byte-p-forward-to-integerpsigned-byte-p-forward-to-integerp30520,1246932 (defthm unsigned-byte-p-forward-to-nonnegative-integerpunsigned-byte-p-forward-to-nonnegative-integerp30525,1247066 (defmacro the-fixnum (n)the-fixnum30533,1247329 (defun-one-output zpf (x)zpf30537,1247407 (defun zpf (x)zpf30541,1247516 (defmacro logand (&rest args)logand30790,1255710 (defmacro logeqv (&rest args)logeqv30813,1256442 (defmacro logior (&rest args)logior30836,1257186 (defmacro logxor (&rest args)logxor30859,1257930 (defun integer-length (i)integer-length30882,1258674 (defun binary-logand (i j)binary-logand30911,1259654 (defun lognand (i j)lognand30925,1260121 (defun binary-logior (i j)binary-logior30944,1260697 (defun logorc1 (i j)logorc130950,1260874 (defun logorc2 (i j)logorc230971,1261558 (defun logandc1 (i j)logandc130992,1262243 (defun logandc2 (i j)logandc231013,1262917 (defun binary-logeqv (i j)binary-logeqv31033,1263575 (defun binary-logxor (i j)binary-logxor31039,1263742 (defun lognor (i j)lognor31045,1263901 (defun logtest (x y)logtest31066,1264517 (defconst *BOOLE-1* 0)*BOOLE-1*31090,1265257 (defconst *BOOLE-2* 1)*BOOLE-2*31091,1265285 (defconst *BOOLE-AND* 2)*BOOLE-AND*31092,1265313 (defconst *BOOLE-ANDC1* 3)*BOOLE-ANDC1*31093,1265341 (defconst *BOOLE-ANDC2* 4)*BOOLE-ANDC2*31094,1265369 (defconst *BOOLE-C1* 5)*BOOLE-C1*31095,1265397 (defconst *BOOLE-C2* 6)*BOOLE-C2*31096,1265425 (defconst *BOOLE-CLR* 7)*BOOLE-CLR*31097,1265453 (defconst *BOOLE-EQV* 8)*BOOLE-EQV*31098,1265481 (defconst *BOOLE-IOR* 9)*BOOLE-IOR*31099,1265509 (defconst *BOOLE-NAND* 10)*BOOLE-NAND*31100,1265537 (defconst *BOOLE-NOR* 11)*BOOLE-NOR*31101,1265565 (defconst *BOOLE-ORC1* 12)*BOOLE-ORC1*31102,1265593 (defconst *BOOLE-ORC2* 13)*BOOLE-ORC2*31103,1265621 (defconst *BOOLE-SET* 14)*BOOLE-SET*31104,1265649 (defconst *BOOLE-XOR* 15)*BOOLE-XOR*31105,1265677 (defun boole$ (op i1 i2)boole$31107,1265706 (deflabel ioio31177,1268288 (defdoc output-to-fileoutput-to-file31420,1279238 (defdoc *standard-co**standard-co*31475,1281461 (defdoc *standard-oi**standard-oi*31514,1283534 (defdoc *standard-ci**standard-ci*31528,1284114 (defdoc print-controlprint-control31544,1284722 (defdoc character-encodingcharacter-encoding31643,1290006 (defun set-forms-from-bindings (bindings)set-forms-from-bindings31690,1292193 (defconst *print-control-defaults**print-control-defaults*31702,1292653 (defun alist-difference-eq (alist1 alist2)alist-difference-eq31727,1293947 (defmacro with-print-defaults (bindings form)with-print-defaults31743,1294491 (defmacro reset-print-control ()reset-print-control31752,1294962 (defun digit-to-char (n)digit-to-char31756,1295073 (defun print-base-p (print-base)print-base-p31797,1296087 (defun explode-nonnegative-integer (n print-base ans)explode-nonnegative-integer31804,1296240 (defthm true-listp-explode-nonnegative-integertrue-listp-explode-nonnegative-integer31852,1297860 (defun explode-atom (x print-base)explode-atom31881,1298784 (defun explode-atom+ (x print-base print-radix)explode-atom+31925,1300435 (defthm true-list-listp-forward-to-true-listp-assoc-equaltrue-list-listp-forward-to-true-listp-assoc-equal31961,1301902 (defthm true-listp-cadr-assoc-eq-for-open-channels-ptrue-listp-cadr-assoc-eq-for-open-channels-p31980,1302870 (defun open-input-channel-p1 (channel typ state-state)open-input-channel-p131995,1303384 (defun open-output-channel-p1 (channel typ state-state)open-output-channel-p132010,1304047 (defun open-input-channel-p (channel typ state-state)open-input-channel-p32024,1304689 (defun open-output-channel-p (channel typ state-state)open-output-channel-p32030,1304958 (defun open-output-channel-any-p1 (channel state-state)open-output-channel-any-p132036,1305229 (defun open-output-channel-any-p (channel state-state)open-output-channel-any-p32043,1305570 (defun open-input-channel-any-p1 (channel state-state)open-input-channel-any-p132048,1305782 (defun open-input-channel-any-p (channel state-state)open-input-channel-any-p32055,1306119 (defmacro print-case ()print-case32060,1306329 (defmacro acl2-print-case (&optional (st 'state))acl2-print-case32069,1306594 (defun set-print-case (case state)set-print-case32072,1306666 (defmacro set-acl2-print-case (case)set-acl2-print-case32112,1308652 (defmacro print-base (&optional (st 'state))print-base32118,1308849 (defmacro acl2-print-base (&optional (st 'state))acl2-print-base32121,1308930 (defmacro print-radix (&optional (st 'state))print-radix32124,1309002 (defmacro acl2-print-radix (&optional (st 'state))acl2-print-radix32127,1309085 (defun check-print-base (print-base ctx)check-print-base32130,1309159 (defun set-print-base (base state)set-print-base32187,1312001 (defmacro set-acl2-print-base (base)set-acl2-print-base32227,1313306 (defun set-print-circle (x state)set-print-circle32233,1313503 (defun set-print-escape (x state)set-print-escape32237,1313621 (defun set-print-pretty (x state)set-print-pretty32241,1313739 (defun set-print-radix (x state)set-print-radix32245,1313857 (defun set-print-readably (x state)set-print-readably32287,1315054 (defun check-null-or-natp (n fn)check-null-or-natp32291,1315176 (defun set-print-length (n state)set-print-length32302,1315524 (defun set-print-level (n state)set-print-level32308,1315761 (defun set-print-lines (n state)set-print-lines32314,1315995 (defun set-print-right-margin (n state)set-print-right-margin32320,1316229 (defmacro get-input-stream-from-channel (channel)get-input-stream-from-channel32327,1316501 (defmacro get-output-stream-from-channel (channel)get-output-stream-from-channel32334,1316691 (defmacro with-print-controls (default bindings &rest body)with-print-controls32341,1316883 (defun princ$ (x channel state-state)princ$32405,1320075 (defun write-byte$ (x channel state-state)write-byte$32574,1327171 (defvar *print-circle-stream* nil)*print-circle-stream*32603,1328339 (defmacro er (severity context str &rest str-args)er32605,1328375 (defmacro er@par (severity context str &rest str-args)er@par32721,1334037 (defun get-serialize-character (state)get-serialize-character32744,1334962 (defun w (state)w32749,1335169 (defun hons-enabledp (state)hons-enabledp32761,1335696 (defun set-serialize-character (c state)set-serialize-character32766,1335859 (defun print-object$-ser (x serialize-character channel state-state)print-object$-ser32793,1337045 (defthm all-boundp-preserves-assoc-equalall-boundp-preserves-assoc-equal32847,1339325 (defun print-object$ (x channel state)print-object$32864,1339894 (defparameter *file-clock* 1)*file-clock*32900,1341600 (defun make-input-channel (file-name clock)make-input-channel32903,1341644 (defun make-output-channel (file-name clock)make-output-channel32917,1342154 (defun-one-output setup-standard-io ()setup-standard-io32939,1343168 (defun-one-output lisp-book-syntaxp1 (s stream)lisp-book-syntaxp132979,1344660 (defun-one-output lisp-book-syntaxp (file)lisp-book-syntaxp33060,1347783 (defparameter *parser* nil)*parser*33085,1348919 (defun-one-output parse-infix-file (infile outfile)parse-infix-file33098,1349389 (defun open-input-channel (file-name typ state-state)open-input-channel33128,1350631 (defthm nth-update-nthnth-update-nth33243,1355801 (defthm true-listp-update-nthtrue-listp-update-nth33250,1355987 (defthm nth-update-nth-arraynth-update-nth-array33263,1356337 (defun close-input-channel (channel state-state)close-input-channel33269,1356518 (defun open-output-channel (file-name typ state-state)open-output-channel33314,1358412 (defun open-output-channel! (file-name typ state)open-output-channel!33414,1363507 (defmacro assert$ (test form)assert$33545,1368246 (defun fmt-to-comment-window (str alist col evisc-tuple)fmt-to-comment-window33569,1369050 (defun fmt-to-comment-window! (str alist col evisc-tuple)fmt-to-comment-window!33620,1371146 (defun pairlis2 (x y)pairlis233634,1371508 (defmacro cw (str &rest args)cw33642,1371792 (defmacro cw! (str &rest args)cw!33763,1376284 (defun subseq-list (lst start end)subseq-list33782,1376935 (defun subseq (seq start end)subseq33793,1377302 (defun lock-symbol-name-p (lock-symbol)lock-symbol-name-p33833,1379069 (defun assign-lock (key)assign-lock33842,1379361 (defmacro with-lock (bound-symbol &rest forms)with-lock33863,1380039 (defmacro deflock (lock-symbol)deflock33874,1380438 (defun get-output-stream-string$-fn (channel state-state)get-output-stream-string$-fn33918,1382188 (defmacro get-output-stream-string$ (channel state-stateget-output-stream-string$33964,1384200 (defun close-output-channel (channel state-state)close-output-channel33984,1385128 (defun maybe-finish-output$ (channel state)maybe-finish-output$34090,1389943 (defmacro legal-acl2-character-p (x)legal-acl2-character-p34110,1390653 (defun read-char$ (channel state-state)read-char$34126,1391201 (defun peek-char$ (channel state-state)peek-char$34161,1392676 (defun read-byte$ (channel state-state)read-byte$34187,1393726 (defun-one-output parse-infix-from-terminal (eof)parse-infix-from-terminal34216,1394875 (defparameter *acl2-read-suppress* nil)*acl2-read-suppress*34241,1395930 (defun read-object (channel state-state)read-object34243,1395971 (defun read-object-suppress (channel state)read-object-suppress34339,1400278 (defconst *suspiciously-first-numeric-chars**suspiciously-first-numeric-chars*34356,1401036 (defconst *suspiciously-first-hex-chars**suspiciously-first-hex-chars*34363,1401236 (defconst *base-10-chars**base-10-chars*34373,1401488 (defconst *hex-chars**hex-chars*34380,1401630 (defconst *letter-chars**letter-chars*34389,1401820 (defconst *slashable-chars**slashable-chars*34399,1402140 (defun some-slashable (l)some-slashable34407,1402425 (defun prin1-with-slashes1 (l slash-char channel state)prin1-with-slashes134415,1402630 (defun prin1-with-slashes (s slash-char channel state)prin1-with-slashes34434,1403369 (defmacro suspiciously-first-numeric-chars (print-base)suspiciously-first-numeric-chars34460,1404456 (defmacro numeric-chars (print-base)numeric-chars34465,1404621 (defun may-need-slashes1 (lst flg potnum-chars)may-need-slashes134470,1404729 (defmacro potential-numberp (s0 n0 print-base)potential-numberp34494,1405761 (defun may-need-slashes-fn (x print-base)may-need-slashes-fn34556,1408187 (defmacro may-need-slashes (x &optional (print-base '10))may-need-slashes34805,1419128 (defun needs-slashes (x state)needs-slashes34814,1419522 (defparameter *t-stack* (make-array$ 5))*t-stack*34830,1420050 (defparameter *t-stack-length* 0)*t-stack-length*34832,1420092 (defun t-stack-length1 (state-state)t-stack-length134837,1420131 (defun t-stack-length (state-state)t-stack-length34848,1420466 (defun make-list-ac (n val ac)make-list-ac34855,1420664 (defmacro make-list (size &key initial-element)make-list34862,1420872 (defun extend-t-stack (n val state-state)extend-t-stack34879,1421504 (defconst *directory-separator**directory-separator*34954,1424170 (defconst *directory-separator-string**directory-separator-string*34957,1424210 (defmacro os-er (os fnname)os-er34960,1424284 (defun os (wrld)os34968,1424552 (defun mswindows-drive1 (filename)mswindows-drive134978,1424790 (defun mswindows-drive (filename state)mswindows-drive35003,1425995 (defun pathname-os-to-unix (str os state)pathname-os-to-unix35018,1426617 (defun ccl-at-least-1-3-p ()ccl-at-least-1-3-p35059,1428127 (defun pathname-unix-to-os (str state)pathname-unix-to-os35066,1428438 (defun shrink-t-stack (n state-state)shrink-t-stack35120,1430827 (defun aref-t-stack (i state-state)aref-t-stack35145,1431684 (defun aset-t-stack (i val state-state)aset-t-stack35160,1432198 (defparameter *32-bit-integer-stack**32-bit-integer-stack*35188,1432989 (defparameter *32-bit-integer-stack-length* 0)*32-bit-integer-stack-length*35191,1433078 (defun 32-bit-integer-stack-length1 (state-state)32-bit-integer-stack-length135195,1433129 (defun 32-bit-integer-stack-length (state-state)32-bit-integer-stack-length35203,1433438 (defun extend-32-bit-integer-stack (n val state-state)extend-32-bit-integer-stack35210,1433662 (defun shrink-32-bit-integer-stack (n state-state)shrink-32-bit-integer-stack35258,1435980 (defun aref-32-bit-integer-stack (i state-state)aref-32-bit-integer-stack35289,1437110 (defun aset-32-bit-integer-stack (i val state-state)aset-32-bit-integer-stack35312,1437880 (defmacro f-big-clock-negative-p (st)f-big-clock-negative-p35341,1438909 (defmacro f-decrement-big-clock (st)f-decrement-big-clock35350,1439147 (defun big-clock-negative-p (state-state)big-clock-negative-p35369,1439731 (defun decrement-big-clock (state-state)decrement-big-clock35389,1440613 (defun list-all-package-names (state-state)list-all-package-names35414,1441567 (defun user-stobj-alist (state-state)user-stobj-alist35430,1442167 (defun update-user-stobj-alist (x state-state)update-user-stobj-alist35440,1442487 (defun power-eval (l b)power-eval35452,1442921 (defun-one-output idate ()idate35460,1443131 (defun read-idate (state-state)read-idate35472,1443443 (defun get-internal-time ()get-internal-time35492,1444117 (defdoc get-internal-timeget-internal-time35497,1444276 (defun read-run-time (state-state)read-run-time35547,1446435 (defparameter *next-acl2-oracle-value* nil)*next-acl2-oracle-value*35588,1448015 (defun read-acl2-oracle (state-state)read-acl2-oracle35590,1448060 (defun read-acl2-oracle@par (state-state)read-acl2-oracle@par35611,1448739 (defun read-acl2-oracle@par (state-state)read-acl2-oracle@par35636,1449768 (defun getenv$ (str state)getenv$35657,1450757 (defun setenv$ (str val)setenv$35686,1451783 (defun random$ (limit state)random$35742,1453908 (defthm natp-random$natp-random$35781,1455366 (defthm random$-linearrandom$-linear35785,1455457 (defvar *last-sys-call-status* 0)*last-sys-call-status*35802,1455907 (defun sys-call (command-string args)sys-call35804,1455942 (defun sys-call-status (state)sys-call-status35890,1459304 (defun read-file-by-lines (file &optional delete-after-reading)read-file-by-lines35918,1460364 (defun system-call+ (string arguments)system-call+35941,1461118 (defthm update-acl2-oracle-preserves-state-p1update-acl2-oracle-preserves-state-p136101,1467765 (defun sys-call+ (command-string args state)sys-call+36109,1468021 (defthm read-run-time-preserves-state-p1read-run-time-preserves-state-p136257,1474774 (defthm read-acl2-oracle-preserves-state-p1read-acl2-oracle-preserves-state-p136265,1475063 (defthm nth-0-read-run-time-type-prescriptionnth-0-read-run-time-type-prescription36281,1475546 (defun main-timer (state)main-timer36299,1476113 (defun put-assoc-eq-exec (name val alist)put-assoc-eq-exec36312,1476616 (defun put-assoc-eql-exec (name val alist)put-assoc-eql-exec36324,1477076 (defun put-assoc-equal (name val alist)put-assoc-equal36336,1477541 (defmacro put-assoc-eq (name val alist)put-assoc-eq36342,1477813 (defmacro put-assoc-eql (name val alist)put-assoc-eql36347,1477985 (defthm put-assoc-eq-exec-is-put-assoc-equalput-assoc-eq-exec-is-put-assoc-equal36350,1478072 (defthm put-assoc-eql-exec-is-put-assoc-equalput-assoc-eql-exec-is-put-assoc-equal36354,1478206 (defmacro put-assoc (name val alist &key (test ''eql))put-assoc36358,1478342 (defun set-timer (name val state)set-timer36425,1481004 (defun get-timer (name state)get-timer36434,1481288 (defun push-timer (name val state)push-timer36445,1481665 (defthm rationalp-+rationalp-+36454,1482049 (defthm rationalp-*rationalp-*36550,1486315 (defthm rationalp-unary--rationalp-unary--36555,1486430 (defthm rationalp-unary-/rationalp-unary-/36559,1486513 (defthm realp-+realp-+36568,1486849 (defthm realp-*realp-*36574,1486973 (defthm realp-unary--realp-unary--36580,1487097 (defthm realp-unary-/realp-unary-/36585,1487193 (defthm rationalp-implies-acl2-numberprationalp-implies-acl2-numberp36591,1487339 (defun pop-timer (name flg state)pop-timer36594,1487423 (defun add-timers (name1 name2 state)add-timers36612,1488048 (defthm nth-0-consnth-0-cons36626,1488611 (defthm nth-add1nth-add136636,1488813 (defthm main-timer-type-prescriptionmain-timer-type-prescription36643,1489013 (defthm ordered-symbol-alistp-add-pair-forwardordered-symbol-alistp-add-pair-forward36649,1489208 (defthm assoc-add-pairassoc-add-pair36658,1489477 (defthm add-pair-preserves-all-boundpadd-pair-preserves-all-boundp36666,1489757 (defthm state-p1-update-main-timerstate-p1-update-main-timer36673,1490016 (defun increment-timer (name state)increment-timer36704,1491407 (defun print-rational-as-decimal (x channel state)print-rational-as-decimal36727,1492402 (defun print-timer (name channel state)print-timer36748,1493172 (defun known-package-alist (state)known-package-alist36756,1493515 (defun prin1$ (x channel state)prin1$36772,1493921 (defun current-package (state)current-package36953,1502711 (defthm state-p1-update-nth-2-worldstate-p1-update-nth-2-world37007,1505558 (defconst *initial-untouchable-fns**initial-untouchable-fns*37045,1506917 (defconst *initial-untouchable-vars**initial-untouchable-vars*37127,1509406 (defun ld-skip-proofsp (state)ld-skip-proofsp37285,1513923 (defun-one-output bad-lisp-objectp (x)bad-lisp-objectp37396,1519720 (defun-one-output chk-bad-lisp-object (x)chk-bad-lisp-object37630,1531590 (defmacro assign (x y)assign37672,1532824 (defmacro @ (x)@37712,1534350 (defun make-var-lst1 (root sym n acc)make-var-lst137746,1535630 (defun make-var-lst (sym n)make-var-lst37773,1536536 (defun union-eq-exec (l1 l2)union-eq-exec37781,1536766 (defun union-eql-exec (l1 l2)union-eql-exec37791,1537148 (defun union-equal (l1 l2)union-equal37801,1537532 (defmacro union-eq (&rest lst)union-eq37807,1537767 (defthm union-eq-exec-is-union-equalunion-eq-exec-is-union-equal37810,1537828 (defthm union-eql-exec-is-union-equalunion-eql-exec-is-union-equal37814,1537928 (defun parse-args-and-test (x tests default ctx form name)parse-args-and-test37818,1538030 (defmacro union$ (&whole form &rest x)union$37867,1540274 (defun subst-for-nth-arg (new n args)subst-for-nth-arg37939,1543409 (defmacro the-mv (args type body &optional state-pos)the-mv37949,1543711 (defmacro the-mv (vars type body &optional state-pos)the-mv38003,1545645 (defmacro the2s (x y)the2s38014,1546189 (deflabel bibliographybibliography38017,1546238 (defun non-free-var-runes (runes free-var-runes-once free-var-runes-all acc)non-free-var-runes38038,1547040 (defun free-var-runes (flg wrld)free-var-runes38053,1547758 (defthm natp-position-ac ; for admission of absolute-pathname-string-pnatp-position-ac38061,1547977 (defun absolute-pathname-string-p (str directoryp os)absolute-pathname-string-p38069,1548313 (defun include-book-dir-alistp (x os)include-book-dir-alistp38115,1550440 (defun illegal-ruler-extenders-values (x wrld)illegal-ruler-extenders-values38124,1550755 (defun intersection-eq-exec (l1 l2)intersection-eq-exec38136,1551172 (defun intersection-eql-exec (l1 l2)intersection-eql-exec38148,1551588 (defun intersection-equal (l1 l2)intersection-equal38159,1552009 (defmacro intersection-eq (&rest lst)intersection-eq38169,1552331 (defthm intersection-eq-exec-is-intersection-equalintersection-eq-exec-is-intersection-equal38172,1552406 (defthm intersection-eql-exec-is-intersection-equalintersection-eql-exec-is-intersection-equal38176,1552534 (defmacro intersection$ (&whole form &rest x)intersection$38180,1552664 (defun table-alist (name wrld)table-alist38261,1556231 (defun ruler-extenders-msg-aux (vals return-last-table)ruler-extenders-msg-aux38269,1556461 (defun ruler-extenders-msg (x wrld)ruler-extenders-msg38286,1557229 (defmacro chk-ruler-extenders (x soft ctx wrld)chk-ruler-extenders38319,1558758 (defmacro fixnum-bound () ; most-positive-fixnum in Allegro CL and many othersfixnum-bound38329,1559215 (defconst *default-step-limit**default-step-limit*38332,1559315 (deflabel acl2-defaults-tableacl2-defaults-table38474,1564345 (defmacro set-enforce-redundancy (x)set-enforce-redundancy38788,1578037 (defmacro set-enforce-redundancy (x)set-enforce-redundancy38861,1581776 (defmacro set-ignore-doc-string-error (x)set-ignore-doc-string-error38866,1581861 (defmacro set-ignore-doc-string-error (x)set-ignore-doc-string-error38911,1583718 (defmacro default-verify-guards-eagerness-from-table (alist)default-verify-guards-eagerness-from-table38915,1583791 (defun default-verify-guards-eagerness (wrld)default-verify-guards-eagerness38919,1583920 (defmacro set-verify-guards-eagerness (x)set-verify-guards-eagerness38927,1584259 (defmacro set-verify-guards-eagerness (x)set-verify-guards-eagerness38983,1587101 (defun default-compile-fns (wrld)default-compile-fns38987,1587174 (defmacro set-compile-fns (x)set-compile-fns38993,1587431 (defmacro set-compile-fns (x)set-compile-fns39051,1589965 (defun set-compiler-enabled (val state)set-compiler-enabled39055,1590026 (defun default-measure-function (wrld)default-measure-function39080,1591110 (defmacro set-measure-function (name)set-measure-function39087,1591400 (defmacro set-measure-function (name)set-measure-function39127,1593020 (defun default-well-founded-relation (wrld)default-well-founded-relation39131,1593092 (defmacro set-well-founded-relation (rel)set-well-founded-relation39138,1593384 (defmacro set-well-founded-relation (rel)set-well-founded-relation39177,1595130 (defmacro default-defun-mode-from-table (alist)default-defun-mode-from-table39183,1595243 (defun default-defun-mode (wrld)default-defun-mode39193,1595542 (defun default-defun-mode-from-state (state)default-defun-mode-from-state39235,1597914 (defmacro logic nillogic39240,1598054 (defmacro logic () nil)logic39279,1599471 (defmacro program nilprogram39282,1599513 (defmacro program () nil)program39338,1601298 (defun invisible-fns-table (wrld)invisible-fns-table39340,1601325 (defmacro set-invisible-fns-table (alist)set-invisible-fns-table39388,1603431 (defun unary-function-symbol-listp (lst wrld)unary-function-symbol-listp39461,1606202 (defun invisible-fns-entryp (key val wrld)invisible-fns-entryp39479,1607001 (defmacro add-invisible-fns (top-fn &rest unary-fns)add-invisible-fns39491,1607320 (defmacro remove-invisible-fns (top-fn &rest unary-fns)remove-invisible-fns39533,1609171 (defmacro set-invisible-fns-alist (alist)set-invisible-fns-alist39578,1611065 (defmacro invisible-fns-alist (wrld)invisible-fns-alist39585,1611371 (defmacro set-bogus-defun-hints-ok (x)set-bogus-defun-hints-ok39592,1611606 (defmacro set-bogus-defun-hints-ok (x)set-bogus-defun-hints-ok39615,1612435 (defmacro set-bogus-mutual-recursion-ok (x)set-bogus-mutual-recursion-ok39620,1612522 (defmacro set-bogus-mutual-recursion-ok (x)set-bogus-mutual-recursion-ok39670,1614675 (defdoc ruler-extendersruler-extenders39674,1614750 (defmacro set-ruler-extenders (x)set-ruler-extenders40077,1631033 (defmacro set-ruler-extenders (x)set-ruler-extenders40102,1631939 (defmacro set-irrelevant-formals-ok (x)set-irrelevant-formals-ok40107,1632021 (defmacro set-irrelevant-formals-ok (x)set-irrelevant-formals-ok40142,1633262 (defmacro set-ignore-ok (x)set-ignore-ok40147,1633350 (defmacro set-ignore-ok (x)set-ignore-ok40191,1635028 (defmacro set-inhibit-warnings! (&rest x)set-inhibit-warnings!40196,1635104 (defmacro set-inhibit-warnings! (&rest lst)set-inhibit-warnings!40205,1635269 (defmacro set-inhibit-warnings (&rest lst)set-inhibit-warnings40224,1636033 (defmacro set-inhibit-output-lst (lst)set-inhibit-output-lst40274,1638350 (defmacro set-inhibited-summary-types (lst)set-inhibited-summary-types40342,1641563 (defmacro set-state-ok (x)set-state-ok40397,1643855 (defmacro set-state-ok (x)set-state-ok40470,1647025 (defmacro set-let*-abstractionp (x)set-let*-abstractionp40479,1647261 (defmacro set-let*-abstractionp (x)set-let*-abstractionp40526,1649168 (defmacro set-let*-abstraction (x)set-let*-abstraction40530,1649235 (defun let*-abstractionp (state)let*-abstractionp40538,1649469 (defconst *initial-backchain-limit* '(nil nil))*initial-backchain-limit*40552,1649965 (defconst *initial-default-backchain-limit* '(nil nil))*initial-default-backchain-limit*40554,1650014 (defmacro set-backchain-limit (limit)set-backchain-limit40557,1650088 (defmacro set-backchain-limit (limit)set-backchain-limit40610,1652460 (defun backchain-limit (wrld flg)backchain-limit40614,1652533 (defmacro set-default-backchain-limit (limit)set-default-backchain-limit40773,1659384 (defmacro set-default-backchain-limit (limit)set-default-backchain-limit40839,1662742 (defun default-backchain-limit (wrld flg)default-backchain-limit40843,1662823 (defun step-limit-from-table (wrld)step-limit-from-table40919,1666137 (defparameter *step-limit-error-p**step-limit-error-p*40938,1666945 (defmacro set-prover-step-limit (limit)set-prover-step-limit40947,1667223 (defmacro set-prover-step-limit (limit)set-prover-step-limit41109,1674603 (defparameter *rewrite-depth-max* 0) ; records max depth per event*rewrite-depth-max*41128,1675340 (defparameter *rewrite-depth-alist* nil) ; records max depth per book*rewrite-depth-alist*41129,1675411 (defconst *default-rewrite-stack-limit**default-rewrite-stack-limit*41177,1677249 (defmacro set-rewrite-stack-limit (limit)set-rewrite-stack-limit41191,1677878 (defmacro set-rewrite-stack-limit (limit)set-rewrite-stack-limit41231,1679521 (defun rewrite-stack-limit (wrld)rewrite-stack-limit41235,1679598 (defmacro set-nu-rewriter-mode (x)set-nu-rewriter-mode41281,1681442 (defmacro set-nu-rewriter-mode (x)set-nu-rewriter-mode41335,1683791 (defun nu-rewriter-mode (wrld)nu-rewriter-mode41339,1683857 (defun case-split-limitations (wrld)case-split-limitations41359,1684822 (defmacro sr-limit (wrld)sr-limit41388,1686065 (defmacro case-limit (wrld)case-limit41391,1686133 (defmacro set-case-split-limitations (lst)set-case-split-limitations41395,1686221 (defmacro set-case-split-limitations (lst)set-case-split-limitations41537,1693686 (defconst *initial-acl2-defaults-table**initial-acl2-defaults-table*41548,1694158 (defun untrans-table (wrld)untrans-table41554,1694379 (defmacro add-macro-fn (macro macro-fn &optional right-associate-p)add-macro-fn41593,1695459 (defmacro add-binop (macro macro-fn)add-binop41636,1697642 (defmacro remove-macro-fn (macro-fn)remove-macro-fn41647,1697953 (defmacro remove-binop (macro-fn)remove-binop41676,1699116 (defun match-free-default (wrld)match-free-default41690,1699582 (defmacro set-match-free-default (x)set-match-free-default41698,1699913 (defmacro set-match-free-default (x)set-match-free-default41759,1702807 (defmacro set-match-free-error (x)set-match-free-error41763,1702875 (defun match-free-override (wrld)match-free-override41808,1705026 (defmacro add-match-free-override (flg &rest runes)add-match-free-override41826,1705785 (defmacro add-match-free-override (flg &rest runes)add-match-free-override42013,1715056 (defmacro add-include-book-dir (keyword dir)add-include-book-dir42017,1715147 (defmacro delete-include-book-dir (keyword)delete-include-book-dir42075,1718140 (defconst *non-linear-rounds-value* 3)*non-linear-rounds-value*42116,1719906 (defun non-linearp (wrld)non-linearp42118,1719946 (defmacro set-non-linearp (toggle)set-non-linearp42129,1720282 (defmacro set-non-linearp (toggle)set-non-linearp42153,1721007 (defmacro set-non-linear (toggle)set-non-linear42157,1721078 (defun tau-auto-modep (wrld)tau-auto-modep42165,1721304 (defmacro set-tau-auto-mode (toggle)set-tau-auto-mode42203,1722994 (defmacro set-tau-auto-mode (toggle)set-tau-auto-mode42323,1729780 (defmacro defttag (tag-name &key doc)defttag42328,1729870 (defmacro defttag (&rest args)defttag42595,1743346 (defun ttag (wrld)ttag42599,1743411 (defdoc complex-rationalpcomplex-rationalp42611,1743795 (deflabel letlet42625,1744243 (defdoc fletflet42750,1749814 (defun-one-output what-is-the-global-state ()what-is-the-global-state42880,1754866 (deflabel macro-aliases-tablemacro-aliases-table42947,1758021 (defun macro-aliases (wrld)macro-aliases43063,1762468 (defmacro add-macro-alias (macro-name fn-name)add-macro-alias43067,1762587 (defmacro remove-macro-alias (macro-name)remove-macro-alias43132,1765341 (deflabel nth-aliases-tablenth-aliases-table43165,1766710 (defun nth-aliases (wrld)nth-aliases43205,1768138 (defmacro add-nth-alias (alias-name name)add-nth-alias43209,1768253 (defmacro remove-nth-alias (alias-name)remove-nth-alias43230,1768903 (deflabel default-hints-tabledefault-hints-table43266,1770533 (defun default-hints (wrld)default-hints43287,1771299 (defmacro set-default-hints (lst)set-default-hints43312,1772261 (defmacro set-default-hints! (lst)set-default-hints!43371,1774670 (defmacro set-default-hints! (lst)set-default-hints!43389,1775370 (defmacro add-default-hints (lst &key at-end)add-default-hints43393,1775438 (defmacro add-default-hints! (lst &key at-end)add-default-hints!43442,1777579 (defmacro add-default-hints! (lst)add-default-hints!43462,1778433 (defmacro remove-default-hints (lst)remove-default-hints43466,1778501 (defmacro remove-default-hints! (lst)remove-default-hints!43506,1780044 (defmacro remove-default-hints! (lst)remove-default-hints!43524,1780826 (defmacro set-override-hints-macro (lst at-end ctx)set-override-hints-macro43529,1780914 (defmacro set-override-hints-macro (&rest args)set-override-hints-macro43535,1781137 (defmacro add-override-hints! (lst &key at-end)add-override-hints!43539,1781219 (defmacro add-override-hints (lst &key at-end)add-override-hints43553,1781721 (defmacro set-override-hints! (lst)set-override-hints!43583,1782993 (defmacro set-override-hints (lst)set-override-hints43596,1783434 (defmacro remove-override-hints! (lst)remove-override-hints!43620,1784291 (defmacro remove-override-hints (lst)remove-override-hints43633,1784792 (defmacro set-rw-cache-state (val)set-rw-cache-state43659,1785855 (defmacro set-rw-cache-state! (val)set-rw-cache-state!43937,1801305 (defmacro set-rw-cache-state! (val)set-rw-cache-state!43952,1801836 (defconst *legal-rw-cache-states**legal-rw-cache-states*43956,1801905 (defun fix-true-list (x)fix-true-list43965,1802110 (defthm pairlis$-fix-true-listpairlis$-fix-true-list43983,1802570 (defun boolean-listp (lst)boolean-listp43987,1802668 (defthm boolean-listp-consboolean-listp-cons43998,1802986 (defthm boolean-listp-forwardboolean-listp-forward44007,1803239 (defthm boolean-listp-forward-to-symbol-listpboolean-listp-forward-to-symbol-listp44017,1803502 (defaxiom completion-of-+completion-of-+44035,1804280 (defthm default-+-1default-+-144046,1804519 (defthm default-+-2default-+-244051,1804652 (defaxiom completion-of-*completion-of-*44056,1804785 (defthm default-*-1default-*-144065,1804972 (defthm default-*-2default-*-244069,1805058 (defaxiom completion-of-unary-minuscompletion-of-unary-minus44073,1805144 (defthm default-unary-minusdefault-unary-minus44080,1805281 (defaxiom completion-of-unary-/completion-of-unary-/44084,1805373 (defthm default-unary-/default-unary-/44092,1805548 (defaxiom completion-of-<completion-of-<44099,1805728 (defthm default-<-1default-<-144111,1806152 (defthm default-<-2default-<-244120,1806385 (defaxiom completion-of-carcompletion-of-car44129,1806618 (defthm default-cardefault-car44137,1806761 (defaxiom completion-of-cdrcompletion-of-cdr44141,1806842 (defthm default-cdrdefault-cdr44149,1806985 (defthm cons-car-cdrcons-car-cdr44153,1807066 (defaxiom completion-of-char-codecompletion-of-char-code44159,1807187 (defthm default-char-codedefault-char-code44166,1807336 (defaxiom completion-of-code-charcompletion-of-code-char44171,1807481 (defaxiom completion-of-complexcompletion-of-complex44192,1808076 (defthm default-complex-1default-complex-144200,1808303 (defthm default-complex-2default-complex-244208,1808536 (defthm complex-0complex-044217,1808858 (defthm add-def-complexadd-def-complex44225,1809073 (defthm realpart-+realpart-+44240,1809748 (defthm imagpart-+imagpart-+44245,1809878 (defaxiom completion-of-coercecompletion-of-coerce44250,1810008 (defthm default-coerce-1default-coerce-144261,1810271 (defthm make-character-list-make-character-listmake-character-list-make-character-list44267,1810454 (defthm default-coerce-2default-coerce-244271,1810593 (defthm default-coerce-3default-coerce-344285,1811151 (defaxiom completion-of-denominatorcompletion-of-denominator44291,1811335 (defthm default-denominatordefault-denominator44298,1811489 (defaxiom completion-of-floor1completion-of-floor144309,1811871 (defthm default-floor1default-floor144320,1812121 (defaxiom floor1-integer-xfloor1-integer-x44328,1812327 (defaxiom floor1-x-<=-xfloor1-x-<=-x44335,1812510 (defaxiom x-<-add1-floor1-xx-<-add1-floor1-x44343,1812704 (defthm floor1-valuefloor1-value44353,1813023 (defaxiom completion-of-imagpartcompletion-of-imagpart44361,1813213 (defthm default-imagpartdefault-imagpart44368,1813361 (defaxiom completion-of-intern-in-package-of-symbolcompletion-of-intern-in-package-of-symbol44373,1813475 (defaxiom completion-of-numeratorcompletion-of-numerator44392,1814135 (defthm default-numeratordefault-numerator44399,1814283 (defaxiom completion-of-realpartcompletion-of-realpart44404,1814396 (defthm default-realpartdefault-realpart44411,1814544 (defaxiom completion-of-symbol-namecompletion-of-symbol-name44416,1814658 (defthm default-symbol-namedefault-symbol-name44423,1814811 (defaxiom completion-of-symbol-package-namecompletion-of-symbol-package-name44429,1814978 (defthm default-symbol-package-namedefault-symbol-package-name44436,1815155 (defdoc i-smalli-small44445,1815435 (defdoc i-closei-close44454,1815684 (defdoc i-largei-large44462,1815939 (defdoc i-limitedi-limited44471,1816202 (defdoc standardpstandardp44480,1816450 (defdoc standard-partstandard-part44501,1817475 (defun i-small (x)i-small44513,1817830 (defun i-close (x y)i-close44518,1817940 (defun i-large (x)i-large44524,1818066 (defmacro i-limited (x)i-limited44530,1818189 (defaxiom i-large-integer-is-largei-large-integer-is-large44537,1818404 (defaxiom standardp-plusstandardp-plus44545,1818691 (defaxiom standardp-uminusstandardp-uminus44550,1818811 (defaxiom standardp-timesstandardp-times44554,1818897 (defaxiom standardp-udividestandardp-udivide44559,1819018 (defaxiom standardp-complexstandardp-complex44563,1819105 (defaxiom standardp-onestandardp-one44571,1819423 (defaxiom standard-part-of-standardpstandard-part-of-standardp44577,1819547 (defaxiom standardp-standard-partstandardp-standard-part44582,1819690 (defaxiom standard-part-of-reals-is-idempotentstandard-part-of-reals-is-idempotent44586,1819793 (defaxiom standard-part-of-complexstandard-part-of-complex44591,1819953 (defaxiom standard-part-of-plusstandard-part-of-plus44597,1820131 (defaxiom standard-part-of-uminusstandard-part-of-uminus44602,1820272 (defaxiom standard-part-of-timesstandard-part-of-times44606,1820377 (defaxiom standard-part-of-udividestandard-part-of-udivide44611,1820559 (defaxiom standard-part-<=standard-part-<=44617,1820743 (defaxiom small-are-limitedsmall-are-limited44623,1820903 (defaxiom standards-are-limitedstandards-are-limited44630,1821079 (defthm standard-constants-are-limitedstandard-constants-are-limited44636,1821248 (defaxiom limited-integers-are-standardlimited-integers-are-standard44644,1821497 (defaxiom standard+small->i-limitedstandard+small->i-limited44651,1821733 (defdoc acl2-numberpacl2-numberp44659,1821917 (defdoc ++44667,1822123 (defdoc binary-+binary-+44683,1822432 (defdoc binary-*binary-*44709,1823042 (defdoc --44733,1823606 (defdoc unary--unary--44762,1824097 (defdoc unary-/unary-/44784,1824601 (defdoc <<44808,1825165 (defdoc carcar44838,1826076 (defdoc cdrcdr44857,1826488 (defdoc char-codechar-code44876,1826902 (defdoc characterpcharacterp44894,1827285 (defdoc code-charcode-char44902,1827453 (defdoc complexcomplex44925,1828008 (defdoc conscons44973,1829829 (defdoc conspconsp44982,1830112 (defdoc coercecoerce44989,1830273 (defdoc denominatordenominator45038,1831924 (defdoc equalequal45056,1832266 (defdoc ifif45067,1832547 (defdoc imagpartimagpart45083,1832959 (defdoc integerpintegerp45101,1833292 (defdoc intern-in-package-of-symbolintern-in-package-of-symbol45108,1833451 (defdoc numeratornumerator45164,1835666 (defdoc rationalprationalp45182,1835999 (defdoc realpartrealpart45190,1836195 (defdoc stringpstringp45208,1836523 (defdoc symbol-namesymbol-name45215,1836672 (defdoc symbol-package-namesymbol-package-name45233,1837010 (defdoc symbolpsymbolp45260,1837962 (defdoc quotequote45267,1838111 (defun double-rewrite (x)double-rewrite45275,1838276 (defparameter *acl2-time-limit* nil)*acl2-time-limit*45508,1849241 (defparameter *acl2-time-limit-boundp* nil)*acl2-time-limit-boundp*45510,1849279 (defun chk-with-prover-time-limit-arg (time)chk-with-prover-time-limit-arg45514,1849327 (defmacro with-prover-time-limit1-raw (time form)with-prover-time-limit1-raw45531,1850000 (defmacro with-prover-time-limit1 (time form)with-prover-time-limit145551,1850875 (defmacro with-prover-time-limit (time form)with-prover-time-limit45554,1850981 (defparameter *time-limit-tags* nil)*time-limit-tags*45651,1855899 (defmacro catch-time-limit5 (form)catch-time-limit545653,1855937 (defmacro catch-time-limit5@par (form)catch-time-limit5@par45677,1856921 (defun time-limit5-reached-p (msg)time-limit5-reached-p45713,1858555 (defmacro catch-step-limit (form)catch-step-limit45757,1860594 (defconst *guard-checking-values**guard-checking-values*45786,1861789 (defun chk-with-guard-checking-arg (val)chk-with-guard-checking-arg45789,1861855 (defmacro with-guard-checking1-raw (val form)with-guard-checking1-raw45801,1862374 (defmacro with-guard-checking1 (val form)with-guard-checking145812,1862770 (defmacro with-guard-checking (val form)with-guard-checking45815,1862868 (defun abort! ()abort!45841,1863807 (defmacro a! ()a!45855,1864110 (defun p! ()p!45879,1864978 (defparameter *wormhole-status-alist* nil)*wormhole-status-alist*45922,1866667 (defparameter *inhibit-wormhole-activityp* nil)*inhibit-wormhole-activityp*45925,1866728 (defun wormhole1 (name input form ld-specials)wormhole145927,1866777 (defun wormhole-p (state)wormhole-p46023,1870784 (defun duplicates (lst)duplicates46040,1871264 (defun evens (l)evens46047,1871497 (defun odds (l)odds46053,1871643 (defun set-equalp-equal (lst1 lst2)set-equalp-equal46057,1871721 (defparameter *metafunction-context* nil)*metafunction-context*46179,1878667 (DEFMACRO |Access REWRITE-CONSTANT record field CURRENT-CLAUSE||Access46299,1883189 (defun record-error (name rec)record-error46316,1883760 (defun record-accessor-function-name (name field)record-accessor-function-name46322,1883930 (defmacro access (name rec field)access46334,1884315 (defun mfc-clause (mfc)mfc-clause46343,1884655 (defun mfc-rdepth (mfc)mfc-rdepth46377,1886053 (defun type-alist-entryp (x)type-alist-entryp46387,1886348 (defun type-alistp (x)type-alistp46403,1886836 (defun mfc-type-alist (mfc)mfc-type-alist46410,1886995 (defun mfc-ancestors (mfc)mfc-ancestors46426,1887438 (defun mfc-unify-subst (mfc)mfc-unify-subst46442,1887875 (defun mfc-world (mfc)mfc-world46452,1888190 (defthm pseudo-term-listp-mfc-clausepseudo-term-listp-mfc-clause46465,1888561 (defthm type-alistp-mfc-type-alisttype-alistp-mfc-type-alist46468,1888639 (defun bad-atom (x)bad-atom46503,1890631 (defthm bad-atom-compound-recognizerbad-atom-compound-recognizer46514,1890849 (defun-one-output bad-atom<= (x y)bad-atom<=46526,1891143 (defaxiom booleanp-bad-atom<=booleanp-bad-atom<=46535,1891497 (defaxiom bad-atom<=-antisymmetricbad-atom<=-antisymmetric46540,1891633 (defaxiom bad-atom<=-transitivebad-atom<=-transitive46548,1891839 (defaxiom bad-atom<=-totalbad-atom<=-total46557,1892102 (defun alphorder (x y)alphorder46567,1892372 (defun lexorder (x y)lexorder46643,1894975 (defthm alphorder-reflexivealphorder-reflexive46708,1897027 (defthm alphorder-transitivealphorder-transitive46725,1897544 (defthm alphorder-anti-symmetricalphorder-anti-symmetric46736,1897878 (defthm alphorder-totalalphorder-total46760,1898893 (defthm lexorder-reflexivelexorder-reflexive46775,1899430 (defthm lexorder-anti-symmetriclexorder-anti-symmetric46778,1899476 (defthm lexorder-transitivelexorder-transitive46783,1899615 (defthm lexorder-totallexorder-total46788,1899765 (defun merge-lexorder (l1 l2 acc)merge-lexorder46804,1900296 (defthm true-listp-merge-sort-lexordertrue-listp-merge-sort-lexorder46830,1901044 (defun merge-sort-lexorder (l)merge-sort-lexorder46836,1901237 (defdoc bddbdd46851,1901863 (defun if* (x y z)if*46885,1903313 (defun resize-list (lst n default-value)resize-list47069,1910463 (deflabel theory-functionstheory-functions47085,1910983 (defun e/d-fn (theory e/d-list enable-p)e/d-fn47120,1912606 (defmacro e/d (&rest theories)e/d47131,1913127 (defun mod-expt (base exp mod)mod-expt47210,1916110 (defmacro fcons-term* (&rest x)fcons-term*47254,1917883 (defun conjoin2 (t1 t2)conjoin247271,1918561 (defun conjoin (l)conjoin47285,1919059 (defun conjoin2-untranslated-terms (t1 t2)conjoin2-untranslated-terms47291,1919228 (defun conjoin-untranslated-terms (l)conjoin-untranslated-terms47307,1919670 (defun disjoin2 (t1 t2)disjoin247319,1920009 (defun disjoin (lst)disjoin47331,1920367 (defun disjoin-lst (clause-list)disjoin-lst47337,1920552 (defun conjoin-clauses (clause-list)conjoin-clauses47343,1920774 (defconst *true-clause* (list *t*))*true-clause*47347,1920908 (defconst *false-clause* nil)*false-clause*47349,1920945 (defun clauses-result (tuple)clauses-result47351,1920976 (defdoc sharp-dot-readersharp-dot-reader47356,1921124 (defdoc sharp-comma-readersharp-comma-reader47395,1922413 (defdoc sharp-bang-readersharp-bang-reader47403,1922618 (defdoc sharp-u-readersharp-u-reader47435,1923416 (defdoc evisc-tableevisc-table47487,1924950 (defconst *top-hint-keywords**top-hint-keywords*47731,1933483 (defconst *hint-keywords**hint-keywords*47741,1933891 (defmacro add-custom-keyword-hint (key uterm1 &key (checker '(value t)))add-custom-keyword-hint47786,1935294 (defmacro add-custom-keyword-hint (&rest args)add-custom-keyword-hint47874,1939078 (defmacro remove-custom-keyword-hint (keyword)remove-custom-keyword-hint47878,1939159 (defun splice-keyword-alist (key new-segment keyword-alist)splice-keyword-alist47911,1940296 (deflabel custom-keyword-hintscustom-keyword-hints47924,1940838 (defmacro show-custom-keyword-hint-expansion (flg)show-custom-keyword-hint-expansion47934,1941150 (defun search-fn-guard (seq1 seq2 from-end test start1 start2 end1 end2search-fn-guard47963,1942030 (defun search-from-start (seq1 seq2 start2 end2)search-from-start48011,1944178 (defun search-from-end (seq1 seq2 start2 end2 acc)search-from-end48034,1945038 (defmacro search (seq1 seq2search48215,1950694 (defthm eqlablep-ntheqlablep-nth48279,1953737 (defun count-stringp (item x start end)count-stringp48284,1953866 (defun count-listp (item x end)count-listp48299,1954399 (defmacro count (item sequence &key (start '0) end)count48334,1955531 (defun make-sharp-atsign (i)make-sharp-atsign48374,1957123 (defun sharp-atsign-alist (i acc)sharp-atsign-alist48381,1957339 (defmacro time$1-raw (val x)time$1-raw48488,1962299 (defmacro time$1 (val form)time$148508,1962982 (defmacro time$ (x &keytime$48511,1963052 (defmacro our-multiple-value-prog1 (form &rest other-forms)our-multiple-value-prog148693,1969769 (defconst *mv-vars**mv-vars*48709,1970257 (defconst *mv-var-values**mv-var-values*48716,1970402 (defconst *mv-extra-var* (gensym))*mv-extra-var*48720,1970496 (defun protect-mv (form &optional multiplicity)protect-mv48722,1970532 (defmacro our-time (x &key real-mintime run-mintime minalloc msg args)our-time48770,1972296 (defun-one-output gc$-fn (args)gc$-fn48905,1978237 (defun gc$-fn (args)gc$-fn48929,1979081 (defmacro gc$ (&rest args)gc$48934,1979164 (defun-one-output gc-verbose-fn (arg)gc-verbose-fn48963,1980149 (defun gc-verbose-fn (arg)gc-verbose-fn48978,1980617 (defmacro gc-verbose (arg)gc-verbose48983,1980705 (defun get-wormhole-status (name state)get-wormhole-status49008,1981600 (defun file-write-date$ (file state)file-write-date$49037,1982694 (defun debugger-enable (state)debugger-enable49055,1983206 (defun break$ ()break$49060,1983397 (defvar *ccl-print-call-history-count**ccl-print-call-history-count*49096,1984582 (defun print-call-history ()print-call-history49119,1985629 (defun debugger-enabledp (state)debugger-enabledp49170,1987549 (defun maybe-print-call-history (state)maybe-print-call-history49177,1987825 (defmacro with-reckless-readtable (form)with-reckless-readtable49184,1988116 (defmacro set-debugger-enable (val)set-debugger-enable49202,1988723 (defun set-debugger-enable-fn (val state)set-debugger-enable-fn49336,1994614 (defun add-@par-suffix (symbol)add-@par-suffix49369,1995976 (defun generate-@par-mappings (symbols)generate-@par-mappings49375,1996151 (defconst *@par-mappings**@par-mappings*49392,1996996 (defun make-identity-for-@par-mappings (mappings)make-identity-for-@par-mappings49586,2003846 (defmacro define-@par-macros ()define-@par-macros49600,2004411 (defun replace-defun@par-with-defun (forms)replace-defun@par-with-defun49617,2005007 (defmacro mutual-recursion@par (&rest forms)mutual-recursion@par49628,2005374 (defun defun@par-fn (name parallel-version rst)defun@par-fn49632,2005493 (defun mutual-recursion@par-guardp (rst)mutual-recursion@par-guardp49652,2006235 (defun mutual-recursion@par-fn (forms serial-and-par)mutual-recursion@par-fn49664,2006717 (defmacro mutual-recursion@par (&rest forms)mutual-recursion@par49686,2007678 (defmacro defun@par (name &rest args)defun@par49690,2007846 (defmacro serial-first-form-parallel-second-form (x y)serial-first-form-parallel-second-form49704,2008344 (defmacro serial-first-form-parallel-second-form@par (x y)serial-first-form-parallel-second-form@par49712,2008505 (defmacro serial-only (x)serial-only49719,2008655 (defmacro serial-only@par (x)serial-only@par49726,2008737 (defmacro parallel-only (x)parallel-only49733,2008833 (defmacro parallel-only@par (x)parallel-only@par49741,2008944 (defmacro mv@par (&rest rst)mv@par49748,2009030 (defmacro value@par (val)value@par49754,2009190 (defmacro state-mac ()state-mac49760,2009264 (defmacro state-mac@par ()state-mac@par49767,2009346 (defmacro mv-let@par (vars call &rest rst)mv-let@par49774,2009425 (defmacro warning$@par (&rest rst)warning$@par49780,2009616 (defmacro error-in-parallelism-mode (fake-return-value form)error-in-parallelism-mode49789,2009865 (defmacro error-in-parallelism-mode@par (return-value form)error-in-parallelism-mode@par49794,2009985 (defun increment-timer@par (name state)increment-timer@par49816,2010898 (defconst *waterfall-printing-values**waterfall-printing-values*49824,2011124 (defconst *waterfall-parallelism-values**waterfall-parallelism-values*49827,2011198 (defun symbol-constant-fn (prefix sym)symbol-constant-fn49834,2011426 (defun stobjs-in (fn w)stobjs-in49845,2011779 (defmacro oracle-funcall (fn &rest args)oracle-funcall49860,2012183 (defun all-nils (lst)all-nils49894,2013291 (defun oracle-apply-guard (fn args state)oracle-apply-guard49900,2013457 (defun oracle-apply (fn args state)oracle-apply49913,2013951 (defun oracle-apply-raw (fn args state)oracle-apply-raw50039,2019586 (defun time-tracker-fn (tag kwd kwdp times interval min-time msg)time-tracker-fn50074,2021042 (defmacro time-tracker (tag &optional (kwd 'nil kwdp)time-tracker50143,2023353 (defmacro time-tracker (&rest args)time-tracker50148,2023551 (defdoc time-trackertime-tracker50152,2023621 (defdoc time-tracker-tautime-tracker-tau50397,2036637 (defg *inside-absstobj-update* #(0))*inside-absstobj-update*50464,2039234 (defun set-absstobj-debug-fn (val always)set-absstobj-debug-fn50466,2039272 (defmacro set-absstobj-debug (val &key (event-p 't) always on-skip-proofs)set-absstobj-debug50497,2040704 (defun =-len (x n)>=-len12187,508654 (defun all->=-len (lst n)all->=-len12195,508822 (defun strip-cadrs (x)strip-cadrs12202,509011 (defun strip-cddrs (x)strip-cddrs12209,509211 (defun global-set-lst (alist wrld)global-set-lst12214,509355 (defmacro cons-term1-body-mv2 ()cons-term1-body-mv212221,509612 (defun cons-term1-mv2 (fn args form)cons-term1-mv212231,509951 (defun sublis-var1 (alist form)sublis-var112238,510140 (defun sublis-var1-lst (alist l)sublis-var1-lst12258,511006 (defun sublis-var (alist form)sublis-var12273,511613 (defun sublis-var-lst (alist l)sublis-var-lst12299,512689 (defun subcor-var1 (vars terms var)subcor-var112308,513013 (defun subcor-var (vars terms form)subcor-var12319,513414 (defun subcor-var-lst (vars terms forms)subcor-var-lst12335,514093 (defun car-cdr-nest1 (term ad-lst n)car-cdr-nest112349,514625 (defun car-cdr-nest (term)car-cdr-nest12363,515133 (defun collect-non-trivial-bindings (vars vals)collect-non-trivial-bindings12386,515838 (defun untranslate-and (p q iff-flg)untranslate-and12393,516132 (defun untranslate-or (p q)untranslate-or12409,516572 (defun case-length (key term)case-length12419,516829 (defun cond-length (term)cond-length12453,518174 (defconst *untranslate-boolean-primitives**untranslate-boolean-primitives*12461,518408 (defun right-associated-args (fn term)right-associated-args12464,518464 (defun dumb-negate-lit (term)dumb-negate-lit12478,518976 (defun dumb-negate-lit-lst (lst)dumb-negate-lit-lst12495,519541 (defun term-stobjs-out-alist (vars args alist wrld)term-stobjs-out-alist12502,519717 (defun term-stobjs-out (term alist wrld)term-stobjs-out12511,520014 (defun accessor-root (n term wrld)accessor-root12554,521622 (defvar *load-compiled-stack* nil)*load-compiled-stack*12591,523466 (defun observe-raw-mode-setting (v state)observe-raw-mode-setting12594,523519 (defmacro progn! (&rest r)progn!12675,527630 (defmacro progn! (&rest r)progn!12804,533688 (defun ld-redefinition-action (state)ld-redefinition-action12844,535708 (deflabel redefining-programsredefining-programs13013,545338 (defun chk-ld-redefinition-action (val ctx state)chk-ld-redefinition-action13142,552031 (defun set-ld-redefinition-action (val state)set-ld-redefinition-action13150,552372 (defmacro redef nilredef13157,552584 (defmacro redef! nilredef!13176,553167 (defmacro redef+ nilredef+13195,553751 (defmacro redef- nilredef-13243,555476 (defun chk-current-package (val ctx state)chk-current-package13277,556495 (defun set-current-package (val state)set-current-package13282,556702 (defun standard-oi (state)standard-oi13293,556977 (defun read-standard-oi (state)read-standard-oi13319,558196 (defun chk-standard-oi (val ctx state)chk-standard-oi13332,558684 (defun set-standard-oi (val state)set-standard-oi13345,559058 (defun standard-co (state)standard-co13351,559250 (defun chk-standard-co (val ctx state)chk-standard-co13373,560196 (defun set-standard-co (val state)set-standard-co13380,560398 (defun proofs-co (state)proofs-co13387,560566 (defun chk-proofs-co (val ctx state)chk-proofs-co13405,561193 (defun set-proofs-co (val state)set-proofs-co13412,561391 (deflabel promptprompt13419,561551 (defun ld-prompt (state)ld-prompt13440,562440 (defun chk-ld-prompt (val ctx state)chk-ld-prompt13483,564518 (defun set-ld-prompt (val state)set-ld-prompt13494,564934 (defun ld-keyword-aliases (state)ld-keyword-aliases13501,565094 (defun ld-keyword-aliasesp (key val wrld)ld-keyword-aliasesp13581,568829 (defmacro add-ld-keyword-alias! (key val)add-ld-keyword-alias!13608,569712 (defmacro add-ld-keyword-alias! (key val)add-ld-keyword-alias!13615,569957 (defmacro add-ld-keyword-alias (key val)add-ld-keyword-alias13619,570036 (defmacro set-ld-keyword-aliases! (alist)set-ld-keyword-aliases!13623,570141 (defmacro set-ld-keyword-aliases! (alist)set-ld-keyword-aliases!13630,570395 (defmacro set-ld-keyword-aliases (alist &optional state)set-ld-keyword-aliases13634,570472 (defun ld-missing-input-ok (state)ld-missing-input-ok13642,570730 (defun msgp (x)msgp13664,571669 (defun chk-ld-missing-input-ok (val ctx state)chk-ld-missing-input-ok13670,571791 (defun set-ld-missing-input-ok (val state)set-ld-missing-input-ok13677,572043 (defun ld-pre-eval-filter (state)ld-pre-eval-filter13684,572243 (defun new-namep (name wrld)new-namep13714,573685 (defun chk-ld-pre-eval-filter (val ctx state)chk-ld-pre-eval-filter13807,577481 (defun set-ld-pre-eval-filter (val state)set-ld-pre-eval-filter13817,577895 (defun ld-pre-eval-print (state)ld-pre-eval-print13824,578091 (defun chk-ld-pre-eval-print (val ctx state)chk-ld-pre-eval-print13860,579960 (defun set-ld-pre-eval-print (val state)set-ld-pre-eval-print13865,580139 (defun ld-post-eval-print (state)ld-post-eval-print13872,580331 (defun chk-ld-post-eval-print (val ctx state)chk-ld-post-eval-print13934,583964 (defun set-ld-post-eval-print (val state)set-ld-post-eval-print13939,584159 (defun ld-error-triples (state)ld-error-triples13946,584355 (defun chk-ld-error-triples (val ctx state)chk-ld-error-triples13971,585517 (defun set-ld-error-triples (val state)set-ld-error-triples13976,585687 (defun ld-error-action (state)ld-error-action13983,585875 (defun chk-ld-error-action (val ctx state)chk-ld-error-action14098,592162 (defun set-ld-error-action (val state)set-ld-error-action14103,592358 (defun ld-query-control-alist (state)ld-query-control-alist14110,592542 (defun ld-query-control-alistp (val)ld-query-control-alistp14168,595785 (defun cdr-assoc-query-id (id alist)cdr-assoc-query-id14182,596275 (defun chk-ld-query-control-alist (val ctx state)chk-ld-query-control-alist14187,596436 (defun set-ld-query-control-alist (val state)set-ld-query-control-alist14193,596616 (defun ld-verbose (state)ld-verbose14200,596828 (defun chk-ld-verbose (val ctx state)chk-ld-verbose14231,598267 (defun set-ld-verbose (val state)set-ld-verbose14240,598544 (defconst *nqthm-to-acl2-primitives**nqthm-to-acl2-primitives*14247,598708 (defconst *nqthm-to-acl2-commands**nqthm-to-acl2-commands*14315,600676 (defun nqthm-to-acl2-fn (name state)nqthm-to-acl2-fn14407,604376 (defmacro nqthm-to-acl2 (x)nqthm-to-acl214526,608911 (defun allocate-fixnum-range (fixnum-lo fixnum-hi)allocate-fixnum-range14734,616969 (defmacro allegro-allocate-slowly (&key (free-bytes-new-other '1024)allegro-allocate-slowly14793,619795 (defun allegro-allocate-slowly-fn (free-bytes-new-otherallegro-allocate-slowly-fn14802,620354 (defmacro clear-pstk ()clear-pstk14839,621971 (defconst *pstk-vars**pstk-vars*14846,622151 (defun pstk-bindings-and-args (args vars)pstk-bindings-and-args14861,622374 (defmacro pstk (form)pstk14893,623455 (defun pstack-fn (allp state)pstack-fn14941,625291 (defmacro pstack (&optional allp)pstack14964,626150 (defun verbose-pstack (flg-or-list)verbose-pstack15033,628589 (defun pop-inhibit-output-lst-stack (state)pop-inhibit-output-lst-stack15066,629838 (defun push-inhibit-output-lst-stack (state)push-inhibit-output-lst-stack15076,630269 (defun set-gc-threshold$-fn (new-threshold verbose-p)set-gc-threshold$-fn15082,630514 (defmacro set-gc-threshold$ (new-threshold &optional (verbose-p 't))set-gc-threshold$15150,633472 parallel.lisp,4019 (defdoc deflockdeflock49,1910 (defdoc compiling-acl2pcompiling-acl2p133,5099 (defdoc parallelparallel172,6442 (defdoc parallelism-buildparallelism-build182,6596 (defun set-parallel-execution-fn (val ctx state)set-parallel-execution-fn190,6749 (defmacro set-parallel-execution (value)set-parallel-execution231,8769 (defdoc parallel-executionparallel-execution291,11345 (defun waterfall-printing-value-for-parallelism-value (value)waterfall-printing-value-for-parallelism-value301,11627 (defdoc unsupported-waterfall-parallelism-featuresunsupported-waterfall-parallelism-features335,13125 (defdoc unsupported-parallelism-featuresunsupported-parallelism-features492,21395 (defdoc waterfall-printingwaterfall-printing534,23391 (defdoc waterfall-parallelismwaterfall-parallelism542,23565 (defun print-set-waterfall-parallelism-notice (val print-val state)print-set-waterfall-parallelism-notice550,23738 (defun check-for-no-override-hints (ctx state)check-for-no-override-hints586,25276 (defun set-waterfall-parallelism-fn (val ctx state)set-waterfall-parallelism-fn616,26726 (defmacro set-waterfall-parallelism1 (val)set-waterfall-parallelism1706,31141 (defmacro save-memo-table ()save-memo-table735,32355 (defun clear-memo-table-events (alist acc)clear-memo-table-events743,32539 (defmacro clear-memo-table ()clear-memo-table751,32808 (defmacro save-and-clear-memoization-settings ()save-and-clear-memoization-settings759,33029 (defun set-memo-table-events (alist acc)set-memo-table-events785,33703 (defmacro restore-memoization-settings ()restore-memoization-settings793,33979 (defmacro set-waterfall-parallelism (val)set-waterfall-parallelism819,34577 (defdoc waterfall-parallelism-for-book-certificationwaterfall-parallelism-for-book-certification970,41910 (defun set-waterfall-printing-fn (val ctx state)set-waterfall-printing-fn1001,43191 (defmacro set-waterfall-printing (val)set-waterfall-printing1023,44114 (defun set-waterfall-parallelism-hacks-enabled-guard (wrld)set-waterfall-parallelism-hacks-enabled-guard1099,47512 (defmacro set-waterfall-parallelism-hacks-enabled (val)set-waterfall-parallelism-hacks-enabled1110,47976 (defmacro set-waterfall-parallelism-hacks-enabled! (val)set-waterfall-parallelism-hacks-enabled!1148,49563 (defdoc parallelism-at-the-top-levelparallelism-at-the-top-level1168,50190 (defdoc parallelism-tutorialparallelism-tutorial1223,52539 (defdoc granularitygranularity1507,63922 (defdoc parallelism-performanceparallelism-performance1615,68363 (defdoc early-terminationearly-termination1652,70135 (defdoc parallel-pushing-of-subgoals-for-inductionparallel-pushing-of-subgoals-for-induction1702,72336 (defun caar-is-declarep (x)caar-is-declarep1744,74768 (defun declare-granularity-p (x)declare-granularity-p1753,74979 (defun check-and-parse-for-granularity-form (x)check-and-parse-for-granularity-form1766,75357 (defmacro pargs (&rest forms)pargs1803,77045 (defmacro plet (&rest forms)plet1858,79471 (defun binary-pand (x y)binary-pand1908,81361 (defmacro pand (&rest forms)pand1916,81509 (defun binary-por (x y)binary-por2010,85305 (defmacro por (&rest forms)por2018,85461 (defun or-list (x)or-list2083,87981 (defun and-list (x)and-list2091,88136 (defun cpu-core-count (state)cpu-core-count2098,88283 (defmacro spec-mv-let (bindings computation body)spec-mv-let2324,98021 (defdoc error-triples-and-parallelismerror-triples-and-parallelism2448,103112 (defdoc with-output-lockwith-output-lock2498,105651 (defdoc acl2p-key-checkpointsacl2p-key-checkpoints2600,108604 (defun set-total-parallelism-work-limit-fn (val state)set-total-parallelism-work-limit-fn2715,113681 (defmacro set-total-parallelism-work-limit (val)set-total-parallelism-work-limit2720,113889 (defun set-total-parallelism-work-limit-error-fn (val state)set-total-parallelism-work-limit-error-fn2776,116331 (defmacro set-total-parallelism-work-limit-error (val)set-total-parallelism-work-limit-error2781,116543 translate.lisp,19625 (deflabel syntaxsyntax23,881 (deflabel termterm41,1351 (defun termp (x w)termp228,11066 (defun term-listp (x w)term-listp254,11935 (defun computed-hint-tuple-listp (x wrld)computed-hint-tuple-listp261,12065 (defun macro-args (x w)macro-args281,12620 (defconst *macro-expansion-ctx* "macro expansion")*macro-expansion-ctx*384,15627 (defun error-trace-suggestion (two-leading-spaces)error-trace-suggestion386,15679 (defun ignored-attachment-msg (ignored-attachment)ignored-attachment-msg397,16030 (defdoc ignored-attachmentignored-attachment405,16439 (defun ev-fncall-null-body-er-msg (ignored-attachment fn args)ev-fncall-null-body-er-msg518,20496 (defun ev-fncall-null-body-er (ignored-attachment fn args latches)ev-fncall-null-body-er539,21266 (defun ev-fncall-creator-er-msg (fn)ev-fncall-creator-er-msg544,21420 (defun unknown-pkg-error-msg (fn pkg-name)unknown-pkg-error-msg556,22021 (defun illegal-msg ()illegal-msg562,22208 (defun program-only-er-msg (fn args safe-mode)program-only-er-msg566,22299 (defconst *safe-mode-guard-er-addendum**safe-mode-guard-er-addendum*578,22906 (defun find-first-non-nil (lst)find-first-non-nil583,23150 (defun latch-stobjs1 (stobjs-out vals latches)latch-stobjs1590,23348 (defun latch-stobjs (stobjs-out vals latches)latch-stobjs643,25893 (defvar *raw-guard-warningp*)*raw-guard-warningp*668,27122 (defun actual-stobjs-out1 (stobjs-in args user-stobj-alist)actual-stobjs-out1670,27153 (defun apply-symbol-alist (alist lst acc)apply-symbol-alist686,27960 (defun apply-inverse-symbol-alist (alist lst acc)apply-inverse-symbol-alist700,28583 (defun actual-stobjs-out (fn args wrld user-stobj-alist)actual-stobjs-out715,29120 (defun raw-ev-fncall (fn args latches w user-stobj-alistraw-ev-fncall726,29600 (defun translated-acl2-unwind-protectp4 (term)translated-acl2-unwind-protectp4822,34182 (defun translated-acl2-unwind-protectp (term)translated-acl2-unwind-protectp897,37781 (defun stobjp (x known-stobjs w)stobjp908,38185 (defun acl2-system-namep (name wrld)acl2-system-namep1254,53562 (defparameter *ev-shortcut-okp**ev-shortcut-okp*1327,55827 (defun w-of-any-state (st)w-of-any-state1342,56491 (defun untranslate-preprocess-fn (wrld)untranslate-preprocess-fn1355,57054 (defmacro untranslate* (term iff-flg wrld)untranslate*1361,57319 (defmacro raw-guard-warningp-binding ()raw-guard-warningp-binding1377,57972 (defun untouchable-fn-p (fn w)untouchable-fn-p1405,59492 (defun save-ev-fncall-guard-er (fn guard stobjs-in args)save-ev-fncall-guard-er1409,59659 (defrec attachmentattachment1418,59967 (defrec attachment-componentattachment-component1425,60085 (defun attachment-record-pairs (records acc)attachment-record-pairs1432,60202 (defun all-attachments (wrld)all-attachments1440,60438 (defun gc-off1 (guard-checking-on)gc-off11444,60567 (defun gc-off (state)gc-off1448,60662 (defun return-last-lookup (sym wrld)return-last-lookup1465,61157 (defun make-let-or-let* (bindings body)make-let-or-let*1480,61630 (defmacro untranslate*-lst (lst iff-flg wrld)untranslate*-lst1494,62106 (defun apply-user-stobj-alist-or-kwote (user-stobj-alist lst acc)apply-user-stobj-alist-or-kwote1505,62400 (defun ev-fncall-rec-logical (fn args w user-stobj-alist big-n safe-mode gc-offev-fncall-rec-logical1544,64230 (defun ev-fncall-rec (fn args w user-stobj-alist big-n safe-mode gc-off latchesev-fncall-rec1869,77452 (defun ev-rec-return-last (fn arg2 arg3 alist w user-stobj-alist big-nev-rec-return-last1916,79319 (defun ev-rec (form alist w user-stobj-alist big-n safe-mode gc-off latchesev-rec2016,84242 (defun ev-rec-lst (lst alist w user-stobj-alist big-n safe-mode gc-off latchesev-rec-lst2202,93566 (defun ev-rec-acl2-unwind-protect (form alist w user-stobj-alist big-nev-rec-acl2-unwind-protect2235,95036 (defun ev-fncall-w (fn args w user-stobj-alist safe-mode gc-offev-fncall-w2456,105118 (defun ev-fncall-guard-er-msg (fn guard stobjs-in args w user-stobj-alist extra)ev-fncall-guard-er-msg2524,107694 (defun ev-fncall-guard-er (fn args w user-stobj-alist latches extra)ev-fncall-guard-er2580,110120 (defun ev-fncall-msg (val wrld user-stobj-alist)ev-fncall-msg2591,110521 (defun untranslate1 (term iff-flg untrans-tbl preprocess-fn wrld)untranslate12642,112353 (defun untranslate-cons1 (term untrans-tbl preprocess-fn wrld)untranslate-cons12871,124505 (defun untranslate-cons (term untrans-tbl preprocess-fn wrld)untranslate-cons2891,125507 (defun untranslate-if (term iff-flg untrans-tbl preprocess-fn wrld)untranslate-if2902,125952 (defun untranslate-into-case-clauses (key term iff-flg untrans-tbl preprocess-fnuntranslate-into-case-clauses2921,126915 (defun untranslate-into-cond-clauses (term iff-flg untrans-tbl preprocess-fnuntranslate-into-cond-clauses2963,129236 (defun untranslate1-lst (lst iff-flg untrans-tbl preprocess-fn wrld)untranslate1-lst2978,129977 (defun ev-fncall (fn args state latches hard-error-returns-nilp aok)ev-fncall2989,130410 (defun ev (form alist state latches hard-error-returns-nilp aok)ev3002,130989 (defun ev-lst (lst alist state latches hard-error-returns-nilp aok)ev-lst3036,132293 (defun untranslate (term iff-flg wrld)untranslate3054,133033 (defun untranslate-lst (lst iff-flg wrld)untranslate-lst3081,134112 (defun ev-w (form alist w user-stobj-alist safe-mode gc-offev-w3119,135670 (defun ev-w-lst (lst alist w user-stobj-alist safe-mode gc-offev-w-lst3180,138215 (defun silent-error (state)silent-error3306,144400 (defmacro cmp-to-error-triple (form)cmp-to-error-triple3309,144449 (defmacro cmp-to-error-triple@par (form)cmp-to-error-triple@par3325,145007 (defmacro cmp-to-error-double (form)cmp-to-error-double3341,145590 (defmacro cmp-and-value-to-error-quadruple (form)cmp-and-value-to-error-quadruple3357,146221 (defmacro cmp-and-value-to-error-quadruple@par (form)cmp-and-value-to-error-quadruple@par3379,147135 (defun er-cmp-fn (ctx msg)er-cmp-fn3399,147968 (defmacro er-cmp (ctx str &rest args)er-cmp3407,148148 (defmacro value-cmp (x)value-cmp3414,148333 (defun er-progn-fn-cmp (lst)er-progn-fn-cmp3417,148374 (defmacro er-progn-cmp (&rest lst)er-progn-cmp3439,149332 (defmacro er-let*-cmp (alist body)er-let*-cmp3444,149477 (defun warning1-cw (ctx summary str alist wrld state-vars)warning1-cw3469,150434 (defmacro warning$-cw1 (&rest args)warning$-cw13476,150638 (defmacro warning$-cw (ctx &rest args)warning$-cw3501,151460 (defun chk-length-and-keys (actuals form wrld)chk-length-and-keys3514,152006 (defun bind-macro-args-keys1 (args actuals allow-flg alist form wrldbind-macro-args-keys13534,152854 (defun bind-macro-args-keys (args actuals alist form wrld state-vars)bind-macro-args-keys3593,155712 (defun bind-macro-args-after-rest (args actuals alist form wrld state-vars)bind-macro-args-after-rest3611,156429 (defun bind-macro-args-optional (args actuals alist form wrld state-vars)bind-macro-args-optional3621,156826 (defun bind-macro-args1 (args actuals alist form wrld state-vars)bind-macro-args13656,158336 (defun bind-macro-args (args form wrld state-vars)bind-macro-args3682,159465 (defun macroexpand1-cmp (x ctx wrld state-vars)macroexpand1-cmp3690,159815 (defun macroexpand1 (x ctx state)macroexpand13749,162403 (defun chk-declare (form ctx)chk-declare3753,162562 (defun collect-dcls (l ctx)collect-dcls3773,163517 (defun acceptable-dcls-alist (state-vars)acceptable-dcls-alist3784,163898 (defconst *documentation-strings-permitted**documentation-strings-permitted*3813,165111 (defconst *dcl-explanation-alist**dcl-explanation-alist*3819,165262 (defun tilde-*-conjunction-phrase1 (syms alist)tilde-*-conjunction-phrase13829,165784 (defun tilde-*-conjunction-phrase (syms alist)tilde-*-conjunction-phrase3840,166262 (defun collect-non-legal-variableps (lst)collect-non-legal-variableps3857,166915 (defun optimize-alistp (lst)optimize-alistp3863,167143 (defun chk-dcl-lst (l vars binder ctx wrld state-vars)chk-dcl-lst3876,167564 (defun number-of-strings (l)number-of-strings4054,177039 (defun remove-strings (l)remove-strings4060,177202 (defun get-string (l)get-string4066,177368 (defun collect-declarations-cmp (lst vars binder ctx wrld state-vars)collect-declarations-cmp4071,177492 (defun collect-declarations (lst vars binder state ctx)collect-declarations4111,179484 (defun listify (l)listify4116,179744 (defun translate-declaration-to-guard-var-lst (x var-lst wrld)translate-declaration-to-guard-var-lst4120,179842 (defun translate-dcl-lst (edcls wrld)translate-dcl-lst4134,180505 (defun dcl-guardian (term-lst)dcl-guardian4148,181101 (defun ignore-vars (dcls)ignore-vars4181,182638 (defun ignorable-vars (dcls)ignorable-vars4187,182821 (defun mv-nth-list (var i maximum)mv-nth-list4193,183016 (defabbrev translate-bind (x val bindings)translate-bind4198,183193 (defun translate-deref (x bindings)translate-deref4204,183328 (defun translate-unbound (x bindings)translate-unbound4220,183894 (defun listlis (l1 l2)listlis4228,184098 (defun find-first-var (term)find-first-var4238,184305 (defun find-first-var-lst (lst)find-first-var-lst4246,184543 (defun find-first-fnsymb (term)find-first-fnsymb4254,184714 (defun find-first-fnsymb-lst (lst)find-first-fnsymb-lst4262,184988 (defun find-pkg-witness (term)find-pkg-witness4268,185149 (defmacro trans-er (&rest args)trans-er4287,185865 (defmacro trans-er+ (form ctx str &rest args)trans-er+4303,186600 (defmacro trans-er+? (cform x ctx str &rest args)trans-er+?4322,187370 (defmacro trans-value (x &optional (bindings 'bindings))trans-value4338,188024 (defmacro trans-er-let* (alist body)trans-er-let*4344,188171 (defun hide-ignored-actuals (ignore-vars bound-vars value-forms)hide-ignored-actuals4367,189008 (defun augment-ignore-vars (bound-vars value-forms acc)augment-ignore-vars4393,189977 (defun compute-stobj-flags (lst known-stobjs w)compute-stobj-flags4501,195792 (defun prettyify-stobj-flags (lst)prettyify-stobj-flags4514,196285 (defun unprettyify-stobj-flags (lst)unprettyify-stobj-flags4523,196571 (defun prettyify-stobjs-out (stobjs-out)prettyify-stobjs-out4528,196745 (defun defstobj-supporterp (name wrld)defstobj-supporterp4537,197041 (defun stobj-creatorp (name wrld)stobj-creatorp4553,197754 (defun defstobj-fnname (root key1 key2 renaming-alist)defstobj-fnname4565,198172 (defun parse-with-local-stobj (x)parse-with-local-stobj4624,200785 (defun ffnnamep (fn term)ffnnamep4644,201292 (defun ffnnamep-lst (fn l)ffnnamep-lst4658,201745 (defconst *synp-trans-err-string**synp-trans-err-string*4668,201966 (defconst *ttag-fns-and-macros**ttag-fns-and-macros*4674,202314 (defun unknown-binding-msg (stobjs-bound str1 str2 str3)unknown-binding-msg4696,202993 (defconst *oneify-primitives**oneify-primitives*4714,204135 (defconst *macros-for-nonexpansion-in-raw-lisp**macros-for-nonexpansion-in-raw-lisp*4760,205908 (defun chk-no-duplicate-defuns-cmp (lst ctx)chk-no-duplicate-defuns-cmp4820,208214 (defun chk-no-duplicate-defuns (lst ctx state)chk-no-duplicate-defuns4830,208634 (defun chk-state-ok-msg (wrld)chk-state-ok-msg4833,208745 (defun chk-state-ok (ctx wrld state)chk-state-ok4848,209371 (defun chk-arglist-msg (args chk-state wrld)chk-arglist-msg4853,209517 (defun msg-to-cmp (ctx msg)msg-to-cmp4870,210251 (defun chk-arglist-cmp (args chk-state ctx wrld)chk-arglist-cmp4879,210492 (defun@par chk-arglist (args chk-state ctx wrld state)chk-arglist4882,210600 (defun logical-name-type (name wrld quietp)logical-name-type4887,210786 (defun chk-all-but-new-name-cmp (name ctx new-type w)chk-all-but-new-name-cmp4912,211785 (defun chk-all-but-new-name (name ctx new-type w state)chk-all-but-new-name4943,213042 (defun chk-defuns-tuples-cmp (lst local-p ctx wrld state-vars)chk-defuns-tuples-cmp4946,213171 (defun chk-defuns-tuples (lst local-p ctx wrld state)chk-defuns-tuples4990,215049 (defun non-trivial-encapsulate-ee-entries (embedded-event-lst)non-trivial-encapsulate-ee-entries4994,215243 (defconst *ec-call-bad-ops**ec-call-bad-ops*5003,215651 (defmacro return-last-call (fn &rest args)return-last-call5013,216044 (defmacro prog2$-call (x y)prog2$-call5016,216131 (defun name-dropper (lst)name-dropper5019,216205 (defun first-assoc-eq (keys alist)first-assoc-eq5035,216920 (defun context-for-encapsulate-pass-2 (wrld in-local-flg)context-for-encapsulate-pass-25043,217185 (defconst *brr-globals**brr-globals*5073,218600 (defun unknown-binding-msg-er (x ctx stobjs-bound str1 str2 str3)unknown-binding-msg-er5079,218694 (defun congruent-stobj-rep (name wrld)congruent-stobj-rep5099,219680 (defun congruent-stobjsp (st1 st2 wrld)congruent-stobjsp5105,219881 (defun stobjs-in-out1 (stobjs-in args known-stobjs wrld alist new-stobjs-in)stobjs-in-out15109,219998 (defun stobjs-in-matchp (stobjs-in args)stobjs-in-matchp5134,221158 (defun stobjs-in-out (fn args stobjs-out known-stobjs wrld)stobjs-in-out5142,221421 (defun non-trivial-stobj-binding (stobj-flags bindings)non-trivial-stobj-binding5180,223211 (defun formalized-varlistp (varlist formal-lst)formalized-varlistp5192,223799 (defun throw-nonexec-error-p1 (targ1 targ2 name formals)throw-nonexec-error-p15204,224280 (defun throw-nonexec-error-p (body name formals)throw-nonexec-error-p5224,225064 (defun chk-flet-declarations (names decls declare-form ctx)chk-flet-declarations5239,225779 (defun chk-flet-declare-form (names declare-form ctx)chk-flet-declare-form5265,227103 (defun chk-flet-declare-form-list (names declare-form-list ctx)chk-flet-declare-form-list5280,227704 (defun translate-stobj-type-to-guard (x var wrld)translate-stobj-type-to-guard5287,228003 (defun get-stobj-creator (stobj wrld)get-stobj-creator5322,229788 (defun stobj-updater-guess-from-accessor (accessor)stobj-updater-guess-from-accessor5360,231498 (defun parse-stobj-let1 (bindings producer-vars bound-vars actuals stobjparse-stobj-let15381,232254 (defun illegal-stobj-let-msg (msg form)illegal-stobj-let-msg5473,236376 (defun parse-stobj-let (x)parse-stobj-let5477,236500 (defun pairlis-x1 (x1 lst)pairlis-x15571,240606 (defun pairlis-x2 (lst x2)pairlis-x25579,240794 (defun no-duplicatesp-checks-for-stobj-let-actuals/alist (alist)no-duplicatesp-checks-for-stobj-let-actuals/alist5587,240986 (defun no-duplicatesp-checks-for-stobj-let-actuals (exprs alist)no-duplicatesp-checks-for-stobj-let-actuals5601,241683 (defun stobj-let-fn (x)stobj-let-fn5625,242710 (defun the-live-var (name)the-live-var5659,244208 (defun the-live-var-bindings (stobj-names)the-live-var-bindings5678,245059 (defun the-maybe-live-var-bindings (stobj-names)the-maybe-live-var-bindings5684,245301 (defun stobj-let-fn-raw (x)stobj-let-fn-raw5694,245705 (defun stobj-field-accessor-p (fn stobj wrld)stobj-field-accessor-p5780,249171 (defun chk-stobj-let/bindings (stobj bound-vars actuals wrld)chk-stobj-let/bindings5802,249758 (defun chk-stobj-let/updaters1 (updaters accessors lst)chk-stobj-let/updaters15840,251657 (defun chk-stobj-let/updaters (updaters corresp-accessor-fns stobj wrld)chk-stobj-let/updaters5870,253133 (defun chk-stobj-let (bound-vars actuals stobj updaters corresp-accessor-fnschk-stobj-let5877,253393 (defun all-nils-or-x (x lst)all-nils-or-x5899,254287 (defun stobj-field-fn-of-stobj-type-p (fn wrld)stobj-field-fn-of-stobj-type-p5908,254549 (defun stobj-recognizer-p (fn wrld)stobj-recognizer-p5920,255105 (defmacro trans-or (form1 condition form2 extra-msg)trans-or5929,255380 (defun translate11-flet-alist (form fives stobjs-out bindings known-stobjstranslate11-flet-alist5960,256723 (defun translate11-flet-alist1 (form five stobjs-out bindings known-stobjstranslate11-flet-alist15974,257381 (defun translate11-flet (x stobjs-out bindings known-stobjs flet-alisttranslate11-flet6176,266964 (defun translate-stobj-calls (calls len bindings known-stobjs flet-alisttranslate-stobj-calls6244,269904 (defun translate11-let (x tbody0 targs stobjs-out bindings known-stobjstranslate11-let6280,271725 (defun translate11-let* (x tbody targs stobjs-out bindings known-stobjstranslate11-let*6486,282473 (defun translate11-mv-let (x tbody0 stobjs-out bindings known-stobjstranslate11-mv-let6511,283698 (defun translate11-wormhole-eval (x y z bindings flet-alist ctx wrldtranslate11-wormhole-eval6731,294808 (defun translate11-call (form fn args stobjs-out stobjs-out2 bindingstranslate11-call6826,299142 (defun translate11 (x stobjs-out bindings known-stobjs flet-alisttranslate117012,308534 (defun translate11-lst (lst stobjs-out bindings known-stobjstranslate11-lst8196,367641 (defun translate1-cmp (x stobjs-out bindings known-stobjs ctx w state-vars)translate1-cmp8318,373932 (defun@par translate1 (x stobjs-out bindings known-stobjs ctx w state)translate18383,377021 (defun collect-programs (names wrld)collect-programs8388,377238 (defun all-fnnames1 (flg x acc)all-fnnames18420,378393 (defmacro all-fnnames (term)all-fnnames8439,379085 (defmacro all-fnnames-lst (lst)all-fnnames-lst8442,379148 (defun translate-cmp (x stobjs-out logic-modep known-stobjs ctx w state-vars)translate-cmp8445,379211 (defun@par translate (x stobjs-out logic-modep known-stobjs ctx w state)translate8470,380368 (defun translatable-p (form stobjs-out bindings known-stobjs ctx wrld)translatable-p8501,381836 (defmacro chk-translatable (form shape)chk-translatable8508,382127 (defun replaced-stobj (name)replaced-stobj8534,383381 (defun replace-stobjs1 (stobjs-out val)replace-stobjs18540,383550 (defun replace-stobjs (stobjs-out val)replace-stobjs8548,383846 (defun non-stobjps (vars known-stobjs w)non-stobjps8589,385739 (defun user-stobjsp (stobjs-out)user-stobjsp8596,385990 (defun put-assoc-eq-alist (alist1 alist2)put-assoc-eq-alist8603,386194 (defun-one-output chk-user-stobj-alist (stobjs alist acc ctx)chk-user-stobj-alist8637,387915 (defun user-stobj-alist-safe (ctx stobjs state)user-stobj-alist-safe8662,389006 (defun ev-for-trans-eval (trans vars stobjs-out ctx state aok)ev-for-trans-eval8671,389280 (defun ev-w-for-trans-eval (trans vars stobjs-out ctx state aok)ev-w-for-trans-eval8723,391570 (defun trans-eval (form ctx state aok)trans-eval8759,392847 (defun simple-translate-and-eval (x alist ok-stobj-names msg ctx wrld statesimple-translate-and-eval8805,394717 (defun error-fms-cw (hardp ctx str alist)error-fms-cw8899,399341 (defmacro error-fms@par (&rest args)error-fms@par8916,400022 (defun simple-translate-and-eval-cmp (x alist ok-stobj-names msg ctx wrld statesimple-translate-and-eval-cmp8919,400086 (defun simple-translate-and-eval-error-double (x alist ok-stobj-names msg ctxsimple-translate-and-eval-error-double8981,403056 (defun simple-translate-and-eval@par (x alist ok-stobj-names msg ctx wrld statesimple-translate-and-eval@par9010,404340 (defun tilde-*-alist-phrase1 (alist evisc-tuple level)tilde-*-alist-phrase19026,405172 (defun tilde-*-alist-phrase (alist evisc-tuple level)tilde-*-alist-phrase9032,405438 (defun set-temp-touchable-fns (x state)set-temp-touchable-fns9044,405733 (defun set-temp-touchable-vars (x state)set-temp-touchable-vars9061,406428 (defun clear-temp-touchable-fns (state)clear-temp-touchable-fns9078,407126 (defun clear-temp-touchable-vars (state)clear-temp-touchable-vars9081,407215 (defun mapcar$ (fn l state)mapcar$9097,407980 (defun mapdo (fn l state)mapdo9119,408884 (defun always (fn l state)always9132,409343 (defun thereis (fn l state)thereis9146,409760 type-set-a.lisp,3571 (defconst *ts-non-negative-integer* (ts-union0 *ts-zero**ts-non-negative-integer*124,5901 (defconst *ts-non-positive-integer* (ts-union0 *ts-zero**ts-non-positive-integer*127,6030 (defconst *ts-integer* (ts-union0 *ts-positive-integer**ts-integer*130,6159 (defconst *ts-rational* (ts-union0 *ts-integer**ts-rational*134,6318 (defconst *ts-real* (ts-union0 *ts-integer**ts-real*141,6570 (defconst *ts-complex* (ts-union0 *ts-complex-rational**ts-complex*151,6940 (defconst *ts-acl2-number**ts-acl2-number*160,7355 (defconst *ts-rational-acl2-number* (ts-union0 *ts-rational**ts-rational-acl2-number*166,7525 (defconst *ts-non-rational-acl2-number* (ts-union0 *ts-positive-non-ratio**ts-non-rational-acl2-number*170,7683 (defconst *ts-negative-rational* (ts-union0 *ts-negative-integer**ts-negative-rational*174,7913 (defconst *ts-positive-rational* (ts-union0 *ts-positive-integer**ts-positive-rational*177,8046 (defconst *ts-non-positive-rational* (ts-union0 *ts-zero**ts-non-positive-rational*180,8179 (defconst *ts-non-negative-rational* (ts-union0 *ts-zero**ts-non-negative-rational*183,8311 (defconst *ts-ratio* (ts-union0 *ts-positive-ratio**ts-ratio*186,8443 (defconst *ts-non-ratio* (ts-union0 *ts-positive-non-ratio**ts-non-ratio*197,8772 (defconst *ts-negative-real* (ts-union0 *ts-negative-integer**ts-negative-real*200,8895 (defconst *ts-positive-real* (ts-union0 *ts-positive-integer**ts-positive-real*204,9084 (defconst *ts-non-positive-real* (ts-union0 *ts-zero**ts-non-positive-real*208,9273 (defconst *ts-non-negative-real* (ts-union0 *ts-zero**ts-non-negative-real*211,9393 (defconst *ts-cons* (ts-union0 *ts-proper-cons**ts-cons*215,9515 (defconst *ts-boolean* (ts-union0 *ts-nil* *ts-t*))*ts-boolean*218,9616 (defconst *ts-true-list* (ts-union0 *ts-nil* *ts-proper-cons*))*ts-true-list*220,9669 (defconst *ts-non-nil* (ts-complement0 *ts-nil*))*ts-non-nil*222,9734 (defconst *ts-symbol* (ts-union0 *ts-nil**ts-symbol*224,9785 (defconst *ts-true-list-or-string* (ts-union0 *ts-true-list* *ts-string*))*ts-true-list-or-string*228,9929 (defconst *ts-empty* 0)*ts-empty*230,10005 (defconst *ts-unknown* -1)*ts-unknown*232,10030 (defun one-bit-type-setp (ts)one-bit-type-setp240,10392 (defconst *code-type-set-alist**code-type-set-alist*278,12045 (defun logior-lst (lst ans)logior-lst362,15691 (defun logand-lst (lst ans)logand-lst368,15821 (defun ts-complement-fn (x)ts-complement-fn376,15970 (defun ts-union-fn (x)ts-union-fn382,16117 (defun ts-intersection-fn (x)ts-intersection-fn392,16425 (defun eval-type-set (x)eval-type-set402,16744 (defun eval-type-set-lst (x)eval-type-set-lst423,17560 (defmacro ts-complement (x)ts-complement436,17878 (defmacro ts-intersection (&rest x)ts-intersection439,17952 (defmacro ts-union (&rest x)ts-union442,18036 (defmacro ts-subsetp (ts1 ts2)ts-subsetp445,18106 (defun type-set-binary-+-alist-entry (ts1 ts2)type-set-binary-+-alist-entry460,18553 (defun type-set-binary-+-alist1 (i j lst)type-set-binary-+-alist1592,25217 (defun type-set-binary-+-alist (i j lst)type-set-binary-+-alist601,25623 (defun type-set-binary-*-alist-entry (ts1 ts2)type-set-binary-*-alist-entry609,25887 (defun type-set-binary-*-alist1 (i j lst)type-set-binary-*-alist1742,32974 (defun type-set-binary-*-alist (i j lst)type-set-binary-*-alist752,33433 (defun type-set-<-alist-entry (ts1 ts2)type-set-<-alist-entry760,33697 (defun type-set-<-alist1 (i j lst)type-set-<-alist1844,37738 (defun type-set-<-alist (i j lst)type-set-<-alist854,38117 linear-a.lisp,9228 (defabbrev ts-acl2-numberp (ts)ts-acl2-numberp35,1338 (defabbrev ts-rationalp (ts)ts-rationalp38,1407 (defabbrev ts-real/rationalp (ts)ts-real/rationalp41,1470 (defabbrev ts-integerp (ts)ts-integerp47,1618 (defun all-quoteps (lst)all-quoteps50,1679 (defun dumb-occur (x y)dumb-occur57,1826 (defun dumb-occur-lst (x lst)dumb-occur-lst68,2143 (defrec history-entryhistory-entry84,2675 (defun pt-occur (n pt)pt-occur193,9106 (defun pt-intersectp (pt1 pt2)pt-intersectp201,9304 (deflabel ttreettree293,13656 (defun tag-tree-occur (tag val ttree)tag-tree-occur320,14942 (defun remove-tag-from-tag-tree (tag ttree)remove-tag-from-tag-tree328,15118 (defun remove-tag-from-tag-tree! (tag ttree)remove-tag-from-tag-tree!339,15416 (defmacro extend-tag-tree (tag vals ttree)extend-tag-tree354,15866 (defun add-to-tag-tree (tag val ttree)add-to-tag-tree362,16043 (defun add-to-tag-tree! (tag val ttree)add-to-tag-tree!381,16622 (defconst *fake-rune-for-anonymous-enabled-rule**fake-rune-for-anonymous-enabled-rule*404,17454 (defabbrev push-lemma (rune ttree)push-lemma407,17552 (defun delete-assoc-eq-assoc-eq-1 (alist1 alist2)delete-assoc-eq-assoc-eq-1431,18808 (defun delete-assoc-eq-assoc-eq (alist1 alist2)delete-assoc-eq-assoc-eq444,19349 (defun cons-tag-trees1 (ttree1 ttree2 ttree3)cons-tag-trees1450,19526 (defun cons-tag-trees (ttree1 ttree2)cons-tag-trees471,20536 (defmacro tagged-objects (tag ttree)tagged-objects493,21399 (defmacro tagged-objectsp (tag ttree)tagged-objectsp501,21558 (defun tagged-object (tag ttree)tagged-object511,21865 (deflock *ttree-lock*)*ttree-lock*537,23163 (defun@par accumulate-ttree-and-step-limit-into-state (ttree step-limit state)accumulate-ttree-and-step-limit-into-state539,23187 (defun pts-to-ttree-lst (pts)pts-to-ttree-lst582,25156 (defun marry-parents (parents1 parents2)marry-parents594,25695 (defun collect-parents1 (pt ans)collect-parents1606,26088 (defun collect-parents0 (pts ans)collect-parents0615,26319 (defun collect-parents (ttree)collect-parents622,26479 (defun ignore-polyp (parents pt)ignore-polyp631,26844 (defun to-be-ignoredp1 (pts pt)to-be-ignoredp1646,27492 (defun to-be-ignoredp (ttree pt)to-be-ignoredp651,27644 (defrec assumnote (cl-id rune . target) t)assumnote679,28813 (defrec assumptionassumption694,29746 (defrec fc-derivationfc-derivation755,33360 (defun contains-assumptionp (ttree)contains-assumptionp767,33715 (defun contains-assumptionp-fc-derivations (lst)contains-assumptionp-fc-derivations777,34074 (defun remove-assumption-entries-from-type-alist (type-alist)remove-assumption-entries-from-type-alist783,34296 (defun force-assumption1force-assumption1802,35012 (defun dumb-occur-in-type-alist (var type-alist)dumb-occur-in-type-alist832,36345 (defun all-dumb-occur-in-type-alist (vars type-alist)all-dumb-occur-in-type-alist841,36541 (defun force-assumptionforce-assumption848,36755 (defun tag-tree-occur-assumption-nil-1 (lst)tag-tree-occur-assumption-nil-1945,42074 (defun tag-tree-occur-assumption-nil (ttree)tag-tree-occur-assumption-nil952,42290 (defun assumption-free-ttreep (ttree)assumption-free-ttreep959,42535 (defconst *impossible-assumption**impossible-assumption*979,43381 (deflabel linear-arithmeticlinear-arithmetic1483,63384 (defmacro fn-count-evg-max-val ()fn-count-evg-max-val1590,68216 (defmacro fn-count-evg-max-val-neg ()fn-count-evg-max-val-neg1598,68462 (defmacro fn-count-evg-max-calls ()fn-count-evg-max-calls1601,68532 (defun min-fixnum (x y)min-fixnum1612,68842 (defun fn-count-evg-rec (evg acc calls)fn-count-evg-rec1620,69075 (defmacro fn-count-evg (evg)fn-count-evg1693,72318 (defun var-fn-count-1 (flg x var-count-acc fn-count-acc p-fn-count-accvar-fn-count-11696,72380 (defmacro var-fn-count (term invisible-fns-table)var-fn-count1798,77743 (defmacro var-or-fn-count-< (var-count1 var-count2 fn-count1 fn-count2var-or-fn-count-<1804,77896 (defun term-order1 (term1 term2 invisible-fns-table)term-order11831,79223 (defun arith-term-order (term1 term2)arith-term-order1902,83620 (defrec polypoly1916,83960 (defabbrev first-var (p) (caar (access poly p :alist)))first-var2046,90529 (defabbrev first-coefficient (p) (cdar (access poly p :alist)))first-coefficient2048,90586 (defun good-coefficient (c)good-coefficient2071,91644 (defun good-pot-varp (x)good-pot-varp2074,91694 (defun good-polyp (p)good-polyp2084,92074 (defun logical-< (x y)logical-<2094,92468 (defun logical-<= (x y)logical-<=2107,92880 (defun evaluate-ground-poly (p)evaluate-ground-poly2117,93206 (defun impossible-polyp (p)impossible-polyp2126,93475 (defun true-polyp (p)true-polyp2130,93584 (defun silly-polyp (poly)silly-polyp2134,93678 (defun impossible-poly (ttree)impossible-poly2141,93884 (defun base-poly0 (ttree parents relation rational-poly-p derived-from-not-equalityp)base-poly02151,94121 (defun base-poly (ttree relation rational-poly-p derived-from-not-equalityp)base-poly2164,94486 (defun poly-alist-equal (alist1 alist2)poly-alist-equal2177,94859 (defun poly-equal (poly1 poly2)poly-equal2191,95256 (defun poly-weakerp (poly1 poly2 parents-check)poly-weakerp2202,95635 (defun poly-member (p lst)poly-member2235,97018 (defun new-and-ugly-linear-varsp (lst flag term)new-and-ugly-linear-varsp2251,97675 (defun filter-polys (lst ans)filter-polys2277,98693 (defun add-linear-variable1 (n var alist)add-linear-variable12304,99663 (defun zero-factor-p (term)zero-factor-p2328,100653 (defun get-coefficient (term acc)get-coefficient2349,101487 (defun add-linear-variable (term side p)add-linear-variable2363,102025 (defun dumb-eval-yields-quotep (term)dumb-eval-yields-quotep2385,102699 (defun dumb-eval (term)dumb-eval2407,103504 (defun add-linear-term (term side p)add-linear-term2436,104490 (defun add-linear-terms-fn (rst)add-linear-terms-fn2506,107054 (defmacro add-linear-terms (&rest rst)add-linear-terms2520,107505 (defun normalize-poly1 (coeff alist)normalize-poly12538,108056 (defun normalize-poly (p)normalize-poly2545,108249 (defun normalize-poly-lst (poly-lst)normalize-poly-lst2561,108652 (defrec linear-pot ((loop-stopper-value . negatives) . (var . positives)) t)linear-pot2574,108927 (defun modify-linear-pot (pot pos neg)modify-linear-pot2583,109431 (defconst *max-linear-pot-loop-stopper-value* 3)*max-linear-pot-loop-stopper-value*2657,112361 (defun loop-stopper-value-of-var (var pot-lst)loop-stopper-value-of-var2659,112411 (defun set-loop-stopper-values (new-vars new-pot-lst term value)set-loop-stopper-values2670,112800 (defun var-in-pot-lst-p (var pot-lst)var-in-pot-lst-p2702,114447 (defun bounds-poly-with-var (poly-lst pt bounds-poly)bounds-poly-with-var2712,114712 (defun bounds-polys-with-var (var pot-lst pt)bounds-polys-with-var2738,115940 (defun polys-with-var1 (var pot-lst)polys-with-var12763,116869 (defun polys-with-var (var pot-lst)polys-with-var2770,117172 (defun polys-with-pots (polys pot-lst ans)polys-with-pots2782,117608 (defun new-vars-in-pot-lst (new-pot-lst old-pot-lst include-variableps)new-vars-in-pot-lst2795,118024 (defun changed-pot-vars (new-pot-lst old-pot-lst to-be-ignored-lst)changed-pot-vars2843,120598 (defun infect-polys (lst ttree parents)infect-polys2869,121822 (defun infect-first-n-polys (lst n ttree parents)infect-first-n-polys2884,122406 (defun infect-new-polys (new-pot-lst old-pot-lst ttree)infect-new-polys2899,123017 (defun fcomplementary-multiplep1 (alist1 alist2)fcomplementary-multiplep12970,126312 (defun fcomplementary-multiplep (poly1 poly2)fcomplementary-multiplep2983,126796 (defun already-used-by-find-equational-polyp-lst (poly1 lst)already-used-by-find-equational-polyp-lst2998,127451 (defun already-used-by-find-equational-polyp (poly1 hist)already-used-by-find-equational-polyp3003,127667 (defun cons-term-binary-+-constant (x term)cons-term-binary-+-constant3032,129115 (defun cons-term-unary-- (term)cons-term-unary--3041,129412 (defun cons-term-binary-*-constant (x term)cons-term-binary-*-constant3047,129646 (defun find-equational-poly-rhs1 (alist)find-equational-poly-rhs13058,130009 (defun find-equational-poly-rhs (poly1)find-equational-poly-rhs3072,130511 (defun find-equational-poly3 (poly1 poly2 hist)find-equational-poly33103,131546 (defun find-equational-poly2 (poly1 negatives hist)find-equational-poly23131,132829 (defun find-equational-poly1 (positives negatives hist)find-equational-poly13151,133529 (defun find-equational-poly (pot hist)find-equational-poly3171,134277 (defun get-coeff-for-cancel1 (alist1 alist2)get-coeff-for-cancel13208,135903 (defun cancel2 (alist coeff)cancel23232,136711 (defun cancel1 (alist1 alist2 coeff)cancel13240,136914 (defun cancel (p1 p2)cancel3266,137953 (defun cancel-poly-against-all-polys (p polys pt ans)cancel-poly-against-all-polys3336,141335 (defun add-poly (p pot-lst to-do-next pt nonlinearp)add-poly3427,145440 (defun prune-poly-lst (poly-lst ans)prune-poly-lst3554,150929 (defun add-polys1 (lst pot-lst new-lst pt nonlinearp max-roundsadd-polys13562,151192 (defun add-polys0 (lst pot-lst pt nonlinearp max-rounds)add-polys03607,153282 type-set-b.lisp,22537 (defconst *number-of-numeric-type-set-bits**number-of-numeric-type-set-bits*26,995 (defconst *type-set-binary-+-table-list**type-set-binary-+-table-list*30,1099 (defconst *type-set-binary-+-table**type-set-binary-+-table*39,1468 (defconst *type-set-binary-*-table-list**type-set-binary-*-table-list*43,1589 (defconst *type-set-binary-*-table**type-set-binary-*-table*52,1958 (defconst *type-set-<-table-list**type-set-<-table-list*60,2240 (defconst *type-set-<-table**type-set-<-table*75,2691 (defun assoc-equal-cdr (x alist)assoc-equal-cdr89,3292 (defun runep (x wrld)runep97,3517 (defmacro base-symbol (rune)base-symbol228,10269 (defmacro strip-base-symbols (runes)strip-base-symbols242,10835 (deflabel executable-counterpartexecutable-counterpart245,10898 (deflabel worldworld324,14702 (deflabel runerune433,21437 (deflabel rule-namesrule-names580,28785 (defun fnume (rune wrld)fnume595,29023 (defun frunic-mapping-pair (rune wrld)frunic-mapping-pair611,29800 (defun fn-rune-nume (fn nflg xflg wrld)fn-rune-nume619,30054 (defun definition-runes (fns xflg wrld)definition-runes635,30839 (defun get-next-nume (lst)get-next-nume640,31021 (defun deref-macro-name (macro-name macro-aliases)deref-macro-name676,32855 (defun deref-macro-name-lst (macro-name-lst macro-aliases)deref-macro-name-lst682,33014 (defconst *abbrev-rune-alist**abbrev-rune-alist*687,33260 (defun translate-abbrev-rune (x macro-aliases)translate-abbrev-rune693,33403 (defun rule-name-designatorp (x macro-aliases wrld)rule-name-designatorp703,33758 (defun theoryp1 (lst macro-aliases wrld)theoryp1748,36001 (defun theoryp (lst wrld)theoryp754,36205 (defun theoryp!1 (lst fail-flg macro-aliases wrld)theoryp!1762,36478 (defun theoryp! (lst wrld)theoryp!784,37507 (defun runic-theoryp1 (prev-nume lst wrld)runic-theoryp1789,37627 (defun runic-theoryp (lst wrld)runic-theoryp804,38175 (defun find-mapping-pairs-tail1 (rune mapping-pairs)find-mapping-pairs-tail1880,42450 (defun find-mapping-pairs-tail (rune mapping-pairs wrld)find-mapping-pairs-tail895,43118 (defun augment-runic-theory1 (lst mapping-pairs wrld ans)augment-runic-theory1912,43941 (defun augment-runic-theory (lst wrld)augment-runic-theory934,44999 (defconst *bad-runic-designator-string**bad-runic-designator-string*959,46319 (defun convert-theory-to-unordered-mapping-pairs1 (lst macro-aliases wrld ans)convert-theory-to-unordered-mapping-pairs1965,46679 (defun convert-theory-to-unordered-mapping-pairs (lst wrld)convert-theory-to-unordered-mapping-pairs1025,49030 (defun duplicitous-cons-car (x y)duplicitous-cons-car1043,49847 (defun duplicitous-revappend-car (lst ans)duplicitous-revappend-car1052,50099 (defun duplicitous-merge-car (parity lst1 lst2 ans)duplicitous-merge-car1060,50362 (defun duplicitous-sort-car (parity lst)duplicitous-sort-car1094,52187 (defun augment-theory (lst wrld)augment-theory1115,53192 (defmacro assert$-runic-theoryp (runic-theory-expr wrld)assert$-runic-theoryp1143,54581 (defun runic-theory (lst wrld)runic-theory1157,54899 (defrec enabled-structureenabled-structure1198,56839 (defun enabled-numep (nume ens)enabled-numep1282,61643 (defun enabled-arith-numep (nume ens)enabled-arith-numep1297,62147 (defun enabled-runep (rune ens wrld)enabled-runep1317,62997 (defmacro active-runep (rune)active-runep1325,63279 (defun enabled-xfnp (fn ens wrld)enabled-xfnp1360,64465 (defun sublis-var! (alist term ens wrld ttree)sublis-var!1381,65302 (defun sublis-var!-lst (alist lst ens wrld ttree)sublis-var!-lst1490,69492 (defun theory-warning-fns-aux (runes1 runes2 max-numetheory-warning-fns-aux1507,70081 (defun theory-warning-fns (ens1 ens2 w)theory-warning-fns1535,71298 (defun@par maybe-warn-about-theory (ens1 force-xnume-en1 imm-xnume-en1 ens2maybe-warn-about-theory1562,72904 (defrec theory-invariant-recordtheory-invariant-record1617,75301 (defun@par chk-theory-invariant1 (theory-expr ens invariant-alist errp-acc ctxchk-theory-invariant11621,75370 (defun@par chk-theory-invariant (theory-expr ens ctx state)chk-theory-invariant1709,79790 (defrec clause-idclause-id1735,80701 (defun pos-listp (l)pos-listp1748,81154 (defun all-digits-p (lst radix)all-digits-p1755,81315 (defun d-pos-listp (lst)d-pos-listp1764,81658 (defun clause-id-p (cl-id)clause-id-p1776,82100 (defconst *initial-clause-id**initial-clause-id*1791,82467 (defun chars-for-tilde-@-clause-id-phrase/periods (lst)chars-for-tilde-@-clause-id-phrase/periods1815,83611 (defun chars-for-tilde-@-clause-id-phrase/primes (n)chars-for-tilde-@-clause-id-phrase/primes1825,83958 (defun chars-for-tilde-@-clause-id-phrase (id)chars-for-tilde-@-clause-id-phrase1833,84237 (defun string-for-tilde-@-clause-id-phrase (id)string-for-tilde-@-clause-id-phrase1859,85552 (defun@par load-theory-into-enabled-structureload-theory-into-enabled-structure1867,85777 (defun initial-global-enabled-structure (root-string)initial-global-enabled-structure1955,90372 (defun recompress-global-enabled-structure (varname wrld)recompress-global-enabled-structure1984,91590 (defun recompress-stobj-accessor-arrays (stobj-names wrld)recompress-stobj-accessor-arrays2044,94494 (defconst *fake-rune-for-type-set**fake-rune-for-type-set*2100,97530 (defun puffert (ttree)puffert2105,97667 (defun immediate-forcep (fn ens)immediate-forcep2115,98079 (defmacro numeric-type-set (ts)numeric-type-set2123,98288 (defmacro rational-type-set (ts)rational-type-set2146,99235 (defmacro real-type-set (ts)real-type-set2167,100017 (defun type-set-binary-+ (term ts1 ts2 ttree ttree0)type-set-binary-+2188,100793 (defun type-set-binary-* (ts1 ts2 ttree ttree0)type-set-binary-*2230,102850 (defun type-set-not (ts ttree ttree0)type-set-not2246,103418 (defun type-set-<-1 (r arg2 commutedp type-alist)type-set-<-12254,103618 (defun type-set-< (arg1 arg2 ts1 ts2 type-alist ttree ttree0 pot-lst pt)type-set-<2371,108055 (defun type-set-unary-- (ts ttree ttree0)type-set-unary--2533,115792 (defun type-set-unary-/ (ts ttree ttree0)type-set-unary-/2556,116814 (defun type-set-numerator (ts ttree ttree0)type-set-numerator2575,117712 (defun type-set-realpart (ts ttree ttree0)type-set-realpart2590,118396 (defun type-set-imagpart (ts ttree ttree0)type-set-imagpart2601,118789 (defun type-set-complex (ts1 ts2 ttree ttree0)type-set-complex2622,119568 (defun type-set-floor1 (ts ttree ttree0)type-set-floor12665,121386 (defun type-set-standard-part (ts ttree ttree0)type-set-standard-part2680,122063 (defun type-set-standardp (ts ttree ttree0)type-set-standardp2696,122731 (defrec recognizer-tuplerecognizer-tuple2712,123257 (defconst *initial-recognizer-alist**initial-recognizer-alist*2822,127951 (defun most-recent-enabled-recog-tuple (fn alist ens)most-recent-enabled-recog-tuple2928,131698 (defun type-set-recognizer (recog-tuple arg-ts ttree ttree0)type-set-recognizer2940,132209 (defun type-set-car (ts ttree ttree0)type-set-car2973,133765 (defun type-set-cdr (ts ttree ttree0)type-set-cdr2977,133902 (defun type-set-coerce (term ts1 ts2 ttree1 ttree2 ttree0)type-set-coerce2988,134256 (defun type-set-intern-in-package-of-symbol (ts1 ts2 ttree1 ttree2 ttree0)type-set-intern-in-package-of-symbol3010,135128 (defun type-set-length (ts ttree ttree0)type-set-length3017,135414 (defun type-set-cons (ts2 ttree ttree0)type-set-cons3028,135782 (defconst *singleton-type-sets**singleton-type-sets*3039,136135 (defun type-set-equal (ts1 ts2 ttree ttree0)type-set-equal3042,136204 (defun type-set-quote (evg)type-set-quote3055,136737 (defun type-set-char-code (ts ttree ttree0)type-set-char-code3092,138039 (defun fn-count-1 (flg x fn-count-acc p-fn-count-acc)fn-count-13102,138387 (defmacro fn-count (term)fn-count3140,140215 (defun term-order (term1 term2)term-order3143,140273 (defrec type-prescriptiontype-prescription3298,147920 (defun find-runed-type-prescription (rune lst)find-runed-type-prescription3341,149710 (defconst *expandable-boot-strap-non-rec-fns**expandable-boot-strap-non-rec-fns*3352,150037 (defun mv-atf (not-flg mbt mbf tta fta ttree1 ttree2)mv-atf3410,152883 (defun assume-true-false-error (type-alist x temp-temp)assume-true-false-error3448,154460 (defun non-cons-cdr (term)non-cons-cdr3457,154830 (defconst *one-way-unify1-implicit-fns**one-way-unify1-implicit-fns*3475,155630 (defun one-way-unify1 (pat term alist)one-way-unify13486,155794 (defun one-way-unify1-lst (pl tl alist)one-way-unify1-lst3719,167461 (defun one-way-unify1-equal1 (pat1 pat2 term1 term2 alist)one-way-unify1-equal13736,168121 (defun one-way-unify1-equal (pat1 pat2 term1 term2 alist)one-way-unify1-equal3760,169273 (defun one-way-unify (pat term)one-way-unify3768,169512 (defun canonical-representative (equiv term type-alist)canonical-representative3828,172431 (defun subst-type-alist1-check (old equiv type-alist)subst-type-alist1-check3874,174043 (defun nil-fn ()nil-fn3885,174424 (defconst *nil-fn-ts-entry**nil-fn-ts-entry*3893,174642 (defun subst-type-alist1 (new old equiv ttree type-alist acc)subst-type-alist13898,174772 (defun subst-type-alist (new old equiv ttree type-alist)subst-type-alist3966,177969 (defun infect-type-alist-entry (entry ttree)infect-type-alist-entry3981,178571 (defun infect-new-type-alist-entries2 (new-type-alist old-type-alist ttree)infect-new-type-alist-entries23989,178789 (defun infect-new-type-alist-entries1 (new-type-alist old-type-alist ttree n)infect-new-type-alist-entries14008,179587 (defun infect-new-type-alist-entries (new-type-alist old-type-alist ttree)infect-new-type-alist-entries4021,180131 (defun extend-type-alist-simple (term ts ttree type-alist)extend-type-alist-simple4040,181045 (defun extend-type-alist1 (equiv occursp1 occursp2 both-canonicalp arg1-canonextend-type-alist14059,181825 (defun extend-type-alist (term ts ttree type-alist wrld)extend-type-alist4137,185090 (defun zip-variable-type-alist (vars pairs)zip-variable-type-alist4188,187338 (defun assoc-equiv (fn arg1 arg2 alist)assoc-equiv4202,187949 (defun assoc-equiv+ (equiv arg1 arg2 type-alist)assoc-equiv+4221,188700 (defun assoc-type-alist (term type-alist wrld)assoc-type-alist4287,191679 (defun look-in-type-alist (term type-alist wrld)look-in-type-alist4304,192295 (defun member-char-stringp (chr str i)member-char-stringp4309,192451 (defun terminal-substringp1 (str1 str2 max1 max2)terminal-substringp14320,192836 (defun terminal-substringp (str1 str2 max1 max2)terminal-substringp4336,193472 (defun evg-occur (x y)evg-occur4346,193898 (defun occur (term1 term2)occur4415,196809 (defun occur-lst (term1 args2)occur-lst4427,197210 (defun pseudo-variantp (term1 term2)pseudo-variantp4452,198053 (defun pseudo-variantp-list (args1 args2)pseudo-variantp-list4487,199469 (defun worse-than-builtin (term1 term2)worse-than-builtin4749,207467 (defun worse-than-or-equal-builtin (term1 term2)worse-than-or-equal-builtin4787,209210 (defun basic-worse-than-lst1 (args1 args2)basic-worse-than-lst14819,210512 (defun basic-worse-than-lst2 (args1 args2)basic-worse-than-lst24838,211361 (defun basic-worse-than (term1 term2)basic-worse-than4854,212033 (defun some-subterm-worse-than-or-equal (term1 term2)some-subterm-worse-than-or-equal4904,214302 (defun some-subterm-worse-than-or-equal-lst (args term2)some-subterm-worse-than-or-equal-lst4924,215140 (defun worse-than-lst (args term2)worse-than-lst4935,215678 (defrec ancestorancestor4981,217082 (defmacro make-ancestor-binding-hyp (hyp unify-subst)make-ancestor-binding-hyp4995,217597 (defmacro ancestor-binding-hyp-p (anc)ancestor-binding-hyp-p5002,217770 (defmacro ancestor-binding-hyp/hyp (anc)ancestor-binding-hyp/hyp5006,217867 (defmacro ancestor-binding-hyp/unify-subst (anc)ancestor-binding-hyp/unify-subst5009,217941 (defun push-ancestor (lit tokens ancestors)push-ancestor5014,218081 (defun ancestor-listp (x)ancestor-listp5044,219448 (defun earlier-ancestor-biggerp (var-cnt fn-cnt p-fn-cnt tokens ancestors)earlier-ancestor-biggerp5072,220535 (defun equal-mod-commuting (x y wrld)equal-mod-commuting5102,222021 (defun ancestors-check1 (lit-atm lit var-cnt fn-cnt p-fn-cnt ancestors tokens)ancestors-check15130,223016 (defun ancestors-check-builtin (lit ancestors tokens)ancestors-check-builtin5271,230556 (defproxy ancestors-check (* * *) => (mv * *))ancestors-check5319,232590 (defun map-multiply-car (multiplicative-constant x)map-multiply-car5386,235037 (defun normalize-addend (addend)normalize-addend5392,235284 (defun insert-cdr-term-order (item list)insert-cdr-term-order5416,236243 (defun normalize-linear-sum-2 (term)normalize-linear-sum-25425,236501 (defun normalize-linear-sum-1 (additive-constant term)normalize-linear-sum-15437,236969 (defun normalize-linear-sum (term)normalize-linear-sum5487,239324 (defun normalize-linear-sum-p1 (stripped-term term-to-match)normalize-linear-sum-p15557,242134 (defun normalize-linear-sum-p (stripped-term term-to-match)normalize-linear-sum-p5566,242526 (defun type-set-finish-1 (additive-const multiplicative-const stripped-termtype-set-finish-15594,243758 (defun type-set-finish (x ts0 ttree0 ts1 ttree1 type-alist)type-set-finish5786,254215 (defun search-type-alist-rec (term alt-term typ type-alist unify-subst ttree)search-type-alist-rec5824,255889 (defun free-varsp (term alist)free-varsp5862,257639 (defun free-varsp-lst (args alist)free-varsp-lst5867,257805 (defun search-type-alist-with-rest (term typ type-alist unify-subst ttree wrld)search-type-alist-with-rest5874,257968 (defun search-type-alist (term typ type-alist unify-subst ttree wrld)search-type-alist5907,259517 (defun term-and-typ-to-lookup (hyp wrld)term-and-typ-to-lookup5956,261923 (defun lookup-hyp (hyp type-alist wrld unify-subst ttree)lookup-hyp5977,262832 (defun bind-free-vars-to-unbound-free-vars (vars alist)bind-free-vars-to-unbound-free-vars5987,263222 (defabbrev x-xrunep (xrune) ; extended xrunepx-xrunep6070,267627 (defabbrev hyp-xrune (n rune)hyp-xrune6074,267733 (defabbrev hyp-xrune-rune (xrune)hyp-xrune-rune6078,267830 (defabbrev conc-xrune (rune)conc-xrune6083,267963 (defabbrev conc-xrune-rune (xrune)conc-xrune-rune6087,268058 (defabbrev xrune-rune (xrune)xrune-rune6092,268192 (defabbrev rune= (rune1 rune2)rune=6099,268355 (defabbrev xrune= (xrune1 xrune2)xrune=6103,268464 (defun prettyify-xrune (xrune)prettyify-xrune6113,268825 (defrec accp-infoaccp-info6123,269164 (defrec accp-entryaccp-entry6156,270359 (defun merge-accumulated-persistence-aux (xrune entry alist)merge-accumulated-persistence-aux6226,273765 (defun merge-accumulated-persistence-rec (new-alist old-alist)merge-accumulated-persistence-rec6249,274867 (defun merge-accumulated-persistence (new-alist old-alist)merge-accumulated-persistence6258,275202 (defun add-accumulated-persistence-s (xrune delta-s delta-f alistadd-accumulated-persistence-s6368,279607 (defun add-accumulated-persistence-f (xrune delta-s delta-f alistadd-accumulated-persistence-f6395,280748 (defun accumulated-persistence-make-failures (alist)accumulated-persistence-make-failures6425,282011 (defun add-accumulated-persistence (xrune success-p delta-s delta-fadd-accumulated-persistence6437,282560 (defmacro accumulated-persistence (flg)accumulated-persistence6461,283659 (defmacro set-accumulated-persistence (flg)set-accumulated-persistence6862,304577 (defdoc accumulated-persistence-subtletiesaccumulated-persistence-subtleties6865,304657 (defun merge-car-> (l1 l2)merge-car->6995,309798 (defun merge-sort-car-> (l)merge-sort-car->7002,310023 (defconst *accp-major-separator**accp-major-separator*7007,310188 (defconst *accp-minor-separator**accp-minor-separator*7010,310265 (defun show-accumulated-persistence-phrase0 (entry key)show-accumulated-persistence-phrase07013,310342 (defun show-accumulated-persistence-phrase1 (key alist mergep acc)show-accumulated-persistence-phrase17041,311348 (defun show-accumulated-persistence-remove-useless (alist acc)show-accumulated-persistence-remove-useless7073,312710 (defun show-accumulated-persistence-phrase-key (key entry lastcdr)show-accumulated-persistence-phrase-key7083,313114 (defun show-accumulated-persistence-phrase2-merge (key alist last-key-info)show-accumulated-persistence-phrase2-merge7100,313827 (defun show-accumulated-persistence-phrase2-not-merge (key alist)show-accumulated-persistence-phrase2-not-merge7130,315051 (defun show-accumulated-persistence-phrase2 (key alist mergep)show-accumulated-persistence-phrase27139,315498 (defun split-xrune-alist (alist rune-alist hyp-xrune-alist conc-xrune-alist)split-xrune-alist7143,315711 (defun sort-xrune-alist-by-rune1 (rune-alist hyp-xrune-alist conc-xrune-alistsort-xrune-alist-by-rune17164,316730 (defun sort-xrune-alist-by-rune (alist display)sort-xrune-alist-by-rune7197,318336 (defun pop-accp-fn (info success-p)pop-accp-fn7215,319172 (defun pop-accp-fn-iterate (info n)pop-accp-fn-iterate7272,321700 (defun show-accumulated-persistence-phrase (key/display accp-info)show-accumulated-persistence-phrase7288,322374 (defmacro show-accumulated-persistence (&optional (sortkey ':frames)show-accumulated-persistence7360,325988 (defun push-accp (rune x-info)push-accp7410,328392 (defun pop-accp (success-p x-info)pop-accp7456,331054 (defmacro with-accumulated-persistence (rune vars success-p bodywith-accumulated-persistence7473,331815 (defun assume-true-false-<assume-true-false-<7523,334017 (defun mv-atf-2 (not-flg true-type-alist false-type-alistmv-atf-27729,342635 (defun binding-hyp-p (hyp alist wrld)binding-hyp-p7782,344760 (defmacro adjust-ignore-for-atf (not-flg ignore)adjust-ignore-for-atf7830,347138 (defun backchain-limit-reachedp1 (n ancestors)backchain-limit-reachedp17842,347537 (defun backchain-limit-reachedp (n ancestors)backchain-limit-reachedp7847,347704 (defun new-backchain-limit (new-offset old-limit ancestors)new-backchain-limit7858,348223 (defproxy oncep-tp (* *) => *)oncep-tp7885,349162 (defun oncep-tp-builtin (rune wrld)oncep-tp-builtin7887,349194 (defun type-set-rec (x force-flg dwp type-alist ancestors ens w ttreetype-set-rec7897,349387 (defun type-set-lst (x force-flg dwp type-alist ancestors ens wtype-set-lst8429,373922 (defun type-set-relieve-hyps-free (term typ rest-type-alisttype-set-relieve-hyps-free8450,374947 (defun type-set-relieve-hyps1 (hyp forcep rune target hyps backchain-limit-lsttype-set-relieve-hyps18493,377002 (defun type-set-relieve-hyps (rune target hyps backchain-limit-lsttype-set-relieve-hyps8649,382804 (defun extend-type-alist-with-bindings (alist force-flg dwp type-alistextend-type-alist-with-bindings8920,395576 (defun type-set-with-rule (tp term force-flg dwp type-alist ancestors ens wtype-set-with-rule8947,396757 (defun type-set-with-rule1 (alist vars force-flg dwp type-alist ancestors ens wtype-set-with-rule19036,400812 (defun type-set-with-rules (tp-lst term force-flg dwp type-alist ancestors enstype-set-with-rules9073,402863 (defun type-set-primitive (term force-flg dwp type-alist ancestors ens w ttree0type-set-primitive9126,405256 (defun assume-true-false-if (not-flg x xttree force-flg dwpassume-true-false-if9663,427822 (defun assume-true-false-rec (x xttree force-flg dwp type-alist ancestors ens wassume-true-false-rec9963,443098 (defun assume-true-false1 (not-flg x xttree force-flg dwp type-alist ancestorsassume-true-false111066,498228 (defun proper/improper-cons-ts-tuple (term ts ttree force-flg dwp type-alistproper/improper-cons-ts-tuple11124,500807 (defun extend-with-proper/improper-cons-ts-tupleextend-with-proper/improper-cons-ts-tuple11198,504314 (defun type-set (x force-flg dwp type-alist ens w ttree pot-lst pt)type-set11217,505133 (defun assume-true-false (x xttree force-flg dwp type-alist ens w pot-lst ptassume-true-false11372,512808 (defun ok-to-force-ens (ens)ok-to-force-ens11379,513124 (defun add-linear-assumption (target term type-alist ensadd-linear-assumption11388,513481 (defun return-type-alistreturn-type-alist11496,518354 (defun type-alist-equality-loop1 (type-alist top-level-type-alist ens w)type-alist-equality-loop111526,519511 (defun clean-up-alist (alist ans)clean-up-alist11617,523567 (defun duplicate-keysp (alist)duplicate-keysp11630,524132 (defun clean-type-alist (type-alist)clean-type-alist11643,524629 (defun type-alist-equality-loop-exit (type-alist)type-alist-equality-loop-exit11660,525391 (defconst *type-alist-equality-loop-max-depth* 10)*type-alist-equality-loop-max-depth*11666,525613 (defun type-alist-equality-loop (type-alist0 ens w n)type-alist-equality-loop11668,525665 (defun put-assoc-equal-ts (term ts ttree type-alist)put-assoc-equal-ts11699,527049 (defun reconsider-type-alist (type-alist xtype-alist force-flg ens w pot-lstreconsider-type-alist11711,527589 (defun type-alist-clause-finish1 (lits ttree-lst force-flg type-alist ens wrld)type-alist-clause-finish111773,530512 (defun type-alist-clause-finish (lits ttree-lst force-flg type-alist ens wrldtype-alist-clause-finish11809,532253 (defun type-alist-clause (cl ttree-lst force-flg type-alist ens wrldtype-alist-clause11941,539350 (defun known-whether-nil (x type-alist ens force-flg dwp wrld ttree)known-whether-nil12003,542946 (defun ts-booleanp (term ens wrld)ts-booleanp12035,544632 (defun weak-cons-occur (x y)weak-cons-occur12046,545119 (defun equal-x-cons-x-yp (lhs rhs)equal-x-cons-x-yp12060,545577 (defun not-ident (term1 term2 type-alist ens wrld)not-ident12135,548929 (defun first-if (args i)first-if12170,550568 (defun all-variablep (lst)all-variablep12184,551056 (defun normalize-with-type-set (term iff-flg type-alist ens wrld ttree)normalize-with-type-set12189,551191 (defun normalize (term iff-flg type-alist ens wrld ttree)normalize12221,552379 (defun normalize-lst (args iff-flg type-alist ens wrld ttree)normalize-lst12370,559473 (defun normalize-or-distribute-first-if (term iff-flg type-alist ens wrldnormalize-or-distribute-first-if12379,559937 (defun distribute-first-if (term iff-flg type-alist ens wrld ttree)distribute-first-if12397,560743 (defun decode-type-set1 (ts alist)decode-type-set112517,566224 (defun decode-type-set (ts)decode-type-set12527,566622 (defmacro dts (term type-alist)dts12545,567342 (defun ens (state)ens12562,567853 (defmacro git (sym prop)git12565,567923 linear-b.lisp,1003 (defun polys-from-type-set (term force-flag dwp type-alist ens wrld ttree)polys-from-type-set36,1154 (defun add-type-set-polys (var-lst new-pot-lst old-pot-lstadd-type-set-polys133,5323 (defun add-polynomial-inequalities (lst pot-lst pt nonlinearp type-alist ensadd-polynomial-inequalities176,7396 (defparameter *add-polys-counter**add-polys-counter*203,8547 (defun add-polys (lst pot-lst pt nonlinearp type-alist ens force-flg wrld)add-polys210,8702 (defun eval-ground-subexpressions (term ens wrld state ttree)eval-ground-subexpressions231,9291 (defun eval-ground-subexpressions-lst (lst ens wrld state ttree)eval-ground-subexpressions-lst326,12671 (defun poly-set (op poly1 poly2)poly-set341,13175 (defun linearize1 (term positivep type-alist ens force-flg wrld ttree state)linearize1423,17357 (defun linearize (term positivep type-alist ens force-flg wrld ttree state)linearize802,33926 (defun linearize-lst1linearize-lst1847,36093 (defun linearize-lstlinearize-lst877,37416 non-linear.lisp,2036 (defun cleanse-type-alist (type-alist pt)cleanse-type-alist29,1083 (defun var-with-divisionp (var)var-with-divisionp56,1948 (defun varify (x)varify87,3196 (defun varify! (x)varify!110,3790 (defun varify!-lst1 (lst acc)varify!-lst1119,4021 (defun varify!-lst (lst)varify!-lst124,4141 (defun invert-var (var)invert-var131,4311 (defun part-of1 (var1 var2)part-of1172,6125 (defun part-of (var1 var2)part-of203,7114 (defun product-already-triedp (var-list products-already-tried)product-already-triedp220,7633 (defun too-many-polysp (var-lst pot-lst counter)too-many-polysp234,8146 (defun expanded-new-vars-in-pot-lst2 (new-var vars-to-skip vars-to-return)expanded-new-vars-in-pot-lst2252,8721 (defun expanded-new-vars-in-pot-lst1 (new-pot-lst vars-to-skipexpanded-new-vars-in-pot-lst1285,10134 (defun expanded-new-vars-in-pot-lst (new-pot-lst old-pot-lst)expanded-new-vars-in-pot-lst331,12494 (defun extract-bounds (bounds-polys)extract-bounds355,13495 (defun good-bounds-in-pot (var pot-lst pt)good-bounds-in-pot397,15667 (defun inverse-polys (var inv-var pot-lst ttree pt)inverse-polys426,16721 (defun add-inverse-polys (varadd-inverse-polys762,33525 (defun add-polys-from-type-set (var pot-lst type-alistadd-polys-from-type-set805,35285 (defun length-of-shortest-polys-with-var (poly-lst pt n)length-of-shortest-polys-with-var841,36731 (defun shortest-polys-with-var1 (poly-lst pt n)shortest-polys-with-var1861,37649 (defun shortest-polys-with-var (var pot-lst pt)shortest-polys-with-var873,38158 (defun binary-*-leaves (term)binary-*-leaves905,39466 (defun binary-*-tree (leaves)binary-*-tree911,39624 (defun merge-arith-term-order (l1 l2)merge-arith-term-order929,40137 (defun insert-arith-term-order (item list)insert-arith-term-order936,40398 (defun sort-arith-term-order (list)sort-arith-term-order945,40654 (defun multiply-alist-and-const (alist const poly)multiply-alist-and-const952,40855 (defun collect-rational-poly-p-lst (poly-lst)collect-rational-poly-p-lst967,41543 tau.lisp,22407 (defun almost-lexorder-singletons (x y)almost-lexorder-singletons49,2456 (defun member-nil-neg-evgs (neg-evg-lst)member-nil-neg-evgs57,2733 (defun member-neg-evgs1 (evg neg-evg-lst)member-neg-evgs161,2844 (defun member-neg-evgs (x neg-evg-lst)member-neg-evgs71,3173 (defun insert-neg-evgs1 (evg x neg-evg-lst)insert-neg-evgs184,3633 (defun insert-neg-evgs (x neg-evg-lst)insert-neg-evgs96,4077 (defun merge-car-< (l1 l2)merge-car-<106,4456 (defun merge-sort-car-< (l)merge-sort-car-<113,4681 (defun merge-cadr-< (l1 l2)merge-cadr-<121,4921 (defun merge-sort-cadr-< (l)merge-sort-cadr-<128,5151 (defun strip-caddrs (x)strip-caddrs136,5398 (defun unprettyify/add-hyps-to-pairs (hyps lst)unprettyify/add-hyps-to-pairs148,5972 (defun flatten-ands-in-lit (term)flatten-ands-in-lit157,6311 (defun unprettyify (term)unprettyify170,6843 (defun reprettyify (hyps concl wrld)reprettyify219,9041 (defun remove-guard-holders1 (changedp0 term)remove-guard-holders1274,11766 (defun remove-guard-holders1-lst (lst)remove-guard-holders1-lst363,15730 (defun remove-guard-holders (term)remove-guard-holders374,16170 (defun remove-guard-holders-lst (lst)remove-guard-holders-lst384,16442 (defun remove-guard-holders1-lst-lst (lst)remove-guard-holders1-lst-lst393,16687 (defun remove-guard-holders-lst-lst (lst)remove-guard-holders-lst-lst403,17133 (defun convert-returned-vars-to-term-lst (term vars)convert-returned-vars-to-term-lst419,17665 (defun implicate (t1 t2)implicate424,17872 (defrec type-set-inverter-rule ((nume . ts) terms . rune) nil)type-set-inverter-rule436,18198 (defconst *initial-type-set-inverter-rules**initial-type-set-inverter-rules*464,19621 (defun convert-type-set-to-term-lst (ts rules ens lst ttree)convert-type-set-to-term-lst687,30092 (defun subst-var (new old form)subst-var726,31680 (defun subst-var-lst (new old l)subst-var-lst737,32079 (defun convert-type-set-to-term (x ts ens w ttree)convert-type-set-to-term747,32387 (defun convert-type-prescription-to-term (tp ens wrld)convert-type-prescription-to-term789,34338 (defun all-runes-in-lmi (lmi wrld ans)all-runes-in-lmi813,35524 (defun all-runes-in-lmi-lst (lmi-lst wrld ans)all-runes-in-lmi-lst828,36119 (defun all-runes-in-var-to-runes-alist (alist ans)all-runes-in-var-to-runes-alist833,36327 (defun all-runes-in-var-to-runes-alist-lst (lst ans)all-runes-in-var-to-runes-alist-lst839,36526 (defun all-runes-in-elim-sequence-lst (lst ans)all-runes-in-elim-sequence-lst847,36758 (defun all-runes-in-elim-sequence (elim-sequence ans)all-runes-in-elim-sequence853,36956 (defun all-runes-in-ttree-fc-derivation-lst (lst ans)all-runes-in-ttree-fc-derivation-lst868,37637 (defun all-runes-in-ttree-find-equational-poly-lst (lst ans)all-runes-in-ttree-find-equational-poly-lst877,37986 (defun all-runes-in-ttree (ttree ans)all-runes-in-ttree887,38403 (defun all-runes-in-ttree-lst (lst ans)all-runes-in-ttree-lst1014,42576 (defdoc introduction-to-the-tau-systemintroduction-to-the-tau-system1021,42764 (defdoc dealing-with-tau-problemsdealing-with-tau-problems1248,54244 (defdoc future-work-related-to-the-tau-systemfuture-work-related-to-the-tau-system1388,61372 (defrec tau-interval (domain (lo-rel . lo) . (hi-rel . hi)) t)tau-interval1857,85106 (defconst *tau-empty-interval**tau-empty-interval*2028,93268 (defun tau-empty-intervalp (int)tau-empty-intervalp2041,93685 (defun = (a-rel a b-rel b)upper-bound->=4072,181224 (defun lower-bound-> (a-rel a b-rel b)lower-bound->4084,181459 (defun upper-bound-< (a-rel a b-rel b)upper-bound-<4096,181693 (defun tau-subintervalp (interval1 interval2)tau-subintervalp4261,187802 (defun tau-implies (tau1 tau2 ens wrld)tau-implies4303,189973 (defun empty-tau-intervalp (lo-rel lo hi-rel hi)empty-tau-intervalp4349,192066 (defun singleton-tau-intervalp (lo-rel lo hi-rel hi)singleton-tau-intervalp4358,192289 (defun make-identity-interval (interval evg)make-identity-interval4375,192885 (defun identity-intervalp (int)identity-intervalp4406,194174 (defun delete-consecutive-integers-upward (k neg-evgs)delete-consecutive-integers-upward4487,198011 (defun delete-consecutive-integers-downward (k neg-evgs)delete-consecutive-integers-downward4519,199706 (defun collect- *)too-many-ifs-pre-rewrite4471,198775 (defun too-many-ifs-pre-rewrite-builtin (args counts)too-many-ifs-pre-rewrite-builtin4473,198823 (defun occur-cnt-bounded (term1 term2 a m bound-m)occur-cnt-bounded4519,200323 (defun occur-cnt-bounded-lst (term1 lst a m bound-m)occur-cnt-bounded-lst4547,201432 (defun too-many-ifs1 (args val lhs rhs ctx)too-many-ifs14566,202210 (defproxy too-many-ifs-post-rewrite (* *) => *)too-many-ifs-post-rewrite4592,203191 (defun too-many-ifs-post-rewrite-builtin (args val)too-many-ifs-post-rewrite-builtin4594,203240 (defun all-args-occur-in-top-clausep (args top-clause)all-args-occur-in-top-clausep4611,203900 (defun cons-count-bounded-ac (x i)cons-count-bounded-ac4616,204109 (defun cons-count-bounded (x)cons-count-bounded4633,204793 (defun max-form-count (term)max-form-count4639,204901 (defun max-form-count-lst (lst acc)max-form-count-lst4715,208411 (defun controller-complexity1 (flg args controller-pocket)controller-complexity14726,208737 (defun controller-complexity (flg term controller-alist)controller-complexity4749,209681 (defun controller-pocket-simplerp (call result controller-alist)controller-pocket-simplerp4762,210257 (defun constant-controller-pocketp1 (args controller-pocket)constant-controller-pocketp14780,210981 (defun constant-controller-pocketp (term controller-alist)constant-controller-pocketp4789,211381 (defun some-controller-pocket-constant-and-non-controller-simplerpsome-controller-pocket-constant-and-non-controller-simplerp4800,211840 (defun rewrite-fncallp (call result cliquep top-clause current-clauserewrite-fncallp4816,212551 (defun rewrite-fncallp-listp (call lst cliquep top-clause current-clauserewrite-fncallp-listp4882,215529 (defun contains-rewriteable-callpcontains-rewriteable-callp4900,216199 (defun contains-rewriteable-callp-lstcontains-rewriteable-callp-lst4930,217518 (defrec linear-lemma ((nume . hyps) max-term concllinear-lemma4943,217969 (defrec current-literal (not-flg . atm) t)current-literal4955,218265 (defrec rewrite-constantrewrite-constant4957,218309 (defconst *default-rw-cache-state**default-rw-cache-state*5084,224277 (defconst *empty-rewrite-constant**empty-rewrite-constant*5087,224322 (defrec metafunction-contextmetafunction-context5110,225020 (defun ok-to-force (rcnst)ok-to-force5125,225652 (defun plist-to-alist (lst)plist-to-alist5214,229703 (defmacro adjust-rdepth (rdepth)adjust-rdepth5223,229985 (defun add-rewrite-args (extra-formals keyword-extra-formals alist)add-rewrite-args5232,230204 (defrec step-limit-recordstep-limit-record5252,231026 (defun step-limit-start (state)step-limit-start5267,231649 (defun step-limit-strictp (state)step-limit-strictp5276,231943 (defun initial-step-limit (wrld state)initial-step-limit5288,232362 (defun step-limit-error1 (ctx str start where state)step-limit-error15317,233795 (defmacro step-limit-error (superior-context-p)step-limit-error5329,234170 (defmacro decrement-step-limit (step-limit)decrement-step-limit5351,234913 (defmacro rewrite-entry (&rest args)rewrite-entry5373,235632 (deflabel free-variablesfree-variables5452,239288 (deflabel free-variables-type-prescriptionfree-variables-type-prescription5658,249881 (deflabel free-variables-examplesfree-variables-examples5753,253104 (deflabel free-variables-examples-rewritefree-variables-examples-rewrite5767,253673 (deflabel free-variables-examples-forward-chainingfree-variables-examples-forward-chaining6320,272877 (defconst *fake-rune-for-linear**fake-rune-for-linear*6519,279921 (defrec gframe (sys-fn bkptr . args) t)gframe6526,280108 (defparameter *deep-gstack* nil)*deep-gstack*6566,281840 (defmacro push-gframe (sys-fn bkptr &rest args)push-gframe6568,281874 (defparameter *saved-deep-gstack* nil)*saved-deep-gstack*6607,283469 (defmacro initial-gstack (sys-fn bkptr &rest args)initial-gstack6609,283509 (defun tilde-@-bkptr-phrase (calling-sys-fn called-sys-fn bkptr)tilde-@-bkptr-phrase6624,284076 (defun cw-gframe (i calling-sys-fn frame evisc-tuple)cw-gframe6672,286557 (defun cw-gstack1 (i calling-sys-fn lst evisc-tuple)cw-gstack16744,289545 (defun cw-gstack-fn (evisc-tuple frames)cw-gstack-fn6751,289855 (defmacro cw-gstack (&key (evisc-tuple 'nil evisc-tuplep) (frames 'nil))cw-gstack6813,292711 (defun restore-brr-globals1 (name new-alist old-alist)restore-brr-globals16975,301974 (defun restore-brr-globals (state)restore-brr-globals6984,302292 (defun save-brr-globals (state)save-brr-globals7012,303400 (defun get-brr-global (var state)get-brr-global7036,304348 (defun exit-brr-wormhole (state)exit-brr-wormhole7073,306213 (defmacro brr-wormhole (entry-lambda input-alist test-form aliases)brr-wormhole7082,306538 (defun initialize-brr-stack (state)initialize-brr-stack7134,308879 (defun lookup-brr-stack (var-name stack)lookup-brr-stack7153,309617 (defun clean-brr-stack1 (gstack stack)clean-brr-stack17165,310139 (defun clean-brr-stack (gstack stack)clean-brr-stack7171,310311 (defun get-brr-local (var state)get-brr-local7219,312899 (defun put-brr-local1 (gstack var val stack)put-brr-local17232,313524 (defun put-brr-local (var val state)put-brr-local7243,314006 (defun put-brr-local-lst (alist state)put-brr-local-lst7258,314682 (defun some-cdr-equalp (little big)some-cdr-equalp7263,314880 (defun push-brr-stack-frame (state)push-brr-stack-frame7271,315100 (defun pop-brr-stack-frame (state)pop-brr-stack-frame7302,316515 (defun decode-type-alist (type-alist)decode-type-alist7313,316966 (defun translate-break-condition (xterm ctx state)translate-break-condition7324,317400 (defun eval-break-condition (rune term ctx state)eval-break-condition7340,318018 (defconst *default-free-vars-display-limit* 30)*default-free-vars-display-limit*7357,318644 (defmacro set-free-vars-display-limit (n)set-free-vars-display-limit7359,318693 (defun free-vars-display-limit (state)free-vars-display-limit7369,319125 (defun limit-failure-reason (failures-remaining failure-reason elided-p)limit-failure-reason7379,319445 (defun limit-failure-reason-alist (failures-remaining alist elided-p)limit-failure-reason-alist7401,320479 (defun fix-free-failure-reason (failure-reason)fix-free-failure-reason7426,321705 (defun fix-free-failure-reason-alist (x acc)fix-free-failure-reason-alist7439,322057 (defun tilde-@-failure-reason-free-phrase (hyp-number alist level unify-substtilde-@-failure-reason-free-phrase7454,322459 (defun tilde-@-failure-reason-phrase1 (failure-reason level unify-substtilde-@-failure-reason-phrase17491,324202 (defun tilde-@-failure-reason-phrase (failure-reason level unify-substtilde-@-failure-reason-phrase7616,330682 (defun stuff-standard-oi (cmds state)stuff-standard-oi7630,331420 (deflabel break-rewritebreak-rewrite7655,332673 (deflabel break-lemmabreak-lemma7906,346196 (deflabel brr-commandsbrr-commands7987,349849 (defdoc dmrdmr8033,351947 (defun raw-mode-p (state)raw-mode-p8200,360620 (defun defun-mode-prompt-string (state)defun-mode-prompt-string8203,360688 (defun brr-prompt (channel state)brr-prompt8224,361202 (defun ts< (x y)ts<8238,361685 (defun add-to-type-alist-segments (ts term segs)add-to-type-alist-segments8264,362137 (defun merge-term-order (l1 l2)merge-term-order8277,362475 (defun merge-sort-term-order (l)merge-sort-term-order8284,362712 (defun sort-type-alist-segments (segs)sort-type-alist-segments8289,362902 (defun type-alist-segments (type-alist acc)type-alist-segments8301,363315 (defun print-terms (terms iff-flg wrld)print-terms8310,363639 (defun print-type-alist-segments (segs wrld)print-type-alist-segments8325,364097 (defun print-type-alist (type-alist wrld)print-type-alist8343,364837 (defun tilde-*-ancestors-stack-msg1 (i ancestors wrld evisc-tuple)tilde-*-ancestors-stack-msg18348,364994 (defun tilde-*-ancestors-stack-msg (ancestors wrld evisc-tuple)tilde-*-ancestors-stack-msg8372,366203 (defun brkpt1 (lemma target unify-subst type-alist ancestors initial-ttreebrkpt18376,366368 (defun brkpt2 (wonp failure-reason unify-subst gstack rewritten-rhs final-ttreebrkpt28612,375512 (defrec expand-hintexpand-hint8880,386336 (defun binds-to-constants-p (unify-subst)binds-to-constants-p8897,386710 (defun expand-permission-result1 (term expand-lst geneqv wrld)expand-permission-result18904,386983 (defun remove1-by-position (target-index lst acc)remove1-by-position8979,390874 (defun expand-permission-result (term rcnst geneqv wrld)expand-permission-result8989,391270 (defun expand-permission-p (term rcnst geneqv wrld)expand-permission-p9066,394349 (defun one-way-unify-restrictions1 (pat term restrictions)one-way-unify-restrictions19078,394886 (defun one-way-unify-restrictions (pat term restrictions)one-way-unify-restrictions9088,395249 (defun ev-fncall! (fn args state latches aok)ev-fncall!9094,395431 (defun ev-fncall-meta (fn args state)ev-fncall-meta9138,397340 (defun get-evg (q ctx)get-evg9172,398973 (defun ev-synp (synp-term unify-subst mfc state)ev-synp9184,399280 (defun bad-synp-alist1 (alist unify-subst vars-to-be-bound wrld)bad-synp-alist19216,400761 (defun bad-synp-alist1-lst (alist-lst unify-subst vars-to-be-bound wrld)bad-synp-alist1-lst9246,402048 (defun bind-free-info (x unify-subst vars-to-be-bound wrld)bind-free-info9253,402351 (defun evgs-or-t (lst alist)evgs-or-t9281,403507 (deflabel nu-rewriternu-rewriter9643,421459 (defparameter *nth-update-tracingp* nil)*nth-update-tracingp*9754,426259 (defparameter *lambda-abstractp* t)*lambda-abstractp*9757,426338 (defparameter *nu-memos* (make-array$ '(65535)))*nu-memos*9760,426412 (defparameter *nu-memos-ens* nil)*nu-memos-ens*9763,426499 (defparameter *nu-memos-wrld* nil)*nu-memos-wrld*9766,426571 (defparameter *ring-buffer-size* (the (integer 0 1000) 5))*ring-buffer-size*9769,426644 (defun-one-output initialize-nu-memos (i)initialize-nu-memos9783,427283 (defun-one-output clear-nu-memos1 (i)clear-nu-memos19794,427646 (defun-one-output clear-nu-memos (reallyp ens wrld)clear-nu-memos9813,428428 (defmacro this-item (x) `(car ,x))this-item9832,429240 (defmacro next-ptr (x) `(cadr ,x))next-ptr9833,429275 (defmacro prev-ptr (x) `(cddr ,x))prev-ptr9834,429310 (defun-one-output link-em (x y)link-em9845,429616 (defun-one-output destructive-get-memo1 (d1 d2 d3 ptr0 ptr1 ptr2)destructive-get-memo19850,429735 (defun-one-output destructive-get-memo (d1 d2 d3 rbuff)destructive-get-memo9898,431189 (defun get-memo (recursively term stack memo-alist)get-memo9912,431737 (defun-one-output destructive-retire-memo (item rbuff)destructive-retire-memo9967,433667 (defun-one-output destructive-insert-memo (item rbuff)destructive-insert-memo9979,434104 (defun-one-output nu-memo-stats1 (i buckets items full-buckets)nu-memo-stats19998,434735 (defun-one-output nu-memo-stats ()nu-memo-stats10015,435413 (defun memo-key1 (term)memo-key110029,435812 (defun bounded-length (lst ans max)bounded-length10060,437063 (defun memo-key (term stack)memo-key10071,437288 (defun mk (term stack)mk10095,438153 (defun show-rbuff1 (r s)show-rbuff110103,438354 (defun show-rbuff (r)show-rbuff10106,438451 (defun memo-exit (recursivelyp term stack flg term2 stack2 ttree2 memo-alist)memo-exit10121,439221 (defun nfix-quote (x)nfix-quote10167,440925 (defun bound-in-framep (var vars terms)bound-in-framep10188,441565 (defun non-rec-defun1 (lemmas ens ttree)non-rec-defun110197,441848 (defun non-rec-defun (fn ens wrld ttree)non-rec-defun10243,444149 (defun deref-var (var stack ens wrld ttree)deref-var10304,446777 (defun deref (term stack ens wrld ttree)deref10317,447244 (defun equal-derefs (term1 stack1 term2 stack2)equal-derefs10386,449497 (defun filter-args (formals vars actuals)filter-args10401,449936 (defun foccurrences (term1 term2 ans)foccurrences10409,450206 (defun foccurrences-lst (term lst ans)foccurrences-lst10438,451180 (defun every-var-at-most-oncep (vars body)every-var-at-most-oncep10445,451411 (defun every-actual-simple (args)every-actual-simple10455,451718 (defun make-clean-lambda-application (formals body args)make-clean-lambda-application10462,451909 (defun lambda-stack (stack term)lambda-stack10515,454267 (defun shorten-each-stack-to-len (n terms stacks)shorten-each-stack-to-len10542,455406 (defun all-equal-stacks (the-stack terms stacks)all-equal-stacks10553,455839 (defun non-quoted-stack (terms stacks)non-quoted-stack10562,456223 (defun cdr-all (x)cdr-all10568,456402 (defun len-common-tail (n terms stacks)len-common-tail10572,456498 (defun butlast-all-stacks (terms stacks n)butlast-all-stacks10577,456667 (defun min-stack-len (terms stacks)min-stack-len10584,456920 (defun reconcile-stacks (terms stacks)reconcile-stacks10594,457250 (defun reconcile-terms (terms exts)reconcile-terms10643,459192 (defun all-equal (x lst)all-equal10649,459366 (defun recon (terms stacks)recon10655,459524 (defun with-reconciliation-let-bindings (terms var)with-reconciliation-let-bindings10678,460513 (defmacro with-reconciliation (terms stacks common-stack body)with-reconciliation10684,460772 (defun lambda-stack-one-way-unify1 (pat term stack alist ens wrld ttree)lambda-stack-one-way-unify110717,461750 (defun lambda-stack-one-way-unify1-lst (pat-lst term-lst stack alistlambda-stack-one-way-unify1-lst10766,463599 (defun lambda-stack-one-way-unify (pat term stack ens wrld ttree)lambda-stack-one-way-unify10786,464423 (defun apply-abbrevs-to-lambda-stack1 (term stack ens wrld lemmas ttree)apply-abbrevs-to-lambda-stack110815,465423 (defun apply-abbrevs-to-lambda-stack (hitp term stack ens wrld state ttree)apply-abbrevs-to-lambda-stack10858,467506 (defconst *fake-rune-for-nu-rewriter**fake-rune-for-nu-rewriter*10955,471186 (defun nth-update-rewriter1-continuenth-update-rewriter1-continue10962,471333 (defun nth-update-rewriter1nth-update-rewriter111020,473416 (defun nth-update-rewriter1-lst (recursivelyp args stacknth-update-rewriter1-lst11567,496597 (defun nth-update-rewriter-targetp (term wrld)nth-update-rewriter-targetp11590,497603 (defun nth-update-rewriter-target-lstp (lst wrld)nth-update-rewriter-target-lstp11604,498214 (defun make-stack-from-alist (term alist)make-stack-from-alist11613,498459 (defun nth-update-rewriter (recursivep term alist ens wrld state)nth-update-rewriter11636,499355 (defun collect-by-position (sub-domain full-domain full-range)collect-by-position11752,504164 (defun make-lambda-application (formals body actuals)make-lambda-application11770,504867 (defun lambda-nest-hidep (term)lambda-nest-hidep11805,506083 (defun lambda-nest-unhide (term)lambda-nest-unhide11821,506538 (defun search-type-alist+ (term typ type-alist unify-subst ttree wrld)search-type-alist+11832,506830 (defun oncep (nume-runes match-free rune nume)oncep11852,507665 (defabbrev memo-activep (memo)memo-activep11866,508287 (defabbrev activate-memo (memo)activate-memo11869,508357 (defmacro zero-depthp (depth)zero-depthp11872,508422 (defmacro rdepth-error (form &optional preprocess-p)rdepth-error11889,509091 (defun bad-synp-hyp-msg1 (hyp bound-vars all-vars-bound-p wrld)bad-synp-hyp-msg111921,510795 (defun bad-synp-hyp-msg (hyps bound-vars all-vars-bound-p wrld)bad-synp-hyp-msg12075,519570 (defmacro sl-let (vars form &rest rest)sl-let12102,520857 (defmacro sl-let@par (vars form &rest rest)sl-let@par12113,521110 (defmacro rewrite-entry-extending-failure (unify-subst failure-reason formrewrite-entry-extending-failure12125,521478 (defun set-difference-assoc-eq (lst alist)set-difference-assoc-eq12140,522337 (defun extend-unify-subst (alist unify-subst)extend-unify-subst12150,522772 (defun relieve-hyp-synp (rune hyp0 unify-subst rdepth type-alist wrld staterelieve-hyp-synp12159,523051 (defun push-lemma? (rune ttree)push-lemma?12270,528183 (defmacro push-lemma+ (rune ttree rcnst ancestors rhs rewritten-rhs)push-lemma+12275,528269 (defmacro push-splitter? (rune ttree rcnst ancestors rhs rewritten-rhs)push-splitter?12290,528838 (defmacro prepend-step-limit (n form)prepend-step-limit12308,529560 (defrec rw-cache-entryrw-cache-entry12319,529908 (defmacro free-failure-p (r)free-failure-p12381,532565 (defabbrev combine-free-failure-reasons (r1 r2)combine-free-failure-reasons12384,532629 (defun combine-free-failure-alists (a1 a2)combine-free-failure-alists12397,533070 (defun combine-sorted-rw-cache-lists1 (l1 l2)combine-sorted-rw-cache-lists112438,534539 (defun split-psorted-list1 (lst acc)split-psorted-list112491,536902 (defun split-psorted-list (lst)split-psorted-list12499,537163 (defun merge-lexorder-fast (l1 l2)merge-lexorder-fast12512,537661 (defun merge-sort-lexorder-fast (l)merge-sort-lexorder-fast12529,538319 (defun sort-rw-cache-list (lst)sort-rw-cache-list12569,539968 (defun combine-rw-cache-lists (lst1 lst2)combine-rw-cache-lists12587,540572 (defun merge-rw-caches (alist1 alist2)merge-rw-caches12611,541536 (defmacro sorted-rw-cache-p (cache)sorted-rw-cache-p12643,543056 (defun merge-symbol-alistp (a1 a2)merge-symbol-alistp12649,543187 (defun merge-sort-symbol-alistp (alist)merge-sort-symbol-alistp12659,543472 (defun cdr-sort-rw-cache (cache)cdr-sort-rw-cache12671,543941 (defun combine-rw-caches (c1 c2)combine-rw-caches12686,544412 (defun unify-subst-subsetp (a1 a2)unify-subst-subsetp12704,545127 (defun rw-cache-list-lookup (unify-subst hyps recs)rw-cache-list-lookup12716,545515 (defstub relieve-hyp-failure-entry-skip-prelieve-hyp-failure-entry-skip-p12745,546819 (defun relieve-hyp-failure-entry-skip-p-builtin (rune unify-subst hyps ttreerelieve-hyp-failure-entry-skip-p-builtin12749,546910 (defmacro rw-cache-active-p (rcnst)rw-cache-active-p12758,547263 (defun assoc-rw-cache (key alist)assoc-rw-cache12762,547390 (defun put-assoc-rw-cache1 (key val alist)put-assoc-rw-cache112770,547618 (defun put-assoc-rw-cache (key val alist)put-assoc-rw-cache12782,548112 (defun relieve-hyp-failure-entry (rune unify-subst hyps ttree step-limit)relieve-hyp-failure-entry12791,548390 (defun maybe-extend-tag-tree (tag vals ttree)maybe-extend-tag-tree12816,549422 (defun accumulate-rw-cache1 (replace-p tag new-ttree old-ttree)accumulate-rw-cache112823,549599 (defun accumulate-rw-cache (replace-p new-ttree old-ttree)accumulate-rw-cache12870,551563 (defun accumulate-rw-cache? (replace-p new-ttree old-ttree)accumulate-rw-cache?12889,552483 (defun dumb-occur-var (var term)dumb-occur-var12905,553168 (defun dumb-occur-var-lst (var lst)dumb-occur-var-lst12915,553497 (defun restrict-alist-to-all-vars1 (alist term)restrict-alist-to-all-vars112921,553661 (defun all-vars-boundp (term alist)all-vars-boundp12939,554406 (defun all-vars-lst-boundp (lst alist)all-vars-lst-boundp12947,554688 (defun restrict-alist-to-all-vars (alist term)restrict-alist-to-all-vars12956,554971 (defun push-rw-cache-entry (entry tag rune ttree)push-rw-cache-entry12979,555968 (defstub rw-cache-debugrw-cache-debug13001,556726 (defstub rw-cache-debug-actionrw-cache-debug-action13005,556822 (defun rw-cache-debug-builtin (rune target unify-subst failure-reasonrw-cache-debug-builtin13009,556925 (defun rw-cache-debug-action-builtin (rune target unify-subst failure-reasonrw-cache-debug-action-builtin13015,557150 (defun rw-cacheable-failure-reason-builtin (failure-reason)rw-cacheable-failure-reason-builtin13033,557781 (defun rw-cacheable-nil-tag (failure-reason)rw-cacheable-nil-tag13046,558315 (defun note-relieve-hyp-failure (rune unify-subst failure-reason ttree hypsnote-relieve-hyp-failure13067,559189 (defun replace-free-rw-cache-entry1 (unify-subst hyps entry recs)replace-free-rw-cache-entry113117,561644 (defun replace-free-rw-cache-entry (entry tag rune unify-subst hyps ttree)replace-free-rw-cache-entry13138,562648 (defun rw-cache-alist-nil-tag-p (alist)rw-cache-alist-nil-tag-p13157,563318 (defabbrev merge-free-failure-reasons-nil-tag (r1 r2)merge-free-failure-reasons-nil-tag13171,563933 (defun merge-free-failure-alists-nil-tag (a1 a2)merge-free-failure-alists-nil-tag13184,564513 (defun note-rw-cache-free-nil-tag (rune unify-subst hyps ttreenote-rw-cache-free-nil-tag13230,566295 (defun note-relieve-hyps-failure-free (rune unify-subst hyps ttree old-entrynote-relieve-hyps-failure-free13281,568423 (defun rw-cache-enter-context (ttree)rw-cache-enter-context13318,569824 (defun erase-rw-cache (ttree)erase-rw-cache13326,570099 (defun rw-cache-exit-context (old-ttree new-ttree)rw-cache-exit-context13335,570323 (defun restore-rw-cache-any-tag (new-ttree old-ttree)restore-rw-cache-any-tag13363,571582 (defun cons-tag-trees-rw-cache (ttree1 ttree2)cons-tag-trees-rw-cache13386,572770 (defun normalize-rw-any-cache (ttree)normalize-rw-any-cache13457,575590 (defun cons-tag-trees-rw-cache-first (ttree1 ttree2)cons-tag-trees-rw-cache-first13469,575986 (defun alist-keys-subsetp (x keys)alist-keys-subsetp13483,576418 (defmacro tag-tree-tags-subsetp (ttree tags)tag-tree-tags-subsetp13489,576572 (defun rw-cache (ttree)rw-cache13495,576685 (defun rw-cached-failure-pair (unify-subst rw-cache-alist)rw-cached-failure-pair13510,577144 (defun extend-rw-cache-alist-free (rcnst new-unify-substextend-rw-cache-alist-free13531,578114 (defun rw-cache-add-failure-reason (rcnst new-unify-substrw-cache-add-failure-reason13551,579131 (defun rewrite (term alist bkptr ; &extra formalsrewrite13576,580154 (defun rewrite-solidify-plus (term ; &extra formalsrewrite-solidify-plus14265,611872 (defun rewrite-if (test unrewritten-test left right alist ; &extra formalsrewrite-if14324,614637 (defun rewrite-args (args alist bkptr; &extra formalsrewrite-args14475,622630 (defun rewrite-primitive (fn args ; &extra formalsrewrite-primitive14503,623687 (defun rewrite-equal (lhs rhs lhs-ancestors rhs-ancestors ; &extra formalsrewrite-equal14541,625202 (defun relieve-hyprelieve-hyp14844,639206 (defun relieve-hyps1-iter (rune target hyps backchain-limit-lstrelieve-hyps1-iter15216,658589 (defun relieve-hyps1 (rune target hyps backchain-limit-lstrelieve-hyps115253,660550 (defun relieve-hyps1-free-1relieve-hyps1-free-115412,668177 (defun relieve-hyps1-free-2relieve-hyps1-free-215531,673712 (defun relieve-hyps (rune target hyps backchain-limit-lstrelieve-hyps15720,681813 (defun rewrite-with-lemma (term lemma ; &extra formalsrewrite-with-lemma15855,688112 (defun rewrite-with-lemmas1 (term lemmasrewrite-with-lemmas116202,705554 (defun rewrite-fncall (rule term ; &extra formalsrewrite-fncall16246,707501 (defun rewrite-with-lemmas (term ; &extra formalsrewrite-with-lemmas16632,727917 (defun rewrite-linear-term (term alist ; &extra formalsrewrite-linear-term16773,734734 (defun rewrite-linear-term-lst (term-lst ttrees ; &extra formalsrewrite-linear-term-lst16862,738749 (defun add-linear-lemma (term lemma ; &extra formalsadd-linear-lemma16915,741120 (defun add-linear-lemmas (term linear-lemmas ; &extra formalsadd-linear-lemmas17153,752238 (defun multiply-alists2 (alist-entry1 alist-entry2 poly ; &extra formalsmultiply-alists217206,754451 (defun multiply-alists1 (alist-entry alist2 poly ; &extra formalsmultiply-alists117265,757100 (defun multiply-alists (alist1 alist2 poly ; &extra formalsmultiply-alists17312,758825 (defun multiply-polys1 (alist1 const1 rel1 alist2 const2 rel2multiply-polys117368,760812 (defun multiply-polys (poly1 poly2 ; &extra formalsmultiply-polys17540,768426 (defun multiply-pots2 (poly big-poly-list new-poly-list ; &extra formalsmultiply-pots217602,771026 (defun multiply-pots1 (poly-list big-poly-list new-poly-list ; &extra formalsmultiply-pots117663,773149 (defun multiply-pots-super-filter (var-list pot-lst-to-look-in ; &extra formalsmultiply-pots-super-filter17712,774825 (defun multiply-pots-filter (var-list pot-lst-to-look-in ; &extra formalsmultiply-pots-filter17768,777000 (defun multiply-pots (var-list pot-lst-to-look-in ; &extra formalsmultiply-pots17822,779229 (defun add-multiplied-polys-filter (var-list products-already-triedadd-multiplied-polys-filter17875,781177 (defun add-multiplied-polys (var-list products-already-triedadd-multiplied-polys17955,784777 (defun deal-with-product1 (part-of-new-var var-listdeal-with-product118039,788286 (defun deal-with-product (new-var pot-lst-to-look-indeal-with-product18158,793972 (defun deal-with-factor (new-var pot-lst-to-look-indeal-with-factor18204,795866 (defun deal-with-division (new-var inverse-vardeal-with-division18292,799972 (defun non-linear-arithmetic1 (new-vars pot-lst ;;; to look-in/step-downnon-linear-arithmetic118409,805103 (defun non-linear-arithmetic (new-vars pot-lst ;;; to look-in/step-downnon-linear-arithmetic18502,809449 (defun add-polys-and-lemmas2-nl (new-vars old-pot-lst ; &extra formalsadd-polys-and-lemmas2-nl18791,821031 (defun add-polys-and-lemmas1-nl (old-pot-lst cnt ; &extra formalsadd-polys-and-lemmas1-nl18884,825241 (defun add-polys-and-lemmas1 (new-vars old-pot-lst ; &extra formalsadd-polys-and-lemmas119008,830484 (defun add-polys-and-lemmas (lst disjunctsp ; &extra formalsadd-polys-and-lemmas19074,833196 (defun add-disjunct-polys-and-lemmas (lst1 lst2 ; &extra formalsadd-disjunct-polys-and-lemmas19171,837270 (defun add-disjuncts-polys-and-lemmas (split-lst to-do-lateradd-disjuncts-polys-and-lemmas19285,842402 (defun add-terms-and-lemmas (term-lst ttrees positivepadd-terms-and-lemmas19457,850502 (defun rewrite-with-linear (term ; &extra formalsrewrite-with-linear19552,854858 simplify.lisp,19196 (defun negate-lit (term type-alist ens force-flg wrld)negate-lit65,2819 (defun pegate-lit (term type-alist ens force-flg wrld)pegate-lit86,3645 (defun add-literal (lit cl at-end-flg)add-literal107,4440 (defun add-each-literal (cl)add-each-literal144,5860 (defun subsumes-rec (count cl1 cl2 alist)subsumes-rec206,8828 (defun subsumes1-equality-with-const (count lit x const1 tl1 tl2 cl2 alist)subsumes1-equality-with-const252,11316 (defun subsumes1 (count lit tl1 tl2 cl2 alist)subsumes1301,14150 (defun subsumes!-rec (cl1 cl2 alist)subsumes!-rec336,15656 (defun subsumes!1-equality-with-const (lit x const1 tl1 tl2 cl2 alist)subsumes!1-equality-with-const363,16943 (defun subsumes!1 (lit tl1 tl2 cl2 alist)subsumes!1399,18696 (defconst *init-subsumes-count**init-subsumes-count*419,19397 (defun subsumes (init-subsumes-count cl1 cl2 alist)subsumes439,20215 (defun some-member-subsumes (init-subsumes-count cl-set cl acc)some-member-subsumes462,21200 (defun conjoin-clause-to-clause-set (cl cl-set)conjoin-clause-to-clause-set475,21722 (defun add-each-literal-lst (cl-set)add-each-literal-lst494,22646 (defun conjoin-clause-sets (cl-set1 cl-set2)conjoin-clause-sets500,22849 (defun some-element-member-complement-term (lst1 lst2)some-element-member-complement-term506,23055 (defun disjoin-clauses1 (cl1 cl2)disjoin-clauses1514,23376 (defun disjoin-clauses (cl1 cl2)disjoin-clauses530,24029 (defun disjoin-clause-segment-to-clause-set (segment cl-set)disjoin-clause-segment-to-clause-set548,24640 (defun split-on-assumptions (assumptions cl ans)split-on-assumptions567,25558 (defun rewrite-clause-action (lit branches)rewrite-clause-action581,26033 (defrec forward-chaining-ruleforward-chaining-rule717,33879 (defrec fc-activationfc-activation727,34373 (defun suspend-fc-activation (act inst-hyp hyps unify-subst ttree)suspend-fc-activation840,40932 (defun prettyify-fc-activation (act level)prettyify-fc-activation901,43353 (defun prettyify-fc-activations (acts level)prettyify-fc-activations976,46700 (defun make-fc-activation (term rule ttree ens)make-fc-activation981,46897 (defun make-fc-activations (term rules ttree ens activations)make-fc-activations1029,49411 (defun collect-terms-and-activations (term ttree wrld ens trigger-terms activations)collect-terms-and-activations1039,49820 (defun collect-terms-and-activations-lstcollect-terms-and-activations-lst1092,52378 (defun collect-terms-and-activations-from-fcd-lst (fcd-lst wrld enscollect-terms-and-activations-from-fcd-lst1105,52966 (defun sublis-varp (alist term)sublis-varp1137,54353 (defun sublis-var-lstp (alist l)sublis-var-lstp1146,54638 (defun mult-search-type-alist (rest-hyps concls term typ type-alistmult-search-type-alist1155,54885 (defun mult-lookup-hyp (hyp rest-hyps concls type-alist wrld unify-subst ttreemult-lookup-hyp1226,58673 (defun ev-respecting-ens (form alist state latches ttree ens wrld)ev-respecting-ens1245,59482 (defun ev-lst-respecting-ens (lst alist state latches ttree ens wrld)ev-lst-respecting-ens1302,62400 (defun add-fc-derivations (rune concls unify-subst inst-triggeradd-fc-derivations1401,67961 (defun prettyify-fc-derivation (fcd level)prettyify-fc-derivation1438,69596 (defun prettyify-fc-derivations (fcd-lst level)prettyify-fc-derivations1499,72210 (defun expunge-fc-derivations-lst (fc-derivation-lst ttree)expunge-fc-derivations-lst1507,72441 (defun expunge-fc-derivations (ttree)expunge-fc-derivations1517,72952 (defun current-fc-call-number (data)current-fc-call-number1707,82792 (defun current-fc-call-alist (data)current-fc-call-alist1711,82911 (defun put-current-fc-call-alist (call-alist data)put-current-fc-call-alist1715,83029 (defun initialize-fc-wormhole-sites ()initialize-fc-wormhole-sites1733,83866 (deflabel forward-chaining-reportsforward-chaining-reports1748,84317 (defun show-fc-criteria ()show-fc-criteria2034,97548 (defun reset-fc-reporting ()reset-fc-reporting2057,98085 (defun translate-fc-criterion (x state)translate-fc-criterion2088,98952 (defun translate-fc-criteria (lst state)translate-fc-criteria2119,100167 (defun set-fc-criteria-fn (x state)set-fc-criteria-fn2136,100902 (defmacro set-fc-criteria (&rest x)set-fc-criteria2160,101707 (defun set-fc-report-on-the-fly (flg)set-fc-report-on-the-fly2232,104949 (defun new-fc-call (caller cl pts force-flg do-not-reconsiderp wrld ensnew-fc-call2290,107213 (defun member-one-way-unify1 (term pat-lst unify-subst)member-one-way-unify12336,109245 (defun satisfying-fc-activation1p (criterion act)satisfying-fc-activation1p2350,109693 (defun satisfying-fc-activationp (criteria act)satisfying-fc-activationp2369,110392 (defun collect-satisfying-fc-activations (criteria acts ans)collect-satisfying-fc-activations2374,110600 (defun satisfying-virtual-fc-activation1p (criterion act0 unify-subst)satisfying-virtual-fc-activation1p2392,111530 (defun satisfying-virtual-fc-activationp (criteria act0 unify-subst)satisfying-virtual-fc-activationp2418,112618 (defun satisfying-fc-derivation1p (criterion fcd)satisfying-fc-derivation1p2430,113223 (defun satisfying-fc-derivationp (criteria fcd)satisfying-fc-derivationp2444,113701 (defun collect-satisfying-fc-derivations (criteria fcd-lst ans)collect-satisfying-fc-derivations2449,113909 (defun filter-satisfying-virtual-fc-activation (act0 inst-hyp hyps unify-subst ttree)filter-satisfying-virtual-fc-activation2464,114566 (defun filter-all-satisfying-fc-derivations (fcd-lst)filter-all-satisfying-fc-derivations2517,117086 (defun filter-satisfying-fc-activations (acts)filter-satisfying-fc-activations2562,119083 (defun filter-redundant-approved-fc-derivation (fcd)filter-redundant-approved-fc-derivation2596,120409 (defun collect-rune-trigger-pairs-from-fc-activations (acts ans)collect-rune-trigger-pairs-from-fc-activations2696,124749 (defun collect-rune-trigger-pairs-from-fc-derivations (fcds ans)collect-rune-trigger-pairs-from-fc-derivations2706,125248 (defun prettyify-subst (alist)prettyify-subst2717,125751 (defun collect-fc-status-site-1 (rune inst-trigger acts)collect-fc-status-site-12724,126024 (defun collect-fc-status-sites-2-3-5 (rune inst-trigger all-fcdscollect-fc-status-sites-2-3-52750,127282 (defun prettyify-blocked-fc-inst-hyp (inst-hyp hyps unify-subst)prettyify-blocked-fc-inst-hyp2784,128971 (defun collect-fc-status-site-4 (rune inst-trigger acts)collect-fc-status-site-42803,129775 (defun collect-fc-status (rune inst-trigger site1 site2 site3 site4 site5)collect-fc-status2831,131067 (defun make-fc-activity-report1 (rune-trigger-pairs site1 site2 site3 site4 site5)make-fc-activity-report12843,131585 (defun make-fc-activity-report (call-alist)make-fc-activity-report2855,132166 (defun fc-report1 (whs k)fc-report12881,133210 (defun fc-report (k)fc-report2920,134589 (defun fc-exit (flg type-alist ttree-or-fc-pairsfc-exit2954,135612 (defun advance-fc-activation1advance-fc-activation13072,141659 (defun advance-fc-activation2advance-fc-activation23230,148410 (defun advance-fc-activation3advance-fc-activation33462,158532 (defun advance-fc-activation (act fc-round type-alist ens force-flg wrld state oncep-overrideadvance-fc-activation3486,159501 (defun advance-fc-activations (lst fc-round type-alist ens force-flg wrld state oncep-overrideadvance-fc-activations3511,160540 (defun fc-pair-lst (fcd-lst)fc-pair-lst3532,161578 (defun fc-pair-lst-type-alist (fc-pair-lst fcd-lst type-alist force-flg ens wrld)fc-pair-lst-type-alist3554,162602 (defmacro fcd-runep (rune ttree)fcd-runep3676,168253 (defun fcd-runep-lst (rune lst)fcd-runep-lst3687,168794 (defmacro fcd-worse-than-or-equal (concl fn-cnt p-fn-cnt ttree)fcd-worse-than-or-equal3693,169041 (defun fcd-worse-than-or-equal-lst (concl fn-cnt p-fn-cnt lst)fcd-worse-than-or-equal-lst3705,169640 (defun exists-fcd-worse-than-or-equal (fcd-lst concl fn-cnt p-fn-cnt)exists-fcd-worse-than-or-equal3741,171456 (defun all-dumb-occur-lst (args cl)all-dumb-occur-lst3757,172181 (defun all-args-occur-after-strip-not (term cl)all-args-occur-after-strip-not3762,172344 (defun approved-fc-derivationp (fcd cl)approved-fc-derivationp3778,173038 (defun approve-fc-derivations (new-fcd-lst cl approved-this-round all-approved)approve-fc-derivations3864,176138 (defun max-level-no (term wrld)max-level-no3892,177536 (defun max-level-no-lst (args wrld)max-level-no-lst3907,178175 (defun get-level-no (fn wrld)get-level-no3912,178338 (defun sort-approved1-rating1 (term wrld fc vc)sort-approved1-rating13926,178638 (defun sort-approved1-rating1-lst (lst wrld fc vc)sort-approved1-rating1-lst3944,179487 (defun sort-approved1-rating (term wrld)sort-approved1-rating3951,179738 (defun sort-approved1 (approved wrld)sort-approved13969,180665 (defun sort-approved (approved wrld)sort-approved3978,180968 (defun strip-fcd-concls (fcd-lst)strip-fcd-concls3985,181225 (defun type-alist-fcd-lst (fcd-lst type-alisttype-alist-fcd-lst3993,181502 (defun every-concl-member-equalp (fcd-lst trigger-terms)every-concl-member-equalp4043,183741 (defun forward-chain1 (cl fc-round trigger-terms activations type-alist force-flg wrldforward-chain14057,184252 (defun forward-chain-top (caller cl pts force-flg do-not-reconsiderp wrld ensforward-chain-top4135,188551 (defun forward-chain (cl pts force-flg do-not-reconsiderp wrld ensforward-chain4240,193567 (defun select-forward-chained-concls-and-ttrees (fc-pair-lst pt lits ttree-lst)select-forward-chained-concls-and-ttrees4258,194442 (defun rewrite-clause-type-alist (tail new-clause fc-pair-lst rcnst wrldrewrite-clause-type-alist4532,207276 (defun maximal-multiples1 (term-lst new-vars avoid-vars pkg-witness)maximal-multiples14836,221859 (defun maximal-multiples (term pkg-witness)maximal-multiples4850,222355 (defun lambda-abstract1 (vars terms)lambda-abstract14863,222790 (defun lambda-abstract (term pkg-witness)lambda-abstract4872,223141 (defun mutually-exclusive-tests (a b)mutually-exclusive-tests4889,223660 (defun mutually-exclusive-subsumptionp (a b c)mutually-exclusive-subsumptionp4920,224777 (defun cleanup-if-expr (x trues falses)cleanup-if-expr4940,225471 (defun cleanup-if-expr-lst (x trues falses)cleanup-if-expr-lst4964,226383 (defun all-type-reasoning-tags-p1 (lemmas)all-type-reasoning-tags-p14970,226573 (defun all-type-reasoning-tags-p (ttree)all-type-reasoning-tags-p4977,226831 (defun try-clause (atm clause wrld)try-clause4980,226935 (defconst *trivial-non-nil-ttree**trivial-non-nil-ttree*4995,227362 (defun make-non-nil-ttree (ttree)make-non-nil-ttree4998,227414 (defun try-type-set-and-clause (atm ans ttree ttree0 current-clause wrld enstry-type-set-and-clause5002,227493 (defun lambda-subtermp (term)lambda-subtermp5033,228923 (defun lambda-subtermp-lst (termlist)lambda-subtermp-lst5043,229177 (defun rewrite-atm (atm not-flg bkptr gstack type-alist wrldrewrite-atm5051,229335 (defun every-occurrence-equiv-hittablep1every-occurrence-equiv-hittablep15432,246164 (defun every-occurrence-equiv-hittablep1-listpevery-occurrence-equiv-hittablep1-listp5471,247512 (defun every-occurrence-equiv-hittablep (equiv old geneqv term ens wrld)every-occurrence-equiv-hittablep5489,248007 (defun every-occurrence-equiv-hittablep-in-clausep (equiv old cl ens wrld)every-occurrence-equiv-hittablep-in-clausep5510,248867 (defun some-occurrence-equiv-hittablep1 (equiv old geneqv term ens wrld)some-occurrence-equiv-hittablep15532,249623 (defun some-occurrence-equiv-hittablep1-listpsome-occurrence-equiv-hittablep1-listp5564,250702 (defun some-occurrence-equiv-hittablep (equiv old geneqv term ens wrld)some-occurrence-equiv-hittablep5580,251133 (defun equiv-hittable-in-some-other-lit (equiv term n cl i ens wrld)equiv-hittable-in-some-other-lit5597,251740 (defun find-trivial-equivalence1find-trivial-equivalence15610,252296 (defun find-trivial-equivalence (not-just-quotep-flg cl ens wrld avoid-lst)find-trivial-equivalence5766,260335 (defun add-literal-and-pt1 (cl-tail pt cl pt-lst)add-literal-and-pt15817,262821 (defun add-literal-and-pt (lit pt cl pt-lst ttree)add-literal-and-pt5835,263637 (defun add-binding-to-tag-tree (var term ttree)add-binding-to-tag-tree5866,264771 (defun subst-equiv-and-maybe-delete-litsubst-equiv-and-maybe-delete-lit5880,265299 (defun remove-trivial-equivalencesremove-trivial-equivalences5935,268048 (defrec built-in-clause ((nume . all-fnnames) clause . rune) t)built-in-clause6006,271892 (defconst *initial-built-in-clauses**initial-built-in-clauses*6019,272604 (defun built-in-clausep2 (bic-lst cl fns ens)built-in-clausep26247,281121 (defun built-in-clausep1 (bic-alist cl fns ens)built-in-clausep16260,281711 (defun possible-trivial-clause-p (cl)possible-trivial-clause-p6283,282851 (defun trivial-clause-p (cl wrld)trivial-clause-p6307,283780 (defun built-in-clausep (caller cl ens match-free-override wrld state)built-in-clausep6312,283930 (defun crunch-clause-segments1 (seg1 pts1 cl pts)crunch-clause-segments16385,287747 (defun crunch-clause-segments2 (cl pts seg1 pts1)crunch-clause-segments26425,289938 (defun crunch-clause-segments (seg1 pts1 seg2 ens wrld state ttree)crunch-clause-segments6439,290449 (defun strip-non-rewrittenp-assumptions1 (recs acc)strip-non-rewrittenp-assumptions16506,294109 (defun strip-non-rewrittenp-assumptions (ttree)strip-non-rewrittenp-assumptions6521,294816 (defun assumnote-list-to-token-list (assumnote-list)assumnote-list-to-token-list6538,295548 (defun resume-suspended-assumption-rewriting1resume-suspended-assumption-rewriting16544,295761 (defun resume-suspended-assumption-rewritingresume-suspended-assumption-rewriting6702,303689 (defun helpful-little-ecnt-msg (case-limit ecnt)helpful-little-ecnt-msg6797,308838 (defun rewrite-clause (tail pts bkptr gstack new-clause fc-pair-lst wrldrewrite-clause6811,309213 (defun rewrite-clause-lst (segs bkptr gstack cdr-tail cdr-pts new-clauserewrite-clause-lst7169,326436 (defun setup-simplify-clause-pot-lst1 (cl ttrees type-alist rcnst wrld statesetup-simplify-clause-pot-lst17237,329276 (defun setup-simplify-clause-pot-lst (cl ttrees fc-pair-lstsetup-simplify-clause-pot-lst7270,330611 (defun sequential-subst-var-term (alist term)sequential-subst-var-term7357,333898 (defun process-equational-polysprocess-equational-polys7369,334364 (defun enumerate-elements (lst i)enumerate-elements7548,342989 (defun already-used-by-fertilize-clausep (lit hist get-clause-id)already-used-by-fertilize-clausep7552,343110 (defun unhidden-lit-info (hist clause pos wrld)unhidden-lit-info7569,343969 (defun tilde-@-hyp-phrase (len-tail cl)tilde-@-hyp-phrase7588,344821 (defun simplify-clause1 (top-clause hist rcnst wrld state step-limit)simplify-clause17617,345931 (defun some-element-dumb-occur-lst (lst1 lst2)some-element-dumb-occur-lst7795,354551 (defrec prove-spec-varprove-spec-var7808,355121 (defrec gag-infogag-info7835,356297 (defrec gag-stategag-state7851,356859 (defconst *initial-gag-state**initial-gag-state*7859,357255 (defconst *empty-prove-spec-var**empty-prove-spec-var*7867,357428 (defun controller-unify-subst2 (vars acc)controller-unify-subst27882,357830 (defun controller-unify-subst1 (actuals controllers acc)controller-unify-subst17889,358069 (defun controller-unify-subst (name term def-body)controller-unify-subst7897,358402 (defun filter-disabled-expand-terms (terms ens wrld)filter-disabled-expand-terms7904,358703 (defun found-hit-rewrite-hist-entry (hist)found-hit-rewrite-hist-entry7985,362752 (defabbrev append? (x y)append?7999,363321 (defun simplify-clause (cl hist pspv wrld state step-limit)simplify-clause8003,363395 (defun settled-down-clause (clause hist pspv wrld state)settled-down-clause8357,381955 (defun member-class-name-runes (class name runes)member-class-name-runes8380,382927 (defun extract-and-classify-lemmas2 (names class ignore-lst if-intro case-splitextract-and-classify-lemmas28388,383200 (defun extract-and-classify-lemmas1 (class-alist ignore-lst if-intro case-splitextract-and-classify-lemmas18413,384498 (defun runes-to-class-alist1 (runes alist)runes-to-class-alist18430,385265 (defun strict-merge-symbol-< (l1 l2 acc)strict-merge-symbol-<8444,385897 (defun strict-merge-sort-symbol-< (l)strict-merge-sort-symbol-<8465,386730 (defun strict-symbol-<-sortedp (x)strict-symbol-<-sortedp8482,387241 (defun sort-symbol-listp (x)sort-symbol-listp8489,387469 (defun strict-merge-sort-symbol-<-cdrs (alist)strict-merge-sort-symbol-<-cdrs8495,387637 (defun runes-to-class-alist (runes)runes-to-class-alist8501,387871 (defun extract-and-classify-lemmas (ttree ignore-lst forced-runes)extract-and-classify-lemmas8508,388063 (deflabel SimpleSimple8540,389455 (defun tilde-*-conjunction-of-possibly-forced-names-phrase1 (alist)tilde-*-conjunction-of-possibly-forced-names-phrase18573,390850 (defun tilde-*-conjunction-of-possibly-forced-names-phrase (lst)tilde-*-conjunction-of-possibly-forced-names-phrase8587,391398 (defconst *fake-rune-alist**fake-rune-alist*8602,392001 (defun rune-< (x y)rune-<8615,392464 (defun merge-runes (l1 l2)merge-runes8630,392822 (defun merge-sort-runes (l)merge-sort-runes8637,393040 (defun tilde-*-simp-phrase1 (alist abbreviations-flg)tilde-*-simp-phrase18642,393205 (defun tilde-*-raw-simp-phrase1 (runes forced-runes punct ignore-lst phrasetilde-*-raw-simp-phrase18772,398347 (defun recover-forced-runes1 (recs ans)recover-forced-runes18821,400637 (defun recover-forced-runes (ttree)recover-forced-runes8833,401014 (defun tilde-*-raw-simp-phrase (ttree punct phrase)tilde-*-raw-simp-phrase8845,401555 (defun tilde-*-simp-phrase (ttree)tilde-*-simp-phrase8872,402507 (defun tilde-@-pool-name-phrase (forcing-round pool-lst)tilde-@-pool-name-phrase8904,403928 (defun tilde-@-pool-name-phrase-lst (forcing-round lst)tilde-@-pool-name-phrase-lst8942,405866 (defun tilde-@-clause-id-phrase (id)tilde-@-clause-id-phrase8947,406092 (defrec bddnotebddnote9036,409059 (defun tilde-@-bddnote-phrase (x)tilde-@-bddnote-phrase9040,409160 (defun parse-natural1 (str i maximum ans)parse-natural19054,409673 (defun parse-natural (dflg str i maximum)parse-natural9080,410625 (defun parse-dotted-naturals (dflg str i maximum ans)parse-dotted-naturals9101,411431 (defun parse-match (pat j patmax str i strmax)parse-match9137,413109 (defun parse-primes (str i maximum)parse-primes9151,413672 (defun parse-clause-id2 (forcing-round pool-lst str i maximum)parse-clause-id29177,414655 (defun parse-clause-id1 (forcing-round str i maximum)parse-clause-id19207,415667 (defun parse-clause-id (str)parse-clause-id9272,417946 (defun tilde-@-case-split-limitations-phrase (sr-flg case-flg prefix)tilde-@-case-split-limitations-phrase9310,419249 (defun simplify-clause-msg1 (signal cl-id clauses speciousp ttree pspv state)simplify-clause-msg19325,419791 (deflabel specious-simplificationspecious-simplification9447,426022 (defun settled-down-clause-msg1 (signal clauses ttree pspv state)settled-down-clause-msg19559,431610 bdd.lisp,8083 (defmacro mvf (x &rest rest)mvf45,1569 (defmacro logandf (&rest args)logandf57,2133 (defmacro logxorf (&rest args)logxorf60,2201 (defmacro logiorf (&rest args)logiorf63,2268 (defmacro ashf (x y)ashf66,2335 (defmacro mx-id-bound ()mx-id-bound70,2441 (defmacro 1+mx-id (x)1+mx-id78,2692 (defmacro bdd-error (mx-id fmt-string fmt-alist bad-cst ttree)bdd-error125,4696 (defmacro unique-id (x) `(the-fixnum (car ,x)))unique-id207,8076 (defmacro tst (x) `(cadr ,x)) ;a cst, not a number; but beware since tst=trmtst209,8125 (defmacro cst-boolp (x) `(caddr ,x))cst-boolp215,8415 (defmacro tbr (x) `(cadddr ,x))tbr217,8453 (defmacro fbr (x) `(cddddr ,x))fbr218,8485 (defmacro leafp (x)leafp220,8518 (defmacro trm (x) `(cadr ,x))trm223,8561 (defun bdd-constructors (wrld)bdd-constructors225,8592 (defun make-leaf-cst (unique-id trm boolp)make-leaf-cst235,8944 (defun evg-fn-symb (x)evg-fn-symb245,9246 (defun bdd-constructor-trm-p (trm bdd-constructors)bdd-constructor-trm-p274,10233 (defun evg-type (x)evg-type280,10446 (defun make-if-cst (unique-id tst tbr fbr bdd-constructors)make-if-cst296,10986 (defconst *cst-t* (make-leaf-cst 1 *t* t))*cst-t*355,13913 (defconst *cst-nil* (make-leaf-cst 2 *nil* t))*cst-nil*356,13958 (defmacro cst= (cst1 cst2)cst=358,14006 (defmacro cst-tp (cst)cst-tp362,14084 (defmacro cst-nilp (cst)cst-nilp365,14135 (defmacro cst-varp (cst)cst-varp368,14188 (defun cst-nonnilp (cst)cst-nonnilp371,14241 (defun bool-mask1 (formals vars rune)bool-mask1395,15298 (defun boolean-term-var (x)boolean-term-var412,16118 (defun boolean-hyps-vars (hyps)boolean-hyps-vars445,17302 (defun first-boolean-type-prescription (type-prescription-list ens formals)first-boolean-type-prescription460,17682 (defun recognizer-rune (fn recognizer-alist wrld ens)recognizer-rune495,19293 (defun bool-mask (fn recognizer-alist wrld ens)bool-mask505,19719 (defun commutative-p1 (fn lemmas ens)commutative-p1549,21308 (defun find-equivalence-rune (fn rules)find-equivalence-rune578,22704 (defun equivalence-rune1 (fn congruences)equivalence-rune1589,23057 (defun equivalence-rune (fn wrld)equivalence-rune616,24053 (defun commutative-p (fn ens wrld)commutative-p626,24424 (defun op-alist (fns acc i ens wrld)op-alist643,25062 (defun op-alist-info (fn op-alist)op-alist-info679,26597 (defmacro if-op-code () 3)if-op-code702,27209 (defmacro hash-size ()hash-size704,27237 (defmacro if-hash-index (x y z)if-hash-index731,28315 (defun op-hash-index1 (args i acc)op-hash-index1743,28736 (defmacro op-hash-index (op-code args)op-hash-index770,29783 (defmacro op-hash-index-2 (op-code arg1 arg2)op-hash-index-2775,29918 (defmacro op-hash-index-if (arg1 arg2 arg3)op-hash-index-if785,30189 (defun if-search-bucket (x y z lst)if-search-bucket795,30500 (defun cst=-lst (x y)cst=-lst806,30790 (defmacro eq-op (x y)eq-op812,30913 (defun op-search-bucket (op args lst)op-search-bucket818,31020 (defun op-search-bucket-2 (op arg1 arg2 lst)op-search-bucket-2831,31499 (defun op-search-bucket-if (arg1 arg2 arg3 lst)op-search-bucket-if848,32233 (defun chk-memo (op-code op args op-ht)chk-memo864,32847 (defun chk-memo-2 (op-code op arg1 arg2 op-ht)chk-memo-2882,33516 (defun chk-memo-if (arg1 arg2 arg3 op-ht)chk-memo-if898,34050 (defmacro half-hash-size ()half-hash-size918,34764 (defmacro fourth-hash-size ()fourth-hash-size921,34818 (defun op-hash-index-string (index acc string)op-hash-index-string924,34874 (defun op-hash-index-evg (evg)op-hash-index-evg937,35270 (defun op-search-bucket-quote (evg bucket)op-search-bucket-quote970,36309 (defun chk-memo-quotep (term op-ht)chk-memo-quotep978,36584 (defun bdd-quotep (term op-ht mx-id)bdd-quotep994,37068 (defmacro bdd-quotep+ (term op-ht if-ht mx-id ttree)bdd-quotep+1027,38179 (defrec bdd-rulebdd-rule1044,38998 (defun rewrite-rule-to-bdd-rule (lemma)rewrite-rule-to-bdd-rule1048,39040 (defun bdd-rules-alist1bdd-rules-alist11054,39242 (defun extra-rules-for-bdds (fn wrld)extra-rules-for-bdds1132,43377 (defun bdd-rules-alist (fns all-fns bdd-rules-alist ens wrld)bdd-rules-alist1199,46029 (defmacro one-way-unify1-cst-2 (mx-id p1 p2 cst1 cst2 alist op-ht)one-way-unify1-cst-21261,48649 (defmacro one-way-unify1-cst-3 (mx-id p1 p2 p3 cst1 cst2 cst3 alist op-ht)one-way-unify1-cst-31275,49267 (defun one-way-unify1-cst (mx-id pat cst alist op-ht)one-way-unify1-cst1293,49997 (defun one-way-unify1-cst-lst (mx-id pl cstl alist op-ht)one-way-unify1-cst-lst1474,60842 (defun one-way-unify1-cst-equal (mx-id pat1 pat2 cst1 cst2 alist op-ht)one-way-unify1-cst-equal1492,61495 (defun some-one-way-unify-cst-lst (cst-lst rules op-ht mx-id ttree)some-one-way-unify-cst-lst1506,61984 (defun leaf-cst-list (lst bool-vars acc mx-id)leaf-cst-list1535,63135 (defun decode-cst (cst cst-array)decode-cst1557,63988 (defun decode-cst-lst (cst-lst cst-array)decode-cst-lst1595,65259 (defun decode-cst-alist1 (alist cst-array)decode-cst-alist11607,65626 (defun decode-cst-alist (cst-alist cst-array)decode-cst-alist1619,66074 (defun leaf-cst-list-array (mx-id)leaf-cst-list-array1625,66256 (defconst *some-non-nil-value* "Some non-nil value")*some-non-nil-value*1632,66487 (defun falsifying-assignment1 (cst acc cst-array)falsifying-assignment11634,66541 (defun falsifying-assignment (cst mx-id)falsifying-assignment1671,67928 (defun make-if (mx-id n op args x y z op-ht if-ht bdd-constructors)make-if1681,68289 (defun make-if-no-memo (mx-id x y z op-ht if-ht bdd-constructors)make-if-no-memo1760,71876 (defmacro split-var (cst)split-var1794,73155 (defun min-var (acc args)min-var1807,73494 (defun combine-op-csts1 (var-id args)combine-op-csts11827,74082 (defun bool-flg (args mask)bool-flg1860,75605 (defun some-bdd-constructorp (args bdd-constructors)some-bdd-constructorp1882,76256 (defun combine-op-csts-simplecombine-op-csts-simple1889,76513 (defmacro bdd-mv-let (vars form body)bdd-mv-let1941,78444 (defmacro combine-if-csts+ (cst1 cst2 cst3 op-ht if-ht mx-id bdd-constructors)combine-if-csts+2004,80869 (defun combine-if-csts1 (var-id args)combine-if-csts12013,81192 (defun combine-if-cstscombine-if-csts2033,81917 (defun cst-list-to-evg-list (cst-lst)cst-list-to-evg-list2111,85081 (defun cst-quote-listp (cst-lst)cst-quote-listp2117,85244 (defrec bddspvbddspv2125,85428 (defun bdd-ev-fncallbdd-ev-fncall2136,85831 (defmacro combine-op-csts+ (mx-id comm-p enabled-exec-p op-code op mask args op-htcombine-op-csts+2166,87051 (defun make-if-for-opmake-if-for-op2184,87998 (defun combine-op-csts (mx-id enabled-exec-p op-code op mask args op-htcombine-op-csts2234,89514 (defun combine-op-csts-comm (mx-id comm-p enabled-exec-p op-code op mask arg1combine-op-csts-comm2264,90648 (defun combine-op-csts-gutscombine-op-csts-guts2341,93875 (defun bdd (term alist op-ht if-ht mx-id ttree bddspv state)bdd2462,99048 (defun bdd-alist (formals actuals alist op-ht if-ht mx-id ttree bddspv state)bdd-alist2583,103825 (defun bdd-list (lst alist op-ht if-ht mx-id ttree bddspv state)bdd-list2602,104549 (defun if-ht-max-length (state)if-ht-max-length2629,105483 (defun op-ht-max-length (state)op-ht-max-length2634,105638 (defun leaf-cst-list-to-alist (leaf-cst-list)leaf-cst-list-to-alist2639,105793 (defvar *request-bigger-fixnum-table**request-bigger-fixnum-table*2653,106221 (defun bdd-top (term input-vars bool-vars bdd-constructors cl-id ens state)bdd-top2656,106311 (defun get-bool-vars (vars type-alist ttree acc)get-bool-vars2764,111984 (defun bdd-clause1 (hint-alist type-alist cl position ttree0 cl-id ens wrldbdd-clause12782,112589 (defmacro expand-and-or-simple+expand-and-or-simple+2908,118017 (defun expand-and-or-simpleexpand-and-or-simple2920,118482 (defun expand-clauseexpand-clause3067,124978 (defun bdd-clause (bdd-hint cl-id top-clause pspv wrld state)bdd-clause3092,125980 (deflabel obddobdd3208,131943 (deflabel bdd-algorithmbdd-algorithm3216,132097 (deflabel bdd-introductionbdd-introduction3726,157290 other-processes.lisp,6751 (defun strip-final-digits1 (lst)strip-final-digits130,1253 (defun strip-final-digits (str)strip-final-digits51,2117 (defconst *var-families-by-type**var-families-by-type*76,3315 (defun assoc-ts-subsetp (ts alist)assoc-ts-subsetp97,4014 (defun first-non-member-eq (lst1 lst2)first-non-member-eq107,4281 (defun abbreviate-hyphenated-string1 (str i maximum prev-c)abbreviate-hyphenated-string1120,4689 (defun abbreviate-hyphenated-string (str)abbreviate-hyphenated-string147,5788 (defun generate-variable-root1 (term avoid-lst type-alist ens wrld)generate-variable-root1164,6375 (defun generate-variable-root (term avoid-lst type-alist ens wrld)generate-variable-root225,8865 (defun generate-variable (term avoid-lst type-alist ens wrld)generate-variable244,9569 (defun generate-variable-lst (term-lst avoid-lst type-alist ens wrld)generate-variable-lst256,10121 (defrec elim-ruleelim-rule290,11827 (defun occurs-nowhere-else (var args c i)occurs-nowhere-else296,11965 (defun first-nomination (term votes nominations)first-nomination308,12364 (defun second-nomination (term votes nominations)second-nomination315,12532 (defun some-hyp-probably-nilp (hyps type-alist ens wrld)some-hyp-probably-nilp328,12953 (defun sublis-expr (alist term)sublis-expr354,13821 (defun sublis-expr-lst (alist lst)sublis-expr-lst372,14513 (defun nominate-destructor-candidatenominate-destructor-candidate379,14679 (defun nominate-destructor-candidatesnominate-destructor-candidates504,21176 (defun nominate-destructor-candidates-lstnominate-destructor-candidates-lst527,22004 (defun sum-level-nos (lst wrld)sum-level-nos554,23112 (defun pick-highest-sum-level-nos (nominations wrld dterm max-score)pick-highest-sum-level-nos570,23777 (defun select-instantiated-elim-rule (clause type-alist eliminables ens wrld)select-instantiated-elim-rule589,24561 (defun type-restriction-segment (cl terms vars type-alist ens wrld)type-restriction-segment640,27174 (defun subterm-one-way-unify (pat term)subterm-one-way-unify736,31670 (defun subterm-one-way-unify-lst (pat-lst term)subterm-one-way-unify-lst749,32208 (defrec generalize-rule (nume formula . rune) nil)generalize-rule759,32501 (defun apply-generalize-rule (gen-rule term ens)apply-generalize-rule761,32553 (defun generalize-rule-segment1 (generalize-rules term ens)generalize-rule-segment1800,34317 (defun generalize-rule-segment (terms vars ens wrld)generalize-rule-segment822,35419 (defun generalize1 (cl type-alist terms vars ens wrld)generalize1846,36598 (defun apply-instantiated-elim-rule (rule cl type-alist avoid-vars ens wrld)apply-instantiated-elim-rule879,38225 (defun eliminate-destructors-clause1 (cl eliminables avoid-vars ens wrldeliminate-destructors-clause1995,44107 (defun owned-vars (process mine-flg history)owned-vars1140,51274 (defun eliminate-destructors-clause (clause hist pspv wrld state)eliminate-destructors-clause1219,55508 (defun prettyify-clause1 (cl wrld)prettyify-clause11253,57158 (defun prettyify-clause2 (cl wrld)prettyify-clause21258,57344 (defun prettyify-clause (cl let*-abstractionp wrld)prettyify-clause1272,57954 (defun prettyify-clause-lst (clauses let*-abstractionp wrld)prettyify-clause-lst1285,58505 (defun prettyify-clause-set (clauses let*-abstractionp wrld)prettyify-clause-set1291,58789 (defun tilde-*-elim-phrase/alist1 (alist wrld)tilde-*-elim-phrase/alist11297,59054 (defun tilde-*-elim-phrase/alist (alist wrld)tilde-*-elim-phrase/alist1304,59325 (defun tilde-*-elim-phrase3 (var-to-runes-alist)tilde-*-elim-phrase31316,59769 (defun tilde-*-elim-phrase2 (alist restricted-vars var-to-runes-alist ttree wrld)tilde-*-elim-phrase21324,60172 (defun tilde-*-elim-phrase1 (lst i already-used wrld)tilde-*-elim-phrase11351,61496 (defun tilde-*-elim-phrase (lst wrld)tilde-*-elim-phrase1384,63126 (defun tilde-*-untranslate-lst-phrase (lst flg wrld)tilde-*-untranslate-lst-phrase1404,63744 (defun eliminate-destructors-clause-msg1 (signal clauses ttree pspv state)eliminate-destructors-clause-msg11409,63890 (defun almost-quotep1 (term)almost-quotep11462,66324 (defun almost-quotep1-listp (terms)almost-quotep1-listp1473,66697 (defun almost-quotep (term)almost-quotep1480,66862 (defun destructor-applied-to-varsp (term ens wrld)destructor-applied-to-varsp1492,67217 (defun dumb-occur-lst-except (term lst lit)dumb-occur-lst-except1509,67900 (defun fertilize-feasible (lit cl hist term ens wrld)fertilize-feasible1527,68709 (defun fertilize-complexity (term wrld)fertilize-complexity1545,69499 (defun maximize-fertilize-complexity (terms wrld)maximize-fertilize-complexity1555,69834 (defun first-fertilize-lit (lst cl hist ens wrld)first-fertilize-lit1562,70038 (defun cross-fertilizep/c (equiv cl direction lhs1 rhs1)cross-fertilizep/c1596,71602 (defun cross-fertilizep/d (equiv cl direction lhs1 rhs1)cross-fertilizep/d1610,72093 (defun cross-fertilizep (equiv cl pspv direction lhs1 rhs1)cross-fertilizep1624,72584 (defun delete-from-ttree (tag val ttree)delete-from-ttree1649,73901 (defun fertilize-clause1 (cl lit1 equiv lhs1 rhs1fertilize-clause11661,74427 (defun fertilize-clause (cl-id cl hist pspv wrld state)fertilize-clause1865,84067 (defun fertilize-clause-msg1 (signal clauses ttree pspv state)fertilize-clause-msg11953,88534 (defun collectable-fnp (fn ens wrld)collectable-fnp1998,90495 (defun smallest-common-subterms1 (term1 term2 ens wrld ans)smallest-common-subterms12016,91124 (defun smallest-common-subterms1-lst (terms term2 ens wrld ans)smallest-common-subterms1-lst2077,93556 (defun dumb-fn-count-1 (flg x acc)dumb-fn-count-12098,94237 (defun dumb-fn-count (x)dumb-fn-count2112,94766 (defun smallest-common-subterms (term1 term2 ens wrld ans)smallest-common-subterms2135,95655 (defun generalizing-relationp (term wrld)generalizing-relationp2151,96368 (defun generalizable-terms-across-relations (cl ens wrld ans)generalizable-terms-across-relations2182,97735 (defun generalizable-terms-across-literals1 (lit1 cl ens wrld ans)generalizable-terms-across-literals12197,98356 (defun generalizable-terms-across-literals (cl ens wrld ans)generalizable-terms-across-literals2203,98602 (defun generalizable-terms (cl ens wrld)generalizable-terms2215,99081 (defun generalize-clause (cl hist pspv wrld state)generalize-clause2226,99435 (defun tilde-*-gen-phrase/alist1 (alist wrld)tilde-*-gen-phrase/alist12307,103268 (defun tilde-*-gen-phrase/alist (alist wrld)tilde-*-gen-phrase/alist2314,103537 (defun tilde-*-gen-phrase (alist restricted-vars var-to-runes-alist ttree wrld)tilde-*-gen-phrase2322,103706 (defun generalize-clause-msg1 (signal clauses ttree pspv state)generalize-clause-msg12376,106558 induct.lisp,9977 (defun select-x-cl-set (cl-set induct-hint-val)select-x-cl-set23,881 (defun unchangeables (formals args quick-block-info subset ans)unchangeables35,1336 (defun changeables (formals args quick-block-info subset ans)changeables50,1930 (defun sound-induction-principle-mask1 (formals args quick-block-infosound-induction-principle-mask166,2516 (defun sound-induction-principle-mask (term formals quick-block-info subset)sound-induction-principle-mask104,4307 (defrec candidatecandidate162,7269 (defun count-non-nils (lst)count-non-nils209,10209 (defun controllers (formals args subset ans)controllers214,10354 (defun changed/unchanged-vars (x args mask ans)changed/unchanged-vars221,10637 (defrec tests-and-alists (tests alists) nil)tests-and-alists228,10925 (defun tests-and-alists/alist (alist args mask call-args)tests-and-alists/alist230,10971 (defun tests-and-alists/alists (alist args mask calls)tests-and-alists/alists257,12146 (defrec tests-and-calls (tests . calls) nil)tests-and-calls279,13169 (defrec justificationjustification294,14018 (defun tests-and-alists (alist args mask tc)tests-and-alists299,14132 (defun tests-and-alists-lst (alist args mask machine)tests-and-alists-lst315,14877 (defun flesh-out-induction-principle (term formals justification mask machineflesh-out-induction-principle327,15346 (defun intrinsic-suggested-induction-candintrinsic-suggested-induction-cand364,16488 (defrec induction-rule (nume (pattern . condition) scheme . rune) nil)induction-rule404,18660 (defun apply-induction-rule (rule term type-alist xterm ttree seen ens wrld)apply-induction-rule408,18751 (defun suggested-induction-cands1suggested-induction-cands1518,22845 (defun suggested-induction-candssuggested-induction-cands577,25248 (defun get-induction-cands (term type-alist ens wrld ans)get-induction-cands601,26255 (defun get-induction-cands-lst (lst type-alist ens wrld ans)get-induction-cands-lst617,26774 (defun get-induction-cands-from-cl-set1 (cl-set ens oncep-override wrld stateget-induction-cands-from-cl-set1632,27150 (defun get-induction-cands-from-cl-set (cl-set pspv wrld state)get-induction-cands-from-cl-set659,28281 (defun pigeon-holep-apply (fn pigeon hole)pigeon-holep-apply694,29800 (defun pigeon-holep (pigeons holes filled-holes fn)pigeon-holep736,31588 (defun pigeon-holep1 (pigeon pigeons lst n holes filled-holes fn)pigeon-holep1755,32410 (defun flush-cand1-down-cand2 (cand1 cand2)flush-cand1-down-cand2776,33110 (defun flush-candidates (cand1 cand2)flush-candidates812,34945 (defun alists-agreep (alist1 alist2 vars)alists-agreep837,36181 (defun irreconcilable-alistsp (alist1 alist2)irreconcilable-alistsp850,36682 (defun affinity (aff alist1 alist2 vars)affinity868,37448 (defun member-affinity (aff alist alist-lst vars)member-affinity881,37921 (defun occur-affinity (aff alist lst vars)occur-affinity890,38209 (defun some-occur-affinity (aff alists lst vars)some-occur-affinity906,38779 (defun all-occur-affinity (aff alists lst vars)all-occur-affinity915,39113 (defun contains-affinity (aff lst vars)contains-affinity924,39446 (defun antagonistic-tests-and-alists-lstp (lst vars)antagonistic-tests-and-alists-lstp937,39933 (defun antagonistic-tests-and-alists-lstsp (lst1 lst2 vars)antagonistic-tests-and-alists-lstsp963,40864 (defun every-alist1-matedp (lst1 lst2 vars)every-alist1-matedp978,41349 (defun merge-alist1-into-alist2 (alist1 alist2 vars)merge-alist1-into-alist2999,42274 (defun merge-alist1-lst-into-alist2 (alist1-lst alist2 vars)merge-alist1-lst-into-alist21020,43207 (defun merge-lst1-into-alist2 (lst1 alist2 vars)merge-lst1-into-alist21034,43679 (defun merge-lst1-into-alist2-lst (lst1 alist2-lst vars)merge-lst1-into-alist2-lst1050,44156 (defun merge-lst1-into-lst2 (lst1 lst2 vars)merge-lst1-into-lst21055,44392 (defun merge-tests-and-alists-lsts (lst1 lst2 vars)merge-tests-and-alists-lsts1065,44803 (defun merge-cand1-into-cand2 (cand1 cand2)merge-cand1-into-cand21146,49374 (defun merge-candidates (cand1 cand2)merge-candidates1244,54737 (defun controller-variables1 (args controller-pocket)controller-variables11259,55207 (defun controller-variables (term controller-alist)controller-variables1274,55837 (defun induct-vars1 (lst wrld)induct-vars11288,56542 (defun induct-vars (cand wrld)induct-vars1302,56966 (defun vetoedp (cand vars lst changed-vars-flg)vetoedp1321,57713 (defun compute-vetoes1 (lst cand-lst wrld)compute-vetoes11345,58716 (defun compute-vetoes2 (lst cand-lst)compute-vetoes21367,59557 (defun compute-vetoes (cand-lst wrld)compute-vetoes1383,60108 (defun induction-complexity1 (lst wrld)induction-complexity11447,63447 (defun maximal-elements-apply (fn x wrld)maximal-elements-apply1465,64260 (defun maximal-elements1 (lst winners maximum fn wrld)maximal-elements11478,64700 (defun maximal-elements (lst fn wrld)maximal-elements1503,65735 (defun intersectp-eq/union-equal (x y)intersectp-eq/union-equal1527,66628 (defun equal/union-equal (x y)equal/union-equal1533,66816 (defun subsetp-equal/smaller (x y)subsetp-equal/smaller1539,66969 (defun m&m-apply (fn x y)m&m-apply1544,67087 (defun count-off (n lst)count-off1557,67604 (defun m&m-search (x y-lst del fn)m&m-search1565,67810 (defun m&m1 (pairs del ans n fn)m&m11583,68570 (defun m&m (bag fn)m&m1634,70709 (defun cons-subset-tree (x y)cons-subset-tree1719,75214 (defabbrev car-subset-tree (x)car-subset-tree1736,75638 (defabbrev cdr-subset-tree (x)cdr-subset-tree1742,75723 (defun or-subset-trees (tree1 tree2)or-subset-trees1748,75808 (defun m&m-over-powerset1 (st subset stree ans fn)m&m-over-powerset11760,76306 (defun m&m-over-powerset (st fn)m&m-over-powerset1786,77103 (defun all-picks2 (pocket pick ans)all-picks21895,82378 (defun all-picks2r (pocket pick ans)all-picks2r1901,82558 (defun all-picks1 (pocket picks ans rflg)all-picks11907,82747 (defun all-picks (pockets rflg)all-picks1916,83066 (defun dumb-negate-lit-lst-lst (cl-set)dumb-negate-lit-lst-lst1957,85038 (defun induction-hyp-clause-segments2 (alists cl ans)induction-hyp-clause-segments21969,85533 (defun induction-hyp-clause-segments1 (alists cl-set ans)induction-hyp-clause-segments11975,85778 (defun induction-hyp-clause-segments (alists cl-set)induction-hyp-clause-segments1987,86196 (defun induction-formula3 (neg-tests hyp-segments cl ans)induction-formula32023,87958 (defun induction-formula2 (cl cl-set ta-lst ans)induction-formula22066,89723 (defun induction-formula1 (lst cl-set ta-lst ans)induction-formula12095,90969 (defun induction-formula (cl-set ta-lst)induction-formula2111,91721 (defun all-picks-size (cl-set)all-picks-size2183,94929 (defun induction-formula-size1 (hyps-size concl-size ta-lst)induction-formula-size12190,95132 (defun induction-formula-size (cl-set ta-lst)induction-formula-size2206,95866 (defconst *maximum-induct-size* 100)*maximum-induct-size*2221,96462 (defun termify-clause-set (clauses)termify-clause-set2226,96584 (defun inform-simplify3 (alist terms ans)inform-simplify32243,97257 (defun inform-simplify2 (alists terms ans)inform-simplify22252,97576 (defun inform-simplify1 (ta-lst terms ans)inform-simplify12262,97864 (defun inform-simplify (ta-lst terms pspv)inform-simplify2276,98433 (defun all-vars1-lst-lst (lst ans)all-vars1-lst-lst2327,101319 (defun gen-new-name1 (char-lst wrld i)gen-new-name12336,101620 (defun gen-new-name (root wrld)gen-new-name2346,101945 (defun unmeasured-variables3 (vars alist)unmeasured-variables32354,102177 (defun unmeasured-variables2 (vars alists)unmeasured-variables22362,102489 (defun unmeasured-variables1 (vars ta-lst)unmeasured-variables12368,102719 (defun unmeasured-variables (measured-vars cand)unmeasured-variables2377,103131 (defun tilde-@-well-founded-relation-phrase (rel wrld)tilde-@-well-founded-relation-phrase2388,103620 (defun measured-variables (cand wrld)measured-variables2406,104367 (defun induct-msg/continue (pool-lstinduct-msg/continue2416,104756 (defun rec-fnnames (term wrld)rec-fnnames2597,113148 (defun rec-fnnames-lst (lst wrld)rec-fnnames-lst2608,113630 (defun induct-msg/lose (pool-name induct-hint-val state)induct-msg/lose2615,113801 (defun@par load-hint-settings-into-rcnst (hint-settings rcnst cl-id wrld ctxload-hint-settings-into-rcnst2677,116943 (defun update-hint-settings (new-hint-settings old-hint-settings)update-hint-settings2747,120320 (defun@par load-hint-settings-into-pspv (increment-flg hint-settings pspv cl-idload-hint-settings-into-pspv2762,120885 (defun restore-hint-settings-in-pspv (new-pspv old-pspv)restore-hint-settings-in-pspv2795,122351 (defun remove-trivial-clauses (clauses wrld)remove-trivial-clauses2834,124475 (defun non-standard-vector-check (vars accum)non-standard-vector-check2843,124755 (defun merge-ns-check (checks clause accum)merge-ns-check2851,125019 (defun trap-non-standard-vector-aux (cl-set accum-cl checks wrld)trap-non-standard-vector-aux2859,125302 (defun remove-adjacent-duplicates (lst)remove-adjacent-duplicates2873,125997 (defun non-standard-induction-vars (candidate wrld)non-standard-induction-vars2880,126272 (defun trap-non-standard-vector (cl-set candidate accum-cl wrld)trap-non-standard-vector2892,126696 (defun induct (forcing-round pool-lst cl-set hint-settings pspv wrld ctx state)induct2900,127058 (defun pair-vars-with-lits (cl)pair-vars-with-lits3091,135667 (defun ffnnames-subsetp (term lst)ffnnames-subsetp3104,136009 (defun ffnnames-subsetp-listp (terms lst)ffnnames-subsetp-listp3118,136513 (defun probably-not-validp (cl)probably-not-validp3128,136793 (defun irrelevant-lits (alist)irrelevant-lits3161,138369 (defun eliminate-irrelevance-clause (cl hist pspv wrld state)eliminate-irrelevance-clause3173,138852 (defun eliminate-irrelevance-clause-msg1 (signal clauses ttree pspv state)eliminate-irrelevance-clause-msg13203,140170 history-management.lisp,53405 (defrec goal-tree (children processor cl-id . fanout) nil)goal-tree37,1262 (deflabel proof-treeproof-tree95,4572 (deflabel proof-tree-examplesproof-tree-examples181,8507 (defun start-proof-tree-fn (remove-inhibit-p state)start-proof-tree-fn452,18636 (defmacro start-proof-tree ()start-proof-tree465,19018 (defmacro start-proof-tree ()start-proof-tree494,20059 (defmacro checkpoint-forced-goals (val)checkpoint-forced-goals504,20350 (defun stop-proof-tree-fn (state)stop-proof-tree-fn526,21134 (defmacro stop-proof-tree ()stop-proof-tree532,21345 (deflabel proof-tree-detailsproof-tree-details560,22404 (defun insert-into-goal-tree-rec (cl-id processor n goal-tree)insert-into-goal-tree-rec605,24607 (defun insert-into-goal-tree-lst (cl-id processor n goal-tree-lst)insert-into-goal-tree-lst613,24929 (defun insert-into-goal-tree (cl-id processor n goal-tree)insert-into-goal-tree637,25726 (defun set-difference-equal-changedp (l1 l2)set-difference-equal-changedp660,26743 (defun prune-goal-tree (forcing-round dead-clause-ids goal-tree)prune-goal-tree678,27394 (defun prune-goal-tree-lst (forcing-round dead-clause-ids goal-tree-lst)prune-goal-tree-lst765,31055 (defun prune-proof-tree (forcing-round dead-clause-ids proof-tree)prune-proof-tree782,31722 (defun print-string-repeat (increment level col channel state)print-string-repeat796,32340 (defconst *format-proc-alist**format-proc-alist*808,32743 (defun format-forced-subgoals (clause-ids col max-col channel state)format-forced-subgoals821,33141 (defun format-processor (col goal-tree channel state)format-processor850,34261 (defun format-goal-tree-lstformat-goal-tree-lst903,36276 (defun format-goal-tree (goal-tree level increment checkpointsformat-goal-tree935,37528 (defun format-proof-tree (proof-tree-rev increment checkpointsformat-proof-tree985,39680 (defun print-proof-tree1 (ctx channel state)print-proof-tree11009,40661 (defconst *proof-failure-string**proof-failure-string*1022,41221 (defun print-proof-tree-ctx (ctx channel state)print-proof-tree-ctx1025,41287 (defconst *proof-tree-start-delimiter* "#<\\<0")*proof-tree-start-delimiter*1050,42144 (defconst *proof-tree-end-delimiter* "#>\\>")*proof-tree-end-delimiter*1052,42194 (defun print-proof-tree-finish (state)print-proof-tree-finish1054,42241 (defun print-proof-tree (state)print-proof-tree1064,42669 (defun decorate-forced-goals-1 (goal-tree clause-id-list forced-clause-id)decorate-forced-goals-11089,43517 (defun decorate-forced-goals-1-lstdecorate-forced-goals-1-lst1106,44227 (defun decorate-forced-goals (forcing-round goal-tree clause-id-list-list n)decorate-forced-goals1131,45124 (defun decorate-forced-goals-in-proof-treedecorate-forced-goals-in-proof-tree1149,45764 (defun assumnote-list-to-clause-id-list (assumnote-list)assumnote-list-to-clause-id-list1167,46501 (defun assumnote-list-list-to-clause-id-list-list (assumnote-list-list)assumnote-list-list-to-clause-id-list-list1173,46723 (defun extend-proof-tree-for-forcing-roundextend-proof-tree-for-forcing-round1179,46994 (defun initialize-proof-tree1 (parent-clause-id x pool-lst forcing-round ctxinitialize-proof-tree11213,48216 (defun initialize-proof-tree (parent-clause-id x ctx state)initialize-proof-tree1251,49760 (defconst *star-1-clause-id**star-1-clause-id*1276,50788 (defun revert-goal-tree-rec (cl-id revertp goal-tree)revert-goal-tree-rec1285,50945 (defun revert-goal-tree-lst (or-p cl-id revertp goal-tree-lst)revert-goal-tree-lst1310,52080 (defun revert-goal-tree (cl-id revertp goal-tree)revert-goal-tree1337,53296 (defrec pool-element (tag clause-set . hint-settings) t)pool-element1358,54210 (defun pool-lst1 (pool n ans)pool-lst11360,54268 (defun pool-lst (pool)pool-lst1367,54522 (defun increment-proof-treeincrement-proof-tree1388,55371 (defun goal-tree-with-cl-id (cl-id goal-tree-lst)goal-tree-with-cl-id1477,59673 (defun goal-tree-choose-disjunct-rec (cl-id disjunct-cl-id goal-tree)goal-tree-choose-disjunct-rec1486,59949 (defun goal-tree-choose-disjunct-lst (cl-id disjunct-cl-id goal-tree-lst)goal-tree-choose-disjunct-lst1515,61177 (defun goal-tree-choose-disjunct (cl-id disjunct-cl-id goal-tree)goal-tree-choose-disjunct1530,61982 (defun install-disjunct-into-proof-tree (cl-id disjunct-cl-id state)install-disjunct-into-proof-tree1541,62419 (defun logical-namep (name wrld)logical-namep1599,65288 (defun logical-name-type-string (typ)logical-name-type-string1618,65979 (defun signature-fns (signatures)signature-fns1717,71018 (defun make-event-tuple (n d form ev-type namex symbol-class)make-event-tuple1732,71630 (defun access-event-tuple-number (x)access-event-tuple-number1784,74129 (defun access-event-tuple-depth (x)access-event-tuple-depth1792,74377 (defun access-event-tuple-type (x)access-event-tuple-type1795,74452 (defun access-event-tuple-namex (x)access-event-tuple-namex1804,74688 (defun access-event-tuple-form (x)access-event-tuple-form1827,75490 (defun access-event-tuple-symbol-class (x)access-event-tuple-symbol-class1832,75580 (defrec command-tuplecommand-tuple1856,76809 (defun make-command-tuple (n defun-mode form cbd last-make-event-expansion)make-command-tuple1873,77469 (defun access-command-tuple-number (x)access-command-tuple-number1891,78183 (defun access-command-tuple-defun-mode (x)access-command-tuple-defun-mode1894,78259 (defun access-command-tuple-form (x)access-command-tuple-form1900,78425 (defun safe-access-command-tuple-form (x)safe-access-command-tuple-form1910,78667 (defun access-command-tuple-last-make-event-expansion (x)access-command-tuple-last-make-event-expansion1923,79017 (defun access-command-tuple-cbd (x)access-command-tuple-cbd1926,79131 (defun max-absolute-event-number (wrld)max-absolute-event-number1931,79239 (defun next-absolute-event-number (wrld)next-absolute-event-number1941,79638 (defun max-absolute-command-number (wrld)max-absolute-command-number1944,79721 (defun next-absolute-command-number (wrld)next-absolute-command-number1952,80000 (defun scan-to-event (wrld)scan-to-event1957,80117 (defun scan-to-command (wrld)scan-to-command1968,80405 (defun scan-to-landmark-number (flg n wrld)scan-to-landmark-number1978,80667 (defun add-to-zap-table (val zt)add-to-zap-table2115,88260 (defun fetch-from-zap-table (n zt)fetch-from-zap-table2123,88502 (defconst *event-index-interval* 10)*event-index-interval*2142,89183 (defconst *command-index-interval* 10)*command-index-interval*2143,89220 (defun update-world-index (flg wrld)update-world-index2145,89260 (defun lookup-world-index1 (n interval index wrld)lookup-world-index12219,92801 (defun lookup-world-index (flg n wrld)lookup-world-index2241,93707 (defun store-absolute-event-number (namex n wrld boot-strap-flg)store-absolute-event-number2270,94959 (defun the-namex-symbol-class1 (lst wrld symbol-class1)the-namex-symbol-class12303,96187 (defun the-namex-symbol-class (namex wrld)the-namex-symbol-class2325,97459 (defun add-event-landmark (form ev-type namex wrld boot-strap-flg)add-event-landmark2333,97710 (defun scan-to-defpkg (name wrld)scan-to-defpkg2363,99108 (defun scan-to-include-book (full-book-name wrld)scan-to-include-book2378,99745 (defun assoc-equal-cadr (x alist)assoc-equal-cadr2392,100348 (defun multiple-assoc-terminal-substringp1 (x i alist)multiple-assoc-terminal-substringp12397,100508 (defun multiple-assoc-terminal-substringp (x alist)multiple-assoc-terminal-substringp2403,100817 (defun possibly-add-lisp-extension (str)possibly-add-lisp-extension2412,101207 (defun decode-logical-name (name wrld)decode-logical-name2428,101683 (defun er-decode-logical-name (name wrld ctx state)er-decode-logical-name2471,103230 (defun renew-lemmas (fn lemmas)renew-lemmas2503,104500 (defun renew-name/erase (name old-getprops wrld)renew-name/erase2514,104942 (defun renew-name/overwrite (name old-getprops wrld)renew-name/overwrite2540,105980 (defun renew-name (name renewal-mode wrld)renew-name2693,112019 (defun renew-names (names renewal-mode wrld)renew-names2736,114109 (defun collect-redefined (wrld ans)collect-redefined2742,114327 (defun scrunch-eq (lst)scrunch-eq2762,115120 (defun print-redefinition-warning (wrld ctx state)print-redefinition-warning2767,115289 (defun initialize-summary-accumulators (state)initialize-summary-accumulators2805,116935 (defun print-warnings-summary (state)print-warnings-summary2847,118723 (defun print-time-summary (state)print-time-summary2871,119542 (defun prover-steps (state)prover-steps2934,122101 (defun print-steps-summary (steps state)print-steps-summary2954,123001 (defun print-rules-summary (state)print-rules-summary2973,123768 (defun merge-cdr-> (l1 l2)merge-cdr->3003,124991 (defun merge-sort-cdr-> (l)merge-sort-cdr->3011,125237 (defconst *gag-prefix* "([ ")*gag-prefix*3016,125402 (defconst *gag-suffix* (msg "])~|"))*gag-suffix*3017,125432 (defun gag-start-msg (cl-id pool-name)gag-start-msg3019,125470 (defun print-gag-info (info state)print-gag-info3027,125711 (defun set-checkpoint-summary-limit-fn (val state)set-checkpoint-summary-limit-fn3038,126150 (defmacro set-checkpoint-summary-limit (val)set-checkpoint-summary-limit3057,126801 (defun print-gag-stack-rev (lst limit orig-limit msg chan state)print-gag-stack-rev3121,129188 (defun maybe-print-nil-goal-generated (gag-state chan state)maybe-print-nil-goal-generated3146,130370 (defun print-gag-state1 (gag-state state)print-gag-state13154,130692 (defun erase-gag-state (state)erase-gag-state3242,134467 (defun print-gag-state (state)print-gag-state3252,134920 (defun clause-id-is-top-level (cl-id)clause-id-is-top-level3260,135151 (defun clause-id-is-induction-round (cl-id)clause-id-is-induction-round3265,135309 (defun clause-id-is-forcing-round (cl-id)clause-id-is-forcing-round3270,135466 (defun print-acl2p-checkpoints1 (checkpoints top-level-banner-printedprint-acl2p-checkpoints13278,135664 (deflock *acl2p-checkpoint-saving-lock*)*acl2p-checkpoint-saving-lock*3322,137783 (defun erase-acl2p-checkpoints-for-summary (state)erase-acl2p-checkpoints-for-summary3325,137836 (defun print-acl2p-checkpoints (state)print-acl2p-checkpoints3330,137997 (defun character-alistp (x)character-alistp3361,139478 (defun tilde-@p (arg)tilde-@p3376,139940 (defun print-failure (erp ctx state)print-failure3383,140109 (defstub initialize-event-user (ctx qbody state) state)initialize-event-user3400,140846 (defstub finalize-event-user (ctx qbody state) state)finalize-event-user3402,140903 (defdoc initialize-event-userinitialize-event-user3404,140958 (defdoc finalize-event-userfinalize-event-user3472,143507 (defun lmi-seed (lmi)lmi-seed3598,148259 (defun lmi-techs (lmi)lmi-techs3616,148954 (defun lmi-seed-lst (lmi-lst)lmi-seed-lst3626,149258 (defun lmi-techs-lst (lmi-lst)lmi-techs-lst3631,149428 (defun filter-atoms (flg lst)filter-atoms3636,149597 (defun print-runes-summary (ttree channel state)print-runes-summary3646,149882 (defun use-names-in-ttree (ttree)use-names-in-ttree3659,150260 (defun by-names-in-ttree (ttree)by-names-in-ttree3666,150527 (defrec clause-processor-hintclause-processor-hint3673,150779 (defun clause-processor-fns (cl-proc-hints)clause-processor-fns3677,150850 (defun cl-proc-names-in-ttree (ttree)cl-proc-names-in-ttree3684,151149 (defun print-hint-events-summary (ttree channel state)print-hint-events-summary3690,151387 (defun print-splitter-rules-summary (cl-id clauses ttree channel state)print-splitter-rules-summary3713,152465 (defun print-rules-and-hint-events-summary (state)print-rules-and-hint-events-summary3772,155071 (defun last-prover-steps (state)last-prover-steps3800,156416 (defun print-summary (erp noop-flg ctx state)print-summary3854,158784 (defun with-prover-step-limit-fn (limit form no-change-flg)with-prover-step-limit-fn4014,165599 (defmacro with-prover-step-limit (limit formwith-prover-step-limit4109,169384 (defmacro with-prover-step-limit (limit formwith-prover-step-limit4268,176854 (defmacro with-prover-step-limit! (limit form &optional no-change-flg)with-prover-step-limit!4275,177129 (defrec proved-functional-instances-alist-entryproved-functional-instances-alist-entry4291,177781 (defun supply-name-for-proved-functional-instances-alist-entry (name lst)supply-name-for-proved-functional-instances-alist-entry4347,180684 (defun proved-functional-instances-from-tagged-objects (name lst)proved-functional-instances-from-tagged-objects4354,180994 (defun add-command-landmark (defun-mode form cbd last-make-event-expansionadd-command-landmark4593,189413 (defun find-longest-common-retraction1 (wrld1 wrld2)find-longest-common-retraction14629,191173 (defun find-longest-common-retraction1-event (wrld1 wrld2)find-longest-common-retraction1-event4635,191395 (defun find-longest-common-retraction (event-p wrld1 wrld2)find-longest-common-retraction4641,191619 (defun install-global-enabled-structure (wrld state)install-global-enabled-structure4662,192468 (defvar *defattach-fns*)*defattach-fns*4698,194179 (defun set-w (flg wrld state)set-w4700,194205 (defun set-w! (wrld state)set-w!4777,197641 (defmacro save-event-state-globals (form)save-event-state-globals4887,203123 (defun attachment-alist (fn wrld)attachment-alist4905,203705 (defun attachment-pair (fn wrld)attachment-pair4914,204089 (defconst *protected-system-state-globals**protected-system-state-globals*4920,204313 (defun state-global-bindings (names)state-global-bindings4974,207737 (defmacro protect-system-state-globals (form)protect-system-state-globals4980,207936 (defun formal-value-triple (erp val)formal-value-triple4991,208285 (defun formal-value-triple@par (erp val)formal-value-triple@par5002,208581 (defun@par translate-simple-or-error-triple (uform ctx wrld state)translate-simple-or-error-triple5009,208738 (defun xtrans-eval (uterm alist trans-flg ev-flg ctx state aok)xtrans-eval5098,213169 (defun xtrans-eval-with-ev-w (uterm alist trans-flg ev-flg ctx state aok)xtrans-eval-with-ev-w5190,217006 (defun xtrans-eval@par (uterm alist trans-flg ev-flg ctx state aok)xtrans-eval@par5266,219906 (defmacro xtrans-eval-state-fn-attachment (form ctx)xtrans-eval-state-fn-attachment5269,220045 (defmacro with-ctx-summarized (ctx body)with-ctx-summarized5303,221477 (defmacro revert-world-on-error (form)revert-world-on-error5390,225782 (defun@par chk-theory-expr-value1 (lst wrld expr macro-aliases ctx state)chk-theory-expr-value15408,226563 (defun@par chk-theory-expr-value (lst wrld expr ctx state)chk-theory-expr-value5436,227918 (defun theory-fn-translated-callp (x)theory-fn-translated-callp5444,228267 (defun eval-theory-expr (expr ctx wrld state)eval-theory-expr5463,228867 (defun eval-theory-expr@par (expr ctx wrld state)eval-theory-expr@par5498,230066 (defun append-strip-cdrs (x y)append-strip-cdrs5540,231611 (defun no-rune-based-on (runes symbols)no-rune-based-on5547,231768 (defun revappend-delete-runes-based-on-symbols1 (runes symbols ans)revappend-delete-runes-based-on-symbols15553,231956 (defun revappend-delete-runes-based-on-symbols (runes symbols ans)revappend-delete-runes-based-on-symbols5565,232510 (defun current-theory1 (lst ans redefined)current-theory15586,233449 (defun first-n-ac-rev (i l ac)first-n-ac-rev5624,235256 (defun longest-common-tail-length-rec (old new acc)longest-common-tail-length-rec5640,235813 (defun longest-common-tail-length (old new)longest-common-tail-length5654,236380 (defun extend-current-theory (old-th new-th old-aug-th wrld)extend-current-theory5663,236728 (defun update-current-theory (theory0 wrld)update-current-theory5698,238212 (defun put-cltl-command (cltl-cmd wrld wrld0)put-cltl-command5722,239429 (defun strip-non-nil-base-symbols (runes acc)strip-non-nil-base-symbols5739,240198 (defun install-proof-supporters (namex ttree wrld)install-proof-supporters5747,240462 (defun install-event (val form ev-type namex ttree cltl-cmdinstall-event5785,242297 (deflabel redundant-eventsredundant-events6001,254263 (defun stop-redundant-event-fn (ctx state extra-msg)stop-redundant-event-fn6301,269461 (defmacro stop-redundant-event (ctx state &optional extra-msg)stop-redundant-event6332,270602 (defrec command-number-baseline-infocommand-number-baseline-info6368,272247 (defun absolute-to-relative-command-number (n wrld)absolute-to-relative-command-number6372,272327 (defun relative-to-absolute-command-number (n wrld)relative-to-absolute-command-number6377,272514 (defun normalize-absolute-command-number (n wrld)normalize-absolute-command-number6382,272701 (defun tree-occur (x y)tree-occur6432,274631 (defun cd-form-matchp (pat form)cd-form-matchp6441,274820 (defun cd-some-event-matchp (pat wrld)cd-some-event-matchp6454,275196 (defun cd-search (pat earliestp start-wrld end-wrld)cd-search6473,275976 (defun superior-command-world (wrld1 wrld ctx state)superior-command-world6514,277780 (defun er-decode-cd (cd wrld ctx state)er-decode-cd6545,279268 (defrec ldd-statusldd-status6732,288481 (defun make-ldd-flags (class markp status fullp)make-ldd-flags6736,288619 (defun make-ldd (class markp status n fullp form)make-ldd6749,289296 (defun access-ldd-class (ldd) (caaar ldd))access-ldd-class6766,289981 (defun access-ldd-markp (ldd) (cdaar ldd))access-ldd-markp6767,290025 (defun access-ldd-status (ldd) (cadar ldd))access-ldd-status6768,290069 (defun access-ldd-fullp (ldd) (cddar ldd))access-ldd-fullp6769,290113 (defun access-ldd-n (ldd) (cadr ldd))access-ldd-n6770,290157 (defun access-ldd-form (ldd) (cddr ldd))access-ldd-form6771,290200 (defun big-d-little-d-name1 (lst ens ans)big-d-little-d-name16773,290244 (defun big-d-little-d-name (name ens wrld)big-d-little-d-name6787,290868 (defun big-d-little-d-clique1 (names ens wrld ans)big-d-little-d-clique16801,291510 (defun big-d-little-d-clique (names ens wrld)big-d-little-d-clique6814,292060 (defun big-d-little-d-event (ev-tuple ens wrld)big-d-little-d-event6825,292519 (defun big-d-little-d-command-block (wrld1 ens wrld s)big-d-little-d-command-block6851,293735 (defun big-m-little-m-name (name wrld)big-m-little-m-name6894,295745 (defun big-m-little-m-clique1 (names wrld ans)big-m-little-m-clique16907,296180 (defun big-m-little-m-clique (names wrld)big-m-little-m-clique6920,296672 (defun big-m-little-m-event (ev-tuple wrld)big-m-little-m-event6930,297040 (defun big-m-little-m-command-block (wrld1 wrld s)big-m-little-m-command-block6944,297574 (defun symbol-class-char (symbol-class)symbol-class-char6972,298648 (defun defun-mode-string (defun-mode)defun-mode-string6983,298964 (defun big-c-little-c-event (ev-tuple wrld)big-c-little-c-event6991,299218 (defun big-c-little-c-command-block (wrld1 wrld s)big-c-little-c-command-block7018,300487 (defun print-ldd-full-or-sketch/mutual-recursion (lst)print-ldd-full-or-sketch/mutual-recursion7056,302287 (defun print-ldd-full-or-sketch/encapsulate (lst)print-ldd-full-or-sketch/encapsulate7065,302595 (defun normalize-char (c hyphen-is-spacep)normalize-char7078,303121 (defun normalize-string1 (str hyphen-is-spacep j ans)normalize-string17084,303277 (defun normalize-string (str hyphen-is-spacep)normalize-string7096,303811 (defun string-matchp (pat-lst str j jmax normp skippingp)string-matchp7106,304216 (defun string-search1 (pat-lst str j j-max normp)string-search17132,305204 (defun string-search (pat str normp)string-search7139,305431 (defun doc-stringp (str)doc-stringp7156,306022 (defconst *zapped-doc-string**zapped-doc-string*7173,306522 (defun zap-doc-string-from-event-form/all-but-last (lst)zap-doc-string-from-event-form/all-but-last7176,306591 (defun zap-doc-string-from-event-form/second-arg (form)zap-doc-string-from-event-form/second-arg7185,306950 (defun zap-doc-string-from-event-form/third-arg (form)zap-doc-string-from-event-form/third-arg7195,307218 (defun zap-doc-string-from-event-form/mutual-recursion (lst)zap-doc-string-from-event-form/mutual-recursion7206,307542 (defun zap-doc-string-from-event-form/doc-keyword (lst)zap-doc-string-from-event-form/doc-keyword7211,307783 (defun zap-doc-string-from-event-form (form)zap-doc-string-from-event-form7232,308749 (defun print-ldd-full-or-sketch (fullp form)print-ldd-full-or-sketch7251,309530 (defmacro with-base-10 (form)with-base-107300,311320 (defun print-ldd-formula-column (state)print-ldd-formula-column7330,312644 (defun print-ldd (ldd channel state)print-ldd7335,312788 (defun print-ldds (ldds channel state)print-ldds7392,315020 (defun make-command-ldd (markp fullp cmd-wrld ens wrld)make-command-ldd7423,316575 (defun make-event-ldd (markp indent fullp ev-tuple ens wrld)make-event-ldd7442,317408 (defun make-ldds-command-sequence (cmd-wrld1 cmd2 ens wrld markp ans)make-ldds-command-sequence7457,317962 (defun make-ldds-command-block1 (wrld1 cmd-ldd indent fullp super-stk ens wrldmake-ldds-command-block17481,319089 (defun make-ldds-command-block (cmd-wrld ens wrld fullp ans)make-ldds-command-block7552,321505 (defun pcb-pcb!-fn (cd fullp state)pcb-pcb!-fn7570,322290 (defun pcb!-fn (cd state)pcb!-fn7583,322753 (defun pcb-fn (cd state)pcb-fn7586,322808 (defmacro pcb! (cd)pcb!7589,322864 (defun pc-fn (cd state)pc-fn7609,323495 (defmacro pc (cd)pc7621,323904 (defun pcs-fn (cd1 cd2 markp state)pcs-fn7753,330037 (defmacro pcs (cd1 cd2)pcs7808,332611 (defun get-command-sequence-fn1 (cmd-wrld1 cmd2 ans)get-command-sequence-fn17833,333554 (defun get-command-sequence-fn (cd1 cd2 state)get-command-sequence-fn7846,333976 (defmacro get-command-sequence (cd1 cd2)get-command-sequence7871,335052 (defmacro gcs (cd1 cd2)gcs7895,335925 (defmacro pbt (cd1)pbt7898,335987 (defmacro pcb (cd)pcb7922,336880 (defun print-indented-list-msg (objects indent final-string)print-indented-list-msg7953,338358 (defun print-indented-list (objects indent last-col channel evisc-tuple state)print-indented-list7972,339007 (defun print-book-path (book-path indent channel state)print-book-path7979,339293 (defun pe-fn1 (wrld channel ev-wrld cmd-wrld state)pe-fn17994,339803 (defun pe-fn2 (logical-name wrld channel ev-wrld state)pe-fn28024,340850 (defun pe-fn (logical-name state)pe-fn8034,341388 (defmacro pe (logical-name)pe8092,343959 (defmacro pe! (logical-name)pe!8140,345794 (defun command-block-names1 (wrld ans symbol-classes)command-block-names18155,346197 (defun command-block-names (wrld symbol-classes)command-block-names8190,347892 (defun symbol-name-lst (lst)symbol-name-lst8199,348265 (defun acl2-query-simulate-interaction (msg alist controlledp ans state)acl2-query-simulate-interaction8204,348410 (defun acl2-query1 (id qt alist state)acl2-query18222,349122 (defun acl2-query (id qt alist state)acl2-query8275,351238 (defun collect-names-in-defun-modes (names defun-modes wrld)collect-names-in-defun-modes8325,353576 (defun ubt-ubu-query (kwd wrld1 wrld0 seen kept-commands wrld state banger)ubt-ubu-query8336,354019 (defmacro ubt (cd)ubt8459,360909 (defmacro ubt! (cd)ubt!8502,362870 (defmacro ubu (cd)ubu8520,363410 (defmacro ubu! (cd)ubu!8551,364624 (defmacro u nilu8570,365199 (defun chk-virgin (name new-type wrld)chk-virgin8592,365880 (deflabel namename8604,366140 (defun chk-boot-strap-redefineable-namep (name ctx wrld state)chk-boot-strap-redefineable-namep8651,368367 (defun maybe-coerce-overwrite-to-erase (old-type new-type mode)maybe-coerce-overwrite-to-erase8667,369078 (defun redefinition-renewal-mode (name old-type new-type reclassifyingp ctxredefinition-renewal-mode8673,369256 (defun redefined-names1 (wrld ans)redefined-names19010,385981 (defun redefined-names (state)redefined-names9020,386359 (defun chk-redefineable-namep (name new-type reclassifyingp ctx wrld state)chk-redefineable-namep9071,388236 (defun chk-just-new-name (name new-type reclassifyingp ctx w state)chk-just-new-name9130,390790 (defun no-new-namesp (lst wrld)no-new-namesp9182,393148 (defun chk-just-new-names (names new-type reclassifyingp ctx w state)chk-just-new-names9191,393368 (defconst *return-character* (code-char 13))*return-character*9217,394586 (defun read-symbol-from-string1 (str i len ans)read-symbol-from-string19219,394632 (defun read-symbol-from-string2 (str i len ans)read-symbol-from-string29234,395196 (defun read-symbol-from-string (str i pkg-witness)read-symbol-from-string9243,395527 (defun scan-past-newline (str i maximum)scan-past-newline9294,398087 (defun scan-past-newlines (str i maximum)scan-past-newlines9301,398303 (defun scan-past-tilde-slash (str i maximum)scan-past-tilde-slash9308,398516 (defun scan-to-doc-string-part1 (parti str i maximum)scan-to-doc-string-part19320,399081 (defun scan-to-doc-string-part (i str)scan-to-doc-string-part9331,399384 (defun get-one-liner-as-string1 (str i j acc)get-one-liner-as-string19350,400146 (defun get-one-liner-as-string (str)get-one-liner-as-string9355,400320 (defun read-doc-string-citations1 (name str i)read-doc-string-citations19363,400603 (defun read-doc-string-citations (name str)read-doc-string-citations9377,401111 (defun doc-topicp (name wrld)doc-topicp9393,401880 (defun ignore-doc-string-error (wrld)ignore-doc-string-error9396,401972 (defmacro er-doc (ctx str &rest str-args)er-doc9400,402113 (defun chk-doc-string-citations (str citations wrld)chk-doc-string-citations9414,402749 (defun chk-well-formed-doc-string (name doc ctx state)chk-well-formed-doc-string9446,404314 (defun translate-doc (name doc ctx state)translate-doc9540,408901 (defun translate-doc-lst (names docs ctx state)translate-doc-lst9562,409820 (defun get-cites (citations)get-cites9569,410098 (defun alpha-< (x y)alpha-<9580,410435 (defun merge-alpha-< (l1 l2)merge-alpha-<9600,411178 (defun merge-sort-alpha-< (l)merge-sort-alpha-<9607,411403 (defun update-alpha-<-alist (key val alist)update-alpha-<-alist9612,411578 (defun put-cited-bys (name citations alist)put-cited-bys9624,412028 (defun update-doc-database (name doc pair wrld)update-doc-database9649,412969 (defun update-doc-database-lst (names docs pairs wrld)update-doc-database-lst9674,414047 (defun putprop-unless (sym key val exception wrld)putprop-unless9682,414316 (defun redefined-warning (redef ctx state)redefined-warning9690,414578 (defun get-event (name wrld)get-event9703,415036 (defun redundant-labelp (name event-form wrld)redundant-labelp9716,415392 (defun deflabel-fn (name state doc event-form)deflabel-fn9726,415756 (defun defdoc-fn (name state doc event-form)defdoc-fn9775,417566 (defmacro defdoc (&whole event-form name doc) ;See notedefdoc9825,419365 (defun access-doc-string-database (name state)access-doc-string-database9897,422715 (defun get-doc-string (name state)get-doc-string9930,423864 (defun get-doc-string-de-indent1 (str i)get-doc-string-de-indent19937,424046 (defun get-doc-string-de-indent (str)get-doc-string-de-indent9941,424187 (defun use-doc-string-de-indent (d str i maximum)use-doc-string-de-indent9953,424703 (defun doc-prefix (state)doc-prefix9964,425023 (defun princ-prefix (prefix channel state)princ-prefix9969,425144 (defun length-prefix (prefix)length-prefix9975,425386 (defun save-more-doc-state (str i maximum de-indent prefix state)save-more-doc-state9979,425511 (defun doc-char-subst-table-p (x)doc-char-subst-table-p9988,425900 (defun set-doc-char-subst-table (x state)set-doc-char-subst-table10001,426195 (defun doc-char-subst-table (state)doc-char-subst-table10011,426616 (defun doc-fmt-alist (state)doc-fmt-alist10017,426741 (defconst *terminal-markup-table**terminal-markup-table*10020,426810 (defun doc-markup-table (state)doc-markup-table10149,433554 (defun doc-scan-past-tilde-key (name orig-position posn str maximum acc state)doc-scan-past-tilde-key10154,433723 (defun doc-scan-past-tilde-argdoc-scan-past-tilde-arg10182,434989 (defun doc-scan-past-tildedoc-scan-past-tilde10254,437825 (defun assoc-char-alist-stringp (char-alist str len)assoc-char-alist-stringp10292,439582 (defun apply-char-subst-table1 (char-lst acc char-subst-table)apply-char-subst-table110302,439895 (defun apply-char-subst-table (s char-subst-table spack)apply-char-subst-table10323,440788 (defun read-pointer-and-text1 (lst pacc sacc)read-pointer-and-text110350,441843 (defun read-pointer-and-text2 (lst acc)read-pointer-and-text210374,442929 (defun read-pointer-and-text-raw (str)read-pointer-and-text-raw10383,443261 (defun posn-char-stringp (chr str i)posn-char-stringp10396,443672 (defun replace-colons (p)replace-colons10406,443896 (defun read-pointer-and-text (str bar-sep-p)read-pointer-and-text10419,444315 (defun lookup-fmt-alist (str flag fmt-alist char-subst-table bar-sep-p)lookup-fmt-alist10427,444542 (defun bar-sep-p (state)bar-sep-p10517,448684 (defun char-to-string-alistp (lst)char-to-string-alistp10521,448793 (defun missing-fmt-alist-chars1 (str char-to-tilde-s-string-alist fmt-alist)missing-fmt-alist-chars110532,449123 (defun missing-fmt-alist-chars (str fmt-alist)missing-fmt-alist-chars10553,450008 (defun complete-fmt-alist (topic-name fmt-alist undocumented-filecomplete-fmt-alist10579,451227 (defmacro mv-to-state (n form)mv-to-state10606,452457 (defun print-par-entry (entry fmt-alist char-subst-table channel state)print-par-entry10620,452885 (defun print-doc-string-part1 (str i maximum de-indent prefixprint-doc-string-part110628,453139 (defun print-doc-string-part-mvprint-doc-string-part-mv10943,469591 (defun print-doc-string-partprint-doc-string-part11040,474219 (defun get-doc-section (section alist)get-doc-section11049,474560 (defmacro pstate-global-let* (bindings body)pstate-global-let*11057,474855 (defun print-doc (name n prefixprint-doc11071,475283 (defun print-doc-lst (lst prefixprint-doc-lst11164,479291 (defun degree-of-match2 (ch1 ch2 str i maximum)degree-of-match211181,479970 (defun degree-of-match1 (pat-lst str maximum)degree-of-match111189,480269 (defun degree-of-match (pat-lst str)degree-of-match11195,480515 (defun find-likely-near-misses (pat-lst alist)find-likely-near-misses11209,481052 (defun print-doc-dwim (name ctx state)print-doc-dwim11227,481867 (defun end-doc (channel state)end-doc11244,482486 (defun doc-fn (name state)doc-fn11258,482950 (defun more-fn (ln state)more-fn11314,485481 (defun doc!-fn (name state)doc!-fn11338,486534 (defmacro more nilmore11364,487731 (defmacro more! nilmore!11412,489806 (defun print-doc-outlineprint-doc-outline11437,490657 (defun print-doc-outline-lst (name-lst prefixprint-doc-outline-lst11467,491782 (deflabel finding-documentationfinding-documentation11481,492511 (deflabel markupmarkup11506,493682 (deflabel doc-stringdoc-string11789,505147 (deflabel print-doc-start-columnprint-doc-start-column12021,517849 (defmacro doc (name)doc12043,518737 (defmacro doc! (name)doc!12108,521731 (defun more-doc-fn (name state)more-doc-fn12126,522149 (defmacro more-doc (name)more-doc12145,522924 (defun get-doc-section-symbols (alist ans)get-doc-section-symbols12171,523902 (defun get-docs-apropos1 (pat-lst alist ans)get-docs-apropos112176,524095 (defun get-docs-apropos (pat alist)get-docs-apropos12182,524370 (defun docs-fn (x state)docs-fn12185,524475 (defmacro docs (x)docs12240,526641 (defun print-top-doc-topics (doc-alist channel state)print-top-doc-topics12289,528657 (defun help-fn (state)help-fn12301,529114 (deflabel qq12359,531677 (defmacro help nilhelp12381,532476 (deflabel logical-namelogical-name12396,532719 (deflabel commandcommand12471,536262 (deflabel command-descriptorcommand-descriptor12497,537299 (defun trans-fn (form state)trans-fn12585,541680 (defun trans!-fn (form state)trans!-fn12612,542884 (defmacro trans (form)trans12633,543731 (defmacro trans! (form)trans!12679,545141 (defun trans1-fn (form state)trans1-fn12696,545610 (defmacro trans1 (form)trans112706,545997 (defun tilde-*-props-fn-phrase1 (alist)tilde-*-props-fn-phrase112726,546515 (defun tilde-*-props-fn-phrase (alist)tilde-*-props-fn-phrase12733,546751 (defun props-fn (sym state)props-fn12737,546867 (defmacro props (sym)props12758,547641 (deflabel enter-boot-strap-modeenter-boot-strap-mode12773,547938 (deflabel exit-boot-strap-modeexit-boot-strap-mode12804,549387 (defun walkabout-nth (i x)walkabout-nth12828,550351 (defun walkabout-ip (i x)walkabout-ip12851,551174 (defun walkabout-huh (state)walkabout-huh12862,551494 (defun walkabout1 (i x state intern-flg evisc-tuple alt-evisc-tuple)walkabout112867,551647 (defun walkabout (x state)walkabout12966,555822 (defun walkabout=-fn (var state)walkabout=-fn13081,558865 (defmacro walkabout= (var)walkabout=13086,559009 (defun lookup-bddnote (cl-id bddnotes)lookup-bddnote13091,559140 (defun update-bddnote-with-term (cl-id term bddnotes)update-bddnote-with-term13098,559337 (defmacro show-bdd (&optional strshow-bdd13111,559815 (defun show-bdd-goal (query-response bddnote chan state)show-bdd-goal13200,563669 (defun merge-car-term-order (l1 l2)merge-car-term-order13251,565826 (defun merge-sort-car-term-order (l)merge-sort-car-term-order13258,566087 (defun falsifying-pair-p (term val asst)falsifying-pair-p13263,566297 (defun bogus-falsifying-assignment-var (asst)bogus-falsifying-assignment-var13272,566594 (defun show-falsifying-assignment (query-response bddnote chan state)show-falsifying-assignment13283,566846 (defun show-bdd-term (query-response bddnote chan state)show-bdd-term13359,570480 (defun tilde-*-substitution-phrase1 (alist is-replaced-by-str evisc-tuple wrld)tilde-*-substitution-phrase113428,573417 (defun tilde-*-substitution-phrase (alist is-replaced-by-str evisc-tuple wrld)tilde-*-substitution-phrase13439,573949 (defun show-bdd-backtrace (call-stack cst-array chan state)show-bdd-backtrace13445,574202 (defun show-bdd-fn (str goal-query-responseshow-bdd-fn13474,575266 (defun get-docs (lst)get-docs13536,578226 (defun get-guards2 (edcls targets wrld acc)get-guards213549,578664 (defun get-guards1 (edcls targets wrld)get-guards113633,582343 (defun get-guards (lst split-types-lst split-types-p wrld)get-guards13636,582424 (defun get-guardsp (lst wrld)get-guardsp13681,584419 (defconst *no-measure**no-measure*13699,585287 (defun get-measures1 (m edcls ctx state)get-measures113702,585320 (defun get-measures2 (lst ctx state)get-measures213733,586851 (defun get-measures (symbol-class lst ctx state)get-measures13741,587154 (defconst *no-ruler-extenders**no-ruler-extenders*13757,587784 (defconst *basic-ruler-extenders**basic-ruler-extenders*13760,587825 (defun get-ruler-extenders1 (r edcls default ctx wrld state)get-ruler-extenders113763,587886 (defun get-ruler-extenders2 (lst default ctx wrld state)get-ruler-extenders213808,589981 (defmacro default-ruler-extenders-from-table (alist)default-ruler-extenders-from-table13817,590410 (defun default-ruler-extenders (wrld)default-ruler-extenders13823,590606 (defun get-ruler-extenders-lst (symbol-class lst ctx state)get-ruler-extenders-lst13842,591446 (defun get-hints1 (edcls)get-hints113860,592245 (defun get-hints (lst)get-hints13887,593162 (defun get-guard-hints1 (edcls)get-guard-hints113897,593474 (defun get-guard-hints (lst)get-guard-hints13925,594453 (defun get-std-hints1 (edcls)get-std-hints113936,594814 (defun get-std-hints (lst)get-std-hints13965,595776 (defun get-normalizep (edcls ans ctx state)get-normalizep13975,596104 (defun get-normalizeps (lst acc ctx state)get-normalizeps14014,597504 (defconst *unspecified-xarg-value**unspecified-xarg-value*14025,598001 (defun get-unambiguous-xargs-flg1/edcls1 (key v edcls event-msg)get-unambiguous-xargs-flg1/edcls114032,598143 (defun get-unambiguous-xargs-flg1/edcls (key v edcls event-msg ctx state)get-unambiguous-xargs-flg1/edcls14065,599861 (defun get-unambiguous-xargs-flg1 (key lst event-msg ctx state)get-unambiguous-xargs-flg114076,600249 (defun get-unambiguous-xargs-flg (key lst default ctx state)get-unambiguous-xargs-flg14094,601190 (defun get-unambiguous-xargs-flg-lst (key lst default ctx state)get-unambiguous-xargs-flg-lst14123,602722 (defun chk-xargs-keywords1 (edcls keywords ctx state)chk-xargs-keywords114144,603747 (defun chk-xargs-keywords (lst keywords ctx state)chk-xargs-keywords14161,604593 (defun get-names (lst)get-names14182,605419 (defun get-bodies (lst)get-bodies14187,605539 (defun find-nontrivial-rulers (var term)find-nontrivial-rulers14194,605687 (defun find-nontrivial-rulers-lst (var termlist flg)find-nontrivial-rulers-lst14219,606729 (defun tilde-@-free-vars-phrase (vars term wrld)tilde-@-free-vars-phrase14228,607062 (defun chk-free-vars (name formals term loc-str ctx state)chk-free-vars14255,608398 (defun chk-declared-ignores (name ignores term loc-str ctx state)chk-declared-ignores14274,609238 (defun chk-free-and-ignored-vars (name formals guard split-types-term measurechk-free-and-ignored-vars14286,609755 (defun chk-free-and-ignored-vars-lsts (names arglists guards split-types-termschk-free-and-ignored-vars-lsts14343,612735 (defun putprop-x-lst1 (symbols key value wrld)putprop-x-lst114380,614924 (defun putprop-x-lst2 (symbols key vals wrld)putprop-x-lst214390,615232 (defun putprop-x-lst2-unless (symbols key vals exception wrld)putprop-x-lst2-unless14401,615578 (defun@par translate-term-lst (terms stobjs-out logic-modep known-stobjs-lsttranslate-term-lst14417,616285 (defun find-named-lemma (sym lst top-level)find-named-lemma14513,620942 (defun find-runed-lemma (rune lst)find-runed-lemma14530,621604 (defun free-varsp-member (term vars)free-varsp-member14543,621953 (defun free-varsp-member-lst (args vars)free-varsp-member-lst14551,622203 (defun@par translate-expand-term1 (name form free-vars ctx wrld state)translate-expand-term114558,622384 (defun@par translate-expand-term (x ctx wrld state)translate-expand-term14683,628178 (defun@par translate-expand-hint1 (arg acc ctx wrld state)translate-expand-hint114711,629317 (defun@par translate-expand-hint (arg ctx wrld state)translate-expand-hint14724,629872 (defun cons-all-to-lst (new-members lst)cons-all-to-lst14778,632007 (defun@par translate-substitution (substn ctx wrld state)translate-substitution14783,632189 (defun@par translate-substitution-lst (substn-lst ctx wrld state)translate-substitution-lst14832,634502 (defun get-rewrite-and-defn-runes-from-runic-mapping-pairs (pairs)get-rewrite-and-defn-runes-from-runic-mapping-pairs14842,634854 (defun@par translate-restrict-hint (arg ctx wrld state)translate-restrict-hint14851,635196 (defconst *do-not-processes**do-not-processes*14900,637286 (defun coerce-to-process-name-lst (lst)coerce-to-process-name-lst14904,637422 (defun coerce-to-acl2-package-lst (lst)coerce-to-acl2-package-lst14911,637661 (defun@par chk-do-not-expr-value (lst expr ctx state)chk-do-not-expr-value14918,637874 (defun@par translate-do-not-hint (expr ctx state)translate-do-not-hint14946,639159 (defun@par translate-do-not-induct-hint (arg ctx wrld state)translate-do-not-induct-hint14987,640730 (defun@par translate-hands-off-hint1 (arg ctx wrld state)translate-hands-off-hint115013,642051 (defun@par translate-hands-off-hint (arg ctx wrld state)translate-hands-off-hint15053,643589 (defun truncated-class (rune mapping-pairs classes)truncated-class15075,644357 (defun tests-and-alists-lst-from-fn (fn wrld)tests-and-alists-lst-from-fn15089,645052 (defun corollary (rune wrld)corollary15110,646071 (defun formula (name normalp wrld)formula15191,649551 (defun pf-fn (name state)pf-fn15212,650458 (defmacro pf (name)pf15247,651896 (defun merge-symbol-< (l1 l2 acc)merge-symbol-<15268,652537 (defun merge-sort-symbol-< (l)merge-sort-symbol-<15275,652810 (defconst *non-instantiable-primitives**non-instantiable-primitives*15283,653073 (defun instantiablep (fn wrld)instantiablep15323,654722 (defun all-ffn-symbs (term ans)all-ffn-symbs15361,656398 (defun all-ffn-symbs-lst (lst ans)all-ffn-symbs-lst15371,656793 (defconst *unknown-constraints**unknown-constraints*15378,656961 (defun constraint-info (fn wrld)constraint-info15386,657184 (defdoc constraintconstraint15453,660158 (defun@par chk-equal-arities (fn1 n1 fn2 n2 ctx state)chk-equal-arities15904,680294 (defun extend-sorted-symbol-alist (pair alist)extend-sorted-symbol-alist15924,680904 (defun@par chk-equiv-classicalp (fn1 fn2 termp ctx wrld state)chk-equiv-classicalp15938,681271 (defun@par translate-functional-substitution (substn ctx wrld state)translate-functional-substitution15957,682076 (defun sublis-fn-rec (alist term bound-vars allow-freevars-p)sublis-fn-rec16079,687797 (defun sublis-fn-rec-lst (alist terms bound-vars allow-freevars-p)sublis-fn-rec-lst16197,694000 (defun sublis-fn (alist term bound-vars)sublis-fn16212,694566 (defun sublis-fn-simple (alist term)sublis-fn-simple16219,694780 (defun sublis-fn-lst-simple (alist termlist)sublis-fn-lst-simple16229,695103 (defun instantiable-ffn-symbs (term wrld ans ignore-fns)instantiable-ffn-symbs16240,695384 (defun instantiable-ffn-symbs-lst (lst wrld ans ignore-fns)instantiable-ffn-symbs-lst16267,696386 (defun unknown-constraint-supporters (fn wrld)unknown-constraint-supporters16278,696780 (defun collect-instantiablep1 (fns wrld ignore-fns)collect-instantiablep116298,697683 (defun all-instantiablep (fns wrld)all-instantiablep16309,698060 (defun collect-instantiablep (fns wrld ignore-fns)collect-instantiablep16315,698223 (defun immediate-instantiable-ancestors (fn wrld ignore-fns)immediate-instantiable-ancestors16324,698485 (defun instantiable-ancestors (fns wrld ans)instantiable-ancestors16369,700746 (defun hitp (term alist)hitp16388,701408 (defun hitp-lst (terms alist)hitp-lst16401,701850 (defun event-responsible-for-proved-constraint (name alistevent-responsible-for-proved-constraint16408,701999 (defun getprop-x-lst (symbols prop wrld)getprop-x-lst16453,704255 (defun filter-hitps (lst alist ans)filter-hitps16459,704488 (defun relevant-constraints1 (names alist proved-fnl-insts-alist constraintsrelevant-constraints116466,704681 (defun relevant-constraints1-axioms (names alist proved-fnl-insts-alistrelevant-constraints1-axioms16632,712842 (defun relevant-constraints (thm alist proved-fnl-insts-alist wrld)relevant-constraints16682,715351 (defun bound-vars (term ans)bound-vars16733,717604 (defun bound-vars-lst (terms ans)bound-vars-lst16744,718008 (defun@par translate-lmi/instance (formula constraints event-names new-entriestranslate-lmi/instance16752,718169 (defun@par translate-lmi/functional-instance (formula constraints event-namestranslate-lmi/functional-instance16793,720260 (defun@par translate-lmi (lmi normalizep ctx wrld state)translate-lmi16890,725249 (deflabel functional-instantiation-in-acl2rfunctional-instantiation-in-acl2r16997,729843 (deflabel lemma-instancelemma-instance17020,730897 (defun@par translate-use-hint1 (arg ctx wrld state)translate-use-hint117129,736975 (defun@par translate-use-hint (arg ctx wrld state)translate-use-hint17152,738114 (defun convert-name-tree-to-new-name1 (name-tree char-lst sym)convert-name-tree-to-new-name117264,743596 (defun convert-name-tree-to-new-name (name-tree wrld)convert-name-tree-to-new-name17291,744944 (defun@par translate-by-hint (name-tree arg ctx wrld state)translate-by-hint17313,746001 (defun@par translate-cases-hint (arg ctx wrld state)translate-cases-hint17381,749086 (defun@par translate-case-split-limitations-hint (arg ctx wrld state)translate-case-split-limitations-hint17396,749574 (defun@par translate-no-op-hint (arg ctx wrld state)translate-no-op-hint17420,750522 (defun@par translate-error-hint (arg ctx wrld state)translate-error-hint17426,750670 (defun@par translate-induct-hint (arg ctx wrld state)translate-induct-hint17435,750963 (defconst *built-in-executable-counterparts**built-in-executable-counterparts*17447,751440 (defconst *s-prop-theory**s-prop-theory*17467,752106 (defconst *definition-minimal-theory**definition-minimal-theory*17480,752604 (defdoc theories-and-primitivestheories-and-primitives17488,752906 (defun translate-in-theory-hinttranslate-in-theory-hint17596,756698 (defun translate-in-theory-hint@partranslate-in-theory-hint@par17663,759762 (defun all-function-symbolps (fns wrld)all-function-symbolps17742,763585 (defun non-function-symbols (lst wrld)non-function-symbols17748,763808 (defun collect-non-logic-mode (alist wrld)collect-non-logic-mode17755,764050 (defun@par translate-bdd-hint1 (top-arg rest ctx wrld state)translate-bdd-hint117763,764361 (defun@par translate-bdd-hint (arg ctx wrld state)translate-bdd-hint17849,768392 (defun@par translate-nonlinearp-hint (arg ctx wrld state)translate-nonlinearp-hint17867,769026 (defun@par translate-backchain-limit-rw-hint (arg ctx wrld state)translate-backchain-limit-rw-hint17879,769367 (defun@par translate-no-thanks-hint (arg ctx wrld state)translate-no-thanks-hint17889,769692 (defun@par translate-reorder-hint (arg ctx wrld state)translate-reorder-hint17895,769842 (defun arity-mismatch-msg (sym expected-arity wrld)arity-mismatch-msg17907,770207 (defun@par translate-clause-processor-hint (form ctx wrld state)translate-clause-processor-hint17938,771509 (defun@par translate-custom-keyword-hint (arg uterm2 ctx wrld state)translate-custom-keyword-hint18059,777364 (defun custom-keyword-hint (key wrld)custom-keyword-hint18115,780028 (defun remove-all-no-ops (key-val-lst)remove-all-no-ops18126,780362 (defun remove-redundant-no-ops (key-val-lst)remove-redundant-no-ops18134,780667 (defun find-first-custom-keyword-hint (user-hints wrld)find-first-custom-keyword-hint18153,781264 (defconst *custom-keyword-max-iterations**custom-keyword-max-iterations*18173,782029 (defun@par custom-keyword-hint-interpreter1custom-keyword-hint-interpreter118176,782079 (defun@par custom-keyword-hint-interpretercustom-keyword-hint-interpreter18350,789612 (defun custom-keyword-hint-in-computed-hint-form (computed-hint-tuple)custom-keyword-hint-in-computed-hint-form18381,791058 (defun@par put-cl-id-of-custom-keyword-hint-in-computed-hint-formput-cl-id-of-custom-keyword-hint-in-computed-hint-form18420,792919 (defun make-disjunctive-clause-id (cl-id i pkg-name)make-disjunctive-clause-id18448,794093 (defun make-disjunctive-goal-spec (str i pkg-name)make-disjunctive-goal-spec18456,794384 (defun minimally-well-formed-or-hintp (val)minimally-well-formed-or-hintp18461,794570 (defun split-keyword-alist (key keyword-alist)split-keyword-alist18469,794840 (defun distribute-other-hints-into-or1 (pre x post)distribute-other-hints-into-or118480,795278 (defun distribute-other-hints-into-or (keyword-alist)distribute-other-hints-into-or18485,795469 (defconst *hint-expression-basic-vars**hint-expression-basic-vars*18506,796217 (defconst *hint-expression-override-vars**hint-expression-override-vars*18509,796328 (defconst *hint-expression-backtrack-vars**hint-expression-backtrack-vars*18512,796425 (defconst *hint-expression-all-vars**hint-expression-all-vars*18517,796610 (defun@par translate-hint-expression (name-tree term hint-type ctx wrld state)translate-hint-expression18522,796816 (defun@par translate-backtrack-hint (name-tree arg ctx wrld state)translate-backtrack-hint18709,804205 (defun@par translate-rw-cache-state-hint (arg ctx wrld state)translate-rw-cache-state-hint18712,804348 (defun@par translate-or-hint (name-tree str arg ctx wrld state)translate-or-hint18723,804690 (defun@par translate-hint-settings (name-tree str key-val-lst ctx wrld state)translate-hint-settings18775,806956 (defun@par translate-x-hint-value (name-tree str x arg ctx wrld state)translate-x-hint-value18809,808293 (defun replace-goal-spec-in-name-tree1 (name-tree goal-spec)replace-goal-spec-in-name-tree118883,810918 (defun replace-goal-spec-in-name-tree (name-tree goal-spec)replace-goal-spec-in-name-tree18903,811659 (defun@par translate-hint (name-tree pair hint-type ctx wrld state)translate-hint18925,812656 (defun@par translate-hint-expressions (name-tree terms hint-type ctx wrld state)translate-hint-expressions19264,829727 (defun@par check-translated-override-hint (hint uhint ctx state)check-translated-override-hint19284,830597 (defun@par translate-hints1 (name-tree lst hint-type override-hints ctx wrld state)translate-hints119295,831045 (defun@par warn-on-duplicate-hint-goal-specs (lst seen ctx state)warn-on-duplicate-hint-goal-specs19382,836056 (defun@par translate-hints2 (name-tree lst hint-type override-hints ctx wrld state)translate-hints219403,837258 (defun override-hints (wrld)override-hints19415,837907 (defun@par translate-hints (name-tree lst ctx wrld state)translate-hints19559,845629 (defun@par translate-hints+1 (name-tree lst default-hints ctx wrld state)translate-hints+119563,845793 (defun translate-hints+ (name-tree lst default-hints ctx wrld state)translate-hints+19574,846167 (defun translate-override-hints (name-tree lst ctx wrld state)translate-override-hints19583,846551 (defun@par apply-override-hint1apply-override-hint119598,847162 (defun@par apply-override-hintapply-override-hint19711,851722 (defun@par apply-override-hintsapply-override-hints19734,852952 (defun@par eval-and-translate-hint-expressioneval-and-translate-hint-expression19757,853735 (deflabel goal-specgoal-spec20007,865666 (deflabel hints-and-the-waterfallhints-and-the-waterfall20114,869993 (deflabel hintshints20317,881905 (deflabel clause-identifierclause-identifier21059,917791 (deflabel computed-hintscomputed-hints21112,919841 (deflabel using-computed-hints-1using-computed-hints-121265,928142 (deflabel using-computed-hints-2using-computed-hints-221316,929363 (deflabel using-computed-hints-3using-computed-hints-321421,933601 (deflabel using-computed-hints-4using-computed-hints-421541,938576 (deflabel using-computed-hints-5using-computed-hints-521686,944690 (deflabel using-computed-hints-6using-computed-hints-621750,947051 (deflabel using-computed-hints-7using-computed-hints-722006,957230 (deflabel using-computed-hints-8using-computed-hints-822237,966461 (deflabel using-computed-hintsusing-computed-hints22279,968357 (defmacro ttags-seen ()ttags-seen22385,972920 (defrec certify-book-infocertify-book-info22443,975452 (defun active-book-name (wrld state)active-book-name22447,975547 (defrec deferred-ttag-notedeferred-ttag-note22458,976029 (defun fms-to-standard-co (str alist state evisc-tuple)fms-to-standard-co22462,976103 (defun print-ttag-note (val active-book-name include-bookp deferred-p state)print-ttag-note22469,976338 (defun show-ttag-notes1 (notes state)show-ttag-notes122524,978991 (defun show-ttag-notes-fn (state)show-ttag-notes-fn22536,979492 (defmacro show-ttag-notes ()show-ttag-notes22553,980291 (defun set-deferred-ttag-notes (val state)set-deferred-ttag-notes22557,980391 (defun ttags-from-deferred-ttag-notes1 (notes)ttags-from-deferred-ttag-notes122644,984045 (defun ttags-from-deferred-ttag-notes (notes)ttags-from-deferred-ttag-notes22654,984422 (defun print-deferred-ttag-notes-summary (state)print-deferred-ttag-notes-summary22657,984522 (defun notify-on-defttag (val active-book-name include-bookp state)notify-on-defttag22674,985296 (defun ttag-allowed-p (ttag ttags active-book-name acc)ttag-allowed-p22711,986823 (defun chk-acceptable-ttag1 (val active-book-name ttags-allowed ttags-seenchk-acceptable-ttag122735,987811 (defun chk-acceptable-ttag (val include-bookp ctx wrld state)chk-acceptable-ttag22801,991130 (defun chk-acceptable-ttags2 (ttag filenames ttags-allowed ttags-seenchk-acceptable-ttags222816,991614 (defun chk-acceptable-ttags1 (vals active-book-name ttags-allowed ttags-seenchk-acceptable-ttags122832,992555 (defun chk-acceptable-ttags (vals include-bookp ctx wrld state)chk-acceptable-ttags22861,994247 (defun chk-table-nil-args (op bad-arg bad-argn ctx state)chk-table-nil-args22877,994992 (defun chk-table-guard (name key val ctx wrld state)chk-table-guard22889,995397 (defun chk-table-guards-rec (name alist ctx pair wrld state)chk-table-guards-rec22931,997197 (defun chk-table-guards (name alist ctx wrld state)chk-table-guards22945,997889 (defun put-assoc-equal-fast (name val alist)put-assoc-equal-fast22958,998513 (defun global-set? (var val wrld old-val)global-set?22970,998934 (defun cltl-def-from-name2 (fn stobj-function axiomatic-p wrld)cltl-def-from-name222975,999046 (defrec absstobj-infoabsstobj-info22998,999967 (defun cltl-def-from-name1 (fn stobj-function axiomatic-p wrld)cltl-def-from-name123002,1000023 (defun cltl-def-from-name (fn wrld)cltl-def-from-name23027,1001137 (defun table-cltl-cmd (name key val op ctx wrld)table-cltl-cmd23039,1001599 (defun table-fn1 (name key val op term ctx wrld state event-form)table-fn123126,1006047 (defun table-fn (name args state event-form)table-fn23315,1015077 (defun set-override-hints-fn (lst at-end ctx wrld state)set-override-hints-fn23417,1019494 prove.lisp,21496 (defun abbreviationp1 (lambda-flg vars term2)abbreviationp137,1383 (defun abbreviationp1-lst (lambda-flg vars lst)abbreviationp1-lst55,2103 (defun abbreviationp (lambda-flg vars term2)abbreviationp63,2362 (defun all-vars-bag (term ans)all-vars-bag76,2858 (defun all-vars-bag-lst (lst ans)all-vars-bag-lst81,3012 (defun find-abbreviation-lemma (term geneqv lemmas ens wrld)find-abbreviation-lemma87,3175 (defun expand-abbreviations-with-lemma (term geneqvexpand-abbreviations-with-lemma122,4780 (defun expand-abbreviations (term alist geneqv fns-to-be-ignored-by-rewriteexpand-abbreviations150,5859 (defun expand-abbreviations-lst (lst alist geneqv-lstexpand-abbreviations-lst453,20221 (defun and-orp (term bool)and-orp473,21210 (defun find-and-or-lemma (term bool lemmas ens wrld)find-and-or-lemma484,21540 (defun expand-and-or (term bool fns-to-be-ignored-by-rewrite ens wrld stateexpand-and-or516,23118 (defun clausify-input1 (term bool fns-to-be-ignored-by-rewrite ens wrld stateclausify-input1623,28010 (defun clausify-input1-lst (lst fns-to-be-ignored-by-rewrite ens wrld stateclausify-input1-lst696,31558 (defun clausify-input (term fns-to-be-ignored-by-rewrite ens wrld state ttreeclausify-input716,32544 (defun expand-some-non-rec-fns-in-clauses (fns clauses wrld)expand-some-non-rec-fns-in-clauses742,33915 (defun no-op-histp (hist)no-op-histp761,34708 (defun expand-any-final-implies1 (term wrld)expand-any-final-implies1797,36294 (defun expand-any-final-implies1-lst (term-lst wrld)expand-any-final-implies1-lst829,37541 (defun expand-any-final-implies (cl wrld)expand-any-final-implies838,37783 (defun rw-cache-state (wrld)rw-cache-state861,38637 (defmacro make-rcnst (ens wrld &rest args)make-rcnst866,38807 (defun cheap-type-alist-and-pot-lst (cl ens wrld state)cheap-type-alist-and-pot-lst897,40189 (defconst *tau-ttree**tau-ttree*933,42001 (defun tau-clausep (clause ens wrld state calist)tau-clausep938,42132 (defun tau-clausep-lst-rec (clauses ens wrld ans ttree state calist)tau-clausep-lst-rec968,43160 (defun tau-clausep-lst (clauses ens wrld ans ttree state calist)tau-clausep-lst995,44156 (defun preprocess-clause (cl hist pspv wrld state step-limit)preprocess-clause1007,44567 (defun tilde-*-preprocess-phrase (ttree)tilde-*-preprocess-phrase1208,55184 (defun tilde-*-raw-preprocess-phrase (ttree punct)tilde-*-raw-preprocess-phrase1234,56087 (defun preprocess-clause-msg1 (signal clauses ttree pspv state)preprocess-clause-msg11259,56913 (defun more-than-simplifiedp (hist)more-than-simplifiedp1317,59440 (defun delete-assoc-eq-lst (lst alist)delete-assoc-eq-lst1337,60301 (defun delete-assumptions-1 (recs only-immediatep)delete-assumptions-11345,60572 (defun delete-assumptions (ttree only-immediatep)delete-assumptions1369,61677 (defun save-and-print-acl2p-checkpoint (cl-id prettyified-clausesave-and-print-acl2p-checkpoint1389,62381 (defun find-the-first-checkpoint (h checkpoint-processors)find-the-first-checkpoint1453,65470 (defun acl2p-push-clause-printing (cl hist pspv wrld state)acl2p-push-clause-printing1479,66541 (defun@par push-clause (cl hist pspv wrld state)push-clause1520,68260 (defun push-clause-msg1-abort (cl-id ttree pspv state)push-clause-msg1-abort1870,83741 (defun push-clause-msg1 (cl-id signal clauses ttree pspv state)push-clause-msg11910,85411 (deflabel otf-flgotf-flg1934,86335 (defun clause-set-subsumes-1 (init-subsumes-count cl-set1 cl-set2 acc)clause-set-subsumes-11978,88178 (defun clause-set-subsumes (init-subsumes-count cl-set1 cl-set2)clause-set-subsumes1992,88921 (defun preprocess-clause? (cl hist pspv wrld state step-limit)preprocess-clause?2005,89390 (defun apply-use-hint-clauses (temp clauses pspv wrld state step-limit)apply-use-hint-clauses2012,89736 (defun apply-cases-hint-clause (temp cl pspv wrld)apply-cases-hint-clause2125,94886 (defun term-list-listp (l w)term-list-listp2161,96157 (defun non-term-listp-msg (x w)non-term-listp-msg2168,96323 (defun non-term-list-listp-msg (l w)non-term-list-listp-msg2185,96769 (defun eval-clause-processor (clause term stobjs-out ctx state)eval-clause-processor2203,97242 (defun eval-clause-processor@par (clause term stobjs-out ctx state)eval-clause-processor@par2264,99856 (defun apply-top-hints-clause1 (temp cl-id cl pspv wrld state step-limit)apply-top-hints-clause12339,103130 (defun@par apply-top-hints-clause (cl-id cl hist pspv wrld ctx state step-limit)apply-top-hints-clause2622,115943 (defun tilde-@-lmi-phrase (lmi-lst k event-names)tilde-@-lmi-phrase2715,120691 (defun or-hit-msg (gag-mode-only-p cl-id ttree)or-hit-msg2792,124085 (defun apply-top-hints-clause-msg1apply-top-hints-clause-msg12815,125265 (defun previous-process-was-speciousp (hist)previous-process-was-speciousp3022,135320 (defconst *preprocess-clause-ledge**preprocess-clause-ledge*3120,140330 (defmacro initialize-pspv-for-gag-mode (pspv)initialize-pspv-for-gag-mode3151,141576 (defun waterfall-update-gag-state (cl-id clause proc signal ttree pspvwaterfall-update-gag-state3226,144790 (defun waterfall-update-gag-state@par (cl-id clause proc signal ttree pspv state)waterfall-update-gag-state@par3406,153957 (defun@par record-gag-state (gag-state state)record-gag-state3415,154291 (defun@par gag-state-exiting-cl-id (signal cl-id pspv state)gag-state-exiting-cl-id3421,154478 (defun remove-pool-lst-from-gag-state (pool-lst gag-state state)remove-pool-lst-from-gag-state3496,158438 (defun pop-clause-update-gag-state-pop (pool-lsts gag-state msgs msg-p state)pop-clause-update-gag-state-pop3550,160853 (defun gag-mode-jppl-flg (gag-state)gag-mode-jppl-flg3571,161530 (defmacro splitter-output ()splitter-output3592,162366 (defdoc splittersplitter3608,163056 (defmacro set-splitter-output (val)set-splitter-output3764,170386 (defun waterfall-msg1waterfall-msg13798,172019 (defmacro io?-prove-cw (vars body &rest keyword-args)io?-prove-cw3859,174232 (defmacro io?-prove@par (&rest rst)io?-prove@par3871,174513 (defun waterfall-print-clause-body (cl-id clause state)waterfall-print-clause-body3878,174672 (defmacro waterfall-print-clause-id-fmt1-call (cl-id)waterfall-print-clause-id-fmt1-call3892,175140 (defmacro waterfall-print-clause-id-fmt1-call@par (cl-id)waterfall-print-clause-id-fmt1-call@par3905,175516 (defmacro waterfall-print-clause-id (cl-id)waterfall-print-clause-id3918,175914 (defmacro waterfall-print-clause-id@par (cl-id)waterfall-print-clause-id@par3925,176112 (defproxy print-clause-id-okp (*) => *)print-clause-id-okp3966,178163 (defun print-clause-id-okp-builtin (cl-id)print-clause-id-okp-builtin3968,178204 (defun@par waterfall-print-clause (suppress-print cl-id clause state)waterfall-print-clause3976,178406 (defun some-parent-is-checkpointp (hist state)some-parent-is-checkpointp3997,179285 (defun@par waterfall-msgwaterfall-msg4005,179564 (defun put-ttree-into-pspv (ttree pspv)put-ttree-into-pspv4093,183584 (defun set-cl-ids-of-assumptions1 (recs cl-id)set-cl-ids-of-assumptions14098,183774 (defun set-cl-ids-of-assumptions (ttree cl-id)set-cl-ids-of-assumptions4108,184243 (defun collect-assumptions1 (recs only-immediatep ans)collect-assumptions14127,185130 (defun collect-assumptions (ttree only-immediatep)collect-assumptions4145,185851 (defun sublis-var-lst-lst (alist clauses)sublis-var-lst-lst4169,186990 (defun add-segments-to-clause (clause segments)add-segments-to-clause4174,187178 (defun split-initial-extra-info-lits (cl hyps-rev)split-initial-extra-info-lits4180,187413 (defun conjoin-clause-to-clause-set-extra-info1 (tags-rev cl0 cl cl-setconjoin-clause-to-clause-set-extra-info14187,187686 (defun conjoin-clause-to-clause-set-extra-info (cl cl-set)conjoin-clause-to-clause-set-extra-info4233,189795 (defun conjoin-clause-sets-extra-info (cl-set1 cl-set2)conjoin-clause-sets-extra-info4253,190866 (defun maybe-add-extra-info-lit (debug-info term clause wrld)maybe-add-extra-info-lit4270,191669 (defun conjoin-clause-sets+ (debug-info cl-set1 cl-set2)conjoin-clause-sets+4279,192026 (defconst *equality-aliases**equality-aliases*4283,192206 (defun term-equated-to-constant (term)term-equated-to-constant4290,192410 (defun simplify-clause-for-term-equal-const-1 (var const cl)simplify-clause-for-term-equal-const-14301,192738 (defun simplify-clause-for-term-equal-const (var const cl)simplify-clause-for-term-equal-const4321,193648 (defun add-literal-smart (lit cl at-end-flg)add-literal-smart4330,193903 (defun guard-clauses (term debug-info stobj-optp clause wrld ttree)guard-clauses4349,194554 (defun guard-clauses-lst (lst debug-info stobj-optp clause wrld ttree)guard-clauses-lst4599,205948 (defun linked-variables1 (vars direct-links changedp direct-links0)linked-variables14614,206451 (defun linked-variables (vars direct-links)linked-variables4631,207171 (defun contains-constrained-constantp (term wrld)contains-constrained-constantp4692,210541 (defun contains-constrained-constantp-lst (lst wrld)contains-constrained-constantp-lst4706,211185 (defun disvar-type-alist1 (vars type-alist wrld)disvar-type-alist14714,211466 (defun collect-all-vars (lst)collect-all-vars4722,211840 (defun disvar-type-alist (type-alist term wrld)disvar-type-alist4726,211967 (defun unencumber-type-alist (type-alist term rewrittenp wrld)unencumber-type-alist4746,212831 (defun unencumber-assumption (assn wrld)unencumber-assumption4789,214837 (defun unencumber-assumptions (assumptions wrld ans)unencumber-assumptions4859,217065 (defun dumb-type-alist-implicationp1 (type-alist1 type-alist2 seen)dumb-type-alist-implicationp14896,218652 (defun dumb-type-alist-implicationp2 (type-alist1 type-alist2)dumb-type-alist-implicationp24908,219283 (defun dumb-type-alist-implicationp (type-alist1 type-alist2)dumb-type-alist-implicationp4914,219563 (defun partition-according-to-assumption-term (assumptions alist)partition-according-to-assumption-term4978,222941 (defun exists-assumption-with-weaker-type-alist (assumption assumptions i)exists-assumption-with-weaker-type-alist5000,223832 (defun add-assumption-with-weak-type-alist (assumption assumptions ans)add-assumption-with-weak-type-alist5018,224619 (defun dumb-keep-assumptions-with-weakest-type-alists (assumptions kept)dumb-keep-assumptions-with-weakest-type-alists5043,225744 (defun dumb-assumption-subsumption1 (partitions ans)dumb-assumption-subsumption15078,227260 (defun dumb-assumption-subsumption (assumptions)dumb-assumption-subsumption5094,227786 (defun clausify-type-alist (type-alist cl ens w seen ttree)clausify-type-alist5111,228592 (defun clausify-assumption (assumption ens wrld)clausify-assumption5138,229831 (defun clausify-assumptions (assumptions ens wrld pairs ttree)clausify-assumptions5153,230232 (defun strip-assumption-terms (lst)strip-assumption-terms5171,230953 (defun add-splitters-to-ttree1 (assumnotes tag ttree)add-splitters-to-ttree15179,231212 (defun add-splitters-to-ttree (immediatep tag assumptions ttree)add-splitters-to-ttree5188,231526 (defun maybe-add-splitters-to-ttree (splitter-output immediatep tagmaybe-add-splitters-to-ttree5202,232006 (defun extract-and-clausify-assumptions (cl ttree only-immediatep ens wrldextract-and-clausify-assumptions5208,232260 (defun@par waterfall-step1 (processor cl-id clause hist pspv wrld statewaterfall-step15333,238754 (defun@par process-backtrack-hint (cl-id clause clauses processor new-histprocess-backtrack-hint5368,239889 (defun set-rw-cache-state-in-pspv (pspv val)set-rw-cache-state-in-pspv5409,241803 (defun maybe-set-rw-cache-state-disabled (pspv)maybe-set-rw-cache-state-disabled5417,242115 (defun maybe-set-rw-cache-state-enabled (pspv)maybe-set-rw-cache-state-enabled5425,242397 (defun accumulate-rw-cache-into-pspv (processor ttree pspv)accumulate-rw-cache-into-pspv5433,242678 (defun erase-rw-cache-from-pspv (pspv)erase-rw-cache-from-pspv5463,244154 (defconst *simplify-clause-ledge**simplify-clause-ledge*5484,245048 (defconst *simplify-clause-ledge-complement**simplify-clause-ledge-complement*5487,245141 (defun@par waterfall-step-cleanup (processor cl-id clause hist wrld statewaterfall-step-cleanup5491,245281 (defun@par waterfall-step (processor cl-id clause hist pspv wrld ctx statewaterfall-step5651,252201 (defun@par find-applicable-hint-settings1find-applicable-hint-settings15823,260845 (defun@par find-applicable-hint-settings (cl-id clause hist pspv ctx hints wrldfind-applicable-hint-settings6006,270100 (defun@par thanks-for-the-hint (goal-already-printed-p hint-settings state)thanks-for-the-hint6020,270789 (defun lmi-name-or-rune (lmi)lmi-name-or-rune6088,273813 (defun enabled-lmi-names1 (ens pairs)enabled-lmi-names16101,274213 (defun enabled-lmi-names (ens lmi-lst wrld)enabled-lmi-names6116,274755 (defdoc using-enabled-rulesusing-enabled-rules6137,275463 (defun@par maybe-warn-for-use-hint (pspv cl-id ctx wrld state)maybe-warn-for-use-hint6212,278479 (defun@par maybe-warn-about-theory-simple (ens1 ens2 ctx wrld state)maybe-warn-about-theory-simple6235,279328 (defun@par maybe-warn-about-theory-from-rcnsts (rcnst1 rcnst2 ctx ens wrldmaybe-warn-about-theory-from-rcnsts6246,279841 (defun waterfall-or-hit-msg-a (cl-id user-hinti d-cl-id i branch-cnt state)waterfall-or-hit-msg-a6292,281931 (defun waterfall-or-hit-msg-b (cl-id d-cl-id branch-cnt state)waterfall-or-hit-msg-b6328,283263 (defun tilde-*-or-hit-summary-phrase1 (summary)tilde-*-or-hit-summary-phrase16348,284149 (defun tilde-*-or-hit-summary-phrase (summary)tilde-*-or-hit-summary-phrase6361,284622 (defun waterfall-or-hit-msg-c (parent-cl-id results revert-d-cl-id cl-id summarywaterfall-or-hit-msg-c6374,285055 (defun term-difficulty1 (term wrld n)term-difficulty16443,288179 (defun term-difficulty1-lst (lst wrld n)term-difficulty1-lst6457,288782 (defun term-difficulty (term wrld)term-difficulty6463,288970 (defun clause-difficulty (cl wrld)clause-difficulty6471,289315 (defun clause-set-difficulty (cl-set wrld)clause-set-difficulty6476,289433 (defun pool-difficulty (element0 pool wrld)pool-difficulty6485,289793 (defun how-many-to-be-proved (element0 pool)how-many-to-be-proved6496,290196 (defun pick-best-pspv-for-waterfall0-or-hit1pick-best-pspv-for-waterfall0-or-hit16508,290590 (defun pick-best-pspv-for-waterfall0-or-hit (results pspv0 wrld)pick-best-pspv-for-waterfall0-or-hit6561,293194 (defun change-or-hit-history-entry (i hist cl-id)change-or-hit-history-entry6595,294606 (defun@par pair-cl-id-with-hint-setting (cl-id hint-settings)pair-cl-id-with-hint-setting6647,296722 (defun apply-reorder-hint-front (indices len clauses acc)apply-reorder-hint-front6680,298405 (defun apply-reorder-hint-back (indices current-index clauses acc)apply-reorder-hint-back6687,298649 (defun filter-> (lst max)filter->6694,299006 (defun@par apply-reorder-hint (pspv clauses ctx state)apply-reorder-hint6701,299191 (defun pspv-equal-except-for-tag-tree-and-pool (x y)pspv-equal-except-for-tag-tree-and-pool6732,300543 (defun list-extensionp-aux (rev-base rev-extension)list-extensionp-aux6764,302020 (defun list-extensionp (base extension)list-extensionp6775,302348 (defun find-list-extensions (base extension acc)find-list-extensions6784,302571 (defun combine-pspv-pools (base x y debug-pspv)combine-pspv-pools6791,302790 (defun combine-pspv-tag-trees (x y)combine-pspv-tag-trees6802,303101 (defun print-pspvs (base x y debug-pspv)print-pspvs6811,303402 (defun combine-prove-spec-vars (base x y ctx debug-pspv signal1 signal2)combine-prove-spec-vars6820,303593 (defun speculative-execution-valid (x y)speculative-execution-valid6876,305567 (defun abort-will-occur-in-pool (pool)abort-will-occur-in-pool6891,306107 (defrec maybe-to-be-proved-by-inductionmaybe-to-be-proved-by-induction6933,308269 (defun convert-maybes-to-tobe-subgoals (pool)convert-maybes-to-tobe-subgoals6943,308546 (defun convert-maybes-to-tobes (pool)convert-maybes-to-tobes6958,309155 (defun convert-maybes-to-tobes-in-pspv (pspv)convert-maybes-to-tobes-in-pspv7026,312151 (defun erase-rw-cache-any-tag-from-pspv (pspv)erase-rw-cache-any-tag-from-pspv7035,312496 (defun restore-rw-cache-state-in-pspv (new-pspv old-pspv)restore-rw-cache-state-in-pspv7046,312862 (defvar *waterfall-parallelism-timings-ht-alist* nil*waterfall-parallelism-timings-ht-alist*7060,313616 (defvar *waterfall-parallelism-timings-ht* nil*waterfall-parallelism-timings-ht*7066,313910 (defun setup-waterfall-parallelism-ht-for-name (name)setup-waterfall-parallelism-ht-for-name7074,314296 (defun clear-current-waterfall-parallelism-ht ()clear-current-waterfall-parallelism-ht7098,315460 (defun flush-waterfall-parallelism-hashtables ()flush-waterfall-parallelism-hashtables7104,315593 (defun save-waterfall-timings-for-cl-id (key value)save-waterfall-timings-for-cl-id7112,315821 (defun lookup-waterfall-timings-for-cl-id (key)lookup-waterfall-timings-for-cl-id7119,316041 (defmacro waterfall1-wrapper (form)waterfall1-wrapper7127,316323 (defparameter *acl2p-starting-proof-time* 0.0d0)*acl2p-starting-proof-time*7138,316778 (defun waterfall1-wrapper@par-before (cl-id state)waterfall1-wrapper@par-before7141,316839 (defun waterfall1-wrapper@par-after (cl-id start-time state)waterfall1-wrapper@par-after7178,318379 (defmacro waterfall1-wrapper@par (&rest form)waterfall1-wrapper@par7203,319373 (defun increment-waterfall-parallelism-counter (abbreviated-symbol)increment-waterfall-parallelism-counter7216,319826 (defun halves-with-length (clauses)halves-with-length7241,320597 (defun@par waterfall1waterfall17256,321112 (defun@par waterfall0-with-hint-settingswaterfall0-with-hint-settings7337,324697 (defun@par waterfall0 (ledge cl-id clause hist pspv hints ens wrld ctx statewaterfall07380,326518 (defun@par waterfall0-or-hit (ledge cl-id clause hist pspv hints ens wrld ctxwaterfall0-or-hit7685,339184 (defun waterfall1-lst (n parent-cl-id clauses hist pspv jppl-flgwaterfall1-lst7898,348783 (defun waterfall1-lst@par-serial (n parent-cl-id clauses hist pspv jppl-flgwaterfall1-lst@par-serial7973,351650 (defun waterfall1-tree@par-pseudo-parallel (n parent-cl-id clauses hist pspvwaterfall1-tree@par-pseudo-parallel8037,354388 (defun waterfall1-tree@par-parallel (n parent-cl-id clauses hist pspv jppl-flgwaterfall1-tree@par-parallel8202,362384 (defun waterfall1-lst@par (n parent-cl-id clauses hist pspv jppl-flgwaterfall1-lst@par8417,373135 (defun waterfall (forcing-round pool-lst x pspv hints ens wrld ctx statewaterfall8556,378989 (defun some-pool-member-subsumes (pool clause-set)some-pool-member-subsumes8656,383838 (defun add-to-pop-historyadd-to-pop-history8672,384543 (defun pop-clause1 (pool pop-history)pop-clause18718,386687 (defun make-defthm-forms-for-byes (byes wrld)make-defthm-forms-for-byes8794,390173 (defun pop-clause-msg1 (forcing-round lst jppl-flg prev-action gag-state msg-ppop-clause-msg18805,390575 (defun pop-clause-msg (forcing-round pop-history jppl-flg pspv state)pop-clause-msg8971,398660 (defun subsumed-clause-ids-from-pop-history (forcing-round pop-history)subsumed-clause-ids-from-pop-history8996,399547 (defun increment-proof-tree-pop-clause (forcing-round pop-history state)increment-proof-tree-pop-clause9010,400125 (defun pop-clause (forcing-round pspv jppl-flg state)pop-clause9023,400708 (defun tilde-@-assumnotes-phrase-lst (lst wrld)tilde-@-assumnotes-phrase-lst9059,402341 (defun tilde-*-assumnotes-column-phrase (assumnotes wrld)tilde-*-assumnotes-column-phrase9111,405171 (defun tilde-@-assumnotes-phrase-lst-gag-mode (lst acc)tilde-@-assumnotes-phrase-lst-gag-mode9118,405388 (defun tilde-*-assumnotes-column-phrase-gag-mode (assumnotes)tilde-*-assumnotes-column-phrase-gag-mode9158,407267 (defun process-assumptions-msg1 (forcing-round n pairs state)process-assumptions-msg19165,407496 (defun process-assumptions-msg (forcing-round n0 n pairs state)process-assumptions-msg9202,408977 (deflabel forcing-roundforcing-round9249,411209 (deflabel failurefailure9384,418560 (deflabel failed-forcingfailed-forcing9430,421203 (defun count-assumptions (ttree)count-assumptions9577,428226 (defun add-type-alist-runes-to-ttree1 (type-alist runes)add-type-alist-runes-to-ttree19584,428412 (defun add-type-alist-runes-to-ttree (type-alist ttree)add-type-alist-runes-to-ttree9592,428684 (defun process-assumptions-ttree (assns ttree)process-assumptions-ttree9601,429105 (defun process-assumptions (forcing-round pspv wrld state)process-assumptions9613,429524 (defun do-not-induct-msg (forcing-round pool-lst state)do-not-induct-msg9693,432878 (defun prove-loop2 (forcing-round pool-lst clauses pspv hints ens wrld ctxprove-loop29720,433810 (defun prove-loop1 (forcing-round pool-lst clauses pspv hints ens wrld ctxprove-loop19827,438595 (defun print-pstack-and-gag-state (state)print-pstack-and-gag-state9838,439001 (defun prove-loop0 (clauses pspv hints ens wrld ctx state)prove-loop09869,440332 (defmacro bind-acl2-time-limit (form)bind-acl2-time-limit9894,441382 (defun prove-loop (clauses pspv hints ens wrld ctx state)prove-loop9917,442387 (defmacro make-pspv (ens wrld &rest args)make-pspv9949,443750 (defun chk-assumption-free-ttree (ttree ctx state)chk-assumption-free-ttree9973,444759 (defun prove (term pspv hints ens wrld ctx state)prove10025,447197 defuns.lisp,23404 (defconst *mutual-recursion-ctx-string**mutual-recursion-ctx-string*31,1307 (defun translate-bodies1 (non-executablep names bodies bindingstranslate-bodies134,1394 (defun chk-non-executable-bodies (names arglists bodies non-executablep ctxchk-non-executable-bodies112,4978 (defun translate-bodies (non-executablep names arglists bodies known-stobjs-lsttranslate-bodies148,6709 (defun chk-mutual-recursion-bad-names (lst names bodies)chk-mutual-recursion-bad-names179,8164 (defconst *chk-mutual-recursion-string**chk-mutual-recursion-string*187,8476 (defun chk-mutual-recursion1 (names bodies warnp ctx state)chk-mutual-recursion1196,8863 (defun chk-mutual-recursion (names bodies ctx state)chk-mutual-recursion216,9530 (defun ffnnamep-mod-mbe (fn term)ffnnamep-mod-mbe245,10735 (defun ffnnamep-mod-mbe-lst (fn l)ffnnamep-mod-mbe-lst264,11537 (defun putprop-recursivep-lst (names bodies wrld)putprop-recursivep-lst279,11955 (defrec tests-and-call (tests call) nil)tests-and-call307,13291 (defun all-calls (names term alist ans)all-calls316,13658 (defun all-calls-lst (names lst alist ans)all-calls-lst345,14963 (defun all-calls-alist (names alist ans)all-calls-alist354,15203 (defun termination-machine1 (tests calls ans)termination-machine1363,15528 (defun ffnnamesp-eq (fns term)ffnnamesp-eq383,16304 (defun ffnnamesp-eq-lst (fns l)ffnnamesp-eq-lst393,16685 (defun member-eq-all (a lst)member-eq-all401,16823 (defun termination-machine (names body alist tests ruler-extenders)termination-machine407,16918 (defun termination-machine-for-list (names bodies alist tests ruler-extenders)termination-machine-for-list514,21848 (defun termination-machines (names bodies ruler-extenders-lst)termination-machines522,22236 (defun proper-dumb-occur-as-output (x y)proper-dumb-occur-as-output537,22839 (defun always-tested-and-changedp (var pos t-machine)always-tested-and-changedp562,23997 (defun guess-measure (name defun-flg args pos t-machine ctx wrld state)guess-measure588,25059 (defun guess-measure-alist (names arglists measures t-machines ctx wrld state)guess-measure-alist644,28120 (defun remove-built-in-clauses (cl-set ens oncep-override wrld state ttree)remove-built-in-clauses679,29922 (defun length-exceedsp (lst n)length-exceedsp714,31407 (defun clean-up-clause-set (cl-set ens wrld ttree state)clean-up-clause-set719,31533 (defun measure-clause-for-branch (name tc measure-alist rel wrld)measure-clause-for-branch801,35292 (defun measure-clauses-for-fn1 (name t-machine measure-alist rel wrld)measure-clauses-for-fn1832,36427 (defun measure-clauses-for-fn (name t-machine measure-alist mp rel wrld)measure-clauses-for-fn846,37045 (defun measure-clauses-for-clique (names t-machines measure-alist mp rel wrld)measure-clauses-for-clique877,38160 (defun tilde-*-measure-phrase1 (alist wrld)tilde-*-measure-phrase1894,38868 (defun tilde-*-measure-phrase (alist wrld)tilde-*-measure-phrase902,39209 (defun find-?-measure (measure-alist)find-?-measure930,40209 (defun prove-termination (names t-machines measure-alist mp rel hints otf-flgprove-termination939,40512 (defun putprop-justification-lst (measure-alist subset-lst mp relputprop-justification-lst1142,50446 (defun union-equal-to-end (x y)union-equal-to-end1170,51570 (defun cross-tests-and-calls3 (tacs tacs-lst)cross-tests-and-calls31179,51827 (defun cross-tests-and-calls2 (tacs-lst1 tacs-lst2)cross-tests-and-calls21195,52696 (defun cross-tests-and-calls1 (tacs-lst-lst acc)cross-tests-and-calls11203,52955 (defun sublis-tests-rev (test-alist acc)sublis-tests-rev1212,53270 (defun all-calls-test-alist (names test-alist acc)all-calls-test-alist1230,54017 (defun cross-tests-and-calls (names top-test-alist top-calls tacs-lst-lst)cross-tests-and-calls1242,54423 (defun induction-machine-for-fn1 (names body alist test-alist callsinduction-machine-for-fn11309,57925 (defun induction-machine-for-fn1-lst (names bodies alist ruler-extenders accinduction-machine-for-fn1-lst1669,76151 (defun term-equated-to-constant-in-termlist (lst)term-equated-to-constant-in-termlist1693,77225 (defun simplify-tests (var const tests)simplify-tests1702,77526 (defun simplify-tests-and-calls (tc)simplify-tests-and-calls1723,78461 (defun simplify-tests-and-calls-lst (tc-list)simplify-tests-and-calls-lst1747,79416 (defun induction-machine-for-fn (names body ruler-extenders)induction-machine-for-fn1769,80133 (defun induction-machines (names bodies ruler-extenders-lst)induction-machines1791,81054 (defun putprop-induction-machine-lst (names bodies ruler-extenders-lstputprop-induction-machine-lst1811,81943 (defun quick-block-initial-settings (formals)quick-block-initial-settings1825,82497 (defun quick-block-info1 (var term)quick-block-info11830,82672 (defun quick-block-info2 (setting info1)quick-block-info21835,82821 (defun quick-block-settings (settings formals args)quick-block-settings1843,83047 (defun quick-block-down-t-machine (name settings formals t-machine)quick-block-down-t-machine1852,83480 (defun quick-block-info (name formals t-machine)quick-block-info1868,84109 (defun putprop-quick-block-info-lst (names t-machines wrld)putprop-quick-block-info-lst1886,84876 (deflabel subversive-recursionssubversive-recursions1902,85506 (deflabel subversive-inductionssubversive-inductions2056,93495 (defmacro big-mutrec (names)big-mutrec2066,93663 (defmacro update-w (condition new-w &optional retract-p)update-w2076,94068 (defun get-sig-fns1 (ee-lst)get-sig-fns12100,94916 (defun get-sig-fns (wrld)get-sig-fns2111,95339 (defun selected-all-fnnames-lst (formals subset actuals acc)selected-all-fnnames-lst2114,95422 (defun subversivep (fns t-machine formals-and-subset-alist wrld)subversivep2122,95722 (defun subversive-cliquep (fns t-machines formals-and-subset-alist wrld)subversive-cliquep2155,97064 (defun prove-termination-non-recursive (names bodies mp rel hints otf-flgprove-termination-non-recursive2190,98867 (defun prove-termination-recursive (names arglists measures t-machinesprove-termination-recursive2235,100687 (defun put-induction-info-recursive (names arglists col ttree measure-alistput-induction-info-recursive2278,102342 (defun put-induction-info (names arglists measures ruler-extenders-lst bodiesput-induction-info2330,104417 (defun destructure-definition (term install-body ens wrld ttree)destructure-definition2413,108408 (defun member-rewrite-rule-rune (rune lst)member-rewrite-rule-rune2470,110419 (defun replace-rewrite-rule-rune (rune rule lst)replace-rewrite-rule-rune2479,110712 (defun preprocess-hyp (hyp)preprocess-hyp2491,111130 (defun preprocess-hyps (hyps)preprocess-hyps2510,111945 (defun add-definition-rule-with-ttree (rune nume clique controller-alistadd-definition-rule-with-ttree2515,112101 (defun add-definition-rule (rune nume clique controller-alist install-body termadd-definition-rule2596,115787 (defun listof-standardp-macro (lst)listof-standardp-macro2605,116161 (defun putprop-body-lst (names arglists bodies normalizepsputprop-body-lst2620,116571 (defun type-set-implied-by-term1 (term tvar fvar)type-set-implied-by-term12909,132219 (defun type-set-implied-by-term (var not-flg term ens wrld ttree)type-set-implied-by-term2934,133239 (defun putprop-initial-type-prescriptions (names wrld)putprop-initial-type-prescriptions2954,134134 (defun map-returned-formals-via-formals (formals pockets returned-formals)map-returned-formals-via-formals3003,136324 (defun map-type-sets-via-formals (formals ts-lst returned-formals)map-type-sets-via-formals3034,137962 (defun vector-ts-union (ts-lst1 ts-lst2)vector-ts-union3050,138671 (defun map-cons-tag-trees (lst ttree)map-cons-tag-trees3059,138983 (defun type-set-and-returned-formals-with-rule1type-set-and-returned-formals-with-rule13068,139202 (defun type-set-and-returned-formals-with-rule (tp term type-alist ens wrldtype-set-and-returned-formals-with-rule3124,141482 (defun type-set-and-returned-formals-with-rulestype-set-and-returned-formals-with-rules3205,144698 (defun type-set-and-returned-formals (term type-alist ens wrld ttree)type-set-and-returned-formals3276,147662 (defun type-set-and-returned-formals-lsttype-set-and-returned-formals-lst3564,161696 (defun guess-type-prescription-for-fn-step (name body ens wrld ttree)guess-type-prescription-for-fn-step3582,162463 (defconst *clique-step-install-interval**clique-step-install-interval*3627,164525 (defun guess-and-putprop-type-prescription-lst-for-clique-stepguess-and-putprop-type-prescription-lst-for-clique-step3635,164804 (defun cleanse-type-prescriptionscleanse-type-prescriptions3699,167444 (defun guess-and-putprop-type-prescription-lst-for-cliqueguess-and-putprop-type-prescription-lst-for-clique3767,170690 (defun get-normalized-bodies (names wrld)get-normalized-bodies3818,173066 (defun putprop-type-prescription-lst (names subversive-p def-nume ens wrldputprop-type-prescription-lst3834,173686 (defun putprop-level-no-lst (names wrld)putprop-level-no-lst3979,179216 (defun primitive-recursive-argp (var term wrld)primitive-recursive-argp4005,180556 (defun primitive-recursive-callp (formals args wrld)primitive-recursive-callp4069,184157 (defun primitive-recursive-callsp (formals calls wrld)primitive-recursive-callsp4074,184389 (defun primitive-recursive-machinep (formals machine wrld)primitive-recursive-machinep4079,184621 (defun putprop-primitive-recursive-defunp-lst (names wrld)putprop-primitive-recursive-defunp-lst4096,185377 (defun make-controller-pocket (formals vars)make-controller-pocket4130,186922 (defun make-controller-alist1 (names wrld)make-controller-alist14143,187395 (defun make-controller-alist (names wrld)make-controller-alist4167,188544 (defun max-nume-exceeded-error (ctx)max-nume-exceeded-error4184,189400 (defun putprop-defun-runic-mapping-pairs1 (names def-nume tp-flg ind-flg wrld)putprop-defun-runic-mapping-pairs14193,189820 (defun putprop-defun-runic-mapping-pairs (names tp-flg wrld)putprop-defun-runic-mapping-pairs4230,191585 (defun eval-ground-subexpressions-lst-lst (lst-lst ens wrld state ttree)eval-ground-subexpressions-lst-lst4312,195799 (defun guard-clauses+ (term debug-info stobj-optp clause ens wrld state ttree)guard-clauses+4327,196382 (defun guard-clauses-for-body (hyp-segments body debug-info stobj-optp ensguard-clauses-for-body4336,196830 (defun guard-clauses-for-fn (name debug-p ens wrld state ttree)guard-clauses-for-fn4360,197846 (defun guard-clauses-for-clique (names debug-p ens wrld state ttree)guard-clauses-for-clique4430,200795 (defun print-verify-guards-msg (names col state)print-verify-guards-msg4451,201656 (defun collect-ideals (names wrld acc)collect-ideals4477,202560 (defun collect-non-ideals (names wrld)collect-non-ideals4483,202799 (defun collect-non-common-lisp-compliants (names wrld)collect-non-common-lisp-compliants4489,203038 (defun all-fnnames1-exec (flg x acc)all-fnnames1-exec4496,203358 (defmacro all-fnnames-exec (term)all-fnnames-exec4524,204550 (defun chk-common-lisp-compliant-subfunctionschk-common-lisp-compliant-subfunctions4527,204623 (defun chk-acceptable-verify-guards-formula (name x ctx wrld state)chk-acceptable-verify-guards-formula4556,206065 (defun chk-acceptable-verify-guards (name ctx wrld state)chk-acceptable-verify-guards4603,208208 (defun guard-obligation-clauses (x guard-debug ens wrld state)guard-obligation-clauses4694,212374 (defun guard-obligation (x guard-debug ctx state)guard-obligation4767,215437 (defun prove-guard-clauses-msg (names cl-set cl-set-ttree displayed-goalprove-guard-clauses-msg4847,219004 (defmacro verify-guards-formula (x &key guard-debug &allow-other-keys)verify-guards-formula4884,220593 (defun prove-guard-clauses (names hints otf-flg guard-debug ctx ens wrld state)prove-guard-clauses4932,222757 (defun verify-guards-fn1 (names hints otf-flg guard-debug ctx state)verify-guards-fn15032,227312 (defun verify-guards-fn (name state hints otf-flg guard-debug doc event-form)verify-guards-fn5268,237438 (defconst *super-defun-wart-table**super-defun-wart-table*5359,241091 (defun project-out-columns-i-and-j (i j table)project-out-columns-i-and-j5408,243773 (defconst *super-defun-wart-binding-alist**super-defun-wart-binding-alist*5414,243972 (defconst *super-defun-wart-stobjs-in-alist**super-defun-wart-stobjs-in-alist*5417,244078 (defun super-defun-wart-bindings (bindings)super-defun-wart-bindings5420,244186 (defun store-stobjs-ins (names stobjs-ins w)store-stobjs-ins5427,244474 (defun store-super-defun-warts-stobjs-in (names wrld)store-super-defun-warts-stobjs-in5433,244726 (defun collect-old-nameps (names wrld)collect-old-nameps5447,245210 (defun defuns-fn-short-cut (names docs pairs guards split-types-terms bodiesdefuns-fn-short-cut5453,245434 (defun print-defun-msg/collect-type-prescriptions (names wrld)print-defun-msg/collect-type-prescriptions5490,247236 (defun print-defun-msg/type-prescriptions1 (alist simp-phrase col state)print-defun-msg/type-prescriptions15514,248230 (defun print-defun-msg/type-prescriptions (names ttree wrld col state)print-defun-msg/type-prescriptions5556,250109 (defun simple-signaturep (fn wrld)simple-signaturep5599,251626 (defun all-simple-signaturesp (names wrld)all-simple-signaturesp5612,252106 (defun print-defun-msg/signatures1 (names wrld state)print-defun-msg/signatures15617,252290 (defun print-defun-msg/signatures (names wrld state)print-defun-msg/signatures5641,253185 (defun print-defun-msg (names ttree wrld col state)print-defun-msg5654,253646 (defun get-ignores (lst)get-ignores5682,254800 (defun get-ignorables (lst)get-ignorables5688,254964 (defun chk-all-stobj-names (lst msg ctx wrld state)chk-all-stobj-names5694,255137 (defun get-declared-stobj-names (edcls ctx wrld state)get-declared-stobj-names5710,255838 (defun get-stobjs-in-lst (lst ctx wrld state)get-stobjs-in-lst5751,257690 (defun chk-stobjs-out-bound (names bindings ctx state)chk-stobjs-out-bound5803,260303 (defun store-stobjs-out (names bindings w)store-stobjs-out5811,260638 (defun unparse-signature (x)unparse-signature5820,260931 (defun chk-defun-mode (defun-mode ctx state)chk-defun-mode5832,261358 (defun scan-to-cltl-command (wrld)scan-to-cltl-command5846,261842 (defconst *xargs-keywords**xargs-keywords*5860,262328 (defun plausible-dclsp1 (lst)plausible-dclsp15870,262652 (defun plausible-dclsp (lst)plausible-dclsp5889,263473 (defun dcl-fields1 (lst)dcl-fields15918,264834 (defun dcl-fields (lst)dcl-fields5925,265121 (defun strip-keyword-list (fields lst)strip-keyword-list5939,265639 (defun strip-dcls1 (fields lst)strip-dcls15953,266142 (defun strip-dcls (fields lst)strip-dcls5966,266756 (defun fetch-dcl-fields2 (field-names kwd-list acc)fetch-dcl-fields25984,267591 (defun fetch-dcl-fields1 (field-names lst)fetch-dcl-fields15994,268004 (defun fetch-dcl-fields (field-names lst)fetch-dcl-fields6005,268547 (defun fetch-dcl-field (field-name lst)fetch-dcl-field6016,269053 (defun set-equalp-eq (lst1 lst2)set-equalp-eq6034,269939 (defun non-identical-defp-chk-measures (name new-measures old-measuresnon-identical-defp-chk-measures6042,270243 (defun non-identical-defp (def1 def2 chk-measure-p wrld)non-identical-defp6096,272713 (defun identical-defp (def1 def2 chk-measure-p wrld)identical-defp6287,282403 (defun redundant-or-reclassifying-defunp0 (defun-mode symbol-classredundant-or-reclassifying-defunp06294,282644 (defun redundant-or-reclassifying-defunp (defun-mode symbol-classredundant-or-reclassifying-defunp6531,292820 (defun redundant-or-reclassifying-defunsp10 (defun-mode symbol-classredundant-or-reclassifying-defunsp106554,294201 (defun redundant-or-reclassifying-defunsp1 (defun-mode symbol-classredundant-or-reclassifying-defunsp16574,295081 (defun recover-defs-lst (fn wrld)recover-defs-lst6579,295370 (defun get-clique (fn wrld)get-clique6644,298637 (defun redundant-or-reclassifying-defunsp0 (defun-mode symbol-classredundant-or-reclassifying-defunsp06658,299233 (defun get-unnormalized-bodies (names wrld)get-unnormalized-bodies6706,302050 (defun strip-last-elements (lst)strip-last-elements6712,302299 (defun redundant-or-reclassifying-defunsp (defun-mode symbol-classredundant-or-reclassifying-defunsp6718,302500 (defun collect-when-cadr-eq (sym lst)collect-when-cadr-eq6869,310639 (defun all-programp (names wrld)all-programp6875,310853 (defun formal-position (var formals i)formal-position6952,314684 (defun make-posns (formals vars)make-posns6957,314843 (defun relevant-posns-term (fn formals term fns clique-alist posns)relevant-posns-term6964,315031 (defun relevant-posns-term-lst (fn formals lst fns clique-alist posns)relevant-posns-term-lst6985,315921 (defun relevant-posns-call (fn formals actuals i fns clique-alistrelevant-posns-call6992,316193 (defun relevant-posns-clique1 (fns arglists bodies all-fns ans)relevant-posns-clique17022,317584 (defun relevant-posns-clique-recur (fns arglists bodies clique-alist)relevant-posns-clique-recur7050,318810 (defun relevant-posns-clique-init (fns arglists guards split-types-termsrelevant-posns-clique-init7057,319198 (defun relevant-posns-lambdas (term ans)relevant-posns-lambdas7104,320989 (defun relevant-posns-lambdas-lst (termlist ans)relevant-posns-lambdas-lst7116,321393 (defun relevant-posns-merge (alist acc)relevant-posns-merge7123,321601 (defun relevant-posns-lambdas-top (bodies)relevant-posns-lambdas-top7135,322133 (defun relevant-posns-clique (fns arglists guards split-types-terms measuresrelevant-posns-clique7139,322295 (defun irrelevant-non-lambda-slots-clique2 (fn formals i posns acc)irrelevant-non-lambda-slots-clique27156,323245 (defun irrelevant-non-lambda-slots-clique1 (fns arglists clique-alist acc)irrelevant-non-lambda-slots-clique17164,323566 (defun irrelevant-non-lambda-slots-clique (fns arglists guardsirrelevant-non-lambda-slots-clique7176,324118 (defun tilde-*-irrelevant-formals-msg1 (slots)tilde-*-irrelevant-formals-msg17194,325136 (defun tilde-*-irrelevant-formals-msg (slots)tilde-*-irrelevant-formals-msg7202,325502 (defun chk-irrelevant-formals (fns arglists guards split-types-terms measureschk-irrelevant-formals7205,325634 (deflabel irrelevant-formalsirrelevant-formals7236,326979 (defun chk-logic-subfunctions (names0 names terms wrld str ctx state)chk-logic-subfunctions7305,330297 (defun get-non-classical-fns-from-list (names wrld fns-sofar)get-non-classical-fns-from-list7332,331554 (defmacro get-non-classical-fns (lst wrld)get-non-classical-fns7344,332075 (defun get-non-classical-fns-aux (lst wrld fns-sofar)get-non-classical-fns-aux7348,332191 (defun strip-missing-measures (lst accum)strip-missing-measures7363,332806 (defun chk-classical-measures (measures names ctx wrld state)chk-classical-measures7371,333063 (defun chk-no-recursive-non-classical (non-classical-fns names mp relchk-no-recursive-non-classical7391,333945 (defun union-collect-non-x (x lst)union-collect-non-x7428,335724 (defun translate-measures (terms ctx wrld state)translate-measures7433,335899 (defun redundant-predefined-error-msg (name)redundant-predefined-error-msg7465,337408 (defun chk-acceptable-defuns-redundancy (names ctx wrld state)chk-acceptable-defuns-redundancy7479,338082 (defun chk-acceptable-defuns-verify-guards-er (names ctx wrld state)chk-acceptable-defuns-verify-guards-er7566,341714 (defun chk-non-executablep (defun-mode non-executablep ctx state)chk-non-executablep7627,344467 (defun chk-acceptable-defuns0 (fives ctx wrld state)chk-acceptable-defuns07649,345357 (defun get-boolean-unambiguous-xargs-flg-lst (key lst default ctx state)get-boolean-unambiguous-xargs-flg-lst7705,347848 (defun chk-acceptable-defuns1 (names fives stobjs-in-lst defun-modechk-acceptable-defuns17713,348259 (defun conditionally-memoized-fns (fns memoize-table)conditionally-memoized-fns8011,361892 (defun chk-acceptable-defuns (lst ctx wrld state #+:non-standard-analysis std-p)chk-acceptable-defuns8029,362665 (deflabel XARGSXARGS8154,368736 (defmacro link-doc-to-keyword (name parent see)link-doc-to-keyword8303,375895 (defmacro link-doc-to (name parent see)link-doc-to8319,376220 (defun build-valid-std-usage-clause (arglist body)build-valid-std-usage-clause8488,383255 (defun verify-valid-std-usage (names arglists bodies hints otf-flgverify-valid-std-usage8496,383579 (defun union-eq1-rev (x y)union-eq1-rev8575,387185 (defun collect-hereditarily-constrained-fnnames (names wrld ans)collect-hereditarily-constrained-fnnames8585,387456 (defun putprop-hereditarily-constrained-fnnames-lst (names bodies wrld)putprop-hereditarily-constrained-fnnames-lst8599,388036 (defun defuns-fn1 (tuple ens big-mutrec names arglists docs pairs guardsdefuns-fn18640,390048 (defun defuns-fn0 (names arglists docs pairs guards measuresdefuns-fn08803,397473 (defun strip-non-hidden-package-names (known-package-alist)strip-non-hidden-package-names8854,399121 (defun in-package-fn (str state)in-package-fn8863,399545 (defun defstobj-functionsp (names embedded-event-lst)defstobj-functionsp8885,400388 (defun index-of-non-number (lst)index-of-non-number8949,404024 (defun non-std-error (fn index formals actuals)non-std-error8958,404230 (defun non-std-body (name formals body)non-std-body8967,404636 (defun non-std-def-lst (def-lst)non-std-def-lst8981,405129 (defun make-udf-insigs (names wrld)make-udf-insigs8996,405695 (defun intro-udf (insig wrld)intro-udf9005,405984 (defun intro-udf-lst1 (insigs wrld)intro-udf-lst19044,407379 (defun intro-udf-lst2 (insigs kwd-value-list-lst)intro-udf-lst29050,407584 (defun intro-udf-lst (insigs kwd-value-list-lst wrld)intro-udf-lst9088,409423 (defun defun-ctx (def-lst state event-form #+:non-standard-analysis std-p)defun-ctx9106,410241 (defun install-event-defuns (names event-form def-lst0 symbol-classinstall-event-defuns9124,410865 (defun defuns-fn (def-lst state event-form #+:non-standard-analysis std-p)defuns-fn9175,413012 (defun defun-fn (def state event-form #+:non-standard-analysis std-p)defun-fn9318,418817 (defun args-fn (name state)args-fn9333,419360 (defmacro args (name)args9420,423570 (defun make-verify-termination-def (old-def new-dcls wrld)make-verify-termination-def9441,424203 (defun make-verify-termination-defs-lst (defs-lst lst wrld)make-verify-termination-defs-lst9470,425455 (defun chk-acceptable-verify-termination1 (lst clique fn1 ctx wrld state)chk-acceptable-verify-termination19486,426245 (defun uniform-defun-modes (defun-mode clique wrld)uniform-defun-modes9524,428390 (defun chk-acceptable-verify-termination (lst ctx wrld state)chk-acceptable-verify-termination9538,428994 (defun verify-termination1 (lst state)verify-termination19609,432205 (defun verify-termination-boot-strap-fn (lst state event-form)verify-termination-boot-strap-fn9642,433646 (defmacro when-logic3 (str x)when-logic39673,434732 (defun verify-termination-fn (lst state)verify-termination-fn9682,434998 (defun fns-used-in-axioms (lst wrld ans)fns-used-in-axioms9709,436191 (defun check-out-instantiablep1 (fns wrld)check-out-instantiablep19735,437164 (defun check-out-instantiablep (wrld)check-out-instantiablep9744,437452 proof-checker-pkg.lisp,40 (defpkg "ACL2-PC" nil)"ACL2-PC"23,881 proof-checker-a.lisp,8305 (defmacro pc-value (sym)pc-value29,1271 (defmacro pc-assign (key val)pc-assign35,1467 (defun initialize-pc-acl2 (state)initialize-pc-acl244,1747 (defmacro state-stack ()state-stack55,2076 (defmacro old-ss ()old-ss58,2129 (defmacro ss-alist ()ss-alist63,2244 (defun define-global-name (var)define-global-name66,2291 (defmacro define-global (var)define-global71,2407 (define-global pc-prompt)pc-prompt78,2623 (define-global pc-prompt-depth-prefix)pc-prompt-depth-prefix79,2649 (define-global pc-print-macroexpansion-flg)pc-print-macroexpansion-flg80,2688 (define-global pc-print-prompt-and-instr-flg)pc-print-prompt-and-instr-flg82,2786 (defrec pc-statepc-state93,3346 (defconst *pc-state-fields-for-primitives**pc-state-fields-for-primitives*102,3459 (defmacro instruction (&optional state-stack-supplied-p)instruction105,3572 (defmacro goals (&optional state-stack-supplied-p)goals112,3790 (defmacro abbreviations (&optional state-stack-supplied-p)abbreviations119,3996 (defmacro local-tag-tree (&optional state-stack-supplied-p)local-tag-tree126,4218 (defmacro pc-ens (&optional state-stack-supplied-p)pc-ens133,4442 (defmacro tag-tree (&optional state-stack-supplied-p)tag-tree140,4650 (defrec goalgoal153,5192 (defconst *goal-fields**goal-fields*159,5279 (defmacro conc (&optional ss-supplied-p)conc162,5354 (defmacro hyps (&optional ss-supplied-p)hyps165,5449 (defmacro current-addr (&optional ss-supplied-p)current-addr168,5544 (defmacro goal-name (&optional ss-supplied-p)goal-name171,5655 (defmacro depends-on (&optional ss-supplied-p)depends-on174,5760 (defmacro make-official-pc-command (sym)make-official-pc-command177,5867 (defun intern-in-keyword-package (sym)intern-in-keyword-package181,6021 (defun make-pretty-pc-command (x)make-pretty-pc-command185,6142 (defun make-pretty-pc-instr (instr)make-pretty-pc-instr190,6308 (defmacro change-pc-state (pc-s &rest args)change-pc-state201,6703 (defun make-official-pc-instr (instr)make-official-pc-instr204,6787 (defun check-formals-length (formals args fn ctx state)check-formals-length221,7430 (defun check-&optional-and-&rest (formals state)check-&optional-and-&rest250,8843 (defun make-let-pairs-from-formals (formals arg)make-let-pairs-from-formals276,9986 (defun all-symbols (form)all-symbols292,10609 (defun all-symbols-list (x)all-symbols-list306,10959 (defun make-access-bindings (record-name record fields)make-access-bindings314,11099 (defun let-form-for-pc-state-vars (form)let-form-for-pc-state-vars320,11356 (defun check-field-names (formals ctx state)check-field-names332,11875 (defmacro print-no-change (&optional str alist (col '0))print-no-change342,12293 (defmacro print-no-change2 (&rest args)print-no-change2345,12399 (defun print-no-change-fn (str alist col state)print-no-change-fn349,12509 (defmacro maybe-update-instruction (instr pc-state-and-state)maybe-update-instruction369,13303 (defun add-pc-doc-header (command-type str)add-pc-doc-header378,13703 (defun remove-doc (command-type body)remove-doc389,13990 (defun pc-primitive-defun-form (raw-name name formals doc body)pc-primitive-defun-form396,14302 (defun pc-command-table-guard (key val wrld)pc-command-table-guard419,15194 (defmacro add-pc-command (name command-type)add-pc-command452,16071 (defmacro pc-command-type (name)pc-command-type455,16167 (defmacro print-no-change3 (&optional str alist (col '0))print-no-change3458,16273 (defun add-pc-command-1 (name command-type state)add-pc-command-1462,16412 (defun toggle-pc-macro-fn (name new-tp state)toggle-pc-macro-fn469,16616 (defun pc-meta-or-macro-defun (raw-name name formals doc body)pc-meta-or-macro-defun494,18233 (defun goal-names (goals)goal-names513,19018 (defun instructions-of-state-stack (ss acc)instructions-of-state-stack519,19162 (defmacro fms0 (str &optional alist col (evisc-tuple 'nil evisc-tuple-p))fms0528,19442 (defmacro with-output-forced (output-chan signature code)with-output-forced542,19986 (defun print-pc-prompt (state)print-pc-prompt566,20775 (defun pc-macroexpand (raw-instr state)pc-macroexpand577,21128 (defun find-goal (name goals)find-goal605,22419 (defun print-all-goals-proved-message (state)print-all-goals-proved-message612,22600 (defmacro when-goals (form)when-goals621,22874 (defmacro when-goals-trip (form)when-goals-trip626,22979 (defun current-immediate-deps (goal-name goal-names)current-immediate-deps632,23125 (defun goal-dependent-p (parent name)goal-dependent-p642,23550 (defun current-all-deps (goal-name goal-names)current-all-deps650,23766 (defun maybe-print-proved-goal-message (goal old-goals goals state)maybe-print-proved-goal-message660,24179 (defun accumulate-ttree-in-pc-state (pc-state state)accumulate-ttree-in-pc-state703,26335 (defun pc-process-assumptions (pc-ens ttree wrld state)pc-process-assumptions710,26628 (defun make-implication (assumptions concl)make-implication737,27407 (defun cl-set-to-implications (cl-set)cl-set-to-implications743,27557 (defun known-assumptions (type-alist assns)known-assumptions750,27792 (defun add-assumptions-to-top-goaladd-assumptions-to-top-goal767,28524 (defun unproved-goals (pc-state)unproved-goals794,29767 (defun make-pc-ens (pc-ens state)make-pc-ens802,29986 (defun initial-rcnst-from-ens (ens wrld splitter-output)initial-rcnst-from-ens807,30072 (defun make-new-goals-fixed-hyps (termlist hyps goal-name start-index)make-new-goals-fixed-hyps817,30496 (defun pc-single-step-primitive (instr state)pc-single-step-primitive830,30977 (defun maybe-print-macroexpansion (instr raw-instr state)maybe-print-macroexpansion1014,40452 (defun pc-single-step-1 (raw-instr state)pc-single-step-11033,41476 (defun union-lastn-pc-tag-trees (n ss acc)union-lastn-pc-tag-trees1105,45355 (defun pc-single-step (raw-instr state)pc-single-step1118,45751 (defconst *pc-complete-signal* 'acl2-pc-complete)*pc-complete-signal*1149,47365 (defmacro catch-throw-to-local-top-level (form)catch-throw-to-local-top-level1151,47416 (defun pc-main-loop (instr-list quit-conditions last-valuepc-main-loop1179,48411 (defun make-initial-goal (term)make-initial-goal1264,51976 (defun initial-state-stack (term raw-term event-name rule-classes pc-ens)initial-state-stack1272,52134 (defun event-name-and-types-and-raw-term (state-stack)event-name-and-types-and-raw-term1282,52526 (defmacro install-initial-state-stack (term raw-term event-name rule-classes)install-initial-state-stack1285,52648 (defun pc-main1 (instr-list quit-conditions pc-print-prompt-and-instr-flgpc-main11295,53051 (defun pc-main (term raw-term event-name rule-classes instr-listpc-main1302,53300 (defun pc-top (raw-term event-name rule-classes instr-list quit-conditions state)pc-top1308,53624 (defun illegal-fnp (x w)illegal-fnp1329,54338 (defun illegal-fnp-list (x w)illegal-fnp-list1343,54725 (defun verify-fn (raw-term raw-term-supplied-p event-name rule-classesverify-fn1349,54868 (defun print-unproved-goals-message (goals state)print-unproved-goals-message1403,56789 (defun state-stack-from-instructionsstate-stack-from-instructions1412,57176 (defun state-from-instructionsstate-from-instructions1423,57657 (defun print-pc-defthm (ev state)print-pc-defthm1431,57947 (defmacro print-pc-goal (&optional goal)print-pc-goal1442,58320 (defmacro print-pc-state (&optional pc-state)print-pc-state1467,59442 (defun proof-checkerproof-checker1486,60241 (deflabel proof-checker-commandsproof-checker-commands1571,64102 (deflabel macro-commandmacro-command1581,64405 (defmacro verify (&optional (raw-term 'nil raw-term-supplied-p)verify1608,65634 (deflabel instructionsinstructions1660,67491 (defun sublis-expr-non-quoteps (alist term)sublis-expr-non-quoteps2184,87602 (defun sublis-expr-non-quoteps-lst (alist lst)sublis-expr-non-quoteps-lst2202,88366 (defun invert-abbreviations-alist (alist)invert-abbreviations-alist2209,88568 (defun abbreviate (term abbreviations)abbreviate2216,88799 (defmacro untrans0 (term &optional iff-flg abbreviations)untrans02221,88957 (defun untrans0-lst-fn (termlist iff-flg abbreviations state)untrans0-lst-fn2227,89152 (defmacro untrans0-lst (termlist &optional iff-flg abbreviations)untrans0-lst2233,89382 defthm.lisp,29770 (defun remove-lambdas (term)remove-lambdas51,2105 (defun remove-lambdas-lst (termlist)remove-lambdas-lst61,2420 (defun interpret-term-as-rewrite-rule2 (name hyps lhs rhs wrld)interpret-term-as-rewrite-rule272,2676 (defun interpret-term-as-rewrite-rule1 (term equiv-okp ens wrld)interpret-term-as-rewrite-rule1107,3973 (defun interpret-term-as-rewrite-rule (name hyps term ens wrld)interpret-term-as-rewrite-rule144,5842 (defun non-recursive-fnnames (term ens wrld)non-recursive-fnnames191,7520 (defun non-recursive-fnnames-lst (lst ens wrld)non-recursive-fnnames-lst206,8212 (defun hide-lambdas1 (formals)hide-lambdas1217,8637 (defun hide-lambdas (lst)hide-lambdas227,8991 (defun variantp (term1 term2)variantp237,9369 (defun surrounding-fns1 (vars term fn acc)surrounding-fns1255,9841 (defun surrounding-fns-lst (vars term-list fn acc)surrounding-fns-lst283,10936 (defun surrounding-fns (vars term)surrounding-fns291,11154 (defun loop-stopper1 (alist vars lhs)loop-stopper1317,12154 (defun loop-stopper (lhs rhs)loop-stopper327,12573 (defun remove-irrelevant-loop-stopper-pairs (pairs vars)remove-irrelevant-loop-stopper-pairs502,22921 (defun put-match-free-value (match-free-value rune wrld)put-match-free-value520,23572 (defun free-vars-in-hyps (hyps bound-vars wrld)free-vars-in-hyps538,24207 (defun free-vars-in-hyps-simple (hyps bound-vars)free-vars-in-hyps-simple578,26063 (defun free-vars-in-fc-hyps (triggers hyps concls)free-vars-in-fc-hyps590,26581 (defun free-vars-in-hyps-considering-bind-free (hyps bound-vars wrld)free-vars-in-hyps-considering-bind-free602,26998 (defun all-vars-in-hyps (hyps)all-vars-in-hyps669,30005 (defun match-free-value (match-free hyps pat wrld)match-free-value700,31295 (defun match-free-fc-value (match-free hyps concls triggers wrld)match-free-fc-value710,31608 (defun rule-backchain-limit-lst (backchain-limit-lst hyps wrld flg)rule-backchain-limit-lst723,32205 (defun create-rewrite-rule (rune nume hyps equiv lhs rhs loop-stopper-lstcreate-rewrite-rule731,32546 (defun hyps-that-instantiate-free-vars (free-vars hyps)hyps-that-instantiate-free-vars773,34336 (defun maybe-one-way-unify (pat term alist)maybe-one-way-unify790,35023 (defun maybe-one-way-unify-lst (pat-lst term-lst alist)maybe-one-way-unify-lst808,35722 (defun maybe-one-way-unify-with-some (pat term-lst alist)maybe-one-way-unify-with-some815,36003 (defun maybe-subsumes (cl1 cl2 alist)maybe-subsumes824,36378 (defun subsumes-rewrite-rule (rule1 rule2 wrld)subsumes-rewrite-rule849,37587 (defun find-subsumed-rule-names (lst rule ens wrld)find-subsumed-rule-names901,39816 (defun find-subsuming-rule-names (lst rule ens wrld)find-subsuming-rule-names918,40669 (defun forced-hyps (lst)forced-hyps934,41477 (defun strip-top-level-nots-and-forces (hyps)strip-top-level-nots-and-forces943,41803 (defun free-variable-error? (token name ctx wrld state)free-variable-error?957,42386 (defun extend-geneqv-alist (var geneqv alist wrld)extend-geneqv-alist976,43434 (defun covered-geneqv-alist (term geneqv alist wrld)covered-geneqv-alist988,43848 (defun covered-geneqv-alist-lst (termlist geneqv-lst alist wrld)covered-geneqv-alist-lst1009,44746 (defun uncovered-equivs (geneqv covered-geneqv wrld)uncovered-equivs1019,45169 (defun uncovered-equivs-alist (term geneqv var-geneqv-alist var-geneqv-alist0uncovered-equivs-alist1029,45553 (defun uncovered-equivs-alist-lst (termlist geneqv-lst var-geneqv-alistuncovered-equivs-alist-lst1123,49818 (defun double-rewrite-opportunities (hyp-index hyps var-geneqv-alistdouble-rewrite-opportunities1143,50828 (defun show-double-rewrite-opportunities1 (location var-equivs-alistshow-double-rewrite-opportunities11244,56794 (defun show-double-rewrite-opportunities (hyp-var-equivs-counts-alist-pairsshow-double-rewrite-opportunities1279,58722 (defun irrelevant-loop-stopper-pairs (pairs vars)irrelevant-loop-stopper-pairs1301,59814 (defun chk-rewrite-rule-warnings (name match-free loop-stopper rule ctx enschk-rewrite-rule-warnings1313,60194 (defun chk-acceptable-rewrite-rule2 (name match-free loop-stopper hyps conclchk-acceptable-rewrite-rule21496,69429 (defun chk-acceptable-rewrite-rule1 (name match-free loop-stopper lst ctx enschk-acceptable-rewrite-rule11527,70890 (defun chk-acceptable-rewrite-rule (name match-free loop-stopper term ctx enschk-acceptable-rewrite-rule1546,71748 (defun add-rewrite-rule2 (rune nume hyps concl loop-stopper-lstadd-rewrite-rule21565,72732 (defun add-rewrite-rule1 (rune nume lst loop-stopper-lstadd-rewrite-rule11638,76389 (defun add-rewrite-rule (rune nume loop-stopper-lst termadd-rewrite-rule1659,77356 (deflabel linearlinear1675,78021 (defun expand-inequality-fncall1 (term)expand-inequality-fncall11814,85809 (defun expand-inequality-fncall (term)expand-inequality-fncall1837,86784 (defun all-vars-in-poly-lst (lst)all-vars-in-poly-lst1882,89018 (defun subbagp-eq (bag1 bag2)subbagp-eq1893,89369 (defun always-biggerp-data (term)always-biggerp-data1900,89568 (defun always-biggerp-data-lst (lst)always-biggerp-data-lst1908,89759 (defun always-biggerp (abd1 abd2)always-biggerp1916,89952 (defun no-element-always-biggerp (abd-lst abd)no-element-always-biggerp1940,91005 (defun maximal-terms1 (abd-lst abd-lst0 needed-vars)maximal-terms11950,91362 (defun maximal-terms (lst hyp-vars concl-vars)maximal-terms1965,91985 (defun collect-when-ffnnamesp (fns lst)collect-when-ffnnamesp2000,93721 (defun make-free-max-terms-msg1 (max-terms vars hyps)make-free-max-terms-msg12010,94026 (defun make-free-max-terms-msg (name max-terms vars hyps)make-free-max-terms-msg2037,95393 (defun external-linearize (term ens wrld state)external-linearize2053,95997 (defun bad-synp-hyp-msg-for-linear (max-terms hyps wrld)bad-synp-hyp-msg-for-linear2063,96240 (defun show-double-rewrite-opportunities-linear (hyps max-terms final-term nameshow-double-rewrite-opportunities-linear2072,96620 (defun chk-acceptable-linear-rule2chk-acceptable-linear-rule22093,97521 (defun chk-acceptable-linear-rule1 (name match-free trigger-terms lst ctx enschk-acceptable-linear-rule12232,105258 (defun chk-acceptable-linear-rule (name match-free trigger-terms term ctx enschk-acceptable-linear-rule2248,105937 (defun add-linear-rule3 (rune nume hyps concl max-termsadd-linear-rule32266,106792 (defun add-linear-rule2 (rune nume trigger-terms hyps concladd-linear-rule22308,108745 (defun add-linear-rule1 (rune nume trigger-terms lstadd-linear-rule12323,109448 (defun add-linear-rule (rune nume trigger-terms termadd-linear-rule2339,110251 (deflabel well-founded-relationwell-founded-relation2381,111647 (defun destructure-well-founded-relation-rule (term)destructure-well-founded-relation-rule2561,119582 (defun chk-acceptable-well-founded-relation-rule (name term ctx wrld state)chk-acceptable-well-founded-relation-rule2600,120899 (defun add-well-founded-relation-rule (rune nume term wrld)add-well-founded-relation-rule2637,122528 (deflabel built-in-clausebuilt-in-clause2649,123014 (defun chk-acceptable-built-in-clause-rule2 (name hyps concl ctx wrld state)chk-acceptable-built-in-clause-rule22786,128489 (defun chk-acceptable-built-in-clause-rule1 (name lst ctx wrld state)chk-acceptable-built-in-clause-rule12811,129588 (defun chk-acceptable-built-in-clause-rule (name term ctx wrld state)chk-acceptable-built-in-clause-rule2825,130115 (defun fn-and-maximal-level-no (term wrld fn max)fn-and-maximal-level-no2841,130847 (defun fn-and-maximal-level-no-lst (lst wrld fn max)fn-and-maximal-level-no-lst2867,132104 (defun built-in-clause-discriminator-fn (cl wrld)built-in-clause-discriminator-fn2876,132352 (defun all-fnnames-subsumer (cl wrld)all-fnnames-subsumer2882,132525 (defun make-built-in-clause-rules1 (rune nume clauses wrld)make-built-in-clause-rules12906,133958 (defun chk-initial-built-in-clauses (lst wrld good-lst some-badp)chk-initial-built-in-clauses2930,135135 (defun make-built-in-clause-rules (rune nume lst wrld)make-built-in-clause-rules2959,136375 (defun classify-and-store-built-in-clause-rules (lst pots wrld)classify-and-store-built-in-clause-rules2975,137041 (defun add-built-in-clause-rule (rune nume term wrld)add-built-in-clause-rule3021,139510 (deflabel compound-recognizercompound-recognizer3053,141010 (defun destructure-compound-recognizer (term)destructure-compound-recognizer3264,151242 (defun make-recognizer-tuple (rune nume parity fn var term ens wrld)make-recognizer-tuple3322,153795 (defun comment-on-new-recog-tuple1 (new-recog-tuple recognizer-alistcomment-on-new-recog-tuple13389,156783 (defun comment-on-new-recog-tuple (new-recog-tuple ctx ens wrld state)comment-on-new-recog-tuple3447,159882 (defun chk-acceptable-compound-recognizer-rule (name term ctx ens wrld state)chk-acceptable-compound-recognizer-rule3510,163011 (defun add-compound-recognizer-rule (rune nume term ens wrld)add-compound-recognizer-rule3567,165414 (deflabel forward-chainingforward-chaining3590,166473 (defun chk-triggers (name match-free hyps terms hyps-vars concls-vars ctx enschk-triggers3744,174739 (defun destructure-forward-chaining-term (term)destructure-forward-chaining-term3870,181943 (defun chk-acceptable-forward-chaining-rule (name match-free trigger-terms termchk-acceptable-forward-chaining-rule3911,183865 (defun putprop-forward-chaining-rules-lstputprop-forward-chaining-rules-lst3932,184611 (defun add-forward-chaining-rule (rune nume trigger-terms term match-free wrld)add-forward-chaining-rule3954,185576 (deflabel metameta3970,186313 (deflabel evaluator-restrictionsevaluator-restrictions4358,205814 (deflabel extended-metafunctionsextended-metafunctions4831,223133 (deflabel meta-extractmeta-extract5193,239409 (defun evaluator-clause/arglist (evfn formals x)evaluator-clause/arglist5520,255629 (defun evaluator-clause (evfn fn-args)evaluator-clause5534,256219 (defun evaluator-clauses1 (evfn fn-args-lst)evaluator-clauses15562,257216 (defun evaluator-clauses (evfn evfn-lst fn-args-lst)evaluator-clauses5567,257419 (defun cdrp (x term)cdrp5668,262899 (defun expand-eq-and-atom-term-lst (lst)expand-eq-and-atom-term-lst5684,263448 (defun normalize-alleged-evaluator-clause (clause)normalize-alleged-evaluator-clause5738,265849 (defun normalize-alleged-evaluator-clause-set (lst)normalize-alleged-evaluator-clause-set5748,266194 (defun shallow-clausify1 (lst)shallow-clausify15753,266408 (defun shallow-clausify (term)shallow-clausify5766,266790 (defun find-evfn-lst-in-clause (evfn cl)find-evfn-lst-in-clause5786,267659 (defun guess-evfn-lst-for-evfn (evfn cl-set)guess-evfn-lst-for-evfn5817,268743 (defun find-fn-in-clause (cl wrld)find-fn-in-clause5829,269224 (defun guess-fn-args-lst-for-evfn (cl-set wrld)guess-fn-args-lst-for-evfn5843,269740 (defun normalized-evaluator-cl-set (ev wrld)normalized-evaluator-cl-set5857,270425 (defun chk-evaluator (evfn wrld ctx state)chk-evaluator5867,270749 (defun defevaluator-form/defthms (evfn prefix i clauses)defevaluator-form/defthms5948,274763 (defun defevaluator-form/fns-clauses (evfn fn-args-lst)defevaluator-form/fns-clauses5963,275503 (defconst *defevaluator-form-base-theory**defevaluator-form-base-theory*5985,276273 (defun defevaluator-form (evfn evfn-lst fn-args-lst)defevaluator-form5994,276616 (defun pairs-to-macro-alias-msgs (alist)pairs-to-macro-alias-msgs6046,278681 (defun defevaluator-check-msg (alist macro-aliases wrld bad macro-alist)defevaluator-check-msg6053,278971 (defun defevaluator-check (x evfn evfn-lst fn-args-lst ctx state)defevaluator-check6089,280813 (defun defevaluator-check-form (x evfn evfn-lst fn-args-lst)defevaluator-check-form6115,281949 (defmacro defevaluator (&whole x evfn evfn-lst fn-args-lst &key skip-checks)defevaluator6129,282308 (deflabel term-tableterm-table6251,287343 (defun remove-meta-extract-contextual-hyps (hyps ev mfc-symbol a)remove-meta-extract-contextual-hyps6287,288716 (defun remove-meta-extract-global-hyps (hyps ev)remove-meta-extract-global-hyps6318,290111 (defun meta-rule-hypothesis-functions (hyp ev x a mfc-symbol)meta-rule-hypothesis-functions6338,290820 (defun meta-fn-args (term extendedp ens state)meta-fn-args6380,292547 (defun chk-meta-function (metafn name trigger-fns extendedpchk-meta-function6406,293397 (defun ev-lst-from-ev (ev wrld)ev-lst-from-ev6460,295804 (defun attached-fns (fns wrld)attached-fns6469,296010 (defun siblings (f wrld)siblings6480,296461 (defun canonical-sibling (f wrld)canonical-sibling6485,296620 (defun canonical-ffn-symbs (term wrld ans ignore-fns rlp)canonical-ffn-symbs6494,296921 (defun canonical-ffn-symbs-lst (lst wrld ans ignore-fns rlp)canonical-ffn-symbs-lst6521,297849 (defun collect-canonical-siblings (fns wrld ans ignore-fns)collect-canonical-siblings6532,298125 (defun immediate-canonical-ancestors (fn wrld ignore-fns rlp)immediate-canonical-ancestors6544,298537 (defun canonical-ancestors-rec (fns wrld ans rlp)canonical-ancestors-rec6571,299948 (defun canonical-ancestors (fn wrld rlp)canonical-ancestors6586,300493 (defun canonical-ancestors-lst (fns wrld)canonical-ancestors-lst6599,301030 (defun chk-evaluator-use-in-rule (name meta-fn hyp-fn extra-fns rule-type evchk-evaluator-use-in-rule6607,301296 (defun chk-rule-fn-guard (function-string rule-type fn ctx wrld state)chk-rule-fn-guard6759,308748 (defun chk-acceptable-meta-rule (name trigger-fns term ctx ens wrld state)chk-acceptable-meta-rule6803,310822 (defun add-meta-rule1 (lst rule wrld)add-meta-rule16917,316267 (defun maybe-putprop-lst (symb-lst key val wrld)maybe-putprop-lst6931,316813 (defun mark-attachment-disallowed2 (fns msg wrld)mark-attachment-disallowed26941,317166 (defun mark-attachment-disallowed1 (canonical-fns msg wrld)mark-attachment-disallowed16967,318311 (defun mark-attachment-disallowed (meta-fns ev msg wrld)mark-attachment-disallowed6976,318669 (defun add-meta-rule (rune nume trigger-fns term backchain-limit wrld)add-meta-rule6989,319176 (deflabel elimelim7044,321275 (defun destructors (term ans)destructors7187,328334 (defun destructors-lst (lst ans)destructors-lst7203,328968 (defun strip-ffn-symbs (lst)strip-ffn-symbs7210,329128 (defun chk-acceptable-elim-rule1 (name vars dests ctx wrld state)chk-acceptable-elim-rule17215,329270 (defun chk-acceptable-elim-rule (name term ctx wrld state)chk-acceptable-elim-rule7238,330281 (defun add-elim-rule1 (rune nume hyps equiv lhs rhs lst dests wrld)add-elim-rule17293,332599 (defun add-elim-rule (rune nume term wrld)add-elim-rule7318,333707 (deflabel generalizegeneralize7330,334150 (defun chk-acceptable-generalize-rule (name term ctx wrld state)chk-acceptable-generalize-rule7369,335647 (defun add-generalize-rule (rune nume term wrld)add-generalize-rule7376,335839 (deflabel type-prescriptiontype-prescription7388,336271 (defun find-type-prescription-pat (term ens wrld)find-type-prescription-pat7575,346019 (defun type-prescription-disjunctp (var term)type-prescription-disjunctp7617,347766 (defun type-prescription-conclp (var concl)type-prescription-conclp7663,349973 (defun subst-nil-into-type-prescription-disjunct (var term)subst-nil-into-type-prescription-disjunct7688,351071 (defun subst-nil-into-type-prescription-concl (var concl)subst-nil-into-type-prescription-concl7713,352024 (defun unprettyify-tp (term)unprettyify-tp7745,353461 (defun destructure-type-prescription (name typed-term term ens wrld)destructure-type-prescription7762,354031 (defun add-type-prescription-rule (rune nume typed-term termadd-type-prescription-rule7923,363163 (defun strong-compound-recognizer-p (fn recognizer-alist ens)strong-compound-recognizer-p8000,366460 (defun warned-non-rec-fns-for-tp (term recognizer-alist ens wrld)warned-non-rec-fns-for-tp8010,366943 (defun warned-non-rec-fns-tp-hyps1 (hyps recognizer-alist ens wrld acc)warned-non-rec-fns-tp-hyps18036,368020 (defun warned-non-rec-fns-tp-hyps (hyps ens wrld)warned-non-rec-fns-tp-hyps8053,368857 (defun chk-acceptable-type-prescription-rule (name typed-term termchk-acceptable-type-prescription-rule8058,369057 (deflabel equivalenceequivalence8195,376333 (defun boolean-fn (fn)boolean-fn8498,386253 (defun reflexivity (fn)reflexivity8506,386451 (defun symmetry (fn)symmetry8512,386541 (defun transitivity (fn)transitivity8519,386655 (defun equivalence-relation-condition (fn)equivalence-relation-condition8527,386806 (defun find-candidate-equivalence-relation (clauses)find-candidate-equivalence-relation8545,387566 (defun collect-problematic-pre-equivalence-rule-names (lst)collect-problematic-pre-equivalence-rule-names8559,388081 (defun chk-acceptable-equivalence-rule (name term ctx wrld state)chk-acceptable-equivalence-rule8578,389127 (defun add-equivalence-rule (rune nume term ens wrld)add-equivalence-rule8701,395263 (deflabel refinementrefinement8841,401371 (defun chk-acceptable-refinement-rule (name term ctx wrld state)chk-acceptable-refinement-rule8898,403559 (defun collect-coarsenings (wrld)collect-coarsenings8937,405494 (defun putprop-coarsenings (alist wrld)putprop-coarsenings8947,405823 (defun union-values (lst alist)union-values8975,407124 (defun extend-value-set (lst alist)extend-value-set8983,407363 (defun extend-each-value-set (alist1 alist2)extend-each-value-set8997,408028 (defun close-value-sets (alist)close-value-sets9007,408359 (defun add-refinement-rule (name nume term wrld)add-refinement-rule9021,408987 (deflabel congruencecongruence9042,409781 (defun corresponding-args-eq-except (args1 args2 xk yk)corresponding-args-eq-except9191,416688 (defun interpret-term-as-congruence-rule (name term wrld)interpret-term-as-congruence-rule9205,417281 (defun some-congruence-rule-same (equiv rules)some-congruence-rule-same9286,421209 (defun some-congruence-rule-has-refinement (equiv rules wrld)some-congruence-rule-has-refinement9295,421505 (defun chk-acceptable-congruence-rule (name term ctx wrld state)chk-acceptable-congruence-rule9305,421863 (defun putnth (val n lst)putnth9383,425609 (defun add-congruence-rule-to-congruence (rule k congruence)add-congruence-rule-to-congruence9390,425871 (defun add-congruence-rule (rune nume term wrld)add-congruence-rule9400,426278 (deflabel definitiondefinition9446,428466 (defun chk-destructure-definition (name term ctx wrld state)chk-destructure-definition9619,438333 (defun chk-acceptable-definition-install-body (fn hyps equiv args bodychk-acceptable-definition-install-body9631,438896 (defun chk-acceptable-definition-rulechk-acceptable-definition-rule9702,441843 (deflabel inductioninduction9752,443989 (defun chk-acceptable-induction-rule (name term ctx wrld state)chk-acceptable-induction-rule9915,452723 (defun add-induction-rule (rune nume pat-term cond-term scheme-term term wrld)add-induction-rule9922,452914 (deflabel type-set-invertertype-set-inverter9939,453584 (defun chk-acceptable-type-set-inverter-rule (name ts term ctx ens wrld state)chk-acceptable-type-set-inverter-rule9998,456157 (defun add-type-set-inverter-rule (rune nume ts term ens wrld)add-type-set-inverter-rule10066,459047 (deflabel clause-processorclause-processor10095,460169 (defun tilde-@-illegal-clause-processor-sig-msg (cl-proc stobjs-in stobjs-out)tilde-@-illegal-clause-processor-sig-msg10387,474159 (defun destructure-clause-processor-rule (term)destructure-clause-processor-rule10422,475370 (defun chk-acceptable-clause-processor-rule (name term ctx wrld state)chk-acceptable-clause-processor-rule10447,476369 (defun add-clause-processor-rule (name term wrld)add-clause-processor-rule10555,481989 (defun trusted-clause-processor-table-guard (key val wrld)trusted-clause-processor-table-guard10600,483847 (defmacro define-trusted-clause-processordefine-trusted-clause-processor10681,487696 (defun primitive-instructionp (instr state)primitive-instructionp11111,507258 (defun non-primitive-instructions (instructions state)non-primitive-instructions11119,507571 (defun chk-primitive-instruction-listp (instructions ctx state)chk-primitive-instruction-listp11129,507884 (defun translate-instructions (name instructions ctx wrld state)translate-instructions11137,508123 (defun controller-alistp (clique alist wrld)controller-alistp11144,508371 (defun alist-to-keyword-alist (alist ans)alist-to-keyword-alist11164,509108 (defun loop-stopper-alist-p (x wrld)loop-stopper-alist-p11176,509546 (defun guess-controller-alist-for-definition-rule (names formals body ctx wrldguess-controller-alist-for-definition-rule11189,509900 (defun chk-legal-linear-trigger-terms1 (term lst name ctx state)chk-legal-linear-trigger-terms111204,510623 (defun chk-legal-linear-trigger-terms (terms lst name ctx state)chk-legal-linear-trigger-terms11225,511653 (defun backchain-limit-listp (lst)backchain-limit-listp11253,513067 (defun eliminate-macro-aliases (lst macro-aliases wrld)eliminate-macro-aliases11266,513377 (defun translate-rule-class-alist (token alist seen corollary name x ctx enstranslate-rule-class-alist11293,514749 (defun translate-rule-class1 (class tflg name x ctx ens wrld state)translate-rule-class112006,553091 (defun reason-for-non-keyword-value-listp (x)reason-for-non-keyword-value-listp12067,555824 (defun translate-rule-class (name x thm ctx ens wrld state)translate-rule-class12086,556388 (defun translate-rule-classes1 (name classes thm ctx ens wrld state)translate-rule-classes112137,558695 (defun translate-rule-classes (name classes thm ctx ens wrld state)translate-rule-classes12159,559553 (defun chk-acceptable-x-rule (name class ctx ens wrld state)chk-acceptable-x-rule12190,561022 (defun chk-acceptable-x-rules (name classes ctx ens wrld state)chk-acceptable-x-rules12275,564814 (defun collect-keys-eq (sym-list alist)collect-keys-eq12290,565517 (defun chk-acceptable-rules (name classes ctx ens wrld state)chk-acceptable-rules12299,565873 (defun add-x-rule (rune nume class ens wrld state)add-x-rule12389,569095 (defun add-rules1 (mapping-pairs classes ens wrld state)add-rules112500,574125 (defun truncate-class-alist (alist term)truncate-class-alist12519,574965 (defun truncate-classes (classes term)truncate-classes12537,575756 (defun make-runes1 (event-name classes runes)make-runes112555,576635 (defun make-runes (event-name classes)make-runes12594,578280 (defun make-runic-mapping-pairs (nume runes)make-runic-mapping-pairs12602,578518 (defun add-rules (name classes term untranslated-term ens wrld state)add-rules12616,579057 (defun redundant-theoremp (name term classes wrld)redundant-theoremp12639,580265 (defun non-tautological-classes (term classes)non-tautological-classes12652,580843 (defun prove-corollaries1 (name term i n rule-classes ens wrld ctx state ttree)prove-corollaries112670,581640 (defun prove-corollaries (name term rule-classes ens wrld ctx state)prove-corollaries12730,584671 (defun enabled-runep-string (rune ens wrld)enabled-runep-string12764,586094 (defun untranslate-hyps (hyps wrld)untranslate-hyps12769,586208 (defun info-for-lemmas (lemmas numes ens wrld)info-for-lemmas12774,586385 (defun world-to-next-event (wrld)world-to-next-event12811,588368 (defun assoc-eq-eq (x y alist)assoc-eq-eq12819,588619 (defun actual-props (props seen acc)actual-props12830,588955 (defun info-for-well-founded-relation-rules (rules)info-for-well-founded-relation-rules12848,589577 (defun info-for-built-in-clause-rules1 (rules numes ens wrld)info-for-built-in-clause-rules112863,590175 (defun info-for-built-in-clause-rules (alist numes ens wrld)info-for-built-in-clause-rules12880,590964 (defun info-for-compound-recognizer-rules (rules numes ens wrld)info-for-compound-recognizer-rules12886,591204 (defun info-for-generalize-rules (rules numes ens wrld)info-for-generalize-rules12910,592386 (defun info-for-linear-lemmas (rules numes ens wrld)info-for-linear-lemmas12927,593149 (defun info-for-eliminate-destructors-rule (rule numes ens wrld)info-for-eliminate-destructors-rule12957,594782 (defun info-for-congruences (val numes ens wrld)info-for-congruences12985,596259 (defun info-for-coarsenings (val numes ens wrld)info-for-coarsenings12993,596468 (defun info-for-forward-chaining-rules (rules numes ens wrld)info-for-forward-chaining-rules13001,596661 (defun decode-type-set-lst (lst)decode-type-set-lst13029,598039 (defun info-for-type-prescriptions (rules numes ens wrld)info-for-type-prescriptions13035,598178 (defun info-for-induction-rules (rules numes ens wrld)info-for-induction-rules13065,599748 (defun info-for-type-set-inverter-rules (rules numes ens wrld)info-for-type-set-inverter-rules13088,600857 (defun info-for-x-rules (sym key val numes ens wrld)info-for-x-rules13108,601800 (defun info-for-rules (props numes ens wrld)info-for-rules13167,603913 (defun print-info-for-rules-entry (keys vals chan state)print-info-for-rules-entry13177,604304 (defun print-info-for-rules (info chan state)print-info-for-rules13202,605499 (defun pr-body (wrld-segment numes wrld state)pr-body13212,605879 (defun pr-fn (name state)pr-fn13221,606121 (defun print-clause-processor-rules1 (alist wrld state)print-clause-processor-rules113235,606786 (defmacro print-clause-processor-rules ()print-clause-processor-rules13248,607309 (defun new-numes (world-segment)new-numes13254,607543 (defun world-to-next-command (wrld ans)world-to-next-command13265,607896 (defun pr!-fn (cd state)pr!-fn13272,608164 (defmacro pr (name)pr13281,608455 (defmacro pr! (cd)pr!13328,610048 (defun disabledp-fn-lst (runic-mapping-pairs ens)disabledp-fn-lst13355,610978 (defun disabledp-fn (name ens wrld)disabledp-fn13362,611299 (defmacro disabledp (name)disabledp13385,612295 (defun access-x-rule-rune (x rule)access-x-rule-rune13415,613529 (defun collect-x-rules-of-rune (x rune lst ans)collect-x-rules-of-rune13459,615785 (defun collect-congruence-rules-of-rune-in-geneqv-lst (geneqv-lst rune ans)collect-congruence-rules-of-rune-in-geneqv-lst13470,616190 (defun collect-congruence-rules-of-rune (congruences rune ans)collect-congruence-rules-of-rune13482,616639 (defun find-rules-of-rune2 (rune sym key val ans)find-rules-of-rune213497,617285 (defun find-rules-of-rune1 (rune props ans)find-rules-of-rune113591,621367 (defun find-rules-of-rune (rune wrld)find-rules-of-rune13607,622118 (defun collect-non-backchain-subclass (rules)collect-non-backchain-subclass13651,624128 (defun chk-acceptable-monitor (rune expr ctx state)chk-acceptable-monitor13661,624504 (defun chk-acceptable-monitors (lst ctx state)chk-acceptable-monitors13736,628130 (defun monitor1 (rune form ctx state)monitor113757,628983 (defun unmonitor1 (rune ctx state)unmonitor113776,629749 (defun monitor-fn (rune expr state)monitor-fn13795,630536 (defun unmonitor-fn (rune ctx state)unmonitor-fn13821,631360 (defun monitored-runes-fn (state)monitored-runes-fn13864,632935 (defun brr-fn (flg state)brr-fn13880,633394 (defmacro brr (flg)brr13895,633823 (deflabel why-brrwhy-brr13967,637789 (defmacro brr@ (sym)brr@14024,640860 (defmacro monitor (rune expr)monitor14151,647625 (defmacro unmonitor (rune)unmonitor14321,656705 (defmacro monitored-runes ()monitored-runes14348,657580 (defun proceed-from-brkpt1 (action runes ctx state)proceed-from-brkpt114364,657965 (defun exit-brr (state)exit-brr14396,659232 (defun ok-if-fn (term state)ok-if-fn14407,659603 (defmacro ok-if (term)ok-if14415,659848 (defun print-rule-storage-dependencies (name ttree state)print-rule-storage-dependencies14462,661893 (defun defaxiom-supporters (tterm name ctx wrld state)defaxiom-supporters14479,662505 (defun defaxiom-fn (name term state rule-classes doc event-form)defaxiom-fn14532,665346 (defun warn-on-inappropriate-defun-mode (assumep event-form ctx state)warn-on-inappropriate-defun-mode14625,669733 (defun add-hyp-standardp-var-lst (vars)add-hyp-standardp-var-lst14639,670253 (defun strengthen-hyps-using-transfer-principle (hyps vars)strengthen-hyps-using-transfer-principle14646,670441 (defun weaken-using-transfer-principle (term)weaken-using-transfer-principle14658,670781 (defun remove-standardp-hyp (tterm)remove-standardp-hyp14682,671651 (defun remove-standardp-hyps (tterm)remove-standardp-hyps14690,671886 (defun remove-standardp-hyps-and-standardp-conclusion (tterm)remove-standardp-hyps-and-standardp-conclusion14702,672289 (defun chk-classical-term-or-standardp-of-classical-term (tterm term ctx wrld state)chk-classical-term-or-standardp-of-classical-term14713,672646 (defmacro with-waterfall-parallelism-timings (name form)with-waterfall-parallelism-timings14731,673403 (defmacro with-waterfall-parallelism-timings (name form)with-waterfall-parallelism-timings14741,673811 (defun defthm-fn1 (name term statedefthm-fn114745,673903 (defun defthm-fn (name term statedefthm-fn14891,680973 (defmacro thm (term &key hints otf-flg doc)thm14914,681575 (defun thm-fn (term state hints otf-flg doc)thm-fn14946,682400 (defun chk-extensible-rule-classes (rule-classes ctx state)chk-extensible-rule-classes14979,683829 (defun extend-rule-classes (class rule-classes)extend-rule-classes14988,684177 (defun gen-new-name-in-package-of-symbol1 (char-lst cnt pkgsym wrld)gen-new-name-in-package-of-symbol114999,684537 (defun gen-new-name-in-package-of-symbol (sym pkgsym wrld)gen-new-name-in-package-of-symbol15019,685333 (defmacro defequiv (equivdefequiv15039,686055 (defmacro defrefinement (equiv1 equiv2defrefinement15098,688147 (defmacro defcong (&whole xdefcong15153,690082 other-events.lisp,60906 (defun legal-initp (x)legal-initp46,2129 (defun macro-arglist-keysp (args keys-passed)macro-arglist-keysp58,2576 (defun macro-arglist-after-restp (args)macro-arglist-after-restp91,4156 (defun macro-arglist-optionalp (args)macro-arglist-optionalp98,4362 (defun macro-arglist1p (args)macro-arglist1p125,5340 (defun subsequencep (lst1 lst2)subsequencep142,5953 (defun collect-lambda-keywordps (lst)collect-lambda-keywordps155,6395 (defun macro-args-structurep (args)macro-args-structurep162,6655 (defun macro-vars-key (args)macro-vars-key182,7611 (defun macro-vars-after-rest (args)macro-vars-after-rest206,8490 (defun macro-vars-optional (args)macro-vars-optional219,8841 (defun macro-vars (args)macro-vars240,9591 (defun chk-legal-defconst-name (name state)chk-legal-defconst-name260,10312 (defun defconst-fn1 (name val doc doc-pair w state)defconst-fn1274,10944 (defvar *hcomp-fn-ht* nil)*hcomp-fn-ht*285,11207 (defvar *hcomp-const-ht* nil)*hcomp-const-ht*286,11234 (defvar *hcomp-macro-ht* nil)*hcomp-macro-ht*287,11264 (defvar *hcomp-fn-alist* nil)*hcomp-fn-alist*288,11294 (defvar *hcomp-const-alist* nil)*hcomp-const-alist*289,11324 (defvar *hcomp-macro-alist* nil)*hcomp-macro-alist*290,11357 (defconstant *hcomp-fake-value* 'acl2_invisible::hcomp-fake-value)*hcomp-fake-value*291,11390 (defvar *hcomp-book-ht* nil)*hcomp-book-ht*292,11457 (defvar *hcomp-const-restore-ht* nil)*hcomp-const-restore-ht*293,11486 (defvar *hcomp-fn-macro-restore-ht**hcomp-fn-macro-restore-ht*294,11524 (defvar *declaim-list* nil)*declaim-list*327,12707 (defrec hcomp-book-ht-entryhcomp-book-ht-entry331,12739 (defun defconst-val-raw (full-book-name name)defconst-val-raw341,13031 (defun defconst-val (name form ctx wrld state)defconst-val352,13520 (defun defconst-fn (name form state doc event-form)defconst-fn437,16794 (defun chk-legal-init-msg (x)chk-legal-init-msg495,19265 (defun chk-legal-init (x ctx state)chk-legal-init520,20228 (defun chk-macro-arglist-keys (args keys-passed)chk-macro-arglist-keys525,20372 (defun chk-macro-arglist-after-rest (args)chk-macro-arglist-after-rest598,24489 (defun chk-macro-arglist-optional (args)chk-macro-arglist-optional605,24749 (defun chk-macro-arglist1 (args)chk-macro-arglist1640,26388 (defun chk-macro-arglist-msg (args chk-state wrld)chk-macro-arglist-msg660,27229 (defun chk-macro-arglist (args chk-state ctx state)chk-macro-arglist740,31252 (defun defmacro-fn1 (name args doc doc-pair guard body w state)defmacro-fn1745,31438 (defun chk-defmacro-width (rst)chk-defmacro-width761,31975 (defun redundant-defmacrop (name args guard body w)redundant-defmacrop775,32477 (defun defmacro-fn (mdef state event-form)defmacro-fn793,33303 (defconst *initial-event-defmacros**initial-event-defmacros*924,40005 (defun boot-translate (x)boot-translate1145,47975 (defun primordial-event-macro-and-fn1 (actuals)primordial-event-macro-and-fn11188,49682 (defun primordial-event-macro-and-fn (form wrld)primordial-event-macro-and-fn1217,51099 (defun primordial-event-macros-and-fns (lst wrld)primordial-event-macros-and-fns1352,57759 (defconst *initial-type-prescriptions**initial-type-prescriptions*1368,58354 (defun collect-world-globals (wrld ans)collect-world-globals1390,59173 (defun primordial-world-globals (operating-system)primordial-world-globals1397,59443 (defun arglists-to-nils (arglists)arglists-to-nils1543,66398 (defconst *unattachable-primitives**unattachable-primitives*1549,66626 (defun primordial-world (operating-system)primordial-world1567,67349 (defun same-name-twice (l)same-name-twice1633,69780 (defun conflicting-imports (l)conflicting-imports1641,70012 (defun chk-new-stringp-name (ev-type name ctx w state)chk-new-stringp-name1648,70183 (deflabel package-reincarnation-import-restrictionspackage-reincarnation-import-restrictions1687,71730 (defun chk-package-reincarnation-import-restrictions (name proposed-imports)chk-package-reincarnation-import-restrictions1750,75086 (defun convert-book-name-to-cert-name (x cert-op)convert-book-name-to-cert-name1762,75524 (defun unrelativize-book-path (lst dir)unrelativize-book-path1785,76415 (defun tilde-@-defpkg-error-phrase (name package-entry new-not-old old-not-newtilde-@-defpkg-error-phrase1794,76791 (defconst *1*-pkg-prefix**1*-pkg-prefix*1843,79466 (defun chk-acceptable-defpkg (name form defpkg-book-path hidden-p ctx w state)chk-acceptable-defpkg1856,79892 (defun defpkg-fn (name form state doc book-path hidden-p event-form)defpkg-fn2051,88562 (defun theory-fn-callp (x)theory-fn-callp2227,97485 (defun intersection-augmented-theories-fn1 (lst1 lst2 ans)intersection-augmented-theories-fn12250,98346 (defmacro check-theory (lst wrld ctx form)check-theory2270,99314 (defun intersection-theories-fn (lst1 lst2 wrld)intersection-theories-fn2278,99624 (defmacro intersection-theories (lst1 lst2)intersection-theories2287,99971 (defun union-augmented-theories-fn1 (lst1 lst2 ans)union-augmented-theories-fn12319,100954 (defun union-theories-fn1 (lst1 lst2 nume wrld ans)union-theories-fn12336,101806 (defun union-theories-fn (lst1 lst2 lst1-known-to-be-runic wrld)union-theories-fn2360,102903 (defmacro union-theories (lst1 lst2)union-theories2399,104363 (defun set-difference-augmented-theories-fn1 (lst1 lst2 ans)set-difference-augmented-theories-fn12445,105656 (defun set-difference-theories-fn1 (lst1 lst2 nume wrld ans)set-difference-theories-fn12460,106379 (defun set-difference-theories-fn (lst1 lst2 lst1-known-to-be-runic wrld)set-difference-theories-fn2484,107459 (defmacro set-difference-theories (lst1 lst2)set-difference-theories2515,108651 (defun universal-theory-fn1 (lst ans redefined)universal-theory-fn12554,109949 (defun universal-theory-fn (logical-name wrld)universal-theory-fn2606,112806 (defmacro universal-theory (logical-name)universal-theory2637,114355 (defun function-theory-fn1 (token lst ans redefined)function-theory-fn12687,116403 (defun function-theory-fn (logical-name wrld)function-theory-fn2747,119313 (defmacro function-theory (logical-name)function-theory2763,119971 (defun executable-counterpart-theory-fn (logical-name wrld)executable-counterpart-theory-fn2802,121292 (defmacro executable-counterpart-theory (logical-name)executable-counterpart-theory2817,121929 (defun standard-theories (wrld)standard-theories2860,123480 (defun current-theory-fn (logical-name wrld)current-theory-fn2866,123730 (defmacro current-theory (logical-name)current-theory2886,124527 (defconst *initial-return-last-table**initial-return-last-table*3020,129526 (defun end-prehistoric-world (wrld)end-prehistoric-world3048,130830 (defun theory-namep (name wrld)theory-namep3092,132881 (defun theory-fn (name wrld)theory-fn3101,133138 (defmacro theory (name)theory3113,133547 (defun deftheory-fn (name expr state doc event-form)deftheory-fn3143,134400 (defun in-theory-fn (expr state doc event-form)in-theory-fn3265,139761 (defun in-arithmetic-theory-fn (expr state doc event-form)in-arithmetic-theory-fn3331,142480 (defmacro disable (&rest rst)disable3394,145110 (defmacro enable (&rest rst)enable3434,146442 (defmacro theory-invariant (&whole event-form term &key key (error 't))theory-invariant3488,148342 (defmacro theory-invariant (&rest args)theory-invariant3689,158323 (defmacro incompatible (rune1 rune2)incompatible3693,158397 (deflabel signaturesignature3734,159871 (defconst *generic-bad-signature-string**generic-bad-signature-string*3883,167472 (defconst *signature-keywords**signature-keywords*3889,167759 (defun duplicate-key-in-keyword-value-listp (l)duplicate-key-in-keyword-value-listp3894,167866 (defun chk-signature (x ctx wrld state)chk-signature3901,168111 (defun chk-signatures (signatures ctx wrld state)chk-signatures4167,180958 (defun chk-acceptable-encapsulate1 (signatures form-lst ctx wrld state)chk-acceptable-encapsulate14219,183573 (defun primitive-event-macros ()primitive-event-macros4270,186152 (deflabel embedded-event-formembedded-event-form4410,190850 (defun name-introduced (trip functionp)name-introduced4565,197154 (defun chk-embedded-event-form-orig-form-msg (orig-form state)chk-embedded-event-form-orig-form-msg4642,200634 (defun chk-embedded-event-form (form orig-form wrld ctx state names portcullispchk-embedded-event-form4649,200883 (defun destructure-expansion (form)destructure-expansion5006,218724 (defun rebuild-expansion (wrappers form)rebuild-expansion5020,219290 (defun set-raw-mode-on (state)set-raw-mode-on5025,219467 (defun set-raw-mode-off (state)set-raw-mode-off5030,219634 (defmacro set-raw-mode-on! ()set-raw-mode-on!5036,219821 (defmacro set-raw-mode (flg)set-raw-mode5054,220469 (defun-one-output stobj-out (val)stobj-out5193,227030 (defun mv-ref! (i)mv-ref!5204,227422 (defmacro add-raw-arity (name val)add-raw-arity5243,228250 (defmacro remove-raw-arity (name)remove-raw-arity5296,230266 (defun raw-arity (form wrld state)raw-arity5320,231097 (defun alist-to-bindings (alist)alist-to-bindings5383,233809 (defun-one-output acl2-raw-eval-form-to-eval (form)acl2-raw-eval-form-to-eval5390,233991 (defun acl2-raw-eval (form state)acl2-raw-eval5420,235214 (defun acl2-raw-eval (form state)acl2-raw-eval5443,236099 (defun acl2-raw-eval (form state)acl2-raw-eval5461,236795 (defun get-and-chk-last-make-event-expansion (form wrld ctx state names)get-and-chk-last-make-event-expansion5464,236870 (defconst *local-value-triple-elided**local-value-triple-elided*5491,238053 (defun elide-locals-rec (form strongp)elide-locals-rec5501,238311 (defun elide-locals-lst (x strongp)elide-locals-lst5556,240902 (defun elide-locals (form environment strongp)elide-locals5567,241343 (defun make-record-expansion (event expansion)make-record-expansion5591,242508 (defun eval-event-lst (index expansion-alist ev-lst quietp environmenteval-event-lst5598,242731 (defun equal-insig (insig1 insig2)equal-insig5803,253064 (defun bad-signature-alist (insigs kwd-value-list-lst udf-fns wrld)bad-signature-alist5823,253981 (defmacro if-ns (test tbr fbr ctx)if-ns5888,257110 (defun tilde-*-bad-insigs-phrase1 (alist)tilde-*-bad-insigs-phrase15907,257634 (defun tilde-*-bad-insigs-phrase (alist)tilde-*-bad-insigs-phrase5934,258933 (defun union-eq-cars (alist)union-eq-cars5946,259429 (defun chk-acceptable-encapsulate2 (insigs kwd-value-list-lst wrld ctx state)chk-acceptable-encapsulate25950,259552 (defun conjoin-into-alist (fn thm alist)conjoin-into-alist5993,261304 (defun classes-theorems (classes)classes-theorems6007,261833 (defun constraints-introduced1 (thms fns ans)constraints-introduced16019,262217 (defun new-trips (wrld3 proto-wrld3 seen acc)new-trips6035,262844 (defun constraints-introduced (new-trips fns ans)constraints-introduced6115,267165 (defun putprop-constraints (fn constrained-fns constraint-lstputprop-constraints6235,272394 (deflabel local-incompatibilitylocal-incompatibility6269,273894 (defun maybe-install-acl2-defaults-table (acl2-defaults-table state)maybe-install-acl2-defaults-table6371,279021 (defun in-encapsulatep (embedded-event-lst non-trivp)in-encapsulatep6389,279713 (defun update-for-redo-flat (n ev-lst state)update-for-redo-flat6405,280274 (defmacro redo-flat (&key (succ-ld-skip-proofsp 't)redo-flat6424,281017 (defun cert-op (state)cert-op6574,288107 (defun eval-event-lst-environment (in-encapsulatep state)eval-event-lst-environment6592,288839 (defun process-embedded-eventsprocess-embedded-events6611,289576 (defun constrained-functions (exported-fns sig-fns new-trips)constrained-functions6901,305036 (defun collect-logicals (names wrld)collect-logicals6927,306283 (defun exported-function-names (new-trips)exported-function-names6936,306567 (defun get-subversives (fns wrld)get-subversives6948,306985 (defun ancestral-ffn-symbs-lst (lst trips ans)ancestral-ffn-symbs-lst6958,307410 (defun constraints-list (fns wrld acc seen)constraints-list6962,307565 (defun encapsulate-constraint (sig-fns exported-names new-trips wrld)encapsulate-constraint6977,308277 (defun new-dependent-clause-processors (new-tbl old-tbl)new-dependent-clause-processors7087,313499 (defun bogus-exported-compliants (names exports-with-sig-ancestors sig-fnsbogus-exported-compliants7104,314206 (defun encapsulate-pass-2 (insigs kwd-value-list-lst ev-lstencapsulate-pass-27157,316789 (defun tilde-@-abbreviate-object-phrase (x)tilde-@-abbreviate-object-phrase7948,347075 (defun encapsulate-ctx (signatures form-lst)encapsulate-ctx7982,348132 (defun print-encapsulate-msg1 (insigs form-lst state)print-encapsulate-msg18008,349199 (defun print-encapsulate-msg2 (insigs form-lst state)print-encapsulate-msg28025,349905 (defun print-encapsulate-msg3/exported-names (insigs lst)print-encapsulate-msg3/exported-names8037,350239 (defun print-encapsulate-msg3/constraints (constrained-fns constraintsprint-encapsulate-msg3/constraints8056,350843 (defun print-encapsulate-msg3 (ctx insigs form-lst exported-namesprint-encapsulate-msg38104,352831 (defun find-first-non-local-name (x)find-first-non-local-name8181,356861 (defun find-first-non-local-name-lst (lst)find-first-non-local-name-lst8252,359697 (defun corresponding-encap-events (old-evs new-evs ans)corresponding-encap-events8271,360526 (defun corresponding-encaps (old new)corresponding-encaps8298,361632 (defun redundant-encapsulate-tuplep (event-form mode ruler-extenders vgeredundant-encapsulate-tuplep8306,361876 (defun redundant-encapsulatep (signatures ev-lst event-form wrld)redundant-encapsulatep8345,363883 (defun mark-missing-as-hidden-p (a1 a2)mark-missing-as-hidden-p8427,367945 (defun known-package-alist-included-p (a1 a2)known-package-alist-included-p8440,368470 (defun encapsulate-fix-known-package-alist (pass1-k-p-alist wrld)encapsulate-fix-known-package-alist8453,369024 (defun subst-by-position1 (alist lst index acc)subst-by-position18488,370979 (defun subst-by-position (alist lst index)subst-by-position8508,371696 (defun intro-udf-guards (insigs kwd-value-list-lst wrld-acc wrld ctx state)intro-udf-guards8526,372515 (defun intro-udf-non-classicalp (insigs kwd-value-list-lst wrld)intro-udf-non-classicalp8567,374321 (defun assoc-proof-supporters-alist (sym alist)assoc-proof-supporters-alist8582,375100 (defun update-proof-supporters-alist-3 (names local-alist old new wrld)update-proof-supporters-alist-38590,375392 (defun posn-first-non-event (names wrld idx)posn-first-non-event8617,376592 (defun update-proof-supporters-alist-2 (names local-alist wrld)update-proof-supporters-alist-28624,376843 (defun update-proof-supporters-alist-1 (namex names local-alistupdate-proof-supporters-alist-18635,377342 (defun update-proof-supporters-alist (new-proof-supporters-alistupdate-proof-supporters-alist8653,378225 (defun install-proof-supporters-alist (new-proof-supporters-alistinstall-proof-supporters-alist8674,379117 (defun encapsulate-fn (signatures ev-lst state event-form)encapsulate-fn8687,379710 (defun progn-fn1 (ev-lst progn!p bindings state)progn-fn19048,398943 (defun progn-fn (ev-lst state)progn-fn9137,403018 (defun progn!-fn (ev-lst bindings state)progn!-fn9140,403086 (defun make-event-ctx (event-form)make-event-ctx9146,403403 (defun protected-eval (form on-behalf-of ctx state aok)protected-eval9151,403571 (defun make-event-debug-pre (form on-behalf-of state)make-event-debug-pre9211,406334 (defun make-event-debug-post (debug-depth expansion0 state)make-event-debug-post9228,407057 (defmacro do-proofs? (do-proofsp form)do-proofs?9237,407419 (defun make-event-fn2 (expansion0 whole-form in-encapsulatep check-expansionmake-event-fn29254,407934 (defun make-event-fn2-lst (expansion-lst whole-form in-encapsulatepmake-event-fn2-lst9301,409633 (defun make-event-fn1 (expansion0 whole-form in-encapsulatep check-expansionmake-event-fn19326,410739 (defun ultimate-expansion (x)ultimate-expansion9337,411259 (defun make-event-fn (form expansion? check-expansion on-behalf-of whole-formmake-event-fn9366,412668 (deflabel booksbooks9719,430647 (deflabel community-bookscommunity-books9786,434029 (defun chk-book-name (book-name full-book-name ctx state)chk-book-name9804,434732 (defun include-book-alist-subsetp (alist1 alist2)include-book-alist-subsetp9849,436782 (defun get-portcullis-cmds (wrld cmds cbds names ctx state)get-portcullis-cmds9889,438735 (defun remove-after-last-directory-separator (p)remove-after-last-directory-separator9922,440141 (defun merge-using-dot-dot (p s)merge-using-dot-dot9930,440465 (defun our-merge-pathnames (p s)our-merge-pathnames9964,441772 (defun expand-tilde-to-user-home-dir (str os ctx state)expand-tilde-to-user-home-dir9995,442921 (defvar *canonical-unix-pathname-action**canonical-unix-pathname-action*10032,444420 (defun canonical-unix-pathname (x dir-p state)canonical-unix-pathname10044,444951 (defun unix-truename-pathname (x dir-p state)unix-truename-pathname10130,449923 (defun chk-live-state-p (fn state)chk-live-state-p10150,450738 (defun-overrides canonical-pathname (pathname dir-p state)canonical-pathname10162,451169 (defun acl2-magic-canonical-pathname (x)acl2-magic-canonical-pathname10169,451398 (defdoc canonical-pathnamecanonical-pathname10200,452401 (defun canonical-dirname! (pathname ctx state)canonical-dirname!10239,454291 (defun directory-of-absolute-pathname (pathname)directory-of-absolute-pathname10250,454717 (defun extend-pathname (dir file-name state)extend-pathname10256,454927 (defun maybe-add-separator (str)maybe-add-separator10300,456948 (defun set-cbd-fn (str state)set-cbd-fn10306,457148 (defmacro set-cbd (str)set-cbd10344,458702 (defun set-cbd-state (str state)set-cbd-state10375,459761 (defun parse-book-name (dir x extension ctx state)parse-book-name10391,460260 (defun cbd-fn (state)cbd-fn10446,462641 (defmacro cbd nilcbd10453,462948 (defun make-include-books-absolute (form cbd dir names make-event-parent os ctxmake-include-books-absolute10569,468644 (defun make-include-books-absolute-lst (forms cbd dir names make-event-parentmake-include-books-absolute-lst10777,478416 (defun first-known-package-alist (wrld-segment)first-known-package-alist10790,478944 (defmacro string-prefixp (root string)string-prefixp10810,479636 (defun relativize-book-path (filename root)relativize-book-path10819,479914 (defun relativize-book-path-lst1 (lst root)relativize-book-path-lst110844,480911 (defun relativize-book-path-lst (lst root current)relativize-book-path-lst10851,481197 (defun defpkg-items-rec (new-kpa old-kpa system-books-dirdefpkg-items-rec10859,481498 (defun defpkg-items (new-kpa ctx w state)defpkg-items10937,485286 (defun new-defpkg-list2 (imports all-defpkg-items acc seen)new-defpkg-list210950,485798 (defun make-hidden-defpkg (name imports/doc/book-path)make-hidden-defpkg10974,486633 (defun new-defpkg-list1new-defpkg-list110983,486943 (defun new-defpkg-list (defpkg-items base-kpa earlier-kpa)new-defpkg-list11017,488275 (defun term-ignore-okp (x)term-ignore-okp11060,490668 (defun term-list-ignore-okp (x)term-list-ignore-okp11071,491050 (defun hidden-defpkg-events1 (kpa system-books-dirhidden-defpkg-events111079,491201 (defun hidden-defpkg-events (kpa w ctx state)hidden-defpkg-events11135,493403 (defun fix-portcullis-cmds1 (dir cmds cbds ans names os ctx state)fix-portcullis-cmds111143,493731 (defun fix-portcullis-cmds (dir cmds cbds names os wrld ctx state)fix-portcullis-cmds11154,494238 (defun collect-uncertified-books (alist)collect-uncertified-books11206,496889 (defun chk-in-package (channel file empty-okp ctx state)chk-in-package11220,497466 (defmacro ill-formed-certificate-er (ctx mark file1 file2ill-formed-certificate-er11263,499365 (defun include-book-er-warning-summary (keyword suspect-book-action-alistinclude-book-er-warning-summary11289,500313 (defun include-book-er1 (file1 file2 msg warning-summary ctx state)include-book-er111337,502213 (defun include-book-er (file1 file2 msg keyword suspect-book-action-alist ctxinclude-book-er11352,502884 (defun post-alist-from-channel (x y ch state)post-alist-from-channel11377,504200 (defun certificate-file-and-input-channel1 (full-book-name cert-op state)certificate-file-and-input-channel111404,505372 (defmacro pcert-op-p (cert-op)pcert-op-p11412,505643 (defun certificate-file-and-input-channel (full-book-name old-cert-op state)certificate-file-and-input-channel11415,505754 (defun cert-annotations-and-checksum-from-cert-file (full-book-name state)cert-annotations-and-checksum-from-cert-file11455,507656 (defun tilde-@-cert-post-alist-phrase (full-book-name familiar-nametilde-@-cert-post-alist-phrase11475,508486 (defun tilde-*-book-check-sums-phrase1 (reqd-alist actual-alist-cddrs state)tilde-*-book-check-sums-phrase111514,510568 (defun tilde-*-book-check-sums-phrase (reqd-alist actual-alist state)tilde-*-book-check-sums-phrase11573,513273 (defun get-cmds-from-portcullis1 (eval-hidden-defpkgs ch ctx state ans)get-cmds-from-portcullis111589,514011 (defun hidden-defpkg-events-simple (kpa acc)hidden-defpkg-events-simple11620,515257 (defun get-cmds-from-portcullis (file1 file2 eval-hidden-defpkgs ch ctx state)get-cmds-from-portcullis11644,515947 (defun convert-book-name-to-port-name (x)convert-book-name-to-port-name11676,517250 (defun chk-raise-portcullis2 (file1 file2 ch port-file-p ctx state ans)chk-raise-portcullis211685,517567 (defun chk-raise-portcullis1 (file1 file2 ch port-file-p ctx state)chk-raise-portcullis111767,521726 (defun mark-local-included-books (post-alist1 post-alist2)mark-local-included-books11798,522942 (defun unmark-and-delete-local-included-books (post-alist3)unmark-and-delete-local-included-books11830,524622 (defun decimal-string-to-number (s bound expo)decimal-string-to-number11845,525344 (defun parse-version (version)parse-version11876,526727 (defun-one-output latest-release-note-string ()latest-release-note-string11921,528775 (defun earlier-acl2-versionp (version1 version2)earlier-acl2-versionp11929,529077 (defun acl2-version-r-p (version)acl2-version-r-p11958,530169 (defun ttag-alistp (x)ttag-alistp11964,530342 (defun cert-annotationsp (x)cert-annotationsp11976,530683 (defun include-book-alist-entryp (entry)include-book-alist-entryp11990,531076 (defun include-book-alistp1 (x local-markers-allowedp)include-book-alistp112002,531531 (defun include-book-alistp (x local-markers-allowedp)include-book-alistp12017,532080 (defrec cert-objcert-obj12027,532515 (defun check-sum-cert-obj (cmds pre-alist post-alist expansion-alist)check-sum-cert-obj12041,533093 (defun chk-raise-portcullis (file1 file2 ch light-chkp callerchk-raise-portcullis12052,533600 (defun chk-certificate-file1 (file1 file2 ch light-chkp callerchk-certificate-file112273,543906 (defun certificate-file (full-book-name state)certificate-file12386,549277 (defun chk-certificate-file (file1 dir caller ctx statechk-certificate-file12394,549621 (defun equal-modulo-hidden-defpkgs (cmds1 cmds2)equal-modulo-hidden-defpkgs12488,554618 (defun cert-obj-for-convert (full-book-name dir pre-alist fixed-cmdscert-obj-for-convert12506,555321 (defun symbol-name-equal (x str)symbol-name-equal12553,557980 (defun chk-acceptable-certify-book1 (file dir k cmds cert-obj cbds nameschk-acceptable-certify-book112558,558111 (defun translate-book-names (filenames cbd state acc)translate-book-names12669,563300 (defun fix-ttags (ttags ctx cbd state seen acc)fix-ttags12683,563889 (defun chk-well-formed-ttags (ttags cbd ctx state)chk-well-formed-ttags12723,565806 (defun check-certificate-file-exists (full-book-name cert-op ctx state)check-certificate-file-exists12734,566215 (defun chk-acceptable-certify-book (book-name full-book-name dirchk-acceptable-certify-book12768,567998 (defun print-objects (lst ch state)print-objects12933,576839 (defun replace-initial-substring (s old old-length new)replace-initial-substring12938,577015 (defun replace-string-prefix-in-tree (tree old old-length new)replace-string-prefix-in-tree12951,577489 (defmacro with-output-object-channel-sharing (chan filename bodywith-output-object-channel-sharing12963,578070 (defun elide-locals-and-split-expansion-alist (alist acl2x-alist x y)elide-locals-and-split-expansion-alist13000,579494 (defun make-certificate-file1 (file portcullis certification-file post-alist3make-certificate-file113046,581998 (defun make-certificate-file-relocated (file portcullis certification-filemake-certificate-file-relocated13125,585355 (defun make-certificate-file (file portcullis post-alist1 post-alist2make-certificate-file13150,586478 (defun make-certificate-files (full-book-name portcullis post-alist1make-certificate-files13295,593820 (defun open-input-object-file (file ctx state)open-input-object-file13338,595885 (defun read-object-file1 (channel state ans)read-object-file113360,596767 (defun read-object-file (file ctx state)read-object-file13372,597221 (defun chk-cert-annotationschk-cert-annotations13389,598053 (defun chk-cert-annotations-post-alistchk-cert-annotations-post-alist13457,601356 (defun chk-input-object-file (file ctx state)chk-input-object-file13544,605446 (defun include-book-dir (dir state)include-book-dir13558,606082 (defmacro include-book-dir-with-chk (soft-or-hard ctx dir)include-book-dir-with-chk13570,606643 (defun newly-defined-top-level-fns-rec (trips collect-p full-book-name acc)newly-defined-top-level-fns-rec13589,607592 (defun newly-defined-top-level-fns (old-wrld new-wrld full-book-name)newly-defined-top-level-fns13623,609206 (defun accumulate-post-alist (post-alist include-book-alist)accumulate-post-alist13654,610378 (defun skipped-proofsp-in-post-alist (post-alist)skipped-proofsp-in-post-alist13673,611248 (defun check-sum-cert (portcullis-cmds expansion-alist book-ev-lst)check-sum-cert13692,611925 (defmacro with-hcomp-bindings (do-it form)with-hcomp-bindings13705,612462 (defmacro with-hcomp-bindings (do-it form)with-hcomp-bindings13710,612558 (defmacro with-hcomp-ht-bindings (form)with-hcomp-ht-bindings13727,613172 (defmacro with-hcomp-ht-bindings (form)with-hcomp-ht-bindings13731,613238 (defun get-declaim-list (state)get-declaim-list13764,614784 (defun tilde-@-book-stack-msg (reason load-compiled-stack)tilde-@-book-stack-msg13770,614908 (defun convert-book-name-to-acl2x-name (x)convert-book-name-to-acl2x-name13802,616461 (defun acl2x-alistp (x index len)acl2x-alistp13814,616860 (defun read-acl2x-file (acl2x-file full-book-name len acl2x ctx state)read-acl2x-file13824,617128 (defun eval-port-file (full-book-name ctx state)eval-port-file13885,619738 (defun getenv! (str state)getenv!13958,623101 (defun update-pcert-books (full-book-name pcert-p wrld)update-pcert-books13967,623347 (defun convert-non-nil-symbols-to-keywords (x)convert-non-nil-symbols-to-keywords13975,623603 (defun include-book-fn1 (user-book-name stateinclude-book-fn113983,623891 (defun include-book-fn (user-book-name stateinclude-book-fn14708,660985 (defun spontaneous-decertificationp1 (ibalist alist files)spontaneous-decertificationp114810,665517 (defun spontaneous-decertificationp (alist1 alist2)spontaneous-decertificationp14856,667669 (defun remove-duplicates-equal-from-end (lst acc)remove-duplicates-equal-from-end14866,668090 (defun include-book-alist-subsetp-failure-witnesses (alist1 strip-cddrs-alist2 acc)include-book-alist-subsetp-failure-witnesses14872,668353 (defun expansion-filename (full-book-name convert-to-os-p state)expansion-filename14982,674684 (defun write-expansion-file (portcullis-cmds declaim-list new-fns-execwrite-expansion-file14999,675320 (defun collect-ideal-user-defuns1 (tl wrld ans)collect-ideal-user-defuns115174,683717 (defun collect-ideal-user-defuns (wrld)collect-ideal-user-defuns15203,684777 (defun set-difference-eq-sorted (lst1 lst2 ans)set-difference-eq-sorted15210,685016 (defun expansion-alist-pkg-names0 (x base-kpa acc)expansion-alist-pkg-names015223,685549 (defun hons-union-ordered-string-lists (x y)hons-union-ordered-string-lists15238,686070 (defun expansion-alist-pkg-names-memoize (x)expansion-alist-pkg-names-memoize15255,686612 (defun expansion-alist-pkg-names (x base-kpa)expansion-alist-pkg-names15265,686923 (defun delete-names-from-kpa (names kpa)delete-names-from-kpa15296,688354 (defun print-certify-book-step-2 (ev-lst expansion-alist pcert0-file acl2x-fileprint-certify-book-step-215305,688636 (defun print-certify-book-step-3 (state)print-certify-book-step-315335,690334 (defun print-certify-book-guards-warningprint-certify-book-guards-warning15345,690756 (defun chk-certify-book-step-3 (post-alist2 post-alist1 ctx state)chk-certify-book-step-315373,691833 (defun print-certify-book-step-4 (full-book-name expansion-filename cert-opprint-certify-book-step-415437,695539 (defun print-certify-book-step-5 (full-book-name state)print-certify-book-step-515453,696221 (defun hcomp-build-from-portcullis (trips state)hcomp-build-from-portcullis15460,696478 (defstub acl2x-expansion-alist (expansion-alist state)acl2x-expansion-alist15559,701995 (defun hons-copy-with-state (x state)hons-copy-with-state15570,702470 (defun identity-with-state (x state)identity-with-state15575,702596 (defun write-acl2x-file (expansion-alist acl2x-file ctx state)write-acl2x-file15590,703063 (defun merge-into-expansion-alist1 (acl2x-expansion-alistmerge-into-expansion-alist115611,703822 (defun acl2x-alistp-domains-subsetp (x y)acl2x-alistp-domains-subsetp15638,705307 (defun merge-into-expansion-alist (acl2x-expansion-alistmerge-into-expansion-alist15651,705696 (defun restrict-expansion-alist (index expansion-alist)restrict-expansion-alist15677,706940 (defun elide-locals-from-expansion-alist (alist acc)elide-locals-from-expansion-alist15689,707351 (defun write-port-file (full-book-name cmds ctx state)write-port-file15705,708019 (defmacro save-parallelism-settings (form)save-parallelism-settings15734,709029 (defun include-book-alist-equal-modulo-local (old-post-alist new-post-alist)include-book-alist-equal-modulo-local15747,709479 (defun copy-object-channel-until-marker (marker ch-from ch-to state)copy-object-channel-until-marker15769,710671 (defun copy-pcert0-to-pcert1 (from to ctx state)copy-pcert0-to-pcert115779,711075 (defun touch? (filename old-filename ctx state)touch?15810,712549 (defun convert-book-name-to-compiled-name (full-book-name state)convert-book-name-to-compiled-name15847,714260 (defun certify-book-finish-convert (new-post-alist old-post-alistcertify-book-finish-convert15857,714666 (defun delete-cert-files (full-book-name)delete-cert-files15890,716369 (defun include-book-alist-uncertified-books (alist acc state)include-book-alist-uncertified-books15900,716728 (defun count-forms-in-channel (ch state n)count-forms-in-channel15937,718442 (defun skip-forms-in-channel (n ch state)skip-forms-in-channel15943,718651 (defun post-alist-from-pcert1-1 (n first-try-p pcert1-file msg ctx state)post-alist-from-pcert1-115950,718931 (defun post-alist-from-pcert1 (pcert1-file msg ctx state)post-alist-from-pcert115998,720639 (defun certificate-post-alist (pcert1-file cert-file full-book-name ctx state)certificate-post-alist16015,721113 (defun certify-book-finish-complete (full-book-name ctx state)certify-book-finish-complete16031,721858 (defun expansion-alist-conflict (acl2x-expansion-alistexpansion-alist-conflict16085,724228 (defun chk-absstobj-invariants (extra-msg state)chk-absstobj-invariants16117,725931 (defun certify-book-fn (user-book-name k compile-flg defaxioms-okpcertify-book-fn16172,728502 (defmacro certify-book (user-book-namecertify-book16994,777340 (defmacro certify-book! (user-book-name &optionalcertify-book!17239,791239 (defdoc provisional-certificationprovisional-certification17273,792650 (deflabel pathnamepathname17596,811188 (deflabel book-examplebook-example17641,813538 (deflabel full-book-namefull-book-name17812,822493 (deflabel book-namebook-name17857,824340 (deflabel book-contentsbook-contents17948,828591 (deflabel certificatecertificate18026,831898 (deflabel portcullisportcullis18109,836292 (deflabel versionversion18221,842399 (deflabel keepkeep18315,847402 (deflabel uncertified-booksuncertified-books18348,848972 (deflabel books-certification-classicbooks-certification-classic18411,852428 (defdoc books-certificationbooks-certification18656,864258 (defun redundant-defchoosep (name event-form wrld)redundant-defchoosep18924,876235 (defun chk-arglist-for-defchoose (args bound-vars-flg ctx state)chk-arglist-for-defchoose18938,876844 (defun defchoose-constraint-basic (fn bound-vars formals tbody ctx wrld state)defchoose-constraint-basic18956,877678 (defun generate-variable-lst-simple (var-lst avoid-lst)generate-variable-lst-simple18997,879019 (defun defchoose-constraint-extra (fn bound-vars formals body)defchoose-constraint-extra19014,879672 (defun defchoose-constraint (fn bound-vars formals body tbody strengthen ctxdefchoose-constraint19089,883545 (defun defchoose-fn (def state event-form)defchoose-fn19102,884120 (defun non-acceptable-defun-sk-p (name args body doc quant-ok rewrite exists-p)non-acceptable-defun-sk-p19272,892554 (defmacro defun-sk (name args bodydefun-sk19352,896554 (deflabel forallforall19725,913335 (deflabel existsexists19739,913726 (deflabel defun-sk-exampledefun-sk-example19753,914121 (defdoc quantifier-tutorialquantifier-tutorial19849,917615 (deflabel quantifiersquantifiers20452,945117 (deflabel quantifiers-using-recursionquantifiers-using-recursion20489,946876 (deflabel quantifiers-using-defun-skquantifiers-using-defun-sk20520,947709 (deflabel quantifiers-using-defun-sk-extendedquantifiers-using-defun-sk-extended20570,949335 (defun doublet-style-symbol-to-symbol-alistp (x)doublet-style-symbol-to-symbol-alistp20664,952465 (defun chk-legal-defstobj-name (name state)chk-legal-defstobj-name20677,952991 (defun chk-unrestricted-guards-for-user-fns (names wrld ctx state)chk-unrestricted-guards-for-user-fns20690,953471 (defconst *expt2-28* (expt 2 28))*expt2-28*20703,954018 (defun fix-stobj-array-type (type wrld)fix-stobj-array-type20705,954053 (defun chk-stobj-field-descriptor (name field-descriptor ctx wrld state)chk-stobj-field-descriptor20727,954907 (defun chk-acceptable-defstobj-renamingchk-acceptable-defstobj-renaming20911,964074 (defun defconst-name (name)defconst-name21041,970316 (defun chk-acceptable-defstobj1chk-acceptable-defstobj121046,970438 (defconst *defstobj-keywords**defstobj-keywords*21139,975418 (defun defstobj-redundancy-bundle (args)defstobj-redundancy-bundle21142,975492 (defun old-defstobj-redundancy-bundle (name wrld)old-defstobj-redundancy-bundle21176,976560 (defun redundant-defstobjp (name args wrld)redundant-defstobjp21189,977051 (defun congruent-stobj-fields (fields1 fields2)congruent-stobj-fields21215,978410 (defun chk-acceptable-defstobj (name args ctx wrld state)chk-acceptable-defstobj21226,978843 (defun defstobj-fields-template (field-descriptors renaming wrld)defstobj-fields-template21394,986669 (defun defstobj-template (name args wrld)defstobj-template21440,988670 (defun defstobj-component-recognizer-calls (ftemps n var ans)defstobj-component-recognizer-calls21486,990817 (defun defstobj-component-recognizer-axiomatic-defs (name template ftemps wrld)defstobj-component-recognizer-axiomatic-defs21513,991961 (defun defstobj-field-fns-axiomatic-defs (top-recog var n ftemps wrld)defstobj-field-fns-axiomatic-defs21585,995225 (defun defstobj-axiomatic-init-fields (ftemps wrld)defstobj-axiomatic-init-fields21699,1001003 (defun defstobj-creator-fn (creator-name field-templates wrld)defstobj-creator-fn21723,1001945 (defun defstobj-axiomatic-defs (name template wrld)defstobj-axiomatic-defs21733,1002237 (defun simple-array-type (array-etype dimensions)simple-array-type21772,1004024 (defun-one-output stobj-copy-array-aref (a1 a2 i n)stobj-copy-array-aref21784,1004409 (defun-one-output stobj-copy-array-svref (a1 a2 i n)stobj-copy-array-svref21802,1005120 (defun-one-output stobj-copy-array-fix-aref (a1 a2 i n)stobj-copy-array-fix-aref21817,1005577 (defmacro live-stobjp (name)live-stobjp21834,1006252 (defun array-etype-is-fixnum-type (array-etype)array-etype-is-fixnum-type21844,1006684 (defun defstobj-field-fns-raw-defs (var flush-var inline n ftemps)defstobj-field-fns-raw-defs21880,1008073 (defun defstobj-raw-init-fields (ftemps)defstobj-raw-init-fields22061,1016765 (defun defstobj-raw-init (template)defstobj-raw-init22118,1019264 (defun defstobj-raw-defs (name template congruent-stobj-rep wrld)defstobj-raw-defs22126,1019509 (defun put-stobjs-in-and-outs1 (name ftemps wrld)put-stobjs-in-and-outs122173,1021519 (defun put-stobjs-in-and-outs (name template wrld)put-stobjs-in-and-outs22219,1023185 (defun defconst-name-alist (lst n)defconst-name-alist22272,1025502 (defun accessor-array (name field-names)accessor-array22278,1025662 (defun strip-accessor-names (x)strip-accessor-names22288,1026081 (defun defstobj-defconsts (names index)defstobj-defconsts22298,1026328 (defun defstobj-fn (name args state event-form)defstobj-fn22304,1026513 (defun absstobj-name (name type)absstobj-name22752,1050824 (defmacro defabsstobj (&whole event-formdefabsstobj22781,1051980 (defmacro defabsstobj (&whole event-formdefabsstobj22892,1056670 (defun concrete-stobj (st wrld)concrete-stobj23549,1089101 (defmacro defabsstobj-missing-events (&whole event-formdefabsstobj-missing-events23557,1089380 (defun redundant-defabsstobjp (name event-form wrld)redundant-defabsstobjp23603,1091459 (defun absstobj-correspondence-concl-lst (stobjs-out i st$c corr-fn)absstobj-correspondence-concl-lst23607,1091621 (defun absstobj-correspondence-formula (f$a f$c corr-fn formals guard-pre stabsstobj-correspondence-formula23618,1092149 (defun absstobj-preserved-formula (f$a f$c formals guard-pre st st$c st$ap wrld)absstobj-preserved-formula23669,1094185 (defrec absstobj-methodabsstobj-method23719,1096067 (defun fn-stobj-updates-p (st fn wrld)fn-stobj-updates-p23738,1096780 (defun stobj-updates-p (st term wrld)stobj-updates-p23762,1097470 (defun stobj-updates-listp (st x wrld)stobj-updates-listp23837,1100270 (defun unprotected-export-p (st$c name wrld)unprotected-export-p23847,1100625 (defun translate-absstobj-field (st st$c field type protect-defaulttranslate-absstobj-field23851,1100770 (defun simple-translate-absstobj-fields (st st$c fields types protect-defaultsimple-translate-absstobj-fields24197,1120041 (defun one-way-unify-p (pat term)one-way-unify-p24219,1120996 (defun obviously-iff-equiv-terms (x y)obviously-iff-equiv-terms24229,1121259 (defun chk-defabsstobj-method-lemmas (method st st$c st$ap corr-fnchk-defabsstobj-method-lemmas24274,1123278 (defun chk-defabsstobj-method (method st st$c st$ap corr-fn congruent-tochk-defabsstobj-method24387,1128794 (defun chk-acceptable-defabsstobj1 (st st$c st$ap corr-fn fieldschk-acceptable-defabsstobj124411,1129851 (defun first-keyword (lst)first-keyword24458,1131815 (defun chk-acceptable-defabsstobj (name st$c recognizer st$ap creator corr-fnchk-acceptable-defabsstobj24465,1132002 (defun defabsstobj-axiomatic-defs (st$c methods)defabsstobj-axiomatic-defs24550,1135935 (defun defabsstobj-raw-def (method)defabsstobj-raw-def24570,1136982 (defun defabsstobj-raw-defs-rec (methods)defabsstobj-raw-defs-rec24620,1139114 (defun defabsstobj-raw-defs (st-name methods)defabsstobj-raw-defs24628,1139331 (defun expand-recognizer (st-name recognizer see-doc ctx state)expand-recognizer24664,1140962 (defun put-absstobjs-in-and-outs (st methods wrld)put-absstobjs-in-and-outs24679,1141625 (defun method-exec (name methods)method-exec24697,1142484 (defun defabsstobj-raw-init (creator-name methods)defabsstobj-raw-init24706,1142819 (defun defabsstobj-missing-msg (missing wrld)defabsstobj-missing-msg24709,1142913 (defun update-guard-post (logic-subst methods)update-guard-post24733,1144014 (defun defabsstobj-logic-subst (methods)defabsstobj-logic-subst24753,1145120 (defun chk-defabsstobj-guard (method ctx wrld state-vars)chk-defabsstobj-guard24759,1145380 (defun chk-defabsstobj-guards1 (methods msg ctx wrld state-vars)chk-defabsstobj-guards124784,1146659 (defun chk-defabsstobj-guards (methods congruent-to ctx wrld state)chk-defabsstobj-guards24800,1147393 (defun make-absstobj-logic-exec-pairs (methods)make-absstobj-logic-exec-pairs24812,1147973 (defun defabsstobj-fn1 (st-name st$c recognizer creator corr-fn exportsdefabsstobj-fn124818,1148257 (defun defabsstobj-fn (st-name st$c recognizer creator corr-fn exportsdefabsstobj-fn25035,1158872 (deflabel stobjstobj25055,1159937 (deflabel stobj-example-1stobj-example-125158,1165330 (deflabel declare-stobjsdeclare-stobjs25391,1174588 (deflabel stobj-example-1-defunsstobj-example-1-defuns25442,1176828 (deflabel stobj-example-1-implementationstobj-example-1-implementation25561,1181473 (deflabel stobj-example-1-proofsstobj-example-1-proofs25636,1184285 (deflabel stobj-example-2stobj-example-225786,1189845 (deflabel stobj-example-3stobj-example-325880,1192929 (defdoc resize-listresize-list26118,1201956 (defun-one-output mv-let-for-with-local-stobj (mv-let-form st creator flet-fns w)mv-let-for-with-local-stobj26132,1202476 (defmacro with-local-stobj (&rest args)with-local-stobj26198,1205849 (deflabel with-local-stobjwith-local-stobj26434,1212096 (defun create-state ()create-state26514,1215202 (defmacro with-local-state (mv-let-form)with-local-state26518,1215299 (defdoc nested-stobjsnested-stobjs26807,1227050 (defmacro stobj-let (&whole x &rest args)stobj-let27371,1252039 (defun push-untouchable-fn (name fn-p state doc event-form)push-untouchable-fn27380,1252234 (defun remove-untouchable-fn (name fn-p state doc event-form)remove-untouchable-fn27435,1254315 (defun def-body-lemmas (def-bodies lemmas)def-body-lemmas27490,1256499 (defmacro show-bodies (fn)show-bodies27498,1256809 (defun set-body-fn1 (rune def-bodies acc)set-body-fn127548,1258911 (defun set-body-fn (fn name-or-rune state event-form)set-body-fn27558,1259277 (defdoc tracetrace27604,1260936 (defparameter *trace-evisc-tuple**trace-evisc-tuple*27632,1262075 (defparameter *trace-evisc-tuple-world**trace-evisc-tuple-world*27635,1262117 (defun trace-evisc-tuple ()trace-evisc-tuple27638,1262165 (defun trace-multiplicity (name state)trace-multiplicity27647,1262434 (defun first-trace-printing-column (state)first-trace-printing-column27660,1262787 (defun trace-ppr (x trace-evisc-tuple msgp state)trace-ppr27676,1263276 (defvar *inside-trace$* nil)*inside-trace$*27687,1263576 (defun custom-trace-ppr (direction x &optional evisc-tuple msgp)custom-trace-ppr27690,1263623 (defun *1*defp (trace-spec wrld)*1*defp27751,1266221 (defun trace$-er-msg (fn)trace$-er-msg27756,1266367 (defun decls-and-doc (forms)decls-and-doc27760,1266463 (defun trace$-when-gcond (gcond form)trace$-when-gcond27770,1266739 (defun stobj-evisceration-alist (user-stobj-alist state)stobj-evisceration-alist27775,1266828 (defun trace-evisceration-alist (state)trace-evisceration-alist27783,1267223 (defun set-trace-evisc-tuple (val state)set-trace-evisc-tuple27787,1267381 (defun chk-trace-options-aux (form kwd formals ctx wrld state)chk-trace-options-aux27863,1270425 (defun trace$-value-msgp (x)trace$-value-msgp27890,1271607 (defun chk-trace-options (fn predefined trace-options formals ctx wrld state)chk-trace-options27903,1272054 (defun memoize-off-trace-error (fn ctx)memoize-off-trace-error27997,1276165 (defun untrace$-fn1 (fn state)untrace$-fn128005,1276526 (defun untrace$-rec (fns ctx state)untrace$-rec28056,1278492 (defun untrace$-fn (fns state)untrace$-fn28075,1279142 (defun maybe-untrace$-fn (fn state)maybe-untrace$-fn28086,1279533 (defmacro maybe-untrace$ (fn)maybe-untrace$28095,1279837 (defmacro maybe-untrace (fn)maybe-untrace28099,1279920 (defun maybe-untrace! (fn &optional verbose)maybe-untrace!28109,1280186 (defun increment-trace-level ()increment-trace-level28133,1280925 (defun trace$-def (arglist def trace-options predefined multiplicity ctx)trace$-def28139,1281106 (defun trace$-install (fn formals def trace-options predefined multiplicitytrace$-install28315,1289292 (defun oneified-def (fn wrld &optional trace-rec-for-none)oneified-def28347,1290614 (defun trace$-fn-general (trace-spec ctx state)trace$-fn-general28363,1291233 (defun trace$-fn-simple (trace-spec ctx state)trace$-fn-simple28555,1300443 (defconst *trace-keywords**trace-keywords*28558,1300542 (defconst *trace-keywords-needing-ttag**trace-keywords-needing-ttag*28567,1300790 (defun all-keywords-p (keywords)all-keywords-p28574,1301013 (defun first-assoc-keyword (keys x)first-assoc-keyword28580,1301173 (defconst *illegal-trace-spec-fmt-string**illegal-trace-spec-fmt-string*28588,1301452 (defun trace$-fn (trace-spec ctx state)trace$-fn28593,1301680 (defun trace$-lst (trace-spec-lst ctx state)trace$-lst28627,1303472 (defmacro trace$ (&rest trace-specs)trace$28638,1303817 (defmacro with-ubt! (form)with-ubt!29166,1327433 (defmacro trace! (&rest fns)trace!29186,1328249 (defmacro untrace$ (&rest fns)untrace$29427,1335744 (defun open-trace-file-fn (filename state)open-trace-file-fn29451,1336462 (defmacro open-trace-file (filename)open-trace-file29477,1337562 (defun close-trace-file-fn (quiet-p state)close-trace-file-fn29498,1338187 (defmacro close-trace-file ()close-trace-file29511,1338689 (defmacro break-on-error (&optional (on 't))break-on-error29527,1339158 (defun defexec-extract-key (x keyword result result-p)defexec-extract-key29628,1344071 (defun parse-defexec-dcls-1 (alist guard guard-p hints hints-p measureparse-defexec-dcls-129650,1345115 (defun fix-exec-xargs (exec-xargs hints hints-pfix-exec-xargs29773,1350556 (defun parse-defexec-dcls (dcls-and-strings finalparse-defexec-dcls29802,1351805 (defmacro defexec (&whole whole fn formals &rest rest)defexec29874,1355405 (defrec sar ; single-applicable-rewritesar30129,1366734 (defun applicable-rewrite-rules1 (term geneqv lemmas current-indexapplicable-rewrite-rules130144,1367578 (defun applicable-linear-rules1 (term lemmas current-index target-name-or-runeapplicable-linear-rules130204,1370170 (defun pc-relieve-hyp (rune hyp unify-subst type-alist wrld state ens ttree)pc-relieve-hyp30256,1372238 (defun pc-relieve-hyps1-iter (rune hyps unify-subst-lst unify-substpc-relieve-hyps1-iter30342,1375846 (defun pc-relieve-hyps1 (rune hyps unify-subst unify-subst0 ttree0 type-alistpc-relieve-hyps130363,1376832 (defun pc-relieve-hyps (rune hyps unify-subst type-alist keep-unify-subst wrldpc-relieve-hyps30414,1379235 (defun remove-trivial-lits (lst type-alist alist wrld ens ttree)remove-trivial-lits30427,1379834 (defun unrelieved-hyps (rune hyps unify-subst type-alist keep-unify-subst wrldunrelieved-hyps30450,1380883 (defun untranslate-subst-abb (sub abbreviations state)untranslate-subst-abb30474,1381899 (defun show-rewrite-linear (caller index col rune nume show-more subst-hypsshow-rewrite-linear30481,1382167 (defun show-rewrites-linears (caller app-rules col abbreviationsshow-rewrites-linears30574,1386888 (defun expand-assumptions-1 (term)expand-assumptions-130647,1389987 (defun expand-assumptions (x)expand-assumptions30662,1390631 (defun hyps-type-alist (assumptions ens wrld state)hyps-type-alist30672,1390862 (defun show-rewrites-linears-fn (caller rule-id enabled-only-flg ensshow-rewrites-linears-fn30686,1391523 (defun show-meta-lemmas1 (lemmas rule-id term wrld ens state)show-meta-lemmas130786,1396269 (defun show-meta-lemmas (term rule-id state)show-meta-lemmas30843,1398899 (defun decoded-type-set-from-tp-rule (tp unify-subst wrld ens)decoded-type-set-from-tp-rule30853,1399314 (defun show-type-prescription-rule (rule unify-subst type-alist abbreviationsshow-type-prescription-rule30873,1400078 (defun show-type-prescription-rules1 (rules term rule-id type-alistshow-type-prescription-rules130913,1402093 (defun show-type-prescription-rules (term rule-id abbreviations all-hyps ensshow-type-prescription-rules30935,1403133 (defun pl2-fn (form rule-id caller state)pl2-fn30967,1404559 (defun pl-fn (name state)pl-fn31025,1407476 (defmacro pl (name)pl31062,1408989 (defmacro pl2 (form rule-id)pl231108,1411204 (defun acl2-defaults-table-local-ctx-p (state)acl2-defaults-table-local-ctx-p31194,1416001 (defun add-include-book-dir-fn (keyword dir state)add-include-book-dir-fn31201,1416264 (defun delete-include-book-dir-fn (keyword state)delete-include-book-dir-fn31293,1420703 (defun add-custom-keyword-hint-fn (key uterm1 uterm2 state)add-custom-keyword-hint-fn31348,1423217 (defmacro reset-prehistory (&rest args)reset-prehistory31436,1427132 (defmacro reset-prehistory (&whole event-form &optional permanent-p doc)reset-prehistory31441,1427223 (defun checkpoint-world (flushp state)checkpoint-world31497,1429592 (defvar *checkpoint-world-len-and-alist-stack**checkpoint-world-len-and-alist-stack*31504,1429698 (defmacro checkpoint-world-len-and-alist ()checkpoint-world-len-and-alist31507,1429753 (defun checkpoint-world1 (flushp wrld state)checkpoint-world131510,1429847 (defun checkpoint-world (flushp state)checkpoint-world31536,1430954 (defun reset-kill-ring (n state)reset-kill-ring31548,1431233 (defun reset-prehistory-fn (permanent-p state doc event-form)reset-prehistory-fn31587,1432873 (defun memoize-table-chk-commutative (str fn val ctx wrld)memoize-table-chk-commutative31649,1435700 (defun memoize-table-chk (key val wrld)memoize-table-chk31700,1438151 (defun remove-stobjs-in-by-position (lst stobjs-in)remove-stobjs-in-by-position31901,1447944 (defun alist-to-doublets (alist)alist-to-doublets31912,1448425 (defun print-gv1 (fn-guard-stobjsin-args state)print-gv131918,1448631 (defun print-gv-fn (evisc-tuple state)print-gv-fn31940,1449367 (defmacro print-gv (&key (evisc-tupleprint-gv31970,1450553 (defun disable-iprint-ar (state)disable-iprint-ar32132,1455983 (defun enable-iprint-ar (state)enable-iprint-ar32155,1457139 (defconst *iprint-actions**iprint-actions*32173,1457934 (defun set-iprint-fn1 (x state)set-iprint-fn132176,1458001 (defun set-iprint-fn (x state)set-iprint-fn32205,1459019 (defun set-iprint-hard-bound (n ctx state)set-iprint-hard-bound32217,1459482 (defun set-iprint-soft-bound (n ctx state)set-iprint-soft-bound32230,1459953 (defmacro set-iprint (&optional (action ':RESET ; default ignoredset-iprint32243,1460424 (defconst *evisc-tuple-sites**evisc-tuple-sites*32474,1470523 (defun set-site-evisc-tuple (site evisc-tuple ctx state)set-site-evisc-tuple32477,1470595 (defun chk-evisc-tuple (evisc-tuple ctx state)chk-evisc-tuple32507,1472083 (defun set-evisc-tuple-lst (keys evisc-tuple acc ctx state)set-evisc-tuple-lst32516,1472408 (defun set-evisc-tuple-fn1 (keys all-keys evisc-tuple acc ctx state)set-evisc-tuple-fn132527,1472856 (defun iprint-virginp (state)iprint-virginp32570,1474813 (defun set-evisc-tuple-fn (evisc-tupleset-evisc-tuple-fn32577,1475078 (defmacro set-evisc-tuple (evisc-tupleset-evisc-tuple32643,1478101 (defmacro top-level (form &rest declares)top-level32757,1483834 (defun translate-defattach-helpers (kwd-value-lst name-tree ctx wrld state)translate-defattach-helpers33427,1520681 (defconst *defattach-keys**defattach-keys*33485,1523125 (defun defattach-unknown-constraints-error (name wrld ctx state)defattach-unknown-constraints-error33491,1523262 (defun intersection-domains (a1 a2)intersection-domains33503,1523805 (defun process-defattach-args1 (args ctx wrld state erasures explicit-erasuresprocess-defattach-args133513,1524126 (defun duplicate-keysp-eq (alist)duplicate-keysp-eq33814,1539819 (defun split-at-first-keyword (args)split-at-first-keyword33824,1540121 (defun filter-for-attachment (attachment-alist helpers-lst attach-by-defaultfilter-for-attachment33839,1540621 (defconst *defattach-keys-plus-skip-checks**defattach-keys-plus-skip-checks*33861,1541756 (defun process-defattach-args (args ctx state)process-defattach-args33864,1541841 (defun prove-defattach-guards1 (i n attachment-alist-tail attachment-alistprove-defattach-guards133993,1547875 (defun prove-defattach-guards (attachment-alist helpers-lst ctx ens wrld state)prove-defattach-guards34059,1551022 (defun defattach-constraint-rec (alist full-alist proved-fnl-insts-alistdefattach-constraint-rec34088,1552293 (defun defattach-constraint (attachment-alist proved-fnl-insts-alist wrld ctxdefattach-constraint34157,1555441 (defun prove-defattach-constraint (goal event-names attachment-alistprove-defattach-constraint34167,1555909 (defun attachment-component-owner (g path)attachment-component-owner34418,1569099 (defun intersection1-eq (x y)intersection1-eq34447,1570573 (defun defattach-component-has-owner (g g0 comps)defattach-component-has-owner34456,1570906 (defun defattach-merge-into-component (g0 ext-succ0 comps0 ext-succ1 g1defattach-merge-into-component34472,1571698 (defun defattach-merge-components (g0 ext-succ0 comps0 ext-succ1 g1 comps1defattach-merge-components34588,1575664 (defun defattach-merge (r0 r1)defattach-merge34630,1577580 (defun defattach-merge-lst (r lst changedp)defattach-merge-lst34654,1578547 (defun defattach-merge-lst-lst (to-do done changedp)defattach-merge-lst-lst34672,1579310 (defun defattach-loop-error-msg (loop end)defattach-loop-error-msg34698,1580600 (defun defattach-loop-error (loop ctx state)defattach-loop-error34707,1580916 (defun defattach-close (records ctx state)defattach-close34714,1581199 (defun defattach-erase-components (components canonical-erased-fs)defattach-erase-components34723,1581533 (defun defattach-erase-p (record erasures canonical-erased-fs)defattach-erase-p34754,1583154 (defun defattach-erase1 (records attachments erasures canonical-erased-fsdefattach-erase134772,1583986 (defun defattach-erase (records attachments erasures wrld)defattach-erase34807,1585486 (defun collect-ext-anc (f records)collect-ext-anc34827,1586503 (defun extend-attachment-components (comps g0 ext-succ f g)extend-attachment-components34838,1586888 (defun component-path-extension (f comps)component-path-extension34878,1588937 (defun extend-attachment-record (pair f-canon g-canon rec)extend-attachment-record34895,1589769 (defun update-attachment-records1 (pair f-canon g-canon records)update-attachment-records134932,1591444 (defun update-attachment-records (pair f-canon g-canon records wrld ctx state)update-attachment-records34963,1592984 (defun attachment-records (attachments records wrld ctx state)attachment-records35000,1594900 (defun chk-defattach-loop (attachments erasures wrld ctx state)chk-defattach-loop35016,1595511 (defun defaxiom-supporter-msg-list (symbols wrld)defaxiom-supporter-msg-list35051,1597100 (defun chk-acceptable-defattach (args proved-fnl-insts-alist ctx wrld state)chk-acceptable-defattach35061,1597604 (defun attachment-cltl-cmd (erasures alist)attachment-cltl-cmd35172,1603085 (defun defattach-fn (args state event-form)defattach-fn35186,1603649 (defun chk-return-last-entry (key val wrld)chk-return-last-entry35269,1607992 (defdoc return-last-tablereturn-last-table35331,1610882 (defmacro defmacro-last (fn &key raw (top-level-ok 't))defmacro-last35357,1612184 (defdoc printing-to-stringsprinting-to-strings35380,1613081 (defconst *fmt-control-defaults**fmt-control-defaults*35438,1615924 (defconst *fixed-fmt-controls**fixed-fmt-controls*35462,1617036 (defun fmt-control-bindings1 (alist fmt-control-defaults-tail)fmt-control-bindings135471,1617296 (defun fmt-control-bindings (alist)fmt-control-bindings35495,1618417 (defun set-iprint-ar (iprint-ar state)set-iprint-ar35500,1618584 (defmacro channel-to-string (form channel-varchannel-to-string35508,1618848 (defun fms-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action)fms-to-string-fn35615,1623391 (defmacro fms-to-string (str alist &key evisc-tuple fmt-control-alist iprint)fms-to-string35620,1623623 (defun fms!-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action)fms!-to-string-fn35624,1623832 (defmacro fms!-to-string (str alist &key evisc-tuple fmt-control-alist iprint)fms!-to-string35629,1624066 (defun fmt-to-string-fn (str alist evisc-tuple fmt-control-alist iprint-action)fmt-to-string-fn35633,1624277 (defmacro fmt-to-string (str alist &key evisc-tuple fmt-control-alist iprint)fmt-to-string35638,1624509 (defun fmt!-to-string-fn (str alist evisc-tuple fmt-control-alistfmt!-to-string-fn35642,1624718 (defmacro fmt!-to-string (str alist &key evisc-tuple fmt-control-alist iprint)fmt!-to-string35648,1624982 (defun fmt1-to-string-fn (str alist col evisc-tuple fmt-control-alistfmt1-to-string-fn35652,1625193 (defmacro fmt1-to-string (str alist col &key evisc-tuple fmt-control-alistfmt1-to-string35658,1625465 (defun fmt1!-to-string-fn (str alist col evisc-tuple fmt-control-alistfmt1!-to-string-fn35663,1625715 (defmacro fmt1!-to-string (str alist col &key evisc-tuple fmt-control-alistfmt1!-to-string35669,1625987 (defdoc dead-eventsdead-events35681,1626624 (defun attachment-pairs (fns wrld acc)attachment-pairs35822,1634390 (defun sibling-attachments (f wrld)sibling-attachments35834,1634726 (defun extended-ancestors4 (fns wrld fal)extended-ancestors435840,1634889 (defun extended-ancestors3 (components wrld fal)extended-ancestors335851,1635253 (defun extended-ancestors2 (canon-gs arfal wrld canon-gs-fal fal)extended-ancestors235866,1635793 (defun canonical-cdrs (alist wrld acc)canonical-cdrs35888,1636893 (defun extended-ancestors1 (fns canon-gs arfal wrld fal)extended-ancestors135895,1637144 (defun attachment-records-fal (attachment-records fal)attachment-records-fal35918,1638312 (defun extended-ancestors (f wrld)extended-ancestors35926,1638634 (defun ext-anc-attachment-missing (alist wrld)ext-anc-attachment-missing35940,1639170 (defun ext-anc-attachments-valid-p-1 (fns alist wrld)ext-anc-attachments-valid-p-135950,1639447 (defun ext-anc-attachments-valid-p (fns ext-anc-attachments wrld acc)ext-anc-attachments-valid-p35961,1639821 (defconst *inline-suffix* "$INLINE")*inline-suffix*35988,1641022 (defconst *inline-suffix-len-minus-1* (1- (length *inline-suffix*)))*inline-suffix-len-minus-1*35989,1641059 (defconst *notinline-suffix* "$NOTINLINE")*notinline-suffix*35990,1641128 (defconst *notinline-suffix-len-minus-1* (1- (length *notinline-suffix*)))*notinline-suffix-len-minus-1*35991,1641171 (defconst *non-stobj-var-root* (coerce (symbol-name 'non-stobj-var) 'list))*non-stobj-var-root*35992,1641246 (defun defun-inline-form (name formals lst defun-type suffix)defun-inline-form35994,1641323 (defmacro defun-inline (name formals &rest lst)defun-inline36024,1642574 (defmacro defund-inline (name formals &rest lst)defund-inline36142,1648069 (defmacro defun-notinline (name formals &rest lst)defun-notinline36156,1648516 (defmacro defund-notinline (name formals &rest lst)defund-notinline36178,1649564 (defun regenerate-tau-database-fn0 (user-auto-modep auto-modep ens tripsregenerate-tau-database-fn036192,1650027 (defun regenerate-tau-database-fn1regenerate-tau-database-fn136236,1652160 (defun regenerate-tau-database-fn (state doc event-form)regenerate-tau-database-fn36307,1655664 (defun rational-to-decimal-string (x state)rational-to-decimal-string36353,1657386 (defvar *time-tracker-alist* nil)*time-tracker-alist*36368,1657946 (defvar *time-tracker-disabled-p* nil)*time-tracker-disabled-p*36370,1657981 (defstruct time-trackertime-tracker36372,1658021 (defun tt-print-msg (tag msg total)tt-print-msg36401,1659307 (defun tt-init (tag times interval msg)tt-init36416,1659880 (defun tt-end (tag)tt-end36459,1661572 (defun tt-print? (tag min-time msg)tt-print?36469,1661912 (defun tt-stop (tag)tt-stop36521,1664328 (defun tt-start (tag)tt-start36543,1665191 (defun program-declared-p1 (dcls)program-declared-p136566,1666070 (defun program-declared-p (def)program-declared-p36576,1666407 (defmacro defund (&rest def)defund36588,1666826 (defmacro defund (&rest def)defund36644,1669010 (defun magic-ev-fncall-cl-proc (x)magic-ev-fncall-cl-proc36653,1669420 (defun-overrides magic-ev-fncall (fn args state hard-error-returns-nilp aok)magic-ev-fncall36678,1670109 ld.lisp,23815 (defun default-print-prompt (channel state)default-print-prompt27,1032 (defun print-prompt (prompt output-channel state)print-prompt133,5763 (defun initialize-timers (state)initialize-timers164,6996 (defun maybe-add-command-landmark (old-wrld old-default-defun-mode formmaybe-add-command-landmark171,7199 (defun replace-last-cdr (x val)replace-last-cdr229,9791 (defun ld-standard-oi-missing (val file-name ld-missing-input-ok ctx state)ld-standard-oi-missing234,9951 (defun chk-acceptable-ld-fn1-pair (pair ld-missing-input-ok ctx statechk-acceptable-ld-fn1-pair247,10578 (defun close-channels (channel-closing-alist state)close-channels396,17242 (defun chk-acceptable-ld-fn1 (alist ld-missing-input-ok ctx state co-stringchk-acceptable-ld-fn1431,18829 (defun chk-acceptable-ld-fn (alist state)chk-acceptable-ld-fn551,25219 (defun f-put-ld-specials (alist state)f-put-ld-specials592,27140 (defun f-get-ld-specials (state)f-get-ld-specials650,29553 (defun ld-read-keyword-command1 (n state)ld-read-keyword-command1690,31173 (defun exit-ld (state)exit-ld709,31897 (defun macro-minimal-arity1 (lst)macro-minimal-arity1718,32212 (defun macro-minimal-arity (sym default wrld)macro-minimal-arity725,32415 (defun ld-read-keyword-command (key state)ld-read-keyword-command731,32670 (defun ld-read-command (state)ld-read-command802,35594 (deflabel acl2-customizationacl2-customization838,37435 (deflabel keyword-commandskeyword-commands915,41586 (defun ld-print-command (keyp form col state)ld-print-command970,44057 (defun ld-filter-command (form state)ld-filter-command992,44638 (defun-one-output ppr? (x raw-x col channel state)ppr?1016,45888 (defun ld-print-results (trans-ans state)ld-print-results1029,46261 (defun ld-print-prompt (state)ld-print-prompt1138,50898 (defun good-bye-fn (status)good-bye-fn1154,51485 (defmacro good-bye (&optional (status '0))good-bye1160,51613 (defun ld-return-error (state)ld-return-error1197,52831 (defun initialize-accumulated-warnings ()initialize-accumulated-warnings1205,53087 (defun ld-read-eval-print (state)ld-read-eval-print1210,53192 (defun ld-loop (state)ld-loop1344,59381 (defvar *first-entry-to-ld-fn-body-flg*)*first-entry-to-ld-fn-body-flg*1370,60522 (defun update-cbd (standard-oi0 state)update-cbd1372,60564 (defun ld-fn-body (standard-oi0 new-ld-specials-alist state)ld-fn-body1407,62178 (defun ld-fn1 (standard-oi0 alist state bind-flg)ld-fn11502,66601 (defun ld-fn-alist (alist state)ld-fn-alist1542,68525 (defmacro with-interrupts (&rest forms)with-interrupts1574,70027 (defun ld-fn0 (alist state bind-flg)ld-fn01591,70527 (defun ld-fn (alist state bind-flg)ld-fn1776,80539 (defmacro ld (standard-oild1836,82881 (defdoc calling-ld-in-bad-contextscalling-ld-in-bad-contexts2151,100435 (defmacro quick-test nilquick-test2190,102313 (defun wormhole-prompt (channel state)wormhole-prompt2214,103197 (defun reset-ld-specials-fn (reset-channels-flg state)reset-ld-specials-fn2226,103699 (defmacro reset-ld-specials (reset-channels-flg)reset-ld-specials2239,104239 (defun maybe-reset-defaults-table1maybe-reset-defaults-table12268,105562 (defun maybe-reset-defaults-table2maybe-reset-defaults-table22306,107198 (defun maybe-reset-defaults-table (pre-defaults-tbl post-defaults-tbl state)maybe-reset-defaults-table2315,107544 (defun delete-something (lst)delete-something2320,107846 (defun store-in-kill-ring (x0 ring)store-in-kill-ring2330,108173 (defun rotate-kill-ring1 (ring xn)rotate-kill-ring12350,109189 (defun rotate-kill-ring (ring xn)rotate-kill-ring2355,109356 (defun ubt-ubu-fn1 (kwd wrld pred-wrld state)ubt-ubu-fn12367,109902 (defun ubt-ubu-fn (kwd cd state)ubt-ubu-fn2422,112013 (defun ubt!-ubu!-fn (kwd cd state)ubt!-ubu!-fn2474,114411 (defmacro ubt-prehistory ()ubt-prehistory2491,114868 (defun ubt-prehistory-fn (state)ubt-prehistory-fn2504,115317 (defun oops-warning (state)oops-warning2540,116912 (defun oops-fn (state)oops-fn2554,117482 (defmacro oops niloops2581,118793 (defmacro i-am-here ()i-am-here2678,123223 (defun rebuild-fn-read-filter (file state)rebuild-fn-read-filter2712,124337 (defun rebuild-fn (file filter filterp dir state)rebuild-fn2744,125807 (defmacro rebuild (file &optional (filter 'nil filterp)rebuild2769,126601 (defconst *basic-sweep-error-str**basic-sweep-error-str*2975,137213 (defun sweep-symbol-binding-for-bad-symbol (sym obj deceased-packages state)sweep-symbol-binding-for-bad-symbol2982,137587 (defun sweep-global-lst (l deceased-packages state)sweep-global-lst3004,138604 (defun sweep-stack-entry-for-bad-symbol (name i obj deceased-packages state)sweep-stack-entry-for-bad-symbol3013,138924 (defun sweep-t-stack (i deceased-packages state)sweep-t-stack3036,139981 (defun sweep-acl2-oracle (i deceased-packages state)sweep-acl2-oracle3044,140295 (defun sweep-global-state-for-lisp-objects (deceased-packages state)sweep-global-state-for-lisp-objects3059,140829 (deflabel compilationcompilation3105,142523 (defdoc book-compiled-filebook-compiled-file3152,144684 (deflabel escape-to-common-lispescape-to-common-lisp3244,150360 (deflabel copyrightcopyright3259,150792 (deflabel acknowledgmentsacknowledgments3287,151795 (deflabel breaksbreaks3433,159046 (deflabel ordinalsordinals3478,161199 (defmacro wet (form &rest kwd-args)wet3620,168216 (defmacro disassemble$ (fn &rest argsdisassemble$3802,176740 (deflabel release-notesrelease-notes3860,179431 (deflabel note1note13872,179751 (deflabel note2note23893,180380 (deflabel note3note33927,182007 (deflabel note4note44029,187845 (deflabel note5note54190,194885 (deflabel note6note64437,207200 (deflabel note7note74562,212573 (deflabel note8note84761,221308 (deflabel note8-updatenote8-update5108,237104 (deflabel note9note95168,239323 (deflabel note-2-0note-2-05249,242380 (deflabel note-2-1note-2-15282,243603 (deflabel note-2-2note-2-25300,243983 (deflabel note-2-3note-2-35362,246869 (deflabel note-2-4note-2-45422,249166 (deflabel note-2-5note-2-55468,250719 (deflabel |NOTE-2-5(R)||NOTE-2-55798,267123 (deflabel note-2-6note-2-65814,267420 (deflabel note-2-6-new-functionalitynote-2-6-new-functionality5833,268076 (deflabel note-2-6-proofsnote-2-6-proofs6016,277117 (deflabel note-2-6-rulesnote-2-6-rules6128,282827 (deflabel note-2-6-guardsnote-2-6-guards6199,286189 (deflabel note-2-6-proof-checkernote-2-6-proof-checker6267,289225 (deflabel note-2-6-systemnote-2-6-system6293,290134 (deflabel note-2-6-othernote-2-6-other6340,292302 (deflabel |NOTE-2-6(R)||NOTE-2-66460,297859 (deflabel note-2-7note-2-76476,298186 (deflabel note-2-7-bug-fixesnote-2-7-bug-fixes6563,301725 (deflabel note-2-7-new-functionalitynote-2-7-new-functionality6850,316186 (deflabel note-2-7-proofsnote-2-7-proofs7006,324140 (deflabel note-2-7-rulesnote-2-7-rules7108,329615 (deflabel note-2-7-guardsnote-2-7-guards7129,330535 (deflabel note-2-7-proof-checkernote-2-7-proof-checker7159,331355 (deflabel note-2-7-systemnote-2-7-system7170,331638 (deflabel note-2-7-othernote-2-7-other7240,334715 (deflabel |NOTE-2-7(R)||NOTE-2-77413,343015 (deflabel note-2-8note-2-87438,343779 (deflabel note-2-8-bug-fixesnote-2-8-bug-fixes7601,350774 (deflabel note-2-8-new-functionalitynote-2-8-new-functionality8051,371825 (deflabel note-2-8-proofsnote-2-8-proofs8197,379974 (deflabel note-2-8-rulesnote-2-8-rules8274,384223 (deflabel note-2-8-guardsnote-2-8-guards8315,385983 (deflabel note-2-8-proof-checkernote-2-8-proof-checker8326,386205 (deflabel note-2-8-systemnote-2-8-system8359,387871 (deflabel note-2-8-ordinalsnote-2-8-ordinals8388,389155 (deflabel note-2-8-othernote-2-8-other8396,389303 (deflabel |NOTE-2-8(R)||NOTE-2-88472,392455 (deflabel note-2-9note-2-98489,392928 (deflabel |NOTE-2-9(R)||NOTE-2-99077,418499 (deflabel note-2-9-1note-2-9-19092,418826 (deflabel note-2-9-2note-2-9-29306,428171 (deflabel note-2-9-3note-2-9-39528,437919 (deflabel note-2-9-3-ppr-changenote-2-9-3-ppr-change9760,448491 (deflabel note-2-9-4note-2-9-410038,457022 (deflabel note-2-9-5note-2-9-510421,476341 (deflabel note-3-0note-3-010776,493113 (deflabel |NOTE-3-0(R)||NOTE-3-010803,494131 (deflabel note-3-0-1note-3-0-110818,494434 (deflabel |NOTE-3-0-1(R)||NOTE-3-0-111145,511148 (deflabel note-3-0-2note-3-0-211160,511461 (deflabel note-3-1note-3-111675,533893 (deflabel |NOTE-3-1(R)||NOTE-3-111690,534298 (deflabel note-3-2note-3-211706,534606 (deflabel |NOTE-3-2(R)||NOTE-3-212373,563784 (deflabel note-3-2-1note-3-2-112400,564681 (deflabel |NOTE-3-2-1(R)||NOTE-3-2-112676,578164 (deflabel note-3-3note-3-312689,578362 (deflabel |NOTE-3-3(R)||NOTE-3-313192,601531 (deflabel note-3-4note-3-413205,601725 (deflabel |NOTE-3-4(R)||NOTE-3-413956,637774 (deflabel note-3-5note-3-513976,638347 (deflabel |NOTE-3-5(R)||NOTE-3-514944,684090 (deflabel note-3-6note-3-615022,687059 (deflabel |NOTE-3-6(R)||NOTE-3-615478,706481 (deflabel note-3-6-1note-3-6-115491,706673 (deflabel note-4-0note-4-015540,708588 (deflabel |NOTE-4-0(R)||NOTE-4-016581,756467 (deflabel note-4-0-wormhole-changesnote-4-0-wormhole-changes16594,756652 (deflabel note-4-1note-4-116661,759183 (deflabel |NOTE-4-1(R)||NOTE-4-116821,766352 (deflabel note-4-2note-4-216834,766542 (deflabel |NOTE-4-2(R)||NOTE-4-217393,794464 (deflabel note-4-3note-4-317406,794652 (deflabel |NOTE-4-3(R)||NOTE-4-318229,829528 (deflabel note-5-0note-5-018242,829713 (deflabel note-6-0note-6-019471,886571 (deflabel note-6-1note-6-120018,912909 (deflabel note-6-2note-6-220431,931737 (deflabel note-6-3note-6-320932,956015 (deflabel the-methodthe-method21360,976638 (deflabel lplp21455,981923 (defun-one-output compiled-function-p! (fn)compiled-function-p!21523,985513 (defun compile-function (ctx fn0 state)compile-function21535,985969 (defun getpid$ ()getpid$21597,988226 (defun-one-output tmp-filename (dir suffix)tmp-filename21621,989052 (defun keep-tmp-files (state)keep-tmp-files21647,990085 (defun comp-fn (fns gcl-flg tmp-suffix state)comp-fn21650,990156 (defmacro comp (fns)comp21765,994446 (defmacro comp (fns)comp21770,994517 (defmacro comp-gcl (fns)comp-gcl21880,1000212 (defun scan-past-deeper-event-landmarks (depth wrld)scan-past-deeper-event-landmarks21899,1000940 (defun puffable-encapsulate-p (cddr-car-wrld)puffable-encapsulate-p21919,1001696 (defun puffable-command-blockp (wrld cmd-form)puffable-command-blockp21930,1002110 (defun puffable-command-numberp (i state)puffable-command-numberp21964,1003544 (defun puff-command-block (wrld ans restore-cbd ctx state)puff-command-block21979,1004084 (defun commands-back-to (wrld1 wrld2 ans)commands-back-to22117,1010787 (defun puffed-command-sequence (cd ctx wrld state)puffed-command-sequence22132,1011338 (defun puff-fn1 (cd state)puff-fn122155,1012220 (defun puff-report (caller new-cd1 new-cd2 cd state)puff-report22246,1016803 (defun puff-fn (cd state)puff-fn22259,1017395 (defun puff*-fn11 (ptr k i j state)puff*-fn1122263,1017526 (defun puff*-fn1 (ptr k state)puff*-fn122284,1018276 (defun puff*-fn (cd state)puff*-fn22312,1019604 (defmacro puff (cd)puff22380,1022662 (defmacro puff* (cd)puff*22513,1030419 (defmacro mini-proveall nilmini-proveall22591,1033484 (defmacro exit (&optional (status '0))exit22829,1040695 (defmacro quit (&optional (status '0))quit22840,1040889 (defmacro set-guard-checking (flg)set-guard-checking22851,1041083 (defun dmr-stop-fn (state)dmr-stop-fn23127,1053147 (defmacro dmr-stop ()dmr-stop23139,1053642 (defun dmr-start-fn (state)dmr-start-fn23143,1053756 (defmacro dmr-start ()dmr-start23165,1054675 (defconst *home-page**home-page*23169,1054792 (defconst *home-page-references**home-page-references*23635,1070572 (deflabel |Pages Written Especially for the Tours||Pages23648,1071129 (deflabel |Undocumented Topic||Undocumented23671,1072279 (deflabel |Common Lisp||Common23677,1072451 (deflabel |An Example Common Lisp Function Definition||An23727,1074485 (deflabel |The Tours||The23765,1075853 (deflabel |A Flying Tour of ACL2||A23808,1077868 (deflabel |About the ACL2 Home Page||About23851,1079839 (deflabel |A Walking Tour of ACL2||A23881,1081011 (deflabel |What Is ACL2(Q)||What23908,1082057 (deflabel |About Models||About23935,1082930 (deflabel |Models in Engineering||Models23962,1083714 (deflabel |The Falling Body Model||The23987,1084315 (deflabel |Corroborating Models||Corroborating24009,1085029 (deflabel |Models of Computer Hardware and Software||Models24054,1087062 (deflabel |A Typical State||A24092,1088060 (deflabel |Functions for Manipulating these Objects||Functions24119,1088944 (deflabel |Common Lisp as a Modeling Language||Common24152,1090059 (deflabel |Analyzing Common Lisp Models||Analyzing24189,1091588 (deflabel |What is a Mathematical Logic(Q)||What24229,1093002 (deflabel |A Trivial Proof||A24254,1093819 (deflabel |What is a Mechanical Theorem Prover(Q)||What24263,1094033 (deflabel |What is a Mechanical Theorem Prover(Q) (cont)||What24285,1094841 (deflabel |ACL2 as an Interactive Theorem Prover||ACL224299,1095271 (deflabel |ACL2 as an Interactive Theorem Prover (cont)||ACL224312,1095741 (deflabel |ACL2 System Architecture||ACL224343,1097094 (deflabel |Modeling in ACL2||Modeling24368,1098215 (deflabel |Running Models||Running24394,1099011 (deflabel |Symbolic Execution of Models||Symbolic24426,1100101 (deflabel |Proving Theorems about Models||Proving24451,1100719 (deflabel |What is Required of the User(Q)||What24488,1102020 (deflabel |How Long Does It Take to Become an Effective User(Q)||How24519,1102960 (deflabel |Other Requirements||Other24541,1103888 (deflabel |The End of the Flying Tour||The24570,1104639 (deflabel |An Example of ACL2 in Use||An24587,1104985 (deflabel |How To Find Out about ACL2 Functions||How24638,1107204 (deflabel |How To Find Out about ACL2 Functions (cont)||How24668,1108368 (deflabel |The Admission of App||The24701,1109673 (deflabel |A Tiny Warning Sign||A24746,1111096 (deflabel |Revisiting the Admission of App||Revisiting24765,1111761 (deflabel |Evaluating App on Sample Input||Evaluating24806,1113188 (deflabel |Conversion||Conversion|24848,1114164 (deflabel |The Associativity of App||The24879,1115624 (deflabel |The Theorem that App is Associative||The24921,1117018 (deflabel |Free Variables in Top-Level Input||Free24953,1118304 (deflabel |The Proof of the Associativity of App||The24997,1120020 (deflabel |Name the Formula Above||Name25054,1122924 (deflabel |Perhaps||Perhaps|25063,1123189 (deflabel |Suggested Inductions in the Associativity of App Example||Suggested25072,1123424 (deflabel |Subsumption of Induction Candidates in App Example||Subsumption25095,1124095 (deflabel |Flawed Induction Candidates in App Example||Flawed25118,1124809 (deflabel |The Induction Scheme Selected for the App Example||The25141,1125467 (deflabel |The Induction Step in the App Example||The25162,1126207 (deflabel |The Base Case in the App Example||The25191,1127023 (deflabel |The Justification of the Induction Scheme||The25212,1127553 (deflabel |The Instantiation of the Induction Scheme||The25223,1127903 (deflabel |Nontautological Subgoals||Nontautological25236,1128365 (deflabel |Overview of the Simplification of the Induction Step to T||Overview25247,1128811 (deflabel |On the Naming of Subgoals||On25278,1129956 (deflabel |Overview of the Expansion of ENDP in the Induction Step||Overview25290,1130418 (deflabel |The Expansion of ENDP in the Induction Step (Step 0)||The25303,1131020 (deflabel |The Expansion of ENDP in the Induction Step (Step 1)||The25322,1131640 (deflabel |The Expansion of ENDP in the Induction Step (Step 2)||The25342,1132290 (deflabel |Overview of the Simplification of the Induction Conclusion||Overview25363,1132894 (deflabel |The Simplification of the Induction Conclusion (Step 0)||The25374,1133431 (deflabel |The Simplification of the Induction Conclusion (Step 1)||The25392,1134016 (deflabel |The Simplification of the Induction Conclusion (Step 2)||The25418,1135080 (deflabel |The Simplification of the Induction Conclusion (Step 3)||The25439,1135771 (deflabel |The Simplification of the Induction Conclusion (Step 4)||The25458,1136402 (deflabel |The Simplification of the Induction Conclusion (Step 5)||The25480,1137232 (deflabel |The Simplification of the Induction Conclusion (Step 6)||The25502,1138007 (deflabel |The Simplification of the Induction Conclusion (Step 7)||The25524,1138753 (deflabel |The Simplification of the Induction Conclusion (Step 8)||The25546,1139472 (deflabel |The Simplification of the Induction Conclusion (Step 9)||The25568,1140285 (deflabel |The Simplification of the Induction Conclusion (Step 10)||The25592,1141158 (deflabel |The Simplification of the Induction Conclusion (Step 11)||The25612,1141860 (deflabel |The Simplification of the Induction Conclusion (Step 12)||The25631,1142493 (deflabel |Overview of the Simplification of the Base Case to T||Overview25651,1143033 (deflabel |Overview of the Expansion of ENDP in the Base Case||Overview25679,1143874 (deflabel |Overview of the Final Simplification in the Base Case||Overview25688,1144267 (deflabel |The Final Simplification in the Base Case (Step 0)||The25698,1144632 (deflabel |The Final Simplification in the Base Case (Step 1)||The25716,1145236 (deflabel |The Final Simplification in the Base Case (Step 2)||The25733,1145775 (deflabel |The Final Simplification in the Base Case (Step 3)||The25749,1146231 (deflabel |The End of the Proof of the Associativity of App||The25768,1146772 (deflabel |Popping out of an Inductive Proof||Popping25798,1147656 (deflabel |The Q.E.D. Message||The25808,1148036 (deflabel |The Rules used in the Associativity of App Proof||The25817,1148319 (deflabel |The Time Taken to do the Associativity of App Proof||The25832,1149062 (deflabel |Guiding the ACL2 Theorem Prover||Guiding25848,1149753 (deflabel |Rewrite Rules are Generated from DEFTHM Events||Rewrite25875,1150736 (deflabel |You Must Think about the Use of a Formula as a Rule||You25909,1151695 (deflabel |Using the Associativity of App to Prove a Trivial Consequence||Using25939,1152703 (deflabel |Overview of the Proof of a Trivial Consequence||Overview25959,1153339 (deflabel |The WARNING about the Trivial Consequence||The26010,1155014 (deflabel |The First Application of the Associativity Rule||The26031,1155899 (deflabel |A Sketch of How the Rewriter Works||A26050,1156603 (deflabel |The Summary of the Proof of the Trivial Consequence||The26084,1157842 (deflabel |The End of the Walking Tour||The26096,1158218 (deflabel |ACL2 is an Untyped Language||ACL226129,1159307 (deflabel |Hey Wait! Is ACL2 Typed or Untyped(Q)||Hey26156,1160433 (deflabel |Guards||Guards|26181,1161383 (deflabel |About the Prompt||About26218,1163118 (deflabel |The Event Summary||The26286,1166841 (deflabel |About the Admission of Recursive Definitions||About26333,1169155 (deflabel |About Types||About26364,1170801 (deflabel |Numbers in ACL2||Numbers26409,1173169 (deflabel |ACL2 Characters||ACL226464,1175470 (deflabel |ACL2 Strings||ACL226483,1176295 (deflabel |ACL2 Symbols||ACL226501,1177143 (deflabel |ACL2 Conses or Ordered Pairs||ACL226546,1179817 (deflabel |Guessing the Type of a Newly Admitted Function||Guessing26579,1181328 (defconst *meta-level-function-problem-1**meta-level-function-problem-1*26624,1183297 (defconst *meta-level-function-problem-1a**meta-level-function-problem-1a*26628,1183496 (defconst *meta-level-function-problem-1b**meta-level-function-problem-1b*26632,1183706 (defconst *meta-level-function-problem-1c**meta-level-function-problem-1c*26637,1183933 (defconst *meta-level-function-problem-1d**meta-level-function-problem-1d*26643,1184262 (defconst *meta-level-function-problem-1e**meta-level-function-problem-1e*26650,1184623 (defconst *meta-level-function-problem-2**meta-level-function-problem-2*26656,1184941 (defconst *meta-level-function-problem-3**meta-level-function-problem-3*26663,1185295 (defun acl2-magic-mfc (x)acl2-magic-mfc26683,1186488 (defun mfc-ts-raw (term mfc state forcep)mfc-ts-raw26737,1188382 (defun mfc-rw-raw (term alist obj equiv-info mfc fn state forcep)mfc-rw-raw26819,1192020 (defun mfc-relieve-hyp-raw (hyp alist rune target bkptr mfc statemfc-relieve-hyp-raw26901,1196236 (defun-one-output mfc-ap-raw (term mfc state forcep)mfc-ap-raw27009,1201804 (defmacro mfc-ts (term mfc st &keymfc-ts27115,1208000 (defmacro mfc-rw (term obj equiv-info mfc st &keymfc-rw27124,1208321 (defmacro mfc-rw+ (term alist obj equiv-info mfc st &keymfc-rw+27142,1209163 (defmacro mfc-relieve-hyp (hyp alist rune target bkptr mfc st &keymfc-relieve-hyp27151,1209558 (defmacro mfc-ap (term mfc st &keymfc-ap27162,1210054 (defun congruence-rule-listp (x wrld)congruence-rule-listp27167,1210230 (defun term-alistp-failure-msg (alist wrld)term-alistp-failure-msg27180,1210694 (defun find-runed-linear-lemma (rune lst)find-runed-linear-lemma27197,1211396 (defun mfc-force-flg (forcep mfc)mfc-force-flg27208,1211740 (defun update-rncst-for-forcep (forcep rcnst)update-rncst-for-forcep27213,1211888 (defun trans-eval-lst (lst ctx state aok)trans-eval-lst27263,1214035 (defun print-saved-output (inhibit-output-lst gag-mode state)print-saved-output27269,1214251 (defmacro pso ()pso27312,1216125 (defmacro psog ()psog27327,1216600 (defmacro pso! ()pso!27341,1216985 (defdoc nil-goalnil-goal27356,1217470 (defmacro set-saved-output (save-flg inhibit-flg)set-saved-output27395,1219747 (defmacro set-raw-proof-format (flg)set-raw-proof-format27500,1225510 (defmacro set-print-clause-ids (flg)set-print-clause-ids27527,1226577 (defun set-standard-co-state (val state)set-standard-co-state27575,1228604 (defun set-proofs-co-state (val state)set-proofs-co-state27584,1228939 (defmacro with-standard-co-and-proofs-co-to-file (filename form)with-standard-co-and-proofs-co-to-file27593,1229268 (defmacro wof (filename form) ; Acronym: With Output Filewof27617,1230135 (defmacro psof (filename)psof27643,1230997 (defun set-gag-mode-fn (action state)set-gag-mode-fn27678,1232702 (defmacro set-gag-mode (action)set-gag-mode27709,1233834 (defparameter *initial-cbd* nil)*initial-cbd*27819,1239233 (defvar *return-from-lp* nil)*return-from-lp*27822,1239284 (defun save-exec-fn (exec-filename extra-startup-string host-lisp-argssave-exec-fn27824,1239315 (defmacro save-exec (exec-filename extra-startup-stringsave-exec27886,1241827 (defdoc command-linecommand-line28164,1254612 (deflabel about-acl2about-acl228179,1255078 (defun defun-for-state-name (name)defun-for-state-name28206,1255897 (defmacro defun-for-state (name args)defun-for-state28211,1256027 (defun set-ld-evisc-tuple (val state)set-ld-evisc-tuple28221,1256353 (defun-for-state set-ld-evisc-tuple (val state))set-ld-evisc-tuple28226,1256480 (defun set-abbrev-evisc-tuple (val state)set-abbrev-evisc-tuple28228,1256530 (defun-for-state set-abbrev-evisc-tuple (val state))set-abbrev-evisc-tuple28233,1256665 (defun set-gag-mode-evisc-tuple (val state)set-gag-mode-evisc-tuple28235,1256719 (defun-for-state set-gag-mode-evisc-tuple (val state))set-gag-mode-evisc-tuple28240,1256858 (defun set-term-evisc-tuple (val state)set-term-evisc-tuple28242,1256914 (defun-for-state set-term-evisc-tuple (val state))set-term-evisc-tuple28247,1257045 (defun without-evisc-fn (form state)without-evisc-fn28249,1257097 (defmacro without-evisc (form)without-evisc28260,1257500 proof-checker-b.lisp,18559 (defmacro install-new-pc-meta-or-macro (command-type raw-name name formals doc body)install-new-pc-meta-or-macro23,881 (defun define-pc-meta-or-macro-fn (command-type raw-name formals body)define-pc-meta-or-macro-fn27,1084 (defmacro define-pc-meta (raw-name formals &rest body)define-pc-meta41,1553 (defmacro define-pc-macro (raw-name formals &rest body)define-pc-macro56,2123 (defmacro define-pc-atomic-macro (raw-name formals &rest body)define-pc-atomic-macro95,3725 (defmacro toggle-pc-macro (name &optional new-tp)toggle-pc-macro98,3857 (defmacro define-pc-primitive (raw-name formals &rest body)define-pc-primitive126,4867 (define-pc-primitive comment (&rest x)comment150,5925 (defun non-bounded-nums (nums lower upper)non-bounded-nums169,6340 (defun delete-by-position (lst current-index nums)delete-by-position182,6807 (define-pc-primitive drop (&rest nums)drop192,7191 (define-pc-meta lisp (form)lisp232,8583 (define-pc-primitive fail-primitive ()fail-primitive296,11125 (define-pc-macro fail (&optional hard)fail300,11213 (define-pc-macro illegal (instr)illegal322,11807 (defun chk-assumption-free-ttree-1 (ttree ctx)chk-assumption-free-ttree-1344,12485 (defun put-cdr-assoc-query-id (id val alist)put-cdr-assoc-query-id358,12997 (defun set-query-val (id val state)set-query-val364,13251 (defmacro query-on-exit (&optional (val 'toggle))query-on-exit377,13662 (defun replay-query (state)replay-query380,13760 (define-pc-meta exit (&optional event-name rule-classes do-it-flg)exit393,14482 (define-pc-meta undo (&optional n)undo559,23291 (define-pc-meta restore ()restore613,25261 (defun print-commands (indexed-instrs state)print-commands644,26364 (defun make-pretty-start-instr (state-stack)make-pretty-start-instr659,26962 (defun raw-indexed-instrs (start-index finish-index state-stack)raw-indexed-instrs669,27320 (define-pc-macro sequence-no-restore (instr-list)sequence-no-restore686,28305 (define-pc-macro skip ()skip689,28409 (defmacro define-pc-help (name args &rest body)define-pc-help702,28655 (defun evisc-indexed-instrs-1 (name rev-indexed-instrs)evisc-indexed-instrs-1737,29996 (defun evisc-indexed-instrs-rec (rev-indexed-instrs)evisc-indexed-instrs-rec749,30539 (defun mark-unfinished-instrs (indexed-instrs)mark-unfinished-instrs769,31512 (defun evisc-indexed-instrs (indexed-instrs)evisc-indexed-instrs785,32308 (define-pc-help commands (&optional n evisc-p)commands791,32576 (define-pc-macro comm (&optional n)comm833,34213 (defun promote-guts (pc-state goals hyps x y no-flatten-flg)promote-guts889,36529 (define-pc-primitive promote (&optional do-not-flatten-flag)promote901,36905 (defun remove-by-indices (m indices lst)remove-by-indices940,38603 (define-pc-macro print (form &optional without-evisc)print952,39169 (defun bounded-integer-listp (i j lst)bounded-integer-listp984,40410 (defun fetch-term-and-cl (term addr cl)fetch-term-and-cl997,40825 (defun fetch-term (term addr)fetch-term1039,42965 (defun governors (term addr)governors1049,43292 (defun term-id-iff (term address iff-flg)term-id-iff1057,43584 (defmacro ? (x)?1105,45860 (defstub ?-fn (x)?-fn1108,45892 (defun abbreviations-alist (abbreviations)abbreviations-alist1115,46102 (defun chk-?s (term ctx state)chk-?s1124,46365 (defun chk-?s-lst (term-lst ctx state)chk-?s-lst1145,47243 (defun remove-?s (term abbreviations-alist ctx state)remove-?s1153,47432 (defun translate-abb (x abbreviations ctx state)translate-abb1158,47619 (defmacro trans0 (x &optional abbreviations ctx)trans01171,47950 (defun p-body (conc current-addr abbreviations state)p-body1174,48063 (define-pc-help p ()p1182,48415 (define-pc-help pp ()pp1205,49111 (defun take-by-indices (m indices lst)take-by-indices1224,49633 (defun print-hyps (indexed-hyps ndigits abbreviations state)print-hyps1233,50002 (defun some-> (lst n)some->1246,50629 (defun print-hyps-top (indexed-hyps abbreviations state)print-hyps-top1255,50876 (defun print-governors-top (indexed-hyps abbreviations state)print-governors-top1264,51241 (defun pair-indices (seed indices lst)pair-indices1273,51600 (define-pc-macro hyps (&optional hyps-indices govs-indices)hyps1289,52268 (define-pc-primitive demote (&rest rest-args)demote1407,57646 (defun pair-keywords (keywords lst)pair-keywords1463,59949 (defun null-pool (pool)null-pool1478,60586 (defun initial-pspv (term displayed-goal otf-flg ens wrld splitter-output)initial-pspv1485,60753 (defun pc-prove (term displayed-goal hints otf-flg ens wrld ctx state)pc-prove1493,61070 (defun sublis-equal (alist tree)sublis-equal1516,62109 (defun abbreviations-alist-? (abbreviations)abbreviations-alist-?1526,62392 (defun find-?-fn (x)find-?-fn1536,62795 (defun unproved-pc-prove-clauses (ttree)unproved-pc-prove-clauses1546,63103 (defun prover-call (comm term-to-prove rest-args pc-state state)prover-call1549,63201 (defun make-new-goals (cl-set goal-name start-index)make-new-goals1622,67505 (defun same-goal (goal1 goal2)same-goal1634,68017 (defun remove-byes-from-tag-tree (ttree)remove-byes-from-tag-tree1640,68213 (define-pc-primitive prove (&rest rest-args)prove1643,68296 (defun add-string-val-pair-to-string-val-alist (key key1 val alist)add-string-val-pair-to-string-val-alist1725,71674 (defconst *bash-skip-forcing-round-hints**bash-skip-forcing-round-hints*1743,72475 (define-pc-atomic-macro bash (&rest hints)bash1761,72985 (define-pc-primitive dive (n &rest rest-addr)dive1813,75035 (define-pc-atomic-macro split ()split1874,77464 (define-pc-primitive add-abbreviation (var &optional raw-term)add-abbreviation1916,79190 (defun not-in-domain-eq (lst alist)not-in-domain-eq1982,82213 (define-pc-primitive remove-abbreviations (&rest vars)remove-abbreviations1993,82579 (defun print-abbreviations (vars abbreviations state)print-abbreviations2035,84310 (define-pc-help show-abbreviations (&rest vars)show-abbreviations2079,85994 (defun drop-from-end (n l)drop-from-end2114,87347 (define-pc-primitive up (&optional n)up2121,87587 (define-pc-atomic-macro top ()top2161,89346 (defmacro expand-address-recurseexpand-address-recurse2181,90062 (defmacro dive-once-more-error ()dive-once-more-error2196,90613 (defun abbreviation-raw-term-p (x)abbreviation-raw-term-p2203,90886 (defmacro addr-recur (pos b)addr-recur2207,90964 (defun or-addr (n term iff-flg)or-addr2222,91485 (defun and-addr (n term iff-flg)and-addr2272,93359 (defmacro add-dive-into-macro (name val)add-dive-into-macro2330,95812 (defmacro remove-dive-into-macro (name)remove-dive-into-macro2345,96283 (defun dive-into-macros-table (wrld)dive-into-macros-table2361,96847 (defun rassoc-eq-as-car (key alist)rassoc-eq-as-car2405,98665 (defun expand-address (addr raw-term term abbreviations iff-flgexpand-address2411,98842 (defmacro dv-error (str alist)dv-error2773,116784 (define-pc-atomic-macro dv (&rest rest-args)dv2783,117128 (defun deposit-term (term addr subterm)deposit-term2880,120369 (defun deposit-term-lst (lst n addr subterm)deposit-term-lst2890,120722 (defun geneqv-at-subterm (term addr geneqv ens wrld)geneqv-at-subterm2908,121437 (defun geneqv-at-subterm-top (term addr ens wrld)geneqv-at-subterm-top2927,122208 (defun maybe-truncate-current-address (addr term orig-addr acc state)maybe-truncate-current-address2970,124020 (defun deposit-term-in-goal (given-goal conc current-addr new-term state)deposit-term-in-goal2998,125249 (defun split-implies (term)split-implies3017,126012 (defun equiv-refinementp (equiv1 equiv2 wrld)equiv-refinementp3030,126475 (defun find-equivalence-hyp-term (term hyps target equiv w)find-equivalence-hyp-term3034,126616 (defun flatten-ands-in-lit-lst (x)flatten-ands-in-lit-lst3055,127504 (define-pc-primitive equiv (x y &optional equiv)equiv3061,127656 (define-pc-primitive casesplitcasesplit3173,133057 (define-pc-macro top? ()top?3270,137522 (define-pc-macro contrapose-last ()contrapose-last3276,137633 (define-pc-macro drop-last ()drop-last3284,137906 (define-pc-macro drop-conc ()drop-conc3292,138161 (define-pc-atomic-macro claim (expr &rest rest-args)claim3295,138247 (define-pc-atomic-macro induct (&optional raw-term)induct3362,141231 (defun print-on-separate-lines (vals evisc-tuple chan state)print-on-separate-lines3401,142694 (define-pc-help goals ()goals3411,143107 (defun modified-error-triple-for-sequence (erp val success-expr state)modified-error-triple-for-sequence3430,143660 (define-pc-meta sequencesequence3467,145520 (define-pc-macro negate (&rest instr-list)negate3593,152166 (define-pc-macro succeed (&rest instr-list)succeed3615,152831 (define-pc-macro do-all (&rest instr-list)do-all3637,153453 (define-pc-macro do-strict (&rest instr-list)do-strict3664,154400 (define-pc-macro do-all-no-prompt (&rest instr-list)do-all-no-prompt3682,154937 (define-pc-macro th (&optional hyps-indices govs-indices)th3700,155510 (define-pc-macro protect (&rest instr-list)protect3732,156745 (defun extract-goal (name goals)extract-goal3751,157347 (define-pc-primitive change-goal (&optional name end-flg)change-goal3761,157726 (define-pc-macro cg (&optional name)cg3814,159975 (defun change-by-position (lst index new)change-by-position3830,160384 (define-pc-primitive contrapose (&optional n)contrapose3840,160748 (define-pc-macro contradict (&optional n)contradict3880,162542 (define-pc-atomic-macro pro ()pro3889,162717 (define-pc-atomic-macro nx ()nx3904,163092 (define-pc-atomic-macro bk ()bk3929,163945 (define-pc-help p-top ()p-top3960,165036 (define-pc-macro repeat (instr)repeat3998,166603 (define-pc-macro repeat-rec (instr)repeat-rec4016,167077 (defmacro define-pc-bind (name args &optional doc-string declare-form)define-pc-bind4024,167237 (define-pc-bind quietquiet4041,167943 (define-pc-bind noise (inhibit-output-lst nil)noise4059,168351 (defun find-equivalence-hyp-term-no-target (index term hyps equiv w)find-equivalence-hyp-term-no-target4078,168995 (define-pc-atomic-macro if-not-proved (goal-name cmd)if-not-proved4104,170259 (define-pc-atomic-macro = (&optional x y &rest rest-args)=4123,170948 (define-pc-macro set-success (instr form)set-success4325,180982 (define-pc-macro orelse (instr1 instr2)orelse4328,181071 (defun applicable-rewrite-rules (current-term conc current-addr target-name-or-runeapplicable-rewrite-rules4348,181666 (define-pc-help show-rewrites (&optional rule-id enabled-only-flg)show-rewrites4367,182631 (define-pc-macro sr (&rest args)sr4411,184734 (define-pc-help show-linears (&optional rule-id enabled-only-flg)show-linears4426,185025 (define-pc-macro sls (&rest args)sls4457,186256 (define-pc-macro pl (&optional x)pl4474,186748 (define-pc-macro pr (&optional x)pr4508,188003 (define-pc-help show-type-prescriptions (&optional rule-id)show-type-prescriptions4542,189258 (define-pc-macro st (&rest args)st4569,190347 (defun translate-subst-abb1 (sub abbreviations state)translate-subst-abb14584,190661 (defun single-valued-symbolp-alistp (alist)single-valued-symbolp-alistp4607,191795 (defun check-cars-are-variables (alist state)check-cars-are-variables4614,192017 (defun translate-subst-abb (sub abbreviations state)translate-subst-abb4633,192795 (defun make-rewrite-instr (lemma-id raw-subst instantiate-free)make-rewrite-instr4664,194079 (define-pc-primitive rewrite (&optional rule-id raw-sub instantiate-free)rewrite4671,194343 (defun applicable-linear-rules (current-term target-name-or-runeapplicable-linear-rules4926,207783 (defun make-linear-instr (lemma-id raw-subst instantiate-free)make-linear-instr4940,208358 (define-pc-primitive apply-linear (&optional rule-id raw-sub instantiate-free)apply-linear4947,208620 (define-pc-macro al (&rest args)al5192,221245 (defun pc-help-fn (name state)pc-help-fn5205,221478 (defmacro state-only (triple)state-only5251,223676 (define-pc-help help (&optional instr)help5257,223810 (defun pc-help!-fn (name state)pc-help!-fn5322,226487 (define-pc-help help! (&optional instr)help!5364,228405 (define-pc-macro help-long (&rest args)help-long5378,228907 (define-pc-help more ()more5389,229175 (define-pc-help more! ()more!5401,229432 (defun pc-rewrite*-1pc-rewrite*-15412,229744 (defun pc-rewrite*pc-rewrite*5456,231824 (defun make-goals-from-assumptions (assumptions conc hyps current-addr goal-name start-index)make-goals-from-assumptions5470,232371 (defun make-new-goals-from-assumptions (assumptions goal)make-new-goals-from-assumptions5483,232947 (defconst *default-s-repeat-limit* 10)*default-s-repeat-limit*5493,233268 (define-pc-primitive s (&rest args)s5495,233308 (defun build-pc-enabled-structure-from-ens (new-suffix ens)build-pc-enabled-structure-from-ens5752,245516 (define-pc-primitive in-theory (&optional theory-expr)in-theory5779,246775 (define-pc-atomic-macro s-prop (&rest names)s-prop5871,250920 (define-pc-atomic-macro x (&rest args)x5894,251679 (define-pc-primitive expand (&optionalexpand5953,254002 (define-pc-atomic-macro x-dumb ()x-dumb6050,257694 (define-pc-macro bookmark (tag &rest instr-list)bookmark6065,258106 (defun change-last (lst val)change-last6086,258832 (defun assign-event-name-and-rule-classes (event-name rule-classes state)assign-event-name-and-rule-classes6094,259009 (defun save-fn (name ss-alist state)save-fn6110,259809 (define-pc-macro save (&optional name do-it-flg)save6119,260041 (defmacro retrieve (&optional name)retrieve6167,262114 (define-pc-macro retrieve ()retrieve6185,262518 (defun unsave-fn (name state)unsave-fn6211,263543 (defmacro unsave (name)unsave6215,263645 (define-pc-help unsave (&optional name)unsave6233,264017 (defun show-retrieved-goal (state-stack state)show-retrieved-goal6267,265407 (defun retrieve-fn (name state)retrieve-fn6277,265804 (defun print-all-goals (goals state)print-all-goals6322,267789 (define-pc-help print-all-goals ()print-all-goals6328,267949 (defmacro print-conc (&optional acl2::goal)print-conc6340,268248 (defun print-all-concs (goals state)print-all-concs6352,268729 (define-pc-help print-all-concs ()print-all-concs6359,268935 (defun gen-var-marker (x)gen-var-marker6372,269272 (defun translate-generalize-alist-1 (alist state-vars abbreviations state)translate-generalize-alist-16377,269361 (defun non-gen-var-markers (alist)non-gen-var-markers6432,271446 (defun find-duplicate-generalize-entries (alist var)find-duplicate-generalize-entries6441,271734 (defun translate-generalize-alist-2 (alist avoid-list)translate-generalize-alist-26450,272047 (defun translate-generalize-alist (alist state-vars abbreviations state)translate-generalize-alist6461,272514 (defun all-vars-goals (goals)all-vars-goals6479,273437 (defun pc-state-vars (pc-state)pc-state-vars6486,273692 (define-pc-primitive generalize (&rest args)generalize6490,273877 (define-pc-atomic-macro use (&rest args)use6557,276728 (define-pc-atomic-macro clause-processor (&rest cl-proc-hints)clause-processor6585,277679 (define-pc-macro cl-proc (&rest cl-proc-hints)cl-proc6615,278640 (defun fromto (i j)fromto6624,278893 (define-pc-atomic-macro retain (arg1 &rest rest-args)retain6630,279032 (define-pc-atomic-macro reduce (&rest hints)reduce6664,280162 (define-pc-macro run-instr-on-goal (instr goal-name)run-instr-on-goal6708,281810 (defun run-instr-on-goals-guts (instr goal-names)run-instr-on-goals-guts6719,282066 (define-pc-macro run-instr-on-new-goals (instr existing-goal-namesrun-instr-on-new-goals6726,282315 (define-pc-macro then (instr &optional completion must-succeed-flg)then6739,282782 (defun print-help-separator (state)print-help-separator6764,283628 (defun print-pc-help-rec (lst state)print-pc-help-rec6769,283760 (defun print-all-pc-help-fn (filename state)print-all-pc-help-fn6781,284065 (defmacro print-all-pc-help (&optional filename)print-all-pc-help6797,284748 (define-pc-macro nil ()nil6800,284860 (define-pc-atomic-macro free (var)free6828,285895 (define-pc-macro replay (&optional n replacement-instr)replay6851,286563 (defun instr-name (instr)instr-name6895,288488 (defun pc-free-instr-p (var pc-state)pc-free-instr-p6901,288624 (defun find-possible-put (var state-stack)find-possible-put6906,288793 (define-pc-macro put (var expr)put6918,289279 (define-pc-macro reduce-by-induction (&rest hints)reduce-by-induction6960,291093 (define-pc-macro r (&rest args)r7002,292689 (define-pc-atomic-macro sl (&optional backchain-limit)sl7014,292897 (define-pc-atomic-macro elim ()elim7046,294168 (define-pc-macro ex ()ex7065,294812 (defun save-fc-report-settings ()save-fc-report-settings7080,295131 (defun restore-fc-report-settings ()restore-fc-report-settings7095,295617 (define-pc-help type-alist (&optional concl-flg govs-flg fc-report-flg)type-alist7110,296130 (define-pc-help print-main ()print-main7213,300868 (define-pc-macro pso ()pso7225,301106 (define-pc-macro psog ()psog7245,301815 (define-pc-macro pso! ()pso!7266,302552 (define-pc-macro acl2-wrap (x)acl2-wrap7287,303301 (defmacro acl2-wrap (x)acl2-wrap7303,303664 (define-pc-macro check-proved-goal (goal-name cmd)check-proved-goal7310,303814 (define-pc-macro check-proved (x)check-proved7318,304063 (define-pc-atomic-macro forwardchain (hypn &optional hints quiet-flg)forwardchain7326,304244 (define-pc-atomic-macro bdd (&rest kw-listp)bdd7410,307443 (define-pc-macro runes (&optional flg)runes7434,308269 (define-pc-macro lemmas-used (&optional flg)lemmas-used7455,309079 (defun goal-terms (goals)goal-terms7463,309250 (defun wrap1-aux1 (kept-goal-names all-goals kept-goals removed-goals)wrap1-aux17474,309551 (defun wrap1-aux2 (sym index goals kept-goals removed-goals)wrap1-aux27495,310449 (define-pc-primitive wrap1 (&optional kept-goal-names)wrap17510,311050 (define-pc-atomic-macro wrap (&rest instrs)wrap7589,314364 (define-pc-atomic-macro wrap-induct (&optional raw-term)wrap-induct7635,316321 (define-pc-macro finish-error (instrs)finish-error7667,317238 (define-pc-macro finish (&rest instrs)finish7672,317390 (defun show-geneqv (x with-runes-p)show-geneqv7693,318116 (define-pc-macro geneqv (&optional with-runes-p)geneqv7701,318460 (defun goals-to-clause-list (goals)goals-to-clause-list7735,320169 (defun proof-checker-clause-list (state)proof-checker-clause-list7742,320413 (defun proof-checker-cl-proc (cl instr-list state)proof-checker-cl-proc7745,320489 tutorial.lisp,6702 (deflabel ACL2-TutorialACL2-Tutorial45,1562 (deflabel alternative-introductionalternative-introduction111,4534 (deflabel annotated-acl2-scriptsannotated-acl2-scripts743,34484 (deflabel EmacsEmacs811,37396 (deflabel ACL2-As-Standalone-ProgramACL2-As-Standalone-Program825,37884 (deflabel acl2-sedanacl2-sedan931,40895 (deflabel solution-to-simple-examplesolution-to-simple-example953,41995 (deflabel Tutorial1-Towers-of-HanoiTutorial1-Towers-of-Hanoi1009,43994 (deflabel Tutorial2-Eights-ProblemTutorial2-Eights-Problem1267,53213 (deflabel Tutorial3-Phonebook-ExampleTutorial3-Phonebook-Example1396,57390 (deflabel Tutorial4-Defun-Sk-ExampleTutorial4-Defun-Sk-Example2222,89653 (deflabel Tutorial5-Miscellaneous-ExamplesTutorial5-Miscellaneous-Examples2323,93583 (deflabel file-reading-examplefile-reading-example2333,93863 (deflabel guard-exampleguard-example2389,96296 (deflabel mutual-recursion-proof-examplemutual-recursion-proof-example2699,105463 (deflabel functional-instantiation-examplefunctional-instantiation-example2847,111874 (deflabel StartupStartup2992,116767 (deflabel TidbitsTidbits3077,119678 (deflabel TipsTips3161,123245 (deflabel introduction-to-the-theorem-proverintroduction-to-the-theorem-prover3623,144891 (deflabel dealing-with-key-combinations-of-function-symbolsdealing-with-key-combinations-of-function-symbols3956,161981 (deflabel post-induction-key-checkpointspost-induction-key-checkpoints4138,169870 (deflabel generalizing-key-checkpointsgeneralizing-key-checkpoints4187,172146 (deflabel strong-rewrite-rulesstrong-rewrite-rules4282,176102 (deflabel practice-formulating-strong-rulespractice-formulating-strong-rules4376,180112 (deflabel practice-formulating-strong-rules-1practice-formulating-strong-rules-14440,181923 (deflabel practice-formulating-strong-rules-2practice-formulating-strong-rules-24537,186248 (deflabel practice-formulating-strong-rules-3practice-formulating-strong-rules-34593,188283 (deflabel practice-formulating-strong-rules-4practice-formulating-strong-rules-44720,193178 (deflabel practice-formulating-strong-rules-5practice-formulating-strong-rules-54760,194435 (deflabel practice-formulating-strong-rules-6practice-formulating-strong-rules-64822,196882 (deflabel introduction-to-key-checkpointsintroduction-to-key-checkpoints4930,200841 (deflabel programming-knowledge-taken-for-grantedprogramming-knowledge-taken-for-granted5090,210271 (deflabel example-induction-scheme-nat-recursionexample-induction-scheme-nat-recursion5381,224313 (deflabel example-induction-scheme-down-by-2example-induction-scheme-down-by-25434,225940 (deflabel example-induction-scheme-on-listsexample-induction-scheme-on-lists5492,227619 (deflabel example-induction-scheme-binary-treesexample-induction-scheme-binary-trees5539,228976 (deflabel example-induction-scheme-on-several-variablesexample-induction-scheme-on-several-variables5581,230012 (deflabel example-induction-scheme-upwardsexample-induction-scheme-upwards5627,231015 (deflabel example-induction-scheme-with-accumulatorsexample-induction-scheme-with-accumulators5678,232541 (deflabel example-induction-scheme-with-multiple-induction-stepsexample-induction-scheme-with-multiple-induction-steps5761,235396 (deflabel example-inductionsexample-inductions5825,237168 (deflabel logic-knowledge-taken-for-granted-inductive-prooflogic-knowledge-taken-for-granted-inductive-proof5884,240002 (deflabel logic-knowledge-taken-for-granted-base-caselogic-knowledge-taken-for-granted-base-case6034,247688 (deflabel logic-knowledge-taken-for-granted-q1-answerlogic-knowledge-taken-for-granted-q1-answer6061,248537 (deflabel logic-knowledge-taken-for-granted-q2-answerlogic-knowledge-taken-for-granted-q2-answer6147,252562 (deflabel logic-knowledge-taken-for-granted-q3-answerlogic-knowledge-taken-for-granted-q3-answer6291,257210 (deflabel logic-knowledge-taken-for-granted-instancelogic-knowledge-taken-for-granted-instance6332,258424 (deflabel logic-knowledge-taken-for-granted-propositional-calculuslogic-knowledge-taken-for-granted-propositional-calculus6372,259772 (deflabel logic-knowledge-taken-for-granted-rewritinglogic-knowledge-taken-for-granted-rewriting6620,269105 (deflabel logic-knowledge-taken-for-granted-rewriting-repeatedlylogic-knowledge-taken-for-granted-rewriting-repeatedly6859,279159 (deflabel logic-knowledge-taken-for-granted-equals-for-equalslogic-knowledge-taken-for-granted-equals-for-equals6953,282607 (deflabel logic-knowledge-taken-for-granted-evaluationlogic-knowledge-taken-for-granted-evaluation7004,284514 (deflabel logic-knowledge-taken-for-grantedlogic-knowledge-taken-for-granted7042,285898 (deflabel special-cases-for-rewrite-rulesspecial-cases-for-rewrite-rules7258,294498 (deflabel equivalent-formulas-different-rewrite-rulesequivalent-formulas-different-rewrite-rules7312,296858 (deflabel introduction-to-rewrite-rules-part-2introduction-to-rewrite-rules-part-27385,299806 (deflabel specific-kinds-of-formulas-as-rewrite-rulesspecific-kinds-of-formulas-as-rewrite-rules7624,314121 (deflabel further-information-on-rewritingfurther-information-on-rewriting7711,318450 (deflabel introduction-to-rewrite-rules-part-1introduction-to-rewrite-rules-part-17830,325224 (deflabel introduction-to-the-databaseintroduction-to-the-database8009,334243 (deflabel introduction-to-hintsintroduction-to-hints8241,346169 (deflabel introduction-to-a-few-system-considerationsintroduction-to-a-few-system-considerations8341,351340 (deflabel architecture-of-the-proverarchitecture-of-the-prover8524,360134 (deflabel frequently-asked-questions-by-newcomersfrequently-asked-questions-by-newcomers8605,364191 (deflabel introductory-challengesintroductory-challenges9182,396609 (deflabel introductory-challenge-problem-1introductory-challenge-problem-19218,398161 (deflabel introductory-challenge-problem-1-answerintroductory-challenge-problem-1-answer9251,398972 (deflabel introductory-challenge-problem-2introductory-challenge-problem-29299,400312 (deflabel introductory-challenge-problem-2-answerintroductory-challenge-problem-2-answer9322,400883 (deflabel introductory-challenge-problem-3introductory-challenge-problem-39369,402199 (deflabel introductory-challenge-problem-3-answerintroductory-challenge-problem-3-answer9405,403123 (deflabel introductory-challenge-problem-4introductory-challenge-problem-49524,407128 (deflabel introductory-challenge-problem-4-answerintroductory-challenge-problem-4-answer9581,409400 (deflabel interesting-applicationsinteresting-applications10163,432051 (deflabel advanced-featuresadvanced-features10283,439547 interface-raw.lisp,13165 (defun-*1* mv-list (input-arity x)mv-list676,29373 (defun-*1* return-last (fn x y)return-last680,29447 (defun-*1* wormhole-eval (qname qlambda free-vars)wormhole-eval750,32381 (defun-*1* acl2-numberp (x)acl2-numberp757,32569 (defun-*1* binary-* (x y)binary-*760,32613 (defun-*1* binary-+ (x y)binary-+768,32797 (defun-*1* unary-- (x)unary--779,33071 (defun-*1* unary-/ (x)unary-/785,33180 (defun-*1* < (x y)<791,33309 (defun-*1* apply (x y)apply816,34292 (defun-*1* bad-atom<= (x y)bad-atom<=820,34414 (defun-*1* car (x)car830,34740 (defun-*1* cdr (x)cdr838,34843 (defun-*1* char-code (x)char-code846,34946 (defun-*1* characterp (x)characterp851,35040 (defun-*1* code-char (x)code-char854,35085 (defun-*1* complex (x y)complex861,35236 (defun-*1* complex-rationalp (x)complex-rationalp865,35400 (defun-*1* complexp (x)complexp871,35541 (defun-*1* coerce (x y)coerce874,35582 (defun-*1* cons (x y)cons888,35912 (defun-*1* consp (x)consp891,35949 (defun-*1* denominator (x)denominator894,35984 (defun-*1* equal (x y)equal899,36083 (defun-*1* floor1 (x)floor1903,36147 (defun-*1* if (x y z)if912,36351 (defun-*1* imagpart (x)imagpart917,36552 (defun-*1* integerp (x)integerp922,36640 (defun-*1* intern-in-package-of-symbol (x y)intern-in-package-of-symbol925,36681 (defun-*1* pkg-imports (pkg)pkg-imports931,36863 (defun-*1* pkg-witness (pkg)pkg-witness936,36970 (defun-*1* numerator (x)numerator946,37389 (defun-*1* rationalp (x)rationalp951,37482 (defun-*1* realp (x)realp957,37601 (defun-*1* realpart (x)realpart960,37636 (defun-*1* stringp (x)stringp965,37724 (defun-*1* symbol-name (x)symbol-name968,37763 (defun-*1* symbol-package-name (x)symbol-package-name973,37861 (defun-*1* symbolp (x)symbolp979,38028 (defun standardp (x)standardp995,38669 (defun-*1* standardp (x)standardp999,38719 (defun standard-part (x)standard-part1003,38773 (defun-*1* standard-part (x)standard-part1006,38803 (defun i-large-integer ()i-large-integer1009,38838 (defun-*1* i-large-integer ()i-large-integer1012,38936 (defun-one-output macroexpand1! (x)macroexpand1!1017,39041 (defvar *acl2-gentemp-counter* 0)*acl2-gentemp-counter*1023,39211 (defun-one-output acl2-gentemp (root)acl2-gentemp1024,39245 (defun-one-output oneify-flet-bindings (alist fns w)oneify-flet-bindings1036,39685 (defun-one-output oneify (x fns w)oneify1063,41009 (defun-one-output oneify-lst (lst fns w)oneify-lst1266,48996 (defun-one-output select-stobj (name stobjs terms)select-stobj1274,49191 (defun-one-output super-defstobj-wart-stobjs-in (formals stobj-flag)super-defstobj-wart-stobjs-in1279,49375 (defun-one-output oneify-fail-form (er-type fn formals guard super-stobjs-inoneify-fail-form1287,49712 (defun-one-output get-declared-stobjs (edcls)get-declared-stobjs1308,50421 (defun-one-output warn-for-guard-body (fn)warn-for-guard-body1322,50944 (defun-one-output create-live-user-stobjp-test (stobjs)create-live-user-stobjp-test1334,51529 (defun oneify-cltl-code (defun-mode def stobj-flag wrldoneify-cltl-code1344,51843 (defvar *saved-raw-prompt* nil)*saved-raw-prompt*2088,89063 (defvar *saved-raw-prompt-p* nil)*saved-raw-prompt-p*2089,89095 (defun-one-output install-new-raw-prompt ()install-new-raw-prompt2094,89148 (defun-one-output install-old-raw-prompt ()install-old-raw-prompt2101,89418 (defun-one-output install-new-raw-prompt ()install-new-raw-prompt2111,89685 (defun-one-output install-old-raw-prompt ()install-old-raw-prompt2122,90173 (defun-one-output install-new-raw-prompt ()install-new-raw-prompt2132,90447 (defun-one-output install-old-raw-prompt ()install-old-raw-prompt2139,90678 (defun-one-output install-new-raw-prompt ()install-new-raw-prompt2146,90808 (defun-one-output install-old-raw-prompt ()install-old-raw-prompt2158,91360 (defun-one-output install-new-raw-prompt ()install-new-raw-prompt2172,91852 (defun-one-output install-old-raw-prompt ()install-old-raw-prompt2189,92694 (defun-one-output install-new-raw-prompt ()install-new-raw-prompt2207,93337 (defun-one-output install-old-raw-prompt ()install-old-raw-prompt2210,93389 (defvar *dmr-interval* 1000)*dmr-interval*2223,93743 (defvar *dmr-interval-acl2-par-hack* 300000)*dmr-interval-acl2-par-hack*2224,93772 (defvar *dmr-interval-used*)*dmr-interval-used*2225,93817 (defvar *dmr-indent-max* 20)*dmr-indent-max*2232,94090 (defvar *dmr-file-name*)*dmr-file-name*2238,94374 (defun dmr-file-name ()dmr-file-name2240,94400 (defparameter *dmr-stream**dmr-stream*2253,94998 (defparameter *dmr-counter**dmr-counter*2256,95033 (defun dmr-acl2-par-hack-p ()dmr-acl2-par-hack-p2265,95263 (defun dmr-stop-fn-raw ()dmr-stop-fn-raw2268,95352 (defun initialize-dmr-interval-used ()initialize-dmr-interval-used2274,95481 (defun dmr-start-fn-raw (state)dmr-start-fn-raw2282,95713 (defvar *dmr-array**dmr-array*2296,96178 (defun reverse-into-dmr-array (lst)reverse-into-dmr-array2299,96262 (defparameter *dmr-reusable-string**dmr-reusable-string*2309,96564 (defvar *dmr-indent*)*dmr-indent*2321,96916 (defmacro dmr-increment-indent ()dmr-increment-indent2323,96939 (defun tilde-@-bkptr-string (calling-sys-fn called-sys-fn bkptr)tilde-@-bkptr-string2327,97026 (defvar *dmr-interp-state**dmr-interp-state*2385,99861 (defun dmr-interp-fresh-rewrite-p (calling-sys-fn frame)dmr-interp-fresh-rewrite-p2390,100003 (defun dmr-prefix ()dmr-prefix2397,100216 (defun dmr-interp (i calling-sys-fn frame)dmr-interp2410,100672 (defvar *dmr-delete-string**dmr-delete-string*2485,103412 (defun dmr-string ()dmr-string2491,103528 (defun dmr-flush1 (&optional reset-counter)dmr-flush12575,107292 (defvar *dmr-lock* (make-lock))*dmr-lock*2590,107724 (defun dmr-flush (&optional reset-counter)dmr-flush2592,107757 (defun dmr-display ()dmr-display2604,108192 (defun cw-gstack-short ()cw-gstack-short2616,108569 (defun-one-output fmakunbound! (name)fmakunbound!2771,117320 (defun-one-output maybe-push-undo-stack (fn name &optional extra)maybe-push-undo-stack2778,117589 (defun-one-output maybe-pop-undo-stack (name)maybe-pop-undo-stack2951,125827 (defun-one-output flush-undo-stack (name)flush-undo-stack2964,126168 (defparameter *current-acl2-world-key-ordering**current-acl2-world-key-ordering*3275,142599 (defun-one-output key-lesseqp (key1 key2 ordering)key-lesseqp3299,143328 (defun-one-output merge-into-alist (key val alist)merge-into-alist3308,143604 (defun-one-output destructive-push-assoc (key value alist world-key)destructive-push-assoc3320,144175 (defun-one-output destructive-pop-assoc (key alist)destructive-pop-assoc3337,145045 (defun-one-output remove-current-acl2-world-key (plist)remove-current-acl2-world-key3343,145240 (defun hcomp-init ()hcomp-init4139,190426 (defabbrev reclassifying-value-p (x)reclassifying-value-p4199,193077 (defmacro make-reclassifying-value (x)make-reclassifying-value4206,193230 (defmacro unmake-reclassifying-value (x)unmake-reclassifying-value4212,193360 (defun hcomp-transfer-to-hash-tables ()hcomp-transfer-to-hash-tables4218,193472 (defvar *saved-hcomp-restore-hts* nil)*saved-hcomp-restore-hts*4262,195292 (defun hcomp-restore-defs ()hcomp-restore-defs4264,195332 (defun missing-compiled-book (ctx file reason-msg load-compiled-file state)missing-compiled-book4316,197619 (defun load-compiled-book (file directory-name load-compiled-file ctx state)load-compiled-book4378,200600 (defun include-book-raw (book-name directory-name load-compiled-file dir ctxinclude-book-raw4506,206320 (defun include-book-raw-top (full-book-name directory-name load-compiled-fileinclude-book-raw-top4763,219893 (defmacro hcomp-ht-from-type (type ctx)hcomp-ht-from-type4795,221402 (defmacro hcomp-build-p ()hcomp-build-p4804,221690 (defun install-for-add-trip-hcomp-build (def reclassifyingp evalp)install-for-add-trip-hcomp-build4809,221877 (defun install-for-add-trip-include-book (type name def reclassifyingp)install-for-add-trip-include-book4866,224468 (defun install-for-add-trip (def reclassifyingp evalp)install-for-add-trip4952,228499 (defun install-defs-for-add-trip (defs reclassifying-p wrld declaim-p evalp)install-defs-for-add-trip4968,229138 (defun hcomp-build-from-portcullis-raw (cltl-cmds state)hcomp-build-from-portcullis-raw5088,234699 (defun hcomp-alists-from-hts ()hcomp-alists-from-hts5176,238569 (defun-one-output add-trip (world-name world-key trip)add-trip5221,240428 (defun-one-output undo-trip (world-name world-key trip)undo-trip5789,268590 (defun-one-output flush-trip (name world-key trip)flush-trip5831,270410 (defvar *bad-wrld*)*bad-wrld*5861,271611 (defun check-acl2-world-invariant (wrld old-wrld)check-acl2-world-invariant5863,271632 (defparameter *known-worlds* nil)*known-worlds*5881,272430 (defun update-wrld-structures (wrld state)update-wrld-structures5883,272465 (defun-one-output extend-world1 (name wrld)extend-world15894,272802 (defun-one-output retract-world1 (name wrld)retract-world16007,277962 (defun-one-output recover-world1 (wrld saved-wrld ans)recover-world16108,282353 (defun-one-output recover-world (op name old-wrld universe pkg)recover-world6123,283012 (defun-one-output virginp (name new-type)virginp6251,289634 (defun-one-output chk-virgin2 (name new-type wrld)chk-virgin26262,290020 (defun-one-output chk-package-reincarnation-import-restrictions2chk-package-reincarnation-import-restrictions26329,293242 (defvar user::*acl2-keep-tmp-files* nil)user::*acl2-keep-tmp-files*6368,294478 (defun-one-output enter-boot-strap-mode (system-books-dir operating-system)enter-boot-strap-mode6370,294520 (defun-one-output move-current-acl2-world-key-to-front (wrld)move-current-acl2-world-key-to-front6465,298799 (defun-one-output exit-boot-strap-mode ()exit-boot-strap-mode6554,303406 (defun-one-output ld-alist-raw (standard-oi ld-skip-proofsp ld-error-action)ld-alist-raw6577,304380 (defun enter-boot-strap-pass-2 ()enter-boot-strap-pass-26600,305203 (defconst *acl2-pass-2-files**acl2-pass-2-files*6620,305917 (defun our-update-ht (key val ht)our-update-ht6637,306370 (defun note-fns-in-form (form ht)note-fns-in-form6646,306667 (defun note-fns-in-file (filename ht)note-fns-in-file6735,309850 (defun note-fns-in-files (filenames ht loop-only-p)note-fns-in-files6750,310293 (defun raw-source-name-p (filename-without-extension)raw-source-name-p6763,310864 (defvar *check-built-in-constants-debug* nil)*check-built-in-constants-debug*6770,311145 (defun fns-different-wrt-acl2-loop-only (acl2-files)fns-different-wrt-acl2-loop-only6772,311192 (defun collect-monadic-booleans (fns ens wrld)collect-monadic-booleans6840,314729 (defun check-built-in-constants ()check-built-in-constants6854,315326 (defun-one-output check-none-ideal (trips acc)check-none-ideal7106,326825 (defun check-state-globals-initialized ()check-state-globals-initialized7146,328437 (defun-one-output check-acl2-initialization ()check-acl2-initialization7163,329050 (defun set-initial-cbd ()set-initial-cbd7169,329259 (defun initialize-acl2 (&optional (pass-2-ld-skip-proofsp 'include-book)initialize-acl27197,330400 (defun our-abort (condition your-abort7488,343920 (defun initial-customization-filename ()initial-customization-filename7584,348237 (defun spawn-extra-lispworks-listener ()spawn-extra-lispworks-listener7669,351853 (defun lp (&rest args)lp7695,353056 (defmacro lp! (&rest args)lp!7919,363205 (defun acl2-compile-file (full-book-name os-expansion-filename)acl2-compile-file7925,363372 (defun-one-output delete-auxiliary-book-files (full-book-name)delete-auxiliary-book-files7994,366411 (defun delete-expansion-file (expansion-filename state)delete-expansion-file8027,368093 (defun compile-uncompiled-defuns (file &optional (fns :some) gcl-flgcompile-uncompiled-defuns8035,368386 (defun compile-uncompiled-*1*-defuns (file &optional (fns :some) gcl-flg chan0compile-uncompiled-*1*-defuns8215,376717 (defun compile-certified-file (expansion-filename full-book-name state)compile-certified-file8451,387908 (defun compile-for-include-book (full-book-name certified-p ctx state)compile-for-include-book8465,388514 (defun-one-output enabled-structurep (x)enabled-structurep8532,391958 (defun-one-output rcnstp (x)rcnstp8555,392945 (defvar *trace-alist**trace-alist*8571,393359 (defun-one-output assoc-eq-trace-alist (val alist)assoc-eq-trace-alist8574,393426 (defun-one-output print-list-without-stobj-arrays (lst)print-list-without-stobj-arrays8582,393651 (defun-one-output stobj-print-symbol (x user-stobj-alist-tail)stobj-print-symbol8589,393850 (defun-one-output trace-hide-world-and-state (l)trace-hide-world-and-state8606,394605 (defun-one-output saved-build-date-string ()saved-build-date-string8660,396854 (defun-one-output get-stobjs-out-for-declare-form (fn)get-stobjs-out-for-declare-form8671,397150 (defun fix-trace-untrace (new-trace-specs old-trace-specs)fix-trace-untrace8696,398125 (defun fix-trace (old-trace-specs)fix-trace8708,398565 (defun acl2-books-revision ()acl2-books-revision8720,399057 defpkgs.lisp,90 (defconst *acl2-exports**acl2-exports*34,1565 (defpkg "ACL2-USER""ACL2-USER"638,27417 boot-strap-pass-2.lisp,3623 (defattach too-many-ifs-post-rewrite too-many-ifs-post-rewrite-builtin)too-many-ifs-post-rewrite487,19522 (defthm fn-count-evg-rec-type-prescriptionfn-count-evg-rec-type-prescription524,20788 (defthm fn-count-evg-rec-boundfn-count-evg-rec-bound529,20942 (defthm fn-count-1-typefn-count-1-type546,21393 (defthm symbol-listp-cdr-assoc-equalsymbol-listp-cdr-assoc-equal571,22588 (defthm integerp-nth-0-var-fn-count-1integerp-nth-0-var-fn-count-1579,22915 (defthm integerp-nth-1-var-fn-count-1integerp-nth-1-var-fn-count-1606,24205 (defthm integerp-nth-2-var-fn-count-1integerp-nth-2-var-fn-count-1629,25266 (defun member-equal-mod-commuting (x lst wrld)member-equal-mod-commuting660,26527 (defun strip-ancestor-literals (ancestors)strip-ancestor-literals673,27135 (defattach worse-than worse-than-builtin)worse-than756,30389 (defattach worse-than-or-equal worse-than-or-equal-builtin)worse-than-or-equal758,30432 (defattach rw-cache-debug rw-cache-debug-builtin)rw-cache-debug779,31047 (defattach rw-cache-debug-action rw-cache-debug-action-builtin)rw-cache-debug-action783,31174 (defattach rw-cacheable-failure-reason rw-cacheable-failure-reason-builtin)rw-cacheable-failure-reason787,31321 (defthm d-pos-listp-forward-to-true-listpd-pos-listp-forward-to-true-listp812,32089 (defattach print-clause-id-okp print-clause-id-okp-builtin)print-clause-id-okp829,32654 (defattach oncep-tp oncep-tp-builtin)oncep-tp863,33895 (defthm pos-listp-forward-to-integer-listppos-listp-forward-to-integer-listp885,34833 (defthm true-listp-chars-for-tilde-@-clause-id-phrase/periodstrue-listp-chars-for-tilde-@-clause-id-phrase/periods892,35035 (defthm true-listp-explode-atomtrue-listp-explode-atom896,35198 (deftheory definition-minimal-theorydefinition-minimal-theory991,38048 (deftheory executable-counterpart-minimal-theoryexecutable-counterpart-minimal-theory997,38158 (deftheory minimal-theoryminimal-theory1003,38285 (deftheory ground-zero (current-theory :here)ground-zero1053,40237 (defund meta-extract-formula (name state)meta-extract-formula1084,41170 (defun typespec-check (ts x)typespec-check1111,42319 (defun meta-extract-rw+-term (term alist equiv rhs state)meta-extract-rw+-term1123,42693 (defun meta-extract-contextual-fact (obj mfc state)meta-extract-contextual-fact1154,43840 (defun rewrite-rule-term (x)rewrite-rule-term1199,45908 (defmacro meta-extract-global-fact (obj state)meta-extract-global-fact1213,46340 (defun fncall-term (fn arglist state)fncall-term1217,46473 (defun logically-equivalent-states (st1 st2)logically-equivalent-states1234,47115 (defun meta-extract-global-fact+ (obj st state)meta-extract-global-fact+1238,47230 (defun pair-fns-with-measured-subsets (fns wrld acc)pair-fns-with-measured-subsets1295,49630 (defun new-verify-guards-fns1 (wrld installed-wrld acc)new-verify-guards-fns11312,50422 (defun new-verify-guards-fns (state)new-verify-guards-fns1334,51448 (defconst *system-verify-guards-alist**system-verify-guards-alist*1345,51890 (defconst *len-system-verify-guards-alist**len-system-verify-guards-alist*1402,53746 (defmacro chk-new-verified-guards (n)chk-new-verified-guards1405,53831 (defun system-verify-guards-fn-1 (fns-alist acc)system-verify-guards-fn-11439,55325 (defun cons-absolute-event-numbers (fns-alist wrld acc)cons-absolute-event-numbers1452,55934 (defun sort->-absolute-event-number (fns-alist wrld)sort->-absolute-event-number1469,56679 (defun system-verify-guards-fn (alist wrld acc)system-verify-guards-fn1474,56897 (defmacro system-verify-guards ()system-verify-guards1484,57250 hons-raw.lisp,9768 (defmacro hl-without-interrupts (&rest forms)hl-without-interrupts94,4387 (defun hl-mht-fn (&key (test 'eql)hl-mht-fn104,4685 (defvar *allegro-hl-hash-table-size-ht**allegro-hl-hash-table-size-ht*127,5524 (defmacro hl-mht (&rest args)hl-mht131,5637 (defabbrev hl-static-cons (a b)hl-static-cons217,9570 (defabbrev hl-staticp (x)hl-staticp221,9643 (defabbrev hl-static-inverse-cons (x)hl-static-inverse-cons225,9705 (defabbrev hl-machine-address-of (x)hl-machine-address-of231,9793 (defabbrev hl-machine-hash (x)hl-machine-hash235,9869 (defconstant hl-cache-table-sizehl-cache-table-size381,17111 (defconstant hl-cache-table-cutoffhl-cache-table-cutoff386,17198 (defstruct hl-cachehl-cache390,17333 (defun hl-cache-set (key val cache)hl-cache-set402,17687 (defun hl-cache-get (key cache)hl-cache-get427,18622 (defun hl-cache-clear (cache)hl-cache-clear454,19377 (defparameter *hl-hspace-str-ht-default-size* 1000)*hl-hspace-str-ht-default-size*672,29220 (defparameter *hl-ctables-nil-ht-default-size* 5000)*hl-ctables-nil-ht-default-size*673,29277 (defparameter *hl-ctables-cdr-ht-default-size* 100000)*hl-ctables-cdr-ht-default-size*674,29334 (defparameter *hl-ctables-cdr-ht-eql-default-size* 1000)*hl-ctables-cdr-ht-eql-default-size*675,29393 (defparameter *hl-hspace-addr-ht-default-size* 150000)*hl-hspace-addr-ht-default-size*676,29450 (defparameter *hl-hspace-sbits-default-size* 16000000)*hl-hspace-sbits-default-size*677,29509 (defparameter *hl-hspace-other-ht-default-size* 1000)*hl-hspace-other-ht-default-size*678,29570 (defparameter *hl-hspace-fal-ht-default-size* 1000)*hl-hspace-fal-ht-default-size*679,29627 (defparameter *hl-hspace-persist-ht-default-size* 100)*hl-hspace-persist-ht-default-size*680,29684 (defstruct hl-ctableshl-ctables685,29757 (defun hl-initialize-faltable-table (fal-ht-size)hl-initialize-faltable-table702,30323 (defstruct hl-falslothl-falslot743,32434 (defun hl-faltable-init (&key (size *hl-hspace-fal-ht-default-size*))hl-faltable-init789,34103 (defun hl-hspace-init (&key (str-ht-size *hl-hspace-str-ht-default-size*)hl-hspace-init845,35701 (defabbrev hl-flex-alist-too-long (x)hl-flex-alist-too-long927,39535 (defabbrev hl-flex-assoc (key al)hl-flex-assoc943,40137 (defabbrev hl-flex-acons (elem al)hl-flex-acons953,40433 (defun hl-hspace-truly-static-honsp (x hs)hl-hspace-truly-static-honsp995,41938 (defabbrev hl-hspace-find-alist-for-cdr (b ctables)hl-hspace-find-alist-for-cdr1010,42478 (defun hl-hspace-honsp (x hs)hl-hspace-honsp1030,43204 (defun hl-hspace-honsp-wrapper (x)hl-hspace-honsp-wrapper1048,43671 (defun hl-hspace-faltable-wrapper ()hl-hspace-faltable-wrapper1054,43865 (defun hl-hspace-normedp (x hs)hl-hspace-normedp1061,44063 (defun hl-hspace-normedp-wrapper (x)hl-hspace-normedp-wrapper1082,44609 (defun hl-hspace-hons-equal-lite (x y hs)hl-hspace-hons-equal-lite1095,45008 (defun hl-hspace-hons-equal (x y hs)hl-hspace-hons-equal1115,45579 (defconstant hl-minimum-static-inthl-minimum-static-int1191,48564 (defconstant hl-maximum-static-inthl-maximum-static-int1196,48704 (defconstant hl-num-static-intshl-num-static-ints1201,48840 (defconstant hl-dynamic-base-addrhl-dynamic-base-addr1206,49023 (defconstant hl-static-int-shifthl-static-int-shift1215,49312 (ccl::defstatic *hl-symbol-addr-lock**hl-symbol-addr-lock*1223,49701 (defabbrev hl-symbol-addr (s)hl-symbol-addr1228,49866 (defun hl-addr-of-unusual-atom (x str-ht other-ht)hl-addr-of-unusual-atom1276,52072 (defmacro hl-addr-of (x str-ht other-ht)hl-addr-of1319,53771 (defun hl-nat-combine* (a b)hl-nat-combine*1342,54677 (defabbrev hl-addr-combine* (a b)hl-addr-combine*1356,55235 (defparameter *hl-addr-limit-minimum**hl-addr-limit-minimum*1436,59175 (defun hl-make-addr-limit-current (hs)hl-make-addr-limit-current1444,59500 (defun hl-make-addr-limit-next (hs)hl-make-addr-limit-next1457,59930 (defun hl-addr-ht-fullness (hs)hl-addr-ht-fullness1475,60686 (defparameter *hl-addr-limit-should-clear-memo-tables**hl-addr-limit-should-clear-memo-tables*1481,60909 (defun hl-addr-limit-action (hs)hl-addr-limit-action1486,61029 (defun hl-hspace-grow-sbits (idx hs)hl-hspace-grow-sbits1546,63512 (defun hl-hspace-norm-atom (x hs)hl-hspace-norm-atom1590,65758 (defun hl-hspace-hons-normed (a b hint hs)hl-hspace-hons-normed1628,67077 (defun hl-hspace-norm-aux (x cache hs)hl-hspace-norm-aux1782,74804 (defun hl-hspace-norm-expensive (x hs)hl-hspace-norm-expensive1808,75744 (defun hl-hspace-norm (x hs)hl-hspace-norm1826,76379 (defun hl-hspace-persistent-norm (x hs)hl-hspace-persistent-norm1835,76600 (defabbrev hl-hspace-hons (x y hs)hl-hspace-hons1860,77637 (defun hl-slow-alist-warning (name)hl-slow-alist-warning1963,82184 (defun hl-faltable-maphash (f faltable)hl-faltable-maphash1981,82949 (defun hl-faltable-load-empty-slot (alist slot faltable)hl-faltable-load-empty-slot2008,83814 (defun hl-faltable-eject (slot faltable)hl-faltable-eject2031,84701 (defun hl-faltable-get-free-slot (faltable)hl-faltable-get-free-slot2049,85385 (defun hl-faltable-slot-lookup (alist faltable)hl-faltable-slot-lookup2064,85952 (defun hl-faltable-general-lookup (alist faltable)hl-faltable-general-lookup2091,86984 (defun hl-faltable-remove (alist faltable)hl-faltable-remove2106,87639 (defun hl-hspace-fast-alist-free (alist hs)hl-hspace-fast-alist-free2129,88655 (defun hl-hspace-hons-get (key alist hs)hl-hspace-hons-get2137,88859 (defun hl-hspace-hons-acons (key value alist hs)hl-hspace-hons-acons2163,89892 (defun hl-alist-stolen-warning (name)hl-alist-stolen-warning2223,92336 (defun hl-hspace-hons-acons! (key value alist hs)hl-hspace-hons-acons!2241,93090 (defun hl-alist-longest-normed-tail (alist hs)hl-alist-longest-normed-tail2290,95037 (defun hl-make-fast-norm-keys (alist tail hs)hl-make-fast-norm-keys2310,95834 (defun hl-make-fast-alist-put-pairs (alist ht)hl-make-fast-alist-put-pairs2336,96811 (defun hl-hspace-make-fast-alist (alist hs)hl-hspace-make-fast-alist2352,97468 (defun hl-shrink-alist-aux-really-slow (alist ans honsp hs)hl-shrink-alist-aux-really-slow2404,99855 (defun hl-shrink-alist-aux-slow (alist ans table honsp hs)hl-shrink-alist-aux-slow2426,100853 (defun hl-shrink-alist-aux-fast (alist ans table honsp hs)hl-shrink-alist-aux-fast2452,102038 (defun hl-hspace-shrink-alist (alist ans honsp hs)hl-hspace-shrink-alist2483,103415 (defun hl-hspace-fast-alist-len (alist hs)hl-hspace-fast-alist-len2557,106773 (defun hl-check-alist-for-serialize-restore (alist hs)hl-check-alist-for-serialize-restore2576,107497 (defun hl-hspace-restore-fal-for-serialize (alist count hs)hl-hspace-restore-fal-for-serialize2595,108169 (defun hl-restore-fal-for-serialize (alist count)hl-restore-fal-for-serialize2621,109413 (defun hl-hspace-number-subtrees-aux (x seen)hl-hspace-number-subtrees-aux2632,109807 (defun hl-hspace-number-subtrees (x hs)hl-hspace-number-subtrees2644,110141 (defun hl-system-gc ()hl-system-gc2671,111057 (defun hl-hspace-classic-restore (x nil-ht cdr-ht cdr-ht-eql seen-ht)hl-hspace-classic-restore2690,111601 (defun hl-hspace-hons-clear (gc hs)hl-hspace-hons-clear2746,113988 (defun hl-hspace-static-restore (x addr-ht sbits str-ht other-ht)hl-hspace-static-restore2826,117298 (defun hl-hspace-hons-clear (gc hs)hl-hspace-hons-clear2868,119129 (defun hl-fix-sbits-after-gc (sbits)hl-fix-sbits-after-gc2958,122869 (defun hl-rebuild-addr-ht (sbits addr-ht str-ht other-ht)hl-rebuild-addr-ht2979,123673 (defparameter *hl-addr-ht-resize-cutoff**hl-addr-ht-resize-cutoff*3016,125485 (defun hl-hspace-hons-wash (hs)hl-hspace-hons-wash3024,125858 (defun hl-maybe-resize-ht (size src)hl-maybe-resize-ht3172,132728 (defun hl-hspace-resize (str-ht-size nil-ht-size cdr-ht-size cdr-ht-eql-sizehl-hspace-resize3200,133895 (defun hl-get-final-cdr (alist)hl-get-final-cdr3277,136717 (defun hl-hspace-fast-alist-summary (hs)hl-hspace-fast-alist-summary3282,136818 (defun hl-hspace-hons-summary (hs)hl-hspace-hons-summary3325,138597 (defparameter *default-hs**default-hs*3409,141756 (defun hl-maybe-initialize-default-hs ()hl-maybe-initialize-default-hs3439,142903 (defun hl-maybe-initialize-default-hs-wrapper ()hl-maybe-initialize-default-hs-wrapper3446,143043 (defun hons (x y)hons3450,143167 (defun hons-copy (x)hons-copy3455,143303 (defun hons-copy-persistent (x)hons-copy-persistent3460,143440 (defun hons-equal (x y)hons-equal3466,143607 (defun hons-equal-lite (x y)hons-equal-lite3472,143812 (defun hons-summary ()hons-summary3477,143997 (defun hons-clear (gc)hons-clear3482,144120 (defun hons-wash ()hons-wash3487,144244 (defun hons-resize-fn (str-ht nil-ht cdr-ht cdr-ht-eqlhons-resize-fn3492,144361 (defun hons-acons (key val fal)hons-acons3504,144782 (defun hons-acons! (key val fal)hons-acons!3510,144999 (defun hons-shrink-alist (alist ans)hons-shrink-alist3515,145187 (defun hons-shrink-alist! (alist ans)hons-shrink-alist!3520,145338 (defun hons-get (key fal)hons-get3526,145516 (defun fast-alist-free (fal)fast-alist-free3532,145723 (defun fast-alist-len (fal)fast-alist-len3538,145942 (defun number-subtrees (x)number-subtrees3544,146159 (defun fast-alist-summary ()fast-alist-summary3549,146340 (defun make-fast-alist (alist)make-fast-alist3554,146475 (defmacro with-fast-alist-raw (alist form)with-fast-alist-raw3559,146615 (defmacro with-stolen-alist-raw (alist form)with-stolen-alist-raw3588,147847 (defmacro fast-alist-free-on-exit-raw (alist form)fast-alist-free-on-exit-raw3617,149106 (defun clear-hash-tables ()clear-hash-tables3624,149295 (defun wash-memory ()wash-memory3629,149409 memoize-raw.lisp,12915 (defconstant most-positive-mfixnum (1- (expt 2 60)))most-positive-mfixnum69,2711 (deftype mfixnum ()mfixnum71,2765 (defmacro our-syntax (&rest args)our-syntax86,3276 (defmacro our-syntax-nice (&rest args)our-syntax-nice112,4228 (defmacro our-syntax-brief (&rest args)our-syntax-brief122,4588 (defmacro ofn (&rest r) ; For forming strings.ofn131,4813 (defun-one-output ofnum (n) ; For forming numbers.ofnum134,4895 (defmacro ofni (&rest r) ; For forming symbols in package ACL2.ofni145,5220 (defmacro ofnm (&rest r) ; For forming uninterned symbols.ofnm148,5343 (defmacro oft (&rest r) ; For writing to *standard-output*.oft151,5451 (defmacro oftr (&rest r) ; For writing to *trace-output*.oftr155,5582 (defv *number-of-arguments-and-values-ht**number-of-arguments-and-values-ht*165,5858 (defun-one-output input-output-number-error (fn)input-output-number-error208,7395 (defun-one-output number-of-arguments (fn)number-of-arguments218,7889 (defun-one-output number-of-return-values (fn)number-of-return-values242,8711 (defg *float-ticks/second* 1.0)*float-ticks/second*265,9496 (defg *float-internal-time-units-per-second**float-internal-time-units-per-second*267,9529 (defabbrev internal-real-time ()internal-real-time273,9712 (defun-one-output float-ticks/second-init ()float-ticks/second-init281,10037 (defun-one-output safe-incf-aux-error (x inc where)safe-incf-aux-error303,10770 (defmacro safe-incf-aux (x inc where)safe-incf-aux307,10912 (defmacro safe-incf (x inc &optional where)safe-incf338,12083 (defparameter *count-pons-calls* t*count-pons-calls*384,13857 (defg *pons-call-counter* 0)*pons-call-counter*390,14101 (defg *pons-misses-counter* 0)*pons-misses-counter*391,14130 (defmacro maybe-count-pons-calls ()maybe-count-pons-calls395,14254 (defmacro maybe-count-pons-misses ()maybe-count-pons-misses399,14384 (defun-one-output assoc-no-error-at-end (x l)assoc-no-error-at-end403,14518 (defun-one-output too-long (x n)too-long419,15062 (defconstant atom-case-fudge (+ 129 (expt 2 25)))atom-case-fudge432,15669 (defconstant most-positive-fudge (1- (expt 2 24)))most-positive-fudge433,15719 (defconstant most-negative-fudge (- (expt 2 24)))most-negative-fudge434,15770 (defconstant -most-negative-fudge (- most-negative-fudge))-most-negative-fudge435,15820 (defun-one-output atom-case (s)atom-case438,15890 (defmacro sqmpf ()sqmpf457,16642 (defmacro hmnf ()hmnf460,16694 (defmacro static-hons-shift ()static-hons-shift466,16784 (defun-one-output addr-for (x y)addr-for470,16883 (defun-one-output pons (x y ht)pons529,19370 (defmacro pist* (table &rest x)pist*608,21885 (defvar *never-memoize-ht**never-memoize-ht*624,22514 (defun never-memoize-fn (fn)never-memoize-fn981,31074 (defun never-memoize-p (fn)never-memoize-p984,31148 (defparameter *record-bytes**record-bytes*1008,31947 (defparameter *record-calls* t*record-calls*1016,32267 (defparameter *record-hits* t*record-hits*1020,32389 (defparameter *record-hons-calls* t*record-hons-calls*1025,32564 (defparameter *record-mht-calls* t*record-mht-calls*1029,32690 (defparameter *record-pons-calls* t*record-pons-calls*1034,32891 (defparameter *record-time* t*record-time*1038,33029 (defv *report-bytes* #+Clozure t #-Clozure nil*report-bytes*1045,33216 (defv *report-calls* t*report-calls*1049,33372 (defv *report-calls-from* t*report-calls-from*1053,33477 (defv *report-calls-to* t*report-calls-to*1058,33649 (defv *report-hits* t*report-hits*1063,33829 (defv *report-hons-calls* t*report-hons-calls*1067,33977 (defv *report-mht-calls* t*report-mht-calls*1071,34118 (defv *report-pons-calls* t*report-pons-calls*1079,34537 (defv *report-time* t*report-time*1083,34660 (defv *report-on-memo-tables* t*report-on-memo-tables*1087,34794 (defv *report-on-pons-tables* t*report-on-pons-tables*1091,34927 (defg *memoize-info-ht**memoize-info-ht*1095,35060 (defrec memoize-info-ht-entrymemoize-info-ht-entry1106,35365 (defg *memoize-call-array**memoize-call-array*1150,37312 (defg *compute-array* (make-array 0)*compute-array*1182,39001 (defv *initial-max-memoize-fns* 500)*initial-max-memoize-fns*1199,39519 (defg *2max-memoize-fns* (* 2 *initial-max-memoize-fns*))*2max-memoize-fns*1201,39557 (defconstant *ma-bytes-index* 0)*ma-bytes-index*1203,39616 (defconstant *ma-hits-index* 1)*ma-hits-index*1204,39655 (defconstant *ma-mht-index* 2)*ma-mht-index*1205,39694 (defconstant *ma-hons-index* 3)*ma-hons-index*1206,39733 (defconstant *ma-pons-index* 4)*ma-pons-index*1207,39772 (defconstant *ma-initial-max-symbol-to-fixnum* 4)*ma-initial-max-symbol-to-fixnum*1209,39812 (defg *max-symbol-to-fixnum* *ma-initial-max-symbol-to-fixnum*)*max-symbol-to-fixnum*1211,39863 (defg *caller* (* *ma-initial-max-symbol-to-fixnum* *2max-memoize-fns*)*caller*1219,40116 (defn memoize-here-come (n)memoize-here-come1226,40372 (defun memoize-flush1 (lst)memoize-flush11269,42182 (defmacro memoize-flush (st)memoize-flush1287,43060 (defparameter *memo-max-sizes**memo-max-sizes*1298,43227 (defrec memo-max-sizes-entrymemo-max-sizes-entry1315,44018 (defun make-initial-memoize-hash-table (fn init-size)make-initial-memoize-hash-table1325,44466 (defun make-initial-memoize-pons-table (fn init-size)make-initial-memoize-pons-table1376,47107 (defun update-memo-max-sizes (fn pt-size mt-size)update-memo-max-sizes1409,48917 (defun print-memo-max-sizes ()print-memo-max-sizes1438,50455 (defmacro heap-bytes-allocated ()heap-bytes-allocated1470,51885 (defn sync-memoize-call-array ()sync-memoize-call-array1473,51967 (defun memoize-call-array-growmemoize-call-array-grow1498,52887 (defun-one-output symbol-to-fixnum-create (s)symbol-to-fixnum-create1527,54231 (defun-one-output symbol-to-fixnum (s)symbol-to-fixnum1551,55195 (defun-one-output fixnum-to-symbol (n)fixnum-to-symbol1557,55411 (defun-one-output coerce-index (x)coerce-index1562,55570 (defun-one-output memoize-eval-compile (def)memoize-eval-compile1575,55744 (defun-one-output memoizedp-raw (fn)memoizedp-raw1584,56012 (defg *hons-gentemp-counter* 0)*hons-gentemp-counter*1588,56119 (defun-one-output hons-gentemp (root)hons-gentemp1590,56152 (defun-one-output st-lst (st)st-lst1599,56482 (defun-one-output dcls (l)dcls1611,56858 (defg *assoc-eq-hack-ht* (hl-mht :test 'eql))*assoc-eq-hack-ht*1624,57268 (defn assoc-eq-hack (x y)assoc-eq-hack1627,57357 (defun abbrev (x &optionalabbrev1640,57916 (defun prine (obj &optional stream)prine1664,58924 (defun prine-alist (obj &optional stream)prine-alist1669,59055 (defun-one-output mf-trace-exit (fn nrv ans)mf-trace-exit1701,60100 (defg *memoize-fn-signature-error**memoize-fn-signature-error*1711,60419 (defg *sort-to-from-by-calls* nil)*sort-to-from-by-calls*1720,60798 (defvar *memoize-use-attachment-warning-p* t)*memoize-use-attachment-warning-p*1722,60834 (defun memoize-use-attachment-warning (fn at-fn)memoize-use-attachment-warning1724,60881 (defun-one-output memoize-fn-suffix (str sym)memoize-fn-suffix1751,62322 (defun-one-output mis-ordered-commutative-args (x y)mis-ordered-commutative-args1759,62547 (defun our-function-lambda-expression (sym)our-function-lambda-expression1787,63706 (defun memoize-look-up-def (fn cl-defun inline wrld)memoize-look-up-def1795,64033 (defun fix-time (ticks ctx)fix-time1838,65802 (defg *memoize-init-done* nil)*memoize-init-done*1848,66137 (defun memoize-fn (fn &key (condition t) (inline t) (trace nil)memoize-fn1850,66169 (defun-one-output unmemoize-fn (fn)unmemoize-fn2520,97360 (defun-one-output maybe-unmemoize (fn)maybe-unmemoize2570,99447 (defun-one-output memoized-functions ()memoized-functions2582,99969 (defun-one-output length-memoized-functions ()length-memoized-functions2592,100258 (defun-one-output unmemoize-all ()unmemoize-all2601,100542 (defun-one-output memoize-info (k)memoize-info2616,101082 (defun-one-output rememoize-all ()rememoize-all2662,103092 (defun-one-output uses-state (fn)uses-state2684,103755 (defun profile-fn (fn &rest r &key (condition nil) (inline nil)profile-fn2693,104117 (defun-one-output profiled-functions ()profiled-functions2700,104315 (defun-one-output unmemoize-profiled ()unmemoize-profiled2715,104756 (defmacro memoize-on-raw (fn x)memoize-on-raw2726,105044 (defmacro memoize-off-raw (fn x)memoize-off-raw2739,105557 (defun-one-output memoize-condition (fn)memoize-condition2752,106066 (defn global-restore-memoize ()global-restore-memoize2758,106227 (defg *memoize-summary-order-list**memoize-summary-order-list*2768,106505 (defg *memoize-summary-limit* 20*memoize-summary-limit*2799,107361 (defg *shorten-ht* (hl-mht :test 'eql))*shorten-ht*2805,107569 (defn shorten (x n)shorten2807,107610 (defg *memoize-summary-order-reversed* nil*memoize-summary-order-reversed*2836,108918 (defg *print-alist-width* 45)*print-alist-width*2842,109125 (defun-one-output print-alist (alist separation)print-alist2844,109156 (defmacro very-unsafe-incf (x inc &rest r)very-unsafe-incf2874,110228 (defmacro very-very-unsafe-aref-incf (ar loc)very-very-unsafe-aref-incf2888,110563 (defun-one-output pons-summary ()pons-summary2894,110776 (defun memoized-values (&optional (fn (memoized-functions)))memoized-values2935,112365 (defn print-call-stack ()print-call-stack2957,113200 (defun-one-output hons-calls (x)hons-calls2992,114429 (defun-one-output pons-calls (x)pons-calls3005,114798 (defun-one-output bytes-allocated (x)bytes-allocated3018,115165 (defun-one-output number-of-hits (x)number-of-hits3030,115560 (defun-one-output number-of-memoized-entries (x)number-of-memoized-entries3042,115952 (defun-one-output number-of-mht-calls (x)number-of-mht-calls3057,116446 (defun-one-output time-for-non-hits/call (x)time-for-non-hits/call3069,116847 (defun-one-output time/call (x)time/call3074,117021 (defun-one-output hits/calls (x)hits/calls3079,117159 (defun-one-output bytes-allocated/call (x)bytes-allocated/call3085,117320 (defn char-list-fraction (l)char-list-fraction3092,117488 (defn symbol-name-order (s)symbol-name-order3098,117619 (defun-one-output execution-order (s)execution-order3106,117844 (defn compute-calls-and-times ()compute-calls-and-times3116,118138 (defun-one-output number-of-calls (x)number-of-calls3159,120075 (defun-one-output print-not-called ()print-not-called3168,120318 (defun-one-output total-time (x)total-time3185,120875 (defn lex-> (l1 l2)lex->3196,121145 (defun-one-output memoize-summary-sort ()memoize-summary-sort3204,121337 (defun-one-output memoize-summary ()memoize-summary3223,121960 (defg *short-symbol-name-width* 30)*short-symbol-name-width*3299,124961 (defn short-symbol-name (sym)short-symbol-name3301,124998 (defun-one-output outside-p (x)outside-p3310,125322 (defun-one-output memoize-summary-after-compute-calls-and-times ()memoize-summary-after-compute-calls-and-times3314,125447 (defun-one-output empty-ht-p (x)empty-ht-p3629,139953 (defn clear-one-memo-and-pons-hash (l)clear-one-memo-and-pons-hash3633,140066 (defun-one-output clear-memoize-table (k)clear-memoize-table3657,141209 (defun-one-output clear-memoize-tables ()clear-memoize-tables3666,141396 (defn clear-memoize-call-array ()clear-memoize-call-array3681,141773 (defn clear-memoize-statistics ()clear-memoize-statistics3691,142102 (defun-one-output memoize-init ()memoize-init3696,142174 (defg *max-mem-usage* (expt 2 32)*max-mem-usage*3735,143460 (defg *gc-min-threshold* (expt 2 30)*gc-min-threshold*3743,143705 (defun-one-output set-gc-threshold (bound)set-gc-threshold3751,143837 (defmacro globlet (bindings &rest rest)globlet3760,144046 (defmacro globlet (&rest r)globlet3790,145282 (defmacro with-lower-overhead (&rest r)with-lower-overhead3801,145612 (defun acl2h-init-memoizations ()acl2h-init-memoizations3827,146708 (defun acl2h-init-unmemoizations ()acl2h-init-unmemoizations3864,148058 (defun looking-at (str1 str2 &key (start1 0) (start2 0))looking-at3893,149126 (defun meminfo (pat)meminfo3921,150184 (defvar *sol-gc-installed* nil)*sol-gc-installed*3956,151474 (defun set-and-reset-gc-thresholds ()set-and-reset-gc-thresholds3960,151518 (defun start-sol-gc ()start-sol-gc3983,152422 (defun-one-output acl2h-init ()acl2h-init4078,156295 (defun memstat (&rest r)memstat4177,160125 (defmacro memo-on (&rest r)memo-on4180,160182 (defmacro memo-off (&rest r)memo-off4183,160232 (defun clear-memo-tables (&rest r)clear-memo-tables4186,160284 (defn lower-overhead ()lower-overhead4193,160422 (defun our-gctime ()our-gctime4210,160895 (defun update-memo-entry-for-attachments (fns entry wrld)update-memo-entry-for-attachments4213,160978 (defun update-memo-entries-for-attachments (fns wrld state)update-memo-entries-for-attachments4235,161911 multi-threading-raw.lisp,3613 (defmacro without-interrupts (&rest forms)without-interrupts68,2994 (defmacro unwind-protect-disable-interrupts-during-cleanupunwind-protect-disable-interrupts-during-cleanup91,3890 (defun make-atomically-modifiable-counter (initial-value)make-atomically-modifiable-counter151,6759 (defmacro define-atomically-modifiable-counter (name initial-value)define-atomically-modifiable-counter161,6998 (defmacro atomically-modifiable-counter-read (counter)atomically-modifiable-counter-read164,7138 (defmacro atomic-incf (x)atomic-incf174,7342 (defmacro atomic-incf-multiple (counter count)atomic-incf-multiple209,8732 (defmacro atomic-decf (x)atomic-decf237,9909 (defun lockp (x)lockp261,10910 (defun make-lock (&optional lock-name)make-lock273,11297 (defmacro reset-lock (bound-symbol)reset-lock302,12540 (defmacro with-lock (bound-symbol &rest forms)with-lock312,12897 (defun run-thread (name fn-symbol &rest args)run-thread335,13668 (defun interrupt-thread (thread function &rest args)interrupt-thread370,15022 (defun kill-thread (thread)kill-thread390,15718 (defun all-threads ()all-threads402,15977 (defun current-thread ()current-thread412,16209 (defun thread-wait (fn &rest args)thread-wait422,16387 (defmacro with-potential-timeout (body &key timeout)with-potential-timeout437,16980 (defun make-condition-variable ()make-condition-variable532,21197 (defmacro signal-condition-variable (cv)signal-condition-variable551,21762 (defmacro broadcast-condition-variable (cv)broadcast-condition-variable580,22863 (defun wait-on-condition-variable (cv lock &key timeout)wait-on-condition-variable592,23152 (defstruct acl2-semaphoreacl2-semaphore627,24633 (defstruct acl2-semaphoreacl2-semaphore633,24772 (defun make-semaphore (&optional name)make-semaphore638,24893 (defun semaphorep (semaphore)semaphorep695,27666 (defun make-semaphore-notification ()make-semaphore-notification722,28667 (defun semaphore-notification-status (semaphore-notification-object)semaphore-notification-status737,29143 (defun clear-semaphore-notification-status (semaphore-notification-object)clear-semaphore-notification-status754,29787 (defun set-semaphore-notification-status (semaphore-notification-object)set-semaphore-notification-status771,30525 (defun signal-semaphore (semaphore)signal-semaphore780,30877 (defun wait-on-semaphore (semaphore &key notification timeout)wait-on-semaphore807,31791 (defvar *throwable-worker-thread**throwable-worker-thread*929,37533 (defun throw-all-threads-in-list (thread-list)throw-all-threads-in-list943,38210 (defun kill-all-threads-in-list (thread-list)kill-all-threads-in-list991,40264 (defun thread-name (thread)thread-name1003,40635 (defconstant *worker-thread-name* "Worker thread")*worker-thread-name*1013,40876 (defun worker-threads1 (threads)worker-threads11015,40928 (defun worker-threads ()worker-threads1023,41190 (defun all-worker-threads-are-dead ()all-worker-threads-are-dead1030,41351 (defun all-given-threads-are-reset (threads)all-given-threads-are-reset1037,41473 (defun all-worker-threads-are-dead-or-reset ()all-worker-threads-are-dead-or-reset1044,41682 (defun send-die-to-worker-threads ()send-die-to-worker-threads1056,42093 (defun kill-all-worker-threads ()kill-all-worker-threads1088,43418 (defun core-count-raw (&optional (ctx nil) default)core-count-raw1103,43915 (defvar *core-count**core-count*1123,44653 (defvar *unassigned-and-active-work-count-limit**unassigned-and-active-work-count-limit*1131,44892 (defconstant *max-idle-thread-count**max-idle-thread-count*1149,45854 futures-raw.lisp,6148 (defstruct st-futurest-future49,1893 (defmacro st-future (x)st-future59,2101 (defun st-future-read (st-future)st-future-read69,2471 (defun st-future-abort (st-future)st-future-abort83,3032 (defstruct atomic-notificationatomic-notification199,6821 (defstruct barrierbarrier218,7691 (defun broadcast-barrier (barrier)broadcast-barrier243,8941 (defun wait-on-barrier (barrier)wait-on-barrier256,9451 (defstruct mt-futuremt-future266,9779 (define-atomically-modifiable-counter *last-slot-saved* 0)*last-slot-saved*277,10024 (define-atomically-modifiable-counter *last-slot-taken* 0)*last-slot-taken*278,10083 (defvar *future-array*)*future-array*295,11068 (defvar *thread-array*)*thread-array*296,11092 (defvar *future-dependencies*)*future-dependencies*297,11116 (defparameter *future-queue-length-history**future-queue-length-history*299,11148 (defvar *current-thread-index**current-thread-index*305,11241 (defconstant *starting-core* 'start)*starting-core*312,11416 (defconstant *resumptive-core* 'resumptive)*resumptive-core*313,11453 (defvar *allocated-core**allocated-core*315,11498 (defvar *decremented-idle-future-thread-count* nil)*decremented-idle-future-thread-count*338,12667 (defvar *idle-future-core-count**idle-future-core-count*340,12720 (defvar *idle-future-resumptive-core-count**idle-future-resumptive-core-count*342,12806 (defvar *idle-core* (make-semaphore))*idle-core*344,12908 (define-atomically-modifiable-counter *idle-future-thread-count**idle-future-thread-count*346,12947 (defvar *future-added* (make-semaphore))*future-added*365,14032 (defvar *idle-resumptive-core* (make-semaphore))*idle-resumptive-core*367,14074 (defvar *threads-spawned* 0)*threads-spawned*370,14142 (define-atomically-modifiable-counter *unassigned-and-active-future-count**unassigned-and-active-future-count*372,14172 (define-atomically-modifiable-counter *total-future-count**total-future-count*382,14524 (defconstant *future-array-size* 200000)*future-array-size*394,14967 (defmacro faref (array subscript)faref396,15009 (defvar *resource-and-timing-based-parallelizations**resource-and-timing-based-parallelizations*403,15243 (defvar *resource-and-timing-based-serializations**resource-and-timing-based-serializations*408,15429 (defvar *resource-based-parallelizations**resource-based-parallelizations*413,15618 (defvar *resource-based-serializations**resource-based-serializations*418,15782 (defun reset-future-queue-length-history ()reset-future-queue-length-history423,15949 (defun reset-future-parallelism-variables ()reset-future-parallelism-variables426,16038 (defun reset-all-parallelism-variables ()reset-all-parallelism-variables498,18678 (defun futures-parallelism-buffer-has-space-available ()futures-parallelism-buffer-has-space-available505,18992 (defun not-too-many-futures-already-in-existence ()not-too-many-futures-already-in-existence512,19257 (defun futures-resources-available ()futures-resources-available556,21331 (defmacro unwind-protect-disable-interrupts-during-cleanupunwind-protect-disable-interrupts-during-cleanup574,22240 (define-atomically-modifiable-counter *threads-waiting-for-starting-core**threads-waiting-for-starting-core*596,23131 (defun claim-starting-core ()claim-starting-core607,23615 (defun claim-resumptive-core ()claim-resumptive-core631,24705 (defun free-allocated-core ()free-allocated-core669,26043 (defun early-terminate-children (index)early-terminate-children691,26850 (defvar *aborted-futures-via-flag* 0)*aborted-futures-via-flag*706,27452 (defvar *aborted-futures-total* 0)*aborted-futures-total*707,27490 (defvar *futures-resources-available-count* 0)*futures-resources-available-count*710,27545 (defvar *futures-resources-unavailable-count* 0)*futures-resources-unavailable-count*711,27592 (defun set-thread-check-for-abort-and-funcall (future)set-thread-check-for-abort-and-funcall713,27642 (defvar *throwable-future-worker-thread**throwable-future-worker-thread*762,29825 (defun wait-for-a-closure ()wait-for-a-closure778,30433 (defvar *busy-wait-var* 0)*busy-wait-var*860,35369 (defvar *current-waiting-thread* nil)*current-waiting-thread*861,35396 (defvar *fresh-waiting-threads* 0)*fresh-waiting-threads*862,35434 (defun make-tclet-thrown-symbol1 (tags first-tag)make-tclet-thrown-symbol1867,35579 (defun make-tclet-thrown-symbol (tags)make-tclet-thrown-symbol878,35896 (defun make-tclet-bindings1 (tags)make-tclet-bindings1881,35990 (defun make-tclet-bindings (tags)make-tclet-bindings888,36178 (defun make-tclet-thrown-tags1 (tags)make-tclet-thrown-tags1891,36264 (defun make-tclet-thrown-tags (tags)make-tclet-thrown-tags897,36433 (defun make-tclet-catches (rtags body thrown-tag-bindings)make-tclet-catches900,36525 (defun make-tclet-cleanups (thrown-tags cleanups)make-tclet-cleanups910,36891 (defmacro throw-catch-let (tags body cleanups)throw-catch-let918,37143 (defun eval-a-closure ()eval-a-closure989,39646 (defun eval-closures ()eval-closures1044,42000 (defun number-of-idle-threads-and-threads-waiting-for-a-starting-core ()number-of-idle-threads-and-threads-waiting-for-a-starting-core1079,43152 (defun spawn-closure-consumers ()spawn-closure-consumers1088,43455 (defun make-future-with-closure (closure)make-future-with-closure1122,45285 (defun add-future-to-queue (future)add-future-to-queue1162,47400 (defmacro mt-future (x)mt-future1174,47732 (defun mt-future-read (future)mt-future-read1265,51772 (defvar *aborted-futures-via-throw* 0)*aborted-futures-via-throw*1312,53815 (defvar *almost-aborted-future-count* 0)*almost-aborted-future-count*1313,53854 (defun mt-future-abort (future)mt-future-abort1315,53896 (defun abort-future-indices (indices)abort-future-indices1387,57344 (defun print-non-nils-in-array (array n)print-non-nils-in-array1401,57856 (defun futures-still-in-flight ()futures-still-in-flight1410,58135 (defmacro future (x)future1422,58485 (defun future-read (x)future-read1425,58526 (defun future-abort (x)future-abort1428,58572 (defun abort-futures (futures)abort-futures1431,58620 parallel-raw.lisp,4364 (defstruct parallelism-piece ; piece of workparallelism-piece213,9963 (defparameter *reset-parallelism-variables* nil)*reset-parallelism-variables*348,16353 (defparameter *reset-core-count-too**reset-core-count-too*350,16403 (defun reset-parallelism-variables ()reset-parallelism-variables360,16828 (defun eval-and-save-result (work)eval-and-save-result422,18939 (defun pop-work-and-set-thread ()pop-work-and-set-thread440,19673 (defun consume-work-on-work-queue-when-there ()consume-work-on-work-queue-when-there471,21080 (defun spawn-worker-threads-if-needed ()spawn-worker-threads-if-needed617,28131 (defun add-work-list-to-queue (work-list)add-work-list-to-queue661,30311 (defun combine-array-results-into-list (result-array current-position acc)combine-array-results-into-list685,31421 (defun remove-thread-array-from-work-queue-recremove-thread-array-from-work-queue-rec695,31745 (defun remove-thread-array-from-work-queue (thread-array)remove-thread-array-from-work-queue729,33013 (defun terminate-siblings (thread-array)terminate-siblings738,33336 (defun generate-work-list-from-closure-list-recgenerate-work-list-from-closure-list-rec751,33974 (defun generate-work-list-from-closure-listgenerate-work-list-from-closure-list771,34835 (defun pargs-parallelism-buffer-has-space-available ()pargs-parallelism-buffer-has-space-available799,35995 (defun not-too-many-pieces-of-parallelism-work-already-in-existence ()not-too-many-pieces-of-parallelism-work-already-in-existence803,36176 (defun parallelism-resources-available ()parallelism-resources-available814,36751 (defun throw-threads-in-array (thread-array current-position)throw-threads-in-array832,37681 (defun decrement-children-left (children-left-ptr semaphore-notification-obj)decrement-children-left861,39140 (defun wait-for-children-to-finishwait-for-children-to-finish869,39459 (defun wait-for-resumptive-parallelism-resources ()wait-for-resumptive-parallelism-resources887,40210 (defun early-terminate-children-and-rewaitearly-terminate-children-and-rewait907,41150 (defun prepare-to-wait-for-children ()prepare-to-wait-for-children940,42428 (defun parallelize-closure-list (closure-list &optional terminate-early-function)parallelize-closure-list959,43169 (defun parallelize-fn (parent-fun-name arg-closures &optional terminate-early-function)parallelize-fn1069,48024 (defmacro closure-for-expression (x)closure-for-expression1085,48731 (defmacro closure-list-for-expression-list (x)closure-list-for-expression-list1088,48799 (defun parallelism-condition (gran-form-exists gran-form)parallelism-condition1097,49082 (defmacro pargs (&rest forms)pargs1107,49401 (defun plet-doublets (bindings bsym n)plet-doublets1127,50104 (defun make-closures (bindings)make-closures1134,50313 (defun identity-list (&rest rst) rst)identity-list1147,50810 (defun make-list-until-non-declare (remaining-list acc)make-list-until-non-declare1149,50849 (defun parse-additional-declare-forms-for-let (x)parse-additional-declare-forms-for-let1155,51114 (defmacro plet (&rest forms)plet1165,51510 (defmacro pand (&rest forms)pand1189,52475 (defmacro por (&rest forms)por1210,53261 (defun signal-semaphores (sems)signal-semaphores1232,54030 (defmacro spec-mv-let (bindings computation body)spec-mv-let1238,54184 (defun number-of-active-threads-aux (threads acc)number-of-active-threads-aux1277,55525 (defun number-of-active-threads ()number-of-active-threads1289,55865 (defun number-of-threads-waiting-on-a-child-aux (threads acc)number-of-threads-waiting-on-a-child-aux1292,55951 (defun number-of-threads-waiting-on-a-child ()number-of-threads-waiting-on-a-child1304,56335 (defun future-queue-length ()future-queue-length1307,56445 (defun total-number-of-threads ()total-number-of-threads1325,57206 (defvar *refresh-rate-indicator* 0)*refresh-rate-indicator*1328,57267 (defmacro value-of-symbol (var)value-of-symbol1330,57304 (defun acl2p-sum-list1 (lst acc)acl2p-sum-list11344,57930 (defun acl2p-sum-list (lst)acl2p-sum-list1350,58086 (defun average-future-queue-size ()average-future-queue-size1357,58241 (defun print-interesting-parallelism-variables-str ()print-interesting-parallelism-variables-str1361,58391 (defun print-interesting-parallelism-variables ()print-interesting-parallelism-variables1420,60474 serialize-raw.lisp,3484 (defparameter *ser-verbose* nil)*ser-verbose*269,12198 (defmacro ser-time? (form)ser-time?271,12232 (defmacro ser-print? (msg &rest args)ser-print?276,12314 (defmacro ser-write-char (x stream)ser-write-char290,12862 (defmacro ser-write-byte (x stream)ser-write-byte293,12943 (defmacro ser-read-char (stream)ser-read-char296,13048 (defmacro ser-read-byte (stream)ser-read-byte301,13275 (defun ser-encode-magic (stream)ser-encode-magic304,13373 (defun ser-decode-magic (stream)ser-decode-magic311,13607 (defun ser-encode-nat-fixnum (n stream)ser-encode-nat-fixnum371,16345 (defun ser-encode-nat-large (n stream)ser-encode-nat-large387,16885 (defmacro ser-encode-nat (n stream)ser-encode-nat405,17568 (defun ser-decode-nat-large (shift value stream)ser-decode-nat-large417,17885 (defmacro ser-decode-nat-body (shift)ser-decode-nat-body435,18544 (defun ser-decode-nat (stream)ser-decode-nat469,19951 (defun ser-encode-rat (x stream)ser-encode-rat498,20874 (defun ser-decode-rat (stream)ser-decode-rat504,21070 (defun ser-encode-complex (x stream)ser-encode-complex533,22164 (defun ser-decode-complex (stream)ser-decode-complex538,22310 (defun ser-encode-str (x stream)ser-encode-str564,23389 (defun ser-decode-str (version hons-mode stream)ser-decode-str573,23727 (defstruct ser-decoderser-decoder605,24863 (defun ser-encode-nats (x stream)ser-encode-nats624,25485 (defun ser-decode-and-load-nats (decoder stream)ser-decode-and-load-nats631,25686 (defun ser-encode-rats (x stream)ser-encode-rats651,26435 (defun ser-decode-and-load-rats (decoder stream)ser-decode-and-load-rats658,26637 (defun ser-encode-complexes (x stream)ser-encode-complexes677,27321 (defun ser-decode-and-load-complexes (decoder stream)ser-decode-and-load-complexes684,27532 (defun ser-encode-chars (x stream)ser-encode-chars703,28217 (defun ser-decode-and-load-chars (decoder stream)ser-decode-and-load-chars710,28421 (defun ser-encode-strs (x stream)ser-encode-strs729,29094 (defun ser-decode-and-load-strs (hons-mode decoder stream)ser-decode-and-load-strs736,29294 (defun ser-encode-package (pkg x stream)ser-encode-package780,31261 (defun ser-decode-and-load-package (check-packagesp decoder stream)ser-decode-and-load-package789,31570 (defun ser-encode-packages (alist stream)ser-encode-packages817,32914 (defun ser-decode-and-load-packages (check-packagesp decoder stream)ser-decode-and-load-packages825,33238 (defun ser-hashtable-init (size test)ser-hashtable-init841,33776 (defstruct ser-encoderser-encoder854,34211 (defmacro ser-see-obj (x table)ser-see-obj924,37257 (defun ser-gather-atoms (x encoder)ser-gather-atoms934,37510 (defun ser-make-atom-map (encoder)ser-make-atom-map1070,43758 (defun ser-encode-consesser-encode-conses1178,48408 (defmacro ser-decode-loop (version hons-mode)ser-decode-loop1230,50856 (defun ser-decode-and-load-conses (hons-mode decoder stream)ser-decode-and-load-conses1267,52419 (defun ser-encode-fals (encoder)ser-encode-fals1355,56706 (defun ser-decode-and-restore-fals (decoder hons-mode stream)ser-decode-and-restore-fals1383,57845 (defun ser-encode-atoms (encoder)ser-encode-atoms1410,59185 (defun ser-encode-to-stream (obj stream)ser-encode-to-stream1425,59808 (defun ser-decode-and-load-atoms (check-packagesp hons-mode decoder stream)ser-decode-and-load-atoms1498,62752 (defun ser-decode-from-stream (check-packagesp hons-mode stream)ser-decode-from-stream1508,63170 acl2-sources/tau.lisp0000664002132200015000000226237512222115527014255 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; In this file we define the Tau system, a collection of data strutures and ; algorithms for reasoning quickly about monadic predicates. However, we need ; certain basic facilities from the rest of ACL2's sources and we have put them ; all in the following ``prelude.'' See the Essay on the Tau System below for ; a discussion of the tau design. ; Prelude: General-Purpose Functions Having Nothing Specific to do with Tau ; A ``singleton evg list'' is a singleton list with an evg in it. A ``neg-evg ; list'' is a duplicate-free list of singleton evg lists, ordered ascending ; almost according to lexorder. The ``almost'' is due to the fact that we let ; the singleton list containing NIL be the smallest thing in our ordering. So ; a typical neg-evg list might be ((NIL) (0) (1) (2) (A) (B) (C)). ; Foreshadowing: a neg-evg list represents the evgs ruled out by a tau. If a ; tau were to contain the neg-evgs above, then the tau would include the ; conjuncts (not (equal x 'NIL)), (not (equal x '0)), ..., and (not (equal x ; 'C)). We want NIL in the front so that we can rapidly answer the question ; ``does this tau mean the subject is non-NIL?'' ; We define the ``almost-lexorder'' function as well as functions for ; determining whether a singleton is a member of a neg-evg list and for ; inserting a singleton into such a list. These functions are optimized ; exploiting the fact that every item compared is known to be a singleton, so ; instead of using lexorder on (x) and (y) we use it on x and y, i.e., we ; always compare just the cars of the lists. (defun almost-lexorder-singletons (x y) (cond ((eq (car x) nil) t) ((eq (car y) nil) nil) (t (lexorder (car x) (car y))))) ; We actually don't use almost-lexorder-singletons in the membership and ; insertion functions because we make special cases for nil. (defun member-nil-neg-evgs (neg-evg-lst) (and (consp neg-evg-lst) (eq (car (car neg-evg-lst)) nil))) (defun member-neg-evgs1 (evg neg-evg-lst) ; Evg is non-NIL and neg-evg-lst is a neg-evg list that does not contain (NIL). (cond ((endp neg-evg-lst) nil) ((lexorder (car (car neg-evg-lst)) evg) (or (equal evg (car (car neg-evg-lst))) (member-neg-evgs1 evg (cdr neg-evg-lst)))) (t nil))) (defun member-neg-evgs (x neg-evg-lst) ; X is a singleton list and neg-evg-lst is a neg-evg list -- a list of ; singletons ordered almsot with lexorder but with (nil) as the smallest item ; in the ordering. We return t if x is a member of neg-evg-lst and nil ; otherwise. (cond ((endp neg-evg-lst) nil) ((equal (car x) (car (car neg-evg-lst))) t) ((eq (car x) nil) nil) (t (member-neg-evgs1 (car x) (cdr neg-evg-lst))))) (defun insert-neg-evgs1 (evg x neg-evg-lst) ; X is a singleton evg list containing evg. Evg is non-nil. Neg-evg-lst is a ; neg-evg list that does not contain (NIL) and does not contain x. We insert x ; into neg-evg-lst. (cond ((endp neg-evg-lst) (cons x nil)) ((lexorder (car (car neg-evg-lst)) evg) (cons (car neg-evg-lst) (insert-neg-evgs1 evg x (cdr neg-evg-lst)))) (t (cons x neg-evg-lst)))) (defun insert-neg-evgs (x neg-evg-lst) (cond ((endp neg-evg-lst) (cons x neg-evg-lst)) ((eq (car x) nil) (cons x neg-evg-lst)) ((eq (car (car neg-evg-lst)) nil) (cons (car neg-evg-lst) (insert-neg-evgs1 (car x) x (cdr neg-evg-lst)))) (t (insert-neg-evgs1 (car x) x neg-evg-lst)))) ; Now we define some other sorting functions. (defun merge-car-< (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((< (car (car l1)) (car (car l2))) (cons (car l1) (merge-car-< (cdr l1) l2))) (t (cons (car l2) (merge-car-< l1 (cdr l2)))))) (defun merge-sort-car-< (l) ; Merge-sort, where elements a and b are compared via (car a) < (car b). (cond ((null (cdr l)) l) (t (merge-car-< (merge-sort-car-< (evens l)) (merge-sort-car-< (odds l)))))) (defun merge-cadr-< (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((< (cadr (car l1)) (cadr (car l2))) (cons (car l1) (merge-cadr-< (cdr l1) l2))) (t (cons (car l2) (merge-cadr-< l1 (cdr l2)))))) (defun merge-sort-cadr-< (l) ; Merge-sort, where elements a and b are compared via (cadr a) < (cadr b). (cond ((null (cdr l)) l) (t (merge-cadr-< (merge-sort-cadr-< (evens l)) (merge-sort-cadr-< (odds l)))))) (defun strip-caddrs (x) (declare (xargs :guard (all->=-len x 3))) (cond ((null x) nil) (t (cons (caddar x) (strip-caddrs (cdr x)))))) ; In forming rules from terms we often strip out individual branches of the ; term, e.g., (implies (and h1 h2) (and c1 (implies h3 c2))) is treated as ; though it were (and (implies (and h1 h2) c1) (implies (and h1 h2 h3) c2)), ; except after distributing the IMPLIES over the concluding ANDs, we do not ; form a term but just traffic in list of pairs (((h1 h2) . c1) ((h1 h2 h3) ; . c2)). This is called ``unprettyifying.'' (defun unprettyify/add-hyps-to-pairs (hyps lst) ; Each element of lst is a pair of the form (hypsi . concli), where hypsi ; is a list of terms. We append hyps to hypsi in each pair. (cond ((null lst) nil) (t (cons (cons (append hyps (caar lst)) (cdar lst)) (unprettyify/add-hyps-to-pairs hyps (cdr lst)))))) (defun flatten-ands-in-lit (term) (case-match term (('if t1 t2 t3) (cond ((equal t2 *nil*) (append (flatten-ands-in-lit (dumb-negate-lit t1)) (flatten-ands-in-lit t3))) ((equal t3 *nil*) (append (flatten-ands-in-lit t1) (flatten-ands-in-lit t2))) (t (list term)))) (& (cond ((equal term *t*) nil) (t (list term)))))) (defun unprettyify (term) ; This function returns a list of pairs (hyps . concl) such that the ; conjunction of all (implies (and . hyps) concl) is equivalent to ; term. Hyps is a list of hypotheses, implicitly conjoined. Concl ; does not begin with an AND (of course, its a macro, but concl ; doesn't begin with an IF that represents an AND) or IMPLIES. ; In addition concl doesn't begin with an open lambda. ; This function is used to extract the :REWRITE rules from a term. ; Lambdas are sort of expanded to expose the conclusion. They are not ; expanded in the hypotheses, or within an function symbol other than ; the top-level IFs and IMPLIES. But top-level lambdas (those enclosing ; the entire term) are blown away. ; Thus, if you have a proposed :REWRITE rule ; (implies (and p q) (let ((x a)) (equal (f x) b))) ; it will be converted to ; (implies (and p q) (equal (f a) b)). ; The rule ; (let ((x a)) (implies (and (p x) (q x)) (equal (f x) b))) [1] ; will become ; (implies (and (p a) (q a)) (equal (f a) b)). [2] ; But ; (implies (let ((x a)) (and (p x) (q x))) [3] ; (equal (let ((x a)) (f x)) b)) ; stays untouched. In general, once you've moved the let into a ; hypothesis it is not seen or opened. Once you move it into the ; equivalence relation of the conclusion it is not seen or opened. ; Note that this processing of lambdas can cause terms to be duplicated ; and simplified more than once (see a in [2] compared to [3]). (case-match term (('if t1 t2 t3) (cond ((equal t2 *nil*) (append (unprettyify (dumb-negate-lit t1)) (unprettyify t3))) ((equal t3 *nil*) (append (unprettyify t1) (unprettyify t2))) (t (list (cons nil term))))) (('implies t1 t2) (unprettyify/add-hyps-to-pairs (flatten-ands-in-lit t1) (unprettyify t2))) ((('lambda vars body) . args) (unprettyify (subcor-var vars args body))) (& (list (cons nil term))))) (defun reprettyify (hyps concl wrld) (cond ((null hyps) (untranslate concl t wrld)) ((null (cdr hyps)) `(IMPLIES ,(untranslate (car hyps) t wrld) ,(untranslate concl t wrld))) (t `(IMPLIES (AND ,@(untranslate-lst hyps t wrld)) ,(untranslate concl t wrld))))) ; Rockwell Addition: A major change is the removal of THEs from ; many terms. ; Essay on the Removal of Guard Holders ; We now develop the code to remove certain trivial calls, such as those ; generated by THE, from a term. Suppose for example that the user types (THE ; type expr), type is translated (using translate-declaration-to-guard) into a ; predicate in one variable. The variable is always VAR. Denote this ; predicate as (guard VAR). Then the entire (THE type expr) is translated into ; ((LAMBDA (VAR) (THE-CHECK (guard VAR) 'type VAR)) expr). The-check is ; defined to have a guard that logically is its first argumennt, so when we ; generate guards for the translation above we generate the obligation to prove ; (guard expr). Futhermore, the definition of the-check is such that unless ; the value of state global 'guard-checking-on is :none, executing it in the ; *1* function tests (guard expr) at runtime and signals an error. ; But logically speaking, the definition of (THE-check g x y) is y. Hence, ; (THE type expr) ; = ((LAMBDA (VAR) (THE-check (guard VAR) 'type VAR)) expr) ; = ((LAMBDA (VAR) VAR) expr) ; = expr. ; Observe that this is essentially just the expansion of certain non-rec ; functions (namely, THE-CHECK and the lambda application) and ; IF-normalization. ; We belabor this obvious point because until Version_2.5, we kept the THEs in ; bodies, which injected them into the theorem proving process. We now remove ; them from the stored BODY property. It is not obvious that this is a benign ; change; it might have had unintended side-affects on other processing, e.g., ; guard generation. But the BODY property has long been normalized with ; certain non-rec fns expanded, and so we argue that the removal of THE could ; have been accomplished by the processing we were already doing. ; But there is another place we wish to remove such ``guard holders.'' We want ; the guard clauses we generate not to have these function calls in them. The ; terms we explore to generate the guards WILL have these calls in them. But ; the output we produce will not, courtesy of the following code which is used ; to strip the guard holders out of a term. ; Starting with Version_2.8 the ``guard holders'' code appears elsewhere, ; because remove-guard-holders needs to be defined before it is called by ; constraint-info. (mutual-recursion (defun remove-guard-holders1 (changedp0 term) ; We return (mv changedp new-term), where new-term is provably equal to term, ; and where if changedp is nil, then changedp0 is nil and new-term is identical ; to term. The second part can be restated as follows: if changedp0 is true ; then changedp is true (a kind of monotonicity), and if the resulting term is ; distinct from the input term then changedp is true. Intuitively, if changedp ; is true then new-term might be distinct from term but we don't know that ; (especially if changedp0 is true), but if changedp is nil then we know that ; new-term is just term. ; See the Essay on the Removal of Guard Holders. ; WARNING: The resulting term is in quote normal form. We take advantage of ; this fact in our implementation of :by hints, in function ; apply-top-hints-clause1, to increase the chances that the "easy-winp" case ; holds. ; WARNING. Remove-guard-holders is used in constraint-info, ; induction-machine-for-fn1, and termination-machine, so (remove-guard-holders1 ; term) needs to be provably equal to term, for every term, in the ground-zero ; theory. In fact, because of the use in constraint-info, it needs to be the ; case that for any axiomatic event e, (remove-guard-holders e) can be ; substituted for e without changing the logical power of the set of axioms. ; Actually, we want to view the logical axiom added by e as though ; remove-guard-holders had been applied to it, and hence RETURN-LAST and ; MV-LIST appear in *non-instantiable-primitives*. (cond ((variablep term) (mv changedp0 term)) ((fquotep term) (mv changedp0 term)) ((or (eq (ffn-symb term) 'RETURN-LAST) (eq (ffn-symb term) 'MV-LIST)) ; Recall that PROG2$ (hence, RETURN-LAST) is used to attach the dcl-guardian of ; a LET to the body of the LET for guard generation purposes. A typical call ; of PROG2$ is (PROG2$ dcl-guardian body), where dcl-guardian has a lot of IFs ; in it. Rather than distribute them over PROG2$ and then when we finally get ; to the bottom with things like (prog2$ (illegal ...) body) and (prog2$ T ; body), we just open up the prog2$ early, throwing away the dcl-guardian. (remove-guard-holders1 t (car (last (fargs term))))) ((flambdap (ffn-symb term)) (case-match term ((('LAMBDA ('VAR) ('THE-CHECK & & 'VAR)) val) (remove-guard-holders1 t val)) ((('LAMBDA formals ('RETURN-LAST ''MBE1-RAW & logic)) . args) ; pattern for equality variants (mv-let (changedp1 args1) (remove-guard-holders1-lst args) (declare (ignore changedp1)) (mv-let (changedp2 logic2) (remove-guard-holders1 nil logic) (declare (ignore changedp2)) (mv t (subcor-var formals args1 logic2))))) (& (mv-let (changedp1 lambda-body) (remove-guard-holders1 nil (lambda-body (ffn-symb term))) (mv-let (changedp2 args) (remove-guard-holders1-lst (fargs term)) (cond ((or changedp1 changedp2) (mv t (mcons-term (if changedp1 (make-lambda (lambda-formals (ffn-symb term)) lambda-body) (ffn-symb term)) args))) (t (mv changedp0 term)))))))) (t (mv-let (changedp1 args) (remove-guard-holders1-lst (fargs term)) (cond ((null changedp1) (cond ((quote-listp args) (let ((new-term (mcons-term (ffn-symb term) args))) (cond ((equal term new-term) ; even if not eq (mv changedp0 term)) (t (mv t new-term))))) (t (mv changedp0 term)))) (t (mv t (mcons-term (ffn-symb term) args)))))))) (defun remove-guard-holders1-lst (lst) (cond ((null lst) (mv nil nil)) (t (mv-let (changedp1 a) (remove-guard-holders1 nil (car lst)) (mv-let (changedp2 b) (remove-guard-holders1-lst (cdr lst)) (cond ((or changedp1 changedp2) (mv t (cons a b))) (t (mv nil lst)))))))) ) (defun remove-guard-holders (term) ; Return a term equal to term, but slightly simplified. See also the warning ; in remove-guard-holders1. (mv-let (changedp result) (remove-guard-holders1 nil term) (declare (ignore changedp)) result)) (defun remove-guard-holders-lst (lst) ; Return a list of terms element-wise equal to lst, but slightly simplified. (mv-let (changedp result) (remove-guard-holders1-lst lst) (declare (ignore changedp)) result)) (defun remove-guard-holders1-lst-lst (lst) (cond ((null lst) (mv nil nil)) (t (mv-let (changedp1 a) (remove-guard-holders1-lst (car lst)) (mv-let (changedp2 b) (remove-guard-holders1-lst-lst (cdr lst)) (cond ((or changedp1 changedp2) (mv t (cons a b))) (t (mv nil lst)))))))) (defun remove-guard-holders-lst-lst (lst) ; Return a list of clauses element-wise equal to lst, but slightly simplified. (mv-let (changedp result) (remove-guard-holders1-lst-lst lst) (declare (ignore changedp)) result)) ; Before moving on, we develop the code to translate a type-prescription ; to a term so that we can recognize if a type-prescription can be ; represented as a tau signature rule. ; The next few functions are used to produced the formulas represented by ; type-prescriptions. (defun convert-returned-vars-to-term-lst (term vars) (cond ((null vars) nil) (t (cons (mcons-term* 'equal term (car vars)) (convert-returned-vars-to-term-lst term (cdr vars)))))) (defun implicate (t1 t2) ; We return a term equivalent to (IMPLIES t1 t2). (cond ((equal t1 *t*) t2) ((equal t1 *nil*) *t*) ((equal t2 *t*) *t*) ((equal t2 *nil*) (dumb-negate-lit t1)) (t (mcons-term* 'implies t1 t2)))) ; We now develop the function that converts a type-set into a term. (defrec type-set-inverter-rule ((nume . ts) terms . rune) nil) ; A type-set-inverter-rule states that x has type-set ts iff the conjunction of ; terms{X/x} is true. Thus, for example, if :ts is *ts-integer* then :terms is ; '((INTEGERP X)). ; WARNING: See the warning in convert-type-set-to-term if you are ever ; tempted to generalize type-set-inverter-rules to allow hypotheses that ; may be FORCEd or CASE-SPLITd! ; A type-set, s, is a disjunction of the individual bits in it. Thus, to ; create a term whose truth is equivalent to the claim that X has type-set s it ; is sufficient to find any type-set-inverter-rule whose :ts is a subset of s ; and then disjoin the :term of that rule to the result of recursively creating ; the term recognizing s minus :ts. This assumes one has a rule for each ; single type bit. ; The following is the initial setting of the world global variable ; 'type-set-inverter-rules. WARNING: EACH TERM IN :TERMS MUST BE IN TRANSLATED ; FORM. The list is ordered in an important way: the larger type-sets are at ; the front. This ordering is exploited by convert-type-set-to-term-lst which ; operates by finding the largest recognized type set group and knocks it out ; of the type set. ;; RAG - I added a rule for realp, non-zero real, non-negative real, ;; non-positive real, positive real, negative real, irrational, ;; negative irrational, positive irrational, and complex. (defconst *initial-type-set-inverter-rules* (list (make type-set-inverter-rule ;;; 11 (14) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-complement *ts-cons*) :terms '((atom x))) (make type-set-inverter-rule ;;; 6 (9) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-acl2-number* :terms '((acl2-numberp x))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (7) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-real* :terms '((realp x))) (make type-set-inverter-rule ;;; 5 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-rational* :terms '((rationalp x))) (make type-set-inverter-rule ;;; 5 (8) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-intersection *ts-acl2-number* (ts-complement *ts-zero*)) :terms '((acl2-numberp x) (not (equal x '0)))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (6) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-intersection *ts-real* (ts-complement *ts-zero*)) :terms '((realp x) (not (equal x '0)))) (make type-set-inverter-rule ;;; 4 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-intersection *ts-rational* (ts-complement *ts-zero*)) :terms '((rationalp x) (not (equal x '0)))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (4) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-union *ts-positive-real* *ts-zero*) :terms '((realp x) (not (< x '0)))) (make type-set-inverter-rule ;;; 3 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-union *ts-positive-rational* *ts-zero*) :terms '((rationalp x) (not (< x '0)))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (4) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-union *ts-negative-real* *ts-zero*) :terms '((realp x) (not (< '0 x)))) (make type-set-inverter-rule ;;; 3 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-union *ts-negative-rational* *ts-zero*) :terms '((rationalp x) (not (< '0 x)))) (make type-set-inverter-rule ;;; 3 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-integer* :terms'((integerp x))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-intersection *ts-integer* (ts-complement *ts-zero*)) :terms '((integerp x) (not (equal x '0)))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (3) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-positive-real* :terms'((realp x) (< '0 x))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-positive-rational* :terms'((rationalp x) (< '0 x))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (3) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-negative-real* :terms'((realp x) (< x '0))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-negative-rational* :terms'((rationalp x) (< x '0))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-union *ts-positive-integer* *ts-zero*) :terms '((integerp x) (not (< x '0)))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts (ts-union *ts-negative-integer* *ts-zero*) :terms '((integerp x) (not (< '0 x)))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (2) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-non-ratio* :terms'((realp x) (not (rationalp x)))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-ratio* :terms'((rationalp x) (not (integerp x)))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (1) bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-negative-non-ratio* :terms'((realp x) (not (rationalp x)) (< x '0))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-negative-ratio* :terms'((rationalp x) (not (integerp x)) (< x '0))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-negative-integer* :terms'((integerp x) (< x '0))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (1) bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-positive-non-ratio* :terms'((realp x) (not (rationalp x)) (< '0 x))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-positive-ratio* :terms'((rationalp x) (not (integerp x)) (< '0 x))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-positive-integer* :terms'((integerp x) (< '0 x))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (2) bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-complex* :terms'((complexp x))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-complex-rational* :terms'((complex-rationalp x))) #+:non-standard-analysis (make type-set-inverter-rule ;;; _ (1) bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-complex-non-rational* :terms'((complexp x) (not (complex-rationalp x)))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-zero* :terms'((equal x '0))) (make type-set-inverter-rule ;;; 3 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-symbol* :terms'((symbolp x))) (make type-set-inverter-rule ;;;2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-boolean* :terms'((if (equal x 't) 't (equal x 'nil)))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-cons* :terms'((consp x))) (make type-set-inverter-rule ;;; 2 bits :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-true-list* :terms'((true-listp x))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-improper-cons* :terms'((consp x) (not (true-listp x)))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-proper-cons* :terms'((consp x) (true-listp x))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-non-t-non-nil-symbol* :terms '((symbolp x) (not (equal x 't)) (not (equal x 'nil)))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-t* :terms'((equal x 't))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-nil* :terms'((equal x 'nil))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-string* :terms'((stringp x))) (make type-set-inverter-rule ;;; 1 bit :nume nil :rune *fake-rune-for-anonymous-enabled-rule* :ts *ts-character* :terms'((characterp x))))) (defun convert-type-set-to-term-lst (ts rules ens lst ttree) ; We map over the list of type-set-inverter rules and each time we find an ; enabled rule whose :ts is a subset of ts, we accumulate its conjoined :terms ; and its :rune, and knock off those bits of ts. We return (mv lst ttree), ; where lst is a list of terms in the variable X whose disjunction is ; equivalent to ts. (cond ((null rules) (mv (reverse lst) ttree)) ((and (enabled-numep (access type-set-inverter-rule (car rules) :nume) ens) (ts= (access type-set-inverter-rule (car rules) :ts) (ts-intersection (access type-set-inverter-rule (car rules) :ts) ts))) (convert-type-set-to-term-lst (ts-intersection ts (ts-complement (access type-set-inverter-rule (car rules) :ts))) (cdr rules) ens (add-to-set-equal (conjoin (access type-set-inverter-rule (car rules) :terms)) lst) (push-lemma (access type-set-inverter-rule (car rules) :rune) ttree))) (t (convert-type-set-to-term-lst ts (cdr rules) ens lst ttree)))) ; Here is one of the most basic functions in the theorem prover. It is ; surprising we got so far into the sources without defining it! ; (Students of our code should study this elementary function just to see how ; we recur through terms. The function instantiates a variable, i.e., ; (subst-var new old form) substitutes the term new for the variable old in the ; term form. For example, (subst-var '(car a) 'x '(foo x y)) = '(foo (car a) ; y).) (mutual-recursion (defun subst-var (new old form) (declare (xargs :guard (and (pseudo-termp new) (variablep old) (pseudo-termp form)))) (cond ((variablep form) (cond ((eq form old) new) (t form))) ((fquotep form) form) (t (cons-term (ffn-symb form) (subst-var-lst new old (fargs form)))))) (defun subst-var-lst (new old l) (declare (xargs :guard (and (pseudo-termp new) (variablep old) (pseudo-term-listp l)))) (cond ((endp l) nil) (t (cons (subst-var new old (car l)) (subst-var-lst new old (cdr l)))))) ) (defun convert-type-set-to-term (x ts ens w ttree) ; Given a term x and a type set ts we generate a term that expresses the ; assertion that "x has type set ts". E.g., if x is the term (FN X Y) and ts ; is *ts-rational* then our output will be (RATIONALP (FN X Y)). We return (mv ; term ttree), where term is the term and ttree contains the 'lemmas used. We ; do not use disabled type-set-inverters. ; Note: It would be a major change to make this function force assumptions. ; If the returned ttree contains 'assumption tags then the primary use of ; this function, namely the expression of type-alists in clausal form so that ; assumptions can be attacked as clauses, becomes problematic. Don't glibbly ; generalize type-set-inverter rules to force assumptions. (cond ((ts= ts *ts-unknown*) (mv *t* ttree)) ; The following case is probably fine, but we commented it out for Version_3.5 ; because of a soundness issue that no longer exists (because we don't create ; type-prescription rules for subversive functions; see ; putprop-type-prescription-lst). We will leave it commented out (we are soon ; releasing Version_4.0 as of this writing), because it seems to be an ; unimportant case that hasn't been missed. ; ((and (ts= ts *ts-t*) ; (ts-booleanp x ens w)) ; (mv x ttree)) ((ts-complementp ts) (mv-let (lst ttree) (convert-type-set-to-term-lst (ts-complement ts) (global-val 'type-set-inverter-rules w) ens nil ttree) (mv (subst-var x 'x (conjoin (dumb-negate-lit-lst lst))) ttree))) (t (mv-let (lst ttree) (convert-type-set-to-term-lst ts (global-val 'type-set-inverter-rules w) ens nil ttree) (mv (subst-var x 'x (disjoin lst)) ttree))))) (defun convert-type-prescription-to-term (tp ens wrld) ; Tp is a type-prescription. We generate a term that expresses it relative to ; the supplied ens. We will usually store this term in the :corollary of tp ; itself; generally the current :corollary field of tp is *t* right now because ; tp was generated by putprop-initial-type-prescriptions. We return ; the generated corollary term and a ttree citing the type-set-inverter ; rules used. (mv-let (concl ttree) (convert-type-set-to-term (access type-prescription tp :term) (access type-prescription tp :basic-ts) ens wrld nil) (mv (implicate (conjoin (access type-prescription tp :hyps)) (disjoin (cons concl (convert-returned-vars-to-term-lst (access type-prescription tp :term) (access type-prescription tp :vars))))) ttree))) ; The function all-runes-in-ttree, defined below, is typically used by each ; event function to recover the supporting runes from a ttree. (defun all-runes-in-lmi (lmi wrld ans) ; When we collect all the runes "in" lmi we actually expand symbolic lmis, ; e.g., ASSOC-OF-APP, to the list of all runes based on that symbol. (cond ((symbolp lmi) (union-equal (strip-cdrs (getprop lmi 'runic-mapping-pairs nil 'current-acl2-world wrld)) ans)) ((or (eq (car lmi) :instance) (eq (car lmi) :functional-instance)) (all-runes-in-lmi (cadr lmi) wrld ans)) ((eq (car lmi) :theorem) ans) (t (add-to-set-equal lmi ans)))) (defun all-runes-in-lmi-lst (lmi-lst wrld ans) (cond ((null lmi-lst) ans) (t (all-runes-in-lmi-lst (cdr lmi-lst) wrld (all-runes-in-lmi (car lmi-lst) wrld ans))))) (defun all-runes-in-var-to-runes-alist (alist ans) (cond ((null alist) ans) (t (all-runes-in-var-to-runes-alist (cdr alist) (union-equal (cdr (car alist)) ans))))) (defun all-runes-in-var-to-runes-alist-lst (lst ans) (cond ((endp lst) ans) (t (all-runes-in-var-to-runes-alist-lst (cdr lst) (all-runes-in-var-to-runes-alist (car lst) ans))))) (mutual-recursion (defun all-runes-in-elim-sequence-lst (lst ans) (cond ((endp lst) ans) (t (all-runes-in-elim-sequence-lst (cdr lst) (all-runes-in-elim-sequence (car lst) ans))))) (defun all-runes-in-elim-sequence (elim-sequence ans) ; Elim-sequence is a list of elements, each of which is of the form ; (rune rhs lhs alist restricted-vars var-to-runes-alist ttree) ; 0 1 2 3 4 5 6 (cond ((null elim-sequence) ans) (t (all-runes-in-elim-sequence (cdr elim-sequence) (all-runes-in-ttree (nth 6 (car elim-sequence)) (all-runes-in-var-to-runes-alist (nth 5 (car elim-sequence)) (add-to-set-equal (nth 0 (car elim-sequence)) ans))))))) (defun all-runes-in-ttree-fc-derivation-lst (lst ans) (cond ((endp lst) ans) (t (all-runes-in-ttree-fc-derivation-lst (cdr lst) (add-to-set-equal (access fc-derivation (car lst) :rune) (all-runes-in-ttree (access fc-derivation (car lst) :ttree) ans)))))) (defun all-runes-in-ttree-find-equational-poly-lst (lst ans) (cond ((endp lst) ans) (t (all-runes-in-ttree-find-equational-poly-lst (cdr lst) (let ((val (car lst))) ; Shape: (poly1 . poly2) (all-runes-in-ttree (access poly (car val) :ttree) (all-runes-in-ttree (access poly (cdr val) :ttree) ans))))))) (defun all-runes-in-ttree (ttree ans) ; Ttree is any ttree produced by this system. We sweep it collecting into ans ; every rune in it. (cond ((endp ttree) ans) (t (all-runes-in-ttree (cdr ttree) (let ((tag (caar ttree)) (lst (cdar ttree))) (case tag (lemma ; Shape: rune (union-equal lst ans)) (:by ; Shape: (lmi-lst thm-cl-set constraint-cl k) ; As of this writing, there aren't any runes in an lmi list that are being ; treated as runes. Imagine proving a lemma that is then supplied in a :use ; hint. It shouldn't matter, from the point of view of tracking RUNES, whether ; that lemma created a rewrite rule that is currently disabled or whether that ; lemma has :rule-classes nil. ans) (:bye ; Shape: (name . cl), where name is a "new" name, not the name of something ; used. ans) (:or ; Shape (parent-cl-id nil-or-branch-number ((user-hint1 . hint-settings1) ...)) ; See comment about the :by case above. ans) (:use ; Shape: ((lmi-lst (hyp1 ...) cl k) . n) ; See comment for the :by case above. ans) (:cases ; Shape: ((term1 ... termn) . clauses) ans) (preprocess-ttree ; Shape: ttree (all-runes-in-ttree-lst lst ans)) (assumption ; Shape: term ans) (pt ; Shape: parent tree - just numbers ans) (fc-derivation ; Shape: fc-derivation record (all-runes-in-ttree-fc-derivation-lst lst ans)) (find-equational-poly ; Shape: (poly1 . poly2) (all-runes-in-ttree-find-equational-poly-lst lst ans)) (variables ; Shape: var-lst ans) ((splitter-if-intro splitter-case-split splitter-immed-forced) ; Shape: rune (Note: objects are a subset 'lemmas objects.) ans) (elim-sequence ; Shape: ((rune rhs lhs alist restricted-vars var-to-runes-alist ttree) ...) (all-runes-in-elim-sequence-lst lst ans)) ((literal ;;; Shape: term hyp-phrase ;;; tilde-@ phrase equiv ;;; equiv relation bullet ;;; term target ;;; term cross-fert-flg ;;; boolean flg delete-lit-flg ;;; boolean flg clause-id ;;; clause-id binding-lst ;;; alist binding vars to terms terms ;;; list of terms restricted-vars) ;;; list of vars ans) ((rw-cache-nil-tag rw-cache-any-tag) ; Shape: rw-cache-entry ans) (var-to-runes-alist ; Shape: (...(var . (rune1 ...))...) (all-runes-in-var-to-runes-alist-lst lst ans)) (ts-ttree ; Shape: ttree (all-runes-in-ttree-lst lst ans)) ((irrelevant-lits clause) ; Shape: clause ans) (hidden-clause ; Shape: t ans) (abort-cause ; Shape: symbol ans) (bddnote ; Shape: bddnote ; A bddnote has a ttree in it. However, whenever a bddnote is put into a given ; ttree, the ttree from that bddnote is also added to the same given ttree. ; So, we don't really think of a bddnote as containing a "ttree" per se, but ; rather, a sort of data structure that is isomorphic to a ttree. ans) (case-limit ; Shape: t ans) (sr-limit ; Shape: t ans) (:clause-processor ; Shape: (clause-processor-hint . clauses) ans) (otherwise (er hard 'all-runes-in-ttree "This function must know every possible tag so that ~ it can recover the runes used in a ttree. The ~ unknown tag ~x0, associated with the list of ~ values ~x1, has just been encountered." tag lst)))))))) (defun all-runes-in-ttree-lst (lst ans) (cond ((endp lst) ans) (t (all-runes-in-ttree-lst (cdr lst) (all-runes-in-ttree (car lst) ans))))) ) (defdoc introduction-to-the-tau-system ":Doc-Section introduction-to-the-tau-system a decision procedure for runtime types~/ This doc topic is the main source of information about the tau system and discusses the general idea behind the procedure and how to exploit it. ~i[A ``Type-Checker'' for an Untyped Language] Because ACL2 is an untyped language it is impossible to type check it. All functions are total. An ~i[n]-ary function may be applied to any combination of ~i[n] ACL2 objects. The syntax of ACL2 stipulates that ~c[(]~i[fn a1...an]~c[)] is a well-formed term if ~i[fn] is a function symbol of ~i[n] arguments and the ~i[ai] are well-formed terms. No mention is made of the ``types'' of terms. That is what is meant by saying ACL2 is an untyped language. Nevertheless, the system provides a variety of monadic Boolean function symbols, like ~ilc[natp], ~ilc[integerp], ~ilc[alistp], etc., that recognize different ``types'' of objects at runtime. Users typically define many more such recognizers for domain-specific ``types.'' Because of the prevalence of such ``types,'' ACL2 must frequently reason about the inclusion of one ``type'' in another. It must also reason about the consequences of functions being defined so as to produce objects of certain ``types'' when given arguments of certain other ``types.'' Because the word ``type'' in computer science tends to imply syntactic or semantic restrictions on functions, we avoid using that word henceforth. Instead, we just reason about monadic Boolean predicates. You may wish to think of ``tau'' as synonymous with ``type'' but without any suggestion of syntactic or semantic restrictions.~/ ~i[Design Philosophy] The following basic principles were kept in mind when developing tau checker and may help you exploit it. (1) The tau system is supposed to be a lightweight, fast, and helpful decision procedure for an elementary subset of the logic focused on monadic predicates and function signatures. (2) Most subgoals produced by the theorem prover are not in any decidable subset of the logic! Thus, decision procedures fail to prove the vast majority of the formulas they see and will be net time-sinks if tried too often no matter how fast they are. Tau reasoning is used by the prover as part of ~c[preprocess-clause], one of the first proof techniques the system tries. The tau system filters out ``obvious'' subgoals. The tau system is only tried when subgoals first enter the waterfall and when they are stable under simplification. (3) The tau system is ``benign'' in the sense that the only way it contributes to a proof is to eliminate (prove!) subgoals. It does not rewrite, simplify, or change formulas. Tau reasoning is not used by the rewriter. The tau system either eliminates a subgoal by proving it or leaves it unchanged. (4) It is impossible to infer automatically the relations between arbitrary recursively defined predicates and functions. Thus, the tau system's knowledge of tau relationships and function signatures is gleaned from theorems stated by the user and proved by the system. (5) Users wishing to build effective ``type-checkers'' for their models must learn how rules affect the tau system's behavior. There are two main forms of tau rules: those that reveal inclusion/exclusion relations between named tau predicates, e.g., that 16-bit naturals are also 32-bit naturals, ~bv[] (implies (n16p x) (n32p x)), ~ev[] and signatures for all relevant functions, e.g., writing a 32-bit natural to a legal slot in a register file produces a register file: ~bv[] (implies (and (natp n) (< n 16) (n32p val) (register-filep regs)) (register-filep (update-nth n val regs))). ~ev[] For a complete description of acceptable forms see ~c[:]~ilc[tau-system]. (6) The tau system is ``greedy'' in its efforts to augment its database. Its database is potentially augmented when rules of ~i[any] ~c[:rule-class] (see ~c[:]~ilc[rule-classes]) are proved. For example, if you make a ~c[:]~ilc[rewrite] or ~c[:]~ilc[type-prescription] rule which expresses a relationship between one tau and another (e.g., that ~c[(P x)] implies ~c[(Q x)]), ACL2 will build it into the tau database. The rule-class ~c[:]~ilc[tau-system] can be used to add a rule to the tau database without adding any other kind of rule. (7) Greediness is forced into the design by benignity: the tau system may ``know'' some fact that the rewriter does not, and because tau reasoning is not used in rewriting, that missing fact must be conveyed to the rewriter through some other class of rule, e.g., a ~c[:]~ilc[rewrite] or ~c[:]~ilc[type-prescription] or ~c[:]~ilc[forward-chaining] rule. By making the tau system greedy, we allow the user to program the rewriter and the tau system simultaneously while keeping them separate. However, this means you must keep in mind the effects of a rule on ~i[both] the rewriter and the tau system and use ~c[:]~ilc[tau-system] rules explicitly when you want to ``talk'' just to the tau system. (8) Tau rules are built into the database with as much preprocessing as possible (e.g., the system transitively closes inclusion/exclusion relationships at rule-storage time) so the checker can be fast. (9) For speed, tau does not track dependencies and is not sensitive to the enabled/disabled status (see ~ilc[enable] and ~ilc[disable]) of rules. Once a fact has been built into the tau database, the only way to prevent that fact from being used is by disabling the entire tau system, by disabling ~c[(:]~ilc[executable-counterpart]~c[ tau-system)]. If any tau reasoning is used in a proof, the rune ~c[(:]~ilc[executable-counterpart]~c[ tau-system)] is reported in the summary. For a complete list of all the runes in the tau database, evaluate ~c[(global-val 'tau-runes (w state))]. Any of these associated theorems could have been used. These design criteria are not always achieved! For example, the tau system's ``greediness'' can be turned off (see ~ilc[set-tau-auto-mode]), the tau database can be regenerated from scratch to ignore disabled rules (see ~ilc[regenerate-tau-database]), and disabling the ~ilc[executable-counterpart] of a tau predicate symbol will prevent the tau system from trying to run the predicate on constants. The tau system's benignity can be frustrating since it might ``know'' something the rewriter does not. More problematically, the tau system is not always ``fast'' and not always ``benign!'' The typical way tau reasoning can slow a proof down is by evaulating expensive tau predicates on constants. The typical way tau reasoning can hurt a previously successful proof is by proving some subgoals (!) and thus causing the remaining subgoals to have different ~il[clause-identifier]s, thus making explicit hints no longer applicable. We deal with such problems in ~ilc[dealing-with-tau-problems]. ~i[Technical Details] The tau system consists of both a database and an algorithm for using the database. The database contains theorems that match certain schemas allowing them to be stored in the tau database. Roughly speaking the schemas encode ``inclusion'' and ``exclusion'' relations, e.g., that ~c[natp] implies ~c[integerp] and that ~c[integerp] implies not ~c[consp], and they encode ``signatures'' of functions, e.g., theorems that relate the output of a function to the input, provided only tau predicates are involved. By ``tau predicates'' we mean the application of a monadic Boolean-valued function symbol, the equality of something to a quoted constant, an arithmetic ordering relation between something and a rational constant, or the logical negation of such a term. Here are some examples of tau predicates: ~bv[] (natp i) (not (consp x)) (equal y 'MONDAY) (not (eql 23 k)) (< 8 max) (<= max 24) ~ev[] Synonyms for ~ilc[equal] include ~ilc[=], ~ilc[eq], and ~ilc[eql]. Note that negated equalites are also allowed. The arithmetic ordering relations that may be used are ~ilc[<], ~ilc[<=], ~ilc[>=], and ~ilc[>]. One of the arguments to every arithmetic ordering relation must be an integer or rational constant for the term to be treated as a tau predicate. A ``tau'' is a data object representing a set of signed (positive or negative) tau predicates whose meaning is the conjunction of the literals in the set. When we say that a term ``has'' a given tau we mean the term satisfies all of the recognizers in that tau. The tau algorithm is a decision procedure for the logical theory described (only) by the rules in the database. The algorithm takes a term and a list of assumptions mapping subterms (typically variable symbols) to tau, and returns the tau of the given term. When the system is called upon to decide whether a term satisfies a given monadic predicate, it computes the tau of the term and asks whether the predicate is in that set. More generally, to determine if a term satisfies a tau, ~i[s], we compute a tau, ~i[r], for the term and ask whether ~i[s] is a subset of ~i[r]. To determine whether a constant, ~i[c], satisfies tau ~i[s] we apply each of the literals in ~i[s] to ~i[c]. Evaluation might, of course, be time-consuming for complex user-defined predicates. The tau database contains rules derived from definitions and theorems stated by the user. See ~c[:]~ilc[tau-system] for a description of the acceptable forms of tau rules. To shut off the greedy augmentation of the tau database, ~pl[set-tau-auto-mode]. This may be of use to users who wish to tightly control the rules in the tau database. To add a rule to the tau database without adding any other kind of rule, use the rule class ~c[:]~ilc[tau-system]. There are some slight complexities in the design related to how we handle events with both ~c[:tau-system] corollaries and corollaries of other ~c[:rule-classes], see ~ilc[set-tau-auto-mode]. To prevent tau reasoning from being used, disable the ~c[:]~ilc[executable-counterpart] of ~c[tau-system], i.e., execute ~bv[] (in-theory (disable (:executable-counterpart tau-system))) ~ev[] or, equivalently, ~bv[] (in-theory (disable (tau-system))) ~ev[] To prevent tau from being used in the proof of a particular subgoal, locally disable the ~c[:]~ilc[executable-counterpart] of ~c[tau-system] with a local ~c[:in-theory] hint (~pl[hints]). The event command ~ilc[tau-status] is a macro that can be used to toggle both whether tau reasoning is globally enabled and whether the tau database is augmented greedily. For example, the event ~bv[] (tau-status :system nil :auto-mode nil) ~ev[] prevents the tau system from being used in proofs and prevents the augmentation of the tau database by rules other than those explicitly labeled ~c[:]~ilc[tau-system]. To see what the tau system ``knows'' about a given function symbol ~pl[tau-data]. To see the entire tau database, ~pl[tau-database]. To regenerate the tau database using only the runes listed in the current enabled theory, ~pl[regenerate-tau-database].~/ :cite tau-system :cite set-tau-auto-mode :cite regenerate-tau-database") (defdoc dealing-with-tau-problems ":Doc-Section introduction-to-the-tau-system some advice on dealing with problems caused by the tau system~/ For background on the tau system, ~pl[introduction-to-the-tau-system]. The two most common problems caused by the tau system have to do with the system's interaction with ``legacy'' proof scripts. Such scripts may suffer because they were not designed to exploit tau reasoning and which may configure the tau database in quite incomplete and arbitrary ways. The two most common problems we have seen are (a) significant slow downs in a few proofs and (b) failed proof attempts due to hints being misapplied because the tau system caused subgoals to be renumbered. We discuss the rather limited means of dealing with these problems here. In ~il[future-work-related-to-the-tau-system] we list some major inadequacies of the tau system. If the tau system contributes to a proof, the ~il[rune] ~c[(:]~ilc[executable-counterpart]~c[ tau-system)] will be listed among the Rules in the Summary. However, merely by being attempted the tau system can slow down proofs in which it makes no contribution. The most brutal and fool-proof way to isolate a proof from the tau system is to disable the entire system. This can be done globally by ~bv[] (in-theory (disable (tau-system))) ; (:executable-counterpart tau-system) ~ev[] or locally with a subgoal specific hint: ~bv[] ... :hints ((\"...subgoal id...\" :in-theory (disable (tau-system)))) ~ev[] Conducting a proof with and without the participation of the tau system can help you determine whether tau reasoning is helping or hurting.~/ ~i[Dealing with Slowdowns] The ~ilc[time-tracker] utility was added to allow users to investigate whether excessive amounts of time are being spent in a given function. It was then used to annotate the code for the tau system as described in ~il[time-tracker-tau]. The result is that if ``excessive'' time is spent in tau reasoning, messages to that effect will be printed to the proof log. The question is: aside from disabling the tau system how can the proof be sped up? There are two common causes of slowdown in the tau system. The first stems from the system's use of ~c[:]~ilc[executable-counterpart]s to determine whether a constant has a given tau. Recall that a tau is a conjunction of monadic predicates. To determine whether some constant satisfies the tau, the predicates are executed. If you have a hard-to-compute predicate this can be very slow. The most typical such predicates in ACL2 applications are those that check invariants, e.g., that recognize ``good states'' or ``well-formed data.'' These are often written inefficiently because they are intended only for used in theorems and, before the tau system was added, they may have never been applied to constants. The most common constants tau predicates are applied to are ~c[0], ~c[T], and ~c[NIL], although different models may stress other constants. To understand why ~c[NIL] for example is frequently tested, if the test of an ~c[IF]-expression is computed to have tau ~i[s] then the next question we ask is ``does ~c[nil] satisfy ~i[s]?'' You may determine whether the tau system is spending time executing tau predicates by observing the rewriter ~-[] ~pl[dmr] ~-[] or by interrupting the system and getting a backtrace (~pl[set-debugger-enable]). If excessive time is being spent in a tau predicate, a draconian solution is to disable the ~c[:]~ilc[executable-counterpart] of that predicate, for example in either of these equivalent ways. The tau system does not execute disabled ~c[:]~ilc[executable-counterpart]s. ~bv[] (in-theory (disable (:executable-counterpart foo))) (in-theory (disable (foo))) ~ev[] In either case above, you may prefer to provide local ~c[:]~ilc[in-theory] ~c[:]~ilc[hints] rather than ~c[:in-theory] ~il[events]. Disabling the executable counterpart of expensive tau predicates will weaken the tau system, probably only negligibly, because it can no longer run the predicates to determine whether they admits given constants. A more sophisticated solution is to make the tau system record values of the ~c[:]~ilc[logic]-mode function in question, so that the system will look up the necessary values rather than running the function every time the question arises. It will look up recorded values whether the executable counterpart of the tau predicate is enabled or disabled. Here is an example of a lemma that can provide such a solution. See the discussion of the ~i[Eval] form of ~c[:]~ilc[tau-system] rules. ~bv[] (defthm lemma (and (foo 0) (foo 17) (foo t) (not (foo '(a b c)))) :rule-classes :tau-system) ~ev[] It might be difficult to determine ~i[which] constants are being repeatedly tested, although tracing (~ilc[trace$]) suspected tau predicates will show what they are being called on. At the moment there are no better user-level tools to discover this. However, some users may wish to consider the following hack: In the ACL2 source file ~c[tau.lisp], immediately after the definition of the system function ~c[ev-fncall-w-tau-recog], there is a comment which contains some raw Lisp code that can be used to investigate whether tau's use of evaluation on constants is causing a problem and to determine which constants are involved. The second main cause of slowdowns by the tau system is that the system contains ``too many'' conjunctive rules (see the ~i[Conjunctive] form in ~ilc[tau-system]). Unfortunately, we have no tools for either identifying the problem or addressing it! That said, let us tell you what we do know! Conjunctive rules are used to ``complete'' each tau as it is built. Referring to the ~c[weekdayp] example in ~ilc[tau-system], if a tau is constructed that recognizes weekdays but not ~c[MON], ~c[TUE], ~c[THU], or ~c[FRI], it is completed by adding that the tau recognizes (only) ~c[WED]. This means that when we construct a tau we scan all known conjunctive rules and see whether all but one of the literals of any conjunctive rule are present. This can be expensive. To mitigate this expense, the tau system caches the computation on a per proof basis (the cache is cleared after every proof). To learn what conjunctive rules there are in your system, evaluate ~bv[] (assoc 'tau-conjunctive-rules (tau-database (w state))) ~ev[] Perhaps by sending the implementors that list, we can think of ways to index the conjunctive rules to save time. ~i[Dealing with Misapplied Hints] The second common problem caused by the tau system in legacy proof scripts is that it can cause subgoals to be renumbered and thus cause hints to be missed. The only ways to address this problem is either to disable the tau system (locally or globally by disabling ~c[(:executable-counterpart tau-system)]) or change the legacy hints to use the new subgoal names.~/") (defdoc future-work-related-to-the-tau-system ":Doc-Section introduction-to-the-tau-system some tau system problems that we hope someone will address~/ The tau system is inexpressive compared to modern polymorphic type systems ~-[] something that may be unavoidable in an untyped first-order language. However, we believe its effectiveness could be greatly increased by the development of some additional tools. We also believe that most of these tools could be provided by ACL2 users creating certified community books that exploit the basic tools already provided. It is likely the initial attempts to create such books will show the inadequacy of some of the current algorithms and data structures in the tau system and might point the way to improvements. As implementors of ACL2 our preference is to support the improvement of the core algorithms and data structures and provide customizable hooks allowing users to exploit them by developing effective and convenient interfaces. However, we want the further elaboration and optimization of the tau system to be based on user experience not just speculation. Some tools we ~i[think] might help are listed below. We invite volunteers and further suggestions.~/ ~i[A tau-centric signature notation] for use in function definitions, exploited by a macro replacing ~c[defun] so that input-output relationships phrased in tau terms are proved as ~c[:tau-system] rules immediately after functions are introduced: We have, for example, experimented with a book defining a macro that allows the definition of the function ~c[ap] (append) accompanied by a signature rule. Subsequent ~c[defsig] events can add other signatures in the same notation. ~bv[] (defsig ap (true-listp * true-listp ==> true-listp) (x y) (if (consp x) (cons (car x) (ap (cdr x) y)) y)) (defsig ap (integer-listp * integer-listp ==> integer-listp)) ~ev[] This experimental book provides succinct syntax for all tau signatures. For example, that book parses the signature ~bv[] (NATP (/= 3 5 7) (< 16) * TRUE-LISTP ==> BOOLEANP * INTEGER-LISTP * NATP) ~ev[] to be the signature of a function with two inputs and three outputs. The first input must be a natural number, different from 3, 5, and 7, and less than 16. The second input must be a true list. The first output is a boolean, the second a list of integers, and the third a natural. To express this signature for function ~c[fn] as a formula requires significantly more typing by the user: ~bv[] (IMPLIES (AND (NATP X) (NOT (EQUAL X 3)) (NOT (EQUAL X 5)) (NOT (EQUAL X 7)) (< X 16) (TRUE-LISTP Y)) (AND (BOOLEANP (MV-NTH 0 (FN X Y))) (INTEGER-LISP (MV-NTH 1 (FN X Y))) (NATP (MV-NTH 2 (FN X Y))))) ~ev[] We suspect that the provision of some succinct tau-centric notation (not necessarily the one above) for signatures at definition-time will mean users get more benefit from the tau system. ~i[Some tau inference mechanisms]: This phrase suggests two different improvements. One is to implement a mechanism that adds or completes signatures for function definitions by exploiting knowledge of commonly used recursive schemas and the signatures of the subroutines in the body. For example, the definition of ~c[ap] above rather obviously has the signature ~bv[] (integer-listp * integer-listp ==> integer-listp) ~ev[] and many others just based on the two recursive schemas that (a) collect certain elements from lists and (b) check that all elements have a certain property. The other ``tau inference'' improvement is to implement a mechanism for inferring relations between user-defined Booleans, perhaps by exploiting example generation, testing, and knowledge of recursive schemas. For example, it is fairly obvious that ~ilc[symbol-alistp] implies ~ilc[alistp]. Making the user state these relations invites omissions that render the tau system very unpredictable. ~i[A tau assistant]: It would be useful to have a way to find out what tau rules are missing. Given a term that the user believes should ``obviously'' have some tau (``type'') what rules might be added to make the tau algorithm compute that expected tau? For example, if ~c[(g x)] is known to satisfy ~c[P] and ~c[(f x)] is known to satisfy ~c[R] when its argument satisfies ~c[S]: ~bv[] g : T ==> P f : S ==> R ~ev[] then if the user asserts that ~c[(f (g x))] ``ought'' to have tau ~c[R], one plausible suggestion is the simple tau rule that ~c[(P x)] implies ~c[(S x)]. Such assistance ~-[] while still confronting an undecidable problem ~-[] might be easier to implement within the tau framework than more generally in ACL2. (Many users have wanted such an assistant to suggest lemmas for the rewriter.)~/") ; Essay on the Tau System ; This essay touches on a wide variety of topics in the design of the tau ; system. It is consequently divided into many subsections with the following ; headers. We recommend scanning this list for subsections of interest; an ; introduction to tau is provided by the first six or so, in order. ; On Tau-Like Terms ; On the Name ``tau'' ; On Some Basic Ideas ; On Tau Recognizers -- Part 1 ; On the Tau Database and General Design ; On Tau Recognizers -- Part 2 ; On Tau Intervals and < versus <= ; On the Tau Data Structure ; On the Built-in Tau and the Abuse of Tau Representation ; On the Additional Restrictions on Tau Fields ; On the Use of ENS by Function Evaluation in the Tau System ; On the Most Basic Implications of Being in an Interval ; On Firing Signature Rules ; On Comparing Bounds ; On the Proof of Correctness of upper-bound-< ; On the Near-Subset Relation for Intervals ; On the Tau Database ; On Closing the Database under Conjunctive Rules ; On Converting Theorems in the World to Tau Rules ; On Tau-Like Terms ; On Loops in Relieving Dependent Hyps in Tau Signature Rules ; On the Tau Msgp Protocol ; On Removal of Ancestor Literals -- The Satriana Hack Prequel ; On the Motivation for Tau-Subrs ; On the Tau Completion Alist (calist) ; On Disjoining Tau ; On the Role of Rewriting in Tau ; On Tau-Clause -- Using Tau to Prove or Mangle Clauses ; On Tau Debugging Features ; On Tau-Like Terms ; The Tau system is a collection of data structures and algorithms for ; reasoning quickly about the things we know to be true about a term. It was ; motivated by our frustration over the time it took ACL2 to do elementary ; guard-like proofs -- ``proofs'' that could be almost instantaneous in a ; strongly typed language. A tau is a representation of a set of ``tau ; recognizers'' denoting the function obtained by conjoining the recognizers. ; To say that e has a given tau is to say that the function denoted by the tau ; is true of e. Informally, ``the'' tau of a term is all the tau-like things ; we know to be true about the term. The tau recognizers include all the named ; Boolean functions of one argument, their negations, and all the functions ; (lambda (x) (EQUAL x ')) and (lambda (x) (< x ')), and their ; negations. ; On the Name ``tau'' ; ``Tau'' might stand for ``Types Are Unnecessary'' and if it did the name ; would be ironic because this whole idea is a tribute to type checking! The ; truth is that ``tau'' doesn't stand for anything! When we designed this ; system we needed a name for the meta objects denoting the set of monadic ; predicates (``signed tau recognizers'') that we know to hold about a term in ; a given context. ; We were tempted to call such a set a ``type'' of the term but felt that was ; inappropriate because the literature on types is so extensive and we have no ; interest in defending the proposition that our objects are ``types''. We ; really don't think of them as anythin more than sets of recognizers known ; known to be true. We could not use the words ``sorts'' or ``kinds'' for ; similar reasons. So we temporarily adopted the name ``recognizer sets'' ; abbreviated ``rs.'' But this was an unfortunate acronym for two reasons: ; typically pronounced ``are ess'' it was unclear whether to write ``given an ; rs'' or ``given a rs'' since the former ``sounds right'' when ``rs'' is ; pronounced ``are ess'' but wrong when ``rs'' is read ``recognizer set.'' ; Furthermore, is ``rs'' singular or plural? Did we really want to write ; ``Given a set of rses?'' Nevertheless, we got this idea working, in a ; stand-alone way, under the name rs. Only when it was integrated into ACL2 ; proper did adopt the name ``tau'' for these objects. We chose ``tau'' ; because it had no fixed connotation in the literature, it is short, and it ; started with a pronounced consonant. We use ``tau'' as both a singular noun ; and a plural one. We might say ``t1 is a tau'' and we might say that the ; ``tau of x and y are t1 and t2 respectively''. ; On Some Basic Ideas ; We identify certain expressions as primitive ``tau recognizers'' and then ; develop a data structure, tau, to represent conjunctions of these tau ; recognizers. Speaking precisely in the language of metamathematics, tau ; recognizers and tau are predicates in the semantic sense. In particular in ; ACL2's terms, they represent not function SYMBOLS or TERMS but FUNCTIONS. ; They can be applied to any single object and yield T or NIL to indicate ; whether the object has the property or not. For example, we might wish to ; express the property of being a natural number between 1 and 7, or of being ; an alist mapping 32-bit integers to 32-bit integers. The whole idea of the ; tau system is that by representing primitive tau recognizers as objects and ; allowing the representation of the conjunction and negation of these objects ; we can precompute the implications of some thing having a given property ; independently of whatever thing we might be talking about. ; This leads to another basic idea in the tau system: these precomputed ; relationships between tau properties are stored in a database, so that upon ; learning that an object has one of the properties we can rapidly determine ; all of the tau recognizers it satisfies. ; A third basic idea is that the tau database will not be deduced ; automatically but will be derived from rules proved by the user. The tau ; system mines rules as they are proved and builds its database. Several ; forms of theorems are of interest. ; Boolean: ; (booleanp (f v)) ; Eval: ; (p 'const) ; Simple: ; (implies (p v) (q v)) ; Conjunctive: ; (implies (and (p1 v) (p2 v) ...) (q v)) ; Signature [form 1]: ; (implies (and (p1 x1) (p2 x2) ...) ; (q (f x1 x2 ...))) ; Signature [form 2]: ; (implies (and (p1 x1) (p2 x2) ...) ; (q (mv-nth 'n (f x1 x2 ...)))) ; Big Switch: ; (equal (fn . formals) body), where body is a big switch on some formal. ; MV-NTH Synonym: ; (equal (fn x y) (mv-nth x y)) ; where the monadic predicate symbols above, p, q, p1, p2, etc., may be are our ; signed tau recognizers. ; On Tau Recognizers -- Part 1 ; So what are the tau recognizers? They are the following functions: ; (i) named, monadic Boolean, non-constant functions, both built-in and ; defined, such as NATP, INTEGERP, CONSP, ALISTP, etc., including ; user-defined ones, and their negations, e.g., (lambda (x) (NOT (NATP ; x))); see what we mean by ``non-constant, non-equality'' below. ; (ii) all functions (lambda (x) (EQUAL x 'evg)) and their negations, ; where we also accept EQ, EQL, and = in place of EQUAL, and ; (iii) all functions (lambda (x) (< x ')) or (lambda (x) (< ' x)), ; for rational , and their negations, where we also accept > in ; place of <. Note that <= and >= are ACL2 macros, not function symbols, ; so there is no point in accepting them. When mining ACL2 terms for tau ; recognizers (i.e., tau-like-term) and encountering (> x y), we act as ; though we'd just seen (< y x). ; If sign is T or NIL and r is a tau recognizer, then by sign/r we mean r if ; sign is T and the negation of r of sign is NIL. Think of the positive sign ; being T and the negative sign being NIL. Thus, (sign/r e) is (NOT (EQUAL e ; 'ABC)) if sign is NIL and r is (lambda (x) (EQUAL x 'ABC)). ; The Non-Constant Non-Equality Premise: No function symbol is defined or ; constrained to be constantly T or constantly NIL, and no function symbol is ; defined or constrained to be equivalent to (EQUAL v 'evg) or (NOT (EQUAL v ; 'evg)). Thus, we don't expect to handle functions like: ; (defun everywhere-true (x) (declare (ignore x)) T) ; or ; (defun is-loadp (x) (eq x 'LOAD)) ; A user wishing to test equality-with-constant should use EQUAL (or one of its ; variants) and the constant, not hide it inside a monadic Boolean function. ; Our code is sound in the presence of such pathological defuns, but it is not ; ``as complete'' as it would be if they were avoided. These restrictions ; could be dropped by extending the form of Synonym theorems so that the tau ; system understood these symbols. But equalities like (eq x 'LOAD) are so ; useful it is hard to make sure the rest of the theorem prover is aware of ; them except by leaving them out in the open. ; One implication of the Non-Constant Non-Equality Premise is that we will ; never see (or in any case, take no note of) theorems of the form (fn x) --> ; (equal x 'evg), because such a theorem implies that fn violates the premise. ; (encapsulate ((fn (x) t)) ; (local (defun fn (x) (equal x '23))) ; (defthm fn-constraint (and (booleanp (fn x)) ; (implies (fn x) (equal x '23))) ; :rule-classes nil)) ; (thm (or (equal (fn x) nil) ; (equal (fn x) (equal x '23))) ; :hints (("Goal" :use fn-constraint))) ; We discuss how tau recognizers are represented in our code in ``On Tau ; Recognizers -- Part 2'' below. ; On the Tau Database and General Design ; It is possible to turn the tau reasoning engine on or off, by toggling the ; enabled status of the rune for TAU-SYSTEM. ; Our design allows for theorems to enter the tau database in either of two ; ways, explicitly (because they have rule-classe :tau-system) or implicitly ; (because they are of the right syntactic shape). Non-:tau-system theorems ; are swept up into the database implicitly only when the tau system is in ; ``automatic mode.'' ; The two modes just mentioned -- whether tau reasoning is used in proofs ; and whether the tau database is extended implicitly -- are independent. ; The tau system does not track the rules it uses. This design decision was ; motivated by the desire to make tau reasoning fast. The tau database does ; not record which tau facts come from which theorems or runes. It is ; impossible to prevent tau from using a fact in the database unless you ; disable TAU-SYSTEM altogether. However, there is a facility for regenerating ; the tau database with respect to a given enabled structure. ; Thus, the tau database may be built incrementally as each event is processed ; or may be built in one fell swoop. In the early implementations of the tau ; system the various modes and features were scattered all over our source ; code. For example, we found ourselves possibly extending the tau database: ; (a) when :tau-system rules are added by add-x-rule ; (b) when any non-:tau-system rule is added (e.g., by add-rewrite-rule) ; (c) when a defun or constraint added a Boolean type-prescription rule to ; a monadic function ; (d) when an explicit regenerate event is triggered. ; This became too complicated. We have therefore moved all the tau database ; extension code into this file and invoke it only two places: in install-event ; and in the regeneration event (which mimics the sequential invocation on ; successive events in the world). ; One odd aspect of this implementation is that add-x-rule, which ``ought'' to ; know how to add every kind of rule class, does NOT add :tau-system rules. ; Instead, it is just a quiet no-op on :tau-system rules and those rules are ; handled by the centralized tau facility developed here. To do otherwise ; would mean that :tau-system rules must be added both by add-x-rule and by ; regeneration of the tau database. ; Another oddity is that it is difficult to check that certain ``syntactic'' ; criteria are met because the criteria are really dependent on previously ; proved and exported theorems. For example, if p and q are monadic Boolean ; functions, ; (defthm p-implies-q (implies (p x) (q x)) :rule-classes :tau-system) ; is a legal tau rule. But now imagine that p and p-implies-q are introduced ; inside an encapsulate constraining p. Then if the encapsulation fails to ; export the fact that p is Boolean, the second pass will fail because ; p-implies-q is not a tau rule. The ``syntactic'' check that it is a legal ; rule, made in the first pass, is actually not quite enough to ensure ; legality. We cause a hard error in the second pass when this occurs. This ; is not unprecedented; see the hard error caused by ; add-type-prescription-rule. But there is a similar but more common and ; insidious problem arising from regeneration. Forget about encapsulation; ; let p, q, and p-implies-q just be top-level events. But imagine we're ; regenerating the tau database under an ens in which the Boolean nature of p ; is disabled. Then p-implies-q is no longer a legal :tau-system rule even ; though it was legal when checked. Had p-implies-q just been a :rewrite rule, ; say, and swept into the tau database by auto mode, it would make sense to ; quietly ignore this situation (and not add a tau rule). But if p-implies-q ; is an explicit :tau-system rule it seems glib to ignore it in the ; reconstruction of the database but harsh to cause a hard error because of ; the disabled status of a :type-prescription rule. So instead, we collect a ; list of all the explicit :tau-system rules excluded by the user's chosen ens ; and report it at the end of the regeneration process. We call these ``lost ; tau rules.'' ; On Tau Recognizers -- Part 2 ; Recall that a tau is a set of tau recognizers representing their conjunction ; and denotes a monadic Boolean function. We describe how we represent tau ; below. But a basic idea is that we'll group all the positive recognizer ; symbols into one list representing their conjunction and all the (positive ; versions of the) negative ones in another list representing their ; disjunction. Thus, we really only represent positive tau recognizers and use ; the sign convention (or the location of the recognizer in some data ; structure) to negate them. ; To make such lists of function symbols easier to search for a given symbol ; we order them and assign each monadic Boolean function symbol a unique ; natural number called its tau index. The index of each such function will be ; stored on its property list under the property tau-pair, in a pair containing ; the index and function symbol. Thus, one might find under 'symbolp 'tau-pair ; the pair (7 . SYMBOLP), or whatever the index for symbolp is. ; Note: When constants in this code refer to the indices of specific functions, ; we have a check in check-built-in-constants that insures that the index is ; still correct, since they may shift as Boolean functions are added. ; As noted, we use the notation sign/r to denote a signed tau recognizer, where ; sign = T means the recognizer is positive and sign = NIL means it is ; negative. Often in our code r is actually a tau-pair. That is, we might ; write sign/r in a comment but in the code r is (7 . SYMBOLP). If the ; associated sign were NIL, this would denote (lambda (x) (not (symbolp x))). ; This raises the question of how we represent the unnamed tau recognizers as ; ACL2 objects? We certainly do not want to represent the function (lambda (x) ; (equal x 'abc)) as the list object '(lambda (x) (equal x 'abc))! We need a ; succinct and cheap representation for ``equalities with constants'' and ; ``arithmetic inequalities with rationals'' that correspond to the role played ; by tau pairs for those recognizers with symbolic names like SYMBOLP. ; In general our code uses two variables, e.g., sign and r, in tandem to ; represent a signed recognizer sign/r. We only represent positive ; recognizers, r, and use the sign part of the sign/r notation to handle the ; negatives. ; The equality tau recognizers, (lambda (x) (equal x ')), are represented ; by a singleton list containing the , i.e., (). This is cheap ; because it is just the cdr of the (QUOTE ) inside the equality term. It ; is convenient because to apply a named tau recognizer to an evg (to see ; whether it is true or false) we apply the named function to a singleton list ; containing the evg in question, so this notation facilitates that application ; without consing. We can tell whether an r in the sign/r notation is a ; tau-pair (representing a named recognizer) or a singleton (representing ; an equality with that ) by testing (cdr r). If (cdr r) is non-nil, it ; is the function symbol naming the recognizer, otherwise r is a singleton evg. ; The arithmetic inequality tau recognizers, (lambda (x) (< x 'k)) and (lambda ; (x) (< 'k x)), are represented by (k . :lessp-x-k) and (k . :lessp-k-x) ; respectively. Note that this might look like a tau pair but :lessp-x-k and ; :lessp-k-x are keywords and hence not function symbols. Note also that ; putting the keyword in the cdr of the cons rather than in the car was a ; deliberate decision, even though it is sort of backward if the keyword is ; thought of the ``function symbol'' and the k is a ``parameter.'' By putting ; the keyword in the cdr we can tell what sort of recognizer r represents by ; using eq tests on the cdr only. ; Thus, the following test can determine what kind of tau recognizer r ; represents. (Technically, the terms shown in the comments below are ; the BODIES of the (lambda (x) ...) expression r represents.) ; (cond ((eq (cdr r) nil) ; (let ((evg (car r))) ; ... ; r represents (EQUAL x 'evg), furthermore r ; ; is a singleton list containing evg suitable ; ; for using ev-fncall-w to apply some monadic ; ; function. ; )) ; ((eq (cdr r) :lessp-x-k) ; (let ((k (car r))) ; ... ; r represents (< x 'k), where k is a RATIONALP. ; )) ; ((eq (cdr r) :lessp-k-x) ; (let ((k (car r))) ; ... ; r represents (< 'k x), where k is a RATIONALP. ; )) ; (t (let ((p (cdr r)) ; (i (car r))) ; ... ; r represents (p x), furthermore, i is the ; ; tau index of monadic function symbol p. ; ))) ; Note that tests in CCL show that is sped up (by perhaps 10%) by binding the ; variable symbol discriminator to (cdr r) and using discriminator everywhere ; (cdr r) appears above. ; On Tau Intervals and < versus <= ; The tau data structure is defined below. But it contains another data ; structure called a tau-interval, which represents a function that bounds its ; argument to an interval between two rationals. Its argument need not be ; a rational, indeed, its argument need not even be an acl2-numberp. ; For example, every non-numeric ACL2 object x satisfies (-2 < x < 2)! (defrec tau-interval (domain (lo-rel . lo) . (hi-rel . hi)) t) ; where ; * domain is INTEGERP, RATIONALP, ACL2-NUMBERP, or NIL and indicates the ; domain of the interval. That is, it is either the integer line, the ; rational line, the acl2-numberp line, or the entire ACL2 universe ``line''. ; (The ACL2 universe ``line'' is not quite a line: the acl2-numbers are laid ; out linearly as one would expect, and all the non-acl2-numbers are in an ; equivalence class with 0.) If the domain is INTEGERP, we impose additional ; invariants on the other components as described below. ; * lo-rel and hi-rel are T or NIL and indicate whether the corresponding ; relation is < or <= (T is strict; nil is weak inequality); if the domain ; is INTEGERP, we always use weak inequalities; and ; * lo and hi are either NIL or rationals and represent the lower and upper ; bounds, with NIL being the corresponding infinity. If the domain is INTEGERP, ; we squeeze the bounds to be integers. Thus, while it is technically ; possible to create a tau-interval with ; (make tau-interval ; :domain 'INTEGERP ; :lo 1/2 ; :lo-rel t ; :hi-rel t ; :hi 5/2) ; we prefer to round the bounds up and down (respectively) and weaken the ; relations, converting 1/2 < x < 5/2 to 1 <= x <= 2. This adjustment ; is done with the function squeeze-k. ; Technically there is no reason to exclude the bounds from being ; complex-rationals. <= is a non-strict total order on acl2-numberp. For ; example, it is a theorem that if (<= #c(1 2) x) and (<= x #c(1 2)) then x = ; #c(1 2). So we could allow the bounds to be complex-rationals and continue ; more or less as we do here. However, (a) it gets a little more complicated ; because in squeezing intervals around integers we'd have to take the ; realpart of the bounds, e.g., to raise a strict lower bound on an integer ; we'd use (+ (floor (REALPART lo) 1) 1) instead of (+ (floor lo 1) 1) if lo ; can be complex, and (b) complex bounds probably never occur! So the little ; added complexity probably wouldn't buy us anything. ; Terminology: A ``bound'' is determined by a pair, namely a ``relation'' ; (coded as a Boolean flag) and an ``extent'' (coded as either a NIL -- ; representing an infinity -- or a rational). Which infinity nil represents ; depends on whether we're talking about an upper or lower bound. ; We sometimes use the following notation: ; (defun x and (DIV4P y) ; - generate the greatest y such that y < x and (DIV4P y) ; The infrastructure to support this is tedious. One would have to prove rules ; characterizing the correctness of such generators and then use such rules to ; build a data structure that would be used when new pos-pairs are added or ; when bounds are changed. Perhaps somebody would like a project? End of Aside. ; We use tau-intervals to say everything they can. This is a strange remark that ; bears explanation. For example, a tau might say that its subject is a natural ; number (in addition to having many other properties). Should the interval in that ; tau redundantly be the tau-interval equivalent to NATP, ; (lambda (x) (and (integerp x) (<= 0 x))) ; or should we just store NIL as the tau-interval for this tau on the grounds ; that the NATP property is stored elsewhere? In short, should the ; tau-interval in a tau be allowed to be redundant? Our answer is yes: we use ; tau-intervals to say everything they can. The idea is the linear arithmetic ; facts about the subject will be found in the tau-interval -- even if they are ; pretty unrestrictive like INTEGERP or RATIONALP without any bounds. We will ; arrange to adjust the tau-interval (shrinking it) whenever we learn anything ; that allows that. ; Perhaps the strangest implication of this decision is that a tau-interval can ; say that its subject is equal to a particular rational constant, by weakly ; bounding the subject above and below by that rational constant. In the case ; that the constant is 0, one must add that the subject is rationalp. This all ; follows from a theorem shown among the ``helpful reminders about ACL2 ; arithmetic'' below. ; A further implication is that if we are using a tau to represent a ; conjunctive rule and we wish to represent the conjunct (equal e 5) we will ; get the same thing as the tau for (and (equal e 5) (<= 5 e) (<= e 5)). ; Finally, some intervals are contradictory and are never built; we try to ; signal contradiction instead of creating empty intervals like: ; (lambda (x) (and (rationalp x) (< 10 x) (< x 5))). ; However, in the event that we need a canonical empty interval, here is the ; one we use. (defconst *tau-empty-interval* (make tau-interval :domain nil :lo-rel t :lo 0 :hi-rel t :hi 0)) ; Because we do not guarantee always to construct the canonical empty interval, ; we test for it with this more general function. This function recognizes ; *tau-empty-interval* and also has the property that if int is an interval ; that passes this test, then no x is in it. (defun tau-empty-intervalp (int) (and int (access tau-interval int :lo) (access tau-interval int :hi) (if (or (access tau-interval int :lo-rel) (access tau-interval int :hi-rel)) (<= (access tau-interval int :hi) (access tau-interval int :lo)) (< (access tau-interval int :hi) (access tau-interval int :lo))))) ; Here are some helpful reminders about ACL2 arithmetic... Some of the more ; ``obvious'' lemmas are interesting because of what they DON'T say. For ; example, some obvious properties of < and <= are missing hypotheses that ; would restrict them to numeric inputs. ; (er-progn ; ; ; All of the following could be proved before tau was involved. ; (in-theory (disable (tau-system))) ; (include-book "arithmetic-5/top" :dir :system) ; ; ; Integers are rationals and rationals are (acl2-)numbers. ; (thm (and (implies (integerp x) ; (rationalp x)) ; (implies (rationalp x) ; (acl2-numberp x)))) ; ; ; Numbers are partitioned into rationals and complex-rationals. ; (thm (and (iff (acl2-numberp x) ; (or (rationalp x) ; (complex-rationalp x))) ; (implies (rationalp x) (not (complex-rationalp x))))) ; ; ; < is transitive, whether applied to numbers or not. ; (thm (implies (and (< x y) (< y z)) (< x z))) ; ; ; < is anti-symmetric, whether applied to numbers or not. ; (thm (implies (< x y) (not (< y x)))) ; ; ; Trichotomy holds, but you must know the arguments are both numbers. ; (thm ; (implies (and (acl2-numberp x) ; (acl2-numberp y)) ; (or (< x y) ; (< y x) ; (equal x y)))) ; ; ; If something is strictly above or below 0, it must be a number. ; (thm (implies (or (< x 0) (< 0 x)) ; (acl2-numberp x))) ; ; ; Strict lower bounds on integers can be raised and weakened: ; (thm ; (implies (and (integerp x) ; (rationalp bound)) ; (iff (< bound x) ; (<= (+ (floor bound 1) 1) x)))) ; ; ; Weak lower bounds on integers can be raised: ; (thm ; (implies (and (integerp x) ; (rationalp bound)) ; (iff (<= bound x) ; (<= (ceiling bound 1) x)))) ; ; ; Strict upper bounds on integers can be lowered and weakened: ; (thm ; (implies (and (integerp x) ; (rationalp bound)) ; (iff (< x bound) ; (<= x (- (ceiling bound 1) 1))))) ; ; ; Weak upper bounds on integers can be lowered: ; (thm ; (implies (and (integerp x) ; (rationalp bound)) ; (iff (<= x bound) ; (<= x (floor bound 1))))) ; ; ; The other inequalities are just signed less thans, whether applied to numbers ; ; or not: ; ; (thm (and (iff (<= x y) (not (< y x))) ; (iff (> x y) (< y x)) ; (iff (>= x y) (not (< x y))) ; (iff (>= x y) (<= y x)))) ; ; ; Note that the theorem above shows that we can do everything with signed < or ; ; with < and <=. These theorems are interesting only because they don't have ; ; hypotheses about x and y being numbers. ; ; ; An interval bounded above and below by the same rational contains exactly one ; ; element, provided either the interval is restricted to numbers or one of the ; ; bounds is non-0. ; ; (thm (implies ; (and (rationalp lo) ; (rationalp hi) ; (<= lo x) ; (<= x hi) ; (equal lo hi) ; (or (acl2-numberp x) ; (not (equal hi 0)))) ; (equal x hi))) ; ; ; Stated slightly differently, the recognizer (lambda (x) (equal x 'b)) for a ; ; rational constant b, is equivalent to the interval recognizer: ; ; (lambda (x) (and (rationalp x) (<= b x) (<= x b))): ; ; (thm (implies (rationalp b) ; (iff (equal x b) ; (and (rationalp x) ; (<= b x) ; (<= x b))))) ; ; ; Or, in the even more special case that b is an integer: ; ; (thm (implies (integerp b) ; (iff (equal x b) ; (and (integerp x) ; (<= b x) ; (<= x b))))) ; ; ; (By the way, one might ask how it is that a complex cannot lie ``between'' ; ; the rational b and the rational b. The reason is that if u+iv <= b and b <= ; ; u+iv then one can show that u=b and v=0. This happens because (u+iv <= b) is ; ; equivalent to (u r is represented by the set C = {-p -q -r}. To use C as ; a conjunctive rule we ask whether some tau, just derived, contains all but ; one of the elements of C and if so we can add the negation of the omitted ; element of C. ; This brings us to Signature rules of both forms. Signature rules tells us ; facts about the value of a function given facts about its inputs. Signature ; rules play the primary role in determining the tau of a term (or of its ; MV-NTH components) given tau assumptions about subterms. (defrec signature-rule (input-tau-list (vars . dependent-hyps) output-sign output-recog) t) ; Signature rules are hung on some n-ary function symbol. The formula from ; which such a rule is derived is: ; (implies (and (tau_1 v1) ... (tau_n vn) ; (dhyp_1 v1 ... vn) ... (dhyp_k v1 ... vn)) ; (tau (fn v1 ... vn))) ; modulo several obvious generalizations of the form and arbitrary ordering and ; mixing of the hypotheses. The representation of such a formula as a ; signature-rule is: ; :inputs-tau-list - (tau_1 ... tau_n) -- required tau of corresponding vars ; :vars - (v1 ... vn) -- the vars used in the conclusion ; :dependent-hyps - list of terms in vars ((dhyp_1 v1 ... vn) ...) ; :output-sign - T (positive) or NIL (negative) ; :output-recog - tau, i.e., tau-pair or singleton evg list ; Foreshadowing: We use this same record for both forms of signature rules. ; Under the SIGNATURE-RULES-FORM-1 property of a function symbol fn we will ; find a list of these records, interpreted in the obvious way about calls of ; fn: if the actuals satisfy their respective tau in :inputs-tau-lst and the ; :dependent-hyps are relieved, then the output of fn satisfies the output-sign ; and output-recog. Under the SIGNATURE-RULES-FORM-2 property of a fn with ; output arity m we may find a list of length m, each element being a list of ; these same records. The interpretation of the records in slot i is that the ; ith value returned by fn has the indicated tau. We will allow both ; properties to exist for a function symbol. That is, one might prove that (fn ; x y) is a true-list of length 3 and might also characterize the tau of ; (mv-nth '0 (fn x y)), (mv-nth '1 (fn x y)), and (mv-nth '2 (fn x y)). When ; trying to determine the tau of an (mv-nth 'i (fn a b)) expression we use only ; the rules in the ith slot of the form 2 rules for f. When trying to ; determine the tau of (fn a b) we use only the form 1 rules for fn. ; Finally, when we are processing terms, either to find their tau or to assume ; them true, we may do a small amount of rewriting. In particular, we expand ; so-called big switch function calls. Roughly speaking, a ``big switch'' ; function is a nonrec function that is initially controlled entirely by the ; tau of a single formal variable. For example, (little-switch key x) defined ; to be (symbolp x) if key is 'SYMBOLP, (natp x) if key is 'NATP, and (consp x) ; otherwise, is a big switch function with the variable key as its ``switch ; variable.'' ; If fn is a big switch function, then we can partially evaluate a call of that ; function efficiently under some tau assumptions. In particular, we do not ; need to instantiate the entire body of the definition. If we know enough ; about the switch actual to navigate past all those tau tests on the switch ; formal, we can reach a ``leaf'' and instantiate just it. If we cannot ; navigate to a leaf, we do not expand the big switch function at all. ; If a theorem, (EQUAL (fn . formals) body), defines a big switch function we ; make a big switch rule of the form: (defrec big-switch-rule (formals switch-var switch-var-pos body) nil) ; where switch-var is the switch var and switch-var-pos is its position in ; formals. That rule will be stored under fn on the property 'big-switch. (defun tau-simple-implicants (sign pred wrld) (getprop pred (if sign 'pos-implicants 'neg-implicants) nil 'current-acl2-world wrld)) ; Obviously Contradictory Recognizer Sets ; We define make-tau which takes the four components of a tau and returns the ; corresponding instance of tau, unless that tau is obviously contradictory, in ; which case we return *tau-contradiction*. The four arguments are assumed ; individually to be well-formed, e.g., duplicate-free and ordered ; appropriately. (defun tau-pair-member1 (index pairs) (cond ((endp pairs) nil) ((>= (car (car pairs)) index) (or (equal (car (car pairs)) index) (tau-pair-member1 index (cdr pairs)))) (t nil))) (defun tau-pair-member (pair pairs) ; This function determines whether (car pair) is a member of (strip-cars ; pairs), but assumes that pairs is ordered descending on the cars, as enforced ; by merge-sort-car->. Note that it is tempting to think this function ; determines whether pair is a member of pairs, but in fact the cdrs of the ; pairs are totally ignored. Thus, (tau-pair-member '(1 . A) '((1 . B))) is ; true! (tau-pair-member1 (car pair) pairs)) ; On the Use of ENS by Function Evaluation in the Tau System ; The following function is used throughout the tau system to determine whether ; a recognizer will accept (recognize) a given constant or not. In an earlier ; incarnation of the function, it was not sensitive to the enabled status of the ; executable counterpart of the tau recognizer fn. ; One argument for making the tau system respect the current enabled structure ; is that some authors disable :executable-counterparts in their proofs and if ; tau does not respect the disabling, it is behaving contrary to the author's ; wishes. This happens in the file Proof-Of-Equiv-From-M-Corr.lisp of ; community book books/workshops/1999/embedded/Proof-Of-Contribution/ by ; P.G. Bertoli and P. Traverso (which is a chapter in the ACL2 Case Studies ; book), where the authors execute (in-theory (current-theory 'ground-zero)) ; near the top of the book, thus disabling almost everything. The hypothesis ; (rel-prime-moduli '(11 13 15 17 19)) is used frequently and proofs break if ; it is evaluated. This particular book is not a very good argument for ; respecting ens, since the script could probably have been developed without ; this Draconian disabling. A better reason to make tau respect ens is that ; some executable counterparts could be so inefficient to compute that the user ; would prefer that they never be run. But tau would run them and lose. In ; fact, in that earlier incarnation just mentioned, tau did run them and did ; lose. See the example below. ; On the other side, however, is the tau system convention that if a recognizer ; in the pos-pairs of a tau accepts a given evg, then that evg is not included ; in the neg-evgs. For example, suppose we want to construct the tau that ; denotes (lambda (v) (and (not (equal v 23)) (foop v))). But suppose that ; (foop 23) is false. Then there is no point in including the ``(not (equal v ; 23))'' since is implied by the (foop v). Indeed, we'd represent this tau as ; the set {foop}. Suppose that tau were stored as the positive implicants of ; some recognizer in the database. Now imagine the user disables the ; executable-counterpart of foop. Are we to recompute the normal form of all ; the stored taus? We just can't bear the thought! ; So we have adopted a slightly inelegant approach: Tau is sensitive to the ; enabled status of the executable counterparts of the tau recognizers it needs ; to evaluate but suffers unnecessary incompleteness as a result. For example, ; one might build a tau in the database that contains foop and that (at the ; time it was built) also records that 23 is not in the tau. But then one ; disables foop and thereafter it is unknown whether 23 is in the tau or not. ; The second occurrence of a local LEMMA4 in the community book ; unicode/utf8-decode.lisp provides an interesting test of the available ; strategies for dealing with expensive calls of ev-fncall-w-tau-recog. ; If the tau-system is disabled, that LEMMA4 takes only about 0.06 seconds to ; prove (on a 2.6 GHz Intel Core i7 with 16 GB 1600 MHz DDR3 Macbook Pro). But ; if tau-system is enabled and no special steps are taken, it takes 5.91 ; seconds. The problem is that the tau recognizer TEST-UTF8-COMBINE4 is ; evaluated several times by the tau system as it computes the tau for various ; terms, and TEST-UTF8-COMBINE4 is very expensive to compute. Tau evaluates ; this user-defined predicate a total of 11 times in all, on 0, NIL, and T. ; This raises two problems: how do you discover the source of this slowdown, ; and what do you do about it after discovering it? It was discovered by ; noticing the time difference for the utf8-decode book in two successive ; regressions and then using the Allegro profiler to identify the *1* function ; for TEST-UTF8-COMBINE4 as the culprit. Once that was confirmed, experiments ; were done to determine all the calls of ev-fncall-w-tau-recognizer in the ; entire book (just to see if it got excessive). It turns out it is called a ; total of 102,004 times to evaluate 96 different tau recognizers on these ; constants: (-1 244 240 237 224 1 4 3 2 0 T NIL). A total of 480 distinct (fn ; 'evg) are involved and about 5.8 seconds is consumed in these. ; But by far the most expensive calls are the 11 calls of TEST-UTF8-COMBINE4: ; (evg val n secs) ; (0 T 7 0.180351) ; (T T 2 1.152991) ; (NIL T 2 1.163224) ; E.g., TEST-UTF8-COMBINE4 is called 7 times on 0 and the first call took ; 0.180351 seconds. Note how expensive T and NIL are. ; After the defun of ev-fncall-w-tau-recog, below, we include a comment that ; shows the raw Lisp code used to accumulate such information. This is ; preserved here for future reference and may be advertised to some users ; suffering slowdowns. NOTE: After these notes were written, we implemented ; the time-tracker mechanism as a way of diagnosing such slowdowns. ; There are four mechanisms in tau for dealing with this. Mechanism A is to do ; nothing and suffer the slowdown. Mechanism B is to disable the tau-system! ; Mechanism C is to prove the ``mechanism-c-lemma,'' characterizing the value ; of the expensive recognizer on the constants in question, provided one can ; determine what those constants are. Mechanism D is to disable the expensive ; recognizer and perhaps lose some completeness. ; Mechanism LEMMA4 Proof Time Reported in Summary ; A [do nothing] 5.91 seconds ; B [disable tau] 0.07 seconds ; C [build in expensive calls] 0.07 seconds ; D [disable expensive calls] 0.07 seconds ; Here is the lemma used in Mechanism C: ; (local (defthm mechanism-c-lemma ; (and (test-utf8-combine4 0) ; (test-utf8-combine4 nil) ; (test-utf8-combine4 t)) ; :rule-classes :tau-system)) ; Time: 2.50 seconds (prove: 2.50, print: 0.00, other: 0.00) ; This lemma stores the value of the recognizer on its property list and it is ; looked up instead of computed. ; Note that while the lemma allows the fast proof of LEMMA4, the lemma itself ; takes 2.5 seconds to prove. (defun ev-fncall-w-tau-recog (fn evg-lst ens wrld) ; Fn is a monadic Boolean function symbol known to tau and evg-lst is a ; singleton list containing an evg. We apply fn to evg-lst and return the ; value, T or NIL, or we return :UNEVALABLE. We check the unevalable-but-known ; property of fn first. By checking it first we allow the user to prove a ; :tau-system rule that short-circuits long but successful calculations, if the ; user is clever enough to realize that slowdowns are due to repeated ; evaluations of such functions. ; Warning: If this function is changed to call itself recursively, reconsider ; the setf expression in the comment after this defun. (cond ((enabled-xfnp fn ens wrld) (let* ((ubk (getprop fn 'unevalable-but-known nil 'current-acl2-world wrld)) (temp (if ubk (assoc-equal (car evg-lst) ubk) nil))) (cond (temp (cdr temp)) ; previously stored T or NIL (t (mv-let (erp val) (ev-fncall-w fn evg-lst wrld nil nil t t nil) ; The arguments to ev-fncall-w above, after wrld, are user-stobj-alist (= nil), ; safe-mode (= nil), gc-off (= t), hard-error-returns-nilp (= t), and aok (= ; nil). These are the same arguments used in the call of ev-fncall-w in ; sublis-var!. (cond (erp :UNEVALABLE) (val t) (t nil))))))) (t :UNEVALABLE))) ; Some Implementor-Level Performance Investigation Tools ; ; ; Ev-fncall-w-tau-recog, above, is used to evaluate tau predicates on ; ; constants. Of course, if the tau predicate in question is complicated this ; ; evaluation can be slow. Tau Eval rules can be used to store pre-computed ; ; results and speed up subsequent evaluations. But what predicates and what ; ; constants need to be pre-computed and stored? ; ; ; In this comment we drop some raw Lisp code used to collect every call of ; ; ev-fncall-w-tau-recog. Data is stored in a simple alist and so if the number ; ; of distinct recognizers grows large, this data collection will slow down the ; ; proof. After collecting the data, one can learn what recognizers were ; ; called, what constants they were called on, how many times each recognizer ; ; was called on each constant, and how long the first (and only computed) call ; ; of each recognizer took. ; ; ; When you are ready to collect the data for a proof or proofs, do this to ; ; exit the ACL2 loop and enter raw Lisp. ; ; (value :q) ; ; ; Next, save the current definition of ev-fncall-w-tau-recog in ; ; ev-fncall-w-tau-recog1. ; ; ; Warning: This setf hack works only if ev-fncall-w-tau-recog is not ; ; recursively defined and our sources don't already use the symbol ; ; ev-fncall-w-tau-recog1. ; ; (setf (symbol-function 'ev-fncall-w-tau-recog1) ; (symbol-function 'ev-fncall-w-tau-recog)) ; ; ; Now declare the collection site. The setq after this defvar is useful if ; ; you run multiple tests and want to clear the site. ; ; (defvar ev-fncall-w-tau-recog-alist nil) ; (setq ev-fncall-w-tau-recog-alist nil) ; ; ; Now redefine the raw Lisp version of ev-fncall-w-tau-recog to ; ; collect the data: ; ; (defun ev-fncall-w-tau-recog (fn evg-lst ens wrld) ; (let ((fn-alist (assoc-eq fn ev-fncall-w-tau-recog-alist))) ; (cond ; (fn-alist ; (let ((evg-val-cnt-time (assoc-equal (car evg-lst) (cdr fn-alist)))) ; (cond ; (evg-val-cnt-time ; (setf (caddr evg-val-cnt-time) (+ 1 (caddr evg-val-cnt-time))) ; (cadr evg-val-cnt-time)) ; (t ; (let* ((start-time (get-internal-run-time)) ; (val (ev-fncall-w-tau-recog1 fn evg-lst ens wrld)) ; (total-time (- (get-internal-run-time) start-time))) ; (setf (cdr fn-alist) ; (cons (list (car evg-lst) val 1 total-time) (cdr fn-alist))) ; val))))) ; (t (let* ((start-time (get-internal-run-time)) ; (val (ev-fncall-w-tau-recog1 fn evg-lst ens wrld)) ; (total-time (- (get-internal-run-time) start-time))) ; (setq ev-fncall-w-tau-recog-alist ; (cons (cons fn (list (list (car evg-lst) val 1 total-time))) ; ev-fncall-w-tau-recog-alist)) ; val))))) ; ; ; Return to the loop and run your proof(s). ; (lp) ; ; ; ; ; ; Exit ACL2 and re-enter raw Lisp: ; (value :q) ; ; ; The collected data is stored in ev-fncall-w-tau-recog-alist. But this ; ; list can be big so we tend to investigate its size before just printing it. ; ; ; For the record, every element of ev-fncall-w-tau-recog-alist is a ; ; fn-dot-alist, e.g., (fn . alist), and alist is a list of 4-tuples, each of ; ; the form (const val count time), where ; ; ; (nth 0 fourtuple) -- evg that fn is applied to ; ; (nth 1 fourtuple) -- val of fn on evg ; ; (nth 2 fourtuple) -- number of times fn applied to evg ; ; (nth 3 fourtuple) -- time it took to eval (fn evg) once ; ; ; Time, above, is measured in CLTL internal time units, where the Common Lisp ; ; global internal-time-units-per-second says how many of these units are in ; ; one second. ; ; ; Some typical expressions for investigating the evaluation of recognizers on ; ; constants: ; ; ; Number of distinct tau recogs evaluated: ; (len ev-fncall-w-tau-recog-alist) ; ; ; Total amount of time, in seconds, devoted to ev-fncall-w-tau-recog ; ; if every call were computed from the definition of the recognizers. ; (list (float ; (/ (loop for fn-dot-alist in ev-fncall-w-tau-recog-alist ; sum ; (loop for fourtuple in (cdr fn-dot-alist) ; sum (* (nth 2 fourtuple) ; (nth 3 fourtuple)))) ; internal-time-units-per-second)) ; 'seconds) ; ; ; Number of distinct constants concerned: ; (len (loop for fn-dot-alist in ev-fncall-w-tau-recog-alist ; with ans ; do (setq ans (union-equal (loop for fourtuple in (cdr fn-dot-alist) ; collect (nth 0 fourtuple)) ; ans)) ; finally (return ans))) ; ; To see the constants themselves, just drop the len above. ; ; ; Number of calls of ev-fncall-tau-recog ; (loop for fn-dot-alist in ev-fncall-w-tau-recog-alist ; sum (loop for fourtuple in (cdr fn-dot-alist) sum (nth 3 fourtuple))) ; ; ; Display of all the data, sorted to show most expensive calls first: ; ; Time is measured in internal-time-units and internal-time-units-per-second ; ; says how big those units are. ; (loop for x in ; (cons '(time (fn evg) = val (count)) ; (merge-sort-car-> ; (loop for fn-dot-alist in ev-fncall-w-tau-recog-alist ; append ; (let ((fn (car fn-dot-alist))) ; (loop for fourtuple in (cdr fn-dot-alist) ; collect (list (nth 3 fourtuple) ; (list fn (nth 0 fourtuple)) ; '= (nth 1 fourtuple) ; (list (nth 2 fourtuple)))))))) ; do (print x)) ; ; ; Sort the list of recognizers by total time spent in each. ; (merge-sort-car-> ; (loop for fn-dot-alist in ev-fncall-w-tau-recog-alist ; collect ; (list (loop for fourtuple in (cdr fn-dot-alist) ; sum (* (nth 2 fourtuple) (nth 3 fourtuple))) ; (car fn-dot-alist)))) ; ; End of Some Implementor-Level Performance Investigation Tools (defun bad-val-or-unknowns (bad-val pairs evg-lst ens wrld) ; Bad-val is t or nil. If bad-val is t, understand it to mean that we are ; looking for a true or non-nil value. If bad-val is nil, we are looking for a ; false or nil value. Pairs is a list of tau-pairs, (i . pred). Evg-lst is a ; SINGLETON list containing one evg! We determine whether there is a pred in ; pairs that evals to bad-val on this evg. If so we return T. If all eval to ; something other than the bad-val, we return NIL. If some cannot be evaled ; and none eval to the bad-val, we return the list of unevalable tau pairs. (cond ((endp pairs) nil) (t (let ((val (ev-fncall-w-tau-recog (cdr (car pairs)) evg-lst ens wrld))) (cond ((eq val :UNEVALABLE) (let ((rest (bad-val-or-unknowns bad-val (cdr pairs) evg-lst ens wrld))) (cond ((eq rest t) t) (t (cons (car pairs) rest))))) (bad-val (if val t (bad-val-or-unknowns bad-val (cdr pairs) evg-lst ens wrld))) (t (if val (bad-val-or-unknowns bad-val (cdr pairs) evg-lst ens wrld) t))))))) (defun exists-bad-valp (bad-val pairs evg-lst ens wrld) ; Bad-val is t or nil. If bad-val is t, understand it to mean that we are ; looking for a true or non-nil value. If bad-val is nil, we are looking for a ; false or nil value. Pairs is a list of tau-pairs, (i . pred). Evg-lst is a ; SINGLETON list containing one evg! We determine whether there is a pred in ; pairs that evals to bad-val on this evg. If so, we return T. If not we ; return NIL. Note that preds in pairs that cannot be evaluated are skipped. ; They are not considered ``good'' or ``bad''. What we said is what we mean: ; we're looking for a pred that evals to bad-val! (cond ((endp pairs) nil) (t (let ((val (ev-fncall-w-tau-recog (cdr (car pairs)) evg-lst ens wrld))) (cond ((eq val :UNEVALABLE) (exists-bad-valp bad-val (cdr pairs) evg-lst ens wrld)) (bad-val (if val t (exists-bad-valp bad-val (cdr pairs) evg-lst ens wrld))) (t (if val (exists-bad-valp bad-val (cdr pairs) evg-lst ens wrld) t))))))) (defun all-eval-valp (good-val pairs evg-lst ens wrld) ; Good-val is t or nil. If good-val is t, understand it to mean that we are ; looking for a true or non-nil value. If good-val is nil, we are looking for ; a false or nil value. Pairs is a list of tau-pairs, (i . pred). Evg-lst is ; a SINGLETON list containing one evg! We determine whether every pred in ; pairs evals to good-val on this evg. If so, we return T. If not we return ; NIL. If a pred in pairs cannot be evaled, it means it did not eval to ; good-val! (cond ((endp pairs) t) (t (let ((val (ev-fncall-w-tau-recog (cdr (car pairs)) evg-lst ens wrld))) (cond ((eq val :UNEVALABLE) nil) (good-val (if val (all-eval-valp val (cdr pairs) evg-lst ens wrld) nil)) (t (if val nil (all-eval-valp val (cdr pairs) evg-lst ens wrld)))))))) (defun delete-bad-vals (neg-evgs pos-pairs neg-pairs ens wrld) ; We copy neg-evgs deleting those that falsify some element of pos-pairs or ; satisfy some element of neg-pairs. We return (mv changedp result). Changedp ; is t if the result is different from neg-evgs; else changedp is nil. (cond ((endp neg-evgs) (mv nil nil)) (t (mv-let (changedp result) (delete-bad-vals (cdr neg-evgs) pos-pairs neg-pairs ens wrld) (cond ((exists-bad-valp nil pos-pairs (car neg-evgs) ens wrld) (mv t result)) ((exists-bad-valp t neg-pairs (car neg-evgs) ens wrld) (mv t result)) ((null changedp) (mv nil neg-evgs)) (t (mv t (cons (car neg-evgs) result)))))))) (defun delete-bad-vals1 (neg-evgs sign tau-pair ens wrld) ; Despite the name, this function is not a subroutine of delete-bad-vals. ; Instead, it is a minor extension of it, equivalent to ; (delete-bad-vals neg-evgs ; (if sign (list tau-pair) nil) ; (if sign nil (list tau-pair)) ; ens wrld) ; but avoids the consing of (list tau-pair). ; We sweep neg-evgs (a list of singleton lists containing evgs) and delete ; those that are bad in the sense of being redundanct wrt to a particular ; signed recognizer. We return (mv changedp result). (cond ((endp neg-evgs) (mv nil nil)) (t (let ((val (ev-fncall-w-tau-recog (cdr tau-pair) (car neg-evgs) ens wrld))) (mv-let (changedp result) (delete-bad-vals1 (cdr neg-evgs) sign tau-pair ens wrld) (cond ((eq val :UNEVALABLE) (if changedp (mv t (cons (car neg-evgs) result)) (mv nil neg-evgs))) (val (if sign (if changedp (mv t (cons (car neg-evgs) result)) (mv nil neg-evgs)) (mv t result))) (t (if sign (mv t result) (if changedp (mv t (cons (car neg-evgs) result)) (mv nil neg-evgs)))))))))) (defun tau-pairs-subsetp (pairs1 pairs2) ; Provided both arguments are duplicate-free ordered descending tau-pair lists ; we determine whether (subsetp-equal pairs1 pairs2). (cond ((endp pairs1) t) ((endp pairs2) nil) ((>= (car (car pairs1)) (car (car pairs2))) (if (equal (car (car pairs1)) (car (car pairs2))) (tau-pairs-subsetp (cdr pairs1) (cdr pairs2)) nil)) (t (tau-pairs-subsetp pairs1 (cdr pairs2))))) (defun tau-pairs-near-subsetp (pairs1 pairs2 e) ; Pairs1 and pairs2 are duplicate-free ordered descending tau-pair lists. We ; determine whether there is exactly one elemnt of pairs1 not in pairs2. We ; return t if pairs1 is a subset of pairs2, nil if more than one element of ; pairs1 fails to be pairs2, and the missing tau-pair otherwise. ; Optimization Note: Pairs1 cannot be a near subset of pairs2 if the index of ; the second pair in pairs1 is greater than the index of the first pair in ; pairs2 the first time this function is entered (or more generally when e = ; nil). But this optimization (coded by renaming this function to be the ; workhorse, tau-pairs-near-subsetp1, and adding a top-level function to check ; for the short-cut) only saved about one half of one percent of the total ; number of calls of the workhorse over a test involving about 380 million ; calls. So we don't implement this optimation. (cond ((endp pairs1) (if e e t)) ((endp pairs2) (if e nil (if (endp (cdr pairs1)) (car pairs1) nil))) ((>= (car (car pairs1)) (car (car pairs2))) (if (equal (car (car pairs1)) (car (car pairs2))) (tau-pairs-near-subsetp (cdr pairs1) (cdr pairs2) e) (if e nil (tau-pairs-near-subsetp (cdr pairs1) pairs2 (car pairs1))))) (t (tau-pairs-near-subsetp pairs1 (cdr pairs2) e)))) (defun tau-pairs-intersectionp (pairs1 pairs2) ; The two arguments are lists of tau-pairs, ordered descending. We determine ; whether there is an element in both lists. We key only on the indices and ; ignore the cdrs. Thus, we say there is an intersection between ((1 . a)) and ; ((1 . b)). (cond ((endp pairs1) nil) ((endp pairs2) nil) ((>= (car (car pairs1)) (car (car pairs2))) (if (equal (car (car pairs1)) (car (car pairs2))) t (tau-pairs-intersectionp (cdr pairs1) pairs2))) (t (tau-pairs-intersectionp pairs1 (cdr pairs2))))) (defun insert-tau-pair (pair pairs) ; Pair is a tau-pair and pairs is a list of tau-pairs, ordered descending. We ; insert pair into pairs or else return t if it is already there. (cond ((endp pairs) (cons pair pairs)) ((>= (car (car pairs)) (car pair)) (if (eql (car (car pairs)) (car pair)) t (let ((rest (insert-tau-pair pair (cdr pairs)))) (if (eq rest t) t (cons (car pairs) rest))))) (t (cons pair pairs)))) ; On the Most Basic Implications of Being in an Interval ; Suppose x is in an interval (lo x='evg then ; either (q x) is always false or else (q x) <--> x='evg. This trivial ; observation is confirmed by: ; (encapsulate ((q (x) t)) ; (local (defun q (x) (equal x '23))) ; (defthm q-constraint (and (booleanp (q x)) ; (implies (q x) (equal x '23))) ; :rule-classes nil)) ; (thm (or (equal (q x) nil) ; (equal (q x) (equal x '23))) ; :hints (("Goal" :use q-constraint))) ; The case for (not (q x)) --> x='evg is symmetric. But we assume no pred is ; defined to be constantly t or nil and none hides an equality-with-constant. ; On the other hand, it is possible for the preds to imply the negation of an ; equality-with-constant. For example, if pos-pairs contains integerp ; and evg is 'ABC, then (integerp x) --> X/='ABC is true and hence X='ABC is ; false. ((exists-bad-valp nil (access tau tau :pos-pairs) recog ens wrld) nil) ((exists-bad-valp t (access tau tau :neg-pairs) recog ens wrld) nil) (t '?))) (t ; sign/recog is a negative equality on the evg in recog (cond ((access tau tau :pos-evg) (not (equal recog (access tau tau :pos-evg)))) ((member-neg-evgs recog (access tau tau :neg-evgs)) t) ((not (eval-tau-interval (access tau tau :interval) (car recog))) t) ((exists-bad-valp nil (access tau tau :pos-pairs) recog ens wrld) t) ((exists-bad-valp t (access tau tau :neg-pairs) recog ens wrld) t) (t '?))))) ((eq discriminator :lessp-x-k) (let ((k (car recog)) (interval (access tau tau :interval))) (cond ((null interval) (cond ((tau-pair-member *tau-acl2-numberp-pair* (access tau tau :neg-pairs)) (signate sign (< 0 k))) (t '?))) (t (let ((ans (interval-decider (access tau-interval interval :lo-rel) (access tau-interval interval :lo) (access tau-interval interval :hi-rel) (access tau-interval interval :hi) t k))) (signate sign ans)))))) ((eq discriminator :lessp-k-x) (let ((k (car recog)) (interval (access tau tau :interval))) (cond ((null interval) (cond ((tau-pair-member *tau-acl2-numberp-pair* (access tau tau :neg-pairs)) (signate sign (< k 0))) (t '?))) (t (let ((ans (interval-decider (access tau-interval interval :lo-rel) (access tau-interval interval :lo) (access tau-interval interval :hi-rel) (access tau-interval interval :hi) nil k))) (signate (not sign) ans)))))) (t ; recog is a tau-pair and discriminator is the predicate symbol (cond ((access tau tau :pos-evg) ; tau is X='evg (let ((val (ev-fncall-w-tau-recog discriminator (access tau tau :pos-evg) ens wrld))) (cond ((eq val :UNEVALABLE) (cond ((tau-pair-member recog (if sign (access tau tau :pos-pairs) (access tau tau :neg-pairs))) t) ((tau-pair-member recog (if sign (access tau tau :neg-pairs) (access tau tau :pos-pairs))) nil) (t '?))) (sign (if val t nil)) (t (if val nil t))))) ((tau-pair-member recog (if sign (access tau tau :pos-pairs) (access tau tau :neg-pairs))) t) ((tau-pair-member recog (if sign (access tau tau :neg-pairs) (access tau tau :pos-pairs))) nil) (t '?)))))) ; Before running these tests, add the defuns and macros for ptrans and mktau ; from the section ``On Tau Debugging Features'' below. They can't be defined ; at this point because they use some functions not yet defined. ; ; (logic) ; ; (defun predicted (expr val) ; (if (equal expr val) 'Passed 'Failed!)) ; ; ; Every element of the following list should be PASSED. ; ; (list ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau t *tau-natp-pair* ; (ens state) (w state)) ; t)) ; ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau nil *tau-natp-pair* ; (ens state) (w state)) ; nil)) ; ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau t '(16 . nil) ; (ens state) (w state)) ; '?)) ; ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau t '(15 . nil) ; (ens state) (w state)) ; NIL)) ; ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau nil '(15 . nil) ; (ens state) (w state)) ; T)) ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau nil '(20 . nil) ; (ens state) (w state)) ; T)) ; (let ((tau (mktau t (natp x) (evenp x) (not (equal x 20))))) ; (predicted (reduce-sign/recog tau t '(20 . nil) ; (ens state) (w state)) ; NIL)) ; (let ((tau (mktau t (natp x) (evenp x)))) ; (predicted (reduce-sign/recog tau t '(17 . nil) ; (ens state) (w state)) ; NIL)) ; ; ; If (10 <= x <= 20) then (x =17) may or may not be true. ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(17 . nil) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= 20) then (x/=17) may or may not be true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(17 . nil) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= 20) then (x =21) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(21 . nil) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (x/=21) is true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(21 . nil) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (x ='ABC) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(abc . nil) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (x/='ABC) is true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(abc . nil) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (x =20) may or may not be true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(20 . nil) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= 20) then (x/=20) may or may not be true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(20 . nil) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x < 20) then (x =20) is false ; (let ((tau (mktau t (<= 10 x) (< x 20)))) ; (predicted (reduce-sign/recog tau t '(20 . nil) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x < 20) then (x/=20) is true ; (let ((tau (mktau t (<= 10 x) (< x 20)))) ; (predicted (reduce-sign/recog tau nil '(20 . nil) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (x =9) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(9 . nil) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (x < 9) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(9 . :lessp-x-k) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (x < 15) is unknown ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(15 . :lessp-x-k) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= 20) then (x < 24) is true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(24 . :lessp-x-k) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (x < 21) is true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(21 . :lessp-x-k) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (x < 20) is unknown ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(20 . :lessp-x-k) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= 20) then (x < 10) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(10 . :lessp-x-k) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (5 < x) is true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(5 . :lessp-k-x) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (25 < x) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(25 . :lessp-k-x) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (15 < x) is unknown ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau t '(15 . :lessp-k-x) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= 20) then (5 <= x) is true ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(5 . :lessp-x-k) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= 20) then (25 <= x) is false ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(25 . :lessp-x-k) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= 20) then (15 <= x) is unknown ; (let ((tau (mktau t (<= 10 x) (<= x 20)))) ; (predicted (reduce-sign/recog tau nil '(15 . :lessp-x-k) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= infinity) then (x <= 15) is unknown ; (let ((tau (mktau t (<= 10 x)))) ; (predicted (reduce-sign/recog tau nil '(15 . :lessp-k-x) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= infinity) then (x <= 15) is true ; (let ((tau (mktau t (<= x 10)))) ; (predicted (reduce-sign/recog tau nil '(15 . :lessp-k-x) ; (ens state) (w state)) ; t)) ; ; ; If (10 <= x <= infinity) then (x = 'abc) is false ; (let ((tau (mktau t (<= 10 x)))) ; (predicted (reduce-sign/recog tau t '(abc . nil) ; (ens state) (w state)) ; nil)) ; ; ; If (10 <= x <= infinity) then (x = 20) is unknown ; (let ((tau (mktau t (<= 10 x)))) ; (predicted (reduce-sign/recog tau t '(20 . nil) ; (ens state) (w state)) ; '?)) ; ; ; If (10 <= x <= infinity) then (acl2-numberp x) is true ; (let ((tau (mktau t (<= 10 x)))) ; (predicted (reduce-sign/recog tau t *tau-acl2-numberp-pair* ; (ens state) (w state)) ; t)) ; ; If (not (acl2-numberp x)), then (< x 30) is true. ; (let ((tau (mktau t (not (acl2-numberp x))))) ; (predicted (reduce-sign/recog tau t '(30 . :lessp-x-k) ; (ens state) (w state)) ; t)) ; ; ; If (not (acl2-numberp x)), then (< x -30) is false ; (let ((tau (mktau t (not (acl2-numberp x))))) ; (predicted (reduce-sign/recog tau t '(-30 . :lessp-x-k) ; (ens state) (w state)) ; nil)) ; ; ; If (< x -30) then (acl2-numberp x) is true ; (let ((tau (mktau t (< x -30)))) ; (predicted (reduce-sign/recog tau t *tau-acl2-numberp-pair* ; (ens state) (w state)) ; t)) ; ; ; If 0 <= x <= 0 and (acl2-numberp x) then x is 0 ; (let ((tau (mktau t (<= 0 x) (<= x 0) (acl2-numberp x)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; t)) ; ; ; Here I repeat the above for other permutations of the order in which the ; ; hyps are processed: ; ; (let ((tau (mktau t (<= x 0) (<= 0 x) (acl2-numberp x)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; t)) ; ; (let ((tau (mktau t (<= x 0) (acl2-numberp x) (<= 0 x)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; t)) ; ; (let ((tau (mktau t (<= 0 x) (acl2-numberp x) (<= x 0)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; t)) ; ; (let ((tau (mktau t (acl2-numberp x) (<= x 0) (<= 0 x)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; t)) ; ; (let ((tau (mktau t (acl2-numberp x) (<= 0 x) (<= x 0)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; t)) ; ; ; IF (0 <= x <= 0) then (x=0) is unknown. ; (let ((tau (mktau t (<= 0 x) (<= x 0)))) ; (predicted (reduce-sign/recog tau t '(0 . nil) ; (ens state) (w state)) ; '?)) ; ; ; IF (1/2 <= x <= 1/2) then (x=1/2) is t ; (let ((tau (mktau t (<= 1/2 x) (<= x 1/2)))) ; (predicted (reduce-sign/recog tau t '(1/2 . nil) ; (ens state) (w state)) ; t)) ; ; ; IF (#c(1 2) <= x <= #c(1 2)) then (x=#c(1 2)) is '? -- because we can't handle ; ; complex bounds! ; (let ((tau (mktau t (<= #c(1 2) x) (<= x #c(1 2))))) ; (predicted (reduce-sign/recog tau t '(#c(1 2) . nil) ; (ens state) (w state)) ; '?)) ; ) ; ----------------------------------------------------------------- ; On Firing Signature Rules ; Consider a term like (fn a). Suppose tau1 is the tau for a. Suppose the ; signature for fn is fn: tau2 --> tau3. To fire this rule we will need to ; determine whether S[tau1] \subset S[tau2]. ; To think about this, ignore the concrete structure of a tau, including the ; signs of things; think a tau as a set of recognizers (!). The meaning of a ; recognizer set is the conjunction of its elements -- so bigger sets of ; recognizers comprehend smaller sets of objects. In particular, S[tau1] ; \subset S[tau2] if tau2 \subset tau1. (Of course, it is possible for S[tau1] ; to be a subset of S[tau2] under other conditions, but this one is ; sufficient.) ; Let tau1 = {p1 ... pn} and tau2 = {q1 ... qk}. We want to know whether ; S[tau1] \subset S[tau2], which is equivalent to M[tau1] --> M[tau2], which is ; (implies (and (p1 x) ... (pn x)) (and (q1 x) ... (qk x))). ; Note that one way to prove this conjecture would be to show that each qi is ; some pj. ; Because of the concrete structure of tau we need only compare them ; component-wise. For example, if q is among the :neg-pairs of one we need to ; search for it only among the :neg-pairs of the other. This holds for the ; four components pos-evg, neg-evgs, pos-pairs, and neg-pairs. We have to ; handle intervals specially. (defun every-neg-evg-in-tau-p1 (neg-evgs1 neg-evgs2 pos-pairs2 neg-pairs2 interval2 ens wrld) ; Neither neg-evgs1 nor neg-evgs2 contains (NIL). This function is just an ; efficient way of checking that every element of neg-evgs1 is (a) in ; neg-evgs2, or (b) subsumed by the tau-interval interval2, (c) subsumed by an ; element of pos-pairs2, or (d) subsumed by an element of neg-pairs2. For ; example, if an element of neg-evgs1 is /=7 then it might be subsumed in ; another tau by with an explicit /=7 in it, or by an interval not including 7, ; or by some sign/recognizer in the other tau being false on it. We could do ; this by mapping over neg-evgs1 and just check member-neg-evgs and the other ; checks each element. But that method is quadratic in the lengths of the two ; neg-evgs, since member-neg-evgs would repeatedly search neg-evgs2. Instead, ; we exploit the fact that both neg-evgs are ``almost'' lexordered ascending. (cond ((endp neg-evgs1) t) ((endp neg-evgs2) (and (or (not (eval-tau-interval interval2 (car (car neg-evgs1)))) (exists-bad-valp nil pos-pairs2 (car neg-evgs1) ens wrld) (exists-bad-valp t neg-pairs2 (car neg-evgs1) ens wrld)) (every-neg-evg-in-tau-p1 (cdr neg-evgs1) nil pos-pairs2 neg-pairs2 interval2 ens wrld))) ((lexorder (car (car neg-evgs1)) (car (car neg-evgs2))) (cond ((equal (car (car neg-evgs1)) (car (car neg-evgs2))) (every-neg-evg-in-tau-p1 (cdr neg-evgs1) (cdr neg-evgs2) pos-pairs2 neg-pairs2 interval2 ens wrld)) (t (and (or (not (eval-tau-interval interval2 (car (car neg-evgs1)))) (exists-bad-valp nil pos-pairs2 (car neg-evgs1) ens wrld) (exists-bad-valp t neg-pairs2 (car neg-evgs1) ens wrld)) (every-neg-evg-in-tau-p1 (cdr neg-evgs1) neg-evgs2 pos-pairs2 neg-pairs2 interval2 ens wrld))))) (t (every-neg-evg-in-tau-p1 neg-evgs1 (cdr neg-evgs2) pos-pairs2 neg-pairs2 interval2 ens wrld)))) (defun every-neg-evg-in-tau-p (neg-evgs tau ens wrld) ; Think of M[neg-evgs] as a conjunction of negative equalities with constants. ; We wish to know whether M[tau] --> M[neg-evgs]. We answer t if so, and nil if ; we do not know. (cond ((endp neg-evgs) t) ((access tau tau :pos-evg) ; If tau says x is some specific evg, then M[neg-evgs] is true as long as evg ; isn't among the neg-evgs. I.e., x=5 --> (x/=7 & x/='ABC). One might wonder ; whether we need to look into the :pos or :neg-pairs of tau, which might ; contain some unevalable predicates, e.g., that P is also true of 5. But that ; cannot help us establish an inequality. (not (member-neg-evgs (access tau tau :pos-evg) neg-evgs))) ; The following is essentially every-neg-evg-in-tau-p1 unrolled for the (NIL) ; case possibly at the front of the two relevant neg-evgs. (t (let ((neg-evgs1 neg-evgs) (neg-evgs2 (access tau tau :neg-evgs)) (pos-pairs2 (access tau tau :pos-pairs)) (neg-pairs2 (access tau tau :neg-pairs)) (interval2 (access tau tau :interval))) (cond ((eq (car (car neg-evgs1)) nil) (cond ((endp neg-evgs2) (and (or (not (eval-tau-interval interval2 nil)) (exists-bad-valp nil pos-pairs2 *nil-singleton-evg-list* ens wrld) (exists-bad-valp t neg-pairs2 *nil-singleton-evg-list* ens wrld)) (every-neg-evg-in-tau-p1 (cdr neg-evgs1) nil pos-pairs2 neg-pairs2 interval2 ens wrld))) ((eq (car (car neg-evgs2)) nil) (every-neg-evg-in-tau-p1 (cdr neg-evgs1) (cdr neg-evgs2) pos-pairs2 neg-pairs2 interval2 ens wrld)) (t (and (or (not (eval-tau-interval interval2 nil)) (exists-bad-valp nil pos-pairs2 *nil-singleton-evg-list* ens wrld) (exists-bad-valp t neg-pairs2 *nil-singleton-evg-list* ens wrld)) (every-neg-evg-in-tau-p1 (cdr neg-evgs1) neg-evgs2 pos-pairs2 neg-pairs2 interval2 ens wrld))))) ((and (consp neg-evgs2) (eq (car (car neg-evgs2)) nil)) (every-neg-evg-in-tau-p1 neg-evgs1 (cdr neg-evgs2) pos-pairs2 neg-pairs2 interval2 ens wrld)) (t (every-neg-evg-in-tau-p1 neg-evgs1 neg-evgs2 pos-pairs2 neg-pairs2 interval2 ens wrld))))))) ; We will return to the issue of firing signature rules after we've dealt with ; intervals. ; On Comparing Bounds ; We now define four functions for comparing bounds: ; lower-bound-<= ; lower-bound-> ; upper-bound->= ; upper-bound-< ; All four take the specification of two bounds, a-rel a and b-rel b, which are ; each upper or lower bounds of two intervals over the same domain and decide ; whether the first bound is in the given relation to the second one. For ; example, (upper-bound-< a-rel a b-rel b) decides whether the upper bound ; a-rel a is strictly below the upper bound b-rel b. ; These functions are complicated by the fact that they compare bounds not ; merely numbers (we must take into account the strengths of the ; inequalities), that the domains are the same, that for INTEGERP domain the ; bounds are in our normal form (rel = nil and extent is an integer or NIL), and ; that a and/or b may be NIL, indicating the appropriate infinity. ; Nevertheless, the functions are very simple. We first define all four and ; then we exhibit a big comment specifying one of them and proving it correct. ; The two lower-bound functions are actually the negations of each other, as ; are the two upper-bound functions. ; (lower-bound-<= a-rel a b-rel b) <--> (not (lower-bound-> a-rel a b-rel b)) ; (upper-bound->= a-rel a b-rel b) <--> (not (upper-bound-< a-rel a b-rel b)) ; a is NIL or a rational and b is NIL or a rational. ; Why define all four if two of them are the negations of the other two? The ; answer is the same as to the question, ``why define ``>='' if it is just the ; negation of ``<''? Answer: it clarifies thinking if one doesn't introduce ; negations. We sometimes need ``>='' and sometimes need ``<''. ; Specification of Bound Comparisons ; We specify all four in terms of (implicit) intervals A and B because all ; our applications involve intervals and we find it clearer to think about ; relations on intervals than on bounds. The (upper or lower, as appropriate) ; bounds on A are given by a-rel and a, and those on B are b-rel and b. For ; brevity in our comments, we use LA(x) to mean that x satisfies the lower ; bound on A. For example, if A's lower bound is given by arel=t and a = 5, ; then LA(x) means (< 5 x). Similar conventions are made for LB(x), UA(x), and ; UB(x). ; We assume that the domains of both A and B are the same. We furthermore ; assume that if the domain is INTEGERP, then a-rel and b-rel are both nil (<=) ; and that a and b are integers if they are non-nil. ; We describe the meaning of each of our four functions in three ways: an ; informal English sentence, a formula, and a picture. In the pictures we use ; parentheses to mark the bounds of each interval. But we do not mean to ; suggest that the intervals are open at that end. ; LOWER-BOUND-<= ; Informally: lower bound of A is weakly below the lower bound of B, by which ; we mean that anything satisfying the lower bound of B will also satisfy that ; for A ; Formally: [all x : LB(x) ---> LA(x)] ; Picture: A B ; (---(---... ; UPPER-BOUND->= ; Informally: upper bound of A is weakly above the upper bound of B by which ; we mean that anything satisfying the upper bound of B will also satisfy the ; upper bound of A ; Formally: [all x : UB(x) ---> UA(x)] ; Picture: B A ; ...---)---) ; LOWER-BOUND-> ; Informally: lower bound of A is strictly above the lower bound of B, by ; which we mean not only that anything satisfying the lower bound of A ; satisfies the lower bound of B but also that there is an element of the ; domain which satisfies the lower bound of B but not that of A ; Formally: [all x : LA(x) ---> LB(x)] & [exists z : dom(z) & LB(z) & -LA(z)] ; Picture: B A ; (-z-(---... ; ; UPPER-BOUND-< ; Informally: Upper bound of A is strictly below the upper bound of B, by ; which we mean not only that anything satisfying the upper bound of A ; satisfies the upper bound of B but also that there is a z in the domain that ; satisfies the upper bound of B but not that of A ; Formally: [all x : UA(x) ---> UB(x)] & [exists z : dom(z) & UB(z) & -UA(z)] ; Picture: A B ; ...---)-z-) (defun lower-bound-<= (a-rel a b-rel b) ; See Specification of Bound Comparisons, above. (if (null a) t (if (null b) nil (if (and a-rel (not b-rel)) (< a b) (<= a b))))) ; Note that if (and a-rel (not b-rel)) then the ``a'' bound is ``a < ...'' and ; the ``b'' bound is ``b <= ...''. Thus, if a=b, the b interval extends further. (defun upper-bound->= (a-rel a b-rel b) ; See Specification of Bound Comparisons, above. (if (null a) t (if (null b) nil (if (and a-rel (not b-rel)) (< b a) (<= b a))))) (defun lower-bound-> (a-rel a b-rel b) ; See Specification of Bound Comparisons, above. (if (null a) nil (if (null b) t (if (and a-rel (not b-rel)) (>= a b) (> a b))))) (defun upper-bound-< (a-rel a b-rel b) ; See Specification of Bound Comparisons, above. (if (null a) nil (if (null b) t (if (and a-rel (not b-rel)) (<= a b) (< a b))))) ; On the Proof of Correctness of upper-bound-< ; (verify-termination lower-bound-<=) ; (verify-termination lower-bound->) ; (verify-termination upper-bound->=) ; (verify-termination upper-bound-<) ; (include-book "arithmetic-5/top" :dir :system) ; (defun a-rel a b-rel b))))) ; (thm (implies (and (or (rationalp a) (null a)) ; (or (rationalp b) (null b))) ; (iff (upper-bound->= a-rel a b-rel b) ; (not (upper-bound-< a-rel a b-rel b))))) ; We will prove that upper-bound-< is correct wrt our specification. ; Theorem: Correctness of upper-bound-<. ; Suppose you have two intervals, A and B, over the same domain and suppose ; that a-rel a and b-rel b specify the bounds, respectively. Either or both of ; a and b may be NIL, denoting positive infinities. If either is non-NIL, it ; is a rational. Furthermore, if the domain in question is INTEGERP, both ; a-rel and b-rel are nil (<=) and a and b are integers unless they are NIL ; (infinity). ; We are to return T iff ; The picture: ; Picture: A B ; ...---)-z-) ; [i] & [ii] ; [all x : UA(x) ---> UB(x)] & [exists z : dom(z) & UB(z) & -UA(z)] ; Proof Sketch: Step 1. We first deal with the two infinities. Step 2. Then ; we show, for rational a and b, that if we return T, conjuncts [i] and [ii] ; above both hold. Step 3. Then we show, for rational a and b, that if we ; return nil, at least one of the conjuncts fails to hold. ; Step 1: Suppose a is positive infinity. Then upper-bound-< returns nil. To ; see that this is correct, case split on whether b is positive infinity. Then ; there can be no z such that UB(z) but such that -UA(z), so [ii] fails. If b ; is finite, then [i] fails: let x be b+1 and observe that UA(x) holds but ; UB(x) requires (= have onerous preconditions: the two bounds provided must ; govern two intervals, A and B, with the same domain and if that domain is ; INTEGERP, then both relations are weak (<=) and both extents are integers. ; Now in the case below, the two intervals may or may not have the same domain. ; If they do, all is well. But if they don't, then we can imagine that the ; more restrictive domain, which necessarily must be dom1, is replaced by the ; less restrictive one, dom2, which necessarily won't be INTEGERP and which ; must include the integers and, more generally, the rationals. So if that ; imaginary extension of interval1 to dom2 is in interval2, so is interval1. (lower-bound-<= (access tau-interval interval2 :lo-rel) (access tau-interval interval2 :lo) (access tau-interval interval1 :lo-rel) (access tau-interval interval1 :lo)) (upper-bound->= (access tau-interval interval2 :hi-rel) (access tau-interval interval2 :hi) (access tau-interval interval1 :hi-rel) (access tau-interval interval1 :hi))))) (defun tau-implies (tau1 tau2 ens wrld) ; If we return non-nil, then M[tau1] --> M[tau2]. (cond ((eq tau1 *tau-contradiction*) t) ((eq tau2 *tau-contradiction*) nil) ((access tau tau2 :pos-evg) (if (access tau tau1 :pos-evg) (equal (access tau tau2 :pos-evg) (access tau tau1 :pos-evg)) nil)) ((access tau tau1 :pos-evg) ; M[Tau1] is an equality to some evg and M[tau2] is a conjunction of negative ; equalities, positive and negative recognizers, and an interval. M[tau1] ; makes M[tau2] true if every element of tau2 is satisfied by the evg in ; question. (and (not (member-neg-evgs (access tau tau1 :pos-evg) (access tau tau2 :neg-evgs))) (all-eval-valp t (access tau tau2 :pos-pairs) (access tau tau1 :pos-evg) ens wrld) (all-eval-valp nil (access tau tau2 :neg-pairs) (access tau tau1 :pos-evg) ens wrld) (eval-tau-interval (access tau tau2 :interval) (car (access tau tau1 :pos-evg))))) (t ; Every neg evg in tau2 must be implied by tau1, every symbolic recognizer of ; tau2 must be among the same-signed recognizers of tau1 (tau1 is stronger and ; so may contain additional conjuncts), and the interval of tau1 must be ; contained in that of tau2. Note that we're generally interested in ``is each ; component of tau2 smaller than the corresponding component of tau1'' but in ; the case of the intervals the roles are reversed and tau1's must be the ; smaller interval. (and (every-neg-evg-in-tau-p (access tau tau2 :neg-evgs) tau1 ens wrld) (tau-pairs-subsetp (access tau tau2 :pos-pairs) (access tau tau1 :pos-pairs)) (tau-pairs-subsetp (access tau tau2 :neg-pairs) (access tau tau1 :neg-pairs)) (tau-subintervalp (access tau tau1 :interval) (access tau tau2 :interval)))))) (defun empty-tau-intervalp (lo-rel lo hi-rel hi) ; We return t if the arguments describe an empty interval. (cond ((null lo) nil) ((null hi) nil) ((or lo-rel hi-rel) (<= hi lo)) (t (< hi lo)))) (defun singleton-tau-intervalp (lo-rel lo hi-rel hi) ; A well-formed interval contains exactly one element iff both relations are <= ; and the two endpoints are non-nil and equal. ; Caution: The name ``singleton interval'' is misleading! If the domain is ; NIL and the ``singleton interval'' is 0 <= ... <= 0, then it contains ALL ; non-numbers! (and lo (equal lo hi) (not lo-rel) (not hi-rel))) ; We are in the process of defining add-to-tau1, which adds a signed recognizer to a ; tau. When we add an equal-to-constant, we might need to add an identity interval. (defun make-identity-interval (interval evg) ; We make the interval containing exactly evg, if such an interval can be ; expressed. We reuse interval if it already says what we want. (let ((dom (cond ((integerp evg) 'integerp) ((rationalp evg) 'rationalp) ; If we allowed bounds to be complex-rationals then we could handle the acl2-numberp ; case for an identity interval, e.g., x = #c(3 5) <--> #c(3 5) <= x <= #c(3 5). ; But to make things simpler we've prohibited complex bounds so we cannot ; express an equality-with-complex-rational as an interval. ;((acl2-numberp evg) 'acl2-numberp) (t nil)))) (cond ((null dom) nil) ((and (eq dom (access tau-interval interval :domain)) (eql evg (access tau-interval interval :lo)) (eq nil (access tau-interval interval :lo-rel)) (eql evg (access tau-interval interval :hi)) (eq nil (access tau-interval interval :hi-rel))) ; We use EQL above instead of = because the interval bounds might be NIL. interval) (t (make tau-interval :domain dom :lo evg :lo-rel nil :hi-rel nil :hi evg))))) (defun identity-intervalp (int) ; If this function returns t then int is an identity interval and (access ; tau-interval int :lo) is the object identified. To be an identity, an ; interval must have the domain INTEGERP or RATIONALP, the relations must be ; weak (<=), and lo and hi must be non-nil and equal. (and (or (eq (access tau-interval int :domain) 'INTEGERP) (eq (access tau-interval int :domain) 'RATIONALP)) (null (access tau-interval int :lo-rel)) (null (access tau-interval int :hi-rel)) (access tau-interval int :lo) (eql (access tau-interval int :lo) (access tau-interval int :hi)))) ; Suppose we wish to add to a tau the fact that x is not some particular evg. ; Then we add the negatively signed version of the recognizer (evg . nil). We ; will first check that the evg in question is not already ruled out by the ; other components. Then we add it to the neg-evgs. But what about the ; interval? ; The most general rule is this: ; If the domain is ACL2-NUMBERP and the excepted evg is equal to one of the ; endpoints (meaning the evg is the bound and the corresponding rel is weak), ; we can strengthen the rel. For example, the interval over the ACL2-NUMBERPs ; such that 5 <= x < 10, when conjoined with x /= 5, produces 5 < x < 10. (Of ; course, INTEGERP intervals are a special case discussed below.) The ; following theorem justifies this for ACL2-NUMBERP intervals and the lo bound ; and the symmetric theorem holds for the hi bound. Since it holds for ; ACL2-NUMBERP, it holds for RATIONALP and INTEGERP intervals too. However, it ; does not hold for unrestricted intervals. For example, if the domain is ; unrestricted and 0 <= x < 10, and we add x/=0, we cannot change the lower ; bound to 0 < x < 10, since x='NAN is in the original interval and satisfies ; x/=0 but is not in the changed interval. ; For the defun of (car (car neg-evgs)) k) (mv k nil)) ((eql (car (car neg-evgs)) k) (mv (- k 1) nil)) (t (mv-let (k1 neg-evgs1) (delete-consecutive-integers-downward k (cdr neg-evgs)) (cond ((eql (car (car neg-evgs)) k1) (mv (- k1 1) neg-evgs1)) (t (mv k1 (cons (car neg-evgs) neg-evgs1)))))))) ; (delete-consecutive-integers-downward 8 '((3)(4)(7)(8)(17))) ; = (mv 6 '((3) (4))) (defun collect- rel k lo-rel lo)) (mv rel k) (mv lo-rel lo)) (mv-let (hi-rel hi) (if (and upper-boundp (upper-bound-< rel k hi-rel hi)) (mv rel k) (mv hi-rel hi)) ; Note that we might have changed either or both of the bounds. For example, ; if we changed the domain to INTEGERP, we might have moved both bounds. In ; addition, one of the bounds might have been pulled toward the other by ; upper-boundp, rel, and k. But regardless of how we obtained them, the best ; bounds we have, based entirely on the domain, the bounds of the original ; interval, and the rel,k adjustment, are now lo-rel, lo, hi-rel, and hi. ; However, it is possible that we can do further adjustment via the neg-evgs, ; and we might have to adjust either or both of the bounds. (mv-let (new-lo-rel new-lo new-neg-evgs) (if (and (equal lo-rel lo-rel0) (equal lo lo0)) (mv lo-rel lo neg-evgs) (adjust-lower-bound-with-neg-evgs domain lo-rel lo neg-evgs)) (mv-let (new-hi-rel new-hi new-neg-evgs) (if (and (equal hi-rel hi-rel0) (equal hi hi0)) (mv hi-rel hi new-neg-evgs) (adjust-upper-bound-with-neg-evgs domain hi-rel hi new-neg-evgs)) ; So now the new, best bounds are given by new-lo-rel, new-lo, new-hi-rel, and ; new-hi, and the neg-evgs by new-neg-evgs. Next we bind new-domain to ; ACL2-NUMBERP if it was completely unrestricted and the interval does not ; include 0; otherwise it is domain, as before. (let ((new-domain (if (and (null domain) (or (positive-lower-boundp new-lo-rel new-lo) (negative-upper-boundp new-hi-rel new-hi))) 'acl2-numberp domain))) ; Even if the domain has been changed to ACL2-NUMBERP, we need not further ; filter the :neg-evgs: they will have been filtered by the adjustments to ; accommodate the interval bounds which are away from 0. It remains only to ; see if this interval is empty or a singleton. (cond ((and (eq new-domain domain0) (eq new-lo-rel lo-rel0) (eql new-lo lo0) (eq new-hi-rel hi-rel0) (eql new-hi hi0)) tau) ((empty-tau-intervalp new-lo-rel new-lo new-hi-rel new-hi) *tau-contradiction*) ((and new-domain (singleton-tau-intervalp new-lo-rel new-lo new-hi-rel new-hi)) ; If the interval is a singleton, e.g., k <= x <= k, then if the domain is ; non-nil, we know the tau is the identity. (In this case we will also know ; that the domain is either INTEGERP or RATIONALP as per the type of k.) The ; only case in which a singleton interval does not convert to a constant is if ; k=0 and the new-domain is nil. Furthermore, if k/=0, then domain is never ; nil. (set-tau-pos-evg tau (cons new-hi nil) ens wrld)) (t (change tau tau :interval (make tau-interval :domain new-domain :lo new-lo :lo-rel new-lo-rel :hi-rel new-hi-rel :hi new-hi) :neg-evgs new-neg-evgs)))))))))))))) (defun add-to-tau1 (sign recog tau ens wrld) ; Recog is a tau-pair, singleton evg list, or one of the inequality ; recognizers, (k . :lessp-x-k) or (k . :lessp-k-x). Tau is a tau object or ; *tau-contradiction* and is not obviously contradictory unless it is ; *tau-contradiction*. We add sign/recog to tau -- WITHOUT any propagation of ; implicants. We clean up the new tau as much as possible, e.g., delete pos- ; or neg-pairs evaluable under pos-evg changes and delete negative evgs made ; redundant by pos- or neg-pairs, etc. We return the modified tau or ; *tau-contradiction*. ; Note that adding NATP, for example, to a tau will set the domain of the tau's ; interval to INTEGERP and the lower bound to at least 0. But INTEGERP is not ; added to the tau's :pos-pairs. The domain of a tau's interval is implied by ; the recognizers in the tau but is not necessarily among them. We use the ; domain to do things like adjust the bounds, not to decide whether a given ; recognizer is true. To add sign/recog AND ITS IMPLICANTS to tau, use ; add-to-tau, not add-to-tau1. ; The motivation of our cleaning up is to keep tau small. Our basic ; assumption is that we'll query taus more often than we build them, and ; smaller taus generally allow faster queries since queries involve search. (let ((discriminator (cdr recog))) (cond ((eq tau *tau-contradiction*) *tau-contradiction*) ((eq discriminator nil) (cond (sign ; We are adding a positive evg equality to tau. (cond ((access tau tau :pos-evg) (if (equal recog (access tau tau :pos-evg)) tau *tau-contradiction*)) ((member-neg-evgs recog (access tau tau :neg-evgs)) *tau-contradiction*) ((not (eval-tau-interval (access tau tau :interval) (car recog))) ; The evg falls outside the tau interval. *tau-contradiction*) (t (let ((new-pos-pairs (bad-val-or-unknowns nil (access tau tau :pos-pairs) recog ens wrld))) (cond ((eq new-pos-pairs t) ; There is a pos-pair that evals to nil on this new evg. *tau-contradiction*) (t (let ((new-neg-pairs (bad-val-or-unknowns t (access tau tau :neg-pairs) recog ens wrld))) (cond ((eq new-neg-pairs t) ; There is a neg-pair that evals to t on this new evg. *tau-contradiction*) (t (make tau :pos-evg recog :neg-evgs nil :pos-pairs new-pos-pairs :neg-pairs new-neg-pairs :interval (make-identity-interval (access tau tau :interval) (car recog)))))))))))) (t ; We are adding an evg to :neg-evgs. (cond ((access tau tau :pos-evg) (if (equal recog (access tau tau :pos-evg)) *tau-contradiction* tau)) ((member-neg-evgs recog (access tau tau :neg-evgs)) tau) ((not (eval-tau-interval (access tau tau :interval) (car recog))) tau) ((exists-bad-valp nil (access tau tau :pos-pairs) recog ens wrld) tau) ((exists-bad-valp t (access tau tau :neg-pairs) recog ens wrld) tau) (t ; Recog, which is a negative equality, /= evg, where evg is (car recog), is not ; ruled out by any existing component of tau. Thus, we will add it to tau and ; clean up. Adding a negative equality cannot change either :pos-pairs or ; :neg-pairs (without violating our rule that no recognizer is just a hidden ; equality-to-constant). Nor will it change the (empty) :pos-evg field. But ; it might change the interval! For example, if we have an INTEGERP interval ; with 0 <= x <= 10 and we know x/=8 and x/=9, and we add x/=10, we get the ; interval 0 <= x <= 7 and must eliminate the now redundant elements of ; neg-evgs. If the interval is merely RATIONALP and we exclude 10, we'd ; strengthen the upper bound to x < 10. Note that the neg-evgs and the ; interval must be consistent initially, so neg-evgs contains no element ; outside the interval. So the only way a new neg-evg can affect the interval ; is if the evg is ``equal to one of the endpoints,'' which means that the evg ; is a rational, one of the bounding relations is weak (<=), and the evg is ; equal to the bound used in that weak relation. (let ((interval (access tau tau :interval))) (cond ((and interval (rationalp (car recog))) (cond ((and (null (access tau-interval interval :lo-rel)) (eql (access tau-interval interval :lo) (car recog))) ; The excluded evg is equal to the weak lower bound so we strengthen the ; lower bound from evg <= ... to evg < ..., with the appropriate tightening. (tighten-bound tau nil ; new domain = ``no change'' nil ; upper-boundp t ; = new :lo-rel (strengthened) (car recog) ; = new :lo ens wrld)) ((and (null (access tau-interval interval :hi-rel)) (eql (access tau-interval interval :hi) (car recog))) ; The excluded evg is equal to the weak upper bound. (tighten-bound tau nil ; new domain = ``no change'' t ; upper-boundp t ; = new :hi-rel (strengthened) (car recog) ; = new :hi ens wrld)) ; In the two t cases below, we're just adding the /= evg to the :neg-evgs either ; because the excluded evg is neither the lower nor the upper bound of the interval ; or because there is no interval. (t (change tau tau :neg-evgs (insert-neg-evgs recog (access tau tau :neg-evgs)))))) (t (change tau tau :neg-evgs (insert-neg-evgs recog (access tau tau :neg-evgs))))))))))) ; So that completes the code for adding a signed equality-to-constant (positive ; or negative). ((or (eq discriminator :lessp-x-k) (eq discriminator :lessp-k-x)) (let ((k (car recog))) ; Shift from recognizer notation to interval notation ; recog sign lower upper ; :lessp-x-k t (< x k) ; :lessp-x-k nil (<= k x) ; :lessp-k-x t (< k x) ; :lessp-k-x nil (<= x k) (mv-let (upper-boundp rel) (if (eq discriminator :lessp-x-k) (if sign (mv t t) (mv nil nil)) (if sign (mv nil t) (mv t nil))) (tighten-bound tau nil ; new domain = ``no change'' upper-boundp rel k ens wrld)))) ; That completes the code for adding a signed :lessp-x-k or :lessp-k-x. (t ; recog is (i . fn) (cond ((access tau tau :pos-evg) ; tau is a :pos-evg. Then if sign/fn is true on the evg, we don't change ; anything; if sign/fn is false on the evg, we return *tau-contradiction*; else ; sign/fn is unevaluable and we add fn to :pos-pairs or :neg-pairs. (let ((val (ev-fncall-w-tau-recog discriminator (access tau tau :pos-evg) ens wrld))) (cond ((eq val :UNEVALABLE) ; Recog is unevaluable on evg. We add it to the appropriate pairs list. But ; we must make sure it is not already there and not in the other list! (cond (sign (let ((new-pos-pairs (insert-tau-pair recog (access tau tau :pos-pairs)))) (cond ((eq new-pos-pairs t) tau) ((tau-pair-member recog (access tau tau :neg-pairs)) *tau-contradiction*) (t (change tau tau :pos-pairs new-pos-pairs))))) (t (let ((new-neg-pairs (insert-tau-pair recog (access tau tau :neg-pairs)))) (cond ((eq new-neg-pairs t) tau) ((tau-pair-member recog (access tau tau :pos-pairs)) *tau-contradiction*) (t (change tau tau :neg-pairs new-neg-pairs))))))) ((eq val sign) ; same as (iff val sign), as val and sign are Boolean ; recog evals as expected; don't bother to store tau) (t ; recog evals to false; signal contradiction *tau-contradiction*)))) ((tau-pair-member recog (if sign (access tau tau :pos-pairs) (access tau tau :neg-pairs))) ; If recog is already in the appropriate pairs list, there is nothing to tau) ((tau-pair-member recog (if sign (access tau tau :neg-pairs) (access tau tau :pos-pairs))) ; If recog is in the other pairs list, we have a contradiction. *tau-contradiction*) ; Note that we do not have to check the :interval of tau to see whether it ; approves the addition of sign/recog since the only part of the interval that ; matters directly is the domain and that is reflected in the :pos-pairs of ; tau. Of course, it may be that sign/recog implies, via the database, a ; contradictory relationship with tau's :interval, but we must start to add it ; to find out. (t ; Otherwise, it is ok to add recog to the appropriate pairs list. But it may ; make it possible to delete some of the :neg-evgs in tau. Suppose one of ; those :neg-evgs asserts that X is not 'ABC. Suppose that recog asserts that ; X is INTEGERP. Then we don't need that particular element of :neg-evgs any ; more. Note that we might worry that the assertion of sign/recog makes one of ; our :neg-evgs false, but that would violate the Non-Constant Non-Equality ; Premise. (mv-let (changedp new-neg-evgs) (delete-bad-vals1 (access tau tau :neg-evgs) sign recog ens wrld) (let ((tau1 (if sign (if changedp (change tau tau :neg-evgs new-neg-evgs :pos-pairs (insert-tau-pair recog (access tau tau :pos-pairs))) (change tau tau :pos-pairs (insert-tau-pair recog (access tau tau :pos-pairs)))) (if changedp (change tau tau :neg-evgs new-neg-evgs :neg-pairs (insert-tau-pair recog (access tau tau :neg-pairs))) (change tau tau :neg-pairs (insert-tau-pair recog (access tau tau :neg-pairs))))))) ; Tau1 now has the recog in :pos-pairs or :neg-pairs as per sign and the ; :neg-evgs of tau1 is consistent with the new recog (and all the old ones). ; But the interval is not yet updated. We will call tighten-bound on tau1 to ; do that. Note in every call of tighten-bound below, the new domain is either ; (a) implied by the recognizers in tau1 or (b) implied by the existing bounds, ; or (c) implied by the new bounds. ; Here is how the interval can change. Note that we are only concerned with ; the interval here, not with adding additional recognizers to :pos-pairs or ; :neg-pairs. See the Note 1 below. ; sign/recog effect (do both if two things are listed) ; t/NATP INTEGERP domain and 0 <= ... ; t/POSP INTEGERP domain and 0 < ... ; t/INTEGERP INTEGERP domain ; t/RATIONALP RATIONALP domain ; t/ACL2-NUMBERP ACL2-NUMBERP domain ; t/MINUSP ACL2-NUMBERP domain and ... < 0 ; nil/MINUSP 0 <= ... ; Certain negatively signed recogs can cause changes if we know certain ; conditions: ; sign/recog condition effect ; nil/NATP INTEGERP domain ... < 0 ; nil/POSP INTEGERP domain ... <= 0 ; nil/ACL2-NUMBERP NIL domain 0 <= ... <= 0 ; nil/ACL2-NUMBERP non-NIL domain contradiction [See Note 2 below] ; nil/RATIONALP RATIONALP domain contradiction [See Note 2 below] ; nil/RATIONALP INTEGERP domain contradiction [See Note 2 below] ; nil/INTEGERP INTEGERP domain contradiction [See Note 2 below] ; Note 1: The effects listed above merely concern the interval. If add-to-tau1 ; added additional recognizers to :pos-pairs or :neg-pairs it would violate ; its contract not to add implicants. For example, it may be tempting to ; include: ; nil/NATP 0 <= ... nil/INTEGERP ; nil/POSP 0 < ... nil/INTEGERP ; E.g., if we've added the negation of NATP or POSP and we know, from the ; interval, that the sign is ok, then we could add the negation of INTEGERP. ; But that would violate the contract and make it impossible to construct tau ; that just represent arbitrary non-contradictory sets of recognizers as needed ; for conjunctive rules. ; Note 2: Since the tau is not necessarily closed, it is possible to have a ; restricted domain without having that predicate in the :pos-pairs. For ; example, t/NATP sets the domain to INTEGERP and without this rule we would ; not detect a contradiction from then adding nil/RATIONALP. One might think ; that there is no way to get a RATIONALP domain except to explicitly add ; t/RATIONALP, so one could argue that the second rule noted above: ; nil/RATIONALP RATIONALP domain contradiction [See Note 2 below] ; is unnecessary because we will detect the contradiction in :pos-pairs. ; But it is possible to get a RATIONALP domain without adding t/RATIONALP! ; Just form the tau for (equal e 1/2). It gives a tau with :pos-evg ; 1/2 and the singleton RATIONALP interval on 1/2. (cond (sign (case discriminator (NATP (tighten-bound tau1 'INTEGERP nil nil 0 ens wrld)) (POSP (tighten-bound tau1 'INTEGERP nil t 0 ens wrld)) (INTEGERP (tighten-bound tau1 'INTEGERP nil nil nil ens wrld)) (RATIONALP (tighten-bound tau1 'RATIONALP nil nil nil ens wrld)) (ACL2-NUMBERP (tighten-bound tau1 'ACL2-NUMBERP nil nil nil ens wrld)) (MINUSP (tighten-bound tau1 'ACL2-NUMBERP t t 0 ens wrld)) (otherwise tau1))) (t (let ((domain (access tau-interval (access tau tau1 :interval) :domain))) (case discriminator (ACL2-NUMBERP ; If we are adding nil/ACL2-NUMBERP and the interval has a non-nil domain, then ; it contradicts nil/ACL2-NUMBERP. Otherwise, we change the interval to be 0 ; <= ... <= 0 with the unrestricted domain. To set this interval we ; do two successive tighten-bounds, first (innermost) on the lower bound ; bound and then on the upper. (cond ((eq domain nil) ; Args to tighten-bound: tau domain upper-boundp rel k wrld. (tighten-bound (tighten-bound tau1 nil nil nil 0 ens wrld) nil t nil 0 ens wrld)) (t *tau-contradiction*))) (RATIONALP (if (or (eq domain 'RATIONALP) (eq domain 'INTEGERP)) *tau-contradiction* tau1)) (INTEGERP (if (eq domain 'INTEGERP) *tau-contradiction* tau1)) (MINUSP (tighten-bound tau1 nil nil nil 0 ens wrld)) (NATP (cond ((eq domain 'INTEGERP) (tighten-bound tau1 'INTEGERP t t 0 ens wrld)) (t tau1))) (POSP (cond ((eq domain 'INTEGERP) (tighten-bound tau1 nil t nil 0 ens wrld)) (t tau1))) (otherwise tau1))))))))))))) (defun add-recogs-to-tau1 (sign recog-lst tau ens wrld) ; Recog-lst is a list of recognizers, all of which have the same sign. We add ; each to tau and return tau', where tau' might be *tau-contradiction*. Note: ; In fact, each recog in recog-lst is the same ``kind'', either each is a ; singleton evg list (because recog-lst was the :neg-evgs of some recognizer ; set) or is a tau-pair (from a :pos-pairs or :neg-pairs). But we don't ; exploit this uniformity. (cond ((endp recog-lst) tau) (t (let ((tau (add-to-tau1 sign (car recog-lst) tau ens wrld))) (cond ((eq tau *tau-contradiction*) *tau-contradiction*) (t (add-recogs-to-tau1 sign (cdr recog-lst) tau ens wrld))))))) ; The two recognizers (k . :lessp-x-k) and (k . :lessp-k-x) define either upper ; or lower bounds, depending on the sign. ; token sign lower upper ; :lessp-x-k t (< x k) ; :lessp-x-k nil (<= k x) ; :lessp-k-x t (< k x) ; :lessp-k-x nil (<= x k) (defun tau-interval-endpoint-to-sign-k-token (upper-boundp rel k) ; We return (mv sign k token) where the bound described by upper-boundp, k, and ; rel is sign/(k . token). However, the latter is well-formed only if k is ; rational. This avoids consing up the concrete representation of the ; recognizer, (cons k token), though sometimes we immediately just cons it up ; anyway. (if upper-boundp (if rel (mv t k :lessp-x-k) ; (< x k) (mv nil k :lessp-k-x)) ; (<= x k) (if rel (mv t k :lessp-k-x) ; (< k x) (mv nil k :lessp-x-k)))) ; (<= k x) (defun tau-union (tau1 tau2 ens wrld) ; We add every sign/recog in tau1 to tau2 using add-to-tau1. We return tau2'. ; Tau2' may be *tau-contradiction*. This function does not add implicants ; because we assume both tau1 and tau2 are already closed under simple ; implication. (let ((tau2 (if (access tau tau1 :pos-evg) (add-to-tau1 t (access tau tau1 :pos-evg) tau2 ens wrld) tau2))) (let ((tau2 (if (eq tau2 *tau-contradiction*) *tau-contradiction* (add-recogs-to-tau1 nil (access tau tau1 :neg-evgs) tau2 ens wrld)))) (let ((tau2 (if (eq tau2 *tau-contradiction*) *tau-contradiction* (add-recogs-to-tau1 t (access tau tau1 :pos-pairs) tau2 ens wrld)))) (let ((tau2 (if (eq tau2 *tau-contradiction*) *tau-contradiction* (add-recogs-to-tau1 nil (access tau tau1 :neg-pairs) tau2 ens wrld)))) (if (eq tau2 *tau-contradiction*) *tau-contradiction* ; To add the interval of tau1 to tau2, we need not add the domain recognizer ; because it would be implied by the recognizers and bounds in tau1 and we'll ; add all those. (let ((interval (access tau tau1 :interval))) (mv-let (sign1 recog1) (if (and interval (access tau-interval interval :lo)) (mv-let (sign k token) (tau-interval-endpoint-to-sign-k-token nil (access tau-interval interval :lo-rel) (access tau-interval interval :lo)) (mv sign (cons k token))) (mv nil nil)) (mv-let (sign2 recog2) (if (and interval (access tau-interval interval :hi)) (mv-let (sign k token) (tau-interval-endpoint-to-sign-k-token t (access tau-interval interval :hi-rel) (access tau-interval interval :hi)) (mv sign (cons k token))) (mv nil nil)) ; If recog1 is non-nil, then sign1/recog1 represents the lower bound of the ; interval in tau1. If recog2 is non-nil, then sign2/recog2 represents the ; upper bound of the interval in tau1. We add both to tau2. (let ((tau2 (if recog1 (add-to-tau1 sign1 recog1 tau2 ens wrld) tau2))) (if (eq tau2 *tau-contradiction*) *tau-contradiction* (let ((tau2 (if recog2 (add-to-tau1 sign2 recog2 tau2 ens wrld) tau2))) tau2)))))))))))) ; The next two functions are used to implement the deduction that if a tau ; includes all the rationals between between lo and hi but excludes each of the ; the integers in that range, then the tau is non-INTEGERP. (defun all-integers-in-range-excludedp1 (lo hi neg-evgs) ; Lo and hi are integers and neg-evgs is a :neg-evgs list from a tau except ; that the leading (NIL), if any, has been stripped off, i.e., neg-evgs is a ; duplicate-free ``ordered ascending according to lexorder'' (see ; Prelude: ; General-Purpose Functions Having Nothing Specific to do with Tau) of ; singleton evgs not including (NIL). We check that every integer i such that ; lo <= i <= hi that (list i) is an element of neg-evgs. We know that the ; ordering is such that (NIL), if present, precedes all rationals, all ; rationals precede all complex-rationals, and all complex-rationals precede ; everything else. Integers and non-integer rationals are mixed by magnitude ; with negatives first. Thus, if we are looking for the intergers from -3 to ; 2, say, we can cdr our way through neg-evgs until we find them all or hit a ; rational bigger than hi or hit a non-rational. (cond ((> lo hi) t) ((endp neg-evgs) nil) ((equal lo (car (car neg-evgs))) (all-integers-in-range-excludedp1 (+ 1 lo) hi (cdr neg-evgs))) ((or (not (rationalp (car (car neg-evgs)))) (> (car (car neg-evgs)) hi)) nil) (t (all-integers-in-range-excludedp1 lo hi (cdr neg-evgs))))) (defun all-integers-in-range-excludedp (lo-rel lo hi-rel hi neg-evgs) ; Lo and hi are rationals that bound an interval. Neg-evgs is the :neg-evgs of ; a tau. We check that every integer between lo and hi is excluded -- which ; means for each i from lo to hi inclusive, (list i) is a member of neg-evgs. ; We can delete the NIL that might be at the front of neg-evgs. Furthermore, ; if the number of integers between lo and hi inclusive is greater than the ; length of the exclusions, we know some integer is not excluded. Otherwise, ; we check each one. ; We were once afraid that this deduction will be expensive. For example, what ; if lo is 0 and hi is (expt 2 32)? However, we'll only check as many elements ; of neg-evgs as the formula has excluded. So we have hopes this won't kill ; us! (let* ((ilo (squeeze-k nil lo-rel lo)) (ihi (squeeze-k t hi-rel hi)) (lst (if (and (consp neg-evgs) (eq nil (car (car neg-evgs)))) (cdr neg-evgs) neg-evgs)) (k (+ 1 (- ihi ilo)))) ; After squeezing the rational extents, the relations are weak (<=), so we ; check every integer between ilo and ihi, inclusive. (cond ((> k (len lst)) nil) (t (all-integers-in-range-excludedp1 ilo ihi lst))))) (defun add-to-tau (sign recog tau ens wrld) ; Recog is a tau-pair or singleton evg list. Tau is a tau object, not ; *tau-contradiction*. We add sign/recog and its implicants to tau. We assume ; tau is a complete tau and we return a complete tau', where tau' may be ; *tau-contradiction*. (let ((tau1 (let ((discriminator (cdr recog))) (cond ((or (eq discriminator nil) (eq discriminator :lessp-x-k) (eq discriminator :lessp-k-x)) (add-to-tau1 sign recog tau ens wrld)) (t (tau-union (tau-simple-implicants sign discriminator wrld) ; One might think we could just use tau here, instead of the add-to-tau1 ; expression below. However, we use the presence of certain recognizers to ; prevent recursive looping. For example, if the interval in the tau1 that we ; are creating is below 0, we add MINUSP and its implicants -- IF MINUSP isn't ; already there. This is done with a full-blown add-to-tau call further below. ; But we do not wish to depend upon the order in which the recognizers in the ; implicants are added, and if MINUSP is added later than others, we might ; loop. So here we immediately add the recognizer we were asked to add, and ; then we add all the implicants. The implicants will include sign/recog, ; which will be redundant. (add-to-tau1 sign recog tau ens wrld) ens wrld)))))) ; We have added sign/recog to tau using add-to-tau1. We now inspect tau1 for ; certain further deductions we can make, related to the interval in tau. (cond ((eq tau1 *tau-contradiction*) *tau-contradiction*) (t (let* ((interval (access tau tau1 :interval)) (domain (access tau-interval interval :domain)) (lo-rel (access tau-interval interval :lo-rel)) (lo (access tau-interval interval :lo)) (hi-rel (access tau-interval interval :hi-rel)) (hi (access tau-interval interval :hi)) (pos-pairs (access tau tau1 :pos-pairs)) (neg-pairs (access tau tau1 :neg-pairs))) (cond ((or (null interval) (and (null lo) (null hi)) (access tau tau1 :pos-evg)) ; If tau1 has an unrestricted interval (regardless of the domain) there's ; nothing more we can deduce from it because everything below is based on ; properties of the bounds. In addition, if tau1 recognizes a constant, we ; don't add anything more to it. tau1) ; We now consider deductions we can make relating the pos-pairs and neg-pairs ; to the interval bounds. We first consider deducing positive recogs and then ; negative recogs. In all cases when we add a recog to tau1 we do so with a ; full blown recursive call, so that we might fire more than one of these rules ; before we're done. We protect our recursive calls with explicit checks ; confirming that the recog isn't already present. We assume that if a tau is ; not that for a constant, then adding a recognizer that is not explicitly ; present will change the tau so the recognizer is explicitly present. ; The bounds on the interval may allow us to deduce certain other recognizers. ; The implication tree of the recognizers in question is: ; posp --> natp---> ; \ ; --> acl2-numberp ; / ; minusp --------- ; The implicants of each imply all the others along the branches above. So we ; just look for the strongest ones. ; domain and bound effect (if not already there) ; any & ... < 0 t/MINUS ; INTEGERP & 0 < ... t/POSP ; INTEGERP & 0 <= ... t/NATP ; INTEGERP t/INTEGERP ; See Note ; RATIONALP t/RATIONALP ; See Note ; ACL2-NUMBERP t/ACL2-NUMBERP ; Note: We don't think these cases can occur but signal a hard error if they ; do. ((and hi (if hi-rel (<= hi 0) (< hi 0)) (not (tau-pair-member *tau-minusp-pair* pos-pairs))) ; If the interval is bounded strictly below 0, we can add MINUSP and its ; implicants (if MINUSP is not already there). Among the implicants will be ; ACL2-NUMBERP. It is through this mechanism that tau knows that (< x 0) --> ; (ACL2-NUMBERP x). Even though an interval with a negative upper bound will ; have an ACL2-NUMBERP domain, the tau won't have ACL2-NUMBERP in it until we ; add it. (add-to-tau t *tau-minusp-pair* tau1 ens wrld)) ((and (eq domain 'INTEGERP) ; (null lo-rel) ; is guaranteed with INTEGERP domain lo (< 0 lo) (not (tau-pair-member *tau-posp-pair* pos-pairs))) ; If the interval is over the positive integers, we add POSP and its implicants ; (if POSP is not already there). (add-to-tau t *tau-posp-pair* tau1 ens wrld)) ((and (eq domain 'INTEGERP) lo (<= 0 lo) (not (tau-pair-member *tau-natp-pair* pos-pairs))) ; If the interval is over the non-negative integers, we can add NATP and its ; implicants (if NATP is not already there). (add-to-tau t *tau-natp-pair* tau1 ens wrld)) ((or (and (eq domain 'INTEGERP) (not (tau-pair-member *tau-integerp-pair* pos-pairs))) (and (eq domain 'RATIONALP) (not (tau-pair-member *tau-rationalp-pair* pos-pairs)))) (er hard 'add-to-tau "It was thought impossible for a tau to have an :interval with ~ domain ~x0 without that recognizer being in its :pos-pairs, ~ but it happened with ~x1. One way this might have happened is ~ if the initial tau was ``incomplete,'' e.g., had NATP in it ~ but not its implicants. But add-to-tau is supposed to be ~ called on complete taus and guarantees the completeness of its ~ result." domain tau1)) ((and (eq domain 'ACL2-NUMBERP) (not (tau-pair-member *tau-acl2-numberp-pair* pos-pairs))) ; If an interval is over the numbers, we add ACL2-NUMBERP and all its ; implicants (if ACL2-NUMBERP is not already there). (The primitives for ; constructing intervals use the bounds to infer the ACL2-NUMBERP domain when ; possible.) (add-to-tau t *tau-acl2-numberp-pair* tau1 ens wrld)) ; Now we consider some negative deductions: ; sign/recog condition effect ; nil/NATP 0 <= ... nil/INTEGERP ; nil/POSP 0 < ... nil/INTEGERP ; t/RATIONALP lo C is stored as ; the tau {A B -C}. If we ever encounter a tau such that some conjunctive rule ; is a near subset of it, we can assert the negation of the missing element. ; For example, given the conjunctive rule {A B -C} and tau {A -C D E F G}, we ; can assert -B, to get {A -B -C D E F G}. Think of the conjunctive rule being ; stated in the contrapositive: (A & -C) --> -B, and the tau as describing some ; object with properties A and -C (among others). Then the object also has ; property -B. ; Note that if we regard the objects in our sets as terms, we really want to ; know whether the conjunction of the terms in set2 imply all (subset) or all ; but one (near-subset) of the terms in set1. Typically, set2 describes the ; context we're in and set1 represents a bunch of hypotheses we'd like to ; relieve. For all the elements but the interval bounds, we just look for ; membership, but for the bounds we look for implication. ; Our functions for checking this property (across various data structures like ; almost lexordered lists, tau-pairs, tau, etc) all follow the convention that ; they return t if every element of the former is in the latter (i.e., the ; subset relation holds), nil if more than one element fails to occur, and the ; unique missing element otherwise. We are careful that the sets to which we ; apply this concept never contain nil as an element. (defun pos-evg-near-subsetp (pos-evg1 pos-evg2) ; This is an odd little function that plays the role for the :pos-evg slot of ; tau that tau-pairs-near-subsetp plays for :pos-pairs and ; :neg-pairs. By defining it explicitly we allow tau-near-subsetp to be defined ; as a composition across the five components. ; The :pos-evg slot of a tau is either nil or represents a singleton set ; containing the :pos-evg. So think of s1 and s2 as the sets corresponding to ; the given pos-evg1 and pos-evg2. Then we determine whether s1 is a ; near-subset of s2: If s1 is a subset of s2, we return t, if exactly one ; element of s1 fails to be an element of s2, we return that missing element, ; otherwise we return nil. However, since s1 contains at most one element, we ; never return nil! (if pos-evg1 (if pos-evg2 (if (equal pos-evg1 pos-evg2) t pos-evg1) pos-evg1) t)) (defun neg-evgs-near-subsetp (neg-evgs1 neg-evgs2 e pos-pairs2 neg-pairs2 interval2 ens wrld) ; This is analogous to tau-pairs-near-subsetp, but for the neg-evgs components. ; We wish to check the near-subset relation between the neg-evgs of two tau, ; tau1 and tau2. (Recall that lst1 is a near-subset of lst2 if there is ; exactly one element of lst1 that is not in lst2. Our convention is that ; ``near-subset'' functions return t to indicate that every element of the one ; is in the other, nil to indicate that more than one element is missing, and ; otherwise returns the missing element.) However, suppose x /= 23 is in tau1 ; by being in its neg-evgs, while it is not in the neg-evgs of tau2 because ; that evg is ruled out by some pair. For example, tau1 might be the integers ; except 23, while tau2 might be the even integers. If the assumption that x = ; 23 allows us to falsify some pos-pair in tau2, then x = 23 isn't really ; missing from tau2, it is swept up by the pos-pairs. Neg-pairs are, of ; course, symmetric. But this concept is just that codified in ; exists-bad-valp. Similar remarks apply to the interval of tau2 ruling out an ; evg. ; If every element of neg-evgs1 is either in neg-evgs2 or else is subsumed by ; some pair in the interval, pos-pairs2, or neg-pairs2, we return t. If more ; than one element of neg-evgs1 fails to have that property (i.e., is in ; neg-evgs2 or is subsumed by the pairs), we return nil. Otherwise, we return ; the missing element. (cond ((endp neg-evgs1) (if e e t)) ((endp neg-evgs2) (cond ((or (not (eval-tau-interval interval2 (car (car neg-evgs1)))) (exists-bad-valp nil pos-pairs2 (car neg-evgs1) ens wrld) (exists-bad-valp t neg-pairs2 (car neg-evgs1) ens wrld)) (neg-evgs-near-subsetp (cdr neg-evgs1) nil e pos-pairs2 neg-pairs2 interval2 ens wrld)) ; Otherwise, we know (car neg-evgs1) really is missing and so we fail if we've ; already seen a missing element and we continue with (car neg-evgs1) as our ; candidate unique witness if we haven't. (t (if e nil (neg-evgs-near-subsetp (cdr neg-evgs1) nil (car neg-evgs1) pos-pairs2 neg-pairs2 interval2 ens wrld))))) ((almost-lexorder-singletons (car neg-evgs1) (car neg-evgs2)) (if (equal (car neg-evgs1) (car neg-evgs2)) (neg-evgs-near-subsetp (cdr neg-evgs1) (cdr neg-evgs2) e pos-pairs2 neg-pairs2 interval2 ens wrld) ; At this point, we've discovered that (car neg-evgs1) is missing from ; neg-evgs2. But we can't treat it as missing until we make sure it ; isn't subsumed by one of the pairs or the interval. (cond ((or (not (eval-tau-interval interval2 (car (car neg-evgs1)))) (exists-bad-valp nil pos-pairs2 (car neg-evgs1) ens wrld) (exists-bad-valp t neg-pairs2 (car neg-evgs1) ens wrld)) ; So the ``missing'' element is actually subsumed elsewhere tau2 and we can ; treat it as found. (neg-evgs-near-subsetp (cdr neg-evgs1) (cdr neg-evgs2) e pos-pairs2 neg-pairs2 interval2 ens wrld)) (t ; Otherwise, it really is missing and we either fail or make this the candidate ; unique witness. (if e nil (neg-evgs-near-subsetp (cdr neg-evgs1) neg-evgs2 (car neg-evgs1) pos-pairs2 neg-pairs2 interval2 ens wrld)))))) (t (neg-evgs-near-subsetp neg-evgs1 (cdr neg-evgs2) e pos-pairs2 neg-pairs2 interval2 ens wrld)))) ; On the Near-Subset Relation for Intervals ; We now develop the code for checking the near-subset relation for intervals. ; However, it is useful to recap why we care about near-subsets. ; Each conjunctive rule is stored as a tau: the rule p1 & ... & pn --> q is ; stored as the tau p1 & ... & pn & -q. Suppose we have some facts about x, ; expressed as a tau for x, and wish to try to fire a conjunctive rule. If ; each of the conjuncts in the rule tau are in the factual tau, then we have a ; contradiction. But if all but one of the conjuncts of the rule are among the ; facts, then we may add the negation of the missing predicate to the facts. ; For example, if we know p1 & ... & pn then we can add q. More interestingly, ; if we know p1 & ... pn-1 & -q, we can add -pn. So it is important to be able ; to determine if all but one of the conjuncts of a tau are in another tau. We ; call this relation ``near subset.'' We will check the near-subset relation ; essentially component-wise, comparing pos-pairs to pos-pairs, neg-evgs to ; neg-evgs, etc. ; But what does it mean to be a near subset if intervals are involved? Think ; of an interval coding at most two more recognizers, zero or one chosen from ; group LO below and zero or one chosen from group HI: ; LO: (lambda (x) (<= 'lo x)) ; (lambda (x) (< 'lo x)) ; HI: (lambda (x) (<= x 'hi)) ; (lambda (x) (< x 'hi)) ; Of course, if there is no upper or lower bound (i.e., the interval is somehow ; infinite) it just means that no recognizer is chosen. ; Now, consider the conjunctive rule: (integerp x) & 0 <= x <= 100 --> (Q x) ; coded as a tau-like object: ; Rule: (integerp x) & (<= 0 x) & (<= x 100) & -(Q x) ; and a set of known facts: ; Facts: (integerp x) & (<= x 100) & -(Q x) ; we see that the Rule is a near-subset of the Facts. One Rule hypothesis is ; missing: (<= 0 x). That means we can augment the facts with its negation: (< ; x 0). ; Now what if we have even stronger Facts than the Rule requires? ; Rule: (integerp x) & (<= 0 x) & (<= x 100) & -(Q x) ; Facts: (integerp x) & (<= x 75) & -(Q x) ; Clearly, if x is below 75, it is below 100. So we can add (< x 0) here too. ; Finally, what if a fact actually contradicts the missing hypothesis? ; Suppose we have: ; Rule: (integerp x) & (<= 0 x) & (<= x 100) & -(Q x) ; Facts: (integerp x) & (< x -100) & -(Q x) ; Again, the Rule is a near subset of the Facts. In particular, we know ; (integerp x), we know (<= x 100) -- indeed, x is negative, and we know -(Q ; x). The missing hypothesis is (<= 0 x). ; But this hypothesis is actually contradicted by (< x -100). So we shouldn't ; add its negation. Why? What if we did? What if we added the "new" fact (< ; x 0). We haven't helped. We already know (< x -100). ; So we need to be able to compare the inequality hypotheses of a rule with ; some inequality facts and determine whether (a) all the hypotheses are true ; according to the facts -- in which case we have a contradiction since ; conjunctive rules are stored the way they are, or (b) there is exactly one ; hypothesis missing from the facts and it is not contradicted by the facts -- ; in which case we can add its negation to the facts, or (c) multiple ; hypotheses are missing and we do nothing. ; So now we need to be able to answer the question: given a single inequality ; fact can we determine whether a single inequality hypothesis is true? Is ; false? Is unknown? ; We next define tau-interval-endpoint-implication which takes a ``fact,'' A, a ; ``hypothesis,'' B, and decides whether the hypothesis is true given A, false ; given A, or undecided. Below is a macro definition used solely to justify ; the definition of this function. ; The macro contains a table with columns labeled (A x), (B x), C, and Z. The ; meaning of each row in this table is: ; ``Thm: C <--> [forall x : (A x) --> (B x)] ; Proof: C --> ((A x) --> (B x)) is obvious. The other direction is proved by ; first instantiating the universal x with Z and then proving ((A Z) --> (B Z)) ; --> C. Q.E.D.'' ; The commented-out code below actually checks these entries. ; (tau-status :system nil) ; (include-book "arithmetic-5/top" :dir :system) ; ; (defun generate-ABC-thms (lst) ; (cond ((endp lst) nil) ; (t (let ((A (nth 0 (car lst))) ; (B (nth 1 (car lst))) ; (C (nth 2 (car lst))) ; (Z (nth 3 (car lst)))) ; (cons `(implies (and (rationalp k) ; (rationalp d)) ; (and ; (implies ,C (implies ,A ,B)) ; (implies (let ((x ,Z)) ; (implies ,A ,B)) ; ,C))) ; (generate-ABC-thms (cdr lst))))))) ; ; (defmacro confirm-ABC-table nil ; `(thm ; (and ; ,@(generate-ABC-thms ; ; fact question ; ; (A x) (B x) C Z ; '(((< k x) (< d x) (<= d k) (+ k (/ (- d k) 2))) ; 1 ; ((< k x) (<= d x) (<= d k) (+ k (/ (- d k) 2))) ; 2 ; ((< k x) (<= x d) NIL (+ (max k d) 1)) ; 3 ; ((< k x) (< x d) NIL (+ (max k d) 1)) ; 4 ; ; ((<= k x) (< d x) (< d k) (+ k (/ (- d k) 2))) ; 5 ; ((<= k x) (<= d x) (<= d k) (+ k (/ (- d k) 2))) ; 6 ; ((<= k x) (<= x d) NIL (+ (max k d) 1)) ; 7 ; ((<= k x) (< x d) NIL (+ (max k d) 1)) ; 8 ; ; ((<= x k) (< d x) NIL (- (min k d) 1)) ; 9 ; ((<= x k) (<= d x) NIL (- (min k d) 1)) ; 10 ; ((<= x k) (<= x d) (<= k d) (+ d (/ (- k d) 2))) ; 11 ; ((<= x k) (< x d) (< k d) (+ d (/ (- k d) 2))) ; 12 ; ; ((< x k) (< d x) NIL (- (min k d) 1)) ; 13 ; ((< x k) (<= d x) NIL (- (min k d) 1)) ; 14 ; ((< x k) (<= x d) (<= k d) (+ d (/ (- k d) 2))) ; 15 ; ((< x k) (< x d) (<= k d) (+ d (/ (- k d) 2)))))))); 16 ; ; (confirm-ABC-table) ; The success of confirm-ABC-table means that the entries in the above table ; are correct. Thus, we know that ; [forall x : (A x) --> (B x)] ; is a theorem if the condition in column C is true. ; For example, consider a function call (fn a) and imagine that we know that ; the actual, a, has tau A. But imagine that the signature of fn requires B. ; Then if C is true, the signature requirement is satisfied. Alternatively, we ; might have a Conjunctive Rule, which when applied to the term a, produces the ; literal (B a). If we know (A a) then we know (B a) holds, provided C is ; true. ; We are also interested in the situation in which (A x) means that (B x) is ; false. That can be investigated by considering: ; [forall x : (A x) --> -(B x)]. ; For every (B x), -(B x) is also in the table, just a few rows away. Here is ; a rearrangement of the table in which we've deleted the Z column (the proofs) ; and included, in the C' column, the C condition for the negation of each B ; with the same A. The final line number is the line in the table that ; justifies -(B x). ; The ABC Table ; [forall x : A --> B] is true if C ; [forall x : A --> -B] is true if C' ; A B C C' ; 1 (< k x) (< d x) (<= d k) NIL ; 3 ; 2 (< k x) (<= d x) (<= d k) NIL ; 4 ; 3 (< k x) (<= x d) NIL (<= d k) ; 1 ; 4 (< k x) (< x d) NIL (<= d k) ; 2 ; 5 (<= k x) (< d x) (< d k) NIL ; 7 ; 6 (<= k x) (<= d x) (<= d k) NIL ; 8 ; 7 (<= k x) (<= x d) NIL (< d k) ; 5 ; 8 (<= k x) (< x d) NIL (<= d k) ; 6 ; 9 (<= x k) (< d x) NIL (<= k d) ; 11 ; 10 (<= x k) (<= d x) NIL (< k d) ; 12 ; 11 (<= x k) (<= x d) (<= k d) NIL ; 9 ; 12 (<= x k) (< x d) (< k d) NIL ; 10 ; 13 (< x k) (< d x) NIL (<= k d) ; 15 ; 14 (< x k) (<= d x) NIL (<= k d) ; 16 ; 15 (< x k) (<= x d) (<= k d) NIL ; 13 ; 16 (< x k) (< x d) (<= k d) NIL ; 14 ; For example, suppose we have a lower bound, A: (< 20 a), among our facts. ; Suppose the rule has the hypothesis B: (<= 15 a). We want to know whether ; the hypothesis, B, follows from the fact A. These two choices of A and B ; correspond to line 2 in the table, with k = 20 and d = 15. So we ask ; question C, (<= 15 20). The answer is T, so we know that the hypothesis B is ; satisfied. However, if B were (<= 25 a), then C would become (<= 25 20) and ; we could not relieve B. ; Note that every row contains a NIL in C or C'. So if we index into the table ; for a given A and B and find a non-NIL comparison term in C, then the table ; might tell us that B is true (if C evaluates to true), but cannot tell us ; that B is false (because C' will be NIL). ; If on the other hand we index into the table for a given A and B and find a ; NIL in C, the table cannot tell us that B is true but it MIGHT be able to ; tell us that it is false, by checking the condition in the C' column. ; For example, consider A: (<= x 20) and B: (< 25 x). This is line 9. Here, C ; = NIL, so the table cannot tell us that B is true. However, looking at the ; C' column we see the test (<= 20 25), which is true. So we know B is false. ; Finally notice that the A in lines 1-8 are lower bounds and those in lines ; 9-16 are upper bounds. Similarly, every block of B has two lower bound cases ; (e.g., lines 1 and 2 in the first block) and two upper bound cases (lines 3 ; and 4). A lower bound can sometimes establish that another lower bound ; holds; a lower bound can never establish that an upper bound holds but can ; sometimes show that an upper bound is false. The situation is symmetric with ; respect to upper bounds. These remarks only apply to finite (i.e., rational) ; bounds; if the bound in the conclusion is infinite, of course the implication ; holds. (defun tau-interval-endpoint-implication (ap arel k bp brel d) ; Ap and bp should be named ``a-upper-boundp'' and ``b-upper-boundp'' ; respectively. ; This function takes the interval-level descriptions of the endpoints of two ; intervals. The first three formals, ap, k, and arel, describe one bound of ; an interval and the next three, bp, d, and brel, describe another. We call ; these two bounds A and B respectively. The coding is ; p rel A B ; t t (< x k) (< x d) ; t nil (<= x k) (<= x d) ; nil t (< k x) (< d x) ; nil nil (<= k x) (<= d x) ; We return t if [forall x : A --> B]. We return nil if [forall x : A --> -B]. ; We return ? otherwise. We simply implement the ABC Table above, except we ; first interpret those k and/or d representing infinities. If d is NIL then B ; is true (because d represents either negative or positive infinity depending ; on whether it is a lower or upper bound). If k is NIL, then A is true and ; thus implies nothing useful. Thus the structure of the code below is: (if k ; (if d t) (if d '? t)). While implementing the table we show the ; relevant blocks from the table, rearranging the lines to show the order of ; the tests. ; Finally, a literal translation of the table, say for lines 16 and 15 below would ; ask whether brel is t or nil and test (<= k d) for the one and (<= k d) for ; the other. But since the two tests are identical, we exploit (if x y y) = y by ; commenting out the (if x ... y) parts. (if k (if d (if ap (if arel ; A B C C' ; 16 (< x k) (< x d) | (<= k d) NIL ; 15 (< x k) (<= x d) | (<= k d) NIL ; 13 (< x k) (< d x) | NIL (<= k d) ; 14 (< x k) (<= d x) | NIL (<= k d) (if bp ; (if brel (if (<= k d) t '?) ; (if (<= k d) t '?)) ; (if brel (if (<= k d) nil '?) ; (if (<= k d) nil '?)) ) ; 12 (<= x k) (< x d) | (< k d) NIL ; 11 (<= x k) (<= x d) | (<= k d) NIL ; 9 (<= x k) (< d x) | NIL (<= k d) ; 10 (<= x k) (<= d x) | NIL (< k d) (if bp (if brel (if (< k d) t '?) (if (<= k d) t '?)) (if brel (if (<= k d) nil '?) (if (< k d) nil '?)))) (if arel ; 4 (< k x) (< x d) | NIL (<= d k) ; 3 (< k x) (<= x d) | NIL (<= d k) ; 1 (< k x) (< d x) | (<= d k) NIL ; 2 (< k x) (<= d x) | (<= d k) NIL (if bp ; (if brel (if (<= d k) nil '?) ; (if (<= d k) nil '?)) ; (if brel (if (<= d k) t '?) ; (if (<= d k) t '?)) ) ; 8 (<= k x) (< x d) | NIL (<= d k) ; 7 (<= k x) (<= x d) | NIL (< d k) ; 5 (<= k x) (< d x) | (< d k) NIL ; 6 (<= k x) (<= d x) | (<= d k) NIL (if bp (if brel (if (<= d k) nil '?) (if (< d k) nil '?)) (if brel (if (< d k) t '?) (if (<= d k) t '?))))) t) (if d '? t))) ; Independently of tau we can prove that when the above function ; returns t, then A --> B, and when it returns nil, then A --> -B. ; (tau-status :system nil) ; (verify-termination tau-interval-endpoint-implication) ; In the defun below, p indicates whether rel,k is an upper ; or lower bound on x. ; (defun B and A --> -B for each combination. If any B ; comes back false or multiple Bs come back unknown, we quit with NIL (which ; might always be interpreted by our caller to mean ``multiple recognizers are ; unknown'' but, when discovered meant ``one recognizer is simply false and so ; you can't productively assume its negation''), if every B comes back true, we ; report T (meaning all the hypotheses are true), and otherwise, exactly one B ; is unknown and we return ``it.'' But what we actually return is (mv ; missing-sign missing-k missing-token) where if missing-token is t or nil we ; mean ``all found'' or ``multiple missing'' (or ``one false''), and otherwise ; missing-token is :lessp-x-k or :lessp-k-x and missing-sign and missing-k are ; the other pieces of the missing sign/recog. ; In the following we use ``A lo'' to mean the lower bound of interval2, ``B ; lo'' to mean the lower bound of interval1, etc. We use ``A lo --> B lo'' to ; mean the question: ``does A lo imply B lo (t), the negation of B lo (nil), or ; is it unknown (?)?'' (let ((b-lo-rel (access tau-interval interval1 :lo-rel)) (b-lo-d (access tau-interval interval1 :lo)) (b-hi-rel (access tau-interval interval1 :hi-rel)) (b-hi-d (access tau-interval interval1 :hi)) (a-lo-rel (access tau-interval interval2 :lo-rel)) (a-lo-k (access tau-interval interval2 :lo)) (a-hi-rel (access tau-interval interval2 :hi-rel)) (a-hi-k (access tau-interval interval2 :hi))) ; We first ask ``A hi --> B hi''. If that returns t, we ask ``A lo --> B lo''. ; If that returns t, we're done and return t (in a triple). If either test ; returns nil, we return nil (in a triple). But if ``A lo --> B lo'' returns ; ?, we have to try ``A hi --> B lo'' because it might report that B lo is ; false. (For example, A might be [15 <= ... <= 20] and B might be [30 <= ; ... ...]. (<= 15 x) does not establish that (<= 30 x), so we get ? on the ; ``A lo --> B lo'' comparison. But we get NIL on ``A hi --> B lo'' because ; (<= x 20) means (<= 30 x) is false.) Similar remarks apply if ``A hi --> B ; hi'' returns ?. (let ((temp (tau-interval-endpoint-implication ; (A hi --> B hi) t a-hi-rel a-hi-k t b-hi-rel b-hi-d))) (cond ((eq temp t) (let ((temp (tau-interval-endpoint-implication ; (A lo --> B lo) nil a-lo-rel a-lo-k nil b-lo-rel b-lo-d))) (cond ((eq temp t) (mv nil nil t)) ((eq temp nil) (mv nil nil nil)) (t (let ((temp (tau-interval-endpoint-implication ; (A hi --> B lo) t a-hi-rel a-hi-k nil b-lo-rel b-lo-d))) (cond ((eq temp t) (mv nil nil (er hard 'tau-interval-near-subsetp "``A hi --> B lo'' returned T after ``A lo --> B ~ lo'' returned unknown. This is not supposed to ~ happen! The only way an upper bound like ``A hi'' ~ can imply a lower bound like ``B lo'' is if the ~ lower bound is negative infinity (i.e., the bound ~ is vacuous), in which case ``A lo --> B lo'' would ~ have succeeded. A is this tau-interval ~x0 and B ~ is this one ~x1." interval2 interval1))) ((eq temp nil) (mv nil nil nil)) (t ; B-lo-d cannot be nil, and so must be a rational, since B lo is missing. ; (If the bound were nil, it would be trivially true.) So the resulting ; (k . token) is a well-formed inequality recognizer. (tau-interval-endpoint-to-sign-k-token nil b-lo-rel b-lo-d)))))))) ((eq temp nil) (mv nil nil nil)) (t (let ((temp (tau-interval-endpoint-implication ; (A lo --> B hi) nil a-lo-rel a-lo-k t b-hi-rel b-hi-d))) (cond ((eq temp t) (mv nil nil (er hard 'tau-interval-near-subsetp "``A lo --> B hi'' returned T after ``A hi --> B hi'' ~ returned unknown (?). This is not supposed to happen! ~ The only way a lower bound like ``A lo'' can imply an ~ upper bound like ``B hi'' is if the upper bound is ~ positive infinity (i.e., the bound is vacuous), in ~ which case ``A hi --> B hi'' would have succeeded. A ~ is this tau-interval ~x0 and B is this one ~x1." interval2 interval1))) ((eq temp nil) (mv nil nil nil)) (t (let ((temp (tau-interval-endpoint-implication ; (A lo --> B lo) nil a-lo-rel a-lo-k nil b-lo-rel b-lo-d))) (cond ((eq temp t) ; B-hi-d cannot be nil and so must be rational, as explain for b-lo-d above. (tau-interval-endpoint-to-sign-k-token t b-hi-rel b-hi-d)) ((eq temp nil) (mv nil nil nil)) (t (let ((temp ; (A hi --> B lo) (tau-interval-endpoint-implication t a-hi-rel a-hi-k nil b-lo-rel b-lo-d))) (cond ((eq temp t) (mv nil nil (er hard 'tau-interval-near-subsetp "``A hi --> B lo'' returned T after ``A lo ~ --> B lo'' returned unknown. This is not ~ supposed to happen! The only way an upper ~ bound like ``A hi'' can imply a lower bound ~ like ``B lo'' is if the lower bound is ~ negative infinity (i.e., the bound is ~ vacuous), in which case ``A lo --> B lo'' ~ would have succeeded. A is this ~ tau-interval ~x0 and B is this one ~x1." interval2 interval1))) (t ; We only asked ``A hi --> B lo'' to detect the possible hard error. The only ; expected answer is NIL or ?. And in both cases we fail because we already have ; B hi missing and now we've got B lo either missing or false. (mv nil nil nil))))))))))))))) (defun tau-near-subsetp (tau1 tau2 ens wrld) ; Think of tau1 and tau2 simply as sets of recognizers. If tau1 is a subset of ; tau2, we signal a contradiction. If more than one element of tau1 is not in ; tau2, we return nil; otherwise, we return the sign and recog of the missing ; element. We return (mv contradictionp sign recog), where the last two are ; nil when contradictionp is t. ; Some of the possibilities below don't really make sense -- we'd never be ; presented with a Conjunctive rule tau1 consisting of a :pos-evg. But we ; behave as per the spec above just to keep the interface clean. The main ; thing to remember is that we need only look for :neg-evgs, :pos-pairs, ; neg-pairs, and :interval bounds of tau1 in the corresponding components of ; tau2. ; Imagine computing the five component-wise near-subsetp answers. For each ; obtain a signed answer, sign1/temp1, sign2/temp2, etc, where if tempi = t, ; subset holds, if tempi=nil, two or more elements failed to be included, and ; otherwise tempi is the missing element. ; If any tempi is nil, we fail: (mv nil nil nil). If all are t, we signal ; contradiction: (mv t nil nil). If exactly one tempi is the missing element ; and other tempi are t, we won: (mv nil signi tempi) for the i that was ; witnessed. Otherwise, we fail. ; Efficiency considerations: By cascading the (null tempi) checks we avoid even ; computing tempi for those i beyond the first failure. Furthermore, if any ; tempi is nil, temp4 is nil. And we don't actually have to remember most ; signi because we know that for temp1 and temp3, the signs are positive (t), ; and for temp2 and temp4, the signs are negative (nil). (let* ((temp1 (pos-evg-near-subsetp (access tau tau1 :pos-evg) (access tau tau2 :pos-evg))) (temp2 (cond ((null temp1) nil) (t (neg-evgs-near-subsetp (access tau tau1 :neg-evgs) (access tau tau2 :neg-evgs) nil (access tau tau2 :pos-pairs) (access tau tau2 :neg-pairs) (access tau tau2 :interval) ens wrld)))) (temp3 (cond ((null temp2) nil) (t (tau-pairs-near-subsetp (access tau tau1 :pos-pairs) (access tau tau2 :pos-pairs) nil)))) (temp4 (cond ((null temp3) nil) (t (tau-pairs-near-subsetp (access tau tau1 :neg-pairs) (access tau tau2 :neg-pairs) nil))))) ; Recall that ``near-subset'' for intervals has a different interface: (mv-let (sign5 k5 temp5) (cond ((null temp4) (mv nil nil nil)) (t (tau-interval-near-subsetp (access tau tau1 :interval) (access tau tau2 :interval)))) ; Observe that if any tempi is nil, temp5 is nil. If temp5 is not nil and not ; t, then it is either :lessp-x-k or :lessp-k-x and sign5/(k5 . temp5) is the ; single missing, not contradicted, endpoint of the interval in tau2. (cond ((null temp5) (mv nil nil nil)) ; Remember: temp1 and temp3 have positive (t) sign and temp2 and temp4 have ; negative (nil) sign. Temp5 has sign5. (t (if (eq temp1 t) (if (eq temp2 t) (if (eq temp3 t) (if (eq temp4 t) (if (eq temp5 t) (mv t nil nil) (mv nil sign5 (cons k5 temp5))) (if (eq temp5 t) (mv nil nil temp4) (mv nil nil nil))) (if (and (eq temp4 t) (eq temp5 t)) (mv nil t temp3) (mv nil nil nil))) (if (and (eq temp3 t) (eq temp4 t) (eq temp5 t)) (mv nil nil temp2) (mv nil nil nil))) (if (and (eq temp2 t) (eq temp3 t) (eq temp4 t) (eq temp5 t)) (mv nil t temp1) (mv nil nil nil)))))))) ; Here is a test that yields a contradiction (all elements are found). If you ; change the 25 to a 24, that becomes the missing element. If you additionally ; change 23 to 22, it fails because there are two missing elements. ; (tau-near-subsetp ; (mktau nil (not (equal x 23)) (not (equal x 25)) (integerp x)) ; (mktau t (not (equal x 20)) (not (equal x 44)) (integerp x) (evenp x)) ; (ens state) (w state)) ; Here there is one missing element, (11 . :lessp-x-k). If you change the 7 to a 4 ; both parts are missing so we fail. If you add to the (<= 7 x) the additional ; restriction that (<= x 10), we get a contradiction. ; (tau-near-subsetp ; (mktau nil (<= 5 x) (< x 11)) ; (mktau t (<= 7 x)) ; (ens state) (w state)) ; ----------------------------------------------------------------- ; On the Tau Database ; We build a database from certain kinds of theorems, as shown below. In the ; forms shown, p, q, p1, p2, ..., are all understood to be tau recognizers (and ; thus may be (fn x), (EQUAL x 'evg), (< x 'rational), or (< 'rational x), ; and/or their negations. ; Simple: ; (implies (p v) (q v)) ; Conjunctive: ; (implies (and (p1 v) (p2 v) ...) (q v)) ; Signature [form 1] ; (implies (and (p1 x1) (p2 x2) ...) ; (q (f x1 x2 ...))) ; Signature [form 2] ; (implies (and (p1 x1) (p2 x2) ...) ; (q (mv-nth 'n (f x1 x2 ...)))) ; Big Switch ; (equal (fn . formals) body), where body is a big switch on some formal. ; MV-NTH Synonym: ; (equal (fn x y) (mv-nth x y)) ; The database will maintain the following properties on certain ; function symbols fn. ; property value ; tau-pair (i . fn), means fn is known to tau database ; tau-pair-saved (i . fn), means fn has in the past been known ; and will have this index whenever known ; pos-implicants tau: for every sign/recog in tau, it is known ; that (fn x) --> (sign/recog x) ; neg-implicants tau: for every sign/recog in tau, it is known ; that -(fn x) --> (sign/recog x) ; unevalable-but-known ((evg . bool) ...): means that ; (fn 'evg) = bool, even though fn has ; been detected to be unevaluable on 'evg. ; This alist is populated from Simple Rule form ; theorems proved about fn, i.e., ; (equal x 'evg) --> sign/(fn x). ; ; signature-rules-form-1 (sig-rule1 ... sig-rulen): see defrec for ; signature-rule ; signature-rules-form-2 ((sig-rule1,1 ... sig-rule1,n1) ; (sig-rule2,1 ... sig-rule2,n2) ; ...) ; where each slot corresponds to a slot in ; the MV returned by fn and each sig-rule is ; as described in defrec signature-rule ; big-switch see defrec for big-switch-rule; indicates ; that fn is a big switch function ; tau-bounders-form-1 (bc1 ... bcn), a list of tau bounder-correctness ; records, all of which have fn as their :subject-fn. ; tau-bounders-form-2 ((bc1,1 ... bc1,n1) ; (bc2,1 ... bc2,n2) ; ...) ; where each slot corresponds to a slot in the ; MV returned by fn and each bc is a bounder ; correctness record with :subject-fn fn. ; global-value we use the global-value property of the following ; symbol GLOBAL-VALUE ; tau-next-index n ; tau-conjunctive-rules (conj-rule1 ... conj-rulek), ; where each conj-rulei is ; just a tau ; tau-runes (rune1 rune2 ...) ; tau-mv-nth-synonyms (fn1 ... fnk) ; tau-lost-runes (rune1 rune2 ...) ; The first three proeprties are called ``tau recognizer properties'' and the ; other fn specific properties are called ``tau function properties.'' A ; recognizer symbol may have both kinds of properties, e.g., it may be ; unevalable-but-known and may be a big-switch. ; Closure of Simple Rules under Contraposition and Transitivity ; We now turn to implementing the closure operations. We close the simple ; rules under contraposition and transitivity. The function tau-put, given for ; example, (p x) --> (q x), will store it by adding q to the pos-implicants of ; p. Since q is just a signed tau recognizer and the pos-implicants of p is ; just a tau, tau-put uses add-to-tau1 to store q in the pos-implicants of p. ; The driver and main function of simple rule storage is tau-put*, which (i) ; uses tau-put to handle (p x) --> (q x), then (ii) uses tau-put to handle the ; contrapositive -(q x) --> -(p x), then (iii) calls itself recursively to ; propagate the addition of q to pos-implicants of p, and then (iv) calls ; itself recursively again to propagate the addition of -p to neg-implicants of ; q. ; This note concerns how tau-put handles p-->q, for arbitrary signed tau ; recognizers. It may update the tau database or not, depending on the ; formula. It may also signal that a contradiction has been detected. But ; things may get complicated when p and/or q is either an ; equality-with-constant or one of the two inequalities-with-constants, ; :lessp-x-k or :lessp-k-x. ; For example, x='A --> (p x) usually tells us nothing because (a) if we ever ; compute that a term has the tau {'A} then we will use evaluation to determine ; what other tau predicates it satisfies, and (b) most predicates are evalable ; and so (p 'A) either evaluates to T or to NIL. So tau-put uses evaluation to ; determine whether there is anything to store. If (p 'A) computes to T, ; tau-put stores nothing; in the case where it computes to NIL, tau-put signals ; a contradiction. But there is a third case: x='A --> (p x) could be provable ; but (p 'A) might not be evalable. In this third case, tau-put would add (p ; 'A) = T to the UNEVALABLE-BUT-KNOWN property of p. ; Tau-put's treatment of p-->q is asymmetric. Even though p-->q and its ; contrapositive, -q-->-p are logically equivalent, we may do something in ; response to the one and do nothing in respose to the other. For example, (p ; x) --> (q x) causes updates to p's pos-implicants and its contrapositive ; causes updates to q's neg-implicants. But there is only one thing we can ; learn from x='A --> (p x) and that is, at most, that p is unevalable but true ; on 'A. There is no point in attempting to store that fact for both x='A --> ; (p x) and its contrapositive -(p x) --> x/='A. So tau-put does not always ; act on every version of every formula. ; To make sure we cover all the cases, we build a table of possibilities and ; describe the appropriate action by tau-put. ; How we build the Tau-Put Table ; We consider all the formulas of the form p-->q, where p and q are any of the ; signed tau recognizers. For each choice of p and q, we determine what we can ; learn (i.e., store in the database) from the discovery that p-->q is a ; theorem. To make it easier to think about, we let p take on the following ; four forms (handling both signs explicitly later below). ; ; (p x) ; p represents some arbitray monadic Boolean function symbol ; (= x a) ; = represents EQUAL and a stands for an arbitrary quoted constant ; (< x i) ; i represents an arbitrary quoted rational constant ; (< i x) ; i represents an arbitrary quoted rational constant ; Similarly, we let q take on ; (q x) ; (= x b) ; (< x j) ; (< j x) ; where b and j are constants analogous to a and i above. For each choice of p ; and each choice of q, we will consider ; p --> q ; p --> -q ; -p --> q ; -p --> -q ; This methodology produces a ``contrapositive variant'' for each formula but ; assures that we cover all the cases. For example, formulas 1 and 4 of the ; table below are contrapositive variants of each other. In the table this is ; noted this way: ; 1 ( 4) (p x) --> (q x) ; 4 ( 1) -(p x) --> -(q x) ; because if we take the contrapositive of either and swap the roles of the ; symbols p and q we get the other formula. More interestingly, formulas 8 and ; 17 are contrapositive variants, where here we also swap the roles of a and b. ; 8 (17) -(p x) --> -(= x b) ;17 ( 8) (= x a) --> (q x) ; The contrapositive variants noted in the table below were mechanically ; identified, so you may assume it is a complete and accurate identification. ; Why is this notion relevant? First, contrapositives are, of course, ; equivalent formulas. Furthermore, p, q, a, b, and i, and j merely represent ; arbitrary examples of symbols or terms of certain syntactic classes. If our ; analysis calls for the same action on a formula as on its contrapositive, ; then there is no point in coding tau-put to recognize and act on both forms. ; So for example, our reaction to formula 8 above is to try to evaluate the ; function symbol bound to the pattern variable P on the constant bound to the ; pattern variable b and if it is unevalable we store the fact that the ; function is unevalable but true on that constant. This is the same thing ; we'd do on formula 17, where we'd name the function and constant q and a but ; then carry out the same actions. So we do not do anything on formulas of ; form 17 because we know we will deal with the contrapositive sometime. ; On the other hand, if our analysis calls for different actions on the ; different forms, then the fact that one is the contrapositive variant of ; another is irrelevant. (Note that 22, 23, 42, 43, 62 and 63 are their own ; contrapositive variants and are marked with ( *).) ; We indicate actions in the table as follows: ; code action update? ; S store the obvious add-to-tau1 into the database yes ; U may tell us some fn is unevalable but known maybe ; V violates Non-Constant Non-Equality no ; N no action (mv Nil nil wrld) no ; T contradiction (mv T nil wrld) no ; W wait for contrapositive no ; if ... describes one of two outcomes, either N or T no ; Apology: It may seem odd to use N to code for no action (e.g., the formula is ; trivially true) and T to code for contradiction, but those action codes make ; it easier to reconcile the table entries with the code because no action (N) ; is coded as (mv Nil ...) and contradiction (T) is coded as (mv T ...). ; We sometimes use an if in the table. For example, x=a --> x=b is either ; trivial (N) or contradictory (T), depending on whether constants a and b are ; equal. But sometimes we use arithmetic inequalities in these tests. For ; example, x=a --> x < j is trivial if a < j but contradictory otherwise. ; However, when coding these comparisons remember that while i and j are known ; to be rational constants, a and b are not and the proper interpretation of ; ``a (= x b) T [demo: let x=(cons a b)] ; means that the formula is unprovable in a consistent theory, as can be ; demonstrated by proving the negation of an instance: ; (thm (let ((x (cons a b))) ; (not (implies (not (equal x a)) (equal x b))))) ; Some other lines have notes like ``[pf 5]'' referencing proofs (enumerated by ; the line number of the formula in question) presented at the end of the ; table. In these demonstrations and proofs, A, B, I, and J are not ; instantiable! They're constants! ;----------------------------------------------------------------- ; Tau-Put Table of Actions for Storing Simple Tau Rules ;----------------------------------------------------------------- ; 1 ( 4) (p x) --> (q x) S -- add q to pos-imps p ; 2 ( 2) (p x) --> -(q x) S -- add -q to pos-imps p ; 3 ( 3) -(p x) --> (q x) S -- add q to neg-imps p ; 4 ( 1) -(p x) --> -(q x) S -- add -q to neg-imps p ; 5 (20) (p x) --> (= x b) V [pf 5] -- ignore ; 6 (18) (p x) --> -(= x b) U -- store if -(p b) unevalable ; 7 (19) -(p x) --> (= x b) V [cf 5] -- ignore ; 8 (17) -(p x) --> -(= x b) U -- store if (p b) unevalable ; 9 (36) (p x) --> (< x j) S -- add (< x j) to pos-imps p ; 10 (34) (p x) --> -(< x j) S -- add -(< x j) to pos-imps p ; 11 (35) -(p x) --> (< x j) S -- add (< x j) to neg-imps p ; 12 (33) -(p x) --> -(< x j) S -- add -(< x j) to neg-imps p ; 13 (52) (p x) --> (< j x) S -- add (< j x) to pos-imps p ; 14 (50) (p x) --> -(< j x) S -- add -(< j x) to pos-imps p ; 15 (51) -(p x) --> (< j x) S -- add (< j x) to neg-imps p ; 16 (49) -(p x) --> -(< j x) S -- add -(< j x) to neg-imps p ;----------------------------------------------------------------- ; 17 ( 8) (= x a) --> (q x) W -- wait for ( 8) ; 18 ( 6) (= x a) --> -(q x) W -- wait for ( 6) ; 19 ( 7) -(= x a) --> (q x) W -- wait for ( 7) ; 20 ( 5) -(= x a) --> -(q x) W -- wait for ( 5) ; 21 (24) (= x a) --> (= x b) if a=b, N else T ; 22 ( *) (= x a) --> -(= x b) if a=b, T else N ; 23 ( *) -(= x a) --> (= x b) T [demo: let x=(cons a b)] ; 24 (21) -(= x a) --> -(= x b) W -- wait for (21) ; 25 (40) (= x a) --> (< x j) if a -(< x j) if j<=a, N else T ; 27 (39) -(= x a) --> (< x j) T [demo: let x = (+ (max a j) 1)] ; 28 (37) -(= x a) --> -(< x j) T [demo: let x = (- (min a j) 1)] ; 29 (56) (= x a) --> (< j x) if j -(< j x) if a<=j, N else T ; 31 (55) -(= x a) --> (< j x) T [demo: let x = (- (min a j) 1)] ; 32 (53) -(= x a) --> -(< j x) T [demo: let x = (+ (max a j) 1)] ;----------------------------------------------------------------- ; 33 (12) (< x i) --> (q x) W -- wait for (12) ; 34 (10) (< x i) --> -(q x) W -- wait for (10) ; 35 (11) -(< x i) --> (q x) W -- wait for (11) ; 36 ( 9) -(< x i) --> -(q x) W -- wait for ( 9) ; 37 (28) (< x i) --> (= x b) W -- wait for (28) ; 38 (26) (< x i) --> -(= x b) W -- wait for (26) ; 39 (27) -(< x i) --> (= x b) W -- wait for (27) ; 40 (25) -(< x i) --> -(= x b) W -- wait for (25) ; 41 (44) (< x i) --> (< x j) if i<=j, N else T [pf 41] ; 42 ( *) (< x i) --> -(< x j) T [demo: let x = (- (min i j) 1)] ; 43 ( *) -(< x i) --> (< x j) T [demo: let x = (+ (max i j) 1)] ; 44 (41) -(< x i) --> -(< x j) W -- wait for (41) ; 45 (60) (< x i) --> (< j x) T [demo: let x = (- (min i j) 1)] ; 46 (58) (< x i) --> -(< j x) if i<=j, N else T [pf 46] ; 47 (59) -(< x i) --> (< j x) if j -(< j x) T [demo: let x = (+ (max i j) 1)] ;----------------------------------------------------------------- ; 49 (16) (< i x) --> (q x) W -- wait for (16) ; 50 (14) (< i x) --> -(q x) W -- wait for (14) ; 51 (15) -(< i x) --> (q x) W -- wait for (15) ; 52 (13) -(< i x) --> -(q x) W -- wait for (13) ; 53 (32) (< i x) --> (= x b) W -- wait for (32) ; 54 (30) (< i x) --> -(= x b) W -- wait for (30) ; 55 (31) -(< i x) --> (= x b) W -- wait for (31) ; 56 (29) -(< i x) --> -(= x b) W -- wait for (29) ; 57 (48) (< i x) --> (< x j) W -- wait for (48) ; 58 (46) (< i x) --> -(< x j) W -- wait for (46) ; 59 (47) -(< i x) --> (< x j) W -- wait for (47) ; 60 (45) -(< i x) --> -(< x j) W -- wait for (45) ; 61 (64) (< i x) --> (< j x) if j<=i, N else T [pf 61] ; 62 ( *) (< i x) --> -(< j x) T [demo: let x = (+ (max i j) 1)] ; 63 ( *) -(< i x) --> (< j x) T [demo: let x = (- (min i j) 1)] ; 64 (61) -(< i x) --> -(< j x) W -- wait for (61) ;----------------------------------------------------------------- ; Proofs cited above: ; First, ; (include-book "arithmetic-5/top" :dir :system) ; (tau-status :system nil) ; Proof 5: We prove that if formula 5, (p x) --> (= x b), is a theorem ; then p is either a constant function or an equality with a constant. ; We assume p and b are constrained as in formula 5 and prove ; [forall x1 : (p x1) = nil] or [forall x2 : (p x2) = (equal x2 (b))]. ; (encapsulate ((p (x) t) ; (b () t)) ; (local (defun p (x) (declare (ignore x)) nil)) ; (local (defun b () nil)) ; (defthm booleanp-p (booleanp (p x)) :rule-classes :type-prescription) ; (defthm formula-5 (implies (p x) (equal x (b))) ; :rule-classes nil)) ; (defstub x1 () t) ; (defstub x2 () t) ; (thm (or (equal (p (x1)) nil) ; (equal (p (x2)) (equal (x2) (b)))) ; :hints (("Goal" :use ((:instance formula-5 (x (x1))) ; (:instance formula-5 (x (x2))))))) ; (ubt! 3) ; Proof 41: We prove that 41 is a theorem iff i<=j is a theorem. ; Case: thm 41 |= i<=j: ; (thm (implies (let ((x (+ j (/ (- i j) 2)))) ; (implies (< x i) (< x j))) ; (<= i j))) ; Case: i<=j |= thm 41: ; (thm (implies (<= i j) ; (implies (< x i) (< x j)))) ; Proof 46: We prove that 46 is a theorem iff i<=j is a theorem. ; Case: thm 46 |= i<=j: ; (thm (implies (let ((x (+ i (/ (- j i) 2)))) ; (implies (< x i) (not (< j x)))) ; (<= i j))) ; Case: i<=j |= thm 46: ; (thm (implies (<= i j) ; (implies (< x i) (not (< j x))))) ; Proof 47: We prove that 47 is a theorem iff j (= x b) if a=b, N else T ; 22 ( *) (= x a) --> -(= x b) if a=b, T else N ; 23 ( *) -(= x a) --> (= x b) T [demo: let x=(cons a b)] ; 24 (21) -(= x a) --> -(= x b) W -- wait for (21) ; We get to these lines if both p and q are signed equalities -- conditions we ; check by observing that (cdr p-recog) and (cdr q-recog) are both nil. Lines ; 21 and 22 would have us test whether the two constant arguments of the ; equalities are equal, and either do nothing or signal a contradiction. Line ; 23 would have us signal a contradiction. And line 24 would have us do ; nothing. To determine which action to take, we have to test the signs and ; the constants. But just doing nothing in all four cases is sound and faster. ; On the other hand, the situation will probably never arise since the user is ; not likely to prove any formula of these forms so the efficiency is of little ; concern. Really, how likely is it that we'll see theorems like (implies ; (equal x 23) (equal x 27)) or (implies (equal x 23) (equal x 23)) proved as ; rules? ; Is the danger of bugs in our code worth the extra robustness of testing for ; explicit contradictions? ; After some consideration of this issue, we decided to code the table ; precisely, if only to document the analysis and make the case analysis ; uniform. ; Now, regarding the order in which we enumerate the cases. The table suggests ; that we look first for recognizer function applications, then equalities, ; then the two inequalities. But recall the concrete representation of tau ; recognizers as shown in tau-like-term and consider programming the necessary ; tests. Efficiency suggests we test for equality first, then the two ; inequalities, and handle function applications as the final case. The basic ; decoding of a concrete tau recognizer, recog, is as follows: ; Tests in order tried term represented by recog evg var name ; (eq (cdr recog) nil) `(EQUAL X ',(car recog)) a or b ; (eq (cdr recog) :lessp-x-k) `(< X ',(car recog)) i or j ; (eq (cdr recog) :lessp-k-x) `(< ',(car recog) X) i or j ; otherwise `(,(cdr recog) X) p or q ; Note that as we try the tests we bind variables a, b, i, and j to the ; corresponding evgs and we bind p and q to the corresponding function symbols ; to make our code match the names used in our table. ; Note: while our table shows ``terms'' like (= x a) the corresponding term is ; `(EQUAL x ',a). That is the variable a in our code corresponds to the evg of ; quoted constant denoted by a in our table. (defun tau-put (p-sign p-recog q-sign q-recog ens wrld) ; Each of p-recog and q-recog is a concrete tau recognizer with the ; corresponding sign. ; Think of the two signs and two recognizers as representing signed recognizers ; p and q. This function is called on every simple tau rule, p --> q. It will ; also be called on the contrapositive. We implement the actions listed in the ; Tau-Put Table and we reproduce lines from that table to document what we're ; doing. We test the cases on p and then on q in the order tried above, thus ; first considering (EQUAL x ...), then (< x ...), then (< ... x), and finally ; (fn x). ; We return (mv contradictionp changedp wrld') and this is a No-Change Loser on ; wrld. ; The actions recorded in the table and their implementations are: ; S store stuff? (mv contradictionp changedp wrld') -- as appropriate ; U unevalable? (mv contradictionp changedp wrld') -- as appropriate ; V violation (mv nil nil wrld) ; N no action (mv nil nil wrld) ; T signal contradiction (mv t nil wrld) ; W wait -- ignore (mv nil nil wrld) ; T contradiction (mv t nil wrld) (let ((p-discriminator (cdr p-recog))) (cond ((eq p-discriminator nil) (let ((a (car p-recog)) ; p-recog represents (EQUAL x 'a) (q-discriminator (cdr q-recog))) (cond ((eq q-discriminator nil) (let ((b (car q-recog))) ; 21 (24) (= x a) --> (= x b) if a=b, N else T ; 22 ( *) (= x a) --> -(= x b) if a=b, T else N ; 23 ( *) -(= x a) --> (= x b) T [demo: let x=(cons a b)] ; 24 (21) -(= x a) --> -(= x b) W -- wait for (21) (if p-sign (if q-sign (mv (if (equal a b) nil t) nil wrld) (mv (if (equal a b) t nil) nil wrld)) (if q-sign (mv t nil wrld) (mv nil nil wrld))))) ((eq q-discriminator :lessp-x-k) (let ((j (car q-recog))) ; 25 (40) (= x a) --> (< x j) if a -(< x j) if j<=a, N else T ; 27 (39) -(= x a) --> (< x j) T [demo: let x = (+ (max a j) 1)] ; 28 (37) -(= x a) --> -(< x j) T [demo: let x = (- (min a j) 1)] (if p-sign (if q-sign (mv (if (< (fix a) j) nil t) nil wrld) (mv (if (<= j (fix a)) nil t) nil wrld)) (mv t nil wrld)))) ((eq q-discriminator :lessp-k-x) (let ((j (car q-recog))) ; 29 (56) (= x a) --> (< j x) if j -(< j x) if a<=j, N else T ; 31 (55) -(= x a) --> (< j x) T [demo: let x = (- (min a j) 1)] ; 32 (53) -(= x a) --> -(< j x) T [demo: let x = (+ (max a j) 1)] (if p-sign (if q-sign (mv (if (< j (fix a)) nil t) nil wrld) (mv (if (<= (fix a) j) nil t) nil wrld)) (mv t nil wrld)))) (t ; (let ((q q-discriminator)) ; 17 ( 8) (= x a) --> (q x) W -- wait for ( 8) ; 18 ( 6) (= x a) --> -(q x) W -- wait for ( 6) ; 19 ( 7) -(= x a) --> (q x) W -- wait for ( 7) ; 20 ( 5) -(= x a) --> -(q x) W -- wait for ( 5) (mv nil nil wrld) ;) )))) ((eq p-discriminator :lessp-x-k) (let ((i (car p-recog)) ; p-recog represents (< x i) (q-discriminator (cdr q-recog))) (cond ((eq q-discriminator nil) ; (let ((b (car q-recog))) ; 37 (28) (< x i) --> (= x b) W -- wait for (28) ; 38 (26) (< x i) --> -(= x b) W -- wait for (26) ; 39 (27) -(< x i) --> (= x b) W -- wait for (27) ; 40 (25) -(< x i) --> -(= x b) W -- wait for (25) (mv nil nil wrld) ;) ) ((eq q-discriminator :lessp-x-k) (let ((j (car q-recog))) ; 41 (44) (< x i) --> (< x j) if i<=j, N else T [pf 41] ; 42 ( *) (< x i) --> -(< x j) T [demo: let x = (- (min i j) 1)] ; 43 ( *) -(< x i) --> (< x j) T [demo: let x = (+ (max i j) 1)] ; 44 (41) -(< x i) --> -(< x j) W -- wait for (41) (if p-sign (if q-sign (mv (if (<= i j) nil t) nil wrld) (mv t nil wrld)) (if q-sign (mv t nil wrld) (mv nil nil wrld))))) ((eq q-discriminator :lessp-k-x) (let ((j (car q-recog))) ; 45 (60) (< x i) --> (< j x) T [demo: let x = (- (min i j) 1)] ; 46 (58) (< x i) --> -(< j x) if i<=j, N else T [pf 46] ; 47 (59) -(< x i) --> (< j x) if j -(< j x) T [demo: let x = (+ (max i j) 1)] (if p-sign (if q-sign (mv t nil wrld) (mv (if (<= i j) nil t) nil wrld)) (if q-sign (mv (if (< j i) nil t) nil wrld) (mv t nil wrld))))) (t ; (let ((q q-discriminator)) ; 33 (12) (< x i) --> (q x) W -- wait for (12) ; 34 (10) (< x i) --> -(q x) W -- wait for (10) ; 35 (11) -(< x i) --> (q x) W -- wait for (11) ; 36 ( 9) -(< x i) --> -(q x) W -- wait for ( 9) (mv nil nil wrld) ;) )))) ((eq p-discriminator :lessp-k-x) (let ((i (car p-recog)) ; p-recog represents (< i x) (q-discriminator (cdr q-recog))) (cond ((eq q-discriminator nil) ; (let ((b (car q-recog))) ; 53 (32) (< i x) --> (= x b) W -- wait for (32) ; 54 (30) (< i x) --> -(= x b) W -- wait for (30) ; 55 (31) -(< i x) --> (= x b) W -- wait for (31) ; 56 (29) -(< i x) --> -(= x b) W -- wait for (29) (mv nil nil wrld) ;) ) ((eq q-discriminator :lessp-x-k) ; (let ((j (car q-recog))) ; 57 (48) (< i x) --> (< x j) W -- wait for (48) ; 58 (46) (< i x) --> -(< x j) W -- wait for (46) ; 59 (47) -(< i x) --> (< x j) W -- wait for (47) ; 60 (45) -(< i x) --> -(< x j) W -- wait for (45) (mv nil nil wrld) ;) ) ((eq q-discriminator :lessp-k-x) (let ((j (car q-recog))) ; 61 (64) (< i x) --> (< j x) if j<=i, N else T [pf 61] ; 62 ( *) (< i x) --> -(< j x) T [demo: let x = (+ (max i j) 1)] ; 63 ( *) -(< i x) --> (< j x) T [demo: let x = (- (min i j) 1)] ; 64 (61) -(< i x) --> -(< j x) W -- wait for (61) (if p-sign (if q-sign (mv (if (<= j i) nil t) nil wrld) (mv t nil wrld)) (if q-sign (mv t nil wrld) (mv nil nil wrld))))) (t ; (let ((q q-discriminator)) ; 49 (16) (< i x) --> (q x) W -- wait for (16) ; 50 (14) (< i x) --> -(q x) W -- wait for (14) ; 51 (15) -(< i x) --> (q x) W -- wait for (15) ; 52 (13) -(< i x) --> -(q x) W -- wait for (13) (mv nil nil wrld) ;) )))) (t (let ((p p-discriminator) ; p-recog represents (p x) (q-discriminator (cdr q-recog))) (cond ((eq q-discriminator nil) (let ((b (car q-recog))) ; 5 (20) (p x) --> (= x b) V [pf 5] -- ignore ; 6 (18) (p x) --> -(= x b) U -- store if -(p b) unevalable ; 7 (19) -(p x) --> (= x b) V [cf 5] -- ignore ; 8 (17) -(p x) --> -(= x b) U -- store if (p b) unevalable (if p-sign (if q-sign (mv nil nil wrld) ; Recall: q-recog here is (b), a singleton list containing b. Formula 6 is ; equivalent to x=b --> -(p x). The code below for handling line 6 could be ; easily combined with the code for handling line 8. But that would break the ; correspondence between lines of the table and if-then-else clauses here. (let ((val (ev-fncall-w-tau-recog p q-recog ens wrld))) (cond ((eq val :UNEVALABLE) (mv nil t (putprop p 'unevalable-but-known (cons (cons b nil) ; records that (p 'b) = nil (getprop p 'unevalable-but-known nil 'current-acl2-world wrld)) wrld))) ((eq val nil) (mv nil nil wrld)) (t (mv t nil wrld))))) (if q-sign (mv nil nil wrld) (let ((val (ev-fncall-w-tau-recog p q-recog ens wrld))) (cond ((eq val :UNEVALABLE) (mv nil t (putprop p 'unevalable-but-known (cons (cons b t) ; records that (p 'b) = t (getprop p 'unevalable-but-known nil 'current-acl2-world wrld)) wrld))) ((eq val nil) (mv t nil wrld)) (t (mv nil nil wrld)))))))) (t ; Else, q is an inequality or function application and we do the ; same thing on all of them. ; 9 (36) (p x) --> (< x j) S -- add (< x j) to pos-imps p ; 10 (34) (p x) --> -(< x j) S -- add -(< x j) to pos-imps p ; 11 (35) -(p x) --> (< x j) S -- add (< x j) to neg-imps p ; 12 (33) -(p x) --> -(< x j) S -- add -(< x j) to neg-imps p ; 13 (52) (p x) --> (< j x) S -- add (< j x) to pos-imps p ; 14 (50) (p x) --> -(< j x) S -- add -(< j x) to pos-imps p ; 15 (51) -(p x) --> (< j x) S -- add (< j x) to neg-imps p ; 16 (49) -(p x) --> -(< j x) S -- add -(< j x) to neg-imps P ; 1 ( 4) (p x) --> (q x) S -- add q to pos-imps p ; 2 ( 2) (p x) --> -(q x) S -- add -q to pos-imps p ; 3 ( 3) -(p x) --> (q x) S -- add q to neg-imps p ; 4 ( 1) -(p x) --> -(q x) S -- add -q to neg-imps p (let* ((old-tau (tau-simple-implicants p-sign p-discriminator wrld)) (new-tau (add-to-tau1 q-sign q-recog old-tau ens wrld))) (cond ((eq new-tau *tau-contradiction*) (mv t nil wrld)) ((equal old-tau new-tau) (mv nil nil wrld)) (t (mv nil t (putprop p-discriminator (if p-sign 'pos-implicants 'neg-implicants) new-tau wrld)))))))))))) (mutual-recursion (defun tau-put* (p-sign p-recog q-sign q-recog ens wrld) ; Let p be p-sign/p-recog and q be q-sign/q-recog. We store q as an implicant ; of p, then store the contrapositive version, and then close both additions ; under transitivity. More precisely, we first do two simple stores: q as an ; implicant of p, followed by NOT/p as an implicant of NOT/q. Then, for every ; element, r, of the implicants of q, we store r as an implicant of p. Then, ; for every element, r, of the implicants of NOT/p, we store r as an implicant ; of NOT/q. We return (mv contradictionp changedp wrld'). Wrld' is always ; either wrld unchanged (changedp=nil) or wrld extended appropriately. ; Note: It is possible for p-->q NOT to change the world but for -q --> -p to ; change the world. For example, (x=2) --> (evenp x) does nothing, but -(evenp ; x) --> (x /= 2) adds the inequality to the implicants of not/evenp. ; (Remember, we are storing a user-supplied theorem to that effect.) (mv-let (contradictionp changedp1 wrld) (tau-put p-sign p-recog q-sign q-recog ens wrld) (cond (contradictionp (mv t nil wrld)) (t (mv-let (contradictionp changedp2 wrld) (tau-put (not q-sign) q-recog (not p-sign) p-recog ens wrld) (cond (contradictionp (mv t (or changedp1 changedp2) wrld)) ((and (not changedp1) (not changedp2)) (mv nil nil wrld)) ; At this point we know something changed, so we pursue the implicants. But it ; could be that we can avoid one implicant chase or the other due to the ; no-change flags. But every exit from this function below will indicate that ; wrld has changed because it changed above. (t (let ((q-discriminator (cdr q-recog))) (mv-let (contradictionp changedp wrld) (if (and changedp1 q-discriminator (not (eq q-discriminator :lessp-k-x)) (not (eq q-discriminator :lessp-x-k))) ; q is a function call and we added q to the implicants of p. So we have to ; add all the implicants of q to the implicants of p. However, if adding q to ; the implicants of p didn't really change p (and provided the database was ; already closed), then we don't have to do anything. Also, if q-recog is not ; a tau-pair but is a singleton evg list, we don't chase it's implicants. (tau-put*-tau p-sign p-recog (tau-simple-implicants q-sign q-discriminator wrld) ens wrld) (mv nil t wrld)) (declare (ignore changedp)) ; always t! (let ((p-discriminator (cdr p-recog))) (cond (contradictionp (mv t t wrld)) ((and changedp2 p-discriminator (not (eq p-discriminator :lessp-k-x)) (not (eq p-discriminator :lessp-x-k))) ; This is the symmetric case, which we do only if we really changed the ; implicants of q and p is function call. (tau-put*-tau (not q-sign) q-recog (tau-simple-implicants (not p-sign) p-discriminator wrld) ens wrld)) (t (mv nil t wrld))))))))))))) (defun tau-put*-tau (p-sign p-recog tau ens wrld) ; We map over every r-sign/r-recog in tau and do ; (tau-put* p-sign p-recog r-sign r-recog ...). ; Technically we return (mv contradictionp changedp wrld'). But actually the ; returned changedp flag is always t and we don't bother keeping track of that ; because we don't call this function except when the input wrld has changed. ; While abstractly tau is a set of recognizers that we might map over, ; concretely it is a tau with five quite different components: pos-evg, ; neg-evgs, pos-pairs, neg-pairs, and an interval. It is clear how to map over ; the two -pairs. But is there any point in mapping over the others? Yes! ; For example, consider pos-evg. Adding that to p-sign/p-recog could have ; wonderful effects. More likely, adding each of the neg-evgs could populate ; unevalable-but-known fields. Thus, we really do map over every part of tau. (mv-let (contradictionp changedp wrld) (if (access tau tau :pos-evg) (tau-put* p-sign p-recog t (access tau tau :pos-evg) ens wrld) (mv nil t wrld)) (declare (ignore changedp)) ; always t (cond (contradictionp (mv t t wrld)) (t (mv-let (contradictionp changedp wrld) (tau-put*-recogs p-sign p-recog nil (access tau tau :neg-evgs) ens wrld) (declare (ignore changedp)) ; always t (cond (contradictionp (mv t t wrld)) (t (mv-let (contradictionp changedp wrld) (tau-put*-recogs p-sign p-recog t (access tau tau :pos-pairs) ens wrld) (declare (ignore changedp)) (cond (contradictionp (mv t t wrld)) (t (mv-let (contradictionp changedp wrld) (tau-put*-recogs p-sign p-recog nil (access tau tau :neg-pairs) ens wrld) (declare (ignore changedp)) (cond (contradictionp (mv t t wrld)) (t (tau-put*-interval-endpoints p-sign p-recog (access tau tau :interval) ens wrld)))))))))))))) (defun tau-put*-interval-endpoints (p-sign p-recog r-interval ens wrld) ; We store the implication from p-sign/p-recog to the endpoints of r-interval ; and return (mv contradictionp changedp wrld), except changedp is always t ; because we know the world changed before we called this function. (cond ((null r-interval) (mv nil t wrld)) (t (mv-let (lo-sign lo-recog) (if (access tau-interval r-interval :lo) (mv-let (sign k token) (tau-interval-endpoint-to-sign-k-token nil ; upper-boundp (access tau-interval r-interval :lo-rel) (access tau-interval r-interval :lo)) (mv sign (cons k token))) (mv nil nil)) (mv-let (hi-sign hi-recog) (if (access tau-interval r-interval :hi) (mv-let (sign k token) (tau-interval-endpoint-to-sign-k-token t ; upper-boundp (access tau-interval r-interval :hi-rel) (access tau-interval r-interval :hi)) (mv sign (cons k token))) (mv nil nil)) (if lo-recog (mv-let (contradictionp changedp wrld) (tau-put* p-sign p-recog lo-sign lo-recog ens wrld) (declare (ignore changedp)) (cond (contradictionp (mv t t wrld)) (t (if hi-recog (tau-put* p-sign p-recog hi-sign hi-recog ens wrld) (mv nil t wrld))))) (if hi-recog (tau-put* p-sign p-recog hi-sign hi-recog ens wrld) (mv nil t wrld)))))))) (defun tau-put*-recogs (p-sign p-recog r-sign r-recogs ens wrld) ; In our usage of this function r-recogs is always either a list of tau-pairs ; (namely, :pos-pairs or :neg-pairs) or a list of singleton evg lists ; (:neg-evgs). But in any case, each element of r-recogs is the atom ; of a recognizer. The sign of all the atoms in r-recogs is r-sign. ; For every r-sign/r-recog in r-sign/r-recogs (slight abuse of notation), we ; (tau-put* p-sign p-recog r-sign r-recog ...). ; We return (mv contradictionp changedp wrld), but in fact changedp is always ; t as explained in tau-put*-tau. (cond ((endp r-recogs) (mv nil t wrld)) (t (mv-let (contradictionp changedp wrld) (tau-put* p-sign p-recog r-sign (car r-recogs) ens wrld) (declare (ignore changedp)) (cond (contradictionp (mv t t wrld)) (t (tau-put*-recogs p-sign p-recog r-sign (cdr r-recogs) ens wrld))))))) ) ; On Closing the Database under Conjunctive Rules ; At one point we considered closing the database under the conjunctive rules, ; in so far as we could. We knew that we had to keep conjunctive rules around ; anyway and use them at query time because at query time we are given a list ; of knowns and must consider what the conjunctive rules tell us about that ; particular combination of recognizers. But it seemed like possibly a good ; idea to also use the rules on the database itself. But that is very ; complicated. Suppose we have a db closed under everything (including ; conjunctive rules). Then we add p-->q. This will touch the implicants of p ; (to add q and all the things q implies), -q (to add -p and all the things -p ; implies), and for every r in the implicants of q, -r (to add -p), and for ; every s in the implicants of -p, -s (to add q), and so on. Every one of ; those implicant lists has to be closed again under conjunctive rules. So we ; would need to track all the recs we change, not just whether we changed ; anything. ; Furthermore, if a conjunctive rule is added, we have to close every implicant ; list in the world again. ; Finally, how common will it be for the user to have told us that (a & b) --> ; c, and then told us that p --> a and p --> b? Certainly it will happen, ; e.g., if p is added after the primitive types a, b, and c, have been defined. ; It turns out that this happens a lot. For example, in the Rockwell benchmark ; used to test early versions of this (in which all :type-prescription and ; :forward-chaining rules were interpreted as :tau-system rules) we mapped over ; all the tau recognizers and asked how often does a conjunctive rule fire on ; the implicants (positive and negative). The answer was that of 11,940 ; implicants stored in the database, 4,640 of them could be extended with ; conjunctive rules. ; ----------------------------------------------------------------- ; On Converting Theorems in the World to Tau Rules ; We populate the database by processing certain theorems. However, we do ; this both when an event is first processed (in install-event) and also when ; the tau database is regenerated (regenerate-tau-database). We call this ; ``visiting'' the event; obviously, there is a first visit. That is, ; sometimes when we visit an event we've already processed it once. ; Recall also that theorems are added to the tau database either because they ; are explicitly labeled :tau-system rules or because we are in automatic mode ; and the theorems are of the appropriate form. This is also done when we ; visit an event, rather than in the core code for each event. This is a ; deliberate design decision to bring all the tau database updates into one ; place so we can implement regenerate-tau-database more robustly. ; The following code deals with recognizing the shapes of theorems that can be ; represented as tau rules and adding those rules to the database. We ; ultimately define the function for visiting an event (tuple) and adding its ; rules. ; Tau Boolean rules tell us that a function symbol is a monadic Boolean and ; thus should be tracked by the tau system. These rules cause the function ; symbol to get a tau-pair and cause its pos- and neg-implicants to be ; appropriately set. However, when in automatic mode, tau also automatically ; recognize monadic Boolean functions introduced by defun (or ; mutual-recursion). For that reason, we first define the function that visits ; a function symbol after its introduction and sets up its tau properties. ; We'll then use that function to add Boolean tau rules. (defconst *non-tau-monadic-boolean-functions* '(NOT DEBUGGER-ENABLEDP)) #+:non-standard-analysis (defun classicalp (fn wrld) ; WARNING: This function is expected to return t for fn = :?, in support of ; measures (:? v1 ... vk), since classicalp is called by ; get-non-classical-fns-from-list in support of get-non-classical-fns-aux. (getprop fn 'classicalp ; We guarantee a 'classicalp property of nil for all non-classical ; functions. We make no claims about the existence of a 'classicalp ; property for classical functions; in fact, as of Version_2.5 our ; intention is to put no 'classicalp property for classical functions. t 'current-acl2-world wrld)) ;; RAG - This function tests whether a list of names is made up purely ;; of classical function names (i.e., not descended from the ;; non-standard function symbols) #+:non-standard-analysis (defun classical-fn-list-p (names wrld) (cond ((null names) t) ((not (classicalp (car names) wrld)) nil) (t (classical-fn-list-p (cdr names) wrld)))) (defun monadic-boolean-fnp (fn ens wrld) ; Fn is known to be a function symbol. We return (mv t ttree) if fn is a ; monadic Boolean function and (mv nil nil) otherwise. Ttree is from the proof ; that fn is Boolean. (cond ((and ; We exclude all non-classical functions from consideration by tau. It is not clear ; that this is necessary but it's a safe thing to do until we've thought more about it. #+:non-standard-analysis (classicalp fn wrld) (equal (arity fn wrld) 1) (member-eq (symbol-class fn wrld) '(:ideal :common-lisp-compliant)) (not (member-eq fn *non-tau-monadic-boolean-functions*))) (mv-let (ts ttree) (type-set (fcons-term* fn '(V)) nil nil nil ens wrld nil nil nil) ; The type-set check below intentionally rejects the constant T and constant ; NIL functions. (cond ((ts= ts *ts-boolean*) (mv t ttree)) (t (mv nil nil))))) (t (mv nil nil)))) (defun putprop-if-different (sym prop val wrld) ; Wrld must be the ACL2 logial world, not some user-defined world. We store ; val as the property prop of sym unless it is already stored there. (if (equal (getprop sym prop *acl2-property-unbound* 'current-acl2-world wrld) val) wrld (putprop sym prop val wrld))) (defun tau-visit-function-introduction (fn wrld) ; This function makes fn almost virgin wrt to the tau system. Those functions ; in *primitive-monadic-booleans* as explained below. For all other functions ; fn, the only tau property of fn that is preserved is its tau-pair-saved ; property (if any), which is the tau-pair fn will be assigned if it is ever ; again classified as a tau recognizer. ; This function is called when we are regenerating the tau database by ; scanning the world and we encounter a function introduction, i.e., a FORMALs ; property. But the monadic primitives, like CONSP and SYMBOLP, are known to ; be Boolean and are so initialized when we begin regenerating the world, in an ; immitation of what happens during boot-strap. We do not want our encounter ; with their subsequent FORMALS binding to erase those tau properties. (cond ((member-eq fn *primitive-monadic-booleans*) wrld) (t (putprop-if-different fn 'TAU-PAIR *acl2-property-unbound* (putprop-if-different fn 'TAU-POS-IMPLICANTS *acl2-property-unbound* (putprop-if-different fn 'TAU-NEG-IMPLICANTS *acl2-property-unbound* (putprop-if-different fn 'UNEVALABLE-BUT-KNOWN *acl2-property-unbound* (putprop-if-different fn 'SIGNATURE-RULES-FORM-1 *acl2-property-unbound* (putprop-if-different fn 'SIGNATURE-RULES-FORM-2 *acl2-property-unbound* (putprop-if-different fn 'BIG-SWITCH *acl2-property-unbound* wrld)))))))))) (defun tau-visit-function-introductions (fns wrld) (cond ((endp fns) wrld) (t (tau-visit-function-introductions (cdr fns) (tau-visit-function-introduction (car fns) wrld))))) (defun putprop-tau-pair (fn wrld) ; Assign and store a ``new'' tau-pair to fn, updating the global value of ; tau-next-index as necessary. We return the updated world. ; However, if fn has ever had a tau-pair, we re-assign the same pair to it. ; This is an attempt to minimize the differences between regenerations of tau ; databases under different enabled structures. (let ((old-pair (getprop fn 'tau-pair-saved nil 'current-acl2-world wrld))) (if old-pair (putprop fn 'tau-pair old-pair wrld) (let* ((nexti (global-val 'tau-next-index wrld)) (new-pair (cons nexti fn))) (putprop fn 'tau-pair new-pair (putprop fn 'tau-pair-saved new-pair (global-set 'tau-next-index (+ 1 nexti) wrld))))))) (defun initialize-tau-pred (fn wrld) ; Fn is known to be a monadic Boolean function symbol. We initialize the tau ; properties of fn as a monadic Boolean, i.e., give it a tau-pair and set up ; its pos- and neg-implicants. It is assumed that all of fn's tau properties ; have previously been cleared. We considered adding the appropriate :domain ; for the intervals associated with the arithmetic primitive monadic Booleans ; (cf. *primitive-monadic-booleans*) upon which this is called. For example, ; when fn is INTEGERP we could add an unrestricted interval over that :domain. ; But we don't see any point because the :domain of the interval is irrelevant ; for anything except local bounds adjustments. When the pos-implicants of ; INTEGERP are added to some tau, the addition of the tau-pair for INTEGERP ; will trigger the setting of the :domain in that new tau; the setting of the ; :domain in the :pos-implicants is ignored. (let* ((wrld1 (putprop-tau-pair fn wrld)) (tau-pair (getprop fn 'tau-pair nil 'current-acl2-world wrld1)) (wrld2 (putprop fn 'pos-implicants (make tau :pos-evg nil :neg-evgs nil :pos-pairs (list tau-pair) :neg-pairs nil :interval nil) wrld1)) (wrld3 (putprop fn 'neg-implicants (make tau :pos-evg nil :neg-evgs nil :pos-pairs nil :neg-pairs (list tau-pair) :interval nil) wrld2))) wrld3)) (defun initialize-tau-preds (fns wrld) (cond ((endp fns) wrld) (t (initialize-tau-preds (cdr fns) (initialize-tau-pred (car fns) wrld))))) (defun tau-boolean-formp (hyps concl wrld) ; We return t or nil to indicate whether (IMPLIES (AND . hyps) concl) is of the ; form (booleanp (f v)), where f is a function symbol and v is a variable ; symbol. Hyps is a list of translated terms and concl is a translated term ; (so it suffices to check merely that f and v are symbols). (declare (ignore wrld)) (and (null hyps) (case-match concl (('BOOLEANP (f v)) (and (symbolp f) (symbolp v))) (& nil)))) (defun tau-eval-formp (hyps concl wrld) (and (null hyps) (mv-let (sign atm) (strip-not concl) (declare (ignore sign)) (and (not (variablep atm)) (not (fquotep atm)) (symbolp (ffn-symb atm)) (getprop (ffn-symb atm) 'tau-pair nil 'current-acl2-world wrld) (quotep (fargn atm 1)))))) (defun set-tau-runes (flg val wrld) ; This function updates the global-value of tau-runes, adding val to its ; current value. Flg should be 'list or nil indicating whether val is a list ; of runes or a single rune. ; Note: The reason we need union-equal and add-to-set-equal, even though we ; never visit the same rule twice, is that some rules split into many (hyps ; . concl) pairs and each pair has the same rune. For example, if a function ; foo has a :type-prescription rule that says the result is a symbol other than ; T or NIL, it turns into the conjunction of (symbolp v), (not (equal v 'T)), ; (not (equal v 'NIL)) and each is added with the same rune. (let ((runes0 (global-val 'tau-runes wrld))) (global-set 'tau-runes (cond ((eq flg 'list) (union-equal val runes0)) (t (add-to-set-equal val runes0))) wrld))) (defun add-tau-boolean-rule (rune hyps concl wrld) ; To add a new tau Boolean rule, in which hyps is nil and concl is (BOOLEANP ; (fn v)), we make f a tau recognizer by giving it a (possibly new) tau pair ; and initializing its pos- and neg-implicants. We also add rune to tau-runes. ; However, we must first check that fn is not already known to be a tau ; predicate so that we don't re-initialize its tau properties and wipe out ; existing ones. Sol Swords constructed a script in which proving that NATP ; was BOOLEANP inadventently wiped out the bootstrap properties of NATP because ; we failed to detect that we already knew NATP was BOOLEANP. (declare (ignore hyps)) (let ((fn (ffn-symb (fargn concl 1)))) (cond ((getprop fn 'tau-pair nil 'current-acl2-world wrld) ; We still add rune to the global-value of tau-runes even though the rune ; doesn't otherwise change our world. The reason is simply that we think the ; user may expect to see it there if he or she proves a tau rule. (set-tau-runes nil rune wrld)) (t (initialize-tau-pred fn (set-tau-runes nil rune wrld)))))) (defun add-tau-eval-rule (rune hyps concl wrld) (declare (ignore hyps)) (mv-let (sign atm) (strip-not concl) (let ((fn (ffn-symb atm)) (evg (cadr (fargn atm 1)))) (putprop fn 'unevalable-but-known (cons (cons evg (if sign nil t)) (getprop fn 'unevalable-but-known nil 'current-acl2-world wrld)) (set-tau-runes nil rune wrld))))) ; On Tau-Like Terms ; We need to recognize terms that are suitable applications of signed tau ; recognizers. We call these ``tau-like'' terms. Technically, a term is ; ``tau-like'' if it is either (pred e) or (NOT (pred e)) where pred is a tau ; recognizer. Recall that to be a tau recognizer, pred must be a function ; symbol with a tau-pair or be one of the built-in variants of EQUAL or <. If ; a term is tau-like, then the ``subject'' of that tau-like term is e. ; For later work we need to identify when a list of terms are all tau-like (and ; perhaps about a common subject or about various subjects but all of which are ; variable symbols). Our code is simpler if we elaborate the tau-like check ; with certain criteria on the subject. ; criteria meaning ; :VARIOUS-ANY subjects may vary and may be any term ; :SAME-ANY subjects must all be the same but may be any term ; :VARIOUS-VAR subjects may vary but must all be variable symbols ; :SAME-VAR subjects must all be the same variable symbol ; term subjects must be term ; The way we enforce a criterion across a list of terms is to change certain ; of the keyword criteria into term criteria as we go. For example, to check ; that a list of terms satisfy :VARIOUS-ANY we check that criterion for each ; term, but to check that a list of terms satisfies :SAME-ANY or :SAME-VAR we ; shift the criterion from the input one to whatever subject we find in the ; first term we check. (defun tau-like-subject-criterion (criterion x) ; Criterion is one of the criteria above and x is a term that is the subject of ; a tau-like term. We return nil if x does not meet criterion; else we return ; the next criterion to use in checking subsequent allegedly tau-like terms with ; this same criterion. Thus, this function is both a predicate indicating ; whether x meets the criterion, and a function used to select the next ; criterion when mapping across a list of terms. (case criterion (:VARIOUS-ANY :VARIOUS-ANY) (:SAME-ANY x) (:VARIOUS-VAR (if (variablep x) :VARIOUS-VAR nil)) (:SAME-VAR (if (variablep x) x nil)) (otherwise (if (equal criterion x) criterion nil)))) (defun tau-like-term (term criterion wrld) ; If term is tau-like we return (mv sign recog e criterion'), else we return all ; nils. If recog is non-nil, then it is the internal representation of a tau recognizer: ; tau recognizer concrete notes ; form representation ; (fn x) (index . fn) fn is a symbol with given tau pair ; (equiv x 'evg) (evg . nil) equiv is EQUAL, EQ, EQL, or = ; (equiv 'evg x) ; (< x 'k) (k . :lessp-x-k) k is a rational ; (> 'k x) ; (< 'k x) (k . :lessp-k-x) k is a rational ; (> x 'k) ; To be ``tau-like'' term must be a possibly negated term of one of the forms ; above The returned sign is T if term is positive; sign is nil for negated ; terms. The returned e is the subject of the term and meets the input ; criterion. Criterion' is the NEXT criterion to use when mapping across a ; list of terms to determine if they are all tau-like in the given sense. ; Warning: This function could treat (IFF x NIL) like (NOT x). It could treat ; (IFF x T) or even (IFF x 123) like (NOT (NOT x)). It could do lots of ; rewriting to comprehend tau-like meaning in various forms of terms. But it ; doesn't. Furthermore, if you change this function to give special treatment ; to IFF, be sure to reconsider tau-like-propositionp and ; expand-tau-like-proposition where also consider looking into IFF and EQUAL. ; Keep this function in sync with tau-big-switch-var (let ((equiv-fns '(EQUAL EQ EQL =)) (inequality-fns '(< >))) (mv-let (sign atm) (if (and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'NOT)) (mv nil (fargn term 1)) (mv t term)) (case-match atm ((fn e) (cond ((symbolp fn) (let ((tau-pair (getprop fn 'tau-pair nil 'current-acl2-world wrld))) (cond (tau-pair (let ((next-criterion (tau-like-subject-criterion criterion e))) (cond (next-criterion (mv sign tau-pair e next-criterion)) (t (mv nil nil nil nil))))) (t (mv nil nil nil nil))))) (t (mv nil nil nil nil)))) ((g e ('quote . singleton-evg)) ; This matches both the equal-to-constant and arithmetic inequality cases. We have ; to decide which, if either, case we're in. Below, we look for the commuted case. (cond ((member-eq g equiv-fns) (let ((next-criterion (tau-like-subject-criterion criterion e))) (cond (next-criterion (mv sign singleton-evg e next-criterion)) (t (mv nil nil nil nil))))) ((member-eq g inequality-fns) (let ((next-criterion (tau-like-subject-criterion criterion e)) (k (car singleton-evg))) (cond ((and next-criterion (rationalp k)) (mv sign (if (eq g '<) (cons k :lessp-x-k) (cons k :lessp-k-x)) e next-criterion)) (t (mv nil nil nil nil))))) (t (mv nil nil nil nil)))) ((g ('quote . singleton-evg) e) (cond ((member-eq g equiv-fns) (let ((next-criterion (tau-like-subject-criterion criterion e))) (cond (next-criterion (mv sign singleton-evg e next-criterion)) (t (mv nil nil nil nil))))) ((member-eq g inequality-fns) (let ((next-criterion (tau-like-subject-criterion criterion e)) (k (car singleton-evg))) (cond ((and next-criterion (rationalp k)) (mv sign (if (eq g '<) (cons k :lessp-k-x) (cons k :lessp-x-k)) e next-criterion)) (t (mv nil nil nil nil))))) (t (mv nil nil nil nil)))) (& (mv nil nil nil nil)))))) (defun tau-like-term-listp (lst criterion wrld) ; A list of terms is ``tau-like'' if every term in it is tau-like, and the ; subject of each term meets a certain criterion. If lst is tau-like in the ; given sense, we return a non-nil answer; else we return nil. In the ; successful case, the non-nil answer is the common subject of all the terms, ; if the initial criterion is :SAME-ANY, :SAME-VAR, or itself the designated ; subject term. Otherwise, the non-nil result is just the criterion checked. ; Since the list might be empty, the resulting non-nil answer could be any of ; the legal criterion. (cond ((endp lst) criterion) (t (mv-let (sign recog e next-criterion) (tau-like-term (car lst) criterion wrld) (declare (ignore sign e)) (cond ((null recog) nil) (t (tau-like-term-listp (cdr lst) next-criterion wrld))))))) ; Recognizing Simple and Conjunctive Rules ; Simple rules are actually a special case of conjunctive rules. We define ; the two acceptors first and then the two adders. (defun tau-conjunctive-formp (hyps concl wrld) ; Hyps and concl are the corresponding components of the pairs generated by ; unprettyify on some theorem: hyps is a list of terms and concl is a term. We ; return t or nil to indicate whether (implies (and . hyps) concl) is suitable ; as a tau Conjunctive rule. It must be of the ; Subtype Form: ; (implies (and (tau-like1 v) (tau-like2 v) ...) ; (tau-likek v)) ; where each tau-likei is a recognizer about a common variable symbol v. ; Warning: Given the way this function is defined, (hyps . concl) can be BOTH a ; Conjunctive rule and a Simple Rule! If (hyps . concl) meets the criteria for ; a Conjunctive rule, then it is actually stored as a Simple rule if hyps is of ; length 1. Do not change this functionality lightly. (let ((hyp-subject (and hyps (tau-like-term-listp hyps :same-var wrld)))) (cond (hyp-subject ; Since hyps is non-empty, if hyp-subject is non-nil it is the common, ; variablep subject of all the hyps. We check that the conclusion is a ; tau-like term with that same subject. (mv-let (sign recog e criterion) (tau-like-term concl hyp-subject wrld) (declare (ignore sign e criterion)) (if recog t nil))) (t nil)))) (defun tau-simple-formp (hyps concl wrld) ; Return t or nil to indicate whether (implies (and . hyps) concl) is a Simple ; Subtype rule. This means it has the form (implies (tau-like1 v) (tau-like2 ; v)) for two signed tau recognizers and a common variable symbol v. (and (tau-conjunctive-formp hyps concl wrld) (null (cdr hyps)))) (defun add-tau-simple-rule (rune hyps concl ens wrld) ; To add a simple subtype rule we extend the implicants in the database, ; maintaining closure. We know that hyps and concl satisfy tau-simple-formp, ; so we can ignore the subject of the recogs. (We know the subject of the hyp ; and the concl is some common variable.) We use the criterion :VARIOUS-ANY in ; the tau-like-term calls below because it doesn't do any checks. (mv-let (p-sign p-recog e criterion) (tau-like-term (car hyps) :various-any wrld) (declare (ignore e criterion)) (mv-let (q-sign q-recog e criterion) (tau-like-term concl :various-any wrld) (declare (ignore e criterion)) (mv-let (contradictionp changedp wrld) (tau-put* p-sign p-recog q-sign q-recog ens wrld) (declare (ignore changedp)) (cond (contradictionp (er hard 'add-tau-simple-rule "It was thought impossible for the addition ~ of a simple subtype tau rule (derived from a ~ theorem) to yield a contradiction but it has ~ happened with the rule named ~x0, derived ~ from ~x1." rune (reprettyify hyps concl wrld))) (t (set-tau-runes nil rune wrld))))))) (defun convert-tau-like-terms-to-tau (complete-flg terms ens wrld) ; We convert a list of tau-like terms, terms, to a tau. If the complete-flg is ; t, the resulting tau is ``complete'' in that it includes the implicants. Otherwise, ; it just includes the tau representations of the terms themselves. (cond ((endp terms) *tau-empty*) (t (mv-let (sign recog e criterion) (tau-like-term (car terms) :various-any wrld) (declare (ignore e criterion)) (let ((tau1 (convert-tau-like-terms-to-tau complete-flg (cdr terms) ens wrld))) (if complete-flg (add-to-tau sign recog tau1 ens wrld) (add-to-tau1 sign recog tau1 ens wrld))))))) (defun add-tau-conjunctive-rule (rune hyps concl ens wrld) ; A conjunctive rule (implies (and (p1 x) ... (pk x)) (q x)) is stored as {p1 ; ... pk -q}. The idea is that if we detect that we know all but one of the ; elements of such a set, we can assert the remaining element. This is ; actually stored as a tau so that it is easier to search for each ; ``kind'' of recog in it. But we are not interested in the semantics of such ; tau, i.e., M[tau] is irrelevant for conjunctive rules; we're just exploiting ; the data structure. (let ((tau (convert-tau-like-terms-to-tau nil (append hyps (list (case-match concl (('NOT p) p) (& `(NOT ,concl))))) ; Note that I move the negated conclusion into the hyps to produce the list ; that is converted to a tau. I avoid dumb-negate-lit simply because it does ; more than just add or strip a NOT. ens wrld))) ; If we get a contradiction it is because hyps --> concl is a tau-detectable ; tautology and we just ignore it. (if (equal tau *tau-contradiction*) wrld (set-tau-runes nil rune (global-set 'tau-conjunctive-rules (cons tau (global-val 'tau-conjunctive-rules wrld)) wrld))))) ; We now turn to signature rules. ; On Loops in Relieving Dependent Hyps in Tau Signature Rules ; We allow signature rules with non-tau-like hypotheses. These hyps are saved ; in the :dependent-hyps field of signature-rule records. If the tau-like hyps ; in :input-tau-list are relieved then ok-to-fire-signature-rulep next attempts ; to relieve each dependent one by instantiating it and applying type-set ; (which also invokes a little linear arithmetic). At one point ; ok-to-fire-signature-rulep also recursively applied tau-term, and asked ; whether the resulting tau contained nil among its :neg-evgs (meaning the hyp ; is non-nil). But the recursive use of tau-term caused infinite backchaining. ; The loop was first detected in the regression: ; cd books/workshops/2006/cowles-gamboa-euclid/Euclid/ ; (defpkg "GAUSS-INT" ; (union-eq *acl2-exports* ; *common-lisp-symbols-from-main-lisp-package*)) ; (ld "ed5aa.lisp" :ld-pre-eval-print t) ; The command above will fail with a stack overflow at in the [GAUSS-INT::] event ; SQ-ABS-<-SQ-ABS-* ; However, here is a simpler example that loops: ; (encapsulate ((q (x) t)(p (x) t) (mum (x) t) (bar (x) t)) ; (local (defun q (x) (equal x t))) ; (local (defun p (x) (equal x t))) ; (local (defun mum (x) (cons x x))) ; (local (defun bar (x) (cons x x))) ; (defthm p-sig ; (implies (consp x) (equal (p x) nil))) ; (defthm q-sig ; (implies (consp x) (equal (q x) nil))) ; (defthm bar-sig ; (implies (q (mum x)) (consp (bar x))) ; :rule-classes :tau-system) ; (defthm mum-sig ; (implies (p (bar x)) (consp (mum x))) ; :rule-classes :tau-system)) ; ; (thm (consp (mum a))) ; Note that both bar-sig and mum-sig contain non-tau hyps, i.e., dependent ; hyps. In trying to compute the signature of (mum a) using mum-sig we compute ; the tau of (bar a), but in computing the tau of (bar a) with bar-sig we ; compute the tau of (mum a). The p-sig and q-sig rules are present so that we ; can bury (bar x) and (mum x) in the dependent hyps and still dive down to ; them when computing the tau of the dependent hyps. ; To stop this loop we took Draconian action: we simply eliminated use of ; tau-term in ok-to-fire-signature-rulep. Perhaps there is a smarter way? (defun partition-signature-hyps-into-tau-alist-and-others (hyps alist others ens wrld) ; We partition hyps into two lists: the tau-like terms about variables and the ; others. To represent the first partition, we build an alist pairing each ; variable with its associated tau (except the tau is not closed under the ; database, it just contains the recognizers explicitly listed for that ; variable). The other hyps are merely assembled into a list in the same order ; as they appear in hyps. Hyps0 and concl0 are the components of the ; unprettyified theorem from which this rule is being constructed and are used ; only for error reporting. We return (mv contradictionp tau-alist others). ; Contradictionp is t when we find a contradiction among the hyps. For ; example, ; (implies (and (stringp x) (not (stringp x))) (true-listp (foo x y))) ; is a theorem but tells us absolutely nothing about the type of foo. ; If this function signals contradictionp, no signature rule should be ; built. (cond ((endp hyps) (mv nil alist others)) (t (mv-let (contradictionp alist others) (partition-signature-hyps-into-tau-alist-and-others (cdr hyps) alist others ens wrld) (cond (contradictionp (mv contradictionp nil nil)) (t (mv-let (sign recog v criterion) (tau-like-term (car hyps) :various-var wrld) (declare (ignore criterion)) (cond (recog (let ((old-tau (or (cdr (assoc-eq v alist)) *tau-empty*))) (let ((new-tau (add-to-tau1 sign recog old-tau ens wrld))) ; Note: We use add-to-tau1 because we are not interested in the implicants. (cond ((eq new-tau *tau-contradiction*) (mv t nil nil)) (t (mv nil (put-assoc-eq v new-tau alist) others)))))) (t (mv nil alist (cons (car hyps) others))))))))))) (defun tau-boolean-signature-formp (hyps concl) ; The tau system cannot handle disjunctions and is thus incapable of coding ; (or (equal e 'T) (equal e 'NIL)) as a signature. But this is a very special ; case: (BOOLEANP e), and many function have that as a type-prescription rule. ; So we recognize as form 1 signatures expressions of the form: ; (IF (EQUAL (fn v1 ... vn) 'T) ; T ; (EQUAL (fn v1 ... vn) 'NIL)) ; and variants. We recognize variants because the user might have written an ; OR which actually translates into the IF above with the true-branch replaced ; by a repetition of the test, even though the conversion of type-prescription ; rules to terms does short-cuts that and puts a T there. ; Warning: All variants MUST have the property that (fargn (fargn concl 1) 1) is ; the term (fn v1 ... vn). (cond ((null hyps) (let ((e (case-match concl ; Note this position: * (('IF ('EQUAL e ''T) ''T ('EQUAL e ''NIL)) e) (('IF ('EQUAL e ''T) ('EQUAL e ''T) ('EQUAL e ''NIL)) e) (('IF ('EQUAL e ''NIL) ''T ('EQUAL e ''T)) e) (('IF ('EQUAL e ''NIL) ('EQUAL e ''NIL) ('EQUAL e ''T)) e) ; Note this position: * (& nil)))) (and e (nvariablep e) (not (fquotep e)) (symbolp (ffn-symb e)) (symbol-listp (fargs e)) (no-duplicatesp-eq (fargs e))))) (t nil))) (defun tau-signature-formp (hyps concl wrld) ; We return 1 or 2 or nil to indicate whether (implies (and . hyps) concl) is ; suitable as a tau signature rule of the indicated form. To be of form 1, it ; must be of the ; Signature Form 1: ; (implies (and (tau-like1_1 v1) ... (tau-like1_k v1) ; ... ; (tau-liken_1 vn) ... (tau-liken_j vn) ; (dhyp_1 v1 ... vn) ... (dhyp_k v1 ... vn)) ; (tau-like (fn v1 ... vn))) ; That is, we get to make an arbitrary number of non-nil tau-like hypotheses ; about distinct variables v1, ..., vn, and we get to make an arbitrary number ; of non-nil non-tau-like hypotheses about any of the variables, and then ; assert a tau-like conclusion about (fn v1 ... vn). No free vars are allowed. ; We recognize (OR (EQUAL (fn v1 ... vn) T) (EQUAL (fn v1 ... vn) NIL)) as ; synonymous with (BOOLEANP (fn v1 ... vn)) and thus as a form 1 signature rule. ; To be of form 2 it must be of the form: ; Signature Form 2 ; (implies (and (tau-like1_1 v1) ... (tau-like1_k v1) ; ... ; (tau-liken_1 vn) ... (tau-liken_j vn) ; (dhyp_1 v1 ... vn) ... (dhyp_k v1 ... vn)) ; (tau-like (mv-nth 'i (fn v1 ... vn))) ; Note that the hypotheses are of the same form as in form 1. Note also that ; the hyps are thus unconstrained except for the free-var restriction: any list ; of hyps can be partitioned into those that are tau-like and those that are ; not! (cond ; We exclude all non-classical functions from consideration by tau. We could just check that ; the fn being signed and the dependent hyps are classical, since we know that all tau predicates ; are classical. However, it is simplest to check that every function in the formula is ; classical. #+:non-standard-analysis ((not (classical-fn-list-p (all-fnnames1 nil concl (all-fnnames1 t hyps nil)) wrld)) nil) ((member-equal *nil* hyps) ; We first confirm that hyps is not identically nil. While we handle this ; case correctly -- it would be a dependent hyp and would never be relieved -- ; it almost certainly indicates a misunderstanding on the part of the user. nil) ((tau-boolean-signature-formp hyps concl) 1) (t (mv-let (sign recog e criterion) (tau-like-term concl :same-any wrld) (declare (ignore sign criterion)) (cond ((or (null recog) (variablep e) (fquotep e)) nil) ; The conclusion is a tau-like term applied to something. We now check that ; the something is either (mv-nth 'i (fn x1 ... xn)) or (fn x1 ... xn), where ; fn is a function symbol and the xi are distinct variables and that there are ; no free vars among the hyps. ((or (eq (ffn-symb e) 'mv-nth) (member-eq (ffn-symb e) (global-val 'tau-mv-nth-synonyms wrld))) ; Notice that this simple check for mv-nth or a synonym precludes us having a ; useful signature rule about MV-NTH itself. It is hard to imagine a useful ; rule about (MV-NTH X1 X2)! (cond ((and (quotep (fargn e 1)) (natp (cadr (fargn e 1)))) (let ((e (fargn e 2))) (cond ((and (nvariablep e) (not (fquotep e)) (symbolp (ffn-symb e)) (symbol-listp (fargs e)) ; faster than arglistp (no-duplicatesp-eq (fargs e)) ; for a list of terms. (not (ffnnamep-lst (ffn-symb e) hyps)) (subsetp (all-vars1-lst hyps nil) (fargs e))) 2) (t nil)))) (t nil))) ((and (symbolp (ffn-symb e)) (nvariablep e) (not (fquotep e)) (symbolp (ffn-symb e)) (symbol-listp (fargs e)) ; faster than arglistp (no-duplicatesp-eq (fargs e)) ; for a list of terms. (not (ffnnamep-lst (ffn-symb e) hyps)) (subsetp (all-vars1-lst hyps nil) (fargs e))) 1) (t nil)))))) (defun replace-vars-by-bindings (vars alist) ; Given a list of vars and an alist mapping vars to objects, we return the ; result of replacing each var in the list by its image under the alist. (cond ((endp vars) nil) (t (cons (or (cdr (assoc-eq (car vars) alist)) *tau-empty*) (replace-vars-by-bindings (cdr vars) alist))))) (defun add-tau-signature-rule (rune form hyps concl ens wrld) ; Form is either 1 or 2 and indicates which form of signature rule we can ; construct from (implies (and . hyp) concl). We update the database ; appropriately. Look at the comment in tau-signature-formp for a description ; of the two forms. (let ((concl (if (tau-boolean-signature-formp hyps concl) `(BOOLEANP ,(fargn (fargn concl 1) 1)) concl))) (mv-let (sign recog e criterion) (tau-like-term concl :various-any wrld) (declare (ignore criterion)) (let* ((temp recog) (recog (if temp recog *tau-booleanp-pair*)) (sign (if temp sign t)) (e (if temp e (fargn concl 1)))) (let* ((fn (if (eql form 1) (ffn-symb e) (ffn-symb (fargn e 2)))) (i (if (eql form 1) nil (cadr (fargn e 1)))) (vars (fargs (if (eql form 1) e (fargn e 2))))) (mv-let (contradictionp alist others) (partition-signature-hyps-into-tau-alist-and-others hyps nil nil ens wrld) (cond (contradictionp ; The hyps are contradictory, so there is no useful signature rule to store. wrld) (t (let ((rule (make signature-rule :input-tau-list (replace-vars-by-bindings vars alist) :vars vars :dependent-hyps others :output-sign sign :output-recog recog))) ; It is easy to imagine that the same signature gets stored in two different ; theorems, as happens in the Rockwell work where types are mechanically ; generated and there is some redundancy. So we check. (cond ((eql form 1) (let ((sigs (getprop fn 'signature-rules-form-1 nil 'current-acl2-world wrld))) (if (member-equal rule sigs) wrld (set-tau-runes nil rune (putprop fn 'signature-rules-form-1 (cons rule sigs) wrld))))) (t (let ((sigs (getprop fn 'signature-rules-form-2 nil 'current-acl2-world wrld))) (if (member-equal rule (nth i sigs)) wrld (set-tau-runes nil rune (putprop fn 'signature-rules-form-2 (update-nth i (cons rule (nth i sigs)) sigs) wrld))))))))))))))) ; Now we turn our attention to recognizing big switch functions. ; We say a variable v is a ``switch'' (or ``flag'') in a term ; iff ; (a) the term is of the form (IF x y z), where x is a recognizer term, v is ; the only variable free in x, and v is a switch for both y and z, or ; (b) v is not free in the term. ; Note that any variable that does not occur freely in term is (pathologically) ; a switch for the term. But let's confine our attention to variables that ; occur freely in term. ; If v occurs freely in term and is a switch for term, then term must begin ; with an IF and the test must be a recognizer on v. Thus, at most one ; variable occuring freely in term is a switch for term. ; We say an unconditional equation defines a ``big switch function'' if the ; equation is of the form ; (equal (fn v1 ... vk) body) ; where fn is a function symbol, fn is not a tau recognizer, the vi are ; distinct variables, the free vars of body are a subset of the vi, and one of ; the vi is free in body and is the switch for body, and body does not call fn. ; The largest subterms of body that do not contain the switch var are called ; the ``leaves'' of the switch. ; Note that it is possible for a big switch equation to define a function fn ; about which we also have signatures. In this case, the tau mechanism will ; ignore the big switch definition. That is, the presence of a signature in ; tau overrides the possibility of expanding the function. (defun switch-varp (v term wrld) (case-match term (('IF x y z) (mv-let (sign recog e criterion) (tau-like-term x :various-any wrld) (declare (ignore sign criterion)) (cond ((and recog (eq v e) (switch-varp v y wrld) (switch-varp v z wrld)) t) (t (not (occur v term)))))) (& (not (occur v term))))) (defun tau-big-switch-equationp (term wrld) (case-match term (('EQUAL (fn . vars) ('IF test x y)) (let* ((test-vars (all-vars test)) (v (car test-vars)) (body-vars (all-vars1 y (all-vars1 x test-vars)))) (and ; We exclude all non-classical functions from consideration by tau. We could ; just check that the fn being signed and the dependent hyps are classical, ; since we know that all tau predicates are classical. However, it is simplest ; to check that every function in the formula is classical. #+:non-standard-analysis (classical-fn-list-p (all-fnnames1 nil term nil) wrld) (symbolp fn) (not (equal fn 'quote)) (not (getprop fn 'tau-pair nil 'current-acl2-world wrld)) (not (getprop fn 'big-switch nil 'current-acl2-world wrld)) (symbol-listp vars) (no-duplicatesp vars) test-vars (null (cdr test-vars)) (subsetp-eq body-vars vars) (mv-let (sign recog e criterion) (tau-like-term test :various-any wrld) (declare (ignore sign criterion)) (and recog (eq e v))) (not (ffnnamep fn (fargn term 2))) (switch-varp v x wrld) (switch-varp v y wrld)))) (& nil))) (defun tau-big-switch-var (term) ; Given that term is recognized by tau-big-switch-equationp, return the switch ; var. Note that this function recapitulates some of the checks in ; tau-like-term. Keep the two functions in sync. ; We know term = (EQUAL (fn . vars) (IF test x y)) and that test is recognized ; by tau-like-term. We return the subject of test. (mv-let (sign test) (strip-not (fargn (fargn term 2) 1)) (declare (ignore sign)) ; Test is now (recog v) or (equal a1 a2) or (< a1 a2). If we're in the recog ; case, v is the switch var; otherwise either a1 or a2 is a quoted evg and the ; other one is the switch var. (cond ((null (cdr (fargs test))) (fargn test 1)) ((quotep (fargn test 1)) (fargn test 2)) (t (fargn test 1))))) (defun tau-big-switch-formp (hyps concl wrld) (and (null hyps) (tau-big-switch-equationp concl wrld))) (defun add-tau-big-switch-rule (rune hyps concl wrld) ; We know that hyps is nil and concl satisfies the big switch equation ; criterion: (EQUAL (fn . formals) body). (declare (ignore hyps)) (let* ((fn (ffn-symb (fargn concl 1))) (formals (fargs (fargn concl 1))) (body (fargn concl 2)) (switch-var (tau-big-switch-var concl)) (switch-var-pos (- (length formals) (length (member-eq switch-var formals))))) (set-tau-runes nil rune (putprop fn 'big-switch (MAKE big-switch-rule :formals formals :switch-var switch-var :switch-var-pos switch-var-pos :body body) wrld)))) (defun tau-mv-nth-synonym-formp (hyps concl) (and (null hyps) (case-match concl (('EQUAL (fn x y) ('MV-NTH x y)) (and (not (flambdap fn)) (variablep x) (variablep y) (not (eq x y)))) (('EQUAL ('MV-NTH x y) (fn x y)) (and (not (flambdap fn)) (variablep x) (variablep y) (not (eq x y)))) (& nil)))) (defun add-tau-mv-nth-synonym-rule (rune hyps concl wrld) (declare (ignore hyps)) (let* ((fn (if (eq (ffn-symb (fargn concl 1)) 'MV-NTH) (ffn-symb (fargn concl 2)) (ffn-symb (fargn concl 1)))) (fns (global-val 'tau-mv-nth-synonyms wrld))) (cond ((member-eq fn fns) wrld) (t (set-tau-runes nil rune (global-set 'tau-mv-nth-synonyms (cons fn fns) wrld)))))) ; A bounder correctness theorem is parsed into the following ; record: (defrec bounder-correctness ((subject-fn . acceptable-domains) . (bounder-fn . bounder-args)) t) ; Here, subject-fn is the function symbol whose value is constrained to the ; interval computed by bounder-fn. Both are function symbols. ; Acceptable-domains codes the domain requirements on the arguments to ; subject-fn; it is a list of elements in 1:1 correspondence with the actuals ; of subject-fn: each element is a list of interval domain recognizers, ; indicating that the corresponding actual must lie in an interval over one of ; the listed domains or that no restriction is made. The last field of a ; bounder-correctness record is a list of numbers, in 1:1 correspondence with ; the arguments of bounder-fn, indicating which actual interval is to be passed ; to bounder-fn in each position. ; For example, here is a bounder correctness theorem for the function fn: ; (implies (and (tau-intervalp int1) ; (tau-intervalp int3) ; (or (equal (tau-interval-dom int1) 'integerp) ; (equal (tau-interval-dom int1) 'rationalp)) ; (equal (tau-interval-dom int3) 'integerp) ; (in-tau-intervalp x int1) ; (in-tau-intervalp z int3)) ; (and (tau-intervalp (bounder int3 int1)) ; (in-tau-intervalp (fn x y z) (bounder int3 int1)))) ; and here is the corresponding bounder-correctness record which ; represents this theorem: ; (make bounder-correctness ; :subject-fn 'fn ; :acceptable-domains '((INTEGERP RATIONALP) ; (INTEGERP RATIONALP ACL2-NUMBERP NIL) ; (INTEGERP)) ; :bounder-fn 'bounder ; :bounder-args '(2 0)) ; If tau-term sees a call of fn, it determines the tau of the arguments (or at ; least those with non-T acceptable-domains) and checks that each tau contains ; an interval with an acceptable domain. If so, it collects the intervals of ; the tau in the positions listed in :bounder-args (in the order listed) and ; applies bounder to that list of arguments to get an interval known to contain ; the fn term. (defun all-cars-nil (pairs) (cond ((endp pairs) t) (t (and (null (car (car pairs))) (all-cars-nil (cdr pairs)))))) (defun find-subject-bounder-link-term (pairs pairs0) ; Pairs is a list of (hyp . concl) pairs. To be sensible, every hyp must be ; nil; i.e., pairs is obtained by unprettyifying a conjunction and so is ; essentially a list of conjuncts except each is embedded in (nil . conjunct). ; Pairs0 is initially pairs. We find the first conjunct of the form ; (IN-TAU-INTERVALP subject-term bounder-term) such that (TAU-INTERVALP bounder-term) ; is also among the conjuncts. (Thus, we search pairs for the first pattern ; and when we find a candidate, we check the full list, pairs0, for the ; second.) We return either the IN-TAU-INTERVALP term we found or nil. (cond ((endp pairs) nil) ((and (null (car (car pairs))) (nvariablep (cdr (car pairs))) (eq (ffn-symb (cdr (car pairs))) 'in-tau-intervalp) (member-equal (cons nil (cons 'tau-intervalp (cddr (cdr (car pairs))))) pairs0)) (cdr (car pairs))) (t (find-subject-bounder-link-term (cdr pairs) pairs0)))) (defun tau-bounder-doms-extraction (term ivars ivar) (case-match term (('EQUAL ('TAU-INTERVAL-DOM v) ('QUOTE evg)) (cond ((and (if ivar (eq v ivar) (member-eq v ivars)) (member-eq evg '(INTEGERP RATIONALP ACL2-NUMBERP NIL))) (mv v (list evg))) (t (mv nil nil)))) (('IF ('EQUAL ('TAU-INTERVAL-DOM v) ('QUOTE evg)) ('EQUAL ('TAU-INTERVAL-DOM v) ('QUOTE evg)) else-term) (cond ((and (if ivar (eq v ivar) (member-eq v ivars)) (member-eq evg '(INTEGERP RATIONALP ACL2-NUMBERP NIL))) (mv-let (ivar doms) (tau-bounder-doms-extraction else-term ivars v) (cond (ivar (mv ivar (add-to-set-eq evg doms))) (t (mv nil nil))))) (t (mv nil nil)))) (& (mv nil nil)))) (defun tau-bounder-hyp-extraction (hyps svars ivars missing-ivars1 missing-ivars2 ivar-to-doms-alist ivar-to-svar-pos-alist svar-to-ivar-alist) (cond ((endp hyps) (cond ((and (null missing-ivars1) (null missing-ivars2)) ; If both lists of missing ivars are empty then we know that for each ivar, (a) ; we have an INTERVALP hyp, (b) we have an (IN-TAU-INTERVALP svar ivar) hyp, and ; (c) ivar is paired with a unique svar position in ivar-to-svar-pos-alist. (mv t ivar-to-doms-alist ivar-to-svar-pos-alist svar-to-ivar-alist)) (t (mv nil nil nil nil)))) ((or (variablep (car hyps)) (fquotep (car hyps))) (mv nil nil nil nil)) ((eq (ffn-symb (car hyps)) 'TAU-INTERVALP) (let ((ivar (fargn (car hyps) 1))) (cond ((member-eq ivar missing-ivars1) ; The hyp is (TAU-INTERVALP ivar), so we can ``check off'' ivar from the ; ones we're looking for. (tau-bounder-hyp-extraction (cdr hyps) svars ivars (remove1-eq ivar missing-ivars1) missing-ivars2 ivar-to-doms-alist ivar-to-svar-pos-alist svar-to-ivar-alist)) (t (mv nil nil nil nil))))) ((eq (ffn-symb (car hyps)) 'IN-TAU-INTERVALP) (let ((svar (fargn (car hyps) 1)) (ivar (fargn (car hyps) 2))) (cond ((and (member-eq svar svars) (member-eq ivar missing-ivars2) (not (assoc-eq svar svar-to-ivar-alist))) ; The hyp is (IN-TAU-INTERVALP svar ivar). Since we know that no such hyp for ivar ; has been seen before -- ivar is not among the missing in-tau-intervalp-vars -- we ; also know there is no correspondent for it in the ivar-to-svar-pos-alist. ; Since we know that svar is not already mapped to another ivar, we know this ; mapping will be unique. So we ``check off'' ivar and record the position of ; the svar it tracks and the correspondence between svar and ivar. (let ((n (- (len svars) (len (member-eq svar svars))))) (tau-bounder-hyp-extraction (cdr hyps) svars ivars missing-ivars1 (remove1-eq ivar missing-ivars2) ivar-to-doms-alist (cons (cons ivar n) ivar-to-svar-pos-alist) (cons (cons svar ivar) svar-to-ivar-alist)))) (t (mv nil nil nil nil))))) (t (mv-let (ivar doms) (tau-bounder-doms-extraction (car hyps) ivars nil) (cond ((and ivar (not (assoc-eq ivar ivar-to-doms-alist))) (tau-bounder-hyp-extraction (cdr hyps) svars ivars missing-ivars1 missing-ivars2 (cons (cons ivar doms) ivar-to-doms-alist) ivar-to-svar-pos-alist svar-to-ivar-alist)) (t (mv nil nil nil nil))))))) (defconst *all-interval-domains* '(INTEGERP RATIONALP ACL2-NUMBERP NIL)) (defun acceptable-domains-for-bounder (svars svar-to-ivar-alist ivar-to-doms-alist) (cond ((endp svars) nil) (t (let ((temp (assoc-eq (car svars) svar-to-ivar-alist))) (cons (cond ((null temp) *all-interval-domains*) (t (let* ((ivar (cdr temp)) (temp (assoc-eq ivar ivar-to-doms-alist))) (cond ((null temp) *all-interval-domains*) (t (cdr temp)))))) (acceptable-domains-for-bounder (cdr svars) svar-to-ivar-alist ivar-to-doms-alist)))))) (defun bounder-args (ivars ivar-to-svar-pos-alist) (cond ((endp ivars) nil) (t (cons (cdr (assoc-eq (car ivars) ivar-to-svar-pos-alist)) (bounder-args (cdr ivars) ivar-to-svar-pos-alist))))) (defun tau-bounder-formp (term wrld) ; This function, while a peer of the other tau form recognizers, e.g., ; tau-boolean-formp, tau-simple-formp, etc., is actually quite odd. Unlike ; those other functions, it takes the whole term, not (hyps . concl) pairs ; stripped out of the term. Also, unlike those other functions which truly ; just recognize the desired forms, this function returns some additional ; information gleaned while parsing the term. ; This function returns (mv form j bc) where form is nil, 1, or 2, where nil ; means term is NOT of the form of a bounder correctness rule, 1 means it is of ; form 1, and 2 means it is of form 2. If form is 1, then j is nil and bc is ; the corresponding bounder-correctness record. If form is 2, then j is the ; slot in the MV tuple returned by the :subject-fn of bc, which is the ; corresponding bounder-correctness record. ; To be of form 1, term must be: ; (implies (and (tau-intervalp i1) ; ... ; (tau-intervalp ik) ; (or (equal (tau-interval-dom i1) 'dom1,1) ; ... ; (equal (tau-interval-dom i1) 'dom1,n1)) ; ... ; (or (equal (tau-interval-dom ik) 'domk,1) ; ... ; (equal (tau-interval-dom ik) 'domk,nk)) ; (in-tau-intervalp x1 i1) ; ... ; (in-tau-intervalp xk ik)) ; (and (tau-intervalp (bounder-fn i1 ... ik)) ; (in-tau-intervalp (fn x1 ... xk y1 ...) ; (bounder-fn i1 ... ik)) ; ...)) ; except we don't care about the order in which the hyps, the conclusion, or ; their individual parts (including the variables presented as actuals to the ; calls of fn and bounder-fn) occur, and not every interval i has to have a ; domain restriction. ; Form 2 is just like form 1 above except the (fn x1 ... xk y1 ...) is ; (mv-nth 'j (fn x1 ... xk y1 ...)). (cond ((and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'implies)) (let* ((hyp-pairs (unprettyify (fargn term 1))) (concl-pairs (unprettyify (fargn term 2))) (link-term (find-subject-bounder-link-term concl-pairs concl-pairs))) ; Note: link-term is the (IN-TAU-INTERVALP x bounder) where the conclusion also ; contains (TAU-INTERVALP bounder). So next we do a few simple checks to see if it ; is plausible that this is a bounder rule of either form and then we split the ; two forms and check their pieces more carefully. (cond ((and link-term (all-cars-nil hyp-pairs) (nvariablep (fargn link-term 1)) (not (fquotep (fargn link-term 1))) (nvariablep (fargn link-term 2)) (not (fquotep (fargn link-term 2))) (symbolp (ffn-symb (fargn link-term 2))) (symbol-listp (fargs (fargn link-term 2))) (no-duplicatesp-eq (fargs (fargn link-term 2)))) ; So we know that the hyp-pairs is a list of conjuncts paired with nil (in the ; cars), and that we found a link-term in the conclusion and that it is of the ; form (IN-TAU-INTERVALP (g ...) (bounder-fn i1 ... ik)) where the i are distinct ; variable symbols. By looking at g we can decide if this is plausibly form 1 ; or form 2. (cond ((or (eq (ffn-symb (fargn link-term 1)) 'mv-nth) (member-eq (ffn-symb (fargn link-term 1)) (global-val 'tau-mv-nth-synonyms wrld))) (cond ((and (quotep (fargn (fargn link-term 1) 1)) (natp (cadr (fargn (fargn link-term 1) 1)))) ; We are in the plausibly Form 2 case. We know nothing about subject-term ; below but we know bounder-fn below is a function symbol and the ivars are ; distinct variable symbols. (let ((j (cadr (fargn (fargn link-term 1) 1))) (subject-term (fargn (fargn link-term 1) 2)) (bounder-fn (ffn-symb (fargn link-term 2))) (ivars (fargs (fargn link-term 2)))) (cond ((and (nvariablep subject-term) (not (fquotep subject-term)) (symbolp (ffn-symb subject-term)) (symbol-listp (fargs subject-term)) (no-duplicatesp-eq (fargs subject-term)) (not (intersectp-eq (fargs subject-term) ivars))) (let ((fn (ffn-symb subject-term)) (svars (fargs subject-term)) (hyps (strip-cdrs hyp-pairs))) ; Let subject and bounder be the terms (fn . svars) and (bounder-fn . ivars). ; We know that the conclusion contains (TAU-INTERVALP bounder) and also ; (IN-TAU-INTERVALP (MV-NTH 'j subject) bounder). Furthermore, we know that both ; fn and bounder-fn are function symbols, that they are applied to distinct ; variable symbols, svars and ivars, respectively, and that those variable ; symbols do not overlap. Finally, hyps is a list of hypothesis terms ; implicitly conjoined. ; Next, every hyp in hyps has to be of one of the following forms: (TAU-INTERVALP ; ivar), (IN-TAU-INTERVALP svar ivar), or the IF form of a disjunction of (EQUAL ; (TAU-INTERVAL-DOM ivar) 'dom). Furthermore, we MUST have an INTERVALP hyp and an ; IN-TAU-INTERVALP hyp for EVERY bounder-var, and a unique svar must be paired to ; each ivar by those IN-TAU-INTERVALP hyps. (mv-let (flg ivar-to-doms-alist ivar-to-svar-pos-alist svar-to-ivar-alist) (tau-bounder-hyp-extraction hyps svars ivars ivars ; initial missing (TAU-INTERVALP ivar) ivars ivars ; initial missing (IN-TAU-INTERVALP * ivar) ivars nil ; initial alist mapping ivars to domains nil ; initial alist mapping ivars to svar positions nil ; initial alist mapping svars to ivars ) (cond (flg (mv 2 ; Form 2 j ; mv-nth slot concerned (make bounder-correctness :subject-fn fn :acceptable-domains (acceptable-domains-for-bounder svars svar-to-ivar-alist ivar-to-doms-alist) :bounder-fn bounder-fn :bounder-args (bounder-args ivars ivar-to-svar-pos-alist)))) (t (mv nil nil nil)))))) (t (mv nil nil nil))))) (t (mv nil nil nil)))) (t ; We are in the plausibly Form 1 case. We know nothing about subject-term ; below but we know bounder-fn below is a function symbol and the ivars are ; distinct variable symbols. (let ((j nil) (subject-term (fargn link-term 1)) (bounder-fn (ffn-symb (fargn link-term 2))) (ivars (fargs (fargn link-term 2)))) (cond ((and (nvariablep subject-term) (not (fquotep subject-term)) (symbolp (ffn-symb subject-term)) (symbol-listp (fargs subject-term)) (no-duplicatesp-eq (fargs subject-term)) (not (intersectp-eq (fargs subject-term) ivars))) (let ((fn (ffn-symb subject-term)) (svars (fargs subject-term)) (hyps (strip-cdrs hyp-pairs))) ; Let subject and bounder be the terms (fn . svars) and (bounder-fn . ivars). ; We know that the conclusion contains (TAU-INTERVALP bounder) and also ; (IN-TAU-INTERVALP subject bounder). Furthermore, we know that both ; fn and bounder-fn are function symbols, that they are applied to distinct ; variable symbols, svars and ivars, respectively, and that those variable ; symbols do not overlap. Finally, hyps is a list of hypothesis terms ; implicitly conjoined. ; Next, every hyp in hyps has to be of one of the following forms: (TAU-INTERVALP ; ivar), (IN-TAU-INTERVALP svar ivar), or the IF form of a disjunction of (EQUAL ; (TAU-INTERVAL-DOM ivar) 'dom). Furthermore, we MUST have an INTERVALP hyp and an ; IN-TAU-INTERVALP hyp for EVERY bounder-var, and a unique svar must be paired to ; each ivar by those IN-TAU-INTERVALP hyps. (mv-let (flg ivar-to-doms-alist ivar-to-svar-pos-alist svar-to-ivar-alist) (tau-bounder-hyp-extraction hyps svars ivars ivars ; initial missing (TAU-INTERVALP ivar) ivars ivars ; initial missing (IN-TAU-INTERVALP * ivar) ivars nil ; initial alist mapping ivars to domains nil ; initial alist mapping ivars to svar positions nil ; initial alist mapping svars to ivars ) (cond (flg (mv 1 ; Form 1 j ; mv-nth slot concerned = nil (make bounder-correctness :subject-fn fn :acceptable-domains (acceptable-domains-for-bounder svars svar-to-ivar-alist ivar-to-doms-alist) :bounder-fn bounder-fn :bounder-args (bounder-args ivars ivar-to-svar-pos-alist)))) (t (mv nil nil nil)))))) (t (mv nil nil nil))))))) (t (mv nil nil nil))))) (t (mv nil nil nil)))) ; In the following code we develop the idea of adding a bounder-correctness ; record to a list of such records, except that we eliminate subsumed records. ; A bounder-correctness record bc1 is subsumed by another, bc2, if the ; subject-fn, bounder-fn, and bounder-args are all identical and the successive ; acceptable domains of bc1 are subsets of the corresponding domains of bc2. ; For example, bc1 might have :acceptable-domains of ((INTEGERP) (RATIONALP)) ; while bc2 might have :acceptable-domains of ((INTEGERP RATIONALP) (INTEGERP ; RATIONALP)). Given that everything else is the same, it is obvious that bc2 ; applies more often. ; The reason we need such a function is that when developing a book of bounder ; theorems we frequently encounter many lemmas (and also many corollaries of ; each bounder correctness theorem) that are less complete than the final ; bounder correctness theorem for a function. (defun pairwise-subsetp-eq (doms-lst1 doms-lst2) (cond ((endp doms-lst1) t) ((subsetp-eq (car doms-lst1) (car doms-lst2)) (pairwise-subsetp-eq (cdr doms-lst1) (cdr doms-lst2))) (t nil))) (defun bounder-subsumedp (bc1 bc2) ; Bc1 and bc2 are two bounder-correctness records. We determine whether bc1 is ; subsumed by bc2. The only way one bounder subsumes another is if the ; successive acceptable domains of bc1 are subsets of the corresponding ones of ; bc2. All other fields have to be the same. Since we assume that both bc1 ; and bc2 are about the same subject-fn, we delay that test to the end because ; it will most likely be true. We check the bounder-fn and bounder-arg fields ; first because they're fast checks. (and (eq (access bounder-correctness bc1 :bounder-fn) (access bounder-correctness bc2 :bounder-fn)) (equal (access bounder-correctness bc1 :bounder-args) (access bounder-correctness bc2 :bounder-args)) (pairwise-subsetp-eq (access bounder-correctness bc1 :acceptable-domains) (access bounder-correctness bc2 :acceptable-domains)) (eq (access bounder-correctness bc1 :subject-fn) (access bounder-correctness bc2 :subject-fn)))) (defun bounder-subsumedp-by-some (bc bounders) (cond ((endp bounders) nil) ((bounder-subsumedp bc (car bounders)) t) (t (bounder-subsumedp-by-some bc (cdr bounders))))) (defun delete-some-subsumed-bounders (bc bounders) (cond ((endp bounders) nil) ((bounder-subsumedp (car bounders) bc) (delete-some-subsumed-bounders bc (cdr bounders))) (t (cons (car bounders) (delete-some-subsumed-bounders bc (cdr bounders)))))) (defun add-bounder-to-bounders (bc bounders) (cond ((bounder-subsumedp-by-some bc bounders) bounders) (t (cons bc (delete-some-subsumed-bounders bc bounders))))) (defun add-tau-bounder-rule (rune form j bc wrld) ; Form is 1 or 2, j is the slot that a form 2 rule occupies, and bc is the ; bounder-correctness record representing a bounder correctness theorem. We ; store it under the 'tau-bounders-form-1 or -2 property of the subject-fn and ; add rune to the tau runes. ; Note that when we add a new bounder-correctness rule we may delete some old ; ones or not actually add anything (if the new rule is subsumed). So we ; actually ``add'' the rule to the appropriate list of bounders and then see if ; changed anything before modifying the world. (let ((subject-fn (access bounder-correctness bc :subject-fn))) (cond ((equal form 1) (let* ((bounders0 (getprop subject-fn 'tau-bounders-form-1 nil 'current-acl2-world wrld)) (bounders1 (add-bounder-to-bounders bc bounders0))) (if (equal bounders0 bounders1) wrld (set-tau-runes nil rune (putprop subject-fn 'tau-bounders-form-1 bounders1 wrld))))) (t (let* ((slots (getprop subject-fn 'tau-bounders-form-2 nil 'current-acl2-world wrld)) (bounders0 (nth j slots)) (bounders1 (add-bounder-to-bounders bc bounders0))) (if (equal bounders0 bounders1) wrld (set-tau-runes nil rune (putprop subject-fn 'tau-bounders-form-2 (update-nth j bounders1 slots) wrld)))))))) ; Now we define the functions for checking and adding tau rules. ; The following function strips FORCE and CASE-SPLIT off of the hyps so that ; they don't trick us into missing tau rules. (defun strip-force-and-case-split (lst) (cond ((endp lst) nil) (t (let* ((hyp (car lst)) (rest (strip-force-and-case-split (cdr lst)))) (case-match hyp (('force hyp) (cons hyp rest)) (('case-split hyp) (cons hyp rest)) (& (cons hyp rest))))))) (defun strip-force-and-case-split-in-hyps-of-pairs (pairs) (cond ((endp pairs) nil) (t (cons (cons (strip-force-and-case-split (car (car pairs))) (cdr (car pairs))) (strip-force-and-case-split-in-hyps-of-pairs (cdr pairs)))))) ; Note for future improvement: When we check whether a rule (of any class) is ; suitable as a tau-system rule, we call a number of functions to prepare the ; rule for the check. In particular, we call (in order) ; remove-guard-holders ; unprettyify ; strip-force-and-case-split-in-hyps-of-pairs ; split-on-conjoined-disjunctions-in-hyps-of-pairs ; to get a list of (hyps . concl) pairs that we then inspect for suitability ; under the tau constraints. This is done in three places. The places are ; shown below and the notation ``fn < gn'' should be read as ``fn is called by ; gn'': ; chk-acceptable-tau-rule < chk-acceptable-x-rules (on :tau-system rules only) ; add-tau-rule < ; tau-visit-defthm1 < tau-visit-defthm < TAU-VISIT-EVENT < [see below] ; tau-rules-from-type-prescriptions < ; tau-visit-defuns1 < tau-visit-defuns < tau-visit-defun < TAU-VISIT-EVENT ; < TAU-VISIT-EVENT ; Note that add-tau-rule and tau-rules-from-type-prescriptions are essentially ; only called from TAU-VISIT-EVENT, which is called by install-event (and ; during regenerate-tau-database, which is so rare we will ignore it). Recall ; that while one might expect add-tau-rule to be called by add-x-rule it is ; not: tau rules are added by the install-event. ; It is of some concern that the nest of preparatory functions (like ; strip-force-and-case-split-in-hyps-of-pairs and ; split-on-conjoined-disjunctions-in-hyps-of-pairs) causes too much consing. ; So there are two questions: (a) Can we prepare the hyps for the tau ; constraint checks without repeatedly copying them? (b) Do we prepare the ; hyps multiple times for the same rule? ; When a :tau-system rule is proposed, we prepare the hyps for each rule twice: ; once when we check that the rule is an acceptable tau-system rule and then ; again when we store it in install-event. We cannot avoid this unless we save ; the prepared (hyps . concl) pairs from before the proof and feed them to ; install-event. But this is quite a paradigm shift from the current and ; widely used interface with install-event so we chose not to do it. On the ; other hand, when a :rewrite or other class of rule is proposed, we prepare ; the hyps only once: when we try to add it (optionally) as a tau rule. ; Since the vast majority of rules are presumably not :tau-system rules, we're ; willing to live with the current redundancy for :tau-system rules. If we ; come to believe that the preparatory processing of rules for tau is costing ; too much, it is probably best to focus first on question (a) above: preparing ; more efficiently (or checking without preparing). ; A useful little test is to define foo, trace one of the preparatory functions, ; and then add a rule, either as a :tau-system rule or as a :rewrite rule. Note ; how many times we prepare the rule for checking. ; (defun foo (x) (mv x x)) ; (trace$ strip-force-and-case-split-in-hyps-of-pairs) ; (defthm sig (implies (force (natp x)) (natp (mv-nth 1 (foo x)))) :rule-classes :tau-system) ; (u) ; (defthm sig (implies (force (natp x)) (natp (mv-nth 1 (foo x))))) (defun acceptable-tau-rulep (pair wrld) (let ((hyps (car pair)) (concl (cdr pair))) (cond ((tau-boolean-formp hyps concl wrld) 'BOOLEAN) ((tau-eval-formp hyps concl wrld) 'EVAL) ((tau-simple-formp hyps concl wrld) 'SIMPLE) ((tau-conjunctive-formp hyps concl wrld) 'CONJUNCTIVE) (t (let ((n (tau-signature-formp hyps concl wrld))) (cond ((equal n 1) 'SIGNATURE-FORM-1) ((equal n 2) 'SIGNATURE-FORM-2) ((tau-big-switch-formp hyps concl wrld) 'BIG-SWITCH) ((tau-mv-nth-synonym-formp hyps concl) 'MV-NTH-SYNONYM) (t nil))))))) (defun acceptable-tau-rulesp (flg pairs wrld) ; Pairs was computed by unprettyify and consists of pairs of the form (hyps ; . concl). We check that every (flg = :all) or some (flg = :some) element of ; pairs may be coded as a tau rule of some kind. (cond ((endp pairs) (eq flg :all)) ((acceptable-tau-rulep (car pairs) wrld) (if (eq flg :all) (acceptable-tau-rulesp flg (cdr pairs) wrld) t)) ((eq flg :all) nil) (t (acceptable-tau-rulesp flg (cdr pairs) wrld)))) (defun acceptable-tau-rules (pairs wrld) ; Pairs was computed by unprettyify and consists of pairs of the form (hyps ; . concl). We collect every acceptable tau rule pair. (cond ((endp pairs) nil) ((acceptable-tau-rulep (car pairs) wrld) (cons (car pairs) (acceptable-tau-rules (cdr pairs) wrld))) (t (acceptable-tau-rules (cdr pairs) wrld)))) (defun cross-prod1 (a lst2) (cond ((endp lst2) nil) (t (cons (append a (car lst2)) (cross-prod1 a (cdr lst2)))))) (defun cross-prod (lst1 lst2) (cond ((endp lst1) nil) (t (append (cross-prod1 (car lst1) lst2) (cross-prod (cdr lst1) lst2))))) (defun cnf-dnf (sign term cnfp) ; This function converts sign/term into either CNF or DNF form. (Sign is t for ; positive, nil for negative.) However, it only recognizes variables, ; constants, NOT, and IFs that represent ANDs or ORs. It does not deal with ; IMPLIES or IFF (but it does recognize (IF x y T) as (OR (NOT x) y)). We ; return a list of lists of terms. If cnfp is t, the answer should be ; interpreted as a conjunction of disjunctions. If cnfp is nil, the answer ; should be interpreted as a disjunction of conjunctions. This function is not ; particularly efficient; it's only intended use at the time of its ; creation is to preprocess the hypotheses of rules so that from ; (AND p (OR q r) s) we could get (OR (AND p q r) (AND p r s)), as in ; the following use: ; ACL2 !>:trans (AND p (OR q r) s) ; (IF P (IF (IF Q Q R) S 'NIL) 'NIL) ; and ; (cnf-dnf t '(IF P (IF (IF Q Q R) S 'NIL) 'NIL) nil) ; = ((P Q S) (P R S)) (cond ((variablep term) (list (list (if sign term (list 'NOT term))))) ((fquotep term) (let ((val (if (eq (cadr term) nil) (not sign) sign))) ; Val is propositionally equivalent to (if sign term (not term)). (if val (if cnfp nil ; (AND) = T '(nil)) ; (OR (AND)) = T (if cnfp '(nil) ; (AND (OR)) = NIL nil)))) ; (OR) = NIL ((eq (ffn-symb term) 'NOT) (cnf-dnf (not sign) (fargn term 1) (not cnfp))) ((eq (ffn-symb term) 'IF) (let ((x (fargn term 1)) (y (fargn term 2)) (z (fargn term 3))) ; Cases we consider: ; (if x y nil) = (AND x y) ; (if x nil z) = (AND (NOT x) z) ; (if x y t) = (OR (NOT x) y) ; (if x t z) <--> (OR x z) = (if x x z) (cond ((equal z *nil*) ; (AND x y) (if cnfp (append (cnf-dnf sign x cnfp) (cnf-dnf sign y cnfp)) (cross-prod (cnf-dnf sign x cnfp) (cnf-dnf sign y cnfp)))) ((equal y *nil*) ; (AND (NOT x) z) (if cnfp (append (cnf-dnf (not sign) x cnfp) (cnf-dnf sign z cnfp)) (cross-prod (cnf-dnf (not sign) x cnfp) (cnf-dnf sign z cnfp)))) ((equal z *t*) ; (OR (NOT x) y) (if cnfp (cross-prod (cnf-dnf (not sign) x cnfp) (cnf-dnf sign y cnfp)) (append (cnf-dnf (not sign) x cnfp) (cnf-dnf sign y cnfp)))) ((or (equal x y) ; (OR x z) (equal y *t*)) (if cnfp (cross-prod (cnf-dnf sign x cnfp) (cnf-dnf sign z cnfp)) (append (cnf-dnf sign x cnfp) (cnf-dnf sign z cnfp)))) (t (list (list (if sign term (list 'NOT term)))))))) (t (list (list (if sign term (list 'NOT term))))))) (defun split-on-conjoined-disjunctions (lst) ; List is a list of terms that are implicitly conjoined, as found in the hyps ; returned in the pairs produced by unprettyify. We split on the disjunctions ; among them, converting (the translated version of) ; (P (OR Q1 Q2) R (OR S1 S2)) to ; ((P Q1 R S1) ; (P Q1 R S2) ; (P Q2 R S1) ; (P Q2 R S2)) (cond ((endp lst) nil) ((endp (cdr lst)) (cnf-dnf t (car lst) nil)) (t (cross-prod (cnf-dnf t (car lst) nil) (split-on-conjoined-disjunctions (cdr lst)))))) (defun split-on-conjoined-disjunctions-in-hyps-of-pairs (pairs) ; Pairs is a list of (hyps . concl) pairs as produced by unprettify. We split ; out the disjunctions in the hyps, producing another list of pairs. E.g., ; from these two pairs (((p (or q1 q2)) . concl1) ((r (or s1 s2)) . concl2)) ; we get these four: ; (((p q1) . concl1) ((p q2) .concl1) ((r s1) . concl2) ((r s2) . concl2)). (cond ((endp pairs) nil) (t (let ((hyps-lst (split-on-conjoined-disjunctions (car (car pairs))))) (cond ((endp hyps-lst) (cons (car pairs) (split-on-conjoined-disjunctions-in-hyps-of-pairs (cdr pairs)))) (t (append (pairlis-x2 hyps-lst (cdr (car pairs))) (split-on-conjoined-disjunctions-in-hyps-of-pairs (cdr pairs))))))))) (defun chk-acceptable-tau-rule (name term ctx wrld state) (let ((term1 (remove-guard-holders term))) (mv-let (form j bc) (tau-bounder-formp term1 wrld) (declare (ignore j bc)) (cond (form ; Term is a bounder correctness theorem of either form 1 or 2 (depending on ; form) and about slot j (if form 2), where bc is the bounder-correctness ; record representing it. But all we care about here is to report that term is ; acceptable. (value nil)) (t (let ((pairs (split-on-conjoined-disjunctions-in-hyps-of-pairs (strip-force-and-case-split-in-hyps-of-pairs (unprettyify term1))))) (cond ((acceptable-tau-rulesp :all pairs wrld) (value nil)) ((null (cdr pairs)) (er soft ctx "The formula of the theorem ~x0 fails to fit any of the forms ~ for acceptable :TAU-SYSTEM rules. See :DOC tau-system for the ~ details of the acceptable forms." name)) (t (er soft ctx "The formula of the theorem ~x0 gives rise to ~n1 normalized ~ formulas (e.g., after stripping out conjuncts in the ~ conclusion, etc.). In order to be a :TAU-SYSTEM rule, each ~ of these formulas must be acceptable as a tau rule and at ~ least one of them fails to be. See :DOC tau for details of ~ the acceptable forms." name (length pairs)))))))))) ; On the Tau Msgp Protocol ; Several of the functions that add tau rules obey the Tau Msgp Protocol. In ; that protocol, we return (mv msgp wrld), where msgp is either nil or an error ; message to be handled (signalled) by some caller of the function in question. ; When msgp is nil, wrld is the properly extended wrld. When msgp is non-nil, ; wrld is the original wrld passed into the function, not some partially ; updated extension. That is, functions obeying the msgp protocol are No ; Change Losers on wrld. Most functions following the protocol take an ; additional argument, wrld0, as the ``good'' wrld to preserve. ; The reason we have the protocol is that we cannot always make tau rules out ; of previously approved :tau-system formulas because the ens has changed and ; some previously identified tau recognizers are no longer identified as tau ; recognizers. This may or may not be an error, depending on lost-rune-action. ; When not an error, we just accumulate those lost runes on the global value of ; 'tau-lost-runes. The possibility that we should actually signal an error ; arises when we are processing the original introduction of the :tau-system ; rule, where the explanation of the inadequacy of the syntactic check is due ; to it having been done in the first pass of an encapsulate that failed to ; export the Boolean property to the second pass where the error is to be ; signalled. (defun add-tau-rule1 (lost-rune-action rune pairs ens wrld wrld0) ; We try to convert each (hyps . concl) pair in pairs to a tau rule and extend ; wrld accordingly. We obey the Tau Msgp Protocol and return (mv msgp wrld'). ; Wrld0 is the original world we started with and will be returned in the error ; case, as per the protocol. Pairs was derived by unprettyify from some ; :corollary for a rule named rune. ; Lost-rune-action determines what we do if we encounter a term that cannot be ; coded as a tau rule. If lost-rune-action is IGNORE, we quietly ignore such ; terms. If lost-rune-action is REPORT, we return a non-nil error message. ; This can happen if we're in the second pass of an encapsulate and discover ; that a function that was Boolean during the first pass is no longer known to ; be Boolean. If lost-rune-action is ACCUMULATE then we add the rune of the ; lost rule to the 'tau-lost-runes list in wrld. (cond ((endp pairs) (mv nil wrld)) (t (mv-let (msgp wrld) (let* ((hyps (car (car pairs))) (concl (cdr (car pairs))) (kind (acceptable-tau-rulep (cons hyps concl) wrld))) (case kind (BOOLEAN (mv nil (add-tau-boolean-rule rune hyps concl wrld))) (EVAL (mv nil (add-tau-eval-rule rune hyps concl wrld))) (SIMPLE (mv nil (add-tau-simple-rule rune hyps concl ens wrld))) (CONJUNCTIVE (mv nil (add-tau-conjunctive-rule rune hyps concl ens wrld))) (SIGNATURE-FORM-1 (mv nil (add-tau-signature-rule rune 1 hyps concl ens wrld))) (SIGNATURE-FORM-2 (mv nil (add-tau-signature-rule rune 2 hyps concl ens wrld))) (BIG-SWITCH (mv nil (add-tau-big-switch-rule rune hyps concl wrld))) (MV-NTH-SYNONYM (mv nil (add-tau-mv-nth-synonym-rule rune hyps concl wrld))) (otherwise (cond ((eq lost-rune-action 'REPORT) (mv (msg "Unable to generate a :TAU-SYSTEM rule for the rune ~x0 ~ with formula ~x1. A possible explanation is that we are ~ in the second pass of an ENCAPSULATE (e.g., we've just ~ printed ``End of Encapsulated Events.''). If so, then ~ evidently the formula in question was accepted during the ~ first pass but is no longer acceptable. This can happen ~ if the ENCAPSULATE established too few constraints. For ~ example, the local witness to some monadic function might ~ have been Boolean but that fact was not exported as ~ :TAU-SYSTEM (or, in tau automatic mode, ~ :TYPE-PRESCRIPTION) rule. See :DOC tau for the details ~ of the acceptable forms of :TAU-SYSTEM rules." rune (reprettyify hyps concl wrld)) ; No Change Loser on wrld0 when msgp non-nil: wrld0)) ((eq lost-rune-action 'ACCUMULATE) (mv nil (global-set 'tau-lost-runes (cons rune (global-val 'tau-lost-runes wrld)) wrld))) (t ; IGNORE lost runes (mv nil wrld)))))) (cond ; No Change Loser on wrld0 when msgp non-nil: (msgp (mv msgp wrld0)) (t (add-tau-rule1 lost-rune-action rune (cdr pairs) ens wrld wrld0))))))) (defun add-tau-rule (first-visitp rune term ens wrld0) ; We convert term into tau rules, if possible. We obey the Tau Msgp Protocol ; and return (mv msgp wrld'); No Change Loser on wrld0. ; First-visitp is t if this is the first time in this world that this rune has ; been visited. What that really means is that tau is visiting the event that ; install-event is installing. Otherwise, this is a re-visit of an event. ; Based on whether this is the first visit or not, we set lost-rune-action to ; REPORT, ACCUMULATE, or IGNORE to indicate what should be done if term cannot ; be represented as a tau rule. REPORT means we'll report a non-nil msgp; ; ACCUMULATE means we just add rune to 'tau-lost-runes, and IGNORE means we ; just quietly ignore the situation. (let ((term1 (remove-guard-holders term))) (mv-let (form j bc) (tau-bounder-formp term1 wrld0) (cond (form ; Term is a bounder correctness theorem of form 1 or 2 (depending on form), j ; is the form 2 slot, and bc is the bounder-correctness record that represents ; term. We add it to the list of of bounder-correctness records for the ; subject-fn. (mv nil (add-tau-bounder-rule rune form j bc wrld0))) (t (let* ((pairs (split-on-conjoined-disjunctions-in-hyps-of-pairs (strip-force-and-case-split-in-hyps-of-pairs (unprettyify term1)))) (lost-rune-action (if (eq (car rune) :tau-system) (if first-visitp 'REPORT 'ACCUMULATE) 'IGNORE))) (add-tau-rule1 lost-rune-action rune pairs ens wrld0 wrld0))))))) ; We now turn to the topic of ``visiting'' events and building up the tau ; database. Recall that we may be visiting an event for the first time (e.g., ; in the install-event just after it has been executed) or as part of a ; chronological sweep of the world to regenerate the tau database under a ; different enabled structure. But from tau's perspective, every visit to an ; event is (almost) like the first time. This means that it must essentially ; erase any tau properties before starting to add the ``new'' ones. ; We have already defined tau-visit-function-introduction where we clear the ; tau properties of a function symbol. This is not necessary on the first ; visit to a DEFUN because we know the symbol is new. Furthermore, on an ; ENCAPSULATE event, it is too late to initialize the tau properties of the ; constrained functions when we see the encapuslate event! So we visit ; function introductions when we see FORMALS properties stored on the world and ; we don't consider that part of the (re-)visits to events. ; What does tau learn by visiting a defined function? (a) Whether the function ; is a tau recognizer. (b) Whether the function's type-prescription(s) are tau ; signature rules. (c) Whether the function's definitional equation is a ; big-switch rule. (d) Whether the function's definitional equation is an ; mv-nth-synonym. The last three possibilities are contingent upon tau being ; in automatic mode and upon certain runes being enabled. (defun discover-tau-pred (fn ens wrld) ; If fn is a monadic Boolean under ens, we initialize the tau properties for a ; tau recognizer; else not. We return the modified wrld. ; Note: This function (re-)initializes the tau properties of fn! Normally, ; this is a straightforward initialization because discover-tau-pred (actually ; the -preds version below) is only called by tau-visit-defuns, which will have ; just introduced the new name fn. However, it is possible that fn is being ; redefined. In that case, either we Erased old properties or else we are ; Overwriting. In the former case, the re-initialization here is still ; correct. In the latter case, one might argue that we should not ; re-initialize because we're supposed to just add to existing properties. But ; the tau implicants already stored under fn and derived from its now-obsolete ; definition may be quite extensive. We think it is quite likely that adding ; new deductions to those implicants from this new definition will produce ; inconsistency. Therefore, we make the arbitrary decision to Erase the tau ; properties even upon Overwriting redefinitions. (mv-let (monadic-booleanp ttree) (monadic-boolean-fnp fn ens wrld) (cond (monadic-booleanp (initialize-tau-pred fn (set-tau-runes 'list (tagged-objects 'lemma ttree) wrld))) (t wrld)))) (defun discover-tau-preds (fns ens wrld) (cond ((endp fns) wrld) (t (discover-tau-preds (cdr fns) ens (discover-tau-pred (car fns) ens wrld))))) (defun tau-rules-from-type-prescriptions (tp-lst fn ens wrld wrld0) ; We map over tp-lst, which is a list of type-prescriptions on fn, and for ; every rule that is both enabled in ens and has fn as a base symbol, we store ; an appropriate tau rule (if possible) into wrld. This function should only ; be called if tau is in automatic mode. The reason we focus on those rules ; with base symbol fn is that when visiting a defun we act like the defun event ; was just executed. We obey the Tau Msgp Protocol and return (mv msgp wrld'); ; No Change Loser on wrld0. ; Programming Note: At first we thought we could achieve No Change Loser status ; without passing in wrld0. The idea is that add-tau-rule1 is a No Change ; Loser and so if it signalled an error, we'd get back what we gave it. But ; because this function is recursive and maps over tp-lst, it could be that ; what we gave add-tau-rule1 has already been extended. ; Note also that this function uses add-tau-rule1 rather than add-tau-rule. In ; so doing, it precludes the possibility that the rule being added is a ; tau-bounder-formp, which is handled by add-tau-rule. But no ; type-prescription can be a bounder correctness theorem. (cond ((endp tp-lst) (mv nil wrld)) ((and (eq (cadr (access type-prescription (car tp-lst) :rune)) fn) (enabled-numep (access type-prescription (car tp-lst) :nume) ens)) (mv-let (term ttree) (convert-type-prescription-to-term (car tp-lst) ens wrld) (let ((pairs (acceptable-tau-rules (split-on-conjoined-disjunctions-in-hyps-of-pairs (strip-force-and-case-split-in-hyps-of-pairs (unprettyify (remove-guard-holders term)))) wrld))) (cond ((null pairs) (tau-rules-from-type-prescriptions (cdr tp-lst) fn ens wrld wrld0)) (t (mv-let (msgp wrld) (add-tau-rule1 'IGNORE ; this is a :TYPE-PRESCRIPTION rune ; so we can't count on it being of ; interest (access type-prescription (car tp-lst) :rune) pairs ens wrld wrld0) ; If msgp is non-nil, then x is nil and the attempt to add this rule caused an ; error as explained by msgp. On the other hand, if msgp is nil, then x is the ; extended wrld. (cond (msgp ; We actually know here that wrld is wrld0 given that add-tau-rule1 is a No ; Change Loser, but just to be explicit: (mv msgp wrld0)) (t (tau-rules-from-type-prescriptions (cdr tp-lst) fn ens (set-tau-runes 'list (tagged-objects 'lemma ttree) wrld) wrld0))))))))) (t (tau-rules-from-type-prescriptions (cdr tp-lst) fn ens wrld wrld0)))) (defun original-def-body1 (fn def-bodies) (cond ((endp def-bodies) nil) ((eq (cadr (access def-body (car def-bodies) :rune)) fn) (car def-bodies)) (t (original-def-body1 fn (cdr def-bodies))))) (defun original-def-body (fn wrld) ; Return the def-body originally added for fn in wrld, identified as the body ; whose rune has base symbol fn. (original-def-body1 fn (getprop fn 'def-bodies nil 'current-acl2-world wrld))) (defun tau-like-propositionp (var term wrld) ; Warning: Keep this function in sync with expand-tau-like-proposition. ; We determine whether term is propositional expression built out of tau-like ; terms with subject var, which is a variable symbol. We give special ; treatment only to the propositional-like functions typically expanded by ; expand-some-non-rec-fn (in *definition-minimal-theory*) that are likely to ; appear in defuns expressing propositional facts about boolean recognizers. ; So, for example, we handle IFF but not EQUAL and its synonyms even though ; when both arguments are Boolean they are the same. See the comment in ; expand-tau-like-proposition below. (mv-let (sign recog e criteria) (tau-like-term term var wrld) (declare (ignore sign e criteria)) (cond (recog t) ((variablep term) nil) ((fquotep term) (or (equal term *t*) (equal term *nil*))) ((eq (ffn-symb term) 'IF) (and (tau-like-propositionp var (fargn term 1) wrld) (tau-like-propositionp var (fargn term 2) wrld) (tau-like-propositionp var (fargn term 3) wrld))) ((eq (ffn-symb term) 'RETURN-LAST) (tau-like-propositionp var (fargn term 3) wrld)) ((and (eq (ffn-symb term) 'NOT) (eq (ffn-symb term) 'NULL)) (tau-like-propositionp var (fargn term 1) wrld)) ((or (eq (ffn-symb term) 'IMPLIES) (eq (ffn-symb term) 'IFF)) (and (tau-like-propositionp var (fargn term 1) wrld) (tau-like-propositionp var (fargn term 2) wrld))) (t nil)))) (defun expand-tau-like-proposition (term) ; Warning: Keep this function in sync with tau-like-propositionp for some ; unknown variable var. That implies that var is the only variable in the ; term. It should be the case that if term is a tau-like-propositionp, then ; the value of term is EQUAL to the value of (expand-tau-like-proposition ; term). Key to the correctness of the definition below is that all the ; components of tau-like propositions are boolean. ; Warning: At one point we considered handling (EQUAL x y) as (IFF x y) since ; we ``knew'' that x and y are tau-like propositions. However, we don't. It ; could be that we are looking at (EQUAL var '123) in which case it is a ; tau-like proposition because it is a tau recognizer. If we dive into it we ; get out of the boolean world. So to treat EQUAL as IFF we have to make sure ; that both arguments are tau like. Rather than consider that, we just punt ; and don't consider EQUAL, EQ, EQL, and = as propositional connectors. (cond ((variablep term) term) ((fquotep term) term) ((eq (ffn-symb term) 'IF) (fcons-term* 'IF (expand-tau-like-proposition (fargn term 1)) (expand-tau-like-proposition (fargn term 2)) (expand-tau-like-proposition (fargn term 3)))) ((eq (ffn-symb term) 'RETURN-LAST) (expand-tau-like-proposition (fargn term 3))) ((or (eq (ffn-symb term) 'NOT) (eq (ffn-symb term) 'NULL)) ; we map (NULL x) to (NOT x) (fcons-term* 'NOT (expand-tau-like-proposition (fargn term 1)))) ((eq (ffn-symb term) 'IMPLIES) (fcons-term* 'IF (expand-tau-like-proposition (fargn term 1)) (expand-tau-like-proposition (fargn term 2)) *t*)) ((eq (ffn-symb term) 'IFF) ; Warning: We know that both arguments are Boolean. Why? Because this term is ; a tau-like-proposition and we don't treat (IFF x NIL), say, as a tau ; recognizer! If we did change tau-like-term to look into (IFF NIL x) we would ; have to check here that x is Boolean to do the expansion we do below! (let ((arg2 (expand-tau-like-proposition (fargn term 2)))) (fcons-term* 'IF (expand-tau-like-proposition (fargn term 1)) arg2 (fcons-term* 'NOT arg2)))) (t term))) (defun add-only-simple-and-conjunctive-tau-rules (rune pairs ens wrld) ; We believe every clause in clauses corresponds to a Simple or Conjunctive ; rule. But we confirm that, adding each Simple or Conjunctive rule and ; ignoring any clause that is not. (cond ((endp pairs) wrld) (t (add-only-simple-and-conjunctive-tau-rules rune (cdr pairs) ens (let ((hyps (car (car pairs))) (concl (cdr (car pairs)))) (cond ((tau-conjunctive-formp hyps concl wrld) ; Simple if hyps is of length 1. (cond ((null (cdr hyps)) (add-tau-simple-rule rune hyps concl ens wrld)) (t (add-tau-conjunctive-rule rune hyps concl ens wrld)))) (t wrld))))))) (defun convert-normalized-term-to-pairs (rhyps term ans) ; Term is a term in IF-normal form. We convert it to a list of (hyps . concl) ; pairs such that the conjunction of the (implies (and ,hyps) concl) terms is ; IFF-equivalent to term. (cond ((variablep term) (cons (cons (revappend rhyps nil) term) ans)) ((fquotep term) (if (equal term *nil*) (cond ((consp rhyps) (cons (cons (revappend (cdr rhyps) nil) (dumb-negate-lit (car rhyps))) ans)) (t (cons (cons nil *nil*) ans))) ans)) ((eq (ffn-symb term) 'IF) (cond ((equal (fargn term 3) *nil*) (convert-normalized-term-to-pairs rhyps (fargn term 2) (cons (cons (revappend rhyps nil) (fargn term 1)) ans))) ((equal (fargn term 2) *nil*) (convert-normalized-term-to-pairs rhyps (fargn term 3) (cons (cons (revappend rhyps nil) (dumb-negate-lit (fargn term 1))) ans))) (t (convert-normalized-term-to-pairs (cons (fargn term 1) rhyps) (fargn term 2) (convert-normalized-term-to-pairs (cons (dumb-negate-lit (fargn term 1)) rhyps) (fargn term 3) ans))))) (t (cons (cons (revappend rhyps nil) term) ans)))) ; In convert-term-to-pairs, below, we convert a term (known to be a theorem) ; into a list of (hyps . concl) pairs. We basically walk through its ; propositional structure collecting hyps and then the conclusion at each tip. ; We ought to just clausify the term instead, but we have not yet defined ; clausify. The following code is a cheap and dirty approximation of clausify ; used just to process non-recursive propositional definition bodies containing ; only tau recognizers. ; To see the need for more than stripping, define a recognizer as a disjunction ; of other recognizers, e.g., (DEFUN FOO (X) (OR (A X) (B X) (C X) (D X))). To ; make it easier to play, let's just think of proposition letters: ; FOO <--> (A v B v C v D). ; We want to store a tau rule of the form (A v B v C v D) --> foo. ; If we express that as an IF we get ; (IF (IF A 'T (IF B 'T (IF C 'T D))) FOO 'T) ; which normalizes to ; (IF A FOO (IF B FOO (IF C FOO (IF D FOO 'T)))) ; and then a rudimentary strip branches produces the pairs: ; (((A) . FOO) ; ((-A B) . FOO) ; ((-A -B C) . FOO) ; ((-A -B -C D) . FOO)) ; Our job now is to clean this up by getting rid of unnecessary hyps to produce: ; (((A) . FOO) ; ((B) . FOO) ; ((C) . FOO) ; ((D) . FOO)) ; This is done quite nicely by the subsumption-replacement loop in clausify. ; But we don't have clausify. (defun complementaryp (lit1 lit2) (declare (xargs :guard (and (pseudo-termp lit1) (pseudo-termp lit2)))) ; Suppose lit1 and lit2 are terms and neither is of the form (NOT (NOT &)). ; Then we determine whether one is the complement of the other, i.e., one ; is (NOT lit) where lit is the other. Note that we do not use any ; commuativity knowledge. Thus, ; WARNING: (EQUAL A B) and (NOT (EQUAL B A)) are *not* complementaryp, by ; this definition! (or (and (nvariablep lit1) (not (fquotep lit1)) (eq (ffn-symb lit1) 'not) (equal (fargn lit1 1) lit2)) (and (nvariablep lit2) (not (fquotep lit2)) (eq (ffn-symb lit2) 'not) (equal (fargn lit2 1) lit1)))) ; Note on Terminology: Below we use the expression ``ancestor literal'' which ; was introduced by Bob Kowalski in the early 1970s to name a literal ; previously resolved upon in an SL-resolution proof. Our ancestor literals ; are similar in spirit but we make no claim that they are the exactly the same ; as Kowalski's; they may be, but we haven't thought about it. The word ; ``ancestor'' is just appropriate. We define our use of the term below. (defun subsumes-but-for-one-negation (hyps1 hyps2 ancestor-lits) ; First, think of hyps1 and hyps2 as merely sets of literals. This function ; returns either nil or identifies a literal, lit, in hyps1, whose complement, ; lit', occurs in hyps2, and such that the result of removing lit from hyps1 is ; a subset of hyps2. It ``identifies'' that literal by returning the non-nil ; list whose car is lit' (the element of hyps2) and whose cdr is ancestor-lits, ; i.e., (cons lit' ancestor-lits). We call lit' an ``ancestor literal'' in ; this context. ; However, what we check is weaker than the spec above. Our requirement on ; this function is that when it is non-nil. the result is as described above. ; However, if the function returns nil it does not mean there is no such lit! ; It only means we failed to find it. ; What we actually check is this: Let lit be the first literal of hyps1 that is ; not equal to its counterpart at the same position in hyps2. Let tail1 be the ; remaining literals of hyps1. Let tail2 be the tail of hyps2 starting with ; the counterpart of lit. Then we insist that somewhere in tail2 the ; complement, lit', of lit appears, that every literal passed before the first ; occurrence of lit' is in ancestor-lits, and that tail1 is a subset of the ; rest of tail2 from the point at which lit' was found. We return either nil ; or (cons lit' ancestor-lits) depending on whether these conditions hold. ; Thus, if this function returns non-nil, the lit' it identifies occurs in ; hyps2, occurs complemented in hyps1, and all the other literals of hyps1 are ; in hyps2. ; In this function, one may think of ancestor-lits merely as heuristic ; ``permissions'' to scan deeper into hyps2 to find the complement of lit. But ; in subsequent functions it will play a crucial soundness role. (cond ((endp hyps1) nil) ((endp hyps2) nil) ((equal (car hyps1) (car hyps2)) (subsumes-but-for-one-negation (cdr hyps1) (cdr hyps2) ancestor-lits)) ((complementaryp (car hyps1) (car hyps2)) (if (subsetp-equal (cdr hyps1) (cdr hyps2)) (cons (car hyps2) ancestor-lits) nil)) ((member-equal (car hyps2) ancestor-lits) (subsumes-but-for-one-negation hyps1 (cdr hyps2) ancestor-lits)) (t nil))) ; On Removal of Ancestor Literals -- The Satriana Hack Prequel ; Pair1 is a (hyps . concl) pair, say (hyps1 . concl1) and pairs is a list of ; such pairs. We ``clean up'' pairs with a very limited one-pass ; ``subsumption-replacement'' loop. The idea is to use pair1 to clean up the ; first pair of pairs, pair2, producing new-pair2. Then we use new-pair2 to ; clean up the next pair of pairs, etc. The tricky bit is the use of ; ancestor-lits, which is accumulated from all the pairs we've cleaned up and ; the maintenance of which is crucial to soundness. This entire body of code ; is reminiscent of the Satriani Hack, which is actually a little more ; elaborate (and slower) because it checks for subsumptions we don't here. ; We can ``use pair1 to clean pair2'' if the following three conditions hold: ; (a) they have the same concls, (b) there is a literal, lit, of the hyps of ; pair1 that appears complemented in the hyps of pair2, and (c) removing lit ; from the hyps of pair1 produces a subset of the hyps of pair2. Note that ; conditions (b) and (c) are assured by subsumes-but-for-one-negation, which ; treats ancestor-lits merely as heuristic permissions to scan further. ; If we can use pair1 to clean up pair2, and the complement of the identified ; lit is lit', then we ``clean up pair2'' by removing from its hyps not just ; lit' but all the ancestor-lits! ; In the discussion below we call lit' the ``ancestor literal'' of the cleaned ; up pair2. The ancestor literal of a newly derived pair is the literal ; removed from the parent pair. Its complement occurs as a hyp in an earlier ; pair and that earlier pair's other hyps all occur in the newly derived pair. ; For example, if pair1 is ((A B C) . concl) and pair2 is ((A -B C D) . concl) ; then we can use pair1 to clean up pair2 and the process of checking that ; identifies -B as an ancestor literal. Cleaning up pair2 produces pair2', ((A ; C D) . concl) and -B is the ancestor of literal of that pair in the context ; of this derivation. ; Why is it sound to clean up this way? We start with two observations. ; Observation 1. If we have two theorems of the form ((H1 & p) --> c) and ((H2 ; & -p) --> c), where H1 is a subset of H2, then we can replace the second with ; (H2 --> c). This is most easily seen by putting the first into ; contrapositive form, ((H1 & -c) --> -p), and observing that we can then ; rewrite the -p in the second theorem to T. ; Observation 2: We maintain the following invariant about ancestors-lits and the ; ``current'' pair (the car of pairs), which we call pair2 in the statement of ; the invariant: ; for every element, lit' (with complement lit), of ancestor-lits there ; exists a pair, say pair', among those we have already collected such that ; the concl of pair' is the concl of the pair2, lit occurs in the hyps of ; pair', and the remaining hyps of pair' form a subset of the hyps of pair2. ; If this invariant is true, then we can remove every ancestor-lit from pair2. ; The previous pair' can with Observation 1 to remove each ancestor. Note that ; lit' may not appear in the hyps of pair2. ; One might wonder what gives us the right, subsequently, to remove the ; ancestor lit of pair2 from the next pair encountered, even though we just ; check that those hyps, which may be missing lit', are a subset of the next ; pair. The invariant insures we can. But if this connundrum bothers you, ; just add lit' as a hyp to pair2: it is never unsound to add a hypothesis to a ; theorem! [The key part of the invariant is that, except for lit, the hyps of ; pair' are members of the hyps of pair2.] ; The invariant in Observation 2 is clearly true when ancestors-lit is nil, and ; inspection of the code below shows that we make it nil when the concls of two ; successive pairs are not equal and when the hyps of one are not in the ; appropriate subset relation with the next. The only time we add a lit' to ; ancestors-lit we know that lit is a member of the hyps pair1 (which we ; collect) and that the rest of the hyps of pair1 are a subset of the hyps of ; pair2. Since subset is transitive, we know that the pairs from which the ; ancestors lit descend (minus the lit in question) are all subsets of the hyps ; of the last pair collected. ; Here is an example of this process: ; Suppose we have: ; pairs pairs' ancestor-lits ; [1] x y A u --> concl [1'] x y A u --> concl ; [2] x y -A B u v --> concl [2'] x y B u v --> concl -A ; [3] x y -A -B C u v w --> concl [3'] x y C u v w --> concl -B -A ; ----- let's consider the next step --- ; [4] x y -A -B -C D u v w z --> concl [4'] x y D u v w z --> concl -C -B -A ; By the time we use [2] to clean up [3] to produce [3'], we're the state shown ; above the ``let's consider'' line. We will next produce [4'] from [3'] and ; [4]. Using Observation 1, [3'] allows us to knock off the -C from [4] ; because the concls of [3'] and [4] are the same and the hyps of [3'] are a ; subset of those of [4] except for the literal C which appears negated in the ; hyps of [4]. -C is the ancestor literal of this step. ; We thus get to remove -C from [4] and add -C to the ancestor lits going ; forward. But we also get to remove the other ancestor lits, -B and -A. We ; show why below. ; Why can we remove -B? By the invariant, we know there exists a pair' among ; those we have already collected such that the concl of pair' is the concl of ; the [4], B occurs in the hyps of pair', and the remaining hyps of pair' form ; a subset of the hyps of [4]. The pair' in question is [2'] and it allows us ; to drop -B from [4]. ; Why can we remove -A? By the invariant, we know there exists a pair' among ; those we have already collected such that the concl of pair' is the concl of ; [4], A occurs in the hyps of pair', and the remaining hyps of pair' form a ; subset of the hyps of [4]. The pair' in question is [1'] and it allows us to ; drop -A from [4]. ; In this example, all the ancestor lits are in the current pair. But that need ; not be true. For example, imagine that [4] did not contain -A. ; [4] x y -B -C D u v w z --> concl [4'] x y D u v w z --> concl -C -B -A ; The presence of -A in ancestors still means there is a collected pair, pair', ; that would allow us to delete A because the other hyps of pair' are a subset ; of [4]. We know the subset relation holds because the hyps of [1'] (minus A) ; are a subset of [2'], the hyps of [2'] (minus B) are a subset of those of ; [3'], and the hyps of [3'] (minus C) are a subset of those of [4]. So we ; COULD delete -A even if it weren't there! ; The function remove-ancestor-literals-from-pairs below is the top-level ; entry to this process. It takes a list of pairs, uses the first one as ; pair1 and the rest as pairs. The initial ancestor-lits is nil. ; Here are some examples of the function in action. These examples show how we ; are sensitive to the order of the literals. This sensitivity is deemed ; unimportant because stripping the branches of normalized IFs produces the ; regular order that we exploit. ; (remove-ancestor-literals-from-pairs ; '(((x y A u) . concl) ; ((x y (NOT A) B u v) . concl) ; ((x y (NOT A) (NOT B) C u v w) . concl) ; ((x y (NOT A) (NOT B) (NOT C) D u v w z) . concl))) ; = ; (((x y A u) . concl) ; ((x y B u v) . concl) ; ((x y C u v w) . concl) ; ((x y D u v w z) . concl)) ; However, if we permute the first pair we fail to get any of the -A, but we do ; knock off the -B, -C, and -D: ; (remove-ancestor-literals-from-pairs ; '(((x A y u) . concl) ; ((x y (NOT A) B u v) . concl) ; ((x y (NOT A) (NOT B) C u v w) . concl) ; ((x y (NOT A) (NOT B) (NOT C) D u v w z) . concl))) ; = ; (((x A y u) . concl) ; ((x y (not A) B u v) . concl) ; ((x y (not A) C u v w) . concl) ; ((x y (not A) D u v w z) . concl)) ; Similarly, if the second pair fails the subset test, we start over. ; (remove-ancestor-literals-from-pairs ; '(((x y A u1) . concl) ; ((x y (NOT A) B u1 v) . concl) ; ((x y (NOT B) C u2 v w) . concl) ; ((x y (NOT A) (NOT B) (NOT C) D u2 v w z) . concl))) ; = ; (((x y A u1) . concl) ; ((x y B u1 v) . concl) ; ((x y (not B) C u2 v w) . concl) ; ((x y (not A) (not B) (not C) D u2 v w z) . concl)) ; Note that we do not get to knock off the -B in the third pair or the -A and ; -B in the fourth, because u1 is not u2. We would actually be justified in ; knocking off the -C in the fourth, but we fail to find it because -A is ; blocking the subset check for going deep enough to find the -C. We would get ; the improved result if we permuted the fourth input pair: ; (remove-ancestor-literals-from-pairs ; '(((x y A u1) . concl) ; ((x y (NOT A) B u1 v) . concl) ; ((x y (NOT B) C u2 v w) . concl) ; ; On the next line, moved the -A to end. ; ((x y (NOT B) (NOT C) D u2 v w z (NOT A)) . concl))) ; = ; (((x y A u1) . concl) ; ((x y B u1 v) . concl) ; ((x y (not B) C u2 v w) . concl) ; ((x y (not B) D u2 v w z (not A)) . concl)) ; Returning to the first, classic, example but changing the concl of the last ; two rules: ; (remove-ancestor-literals-from-pairs ; '(((x y A u) . concl1) ; ((x y (NOT A) B u v) . concl1) ; ((x y (NOT A) (NOT B) C u v w) . concl2) ; ((x y (NOT A) (NOT B) (NOT C) D u v w z) . concl2))) ; = ; (((x y A u) . concl1) ; ((x y B u v) . concl1) ; ((x y (not A) (not B) C u v w) . concl2) ; ((x y (not A) (not B) D u v w z) . concl2)) ; we see we simplify the two concl1 pairs and the two concl2 pairs, but we ; don't carry over from concl1 to concl2. (defun remove-ancestor-literals-from-pairs1 (pair1 pairs ancestor-lits) ; See the discussion above On Removal of Ancestor Literals -- The Satriana Hack ; Prequel (cond ((endp pairs) nil) (t (let ((pair2 (car pairs))) (cond ((equal (cdr pair1) (cdr pair2)) (let ((new-ancestor-lits (subsumes-but-for-one-negation (car pair1) (car pair2) ancestor-lits))) (cond (new-ancestor-lits (let ((new-pair2 (cons (set-difference-equal (car pair2) new-ancestor-lits) (cdr pair2)))) (cons new-pair2 (remove-ancestor-literals-from-pairs1 new-pair2 (cdr pairs) new-ancestor-lits)))) (t (cons pair2 (remove-ancestor-literals-from-pairs1 pair2 (cdr pairs) nil)))))) (t (cons pair2 (remove-ancestor-literals-from-pairs1 pair2 (cdr pairs) nil)))))))) (defun remove-ancestor-literals-from-pairs (pairs) ; See the discussion above On Removal of Ancestor Literals -- The Satriana Hack ; Prequel for a thorough treatment of this function. Pairs is a list of (hyps ; . concl) pairs and we clean it up -- eliminating unnecessary hyps -- using a ; very limited form of subsumption-replacement akin to the Satriana Hack. (cond ((endp pairs) nil) ((endp (cdr pairs)) pairs) (t (cons (car pairs) (remove-ancestor-literals-from-pairs1 (car pairs) (cdr pairs) nil))))) (defun convert-term-to-pairs (term ens wrld) ; Term is assumed to be the result of expanding a tau-like-propositionp. Thus, ; it is a term composed entirely of T, NIL, (recog var), and IF expressions ; composed of such terms. We wish to convert term to a list of (hyps . concl) pairs. (mv-let (nterm ttree) (normalize term t nil ens wrld nil) (mv (remove-ancestor-literals-from-pairs (convert-normalized-term-to-pairs nil nterm nil)) (all-runes-in-ttree ttree nil)))) ; On the Motivation for Tau-Subrs ; Consider two tau recognizers, A and B, and their conjunction as another tau ; recognizer, AB. Suppose we know the theorem (AB x) --> (AB (fn x)) as a tau ; signature rule. Then does tau prove that same theorem? No! ; The reason is that preprocess-clause, which tries tau-clausep, expands some ; functions. It will expand the (AB x) in the hypothesis, because it is a ; conjunction, but it will not expand it in the conclusion. The result is that ; tau-clausep is presented with: ((NOT (A x)) (NOT (B x)) (AB (fn x))) and ; without some more work, tau does not know know that A & B --> AB. That is ; the role of tau-subrs below. It essentially recognizes nonrecursive ; conjunctions of tau and adds the appropriate tau rules in both directions: ; (A & B) --> AB and AB --> A and AB --> B. ; Below is an example. The first thm below failed until tau-subrs was implemented. ; (defun A (x) (integerp x)) ; (defun B (x) (< 1000 x)) ; (defun AB (x) (and (A x)(B x))) ; (defun fn (x) (+ x 1)) ; (defthm fn-sig ; (implies (AB x) (AB (fn x))) ; :rule-classes :tau-system) ; We disable B to keep the example simple. As an extra stresser on tau during ; this experiment, we leave A enabled so that its (trivial) conjunctive nature ; must be also exploited by tau. ; (in-theory (disable fn B)) ; This should succeed whether AB is enabled or not. The first thm failed ; before tau-subr was added. ; (thm (implies (AB x) (AB (fn x))) :hints (("Goal" :in-theory (enable AB)))) ; (thm (implies (AB x) (AB (fn x))) :hints (("Goal" :in-theory (disable AB)))) ; Now for the disjunctive analogue: ; (defun C (x) (stringp x)) ; (defun AC (x) (or (A x) (C x))) ; (defun gn (x) (if (integerp x) (if (< 999 x) "NaN" (+ x 1)) "NaN")) ; (defthm gn-sig ; (implies (AC x) (AC (gn x))) ; :rule-classes :tau-system :hints (("Goal" :in-theory (enable A)))) ; (thm (implies (AC x) (AC (gn x))) :hints (("Goal" :in-theory (enable AC)))) ; (thm (implies (AC x) (AC (gn x))) :hints (("Goal" :in-theory (disable AC)))) (defun tau-subrs (rune fn formals rhs ens wrld) ; See the On the Motivation for Tau-Subrs for some background. ; We know fn is a non-rec monadic Boolean, formals has one element, v, rhs is ; the body of fn. Thus, (fn v) --> rhs and rhs --> (fn v). However, we only ; add the corresponding tau rules if rhs is a propositional combination of ; tau-like :same-var terms. So, for example, rhs might be (and (A x) (B x)) ; or (or (A x) (and (B x) (C x))) but may not contain interesting non-tau ; functions. If rhs is a propositional combination of tau-like terms, we add ; the rules that link fn to the subroutines in rhs. (let ((var (car formals))) (cond ((tau-like-propositionp var rhs wrld) (let* ((lhs (fcons-term fn formals)) (rhs1 (expand-tau-like-proposition rhs))) (mv-let (pairs1 runes1) (convert-term-to-pairs (fcons-term* 'IF lhs rhs1 *t*) ens wrld) (mv-let (pairs2 runes2) (convert-term-to-pairs (fcons-term* 'IF rhs1 lhs *t*) ens wrld) ; We only consider the possibilities of these clauses being Simple or ; Conjunctive rules. We do not think they could be signature rules because of ; the tau-like-propositionp condition, but we take no chances. We also ; store in tau-runes all the runes used in the normalization of the body. (let ((wrld (add-only-simple-and-conjunctive-tau-rules rune pairs1 ens (add-only-simple-and-conjunctive-tau-rules rune pairs2 ens wrld)))) (set-tau-runes 'list (union-equal runes1 runes2) wrld)))))) (t wrld)))) (defun tau-visit-defuns1 (fns ens wrld wrld0) ; This function is a subroutine of tau-visit-defuns and should only be called ; if tau is in automatic mode. We collect the big-switch, mv-nth-synonym, and ; signature rules, if any for the given ens, for each fn in fns. We obey the ; Tau Msgp Protocol and return (msgp wrld'); No Change Loser on wrld0. (cond ((endp fns) (mv nil wrld)) (t (let* ((fn (car fns)) (db (original-def-body fn wrld)) (formals (access def-body db :formals)) (rhs (access def-body db :concl)) (def-eqn (cond ((null db) nil) ((access def-body db :hyp) nil) (t (fcons-term* 'equal (fcons-term fn formals) rhs)))) (def-nume (access def-body db :nume)) (def-rune (access def-body db :rune))) ; First, we collect the big-switch and mv-nth-synonym rules (if any) from fn, ; provided the definitional equation is enabled in ens. It is impossible for ; fn to be both a big switch and an mv-nth synonym. Note also that the def-eqn ; is nil if it somehow has hypotheses. (let ((wrld (cond ((or (null def-eqn) (not (enabled-numep def-nume ens))) wrld) ((tau-big-switch-equationp def-eqn wrld) (add-tau-big-switch-rule def-rune nil def-eqn wrld)) ((tau-mv-nth-synonym-formp nil def-eqn) (add-tau-mv-nth-synonym-rule def-rune nil def-eqn wrld)) (t (tau-subrs def-rune fn formals rhs ens wrld))))) (mv-let (msgp wrld) (tau-rules-from-type-prescriptions (getprop fn 'type-prescriptions nil 'current-acl2-world wrld) fn ens wrld wrld0) (cond (msgp (mv msgp wrld0)) (t (tau-visit-defuns1 (cdr fns) ens wrld wrld0))))))))) (defun tau-visit-defuns (fns auto-modep ens wrld0) ; This is the function the tau system uses to update the tau database in ; response to a mutual-recursion event. Every element of fns is a defined ; function in wrld0. That means that each has all the properties one would ; expect of a defined function except the tau properties, which are determined ; below. We assume that all tau properties of the fns in question are cleared ; (either because this is the first visit and the fns are all new or because we ; are in a tau regeneration and we have already visited the corresponding ; function introductions. ; We follow the Tau Msgp Protocol and return (mv msgp wrld'). No Change Loser ; on wrld0. ; We identify all the tau recognizers before we start to process any other kind ; of tau rules, so that if a mutually recursive nest introduces several ; recognizers, we treat them all appropriately when doing the syntactic checks ; for other rules. We discover the tau recognizers among defun'd functions ; whether or not we are in automatic mode. (let* ((wrld (discover-tau-preds fns ens wrld0))) ; Then, if in auto mode, gather the big-switch, mv-nth-synonym, and signature ; rules for each member of fns. (if auto-modep (tau-visit-defuns1 fns ens wrld wrld0) (mv nil wrld)))) (defun tau-visit-defun (fn auto-modep ens wrld0) ; This function updates the tau database in response to a single defun. ; It is just a special case of mutual-recursion. We obey the Tau ; Msgp Protocol and return (mv msgp wrld'); No Change Loser on wrld0. (tau-visit-defuns (list fn) auto-modep ens wrld0)) ; Next we implement the tau visit to a defthm event. In manual mode, all we do ; is add the ens-enabled explicit :tau-system rules originally introduced by ; this event. In automatic mode, we mine each of the ens-enabled ; non-:tau-system rules. But there are four nuances. ; First, what if an event has both :tau-system and non-:tau-system rules and we ; are in automatic mode? Do we look for tau rules among the non-:tau-system ; rules? Our answer to that is no: if the user mentions :tau-system rules in ; an event, we assume that those are the only tau rules wanted. We think it ; would be too confusing if the user has to think about the impact of his ; non-:tau-system rules while being explicit with his :tau-system rules. If ; there is no mention of :tau-system rules and we're in automatic mode, we look ; for implicit rules. ; Second, how does the ens affect this analysis? What if an event has an ; explicit :tau-system rule and it is disabled in ens? Do we act as though ; that rule class were not present and look for implicit rules? Or does the ; presence of a :tau-system rule, even a disabled one, signal that the user did ; not intend for the other rules to be mined? Our answer is that the presence ; of a :tau-system rule signals that nothing automatic should be done, even if ; the :tau-system rule is disabled. Note that an event might have several ; :tau-system rules, under different runes, and some might be enabled and other ; disabled. Still, our analysis is the same: Any :tau-system rule in the ; original event means we look only at :tau-system rules and add the ones that ; are enabled. ; Third, this whole discussion suggests that an event has "enabled rules" but ; the chain of possession is more involved. An event name has (truncated) ; rule-classes and runic-mapping-pairs (which are in 1:1 correspondence), ; truncated rule-classes each have a :corollary keyword (or not, in which case ; the event's theorem is to be used), rules are derived from corollaries, rules ; have runes, and runes are enabled or disabled. So we have to deal with this ; mapping. ; Fourth, what do we do if an explicit :tau-system rule cannot be stored as a ; tau rule? This will not happen during the original execution of the ; introductory event: chk-acceptable-rules will detect the problem and signal a ; soft error. But it is possible in subsequent visits to a :tau-system rule. ; For example, perhaps a monadic function symbol that was previously ; recognized as Boolean is no longer so recognized because of ens. We believe ; that even a soft error in this situation is unduly harsh. The user is ; unlikely to know ahead of time that the ens will have that effect. Our ; design is to keep a list of ``lost'' runes to be printed at the end of the ; regeneration process. However, this means we must know while processing ; corollaries whether we are to signal an error or add the rune to the lost ; runes. (defun corollaries-and-runes-of-enabled-rules (tau-system-onlyp rule-classes runic-mapping-pairs ens theorem) ; We map over rule-classes and runic-mapping-pairs in parallel and collect the ; corollaries of those rules that are enabled in ens. We return a list of ; pairs, (formula . rune), where formula is the corollary of a collected rule ; and rune is its rune. If tau-system-onlyp is on, we only collect such ; :tau-system rules, otherwise we collect all types of rules. The given ; rule-classes have been ``truncated,'' which means that the :corollary has ; been omited if the formula was the same as the 'theorem property of the ; event. So this function is also given the theorem property of the event. (cond ((endp rule-classes) nil) (t (let* ((class-name (car (car rule-classes))) (formula (or (cadr (assoc-keyword :corollary (cdr (car rule-classes)))) theorem)) (nume (car (car runic-mapping-pairs))) (rune (cdr (car runic-mapping-pairs))) (rest (corollaries-and-runes-of-enabled-rules tau-system-onlyp (cdr rule-classes) (cdr runic-mapping-pairs) ens theorem))) (cond ((and (or (not tau-system-onlyp) (eq class-name :tau-system)) (enabled-numep nume ens)) (cons (cons formula rune) rest)) (t rest)))))) (defun tau-visit-defthm1 (first-visitp terms-and-runes ens wrld wrld0) ; Terms-and-runes is a list of pairs, each of the form (term . rune), where ; term is a translated term used to create a rule, rune is its rune, and it is ; known that rune is enabled in the ens in question (which is not provided ; here). We extend wrld with the tau rules we can get from the terms and add ; each rune so used to tau-runes. We obey the Tau Msgp Protocol and return (mv ; msgp wrld'); No Change Loser on wrld0. (cond ((endp terms-and-runes) (mv nil wrld)) (t (let ((term (car (car terms-and-runes))) (rune (cdr (car terms-and-runes)))) ; We know rune is enabled since terms-and-runes is constructed that way. (mv-let (msgp wrld) (add-tau-rule first-visitp rune term ens wrld) (cond (msgp (mv msgp wrld0)) (t (tau-visit-defthm1 first-visitp (cdr terms-and-runes) ens wrld wrld0)))))))) (defun tau-visit-defthm (first-visitp name auto-modep ens wrld0) ; This is the function the tau system uses to update the tau database in ; response to a defthm event named name, which has been introduced into wrld0. ; We follow the Tau Msgp Protocol and return (mv msgp wrld'). No Change Loser ; on wrld0. (let* ((classes (getprop name 'classes nil 'current-acl2-world wrld0)) (terms-and-runes (corollaries-and-runes-of-enabled-rules (or (not auto-modep) (assoc-eq :tau-system classes)) classes (getprop name 'runic-mapping-pairs nil 'current-acl2-world wrld0) ens (getprop name 'theorem nil 'current-acl2-world wrld0)))) (tau-visit-defthm1 first-visitp terms-and-runes ens wrld0 wrld0))) (defun tau-visit-event (first-visitp ev-type namex auto-modep ens ctx wrld state) ; This is the function tau uses to visit an event that was carried out in wrld. ; It is assumed that all the function symbols encountered in this event are new ; to tau, because the function symbols were either all new or introductions ; were made chronologically earlier in world and visited by tau before getting ; here. ; First-timep is t if this function is being called from install-event and is ; nil if it is being called while regenerating the tau database. The flag ; affects whether we signal an error or quietly accumulate lost :tau-system ; runes. Ev-type is the name of the primitive event macro that created the ; event. The only values of interest to tau are DEFUN, DEFUNS, ENCAPSULATE, ; DEFCHOOSE, DEFTHM, and DEFAXIOM. Namex is either a list of names, a single ; name, or 0 indicating the names introduced by the event (0 means no names ; were introduced). ; DEFSTOBJ and DEFABSSTOBJ introduce function symbols, some of which are even ; monadic Booleans. But we ignore DEFSTOBJ and DEFABSSTOBJ here. Why? ; Because all of the functions that they introduce are introduced by embedded ; defun events and so we will find defun event-tuples for each. ; ENCAPSULATE introduces function symbols, some of which may be monadic ; Booleans or have other tau properties. But all these properties are ; established by embedded defun/defthm events. ; DEFCHOOSE introduces a function symbol. But at the time of this writing the ; constraint cannot possibly be of itnterest to tau. For example, could a ; defchoose event make fn a monadic Boolean? Not syntactically. We come close ; with: ; (defchoose pick-a-bool (x) (y) (booleanp x)) ; but that would give the constraint: ; (implies (booleanp x) ; (booleanp (pick-a-bool y))) ; from which one could prove (booleanp (pick-a-bool y)). So the axiom that ; exists after the defchoose is not syntactically acceptable for a tau Boolean ; rule and the revelation that pick-a-bool is in fact Boolean only comes via a ; subsequent defthm event which will be visited eventually. By similar ; reasoning, we the defchoose axiom can never be a big-switch or an mv-nth ; synonym. (mv-let (msgp wrld1) (case ev-type (DEFUN (tau-visit-defun namex auto-modep ens wrld)) (DEFUNS (tau-visit-defuns namex auto-modep ens wrld)) ((DEFTHM DEFAXIOM) (tau-visit-defthm first-visitp namex auto-modep ens wrld)) (otherwise (mv nil wrld))) (cond (msgp (er soft ctx "~@0" msgp)) (t (value wrld1))))) ; ----------------------------------------------------------------- ; Using Conjunctive and Signature Rules (defun apply-conjunctive-tau-rule (tau1 tau2 ens wrld) ; This function checks the conditions for a conjunctive tau rule, tau1, to ; apply. It is given tau2, the set of all known recognizers. It returns (mv ; contradictionp sign recog), contradictionp is t if the known facts contradict ; the rule. If contradictionp is nil, then recog determines if the conditions ; are met: recog = nil means the rule cannot be fired. Otherwise, sign/recog ; should be added to set of known recognizers. However, the caller is ; responsible for adding it. Sign is irrelevant if recog = nil. ; Recall: The theorem (implies (and a (not b) c) d) produces the Conjunctive ; rule {a (not b) c (not d)}, represented as a tau. Note that if ; the first three elements are in the set of known recognizers, then we can add ; the negation of the last to the set. More generally, if all but one of the ; elements of the conjunctive rule are known, we can add the negation of the ; unknown one. If every element of the conjunctive rule is known, we have a ; contradiction. (mv-let (contradictionp sign recog) (tau-near-subsetp tau1 tau2 ens wrld) (cond (contradictionp (mv t nil nil)) ; Note: If the rule didn't fire, the returned recog is nil, even though sign ; might be t. (t (mv nil (not sign) recog))))) (defun apply-conjunctive-tau-rules2 (conjunctive-rules tau ens wrld changedp) ; We map over the list of conjunctive tau rules, trying each wrt the known ; recognizers in tau. We extend tau as rules fire and set changedp if any rule ; fires. We return (mv changedp tau'), where tau' may be *tau-contradiction*. ; We assume tau is not *tau-contradiction* to begin with! (cond ((endp conjunctive-rules) (mv changedp tau)) (t (mv-let (contradictionp sign recog) (apply-conjunctive-tau-rule (car conjunctive-rules) tau ens wrld) (cond (contradictionp (mv t *tau-contradiction*)) ((null recog) (apply-conjunctive-tau-rules2 (cdr conjunctive-rules) tau ens wrld changedp)) (t (let ((new-tau (add-to-tau sign recog tau ens wrld))) (cond ((eq new-tau *tau-contradiction*) (mv t *tau-contradiction*)) (t (apply-conjunctive-tau-rules2 (cdr conjunctive-rules) new-tau ens wrld (or changedp (not (equal tau new-tau))))))))))))) (defun apply-conjunctive-tau-rules1 (conjunctive-rules tau ens wrld max-loop) ; This function repeatedly applies conjunctive rules until no changes occur. ; It returns just the extended tau, which might be *tau-contradiction*. We ; assume tau is not *tau-contradiction* to begin with! ; Max-loop is nil or a natp and controls how many times we iterate. Max-loop = ; nil means loop until tau stabilizes. (if (and max-loop (equal max-loop 0)) tau (mv-let (changedp tau) (apply-conjunctive-tau-rules2 conjunctive-rules tau ens wrld nil) (cond ((eq tau *tau-contradiction*) *tau-contradiction*) (changedp (apply-conjunctive-tau-rules1 conjunctive-rules tau ens wrld (if max-loop (- max-loop 1) nil))) (t tau))))) ; On the Tau Completion Alist (calist) ; We call the process of extending a tau with conjunction rules ``completion.'' ; Completion can be quite expensive. We therefore remember all the tau we have ; completed in a given proof and look up the answers when we can. We use a ; simple alist mapping input (partial) tau to output (completed) tau. The ; alist, called calist, is accumulated throughout an entire proof and is stored ; in the pspv when we're not in tau. This mechanism may become too expensive ; if there are a lot of different partial tau involved in a proof. But out ; experience so far is that the number is reasonable. ; For example, consider the community book ; books/centaur/vl/transforms/xf-expr-simp.lisp and the event within it: ; (verify-guards vl-expr-simp). If one modifies these sources so as to ignore ; the values found in the calist (just choose the second t clause below) then ; the verify-guards event named above takes about 42 seconds to process (on a ; 2.6 GHz Intel Core i7 with 16 GB 1600 MHz DDR3 Macbook Pro). If one uses the ; values in calist, then it takes about 6 seconds. In all, 50 different ; partial tau are mentioned in this proof. Completion is called about 41,000 ; times on these various tau -- some as many as 6,000 times each. 49 different ; complete tau are generated. 27 of the 50 partial tau are changed by ; completion and the other 23 are actually already complete. (Note that since ; we know that the val we compute is complete, we could pair it with itself on ; the new calist. However, this does not gain anything in this particular ; test. Apparently we often compute tau and do not even try to subsequently ; complete them so remembering the already completed ones is not much help.) (defun apply-conjunctive-tau-rules (tau ens wrld calist) (cond ((eq tau *tau-contradiction*) (mv *tau-contradiction* calist)) (t (let ((temp (assoc-equal tau calist))) (cond (temp (mv (cdr temp) calist)) (t (let ((val (apply-conjunctive-tau-rules1 (getprop 'tau-conjunctive-rules 'global-value nil 'current-acl2-world wrld) tau ens wrld nil))) (mv val (cons (cons tau val) calist))))))))) (defun add-to-tau! (sign recog tau ens wrld calist) ; Recog is a tau-pair or singleton evg list or else the special symbol :SKIP. ; Tau is a tau object, not *tau-contradiction*. We add sign/recog, its ; implicants, and all conjunctive rules to tau. We return tau', where tau' may ; be *tau-contradiction*. When recog is :SKIP it means ``don't really add ; anything to tau, just apply conjunctive rules!'' (let ((new-tau (if (eq recog :SKIP) tau (add-to-tau sign recog tau ens wrld)))) (cond ((and (not (eq recog :SKIP)) (equal tau new-tau)) (mv tau calist)) (t (apply-conjunctive-tau-rules new-tau ens wrld calist))))) ; ----------------------------------------------------------------- ; Dealing with Terms ; We now turn our attention to computing the recognizer sets of terms ; and maintaining a list of assumptions about terms. We start by ; using signature rules. Then we will define how to use an alist of ; tau assumptions to compute the tau of a term. Then we will define ; how to build an alist of tau assumptions. ; A slightly confusing fact about signature rules is that both form 1 and form ; 2 rules are the same. The first applies to expressions like (fn a b) and the ; second applies to expressions like (mv-nth 'i (fn a b)). To fire a rule, we ; just need to know whether a and b have appropriate tau (and whether the ; dependent hyps are relieved). If a rule fires, we just need to know the sign ; and recog for the value produced. We don't actually need to know fn or i or ; even which form of rule we are applying. (defun all-tau-emptyp (lst) (cond ((endp lst) t) (t (and (equal (car lst) *tau-empty*) (all-tau-emptyp (cdr lst)))))) (defun all-unrestricted-signature-rulesp (sigrules) ; In general, when we see (fn a1 ... an) we will obtain the taus of the actuals, ; and then apply the signature rules of fn to (tau1 ... taun). However, if all ; the rules for fn have unrestricted inputs, e.g., rules like: (fn *tau-empty* ; ... *tau-empty*) ==> sign/recog as happens for cons (consp), reverse ; (true-listp), member (boolean), etc., there no point in even getting the tau ; of the actuals. Of course, we must also make sure there are no dependent ; hyps. (cond ((endp sigrules) t) ((and (null (access signature-rule (car sigrules) :dependent-hyps)) (all-tau-emptyp (access signature-rule (car sigrules) :input-tau-list))) (all-unrestricted-signature-rulesp (cdr sigrules))) (t nil))) ; ----------------------------------------------------------------- ; On Disjoining Tau ; In trying to compute the tau of (IF a b c), we compute the tau of b and c and ; disjoin them. Because taus are conjunctions of all the things we know, ; disjoining them is akin to intersecting their representations. This reduces ; the number of recognizers in them and hence enlarges the set of objects in ; the tau. For example, if b has a tau that includes P and Q and c has a tau ; that includes P and R, their disjunction includes P but not Q or R. ; We lose a lot of precision in disjoining tau. Here are two examples. ; For example, b might include RATIONALP and hence also ACL2-NUMBERP; c might ; include INTEGERP and hence also ACL2-NUMBERP; But their disjunction just ; includes ACL2-NUMBERP. We've lost that it is an integer or a rational. ; Intervals raise another problem. To take an extreme case, suppose the tau of ; b and c are both equalities-to-constant, say =0 and =100. Then their ; respective intervals are 0 <= ... <= 0 and 100 <= ... <= 100 and when we ; ``disjoin'' them the only interval we can create that captures both is 0 <= ; ... <= 100. ; Another problem has to do with the fact that some recognizers that ``ought'' ; to be in a tau are removed when the tau becomes an equality-to-constant. For ; example, we might have two taus both of which contain WEEKDAYP. But then we ; might assert ='MONDAY into one and ='TUESDAY into the other. These explicit ; constants subsume all the :pos-pairs that compute true on them, and we would ; then drop the WEEKDAYP recognizers from each tau. But now if we disjoin the ; two tau, we get the empty tau, even though it would have been legitimate to ; produce {WEEKDAYP} (defun disjoin-intervals (interval1 interval2) ; The two arguments are tau-intervals. We form the interval that stretches ; from the lower lower bound of the two to the upper upper bound, and has as ; its domain the less restrictive of the two domains. ; Motivation: Suppose we are finding the tau of (IF a b c) and suppose we have ; computed the tau of b and c and that they have intervals interval1 and ; interval2. Then a tau for (IF a b c) will have the interval we compute ; here. ; The domain of the new interval is the most less restrictive of the two. For ; example, if one is INTEGERP and the other is RATIONALP, we choose RATIONALP, ; since the first interval is also an interval over the rationals. Note that ; the only way we can produce an INTEGERP interval is if both are INTEGERP. ; One implication is that the chosen endpoints are known to satisfy the ; requirement on INTEGERP intervals of using the relation <= (nil) and integer ; extends, since both endpoints come from INTEGERP intervals. ; A more problematic issue is whether the new domain is derivable from the ; :pos-pairs of the tau into which we put this interval. Our computation of ; the :pos-pairs of the new tau will insure that it is because we will ; explicitly include INTEGERP, RATIONALP, or ACL2-NUMBERP as needed. But it ; was not recognized at first that this would be necessary. In particular, it ; was thought naively that since the domain was derivable from the :pos-pairs ; of both ancestor taus, it would be derivable from their disjunction. But ; that argument is naive because it forgets that the :pos-pairs of ; equality-to-constants is empty! (Recall that when a tau becomes an ; equality-to-constant, we just save those recognizers that won't evaluate ; appropriately on that constant, thus representing all others implicitly.) ; While this handling of the domains of intervals is accurate as far as it ; goes, we really lose a lot of precision in disjoining taus. See the ; discussion On Disjoining Tau. (let* ((dom1 (access tau-interval interval1 :domain)) (dom2 (access tau-interval interval2 :domain)) (dom (cond ((eq dom1 dom2) dom1) ((eq dom1 'INTEGERP) dom2) ((eq dom2 'INTEGERP) dom1) ((eq dom1 'RATIONALP) dom2) ((eq dom2 'RATIONALP) dom1) ((eq dom1 'ACL2-NUMBERP) dom2) (t dom1))) (lo-rel1 (access tau-interval interval1 :lo-rel)) (lo-rel2 (access tau-interval interval2 :lo-rel)) (lo1 (access tau-interval interval1 :lo)) (lo2 (access tau-interval interval2 :lo)) (hi-rel1 (access tau-interval interval1 :hi-rel)) (hi-rel2 (access tau-interval interval2 :hi-rel)) (hi1 (access tau-interval interval1 :hi)) (hi2 (access tau-interval interval2 :hi))) (mv-let (lo-rel lo) (if (lower-bound-> lo-rel1 lo1 lo-rel2 lo2) (mv lo-rel2 lo2) (mv lo-rel1 lo1)) (mv-let (hi-rel hi) (if (upper-bound-< hi-rel1 hi1 hi-rel2 hi2) (mv hi-rel2 hi2) (mv hi-rel1 hi1)) (if (and (null dom) (null lo) (null hi)) nil ; universal interval (make tau-interval :domain dom :lo-rel lo-rel :lo lo :hi-rel hi-rel :hi hi)))))) ; The following theorem establishes the correctness of disjoin-intervals. We ; prove below that if int1 and int2 are intervals containing x and y ; respectively, then their disjunction is an interval that contains x and y. ; Recall that intervalp actually requires that its argument be a cons structure ; whereas we sometimes use nil to represent the universal interval in which all ; fields are nil. Thus, we actually prove that disjoin-intervals is either nil ; or an interval and we allow int1 and int2 the same freedom. ; (verify-termination lower-bound->) ; (verify-termination upper-bound-<) ; (verify-termination disjoin-intervals) ; (thm (implies (and (or (equal int1 nil) (tau-intervalp int1)) ; (or (equal int2 nil) (tau-intervalp int2)) ; (in-tau-intervalp x int1) ; (in-tau-intervalp y int2)) ; (and (or (equal (disjoin-intervals int1 int2) nil) ; (tau-intervalp (disjoin-intervals int1 int2))) ; (in-tau-intervalp x (disjoin-intervals int1 int2)) ; (in-tau-intervalp y (disjoin-intervals int1 int2)) ; ))) ; 52,409 subgoals ; Time: 213.06 seconds (prove: 210.02, print: 3.04, other: 0.00) ; Prover steps counted: 37141155 ; While we're at it we also define how to conjoin two intervals, which ; shrinks them: (defun conjoin-intervals (interval1 interval2) ; The two arguments are tau-intervals. We form the interval that stretches ; from the upper lower bound of the two to the lower upper bound, and has as ; its domain the more restrictive of the two domains. ; Motivation: Suppose we are finding the tau of a term and know the term lies in ; both interval1 and in interval2. Then the conjunction of those two intervals ; is a more restrictive interval that contains the term. ; The domain of the new interval is the more restrictive of the two. For ; example, if one is INTEGERP and the other is RATIONALP, we choose INTEGERP. ; A more problematic issue is whether the new domain is derivable from the ; :pos-pairs of the tau into which we put this interval. Our computation of ; the :pos-pairs of the new tau will insure that it is because we will ; explicitly include INTEGERP, RATIONALP, or ACL2-NUMBERP as needed. ; If the intervals have an empty intersection, we return *tau-empty-interval*. ; As of this writing (January, 2013, version_6.0+) this is the only place that ; we return the empty interval. (let* ((dom1 (access tau-interval interval1 :domain)) (dom2 (access tau-interval interval2 :domain)) (dom (cond ((eq dom1 dom2) dom1) ((eq dom1 'INTEGERP) dom1) ((eq dom2 'INTEGERP) dom2) ((eq dom1 'RATIONALP) dom1) ((eq dom2 'RATIONALP) dom2) ((eq dom1 'ACL2-NUMBERP) dom1) (t dom2))) (lo-rel1 (access tau-interval interval1 :lo-rel)) (lo-rel2 (access tau-interval interval2 :lo-rel)) (lo1 (access tau-interval interval1 :lo)) (lo2 (access tau-interval interval2 :lo)) (hi-rel1 (access tau-interval interval1 :hi-rel)) (hi-rel2 (access tau-interval interval2 :hi-rel)) (hi1 (access tau-interval interval1 :hi)) (hi2 (access tau-interval interval2 :hi))) ; If one interval is INTEGERP and the other is not, we first squeeze the ; non-INTEGERP interval down to its integers. (mv-let (lo-rel1 lo1 hi-rel1 hi1) (if (and (eq dom 'INTEGERP) (not (eq dom1 'INTEGERP))) (mv nil (squeeze-k nil lo-rel1 lo1) nil (squeeze-k t hi-rel1 hi1)) (mv lo-rel1 lo1 hi-rel1 hi1)) (mv-let (lo-rel2 lo2 hi-rel2 hi2) (if (and (eq dom 'INTEGERP) (not (eq dom2 'INTEGERP))) (mv nil (squeeze-k nil lo-rel2 lo2) nil (squeeze-k t hi-rel2 hi2)) (mv lo-rel2 lo2 hi-rel2 hi2)) ; At this point, if either interval is INTEGERP, both are. This is necessary ; because the lower-bound-> and upper-bound-< functions require both intervals ; to have the same domains. (mv-let (lo-rel lo) (if (lower-bound-> lo-rel1 lo1 lo-rel2 lo2) (mv lo-rel1 lo1) (mv lo-rel2 lo2)) (mv-let (hi-rel hi) (if (upper-bound-< hi-rel1 hi1 hi-rel2 hi2) (mv hi-rel1 hi1) (mv hi-rel2 hi2)) (cond ((and (null dom) (null lo) (null hi)) ; universal interval -- Don't confuse this with the empty interval! nil) ((and lo hi (if (or lo-rel hi-rel) (>= lo hi) (> lo hi))) *tau-empty-interval*) (t (make tau-interval :domain dom :lo-rel lo-rel :lo lo :hi-rel hi-rel :hi hi))))))))) ; Here is the correctness of conjoin-intervals. See the discussion of the correctness of ; disjoin-intervals above. ; (verify-termination lower-bound->) ; (verify-termination upper-bound-<) ; (verify-termination squeeze-k) ; (verify-termination conjoin-intervals) ; (include-book "arithmetic-5/top" :dir :system) ; (defthm lemma ; (implies (and (rationalp lo) ; (not (integerp lo)) ; (integerp x) ; (<= lo x)) ; (<= (+ 1 (floor lo 1)) x)) ; :rule-classes :linear) ; (thm (implies (and (or (equal int1 nil) (tau-intervalp int1)) ; (or (equal int2 nil) (tau-intervalp int2)) ; (in-tau-intervalp x int1) ; (in-tau-intervalp x int2)) ; (and (or (equal (conjoin-intervals int1 int2) nil) ; (tau-intervalp (conjoin-intervals int1 int2))) ; (in-tau-intervalp x (conjoin-intervals int1 int2)) ; ))) ; The following establishes that if the conjunction of two intervals is empty ; then the equality of the two objects is false. We exploit this in ; tau-interval-equal-decider. ; ; (thm (implies (and (or (equal int1 nil) (tau-intervalp int1)) ; (or (equal int2 nil) (tau-intervalp int2)) ; (in-tau-intervalp x int1) ; (in-tau-intervalp y int2) ; (equal (conjoin-intervals int1 int2) *tau-empty-interval*)) ; (not (equal x y)))) (defun combine-pos/neg-pairs-from-tau1 (sign pos-evg1 pairs1 pos-evg2 pairs2 ens wrld) ; Pairs1 and pairs2 are lists of tau-pairs, ordered descending by index. ; We intersect them, returning a list of tau-pairs ordered descending. ; However, we implicitly augment each list with the pairs which have been ; dropped because they are true (or false) because of the corresponding ; :pos-evg. ; For example, suppose we are dealing with :pos-pairs (sign=t and both pair1 ; and pair2 are :pos-pairs of their respective tau). Suppose pos-evg1 contains ; the evg 7 and pos-evg2 is nil. Then pairs1 will only contain recognizers ; that cannot be evaluated on 7. Thus, for example, pairs1 will NOT include ; INTEGERP. But pairs2 may contain INTEGERP. Our answer will include INTEGERP ; because it is IMPLICITLY listed in pairs1. ; Referring back to the discussion On Disjoining Tau above, if either of the ; pairs includes WEEKDAYP, we would preserve WEEKDAYP by this hack. But this ; hack will not ``preserve'' WEEKDAYP if it has been made implicit in both tau! ; If both pos-evg1 and pos-evg2 are nil, this function is equivalent to ; intersection-equal. But it is probably faster because it exploits ; the fact that the lists are ordered. (cond ((endp pairs1) (cond ((endp pairs2) nil) ((and pos-evg1 (eq (ev-fncall-w-tau-recog (cdr (car pairs2)) pos-evg1 ens wrld) sign)) (cons (car pairs2) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 nil pos-evg2 (cdr pairs2) ens wrld))) (t (combine-pos/neg-pairs-from-tau1 sign pos-evg1 nil pos-evg2 (cdr pairs2) ens wrld)))) ((endp pairs2) (cond ((and pos-evg2 (eq (ev-fncall-w-tau-recog (cdr (car pairs1)) pos-evg2 ens wrld) sign)) (cons (car pairs1) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 (cdr pairs1) pos-evg2 nil ens wrld))) (t (combine-pos/neg-pairs-from-tau1 sign pos-evg1 (cdr pairs1) pos-evg2 nil ens wrld)))) ((equal (car pairs1) ; We could compare just the indices, but that might (car pairs2)) ; be slower since the pairs are probably EQ (cons (car pairs1) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 (cdr pairs1) pos-evg2 (cdr pairs2) ens wrld))) ((and pos-evg1 (eq (ev-fncall-w-tau-recog (cdr (car pairs2)) pos-evg1 ens wrld) sign)) (cons (car pairs2) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 pairs1 pos-evg2 (cdr pairs2) ens wrld))) ((and pos-evg2 (eq (ev-fncall-w-tau-recog (cdr (car pairs1)) pos-evg2 ens wrld) sign)) (cons (car pairs1) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 (cdr pairs1) pos-evg2 pairs2 ens wrld))) ((> (car (car pairs1)) (car (car pairs2))) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 (cdr pairs1) pos-evg2 pairs2 ens wrld)) (t (combine-pos/neg-pairs-from-tau1 sign pos-evg1 pairs1 pos-evg2 (cdr pairs2) ens wrld)))) (defun augment-pos/neg-pairs-with-implicit-numeric-recogs (pos-evg sign pairs wrld) ; Pairs is the :pos-pairs (or :neg-pairs, as per sign) of some tau whose ; :pos-evg is pos-evg. If :pos-evg is non-nil, then the tau in question is an ; equality-with-constant. If in fact the constant is numeric, we augment pairs ; with the positive (or negative) implicants of the most specific tau-interval ; domain (INTEGERP, RATIONALP, or ACL2-NUMBERP) containing that constant. (if pos-evg (let* ((pred (if (acl2-numberp (car pos-evg)) (cond ((integerp (car pos-evg)) 'INTEGERP) ((rationalp (car pos-evg)) 'RATIONALP) (t 'ACL2-NUMBERP)) nil)) (implicit-pairs (if pred (if sign (access tau (tau-simple-implicants t pred wrld) :pos-pairs) (access tau (tau-simple-implicants t pred wrld) :neg-pairs)) nil))) (merge-car-> pairs implicit-pairs)) pairs)) (defun combine-pos/neg-pairs-from-tau (sign tau1 tau2 ens wrld) (let* ((pos-evg1 (access tau tau1 :pos-evg)) (pos-evg2 (access tau tau2 :pos-evg)) (pairs1 (augment-pos/neg-pairs-with-implicit-numeric-recogs pos-evg1 sign (if sign (access tau tau1 :pos-pairs) (access tau tau1 :neg-pairs)) wrld)) (pairs2 (augment-pos/neg-pairs-with-implicit-numeric-recogs pos-evg2 sign (if sign (access tau tau2 :pos-pairs) (access tau tau2 :neg-pairs)) wrld))) (combine-pos/neg-pairs-from-tau1 sign pos-evg1 pairs1 pos-evg2 pairs2 ens wrld))) (defun combine-tau (tau1 tau2 ens wrld) ; We form a tau, tau3, such that ((M[tau1] --> M[tau3]) & (M[tau2] --> ; M[tau3])). For example, if tau1 includes the recognizers P and Q and tau2 ; includes P and R, then all we can include is P. Thus, tau3 is sort of the ; ``intersection'' of tau1 and tau2, which because of the conjunctive nature of ; taus, is really disjunction. The way we disjoin intervals is to widen them. ; For example, we disjoin the interval 1 <= ... <= 5 with the interval 100 <= ; ... <= 200 to get the interval 1 <= ... <= 200.. ; If neither tau1 nor tau2 is *tau-contradiction*, we don't have to worry about ; consistency because everything in the resulting tau is in both tau1 and tau2, ; which are known to be consistent. But it is possible that one or both of ; tau1 and tau2 is contradictory. Think of *tau-contradiction* as denoting the ; (infinite) set of recognizers of both signs. Then the intersection is the ; other set. ; Note that intersection-equal preserves the order of its first argument; so if ; two lists are ordered according to some criteria, so is their intersection. (cond ((eq tau1 *tau-contradiction*) tau2) ((eq tau2 *tau-contradiction*) tau1) (t ; We avoid consing up *tau-empty* by testing for it first. The computation of ; :pos-evg below is a little strange. We ought to ask whether both are ; non-empty and then compare their contents. But if one is empty and the other ; isn't, then their ``contents'' won't be equal and nil is the correct answer. (let ((pos-evg (if (equal (access tau tau1 :pos-evg) (access tau tau2 :pos-evg)) (access tau tau1 :pos-evg) nil)) (neg-evgs (intersection-equal (access tau tau1 :neg-evgs) (access tau tau2 :neg-evgs))) ; As noted above, disjoining loses a lot of precision. Indeed, before we added ; the augmentation of :pos-pairs by implicit numeric recognizers (as done in ; combine-pos/neg-pairs-from-tau), we could produce tau that violated our ; convention that the domain of every interval is derivable from the ; :pos-pairs. Just consider two tau, =1 and =2, and disjoin them. Their ; empty :pos-pairs produce an empty :pos-pairs EVEN THOUGH the interval ; provided has domain INTEGERP. (pos-pairs (combine-pos/neg-pairs-from-tau t tau1 tau2 ens wrld)) (neg-pairs (combine-pos/neg-pairs-from-tau nil tau1 tau2 ens wrld)) (interval (disjoin-intervals (access tau tau1 :interval) (access tau tau2 :interval)))) (let ((dom (access tau-interval interval :domain)) (lo-rel (access tau-interval interval :lo-rel)) (lo (access tau-interval interval :lo)) (hi-rel (access tau-interval interval :hi-rel)) (hi (access tau-interval interval :hi))) (cond ((and (not (singleton-tau-intervalp lo-rel lo hi-rel hi)) dom (not (tau-pair-member (case dom (INTEGERP *tau-integerp-pair*) (RATIONALP *tau-rationalp-pair*) (otherwise *tau-acl2-numberp-pair*)) pos-pairs))) (er hard 'combine-tau "We have just constructed an interval, ~x0, whose domain is not ~ in the :pos-pairs of the tau we planned to put it into! The ~ offending interval was constructed by disjoin-intervals from ~ the intervals in these two tau ~x1 and ~x2." interval tau1 tau2)) ((or pos-evg neg-evgs pos-pairs neg-pairs interval) (make tau :pos-evg pos-evg :neg-evgs neg-evgs :pos-pairs pos-pairs :neg-pairs neg-pairs :interval interval)) (t *tau-empty*))))))) ; ----------------------------------------------------------------- ; Tau-Term and Tau-Assume ; Tau-term is the function that takes a term and an alist of tau assumptions, ; tau-alist, and computes the tau of the term under the assumptions in ; tau-alist. Tau-assume takes a term and tau-alist and augments the tau-alist ; with the assumption that the term is true (or false as specified by a flag). ; These two functions are mutually recursive. However, before we get to their ; definitions we must deal with various auxiliary issues and helper functions. ; On the Role of Rewriting in Tau ; We have gone back and forth on the question of whether tau-term and ; tau-assume should do any rewriting. The existence of mutually recursive ; recognizers is the motivation of our decision to do some rewriting. If ; recognizers are mutually recursive then the system will involve ``big ; switch'' recognizers that turn into standard recognizer nests depending on ; the value of some flag. (Think of the flagged version of a mutually ; recursive clique of recognizers, except replace the singly recursive calls ; with their mutually recursive but now disabled counterparts.) We must expand ; these big switches when their controlling flags are known. It also pays to ; do this expansion in the context of some tau assumptions, since that permits ; simplification. If we do not do this rewriting in the tau system we spill ; over into the simplifier where much more heavy-duty machinery is invoked. ; But how should rewriting in tau interact with signature rules? Our current ; policy is that rewriting will not expand a function that possesses a tau ; signature rule. The idea is that if the user has provided a signature rule, ; that is what we use. If no signature rule is provided and the function is a ; big switch, we consider expanding it. ; Given this simple heuristic, how do we handle (mv-nth 3 (fn a b))? Clearly ; if there is a form 2 signature rule for this we apply it. But if not, we ; expand (fn a b) and look for a signature rule about the result. In the happy ; event that (fn a b) is a big-switch-like function and expands to (gn b), ; we're in good shape. But if it expands into an IF, we push the mv-nth ; down to the tips of the IF before recurring. (defun push-mv-nth-down (i term) ; We put (MV-NTH i *) around each output of the IF-tree term. (cond ((variablep term) (fcons-term* 'MV-NTH i term)) ((fquotep term) ; We only call this function when i is a quoted natp. Thus, ; we can simply compute the answer here. (kwote (mv-nth (cadr i) (cadr term)))) ((eq (ffn-symb term) 'IF) (fcons-term* 'IF (fargn term 1) (push-mv-nth-down i (fargn term 2)) (push-mv-nth-down i (fargn term 3)))) (t (fcons-term* 'MV-NTH i term)))) ; If term defines a big switch function, then we can partially evaluate a call ; of that function efficiently under a tau-alist. Here is how. (defun tau-expand-big-switch (body switch-var switch-tau var-alist ens wrld) ; Body is the body of a big switch function being applied to some actuals. ; Switch-var is the switch var of that switch, switch-val is the corresponding ; actual expression, and switch-tau is the tau of that actual. It is assumed ; that switch-tau is not *tau-contradiction*. Var-alist is the variable ; substitution mapping formals to actuals. We partially evaluate body under ; the assumptions in tau-alist. We return (mv hitp term') where hitp is T or ; NIL. If hitp is T, term' is provably equal to body/var-alist under ; tau-alist. If hitp is NIL, term' is NIL and irrelevant. The heuristic we ; use to determine whether we hit or not is whether tau-alist allows us to ; navigate past all the switch tests. We have arrived at a leaf if the body no ; longer mentions switch-var. This way we don't have to build the ; instantiation of any part of body except that leaf. (cond ((or (variablep body) (fquotep body) (not (eq (ffn-symb body) 'IF))) ; If we are at a leaf of the big switch, we instantiate the body and we ; report hitp = t. Otherwise, we report hitp = nil. We are at a leaf ; if the switch-var no longer occurs in the body. (cond ((occur switch-var body) (mv nil nil)) (t (mv t (sublis-var var-alist body))))) (t (mv-let (sign recog e criterion) (tau-like-term (fargn body 1) :various-any wrld) (declare (ignore criterion)) (cond ((and recog (eq e switch-var)) ; Body is of the form (IF (sign/recog switch-var) ...) (let ((temp (reduce-sign/recog switch-tau sign recog ens wrld))) (cond ((eq temp t) (tau-expand-big-switch (fargn body 2) switch-var switch-tau var-alist ens wrld)) ((eq temp nil) (tau-expand-big-switch (fargn body 3) switch-var switch-tau var-alist ens wrld)) (t (mv nil nil))))) (t (mv nil nil))))))) ; ----------------------------------------------------------------- ; Closing in On Tau-Term and Tau-Assume ; Because conjunction is treated as a special case in tau-assume, we must be ; able to deconstruct IFs that are really conjunctions. (defun deconstruct-conjunction (sign a b c) ; Consider sign/(IF a b c). There are four cases in which this is a ; conjunction. ; sign=t c=nil (and a b) ; sign=t b=nil (and (not a) c) ; sign=nil b=a or b=T (and (not a) (not c)) ; sign=nil c=t (and a (not b)) ; We return (mv conjunctionp sign1 conjunct1 sign2 conjunct2), where either ; conjunctionp is nil (meaning sign/(IF a b c) is not a conjunction), or else ; conjunctionp is t and sign/(IF a b c) <--> (AND sign1/conjunct1 ; sign2/conjunct2) (if sign (if (equal c *nil*) (mv t t a t b) (if (equal b *nil*) (mv t nil a t c) (mv nil nil nil nil nil))) (if (or (equal a b) (equal b *t*)) (mv t nil a nil c) (if (equal c *t*) (mv t t a nil b) (mv nil nil nil nil nil))))) ; Finally: the definitions of tau-term and tau-assume! (defun ok-to-fire-signature-rulep1 (formal-tau-lst actual-tau-lst ens wrld) ; Note: The claim below that formal-tau-lst and actual-tau-lst are equal-length ; is not quite true! See note below or Abuse of Tau Representation. ; This function takes two equal-length lists of tau and compares corresponding ; ones. For each pair, formal-tau and actual-tau, we check that every ; recognizer in formal-tau is included in actual-tau. In general, each ; actual-tau is much bigger than its corresponding formal-tau because the ; formal-tau only includes the specific recognizer(s) mentioned in a signature ; theorem while the actual-tau includes all the implicants. So, for example, ; formal-tau might be {integerp} but actual-tau might be {natp integerp ; rationalp}. What we are really checking is that any object satisfying all ; the actual-tau recognizers will satisfy all the formal-tau recognizers, i.e., ; (implies M[actual-tau] M[formal-tau]). We return t or nil. ; Note: We sometimes call this function with tau-lst2 = nil, knowing that every ; element will be nil and that when nil is treated like a tau it behaves like ; *tau-empty*! Note that the only corresponding formal-tau that would be ; acceptable is the empty one. (cond ((endp formal-tau-lst) t) ((tau-implies (car actual-tau-lst) (car formal-tau-lst) ens wrld) (ok-to-fire-signature-rulep1 (cdr formal-tau-lst) (cdr actual-tau-lst) ens wrld)) (t nil))) (defun smart-member-equal-+- (lit clause) ; We return '+ if lit is a member of clause. We return '- if the complement of ; lit is a member of clause. Otherwise we return nil. If both conditions are ; met, we return either '+ or '- depending on which occurs first. For example, ; let clause be '(A (NOT B)). Then if lit is A we return '+. If lit is (NOT ; A) we return '-. We also return '- when lit is B. If lit is C we return ; nil. (cond ((null clause) nil) ((equal lit (car clause)) '+) ((let ((lit1 lit) (lit2 (car clause))) (or (and (nvariablep lit1) (not (fquotep lit1)) (eq (ffn-symb lit1) 'not) (equal (fargn lit1 1) lit2)) (and (nvariablep lit2) (not (fquotep lit2)) (eq (ffn-symb lit2) 'not) (equal (fargn lit2 1) lit1)))) '-) (t (smart-member-equal-+- lit (cdr clause))))) ; Here are useful trace$ commands for tau-term and tau-assume: ; (trace$ (tau-term :entry ; (list 'tau-term (car arglist) ; (decode-tau-alist (cadr arglist) nil) (caddr arglist)) ; :exit ; (list 'tau-term (if (eq value *tau-contradiction*) value ; (decode-tau value (car arglist)))))) ; ; (trace$ (tau-assume :entry (list 'tau-assume ; (if (car arglist) 'positive 'negative) ; (cadr arglist) ; (decode-tau-alist (caddr arglist) nil) ; (cadddr arglist)) ; :exit (list 'tau-assume ; (if (nth 0 values) 'contradiction! nil) ; (if (nth 1 values) 'must-be-true! nil) ; (if (nth 2 values) 'must-be-false! nil) ; (decode-tau-alist (nth 3 values) nil)))) (defmacro recursivep (fn wrld) ; Experiments show a slight speedup in Allegro CL (perhaps a half percent on a ; very small run) if we make this a macro. `(access def-body (def-body ,fn ,wrld) :recursivep)) (defun find-first-acceptable-domain (actual-dom acceptable-domains) ; Actual-dom is an interval domain, i.e., INTEGERP, RATIONALP, ACL2-NUMBERP, or ; NIL, of an interval known to contain the value of some actual parameter to a ; call of some (unspecified) function fn. Acceptable-domains is a list of the ; interval domains acceptable for a bounder of fn for that same argument of fn. ; We determine whether actual-dom is one the acceptable ones. In the most ; trivial situation, we might expect the actual domain to be something like ; INTEGERP and the acceptable domains to be (INTEGERP RATIONALP). But we ; consider INTEGERP to be ok for the acceptable domains (RATIONALP ; ACL2-NUMBERP) too, because if the actual is an integer then it is also a ; rational. Therefore we actually return two results, (mv flg dom). Flg is t ; or nil indicating whether actual-dom is acceptable, and dom is the first ; listed acceptable domain that includes the actual one. For example, given ; actual-dom = 'INTEGERP and acceptable-domains = '(RATIONALP ACL2-NUMBERP), we ; return (mv t 'RATIONALP). Note that if the acceptable domains are presented ; in the reversed order we'd get (mv t 'ACL2-NUMBERP). It would be best to ; keep the acceptable domains ordered from most to least restrictive. (cond ((endp acceptable-domains) (mv nil nil)) ((eq actual-dom (car acceptable-domains)) (mv t actual-dom)) (t (let* ((more-specific-acceptable-domains (cdr (assoc-eq (car acceptable-domains) '((rationalp integerp) (acl2-numberp rationalp integerp) (nil acl2-numberp rationalp integerp))))) (winner (member-eq actual-dom more-specific-acceptable-domains))) (cond (winner (mv t (car acceptable-domains))) (t (find-first-acceptable-domain actual-dom (cdr acceptable-domains)))))))) (defun tau-lst-with-acceptable-domainsp (actual-tau-lst doms-lst) ; Actual-tau-lst is a list of the tau of successive arguments to some call of ; some function fn. Doms-lst is the :acceptable-domains field of a ; bounder-correctness rule for fn. We check that the actual tau satisfy ; the acceptable domain criteria of the rule. ; An actual in domain INTEGERP satisfies a bounder with an acceptable domains ; (RATIONALP ACL2-NUMBERP). Note that it would be a mistake to pass such an ; interval to the corresponding bounder! The bounder was proved correct under ; the assumption that the domain was either RATIONALP or ACL2-NUMBERP. Thus, ; the bounder could actually look at the domain of its argument and do ; something completely wrong for INTEGERP and still be proved correct. ; However, an INTEGERP interval is contained within the corresponding RATIONALP ; interval, so we can modify the actual interval by enlarging the domain to ; RATIONALP and get a correct answer from the bounder. ; But note that this is a two-step process. First we check that all the ; actuals have acceptable domains. Then we enlarge the actual intervals as ; required and apply the bounder. This function just checks acceptability. (cond ((endp doms-lst) t) ((eq (car doms-lst) t) (tau-lst-with-acceptable-domainsp (cdr actual-tau-lst) (cdr doms-lst))) (t (mv-let (flg dom) (find-first-acceptable-domain (access tau-interval (access tau (car actual-tau-lst) :interval) :domain) (car doms-lst)) (declare (ignore dom)) (and flg (tau-lst-with-acceptable-domainsp (cdr actual-tau-lst) (cdr doms-lst))))))) (defun collect-bounder-args (actual-tau-lst doms-lst bounder-args) ; Actual-tau-lst is a list of tau corresponding to the arguments of some ; (unspecified) function, fn. Doms-lst is the corresponding list of acceptable ; domains for some bounder function. Bounder-args is a list of naturals ; specifying the positions of the relevant arguments for the bounder function. ; For example, actual-tau-lst might be (t0 t1 t2 t3) and the bounder function ; requires in its first argument the interval from t3 and in its second ; argument the interval from t1. Then bounder-args would be (3 1). ; We collect the intervals of those tau (whose positions are) listed in ; bounder-args and we change the domain of each collected interval to be the ; domain that admitted the actual one. So for example, if we collect the ; interval from t3 and its domain is INTEGERP but the acceptable domains there ; are (RATIONALP ACL2-NUMBERP), the we change the interval's domain to ; RATIONALP before collecting it. (cond ((endp bounder-args) nil) (t (let ((actual-int (access tau (nth (car bounder-args) actual-tau-lst) :interval)) (acceptable-doms (nth (car bounder-args) doms-lst))) (mv-let (flg dom) (find-first-acceptable-domain (access tau-interval actual-int :domain) acceptable-doms) (declare (ignore flg)) (cons (change tau-interval actual-int :domain dom) (collect-bounder-args actual-tau-lst doms-lst (cdr bounder-args)))))))) (defun bounding-interval (bc actual-tau-lst wrld) ; Bc is a bounder-correctness record for some fn and actual-tau-lst is the list ; of tau of the actuals of a call of fn. If those actuals satisfy the domains ; proved acceptable for the bounder, we apply the bounder appropriately and ; return the resulting interval. Otherwise, we return nil -- which is the ; universal interval. (let ((doms-lst (access bounder-correctness bc :acceptable-domains))) (cond ((tau-lst-with-acceptable-domainsp actual-tau-lst doms-lst) (let ((bounder-fn (access bounder-correctness bc :bounder-fn)) (bounder-args (collect-bounder-args actual-tau-lst doms-lst (access bounder-correctness bc :bounder-args)))) (mv-let (erp val) (ev-fncall-w bounder-fn bounder-args wrld nil nil t t nil) (cond (erp nil) (t val))))) (t nil)))) (defun conjoin-bounding-intervals (bounders actual-tau-lst wrld) ; Given a list of bounder-correctness records for some function and the actual ; tau for a call of that function, we conjoin together all the intervals the ; bounders compute for the call. This interval will contain the value of the ; call. (cond ((endp bounders) nil) (t (conjoin-intervals (bounding-interval (car bounders) actual-tau-lst wrld) (conjoin-bounding-intervals (cdr bounders) actual-tau-lst wrld))))) (defun apply-tau-bounders (bounders actual-tau-lst ens wrld calist) ; Given the bounders for a function and the tau of its actuals, we compute the ; tau of the call -- as computed just by the bounders of the function. This ; function does not take account of the signature rules for the function. We ; return (mv tau' calist'). (let ((int (conjoin-bounding-intervals bounders actual-tau-lst wrld))) (cond ((null int) (mv *tau-empty* calist)) ((tau-empty-intervalp int) (mv *tau-contradiction* calist)) (t (let ((dom (access tau-interval int :domain)) (lo-rel (access tau-interval int :lo-rel)) (lo (access tau-interval int :lo)) (hi-rel (access tau-interval int :hi-rel)) (hi (access tau-interval int :hi))) (cond ((and (null dom) (null lo) (null hi)) (mv *tau-empty* calist)) (t (let* ((dom-pair (case dom (integerp *tau-integerp-pair*) (rationalp *tau-rationalp-pair*) (acl2-numberp *tau-acl2-numberp-pair*) (otherwise nil))) (tau1 (if (null dom-pair) *tau-empty* (add-to-tau t dom-pair *tau-empty* ens wrld)))) (mv-let (sign k token) (tau-interval-endpoint-to-sign-k-token nil lo-rel lo) (let ((tau2 (if (null k) tau1 (add-to-tau sign (cons k token) tau1 ens wrld)))) (mv-let (sign k token) (tau-interval-endpoint-to-sign-k-token t hi-rel hi) (add-to-tau! sign (if (null k) :SKIP (cons k token)) tau2 ens wrld calist)))))))))))) ; The following two functions are used in tau-term to use intervals to ; determine whether (EQUAL x y) and (< x y) are T or NIL (or unknown). (defun tau-interval-equal-decider (int1 int2) ; If x is in interval int1 and y is in interval int2, what can we say about ; (equal x y)? The possible answers are T, NIL, or ?. The only way we can ; answer T is if both intervals are identities with the same constant. We can ; answer NIL if the intervals do not intersect. Otherwise, we return ?. We ; determine whether the intervals intersect by conjoining them and looking for ; the empty interval. A theorem in a comment after conjoin-interval ; establishes the soundness of this use of conjoin-intervals. (cond ((and (identity-intervalp int1) (identity-intervalp int2) (equal (access tau-interval int1 :lo) (access tau-interval int2 :lo))) t) ((equal (conjoin-intervals int1 int2) *tau-empty-interval*) nil) (t '?))) ; (verify-termination lower-bound->) ; (verify-termination upper-bound-<) ; (verify-termination squeeze-k) ; (verify-termination conjoin-intervals) ; (include-book "arithmetic-5/top" :dir :system) ; (verify-termination identity-intervalp) ; (verify-termination tau-interval-equal-decider) ; (defthm lemma ; (implies (and (rationalp lo) ; (not (integerp lo)) ; (integerp x) ; (<= lo x)) ; (<= (+ 1 (floor lo 1)) x)) ; :rule-classes :linear) ; Below we prove that the EQUAL decider is correct when it returns ; t or nil and we make no claims about it when it returns ?. ; (thm (implies (and (or (null int1) (tau-intervalp int1)) ; (or (null int2) (tau-intervalp int2)) ; (in-tau-intervalp x int1) ; (in-tau-intervalp y int2) ; (eq (tau-interval-equal-decider int1 int2) t)) ; (equal x y))) ; (thm (implies (and (or (null int1) (tau-intervalp int1)) ; (or (null int2) (tau-intervalp int2)) ; (in-tau-intervalp x int1) ; (in-tau-intervalp y int2) ; (eq (tau-interval-equal-decider int1 int2) nil)) ; (not (equal x y)))) (defun tau-interval-<-decider (int1 int2) ; If x is in interval int1 and y is in interval int2, then what can we say ; about (< x y)? Basically, we return t if int1 is entirely below int2, nil if ; int1 is above int2, and ? otherwise. (let ((lo-rel1 (access tau-interval int1 :lo-rel)) (lo1 (access tau-interval int1 :lo)) (hi-rel1 (access tau-interval int1 :hi-rel)) (hi1 (access tau-interval int1 :hi)) (lo-rel2 (access tau-interval int2 :lo-rel)) (lo2 (access tau-interval int2 :lo)) (hi-rel2 (access tau-interval int2 :hi-rel)) (hi2 (access tau-interval int2 :hi))) (cond ((and hi1 lo2 ( 0. Let i be the position of a given ; literal. Then we will assign that literal i if it is a negative variable ; equality, i+max, if it a positive variable equality or other variable ; recognizer, and i+2max otherwise. We call this number the key number of the ; literal. (defun annotate-clause-with-key-numbers (clause max i wrld) ; We replace each lit of clause with (k i lit), where k is its key number and i ; is its position in the original clause. To sort the resulting list for ; purposes of tau-assume, use merge-sort-car-<. To restore that sorted list to ; its original order use merge-sort-cadr-< (cond ((endp clause) nil) (t (let ((lit (car clause))) (mv-let (rsign recog e criterion) (tau-like-term lit :various-any wrld) (declare (ignore rsign criterion)) (cons (list (if (and recog (variablep e)) (if (cdr recog) ; i.e., recog = (i . fn), not (evg). (+ i (* 2 max)) i) (+ i (* 3 max))) i lit) (annotate-clause-with-key-numbers (cdr clause) max (+ 1 i) wrld))))))) (defun tau-clause1p (triples tau-alist type-alist pot-lst ens wrld calist) ; Warning: type-alist and pot-lst are always nil! See the Note at the top of ; mutual-recursion clique defining tau-term and tau-assume. ; Triples is some tail of an annotated clause (that has been reordered by its ; key numbers). Tau-alist assumes the falsity of every literal of the original ; reordered annotation PRECEDING the first literal in triples. Similarly, ; type-alist and pot-list record the falsity of every literal and are used to ; relieve dependent hyps. See On Loops in Relieving Dependent Hyps in Tau ; Signature Rules for a note about our weakness at relieving these hyps. ; We successively assume the falsity of every literal in triples. We return ; either T or NIL, where T means the annotated clause is true by tau reasoning ; and NIL means we could not prove it by tau reasoning. (cond ((endp triples) (mv nil calist)) (t (mv-let (contradictionp mbt mbf tau-alist calist) (tau-assume nil (caddr (car triples)) tau-alist type-alist pot-lst ens wrld calist) (declare (ignore mbt)) ; Note that we assume the truth of (NOT lit). If (NOT lit) must be false, then ; lit must be true and the clause is true. If (NOT lit) must be true, lit must ; be false and we can drop it from the clause. If we get a contradiction then ; we can prove the clause is true. (cond (contradictionp (mv t calist)) (mbf (mv t calist)) (t (tau-clause1p (cdr triples) tau-alist type-alist pot-lst ens wrld calist))))))) ; We are now almost ready to define tau-clausep, the function that recognizes ; true clauses based on tau reasoning... except we can't because we might need ; to construct a type-alist and pot-lst for use in relieving dependent hyps and ; need that machinery. So we delay the definition of tau-clausep until ; prove.lisp. ; ----------------------------------------------------------------- ; Gathering Data about the TAU World ; At the bottom of this section you'll find two forms that print stats. (defun tau-get-all-preds (wrld ans) (cond ((endp wrld) ans) ((eq (cadr (car wrld)) 'tau-pair) (tau-get-all-preds (cdr wrld) (add-to-set-eq (car (car wrld)) ans))) (t (tau-get-all-preds (cdr wrld) ans)))) (defun tau-get-stats-new-max-block (pred prop val-len block) ; Block is a data structure that maintains information about the two longest ; implicant lists seen so far. We accumulate into it the current implicant ; list. (case-match block (((':MAX max-pred max-prop max-len) (':NEXT & & next-len)) (cond ((> val-len max-len) `((:MAX ,pred ,prop ,val-len) (:NEXT ,max-pred ,max-prop ,max-len))) ((> val-len next-len) `((:MAX ,max-pred ,max-prop ,max-len) (:NEXT ,pred ,prop ,val-len))) (t block))) (& block))) (defun tau-size (tau) (+ (if (access tau tau :pos-evg) 1 0) (length (access tau tau :neg-evgs)) (length (access tau tau :pos-pairs)) (length (access tau tau :neg-pairs)))) (defun tau-get-stats-on-implicant-sizes (preds wrld ans max-block) ; Max2 is (pred prop . value) for the longest implicant list. ; Max1 is the second longest. The -lens are the corresponding lengths. (cond ((endp preds) (list (merge-sort-lexorder ans) max-block)) (t (let ((pos-size (tau-size (getprop (car preds) 'pos-implicants nil 'current-acl2-world wrld))) (neg-size (tau-size (getprop (car preds) 'neg-implicants nil 'current-acl2-world wrld)))) (tau-get-stats-on-implicant-sizes (cdr preds) wrld (cons pos-size (cons neg-size ans)) (tau-get-stats-new-max-block (car preds) 'neg-implicants neg-size (tau-get-stats-new-max-block (car preds) 'pos-implicants pos-size max-block))))))) (defun decode-recog (sign recog e) (let* ((discriminator (cdr recog)) (atm (cond ((null discriminator) `(equal ,e (quote ,@recog))) ((eq discriminator :lessp-k-x) `(< ',(car discriminator) ,e)) ((eq discriminator :lessp-x-k) `(< ,e ',(car discriminator))) (t `(,discriminator ,e))))) (if sign atm `(not ,atm)))) (defun decode-recog-lst (sign lst e) (cond ((endp lst) nil) (t (cons (decode-recog sign (car lst) e) (decode-recog-lst sign (cdr lst) e))))) (defun decode-tau1 (tau e) (append (decode-recog-lst t (if (access tau tau :pos-evg) (list (access tau tau :pos-evg)) nil) e) (decode-recog-lst t (revappend (access tau tau :pos-pairs) nil) e) (decode-tau-interval (access tau tau :interval) e t) (decode-recog-lst nil (access tau tau :neg-evgs) e) (decode-recog-lst nil (revappend (access tau tau :neg-pairs) nil) e))) (defun decode-tau (tau e) (cond ((eq tau *tau-contradiction*) nil) (t (let ((terms (decode-tau1 tau e))) (cond ((endp terms) t) ((endp (cdr terms)) (car terms)) (t `(and ,@terms))))))) ; On Tau Debugging Features ; These functions and macros are for debugging only. They're not used by ; our code. ; (program) ; ; (mutual-recursion ; (defun ptrans (term) ; ; Like translate, but very limited: quote unquoted constants and expand ; ; >, <=, and >=. ; (cond ((atom term) ; (cond ; ((or (eq term nil) ; (eq term t)) ; (list 'quote term)) ; ((symbolp term) term) ; ((or (acl2-numberp term) ; (stringp term) ; (characterp term)) ; (list 'quote term)) ; (t term))) ; ((eq (car term) 'quote) ; term) ; ((member (car term) '(> <= >=)) ; (let ((a1 (ptrans (nth 1 term))) ; (a2 (ptrans (nth 2 term)))) ; (case (car term) ; (> `(< ,a2 ,a1)) ; (<= `(not (< ,a2 ,a1))) ; (otherwise `(not (< ,a1 ,a2)))))) ; (t (cons (car term) ; (ptrans-lst (cdr term)))))) ; (defun ptrans-lst (terms) ; (cond ((endp terms) nil) ; (t (cons (ptrans (car terms)) ; (ptrans-lst (cdr terms))))))) ; ; ; (mktau nil (natp e) (<= e 100)) produces a tau representing just those two ; ; recognizers. Replacing the nil by t produces a ``complete'' tau with the ; ; implicants. Mktau! makes such a tau and then decodes it back to a pseudo-term. ; ; (defmacro mktau (complete-flg &rest terms) ; `(convert-tau-like-terms-to-tau ,complete-flg ',(ptrans-lst terms) ; (ens state) (w state))) ; ; (defmacro mktau! (complete-flg &rest terms) ; `(decode-tau ; (convert-tau-like-terms-to-tau ,complete-flg ; ',(ptrans-lst terms) ; (ens state) ; (w state)) ; 'e)) ; ; (logic) (defun decode-tau-conjunctive-rule (tau) (cond ((eq tau *tau-contradiction*) nil) (t (let* ((terms (decode-tau1 tau 'v))) ; Terms ought to contain at least three literals if it is really a conjunctive ; rule. It cannot be nil because of the test above. If it contains just 1 ; literal it could be thought of as the pathological conjunctive rule (t & t) ; --> p, where tau is -p. Similarly for two literals. (cond ((null (cdr terms)) (dumb-negate-lit (car terms))) (t (let ((concl (dumb-negate-lit (car (last terms)))) (hyps (all-but-last terms))) (cond ((null hyps) (dumb-negate-lit concl)) ((null (cdr hyps)) `(implies ,(car hyps) ,concl)) (t `(implies (and ,@hyps) ,concl)))))))))) (defun decode-tau-conjunctive-rules (lst) (cond ((endp lst) nil) (t (cons (decode-tau-conjunctive-rule (car lst)) (decode-tau-conjunctive-rules (cdr lst)))))) (defun decode-tau-lst (lst e-lst) (cond ((endp lst) nil) (t (let ((hyp (decode-tau (car lst) (car e-lst)))) (cond ((eq hyp t) (decode-tau-lst (cdr lst) (cdr e-lst))) (t (cons hyp (decode-tau-lst (cdr lst) (cdr e-lst))))))))) (defun decode-tau-alist (alist seen) (cond ((endp alist) nil) ((member-equal (car (car alist)) seen) (decode-tau-alist (cdr alist) seen)) (t (cons (decode-tau (cdr (car alist)) (car (car alist))) (decode-tau-alist (cdr alist) (cons (car (car alist)) seen)))))) (defun decode-tau-signature-rule (flg fn sr wrld) ; If flg is nil, we decode sr as a form 1 rule about (fn v1 ... vk). If flg is ; non-nil, it is some natural i and we decode sr as a form 2 rule about (mv-nth ; i (fn v1 ... vk)). (cond ((and (symbolp fn) (arity fn wrld)) (let* ((vars (access signature-rule sr :vars)) (input-tau-list (access signature-rule sr :input-tau-list)) (dependent-hyps (access signature-rule sr :dependent-hyps)) (output-recog (access signature-rule sr :output-recog)) (output-sign (access signature-rule sr :output-sign)) (discriminator (cdr output-recog)) (eterm (if (null flg) `(,fn ,@vars) `(mv-nth ,flg (,fn ,@vars)))) (hyps (append (decode-tau-lst input-tau-list vars) dependent-hyps)) (concl-atm (cond ((null discriminator) `(equal ,eterm ,(kwote (car (access signature-rule sr :output-recog))))) ((eq discriminator :lessp-k-x) `(< ,(kwote (car output-recog)) ,eterm)) ((eq discriminator :lessp-x-k) `(< ,eterm ,(kwote (car output-recog)))) (t `(,discriminator ,eterm)))) (concl (if output-sign concl-atm (fcons-term* 'not concl-atm)))) (reprettyify hyps concl wrld))) (t nil))) (defun decode-tau-signature-rules1 (flg fn sr-lst wrld) (cond ((endp sr-lst) nil) (t (cons (decode-tau-signature-rule flg fn (car sr-lst) wrld) (decode-tau-signature-rules1 flg fn (cdr sr-lst) wrld))))) (defun decode-tau-signature-rules2 (i fn mv-lst wrld) (cond ((endp mv-lst) nil) (t (append (decode-tau-signature-rules1 i fn (car mv-lst) wrld) (decode-tau-signature-rules2 (+ 1 i) fn (cdr mv-lst) wrld))))) (defun decode-tau-signature-rules (flg fn sr-lst wrld) (let ((temp (cond ((null flg) (decode-tau-signature-rules1 nil fn sr-lst wrld)) (t (decode-tau-signature-rules2 0 fn sr-lst wrld))))) (cond ((null temp) t) ((null (cdr temp)) (car temp)) (t `(and ,@temp))))) ; We compute various counts relating to signature rules: ; - fn-cnt-1: how many functions have form 1 signatures ONLY ; - fn-cnt-2: how many functions have form 2 signatures ONLY ; - fn-cnt-1-and-2: how many functions have BOTH form 1 and 2 signatures ; - multi-sig-cnt-1: how many functions have more than 1 form 1 signature ; - multi-sig-cnt-2: how many functions have more than 1 form 2 signature ; for some slot ; - multi-sig-cnt-alist: for each fn with with more than one signature ; (of either form) (defun tau-get-all-sig-fns (wrld fns-seen) (cond ((endp wrld) fns-seen) ((and (or (eq (cadr (car wrld)) 'signature-rules-form-1) (eq (cadr (car wrld)) 'signature-rules-form-2)) (not (member-eq (car (car wrld)) fns-seen))) (tau-get-all-sig-fns (cdr wrld) (cons (car (car wrld)) fns-seen))) (t (tau-get-all-sig-fns (cdr wrld) fns-seen)))) (defun some-slot-has-multiple-tau-sigs (slots) (cond ((endp slots) nil) ((> (length (car slots)) 1) t) (t (some-slot-has-multiple-tau-sigs (cdr slots))))) (defun count-tau-sigs-by-slot (slots) (cond ((endp slots) nil) (t (cons (length (car slots)) (count-tau-sigs-by-slot (cdr slots)))))) (defun collect-rules-with-dependent-hyps (fn i rules wrld ans) (cond ((endp rules) ans) (t (collect-rules-with-dependent-hyps fn i (cdr rules) wrld (if (access signature-rule (car rules) :dependent-hyps) (cons (decode-tau-signature-rule i fn (car rules) wrld) ans) ans))))) (defun collect-rules-with-dependent-hyps-across-mv (fn i mv-lst wrld ans) (cond ((endp mv-lst) ans) (t (collect-rules-with-dependent-hyps-across-mv fn (+ i 1) (cdr mv-lst) wrld (collect-rules-with-dependent-hyps fn i (car mv-lst) wrld ans))))) (defun tau-get-stats-on-signatures1 (fn wrld fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2 multi-sig-cnt-1 multi-sig-cnt-2 multi-sig-cnt-alist rules-with-dependent-hyps) (let ((sigs1 (getprop fn 'signature-rules-form-1 nil 'current-acl2-world wrld)) (sigs2 (getprop fn 'signature-rules-form-2 nil 'current-acl2-world wrld))) (mv-let (fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2) (cond ((and sigs1 sigs2) (mv fn-cnt-1 fn-cnt-2 (+ 1 fn-cnt-1-and-2))) (sigs1 (mv (+ 1 fn-cnt-1) fn-cnt-2 fn-cnt-1-and-2)) (t (mv fn-cnt-1 (+ 1 fn-cnt-2) fn-cnt-1-and-2))) (let* ((multi-sig-cnt-1 (if (> (length sigs1) 1) (+ 1 multi-sig-cnt-1) multi-sig-cnt-1)) (multi-sig-cnt-2 (if (some-slot-has-multiple-tau-sigs sigs2) (+ 1 multi-sig-cnt-2) multi-sig-cnt-2)) (multi-sig-cnt-alist (cond ((or (> (length sigs1) 1) (some-slot-has-multiple-tau-sigs sigs2)) (cons `(,fn ,(length sigs1) (mv ,@(count-tau-sigs-by-slot sigs2))) multi-sig-cnt-alist)) (t multi-sig-cnt-alist))) (rules-with-dependent-hyps (collect-rules-with-dependent-hyps-across-mv fn 0 sigs2 wrld (collect-rules-with-dependent-hyps fn nil sigs1 wrld rules-with-dependent-hyps)))) (mv fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2 multi-sig-cnt-1 multi-sig-cnt-2 multi-sig-cnt-alist rules-with-dependent-hyps))))) (defun tau-get-stats-on-signatures (fns wrld fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2 multi-sig-cnt-1 multi-sig-cnt-2 multi-sig-cnt-alist rules-with-dependent-hyps) (cond ((endp fns) `((:fns-with-signatures (:form-1-only ,fn-cnt-1) (:form-2-only ,fn-cnt-2) (:both-forms ,fn-cnt-1-and-2)) (:fns-with-multiple-sigs (:form-1 ,multi-sig-cnt-1) (:form-2 ,multi-sig-cnt-2)) (:fn-with-multiple-sigs-details (fn form-1-cnt (mv slot-1-cnt dot-dot-dot slot-k-cnt)) ,@multi-sig-cnt-alist) (:rules-with-dependent-hyps ,(length rules-with-dependent-hyps) ,(revappend rules-with-dependent-hyps nil)))) (t (mv-let (fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2 multi-sig-cnt-1 multi-sig-cnt-2 multi-sig-cnt-alist rules-with-dependent-hyps) (tau-get-stats-on-signatures1 (car fns) wrld fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2 multi-sig-cnt-1 multi-sig-cnt-2 multi-sig-cnt-alist rules-with-dependent-hyps) (tau-get-stats-on-signatures (cdr fns) wrld fn-cnt-1 fn-cnt-2 fn-cnt-1-and-2 multi-sig-cnt-1 multi-sig-cnt-2 multi-sig-cnt-alist rules-with-dependent-hyps))))) (defun collect-tau-big-switches (wrld ans) (cond ((endp wrld) ans) ((eq (cadr (car wrld)) 'big-switch) (collect-tau-big-switches (cdr wrld) (add-to-set-eq (car (car wrld)) ans))) (t (collect-tau-big-switches (cdr wrld) ans)))) (defun tau-sum-lst (x) ; This function should be called sum-lst, but we put a ``tau'' in it ; just to allow a function named ``sum-lst'' to be defined by the user. (cond ((endp x) 0) (t (+ (car x) (tau-sum-lst (cdr x)))))) (defun tau-get-stats (wrld) (let* ((preds (tau-get-all-preds wrld nil)) (fns (tau-get-all-sig-fns wrld nil)) (implicant-sizes (tau-get-stats-on-implicant-sizes preds wrld nil `((:MAX nil nil 0) (:NEXT nil nil 0)))) (implicants (car implicant-sizes)) ; really this is list of sizes (sum-size (tau-sum-lst implicants))) `((:preds ,(length preds)) (:implicant-sizes ,implicant-sizes) (:average-implicant-size ,(floor sum-size (length implicants)) -- ,(ceiling sum-size (length implicants))) (:median-implicant-size ,(if (evenp (length implicants)) `(,(nth (- (floor (length implicants) 2) 1) implicants) -- ,(nth (floor (length implicants) 2) implicants)) (nth (floor (length implicants) 2) implicants))) (:conjunctive-rules ,(length (getprop 'tau-conjunctive-rules 'global-value nil 'current-acl2-world wrld))) ,@(tau-get-stats-on-signatures fns wrld 0 0 0 0 0 nil nil) (:big-switches ,(collect-tau-big-switches wrld nil)) (:mv-nth-synonyms ,(global-val 'tau-mv-nth-synonyms wrld)) (:tau-runes ,(len (global-val 'tau-runes wrld))) (:tau-lost-runes ,(len (global-val 'tau-lost-runes wrld)))))) ; If you execute this form you get a self-explanatory printout. ; (tau-get-stats (w state)) ; If you define the fns and macros in the comment containing defmacro mktau, you ; can also write: ; (mktau t (not (equal x 23)) (natp x) (integerp x) (not (symbolp x))) ; to get a complete tau, or use a nil in place of the t to get one that just ; codes those recognizers. Be careful of the need for translation -- ptrans ; defined above is used which is a poor-man's translate. ; If you have a tau, you can print it as an untranslated term with (decode-tau ; tau 'v). A variety of decoders are provided above, including for a list of ; tau, a tau-alist, a tau signature of either form, a list of signatures of ; either form, and (below) all the information tau knows about a function ; symbol. See tau-data. (defmacro tau-status (&key (system 'nil system-p) (auto-mode 'nil auto-mode-p)) ":Doc-Section switches-parameters-and-modes query or set tau system status~/ ~bv[] Examples: (tau-status) (tau-status :system t) (tau-status :auto-mode nil) (tau-status :system t :auto-mode nil) General Form: (tau-status :system a :auto-mode b) ~ev[] where ~c[a] and ~c[b] are Booleans. Both keyword arguments are optional and they may be presented in either order. Value ~c[a] controls whether the ~ilc[tau-system] is used during subsequent proofs. Value ~c[b] controls whether tau system rules are added automatically (``greedily'') when rules of other ~ilc[rule-classes] are added. If no arguments are supplied, this is not an event and just returns an error-triple (~pl[error-triples]) indicating the current settings. ~l[introduction-to-the-tau-system] for background details.~/ The two flags are independent. For example, the tau system may be disabled in proof attempts even though it is automatically (and silently) extending its database as rules of other classes are added. Flag (a) is actually toggled by enabling or disabling the ~c[:]~ilc[executable-counterpart] of ~ilc[tau-system]. Flag (b) is toggled with the function ~ilc[set-tau-auto-mode], which manipulates the ~ilc[acl2-defaults-table]. This macro expands into zero, one, or two ~il[events], as required by the supplied values of flags ~c[a] and ~c[b]. If no arguments are supplied the form is not an event and simply returns (as an error triple ~c[(mv nil ans state)]; ~pl[error-triples]) the current settings of the two flags. For example: ~bv[] ACL2 !>(tau-system) ((:SYSTEM NIL) (:AUTO-MODE T)) ~ev[] intended to be self-explanatory.~/ :cited-by introduction-to-the-tau-system" (cond (system-p (cond (auto-mode-p `(progn (in-theory (,(if system 'enable 'disable) (:executable-counterpart tau-system))) (set-tau-auto-mode ,auto-mode))) (t `(in-theory (,(if system 'enable 'disable) (:executable-counterpart tau-system)))))) (auto-mode-p `(set-tau-auto-mode ,auto-mode)) (t '(er-let* ((amp (table acl2-defaults-table :tau-auto-modep))) (value (list (list :system (if (enabled-numep *tau-system-xnume* (ens state)) t nil)) (list :auto-mode amp))))))) (defun tau-data-fn (fn wrld) (let ((fn (deref-macro-name fn (macro-aliases wrld)))) (cond ((and (symbolp fn) (arity fn wrld)) (let ((tau-pair (getprop fn 'tau-pair nil 'current-acl2-world wrld)) (sigs1 (getprop fn 'signature-rules-form-1 nil 'current-acl2-world wrld)) (sigs2 (getprop fn 'signature-rules-form-2 nil 'current-acl2-world wrld)) (big-switch (getprop fn 'big-switch nil 'current-acl2-world wrld)) (mv-nth-synonym (member-eq fn (global-val 'tau-mv-nth-synonyms wrld)))) (cond ((or tau-pair sigs1 sigs2 big-switch mv-nth-synonym) `(,fn (recognizer-index ,(car tau-pair)) (pos-implicants ,(decode-tau (getprop fn 'pos-implicants *tau-empty* 'current-acl2-world wrld) 'v)) (neg-implicants ,(decode-tau (getprop fn 'neg-implicants *tau-empty* 'current-acl2-world wrld) 'v)) (signatures ,(let ((sigs1 (decode-tau-signature-rules nil fn sigs1 wrld)) (sigs2 (decode-tau-signature-rules t fn sigs2 wrld))) (if (eq sigs1 t) (if (eq sigs2 t) t sigs2) (if (eq sigs2 t) sigs1 `(and ,sigs1 ,sigs2))))) (big-switch? ,(if big-switch :yes :no)) (mv-nth-synonym? ,(if mv-nth-synonym :yes :no)))) (t nil)))) (t nil)))) (defmacro tau-data (fn) ":Doc-Section History to see what tau knows about a function symbol~/ ~bv[] Examples: (tau-data binary-+) General Form: (tau-data fn) ~ev[] This macro returns a list structure that indicates what facts about the symbol ~c[fn] are known to the tau system. ~c[Fn] should either be a function symbol or else a macro associated with ~c[fn]; ~pl[macro-aliases-table]. ~l[introduction-to-the-tau-system] for background details.~/ The list structure should be self-explanatory given the following brief comments. The ``index'' of a function, when non-~c[nil], means the function is a monadic Boolean function treated by the tau system as a tau predicate. The ``positive'' and ``negative implicants'' are conjunctions that indicate the tau implied by the given one or its negation. The ``signatures'' entry is a formula indicating all the known signatures. If the signatures formula is ~c[T] it means there are no known signatures. (Signatures is the conjunction of all signature rules and the empty conjunction is ~c[T].) If you wish to see a long list of all the runes from which some tau information has been gleaned, evaluate ~c[(global-val 'tau-runes (w state))].~/ :cited-by introduction-to-the-tau-system" `(tau-data-fn ',fn (w state))) (defun all-fnnames-world1 (trips logicp wrld ans) ; Logicp should be t if you want to collect :logic mode function symbols, ; nil if you want to collect :program mode functions, and :either if you ; want to collect both. (cond ((endp trips) ans) ((and (eq (cadr (car trips)) 'formals) (if (eq logicp t) (not (eq (symbol-class (car (car trips)) wrld) :program)) (if logicp t (eq (symbol-class (car (car trips)) wrld) :program))) (not (member-eq (car (car trips)) ans))) (all-fnnames-world1 (cdr trips) logicp wrld (cons (car (car trips)) ans))) (t (all-fnnames-world1 (cdr trips) logicp wrld ans)))) (defun all-fnnames-world (mode wrld) ; Collect all function symbols of the given mode in world. Mode may be :logic, ; :program, or :either. (all-fnnames-world1 wrld (if (eq mode :logic) t (if (eq mode :program) nil :either)) wrld nil)) (defun tau-data-fns (fns wrld ans) (cond ((endp fns) ans) (t (tau-data-fns (cdr fns) wrld (let ((temp (tau-data-fn (car fns) wrld))) (if temp (cons temp ans) ans)))))) (defun tau-database (wrld) ":Doc-Section History to see the tau database as a (very large) object~/ ~bv[] Example: (tau-database (w state)) ~ev[] This function returns a large list object that shows in a human-readable way what the tau system knows about every function symbol. It is supposed to be self-explanatory. ~l[introduction-to-the-tau-system] for background details.~/ If the output is not self-explanatory, please contact the implementors and we will improve the output or the documentation.~/ :cited-by introduction-to-the-tau-system" (tau-data-fns (merge-sort-lexorder (all-fnnames-world :logic wrld)) wrld `((tau-next-index ,(global-val 'tau-next-index wrld)) (tau-conjunctive-rules ,(decode-tau-conjunctive-rules (merge-sort-lexorder (global-val 'tau-conjunctive-rules wrld)))) (tau-mv-nth-synonyms ,(merge-sort-lexorder (global-val 'tau-mv-nth-synonyms wrld))) (tau-runes ,(merge-sort-lexorder (global-val 'tau-runes wrld))) (tau-lost-runes ,(merge-sort-lexorder (global-val 'tau-lost-runes wrld)))))) ; ----------------------------------------------------------------- ; Regenerating the Tau Database ; Because tau does not track which runes it is using, disabling a rune has no ; effect on the inferences tau makes. However, we do provide the ability to ; recompute the tau database with respect to a given enabled structure. This ; is an event command and we cannot actually define the entire event command ; until we have more infrastructure in place. (The regenerate-tau-database ; event is defined in other-events.lisp.) But we can do the hard work, which ; is mapping over the world and re-visiting every event for its tau rules. ; The regeneration facility first clears the tau global variables. Then it ; revisits every tuple in the world, in chronological order, and calls the ; appropriate tau-visit function on each relevant tuple. The only relevant ; tuples are those that declare new function symbols and those that mark event ; boundaries, i.e., triples of the forms (fn FORMALS . (v1 ... vn)) and ; (EVENT-LANDMARK GLOBAL-VALUE . ev-tuple) We look out for the ; exit-boot-strap-mode event-landmark because there we switch the boot-strap ; setting of auto-modep to the user-specified setting. Visiting the tuples in ; chronological order is hard to do efficiently because of the length of the ; world (about 87,000 at the moment), so we just reverse the world, collecting ; only the tuples of interest. (defun initialize-tau-globals (wrld) ; Keep this in sync with the initial tau global values in ; primordial-world-globals, which at the moment consists of ; (tau-runes nil) ; (tau-next-index 0) ; <--- We do not re-initialize this one! ; (tau-conjunctive-rules nil) ; (tau-mv-nth-synonyms nil) ; (tau-lost-runes nil) (putprop-if-different 'tau-runes 'global-value nil ; (putprop-if-different ; 'tau-next-index 'global-value 0 (putprop-if-different 'tau-conjunctive-rules 'global-value nil (putprop-if-different 'tau-mv-nth-synonyms 'global-value nil (putprop-if-different 'tau-lost-runes 'global-value nil wrld))) ; ) )) ; Tau Todo: Investigate how redefinition screws up this collection... (defun collect-tau-relevant-triples (wrld ans) (cond ((endp wrld) ans) ((or (and (eq (cadr (car wrld)) 'FORMALS) (not (eq (cddr (car wrld)) *acl2-property-unbound*))) (and (eq (car (car wrld)) 'EVENT-LANDMARK) (eq (cadr (car wrld)) 'GLOBAL-VALUE))) (collect-tau-relevant-triples (cdr wrld) (cons (car wrld) ans))) (t (collect-tau-relevant-triples (cdr wrld) ans)))) ; To define the regeneration event we must have some additional event-level ; infrastructure (e.g., how to interpret the event tuples stored as ; EVENT-LANDMARKs, not to mention ctx computation, install-event, etc. So we ; delay the rest of the regeneration event until other-events.lisp. (deflabel bounders :doc ":Doc-Section Tau-System intervals, bounder functions, and bounder correctness~/ ~bv[] ~i[Bounder Forms 1 and 2]: (implies (and (tau-intervalp i1) ... (or (equal (tau-interval-dom i1) 'dom1-1) ...) ... (in-tau-intervalp x1 i1) ...) (and (tau-intervalp (bounder-fn i1 ...)) (in-tau-intervalp ~i[target] (bounder-fn i1 ...)))) ~ev[] where ~i[target] is either ~c[(fn x1 ... y1 ...)] or ~c[(mv-nth 'n (fn x1 ... y1 ...))], depending on whether we are in the ~i[Form 1] or ~i[Form 2] case, respectively. However, the shape above is meant just as a reminder. Details are given below.~/ This topic first explains the basic shape of ~i[Bounder Form 1]. Then it illustrates ~i[Bounder Form 2]. Finally, it deals briefly with proving bounder correctness theorems. The community book ~c[tau-bounders/elementary-bounders] contains bounders for various elementary functions including ~ilc[+], ~ilc[*], ~ilc[/], ~ilc[FLOOR], ~ilc[MOD], ~ilc[LOGAND], ~ilc[LOGNOT], ~ilc[LOGIOR], ~ilc[LOGORC1], ~ilc[LOGEQV], ~ilc[LOGXOR], and ~ilc[ASH]. You might look at or include this book to see more example theorems, to see how proofs of such theorems are managed, and to experiment with their effects on proving theorems involving arithmetic over finite or half-finite intervals. A bounder correctness theorem establishes that ~c[bounder-fn] is a ``bounder'' for the function ~c[fn]. That means that when trying to compute a tau for a call of ~c[fn] (or, in the case of ~i[Form 2], for the ~c[n]th component of the multiple-value vector returned by a call of ~c[fn]) the tau system can call ~c[bounder-fn] on the intervals containing certain arguments of ~c[fn]. Let us start with an example. Let ~c[fn] be the addition function, ~c[+] (actually, ~ilc[binary-+]). Consider the target term ~c[(+ x y)] and contemplate the question: if you know intervals containing ~c[x] and ~c[y], say ~c[intx] and ~c[inty] respectively, what is an interval containing their sum? The answer is pretty easy to state in English: the domain of the answer interval is the less restrictive of the domains of ~c[intx] and ~c[inty]. The lower bound of the answer interval is the sum of the lower bounds of ~c[intx] and ~c[inty], and the lower relation is the stronger of the lower relations of ~c[intx] and ~c[inty]. Analogous comments define the upper bound and relation of the answer interval. So for example, if ~c[x] is an ~c[INTEGERP] such that ~c[0 <= x <= 10] and ~c[y] is a ~c[RATIONALP] such that ~c[0 < y <= 20], then ~c[(+ x y)] is a ~c[RATIONALP] such that ~c[0 < (+ x y) <= 30]. Defining this precisely is more tedious than describing it in English because one must make precise the notions of ``less restrictive'' domains, ``weaker'' relations, and the possibility that either or both of the bounds could be ``infinite.'' But we can easily imagine defining the function ~c[bounder-for-+] that returns the answer interval described, given ~c[intx] and ~c[inty]. Then the following ~i[Bounder Form 1] formula establishes the correctness of ~c[bounder-for-+] and allows the tau system to use it to produce bounds in the tau computed for ~c[+]-expressions: ~bv[] (implies (and (tau-intervalp intx) (tau-intervalp inty) (in-tau-intervalp x intx) (in-tau-intervalp y inty)) (and (tau-intervalp (bounder-for-+ intx inty)) (in-tau-intervalp (+ x y) (bounder-for-+ intx inty)))) ~ev[] For example, suppose we have a formula with the following hypotheses ~bv[] (and (integerp a) (<= 0 a) (<= a 10) (rationalp b) (< 0 b) (<= b 20)) ~ev[] and suppose the tau system encounters the term ~c[(+ a b)]. When the term is enountered, the tau for ~c[a] would include an ~c[INTEGERP] interval such that ~c[0 <= a <= 10] and the tau for ~c[b] would include a ~c[RATIONALP] interval such that ~c[0 < b <= 20]. In its most primitive configuration, the tau system would only know that the tau for ~c[(+ a b)] includes the recognizer ~c[RATIONALP] (and all that it is known to imply). But after the bounder theorem above is proved and available as a ~c[:tau-system] rule the tau system would infer that ~c[(+ a b)] was in the ~c[RATIONALP] interval such that ~c[0 < (+ a b) <= 30]. Thus, by defining bounder functions and proving them correct the user can give the tau system the ability to compute the bounds on function calls as a function of the known bounds on their actuals. It is sometimes useful to restrict the domains of the intervals to be considered. For example, in bounding ~c[*]-expressions it is simplifying to restrict one's attention to intervals over the integers or rationals (and thus exclude the complex rationals so one need not think about the getting negative bounds by multiplying two ``positive'' complex rationals or how to ``round up'' from complex bounds to the rationals required by our intervals). If we were to define ~c[bounder-for-*] so that it works correctly to bound ~c[*]-expressions, but only for integer or rational arguments, its correctness theorem would be: ~bv[] (implies (and (tau-intervalp intx) ; (a) (tau-intervalp inty) (or (equal (tau-interval-dom intx) 'INTEGERP) ; (b) (equal (tau-interval-dom intx) 'RATIONALP)) (or (equal (tau-interval-dom inty) 'INTEGERP) (equal (tau-interval-dom inty) 'RATIONALP)) (in-tau-intervalp x intx) ; (c) (in-tau-intervalp y inty)) (and (tau-intervalp (bounder-for-* intx inty)) ; (d) (in-tau-intervalp (* x y) ; (e) (bounder-for-* intx inty)))) ~ev[] In this case, ~c[bounder-for-*] would be applied to the intervals for ~c[x] and ~c[y] only if those intervals were over the integers or the rationals. The above theorem for ~c[bounder-for-*] begins to suggest the general form of a bounder theorem and we will use it to explain the general form. The hypotheses of a bounder theorem must be a conjunction and the conjuncts must be partitionable into three parts, (a), (b), and (c). The conclusion, must be a conjunction, must contain at least two conjuncts, (d) and (e), and is allowed to contain others that are simply ignored for purposes of bounders. (See the note below about why we allow but ignore additional conjuncts in the conclusion.) Part (a) introduces some distinct ``interval variables,'' here called ``ivars,'' that are known to denote intervals; for the example above, the ivars are ~c[intx] and ~c[inty]. Each hypothesis in part (a) is of the form ~c[(TAU-INTERVALP ivar)]. Part (b) allows us to restrict the domains of some of the intervals. Each hypothesis in part (b) must be a disjunction and each of the disjuncts must be of the form ~c[(EQUAL (TAU-INTERVAL-DOM ivar) 'dom)], where ~c[ivar] is one of the interval variables and ~c[dom] is one of ~c[INTEGERP], ~c[RATIONALP], ~c[ACL2-NUMBERP], or ~c[NIL]. It is not necessary to restrict every interval variable. Indeed, part (b) may be empty, as in the theorem for ~c[bounder-for-+] above. Part (c) consists of a set of ~c[(IN-TAU-INTERVALP avar ivar)] hypotheses where each ~c[avar] is a variable and no two hypotheses in part (c) use the same ~c[avar] or ~c[ivar]. We call the set of all such ~c[avar] the ``actual variables'' or ``avars.'' The avars and ivars must be distinct. Part (c) sets up a correspondence between the avars and the ivars, each avar is in an interval denoted by one ivar. Part (d) introduces the name of the bounder function, here ~c[bounder-for-*], and the order of its ivar arguments. We see that ~c[bounder-for-*] takes two arguments and they correspond, in order, to the intervals containing ~c[x] and ~c[y]. Part (d) also establishes that the bounder function always returns an interval under hypotheses (a), (b), and (c). Note that it is sometimes useful to return the ``universal interval'' (one that contains everything) if you don't want to compute a better interval for some case; see ~ilc[tau-intervalp] or ~ilc[in-tau-intervalp]. Part (e) introduces the name of the function being bounded, here ~c[*], and the order of its arguments. It establishes that the function being bounded really is bounded by the interval computed by the bounder function. In general, the function being bounded may take additional arguments. It is possible that the function being bounded takes some arguments that do not affect the bounds of its output. Thus, parts (c) and (e) together establish a mapping between the actuals of a call of the function being bounded and the intervals to be supplied to the bounder. The parts identified above may be presented in any order and the literals constituting those parts may be mingled. Thus, for example, here is another version of the theorem above that generates the same bounding information for the tau system. In this version, the hypotheses and conclusions are rearranged, ~c[bounder-for-*] takes its arguments in the opposite order, and the theorem includes an additional conclusion. ~bv[] (implies (and (tau-intervalp intx) ; (a) (or (equal (tau-interval-dom intx) 'INTEGERP) ; (b) (equal (tau-interval-dom intx) 'RATIONALP)) (in-tau-intervalp x intx) ; (c) (tau-intervalp inty) ; (a) (or (equal (tau-interval-dom inty) 'INTEGERP) ; (b) (equal (tau-interval-dom inty) 'RATIONALP)) (in-tau-intervalp y inty)) (and (in-tau-intervalp (* x y) ; (e) (bounder-for-* inty intx)) (tau-intervalp (bounder-for-* inty intx)) ; (d))) (or (equal (tau-interval-dom (bounder-for-* inty intx)) 'INTEGERP) (equal (tau-interval-dom (bounder-for-* inty intx)) 'RATIONALP)) ~ev[] ~i[Note on why bounder forms allow additional conjuncts in the conclusion]: It is often the case that one creates bounders by composing other bounders. To prove compositional bounds correct one must often prove more than the mere correctness of the components. For example, one might need to prove that the domain of the new bounding interval is ~c[INTEGERP] or otherwise restricted. We allow such ``unnecessary'' conclusions simply to save the user the burden of stating multiple theorems. ~i[An Illustration of Bounder Form 2]: Suppose ~c[(quad i)] is defined so that truncates the integer ~c[i] to the largest multiple of 4 weakly below ~c[i] and, additionally, returns the remainder. For example, ~c[(quad 26)] returns ~c[(mv 24 2)]. Then here are bounders for each of its return values: ~bv[] (defun quad-bounds-0 (i) (cond ((and (tau-interval-lo i) (<= 0 (tau-interval-lo i))) (make-tau-interval 'integerp nil 0 nil (tau-interval-hi i))) (t (make-tau-interval nil nil nil nil nil)))) (defun quad-bounds-1 (i) (cond ((and (tau-interval-lo i) (<= 0 (tau-interval-lo i))) (make-tau-interval 'integerp nil 0 nil 3)) (t (make-tau-interval nil nil nil nil nil)))) ~ev[] Note that the bounders assume ~c[i] is an ~c[INTEGERP] and return the universal interval when ~c[i] is not a natural. As noted in the discussion below about how to prove bounder correctness theorems, proving these bounders correct will require an arithmetic book, e.g., ~bv[] (include-book \"arithmetic-5/top\" :dir :system) ~ev[] Here then are two bounder correctness theorems of ~i[Form 2]: ~bv[] (defthm quad-bounds-0-correct (implies (and (tau-intervalp i) (equal (tau-interval-dom i) 'INTEGERP) (in-tau-intervalp x i)) (and (tau-intervalp (quad-bounds-0 i)) (in-tau-intervalp (mv-nth 0 (quad x)) (quad-bounds-0 i)))) :rule-classes :tau-system) (defthm quad-bounds-1-correct (implies (and (tau-intervalp i) (equal (tau-interval-dom i) 'INTEGERP) (in-tau-intervalp x i)) (and (tau-intervalp (quad-bounds-1 i)) (in-tau-intervalp (mv-nth 1 (quad x)) (quad-bounds-1 i)))) :rule-classes :tau-system) ~ev[] As noted above, if these bounders are to be used in constructing other bounders, we might include (in the first theorem) an additional concluding conjunct, such as ~bv[] (equal (tau-interval-dom (quad-bounds-0 i)) 'INTEGERP) ~ev[] so that we can keep ~c[quad-bounds-0] disabled to allow us to use ~c[quad-bounds-0-correct] as a ~c[:rewrite] or other rule and still relieve hypotheses about the domain of the interval it produces. These hypotheses would arise if some other verified bounder was called on the produced interval. In addition, as noted below, we might replace the ~c[:rule-classes] above with ~bv[] :rule-classes ((:rewrite) (:forward-chaining :trigger-terms ((quad-bounds-0 i)))) ~ev[] Since the theorem is being stored as some kind of rule and since it satisfies the ~i[Bounder Form 2] shape, it will additionally be stored as a ~c[:tau-system] rule. ~i[Note on proving bounder theorems]: Proving bounder theorems is just like proving any other arithmetic theorem and you will need whatever libraries are appropriate for the problem domain you are working in. Do not expect the tau system to be of much use in proving bounder theorems. A typical bounder theorem might require you to prove a subgoal like ~c[(< (fn x y) (g (tau-interval-hi int1) int2))]. But tau deals with inequalities relating terms to constants, e.g., ~c[(< ... 16)]. A bounder theorem is a sort of ``metatheorem'' about ~i[how to construct] bounded intervals from other bounded intervals. So when you undertake to define a bounder and prove it correct, go into the project with your eyes open! But bounder functions can be broadly divided into two classes, those defined in terms of arithmetic on the interval bounds and those defined in terms of other bounders. For example, given that ~bv[] (LOGXOR x y) = (LOGNOT (LOGEQV x y)) ~ev[] an interval for bounding ~c[LOGXOR] can be constructed by composing the constructions of intervals for ~c[LOGEQV] and ~c[LOGNOT]. So some bounder correctness proofs will involve direct manipulation of arithmetic inequalities and others might involve appeal to the correctness of other bounders, depending on how the new bounder is defined. Regardless of which style of bounder we are dealing with, we have found it useful to prove the basic theorems relating the tau interval accessors to ~ilc[MAKE-TAU-INTERVAL], e.g., ~bv[] (equal (tau-interval-dom (make-tau-interval dom lo-rel lo hi-rel hi)) dom) ~ev[] and then disable those functions to avoid seeing excessive ~c[car]s and ~c[cdr]s. When dealing with bounders defined in the direct, arithmetic style, we tend to keep ~ilc[TAU-INTERVALP] and ~ilc[IN-TAU-INTERVALP] enabled so they unfold and expose the algebra. When dealing with bounders defined compositionally in terms of other verified bounders, we tend to keep ~ilc[TAU-INTERVALP] and ~ilc[IN-TAU-INTERVALP] disabled so we can rely on the previously proved bounder theorems as rewrite and forward chaining rules. Note that this last remark means that when you prove bounder correctness theorems you should include corollaries that are useful ~c[:rewrite] and possibly ~c[:forward-chaining] rules if you anticipate using that bounder in more complex ones. We tend to trigger the forward chaining with the bounder expression itself, rather than one of the hypotheses. For example in the rule above for ~c[bounder-for-*] we would include ~c[(:forward-chaining :trigger-terms ((tau-bounder-expt2 int2)))] and let the ~c[in-tau-intervalp] hypotheses select the free variables ~c[x] and ~c[y]. ~/") acl2-sources/translate.lisp0000664002132200015000000144110412222115527015445 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") (deflabel syntax :doc ":Doc-Section Miscellaneous the syntax of ACL2 is that of Common Lisp~/ For the details of ACL2 syntax, see CLTL. For examples of ACL2 syntax, use ~c[:]~ilc[pe] to print some of the ACL2 system code. For example: ~bv[] :pe assoc-equal :pe dumb-occur :pe var-fn-count :pe add-linear-variable-to-alist ~ev[]~/ There is no comprehensive description of the ACL2 syntax yet, except that found in CLTL. Also ~pl[term].") (deflabel term :doc ":Doc-Section Miscellaneous the three senses of well-formed ACL2 expressions or formulas~/ ~bv[] Examples of Terms: (cond ((caar x) (cons t x)) (t 0)) ; an untranslated term (if (car (car x)) (cons 't x) '0) ; a translated term (car (cons x y) 'nil v) ; a pseudo-term ~ev[]~/ In traditional first-order predicate calculus a ``term'' is a syntactic entity denoting some object in the universe of individuals. Often, for example, the syntactic characterization of a term is that it is either a variable symbol or the application of a function symbol to the appropriate number of argument terms. Traditionally, ``atomic formulas'' are built from terms with predicate symbols such as ``equal'' and ``member;'' ``formulas'' are then built from atomic formulas with propositional ``operators'' like ``not,'' ``and,'' and ``implies.'' Theorems are formulas. Theorems are ``valid'' in the sense that the value of a theorem is true, in any model of the axioms and under all possible assignments of individuals to variables. However, in ACL2, terms are used in place of both atomic formulas and formulas. ACL2 does not have predicate symbols or propositional operators as distinguished syntactic entities. The ACL2 universe of individuals includes a ``true'' object (denoted by ~c[t]) and a ``false'' object (denoted by ~c[nil]), predicates and propositional operators are functions that return these objects. Theorems in ACL2 are terms and the ``validity'' of a term means that, under no assignment to the variables does the term evaluate to ~c[nil]. We use the word ``term'' in ACL2 in three distinct senses. We will speak of ``translated'' terms, ``untranslated'' terms, and ``pseudo-'' terms. ~em[Translated Terms: The Strict Sense and Internal Form] In its most strict sense, a ``term'' is either a legal variable symbol, a quoted constant, or the application of an n-ary function symbol or closed ~c[lambda] expression to a true list of n terms. The legal variable symbols are symbols other than ~c[t] or ~c[nil] which are not in the keyword package, do not start with ampersand, do not start and end with asterisks, and if in the main Lisp package, do not violate an appropriate restriction (~pl[name]). Quoted constants are expressions of the form ~c[(quote x)], where ~c[x] is any ACL2 object. Such expressions may also be written ~c['x]. Closed ~c[lambda] expressions are expressions of the form ~c[(lambda (v1 ... vn) body)] where the ~c[vi] are distinct legal variable symbols, ~c[body] is a term, and the only free variables in ~c[body] are among the ~c[vi]. The function ~c[termp], which takes two arguments, an alleged term ~c[x] and a logical world ~c[w] (~pl[world]), recognizes terms of a given extension of the logic. ~c[Termp] is defined in ~c[:]~ilc[program] mode. Its definition may be inspected with ~c[:]~ilc[pe] ~c[termp] for a complete specification of what we mean by ``term'' in the most strict sense. Most ACL2 term-processing functions deal with terms in this strict sense and use ~c[termp] as a ~il[guard]. That is, the ``internal form'' of a term satisfies ~c[termp], the strict sense of the word ``term.'' ~em[Untranslated Terms: What the User Types] While terms in the strict sense are easy to explore (because their structure is so regular and simple) they can be cumbersome to type. Thus, ACL2 supports a more sugary syntax that includes uses of macros and constant symbols. Very roughly speaking, macros are functions that produce terms as their results. Constants are symbols that are associated with quoted objects. Terms in this sugary syntax are ``translated'' to terms in the strict sense; the sugary syntax is more often called ``untranslated.'' Roughly speaking, translation just implements macroexpansion, the replacement of constant symbols by their quoted values, and the checking of all the rules governing the strict sense of ``term.'' More precisely, macro symbols are as described in the documentation for ~ilc[defmacro]. A macro, ~c[mac], can be thought of as a function, ~c[mac-fn], from ACL2 objects to an ACL2 object to be treated as an untranslated term. For example, ~ilc[caar] is defined as a macro symbol; the associated macro function maps the object ~c[x] into the object ~c[(car (car x))]. A macro form is a ``call'' of a macro symbol, i.e., a list whose ~ilc[car] is the macro symbol and whose ~ilc[cdr] is an arbitrary true list of objects, used as a term. Macroexpansion is the process of replacing in an untranslated term every occurrence of a macro form by the result of applying the macro function to the appropriate arguments. The ``appropriate'' arguments are determined by the exact form of the definition of the macro; macros support positional, keyword, optional and other kinds of arguments. ~l[defmacro]. In addition to macroexpansion and constant symbol dereferencing, translation implements the mapping of ~ilc[let] and ~ilc[let*] forms into applications of ~c[lambda] expressions and closes ~c[lambda] expressions containing free variables. Thus, the translation of ~bv[] (let ((x (1+ i))) (cons x k)) ~ev[] can be seen as a two-step process that first produces ~bv[] ((lambda (x) (cons x k)) (1+ i)) ~ev[] and then ~bv[] ((lambda (x k) (cons x k)) (1+ i) k) . ~ev[] Observe that the body of the ~ilc[let] and of the first ~c[lambda] expression contains a free ~c[k] which is finally bound and passed into the second ~c[lambda] expression. Translation also maps ~ilc[flet] forms into applications of ~c[lambda] expressions. ~l[flet]. When we say, of an event-level function such as ~ilc[defun] or ~ilc[defthm], that some argument ``must be a term'' we mean an untranslated term. The event functions translate their term-like arguments. To better understand the mapping between untranslated terms and translated terms it is convenient to use the keyword command ~c[:]~ilc[trans] to see examples of translations. ~l[trans] and also ~pl[trans1]. Finally, we note that the theorem prover prints terms in untranslated form. But there can be more than one correct untranslated term corresponding to a given translated term. For example, the translated term ~c[(if x y 'nil)] can be untranslated as ~c[(if x y nil)] and can also be untranslated as ~c[(and x y)]. The theorem prover attempts to print an untranslated term that is as helpful to the user as possible. In particular, consider a term of the form ~c[(nth k st)] where ~c[st] is a single-threaded object (~pl[stobj]) and the ~c[kth] accessor of ~c[st] is, say, ~c[kn]. The theorem prover typically would expand ~c[(kn st)] to ~c[(nth k st)]. If ~c[k] is large then it could be difficult for the user to make sense out of a proof transcript that mentions the expanded term. Fortunately, the untranslation of ~c[(nth k st)] would be ~c[(nth *kn* st)]; here ~c[*kn*] would be a constant (~pl[defconst]) added by the ~ilc[defstobj] event introducing ~c[st], defined to have value ~c[k]. The user can extend this user-friendly style of printing applications of ~ilc[nth] to stobjs; ~pl[add-nth-alias]. These remarks about printing applications of function ~ilc[nth] extend naturally to function ~ilc[update-nth]. Moreover, the prover will attempt to treat terms as ~il[stobj]s for the above purpose when appropriate. For example, if function ~c[foo] has ~il[signature] ~c[((foo * st) => (mv * * * st))], where ~c[st] is introduced with ~c[(defstobj st f0 f1)], then the ~il[term] ~c[(nth '1 (mv-nth '3 (foo x st0)))] will be printed as ~c[(nth *f1* (mv-nth 3 (foo x st0)))]. ~em[Pseudo-Terms: A Common Guard for Metafunctions] Because ~c[termp] is defined in ~c[:]~ilc[program] mode, it cannot be used effectively in conjectures to be proved. Furthermore, from the perspective of merely guarding a term processing function, ~c[termp] often checks more than is required. Finally, because ~c[termp] requires the logical ~il[world] as one of its arguments it is impossible to use ~c[termp] as a ~il[guard] in places where the logical ~il[world] is not itself one of the arguments. For these reasons we support the idea of ``pseudo-terms.'' A pseudo-term is either a symbol (but not necessarily one having the syntax of a legal variable symbol), a true list beginning with ~c[quote] (but not necessarily well-formed), or the ``application of'' a symbol or pseudo ~c[lambda] expression to a true list of pseudo-terms. A pseudo ~c[lambda] expression is an expression of the form ~c[(lambda (v1 ... vn) body)] where the ~c[vi] are all symbols and ~c[body] is a pseudo-term. Pseudo-terms are recognized by the unary function ~ilc[pseudo-termp]. If ~c[(termp x w)] is true, then ~c[(pseudo-termp x)] is true. However, if ~c[x] fails to be a (strict) term it may nevertheless still be a pseudo-term. For example, ~c[(car a b)] is not a term, because ~ilc[car] is applied to the wrong number of arguments, but it is a pseudo-term. The structures recognized by ~ilc[pseudo-termp] can be recursively explored with the same simplicity that terms can be. In particular, if ~c[x] is not a ~c[variablep] or an ~c[fquotep], then ~c[(ffn-symb x)] is the function (~c[symbol] or ~c[lambda] expression) and ~c[(fargs x)] is the list of argument pseudo-terms. A metafunction (~pl[meta]) or clause-processor (~pl[clause-processor]) may use ~ilc[pseudo-termp] as the ~il[guard].") (mutual-recursion (defun termp (x w) (cond ((atom x) (legal-variablep x)) ((eq (car x) 'quote) (and (consp (cdr x)) (null (cddr x)))) ((symbolp (car x)) (let ((arity (arity (car x) w))) (and arity (true-listp (cdr x)) (eql (length (cdr x)) arity) (term-listp (cdr x) w)))) ((and (consp (car x)) (true-listp (car x)) (eq (car (car x)) 'lambda) (equal 3 (length (car x))) (arglistp (cadr (car x))) (termp (caddr (car x)) w) (null (set-difference-eq (all-vars (caddr (car x))) (cadr (car x)))) (term-listp (cdr x) w) (equal (length (cadr (car x))) (length (cdr x)))) t) (t nil))) (defun term-listp (x w) (cond ((atom x) (equal x nil)) ((termp (car x) w) (term-listp (cdr x) w)) (t nil))) ) (defun computed-hint-tuple-listp (x wrld) (cond ((consp x) (let ((tuple (car x))) (and (true-listp tuple) (eq (car tuple) 'EVAL-AND-TRANSLATE-HINT-EXPRESSION) (booleanp (caddr tuple)) (termp (cadddr tuple) wrld) (computed-hint-tuple-listp (cdr x) wrld)))) (t (null x)))) (table default-hints-table nil nil :guard (case key ((t) (true-listp val)) (:override (computed-hint-tuple-listp val world)) (t nil))) (table default-hints-table nil nil :clear) (defun macro-args (x w) ":Doc-Section Miscellaneous the formals list of a macro definition~/ ~bv[] Examples: (x y z) (x y z &optional max (base '10 basep)) (x y &rest rst) (x y &key max base) (&whole sexpr x y) ~ev[]~/ The ``lambda-list'' of a macro definition may include simple formal parameter names as well as appropriate uses of the following ~c[lambda]-list keywords from CLTL (pp. 60 and 145), respecting the order shown: ~bv[] &whole, &optional, &rest, &body, &key, and &allow-other-keys. ~ev[] ACL2 does not support ~c[&aux] and ~c[&environment]. In addition, we make the following restrictions:~bq[] (1) initialization forms in ~c[&optional] and ~c[&key] specifiers must be quoted values; (2) ~c[&allow-other-keys] may only be used once, as the last specifier; and (3) destructuring is not allowed. ~eq[]You are encouraged to experiment with the macro facility. One way to do so is to define a macro that does nothing but return the quotation of its arguments, e.g., ~bv[] (defmacro demo (x y &optional opt &key key1 key2) (list 'quote (list x y opt key1 key2))) ~ev[] You may then execute the macro on some sample forms, e.g., ~bv[] term value (demo 1 2) (1 2 NIL NIL NIL) (demo 1 2 3) (1 2 3 NIL NIL) (demo 1 2 :key1 3) error: non-even key/value arglist (because :key1 is used as opt) (demo 1 2 3 :key2 5) (1 2 3 NIL 5) ~ev[] In particular, Common Lisp specifies that if you use both ~c[&rest] and ~c[&key], then both will be bound using the same list of arguments. The following example should serve to illustrate how this works. ~bv[] ACL2 !>(defmacro foo (&rest args &key k1 k2 k3) (list 'quote (list args k1 k2 k3))) Summary Form: ( DEFMACRO FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo :k1 3 :k2 4 :k3 5) ((:K1 3 :K2 4 :K3 5) 3 4 5) ACL2 !>(foo :k1 3 :k2 4) ((:K1 3 :K2 4) 3 4 NIL) ACL2 !>(foo :k1 3 :bad-key 7) ACL2 Error in macro expansion: Illegal key/value args (:BAD-KEY 7) in macro expansion of (FOO :K1 3 :BAD-KEY 7). The argument list for FOO is (&REST ARGS &KEY K1 K2 K3). ACL2 !> ~ev[] If we don't want to get the error above, we can use ~c[&allow-other-keys], as follows. ~bv[] ACL2 !>(defmacro bar (&rest args &key k1 k2 k3 &allow-other-keys) (list 'quote (list args k1 k2 k3))) Summary Form: ( DEFMACRO BAR ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) BAR ACL2 !>(bar :k1 3 :bad-key 7) ((:K1 3 :BAD-KEY 7) 3 NIL NIL) ACL2 !> ~ev[] Also ~pl[trans].~/" (getprop x 'macro-args '(:error "We thought macro-args was only called if there ~ were (zero or more) macro-args.") 'current-acl2-world w)) (defconst *macro-expansion-ctx* "macro expansion") (defun error-trace-suggestion (two-leading-spaces) ; Warning: Do not eliminate the message about print-gv without first reading ; the comment about it in ev-fncall-guard-er-msg. (declare (xargs :mode :program)) (msg "~s0To debug see :DOC print-gv, see :DOC trace, and see :DOC wet." (if two-leading-spaces " " ""))) (defun ignored-attachment-msg (ignored-attachment) (cond (ignored-attachment (msg "~|~%Note that because of logical ~ considerations, attachments (including ~x0) ~ must not be called in this context. See ~ :DOC ignored-attachment." ignored-attachment)) (t ""))) (defdoc ignored-attachment ":Doc-Section miscellaneous why attachments are sometimes not used~/ Attachments provide a way to execute constrained functions. But in some cases, ACL2 will not permit such execution. We discuss this issue briefly here. For more information about attachments, ~pl[defattach]. We illustrate this issue with the following example, discussed below. ~bv[] (encapsulate () (defstub foo () t) (defn foo-impl-1 () t) (defattach foo foo-impl-1) (defn foo-impl-2 () nil) (local (defattach foo foo-impl-2)) (defmacro mac () (foo)) ; nil in the first pass, t in the second pass (defun bar () (mac)) ; nil in the first pass, t in the second pass (defthm bar-is-nil (equal (bar) nil)) ) ~ev[] Here, a non-executable function ~c[foo] is introduced with no constraints, and is provided two contradictory implementations, ~c[foo-impl-1] and ~c[foo-impl-2]. A function, ~c[bar], is defined using a macro, ~c[mac], whose expansion depends on which of ~c[foo-impl-1] or ~c[foo-impl-2] is attached to ~c[foo]. If ACL2 were to allow this, then as indicated by the comments above, ~c[(bar)] would be defined to be ~c[nil] on the first pass of the ~ilc[encapsulate] form, where ~c[foo] is attached to ~c[foo-impl-2]; but ~c[(bar)] would be defined to be ~c[t] on the second pass, where ~c[foo] is attached to ~c[foo-impl-1] because the second ~ilc[defattach] call is ~ilc[local]. Thus, after execution of the ~c[encapsulate] form, ~c[(bar)] would be provably equal to ~c[t] even though there would be a theorem, ~c[bar-is-nil] ~-[] proved during the first pass of the ~c[encapsulate] ~-[] saying that ~c[(bar)] is ~c[nil]! Fortunately, ACL2 does not permit this to happen. The example above produces the following output. ~bv[] ACL2 !>>(DEFUN BAR NIL (MAC)) ACL2 Error in ( DEFUN BAR ...): In the attempt to macroexpand the form (MAC), evaluation of the macro body caused the following error: ACL2 cannot ev the call of undefined function FOO on argument list: NIL Note that because of logical considerations, attachments (including FOO-IMPL-2) must not be called in this context. ~ev[] We see, then, the importance of disallowing evaluation using attachments during macroexpansion. ACL2 is careful to avoid attachments in situations, like this one, where using attachments could be unsound.~/ We conclude with an example illustrating how ~ilc[make-event] can be used to work around the refusal of ACL2 to use attachments during macroexpansion. The idea is that ~ilc[make-event] expansions are stored, and this avoids the issue of ~ilc[local] attachments. In particular, for the example below, the second ~ilc[defattach] affects the body of ~c[f2] even though that ~ilc[defattach] is ~ilc[local], because the expansion of the corresponding ~ilc[make-event] is saved during the first pass of ~ilc[certify-book], when full admissibility checks are done. Then even after including the book, the definition of ~c[f2] will be based on the second (~ilc[local]) ~ilc[defattach] form below. ~bv[] (in-package \"ACL2\") (defun body-1 (name formals body) (declare (ignore name)) `(if (consp ,(car formals)) ,body nil)) (defun body-2 (name formals body) (declare (ignore name)) `(if (acl2-numberp ,(car formals)) ,body t)) (defmacro defun+ (name formals body) `(make-event (if (foo) ; attachable stub '(defun ,name ,formals ,(body-1 name formals body)) '(defun ,name ,formals ,(body-2 name formals body))))) ;;; (defun+ f1 (x y) (cons x y)) ; fails because foo has no attachment (defstub foo () t) (defn foo-true () t) (defn foo-false () nil) (defattach foo foo-true) (defun+ f1 (x y) (cons x y)) (local (defattach foo foo-false)) (defun+ f2 (x y) (cons x y)) (assert-event (equal (f1 3 t) nil)) (assert-event (equal (f2 3 t) (cons 3 t))) ~ev[]~/") (defun ev-fncall-null-body-er-msg (ignored-attachment fn args) (cond ((eq fn :non-exec) ; This is a special case for calls of (non-exec form), where in this case, args ; is form. (assert$ (null ignored-attachment) ; This case has nothing to do with attachments. (msg "ACL2 has been instructed to cause an error because of an attempt ~ to evaluate the following form (see :DOC non-exec):~|~% ~ ~x0.~|~%~@1" args ; actually, the form (error-trace-suggestion nil)))) (t (msg "ACL2 cannot ev the call of undefined function ~x0 on argument ~ list:~|~%~x1~@2~|~%~@3" fn args (ignored-attachment-msg ignored-attachment) (error-trace-suggestion nil))))) (defun ev-fncall-null-body-er (ignored-attachment fn args latches) (mv t (ev-fncall-null-body-er-msg ignored-attachment fn args) latches)) (defun ev-fncall-creator-er-msg (fn) (msg "An attempt has been made to call the stobj creator function ~x0. This ~ error is being reported even though guard-checking may have been turned ~ off, because ACL2 does not support non-compliant live stobj manipulation. ~ ~ If you did not explicitly call ~x0 then this error is probably due to ~ an attempt to evaluate a with-local-stobj form directly in the top-level ~ loop. Such forms are only allowed in the bodies of functions and in ~ theorems. Also see :DOC with-local-stobj.~@1" fn (error-trace-suggestion t))) (defun unknown-pkg-error-msg (fn pkg-name) (msg "The call ~x0 is illegal because the argument is not the name of a ~ package currently known to ACL2." (list fn pkg-name))) (defun illegal-msg () (msg "Evaluation aborted.~@0" (error-trace-suggestion t))) (defun program-only-er-msg (fn args safe-mode) (msg "The call ~x0 is an illegal call of a function that has been marked as ~ ``program-only,'' and hence has special raw Lisp code. This call is ~ illegal because program-only functions are only allowed to invoke their ~ raw Lisp code, but in this case there was an attempt to invoke executable ~ counterpart code ~#1~[because of guard-checking (see :DOC ~ guard-evaluation-table)~/because it is being called under a ``safe mode'' ~ that is used, for example, during macroexpansion~]." (cons fn args) (if safe-mode 1 0))) (defconst *safe-mode-guard-er-addendum* " The guard is being checked because this function is a primitive and a ~ \"safe\" mode is being used for defconst, defpkg, macroexpansion, or ~ another operation where safe mode is required.") (defun find-first-non-nil (lst) (cond ((endp lst) nil) (t (or (car lst) (find-first-non-nil (cdr lst)))))) ; For a discussion of stobj latching, see Stobj Latching below. (defun latch-stobjs1 (stobjs-out vals latches) (cond ((endp stobjs-out) latches) ((car stobjs-out) (let ((temp (assoc-eq (car stobjs-out) latches))) (cond ; Suppose (car stobjs-out) is some stobj, $st, and (car vals) is the ; new value, val. We wish to bind '$st in latches to val. It is an ; error if we can't find a binding for '$st. Otherwise, put-assoc-eq ; will do the job. But in the special, live, case, val is EQ to the ; current binding of '$st in latches, because all the objects are ; live. In this case, we can avoid the put-assoc-eq and just leave ; latches unchanged. The clause below is safe whether val is a live ; object or not: if it's the same thing as what is there, the ; put-assoc-eq won't change latches anyway. But the real intent of ; this clause is make the final value of latches, in general, EQ to ; the original value of latches. ((not temp) (er hard! 'latch-stobjs "We are trying to latch a value for the single-threaded ~ object named ~x0, but there is no entry for that name in ~ the stobj latches provided. The possible latch names are ~ ~&1.~#2~[~/ This error most likely is caused by the ~ attempt to ev a form that is not ``supposed'' to mention ~ stobjs but does. Often when dealing with forms that are ~ not supposed to mention stobjs we call ev with last ~ argument NIL and then ignore the resulting latches.~]" (car stobjs-out) (strip-cars latches) (if latches 0 1))) #-acl2-loop-only ((eq (cdr temp) (car vals)) (latch-stobjs1 (cdr stobjs-out) (cdr vals) latches)) (t #-acl2-loop-only (er hard! 'latch-stobjs1 "We had thought that the values in user-stobj-alist match up ~ with the values of corresponding stobjs. Please contact ~ the ACL2 implementors.") #+acl2-loop-only (latch-stobjs1 (cdr stobjs-out) (cdr vals) (put-assoc-eq (car stobjs-out) (car vals) latches)))))) (t (latch-stobjs1 (cdr stobjs-out) (cdr vals) latches)))) (defun latch-stobjs (stobjs-out vals latches) ; Update the latches so that it contains the stobj objects returned in ; val. Val is either a single value or a list of 2 or more values, as ; indicated by stobjs-out. If stobjs-out is nil it is treated as a ; list of as many nils as necessary and no change is made to val. If ; latches is nil, we do nothing. This means that we are not recording ; the ``current'' stobjs and one must be careful to obey the ; restrictions in the Essay on EV. (cond ((null latches) latches) ((null stobjs-out) latches) ((null (cdr stobjs-out)) (cond ((car stobjs-out) ; We call latch-stobjs1 rather than put-assoc-eq to get the error check. (latch-stobjs1 stobjs-out (list vals) latches)) (t latches))) (t (latch-stobjs1 stobjs-out vals latches)))) #-acl2-loop-only ; We deliberately do not assign a value for the following. It is let-bound in ; ev and friends and assigned during the evaluation of *1* functions. If we ; call *1* functions directly in raw Lisp, we will presumably get an ; unbound-variable error, but at least that will call our attention to the fact ; that it should be bound before calling *1* functions. (defvar *raw-guard-warningp*) (defun actual-stobjs-out1 (stobjs-in args user-stobj-alist) (cond ((endp stobjs-in) (assert$ (null args) nil)) (t (let ((rest (actual-stobjs-out1 (cdr stobjs-in) (cdr args) user-stobj-alist))) (cond ((or (null (car stobjs-in)) (eq (car stobjs-in) 'state)) rest) (t (let ((pair (rassoc-equal (car args) user-stobj-alist))) (assert$ pair (cond ((eq (car stobjs-in) (car pair)) rest) (t (acons (car stobjs-in) (car pair) rest))))))))))) (defun apply-symbol-alist (alist lst acc) ; Alist represents a function to apply to each element of lst, a list of ; symbols. (This function is the identity on elements not in the domain of ; alist.) The resulting list is accumulated into acc and reversed. (cond ((endp lst) (reverse acc)) (t (apply-symbol-alist alist (cdr lst) (cons (let ((pair (assoc-eq (car lst) alist))) (cond (pair (cdr pair)) (t (car lst)))) acc))))) (defun apply-inverse-symbol-alist (alist lst acc) ; See apply-symbol-alist. Here, though, we apply the inverse of the mapping ; represented by alist. We assume that the cdrs of alist are suitable for ; testing with eq (i.e., symbols or stobjs). (cond ((endp lst) (reverse acc)) (t (apply-inverse-symbol-alist alist (cdr lst) (cons (let ((pair (rassoc-eq (car lst) alist))) (cond (pair (car pair)) (t (car lst)))) acc))))) (defun actual-stobjs-out (fn args wrld user-stobj-alist) (let ((stobjs-out (stobjs-out fn wrld))) (cond ((all-nils stobjs-out) ; optimization for common case stobjs-out) (t (let ((stobjs-in (stobjs-in fn wrld))) (let ((alist (actual-stobjs-out1 stobjs-in args user-stobj-alist))) (cond (alist (apply-symbol-alist alist stobjs-out nil)) (t stobjs-out)))))))) #-acl2-loop-only (defun raw-ev-fncall (fn args latches w user-stobj-alist hard-error-returns-nilp aok) (let ((*aokp* aok)) (the #+acl2-mv-as-values (values t t t) #-acl2-mv-as-values t (let* ((pair (assoc-eq 'state latches)) (w (if pair (w (cdr pair)) w)) ; (cdr pair) = *the-live-state* (throw-raw-ev-fncall-flg t) (*1*fn (*1*-symbol fn)) (applied-fn (cond ((fboundp *1*fn) *1*fn) ((and (global-val 'boot-strap-flg w) (not (global-val 'boot-strap-pass-2 w))) fn) (t (er hard 'raw-ev-fncall "We had thought that *1* functions were ~ always defined outside the first pass of ~ initialization, but the *1* function for ~ ~x0, which should be ~x1, is not." fn *1*fn)))) (stobjs-out (cond ((eq fn 'return-last) ; Things can work out fine if we imagine that return-last returns a single ; value: in the case of (return-last ... (mv ...)), the mv returns a list and ; we just pass that along. '(nil)) ; The next form was originally conditionalized with #+acl2-extra-checks, but we ; want to do this unconditionally. (latches ; optimization (actual-stobjs-out fn args w user-stobj-alist)) (t (stobjs-out fn w)))) (val (catch 'raw-ev-fncall (cond ((not (fboundp fn)) (er hard 'raw-ev-fncall "A function, ~x0, that was supposed to be ~ defined is not. Supposedly, this can only ~ arise because of aborts during undoing. ~ There is no recovery from this erroneous ~ state." fn))) (prog1 (let ((*hard-error-returns-nilp* hard-error-returns-nilp)) #-acl2-mv-as-values (apply applied-fn args) #+acl2-mv-as-values (cond ((null (cdr stobjs-out)) (apply applied-fn args)) (t (multiple-value-list (apply applied-fn args))))) (setq throw-raw-ev-fncall-flg nil)))) ; It is important to rebind w here, since we may have updated state since the ; last binding of w. (w (if pair ; We use the live state now if and only if we used it above, in which case (cdr ; pair) = *the-live-state*. (w (cdr pair)) w))) ; Observe that if a throw to 'raw-ev-fncall occurred during the ; (apply fn args) then the local variable throw-raw-ev-fncall-flg ; is t and otherwise it is nil. If a throw did occur, val is the ; value thrown. (cond (throw-raw-ev-fncall-flg (mv t (ev-fncall-msg val w user-stobj-alist) latches)) (t #-acl2-mv-as-values ; adjust val for the multiple value case (let ((val (cond ((null (cdr stobjs-out)) val) (t (cons val (mv-refs (1- (length stobjs-out)))))))) (mv nil val ; The next form was originally conditionalized with #+acl2-extra-checks, with ; value latches when #-acl2-extra-checks; but we want this unconditionally. (latch-stobjs stobjs-out ; adjusted to actual-stobjs-out val latches))) #+acl2-mv-as-values ; val already adjusted for multiple value case (mv nil val ; The next form was originally conditionalized with #+acl2-extra-checks, with ; value latches when #-acl2-extra-checks; but we want this unconditionally. (latch-stobjs stobjs-out ; adjusted to actual-stobjs-out val latches)))))))) (defun translated-acl2-unwind-protectp4 (term) ; This hideous looking function recognizes those terms that are the ; translations of (acl2-unwind-protect "expl" body cleanup1 cleanup2). ; The acl2-unwind-protect macro expands into an MV-LET and that MV-LET ; is translated in one of two ways depending on whether it occurs in a ; definition body (i.e., stobjs-out of translate11 is non-t) or in a ; definition (i.e., stobjs-out is t). We look for both translations. ; We return 4 results. The first is t or nil according to whether ; term is of one of the two forms. If nil, the other results are nil. ; If term is of either form, we return in the other three results: ; body, cleanup1 and cleanup2 such that term is equivalent to ; (acl2-unwind-protect "expl" body cleanup1 cleanup2). ; WARNING: This function must be kept in sync with the defmacro of ; acl2-unwind-protect, the translate1 clauses dealing with mv-let and let, and ; the defmacro of mv-let. (case-match term ((('LAMBDA (mv . vars) (('LAMBDA ('ACL2-UNWIND-PROTECT-ERP 'ACL2-UNWIND-PROTECT-VAL 'STATE . vars) ('IF 'ACL2-UNWIND-PROTECT-ERP ('(LAMBDA (STATE ACL2-UNWIND-PROTECT-VAL ACL2-UNWIND-PROTECT-ERP) (CONS ACL2-UNWIND-PROTECT-ERP (CONS ACL2-UNWIND-PROTECT-VAL (CONS STATE 'NIL)))) cleanup1 'ACL2-UNWIND-PROTECT-VAL 'ACL2-UNWIND-PROTECT-ERP) ('(LAMBDA (STATE ACL2-UNWIND-PROTECT-VAL ACL2-UNWIND-PROTECT-ERP) (CONS ACL2-UNWIND-PROTECT-ERP (CONS ACL2-UNWIND-PROTECT-VAL (CONS STATE 'NIL)))) cleanup2 'ACL2-UNWIND-PROTECT-VAL 'ACL2-UNWIND-PROTECT-ERP))) '(MV-NTH '0 mv) '(MV-NTH '1 mv) '(MV-NTH '2 mv) . vars)) body . vars) (declare (ignore mv vars)) ; Does it matter what mv is? In principle it surely does: if mv is some ; screwy variable then it might be that this term doesn't actually have the ; semantics we are about to ascribe to it. We know mv is not in vars since ; this is a termp and mv and vars are used in the same lambda arglist. But ; what if mv is, say, ACL2-UNWIND-PROTECT-ERP? Is the semantics affected? ; No: mv's binding, no matter what name we chose outside of vars, is ; unaffected. Similarly, the names in vars are irrelevant, given that we know ; they don't include ACL2-UNWIND-PROTECT-ERP, etc., which is assured by the ; same observation that term is a termp. (mv t body cleanup1 cleanup2)) ((('LAMBDA ('ACL2-UNWIND-PROTECT-ERP 'ACL2-UNWIND-PROTECT-VAL 'STATE . vars) ('IF 'ACL2-UNWIND-PROTECT-ERP ('(LAMBDA (STATE ACL2-UNWIND-PROTECT-VAL ACL2-UNWIND-PROTECT-ERP) (CONS ACL2-UNWIND-PROTECT-ERP (CONS ACL2-UNWIND-PROTECT-VAL (CONS STATE 'NIL)))) cleanup1 'ACL2-UNWIND-PROTECT-VAL 'ACL2-UNWIND-PROTECT-ERP) ('(LAMBDA (STATE ACL2-UNWIND-PROTECT-VAL ACL2-UNWIND-PROTECT-ERP) (CONS ACL2-UNWIND-PROTECT-ERP (CONS ACL2-UNWIND-PROTECT-VAL (CONS STATE 'NIL)))) cleanup2 'ACL2-UNWIND-PROTECT-VAL 'ACL2-UNWIND-PROTECT-ERP))) ('MV-NTH ''0 body) ('MV-NTH ''1 body) ('MV-NTH ''2 body) . vars) (declare (ignore vars)) (mv t body cleanup1 cleanup2)) (& (mv nil nil nil nil)))) (defun translated-acl2-unwind-protectp (term) ; Just for convenience we define the predicate version of translated-acl2- ; unwind-protectp4 to return t or nil according to whether term is the ; translation of an acl2-unwind-protect expression. (mv-let (ans body cleanup1 cleanup2) (translated-acl2-unwind-protectp4 term) (declare (ignore body cleanup1 cleanup2)) ans)) (defun stobjp (x known-stobjs w) ; We recognize whether x is to be treated as a stobj name. Known-stobjs is a ; list of all such names, or else T, standing for all stobj names in w. During ; translation, only certain known stobjs in w are considered stobjs, as per the ; user's :stobjs declare xargs. If you want to know whether x has been defined ; as a stobj in w, use known-stobjs = t. ; Slight abuse permitted: Sometimes known-stobjs will be a list of stobj flags! ; E.g., we might supply (NIL NIL STATE NIL $S) where (STATE $S) is technically ; required. But we should never ask if NIL is a stobj because we only ask this ; of variable symbols. But just to make this an ironclad guarantee, we include ; the first conjunct below. (and x (symbolp x) (if (eq known-stobjs t) (getprop x 'stobj nil 'current-acl2-world w) (member-eq x known-stobjs)))) ; Essay on EV ; Ev, below, will take the following arguments: ; (ev form alist state latches hard-error-returns-nilp aok) ; It returns (mv erp val latches'). ; Ev is actually defined in terms of ev-rec, an analogous function that ; takes the ACL2 world rather than state. ; Hard-error-returns-nil is explained in the comment in hard-error. ; We do not deal with it further below. ; Aok is short for "Attachments are OK", and as the name suggests, ; allows the use of attachments when non-nil. This parameter is discussed at ; some length near the end of this Essay. Till then, we assume that its value ; is nil. ; Imprecise Spec: If erp is t, some evaluation error occurred (e.g., ; an unbound variable was encountered). Otherwise, erp is nil, val is ; the value of form under alist, and latches' is the final value of ; all the single-threaded objects after the evaluation of form. ; But there are many subtle issues here having to do with the handling ; of single-threaded objects. In the following discussion we use ; (bump state) as a generic function that changes state, as by ; incrementing a global variable in state and returning the modified ; state. ; Assumptions on the input to EV: ; (0) If latches is nil, then either form is known not to modify any ; stobjs (in which case it really doesn't matter what latches is) or ; else there are no live stobjs in alist. In short, if latches is ; nil, we don't keep track of the current values of the stobjs but you ; better not ev a form on a live object (because it will actually ; change the object but not record the new current value on latches). ; (1) If latches is non-nil, then if a stobj name, such as STATE, is bound ; in alist to some value s then ; (1a) s is of the correct shape for that stobj and ; (1b) that stobj name is bound in latches to s. ; Informally, the initial values of the stobjs in alist are identical ; to their initial current values and consistent with the stobj ; definitions. ; (2) If alist binds a stobj name to a live object, then form must be ; single-threaded. ; Clarification of the output spec: ; If no stobj names are bound in alist to live objects, then the ; latches on input may be nil and the final latches may ; be ignored. ; If form is not single-threaded, the meaning of the final latches ; is essentially random. ; In the most common case (where we are using ev to evaluate a form ; typed by the user at the top-level), state is *the-live-state*, all ; the stobj names are bound in alist to their current live objects ; (including 'state to *the-live-state*), and form is single-threaded. ; Observations about the Assumptions ; The only way alist can bind a stobj name to a live object is if we ; did that in our own source code. In particular, a user cannot write ; (list (cons 'state state) (cons '$s $s)), unless the user has access to ; something like coerce-state-to-object. These comments assume such ; magic functions have been made untouchable. ; No live object can be in the final latches unless they were ; there to begin with. If a live object is in the final current ; stobjs, then it was put there by a stobj producing fncall. But that ; would mean there was a live stobj in alist. That, in turn, means ; the same live object was originally in the initial current stobjs. ; Thus, the only time live objects appear in the final latches ; is if we're in our own source code. ; We guarantee, via functions like trans-eval, that assumptions (1) ; and (2) are met in all our calls of ev. ; Further Discussion of the Assumptions: ; Suppose that the symbol 'state is bound in alist to s. Suppose the ; value of the formal parameter state is d. Both s and d are ; state-ps. We call the latter state d because it is the state from ; which ev obtains the definitions of the functions. We also use d to ; determine whether guards should be checked. D is not changed in ev, ; except to decrement the big clock in it to ensure termination. ; By assumption (1), we know that the binding of state in ; latches is s, initially. But in general, the two bindings ; can differ: the binding of state in alist is the initial value of ; state and the binding in the final latches is the final value ; of state. ; Generally speaking, d is *the-live-state*. Indeed, at one point we ; believed: ; The Bogus Live State Claim for :Program Mode Functions: If a ; :program mode function takes STATE as an argument then the function ; can only be evaluated on the live state. ; Below I give a ``proof'' of this claim, for a call of ev stemming ; from a legal form typed by the user to the top-level ACL2 loop. ; Then I give a counterexample! ; ``PROOF:'' The call was translated. Since ev is a :program mode ; function, the call cannot appear in a theorem or other context in ; which the stobj restrictions were not enforced. Hence, the only ; allowable term in the state slot is state itself. Hence, state must ; be *the-live-state*, as it is at the top of LP. ; Now here is a way to run ev from within the loop on a state other ; than the live state: Ev a call of ev. Here is a concrete form. ; First, go outside the loop and call (build-state) to obtain a dummy ; state. I will write that '(NIL ... NIL). At present, it has 15 ; components, most of which are nil, but some, like the initial global ; table, are non-trivial. Then inside the loop execute: ; (let ((st (build-state))) ; (ev `(ev 'a '((a . 1)) ',st 'nil 'nil 't) nil state nil nil t)) ; The outermost state above is indeed the live one, but the inner ev is ; executed on a dummy state. The computation above produces the result ; (NIL (NIL 1 NIL) NIL). ; The inner state object has to pass the state-p predicate if guard ; checking is enabled in the outer state. If guard checking is turned ; off in the live state, the following example shows the inner ev ; running on something that is not even a state-p. To make this ; example work, first evaluate :set-guard-checking nil. ; (ev '(ev 'a '((a . 1)) '(nil nil nil nil nil 0) 'nil 'nil 't) ; nil state nil nil t) ; The 0, above, is the big-clock-entry and must be a non-negative ; integer. The result is (NIL (NIL 1 NIL) NIL). ; Finally, the example below shows the inner ev running a function, ; foo, defined in the dummy world. It doesn't matter if foo is ; defined in the live state or not. The example below shows the state ; returned by build-state at the time of this writing, but modified to ; have a non-trivial CURRENT-ACL2-WORLD setting giving FORMALS and a ; BODY to the symbol FOO. ; (ev '(ev '(foo a) ; '((a . 1)) ; '(NIL NIL ; ((ACCUMULATED-TTREE) ; (AXIOMSP) ; (BDDNOTES) ; (CERTIFY-BOOK-FILE) ; (CONNECTED-BOOK-DIRECTORY) ; (CURRENT-ACL2-WORLD ; . ((foo formals . (x)) (foo body . (cons 'dummy-foo x)))) ; (CURRENT-PACKAGE . "ACL2") ; (EVISCERATE-HIDE-TERMS) ; (FMT-HARD-RIGHT-MARGIN . 77) ; (FMT-SOFT-RIGHT-MARGIN . 65) ; (GSTACKP) ; (GUARD-CHECKING-ON . T) ; (INFIXP) ; (INHIBIT-OUTPUT-LST SUMMARY) ; (IN-LOCAL-FLG . NIL) ; (LD-LEVEL . 0) ; (LD-REDEFINITION-ACTION) ; (LD-SKIP-PROOFSP) ; (MORE-DOC-MAX-LINES . 45) ; (MORE-DOC-MIN-LINES . 35) ; (MORE-DOC-STATE) ; (PRINT-DOC-START-COLUMN . 15) ; (PROMPT-FUNCTION . DEFAULT-PRINT-PROMPT) ; (PROOF-TREE-CTX) ; (PROOFS-CO ; . ACL2-OUTPUT-CHANNEL::STANDARD-CHARACTER-OUTPUT-0) ; (SKIPPED-PROOFSP) ; (STANDARD-CO ; . ACL2-OUTPUT-CHANNEL::STANDARD-CHARACTER-OUTPUT-0) ; (STANDARD-OI ; . ACL2-OUTPUT-CHANNEL::STANDARD-OBJECT-INPUT-0) ; (TIMER-ALIST) ; (TRIPLE-PRINT-PREFIX . " ") ; (UNDONE-WORLDS-KILL-RING NIL NIL NIL) ; (UNTOUCHABLE-FNS) ; (UNTOUCHABLE-VARS) ; (WINDOW-INTERFACEP) ; (WORMHOLE-NAME)) ; NIL NIL 4000000 ; NIL NIL 1 NIL NIL NIL NIL NIL NIL) ; 'nil 'nil 't) nil state nil nil t) ; The output of the ev above is (NIL (NIL (DUMMY-FOO . 1) NIL) NIL). ; The above example can be made slightly more interesting by replacing ; the three occurrences of FOO by EV. It still produces the same ; thing and illustrate the fact that EV doesn't mean what you might ; think it means once you get into an EV! ; The intuition that ``d must be *the-live-state*'' is only true at ; the outermost call of EV. But things take care of themselves inside ; subsequent calls because, if d is not *the-live-state*, EV just runs ; as defined, whatever that means. ; Stobj Latching: How Do We Compute the Final Latches? ; This is simpler than it at first appears: First, we map over the ; term in evaluation order. Every time we apply a function symbol to ; a list of (evaluated) terms, we ``latch'' into latches each of ; the stobj values indicated by the symbol's stobjs-out. ; The order of the sweep is controlled by ev and ev-lst. But all the ; latching is done by ev-fncall. This is surprising because ev-fncall ; does not handle LAMBDAs and translation has entirely eliminated all ; MV-LETs and MVs. ; Let us consider some examples to see why this works -- and to drive ; home some points it took me a while to see. In the following, ; (defun bump (state) (f-put-global 'bump (@ bump) state)) ; (defun bump3 (x state) (let ((state (bump state))) (mv nil x state))) ; Consider the translate (==>) of ; :trans (let ((state (bump state))) ; (mv a state b)) ; ==> ; ((LAMBDA (STATE B A) ; (CONS A (CONS STATE (CONS B 'NIL)))) ; (BUMP STATE) ; B A) ; Sweep order is (BUMP STATE), B, A, and then the CONS nest. Of these, only ; the BUMP has a non-trivial stobjs-out. We latch the state coming out of ; (BUMP STATE). ; :trans (mv-let (erp val state) ; (bump3 x state) ; (mv (and erp val) (cons erp val) state)) ; ==> ; ((LAMBDA (MV) ; ((LAMBDA (ERP VAL STATE) ; (CONS (IF ERP VAL 'NIL) ; (CONS (CONS ERP VAL) ; (CONS STATE 'NIL)))) ; (MV-NTH '0 MV) ; (MV-NTH '1 MV) ; (MV-NTH '2 MV))) ; (BUMP3 X STATE)) ; We latch the third value of (BUMP3 X STATE), when we ev-fncall ; BUMP3. No other function causes us to latch, so that is the final ; latches. ; :trans (mv-let (erp val state) ; (bump3 x state) ; (let ((state (bump state))) ; (mv erp val state))) ; ==> ; ((LAMBDA (MV) ; ((LAMBDA (ERP VAL STATE) ; ((LAMBDA (STATE VAL ERP) ; (CONS ERP (CONS VAL (CONS STATE 'NIL)))) ; (BUMP STATE) ; VAL ERP)) ; (MV-NTH '0 MV) ; (MV-NTH '1 MV) ; (MV-NTH '2 MV))) ; (BUMP3 X STATE)) ; We latch the third value of (BUMP3 X STATE), when we ev-fncall BUMP3. ; The next non-trivial stobjs-out function we ev-fncall is the BUMP. ; We latch its result, which gives us the final latches. ; The restrictions imposed by translate ensure that we will never ; encounter terms like (fn a (bump state) b (bump state) c) where ; there is more than one latched stobj coming out of an arglist. But ; we do not exploit this fact. We just latch every stobj-out as we go ; across the args. Similarly, the translate restrictions ensure that ; if a stobj is returned by some function, then it gets out. So we ; can latch it when it is returned by the function, even though it ; apparently goes into a CONS nest, say, from which it may not, a ; priori, get out. ; We close with a discussion of the final argument of ev and many other ; evaluator functions, aok. In short: The safe value for aok is nil, but it is ; more powerful (fewer aborts) to use t rather than nil for aok, if that is ; sound. Unless you are writing ACL2 system code, it probably is sound to use ; t. But now we discuss in more depth the question of assigning a value to ; aok. ; Most or all of the evaluator functions (ev, ev-fncall, trans-eval, ; simple-translate-and-eval, etc.) have a final argument called aok, which is ; mnemonic for "attachments OK". The conservative value to use is nil, which ; means that no attachments (in the sense of defattach) will be used by the ; evaluator. But if you want attachments to be allowed by the evaluator, then ; use aok = t. ; In ACL2's own source code, aok is usually t, but it is (and must of course, ; be) nil whenever we are simplifying terms during a proof. See the Essay on ; Defattach for related discussion. ; Here, in brief, is the logical story (which is important to understand when ; deciding to use aok=t). The evaluator functions can all be thought of as ; producing a result that is provably equal to a given term. But the question ; is: Provably equal in what formal theory? The "official" theory of the ; current ACL2 world has nothing to do with attachments, and is the theory for ; which we have a prover. So if the rewriter, say, wants to use ev-fncall to ; replace one term by another, the input and output terms should be provably ; equal without attachments, which is why we use aok=nil in the call of ; ev-fncall under rewrite. On the other hand, in the top-level loop we ; presumably want to use all attachments -- the whole point of (defattach f g) ; for an encapsulated f and defined g is to evaluate under the equation (equal ; (f x) (g x)). So the call of trans-eval under ld-read-eval-print has aok=t. ; Thus, if you are calling simple-translate-and-eval for something like hints, ; then probably it's fine to use aok=t -- hints don't affect soundness and one ; might want to take advantage of attachments. As ACL2 evolves, many of its ; system functions may be encapsulated with default attachments, so one will ; want to use aok=t whenever possible in order to avoid an "undefined function" ; error when such a system function is called. (defun acl2-system-namep (name wrld) ; Name is a name defined in wrld. We determine whether it is one of ours or is ; user-defined. ; If name is not defined -- more precisely, if we have not yet laid down an ; 'absolute-event-number property for it -- then we return nil except in the ; boot-strap world. (cond ((global-val 'boot-strap-flg wrld) t) (t (getprop name 'predefined nil 'current-acl2-world wrld)))) #+acl2-loop-only (encapsulate ; We introduce big-n and decrement-big-n with no axioms. We could certainly ; add axioms, namely that (big-n) is a positive integer and decrement-big-n ; decrements, but we choose not to do so. Instead, we keep these axiom-free ; and introduce executable versions in program mode, just below. We imagine ; that n is a positive integer larger than the lengths of all computations that ; will ever take place with ACL2, and that decrement-big-n is 1-. We also make ; big-n untouchable, since without that we have been able to prove nil, as ; follows: ; (in-package "ACL2") ; (defun foo () (big-n)) ; (defthm bad1 (equal (foo) '(nil)) :rule-classes nil) ; (defthm bad2 ; (equal (big-n) '(nil)) ; :hints (("Goal" :use bad1 :in-theory (disable (foo)))) ; :rule-classes nil) ; (defun bar () 0) ; (defthm bad3 ; (equal (bar) '(nil)) ; :hints (("Goal" :by (:functional-instance bad2 (big-n bar)))) ; :rule-classes nil) ; (defthm bad ; nil ; :hints (("Goal" :use bad3)) ; :rule-classes nil) ; We also make decrement-big-n and zp-big-n untouchable, just because we are a ; bit paranoid here. (((big-n) => *) ((decrement-big-n *) => *) ((zp-big-n *) => *)) (local (defun big-n () 0)) (local (defun decrement-big-n (n) (declare (ignore n)) 0)) (local (defun zp-big-n (n) (declare (ignore n)) nil))) #-acl2-loop-only (progn ; (defconstant *big-n-special-object* '(nil . nil)) has been moved to ; acl2.lisp, to avoid a CLISP compiler warning. (defun big-n () *big-n-special-object*) (defmacro decrement-big-n (n) `(if (eq ,n *big-n-special-object*) *big-n-special-object* (1- ,n))) (defmacro zp-big-n (n) `(if (eq ,n *big-n-special-object*) nil (zp ,n)))) #-acl2-loop-only (defparameter *ev-shortcut-okp* ; The code for ev-fncall-rec has a shortcut, calling raw-ev-fncall to execute ; using *1* functions. Because the *1* functions use (live) state globals ; guard-checking-on and safe-mode, these need to agree with the corresponding ; parameters of ev-fncall-rec in order for it to be sound to call ; raw-ev-fncall. We may bind *ev-shortcut-okp* to t when we know that this ; agreement is ensured. ; There are times where avoiding the shortcut can get us into trouble. In ; particular, we have seen a case where the logic code for an ev-nest function ; produced nil for a call of state-p or state-p1 on *the-live-state*. nil) (defun w-of-any-state (st) ; This returns (w state) but, unlike w, st is not (known to be) ; single-threaded, so it can be used on the binding of 'STATE in the latches of ; a call of a function in the ev nest. In the raw Lisp case, we have the same ; f-get-global code as in the definition of w. For the logic, we open up ; f-get-global and then get-global to get the body below. #-acl2-loop-only (cond ((live-state-p st) (return-from w-of-any-state (f-get-global 'current-acl2-world st)))) (cdr (assoc 'current-acl2-world (global-table st)))) (defun untranslate-preprocess-fn (wrld) (declare (xargs :guard (plist-worldp wrld))) (cdr (assoc-eq 'untranslate-preprocess (table-alist 'user-defined-functions-table wrld)))) (defmacro untranslate* (term iff-flg wrld) ; We need to call untranslate in ev-fncall-guard-er and ev-fncall-msg, where we ; have not yet called ev-fncall. So we define this version of untranslate now ; and defer untranslate (and untranslate-lst) until after defining the ev ; family of functions. We document in the guard below our expectation that ; wrld is a symbol, in order to avoid any overhead (e.g., from defabbrev). (declare (xargs :guard (symbolp wrld))) `(untranslate1 ,term ,iff-flg (untrans-table ,wrld) (untranslate-preprocess-fn ,wrld) ,wrld)) #-acl2-loop-only (defmacro raw-guard-warningp-binding () ; We bind *raw-guard-warningp* in calls of ev-fncall, ev, ev-lst, ev-w, ; ev-w-lst, and ev-fncall-w. The initial binding is t if guard-checking is on, ; else nil. When a *1* function is poised to call warn-for-guard-body to print ; a warning related to guard violations, it first checks that ; *raw-guard-warningp*. Hence, we do not want to re-assign this variable once ; it is bound to nil by warn-for-guard-body, because we only want to see the ; corresponding guard warning once per top-level evaluation. We do however ; want to re-assign this variable from t to nil once the warning has been ; printed and also if guard-checking has been turned off, in particular for the ; situation involving the prover that is described in the next paragraph. (But ; if guard-checking were, surprisingly, to transition instead from nil to t, ; and we failed to re-assign this variable from nil to t, we could live with ; that.) ; Note that *raw-guard-warningp* will be bound to t just under the trans-eval ; at the top level. If we then enter the prover we will bind guard-checking-on ; to nil, and we then want to re-bind *raw-guard-warningp* to nil if we enter ; ev-fncall during the proof, so that the proof output will not contain guard ; warning messages. (This was handled incorrectly in Version_2.9.1.) '(if (and (boundp '*raw-guard-warningp*) (null *raw-guard-warningp*)) nil (eq (f-get-global 'guard-checking-on *the-live-state*) t))) (defun untouchable-fn-p (fn w) (and (not (member-eq fn *user-defined-functions-table-keys*)) ; optimization (member-eq fn (global-val 'untouchable-fns w)))) (defun save-ev-fncall-guard-er (fn guard stobjs-in args) (wormhole-eval 'ev-fncall-guard-er-wormhole '(lambda (whs) (make-wormhole-status whs :ENTER (list fn guard stobjs-in args))) nil)) (defrec attachment ; See the Essay on Merging Attachment Records. ((g . ext-succ) . (components . pairs)) nil) (defrec attachment-component ; See the Essay on Merging Attachment Records. ((ext-anc . ord-anc) . path) nil) (defun attachment-record-pairs (records acc) (cond ((endp records) acc) (t (attachment-record-pairs (cdr records) (append (access attachment (car records) :pairs) acc))))) (defun all-attachments (wrld) (attachment-record-pairs (global-val 'attachment-records wrld) nil)) (defun gc-off1 (guard-checking-on) (member-eq guard-checking-on '(nil :none))) (defun gc-off (state) (gc-off1 (f-get-global 'guard-checking-on state))) #-acl2-loop-only (progn (defvar *return-last-arg2*) (defvar *return-last-arg3*) (defvar *return-last-alist*) (defvar *return-last-fn-w*) (defvar *return-last-fn-user-stobj-alist*) (defvar *return-last-fn-big-n*) (defvar *return-last-fn-safe-mode*) (defvar *return-last-fn-gc-off*) (defvar *return-last-fn-latches*) (defvar *return-last-fn-hard-error-returns-nilp*) (defvar *return-last-fn-aok*)) (defun return-last-lookup (sym wrld) ; Keep this in sync with chk-return-last-entry and with the comment about these ; macros in *initial-return-last-table*. (assert$ (and (symbolp sym) sym) ; otherwise we shouldn't call return-last-lookup (case sym (progn 'prog2$) (mbe1-raw 'mbe1) (ec-call1-raw 'ec-call1) (with-guard-checking1-raw 'with-guard-checking1) (otherwise (cdr (assoc-eq sym (table-alist 'return-last-table wrld))))))) (defun make-let-or-let* (bindings body) (declare (xargs :guard (doubleton-list-p bindings))) (cond ((and bindings (null (cdr bindings))) (case-match body (('let ((& &)) x) `(let* (,@bindings ,@(cadr body)) ,x)) (('let* rest-bindings x) `(let* ,(cons (car bindings) rest-bindings) ,x)) (& (make-let bindings body)))) (t (make-let bindings body)))) (defmacro untranslate*-lst (lst iff-flg wrld) ; See untranslate*. (declare (xargs :guard (symbolp wrld))) `(untranslate1-lst ,lst ,iff-flg (untrans-table ,wrld) (untranslate-preprocess-fn ,wrld) ,wrld)) (defun apply-user-stobj-alist-or-kwote (user-stobj-alist lst acc) ; This function accumulates into acc (eventually reversing the accumulation) ; the result of replacing each element of lst either with its reverse lookup in ; user-stobj-alist, if it is a bad-atom (i.e., a stobj -- which it is!), else ; with the result of quoting that element. ; We considered using rassoc-eq in place of rassoc-equal below, but that would ; prevent guard verification down the road (unless we change to guard of eq to ; allow bad-atoms in place of symbols). So we are content to use rassoc-equal, ; which may be quite fast on bad atoms, and since (as of this writing) we only ; use this function for occasional user-level error and debug messages. (cond ((endp lst) (reverse acc)) (t (apply-user-stobj-alist-or-kwote user-stobj-alist (cdr lst) (cons (cond ((bad-atom (car lst)) (let ((pair (rassoc-equal (car lst) user-stobj-alist))) (cond (pair (car pair)) (t ; We are looking at a local stobj or a stobj bound by stobj-let. '||)))) (t (kwote (car lst)))) acc))))) (mutual-recursion ; Here we combine what may naturally be thought of as two separate ; mutual-recursion nests: One for evaluation and one for untranslate. However, ; functions in the ev nest call untranslate1 for error messages, and ; untranslate1 calls ev-fncall-w. We are tempted to place the definitions of ; the untranslate functions first, but Allegro CL (6.2 and 7.0) produces a ; bogus warning in that case (which goes away if the char-code case is ; eliminated from ev-fncall-rec-logical!). (defun ev-fncall-rec-logical (fn args w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) ; This is the "slow" code for ev-fncall-rec, for when raw-ev-fncall is not ; called. ; The following guard is simply a way to trick ACL2 into not objecting ; to the otherwise irrelevant hard-error-returns-nilp. See the comment ; in ev, below, for a brief explanation. See hard-error for a more ; elaborate one. ; Keep this function in sync with *primitive-formals-and-guards*. (declare (xargs :guard (and (plist-worldp w) (equal hard-error-returns-nilp hard-error-returns-nilp)))) (cond ((zp-big-n big-n) (mv t (cons "Evaluation ran out of time." nil) latches)) (t (let* ((x (car args)) (y (cadr args)) (pair (assoc-eq 'state latches)) (w (if pair (w-of-any-state (cdr pair)) w)) (safe-mode-requires-check (and safe-mode (acl2-system-namep fn w) (not (equal (symbol-package-name fn) "ACL2")))) (guard-checking-off (and gc-off ; Safe-mode defeats the turning-off of guard-checking, as does calling a stobj ; primitive that takes its live stobj as an argument. If the latter changes, ; consider also changing oneify-cltl-code. (not safe-mode-requires-check) (not (let ((st (getprop fn 'stobj-function nil 'current-acl2-world w))) (and st (assoc-eq st latches) (member-eq st (stobjs-in fn w))))))) (extra (and gc-off (cond (safe-mode-requires-check t) ((not guard-checking-off) :live-stobj) (t nil))))) ; Keep this in sync with *primitive-formals-and-guards*. (case fn (ACL2-NUMBERP (mv nil (acl2-numberp x) latches)) (BAD-ATOM<= (cond ((or guard-checking-off (and (bad-atom x) (bad-atom y))) (mv nil (bad-atom<= x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (BINARY-* (cond ((or guard-checking-off (and (acl2-numberp x) (acl2-numberp y))) (mv nil (* x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (BINARY-+ (cond ((or guard-checking-off (and (acl2-numberp x) (acl2-numberp y))) (mv nil (+ x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (UNARY-- (cond ((or guard-checking-off (acl2-numberp x)) (mv nil (- x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (UNARY-/ (cond ((or guard-checking-off (and (acl2-numberp x) (not (= x 0)))) (mv nil (/ x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (< (cond ((or guard-checking-off (and (real/rationalp x) (real/rationalp y))) (mv nil (< x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (CAR (cond ((or guard-checking-off (or (consp x) (eq x nil))) (mv nil (car x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (CDR (cond ((or guard-checking-off (or (consp x) (eq x nil))) (mv nil (cdr x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (CHAR-CODE (cond ((or guard-checking-off (characterp x)) (mv nil (char-code x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (CHARACTERP (mv nil (characterp x) latches)) (CODE-CHAR (cond ((or guard-checking-off (and (integerp x) (<= 0 x) (< x 256))) (mv nil (code-char x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (COMPLEX (cond ((or guard-checking-off (and (real/rationalp x) (real/rationalp y))) (mv nil (complex x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (COMPLEX-RATIONALP (mv nil (complex-rationalp x) latches)) #+:non-standard-analysis (COMPLEXP (mv nil (complexp x) latches)) (COERCE (cond ((or guard-checking-off (or (and (stringp x) (eq y 'list)) (and (character-listp x) (eq y 'string)))) (mv nil (coerce x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (CONS (mv nil (cons x y) latches)) (CONSP (mv nil (consp x) latches)) (DENOMINATOR (cond ((or guard-checking-off (rationalp x)) (mv nil (denominator x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (EQUAL (mv nil (equal x y) latches)) #+:non-standard-analysis (FLOOR1 (cond ((or guard-checking-off (realp x)) (mv nil (floor x 1) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (IF (mv nil (er hard 'ev-fncall-rec "This function should not be called with fn = 'IF!") latches)) (IMAGPART (cond ((or guard-checking-off (acl2-numberp x)) (mv nil (imagpart x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (INTEGERP (mv nil (integerp x) latches)) (INTERN-IN-PACKAGE-OF-SYMBOL (cond ((or guard-checking-off (and (stringp x) (symbolp y))) (mv nil (intern-in-package-of-symbol x y) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (NUMERATOR (cond ((or guard-checking-off (rationalp x)) (mv nil (numerator x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (PKG-IMPORTS (cond ((or guard-checking-off (stringp x)) (mv nil (pkg-imports x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (PKG-WITNESS (cond ((or guard-checking-off (and (stringp x) (not (equal x "")))) (mv nil (pkg-witness x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (RATIONALP (mv nil (rationalp x) latches)) #+:non-standard-analysis (REALP (mv nil (realp x) latches)) (REALPART (cond ((or guard-checking-off (acl2-numberp x)) (mv nil (realpart x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (STRINGP (mv nil (stringp x) latches)) (SYMBOL-NAME (cond ((or guard-checking-off (symbolp x)) (mv nil (symbol-name x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (SYMBOL-PACKAGE-NAME (cond ((or guard-checking-off (symbolp x)) (mv nil (symbol-package-name x) latches)) (t (ev-fncall-guard-er fn args w user-stobj-alist latches extra)))) (SYMBOLP (mv nil (symbolp x) latches)) ; The next two functions have the obvious behavior on standard objects, which ; are the only ones ever present inside ACL2. #+:non-standard-analysis (STANDARDP (mv nil t latches)) #+:non-standard-analysis (STANDARD-PART (mv nil x latches)) #+:non-standard-analysis (I-LARGE-INTEGER ; We could omit this case, allowing a fall-through. (ev-fncall-null-body-er nil fn nil latches)) (otherwise (cond ((and (null args) (car (stobjs-out fn w))) (mv t (ev-fncall-creator-er-msg fn) latches)) (t (let ((alist (pairlis$ (formals fn w) args)) (body (body fn nil w)) (attachment (and aok (cdr (assoc-eq fn (all-attachments w)))))) (mv-let (er val latches) (ev-rec (if guard-checking-off ''t (guard fn nil w)) alist w user-stobj-alist (decrement-big-n big-n) (eq extra t) guard-checking-off latches hard-error-returns-nilp aok) (cond (er (mv er val latches)) ((null val) (ev-fncall-guard-er fn args w user-stobj-alist latches extra)) ((and (eq fn 'hard-error) (not hard-error-returns-nilp)) ; Before we added this case, the following returned nil even though the result ; was t if we replaced ev-fncall-rec-logical by ev-fncall-rec. That wasn't ; quite a soundness bug, event though the latter is defined to be the former, ; because ev-fncall-rec is untouchable; nevertheless the discrepancy was ; troubling. ; (mv-let (erp val ign) ; (ev-fncall-rec-logical 'hard-error '(top "ouch" nil) (w state) ; (user-stobj-alist state) ; 100000 nil nil nil nil t) ; (declare (ignore ign val)) ; erp) (mv t (illegal-msg) latches)) ((eq fn 'throw-nonexec-error) (ev-fncall-null-body-er nil (car args) ; fn (cadr args) ; args latches)) ((member-eq fn '(pkg-witness pkg-imports)) (mv t (unknown-pkg-error-msg fn (car args)) latches)) (attachment (ev-fncall-rec-logical attachment args w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) ((null body) (ev-fncall-null-body-er (and (not aok) attachment) fn args latches)) (t (mv-let (er val latches) (ev-rec body alist w user-stobj-alist (decrement-big-n big-n) (eq extra t) guard-checking-off latches hard-error-returns-nilp aok) (cond (er (mv er val latches)) ((eq fn 'return-last) ; avoid stobjs-out for return-last (mv nil val latches)) (t (mv nil val (latch-stobjs (actual-stobjs-out fn args w user-stobj-alist) val latches))))))))))))))))) (defun ev-fncall-rec (fn args w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) ; WARNING: This function should only be called with *raw-guard-warningp* bound. (declare (xargs :guard (plist-worldp w))) #-acl2-loop-only (cond (*ev-shortcut-okp* (cond ((fboundp fn) ; If fn is unbound and we used the logical code below, we'd get a ; hard error as caused by (formals fn w). (return-from ev-fncall-rec (raw-ev-fncall fn args latches w user-stobj-alist hard-error-returns-nilp aok))))) (t (let ((pair (assoc-eq 'state latches))) (if (and pair (eq (cdr pair) *the-live-state*)) (progn (er hard 'ev-fncall-rec "ACL2 implementation error: An attempt is being made to ~ evaluate a form involving the live state when ~ *ev-shortcut-okp* is nil. Please contact the ACL2 ~ implementors.") (return-from ev-fncall-rec (mv t (cons "Implementation error" nil) latches))))))) (ev-fncall-rec-logical fn args w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok)) #-acl2-loop-only (progn (defvar *return-last-arg2*) (defvar *return-last-arg3*) (defvar *return-last-alist*) (defvar *return-last-fn-w*) (defvar *return-last-fn-user-stobj-alist*) (defvar *return-last-fn-big-n*) (defvar *return-last-fn-safe-mode*) (defvar *return-last-fn-gc-off*) (defvar *return-last-fn-latches*) (defvar *return-last-fn-hard-error-returns-nilp*) (defvar *return-last-fn-aok*)) (defun ev-rec-return-last (fn arg2 arg3 alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) ; This function should only be called when fn is a key of return-last-table, ; and is not mbe1-raw (which is handled directly in ev-rec, to avoid executing ; the :exec code). Moreover, we get here only when the original return-last ; form is given a quoted first argument, so that ev-rec evaluation will treat ; return-last similarly to how it is treated in raw Lisp. See the comment in ; ev-rec about how we leave it to the user not to remove a key from ; return-last-table before passing quotation of that key as the first argument ; of a return-last call. (assert$ (not (eq fn 'mbe1-raw)) (mv-let (er arg2-val latches) (let (#-acl2-loop-only (*aokp* ; See the #-acl2-loop-only definition of return-last and the comment just ; below. Note that fn is not mbe1-raw, so this binding is legal. ; Unlike the raw Lisp definition of return-last, we see no need to bind ; *attached-fn-called* here (for the #+hons version), because that would amount ; to trying to manage the use of memoization with calls of top-level calls of ; ev-rec on behalf of the trans-eval call under ld-read-eval-print. t)) (ev-rec arg2 alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp ; There is no logical problem with using attachments when evaluating the second ; argument of return-last, because logically the third argument provides the ; value(s) of a return-last call. See related treatment of aokp in the ; #-acl2-loop-only definition of return-last. t)) (cond (er (mv er arg2-val latches)) (t (case fn ; We provide efficient handling for some common primitive cases. Keep these ; cases in sync with corresponding cases in the #-acl2-loop-only definition of ; return-last. Note however that mbe1-raw is already handled in ev-rec; we ; thus know that fn is not mbe1-raw. ; In the case of ec-call1 we expect ev-rec to call the appropriate *1* function ; anyhow, so we can treat it as a progn. ((progn ec-call1-raw) (ev-rec arg3 alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) (with-guard-checking1-raw (return-last 'with-guard-checking1-raw arg2-val (ev-rec arg3 alist w user-stobj-alist (decrement-big-n big-n) safe-mode (gc-off1 arg2-val) latches hard-error-returns-nilp aok))) (otherwise #+acl2-loop-only (ev-rec arg3 alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok) ; The following raw Lisp code is a bit odd in its use of special variables. ; Our original motivation was to work around problems that SBCL had with large ; quoted constants in terms passed to eval (SBCL bug 654289). While this issue ; was fixed in SBCL 1.0.43.19, nevertheless we believe that it is still an ; issue for CMUCL and, for all we know, it could be an issue for future Lisps. ; The use of special variables keeps the terms small that are passed to eval. #-acl2-loop-only (let ((*return-last-arg2* arg2-val) (*return-last-arg3* arg3) (*return-last-alist* alist) (*return-last-fn-w* w) (*return-last-fn-user-stobj-alist* user-stobj-alist) (*return-last-fn-big-n* big-n) (*return-last-fn-safe-mode* safe-mode) (*return-last-fn-gc-off* gc-off) (*return-last-fn-latches* latches) (*return-last-fn-hard-error-returns-nilp* hard-error-returns-nilp) (*return-last-fn-aok* aok)) (eval `(,fn *return-last-arg2* (ev-rec *return-last-arg3* *return-last-alist* *return-last-fn-w* *return-last-fn-user-stobj-alist* *return-last-fn-big-n* *return-last-fn-safe-mode* *return-last-fn-gc-off* *return-last-fn-latches* *return-last-fn-hard-error-returns-nilp* *return-last-fn-aok*))))))))))) (defun ev-rec (form alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) ; WARNING: This function should only be called with *raw-guard-warningp* bound. ; See also ev-respecting-ens. ; Note: Latches includes a binding of 'state. See the Essay on EV. ; If you provide no latches and form changes some stobj, a hard error ; occurs. Thus, if you provide no latches and no error occurs, you ; may ignore the output latches. ; Hard-error-returns-nilp is explained in the comment in hard-error. ; Essentially, two behaviors of (hard-error ...) are possible: return ; nil or signal an error. Both are sound. If hard-error-returns-nilp ; is t then hard-error just returns nil; this is desirable setting if ; you are evaluating a form in a conjecture being proved: its logical ; meaning really is nil. But if you are evaluating a form for other ; reasons, e.g., to compute something, then hard-error should probably ; signal an error, because something is wrong. In that case, ; hard-error-returns-nilp should be set to nil. Nil is the ; conservative setting. (declare (xargs :guard (and (plist-worldp w) (termp form w) (symbol-alistp alist)))) (cond ((zp-big-n big-n) (mv t (cons "Evaluation ran out of time." nil) latches)) ((variablep form) (let ((pair (assoc-eq form alist))) (cond (pair (mv nil (cdr pair) latches)) (t (mv t (msg "Unbound var ~x0." form) latches))))) ((fquotep form) (mv nil (cadr form) latches)) ((translated-acl2-unwind-protectp form) ; We relegate this special case to a separate function, even though it could be ; open-coded, because it is so distracting. (ev-rec-acl2-unwind-protect form alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) ((eq (ffn-symb form) 'wormhole-eval) ; Because this form has been translated, we know it is of the form ; (wormhole-eval 'name '(lambda ...) term) where the quoted lambda is either ; (lambda (whs) body) or (lambda () body), where body has also been translated. ; Furthermore, we know that all the free variables of the lambda are bound in ; the current environment. Logically this term returns nil. Actually, it ; applies the lambda expression to the most recent output of the named wormhole ; and stores the result as the most recent output. #+acl2-loop-only (mv nil nil latches) #-acl2-loop-only (progn (cond (*wormholep* (setq *wormhole-status-alist* (put-assoc-equal (f-get-global 'wormhole-name *the-live-state*) (f-get-global 'wormhole-status *the-live-state*) *wormhole-status-alist*)))) (let* ((*wormholep* t) (name (cadr (fargn form 1))) (formals (lambda-formals (cadr (fargn form 2)))) (whs (car formals)) ; will be nil if formals is nil! (body (lambda-body (cadr (fargn form 2)))) (alist (if formals (cons (cons whs (cdr (assoc-equal name *wormhole-status-alist*))) alist) alist))) (mv-let (body-er body-val latches) (ev-rec body alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok) (cond (body-er (mv t body-val latches)) (t (setq *wormhole-status-alist* (put-assoc-equal name body-val *wormhole-status-alist*)) (mv nil nil latches))))))) ((eq (ffn-symb form) 'if) (mv-let (test-er test latches) (ev-rec (fargn form 1) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok) (cond (test-er (mv t test latches)) (test (ev-rec (fargn form 2) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) (t (ev-rec (fargn form 3) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok))))) ((eq (ffn-symb form) 'mv-list) (ev-rec (fargn form 2) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) ((and (eq (ffn-symb form) 'return-last) (not (and (equal (fargn form 1) ''mbe1-raw) ; We generally avoid running the :exec code for an mbe call. But in safe-mode, ; it is critical to run the exec code and check its equality to the logic code ; (respecting the guard of return-last in the case that the first argument is ; 'mbe1-raw). See the comments in note-4-3 for an example showing why it is ; unsound to avoid this check in safe-mode, and see (defun-*1* return-last ...) ; for a discussion of why we do not consider the case (not gc-off) here. safe-mode))) (let ((fn (and (quotep (fargn form 1)) (unquote (fargn form 1))))) (cond ((and fn (symbolp fn)) ; Translate11 will generally ensure that the value of (return-last-lookup fn w) ; is not nil. What happens if the user (with an active trust tag) removes the ; association of a key in return-last-table with a non-nil value? The ; resulting state will be a weird one, in which a direct evaluation of the ; return-last form in raw Lisp will continue to take effect. So we match that ; behavior here, rather than requiring (return-last-lookup fn w) to be non-nil. ; We leave it to translate11 to enforce this requirement on return-last calls, ; and we leave it to the user not to remove a key from return-last-table before ; passing quotation of that key as the first argument of a return-last call. (cond ((eq fn 'mbe1-raw) ; We avoid running the exec code (see comment above). (ev-rec (fargn form 3) ; optimization: avoid exec argument alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) (t (ev-rec-return-last fn (fargn form 2) (fargn form 3) alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok)))) (t ; first arg is not quotep with special behavior; treat as progn (mv-let (args-er args latches) (ev-rec-lst (fargs form) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok) (cond (args-er (mv t args latches)) (t (mv nil (car (last args)) latches)))))))) (t (mv-let (args-er args latches) (ev-rec-lst (fargs form) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok) (cond (args-er (mv t args latches)) ((flambda-applicationp form) (ev-rec (lambda-body (ffn-symb form)) (pairlis$ (lambda-formals (ffn-symb form)) args) w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok)) (t (ev-fncall-rec (ffn-symb form) args w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok))))))) (defun ev-rec-lst (lst alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) ; WARNING: This function should only be called with *raw-guard-warningp* bound. (declare (xargs :guard (and (plist-worldp w) (term-listp lst w) (symbol-alistp alist)))) (cond ((zp-big-n big-n) (mv t (cons "Evaluation ran out of time." nil) latches)) ((null lst) (mv nil nil latches)) (t (mv-let (first-er first-val first-latches) (ev-rec (car lst) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off latches hard-error-returns-nilp aok) (cond (first-er (mv first-er first-val first-latches)) (t (mv-let (rest-er rest-val rest-latches) (ev-rec-lst (cdr lst) alist w user-stobj-alist (decrement-big-n big-n) safe-mode gc-off first-latches hard-error-returns-nilp aok) (cond (rest-er (mv rest-er rest-val rest-latches)) (t (mv nil (cons first-val rest-val) rest-latches)))))))))) (defun ev-rec-acl2-unwind-protect (form alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) ; WARNING: This function should only be called with *raw-guard-warningp* bound. ; Sketch: We know that form is a termp wrt w and that it is recognized by ; translated-acl2-unwind-protectp. We therefore unpack it into its body and ; two cleanup forms and give it special attention. If the body evaluates ; without either an abort or any kind of "evaluation error" (e.g., ubv, udf, or ; guard error) then we return exactly what we would have returned had we ; evaluated form without special treatment. But if body causes an evaluation ; error we run the cleanup1 code, just as Common Lisp would had the body been ; compiled and caused a hard lisp error. Furthermore, if the evaluation of ; body is aborted, we ensure that the cleanup1 code is EV'd upon unwinding. ; See the unwind-protect essay in axioms.lisp. (declare (xargs :guard (and (plist-worldp w) (termp form w) (symbol-alistp alist)))) (let ((temp nil)) #+acl2-loop-only (declare (ignore temp)) (mv-let (ans body cleanup1 cleanup2) (translated-acl2-unwind-protectp4 form) (declare (ignore ans)) #-acl2-loop-only (cond ((live-state-p (cdr (assoc-eq 'STATE alist))) ; This code implements our unwind-protection from aborts. Intuitively, we wish ; to push the cleanup form onto the unwind-protect stack provided the STATE ; being modified is the live state. It is possible that STATE is not bound in ; alist. If this happens then it is certainly not the live state and we do not ; push anything. ; The next problem, however, is what do we push? In normal circumstances -- ; i.e., body terminating without an evaluation error but signalling an error -- ; cleanup1 is evaluated by ev. But cleanup1 is evaluated in w, which may or ; may not be the installed world. Hence, the meaning in w of the function ; symbol in the car of cleanup1 may be different from the raw lisp definition ; (if any) of that symbol. So we can't do the usual and just push the car of ; cleanup1 and the values (in alist) of the arguments. Furthermore, there is ; delicacy concerning the possibility that not all of the argument variables ; are bound in alist. To make matters slightly worse, we can't cause any ; errors right now, no matter how screwed up cleanup1 might be, because no ; abort has happened and we are obliged to respect the semantics unless an ; abort happens. To make a long story short, we do what is pretty obvious: we ; push onto the undo stack a form that calls EV to do the cleanup! We use ; FUNCTION to capture the local environment, e.g., alist, which contains the ; values of all the variables occurring in the cleanup form. (setq temp (cons "ev-rec-acl2-unwind-protect" #'(lambda nil ; The Unwind-Protect Essay says that we have the freedom to give arbitrary ; semantics to acl2-unwind-protect in the face of an abort. So in this raw ; Lisp code, we take the liberty of binding *ev-shortcut-okp* to t even though ; when this cleanup code is executed, we may violate the requirement that the ; values of state globals guard-checking-on and safe-mode are respected in the ; arguments to ev-rec when *ev-shortcut-okp* is t. This seems like quite a ; minor violation when doing cleanup. (let ((*ev-shortcut-okp* t)) (mv-let (erp val latches) (ev-rec cleanup1 alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) (declare (ignore latches)) ; Since 'STATE in alist is the live state, latches must be too. (cond (erp (let ((state *the-live-state*)) (er soft 'acl2-unwind-protect "~@0" val)))))) *the-live-state*))) (push-car temp *acl2-unwind-protect-stack* 'ev-rec-acl2-unwind-protect))) (mv-let (body-erp body-val body-latches) (ev-rec body alist w user-stobj-alist big-n safe-mode gc-off latches hard-error-returns-nilp aok) (cond (body-erp ; "hard error", e.g., guard error in body ; It is possible that the evaluation of body pushed some additional ; cleanup forms before the abort occurred. We must get back down to ; the form we pushed. This is analogous to the similar situation in ; acl2-unwind-protect itself. #-acl2-loop-only (cond (temp (acl2-unwind -1 temp))) (mv-let (clean-erp clean-val clean-latches) (ev-rec cleanup1 (put-assoc-eq 'state (cdr (assoc-eq 'state body-latches)) alist) w user-stobj-alist big-n safe-mode gc-off body-latches hard-error-returns-nilp aok) #-acl2-loop-only (cond (temp (pop (car *acl2-unwind-protect-stack*)))) (cond (clean-erp ; "hard error," e.g., guard error in cleanup! (mv t (msg "An evaluation error, ``~@0'', occurred while ~ evaluating the body of an acl2-unwind-protect ~ form. While evaluating the first cleanup form a ~ second evaluation error occurred, ``~@1''. The ~ body of the acl2-unwind-protect is ~p2 and the ~ first cleanup form is ~p3. Because the cleanup ~ form failed, the state being returned may not be ~ fully cleaned up." body-val clean-val (untranslate* body nil w) (untranslate* cleanup1 nil w)) clean-latches)) (t ; In this case, clean-val is the binding of 'state in ; clean-latches because the cleanup form produces a state. (mv body-erp body-val clean-latches))))) ((car body-val) ; "soft error," i.e., body signalled error ; We think this call of acl2-unwind is unnecessary. It is here in ; case the evaluation of body pushed some additional forms onto the ; unwind protect stack and it removes those forms down to the one we ; pushed. But if a soft error has arisen, any forms pushed would have ; been popped on the way back to here. But this code is safer. #-acl2-loop-only (cond (temp (acl2-unwind -1 temp))) ; Because body is known to produce an error triple we know its car is ; the error flag, the cadr is the value, and the caddr is a state ; The test above therefore detects that the body signalled an error. (mv-let (clean-erp clean-val clean-latches) (ev-rec cleanup1 (put-assoc-eq 'state (cdr (assoc-eq 'state body-latches)) alist) w user-stobj-alist big-n safe-mode gc-off body-latches hard-error-returns-nilp aok) #-acl2-loop-only (cond (temp (pop (car *acl2-unwind-protect-stack*)))) (cond (clean-erp ; "hard error," e.g., guard error in cleanup! (mv t (msg "An evaluation error, ``~@0'', occurred while ~ evaluating the first cleanup form of an ~ acl2-unwind-protect. The body of the ~ acl2-unwind-protect is ~p1 and the first cleanup ~ form is ~p2. Because the cleanup form failed, ~ the state being returned may not be fully cleaned ~ up." clean-val (untranslate* body nil w) (untranslate* cleanup1 nil w)) clean-latches)) (t ; We pass a SOFT error up, containing the cleaned up state. (mv nil (list (car body-val) (cadr body-val) (cdr (assoc-eq 'state clean-latches))) clean-latches))))) (t ; no hard or soft error ; Same safety check described above. #-acl2-loop-only (cond (temp (acl2-unwind -1 temp))) (mv-let (clean-erp clean-val clean-latches) (ev-rec cleanup2 (put-assoc-eq 'state (cdr (assoc-eq 'state body-latches)) alist) w user-stobj-alist big-n safe-mode gc-off body-latches hard-error-returns-nilp aok) #-acl2-loop-only (cond (temp (pop (car *acl2-unwind-protect-stack*)))) (cond (clean-erp ; "hard error," e.g., guard error in cleanup! (mv t (msg "An evaluation error, ``~@0'', occurred while ~ evaluating the second cleanup form of an ~ acl2-unwind-protect. The body of the ~ acl2-unwind-protect is ~p1 and the second cleanup ~ form is ~p2. Because the cleanup form failed, ~ the state being returned may not be fully cleaned ~ up." clean-val (untranslate* body nil w) (untranslate* cleanup2 nil w)) clean-latches)) (t (mv nil (list (car body-val) (cadr body-val) (cdr (assoc-eq 'state clean-latches))) clean-latches)))))))))) (defun ev-fncall-w (fn args w user-stobj-alist safe-mode gc-off hard-error-returns-nilp aok) (declare (xargs :guard (plist-worldp w))) ; WARNING: Do not call this function if args contains the live state ; or any other live stobjs and evaluation of form could modify any of ; those stobjs. Otherwise, the calls of ev-fncall-rec below violate ; requirement (1) in The Essay on EV, which is stated explicitly for ; ev but, in support of ev, is applicable to ev-fncall-rec as well. ; Note that users cannot make such a call because they cannot put live ; stobjs into args. ; It may see inappropriate that we temporarily modify state in a ; function that does not take state. But what we are really doing is ; writing a function that has nothing to do with state, yet handles ; guards in a way appropriate to the current world. We need to modify ; the state to match the inputs safe-mode and gc-off. ; Keep the two ev-fncall-rec calls below in sync. (cond ((untouchable-fn-p fn w) (mv t (msg "Attempted to call ev-fncall-w with function ~x0, which is untouchable." fn))) ; See the comment in ev for why we don't check the time limit here. (t #-acl2-loop-only (let ((*ev-shortcut-okp* t) (*raw-guard-warningp* (raw-guard-warningp-binding))) (state-free-global-let* ((safe-mode safe-mode) (guard-checking-on ; Guard-checking-on will be t or nil -- not :nowarn, :all, or :none, but it ; doesn't seem that this would be a problem. (not gc-off))) (mv-let (erp val latches) (ev-fncall-rec fn args w user-stobj-alist (big-n) safe-mode gc-off nil ; latches hard-error-returns-nilp aok) (progn (when latches (er hard 'ev-fncall-w "The call ~x0 returned non-nil latches." (list 'ev-fncall-w fn args ' (if user-stobj-alist ' nil) safe-mode gc-off hard-error-returns-nilp aok))) (mv erp val))))) #+acl2-loop-only (mv-let (erp val latches) (ev-fncall-rec fn args w user-stobj-alist (big-n) safe-mode gc-off nil ; latches hard-error-returns-nilp aok) (declare (ignore latches)) (mv erp val))))) (defun ev-fncall-guard-er-msg (fn guard stobjs-in args w user-stobj-alist extra) ; Guard is printed directly, so should generally be in untranslated form. ; Note that user-stobj-alist is only used for error messages, so this function ; may be called in the presence of local stobjs. (prog2$ (save-ev-fncall-guard-er fn guard stobjs-in args) (msg "The guard for the~#0~[ :program~/~] function call ~x1, which is ~P23, is ~ violated by the arguments in the call ~P45.~@6~@7~@8" (if (programp fn w) 0 1) (cons fn (formals fn w)) guard nil ; might prefer (term-evisc-tuple nil state) if we had state here (cons fn (untranslate*-lst (apply-user-stobj-alist-or-kwote user-stobj-alist args nil) nil w)) (evisc-tuple 3 4 nil nil) (cond ((and (eq fn 'return-last) (eq (car args) 'mbe1-raw)) (msg " This offending call is equivalent to the more common form, ~ ~x0." `(mbe :logic ,(untranslate* (kwote (caddr args)) nil w) :exec ,(untranslate* (kwote (cadr args)) nil w)))) (t "")) (cond ((eq extra :live-stobj) ; This case occurs if we attempt to execute the call of a "oneified" function ; on a live stobj (including state) when the guard of the fn is not satisfied, ; where the function is either a primitive listed in *super-defun-wart-table* ; or is defined by defstobj or defabsstobj. ; Warning: Before removing this error, consider that in general guard-checking ; may be defeated by :set-guard-checking :none, so we may be relying on this ; error for built-in functions like aset-t-stack that rely on guard-checking to ; validate their arguments. (msg "~|This error is being reported even though guard-checking ~ has been turned off, because the stobj argument of ~x0 is ~ the ``live'' ~p1 and ACL2 does not support non-compliant ~ live stobj manipulation." fn (find-first-non-nil stobjs-in))) ((eq extra :no-extra) "") (extra *safe-mode-guard-er-addendum*) (t "~|See :DOC set-guard-checking for information about suppressing ~ this check with (set-guard-checking :none), as recommended for ~ new users.")) (error-trace-suggestion t)))) (defun ev-fncall-guard-er (fn args w user-stobj-alist latches extra) ; Note that user-stobj-alist is only used for error messages, so this function ; may be called in the presence of local stobjs. (mv t (ev-fncall-guard-er-msg fn (untranslate* (guard fn nil w) t w) (stobjs-in fn w) args w user-stobj-alist extra) latches)) (defun ev-fncall-msg (val wrld user-stobj-alist) ; Warning: Keep this in sync with ev-fncall-rec-logical. ; Note that user-stobj-alist is only used for error messages, so this function ; may be called in the presence of local stobjs. (cond ((and (consp val) (eq (car val) 'ev-fncall-null-body-er)) (ev-fncall-null-body-er-msg (cadr val) (caddr val) (cdddr val))) ((and (consp val) (eq (car val) 'ev-fncall-guard-er)) ; We get here if val is of the form (ev-fncall-guard-er fn args guard ; stobjs-in safep). This happens if a :program function finds its ; guard violated or a :logic function finds its guard violated while ; guard-checking is on. (ev-fncall-guard-er-msg (cadr val) (cadddr val) (car (cddddr val)) (caddr val) wrld user-stobj-alist (cadr (cddddr val)))) ((and (consp val) (eq (car val) 'ev-fncall-creator-er)) ; This is similar to the preceding case, except that there are no stobjs-in. (ev-fncall-creator-er-msg (cadr val))) ((and (consp val) (member-eq (car val) '(pkg-witness-er pkg-imports-er))) (unknown-pkg-error-msg (car val) (cadr val))) ; At one time we had the following case: ; ((and (consp val) ; (eq (car val) 'program-only-er)) ; In this case we (essentially) returned (program-only-er-msg (cadr val) (caddr ; val) (cadr (cddddr val))). But we get here by catching a throw of val, which ; no longer is of the form (program-only-er ...); see the comment about the ; call of oneify-fail-form on 'program-only-er (and other arguments) in ; oneify-cltl-code. ((eq val 'illegal) (illegal-msg)) (t (er hard 'raw-ev-fncall "An unrecognized value, ~x0, was thrown to 'raw-ev-fncall.~@1" val (error-trace-suggestion t))))) (defun untranslate1 (term iff-flg untrans-tbl preprocess-fn wrld) ; Warning: It would be best to keep this in sync with ; obviously-iff-equiv-terms, specifically, giving similar attention in both to ; functions like implies, iff, and not, which depend only on the propositional ; equivalence class of each argument. ; We return a Lisp form that translates to term if iff-flg is nil and ; that translates to a term iff-equivalent to term if iff-flg is t. ; Wrld is an ACL2 logical world, which may be used to improve the ; appearance of the result, in particular to allow (nth k st) to be ; printed as (nth *field-name* st) if st is a stobj name and ; field-name is the kth field name of st; similarly for update-nth. ; It is perfectly appropriate for wrld to be nil if such extra ; information is not important. ; Note: The only reason we need the iff-flg is to let us translate (if ; x1 t x2) into (or x1 x2) when we are in an iff situation. We could ; ask type-set to check that x1 is Boolean, but that would require ; passing wrld into untranslate. That, in turn, would require passing ; wrld into such syntactic places as prettyify-clause and any other ; function that might want to print a term. ; Warning: This function may not terminate. We should consider making it ; primitive recursive by adding a natural number ("count") parameter. (let ((term (if preprocess-fn (mv-let (erp term1) (ev-fncall-w preprocess-fn (list term wrld) wrld nil ; user-stobj-alist nil ; safe-mode nil ; gc-off nil ; hard-error-returns-nilp t) (or (and (null erp) term1) term)) term))) (cond ((variablep term) term) ((fquotep term) (cond ((or (acl2-numberp (cadr term)) (stringp (cadr term)) (characterp (cadr term)) (eq (cadr term) nil) (eq (cadr term) t) (keywordp (cadr term))) (cadr term)) (t term))) ((flambda-applicationp term) (make-let-or-let* (collect-non-trivial-bindings (lambda-formals (ffn-symb term)) (untranslate1-lst (fargs term) nil untrans-tbl preprocess-fn wrld)) (untranslate1 (lambda-body (ffn-symb term)) iff-flg untrans-tbl preprocess-fn wrld))) ((and (eq (ffn-symb term) 'nth) (quotep (fargn term 1)) (integerp (cadr (fargn term 1))) (<= 0 (cadr (fargn term 1)))) (let ((accessor-name (accessor-root (cadr (fargn term 1)) (fargn term 2) wrld))) (list 'nth (or accessor-name (cadr (fargn term 1))) (untranslate1 (fargn term 2) nil untrans-tbl preprocess-fn wrld)))) ((and (eq (ffn-symb term) 'update-nth) (quotep (fargn term 1)) (integerp (cadr (fargn term 1))) (<= 0 (cadr (fargn term 1)))) (let ((accessor-name (accessor-root (cadr (fargn term 1)) (fargn term 3) wrld))) (list 'update-nth (or accessor-name (cadr (fargn term 1))) (untranslate1 (fargn term 2) nil untrans-tbl preprocess-fn wrld) (untranslate1 (fargn term 3) nil untrans-tbl preprocess-fn wrld)))) ((and (eq (ffn-symb term) 'update-nth-array) (quotep (fargn term 1)) (integerp (cadr (fargn term 1))) (<= 0 (cadr (fargn term 1)))) (let ((accessor-name (accessor-root (cadr (fargn term 1)) (fargn term 4) wrld))) (list 'update-nth-array (or accessor-name (cadr (fargn term 1))) (untranslate1 (fargn term 2) nil untrans-tbl preprocess-fn wrld) (untranslate1 (fargn term 3) nil untrans-tbl preprocess-fn wrld) (untranslate1 (fargn term 4) nil untrans-tbl preprocess-fn wrld)))) ((eq (ffn-symb term) 'binary-+) (cons '+ (untranslate1-lst (right-associated-args 'binary-+ term) nil untrans-tbl preprocess-fn wrld))) ((eq (ffn-symb term) 'unary-/) (list '/ (untranslate1 (fargn term 1) nil untrans-tbl preprocess-fn wrld))) ((eq (ffn-symb term) 'unary--) (list '- (untranslate1 (fargn term 1) nil untrans-tbl preprocess-fn wrld))) ((eq (ffn-symb term) 'if) (case-match term (('if x1 *nil* *t*) (list 'not (untranslate1 x1 t untrans-tbl preprocess-fn wrld))) (('if x1 x2 *nil*) (untranslate-and (untranslate1 x1 t untrans-tbl preprocess-fn wrld) (untranslate1 x2 iff-flg untrans-tbl preprocess-fn wrld) iff-flg)) (('if x1 *nil* x2) (untranslate-and (list 'not (untranslate1 x1 t untrans-tbl preprocess-fn wrld)) (untranslate1 x2 iff-flg untrans-tbl preprocess-fn wrld) iff-flg)) (('if x1 x1 x2) (untranslate-or (untranslate1 x1 iff-flg untrans-tbl preprocess-fn wrld) (untranslate1 x2 iff-flg untrans-tbl preprocess-fn wrld))) (('if x1 x2 *t*) ; Observe that (if x1 x2 t) = (if x1 x2 (not nil)) = (if x1 x2 (not x1)) = ; (if (not x1) (not x1) x2) = (or (not x1) x2). (untranslate-or (list 'not (untranslate1 x1 t untrans-tbl preprocess-fn wrld)) (untranslate1 x2 iff-flg untrans-tbl preprocess-fn wrld))) (('if x1 *t* x2) (cond ((or iff-flg (and (nvariablep x1) (not (fquotep x1)) (member-eq (ffn-symb x1) *untranslate-boolean-primitives*))) (untranslate-or (untranslate1 x1 t untrans-tbl preprocess-fn wrld) (untranslate1 x2 iff-flg untrans-tbl preprocess-fn wrld))) (t (untranslate-if term iff-flg untrans-tbl preprocess-fn wrld)))) (& (untranslate-if term iff-flg untrans-tbl preprocess-fn wrld)))) ((and (eq (ffn-symb term) 'not) (nvariablep (fargn term 1)) (not (fquotep (fargn term 1))) (member-eq (ffn-symb (fargn term 1)) '(< o<))) (list (if (eq (ffn-symb (fargn term 1)) '<) '<= 'o<=) (untranslate1 (fargn (fargn term 1) 2) nil untrans-tbl preprocess-fn wrld) (untranslate1 (fargn (fargn term 1) 1) nil untrans-tbl preprocess-fn wrld))) ((eq (ffn-symb term) 'not) (dumb-negate-lit (untranslate1 (fargn term 1) t untrans-tbl preprocess-fn wrld))) ((member-eq (ffn-symb term) '(implies iff)) (fcons-term* (ffn-symb term) (untranslate1 (fargn term 1) t untrans-tbl preprocess-fn wrld) (untranslate1 (fargn term 2) t untrans-tbl preprocess-fn wrld))) ((eq (ffn-symb term) 'cons) (untranslate-cons term untrans-tbl preprocess-fn wrld)) ((and (eq (ffn-symb term) 'synp) ; Even though translate insists that the second argument of synp is quoted, can ; we really guarantee that every termp given to untranslate came through ; translate? Not necessarily; for example, maybe substitution was performed ; for some reason (say, in the proof-checker one replaces the quoted argument ; by a variable known to be equal to it). (quotep (fargn term 2))) ; We store the quotation of the original form of a syntaxp or bind-free ; hypothesis in the second arg of its expansion. We do this so that we ; can use it here and output something that the user will recognise. (cadr (fargn term 2))) (t (cond ((and (eq (ffn-symb term) 'return-last) (quotep (fargn term 1)) (let* ((key (unquote (fargn term 1))) (fn (and (symbolp key) key (let ((tmp (return-last-lookup key wrld))) (if (consp tmp) (car tmp) tmp))))) (and fn (cons fn (untranslate1-lst (cdr (fargs term)) nil untrans-tbl preprocess-fn wrld)))))) (t (let* ((pair (cdr (assoc-eq (ffn-symb term) untrans-tbl))) (op (car pair)) (flg (cdr pair))) (cond (op (cons op (untranslate1-lst (cond ((and flg (cdr (fargs term)) (null (cddr (fargs term)))) (right-associated-args (ffn-symb term) term)) (t (fargs term))) nil untrans-tbl preprocess-fn wrld))) (t (mv-let (fn guts) (car-cdr-nest term) (cond (fn (list fn (untranslate1 guts nil untrans-tbl preprocess-fn wrld))) (t (cons (ffn-symb term) (untranslate1-lst (fargs term) nil untrans-tbl preprocess-fn wrld)))))))))))))) (defun untranslate-cons1 (term untrans-tbl preprocess-fn wrld) ; This function digs through a 'cons nest, untranslating each of the ; elements and the final non-cons cdr. It returns two results: the ; list of untranslated elements and the untranslated final term. (cond ((variablep term) (mv nil (untranslate1 term nil untrans-tbl preprocess-fn wrld))) ((fquotep term) (mv nil (untranslate1 term nil untrans-tbl preprocess-fn wrld))) ((eq (ffn-symb term) 'cons) (mv-let (elements x) (untranslate-cons1 (fargn term 2) untrans-tbl preprocess-fn wrld) (mv (cons (untranslate1 (fargn term 1) nil untrans-tbl preprocess-fn wrld) elements) x))) (t (mv nil (untranslate1 term nil untrans-tbl preprocess-fn wrld))))) (defun untranslate-cons (term untrans-tbl preprocess-fn wrld) ; Term is a non-quote term whose ffn-symb is 'cons. We untranslate ; it into a CONS, a LIST, or a LIST*. (mv-let (elements x) (untranslate-cons1 term untrans-tbl preprocess-fn wrld) (cond ((eq x nil) (cons 'list elements)) ((null (cdr elements)) (list 'cons (car elements) x)) (t (cons 'list* (append elements (list x))))))) (defun untranslate-if (term iff-flg untrans-tbl preprocess-fn wrld) (cond ((> (case-length nil term) 2) (case-match term (('if (& key &) & &) (list* 'case key (untranslate-into-case-clauses key term iff-flg untrans-tbl preprocess-fn wrld))))) ((> (cond-length term) 2) (cons 'cond (untranslate-into-cond-clauses term iff-flg untrans-tbl preprocess-fn wrld))) (t (list 'if (untranslate1 (fargn term 1) t untrans-tbl preprocess-fn wrld) (untranslate1 (fargn term 2) iff-flg untrans-tbl preprocess-fn wrld) (untranslate1 (fargn term 3) iff-flg untrans-tbl preprocess-fn wrld))))) (defun untranslate-into-case-clauses (key term iff-flg untrans-tbl preprocess-fn wrld) ; We generate the clauses of a (case key ...) stmt equivalent to term. ; We only call this function when the case-length of term is greater ; than 1. If we called it when case-length were 1, it would not ; terminate. (case-match term (('if (pred !key ('quote val)) x y) (cond ((and (or (eq pred 'equal) (eq pred 'eql)) (eqlablep val)) (cond ((or (eq val t) (eq val nil) (eq val 'otherwise)) (cons (list (list val) (untranslate1 x iff-flg untrans-tbl preprocess-fn wrld)) (untranslate-into-case-clauses key y iff-flg untrans-tbl preprocess-fn wrld) )) (t (cons (list val (untranslate1 x iff-flg untrans-tbl preprocess-fn wrld)) (untranslate-into-case-clauses key y iff-flg untrans-tbl preprocess-fn wrld))))) ((and (eq pred 'member) (eqlable-listp val)) (cons (list val (untranslate1 x iff-flg untrans-tbl preprocess-fn wrld)) (untranslate-into-case-clauses key y iff-flg untrans-tbl preprocess-fn wrld))) (t (list (list 'otherwise (untranslate1 term iff-flg untrans-tbl preprocess-fn wrld)))))) (& (list (list 'otherwise (untranslate1 term iff-flg untrans-tbl preprocess-fn wrld)))))) (defun untranslate-into-cond-clauses (term iff-flg untrans-tbl preprocess-fn wrld) ; We know cond-length is greater than 1; else this doesn't terminate. (case-match term (('if x1 x2 x3) (cons (list (untranslate1 x1 t untrans-tbl preprocess-fn wrld) (untranslate1 x2 iff-flg untrans-tbl preprocess-fn wrld)) (untranslate-into-cond-clauses x3 iff-flg untrans-tbl preprocess-fn wrld))) (& (list (list t (untranslate1 term iff-flg untrans-tbl preprocess-fn wrld)))))) (defun untranslate1-lst (lst iff-flg untrans-tbl preprocess-fn wrld) (cond ((null lst) nil) (t (cons (untranslate1 (car lst) iff-flg untrans-tbl preprocess-fn wrld) (untranslate1-lst (cdr lst) iff-flg untrans-tbl preprocess-fn wrld))))) ;; RAG - I relaxed the guards for < and complex to use realp instead ;; of rationalp. I also added complexp, realp, and floor1. ) (defun ev-fncall (fn args state latches hard-error-returns-nilp aok) (declare (xargs :guard (state-p state))) (let #-acl2-loop-only ((*ev-shortcut-okp* (live-state-p state)) (*raw-guard-warningp* (raw-guard-warningp-binding))) #+acl2-loop-only () ; See the comment in ev for why we don't check the time limit here. (ev-fncall-rec fn args (w state) (user-stobj-alist state) (big-n) (f-get-global 'safe-mode state) (gc-off state) latches hard-error-returns-nilp aok))) (defun ev (form alist state latches hard-error-returns-nilp aok) (declare (xargs :guard (and (state-p state) (termp form (w state)) (symbol-alistp alist)))) (let #-acl2-loop-only ((*ev-shortcut-okp* (live-state-p state)) (*raw-guard-warningp* (raw-guard-warningp-binding))) #+acl2-loop-only () ; At one time we called time-limit5-reached-p here so that we can quit if we ; are out of time. But we were then able to get into an infinite loop as ; follows: ; (defun foo (x) (cons x x)) ; :brr t ; :monitor (:definition foo) t ; (ld '((thm (equal (foo x) (cons x x))))) ; [Hit control-c repeatedly.] ; We didn't analyze this issue completely (presumably has something to do with ; cleaning up), but a simple solution is to avoid this time-limit check. ; (cond ; ((time-limit5-reached-p ; "Out of time in the evaluator (ev).") ; nil, or throws ; (mv t ; value shouldn't matter ; (cons "Implementation error" nil) ; latches)) ; (t (ev-rec form alist (w state) (user-stobj-alist state) (big-n) (f-get-global 'safe-mode state) (gc-off state) latches hard-error-returns-nilp aok))) (defun ev-lst (lst alist state latches hard-error-returns-nilp aok) (declare (xargs :guard (and (state-p state) (term-listp lst (w state)) (symbol-alistp alist)))) (let #-acl2-loop-only ((*ev-shortcut-okp* (live-state-p state)) (*raw-guard-warningp* (raw-guard-warningp-binding))) #+acl2-loop-only () ; See the comment in ev for why we don't check the time limit here. (ev-rec-lst lst alist (w state) (user-stobj-alist state) (big-n) (f-get-global 'safe-mode state) (gc-off state) latches hard-error-returns-nilp aok))) (defun untranslate (term iff-flg wrld) (let ((user-untranslate (cdr (assoc-eq 'untranslate (table-alist 'user-defined-functions-table wrld))))) (if user-untranslate (mv-let (erp val) (ev-fncall-w user-untranslate (list term iff-flg wrld) wrld nil ; user-stobj-alist nil ; safe-mode nil ; gc-off nil ; hard-error-returns-nilp t) (cond (erp #-acl2-loop-only (progn (error-fms t user-untranslate (car val) (cdr val) *the-live-state*) (er hard 'untranslate "Please fix ~x0 (see message above and see :doc ~ user-defined-functions-table)." user-untranslate)) (untranslate* term iff-flg wrld)) (t val))) (untranslate* term iff-flg wrld)))) (defun untranslate-lst (lst iff-flg wrld) (let ((user-untranslate-lst (cdr (assoc-eq 'untranslate-lst (table-alist 'user-defined-functions-table wrld))))) (if user-untranslate-lst (mv-let (erp val) (ev-fncall-w user-untranslate-lst (list lst iff-flg wrld) wrld nil ; user-stobj-alist nil ; safe-mode nil ; gc-off nil ; hard-error-returns-nilp t) (cond (erp #-acl2-loop-only (progn (error-fms t user-untranslate-lst (car val) (cdr val) *the-live-state*) (er hard 'untranslate-lst "Please fix ~x0 (see message above and see :doc ~ user-defined-functions-table)." user-untranslate-lst #+acl2-loop-only nil)) (untranslate1-lst lst iff-flg (untrans-table wrld) (untranslate-preprocess-fn wrld) wrld)) (t val))) (untranslate1-lst lst iff-flg (untrans-table wrld) (untranslate-preprocess-fn wrld) wrld)))) (defun ev-w (form alist w user-stobj-alist safe-mode gc-off hard-error-returns-nilp aok) ; WARNING: Do not call this function if alist contains the live state or any ; other live stobjs and evaluation of form could modify any of those stobjs. ; Otherwise, the calls of ev-rec below violate requirement (1) in The Essay on ; EV, which is stated explicitly for ev but, in support of ev, is applicable to ; ev-rec as well. Note that users cannot make such a call because they cannot ; put live stobjs into alist. ; Unlike ev-fncall-w, we do not check for untouchables (by traversing the form ; before evaluating) and hence we must make ev-w untouchable. It would be ; simple enough to create a user-available version of ev-w, if requested, which ; would need to do an untouchables check. ; Note that user-stobj-alist is only used for error messages, so this function ; may be called in the presence of local stobjs. Probably user-stobj-alist ; could be replaced as nil because of the stobj restriction on alist. (declare (xargs :guard (and (plist-worldp w) (termp form w) (symbol-alistp alist)))) ; See the comment in ev for why we don't check the time limit here. #-acl2-loop-only (let ((*ev-shortcut-okp* t) (*raw-guard-warningp* (raw-guard-warningp-binding))) (state-free-global-let* ((safe-mode safe-mode) (guard-checking-on ; Guard-checking-on will be t or nil -- not :nowarn, :all, or :none -- but it ; doesn't seem that this would be a problem, provided the call is made with ; gc-off set to t if guard-checking-on is either nil or :none (don't forget ; :none!). (not gc-off))) (mv-let (erp val latches) (ev-rec form alist w user-stobj-alist (big-n) safe-mode gc-off nil ; latches hard-error-returns-nilp aok) (progn (when latches (er hard! 'ev-w "The call ~x0 returned non-nil latches." (list 'ev-w form alist ' (if user-stobj-alist ' nil) safe-mode gc-off hard-error-returns-nilp aok))) (mv erp val))))) #+acl2-loop-only (mv-let (erp val latches) (ev-rec form alist w user-stobj-alist (big-n) safe-mode gc-off nil ; latches hard-error-returns-nilp aok) (declare (ignore latches)) (mv erp val))) (defun ev-w-lst (lst alist w user-stobj-alist safe-mode gc-off hard-error-returns-nilp aok) ; WARNING: See the warning in ev-w, which explains that live stobjs must not ; occur in alist. ; Note that user-stobj-alist is only used for error messages, so this function ; may be called in the presence of local stobjs. Probably user-stobj-alist ; could be replaced as nil because of the stobj restriction on alist. ; See the comment in ev-w about untouchables. (declare (xargs :guard (and (plist-worldp w) (term-listp lst w) (symbol-alistp alist)))) ; See the comment in ev for why we don't check the time limit here. #-acl2-loop-only (let ((*ev-shortcut-okp* t) (*raw-guard-warningp* (raw-guard-warningp-binding))) (state-free-global-let* ((safe-mode safe-mode) (guard-checking-on ; Guard-checking-on will be t or nil -- not :nowarn, :all, or :none -- but it ; doesn't seem that this would be a problem, provided the call is made with ; gc-off set to t if guard-checking-on is either nil or :none (don't forget ; :none!). (not gc-off))) (mv-let (erp val latches) (ev-rec-lst lst alist w user-stobj-alist (big-n) safe-mode gc-off nil ; latches hard-error-returns-nilp aok) (progn (when latches (er hard 'ev-w-lst "The call ~x0 returned non-nil latches." (list 'ev-w-lst lst alist ' (if user-stobj-alist ' nil) safe-mode gc-off hard-error-returns-nilp aok))) (mv erp val))))) #+acl2-loop-only (mv-let (erp val latches) (ev-rec-lst lst alist w user-stobj-alist (big-n) safe-mode gc-off nil ; latches hard-error-returns-nilp aok) (declare (ignore latches)) (mv erp val))) ; Essay on Other Worlds ; In Version 1.7 and earlier, ev and its supporters were coded so that ; they took both a world and a state as input. The world supplied the ; definitions of the functions. The state was used for nothing but a ; termination argument -- but we did slip into raw Lisp when that was ; thought appropriate. The code was was (supposed to be) sound when ; evaluated on states other than the live state. This was imagined to ; be possible if ground calls of ev-fncall arose in terms being ; proved. The raw lisp counterpart of ev verified that the world in ; the given state is properly related to the world in the live state. ; The following pre-Version 1.8 comment addresses concerns related to ; the evaluation of a fn in a world other than the one installed in ; state. These comments are now outdated, but are left here because ; we gave the issue some careful thought at the time. ; We wish to jump into Common Lisp to compute the value of fn on ; args. We know that fn is a function symbol in w because the guard ; for ev requires that we only evaluate terms. But the Common Lisp ; state reflects the definitions of the currently installed world, ; inst-w, while we have to compute fn by the definitions in world w. ; In addition, we can use the Common Lisp code only if the guards ; have been verified. So we need to know two things: (a) that the ; two worlds w and inst-w are in an appropriate relationship, and ; (b) that the guards for fn are all satisfied. ; We address (a) first. It is clear that inst-w can be used to ; compute fn in w if every function ancestral to fn in w is defined ; exactly the same way in inst-w. When this condition holds, we say ; "inst-w is sufficient to compute fn in w." This sufficiency ; condition is too expensive to check explicitly. Note, however, ; that if inst-w is an extension of w, then inst-w is sufficient. ; Note also that if w is an extension of inst-w and fn is defined in ; inst-w, then inst-w is sufficient. Now if w is an extension of ; inst-w and fn is defined in w then it is defined in inst-w iff it ; is fboundp. Proof: Suppose fn is not defined in inst-w but is ; fboundp. Then fn is some function like RPLACA or LP. But in that ; case, fn couldn't be defined in w because to define it would ; require that we smash its symbol-function. Q.E.D. So in fact, we ; check that one of the two worlds is an extension of the other and ; that fn is fboundp. ; Now for (b). We wish to check that the guards for fn are all ; valid. Of course, all we can do efficiently is see whether the ; 'guards-checked property has been set. But it doesn't matter ; which world we check that in because if the guards have been ; checked in either then they are valid in both. So we just see if ; they have been checked in whichever of the two worlds is the ; extension. ; Essay on Context-message Pairs ; Recall that translate returns state, which might be modified. It can be ; useful to have a version of translate that does not return state, for example ; in development of a parallel version of the waterfall (Ph.D. research by ; David Rager ongoing in 2010). Starting after Version_4.1, we provide a ; version of translate that does not return state. More generally, we support ; an analogy of the "error triples" programming idiom: rather than passing ; around triples (mv erp val state), we pass around pairs (mv ctx msg), as ; described below. If foo is a function that returns an error triple, we may ; introduce foo-cmp as the analogous function that returns a message pair. We ; try to avoid code duplication, for example by using the wrapper ; cmp-to-error-triple. ; An error is indicated when the context (first) component of a context-message ; pair is non-nil. There are two possibilities in this case. The second ; component can be nil, indicating that the error does not cause a message to ; be printed. Otherwise, the first component is a context suitable for er and ; such, while the second component is a message (fmt-string . fmt-args), ; suitable as a ~@ fmt argument. (defun silent-error (state) (mv t nil state)) (defmacro cmp-to-error-triple (form) ; Here we convert a context-message pair (see the Essay on Context-message ; Pairs) to an error triple, printing an error message if one is called for. ; Keep in sync with cmp-to-error-triple@par. `(mv-let (ctx msg-or-val) ,form (cond (ctx (cond (msg-or-val (assert$ (not (eq ctx t)) (er soft ctx "~@0" msg-or-val))) (t (silent-error state)))) (t (value msg-or-val))))) #+acl2-par (defmacro cmp-to-error-triple@par (form) ; Here we convert a context-message pair (see the Essay on Context-message ; Pairs) to the #+acl2-par version of an error triple, printing an error ; message if one is called for. ; Keep in sync with cmp-to-error-triple. `(mv-let (ctx msg-or-val) ,form (cond (ctx (cond (msg-or-val (assert$ (not (eq ctx t)) (er@par soft ctx "~@0" msg-or-val))) (t (mv@par t nil state)))) (t (value@par msg-or-val))))) (defmacro cmp-to-error-double (form) ; This is a variant of cmp-to-error-triple that returns (mv erp val) rather ; than (mv erp val state). `(mv-let (ctx msg-or-val) ,form (cond (ctx (prog2$ (cond (msg-or-val (assert$ (not (eq ctx t)) (error-fms-cw nil ctx "~@0" (list (cons #\0 msg-or-val))))) (t nil)) (mv t nil))) (t (mv nil msg-or-val))))) (defmacro cmp-and-value-to-error-quadruple (form) ; We convert a context-message pair and an extra-value (see the Essay on ; Context-message Pairs) to an error quadruple (mv t value extra-value state), ; printing an error message if one is called for. ; Keep in sync with cmp-and-value-to-error-quadruple@par. `(mv-let (ctx msg-or-val extra-value) ,form (cond (ctx (cond (msg-or-val (assert$ (not (eq ctx t)) (mv-let (erp val state) (er soft ctx "~@0" msg-or-val) (declare (ignore erp val)) (mv t nil extra-value state)))) (t (mv t nil extra-value state)))) (t (mv nil msg-or-val extra-value state))))) #+acl2-par (defmacro cmp-and-value-to-error-quadruple@par (form) ; We convert a context-message pair and an extra value (see the Essay on ; Context-message Pairs) to the #+acl2-par version of an error quadruple, ; printing an error message if one is called for. ; Keep in sync with cmp-and-value-to-error-quadruple. `(mv-let (ctx msg-or-val extra-value) ,form (cond (ctx (cond (msg-or-val (assert$ (not (eq ctx t)) (mv-let (erp val) (er@par soft ctx "~@0" msg-or-val) (declare (ignore erp val)) (mv t nil extra-value)))) (t (mv t nil extra-value)))) (t (mv nil msg-or-val extra-value))))) (defun er-cmp-fn (ctx msg) ; Warning: Keep in sync with trans-er. For an explanation, see the ; corresponding warning in trans-er. (declare (xargs :guard t)) (mv ctx msg)) (defmacro er-cmp (ctx str &rest args) ; Warning: Keep in sync with trans-er. For an explanation, see the ; corresponding warning in trans-er. `(er-cmp-fn ,ctx (msg ,str ,@args))) (defmacro value-cmp (x) `(mv nil ,x)) (defun er-progn-fn-cmp (lst) ; Warning: Keep this in sync with er-progn-fn. (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((endp (cdr lst)) (car lst)) (t (list 'mv-let '(er-progn-not-to-be-used-elsewhere-ctx er-progn-not-to-be-used-elsewhere-msg) (car lst) ; Avoid possible warning after optimized compilation: '(declare (ignorable er-progn-not-to-be-used-elsewhere-msg)) (list 'if 'er-progn-not-to-be-used-elsewhere-ctx '(mv er-progn-not-to-be-used-elsewhere-ctx er-progn-not-to-be-used-elsewhere-msg) (list 'check-vars-not-free '(er-progn-not-to-be-used-elsewhere-ctx er-progn-not-to-be-used-elsewhere-msg) (er-progn-fn-cmp (cdr lst)))))))) (defmacro er-progn-cmp (&rest lst) (declare (xargs :guard (and (true-listp lst) lst))) (er-progn-fn-cmp lst)) (defmacro er-let*-cmp (alist body) ; Warning: Keep this in sync with er-let*. ; This macro introduces the variable er-let-star-use-nowhere-else. ; The user who uses that variable in his forms is likely to be ; disappointed by the fact that we rebind it. (declare (xargs :guard (and (doubleton-list-p alist) (symbol-alistp alist)))) (cond ((null alist) (list 'check-vars-not-free '(er-let-star-use-nowhere-else) body)) (t (list 'mv-let (list 'er-let-star-use-nowhere-else (caar alist)) (cadar alist) (list 'cond (list 'er-let-star-use-nowhere-else (list 'mv 'er-let-star-use-nowhere-else (caar alist))) (list t (list 'er-let*-cmp (cdr alist) body))))))) (defun warning1-cw (ctx summary str alist wrld state-vars) ; This function has the same effect as warning1, except that printing is in a ; wormhole and hence doesn't modify state. (warning1-form t)) (defmacro warning$-cw1 (&rest args) ; This macro assumes that wrld and state-vars are bound to a world and ; state-vars record, respectively. ; Warning: Keep this in sync with warning$. (list 'warning1-cw (car args) ; We seem to have seen a GCL 2.6.7 compiler bug, laying down bogus calls of ; load-time-value, when replacing (consp (cadr args)) with (and (consp (cadr ; args)) (stringp (car (cadr args)))). But it seems fine to have the semantics ; of warning$ be that conses are quoted in the second argument position. (if (consp (cadr args)) (kwote (cadr args)) (cadr args)) (caddr args) (make-fmt-bindings '(#\0 #\1 #\2 #\3 #\4 #\5 #\6 #\7 #\8 #\9) (cdddr args)) 'wrld 'state-vars)) (defmacro warning$-cw (ctx &rest args) ; This differs from warning$-cw1 only in that state-vars and wrld are bound ; here for the user, so that warnings are not suppressed merely by virtue of ; the value of state global 'ld-skip-proofsp. Thus, unlike warning$ and ; warning$-cw, there is no warning string, and a typical use of this macro ; might be: ; (warning$-cw ctx "The :REWRITE rule ~x0 loops forever." name). `(let ((state-vars (default-state-vars #-hons nil #+hons :hons)) (wrld nil)) (warning$-cw1 ,ctx nil ,@args))) (defun chk-length-and-keys (actuals form wrld) (cond ((null actuals) (value-cmp nil)) ((null (cdr actuals)) (er-cmp *macro-expansion-ctx* "A non-even key/value arglist was encountered while macro ~ expanding ~x0. The argument list for ~x1 is ~%~F2." form (car form) (macro-args (car form) wrld))) ((keywordp (car actuals)) (chk-length-and-keys (cddr actuals) form wrld)) (t (er-cmp *macro-expansion-ctx* "A non-keyword was encountered while macro expanding ~x0 ~ where a keyword was expected. The formal parameters list ~ for ~x1 is ~%~F2." form (car form) (macro-args (car form) wrld))))) (defun bind-macro-args-keys1 (args actuals allow-flg alist form wrld state-vars) ; We need parameter state-vars because of the call of warning$-cw1 below. (cond ((null args) (cond ((or (null actuals) allow-flg) (value-cmp alist)) (t (er-cmp *macro-expansion-ctx* "Illegal key/value args ~x0 in macro expansion of ~ ~x1. The argument list for ~x2 is ~%~F3." actuals form (car form) (macro-args (car form) wrld))))) ((eq (car args) '&allow-other-keys) (value-cmp alist)) (t (let* ((formal (cond ((atom (car args)) (car args)) ((atom (caar args)) (caar args)) (t (cadr (caar args))))) (key (cond ((atom (car args)) (intern (symbol-name (car args)) "KEYWORD")) ((atom (car (car args))) (intern (symbol-name (caar args)) "KEYWORD")) (t (caaar args)))) (tl (assoc-keyword key actuals)) (alist (cond ((and (consp (car args)) (= 3 (length (car args)))) (cons (cons (caddr (car args)) (not (null tl))) alist)) (t alist)))) (prog2$ (cond ((assoc-keyword key (cddr tl)) (warning$-cw1 *macro-expansion-ctx* "Duplicate-Keys" "The keyword argument ~x0 occurs twice in ~ ~x1. This situation is explicitly ~ allowed in Common Lisp (see CLTL2, page ~ 80) but it often suggests a mistake was ~ made. The leftmost value for ~x0 is used." key form)) (t nil)) (bind-macro-args-keys1 (cdr args) (remove-keyword key actuals) allow-flg (cons (cons formal (cond (tl (cadr tl)) ((atom (car args)) nil) ((> (length (car args)) 1) (cadr (cadr (car args)))) (t nil))) alist) form wrld state-vars)))))) (defun bind-macro-args-keys (args actuals alist form wrld state-vars) (er-progn-cmp (chk-length-and-keys actuals form wrld) (cond ((assoc-keyword :allow-other-keys (cdr (assoc-keyword :allow-other-keys actuals))) (er-cmp *macro-expansion-ctx* "ACL2 prohibits multiple :allow-other-keys because ~ implementations differ significantly concerning which ~ value to take.")) (t (value-cmp nil))) (bind-macro-args-keys1 args actuals (let ((tl (assoc-keyword :allow-other-keys actuals))) (and tl (cadr tl))) alist form wrld state-vars))) (defun bind-macro-args-after-rest (args actuals alist form wrld state-vars) (cond ((null args) (value-cmp alist)) ((eq (car args) '&key) (bind-macro-args-keys (cdr args) actuals alist form wrld state-vars)) (t (er-cmp *macro-expansion-ctx* "Only keywords and values may follow &rest or &body; error in ~ macro expansion of ~x0." form)))) (defun bind-macro-args-optional (args actuals alist form wrld state-vars) (cond ((null args) (cond ((null actuals) (value-cmp alist)) (t (er-cmp *macro-expansion-ctx* "Wrong number of args in macro expansion of ~x0." form)))) ((eq (car args) '&key) (bind-macro-args-keys (cdr args) actuals alist form wrld state-vars)) ((member (car args) '(&rest &body)) (bind-macro-args-after-rest (cddr args) actuals (cons (cons (cadr args) actuals) alist) form wrld state-vars)) ((symbolp (car args)) (bind-macro-args-optional (cdr args) (cdr actuals) (cons (cons (car args) (car actuals)) alist) form wrld state-vars)) (t (let ((alist (cond ((equal (length (car args)) 3) (cons (cons (caddr (car args)) (not (null actuals))) alist)) (t alist)))) (bind-macro-args-optional (cdr args) (cdr actuals) (cons (cons (car (car args)) (cond (actuals (car actuals)) ((>= (length (car args)) 2) (cadr (cadr (car args)))) (t nil))) alist) form wrld state-vars))))) (defun bind-macro-args1 (args actuals alist form wrld state-vars) (cond ((null args) (cond ((null actuals) (value-cmp alist)) (t (er-cmp *macro-expansion-ctx* "Wrong number of args in macro expansion of ~x0." form)))) ((member-eq (car args) '(&rest &body)) (bind-macro-args-after-rest (cddr args) actuals (cons (cons (cadr args) actuals) alist) form wrld state-vars)) ((eq (car args) '&optional) (bind-macro-args-optional (cdr args) actuals alist form wrld state-vars)) ((eq (car args) '&key) (bind-macro-args-keys (cdr args) actuals alist form wrld state-vars)) ((null actuals) (er-cmp *macro-expansion-ctx* "Wrong number of args in macro expansion of ~x0." form)) (t (bind-macro-args1 (cdr args) (cdr actuals) (cons (cons (car args) (car actuals)) alist) form wrld state-vars)))) (defun bind-macro-args (args form wrld state-vars) (cond ((and (consp args) (eq (car args) '&whole)) (bind-macro-args1 (cddr args) (cdr form) (list (cons (cadr args) form)) form wrld state-vars)) (t (bind-macro-args1 args (cdr form) nil form wrld state-vars)))) (defun macroexpand1-cmp (x ctx wrld state-vars) (let ((gc-off (gc-off1 (access state-vars state-vars :guard-checking-on)))) (er-let*-cmp ((alist (bind-macro-args (macro-args (car x) wrld) x wrld state-vars))) (mv-let (erp guard-val) (ev-w (guard (car x) nil wrld) alist wrld nil ; user-stobj-alist t gc-off nil ; It is probably critical to use nil for the aok argument of this call. ; Otherwise, one can imagine a book with sequence of events ; (local EVENT0) ; (defattach ...) ; EVENT0 ; such that a change in macroexpansion, due to the defattach, causes a ; different event to be exported from the book, for EVENT0, than the local one ; originally admitted. nil) (cond (erp (er-cmp ctx "In the attempt to macroexpand the form ~x0 ~ evaluation of the guard for ~x2 caused the ~ following error:~|~%~@1" x guard-val (car x))) ((null guard-val) (er-cmp ctx "In the attempt to macroexpand the form ~x0 the guard, ~ ~x1, for ~x2 failed." x (guard (car x) nil wrld) (car x))) (t (mv-let (erp expansion) (ev-w (getprop (car x) 'macro-body '(:error "Apparently macroexpand1 was ~ called where there was no ~ macro-body.") 'current-acl2-world wrld) alist wrld nil ; user-stobj-alist (not (global-val 'boot-strap-flg wrld)) ; safe-mode gc-off nil nil) (cond (erp (er-cmp ctx "In the attempt to macroexpand the ~ form ~x0, evaluation of the macro ~ body caused the following ~ error:~|~%~@1" x expansion)) (t (value-cmp expansion)))))))))) (defun macroexpand1 (x ctx state) (cmp-to-error-triple (macroexpand1-cmp x ctx (w state) (default-state-vars t)))) (defun chk-declare (form ctx) (let ((msg "An expression has occurred where we expect a form whose car is ~ DECLARE; yet, that expression is ~x0. This problem generally is ~ caused by (a) a parenthesis mistake, (b) the use of an ``implicit ~ PROGN'' so that a term that you intended to be part of the body was ~ taken as a declaration, or (c) the incorrect belief that ~ macroexpansion is applied to declarations. See :DOC declare.")) (cond ((or (not (consp form)) (not (symbolp (car form)))) (er-cmp ctx msg form)) ((eq (car form) 'declare) (cond ((not (true-listp form)) (er-cmp ctx "A declaration must be a true-list but ~x0 is not. ~ See :DOC declare." form)) (t (value-cmp form)))) (t (er-cmp ctx msg form))))) (defun collect-dcls (l ctx) (cond ((null l) (value-cmp nil)) (t (er-let*-cmp ((expansion (chk-declare (car l) ctx)) (rst (collect-dcls (cdr l) ctx))) (value-cmp (append (cdr expansion) rst)))))) ; The following alist maps "binders" to the permitted types of ; declarations at the top-level of the binding environment. (defun acceptable-dcls-alist (state-vars) ; Warning: Keep this in sync with :DOC declare. ; The declarations when (hons-enabledp state) have been found useful by Bob ; Boyer for the experimental hons version, but have not yet been carefully ; evaluated for soundness, so we do not advertise them. In fact, we decided ; that inline and notinline are not appropriate to support when we introduced ; defun-inline. (let ((hons-enabledp (access state-vars state-vars :hons-enabled))) `((let ignore ignorable type ,@(if hons-enabledp '(dynamic-extent) nil)) (mv-let ignore ignorable type) (flet ignore ignorable type) ; for each individual definition in the flet (defmacro ignore ignorable type xargs) (defuns ignore ignorable type optimize xargs ,@(if hons-enabledp '(inline notinline) nil))))) ; The following list gives the names of binders that permit at most ; one documentation string among their declarations. If this list is ; changed, visit all calls of collect-declarations because its answer ; is known NOT to have a doc string in it if the binder on which it ; was called is not in this list. (defconst *documentation-strings-permitted* '(defmacro defuns)) ; For each type of declaration the following alist offers an explanatory ; string. (defconst *dcl-explanation-alist* '((ignore "(IGNORE v1 ... vn) and (IGNORABLE v1 ... vn), where the vi are ~ introduced in the immediately superior lexical environment") (type "(TYPE type v1 ... vn), as described on pg 158 of CLTL") (xargs "(XARGS :key1 :val1 ... :keyn :valn), where each :keyi is a ~ keyword (e.g., :GUARD or :HINTS)"))) ; The following two functions are used to create an appropriate error ; message explaining what kinds of declarations are permitted by a binder. (defun tilde-*-conjunction-phrase1 (syms alist) (cond ((null syms) nil) (t (let ((temp (assoc-eq (car syms) alist))) (cons (cond (temp (cdr temp)) (t (coerce (cons #\( (append (explode-atom (car syms) 10) (coerce " ...)" 'list))) 'string))) (tilde-*-conjunction-phrase1 (cdr syms) alist)))))) (defun tilde-*-conjunction-phrase (syms alist) ; Syms is a list of symbols. Alist maps symbols to strings, called ; the "explanation" of each symbol. We create an object that when ; given to the tilde-* fmt directive will print out the conjunction of ; the explanations for each of the symbols. (let ((syms (cond ((member-eq 'ignorable syms) (let ((syms (remove1-eq 'ignorable syms))) (if (member-eq 'ignore syms) syms (cons 'ignore syms)))) (t syms)))) (list "" "~@*" "~@* and " "~@*, " (tilde-*-conjunction-phrase1 syms alist)))) (defun collect-non-legal-variableps (lst) (cond ((null lst) nil) ((legal-variablep (car lst)) (collect-non-legal-variableps (cdr lst))) (t (cons (car lst) (collect-non-legal-variableps (cdr lst)))))) (defun optimize-alistp (lst) (cond ((atom lst) (null lst)) ((consp (car lst)) (and (consp (cdar lst)) (null (cddar lst)) (symbolp (caar lst)) (integerp (cadar lst)) (<= 0 (cadar lst)) (<= (cadar lst) 3) (optimize-alistp (cdr lst)))) (t (and (symbolp (car lst)) (optimize-alistp (cdr lst)))))) (defun chk-dcl-lst (l vars binder ctx wrld state-vars) ; L is the list of expanded declares. Vars is a list of variables ; bound in the immediately superior lexical environment. Binder is ; a binder, as listed in acceptable-dcls-alist. (cond ((null l) (value-cmp nil)) (t (er-progn-cmp (let ((entry (car l))) (cond ((not (consp entry)) (er-cmp ctx "Each element of a declaration must be a cons, but ~x0 is ~ not. See :DOC declare." entry)) (t (let ((dcl (car entry)) (temp (cdr (assoc-eq binder (acceptable-dcls-alist state-vars))))) (cond ((not (member-eq dcl temp)) (er-cmp ctx "The only acceptable declaration~#0~[~/s~] at the ~ top-level of ~#1~[an FLET binding~/a ~x2 form~] ~ ~#0~[is~/are~] ~*3. The declaration ~x4 is thus ~ unacceptable here. See :DOC declare." temp (if (eq binder 'flet) 0 1) binder (tilde-*-conjunction-phrase temp *dcl-explanation-alist*) entry)) ((not (true-listp entry)) (er-cmp ctx "Each element of a declaration must end in NIL but ~ ~x0 does not. See :DOC declare." entry)) (t (case dcl ((dynamic-extent notinline inline) (cond ((access state-vars state-vars :hons-enabled) ; should check we are binding a variable of a let (value-cmp nil)) (t (er-cmp ctx "The declaration ~x0 is illegal except in ~ a hons-enabled ACL2 executable. See ~ :DOC hons-and-memoization." dcl)))) (optimize (cond ((optimize-alistp (cdr entry)) (value-cmp nil)) (t (er-cmp ctx "Each element in the list following an ~ OPTIMIZE declaration must be either a ~ symbol or a pair of the form (quality ~ value), where quality is a symbol and ~ value is an integer between 0 and 3. ~ Your OPTIMIZE declaration, ~x0, does not ~ meet this requirement." entry)))) ((ignore ignorable) (cond ((subsetp (cdr entry) vars) (value-cmp nil)) (t (er-cmp ctx "The variables of an ~x0 declaration must ~ be introduced in the immediately ~ superior lexical environment, but ~&1, ~ which ~#1~[is~/are~] said to be ~ ~#2~[ignored~/ignorable~] in ~x3, ~ ~#1~[is~/are~] not bound immediately ~ above the declaration. See :DOC declare." dcl (set-difference-equal (cdr entry) vars) (if (eq dcl 'ignore) 0 1) entry)))) (type (cond ((not (>= (length entry) 3)) (er-cmp ctx "The length of a type declaration must be at ~ least 3, but ~x0 does not satisfy this ~ condition. See :DOC declare." entry)) ((collect-non-legal-variableps (cddr entry)) (er-cmp ctx "Only the types of variables can be declared by ~ TYPE declarations such as ~x0. But ~&1 ~#1~[is ~ not a legal ACL2 variable symbol~/are not legal ~ ACL2 variable symbols~]. See :DOC declare." entry (collect-non-legal-variableps (cddr entry)))) ((not (subsetp (cddr entry) vars)) (er-cmp ctx "The variables declared in a type declaration, ~ such as ~x0, must be bound immediately above, ~ but ~&1 ~#1~[is~/are~] not bound. See :DOC ~ declare." entry (set-difference-equal (cddr entry) vars))) ((not (translate-declaration-to-guard (cadr entry) 'var wrld)) ; We use the variable var because we are not interested in the ; particular value returned, only whether (cadr entry) stands for some ; type. (cond ((and (true-listp (cadr entry)) (int= (length (cadr entry)) 3) (eq (car (cadr entry)) 'or) (eq (cadr (cadr entry)) t)) ; The type-spec is (or t x). There is an excellent change that this comes from ; (the type-spec ...); see the-fn. So we change the error message a bit for ; this case. Note that the error message is accurate, since (or t x) is ; illegal as a type-spec iff x is illegal. And the message is reasonable ; because it is not misleading and it is likely to be only for THE, where the ; user did not use an explicit declaration (which was generated by us). (er-cmp ctx "~x0 fails to be a legal type-spec. See ~ :MORE-DOC type-spec." (caddr (cadr entry)))) ((weak-satisfies-type-spec-p (cadr entry)) (er-cmp ctx "In the declaration ~x0, ~x1 fails to be a ~ legal type-spec because the symbol ~x2 is not ~ a known function symbol~@3. See :MORE-DOC ~ type-spec." entry (cadr entry) (cadr (cadr entry)) (if (eq (getprop (cadr (cadr entry)) 'macro-args t 'current-acl2-world wrld) t) "" "; rather, it is the name of a macro"))) (t (er-cmp ctx "In the declaration ~x0, ~x1 fails to be a ~ legal type-spec. See :MORE-DOC type-spec." entry (cadr entry))))) (t (value-cmp nil)))) (xargs (cond ((not (keyword-value-listp (cdr entry))) (er-cmp ctx "The proper form of the ACL2 declaration is ~ (XARGS :key1 val1 ... :keyn valn), where each ~ :keyi is a keyword and no key occurs twice. ~ Your ACL2 declaration, ~x0, is not of this ~ form. See :DOC xargs." entry)) ((not (no-duplicatesp-equal (evens (cdr entry)))) (er-cmp ctx "Even though Common Lisp permits duplicate ~ occurrences of keywords in keyword/actual ~ lists, all but the left-most occurrence are ~ ignored. You have duplicate occurrences of the ~ keyword~#0~[~/s~] ~&0 in your declaration ~x1. ~ This suggests a mistake has been made." (duplicates (evens (cdr entry))) entry)) ((and (eq binder 'defmacro) (assoc-keyword :stobjs (cdr entry))) (er-cmp ctx "The use of the :stobjs keyword is prohibited ~ for an xargs declaration in a call of defmacro.")) (t (value-cmp nil)))) (otherwise (mv t (er hard! 'chk-dcl-lst "Implementation error: A declaration, ~x0, is ~ mentioned in acceptable-dcls-alist but not in ~ chk-dcl-lst." dcl)))))))))) (chk-dcl-lst (cdr l) vars binder ctx wrld state-vars))))) (defun number-of-strings (l) (cond ((null l) 0) ((stringp (car l)) (1+ (number-of-strings (cdr l)))) (t (number-of-strings (cdr l))))) (defun remove-strings (l) (cond ((null l) nil) ((stringp (car l)) (remove-strings (cdr l))) (t (cons (car l) (remove-strings (cdr l)))))) (defun get-string (l) (cond ((null l) nil) ((stringp (car l)) (list (car l))) (t (get-string (cdr l))))) (defun collect-declarations-cmp (lst vars binder ctx wrld state-vars) ; Lst is a list of (DECLARE ...) forms, and/or documentation strings. ; We check that the elements are declarations of the types appropriate ; for binder, which is one of the names bound in ; acceptable-dcls-alist. For IGNORE and TYPE declarations, which ; are seen as part of term translation (e.g., in LETs), we check that ; the variables mentioned are bound in the immediately superior ; lexical scope (i.e., are among the vars (as supplied) bound by ; binder). But for all other declarations, e.g., GUARD, we merely ; check the most routine syntactic conditions. WE DO NOT TRANSLATE ; the XARGS. We return a list of the checked declarations. I.e., if ; given ((DECLARE a b)(DECLARE c d)) we return (a b c d), or else ; cause an error. If given ((DECLARE a b) "Doc string" (DECLARE c d)) ; (and binder is among those in *documentation-strings-permitted*), ; we return ("Doc string" a b c d). ; If binder is among those in *documentation-strings-permitted* we permit ; at most one documentation string in lst. Otherwise, we cause an error. (cond ((> (number-of-strings lst) (if (member-eq binder *documentation-strings-permitted*) 1 0)) (cond ((member-eq binder *documentation-strings-permitted*) (er-cmp ctx "At most one documentation string is permitted at the ~ top-level of ~x0 but you have provided ~n1." binder (number-of-strings lst))) (t (er-cmp ctx "Documentation strings are not permitted in ~x0 forms." binder)))) (t (er-let*-cmp ((dcls (collect-dcls (remove-strings lst) ctx))) (er-progn-cmp (chk-dcl-lst dcls vars binder ctx wrld state-vars) (value-cmp (append (get-string lst) dcls))))))) (defun collect-declarations (lst vars binder state ctx) (cmp-to-error-triple (collect-declarations-cmp lst vars binder ctx (w state) (default-state-vars t)))) (defun listify (l) (cond ((null l) *nil*) (t (list 'cons (car l) (listify (cdr l)))))) (defun translate-declaration-to-guard-var-lst (x var-lst wrld) ; It is assumed that (translate-declaration-to-guard x 'var wrld) is ; non-nil. This function translates the declaration x for each of the ; vars in var-lst and returns the list of translations. (declare (xargs :guard (and (true-listp var-lst) (plist-worldp wrld)))) (cond ((null var-lst) nil) (t (cons (translate-declaration-to-guard x (car var-lst) wrld) (translate-declaration-to-guard-var-lst x (cdr var-lst) wrld))))) (defun translate-dcl-lst (edcls wrld) ; Given a bunch of expanded dcls we find all the (TYPE x v1 ... vn) ; dcls among them and make a list of untranslated terms expressing the ; type restriction x for each vi. (cond ((null edcls) nil) ((eq (caar edcls) 'type) (append (translate-declaration-to-guard-var-lst (cadr (car edcls)) (cddr (car edcls)) wrld) (translate-dcl-lst (cdr edcls) wrld))) (t (translate-dcl-lst (cdr edcls) wrld)))) (defun dcl-guardian (term-lst) ; Suppose term-lst is a list of terms, e.g., '((INTEGERP X) (SYMBOLP V)). ; We produce an expression that evaluates to t if the conjunction of the ; terms is true and returns a call of illegal otherwise. (cond ((or (null term-lst) ; A special case is when term-list comes from (the (type type-dcl) x). The ; expansion of this call of THE results in a declaration of the form (declare ; (type (or t type-dcl) var)). We have seen examples where generating the ; resulting if-term, to be used in a call of prog2$, throws off a proof that ; succeeded before the addition of this declaration (which was added in order ; to handle (the (satisfies pred) term)); specifically, len-pushus in ; symbolic/tiny-fib/tiny.lisp (and probably in every other tiny.lisp). Here we ; simplify the resulting term (if t t (type-pred x)) to t. And when we use ; dcl-guardian to create (prog2$ type-test u), we instead simply create u if ; type-test is t. (let ((term (car term-lst))) (and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'if) (equal (fargn term 1) *t*) (equal (fargn term 2) *t*)))) *t*) (t (fcons-term* 'if (car term-lst) (dcl-guardian (cdr term-lst)) '(illegal 'verify-guards '"Some TYPE declaration is violated." 'nil))))) (defun ignore-vars (dcls) (cond ((null dcls) nil) ((eq (caar dcls) 'ignore) (append (cdar dcls) (ignore-vars (cdr dcls)))) (t (ignore-vars (cdr dcls))))) (defun ignorable-vars (dcls) (cond ((null dcls) nil) ((eq (caar dcls) 'ignorable) (append (cdar dcls) (ignorable-vars (cdr dcls)))) (t (ignorable-vars (cdr dcls))))) (defun mv-nth-list (var i maximum) (cond ((= i maximum) nil) (t (cons (fcons-term* 'mv-nth (list 'quote i) var) (mv-nth-list var (1+ i) maximum))))) (defabbrev translate-bind (x val bindings) ; Used only in translation. Binds x to val on bindings. (cons (cons x val) bindings)) (defun translate-deref (x bindings) ; X is t, a consp value or the name of some function. If the last, we ; chase down its ``ultimate binding'' in bindings. Bindings may ; contain many indirections, but may not be circular except when x is ; bound to x itself. We return nil if x is not bound in bindings. (cond ((eq x t) t) ((consp x) x) (t (let ((p (assoc-eq x bindings))) (cond (p (cond ((eq x (cdr p)) x) (t (translate-deref (cdr p) bindings)))) (t nil)))))) (defun translate-unbound (x bindings) ; X is considered unbound if it is a function name whose ultimate ; binding is a function name. (and (not (eq x t)) (atom (translate-deref x bindings)))) (defun listlis (l1 l2) ; Like pairlis$, but LISTs instead of CONSes. (cond ((null l1) nil) (t (cons (list (car l1) (car l2)) (listlis (cdr l1) (cdr l2)))))) (mutual-recursion (defun find-first-var (term) (cond ((variablep term) term) ((fquotep term) nil) ((find-first-var-lst (fargs term))) ((flambdap (ffn-symb term)) (car (lambda-formals (ffn-symb term)))) (t nil))) (defun find-first-var-lst (lst) (cond ((null lst) nil) (t (or (find-first-var (car lst)) (find-first-var-lst (cdr lst)))))) ) (mutual-recursion (defun find-first-fnsymb (term) (cond ((variablep term) nil) ((fquotep term) nil) ((flambdap (ffn-symb term)) (or (find-first-fnsymb-lst (fargs term)) (find-first-fnsymb (lambda-body (ffn-symb term))))) (t (ffn-symb term)))) (defun find-first-fnsymb-lst (lst) (cond ((null lst) nil) (t (or (find-first-fnsymb (car lst)) (find-first-fnsymb-lst (cdr lst)))))) ) (defun find-pkg-witness (term) ; This function must return a symbol. Imagine that term is to be replaced by ; some variable symbol. In which package do we intern that symbol? This ; function finds a symbol which is used with intern-in-package-of-symbol. ; Thus, the package of the returned symbol is important to human readability. ; We return the first variable we see in term, if there is one. Otherwise, we ; return the first function symbol we see, if there is one. Otherwise, we ; return the symbol 'find-pkg-witness. (or (find-first-var term) (find-first-fnsymb term) 'find-pkg-witness)) ; TRANSLATE ; For comments on translate, look after the following nest. (defmacro trans-er (&rest args) ; Warning: Keep in sync with er-cmp (see commented-out call below) and ; er-cmp-fn. We avoid using er-cmp because we don't want break-on-error to ; break on translate errors, since we know that sometimes translate errors are ; benign -- for example, in translate11 we backtrack if there is an error in ; translating the term tbr in (IF tst tbr fbr), to translate fbr first. ; Like er-cmp but returns 3 values, the additional one being the current value ; of bindings. See also trans-er+ and trans-er+?. `(mv-let (ctx msg-or-val) ; (er-cmp ,@args) ; See "keep in sync" comment above. (mv ,(car args) (msg ,(cadr args) ,@(cddr args))) (mv ctx msg-or-val bindings))) (defmacro trans-er+ (form ctx str &rest args) ; Warning: Keep in sync with er-cmp (see commented-out call below) and ; er-cmp-fn. For an explanation, see the corresponding warning in trans-er. ; This macro is like trans-er, but it also prints the offending context, form, ; which could be the untranslated term or a surrounding term, etc. `(mv-let (ctx msg-or-val) ; (er-cmp ,ctx ; See "keep in sync" comment above. ; "~@0 Note: this error occurred in the context ~x1." ; (msg ,str ,@args) ; ,form) (mv ,ctx (msg "~@0 Note: this error occurred in the context ~x1." (msg ,str ,@args) ,form)) (mv ctx msg-or-val bindings))) (defmacro trans-er+? (cform x ctx str &rest args) ; This macro behaves as trans-er+ using cform, if x and cform are distinct (in ; which case cform can provide context beyond x); else it behaves as trans-er. ; The guard is for efficiency, to guarantee that we don't evaluate x or cform ; twice. (Actually x is only evaluated once by the expansion of this macro, ; but it is likely evaluated in another place by the calling code.) (declare (xargs :guard (and (symbolp cform) (symbolp x)))) `(cond ((equal ,x ,cform) (trans-er ,ctx ,str ,@args)) (t (trans-er+ ,cform ,ctx ,str ,@args)))) (defmacro trans-value (x &optional (bindings 'bindings)) ; Like value-cmp but returns 3 values, erp, x, and bindings. `(mv nil ,x ,bindings)) (defmacro trans-er-let* (alist body) ; Like er-let*-cmp but deals in trans-er's 3-tuples and binds and returns ; bindings. (declare (xargs :guard (alistp alist))) (cond ((null alist) (list 'check-vars-not-free '(er-let-star-use-nowhere-else) body)) (t (list 'mv-let (list 'er-let-star-use-nowhere-else (caar alist) 'bindings) (cadar alist) (list 'cond (list 'er-let-star-use-nowhere-else (list 'mv 'er-let-star-use-nowhere-else (caar alist) 'bindings)) (list t (list 'trans-er-let* (cdr alist) body))))))) (defun hide-ignored-actuals (ignore-vars bound-vars value-forms) (cond ; Most of the time there won't be any ignore-vars, so we don't mind ; paying the price of checking the following condition on each ; recursive call (even though the answer remains the same). ((null ignore-vars) value-forms) ((null bound-vars) nil) ((and (member-eq (car bound-vars) ignore-vars) (let ((form (car value-forms))) (and (or (variablep form) (fquotep form) (not (eq (ffn-symb form) 'hide))) (cons (fcons-term* 'hide form) (hide-ignored-actuals ignore-vars (cdr bound-vars) (cdr value-forms))))))) (t (cons (car value-forms) (hide-ignored-actuals ignore-vars (cdr bound-vars) (cdr value-forms)))))) (defun augment-ignore-vars (bound-vars value-forms acc) ; Note added shortly before releasing ACL2 Version_6.1. This function seems to ; have been added in Version_2.9.4. It's not clear that we need this function, ; since it doesn't seem that translate11 is passed a form with HIDE calls ; already added in the manner described below. For now we'll continue to calls ; this function, as it seems harmless enough. We might want to try a ; regression sometime with it redefined simply to return acc, and if that ; succeeds, we could consider deleting it. (But that seems dangerous to do ; just before a release!) ; Bound-vars and value-forms are lists of the same length. Return the result ; of extending the list acc by each member of bound-vars for which the ; corresponding element of value-forms (i.e., in the same position) is a call ; of hide. Since translate11 inserts a call of hide for each bound var, this ; function returns a list that contains every variable declared ignored in the ; original let form binding bound-vars to value-forms (or the corresponding ; untranslations of the terms in value-forms). (cond ((endp bound-vars) acc) ((let ((form (car value-forms))) (or (variablep form) (fquotep form) (not (eq (ffn-symb form) 'hide)))) (augment-ignore-vars (cdr bound-vars) (cdr value-forms) acc)) (t (augment-ignore-vars (cdr bound-vars) (cdr value-forms) (cons (car bound-vars) acc))))) ; Essay on STOBJS-IN and STOBJS-OUT ; Once upon a time, before user-defined single-threaded objects came along, ; every function symbol had four aspects to its syntactic character: ; * its arity ; * which of its inputs was STATE ; * its multiplicity (how many results it returns) ; * which of its outputs was STATE ; These were coded on the property list in a somewhat optimized way involving ; the four properties FORMALS, STATE-IN, MULTIPLICITY, and STATE-OUT. If ; STATE-IN was absent or NIL, then STATE was not a formal. Otherwise, STATE-IN ; indicated the position (1-based) of STATE in the FORMALS. If MULTIPLICITY ; was absent, it was implicitly 1. If STATE-OUT was T then multiplicity was 1 ; and STATE was the single result. We review these old characteristics because ; they were generalized when we introduced single-threaded objects, or ; ``stobjs''. ; Since the introduction of stobjs, every function has four aspects to its ; syntactic character: ; * its arity ; * which of its inputs are stobjs ; * its multiplicity ; * which of its outputs are stobjs ; This is coded on the property list as follows. First, a ``STOBJ flag'' is ; either NIL or the name of a stobj (including STATE). A list of n STOBJ flags ; can thus indicate which elements of another list of length n are stobjs and ; which stobjs they are. ; FORMALS gives the list of formals. ; STOBJS-IN is a list of STOBJ flags that is interpreted in 1:1 correspondence ; with the formals. Every function symbol must have a STOBJS-IN property. We ; do not support space-efficient coding of any special cases. Each formal must ; be the corresponding stobj. ; STOBJS-OUT is a list of stobj flags indicating both the multiplicity and ; which outputs are stobjs, and the correspondence between output stobjs and ; input stobjs. For example, if the STOBJS-IN property is (nil $s1 $s2 nil) ; and the STOBJS-OUT property is (nil $s2), then two values are returned, where ; the second value returned is the same stobj as the third input (labeled $s2 ; above). Every function must have a STOBJS-OUT property, with the effective ; exception of return-last: an error is caused if the function stobjs-out is ; applied to return-last, which always returns its last argument (possibly a ; multiple value) and should generally be considered as not having STOBJS-OUT. ; We now consider translation performed on behalf of evaluation (as opposed to ; translating only for the logic, as when translating proposed theorems). ; During translation of each argument of a function call, we generally have a ; stobj flag associated with the term we are translating, indicating the ; expected stobj, if any, produced by the term. Consider a stobj flag, $s, ; that is non-nil, i.e., is a stobj name. Then the term occupying the ; corresponding slot MUST be the stobj name $s, except in the case that ; congruent stobjs are involved (see below). We think of the stobj flags as ; meaning that the indicated stobj name is the only term that can be passed ; into that slot. ; We mentioned a relaxation above for the case of congruent stobjs. (See :DOC ; defstobj for an introduction to congruent stobjs.) Consider again a function ; call. Each argument corresponding to a non-nil stobj flag should be ; a stobj that is congruent to that flag (a stobj). Moreoever, no two such ; arguments may be the same. ; We turn now from translation to evaluation in the logic (i.e., with *1* ; functions that might or might not pass control to raw Lisp functions). ; Our stobj primitives are all capable of computing on the logical objects that ; represent stobjs. But they give special treatment to the live ones. There ; are two issues. First, we do not want a live one ever to get into a ; non-stobj slot because the rest of the functions do not know how to handle ; it. So if the actual is a live stobj, the formal must be a stobj. Second, ; if the ith element of STOBJS-IN is a stobj, $s, and the jth element of ; STOBJS-OUT is also $s, and the ith actual of a call is a live stobj, then the ; jth return value from that call is that same live stobj. This is the only ; way that a live stobj can be found in the output (unless there is a call of a ; creator function, which is untouchable). (defun compute-stobj-flags (lst known-stobjs w) ; Lst is a list of possibly UNTRANSLATED terms! This function ; computes the stobj flags for the elements of the list, assigning nil ; unless the element is a symbol with a 'STOBJ property in w. (cond ((endp lst) nil) ((stobjp (car lst) known-stobjs w) (cons (car lst) (compute-stobj-flags (cdr lst) known-stobjs w))) (t (cons nil (compute-stobj-flags (cdr lst) known-stobjs w))))) (defun prettyify-stobj-flags (lst) ; Note: The use of * to denote NIL here is arbitrary. But if another ; symbol is used, make sure it could never be defined as a stobj by ; the user! (cond ((endp lst) nil) (t (cons (or (car lst) '*) (prettyify-stobj-flags (cdr lst)))))) (defun unprettyify-stobj-flags (lst) (cond ((endp lst) nil) (t (cons (if (eq (car lst) '*) nil (car lst)) (unprettyify-stobj-flags (cdr lst)))))) (defun prettyify-stobjs-out (stobjs-out) ; This function uses prettyify-stobj-flags in the singleton case just ; to localize the choice of external form to that function. (if (cdr stobjs-out) (cons 'mv (prettyify-stobj-flags stobjs-out)) (car (prettyify-stobj-flags stobjs-out)))) (defun defstobj-supporterp (name wrld) ; If name is supportive of a single-threaded object implementation, we return ; the name of the stobj. Otherwise, we return nil. By "supportive" we mean ; name is the object name, the live var, a recognizer, accessor, updater, ; helper, resizer, or length function, or a constant introduced by the ; defstobj, or in the case of defabsstobj, a recognizer, accessor, or (other) ; exported function. (cond ((getprop name 'stobj nil 'current-acl2-world wrld) name) ((getprop name 'stobj-function nil 'current-acl2-world wrld)) ((getprop name 'stobj-constant nil 'current-acl2-world wrld)) (t (getprop name 'stobj-live-var nil 'current-acl2-world wrld)))) (defun stobj-creatorp (name wrld) ; Returns the name of the stobj that name creates, if name is a stobj creator; ; else returns nil. ; Keep the null test below in sync with the null test (and stobj-flag (null ; (cadr def))) near the top of oneify-cltl-code. (and (symbolp name) (null (getprop name 'formals t 'current-acl2-world wrld)) (getprop name 'stobj-function nil 'current-acl2-world wrld))) (defun defstobj-fnname (root key1 key2 renaming-alist) ; Warning: Keep this in sync with stobj-updater-guess-from-accessor. ; This has been moved from other-events.lisp, where other stobj-related ; functions are defined, because it is used in parse-with-local-stobj, which is ; used in translate11. ; This function generates the actual name we will use for a function generated ; by defstobj. Root and renaming-alist are, respectively, a symbol and an ; alist. Key1 describes which function name we are to generate and is one of ; :length, :resize, :recognizer, :accessor, :updater, or :creator. Key2 ; describes the ``type'' of root. It is :top if root is the name of the live ; object (and hence, root starts with a $) and it is otherwise either :array or ; :non-array. Note that if renaming-alist is nil, then this function returns ; the ``default'' name used. If renaming-alist pairs some default name with an ; illegal name, the result is, of course, an illegal name. (let* ((default-fnname (case key1 (:recognizer (case key2 (:top (packn-pos (list (coerce (append (coerce (symbol-name root) 'list) '(#\P)) 'string)) root)) (otherwise (packn-pos (list root "P") root)))) ; This function can legitimately return nil for key1 values of :length ; and :resize. We are careful in the assoc-eq call below not to look ; for nil on the renaming-alist. That check is probably not ; necessary, but we include it for robustness. (:length (and (eq key2 :array) (packn-pos (list root "-LENGTH") root))) (:resize (and (eq key2 :array) (packn-pos (list "RESIZE-" root) root))) (:accessor (case key2 (:array (packn-pos (list root "I") root)) (otherwise root))) (:updater (case key2 (:array (packn-pos (list "UPDATE-" root "I") root)) (otherwise (packn-pos (list "UPDATE-" root) root)))) (:creator (packn-pos (list "CREATE-" root) root)) (otherwise (er hard 'defstobj-fnname "Implementation error (bad case); please contact ACL2 ~ implementors.")))) (temp (and default-fnname ; see comment above (assoc-eq default-fnname renaming-alist)))) (if temp (cadr temp) default-fnname))) (defun parse-with-local-stobj (x) ; x is a with-local-stobj form. We return (mv erp stobj-name mv-let-form ; creator-name). (case-match x ((st ('mv-let . mv-let-body)) (cond ((symbolp st) (mv nil st (cons 'mv-let mv-let-body) (defstobj-fnname st :creator :top nil))) (t (mv t nil nil nil)))) ((st ('mv-let . mv-let-body) creator) (mv nil st (cons 'mv-let mv-let-body) creator)) (& (mv t nil nil nil)))) (mutual-recursion (defun ffnnamep (fn term) ; We determine whether the function fn (possibly a lambda-expression) ; is used as a function in term. (cond ((variablep term) nil) ((fquotep term) nil) ((flambda-applicationp term) (or (equal fn (ffn-symb term)) (ffnnamep fn (lambda-body (ffn-symb term))) (ffnnamep-lst fn (fargs term)))) ((eq (ffn-symb term) fn) t) (t (ffnnamep-lst fn (fargs term))))) (defun ffnnamep-lst (fn l) (declare (xargs :guard (and (symbolp fn) (pseudo-term-listp l)))) (if (null l) nil (or (ffnnamep fn (car l)) (ffnnamep-lst fn (cdr l))))) ) (defconst *synp-trans-err-string* "A synp term must take three quoted arguments, unlike ~x0. Normally, a call ~ to synp is the result of the macroexpansion of a call to syntaxp or ~ bind-free, but this does not seem to be the case here. If you believe this ~ error message is itself in error please contact the maintainers of ACL2.") (defconst *ttag-fns-and-macros* ; Each cdr is either nil or a msg. `((open-output-channel!) (progn!) ; protected because it is legal in books; it's OK to omit progn-fn (remove-untouchable-fn . ,(msg " Note that the same restriction applies to the macro ~x0, whose ~ expansions generate calls of ~x1." 'remove-untouchable 'remove-untouchable-fn)) (set-raw-mode-on . ,(msg " If you do not plan to certify books in this session, then ~ instead you may want to call ~x0; see :DOC ~x0." 'set-raw-mode-on!)) (set-temp-touchable-fns) (set-temp-touchable-vars) (sys-call) )) (defun unknown-binding-msg (stobjs-bound str1 str2 str3) (msg "The single-threaded object~#0~[ ~&0 has~/s ~&0 have~] been bound in ~@1. ~ It is a requirement that ~#0~[this object~/these objects~] be among the ~ outputs of ~@2. But, at the time at which we process ~@2, we are unable ~ to determine what the outputs are and so cannot allow it. This situation ~ arises when the output of ~@2 is a recursive call of the function being ~ admitted and the call is encountered before we have encountered the first ~ base case of the function (which would tell us what single-threaded ~ objects are being returned). In the case of the admission of a clique of ~ mutually-recursive functions, the situation can additionally arise when ~ the output of ~@2 is a call of a function in the clique and that function ~ appears in the clique after the definition in question. This situation ~ can be eliminated by rearranging the order of the branches of an IF ~ and/or rearranging the order of the presentation of a clique of mutually ~ recursive functions." stobjs-bound str1 str2 str3)) (defconst *oneify-primitives* ;;;; Some day we should perhaps remove consp and other such functions from this ;;;; list because of the "generalized Boolean" problem. ; Add to this list whenever we find a guardless function in #+acl2-loop-only. '(if equal cons not consp atom acl2-numberp characterp integerp rationalp stringp symbolp ; We want fmt-to-comment-window (which will arise upon macroexpanding calls of ; cw) to be executed always in raw Lisp, so we add it to this list in order to ; bypass its *1* function. fmt-to-comment-window ; When we oneify, we sometimes do so on code that was laid down for constrained ; functions. Therefore, we put throw on the list. throw-raw-ev-fncall ; The next group may be important for the use of safe-mode. makunbound-global trans-eval ev ev-lst ev-fncall ; fmt-to-comment-window ; already included above sys-call-status ; pstack-fn untranslate untranslate-lst trace$-fn-general untrace$-fn-general untrace$-fn1 maybe-untrace$-fn memoize-on memoize-off ; especially relevant for #+hons set-w acl2-unwind-protect ; We know that calls of mv-list in function bodies are checked syntactically to ; satisfy arity and syntactic requirements, so it is safe to call it in raw ; Lisp rather than somehow considering its *1* function. We considered adding ; return-last as well, but not only does return-last have a guard other than T, ; but indeed (return-last 'mbe1-raw exec logic) macroexpands in raw Lisp to ; exec, which isn't what we want in oneified code. We considered adding ; functions in *defun-overrides*, but there is no need, since defun-overrides ; makes suitable definitions for *1* functions. mv-list )) (defconst *macros-for-nonexpansion-in-raw-lisp* ; If a symbol, sym, is on this list then the form (sym a1 ... ak) is oneified ; to (sym a1' ... ak') where ai' is the oneification of ai. Thus, conditions ; for sym being put on this list include that it is defined as a function or ; macro in raw lisp and that it is "applied" to a list of terms. Another ; condition is that it not have a guard, because if a guard is present it is ; likely that Common Lisp will cause an error when we run the oneified version ; on inappropriate inputs. ; The value of this list should be a subset of ; (loop for x in (w state) when (eq (cadr x) 'macro-body) collect (car x)) ; Below we exhibit the value of the sloop above and comment out the macros we ; do not want on it. The macros commented out will be translated away in ; oneified code. ; When in doubt, comment it out! '(f-decrement-big-clock ; we leave these two in oneified code because they f-big-clock-negative-p ; are handled by our raw lisp ; make-list ; ; Must omit f-put-global, f-get-global, and f-boundp-global, in order to ; ; avoid calling global-table in raw Lisp. ; mv-let ; not of the right shape so special-cased in oneify mv ; The following are not in primitive-event-macros (which is handled directly ; in oneify-cltl-code). ; Note that safe-mode for make-event will require addition of the following four: ; certify-book make-event defpkg in-package ; acl2-unwind-protect ; pprogn ; the list* ; rest tenth ninth eighth seventh sixth fifth fourth third second first cddddr ; cdddar cddadr cddaar cdaddr cdadar cdaadr cdaaar cadddr caddar cadadr cadaar ; caaddr caadar caaadr caaaar cdddr cddar cdadr cdaar caddr cadar caadr caaar ; cddr cdar cadr caar ; case progn mutual-recursion ; / * >= > <= ; guarded ; let* cond ; + - ; guarded or and list ; local ; defdoc with-live-state )) ; Historical Note: The following material -- chk-no-duplicate-defuns, ; chk-state-ok, chk-arglist, and chk-defuns-tuples -- used to be in the file ; defuns.lisp. It is mainly concerned with translating hints. But we had to ; move it to before prove.lisp when we added hint functions, and then we had to ; move it before translate11 when we introduced flet. (defun chk-no-duplicate-defuns-cmp (lst ctx) (declare (xargs :guard (true-listp lst))) (cond ((no-duplicatesp lst) (value-cmp nil)) (t (er-cmp ctx "We do not permit duplications among the list of symbols ~ being defined. However, the symbol~#0~[ ~&0 is~/s ~&0 ~ are each~] defined more than once." (duplicates lst))))) (defun chk-no-duplicate-defuns (lst ctx state) (cmp-to-error-triple (chk-no-duplicate-defuns-cmp lst ctx))) (defun chk-state-ok-msg (wrld) ; We are in a context where 'state is a member of a list of formals. Is this ; OK? (cond ((and (not (global-val 'boot-strap-flg wrld)) (not (cdr (assoc-eq :state-ok (table-alist 'acl2-defaults-table wrld))))) (msg "The variable symbol STATE should not be used as a formal ~ parameter of a defined function unless you are aware of its ~ unusual status and the restrictions enforced on its use. See ~ :DOC set-state-ok.")) (t nil))) (defun chk-state-ok (ctx wrld state) (let ((msg (chk-state-ok-msg wrld))) (cond (msg (er soft ctx "~@0" msg)) (t (value nil))))) (defun chk-arglist-msg (args chk-state wrld) (cond ((arglistp args) (if (and chk-state (member-eq 'state args)) (chk-state-ok-msg wrld) nil)) ((not (true-listp args)) (msg "The argument list to a function must be a true list but ~x0 is ~ not." args)) (t (mv-let (culprit explan) (find-first-bad-arg args) (msg "The argument list to a function must be a true list ~ of distinct, legal variable names. ~x0 is not such ~ a list. The element ~x1 violates the rules because ~ it ~@2." args culprit explan))))) (defun msg-to-cmp (ctx msg) ; Convert a given context and message to a corresponding context-message pair ; (see the Essay on Context-message Pairs). (assert$ ctx (cond (msg (mv ctx msg)) (t (mv nil nil))))) (defun chk-arglist-cmp (args chk-state ctx wrld) (msg-to-cmp ctx (chk-arglist-msg args chk-state wrld))) (defun@par chk-arglist (args chk-state ctx wrld state) (let ((msg (chk-arglist-msg args chk-state wrld))) (cond (msg (er@par soft ctx "~@0" msg)) (t (value@par nil))))) (defun logical-name-type (name wrld quietp) ; Given a logical-namep we determine what sort of logical object it is. (cond ((stringp name) 'package) ((function-symbolp name wrld) 'function) ((getprop name 'macro-body nil 'current-acl2-world wrld) 'macro) ((getprop name 'const nil 'current-acl2-world wrld) 'const) ((getprop name 'theorem nil 'current-acl2-world wrld) 'theorem) ((not (eq (getprop name 'theory t 'current-acl2-world wrld) t)) 'theory) ((getprop name 'label nil 'current-acl2-world wrld) 'label) ((getprop name 'stobj nil 'current-acl2-world wrld) ; Warning: Non-stobjs can have the stobj property, so do not move this cond ; clause upward! 'stobj) ((getprop name 'stobj-live-var nil 'current-acl2-world wrld) 'stobj-live-var) (quietp nil) (t (er hard 'logical-name-type "~x0 is evidently a logical name but of undetermined type." name)))) (defun chk-all-but-new-name-cmp (name ctx new-type w) ; We allow new-type to be NIL. Currently, its only uses are to allow ; redefinition of functions, macros, and consts residing in the main Lisp ; package, and to allow events to use the main Lisp package when they ; do not introduce functions, macros, or constants. (cond ((not (symbolp name)) (er-cmp ctx "Names must be symbols and ~x0 is not." name)) ((keywordp name) (er-cmp ctx "Keywords, such as ~x0, may not be defined or constrained." name)) ((and (member-eq new-type '(function const stobj macro constrained-function)) (equal *main-lisp-package-name* (symbol-package-name name)) (not (global-val 'boot-strap-flg w)) (or ; Only definitions can be redefined from :program mode to :logic mode. (not (eq new-type 'function)) (not (eq (logical-name-type name w t) 'function)))) (er-cmp ctx "Symbols in the main Lisp package, such as ~x0, may not be ~ defined or constrained." name)) (t (value-cmp nil)))) (defun chk-all-but-new-name (name ctx new-type w state) (cmp-to-error-triple (chk-all-but-new-name-cmp name ctx new-type w))) (defun chk-defuns-tuples-cmp (lst local-p ctx wrld state-vars) (cond ((atom lst) ; This error message can never arise because we know terms are true ; lists. (cond ((eq lst nil) (value-cmp nil)) (t (er-cmp ctx "A list of definitions must be a true list.")))) ((not (true-listp (car lst))) (er-cmp ctx "Each~#0~[ local~/~] definition must be a true list and ~x1 ~ is not." (if local-p 0 1) (if local-p (car lst) (cons 'DEFUN (car lst))))) ((not (>= (length (car lst)) 3)) (er-cmp ctx "A definition must be given three or more arguments, but ~x0 ~ has length only ~x1." (car lst) (length (car lst)))) (t (er-progn-cmp (chk-all-but-new-name-cmp (caar lst) ctx 'function wrld) (chk-arglist-cmp (cadar lst) nil ctx wrld) (er-let*-cmp ((edcls (collect-declarations-cmp (butlast (cddar lst) 1) (cadar lst) (if local-p 'flet 'defuns) ctx wrld state-vars)) (rst (chk-defuns-tuples-cmp (cdr lst) local-p ctx wrld state-vars))) (value-cmp (cons (list* (caar lst) (cadar lst) (if (stringp (car edcls)) (car edcls) nil) (if (stringp (car edcls)) (cdr edcls) edcls) (last (car lst))) rst))))))) (defun chk-defuns-tuples (lst local-p ctx wrld state) (cmp-to-error-triple (chk-defuns-tuples-cmp lst local-p ctx wrld (default-state-vars t)))) (defun non-trivial-encapsulate-ee-entries (embedded-event-lst) (cond ((endp embedded-event-lst) nil) ((and (eq (caar embedded-event-lst) 'encapsulate) (cadar embedded-event-lst)) (cons (car embedded-event-lst) (non-trivial-encapsulate-ee-entries (cdr embedded-event-lst)))) (t (non-trivial-encapsulate-ee-entries (cdr embedded-event-lst))))) (defconst *ec-call-bad-ops* ; We are conservative here, avoiding (ec-call (fn ...)) when we are the least ; bit nervous about that. Reasons to be nervous are special treatment of a ; function symbol by guard-clauses (if) or special treatment in oneify ; (return-last and anything in *oneify-primitives*). (union-equal '(if wormhole-eval return-last) *oneify-primitives*)) (defmacro return-last-call (fn &rest args) `(fcons-term* 'return-last ',fn ,@args)) (defmacro prog2$-call (x y) `(fcons-term* 'return-last ''progn ,x ,y)) (defun name-dropper (lst) ; This function builds a term that mentions each element of lst. Provided the ; elements of list are translated terms, the output is a translated term. ; Provided every element of lst has a guard of t, the output has a guard of t. ; The intention here is that lst is a list of distinct variable names and ; name-dropper builds a translated term whose free-vars are those variables; ; futhermore, it is cheap to evaluate and always has a guard of T. ; The general form is either 'NIL, a single var, or a PROG2$ nest around ; the vars. (cond ((endp lst) *nil*) ((endp (cdr lst)) (car lst)) (t (prog2$-call (car lst) (name-dropper (cdr lst)))))) (defun first-assoc-eq (keys alist) (declare (xargs :guard (and (alistp alist) (symbol-listp keys)))) (cond ((endp keys) nil) (t (or (assoc-eq (car keys) alist) (first-assoc-eq (cdr keys) alist))))) (defun context-for-encapsulate-pass-2 (wrld in-local-flg) ; Return 'illegal if we are in pass 2 of a non-trivial encapsulate, or if known ; to be non-local (as per in-local-flg) in pass 1 of a non-trivial encapsulate. ; We include the latter because presumably it is courteous to the user to ; signal an issue during pass 1, rather than waiting till the inevitable ; problem in pass 2. ; If we are in pass 1 of a non-trivial encapsulate but in a local context, then ; we might or might not be in an illegal context for the corresponding pass 2, ; depending on whether the local wrapper is close enough to make the context ; disappear in pass 2. So we return 'maybe in this case. Otherwise, we return ; nil. (let ((ee-entries (non-trivial-encapsulate-ee-entries (global-val 'embedded-event-lst wrld)))) (and ee-entries ; we are in at least one non-trivial encapsulate (cond ((or ; The term (cddr (car ee-entries)) is true exactly when we are in pass 2 of the ; immediately superior non-trivial encapsulate, hence holds if we are in pass 2 ; of some superior encapsulate (since then we would be skipping pass 1 of its ; inferior encapsulates). So (cddr (car ee-entries)) is non-nil if and only if ; we are in pass 2 of some encapsulate. (cddr (car ee-entries)) (null in-local-flg)) 'illegal) (t 'maybe))))) (defconst *brr-globals* '(brr-monitored-runes brr-stack brr-gstack brr-alist)) (defun unknown-binding-msg-er (x ctx stobjs-bound str1 str2 str3) (mv-let (erp msg bindings) (let ((bindings nil)) ; don't-care (trans-er+ x ctx "~@0" (msg "The single-threaded object~#0~[ ~&0 has~/s ~&0 have~] been bound ~ in ~@1. It is a requirement that ~#0~[this object~/these ~ objects~] be among the outputs of ~@2. But, at the time at which ~ we process ~@2, we are unable to determine what the outputs are ~ and so cannot allow it. In the case of the admission of a clique ~ of mutually-recursive functions, this situation can arise when ~ the output of ~@2 is a call of a function defined in the clique ~ after the definition containing ~@2, in which case the problem ~ might be eliminated by rearranging the order of the definitions." stobjs-bound str1 str2 str3))) (declare (ignore bindings)) (mv erp msg :UNKNOWN-BINDINGS))) (defun congruent-stobj-rep (name wrld) (assert$ wrld ; use congruent-stobj-rep-raw if wrld is not available (or (getprop name 'congruent-stobj-rep nil 'current-acl2-world wrld) name))) (defun congruent-stobjsp (st1 st2 wrld) (eq (congruent-stobj-rep st1 wrld) (congruent-stobj-rep st2 wrld))) (defun stobjs-in-out1 (stobjs-in args known-stobjs wrld alist new-stobjs-in) ; We are translating an application of a function to args. where args satisfies ; the stobjs discipline of passing a stobj name to a stobjs-in position; see ; the comment about this in translate11-call. (cond ((endp stobjs-in) (if (null args) (mv alist (reverse new-stobjs-in)) (mv :failed nil))) ((endp args) (mv :failed nil)) ((null (car stobjs-in)) (stobjs-in-out1 (cdr stobjs-in) (cdr args) known-stobjs wrld alist (cons nil new-stobjs-in))) ((and (car stobjs-in) (stobjp (car args) known-stobjs wrld) (not (rassoc-eq (car args) alist)) ; equiv. to not member of new-stobjs-in (or (eq (car stobjs-in) (car args)) (congruent-stobjsp (car stobjs-in) (car args) wrld))) (stobjs-in-out1 (cdr stobjs-in) (cdr args) known-stobjs wrld (acons (car stobjs-in) (car args) alist) (cons (car args) new-stobjs-in))) (t (mv :failed nil)))) (defun stobjs-in-matchp (stobjs-in args) (cond ((endp stobjs-in) (null args)) ((endp args) nil) ((or (null (car stobjs-in)) (eq (car stobjs-in) (car args))) (stobjs-in-matchp (cdr stobjs-in) (cdr args))) (t nil))) (defun stobjs-in-out (fn args stobjs-out known-stobjs wrld) ; We are translating an application of fn to args, where fn has the indicated ; stobjs-out and args satisfies the stobjs discipline of passing a stobj name ; to a stobjs-in position. See the comment about this discipline in ; translate11-call. ; In general we return (mv flg new-stobjs-in new-stobjs-out), according to one ; of the following cases. ; - If flg is :failed, then we return (mv :failed stobjs-in stobjs-out), where ; stobjs-in is the stobjs-in of fn and stobjs-out is returned unchanged. ; - Otherwise flg is an alist, and either stobjs-out is a symbol (representing ; a function symbol or :stobjs-out bound in an implicit bindings in ; translate11), or else fn can be viewed as mapping new-stobjs-in to ; new-stobjs-out. Alist maps the original stobjs-in to new-stobjs-in; in ; particular, if alist is nil then new-stobjs-in is equal to the original ; stobjs-in. (let ((stobjs-in (cond ((consp fn) (compute-stobj-flags (lambda-formals fn) known-stobjs wrld)) (t (stobjs-in fn wrld))))) (cond ((stobjs-in-matchp stobjs-in args) (mv nil stobjs-in stobjs-out)) (t (mv-let (alist new-stobjs-in) (stobjs-in-out1 stobjs-in args known-stobjs wrld nil nil) (cond ((eq alist :failed) (mv :failed stobjs-in stobjs-out)) ((symbolp stobjs-out) (mv alist new-stobjs-in stobjs-out)) (t (mv alist new-stobjs-in (apply-symbol-alist alist stobjs-out nil))))))))) (defun non-trivial-stobj-binding (stobj-flags bindings) (declare (xargs :guard (and (symbol-listp stobj-flags) (symbol-doublet-listp bindings) (eql (length stobj-flags) (length bindings))))) (cond ((endp stobj-flags) nil) ((or (null (car stobj-flags)) (assert$ (eq (car stobj-flags) (caar bindings)) (eq (car stobj-flags) (cadar bindings)))) (non-trivial-stobj-binding (cdr stobj-flags) (cdr bindings))) (t (car stobj-flags)))) (defun formalized-varlistp (varlist formal-lst) (declare (xargs :guard (and (symbol-listp varlist) (pseudo-termp formal-lst)))) (cond ((endp varlist) (equal formal-lst *nil*)) ((variablep formal-lst) nil) (t (and ; (not (fquotep formal-lst)) (eq (ffn-symb formal-lst) 'cons) (eq (car varlist) (fargn formal-lst 1)) (formalized-varlistp (cdr varlist) (fargn formal-lst 2)))))) (defun throw-nonexec-error-p1 (targ1 targ2 name formals) ; Consider a term (return-last targ1 targ2 ...). We recognize when this term ; is of the form (return-last 'progn (throw-non-exec-error x ...) ...), with ; some additional requirements as explained in a comment in ; throw-nonexec-error-p. (and (quotep targ1) (eq (unquote targ1) 'progn) (nvariablep targ2) ; (not (fquotep targ2)) (eq (ffn-symb targ2) 'throw-nonexec-error) (or (null name) (let ((qname (fargn targ2 1))) (and (quotep qname) (if (eq name :non-exec) (eq (unquote qname) :non-exec) (and (eq (unquote qname) name) (formalized-varlistp formals (fargn targ2 2))))))))) (defun throw-nonexec-error-p (body name formals) ; We recognize terms that could result from translating (prog2$ ; (throw-nonexec-error x ...) ...), i.e., terms of the form (return-last 'progn ; (throw-non-exec-error x ...) ...). If name is nil, then there are no further ; requirements. If name is :non-exec, then we require that x be (quote ; :non-exec). Otherwise, we require that x be (quote name) and that the second ; argument of throw-non-exec-error be (cons v1 (cons v2 ... (cons vk nil) ; ...)), where formals is (v1 v2 ... vk). (and (nvariablep body) ; (not (fquotep body)) (eq (ffn-symb body) 'return-last) (throw-nonexec-error-p1 (fargn body 1) (fargn body 2) name formals))) (defun chk-flet-declarations (names decls declare-form ctx) (cond ((null decls) (value-cmp nil)) ((atom decls) (er-cmp ctx "The DECLARE form for an FLET expression must be a ~ true-list. The form ~x0 is thus illegal. See :DOC flet." declare-form)) (t (let ((decl (car decls))) (cond ((and (consp decl) (member-eq (car decl) '(inline notinline)) (true-listp (cdr decl)) (subsetp-eq (cdr decl) names)) (chk-flet-declarations names (cdr decls) declare-form ctx)) (t (er-cmp ctx "Each declaration in a DECLARE form of an flet ~ expression must be of the form (INLINE . fns) ~ or (NOTINLINE . fns), where fns is a true-list ~ of names that are all defined by the FLET ~ expression. The declare form ~x0 is thus ~ illegal because of its declaration, ~x1. See ~ :DOC flet." declare-form decl))))))) (defun chk-flet-declare-form (names declare-form ctx) (cond ((null declare-form) (value-cmp nil)) (t (case-match declare-form (('declare . decls) (chk-flet-declarations names decls declare-form ctx)) (& (er-cmp ctx "The optional DECLARE forms for an flet expression must each ~ be of the form (DECLARE DCL1 DCL2 ... DCLk), where each ~ DCLi is an INLINE or NOTINLINE declaration. The form ~x0 ~ is thus not a legal DECLARE form. See :DOC flet." declare-form)))))) (defun chk-flet-declare-form-list (names declare-form-list ctx) (cond ((endp declare-form-list) (value-cmp nil)) (t (er-progn-cmp (chk-flet-declare-form names (car declare-form-list) ctx) (chk-flet-declare-form-list names (cdr declare-form-list) ctx))))) (defun translate-stobj-type-to-guard (x var wrld) ; This function is a variant of translate-declaration-to-guard. Like that ; function, x is an alleged type about the variable symbol var -- think ; (DECLARE (TYPE x ...)) -- and results in an UNTRANSLATED term about var if x ; is seen to be a valid type-spec for ACL2. Unlike that function, here we ; allow x to be a stobj name, which may be used as a type in a field of another ; stobj (introduced after x). We return nil if x is not either sort of valid ; type spec. ; Our intended use of this function is in generation of guards for field ; accessors and updaters, in the case of fields that are themselves stobjs. ; When the defstobj is introduced, there are no stobj declarations, so the ; subfield's recognizer (stobj-recog, below) is called on an ordinary object. ; We started allowing such calls when translating for execution after ; Version_6.1; see translate11-call. ; Presumably the two arguments of OR below -- the guard corresponding to the ; type x, and the stobj recognizer term -- cannot both be non-nil, since types ; are in the main Lisp package and hence cannot be stobj names. Nevertheless, ; in case we're wrong about that, we translate the type first, in order to ; avoid ambiguity in case there is a stobj previously defined locally in a book ; or encapsulate, for example (which would cause x to translate as a type in ; the first pass but to a type in the second pass). (or (translate-declaration-to-guard x var wrld) (let ((stobj-recog (and (not (eq x 'state)) (cadr ; Use stobjp below, not getprop, since we do not know that x is a symbol. (stobjp x t wrld))))) (and stobj-recog (list stobj-recog var))))) (defun get-stobj-creator (stobj wrld) ; This function assumes that wrld is an ACL2 logical world, although wrld may ; be nil when we call this in raw Lisp. ; If stobj is a stobj name, return the name of its creator; else nil. We use ; the fact that the value of the 'stobj property is (*the-live-var* recognizer ; creator ...) for all user defined stobj names, is '(*the-live-state*) for ; STATE, and is nil for all other names. (cond ((eq stobj 'state) 'state-p) ((not (symbolp stobj)) nil) (wrld (caddr (getprop stobj 'stobj nil 'current-acl2-world wrld))) (t #-acl2-loop-only (let ((d (get (the-live-var stobj) 'redundant-raw-lisp-discriminator))) (cond ((eq (car d) 'defabsstobj) ; Then d is (defabstobj name . keyword-alist). (let ((tail (assoc-keyword :CREATOR d))) (cond (tail (let* ((field-descriptor (cadr tail)) (c (if (consp field-descriptor) (car field-descriptor) field-descriptor))) (assert$ (symbolp c) c))) (t (let ((name (cadr d))) (absstobj-name name :CREATOR)))))) (t (caddr d)))) #+acl2-loop-only (er hard 'stobj-creator "Implementation error: The call ~x0 is illegal, because ~ get-stobj-creator must not be called inside the ACL2 loop (as ~ is the case here) with wrld = nil." `(get-stobj-creator ,stobj nil))))) (defun stobj-updater-guess-from-accessor (accessor) ; Warning: Keep the following in sync with defstobj-fnname. ; This function guesses a stobj updater name for a field from the accessor name ; for that field. We use it to supply a reasonable default when a stobj-let ; binding does not specify an updater, but ultimately we check it just as we ; would check a supplied updater name. ; The following example shows why this is only a guess. ; (defpkg "MY-PKG" '(fldi)) ; (defstobj st (my-pkg::fld :type (array t (8)))) ; Then the accessor is ACL2::FLDI and the updater is MY-PKG::UPDATE-FLDI. But ; the call of pack-pos below, with acc bound to ACL2::FLDI, yields ; ACL2::UPDATE-FLDI. (packn-pos (list "UPDATE-" accessor) accessor)) (defun parse-stobj-let1 (bindings producer-vars bound-vars actuals stobj updaters corresp-accessor-fns) ; Either return (mv binding nil nil ... nil) for some unsuitable binding in ; bindings, or else return the result of accumulating from bindings into the ; other argments. See parse-stobj-let. Note that stobj is initially nil, but ; is bound by the first recursive call and must be the same at every ensuing ; recursive call. (declare (xargs :guard (and (true-listp bindings) (true-listp producer-vars) (true-listp bound-vars) (true-listp actuals) (true-listp updaters)))) (cond ((endp bindings) (mv nil (reverse bound-vars) (reverse actuals) stobj (reverse updaters) (reverse corresp-accessor-fns))) (t (let ((binding (car bindings))) (case-match binding ((s act . rest) ; e.g. (st1 (fld1 st+) update-fld1) (cond ((not (or (null rest) (and (consp rest) (null (cdr rest)) (symbolp (car rest))))) (mv binding nil nil nil nil nil)) ((not (and (true-listp act) (member (length act) '(2 3)) (symbolp (car act)) (symbolp (car (last act))))) (mv binding nil nil nil nil nil)) (t (let ((arrayp (eql (length act) 3))) ; e.g. (fld3i 4 st+) (cond ((and arrayp (let ((index (cadr act))) ; As discussed in the Essay on Nested Stobjs, the index must be a natural ; number or else a symbol that is not among the producer variables. We relax ; the former condition to allow a quoted natural. (not (or (and (symbolp index) (not (member-eq index producer-vars))) (natp index) (and (consp index) (consp (cdr index)) (null (cddr index)) (eq (car index) 'quote) (natp (cadr index))))))) (mv binding nil nil nil nil nil)) (t (let ((accessor (car act)) (stobj0 (car (last act))) (update-fn (car rest))) (cond ((or (null stobj0) (and stobj (not (eq stobj0 stobj)))) (mv binding nil nil nil nil nil)) ((member-eq s producer-vars) (parse-stobj-let1 (cdr bindings) producer-vars (cons s bound-vars) (cons act actuals) stobj0 (cons (cons (or update-fn (stobj-updater-guess-from-accessor accessor)) (if arrayp (list* (cadr act) ; index s (cddr act)) (cons s (cdr act)))) updaters) (cons accessor corresp-accessor-fns))) (t (parse-stobj-let1 (cdr bindings) producer-vars (cons s bound-vars) (cons act actuals) stobj0 updaters corresp-accessor-fns)))))))))) (& (mv binding nil nil nil nil nil))))))) (defun illegal-stobj-let-msg (msg form) (msg "~@0 The form ~x1 is thus illegal. See :DOC stobj-let." msg form)) (defun parse-stobj-let (x) ; This function is used both in the definition of the stobj-let macro and, in ; translate11, to translate stobj-let forms. This function is not responsible ; for all error checking, as some checks take place in translate11, which must ; ensure that x and its oneification will execute correctly. Nevertheless, the ; error checking done in this function is useful for giving feedback on misuses ; of stobj-let in contexts such as theorems in which translate11 will not ; insist on correctness for execution, such as single-threadedness. Of course, ; users who have a specific reason for "misusing" stobj-let in such contexts ; are welcome to avoid stobj-let and write let-expressions instead. ; X is a stobj-let form. We return (mv erp bound-vars actuals stobj ; producer-vars producer updaters corresponding-accessor-fns consumer), where ; erp is either a msg or nil, and when erp is nil: ; - bound-vars is a list of symbols, without duplicates; ; - actuals is a corresponding list of untranslated field accessors; ; - stobj is the stobj accessed by those field accessors; ; - producer-vars is the true-list of producer variables ; - producer is an untranslated expression that returns values corresponding to ; producer-vars; ; - updaters is a list of stobj updaters, obtained from producer-vars, actuals, ; and any updaters specified explicitly in the first argument of the ; stobj-let; ; - corresponding-accessor-fns is a list of accessor functions that corresponds ; positionally to updaters; and ; - consumer is an expression that provides the return value(s). ; For example, if x is ; (stobj-let ; ((st1 (fld1 st+)) ; (st2 (fld2 st+) update-fld2) ; (st3 (fld3i 4 st+))) ; (x st1 y st3) ; (producer st1 u st2 v st3) ; (consumer st+ u x y v w)) ; then we return: ; (mv nil ; erp ; (st1 st2 st3) ; bound-vars ; ((fld1 st+) (fld2 st+) (fld3i 4 st+)) ; untranslated actuals ; st+ ; stobj accessed above ; (x st1 y st3) ; producer-vars ; (producer st1 u st2 v st3) ; producer (untranslated) ; ((update-fld1 st+) ; stobj updaters ; (update-fld3i 4 st3 st+)) ; (fld1 fld3) ; accessors for the updaters ; (consumer st+ u x y v w) ; consumer (untranslated) ; ) (declare (xargs :guard t)) (case-match x (('stobj-let bindings producer-vars producer consumer) (cond ((not (and bindings ; We could check true-list-listp here, but we prefer to leave such a check to ; parse-stobj-let1 so that the error message can refer to the particular ; ill-formed binding. (true-listp bindings))) (mv (illegal-stobj-let-msg "The bindings of a STOBJ-LET form must be a non-empty true-list." x) nil nil nil nil nil nil nil nil)) ((not (and producer-vars (arglistp producer-vars))) (mv (illegal-stobj-let-msg "The producer-variables of a STOBJ-LET form must be a non-empty ~ list of legal variable names." x) nil nil nil nil nil nil nil nil)) (t (mv-let (bad bound-vars actuals stobj updaters corresp-accessor-fns) (parse-stobj-let1 bindings producer-vars nil nil nil nil nil) (cond (bad (mv (illegal-stobj-let-msg (msg "Illegal binding for stobj-let, ~x0." bad) x) nil nil nil nil nil nil nil nil)) (t (mv nil bound-vars actuals stobj producer-vars producer updaters corresp-accessor-fns consumer))))))) (& (mv (illegal-stobj-let-msg "The proper form of a stobj-let is (STOBJ-LET ~ )." x) nil nil nil nil nil nil nil nil)))) (defun pairlis-x1 (x1 lst) ; Cons x1 onto the front of each element of lst. (cond ((null lst) nil) (t (cons (cons x1 (car lst)) (pairlis-x1 x1 (cdr lst)))))) (defun pairlis-x2 (lst x2) ; Make an alist pairing each element of lst with x2. (cond ((null lst) nil) (t (cons (cons (car lst) x2) (pairlis-x2 (cdr lst) x2))))) (defun no-duplicatesp-checks-for-stobj-let-actuals/alist (alist) (cond ((endp alist) nil) (t (let ((indices (cdar alist))) (cond ((or (null (cdr indices)) (and (nat-listp indices) (no-duplicatesp indices))) (no-duplicatesp-checks-for-stobj-let-actuals/alist (cdr alist))) (t (cons `(chk-no-duplicatesp ; The use of reverse is just aesthetic, to preserve the original order. (list ,@(reverse indices))) (no-duplicatesp-checks-for-stobj-let-actuals/alist (cdr alist))))))))) (defun no-duplicatesp-checks-for-stobj-let-actuals (exprs alist) ; Alist associates array field accessor names with lists of index terms. (cond ((endp exprs) (no-duplicatesp-checks-for-stobj-let-actuals/alist alist)) (t (let ((expr (car exprs))) (no-duplicatesp-checks-for-stobj-let-actuals (cdr exprs) (cond ((eql (length expr) 3) ; array case, (fldi index st) (let* ((key (car expr)) (index (cadr expr)) (index (if (consp index) (assert$ (and (eq (car index) 'quote) (natp (cadr index))) (cadr index)) index)) (entry (assoc-eq key alist))) (put-assoc-eq key (cons index (cdr entry)) alist))) (t alist))))))) (defun stobj-let-fn (x) ; Warning: Keep this in sync with stobj-let-fn-raw, with the handling of ; stobj-let in translate11, and with the handling of stobj-let in oneify. ; See the Essay on Nested Stobjs. (mv-let (msg bound-vars actuals stobj producer-vars producer updaters corresp-accessor-fns consumer) (parse-stobj-let x) (declare (ignore corresp-accessor-fns)) (cond (msg (er hard 'stobj-let "~@0" msg)) (t (let* ((guarded-producer `(check-vars-not-free (,stobj) ,producer)) (guarded-consumer `(check-vars-not-free ,bound-vars ,consumer)) (updated-guarded-consumer `(let* ,(pairlis-x1 stobj (pairlis$ updaters nil)) ,guarded-consumer)) (form `(let ,(pairlis$ bound-vars (pairlis$ actuals nil)) (declare (ignorable ,@bound-vars)) ,(cond ((cdr producer-vars) `(mv-let ,producer-vars ,guarded-producer ,updated-guarded-consumer)) (t `(let ((,(car producer-vars) ,guarded-producer)) ,updated-guarded-consumer))))) (no-dups-exprs (no-duplicatesp-checks-for-stobj-let-actuals actuals nil))) `(progn$ ,@no-dups-exprs ,form)))))) (defun the-live-var (name) ; If the user declares a single-threaded object named $S then we will ; use *the-live-$s* as the Lisp parameter holding the live object ; itself. One might wonder why we don't choose to name this object ; $s? Perhaps we could, since starting with Version 2.6 we no longer ; get the symbol-value of *the-live-$s* except at the top level, ; because of local stobjs. Below we explain our earlier thinking. ; Historical Plaque for Why the Live Var for $S Is Not $S ; [Otherwise] Consider how hard it would then be to define the raw defs ; (below). $S is the formal parameter, and naturally so since we want ; translate to enforce the rules on single-threadedness. The raw code ; has to check whether the actual is the live object. We could hardly ; write (eq $S $S). (packn-pos (list "*THE-LIVE-" name "*") name)) (defun the-live-var-bindings (stobj-names) (cond ((endp stobj-names) nil) (t (cons (let ((stobj (car stobj-names))) `(,(the-live-var stobj) ,stobj)) (the-live-var-bindings (cdr stobj-names)))))) (defun the-maybe-live-var-bindings (stobj-names) (cond ((endp stobj-names) nil) (t (cons (let* ((stobj (car stobj-names)) (live-var (the-live-var stobj))) `(,live-var (if (live-stobjp ,stobj) ,stobj ,live-var))) (the-maybe-live-var-bindings (cdr stobj-names)))))) (defun stobj-let-fn-raw (x) ; Warning: Keep this in sync with stobj-let-fn and with the ; handling of stobj-let in translate11. ; See the Essay on Nested Stobjs. (mv-let (msg bound-vars actuals stobj producer-vars producer updaters corresp-accessor-fns consumer) (parse-stobj-let x) (declare (ignore stobj updaters corresp-accessor-fns)) (cond (msg (er hard 'stobj-let "~@0" msg)) (t ; Should we allow trans-eval under a stobj-let? We decided not to, for two ; reasons: first, potential user confusion over the meaning of a stobj ; reference (which in the trans-eval case is to the value in the ; *user-stobj-alist*, not to the value bound by a superior stobj-let); and ; second, difficulty in getting the implementation right! The following ; example illustrates how trans-eval would operate, were we to allow it in such ; a circumstance. Note that the trans-eval call below updates the global ; stobj, sub1, not the locally bound sub1 that is a field of top1. ; (defstobj sub1 sub1-fld1) ; (defstobj top1 (top1-fld :type sub1)) ; ; (defun f (x top1 state) ; (declare (xargs :stobjs (top1 state) :mode :program)) ; (stobj-let ; ((sub1 (top1-fld top1))) ; (sub1 state) ; (mv-let (erp val state) ; ; ; NOTE: The reference to sub1 inside the following trans-eval call is ; ; actually a reference to the global sub1 from the *user-stobj-alist*, not ; ; to the sub1 bound by stobj-let above. ; ; (trans-eval `(update-sub1-fld1 ',x sub1) 'my-top state t) ; (declare (ignore erp val)) ; (mv sub1 state)) ; top1)) ; ; (f 7 top1 state) ; (assert-event (equal (sub1-fld1 sub1) 7)) ; (f 8 top1 state) ; (assert-event (equal (sub1-fld1 sub1) 8)) ; ; (defun f2 (top1) ; (declare (xargs :stobjs top1 :mode :program)) ; (stobj-let ; ((sub1 (top1-fld top1))) ; (val) ; (sub1-fld1 sub1) ; val)) ; ; (assert-event (equal (f2 top1) nil)) ; Thus, in the code below, we bind the live var for each bound stobj so that we ; will get the error "It is illegal to run ACL2 evaluators...." when attempting ; to call trans-eval (as trans-eval calls ev-for-trans-eval, which calls ; user-stobj-alist-safe, which calls chk-user-stobj-alist, which checks the ; global *user-stobj-alist* against the live stobj values). ; Another reason to bind the-live-stobj is in case we need to print the stobj ; during guard violations or tracing, in which case we can distinguish it from ; the global stobj with the same name. See for example stobj-print-symbol, ; which is used during tracing. `(let* (,@(pairlis$ bound-vars (pairlis$ actuals nil)) ,@(the-live-var-bindings bound-vars)) (declare (ignorable ,@bound-vars)) ,(cond ((cdr producer-vars) `(mv-let ,producer-vars ,producer ,@(let ((ignore-vars (intersection-eq producer-vars bound-vars))) (and ignore-vars `((declare (ignore ,@ignore-vars))))) ,consumer)) (t `(let ((,(car producer-vars) ,producer)) ,@(and (member-eq (car producer-vars) bound-vars) `((declare (ignore ,(car producer-vars))))) ,consumer)))))))) (defun stobj-field-accessor-p (fn stobj wrld) (and ; We believe that the first check is subsumed by the others, but we leave it ; here for the sake of robustness. (eq (getprop fn 'stobj-function nil 'current-acl2-world wrld) stobj) ; The 'stobj property of stobj is (*the-live-var* recognizer creator ...). (member-eq fn (cdddr (getprop stobj 'stobj nil 'current-acl2-world wrld))) ; At this point, fn could still be a constant. (function-symbolp fn wrld) ; Now distinguish accessors from updaters. (not (eq (car (stobjs-out fn wrld)) stobj)))) (defun chk-stobj-let/bindings (stobj bound-vars actuals wrld) ; The bound-vars and actuals have been returned by parse-stobj-let, so we know ; that some basic syntactic requirements have been met and that the two lists ; have the same length. See also chk-stobj-let. (cond ((endp bound-vars) nil) (t (let* ((var (car bound-vars)) (actual (car actuals)) (accessor (car actual)) (st (car (last actual)))) (assert$ (eq st stobj) ; guaranteed by parse-stobj-let (cond ((not (stobj-field-accessor-p accessor stobj wrld)) (msg "The name ~x0 is not the name of a field accessor ~ for the stobj ~x1." accessor stobj)) ((not (stobjp var t wrld)) (msg "The stobj-let bound variable ~x0 is not the name ~ of a known single-threaded object in the current ~ ACL2 world." var)) ((not (eq (congruent-stobj-rep var wrld) (congruent-stobj-rep (car (stobjs-out accessor wrld)) wrld))) (msg "The stobj-let bound variable ~x0 is not the same ~ as, or even congruent to, the output ~x1 of accessor ~ ~x2 (of stobj ~x3)." var (car (stobjs-out (caar actuals) wrld)) (caar actuals) stobj)) (t (chk-stobj-let/bindings stobj (cdr bound-vars) (cdr actuals) wrld)))))))) (defun chk-stobj-let/updaters1 (updaters accessors lst) ; Lst is the cdddr of the 'stobj property of a stobj in an implicit world, ; accessors is a list of field accessors for that stobj, and updaters is a list ; of the same length as accessors. We check for each i < (length accessors), ; the ith updater is indeed the stobj field updater corresponding to the ith ; accessor. Recall that the 'stobj property is a list of the form ; (*the-live-var* recognizer creator ...), and that each field updater ; immediately follows the corresponding field accessor in that list. (cond ((endp updaters) nil) (t (let* ((updater-expr (car updaters)) (updater (car updater-expr)) (accessor (car accessors)) (accessor-tail (member-eq (car accessors) lst)) (actual-updater (cadr accessor-tail))) (assert$ ; This assertion should be true because of the check done by a call of ; stobj-field-accessor-p in chk-stobj-let/bindings. accessor-tail (cond ((eq updater actual-updater) (chk-stobj-let/updaters1 (cdr updaters) (cdr accessors) lst)) (t (msg "The stobj-let bindings have specified that the stobj ~ field updater corresponding to accessor ~x0 is ~x1, ~ but the actual corresponding updater is ~x2." accessor updater actual-updater)))))))) (defun chk-stobj-let/updaters (updaters corresp-accessor-fns stobj wrld) (chk-stobj-let/updaters1 updaters corresp-accessor-fns (cdddr ; optimization: pop live-var, recognizer, and creator (getprop stobj 'stobj nil 'current-acl2-world wrld)))) (defun chk-stobj-let (bound-vars actuals stobj updaters corresp-accessor-fns known-stobjs wrld) ; The inputs (other than wrld) have been returned by parse-stobj-let, so we ; know that some basic syntactic requirements have been met. Others are to be ; checked directly by translate11 after the present check passes. Here, we ; do the checks necessary after parse-stobj-let but before translate11. (cond ((not (stobjp stobj known-stobjs wrld)) (msg "The name ~x0 is not the name of a known single-threaded object in the ~ current context." stobj)) ((getprop stobj 'absstobj-info nil 'current-acl2-world wrld) (msg "The name ~x0 is the name of an abstract stobj." stobj)) ((chk-stobj-let/bindings stobj bound-vars actuals wrld)) ((chk-stobj-let/updaters updaters corresp-accessor-fns stobj wrld)) (t nil))) (defun all-nils-or-x (x lst) (declare (xargs :guard (and (symbolp x) (true-listp lst)))) (cond ((endp lst) t) ((or (eq (car lst) x) (null (car lst))) (all-nils-or-x x (cdr lst))) (t nil))) (defun stobj-field-fn-of-stobj-type-p (fn wrld) ; Return true if fn is a stobj accessor or updater for a field of stobj type. ; This is equivalent to taking or returning that stobj type, respectively, ; which is equivalent to taking or returning some stobj other than the ; stobj-function for fn (i.e., the stobj containing the field in question). (let ((st (getprop fn 'stobj-function nil 'current-acl2-world wrld))) (and st (or (not (all-nils-or-x st (stobjs-in fn wrld))) (not (all-nils-or-x st (stobjs-out fn wrld))))))) (defun stobj-recognizer-p (fn wrld) ; Fn is a function symbol of wrld. We return true when fn is a stobj ; recognizer in wrld. (let ((stobj (getprop fn 'stobj-function nil 'current-acl2-world wrld))) (and stobj (eq fn (get-stobj-recognizer stobj wrld))))) (defmacro trans-or (form1 condition form2 extra-msg) ; Like trans-er-let*, this function deals in trans-er's 3-tuples (mv erp val ; bindings). The 3-tuple produced by form1 is returned except in one case: ; that 3-tuple has non-nil first value (erp), condition is true, and form2 ; produces a 3-tuple of the form (mv nil val bindings), in which case that ; 3-tuple is returned. `(let ((trans-or-extra-msg ,extra-msg)) (mv-let (trans-or-erp trans-or-val trans-or-bindings) ,form1 (cond ((and trans-or-erp (check-vars-not-free (trans-or-er trans-or-val trans-or-bindings trans-or-extra-msg) ,condition)) (mv-let (erp val bindings) (check-vars-not-free (trans-or-er trans-or-val trans-or-bindings trans-or-extra-msg) ,form2) (cond (erp (mv trans-or-erp (msg "~@0~@1" trans-or-val trans-or-extra-msg) trans-or-bindings)) (t (mv nil val bindings))))) (t (mv trans-or-erp trans-or-val trans-or-bindings)))))) (mutual-recursion (defun translate11-flet-alist (form fives stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars) (cond ((endp fives) (trans-value flet-alist)) (t (trans-er-let* ((flet-entry (translate11-flet-alist1 form (car fives) stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars)) (flet-entries (translate11-flet-alist form (cdr fives) stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars))) (trans-value (cons flet-entry flet-entries)))))) (defun translate11-flet-alist1 (form five stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars) (let* ((name (car five)) (bound-vars (cadr five)) (edcls (fourth five)) (body (fifth five)) (new-stobjs-out (if (eq stobjs-out t) t (genvar name (symbol-name name) nil (strip-cars bindings))))) (cond ((member-eq name '(flet with-local-stobj throw-raw-ev-fncall untrace$-fn-general)) ; This check may not be necessary, because of our other checks. But the ; symbols above are not covered by our check for the 'predefined property. (trans-er+ form ctx "An FLET form has attempted to bind ~x0. However, this ~ symbol must not be FLET-bound." name)) ((getprop name 'predefined nil 'current-acl2-world wrld) (trans-er+ form ctx "An FLET form has attempted to bind ~x0, which is predefined ~ in ACL2 hence may not be FLET-bound." name)) #-acl2-loop-only ((or (special-form-or-op-p name) (and (or (macro-function name) (fboundp name)) (not (getprop name 'macro-body nil 'current-acl2-world wrld)) (eq (getprop name 'formals t 'current-acl2-world wrld) t))) (prog2$ (er hard ctx "It is illegal to FLET-bind ~x0, because it is defined as a ~ ~s1 in raw Lisp~#2~[~/ but not in the ACL2 loop~]." name (cond ((special-form-or-op-p name) "special operator") ((macro-function name) "macro") (t "function")) (if (special-form-or-op-p name) 0 1)) (mv t nil ; empty "message": see the Essay on Context-message Pairs nil))) (t (trans-er-let* ((tdcls (translate11-lst (translate-dcl-lst edcls wrld) nil ;;; '(nil ... nil) bindings known-stobjs "in a DECLARE form in an FLET binding" flet-alist form ctx wrld state-vars)) (tbody (translate11 body new-stobjs-out (if (eq stobjs-out t) bindings (translate-bind new-stobjs-out new-stobjs-out bindings)) known-stobjs flet-alist form ctx wrld state-vars))) (let ((used-vars (union-eq (all-vars tbody) (all-vars1-lst tdcls nil))) (ignore-vars (ignore-vars edcls)) (ignorable-vars (ignorable-vars edcls)) (stobjs-out (translate-deref new-stobjs-out bindings))) (cond ; We skip the following case, where stobjs-out is not yet bound to a consp and ; some formal is a stobj, in favor of the next, which removes the stobjs-bound ; criterion. But we leave this case here as a comment in case we ultimately ; find a way to eliminate the more sweeping case after it. Note: ; unknown-binding-msg has been replaced by unknown-binding-msg-er, so a bit of ; rework will be needed if this case is to be reinstalled. Also note that we ; will need to bind stobjs-bound to ; ((and (not (eq stobjs-out t)) ; (collect-non-x ; stobjs-bound ; nil ; (compute-stobj-flags bound-vars ; known-stobjs ; wrld)) ; (not (consp stobjs-out))) ; (trans-er ctx ; "~@0" ; (unknown-binding-msg ; (collect-non-x ; stobjs-bound ; nil ; (compute-stobj-flags bound-vars ; known-stobjs ; wrld)) ; (msg "the formals of an FLET binding for function ~x0" ; name) ; "the body of this FLET binding" ; "that body"))) ((and (not (eq stobjs-out t)) (not (consp stobjs-out))) ; Warning: Before changing this case, see the comment above about the ; commented-out preceding case. ; We might be able to fix this case by using the :UNKNOWN-BINDINGS trick ; employed by unknown-binding-msg-er; see that function and search for ; :UNKNOWN-BINDINGS, to see how that works. (trans-er+ form ctx "We are unable to determine the output signature for an ~ FLET-binding of ~x0. You may be able to remedy the ~ situation by rearranging the order of the branches of ~ an IF and/or rearranging the order of the presentation ~ of a clique of mutually recursive functions. If you ~ believe you have found an example on which you believe ~ ACL2 should be able to complete this translation, ~ please send such an example to the ACL2 implementors." name)) ((intersectp-eq used-vars ignore-vars) (trans-er+ form ctx "Contrary to the declaration that ~#0~[it is~/they ~ are~] IGNOREd, the variable~#0~[ ~&0 is~/s ~&0 are~] ~ used in the body of an FLET-binding of ~x1, whose ~ formal parameter list includes ~&2." (intersection-eq used-vars ignore-vars) name bound-vars)) (t (let* ((diff (set-difference-eq bound-vars (union-eq used-vars (union-eq ignorable-vars ignore-vars)))) (ignore-ok (if (null diff) t (cdr (assoc-eq :ignore-ok (table-alist 'acl2-defaults-table wrld)))))) (cond ((null ignore-ok) (trans-er+ form ctx "The variable~#0~[ ~&0 is~/s ~&0 are~] not used in ~ the body of the LET expression that binds ~&1. ~ But ~&0 ~#0~[is~/are~] not declared IGNOREd or ~ IGNORABLE. See :DOC set-ignore-ok." diff bound-vars)) (t (prog2$ (cond ((eq ignore-ok :warn) (warning$-cw1 ctx "Ignored-variables" "The variable~#0~[ ~&0 is~/s ~&0 are~] not ~ used in the body of an FLET-binding of ~x1 ~ that binds ~&2. But ~&0 ~#0~[is~/are~] not ~ declared IGNOREd or IGNORABLE. See :DOC ~ set-ignore-ok." diff name bound-vars)) (t nil)) (let* ((tbody (cond (tdcls (let ((guardian (dcl-guardian tdcls))) (cond ((equal guardian *t*) ; See the comment about THE in dcl-guardian. tbody) (t (prog2$-call guardian tbody))))) (t tbody))) (body-vars (all-vars tbody)) (extra-body-vars (set-difference-eq body-vars bound-vars))) (cond (extra-body-vars ; Warning: Do not eliminate this error without thinking about the possible role ; of variables that are declared special in Common Lisp. There might not be ; such an issue, but we haven't thought about it. (trans-er+ form ctx "The variable~#0~[ ~&0 is~/s ~&0 are~] used in ~ the body of an FLET-binding of ~x1 that only ~ binds ~&2. In ACL2, every variable occurring ~ in the body of an FLET-binding, (fn vars ~ body), must be in vars, i.e., a formal ~ parameter of that binding. The ACL2 ~ implementors may be able to remove this ~ restriction, with some effort, if you ask." extra-body-vars name bound-vars)) (t (trans-value (list* name (make-lambda bound-vars tbody) stobjs-out) (if (eq new-stobjs-out t) bindings (delete-assoc-eq new-stobjs-out bindings)))))))))))))))))) (defun translate11-flet (x stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars) (cond ((< (length x) 3) (trans-er ctx "An FLET form must have the form (flet bindings body) or (flet ~ bindings declare-form1 ... declare-formk body), but ~x0 does ~ not have this form. See :DOC flet." x)) (t (let ((defs (cadr x)) (declare-form-list (butlast (cddr x) 1)) (body (car (last x)))) (mv-let (erp fives) (chk-defuns-tuples-cmp defs t ctx wrld state-vars) (let ((names (and (not erp) (strip-cars fives)))) (mv-let (erp msg) (if erp ; erp is a ctx and fives is a msg (mv erp fives) ; Note that we do not need to call chk-xargs-keywords, since ; acceptable-dcls-alist guarantees that xargs is illegal. (er-progn-cmp (chk-no-duplicate-defuns-cmp names ctx) (chk-flet-declare-form-list names declare-form-list ctx))) (cond (erp ; Erp is a context that we are ignoring in the message below. Probably it is ; ctx anyhow, but if not, there isn't an obvious problem with ignoring it. (trans-er ctx "~@0~|~%The above error indicates a problem with the ~ form ~p1." msg x)) ((first-assoc-eq names (table-alist 'return-last-table wrld)) ; What horrors may lie ahead, for example, with ; (flet ((ec-call1-raw ....)) (ec-call ...))? The problem is that ec-call ; expands to a call of ec-call1-raw, but only through several steps that the ; user might not notice, and only in raw Lisp. Of course it's doubtful that ; someone would flet-bound ec-call1-raw; but it isn't hard to imagine a binding ; whose error isn't so obvious. Of course, someday a serious system hacker ; might want to flet ec-call1-raw; in that case, with a trust tag that person ; can also edit the code here! (trans-er ctx "It is illegal for FLET to bind a symbol that is given ~ special handling by ~x0. The FLET-binding~#1~[ is~/s ~ are~] thus illegal for ~&1. See :DOC ~ return-last-table." 'return-last (intersection-eq names (strip-cars (table-alist 'return-last-table wrld))))) (t (trans-er-let* ((flet-alist (translate11-flet-alist x fives stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars))) (translate11 body stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))))))))))) (defun translate-stobj-calls (calls len bindings known-stobjs flet-alist cform ctx wrld state-vars) ; Calls is a list of applications of stobj accessor or updater calls, as ; returned by parse-stobj-let1 and vetted by chk-stobj-let. We translate those ; applications without going through translate11, because in the case of ; updater calls, the calls update stobj fields, which is illegal except in ; proper support of a stobj-let form. ; We return a usual context-message triple: either (mv ctx erp bindings) or (mv ; nil translated-calls bindings). The only syntax changed by translation is ; in the case of an index for an array update, where len is the length of a ; call for such a case (3 for accessor calls, 4 for updater calls). (cond ((endp calls) (trans-value nil)) (t (trans-er-let* ((rest (translate-stobj-calls (cdr calls) len bindings known-stobjs flet-alist cform ctx wrld state-vars))) (let ((call (car calls))) (cond ((eql (length call) len) ; e.g. (fldi index parent-st) (trans-er-let* ((index ; We know from parse-stobj-let1 that the index is either a symbol, a natural ; number, or the quotation of a natural number. But in case we relax that ; restriction someday, and because a symbol can be a variable or a constant, we ; do not rely on that fact here. (translate11 (cadr call) '(nil) bindings known-stobjs flet-alist cform ctx wrld state-vars))) (trans-value (cons (list* (car call) index (cddr call)) rest)))) (t (trans-value (cons call rest))))))))) (defun translate11-let (x tbody0 targs stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars) ; Warning: If the final form of a translated let is changed, ; be sure to reconsider translated-acl2-unwind-protectp. ; X is a cons whose car is 'LET. If tbody0 is nil, as is the case for a ; user-supplied LET expression, then this function is nothing more than the ; restriction of function translate11 to that case. Otherwise, the LET ; expression arises from a STOBJ-LET expression, and we make the following ; exceptions: the bindings are allowed to bind more than one stobj; we suppress ; the check that a stobj bound in the LET bindings must be returned by the LET; ; tbody0 is used as the translation of the body of the LET; and targs, if ; non-nil, is used as the translation of the strip-cadrs of the bindings of the ; let, as these are assumed already to be translated. ; In translating LET and MV-LET we generate "open lambdas" as function ; symbols. The main reason we did this was to prevent translate from ; exploding in our faces when presented with typical DEFUNs (e.g., our ; own code). Note that such LAMBDAs can be expanded away. However, ; expansion affects the guards. Consider (let ((x (car 3))) t), which ; expands to ((lambda (x) t) (car 3)). (cond ((not (and (>= (length x) 3) (doubleton-list-p (cadr x)))) (trans-er ctx "The proper form of a let is (let bindings dcl ... dcl body), ~ where bindings has the form ((v1 term) ... (vn term)) and the ~ vi are distinct variables, not constants, and do not begin ~ with an asterisk, but ~x0 does not have this form." x)) ((not (arglistp (strip-cars (cadr x)))) (mv-let (culprit explan) (find-first-bad-arg (strip-cars (cadr x))) (trans-er ctx "The form ~x0 is an improper let expression because it ~ attempts to bind ~x1, which ~@2." x culprit explan))) (t (let* ((bound-vars (strip-cars (cadr x))) (multiple-bindings-p (consp (cdr bound-vars))) (stobj-flags (and (not (eq stobjs-out t)) (compute-stobj-flags bound-vars known-stobjs wrld))) (stobjs-bound (and stobj-flags ; optimization (collect-non-x nil stobj-flags)))) (cond ((and stobj-flags ; optimization (often false) multiple-bindings-p (null tbody0) (non-trivial-stobj-binding stobj-flags (cadr x))) (trans-er ctx "A single-threaded object name, such as ~x0, may be ~ LET-bound to other than itself only when it is the only ~ binding in the LET, but ~x1 binds more than one variable." (non-trivial-stobj-binding stobj-flags (cadr x)) x)) (t (mv-let (erp edcls) (collect-declarations-cmp (butlast (cddr x) 1) bound-vars 'let ctx wrld state-vars) (cond (erp (mv erp edcls bindings)) (t (trans-er-let* ((value-forms (cond (targs (trans-value targs)) ((and stobjs-bound ; hence (not (eq stobjs-out t)) (not multiple-bindings-p)) ; In this case, we know that the only variable of the LET is a stobj name. ; Note that (list (car bound-vars)) is thus a stobjs-out specifying ; a single result consisting of that stobj. (trans-er-let* ((val (translate11 (cadr (car (cadr x))) (list (car bound-vars)) bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value (list val)))) (t (translate11-lst (strip-cadrs (cadr x)) (if (eq stobjs-out t) t stobj-flags) bindings known-stobjs "in a LET binding (or LAMBDA ~ application)" flet-alist x ctx wrld state-vars)))) (tbody (if tbody0 (trans-value tbody0) (translate11 (car (last x)) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (tdcls (translate11-lst (translate-dcl-lst edcls wrld) (if (eq stobjs-out t) t nil) ;;; '(nil ... nil) bindings known-stobjs "in a DECLARE form in a LET (or LAMBDA)" flet-alist x ctx wrld state-vars))) (let ((used-vars (union-eq (all-vars tbody) (all-vars1-lst tdcls nil))) (ignore-vars (ignore-vars edcls)) (ignorable-vars (ignorable-vars edcls)) (stobjs-out (translate-deref stobjs-out bindings))) (cond ((and stobjs-bound ; hence (not (eq stobjs-out t)) (not (consp stobjs-out))) (unknown-binding-msg-er x ctx stobjs-bound "a LET" "the LET" "the LET")) ((and (null tbody0) ; else skip this check stobjs-bound ; hence (not (eq stobjs-out t)) (not multiple-bindings-p) ; possible stobj mod in bindings (not (eq (caar (cadr x)) (cadar (cadr x)))) ; stobj mod in bindings (assert$ (null (cdr stobjs-bound)) (not (member-eq (car stobjs-bound) stobjs-out)))) (let ((stobjs-returned (collect-non-x nil stobjs-out))) (trans-er+ x ctx "The single-threaded object ~x0 has been bound ~ in a LET. It is a requirement that this ~ object be among the outputs of the LET, but ~ it is not. The LET returns ~#1~[no ~ single-threaded objects~/the single-threaded ~ object ~&2~/the single-threaded objects ~&2~]." (car stobjs-bound) (zero-one-or-more stobjs-returned) stobjs-returned))) ((intersectp-eq used-vars ignore-vars) (trans-er+ x ctx "Contrary to the declaration that ~#0~[it ~ is~/they are~] IGNOREd, the variable~#0~[ ~&0 ~ is~/s ~&0 are~] used in the body of the LET ~ expression that binds ~&1." (intersection-eq used-vars ignore-vars) bound-vars)) (t (let* ((ignore-vars (augment-ignore-vars bound-vars value-forms ignore-vars)) (diff (set-difference-eq bound-vars (union-eq used-vars (union-eq ignorable-vars ignore-vars)))) (ignore-ok (if (null diff) t (cdr (assoc-eq :ignore-ok (table-alist 'acl2-defaults-table wrld)))))) (cond ((null ignore-ok) (trans-er+ x ctx "The variable~#0~[ ~&0 is~/s ~&0 are~] not ~ used in the body of the LET expression that ~ binds ~&1. But ~&0 ~#0~[is~/are~] not ~ declared IGNOREd or IGNORABLE. See :DOC ~ set-ignore-ok." diff bound-vars)) (t (prog2$ (cond ((eq ignore-ok :warn) (warning$-cw1 ctx "Ignored-variables" "The variable~#0~[ ~&0 is~/s ~&0 are~] ~ not used in the body of the LET ~ expression that binds ~&1. But ~&0 ~ ~#0~[is~/are~] not declared IGNOREd ~ or IGNORABLE. See :DOC set-ignore-ok." diff bound-vars)) (t nil)) (let* ((tbody (cond (tdcls (let ((guardian (dcl-guardian tdcls))) (cond ((equal guardian *t*) ; See the comment about THE in dcl-guardian. tbody) (t (prog2$-call guardian tbody))))) (t tbody))) (body-vars (all-vars tbody)) (extra-body-vars (set-difference-eq body-vars bound-vars))) (trans-value (cons (make-lambda (append bound-vars extra-body-vars) tbody) ; See the analogous line in the handling of MV-LET for an explanation ; of hide-ignored-actuals. (append (hide-ignored-actuals ignore-vars bound-vars value-forms) extra-body-vars))))))))))))))))))))) (defun translate11-let* (x tbody targs stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars) ; This function is analogous to translate11-let, but it is for let* instead of ; let and here we assume no declarations. Thus, x is (let* ((var1 arg1) (vark ; ... argk)) body), where targs is the list of translations of arg1, ..., argk ; and tbody is the translation of body. Note that unlike translate11-let, here ; tbody and targs are not optional. (cond ((endp targs) (trans-value tbody)) (t (case-match x (('let* (pair . pairs) y) (let ((body0 `(let* ,pairs ,y))) (trans-er-let* ((tbody0 (translate11-let* body0 tbody (cdr targs) stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars))) (translate11-let `(let (,pair) ,body0) tbody0 (list (car targs)) stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars)))) (& (trans-er+ x ctx "Implementation error: Unexpected form for ~x0." 'translate11-let*)))))) (defun translate11-mv-let (x tbody0 stobjs-out bindings known-stobjs local-stobj local-stobj-creator flet-alist ctx wrld state-vars) ; X is a cons whose car is 'MV-LET. This function is nothing more than the ; restriction of function translate11 to that case, with two exceptional cases: ; if tbody0 is not nil, then it is to be used as the translation of the body of ; the MV-LET, and we suppress the check that a stobj bound by MV-LET must be ; returned by the MV-LET; and if local-stobj is not nil, then we are in the ; process of translating (with-local-stobj local-stobj x local-stobj-creator), ; where we know that local-stobj-creator is the creator function for the stobj ; local-stobj. ; Warning: If the final form of a translated mv-let is changed, be sure to ; reconsider translated-acl2-unwind-protectp. (cond ((not (and (true-listp (cadr x)) (> (length (cadr x)) 1))) (trans-er ctx "The first form in an MV-LET expression must be a true list of ~ length 2 or more. ~x0 does not meet these conditions." (cadr x))) ((not (arglistp (cadr x))) (mv-let (culprit explan) (find-first-bad-arg (cadr x)) (trans-er ctx "The first form in an MV-LET expression must be a list ~ of distinct variables of length 2 or more, but ~x0 ~ does not meet these conditions. The element ~x1 ~@2." x culprit explan))) ((not (>= (length x) 4)) (trans-er ctx "An MV-LET expression has the form (mv-let (var var var*) form ~ dcl* form) but ~x0 does not have sufficient length to meet ~ this condition." x)) (t (let* ((bound-vars (cadr x)) (producer-known-stobjs (if (and local-stobj (not (eq known-stobjs t))) (add-to-set-eq local-stobj known-stobjs) known-stobjs)) (bound-stobjs-out (if (and (eq stobjs-out t) ; If local-stobj is true (hence we are being called by translate in the case of ; a with-local-stobj term), then we want to do syntax-checking that we wouldn't ; normally do with stobjs-out = t, because we don't have a spec for ; with-local-stobj in the case that this syntax-checking is turned off. (not local-stobj)) t (compute-stobj-flags bound-vars producer-known-stobjs wrld))) (stobjs-bound0 (if (eq bound-stobjs-out t) nil (collect-non-x nil bound-stobjs-out))) (stobjs-bound ; Stobjs-bound is perhaps an odd name for this variable, since if there is a ; local stobj, then literally speaking it is bound -- though we do not consider ; it so here. Really, stobjs-bound is the list of stobj names that we require ; to come out of the mv-let. (if local-stobj (remove1-eq local-stobj stobjs-bound0) stobjs-bound0))) (mv-let (erp edcls) (collect-declarations-cmp (butlast (cdddr x) 1) (cadr x) 'mv-let ctx wrld state-vars) (cond (erp ; erp is a ctx and edcls is a msg (trans-er erp "~@0" edcls)) (t (trans-er-let* ((tcall (translate11 (caddr x) bound-stobjs-out bindings producer-known-stobjs flet-alist x ctx wrld state-vars)) (tdcls (translate11-lst (translate-dcl-lst edcls wrld) (if (eq stobjs-out t) t nil) ;;; '(nil ... nil) bindings known-stobjs "in a DECLARE form in an MV-LET" flet-alist x ctx wrld state-vars)) (tbody (if tbody0 (trans-value tbody0) (translate11 (car (last x)) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars)))) (let ((used-vars (union-eq (all-vars tbody) (all-vars1-lst tdcls nil))) (ignore-vars (if local-stobj (cons local-stobj (ignore-vars edcls)) (ignore-vars edcls))) (ignorable-vars (ignorable-vars edcls)) (stobjs-out (translate-deref stobjs-out bindings))) (cond ((and local-stobj (not (member-eq local-stobj ignore-vars))) (trans-er+ x ctx "A local-stobj must be declared ignored, but ~x0 is ~ not. See :DOC with-local-stobj." local-stobj)) ((and stobjs-bound (not (consp stobjs-out))) (unknown-binding-msg-er x ctx stobjs-bound "an MV-LET" "the MV-LET" "the MV-LET")) ((and stobjs-bound (null tbody0) ; else skip this check (not (subsetp stobjs-bound (collect-non-x nil stobjs-out)))) (let ((stobjs-returned (collect-non-x nil stobjs-out))) (trans-er+ x ctx "The single-threaded object~#0~[ ~&0 has~/s ~&0 ~ have~] been bound in an MV-LET. It is a ~ requirement that ~#0~[this object~/these ~ objects~] be among the outputs of the MV-LET, but ~ ~#0~[it is~/they are~] not. The MV-LET returns ~ ~#1~[no single-threaded objects~/the ~ single-threaded object ~&2~/the single-threaded ~ objects ~&2~]." (set-difference-eq stobjs-bound stobjs-returned) (zero-one-or-more stobjs-returned) stobjs-returned))) ((intersectp-eq used-vars ignore-vars) (trans-er+ x ctx "Contrary to the declaration that ~#0~[it is~/they ~ are~] IGNOREd, the variable~#0~[ ~&0 is~/s ~&0 ~ are~] used in the MV-LET expression that binds ~&1." (intersection-eq used-vars ignore-vars) bound-vars)) (t (let* ((diff (set-difference-eq bound-vars (union-eq used-vars (union-eq ignorable-vars ignore-vars)))) (ignore-ok (if (null diff) t (cdr (assoc-eq :ignore-ok (table-alist 'acl2-defaults-table wrld)))))) (cond ((null ignore-ok) (trans-er+ x ctx "The variable~#0~[ ~&0 is~/s ~&0 are~] not used ~ in the body of the MV-LET expression that binds ~ ~&1. But ~&0 ~#0~[is~/are~] not declared ~ IGNOREd or IGNORABLE. See :DOC set-ignore-ok." diff bound-vars)) (t (prog2$ (cond ((eq ignore-ok :warn) (warning$-cw1 ctx "Ignored-variables" "The variable~#0~[ ~&0 is~/s ~&0 are~] not ~ used in the body of the MV-LET expression ~ that binds ~&1. But ~&0 ~#0~[is~/are~] ~ not declared IGNOREd or IGNORABLE. See ~ :DOC set-ignore-ok." diff bound-vars)) (t nil)) (let* ((tbody (cond (tdcls (let ((guardian (dcl-guardian tdcls))) (cond ((equal guardian *t*) ; See the comment about THE in dcl-guardian. tbody) (t (prog2$-call guardian tbody))))) (t tbody))) (body-vars (all-vars tbody)) (extra-body-vars (set-difference-eq body-vars (cadr x))) (vars (all-vars1 tcall extra-body-vars)) (mv-var (genvar 'genvar "MV" nil vars))) (trans-value (list* (make-lambda (cons mv-var extra-body-vars) (cons (make-lambda (append (cadr x) extra-body-vars) tbody) ; When the rewriter encounters ((lambda (... xi ...) body) ... actuali ; ...), where xi is ignored and actuali is in the corresponding ; position, we'd like to tell the rewriter not to bother rewriting ; actuali. We do this by wrapping a hide around it. This typically ; only happens with MV-LET expressions, though we do it for LET ; expressions as well. (append (hide-ignored-actuals ignore-vars (cadr x) (mv-nth-list mv-var 0 (length (cadr x)))) extra-body-vars))) (if local-stobj (let ((tcall-vars (remove1-eq local-stobj (all-vars tcall)))) (cons (make-lambda (cons local-stobj tcall-vars) tcall) (cons (list local-stobj-creator) tcall-vars))) tcall) extra-body-vars)))))))))))))))))) (defun translate11-wormhole-eval (x y z bindings flet-alist ctx wrld state-vars) ; The three arguments of wormhole-eval are x y and z. Here, z has been ; translated but x and y have not been. We want to insure that x and y are ; well-formed quoted forms of a certain shape. We don't actually care about z ; and ignore it! We translated it just for sanity's sake: no point in allowing ; the user ever to write an ill-formed term in a well-formed term. (declare (ignore z)) (cond ((not (and (true-listp x) (equal (length x) 2) (equal (car x) 'quote))) (trans-er ctx "The first argument to wormhole-eval must be a QUOTE expression ~ containing the name of the wormhole in question and ~x0 is not ~ quoted." x)) ((not (and (true-listp y) (equal (length y) 2) (equal (car y) 'quote))) (trans-er ctx "The second argument to wormhole-eval must be a QUOTE ~ expression containing a LAMBDA expression and ~x0 is not ~ quoted." y)) ((not (and (true-listp (cadr y)) (equal (length (cadr y)) 3) (equal (car (cadr y)) 'lambda) (true-listp (cadr (cadr y))) (<= (length (cadr (cadr y))) 1))) (trans-er ctx "The second argument to wormhole-eval must be a QUOTE ~ expression containing a LAMBDA expression with at most one ~ formal, e.g., the second argument must be either of the form ~ '(LAMBDA () body) or of the form (LAMBDA (v) body). But ~x0 ~ is of neither form." y)) (t (let ((lambda-formals (cadr (cadr y))) (lambda-body (caddr (cadr y)))) (cond ((not (arglistp lambda-formals)) (mv-let (culprit explan) (find-first-bad-arg lambda-formals) (trans-er ctx "The quoted lambda expression, ~x0, supplied to ~ wormhole-eval is improper because it binds ~x1, ~ which ~@2." y culprit explan))) (t (let ((whs (car lambda-formals))) ; Whs is either nil or the legal variable name bound by the lambda. (mv-let (body-erp tlambda-body body-bindings) (translate11 lambda-body '(nil) ; stobjs-out nil '(state) ; known-stobjs flet-alist x ctx wrld state-vars) (declare (ignore body-bindings)) (cond (body-erp (mv body-erp tlambda-body bindings)) ((and whs (not (member-eq whs (all-vars tlambda-body)))) (trans-er ctx "The form ~x0 is an improper quoted lambda ~ expression for wormhole-eval because it binds but ~ does not use ~x1, which is understood to be the ~ name you're giving to the current value of the ~ wormhole status for the wormhole in question." y whs)) (t ; We replace the second argument of wormhole-eval by a possibly different ; quoted object. But that is ok because wormhole-eval returns nil no matter ; what objects we pass it. We also compute a form with the same free vars as ; the lambda expression and stuff it in as the third argument, throwing away ; whatever the user supplied. (trans-value (fcons-term* 'wormhole-eval x (list 'quote (if whs `(lambda (,whs) ,tlambda-body) `(lambda () ,tlambda-body))) (name-dropper (if whs (remove1-eq whs (all-vars tlambda-body)) (all-vars tlambda-body))))))))))))))) (defun translate11-call (form fn args stobjs-out stobjs-out2 bindings known-stobjs msg flet-alist ctx wrld state-vars) ; We are translating (for execution, not merely theorems) a call of fn on args. ; Stobjs-out and stobjs-out2 are respectively the expected stobjs-out from the ; present context and the stobjs-out from fn, already dereferenced. Note that ; each of these is either a legitimate (true-list) stobjs-out or else a symbol ; representing an unknown stobjs-out. ; Note that for this call to be suitable, args has to satisfy the stobjs ; discipline of passing a stobj name to a stobjs-in position. We take ; advantage of this point in the call of stobjs-in-out below, where we ; ultimately work our way down to checking stobjp of each arg in ; stobjs-in-out1. For that reason, it is good that we do not call ; translate11-call on arbitrary let expressions, viewed as lambdas, where the ; args would be arbitrary expressions (from the cadrs of the doublets in the ; let-bindings). (mv-let (flg stobjs-in-call stobjs-out-call) (stobjs-in-out fn args stobjs-out2 known-stobjs wrld) ; If flg is :failed, then stobjs-in-call and stobjs-out-call are just the ; stobjs-in and (dereferenced) stobjs-out of fn. In that case we proceed ; happily without any mapping of input stobjs, expecting the usual ; input-mismatch error from a failed call of translate11-lst. (cond ((consp stobjs-out) (cond ((consp stobjs-out-call) ; equivalently: (consp stobjs-out2) (cond ((not (equal stobjs-out stobjs-out-call)) (trans-er+ form ctx "It is illegal to invoke ~@0 here because of a signature ~ mismatch. This function call returns a result of shape ~ ~x1~@2 where a result of shape ~x3 is required." (if (consp fn) msg (msg "~x0" fn)) (prettyify-stobjs-out stobjs-out-call) (if (and flg (not (eq flg :failed))) " (after accounting for the replacement of some input ~ stobjs by congruent stobjs)" "") (prettyify-stobjs-out stobjs-out))) (t (trans-er-let* ; We handle the special translation of wormhole-eval both here, when stobjs-out ; is known, and below, where it is not. Of course, stobjs-out2 (for ; wormhole-eval) is fixed: (nil). Keep this code in sync with that below. ; The odd treatment of wormhole-eval's first two arguments below is due to the ; fact that we actually don't want to translate them. We will insist that they ; actually be quoted forms, not macro calls that expand to quoted forms. So we ; put bogus nils in here and then swap back the untranslated args below. ((targs (trans-or (translate11-lst (if (eq fn 'wormhole-eval) (list *nil* *nil* (nth 2 args)) args) stobjs-in-call bindings known-stobjs msg flet-alist form ctx wrld state-vars) ; Just below, we allow a stobj recognizer to be applied to an ordinary object, ; even when translating for execution (function bodies or top-level loop). ; This is an exception to the the usual rule, which requires stobj functions to ; respect their stobjs-in arguments when translating for execution. We take ; advantage of this exception in our support for stobj fields of stobjs. For ; example, consider the following two events. ; (defstobj sub1 sub1-fld1) ; (defstobj top1 (top1-fld :type sub1)) ; The axiomatic definition generated in the second defstobj for function ; top1-fldp is as follows. ; (defun top1-fldp (x) ; (declare (xargs :guard t :verify-guards t) ; (ignorable x)) ; (sub1p x)) ; At this point, x is an ordinary object; only at the conclusion of a defstobj ; event do we put stobjs-in and stobjs-out properties for the new functions. ; By allowing sub1p to be applied to an ordinary object, we allow the ; definition to be accepted without any (other) special treatment. (and (eq flg :failed) (stobj-recognizer-p fn wrld)) (translate11-lst args '(nil) bindings known-stobjs msg flet-alist form ctx wrld state-vars) (msg " Observe that while it is permitted to apply ~x0 ~ to an ordinary object, this stobj recognizer must ~ not be applied to the wrong stobj." fn)))) (cond ((eq fn 'wormhole-eval) (translate11-wormhole-eval (car args) (cadr args) (caddr targs) bindings flet-alist ctx wrld state-vars)) (t (trans-value (fcons-term fn targs)))))))) (t ; (symbolp stobjs-out2); equivalently, (symbolp stobjs-out-call) ; If flg is a non-empty alist, then the expected stobjs-out is not the ; stobjs-out to be returned by fn on arguments satisfying its declared ; signature. For example, suppose that st1 and st2 are congruent stobjs; ; stobjs-out is (st2); fn is f; f has input signature (st1); and args is (st2), ; i.e., we are considering the call (f st2). Then flg is ((st1 . st2)). We ; apply the mapping, flg, in reverse to stobjs-out = (st2), to deduce that the ; stobjs-out of fn is (st1) -- the point is that if we apply flg to (st1), then ; we get the expected stobjs-out of (st2). (let ((bindings (translate-bind stobjs-out2 (if (consp flg) (apply-inverse-symbol-alist flg stobjs-out nil) stobjs-out) bindings))) (trans-er-let* ((args (translate11-lst args stobjs-in-call bindings known-stobjs msg flet-alist form ctx wrld state-vars))) (trans-value (fcons-term fn args))))))) ((consp stobjs-out-call) ; equivalently: (consp stobjs-out2) (let ((bindings (translate-bind stobjs-out stobjs-out-call bindings))) (trans-er-let* ((targs (trans-or (translate11-lst (if (eq fn 'wormhole-eval) (list *nil* *nil* (nth 2 args)) args) stobjs-in-call bindings known-stobjs msg flet-alist form ctx wrld state-vars) ; See the comment above about applying a stobj recognizer to be applied to an ; ordinary object. (and (eq flg :failed) (stobj-recognizer-p fn wrld)) (translate11-lst args '(nil) bindings known-stobjs msg flet-alist form ctx wrld state-vars) (msg " Observe that while it is permitted to apply ~x0 to ~ an ordinary object, this stobj recognizer must not be ~ applied to the wrong stobj." fn)))) (cond ((eq fn 'wormhole-eval) (translate11-wormhole-eval (car args) (cadr args) (caddr targs) bindings flet-alist ctx wrld state-vars)) (t (trans-value (fcons-term fn targs))))))) (t (let ((bindings ; If the stobjs-in of fn are compatible with args, but only when mapping at ; least one input stobj to a congruent stobj (i.e., flg is a non-empty alist), ; then we cannot simply bind stobjs-out2 to stobjs-out. For example, suppose ; st1 and st2 are congruent stobjs and we are defining a function (f st1 st2) ; in a context where we do not know the expected result signature, i.e., ; stobjs-out is a symbol, nor do we know the stobjs-out of f, which could for ; example be (st1 st2) or (st2 st1). (Is this example even possible? Not ; sure, so let's continue....) If we are looking at a call (f st2 st1), then ; we can actually be certain that the call does _not_ return the output ; signature of f! (if (consp flg) bindings (translate-bind stobjs-out2 stobjs-out bindings)))) (trans-er-let* ((args (translate11-lst args stobjs-in-call bindings known-stobjs msg flet-alist form ctx wrld state-vars))) (trans-value (fcons-term fn args)))))))) (defun translate11 (x stobjs-out bindings known-stobjs flet-alist cform ctx wrld state-vars) ; Bindings is an alist binding symbols either to their corresponding ; STOBJS-OUT or to symbols. The only symbols used are (about-to-be ; introduced) function symbols or the keyword :STOBJS-OUT. When fn is ; bound to gn it means we have determined that the STOBJS-OUT of fn is ; that of gn. We allow fn to be bound to itself -- indeed, it is ; required initially! (This allows bindings to double as a recording ; of all the names currently being introduced.) ; Stobjs-out is one of: ; t - meaning we do not care about multiple-value or stobj ; restrictions (as when translating proposed theorems). ; (s1 s2 ... sk) - a list of 1 or more stobj flags indicating where stobjs ; are returned in the translation of x ; fn - a function name, indicating that we are trying to deduce ; the stobjs-out setting for fn from some output branch, x, ; of its body, as we translate. We also enforce prohibitions ; against the use of DEFUN, IN-PACKAGE, etc inside bodies. ; :stobjs-out - like a function name, except we know we are NOT in a defun ; body and allow DEFUN, IN-PACKAGE, etc., but restrict certain ; calls of return-last. ; See the essay on STOBJS-IN and STOBJS-OUT, above. ; When stobjs-out is a symbol, it must be dereferenced through bindings ; before using it. [One might think that we follow the convention of keeping ; it dereferenced, e.g., by using the new value whenever we bind it. ; But that is hard since the binding may come deep in some recursive ; call of translate.] ; T always deferences to t and nothing else dereferences to t. So you ; can check (eq stobjs-out t) without dereferencing to know whether we ; care about the stobjs-out conditions. ; Known-stobjs is a subset of the list of all stobjs known in world wrld (but ; may contain some NIL elements, to be ignored; see "slight abuse" comment in ; chk-acceptable-defuns1) or else known-stobjs is T and denotes all the stobjs ; in wrld. A name is considered a stobj iff it is in known-stobjs. This ; allows us to implement the :STOBJS declaration in defuns, by which the user ; can declare the stobjs in a function. ; The cform argument is a form that provides context -- it is the one to be ; printed by trans-er+ when there isn't another obvious contextual form to ; print. (Often x carries enough context.) ; Keep this in sync with oneify. (cond ((or (atom x) (eq (car x) 'quote)) ; We handle both the (quote x) and atom case together because both ; have the same effects on calculating the stobjs-out. (let* ((stobjs-out (translate-deref stobjs-out bindings)) (vc (legal-variable-or-constant-namep x)) (const (and (eq vc 'constant) (defined-constant x wrld)))) (cond ((and (symbolp x) (not (keywordp x)) (not vc)) (trans-er+? cform x ctx "The symbol ~x0 is being used as a variable or constant ~ symbol but does not have the proper syntax. Such names ~ must ~@1. See :DOC name." x (tilde-@-illegal-variable-or-constant-name-phrase x))) ((and (eq vc 'constant) (not const)) (trans-er+? cform x ctx "The symbol ~x0 (in package ~x1) has the syntax of a ~ constant, but has not been defined." x (symbol-package-name x))) ((and (not (atom x)) (not (termp x wrld))) (trans-er+? cform x ctx "The proper form of a quoted constant is (quote x), but ~ ~x0 is not of this form." x)) ; We now know that x denotes a term. Let transx be that term. (t (let ((transx (cond ((keywordp x) (kwote x)) ((symbolp x) (cond ((eq vc 'constant) const) (t x))) ((atom x) (kwote x)) (t x)))) ; Now consider the specified stobjs-out. It is fully dereferenced. ; So there are three cases: (1) we don't care about stobjs-out, (2) ; stobjs-out tells us exactly what kind of output is legal here and we ; must check, or (3) stobjs-out is an unknown but we now know its ; value and can bind it. (cond ((eq stobjs-out t) ;;; (1) (trans-value transx)) ((consp stobjs-out) ;;; (2) (cond ((cdr stobjs-out) (trans-er+? cform x ctx "One value, ~x0, is being returned where ~x1 ~ values were expected." x (length stobjs-out))) ((and (null (car stobjs-out)) (stobjp transx known-stobjs wrld)) (trans-er+? cform x ctx "A single-threaded object, namely ~x0, is being ~ used where an ordinary object is expected." transx)) ((and (car stobjs-out) (not (eq (car stobjs-out) transx))) (cond ((stobjp transx known-stobjs wrld) (trans-er+? cform x ctx "The single-threaded object ~x0 is being used ~ where the single-threaded object ~x1 was ~ expected." transx (car stobjs-out))) (t (trans-er+? cform x ctx "The ordinary object ~x0 is being used where ~ the single-threaded object ~x1 was expected." transx (car stobjs-out))))) (t (trans-value transx)))) (t ;;; (3) (trans-value transx (translate-bind stobjs-out (list (if (stobjp transx known-stobjs wrld) transx nil)) bindings))))))))) ((not (true-listp (cdr x))) (trans-er ctx "Function applications in ACL2 must end in NIL. ~x0 is ~ not of this form." x)) ((not (symbolp (car x))) (cond ((or (not (consp (car x))) (not (eq (caar x) 'lambda))) (trans-er ctx "Function applications in ACL2 must begin with a ~ symbol or LAMBDA expression. ~x0 is not of ~ this form." x)) ((or (not (true-listp (car x))) (not (>= (length (car x)) 3)) (not (true-listp (cadr (car x))))) (trans-er ctx "Illegal LAMBDA expression: ~x0." x)) ((not (= (length (cadr (car x))) (length (cdr x)))) (trans-er+ x ctx "The LAMBDA expression ~x0 takes ~#1~[no arguments~/1 ~ argument~/~x2 arguments~] and is being passed ~#3~[no ~ arguments~/1 argument~/~x4 arguments~]." (car x) (zero-one-or-more (length (cadr (car x)))) (length (cadr (car x))) (zero-one-or-more (length (cdr x))) (length (cdr x)))) (t (translate11 (list* 'let (listlis (cadr (car x)) (cdr x)) (cddr (car x))) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars)))) ((and (not (eq stobjs-out t)) (eq (car x) 'mv)) ; If stobjs-out is t we let normal macroexpansion handle mv. (let ((stobjs-out (translate-deref stobjs-out bindings))) (cond ((let ((len (length (cdr x)))) (or (< len 2) (> len ; Keep the number below (which also occurs in the string) equal to the value of ; raw Lisp constant *number-of-return-values*. 32))) (cond ((< (length (cdr x)) 2) (trans-er ctx "MV must be given at least two arguments, but ~x0 has ~ fewer than two arguments." x)) (t (trans-er ctx "MV must be given no more than 32 arguments; thus ~x0 ~ has too many arguments." x)))) ((consp stobjs-out) (cond ((not (int= (length stobjs-out) (length (cdr x)))) (trans-er+? cform x ctx "The expected number of return values for ~x0 is ~x1 ~ but the actual number of return values is ~x2." x (length stobjs-out) (length (cdr x)))) (t (trans-er-let* ((args (translate11-lst (cdr x) stobjs-out bindings known-stobjs 'mv flet-alist x ctx wrld state-vars))) (trans-value (listify args)))))) (t (let* ((new-stobjs-out (compute-stobj-flags (cdr x) known-stobjs wrld)) (bindings (translate-bind stobjs-out new-stobjs-out bindings))) ; When we compute new-stobjs-out, above, we do with untranslated ; terms. The stobj slots of an mv must be occupied by stobj variable ; names! If a slot is occupied by anything else, the occupant must be ; a single non-stobj. (cond ((not (no-duplicatesp (collect-non-x nil new-stobjs-out))) (trans-er ctx "It is illegal to return more than one ~ reference to a given single-threaded object ~ in an MV form. The form ~x0 is thus illegal." x)) (t (trans-er-let* ((args (translate11-lst (cdr x) new-stobjs-out bindings known-stobjs 'mv flet-alist x ctx wrld state-vars))) (trans-value (listify args)))))))))) ((eq (car x) 'mv-let) (translate11-mv-let x nil stobjs-out bindings known-stobjs nil nil ; stobj info flet-alist ctx wrld state-vars)) ((assoc-eq (car x) flet-alist) ; The lambda-bodies in flet-alist are already translated. Our approach is to ; consider a call of an flet-bound function symbol to be a call of the lambda ; to which it is bound in flet-alist. (let* ((entry (assoc-eq (car x) flet-alist)) (lambda-fn (cadr entry)) (formals (lambda-formals lambda-fn)) (stobjs-out (translate-deref stobjs-out bindings)) (stobjs-out2 (translate-deref (cddr entry) bindings))) (cond ((not (eql (length formals) (length (cdr x)))) (trans-er ctx "FLET-bound local function ~x0 takes ~#1~[no ~ arguments~/1 argument~/~x2 arguments~] but in the ~ call ~x3 it is given ~#4~[no arguments~/1 ~ argument~/~x5 arguments~]. The formal parameters ~ list for the applicable FLET-binding of ~x0 is ~X67." (car x) (zero-one-or-more (length formals)) (length formals) x (zero-one-or-more (length (cdr x))) (length (cdr x)) formals nil)) (t (translate11-call x lambda-fn (cdr x) stobjs-out stobjs-out2 bindings known-stobjs (msg "a call of FLET-bound function ~x0" (car x)) flet-alist ctx wrld state-vars))))) ((and bindings (not (eq (caar bindings) :stobjs-out)) (member-eq (car x) '(defun defmacro in-package progn defpkg))) (trans-er+ x ctx "We do not permit the use of ~x0 inside of code to be executed ~ by Common Lisp because its Common Lisp meaning differs from ~ its ACL2 meaning." (car x))) ((and bindings (not (eq (caar bindings) :stobjs-out)) (member-eq (car x) ; The following list should contain every symbol listed in ; primitive-event-macros for which the error message below applies. We keep ; both lists alphabetical to make it convenient to compare them. For ; efficiency, we may omit those that will ultimately expand to calls of table ; (or any other symbol in the list below). We also omit those handled in the ; previous case, above, such as defun. '( #+:non-standard-analysis defthm-std #+:non-standard-analysis defun-std add-custom-keyword-hint add-include-book-dir ; definition explains inclusion here add-match-free-override certify-book comp defattach defaxiom defchoose defconst defdoc deflabel defstobj defabsstobj deftheory defthm defuns delete-include-book-dir ; definition explains inclusion encapsulate in-arithmetic-theory in-theory include-book local ; note: not in (primitive-event-macros) make-event ; note: not in (primitive-event-macros) mutual-recursion progn! push-untouchable regenerate-tau-database remove-untouchable reset-prehistory set-body set-override-hints-macro table theory-invariant value-triple verify-guards with-output ; note: not in (primitive-event-macros) with-prover-step-limit ; not in (primitive-event-macros) verify-termination-boot-strap ))) (trans-er+ x ctx "We do not permit the use of ~x0 inside of code to be executed ~ by Common Lisp because its Common Lisp runtime value and ~ effect differs from its ACL2 meaning." (car x))) ((and (eq (car x) 'pargs) (true-listp x) (member (length x) '(2 3)) ; Notice that we are restricting this error case to a pargs that is ; syntactically well-formed, in the sense that if this pargs has one or two ; arguments, then the form argument is a function call. The rest of the ; well-formedness checking will be done during macro expansion of pargs; by ; making the above restriction, we avoid the possibility that the error message ; below is confusing. (let ((form (car (last x)))) ; should be a function call (or flet-alist (not (and (consp form) (symbolp (car form)) (function-symbolp (car form) wrld)))))) (cond (flet-alist ; It may be fine to have flet-bound functions as in: ; (defun g () ; (flet ((foo (x) (+ x x))) ; (pargs (h (foo 3))))) ; But we haven't thought through whether closures really respect superior FLET ; bindings, so for now we simply punt. (trans-er+ x ctx "~x0 may not be called in the scope of ~x1. If you want ~ support for that capability, please contact the ACL2 ~ implementors." 'pargs 'flet)) (t (let ((form (car (last x)))) (trans-er+ x ctx "~x0 may only be used when its form argument is a function ~ call, unlike the argument ~x1.~@2 See :DOC pargs." 'pargs form (if (and (consp form) (symbolp (car form)) (getprop (car form) 'macro-body nil 'current-acl2-world wrld)) (list " Note that ~x0 is a macro, not a function symbol." (cons #\0 (car form))) "")))))) ((eq (car x) 'translate-and-test) (cond ((not (equal (length x) 3)) (trans-er+ x ctx "TRANSLATE-AND-TEST requires exactly two arguments.")) (t (trans-er-let* ((ans (translate11 (caddr x) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) ; The next mv-let is spiritually just a continuation of the trans-er-let* ; above, as though to say "and let test-term be (translate11 (list ...)...)" ; except that we do not want to touch the current setting of bindings nor ; do we wish to permit the current bindings to play a role in the translation ; of the test. (mv-let (test-erp test-term test-bindings) (translate11 (list (cadr x) 'form) '(nil) nil known-stobjs flet-alist x ctx wrld state-vars) (declare (ignore test-bindings)) (cond (test-erp (mv test-erp test-term bindings)) (t (mv-let (erp msg) (ev-w test-term (list (cons 'form ans) (cons 'world wrld)) wrld nil ; user-stobj-alist (access state-vars state-vars :safe-mode) (gc-off1 (access state-vars state-vars :guard-checking-on)) nil ; We are conservative here, using nil for the following AOK argument in case ; the intended test-term is to be considered in the current theory, without ; attachments. nil) (cond (erp (trans-er+ x ctx "The attempt to evaluate the ~ TRANSLATE-AND-TEST test, ~x0, when ~ FORM is ~x1, failed with the ~ evaluation error:~%~%``~@2''" (cadr x) ans msg)) ((or (consp msg) (stringp msg)) (trans-er+ x ctx "~@0" msg)) (t (trans-value ans))))))))))) ((eq (car x) 'with-local-stobj) ; Even if stobjs-out is t, we do not let normal macroexpansion handle ; with-local-stobj, because we want to make sure that we are dealing with a ; stobj. Otherwise, the raw lisp code will bind a bogus live stobj variable; ; although not particularly harmful, that will give rise to an inappropriate ; compiler warning about not declaring the variable unused. (mv-let (erp st mv-let-form creator) (parse-with-local-stobj (cdr x)) (cond (erp (trans-er ctx "Ill-formed with-local-stobj form, ~x0. ~ See :DOC with-local-stobj." x)) ((assoc-eq :stobjs-out bindings) ; We need to disallow the use of ev etc. for with-local-stobj, because the ; latching mechanism assumes that all stobjs are global, i.e., in the ; user-stobj-alist. (trans-er ctx "Calls of with-local-stobj, such as ~x0, cannot be ~ evaluated directly, as in the top-level loop. ~ See :DOC with-local-stobj and see :DOC top-level." x)) ((and (member-eq creator (global-val 'untouchable-fns wrld)) (not (eq (access state-vars state-vars :temp-touchable-fns) t)) (not (member-eq creator (access state-vars state-vars :temp-touchable-fns)))) (trans-er ctx "Illegal with-local-stobj form~@0~|~% ~y1:~%the stobj ~ creator function ~x2 is untouchable. See :DOC ~ remove-untouchable.~@3" (if (eq creator 'create-state) " (perhaps expanded from a corresponding ~ with-local-state form)," ",") x creator (if (eq creator 'create-state) " Also see :DOC with-local-state, which ~ describes how to get around this restriction and ~ when it may be appropriate to do so." ""))) ((and st (if (eq st 'state) (eq creator 'create-state) (eq st (stobj-creatorp creator wrld)))) (translate11-mv-let mv-let-form nil stobjs-out bindings known-stobjs st creator flet-alist ctx wrld state-vars)) (t (trans-er ctx "Illegal with-local-stobj form, ~x0. The first ~ argument must be the name of a stobj and the third, ~ if supplied, must be its creator function. See :DOC ~ with-local-stobj." x))))) ((and (assoc-eq (car x) *ttag-fns-and-macros*) (not (ttag wrld)) (not (global-val 'boot-strap-flg wrld))) (trans-er+ x ctx "The ~x0 ~s1 cannot be called unless a trust tag is in effect. ~ ~ See :DOC defttag.~@2" (car x) (if (getprop (car x) 'macro-body nil 'current-acl2-world wrld) "macro" "function") (or (cdr (assoc-eq (car x) *ttag-fns-and-macros*)) ""))) ((and (eq (car x) 'stobj-let) (not (eq stobjs-out t))) ; else let stobj-let simply macroexpand ; Keep this in sync with the definition of the stobj-let macro. We use the ; following running example: ; (stobj-let ; ((st1 (fld1 st+)) ; (st2 (fld2 st+) update-fld2) ; (st3 (fld3i 4 st+))) ; (st1) ; PRODUCER-VARS, below ; (producer st1 u st2 v st3) ; PRODUCER, below ; (consumer st+ u x y v w) ; CONSUMER, below ; ) ; ==> ; (let ((st1 (fld1 st+)) ; sti are BOUND-VARS, below ; (st2 (fld2 st+) update-fld2) ; cadrs are ACTUALS, below ; (st3 (fld3i 4 st+))) ; st+ is STOBJ, below ; (let ((st1 (producer st1 u st2 v st3))) ; BODY2 ; (declare (ignorable st1)) ; (let ((st+ (update-fld1 st1 st+))) ; BODY1 ; (consumer st+ u x y v w)))) (mv-let (msg bound-vars actuals stobj producer-vars producer updaters corresp-accessor-fns consumer) (parse-stobj-let x) (cond (msg (trans-er ctx "~@0" msg)) ((assoc-eq :stobjs-out bindings) ; We need to disallow the use of ev etc. for stobj-let, because the latching ; mechanism assumes that all stobjs are global, i.e., in the user-stobj-alist. (trans-er ctx "Calls of stobj-let, such as ~x0, cannot be evaluated ~ directly, as in the top-level loop." x)) (t (let ((msg (chk-stobj-let bound-vars actuals stobj updaters corresp-accessor-fns known-stobjs wrld))) (cond (msg (trans-er ctx "~@0" (illegal-stobj-let-msg msg x))) (t (let* ((new-known-stobjs (if (eq known-stobjs t) t (union-eq bound-vars known-stobjs))) (guarded-producer `(check-vars-not-free (,stobj) ,producer)) (guarded-consumer `(check-vars-not-free ,bound-vars ,consumer)) (letp (null (cdr producer-vars))) (updater-bindings (pairlis-x1 stobj (pairlis-x2 updaters nil))) (body1 `(let* ,updater-bindings ,guarded-consumer)) (body2 (cond (letp `(let ((,(car producer-vars) ,guarded-producer)) (declare (ignorable ,@producer-vars)) ,body1)) (t `(mv-let ,producer-vars ,guarded-producer (declare (ignorable ,@producer-vars)) ,body1))))) (trans-er-let* ((tactuals (translate-stobj-calls actuals 3 bindings new-known-stobjs flet-alist x ctx wrld state-vars)) (tupdaters (translate-stobj-calls updaters 4 bindings new-known-stobjs flet-alist x ctx wrld state-vars)) (tconsumer (translate11 guarded-consumer stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars)) (tbody1 (translate11-let* body1 tconsumer tupdaters stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars)) (tbody2 (cond (letp (translate11-let body2 tbody1 nil stobjs-out bindings new-known-stobjs flet-alist ctx wrld state-vars)) (t (translate11-mv-let body2 tbody1 stobjs-out bindings new-known-stobjs nil nil ; local-stobj args flet-alist ctx wrld state-vars))))) (let ((actual-stobjs-out (translate-deref stobjs-out bindings)) (no-dups-exprs (no-duplicatesp-checks-for-stobj-let-actuals actuals nil))) (cond ((and updaters ; It may be impossible for actual-stobjs-out to be an atom here (presumably ; :stobjs-out). But we cover that case, even if the error message isn't ; particularly beautiful in that case. (or (not (consp actual-stobjs-out)) (not (member-eq stobj actual-stobjs-out)))) (let ((stobjs-returned (and (consp actual-stobjs-out) (collect-non-x nil actual-stobjs-out)))) (trans-er+ x ctx "A STOBJ-LET form has been encountered that ~ specifies (with its list of producer ~ variables) ~#1~[a call~/calls~] of stobj ~ updater~#2~[~/s~] ~&2 of ~x0. It is ~ therefore a requirement that ~x0 be among the ~ outputs of the STOBJ-LET, but it is not. The ~ STOBJ-LET returns ~#3~[no single-threaded ~ objects~/the single-threaded object ~&4~/the ~ single-threaded objects ~&4~/an undetermined ~ output signature in this context~]. See :DOC ~ stobj-let." stobj updaters (remove-duplicates-eq (strip-cars updaters)) (if (consp actual-stobjs-out) (zero-one-or-more stobjs-returned) 3) stobjs-returned))) (t (trans-er-let* ((val (translate11-let `(let ,(pairlis$ bound-vars (pairlis$ actuals nil)) (declare (ignorable ,@bound-vars)) ,body2) tbody2 tactuals stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars))) (cond (no-dups-exprs (trans-er-let* ((chk (translate11 (cons 'and no-dups-exprs) '(nil) bindings known-stobjs flet-alist cform ctx wrld state-vars))) (trans-value (prog2$-call chk val)))) (t (trans-value val)))))))))))))))) ((getprop (car x) 'macro-body nil 'current-acl2-world wrld) (cond ((and (eq stobjs-out :stobjs-out) (member-eq (car x) '(pand por pargs plet)) (eq (access state-vars state-vars :parallel-execution-enabled) t)) (trans-er ctx "Parallel evaluation is enabled, but is not implemented for ~ calls of parallelism primitives (~&0) made directly in the ~ ACL2 top-level loop, as opposed to being made inside a ~ function definition. The call ~x1 is thus illegal. To ~ allow such calls to be evaluated (but without parallelism), ~ either evaluate ~x2 or use the macro top-level. See :DOC ~ parallelism-at-the-top-level and :DOC ~ set-parallel-execution." '(pand por pargs plet) x '(set-parallel-execution :bogus-parallelism-ok))) ((and (member-eq (car x) (global-val 'untouchable-fns wrld)) (not (eq (access state-vars state-vars :temp-touchable-fns) t)) (not (member-eq (car x) (access state-vars state-vars :temp-touchable-fns)))) ; If this error burns you during system maintenance, you can subvert our ; security by setting untouchables to nil in raw Lisp: ; (setf (cadr (assoc 'global-value ; (get 'untouchable-fns *current-acl2-world-key*))) ; nil) (trans-er+ x ctx "It is illegal to call ~x0 because it has been placed on ~ untouchable-fns." (car x))) (t (mv-let (erp expansion) (macroexpand1-cmp x ctx wrld state-vars) (cond (erp (mv erp expansion bindings)) (t (translate11 expansion stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))))))) ((eq (car x) 'let) (translate11-let x nil nil stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars)) ((eq (car x) 'flet) ; (flet bindings form) (translate11-flet x stobjs-out bindings known-stobjs flet-alist ctx wrld state-vars)) ((and (not (eq stobjs-out t)) (null (cdr x)) ; optimization (stobj-creatorp (car x) wrld)) (trans-er+ x ctx "It is illegal to call ~x0 in this context because it is a ~ stobj creator. Stobj creators cannot be called directly ~ except in theorems. If you did not explicitly call a stobj ~ creator, then this error is probably due to an attempt to ~ evaluate a with-local-stobj form directly in the top-level ~ loop. Such forms are only allowed in the bodies of functions ~ and in theorems. Also see :DOC with-local-stobj." (car x))) ((equal (arity (car x) wrld) (length (cdr x))) (cond ((and (member-eq (car x) (global-val 'untouchable-fns wrld)) (not (eq (access state-vars state-vars :temp-touchable-fns) t)) (not (member-eq (car x) (access state-vars state-vars :temp-touchable-fns)))) (trans-er+ x ctx "It is illegal to call ~x0 because it has been placed ~ on untouchable-fns." (car x))) ((eq (car x) 'if) (cond ((stobjp (cadr x) known-stobjs wrld) (trans-er+ x ctx "It is illegal to test on a single-threaded object ~ such as ~x0." (cadr x))) ; Because (cadr x) has not yet been translated, we do not really know it is not ; a stobj! It could be a macro call that expands to a stobj.' The error ; message above is just to be helpful. An accurate check is made below. (t (trans-er-let* ((arg1 (translate11 (cadr x) (if (eq stobjs-out t) t '(nil)) bindings known-stobjs flet-alist x ctx wrld state-vars))) (mv-let (erp2 arg2 bindings2) (trans-er-let* ((arg2 (translate11 (caddr x) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value arg2)) (cond (erp2 (cond ((eq bindings2 :UNKNOWN-BINDINGS) (mv-let (erp3 arg3 bindings) (translate11 (cadddr x) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars) (cond (erp3 (mv erp2 arg2 bindings2)) (t (trans-er-let* ((arg2 (translate11 (caddr x) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value (fcons-term* 'if arg1 arg2 arg3))))))) (t (mv erp2 arg2 bindings2)))) (t (let ((bindings bindings2)) (trans-er-let* ((arg3 (translate11 (cadddr x) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value (fcons-term* 'if arg1 arg2 arg3))))))))))) ((eq (car x) 'synp) ; Synp is a bit odd. We store the quotation of the term to be evaluated in the ; third arg of the synp form. We store the quotation so that ACL2 will not see ; the term as a potential induction candidate. (Eric Smith first pointed out ; this issue.) This, however forces us to treat synp specially here in order ; to translate the term to be evaluated and thereby get a proper ACL2 term. ; Without this special treatment (cadr x), for instance, would be left alone ; whereas it needs to be translated into (car (cdr x)). ; This mangling of the third arg of synp is sound because synp always returns ; t. ; Robert Krug has mentioned the possibility that the known-stobjs below could ; perhaps be t. This would allow a function called by synp to use, although ; not change, stobjs. If this is changed, change the referances to stobjs in ; the documentation for syntaxp and bind-free as appropriate. But before ; making such a change, consider this: no live user-defined stobj will ever ; appear in the unifying substitution that binds variables in the evg of ; (cadddr x). So it seems that such a relaxation would not be of much value. (cond ((not (eq stobjs-out t)) (trans-er ctx "A call to synp is not allowed here. This ~ call may have come from the use of syntaxp ~ or bind-free within a function definition ~ since these two macros expand into calls to ~ synp. The form we were translating when we ~ encountered this problem is ~x0. If you ~ believe this error message is itself in error ~ or that we have been too restrictive, please ~ contact the maintainers of ACL2." x)) ((eql (length x) 4) (mv-let (erp val bindings) (trans-er-let* ((quoted-vars (translate11 (cadr x) '(nil) ; stobjs-out bindings '(state) ; known-stobjs flet-alist x ctx wrld state-vars)) (quoted-user-form (translate11 (caddr x) '(nil) ; stobjs-out bindings '(state) ; known-stobjs flet-alist x ctx wrld state-vars)) (quoted-term (translate11 (cadddr x) '(nil) ; stobjs-out bindings '(state) ; known-stobjs flet-alist x ctx wrld state-vars))) (let ((quoted-term (if (quotep quoted-term) quoted-term (sublis-var nil quoted-term)))) (cond ((quotep quoted-term) (trans-er-let* ((term-to-be-evaluated (translate11 (cadr quoted-term) '(nil) ; stobjs-out bindings '(state) ; known-stobjs flet-alist x ctx wrld state-vars))) (let ((quoted-vars (if (quotep quoted-vars) quoted-vars (sublis-var nil quoted-vars))) (quoted-user-form (if (quotep quoted-user-form) quoted-user-form (sublis-var nil quoted-user-form)))) (cond ((and (quotep quoted-vars) (quotep quoted-user-form)) (trans-value (fcons-term* 'synp quoted-vars quoted-user-form (kwote term-to-be-evaluated)))) (t (trans-er ctx *synp-trans-err-string* x)))))) (t (trans-er ctx *synp-trans-err-string* x))))) (cond (erp (let ((quoted-user-form (caddr x))) (case-match quoted-user-form (('QUOTE ('SYNTAXP form)) (mv erp (msg "The form ~x0, from a ~x1 hypothesis, ~ is not suitable for evaluation in an ~ environment where its variables are ~ bound to terms. See :DOC ~x1. Here ~ is further explanation:~|~t2~@3" form 'syntaxp 5 val) bindings)) (& (mv erp val bindings))))) (t (mv erp val bindings))))) (t (trans-er ctx *synp-trans-err-string* x)))) ((eq stobjs-out t) (trans-er-let* ((args (translate11-lst (cdr x) t bindings known-stobjs nil flet-alist x ctx wrld state-vars))) (trans-value (fcons-term (car x) args)))) ((eq (car x) 'mv-list) ; and stobjs-out is not t (trans-er-let* ((arg1 (translate11 (cadr x) stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (cond ((not (and (quotep arg1) (integerp (unquote arg1)) (<= 2 (unquote arg1)))) (trans-er ctx "A call of ~x0 can only be made when the first ~ argument is explicitly an integer that is at ~ least 2. The call ~x1 is thus illegal." 'mv-list x)) (t (trans-er-let* ((arg2 (translate11 (caddr x) (make-list (unquote arg1) :initial-element nil) bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value (fcons-term* 'mv-list arg1 arg2))))))) ((stobj-field-fn-of-stobj-type-p (car x) wrld) ; and stobjs-out is not t (trans-er+ x ctx "It is illegal to call ~x0 because it is a stobj ~ updater or accessor for a field of stobj type. For a ~ way to generate such a call, see :DOC stobj-let." (car x))) ((eq (car x) 'return-last) ; and stobjs-out is not t (let* ((arg1 (nth 1 x)) (arg2 (nth 2 x)) (arg3 (nth 3 x)) (key (and (consp arg1) (eq (car arg1) 'quote) (consp (cdr arg1)) (cadr arg1))) (keyp (and (symbolp key) key))) (trans-er-let* ((targ1 (translate11 arg1 '(nil) bindings known-stobjs flet-alist x ctx wrld state-vars))) (cond ((and keyp (not (equal targ1 arg1))) ; an optional extra check (trans-er ctx "Implementation error: We have thought that a ~ quotep must translate to itself, but ~x0 did not!" arg1)) ((eq key 'mbe1-raw) ; We need to know that the two arguments of mbe1 have the same signature. If ; for example we have (mv-let (x y) (mbe1 )), but ; has signature *, then Common Lisp will get confused during ; evaluation. This signature requirement is enforced by the trans-er-let* ; bindings below. ; At one time we disallowed the use of mbe inside a non-trivial encapsulate ; when translating for execution (stobjs-out not equal to t). To see why, see ; the example in the comment near the top of (deflabel note-3-4 ...). However, ; we subsequently disallowed guard verification for functions defined ; non-locally inside an encapsulate (see :DOC note-4-0), which is the proper ; fix for this issue. What then is this issue? The issue is that we need to ; be able to trust guard verification; evaluating the :exec branch of an mbe is ; just a special case. (trans-er-let* ((targ2 (translate11 arg2 stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars)) (targ3 (translate11 arg3 stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value (fcons-term* 'return-last targ1 targ2 targ3)))) ((and (eq key 'ec-call1-raw) (not (and (consp arg3) (true-listp arg3) (let ((fn (car arg3))) (and (symbolp fn) (not (member-eq fn *ec-call-bad-ops*)) (function-symbolp fn wrld)))))) (trans-er ctx "A call of ~x0 (including macroexpansion of such a ~ call) must only be made on an argument of the form ~ (FN ...), where FN is a known function symbol of ~ the current ACL2 world not belonging to the list ~ that is the value of the constant ~x1. The ~ argument ~x2 is thus illegal for ~x0, because ~@3." 'ec-call '*ec-call-bad-ops* (car (last x)) (let ((fn (and (consp arg3) (car arg3)))) (cond ((not (and fn (true-listp arg3))) (msg "~x0 does not have the form of a ~ function call" arg3)) ((not (symbolp fn)) (msg "~x0 is not a symbol" fn)) ((member-eq fn *ec-call-bad-ops*) (msg "~x0 belongs to the above list" fn)) ((eq (getprop fn 'macro-args t 'current-acl2-world wrld) t) (assert$ (not (function-symbolp fn wrld)) (msg "~x0 is not a function symbol" fn))) (t (msg "~x0 is a macro, not a function ~ symbol" fn)))))) ((and keyp (let ((val (return-last-lookup key wrld))) (or (null val) (and (consp val) ; see chk-return-last-entry (eq stobjs-out :stobjs-out))))) ; In an early implementation of return-last, we insisted that keyp be true. But ; when we attempted to update the "GL" work of Sol Swords to use return-last, ; we encountered the creation of symbolic terms (presumably for some sort of ; meta reasoning) for which the first argument was not quoted. Rather than try ; to understand whether this was necessary, we decided that others might also ; want to write meta-level functions that cons up return-last terms without a ; quoted first argument; and since it is easy to support that, we do so. (cond ((null (return-last-lookup key wrld)) (trans-er ctx "The symbol ~x0 is specified in the first ~ argument of the form ~x1. But ~x0 is not ~ associated in the table ~x2 with a non-nil ~ value. See :DOC return-last." key x 'return-last-table)) (t (trans-er ctx "Illegal call, ~x0: the association of ~x1 with ~ the symbol ~x2 has been restricted to avoid ~ top-level evaluation of such calls of ~x3. See ~ :DOC return-last. Also consider placing the ~ offending call inside a call of ~x4; see :DOC ~ ~x4." x key (car (return-last-lookup key wrld)) 'return-last 'top-level)))) (t (mv-let (erp targ2 targ2-bindings) (translate11 arg2 '(nil) bindings known-stobjs flet-alist x ctx wrld state-vars) (declare (ignore targ2-bindings)) (cond (erp (mv erp targ2 bindings)) ((throw-nonexec-error-p1 targ1 targ2 :non-exec nil) (mv-let (erp targ3 targ3-bindings) (translate11 arg3 t ; stobjs-out bindings nil ; known-stobjs is irrelevant flet-alist x ctx wrld state-vars) (declare (ignore targ3-bindings)) (cond (erp (mv erp targ3 bindings)) (t (trans-value (fcons-term* 'return-last targ1 targ2 targ3)))))) (t (trans-er-let* ((targ3 (translate11 arg3 stobjs-out bindings known-stobjs flet-alist x ctx wrld state-vars))) (trans-value (fcons-term* 'return-last targ1 targ2 targ3))))))))))) ((eq (getprop (car x) 'non-executablep nil 'current-acl2-world wrld) t) (let ((computed-stobjs-out (compute-stobj-flags (cdr x) known-stobjs wrld))) (trans-er-let* ((args (translate11-lst (cdr x) computed-stobjs-out bindings known-stobjs nil flet-alist x ctx wrld state-vars))) (trans-value (fcons-term (car x) args))))) ((and (member-eq (car x) '(makunbound-global put-global)) (not (eq (access state-vars state-vars :temp-touchable-vars) t)) (or ; Keep this case in sync with the cond cases below (not (and (consp (cadr x)) (eq (car (cadr x)) 'quote) (null (cddr (cadr x))) (symbolp (cadr (cadr x))))) (and (member-eq (cadr (cadr x)) (global-val 'untouchable-vars wrld)) (not (member-eq (cadr (cadr x)) (access state-vars state-vars :temp-touchable-vars)))) (and (eq (car x) 'makunbound-global) (or (always-boundp-global (cadr (cadr x))) (member-eq (cadr (cadr x)) *brr-globals*))) (and (global-val 'boot-strap-flg wrld) (not (or (always-boundp-global (cadr (cadr x))) (member-eq (cadr (cadr x)) *brr-globals*)))))) (cond ( ; Keep this case the same as its twin above (not (and (consp (cadr x)) (eq (car (cadr x)) 'quote) (null (cddr (cadr x))) (symbolp (cadr (cadr x))))) (trans-er+ x ctx "The first arg of ~x0 must be a quoted symbol, ~ unlike ~x1. We make this requirement in ~ support of untouchable-vars." (car x) (cadr x))) ( ; Keep this case the same as its twin above (and (member-eq (cadr (cadr x)) (global-val 'untouchable-vars wrld)) (not (member-eq (cadr (cadr x)) (access state-vars state-vars :temp-touchable-vars)))) (trans-er ctx "State global variable ~x0 has been rendered ~ untouchable and thus may not be directly ~ altered, as in ~x1.~@2" (cadr (cadr x)) x (let ((set-fn (intern-in-package-of-symbol (concatenate 'string "SET-" (symbol-name (cadr (cadr x)))) (cadr (cadr x))))) (cond ((function-symbolp set-fn wrld) (msg "~|There is a function ~x0, which ~ (from the name) may provide the ~ functionality you desire." set-fn)) (t ""))))) ((always-boundp-global (cadr (cadr x))) (trans-er ctx "Built-in state global variables may not be made ~ unbound, as in ~x0." x)) (t ; (global-val 'boot-strap-flg wrld) (trans-er ctx "State global ~x0 needs to be declared for the ~ build by adding it to *initial-global-table*, ~ *initial-ld-special-bindings*, or *brr-globals*." (cadr (cadr x)))))) (t (let ((stobjs-out (translate-deref stobjs-out bindings)) (stobjs-out2 (let ((temp (translate-deref (car x) bindings))) (cond (temp temp) (t (stobjs-out (car x) wrld)))))) (translate11-call x (car x) (cdr x) stobjs-out stobjs-out2 bindings known-stobjs (car x) flet-alist ctx wrld state-vars))))) ((arity (car x) wrld) (trans-er ctx "~x0 takes ~#1~[no arguments~/1 argument~/~x2 ~ arguments~] but in the call ~x3 it is given ~#4~[no ~ arguments~/1 argument~/~x5 arguments~]. The formal ~ parameters list for ~x0 is ~X67." (car x) (zero-one-or-more (arity (car x) wrld)) (arity (car x) wrld) x (zero-one-or-more (length (cdr x))) (length (cdr x)) (formals (car x) wrld) nil)) ((eq (car x) 'declare) (trans-er ctx "It is illegal to use DECLARE as a function symbol, as ~ in ~x0. DECLARE forms are permitted only in very ~ special places, e.g., before the bodies of function ~ definitions, LETs, and MV-LETs. DECLARE forms are ~ never permitted in places in which their ``values'' ~ are relevant. If you already knew this, it is likely ~ you have made a typographical mistake, e.g., including ~ the body in the DECLARE form or closing the superior ~ form before typing the body." x)) (t (trans-er+ x ctx "The symbol ~x0 (in package ~x1) has neither a function nor ~ macro definition in ACL2. ~#2~[Please define ~ it.~/Moreover, this symbol is in the main Lisp package; ~ hence, you cannot define it in ACL2.~]" (car x) (symbol-package-name (car x)) (if (equal (symbol-package-name (car x)) *main-lisp-package-name*) 1 0))))) (defun translate11-lst (lst stobjs-out bindings known-stobjs msg flet-alist cform ctx wrld state-vars) ; WARNING: This function's treatment of stobjs-out is unusual: ; (1) stobjs-out must be either t, nil, or list of stobj flags. ; It CANNOT be a function name (``an unknown''). ; (2) If stobjs-out is nil, it is treated as though it were a list of ; nils as long as lst. ; If stobjs-out is t, we translate each element of lst (with stobjs-out t) ; and return the resulting list. ; If stobjs-out is not t, it is a list of stobj flags as long as lst. ; We consider each element, x, of list in correspondence with each ; flag, flg. If flg is nil, we insist that the translation of x ; return one non-stobj result. If flg is a stobj, we insist that x BE ; flg -- except that x ``is'' a stobj, flg, only if x is flg and x is ; among known-stobjs (with proper treatment of known-stobjs = t). ; Msg is used to describe the form that contains the list, lst, of ; forms being translated. It is only used if an error is caused when ; some element of lst violates the stobj restrictions of stobjs-out. ; If msg is nil, no allusion to the containing form is made. If msg ; is a symbol, we describe the containing form as though it were a ; call of that function symbol. Otherwise, we print msg with ~@ in ; ``the form x is being used, @msg, where a stobj...''. ; The cform argument is a form that provides context -- it is the one to be ; printed by trans-er+ when there isn't another obvious contextual form to ; print. (Often x carries enough context.) (cond ((atom lst) (trans-value nil)) ((eq stobjs-out t) (trans-er-let* ((x (translate11 (car lst) t bindings known-stobjs flet-alist (car lst) ctx wrld state-vars)) (y (translate11-lst (cdr lst) t bindings known-stobjs msg flet-alist cform ctx wrld state-vars))) (trans-value (cons x y)))) ((car stobjs-out) (trans-er-let* ((x (cond ((eq (if (or (eq known-stobjs t) (member-eq (car lst) known-stobjs)) (car lst) nil) (car stobjs-out)) (trans-value (car lst))) ; The following case is checked to allow our use of big-clock-entry to control ; recursion, a violation of our normal rule that state-producing forms are not ; allowed where STATE is expected (except when binding STATE). We have to look ; for the unexpanded form of the macro f-decrement-big-clock as well. ((and (eq (car stobjs-out) 'state) (or (equal (car lst) '(decrement-big-clock state)) (equal (car lst) '(f-decrement-big-clock state)))) (trans-value '(decrement-big-clock state))) ((eq (car lst) (car stobjs-out)) ; In this case, we failed because (car lst) is not considered a stobj even ; though it has the right name. (let ((known-stobjs (collect-non-x nil known-stobjs))) (trans-er+ cform ctx "The form ~x0 is being used~#1~[ ~/, as an ~ argument to a call of ~x2,~/, ~@2,~] where the ~ single-threaded object of that name is ~ required. But in the current context, ~ ~#3~[there are no declared stobj names~/the ~ only declared stobj name is ~&4~/the only ~ declared stobj names are ~&4~]." (car lst) (if (null msg) 0 (if (symbolp msg) 1 2)) msg (cond ((null known-stobjs) 0) ((null (cdr known-stobjs)) 1) (t 2)) known-stobjs))) ((and (symbolp (car lst)) (congruent-stobjsp (car lst) (car stobjs-out) wrld)) (trans-er+ cform ctx "The form ~x0 is being used~#1~[ ~/, as an ~ argument to a call of ~x2,~/, ~@2,~] where the ~ single-threaded object ~x3 was expected, even ~ though these are congruent stobjs. See :DOC ~ defstobj, in particular the discussion of ~ congruent stobjs." (car lst) (if (null msg) 0 (if (symbolp msg) 1 2)) msg (car stobjs-out))) (t (trans-er+ cform ctx "The form ~x0 is being used~#1~[ ~/, as an ~ argument to a call of ~x2,~/, ~@2,~] where the ~ single-threaded object ~x3 is required. Note ~ that the variable ~x3 is required, not merely a ~ term that returns such a single-threaded ~ object, so you may need to bind ~x3 with LET; ~ see :DOC stobj." (car lst) (if (null msg) 0 (if (symbolp msg) 1 2)) msg (car stobjs-out))))) (y (translate11-lst (cdr lst) (cdr stobjs-out) bindings known-stobjs msg flet-alist cform ctx wrld state-vars))) (trans-value (cons x y)))) (t (trans-er-let* ((x (translate11 (car lst) '(nil) bindings known-stobjs flet-alist (car lst) ctx wrld state-vars)) (y (translate11-lst (cdr lst) (cdr stobjs-out) bindings known-stobjs msg flet-alist cform ctx wrld state-vars))) (trans-value (cons x y)))))) ) (defun translate1-cmp (x stobjs-out bindings known-stobjs ctx w state-vars) ; See also translate1 for a corresponding version that also returns state. ; Stobjs-out should be t, a proper STOBJS-OUT setting, a function symbol, or ; the symbol :stobjs-out. ; Stobjs-out t means we do not enforce mv-let or stobjs restrictions. A proper ; STOBJS-OUT setting (a list of stobj flags) enforces the given restrictions. ; A function symbol means we enforce the rules and determine the stobjs-out, ; binding the symbol in the returned bindings alist. In addition, a function ; symbol tells us we are in a definition body and enforce certain rules ; prohibiting calls of functions like DEFUN and IN-PACKAGE. The symbol ; :stobjs-out -- which is not a function symbol -- has the same meaning as a ; function symbol except that it tells us we are NOT processing a definition ; body. As is noted below, if the initial stobjs-out is :stobjs-out, bindings ; MUST be '((:stobjs-out . :stobjs-out)) and we use (eq (caar bindings) ; :stobjs-out) to determine that we are not in a definition. ; CAUTION: If you call this function with stobjs-out being a symbol, say fn, ; make sure that ; (a) fn is bound to itself in bindings, e.g., bindings = ((fn . fn)), and ; (b) fn is not an existing function name in w, in particular, it must not have ; a STOBJS-OUT setting, since that is what we use fn to compute. ; In general, bindings is a list of pairs, one for each fn in the clique being ; introduced, and each is initially bound to itself. If a function symbol is ; not bound in bindings, its STOBJS-OUT is obtained from w. ; Known-stobjs is either a list of stobj names (but may contain some NIL ; elements, to be ignored; see "slight abuse" comment in ; chk-acceptable-defuns1) or T (meaning, all stobj names in world w). A name ; is considered a stobj only if it is in this list. ; State-vars is a state-vars record, typically (default-state-vars t) unless ; one does not have state available, and then (default-state-vars nil). ; We return (mv erp transx bindings), where transx is the translation and ; bindings has been modified to bind every fn (ultimately) to a proper stobjs ; out setting. Use translate-deref to recover the bindings. (trans-er-let* ((result (translate11 x stobjs-out bindings known-stobjs nil x ctx w state-vars))) (cond ((and bindings (null (cdr bindings)) (symbolp (caar bindings)) (eq (caar bindings) (cdar bindings))) ; This case can happen because x is the call of a non-executable function. We ; return a proper stobjs-out value, for example as passed by trans-eval to ; ev-for-trans-eval. This treatment is necessary for the following example, to ; avoid being unable to determine the output signature of g. ; (defun-nx f (x) x) ; (defun g (x) (f x)) ; This treatment is consistent with our use of stobjs-out = (nil) for ; non-executable functions. (trans-value result (translate-bind (caar bindings) '(nil) bindings))) (t (trans-value result))))) (defun@par translate1 (x stobjs-out bindings known-stobjs ctx w state) (cmp-and-value-to-error-quadruple@par (translate1-cmp x stobjs-out bindings known-stobjs ctx w (default-state-vars t)))) (defun collect-programs (names wrld) ; Names is a list of function symbols. Collect the :program ones. (cond ((null names) nil) ((programp (car names) wrld) (cons (car names) (collect-programs (cdr names) wrld))) (t (collect-programs (cdr names) wrld)))) ; The following is made more efficient below by eliminating the mutual ; recursion. This cut the time of a proof using bdds by nearly a factor of 4; ; it was of the form (implies (pred n) (prop n)) where pred has about 1800 ; conjuncts. The culprit was the call(s) of all-fnnames in bdd-rules-alist1, I ; think. ; (mutual-recursion ; ; (defun all-fnnames (term) ; (cond ((variablep term) nil) ; ((fquotep term) nil) ; ((flambda-applicationp term) ; (union-eq (all-fnnames (lambda-body (ffn-symb term))) ; (all-fnnames-lst (fargs term)))) ; (t ; (add-to-set-eq (ffn-symb term) ; (all-fnnames-lst (fargs term)))))) ; ; (defun all-fnnames-lst (lst) ; (cond ((null lst) nil) ; (t (union-eq (all-fnnames (car lst)) ; (all-fnnames-lst (cdr lst)))))) ; ) (defun all-fnnames1 (flg x acc) ; Flg is nil for all-fnnames, t for all-fnnames-lst. Note that this includes ; function names occuring in the :exec part of an mbe. Keep this in sync with ; all-fnnames1-exec. (cond (flg ; x is a list of terms (cond ((null x) acc) (t (all-fnnames1 nil (car x) (all-fnnames1 t (cdr x) acc))))) ((variablep x) acc) ((fquotep x) acc) ((flambda-applicationp x) (all-fnnames1 nil (lambda-body (ffn-symb x)) (all-fnnames1 t (fargs x) acc))) (t (all-fnnames1 t (fargs x) (add-to-set-eq (ffn-symb x) acc))))) (defmacro all-fnnames (term) `(all-fnnames1 nil ,term nil)) (defmacro all-fnnames-lst (lst) `(all-fnnames1 t ,lst nil)) (defun translate-cmp (x stobjs-out logic-modep known-stobjs ctx w state-vars) ; See translate. Here we return a context-message pair; see the Essay on ; Context-message Pairs. State-vars is a state-vars record, typically ; (default-state-vars t) unless one does not have state available, and then ; (default-state-vars nil). (mv-let (erp val bindings) (translate1-cmp x stobjs-out nil known-stobjs ctx w state-vars) (declare (ignore bindings)) (cond (erp ; erp is a ctx and val is a msg (mv erp val)) ((and logic-modep (program-termp val w)) (er-cmp ctx "Function symbols of mode :program are not allowed ~ in the present context. Yet, the function ~ symbol~#0~[ ~&0 occurs~/s ~&0 occur~] in the ~ translation of the form~|~% ~x1,~%~%which is~|~% ~ ~x2." (collect-programs (all-fnnames val) w) x val)) (t (value-cmp val))))) (defun@par translate (x stobjs-out logic-modep known-stobjs ctx w state) ; This is the toplevel entry into translation throughout ACL2, ; excepting translate-bodies, which translates the bodies of ; definitions. The output of translate is (mv erp transx state). ; Stobjs-out should be ; * t - to indicate that we are translating only for logical use, as ; in theorems etc. Do NOT use t for defuns, defmacros, ; defconst, or other events involving Common Lisp execution. ; * (s1 ... sn) - where each si is either nil or a stobj name (possibly ; STATE) to indicate that the mv-let and stobj ; restrictions should be enforced AND that x is to have ; the indicated stobj signature. See the Essay on ; STOBJS-IN and STOBJS-OUT. ; Logic-modep should be set when we want to ensure that the resulting ; term does not mention any function symbols of defun-mode :program. ; This check is NOT made on-the-fly (in translate1) but as an ; after-the-fact convenience here. ; Known-stobjs is either a list of stobj names (but may contain some NIL ; elements, to be ignored; see "slight abuse" comment in ; chk-acceptable-defuns1) or T (meaning, all stobj names in world w). A name ; is considered a stobj only if it is in this list. (cmp-to-error-triple@par (translate-cmp x stobjs-out logic-modep known-stobjs ctx w (default-state-vars t)))) (defun translatable-p (form stobjs-out bindings known-stobjs ctx wrld) (mv-let (erp val bindings) (translate1-cmp form stobjs-out bindings known-stobjs ctx wrld (default-state-vars nil)) (declare (ignore val bindings)) (null erp))) (defmacro chk-translatable (form shape) `(translate-and-test (lambda (qform) (cond ((translatable-p (cadr qform) ',(cond ((eq shape 'state) '(state)) (t (cdr shape))) nil t 'chk-translatable world) t) (t (msg "IO? was given the following body, which fails to ~ translate for the expected shape, STATE:~|~ ~y0" ',form)))) ',form)) ; We now move on to the definition of the function trans-eval, which ; evaluates a form containing references to the free variable STATE, ; and possibly to other stobj names, by binding 'STATE to the given ; state and the other stobj names to their current values in that ; state. Consing STATE and other stobjs into a list is a gross ; violation of our rules on the use of stobjs. We believe it is ; legitimate in the special case that a stobj variable name is used in ; the appropriate places in the form, a check that we can make by ; translating the form and inspecting the STOBJS-IN and STOBJS-OUT. ; We arrange to admit trans-eval to the logic by special dispensation. (defun replaced-stobj (name) (if (eq name 'STATE) ; This is just an optimization because it is so common. 'REPLACED-STATE (packn (list "REPLACED-" name)))) (defun replace-stobjs1 (stobjs-out val) (cond ((endp val) val) ((car stobjs-out) (cons (replaced-stobj (car stobjs-out)) (replace-stobjs1 (cdr stobjs-out) (cdr val)))) (t (cons (car val) (replace-stobjs1 (cdr stobjs-out) (cdr val)))))) (defun replace-stobjs (stobjs-out val) ; Replace the stobj objects indicated by the stobj flags in stobjs-out ; by an ordinary symbol derived from the stobj name. In the case that ; the stobj objects are the live ones, this is crucial to do before ; returning out of trans-eval. Val is either a single value or a list ; of 2 or more values, as indicated by stobjs-out. If stobjs-out is ; nil it is treated as a list of as many nils as necessary and no ; change is made to val. (cond ((null stobjs-out) val) ((null (cdr stobjs-out)) (cond ((car stobjs-out) (replaced-stobj (car stobjs-out))) (t val))) (t (replace-stobjs1 stobjs-out val)))) ; The following is from an old attempt to make the read-eval-print loop handle ; free variables as references to globals. We abandoned this attempt because ; the LAMBDA abstraction handling introduced by mv-let was forcing globals to ; be evaluated before they had been set, making it confusing which value of a ; global was to be used. We have left in trans-eval the code that used this, ; within comments. Note that such an attempt now would need to change ; 'untouchables to 'untouchable-vars. ; (defun build-alist (vars state) ; (declare (xargs :guard (true-listp vars))) ; (cond ((null vars) (value nil)) ; ((eq (car vars) 'state) ; (build-alist (cdr vars) state)) ; ((member (car vars) (global-val 'untouchables (w state))) ; (er soft 'trans-eval ; "The global variable ~x0 is on untouchables." ; (car vars))) ; (t (er-let* ((alist (build-alist (cdr vars) state))) ; (value (cons (cons (car vars) ; (list 'get-global ; (list 'quote (car vars)) 'state)) ; alist)))))) ; (defun non-stobjps (vars known-stobjs w) (cond ((endp vars) nil) ((stobjp (car vars) known-stobjs w) (non-stobjps (cdr vars) known-stobjs w)) (t (cons (car vars) (non-stobjps (cdr vars) known-stobjs w))))) (defun user-stobjsp (stobjs-out) (cond ((endp stobjs-out) nil) ((or (null (car stobjs-out)) (eq (car stobjs-out) 'state)) (user-stobjsp (cdr stobjs-out))) (t t))) (defun put-assoc-eq-alist (alist1 alist2) ; Setting: A form has been evaluated, producing a state with alist1 as its ; user-stobj-alist. The evaluation also produced some latches, which are ; alist2. We wish to merge the latches into the user-stobj-alist of the state ; and this is the workhorse. We know that the form returns at least one user ; stobj (and so, we know the form is not a DEFSTOBJ or DEFABSSTOBJ or its undo ; or redo). Given this knowledge, we wish to store the new stobjs in latches ; back into the user-stobj-alist. ; Spec for this function: Both arguments are duplicate-free symbol alists. For ; every (key . val) in alist2 we a put-assoc-eq of key and val into alist1. (cond ((endp alist2) alist1) ; The following clause is an optimization. If alist1 and alist2 are equal and ; we continued as though this clause weren't here, then we would store each ; (key . val) pair of alist2 into an already identical pair of alist1, ; affecting no change of alist1. So we can stop and return alist1 now. (Note ; that if the two alists contained duplicate keys, this would not be an ; optimization: alist1 = alist2 = '((a . 1) (a . 2)) would yeild '((a . 1) (a ; . 2)) with this optimization in place but would yeild '((a . 2) (a . 2)) ; without this optimization.) This optimization increases the efficiency of ; trans-eval's handling of latches. See the Essay on the Handling of ; User-Stobj-Alist in Trans-Eval. ((equal alist2 alist1) alist1) (t (put-assoc-eq-alist (put-assoc-eq (caar alist2) (cdar alist2) alist1) (cdr alist2))))) #-acl2-loop-only (defun-one-output chk-user-stobj-alist (stobjs alist acc ctx) (if (endp alist) (if acc ; We use interface-er rather than (er hard ...) because we do not expect to be ; in the context of a (catch 'raw-ev-fncall ...). (interface-er "It is illegal to run ACL2 evaluators trans-eval and ~ simple-translate-and-eval on any term that mentions a stobj that ~ has been bound by with-local-stobj or stobj-let. The reason is ~ that those evaluators expect each stobj to match perfectly the ~ corresponding global stobj that is stored in the ACL2 state. The ~ offending stobj name~#0~[ is~/s are~]: ~&0." acc) t) (if (and (member-eq (caar alist) stobjs) (not (eq (symbol-value (the-live-var (caar alist))) (cdar alist)))) (chk-user-stobj-alist stobjs (cdr alist) (cons (caar alist) acc) ctx) (chk-user-stobj-alist stobjs (cdr alist) acc ctx)))) (defun user-stobj-alist-safe (ctx stobjs state) #-acl2-loop-only (if stobjs ; optimization (chk-user-stobj-alist stobjs (user-stobj-alist state) nil ctx) (user-stobj-alist state)) #+acl2-loop-only (declare (ignore ctx stobjs)) (user-stobj-alist state)) (defun ev-for-trans-eval (trans vars stobjs-out ctx state aok) ; Trans is a translated term with the indicated stobjs-out, and vars is ; (all-vars term). We return the result of evaluating trans, but formulated as ; an error triple with possibly updated state as described in trans-eval. ; This function is called by trans-eval, and is a suitable alternative to ; trans-eval when the term to be evaluated has already been translated by ; translate1 with stobjs-out = :stobjs-out. (let ((alist (cons (cons 'state (coerce-state-to-object state)) (user-stobj-alist-safe 'trans-eval vars state)))) (mv-let (erp val latches) (ev trans alist state alist nil aok) ; The first state binding below is the state produced by the evaluation of the ; form. The second state is the first, but with the user-stobj-alist of that ; state (possibly) updated to contain the modified latches. Note that we don't ; bother to modify the user-stobj-alist if the form's output signature does not ; involve a user-defined stobj. The particular forms we have in mind for this ; case are DEFSTOBJ and DEFABSSTOBJ forms and their ``undoers'' and ; ``re-doers''. They compute the state they mean and we shouldn't mess with ; the user-stobj-alist of their results, else we risk overturning carefully ; computed answers by restoring old stobjs. (let ((state (coerce-object-to-state (cdr (car latches))))) (let ((state (cond ((user-stobjsp stobjs-out) (update-user-stobj-alist (put-assoc-eq-alist (user-stobj-alist state) (cdr latches)) state)) (t state)))) (cond (erp ; If ev caused an error, then val is a pair (str . alist) explaining the error. ; We will process it here (as we have already processed the translate errors ; that might have arisen) so that all the errors that might be caused by this ; translation and evaluation are handled within this function. (error1 ctx (car val) (cdr val) state)) (t (mv nil (cons stobjs-out (replace-stobjs stobjs-out val)) state)))))))) #+acl2-par (defun ev-w-for-trans-eval (trans vars stobjs-out ctx state aok) ; See analogous function ev-for-trans-eval. ; Parallelism wart: add an assertion that stobjs-out does not contain state (or ; any other stobj). Perhaps the assertion should be that stobjs-out equals the ; representation for an ordinary value. (let ((alist (cons (cons 'state (coerce-state-to-object state)) (user-stobj-alist-safe 'trans-eval vars state)))) (mv-let (erp val) (ev-w trans alist (w state) (user-stobj-alist state) (f-get-global 'safe-mode state) (gc-off state) nil aok) (cond (erp ; If ev caused an error, then val is a pair (str . alist) explaining ; the error. We will process it here (as we have already processed the ; translate errors that might have arisen) so that all the errors that ; might be caused by this translation and evaluation are handled within ; this function. ; Parallelism wart: check that the above comment is true and applicable in this ; function, even though we call ev-w instead of ev. (error1@par ctx (car val) (cdr val) state)) (t (mv nil (cons stobjs-out (replace-stobjs stobjs-out val)))))))) (defun trans-eval (form ctx state aok) ; Advice: See if simple-translate-and-eval will do the job. ; This function translates form and then evaluates it, with 'state ; bound to state and the user's stobj names bound to their current ; values in (user-stobj-alist state). ; We return an error triple: (mv erp val state'). If erp is t, then ; an error occurred (which has been printed into state'). State' will ; reflect changes caused to single-threaded objects prior to the ; error. ; If erp is nil, val is (stobjs-out . replaced-val), where stobjs-out ; is the stobjs out of the translated form and replaced-val is the ; value of the evaluation of form, with any output stobjs replaced by ; symbols as per replace-stobjs. The final values of the stobjs may ; be found in (user-stobj-alist state'). Note that this change to ; state -- the storage of the final stobjs -- is done at the ; conclusion of the computation and is not directed by form. (mv-let (erp trans bindings state) (translate1 form :stobjs-out '((:stobjs-out . :stobjs-out)) t ctx (w state) state) ; known-stobjs = t. We expect trans-eval to be used only when the ; user is granted full access to the stobjs in state. Of course, some ; applications of trans-eval, e.g., in eval-event-lst, first check ; that the form doesn't access stobjs or state. (cond (erp (mv t nil state)) (t (let ((stobjs-out (translate-deref :stobjs-out bindings)) (vars (all-vars trans))) (cond ((non-stobjps vars t (w state)) ;;; known-stobjs = t (er soft ctx "Global variables, such as ~&0, are not allowed. See ~ :DOC ASSIGN and :DOC @." (non-stobjps vars t (w state)))) ;;; known-stobjs = t (t (ev-for-trans-eval trans vars stobjs-out ctx state aok)))))))) (defun simple-translate-and-eval (x alist ok-stobj-names msg ctx wrld state aok) ; A Note on the Reason this Function Exists: ; This function is a cousin of trans-eval that is much easier to use ; in simple cases. Trans-eval can handle any well-formed term. Thus, ; it must have a way to communicate to the caller how many results are ; being returned and what they are. The obvious thing for trans-eval ; to do is to list the results. But if one of them is STATE or some ; other stobj, it cannot. So trans-eval has a rather complicated ; interface that permits the caller to determine the mulitplicity of ; the result and whether and where the stobjs appear (or, more precisely, ; are supposed to appear) in the output vector. See the documentation ; of trans-eval for its specification. ; This function, simple-translate-and-eval, is designed to handle more ; simply the most common case, namely, when x is supposed to be a term ; that returns one result and that result is not state or any other ; stobj. In that case, we can return the result directly. ; While trans-eval may be used whenever translation and evaluation are ; needed, we recommend using simple-translate-and-eval if the given ; term returns a single, non-stobj result, simply because the ; interface is simpler. ; The Spec of SIMPLE-TRANSLATE-AND-EVAL: We translate x, requiring ; that it be a term that returns one non-stobj result. We verify that ; the translation mentions no variables other than those bound in ; alist and the stobj names listed in ok-stobj-names. We then ; evaluate the translation of x under alist', where alist' is obtained ; from alist by appending the bindings of 'state to state and ; (user-stobj-alist state). (The extra bindings can't hurt. The ; bindings of alist have priority.) If no errors arise, we return a ; pair, (term . val), where term is the translation of x and val is ; its value under alist'. ; Msg is a ~@ message that should describe x and begin with a capital ; letter. For example, msg might be the string "The second argument ; to foo". ; Note that we call translate with logic-modep nil. Thus, :program ; mode functions may appear in x. ; Keep in sync with simple-translate-and-eval@par. (er-let* ((term (translate x '(nil) nil t ctx wrld state))) ; known-stobjs = t. We expect simple-translate-and-eval to be used ; only when the user is granted full access to the stobjs in state ; (without modification rights, of course). (let ((vars (all-vars term)) (legal-vars (append (strip-cars alist) ok-stobj-names))) (cond ((not (subsetp-eq vars legal-vars)) (er soft ctx "~@0 may contain ~#1~[no variables~/only the ~ variable ~&2~/only the variables ~&2~], but ~ ~x3 contains ~&4." msg (cond ((null legal-vars) 0) ((null (cdr legal-vars)) 1) (t 2)) legal-vars x vars)) (t (mv-let (erp val latches) (ev term (append alist (cons (cons 'state (coerce-state-to-object state)) (user-stobj-alist-safe 'simple-translate-and-eval (intersection-eq ok-stobj-names vars) state))) state nil nil aok) (declare (ignore latches)) ; Parallelism wart: since we ignore latches, we should be able to create a ; version of simple-translate-and-eval that returns cmp's. (cond (erp (pprogn (error-fms nil ctx (car val) (cdr val) state) (er soft ctx "~@0 could not be evaluated." msg))) (t (value (cons term val)))))))))) (defun error-fms-cw (hardp ctx str alist) (wormhole 'comment-window-io '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) (list hardp ctx str alist) `(let ((hardp (nth 0 (@ wormhole-input))) (ctx (nth 1 (@ wormhole-input))) (str (nth 2 (@ wormhole-input))) (alist (nth 3 (@ wormhole-input)))) (pprogn (error-fms hardp ctx str alist state) (value :q))) :ld-error-action :error ; for robustness; no error is expected :ld-verbose nil :ld-pre-eval-print nil :ld-prompt nil)) #+acl2-par (defmacro error-fms@par (&rest args) `(error-fms-cw ,@args)) (defun simple-translate-and-eval-cmp (x alist ok-stobj-names msg ctx wrld state aok safe-mode gc-off) ; Warning: Errors printed by this function are not inhibited by ; set-inhibit-output-lst. ; This version of simple-translate-and-eval returns a context-message pair; see ; the Essay on Context-message Pairs. See simple-translate-and-eval for ; documentation, for example that translation is done under the assumption that ; the user is granted full access to the stobjs in state. ; Notice that we pass in safe-mode and gc-off explicitly, rather than reading ; them from state, because there are occasions (e.g., eval-theory-expr@par) ; where at least one of these parameters could differ from its corresponding ; state value. But couldn't we have simply state-global-let*-bound the ; relevant state globals? Well, no, not in contexts like eval-theory-expr@par ; that do not allow modification of state. (er-let*-cmp ((term (translate-cmp x '(nil) nil t ctx wrld (default-state-vars t)))) (let ((vars (all-vars term)) (legal-vars (append (strip-cars alist) ok-stobj-names))) (cond ((not (subsetp-eq vars legal-vars)) (er-cmp ctx "~@0 may contain ~#1~[no variables~/only the variable ~ ~&2~/only the variables ~&2~], but ~x3 contains ~&4." msg (cond ((null legal-vars) 0) ((null (cdr legal-vars)) 1) (t 2)) legal-vars x vars)) (t (mv-let (erp val) ; Note that because translate-cmp is called above with parameter stobjs-out = ; '(nil), we have met the requirement on ev-w; specifically, evaluation of the ; given form cannot modify any stobj. (ev-w term (append alist (cons (cons 'state (coerce-state-to-object state)) (user-stobj-alist-safe 'simple-translate-and-eval (intersection-eq ok-stobj-names vars) state))) (w state) (user-stobj-alist state) safe-mode gc-off nil aok) (cond (erp (prog2$ (error-fms-cw nil ctx (car val) (cdr val)) (er-cmp ctx "~@0 could not be evaluated." msg))) (t (value-cmp (cons term val)))))))))) (defun simple-translate-and-eval-error-double (x alist ok-stobj-names msg ctx wrld state aok safe-mode gc-off) ; Warning: Errors printed by this function are not inhibited by ; set-inhibit-output-lst. ; This version of simple-translate-and-eval returns an error double (mv erp ; val). See simple-translate-and-eval for documentation, for example that ; translation is done under the assumption that the user is granted full access ; to the stobjs in state. ; This function was requested by David Rager so that he could make the ; community book books/cutil/wizard.lisp thread-safe for ACL2(p). We return an ; error double (mv erp val). ; Our plan is to introduce simple-translate-and-eval-cmp first, because we have ; nice idioms for context-message pairs. Then we trivially define ; simple-translate-and-eval-error-double in terms of ; simple-translate-and-eval-cmp. ; See a comment in simple-translate-and-eval-cmp for why we pass in safe-mode ; and gc-off explicitly, rather than reading them from state. (cmp-to-error-double (simple-translate-and-eval-cmp x alist ok-stobj-names msg ctx wrld state aok safe-mode gc-off))) #+acl2-par (defun simple-translate-and-eval@par (x alist ok-stobj-names msg ctx wrld state aok safe-mode gc-off) ; This function is just an ACL2(p) wrapper for ; simple-translate-and-eval-error-double. The history is that this function ; was defined first, but David Rager needed a version that worked in ; non-parallel ACL2 as well; see simple-translate-and-eval-error-double. ; We keep the function simple-translate-and-eval@par because of its handling in ; bodies of functions defined using defun@par according to the table ; *@par-mappings*. See for example the call of simple-translate-and-eval@par ; in (defun@par translate-do-not-hint ...). (simple-translate-and-eval-error-double x alist ok-stobj-names msg ctx wrld state aok safe-mode gc-off)) (defun tilde-*-alist-phrase1 (alist evisc-tuple level) (cond ((null alist) nil) (t (cons (msg "~t0~s1 : ~Y23~|" level (caar alist) (cdar alist) evisc-tuple) (tilde-*-alist-phrase1 (cdr alist) evisc-tuple level ))))) (defun tilde-*-alist-phrase (alist evisc-tuple level) ; This prints out a substitution alist, e.g., ((x . a) (y . b) (z . c)) ; in the form ; x : a ; y : b ; z : c ; when the output is printed with ~*. (list "" "~@*" "~@*" "~@*" (tilde-*-alist-phrase1 alist evisc-tuple level))) (defun set-temp-touchable-fns (x state) ; Keep this in sync with set-temp-touchable-vars. ; Why make the indicated check below, rather than using a guard? Because we ; want that check to be made even when this function is called underneath ; :program mode functions, hence even when guards aren't checked. (cond ((or (eq x t) (symbol-listp x)) (f-put-global 'temp-touchable-fns x state)) (t (prog2$ (er hard 'set-temp-touchable-fns "The first argument to ~x0 may must be either ~x0 or a ~ true list of symbols, unlike:~| ~x1" 'temp-touchable-fns x) state)))) (defun set-temp-touchable-vars (x state) ; Keep this in sync with set-temp-touchable-fns. ; Why make the indicated check below, rather than using a guard? Because we ; want that check to be made even when this function is called underneath ; :program mode functions, hence even when guards aren't checked. (cond ((or (eq x t) (symbol-listp x)) (f-put-global 'temp-touchable-vars x state)) (t (prog2$ (er hard 'set-temp-touchable-vars "The first argument to ~x0 may must be either ~x0 or a ~ true list of symbols, unlike:~| ~x1" 'temp-touchable-vars x) state)))) (defun clear-temp-touchable-fns (state) (f-put-global 'temp-touchable-fns nil state)) (defun clear-temp-touchable-vars (state) (f-put-global 'temp-touchable-vars nil state)) ; Note on functional programming. ; Lest anyone think that ACL2 fails to have a functional programming ; component, we here illustrate how to code some of the traditional ; function manipulating operations of Lisp in ACL2. All these ; operations depend upon the function trans-eval. These functions are ; at the moment not very efficient because they involve a runtime call ; to translate. Futhermore, proving interesting theorems about these ; functions would not be easy because they are tied up with the ; ``big-clock'' story which makes our evaluator primitive recursive. ; But nevertheless it is worth pointing out that this capability at ; least exists in ACL2. (defun mapcar$ (fn l state) ; A version of the traditional lisp mapper, e.g. ; (mapcar$ 'reverse '((1 2 3) (4 5)) state) => ; ((3 2 1) (5 4)) (cond ((null l) (value nil)) (t (er-let* ((ans (trans-eval (list fn (list 'quote (car l))) 'mapcar$ state t)) (rst (mapcar$ fn (cdr l) state))) ; Ans is (stobjs-out . replaced-val), where stobjs-out indicates where ; stobjs are located in replaced-val. However, those stobjs have been ; replaced by simple symbols. The final value of state produced by fn ; is state, which may be among the stobjs-out. We just cons the ; replaced-val into our answer, which is a little peculiar since it ; may contain 'replaced-state, but it's sufficient to indicate what is ; happening and the final state has been side-effected in the proper ; sequence. (value (cons (cdr ans) rst)))))) (defun mapdo (fn l state) ; A mapper that simply applies the fn for side effect (on the ; free variable state), e.g. ; (mapdo '(lambda (x) (princ$ x *standard-co* state)) '(1 2 3) state) ; prints 123 and returns nil. (cond ((null l) (value nil)) (t (er-let* ((ans (trans-eval (list fn (list 'quote (car l))) 'mapdo state t)) (rst (mapdo fn (cdr l) state))) (value nil))))) (defun always (fn l state) ; A universal quantifier, e.g. (always 'rationalp '(1 2 3) state) => ; t (cond ((null l) (value t)) (t (er-let* ((ans (trans-eval (list fn (list 'quote (car l))) 'always state t))) (cond ((null (cdr ans)) (value nil)) (t (always fn (cdr l) state))))))) (defun thereis (fn l state) ; An existential quantifier, e.g. ; (thereis 'rationalp '(a 2 b) state) => '(2 B) (cond ((null l) (value nil)) (t (er-let* ((ans (trans-eval (list fn (list 'quote (car l))) 'thereis state t))) (cond ((cdr ans) (value l)) (t (thereis fn (cdr l) state))))))) acl2-sources/tutorial.lisp0000664002132200015000000156411212222115527015320 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. ; This document currently has the following form: ; ; :doc ACL2-tutorial ; introduction ; OVERVIEW ; ABOUT THIS TUTORIAL: ; GETTING STARTED: ; INTERACTING WITH ACL2: ; :doc examples ; EXAMPLE: TOWERS OF HANOI ; EXAMPLE: EIGHTS PROBLEM ; A LARGER EXAMPLE: A PHONEBOOK SPECIFICATION ; DEFUN-SK-EXAMPLE:: example of quantified notions ; :doc miscellaneous-examples ; * FILE-READING-EXAMPLE:: example of reading files in ACL2 ; * MUTUAL-RECURSION-PROOF-EXAMPLE:: a small proof about mutually ; recursive functions ; * GUARD-EXAMPLE a brief transcript illustrating guards in ACL2 ; STARTUP ; TIDBITS ; TIPS (in-package "ACL2") (deflabel ACL2-Tutorial :doc ":Doc-Section ACL2-Tutorial tutorial introduction to ACL2~/ To learn about ACL2, read at least the following two links. ~bq[] * ~il[interesting-applications Industrial Applications of ACL2] (10 minutes) to help you understand what sophisticated users can do; * ~il[|A Flying Tour of ACL2| A Flying Tour] (10 minutes) to get an overview of the system and what skills the user must have.~eq[] If you want to learn ~em[how to use] ACL2, we recommend that you read a selection of the materials referenced below, depending on your learning style, and do suggested exercises. ~bq[] * ``~il[|A Walking Tour of ACL2| A Walking Tour]'' (1 hour) provides an overview of the theorem prover. * The external site ~url[http://tryacl2.org] provides interactive lessons to get you started using ACL2. * ``~il[introduction-to-the-theorem-prover Introduction to the Theorem Prover]'' (10-40 hours) provides instruction on how to interact with the system. Unlike the three documents above, this document expects you to ~em[think]! It cites the necessary background pages on programming in ACL2 and on the logic and then instructs you in The Method, which is how expert users use ACL2. It concludes with some challenge problems for the ACL2 beginner (including solutions) and an FAQ. Most users will spend several hours a day for several days working through this material. * The book ''Computer-Aided Reasoning: An Approach'' (see ~url[http://www.cs.utexas.edu/users/moore/publications/acl2-books/car/index.html] is worth a careful read, as you work exercises and learn ``The Method.'' * ``~il[annotated-acl2-scripts Annotated ACL2 Scripts and Demos]'' contains relatively elementary proof scripts that have been annotated to help train the newcomer. * Many files (``books'') in the ACL2 community books (~pl[community-books]) are extensively annotated. See the link to ``Lemma Libraries and Utilities'' on the ACL2 home page (~url[http://www.cs.utexas.edu/users/moore/acl2]). * An ``~il[alternative-introduction Alternative Introduction]'' document, while largely subsumed by the topic ``~il[introduction-to-the-theorem-prover Introduction to the Theorem Prover]'' mentioned above, still might be useful because it covers much of the tutorial material in a different way.~eq[] At this point you are probably ready to use ACL2 on your own ~em[small] projects. A common mistake for beginners is to browse the documentation and then try to do something that is too big! Think of a very small project and then simplify it! Note that ACL2 has a very supportive user network. See the link to ``Mailing Lists'' on the ACL2 home page (~url[http://www.cs.utexas.edu/users/moore/acl2]). The topics listed below are a hodge podge, developed over time. Although some of these are not mentioned above, you might find some to be useful as well. ~/~/") (deflabel alternative-introduction :doc ":Doc-Section ACL2-Tutorial introduction to ACL2~/ This section contains introductory material on ACL2 including what ACL2 is, how to get started using the system, how to read the output, and other introductory topics. It was written almost entirely by Bill Young of Computational Logic, Inc. You might also find CLI Technical Report 101 helpful, especially if you are familiar with Nqthm. If you would like more familiarity with Nqthm, we suggest CLI Technical Report 100.~/ ~em[OVERVIEW] ACL2 is an automated reasoning system developed (for the first 9 years) at Computational Logic, Inc. and (from January, 1997) at the University of Texas at Austin. It is the successor to the Nqthm (or Boyer-Moore) logic and proof system and its Pc-Nqthm interactive enhancement. The acronym ACL2 actually stands for ``A Computational Logic for Applicative Common Lisp''. This title suggests several distinct but related aspects of ACL2. We assume that readers of the ACL2 ~il[documentation] have at least a very slight familiarity with some Lisp-like language. We will address the issue of prerequisites further, in ``ABOUT THIS TUTORIAL'' below. As a ~b[logic], ACL2 is a formal system with rigorously defined syntax and semantics. In mathematical parlance, the ACL2 logic is a first-order logic of total recursive functions providing mathematical induction on the ordinals up to epsilon-0 and two extension principles: one for recursive definition and one for constrained introduction of new function symbols, here called encapsulation. The syntax of ACL2 is that of Common Lisp; ACL2 specifications are ``also'' Common Lisp programs in a way that we will make clear later. In less formal language, the ACL2 logic is an integrated collection of rules for defining (or axiomatizing) recursive functions, stating properties of those functions, and rigorously establishing those properties. Each of these activities is mechanically supported. As a ~b[specification language], ACL2 supports modeling of systems of various kinds. An ACL2 function can equally be used to express purely formal relationships among mathematical entities, to describe algorithms, or to capture the intended behavior of digital systems. For digital systems, an ACL2 specification is a mathematical ~b[model] that is intended to formalize relevant aspects of system behavior. Just as physics allows us to model the behavior of continuous physical systems, ACL2 allows us to model digital systems, including many with physical realizations such as computer hardware. As early as the 1930's Church, Kleene, Turing and others established that recursive functions provide an expressive formalism for modeling digital computation. Digital computation should be understood in a broad sense, covering a wide variety of activities including almost any systematic or algorithmic activity, or activity that can be reasonably approximated in that way. This ranges from the behavior of a digital circuit to the behavior of a programming language compiler to the behavior of a controller for a physical system (as long as the system can be adequately modeled discretely). All of these have been modeled using ACL2 or its predecessor Nqthm. ACL2 is a ~b[computational] logic in at least three distinct senses. First, the theory of recursive functions is often considered the mathematics of computation. Church conjectured that any ``effective computation'' can be modeled as a recursive function. Thus, ACL2 provides an expressive language for modeling digital systems. Second, many ACL2 specifications are executable. In fact, recursive functions written in ACL2 ~b[are] Common Lisp functions that can be submitted to any compliant Common Lisp compiler and executed (in an environment where suitable ACL2-specific macros and functions are defined). Third, ACL2 is computational in the sense that calculation is heavily integrated into the reasoning process. Thus, an expression with explicit constant values but no free variables can be simplified by calculation rather than by complex logical manipulations. ACL2 is a powerful, automated ~b[theorem prover] or proof checker. This means that a competent user can utilize the ACL2 system to discover proofs of theorems stated in the ACL2 logic or to check previously discovered proofs. The basic deductive steps in an ACL2-checked proof are often quite large, due to the sophisticated combination of decision procedures, conditional rewriting, mathematical and structural induction, propositional simplification, and complex heuristics to orchestrate the interactions of these capabilities. Unlike some automated proof systems, ACL2 does not produce a formal proof. However, we believe that if ACL2 certifies the ``theoremhood'' of a given conjecture, then such a formal proof exists and, therefore, the theorem is valid. The ultimate result of an ACL2 proof session is a collection of ``~il[events],'' possibly grouped into ``~il[books],'' that can be replayed in ACL2. Therefore, a proof can be independently validated by any ACL2 user. ACL2 may be used in purely automated mode in the shallow sense that conjectures are submitted to the prover and the user does not interact with the proof attempt (except possibly to stop it) until the proof succeeds or fails. However, any non-trivial proof attempt is actually interactive, since successful proof ``~il[events]'' influence the subsequent behavior of the prover. For example, proving a lemma may introduce a rule that subsequently is used automatically by the prover. Thus, any realistic proof attempt, even in ``automatic'' mode, is really an interactive dialogue with the prover to craft a sequence of ~il[events] building an appropriate theory and proof rules leading up to the proof of the desired result. Also, ACL2 supports annotating a theorem with ``~il[hints]'' designed to guide the proof attempt. By supplying appropriate ~il[hints], the user can suggest proof strategies that the prover would not discover automatically. There is a ``~il[proof-tree]'' facility (~pl[proof-tree]) that allows the user to ~il[monitor] the progress and structure of a proof attempt in real-time. Exploring failed proof attempts is actually where heavy-duty ACL2 users spend most of their time. ACL2 can also be used in a more explicitly interactive mode. The ``~il[proof-checker]'' subsystem of ACL2 allows exploration of a proof on a fairly low level including expanding calls of selected function symbols, invoking specific ~il[rewrite] rules, and selectively navigating around the proof. This facility can be used to gain sufficient insight into the proof to construct an automatic version, or to generate a detailed interactive-style proof that can be replayed in batch mode. Because ACL2 is all of these things ~-[] computational logic, specification language, ~il[programming] system, and theorem prover ~-[] it is more than the sum of its parts. The careful integration of these diverse aspects has produced a versatile automated reasoning system suitable for building highly reliable digital systems. In the remainder of this tutorial, we will illustrate some simple uses of this automated reasoning system. ~em[ABOUT THIS TUTORIAL] ACL2 is a complex system with a vast array of features, bells and whistles. However, it is possible to perform productive work with the system using only a small portion of the available functionality. The goals of this tutorial are to: ~bq[] familiarize the new user with the most basic features of and modes of interaction with ACL2; familiarize her with the form of output of the system; and work through a graduated series of examples. ~eq[] The more knowledge the user brings to this system, the easier it will be to become proficient. On one extreme: the ~b[ideal] user of ACL2 is an expert Common Lisp programmer, has deep understanding of automated reasoning, and is intimately familiar with the earlier Nqthm system. Such ideal users are unlikely to need this tutorial. However, without some background knowledge, the beginning user is likely to become extremely confused and frustrated by this system. We suggest that a new user of ACL2 should: ~bq[] (a) have a little familiarity with Lisp, including basic Lisp programming and prefix notation (a Lisp reference manual such as Guy Steele's ``Common Lisp: The Language'' is also helpful); (b) be convinced of the utility of formal modeling; and (c) be willing to gain familiarity with basic automated theorem proving topics such as rewriting and algebraic simplification. ~eq[] We will not assume any deep familiarity with Nqthm (the so-called ``Boyer-Moore Theorem Prover''), though the book ``A Computational Logic Handbook'' by Boyer and Moore (Academic Press, 1988) is an extremely useful reference for many of the topics required to become a competent ACL2 user. We'll refer to it as ACLH below. As we said in the introduction, ACL2 has various facets. For example, it can be used as a Common Lisp ~il[programming] system to construct application programs. In fact, the ACL2 system itself is a large Common Lisp program constructed almost entirely within ACL2. Another use of ACL2 is as a specification and modeling tool. That is the aspect we will concentrate on in the remainder of this tutorial. ~em[GETTING STARTED] This section is an abridged version of what's available elsewhere; feel free to ~pl[startup] for more details. How you start ACL2 will be system dependent, but you'll probably type something like ``acl2'' at your operating system prompt. Consult your system administrator for details. When you start up ACL2, you'll probably find yourself inside the ACL2 ~il[command] loop, as indicated by the following ~il[prompt]. ~bv[] ACL2 !> ~ev[] If not, you should type ~c[(LP)]. ~l[lp], which has a lot more information about the ACL2 ~il[command] loop. There are two ``modes'' for using ACL2, ~c[:]~ilc[logic] and ~c[:]~ilc[program]. When you begin ACL2, you will ordinarily be in the ~c[:]~ilc[logic] mode. This means that any new function defined is not only executable but also is axiomatically defined in the ACL2 logic. (~l[defun-mode] and ~pl[default-defun-mode].) Roughly speaking, ~c[:]~ilc[program] mode is available for using ACL2 as a ~il[programming] language without some of the logical burdens necessary for formal reasoning. In this tutorial we will assume that we always remain in ~c[:]~ilc[logic] mode and that our purpose is to write formal models of digital systems and to reason about them. Now, within the ACL2 ~il[command] loop you can carry out various kinds of activities, including the folllowing. (We'll see examples later of many of these.) ~bq[] define new functions (~pl[defun]); execute functions on concrete data; pose and attempt to prove conjectures about previously defined functions (~pl[defthm]); query the ACL2 ``~il[world]'' or database (e.g., ~pl[pe]); and numerous other things. ~eq[] In addition, there is extensive on-line ~il[documentation], of which this tutorial introduction is a part. ~em[INTERACTING WITH ACL2] The standard means of interacting with ACL2 is to submit a sequence of forms for processing by the ACL2 system. These forms are checked for syntactic and semantic acceptability and appropriately processed by the system. These forms can be typed directly at the ACL2 ~il[prompt]. However, most successful ACL2 users prefer to do their work using the Emacs text editor, maintaining an Emacs ``working'' buffer in which forms are edited. Those forms are then copied to the ACL2 interaction buffer, which is often the ~c[\"*shell*\"] buffer. In some cases, processing succeeds and makes some change to the ACL2 ``logical ~il[world],'' which affects the processing of subsequent forms. How can this processing fail? For example, a proposed theorem will be rejected unless all function symbols mentioned have been previously defined. Also the ability of ACL2 to discover the proof of a theorem may depend on the user previously having proved other theorems. Thus, the order in which forms are submitted to ACL2 is quite important. Maintaining forms in an appropriate order in your working buffer will be helpful for re-playing the proof later. One of the most common ~il[events] in constructing a model is introducing new functions. New functions are usually introduced using the ~ilc[defun] form; we'll encounter some exceptions later. Proposed function definitions are checked to make sure that they are syntactically and semantically acceptable (e.g., that all mentioned functions have been previously defined) and, for recursive functions, that their recursive calls ~b[terminate]. A recursive function definition is guaranteed to terminate if there is some some ``measure'' of the arguments and a ``well-founded'' ordering such that the arguments to the function get smaller in each recursive call. ~l[well-founded-relation]. For example, suppose that we need a function that will append two lists together. (We already have one in the ACL2 ~ilc[append] function; but suppose perversely that we decide to define our own.) Suppose we submit the following definition (you should do so as well and study the system output): ~bv[] (defun my-app (x y) (if (atom x) y (cons (car x) (my-app x y)))) ~ev[] The system responds with the following message: ~bv[] ACL2 Error in ( DEFUN MY-APP ...): No :MEASURE was supplied with the definition of MY-APP. Our heuristics for guessing one have not made any suggestions. No argument of the function is tested along every branch and occurs as a proper subterm at the same argument position in every recursive call. You must specify a :MEASURE. See :DOC defun. ~ev[] This means that the system could not find an expression involving the formal parameters ~c[x] and ~c[y] that decreases under some well-founded order in every recursive call (there is only one such call). It should be clear that there is no such measure in this case because the only recursive call doesn't change the arguments at all. The definition is obviously flawed; if it were accepted and executed it would loop forever. Notice that a definition that is rejected is not stored in the system database; there is no need to take any action to have it ``thrown away.'' Let's try again with the correct definition. The interaction now looks like (we're also putting in the ACL2 ~il[prompt]; you don't type that): ~bv[] ACL2 !>(defun my-app (x y) (if (atom x) y (cons (car x) (my-app (cdr x) y)))) The admission of MY-APP is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We observe that the type of MY-APP is described by the theorem (OR (CONSP (MY-APP X Y)) (EQUAL (MY-APP X Y) Y)). We used primitive type reasoning. Summary Form: ( DEFUN MY-APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.07 seconds (prove: 0.00, print: 0.00, other: 0.07) MY-APP ~ev[] Notice that this time the function definition was accepted. We didn't have to supply a measure explicitly; the system inferred one from the form of the definition. On complex functions it may be necessary to supply a measure explicitly. (~l[xargs].) The system output provides several pieces of information. ~bq[] The revised definition is acceptable. The system realized that there is a particular measure (namely, ~c[(acl2-count x)]) and a well-founded relation (~c[o<]) under which the arguments of ~c[my-app] get smaller in recursion. Actually, the theorem prover proved several theorems to admit ~c[my-app]. The main one was that when ~c[(atom x)] is false the ~c[acl2-count] of ~c[(cdr x)] is less than (in the ~c[o<] sense) the ~c[acl2-count] of ~c[x]. ~ilc[Acl2-count] is the most commonly used measure of the ``size`` of an ACL2 object. ~ilc[o<] is the ordering relation on ordinals less than epsilon-0. On the natural numbers it is just ordinary ``<''. The observation printed about ``the type of MY-APP'' means that calls of the function ~c[my-app] will always return a value that is either a ~il[cons] pair or is equal to the second parameter. The summary provides information about which previously introduced definitions and lemmas were used in this proof, about some notable things to watch out for (the Warnings), and about how long this event took to process. ~eq[] Usually, it's not important to read this information. However, it is a good habit to scan it briefly to see if the type information is surprising to you or if there are Warnings. We'll see an example of them later. After a function is accepted, it is stored in the database and available for use in other function definitions or lemmas. To see the definition of any function use the ~c[:]~ilc[pe] command (~pl[pe]). For example, ~bv[] ACL2 !>:pe my-app L 73:x(DEFUN MY-APP (X Y) (IF (ATOM X) Y (CONS (CAR X) (MY-APP (CDR X) Y)))) ~ev[] This displays the definition along with some other relevant information. In this case, we know that this definition was processed in ~c[:]~ilc[logic] mode (the ``~c[L]'') and was the 73rd ~il[command] processed in the current session. We can also try out our newly defined function on some sample data. To do that, just submit a form to be evaluated to ACL2. For example, ~bv[] ACL2 !>(my-app '(0 1 2) '(3 4 5)) (0 1 2 3 4 5) ACL2 !>(my-app nil nil) NIL ACL2 !> ~ev[] Now suppose we want to prove something about the function just introduced. We conjecture, for example, that the length of the ~il[append] of two lists is the sum of their lengths. We can formulate this conjecture in the form of the following ACL2 ~ilc[defthm] form. ~bv[] (defthm my-app-length (equal (len (my-app x y)) (+ (len x) (len y)))) ~ev[] First of all, how did we know about the functions ~c[len] and ~ilc[+], etc.? The answer to that is somewhat unsatisfying ~-[] we know them from our past experience in using Common Lisp and ACL2. It's hard to know that a function such as ~c[len] exists without first knowing some Common Lisp. If we'd guessed that the appropriate function was called ~ilc[length] (say, from our knowledge of Lisp) and tried ~c[:pe length], we would have seen that ~ilc[length] is defined in terms of ~c[len], and we could have explored from there. Luckily, you can write a lot of ACL2 functions without knowing too many of the primitive functions. Secondly, why don't we need some ``type'' hypotheses? Does it make sense to append things that are not lists? Well, yes. ACL2 and Lisp are both quite weakly typed. For example, inspection of the definition of ~c[my-app] shows that if ~c[x] is not a ~il[cons] pair, then ~c[(my-app x y)] always returns ~c[y], no matter what ~c[y] is. Thirdly, would it matter if we rewrote the lemma with the equality reversed, as follows? ~bv[] (defthm my-app-length2 (equal (+ (len x) (len y)) (len (my-app x y)))). ~ev[] The two are ~b[logically] equivalent, but...yes, it would make a big difference. Recall our remark that a lemma is not only a ``fact'' to be proved; it also is used by the system to prove other later lemmas. The current lemma would be stored as a ~il[rewrite] rule. (~l[rule-classes].) For a ~il[rewrite] rule, a conclusion of the form ~c[(EQUAL LHS RHS)] means to replace instances of the ~c[LHS] by the appropriate instance of the ~c[RHS]. Presumably, it's better to ~il[rewrite] ~c[(len (my-app x y))] to ~c[(+ (len x) (len y))] than the other way around. The reason is that the system ``knows'' more about ~ilc[+] than it does about the new function symbol ~c[my-app]. So let's see if we can prove this lemma. Submitting our preferred ~ilc[defthm] to ACL2 (do it!), we get the following interaction: ~bv[] -------------------------------------------------- ACL2 !>(defthm my-app-length (equal (len (my-app x y)) (+ (len x) (len y)))) Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. These merge into two derived induction schemes. However, one of these is flawed and so we are left with one viable candidate. We will induct according to a scheme suggested by (LEN X), but modified to accommodate (MY-APP X Y). If we let (:P X Y) denote *1 above then the induction scheme we'll use is (AND (IMPLIES (NOT (CONSP X)) (:P X Y)) (IMPLIES (AND (CONSP X) (:P (CDR X) Y)) (:P X Y))). This induction is justified by the same argument used to admit LEN, namely, the measure (ACL2-COUNT X) is decreasing according to the relation O< (which is known to be well-founded on the domain recognized by O-P). When applied to the goal at hand the above induction scheme produces the following two nontautological subgoals. Subgoal *1/2 (IMPLIES (NOT (CONSP X)) (EQUAL (LEN (MY-APP X Y)) (+ (LEN X) (LEN Y)))). But simplification reduces this to T, using the :definitions of FIX, LEN and MY-APP, the :type-prescription rule LEN, the :rewrite rule UNICITY-OF-0 and primitive type reasoning. Subgoal *1/1 (IMPLIES (AND (CONSP X) (EQUAL (LEN (MY-APP (CDR X) Y)) (+ (LEN (CDR X)) (LEN Y)))) (EQUAL (LEN (MY-APP X Y)) (+ (LEN X) (LEN Y)))). This simplifies, using the :definitions of LEN and MY-APP, primitive type reasoning and the :rewrite rules COMMUTATIVITY-OF-+ and CDR-CONS, to Subgoal *1/1' (IMPLIES (AND (CONSP X) (EQUAL (LEN (MY-APP (CDR X) Y)) (+ (LEN Y) (LEN (CDR X))))) (EQUAL (+ 1 (LEN (MY-APP (CDR X) Y))) (+ (LEN Y) 1 (LEN (CDR X))))). But simplification reduces this to T, using linear arithmetic, primitive type reasoning and the :type-prescription rule LEN. That completes the proof of *1. Q.E.D. Summary Form: ( DEFTHM MY-APP-LENGTH ...) Rules: ((:REWRITE UNICITY-OF-0) (:DEFINITION FIX) (:REWRITE COMMUTATIVITY-OF-+) (:DEFINITION LEN) (:REWRITE CDR-CONS) (:DEFINITION MY-APP) (:TYPE-PRESCRIPTION LEN) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:FAKE-RUNE-FOR-LINEAR NIL)) Warnings: None Time: 0.30 seconds (prove: 0.13, print: 0.05, other: 0.12) MY-APP-LENGTH -------------------------------------------------- ~ev[] Wow, it worked! In brief, the system first tried to ~il[rewrite] and simplify as much as possible. Nothing changed; we know that because it said ``Name the formula above *1.'' Whenever the system decides to name a formula in this way, we know that it has run out of techniques to use other than proof by induction. The induction performed by ACL2 is structural or ``Noetherian'' induction. You don't need to know much about that except that it is induction based on the structure of some object. The heuristics infer the structure of the object from the way the object is recursively decomposed by the functions used in the conjecture. The heuristics of ACL2 are reasonably good at selecting an induction scheme in simple cases. It is possible to override the heuristic choice by providing an ~c[:induction] hint (~pl[hints]). In the case of the theorem above, the system inducts on the structure of ~c[x] as suggested by the decomposition of ~c[x] in both ~c[(my-app x y)] and ~c[(len x)]. In the base case, we assume that ~c[x] is not a ~ilc[consp]. In the inductive case, we assume that it is a ~ilc[consp] and assume that the conjecture holds for ~c[(cdr x)]. There is a close connection between the analysis that goes on when a function like ~c[my-app] is accepted and when we try to prove something inductively about it. That connection is spelled out well in Boyer and Moore's book ``A Computational Logic,'' if you'd like to look it up. But it's pretty intuitive. We accepted ~c[my-app] because the ``size'' of the first argument ~c[x] decreases in the recursive call. That tells us that when we need to prove something inductively about ~c[my-app], it's a good idea to try an induction on the size of the first argument. Of course, when you have a theorem involving several functions, it may be necessary to concoct a more complicated ~il[induction] schema, taking several of them into account. That's what's meant by ``merging'' the induction schemas. The proof involves two cases: the base case, and the inductive case. You'll notice that the subgoal numbers go ~b[down] rather than up, so you always know how many subgoals are left to process. The base case (~c[Subgoal *1/2]) is handled by opening up the function definitions, simplifying, doing a little rewriting, and performing some reasoning based on the types of the arguments. You'll often encounter references to system defined lemmas (like ~c[unicity-of-0]). You can always look at those with ~c[:]~ilc[pe]; but, in general, assume that there's a lot of simplification power under the hood that's not too important to understand fully. The inductive case (~c[Subgoal *1/1]) is also dispatched pretty easily. Here we assume the conjecture true for the ~ilc[cdr] of the list and try to prove it for the entire list. Notice that the prover does some simplification and then prints out an updated version of the goal (~c[Subgoal *1/1']). Examining these gives you a pretty good idea of what's going on in the proof. Sometimes one goal is split into a number of subgoals, as happened with the induction above. Sometimes after some initial processing the prover decides it needs to prove a subgoal by induction; this subgoal is given a name and pushed onto a stack of goals. Some steps, like generalization (see ACLH), are not necessarily validity preserving; that is, the system may adopt a false subgoal while trying to prove a true one. (Note that this is ok in the sense that it is not ``unsound.'' The system will fail in its attempt to establish the false subgoal and the main proof attempt will fail.) As you gain facility with using the prover, you'll get pretty good at recognizing what to look for when reading a proof script. The prover's ~il[proof-tree] utility helps with monitoring an ongoing proof and jumping to designated locations in the proof (~pl[proof-tree]). ~l[tips] for a number of useful pointers on using the theorem prover effectively. When the prover has successfully proved all subgoals, the proof is finished. As with a ~ilc[defun], a summary of the proof is printed. This was an extremely simple proof, needing no additional guidance. More realistic examples typically require the user to look carefully at the failed proof log to find ways to influence the prover to do better on its next attempt. This means either: proving some rules that will then be available to the prover, changing the global state in ways that will affect the proof, or providing some ~il[hints] locally that will influence the prover's behavior. Proving this lemma (~c[my-app-length]) is an example of the first. Since this is a ~il[rewrite] rule, whenever in a later proof an instance of the form ~c[(LEN (MY-APP X Y))] is encountered, it will be rewritten to the corresponding instance of ~c[(+ (LEN X) (LEN Y))]. Disabling the rule by executing the ~il[command] ~bv[] (in-theory (disable my-app-length)), ~ev[] is an example of a global change to the behavior of the prover since this ~il[rewrite] will not be performed subsequently (unless the rule is again ~il[enable]d). Finally, we can add a (local) ~il[disable] ``hint'' to a ~ilc[defthm], meaning to ~il[disable] the lemma only in the proof of one or more subgoals. For example: ~bv[] (defthm my-app-length-commutativity (equal (len (my-app x y)) (len (my-app y x))) :hints ((\"Goal\" :in-theory (disable my-app-length)))) ~ev[] In this case, the hint supplied is a bad idea since the proof is much harder with the hint than without it. Try it both ways. By the way, to undo the previous event use ~c[:u] (~pl[u]). To undo back to some earlier event use ~c[:ubt] (~pl[ubt]). To view the current event use ~c[:pe :here]. To list several ~il[events] use ~c[:pbt] (~pl[pbt]). Notice the form of the hint in the previous example (~pl[hints]). It specifies a goal to which the hint applies. ~c[\"Goal\"] refers to the top-level goal of the theorem. Subgoals are given unique names as they are generated. It may be useful to suggest that a function symbol be ~il[disable]d only for Subgoal 1.3.9, say, and a different function ~il[enable]d only on Subgoal 5.2.8. Overuse of such ~il[hints] often suggests a poor global proof strategy. We now recommend that you visit ~il[documentation] on additional examples. ~l[annotated-acl2-scripts].") (deflabel annotated-acl2-scripts :doc ":Doc-Section ACL2-Tutorial examples of ACL2 scripts~/ Beginning users may find these annotated scripts useful. We suggest that you read these in the following order: ~bf[] ~il[Tutorial1-Towers-of-Hanoi] ~il[Tutorial2-Eights-Problem] ~il[Tutorial3-Phonebook-Example] ~il[Tutorial4-Defun-Sk-Example] ~il[Tutorial5-Miscellaneous-Examples] ~ef[] The page ~url[http://www.cs.utexas.edu/users/moore/publications/tutorial/rev3.html] contains a script that illustrates how it feels to use The Method to prove an unusual list reverse function correct. The screen shots of ACL2's proof output are outdated -- in the version shown, ACL2 does not print Key Checkpoints, but the concept of key checkpoint is clear in the discussion and the behavior of the user. See ~url[http://www.cs.utexas.edu/users/moore/acl2/contrib/POLISHING-PROOFS-TUTORIAL.html] for a tutorial on becoming successful at approaching a formalization and proof problem in ACL2. That tutorial, written by Shilpi Goel and Sandip Ray, has two parts: it illustrates how to guide the theorem prover to a successful proof, and it shows how to clean up the proof in order to facilitate maintenance and extension of the resulting book (~pl[books]). At ~url[http://www.cs.utexas.edu/users/moore/publications/tutorial/kaufmann-TPHOLs08/index.html] is the demo given by Matt Kaufmann at TPHOLs08, including all the scripts. There is a gzipped tar file containing the entire contents of the demos. At ~url[http://www.cs.utexas.edu/users/moore/publications/tutorial/sort-equivalence] is a collection of scripts illustrating both high-level strategy and lower-level tactics dealing with the functional equivalence of various list sorting algorithms. Start with the ~c[README] on that directory. There is also a gzipped tar file containing all the scripts, at ~url[http://www.cs.utexas.edu/users/moore/publications/tutorial/sort-equivalence.tgz]. When you feel you have read enough examples, you might want to try the following very simple example on your own. (~l[solution-to-simple-example] for a solution, after you work on this example.) First define the notion of the ``fringe'' of a tree, where we identify trees simply as ~il[cons] structures, with ~il[atom]s at the leaves. For example: ~bv[] ACL2 !>(fringe '((a . b) c . d)) (A B C D) ~ev[] Next, define the notion of a ``leaf'' of a tree, i.e., a predicate ~c[leaf-p] that is true of an atom if and only if that atom appears at the tip of the tree. Define this notion without referencing the function ~c[fringe]. Finally, prove the following theorem, whose proof may well be automatic (i.e., not require any lemmas). ~bv[] (defthm leaf-p-iff-member-fringe (iff (leaf-p atm x) (member-equal atm (fringe x)))) ~ev[] ~/~/") (deflabel Emacs :doc ":Doc-Section ACL2-Tutorial emacs support for ACL2~/ Many successful ACL2 users run in an shell under the Emacs editor. If you do so, then you may wish to load the distributed file ~c[emacs/emacs-acl2.el]. The file begins with considerable comments describing what it offers. It is intended to work both with GNU Emacs and XEmacs. If you are not comfortable with Emacs, you may prefer to use an Eclipse-based interface; ~pl[acl2-sedan].~/~/") (deflabel ACL2-As-Standalone-Program :doc ":Doc-Section ACL2-Tutorial Calling ACL2 from another program~/ ACL2 is intended for interactive use. It is generally unrealistic to expect it to prove theorems fully automatically; ~pl[the-method], and ~pl[introduction-to-the-theorem-prover] for a more detailed tutorial. Nevertheless, here we describe an approach for how to call the ACL2 theorem prover noninteractively. These steps can of course be modified according to your needs. Here, we illustrate how to call ACL2 from another Lisp program (or an arbitrary program) to attempt to prove an arithmetic theorem. ~b[=== STEP 1: ===] Build a suitable ACL2 image by starting ACL2 and then executing the following forms. In particular, these define a macro, ~c[try-thm], that causes ACL2 to exit with with an exit status indicating success or failure of a proof attempt. ~bv[] (include-book \"arithmetic-5/top\" :dir :system) (defmacro try-thm (&rest args) `(mv-let (erp val state) (with-prover-time-limit 3 (thm ,@args)) (declare (ignore val)) (prog2$ (if erp (exit 1) (exit 0)) state)))) (reset-prehistory) ; optional :q (save-exec \"arith-acl2\" \"Included arithmetic-4/top\") ~ev[] If you prefer, above you can replace 3 by some other number of seconds as a time limit for the prover. Also, you can replace ~bv[] (with-prover-time-limit 3 (thm ,@args)) ~ev[] by ~bv[] (with-output :off :all (with-prover-time-limit 3 (thm ,@args))) ~ev[] if you want to turn off output. It may be best to leave the output on, instead eliminating it in the calling program (see Step 3 below). ~b[=== STEP 2: ===] Try a little test. In that same directory try this: ~bv[] echo '(try-thm (equal x x))' | ./arith-acl2 echo $? ~ev[] The exit status should be 0, indicating success. Now try this: ~bv[] echo '(try-thm (not (equal x x)))' | ./arith-acl2 echo $? ~ev[] The exit status should be 1, indicating failure. ~b[=== STEP 3: ===] Create a shell script that automates Step 2, for example: ~bv[] #!/bin/sh (echo \"(try-thm $1)\" | ./arith-acl2) >& /dev/null exit $? ~ev[] ~b[=== STEP 4: ===] Try your script from a Lisp program, if you like. Here is how you can do it in SBCL, for example. (Different Lisps have different ways to do this, as summarized in function ~c[system-call] in ACL2 source file ~c[acl2-init.lisp].) ~bv[] (defun provable? (x) (let ((status (process-exit-code (sb-ext:run-program \"./try-thm.sh\" (list (format nil \"~~s\" x)) :output t :search t)))) (eql status 0))) ~ev[] Then here is a log: ~bv[] * (provable? '(equal x y)) NIL * (provable? '(equal x x)) T * ~ev[] Certainly refinements are possible -- for example the above doesn't distinguish between unprovable and ill-formed input. But it's a start.~/~/") (deflabel acl2-sedan :doc ":Doc-Section ACL2-Tutorial ACL2 Sedan interface~/ Many successful ACL2 users run in an shell under Emacs; ~pl[emacs]. However, those not familiar with Emacs may prefer to start with an Eclipse-based interface initiallly developed by Peter Dillinger and Pete Manolios called the ``ACL2 Sedan'', or ``ACL2s''. As of this writing, the home page for ACL2s is ~url[http://acl2s.ccs.neu.edu/acl2s/doc/].~/ ACL2 sessions in the ACL2 Sedan can utilize non-standard extensions and enhancements, especially geared toward new users, termination reasoning, and attaching rich user interfaces. These extensions are generally available as certifiable ACL2 books, and can be downloaded from ~url[http://acl2s.ccs.neu.edu/acl2s/src/acl2-extensions]. (Some code originating from this project has been migrated to the ACL2 community books, but only after it was quite stable.) Thanks to Peter Dillinger, Pete Manolios, Daron Vroon, and Harsh Raju Chamarthi for their work on the ACL2 Sedan and for making their books available to ACL2 users. ~/") (deflabel solution-to-simple-example :doc ":Doc-Section Annotated-Acl2-Scripts solution to a simple example~/ To see a statement of the problem solved below, ~pl[annotated-acl2-scripts] (in particular the end of that topic).~/ Here is a sequence of ACL2 ~il[events] that illustrates the use of ACL2 to make definitions and prove theorems. We will introduce the notion of the fringe of a tree, as well as the notion of a leaf of a tree, and then prove that the members of the fringe are exactly the leaves. We begin by defining the fringe of a tree, where we identify trees simply as ~il[cons] structures, with ~il[atom]s at the leaves. The definition is recursive, breaking into two cases. If ~c[x] is a ~il[cons], then the ~c[fringe] of ~c[x] is obtained by appending together the ~c[fringe]s of the ~ilc[car] and ~ilc[cdr] (left and right child) of ~c[x]. Otherwise, ~c[x] is an ~il[atom] and its ~c[fringe] is the one-element list containing only ~c[x]. ~bv[] (defun fringe (x) (if (consp x) (append (fringe (car x)) (fringe (cdr x))) (list x))) ~ev[] Now that ~c[fringe] has been defined, let us proceed by defining the notion of an atom appearing as a ``leaf'', with the goal of proving that the leaves of a tree are exactly the members of its ~c[fringe]. ~bv[] (defun leaf-p (atm x) (if (consp x) (or (leaf-p atm (car x)) (leaf-p atm (cdr x))) (equal atm x))) ~ev[] The main theorem is now as follows. Note that the rewrite rule below uses the equivalence relation ~ilc[iff] (~pl[equivalence]) rather than ~ilc[equal], since ~ilc[member] returns the tail of the given list that begins with the indicated member, rather than returning a Boolean. (Use ~c[:pe member] to see the definition of ~ilc[member].) ~bv[] (defthm leaf-p-iff-member-fringe (iff (leaf-p atm x) (member-equal atm (fringe x)))) ~ev[] ") (deflabel Tutorial1-Towers-of-Hanoi :doc ":Doc-Section Annotated-Acl2-Scripts The Towers of Hanoi Example~/ This example was written almost entirely by Bill Young of Computational Logic, Inc.~/ We will formalize and prove a small theorem about the famous ``Towers of Hanoi'' problem. This problem is illustrated by the following picture. ~bv[] | | | | | | --- | | ----- | | ------- | | A B C ~ev[] We have three pegs ~-[] ~c[a], ~c[b], and ~c[c] ~-[] and ~c[n] disks of different sizes. The disks are all initially on peg ~c[a]. The goal is to move all disks to peg ~c[c] while observing the following two rules. 1. Only one disk may be moved at a time, and it must start and finish the move as the topmost disk on some peg; 2. A disk can never be placed on top of a smaller disk. Let's consider some simple instances of this problem. If ~c[n] = 1, i.e., only one disk is to be moved, simply move it from ~c[a] to ~c[c]. If ~c[n] = 2, i.e., two disks are to be moved, the following sequence of moves suffices: move from ~c[a] to ~c[b], move from ~c[a] to ~c[c], move from ~c[b] to ~c[c]. In this doc topic we will show an ACL2 function that generates a suitable list of moves for a tower of ~c[n] disks. Then we will use ACL2 to prove that the number of moves is ~c[(- (expt 2 n) 1)]. For an ACL2 script that proves the correctness of (a version of) this function, see the community book ~c[\"misc/hanoi.lisp\"] in the ~c[books] directory of your ACL2 sources. In general, this problem has a straightforward recursive solution. Suppose that we desire to move ~c[n] disks from ~c[a] to ~c[c], using ~c[b] as the intermediate peg. For the basis, we saw above that we can always move a single disk from ~c[a] to ~c[c]. Now if we have ~c[n] disks and assume that we can solve the problem for ~c[n-1] disks, we can move ~c[n] disks as follows. First, move ~c[n-1] disks from ~c[a] to ~c[b] using ~c[c] as the intermediate peg; move the single disk from ~c[a] to ~c[c]; then move ~c[n-1] disks from ~c[b] to ~c[c] using ~c[a] as the intermediate peg. In ACL2, we can write a function that will return the sequence of moves. One such function is as follows. Notice that we have two base cases. If ~c[(zp n)] then ~c[n] is not a positive integer; we treat that case as if ~c[n] were 0 and return an empty list of moves. If ~c[n] is 1, then we return a list containing the single appropriate move. Otherwise, we return the list containing exactly the moves dictated by our recursive analysis above. ~bv[] (defun move (a b) (list 'move a 'to b)) (defun hanoi (a b c n) (if (zp n) nil (if (equal n 1) (list (move a c)) (append (hanoi a c b (1- n)) (cons (move a c) (hanoi b a c (1- n))))))) ~ev[] Notice that we give ~c[hanoi] four arguments: the three pegs, and the number of disks to move. It is necessary to supply the pegs because, in recursive calls, the roles of the pegs differ. In any execution of the algorithm, a given peg will sometimes be the source of a move, sometimes the destination, and sometimes the intermediate peg. After submitting these functions to ACL2, we can execute the ~c[hanoi] function on various specific arguments. For example: ~bv[] ACL2 !>(hanoi 'a 'b 'c 1) ((MOVE A TO C)) ACL2 !>(hanoi 'a 'b 'c 2) ((MOVE A TO B) (MOVE A TO C) (MOVE B TO C)) ACL2 !>(hanoi 'a 'b 'c 3) ((MOVE A TO C) (MOVE A TO B) (MOVE C TO B) (MOVE A TO C) (MOVE B TO A) (MOVE B TO C) (MOVE A TO C)) ~ev[] From the algorithm it is clear that if it takes ~c[m] moves to transfer ~c[n] disks, it will take ~c[(m + 1 + m) = 2m + 1] moves for ~c[n+1] disks. From some simple calculations, we see that we need the following number of moves in specific cases: ~bv[] Disks 0 1 2 3 4 5 6 7 ... Moves 0 1 3 7 15 31 63 127 ... ~ev[] The pattern is fairly clear. To move ~c[n] disks requires ~c[(2^n - 1)] moves. Let's attempt to use ACL2 to prove that fact. First of all, how do we state our conjecture? Recall that ~c[hanoi] returns a list of moves. The length of the list (given by the function ~c[len]) is the number of moves required. Thus, we can state the following conjecture. ~bv[] (defthm hanoi-moves-required-first-try (equal (len (hanoi a b c n)) (1- (expt 2 n)))) ~ev[] When we submit this to ACL2, the proof attempt fails. Along the way we notice subgoals such as: ~bv[] Subgoal *1/1' (IMPLIES (NOT (< 0 N)) (EQUAL 0 (+ -1 (EXPT 2 N)))). ~ev[] This tells us that the prover is considering cases that are uninteresting to us, namely, cases in which ~c[n] might be negative. The only cases that are really of interest are those in which ~c[n] is a non-negative natural number. Therefore, we revise our theorem as follows: ~bv[] (defthm hanoi-moves-required (implies (and (integerp n) (<= 0 n)) ;; n is at least 0 (equal (len (hanoi a b c n)) (1- (expt 2 n))))) ~ev[] and submit it to ACL2 again. Again the proof fails. Examining the proof script we encounter the following text. (How did we decide to focus on this goal? Some information is provided in ACLH, and the ACL2 documentation on ~il[tips] may be helpful. But the simplest answer is: this was the first goal suggested by the ``~il[proof-tree]'' tool below the start of the proof by induction. ~l[proof-tree].) ~bv[] Subgoal *1/5'' (IMPLIES (AND (INTEGERP N) (< 0 N) (NOT (EQUAL N 1)) (EQUAL (LEN (HANOI A C B (+ -1 N))) (+ -1 (EXPT 2 (+ -1 N)))) (EQUAL (LEN (HANOI B A C (+ -1 N))) (+ -1 (EXPT 2 (+ -1 N))))) (EQUAL (LEN (APPEND (HANOI A C B (+ -1 N)) (CONS (LIST 'MOVE A 'TO C) (HANOI B A C (+ -1 N))))) (+ -1 (* 2 (EXPT 2 (+ -1 N)))))) ~ev[] It is difficult to make much sense of such a complicated goal. However, we do notice something interesting. In the conclusion is a ~il[term] of the following shape. ~bv[] (LEN (APPEND ... ...)) ~ev[] We conjecture that the length of the ~ilc[append] of two lists should be the sum of the lengths of the lists. If the prover knew that, it could possibly have simplified this ~il[term] earlier and made more progress in the proof. Therefore, we need a ~il[rewrite] rule that will suggest such a simplification to the prover. The appropriate rule is: ~bv[] (defthm len-append (equal (len (append x y)) (+ (len x) (len y)))) ~ev[] We submit this to the prover, which proves it by a straightforward induction. The prover stores this lemma as a ~il[rewrite] rule and will subsequently (unless we ~il[disable] the rule) replace ~il[term]s matching the left hand side of the rule with the appropriate instance of the ~il[term] on the right hand side. We now resubmit our lemma ~c[hanoi-moves-required] to ACL2. On this attempt, the proof succeeds and we are done. One bit of cleaning up is useful. We needed the hypotheses that: ~bv[] (and (integerp n) (<= 0 n)). ~ev[] This is an awkward way of saying that ~c[n] is a natural number; natural is not a primitive data type in ACL2. We could define a function ~c[naturalp], but it is somewhat more convenient to define a macro as follows: ~bv[] (defmacro naturalp (x) (list 'and (list 'integerp x) (list '<= 0 x))) ~ev[] Subsequently, we can use ~c[(naturalp n)] wherever we need to note that a quantity is a natural number. ~l[defmacro] for more information about ACL2 macros. With this macro, we can reformulate our theorem as follows: ~bv[] (defthm hanoi-moves-required (implies (naturalp n) (equal (len (hanoi a b c n)) (1- (expt 2 n))))). ~ev[] Another interesting (but much harder) theorem asserts that the list of moves generated by our ~c[hanoi] function actually accomplishes the desired goal while following the rules. When you can state and prove that theorem, you'll be a very competent ACL2 user. By the way, the name ``Towers of Hanoi'' derives from a legend that a group of Vietnamese monks works day and night to move a stack of 64 gold disks from one diamond peg to another, following the rules set out above. We're told that the world will end when they complete this task. From the theorem above, we know that this requires 18,446,744,073,709,551,615 moves: ~bv[] ACL2 !>(1- (expt 2 64)) 18446744073709551615 ACL2 !> ~ev[] We're guessing they won't finish any time soon.") (deflabel Tutorial2-Eights-Problem :doc ":Doc-Section Annotated-Acl2-Scripts The Eights Problem Example~/ This example was written almost entirely by Bill Young of Computational Logic, Inc.~/ This simple example was brought to our attention as one that Paul Jackson has solved using the NuPrl system. The challenge is to prove the theorem: ~bv[] for all n > 7, there exist naturals i and j such that: n = 3i + 5j. ~ev[] In ACL2, we could phrase this theorem using quantification. However we will start with a constructive approach, i.e., we will show that values of ~c[i] and ~c[j] exist by writing a function that will construct such values for given ~c[n]. Suppose we had a function ~c[(split n)] that returns an appropriate pair ~c[(i . j)]. Our theorem would be as follows: ~bv[] (defthm split-splits (let ((i (car (split n))) (j (cdr (split n)))) (implies (and (integerp n) (< 7 n)) (and (integerp i) (<= 0 i) (integerp j) (<= 0 j) (equal (+ (* 3 i) (* 5 j)) n))))) ~ev[] That is, assuming that ~c[n] is a natural number greater than 7, ~c[(split n)] returns values ~c[i] and ~c[j] that are in the appropriate relation to ~c[n]. Let's look at a few cases: ~bv[] 8 = 3x1 + 5x1; 11 = 3x2 + 5x1; 14 = 3x3 + 5x1; ... 9 = 3x3 + 5x0; 12 = 3x4 + 5x0; 15 = 3x5 + 5x0; ... 10 = 3x0 + 5x2; 13 = 3x1 + 5x2; 16 = 3x2 + 5x2; ... ~ev[] Maybe you will have observed a pattern here; any natural number larger than 10 can be obtained by adding some multiple of 3 to 8, 9, or 10. This gives us the clue to constructing a proof. It is clear that we can write split as follows: ~bv[] (defun bump-i (x) ;; Bump the i component of the pair ;; (i . j) by 1. (cons (1+ (car x)) (cdr x))) (defun split (n) ;; Find a pair (i . j) such that ;; n = 3i + 5j. (if (or (zp n) (< n 8)) nil ;; any value is really reasonable here (if (equal n 8) (cons 1 1) (if (equal n 9) (cons 3 0) (if (equal n 10) (cons 0 2) (bump-i (split (- n 3)))))))) ~ev[] Notice that we explicitly compute the values of ~c[i] and ~c[j] for the cases of 8, 9, and 10, and for the degenerate case when ~c[n] is not a natural or is less than 8. For all naturals greater than ~c[n], we decrement ~c[n] by 3 and bump the number of 3's (the value of i) by 1. We know that the recursion must terminate because any integer value greater than 10 can eventually be reduced to 8, 9, or 10 by successively subtracting 3. Let's try it on some examples: ~bv[] ACL2 !>(split 28) (6 . 2) ACL2 !>(split 45) (15 . 0) ACL2 !>(split 335) (110 . 1) ~ev[] Finally, we submit our theorem ~c[split-splits], and the proof succeeds. In this case, the prover is ``smart'' enough to induct according to the pattern indicated by the function split. For completeness, we'll note that we can state and prove a quantified version of this theorem. We introduce the notion ~c[split-able] to mean that appropriate ~c[i] and ~c[j] exist for ~c[n]. ~bv[] (defun-sk split-able (n) (exists (i j) (equal n (+ (* 3 i) (* 5 j))))) ~ev[] Then our theorem is given below. Notice that we prove it by observing that our previous function ~c[split] delivers just such an ~c[i] and ~c[j] (as we proved above). ~bv[] (defthm split-splits2 (implies (and (integerp n) (< 7 n)) (split-able n)) :hints ((\"Goal\" :use (:instance split-able-suff (i (car (split n))) (j (cdr (split n))))))) ~ev[] Unfortunately, understanding the mechanics of the proof requires knowing something about the way ~ilc[defun-sk] works. ~l[defun-sk] or ~pl[Tutorial4-Defun-Sk-Example] for more on that subject.") (deflabel Tutorial3-Phonebook-Example ; Here is another solution to the exercise at the end of this topic. ; (defun good-phonebookp (bk) ; (setp (range bk))) ; ; (defthm member-equal-strip-cdrs-bind ; (implies (and (not (member-equal x (strip-cdrs bk))) ; (not (equal x num))) ; (not (member-equal x (strip-cdrs (bind nm num bk)))))) ; ; (defthm setp-range-bind ; (implies (and (setp (range bk)) ; (not (member num (range bk)))) ; (setp (range (bind nm num bk)))) ; :hints (("Goal" :in-theory (enable bind range)))) ; ; (defthm ADD-PHONE-PRESERVES-NEW-INVARIANT ; (implies (and (good-phonebookp bk) ; (not (member num (range bk)))) ; (good-phonebookp (add-phone nm num bk)))) ; ; (defthm CHANGE-PHONE-PRESERVES-NEW-INVARIANT ; (implies (and (good-phonebookp bk) ; (not (member num (range bk)))) ; (good-phonebookp (change-phone nm num bk)))) ; ; (defthm member-equal-strip-cdrs-rembind ; (implies (not (member-equal x (strip-cdrs bk))) ; (not (member-equal x (strip-cdrs (rembind nm bk)))))) ; ; (defthm setp-strip-cdrs-rembind ; (implies (setp (strip-cdrs bk)) ; (setp (strip-cdrs (rembind nm bk)))) ; :hints (("Goal" :in-theory (enable rembind)))) ; ; (defthm DEL-PHONE-PRESERVES-NEW-INVARIANT ; (implies (good-phonebookp bk) ; (good-phonebookp (del-phone nm bk))) ; :hints (("Goal" :in-theory (enable range)))) :doc ":Doc-Section Annotated-Acl2-Scripts A Phonebook Specification~/ The other tutorial examples are rather small and entirely self contained. The present example is rather more elaborate, and makes use of a feature that really adds great power and versatility to ACL2, namely: the use of previously defined collections of lemmas, in the form of ``~il[books].'' This example was written almost entirely by Bill Young of Computational Logic, Inc.~/ This example is based on one developed by Ricky Butler and Sally Johnson of NASA Langley for the PVS system, and subsequently revised by Judy Crow, ~i[et al], at SRI. It is a simple phone book specification. We will not bother to follow their versions closely, but will instead present a style of specification natural for ACL2. The idea is to model an electronic phone book with the following properties. ~bq[] Our phone book will store the phone numbers of a city. It must be possible to retrieve a phone number, given a name. It must be possible to add and delete entries. ~eq[] Of course, there are numerous ways to construct such a model. A natural approach within the Lisp/ACL2 context is to use ``association lists'' or ``alists.'' Briefly, an alist is a list of pairs ~c[(key . value)] associating a value with a key. A phone book could be an alist of pairs ~c[(name . pnum)]. To find the phone number associated with a given name, we merely search the alist until we find the appropriate pair. For a large city, such a linear list would not be efficient, but at this point we are interested only in ~b[modeling] the problem, not in deriving an efficient implementation. We could address that question later by proving our alist model equivalent, in some desired sense, to a more efficient data structure. We could build a theory of alists from scratch, or we can use a previously constructed theory (book) of alist definitions and facts. By using an existing book, we build upon the work of others, start our specification and proof effort from a much richer foundation, and hopefully devote more of our time to the problem at hand. Unfortunately, it is not completely simple for the new user to know what ~il[books] are available and what they contain. We hope later to improve the documentation of the growing collection of community ~il[books] that are typically downloaded with ACL2; for now, the reader is encouraged to look in the README.html file in the books' top-level directory. For present purposes, the beginning user can simply take our word that a book exists containing useful alist definitions and facts. These definitions and lemmas can be introduced into the current theory using the ~il[command]: ~bv[] (include-book \"data-structures/alist-defthms\" :dir :system) ~ev[] This book has been ``certified,'' which means that the definitions and lemmas have been mechanically checked and stored in a safe manner. (~l[books] and ~pl[include-book] for details.) Including this book makes available a collection of functions including the following: ~bv[] (ALISTP A) ; is A an alist (actually a primitive ACL2 function) (BIND X V A) ; associate the key X with value V in alist A (BINDING X A) ; return the value associated with key X in alist A (BOUND? X A) ; is key X associated with any value in alist A (DOMAIN A) ; return the list of keys bound in alist A (RANGE A) ; return the list of values bound to keys in alist A (REMBIND X A) ; remove the binding of key X in alist A ~ev[] Along with these function definitions, the book also provides a number of proved lemmas that aid in simplifying expressions involving these functions. (~l[rule-classes] for the way in which lemmas are used in simplification and rewriting.) For example, ~bv[] (defthm bound?-bind (equal (bound? x (bind y v a)) (or (equal x y) (bound? x a)))) ~ev[] asserts that ~c[x] will be bound in ~c[(bind y v a)] if and only if: either ~c[x = y] or ~c[x] was already bound in ~c[a]. Also, ~bv[] (defthm binding-bind (equal (binding x (bind y v a)) (if (equal x y) v (binding x a)))) ~ev[] asserts that the resulting binding will be ~c[v], if ~c[x = y], or the binding that ~c[x] had in ~c[a] already, if not. Thus, the inclusion of this book essentially extends our specification and reasoning capabilities by the addition of new operations and facts about these operations that allow us to build further specifications on a richer and possibly more intuitive foundation. However, it must be admitted that the use of a book such as this has two potential limitations: ~bq[] the definitions available in a book may not be ideal for your particular problem; it is (extremely) likely that some useful facts (especially, ~il[rewrite] rules) are not available in the book and will have to be proved. ~eq[] For example, what is the value of ~c[binding] when given a key that is not bound in the alist? We can find out by examining the function definition. Look at the definition of the ~c[binding] function (or any other defined function), using the ~c[:]~ilc[pe] command: ~bv[] ACL2 !>:pe binding d 33 (INCLUDE-BOOK \"/slocal/src/acl2/v1-9/books/public/alist-defthms\") \ >V d (DEFUN BINDING (X A) \"The value bound to X in alist A.\" (DECLARE (XARGS :GUARD (ALISTP A))) (CDR (ASSOC-EQUAL X A))) ~ev[] This tells us that ~c[binding] was introduced by the given ~ilc[include-book] form, is currently ~il[disable]d in the current theory, and has the definition given by the displayed ~ilc[defun] form. We see that ~c[binding] is actually defined in terms of the primitive ~ilc[assoc-equal] function. If we look at the definition of ~ilc[assoc-equal]: ~bv[] ACL2 !>:pe assoc-equal V -489 (DEFUN ASSOC-EQUAL (X ALIST) (DECLARE (XARGS :GUARD (ALISTP ALIST))) (COND ((ENDP ALIST) NIL) ((EQUAL X (CAR (CAR ALIST))) (CAR ALIST)) (T (ASSOC-EQUAL X (CDR ALIST))))) ~ev[] we can see that ~ilc[assoc-equal] returns ~c[nil] upon reaching the end of an unsuccessful search down the alist. So ~c[binding] returns ~c[(cdr nil)] in that case, which is ~c[nil]. Notice that we could also have investigated this question by trying some simple examples. ~bv[] ACL2 !>(binding 'a nil) NIL ACL2 !>(binding 'a (list (cons 'b 2))) NIL ~ev[] These definitions aren't ideal for all purposes. For one thing, there's nothing that keeps us from having ~c[nil] as a value bound to some key in the alist. Thus, if ~c[binding] returns ~c[nil] we don't always know if that is the value associated with the key in the alist, or if that key is not bound. We'll have to keep that ambiguity in mind whenever we use ~c[binding] in our specification. Suppose instead that we wanted ~c[binding] to return some error string on unbound keys. Well, then we'd just have to write our own version of ~c[binding]. But then we'd lose much of the value of using a previously defined book. As with any specification technique, certain tradeoffs are necessary. Why not take a look at the definitions of other alist functions and see how they work together to provide the ability to construct and search alists? We'll be using them rather heavily in what follows so it will be good if you understand basically how they work. Simply start up ACL2 and execute the form shown earlier, but substituting our directory name for the top-level ACL2 directory with yours. Alternatively, just ~bv[] (include-book \"data-structures/alist-defthms\" :dir :system) ~ev[] Then, you can use ~c[:]~il[pe] to look at function definitions. You'll soon discover that almost all of the definitions are built on definitions of other, more primitive functions, as ~c[binding] is built on ~ilc[assoc-equal]. You can look at those as well, of course, or in many cases visit their documentation. The other problem with using a predefined book is that it will seldom be ``sufficiently complete,'' in the sense that the collection of ~il[rewrite] rules supplied won't be adequate to prove everything we'd like to know about the interactions of the various functions. If it were, there'd be no real reason to know that ~c[binding] is built on top of ~ilc[assoc-equal], because everything we'd need to know about ~c[binding] would be nicely expressed in the collection of theorems supplied with the book. However, that's very seldom the case. Developing such a collection of rules is currently more art than science and requires considerable experience. We'll encounter examples later of ``missing'' facts about ~c[binding] and our other alist functions. So, let's get on with the example. Notice that alists are mappings of keys to values; but, there is no notion of a ``type'' associated with the keys or with the values. Our phone book example, however, does have such a notion of types; we map names to phone numbers. We can introduce these ``types'' by explicitly defining them, e.g., names are strings and phone numbers are integers. Alternatively, we can ~b[partially define] or axiomatize a recognizer for names without giving a full definition. A way to safely introduce such ``constrained'' function symbols in ACL2 is with the ~ilc[encapsulate] form. For example, consider the following form. ~bv[] (encapsulate ;; Introduce a recognizer for names and give a ``type'' lemma. (((namep *) => *)) ;; (local (defun namep (x) ;; This declare is needed to tell ;; ACL2 that we're aware that the ;; argument x is not used in the body ;; of the function. (declare (ignore x)) t)) ;; (defthm namep-booleanp (booleanp (namep x)))) ~ev[] This ~ilc[encapsulate] form introduces the new function ~c[namep] of one argument and one result and constrains ~c[(namep x)] to be Boolean, for all inputs ~c[x]. More generally, an encapsulation establishes an environment in which functions can be defined and theorems and rules added without necessarily introducing those functions, theorems, and rules into the environment outside the encapsulation. To be admissible, all the events in the body of an encapsulate must be admissible. But the effect of an encapsulate is to assume only the non-local events. The first ``argument'' to ~c[encapsulate], ~c[((namep (x) t))] above, declares the intended ~il[signature]s of new function symbols that will be ``exported'' from the encapsulation without definition. The ~ilc[local] ~ilc[defun] of ~c[name] defines name within the encapsulation always to return ~c[t]. The ~c[defthm] event establishes that ~c[namep] is Boolean. By making the ~c[defun] local but the ~c[defthm] non-~c[local] this encapsulate constrains the undefined function ~c[namep] to be Boolean; the admissibility of the encapsulation establishes that there exists a Boolean function (namely the constant function returning ~c[t]). We can subsequently use ~c[namep] as we use any other Boolean function, with the proviso that we know nothing about it except that it always returns either ~c[t] or ~c[nil]. We use ~c[namep] to ``recognize'' legal keys for our phonebook alist. We wish to do something similar to define what it means to be a legal phone number. We submit the following form to ACL2: ~bv[] (encapsulate ;; Introduce a recognizer for phone numbers. (((pnump *) => *)) ;; (local (defun pnump (x) (not (equal x nil)))) ;; (defthm pnump-booleanp (booleanp (pnump x))) ;; (defthm nil-not-pnump (not (pnump nil)))). ~ev[] This introduces a Boolean-valued recognizer ~c[pnump], with the additional proviso that the constant ~c[nil] is not a ~c[pnump]. We impose this restriction to guarantee that we'll never bind a name to ~c[nil] in our phone book and thereby introduce the kind of ambiguity described above regarding the use of ~c[binding]. Now a legal phone book is an alist mapping from ~c[namep]s to ~c[pnump]s. We can define this as follows: ~bv[] (defun name-phonenum-pairp (x) ;; Recognizes a pair of (name . pnum). (and (consp x) (namep (car x)) (pnump (cdr x)))) (defun phonebookp (l) ;; Recognizes a list of such pairs. (if (not (consp l)) (null l) (and (name-phonenum-pairp (car l)) (phonebookp (cdr l))))) ~ev[] Thus, a phone book is really a list of pairs ~c[(name . pnum)]. Notice that we have not assumed that the keys of the phone book are distinct. We'll worry about that question later. (It is not always desirable to insist that the keys of an alist be distinct. But it may be a useful requirement for our specific example.) Now we are ready to define some of the functions necessary for our phonebook example. The functions we need are: ~bv[] (IN-BOOK? NM BK) ; does NM have a phone number in BK (FIND-PHONE NM BK) ; find NM's phone number in phonebook BK (ADD-PHONE NM PNUM BK) ; give NM the phone number PNUM in BK (CHANGE-PHONE NM PNUM BK) ; change NM's phone number to PNUM in BK (DEL-PHONE NM PNUM) ; remove NM's phone number from BK ~ev[] Given our underlying theory of alists, it is easy to write these functions. But we must take care to specify appropriate ``boundary'' behavior. Thus, what behavior do we want when, say, we try to change the phone number of a client who is not currently in the book? As usual, there are numerous possibilities; here we'll assume that we return the phone book unchanged if we try anything ``illegal.'' Possible definitions of our phone book functions are as follows. (Remember, an ~c[include-book] form such as the ones shown earlier must be executed in order to provide definitions for functions such as ~c[bound?].) ~bv[] (defun in-book? (nm bk) (bound? nm bk)) (defun find-phone (nm bk) (binding nm bk)) (defun add-phone (nm pnum bk) ;; If nm already in-book?, make no change. (if (in-book? nm bk) bk (bind nm pnum bk))) (defun change-phone (nm pnum bk) ;; Make a change only if nm already has a phone number. (if (in-book? nm bk) (bind nm pnum bk) bk)) (defun del-phone (nm bk) ;; Remove the binding from bk, if there is one. (rembind nm bk)) ~ev[] Notice that we don't have to check whether a name is in the book before deleting, because ~c[rembind] is essentially a no-op if ~c[nm] is not bound in ~c[bk]. In some sense, this completes our specification. But we can't have any real confidence in its correctness without validating our specification in some way. One way to do so is by proving some properties of our specification. Some candidate properties are: ~bq[] 1. A name will be in the book after we add it. 2. We will find the most recently added phone number for a client. 3. If we change a number, we'll find the change. 4. Changing and then deleting a number is the same as just deleting. 5. A name will not be in the book after we delete it. ~eq[] Let's formulate some of these properties. The first one, for example, is: ~bv[] (defthm add-in-book (in-book? nm (add-phone nm pnum bk))). ~ev[] You may wonder why we didn't need any hypotheses about the ``types'' of the arguments. In fact, ~c[add-in-book] is really expressing a property that is true of alists in general, not just of the particular variety of alists we are dealing with. Of course, we could have added some extraneous hypotheses and proved: ~bv[] (defthm add-in-book (implies (and (namep nm) (pnump pnum) (phonebookp bk)) (in-book? nm (add-phone nm pnum bk)))), ~ev[] but that would have yielded a weaker and less useful lemma because it would apply to fewer situations. In general, it is best to state lemmas in the most general form possible and to eliminate unnecessary hypotheses whenever possible. The reason for that is simple: lemmas are usually stored as rules and used in later proofs. For a lemma to be used, its hypotheses must be relieved (proved to hold in that instance); extra hypotheses require extra work. So we avoid them whenever possible. There is another, more important observation to make about our lemma. Even in its simpler form (without the extraneous hypotheses), the lemma ~c[add-in-book] may be useless as a ~il[rewrite] rule. Notice that it is stated in terms of the non-recursive functions ~c[in-book?] and ~c[add-phone]. If such functions appear in the left hand side of the conclusion of a lemma, the lemma may not ever be used. Suppose in a later proof, the theorem prover encountered a ~il[term] of the form: ~bv[] (in-book? nm (add-phone nm pnum bk)). ~ev[] Since we've already proved ~c[add-in-book], you'd expect that this would be immediately reduced to true. However, the theorem prover will often ``expand'' the non-recursive definitions of ~c[in-book?] and ~c[add-phone] using their definitions ~b[before] it attempts rewriting with lemmas. After this expansion, lemma ~c[add-in-book] won't ``match'' the ~il[term] and so won't be applied. Look back at the proof script for ~c[add-in-proof] and you'll notice that at the very end the prover warned you of this potential difficulty when it printed: ~bv[] Warnings: Non-rec Time: 0.18 seconds (prove: 0.05, print: 0.00, other: 0.13) ADD-IN-BOOK ~ev[] The ``Warnings'' line notifies you that there are non-recursive function calls in the left hand side of the conclusion and that this problem might arise. Of course, it may be that you don't ever plan to use the lemma for rewriting or that your intention is to ~il[disable] these functions. ~il[Disable]d functions are not expanded and the lemma should apply. However, you should always take note of such warnings and consider an appropriate response. By the way, we noted above that ~c[binding] is ~il[disable]d. If it were not, none of the lemmas about ~c[binding] in the book we included would likely be of much use for exactly the reason we just gave. For our current example, let's assume that we're just investigating the properties of our specifications and not concerned about using our lemmas for rewriting. So let's go on. If we really want to avoid the warnings, we can add ~c[:rule-classes nil] to each ~c[defthm] event; ~pl[rule-classes]. Property 2 is: we always find the most recently added phone number for a client. Try the following formalization: ~bv[] (defthm find-add-first-cut (equal (find-phone nm (add-phone nm pnum bk)) pnum)) ~ev[] and you'll find that the proof attempt fails. Examining the proof attempt and our function definitions, we see that the lemma is false if ~c[nm] is already in the book. We can remedy this situation by reformulating our lemma in at least two different ways: ~bv[] (defthm find-add1 (implies (not (in-book? nm bk)) (equal (find-phone nm (add-phone nm pnum bk)) pnum))) (defthm find-add2 (equal (find-phone nm (add-phone nm pnum bk)) (if (in-book? nm bk) (find-phone nm bk) pnum))) ~ev[] For technical reasons, lemmas such as ~c[find-add2], i.e., which do not have hypotheses, are usually slightly preferable. This lemma is stored as an ``unconditional'' ~il[rewrite] rule (i.e., has no hypotheses) and, therefore, will apply more often than ~c[find-add1]. However, for our current purposes either version is all right. Property 3 says: If we change a number, we'll find the change. This is very similar to the previous example. The formalization is as follows. ~bv[] (defthm find-change (equal (find-phone nm (change-phone nm pnum bk)) (if (in-book? nm bk) pnum (find-phone nm bk)))) ~ev[] Property 4 says: changing and then deleting a number is the same as just deleting. We can model this as follows. ~bv[] (defthm del-change (equal (del-phone nm (change-phone nm pnum bk)) (del-phone nm bk))) ~ev[] Unfortunately, when we try to prove this, we encounter subgoals that seem to be true, but for which the prover is stumped. For example, consider the following goal. (Note: ~c[endp] holds of lists that are empty.) ~bv[] Subgoal *1/4 (IMPLIES (AND (NOT (ENDP BK)) (NOT (EQUAL NM (CAAR BK))) (NOT (BOUND? NM (CDR BK))) (BOUND? NM BK)) (EQUAL (REMBIND NM (BIND NM PNUM BK)) (REMBIND NM BK))). ~ev[] Our intuition about ~c[rembind] and ~c[bind] tells us that this goal should be true even without the hypotheses. We attempt to prove the following lemma. ~bv[] (defthm rembind-bind (equal (rembind nm (bind nm pnum bk)) (rembind nm bk))) ~ev[] The prover proves this by induction, and stores it as a rewrite rule. After that, the prover has no difficulty in proving ~c[del-change]. The need to prove lemma ~c[rembind-bind] illustrates a point we made early in this example: the collection of ~il[rewrite] rules supplied by a previously certified book will almost never be everything you'll need. It would be nice if we could operate purely in the realm of names, phone numbers, and phone books without ever having to prove any new facts about alists. Unfortunately, we needed a fact about the relation between ~c[rembind] and ~c[bind] that wasn't supplied with the alists theory. Hopefully, such omissions will be rare. Finally, let's consider our property 5 above: a name will not be in the book after we delete it. We formalize this as follows: ~bv[] (defthm in-book-del (not (in-book? nm (del-phone nm bk)))) ~ev[] This proves easily. But notice that it's only true because ~c[del-phone] (actually ~c[rembind]) removes ~b[all] occurrences of a name from the phone book. If it only removed, say, the first one it encountered, we'd need a hypothesis that said that ~c[nm] occurs at most once in ~c[bk]. Ah, maybe that's a property you hadn't considered. Maybe you want to ensure that ~b[any] name occurs at most once in any valid phonebook. To complete this example, let's consider adding an ~b[invariant] to our specification. In particular, suppose we want to assure that no client has more than one associated phone number. One way to ensure this is to require that the domain of the alist is a ``set'' (has no duplicates). ~bv[] (defun setp (l) (if (atom l) (null l) (and (not (member-equal (car l) (cdr l))) (setp (cdr l))))) (defun valid-phonebookp (bk) (and (phonebookp bk) (setp (domain bk)))) ~ev[] Now, we want to show under what conditions our operations preserve the property of being a ~c[valid-phonebookp]. The operations ~c[in-book?] and ~c[find-phone] don't return a phone book, so we don't really need to worry about them. Since we're really interested in the ``types'' of values preserved by our phonebook functions, let's look at the types of those operations as well. ~bv[] (defthm in-book-booleanp (booleanp (in-book? nm bk))) (defthm in-book-namep (implies (and (phonebookp bk) (in-book? nm bk)) (namep nm)) :hints ((\"Goal\" :in-theory (enable bound?)))) (defthm find-phone-pnump (implies (and (phonebookp bk) (in-book? nm bk)) (pnump (find-phone nm bk))) :hints ((\"Goal\" :in-theory (enable bound? binding)))) ~ev[] Note the ``~c[:]~ilc[hints]'' on the last two lemmas. Neither of these would prove without these ~il[hints], because once again there are some facts about ~c[bound?] and ~c[binding] not available in our current context. Now, we could figure out what those facts are and try to prove them. Alternatively, we can ~il[enable] ~c[bound?] and ~c[binding] and hope that by opening up these functions, the conjectures will reduce to versions that the prover does know enough about or can prove by induction. In this case, this strategy works. The hints tell the prover to ~il[enable] the functions in question when considering the designated goal. Below we develop the theorems showing that ~c[add-phone], ~c[change-phone], and ~c[del-phone] preserve our proposed invariant. Notice that along the way we have to prove some subsidiary facts, some of which are pretty ugly. It would be a good idea for you to try, say, ~c[add-phone-preserves-invariant] without introducing the following four lemmas first. See if you can develop the proof and only add these lemmas as you need assistance. Then try ~c[change-phone-preserves-invariant] and ~c[del-phone-preserves-invariant]. They will be easier. It is illuminating to think about why ~c[del-phone-preserves-invariant] does not need any ``type'' hypotheses. ~bv[] (defthm bind-preserves-phonebookp (implies (and (phonebookp bk) (namep nm) (pnump num)) (phonebookp (bind nm num bk)))) (defthm member-equal-strip-cars-bind (implies (and (not (equal x y)) (not (member-equal x (strip-cars a)))) (not (member-equal x (strip-cars (bind y z a)))))) (defthm bind-preserves-domain-setp (implies (and (alistp bk) (setp (domain bk))) (setp (domain (bind nm num bk)))) :hints ((\"Goal\" :in-theory (enable domain)))) (defthm phonebookp-alistp (implies (phonebookp bk) (alistp bk))) (defthm ADD-PHONE-PRESERVES-INVARIANT (implies (and (valid-phonebookp bk) (namep nm) (pnump num)) (valid-phonebookp (add-phone nm num bk))) :hints ((\"Goal\" :in-theory (disable domain-bind)))) (defthm CHANGE-PHONE-PRESERVES-INVARIANT (implies (and (valid-phonebookp bk) (namep nm) (pnump num)) (valid-phonebookp (change-phone nm num bk))) :hints ((\"Goal\" :in-theory (disable domain-bind)))) (defthm remove-equal-preserves-setp (implies (setp l) (setp (remove-equal x l)))) (defthm rembind-preserves-phonebookp (implies (phonebookp bk) (phonebookp (rembind nm bk)))) (defthm DEL-PHONE-PRESERVES-INVARIANT (implies (valid-phonebookp bk) (valid-phonebookp (del-phone nm bk)))) ~ev[] As a final test of your understanding, try to formulate and prove an invariant that says that no phone number is assigned to more than one name. The following hints may help. ~bq[] 1. Define the appropriate invariant. (Hint: remember the function ~c[range].) 2. Do our current definitions of ~c[add-phone] and ~c[change-phone] necessarily preserve this property? If not, consider what hypotheses are necessary in order to guarantee that they do preserve this property. 3. Study the definition of the function ~c[range] and notice that it is defined in terms of the function ~ilc[strip-cdrs]. Understand how this defines the range of an alist. 4. Formulate the correctness theorems and attempt to prove them. You'll probably benefit from studying the invariant proof above. In particular, you may need some fact about the function ~ilc[strip-cdrs] analogous to the lemma ~c[member-equal-strip-cars-bind] above. ~eq[] Below is one solution to this exercise. Don't look at the solution, however, until you've struggled a bit with it. Notice that we didn't actually change the definitions of ~c[add-phone] and ~c[change-phone], but added a hypothesis saying that the number is ``new.'' We could have changed the definitions to check this and return the phonebook unchanged if the number was already in use. ~bv[] (defun pnums-in-use (bk) (range bk)) (defun phonenums-unique (bk) (setp (pnums-in-use bk))) (defun new-pnump (pnum bk) (not (member-equal pnum (pnums-in-use bk)))) (defthm member-equal-strip-cdrs-rembind (implies (not (member-equal x (strip-cdrs y))) (not (member-equal x (strip-cdrs (rembind z y)))))) (defthm DEL-PHONE-PRESERVES-PHONENUMS-UNIQUE (implies (phonenums-unique bk) (phonenums-unique (del-phone nm bk))) :hints ((\"Goal\" :in-theory (enable range)))) (defthm strip-cdrs-bind-non-member (implies (and (not (bound? x a)) (alistp a)) (equal (strip-cdrs (bind x y a)) (append (strip-cdrs a) (list y)))) :hints ((\"Goal\" :in-theory (enable bound?)))) (defthm setp-append-list (implies (setp l) (equal (setp (append l (list x))) (not (member-equal x l))))) (defthm ADD-PHONE-PRESERVES-PHONENUMS-UNIQUE (implies (and (phonenums-unique bk) (new-pnump pnum bk) (alistp bk)) (phonenums-unique (add-phone nm pnum bk))) :hints ((\"Goal\" :in-theory (enable range)))) (defthm member-equal-strip-cdrs-bind (implies (and (not (member-equal z (strip-cdrs a))) (not (equal z y))) (not (member-equal z (strip-cdrs (bind x y a)))))) (defthm CHANGE-PHONE-PRESERVES-PHONENUMS-UNIQUE (implies (and (phonenums-unique bk) (new-pnump pnum bk) (alistp bk)) (phonenums-unique (change-phone nm pnum bk))) :hints ((\"Goal\" :in-theory (enable range)))) ~ev[] ") (deflabel Tutorial4-Defun-Sk-Example :doc ":Doc-Section annotated-acl2-scripts example of quantified notions~/ This example illustrates the use of ~ilc[defun-sk] and ~ilc[defthm] ~il[events] to reason about quantifiers. ~l[defun-sk]. For a more through, systematic beginner's introduction to quantification in ACL2, ~pl[quantifier-tutorial]. Many users prefer to avoid the use of quantifiers, since ACL2 provides only very limited support for reasoning about quantifiers.~/ Here is a list of ~il[events] that proves that if there are arbitrarily large numbers satisfying the disjunction ~c[(OR P R)], then either there are arbitrarily large numbers satisfying ~c[P] or there are arbitrarily large numbers satisfying ~c[R]. ~bv[] ; Introduce undefined predicates p and r. (defstub p (x) t) (defstub r (x) t) ; Define the notion that something bigger than x satisfies p. (defun-sk some-bigger-p (x) (exists y (and (< x y) (p y)))) ; Define the notion that something bigger than x satisfies r. (defun-sk some-bigger-r (x) (exists y (and (< x y) (r y)))) ; Define the notion that arbitrarily large x satisfy p. (defun-sk arb-lg-p () (forall x (some-bigger-p x))) ; Define the notion that arbitrarily large x satisfy r. (defun-sk arb-lg-r () (forall x (some-bigger-r x))) ; Define the notion that something bigger than x satisfies p or r. (defun-sk some-bigger-p-or-r (x) (exists y (and (< x y) (or (p y) (r y))))) ; Define the notion that arbitrarily large x satisfy p or r. (defun-sk arb-lg-p-or-r () (forall x (some-bigger-p-or-r x))) ; Prove the theorem promised above. Notice that the functions open ; automatically, but that we have to provide help for some rewrite ; rules because they have free variables in the hypotheses. The ; ``witness functions'' mentioned below were introduced by DEFUN-SK. (thm (implies (arb-lg-p-or-r) (or (arb-lg-p) (arb-lg-r))) :hints ((\"Goal\" :use ((:instance some-bigger-p-suff (x (arb-lg-p-witness)) (y (some-bigger-p-or-r-witness (max (arb-lg-p-witness) (arb-lg-r-witness))))) (:instance some-bigger-r-suff (x (arb-lg-r-witness)) (y (some-bigger-p-or-r-witness (max (arb-lg-p-witness) (arb-lg-r-witness))))) (:instance arb-lg-p-or-r-necc (x (max (arb-lg-p-witness) (arb-lg-r-witness)))))))) ; And finally, here's a cute little example. We have already ; defined above the notion (some-bigger-p x), which says that ; something bigger than x satisfies p. Let us introduce a notion ; that asserts that there exists both y and z bigger than x which ; satisfy p. On first glance this new notion may appear to be ; stronger than the old one, but careful inspection shows that y and ; z do not have to be distinct. In fact ACL2 realizes this, and ; proves the theorem below automatically. (defun-sk two-bigger-p (x) (exists (y z) (and (< x y) (p y) (< x z) (p z)))) (thm (implies (some-bigger-p x) (two-bigger-p x))) ; A technical point: ACL2 fails to prove the theorem above ; automatically if we take its contrapositive, unless we disable ; two-bigger-p as shown below. That is because ACL2 needs to expand ; some-bigger-p before applying the rewrite rule introduced for ; two-bigger-p, which contains free variables. The moral of the ; story is: Don't expect too much automatic support from ACL2 for ; reasoning about quantified notions. (thm (implies (not (two-bigger-p x)) (not (some-bigger-p x))) :hints ((\"Goal\" :in-theory (disable two-bigger-p)))) ~ev[] ") (deflabel Tutorial5-Miscellaneous-Examples :doc ":Doc-Section annotated-acl2-scripts miscellaneous ACL2 examples~/ The following examples are more advanced examples of usage of ACL2. They are included largely for reference, in case someone finds them useful.~/~/") (deflabel file-reading-example :doc ":Doc-Section Tutorial5-Miscellaneous-Examples example of reading files in ACL2~/ This example illustrates the use of ACL2's ~il[IO] primitives to read the forms in a file. ~l[io].~/ This example provides a solution to the following problem. Let's say that you have a file that contains s-expressions. Suppose that you want to build a list by starting with ~c[nil], and updating it ``appropriately'' upon encountering each successive s-expression in the file. That is, suppose that you have written a function ~c[update-list] such that ~c[(update-list obj current-list)] returns the list obtained by ``updating'' ~c[current-list] with the next object, ~c[obj], encountered in the file. The top-level function for processing such a file, returning the final list, could be defined as follows. Notice that because it opens a channel to the given file, this function modifies ~il[state] and hence must return ~il[state]. Thus it actually returns two values: the final list and the new ~il[state]. ~bv[] (defun process-file (filename state) (mv-let (channel state) (open-input-channel filename :object state) (mv-let (result state) (process-file1 nil channel state) ;see below (let ((state (close-input-channel channel state))) (mv result state))))) ~ev[] The function ~c[process-file1] referred to above takes the currently constructed list (initially, ~c[nil]), together with a channel to the file being read and the ~il[state], and returns the final updated list. Notice that this function is tail recursive. This is important because many Lisp compilers will remove tail recursion, thus avoiding the potential for stack overflows when the file contains a large number of forms. ~bv[] (defun process-file1 (current-list channel state) (mv-let (eofp obj state) (read-object channel state) (cond (eofp (mv current-list state)) (t (process-file1 (update-list obj current-list) channel state))))) ~ev[] As an exercise, you might want to add ~il[guard]s to the functions above and verify the guards (~pl[verify-guards]). ~l[args] or make a call of the form ~c[(guard 'your-function nil (w state))] to see the guard of an existing function.") (deflabel guard-example :doc ":Doc-Section Tutorial5-Miscellaneous-Examples a brief transcript illustrating ~il[guard]s in ACL2~/ This note addresses the question: what is the use of ~il[guard]s in ACL2? Although we recommend that beginners try to avoid ~il[guard]s for a while, we hope that the summary here is reasonably self-contained and will provide a reasonable introduction to guards in ACL2. For a more systematic discussion, ~pl[guard]. For a summary of that topic, ~pl[guard-quick-reference]. Before we get into the issue of ~il[guard]s, let us note that there are two important ``modes'': ~il[defun-mode] ~-[] ``Does this ~il[defun] add an axiom (`:logic mode') or not (`:program mode')?'' (~l[defun-mode].) Only ~c[:]~ilc[logic] mode functions can have their ``~il[guard]s verified'' via mechanized proof; ~pl[verify-guards]. ~ilc[set-guard-checking] ~-[] ``Should runtime ~il[guard] violations signal an error (~c[:all], and usually with ~c[t] or ~c[:nowarn]) or go undetected (~c[nil], ~c[:none])? Equivalently, are expressions evaluated in Common Lisp or in the logic?'' (~l[set-guard-checking].)~/ ~em[Prompt examples] Here some examples of the relation between the ACL2 ~il[prompt] and the ``modes'' discussed above. Also ~pl[default-print-prompt]. The first examples all have ~c[ld-skip-proofsp nil]; that is, proofs are ~em[not] skipped. ~bv[] ACL2 !> ; logic mode with guard checking on ACL2 > ; logic mode with guard checking off ACL2 p!> ; program mode with guard checking on ACL2 p> ; program mode with guard checking off ~ev[] Here are some examples with ~ilc[default-defun-mode] of ~c[:]~ilc[logic]. ~bv[] ACL2 > ; guard checking off, ld-skip-proofsp nil ACL2 s> ; guard checking off, ld-skip-proofsp t ACL2 !> ; guard checking on, ld-skip-proofsp nil ACL2 !s> ; guard checking on, ld-skip-proofsp t ~ev[] ~em[Sample session] ~bv[] ACL2 !>(+ 'abc 3) ACL2 Error in TOP-LEVEL: The guard for the function symbol BINARY-+, which is (AND (ACL2-NUMBERP X) (ACL2-NUMBERP Y)), is violated by the arguments in the call (+ 'ABC 3). ACL2 !>:set-guard-checking nil ;;;; verbose output omitted here ACL2 >(+ 'abc 3) 3 ACL2 >(< 'abc 3) T ACL2 >(< 3 'abc) NIL ACL2 >(< -3 'abc) T ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(defun sum-list (x) (declare (xargs :guard (integer-listp x) :verify-guards nil)) (cond ((endp x) 0) (t (+ (car x) (sum-list (cdr x)))))) The admission of SUM-LIST is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We observe that the type of SUM-LIST is described by the theorem (ACL2-NUMBERP (SUM-LIST X)). We used primitive type reasoning. Summary Form: ( DEFUN SUM-LIST ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, proof tree: 0.00, other: 0.03) SUM-LIST ACL2 !>(sum-list '(1 2 3)) ACL2 Warning [Guards] in TOP-LEVEL: Guard-checking will be inhibited on recursive calls of the executable counterpart (i.e., in the ACL2 logic) of SUM-LIST. To check guards on all recursive calls: (set-guard-checking :all) To leave behavior unchanged except for inhibiting this message: (set-guard-checking :nowarn) 6 ACL2 !>(sum-list '(1 2 abc 3)) ACL2 Error in TOP-LEVEL: The guard for the function symbol BINARY-+, which is (AND (ACL2-NUMBERP X) (ACL2-NUMBERP Y)), is violated by the arguments in the call (+ 'ABC 3). ACL2 !>:set-guard-checking nil ;;;; verbose output omitted here ACL2 >(sum-list '(1 2 abc 3)) 6 ACL2 >(defthm sum-list-append (equal (sum-list (append a b)) (+ (sum-list a) (sum-list b)))) << Starting proof tree logging >> Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. Subsumption reduces that number to two. However, one of these is flawed and so we are left with one viable candidate. ... That completes the proof of *1. Q.E.D. ~ev[] ~em[Guard verification vs. defun] ~bv[] Declare Form Guards Verified? (declare (xargs :mode :program ...)) no (declare (xargs :guard g)) yes (declare (xargs :guard g :verify-guards nil)) no (declare (xargs ......)) no ACL2 >:pe sum-list l 8 (DEFUN SUM-LIST (X) (DECLARE (XARGS :GUARD (INTEGER-LISTP X) :VERIFY-GUARDS NIL)) (COND ((ENDP X) 0) (T (+ (CAR X) (SUM-LIST (CDR X)))))) ACL2 >(verify-guards sum-list) The non-trivial part of the guard conjecture for SUM-LIST, given the :type-prescription rule SUM-LIST, is Goal (AND (IMPLIES (AND (INTEGER-LISTP X) (NOT (CONSP X))) (EQUAL X NIL)) (IMPLIES (AND (INTEGER-LISTP X) (NOT (ENDP X))) (INTEGER-LISTP (CDR X))) (IMPLIES (AND (INTEGER-LISTP X) (NOT (ENDP X))) (ACL2-NUMBERP (CAR X)))). ... ACL2 >:pe sum-list lv 8 (DEFUN SUM-LIST (X) (DECLARE (XARGS :GUARD (INTEGER-LISTP X) :VERIFY-GUARDS NIL)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(sum-list '(1 2 abc 3)) ACL2 Error in TOP-LEVEL: The guard for the function symbol SUM-LIST, which is (INTEGER-LISTP X), is violated by the arguments in the call (SUM-LIST '(1 2 ABC ...)). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>:set-guard-checking nil ;;;; verbose output omitted here ACL2 >(sum-list '(1 2 abc 3)) 6 ACL2 >:comp sum-list Compiling gazonk0.lsp. End of Pass 1. End of Pass 2. Finished compiling gazonk0.lsp. Loading gazonk0.o start address -T 1bbf0b4 Finished loading gazonk0.o Compiling gazonk0.lsp. End of Pass 1. End of Pass 2. Finished compiling gazonk0.lsp. Loading gazonk0.o start address -T 1bc4408 Finished loading gazonk0.o SUM-LIST ACL2 >:q Exiting the ACL2 read-eval-print loop. ACL2>(trace sum-list) (SUM-LIST) ACL2>(lp) ACL2 Version 1.8. Level 1. Cbd \"/slocal/src/acl2/v1-9/\". Type :help for help. ACL2 >(sum-list '(1 2 abc 3)) 6 ACL2 >(sum-list '(1 2 3)) 1> (SUM-LIST (1 2 3))> 2> (SUM-LIST (2 3))> 3> (SUM-LIST (3))> 4> (SUM-LIST NIL)> <4 (SUM-LIST 0)> <3 (SUM-LIST 3)> <2 (SUM-LIST 5)> <1 (SUM-LIST 6)> 6 ACL2 >:pe sum-list-append 9 (DEFTHM SUM-LIST-APPEND (EQUAL (SUM-LIST (APPEND A B)) (+ (SUM-LIST A) (SUM-LIST B)))) ACL2 >(verify-guards sum-list-append) The non-trivial part of the guard conjecture for SUM-LIST-APPEND, given the :type-prescription rule SUM-LIST, is Goal (AND (TRUE-LISTP A) (INTEGER-LISTP (APPEND A B)) (INTEGER-LISTP A) (INTEGER-LISTP B)). ... ****** FAILED ******* See :DOC failure ****** FAILED ****** ACL2 >(defthm common-lisp-sum-list-append (if (and (integer-listp a) (integer-listp b)) (equal (sum-list (append a b)) (+ (sum-list a) (sum-list b))) t) :rule-classes nil) << Starting proof tree logging >> By the simple :rewrite rule SUM-LIST-APPEND we reduce the conjecture to Goal' (IMPLIES (AND (INTEGER-LISTP A) (INTEGER-LISTP B)) (EQUAL (+ (SUM-LIST A) (SUM-LIST B)) (+ (SUM-LIST A) (SUM-LIST B)))). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. ;;;; summary omitted here ACL2 >(verify-guards common-lisp-sum-list-append) The non-trivial part of the guard conjecture for COMMON-LISP-SUM-LIST-APPEND, given the :type-prescription rule SUM-LIST, is Goal (AND (IMPLIES (AND (INTEGER-LISTP A) (INTEGER-LISTP B)) (TRUE-LISTP A)) (IMPLIES (AND (INTEGER-LISTP A) (INTEGER-LISTP B)) (INTEGER-LISTP (APPEND A B)))). ... Q.E.D. That completes the proof of the guard theorem for COMMON-LISP-SUM-LIST-APPEND. COMMON-LISP-SUM-LIST-APPEND is compliant with Common Lisp. ;;;; Summary omitted here. ACL2 >(defthm foo (consp (mv x y))) ... Q.E.D. ~ev[] ~bv[] ACL2 >(verify-guards foo) ACL2 Error in (VERIFY-GUARDS FOO): The number of values we need to return is 1 but the number of values returned by the call (MV X Y) is 2. > (CONSP (MV X Y)) ACL2 Error in (VERIFY-GUARDS FOO): The guards for FOO cannot be verified because the theorem has the wrong syntactic form. See :DOC verify-guards. ~ev[]~/ :cited-by guard") (deflabel mutual-recursion-proof-example :doc ":Doc-Section Tutorial5-Miscellaneous-Examples a small proof about mutually recursive functions~/ Sometimes one wants to reason about mutually recursive functions. Although this is possible in ACL2, it can be a bit awkward. This example is intended to give some ideas about how one can go about such proofs. For an introduction to mutual recursion in ACL2, ~pl[mutual-recursion].~/ We begin by defining two mutually recursive functions: one that collects the variables from a ~il[term], the other that collects the variables from a list of ~il[term]s. We actually imagine the ~il[term] argument to be a ~c[pseudo-termp]; ~pl[pseudo-termp]. ~bv[] (mutual-recursion (defun free-vars1 (term ans) (cond ((atom term) (add-to-set-eq term ans)) ((fquotep term) ans) (t (free-vars1-lst (cdr term) ans)))) (defun free-vars1-lst (lst ans) (cond ((atom lst) ans) (t (free-vars1-lst (cdr lst) (free-vars1 (car lst) ans))))) ) ~ev[] Now suppose that we want to prove the following theorem. ~bv[] (defthm symbol-listp-free-vars1-try-1 (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans)))) ~ev[] Often ACL2 can generate a proof by induction based on the structure of definitions of function symbols occurring in the conjecture. In this case, ACL2 chooses to use an induction scheme suggested by ~c[(symbol-listp ans)], and sadly, that doesn't work. If one were doing this proof with pencil and paper, one would be more likely to prove a combination of the conjecture above and an analogous conjecture about free-vars1-lst. Feel free to try a pencil and paper proof! Or you can read on, to see how one can get ACL2 to do such a proof after all. The trick is to define a function that suggests an appropriate induction. The induction suggested is based on the if-then-else structure of the function's definition, where inductive hypotheses are generated for recursive calls ~-[] below we explain how that works for this function. ~bv[] (defun symbol-listp-free-vars1-induction (x ans) (if (atom x) ; then we just make sure x and ans aren't considered irrelevant: (list x ans) (list (symbol-listp-free-vars1-induction (car x) ans) (symbol-listp-free-vars1-induction (cdr x) ans) (symbol-listp-free-vars1-induction (cdr x) (free-vars1 (car x) ans))))) ~ev[] The if-then-else structure of this function generates two cases. In one case, ~c[(atom x)] is true, and the theorem to be proved should be proved under no additional hypotheses except for ~c[(atom x)]; in other words, ~c[(atom x)] gives us the base case of the induction. In the other case, ~c[(not (atom x))] is assumed together with three instances of the theorem to be proved, one for each recursive call. So, one instance substitutes ~c[(car x)] for ~c[x]; one substitutes ~c[(cdr x)] for ~c[x]; and the third substitutes ~c[(cdr x)] for ~c[x] and ~c[(free-vars1 (car x) ans)] for ~c[ans]. If you think about how you would go about a hand proof of the theorem to follow, you'll likely come up with a similar scheme. We now prove the two theorems together as a conjunction, because the inductive hypotheses for one are sometimes needed in the proof of the other (even when you do this proof on paper!). ~bv[] (defthm symbol-listp-free-vars1 (and (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans))) (implies (and (pseudo-term-listp x) (symbol-listp ans)) (symbol-listp (free-vars1-lst x ans)))) :hints ((\"Goal\" :induct (symbol-listp-free-vars1-induction x ans)))) ~ev[] The above works, but we conclude by illustrating a more efficient approach, in which we restrict to appropriate inductive hypotheses for each case. ~bv[] (ubt 'symbol-listp-free-vars1-induction) (defun symbol-listp-free-vars1-induction (flg x ans) ; Flg is nil if we are ``thinking'' of a single term. (if (atom x) ; whether x is a single term or a list of terms (list x ans) (if flg ; i.e., if x is a list of terms (list (symbol-listp-free-vars1-induction nil (car x) ans) (symbol-listp-free-vars1-induction t (cdr x) (free-vars1 (car x) ans))) (symbol-listp-free-vars1-induction t (cdr x) ans)))) ~ev[] We now state the theorem as a conditional, so that it can be proved nicely using the ~il[induction] scheme that we have just coded. The prover will not store an ~ilc[IF] ~il[term] as a ~il[rewrite] rule, but that's OK (provided we tell it not to try), because we're going to derive the corollaries of interest later and make ~b[them] into ~il[rewrite] rules. ~bv[] (defthm symbol-listp-free-vars1-flg (if flg (implies (and (pseudo-term-listp x) (symbol-listp ans)) (symbol-listp (free-vars1-lst x ans))) (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans)))) :hints ((\"Goal\" :induct (symbol-listp-free-vars1-induction flg x ans))) :rule-classes nil) ~ev[] And finally, we may derive the theorems we are interested in as immediate corollaries. ~bv[] (defthm symbol-listp-free-vars1 (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans))) :hints ((\"Goal\" :by (:instance symbol-listp-free-vars1-flg (flg nil))))) (defthm symbol-listp-free-vars1-lst (implies (and (pseudo-term-listp x) (symbol-listp ans)) (symbol-listp (free-vars1-lst x ans))) :hints ((\"Goal\" :by (:instance symbol-listp-free-vars1-flg (flg t))))) ~ev[] You may find community books (~pl[community-books] that help you to automate this kind of reasoning about mutually recursive functions. See for example the community book ~c[tools/flag.lisp]. ") (deflabel functional-instantiation-example :doc ":Doc-Section Tutorial5-Miscellaneous-Examples a small proof demonstrating functional instantiation~/ The example below demonstrates the use of functional instantiation, that is, the use of a generic result in proving a result about specific functions. In this example we constrain a function to be associative and commutative, with an identity or ``root,'' on a given domain. Next, we define a corresponding function that applies the constrained associative-commutative function to successive elements of a list. We then prove that the latter function gives the same value when we first reverse the elements of the list. Finally, we use functional instantiation to derive the corresponding result for the function that multiplies successive elements of a list. The details of the proof (such as the ~ilc[in-theory] event and particulars of the lemmas) are not the point here. Rather, the point is to demonstrate the interaction of ~ilc[encapsulate] ~il[events] and ~c[:functional-instance] ~il[lemma-instance]s. Of course, if you are interested in details then you may find it helpful to run these ~il[events] through ACL2. Also ~pl[constraint] for more about ~c[:functional-instance] and ~pl[lemma-instance] for general information about the use of previously-proved lemmas.~/ ~bv[] (in-package \"ACL2\") (encapsulate (((ac-fn * *) => *) ((ac-fn-domain *) => *) ((ac-fn-root) => *)) (local (defun ac-fn (x y) (+ x y))) (local (defun ac-fn-root () 0)) (local (defun ac-fn-domain (x) (acl2-numberp x))) (defthm ac-fn-comm (equal (ac-fn x y) (ac-fn y x))) (defthm ac-fn-assoc (equal (ac-fn (ac-fn x y) z) (ac-fn x (ac-fn y z)))) (defthm ac-fn-id (implies (ac-fn-domain x) (equal (ac-fn (ac-fn-root) x) x))) (defthm ac-fn-closed (and (ac-fn-domain (ac-fn x y)) (ac-fn-domain (ac-fn-root))))) ;;;;;;;;;;;;;;;;;;;;;;; ; End of encapsulate. ; ;;;;;;;;;;;;;;;;;;;;;;; ; Define a ``fold'' function that iteratively applies ac-fn over a list. (defun ac-fn-list (x) (if (atom x) (ac-fn-root) (ac-fn (car x) (ac-fn-list (cdr x))))) ; Recognize lists all of whose elements are in the intended domain. (defun ac-fn-domain-list (x) (if (atom x) t (and (ac-fn-domain (car x)) (ac-fn-domain-list (cdr x))))) ; Define a list reverse function. (defun rev (x) (if (atom x) nil (append (rev (cdr x)) (list (car x))))) ; The following is needed for proving ac-fn-list-append, which is ; needed for proving ac-fn-list-rev. (defthm ac-fn-list-closed (ac-fn-domain (ac-fn-list x))) ; Needed for proving ac-fn-list-rev: (defthm ac-fn-list-append (implies (and (ac-fn-domain-list x) (ac-fn-domain-list y)) (equal (ac-fn-list (append x y)) (ac-fn (ac-fn-list x) (ac-fn-list y))))) ; Needed for proving ac-fn-list-rev: (defthm ac-fn-domain-list-rev (equal (ac-fn-domain-list (rev x)) (ac-fn-domain-list x))) ; The following is a good idea because without it, the proof attempt ; for ac-fn-list-rev (see just below) fails with the term (HIDE ; (AC-FN-LIST NIL)). It is often a good idea to disable ; executable-counterparts of functions that call constrained ; functions. (in-theory (disable (:executable-counterpart ac-fn-list))) (defthm ac-fn-list-rev (implies (ac-fn-domain-list x) (equal (ac-fn-list (rev x)) (ac-fn-list x)))) ; Our goal now is to apply functional instantiation to ac-fn-list-rev ; in order to prove the corresponding theorem, times-list-rev, based ; on * instead of ac-fn. (defun times-list (x) (if (atom x) 1 (* (car x) (times-list (cdr x))))) (defun acl2-number-listp (x) (if (atom x) t (and (acl2-numberp (car x)) (acl2-number-listp (cdr x))))) ; The following relies on the following built-in rules for * (whose ; statements correspond directly to their names): commutativity-of-*, ; associativity-of-*, and unicity-of-1. (defthm times-list-rev (implies (acl2-number-listp x) (equal (times-list (rev x)) (times-list x))) :hints ((\"Goal\" :use ((:functional-instance ac-fn-list-rev ;; Instantiate the generic functions: (ac-fn *) (ac-fn-root (lambda () 1)) (ac-fn-domain acl2-numberp) ;; Instantiate the other relevant functions: (ac-fn-list times-list) (ac-fn-domain-list acl2-number-listp)))))) ~ev[]~/") (deflabel Startup :doc ":Doc-Section ACL2-Tutorial How to start using ACL2; the ACL2 ~il[command] loop~/~/ When you start up ACL2, you'll probably find yourself inside the ACL2 ~il[command] loop, as indicated by the following ~il[prompt]. ~bv[] ACL2 !> ~ev[] If not, you should type ~c[(LP)]. ~l[lp], which has a lot more information about the ACL2 ~il[command] loop. You should now be in ACL2. The current ``~il[default-defun-mode]'' is ~c[:]~ilc[logic]; the other mode is ~c[:]~ilc[program], which would cause the letter ~c[p] to be printed in the ~il[prompt]. ~c[:]~ilc[Logic] means that any function we define is not only executable but also is axiomatically defined in the ACL2 logic. ~l[defun-mode] and ~pl[default-defun-mode]. For example we can define a function ~c[my-cons] as follows. (You may find it useful to start up ACL2 and submit this and other ~il[command]s below to the ACL2 ~il[command] loop, as we won't include output below.) ~bv[] ACL2 !>(defun my-cons (x y) (cons x y)) ~ev[] An easy theorem may then be proved: the ~ilc[car] of ~c[(my-cons a b)] is A. ~bv[] ACL2 !>(defthm car-my-cons (equal (car (my-cons a b)) a)) ~ev[] You can place raw Lisp forms to evaluate at start-up into file ~c[acl2-init.lsp] in your home directory, except on Windows systems. For example, if you put the following into ~c[acl2-init.lsp], then ACL2 will print \"HI\" when it starts up. ~bv[] (print \"HI\") ~ev[] But be careful; all bets are off when you submit forms to raw Lisp, so this capability should only be used when you are hacking or when you are setting some Lisp parameters (e.g., ~c[(setq si::*notify-gbc* nil)] to turn off garbage collection notices in GCL). Notice that unlike Nqthm, the theorem ~il[command] is ~ilc[defthm] rather than ~c[prove-lemma]. ~l[defthm], which explains (among other things) that the default is to turn theorems into ~il[rewrite] rules. Various keyword commands are available to query the ACL2 ``~il[world]'', or database. For example, we may view the definition of ~c[my-cons] by invoking a command to print ~il[events], as follows. ~bv[] ACL2 !>:pe my-cons ~ev[] Also ~pl[pe]. We may also view all the lemmas that ~il[rewrite] ~il[term]s whose top function symbol is ~ilc[car] by using the following command, whose output will refer to the lemma ~c[car-my-cons] proved above. ~bv[] ACL2 !>:pl car ~ev[] Also ~pl[pl]. Finally, we may print all the ~il[command]s back through the initial ~il[world] as follows. ~bv[] ACL2 !>:pbt 0 ~ev[] ~l[history] for a list of commands, including these, for viewing the current ACL2 ~il[world]. Continue with the ~il[documentation] for ~il[annotated-acl2-scripts] to see a simple but illustrative example in the use of ACL2 for reasoning about functions.~/") (deflabel Tidbits :doc ":Doc-Section ACL2-Tutorial some basic hints for using ACL2~/~/ ~l[books] for a discussion of books. Briefly, a book is a file whose name ends in ``.lisp'' that contains ACL2 ~il[events]; ~pl[events]. ~l[history] for a list of useful commands. Some examples: ~bv[] :pbt :here ; print the current event :pbt (:here -3) ; print the last four events :u ; undo the last event :pe append ; print the definition of append ~ev[] ~l[documentation] to learn how to print documentation to the terminal. There are also versions of the ~il[documentation] for Mosaic, Emacs Info, and hardcopy. There are quite a few kinds of rules allowed in ACL2 besides ~c[:]~ilc[rewrite] rules, though we hope that beginners won't usually need to be aware of them. ~l[rule-classes] for details. In particular, there is support for ~il[congruence] rewriting. ~l[rune] (``RUle NamE'') for a description of the various kinds of rules in the system. Also ~pl[theories] for a description of how to build ~il[theories] of ~il[rune]s, which are often used in hints; ~pl[hints]. A ``~il[programming] mode'' is supported; ~pl[program], ~pl[defun-mode], and ~pl[default-defun-mode]. It can be useful to prototype functions after executing the command ~c[:]~ilc[program], which will cause definitions to be syntaxed-checked only. ACL2 supports mutual recursion, though this feature is not tied into the automatic discovery of ~il[induction] schemas and is often not the best way to proceed when you expect to be reasoning about the functions. ~l[defuns]; also ~pl[mutual-recursion]. ~l[ld] for discussion of how to load files of ~il[events]. There are many options to ~ilc[ld], including ones to suppress proofs and to control output. The ~c[:]~ilc[otf-flg] (Onward Thru the Fog FLaG) is a useful feature that Nqthm users have often wished for. It prevents the prover from aborting a proof attempt and inducting on the original conjecture. ~l[otf-flg]. ACL2 supports redefinition and redundancy in ~il[events]; ~pl[ld-redefinition-action] and ~pl[redundant-events]. A ~il[proof-tree] display feature is available for use with Emacs. This feature provides a view of ACL2 proofs that can be much more useful than reading the stream of ~il[characters] output by the theorem prover as its ``proof.'' ~l[proof-tree]. An interactive feature similar to Pc-Nqthm is supported in ACL2. ~l[verify] and ~pl[proof-checker]. ACL2 allows you to ~il[monitor] the use of ~il[rewrite] rules. ~l[break-rewrite]. ~l[arrays] to read about applicative, fast ~il[arrays] in ACL2. To quit the ACL2 ~il[command] loop, or (in akcl) to return to the ACL2 ~il[command] loop after an interrupt, type ~c[:]~ilc[q]. To continue (resume) after an interrupt (in akcl), type ~c[:r]. To cause an interrupt (in akcl under Unix (trademark of AT&T)), hit control-C (twice, if inside Emacs). To exit ACL2 altogether, first type ~c[:]~ilc[q] to exit the ACL2 ~il[command] loop, and then exit Lisp (by typing ~c[(user::bye)] in akcl). ~l[state] to read about the von Neumannesque ACL2 ~il[state] object that records the ``current state'' of the ACL2 session. Also ~pl[@], and ~pl[assign], to learn about reading and setting global ~il[state] variables. If you want your own von Neumannesque object, e.g., a structure that can be ``destructively modified'' but which must be used with some syntactic restrictions, ~pl[stobj].~/") (deflabel Tips :doc ":Doc-Section ACL2-Tutorial some hints for using the ACL2 prover~/ We present here some tips for using ACL2 effectively. Though this collection is somewhat ~em[ad hoc], we try to provide some organization, albeit somewhat artificial: for example, the sections overlap, and no particular order is intended. This material has been adapted by Bill Young from a very similar list for Nqthm that appeared in the conclusion of: ``Interaction with the Boyer-Moore Theorem Prover: A Tutorial Study Using the Arithmetic-Geometric Mean Theorem,'' by Matt Kaufmann and Paolo Pecchiari, CLI Technical Report 100, June, 1995. We also draw from a similar list in Chapter 13 of ``A Computational Logic Handbook'' by R.S. Boyer and J S. Moore (Academic Press, 1988). We'll refer to this as ``ACLH'' below. These tips are organized roughly as follows. ~bq[] A. ACL2 Basics B. Strategies for creating events C. Dealing with failed proofs D. Performance tips E. Miscellaneous tips and knowledge F. Some things you DON'T need to know ~eq[]~/ ~em[ACL2 BASICS] ~b[A1. The ACL2 logic.]~nl[] This is a logic of total functions. For example, if ~c[A] and ~c[B] are less than or equal to each other, then we need to know something more in order to conclude that they are equal (e.g., that they are numbers). This kind of twist is important in writing definitions; for example, if you expect a function to return a number, you may want to apply the function ~ilc[fix] or some variant (e.g., ~ilc[nfix] or ~ilc[ifix]) in case one of the formals is to be returned as the value. ACL2's notion of ordinals is important on occasion in supplying ``measure ~il[hints]'' for the acceptance of recursive definitions. Be sure that your measure is really an ordinal. Consider the following example, which ACL2 fails to admit (as explained below). ~bv[] (defun cnt (name a i x) (declare (xargs :measure (+ 1 i))) (cond ((zp (+ 1 i)) 0) ((equal x (aref1 name a i)) (1+ (cnt name a (1- i) x))) (t (cnt name a (1- i) x)))) ~ev[] One might think that ~c[(+ 1 i)] is a reasonable measure, since we know that ~c[(+ 1 i)] is a positive integer in any recursive call of ~c[cnt], and positive integers are ACL2 ordinals (~pl[o-p]). However, the ACL2 logic requires that the measure be an ordinal unconditionally, not just under the governing assumptions that lead to recursive calls. An appropriate fix is to apply ~ilc[nfix] to ~c[(+ 1 i)], i.e., to use ~bv[] (declare (xargs :measure (nfix (+ 1 i)))) ~ev[] in order to guarantee that the measure will always be an ordinal (in fact, a positive integer). For more about admissibility of recursive definitions, ~pl[defun], in particular the discussion of termination. ~b[A2. Simplification.]~nl[] The ACL2 simplifier is basically a rewriter, with some ``~il[linear] arithmetic'' thrown in. One needs to understand the notion of conditional rewriting. ~l[rewrite]. ~b[A3. Parsing of rewrite rules.]~nl[] ACL2 parses ~il[rewrite] rules roughly as explained in ACLH, ~em[except] that it never creates ``unusual'' rule classes. In ACL2, if you want a ~c[:]~ilc[linear] rule, for example, you must specify ~c[:]~ilc[linear] in the ~c[:]~ilc[rule-classes]. ~l[rule-classes], and also ~pl[rewrite] and ~pl[linear]. ~b[A4. Linear arithmetic.]~nl[] On this subject, it should suffice to know that the prover can handle truths about ~ilc[+] and ~ilc[-], and that ~il[linear] rules (see above) are somehow ``thrown in the pot'' when the prover is doing such reasoning. Perhaps it's also useful to know that ~il[linear] rules can have hypotheses, and that conditional rewriting is used to relieve those hypotheses. ~b[A5. Events.]~nl[] Over time, the expert ACL2 user will know some subtleties of its ~il[events]. For example, ~ilc[in-theory] ~il[events] and ~il[hints] are important, and they distinguish between a function and its executable counterpart. ~em[B. STRATEGIES FOR CREATING EVENTS] In this section, we concentrate on the use of definitions and ~il[rewrite] rules. There are quite a few kinds of rules allowed in ACL2 besides ~il[rewrite] rules, though most beginning users probably won't usually need to be aware of them. ~l[rule-classes] for details. In particular, there is support for ~il[congruence] rewriting. Also ~pl[rune] (``RUle NamE'') for a description of the various kinds of rules in the system. ~b[B1. Use high-level strategy.]~nl[] Decompose theorems into ``manageable'' lemmas (admittedly, experience helps here) that yield the main result ``easily.'' It's important to be able to outline non-trivial proofs by hand (or in your head). In particular, avoid submitting goals to the prover when there's no reason to believe that the goal will be proved and there's no ``sense'' of how an induction argument would apply. It is often a good idea to avoid induction in complicated theorems unless you have a reason to believe that it is appropriate. ~b[B2. Write elegant definitions.]~nl[] Try to write definitions in a reasonably modular style, especially recursive ones. Think of ACL2 as a ~il[programming] language whose procedures are definitions and lemmas, hence we are really suggesting that one follow good ~il[programming] style (in order to avoid duplication of ``code,'' for example). When possible, complex functions are best written as compositions of simpler functions. The theorem prover generally performs better on primitive recursive functions than on more complicated recursions (such as those using accumulating parameters). Avoid large non-recursive definitions which tend to lead to large case explosions. If such definitions are necessary, try to prove all relevant facts about the definitions and then ~il[disable] them. Whenever possible, avoid mutual recursion if you care to prove anything about your functions. The induction heuristics provide essentially no help with reasoning about mutually defined functions. Mutually recursive functions can usually be combined into a single function with a ``flag'' argument. (However, ~pl[mutual-recursion-proof-example] for a small example of proof involving mutually recursive functions.) ~b[B3. Look for analogies.]~nl[] Sometimes you can easily edit sequences of lemmas into sequences of lemmas about analogous functions. ~b[B4. Write useful rewrite rules.]~nl[] As explained in A3 above, every ~il[rewrite] rule is a directive to the theorem prover, usually to replace one ~il[term] by another. The directive generated is determined by the syntax of the ~ilc[defthm] submitted. Never submit a ~il[rewrite] rule unless you have considered its interpretation as a proof directive. ~b[B4a. Rewrite rules should simplify.]~nl[] Try to write ~il[rewrite] rules whose right-hand sides are in some sense ``simpler than'' (or at worst, are variants of) the left-hand sides. This will help to avoid infinite loops in the rewriter. ~b[B4b. Avoid needlessly expensive rules.]~nl[] Consider a rule whose conclusion's left-hand side (or, the entire conclusion) is a ~il[term] such as ~c[(consp x)] that matches many ~il[term]s encountered by the prover. If in addition the rule has complicated hypotheses, this rule could slow down the prover greatly. Consider switching the conclusion and a complicated hypothesis (negating each) in that case. ~b[B4c. The ``Knuth-Bendix problem''.]~nl[] Be aware that left sides of ~il[rewrite] rules should match the ``normalized forms'', where ``normalization'' (rewriting) is inside out. Be sure to avoid the use of nonrecursive function symbols on left sides of ~il[rewrite] rules, except when those function symbols are ~il[disable]d, because they tend to be expanded away before the rewriter would encounter an instance of the left side of the rule. Also assure that subexpressions on the left hand side of a rule are in simplified form. ~b[B4d. Avoid proving useless rules.]~nl[] Sometimes it's tempting to prove a ~il[rewrite] rule even before you see how it might find application. If the rule seems clean and important, and not unduly expensive, that's probably fine, especially if it's not too hard to prove. But unless it's either part of the high-level strategy or, on the other hand, intended to get the prover past a particular unproved goal, it may simply waste your time to prove the rule, and then clutter the database of rules if you are successful. ~b[B4e. State rules as strongly as possible, usually.]~nl[] It's usually a good idea to state a rule in the strongest way possible, both by eliminating unnecessary hypotheses and by generalizing subexpressions to variables. Advanced users may choose to violate this policy on occasion, for example in order to avoid slowing down the prover by excessive attempted application of the rule. However, it's a good rule of thumb to make the strongest rule possible, not only because it will then apply more often, but also because the rule will often be easier to prove (see also B6 below). New users are sometimes tempted to put in extra hypotheses that have a ``type restriction'' appearance, without realizing that the way ACL2 handles (total) functions generally lets it handle trivial cases easily. ~b[B4f. Avoid circularity.]~nl[] A stack overflow in a proof attempt almost always results from circular rewriting. Use ~ilc[brr] to investigate the stack; ~pl[break-lemma]. Because of the complex heuristics, it is not always easy to define just when a ~il[rewrite] will cause circularity. See the very good discussion of this topic in ACLH. ~l[break-lemma] for a trick involving use of the forms ~c[brr t] and ~c[(cw-gstack)] for inspecting loops in the rewriter. ~b[B4g. Remember restrictions on permutative rules.]~nl[] Any rule that permutes the variables in its left hand side could cause circularity. For example, the following axiom is automatically supplied by the system: ~bv[] (defaxiom commutativity-of-+ (equal (+ x y) (+ y x))). ~ev[] This would obviously lead to dangerous circular rewriting if such ``permutative'' rules were not governed by a further restriction. The restriction is that such rules will not produce a ~il[term] that is ``lexicographically larger than'' the original ~il[term] (~pl[loop-stopper]). However, this sometimes prevents intended rewrites. See Chapter 13 of ACLH for a discussion of this problem. ~b[B5. Conditional vs. unconditional rewrite rules.]~nl[] It's generally preferable to form unconditional ~il[rewrite] rules unless there is a danger of case explosion. That is, rather than pairs of rules such as ~bv[] (implies p (equal term1 term2)) ~ev[] and ~bv[] (implies (not p) (equal term1 term3)) ~ev[] consider: ~bv[] (equal term1 (if p term2 term3)) ~ev[] However, sometimes this strategy can lead to case explosions: ~ilc[IF] ~il[term]s introduce cases in ACL2. Use your judgment. (On the subject of ~ilc[IF]: ~ilc[COND], ~ilc[CASE], ~ilc[AND], and ~ilc[OR] are macros that abbreviate ~ilc[IF] forms, and propositional functions such as ~ilc[IMPLIES] quickly expand into ~ilc[IF] ~il[term]s.) ~b[B6. Create elegant theorems.]~nl[] Try to formulate lemmas that are as simple and general as possible. For example, sometimes properties about several functions can be ``factored'' into lemmas about one function at a time. Sometimes the elimination of unnecessary hypotheses makes the theorem easier to prove, as does generalizing first by hand. ~b[B7. Use] ~ilc[defaxiom]s ~b[temporarily to explore possibilities.]~nl[] When there is a difficult goal that seems to follow immediately (by a ~c[:use] hint or by rewriting) from some other lemmas, you can create those lemmas as ~ilc[defaxiom] ~il[events] (or, the application of ~ilc[skip-proofs] to ~ilc[defthm] ~il[events]) and then double-check that the difficult goal really does follow from them. Then you can go back and try to turn each ~ilc[defaxiom] into a ~ilc[defthm]. When you do that, it's often useful to ~il[disable] any additional ~il[rewrite] rules that you prove in the process, so that the ``difficult goal'' will still be proved from its lemmas when the process is complete. Better yet, rather than disabling ~il[rewrite] rules, use the ~ilc[local] mechanism offered by ~ilc[encapsulate] to make temporary rules completely ~ilc[local] to the problem at hand. ~l[encapsulate] and ~pl[local]. ~b[B9. Use books.]~nl[] Consider using previously certified ~il[books], especially for arithmetic reasoning. This cuts down the duplication of effort and starts your specification and proof effort from a richer foundation. ~l[community-books]. ~em[C. DEALING WITH FAILED PROOFS] ~b[C1. Look in proof output for goals that can't be further simplified.]~nl[] Use the ``~il[proof-tree]'' utility to explore the proof space. However, you don't need to use that tool to use the ``checkpoint'' strategy. The idea is to think of ACL2 as a ``simplifier'' that either proves the theorem or generates some goal to consider. That goal is the first ``checkpoint,'' i.e., the first goal that does not further simplify. Exception: it's also important to look at the induction scheme in a proof by induction, and if induction seems appropriate, then look at the first checkpoint ~em[after] the induction has begun. Consider whether the goal on which you focus is even a theorem. Sometimes you can execute it for particular values to find a counterexample. When looking at checkpoints, remember that you are looking for any reason at all to believe the goal is a theorem. So for example, sometimes there may be a contradiction in the hypotheses. Don't be afraid to skip the first checkpoint if it doesn't seem very helpful. Also, be willing to look a few lines up or down from the checkpoint if you are stuck, bearing in mind however that this practice can be more distracting than helpful. ~b[C2. Use the ``break rewrite'' facility.]~nl[] ~ilc[Brr] and related utilities let you inspect the ``rewrite stack.'' These can be valuable tools in large proof efforts. ~l[break-lemma] for an introduction to these tools, and ~pl[break-rewrite] for more complete information. The break facility is especially helpful in showing you why a particular rewrite rule is not being applied. ~b[C3. Use induction hints when necessary.] Of course, if you can define your functions so that they suggest the correct inductions to ACL2, so much the better! But for complicated inductions, induction ~il[hints] are crucial. ~l[hints] for a description of ~c[:induct] ~il[hints]. ~b[C4. Use the ``Proof Checker'' to explore.]~nl[] The ~ilc[verify] command supplied by ACL2 allows one to explore problem areas ``by hand.'' However, even if you succeed in proving a conjecture with ~ilc[verify], it is useful to prove it without using it, an activity that will often require the discovery of ~il[rewrite] rules that will be useful in later proofs as well. ~b[C5. Don't have too much patience.]~nl[] Interrupt the prover fairly quickly when simplification isn't succeeding. ~b[C6. Simplify rewrite rules.]~nl[] When it looks difficult to relieve the hypotheses of an existing ~il[rewrite] rule that ``should'' apply in a given setting, ask yourself if you can eliminate a hypothesis from the existing ~il[rewrite] rule. If so, it may be easier to prove the new version from the old version (and some additional lemmas), rather than to start from scratch. ~b[C7. Deal with base cases first.]~nl[] Try getting past the base case(s) first in a difficult proof by induction. Usually they're easier than the inductive step(s), and rules developed in proving them can be useful in the inductive step(s) too. Moreover, it's pretty common that mistakes in the statement of a theorem show up in the base case(s) of its proof by induction. ~b[C8. Use] ~c[:expand] ~b[hints.] Consider giving ~c[:expand] ~il[hints]. These are especially useful when a proof by induction is failing. It's almost always helpful to open up a recursively defined function that is supplying the induction scheme, but sometimes ACL2 is too timid to do so; or perhaps the function in question is ~il[disable]d. ~em[D. PERFORMANCE TIPS] ~b[D1. Disable rules.]~nl[] There are a number of instances when it is crucial to ~il[disable] rules, including (often) those named explicitly in ~c[:use] ~il[hints]. Also, ~il[disable] recursively defined functions for which you can prove what seem to be all the relevant properties. The prover can spend significant time ``behind the scenes'' trying to open up recursively defined functions, where the only visible effect is slowness. ~b[D2. Turn off the ``break rewrite'' facility.] Remember to execute ~c[:brr nil] after you've finished with the ``break rewrite'' utility (~pl[break-rewrite]), in order to bring the prover back up to full speed. ~em[E. MISCELLANEOUS TIPS AND KNOWLEDGE] ~b[E1. Order of application of rewrite rules.]~nl[] Keep in mind that the most recent ~il[rewrite] rules in the ~il[history] are tried first. ~b[E2. Relieving hypotheses is not full-blown theorem proving.]~nl[] Relieving hypotheses on ~il[rewrite] rules is done by rewriting and ~il[linear] arithmetic alone, not by case splitting or by other prover processes ``below'' simplification. ~b[E3. ``Free variables'' in rewrite rules.]~nl[] The set of ``free variables'' of a ~il[rewrite] rule is defined to contain those variables occurring in the rule that do not occur in the left-hand side of the rule. It's often a good idea to avoid rules containing free variables because they are ``weak,'' in the sense that hypotheses containing such variables can generally only be proved when they are ``obviously'' present in the current context. This weakness suggests that it's important to put the most ``interesting'' (specific) hypotheses about free variables first, so that the right instances are considered. For example, suppose you put a very general hypothesis such as ~c[(consp x)] first. If the context has several ~il[term]s around that are known to be ~ilc[consp]s, then ~c[x] may be bound to the wrong one of them. For much more information on free variables, ~pl[free-variables]. ~b[E4. Obtaining information]~nl[] Use ~c[:]~ilc[pl] ~c[foo] to inspect ~il[rewrite] rules whose left hand sides are applications of the function ~c[foo]. Another approach to seeing which ~il[rewrite] rules apply is to enter the ~il[proof-checker] with ~ilc[verify], and use the ~c[show-rewrites] or ~c[sr] command. ~b[E5. Consider esoteric rules with care.]~nl[] If you care to ~pl[rule-classes] and peruse the list of subtopics (which will be listed right there in most versions of this ~il[documentation]), you'll see that ACL2 supports a wide variety of rules in addition to ~c[:]~il[rewrite] rules. Should you use them? This is a complex question that we are not ready to answer with any generality. Our general advice is to avoid relying on such rules as long as you doubt their utility. More specifically: be careful not to use conditional type prescription rules, as these have been known to bring ACL2 to its knees, unless you are conscious that you are doing so and have reason to believe that they are working well. ~em[F. SOME THINGS YOU DON'T NEED TO KNOW] Most generally: you shouldn't usually need to be able to predict too much about ACL2's behavior. You should mainly just need to be able to react to it. ~b[F1. Induction heuristics.]~nl[] Although it is often important to read the part of the prover's output that gives the induction scheme chosen by the prover, it is not necessary to understand how the prover made that choice. (Granted, advanced users may occasionally gain minor insight from such knowledge. But it's truly minor in many cases.) What ~em[is] important is to be able to tell it an appropriate induction when it doesn't pick the right one (after noticing that it doesn't). See C3 above. ~b[F2. Heuristics for expanding calls of recursively defined functions.]~nl[] As with the previous topic, the important thing isn't to understand these heuristics but, rather, to deal with cases where they don't seem to be working. That amounts to supplying ~c[:expand] ~il[hints] for those calls that you want opened up, which aren't. See also C8 above. ~b[F3. The ``waterfall''.]~nl[] As discussed many times already, a good strategy for using ACL2 is to look for checkpoints (goals stable under simplification) when a proof fails, perhaps using the ~il[proof-tree] facility. Thus, it is reasonable to ignore almost all the prover output, and to avoid pondering the meaning of the other ``processes'' that ACL2 uses besides simplification (such as elimination, cross-fertilization, generalization, and elimination of irrelevance). For example, you don't need to worry about prover output that mentions ``type reasoning'' or ``abbreviations,'' for example.") (deflabel introduction-to-the-theorem-prover :doc ":Doc-Section acl2-tutorial how the theorem prover works -- level 0~/ Software is complex, and ACL2 is a piece of software that is used to analyze software -- adding another layer of complexity. Furthermore, instead of being limited to static analysis for certain fixed properties, ACL2 allows you -- indeed, forces you -- to formalize the problem and the questions. It ``knows'' nothing inherent about your problem before you start to interact with it. But it can be used to help answer the most complicated questions you can ask about software. All this is to say that it is not the kind of tool that you just install and then start to use effectively. So OK, you've installed it or confirmed that you can invoke it. Good for you. Now you have to learn how to use it! Your success ultimately comes down to your understanding of your problem domain and your appropriate exploitation of ACL2's strengths and avoidance of its weaknesses. So put aside the idea of sitting down and interacting with it. Instead, learn about it. We assume you know some of the ~il[interesting-applications industrial applications] of ACL2. Realizing that such things can be done may sustain you during the long learning curve! We also assume you have taken both the ~il[|A Flying Tour of ACL2| Flying Tour] and the ~il[|A Walking Tour of ACL2| Walking Tour]. The tours give you a good overview of the whole system where this tutorial focuses on how to use the prover itself. If you haven't visited these links, please do so now. This tutorial will take you several hours -- maybe several days -- to work through. Do not breeze through it as you might a blog. ~i[Think] your way through it. ~i[Remember] what you read. Do not take short cuts. If you start to use ACL2 before you really know how, it will only frustrate you. We recommend that you read this tutorial with an HTML browser so that you can see which links you've followed and which you have not. To give you a sense of what is in store, here is a map of this document. But don't navigate through it from here! Read it linearly, following the links when the text directs you to. ~bv[] ~il[INTRODUCTION-TO-THE-THEOREM-PROVER] ~il[INTRODUCTION-TO-REWRITE-RULES-PART-1] ~il[SPECIAL-CASES-FOR-REWRITE-RULES] ~il[EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES] ~il[INTRODUCTION-TO-KEY-CHECKPOINTS] ~il[DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS] ~il[GENERALIZING-KEY-CHECKPOINTS] ~il[POST-INDUCTION-KEY-CHECKPOINTS] ~il[INTRODUCTION-TO-REWRITE-RULES-PART-2] ~il[STRONG-REWRITE-RULES] ~il[PRACTICE-FORMULATING-STRONG-RULES] ~il[PRACTICE-FORMULATING-STRONG-RULES-1] ~il[PRACTICE-FORMULATING-STRONG-RULES-2] ~il[PRACTICE-FORMULATING-STRONG-RULES-3] ~il[PRACTICE-FORMULATING-STRONG-RULES-4] ~il[PRACTICE-FORMULATING-STRONG-RULES-5] ~il[PRACTICE-FORMULATING-STRONG-RULES-6] ~il[SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES] ~il[FURTHER-INFORMATION-ON-REWRITING] ~il[INTRODUCTION-TO-THE-DATABASE] ~il[INTRODUCTION-TO-HINTS] ~IL[INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS] ~il[INTRODUCTORY-CHALLENGES] ~il[INTRODUCTORY-CHALLENGE-PROBLEM-1] ~il[INTRODUCTORY-CHALLENGE-PROBLEM-2] ~il[INTRODUCTORY-CHALLENGE-PROBLEM-3] ~i[(there are others but at least do a few)] ~il[FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS] ~ev[] If any of the links above are marked as ``visited'' by your browser, use your browser's tools menu to mark all links as unvisited. As you can see, we really think you'll get the most out of this document if you take it seriously. As you read, you will see some links to ``advanced'' topics. These are marked with a tiny warning sign, ``~warn[]''. They lead out of this linear tutorial and into ACL2's hypertext reference manual. We recommend that you ~i[not] visit any of these advanced links until you have read the entire tutorial at least once. After you finish this tutorial material, we recommend that you look at the ACL2 Demos, at the ``Demos'' link of the ACL2 home page, ~url[http://www.cs.utexas.edu/users/moore/acl2]. Most users of ACL2 have bought the book ~i[Computer-Aided Reasoning: An Approach], Kaufmann, Manolios, and Moore, Kluwer Academic Publishers, June, 2000 which is available in paperback from Lulu for approximately $20 (as of 2010). See ~url[http://www.lulu.com/content/1746161]. That book contains hundreds of exercises in programming, proof, and using The Method described here to prove theorems. Solutions to the exercises are online. For more information about the book and a companion one (also available on Lulu) about industrial applications of ACL2, see ~url[http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html#Books] Using ACL2 is akin to having a partner in the theorem proving enterprise. It will do some of the work and you will do some of the work. It can't really be any other way because theorem proving is undecidable. You bring a quirkly, error-prone, creative insight to the problem, and ACL2 brings accuracy, logic, and perserverance. Here we describe a ``model'' of how the system works and introduce some of the ideas and terminology you'll use repeatedly when interacting with it. This article is about the ~i[theorem prover] itself, not the programming language and not the logic. We assume you know enough about the ACL2 programming language that you can define simple functions, run them, and read and write ACL2 constants and terms. For some examples of what we'll take for granted about ACL2 programming, ~pl[programming-knowledge-taken-for-granted]. We also assume you know enough about logic to understand, for example, the words we use to talk about formulas and proofs. To see some examples of what we'll take for granted about your knowledge of logic terminology, ~pl[logic-knowledge-taken-for-granted]. When you give the theorem prover a goal formula to prove, it tries to prove it by breaking it down into subgoals, each of which must be proved in order to prove the original goal. This breaking apart of the goal is done by various ~i[proof techniques] built into ACL2. Different proof techniques break the formula apart in different ways. For example, the ~i[simplifier] rips apart the propositional structure to isolate the various cases and applies rewrite rules to simplify the subterms of the formula, while the ~i[induction] ~i[engine] will attempt to break the goal into some base cases and some induction steps. The theorem prover's behavior is affected by a ~i[database] of ~i[rules] derived from axioms, definitions, and previously proved theorems. The database also records the ~i[enabled status] of each rule; only enabled rules are seen by the prover and you can set the status of a rule. There are many other user-settable switches and parameters affecting the behavior of the prover; you'll learn about some of them later. You guide the theorem prover most of the time simply by identifying lemmas for it to prove. (A ~i[lemma] is just a theorem that you think is useful in the proofs of other theorems.) Why does this guide the theorem prover? Because every time you get the system to prove a theorem, it turns the theorem into a rule (unless you tell it not to) and stores the rule in the database. That changes how the prover behaves subsequently. But ~i[you] determine the kind of rule ACL2 stores. To learn to ``drive'' the theorem prover you have to learn how various rules affect the system's behavior and how it turns proved formulas into rules. But before we discuss this, we discuss a more mathematical activity: how do you figure out the lemmas ACL2 will need in order for it to prove interesting theorems? ACL2 can often help you in this activity, if you use it in the right way. Here is the way we recommend you use ACL2. ~b[The Method]. (1) you present ACL2 with the goal conjecture to prove (2) typically, it fails to prove it (or you abort its attempt), but it prints some ~i[Key Checkpoints] (3) you look at the Key Checkpoints and decide that you know a fact that will help; this tutorial will present some helpful questions to keep in mind (4) you formalize your knowledge as a formula, along with directions for how ACL2 should turn the formula into a rule; this tutorial will tell you about the most commonly used rule, the ~i[rewrite] rule (5) you recursively apply The Method to get ACL2 to prove your formula and to store it as the kind of rule you specified (6) go to step (1) Caveat: This approach simply won't work on some theorems! Basically, this is a ``troubleshooting'' approach, where you're letting ACL2 determine the basic strategy and you're just helping with the subgoals. But for some theorems, ACL2's approach will be misguided and no amount of troubleshooting will salvage its strategy. You'll have a sense that this is happening when it happens because the formulas ACL2 presents to you will raise issues that feel irrelevant to you. The basic truth is that if you think a formula is always true there are usually strong intuitive reasons behind your belief. If you were asked to defend your belief, you'd start to explain your reasons and with training you can turn that into a proof. So when ACL2's formulas present you with things you haven't thought of either (a) you'll have an ``Ah ha!'' moment when you realize you hadn't considered that case or (b) you'll realize that ACL2's approach is different from your intuitive ``proof.'' But, surprisingly often, the troubleshooting approach to finding proofs works quite well, especially as you rein in your expectations and develop a sense of what ACL2 can handle on its own. Of course, if you can decompose the proof into a couple of main lemmas before you start, so much the better: write down your sequence of lemmas, thinking about the rules you want them to generate, and get ACL2 to prove each of them before giving it the main theorem. This ~i[proof planning] approach will gradually become an integral part of your use of The Method. The only mechanized help we can offer with The Method, aside from the theorem prover itself, are tools to help you manage the stack of subgoals it generates when, in step (5) you recursively apply The Method to a lemma. There are both Emacs and Eclipse tools available. To use The Method you have to read the Key Checkpoints printed at the very end of ~i[failed] proof attempts, just after the line that reads: ~bv[] The key checkpoint goals, below, may help you to debug this failure. ~ev[] Most users do not read the output from successful proofs and do not read the output ~i[during] a proof -- they just let it stream by as a sort of gestalt meter on the theorem prover's progress or lack thereof. For example, you'll be able to tell it is in a loop and needs to be interrupted. You will respond to most Key Checkpoints by formulating new lemmas for the system to prove and store as rules designed by you to alter ACL2's behavior so that it proves the Key Checkpoints. You will give each lemma a ~i[name] and write some ~i[formula] to express the mathematical idea. You'll command ACL2 to prove it by typing: ~bv[] (defthm ~i[name] ~i[formula] ...) ~ev[] In the ``...'' you may provide two other kinds of information: ~i[hints] for how to prove ~i[formula] and directives, called ~i[rule-classes], for how to convert ~i[formula] into a rule after it has proved ~i[formula]. Note that ~i[you] are in charge of determining what kind of rule ACL2 generates! There are over a dozen different types of rules with many opportunities to specialize each type. But the most common kind of rule you'll want to generate is a ~i[rewrite] rule. We recommend that you read the following topics in the following order, without skipping anything but links into the reference manual, which are marked by the little warning sign, ``~warn[]''. (1) ~l[introduction-to-rewrite-rules-part-1] to read about the use and design of rewrite rules. (2) ~l[introduction-to-key-checkpoints] to see how to use The Method to help you design rules. (3) ~l[introduction-to-rewrite-rules-part-2] for general guidance on how to turn formulas into effective rules. (4) ~l[introduction-to-the-database] to see how to issue commands that build different kinds of rules and that affect the enabled status of existing rules. (5) ~l[introduction-to-hints] to see how to give the prover hints for how to prove specific theorems or even subgoals of specific proof attempts. (6) ~l[introduction-to-a-few-system-considerations] for a few words about system aspects of interacting with ACL2. (7) ~l[introductory-challenges] for a graduated sequence of good challenge problems for the new user to tackle. ~b[Do not skip this section!] It is here that you ~i[really] learn how to use ACL2 -- by using it. (8) ~l[frequently-asked-questions-by-newcomers] for a list of questions that new users frequently ask, answered mainly by providing links into the reference manual. We recommend that you skim through these questions and remember that you can find the answers here later. We are very interested in receiving suggestions for how to improve this FAQ and this tutorial. See the ACL2 home page, specifically the link ``Mailing Lists''. Please read all of the material cited above (skipping only the reference-manual links (``~warn[]'')) before you try to use ACL2 on problems of your own. By this point you should have read, at least, the following topics from this tutorial introduction to the theorem prover: ~bv[] ~il[INTRODUCTION-TO-THE-THEOREM-PROVER] ~il[INTRODUCTION-TO-REWRITE-RULES-PART-1] ~il[SPECIAL-CASES-FOR-REWRITE-RULES] ~il[EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES] ~il[INTRODUCTION-TO-KEY-CHECKPOINTS] ~il[DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS] ~il[GENERALIZING-KEY-CHECKPOINTS] ~il[POST-INDUCTION-KEY-CHECKPOINTS] ~il[INTRODUCTION-TO-REWRITE-RULES-PART-2] ~il[STRONG-REWRITE-RULES] ~il[PRACTICE-FORMULATING-STRONG-RULES] ~il[PRACTICE-FORMULATING-STRONG-RULES-1] ~il[PRACTICE-FORMULATING-STRONG-RULES-2] ~il[PRACTICE-FORMULATING-STRONG-RULES-3] ~il[PRACTICE-FORMULATING-STRONG-RULES-4] ~il[PRACTICE-FORMULATING-STRONG-RULES-5] ~il[PRACTICE-FORMULATING-STRONG-RULES-6] ~il[SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES] ~il[FURTHER-INFORMATION-ON-REWRITING] ~il[INTRODUCTION-TO-THE-DATABASE] ~il[INTRODUCTION-TO-HINTS] ~IL[INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS] ~il[INTRODUCTORY-CHALLENGES] ~il[INTRODUCTORY-CHALLENGE-PROBLEM-1] ~il[INTRODUCTORY-CHALLENGE-PROBLEM-2] ~il[INTRODUCTORY-CHALLENGE-PROBLEM-3] ~i[(there are others but at least do a few)] ~il[FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS] ~ev[] We also recommend that you look at the ACL2 Demos, at the ``demos'' link of the ACL2 home page ~url[http://www.cs.utexas.edu/users/moore/acl2]. Most users of ACL2 have bought the book ~i[Computer-Aided Reasoning: An Approach], Kaufmann, Manolios, and Moore, Kluwer Academic Publishers, June, 2000 which is available in paperback from Lulu for approximately $20 (as of 2010). See ~url[http://www.lulu.com/content/1746161]. That book contains hundreds of exercises in programming, proof, and using The Method to prove theorems. Solutions to the exercises are online. For more information about the book and a companion one (also available on Lulu) about industrial applications of ACL2, see ~url[http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html#Books] Thank you for spending the time to get acquainted with the basics of the ACL2 theorem prover. Don't hesitate to send further questions to the ACL2 Help address on the ``Mailing Lists'' link of the ACL2 home page. ~b[End of Tutorial Introduction to the Theorem Prover] Below is a list of all of the topics cited on this page. ~/~/") (deflabel dealing-with-key-combinations-of-function-symbols :doc ":Doc-Section introduction-to-the-theorem-prover how to get rid of key combinations of function symbols~/ Suppose ~c[REV] reverses a list, ~c[MEMBER] checks that its first argument is an element of its second, and ~c[SQUARES-PLUS-3P] is some complicated predicate. Suppose you're proving some Main Theorem that involves those concepts and the theorem prover presents you with the following hideous formula as a key checkpoint. What action should you take? Hint: Don't read the formula ``for sense,'' i.e., don't try to understand what this formula is saying! Just look at every subterm involving a nest of two function symbols and ask if you know something about those two symbols that allows you to ~i[simplify] that one subterm. ~bv[] (IMPLIES (AND (CONSP X) (MEMBER (+ 3 (* I I)) (REV X)) (LIST-OF-INTEGERS X) (INTEGERP I) (<= 0 I) (INTEGERP K) (<= 0 K) (< I K) (SQUARES-PLUS-3P K X) (NOT (EQUAL (CAR X) (+ 3 (* I I)))) (NOT (MEMBER (+ 3 (* I I)) X))) (SQUARES-PLUS-3P K (REV X)))? ~ev[] The experienced ACL2 user will stop reading at the second hypothesis! ~bv[] (MEMBER (+ 3 (* I I)) (REV X)) ~ev[] The combination of ~c[MEMBER] and ~c[REV] can be simplified. The question ``is ~c[e] a member of ~c[(REV x)]'' can be answered by asking ``is ~c[e] a member of ~c[x]''. The two questions are equivalent. This insight comes from your intuition about the ~i[semantics] of ~c[REV] -- it just reorders the elements but doesn't add or delete any. The second question is simpler since it doesn't mention ~c[REV], so this is a good transformation to make. And the theorem that they are equivalent is simpler than the key checkpoint above because it involves fewer functions and smaller expressions. You might formalize this insight as ~bv[] (equal (member e (rev x)) (member e x)) ~ev[] But this conjecture is ~i[not] a theorem, because ~c[(member e x)] returns the ~c[cdr] of ~c[x] that begins with ~c[e], not just a Boolean (~c[t] or ~c[nil]) indicating whether ~c[e] is an element of ~c[x]. The location of the first ~c[e] in ~c[(rev x)] is generally different than the location in ~c[x]. So when we say the two questions are ``equivalent'' we don't mean they are equal. We mean that they're propositionally equivalent: both ~c[nil] or both non-~c[nil]. This sense of equivalence is called ``if and only if'' and is checked by the function ~c[iff]. So our intuitive insight can be phrased as this theorem: ~bv[] (iff (member e (rev x)) (member e x)) ~ev[] Suggesting that this formulation of the insight is ``obvious'' begs many questions. Mathematically, we could have avoided ~c[iff] and just written two implications: ~bv[] (and (implies (member e x) (member e (rev x))) (implies (member e (rev x)) (member e x))). ~ev[] or ~bv[] (and (implies (member e x) (member e (rev x))) (implies (not (member e x)) (not (member e (rev x))))). ~ev[] Or we could have used ~c[iff] but ``oriented'' it the other way: ~bv[] (iff (member e x) (member e (rev x))) ~ev[] We choose to write ~bv[] (iff (member e (rev x)) (member e x)) ~ev[] because of ~i[our knowledge of how ACL2 turns formulas into rules!] We deal with this at greater length later. But just to drive the point home, if we issue the command: ~bv[] (defthm member-rev (iff (member e (rev x)) (member e x))) ~ev[] ACL2 will build in a rule that causes every propositional occurrence of ~c[(MEMBER e (REV x))] to be replaced by ~c[(MEMBER e x)]. (By ``propositional occurrence'' we mean an occurrence in which the value is tested, as by ~c[IF] or the propositional connectives. Remember, one might use ~c[member] to determine the location of an element too.) Note carefully: ~i[if you do not tell ACL2 how to make a rule] from a theorem, it makes a rewrite rule. Rewrite rules always replace instances of the left-hand side by the corresponding instances of the right-hand side. That is, when interpreted as a rewrite rule, ~c[(iff ]~i[alpha] ~i[beta]~c[)] makes ACL2 replace ~i[alpha] by ~i[beta]. Probably the biggest mistake new users make is forgetting that every theorem they prove creates a very specific rule. You must remember that you are ~i[programming] ACL2 with these rules. Being careless in your statement of theorems is tantamount to being careless in your programming. What you get is a mess. Had we proved the same equivalence, but with the ~c[iff] commuted, we would be giving ACL2 ~i[bad advice]. We would be telling it ``replace instances of ~c[(MEMBER e x)] by the corresponding instances of ~c[(MEMBER e (REV x))]''! If ACL2 had that rule and ever tried to simplify any ~c[member] expression, e.g., ~c[(MEMBER A B)], it would get into an infinite loop, e.g., producing the following sequence of transformations: ~bv[] (MEMBER A B) (MEMBER A (REV B)) (MEMBER A (REV (REV B))) ... ~ev[] until it eventually exhausted some resource. Recall that we entertained the idea of phrasing our insight about ~c[member] and ~c[rev] with implications rather than ~c[iff]. Generally speaking, implications produce weaker rules -- rules that apply less often. We discuss that later. Now suppose we've proved ~c[member-rev], oriented so as to rewrite ~c[(member e (rev x))] to ~c[(member e x)], and built it in as a rewrite rule. Then suppose we repeated the attempt to prove our Main Theorem. This time, when the prover is processing the hideous Key Checkpoint printed above, our new lemma, ~c[member-rev], will hit it. It will transform the formula to: ~bv[] (IMPLIES (AND (CONSP X) (MEMBER (+ 3 (* I I)) X) ; <-- the hyp has simplified (LIST-OF-INTEGERS X) (INTEGERP I) (<= 0 I) (INTEGERP K) (<= 0 K) (< I K) (SQUARES-PLUS-3P K X) (NOT (EQUAL (CAR X) (+ 3 (* I I)))) (NOT (MEMBER (+ 3 (* I I)) X))) (SQUARES-PLUS-3P K (REV X)))? ~ev[] and then that will collapse to ~c[T], since the ~c[IMPLIES] has contradictory hypotheses (note the last hypothesis above). By proving ~c[member-rev] we proved the hideous checkpoint. We never had to look at the rest of the formula or think about why it is a theorem. Furthermore, attacking the main theorem again, from scratch, with ~c[member-rev] in the database, may eliminate other checkpoints that came up the last time we tried to prove our main goal. So we recommend addressing one checkpoint at a time. This example illustrates that purely ~i[local] thinking -- looking for simplifiable combinations of function symbols -- can sometimes lead to proofs and should always be your first reaction to a key checkpoint: what local fact do you know that would clean up the formula? Don't think about deep questions like ``why is this true?'' until you can't see any way to make it simpler. It is important to train yourself to see combinations of function symbols and to create strong rules for eliminating them. We will give you opportunities to practice this later in the tutorial. If you have been reading the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[INTRODUCTION-TO-KEY-CHECKPOINTS]. ~/~/") (deflabel post-induction-key-checkpoints :doc ":Doc-Section introduction-to-the-theorem-prover reading post-induction key checkpoints~/ Each post-induction key checkpoint is a theorem if and only if the original conjecture was a theorem. The reason is that each subgoal produced by induction concludes with the original formula and simplification preserves equivalence. So if you see a post-induction key checkpoint that is not a theorem, stop looking at the checkpoints! Your original conjecture is not a theorem! Fix it. If you're convinced all the post-induction conjectures are theorems, ask whether each has the hypotheses you'd need to prove it. If the case analysis feels inappropriate or induction hypotheses seem to be missing, then ACL2 might have done the wrong induction. Find the induction scheme it did by reading the first induction message printed after the conjecture was submitted. If it is wrong, then extend ACL2's induction analysis or tell ACL2 what induction to do, as explained shortly. But before you decide the induction hypothesis is missing, look closely for contradictions among the hypotheses of the checkpoint formula. For example, perhaps one of the hypotheses is ~c[(MEMBER e x)] and another is ~c[(NOT (MEMBER e' x'))] where ~c[e], ~c[x], ~c[e'], and ~c[x'] are possibly complicated expressions. Is it possible that ~c[e] and ~c[e'] are equal and ~c[x] and ~c[x'] are equal? If so, then the two hypotheses are contradictory and the checkpoint would be proved if you could find rules that would simplify those expressions to their common forms. So look for theorems about those subexpressions. Or maybe you can get ~c[e] and ~c[e'] to reduce to some common ~c[d] but but find that ~c[x] and ~c[x'] are really different. Then ask whether ~bv[] (implies (member d x) (member d x')) ~ev[] If you could prove that, the key checkpoint would be proved. Of course, you may need other hypotheses from the checkpoint to state your theorems. If you have been working your way through the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to ~il[introduction-to-key-checkpoints]. ~/~/") (deflabel generalizing-key-checkpoints :doc ":Doc-Section introduction-to-the-theorem-prover getting rid of unnecessary specificity~/ Suppose ~c[MEMBER] determines whether its first argument is a member of its second, and ~c[SUBSETP] determines whether every element of its first argument is a member of its second. Suppose that you're trying to prove some Main Theorem and are told the formula below is a key checkpoint. What should you do? ~bv[] Key Checkpoint: (IMPLIES (AND (CONSP A) (INTEGERP (CAR A)) (MEMBER (CAR A) B) (SUBSETP (CDR A) B) (NOT (SUBSETP (CDR A) (APPEND B C)))) (MEMBER (CAR A) C)) ~ev[] The key observation is that the third hypothesis implies the negation of the fourth. Writing it in the contrapositive: ~bv[] (IMPLIES (AND ... (SUBSETP (CDR A) B) ...) (SUBSETP (CDR A) (APPEND B C))) ~ev[] In fact, that is more complicated than it needs to be. A ``correct'' response to the key checkpoint above is to prove ~bv[] (defthm subsetp-append (implies (subsetp a b) (subsetp a (append b c)))). ~ev[] A still better response is to prove this: ~bv[] (defthm subsetp-append-2 (implies (or (subsetp a b) (subsetp a c)) (subsetp a (append b c)))). ~ev[] This version is better because it handles both of the possibilities regarding whether ~c[a] is a subset of the arguments of the ~c[append]. It would be nice if we could think of a ``strong'' version, one in which ~c[(SUBSETP a (APPEND b c))] is rewritten to some clearly simpler term, but nothing comes to mind. In any case, if you cannot see any obvious simplification of the individual terms in the Key Checkpoint, you should ask yourself whether there are connections beween the various propositional parts (as here, with the third and fourth hypotheses). It is a good heuristic to look for relations between parts with the same top-level function symbol (as here, with ~c[SUBSETP]). It is also a good heuristic to throw out parts of the formula that seem disconnected (as here, with the terms involving ~c[(CAR A)]). This discussion suggests several ``modes'' of looking at key checkpoints and the idea of trying the modes in sequence: (1) look for simplifiable combinations of function symbols within propositional components, (2) look for relations between propositional components, and (3) throw out irrelevant or weakly connected components. In all cases you are bringing to bear your intuitions about the ~i[semantics] of the terms. That is, you're not just slinging symbols. You should be looking out for obvious truths about individual parts of the checkpoints... truths that are obvious to you but not to ACL2! Ultimately the three ``modes'' are the same and we do not really recommend adhering to the given sequence. You're just looking for simpler theorems that, in combination, imply the checkpoint. Keeping the ``modes'' in mind may help focus your attention so you consider all the plausible possibilities. After a little experience you'll find yourself looking for all these things simultaneously as part ``cleaning up'' the checkpoints. When your main goal theorems are harder than these, your main concern will be looking at a Key Checkpoint and asking yourself ``why is this true?'' But you don't want to do that until you've cleaned it up ``locally'' as much as possible and sometimes -- more often than you might think -- local considerations can prove many of your checkpoints. If you have been working your way through the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-key-checkpoints]. ~/~/") (deflabel strong-rewrite-rules :doc ":Doc-Section introduction-to-the-theorem-prover formulating good rewrite rules~/ Suppose you notice the following term in a Key Checkpoint: ~bv[] (MEMBER (CAR A) (REV B)). ~ev[] You might think of two theorems for handling this term, which we'll call the ``weak'' and ``strong'' version of ~c[member-rev]. ~bv[] (defthm member-rev-weak (implies (member e b) (member e (rev b)))). (defthm member-rev-strong (iff (member e (rev b)) (member e b))). ~ev[] The ``strong'' version logically implies the ``weak'' version and so deserves the adjective. (Recall that ``~i[p] <---> ~i[q]'' is just ``~i[p] ---> ~i[q]'' and ``~i[q] ---> ~i[p].'' So the strong version quite literally says everything the weak version does and then some.) But the ``strong'' version also produces a ~i[better] rewrite rule. Here are the rules generated from these two formulas, phrased as directives to ACL2's simplifier: ~c[member-rev-weak]: If you ever see an instance of ~c[(MEMBER e (REV b))], backchain to ~c[(MEMBER e b)] (i.e., turn your attention to that term) and if you can show that it is true, replace ~c[(MEMBER e (REV b))] by ~c[T]. ~c[member-rev-strong]: If you ever see an instance of ~c[(MEMBER e (REV b))], replace it by ~c[(MEMBER e b)]. Technical Note: Actually, both rules concern ~i[propositional] occurrences of the ``target'' expression, ~c[(MEMBER e (REV b))], i.e., occurrences of the target in which its truthvalue is the only thing of relevance. (Recall that ~c[(MEMBER x y)] returns the tail of ~c[y] starting with ~c[x]! Evaluate some simple ~c[MEMBER] expressions, like ~c[(MEMBER 3 '(1 2 3 4 5))] to see what we mean.) Both theorems tell us about circumstances in which the target is non-~c[NIL] (i.e., ``true'') without telling us its identity. But ACL2 keeps track of when the target is in a propositional occurrence and can use such rules to rewrite the target to propositionally equivalent terms. So the strong version is better because it will always eliminate ~c[(MEMBER e (REV b))] in favor of ~c[(MEMBER e b)]. That simpler expression may be further reduced if the context allows it. But in any case, the strong version eliminates ~c[REV] from the problem. The weak version only eliminates ~c[REV] when a side-condition can be proved. While either version may permit us to prove the Key Checkpoint that ``suggested'' the rule, the strong version is a better rule to have in the database going forward. For example, suppose ~c[NATS-BELOW] returns the list of natural numbers below its argument. Imagine two alternative scenarios in which a key checkpoint is about to arise involving this term: ~bv[] (MEMBER K (REV (NATS-BELOW J))) ~ev[] ~b[Scenario 1] is when you've got the strong version in your database: it will rewrite the key checkpoint term to ~bv[] (MEMBER K (NATS-BELOW J)) ~ev[] and that's what you'll see in the printed checkpoint unless other rules reduce it further. ~b[Scenario 2] is when you have only the weak version in your database: the weak rule will attempt to reduce the term above to ~c[T] and will, if there are sufficient rules and hypotheses about ~c[K]'s membership in ~c[(NATS-BELOW J)]. But if no such rules or hypotheses are available, you'll be confronted with a key checkpoint involving ~bv[] (MEMBER K (REV (NATS-BELOW J))) ~ev[] and ~i[it will not be obvious] that the problem would go away if you could establish that ~c[K] is in ~c[(NATS-BELOW J)]. Clearly, Scenario 1 is better. We recommend that you now practice formulating strong versions of rules suggested by terms you might see. ~l[practice-formulating-strong-rules]. When you are finished with that, use your browser's ~b[Back Button] to return to ~il[introduction-to-key-checkpoints]. ~/~/") (deflabel practice-formulating-strong-rules :doc ":Doc-Section introduction-to-the-theorem-prover a few simple checkpoints suggesting strong rules~/ Consider these definitions: ~bv[] (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) (defun nats-below (j) (if (zp j) '(0) (cons j (nats-below (- j 1))))) ~ev[] We assume you are familiar with such ACL2 built-ins as ~c[append], ~c[member], ~c[subsetp] and ~c[true-listp]. When we use throw-away names like ~c[FOO], ~c[BAR], and ~c[MUM] below we mean to suggest some arbitrary function you shouldn't think about! We're just trying to train your eye to ignore irrelevant things. Below are some terms that should suggest rewrite rules to you. Imagine that each of these terms occurs in some Key Checkpoint. What rules come to mind? Try to think of the strongest rules you can. Term 1:~nl[] ~c[(TRUE-LISTP (APPEND (FOO A) (BAR B)))] Answers: ~l[practice-formulating-strong-rules-1] Term 2:~nl[] ~c[(TRUE-LISTP (REV (FOO A)))] Answers: ~l[practice-formulating-strong-rules-2] Term 3:~nl[] ~c[(MEMBER (FOO A) (APPEND (BAR B) (MUM C)))] Answers: ~l[practice-formulating-strong-rules-3] Term 4:~nl[] ~c[(SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))] Answers: ~l[practice-formulating-strong-rules-4] Term 5:~nl[] ~c[(SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))] Answers: ~l[practice-formulating-strong-rules-5] Term 6:~nl[] ~c[(MEMBER (FOO A) (NATS-BELOW (BAR B)))] Answers: ~l[practice-formulating-strong-rules-6] We recommend doing all of these little exercises. When you're finished, use your browser's ~b[Back Button] to return to ~il[strong-rewrite-rules]. ~/~/") (deflabel practice-formulating-strong-rules-1 :doc ":Doc-Section introduction-to-the-theorem-prover rules suggested by ~c[(TRUE-LISTP (APPEND (FOO A) (BAR B)))]~/ What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of ~i[strong] rules (~pl[strong-rewrite-rules]). ~bv[] (TRUE-LISTP (APPEND (FOO A) (BAR B))) ~ev[] Obviously, you must think about the conditions under which ~c[(APPEND x y)] returns a true-list. Recall that ~c[APPEND] concatentates ~c[x] and ~c[y], with ~c[y] being the terminal sublist. Its definition is equivalent to ~bv[] (defun append (x y) (if (endp x) y (cons (car x) (append (cdr x) y)))) ~ev[] Technical Note: ~c[Append] is really a macro that permits you to write calls of ~c[append] with more than two arguments. In a sense, ~c[append] ``expects'' its arguments to be lists ending in ~c[nil], so-called ~c[true-listp]s. (Such expectations are formalized in ACL2 by the notion of the ``~il[guard]'' ~warn[] of the function, but we strongly recommend not investigating guards until you're good at using the system.) New users frequently start every new theorem by listing all their expectations on the arguments of functions in the problem. For example, if the new user wants to make some statement about when ~c[(append x y)] is a ~c[true-listp], it is not uncommon for him or her first to write: ~bv[] (implies (and (true-listp x) (true-listp y)) ...) ~ev[] to get ``comfortable.'' Then, thinking about when ~c[(append x y)] is a ~c[true-listp] is easy: it always returns a ~c[true-listp]. It's always a ~c[true-listp].'' This thinking produces the theorem: ~bv[] (defthm true-listp-append-really-weak (implies (and (true-listp x) (true-listp y)) (true-listp (append x y)))) ~ev[] You'll note we gave it a name suggesting it is ``really weak.'' One sense in which it is weak is that it has an unnecessary hypothesis. If ~c[y] is a ~c[true-listp], then ~c[(append x y)] is too, whether ~c[x] is a ~c[true-listp] or not. In ACL2, all functions are total. Logically speaking, it doesn't matter whether ~c[endp] expects its argument to be a ~c[true-listp] or not, it behaves. ~c[(Append x y)] either returns ~c[y] or a ~c[cons] whose second argument is generated by ~c[append]. Thus, if ~c[y] is a ~c[true-listp], the answer is too. So here is an improved version of the rule: ~bv[] (defthm true-listp-append-weak (implies (true-listp y) (true-listp (append x y)))) ~ev[] We still think of it as ``weak'' because it has a hypothesis that limits its applicability. The strong version of the rule is ~bv[] (defthm true-listp-append-strong (equal (true-listp (append x y)) (true-listp y))). ~ev[] That is, ~c[append] returns a ~c[true-listp] ~i[precisely] when its second argument is a ~c[true-listp]. We recommend that the strong version be made a :~ilc[rewrite] ~warn[] rule. The weak version of the rule allows us to reduce ~c[(TRUE-LISTP (APPEND x y))] to true if we can show that ~c[(TRUE-LISTP y)] is true. But suppose ~c[(TRUE-LISTP y)] is actually false. Then ~c[(TRUE-LISTP (APPEND x y))] would not simplify under the weak version of the rule. But under the strong version it would simplify to ~c[NIL]. Technical Note: The weak version of the rule is a useful :~ilc[type-prescription] ~warn[] rule. The type mechanism cannot currently exploit the strong version of the rule. The strategy of ``getting comfortable'' by adding a bunch of hypotheses before you know you need them is not conducive to creating strong rules. We tend to state the main relationship that we intuit about some function and then add the hypotheses we need to make it true. In this case, there were no necessary hypotheses. But if there are, we first identify them and then we ask ``what can I say about the function if these hypotheses aren't true?'' and try to strengthen the statement still further. Use your browser's ~b[Back Button] now to return to ~il[practice-formulating-strong-rules].~/~/") (deflabel practice-formulating-strong-rules-2 :doc ":Doc-Section introduction-to-the-theorem-prover rules suggested by ~c[(TRUE-LISTP (REV (FOO A)))]~/ What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of ~i[strong] rules (~pl[strong-rewrite-rules]). ~bv[] (TRUE-LISTP (REV (FOO A))) ~ev[] The definition of ~c[rev] in this problem is ~bv[] (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) ~ev[] Since the definition terminates with an ~c[endp] test and otherwise ~c[cdr]s the argument, the author of ~c[rev] was clearly expecting ~c[x] to be a ~c[true-listp]. (Indeed, the ``~il[guard]'' ~warn[] for ~c[rev] must require include ~c[(true-listp x)] since that is ~c[endp]'s guard.) So you're naturally justified in limiting your thoughts about ~c[(rev x)] to ~c[x] that are true-lists. This gives rise to the theorem: ~bv[] (defthm true-listp-rev-weak (implies (true-listp x) (true-listp (rev x)))) ~ev[] This is the kind of thinking illustrated in the earlier ~c[append] example (~pl[practice-formulating-strong-rules-1]) and, to paraphrase Z in ~i[Men in Black], it exemplifies ``everything we've come to expect from years of training with typed languages.'' But logically speaking, the definition of ~c[rev] does not require ~c[x] to be a ~c[true-listp]. It can be any object at all: ACL2 functions are total. ~c[Rev] either returns ~c[nil] or the result of appending a singleton list onto the right end of its recursive result. That ~c[append] always returns a ~c[true-listp] since the singleton list is a true list. (~l[practice-formulating-strong-rules-1].) So this is a theorem and a very useful :~ilc[rewrite] ~warn[] rule: ~bv[] (defthm true-listp-rev-strong (true-listp (rev x))). ~ev[] Use your browser's ~b[Back Button] now to return to ~il[practice-formulating-strong-rules]. ~/~/") (deflabel practice-formulating-strong-rules-3 :doc ":Doc-Section introduction-to-the-theorem-prover rules suggested by ~c[(MEMBER (FOO A) (APPEND (BAR B) (MUM C)))]~/ What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of ~i[strong] rules (~pl[strong-rewrite-rules]). ~bv[] (MEMBER (FOO A) (APPEND (BAR B) (MUM C))) ~ev[] Since ~c[(append x y)] contains all the members of ~c[x] and all the members of ~c[y], ~c[e] is a member of ~c[(append x y)] precisely when ~c[e] is a member of ~c[x] or of ~c[y]. So a strong statement of this is: ~bv[] (defthm member-append-strong-false (equal (member e (append x y)) (or (member e x) (member e y)))) ~ev[] However, this is not a theorem because ~c[member] is not Boolean. ~c[(Member e x)], for example, returns the first tail of ~c[x] that starts with ~c[e], or else ~c[nil]. To see an example of this formula that evaluates to ~c[nil], let ~bv[] e = 3 x = '(1 2 3) y = '(4 5 6). ~ev[] Then the left-hand side, ~c[(member e (append x y))] evaluates to ~c[(3 4 5 6)] while the right-hand side evaluates to ~c[(3)]. However, the two sides are propositionally equivalent (both either ~c[nil] or non-~c[nil] together). So this is a useful :~ilc[rewrite] ~warn[] rule: ~bv[] (defthm member-append-strong (iff (member e (append x y)) (or (member e x) (member e y)))). ~ev[] It tells the system that whenever it encounters an instance of ~c[(MEMBER e (APPEND x y))] in a propositional occurrence (where only its truthvalue is relevant), it should be replaced by this disjunction of ~c[(MEMBER e x)] and ~c[(MEMBER e y)]. The following two formulas are true but provide much weaker rules and we would not add them: ~bv[] (implies (member e x) (member e (append x y))) (implies (member e y) (member e (append x y))) ~ev[] because they each cause the system to backchain upon seeing ~c[(MEMBER e (APPEND x y))] expressions and will not apply unless one of the two side-conditions can be established. There is a rewrite rule that is even stronger than ~c[member-append-strong]. It is suggested by the counterexample, above, for the ~c[EQUAL] version of the rule. ~bv[] (defthm member-append-really-strong (equal (member e (append x y)) (if (member e x) (append (member e x) y) (member e y)))) ~ev[] While ~c[member-append-strong] only rewrites ~c[member-append] expressions occurring propositionally, the ~c[-really-strong] version rewrites ~i[every] occurrence. However, this rule will be more useful than ~c[member-append-strong] only if you have occurrences of ~c[member] in non-propositional places. For example, suppose you encountered a term like: ~bv[] (CONS (MEMBER e (APPEND x y)) z). ~ev[] Then the ~c[-strong] rule does not apply but the ~c[-really-strong] rule does. Furthermore, the ~c[-really-strong] rule, by itself, is not quite as good as the ~c[-strong] rule in propositional settings! For example, if you have proved the ~c[-really-strong] rule, you'll notice that the system still has to use induction to prove ~bv[] (IMPLIES (MEMBER E A) (MEMBER E (APPEND B A))). ~ev[] The ~c[-really-strong] rule would rewrite it to ~bv[] (IMPLIES (MEMBER E A) (IF (MEMBER E A) (APPEND (MEMBER E A) B) (MEMBER E B))) ~ev[] which would further simplify to ~bv[] (IMPLIES (MEMBER E A) (APPEND (MEMBER E A) B)) ~ev[] What lemma does this suggest? The answer is the rather odd: ~bv[] (implies x (append x y)) ~ev[] which rewrites propositional occurrences of ~c[(APPEND x y)] to ~c[T] if ~c[x] is non-~c[nil]. This is an inductive fact about ~c[append]. A problem with the ~c[-really-strong] rule is that it transforms even propositional occurrences of ~c[member] into mixed propositional and non-propositional occurrences. ~bv[] (defthm member-append-really-strong (equal (member e (append x y)) ; <-- even if this is a propositional occurrence (if (member e x) (append (member e x) y) ; <-- the member in here is not! (member e y)))) ~ev[] So if you are using the ~c[-really-strong] lemma in a situation in which all your ~c[member] expressions are used propositionally, you'll suddenly find yourself confronted with non-propositional uses of ~c[member]. Our advice is not to use the ~c[-really-strong] version unless your application is inherently using ~c[member] in a non-propositional way. Use your browser's ~b[Back Button] now to return to ~il[practice-formulating-strong-rules]. ~/~/") (deflabel practice-formulating-strong-rules-4 :doc ":Doc-Section introduction-to-the-theorem-prover rules suggested by ~c[(SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))]~/ What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of ~i[strong] rules (~pl[strong-rewrite-rules]). ~bv[] (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C)) ~ev[] When is ~c[(append x y)] a subset of ~c[z]? When everything in ~c[x] is in ~c[z] and everything in ~c[y] is in ~c[z]. We would make it a rewrite rule: ~bv[] (defthm subsetp-append-1-strong (equal (subsetp (append x y) z) (and (subsetp x z) (subsetp y z)))) ~ev[] We put the ``~c[-1-]'' in the name because there is a comparable theorem for when the ~c[append] is in the second argument of the ~c[subsetp]; ~pl[practice-formulating-strong-rules-5]. This strong rule is better than the conditional rule; ~bv[] (defthm subsetp-append-1-weak (implies (and (subsetp x z) (subsetp y z)) (subsetp (append x y) z))) ~ev[] for all the usual reasons. Use your browser's ~b[Back Button] now to return to ~il[practice-formulating-strong-rules]. ~/~/") (deflabel practice-formulating-strong-rules-5 :doc ":Doc-Section introduction-to-the-theorem-prover rules suggested by ~c[(SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))]~/ What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of ~i[strong] rules (~pl[strong-rewrite-rules]). ~bv[] (SUBSETP (FOO A) (APPEND (BAR B) (MUM C))) ~ev[] When is ~c[x] a subset of ~c[(append y z)]? Clearly it is if ~c[x] is a subset of ~c[y] or ~c[x] is a subset of ~c[z]. We could write that: ~bv[] (defthm subsetp-append-2-weak (implies (or (subsetp x y) (subsetp x z)) (subsetp x (append y z)))) ~ev[] The rule generated from this is: ``if you ever encounter (an instance of) ~c[(SUBSETP x (APPEND y z))], backchain to the ~c[or] above and try to establish it. If you can establish it, replace the target by ~c[T].'' This does not fully characterize the situation though. For example, ~c['(1 2 3 4)] is a subset of ~c[(append '(1 3) '(2 4))] without being a subset of either argument of the ~c[append]. However, no obvious equivalence comes to mind -- indeed, to express any of the ideas floating around here requires defining and introducing more functions, which is not recommended unless those functions are already in the problem. For example, if you defined the concept of ``~c[set-minus]'' so that ~c[(set-minus x y)] consists of those elements of ~c[x] not in ~c[y], then you could prove: ~bv[] (defthm subset-append-2-strong-but-messy (equal (subsetp x (append y z)) (and (subsetp (set-minus x z) y) (subsetp (set-minus x y) z)))) ~ev[] But this rewrite rule would ``trade'' ~c[append] away and introduce ~c[set-minus]. That might be a good strategy if ~c[set-minus] were already in the problem. But if it were not, it might not be. We wouldn't recommend this rule unless it were helpful in normalizing the expressions in the problem. We recommend sticking with the weak version of the rule, ~bv[] (defthm subsetp-append-2-weak (implies (or (subsetp x y) (subsetp x z)) (subsetp x (append y z)))). ~ev[] This illustrates the fact that sometimes there is no strong version of a rule! Use your browser's ~b[Back Button] now to return to ~il[practice-formulating-strong-rules]. ~/~/") (deflabel practice-formulating-strong-rules-6 :doc ":Doc-Section introduction-to-the-theorem-prover rules suggested by ~c[(MEMBER (FOO A) (NATS-BELOW (BAR B)))]~/ What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of ~i[strong] rules (~pl[strong-rewrite-rules]). ~bv[] (MEMBER (FOO A) (NATS-BELOW (BAR B))) ~ev[] The definition of ~c[NATS-BELOW] is ~bv[] (defun nats-below (j) (if (zp j) '(0) (cons j (nats-below (- j 1))))) ~ev[] Thus, ~c[(nats-below 7)] is ~c[(7 6 5 4 3 2 1 0)]. So when is ~c[k] a member of ~c[(nats-below j)]? The weakest version is ~bv[] (defthm member-nats-below-weak (implies (and (natp k) (natp j) (<= k j)) (member k (nats-below j)))) ~ev[] But clearly we could improve this to: ~bv[] (defthm member-nats-below-weak-better (implies (and (natp k) (natp j)) (iff (member k (nats-below j)) (<= k j)))) ~ev[] or even ~bv[] (defthm member-nats-below-weak-better (implies (natp j) (iff (member k (nats-below j)) (and (natp k) (<= k j))))) ~ev[] Clearly though, we'd like to get rid of the ~c[(natp j)] hypothesis and the neatest plausible version is: ~bv[] (defthm member-nats-below-weak-neatest (iff (member k (nats-below j)) (and (natp j) (natp k) (<= k j)))) ~ev[] But it is not a theorem! For example, if ~c[j] is ~c[-1] and ~c[k] is 0, then the left-hand side above returns ~c[t], because ~c[(nats-below j)] is ~c[(0)], but the right-hand side is ~c[nil]. But this suggests a strategy for dealing with necessary hypotheses, like ~c[(natp j)]. We can move them into an ~c[IF] on the right-hand side! Something like this might be a useful rewrite rule: ~bv[] (iff (member k (nats-below j)) (if (natp j) (and (natp k) (<= k j)) ...)). ~ev[] We know, from ~c[member-nats-below-weak-better], that if ~c[(natp j)] is true, the ~c[member] is equivalent to ~c[(and (natp k) (<= k j))]. So now consider what we know if ~c[(natp j)] is false. If we can think of some term it's equivalent to nd that term is simpler than the ~c[member] expression, we have a strong rule. But by inspection of the definition of ~c[nats-below], we see that when ~c[(natp j)] is false, ~c[(nats-below j)] is the list ~c[(0)] because ~c[(zp j)] is t. That is, ~c[nats-below] treats all non-natural arguments like they were ~c[0]. Thus, when ~c[(natp j)] is false, ~c[(member k (nats-below j))] is ~c[(member k '(0))], which is ~c[(equal k 0)]. So the strong version is ~bv[] (defthm member-nats-below-strong (iff (member k (nats-below j)) (if (natp j) (and (natp k) (<= k j)) (equal k 0)))) ~ev[] This is a great :~ilc[rewrite] ~warn[] rule. It gets rid of the ~c[member] and ~c[nats-below] and introduces arithmetic. This example illustrates the idea of putting an ~c[if] on the right-hand-side of the equivalence. Many users tend to limit themselves to propositional forms inside ~c[iff] or to simple expressions inside of ~c[equal]. But it is quite natural to use ~c[if] to express what the answer is: if ~c[j] is a natural, then ~c[k] is in ~c[(nats-below j)] precisely if ~c[k] is a natural less than or equal to ~c[j]; if ~c[j] is not a natural, then ~c[k] is in ~c[(nats-below j)] precisely if ~c[k] is ~c[0]. Use ~c[if] to lay out the cases you must consider, if you can think of a simpler, equivalent expression for every possible case. Use your browser's ~b[Back Button] now to return to ~il[practice-formulating-strong-rules]. ~/~/") (deflabel introduction-to-key-checkpoints :doc ":Doc-Section introduction-to-the-theorem-prover What questions to ask at key checkpoints~/ We assume you've read about rewrite rules; ~pl[introduction-to-rewrite-rules-part-1]. When a proof attempt fails, ACL2 prints some ~i[key checkpoints]. These are formulas that we think you should look at. There are two kinds printed: key checkpoints ~i[before an induction], and key checkpoints ~i[under a top-level induction]. (Key checkpoints under deeper inductions and checkpoints that aren't considered ``key'' may exist in the proof attempt, but ACL2 doesn't print them at the end of failed proofs because you shouldn't be distracted by them.) Below is a list of questions to ask yourself about the key checkpoints. Initially, we recommend just picking one key checkpoint ~i[before] an induction (perhaps the simplest looking one) and asking these questions. These questions may lead you to look at other key checkpoints. As you gain more experience you'll elaborate and generalize this advice. ~b[(1) Do you believe this formula is a theorem?] If you don't think it is, it's pointless to try to prove it! You should reconsider your top-level formula in light of the special case suggested by this key checkpoint. ~b[(2) Can it be simplified?] Is there some combination of function symbols in it that could be eliminated or simplified by exploiting some simpler fact? By a ``simpler fact'' we mean a theorem about a few of the symbols in this formula. For an example of this ~pl[dealing-with-key-combinations-of-function-symbols]. Don't think about the deep question ``how can I prove the checkpoint?'' until you've got it into its simplest form. ~b[(3) Is the simpler fact already in the database?] If there is some simpler fact that would help clean up the checkpoint but you believe the simpler fact is already in the database, you can use ~c[:]~ilc[pl] ~warn[], ~c[:]~ilc[pc] ~warn[], ~c[:]~ilc[pbt] ~warn[], and other ~i[history] commands to inspect the database; (~pl[history] ~warn[]). But if you find the allegedly relevant simpler fact in the database, you must ask: ~b[why wasn't it used?] There are four principal reasons: (3a) it is disabled -- so enable it; you'll learn how when you read the coming sections on ~il[introduction-to-the-database] and ~il[introduction-to-hints]. (3b) its left-hand side doesn't match the target -- so improve the rule by generalizing its left-hand side or prove a new rule for this situation; if you decide to remove the old rule from the database, see ~i[undo] commands in ~il[history] ~warn[]. (3c) it is an ~c[IFF] rule but the target doesn't occur propositionally -- so see if you you can strengthen the rule to an ~c[EQUAL] rule or weaken the context of the target by changing the conjecture to use the target propositionally; if you decide to remove the old rule from the database, see ~i[undo] commands in ~il[history] ~warn[]. (3d) the hypotheses of the rule cannot be relieved for this occurrence of the target; this can be caused by the rule's hypotheses being too strong (requiring more than they should), or by the hypotheses of the current conjecture being too weak (you forgot some key hypothesis), or by ACL2 not having the rules it needs to prove that the conjecture's hypotheses really do imply the rule's. Tools are available (~c[:]~pl[brr] ~warn[]) help you figure out why the rule failed, so use them and improve the rule, or the current conjecture, or the database as appropriate. ~b[(4) If the simpler fact is not already known, prove it.] This means you must create a new ~c[defthm] event with appropriate ~i[rule-classes] to store your new theorem so that it will be used. ~l[dealing-with-key-combinations-of-function-symbols]. Then you must start using The Method recursively to prove your new lemma. ~b[(5) Otherwise, is this formula something you'd prove by induction?] If you can't simplify it, it may be because you ``need this fact to prove this fact,'' in which case, induction is the right thing to do. But first, remember that in order for a formulas to be provable by induction, it must be very general. Why must it be general? Because in an inductive proof, the main thing you have to work with is the induction hypothesis, which is an instance of the theorem you're proving. If the theorem is not general enough, you won't be able to assume an instance that will help you. ACL2 may try induction even on formulas that are not general enough. Don't assume that the formula is ripe for induction just because ACL2 found an induction to do! Before you ``approve'' a formula for induction, ask whether it is perhaps a special case of some more general theorem. ~l[generalizing-key-checkpoints] now and then come back here. If you found a generalization, you should probably be proving that formula instead of this one. So formulate the appropriate ~c[defthm] and use The Method recursively to prove it. ~b[(6) If the formula is right for induction, did ACL2 do an induction for it?] You can answer that without looking at the proof. Just see if there are any key checkpoints after induction. If not, why didn't ACL2 induct? Perhaps you told ACL2 not to induct! Perhaps no term in the conjecture suggests an appropriate induction? You could remedy this by extending ACL2's induction analysis by adding to the database. Or you could just tell ACL2 what induction to do for this formula. You'll learn about both later (when you read coming sections of the tutorial). ~b[(7) If ACL2 did do an induction, was it the right one?] You can find the induction scheme used by reading the first induction message in the output log after you submitted the conjecture to ACL2. But most often you will realize the ``wrong'' induction was done just by looking at the post-induction key checkpoints, keeping in mind that each is supposed to be a natural special case of the theorem you're proving. Is the case analysis inappropriate? Are induction hypotheses missing? If so, you should look at the induction scheme. If you determine the wrong induction was done, extend ACL2's induction analysis or tell it which induction to do, which you'll learn about in the coming sections of the tutorial. For more advice about looking at post-induction key checkpoints, ~pl[post-induction-key-checkpoints] now and then come back here. ~b[(8) If the post-induction key checkpoints seems plausible,] ~b[then repeat the questions above for each one of them,] ~b[perhaps starting with the simplest.] In any case, after successfully taking whatever action you've decided on, e.g., proving some new lemma and adding it as a rule: ~b[Start over trying to prove your main conjecture.] This is important! Do not just scroll back to the key checkpoints generated the last time you tried to prove it. Instead, re-generate them in the context of your new, improved database and hints. You will be following this general outline almost all of the time that you're interacting with ACL2. You will not often be asking ``Why is ACL2 making me think about this subgoal? What did ACL2 do to get here? How does ACL2 work?'' Two other ideas are helpful to keep in mind. ~b[Is a key checkpoint unexpectedly complicated?] Pay special attention to the case where the complication seems to be the introduction of low-level details you thought you'd dealt with or by the introduction of symbols you didn't expect to see in this proof. These can be signs that you ought to disable some rules in the database (e.g., a definition of a complicated function about which you've proved all the necessary lemmas or some lemma that transforms the problem as was appropriate for some other proof). ~b[Does the theorem prover just hang up, printing nothing?] If this happens, you must interrupt it. How you interrupt the prover is dependent on which Common Lisp and which interface you're using. But most Common Lisps treat control-c as a console interrupt. If you're in Emacs running ACL2 as a shell process, you must type control-c control-c. If you're in ACL2s, hit the ~i[Interrupt Session] button. Interrupting ACL2 can leave you in an interactive loop similar in appearance but different from ACL2's top-level! So pay careful attention to the prompt and ~pl[breaks] ~warn[]. Once you've regained control from the ``runaway'' theorem prover, there are several tools you can use to find out what it is doing in real-time. Generally speaking, when the theorem prover goes silent for a very long time it is either in some kind of rewrite loop caused by rules that cause it to flip back and forth between various supposedly normal forms, or else it has split the problem into a huge number of cases and suffering a combinatoric explosion. ~l[DMR] ~warn[] and, perhaps, ~pl[accumulated-persistence] ~warn[]. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover]. ~/~/") (deflabel programming-knowledge-taken-for-granted :doc ":Doc-Section introduction-to-the-theorem-prover background knowledge in ACL2 programming for theorem prover tutorial~/ This brief review of the programming language is presented as a sequence of questions and answers meant to test your knowledge of the ACL2 programming language. If you want a gentle introduction to the programming language, see ~url[http://www.cs.utexas.edu/users/moore/publications/gentle-intro-to-acl2-programming.html]. Before we get started with the programming drill, let us remind you that all we're interested in here is the language, not the ``program development environment.'' It's impossible to program in ACL2 or any other language without a decent environment, one that at the very least includes a way to prepare and edit files of programs. The two most popular program development environments among ACL2 users are ~il[Emacs] ~warn[] and the Eclipse-based ~il[ACL2-Sedan] ~warn[]. The Sedan provides the most support for the new user, including real-time syntax checking and a facility for testing among many other features. But in this drill we're not interested in the program development environment, we're interested in your understanding of the ACL2 language. ~b[Q]: What do you think this command does? ~bv[] (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) ~ev[] ~b[A]: It defines a function named ~c[rev] that takes one argument, treats it like a list, and reverses the order of the elements in that list. To figure this out from the definition, you have to know that ~c[append] concatenates two lists. Logically speaking, the ~c[defun] of ~c[rev] adds the axiom: ~bv[] (rev x) = (if (endp x) nil (append (rev (cdr x)) (list (car x)))), ~ev[] implicitly quantified for all ~c[x]. ~b[Q]: Given the ~c[defun] of ~c[rev] above, what are the ~i[formal parameters]? What is the ~i[body] of the definition? Write down a ~i[call] of ~i[append] that occurs in the body of ~c[rev]. What are the ~i[actuals] of that call? ~b[A]: The ~i[formals] of ~c[rev] is the list of variables after the first ~c[rev] in the ~c[defun], namely ~c[(x)]. We say ~c[x] is the first (and only) ~i[formal] here. The ~i[body] of ~c[rev] is the entire ~c[if]-expression. The only ~i[call] of ~c[append] in the body is ~bv[] (append (rev (cdr x)) (list (car x))) ~ev[] and the ~i[actuals] of that call are, respectively, ~c[(rev (cdr x))] and ~c[(list (car x))]. ~b[Q]: What do you get if you evaluate ~c[(rev '(a b c d))]? ~b[A]: ~c[(D C B A)]. ~b[Q]: How did ~c[rev] change the case of the elements, e.g., lowercase ~c[a] was in the input list but uppercase ~c[A] was in the output? ~b[A]: This is a trick question. ~c[Rev] doesn't change the case of the elements. ACL2 is case-insensitive when dealing with symbols. The symbol ~c[a] is read in as the symbol ~c[A]. Thus, when writing function names, for example, we can write ~c[rev], ~c[Rev], ~c[REV], or even ~c[ReV] and always be referring to the function ~c[REV]. By default, ACL2 prints symbols in uppercase. ~b[Q]: What does ~c[(rev '((a b c) \"Abc\" \"a\" b #\\c))] return? ~b[A]: ~c[(#\\c B \"a\" \"Abc\" (A B C))]. If you thought the answer was any of these, then you need to think or read more carefully: ~bv[] (#\\C B \"A\" \"ABC\" (A B C)) (#\\C B \"A\" \"ABC\" (C B A)) ~ev[] The first wrong answer above is wrong because Lisp is ``case insensitive'' only for symbols, not for character objects like ~c[#\\c] (the lowercase character c) or for strings. Furthermore, ~c[\"A\"] is a string, not a symbol; it is different from ~c[A]. The second wrong answer above is wrong because ~c[rev] does not go into the individual elements of the list, it just reverses the order of the elements. So it doesn't change the element ~c[(A B C)] to ~c[(C B A)]. ~b[Q]: In the question about what ~c[(rev '(a b c d))] returns, we put a quote mark before the ~c[(a b c d)] but not before the answer, ~c[(D C B A)]. Why? ~b[A]: The phrase ``~i[x] evaluates to ~i[y]'' treats ~i[x] as a ~i[term] to be evaluated and ~i[y] as an ~i[object]. ~c[(Rev '(a b c d))] is a term to be evaluated and denotes a call of the function ~c[rev] on the value of the argument term ~c['(a b c d)]. The value of that argument term is the object ~c[(a b c d)]. The value of the call of ~c[rev] is the object ~c[(d c b a)]. If you have an object, ~i[obj], and you wish to create a term whose value is ~i[obj], then you put a quote mark in front of it, ~i['obj]. ~b[Q]: Can ~c[rev] be applied to something other than a list? ~b[A]: Yes, every ACL2 function can be applied to any object. ACL2 is an untyped programming language: every variable ranges over the entire universe of objects. In normal usage, ~c[rev] is applied to lists but there is nothing about the syntax of the language that prevents it being applied to non-lists. ~b[Q]: So what does ~c[(rev 23)] evaluate to? ~b[A]: ~c[Nil]. ~b[Q]: Why? ~b[A]: Because ~c[(endp 23)] is ~c[t], because ~c[endp] is defined: ~bv[] (defun endp (x) (not (consp x))) ~ev[] Thus, if ~c[rev] is applied to anything that is not a cons, it returns ~c[nil]. ~b[Q]: So what does ~c[(rev '(a b c . d))] evaluate to? ~b[A]: ~c[(c b a)]. To explain why requires demonstrating that you know what ~c[(a b c . d)] means. It is the object computed by evaluating: ~bv[] (cons 'a (cons 'b (cons 'c 'd))). ~ev[] That is, it is a list whose ``terminal marker'' is the atom ~c[D]. ~c[Rev] treats that list exactly as it treats the ~c[nil]-terminated list of the same elements, ~c[(a b c)], because ~c[(endp 'D)] = ~c[(endp nil)] = ~c[t]. ~b[Q]: What does ~c[(rev 1 2 3)] evaluate to? ~b[A]: That's a trick question. ~c[Rev] takes one argument, not three. So ~c[(rev 1 2 3)] is an ill-formed term. ~b[Q]: What does ~c[(rev '(a b c . d . nil))] evaluate to? ~b[A]: That is a trick question. There is no such object. In Lisp's ``dot notation'' every dot must be followed by a well-formed object and then a close parenthesis. Usually that ``well-formed object'' is an atom. If it is not an atom, i.e., if it is a cons, then the entire expression could have been written without that dot. For example, ~c[(a b c . (d e))] is an object, but it could be written ~c[(a b c d e)]. ~b[Q]: Do ~c[(rev (rev x))] and ~c[x] always evaluate to the same object? ~b[A]: No. ~c[(Rev (rev 23))] evaluates to ~c[nil], not ~c[23]. ~b[Q]: Do ~c[(rev (rev x))] and ~c[x] always evaluate to the same object when ~c[x] is a cons? ~b[A]: No. ~c[(rev (rev '(a b c . d)))] evaluates to ~c[(a b c)], not ~c[(a b c . d)]. ~b[Q]: When are ~c[(rev (rev x))] and ~c[x] equal? ~b[A]: When the terminal marker of ~c[x] is ~c[nil]. ~b[Q]: Can you define a Lisp function that recognizes ~c[nil]-terminated lists? ~b[A]: Yes, but it is not necessary for the user to define that concept because Common Lisp provides such a function which is logically defined as follows: ~bv[] (defun true-listp (x) (if (consp x) (true-listp (cdr x)) (equal x nil))). ~ev[] This can be paraphrased: ~c[(true-listp x)] means that if ~c[x] is a ~c[cons], its ~c[cdr] is a ~c[true-listp] and if ~c[x] is not a ~c[cons], it must be ~c[nil]. Thus, ~c[(true-listp '(a b c))] is ~c[t] and ~c[(true-listp '(a b c . d))] is ~c[nil]. ~b[Q]: Can you write a Lisp formula that says ``If ~c[z] is a ~c[nil]-terminated list then reversing the result of reversing ~c[z] is ~c[z]''? ~b[A]: Yes: ~bv[] (implies (true-listp z) (equal (rev (rev z)) z)). ~ev[] ~b[Q]: Is this all there is to ACL2 programming? ~b[A]: No! ACL2 provides many other features. For a full list of all the primitive functions in ACL2 ~pl[programming] ~warn[]. Some highlights for the beginner are mentioned below, but all of the links below ought to be tagged with the ~warn[] sign. * ~ilc[list]: build a ~c[nil]-terminated list from the values of ~i[n] terms, e.g., ~c[(list x (+ 1 x) (+ 2 x))] returns ~c[(3 4 5)] if ~c[x] is ~c[3]. * ~il[list*]: build a non-~c[nil] terminated list of ~i[n] objects from the values of ~i[n+1] terms, e.g., ~c[(list* x (+ 1 x) (+ 2 x) (* -1 x))] returns the list ~c[(3 4 5 . -3)] if ~c[x] is ~c[3]. * ~ilc[and], ~ilc[or], ~ilc[not], ~ilc[implies], ~ilc[iff]: The propositional connectives. ~c[And] and ~c[or] are allowed to take a varying number of arguments, e.g., ~c[(and p q r)] is just an abbreviation for ~c[(and p (and q r))]. In Lisp, ~c[and] returns ~c[nil] if any of its arguments evaluates to ~c[nil]; otherwise it returns the value of the last argument! Thus, ~c[(and t t 3)] returns ~c[3]! If you object to the idea that ~c[and] is not Boolean, don't give it non-Boolean arguments! Similarly, ~c[or] returns the value of the first argument that evaluates to non-~c[nil], or ~c[nil] if they all evaluate to ~c[nil]. Both ~c[and] and ~c[or] can be thought of as ``lazy'' in that they don't always have to evaluate all their arguments. This is really accomplished by treating ~c[and] and ~c[or] as abbrevations for ~c[if] nests. * ~ilc[+], ~ilc[*], ~ilc[-], ~ilc[/], ~ilc[floor], ~ilc[mod], ~ilc[<], ~ilc[<=], ~ilc[>=], ~ilc[>]: the Lisp elementary arithmetic operators. Both ~c[+] and ~c[*] allow varying numbers of arguments. All the arithmetic operators default non-numeric arguments to ~c[0]. If you don't like the idea that ~c[(+ 1 2 t)] is ~c[3], don't ask ~c[+] to add ~c[t] to something! * ~ilc[natp], ~ilc[integerp], ~ilc[rationalp], ~ilc[characterp], ~ilc[stringp], ~ilc[symbolp], ~ilc[consp]: the recognizers for the primitive data types. The first three recognize subsets of the ACL2 numeric universe. The naturals are a subset of the integers, the integers are a subset of the rationals, and the rationals are a subset of the objects recognized by ~ilc[acl2-numberp], which also includes the ~ilc[complex-rationalp]s. The other recognizers listed above recognize characters, strings, symbols, and conses. * ~ilc[cond]: a convenient way to write a cascading nest of ~c[if]s, e.g., ~bv[] (cond ((not (natp x)) 'non-natural) ((equal x 0) 'zero) ((evenp x) 'positive-even) (t 'positive-odd)) ~ev[] abbreviates ~bv[] (if (not (natp x)) 'non-natural (if (equal x 0) 'zero (if (evenp x) 'positive-even 'positive-odd))). ~ev[] * ~ilc[case]: a convenient way to case split on the identity of an object. ~bv[] (case key (non-natural -1) (zero 0) ((positive-even positive-odd) 'positive-natural) (otherwise 'unknown)) ~ev[] abbreviates ~bv[] (cond ((eql key 'non-natural) -1) ((eql key 'zero) 0) ((member key '(positive-even positive-odd)) 'positive-natural) (t 'unknown)). ~ev[] * user defined macros: using ~ilc[defmacro] ~warn[] you can introduce your own abbreviations. We recommend you not do this until you're good at list processing since macros are functions that build objects representing terms. * ~ilc[mutual-recursion]: allows you to define mutually-recursive functions. * ~ilc[mv] and ~ilc[mv-let]: allow functions to return ``multiple-values''. In Lisp, such functions return vectors of values, the vectors are represented as lists of values, but the implementations are generally more efficient. For example, ~c[(mv x y z)] returns a ``vector'' consisting of the values of ~c[x], ~c[y], and ~c[z]. ~bv[] (mv-let (a b c) (foo x) (bar a b c x)) ~ev[] evaluates ~c[(foo x)], treats the result as a vector of three values, binds the variables ~c[a], ~c[b], and ~c[c] to those three values, and evaluates and returns ~c[(bar a b c x)]. ACL2 also provides many other features, such as single-threaded objects which may be ``destructively modified'' (~pl[stobj] ~warn[], including a very special single-threaded object that records the ~ilc[state] ~warn[] of the ACL2 system), file input and output (~pl[io] ~warn[]), applicative arrays (~pl[arrays] ~warn[]) and property lists (~pl[getprop] ~warn[]) and other facilities necessary for it to be a practical programming language. However, we ~b[strongly] recommend that as a new user you stay away from these features until you are good at proving theorems about elementary list processing! If this little drill made sense to you, you know enough of the programming language to get started. Use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover]. If you are uncomfortable with ACL2 programming, we recommend that you study ~url[http://www.cs.utexas.edu/users/moore/publications/gentle-intro-to-acl2-programming.html] and ~url[http://www.cs.utexas.edu/users/moore/publications/acl2-programming-exercises1.html]. However, we strongly recommend that you first invest in learning either the ~il[Emacs] or Eclipse-based ~il[ACL2-Sedan] program development environments, since it is foolish to try to learn how to program in a stand-alone read-eval-print loop! While getting started, many users find the Hyper-Card a handy index into the documentation for the ACL2 language: ~url[http://www.cs.utexas.edu/users/moore/publications/hyper-card.html] Once you are comfortable with the ACL2 programming language, use your browser's ~b[Back Button] to return to ~il[introduction-to-the-theorem-prover]. ~/~/") (deflabel example-induction-scheme-nat-recursion :doc ":Doc-Section introduction-to-the-theorem-prover induction on natural numbers~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Classical Induction on Natural Numbers]: Induction is familiar in the arithmetic setting. To prove ~c[(p n)], for all ~c[n], by classical induction on the construction of the natural numbers, prove each of the following: ~bv[] ~i[Base Case]: (implies (zp n) (p n)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (zp n)) (p (- n 1))) (p n)) ~ev[] The Base Case establishes that ~c[p] holds for ~c[0]. In fact, because of the definition of ~ilc[zp] ~warn[], it establishes that ~c[(p n)] holds when ~c[n] is ~c[0] and it holds when ~c[n] is not a natural number. The Induction Step establishes that if ~c[n] is a natural number other than ~c[0], and if ~c[p] holds for ~c[n]-1, then ~c[p] holds for ~c[n]. The hypothesis ~c[(p (- n 1))] above is called the ~i[induction hypothesis]. A function that suggests this induction is ~bv[] (defun nat-recursion (n) (if (zp n) n (nat-recursion (- n 1)))) ~ev[] Similarly, the term ~c[(fact n)] suggests this induction if ~c[fact] is defined: ~bv[] (defun fact (k) (if (zp k) 1 (* k (fact (- k 1))))). ~ev[] even though the formal parameter of this definition of ~c[fact] is ~c[k], not ~c[n]. ~/~/") (deflabel example-induction-scheme-down-by-2 :doc ":Doc-Section introduction-to-the-theorem-prover induction downwards 2 steps at a time~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Classical Induction on Natural Numbers Preserving Parity]: Here is another way to decompose natural numbers. To prove ~c[(p n)], for all ~c[n], prove each of the following: ~bv[] ~i[Base Case 1]: (implies (zp n) (p n)) ~ev[] ~bv[] ~i[Base Case 2]: (implies (equal n 1) (p n)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (zp n)) (not (equal n 1)) (p (- n 2))) (p n)) ~ev[] Base Case 1 establishes that ~c[p] holds for ~c[0] (and all objects other than positive naturals). Base Case 2 establishes that ~c[p] holds for ~c[1]. The Induction Step establishes that if ~c[n] is a natural number greater than ~c[1], and if ~c[p] holds for ~c[n]-2, then ~c[p] holds for ~c[n]. Note that we have thus proved that ~c[(p n)] holds, for all ~c[n]. For example, ~c[(p -7)], ~c[(p 'abc)], and ~c[(p 0)] are all established by Base Case 1. ~c[(p 1)] is established by Base Case 2. ~c[(p 2)] is established from ~c[(p 0)] and the Induction Step. Think about it! ~c[(p 3)] is established form ~c[(p 1)] and the Induction Step, etc. A function that suggests this induction is: ~bv[] (defun parity (n) (if (zp n) 'even (if (equal n 1) 'odd (parity (- n 2))))). ~ev[] ~/~/") (deflabel example-induction-scheme-on-lists :doc ":Doc-Section introduction-to-the-theorem-prover induction on lists~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Classical Induction on Lists]: To prove ~c[(p x)], for all ~c[x], by classical induction on the linear list structure, prove each of the following: ~bv[] ~i[Base Case]: (implies (endp x) (p x)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (endp x)) (p (cdr x))) (p x)) ~ev[] An argument analogous to that given for natural numbers, ~il[example-induction-scheme-nat-recursion], establishes ~c[(p x)] for every ~c[x]. For example, ~c[(p -7)], ~c[(p 'abc)], and ~c[(p nil)] are all established by the Base Case. ~c[(p '(Friday))] follows from ~c[(p nil)], given the Induction Step. That sentence bears thinking about! Think about it! Similarly, ~c[(p '(Yellow))] holds for the same reason. ~c[(p '(Thursday Friday))] follows from ~c[(p '(Friday))] and the Induction Step, etc. A function that suggests this induction is ~bv[] (defun app (x y) (if (endp x) y (cons (car x) (app (cdr x) y)))). ~ev[] ~/~/") (deflabel example-induction-scheme-binary-trees :doc ":Doc-Section introduction-to-the-theorem-prover induction on binary trees~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Classical Induction on Binary Trees]: To prove ~c[(p x)], for all ~c[x], by classical induction on binary tree structures, prove each of the following: ~bv[] ~i[Base Case]: (implies (atom x) (p x)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (atom x)) (p (car x)) (p (cdr x))) (p x)) ~ev[] An argument analogous to that given in ~il[example-induction-scheme-on-lists] should convince you that ~c[(p x)] holds for every object. A function that suggests this induction is: ~bv[] (defun flatten (x) (if (atom x) (list x) (app (flatten (car x)) (flatten (cdr x))))). ~ev[] ~/~/") (deflabel example-induction-scheme-on-several-variables :doc ":Doc-Section introduction-to-the-theorem-prover induction on several variables~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Induction on Several Variables] To ~c[(p n x)] for all ~c[n] and all ~c[x], prove each of the following: ~bv[] ~i[Base Case 1]: (implies (endp x) (p n x)) ~ev[] ~bv[] ~i[Base Case 2]: (implies (and (not (endp x)) (zp n)) (p n x)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (endp x)) (not (zp n)) (p (- n 1) (cdr x))) (p n x)) ~ev[] A function that suggests this induction is ~bv[] (defun nth (n x) (if (endp x) nil (if (zp n) (car x) (nth (- n 1) (cdr x))))). ~ev[] ~/~/") (deflabel example-induction-scheme-upwards :doc ":Doc-Section introduction-to-the-theorem-prover induction upwards~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Induction Upwards]: To ~c[(p i max)] for all ~c[i] and all ~c[max], prove each of the following: ~bv[] ~i[Base Case]: (implies (not (and (natp i) (natp max) (< i max))) (p i max)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (natp i) (natp max) (< i max) (p (+ i 1) max)) (p i max)) ~ev[] Note that the induction hypothesis is about an ~c[i] that is ~i[bigger] than the ~c[i] in in the conclusion. In induction, as in recursion, the sense of one thing being ``smaller'' than another is determined by an arbitrary measure of all the variables, not the magnitude or extent of some particular variable. A function that suggests this induction is shown below. ACL2 has to be told the measure, namely the difference between ~c[max] and ~c[i] (coerced to a natural number to insure that the measure is an ordinal). ~bv[] (defun count-up (i max) (declare (xargs :measure (nfix (- max i)))) (if (and (natp i) (natp max) (< i max)) (cons i (count-up (+ 1 i) max)) nil)). ~ev[] ~/~/") (deflabel example-induction-scheme-with-accumulators :doc ":Doc-Section introduction-to-the-theorem-prover induction scheme with accumulators~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. To prove ~c[(p x a)] for all ~c[x] and all ~c[a], prove each of the following: ~bv[] ~i[Base Case]: (implies (endp x) (p x a)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (endp x)) (p (cdr x) (cons (car x) a))) (p x a)) ~ev[] Note that in the induction hypothesis we assume ~c[p] for a smaller ~c[x] but a larger ~c[a]. In fact, we could include as many induction hypotheses as we want and use any terms we want in the ~c[a] position as long as the ~c[x] position is occupied by a smaller term. A function that suggests this particular induction is shown below. ~bv[] (defun rev1 (x a) (if (endp x) a (rev1 (cdr x) (cons (car x) a)))). ~ev[] A function that suggests a similar induction in which three induction hypotheses are provided, one in which the ~c[a] position is occupied by ~c[(cons (car x) a)], another in which the ~c[a] position is occupied by some arbitrary term ~c[b], and a third in which the ~c[a] position is occupied by ~c[a], is suggested by the term ~c[(rev1-modified x a b)] where ~bv[] (defun rev1-modified (x a b) (if (endp x) (list x a b) (list (rev1-modified (cdr x) (cons (car x) a) b) (rev1-modified (cdr x) b b) (rev1-modified (cdr x) a b)))) ~ev[] Remember that the value of this term or function is irrelevant to the induction suggested. Because ACL2's definitional principle insists that all the formal parameters play a role in the computation (at least syntactically), it is common practice when defining functions for their induction schemes to return the ~c[list] of all the formals (to insure all variables are involved) and to combine recursive calls on a given branch with ~c[list] (to avoid introducing additional case analysis as would happen if ~c[and] or ~c[or] or other propositional functions are used). If you tried to prove ~c[(p x a)] and suggested the induct hint ~c[(rev1-modified x a (fact k))], as by ~bv[] (thm (p x a) :hints ((\"Goal\" :induct (rev1-modified x a (fact k))))) ~ev[] the inductive argument would be: ~bv[] ~i[Base Case]: (implies (endp x) (p x a)) ~ev[] ~bv[] ~i[Inductive Step]: (implies (and (not (endp x)) (p (cdr x) (cons (car x) a)) (p (cdr x) (fact k)) (p (cdr x) a)) (p x a)) ~ev[] ~/~/") (deflabel example-induction-scheme-with-multiple-induction-steps :doc ":Doc-Section introduction-to-the-theorem-prover induction scheme with more than one induction step~/ ~l[logic-knowledge-taken-for-granted-inductive-proof] for an explanation of what we mean by the induction ~i[suggested] by a recursive function or a term. ~b[Several Induction Steps]: To ~c[(p x i a)] for all ~c[x], ~c[i], and ~c[a], prove each of the following: ~bv[] ~i[Base Case 1]: (implies (zp i) (p x i a)) ~ev[] ~bv[] ~i[Induction Step 1]: (implies (and (not (zp i)) (equal (parity i) 'even) (p (* x x) (floor i 2) a)) (p x i a)) ~ev[] ~bv[] ~i[Induction Step 2]: (implies (and (not (zp i)) (not (equal (parity i) 'even)) (p x (- i 1) (* x a))) (p x i a)) ~ev[] A function that suggests this induction is the binary exponentiation function for natural numbers. ~bv[] (defun bexpt (x i a) (cond ((zp i) a) ((equal (parity i) 'even) (bexpt (* x x) (floor i 2) a)) (t (bexpt x (- i 1) (* x a) )))). ~ev[] In order to admit this function it is necessary to know that ~c[(floor i 2)] is smaller than ~c[i] in the case above. This can be proved if the community book ~c[\"arithmetic-5/top\"] has been included from the ACL2 system directory, i.e., ~bv[] (include-book \"arithmetic-5/top\" :dir :system) ~ev[] should be executed before defining ~c[bexpt]. ~/~/") (deflabel example-inductions :doc ":Doc-Section introduction-to-the-theorem-prover some examples of induction schemes in ACL2~/ Here are some pages illustrating various induction schemes suggested by recursive functions. ~b[Classical Induction on Natural Numbers]: ~pl[example-induction-scheme-nat-recursion]. ~b[Induction Preserving Even/Odd Parity] or~nl[] ~b[Induction Downwards by 2] or ~nl[] ~b[Induction with Multiple Base Cases]: ~pl[example-induction-scheme-down-by-2] for an induction in which the induction hypothesis decreases the induction variable by an amount other than 1. This illustrates that the induction hypothesis can be about whatever term or terms are needed to explain how the formula recurs. The example also illustrates an induction with more than one Base Case. ~b[Classical Induction on Lists]: ~pl[example-induction-scheme-on-lists] for an induction over linear lists, in which we inductively assume the conjecture for ~c[(cdr x)] and prove it for ~c[x]. It doesn't matter whether the list is ~c[nil]-terminated or not; the Base Case addresses all the possibilities. ~b[Classical Induction on Binary (Cons) Trees]: ~pl[example-induction-scheme-binary-trees] for an induction over the simplest form of binary tree. Here the Induction Step provides two hypotheses, one about the left subtree and one about the right subtree. ~b[Induction on Several Variables]: ~pl[example-induction-scheme-on-several-variables] for an induction in which several variables participate in the case analysis and induction hypotheses. ~b[Induction Upwards]: ~pl[example-induction-scheme-upwards] for an induction scheme in which the induction hypothesis is about something ``bigger than'' the induction conclusion. This illustrates that the sense in which the hypothesis is about something ``smaller'' than the conclusion is determined by a measure of all the variables, not the magnitude or extent of some single variable. ~b[Induction with Auxiliary Variables] or~nl[] ~b[Induction with Accumulators]: ~pl[example-induction-scheme-with-accumulators] for an induction scheme in which one variable ``gets smaller'' but another is completely arbitrary. Such schemes are common when dealing with tail-recursive functions that accumulate partial results in auxiliary variables. This example also shows how to provide several arbitrary terms in a non-inductive variable of a scheme. ~b[Induction with Multiple Induction Steps]: ~pl[example-induction-scheme-with-multiple-induction-steps] for an induction in which we make different inductive hypotheses depending on which case we're in. This example also illustrates the handling of auxiliary variables or accumulators. ~/~/") (deflabel logic-knowledge-taken-for-granted-inductive-proof :doc ":Doc-Section introduction-to-the-theorem-prover a brief explanation of induction~/ We start by showing classical induction on the natural numbers in an ACL2 setting before turning to a more general treatment of induction. ~b[Classical Induction on Natural Numbers]: Induction is familiar in the arithmetic setting. Let ~c[(p n)] denote some formula involving the variable ~c[n] (and perhaps some other variables which we don't exhibit). Then to prove ~c[(p n)], for all ~c[n], by classical induction on the construction of the natural numbers, prove each of the following: ~bv[] ~i[Base Case]: (implies (zp n) (p n)) ~ev[] ~bv[] ~i[Induction Step]: (implies (and (not (zp n)) (p (- n 1))) (p n)) ~ev[] The Base Case establishes that ~c[p] holds for ~c[0]. In fact, because of the definition of ~ilc[zp] ~warn[], it establishes that ~c[(p n)] holds when ~c[n] is ~c[0] and it holds when ~c[n] is not a natural number. The Induction Step establishes that if ~c[n] is a natural number other than ~c[0], and if ~c[p] holds for ~c[n]-1, then ~c[p] holds for ~c[n]. The hypothesis ~c[(p (- n 1))] above is called the ~i[induction hypothesis]. Note that if the Base Case and Induction Step are valid, then we know ~c[(p n)], for all ~c[n]. You can convince yourself of this by picking any object and asking ``how do I know ~c[p] holds for this object?'' For example, ~c[(p -7)], ~c[(p 'abc)], and ~c[(p 0)] are all established by the Base Case. What about ~c[(p 1)]? That follows from ~c[(p 0)], given the Induction Step. Why? To prove ~c[(p 1)] using the Induction Step, you have to establish ~c[(not (zp 1))], which is true, and ~c[(p (- 1 1))], which is ~c[(p 0)], which is true by the Base Case. So ~c[(p 1)] is true. Similar reasoning proves ~c[(p 2)] from from ~c[(p 1)], etc. Clearly, for every natural number other than ~c[0] we could reason like this to show that ~c[p] holds. Since the Base Case handled all the objects that are not natural numbers, and handled ~c[0], we know ~c[(p n)], for all ~c[n]. There is a duality between recursion and induction that ACL2 exploits. The fact that the Base and Induction steps above are sufficient to prove ~c[p] for all objects is related to the fact that the following recursion defines a total, terminating function: ~bv[] (defun nat-recursion (n) (if (zp n) n (nat-recursion (- n 1)))) ~ev[] When this function is admitted we have to prove that if ~c[(zp n)] does not hold, then ~c[(- n 1)] is smaller, in some sense, than ~c[n]. This sense of ``smaller'' is determined by some ~i[measure] of the arguments. That measure must return an ordinal (~il[ordinals] ~warn[]), but the most common measures return natural numbers, which are among the ordinals. Furthermore, that measure should insure that the terms in the recursive calls are smaller than the formals, i.e., the measure of ~c[(- n 1)] must be smaller than the measure of ~c[n], when the recursive branches are taken. This sense of ``smaller'' must be ~i[well-founded]: it must be impossible to have an infinitely descending chain of smaller things. This is true of the less-than relation on the ordinals (see ~ilc[o<] ~warn[]). Well-foundedness means that eventually any recursion must ``bottom out'' because things can't keep getting smaller forever. The recursion in ~c[nat-recursion] ~b[suggests] the induction shown above: the Base Case is defined by the ~c[if] branch that does not lead to a recursive call. The Induction Step is defined by the other branch. The induction hypothesis is defined by what we recur on, i.e., ~c[(- n 1)]. The theorems proved when ~c[nat-recursion] is introduced ~i[justify] the classical induction scheme noted above. Every recursively defined ACL2 function suggests a legal induction and vice versa. Furthermore, every call of a recursively defined function on distinct variable symbols also suggests a legal induction: just take the induction suggested by the function's recursive definition after renaming the formal parameters to be the variables in the call. For example, it should be clear that ~c[(nat-recursion a)] suggests a Base Case defined by ~c[(zp a)], and induction step defined by ~c[(not (zp a))] and an induction hypothesis about ~c[(- a 1)]. Note that the term ~c[(fact n)] suggests the same classical induction on natural numbers shown above, where ~c[fact] is defined as follows (even though we've used the formal parameter ~c[k] below). ~bv[] (defun fact (k) (if (zp k) 1 (* k (fact (- k 1))))) ~ev[] The induction suggested by a term like ~c[(fact n)] is insensitive to the name of the formal parameter used in the ~c[defun]. The induction suggested by a function or term is insensitive to the value returned by the function or term. It doesn't matter what the function returns in its ``base case'' (e.g., ~c[1] in ~c[fact]) or what the function ``does'' to its recursive call (e.g., multiply by ~c[k] in the ~c[defun] of ~c[fact]). All that matters is (i) how the ~c[if] structure breaks down the cases on ~c[k], (ii) which branches lead to recursion, and (iii) what arguments are passed to the recursive calls. Those things determine (i) the case analysis of the induction scheme, (ii) which cases are Base Cases and which are Induction Steps, and (iii) what the induction hypotheses are. For a selection of common inductions schemes in ACL2 (e.g., on the structure of natural numbers, lists, and trees and on several variables at once, multiple base cases, multiple induction hypotheses, multiple induction steps, etc.) ~il[example-inductions check this link]. Every legal ACL2 induction corresponds to an admissible recursive function and vice versa. Similarly, every legal ACL2 induction corresponds to a call of a recursively defined function on distinct variables. ACL2 chooses which induction to do by looking at the terms that occur in the conjecture. For many elementary theorems, ACL2 chooses the right induction by itself. You may occasionally need to tell it what induction to do. You do that by showing it a term that suggests the induction you want. We'll explain how you communicate this to ACL2 later. If you understand how recursive functions suggest inductions, then you know what you need to know to use ACL2. The main point of this discussion of induction is familiarize you with the basic terms: ~i[Base Case] (of which there may be several), ~i[Induction Step] (of which there may be several), ~i[Induction Hypothesis] (of which there may be several in each Induction Step), ~i[measure] and ~i[well-founded relation] ~i[justifying] an induction, and the induction ~i[suggested] by a term or recursive function definition. Furthermore, every Induction Hypothesis is always an ~il[logic-knowledge-taken-for-granted-instance instance] of the conjecture being proved: each induction hypothesis is obtained from the conjecture being proved by applying a ~i[substitution] replacing variables by terms. If you are reviewing the material taken for granted about logic while working your way through the introduction to the theorem prover, please use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted]. ~/~/") (deflabel logic-knowledge-taken-for-granted-base-case :doc ":Doc-Section introduction-to-the-theorem-prover a brief explanation of base cases~/ According to the sentence, the conjecture being proved is ``reversing the reverse of a ~c[true-listp] yields the original list.'' The formula corresponding to this conjecture is: ~bv[] (implies (true-listp z) (equal (rev (rev z)) z)). ~ev[] We're also told that this is an inductive proof. Evidently we're doing an induction on the structure of the list ~c[z]. Then the ~i[Base Case] is the formula: ~bv[] (implies (endp z) (implies (true-listp z) (equal (rev (rev z)) z))). ~ev[] Now use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted]. ~/~/") (deflabel logic-knowledge-taken-for-granted-q1-answer :doc ":Doc-Section introduction-to-the-theorem-prover the inductive step of the ~c[rev-rev] proof -- Answer to Question 1~/ The correct answer to Question 1 in ~il[logic-knowledge-taken-for-granted] is ~i[Choice (iv)]. The Induction Step of the inductive proof of ~bv[] (implies (true-listp z) (equal (rev (rev z)) z)) ~ev[] for an induction on the linear list ~c[z] is: ~bv[] ~i[Induction Step]: (implies (and (not (endp z)) (implies (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z)))) (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] The second hypothesis above is the the ~i[induction hypothesis]. The conclusion above is the formula we are trying to prove. Each induction hypothesis is ~i[always] an ~il[logic-knowledge-taken-for-granted-instance instance] of the formula being proved, i.e., it is obtained from the formula being proved by uniformly replacing the variables in the formula with terms. Notice how the induction hypothesis above is the same as the induction conclusion, except that all the ~c[z]s have been replaced by ~c[(cdr z)]. If you thought the right answer was ~bv[] ~i[Induction Step -- Choice (i)]: (implies (not (endp z)) (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] then perhaps you didn't understand that we're doing an inductive proof. Certainly if you prove the Base Case already discussed and you prove ~i[Choice (i)] above, then you will have proved the goal conjecture, but you would have done it by simple case analysis: prove it when ~c[(endp z)] and prove it when ~c[(not (endp z))]. While logically valid, you probably can't prove ~i[Choice (i)] directly because you have no induction hypothesis to work with. If you thought the right answer was: ~bv[] ~i[Induction Step -- Choice (ii)]: (implies (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z))) ~ev[] then perhaps you misunderstand the difference between the ~i[Induction Step] and the ~i[Induction Hypothesis]. The Induction ~i[Step] is the ``other half'' of the main proof, balancing the Base Case. The Induction ~i[Hypothesis] is just a hypothesis you get to use during the Induction Step. The question Q1 asked what is the Induction Step. If you thought the right answer was: ~bv[] ~i[Induction Step -- Choice (iii)]: (implies (and (not (endp z)) (equal (rev (rev (cdr x))) (cdr x))) ; ~i[``induction hyp''] (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] then you are making the most common mistake newcomers make to induction. You are giving yourself an ``induction hypothesis'' that is not an instance of the conjecture you're proving. This alleged induction hypothesis says that ~c[(rev (rev (cdr x)))] is ~c[(cdr x)], whereas the correct induction hypothesis says those two terms are equal ~i[if] ~c[(true-listp (cdr x))]. This alleged induction hypothesis is a stronger claim than we're trying to prove. It turns out that by making this mistake you can ``prove'' conjectures that are not always true! Remember: the induction hypothesis is always an instance of the conjecture you're proving, not just some piece of it. Of course, ACL2 ``knows'' this and will never make this mistake. But we remind you of it because there may be times when you intuit a different hypothesis and don't understand why ACL2 doesn't use it. If this doesn't make sense, perhaps you should read about ~il[logic-knowledge-taken-for-granted-inductive-proof induction] again. When you understand why ~i[Choice (iv)] is the correct answer, use your browser's ~b[Back Button] to return to ~il[logic-knowledge-taken-for-granted] and go to question Q2. ~/~/") (deflabel logic-knowledge-taken-for-granted-q2-answer :doc ":Doc-Section introduction-to-the-theorem-prover the inductive step of the ~c[rev-rev] proof -- Answer to Question 2~/ The correct answer to Question 2 in ~il[logic-knowledge-taken-for-granted] is ~i[Subgoal (i)] plus any one of the other other three. For your reference, the four choices were: ~bv[] ~i[Subgoal (i)]: (implies (and (not (endp z)) (true-listp z)) (true-listp (cdr z))) ~i[Subgoal (ii)]: (implies (and (not (endp z)) (true-listp z) (equal (rev (rev (cdr z))) (cdr z))) (equal (rev (rev z)) z)) ~i[Subgoal (iii)]: (implies (and (not (endp z)) (equal (rev (rev (cdr z))) (cdr z))) (equal (rev (rev z)) z)) ~i[Subgoal (iv)]: (implies (and (not (endp z)) (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z))) (equal (rev (rev z)) z)) ~ev[] In particular, it is wrong to think the Induction Step of the proof of ~bv[] (implies (true-listp z) (equal (rev (rev z)) z)) ~ev[] can be established by proving just ~i[Subgoal (ii)], ~i[Subgoal (iii)], ~i[Subgoal (iv)], or combinations of those three. You must also prove ~i[Subgoal (i)] or something like it! The Inductive Step for the conjecture above is ~bv[] ~i[Induction Step]: (implies (and (not (endp z)) ; ~i[Induction Hypothesis]: (implies (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z)))) ; ~i[Induction Conclusion]: (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] Note that the Inductive Hypothesis is an implication: ~bv[] (implies (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z))) ~ev[] This hypothesis can be true two different ways. The ``normal'' way -- the way everybody remembers -- is that ~c[(true-listp (cdr z))] is true and thus ~c[(equal (rev (rev (cdr z))) (cdr z))] is true. But the way many people forget is that ~c[(true-listp (cdr z))] is false. You must prove the Induction Step even in this ``forgetable'' case. In this case, the Induction Step simplifies to ~bv[] ~i[Induction Step]: (implies (and (not (endp z)) (not (true-listp (cdr z)))) (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] By Promotion (see the list of tautologies in our discussion of ~il[logic-knowledge-taken-for-granted-propositional-calculus propositional calculus]) this is ~bv[] ~i[Induction Step']: (implies (and (not (endp z)) (not (true-listp (cdr z))) (true-listp z)) (equal (rev (rev z)) z)) ~ev[] Using the Contrapositive and rearranging the order of the hypotheses (see ~il[logic-knowledge-taken-for-granted-propositional-calculus propositional calculus] again), this is ~bv[] ~i[Induction Step'']: (implies (and (not (endp z)) (true-listp z) (not (equal (rev (rev z)) z))) (true-listp (cdr z))) ~ev[] Notice that ~i[Subgoal (i)] implies ~i[Induction Step'']: ~bv[] ~i[Subgoal (i)]: (implies (and (not (endp z)) (truelistp z)) (truelistp (cdr z))) ~ev[] Every inductive proof of an implication raises a case like this. If we denote the conjecture ~c[(implies p q)] as ~c[p ---> q], then the Induction Step will look like this: ~bv[] ( c & (p' ---> q')) ---> (p ---> q) ~ev[] where ~c[c] is the test that determines the inductive step, (e.g., ~c[(not (endp z))]) and ~c[p'] and ~c[q'] are inductive instances of ~c[p] and ~c[q]. Promotion produces ~bv[] ( c & p & (p' ---> q')) ---> q ~ev[] It is then very common to prove that ~c[p] implies ~c[p'], ~bv[] ~i[(i)]: (c & p) ---> p' ~ev[] and then prove that ~c[q'] implies ~c[q], ~bv[] ~i[(ii)]: (c & p & q') ---> q ~ev[] These correspond exactly to our choices ~i[Subgoal (i)] and ~i[Subgoal (ii)]. It is sometimes helpful to remember this diagram: ~bv[] (c & (p' ---> q') ^ | | | | v --> (p ---> q ) ~ev[] When you understand why ~i[Subgoals (i)] and ~i[(ii)] are sufficient, use your browser's ~b[Back Button] to return to ~il[logic-knowledge-taken-for-granted] and go to question Q3. ~/~/") (deflabel logic-knowledge-taken-for-granted-q3-answer :doc ":Doc-Section introduction-to-the-theorem-prover the inductive step of the ~c[rev-rev] proof -- Answer to Question 2~/ The correct answer to Question 3 in ~il[logic-knowledge-taken-for-granted] is that you need to prove ~bv[] ~i[Subgoal to Relieve Hyp 1]: (implies (and (q (f a)) (r a)) (p (f a))) ~ev[] in order to use ~bv[] ~i[Theorem]: (implies (p (f x)) (equal (g (h x)) x)) ~ev[] to rewrite the target ~c[(g (h a))] to ~c[a] in ~bv[] ~i[Goal Conjecture]: (implies (and (q (f a)) (r a)) (s (g (h a)))) ~ev[] If you don't see why, re-read the discussion of ~il[logic-knowledge-taken-for-granted-rewriting rewriting] again. Forgetting about the need to relieve hypotheses is a common mistake in informal proofs. ACL2 won't forget to relieve them. But if you forget about the need to do it, you may be confused when ACL2 doesn't see the ``proof'' you see! Now use your browser's ~b[Back Button] to return to the end of quiz in ~il[logic-knowledge-taken-for-granted]. ~/~/") (deflabel logic-knowledge-taken-for-granted-instance :doc ":Doc-Section introduction-to-the-theorem-prover a brief explanation of substitution instances~/ Let ~i[p] and ~i[q] be terms or formulas (there is no difference in ACL2). Then we say ~i[p] is an ~i[instance] or ~i[substitution instance] of ~i[q] if and only if ~i[p] can be obtained from ~i[q] by uniformly replacing the variables of ~i[q] by terms. Sometimes we call ~i[p] the ~i[target] and ~i[q] the ~i[pattern] because by choosing appropriate replacements we can make the pattern ~i[match] many different targets. For example, the following ~i[target] is an instance of the given ~i[pattern]: ~bv[] ~i[target]: (APP (APP (REV A) (REV B)) (REV C)) ~i[pattern]: (APP (APP x y ) (REV z)) ~ev[] The replacement or ~i[substitution] used in this match of the pattern to the target is: ~bv[] ~i[variable in pattern] ~i[replacement term] x (REV A) y (REV B) z C ~ev[] Such substitutions are usually written this way in ACL2: ~bv[] ((x (REV A)) (y (REV B)) (z C)). ~ev[] Please use your browser's ~b[Back Button] to return to the page that mentioned ``instance.'' ~/~/") (deflabel logic-knowledge-taken-for-granted-propositional-calculus :doc ":Doc-Section introduction-to-the-theorem-prover a brief explanation of propositional calculus~/ It is impossible in this short introduction to teach you propositional calculus if you don't already know it! A typical use of propositional calculus is to observe that ~bv[] (implies (endp z) (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] is equivalent to: ~bv[] (implies (and (endp z) (true-listp z)) (equal (rev (rev z)) z)) ~ev[] If this is surprising and you know propositional calculus, then the problem might be our notation. We're exploiting the tautology ~bv[] ~i[(p ---> (q ---> r)) <---> ((p & q) ---> r)] ~ev[] where ~i[--->] and ~i[<--->] are meant to be the traditional arrows denoting logical implication and logical equivalence. If you don't know propositional calculus, we'll say just a few things to help ease your journey. A ~i[propositional formula], in ACL2, is any formula written entirely in terms of variable symbols, ~c[T], ~c[NIL], and the propositional functions ~c[AND], ~c[OR], ~c[NOT], ~c[IMPLIES], and ~c[IFF]. The ``tautology'' above in traditional notation is this propositional formula in ACL2: ~bv[] (IFF (IMPLIES P (IMPLIES Q R)) (IMPLIES (AND P Q) R)). ~ev[] If you have a formula like ~bv[] (implies ~i[hyp] ~i[concl]) ~ev[] then we say that formula is an ~i[implication], that ~i[hyp] is the ~i[hypothesis], and that ~i[concl] is the conclusion. If the hypothesis is an ~c[and] expression, as in ~bv[] (implies (and ~i[hyp1] ~i[hyp2] ...) ~i[concl]) ~ev[] then we call ~i[hyp1] is the ~i[first hypothesis], ~i[hyp2] is the ~i[second hypothesis], etc. If a term is of the form ~bv[] (and ~i[term1] ~i[term2] ...) ~ev[] we say it is a ~i[conjunction] and that ~i[term1] is the ~i[first conjunct], ~i[term2] is the ~i[second conjunct], etc. We treat an ~c[or]-term analogously but call it a ~i[disjunction] and its arguments are ~i[disjuncts]. A ~i[tautology] is any propositional formula that can be proved by testing it under all combinations of Boolean assignments to its variables. We give an example of such a ~i[truth-table proof] below, but hasten to add that ACL2 does not generally use truth tables to recognize tautologies. It primarily uses ~c[IF]-normalization and BDDs to recognize tautologies, which can be seen as a mix of symbolic manipulation and case analysis. Many tautologies have names, but ACL2 doesn't refer to them by name because it derives them from first principles. We list a few here because we sometimes use the names in our documentation; more importantly, you should look at these formulas and convince yourself that they're always true for all Boolean values of the variables: ~bv[] ~i[Double Negation]: (iff (not (not p)) p) ~i[DeMorgan]: (iff (not (and p q)) (or (not p) (not q))) ~i[Distributivity]: (iff (and p (or q r)) (or (and p q) (and p r))) ~i[Promotion]: (iff (implies p (implies q r)) (implies (and p q) r)) ~i[Implicative Disjunction]: (iff (implies p q) (or (not p) q)) ~i[Contrapositive]: (iff (implies p q) (implies (not q) (not p))) ~i[Generalized Contrapositive]: (iff (implies (and p r) q) (implies (and p (not q)) (not r))) ~ev[] There are, of course, many others, even with these same names! For example, there is a dual version of DeMorgan showing how ~c[not] distributes over ~c[or], a dual version of Distributivity for ~c[or] over ~c[and], etc. Dealing with propositional calculus will not generally be a problem for you because it is decidable and ACL2 has procedures that decide propositional formulas. However, propositional calculus can lead to exponential explosion and can thus explain why ACL2 has ``gone out to lunch.'' In addition, sometimes if you are curious as to ~i[why] ACL2 is working on a certain subgoal the reason can be traced back to propositional calculus. The most common example of this is that to prove a formula of the form ~bv[] (implies (implies p1 q1) (implies p2 q2)) ~ev[] propositional calculus will convert it to ~bv[] (and (implies (and p2 (not p1)) q2) (implies (and p2 q1) q2)) ~ev[] Many users are surprised that the first conjunct above does not have ~c[q1] as a hypothesis. If you ever stare at an ACL2 goal and say to yourself ``A hypothesis is missing!'' the chances are that propositional calculus is ``to blame.'' In particular, if you are trying to prove that ~c[(implies p1 q1)] implies something, you must deal with the case that ~c[(implies p1 q1)] is true because ~c[p1] is false. Think about it. Now we illustrate the truth table method for deciding tautologies, even though that is not what ACL2 generally uses. Consider the formula called Promotion above: ~bv[] (IFF (IMPLIES P (IMPLIES Q R)) (IMPLIES (AND P Q) R)) ~ev[] The formula above is a tautology. It contains three variables, ~c[P], ~c[Q], and ~c[R], and so there are 8 combinations of Boolean assignments to them. If we let ~bv[] ~i[formula1]: (IMPLIES P (IMPLIES Q R)) ~i[formula2]: (IMPLIES (AND P Q) R) ~ev[] then we wish to test the formula ~c[(IFF ]~i[formula1 formula2]~c[)]: ~bv[] P Q R ~i[formula1] ~i[formula2] (IFF ~i[formula1] ~i[formula2]) --------- T T T T T T T T NIL NIL NIL T T NIL T T T T T NIL NIL T T T NIL T T T T T NIL T NIL T T T NIL NIL T T T T NIL NIL NIL T T T ~ev[] So we see that the formula always returns ~c[T] and is thus a tautology. Recall that in the original example at the top of this page we were trying to prove the formula ~bv[] (implies (endp z) (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] This formula is an ~il[logic-knowledge-taken-for-granted-instance instance] of ~bv[] (implies p (implies q r)). ~ev[] The substitution required by the match is ~bv[] ~i[sigma]: ((p (endp z)) (q (true-listp z)) (r (equal (rev (rev z)) z))) ~ev[] Since we know the tautology: ~bv[] (iff (implies p (implies q r)) (implies (and p q) r)). ~ev[] is always true no matter what Boolean values ~c[p], ~c[q], and ~c[r] have, then we know this instance of it (obtained by applying the substitution ~i[sigma] above) is always true: ~bv[] (iff (implies (endp z) ~i[formula1'] (implies (true-listp z) (equal (rev (rev z)) z))) (implies (and (endp z) ~i[formula2'] (true-listp z)) (equal (rev (rev z)) z))). ~ev[] Thus, if we're trying to prove ~i[formula1'] it is permitted to try to to prove ~i[formula2'] instead, because they return the same truthvalue. This sketch of propositional reasoning in ACL2 is a little suspect because we didn't address the possibility that the substitution might replace the propositional variables by non-propositional terms. But the tautology was verified only on Boolean values for those variables. This actually works out because in ACL2 all propositional testing is done against ~c[nil] and any non-~c[nil] value, including ~c[t], is as good as another. However, the tautology allows us to replace one formula by the other only in contexts in which we just care about propositional truth, i.e., whether the formula is ~c[nil] or not. When we prove a formula in ACL2 we are really establishing that it never returns ~c[nil], i.e., no matter what the values of the variables, the value of the formula is non-~c[nil]. A very simple example of this is with Double Negation. ~bv[] (iff (not (not p)) p) ~ev[] is a tautology. This means that if we were trying to prove ~bv[] (implies (not (not p)) ...) ~ev[] we could transform it to ~bv[] (implies p ...). ~ev[] But if we were trying to prove: ~bv[] (equal (not (not p)) p) ~ev[] we could not prove it by using Double Negation! The formula above claims that ~c[(not (not p))] and ~c[p] have identical values. They do not! For example, ~c[(not (not 3))] is ~c[t], not ~c[3]. However, ~c[(not (not 3))] and ~c[t] are propositionally equivalent (i.e., satisfy ~c[iff]) because one is as good as the other in a test. ~i[That] is what Double Negation says. As long as you only use propositional formulas in propositional places this aspect of ACL2 should not affect you. Now please use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted]. ~/~/") (deflabel logic-knowledge-taken-for-granted-rewriting :doc ":Doc-Section introduction-to-the-theorem-prover a brief explanation of rewriting from the logical perspective~/ First we give two examples of rewriting. Then we give a rather detailed description. We recommend you read the description, even if you understand the two examples, just so that you learn our terminology. ~b[Example 1]: Suppose your goal conjecture is: ~bv[] ~i[Goal Conjecture]: (implies (and (endp z) (true-listp z)) (equal (rev (rev z)) z)) ~ev[] Then you can use the following theorem (actually the definitional axiom introduced by the ~c[defun] of ~c[endp]): ~bv[] ~i[Definitional Axiom]: endp (equal (endp x) (not (consp x))). ~ev[] to ~i[rewrite] the ~i[Goal Conjecture] to ~bv[] ~i[Rewritten Goal Conjecture]: (implies (and (not (consp z)) (true-listp z)) (equal (rev (rev z)) z)) ~ev[] Note that in this example, rewriting replaced the call of ~c[endp] by its body after instantiating its body with the actuals from the call. This is sometimes just called ~i[expanding] the definition of ~c[endp]. (The notions of ~i[formal], ~i[body], ~i[call], and ~i[actuals] are discussed in ~il[programming-knowledge-taken-for-granted].) Expanding a definition is an example of ~i[unconditional] rewriting. All definitions in ACL2 are just bare equalities relating a call of the function on its formals to its body. Any time you use an equality theorem, whether a definitional equality or something more general like ~bv[] (equal (append (append x y) z) (append x (append y z))) ~ev[] to replace an instance of one side by the corresponding instance of the other in a goal conjecture, we call that ~i[unconditional] rewriting with the equality. ~b[Example 2]: Suppose your goal conjecture is: ~bv[] ~i[Goal Conjecture]: (implies (and (subsetp a b) (true-listp b) (member e a)) (< (len (rm e b)) (len b))). ~ev[] This conjecture may be read ``if ~c[a] is a subset of the ~c[true-listp] ~c[b] and ~c[e] is a member of ~c[a], then the result of removing ~c[e] from ~c[b] has a shorter length than ~c[b].'' You can use the following theorem: ~bv[] ~i[Theorem]: (implies (member u v) (equal (len (rm u v)) (- (len v) 1))) ~ev[] to ~i[rewrite] the ~i[Goal Conjecture] to ~bv[] ~i[Rewritten Goal Conjecture]: (implies (and (subsetp a b) (true-listp b) (member e a)) (< (- (len b) 1) (len b))). ~ev[] To do this you must know that the following subgoal is provable: ~bv[] ~i[Subgoal to Relieve Hyp 1]: (implies (and (subsetp a b) (true-listp b) (member e a)) (member e b)). ~ev[] This is an example of ~i[conditional] rewriting. In order to use the ~i[Theorem] we had to establish that its hypotheses are satisfied. That is called ~i[relieving the hypotheses] and was done by proving the ~i[Subgoal to Relieve Hyp 1]. Conditional rewriting is the most commonly used proof technique in ACL2. Unconditional rewriting is just a special case, where there are no hypotheses to relieve. Expanding a definition is just another special case, where there are no hypotheses to relieve and the pattern is easy to match because it is a call of a function on distinct variables. This page discusses ~i[rewriting] from the logical perspective. It is important that you are familiar with the notions of a ~i[pattern] term being an ~il[logic-knowledge-taken-for-granted-instance instance] of a ~i[target] term. We often say the pattern ~i[matches] the target. These notions involve a corresponding ~i[substitution] of terms for variables. All these notions are discussed in the link for ``~il[logic-knowledge-taken-for-granted-instance instance]'' above and we recommend you read it before continuing. Then use your browser's ~b[Back Button] to come back here. You should also be aware of the terms introduced in our discussion of ~il[logic-knowledge-taken-for-granted-propositional-calculus propositional calculus]. ~i[Rewriting] is a fundamental rule of inference in our system. The rule allows you to use a theorem, i.e., an axiom, lemma, or definition, to replace one term by another in the goal conjecture you're trying to prove. Suppose you have a theorem that is of the form (or can be put into the form): ~bv[] ~i[Theorem]: (implies (and ~i[hyp1] ... ~i[hypk]) (equal ~i[pattern] ~i[replacement])) ~ev[] From the logical perspective we don't care how the theorem was actually written when it was proved. It might have no hypotheses (in which case the ~i[hypi] could just be ~c[t]), or it could have been written in a different but equivalent propositional style, ~c[(or (not] ~i[hyp1]~c[) ...)], or the equality could have been written the other way around, ~c[(equal ]~i[replacement] ~i[pattern]~c[)]. Such syntactic details don't matter. Just take a theorem and use propositional calculus to rearrange it equivalently into this form for the purposes of this one rewrite step. Suppose ~i[pattern] is an instance of some target term, ~i[target] that occurs in your goal conjecture. Let the corresponding substitution be ~i[sigma]. If ~i[sigma] does not contain a binding for every variable that occurs in ~i[Theorem], then extend ~i[sigma] to ~i[sigma'] by adding one binding for each such variable. (This is necessary only if ~i[pattern] does not contain every variable in ~i[Theorem].) Let ~i[replacement'] be the result of instantiating ~i[replacement] with ~i[sigma']. Let ~i[hypi'] be the result of instantiating ~i[hypi] with ~i[sigma']. Then the ~b[Rewrite Rule of Inference] tells us it is permitted to replace that occurrence of ~i[target] in the goal by ~i[replacement'] -- ~b[if you can prove] each ~i[hypi'] in this context. We make this last condition clear in a moment. The justification for this is that ~i[Theorem] is true for all values of the variables. Hence, it is true for the values specified by ~i[sigma']. If the ~i[hypi'] are true, then the target is really equal to ~i[replacement']. But it is always permitted to replace something by something it's equal to. Rewriting thus involves several steps: (1) Finding a ~i[target] and a ~i[theorem] to use to rewrite it to some more desirable ~i[replacement]. (2) Instantiating ~i[pattern] in the (rearranged) theorem to match ~i[target]. (3) Extending ~i[sigma] to ~i[sigma'] to bind all the variables in ~i[Theorem]. (4) Establishing that the ~i[sigma'] instances of each of the ~i[hypi] hold. This is called ~i[relieving the hypotheses] of the theorem and is discussed in greater detail below. (5) Replacing the occurrence of ~i[target] in the goal conjecture by the ~i[sigma'] instance of ~i[replacement], provided all the hypotheses are relieved. Step (4) above, ~i[relieving the hypotheses], requires first identifying the ``context'' of the target in the goal conjecture. To do this, use propositional calculus to rearrange the goal conjecture so the occurrence of ~i[target] is in the conclusion and let ~i[context] be the hypothesis. ~bv[] ~i[Rearranged Conjecture]: (implies ~i[context] (... ~i[target] ...)) ~ev[] To relieve the hypotheses you must then prove each of the following subgoals: ~bv[] ~i[Subgoal to Relieve Hyp i]: (implies ~i[context] ~i[hypi']). ~ev[] It is important to note that this description of rewriting with ~i[Theorem] describes the process from a strictly logical perspective. The syntax of the theorem and the goal don't matter. You're free to use propositional calculus to rearrange them to put them into the appropriate forms to fit the descriptions given. Clearly, if you have a candidate Theorem in the ``wrong'' form and but it can be rearranged with propositional calculus into the ``right'' form, then that rearranged theorem is also a ~i[Theorem] and can be used as described. But in the actual implementation of ACL2, the syntactic form of a proved ~i[Theorem] affects how it is used by rewriting. If a proved theorem takes the form of an implication concluding with an equality, ACL2 treats the left-hand side of the equality as ~i[pattern] and the right-hand side as ~i[replacement], unless you tell it otherwise. We'll discuss this later. Furthermore, being from the logical perspective this discussion of rewriting does not address (a) how you extend ~i[simga] to ~i[sigma'] -- any extension will do provided it allows you to relieve the hypotheses. The ACL2 theorem prover uses certain heuristics which we'll discuss later, including methods by which you can guide the system in the selection. Crucially, it does not discuss whether it is a ~i[good idea] to do a particular rewrite! For example, the definitional equality: ~bv[] (equal (len x) (if (endp x) 0 (+ 1 (len (cdr x))))) ~ev[] may be used repeatedly, endlessly, to replace ~c[(len a)] by an ever growing sequence of terms: ~bv[] (len a) = (if (endp a) 0 (+ 1 (len (cdr a)))) = (if (endp a) 0 (+ 1 (if (endp (cdr a)) 0 (+ 1 (len (cdr (cdr a))))))) = ... ~ev[] The ACL2 implmentation of rewriting uses certain heuristics and the you can guide the system in its choices. We'll discuss this later. Now use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted].~/~/") (deflabel logic-knowledge-taken-for-granted-rewriting-repeatedly :doc ":Doc-Section introduction-to-the-theorem-prover further information on expanding definitions via rewriting~/ We assume you've read about ``~il[logic-knowledge-taken-for-granted-instance instances]'' and picked up our basic terminology including the ideas of ~i[matching] a ~i[pattern] term to a ~i[target] term, obtaining a ~i[substitution] and how to ~i[instantiate] a term with a substitution. We use these notions below without further citation. In addition, we assume you've read about ``~il[logic-knowledge-taken-for-granted-rewriting rewriting]'' where we introduced the idea of treating a theorem (axiom, definition, or lemma) as a ~i[conditional rewrite] rule and replaced one term by an equivalent one provided we can ~i[relieve the hypotheses]. Suppose you're trying to prove ~i[formula1] and you transform it to ~i[formula2] by rewriting. What happened? ~bv[] ~i[formula1]: (implies (and (not (consp z)) (true-listp z)) (equal (rev (rev z)) z)) ~i[formula2]: (implies (and (not (consp z)) (equal z nil)) (equal (rev (rev z)) z)) ~ev[] Evidently we replaced ~c[(true-listp z)] by ~c[(equal z nil)]. But how did that happen? What really happened was the sequential application of several unconditional rewrites and the use of replacement of equals by equals. The definition of ~c[true-listp] is: ~bv[] (defun true-listp (x) (if (consp x) (true-listp (cdr x)) (equal x nil))). ~ev[] By rewriting once with the definition of ~c[true-listp], we transform ~i[formula1] to: ~bv[] ~i[formula1']: (implies (and (not (consp z)) (if (consp z) (true-listp (cdr z)) (equal z nil))) (equal (rev (rev z)) z)). ~ev[] Note how the call of ~c[true-listp] has been replaced by the entire body of ~c[true-listp]. Next, note that the first hypothesis above is that ~c[(consp z)] is false. That is, ~c[(not (consp z))] is the same as ~c[(equal (consp z) nil)]. Thus, replacement of equals by equals means we can transform ~i[formula1'] to ~bv[] ~i[formula1'']: (implies (and (not (consp z)) (if nil (true-listp (cdr z)) (equal z nil))) (equal (rev (rev z)) z)). ~ev[] (We will explore replacement of equals by equals later.) Furthermore, we know the basic axiom about ~c[if]: ~bv[] ~i[Axiom] if-nil: (if nil x y) = y. ~ev[] Rewriting with this particular axiom, using ~c[(if nil x y)] as the pattern and ~c[y] as the replacement, will transform ~i[formula1''] to ~bv[] ~i[formula2]: (implies (and (not (consp z)) (equal z nil)) (equal (rev (rev z)) z)). ~ev[] Often when we say we derived one formula from another by ``expansion'' and or by ``rewriting'' we take many rewrite steps, as here. We typically use hypotheses of the formula without noting ``replacement of equals by equals'' as when we replaced ~c[(consp z)] by ~c[nil], and we typically omit to mention the use of basic axioms like ~c[if-nil] above. Now use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted].~/~/") (deflabel logic-knowledge-taken-for-granted-equals-for-equals :doc ":Doc-Section introduction-to-the-theorem-prover substitution of equals for equals~/ Anytime you have an equality hypothesis relating two terms, e.g., ~bv[] (equal ~i[lhs] ~i[rhs]) ~ev[] it is legal to substitute one for the other anyplace else in the formula. Doing so does not change the truthvalue of the formula. You can use a ~i[negated equality] this way ~i[provided] it appears in the conclusion. For example, it is ok to transform ~bv[] (implies (true-listp x) (not (equal x 23))) ~ev[] to ~bv[] (implies (true-listp 23) (not (equal x 23))) ~ev[] by substitutions of equals for equals. That is because, by ~il[logic-knowledge-taken-for-granted-propositional-calculus propositional calculus], we could rearrange the formulas into their ~i[contrapositive] forms: ~bv[] (implies (equal x 23) (not (true-listp x))) ~ev[] and ~bv[] (implies (equal x 23) (not (true-listp 23))) ~ev[] and see the equality as a hypothesis and the substitution of ~c[23] for ~c[x] as sensible. Sometimes people speak loosely and say ``substitution of equals for equals'' when they really mean ``substitutions of equivalents for equivalents.'' Equality, as tested by ~c[EQUAL], is only one example of an equivalence relation. The next most common is propositional equivalence, as tested by ~c[IFF]. You can use propositional equivalence hypotheses to substitute one side for the other ~i[provided] the target term occurs in a propositional place, as discussed at the bottom of ~il[logic-knowledge-taken-for-granted-propositional-calculus propositional calculus]. Now use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted].~/~/") (deflabel logic-knowledge-taken-for-granted-evaluation :doc ":Doc-Section introduction-to-the-theorem-prover evaluation during proofs~/ Any time you are proving a formula and see a subterm in the formula that contains no variables, you can just evaluate the subterm. This is familiar from algebra: It is not uncommon to rearrange a polynominal to collect all the constants and then add them up: ~bv[] (3x + 2 + 7y + 2) = (3x + 7y + (2 + 2)) = (3x + 7y + 4). ~ev[] That last step is just evaluation. It happens often in ACL2 proofs because theorems involve constants and defined functions and when those constants ``drift into the maw'' of a function, the function call can be eliminated and replaced by a new constant. ACL2 does this automatically; you don't have to tell it. In fact, there are a few occasions where you might prefer it ~i[not] evaluate and those are the ones you have to look out for! They'll be obvious when they happen because you'll see a mysterious constant crop up in the proof. Evaluation is legal because it is just the repeated use of unconditional rewriting to replace definitions by their instantiated bodies until no function calls remain. Now use your browser's ~b[Back Button] to return to the example proof in ~il[logic-knowledge-taken-for-granted].~/~/") (deflabel logic-knowledge-taken-for-granted :doc ":Doc-Section introduction-to-the-theorem-prover background knowledge in ACL2 logic for theorem prover tutorial~/ You might think that in order to use the theorem prover you have to know the mathematical logic supported by ACL2. But you need to know a lot less about it than you might think. Technically, a theorem is a formula that can be derived from axioms by using rules of inference. Thus, to do a proof you have to know (a) the syntax of formulas, (b) the axioms, and (c) the rules of inference. Traditionally, these things are spelled out in excruciating detail in treatments of mathematical logic -- and for good reason. The whole point of proving theorems is that it is a way to determine that a formula is ``always true'' (under some model of the axioms). By ``always true'' we actually mean what logicians mean when they say the formula is ~i[valid]: true in the model, for all possible values of the variables. Here by ``model of the axioms'' we mean an understanding of the meaning of the various function symbols so that the axioms are true for all values of the variables. If the variables in your conjecture can take on an infinite number of values, proof is often the ~b[only] way to determine that a conjecture is ``always true.'' So if proof is being used to determine that a questionable formula is always true the proof must be carried out flawlessly. Thus, the (a) syntax, (b) axioms, and (c) rules of inference must be described precisely and followed to the letter. But formal mathematical logic was invented to explain how people reason. To the extent that logic mimics human reasoning, proofs can be seen as just extremely carefully crafted arguments. Given that ACL2 is responsible for following the rules ``to the letter,'' your main job is ``explain'' the big leaps. To use the theorem prover you must understand (a) the syntax, because you must be able to write formulas flawlessly. But you don't have to know (b) the axioms and (c) the rules of inference at nearly the same level of precision, as long as you understand the basic structure and language of proofs. Below is part of a proof of a certain theorem. You ought to be able to understand the following. Since what we describe is a proof of one case of the formula, we hope that you're ~i[convinced] that the formula holds for that case. Read this and follow the links to confirm that you understand what happens. Be sure to then use your browser's ~b[Back Button] to return to this page and continue. ~b[An Annotated Proof of] ~bv[] (implies (true-listp z) (equal (rev (rev z)) z)) ~ev[] ``We will prove that reversing the reverse of a ~c[true-listp] yields the original list. The formula stating this is above. We will prove it by ~il[logic-knowledge-taken-for-granted-inductive-proof induction] on the list structure of ~c[z]. The ~il[logic-knowledge-taken-for-granted-base-case base case] of the induction is: ~bv[] (implies (endp z) (implies (true-listp z) (equal (rev (rev z)) z))). ~ev[] This formula is equivalent, by ~il[logic-knowledge-taken-for-granted-propositional-calculus propositional calculus], to ~bv[] (implies (and (endp z) (true-listp z)) (equal (rev (rev z)) z)) ~ev[] ~il[logic-knowledge-taken-for-granted-rewriting Rewriting] with the definition of ~c[endp] produces: ~bv[] (implies (and (not (consp z)) (true-listp z)) (equal (rev (rev z)) z)) ~ev[] ~il[logic-knowledge-taken-for-granted-rewriting-repeatedly Rewriting repeatedly] starting with the definition of ~c[true-listp] produces: ~bv[] (implies (and (not (consp z)) (equal z nil)) (equal (rev (rev z)) z)) ~ev[] Then using the second ~i[hypothesis], just ~il[logic-knowledge-taken-for-granted-equals-for-equals substituting equals for equals], we get ~bv[] (implies (and (not (consp z)) (equal z nil)) (equal (rev (rev nil)) nil)) ~ev[] Since the ~i[conclusion] involves no variables, we can ~il[logic-knowledge-taken-for-granted-evaluation evaluate] it, getting ~bv[] (implies (and (not (consp z)) (equal z nil)) T) ~ev[] But this is an ~il[logic-knowledge-taken-for-granted-instance instance] of the ~il[logic-knowledge-taken-for-granted-propositional-calculus tautology] ~c[(implies p T)]. Thus, the base case is proved.'' Now it is time for a little quiz. There are just three questions. ~b[Q1]: The case above was the Base Case of an inductive proof of ~bv[] (implies (true-listp z) (equal (rev (rev z)) z)) ~ev[] in which we did induction on the structure of the linear list ~c[z]. What is the Induction Step? That is, what do you have to prove besides the Base Case to complete this inductive proof? Below are four commonly given answers; choose one. Then look ~il[logic-knowledge-taken-for-granted-q1-answer here] to find out if you're right. ~bv[] ~i[Induction Step -- Choice (i)]: (implies (not (endp z)) (implies (true-listp z) (equal (rev (rev z)) z))) ~i[Induction Step -- Choice (ii)]: (implies (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z))) ~i[Induction Step -- Choice (iii)]: (implies (and (not (endp z)) (equal (rev (rev (cdr x))) (cdr x))) (implies (true-listp z) (equal (rev (rev z)) z))) ~i[Induction Step -- Choice (iv)]: (implies (and (not (endp z)) (implies (true-listp (cdr z)) (equal (rev (rev (cdr z))) (cdr z)))) (implies (true-listp z) (equal (rev (rev z)) z))) ~ev[] ~b[Q2]: To prove the Induction Step we must prove one or more of the goals below. Which combinations are sufficient to imply the Induction Step? Decide what is required and then look ~il[logic-knowledge-taken-for-granted-q2-answer here] to find out if you're right. To help you, the Induction Step is of the form: ~bv[] ~i[Induction Step]: (implies (and ~i[c] (implies ~i[p'] ~i[q'])) (implies ~i[p] ~i[q])) ~ev[] and beside each candidate subgoal we show its structure in those terms. ~bv[] ~i[Subgoal (i)]: (implies (and (not (endp z)) ; (implies (and ~i[c] (true-listp z)) ; ~i[p]) (true-listp (cdr z))) ; ~i[p']) ~i[Subgoal (ii)]: (implies (and (not (endp z)) ; (implies (and ~i[c] (true-listp z) ; ~i[p] (equal (rev (rev (cdr z))) (cdr z))) ; ~i[q']) (equal (rev (rev z)) z)) ; ~i[q]) ~i[Subgoal (iii)]: (implies (and (not (endp z)) ; (implies (and ~i[c] (equal (rev (rev (cdr z))) (cdr z))) ; ~i[q']) (equal (rev (rev z)) z)) ; ~i[q]) ~i[Subgoal (iv)]: (implies (and (not (endp z)) ; (implies (and ~i[c] (true-listp (cdr z)) ; ~i[p'] (equal (rev (rev (cdr z))) (cdr z))) ; ~i[q']) (equal (rev (rev z)) z)) ; ~i[q]) ~ev[] ~b[Q3]: Suppose you know the theorem ~bv[] ~i[Theorem]: (implies (p (f x)) (equal (g (h x)) x)) ~ev[] and you wish to rewrite the target ~c[(g (h a))] to ~c[a] in ~bv[] ~i[Goal Conjecture]: (implies (and (q (f a)) (r a)) (s (g (h a)))) ~ev[] What must you prove to relieve the hypothesis of ~i[Theorem]? After you've thought about it, look ~il[logic-knowledge-taken-for-granted-q3-answer here] for our answer. ~b[End of the Quiz] If this page made sense, you're ready to read the introduction to the theorem prover. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover].~/~/") (deflabel special-cases-for-rewrite-rules :doc ":Doc-Section introduction-to-the-theorem-prover convenient short forms for rewrite rule formulas~/ In principle, every rewrite rule is made from a formula of this shape: ~bv[] (IMPLIES (AND ~i[hyp1] ... ~i[hypk]) (~i[eqv] ~i[lhs] ~i[rhs])) ~ev[] where ~i[eqv] is either ~c[EQUAL] or ~c[IFF] and the result of expanding any abbreviations in ~i[lhs] is the application of some function symbol other than ~c[IF]. * In the special case where there is only one ~i[hyp] term, i.e., ~i[k]=1, the ~c[(AND ]~i[hyp1]~c[)] can be written ~i[hyp1]. * In the special case where there are no ~i[hyp] terms, ~i[k]=0, the ~c[(AND)] term is logically just ~c[T] and the whole ~c[IMPLIES] can be dropped; such a formula may be written as an unconditional ~c[EQUAL] or ~c[IFF] term. * If you build a rewrite rule from a formula that concludes with ~c[(NOT ]~i[x]~c[)], it is treated as though it were ~c[(EQUAL ]~i[x]~c[ NIL)], which is logically equivalent to what you typed. * If you build a rewrite rule from a formula that concludes with an ~c[AND], ACL2 will build a rewrite rule for each conjunct of the ~c[AND]. This is because ~bv[] (IMPLIES hyp (AND concl1 concl2)) ~ev[] is propositionally equivalent to ~bv[] (AND (IMPLIES hyp concl1) (IMPLIES hyp concl2)). ~ev[] However, if you use an ~c[OR]-expression as a hypothesis, ACL2 does ~i[not] do the dual transformation. Thus, ~c[(IMPLIES (OR hyp1 hyp2) concl)] generates one rewrite rule. * Finally, if you build a rewrite rule from a formula that does not conclude with an ~c[EQUAL], an ~c[IFF], a ~c[NOT], or an ~c[AND,] but with some other term, say, ~i[lhs], then ACL2 acts like you typed ~c[(IFF ]~i[lhs]~c[ T)], which is logically equivalent to what you typed. Thus, regardless of what you type, every rule has ~i[k] hypotheses. For unconditional rules, ~i[k] is 0 and the hypotheses are vacuously true. Whether or not you write an ~c[EQUAL] or an ~c[IFF] in the conclusion, every rule is either an equality or a propositional equivalence, every rule has a left-hand side, and every rule has a right-hand side. Use your browser's ~b[Back Button] now to return to ~il[introduction-to-rewrite-rules-part-1].~/~/") (deflabel equivalent-formulas-different-rewrite-rules :doc ":Doc-Section introduction-to-the-theorem-prover logically equivalent formulas can generate radically different rules~/ Consider the rewrite rules that would be generated from the three commands below. In all three cases, the fact being stated relates the ~c[n]th element of the reverse of ~c[x] to the ~c[n]th element of ~c[x]. In fact, the three formulas are simple rearrangements of each other and are all equivalent. The theorem prover treats all three formulas equivalently when proving them. But the rules generated from them are very different. ~bv[] (defthm nth-rev-1 (implies (and (natp n) (< n (len x))) (equal (nth n (rev x)) (nth (- (len x) (+ 1 n)) x)))) (defthm nth-rev-2 (implies (and (natp n) (< n (len x))) (equal (nth (- (len x) (+ 1 n)) x) (nth n (rev x))))) (defthm nth-rev-3 (implies (and (natp n) (not (equal (nth n (rev x)) (nth (- (len x) (+ 1 n)) x)))) (not (< n (len x))))) ~ev[] Here are the three rewrite rules: ~b[nth-rev-1]:~nl[] Replace instances of ~c[(NTH n (REV x))]~nl[] by ~c[(NTH (- (LEN x) (+ 1 n)) x)],~nl[] if you can establish that ~c[n] is a natural number less than the length of ~c[x]. ~b[nth-rev-2]:~nl[] Replace instances of ~c[(NTH (- (LEN x) (+ 1 n)) x)]~nl[] by ~c[(NTH n (REV x))],~nl[] if you can establish that ~c[n] is a natural number less than the length of ~c[x]. ~b[nth-rev-3]:~nl[] Replace instances of ~c[(< n (LEN x))]~nl[] by ~c[NIL]~nl[] if you can establish that ~c[n] is a natural number and that ~c[(NTH n (REV x))] is different from ~c[(NTH (- (LEN x) (+ 1 n)) x)]. As the driver of ACL2, you have to decide which rule you want ~i[when you give the command to prove it]. If you tell the theorem prover to use both ~c[nth-rev-1] and ~c[nth-rev-2], ACL2 will enter an infinite loop when it sees any term matching either ~c[NTH] expression. Most users would choose form ~c[nth-rev-1] of the rule. It eliminates ~c[rev] from the problem -- at the expense of introducing some arithmetic. But arithmetic is so fundamental it is rarely possible to avoid it and it is likely to be in the problem already since you're indexing into ~c[(rev x)]. The ~c[nth-rev-2] form of the rule is ``bad'' because it introduces ~c[rev] into a problem where it might not have appeared. The ~c[nth-rev-3] version is ``bad'' because it makes the theorem prover shift its attention from a simple arithmetic inequality to a complicated property of ~c[nth] and ~c[rev], which might not be in the problem. Use your browser's ~b[Back Button] now to return to ~il[introduction-to-rewrite-rules-part-1].~/~/") (deflabel introduction-to-rewrite-rules-part-2 :doc ":Doc-Section introduction-to-the-theorem-prover how to arrange rewrite rules~/ You should design your rewrite rules to canonicalize the terms in your problem, that is, your rules should drive terms into some normal form so that different but equivalent terms are rewritten into the preferred shape, making equivalent terms identical. You are very familiar with this idea from algebra, where you learned to normalize polynomials. Thus, when you see ~i[(2x + 6)(3x - 9)] you automaticaly normalize it, by ``multiplying out and collecting like terms,'' to get ~i[(6x^2 - 54)]. This normalization strategy allows you to recognize equivalent terms presented differently, such as ~i[6(x^2 - 9)]. The ACL2 user is responsible for making up the rules. (Standard ``books'' -- files of ACL2 definitions and theorems -- can often provide rules for some sets of functions, e.g., arithmetic.) This is a heavy burden on you but it means you are in charge of your own normal forms. For example, if you use the function ~c[nthcdr], which returns the ~c[n]th ~c[cdr] of a list, you might see both ~c[(cdr (nthcdr i x))] and ~c[(nthcdr i (cdr x))]. These two expressions are equivalent but not identical. You will want to decide which you want to see and prove the rewrite rule that converts the other to that preferred form. Most good users develop an implicit ordering on terms and rewrite ``heavy'' terms to ``lighter'' ones. This insures that there are no loops in their rewrite rules. But this ordering changes according to the user and the problem. Generally, the lightest terms are primitives such as ~c[IF], ~c[EQUAL], arithmetic, etc. Functions defined without explicit recursion tend to be ignored because they are just expanded away (but see below). Recursively defined functions tend to be heavier than any other recursive function used in their definitions, so, for example, if ~c[rev] is defined in terms of ~c[append], ~c[rev] is heavier than ~c[append]. But the size and subtlety of recursively defined functions also affects their place in the ordering. But rewrite rules are surprisingly subtle. Recall that a rewrite rule can be made from a formula of this form: ~bv[] (IMPLIES (AND ~i[hyp1] ... ~i[hypk]) (~i[eqv] ~i[lhs] ~i[rhs])) ~ev[] where ~i[eqv] is either ~c[EQUAL] or ~c[IFF], and ~i[lhs] is a call of a function other than ~c[IF]. In such a rule, ~i[lhs] is the pattern responsible for triggering the rule, the ~i[hypi] are conditions which must be satisfied by the context of the target being rewritten, and ~i[rhs] is the replacement. The replacement only happens if the rule is enabled, the pattern matches, the conditions are satisfied, and (in the case of an ~c[IFF] rule) the target occurs propositionally. There are other heuristic restrictions that we won't discuss here. So how should you phrase a theorem in order to make it an effective rule? General Principles: * ~b[Strengthen the Formula]: The fewer hypotheses a formula has the better the rewrite rule formed from it. The more general the left-hand side the better the rule. The fewer free variables in the hypothesis, the better. The idea is to form a rule that fires predictably. Later in this tutorial you'll get some practice formulating strong rules. * ~b[Choosing the Conclusion]: If a lemma is an implication, you have to choose what the conclusion will be. (You'll also have to ``orient'' that conclusion by choosing a left-hand side and a right-hand side, but we discuss that below). You can swap the conclusion and any hypothesis by negating both, producing a different conclusion. There are generally two (possibly conflicting) heuristics for deciding which part of the formula should be the conclusion: ~i[Choosing the Conclusion Heuristic 1]: Can you make the conclusion be an ~c[EQUAL] or ~c[IFF] expression that has a ``heavy term'' on one side? That will make a rule that replaces the heavy term with a lighter one. We discuss this situation more below. ~i[Choosing the Conclusion Heuristic 2]: Can you make the conclusion be a non-propositional term that contains all the variables mentioned in the hypotheses? By ``non-propositional'' we mean a term that is not just the propositional combination (e.g., with ~c[AND] or ~c[OR]) of other terms but instead some call of some ``heavy'' function? If your conclusion contains all the variables mentioned in the hypotheses, matching it will instantiate all the variables in the hypotheses. That way ACL2 will not have to guess instantiations of unbound variables when it tries to relieve the hypotheses. It is not very good at guessing. * ~b[Orienting the Conclusion]: If the conclusion is an ~c[EQUAL] or an ~c[IFF], you have to decide which is the left-hand side and which is the right. If the conclusion is ~c[(NOT ]~i[lhs]~c[)], then the left-hand side is ~i[lhs] and the right-hand side is ~c[NIL]. If the conclusion is not an ~c[EQUAL], an ~c[IFF], or a ~c[NOT] then the conclusion itself will be the left-hand side and the right-hand side will be ~c[T]. If your lemma was created by looking a Key Checkpoints while using The Method, the left-hand side should match some term in that checkpoint. Remember, the left-hand side is the ``trigger'' that will make the rule fire. It is the pattern that ACL2 will be looking for. * ~b[Pay attention to the free variables]: Look at the variables that occur in the pattern (the left-hand side) and compare them to the variables that occur in the hypotheses. Does some hypothesis contain a variable, say ~i[v], that is not in the pattern? We call ~i[v] a ~i[free variable] because it will not be assigned a value (``bound'') by the process of pattern matching. ACL2 will have to guess a value for ~i[v]. If some hypothesis contains ~i[v] as a free variable, ask whether more than one hypothesis contains ~i[v]? ACL2 uses the first hypothesis containing a free ~i[v] to guide its guess for ~i[v]. To ``guess'' a value for ~i[v], ACL2 uses that hypothesis as a pattern and tries to match it against the assumptions in the checkpoint formula being proved. This means that key hypothesis must be in normal form, to match the rewritten assumptions of the goal. It also means that you should reorder the hypotheses to put the most unusual hypothesis containing a free ~i[v] first in the list of conjuncts. For example, if ~c[v] is free in two hypotheses, ~c[(natp v)] and ~c[(member (nthcdr v a) b)], then we recommend putting the ~c[member] term first. There are likely to be many terms in the goal satisfying the ~c[natp] hypothesis -- or none if ~c[natp] has expanded to an integer inequality -- while there are likely to be few terms satisfying the ~c[member] hypothesis, especially if ~c[a] and ~c[b] are bound by the left-hand side of the rule. Here are some (possibly conflicting) heuristics for choosing the left-hand side: ~i[Choose a Left-Hand Side that Occurs in a Key Checkpoint]: If you use the Method you will tend to do this anyway, because you'll see terms in the Key Checkpoints that you want to get rid of. But many moderately experienced users ``look ahead'' to how the proof will go and formulate a few anticipatory rules with the idea of guiding ACL2 down the preferred path to the proof. When you do that, you risk choosing left-hand sides that won't actually arise in the problem. So when you formulate anticipatory rules, pay special attention to the functions and terms you put in the left-hand sides. The next few paragraphs deal with specific cases. ~i[Avoid Non-Recursive Functions in the Left-Hand Side]: If the left-hand side contains a call of a defined function whose definition is not recursive, then it will almost never match any target in the formula being rewritten unless the function is disabled. Suppose for example you have defined ~c[SQ] so that ~c[(SQ x)] is ~c[(* x x)]. Suppose you considered choosing a left-hand side like ~c[(+ (SQ x) (SQ y))]. Suppose you hoped it would hit the target ~c[(+ (SQ A) (SQ B))] in some formula. But when ACL2 simplifies the formula, it will first rewrite that target to ~bv[] (+ (* A A) (* B B)) ~ev[] by expanding the definition of ~c[SQ], since it could do so without introducing any recursive calls. But now the target won't match your rule. By choosing a left-hand side that occurs in a Key Checkpoint (and is not one of a handful of abbreviations ACL2 uses in its output like ~c[AND], ~c[NOT]), you'll avoid this problem since ~c[SQ] will have already been expanded before the Key Checkpoint is printed. ~i[Disable Non-Recursive Functions]: If you insist on a left-hand side that contains calls of non-recursive functions, remember to disable those non-recursive functions after you've proved all the rules you want about them. By disabling ~c[SQ] you can prevent ACL2 from expanding the definition as it did above. Sometimes you will define a function non-recursively to formalize some concept that is common in your application and you will want to create a sort of algebra of rules about the function. By all means do so, so you can conduct your formal reasoning in terms of the concepts you're informally manipulating. But after proving the required laws, disable the non-recursive concept so that ACL2 just uses your laws and not the messy definition. ~i[Choose a Left-Hand Side Already in Simplest Form]: This is a generalization of the advice above. If any rule will rewrite your left-hand side, it will prevent your rule from matching any target. For example, if you write a left-hand side like ~c[(foo (car (cons x y)))] then it would never match any target! The reason is that even if ~c[(FOO (CAR (CONS A B)))] did occur in some goal formula, before ACL2 would try your rule about ~c[foo] it will use the obvious rule about ~c[CAR] and ~c[CONS] to transform your imagined target to ~c[(FOO A)]. Thus, your rule would not match. So you have to keep in mind ~i[all your other rules] when you choose a left-hand side (and when you choose the hypotheses to guide free variable selection). If you always choose a pattern that matches a term in a Key Checkpoint, you avoid this problem. ~i[Make Sure the Left-Hand Side is ``Heavier'' than the Right]: Sometimes this is obvious, as when you choose ~c[(REV (REV x))] for the left-hand side and ~c[x] for the right. But what do you about ~c[(REV (APPEND x y))] versus ~c[(APPEND (REV y) (REV x))]? Most of the time we choose to drive the heaviest function (in this case ~c[REV]) down toward the variables, lifting the lighter function (~c[APPEND]) up so that we can reason about the lighter function's interaction with the surrounding ``matrix'' of the formula. So we'd rewrite ~c[(REV (APPEND x y))] to ~c[(APPEND (REV y) (REV x))], not vice versa. ~i[Alternative Ways to Talk About the Same Thing]: If your problem and specification use two different ways to talk about the same thing, choose one form and rewrite the other into that form. For example, the ACL2 built-in ~c[nth] returns the nth element of a list, and the built-in function ~c[nthcdr] returns the nth ~c[cdr] of a list. They are defined independently. But ~c[(nth n x)] is the same thing as ~c[(car (nthcdr n x))]. Since ~c[nth] can be expressed in terms of ~c[nthcdr] but not vice versa, it is clear we should prove ~c[(equal (nth n x) (car (nthcdr n x)))] as a rewrite rule if both ~c[nth] and ~c[nthcdr] are involved in the problem. ~i[Don't Let Computational Efficiency Dictate the Terms]: If you have two functions that are equivalent (perhaps one was defined to be computationally more efficient), prove their equivalence as a rewrite rule that eliminates the more complicated function. An extreme example would be a model that uses a sophisticated data structure (like a balanced binary tree, red-black tree, ordered array, or hash table) to implement something simple like an association of keys to values. By proving the equivalence as stated you can eliminate the messy function early and do the bulk of your reasoning in terms of its simple specification. The best ACL2 users become very good at keeping all these things in mind when designing their rewrite rules. Practice makes perfect. Don't be afraid during your learning of ACL2 to undo the rules you first invented and try to make better ones. Finally, be patient! There will be times when you think to yourself ``Why should I spend my time thinking of rules that guide ACL2? I know the proof!'' There are two reasons. First, you may ``know'' the proof but you may well be wrong and part-way through this whole exercise you may realize that you're missing a major hypothesis or special case that breaks your whole conception of the problem. The proof is in the details. Second, most of the time the library of rules you develop in this process will be used over and over again on variants of the main problem in the months and years ahead. This is sometimes called the ~i[proof maintenance] problem. Theorems don't suffer bit rot! But the artifacts you're modeling change and you will need to prove new versions of old theorems. A good general purpose library makes this much easier. We now recommend that you practice inventing strong rules; ~pl[strong-rewrite-rules]. For advice on handling specific kinds of formulas and definitions, ~pl[specific-kinds-of-formulas-as-rewrite-rules]. For more information about the rewriter works and how rules are created, ~pl[further-information-on-rewriting]. If you are working your way through the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] to return to ~il[introduction-to-the-theorem-prover]. ~/~/") (deflabel specific-kinds-of-formulas-as-rewrite-rules :doc ":Doc-Section introduction-to-the-theorem-prover advice about how to handle commonly occurring formulas as rewrite rules~/ Below we give you some guidelines for handling specific, commonly occurring situations. * ~b[Associativity]: If a function ~c[f] is associative, prove ~bv[] (equal (f (f x y) z) (f x (f y z))) ~ev[] ACL2 will use this to flatten ~c[f]-nests ``to the right.'' * ~b[Commutativity]: If a function ~c[f] is commutative, prove both ~bv[] (equal (f x y) (f y x)) ~ev[] and ~bv[] (equal (f x (f y z)) (f y (f x z))) ~ev[] ACL2's heuristics will use these rules to order the arguments alphabetically, so that ~c[(f B (f D (f A C)))] becomes ~c[(f A (f B (f C D)))]. * ~b[Distributivity]: If you have a pair of functions ~c[f] and ~c[g] so that ~c[(f x (g y z))] is ~c[(g (f x y) (f x z))] or some other form of distributivity is provable, arrange your rules to move the lighter function symbol up and the heavier one toward the variable symbols. For example, our arithmetic libraries drive multiplication through addition, producing sums of products rather than products of sums. * ~b[Identity and Other Laws]: Prove the obvious identity and zero laws (or at least anticipate that you might need them down the road) so as to eliminate operators. * ~b[Get Rid of Tail Recursive Functions]: A corollary to the above advice concerns tail recursive functions that use auxiliary variables. New users often define concepts using tail recursions, accumulating partial results in auxiliary variables, because creating such functions is similar to programming with ~c[while] loops. Expert users will use tail recursion when necessary for execution efficiency. But tail recursive functions are messy to reason about: their auxiliary variables have to be properly initialized to make the functions compute the expected results, but to state inductively provable properties of tail recursive functions you must identify the invariants on those auxiliary variables. This problem tends not to happen with ~i[primitive recursive functions]. A primitive recursive function is one that recurs down one variable and holds all the other variables constant in recursion. Most tail-recursive functions can be written elegantly as primitive recursive functions, though one might have to ignore the programmer's desire to make things efficient and define auxiliary functions to appropriately transform the value returned by the recursive call. The classic example is reverse defined in terms of the auxiliary function ~c[append] versus reverse defined tail recursively with an accumulator. By introducing ~c[append] you introduce a concept about which you can state lemmas and decompose the proofs of properties of reverse. So if your problem involves tail recursive functions with auxiliary variables, define the primitive recursive version, prove that the tail recursive function is equivalent to the primitive recursive one, and arrange the rewrite rule to eliminate the tail recursive function. * ~b[Get Rid of Mutually Recursive Functions]: Similarly, if you have used ~c[mutual-recursion] to introduce a clique of mutually recursive functions, ~c[f1], ~c[f2], ..., you will find that to reason about any one function in the nest you have to reason about all of them. Any mutually recursive function can be defined in a singly recursive way. So do that and then prove a rewrite rule that gets rid of all the mutually recursive functions by proving ~bv[] (and (equal (f1 ...) (g1 ...)) (equal (f2 ...) (g2 ...)) ...) ~ev[] where the ~c[gi] are singly recursive. You may need to appeal to a trick to define the ~c[gi]: define a singly recursive function that takes a flag argument and mimics whichever mutually recursive function the flag specifies. ~l[mutual-recursion] ~warn[] and ~pl[mutual-recursion-proof-example] ~warn[]. If you got to this documentation page from the tutorial discussion of rewrite rules, use your browser's ~b[Back Button] now to return to ~il[introduction-to-rewrite-rules-part-2]. ~/~/") (deflabel further-information-on-rewriting :doc ":Doc-Section introduction-to-the-theorem-prover a grab bag of advice and information on rewriting~/ In the following paragraphs we give some links to advanced topics, marked with ``~warn[]''. If you are reading this topic as part of the tutorial on the theorem prover, do not follow these links upon your first reading. Just take note of the existence of the facilities and ideas mentioned. ~b[Arithmetic]: If your goal theorem involves even trivial arithmetic, such as adding or subtracting ~c[1], we recommend that you do ~bv[] (include-book \"arithmetic/top-with-meta\" :dir :system) ~ev[] which loads into ACL2 all the rules in one of the so-called ACL2 ``community books''. (~i[Books] are certified files of definitions, lemmas, etc., usually prepared by other ACL2 users and explicitly shared with the community. The ACL2 installation instructions suggest downloading the community books.) The book \"top-with-meta\" is the most elementary and most widely used arithmetic book. Other community books include \"arithmetic-5/top\" and various hardware and floating-point arithmetic books. ~b[Rules Concluding with Arithmetic Inequalities]: If you are tempted to create a rewrite rule with an arithmetic inequality as its conclusion or left-hand side, think again. Inequalities such as ~bv[] (<= (len (delete e x)) (len x)) ~ev[] make poor left-hand sides for rewrite rules. For example, the inequality above does not match the target ~bv[] (<= (LEN (DELETE E X)) (+ 1 (LEN X))) ~ev[] even though it is sufficient to prove the target (given some simple arithmetic). We recommend that if you have a theorem that establishes an arithmetic inequality, you make it a ~i[linear] rule. ~l[linear] ~warn[]. ~b[Rearranging Formulas Before Making Rules]: It is possible to rearrange the propositional structure of a proved formula before processing it as a rule. This allows you to state a theorem one way ``for publication'' and rearrange it to be stored as a more effective rule. ~l[introduction-to-the-database] (a tutorial topic you'll come to later) and its discussion of the concept of ~c[corollary]. Also, see the discussion of ~c[corollary] in ~ilc[rule-classes] ~warn[]. ~b[Rewriting with New Equivalence Relations]: You may introduce new ~i[equivalence] relations, like ``set-equal'' or ``is-a-permutation'' and cause the rewriter to replace equivalents by equivalents in suitable contexts, where you use ~i[congruence] rules to inform ACL2 of where these more relaxed notions of equivalence may be used; ~pl[equivalence] ~warn[] and ~pl[congruence] ~warn[]. ~b[Pragmatic Advice to Control Rules]: You may attach various ~i[pragmas] to a rule that allow you rather fine heuristic control over whether and how the rule is applied. For example, you may mark a hypothesis to be ~i[forced] (~pl[force] ~warn[]) meaning that the rule is to be applied even if that hypothesis is not relieved -- but if the proof is successful the system will turn its attention to all forced subgoals. You may similarly mark a hypothesis so as to cause a case split, allowing the relief of the hypothesis on one branch and spawning another branch explicitly denying the hypothesis; ~pl[case-split] ~warn[]. You may add a bogus hypothesis that looks at the intended application of the rule and decides whether to apply the rule or not, performing an arbitrary computation on the syntactic context of the application; ~pl[syntaxp] ~warn[]. By providing a ~c[:match-free] modifier to the ~c[:rewrite] rule declaration in your rule-classes, you may tell ACL2 to try all or only the first free variable value it guesses (~pl[rule-classes] ~warn[]). You may provide a bogus hypothesis that computes from the syntactic environment the values to guess for the free variables in a rule; ~pl[bind-free] ~warn[]. You may mark a term so that the rewriter does not dive into it; ~pl[hide] ~warn[]. ~b[Programming Your Own Rewriter]: If you cannot find a way to use rewrite rules to make the transformations you desire, you might investigate the use of ~i[metafunctions]. A metafunction is just a little theorem prover of your own design. It takes as input a list structure representing a term and returns a list structure representing a term. If you can prove that the meaning of the input and output terms are equivalent, you can extend the ACL2 simplifier to call your metafunction. ~l[meta] ~warn[]. ~b[The Order in which Targets are Rewritten]: The rewriter sweeps through terms ``inside-out'' otherwise known as ``left-most innermost first''. Thus, before trying to apply rewrite rules to ~c[(]~i[f a1 ... an]~c[)], rules are applied to the ~i[ai]. This has the good effect of normalizing the ~i[ai]. This fact might help you understand why sometimes your rules ``don't seem to fire.'' For example, suppose you have a rule for rewriting ~c[(len (rev x))] to ~c[(len x)] and suppose you wish to prove a theorem about ~c[(LEN (REV (CONS A B)))]. Suppose ~c[rev] is defined in terms of ~c[append], as shown in ~il[programming-knowledge-taken-for-granted]. Then you might see a checkpoint in which the ~c[(LEN (REV ...))] above has been simplified to ~c[(LEN (APPEND (REV B) (LIST A)))] instead of to ~c[(LEN (CONS A B))]. Why wasn't your rule about ~c[(len (rev x))] applied? The reason is that ~c[(REV (CONS A B))] rewrote to ~c[(APPEND (REV B) (LIST A))] before rules were applied to ~c[(LEN (REV ...))]. You need a rule about ~c[(len (append x y))], as you will see from the checkpoint. ~b[The Order in which Rules are Tried]: The rewriter tries the most recently proved rules first. For example, suppose ~c[f], ~c[g], and ~c[h] are functions defined so that the following two theorems are provable and suppose you executed the following two events in the order shown: ~bv[] (defthm rule1 (equal (f (g x)) (h 1 x))) (defthm rule2 (equal (f (g x)) (h 2 X))) ~ev[] Then if rewrite rules are applied to ~c[(F (G A))], the result will be ~c[(H 2 A)], because the latter rule, ~c[rule2], is applied first. It is generally best not to have conflicting rules or, at least, to understand how such conflicts are resolved. The system will warn you when you propose a rule that conflicts with an existing one. If you were reading this topic as part of the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-rewrite-rules-part-2].~/~/") (deflabel introduction-to-rewrite-rules-part-1 :doc ":Doc-Section introduction-to-the-theorem-prover introduction to ACL2's notion of rewrite rules~/ Rewrite rules make ACL2 replace one term by another. This is done by the rewriter, which is part of ACL2's simplifier. The rewriter sweeps through the goal formula trying all the rewrite rules it knows. Here's an example. Just pretend that you have made a rewrite rule from the formula below. ~bv[] (implies (and (natp i) (< i (len a))) (equal (put i v (append a b)) (append (put i v a) b))) ~ev[] Then every time the rewriter sees a target term that ~i[matches] ~bv[] (put i v (append a b)) ~ev[] it considers the rule, with the variables ~c[i], ~c[v], ~c[a], and ~c[b] of the rule ~i[bound] to whatever terms matched them in the target, say the terms ~i[i], ~i[v], ~i[a], and ~i[b]. To consider the rule, the rewriter first tries to establish (``relieve'') the hypotheses. In particular, it rewrites: ~bv[] (natp ~i[i]) ; ~i[hyp 1] ~ev[] and ~bv[] (< ~i[i] (len ~i[a])). ; ~i[hyp 2] ~ev[] If both hyptheses rewrite to true, then the rule ~i[fires] and replaces the target by: ~bv[] (append (put ~i[i] ~i[v] ~i[a]) ~i[b]). ~ev[] In short, rewrite rules direct ACL2 to rearrange the terms in the goal formulas. We are more precise later, but for now we turn to the question of how do you make a rewrite rule from a formula? The answer is, you prove the formula with the ~c[defthm] command. Recall that ~bv[] (defthm ~i[name] ~i[formula] ...) ~ev[] commands ACL2 to try to prove ~i[formula] and, if successful, build it into the database as a rule according to your specification in the ~i[rule-classes] argument of the ... part of the command. To make it easy for you to generate rewrite rules, ~c[defthm] has a simple heuristic: if you don't tell it what kind of rule to generate from ~i[formula], it generates a rewrite rule! Thus, if this command ~bv[] (defthm ~i[name] ~i[formula]) ~ev[] is successful, ACL2 will have a new rewrite rule in the database, even though you did not ~i[explicitly] tell it to add a rule. A common mistake for the new users is to forget that the above command adds a rewrite rule. This often results in a tangle of rules that lead nowhere or to infinite rewriting that you will have to interrupt. It is also good to remember that the command ~i[only] adds a rule. It does not magically make ACL2 aware of all the mathematical consequences of the formula: it just makes a rewrite rule. When you prove a theorem with ~c[defthm] you are ~i[programming] ACL2. Being careless in your statement of lemmas is tantamount to being careless in your programming. ACL2 can generate rewrite rules from formulas that look like this: ~bv[] (IMPLIES (AND ~i[hyp1] ... ~i[hypk]) (~i[eqv] ~i[lhs] ~i[rhs])) ~ev[] where ~i[eqv] is either ~c[EQUAL] or ~c[IFF], and ~i[lhs] is not a variable symbol, not a constant, and not a call of the function ~c[IF], and not a call of an abbreviation (``macro'') that expands to any of these. So illegal ~i[lhs] include ~c[X], ~c[0], ~c[(IF X Y Y)], and ~c[(OR p q)]. The last is illegal because ~c[OR] is just an abbreviation for a certain ~c[IF]-expression. Technical Note: This tutorial introduction to the theorem prover takes liberties with the truth! We are trying to give you a useful predictive model of the system without burdening you with all the details, which are discussed in the reference manual. For example, using directives in the rule-classes you can rearrange the proved formula into the form you want your rule to take, and you can make ACL2 take advantage of equivalence relations ~i[eqv] other than just ~c[EQUAL] and ~c[IFF]. But we'll ignore these fine points for now. We call the ~i[hyp] terms the ~i[hypotheses] of the rule. We call ~i[lhs] the ~i[left-hand side] of the rule, and we call ~i[rhs] the ~i[right-hand side] of the rule. If the conclusion of the rule is an ~c[EQUAL] term we call it an ~i[equality] rule. Otherwise, it is a ~i[propositional equivalence] rule. If there are no hypotheses, ~i[k]=0, we say the rule is an ~i[unconditional] rewrite rule; otherwise it is ~i[conditional]. ACL2 allows several special cases of the shapes above. ~l[special-cases-for-rewrite-rules], but come back here and continue. A rewrite rule makes ACL2 seek out occurrences of terms that match the left-hand side of the rule and replace those occurrences using the right-hand side, provided all the hypotheses rewrite to true in the context of the application of the rule. That is, the left-hand side is treated as a ~i[pattern] that triggers the rule. The hypotheses are ~i[conditions] that have to be proved in order for the rule to fire. The right-hand side is the ~i[replacement] and is put into the formula where the pattern occurred. Now for some clarifications. ACL2 only considers enabled rules. And ACL2 will use a propositional rule to replace a target only if the target occurs in a propositional place in the formula. Generally that means it occurs in the argument of a propositional connective, like ~c[AND], ~c[OR], ~c[NOT], ~c[IMPLIES], and ~c[IFF], or in the test of an ~c[IF]. When we say that the left-hand side of the rule must ~i[match] the target we mean that we can instantiate the variables in the rule to make the left-hand side identical to the target. To ~i[relieve] or establish the hypotheses of the rule, ACL2 just applies other rewrite rules to try to prove the instantiated hypotheses from the assumptions governing the occurrence of the target. When ACL2 replaces the target, it replaces it with the instantiated right-hand side of the rule and then applies rewrite rules to that. If a hypothesis has variables that do not occur in the left-hand side of the rule, then the pattern matching process won't find values for those variables. We call those ~i[free variables]. They must be instantiated before ACL2 can relieve that hypothesis. To instantiate them, ACL2 has to guess values that would make the hypothesis true in this context, i.e., true given the assumptions of the goal theorem. So if you're trying to prove ~bv[] (IMPLIES (AND (TRUE-LISTP A) (MEMBER (CAR P) A) (MEMBER (CDR P) A)) ...) ~ev[] and the target you're rewriting is in the ``...'' part of the formula, the rewriter knows that ~c[(TRUE-LISTP A)] ~c[(MEMBER (CAR P) A)] and ~c[(MEMBER (CDR P) A)] are true. So if a rewrite rule is considered and the rule has ~c[(member e x)] as a hypothesis, where ~c[e] is a free variable but ~c[x] was bound to ~c[A] in the pattern matching, then it will guess that ~c[e] must be ~c[(CAR P)] or ~c[(CDR P)], even though there are many other possibilities that would make ~c[(MEMBER e A)] true. Of course, whatever guess it makes must also satisfy all the other hypotheses that mention ~c[e] in the rule. It simply isn't very imaginative at guessing! The most predictable rewrite rules have no free variables. You can add pragmatic advice to help ACL2 with free variables, telling it to try all the possibilities it finds, to try just the first, or even to compute a ``creative'' guess. It is possible to make the rewriting process loop forever, e.g., by rewriting ~i[alpha] to ~i[beta] with one set of rules and rewriting ~i[beta] to ~i[alpha] with another. Even a single rule can make the process loop; we'll show you an example of that later in the tutorial. ACL2 can handle commutativity rules without looping. It uses ~c[(equal (+ x y) (+ y x))] to replace ~c[(+ B A)] by ~c[(+ A B)], but not vice versa. (It is sensitive to alphabetic ordering when dealing with ~i[permutative] rules.) Logically equivalent formulas can generate radically different rewrite rules! Rearranging the propositional structure of the formula or swapping the left and right sides of an equality -- while having no effect on the mathematical meaning of a formula -- can have a drastic impact on the pragmatic meaning as a rule. To see an illustration of this, ~pl[equivalent-formulas-different-rewrite-rules]. Developing an effective set of rewrite rules is key to success at using ACL2. We'll look more at this later in the tutorial. If you are working your way through the tutorial for the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover]. If you are reading just about how to make effective rewrite rules, go on to ~il[introduction-to-rewrite-rules-part-2].~/~/") (deflabel introduction-to-the-database :doc ":Doc-Section introduction-to-the-theorem-prover how to update the database~/ We assume you've read ~il[introduction-to-rewrite-rules-part-1] and ~il[introduction-to-key-checkpoints]. The theorem prover's heuristics are influenced by the database of rules and the enabled/disabled status of the rules. You can think of the database as a ~i[global] hint, potentially affecting all parts of a proof attempt. However, in addition to the ``global hint,'' it is possible to give ~i[local] hints that affect the theorem prover's behavior on specific subgoals. We discuss the database here and discuss local hints later in the tutorial. The theorem prover's ``database'' is called the ~i[ACL2 world]. You change the world by issuing commands called ~i[events]. The most common events are ~c[defun] for defining new functions (and predicates) and ~c[defthm] for proving new theorems. Both add rules to the database. Here are some commonly used events. We recommend that upon the first reading of this tutorial you do not follow the links shown below! The links take you into the hypertext reference manual, where it is easy to get lost unless you're looking for detail about one specific feature. ~l[defun] ~warn[] to define a new function or predicate symbol. Definitional axioms are just a kind of ~c[rewrite] rule, but ~c[defun] may also add rules affecting the determination of the type of a term and rules affecting the induction analysis. When you issue a ~c[defun] command you will always supply the ~i[name] of the function or predicate, the list of ~i[formal parameters], ~i[v1,...vn], and the ~i[body]: ~bv[] (defun ~i[name] (~i[v1 ... vn]) ~i[body]) ~ev[] If the event is accepted, a ~i[definitional axiom] is added to the world, ~c[(]~i[name] ~i[v1...vn]~c[)]=~i[body], stored as a special kind of unconditional rewrite rule. However, the ~c[defun] event may require theorems to be proved. Specifically, ~i[measure theorems] must be proved to establish that recursively defined functions always terminate, by proving that an ~i[ordinal] measure of the formal parameters decreases in a ~i[well-founded] way in every recursive call. In addition, if ~i[guards] are being used to declare the ~i[expected domain] of the newly defined function, ~i[guard theorems] might be proved to establish that all functions stay within their expected domains. In any case, you may provide additional information to the ~c[defun] event, coded as part of the ~c[declaration] that Common Lisp allows: ~bv[] (defun ~i[name] (~i[v1 ... vn]) (declare (xargs ...)) ~i[body]) ~ev[] The ~c[xargs] (``extra arguments to ~c[defun]'') entry may specify, among other things, the measure to use in the termination proof, hints for use by the prover during the termination proof, the guard of the new function, and hints for use by the prover during the guard verification step. ~l[defthm] ~warn[] to prove a theorem and to add it as a rule of one or more specified rule-classes. When you issue a ~c[defthm] command you always specify a ~i[name] for the theorem you're trying to prove and a ~i[formula] stating the theorem. You may optionally supply some local hints as we describe later in the tutorial. You may also optionally supply some ~i[rule classes] indicating how you want your formula stored as a rule, after it is proved. We discuss the ~c[defthm] ~i[rule classes] below. ~l[in-theory] ~warn[] to enable or disable rules. Rules have names derived from the names you give to functions and theorems, e.g., ~c[(:REWRITE LEFT-IDENTITY-OF-FOO . 2)] for the second rewrite rule you created from the theorem named ~c[LEFT-IDENTITY-OF-FOO]. Rule names are called ~i[runes]. A ~i[theory] is just a set (list) of runes. The ~i[current theory] is the list of enabled runes and the ~c[in-theory] event can add runes to or delete runes from the current theory. ~l[include-book] ~warn[] to change the world by loading a certified file of other events. The most common use of ~c[include-book] is to load ``community books'' -- books written by other ACL2 users who have released them for distribution to the community. The most common books loaded are probably the arithmetic books: ~bv[] ; * for the most elementary arithmetic, needed for any problem ; that involves even simple addition and multiplication like ; ~c[(+ x (* 2 y) -3)]: (include-book \"arithmetic/top-with-meta\" :dir :system) ; * for more complicated arithmetic involving non-linear terms like ; ~c[(* x y)], ~c[(expt x (+ i j))], and ~c[floor] and ~c[mod] (include-book \"arithmetic-5/top\" :dir :system) ~ev[] But for a complete list of system books, ~pl[books] ~warn[]. ~l[certify-book] ~warn[] to certify a file of events for reuse later. ~l[defconst] ~warn[] to define a new constant, allowing you to write a symbol, e.g., ~c[*weekdays*] in place of some object, e.g., ~c['(MON TUE WED THU FRI)] in formulas. ~l[defmacro] ~warn[] to define a new syntactic abbreviation. The macro facility in Lisp is quite powerful, allowing you to ~i[compute] the form to which some type-in expands. For example, the primitive macro ~c[COND] is defined so that ~c[(COND ((P X) 1)((Q X) 2)(T 3))] expands to ~c[(IF (P X) 1 (IF (Q X) 2 3))]. ~l[defstobj] ~warn[] to introduce a ~i[single-threaded object] that your functions may modify ``destructively'' provided they follow strict syntactic rules. ~l[events] ~warn[] for a complete list of the ACL2 events. There are events to allow mutually recursive definitions, to introduce some new function symbols constrained to satisfy given axioms, to allow the temporary introduction of a ``local'' event to help prove some goal theorem and then disappear, to provide the power of first-order quantification and a choice operator, and many other features. There are also commands that allow you to inspect the world, e.g., to print the command that introduced a given name, to show all the commands back to a certain one, undo the last command or more generally roll-back to an earlier command. ~l[history] ~warn[]. ~b[The Defthm Rule-Classes] We've already discussed the key role that rules play in controlling the behavior of the system. New rules are introduced primiarily with the ~c[defthm] event, though ~c[defun] and other events may introduce rules. To prove ~i[formula] and generate, say a ~c[:rewrite] rule and a ~c[:generalize] rule from it, you would write ~bv[] (defthm ~i[name] ~i[formula] :rule-classes (:rewrite :generalize)) ~ev[] If you wanted to rearrange the shape of the formula before generating the ~c[:rewrite] rule you could provide a ~c[:corollary] modifier to the ~c[:rewrite] rule class: ~bv[] (defthm ~i[name] ~i[formula] :rule-classes ((:rewrite :corollary ...) :generalize)). ~ev[] There are many classes of rules, affecting different parts of the system. Each class is denoted by a keyword, e.g., ~c[:REWRITE], ~c[:LINEAR], etc. You are responsible for specifying the class(es) of rules to be generated from a given formula and several different rules (possibly of different classes) may be derived from a single formula. Each class admits optional modifiers that allow you finer control over each rule. Each class admits the ~c[:corollary] modifier with which you can rearrange the formula before a rule of that class is generated. This allows you to state a theorem in its most elegant form for publication purposes but store it as a rule with the most appropriate hypotheses and conclusion. Other modifiers tend to be specific to certain rule classes, but for example, ~c[:rewrite] rule modifiers include an optional limit on the depth of backchaining and options for handling free variables. We give some links below to other classes of rules. However, we recommend that you not follow these links upon your first reading of this tutorial! ~l[REWRITE] ~warn[] for a description of how to create a rewrite rule. ~l[LINEAR] ~warn[] for a description of how to store theorems concluding with arithmetic inequalities. The trouble with storing ~bv[] (<= (len (delete e x)) (len x)) ~ev[] as a rewrite rule is that it only matches instances of that inequality and thus fails to match ~bv[] (<= (LEN (DELETE E X)) (+ 1 (LEN X))) ~ev[] ACL2 contains an extensible linear arithmetic decision procedure and by storing inequalities as ~c[:linear] rules you can make that decision procedure aware of the basic inequalities between non-primitive numerically valued terms. ~l[EQUIVALENCE] ~warn[], ~pl[CONGRUENCE] ~warn[], and ~pl[REFINEMENT] ~warn[] to learn how to introduce a new equivalence relation to the rewriter. For example, suppose you define ~c[set-equal] so that it returns t precisely if its two arguments are lists containing the same elements, regardless of order or number of occurrences. Note that under this sense of ``equivalence'', ~c[(rev x)] is the identity function and ~c[append] is commutative, for example. ~bv[] (set-equal (rev x) x) (set-equal (append x y) (append y x)) ~ev[] You can make ACL2 use these two theorems as ~c[:rewrite] rules to replace instances of ~c[(REV x)] and ~c[(APPEND x y)] by ~c[set-equal] terms, even though the results are not actually ~c[EQUAL]. This is possible provided the target occurs in a context admitting ~c[set-equal] as a congruence relation. For example, the ~c[:congruence] rule: ~bv[] (implies (set-equal a b) (iff (member e a) (member e b))) ~ev[] gives the rewriter permission to use the above ~c[set-equal] rules as rewrite rules in the second argument of any ~c[member] expression being used in a propositional way. ~l[ELIM] ~warn[] for a description of how to make the system adopt a ``change of variable'' tactic that can trade in ~i[destructor] functions for ~i[constructor] functions. In analogy with how ACL2 eliminates ~c[(CAR X)] and ~c[(CDR X)] by replacing ~c[X] with ~c[(CONS A B)], you can make it eliminate other destructors. For example, the community book ~c[\"arithmetic-5/top\"] provides an ~c[elim] rule that eliminates ~c[(floor x y)] and ~c[(mod x y)] by replacing ~c[x] by ~c[(+ r (* y q))], so that the ~c[floor] expression becomes ~c[q] and the ~c[mod] expression becomes ~c[r]. When introducing your own ~c[elim] rules you will probably also need to introduce ~c[generalize] rules (see below) so that the new variables are appropriately constrained. ~l[GENERALIZE] ~warn[] for a description of how you can make ACL2 restrict the new variables it introduces when generalizing. ACL2 will sometimes replace a term by a new variable and with ~c[generalize] rules you can insure that the new variable symbol has certain properties of the term it replaces. ~l[INDUCTION] ~warn[] for a description of how to tailor the inductions suggested by a term. Most of the time when ACL2 chooses the ``wrong'' induction, the easiest fix is with a local ~c[:induct] hint (see below). But if the same problem arises repeatedly in several theorems, you might want to ``educate'' ACL2's induction heuristic. For a complete list of rule-classes, ~l[rule-classes] ~warn[]. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover].~/~/") (deflabel introduction-to-hints :doc ":Doc-Section introduction-to-the-theorem-prover how to provide hints to the theorem prover~/ We assume you've read ~il[introduction-to-rewrite-rules-part-1], ~il[introduction-to-key-checkpoints], and ~il[introduction-to-the-database]. You may give the theorem prover a hint that is specific to a particular subgoal generated during a proof attempt. Of course, you learn the name of the subgoal by inspecting the key checkpoints or other proof output. You are not expected to anticipate the need for hints at specific subgoals; instead, you usually deduce that a hint is required because the subgoals is not proved but you see that existing rules and context make it provable. The most common hint is to enable and/or disable a particular rule on some particular subgoal. ~bv[] (defthm ~i[name] ~i[formula] :hints ((\"Subgoal *1/3.2''\" :in-theory (disable nth-nthcdr)))) ~ev[] The hint above tells the rewriter that just before it begins to work on ~c[Subgoal *1/3.2''] it should switch to a local theory in which all of the rules generated from the event ~c[nth-nthcdr] are disabled. That local theory remains the one in use for all descendent subgoals generated from the one named, until and unless one of those descendent subgoals has an ~c[:in-theory] hint associated with it. There are many kinds of hints besides ~c[:in-theory] and in general, after each subgoal name, you can give various forms of advice and list various adjustments you wish to make to the context in which the prover is operating when it begins addressing the subgoal named. The top-level goal is always named ~c[Goal]. Thus ~bv[] (defthm ~i[name] ~i[formula] :hints ((\"Goal\" ...~i[hints1]...) (\"Subgoal *1/3.2''\" ...~i[hints2]...))) ~ev[] has the effect of using ~i[hints1] for the top-level goal and all of its children throughout the entire proof, except for ~c[Subgoal *1/3.2''] and its children, where ~i[hints2] is used instead. There are a few hints which ``take effect'' exactly on the subgoal to which they are attached and are not inherited by their descendents. Here is an incomplete list of some of the more common hints; we note the ones that do not pass on their effects to their descendents. We recommend that you not follow the advanced links (marked ``~warn[]'') below until you have read the entire tutorial. ~l[in-theory] ~warn[] for how to enable and/or disable rules. The new theory is computed by a ``theory expression'' (~pl[theories] ~warn[]) such as ~c[(disable nth-nthcdr)] and typically makes adjustments such as additions or deletions to the global current theory. All the relevant new theories are computed before the proof begins. Thus, in ~bv[] (defthm ~i[name] ~i[formula] :hints ((\"Goal\" :in-theory (disable rule1)) (\"Subgoal *1/3.2''\" (disable rule2)))) ~ev[] the theory mentioned for ~c[Goal] is the global current theory minus ~c[rule1], while the theory mentioned for its descendent, ~c[Subgoal *1/3.2''], is the global current theory minus ~c[rule2]. In particular, if both ~c[rule1] and ~c[rule2] are enabled in the global current theory, then ~c[rule1] is enabled during the processing of ~c[Subgoal *1/3.2''] because it was not removed explicitly there. ~l[use] ~warn[] for how to force the theorem prover to take note of particular instances of particular theorems; in particular, the instances are created and added as hypotheses to the subgoal in question. The hint is not inherited by the descendents of the subgoal (since the formula being proved has been modified by the hint). If the rule you are using (with a ~c[:use] hint) is an enabled rewrite rule, it might interfere with the added hypothesis -- by rewriting itself to ~c[T] -- and thus often you will both ~c[:use ...] and ~c[:in-theory (disable ...)]. ~l[expand] ~warn[] for how to tell the theorem prover to expand one or more function calls whenever encountered. ~l[cases] ~warn[] for how to force the theorem prover to do a case split to prove the subgoal under each of an exhaustive list of cases given in the hint. This hint takes action specifically at the named subgoal and is not passed down to its children. ~l[induct] ~warn[] for how to tell the theorem prover to go immediately into induction for the subgoal in question, and to use the induction scheme suggested by the hint rather than the one suggested by the terms in the subgoal itself. This hint is not inherited by its descendents. ~l[hints] ~warn[] for a complete list of all hints, and ~pl[hints-and-the-waterfall] ~warn[] for a more thorough description of how the effects of hints at a subgoal are inherited by the descendents. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover].~/~/") (deflabel introduction-to-a-few-system-considerations :doc ":Doc-Section introduction-to-the-theorem-prover the mechanics of interaction with the theorem prover~/ ACL2 is implemented in Common Lisp. There are many different Common Lisps and they differ in details relating to interacting with the system. We sometimes refer to the host Common Lisp as ``raw Lisp.'' The new user is advised not to operate in raw Lisp as it is possible to, say, redefine ACL2 system faclities like ~c[defthm]. Most people use Emacs (~pl[Emacs] ~warn[]) or the ACL2 Sedan (Eclipse) interface (~pl[ACL2-Sedan] ~warn[]). They provide protection against certain common mistakes, e.g., trying to edit a block of input text after the operating system has buffered it up and sent it to the Lisp reader which is parsing it as you type. More on this below. In addition, the Sedan provides helpful syntax checking and a disciplined approach to the stack of lemmas generated by The Method. But the variety of interfaces to the variety of Lisps mean that there is great variation in what one must type to interact with ACL2. The best example is perhaps trying to interrupt a running proof. If your host Common Lisp is GCL or Allegro and you are typing directly to the Common Lisp process, then you can interrupt a computation by typing ``ctrl-c'' (hold down the Control key and hit the ``c'' key once). But other Lisps may require some other control sequence. If you are typing to an Emacs process which is running the GCL or Allegro Common Lisp process in a shell buffer, you should type ctrl-c ctrl-c. If you are running the ACL2 Sedan, you can use the ~i[Interrupt Session] button on the tool bar. The environment you enter when you interrupt depends on various factors and basically you should endeavor to get back to the top level ACL2 command loop, perhaps by typing some kind of Lisp depenent ``abort'' command like ~c[A] or ~c[:q], or ~c[:abort!]. You can usually determine what environment you're in by paying attention to the prompt, which we discuss below. The ACL2 ``interactive loop'' is called ~ilc[LP] (~warn[]) and is generally invoked automatically from your Common Lisp when you start up the ACL2 process. ~c[LP] is just a special case of an ACL2 function called ~ilc[LD] ~warn[], which the user can call from within the ACL2 interactive loop to enter the loop recursively. New users don't have to know this except that it helps explain why some commands have the string ``~c[-ld-]'' in their names! ACL2 presents itself as a ``read-eval-print'' loop: you're repeatedly prompted for some type-in, which is read, evaluated, and may cause some printing. The prompt tells you something about ACL2's state. In the standard environment, the prompt is ~bv[] ACL2 !> ~ev[] The ``~c[ACL2]'' tells you that the symbols you use in your command are those defined in the standard ACL2 namespace (or, in the jargon of Lisp, the ``current package,'' ~pl[current-package] ~warn[]). You could create a new namespace (~pl[defpkg] ~warn[]) and set the current package to it (~pl[in-package] ~warn[]). The next part of the prompt above (``~c[!]''), the exclamation mark) tells you that before ACL2 evaluates your type-in it will check to see whether ~ilc[guard]s (~warn[]) are respected, i.e., whether the functions used in your command are being supplied arguments in their ``expected domains.'' If evaluation is allowed by the guards, it proceeds exactly according to the ACL2 axioms; if evaluation is not allowed, an error is signaled. ACL2 event commands check their arguments thoroughly at run-time, regardless of Lisp's notion of ``expected domains.'' If the exclamation mark is missing from the prompt, ~bv[] ACL2 > ~ev[] then evaluation occurs strictly according to the ACL2 axioms, without regard for any declared guards. You can switch between these two prompts by typing ~bv[] ACL2 !>:set-guard-checking nil ~ev[] to turn guard checking off and ~bv[] ACL2 >:set-guard-checking t ~ev[] to turn it on. Try typing ~c[(car 7)] to each prompt. If there is a ``~c[p]'' in the prompt, ~bv[] ACL2 p!> ~ev[] with or without the exclamation mark: ~bv[] ACL2 p> ~ev[] it means you are in ~c[:]~ilc[program] (~warn[]) mode rather than ~c[:]~ilc[logic] (~warn[]) mode. In ~c[:program] mode, ~c[defun] just defines Common Lisp programs that you can evaluation but it adds no axioms and you cannot use such defined functions in theorems or invoke ~c[defthm]. ~c[:Program] mode is often used to prototype a model. Most commands are just typical parenthesized Lisp expressions, like ~bv[] ACL2 !>(defthm rev-rev (implies (true-listp x) (equal (rev (rev x)) x))) ~ev[] but some are typed as keywords followed by a certain number of arguments. For example, to undo the last event you may type ~bv[] ACL2 !>:u ~ev[] or to undo back through the introduction of ~c[rev] you may type ~bv[] ACL2 !>:ubt rev ~ev[] The first is equivalent to evaluating ~c[(u)] and the second is equivalent to evaluating ~c[(ubt 'rev)]. ~l[keyword-commands] ~warn[]. So if you see a sentence like ``to turn on the break rewrite facility, execute ~c[:brr t],'' we mean type ~bv[] ACL2 !>:brr t ~ev[] or equivalently ~bv[] ACL2 !>(brr t) ~ev[] If you see a prompt that doesn't look like those above you are probably not typing commands to the standard ACL2 read-eval-print loop! If you've somehow called ~c[LD] recursively, the prompt ``gets deeper,'' e.g., ~bv[] ACL2 !>> ~ev[] and you can pop out one level with ~c[:]~ilc[q] ~warn[] (for ``quit'') or pop to the outermost ACL2 loop with ~c[:]~c[abort!] ~warn[]. If you are in the outermost call of the ACL2 interactive loop and you type ~c[:q], you pop out into raw lisp. The prompt there is generally different from the ACL2 prompt but that is outside our our control and varies from Lisp to Lisp. We have arranged for many (but not all) Lisps to use a raw lisp prompt involving the string ~c[\"~[RAW LISP~]\"]. To get back into the ACL2 interactive loop from raw lisp, evaluate ~c[(LP)]. If you see a prompt that looks like an ACL2 prompt but has a number in front of it, e.g., ~bv[] 1 ACL2 > ~ev[] then you're talking to the break rewrite facility (and you are 1 level deep in the example above). Presumably at earlier time in this session you enabled that facility, with ~c[:brr t], installed a monitor on some rule, invoked the prover, entered the break, and forgot. Everything you have done (e.g., lemmas you might have proved with ~c[defthm]) inside that break will be lost when you exit the break. Since the break rewrite facility is ``ours'' we can tell you how to exit it! To exit our breaks and return to the top-level ACL2 loop, execute ~c[:abort!]. If you discover you've been working in a ~c[brr] break, exit, turn off the break facility wih ~c[:brr nil], and redo whatever ~c[defun]s and ~c[defthm]s you did while in that break. Users of the Emacs interface may occasionally type commands directly in the ~c[*shell*] buffer running ACL2. This can be ``dangerous'' for two reasons. One is that if you type an event, like a ~c[defun] or ~c[defthm], directly to the shell, it will not be recorded in your ``script'' buffer and you may forget it in your final script. The other is that if you attempt to edit a multi-line command on any but the most recent line, e.g., to correct the spelling of ~c[defthm] below after you've typed the ``~c[(implies (true-listp x)]'' you will confuse the Lisp parser because it has already read ``~c[(defth rev-rev]''. ~bv[] ACL2 !>(defth rev-rev (implies (true-listp x) ~ev[] This usually provokes the raw Lisp to enter a low level error break from which you must abort, possibly reenter the ACL2 loop, and re-type the corrected command from scratch. Another common mistake when using interfaces other than the ACL2 Sedan is to type an ill-formed ACL2 expression containing dots or commas, which also often provokes a break into the raw Lisp's error handler. The fundamental lesson is that you should pay attention to the prompt and learn what the different prompts mean -- or use the ACL2 Sedan. If you have been working your way through the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover].~/~/") (deflabel architecture-of-the-prover :doc ":Doc-Section introduction-to-the-theorem-prover a simple overview of how the prover works~/ Six built-in proof techniques are used by ACL2 to decompose the goal formula into subgoals. ~i[simplification] -- decision procedures and rewriting with previously proved rules, but actually including a host of other techniques under your control. Simplification is the only proof technique that can reduce a formula to 0 subgoals (i.e., prove it) rather than just transform it to other formulas. The predominant activity in most proofs is simplification. There are many ways you can affect what the simplifier does to your formulas. Good users spend most of their time thinking about how to control the simplifier. ~i[destructor elimination] -- getting rid of ``destructor terms'' like ~c[(CAR X)] and ~c[(CDR X)] by replacing a variable, e.g., ~c[X], by a ``constructor'' term, e.g., ~c[(CONS A B)]. But you can tell ACL2 about new destructor/constructor combinations. ~i[cross-fertilization] -- using an equivalence hypothesis by substituting one side for the other into the conclusion ~i[and then throwing the hypothesis away]. This is a heuristic that helps use an inductive hypothesis and prepare for another induction. ~i[generalization] -- replacing a term by a new variable and restricting the new variable to have some of the properties of the term. You can control the restrictions imposed on the new variable. This is a heuristic that prepares the goal for another induction. ~i[elimination of irrelevance] -- throwing away unnecessary hypotheses. This is a heuristic that prepares the goal for another induction. ~i[induction] -- selecting an induction scheme to prove a formula. Inductions are ``suggested'' by the recursive functions appearing in the formula. But you can control what inductions are suggested by terms. But you can add additional techniques, called ~i[clause processors]. The various techniques are tried in turn, with simplification first and induction last. Each technique reports one of three outcomes: it found nothing to change (i.e., the technique doesn't ~i[apply] to that subgoal), it decided to abort the proof attempt (typically because there is reason to believe the proof is failing), or it decomposed the goal into ~i[k] subgoals. The last outcome has a special case: if ~i[k] is 0 then the technique proved the goal. Whenever ~i[k] is non-0, the process starts over again with simplification on each of the ~i[k] subgoals. However, it saves up all the subgoals for which induction is the only proof technique left to try. That way you see how it performs on every base case and induction step of one induction before it launches into another induction. It runs until you or one of the proof techniques aborts the proof attempt or until all subgoals have been proved. Note that if simplification produces a subgoal, that subgoal is re-simplified. This process continues until the subgoal cannot be simplified further. Only then is the next proof technique is tried. Such suboals are said to be ~i[stable under simplification]. While this is happening, the prover prints an English narrative describing the process. Basically, after each goal is printed, the system prints an English paragraph that names the next applicable proof technique, gives a brief description of what that technique does to the subgoal, and says how many new subgoals are produced. Then each subgoal is dealt with in turn. If the proof is successful, you could read this log as a proof of the conjecture. But output from successful proofs is generally never read because it is not important to The Method described in ~il[introduction-to-the-theorem-prover]. The output of an unsuccessful proof attempt concludes with some ~i[key] ~i[checkpoints] which usually bear looking at. ~/~/") (deflabel frequently-asked-questions-by-newcomers :doc ":Doc-Section introduction-to-the-theorem-prover some questions newcomers frequently ask~/ This FAQ is for people who've read through all the other sections of the tutorial introduction to the theorem prover (~pl[introduction-to-the-theorem-prover] and all the links from it that are not marked with the little warning sign (``~warn[]''). Do not expect to understand our answers if you haven't taken the time to read through the tutorial. In the answers below you will see more links into the hypertext reference manual. While such links were marked ``~warn[]'' in the tutorial, they are not marked that way here. When you enter the reference manual be prepared to explore and assemble a mini-course on the topic of interest, not a quick fix. ~b[Q]. How do I find something in the ~b[ACL2 documentation]? ~b[A]. Try going to the Search link on the ACL2 home page. ~b[Q]. How does the theorem prover work? ~b[A]. We really don't think you need to know much about the inner workings of the prover to become an effective user. That doesn't mean the system is self-explanatory! It means that stuff you need to learn is not how the theorem prover works but how to interact with it! That is what ~il[introduction-to-the-theorem-prover] is about. However, if you want the most basic overview of the prover, ~pl[architecture-of-the-prover]. ~b[Q]. How do I ~b[define a new function]? ~b[A]. ~l[defun]. ~b[Q]. How do I ~b[define a new predicate]? ~b[A]. ~l[defun]. ~b[Q]. How do I ~b[define a new relation]? ~b[A]. ~l[defun]. ~b[Q]. How do I define a ~b[function or predicate that takes a varying number of arguments]? ~b[A]. You can't. However, ~pl[defmacro] to learn how to define a macro that takes a varying number of arguments and expands into an arbitrary term that you compute. ~b[Q]. How do I define a ~b[macro that is sensitive to the state]? ~b[A]. You can't. However, advanced users should consider ~ilc[make-event]. ~b[Q]. How do I define ~b[mutually recursive] functions? ~b[A]. ~l[mutual-recursion]. However, you should realize that when two functions, say ~c[f] and ~c[g], are mutually recursive, properties of ~c[f] generally have to be stated simultaneously with properties of ~c[g], since inductive proofs about one function require inductive hypotheses about the other. Furthermore, ACL2 does not know how to do inductions for mutually recursive functions and must be told. ~l[mutual-recursion-proof-example]. ~b[Q]. How do I declare the ~b[type signature of a function]? ~b[A]. You can't. ACL2 is a syntactically untyped language and all functions are defined on all objects in the ACL2 universe. We recommend that the new user get used to this and only then explore the use of ACL2 to express and enforce a type regime. In ACL2, the ~i[guard] of a function is akin to the type signature of a function in typed languages. However, ACL2 guards may be arbitrary terms, not just type conditions, they only concern the inputs to the function (not its result), and do not affect the axiom defining the function -- all functions are defined on every combination of objects. You may, of course, prove theorems that establish that every function called in a definition or theorem is supplied with input satisfying its guards (which necessarily involves describe the outputs too). These formulas are called ~i[guard conjectures] and the process of proving them is called ~i[guard verification]. Since guards are arbitrary ACL2 formulas, the ``type regimes'' one tends to enforce in ACL2 can be much for flexible and expressive than those in most programming languages. However, that expressibility also means guard verification can be challenging (indeed undecidable). On the other hand, if one limits oneself to simple type-like guards, lemmas can be proved that make most guard verification fully automatic and one can configure ACL2 to do guard verification automatically at ~c[defun]-time. One may also delay guard verification until ``the right'' lemmas have been proved. By doing guard verification one can make functions execute faster by allowing the code to avoid runtime checks. This is especially valuable to industrial users who have large models used both in verification and as simulation engines for designed artifacts. In addition, guard verification can give you the assurance that you are using your functions within their intended domains and hence is a form of functional specification and verification. However, all these advantages aside, it is remarkably easy to drift away from the simplest type regimes and write a guard that raises deep mathematical problems. New users should not try to confront these problems until they are comfortable with the theorem prover and with lemma development. Therefore, we ~b[strongly] recommend that you forget about types and guards and get used to reasoning about total functions. When you do decide to learn about them, be prepared for a complex story involving specification, execution efficiency, and proof management. ~l[guard]. ~b[Q]. How do I tell ~c[defun] ~b[what measure to use]? ~b[A]. ~l[xargs], specifically ~c[:measure]. ~b[Q]. I specified a measure that always returns a natural number but ACL2 is acting like it's not a natural number. ~b[A]. There are two likely problems. The most likely one is that your measure isn't really always a natural! Suppose the formals of your ~c[defun] are ~c[x] and ~c[y] and your measure is ~c[(m x y)]. Suppose the recursive calls of your function are protected by tests that insure that ~c[x] and ~c[y] are naturals. Then you might assume ~c[x] and ~c[y] are naturals in the measure. But ACL2 has to prove ~c[(o-p (m x y))], where ~ilc[o-p] is the predicate that recognizes ordinals (and naturals are ordinals). Note that the theorem doesn't have any hypotheses! You might intuitively think that your measure has to be an ordinal just under the conditions that lead to recursive calls. That's not what ACL2 enforces. It has to be an ordinal, always. So you must change your specified measure. For example, consider wrapping ~ilc[nfix] around it or around its uses of ~c[x] and ~c[y] to coerce those quantities to naturals. The second most likely explanation is that your measure returns a natural, always, but ACL2 doesn't know that and it takes induction to prove. This might happen if ~c[m] involves some recursive functions. In this case, prove ~c[(natp (m x y))] before your ~c[defun]. Perhaps you should consider making the ~c[natp] lemma a ~c[:]~ilc[type-prescription] lemma to make ACL2's typing algorithm aware of it. ~b[Q]. How do I tell ~c[defun] ~b[what well-founded relation to use]? ~b[A]. ~l[xargs], specifically ~c[:well-founded-relation]. ~b[Q]. How do I show that a ~b[relation is well-founded]? ~b[A]. Prove a theorem establishing that there is an order preserving embedding into the ordinals and store it with ~c[:rule-classes] ~c[:]~ilc[well-founded-relation]. ~b[Q]. What is an ~b[ordinal]? What does it mean to be ~b[well-founded]? ~b[A]. Ordinals are an extension of the natural numbers used to insure that a process can't go on forever. Like naturals, they can be added, multiplied, and exponentiated. There is a sense of one ordinal being less than another. Unlike the naturals, each of which is finite, the ordinals include infinite objects. Now imagine ``standing'' on an ordinal and ``stepping'' to a smaller one. Like the naturals, this ``walk down the ordinals'' can't go on forever, even if you start on an infinite ordinal. That is because the ordinals are ~i[well-founded]. See ~ilc[o-p] for more information about ordinals in ACL2 and about well-foundedness. ~l[ordinals] for a deeper discussion and a discussion of books that can help configure ACL2 to reason about ordinals. ~b[Q]. How can provide ~b[hints for the termination proofs] in ~c[defun]? ~b[A]. ~l[xargs], specifically ~c[:hints] (for the termination proofs) and ~c[:guard-hints] (for the guard verification proofs). ~b[Q]. How do I define a ~b[constant] (something like a ~b[global variable])? ~b[A]. ~l[defconst]. But remember that as an applicative programming language, ACL2 does not have global variables! You can define a symbol to have a fixed value and use the symbol sort of like a global variable in function definitions: you may refer to the value of the symbol in your functions without passing the variable in as formal parameter. But you may not ever change the value of the symbol! ~b[Q]. How do I save the value of a top-level computation for future use? ~b[A]. ~l[assign] and ~pl[@]. ~b[Q]. How do I introduce ~b[new syntactic form] or ~b[abbreviation]? ~b[A]. ~l[defmacro]. ~b[Q]. How can create and modify an array? ~b[A]. ACL2 is a functional language, so it is impossible to destructively modify an existing object; technically, all ``updates'' to objects must be implemented by ``copy-on-write'' semantics. That said, ACL2 provides support for ~il[arrays], provided you use them in a restricted way. They give you constant-time access and change under the use restrictions. ~b[Q]. How do I read from or write to a file? How do I do IO? ~b[A]. To manipulate files, your function must have ~ilc[state] as an argument, so you should read about the restrictions that imposes. For input/output facilities, ~pl[io]. ~b[Q]. How do I define a ~b[structure that can be destructively modified]? ~b[A]. ACL2 is an ~i[applicative programming language]. You can't modify objects arbitrarily! You basically have to ``copy on write,'' which means you construct new objects from old ones, making the changes you want in the new one. If the ~c[car] of some object is ~c[1] at one moment and ~c[2] later, then the basic logical axiom ~c[(car x)] = ~c[(car x)] is violated! However, if the only reference to the old object, e.g., ~i[x], was to pass it to the code that copied and ``changed'' it, then ACL2 can re-use the old object to produce the new one and the axioms would not object. Such syntactic restrictions can make ~i[x] a modifiable structure but they will impose a heavy burden on you as a programmer: if pass such an ~i[x] to a function and the function modifies it, then you must pass ~i[x] only to that function and you must return the modified value and use it henceforth. Such objects are said to be ~i[single threaded]. ~l[defstobj]. ~b[Q]. How do I write a universal quantifier? An existential quantifier? How can I say ``for all'' or ``there exists''? ~b[A] You can't literally write quantifiers. But ACL2 has the power of full first order logic with quantification. ~l[quantifiers]. ~b[Q]. How do I introduce an undefined or uninterpreted function symbol? Can I constrain it to have certain properties? ~b[A]. ~l[encapsulate]. ~b[Q]. How can I hide a lemma? I want to prove a lemma temporarily to use in another proof but I don't want the lemma around thereafter. ~b[A]. One way to get a similar effect is to prove the lemma and then disable it with an ~c[(in-theory (disable ...))] event; ~pl[in-theory]. Another way is to put the lemma and the theorem that needs it into an ~ilc[encapsulate] and wrap a ~ilc[local] around the lemma. ~b[Q]. What is an ~b[event]? ~b[A]. An ~i[event] is a command that adds information to the ACL2 database (the ``logical world''), like ~c[defun] or ~c[defthm]. ~l[events]. ~b[Q]. How do I say that I ~b[do not want a rewrite rule] generated from a theorem? ~b[A]. The command ~bv[] (defthm ~i[name] ~i[formula] :rule-classes nil) ~ev[] will attempt to prove ~i[formula] and, if successful, give ~i[formula] the name ~i[name], which you may use in hints as a theorem, but it will build no rules from the formula. ~b[Q]. How do I say that I want a formula to be ~b[stored as a rewrite rule]? ~b[A]. The command ~bv[] (defthm ~i[name] ~i[formula]) ~ev[] will attempt to prove ~i[formula] and, if successful, it will give ~i[formula] the name ~i[name] and generate a rewrite rule from it, with the runic name ~c[(:rewrite ]~i[name])]. It could happen that ~i[formula] generates more than one rewrite rule, e.g., this happens if the conclusion is an ~c[AND]. In this case, each conjunctive branch through the conclusion generates a rule named ~c[(:rewrite ]~i[name]~c[ . i)], for ~i[i]=1,2,... For more details, ~pl[rewrite]. ~b[Q]. How do I say that I want a formula to be ~b[stored as a rewrite rule] ~b[and some other kinds of rules]? ~b[A]. The command ~bv[] (defthm ~i[name] ~i[formula] :rule-classes (~i[:class1] ... ~i[classk])) ~ev[] will attempt to prove ~i[formula] and, if successful, it will give ~i[formula] the name ~i[name] and generate a rule of each ~i[:classi] specified. Each ~i[:classi] should either be a keyword, like ~c[:REWRITE] or ~c[:GENERALIZE], naming a rule class (~pl[rule-classes]), or else should be a list that starts with a rule class and then lists the relevant modifiers. Be sure to include ~c[:REWRITE] among the rule classes if you want the formula to generate a rewrite rule. It doesn't do that automatically if you explicitly specify ~c[:rule-classes]! ~b[Q]. How do I ~b[rearrange] the shape of a formula before generating a rule from it? ~b[A]. ~l[rule-classes] and read about the ~c[:corollary] modifier. ~b[Q]. What is a ~b[type-prescription]? ~b[A]. ACL2 has an algorithm for determining the type of object returned by a term, where a type is one of the Common Lisp primitive datatypes such as natural, integer, Boolean, cons, true-listp, etc. Rules provided by you can influence this algorithm. ~l[type-prescription]. ~b[Q]. How do ~b[rewrite rules work]? ~b[A]. Re-read the tutorial sections: ~il[introduction-to-rewrite-rules-part-1] and ~il[introduction-to-rewrite-rules-part-2]. ~b[Q]. How do I ~b[see what's in the database]? ~b[A]. You can't look at the entire database with user-friendly tools. You can print the command that introduced a particular name, print the entire sequence of user commands, print the commands in the region between two commands, print all the rewrite rules that apply to a given term or function symbol, and many other options. ~l[history]. If you have loaded a book from another user, you might wish to read the source file. For example, the source file for ~c[(include-book \"arithmetic-5/top\" :dir :system)] is the file named ~c[arithmetic-5/top.lisp] on the ~c[acl2-sources/books/] directory where ever your ACL2 sources are installed. Often, books are well-documented by comments by their authors. Some books have ~c[Readme] or ~c[README] files on the same directory. ~b[Q]. How do I ~b[undo] a command? ~b[A]. ~l[history], especially ~pl[u] (``undo'') and ~pl[ubt] (``undo back through''). ~b[Q]. What ~b[rewrite rules match] a given term? ~b[A]. ~l[pl]. ~b[Q]. What were those ~b[questions to ask when looking at key checkpoints]? ~b[A]. ~l[introduction-to-key-checkpoints]. ~b[Q]. How do I figure out ~b[why a rewrite rule won't fire]? ~b[A]. If you activate rewrite rule monitoring (~pl[brr]) and then install a monitor (~pl[monitor]) on the rule in question, ACL2 will enter an interactive break whenever the pattern of the rule is matched against a target in a proof. So after installing the monitor, re-try the proof and then interact with the rewriter via break commands (~pl[brr-commands]). Like all trace and break packages, you have to know what you're doing to use the break rewrite facility, so read the material in the reference manual. If no interactive break happens after you've installed the monitor on your rule and re-tried the proof, it means no suitable target ever arose. Don't forget to turn off monitoring when you're done as it slows down the system. ~b[Q]. ~b[Why is a proof taking so long?] ~b[A]. Unexpectedly poor performance on simple problems is usually a sign of cyclic rewriting or combinatoric explosion. ~l[dmr] and ~pl[accumulated-persistence]. ~b[Q]. How do I tell ACL2 ~b[what induction to do] for a particular formula? ~b[A]. When issuing the ~c[defthm] command for the formula, supply an ~c[:induct] hint: ~bv[] (defthm ~i[name] ~i[formula] :hints ((\"Goal\" :induct (f x1 ... xn)))) ~ev[] where ~c[f] is a function that recurs the way you want the induction to unfold and ~c[x1 ... xn] are the relevant variables in ~i[formula]. You usually have to define ~c[f] appropriately for each formula that needs an induct hint, though sometimes you can re-use an earlier ~c[f] or one of the functions in the formula itself. It doesn't matter what value ~c[(f x1 ... xn)] returns. All that matters is how it recurs. The termination analysis for ~c[f] justifies the induction done. ~l[hints], especially the section on ~c[:induct] hints; also ~pl[induction]. ~b[Q]. ACL2 doesn't know ~b[simple arithmetic] that can simplify the term ~c[(+ 1 -1 x)]. ~b[A]. You should load an arithmetic book whenever you're dealing with an arithmetic problem. The simplest arithmetic book is typically loaded with the event ~c[(include-book \"arithmetic/top-with-meta\" :dir :system)]. If you're using ~c[floor] and ~c[mod] or non-linear arithmetic like ~c[(* x y)] you should use ~c[(include-book \"arithmetic-5/top\" :dir :system)]. See also the discussion of arithmetic books under the ``Lemma Libraries and Utilities'' link of the ACL2 home page, and ~pl[community-books]. ~b[Q]. ACL2 is not using an ~b[arithmetic lemma] that I proved. ~b[A]. Lemmas concluding with arithmetic inequalities, like ~bv[] (implies (member e x) (< (len (delete e x)) (len x))) ~ev[] are not good rewrite rules because they rarely match targets because of intervening arithmetic operators. For example, the above conclusion doesn't match ~c[(< (LEN (DELETE E X)) (+ 1 (LEN X)))]. You should store such lemmas as ~c[:linear] rules by using the command: ~bv[] (defthm len-delete (implies (member e x) (< (len (delete e x)) (len x))) :rule-classes :linear) ~ev[] ~l[linear]. ~b[Q]. What is a ~b[linear rule]? ~b[A]. ~l[linear]. ~b[Q]. How do I make ACL2 ~b[treat a relation as an equality]? ~b[A]. We assume you mean to treat the relation as an equivalence, i.e., replace one term by another when they are equivalent under your relation. ~l[equivalence], ~pl[congruence], and ~pl[refinement]. ~b[Q]. One of my rewrite rules has a ~b[hypothesis that doesn't rewrite] to true. What do I do? ~b[A]. Prove a rewrite rule that establishes that hypothesis under the relevant assumptions from the context of the intended target. Alternatively, undo the rule and restate it with a ~ilc[force] around the problematic hypothesis, making ACL2 assume the hypothesis when the rule is applied and raising the truth of the hypothesis as an explicit subgoal of the proof. See also ~ilc[case-split]. Of course, you should always state the strongest rewrite rules you can think of, eliminating hypotheses or shifting them to the right-hand side inside of ~c[IF]s; ~pl[strong-rewrite-rules]. ~b[Q]. How do I make ACL2 ``guess'' the ~b[right instantiation of a free variable]? ~b[A]. You can provide a ~c[:restrict] hint that names the problematic lemma and provides an instantiation of the free variable. ~l[hints], specifically ~c[:restrict]. You could alternatively give a hint that ~c[:uses] the rule and provides the appropriate instantiation in full. ~l[hints], specifically ~c[:use]. Recall that ACL2 guesses free variable instantiations by matching the problematic hypothesis to the assumptions in the context of the target. If the appropriate assumption is present but ACL2 is finding another one, try undoing the problematic rule and proving it again, specifying the ~c[:match-free :all] modifier of the ~c[:rewrite] or ~c[:linear] rule class. ~l[rule-classes]. Alternatively, undo and prove the problematic rule again and use a ~ilc[bind-free] form to compute the appropriate instantiation. ~b[Q]. How can I make ACL2 do a ~b[case split] to prove a certain subgoal? ~b[A]. ~l[hints], specifically ~c[:cases]. ~b[Q]. How can I ~b[prevent ACL2 from using a rewrite rule]? ~b[A]. ~l[hints], specifically ~c[:in-theory (disable ...)]. If the use of the rule is problematic in only one subgoal and the lemma is needed in other subgoals, disable the lemma only in the problematic subgoal by specifying the subgoal name (e.g., ~c[\"Subgoal 1/3.2.1\"]) as the goal specifier in the hint. If the rule isn't needed anywhere in the proof, you could use the specifier ~c[\"Goal\"]. If you don't think the rule will ever be needed for a while, you could globally disable it with the event ~c[(in-theory (disable ...))] (~pl[in-theory]) executed before the first problematic proof. If the rule has never been used or must always be disabled, undo it and prove it again with ~c[:]~ilc[rule-classes] ~c[nil]. ~b[Q]. How can I prevent ACL2 from running a definition on constants? I tried disabling the function but that didn't work. ~b[A]. If you have a function named ~c[f] then disabling ~c[f] will disable the definitional axiom about ~c[f]. But ACL2 has another kind of rule about ~c[f], telling it how to evaluate ~c[f]. The ~il[rune] of this rule is ~c[(:executable-counterpart f)]. Try disabling that, as in the ~c[:]~ilc[hints] ~c[((]... ~c[:in-theory (disable (:executable-counterpart f)) ...~c[))]. ~b[Q]. How can I make ACL2 ~b[use a rule] in a proof? ~b[A]. ~l[hints], specifically ~c[:use]. ~b[Q]. How can I make ACL2 expand a function call in a proof? ~b[A]. You can give an ~c[:]~l[expand] hint. ~b[Q]. ACL2 sometimes aborts the proof attempt before showing me all of the subgoals. How can I make it just keep going instead of aborting early? ~b[A]. ~l[otf-flg], which stands for Onward Thru the Fog FLaG. ~b[Q]. How can I ~b[compute when I want a rule to fire]? ~b[A]. ~l[syntaxp]. ~b[Q]. How can I add ~b[pragmatic advice to a lemma after it has been proved]? For example, how can add a forced hypothesis, a backchain limit, or a ~c[syntaxp] test? ~b[A]. You can't. You can undo the lemma, restate it appropriately, and prove it again. This produces the cleanest database. Alternatively, you can prove the restated lemma under a new name -- a task that should be easy since the old version is in the database and will presumably make the proof trivial -- and then disable the old name. ~b[Q]. How can I ~b[stop ACL2 from rewriting a term]? ~b[A]. If you need rewriting done but want to prevent some particular terms from being rewritten, ~pl[hints], specifically ~c[:hands-off]. Alternatively, consider embedding the problematic term in a ~ilc[hide]. Users sometime develop special theories (~pl[theory]) containing just the rules they want and then use hints to switch to those theories on the appropriate subgoals. ~b[Q]. Can I ~b[compute which subgoals a hint refers to]? ~b[A]. Yes, ~pl[computed-hints]. This topic is for advanced users but knowing that it is possible might come in handy someday. ~b[Q]. I want the rewriter to ~b[always use one theory and then switch to another] so that it doesn't use my most complicated before until doing the simple things. ~b[A]. This is possible but is something for the advanced user. It can be done using a form of ~il[computed-hints]. ~l[using-computed-hints-7]. ~b[Q]. Is there a way to attach ~b[the same hint to every defthm]? ~b[A]. ~l[default-hints]. ~b[Q]. How can I just tell ACL2 the proof steps? ~b[A]. ~l[verify] and ~pl[proof-checker]. ~b[Q]. How can I write ~b[my own simplifier]? ~b[A]. ~l[meta]. ~b[Q]. How can I add an axiom or just assume some lemma without proof? ~b[A]. This is very dangerous but is a good strategy for exploring whether or not a certain set of lemmas (and their rules) is sufficient to prove your goal. ~l[defaxiom] and ~pl[skip-proofs]. ~b[Q]. How can redefine a user-defined function? ~b[A]. This is tricky. What if you've already proved theorems about the old definition and then wish to change it? There are several options. ~l[ld-redefinition-action] (and note specifically the discussion of updater function for it, ~c[set-ld-redefinition-action]); also ~pl[redef], ~pl[redef!], ~pl[redef+], and ~pl[redef-]. ~b[Q]. How do I ~b[change a function from] ~c[:program] ~b[mode to] ~c[:logic] ~b[mode]? ~b[A]. ~l[verify-termination]. ~b[Q]. How do I ~b[change the guards] on a function? ~b[A]. You can't. Undo it and redefine it. ~b[Q]. What is ~b[program mode]? ~b[A]. ~l[program]. ~b[Q]. What does the ACL2 ~b[prompt] mean? ~b[A]. ~l[introduction-to-a-few-system-considerations] or, specifically, ~pl[prompt]. ~b[Q]. What is ~b[logic mode]? ~b[A]. ~l[logic]. ~b[Q]. How do I ~b[get into or out of] ~c[:program] ~b[mode?] ~c[:Logic] ~b[mode?] ~b[A]. ~l[program] and ~pl[logic]. You can enter these modes temporarily for a particular ~ilc[defun] by using ~c[(declare (xargs :mode :program))] or ~c[(declare (xargs :mode :logic))] after the list of formal parameters to the definition. ~b[Q]. How do I quit from ACL2? ~b[A]. This varies depending on the interface you're using. ~l[introduction-to-a-few-system-considerations]. ~b[Q]. How do I ~b[load a file] of definitions and theorems created by someone else? ~b[A]. ~l[include-book]. ~b[Q]. How do I ~b[create my own book] of definitions and theorems? ~b[A]. ~l[books]. ~b[Q]. Where are the books referenced by ~b[:dir :system] on my machine? ~b[A]. If your ACL2 is installed on the directory ~i[dir]~c[/acl2-sources] and you follow the standard installation instructions, then the books are typically the files under the directory ~i[dir]~c[/acl2-sources/books/]. ~b[Q]. How can I find out ~b[what books are available]? ~b[A]. Go to the ACL2 home page, ~c[http://www.cs.utexas.edu/u/moore/acl2/] and look at the link labeled ``Lemma Libraries and Utilities.'' ~b[Q]. How do I ~b[produce my own book]? ~b[A]. ~l[books]. ~b[Q]. What is a ~b[decision procedure]? What ~b[decision procedures does ACL2 have]? ~b[A]. A ~i[decision procedure] is an algorithm that given enough time and space can determine, for all the formulas in a certain syntactic class of formulas, whether the formula is a theorem or not. The most well-known decision procedure is for propositional calculus: by testing a formula under all the combinations Boolean assignments to the variables, one can decide if a propositional formula is a theorem. The syntactic class consists of all formulas you can write using just variables, constants, and compositions of the functions ~c[and], ~c[or], ~c[not], ~c[implies], ~c[iff], and ~c[if]. There are, of course, an exponential number of different assignments so the algorithm can be slow. ACL2 contains a propositional decision procedure based on symbolic normalization that can be faster than trying all the combinations of truthvalues -- but is not guaranteed to be faster. ACL2 also contains an optional ~il[bdd] procedure. ACL2 also contains a decision procedure for rational arithmetic involving variables, rational constants, addition, multiplication by rational constants, equality, and the various versions of arithmetic inequality (~c[<], ~c[<=], ~c[>=], and ~c[>]). It can be extended with ~c[:]~ilc[linear] lemmas. ACL2 is complete for equality over uninterpreted (e.g., undefined and unconstrained) function symbols using an algorithm based on transitive closure of equivalence classes under functional substitutivity. Finally, you can make ACL2 use other decision procedures, even ones implemented outside of ACL2; ~pl[clause-processor]. ~b[Q]. ACL2 has the ~b[change of variable] trick (~b[destructor elimination]) that it does to get rid of ~c[(CAR X)] and ~c[(CDR X)] by replacing ~c[x] by ~c[(CONS A B)]. Is there a way to make ACL2 do that for other terms? ~b[A]. Yes. ~l[elim]. ~b[Q]. How can I ~b[prevent destructor elimination]? ~b[A]. ~l[hints], specifically ~c[:do-not '(eliminate-destructors)]. ~b[Q]. How can I ~b[prevent cross-fertilization]? ~b[A]. ~l[hints], specifically ~c[:do-not '(fertilize)]. ~b[Q]. How can I ~b[prevent generalization]? ~b[A]. ~l[hints], specifically ~c[:do-not '(generalize)]. ~b[Q]. How can I make ACL2 ~b[impose a restriction on a new variable] introduced by destructor elimination or generalization? ~b[A]. ~l[generalize]. ~b[Q]. What ~b[rule classes] are there? ~b[A]. ~l[rule-classes]. ~b[Q]. What is a ~b[theory]? ~b[A]. ~l[theories]. ~b[Q]. How are ~b[hints inherited by the children of a subgoal]? ~b[A]. ~l[hints-and-the-waterfall]. ~b[Q]. How do I use ACL2 under ~b[Emacs]? ~b[A]. ~l[emacs]. ~b[Q]. How do I use ACL2 under ~b[Eclipse]? ~b[A]. ~l[ACL2-Sedan]. ~b[Q]. How do I interrupt the prover? ~b[A]. The keyboard sequence for interrupting a running process depends your operating system, host Common Lisp, and user interface (e.g., Emacs, Eclipse, etc.). But perhaps a few examples will help you discover what you need to know. If your host Common Lisp is GCL or Allegro and you are typing directly to the Common Lisp process, then you can interrupt a computation by typing ``ctrl-c'' (hold down the Control key and hit the ``c'' key once). But other Lisps may require some other control sequence. If you are typing to an Emacs process which is running the GCL or Allegro Common Lisp process in a shell buffer, you should type ctrl-c ctrl-c -- that is, you have to type the previously mentioned sequence twice in succession. If you are running the ACL2 Sedan, you can use the ~i[Interrupt Session] button on the tool bar. The environment you enter when you interrupt depends on various factors and basically you should endeavor to get back to the top level ACL2 command loop, perhaps by typing some kind of Lisp depenent ``abort'' command like ~c[A] or ~c[:q], or ~c[:abort!]. You can usually determine what environment you're in by paying attention to the prompt. ~b[Q]. What is the ~b[ACL2 loop]? ~b[A]. That is the name given to the interactive environment ACL2 provides, a ``read-eval-print loop'' in which the user submits successive commands by typing ACL2 expressions and keyword commands. ~l[introduction-to-a-few-system-considerations]. ~b[Q]. What is ~b[raw lisp]? ~b[A]. That is our name for the host Common Lisp in which ACL2 is implemented. ~l[introduction-to-a-few-system-considerations]. There is an ACL2 mode named ~i[raw mode] which is different from ``raw lisp.'' ~l[set-raw-mode]. ~b[Q]. Can I get a tree-like view of a proof? ~b[A]. ~l[proof-tree] for an Emacs utility that displays proof trees and allows you to navigate through a proof from the proof tree. The ACL2 Sedan also supports proof trees and you should see the ACL2s documention on that topic. ~b[Q]. I used the earlier Boyer-Moore theorem prover, Nqthm. How is ACL2 different? ~b[A]. ~l[nqthm-to-acl2]. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover].~/~/") (deflabel introductory-challenges :doc ":Doc-Section introduction-to-the-theorem-prover challenge problems for the new ACL2 user~/ Do each of the problems. In each case, start with a fresh ACL2 (or undo all effects of previous events with ~c[:ubt! 1]). This may require that you ``re-discover'' the same lemma more than once in different problems, but recognizing the need for something you used in some previous project is part of the training. We recommend that you follow The Method and consult the documentation as needed -- but that you not look at our answers until you're well and truly baffled! ~l[introductory-challenge-problem-1] (Answer: ~il[introductory-challenge-problem-1-answer]) ~l[introductory-challenge-problem-2] (Answer: ~il[introductory-challenge-problem-2-answer]) ~l[introductory-challenge-problem-3] (Answer: ~il[introductory-challenge-problem-3-answer]) ~l[introductory-challenge-problem-4] (Answer: ~il[introductory-challenge-problem-4-answer]) In addition to these explicit challenge problems designed for beginners, the ACL2 documentation has many example solutions to problems (not always phrased in the question/answer format here). If you are looking for other examples, you should consider ~il[annotated-acl2-scripts] (Answer: the answers are given in the examples) When you've done the problems and compared your solutions to ours, use your browser's ~b[Back Button] now to return to ~il[introduction-to-the-theorem-prover]. ~/~/") (deflabel introductory-challenge-problem-1 :doc ":Doc-Section introduction-to-the-theorem-prover challenge problem 1 for the new user of ACL2~/ Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing ~bv[] :ubt! 1 ~ev[] which will undo everything since the first user event. Then define this function: ~bv[] (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) ~ev[] Then use The Method to prove: ~bv[] (defthm triple-rev (equal (rev (rev (rev x))) (rev x))) ~ev[] When you've solved this problem, compare your answer to ours; ~pl[introductory-challenge-problem-1-answer]. Then, use your browser's ~b[Back Button] to return to ~il[introductory-challenges]. ~/~/") (deflabel introductory-challenge-problem-1-answer :doc ":Doc-Section introduction-to-the-theorem-prover answer to challenge problem 1 for the new user of ACL2~/ This answer is in the form of an ACL2 script sufficient to lead ACL2 to a proof. ~bv[] (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) ; Trying ~c[triple-rev] at this point produces a key checkpoint containing ; ~c[(REV (APPEND (REV (CDR X)) (LIST (CAR X))))], which suggests: (defthm rev-append (equal (rev (append a b)) (append (rev b) (rev a)))) ; And now ~c[triple-rev] succeeds. (defthm triple-rev (equal (rev (rev (rev x))) (rev x))) ; An alternative, and more elegant, solution is to prove the ~c[rev-rev] ; instead of ~c[rev-append]: ; (defthm rev-rev ; (implies (true-listp x) ; (equal (rev (rev x)) x))) ; ~c[Rev-rev] is also discoverable by The Method because it is ; suggested by the statement of ~c[triple-rev] itself: ~c[rev-rev] ; simplifies a simpler composition of the functions in ~c[triple-rev]. ; Both solutions produce lemmas likely to be of use in future proofs ; about ~c[rev]. ~ev[] Use your browser's ~b[Back Button] now to return to ~il[introductory-challenge-problem-1]. ~/~/") (deflabel introductory-challenge-problem-2 :doc ":Doc-Section introduction-to-the-theorem-prover challenge problem 2 for the new user of ACL2~/ Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing ~c[:ubt! 1]. Use The Method to prove ~bv[] (defthm subsetp-reflexive (subsetp x x)) ~ev[] When you've solved this problem, compare your answer to ours; ~pl[introductory-challenge-problem-2-answer]. Then, use your browser's ~b[Back Button] to return to ~il[introductory-challenges]. ~/~/") (deflabel introductory-challenge-problem-2-answer :doc ":Doc-Section introduction-to-the-theorem-prover answer to challenge problem 2 for the new user of ACL2~/ This answer is in the form of a script sufficient to lead ACL2 to a proof. ~bv[] ; Trying ~c[subsetp-reflexive] at this point produces the key checkpoint: ; (IMPLIES (AND (CONSP X) ; (SUBSETP (CDR X) (CDR X))) ; (SUBSETP (CDR X) X)) ; which suggests the generalization: (defthm subsetp-cdr (implies (subsetp a (cdr b)) (subsetp a b))) ; And now ~c[subsetp-reflexive] succeeds. (defthm subsetp-reflexive (subsetp x x)) ; A weaker version of the lemma, namely the one in which we ; add the hypothesis that ~c[b] is a ~c[cons], is also sufficient. ; (defthm subsetp-cdr-weak ; (implies (and (consp b) ; (subsetp a (cdr b))) ; (subsetp a b))) ; But the ~c[(consp b)] hypothesis is not really necessary in ; ACL2's type-free logic because ~c[(cdr b)] is ~c[nil] if ~c[b] is ; not a ~c[cons]. For the reasons explained in the tutorial, we ; prefer the strong version. ~ev[] Use your browser's ~b[Back Button] now to return to ~il[introductory-challenge-problem-2]. ~/~/") (deflabel introductory-challenge-problem-3 :doc ":Doc-Section introduction-to-the-theorem-prover challenge problem 3 for the new user of ACL2~/ Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing ~c[:ubt! 1]. Define the following functions and use The Method to prove the theorem at the bottom: ~bv[] (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) (defun dupsp (x) ; does x contain duplicate elements? (if (endp x) nil (if (member (car x) (cdr x)) t (dupsp (cdr x))))) (defthm dupsp-rev (equal (dupsp (rev x)) (dupsp x))) ~ev[] When you've solved this problem, compare your answer to ours; ~pl[introductory-challenge-problem-3-answer]. Then, use your browser's ~b[Back Button] to return to ~il[introductory-challenges]. ~/~/") (deflabel introductory-challenge-problem-3-answer :doc ":Doc-Section introduction-to-the-theorem-prover answer to challenge problem 3 for the new user of ACL2~/ This answer is in the form of a script sufficient to lead ACL2 to a proof. ~bv[] ; Trying ~c[dupsp-rev] at this point produces the key checkpoint: ; (IMPLIES (AND (CONSP X) ; (NOT (MEMBER (CAR X) (CDR X))) ; (EQUAL (DUPSP (REV (CDR X))) ; (DUPSP (CDR X)))) ; (EQUAL (DUPSP (APPEND (REV (CDR X)) (LIST (CAR X)))) ; (DUPSP (CDR X)))) ; which suggests the lemma ; (defthm dupsp-append ; (implies (not (member e x)) ; (equal (dupsp (append x (list e))) ; (dupsp x)))) ; However, attempting to prove that, produces a key checkpoint ; containing ~c[(MEMBER (CAR X) (APPEND (CDR X) (LIST E)))]. ; So we prove the lemma: (defthm member-append (iff (member e (append a b)) (or (member e a) (member e b)))) ; Note that we had to use ~c[iff] instead of ~c[equal] since ~c[member] is not a ; Boolean function. ; Having proved this lemma, we return to ~c[dupsp-append] and succeed: (defthm dupsp-append (implies (not (member e x)) (equal (dupsp (append x (list e))) (dupsp x)))) ; So now we return to ~c[dups-rev], expecting success. But it fails ; with the same key checkpoint: ; (IMPLIES (AND (CONSP X) ; (NOT (MEMBER (CAR X) (CDR X))) ; (EQUAL (DUPSP (REV (CDR X))) ; (DUPSP (CDR X)))) ; (EQUAL (DUPSP (APPEND (REV (CDR X)) (LIST (CAR X)))) ; (DUPSP (CDR X)))) ; Why wasn't our ~c[dupsp-append] lemma applied? We have two choices here: ; (1) Think. (2) Use tools. ; Think: When an enabled rewrite rule doesn't fire even though the left-hand ; side matches the target, the hypothesis couldn't be relieved. The ~c[dups-append] ; rule has the hypothesis ~c[(not (member e x))] and after the match with the left-hand side, ; ~c[e] is ~c[(CAR X)] and ~c[x] is ~c[(REV (CDR X))]. So the system couldn't rewrite ; ~c[(NOT (MEMBER (CAR X) (REV (CDR X))))] to true, even though it knows that ; ~c[(NOT (MEMBER (CAR X) (CDR X)))] from the second hypothesis of the checkpoint. ; Obviously, we need to prove ~c[member-rev] below. ; Use tools: We could enable the ``break rewrite'' facility, with ; ACL2 !>:brr t ; and then install an unconditional monitor on the rewrite rule ; ~c[dupsp-append], whose rune is (:REWRITE DUPSP-APPEND), with: ; :monitor (:rewrite dupsp-append) t ; Then we could re-try our main theorem, dupsp-rev. At the resulting ; interactive break we type :eval to evaluate the attempt to relieve the ; hypotheses of the rule. ; (1 Breaking (:REWRITE DUPSP-APPEND) on ; (DUPSP (BINARY-APPEND (REV #) (CONS # #))): ; 1 ACL2 >:eval ; 1x (:REWRITE DUPSP-APPEND) failed because :HYP 1 rewrote to ; (NOT (MEMBER (CAR X) (REV #))). ; Note that the report above shows that hypothesis 1 of the rule ; did not rewrite to T but instead rewrote to an expression ; involving ~c[(member ... (rev ...))]. Thus, we're led to the ; same conclusion that Thinking produced. To get out of the ; interactive break we type: ; 1 ACL2 >:a! ; Abort to ACL2 top-level ; and then turn off the break rewrite tool since we won't need it ; again right now, with: ; ACL2 !>:brr nil ; In either case, by thinking or using tools, we decide to prove: (defthm member-rev (iff (member e (rev x)) (member e x))) ; which succeeds. Now when we try to prove dups-rev, it succeeds. (defthm dupsp-rev (equal (dupsp (rev x)) (dupsp x))) ~ev[] Use your browser's ~b[Back Button] now to return to ~il[introductory-challenge-problem-3]. ~/~/") (deflabel introductory-challenge-problem-4 :doc ":Doc-Section introduction-to-the-theorem-prover challenge problem 4 for the new user of ACL2~/ Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing ~c[:ubt! 1]. This problem is much more open ended than the preceding ones. The challenge is to define a function that collects exactly one copy of each element of a list and to prove that it returns a subset of the list with no duplications. ~b[Hint]: We recommend that you read this hint to align your function names with our solution, to make comparisons easier. Our answer is shown in ~pl[introductory-challenge-problem-4-answer]. In that page you'll see a definition of a function ~c[collect-once] and the proofs of two theorems: ~bv[] (defthm main-theorem-1-about-collect-once (subsetp (collect-once x) x)) (defthm main-theorem-2-about-collect-once (not (dupsp (collect-once x)))) ~ev[] The function ~c[dupsp] is as defined in ~pl[introductory-challenge-problem-3]. This is quite easy. Then, we define a tail-recursive version of the method based on the pseudo-code: ~bv[] a = nil; while (x not empty) { a = if (member (car x) a) then a else (cons (car x) a); x = (cdr x); } return a; ~ev[] We formalize this with the function ~c[while-loop-version], where ~c[(while-loop-version x nil)] is the ``semantics'' of the code above. I.e., the function ~c[while-loop-version] captures the while loop in the pseudo-code above and returns the final value of ~c[a], and it should be invoked with the initial value of ~c[a] being ~c[nil]. We prove ~c[(while-loop-version x nil)] returns a subset of ~c[x] that contains no duplications. Furthermore, we do it two ways: first ``indirectly'' by relating ~c[while-loop-version] to ~c[collect-once], and second (``directly'') without using ~c[collect-once]. Both of these proofs are much harder than the ~c[collect-once] approach, involving about a dozen lemmas each. Compare your solutions to ours at ~pl[introductory-challenge-problem-4-answer]. Then, use your browser's ~b[Back Button] to return to ~il[introductory-challenges]. ~/~/") (deflabel introductory-challenge-problem-4-answer :doc ":Doc-Section introduction-to-the-theorem-prover answer to challenge problem 4 for the new user of ACL2~/ This answer is in the form of a script sufficient to lead ACL2 to a proof, with a brief prologue. We wish to collect one copy of each element in x. We'll actually define the method two ways, primitive recursively and tail-recursively, the latter method being analogous to the program: ~bv[] a = nil; while (x not empty) { a = if (member (car x) a) then a else (cons (car x) a); x = (cdr x); } return a; ~ev[] We'll prove the two ``equivalent'' and we'll prove that they return a subset of ~c[x] that contains no duplications. This page is organized into four sections. (A) We will start by proving that the primitive recursive version correct: it returns a subset of its argument that is duplication free. This will be straightforward. (B) Then we'll define the ~c[while]-loop version and we will prove it ``equivalent'' to the primitive recursive version. This will be challenging primarily because the two methods collect their answers in different orders; even stating the relationship between the two is interesting. Proving it will involve a few lemmas. But once we prove their ``equivalence'' the correctness of the ~c[while]-loop version will be straightforward from the correctness of the primitive recursive version. (C) We will disable the rules we prove about the ~c[while]-loop version and prove it correct directly, without exploiting the primitive recursive version. This requires leading the theorem prover more carefully because reasoning about tail-recursive functions that accumulate results is sometimes delicate. (D) Lessons learned -- a narrative that summarizes what we learn from these examples. We follow The Method, which, recall, involves us in recursive attempts to prove lemmas. We use a notation to indicate our sequence of proof attempts. Here is an example (although in actual use we print things across multiple lines). The number in bracket indicates our ``stack depth''. The ``key term'' is some term from a Key Checkpoint in the failed proof which is responsible for our subsequent action. Sometimes instead of a Key Term we just give an English explanation of what we're thinking. ~bv[] [0] (defthm main ...) Failed! Key Term: ... [1] (defthm lemma-1 ...) Succeeded! [0] (defthm main ...) Failed! Key Term: ... [1] (defthm lemma-2 ...) Failed! Key Term: ... [2] (defthm lemma-2a ...) Succeeded! [2] (defthm lemma-2b ...) Succeeded! [1] (defthm lemma-2 ...) Succeeded! [0] (defthm main ...) Succeeded! ~ev[] The rest of this page is just a re-playable script. ~bv[] ; ----------------------------------------------------------------- ; Section A: The Primitive Recursive Version and Its Correctness ; The property of having duplications is defined as: (defun dupsp (x) (if (endp x) nil (if (member (car x) (cdr x)) t (dupsp (cdr x))))) ; The primitive recursive method of collecting one copy of each element is: (defun collect-once (x) (if (endp x) nil (if (member (car x) (cdr x)) (collect-once (cdr x)) (cons (car x) (collect-once (cdr x)))))) ; [0] (defthm main-theorem-1-about-collect-once (subsetp (collect-once x) x)) ; Succeeded! ; [0] ; (defthm main-theorem-2-about-collect-once ; (not (dupsp (collect-once x)))) ; Failed! ; Key Term: (MEMBER (CAR X) (COLLECT-ONCE (CDR X))) ; [1] (defthm member-collect-once (iff (member e (collect-once a)) (member e a))) ; Succeeded! ; [0] (defthm main-theorem-2-about-collect-once (not (dupsp (collect-once x)))) ; Succeeded! ; That was really easy! ;----------------------------------------------------------------- ; Section B: The While-Loop Version and Its Correctness -- ; presented in two parts: its equivalence to the primitive recursive ; version and then its correctness proved via that equivalence ; The tail-recursive, or while-loop version, is defined as follows. The ; function below is the loop itself and it ought to be called with a = nil to ; implement the initialization of a in the pseudo-code above. (defun while-loop-version (x a) (if (endp x) a (while-loop-version (cdr x) (if (member (car x) a) a (cons (car x) a))))) ; We wish to prove that the two are equivalent. But they are actually ; very different. For example, ; (collect-once '(2 4 1 3 1 2 3 4)) = (1 2 3 4) ; (while-loop-version '(2 4 1 3 1 2 3 4) nil) = (3 1 4 2) ; Things get a little more complicated if a is non-nil: ; (while-loop-version '(2 4 1 3 1 2 3 4) '(2 2 4 4)) = (3 1 2 2 4 4) ; Several observations help explain what is happening. (1) Collect-once ; collects the last occurrence of each element, in the order of their last ; occurrences. So, for example, since the last occurrence of 2 preceeds the ; last occurrence of 3 in '(2 4 1 3 1 2 3 4)), then the collected 2 preceeds ; the collected 3 in the answer. But while-loop-version collects the first ; occurrence of each element, in the reverse order of that occurrence. So it ; adds 2 to its accumulator first and adds 3 last, making 3 preceed 2 in the ; answer. ; (2) The while-loop-version does not collect anything already in a and indeed ; just adds stuff to the front of a, returning everything initially in a plus ; one occurrence of everything in x not in a. ; To state the relationship that holds between these two we have to define two ; other functions. ; This is our familiar list reverse function... (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) ; And this function ``removes'' from x all the elements in y, i.e., copies x ; while dropping the elements of y. (defun list-minus (x y) (if (endp x) nil (if (member (car x) y) (list-minus (cdr x) y) (cons (car x) (list-minus (cdr x) y))))) ; The specific equivalence we're really interested in is ; (equal (while-loop-version x nil) ; (collect-once (rev x))) ; But we will not be able to prove that by induction because it has the ; constant nil where we need a variable, a, in order to admit an appropriate ; inductive instance. So we will attack the most general problem. What is ; (while-loop-version x a) equal to, in terms of collect-once? ; The most general relationship between the two collection functions is: ; (equal (while-loop-version x a) ; (append (collect-once (list-minus (rev x) a)) a)) ; This formula bears thinking about! If you're like us, you won't believe it ; until it is proved! ; [0] ; (defthm general-equivalence ; (equal (while-loop-version x a) ; (append (collect-once (list-minus (rev x) a)) a))) ; Failed! ; Key term in checkpoint: ; (LIST-MINUS (APPEND (REV (CDR X)) (LIST (CAR X))) A) ; [1] (defthm list-minus-append (equal (list-minus (append a b) c) (append (list-minus a c) (list-minus b c)))) ; Succeeded! ; [0] ; (defthm general-equivalence ; (equal (while-loop-version x a) ; (append (collect-once (list-minus (rev x) a)) a))) ; Failed! ; Key term in checkpoint: ; (COLLECT-ONCE (APPEND (LIST-MINUS (REV (CDR X)) A) (LIST (CAR X)))) ; [1] ; (defthm collect-once-append ; (equal (collect-once (append a b)) ; (append (list-minus (collect-once a) b) ; (collect-once b)))) ; Failed! ; Key term: ; (MEMBER (CAR A) (APPEND (CDR A) B)) ; [2] (defthm member-append (iff (member e (append a b)) (or (member e a) (member e b)))) ; Succeeded! ; [1] (defthm collect-once-append (equal (collect-once (append a b)) (append (list-minus (collect-once a) b) (collect-once b)))) ; Succeeded! ; [0] ; (defthm general-equivalence ; (equal (while-loop-version x a) ; (append (collect-once (list-minus (rev x) a)) a))) ; Failed! ; Key term: ; (APPEND (APPEND (LIST-MINUS (COLLECT-ONCE (LIST-MINUS (REV (CDR X)) A)) ; [1] (defthm assoc-append (equal (append (append a b) c) (append a (append b c)))) ; Succeeded! ; [0] ; (defthm general-equivalence ; (equal (while-loop-version x a) ; (append (collect-once (list-minus (rev x) a)) a))) ; Failed! ; Key term: ; (LIST-MINUS (COLLECT-ONCE (LIST-MINUS (REV (CDR X)) A)) ...) ; This key term makes us think of the lemma to move the LIST-MINUS inside the ; COLLECT-ONCE. But when that's done, we will have two LIST-MINUS terms ; nestled together and we will want to combine them into one. Call these two ; lemmas (a) and (b). ; [1] (a) ; (defthm list-minus-collect-once ; (equal (list-minus (collect-once x) a) ; (collect-once (list-minus x a)))) ; Failed! ; Key term: ; (MEMBER (CAR X) (LIST-MINUS (CDR X) A)) ; [2] (A pretty fact) (defthm member-list-minus (iff (member e (list-minus x a)) (and (member e x) (not (member e a))))) ; Succeeded! ; [1] (a) (defthm list-minus-collect-once (equal (list-minus (collect-once x) a) (collect-once (list-minus x a)))) ; Succeeded! ; [1] (b) (defthm list-minus-list-minus (equal (list-minus (list-minus x a) b) (list-minus x (append b a)))) ; Succeeded! ; [0] (defthm general-equivalence (equal (while-loop-version x a) (append (collect-once (list-minus (rev x) a)) a))) ; Succeeded! ; That completes the proof of the ``equivalence'' of the two methods. ; Now we prove (1) that the result of while-loop-version is a subset, and (2) ; that it contains no duplications. We prove the two conjuncts separately. ; [0] (defthm main-theorem-1-about-while-loop (subsetp (while-loop-version x nil) x)) ; Succeeded! ; But the theorem prover works harder to do the proof above than one might have ; expected because it doesn't turn into an instance of ; main-theorem-1-about-collect-once because of the presence of the rev term. ; However, we're content that ACL2 managed to do the proof on its own. ; [0] (defthm main-theorem-2-about-while-loop (not (dupsp (while-loop-version x nil)))) ; So we see that the proof of correctness of while-loop-version isn't hard, ; after we establish the relationship with the primitive recursive version. ; But finding and proving the relationship is fairly challenging. ; ----------------------------------------------------------------- ; Section C: A Direct Proof of the Correctness of the While-Loop Version ; Some would consider the proof in Section B ``indirect'' because we first showed ; how while-loop-version could be expressed as a collect-once and then proved ; our main theorems about while-loop-version, which means those main proofs ; were conducted in terms of collect-once, not while-loop-version. ; It is interesting to compare this proof with the ``direct'' one in which ; we don't use collect-once at all and reason only about while-loop-version. ; So to do that comparison, let's disable all the lemmas we've proved about ; while-loop-version and try to prove the two main theorems above about ; while-loop-version. (in-theory (disable general-equivalence main-theorem-1-about-while-loop main-theorem-2-about-while-loop)) ; [0] ; (defthm main-theorem-1-about-while-loop-redux ; (subsetp (while-loop-version x nil) x)) ; Failed! [Well, the truth is below...] ; We don't even submit this event above because we recognize that it is not ; general enough to permit proof by induction. We need to deal with the nil in ; the second argument of while-loop-version. Experience with induction tells ; us this should be a variable, so we can assume an appropriate inductive ; instance. Therefore, we adopt this subgoal immediately: ; [1] ; (defthm main-lemma-1-about-while-loop-version ; (subsetp (while-loop-version x a) (append x a))) ; Failed! ; Key Term: Does the wrong induction. ; [1] ; (defthm main-lemma-1-about-while-loop-version ; (subsetp (while-loop-version x a) (append x a)) ; :hints ((\"Goal\" :induct (while-loop-version x a)))) ; Failed! Two key terms are suggested ; Key term: (IMPLIES (AND ... (SUBSETP (WHILE-LOOP-VERSION (CDR X) A) (APPEND (CDR X) A))) ; (SUBSETP (WHILE-LOOP-VERSION (CDR X) A) (CONS ... (APPEND (CDR X) A)))) ; Key term: (SUBSETP A A) ; So we'll prove both before trying again. ; [2] (defthm subsetp-cons (implies (subsetp a b) (subsetp a (cons e b)))) ; Succeeded! ; [2] (defthm subsetp-reflexive (subsetp a a)) ; Succeeded! ; [1] ; (defthm main-lemma-1-about-while-loop-version ; (subsetp (while-loop-version x a) (append x a)) ; :hints ((\"Goal\" :induct (while-loop-version x a)))) ; Failed! ; Key Term: ; (IMPLIES (AND ... ; (SUBSETP (WHILE-LOOP-VERSION (CDR X) (CONS (CAR X) A)) ; (APPEND (CDR X) (CONS (CAR X) A)))) ; (SUBSETP (WHILE-LOOP-VERSION (CDR X) (CONS (CAR X) A)) ; (CONS (CAR X) (APPEND (CDR X) A)))) ; We'd be done if we could rewrite the ; (APPEND (CDR X) (CONS (CAR X) A)) ; to ; (CONS (CAR X) (APPEND (CDR X) A)) ; These two terms are not equal! But they are ``set-equal'' and this kind of ; rewriting is possible using user-defined equivalences and congruence rules. ; But the new user should not dive into congruences yet. So we will do this ; with ordinary lemmas: ; The plan then is to prove ; (iff (subsetp a (append b (cons e c))) ; (subsetp a (cons e (append b c)))) ; Consider the first half of this bi-implication: ; (implies (subsetp a (append b (cons e c))) ; hyp1 ; (subsetp a (cons e (append b c)))) ; concl ; Notice that if we knew ; (subsetp (append b (cons e c)) (cons e (append b c))) ; hyp2 ; then we could use hyp1 and hyp2 together with the transitivity of ; subsetp to get concl. ; The proof in the other direction is comparable but requires the ; (subsetp (cons e (append b c)) (append b (cons e c))) ; Thus, our plan is prove ; (a) transitivity of subsetp ; (b) (subsetp (append b (cons e c)) (cons e (append b c))) ; (c) (subsetp (cons e (append b c)) (append b (cons e c))) ; in order to prove ; (d) (iff (subsetp a (append b (cons e c))) ; (subsetp a (cons e (append b c)))) ; [2] (a) (defthm trans-subsetp (implies (and (subsetp a b) (subsetp b c)) (subsetp a c))) ; Succeeded! ; [2] (b) (defthm append-cons-v-cons-append-1 (subsetp (append b (cons e c)) (cons e (append b c)))) ; Succeeded! ; [2] (c) (defthm append-cons-v-cons-append-2 (subsetp (cons e (append b c)) (append b (cons e c)))) ; Succeeded! ; [2] (d) (defthm subsetp-append-cons-cons-append (iff (subsetp a (append b (cons e c))) (subsetp a (cons e (append b c))))) ; Succeeded! ; [1] (defthm main-lemma-1-about-while-loop-version (subsetp (while-loop-version x a) (append x a)) :hints ((\"Goal\" :induct (while-loop-version x a)))) ; Succeeded! ; [0] ; (defthm main-theorem-1-about-while-loop-version ; (subsetp (while-loop-version x nil) x)) ; Failed! [But the truth is below...] ; But we don't submit this because we don't expect it to be proved ; from the main lemma just proved: they don't match! But ; note that if we instantiated the main lemma, replacing a by nil, ; we get: ; (subsetp (while-loop-version x nil) (append x nil)) ; and we could simplify the (append x nil) to x in this context, with ; another congruence rule -- if we were using them. So let's prove ; first that we can simplify (append x nil) inside a subsetp: ; [1] (defthm subsetp-append-nil (iff (subsetp x (append y nil)) (subsetp x y))) ; Succeeded! ; and then just tell ACL2 how to use the lemma to get the main theorem. Note ; that we give a hint to instantiate main-lemma-1... but we also disable ; main-lemma-1... because otherwise it will rewrite itself away! Once the ; instance of main-lemma-1... is sitting around as a hypothesis, ; subsetp-append-nil will rewrite the (append x nil) to x for us and finish the ; proof. ; [0] (defthm main-theorem-1-about-while-loop-version (subsetp (while-loop-version x nil) x) :hints ((\"Goal\" :use (:instance main-lemma-1-about-while-loop-version (x x) (a nil)) :in-theory (disable main-lemma-1-about-while-loop-version)))) ; Succeeded! ; Recall that the main-theorem-1... just proved is just half of what we want. ; We also want: ; [0] ; (defthm main-theorem-2-about-while-loop-version ; (not (dupsp (while-loop-version x nil)))) ; Failed! [But the truth is below...] ; But, again, we don't submit that because the nil makes it not general enough for ; induction. Instead we go immediately to: ; [1] (defthm main-lemma-2-about-while-loop-version (implies (not (dupsp a)) (not (dupsp (while-loop-version x a))))) ; Succeeded! ; This time we know our main-lemma-2... will match (there's no (append x nil) ; in there to mess things up) and so we can complete the proof with: ; [0] (defthm main-theorem-2-about-while-loop-version (not (dupsp (while-loop-version x nil)))) ; Succeeded! ;----------------------------------------------------------------- ; Section D: Lessons Learned ; The most obvious lesson is that it is easier to reason about the primitive ; recursive collect-once than about the while-loop-version. Thus, if your only ; need is for a function that collects one occurrence of each element of a list ; and you don't care about the order in which you collect them and you don't ; need it to be very sparing of stack space when it executes, then use the ; primitive recursive definition and don't even think about while loops! ; So why might you be driven to while-loop-version? One possibility is that ; the list you wish to process is very long and the primitive recursive version ; would produce a stack overflow. In ACL2, that would mean the list would have ; to be several thousand long. Is your application really so demanding? ; Another possibility is that you are modeling in Lisp a while loop expressed ; in some other programming language. In that case, the fidelity of your model to ; the artifact being modeled is important and you should use while-loop-version. ; Another possibility is that for some reason order matters and you really are ; interested in collecting the first occurrence rather than the last. Of ; course this is most likely to be relevant in more interesting applications ; where the occurrences are somehow distinguishable. ; If you are forced to deal with the while-loop-version the question is do you ; do an indirect proof as in Section B or a direct proof as in Section C? ; The indirect proof involved 10 theorems and the direct proof involved 11. ; That is not a significant difference. ; But our sense is that the indirect proof is easier to find, once you figure ; out the basic shape of the relation between while-loop-version collect-once. ; In particular, we had to give the theorem prover two hints in the direct ; proof (versus no hints in the indirect proof). One of our hints was about ; what induction to do and the other was about how to use a previously proved ; instance of a lemma involving an accumulator. Furthermore, we had to think ; carefully about the use of the transitivity of subsetp and we had to hack our ; way around rewriting (append a (cons e b)) to (cons e (append a b)) in a ; subsetp-expression. ; Some of these ``set'' problems could have been handled a lot more elegantly by ; defining set-equal as an equivalence relation and proving the congruence ; rules to allow the rewriting of set-equal terms to set-equal terms inside ; certain expressions like subsetp and member. However, that involves a lot of ; overhead in the form of congruence rules showing that set-equality is ; maintained by replacement of set-equals by set-equals in various argument ; positions of the various functions. See :doc congruence. In general, we ; find congruence-based reasoning extremely neat and powerful when the ; appropriate infrastructure has been built up. But because the infrastructure ; is ``heavy'' we tend not to invest in it for small projects. ; In summary, different users might take home different lessons about whether a ; direct or indirect proof is better here. This is in part due to the ; complexity of the functional relationship between collect-once and ; while-loop-version, which additionall involved append, list-minus, and rev. ; Had the relationship been simpler, the indirect proof would have been ; preferred. ; An undeniable lesson, however, is that it is helpful to know both styles of ; proof and to be able to explore both as needed in your applications. ~ev[] Use your browser's ~b[Back Button] now to return to ~il[introductory-challenge-problem-4]. ~/~/") ; text below was generated quite tediously: I modified an existing ; tex file to produce: ; /v/filer4b/v11q001/text/onr/beige-paper.tex ; then processed it with latex and bibtex, then displayed the pdf and copied ; it to text, and then formatted it as a doc string, eliminating odd characters ; etc. (deflabel interesting-applications :doc ":Doc-Section acl2-tutorial some industrial examples of ACL2 use~/ ACL2 is an interactive system in which you can model digital artifacts and guide the system to mathematical proofs about the behavior of those models. It has been used at such places as AMD, Centaur, IBM, and Rockwell Collins to verify interesting properties of commercial designs. It has been used to verify properties of models of microprocessors, microcode, the Sun Java Virtual Machine, operating system kernels, other verifiers, and interesting algorithms. Here we list just a few of the industrially-relevant results obtained with ACL2. Reading the list may help you decide you want to learn how to use ACL2. If you do decide you want to learn more, we recommend that you take ~il[|The Tours| The Tours] after you leave this page. ACL2 was used at ~b[Motorola Government Systems] to certify several microcode programs for the ~b[Motorola CAP digital signal processor], including a comparator sort program that is particularly subtle. In the same project, ACL2 was used to model the CAP at both the pipelined architectural level and the instruction set level. The architectural model was bit- and cycle-accurate: it could be used to predict every bit of memory on every cycle. The models were proved equivalent under certain hypotheses, the most important being a predicate that analyzed the microcode for certain pipeline hazards. This predicate defined what the hazards were, syntactically, and the equivalence of the two models established the correctness of this syntactic characterization of hazards. Because ACL2 is a functional programming language, the ACL2 models and the hazard predicate could be executed. ACL2 executed microcode interpretr several times faster than the hardware simulator could execute it -- with assurance that the answers were equivalent. In addition, the ACL2 hazard predicate was executed on over fifty microcode programs written by Motorola engineers and extracted from the ROM mechanically. Hazards were found in some of these. (See, for example, Bishop Brock and Warren. A. Hunt, Jr. ``Formal analysis of the motorola CAP DSP.'' In ~i[Industrial-Strength Formal Methods]. Springer-Verlag, 1999.) ACL2 was used at ~b[Advanced Micro Devices] (AMD) to verify the compliance of the ~b[AMD Athon]'s (TM) elementary floating point operations with their IEEE 754 specifications. This followed ground-breaking work in 1995 when ACL2 was used to prove the correctness of the microcode for floating-point division on the ~b[AMD K5]. The AMD Athlon work proved addition, subtraction, multiplication, division, and square root compliant with the IEEE standard. Bugs were found in RTL designs. These bugs had survived undetected in hundreds of millions of tests but were uncovered by ACL2 proof attempts. The RTL in the fabricated Athlon FPU has been mechanically verified by ACL2. Similar ACL2 proofs have been carried out for every major AMD FPU design fabricated since the Athlon. (See for example, David Russinoff. ``A mechanically checked proof of correctness of the AMD5K86 floating-point square root microcode''. ~i[Formal Methods in System Design] Special Issue on Arithmetic Circuits, 1997.) ACL2 was used at ~b[IBM] to verify the floating point divide and square root on the ~b[IBM Power 4]. (See Jun Sawada. ``Formal verification of divide and square root algorithms using series calculation''. In ~i[Proceedings of the ACL2 Workshop 2002], Grenoble, April 2002.) ACL2 was used to verify floating-point addition/subtraction instructions for the ~b[media unit] from ~b[Centaur Technology]'s 64-bit, X86-compatible microprocessor. This unit implements over one hundred instructions, with the most complex being floating-point addition/subtraction. The media unit can add/subtract four pairs of floating-point numbers every clock cycle with an industry-leading two-cycle latency. The media unit was modeled by translating its Verilog design into an HDL deeply embedded in the ACL2 logic. The proofs used a combination of AIG- and BDD-based symbolic simulation, case splitting, and theorem proving. (See Warren A. Hunt, Jr. and Sol Swords. ``Centaur Technology Media Unit Verification''. In ~i[CAV '09: Proceedings of the 21st International] ~b[Conference on Computer Aided Verification], pages 353--367, Berlin, Heidelberg, 2009. Springer-Verlag.) ~b[Rockwell Collins] used ACL2 to prove information flow properties about its ~b[Advanced Architecture MicroProcessor 7 Government Version (AAMP7G)], a Multiple Independent Levels of Security (MILS) device for use in cryptographic applications. The AAMP7G provides MILS capability via a verified secure hardware-based separation kernel. The AAMP7G's design was proved to achieve MILS using ACL2, in accordance with the standards set by EAL-7 of the Common Criteria and Rockwell Collins has received National Security Agency (NSA) certification for the device based on this work. (See David S. Hardin, Eric W. Smith, and William. D. Young. ``A robust machine code proof framework for highly secure applications''. In ~i[Proceedings of the sixth international workshop on the ACL2 theorem prover] ~i[and its applications], pages 11--20, New York, NY, USA, 2006. ACM.) Key properties of the ~b[Sun Java Virtual Machine] and its ~b[bytecode verifier] were verified in ACL2. Among the properties proved were that certain invariants are maintained by ~b[class loading] and that the bytecode verifier insures that execution is safe. In addition, various ~b[JVM bytecode programs] have been verified using this model of the JVM. (See Hanbing Liu. ~i[Formal Specification and] ~i[Verification of a JVM and its Bytecode Verifier]. PhD thesis, University of Texas at Austin, 2006.) The ~b[Boyer-Moore fast string searching algorithm] was verified with ACL2, including a model of the JVM bytecode for the search algorithm itself (but not the preprocessing). (See J S. Moore and Matt Martinez. ``A mechanically checked proof of the correctness of the Boyer-Moore fast string searching algorithm.'' In ~i[Engineering Methods and Tools for Software Safety and Security] pages 267--284. IOS Press, 2009.) ACL2 was used to verify the fidelity between an ~b[ACL2-like theorem prover] and a simple (``trusted by inspection'') ~b[proof checker], thereby establishing (up to the soundness of ACL2) the soundness of the ACL2-like theorem prover. This project was only part of a much larger project in which the resulting ACL2 proof script was then hand-massaged into a script suitable for the ACL2-like theorem prover to process, generating a formal proof of its fidelity that has been checked by the trusted proof checker. (See Jared Davis. ~i[Milawa: A Self-Verifying Theorem Prover]. Ph.D. Thesis, University of Texas at Austin, December, 2009.) These are but a few of the interesting projects carried out with ACL2. Many of the authors mentioned above have versions of the papers on their web pages. In addition, see the link to ``Books and Papers about ACL2 and Its Applications'' on the ACL2 home page (~url[http://www.cs.utexas.edu/users/moore/acl2]). Also see the presentations in each of the workshops listed in the link to ``ACL2 Workshops'' on the ACL2 home page. ~/~/") (deflabel advanced-features ; We omit some of the more obscure features that users are unlikely to use, ; such as SET-RW-CACHE-STATE and SET-TAU-AUTO-MODE. :doc ":Doc-Section ACL2-Tutorial some advanced features of ACL2~/ Maybe you've been using ACL2 for awhile, and you wonder if there are lesser-known features that you might find useful. Then this topic is for you. We present below a ``laundry list'' of some such features, with brief descriptions and links to ~il[documentation] topics. Although the list below is long, it is not intended to be complete, and indeed some topics have been deliberately excluded. Some have fallen out of use, perhaps for good reason, such as ~il[NU-REWRITER] and ~il[OBDD]. Others are already likely to be discovered when needed, such as ~ilc[GETENV$] and perhaps ~ilc[DOUBLE-REWRITE]. Some topics are referenced by documentation for others in the list, such as ~ilc[MBT], which is referenced by ~ilc[MBE]. Some utilities such as ~ilc[PSTACK] and ~ilc[VERBOSE-PSTACK] seem too low-level to be worthy of inclusion below.~/ For an extensive introduction to using the prover, which may include some aspects new to you, ~pl[INTRODUCTION-TO-THE-THEOREM-PROVER]. A shorter topic contains highlights for efficient prover usage: ~pl[TIPS]. Also ~pl[ACL2-SEDAN] for an extension of ACL2 (written by others), ACL2s, that includes an Eclipse-based interface, more powerful and automatic termination reasoning, and other features. We now move on to the list. ~bv[] ======================================== Top-level commands and utilities: ======================================== ~ev[]~bq[] o ~l[A!] and ~pl[P!] to abort or pop. o ~l[ACL2-CUSTOMIZATION] for initial commands to run at startup. o ~l[KEYWORD-COMMANDS] for how keyword commands are processed. o ~l[LD] for many ways to control the top-level loop. o ~l[COMPILATION] for a discussion of ~c[set-compiler-enabled] and other compiler-related utilities. o For useful reader macros `~c[#!]', `~c[#.]', and `~c[#u]', ~pl[SHARP-BANG-READER], ~pl[SHARP-DOT-READER], and ~pl[SHARP-U-READER]. o To save and use an ACL2 executable, ~pl[ACL2-AS-STANDALONE-PROGRAM] and ~pl[SAVE-EXEC]. o For utilities related to timing, ~pl[TIME$], ~pl[WITH-PROVER-TIME-LIMIT], ~pl[WITH-PROVER-STEP-LIMIT], and ~pl[SET-PROVER-STEP-LIMIT]. o To query and manage the database, ~pl[HISTORY] (which discusses many useful utilities, such as ~c[:]~ilc[PBT] and ~c[:]~ilc[PL]), and ~pl[DEAD-EVENTS]. o ~l[ADD-INCLUDE-BOOK-DIR] for linking keyword for ~c[:dir] argument of ~ilc[LD] and ~ilc[INCLUDE-BOOK]. o ~l[REBUILD] for a fast way to load a file without waiting for proofs. o For parallel certification, ~pl[BOOKS-CERTIFICATION] for use of the ~c[-j] option of `make'; also ~pl[PROVISIONAL-CERTIFICATION]. ~eq[]~bv[] ======================================== Some relatively less common events: ======================================== ~ev[]~bq[] o ~l[RESET-PREHISTORY] to reset the prehistory. o ~l[ASSERT-EVENT] to assert that a given form returns a non-nil value. o ~l[DEFATTACH] to execute constrained functions using corresponding attached functions. o ~l[DEFUN-SK] to define a function whose body has an outermost quantifier. o ~l[DEFCHOOSE] to define a Skolem (witnessing) function. o For efficiency consider using ~c[defconst-fast]; ~pl[DEFCONST]. o ~l[SET-VERIFY-GUARDS-EAGERNESS] to specify when ~il[guard] verification is tried by default. ~eq[]~bv[] ======================================== Output and its control (~pl[IO] for additional information): ======================================== ~ev[]~bq[] o ~l[WITH-OUTPUT] to suppress or turn on specified output for an event. o ~l[EVISC-TABLE] for support for abbreviated output. o ~l[NTH-ALIASES-TABLE] for a table used to associate names for ~ilc[NTH]/~ilc[UPDATE-NTH] printing. o ~l[OUTPUT-TO-FILE] to redirect output to a file. o ~l[PRINT-CONTROL] to control ACL2 printing. o ~l[SET-EVISC-TUPLE] to control suppression of details when printing. o ~l[SET-INHIBIT-OUTPUT-LST] to control output by type. o ~l[SET-IPRINT] to allow abbreviated output to be read back in. o ~l[SET-PRINT-BASE] to control the radix in which numbers are printed. o ~l[SET-PRINT-RADIX] to control whether the radix of a number is printed. o ~l[SET-PRINT-CASE] to control whether symbols are printed in upper case or in lower case. ~eq[]~bv[] ======================================== On proving termination for definitions: ======================================== ~ev[]~bq[] o ~l[ORDINALS] for a discussion of ordinals in ACL2. o ~l[RULER-EXTENDERS] for a control on ACL2's termination and induction analyses. o ~l[SET-WELL-FOUNDED-RELATION] to set the default well-founded relation for termination analysis. o ~l[ACL2-SEDAN] for a related tool that provides extra automation for termination proofs. ~eq[]~bv[] ======================================== Proof debugging and output control: ======================================== ~ev[]~bq[] o ~l[ACCUMULATED-PERSISTENCE] to get statistics on which runes are being tried. o ~l[ADD-MACRO-FN] and ~pl[ADD-MACRO-ALIAS] to associate a function name with a macro name. o ~l[BREAK-REWRITE] for how to monitor rewrite rules. o ~l[DMR] for dynamic monitoring of rewriting and other prover activity. o ~l[FORWARD-CHAINING-REPORTS] to see reports about the forward chaining process. o ~l[GUARD-DEBUG] to generate markers to indicate sources of ~il[guard] proof obligations. o ~l[PROOF-CHECKER] for support for low-level interaction. o ~l[REDO-FLAT] for redo on failure of a ~ilc[PROGN], ~ilc[ENCAPSULATE], or ~ilc[CERTIFY-BOOK]. o ~l[SET-GAG-MODE] and ~pl[PSO] to abbreviate or restore proof output. o ~l[SET-INHIBIT-OUTPUT-LST], ~pl[SET-INHIBIT-WARNINGS], and ~pl[SET-INHIBITED-SUMMARY-TYPES] to inhibit various types of output. o ~l[SET-RAW-PROOF-FORMAT] to make proof output display lists of ~il[rune]s. o ~l[SKIP-PROOFS] to skip proofs for a given form. ~eq[]~bv[] ======================================== Program debugging: ======================================== ~ev[]~bq[] o ~l[BREAK$] to cause an immediate Lisp break. o ~l[BREAK-ON-ERROR] to break when encountering a hard or soft error caused by ACL2. o ~l[DISASSEMBLE$] to disassemble a function. o ~l[PRINT-GV] to print a form whose evaluation caused a guard violation. o ~l[PROFILE] to turn on profiling for one function. o ~l[TRACE$] and ~pl[OPEN-TRACE-FILE] to ~il[trace] function evaluations, possibly sending trace output to a file. o ~l[WET] to evaluate a form and print a subsequent error trace. ~eq[]~bv[] ======================================== Programming and evaluation idioms, support, utilities (also ~pl[PROGRAMMING] for more utilities, e.g., ~ilc[RANDOM$]) ======================================== ~ev[]~bq[] o ~l[ARRAYS] and ~l[DEFSTOBJ] for introductions to ACL2 arrays and single-threaded objects (stobjs), respectively, each of which provides efficient destructive operations in an applicative setting. Also ~pl[WITH-LOCAL-STOBJ] for a way to create local stobjs. o ~l[ASSERT$] to cause a hard error if the given test is false. o ~l[CANONICAL-PATHNAME] to obtain the true absolute filename, with soft links resolved. o ~l[CASE-MATCH] for a utility providing pattern matching and destructuring. o ~l[DEFPUN] to define a tail-recursive function symbol. o ~l[EC-CALL] to execute a call in the ACL2 logic instead of raw Lisp. o ~l[ER] to print an error message and ``cause an error''. o ~l[FLET] to provide local binding of function symbols. o ~l[GC$] to invoke the garbage collector. o ~l[MBE] to attach code for execution. o ~l[MV-LIST] to convert a multiple-valued result to a single-valued list. o ~l[MV?] to return one or more values. o For non-executable code, ~pl[DEFUN-NX] and ~pl[NON-EXEC]. o ~l[PROG2$] and ~pl[PROGN$] to execute two or more forms and return the value of the last one. o ~l[PROGRAMMING-WITH-STATE] for how to program using the von Neumannesque ACL2 ~il[state] object. o ~l[TOP-LEVEL] to evaluate a top-level form as a function body. o ~l[WITH-GUARD-CHECKING] to suppress or enable guard-checking for a form. o For ways to fake access to the state ~pl[WORMHOLE], ~pl[WITH-LOCAL-STATE], ~pl[CW], ~pl[CW!], ~pl[PRINTING-TO-STRINGS], ~pl[OBSERVATION-CW], and (dangerous!) ~pl[WITH-LIVE-STATE]. ~eq[]~bv[] ======================================== Connecting with the underlying host Lisp, and doing other evil: ======================================== ~ev[]~bq[] o ~l[DEFTTAG] to introduce a trust tag (ttag). o ~l[DEFMACRO-LAST] to define a macro that returns its last argument, but with side effects. o ~l[PROGN!] to evaluate forms that are not necessarily ~il[events]. o ~l[RETURN-LAST] to return the last argument, perhaps with side effects. o ~l[SET-RAW-MODE] to enter or exit ``raw mode,'' a raw Lisp environment. o ~l[SYS-CALL] to make a system call to the host operating system. ~eq[]~bv[] ======================================== Macros and related utilities: ======================================== ~ev[]~bq[] o ~l[DEFABBREV] for a convenient form of macro definition for simple expansions. o ~l[MACRO-ARGS] for the formals list of a macro definition (~pl[DEFMACRO]). o ~l[MAKE-EVENT] for a sort of extension of ~ilc[DEFMACRO] that allows access to the ~il[state], by evaluating (expanding) a given form and then evaluate the result of that expansion. o ~l[TRANS], ~pl[TRANS!], and ~pl[TRANS1] to print the macroexpansion of a form. ~eq[]~bv[] ======================================== Experimental extensions: ======================================== ~ev[]~bq[] o ~l[HONS-AND-MEMOIZATION] for ACL2(h); in particular, ~pl[MEMOIZE] for efficient function memoization and ~pl[PROFILE] for profiling. o ~l[REAL] for ACL2(r), which supports the real numbers. o ~l[PARALLELISM] for ACL2(p), which supports parallel evaluation and proof. ~eq[]~bv[] ======================================== Database control and query: ======================================== ~ev[]~bq[] o ~l[DISABLEDP] to determine whether a given name or rune is disabled. o For redefinition support ~pl[REDEF], ~pl[REDEF!], ~pl[REDEF+], ~pl[REDEF-], and ~pl[REDEFINED-NAMES]. o ~l[TABLE] for user-managed tables. o ~l[VERIFY-GUARDS-FORMULA] to view a guard proof obligation without doing the proof. ~eq[]~bv[] ======================================== Prover control ======================================== ~ev[]~bq[] o For congruence-based reasoning ~pl[DEFCONG], ~pl[CONGRUENCE], ~pl[EQUIVALENCE], ~pl[DEFEQUIV], and ~pl[DEFREFINEMENT]. o For meta rules and clause processors ~pl[META], ~pl[DEFEVALUATOR], ~pl[CLAUSE-PROCESSOR], ~pl[DEFINE-TRUSTED-CLAUSE-PROCESSOR] (for connecting with external tools, such as SAT solvers), and ~l[EXTENDED-METAFUNCTIONS] (for ~il[state] and context-sensitive metafunctions). o For theory control, ~pl[THEORIES] for detailed information, but in particular ~pl[DEFTHEORY], ~pl[THEORY-FUNCTIONS], ~pl[IN-ARITHMETIC-THEORY] (and ~pl[NON-LINEAR-ARITHMETIC]), and ~pl[THEORY-INVARIANT]. o ~l[HINTS] for a complete list of prover hints, including some of the more obscure ones such as ~c[:restrict], ~c[:]~ilc[clause-processor], ~c[:nonlinearp], ~c[:backchain-limit-rw], ~c[:reorder], and ~c[:backtrack]. Also ~pl[HINTS-AND-THE-WATERFALL] for an explanation of how hints interact with the ACL2 proof process. For other topics related to hints, ~pl[OVERRIDE-HINTS], ~pl[ADD-CUSTOM-KEYWORD-HINT], ~pl[DEFAULT-HINTS], and ~pl[COMPUTED-HINTS] (also ~pl[USING-COMPUTED-HINTS] and for other topics ~c[USING-COMPUTED-HINTS-xxx] ~pl[MISCELLANEOUS]. o ~l[BIND-FREE] to bind ~il[free-variables] of a ~il[rewrite] or ~il[linear] rule. o ~l[CASE-SPLIT] for a utility like ~ilc[FORCE] that immediately splits the top-level goal on the indicated hypothesis. o ~l[CASE-SPLIT-LIMITATIONS] for a way to the number of cases produced at once o ~l[DEFAULT-BACKCHAIN-LIMIT] to specify the backchain limit for a rule. o ~l[FORCE] for an identity function used to force a hypothesis. o ~l[OTF-FLG] for a way to push more than one initial subgoal for induction. o ~l[RULE-CLASSES] to add various kinds of rules to the database, including more unusual sorts such as ~c[:]~ilc[built-in-clause] rules and ~c[:]~ilc[induction] rules. o ~l[SET-BACKCHAIN-LIMIT] to set the backchain-limit used by the type-set and rewriting mechanisms. o ~l[SET-BODY] to set an alternate definition body for ~c[:expand] ~il[hints]. o ~l[SET-REWRITE-STACK-LIMIT] to set the ~il[rewrite] stack depth used by the rewriter. o ~l[SYNTAXP] to attach a heuristic filter on a ~c[:]~ilc[rewrite], ~c[:]~ilc[meta], or ~c[:]~ilc[linear] rule. ~eq[]~/") acl2-sources/type-set-a.lisp0000666002132200015000000011260112222115527015436 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ; The following macro defines the macro the-type-set so that ; (the-type-set x) expands to (the (integer 0 n) x). It also declares ; the symbols listed below as defconsts whose values are the ; successive powers of 2. ; Warning: The first six entries *ts-zero* through ; *ts-complex-rational* are tied down to bit positions 0-5. See, for ; example, our setting up of the +-alist entry. Note however that in ; fact, we are wiring in the first five entries as well, in our ; handling of *type-set-<-table*. Since < is a function defined only ; on the rationals, the latter decision seems safe even given the ; possibility that we'll add additional numeric types in the future. ; WARNING: If new basic type-sets are added, update the function ; one-bit-type-setp below which enumerates all of the basic type-sets ; and also update *initial-type-set-inverter-rules* which must contain ; a rule for every primitive bit! ;; RAG - I added *ts-positive-non-ratio*, *ts-negative-non-ratio*, and ;; *ts-complex-non-rational*. (def-basic-type-sets *ts-zero* *ts-positive-integer* *ts-positive-ratio* #+:non-standard-analysis *ts-positive-non-ratio* *ts-negative-integer* *ts-negative-ratio* #+:non-standard-analysis *ts-negative-non-ratio* ; It is tempting to split the complex rationals into the positive and negative ; complex rationals (i.e., those with positive real parts and those with ; negative real parts). See the ``Long comment on why we extend the ; true-type-alist to accommodate complex rationals'' in assume-true-false. ; For now, we'll resist that temptation. *ts-complex-rational* #+:non-standard-analysis *ts-complex-non-rational* *ts-nil* *ts-t* *ts-non-t-non-nil-symbol* *ts-proper-cons* *ts-improper-cons* *ts-string* *ts-character*) ; Notes on the Implementation of Type-Sets ; Suppose, contrary to truth but convenient for thinking, that there ; were only 3 ``regular'' type bits, say for cons, symbol, and ; character. Then the length of the list on which def-basic-type-sets ; would be called would be 3. Thus the integer 2^3-1 = 7 = ...0000111 ; would be the type set that represented the set of all conses, ; symbols, and characters. The type-set (lognot 7) = ....1111000 = -8 ; would be the complement of that, i.e. the type set that consisted of ; everything but conses, symbols, and characters. Were there only 3 ; regular type bits, 7 and -8 would be the maximum and minimum type ; sets, considered as integers, ; Since, in fact, we name 13 regular type bits above, and 2^13 = 8192, ; the type-sets range from -8192 to +8191. ; It is important to note that even though there are only 13 regular ; type bits, type-sets are not exactly 13 bits wide. A Common Lisp ; integer, when treated as a logical bit vector, can be thought of as ; a infinite series of bits which always concludes with an infinite ; series of 0's (for positive integers) or an infinite series of 1's ; (for negative integers). We think of the bits in these two infinite ; series as standing for the ``irregular'' (non-ACL2) Common Lisp ; types. Returning to the example above of 3 regular bits, and ; imagining that the irregular bits are for floats, arrays, ; pathnames, etc., then ...1111000 can be thought of as representing ; the set of all floats, complexes, arrays, pathnames, ..., etc. ; Since we there are an infinite number of these irregular bits the ; only way we can say this without the ``etc.'' is to say ``not ; conses, symbols, or characters.'' ; When we first implemented ACL2 we did not use this approach. ; Instead we allocated one additional ``regular'' bit which we named ; *ts-other*, denoting all the ``irregular'' objects. We cannot ; reconstruct exactly why we did this, though we believe it had to do ; with the misapprehension that use of the so-called ``sign bit'' (as ; in nqthm) would limit type sets to fixnums. The fallacy of course ; is that in Common Lisp there is no sign bit, there is an infinite ; sequence of them. In any case, the introduction of *ts-other* had ; several bad effects on our thinking, although it did not cause ; unsoundness. The main effect was to lead us to pretend that ; type-sets could be thought of as masks of some fixed width, i.e., ; 14. But then consider two bit vectors that agree on their low order ; 14 bits but differ on the high order bits. Are they the same type ; set or not? Since we compare the type-sets with equality, they ; clearly are not the same. What made our code correct was that such ; type sets could never arise: the setting of the *ts-other* bit was ; always equal to the setting of all the ``irregular'' bits. Of ; course, this invariant would have been violated had we ever created ; a type-set by logioring *ts-other* into another type-set, but we ; never did that. In any case, we now realize that the use of the ; infinite sequence of sign bits a la nqthm is really cleaner because ; it gives us no way to turn on the irregular bits except by ; complementing known bits. (defconst *ts-non-negative-integer* (ts-union0 *ts-zero* *ts-positive-integer*)) (defconst *ts-non-positive-integer* (ts-union0 *ts-zero* *ts-negative-integer*)) (defconst *ts-integer* (ts-union0 *ts-positive-integer* *ts-zero* *ts-negative-integer*)) (defconst *ts-rational* (ts-union0 *ts-integer* *ts-positive-ratio* *ts-negative-ratio*)) ;; RAG - I added the *ts-real* type, analogous to *ts-rational*. #+:non-standard-analysis (defconst *ts-real* (ts-union0 *ts-integer* *ts-positive-ratio* *ts-positive-non-ratio* *ts-negative-ratio* *ts-negative-non-ratio*)) ;; RAG - I added *ts-complex* to include the complex-rationals and ;; non-rationals. #+:non-standard-analysis (defconst *ts-complex* (ts-union0 *ts-complex-rational* *ts-complex-non-rational*)) ;; RAG - I changed the type *ts-acl2-number* to include the new reals ;; and complex numbers as well as the old rational numbers. I added ;; the types *ts-rational-acl2-number* to stand for the old ;; *ts-acl2-number*, and I added *ts-non-rational-acl2-number* to ;; represent the new numbers. (defconst *ts-acl2-number* #+:non-standard-analysis (ts-union0 *ts-real* *ts-complex*) #-:non-standard-analysis (ts-union0 *ts-rational* *ts-complex-rational*)) (defconst *ts-rational-acl2-number* (ts-union0 *ts-rational* *ts-complex-rational*)) #+:non-standard-analysis (defconst *ts-non-rational-acl2-number* (ts-union0 *ts-positive-non-ratio* *ts-negative-non-ratio* *ts-complex-non-rational*)) (defconst *ts-negative-rational* (ts-union0 *ts-negative-integer* *ts-negative-ratio*)) (defconst *ts-positive-rational* (ts-union0 *ts-positive-integer* *ts-positive-ratio*)) (defconst *ts-non-positive-rational* (ts-union0 *ts-zero* *ts-negative-rational*)) (defconst *ts-non-negative-rational* (ts-union0 *ts-zero* *ts-positive-rational*)) (defconst *ts-ratio* (ts-union0 *ts-positive-ratio* *ts-negative-ratio*)) ;; RAG - I added the types *ts-non-ratio*, *ts-negative-real*, ;; *ts-positive-real*, *ts-non-positive-real*, and ;; *ts-non-negative-real*, to mimic their *...-rational* ;; counterparts. #+:non-standard-analysis (progn (defconst *ts-non-ratio* (ts-union0 *ts-positive-non-ratio* *ts-negative-non-ratio*)) (defconst *ts-negative-real* (ts-union0 *ts-negative-integer* *ts-negative-ratio* *ts-negative-non-ratio*)) (defconst *ts-positive-real* (ts-union0 *ts-positive-integer* *ts-positive-ratio* *ts-positive-non-ratio*)) (defconst *ts-non-positive-real* (ts-union0 *ts-zero* *ts-negative-real*)) (defconst *ts-non-negative-real* (ts-union0 *ts-zero* *ts-positive-real*)) ) (defconst *ts-cons* (ts-union0 *ts-proper-cons* *ts-improper-cons*)) (defconst *ts-boolean* (ts-union0 *ts-nil* *ts-t*)) (defconst *ts-true-list* (ts-union0 *ts-nil* *ts-proper-cons*)) (defconst *ts-non-nil* (ts-complement0 *ts-nil*)) (defconst *ts-symbol* (ts-union0 *ts-nil* *ts-t* *ts-non-t-non-nil-symbol*)) (defconst *ts-true-list-or-string* (ts-union0 *ts-true-list* *ts-string*)) (defconst *ts-empty* 0) (defconst *ts-unknown* -1) ;; RAG - In accordance with the comment above on adding new basic type ;; sets, I added *ts-positive-non-ratio*, *ts-negative-non-ratio*, and ;; *ts-complex-non-rational* to this recognizer. I wonder if the ;; speed difference is still faster than logcount. Seems like if it ;; was 75 times faster before, it probably ought to be. (defun one-bit-type-setp (ts) ; Tests in AKCL using one million iterations show that this function, as coded, ; is roughly 75 times faster than one based on logcount. We do not currently ; use this function but it was once used in the double whammy heuristics and ; because we spent some time finding the best way to code it, we've left it for ; now. (or (= (the-type-set ts) *ts-zero*) (= (the-type-set ts) *ts-positive-integer*) (= (the-type-set ts) *ts-positive-ratio*) #+:non-standard-analysis (= (the-type-set ts) *ts-positive-non-ratio*) (= (the-type-set ts) *ts-negative-integer*) (= (the-type-set ts) *ts-negative-ratio*) #+:non-standard-analysis (= (the-type-set ts) *ts-negative-non-ratio*) (= (the-type-set ts) *ts-complex-rational*) #+:non-standard-analysis (= (the-type-set ts) *ts-complex-non-rational*) (= (the-type-set ts) *ts-nil*) (= (the-type-set ts) *ts-t*) (= (the-type-set ts) *ts-non-t-non-nil-symbol*) (= (the-type-set ts) *ts-proper-cons*) (= (the-type-set ts) *ts-improper-cons*) (= (the-type-set ts) *ts-string*) (= (the-type-set ts) *ts-character*))) ; The following fancier versions of the ts functions and macros will serve us ; well below and in type-set-b.lisp. ;; RAG - I added here the new type sets that I had defined: ;; *ts-rational-acl2-number*, *ts-non-rational-acl2-number*, ;; *ts-real*, *ts-non-positive-real*, *ts-non-negative-real*, ;; *ts-negative-real*, *ts-positive-real*, *ts-non-ratio*, ;; *ts-complex*, *ts-positive-non-ratio*, *ts-negative-non-ratio*, and ;; *ts-complex-non-rational*. (defconst *code-type-set-alist* ; This alist serves two distinct purposes. The first is crucial to soundness: ; it maps each known type-set constant symbol to its value. (Unsoundness would ; be introduced by mapping such a symbol to an incorrect value.) Every ; declared type-set constant should be in this list; failure to include a ; symbol precludes its use in ts-union and other type-set building macros. ; Ordering of the alist is unimportant for these purposes. ; The second use is in decode-type-set, where we use it to convert a type-set ; into its symbolic form. For those purposes it is best if the larger ; type-sets, the one containing more 1 bits, are listed first. The heuristic ; for converting a type-set into symbolic form is to note whether the type-set ; contains as a subset one of the type-sets mentioned here and if so include ; the corresponding name in the output and delete from the numeric type-set the ; corresponding bits until all all bits are accounted for. (list (cons '*ts-unknown* *ts-unknown*) (cons '*ts-non-nil* *ts-non-nil*) (cons '*ts-acl2-number* *ts-acl2-number*) (cons '*ts-rational-acl2-number* *ts-rational-acl2-number*) #+:non-standard-analysis (cons '*ts-non-rational-acl2-number* *ts-non-rational-acl2-number*) #+:non-standard-analysis (cons '*ts-real* *ts-real*) (cons '*ts-rational* *ts-rational*) (cons '*ts-true-list-or-string* *ts-true-list-or-string*) (cons '*ts-symbol* *ts-symbol*) (cons '*ts-integer* *ts-integer*) #+:non-standard-analysis (cons '*ts-non-positive-real* *ts-non-positive-real*) #+:non-standard-analysis (cons '*ts-non-negative-real* *ts-non-negative-real*) (cons '*ts-non-positive-rational* *ts-non-positive-rational*) (cons '*ts-non-negative-rational* *ts-non-negative-rational*) #+:non-standard-analysis (cons '*ts-negative-real* *ts-negative-real*) #+:non-standard-analysis (cons '*ts-positive-real* *ts-positive-real*) (cons '*ts-negative-rational* *ts-negative-rational*) (cons '*ts-positive-rational* *ts-positive-rational*) (cons '*ts-non-negative-integer* *ts-non-negative-integer*) (cons '*ts-non-positive-integer* *ts-non-positive-integer*) (cons '*ts-ratio* *ts-ratio*) #+:non-standard-analysis (cons '*ts-non-ratio* *ts-non-ratio*) #+:non-standard-analysis (cons '*ts-complex* *ts-complex*) (cons '*ts-cons* *ts-cons*) (cons '*ts-boolean* *ts-boolean*) (cons '*ts-true-list* *ts-true-list*) (cons '*ts-zero* *ts-zero*) (cons '*ts-positive-integer* *ts-positive-integer*) (cons '*ts-positive-ratio* *ts-positive-ratio*) #+:non-standard-analysis (cons '*ts-positive-non-ratio* *ts-positive-non-ratio*) (cons '*ts-negative-integer* *ts-negative-integer*) (cons '*ts-negative-ratio* *ts-negative-ratio*) #+:non-standard-analysis (cons '*ts-negative-non-ratio* *ts-negative-non-ratio*) #+:non-standard-analysis (cons '*ts-complex-non-rational* *ts-complex-non-rational*) (cons '*ts-complex-rational* *ts-complex-rational*) (cons '*ts-nil* *ts-nil*) (cons '*ts-t* *ts-t*) (cons '*ts-non-t-non-nil-symbol* *ts-non-t-non-nil-symbol*) (cons '*ts-proper-cons* *ts-proper-cons*) (cons '*ts-improper-cons* *ts-improper-cons*) (cons '*ts-string* *ts-string*) (cons '*ts-character* *ts-character*) (cons '*ts-empty* *ts-empty*))) (defun logior-lst (lst ans) (cond ((null lst) ans) (t (logior-lst (cdr lst) (logior (car lst) ans))))) (defun logand-lst (lst ans) (cond ((null lst) ans) (t (logand-lst (cdr lst) (logand (car lst) ans))))) (mutual-recursion (defun ts-complement-fn (x) (let ((y (eval-type-set x))) (if (integerp y) (lognot y) (list 'lognot (list 'the-type-set y))))) (defun ts-union-fn (x) (cond ((null x) '*ts-empty*) ((null (cdr x)) (eval-type-set (car x))) (t (let ((lst (eval-type-set-lst x))) (cond ((integer-listp lst) (logior-lst lst *ts-empty*)) (t (xxxjoin 'logior lst))))))) (defun ts-intersection-fn (x) (cond ((null x) '*ts-unknown*) ((null (cdr x)) (eval-type-set (car x))) (t (let ((lst (eval-type-set-lst x))) (cond ((integer-listp lst) (logand-lst lst *ts-unknown*)) (t (xxxjoin 'logand lst))))))) (defun eval-type-set (x) (cond ((and (symbolp x) (legal-constantp1 x)) (or (cdr (assoc-eq x *code-type-set-alist*)) (er hard 'eval-type-set "The constant ~x0 appears as an argument to a ts- function but is ~ not known to *code-type-set-alist*, whose current value ~ is:~%~x1. You should redefine that constant or define your own ~ ts- functions if you want to avoid this problem." x *code-type-set-alist*))) ((atom x) x) (t (case (car x) (quote (if (integerp (cadr x)) (cadr x) x)) (ts-union (ts-union-fn (cdr x))) (ts-intersection (ts-intersection-fn (cdr x))) (ts-complement (ts-complement-fn (cadr x))) (t x))))) (defun eval-type-set-lst (x) ; This is an improved version of list-of-the-type-set. (cond ((consp x) (let ((y (eval-type-set (car x)))) (cons (if (integerp y) y (list 'the-type-set y)) (eval-type-set-lst (cdr x))))) (t nil))) ) (defmacro ts-complement (x) (list 'the-type-set (ts-complement-fn x))) (defmacro ts-intersection (&rest x) (list 'the-type-set (ts-intersection-fn x))) (defmacro ts-union (&rest x) (list 'the-type-set (ts-union-fn x))) (defmacro ts-subsetp (ts1 ts2) (list 'let (list (list 'ts1 ts1) (list 'ts2 ts2)) ; Warning: Keep the following type in sync with the definition of the-type-set ; in def-basic-type-sets. `(declare (type (integer ,*min-type-set* ,*max-type-set*) ts1 ts2)) '(ts= (ts-intersection ts1 ts2) ts1))) ;; RAG - I modified this to include cases for the irrationals and ;; complex numbers. (defun type-set-binary-+-alist-entry (ts1 ts2) (ts-builder ts1 (*ts-zero* ts2) (*ts-positive-integer* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-positive-integer*) (*ts-negative-integer* *ts-integer*) (*ts-positive-ratio* *ts-positive-ratio*) (*ts-negative-ratio* *ts-ratio*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-positive-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) (*ts-negative-integer* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-integer*) (*ts-negative-integer* *ts-negative-integer*) (*ts-positive-ratio* *ts-ratio*) (*ts-negative-ratio* *ts-negative-ratio*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-negative-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) (*ts-positive-ratio* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-positive-ratio*) (*ts-negative-integer* *ts-ratio*) (*ts-positive-ratio* *ts-positive-rational*) (*ts-negative-ratio* *ts-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-positive-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) (*ts-negative-ratio* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-ratio*) (*ts-negative-integer* *ts-negative-ratio*) (*ts-positive-ratio* *ts-rational*) (*ts-negative-ratio* *ts-negative-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-negative-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) #+:non-standard-analysis (*ts-positive-non-ratio* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-positive-non-ratio*) (*ts-negative-integer* *ts-non-ratio*) (*ts-positive-ratio* *ts-positive-non-ratio*) (*ts-negative-ratio* *ts-non-ratio*) (*ts-positive-non-ratio* *ts-positive-real*) (*ts-negative-non-ratio* *ts-real*) (*ts-complex-rational* *ts-complex-non-rational*) (*ts-complex-non-rational* *ts-complex*))) #+:non-standard-analysis (*ts-negative-non-ratio* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-non-ratio*) (*ts-negative-integer* *ts-negative-non-ratio*) (*ts-positive-ratio* *ts-non-ratio*) (*ts-negative-ratio* *ts-negative-non-ratio*) (*ts-positive-non-ratio* *ts-real*) (*ts-negative-non-ratio* *ts-negative-real*) (*ts-complex-rational* *ts-complex-non-rational*) (*ts-complex-non-rational* *ts-complex*) )) (*ts-complex-rational* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-complex-rational*) (*ts-negative-integer* *ts-complex-rational*) (*ts-positive-ratio* *ts-complex-rational*) (*ts-negative-ratio* *ts-complex-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-complex-non-rational*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-complex-non-rational*) (*ts-complex-rational* *ts-rational-acl2-number*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-non-rational-acl2-number*) )) #+:non-standard-analysis (*ts-complex-non-rational* (ts-builder ts2 (*ts-zero* ts1) (*ts-positive-integer* *ts-complex-non-rational*) (*ts-negative-integer* *ts-complex-non-rational*) (*ts-positive-ratio* *ts-complex-non-rational*) (*ts-negative-ratio* *ts-complex-non-rational*) (*ts-positive-non-ratio* *ts-complex*) (*ts-negative-non-ratio* *ts-complex*) (*ts-complex-rational* *ts-non-rational-acl2-number*) (*ts-complex-non-rational* *ts-acl2-number*))))) (defun type-set-binary-+-alist1 (i j lst) (cond ((< j 0) lst) (t (let ((x (type-set-binary-+-alist-entry i j))) (cond ((= x *ts-unknown*) (type-set-binary-+-alist1 i (1- j) lst)) (t (type-set-binary-+-alist1 i (1- j) (cons (cons (cons i j) x) lst)))))))) (defun type-set-binary-+-alist (i j lst) (cond ((< i 0) lst) (t (type-set-binary-+-alist (1- i) j (type-set-binary-+-alist1 i j lst))))) ;; RAG - I modified this to include cases for the irrationals and ;; complex numbers. (defun type-set-binary-*-alist-entry (ts1 ts2) (ts-builder ts1 (*ts-zero* *ts-zero*) (*ts-positive-integer* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-positive-integer*) (*ts-negative-integer* *ts-negative-integer*) (*ts-positive-ratio* *ts-positive-rational*) (*ts-negative-ratio* *ts-negative-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-positive-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-negative-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) (*ts-negative-integer* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-negative-integer*) (*ts-negative-integer* *ts-positive-integer*) (*ts-positive-ratio* *ts-negative-rational*) (*ts-negative-ratio* *ts-positive-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-negative-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-positive-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) (*ts-positive-ratio* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-positive-rational*) (*ts-negative-integer* *ts-negative-rational*) (*ts-positive-ratio* *ts-positive-rational*) (*ts-negative-ratio* *ts-negative-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-positive-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-negative-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) (*ts-negative-ratio* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-negative-rational*) (*ts-negative-integer* *ts-positive-rational*) (*ts-positive-ratio* *ts-negative-rational*) (*ts-negative-ratio* *ts-positive-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-negative-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-positive-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*) )) #+:non-standard-analysis (*ts-positive-non-ratio* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-positive-non-ratio*) (*ts-negative-integer* *ts-negative-non-ratio*) (*ts-positive-ratio* *ts-positive-non-ratio*) (*ts-negative-ratio* *ts-negative-non-ratio*) (*ts-positive-non-ratio* *ts-positive-real*) (*ts-negative-non-ratio* *ts-negative-real*) (*ts-complex-rational* *ts-complex-non-rational*) (*ts-complex-non-rational* *ts-complex*))) #+:non-standard-analysis (*ts-negative-non-ratio* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-negative-non-ratio*) (*ts-negative-integer* *ts-positive-non-ratio*) (*ts-positive-ratio* *ts-negative-non-ratio*) (*ts-negative-ratio* *ts-positive-non-ratio*) (*ts-positive-non-ratio* *ts-negative-real*) (*ts-negative-non-ratio* *ts-positive-real*) (*ts-complex-rational* *ts-complex-non-rational*) (*ts-complex-non-rational* *ts-complex*))) (*ts-complex-rational* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-complex-rational*) (*ts-negative-integer* *ts-complex-rational*) (*ts-positive-ratio* *ts-complex-rational*) (*ts-negative-ratio* *ts-complex-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-complex-non-rational*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-complex-non-rational*) (*ts-complex-rational* (ts-intersection0 *ts-rational-acl2-number* (ts-complement0 *ts-zero*))) #+:non-standard-analysis (*ts-complex-non-rational* *ts-non-rational-acl2-number*))) #+:non-standard-analysis (*ts-complex-non-rational* (ts-builder ts2 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-complex-non-rational*) (*ts-negative-integer* *ts-complex-non-rational*) (*ts-positive-ratio* *ts-complex-non-rational*) (*ts-negative-ratio* *ts-complex-non-rational*) (*ts-positive-non-ratio* *ts-complex*) (*ts-negative-non-ratio* *ts-complex*) (*ts-complex-rational* *ts-non-rational-acl2-number*) (*ts-complex-non-rational* (ts-intersection0 *ts-acl2-number* (ts-complement0 *ts-zero*))))))) (defun type-set-binary-*-alist1 (i j lst) (cond ((< j 0) lst) (t (let ((x (type-set-binary-*-alist-entry i j))) (cond ((= x *ts-unknown*) (type-set-binary-*-alist1 i (1- j) lst)) (t (type-set-binary-*-alist1 i (1- j) (cons (cons (cons i j) x) lst)))))))) (defun type-set-binary-*-alist (i j lst) (cond ((< i 0) lst) (t (type-set-binary-*-alist (1- i) j (type-set-binary-*-alist1 i j lst))))) ;; RAG - I modified this to include cases for the irrationals and ;; complex numbers. (defun type-set-<-alist-entry (ts1 ts2) (ts-builder ts1 (*ts-zero* (ts-builder ts2 (*ts-zero* *ts-nil*) (*ts-positive-integer* *ts-t*) (*ts-negative-integer* *ts-nil*) (*ts-positive-ratio* *ts-t*) (*ts-negative-ratio* *ts-nil*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-t*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-nil*))) (*ts-positive-integer* (ts-builder ts2 (*ts-zero* *ts-nil*) (*ts-positive-integer* *ts-boolean*) (*ts-negative-integer* *ts-nil*) (*ts-positive-ratio* *ts-boolean*) (*ts-negative-ratio* *ts-nil*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-boolean*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-nil*))) (*ts-negative-integer* (ts-builder ts2 (*ts-zero* *ts-t*) (*ts-positive-integer* *ts-t*) (*ts-negative-integer* *ts-boolean*) (*ts-positive-ratio* *ts-t*) (*ts-negative-ratio* *ts-boolean*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-t*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-boolean*))) (*ts-positive-ratio* (ts-builder ts2 (*ts-zero* *ts-nil*) (*ts-positive-integer* *ts-boolean*) (*ts-negative-integer* *ts-nil*) (*ts-positive-ratio* *ts-boolean*) (*ts-negative-ratio* *ts-nil*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-boolean*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-nil*))) (*ts-negative-ratio* (ts-builder ts2 (*ts-zero* *ts-t*) (*ts-positive-integer* *ts-t*) (*ts-negative-integer* *ts-boolean*) (*ts-positive-ratio* *ts-t*) (*ts-negative-ratio* *ts-boolean*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-t*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-boolean*))) #+:non-standard-analysis (*ts-positive-non-ratio* (ts-builder ts2 (*ts-zero* *ts-nil*) (*ts-positive-integer* *ts-boolean*) (*ts-negative-integer* *ts-nil*) (*ts-positive-ratio* *ts-boolean*) (*ts-negative-ratio* *ts-nil*) (*ts-positive-non-ratio* *ts-boolean*) (*ts-negative-non-ratio* *ts-nil*))) #+:non-standard-analysis (*ts-negative-non-ratio* (ts-builder ts2 (*ts-zero* *ts-t*) (*ts-positive-integer* *ts-t*) (*ts-negative-integer* *ts-boolean*) (*ts-positive-ratio* *ts-t*) (*ts-negative-ratio* *ts-boolean*) (*ts-positive-non-ratio* *ts-t*) (*ts-negative-non-ratio* *ts-boolean*))))) (defun type-set-<-alist1 (i j lst) (cond ((< j 0) lst) (t (let ((x (type-set-<-alist-entry i j))) (cond ((= x *ts-unknown*) (type-set-<-alist1 i (1- j) lst)) (t (type-set-<-alist1 i (1- j) (cons (cons (cons i j) x) lst)))))))) (defun type-set-<-alist (i j lst) (cond ((< i 0) lst) (t (type-set-<-alist (1- i) j (type-set-<-alist1 i j lst))))) acl2-sources/type-set-b.lisp0000664002132200015000000212530712222115527015446 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of the LICENSE file distributed with ACL2. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; LICENSE for more details. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Science ; University of Texas at Austin ; Austin, TX 78701 U.S.A. (in-package "ACL2") ;; RAG - I changed this value from 6 to 9 to make room for the ;; positive-, negative-, and complex-irrationals. (defconst *number-of-numeric-type-set-bits* #+:non-standard-analysis 9 #-:non-standard-analysis 6) (defconst *type-set-binary-+-table-list* (let ((len (expt 2 *number-of-numeric-type-set-bits*))) (cons (list :header :dimensions (list len len) :maximum-length (1+ (* len len)) :default *ts-acl2-number* :name '*type-set-binary-+-table*) (type-set-binary-+-alist (1- len) (1- len) nil)))) (defconst *type-set-binary-+-table* (compress2 'type-set-binary-+-table *type-set-binary-+-table-list*)) (defconst *type-set-binary-*-table-list* (let ((len (expt 2 *number-of-numeric-type-set-bits*))) (cons (list :header :dimensions (list len len) :maximum-length (1+ (* len len)) :default *ts-acl2-number* :name '*type-set-binary-*-table*) (type-set-binary-*-alist (1- len) (1- len) nil)))) (defconst *type-set-binary-*-table* (compress2 'type-set-binary-*-table *type-set-binary-*-table-list*)) ;; RAG - As a consequence of the extra numeric arguments, I had to ;; change this table from 5 to 7, to make room for the positive ;; and negative irrationals. (defconst *type-set-<-table-list* #+:non-standard-analysis (cons (list :header :dimensions '(128 128) :maximum-length (1+ (* 128 128)) :name '*type-set-<-table*) (type-set-<-alist 127 127 nil)) #-:non-standard-analysis (cons (list :header :dimensions '(32 32) :maximum-length 1025 :name '*type-set-<-table*) (type-set-<-alist 31 31 nil)) ) (defconst *type-set-<-table* (compress2 'type-set-<-table *type-set-<-table-list*)) ; Essay on Enabling, Enabled Structures, and Theories ; The rules used by the system can be "enabled" and "disabled". In a ; break with Nqthm, this is true even of :COMPOUND-RECOGNIZER rules. ; We develop that code now. Some of the fundamental concepts here are ; that of "rule names" or "runes" and their associated numeric ; correspondents, numes. We also explain "mapping pairs," "rule name ; designators", "theories," (both "common theories" and "runic ; theories") and "enabled structures". (defun assoc-equal-cdr (x alist) ; Like assoc-equal but compares against the cdr of each pair in alist. (cond ((null alist) nil) ((equal x (cdar alist)) (car alist)) (t (assoc-equal-cdr x (cdr alist))))) (defun runep (x wrld) ; This function returns non-nil iff x is a rune, i.e., a "rule name," ; in wrld. When non-nil, the value of this function is the nume of ; the rune x, i.e., the index allocated to this rule name in the ; enabled array. This function returns nil on fake-runes! See the ; essay on fake-runes below. ; To clear up the confusion wrought by the proliferation of ; nomenclature surrounding rules and the ways by which one might refer ; to them, I have recently adopted more colorful nomenclature than the ; old "rule name," "rule index," "rule id," etc. To wit, ; rune (rule name): ; an object that is syntactically of the form (token symb . x), where ; token is one of the rule-class tokens, e.g., :REWRITE, :META, etc., ; symb is a symbolp with a 'runic-mapping-pairs property, and x is ; either nil or a positive integer that distinguishes this rune from ; others generated from different :rule-classes that share the same ; token and symb. We say that (token symb . x) is "based" on the ; symbol symb. Formally, rn is a rune iff it is of the form (& symb ; . &), symb is a symbolp with a non-nil runic-info and rn is in the ; range of the mapping-pairs of that runic-info. This is just another ; way of saying that the range of the mapping pairs of a symbol is a ; complete list of all of the runes based on that symbol. Each rule ; in the system has a unique rune that identifies it. The user may ; thus refer to any rule by its rune. An ordered list of runes is a ; theory (though we also permit some non-runes in theories presented ; by the user). Each rune has a status, enabled or disabled, as given ; by an enabled structure. I like the name "rune" because its ; etymology (from "rule name") is clear and it connotes an object that ; is at once obscure, atomic, and yet clearly understood by the ; scholar. ; nume (the numeric counterpart of a rune): ; a nonnegative integer uniquely associated with a rune. The nume of ; a rune tells us where in the enabled array we find the status of the ; rune. ; runic mapping pair: ; a pair, (nume . rune), consisting of a rune and its numeric ; counterpart, nume. The 'runic-mapping-pairs property of a symbol is ; the list of all runic mapping pairs whose runes are based on the ; given symbol. The 'runic-mapping-pairs value is ordered by ; ascending numes, i.e., the first nume in the list is the least. The ; primary role of runic mapping pairs is to make it more efficient to ; load a theory (ideally a list of runes) into an enabled structure. ; That process requires that we assemble an ACL2 array, i.e., an alist ; mapping array indices (numes) to their values (runes) in the array. ; We do that by collecting the runic mapping pairs of each rune in the ; theory. We also use these pairs to sort theories: theories are kept ; in descending nume order to make intersection and union easier. To ; sort a theory we replace each rune in it by its mapping pair, sort ; the result by descending cars, and then strip out the runes to obtain ; the answer. More on this when we discuss sort-by-fnume. ; event name: ; The symbol, symb, in a rune (token symb . x), is an event name. ; Some event names are allowed in theories, namely, those that are the ; base symbols of runes. In such usage, an event name stands for one ; or more runes, depending on what kind of event it names. For ; example, if APP is the name of a defun'd function, then when APP ; appears in a theory it stands for the rune (:DEFINITION APP). If ; ASSOC-OF-APP is a lemma name, then when it appears in a theory it ; stands for all the runes based on that name, e.g., if that event ; introduced two rewrite rules and an elim rule, then ASSOC-OF-APP ; would stand for (:REWRITE ASSOC-OF-APP . 1), (:REWRITE ASSOC-OF-APP ; . 2), and (:ELIM ASSOC-OF-APP). This use of event names allows them ; to be confused with rule names. ; Historical Footnote: In nqthm, the executable counterpart of the ; function APP actually had a distinct name, *1*APP, and hence we ; established the expectation that one could prevent the use of that ; "rule" while allowing the use of the other. We now use the runes ; (:DEFINITION APP) and (:EXECUTABLE-COUNTERPART APP) to identify ; those two rules added by a defun event. In fact, the defun adds a ; third rule, named by the rune (:TYPE-PRESCRIPTION APP). In fact, we ; were driven to the invention of runes as unique rule names when we ; added type-prescription lemmas. (cond ((and (consp x) (consp (cdr x)) (symbolp (cadr x))) (car (assoc-equal-cdr x (getprop (cadr x) 'runic-mapping-pairs nil 'current-acl2-world wrld)))) (t nil))) ; Essay on Fake-Runes ; The system has many built in rules that, for regularity, ought to ; have names and numes but don't because they can never be disabled. ; In addition, we sometimes wish to have a rune-like object we can use ; as a mark, without having to worry about the possibility that a ; genuine rune will come along with the same identity. Therefore, we ; have invented the notion of "fake-runes." Fake runes are constants. ; By convention, the constant name is always of the form ; *fake-rune-...* and the value of every fake rune is always ; (:FAKE-RUNE-... nil). Since no rule class will ever start with the ; words "fake rune" this convention will survive the introduction of ; all conceivable new rule classes. It is important that fake runes ; be based on the symbol nil. This way they are assigned the nume ; nil by fnume (below) and will always be considered enabled. The ; function runep does NOT recognize fake runes. Fake runes cannot be ; used in theories, etc. ; The fake runes are: ; *fake-rune-for-anonymous-enabled-rule* ; This fake rune is specially recognized by push-lemma and ignored. Thus, if ; you wish to invent a rule that you don't wish to name or worry will be ; disabled, give the rule this :rune and the :nume nil. ; *fake-rune-for-linear* ; This fake rune is a signal that linear arithmetic was used. ; *fake-rune-for-type-set* ; This fake rune is used by type-set to record that built-in facts about ; primitive functions were used. ; *fake-rune-for-nu-rewriter* ; This fake rune is used by the nth-update-rewriter to record the fact ; that either nth-update-nth or nth-update-nth-array was used. We do ; not want to use either of the actual runes for those two rules ; because one or both might be disabled and it would be disturbing to ; see a rune come into a proof if it is disabled. To turn off the ; nu-rewriter, the user should use the nu-rewriter-mode. ; WARNING: If more fake runes are added, deal with them in *fake-rune-alist*. (defmacro base-symbol (rune) ; The "base symbol" of the rune (:token symbol . x) is symbol. ; Note: The existence of this function and the next one suggest that ; runes are implemented abstractly. Ooooo... we don't know how runes ; are realy laid out. But this just isn't true. We use car to get ; the token of a rune and we use cddr to get x, above. But for some ; reason we defined and began to use base-symbol to get the base ; symbol. In any case, if the structure of runes is changed, all ; mention of runes will have to be inspected. `(cadr ,rune)) (defmacro strip-base-symbols (runes) `(strip-cadrs ,runes)) (deflabel executable-counterpart :doc ":Doc-Section Miscellaneous a rule for computing the value of a function~/ ~bv[] Examples: (:executable-counterpart length) ~ev[] which may be abbreviated in ~il[theories] as ~bv[] (length) ~ev[]~/ Every ~ilc[defun] introduces at least two rules used by the theorem prover. Suppose ~c[fn] is the name of a ~ilc[defun]'d function. Then ~c[(:definition fn)] is the rune (~pl[rune]) naming the rule that allows the simplifier to replace calls of ~c[fn] by its instantiated body. ~c[(:executable-counterpart fn)] is the ~il[rune] for the rule for how to evaluate the function on known constants. When typing ~il[theories] it is convenient to know that ~c[(fn)] is a runic designator that denotes ~c[(:executable-counterpart fn)]. ~l[theories]. If ~c[(:executable-counterpart fn)] is ~il[enable]d, then when applications of ~c[fn] to known constants are seen by the simplifier they are computed out by executing the Common Lisp code for ~c[fn] (with the appropriate handling of ~il[guard]s). Suppose ~c[fact] is defined as the factorial function. If the executable counterpart ~il[rune] of ~c[fact], ~c[(:executable-counterpart fact)], is ~il[enable]d when the simplifier encounters ~c[(fact 12)], then that term will be ``immediately'' expanded to ~c[479001600]. Note that even if subroutines of ~c[fn] have disabled executable counterparts, ~c[fn] will call their Lisp code nonetheless: once an executable counterpart function is applied, no subsidiary enable checks are made. Such one-step expansions are sometimes counterproductive because they prevent the anticipated application of certain lemmas about the subroutines of the expanded function. Such computed expansions can be prevented by disabling the executable counterpart ~il[rune] of the relevant function. For example, if ~c[(:executable-counterpart fact)] is ~il[disable]d, ~c[(fact 12)] will not be expanded by computation. In this situation, ~c[(fact 12)] may be rewritten to ~c[(* 12 (fact 11))], using the rule named ~c[(:definition fact)], provided the system's heuristics permit the introduction of the term ~c[(fact 11)]. Note that lemmas about multiplication may then be applicable (while such lemmas would be inapplicable to ~c[479001600]). In many proofs it is desirable to ~il[disable] the executable counterpart ~il[rune]s of certain functions to prevent their expansion by computation. ~l[executable-counterpart-theory]. Finally: What do we do about functions that are ``constrained'' rather than defined, such as the following? (~l[encapsulate].) ~bv[] (encapsulate (((foo *) => *)) (local (defun foo (x) x))) ~ev[] Does ~c[foo] have an executable counterpart? Yes: since the vast majority of functions have sensible executable counterparts, it was decided that ~st[all] functions, even such ``constrained'' ones, have executable counterparts. We essentially ``trap'' when such calls are inappropriate. Thus, consider for example: ~bv[] (defun bar (x) (if (rationalp x) (+ x 1) (foo x))) ~ev[] If the term ~c[(bar '3)] is encountered by the ACL2 rewriter during a proof, and if the ~c[:executable-counterpart] of ~c[bar] is ~il[enable]d, then it will be invoked to reduce this term to ~c['4]. However, if the term ~c[(bar 'a)] is encountered during a proof, then since ~c['a] is not a ~ilc[rationalp] and since the ~c[:executable-counterpart] of ~c[foo] is only a ``trap,'' then this call of the ~c[:executable-counterpart] of ~c[bar] will result in a ``trap.'' In that case, the rewriter will return the term ~c[(hide (bar 'a))] so that it never has to go through this process again. ~l[hide].~/") (deflabel world :doc ":Doc-Section Miscellaneous ACL2 property lists and the ACL2 logical database~/ The ACL2 logical world is a data structure that includes all logical content resulting from the ~il[command]s evaluated, back through and including initialization, but not including commands that have been undone (~pl[ubt]). Thus in particular, the world includes a represention of the current logical theory, as well as some extra-logical information such as the values of ACL2 ~il[table]s. The rest of this topic focuses on the structure of the the ACL2 world and, more generally, the ``world'' data structure. A ``world'' is a list of triples, each of the form ~c[(sym prop . val)], implementing the ACL2 notion of property lists. ACL2 permits the simultaneous existence of many property list worlds. ``The world'' is often used as a shorthand for ``the ACL2 logical world'' which is the particular property list world used within the ACL2 system to maintain a database that contiains rules, ~il[table]s, and so on.~/ Common Lisp provides the notion of ``property lists'' by which one can attach ``properties'' and their corresponding ``values'' to symbols. For example, one can arrange for the ~c['color] property of the symbol ~c['box-14] to be ~c['purple] and the ~c['color] property of the symbol ~c['triangle-7] to be ~c['yellow]. Access to property lists is given via the Common Lisp function ~c[get]. Thus, ~c[(get 'box-14 'color)] might return ~c['purple]. Property lists can be changed via the special form ~c[setf]. Thus, ~c[(setf (get 'box-14 'color) 'blue)] changes the Common Lisp property list configuration so that ~c[(get 'box-14 'color)] returns ~c['blue]. It should be obvious that ACL2 cannot provide this facility, because Common Lisp's ~c[get] ``function'' is not a function of its argument, but instead a function of some implicit state object representing the property list settings for all symbols. ACL2 provides the functions ~c[getprop] and ~c[putprop] which allow one to mimic the Common Lisp property list facility. However, ACL2's ~c[getprop] takes as one of its arguments a list that is a direct encoding of what was above called the ``state object representing the property list settings for all symbols.'' Because ACL2 already has a notion of ``~il[state]'' that is quite distinct from that used here, we call this property list object a ``world.'' A world is just a true list of triples. Each triple is of the form ~c[(sym prop . val)]. This world can be thought of as a slightly elaborated form of association list and ~c[getprop] is a slightly elaborated form of ~ilc[assoc] that takes two keys. When ~c[getprop] is called on a symbol, ~c[s], property ~c[p], and world, ~c[w], it scans ~c[w] for the first triple whose ~c[sym] is ~c[s] and ~c[prop] is ~c[p] and returns the corresponding ~c[val]. ~c[Getprop] has two additional arguments, one of which that controls what it returns if no such ~c[sym] and ~c[prop] exist in ~c[w], and other other of which allows an extremely efficient implementation. To set some property's value for some symbol, ACL2 provides ~c[putprop]. ~c[(putprop sym prop val w)] merely returns a new world, ~c[w'], in which ~c[(sym prop . val)] has been ~ilc[cons]ed onto the front of ~c[w], thus ``overwriting'' the ~c[prop] value of ~c[sym] in ~c[w] to ~c[val] and leaving all other properties in ~c[w] unchanged. One aspect of ACL2's property list arrangment is that it is possible to have many different property list worlds. For example, ~c['box-14] can have ~c['color] ~c['purple] in one world and can have ~c['color] ~c['yes] in another, and these two worlds can exist simultaneously because ~c[getprop] is explicitly provided the world from which the property value is to be extracted. The efficiency alluded to above stems from the fact that Common Lisp provides property lists. Using Common Lisp's provisions behind the scenes, ACL2 can ``install'' the properties of a given world into the Common Lisp property list state so as to make retrieval via ~c[getprop] very fast in the special case that the world provided to ~c[getprop] has been installed. To permit more than one installed world, each of which is permitted to be changed via ~c[putprop], ACL2 requires that worlds be named and these names are used to distinquish installed versions of the various worlds. At the moment we do not further document ~c[getprop] and ~c[putprop]. However, the ACL2 system uses a property list world, named ~c['current-acl2-world], in which to store the succession of user ~il[command]s and their effects on the logic. This world is often referred to in our ~il[documentation] as ``the world'' though it should be stressed that the user is permitted to have worlds and ACL2's is in no way distinguished except that the user is not permitted to modify it except via event ~il[command]s. The ACL2 world is part of the ACL2 ~il[state] and may be obtained via ~c[(w state)]. ~st[Warning]: The ACL2 world is very large. Its length as of this writing (Version 2.5) is over ~c[40,000] and it grows with each release. Furthermore, some of the values stored in it are pointers to old versions of itself. Printing ~c[(w state)] is something you should avoid because you likely will not have the patience to await its completion. For these practical reasons, the only thing you should do with ~c[(w state)] is provide it to ~c[getprop], as in the form ~bv[] (getprop sym prop default 'current-acl2-world (w state)) ~ev[] to inspect properties within it, or to pass it to ACL2 primitives, such as theory functions, where it is expected. Some ACL2 ~il[command] forms, such as theory expressions (~pl[theories]) and the values to be stored in tables (~pl[table]), are permitted to use the variable symbol ~c[world] freely with the understanding that when these forms are evaluated that variable is bound to ~c[(w state)]. Theoretically, this gives those forms complete knowledge of the current logical configuration of ACL2. However, at the moment, few world scanning functions have been documented for the ACL2 user. Instead, supposedly convenient macro forms have been created and documented. For example, ~c[(current-theory :here)], which is the theory expression which returns the currently ~il[enable]d theory, actually macroexpands to ~c[(current-theory-fn :here world)]. When evaluated with ~c[world] bound to ~c[(w state)], ~c[current-theory-fn] scans the current ACL2 world and computes the set of ~il[rune]s currently ~il[enable]d in it.") (deflabel rune :doc ":Doc-Section Theories a rule name~/ ~bv[] Examples: (:rewrite assoc-of-app) (:linear delta-aref . 2) (:definition length) (:executable-counterpart length) ~ev[]~/ Note: This topic discusses a basic notion of ``rule name'', or ``rune'' for short. Users often use abbrevitions for runes; for example, a ~il[theory] expression ~c[(DISABLE APPEND)] abbreviates the following set of runes: ~c[{(:DEFINITION BINARY-APPEND), (:INDUCTION BINARY-APPEND)}]. ~l[theories] for a discussion of so-called ``runic designators'', which include expressions like ~c[APPEND] (as above) as well as ~c[(APPEND)] (for the executable-counterpart of ~c[BINARY-APPEND]. Runic designators can also be abbreviations including ~c[(:d APPEND)], ~c[(:e APPEND)], ~c[(:i APPEND)], and ~c[(:t APPEND)], which designate the definition, executable-counterpart, induction, and type-prescription rules for ~c[BINARY-APPEND]. For a complete description of runic designators, ~pl[theories]; we return now to the more basic notion of a rune. Background: The theorem prover is driven from a database of rules. The most common rules are ~c[:]~ilc[rewrite] rules, which cause the simplifier to replace one term with another. ~il[Events] introduce rules into the database. For example, a ~ilc[defun] event may introduce runes for symbolically replacing a function call by its instantiated body, for evaluating the function on constants, for determining the type of a call of the function, and for the induction scheme introduced upon defining the function. ~ilc[Defthm] may introduce several rules, one for each of the ~c[:]~ilc[rule-classes] specified (where one rule class is specified if ~c[:]~ilc[rule-classes] is omitted, namely, ~c[:rewrite]). Every rule in the system has a name. Each name is a structured object called a ``rune,'' which is short for ``rule name''. Runes are always of the form ~c[(:token symbol . x)], where ~c[:token] is some keyword symbol indicating what kind of rule is named, ~c[symbol] is the event name that created the rule (and is called the ``base symbol'' of the rune), and ~c[x] is either ~c[nil] or a natural number that makes the rule name distinct from that of rules generated by other ~il[events] or by other ~c[:]~ilc[rule-classes] within the same event. For example, an event of the form ~bv[] (defthm name thm :rule-classes ((:REWRITE :COROLLARY term1) (:REWRITE :COROLLARY term2) (:ELIM :COROLLARY term3))) ~ev[] typically creates three rules, each with a unique rune. The runes are ~bv[] (:REWRITE name . 1), (:REWRITE name . 2), and (:ELIM name). ~ev[] However, a given formula may create more than one rule, and all rules generated by the same ~c[:corollary] formula will share the same rune. Consider the following example. ~bv[] (defthm my-thm (and (equal (foo (bar x)) x) (equal (bar (foo x)) x))) ~ev[] This is treated identically to the following. ~bv[] (defthm my-thm (and (equal (foo (bar x)) x) (equal (bar (foo x)) x)) :rule-classes ((:rewrite :corollary (and (equal (foo (bar x)) x) (equal (bar (foo x)) x))))) ~ev[] In either case, two rules are created: one rewriting ~c[(foo (bar x))] to ~c[x], and one rewriting ~c[(bar (foo x))] to ~c[x]. However, only a single rune is created, ~c[(:REWRITE MY-THM)], because there is only one rule class. But now consider the following example. ~bv[] (defthm my-thm2 (and (equal (foo (bar x)) x) (equal (bar (foo x)) x)) :rule-classes ((:rewrite :corollary (and (equal (foo (bar x)) x) (equal (bar (foo x)) x))) (:rewrite :corollary (and (equal (foo (bar (foo x))) (foo x)) (equal (bar (foo (bar x))) (bar x)))))) ~ev[] This time there are four rules created. The first two rules are as before, and are assigned the rune ~c[(:REWRITE MY-THM . 1)]. The other two rules are similarly generated for the second ~c[:corollary], and are assigned the rune ~c[(:REWRITE MY-THM . 2)]. The function ~ilc[corollary] will return the ~il[corollary] term associated with a given rune in a given ~il[world]. Example: ~bv[] (corollary '(:TYPE-PRESCRIPTION DIGIT-TO-CHAR) (w state)) ~ev[] However, the preferred way to see the corollary term associated with a rune or a name is to use ~c[:pf]; ~pl[pf]. The ~ilc[defun] event creates as many as four rules. ~c[(:definition fn)] is the rune given to the equality axiom defining the function, ~c[fn]. ~c[(:executable-counterpart fn)] is the rune given to the rule for computing ~c[fn] on known arguments. A type prescription rule may be created under the name ~c[(:type-prescription fn)], and an ~il[induction] rule may be created under the name ~c[(:induction fn)]. Runes may be individually ~il[enable]d and ~il[disable]d, according to whether they are included in the current theory. ~l[theories]. Thus, it is permitted to ~il[disable] ~c[(:elim name)], say, while enabling the other rules derived from name. Similarly, ~c[(:definition fn)] may be ~il[disable]d while ~c[(:executable-counterpart fn)] and the type prescriptions for ~c[fn] are ~il[enable]d. Associated with most runes is the formula justifying the rule named. This is called the ``~il[corollary] formula'' of the rune and may be obtained via the function ~ilc[corollary], which takes as its argument a rune and a property list ~il[world]. Also ~pl[pf]. The ~il[corollary] formula for ~c[(:rewrite name . 1)] after the ~ilc[defthm] event above is ~c[term1]. The corollary formulas for ~c[(:definition fn)] and ~c[(:executable-counterpart fn)] are always identical: the defining axiom. Some runes, e.g., ~c[(:definition car)], do not have corollary formulas. ~ilc[Corollary] returns ~c[nil] on such runes. In any case, the corollary formula of a rune, when it is non-~c[nil], is a theorem and may be used in the ~c[:use] and ~c[:by] ~il[hints]. Note: The system has many built in rules that, for regularity, ought to have names but don't because they can never be ~il[disable]d. One such rule is that implemented by the linear arithmetic package. Because many of our subroutines are required by their calling conventions to return the justifying rune, we have invented the notion of ``fake runes.'' Fake runes always have the base symbol ~c[nil], use a keyword token that includes the phrase ``fake-rune'', and are always ~il[enable]d. For example, ~c[(:fake-rune-for-linear nil)] is a fake rune. Occasionally the system will print a fake rune where a rune is expected. For example, when the linear arithmetic fake rune is reported among the rules used in a proof, it is an indication that the linear arithmetic package was used. However, fake runes are not allowed in ~il[theories], they cannot be ~il[enable]d or ~il[disable]d, and they do not have associated ~il[corollary] formulas. In short, despite the fact that the user may sometimes see fake runes printed, they should never be typed.~/") (deflabel rule-names :doc ":Doc-Section Theories How rules are named.~/ ~bv[] Examples: (:rewrite assoc-of-app) (:linear delta-aref . 2) (:definition length) (:executable-counterpart length) ~ev[]~/ ~l[rune].~/") (defun fnume (rune wrld) ; Rune has the shape of a rune. We return its nume. Actually, this function ; admits every fake-rune as a "rune" and returns nil on it (by virtue of the ; fact that the base-symbol of a fake rune is nil and hence there are no ; mapping pairs). This fact may be exploited by functions which have obtained ; a fake rune as the name of some rule and wish to know its nume so they can ; determine if it is enabled. More generally, this function returns nil if ; rune is not a rune in the given world, wrld. Nil is treated as an enabled ; nume by enabled-runep but not by active-runep. (car (assoc-equal-cdr rune (getprop (base-symbol rune) 'runic-mapping-pairs nil 'current-acl2-world wrld)))) (defun frunic-mapping-pair (rune wrld) ; Rune must be a rune in wrld. We return its mapping pair. (assoc-equal-cdr rune (getprop (base-symbol rune) 'runic-mapping-pairs nil 'current-acl2-world wrld))) (defun fn-rune-nume (fn nflg xflg wrld) ; Fn must be a function symbol, not a lambda expression. We return ; either the rune (nflg = nil) or nume (nflg = t) associated with ; either (:DEFINITION fn) (xflg = nil) or (:EXECUTABLE-COUNTERPART fn) ; (xflg = t). This function knows the layout of the runic mapping ; pairs by DEFUNS -- indeed, it knows the layout for all function ; symbols whether DEFUNd or not! See the Essay on the Assignment of ; Runes and Numes by DEFUNS. If fn is a constrained function we ; return nil for all combinations of the flags. (let* ((runic-mapping-pairs (getprop fn 'runic-mapping-pairs nil 'current-acl2-world wrld)) (pair (if xflg (cadr runic-mapping-pairs) (car runic-mapping-pairs)))) (if nflg (car pair) (cdr pair)))) (defun definition-runes (fns xflg wrld) (cond ((null fns) nil) (t (cons (fn-rune-nume (car fns) nil xflg wrld) (definition-runes (cdr fns) xflg wrld))))) (defun get-next-nume (lst) ; We return the next available nume in lst, which is a cdr of the ; current world. We scan down lst, looking for the most recently ; stored 'runic-mapping-pairs entry. Suppose we find it, ((n1 . ; rune1) (n2 . rune2) ... (nk . runek)). Then the next rune will get ; the nume nk+1, which is also just n1+k, where k is the length of the ; list of mapping pairs. Note: If we see (name runic-mapping-pairs . ; atm) where atm is an atom, then atm is :acl2-property-unbound or ; nil, and we keep going. Such tuples appear in part because in ; redefinition the 'runic-mapping-pairs property is "nil'd" out. (cond ((null lst) #+acl2-metering (meter-maid 'get-next-nume 100) 0) ((and (eq (cadr (car lst)) 'runic-mapping-pairs) (consp (cddr (car lst)))) #+acl2-metering (meter-maid 'get-next-nume 100) (+ (car (car (cddr (car lst)))) (length (cddr (car lst))))) (t #+acl2-metering (setq meter-maid-cnt (1+ meter-maid-cnt)) (get-next-nume (cdr lst))))) ; We now formalize the notion of "theory". We actually use two ; different notions of theory here. The first, which is formalized by ; the predicate theoryp, is what the user is accustomed to thinking of ; as a theory. Formally, it is a truelist of rule name designators, ; each of which designates a set of runes. The second is what we ; call a "runic theory" which is an ordered list of runes, where the ; ordering is by descending numes. We sometimes refer to theories as ; "common theories" to distinguish them from runic theories. To every ; common theory there corresponds a runic theory obtained by unioning ; together the runes designated by each element of the common theory. ; We call this the runic theory "corresponding" to the common one. (defun deref-macro-name (macro-name macro-aliases) (let ((entry (assoc-eq macro-name macro-aliases))) (if entry (cdr entry) macro-name))) (defun deref-macro-name-lst (macro-name-lst macro-aliases) (cond ((atom macro-name-lst) nil) (t (cons (deref-macro-name (car macro-name-lst) macro-aliases) (deref-macro-name-lst (cdr macro-name-lst) macro-aliases))))) (defconst *abbrev-rune-alist* '((:d . :definition) (:e . :executable-counterpart) (:i . :induction) (:t . :type-prescription))) (defun translate-abbrev-rune (x macro-aliases) (let ((kwd (and (consp x) (consp (cdr x)) (symbolp (cadr x)) (cdr (assoc-eq (car x) *abbrev-rune-alist*))))) (cond (kwd (list* kwd (deref-macro-name (cadr x) macro-aliases) (cddr x))) (t x)))) (defun rule-name-designatorp (x macro-aliases wrld) ; A rule name designator is an object which denotes a set of runes. ; We call that set of runes the "runic interpretation" of the ; designator. A rune, x, is a rule name designator, denoting {x}. A ; symbol, x, with a 'runic-mapping-pairs property is a designator and ; denotes either {(:DEFINITION x)} or else the entire list of runes in ; the runic-mapping-pairs, depending on whether there is a :DEFINITION ; rune. A symbol x that is a theory name is a designator and denotes ; the runic theory value. Finally, a singleton list, (fn), is a ; designator if fn is a function symbol; it designates ; {(:EXECUTABLE-COUNTERPART fn)}. ; For example, if APP is a function symbol then its runic ; interpretation is {(:DEFINITION APP)}. If ASSOC-OF-APP is a defthm ; event with, say, three rule classes then its runic interpretation is ; a set of three runes, one for each rule generated. The idea here is ; to maintain some consistency with the Nqthm way of disabling names. ; If the user disables APP then only the symbolic definition is ; disabled, not the executable counterpart, while if ASSOC-OF-APP is ; disabled, all such rules are disabled. ; Note: We purposely do not define a function "runic-interpretation" ; which returns runic interpretation of a designator. The reason is ; that we would have to cons that set up for every designator except ; theories. The main reason we'd want such a function is to define ; the runic theory corresponding to a common one. We do that below ; (in convert-theory-to-unordered-mapping-pairs1) and open-code "runic ; interpretation." (cond ((symbolp x) (cond ((getprop (deref-macro-name x macro-aliases) 'runic-mapping-pairs nil 'current-acl2-world wrld) t) (t (not (eq (getprop x 'theory t 'current-acl2-world wrld) t))))) ((and (consp x) (null (cdr x)) (symbolp (car x))) (let ((fn (deref-macro-name (car x) macro-aliases))) (and (function-symbolp fn wrld) (runep (list :executable-counterpart fn) wrld)))) (t (let ((x (translate-abbrev-rune x macro-aliases))) (runep x wrld))))) (defun theoryp1 (lst macro-aliases wrld) (cond ((atom lst) (null lst)) ((rule-name-designatorp (car lst) macro-aliases wrld) (theoryp1 (cdr lst) macro-aliases wrld)) (t nil))) (defun theoryp (lst wrld) ; A (common) theory is a truelist of rule name designators. It is ; possible to turn a theory into a list of runes (which is, itself, a ; theory). That conversion is done by coerce-to-runic-theory. (theoryp1 lst (macro-aliases wrld) wrld)) (defun theoryp!1 (lst fail-flg macro-aliases wrld) (cond ((atom lst) (and (not fail-flg) (null lst))) ((rule-name-designatorp (car lst) macro-aliases wrld) (theoryp!1 (cdr lst) fail-flg macro-aliases wrld)) ((and (symbolp (car lst)) ; Do not use the function macro-args below, as it can cause a hard error! (not (eq (getprop (car lst) 'macro-args t 'current-acl2-world wrld) t))) (prog2$ (cw "~|~%**NOTE**: The name ~x0 is a macro. See :DOC ~ add-macro-alias if you want it to be associated with a ~ function name." (car lst)) (theoryp!1 (cdr lst) t macro-aliases wrld))) (t (prog2$ (cw "~|~%**NOTE**:~%The name ~x0 does not ~ designate a rule or non-empty list of rules." (car lst)) (theoryp!1 (cdr lst) t macro-aliases wrld))))) (defun theoryp! (lst wrld) (theoryp!1 lst nil (macro-aliases wrld) wrld)) ; Now we define what a "runic theory" is. (defun runic-theoryp1 (prev-nume lst wrld) ; We check that lst is an ordered true list of runes in wrld, where ; the ordering is by descending numes. Prev-nume is the nume of the ; previously seen element of lst (or nil if we are at the top-level). (cond ((atom lst) (null lst)) (t (let ((nume (runep (car lst) wrld))) (cond ((and nume (or (null prev-nume) (> prev-nume nume))) (runic-theoryp1 nume (cdr lst) wrld)) (t nil)))))) (defun runic-theoryp (lst wrld) ; A "runic theory" (wrt wrld) is an ordered truelist of runes (wrt wrld), where ; the ordering is that imposed by the numes of the runes, greatest numes first. ; This function returns t or nil according to whether lst is a runic theory in ; wrld. Common theories are converted into runic-theories in order to do such ; operations as union and intersection. Our theory processing functions all ; yield runic-theories. We can save some time in those functions by checking ; if an input theory is in fact a runic theory: if so, we need not sort it. (runic-theoryp1 nil lst wrld)) ; When we start manipulating theories, e.g., unioning them together, ; we will actually first convert common theories into runic theories. ; We keep runic theories ordered so it is easier to intersect and ; union them. However, this raises a slighly technical question, ; namely the inefficiency of repeatedly going to the property lists of ; the basic symbols of the runes to recover (by a search through the ; mapping pairs) the measures by which we compare runes (i.e., the ; numes). We could order theories lexicographically -- there is no ; reason that theories have to be ordered by nume until it is time to ; load the enabled structure. We could also obtain the measure of ; each rune and cons the two together into a mapping pair and sort ; that list on the cars. This would at least save the repeated ; getprops at the cost of copying the list twice (once to pair the ; runes with their numes and once to strip out the final list of ; runes). ; We have compared these three schemes in a slightly simpler setting: ; sorting lists of symbols. The sample list was the list of all event ; names in the initial world, i.e., every symbol in the initial world ; with an 'absolute-event-number property. The lexicographic ; comparison was done with string<. The measure (analogous to the ; nume) was the 'absolute-event-number. We used exactly the same tail ; recursive merge routine used here, changing only the comparator ; expression. The version that conses the nume to the rune before ; sorting paid the price of the initial and final copying. The times ; to sort the 2585 symbols were: ; lexicographic: 1.29 seconds ; getprops: 1.18 seconds ; cars: 0.68 seconds ; We have decided to go the car route. The argument that it does a ; lot of unnecessary consing is unpersuasive in light of the amount of ; consing done by sorting. For example right off the bat in sort we ; divide the list into its evens and odds, thus effectively copying ; the entire list. The point is that as it has always been coded, ; sort produces a lot of garbaged conses, so it is not as though ; copying the list twice is a gross insult to the garbage collector. ; We exhibit some performance measures of our actual theory manipulation ; functions later. See Essay on Theory Manipulation Performance. ; Consider a runic theory. We want to "augment" it by consing onto ; every rune its nume. Common theories cannot be augmented until they ; are converted into runic ones. Naively, then we want to consider two ; transformations: how to convert a common theory to a runic one, and ; how to augment a runic theory. It turns out that the first ; transformation is messier than you'd think due to the possibility ; that distinct elements of the common theory designate duplicate ; runes. More on this later. But no matter what our final design, we ; need the second capability, since we expect that the theory ; manipulation functions will often be presented with runic theories. ; So we begin by augmentation of runic theories. ; Our goal is simply to replace each rune by its frunic-mapping-pair. ; But frunic-mapping-pair has to go to the property list of the basic ; symbol of the rune and then search through the 'runic-mapping-pairs ; for the pair needed. But in a runic theory, it will often be the ; case that adjacent runes have the same symbol, e.g., (:REWRITE LEMMA ; . 1), (:REWRITE LEMMA . 2), ... Furthermore, the second rune will ; occur downstream of the first in the 'runic-mapping-pairs of their ; basic symbol. So by keeping track of where we found the last ; mapping pair we may be able to find the next one faster. (defun find-mapping-pairs-tail1 (rune mapping-pairs) ; Rune is a rune and mapping-pairs is some tail of the ; 'runic-mapping-pairs property of its basic symbol. Furthermore, we ; know that we have not yet passed the pair for rune in mapping-pairs. ; We return the tail of mapping-pairs whose car is the pair for rune. (cond ((null mapping-pairs) (er hard 'find-mapping-pairs-tail "We have exhausted the mapping-pairs of the basic symbol ~ of ~x0 and failed to find that rune." rune)) ((equal rune (cdr (car mapping-pairs))) mapping-pairs) (t (find-mapping-pairs-tail1 rune (cdr mapping-pairs))))) (defun find-mapping-pairs-tail (rune mapping-pairs wrld) ; Rune is a rune and mapping-pairs is some tail of the ; 'runic-mapping-pairs property of some basic symbol -- but not ; necessarily rune's. If it is rune's then rune has not yet been seen ; among those pairs. If it is not rune's, then we get rune's from ; world. In any case, we return a mapping-pairs list whose car is the ; mapping pair for rune. (cond ((and mapping-pairs (eq (base-symbol rune) (cadr (cdr (car mapping-pairs))))) (find-mapping-pairs-tail1 rune mapping-pairs)) (t (find-mapping-pairs-tail1 rune (getprop (base-symbol rune) 'runic-mapping-pairs nil 'current-acl2-world wrld))))) (defun augment-runic-theory1 (lst mapping-pairs wrld ans) ; Lst is a runic theory. We iteratively accumulate onto ans the ; mapping pair corresponding to each element of lst. Mapping-pairs is ; the tail of some 'runic-mapping-pairs property and is used to speed ; up the retrieval of the pair for the first rune in lst. See ; find-mapping-pairs-tail for the requirements on mapping-pairs. The ; basic idea is that as we cdr through lst we also sweep through ; mapping pairs (they are ordered the same way). When the rune we get ; from lst is based on the same symbol as the last one, then we find ; its mapping pair in mapping-pairs. When it is not, we switch our ; attention to the 'runic-mapping-pairs of the new basic symbol. (cond ((null lst) ans) (t (let ((mapping-pairs (find-mapping-pairs-tail (car lst) mapping-pairs wrld))) (augment-runic-theory1 (cdr lst) (cdr mapping-pairs) wrld (cons (car mapping-pairs) ans)))))) (defun augment-runic-theory (lst wrld) ; We pair each rune in the runic theory lst with its nume, returning an ; augmented runic theory. (augment-runic-theory1 (reverse lst) nil wrld nil)) ; Ok, so now we know how to augment a runic theory. How about ; converting common theories to runic ones? That is harder because of ; the duplication problem. For example, '(APP APP) is a common ; theory, but the result of replacing each designator by its rune, ; '((:DEFINITION app) (:DEFINITION app)), is not a runic theory! It ; gets worse. Two distict designators might designate the same rune. ; For example, LEMMA might designate a collection of :REWRITE rules ; while (:REWRITE LEMMA . 3) designates one of those same rules. To ; remove duplicates we actually convert the common theory first to a ; list of (possibly duplicated and probably unordered) mapping pairs ; and then use a bizarre sort routine which removes duplicates. While ; converting a common theory to a unordered and duplicitous list of ; mapping pairs we simply use frunic-mapping-pair to map from a rune ; to its mapping pair; that is, we don't engage in the clever use of ; tails of the mapping pairs properties because we don't expect to see ; too many runes in a common theory, much less for two successive ; runes to be ordered properly. (defconst *bad-runic-designator-string* "This symbol was expected to be suitable for theory expressions; see :DOC ~ theories, in particular the discussion of runic designators. One possible ~ source of this problem is an attempt to include an uncertified book with a ~ deftheory event that attempts to use the above symbol in a deftheory event.") (defun convert-theory-to-unordered-mapping-pairs1 (lst macro-aliases wrld ans) ; This is the place we give meaning to the "runic interpretation" of a ; rule name designator. Every element of lst is a rule name ; designator. (cond ((null lst) ans) ((symbolp (car lst)) (let ((temp (getprop (deref-macro-name (car lst) macro-aliases) 'runic-mapping-pairs nil 'current-acl2-world wrld))) (cond ((and temp (eq (car (cdr (car temp))) :DEFINITION) (eq (car (cdr (cadr temp))) :EXECUTABLE-COUNTERPART)) (convert-theory-to-unordered-mapping-pairs1 (cdr lst) macro-aliases wrld (if (equal (length temp) 4) ; Then we have an :induction rune. See the Essay on the Assignment of Runes ; and Numes by DEFUNS. (cons (car temp) (cons (cadddr temp) ans)) (cons (car temp) ans)))) (temp (convert-theory-to-unordered-mapping-pairs1 (cdr lst) macro-aliases wrld (revappend temp ans))) (t ; In this case, we know that (car lst) is a theory name. Its 'theory ; property is the value of the theory name and is a runic theory. We ; must augment it. The twisted use of ans below -- passing it into ; the accumulator of the augmenter -- is permitted since we don't care ; about order. (convert-theory-to-unordered-mapping-pairs1 (cdr lst) macro-aliases wrld (augment-runic-theory1 (reverse (getprop (car lst) 'theory `(:error ,*bad-runic-designator-string*) 'current-acl2-world wrld)) nil wrld ans)))))) ((null (cdr (car lst))) (convert-theory-to-unordered-mapping-pairs1 (cdr lst) macro-aliases wrld (cons (cadr (getprop (deref-macro-name (car (car lst)) macro-aliases) 'runic-mapping-pairs `(:error ,*bad-runic-designator-string*) 'current-acl2-world wrld)) ans))) (t (convert-theory-to-unordered-mapping-pairs1 (cdr lst) macro-aliases wrld (cons (frunic-mapping-pair (translate-abbrev-rune (car lst) macro-aliases) wrld) ans))))) (defun convert-theory-to-unordered-mapping-pairs (lst wrld) ; This function maps a common theory into a possibly unordered and/or ; duplicitous list of mapping pairs. (convert-theory-to-unordered-mapping-pairs1 lst (macro-aliases wrld) wrld nil)) ; Now we develop a merge sort routine that has four interesting ; properties. First, it sorts arbitrary lists of pairs, comparing on ; their cars which are assumed to be rationals. Second, it can be ; told whether to produce an ascending order or a descending order. ; Third, it deletes all but one occurrence of any element with the ; same car as another. Fourth, its merge routine is tail recursive ; and so can handle very long lists. (The sort routine is not tail ; recursive, but it cuts the list in half each time and so can handle ; long lists too.) (defun duplicitous-cons-car (x y) ; This is like (cons x y) in that it adds the element x to the list y, ; except that it does not if the car of x is the car of the first element ; of y. (cond ((equal (car x) (caar y)) y) (t (cons x y)))) (defun duplicitous-revappend-car (lst ans) ; Like revappend but uses duplicitous-cons-car rather than cons. (cond ((null lst) ans) (t (duplicitous-revappend-car (cdr lst) (duplicitous-cons-car (car lst) ans))))) (defun duplicitous-merge-car (parity lst1 lst2 ans) ; Basic Idea: Lst1 and lst2 must be appropriately ordered lists of ; pairs. Comparing on the cars of respective pairs, we merge the two ; lists, deleting all but one occurrence of any element with the same ; car as another. ; Terminology: Suppose x is some list of pairs and that the car of ; each pair is a rational. We say x is a "measured list" because the ; measure of each element is given by the car of the element. We ; consider two orderings of x. The "parity t" ordering is that in ; which the cars of x are ascending. The "parity nil" ordering is ; that in which the cars of x are descending. E.g., in the parity t ; ordering, the first element of x has the least car and the last ; element of x has the greatest. ; Let lst1 and lst2 be two measured lists. This function merges lst1 ; and lst2 to produce output in the specified parity. However, it ; assumes that its two main inputs, lst1 and lst2, are ordered in the ; opposite parity. That is, if we are supposed to produce output that ; is ascending (parity = t) then the input must be descending (parity ; = nil). This odd requirement allows us to do the merge in a tail ; recursive way, accumulating the answers onto ans. We do it tail ; recursively because we are often called upon to sort huge lists and ; the naive approach has blown the stack of AKCL. (cond ((null lst1) (duplicitous-revappend-car lst2 ans)) ((null lst2) (duplicitous-revappend-car lst1 ans)) ((if parity (> (car (car lst1)) (car (car lst2))) (< (car (car lst1)) (car (car lst2)))) (duplicitous-merge-car parity (cdr lst1) lst2 (duplicitous-cons-car (car lst1) ans))) (t (duplicitous-merge-car parity lst1 (cdr lst2) (duplicitous-cons-car (car lst2) ans))))) (defun duplicitous-sort-car (parity lst) ; Let lst be a list of runes. If parity = t, we sort lst so that the ; numes of the resulting list are ascending; if parity = nil, the ; numes of the resulting list are descending. ; Note: This function is neat primarily because the merge function is ; tail recursive. It is complicated by the entirely extraneous ; requirement that it delete duplicates. The neat thing is that as it ; descends through lst, cutting it in half each time, it recursively ; orders the parts with the opposite sense of ordering. That is, to ; sort into ascending order it recursively sorts the two parts into ; descending order, which it achieves by sorting their parts into ; ascending order, etc. (cond ((null (cdr lst)) lst) (t (duplicitous-merge-car parity (duplicitous-sort-car (not parity) (evens lst)) (duplicitous-sort-car (not parity) (odds lst)) nil)))) (defun augment-theory (lst wrld) ; See also related function runic-theory. ; Given a (common) theory we convert it into an augmented runic ; theory. That is, we replace each designator in lst by the ; appropriate runes, pair each rune with its nume, sort the result and ; remove duplications. In the special case that lst is in fact a ; runic theory -- i.e., is already a properly sorted list of runes -- ; we just augment it directly. We expect this case to occur often. ; The various theory manipulation functions take common theories as ; their inputs but produce runic theories as their outputs. ; Internally, they all operate by augmenting the input theory, ; computing with the augmented theory, and then dropping down to the ; corresponding runic theory at the end with strip-cdrs. Thus if two ; such functions are nested in a user-typed theory expression, the ; inner one will generally have non-runic user-typed input but will ; produce runic output as input for the next one. By recognizing ; runic theories as a special case we hope to improve the efficiency ; with which theory expressions are evaluated, by saving the sorting. (declare (xargs :guard (theoryp lst wrld))) (cond ((runic-theoryp lst wrld) (augment-runic-theory lst wrld)) (t (duplicitous-sort-car nil (convert-theory-to-unordered-mapping-pairs lst wrld))))) (defmacro assert$-runic-theoryp (runic-theory-expr wrld) ; Comment out one of the following two definitions. ;;; Faster, without checking: (declare (ignore wrld)) runic-theory-expr ;;; Slower, with checking: ; `(let ((thy ,runic-theory-expr)) ; (assert$ (runic-theoryp thy ,wrld) ; thy)) ) (defun runic-theory (lst wrld) ; Lst is a common theory. We convert it to a runic theory. ; See also related function augment-theory. (cond ((runic-theoryp lst wrld) lst) (t (assert$-runic-theoryp (strip-cdrs (duplicitous-sort-car nil (convert-theory-to-unordered-mapping-pairs lst wrld))) wrld)))) ; We now develop the foundations of the concept that a rune is "enabled" in the ; current theory. In ACL2, the user can get "into" a theory with the in-theory ; event, which is similar in spirit to in-package but selects a theory as the ; "current" theory. A rune is said to be "enabled" if it is a member of the ; runic theory corresponding to the current (common) theory and is said to be ; "disabled" otherwise. ; Historical Note about Nqthm ; Nqthm had no explicit notion of the current theory. However, implicitly, ; nqthm contained a current theory and the events ENABLE and DISABLE allowed ; the user to add a name to it or delete a name from it. The experimental ; xnqthm, mentioned elsewhere in this system, introduced the notion of theories ; and tied them to enabling and disabling, following suggestions and patches ; implemented by Bill Bevier during the Kit proofs (and implemented in ; Pc-Nqthm, and extended in Nqthm-1992). The ACL2 notion of theory is much ; richer because it allows one to compute the value of theories using functions ; defined within the logic. (end of note) ; Suppose we have a theory which has been selected as current. This may be the ; globally current theory, as set by the in-theory event, or it may be a ; locally current theory, as set by the in-theory hint to defthm or defun. We ; must somehow process the current theory so that we can quickly answer the ; question "is rune enabled?" We now develop the code for doing that. ; The structure defined below is used as a fast way to represent a theory: (defrec enabled-structure ; WARNING: Keep this in sync with enabled-structurep. ((index-of-last-enabling . theory-array) (array-name . array-length) array-name-root . array-name-suffix) t) ; The following invariant is maintained in all instances of this structure. ; Theory-array is an array1p whose array length is array-length. Furthermore ; array-name is a symbol of the form rootj, root is the array-name-root (as a ; list of characters) and j is the array-name-suffix (which however is not ; always used; see load-theory-into-enabled-structure). Thus, if i is a ; nonnegative integer less than array-length, then (acl2-aref1 array-name ; theory-array i) has a satisfied guard. Furthermore, important to efficiency ; but irrelevant to correctness, it will always be the case that the von ; Neumann array associated with array-name is in fact theory-array. Thus the ; above expression executes quickly. To get a new array name, should one ever ; be needed, it can suffice to increment the array-name-suffix and build a name ; from that new value. However, if we hope to use parallel evaluation in the ; waterfall, we instead make a unique name based on each clause-id. See ; load-theory-into-enabled-structure. ; The theory-array of an enabled-structure for a given common theory is (except ; for the header entry) just the augmented runic theory corresponding to the ; given common theory. That is, the ACL2 array alist we need to construct for ; a theory maps each array index to a non-nil value. The non-nil value we ; choose is in fact the corresponding rune. It would suffice, for purposes of ; enabling, to store T in the array to signify enabledness. By storing the ; rune itself we make it possible to determine what runic theory is in the ; array. (There is no general purpose way to map from a nume to its rune ; (short of looking through the whole world).) ; The global variable 'global-enabled-structure contains an instance of the ; enabled-structure record in which the array-name is ENABLED-ARRAY-0, ; array-name-root is the list of characters in "ENABLED-ARRAY-" and the ; array-name-suffix is 0. A rune with nume n is (globally) enabled in the ; current world iff either n is greater than the index-of-last-enabling or ; array[n] is non-nil. This is just the computation done by enabled-numep, ; below. ; The in-theory event loads the 'global-enabled-structure with the theory-value ; and sets the index-of-last-enabling to the maximum nume at that time. This ; structure is passed into prove and thus into rewrite, etc. ; When an in-theory hint setting is provided we change the array name ; appropriately and load the local theory into that structure. We flush each ; such array in order to allow it to be garbage collected; see ; restore-hint-settings-in-pspv. ; Historical Note about Nqthm ; In nqthm we solved this problem by having a list of temporarily disabled ; names which was bound when there were local enabling hint settings. That ; implementation suffered because if hundreds of names were enabled locally the ; time spent searching the list was excessive. In xnqthm we solved that ; problem by storing the enabled status on the property list of each name. We ; could do that here. However, that implementation suffered from the fact that ; if a proof attempt was aborted then we had to carefully clean up the property ; list structures so that they once again reflected the current (global) ; theory. The beauty of the ACL2 approach is that local hint settings have no ; affect on the global theory and yet involve no overhead. ; A delicacy of the current implementation however concerns the relation ; between the global enabled structure and undoing. If the world is backed up ; to some previous point, the 'global-enabled-structure extant there is exposed ; and we are apparently ready to roll. However, the von Neumann array named by ; that structure may be out-dated in the sense that it contains a now undone ; theory. Technically there is nothing wrong with this, but if we let it ; persist things would be very slow because the attempt to access the ; applicative array would detect that the von Neumann array is out of date and ; would result in a linear search of the applicative array. We must therefore ; compress the applicative array (and hence reload the von Neumann one) ; whenever we back up. ; Finally, there is one last problem. Eventually the array-size of the array ; in one of these structures will be too small. This manifests itself when the ; maximum rule index at the time we load the structure is equal to or greater ; than the array-length. At that time we grow the array size by 500. ; Here is how we use an enabled structure, ens, to determine if a nume, rune, ; or function is enabled. (defun enabled-numep (nume ens) ; This function takes a nume (or nil) and determines if it is enabled ; in the enabled structure ens. We treat nil as though it were ; enabled. (cond ((null nume) t) ((> (the-fixnum nume) (the-fixnum (access enabled-structure ens :index-of-last-enabling))) t) (t (aref1 (access enabled-structure ens :array-name) (access enabled-structure ens :theory-array) (the-fixnum nume))))) (defun enabled-arith-numep (nume ens) ; This function takes a nume (or nil) and determines if it is enabled in the ; enabled structure ens. We treat nil as though it were enabled. In current ; usage, ens is always the global arithmetic theory. Any nume created since ; the most recent in-arithmetic-theory is considered disabled. The normal ; enabled-numep would treat these more recent numes as enabled. ; Our calls of the-fixnum assume that nume is less than about 2 billion. That ; seems like a safe assumption! (cond ((null nume) t) ((> (the-fixnum nume) (the-fixnum (access enabled-structure ens :index-of-last-enabling))) nil) (t (aref1 (access enabled-structure ens :array-name) (access enabled-structure ens :theory-array) (the-fixnum nume))))) (defun enabled-runep (rune ens wrld) ; This takes a rune and determines if it is enabled in the enabled structure ; ens. Since fnume returns nil on fake-runes, this function answers that a ; fake rune is enabled. See also active-runep. (enabled-numep (fnume rune wrld) ens)) (defmacro active-runep (rune) ; This takes a rune and determines if it is enabled in the enabled structure ; ens. Unlike enabled-runep, this returns nil if the rune is a fake-rune or is ; not a runep in the given wrld. ":Doc-Section Theories check that a ~il[rune] exists and is ~il[enable]d~/ ~bv[] Example: (active-runep '(:rewrite left-to-right))~/ General Form: (active-runep rune) ~ev[] where ~c[rune] has the shape of a ~il[rune]. This macro expands to an expression using the variables ~c[ens] and ~c[state], and returns non-~c[nil] when the given rune exists and is ~il[enable]d (according to the given ``enabled structure,'' ~c[ens], and the current logical ~il[world] of the given ~ilc[state]). ~l[theory-invariant] for how this macro can be of use.~/" `(let* ((rune ,rune) (nume (and (consp rune) (consp (cdr rune)) (symbolp (cadr rune)) ; The tests above guard the call of fnume just below, the same way that runep ; guards the computation made in its body from the property list. (fnume rune (w state))))) (and nume (enabled-numep nume ens)))) (defun enabled-xfnp (fn ens wrld) ; Fn must be either a function symbol or lambda expression, i.e., something you ; might get from ffn-symb of a term. If fn is a lambda expression or a ; constrained function symbol, we return t. Otherwise, we consider ; (:EXECUTABLE-COUNTERPART fn), and answer whether it is enabled. ; Note: This function exploits the fact that nil is considered enabled by ; enabled-numep. ; Note: Suppose you want to determine whether (:DEFINITION fn) is enabled. ; Perhaps you really want to know if the latest definition rule for fn that has ; non-nil :install-body field is enabled; and you may also want to ask other ; questions about the def-body of fn. Then this function is not the one ; to use! (cond ((flambdap fn) t) (t (enabled-numep (fn-rune-nume fn t t wrld) ens)))) (mutual-recursion (defun sublis-var! (alist term ens wrld ttree) ; This function applies alist to term and evaluates any ground subexpressions ; in that result. We return (mv term' flg ttree') where term' is the resulting ; term, ttree' is an extension of ttree containing the executable counterparts ; used, and flg is t iff term' is a quoted constant. We avoid running disabled ; functions. The flg result is probably not interesting to callers outside of ; this nest. ; This function's behavior on lambda applications is a little strange. ; Consider ; 1. ((lambda (x) (+ (+ 1 1) x)) 1) ==> 3 ; 2. ((lambda (x) (+ (+ 1 1) (undef x))) 1) ==> (+ 2 (undef 1)) ; 3. ((lambda (x) (+ (+ 1 1) x)) a) ==> ((lambda (x) (+ (+ 1 1) x)) a) ; Note: these examples are merely suggestive; binary-+ is a primitive and ; cons-term evaluates it. ; If the actuals to a lambda application are all constants, we take a chance ; and open up the lambda. See example 1. This will generally produce a ; constant. But of course, that path through the lambda body may lead to ; constrained or disabled functions and then we'll get an expanded lambda, as ; in 2. But if the actuals to the lambda are not all constants, we don't even ; try, as in 3. This means that constant subexpressions inside the lambda body ; are still present, even though partial evaluation might lead even to a ; constant. (cond ((variablep term) (let ((a (assoc-eq term alist))) (cond (a (mv (cdr a) (quotep (cdr a)) ttree)) (t (mv term nil ttree))))) ((fquotep term) (mv term t ttree)) ((flambda-applicationp term) (mv-let (args flg ttree) (sublis-var!-lst alist (fargs term) ens wrld ttree) (cond (flg ; (all-quoteps args) (sublis-var! (pairlis$ (lambda-formals (ffn-symb term)) args) (lambda-body (ffn-symb term)) ens wrld ttree)) (t (mv (cons-term (ffn-symb term) args) nil ttree))))) ((eq (ffn-symb term) 'if) (mv-let (arg1 flg1 ttree) (sublis-var! alist (fargn term 1) ens wrld ttree) (cond (flg1 (if (cadr arg1) (sublis-var! alist (fargn term 2) ens wrld ttree) (sublis-var! alist (fargn term 3) ens wrld ttree))) (t (mv-let (arg2 flg2 ttree) (sublis-var! alist (fargn term 2) ens wrld ttree) (mv-let (arg3 flg3 ttree) (sublis-var! alist (fargn term 3) ens wrld ttree) (declare (ignore flg3)) ; We optimize (if x y y) just because we can. We could do a lot more ; if-optimization here if it turns out to be needed. (cond ((equal arg2 arg3) ; If arg2 and arg3 are the same, then they are both quotes or not, so flg2 ; suffices for the resulting flag. (mv arg2 flg2 ttree)) (t (mv (fcons-term* 'if arg1 arg2 arg3) nil ttree))))))))) (t (mv-let (args flg ttree) (sublis-var!-lst alist (fargs term) ens wrld ttree) (cond ; The following test was taken from rewrite with a few modifications ; for the formals used. ((and flg ; (all-quoteps args) (logicalp (ffn-symb term) wrld) ; maybe fn is being admitted (enabled-xfnp (ffn-symb term) ens wrld) ; We don't mind disallowing constrained functions that have attachments, ; because the call of ev-fncall-w below disallows the use of attachments (last ; parameter, aok, is nil). (not (getprop (ffn-symb term) 'constrainedp nil 'current-acl2-world wrld))) (mv-let (erp val) (pstk (ev-fncall-w (ffn-symb term) (strip-cadrs args) wrld nil ; user-stobj-alist nil t t nil)) (cond (erp (mv (cons-term (ffn-symb term) args) nil ttree)) (t (mv (kwote val) t (push-lemma (fn-rune-nume (ffn-symb term) nil t wrld) ttree)))))) (t (mv (cons-term (ffn-symb term) args) nil ttree))))))) (defun sublis-var!-lst (alist lst ens wrld ttree) (cond ((null lst) (mv nil t ttree)) (t (mv-let (x flg1 ttree) (sublis-var! alist (car lst) ens wrld ttree) (mv-let (y flg2 ttree) (sublis-var!-lst alist (cdr lst) ens wrld ttree) (mv (cons x y) (and flg1 flg2) ttree)))))) ) ; Before we develop the code for loading a theory into an enabled ; structure, we put down code for warning when leaving a 0-ary ; function disabled while its executable counterpart is enabled. (defun theory-warning-fns-aux (runes1 runes2 max-nume nume prev-rune1 prev-rune2 w acc) ; See the comment in theory-warning-fns for a general discussion, in particular ; of (1), (2), and (3) below. We apply reverse to the returned accumulator in ; order to return function symbols in the order in which they were defined (a ; minor aesthetic preference). (declare (type (signed-byte 30) nume)) (cond ((eql nume max-nume) (reverse acc)) (t (let* ((found1 (eql (caar runes1) nume)) (found2 (eql (caar runes2) nume)) (curr-rune1 (and found1 (cdar runes1))) (curr-rune2 (and found2 (cdar runes2))) (rest-runes1 (if found1 (cdr runes1) runes1)) (rest-runes2 (if found2 (cdr runes2) runes2))) (theory-warning-fns-aux rest-runes1 rest-runes2 max-nume (1+f nume) curr-rune1 curr-rune2 w (if (and (eq (car curr-rune2) :executable-counterpart) (null prev-rune2) ; (1) (not (and curr-rune1 (null prev-rune1))) ; (2) (null (formals (cadr curr-rune2) w))) ; (3) (cons (cadr curr-rune2) acc) acc)))))) (defun theory-warning-fns (ens1 ens2 w) ; Here is our strategy for producing warnings when an in-theory event or hint ; leaves us with a 0-ary function whose :executable-counterpart is enabled but ; :definition is not. We assume that we have our hands on two enabled ; structures: the pre-existing one, which we call ens1, and the one created by ; the in-theory event or hint, which we call ens2 and is returned by ; load-theory-into-enabled-structure. Note that the length of ens2 is at least ; as great as the length of ens1. We walk through all indices (numes) of ens1. ; When do we find something worth warning about? We only have a problem when ; we find an enabled :executable-counterpart at the current nume in ens2. By ; the Essay on the Assignment of Runes and Numes by DEFUNS, we know that the ; previous nume represents the corresponding :definition. Three conditions ; must now hold: (1) The preceding (:definition) rune is disabled in ens2; (2) ; The same problem was not already present in ens1; and (3) The function in ; question is 0-ary. ; We deal with the arrays as lists ordered by car, rather than using aref1, ; because the two arrays may have the same name in which case the first one is ; probably out of date. We apply cdr to remove the headers. (theory-warning-fns-aux (cdr (access enabled-structure ens1 :theory-array)) (cdr (access enabled-structure ens2 :theory-array)) (1+ (access enabled-structure ens2 :index-of-last-enabling)) 0 nil nil w nil)) (defun@par maybe-warn-about-theory (ens1 force-xnume-en1 imm-xnume-en1 ens2 ctx wrld state) ; Ens1 is the enabled structure before an in-theory event or hint, and ens2 is ; the resulting enabled structure. It is a bit unfortunate that warning-off-p ; is checked twice, but that is a trivial inefficiency, certainly overshadowed ; by the savings in calling theory-warning-fns needlessly. ; Force-xnume-en1 is the enabled status of forcing (*force-xnume*) in ens1. ; Imm-xnume-en1 is the status immediate force mode ; (*immediate-force-modep-xnume*) in ens1. (cond ((warning-disabled-p "Disable") (state-mac@par)) (t (pprogn@par (let ((fns (theory-warning-fns ens1 ens2 wrld))) (if fns (warning$@par ctx ("Disable") "The following 0-ary function~#0~[~/s~] will now have ~#0~[its ~ :definition rune~/their :definition runes~] disabled but ~ ~#0~[its :executable-counterpart rune~/their ~ :executable-counterpart runes~] enabled, which will allow ~ ~#0~[its definition~/their definitions~] to open up after ~ all: ~&0.~|See :DOC theories." fns) (state-mac@par))) (cond ((and force-xnume-en1 (not (enabled-numep *force-xnume* ens2))) (warning$@par ctx ("Disable") "Forcing has transitioned from enabled to disabled.~|See :DOC ~ force.")) ((and (not force-xnume-en1) (enabled-numep *force-xnume* ens2)) (warning$@par ctx ("Disable") "Forcing has transitioned from disabled to enabled.~|See :DOC ~ force.")) (t (state-mac@par))) (cond ((and imm-xnume-en1 (not (enabled-numep *immediate-force-modep-xnume* ens2))) (warning$@par ctx ("Disable") "IMMEDIATE-FORCE-MODEP has transitioned from enabled to ~ disabled.~|See :DOC force.")) ((and (not imm-xnume-en1) (enabled-numep *immediate-force-modep-xnume* ens2)) (warning$@par ctx ("Disable") "IMMEDIATE-FORCE-MODEP has transitioned from disabled to ~ enabled.~|See :DOC immediate-force-modep.")) (t (state-mac@par))))))) ; And now we develop the code for loading a theory into an enabled ; structure. (defrec theory-invariant-record (tterm error . untrans-term) t) (defun@par chk-theory-invariant1 (theory-expr ens invariant-alist errp-acc ctx state) ; We check a theory represented in enabled structure ens against the theory ; invariants in invariant-alist. If theory-expr is :from-hint then this theory ; comes from an :in-theory hint, and if it is :install then it is from ; installing a world; otherwise, theory-expr is a theory expression ; corresponding to this theory. (cond ((null invariant-alist) (mv@par errp-acc nil state)) (t (let* ((table-entry (car invariant-alist)) (inv-name (car table-entry)) (inv-rec (cdr table-entry)) (theory-inv (access theory-invariant-record inv-rec :tterm))) (mv-let (erp okp latches) (ev theory-inv (list (cons 'ens ens) (cons 'state (coerce-state-to-object state))) state nil nil t) (declare (ignore latches)) (cond (erp (let ((msg (msg "Theory invariant ~x0 could not be evaluated on ~ the theory produced by ~@1. Theory invariant, ~ ~P32, produced the error message:~%~@4~@5" inv-name (cond ((eq theory-expr :from-hint) "an :in-theory hint") ((eq theory-expr :install) "the current event") (t (msg "~x0" theory-expr))) (term-evisc-tuple nil state) (access theory-invariant-record inv-rec :untrans-term) okp (if (access theory-invariant-record inv-rec :error) "~|This theory invariant violation causes an ~ error." "")))) (mv-let@par (errp-acc state) (cond ((access theory-invariant-record inv-rec :error) (mv-let@par (erp val state) (er@par soft ctx "~@0" msg) (declare (ignore erp val)) (mv@par t state))) (t (pprogn@par (warning$@par ctx "Theory" "~@0" msg) (mv@par errp-acc state)))) (chk-theory-invariant1@par theory-expr ens (cdr invariant-alist) errp-acc ctx state)))) (okp (chk-theory-invariant1@par theory-expr ens (cdr invariant-alist) errp-acc ctx state)) (t (let ((msg (msg "Theory invariant ~x0 failed on the theory produced ~ by ~@1. Theory invariant ~x0 is ~P32.~@4" inv-name (cond ((eq theory-expr :from-hint) "an :in-theory hint") ((eq theory-expr :install) "the current event") (t (msg "~x0" theory-expr))) (term-evisc-tuple nil state) (access theory-invariant-record inv-rec :untrans-term) (if (access theory-invariant-record inv-rec :error) "~|This theory invariant violation causes an ~ error." "")))) (mv-let@par (errp-acc state) (cond ((access theory-invariant-record inv-rec :error) (mv-let@par (erp val state) (er@par soft ctx "~@0" msg) (declare (ignore erp val)) (mv@par t state))) (t (pprogn@par (warning$@par ctx "Theory" "~@0" msg) (mv@par errp-acc state)))) (chk-theory-invariant1@par theory-expr ens (cdr invariant-alist) errp-acc ctx state)))))))))) (defun@par chk-theory-invariant (theory-expr ens ctx state) ; See the comment in chk-theory-invariant1. (chk-theory-invariant1@par theory-expr ens (table-alist 'theory-invariant-table (w state)) nil ctx state)) ; CLAUSE IDENTIFICATION ; Before we can write the function that prints a description of ; simplify-clause's output, we must be able to identify clauses. This raises ; some issues that are more understandable later, namely, the notion of the ; pool. See Section PUSH-CLAUSE and The Pool. ; Associated with every clause in the waterfall is a unique object ; called the clause identifier or clause-id. These are printed out ; at the user in a certain form, e.g., ; [3]Subgoal *2.1/5.7.9.11''' ; but the internal form of these ids is as follows. (defrec clause-id ; Warning: Keep this in sync with clause-id-p. ; Forcing-round is a natural number, pool-lst and case-lst are generally ; true-lists of non-zero naturals (though elements of case-lst can be of the ; form Dk in the case of a dijunctive split) and primes is a natural. The ; pool-lst is indeed a pool-lst. (See the function pool-lst.) The case-lst is ; structurally analogous. ((forcing-round . pool-lst) case-lst . primes) t) (defun pos-listp (l) (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (posp (car l)) (pos-listp (cdr l)))))) (defun all-digits-p (lst radix) (declare (xargs :guard (and (character-listp lst) (integerp radix) (<= 2 radix) (<= radix 36)))) (cond ((endp lst) t) (t (and (digit-char-p (car lst) radix) (all-digits-p (cdr lst) radix))))) (defun d-pos-listp (lst) (declare (xargs :guard t)) (cond ((atom lst) (null lst)) ((natp (car lst)) (d-pos-listp (cdr lst))) (t (and (symbolp (car lst)) (let ((name (symbol-name (car lst)))) (and (not (equal name "")) (eql (char name 0) #\D) (all-digits-p (cdr (coerce name 'list)) 10))) (d-pos-listp (cdr lst)))))) (defun clause-id-p (cl-id) ; Warning: Keep this in sync with (defrec clause-id ...). (declare (xargs :guard t)) (case-match cl-id (((forcing-round . pool-lst) case-lst . primes) (and (natp forcing-round) (pos-listp pool-lst) (d-pos-listp case-lst) (natp primes))) (& nil))) ; A useful constant, the first clause-id: (defconst *initial-clause-id* ; Note: If this changes, inspect every use of it. As of this writing there is ; one place where we avoid a make clause-id and use *initial-clause-id* instead ; because we know the forcing-round is 0 and pool-lst and case-lst fields are ; both nil and the primes field is 0. (make clause-id :forcing-round 0 :pool-lst nil :case-lst nil :primes 0)) ; Because of the way :DO-NOT-INDUCT name hints are implemented, and because of ; the way we create names of theory arrays, we need to be able to produce a ; string representing a given clause-id. In the case of the above ; :DO-NOT-INDUCT, we use this to form a literal atom of the form |name ; clause-id| where clause-id is what tilde-@-clause-id-phrase will print on id. ; Therefore, we now virtually repeat the definition of ; tilde-@-clause-id-phrase, except this time building a string. We could use ; this to print all clause ids. But that is slow because it involves consing ; up strings. So we suffer the inconvenience of duplication. If ; tilde-@-clause-id-phrase is changed, be sure to change the functions below. (defun chars-for-tilde-@-clause-id-phrase/periods (lst) (declare (xargs :guard (d-pos-listp lst))) (cond ((null lst) nil) ((null (cdr lst)) (explode-atom (car lst) 10)) (t (append (explode-atom (car lst) 10) (cons #\. (chars-for-tilde-@-clause-id-phrase/periods (cdr lst))))))) (defun chars-for-tilde-@-clause-id-phrase/primes (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (cond ((= n 0) nil) ((= n 1) '(#\')) ((= n 2) '(#\' #\')) ((= n 3) '(#\' #\' #\')) (t (cons #\' (append (explode-atom n 10) '(#\')))))) (defun chars-for-tilde-@-clause-id-phrase (id) (declare (xargs :guard (clause-id-p id))) (append (if (= (access clause-id id :forcing-round) 0) nil `(#\[ ,@(explode-atom (access clause-id id :forcing-round) 10) #\])) (cond ((null (access clause-id id :pool-lst)) (cond ((null (access clause-id id :case-lst)) (append '(#\G #\o #\a #\l) (chars-for-tilde-@-clause-id-phrase/primes (access clause-id id :primes)))) (t (append '(#\S #\u #\b #\g #\o #\a #\l #\Space) (chars-for-tilde-@-clause-id-phrase/periods (access clause-id id :case-lst)) (chars-for-tilde-@-clause-id-phrase/primes (access clause-id id :primes)))))) (t (append '(#\S #\u #\b #\g #\o #\a #\l #\Space #\*) (chars-for-tilde-@-clause-id-phrase/periods (access clause-id id :pool-lst)) (cons #\/ (append (chars-for-tilde-@-clause-id-phrase/periods (access clause-id id :case-lst)) (chars-for-tilde-@-clause-id-phrase/primes (access clause-id id :primes))))))))) (defun string-for-tilde-@-clause-id-phrase (id) ; Warning: Keep this in sync with tilde-@-clause-id-phrase. (declare (xargs :guard (clause-id-p id))) (coerce (chars-for-tilde-@-clause-id-phrase id) 'string)) (defun@par load-theory-into-enabled-structure (theory-expr theory augmented-p ens incrmt-array-name-info index-of-last-enabling wrld ctx state) ; Note: Theory must be a runic theory if augmented-p is nil and otherwise an ; augmented runic theory, but never a common theory. ; We do exactly what the name of this function says, we load the given theory ; into the enabled structure ens. If incrmt-array-name-info is t we increment ; the array name suffix. If incrmt-array-name-info is non-boolean, then it is ; a clause-id that we use to create a new name uniquely determined by that ; clause-id. Otherwise, we use the same name. Loading consists of augmenting ; the theory (if augmented-p is nil) to convert it into a list of pairs, (nume ; . rune), mapping numes to their runes, and then compressing it into the named ; array. We set the index of last enabling to be the highest existing nume in ; wrld right now unless index-of-last-enabling is non-nil, in which case we use ; that (which should be a natp). Thus, any name introduced after this is ; enabled relative to this ens. If the array of the ens is too short, we ; extend it by 500. ; A Refresher Course on ACL2 One Dimensional Arrays: ; Suppose that a is an array with :dimension (d) and :maximum-length ; m. Then you access it via (aref1 'name a i). It must be the case ; that i m, a compress is done. If an ; array is never modified, then the mimimum acceptable m is in fact d. ; Note: Every call of this function should be followed by a call of ; maybe-warn-about-theory on the enabled structure passed in and the one ; returned. (let* ((n (or index-of-last-enabling (1- (get-next-nume wrld)))) (d (access enabled-structure ens :array-length)) (new-d (cond ((< n d) d) (t (+ d (* 500 (1+ (floor (- n d) 500))))))) (root (access enabled-structure ens :array-name-root)) (suffix (cond ((eq incrmt-array-name-info t) (1+ (access enabled-structure ens :array-name-suffix))) (t (access enabled-structure ens :array-name-suffix)))) (name (cond ((eq incrmt-array-name-info t) (intern (coerce (append root (explode-nonnegative-integer suffix 10 nil)) 'string) "ACL2")) (incrmt-array-name-info ; must be a clause-id (intern (coerce (append root (chars-for-tilde-@-clause-id-phrase incrmt-array-name-info)) 'string) "ACL2")) (t (access enabled-structure ens :array-name)))) (alist (if augmented-p theory (augment-runic-theory theory wrld))) (ens (make enabled-structure :index-of-last-enabling n :theory-array (compress1 name (cons (list :header :dimensions (list new-d) :maximum-length (1+ new-d) :default nil :name name :order nil) alist)) :array-name name :array-length new-d :array-name-root root :array-name-suffix suffix))) (er-progn@par (if (or (eq theory-expr :no-check) (eq (ld-skip-proofsp state) 'include-book) (eq (ld-skip-proofsp state) 'include-book-with-locals)) (value@par nil) (chk-theory-invariant@par theory-expr ens ctx state)) (value@par ens)))) ; Here is how we initialize the global enabled structure, from which ; all subsequent structures are built. (defun initial-global-enabled-structure (root-string) ; We generate an initial enabled-structure in which everything is enabled. ; The array is empty but of length d (which is the constant 1000 here). ; The array name is formed by adding a "0" to the end of root-string. ; The array-name-root is the list of characters in root-string and the suffix ; is 0. (let* ((root (coerce root-string 'list)) (name (intern (coerce (append root '(#\0)) 'string) "ACL2")) (d 1000)) (make enabled-structure :index-of-last-enabling -1 :theory-array (compress1 name (cons (list :header :dimensions (list d) :maximum-length (1+ d) :default nil :name name) nil)) :array-name name :array-length d :array-name-root root :array-name-suffix 0))) ; And here is the function that must be used when you undo back to a ; previous value of the global enabled structure: (defun recompress-global-enabled-structure (varname wrld) ; Logically speaking this function is a no-op that returns t. It is ; only called from #-acl2-loop-only code. Practically speaking it ; side-effects the von Neumann array named in the global-enabled- ; structure so that it once again contains the current array. ; This function is called when you have reason to believe that the von ; Neumann array associated with the global enabled structure is out of ; date. Suppose that wrld, above, was obtained from the then current ; ACL2 world by rolling back, as with UBT. Then there is possibly a ; new 'global-enabled-structure in wrld. But the array associated ; with it is still the one from the now-no-longer current ACL2 world. ; We just compress the new array again. Because the array was already ; compressed, we know compress1 returns an eq object and so we don't ; actually have to store its value back into the global-enabled-structure. ; Indeed, we don't want to do that because it would require us to put a ; new binding of 'global-enabled-structure on wrld and we don't want to ; to do that. (Once upon a time we did, and it was inconvenient because ; it covered up the most recent command-landmark; indeed, sometimes there ; would be two successive bindings of it.) (declare (xargs :guard (and (equal varname varname) (equal wrld wrld)))) ; Without the odd guard -- some term mentioning all the formals -- the formals ; are recognized as irrelevant! This body below always returns t. ; Parallelism wart: it's unclear whether we need to lock this array operation. ; By design, each array and theory is unique to the current subgoal, so this ; locking should be unnecessary. However, we've seen some slow array access ; warnings, and maybe this is where they're generated. ; (with-acl2-lock ; *acl2-par-arrays-lock* (let* ((ges1 (getprop varname 'global-value nil 'current-acl2-world wrld)) (theory-array (access enabled-structure ges1 :theory-array)) (name (access enabled-structure ges1 :array-name))) ; We would rather not pay the price of making a new array if the proper ; association of array to alist is already set up. Since this function is ; logically a no-op (it is just a function that returns t), it is certainly ; legitimate to punt if we like. But it might be nice to abstract what we are ; doing here and make it available to the ACL2 user. #-acl2-loop-only (when (let ((old-ar (get-acl2-array-property name))) (and old-ar (eq (car old-ar) theory-array))) (return-from recompress-global-enabled-structure t)) (let ((to-ignore (cond (ges1 (prog2$ (flush-compress name) (compress1 name theory-array))) (t nil)))) (declare (ignore to-ignore)) t))) (defun recompress-stobj-accessor-arrays (stobj-names wrld) ; This function has nothing to do with theories, but we place it here because ; it has a similar function to recompress-global-enabled-structure, defined ; just above. This function should be called when the 'accessor-names arrays ; (used for printing nth/update-nth accesses to stobjs) might be out of date. (if (endp stobj-names) t (let* ((st (car stobj-names)) (ar (getprop st 'accessor-names nil 'current-acl2-world wrld))) (prog2$ (or (null ar) (prog2$ (flush-compress st) (compress1 st ar))) (recompress-stobj-accessor-arrays (cdr stobj-names) wrld))))) ; We have defined all the basic concepts having to do with theories ; except the interface to the user, i.e., the "theory manipulation ; functions" with which the user constructs theories, the polite and ; verbose theory checkers that ensure that a theory expression ; produced a theory, and the events for defining theories and setting ; the current one. We do these things later and return now to the ; development of type-set. ; The Type-set-xxx Functions ; We are about to embark on a litany of definitions for determining ; the type-set of various function applications, given the type-sets ; of certain of their arguments. There is no fixed style of ; definition here; because type-set is so often called, we thought it ; best to pass in only those arguments actually needed, rather than ; adhere to some standard style. All of the functions return a type ; set and a ttree. The ttree is always used as an accumulator in the ; sense that we may extend but may not ignore the incoming ttree. ; This raises a problem: Suppose that the ttree records our work in ; establishing the type set, ts, of an argument but that we ultimately ; ignore ts because it is not strong enough to help. If we return the ; extended ttree, we might cause unnecessary case splits (justifying ; the irrelevant fact that the arg has type-set ts). We adopt the ; convention of passing in two ttrees, named ttree and ttree0. Ttree ; is always an extension of ttree0 and records the work done to get ; the argument type sets. Therefore, we may return ttree (possibly ; extended) if our answer is based on the argument type sets, and may ; return ttree0 otherwise (which merely passes along unknown ; previously done work of any sort, not necessarily only work done on this ; particular term -- so do not return nil instead of ttree0!). ; The primitive type-set functions all push the following fake rune into the ; ttree they return, so that we know that primitive type-set knowledge was ; used. Before we invented this fake rune, we attributed this type-set ; reasoning to propositional calculus, i.e., we would report that (implies ; (integerp x) (integerp (1- x))) was proved by trivial observations. But we ; prefer to think of it (and report it) as type reasoning. See the Essay on ; Fake-Runes for a discussion of fake runes. (defconst *fake-rune-for-type-set* '(:FAKE-RUNE-FOR-TYPE-SET nil)) ; To make it convenient to push this rune into a tree we provide: (defun puffert (ttree) ; Once upon a time this function was called Push-Fake-Rune-for-Type-Set. It ; got shortened to pfrts and pronounced "pufferts". The name stuck except that ; the final s was dropped to make it more like a first-person verb form. You ; know, ``I puffert'', ``you puffert'', ``he pufferts.'' Here, we frequently ; puffert ttrees. Right. (push-lemma *fake-rune-for-type-set* ttree)) (defun immediate-forcep (fn ens) ; This function must return 'case-split, t or nil! (cond ((eq fn 'case-split) 'case-split) ((enabled-numep *immediate-force-modep-xnume* ens) t) (t nil))) (defmacro numeric-type-set (ts) ; Warning: This is a dangerous macro because it evaluates ts more than once! ; It is best if the argument is a variable symbol. ; This coerces ts into the type *ts-acl2-number*. That is, if ts contains ; nonnumeric bits then those bits are shut off and *ts-zero* is turned on. ; Another way to look at it is that if term has type ts then (fix term) has ; type (numeric-type-set ts). ; Note: We tried wrapping (the-type-set ...) around the form below, inside the ; backquote, but found that (disassemble 'TYPE-SET-BINARY-+) produced identical ; results either way. `(let ((numeric-ts-use-nowhere-else (ts-intersection (check-vars-not-free (numeric-ts-use-nowhere-else) ,ts) *ts-acl2-number*))) (if (ts= numeric-ts-use-nowhere-else ,ts) ,ts (ts-union numeric-ts-use-nowhere-else *ts-zero*)))) (defmacro rational-type-set (ts) ; Warning: This is a dangerous macro because it evaluates ts more than once! ; It is best if the argument is a variable symbol. ; This macro is like numeric-type-set, but coerces ts to the rationals. Note ; that it reuses the special variable numeric-ts-use-nowhere-else even though ; this is a slight misnomer. `(let ((numeric-ts-use-nowhere-else (ts-intersection (check-vars-not-free (numeric-ts-use-nowhere-else) ,ts) *ts-rational*))) (if (ts= numeric-ts-use-nowhere-else ,ts) ,ts (ts-union numeric-ts-use-nowhere-else *ts-zero*)))) ;; RAG - I added this function analogously to rational-type-set. #+:non-standard-analysis (defmacro real-type-set (ts) ; Warning: This is a dangerous macro because it evaluates ts more than once! ; It is best if the argument is a variable symbol. ; This macro is like numeric-type-set, but coerces ts to the reals. Note ; that it reuses the special variable numeric-ts-use-nowhere-else even though ; this is a slight misnomer. `(let ((numeric-ts-use-nowhere-else (ts-intersection (check-vars-not-free (numeric-ts-use-nowhere-else) ,ts) *ts-real*))) (if (ts= numeric-ts-use-nowhere-else ,ts) ,ts (ts-union numeric-ts-use-nowhere-else *ts-zero*)))) ; We start with probably the most complicated primitive type set ; function, that for binary-+. (defun type-set-binary-+ (term ts1 ts2 ttree ttree0) ; Because 1- (i.e., SUB1) is so common and often is applied to ; strictly positive integers, it is useful to know that, in such ; cases, the result is a non-negative integer. We therefore test for ; (+ x -1) and its commuted version (+ -1 x). To be predictable, we ; also look for (+ x +1), and its commuted version, when x is strictly ; negative. We specially arrange for the answer type-set to be empty ; if either of the input type-sets is empty. This occurs when we are ; guessing type sets. The idea is that some other branch ought to ; give us a nonempty type-set before this one can meaningfully ; contribute to the answer. Before we added the special processing of ; +1 and -1 we did not have to check for the empty case because the ; array referenced by aref2 has the property that if either type-set ; is empty the result is empty. (let ((arg1 (fargn term 1)) (arg2 (fargn term 2))) (cond ((or (ts= ts1 *ts-empty*) (ts= ts2 *ts-empty*)) (mv *ts-empty* ttree)) ((and (equal arg2 ''-1) (ts-subsetp ts1 *ts-positive-integer*)) (mv *ts-non-negative-integer* (puffert ttree))) ((and (equal arg1 ''-1) (ts-subsetp ts2 *ts-positive-integer*)) (mv *ts-non-negative-integer* (puffert ttree))) ((and (equal arg2 ''+1) (ts-subsetp ts1 *ts-negative-integer*)) (mv *ts-non-positive-integer* (puffert ttree))) ((and (equal arg1 ''+1) (ts-subsetp ts2 *ts-negative-integer*)) (mv *ts-non-positive-integer* (puffert ttree))) (t (let ((ans (aref2 'type-set-binary-+-table *type-set-binary-+-table* (numeric-type-set ts1) (numeric-type-set ts2)))) (mv ans (puffert (if (ts= ans *ts-acl2-number*) ttree0 ttree)))))))) (defun type-set-binary-* (ts1 ts2 ttree ttree0) ; See type-set-binary-+ for a few comments. (cond ((or (ts= ts1 *ts-empty*) (ts= ts2 *ts-empty*)) (mv *ts-empty* ttree)) (t (let ((ans (aref2 'type-set-binary-*-table *type-set-binary-*-table* (numeric-type-set ts1) (numeric-type-set ts2)))) (mv ans (puffert (if (ts= ans *ts-acl2-number*) ttree0 ttree))))))) (defun type-set-not (ts ttree ttree0) (cond ((ts= ts *ts-nil*) (mv *ts-t* (puffert ttree))) ((ts-subsetp *ts-nil* ts) (mv *ts-boolean* ttree0)) (t (mv *ts-nil* (puffert ttree))))) (defun type-set-<-1 (r arg2 commutedp type-alist) ; We are trying to determine the truth value of an inequality, (< arg1 ; arg2) where arg1 is (quote r), a rational constant. Except, in the ; case that commutedp is non-nil the inequality at issue is (< arg2 ; (quote r)). ; We scan through the type-alist looking for inequalities that imply ; the truth or falsity of the inequality at issue, and return two ; values --- the determined type of the inequality, by default ; *ts-boolean*, and a governing ttree. ; Here is a trivial example of the problem this code is intended to ; solve. ; (defstub bar (x) t) ; ; (defaxiom bar-thm ; (implies (and (integerp x) ; (< 3 x)) ; (bar x)) ; :rule-classes :type-prescription) ; ; (thm ; (implies (and (integerp x) ; (< 4 x)) ; (bar x))) ; Robert Krug came up with the original version of this patch when ; playing with arithmetic functions that changed sign at some point ; other than zero. Conceptually, this type of reasoning belongs to ; linear arithmetic rather than type-set, but it provides a modest ; improvement at a very small cost. In a perfect world we might want ; to mix type-set reasoning with the linear arithmetic; the ability to ; call add-poly from within type-set or assume-true-false could be ; nice (although perhaps too expensive). (cond ((endp type-alist) (mv *ts-boolean* nil)) (t (let ((type-alist-entry (car type-alist))) (case-match type-alist-entry ((typed-term type . ttree) (mv-let (c leftp x) ; We bind c to nil if we cannot use type-alist-entry. Otherwise, c is a ; rational that is being compared with < to a term x, and leftp is true if and ; only if c occurs on the left side of the <. We postpone the check that x is ; equal to arg2, which is potentially expensive, until we need to make that ; check (if at all). (case-match typed-term (('< ('quote c) x) (if (rationalp c) (mv c t x) (mv nil nil x))) (('< x ('quote c)) (if (rationalp c) (mv c nil x) (mv nil nil nil))) (& (mv nil nil nil))) (cond ((null c) (type-set-<-1 r arg2 commutedp (cdr type-alist))) (leftp ; So type refers to (c < x). (cond ((and (<= r c) (ts= type *ts-t*) (equal x arg2)) ; (r <= c < arg2) implies (r < arg2), and hence also not (arg2 < r). (mv (if commutedp *ts-nil* *ts-t*) (puffert ttree))) ((and (if commutedp (< c r) (<= c r)) (ts= type *ts-nil*) (equal x arg2)) ; (arg2 <= c <= r) implies not (r < arg2); ; (arg2 <= c < r) implies (arg2 < r). (mv (if commutedp *ts-t* *ts-nil*) (puffert ttree))) (t (type-set-<-1 r arg2 commutedp (cdr type-alist))))) (t ; (not leftp) ; So type refers to (arg2 < c). (cond ((and (if commutedp (<= r c) (< r c)) (ts= type *ts-nil*) (equal x arg2)) ; (r < c <= arg2) implies (r < arg2); ; (r <= c <= arg2) implies not (arg2 < r). (mv (if commutedp *ts-nil* *ts-t*) (puffert ttree))) ((and (<= c r) (ts= type *ts-t*) (equal x arg2)) ; (arg2 < c <= r) implies not (r < arg2) and also implies (arg2 < r). (mv (if commutedp *ts-t* *ts-nil*) (puffert ttree))) (t (type-set-<-1 r arg2 commutedp (cdr type-alist)))))))) (& (type-set-<-1 r arg2 commutedp (cdr type-alist)))))))) ;; RAG - I changed complex-rational to complex below. (defun type-set-< (arg1 arg2 ts1 ts2 type-alist ttree ttree0 pot-lst pt) ; This function is not cut from the standard mold because instead of ; taking term it takes the two args. This allows us easily to ; implement certain transformations on inequalities: When x is an ; integer, ; (< x 1) is (not (< 0 x)) and ; (< -1 x) is (not (< x 0)). ; Warning: It is important to assume-true-false that type-set-< make ; these transformations. See the comments about type-set-< in ; assume-true-false. ; As of Version_2.6, this function diverged even further from the standard ; mold. We now use the type-alist to determine the truth or falsity ; of some simple inequalities which would be missed otherwise. ; See type-set-<-1 for details. (let* ((nts1 (numeric-type-set ts1)) (nts2 (numeric-type-set ts2))) (cond ((and (equal arg2 *1*) ; Actually we don't have to add 0 back in, as done by numeric-type-set, before ; making the following test. But let's keep things simple. (ts-subsetp nts1 *ts-integer*)) (mv-let (ts ttree) (type-set-< *0* arg1 *ts-zero* ts1 type-alist (puffert ttree) ; Note: Once upon a time in v2-7, ttree0 was used immediately below ; instead of ttree. Note however that we have depended upon ts1 to ; get here. It might be unsound to do that and then report the ; dependencies of ttree0. However, in v2-7 this was (probably) sound ; because the ttree0 exits were all reporting *ts-boolean* answers. ; But in the new code, below, we use add-polys0 in a way that could ; overwrite ttree with ttree0. Put more intuitively: we think v2-7 ; was sound even with a ttree0 here, but we think v2-8 would be ; unsound with a ttree0 here because of the add-polys0 below. ttree pot-lst pt) (type-set-not ts ttree ttree0))) ((and (quotep arg1) (eql (cadr arg1) -1) (ts-subsetp nts2 *ts-integer*)) (mv-let (ts ttree) (type-set-< arg2 *0* ts2 *ts-zero* type-alist (puffert ttree) ; See note above about this ttree versus the old ttree0. ttree pot-lst pt) (type-set-not ts ttree ttree0))) ; If one of the args is a constant (a quotep) we look in the ; type-alist. If we get a useful answer, we are done. Note that if ; we do get a useful answer here, it is sufficient to use ttree0 ; rather than ttree since our answer does not depend on the type of ; the args. In particular, type-set-<-1 returns an accurate answer ; regardless of whether we make the tests above leading to here. See ; the comments following ``The Type-set-xxx Functions'' above for an ; explanation of ttree0 vs. ttree. (t (mv-let (returned-ts returned-ttree) (cond ((and (quotep arg1) (rationalp (cadr arg1))) (type-set-<-1 (cadr arg1) arg2 nil type-alist)) ((and (quotep arg2) (rationalp (cadr arg2))) (type-set-<-1 (cadr arg2) arg1 t type-alist)) (t (mv *ts-boolean* nil))) (if (not (ts= returned-ts *ts-boolean*)) (mv returned-ts (cons-tag-trees returned-ttree ttree0)) ; We did not get a useful answer by looking in the type-alist. We try ; 'type-set-<-table if we can. (let ((temp-ts (if (or (ts-intersectp ts1 #+:non-standard-analysis *ts-complex* #-:non-standard-analysis *ts-complex-rational*) (ts-intersectp ts2 #+:non-standard-analysis *ts-complex* #-:non-standard-analysis *ts-complex-rational*)) *ts-boolean* (aref2 'type-set-<-table *type-set-<-table* nts1 nts2)))) (cond ((or (ts= temp-ts *ts-t*) (ts= temp-ts *ts-nil*)) (mv temp-ts (puffert ttree))) ((null pot-lst) (mv *ts-boolean* ttree0)) ; We finally try using linear arithmetic by calling add-polys on, first, ; the negation of the original inequality. If this returns a contradictionp, ; the original inequality must be true. If this does not return a ; contradictionp, we try linear arithmetic with the original inequality. ; These final two tries are new to v2-8. (t ; Note: Below there are two calls of base-poly, each of which has the ; ttree ttree0. The argument that we're not dependent on ts1 and ts2 ; here is as follows. The setting of temp-ts, above, which appears to ; rely on ts1 and ts2, is irrelevant here because if we get here, ; temp-ts is *ts-boolean*, which is correct regardless of the ttrees. ; Reader further above, the only uses of ts1 and ts2 are heuristic: ; the methods by which we compute an answer is correct even if the ; preceding tests were not made. (mv-let (contradictionp new-pot-lst) (add-polys0 (list (normalize-poly (add-linear-terms :lhs arg2 :rhs arg1 (base-poly ttree0 '<= ; The following nil is the rational-poly-p flag and we could supply a ; more accurate value by looking at ts1 and ts2. But then it would ; appear that we were depending on them. Actually, we're not: the ; rational-poly-p flag is irrelevant to add-polys0 and could only come ; into play if the poly here created eventually found its way into a ; non-linear setting. But that won't happen because the poly is ; thrown away. However, since the flag is indeed irrelevant we just ; supply nil to avoid the appearance of dependence. nil nil)))) pot-lst pt nil 2) (declare (ignore new-pot-lst)) (if contradictionp (mv *ts-t* (access poly contradictionp :ttree)) (mv-let (contradictionp new-pot-lst) (add-polys0 (list (normalize-poly (add-linear-terms :lhs arg1 :rhs arg2 (base-poly ttree0 '< nil nil)))) pot-lst pt nil 2) (declare (ignore new-pot-lst)) (if contradictionp (mv *ts-nil* (access poly contradictionp :ttree)) (mv *ts-boolean* ttree0)))))))))))))) ;; RAG - I added entries for real and complex irrationals. (defun type-set-unary-- (ts ttree ttree0) (let ((ts1 (numeric-type-set ts))) (cond ((ts= ts1 *ts-acl2-number*) (mv *ts-acl2-number* ttree0)) (t (mv (ts-builder ts1 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-negative-integer*) (*ts-positive-ratio* *ts-negative-ratio*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-negative-non-ratio*) (*ts-negative-integer* *ts-positive-integer*) (*ts-negative-ratio* *ts-positive-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-positive-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*)) (puffert ttree)))))) ;; RAG - I added entries for real and complex irrationals. (defun type-set-unary-/ (ts ttree ttree0) (let* ((ts1 (numeric-type-set ts)) (ans (ts-builder ts1 (*ts-zero* *ts-zero*) (*ts-positive-rational* *ts-positive-rational*) (*ts-negative-rational* *ts-negative-rational*) #+:non-standard-analysis (*ts-positive-non-ratio* *ts-positive-non-ratio*) #+:non-standard-analysis (*ts-negative-non-ratio* *ts-negative-non-ratio*) (*ts-complex-rational* *ts-complex-rational*) #+:non-standard-analysis (*ts-complex-non-rational* *ts-complex-non-rational*)))) (cond ((ts= ans *ts-acl2-number*) (mv *ts-acl2-number* (puffert ttree0))) (t (mv ans (puffert ttree)))))) (defun type-set-numerator (ts ttree ttree0) (let* ((ts1 (rational-type-set ts)) (ans (ts-builder ts1 (*ts-zero* *ts-zero*) (*ts-positive-rational* *ts-positive-integer*) (*ts-negative-rational* *ts-negative-integer*)))) (cond ((ts= ans *ts-integer*) (mv *ts-integer* (puffert ttree0))) (t (mv ans (puffert ttree)))))) ;; RAG - I added an entry for *ts-complex-non-rational*. Note that ;; since we don't know whether the type in non-rational because of an ;; irrational real or imaginary part, all we can say is that the ;; result is real, not necessarily irrational. (defun type-set-realpart (ts ttree ttree0) (cond #+:non-standard-analysis ((ts-intersectp ts *ts-complex-non-rational*) (mv *ts-real* (puffert ttree0))) ((ts-intersectp ts *ts-complex-rational*) (mv *ts-rational* (puffert ttree0))) (t (mv (numeric-type-set ts) (puffert ttree))))) ;; RAG - I added an entry for *ts-complex-non-rational*. (defun type-set-imagpart (ts ttree ttree0) (cond #+:non-standard-analysis ((ts-subsetp ts *ts-complex-non-rational*) (mv (ts-union *ts-positive-real* *ts-negative-real*) (puffert ttree))) #+:non-standard-analysis ((ts-intersectp ts *ts-complex-non-rational*) (mv *ts-real* (puffert ttree0))) ((ts-subsetp ts *ts-complex-rational*) (mv (ts-union *ts-positive-rational* *ts-negative-rational*) (puffert ttree))) ((ts-intersectp ts *ts-complex-rational*) (mv *ts-rational* (puffert ttree0))) (t (mv *ts-zero* (puffert ttree))))) ;; RAG - I allowed reals as well as rationals below for the type of ;; ts1 and ts2. (defun type-set-complex (ts1 ts2 ttree ttree0) (let ((ts1 #+:non-standard-analysis (real-type-set ts1) #-:non-standard-analysis (rational-type-set ts1)) (ts2 #+:non-standard-analysis (real-type-set ts2) #-:non-standard-analysis (rational-type-set ts2))) (cond ((ts= ts2 *ts-zero*) (mv ts1 (puffert ttree))) ((ts= (ts-intersection ts2 *ts-zero*) *ts-empty*) #+:non-standard-analysis (cond ((and (ts-subsetp ts1 *ts-rational*) (ts-subsetp ts2 *ts-rational*)) (mv *ts-complex-rational* (puffert ttree))) ((or (ts-subsetp ts1 *ts-non-ratio*) (ts-subsetp ts2 *ts-non-ratio*)) (mv *ts-complex-non-rational* (puffert ttree))) (t (mv *ts-complex* (puffert ttree)))) #-:non-standard-analysis (mv *ts-complex-rational* (puffert ttree))) #+:non-standard-analysis ((ts= ts1 *ts-real*) (mv *ts-acl2-number* (puffert ttree0))) #-:non-standard-analysis ((ts= ts1 *ts-rational*) (mv *ts-acl2-number* (puffert ttree0))) (t (mv (ts-union ts1 #+:non-standard-analysis (cond ((and (ts-subsetp ts1 *ts-rational*) (ts-subsetp ts2 *ts-rational*)) *ts-complex-rational*) (t *ts-complex*)) #-:non-standard-analysis *ts-complex-rational*) (puffert ttree)))))) ;; RAG - I added this function to account for the new built-in floor1. #+:non-standard-analysis (defun type-set-floor1 (ts ttree ttree0) (let* ((ts1 (real-type-set ts)) (ans (ts-builder ts1 (*ts-zero* *ts-zero*) (*ts-positive-integer* *ts-positive-integer*) (*ts-positive-ratio* *ts-non-negative-integer*) (*ts-positive-non-ratio* *ts-non-negative-integer*) (*ts-negative-real* *ts-negative-integer*)))) (cond ((ts= ans *ts-integer*) (mv *ts-integer* (puffert ttree0))) (t (mv ans (puffert ttree)))))) ;; RAG - I added this function to account for the new built-in standard-part. #+:non-standard-analysis (defun type-set-standard-part (ts ttree ttree0) (let* ((ts1 (numeric-type-set ts)) (ans (ts-builder ts1 (*ts-zero* *ts-zero*) (*ts-positive-real* *ts-non-negative-real*) (*ts-negative-real* *ts-non-positive-real*) (*ts-complex* *ts-acl2-number*)))) (mv (ts-union (ts-intersection ts (ts-complement *ts-acl2-number*)) ans) (puffert (if (ts= ans *ts-acl2-number*) ttree0 ttree))))) ;; RAG - I added this function to account for the new built-in standardp. #+:non-standard-analysis (defun type-set-standardp (ts ttree ttree0) (cond ((ts= ts *ts-zero*) (mv *ts-t* (puffert ttree))) (t (mv *ts-boolean* (puffert ttree0))))) ; Essay on the Recognizer-Alist and Recognizer-Tuples ; The "recognizer alist" of ACL2 is a combination of Nqthm's ; RECOGNIZER-ALIST and its two COMPOUND-RECOGNIZER-ALISTs. The ; recognizer-alist is stored as a global variable in the world w and ; accessed via ; (global-val 'recognizer-alist w). ; The recognizer alist contains records of the following form: (defrec recognizer-tuple ; Warning: In type-set-rec, we assume that the car of a recognizer-tuple cannot ; be the keyword :SKIP-LOOKUP. Do not change the shape of this record to ; violate that assumption! Visit all occurrences of :SKIP-LOOKUP if any such ; change is contemplated. (fn (nume . true-ts) (false-ts . strongp) . rune) t) ; The initial value of the recognizer alist is shown after we discuss the ; meaning of these records. ; In a recognizer-tuple, fn is the name of some Boolean-valued ; function of one argument. True-ts and and false-ts are type sets. ; If such a record is on the recognizer-alist then it is the case that ; (fn x) implies that the type set of x is a subset of true-ts and ; (not (fn x)) implies that the type set of x is a subset of false-ts. ; Furthermore, if strongp is t, then true-ts is the complement of ; false-ts; i.e., (fn x) recognizes exactly the subset identified by ; true-ts. Rune is either a rune or ; *fake-rune-for-anonymous-enabled-rule*. Nume is the nume of rune ; (possibly nil). ; For example, if we prove that ; (BOOLEANP X) -> (OR (EQUAL X T) (EQUAL X NIL)) ; then we can add the following tuple ; (make recognizer-tuple ; :fn BOOLEANP ; :true-ts *ts-boolean* ; :false-ts *ts-unknown* ; :strongp nil ; :nume nil ; :rune *fake-rune-for-anonymous-enabled-rule*) ; to the list. Observe that the false-ts for this pair does not tell us ; much. But if we proved the above AND ; (NOT (BOOLEANP X)) -> (NOT (OR (EQUAL X T) (EQUAL X NIL))) ; we could add the tuple: ; (make recognizer-tuple ; :fn BOOLEANP ; :true-ts *ts-boolean* ; :false-ts (ts-complement *ts-boolean*) ; :strongp t) ; And we would know as much about BOOLEANP as we know about integerp. ; Consider the function PRIMEP. It implies its argument is a positive ; integer. Its negation tells us nothing about the type of its argument. ; (make recognizer-tuple ; :fn PRIMEP ; :true-ts *ts-positive-integer* ; :false-ts *ts-unknown* ; :strongp nil) ; Suppose now x is a term whose type set we know. What is the type ; set of (PRIMEP x)? If the type set for x includes the positive ; integer bit, the type set for (PRIMEP x) may include *ts-t* so we ; will throw that in. If the type set for x includes any of ; *ts-unknown*'s bits (of course it does) we will throw in *ts-nil*. ; The interesting thing about this is that if the type set of x does ; not include the positive integers, we'll know (PRIME x) is nil. ; If we assume (PRIME x) true, we will restrict the type of x to the ; positive integers. If we assume (PRIME x) false, we won't restrict ; x at all. ; Consider the function RATTREEP that recognizes cons-trees of ; rational numbers. We can prove that (RATTREEP x) implies the type ; set of x is in *ts-cons* union *ts-rational*. We can prove that ; (NOT (RATTREEP x)) implies that the type set of x is not ; *ts-rational*. That means the false-ts for RATTREEP is the ; complement of the rationals. If we were asked to get the type set ; of (RATTREEP x) where x is rational, we'd throw in a *ts-t* because ; the type of x intersects the true-ts and we'd not throw in anythine ; else (because the type of x does not interesect the false ts). If ; we were asked to assume (RATTREEP x) then on the true branch x's ; type would be interesected with the conses and the rationals. On ; the false branch, the rationals would be deleted. ; Historical Note: In an earlier version of this code we did not allow ; compound recognizer rules to be enabled or disabled and hence did ; not store the runes and numes. We were much cleverer then ; about allowing newly proved rules to strengthen existing recognizer ; tuples. That is, you could prove a rule about the true-ts and then ; a second about the false-ts, and then perhaps a third tightening up ; the true-ts fact a little, etc. This had the problem that it was ; not possible to identify a single rule name with the known facts ; about the type of fn. Thus, when we decided to track use of all ; rules it was impossible to give a sensible meaning to disabled ; compound recognizer rules in some cases. (E.g., the fact stored ; might be derived from both enabled and disabled rules.) So an ; important aspect of the new design is that there is a 1:1 ; correspondence between what we know and the rule that told us. If ; you have proved a sequence of three rules about fn we will use the ; most recently proved, still-enabled one. If you disable that one, ; we'll naturally fall back on the next most recently still-enabled ; one. ;; RAG - I added recognizers for realp and complexp. (defconst *initial-recognizer-alist* (list (make recognizer-tuple :fn 'integerp :true-ts *ts-integer* :false-ts (ts-complement *ts-integer*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'rationalp :true-ts *ts-rational* :false-ts (ts-complement *ts-rational*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) #+:non-standard-analysis (make recognizer-tuple :fn 'realp :true-ts *ts-real* :false-ts (ts-complement *ts-real*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'complex-rationalp :true-ts *ts-complex-rational* :false-ts (ts-complement *ts-complex-rational*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) #+:non-standard-analysis (make recognizer-tuple :fn 'complexp :true-ts *ts-complex* :false-ts (ts-complement *ts-complex*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'acl2-numberp :true-ts *ts-acl2-number* :false-ts (ts-complement *ts-acl2-number*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'consp :true-ts *ts-cons* :false-ts (ts-complement *ts-cons*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'atom :true-ts (ts-complement *ts-cons*) :false-ts *ts-cons* :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'listp :true-ts (ts-union *ts-cons* *ts-nil*) :false-ts (ts-complement (ts-union *ts-cons* *ts-nil*)) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'true-listp :true-ts *ts-true-list* :false-ts (ts-complement *ts-true-list*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'characterp :true-ts *ts-character* :false-ts (ts-complement *ts-character*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'stringp :true-ts *ts-string* :false-ts (ts-complement *ts-string*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'null :true-ts *ts-nil* :false-ts (ts-complement *ts-nil*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*) (make recognizer-tuple :fn 'symbolp :true-ts *ts-symbol* :false-ts (ts-complement *ts-symbol*) :strongp t :nume nil :rune *fake-rune-for-anonymous-enabled-rule*))) (defun most-recent-enabled-recog-tuple (fn alist ens) ; This function finds the first recognizer-tuple on alist whose :fn is ; fn and whose :nume is enabled-numep. Thus, primitive recognizer ; tuples, like that for rationalp, are always "enabled." (cond ((null alist) nil) ((and (eq fn (access recognizer-tuple (car alist) :fn)) (enabled-numep (access recognizer-tuple (car alist) :nume) ens)) (car alist)) (t (most-recent-enabled-recog-tuple fn (cdr alist) ens)))) (defun type-set-recognizer (recog-tuple arg-ts ttree ttree0) ; Recog-tuple is a recognizer-tuple. Then we know that (fn x) implies ; that the type set of x, arg-ts, is a subset of true-ts. ; Furthermore, we know that ~(fn x) implies that arg-ts is a subset of ; false-ts. In addition, we know that fn is a Boolean valued fn. ; This function is supposed to determine the type set of (fn x) where ; arg-ts is the type set of x. Observe that if arg-ts intersects with ; true-ts then (fn x) might be true, so we should throw in *ts-t*. ; Conversely, if arg-ts does not intersect with true-ts then (fn x) ; cannot possibly be true. Exactly analogous statements can be made ; about false-ts. ; We return two results, the type set of (fn x) and an extension of ; ttree (or ttree0) obtained by adding the named rule, tagged 'lemma. We ; initially considered adding the rule name to the tree only if the ; type-set returned was stronger than just Boolean. But it could be ; that this rule is the rule that established that fn was Boolean and ; so we can't be sure that even that weak response isn't an ; interesting use of this rule. (let ((ts (ts-builder arg-ts ((access recognizer-tuple recog-tuple :true-ts) *ts-t*) ((access recognizer-tuple recog-tuple :false-ts) *ts-nil*)))) (cond ((ts= ts *ts-boolean*) (mv *ts-boolean* (push-lemma (access recognizer-tuple recog-tuple :rune) ttree0))) (t (mv ts (push-lemma (access recognizer-tuple recog-tuple :rune) ttree)))))) (defun type-set-car (ts ttree ttree0) (cond ((ts-intersectp ts *ts-cons*) (mv *ts-unknown* ttree0)) (t (mv *ts-nil* ttree)))) (defun type-set-cdr (ts ttree ttree0) (let ((cdr-ts (ts-builder ts (*ts-proper-cons* *ts-true-list*) (*ts-improper-cons* (ts-complement *ts-true-list*)) (otherwise *ts-nil*)))) (mv cdr-ts (if (ts= cdr-ts *ts-unknown*) ttree0 (puffert ttree))))) (defun type-set-coerce (term ts1 ts2 ttree1 ttree2 ttree0) (cond ((equal (fargn term 2) ''list) ; If the first argument is of type *ts-string* then the result could ; be either nil or a proper cons. But if the first argument isn't ; possibly a string, the result is NIL. (cond ((ts-intersectp *ts-string* ts1) ; We are not really using ts1 here because (coerce x 'list) is always ; a true list. So we report ttree0, not ttree1. (mv *ts-true-list* (puffert ttree0))) (t (mv *ts-nil* (puffert ttree1))))) ((quotep (fargn term 2)) (mv *ts-string* (puffert ttree0))) ((ts-disjointp *ts-non-t-non-nil-symbol* ts2) ; Observe that the first argument (and its ttree1) don't matter here. (mv *ts-string* (puffert ttree2))) (t (mv (ts-union *ts-true-list* *ts-string*) (puffert ttree0))))) (defun type-set-intern-in-package-of-symbol (ts1 ts2 ttree1 ttree2 ttree0) (cond ((ts-disjointp ts1 *ts-string*) (mv *ts-nil* (puffert ttree1))) ((ts-disjointp ts2 *ts-symbol*) (mv *ts-nil* (puffert ttree2))) (t (mv *ts-symbol* (puffert ttree0))))) (defun type-set-length (ts ttree ttree0) (let ((ans (ts-builder ts (*ts-string* *ts-non-negative-integer*) (*ts-cons* *ts-positive-integer*) (otherwise *ts-zero*)))) (cond ((ts= ans *ts-integer*) (mv *ts-integer* (puffert ttree0))) (t (mv ans (puffert ttree)))))) (defun type-set-cons (ts2 ttree ttree0) ; Ts2 is the type set of the second argument of the cons. (let ((ts (ts-builder ts2 (*ts-true-list* *ts-proper-cons*) (otherwise *ts-improper-cons*)))) (cond ((ts= ts *ts-cons*) (mv ts (puffert ttree0))) (t (mv ts (puffert ttree)))))) (defconst *singleton-type-sets* (list *ts-t* *ts-nil* *ts-zero*)) (defun type-set-equal (ts1 ts2 ttree ttree0) (cond ((member ts1 *singleton-type-sets*) (cond ((ts= ts1 ts2) (mv *ts-t* (puffert ttree))) ((ts-intersectp ts1 ts2) (mv *ts-boolean* (puffert ttree0))) (t (mv *ts-nil* (puffert ttree))))) ((ts-intersectp ts1 ts2) (mv *ts-boolean* (puffert ttree0))) (t (mv *ts-nil* (puffert ttree))))) ;; RAG - I added entries here for realp evg. This is probably not ;; needed, since we can't construct realp numbers! (defun type-set-quote (evg) ; Most type-set-xxx functions return a pair consisting of a ts and a ttree. ; But the ttree is irrelevant here and so we don't waste the time passing ; it around. We return the ts of the evg. (cond ((atom evg) (cond ((rationalp evg) (cond ((integerp evg) (cond ((int= evg 0) *ts-zero*) ((> evg 0) *ts-positive-integer*) (t *ts-negative-integer*))) ((> evg 0) *ts-positive-ratio*) (t *ts-negative-ratio*))) #+:non-standard-analysis ((realp evg) (cond ((> evg 0) *ts-positive-non-ratio*) (t *ts-negative-non-ratio*))) ((complex-rationalp evg) *ts-complex-rational*) #+:non-standard-analysis ((complexp evg) *ts-complex-non-rational*) ((symbolp evg) (cond ((eq evg t) *ts-t*) ((eq evg nil) *ts-nil*) (t *ts-non-t-non-nil-symbol*))) ((stringp evg) *ts-string*) (t *ts-character*))) ((true-listp evg) *ts-proper-cons*) (t *ts-improper-cons*))) (defun type-set-char-code (ts ttree ttree0) ; (char-code x) is always a non-negative integer. If x is not a ; characterp, then its code is 0. If x is a character, its code ; might be 0 or positive. (cond ((ts-disjointp ts *ts-character*) (mv *ts-zero* (puffert ttree))) (t (mv *ts-non-negative-integer* (puffert ttree0))))) (defun fn-count-1 (flg x fn-count-acc p-fn-count-acc) ; Warning: Keep this in sync with the var-fn-count-1. ; This definition is similar to var-fn-count-1, except that it counts only fns ; and pseudo-fns, not vars. It was introduced when a check of fn-counts was ; added to ancestors-check1, in order to improve efficiency a bit. (We now use ; var-fn-count for that purpose.) A 2.6% decrease in user time (using Allegro ; 5.0.1) was observed when the fn-counts check was added, yet that test was ; found to be critical in certain cases (see the comment in ancestors-check1). ; We discovered that the Allegro compiler does not do as good a job at tail ; recursion elimination for mutual recursion nests as for single recursion. So ; we code this as a singly recursive function with a flag, flg: When flg is ; nil then x is a term, and otherwise x is a list of terms. We have since also ; taken advantage of the use of this single "flag" function when verifying ; termination and guards. (declare (xargs :guard (and (if flg (pseudo-term-listp x) (pseudo-termp x)) (integerp fn-count-acc) (integerp p-fn-count-acc)) :verify-guards NIL)) (cond (flg (cond ((atom x) (mv fn-count-acc p-fn-count-acc)) (t (mv-let (fn-cnt p-fn-cnt) (fn-count-1 nil (car x) fn-count-acc p-fn-count-acc) (fn-count-1 t (cdr x) fn-cnt p-fn-cnt))))) ((variablep x) (mv fn-count-acc p-fn-count-acc)) ((fquotep x) (mv fn-count-acc (+ p-fn-count-acc (fn-count-evg (cadr x))))) (t (fn-count-1 t (fargs x) (1+ fn-count-acc) p-fn-count-acc)))) (defmacro fn-count (term) `(fn-count-1 nil ,term 0 0)) (defun term-order (term1 term2) ; See term-order1 for comments. ":Doc-Section ACL2::ACL2-built-ins the ordering relation on terms used by ACL2~/ ACL2 must occasionally choose which of two terms is syntactically smaller. The need for such a choice arises, for example, when using equality hypotheses in conjectures (the smaller term is substituted for the larger elsewhere in the formula), in stopping loops in permutative rewrite rules (~pl[loop-stopper]), and in choosing the order in which to try to cancel the addends in linear arithmetic inequalities. When this notion of syntactic size is needed, ACL2 uses ``term order.'' Popularly speaking, term order is just a lexicographic ordering on terms. But the situation is actually more complicated.~/ We define term order only with respect to terms in translated form. ~l[trans]. Constants are viewed as built up by ~em[pseudo-function] applications, as described at the end of this documentation. ~c[Term1] comes before ~c[term2] in the term order iff~bq[] (a) the number of variable occurrences in ~c[term1] is less than that in ~c[term2], or (b) the numbers of variable occurrences in the two terms are equal but the number of function applications in ~c[term1] is less than that in ~c[term2], or (c) the numbers of variable occurrences in the two terms are equal, the numbers of functions applications in the two terms are equal, but pseudo-function application count for ~c[term1] is less than that for ~c[term2], or (d) the numbers of variable occurrences in the two terms are equal, the numbers of functions applications in the two terms are equal, the pseudo-function application counts for the two terms are equal, and ~c[term1] comes before ~c[term2] in a lexicographic ordering, ~ilc[lexorder], based their structure as Lisp objects: ~pl[lexorder]. ~eq[]The function ~c[term-order], when applied to the translations of two ACL2 terms, returns ~c[t] iff the first is ``less than or equal'' to the second in the term order. By ``number of variable occurrences'' we do not mean ``number of distinct variables'' but ``number of times a variable symbol is mentioned.'' ~c[(Cons x x)] has two variable occurrences, not one. Thus, perhaps counterintuitively, a large term that contains only one variable occurrence, e.g., ~c[(standard-char-p (car (reverse x)))] comes before ~c[(cons x x)] in the term order. Since constants contain no variable occurrences and non-constant expressions must contain at least one variable occurrence, constants come before non-constants in the term order, no matter how large the constants. For example, the list constant ~bv[] '(monday tuesday wednesday thursday friday) ~ev[] comes before ~c[x] in the term order. Because term order is involved in the control of permutative rewrite rules and used to shift smaller terms to the left, a set of permutative rules designed to allow the permutation of any two tips in a tree representing the nested application of some function will always move the constants into the left-most tips. Thus, ~bv[] (+ x 3 (car (reverse klst)) (dx i j)) , ~ev[] which in translated form is ~bv[] (binary-+ x (binary-+ '3 (binary-+ (dx i j) (car (reverse klst))))), ~ev[] will be permuted under the built-in commutativity rules to ~bv[] (binary-+ '3 (binary-+ x (binary-+ (car (reverse klst)) (dx i j)))) ~ev[] or ~bv[] (+ 3 x (car (reverse klst)) (dx i j)). ~ev[] Two terms with the same numbers of variable occurrences and function applications and the same pseudo-function application count are ordered by lexicographic means, based on their structures. ~l[lexorder]. Thus, if two terms ~c[(member ...)] and ~c[(reverse ...)] contain the same numbers of variable occurrences and function applications, and no quoted constants, then the ~ilc[member] term is first in the term order because ~ilc[member] comes before ~ilc[reverse] in the term order (which is here reduced to alphabetic ordering). It remains to discuss the notion of ~em[pseudo-function application count]. Clearly, two constants are ordered using cases (c) and (d) of term order, since they each contain 0 variable occurrences and no function calls. This raises the question ``How many function applications are in a constant?'' Because we regard the number of function applications as a more fundamental measure of the size of a constant than lexicographic considerations, we decided that for the purposes of term order, constants would be seen as being built by primitive constructor functions. These constructor functions are not actually defined in ACL2 but merely imagined for the purposes of term order. We here use suggestive names for these imagined functions, ignoring entirely the prior use of these names within ACL2. The imagined applications of these functions are what we refer to as ~em[pseudo-function] applications. The constant function ~c[z] constructs ~c[0]. Positive integers are constructed from ~c[(z)] by the successor function, ~c[s]. Thus ~c[2] is ~c[(s (s (z)))] and contains three function applications. ~c[100] contains one hundred and one applications. Negative integers are constructed from their positive counterparts by ~ilc[-]. Thus, ~c[-2] is ~c[(- (s (s (z))))] and has four applications. Ratios are constructed by the dyadic function ~ilc[/]. Thus, ~c[-1/2] is ~bv[] (/ (- (s (z))) (s (s (z)))) ~ev[] and contains seven applications. Complex rationals are similarly constructed from rationals. All character objects are considered primitive and are constructed by constant functions of the same name. Thus ~c[#\\a] and ~c[#\\b] both contain one application. Strings are built from the empty string, ~c[(o)] by the ``string-cons'' function written ~c[cs]. Thus ~c[\"AB\"] is ~c[(cs (#\\a) (cs (#\\b) (o)))] and contains five applications. Symbols are obtained from strings by ``packing'' the ~ilc[symbol-name] with the unary function ~c[p]. Thus ~c['ab] is ~bv[] (p (cs (#\\a) (cs (#\\b) (o)))) ~ev[] and has six applications. Note that packages are here ignored and thus ~c['acl2::ab] and ~c['my-package::ab] each contain just six applications. Finally, ~il[cons]es are built with ~ilc[cons], as usual. So ~c['(1 . 2)] is ~c[(cons '1 '2)] and contains six applications, since ~c['1] contains two and ~c['2] contains three. This, for better or worse, answers the question ``How many function applications are in a constant?'' Finally, when we refer to the ``pseudo-function application count'', we mean the number of pseudo-function applications as described above, except that we bound this number by the constant ~c[(fn-count-evg-max-val)]. (This bound is important for efficiency, so that constants that are very large ~c[cons] structures do not cause significant slowdown as ACL2 attempts to walk through them while computing their pseudo-function application count.)" (term-order1 term1 term2 nil)) ; Type Prescriptions ; A type-prescription is a structure, below, that describes how to ; compute the type of a term. They are stored on the property list of ; the top function symbol of the term, under the property ; 'type-prescriptions. Unlike Nqthm's "type-prescription-lst" ANY ; enabled type-prescription in 'type-prescriptions may contribute to ; the type-set of the associated function symbol. (defrec type-prescription (basic-ts (nume . term) (hyps . backchain-limit-lst) (vars . rune) . corollary) t) ; Term is a term, hyps is a list of terms, basic-ts is a type-set, and vars is ; a list of variables that occur in term. Let term' be some instance of term ; under the substitution sigma. Then, provided the sigma instance of hyps is ; true, the type-set of term' is the union of basic-ts with the type-sets of ; the sigma images of the vars. Corollary is the theorem (translated term) ; from which this type-prescription was derived. For system-generated ; type-prescriptions it is a term created by convert-type-prescription-to-term. ; Backchain-limit-lst must either be nil, or else a list containing only nil ; and natural numbers, of the same length as hyps. ; (Note: Why do we store the corollary when we could apparently recompute it ; with convert-type-prescription-to-term? The reason is that the computation ; is sensitive to the ens in use and we do not wish the corollary for a ; type-prescription rule to change as the user changes the global enabled ; structure.) ; For example, for APP we might have the type-prescription: ; (make type-prescription :rune ... :nume ... ; :term (app x y) ; :hyps ((true-listp x)) ; :basic-ts *ts-cons* ; :vars '(y) ; :corollary (implies (true-listp x) ; (if (consp (app x y)) ; 't ; (equal (app x y) y)))) ; The above example corresponds to what we'd get from the lemma: ; (implies (true-listp x) ; (or (consp (app x y)) ; (equal (app x y) y))) ; When type-set uses :TYPE-PRESCRIPTION rules it will intersect all ; the known type-sets for term. (defun find-runed-type-prescription (rune lst) ; Lst must be a list of type-prescription rules. We find the first ; one with :rune rune. (cond ((null lst) nil) ((equal rune (access type-prescription (car lst) :rune)) (car lst)) (t (find-runed-type-prescription rune (cdr lst))))) (defconst *expandable-boot-strap-non-rec-fns* '(not implies eq atom eql = /= null endp zerop ; If we ever make 1+ and 1- functions again, they should go back on this list. synp plusp minusp listp return-last mv-list ; We added the-error for Version_4.0 (replaced by the-check after Version_6.1). ; Before that change, but after changing constraint-info to avoid calling ; remove-guard-holders on a definition body (a change in support of ; canonical-ancestors, for use of the Attachment Restriction Lemma in ; justifying attachment to metafunctions and clause-procrocessors, ; cf. chk-evaluator-use-in-rule), the event (defsort :compare< << :prefix <<) ; failed from community book defsort/uniquep.lisp. the-check wormhole-eval force case-split double-rewrite)) ; Warning: All functions listed above must be defun'd non-recursively ; before deftheory definition-minimal-theory! ; There has been some thought about whether we should put IFF on this ; list. We have decided not, because type-set knows a lot about it by ; virtue of its being an equivalence relation. But this position has ; never been seriously scrutinized. ; In a break with nqthm, we have decided to let type-set expand some ; function applications to get better type-sets for them. The ; functions in question are those listed above. ; In an even more pervasive break, we have decided to make type-set ; keep track of the dependencies between literals of the goal clause ; and the type-sets computed. The ttree argument to type-set below is ; a running accumulator that is returned as the second value of ; type-set. Among the tags in the ttree are 'pt tags. The value of ; the tag is a "parent tree" indicating the set of literals of the ; current-clause upon which the type deduction depends. See the Essay ; on Parent Trees. The type-alist in general contains entries of the ; form (term ts . ttree), where ttree is the tag-tree encoding all of ; the 'PTs upon which depend the assertion that term has type-set ts. ; Note on Performance: ; An early time trial detected no measurable difference between the ; old type-set and the new when the ttree is t. This was on a ; collection of simple defuns and thms (flatten, mc-flatten, their ; relation, and a guarded defun of (assoc-eq2 x y alist) where alist ; is a true list of triples of the form (sym1 sym2 . val)) that ; required a total of 15 seconds run-time in both versions. However, ; because the only available "old" ACL2 is the first release, which ; does not have all of the proof techniques in place, and the new ; system does have them in place, it is difficult to make meaningful ; tests. To make matters worse, we are about to go implement forward ; chaining. The bottom line is whether ACL2 is fast enough. We'll ; see... ; We now continue with the development of type-set. (defun mv-atf (not-flg mbt mbf tta fta ttree1 ttree2) ; Every exit of assume-true-false is via this function. See assume- ; true-false for details. It is convenient, and only mildly wrong, to ; think of this function as equivalent to: ; (mv mbt mbf tta fta (cons-tag-trees ttree1 ttree2)). ; This is incorrect on two counts. First, if not-flg is true, we swap ; the roles of mbt/mbf and tta/fta. Second, since the ttree result of ; assume-true-false is irrelevant unless mbt or mbf is t, we sometimes ; produce a nil ttree. ; The reason this function takes two ttrees is that many (but not all) ; paths through assume-true-false have two ttrees in hand at the end. ; One is the ``xttree'' arg of assume-true-false, which was to be ; included in all the ttrees generated by the function. The other is ; some local ttree that describes the derivation of facts during ; assume-true-false. We could combine these two trees before calling ; mv-atf but that would, possibly, waste a cons since the ttrees are ; sometimes ignored. ; Finally, because we know that the ttrees are ignored when mbt and ; mbf are both nil, we sometimes pass in nil for the two ttrees in ; calls of mv-atf where we know they will be ignored. Such a call ; should be taken (by the reader) as a clear signal that the ttrees ; are irrelevant. (if not-flg (mv mbf mbt fta tta (if (or mbt mbf) (cons-tag-trees ttree1 ttree2) nil)) (mv mbt mbf tta fta (if (or mbt mbf) (cons-tag-trees ttree1 ttree2) nil)))) (defun assume-true-false-error (type-alist x temp-temp) (er hard 'assume-true-false-error "It was thought impossible for an equivalence relation, e.g., ~x0, ~ to have anything besides a non-empty proper subset of ~ *ts-boolean* on the type-alist! But in the type-alist ~x1 the ~ term ~x2 has type set ~x3." (ffn-symb x) type-alist x temp-temp)) (defun non-cons-cdr (term) (cond ((variablep term) term) ((fquotep term) term) ((eq (ffn-symb term) 'cons) (non-cons-cdr (fargn term 2))) (t term))) ; Because type-set now uses type-prescription rules with general ; patterns in them (rather than Nqthm-style rules for function ; symbols), we need one-way unification or pattern matching. ; One-way-unify1 can "see" (binary-+ 1 x) in 7, by letting x be 6. Thus, we ; say that binary-+ is an "implicit" symbol to one-way-unify1. Here is the ; current list of implicit symbols. This list is used for heuristic reasons. ; Basically, a quick necessary condition for pat to one-way-unify with term is ; for the function symbols of pat (except for the implicit ones) to be a subset ; of the function smbols of term. (defconst *one-way-unify1-implicit-fns* '(binary-+ binary-* unary-- unary-/ intern-in-package-of-symbol coerce cons)) (mutual-recursion (defun one-way-unify1 (pat term alist) ; This function is a "No-Change Loser" meaning that if it fails and returns nil ; as its first result, it returns the unmodified alist as its second. (declare (xargs :guard (and (pseudo-termp pat) (pseudo-termp term) (alistp alist)))) (cond ((variablep pat) (let ((pair (assoc-eq pat alist))) (cond (pair (cond ((equal (cdr pair) term) (mv t alist)) (t (mv nil alist)))) (t (mv t (cons (cons pat term) alist)))))) ((fquotep pat) (cond ((equal pat term) (mv t alist)) (t (mv nil alist)))) ((variablep term) (mv nil alist)) ((fquotep term) ; Caution: If you change the code below, update *one-way-unify1-implicit-fns*. ; We have historically attempted to unify ``constructor'' terms with explicit ; values, and we try to simulate that here, treating the primitive arithmetic ; operators, intern-in-package-of-symbol, coerce (to a very limited extent), ; and, of course, cons, as constructors. ; In order to prevent loops, we insist that one-way-unification does not ; present the rewriter with ever-more-complex goals. Robert Krug has sent the ; following examples, which motivated the controls in the code for binary-+ and ; binary-* below. ; (defstub foo (x) t) ; (defaxiom foo-axiom ; (equal (foo (* 2 x)) ; (foo x))) ; (thm ; (foo 4)) ; :u ; (defaxiom foo-axiom ; (equal (foo (+ 1 x)) ; (foo x))) ; (thm ; (foo 4)) ; Another interesting example is (thm (foo 4)) after replacing the second ; foo-axiom with (equal (foo (+ -1 x)) (foo x)). (cond ((acl2-numberp (cadr term)) (let ((ffn-symb (ffn-symb pat))) (case ffn-symb (binary-+ (cond ((quotep (fargn pat 1)) (let ((new-evg (- (cadr term) (fix (cadr (fargn pat 1)))))) (cond ((<= (acl2-count new-evg) (acl2-count (cadr term))) (one-way-unify1 (fargn pat 2) (kwote new-evg) alist)) (t (mv nil alist))))) ((quotep (fargn pat 2)) (let ((new-evg (- (cadr term) (fix (cadr (fargn pat 2)))))) (cond ((<= (acl2-count new-evg) (acl2-count (cadr term))) (one-way-unify1 (fargn pat 1) (kwote new-evg) alist)) (t (mv nil alist))))) (t (mv nil alist)))) (binary-* (cond ((or (not (integerp (cadr term))) (int= (cadr term) 0)) (mv nil alist)) ((and (quotep (fargn pat 1)) (integerp (cadr (fargn pat 1))) (> (abs (cadr (fargn pat 1))) 1)) (let ((new-term-evg (/ (cadr term) (cadr (fargn pat 1))))) (cond ((integerp new-term-evg) (one-way-unify1 (fargn pat 2) (kwote new-term-evg) alist)) (t (mv nil alist))))) ((and (quotep (fargn pat 2)) (integerp (cadr (fargn pat 2))) (> (abs (cadr (fargn pat 2))) 1)) (let ((new-term-evg (/ (cadr term) (cadr (fargn pat 2))))) (cond ((integerp new-term-evg) (one-way-unify1 (fargn pat 1) (kwote new-term-evg) alist)) (t (mv nil alist))))) (t (mv nil alist)))) ; We once were willing to unify (- x) with 3 by binding x to -3. John Cowles' ; experience with developing ACL2 arithmetic led him to suggest that we not ; unify (- x) with any constant other than negative ones. Similarly, we do not ; unify (/ x) with any constant other than those between -1 and 1. The code ; below reflects these suggestions. (unary-- (cond ((>= (+ (realpart (cadr term)) (imagpart (cadr term))) 0) (mv nil alist)) (t (one-way-unify1 (fargn pat 1) (kwote (- (cadr term))) alist)))) (unary-/ (cond ((or (>= (* (cadr term) (conjugate (cadr term))) 1) (eql 0 (cadr term))) (mv nil alist)) (t (one-way-unify1 (fargn pat 1) (kwote (/ (cadr term))) alist)))) (otherwise (mv nil alist))))) ((symbolp (cadr term)) (cond ((eq (ffn-symb pat) 'intern-in-package-of-symbol) (let ((pkg (symbol-package-name (cadr term))) (name (symbol-name (cadr term)))) (mv-let (ans alist1) ; We are careful with alist to keep this a no change loser. (one-way-unify1 (fargn pat 1) (kwote name) alist) (cond (ans ; We are unifying 'pkg::name with (intern-in-package-of-symbol x y) where x is ; now unified with "name". So when is (intern-in-package-of-symbol "name" y) ; equal to pkg::name? It would suffice to unify y with any symbol in pkg. It ; might be that y is already such a quoted symbol. Or perhaps we could unify y ; with pkg::name, which is one symbol we know is in pkg. But note that it is ; not necessary that y unify with a symbol in pkg. It would suffice, for ; example, if y could be unified with a symbol in some other package, say gkp, ; with the property that pkg::name was imported into gkp, for then gkp::name ; would be pkg::name. Thus, as is to be expected by all failed unifications, ; failure does not mean there is no instance that is equal to the term. ; Suppose that y is not a quoted symbol and is not a variable (which could ; therefore be unified with pkg::name). What else might unify with "any symbol ; in pkg?" At first sight one might think that if y were ; (intern-in-package-of-symbol z 'pkg::name2) then the result is a symbol in ; pkg no matter what z is. (The idea is that one might think that ; (intern-in-package-of-symbol z 'pkg::name2) is "the" generic expression of ; "any symbol in pkg.") But that is not true because for certain z it is ; possible that the result isn't in pkg. Consider, for example, the ; possibility that gkp::zzz is imported into pkg so that if z is "ZZZ" the ; result is a symbol in gkp not pkg. (cond ((and (nvariablep (fargn pat 2)) (fquotep (fargn pat 2))) (cond ((not (symbolp (cadr (fargn pat 2)))) ; (intern-in-package-of-symbol x y) is NIL if y is not a symbol. So we win if ; term is 'nil and lose otherwise. If we win, note that x is unified ; (unnecessarily) with "NIL" in alist1 and so we report the win with alist! If ; we lose, we have to report alist to be a no change loser. So its alist ; either way. (mv (if (equal term *nil*) ans nil) alist)) (t (if (equal pkg (symbol-package-name (cadr (fargn pat 2)))) (mv ans alist1) (mv nil alist))))) (t (mv-let (ans alist2) (one-way-unify1 (fargn pat 2) term alist1) (cond (ans (mv ans alist2)) (t (mv nil alist))))))) (t (mv nil alist)))))) (t (mv nil alist)))) ((stringp (cadr term)) (cond ((and (eq (ffn-symb pat) 'coerce) (equal (fargn pat 2) ''string)) (one-way-unify1 (fargn pat 1) (kwote (coerce (cadr term) 'list)) alist)) (t (mv nil alist)))) ((consp (cadr term)) (cond ((eq (ffn-symb pat) 'cons) ; We have to be careful with alist below so we are a no change loser. (mv-let (ans alist1) (one-way-unify1 (fargn pat 1) (kwote (car (cadr term))) alist) (cond (ans (mv-let (ans alist2) (one-way-unify1 (fargn pat 2) (kwote (cdr (cadr term))) alist1) (cond (ans (mv ans alist2)) (t (mv nil alist))))) (t (mv nil alist))))) (t (mv nil alist)))) (t (mv nil alist)))) ((cond ((flambda-applicationp pat) (equal (ffn-symb pat) (ffn-symb term))) (t (eq (ffn-symb pat) (ffn-symb term)))) (cond ((eq (ffn-symb pat) 'equal) (one-way-unify1-equal (fargn pat 1) (fargn pat 2) (fargn term 1) (fargn term 2) alist)) (t (mv-let (ans alist1) (one-way-unify1-lst (fargs pat) (fargs term) alist) (cond (ans (mv ans alist1)) (t (mv nil alist))))))) (t (mv nil alist)))) (defun one-way-unify1-lst (pl tl alist) ; This function is NOT a No Change Loser. That is, it may return nil ; as its first result, indicating that no substitution exists, but ; return as its second result an alist different from its input alist. (declare (xargs :guard (and (pseudo-term-listp pl) (pseudo-term-listp tl) (alistp alist)))) (cond ((null pl) (mv t alist)) (t (mv-let (ans alist) (one-way-unify1 (car pl) (car tl) alist) (cond (ans (one-way-unify1-lst (cdr pl) (cdr tl) alist)) (t (mv nil alist))))))) (defun one-way-unify1-equal1 (pat1 pat2 term1 term2 alist) ; At first glance, the following code looks more elaborate than ; necessary. But this function is supposed to be a No Change Loser. ; The first time we coded this we failed to ensure that property. The ; bug is the result of fuzzy thinking in the vicinity of conjunctive ; subgoals. Suppose success requires success on x and success on y. ; The naive way to code it is (mv-let (ans nochanger) x (if ans y (mv ; nil nochanger))), i.e., to solve the x problem and if you win, ; return your solution to the y problem. But if x wins it will have ; changed nochanger. If y then loses, it returns the changed ; nochanger produced by x. Clearly, if x might win and change things ; but ultimate success also depends on y, you must preserve the ; original inputs and explicitly revert to them if y loses. (mv-let (ans alist1) (one-way-unify1 pat1 term1 alist) (cond (ans (mv-let (ans alist2) (one-way-unify1 pat2 term2 alist1) (cond (ans (mv ans alist2)) (t (mv nil alist))))) (t (mv nil alist))))) (defun one-way-unify1-equal (pat1 pat2 term1 term2 alist) (mv-let (ans alist) (one-way-unify1-equal1 pat1 pat2 term1 term2 alist) (cond (ans (mv ans alist)) (t (one-way-unify1-equal1 pat2 pat1 term1 term2 alist))))) ) (defun one-way-unify (pat term) (declare (xargs :guard (and (pseudo-termp pat) (pseudo-termp term)))) ; This function returns two values. The first is T or NIL, according to ; whether unification succeeded. The second value returned is a symbol alist ; that when substituted into pat will produce term, when the unification ; succeeded. ; The use of the phrase ``unify'' here is somewhat opaque but is ; historically justified by its usage in nqthm. Really, all we are ; doing is matching because we do not treat the ``variable symbols'' ; in term as instantiable. ; Note that the fact that this function returns nil should not be ; taken as a sign that no substition makes pat equal to term in the ; current theory. For example, we fail to unify (+ x x) with '2 even ; though '((x . 1)) does the job. (one-way-unify1 pat term nil)) ; Essay on the Invariants on Type-alists, and Canonicality ; There are four invariants on type-alists. ; First invariant on type-alists: No quotep is bound in a type-alist. ; Second invariant on type-alists: when (equiv x y) is bound in a type-alist, ; it is bound to a type of *ts-t* or *ts-nil*. ; Unlike the first two invariants, we will not depend on the third and fourth ; for soundness. We'll present them in a moment. We will maintain them both ; by insisting that the only operations allowed for extending type-alists are ; extend-type-alist-simple, extend-type-alist, extend-type-alist1, and ; extend-type-alist-with-bindings, and zip-variable-type-alist, called in ; accordance with their guards. ; Definition. We say that a term u is "canonical" for an equivalence relation ; equiv of the current ACL2 world and a type-alist if no entry in type-alist is ; of the form ((equiv u z) *ts-t* . ttree). When equiv and type-alist are ; understood, we may say simply that u is canonical. ; Third invariant on type-alists: For every element ((equiv x y) ts . ttree) of ; a type-alist for which equiv is an equivalence relation in the current ACL2 ; world, y is canonical. Moreover, if ts is *ts-nil*, then x is also ; canonical; and, if ts is *ts-t*, then (term-order y x) and x is not y. ; Finally, for each x there is at most one entry in type-alist of the form ; ((equiv x y) *ts-t* . ttree); in this case, or when x = y and there is no ; entry of the form ((equiv y y') *ts-t* . ttree), we say that y is the ; canonical form of x. ; Although we have decided to maintain the third invariant, if later we decide ; not to be insistent on that, we may obtain some speed-up by replacing some ; calls of extend-type-alist by extend-type-alist-simple. Look for the string ; ";;*** -simple" to see some places where that might be especially ; appropriate. Note that even extend-type-alist-simple is careful to preserve ; the first two invariants. ; The fourth invariant on type-alists: No term is ever bound to *ts-unknown*. (defun canonical-representative (equiv term type-alist) ; This function returns a tuple (mv occursp canonicalp term-canon ttree) ; satifying the following description. ; Occursp holds iff, for some x, (equiv term x) or (equiv x term) is bound in ; type-alist. ; Canonicalp is t or nil, and it is t iff term is canonical (see Essay above). ; Term-canon is the canonical form of term, i.e., is term if canonicalp is t ; and otherwise is the unique x such that ((equiv term x) *ts-t* . tt) belongs ; to type-alist for some tt. ; Ttree is a tag-tree justifying the equality of term to term-canon. ; We will use the following easy-to-prove theorem: ; (occursp = nil) ; implies ; (canonicalp = t) ; which implies ; (term-canon = term) ; We will also use the fact that if canonicalp is t then ttree is nil. (declare (xargs :guard (symbolp equiv))) (cond ((null type-alist) (mv nil t term nil)) (t (let ((first-term (caar type-alist)) (ts (cadar type-alist))) (cond ((or (variablep first-term) ; Recall the first invariant on type-alists: type-alists do not bind quoteps. (not (eq (ffn-symb first-term) equiv))) (canonical-representative equiv term (cdr type-alist))) ((equal term (fargn first-term 1)) (cond ((ts= ts *ts-t*) (mv t nil (fargn first-term 2) (cddar type-alist))) (t (mv t t term nil)))) ((equal term (fargn first-term 2)) (mv t t term nil)) (t (canonical-representative equiv term (cdr type-alist)))))))) (defun subst-type-alist1-check (old equiv type-alist) (cond ((null type-alist) nil) (t (or (let ((term (caar type-alist))) (and (nvariablep term) (eq (ffn-symb term) equiv) (or (equal old (fargn term 1)) (equal old (fargn term 2))))) (subst-type-alist1-check old equiv (cdr type-alist)))))) (defun nil-fn () ; This trivial definition is used for making a sort of placeholder entry, ; *nil-fn-ts-entry*, when simplifying type-alists. See subst-type-alist1. (declare (xargs :guard t :mode :logic)) nil) (defconst *nil-fn-ts-entry* (list* (cons-term 'nil-fn nil) *ts-nil* (push-lemma '(:definition nil-fn) nil))) (defun subst-type-alist1 (new old equiv ttree type-alist acc) ; This subsidiary function of subst-type-alist is coded so that we do not do ; any more consing than necessary. Moreover, we expect it to be extremely rare ; that old and new are already related (and hence negatively so) by equiv in ; type-alist; someone is calling this function to create such a relationship. ; See also the comment in subst-type-alist. (cond ((null type-alist) (reverse acc)) (t (subst-type-alist1 new old equiv ttree (cdr type-alist) (let ((term (caar type-alist))) (cond ((and (nvariablep term) (eq (ffn-symb term) equiv) (or (equal old (fargn term 1)) (equal old (fargn term 2)))) ; Note that since subst-type-alist1 is only called by subst-type-alist, and ; subst-type-alist assumes that new and old are canonical in type-alist and ; distinct, we know that the third invariant on type-alists is being preserved: ; we are not creating an entry binding (equiv new new) to *ts-t*. (let ((equiv-call (if (equal old (fargn term 1)) (cons-term* equiv new (fargn term 2)) (cons-term* equiv (fargn term 1) new)))) (cond ((quotep equiv-call) ; If we keep this entry, we will violate the first invariant on type-alists. ; But our algorithm for infect-new-type-alist-entries depends on not dropping ; entries! So we add a silly entry instead. We could have simply retained the ; existing entry, but this way we can see nil-fn explicitly during debugging, ; and we can contemplate cleaning up the type-alist to remove this specific ; entry. Why not simply drop the entry entirely? The problem is that ; subfunctions of assume-true-false make the assumption that they extend the ; type-alist; see infect-new-type-alist-entries. ; It seems worth checking that we're not losing a contradiction, so we check ; that in an assertion. We might drop this assertion in the future. (assert$ (equal (ts= (cadar type-alist) *ts-nil*) (equal equiv-call *nil*)) (cons *nil-fn-ts-entry* acc))) (t (cons (list* equiv-call (cadar type-alist) ; Note on Tracking Equivalence Runes: If we ever give runic names to the ; theorems establishing equivalence- relation-hood and track those names ; through geneqvs, then we ought to push the appropriate rune here, rather than ; use puffert, which was intended for primitives and is thus here somewhat ; misused unless perhaps equiv is 'equal. There are many other places where ; this comment applies. You should inspect every use of puffert below and ask ; the question: is equivalential reasoning happening here or is it really ; primitive type reasoning? We added a function equivalence-rune to record ; commutativity in bdd processing, and this function may be of use here. (puffert (cons-tag-trees (cddar type-alist) ttree))) acc))))) (t (cons (car type-alist) acc)))))))) (defun subst-type-alist (new old equiv ttree type-alist) ; This function creates a new type-alist by replacing each term of the form ; (equiv old x) bound in type-alist by (equiv new x), and each remaining term ; of the form (equiv x old) bound in type-alist by (equiv x new), respectively. ; Each time it makes such a replacement it records ttree as the reason why that ; step is valid. ; We assume that new and old are canonical in type-alist and distinct. (cond ((subst-type-alist1-check old equiv type-alist) (subst-type-alist1 new old equiv ttree type-alist nil)) (t type-alist))) (defun infect-type-alist-entry (entry ttree) ; Entry is of the form (term ts . ttree1) and we add ttree to ttree1. (cons (car entry) (cons (cadr entry) (cons-tag-trees (cddr entry) ttree)))) (defun infect-new-type-alist-entries2 (new-type-alist old-type-alist ttree) ; We infect the newly modified entries in new-type-alist. See ; infect-new-type-alist-entries. (cond ((null new-type-alist) nil) ((equal (caar new-type-alist) (caar old-type-alist)) (cons (car new-type-alist) (infect-new-type-alist-entries2 (cdr new-type-alist) (cdr old-type-alist) ttree))) (t (cons (infect-type-alist-entry (car new-type-alist) ttree) (infect-new-type-alist-entries2 (cdr new-type-alist) (cdr old-type-alist) ttree))))) (defun infect-new-type-alist-entries1 (new-type-alist old-type-alist ttree n) ; We infect the newly created entries in new-type-alist. See ; infect-new-type-alist-entries. (if (zp n) (infect-new-type-alist-entries2 new-type-alist old-type-alist ttree) (cons (infect-type-alist-entry (car new-type-alist) ttree) (infect-new-type-alist-entries1 (cdr new-type-alist) old-type-alist ttree (1- n))))) (defun infect-new-type-alist-entries (new-type-alist old-type-alist ttree) ; New type-alist is an extension of old-type-alist, and ttree ; contains any assumptions made while deriving the extension. We ; need to infect the new entries with these assumptions. This is ; made slightly more complex by the fact that new-type-alist may ; actually be an extension of a modification of old-type-alist ; due to equality facts being added. (See extend-type-alist1.) ; However, that modification is still in 1:1 correspondence with the ; original, i.e., there are no new entries, just modified entries. (if (null ttree) new-type-alist (infect-new-type-alist-entries1 new-type-alist old-type-alist ttree (- (length new-type-alist) (length old-type-alist))))) (defun extend-type-alist-simple (term ts ttree type-alist) ; This function extends type-alist, essentially by adding the entry (term ts . ; ttree). However, this function preserves the first two invariants on ; type-alists; see the "Essay on the Invariants on Type-alists, and ; Canonicality." See also extend-type-alist, which is similar but also ; preserves the third invariant on type-alists. ; This function should never be called on a term that is a call of an ; equivalence relation. When viewed that way, it trivially preserves the third ; invariant on type-alists as well. (cond ((ts= ts *ts-unknown*) type-alist) ((variablep term) (cons (list* term ts ttree) type-alist)) ((fquotep term) type-alist) (t (cons (list* term ts ttree) type-alist)))) (defun extend-type-alist1 (equiv occursp1 occursp2 both-canonicalp arg1-canon arg2-canon swap-flg term ts ttree type-alist) ; This function creates a type-alist in which, intuitively, we bind the term ; (equiv arg1-canon arg2-canon) to ts unless the order is "wrong", in which ; case we use (equiv arg2-canon arg1-canon) instead. ; More precisely, it returns a type-alist that is implied by the current ; type-alist together with the assertion that (equiv arg1-canon arg2-canon) has ; type-set ts, under the following assumptions: ; equiv is an equivalence relation in the current ACL2 world; ; (equiv arg1-canon arg2-canon) is the same as term when (and (not swap-flg) ; both-canonicalp) is non-nil; ; swap-flg is non-nil iff (term-order arg1-canon arg2-canon); ; occurs1p and arg1-canon are returned by some single call of the function ; canonical-representative; ; occurs2p and arg2-canon are returned by some single call of the function ; canonical-representative; ; arg1-canon and arg2-canon are canonical in type-alist (by the two preceding ; assumptions) and distinct. This is important for the correctness of the ; calls of subst-type-alist; and ; ts is either *ts-t* or *ts-nil*. (cons (cond ((and (not swap-flg) both-canonicalp) ; Then term is the term to push on type-alist; no need to cons up a new term. (list* term ts ttree)) (swap-flg (list* (cons-term* equiv arg2-canon arg1-canon) ts (puffert ttree))) (t (list* (cons-term* equiv arg1-canon arg2-canon) ts (puffert ttree)))) (cond ((ts= ts *ts-nil*) type-alist) (swap-flg (cond (occursp2 ; It's easy to see that occursp2 holds if arg2-canon is an argument of an equiv ; term bound in type-alist, even without assuming that type-alist satisfies the ; third invariant on type-alists. Hence if occurs2p fails, there is no ; substituting to be done. (subst-type-alist arg1-canon arg2-canon equiv ttree type-alist)) (t type-alist))) (t (cond (occursp1 ; See comment above for the entirely analogous situation when swap-flg = t. (subst-type-alist arg2-canon arg1-canon equiv ttree type-alist)) (t type-alist)))))) ; Regarding the maintenance of the second invariant on type alists: ; In the case that ; (and (not (ts= ts *ts-t*)) ; (not (ts= ts *ts-nil*)) ; (equivalence-relationp (ffn-symb term) wrld)) ; we used to return an unchanged type-alist when extending a type-alist. ; However, we already implicitly use (I think) the fact that equivalence ; relations are boolean-valued. So, we will do just a bit better in the new ; code. ; Certain violations of the Second invariant on type-alists -- when (equiv x y) ; is bound in a type-alist, it is bound to a type of *ts-t* or *ts-nil* -- is ; reported in assume-true-false by the error function assume-true-false-error, ; which has caught an error in the past. See the "Essay on the Invariants on ; Type-alists, and Canonicality." (defun extend-type-alist (term ts ttree type-alist wrld) ; This function extends type-alist so that term gets type-set ts with the ; indicated ttree. Unlike extend-type-alist-simple, it pays careful attention ; to equivalence relations in an attempt to maintain the third invariant on ; type-alists; see the "Essay on the Invariants on Type-alists, and ; Canonicality." (declare (xargs :guard (and (pseudo-termp term) (not (quotep term))))) (cond ((and (nvariablep term) (not (fquotep term)) (equivalence-relationp (ffn-symb term) wrld)) (cond ((equal (fargn term 1) (fargn term 2)) ; It's bizarre to imagine (ts= ts *ts-t*) being false here, so we'll ignore the ; information we could obtain if it were false. type-alist) ((not (or (ts= ts *ts-t*) (ts= ts *ts-nil*))) (cond ((ts-intersectp ts *ts-nil*) type-alist) (t (extend-type-alist term *ts-t* (puffert ttree) type-alist wrld)))) (t (let ((equiv (ffn-symb term)) (arg1 (fargn term 1)) (arg2 (fargn term 2))) (mv-let (occursp1 canonicalp1 arg1-canon ttree1) (canonical-representative equiv arg1 type-alist) (mv-let (occursp2 canonicalp2 arg2-canon ttree2) (canonical-representative equiv arg2 type-alist) (cond ((equal arg1-canon arg2-canon) type-alist) (t (let ((swap-flg (term-order arg1-canon arg2-canon))) (extend-type-alist1 equiv occursp1 occursp2 (and canonicalp1 canonicalp2) arg1-canon arg2-canon swap-flg term ts (cons-tag-trees ttree1 (cons-tag-trees ttree2 ttree)) type-alist)))))))))) (t (extend-type-alist-simple term ts ttree type-alist)))) (defun zip-variable-type-alist (vars pairs) ; Vars must be a list of distinct variables. Pairs must be a list of the ; same length as vars, pairing type-sets to ttrees. This function is ; like (pairlis$ vars pairs) except that it deletes any binding to *ts-unknown*. ; Under the guards stated, we guarantee the result is a type-alist satisfying ; our invariants. (cond ((null vars) nil) ((ts= (caar pairs) *ts-unknown*) (zip-variable-type-alist (cdr vars) (cdr pairs))) (t (cons (cons (car vars) (car pairs)) (zip-variable-type-alist (cdr vars) (cdr pairs)))))) (defun assoc-equiv (fn arg1 arg2 alist) ; This function is equivalent to ; (or (assoc-equal (list fn arg1 arg2) alist) ; (assoc-equal (list fn arg2 arg1) alist)) ; except that it looks for both at the same time and returns whichever ; one it finds first. We assume that the car of each pair in ; alist is a non-quote term. (cond ((eq alist nil) nil) ((and (not (variablep (caar alist))) (eq (ffn-symb (caar alist)) fn) (if (equal (fargn (caar alist) 2) arg2) (equal (fargn (caar alist) 1) arg1) (and (equal (fargn (caar alist) 1) arg2) (equal (fargn (caar alist) 2) arg1)))) (car alist)) (t (assoc-equiv fn arg1 arg2 (cdr alist))))) (defun assoc-equiv+ (equiv arg1 arg2 type-alist) ; This function body closely parallels code in the 'equal and ; equivalence-relationp cases of assume-true-false. (cond ((equal arg1 arg2) (mv *ts-t* (puffert nil))) ((and (eq equiv 'equal) (quotep arg1) (quotep arg2)) (mv *ts-nil* (push-lemma '(:executable-counterpart equal) nil))) (t (mv-let (occursp1 canonicalp1 arg1-canon ttree1) (canonical-representative equiv arg1 type-alist) (declare (ignore canonicalp1)) (cond ((and occursp1 (equal arg1-canon arg2)) (mv *ts-t* (puffert ttree1))) ((and occursp1 (eq equiv 'equal) (quotep arg1-canon) (quotep arg2)) (mv *ts-nil* (push-lemma '(:executable-counterpart equal) ttree1))) (t (mv-let (occursp2 canonicalp2 arg2-canon ttree2) (canonical-representative equiv arg2 type-alist) (declare (ignore canonicalp2)) (cond ((and occursp2 (equal arg1-canon arg2-canon)) (mv *ts-t* (puffert (cons-tag-trees ttree1 ttree2)))) ((and (eq equiv 'equal) occursp2 (quotep arg1-canon) (quotep arg2-canon)) (mv *ts-nil* (push-lemma '(:executable-counterpart equal) (cons-tag-trees ttree1 ttree2)))) (t (let ((temp-temp (assoc-equiv equiv arg1-canon arg2-canon type-alist))) (cond (temp-temp (cond ((ts= (cadr temp-temp) *ts-t*) ; See comment in corresponding place in the 'equal case of assume-true-false. (mv (er hard 'assoc-equiv+ "Please send the authors of ACL2 a replayable ~ transcript of this problem if possible, so that ~ they can see what went wrong in the function ~ assoc-equiv+. The offending call was ~x0. The ~ surprising type-set arose from a call of ~x1." (list 'assoc-equiv+ (kwote equiv) (kwote arg1) (kwote arg2) type-alist) (list 'assoc-equiv (kwote equiv) (kwote arg1-canon) (kwote arg2-canon) ')) nil)) ((ts= (cadr temp-temp) *ts-nil*) (mv *ts-nil* (cons-tag-trees (cddr temp-temp) (cons-tag-trees ttree1 ttree2)))) (t (let ((erp (assume-true-false-error type-alist (mcons-term* equiv arg1-canon arg2-canon) (cadr temp-temp)))) (mv erp nil))))) (t (mv nil nil))))))))))))) (defun assoc-type-alist (term type-alist wrld) (cond ((variablep term) (let ((temp (assoc-eq term type-alist))) (if temp (mv (cadr temp) (cddr temp)) (mv nil nil)))) ((fquotep term) (mv nil nil)) ((equivalence-relationp (ffn-symb term) wrld) (assoc-equiv+ (ffn-symb term) (fargn term 1) (fargn term 2) type-alist)) (t (let ((temp (assoc-equal term type-alist))) (if temp (mv (cadr temp) (cddr temp)) (mv nil nil)))))) (defun look-in-type-alist (term type-alist wrld) (mv-let (ts ttree) (assoc-type-alist term type-alist wrld) (mv (if ts ts *ts-unknown*) ttree))) (defun member-char-stringp (chr str i) (declare (xargs :guard (and (stringp str) (integerp i) (< i (length str))) :measure (nfix (+ 1 i)))) (cond ((not (mbt (integerp i))) nil) ((< i 0) nil) (t (or (eql chr (char str i)) (member-char-stringp chr str (1- i)))))) (defun terminal-substringp1 (str1 str2 max1 max2) (declare (xargs :guard (and (stringp str1) (stringp str2) (integerp max1) (integerp max2) (< max1 (length str1)) (< max2 (length str2)) (<= max1 max2)) :measure (nfix (+ 1 max1)))) (cond ((not (mbt (integerp max1))) nil) ((< max1 0) t) ((eql (char str1 max1) (char str2 max2)) (terminal-substringp1 str1 str2 (1- max1) (1- max2))) (t nil))) (defun terminal-substringp (str1 str2 max1 max2) (declare (xargs :guard (and (stringp str1) (stringp str2) (integerp max1) (integerp max2) (< max1 (length str1)) (< max2 (length str2))))) (cond ((< max2 max1) nil) (t (terminal-substringp1 str1 str2 max1 max2)))) (defun evg-occur (x y) ; Consider the idealized inductive construction of the ACL2 objects x and y as ; described in the comment for var-fn-count. Imagine that x and y are so ; represented. Then this function answers the question: "Does x occur in y?". ; This function is only used heuristically, so we are allowed to deviate from ; the above-mentioned induction construction. ; A comment had been here for a long time up through Version 3.2.1, asking if ; we have to look into symbol-package-names too. This function is only used ; heuristically, so we choose not to modify it at this time. (declare (xargs :guard t)) (cond ((atom y) (cond ((characterp y) (and (characterp x) (eql x y))) ((stringp y) (cond ((characterp x) (member-char-stringp x y (1- (length y)))) ((stringp x) (terminal-substringp x y (1- (length x)) (1- (length y)))) (t nil))) ((symbolp y) (cond ((characterp x) (let ((sny (symbol-name y))) (member-char-stringp x sny (1- (length sny))))) ((stringp x) (let ((sny (symbol-name y))) (terminal-substringp x sny (1- (length x)) (1- (length sny))))) ((symbolp x) (eq x y)) (t nil))) ((integerp y) (and (integerp x) (or (int= x y) (and (<= 0 x) (<= x (if (< y 0) (- y) y)))))) ((rationalp y) ; We know y is a non-integer rational. X occurs in it either because ; x is the same non-integer rational or x is an integer that occurs in ; the numerator or denominator. (cond ((integerp x) (or (evg-occur x (numerator y)) (evg-occur x (denominator y)))) ((rationalp x) (= x y)) (t nil))) ((complex-rationalp y) ; We know y is a complex rational. X occurs in it either because ; x is the same complex rational or x is a rational that occurs in ; the real or imaginary part. (cond ((rationalp x) (or (evg-occur x (realpart y)) (evg-occur x (imagpart y)))) ((complex-rationalp x) (= x y)) (t nil))) (t (er hard? 'evg-occur "Surprising case: ~x0" `(evg-occur ,x ,y))))) (t (or (evg-occur x (car y)) (evg-occur x (cdr y)))))) (mutual-recursion (defun occur (term1 term2) (declare (xargs :guard (and (pseudo-termp term1) (pseudo-termp term2)))) (cond ((variablep term2) (eq term1 term2)) ((fquotep term2) (cond ((quotep term1) (evg-occur (cadr term1) (cadr term2))) (t nil))) ((equal term1 term2) t) (t (occur-lst term1 (fargs term2))))) (defun occur-lst (term1 args2) (declare (xargs :guard (and (pseudo-termp term1) (pseudo-term-listp args2)))) (cond ((endp args2) nil) (t (or (occur term1 (car args2)) (occur-lst term1 (cdr args2)))))) ) ; Rockwell Addition: I found an exponential explosion in worse-than ; and it is fixed here. ; Up through Version 2.5 worse-than was defined as shown below: ; (defun worse-than (term1 term2) ; (cond ((quick-worse-than term1 term2) t) ; ((variablep term1) nil) ; ((fquotep term1) nil) ; (t (worse-than-lst (fargs term1) term2)))) ; But we discovered via Rockwell examples that this performs terribly ; if term1 and term2 are variants of each other, i.e., the same up to ; the variables used. So we have implemented a short circuit. (mutual-recursion (defun pseudo-variantp (term1 term2) ; We determine whether term1 and term2 are identical up to the ; variables used, down to the variables in term1. ; If (pseudo-variantp term1 term2) is true then we know that ; (worse-than term1 term2) is nil. ; Note: In the theorem proving literature, the word ``variant'' is ; used to mean that the two terms are identical up to a renaming of ; variables. That is checked by our function variantp. This function ; is different and of little logical use. It does not insist that a ; consistent renaming of variable occur, just that the two terms are ; isomorphic down to the variable symbols. It is here to avoid a very ; bad case in the worse-than check. (declare (xargs :guard (and (pseudo-termp term1) (pseudo-termp term2)))) (cond ((variablep term1) ; Suppose that term1 is a variable. The only thing that it can be ; worse than is a quote. That is, if we return t, then we must ensure ; that either term2 is term1 or (worse-than term1 term2) is nil. The ; worse-than will be nil unless term2 is a quote. See the exponential ; sequences below. (not (quotep term2))) ((fquotep term1) (equal term1 term2)) ((or (variablep term2) (fquotep term2)) nil) (t (and (equal (ffn-symb term1) (ffn-symb term2)) (pseudo-variantp-list (fargs term1) (fargs term2)))))) (defun pseudo-variantp-list (args1 args2) (declare (xargs :guard (and (pseudo-term-listp args1) (pseudo-term-listp args2)))) (cond ((endp args1) t) (t (and (pseudo-variantp (car args1) (car args2)) (pseudo-variantp-list (cdr args1) (cdr args2))))))) ; It turns out that without the use of pseudo-variantp in the ; definition of worse-than, below, worse-than's cost grows ; exponentially on pseudo-variant terms. Consider the sequence of ; terms (f a a), (f a (f a a)), ..., and the corresponding sequence ; with variable symbol b used in place of a. Call these terms a1, a2, ; ..., and b1, b2, ... Then if pseudo-variantp were redefined to ; return nil, here are the real times taken to do (worse-than a1 b1), ; (worse-than a2 b2), ...: 0.000, 0.000, 0.000, 0.000, 0.000, 0.000, ; 0.000, 0.020, 0.080, 0.300, 1.110, 4.230, 16.390. This was measured ; on a 330 MHz Pentium II. ; (progn ; (time ; (new-worse-than ; '(f a a) ; '(f b b))) ; ; (time ; (new-worse-than ; '(f a (f a a)) ; '(f b (f b b)))) ; ; (time ; (new-worse-than ; '(f a (f a (f a a))) ; '(f b (f b (f b b))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a a)))) ; '(f b (f b (f b (f b b)))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a a))))) ; '(f b (f b (f b (f b (f b b))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a a)))))) ; '(f b (f b (f b (f b (f b (f b b)))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a a))))))) ; '(f b (f b (f b (f b (f b (f b (f b b))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a a)))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b b)))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a (f a a)))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))) ; ; (time ; (new-worse-than ; '(f a ; (f a (f a (f a (f a (f a (f a (f a (f a (f a (f a (f a a)))))))))))) ; '(f b ; (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))))) ; ; (time ; (new-worse-than ; '(f a ; (f a ; (f a ; (f a (f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))))))) ; '(f b ; (f b ; (f b ; (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))) ; )) ; ) ; If pseudo-variantp is defined so that instead of (not (quotep ; term2)) it insists of (variablep term2) when (variablep term1), then ; the following sequence goes exponential even though the preceding ; one does not. ; (progn ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))) ; )) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))) ; )) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))))) ; )) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))))) ; )) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))))))) ; )) ; ; (time ; (new-worse-than ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))))))) ; )) ; ) ; with times of 0.000, 0.120, 0.250, 0.430, etc. But with the current ; definition of pseudo-variantp, the sequence above is flat. ; However, the sequence with the terms commuted grows exponentially, ; still. ; (progn ; (time ; (new-worse-than ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))))) ; ; (time ; (new-worse-than ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))))) ; ; (time ; (new-worse-than ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))))) ; ; (time ; (new-worse-than ; '(f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; )) ; ; (time ; (new-worse-than ; '(f b ; (f b ; (f b ; (f b (f b (f b (f b (f b (f b (f b (f b (f b (f b b))))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; )) ; ; (time ; (new-worse-than ; '(f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b (f b (f b (f b (f b (f b (f b (f b b)))))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; )) ; ; (time ; (new-worse-than ; '(f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b (f b (f b (f b (f b (f b b))))))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; )) ; ; (time ; (new-worse-than ; '(f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b (f b (f b (f b b)))))))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; )) ; ; (time ; (new-worse-than ; '(f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b ; (f b b))))))))))))))))) ; '(f a (f a (f a (f a (f a (f a (f a (f a (f a a))))))))) ; )) ; ) ; Real times: 0.000, 0.000, 0.010, 0.000, 0.010, 0.020, 0.040, 0.100, ; 0.210, ... (mutual-recursion (defun worse-than-builtin (term1 term2) ; Term1 is worse-than-builtin term2 if it is basic-worse-than term2 or some ; proper subterm of it is worse-than-builtin or equal to term2. However, we ; know that if two terms are pseudo-variants of each other, then the ; worse-than-builtin relation does not hold. ; Warning: With #+hons, there could be performance problems if this is put into ; :logic mode without verifying guards. That is because worse-than-builtin is ; memoized by running acl2h-init, to make it efficient on structure-shared ; objects; but memoization depends on the raw Lisp function being executed, ; while :ideal mode functions are run without ever slipping into raw Lisp. (declare (xargs :guard (and (pseudo-termp term1) (pseudo-termp term2)) :measure (make-ord 1 (+ 1 (acl2-count term1) (acl2-count term2)) 1) :well-founded-relation o<)) (cond ((basic-worse-than term1 term2) t) ((pseudo-variantp term1 term2) nil) ((variablep term1) ; If term1 is a variable and not basic-worse-than term2, what do we know about ; term2? Term2 might be a variable. Term2 cannot be quote. Term2 might be a ; function application. So is X worse-than-builtin X or Y or (F X Y)? No. nil) ((fquotep term1) ; If term1 is a quote and not basic-worse-than term2, what do we know about ; term2? Term2 might be a variable. Also, term2 might be a quote, but if it ; is, term2 is bigger than term1. Term2 might be a function application. So ; is term1 worse-than-builtin a bigger quote? No. Is term1 worse-than-builtin ; a variable or function application? No. nil) (t (worse-than-lst (fargs term1) term2)))) (defun worse-than-or-equal-builtin (term1 term2) ; This function is not really mutually recursive and could be removed from this ; nest. It determines whether term1 is term2 or worse than term2. This nest ; defines worse-than-builtin and does not use this function despite the use of ; similarly named functions. ; Note: This function is supposed to be equivalent to ; (or (equal term1 term2) (worse-than-builtin term1 term2)). ; Clearly, that is equivalent to ; (if (pseudo-variantp term1 term2) ; (or (equal term1 term2) (worse-than-builtin term1 term2)) ; (or (equal term1 term2) (worse-than-builtin term1 term2))) ; But if pseudo-variantp is true, then worse-than-builtin must return nil. And ; if pseudo-variantp is nil, then the equal returns nil. So we can simplify ; the if above to: (declare (xargs :guard (and (pseudo-termp term1) (pseudo-termp term2)) :measure (make-ord 1 (+ 1 (acl2-count term1) (acl2-count term2)) 2) :well-founded-relation o<)) (if (pseudo-variantp term1 term2) (equal term1 term2) (worse-than-builtin term1 term2))) (defun basic-worse-than-lst1 (args1 args2) ; Is some element of args2 ``uglier'' than the corresponding element of args1. ; Technically, a2 is uglier than a1 if a1 is atomic (a variable or constant) ; and a2 is not or a2 is worse-than-builtin a1. (declare (xargs :guard (and (pseudo-term-listp args1) (pseudo-term-listp args2)) :measure (make-ord 1 (+ 1 (acl2-count args1) (acl2-count args2)) 0) :well-founded-relation o<)) (cond ((endp args1) nil) ((or (and (or (variablep (car args1)) (fquotep (car args1))) (not (or (variablep (car args2)) (fquotep (car args2))))) (worse-than-builtin (car args2) (car args1))) t) (t (basic-worse-than-lst1 (cdr args1) (cdr args2))))) (defun basic-worse-than-lst2 (args1 args2) ; Is some element of arg1 worse-than-builtin the corresponding element of args2? (declare (xargs :guard (and (pseudo-term-listp args1) (pseudo-term-listp args2)) :measure (make-ord 1 (+ 1 (acl2-count args1) (acl2-count args2)) 0) :well-founded-relation o<)) (cond ((endp args1) nil) ((worse-than-builtin (car args1) (car args2)) t) (t (basic-worse-than-lst2 (cdr args1) (cdr args2))))) (defun basic-worse-than (term1 term2) ; We say that term1 is basic-worse-than term2 if ; * term2 is a variable and term1 properly contains it, e.g., (F A B) ; is basic-worse-than A; ; * term2 is a quote and term1 is either not a quote or is a bigger ; quote, e.g., both X and '124 are basic-worse-than '17 and '(A B C D ; E) is worse than 'X; or ; * term1 and term2 are applications of the same function and no argument of ; term2 is uglier than the corresponding arg of term1, and some argument of ; term1 is worse-than-builtin the corresponding arg of term2. ; The last case is illustrated by the fact that (F A B) is basic-worse-than (F ; A '17), because B is worse than '17, but (F '17 B) is not basic-worse-than (F ; A '17) because A is worse than '17. Think of term2 as the old goal and term1 ; as the new goal. Do we want to cut off backchaining? Yes, if term1 is ; basic-worse-than term2. So would we backchain from (F A '17) to (F '17 B)? ; Yes, because even though one argument (the second) got worse (it went from 17 ; to B) another argument (the first) got better (it went from A to 17). (declare (xargs :guard (and (pseudo-termp term1) (pseudo-termp term2)) :measure (make-ord 1 (+ 1 (acl2-count term1) (acl2-count term2)) 0) :well-founded-relation o<)) (cond ((variablep term2) (cond ((eq term1 term2) nil) (t (occur term2 term1)))) ((fquotep term2) (cond ((variablep term1) t) ((fquotep term1) (> (fn-count-evg (cadr term1)) (fn-count-evg (cadr term2)))) (t t))) ((variablep term1) nil) ((fquotep term1) nil) ((cond ((flambda-applicationp term1) (equal (ffn-symb term1) (ffn-symb term2))) (t (eq (ffn-symb term1) (ffn-symb term2)))) (cond ((pseudo-variantp term1 term2) nil) ((basic-worse-than-lst1 (fargs term1) (fargs term2)) nil) (t (basic-worse-than-lst2 (fargs term1) (fargs term2))))) (t nil))) (defun some-subterm-worse-than-or-equal (term1 term2) ; Returns t if some subterm of term1 is worse-than-builtin or equal to term2. (declare (xargs :guard (and (pseudo-termp term1) (pseudo-termp term2)) :measure (make-ord 1 (+ 1 (acl2-count term1) (acl2-count term2)) 1) :well-founded-relation o<)) (cond ((variablep term1) (eq term1 term2)) ((if (pseudo-variantp term1 term2) ; see worse-than-or-equal-builtin (equal term1 term2) (basic-worse-than term1 term2)) t) ((fquotep term1) nil) (t (some-subterm-worse-than-or-equal-lst (fargs term1) term2)))) (defun some-subterm-worse-than-or-equal-lst (args term2) (declare (xargs :guard (and (pseudo-term-listp args) (pseudo-termp term2)) :measure (make-ord 1 (+ 1 (acl2-count args) (acl2-count term2)) 0) :well-founded-relation o<)) (cond ((endp args) nil) (t (or (some-subterm-worse-than-or-equal (car args) term2) (some-subterm-worse-than-or-equal-lst (cdr args) term2))))) (defun worse-than-lst (args term2) ; We determine whether some element of args contains a subterm that is ; worse-than-builtin or equal to term2. The subterm in question may be the ; element of args itself. That is, we use ``subterm'' in the ``not necessarily ; proper subterm'' sense. (declare (xargs :guard (and (pseudo-term-listp args) (pseudo-termp term2)) :measure (make-ord 1 (+ 1 (acl2-count args) (acl2-count term2)) 0) :well-founded-relation o<)) (cond ((endp args) nil) (t (or (some-subterm-worse-than-or-equal (car args) term2) (worse-than-lst (cdr args) term2))))) ) (encapsulate ((worse-than (term1 term2) t :guard (and (pseudo-termp term1) (pseudo-termp term2)))) (local (defun worse-than (term1 term2) (declare (ignore term1 term2) nil)))) (encapsulate ((worse-than-or-equal (term1 term2) t :guard (and (pseudo-termp term1) (pseudo-termp term2)))) (local (defun worse-than-or-equal (term1 term2) (declare (ignore term1 term2) nil)))) (defattach (worse-than worse-than-builtin) :skip-checks t) (defattach (worse-than-or-equal worse-than-or-equal-builtin) :skip-checks t) ; Begin treatment of the ancestors stack. (defrec ancestor ; Note: if lit is :binding-hyp, then lit-atm is hyp, fn-count is unify-subst ; and tokens is nil (see relevant comment in earlier-ancestor-biggerp). See ; make-ancestor-binding-hyp, ancestor-binding-hyp-p, ancestor-binding-hyp/hyp, ; and ancestor-binding-hyp/unify-subst. ; To obtain the literals from a list of ancestors, call ; strip-ancestor-literals. (Strip-cars will still work at this time, but we do ; not guarantee that in the future.) (lit atm var-cnt fn-cnt p-fn-cnt tokens) t) (defmacro make-ancestor-binding-hyp (hyp unify-subst) `(make ancestor :lit :binding-hyp :atm ,hyp :var-cnt ,unify-subst :tokens nil)) (defmacro ancestor-binding-hyp-p (anc) `(eq (access ancestor ,anc :lit) :binding-hyp)) (defmacro ancestor-binding-hyp/hyp (anc) `(access ancestor ,anc :atm)) (defmacro ancestor-binding-hyp/unify-subst (anc) `(access ancestor ,anc :var-cnt)) ; Here is how we add a frame to the ancestors stack. (defun push-ancestor (lit tokens ancestors) ; This function is used to push a new pair onto ancestors. Lit is a ; term to be assumed true. Tokens is a list of arbitrary objects. ; Generally, tokens is a singleton list containing the rune of a rule ; through which we are backchaining. But when we rewrite forced ; assumptions we use the ``runes'' from the assumnotes (see defrec ; assumnote) as the tokens. These ``runes'' are not always runes but ; may be symbols. ; Note: It is important that the literal, lit, be in the car of the ; frame constructed below. (let* ((alit lit) (alit-atm (mv-let (not-flg atm) (strip-not alit) (declare (ignore not-flg)) atm))) (mv-let (var-cnt-alit-atm fn-cnt-alit-atm p-fn-cnt-alit-atm) (var-fn-count alit-atm nil) (cons (make ancestor :lit alit ; the literal being assumed true (negation of hyp!) :atm alit-atm ; the atom of that literal :var-cnt var-cnt-alit-atm ; the var-count of that atom :fn-cnt fn-cnt-alit-atm ; the fn-count of that atom :p-fn-cnt p-fn-cnt-alit-atm ; the pseudo-fn-count of that atom :tokens tokens) ; the runes involved in this backchain ancestors)))) (defun ancestor-listp (x) (declare (xargs :guard t)) (cond ((atom x) (null x)) ((not (weak-ancestor-p (car x))) nil) ((ancestor-binding-hyp-p (car x)) (and ; See relevant comment in earlier-ancestor-bigger for why :tokens must be nil ; in this case. (null (access ancestor (car x) :tokens)) (ancestor-listp (cdr x)))) (t (let* ((anc (car x)) (alit (access ancestor anc :lit)) (alit-atm (access ancestor anc :atm)) (var-cnt-alit-atm (access ancestor anc :var-cnt)) (fn-cnt-alit-atm (access ancestor anc :fn-cnt)) (p-fn-cnt-alit-atm (access ancestor anc :p-fn-cnt)) (atokens (access ancestor anc :tokens))) (and (pseudo-termp alit) (pseudo-termp alit-atm) (integerp var-cnt-alit-atm) (integerp fn-cnt-alit-atm) (integerp p-fn-cnt-alit-atm) (true-listp atokens) (ancestor-listp (cdr x))))))) (defun earlier-ancestor-biggerp (var-cnt fn-cnt p-fn-cnt tokens ancestors) ; We return t if some ancestor on ancestors has a bigger fn-count than ; fn-cnt and intersects with tokens. (declare (xargs :guard (and (integerp var-cnt) (integerp fn-cnt) (integerp p-fn-cnt) (true-listp tokens) (ancestor-listp ancestors)))) (cond ((endp ancestors) nil) (t (let* ((anc (car ancestors)) (var-cnt-alit-atm (access ancestor anc :var-cnt)) (fn-cnt-alit-atm (access ancestor anc :fn-cnt)) (p-fn-cnt-alit-atm (access ancestor anc :p-fn-cnt)) (atokens (access ancestor anc :tokens))) (cond ((and (intersectp-equal tokens atokens) ; (Car ancestors) might specify an ancestor-binding-hyp-p. In this case some ; of the values compared below using < are nil. However, those comparisons do ; not take place because in this case atokens is also nil, so the ; intersectp-equal test above returns nil. (var-or-fn-count-< var-cnt var-cnt-alit-atm fn-cnt fn-cnt-alit-atm p-fn-cnt p-fn-cnt-alit-atm)) t) (t (earlier-ancestor-biggerp var-cnt fn-cnt p-fn-cnt tokens (cdr ancestors)))))))) (defun equal-mod-commuting (x y wrld) ; If this function returns t, then either x and y are the same term, or they ; are provably equal by virtue of the commutativity of their common binary ; function symbol. ; Recall that we do not track the use of equivalence relations; so we do not ; report their use here. When we do that, read the Note on Tracking ; Equivalence Runes in subst-type-alist1. (declare (xargs :guard (and (pseudo-termp x) (pseudo-termp y) (plist-worldp wrld)))) (cond ((variablep x) (eq x y)) ((variablep y) nil) ((or (fquotep x) (fquotep y)) nil) ; quotes are handled elsewhere ((equal x y) t) ((flambdap (ffn-symb x)) nil) (t (and (eq (ffn-symb x) (ffn-symb y)) (equivalence-relationp (ffn-symb x) wrld) (equal (fargn x 1) (fargn y 2)) (equal (fargn x 2) (fargn y 1)))))) (defun ancestors-check1 (lit-atm lit var-cnt fn-cnt p-fn-cnt ancestors tokens) ; Roughly speaking, ancestors is a list of all the things we can assume by ; virtue of our trying to prove their negations. That is, when we backchain ; from B to A by applying (implies A B), we try to prove A and so we put (NOT ; A) on ancestors and can legitimately assume it (i.e., (NOT A)) true. We ; return (mv t on-ancestors) if we determine heuristically that further ; backchaining is inadvisable, where on-ancestors is t if roughly speaking, lit ; is a member-equal of ancestors, and otherwise on-ancestors is nil. Otherwise ; we return (mv nil nil). ; We implement the complement check as follows. lit-atm is the atom of the ; literal lit. Consider a literal of ancestors, alit, and its atom, alit-atm. ; If lit-atm is alit-atm and lit is not equal to alit, then lit and alit are ; complementary. The following table supports this observation. It shows all ; the combinations by considering that lit is either a positive or negative p, ; and alit is a p of either sign or some other literal of either sign. The ; entries labeled = mark those when lit is alit. The entries labeled comp mark ; those when lit and alit are complementary. ; lit \ alit: p (not p) q (not q) ; p = comp x x ; (not p) comp = x x (declare (xargs :guard (and (pseudo-termp lit-atm) (pseudo-termp lit) (integerp var-cnt) (integerp fn-cnt) (integerp p-fn-cnt) (ancestor-listp ancestors) (true-listp tokens)))) (cond ((endp ancestors) (mv nil nil)) ((ancestor-binding-hyp-p (car ancestors)) (ancestors-check1 lit-atm lit var-cnt fn-cnt p-fn-cnt (cdr ancestors) tokens)) (t (let ((alit (access ancestor (car ancestors) :lit)) (alit-atm (access ancestor (car ancestors) :atm)) (var-cnt-alit-atm (access ancestor (car ancestors) :var-cnt)) (fn-cnt-alit-atm (access ancestor (car ancestors) :fn-cnt)) (p-fn-cnt-alit-atm (access ancestor (car ancestors) :p-fn-cnt)) (atokens (access ancestor (car ancestors) :tokens))) (cond ((equal-mod-commuting alit lit nil) ; Here, and in the next branch, we provide the empty world to ; equal-mod-commuting. If we want a stronger check then we could provide the ; current ACL2 world instead, but then we would have to add a world argument to ; ancestors-check1 and ancestors-check. As Robert Krug pointed out to us, ; there may be other places in our sources more deserving of generalization ; from 'equal to arbitrary equivalence relations. ; Better yet, we can mark equivalence relations in the ancestors stack, which ; costs a cons each time but may save many getprop calls. (mv t t)) ((equal-mod-commuting lit-atm alit-atm nil) (mv t nil)) ; See the comment above, for the preceding call of equal-mod-commuting. ; In Version_2.5, this function did not have the tokens argument. Instead we ; simply asked whether there was a frame on the ancestors stack such that ; fn-cnt was greater than or equal to the fn-count of the atom in the frame and ; lit-atm was worse-than-or-equal to the atom in the frame. If so, we aborted ; with (mv t nil). (The fn-cnt test is just an optimization because that ; inequality is implied by the worse-than-or-equal test.) But Carlos Pacheco's ; TLA work exposed a situation in which the lit-atm was worse-than-or-equal to ; a completely unrelated atom on the ancestors stack. So we added tokens and ; insisted that the ancestor so dominated by lit-atm was related to lit-atm by ; having a non-empty intersection with tokens. This was added in the final ; polishing of Version_2.6. But we learned that it slowed us down about 10% ; because it allowed so much more backchaining. We finally adopted a very ; conservative change targeted almost exactly to allow Carlos' example while ; preserving the rest of the old behavior. ((intersectp-equal tokens atokens) ; We get here if the current lit-atm is related to that in the current frame. ; We next ask whether the function symbols are the same and lit-atm is bigger. ; If so, we abort. Otherwise, we look for others. (cond ((and (nvariablep alit-atm) (not (fquotep alit-atm)) (nvariablep lit-atm) (not (fquotep lit-atm)) (equal (ffn-symb lit-atm) (ffn-symb alit-atm)) (not (var-or-fn-count-< var-cnt var-cnt-alit-atm fn-cnt fn-cnt-alit-atm p-fn-cnt p-fn-cnt-alit-atm))) (mv t nil)) (t (ancestors-check1 lit-atm lit var-cnt fn-cnt p-fn-cnt (cdr ancestors) tokens)))) ((and (not (var-or-fn-count-< var-cnt var-cnt-alit-atm fn-cnt fn-cnt-alit-atm p-fn-cnt p-fn-cnt-alit-atm)) (worse-than-or-equal lit-atm alit-atm)) ; The clause above is the old Version_2.5 test, but now it is tried only if the ; atms are unrelated by their tokens. Most of the time we want to abort ; backchaining if we pass the check above. But we want to allow continued ; backchaining in Carlos' example. In that example: ; lit-atm = (S::MEM (S::APPLY S::DISKSWRITTEN S::P) ; (S::POWERSET (S::DISK))) ; fn-cnt = 4 ; alit-atm = (S::MEM S::D (S::DISK)) ; fn-cnt-alit-atm = 2 ; with no token intersection. Once upon a time we simply allowed all these, ; i.e., just coded a recursive call here. But that really slowed us down by ; enabling a lot of backchaining. So now we basically want to ask: "Is there a ; really good reason to allow this backchain?" We've decided to allow the ; backchain if there is an earlier ancestor, related by tokens, to the ancestor ; that is trying to veto this one, that is bigger than this one. In Carlos' ; example there is such a larger ancestor; but we suspect most of the time ; there isn't. For example, at the very least it means that the vetoing ; ancestor must be the SECOND (or subsequent) time we've applied some rule on ; this backchaining path! The first time we coded this heuristic we named the ; test ``random-coin-flip'' instead of earlier-ancestor-biggerp; the point: ; this is a pretty arbitrary decision heuristic mainly to make Carlos' example ; work. (cond ((earlier-ancestor-biggerp var-cnt fn-cnt p-fn-cnt atokens (cdr ancestors)) (ancestors-check1 lit-atm lit var-cnt fn-cnt p-fn-cnt (cdr ancestors) tokens)) (t (mv t nil)))) (t (ancestors-check1 lit-atm lit var-cnt fn-cnt p-fn-cnt (cdr ancestors) tokens))))))) ; Note: In the type-set clique, and nowhere else, ancestors might be ; t. The so-called t-ancestors hack is explained below. But this ; function and push-ancestor above DO NOT implement the t-ancestors ; hack. That is because they are used by the rewrite clique where ; there is no such hack, and we didn't want to slow that clique down. (defun ancestors-check-builtin (lit ancestors tokens) ; We return two values. The first is whether we should abort trying to ; establish lit on behalf of the given tokens. The second is whether lit is ; (assumed) true in ancestors. ; We abort iff either lit is assumed true or else it is worse than or equal to ; some other literal we're trying to establish on behalf of some token in ; tokens. (Actually, we compare the atoms of the two literals in the ; worse-than check.) ; A reminder about ancestors: Ancestors is a list of ``frames.'' Each frame is ; of one of two forms. Most frames are constructed by push-ancestor and have ; the form: ; (lit alit cnt1 cnt2 tokens) ; where lit is a term that may be assumed true in the current context, alit is ; the atom of lit (i.e., with a NOT stripped if necessary), cnt1 and cnt2 are ; the fn- and pseudo-fn counts of alit as computed by fn-count-1, and tokens is ; a true-list of arbitrary objects but most often the runes (or base symbols of ; runes) responsible for back chaining to this frame). ; However, sometimes we push a frame of the form: ; (:BINDING-HYP hyp unify-subst) ; where hyp is of the form (equiv var term) and unify-subst is a substitution ; in which var is free and there are no relatively free vars in term. ; For purposes of guard checking all we assume about ancestors is ; ancestor-listp. ; Historical Note: In nqthm, this function was named relieve-hyps-not-ok. (declare (xargs :guard (and (pseudo-termp lit) (ancestor-listp ancestors) (true-listp tokens)))) (cond ((endp ancestors) (mv nil nil)) (t (mv-let (not-flg lit-atm) (strip-not lit) (declare (ignore not-flg)) (mv-let (var-cnt fn-cnt p-fn-cnt) (var-fn-count lit-atm nil) (ancestors-check1 lit-atm lit var-cnt fn-cnt p-fn-cnt ancestors tokens)))))) (defproxy ancestors-check (* * *) => (mv * *)) (defattach (ancestors-check ancestors-check-builtin) :skip-checks t) ; Essay on Type-set Deductions for Integerp ; Robert Krug found it disturbing that ACL2 could not make certain type-set ; deductions about whether terms are definitely integers or are definitely not ; integers. Version_3.2.1 and beyond contains code descended from a ; contribution from Robert (included below through type-set-finish) that ; rectifies this problem, which is illustrated by examples contributed by ; Robert, in community book books/misc/integer-type-set-test.lisp. ; Here is an outline of the issue. Suppose we have: ; Term to type: ; y = a+bx [a and b numeric constants] ; Now suppose we find the following in the type-alist: ; Term typed as definitely integer or definitely not integer: ; y' = a'+b'x [a' constant, b' a non-zero numeric constant] ; Then note: ; y = (a - (ba'/b')) + (b/b')y'. ; Let C = (a - Da') where D = b/b'; so ; y = C + Dy' [where C and D are known to be numeric]. ; We can prove the following in order to justify the claim just above: ; (include-book "arithmetic/top" :dir :system) ; (thm (implies (and (equal y (+ a (* b x))) ; (equal y2 (+ a2 (* b2 x))) ; (acl2-numberp b2) (not (equal b2 0))) ; (let* ((d (/ b b2)) ; (c (- a (* d a2)))) ; (equal y (+ c (* d y2)))))) ; Then we can, of course, potentially strengthen the type known for y by using ; the above alternate form of y, combining in a suitable manner the known types ; of y', C, and D. ; The description above is slightly simplistic, however. Suppose for example ; that y above is 2 + 4u + 6v, while y' is 7 + 10u + 15v. Then in fact we can ; view y and y' as follows. ; y = 2 + 4(u + 3/2 v) ; y' = 7 + 10(u + 3/2 v) ; The representation of a term as having the form a+bx is performed by function ; normalize-linear-sum. ; Robert informs us that he tried an analogous enhancement in which one tries ; to decide rationalp propositions in addition to integerp propositions, but he ; found that to be exceedingly expensive. ; We now develop the code for normalize-linear-sum and then the code for ; type-set-finish-1. The sorting done by normalize-linear-sum-2 was added ; after Version_4.3; an example in normalize-linear-sum illustrates what that ; adds. ; End of Essay on Type-set Deductions for Integerp. (defun map-multiply-car (multiplicative-constant x) (cond ((endp x) nil) (t (cons (cons (* multiplicative-constant (car (car x))) (cdr (car x))) (map-multiply-car multiplicative-constant (cdr x)))))) (defun normalize-addend (addend) ; Addend is a term. We return a pair (multiplicative-constant ; . rest-of-addend), where multiplicative-constant is a rational (not a term) ; and rest-of-addend is a term, such that addend = multiplicative-constant * ; rest-of-addend. The intent is for rest-of-addend to have a coefficient of 1, ; though we do not rely on this for correctness. (cond ((variablep addend) (cons 1 addend)) ((fquotep addend) (cons 1 addend)) ((flambda-applicationp addend) (cons 1 addend)) ((eq (ffn-symb addend) 'UNARY--) (cons -1 (fargn addend 1))) ((eq (ffn-symb addend) 'BINARY-*) (cond ((and (quotep (fargn addend 1)) ; Addend is of the form (* a x). (rationalp (unquote (fargn addend 1)))) (cons (unquote (fargn addend 1)) (fargn addend 2))) (t (cons 1 addend)))) (t (cons 1 addend)))) (defun insert-cdr-term-order (item list) (cond ((endp list) (list item)) ((term-order (cdr (car list)) (cdr item)) (cons (car list) (insert-cdr-term-order item (cdr list)))) (t (cons item list)))) (defun normalize-linear-sum-2 (term) ; Term is (at least initially) a sum. We return a list of pairs ; (multiplicative-constant . rest-of-addend), one pair for each addend of term. ; Furthermore, these pairs are sorted by term-order on rest-of-addend. (cond ((eq (fn-symb term) 'BINARY-+) (insert-cdr-term-order (normalize-addend (fargn term 1)) (normalize-linear-sum-2 (fargn term 2)))) (t (list (normalize-addend term))))) (defun normalize-linear-sum-1 (additive-constant term) ; Given a rational number, additive-constant, and a term, we return (mv a b x) ; where: a and b are rational numbers, x is a term (but see below), and ; additive-constant + term = a + b * x. ; ; The preceding is almost correct. When x would be a sum, it is instead ; represented as a list of pairs --- (multiplicative-constant ; . rest-of-addend), one pair for each addend. Note that there is no ; possibility for confusion, i.e., if two calls produce the same x, then either ; each x represents a term or each x represents a list of pairs of the form ; above. To see this, note that if x is a term of the form ((u . v) . w) then ; u is the symbol LAMBDA, not a number: ; (thm (implies (pseudo-termp (cons (cons u v) w)) (equal u 'lambda))). (cond ((variablep term) (mv additive-constant 1 term)) ((fquotep term) ; degenerate case; any sound result might be fine (cond ((rationalp (unquote term)) (mv (+ additive-constant (unquote term)) 1 *0*)) (t (mv additive-constant 1 term)))) ((flambda-applicationp term) (mv additive-constant 1 term)) ((eq (ffn-symb term) 'UNARY--) (mv additive-constant -1 (fargn term 1))) ((eq (ffn-symb term) 'BINARY-*) (cond ((and (quotep (fargn term 1)) (rationalp (unquote (fargn term 1)))) (mv additive-constant (unquote (fargn term 1)) (fargn term 2))) (t (mv additive-constant 1 term)))) ((eq (ffn-symb term) 'BINARY-+) (let* ((temp-1 (normalize-linear-sum-2 term)) ; Temp-1 is a list of pairs --- (multiplicative-constant . rest-of-addend). ; These pairs are sorted by term order on rest-of-addend. (multiplicative-constant (car (car temp-1)))) (cond ((or (eql multiplicative-constant 0) ; degenerate case (eql multiplicative-constant 1)) (mv additive-constant 1 temp-1)) (t (let ((temp-2 (map-multiply-car (/ multiplicative-constant) temp-1))) (mv additive-constant multiplicative-constant temp-2)))))) (t (mv additive-constant 1 term)))) (defun normalize-linear-sum (term) ; For context, see the Essay on Type-set Deductions for Integerp. Here, we ; return three values: additive-constant, multiplicative-constant, and ; normalized-term such that ; term = 'additive-constant + 'multiplicative-constant * normalized-term, ; where additive-constant and multiplicative-constant are rational numbers ; (not quoted) and normalized-term has a leading coefficient of 1 or 0. We ; intend it to be 1, but there is not much we can do with (+ 3 (* 0 x)). ; ; Pseudo examples: ; (foo x) ==> (mv 0 1 (foo x)) ; (- (foo x)) ==> (mv 0 -1 (foo x)) ; (+ x y) ==> (mv 0 1 (+ x y)) ; (+ (* 3 x) y) ==> (mv 0 3 (+ x (* 1/3 y))) ; (+ 2 (* 3 x) y) ==> (mv 2 3 (+ x (* 1/3 y))) ; ; Note that for best results, sums and products must be right-associated with ; the addends/factors in term order (or at least with constants all the way to ; the left), and terms such as (- (* 3 ...)) do not appear. (* -3 ...) is the ; preferred form. Robert Krug underwent a process to accommodate terms that do ; not satisfy these assumptions, but he tells us that the code rapidly became ; unwieldy and he found no natural stopping point for that process. ; The following example shows why we sort using normalize-linear-sum-2. ; Without sorting, the conclusion doesn't reduce to (foo t). ; (defstub foo (x) t) ; (thm ; should reduce conclusion to (foo t) ; (implies (and (rationalp x) ; (rationalp y) ; (integerp (+ x (* 1/3 y)))) ; (foo (integerp (+ y (* 3 x)))))) ; The following similar example shows why we need to consider terms that aren't ; sums. As above, that is needed in order for the conclusion to simplify to ; (foo t). ; (thm ; should reduce conclusion to (foo t) ; (implies (and (rationalp x) ; (rationalp y) ; (integerp (* 1/3 y))) ; (foo (integerp y)))) (cond ((variablep term) (mv 0 1 term)) ((fquotep term) ; not needed due to the first invariant on type-alists (mv 0 1 term)) ((flambda-applicationp term) (mv 0 1 term)) ((eq (ffn-symb term) 'UNARY--) (mv 0 -1 (fargn term 1))) ((eq (ffn-symb term) 'BINARY-*) (cond ((and (quotep (fargn term 1)) (rationalp (unquote (fargn term 1)))) (mv 0 (unquote (fargn term 1)) (fargn term 2))) (t (mv 0 1 term)))) ((eq (ffn-symb term) 'BINARY-+) (cond ((and (quotep (fargn term 1)) (rationalp (unquote (fargn term 1)))) (normalize-linear-sum-1 (unquote (fargn term 1)) (fargn term 2))) (t (normalize-linear-sum-1 0 term)))) (t (mv 0 1 term)))) (defun normalize-linear-sum-p1 (stripped-term term-to-match) (cond ((null stripped-term) nil) ((and (nvariablep term-to-match) ; (not (fquotep term-to-match)) (eq (ffn-symb term-to-match) 'BINARY-+)) (normalize-linear-sum-p1 (cdr stripped-term) (fargn term-to-match 2))) (t (null (cdr stripped-term))))) (defun normalize-linear-sum-p (stripped-term term-to-match) ; This function is a heuristic filter. It is desirable to return nil if and ; only if there is clearly no hope that there is a match, using the algorithm ; in type-set-finish-1, between the results of normalizing a given term into (& ; & stripped-term) and the result of normalizing a second term, term-to-match. ; Note that stripped-term is either a term or is an alist associating numbers ; with terms. (let ((term ; strip additive constant (cond ((and (nvariablep term-to-match) ; (not (fquotep term-to-match)) (eq (ffn-symb term-to-match) 'BINARY-+) (quotep (fargn term-to-match 1))) (fargn term-to-match 2)) (t term-to-match)))) (cond ((and (consp stripped-term) (consp (car stripped-term)) (acl2-numberp (caar stripped-term))) ; Stripped-term is an alist with entries (number . term). (normalize-linear-sum-p1 stripped-term term)) (t ; Stripped-term is a term. (not (and (nvariablep term) ; (not (fquotep term)) (eq (ffn-symb term) 'BINARY-+))))))) (defun type-set-finish-1 (additive-const multiplicative-const stripped-term ts ttree type-alist) ; For context, see the Essay on Type-set Deductions for Integerp. Here, we are ; taking the type-set of a term ; x = additive-const + multiplicative-const * stripped-term, ; that is known to be rational, where additive-const and multiplicative-const ; are rational numbers. We try to tighten the type set, ts, relative to the ; given type-alist, so that x is known to be an integer or known to be a ratio ; (neither of which is known coming into this function). ; ; We scan through the type-alist looking for a typed-term that is known to be ; an integer or known to be a ratio and equal to typed-additive-const + ; typed-multiplicative-const * stripped-term for some typed-additive-const and ; typed-multiplicative-const. The story continues in the comments below. (cond ((null type-alist) (mv ts ttree)) ((and (or (ts-subsetp (cadr (car type-alist)) *ts-integer*) (ts-subsetp (cadr (car type-alist)) *ts-ratio*)) (normalize-linear-sum-p stripped-term (car (car type-alist)))) (let ((term-to-match (car (car type-alist))) (type-to-match (cadr (car type-alist))) (ttree-to-match (cddr (car type-alist)))) (mv-let (typed-additive-const typed-multiplicative-const stripped-term-to-match) (normalize-linear-sum term-to-match) (cond ((and (equal stripped-term stripped-term-to-match) (not (eql typed-multiplicative-const 0))) ; We have found a typed-term of the desired form, described above. We merge ; the constant appropriately --- see the thm and let-binding immediately below. ; (include-book "arithmetic/top" :dir :system) ; (thm ; (implies (and (rationalp additive-const) ; (rationalp multiplicative-const) ; (rationalp typed-additive-const) ; (rationalp typed-multiplicative-const) ; (not (equal typed-multiplicative-const 0)) ; (equal orig-term (+ additive-const ; (* multiplicative-const term))) ; (equal typed-term (+ typed-additive-const ; (* typed-multiplicative-const term)))) ; (equal orig-term ; (+ (- additive-const ; (* multiplicative-const ; typed-additive-const ; (/ typed-multiplicative-const))) ; (* multiplicative-const ; (/ typed-multiplicative-const) ; typed-term))))) (let* ((merged-multiplicative-const (* multiplicative-const (/ typed-multiplicative-const))) (merged-additive-const (- additive-const (* merged-multiplicative-const typed-additive-const)))) (cond ((and (not (eql merged-additive-const 0)) (not (eql merged-multiplicative-const 1))) (let* ((merged-multiplicative-const-ts (type-set-quote merged-multiplicative-const)) (merged-additive-const-ts (type-set-quote merged-additive-const)) ; We have have the following type information: ; ts: ; type of orig-term (the term we are typing), which intersects both ; *ts-ratio* and *ts-integer* and is contained in *ts-rational* ; type-to-match: ; type of the matching term found in the type-alist (the typed-term above) ; merged-multiplicative-const-ts: ; the type of the merged-multiplicative-const (which is rational) ; merged-additive-const-ts: ; the type of the merged-additive-const (which is rational) ; ; Furthermore, by construction and the thm above, we know that ; (a) orig-term = ; merged-additive-const + merged-multiplicative-const * typed-term ; ; We wish to strengthen the type of orig-term, so we compute its type using (a) ; above. This is done by the calls to aref2 below, which are based on those in ; type-set-binary-* and type-set-binary-+. We then intersect the newly ; computed type with the original one, ts. If this final type is strong ; enough, we return. Otherwise we continue our search. ; (new-ts1 (aref2 'type-set-binary-*-table *type-set-binary-*-table* merged-multiplicative-const-ts type-to-match)) (new-ts2 (aref2 'type-set-binary-+-table *type-set-binary-+-table* merged-additive-const-ts new-ts1)) (new-ts3 (ts-intersection ts new-ts2))) (if (or (ts-subsetp new-ts3 *ts-integer*) (ts-subsetp new-ts3 *ts-ratio*)) (mv new-ts3 (puffert (cons-tag-trees ttree ttree-to-match))) (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree (cdr type-alist))))) ((not (eql merged-additive-const 0)) ; orig-term = merged-additive-const + typed-term. ; ; This is just like the above, but since merged-multiplicative-const ; is 1, we skip typing merged-multiplicative-const * stripped-term-to-match. (let* ((merged-additive-const-ts (type-set-quote merged-additive-const)) (new-ts1 (aref2 'type-set-binary-+-table *type-set-binary-+-table* merged-additive-const-ts type-to-match)) (new-ts2 (ts-intersection ts new-ts1))) (if (or (ts-subsetp new-ts2 *ts-integer*) (ts-subsetp new-ts2 *ts-ratio*)) (mv new-ts2 (puffert (cons-tag-trees ttree ttree-to-match))) (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree (cdr type-alist))))) ((not (eql merged-multiplicative-const 1)) ; orig-term = merged-multiplicative-const * typed-term. ; ; Similar to the above, but here we take advantage of the fact that ; merged-additive-const is known to be 0. (let* ((merged-multiplicative-const-ts (type-set-quote merged-multiplicative-const)) (new-ts1 (aref2 'type-set-binary-*-table *type-set-binary-*-table* merged-multiplicative-const-ts type-to-match)) (new-ts2 (ts-intersection ts new-ts1))) (if (or (ts-subsetp new-ts2 *ts-integer*) (ts-subsetp new-ts2 *ts-ratio*)) (mv new-ts2 (puffert (cons-tag-trees ttree ttree-to-match))) (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree (cdr type-alist))))) (t ; orig-term = typed-term ; ; Presumably ts is at least as strong as type-to-match, but at any rate, this ; isn't a case we care to consider, so we simply recur. (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree (cdr type-alist)))))) (t (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree (cdr type-alist))))))) (t (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree (cdr type-alist))))) (defun type-set-finish (x ts0 ttree0 ts1 ttree1 type-alist) ; We have obtained two type-set answers for the term x. Ts0 and ttree0 were ; obtained by looking the term up in the type-alist; if ts0 is nil, then no ; binding was found in the type-alist. Ts1 and ttree1 were obtained by ; computing the type-set of the term "directly." Both are valid, provided ts0 ; is non-nil. We intersect them. ; Note: Our answer must include ttree1 because that is the accumulated ; dependencies of the type-set computation to date! (mv-let (ts ttree) (cond ((null ts0) (mv ts1 ttree1)) ((ts-subsetp ts1 ts0) ; This is an optimization. We are about to intersect the type-sets and union ; the tag-trees. But if ts1 is a subset of ts0, the intersection is just ts1. ; We need not return ttree0 in this case; note that we must always return ttree1. (mv ts1 ttree1)) (t (mv (ts-intersection ts0 ts1) (cons-tag-trees ttree0 ttree1)))) ; See the Essay on Type-set Deductions for Integerp for why we make the ; following, final attempt. (cond ((and (ts-subsetp ts *ts-rational*) (ts-intersectp ts *ts-integer*) (ts-intersectp ts *ts-ratio*)) (mv-let (additive-const multiplicative-const stripped-term) (normalize-linear-sum x) (type-set-finish-1 additive-const multiplicative-const stripped-term ts ttree type-alist))) (t (mv ts ttree))))) (defun search-type-alist-rec (term alt-term typ type-alist unify-subst ttree) (cond ((null type-alist) (mv nil unify-subst ttree nil)) ((ts-subsetp (cadr (car type-alist)) typ) (mv-let (ans unify-subst) (one-way-unify1 term (car (car type-alist)) unify-subst) (cond (ans (mv t unify-subst (cons-tag-trees (cddr (car type-alist)) ttree) (cdr type-alist))) (alt-term (mv-let (ans unify-subst) (one-way-unify1 alt-term (car (car type-alist)) unify-subst) (cond (ans (mv t unify-subst (cons-tag-trees (cddr (car type-alist)) ttree) (cdr type-alist))) (t (search-type-alist-rec term alt-term typ (cdr type-alist) unify-subst ttree))))) (t (search-type-alist-rec term alt-term typ (cdr type-alist) unify-subst ttree))))) (t (search-type-alist-rec term alt-term typ (cdr type-alist) unify-subst ttree)))) (mutual-recursion (defun free-varsp (term alist) (cond ((variablep term) (not (assoc-eq term alist))) ((fquotep term) nil) (t (free-varsp-lst (fargs term) alist)))) (defun free-varsp-lst (args alist) (cond ((null args) nil) (t (or (free-varsp (car args) alist) (free-varsp-lst (cdr args) alist))))) ) (defun search-type-alist-with-rest (term typ type-alist unify-subst ttree wrld) ; See search-type-alist. (mv-let (term alt-term) (cond ((or (variablep term) (fquotep term) (not (equivalence-relationp (ffn-symb term) wrld))) (mv term nil)) ; Otherwise, term is of the form (equiv term1 term2). If term1 precedes term2 ; in term-order, then term would be stored on a type-alist as (equiv term2 ; term1); see the Essay on the Invariants on Type-alists, and Canonicality ; (specifically, the third invariant described there). In such a case we may ; wish to search for the commuted version instead of term. However, if there ; are free variables in term with respect to unify-subst then we need to search ; both for term and its commuted version, because the more specific term on ; type-alist can have its arguments in either term-order (unless we engage in a ; relatively expensive check; see e.g. maximal-terms). ((free-varsp term unify-subst) (mv term (fcons-term* (ffn-symb term) (fargn term 2) (fargn term 1)))) (t (let ((arg1 (fargn term 1)) (arg2 (fargn term 2))) (cond ((term-order arg1 arg2) (mv (fcons-term* (ffn-symb term) (fargn term 2) (fargn term 1)) nil)) (t (mv term nil)))))) (search-type-alist-rec term alt-term typ type-alist unify-subst ttree))) (defun search-type-alist (term typ type-alist unify-subst ttree wrld) ; We search type-alist for an instance of term bound to a type-set ; that is a subset of typ. Keep this in sync with search-type-alist+. ; For example, if typ is *ts-rational* then we seek an instance of ; term that is known to be a subset of the rationals. Most commonly, ; typ is *ts-non-nil*. In that case, we seek an instance of term ; that is non-nil. Thus, this function can be thought of as trying to ; "make term true." To use this function to "make term false," use ; the ts-complement of the desired type. I.e., if you wish to find a ; false instance of term use *ts-nil*. ; By "instance" here we always mean an instance under an extension of ; unify-subst. The extension is returned when we are successful. ; We return three values. The first indicates whether we succeeded. ; The second is the final unify-subst. The third is a modified ttree ; recording the literals used. If we did not succeed, the second ; and third values are our input unify-subst and ttree. I.e., we are ; a No-Change Loser. ; The No-Change Policy: Many multi-valued functions here return a ; flag that indicates whether they "won" or "lost" and, in the case ; that they won, return "new values" for certain of their arguments. ; Here for example, we return a new value for unify-subst. In early ; coding we adopted the policy that when they "lost" the additional ; values were irrelevant and were often nil. This policy prevented ; the use of such forms as: ; (mv-let (wonp unify-subst) ; (search-type-alist ... unify-subst ...) ; (cond (wonp ...) ; (t otherwise...))) ; because at otherwise... unify-subst was no longer what it had been before ; the search-type-alist. Instead we had to think of a new name for ; it in the mv-let and use the appropriate one below. ; We then adopted what we now call the "No-Change Policy". If a ; function returns a won/lost flag and some altered arguments, the ; No-Change Policy is that it returns its input arguments in case it ; loses. We will note explicitly when a function is a No-Change ; Loser. (mv-let (ans unify-subst ttree rest-type-alist) (search-type-alist-with-rest term typ type-alist unify-subst ttree wrld) (declare (ignore rest-type-alist)) (mv ans unify-subst ttree))) (defun term-and-typ-to-lookup (hyp wrld) (mv-let (not-flg term) (strip-not hyp) (let* ((recog-tuple (and (nvariablep term) (not (fquotep term)) (not (flambda-applicationp term)) (assoc-eq (ffn-symb term) (global-val 'recognizer-alist wrld)))) (typ (if (and recog-tuple (access recognizer-tuple recog-tuple :strongp)) (if not-flg (access recognizer-tuple recog-tuple :false-ts) (access recognizer-tuple recog-tuple :true-ts)) (if not-flg *ts-nil* *ts-non-nil*))) (term (if (and recog-tuple (access recognizer-tuple recog-tuple :strongp)) (fargn term 1) term))) (mv term typ)))) (defun lookup-hyp (hyp type-alist wrld unify-subst ttree) ; See if hyp is true by type-alist or simp-clause considerations -- ; possibly extending the unify-subst. If successful we return t, a ; new unify-subst and a new ttree. No-Change Loser. (mv-let (term typ) (term-and-typ-to-lookup hyp wrld) (search-type-alist term typ type-alist unify-subst ttree wrld))) (defun bind-free-vars-to-unbound-free-vars (vars alist) ; For every var in vars that is not bound in alist, create the binding ; (var . UNBOUND-FREE-var) and add that binding to alist. Return the ; extended alist. We use this function to instantiate free vars in ; FORCEd and CASE-SPLIT hypotheses. In that application, vars will be ; the list of all vars occuring in the hyp and alist will be the ; unify-subst. The name ``unbound free var'' is a little odd out of ; context but we hope it will make the user realize that the variable ; occurred freely and we couldn't find a binding for it to make the hyp ; true before forcing or splitting. (cond ((endp vars) alist) ((assoc-eq (car vars) alist) (bind-free-vars-to-unbound-free-vars (cdr vars) alist)) (t (bind-free-vars-to-unbound-free-vars (cdr vars) (cons (cons (car vars) (packn (list "UNBOUND-FREE-" (car vars)))) alist))))) ; The Accumulated Persistence Essay ; We now develop the code needed to track accumulated-persistence. The ; documentation topic for accumulated-persistence serves as a useful ; introduction to what is implemented by the code, and is a good starting point ; before reading this Essay. ; A typical use of this utility is as follows. ; >(accumulated-persistence t) ; activate and initialize ; > ... ; do proofs ; >(show-accumulated-persistence :frames) ; to display stats ordered by ; ; frame count ; >(accumulated-persistence nil) ; deactivate ; A macro form is available for use in system code to support the tracking of ; accumulated persistence. The special-form ; (with-accumulated-persistence rune (v1 ... vk) body) ; should be used in our source code whenever body is code that attempts to ; apply rune. The list of variables, (v1 ... vk), tell us the multiplicity ; of body. This form is logically equivalent to ; (mv-let (v1 ... vk) body (mv v1 ... vk)) ; which is to say, it is logically equivalent to body itself. (In the case ; where k is 1, we naturally use a let instead of an mv-let.) However, we ; insert some additional code to accumulate the persistence of rune. ; There are optional arguments of with-accumulated-persistence not described in ; this essay, but whose meaning is pretty clear from the code. ; The implementation of accumulated-persistence is as follows. First, the ; necessary global data structures are maintained as the status of a wormhole ; called 'accumulated-persistence. As usual with a wormhole status, it is of ; the form (:key . data), where :key is either :SKIP or :ENTER indicating ; whether wormhole is supposed to actually enter the wormhole. The data itself ; is an accp-info record. The record stores success (useful work) and failure ; (useless work) counts, success and failure stacks, and totals, as follows. ; Conceptually, there is always a current stack for lemmas other than the one ; currently being applied (after push-accp but before pop-accp). The stack is ; initially empty, but every attempt to apply a lemma (with push-accp) pushes ; current information on the stack and starts accumulating information for that ; lemma, generally while relieving hypotheses. More precisely, all our data is ; kept in a single accp-info record, which is the value of the wormhole-data ; field of wormhole-status: ; If accumulated persistence is enabled then we give special treatment to ; hypotheses and conclusions of rules. Each is represented as an ``extended ; rune'' (``xrune''), which provides an accumulation site for work done on ; behalf of that hypothesis or conclusion. Thus, we actually traffic in ; xrunes, where an xrune is either a rune, a hyp-xrune (:hyp rune , n) where n ; is a positive integer, or a conc-xrune (:conc . rune). The hyp-xrune (:hyp ; rune . n) is used in accumulated-persistence for recording the work done on ; behalf of the nth hypothesis of rune, while the conc-xrune (:conc . rune) ; records the work done on behalf of the right-hand-side of the rune. Note ; that :doc accumulated-persistence says that a hyp-xrune is (:hyp n . rune) ; rather than (:hyp rune . n), but we found the latter form much more ; convenient for sorting. Still, we present (:hyp n . rune) to the user by ; calling prettyify-xrune. (defabbrev x-xrunep (xrune) ; extended xrunep (or (eq (car xrune) :hyp) (eq (car xrune) :conc))) (defabbrev hyp-xrune (n rune) (assert$ (not (x-xrunep rune)) (list* :hyp rune n))) (defabbrev hyp-xrune-rune (xrune) (assert$ (and (x-xrunep xrune) (eq (car xrune) :hyp)) (cadr xrune))) (defabbrev conc-xrune (rune) (assert$ (not (x-xrunep rune)) (list* :conc rune))) (defabbrev conc-xrune-rune (xrune) (assert$ (and (x-xrunep xrune) (eq (car xrune) :conc)) (cdr xrune))) (defabbrev xrune-rune (xrune) (cond ((x-xrunep xrune) (if (eq (car xrune) :hyp) (cadr xrune) (cdr xrune))) (t xrune))) (defabbrev rune= (rune1 rune2) (and (eq (car rune1) (car rune2)) (equal (cdr rune1) (cdr rune2)))) (defabbrev xrune= (xrune1 xrune2) (cond ((eq (car xrune1) :hyp) (and (eq (car xrune2) :hyp) (rune= (cadr xrune1) (cadr xrune2)) (eql (cddr xrune1) (cddr xrune2)))) ((eq (car xrune1) :conc) (and (eq (car xrune2) :conc) (rune= (cdr xrune1) (cdr xrune2)))) (t (rune= xrune1 xrune2)))) (defun prettyify-xrune (xrune) ; We sort :hyp xrunes first by their rune (see sort-xrune-alist-by-rune), which ; is why a hyp xrune is of the form (:hyp rune . n) rather than (:hyp n ; . rune). But the latter is more pleasant to view. (cond ((eq (car xrune) :hyp) (list* :hyp (cddr xrune) (cadr xrune))) (t xrune))) (defrec accp-info ; The "s" and "f" suffixes below suggest "success" and "failure", though a ; better distinction is between "useful work" and "useless work". Our ; user-level reports say "useful" and "useless". See just below for a ; description of fields. (((cnt-s . cnt-f) . (stack-s . stack-f)) xrune-stack xrunep . totals) t) ; Cnt-s is the total number of lemmas tried that have led to a successful ; application. ; Cnt-f is similar but for lemmas whose application failed (typically because a ; hypothesis was not relieved). ; Stack-s (resp. stack-f) is a stack of old values of cnt-s (resp. cnt-f). ; Xrune-stack is a list of xrunes corresponding to stack-s and stack-f, where ; each xrune corresponds to a call of push-accp made on behalf of that xrune. ; Xrunep is true if we are to collect information for hypothesis and conclusion ; xrunes, and is nil otherwise. ; Totals is a stack of the accumulated totals, initially '(nil); it is one ; longer than the common length of stack-s and stack-f. Each element of the ; stack is a list associating xrunes with totals accumulated for a ; corresponding xrune (i.e., an ancestral call of push-accp), as follows. (defrec accp-entry ; Warning. We construct lists of such entries that we refer to as alists. ; Before changing the shape of this record, consider whether we are relying on ; it being a record whose car is its :xrune field. See for example ; sort-xrune-alist-by-rune1. ; The "s" and "f" suffixes below suggest "success" and "failure", though a ; better distinction is between "useful work" and "useless work". Our ; user-level reports say "useful" and "useless". See just below for a further ; description of the fields. (xrune . ((n-s . ap-s) . (n-f . ap-f))) t) ; Each element of stack-s (resp. stack-f) corresponds to an attempt to apply a ; certain xrune and the element is the value of cnt-s (resp. cnt-f) at the time ; the attempt started. When the attempt is successful, we increase cnt-s, ; first by one if we have a rune rather than a hyp-xrune or conc-xrune, and ; then by the difference of the current value of cnt-s with the old value (on ; top of stack-s) to find out how many lemmas were usefully tried under that ; xrune, and we accumulate that difference into the success fields of the ; current totals for that xrune; similarly for cnt-f and useless tries. When ; the attempt fails, all accumulation is into the failure (useless) fields of ; the totals. The accumulated totals is a stack of lists, each of which ; contains elements associating xrunes with values n-s, ap-s, n-f, and ap-f, ; where n-s is the number of times xrune was pushed onto the stack and ; successfully applied, ap-s is the accumulated persistence of useful ; applications of that xrune (the total number of applications while that xrune ; was on the stack, not yet including such applications that are recorded in ; more recent stack frames), and n-f and ap-f are similar but for failed ; applications of that xrune. ; Performance: We have seen (Version_3.2.1) about a 50% increase in time with ; accumulated-persistence turned on, in very limited experiments. In later ; versions we have found a significantly smaller increase unless enhanced ; statistics are gathered. Here are results using code developed after ; Version_4.1, using Allegro CL. ; In (community) books directory workshops/2004/legato/support/ ; (see below for discussion of which forms were submitted for each experiment): ; ; (set-inhibit-output-lst '(prove proof-tree)) ; ;; (ld "/projects/acl2/devel-misc/patches/accumulated-persistence-hyps-rhs/patch.lisp") ; ;; (value :q) ; ;;; (load "/projects/acl2/devel-misc/patches/accumulated-persistence-hyps-rhs/patch.fasl") ; ;; (load "/projects/acl2/devel-misc/patches/accumulated-persistence-hyps-rhs/old.fasl") ; ;; (lp) ; ; (accumulated-persistence t) ; (time$ (ld "proof-by-generalization-mult.lisp")) ; ; No accp (skip commented forms) ; ; 97.01 seconds realtime, 95.54 seconds runtime. ; ; Old accp (include only one commented form, (accumulated-persistence t)) ; ; 114.82 seconds realtime, 113.46 seconds runtime. ; ; New accp (include all commented forms except ;;;) ; ; 164.95 seconds realtime, 163.69 seconds runtime. ; ; New accp (include all commented forms) ; ; 165.09 seconds realtime, 163.90 seconds runtime. ; ; The results above were based on code that always gave results for a :conc ; xrune. We now skip that xrune if there are no hypotheses, but that didn't ; make much difference: ; ; 164.84 seconds realtime, 163.50 seconds runtime. (defun merge-accumulated-persistence-aux (xrune entry alist) (cond ((endp alist) ; See the comment below merge-accumulated-persistence for an attempt we made to ; avoid this unfortunate case (unfortunate because we consed up a new alist ; rather than just pushing the entry on the front). (list entry)) ((xrune= xrune (access accp-entry (car alist) :xrune)) (cons (make accp-entry :xrune xrune :n-s (+ (access accp-entry entry :n-s) (access accp-entry (car alist) :n-s)) :ap-s (+ (access accp-entry entry :ap-s) (access accp-entry (car alist) :ap-s)) :n-f (+ (access accp-entry entry :n-f) (access accp-entry (car alist) :n-f)) :ap-f (+ (access accp-entry entry :ap-f) (access accp-entry (car alist) :ap-f))) (cdr alist))) (t (cons (car alist) (merge-accumulated-persistence-aux xrune entry (cdr alist)))))) (defun merge-accumulated-persistence-rec (new-alist old-alist) (cond ((endp new-alist) old-alist) (t (merge-accumulated-persistence-rec (cdr new-alist) (merge-accumulated-persistence-aux (access accp-entry (car new-alist) :xrune) (car new-alist) old-alist))))) (defun merge-accumulated-persistence (new-alist old-alist) ; We tried an approach using sorting with rune-< (before we considered xrunes), ; but it was much more expensive. We used as an example: ; cd books/workshops/2004/legato/support/ ; acl2::(set-inhibit-output-lst '(prove proof-tree)) ; (accumulated-persistence t) ; (time$ (ld "proof-by-generalization-mult.lisp")) ; All timings below are in GCL 2.6.7. ; Using rune-<: ; real time : 198.170 secs ; run-gbc time : 186.320 secs ; child run time : 0.000 secs ; gbc time : 7.720 secs ; Using the present approach, without sorting: ; real time : 141.170 secs ; run-gbc time : 129.070 secs ; child run time : 0.000 secs ; gbc time : 7.900 secs ; Approach through Version_3.2, without accounting for successes/failures: ; real time : 137.140 secs ; run-gbc time : 127.410 secs ; child run time : 0.000 secs ; gbc time : 5.930 secs ; See also below for alternate approach that didn't do as well. (cond ((null old-alist) ; optimization new-alist) (t (merge-accumulated-persistence-rec new-alist old-alist)))) ; The following comment was written before the introduction of xrunes, and we ; have left it unchanged from that time. ; ; The following alternate code doesn't do as well, even though it uses nconc. ; The nconc version doesn't even seem much better on space (in fact worse in an ; Allegro CL test as reported by "space allocation": 669,110,675 cons cells in ; the nconc version vs. 593,299,188). Compare the times below with those ; reported in merge-accumulated-persistence. ; ; Our basic idea was to avoid walking consing up a new alist in ; merge-accumulated-persistence-aux when the given rune wasn't already a key in ; the given alist. ; ; real time : 157.780 secs ; run-gbc time : 146.960 secs ; child run time : 0.000 secs ; gbc time : 8.990 secs ; ; Replacing nconc with revappend: ; ; real time : 168.870 secs ; run-gbc time : 149.930 secs ; child run time : 0.000 secs ; gbc time : 8.930 secs ; ; (defun merge-accumulated-persistence-1 (rune entry alist) ; (cond ((endp alist) ; (er hard 'merge-accumulated-persistence-1 ; "Implementation error: Unexpected end of list! Please contacct ~ ; the ACL2 implementors.")) ; ((rune= rune (access accp-entry (car alist) :rune)) ; (cons (make accp-entry ; :rune rune ; :n-s (+ (access accp-entry entry :n-s) ; (access accp-entry (car alist) :n-s)) ; :ap-s (+ (access accp-entry entry :ap-s) ; (access accp-entry (car alist) :ap-s)) ; :n-f (+ (access accp-entry entry :n-f) ; (access accp-entry (car alist) :n-f)) ; :ap-f (+ (access accp-entry entry :ap-f) ; (access accp-entry (car alist) :ap-f))) ; (cdr alist))) ; (t (cons (car alist) ; (merge-accumulated-persistence-1 rune entry (cdr alist)))))) ; ; (defun assoc-rune= (rune alist) ; (cond ((endp alist) nil) ; ((rune= rune (access accp-entry (car alist) :rune)) ; (car alist)) ; (t (assoc-rune= rune (cdr alist))))) ; ; (defun merge-accumulated-persistence-rec (new-alist old-alist new-alist-new) ; ; ; We merge into old-alist as we go along, except that entries in new-alist that ; ; are not in old-alist are put into new-alist-new. ; ; (cond ((endp new-alist) (nconc new-alist-new old-alist)) ; (t (let* ((rune (access accp-entry (car new-alist) :rune)) ; (pair (assoc-rune= rune old-alist))) ; (merge-accumulated-persistence-rec ; (cdr new-alist) ; (cond (pair (merge-accumulated-persistence-1 ; rune ; (car new-alist) ; old-alist)) ; (t old-alist)) ; (cond (pair new-alist-new) ; (t (cons (car new-alist) new-alist-new)))))))) ; ; ; Also would need to add final argument of nil to call of ; ; merge-accumulated-persistence-rec in merge-accumulated-persistence. ; (defun add-accumulated-persistence-s (xrune delta-s delta-f alist original-alist acc) ; Warning: Keep this in sync with add-accumulated-persistence-f. (cond ((null alist) (cons (make accp-entry :xrune xrune :n-s 1 :ap-s (+ delta-f delta-s) :n-f 0 :ap-f 0) original-alist)) ((xrune= xrune (access accp-entry (car alist) :xrune)) (cons (change accp-entry (car alist) :ap-f 0 ; no change in :n-f :n-s (1+ (access accp-entry (car alist) :n-s)) :ap-s (+ delta-s delta-f (access accp-entry (car alist) :ap-s) (access accp-entry (car alist) :ap-f))) (revappend acc (cdr alist)))) (t (add-accumulated-persistence-s xrune delta-s delta-f (cdr alist) original-alist (cons (car alist) acc))))) (defun add-accumulated-persistence-f (xrune delta-s delta-f alist original-alist acc) ; Warning: Keep this in sync with add-accumulated-persistence-s. ; We assume that every :ap-s field if alist is 0, as is the case for alists ; produced by accumulated-persistence-make-failures. (cond ((null alist) (cons (make accp-entry :xrune xrune :n-s 0 :ap-s 0 :n-f 1 :ap-f (+ delta-f delta-s)) original-alist)) ((xrune= xrune (access accp-entry (car alist) :xrune)) (cons (change accp-entry (car alist) :n-f (1+ (access accp-entry (car alist) :n-f)) :ap-f (assert$ (eql (access accp-entry (car alist) :ap-s) 0) (+ delta-s delta-f (access accp-entry (car alist) :ap-f)))) (revappend acc (cdr alist)))) (t (add-accumulated-persistence-f xrune delta-s delta-f (cdr alist) original-alist (cons (car alist) acc))))) (defun accumulated-persistence-make-failures (alist) (cond ((null alist) nil) (t (cons (change accp-entry (car alist) :n-f (+ (access accp-entry (car alist) :n-f) (access accp-entry (car alist) :n-s)) :n-s 0 :ap-f (+ (access accp-entry (car alist) :ap-f) (access accp-entry (car alist) :ap-s)) :ap-s 0) (accumulated-persistence-make-failures (cdr alist)))))) (defun add-accumulated-persistence (xrune success-p delta-s delta-f alist-stack) ; Alist-stack is a list of lists of accp-entry records. First, we modify the ; top of alist-stack to record everything as useless (failure) if success-p is ; nil. Then, we merge that modification into the second element of ; alist-stack, after incrementing by 1 for the given xrune's :n-s or :n-f and ; by delta for its :ap-s or :ap-f, according to success-p. (assert$ (cdr alist-stack) (let* ((alist (if success-p (car alist-stack) (accumulated-persistence-make-failures (car alist-stack)))) (new-alist (cond (success-p (add-accumulated-persistence-s xrune delta-s delta-f alist alist nil)) (t (add-accumulated-persistence-f xrune delta-s delta-f alist alist nil))))) (cons (merge-accumulated-persistence new-alist (cadr alist-stack)) (cddr alist-stack))))) (defmacro accumulated-persistence (flg) ; Warning: Keep this in syc with set-waterfall-parallelism-fn. ":Doc-Section Other to get statistics on which ~il[rune]s are being tried~/ ~bv[] Useful Forms: (accumulated-persistence t) ; Activate statistics gathering. (accumulated-persistence :all) ; As above, ``enhanced'' (see below) (show-accumulated-persistence :frames) ; Display statistics ordered by (show-accumulated-persistence :tries) ; frames built, times tried, (show-accumulated-persistence :ratio) ; or their ratio. (accumulated-persistence nil) ; Deactivate. Advanced forms: (show-accumulated-persistence :frames-s) ; The `s', `f', and `a' suffixes (show-accumulated-persistence :frames-f) ; stand for `success' (`useful'), (show-accumulated-persistence :frames-a) ; `failure' (`useless'), and `all', (show-accumulated-persistence :tries-s) ; respectively. The only effect of (show-accumulated-persistence :tries-f) ; the `s' and `f' versions is to (show-accumulated-persistence :tries-a) ; sort first by useful or useless ; applications, respectively (see ; below). The `a' versions avoid ; showing the useful/useless ; breakdown. (show-accumulated-persistence :runes) ; Just show runes alphabetically. ~ev[]~/ Note: ~c[set-accumulated-persistence] is equivalent to ~c[accumulated-persistence]. See the end of this item for a discussion of ``enhanced statistics gathering,'' which can be useful for more fine-grained proof debugging. Generally speaking, the more ACL2 knows, the slower it runs. That is because the search space grows with the number of alternative rules. Often, the system tries to apply rules that you have forgotten were even there, if you knew about them in the first place! ``Accumulated-persistence'' is a statistic (originally developed for Nqthm) that helps you identify the rules that are causing ACL2's search space to explode. For other proof debugging utilities, ~pl[break-rewrite] and ~pl[dmr]. Accumulated persistence tracking can be turned on or off. It is generally off. When on, proofs may take perhaps 50% more time than otherwise! But some useful numbers are collected. When it is turned on, by ~bv[] ACL2 !>(accumulated-persistence t) ~ev[] an accumulation site is initialized and henceforth data about which rules are being tried is accumulated into that site. That accumulated data can be displayed with ~c[show-accumulated-persistence], as described in detail below. When accumulated persistence is turned off, with ~c[(accumulated-persistence nil)], the accumulation site is wiped out and the data in it is lost. The ``accumulated persistence'' of a ~il[rune] is the number of ~il[rune]s the system has attempted to apply (since accumulated persistence was last activated) while the given ~il[rune] was being tried. Consider a ~c[:]~ilc[rewrite] rule named ~ilc[rune]. For simplicity, let us imagine that ~ilc[rune] is tried only once in the period during which accumulated persistence is being monitored. Recall that to apply a rewrite rule we must match the left-hand side of the conclusion to some term we are trying to rewrite, establish the hypotheses of ~ilc[rune] by rewriting, and, if successful, then rewrite the right-hand side of the conclusion. We say ~ilc[rune] is ``being tried'' from the time we have matched its left-hand side to the time we have either abandoned the attempt or finished rewriting its right-hand side. (By ``match'' we mean to include any loop-stopper requirement; ~pl[loop-stopper].) During that period of time other rules might be tried, e.g., to establish the hypotheses. The rules tried while ~ilc[rune] is being tried are ``billed'' to ~ilc[rune] in the sense that they are being considered here only because of the demands of ~ilc[rune]. Thus, if no other rules are tried during that period, the accumulated persistence of ~ilc[rune] is ~c[1] ~-[] we ``bill'' ~ilc[rune] once for its own application attempt. If, on the other hand, we tried ~c[10] rules on behalf of that application of ~ilc[rune], then ~ilc[rune]'s accumulated persistence would be ~c[11]. One way to envision accumulated persistence is to imagine that every time a ~il[rune] is tried it is pushed onto a stack. The rules tried on behalf of a given application of a ~il[rune] are thus pushed and popped on the stack above that ~il[rune]. A lot of work might be done on its behalf ~-[] the stack above the ~il[rune] grows and shrinks repeatedly as the search continues for a way to use the ~il[rune]. All the while, the ~il[rune] itself ``persists'' in the stack, until we finish with the attempt to apply it, at which time we pop it off. The accumulated persistence of a ~il[rune] application is thus the number of stack frames built while that ~il[rune] was on the stack. Note that accumulated persistence is tallied whether or not the attempt to apply a ~il[rune] is successful. Each of the rules tried on its behalf might have failed and the attempt to apply the ~il[rune] might have also failed. The ACL2 proof script would make no mention of the ~il[rune] or the rules tried on its behalf because they did not contribute to the proof. But time was spent pursuing the possible application of the ~il[rune] and accumulated persistence is a measure of that time. A high accumulated persistence might come about in two extreme ways. One is that the rule causes a great deal of work every time it is tried. The other is that the rule is ``cheap'' but is tried very often. We therefore keep track of the number of times each rule is tried as well as its persistence. The ratio between the two is the average amount of work done on behalf of the rule each time it is tried. When the accumulated persistence totals are displayed by the function ~c[show-accumulated-persistence] we sort them so that the most expensive ~il[rune]s are shown first. We can sort according to one of three basic keys: ~bv[] :frames - the number of frames built on behalf of the rune :tries - the number of times the rune was tried :ratio - frames built per try ~ev[] The key simply determines the order in which the information is presented. If no argument is supplied to ~c[show-accumulated-persistence], ~c[:frames] is used. The display breaks each total into ``useful'' and ``useless'' subtotals. A ``useful'' rule try is one that is viewed as contributing to the progress of the proof, and the rest are ``useless'' rule applications. For example, if a ~c[:]~ilc[rewrite] rule is tried but its hypotheses are not successfully relieved, then that rule application and all work done on behalf of those hypotheses is ``useless'' work. In general, an attempt to apply a ~il[rune] is viewed as ``useful'' unless the attempt fails or the attempt is on the stack (as described above) for a ~il[rune] application that ultimately fails. A large number of ``useless'' ~c[:frames] or ~c[:tries] along with correspondingly small ``useful'' counts may suggest ~il[rune]s to consider disabling (~pl[disable] and ~pl[in-theory]). Thus, here is a more complete list of the arguments that may be supplied to ~c[show-accumulated-persistence]. Suffixes ``s'', ``f'', and ``a'' are intended to suggest ``success'' (``useful''), ``failure'' (``useless''), and ``all''. ~bv[] :frames - sort by the number of frames built on behalf of the rune :frames-s - as above, but sort by useful applications :frames-f - as above, but sort by useless applications :frames-a - as above, but inhibit display of ``useful'' and ``useless'' subtotals :tries - sort by the number of times the rune was tried :tries-s - as above, but sort by useful applications :tries-f - as above, but sort by useless applications :tries-a - as above, but inhibit display of ``useful'' and ``useless'' subtotals :ratio - sort by frames built per try :useless - show only the runes tried whose tries were all ``useless'' ~ev[] For a given line of the report, every frame credited to a ``useful'' (respectively, ``useless'') rule application is considered ``useful'' (respectively, ``useless''). We illustrate with the following example. ~bv[] (progn (defstub hyp (x) t) (defstub concl (x) t) (defstub bad (x) t) (defstub good (x) t) (defaxiom good-ax (implies (good x) (hyp x))) (defaxiom bad-ax (implies (bad x) (hyp x))) (defaxiom hyp-implies-concl (implies (hyp x) (concl x))) ) (accumulated-persistence t) (thm (implies (good x) (concl x))) (show-accumulated-persistence) ~ev[] To prove the ~ilc[thm] form, ACL2 attempts to rewrite ~c[(concl x)] to true by applying rule ~c[hyp-implies-concl]. It then attempts to establish ~c[(hyp x)] first by trying rule ~c[bad-ax], which fails, and second by trying rule ~c[good-ax], which succeeds. As expected, the report labels as ``useless'' the failure of the attempt to establish the hypothesis, ~c[(bad x)]. ~bv[] -------------------------------- 1 1 ( 1.00) (:REWRITE BAD-AX) 0 0 [useful] 1 1 [useless] -------------------------------- ~ev[] Now consider the top-level application of rule ~c[hyp-implies-concl]. Even though the above report shows the application of ~c[bad-ax] as ``useless'', note that this rule was applied on behalf of the successful (``useful'') application of ~c[hyp-implies-concl], and hence is incorporated into the ``useful'' line for ~c[hyp-implies-concl], as follows. ~bv[] -------------------------------- 3 1 ( 3.00) (:REWRITE HYP-IMPLIES-CONCL) 3 1 [useful] 0 0 [useless] -------------------------------- ~ev[] In summary: categorization of ~c[:frames] as ``useful'' or ``useless'' is based on whether they support ``useful'' or ``useless'' ~c[:tries]. Note that a ~il[rune] with high accumulated persistence may not actually be the ``culprit.'' For example, suppose ~c[rune1] is reported to have a ~c[:ratio] of ~c[101], meaning that on the average a hundred and one frames were built each time ~c[rune1] was tried. Suppose ~c[rune2] has a ~c[:ratio] of ~c[100]. It could be that the attempt to apply ~c[rune1] resulted in the attempted application of ~c[rune2] and no other ~il[rune]. Thus, in some sense, ~c[rune1] is ``cheap'' and ~c[rune2] is the ``culprit'' even though it costs less than ~c[rune1]. If a proof is aborted, then in general, ~ilc[show-accumulated-persistence] will only display totals for runes whose attempted application is complete: that is, if the rewriter was in the process of relieving hypotheses for a rule, then information for that rule will not be included in the tally. We say ``in general'' because, as indicated near the top of the output from ~ilc[show-accumulated-persistence] when such incomplete information is omitted, you can get this information by using argument ~c[:frames-a] or ~c[:tries-a]. There are other subtleties in how rune applications are tallied, documented elsewhere: ~pl[accumulated-persistence-subtleties]. We conclude with a discussion of ``enhanced'' statistics gathering, which is enabled by supplying ~c[accumulated-persistence] the argument ~c[:ALL]: ~bv[] (accumulated-persistence :all) ~ev[] At some additional performance expense (but probably well under a factor of 2 altogether), ACL2 then gathers additional statistics for individual hypotheses of rules as well as their conclusions. To understand how this works, suppose ~c[rn] is a ~il[rune]. Then we prepend the keyword ~c[:CONC] to ~c[rn] to form what we call its ``conclusion xrune'', and for its ~c[I]-th hypothesis we prepend ~c[:HYP I] to ~c[rn] to form its ~c[I]-th ``hypothesis xrune.'' Here, ``xrune'' is pronounced ``ex rune'', and is mnemonic for ``extended rune.'' For example, if ~c[(REWRITE FOO)] is a ~il[rune] then ~c[(:CONC REWRITE FOO)] is its conclusion xrune, and ~c[(:HYP 2 REWRITE FOO)] is a hypothesis xrune corresponding to the second hypothesis of the corresponding rewrite rule. With ~c[(accumulated-persistence :all)], we instruct ACL2 to track not only runes but also xrunes. Then, ~c[(show-accumulated-persistence)] will display information for all xrunes in a format that we consider to be ``raw'', in the sense that data for xrunes are displayed just as for runes. But a ``merged'' format is also available. Here is a summary of display commands, followed below by further discussion. ~bv[] (show-accumulated-persistence :frames t) ; t is optional, i.e., the default ; Display enhanced statistics sorted by frames, in a ``raw'' format. (show-accumulated-persistence :frames :merge) ; Display enhanced statistics sorted by frames, in a ``merged'' format. (show-accumulated-persistence :frames nil) ; Display regular statistics sorted by frames, without the enhancements. ; More generally, the descriptions just above apply for any legal first ; argument: (show-accumulated-persistence KEY t) (show-accumulated-persistence KEY :merge) (show-accumulated-persistence KEY nil) ; Note also these alternate forms, equivalent to the first of the two forms ; just above, i.e., the form with second argument of t: (show-accumulated-persistence KEY :raw) (show-accumulated-persistence KEY) ~ev[] There is a significant difference between how runes are tracked and how ACL2 tracks hypothesis and conclusion xrunes: unlike regular runes, these xrunes do not contribute to the accumulated ~c[:frames] counts. Rather, they serve as accumulation sites without contributing their ~c[:tries] to any accumulation. Consider for example the snippet below, taken from a report created with the ~c[:merge] option (to be discussed further below), i.e., by evaluating the form ~c[(show-accumulated-persistence :frames :merge)]. ~bv[] :frames :tries :ratio rune -------------------------------- 462 211 ( 2.18) (:REWRITE PERM-MEM) 13 6 [useful] 449 205 [useless] ............................. 251 47 ( 5.34) (:HYP 2 :REWRITE PERM-MEM) 6 6 [useful] 245 41 [useless] ............................. 0 211 ( 0.00) (:HYP 1 :REWRITE PERM-MEM) 0 6 [useful] 0 205 [useless] ............................. 0 7 ( 0.00) (:CONC :REWRITE PERM-MEM) 0 6 [useful] 0 1 [useless] -------------------------------- ~ev[] Notice that while ~c[:tries] are recorded for the xrune ~c[(:HYP 1 :REWRITE PERM-MEM)], no ~c[:frames] are recorded. This is because no stack frames were built for runes while this xrune was on the stack ~-[] only for the xrune itself, which as we explained above is not accumulated into the total ~c[:frames] counts. As it turns out, this lack of stack frames is explained by the fact that the rewrite rule ~c[PERM-MEM] has a free variable in the first hypothesis. ~bv[] ACL2 !>:pe perm-mem 18 (DEFTHM PERM-MEM (IMPLIES (AND (PERM X Y) (MEM A X)) (MEM A Y)) :RULE-CLASSES ((:REWRITE :MATCH-FREE :ONCE))) ACL2 !> ~ev[] The second hypothesis, however, does cause additional rewriting in order to rewrite it to true, resulting in 251 stack frames for runes. We see that the conclusion does not lead to creation of any rune stack frames, which might seem to suggest that only 251 stack frames for runes were created on behalf of this rule application ~-[] yet, we see that 462 frames were actually created. The difference is the 211 frames created for the rewrite rule itself. Even if the total had been a bit more than 462, one need not be surprised, as there could be some work recorded during application of the rewrite rule, such as type-prescription reasoning, that is not done during rewriting of a hypothesis or the conclusion. Now suppose we have executed ~c[(accumulated-persistence :all)] and attempted some proofs, and now we are ready to see statistics. The form ~c[(show-accumulated-persistence)] displays statistics exactly as described above, treating these extra xrunes just as though they are runes; similarly for the form ~c[(show-accumulated-persistence KEY)], for any legal ~c[KEY]. A second optional argument may however be supplied to ~c[show-accumulated-persistence]. The default for that second argument is ~c[t], and a second argument of ~c[:raw] is treated the same as ~c[t]; thus, these arguments provide the behavior just described, where data for xrunes are displayed just as for runes. You may restrict output to runes, ignoring hypothesis and conclusion xrunes, by giving a second argument of ~c[nil]. (This gives the same behavior as if we had started with the command ~c[(accumulated-persistence t)] instead of the command ~c[(accumulated-persistence :all)].) Finally, you may give a second argument of ~c[:merge], in which case output will be sorted and displayed as though only runes were tracked (not the extra xrunes), but each data item for a non-rune xrune will be merged so that it is displayed in suitable order just below its corresponding rune, as in the ~c[PERM-MEM] example displayed above. We close by mentioning two aspects of enhanced statistics display for ~c[:CONC] xrunes that have potential to be confusing. First consider the following example. ~bv[] :frames :tries :ratio rune -------------------------------- 14 4 ( 3.50) (:REWRITE DEFAULT-+-2) 0 0 [useful] 14 4 [useless] ............................. 10 4 ( 2.50) (:HYP 1 :REWRITE DEFAULT-+-2) 0 0 [useful] 10 4 [useless] -------------------------------- ~ev[] It may be surprising that no data is displayed for the corresponding ~c[:CONC] xrune. The explanation, however, is simple: the hypothesis never rewrote to true, so the conclusion was never rewritten. This is consistent with the marking as ``useless'' of all ~c[:frames] and ~c[:tries] for the rune and the hypothesis xrune. Note by the way, once again, that the hypothesis xrune does not contribute to any ~c[:frames] count. Another reason not to see data displayed for a ~c[:CONC] xrune is that if a rule has no hypotheses, then no such data is collected. This decision was made because in the case of no hypotheses, we expect it to be very rare that information for the ~c[:CONC] xrune will add any useful insight. On a final note: ~c[(show-accumulated-persistence :runes)] may be used simply to see a list of all ~il[rune]s (or xrunes) displayed alphabetically. Users are encouraged to think about other meters we could install in ACL2 to help diagnose performance problems." ; Our convention is that if accumulated persistence is enabled, the data of the ; accumulated-persistence wormhole is non-nil. If accummulated persistence is ; not enabled, the data is nil. (declare (xargs :guard (member-equal flg '(t 't nil 'nil :all ':all)))) (let* ((flg (if (consp flg) (cadr flg) flg)) (collect-hyp-and-conc-xrunes (eq flg :all))) `(cond #+acl2-par ; the following test is always false when #-acl2-par ((f-get-global 'waterfall-parallelism state) (er hard! 'accumulated-persistence "~x0 is not supported when waterfall-parallelism is enabled." 'accumulated-persistence)) (t (wormhole-eval 'accumulated-persistence '(lambda (whs) (set-wormhole-data whs (if ,flg (make accp-info :cnt-s 0 :cnt-f 0 :stack-s nil :stack-f nil :xrunep ,collect-hyp-and-conc-xrunes :totals '(nil)) nil))) nil))))) (defmacro set-accumulated-persistence (flg) `(accumulated-persistence ,flg)) (defdoc accumulated-persistence-subtleties ":Doc-Section accumulated-persistence some subtle aspects of the counting done by ~ilc[accumulated-persistence]~/ In this topic we cover the overcounting of ``useful'' and of recursive ~il[rune] application attempts, and we describe how ``useless'' ~il[rune] application attempts can actually be critical for a proof's success.~/ ~em[Overcounting of ``useful'' and of recursive rune application attempts.] Not every ~il[rune] application may be necessary for a proof's success. Consider for example: ~bv[] (thm (equal (car (cons a (cdr (cons b x)))) a)) ~ev[] Then ~c[show-accumulated-persistence] will tell us that ~c[:]~ilc[rewrite] rules ~c[car-cons] and ~c[cdr-cons] each had one useful application. However, the rule ~c[cdr-cons] is used to simplify ~c[(cdr (cons b x))] to ~c[x], and this simplification is unecessary for the proof. Indeed, the proof succeeds even when preceded by the event: ~c[(in-theory (disable cdr-cons))]. We thus see that a ~il[rune] application labeled as ``useful'' may be simplifying a term that is not relevant to the proof. As of this writing, we consider every ~c[:]~ilc[forward-chaining] rule application to be ``useful'', for simplicity of the implementation. Moreover, our counting of these rules is such that a single rule may be counted more than once. Next we show how recursive rule applications are overcounted. Consider the following example. ~bv[] (defun mem (a x) (if (atom x) nil (or (equal a (car x)) (mem a (cdr x))))) ~ev[] Now suppose we consider the sequence of theorems ~c[(mem a (list a))], ~c[(mem a (list 1 a))], ~c[(mem a (list 1 2 a))], ~c[(mem a (list 1 2 3 a))], and so on. We will see that the ~c[:frames] reported for each increases quadratically, even though the ~c[:tries] increases linearly; so in this case the ~c[:tries] statistics are more appropriate. Each time the definition of ~c[mem] is applied, a new stack frame is pushed (~pl[accumulated-persistence]), and all subsequent applications of that definition are accumulated into the ~c[:frames] count for that stack frame. The final ~c[:frames] count will be the sum of the counts for those individual frames, which form a linear sequence whose sum is therefore quadratic in the number of applications of the definition of ~c[mem]. ~em[How ``useless'' attempts can be critical for a proof's success.] The command ~c[(accumulated-persistence :useless])] will list rules that did not contribute directly to the proof (~pl[accumulated-persistence], in particular the discussion of ``useless'' there). However, a ``useless'' rule can on rare occasions be critical to the success of a proof. In the following example, we have a ``bad'' rule that can take the proof in the wrong direction, but a ``useless'' rule does a rewrite that prevents the succesful relieving of a hypothesis of the ``bad'' rule. In summary: ~bv[] ; Assume p0. We want to prove p1. ; Key rule: p0 -> p1 = t ; Bad rule that could ruin the proof: p3 -> p1 = p2 ; But unfortunately, we know p3: p0 -> p3 ; Important ``useless'' rule, preventing ``bad rule'' above from firing: p3 = p4 ~ev[] The following event captures the rules described above. ~bv[] (encapsulate ((p0 (x) t) (p1 (x) t) (p2 (x) t) (p3 (x) t) (p4 (x) t)) (local (defun p0 (x) x)) (local (defun p1 (x) x)) (local (defun p2 (x) x)) (local (defun p3 (x) x)) (local (defun p4 (x) x)) ; Key rule: (defthm p0-implies-p1 (implies (p0 x) (p1 x))) ; Bad rule that could ruin the proof: (defthm p3-implies-p1-is-p2 (implies (p3 x) (equal (p1 x) (p2 x)))) ; But unfortunately, we know p3: (defthm p0-implies-p3 (implies (p0 x) (p3 x))) ; Important ``useless'' rule, preventing p3-implies-p1-is-p2 from firing: (defthm p3-is-p4 (equal (p3 x) (p4 x)))) ~ev[] Now we can see that ~c[p3-is-p4] is labeled as ``useless'', by evaluating these commands. ~bv[] (accumulated-persistence t) (thm (implies (p0 x) (p1 x))) (show-accumulated-persistence) ~ev[] If instead we first evaluate ~c[(in-theory (disable p3-is-p4))] before the ~c[thm] above, then the proof fails, even though ~c[p3-is-p4] was labeled as ``useless''! Nevertheless, in general it is probably safe to disable rules reported as ``useless'' by ~c[(show-accumulated-persistence :useless)], and doing so may speed up a proof considerably. Remark. The example above suggests a surprising fact: on rare occasions, a proof may fail when you give an ~c[:]~ilc[in-theory] hint consisting of exactly the ~il[rune]s reported in a proof that succeeds. For, imagine a rule R that is needed in part of the proof but is ``bad'' in a second part, and that some other, ``useless'' rule prevents the application of R in that second part. The example above suggests that disabling this ``useless'' rule can allow the second application of R, thus preventing the proof.") (defun merge-car-> (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((> (car (car l1)) (car (car l2))) (cons (car l1) (merge-car-> (cdr l1) l2))) (t (cons (car l2) (merge-car-> l1 (cdr l2)))))) (defun merge-sort-car-> (l) (cond ((null (cdr l)) l) (t (merge-car-> (merge-sort-car-> (evens l)) (merge-sort-car-> (odds l)))))) (defconst *accp-major-separator* " --------------------------------~%") (defconst *accp-minor-separator* " .............................~%") (defun show-accumulated-persistence-phrase0 (entry key) (let* ((xrune (access accp-entry entry :xrune)) (n-s (access accp-entry entry :n-s)) (n-f (access accp-entry entry :n-f)) (n (+ n-s n-f)) (ap-s (access accp-entry entry :ap-s)) (ap-f (access accp-entry entry :ap-f)) (ap (+ ap-s ap-f))) (let ((main-msg (msg "~c0 ~c1 (~c2.~f3~f4) ~y5" (cons ap 10) (cons n 8) (cons (floor ap n) 5) (mod (floor (* 10 ap) n) 10) (mod (floor (* 100 ap) n) 10) (prettyify-xrune xrune)))) (case key ((:useless :ratio-a :frames-a :tries-a) (list main-msg)) (otherwise (list main-msg (msg "~c0 ~c1 [useful]~%" (cons ap-s 10) (cons n-s 8)) (msg "~c0 ~c1 [useless]~%" (cons ap-f 10) (cons n-f 8)))))))) (defun show-accumulated-persistence-phrase1 (key alist mergep acc) ; Alist has element of the form (x . accp-entry), where x is the key upon which ; we sorted. Last-rune is true (initally t, generally a rune) if and only if ; we are calling show-accumulated-persistence with display = :merge. (cond ((null alist) acc) (t (show-accumulated-persistence-phrase1 key (cdr alist) mergep (cons (cond ((and mergep (cdr alist) (equal (xrune-rune (access accp-entry (cdr (car (cdr alist))) :xrune)) (xrune-rune (access accp-entry (cdr (car alist)) :xrune)))) ; The next entry to process for extending the accumulator has the same rune as ; the current entry in this case. *accp-minor-separator*) (t *accp-major-separator*)) (append (show-accumulated-persistence-phrase0 (cdr (car alist)) key) acc)))))) (defun show-accumulated-persistence-remove-useless (alist acc) (cond ((null alist) (reverse acc)) ((not (eql (access accp-entry (cdr (car alist)) :n-s) 0)) (show-accumulated-persistence-remove-useless (cdr alist) acc)) (t (show-accumulated-persistence-remove-useless (cdr alist) (cons (car alist) acc))))) (defun show-accumulated-persistence-phrase-key (key entry lastcdr) (let* ((ap-s (access accp-entry entry :ap-s)) (ap-f (access accp-entry entry :ap-f)) (ap (+ ap-s ap-f)) (n-s (access accp-entry entry :n-s)) (n-f (access accp-entry entry :n-f)) (n (+ n-s n-f))) (case key ((:frames :frames-a) (list* ap ap-s n n-s lastcdr)) (:frames-s (list* ap-s ap n-s n lastcdr)) (:frames-f (list* ap-f ap n-f n lastcdr)) ((:tries :tries-a) (list* n n-s ap ap-s lastcdr)) (:tries-s (list* n-s n ap-s ap lastcdr)) (:tries-f (list* n-f n ap-f ap lastcdr)) (:useless (list* ap n lastcdr)) (otherwise (list* (/ ap n) lastcdr))))) (defun show-accumulated-persistence-phrase2-merge (key alist last-key-info) ; We augment the given alist by consing a sort key onto the front of each ; entry. The sort key is based on both the entry and the given key, the ; first argument of show-accumulated-persistence. (cond ((null alist) nil) ((x-xrunep (access accp-entry (car alist) :xrune)) (cons (cons (append last-key-info (show-accumulated-persistence-phrase-key key (car alist) nil)) (car alist)) (show-accumulated-persistence-phrase2-merge key (cdr alist) last-key-info))) (t (let ((next-key-info (show-accumulated-persistence-phrase-key key (car alist) (list (access accp-entry (car alist) :xrune))))) (cons (cons (append next-key-info '(t)) (car alist)) (show-accumulated-persistence-phrase2-merge key (cdr alist) next-key-info)))))) (defun show-accumulated-persistence-phrase2-not-merge (key alist) (cond ((null alist) nil) (t (cons (cons (show-accumulated-persistence-phrase-key key (car alist) nil) (car alist)) (show-accumulated-persistence-phrase2-not-merge key (cdr alist)))))) (defun show-accumulated-persistence-phrase2 (key alist mergep) (cond (mergep (show-accumulated-persistence-phrase2-merge key alist nil)) (t (show-accumulated-persistence-phrase2-not-merge key alist)))) (defun split-xrune-alist (alist rune-alist hyp-xrune-alist conc-xrune-alist) ; See sort-rune-alist-for-xrunes. (cond ((endp alist) (mv rune-alist hyp-xrune-alist conc-xrune-alist)) (t (let ((xrune (access accp-entry (car alist) :xrune))) (case (car xrune) (:hyp (split-xrune-alist (cdr alist) rune-alist (cons (car alist) hyp-xrune-alist) conc-xrune-alist)) (:conc (split-xrune-alist (cdr alist) rune-alist hyp-xrune-alist (cons (car alist) conc-xrune-alist))) (t (split-xrune-alist (cdr alist) (cons (car alist) rune-alist) hyp-xrune-alist conc-xrune-alist))))))) (defun sort-xrune-alist-by-rune1 (rune-alist hyp-xrune-alist conc-xrune-alist acc) ; Each input alist is a list of accp-entry records, sorted by lexorder -- ; indeed sorted by the cars, which are distinct xrunes. See ; sort-rune-alist-for-xrunes. We return the (disjoint) union of the three ; inputs, such that every record for a :hyp or :conc xrune is preceded by an ; entry whose :xrune is either another such xrune or is the corresponding rune. (cond ((endp rune-alist) (assert$ (and (null hyp-xrune-alist) (null conc-xrune-alist)) acc)) ((and hyp-xrune-alist (equal (hyp-xrune-rune (caar hyp-xrune-alist)) (caar rune-alist))) (sort-xrune-alist-by-rune1 rune-alist (cdr hyp-xrune-alist) conc-xrune-alist (cons (car hyp-xrune-alist) acc))) ((and conc-xrune-alist (equal (conc-xrune-rune (caar conc-xrune-alist)) (caar rune-alist))) (sort-xrune-alist-by-rune1 rune-alist hyp-xrune-alist (cdr conc-xrune-alist) (cons (car conc-xrune-alist) acc))) (t (sort-xrune-alist-by-rune1 (cdr rune-alist) hyp-xrune-alist conc-xrune-alist (cons (car rune-alist) acc))))) (defun sort-xrune-alist-by-rune (alist display) ; Alist is a list of accp-entry records. We sort them such that every record ; for a :hyp or :conc xrune is preceded either by an entry whose :xrune is ; either another such xrune or is the corresponding rune. (cond ((not (member-eq display '(nil :merge))) alist) (t (mv-let (rune-alist hyp-xrune-alist conc-xrune-alist) (split-xrune-alist alist nil nil nil) (cond ((eq display nil) rune-alist) (t ; (eq display :merge) (sort-xrune-alist-by-rune1 (merge-sort-lexorder rune-alist) (merge-sort-lexorder hyp-xrune-alist) (merge-sort-lexorder conc-xrune-alist) nil))))))) (defun pop-accp-fn (info success-p) ; Warning: Keep the branches below in sync. We considered merging them into a ; single branch, but the fields changed differ in each case, so we keep the ; cases separate in order to save a few conses. (let* ((xrune-stack (access accp-info info :xrune-stack)) (xrune (car xrune-stack)) (xp (x-xrunep xrune)) (new-cnt (and (not xp) ; optimization (cond (success-p (1+ (access accp-info info :cnt-s))) (t (1+ (access accp-info info :cnt-f))))))) (cond (xp (change accp-info info :stack-s (cdr (access accp-info info :stack-s)) :stack-f (cdr (access accp-info info :stack-f)) :xrune-stack (cdr xrune-stack) :totals (add-accumulated-persistence xrune success-p (- (access accp-info info :cnt-s) (car (access accp-info info :stack-s))) (- (access accp-info info :cnt-f) (car (access accp-info info :stack-f))) (access accp-info info :totals)))) (success-p (change accp-info info :cnt-s new-cnt :stack-s (cdr (access accp-info info :stack-s)) :stack-f (cdr (access accp-info info :stack-f)) :xrune-stack (cdr xrune-stack) :totals (add-accumulated-persistence xrune success-p (- new-cnt (car (access accp-info info :stack-s))) (- (access accp-info info :cnt-f) (car (access accp-info info :stack-f))) (access accp-info info :totals)))) (t (change accp-info info :cnt-f new-cnt :stack-s (cdr (access accp-info info :stack-s)) :stack-f (cdr (access accp-info info :stack-f)) :xrune-stack (cdr xrune-stack) :totals (add-accumulated-persistence xrune success-p (- (access accp-info info :cnt-s) (car (access accp-info info :stack-s))) (- new-cnt (car (access accp-info info :stack-f))) (access accp-info info :totals))))))) (defun pop-accp-fn-iterate (info n) (if (zp n) info (pop-accp-fn-iterate (pop-accp-fn info ; For this call of pop-accp-fn, the value of success-p is not terribly ; important at the time we are writing this code, since it is used only in ; show-accumulated-persistence-phrase, only when we throw out information that ; distinguishes between useful and useless. We choose t here in case we ever ; use such information, because we do not want to under-report successes, as ; that could encourage the disabling of rules that are useful even though they ; have been reported as useless. t) (1- n)))) (defun show-accumulated-persistence-phrase (key/display accp-info) ; Alist is the accumulated totals alist from the wormhole data field of ; wormhole-status of the 'accumulated-persistence wormhole. Each element is of ; the form (xrune n . ap) and we sort them into descending order on the ; specified key. Key should be one of the sortkey values from the guard of ; show-accumulated-persistence. (let* ((key (car key/display)) (display (cdr key/display)) (xrune-stack (access accp-info accp-info :xrune-stack)) (accp-info (cond ((and xrune-stack (member-eq key '(:frames-a :tries-a))) (pop-accp-fn-iterate accp-info (length xrune-stack))) (t accp-info))) (totals (access accp-info accp-info :totals))) (cond ((null totals) (msg "There is no accumulated persistence to show. Evaluate ~x0 to ~ activate gathering of accumulated-persistence statistics.~|" '(accumulated-persistence t))) ((eq key :runes) (msg "~x0" (merge-sort-lexorder (strip-cars (car (last totals)))))) (t (let* ((mergep (eq display :merge)) (alist (merge-sort-lexorder (show-accumulated-persistence-phrase2 key (sort-xrune-alist-by-rune (car (last totals)) display) mergep))) (main-phrase (list "" "~@*" "~@*" "~@*" (show-accumulated-persistence-phrase1 key (if (eq key :useless) (show-accumulated-persistence-remove-useless alist nil) alist) mergep nil)))) (cond ((null (cdr totals)) (msg "Accumulated Persistence~@0~|~% :frames :tries ~ :ratio rune~%~*1~@2" (if xrune-stack "" ; we merged, so don't know just what was useful (msg " (~x0 :tries useful, ~x1 :tries not useful)" (access accp-info accp-info :cnt-s) (access accp-info accp-info :cnt-f))) main-phrase *accp-major-separator*)) (t (msg "Accumulated Persistence~|~%~ ***************************************~|~ *** NOTE: INCOMPLETE!!!~|~ *** ~x0 rule attempts are not considered below.~|~ *** Use :frames-a or :tries-a to get more complete ~ totals.~|~ ***************************************~|~ ~% :frames :tries :ratio rune~%~*1~@2" (- (+ (access accp-info accp-info :cnt-s) (access accp-info accp-info :cnt-f) (length xrune-stack)) (+ (car (last (access accp-info accp-info :stack-s))) (car (last (access accp-info accp-info :stack-f))))) main-phrase *accp-major-separator*)))))))) (defmacro show-accumulated-persistence (&optional (sortkey ':frames) (display 't)) (declare (xargs :guard (and (member-eq sortkey '(:ratio :frames :frames-s :frames-f :frames-a :tries :tries-s :tries-f :tries-a :useless :runes)) (member-eq display '(t nil :raw :merge))))) ; This function engages is in a little song-and-dance about the entry code for ; the accumulated-persistence wormhole. If the user has requested that we ; print the accumulated-persistence data, we enter the wormhole to do it -- ; even accumulated persistence has not been turned on. So we have to set the ; code to :ENTER. But we have to remember to turn it back off upon leaving the ; wormhole. How to we know, once we're in the wormhole, whether to set the ; code to :SKIP or :ENTER? When we switch :SKIP to :ENTER to go inside we set ; the data field to :RETURN-TO-SKIP. This doesn't hurt anything because if the ; code was :SKIP, the data was nil anyway. Once inside, if the data is ; :RETURN-TO-SKIP we set the entry code back to :SKIP. `(wormhole 'accumulated-persistence '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) ',(cons sortkey display) '(pprogn (io? temporary nil state nil (fms "~@0" (list (cons #\0 (show-accumulated-persistence-phrase (f-get-global 'wormhole-input state) (wormhole-data (f-get-global 'wormhole-status state))))) *standard-co* state nil)) (value :q)) :ld-prompt nil :ld-missing-input-ok nil :ld-pre-eval-filter :all :ld-pre-eval-print nil :ld-post-eval-print :command-conventions :ld-evisc-tuple nil :ld-error-triples t :ld-error-action :error :ld-query-control-alist nil :ld-verbose nil)) (defun push-accp (rune x-info) (wormhole-eval 'accumulated-persistence '(lambda (whs) ; The wormhole status of the accumulated-persistence wormhole is of the form ; (:key . info), where :key is either :ENTER or :SKIP and info is an accp-info ; record or NIL. When this code is eventually converted to :logic mode and we wish to ; verify its guards we are going to have to confront the question of ; maintaining invariants on a wormhole's status so we don't have to check ; guards at runtime. For example, in the code below, (cdr whs) is assumed to ; be a accp-info record. See the Essay on Wormholes. (let ((info (wormhole-data whs))) (if (and info (or (null x-info) (access accp-info info :xrunep))) (let ((xrune (cond ((natp x-info) (hyp-xrune x-info rune)) ((eq x-info :conc) (conc-xrune rune)) ((null x-info) rune) (t (er hard 'push-accp "Implementation error: ~ Bad value of x-info, ~x0." x-info))))) (set-wormhole-data whs (change accp-info info :xrune-stack (cons xrune (access accp-info info :xrune-stack)) :stack-s (cons (access accp-info info :cnt-s) (access accp-info info :stack-s)) :stack-f (cons (access accp-info info :cnt-f) (access accp-info info :stack-f)) :totals (cons nil (access accp-info info :totals))))) whs))) ; We avoid locking push-accp, in order to benefit the performance of ACL2(p). ; Note that accumulated persistence is disallowed when waterfall-parallelism is ; enabled; see accumulated-persistence and set-waterfall-parallelism-fn. (cons :no-wormhole-lock rune))) (defun pop-accp (success-p x-info) (wormhole-eval 'accumulated-persistence '(lambda (whs) (let ((info (wormhole-data whs))) (if (and info (or (null x-info) (access accp-info info :xrunep))) (set-wormhole-data whs (pop-accp-fn info success-p)) whs))) ; We avoid locking pop-accp, in order to benefit the performance of ACL2(p). ; Note that accumulated persistence is disallowed when waterfall-parallelism is ; enabled; see accumulated-persistence and set-waterfall-parallelism-fn. (cons :no-wormhole-lock success-p))) (defmacro with-accumulated-persistence (rune vars success-p body &optional x-info (condition 'nil condition-p)) ; X-info can be :conc to indicate that we are tracking the right-hand side of a ; rule, or a positive integer n to indicate that we are tracking the nth ; hypothesis of a rule. Otherwise x-info should be nil. ; Vars is a list of variable names, except that the first member of vars may be ; of the form (the type var), which is treated the same as var except that a ; suitable type declaration is added (which can assist GCL in avoiding boxing ; of fixnums). (flet ((fix-var (var) (if (consp var) ; (the type var) (caddr var) var)) (fix-var-declares (var) (and (consp var) ; (the type var) `((declare (type ,(cadr var) ,(caddr var))))))) (flet ((fix-vars (vars) (if (consp vars) (cons (fix-var (car vars)) (cdr vars)) vars))) (let ((form `(prog2$ (push-accp ,rune ,x-info) ,(cond ((and (true-listp vars) (= (length vars) 1)) `(let ((,(fix-var (car vars)) ,body)) ,@(fix-var-declares (car vars)) (prog2$ (pop-accp ,success-p ,x-info) ,(fix-var (car vars))))) (t `(mv-let ,(fix-vars vars) ,body ,@(fix-var-declares (car vars)) (prog2$ (pop-accp ,success-p ,x-info) (mv ,@(fix-vars vars))))))))) (cond (condition-p `(cond (,condition ,form) (t ,body))) (t form)))))) ;; RAG - Changed the assumptions based on rational to realp. (defun assume-true-false-< (not-flg arg1 arg2 ts1 ts2 type-alist ttree xttree w) ; This function returns an extended type-alist by assuming (< ts1 ts2) true if ; not-flg is nil, but assuming (< ts1 ts2) false if not-flg is not nil. It ; assumes that type-set (and hence type-set-<) was not able to decide the truth ; or falsity of (< ts1 ts2). We could put this code in-line in ; assume-true-false, but the `true-type-alist' and `false-type-alist' are dealt ; with symmetrically, so it's convenient to share code via this function. ; Here are the cases we handle. In this sketch we are glib about the ; possibility that arg1 or arg2 is nonnumeric or complex, but our code handles ; the more general situation. ; When we assume (< arg1 arg2) true, ; * if arg1 is positive then arg2 is positive ; * if arg1 is in the nonnegatives then arg2 is strictly positive ; * if arg2 is in the nonpositives then arg1 is strictly negative ; When we say "arg1 is in the nonnegatives" we mean to include the ; case where arg1 is strictly positive. Note also that if arg1 may be ; negative, then arg2 could be anything (given that we've made the ; normalization for integers above). Thus, the above two cases are as ; strong as we can be. ; When we assume (< arg1 arg2) false we find it easier to think about ; assuming (<= arg2 arg1) true: ; * if arg1 is negative, then arg2 is negative ; * if arg1 is nonpositive, then arg2 is nonpositive ; * if arg2 is nonnegative, then arg1 is nonnegative ; Note that if arg1 may be positive then arg2 could be anything, so ; there are no other cases we can express. (cond ((and (not not-flg) (ts-subsetp ts1 (ts-union #+:non-standard-analysis *ts-non-negative-real* #-:non-standard-analysis *ts-non-negative-rational* (ts-complement *ts-acl2-number*))) (ts-intersectp ts2 (ts-complement #+:non-standard-analysis (ts-union *ts-positive-real* *ts-complex*) #-:non-standard-analysis (ts-union *ts-positive-rational* *ts-complex-rational*)))) ; The test says: We are dealing with (< arg1 arg2) where arg1 is non-negative ; or a non-number. We are thus allowed to deduce that arg2 is strictly ; positive or complex. That is, we may delete the non-positive reals ; and non-numbers from its existing type-set. If that doesn't change ; anything, we don't want to do it, so we have the third conjunct above that ; says arg2 contains some non-positive reals or some non-numbers. ; A worry is that the intersection below is empty. Can that happen? If it ; did, then we would have that arg1 is a non-negative real or a non-number, ; and arg2 is a non-positive real or a non-number. Supposedly type-set-< ; would have then reported that (< arg1 arg2) must be false and mbf would be t. ; So the empty intersection cannot arise. (extend-type-alist ;;*** -simple arg2 (ts-intersection ts2 #+:non-standard-analysis (ts-union *ts-positive-real* *ts-complex*) #-:non-standard-analysis (ts-union *ts-positive-rational* *ts-complex-rational*)) (cons-tag-trees ttree xttree) type-alist w)) ; The remaining cases are analogous to that above. ((and (not not-flg) (ts-subsetp ts2 (ts-union #+:non-standard-analysis *ts-non-positive-real* #-:non-standard-analysis *ts-non-positive-rational* (ts-complement *ts-acl2-number*))) (ts-intersectp ts1 (ts-complement #+:non-standard-analysis (ts-union *ts-negative-real* *ts-complex*) #-:non-standard-analysis (ts-union *ts-negative-rational* *ts-complex-rational*)))) (extend-type-alist ;;*** -simple arg1 (ts-intersection ts1 #+:non-standard-analysis (ts-union *ts-negative-real* *ts-complex*) #-:non-standard-analysis (ts-union *ts-negative-rational* *ts-complex-rational*)) (cons-tag-trees ttree xttree) type-alist w)) ((and not-flg (ts-subsetp ts1 #+:non-standard-analysis *ts-negative-real* #-:non-standard-analysis *ts-negative-rational*) (ts-intersectp ts2 #+:non-standard-analysis (ts-complement (ts-union *ts-complex* *ts-negative-real*)) #-:non-standard-analysis (ts-complement (ts-union *ts-complex-rational* *ts-negative-rational*)))) ; We are dealing with (not (< arg1 arg2)) which is (<= arg2 arg1) and we here ; know that arg1 is negative. Thus, arg2 must be negative or complex. See the ; case below for more details. (extend-type-alist ;;*** -simple arg2 (ts-intersection ts2 #+:non-standard-analysis (ts-union *ts-complex* *ts-negative-real*) #-:non-standard-analysis (ts-union *ts-complex-rational* *ts-negative-rational*)) (cons-tag-trees ttree xttree) type-alist w)) ((and not-flg (ts-subsetp ts1 (ts-union #+:non-standard-analysis *ts-non-positive-real* #-:non-standard-analysis *ts-non-positive-rational* (ts-complement *ts-acl2-number*))) (ts-intersectp ts2 #+:non-standard-analysis *ts-positive-real* #-:non-standard-analysis *ts-positive-rational*)) ; Here we are dealing with (not (< arg1 arg2)) which is (<= arg2 arg1). We ; know arg1 is <= 0. We will thus deduce that arg2 is <= 0, and hence not a ; positive real, if we don't already know it. But the worry again arises ; that the intersection of arg2's known type and the complement of the ; positive-reals is empty. Suppose it were. Then arg2 is a strictly ; positive real. But if arg1 is a non-positive real or a non-number ; and arg2 is a positive real, then type-set-< knows that (< arg1 arg2) is ; true. Thus, this worry is again baseless. (extend-type-alist ;;*** -simple arg2 (ts-intersection ts2 (ts-complement #+:non-standard-analysis *ts-positive-real* #-:non-standard-analysis *ts-positive-rational*)) (cons-tag-trees ttree xttree) type-alist w)) ((and not-flg (ts-subsetp ts2 #+:non-standard-analysis *ts-positive-real* #-:non-standard-analysis *ts-positive-rational*) (ts-intersectp ts1 (ts-complement #+:non-standard-analysis (ts-union *ts-complex* *ts-positive-real*) #-:non-standard-analysis (ts-union *ts-complex-rational* *ts-positive-rational*)))) (extend-type-alist ;;*** -simple arg1 (ts-intersection ts1 #+:non-standard-analysis (ts-union *ts-complex* *ts-positive-real*) #-:non-standard-analysis (ts-union *ts-complex-rational* *ts-positive-rational*)) (cons-tag-trees ttree xttree) type-alist w)) ((and not-flg (ts-subsetp ts2 (ts-complement #+:non-standard-analysis (ts-union *ts-complex* *ts-negative-real*) #-:non-standard-analysis (ts-union *ts-complex-rational* *ts-negative-rational*))) (ts-intersectp ts1 #+:non-standard-analysis *ts-negative-real* #-:non-standard-analysis *ts-negative-rational*)) (extend-type-alist ;;*** -simple arg1 (ts-intersection ts1 (ts-complement #+:non-standard-analysis *ts-negative-real* #-:non-standard-analysis *ts-negative-rational*)) (cons-tag-trees ttree xttree) type-alist w)) (t type-alist))) (defun mv-atf-2 (not-flg true-type-alist false-type-alist new-term xnot-flg x shared-ttree xttree ignore) ; This function is a variation of mv-atf in which mbt, mbf, ttree1, ; and ttree2 are all known to be nil. The scenario is that there is ; an implicit term that we want to assume true or false, and we have ; generated two other terms x and new-term to assume true or false ; instead, each with its own parity (xnot-flg and not-flg, ; respectively). We want to avoid putting redundant information on ; the type-alist, which would happen if we are not careful in the case ; that x and new-term are the same term modulo their respective ; parities. ; The tag-tree shared-ttree justifies truth or falsity of new-term ; while xttree justifies truth or falsity of x. ; We assume that new-term is not a call of NOT. ; Ignore is :tta or :fta if we do not care about the value of true-type-alist ; or false-type-alist that is passed in (and may get passed out in the opposite ; position, due to not-flg). (let ((tta0 (and (not (eq ignore :tta)) (extend-type-alist-simple new-term *ts-t* shared-ttree true-type-alist))) (fta0 (and (not (eq ignore :fta)) (extend-type-alist-simple new-term *ts-nil* shared-ttree false-type-alist))) (same-parity (eq not-flg xnot-flg))) (cond ((equal new-term ; new-term is not a call of NOT, so we negate x (cond (same-parity x) (t (dumb-negate-lit x)))) (mv-atf not-flg nil nil tta0 fta0 nil nil)) (t (let ((tta1 (extend-type-alist-simple x (if same-parity *ts-t* *ts-nil*) xttree tta0)) (fta1 (extend-type-alist-simple x (if same-parity *ts-nil* *ts-t*) xttree fta0))) (mv-atf not-flg nil nil tta1 fta1 nil nil)))))) (defun binding-hyp-p (hyp alist wrld) ; Returns (mv forcep flg), where forcep is true if we have a call of force or ; case-split, in which case we consider the argument of that call for flg. Flg ; indicates whether we have a call (equiv var term), where var is a free ; variable with respect to alist (typically a unifying substitution, but we ; only look at the cars) that we want to bind. Starting with Version_2.9.4, we ; allow equiv to be an equivalence relation other than equal; however, to ; preserve existing proofs (in other words, to continue to allow kinds of ; equivalential reasoning done in the past), we only allow binding in the ; non-equal case when the right-hand side is a call of double-rewrite, which ; may well be what is desired anyhow. ; Starting with Version_2.7, we try all bindings of free variables. Moreover, ; in the case that there are free variables, we formerly first looked in the ; type-alist before considering the special case of (equal v term) where v is ; free and term has no free variables. Starting with Version_2.7 we avoid ; considerations of free variables when this special case arises, by handling ; it first. (let* ((forcep (and (nvariablep hyp) (not (fquotep hyp)) (or (eq (ffn-symb hyp) 'force) (eq (ffn-symb hyp) 'case-split)))) (hyp (if forcep (fargn hyp 1) hyp)) (eqp (equalityp hyp))) (mv forcep (cond (eqp (and (variablep (fargn hyp 1)) (not (assoc-eq (fargn hyp 1) alist)) (not (free-varsp (fargn hyp 2) alist)))) (t ; We want to minimize the cost of the checks below. In particular, we do the ; variablep check before the more expensive equivalence-relationp check (which ; can call getprop). (and (nvariablep hyp) (not (fquotep hyp)) (fargs hyp) (variablep (fargn hyp 1)) (equivalence-relationp (ffn-symb hyp) wrld) (let ((arg2 (fargn hyp 2))) (and (not (assoc-eq (fargn hyp 1) alist)) (nvariablep arg2) (not (fquotep arg2)) (eq (ffn-symb arg2) 'double-rewrite) (not (free-varsp arg2 alist)))))))))) (defmacro adjust-ignore-for-atf (not-flg ignore) ; Here, we rebind ignore to indicate which type-alist (tta or fta) is ; irrelevant for passing into a function that will swap them if and only if ; not-flg is true. `(cond ((and ,not-flg (eq ,ignore :fta)) :tta) ((and ,not-flg (eq ,ignore :tta)) :fta) (t ,ignore))) ; To decide if backchaining has gone on long enough we use: (defun backchain-limit-reachedp1 (n ancestors) (cond ((int= n 0) t) ((null ancestors) nil) (t (backchain-limit-reachedp1 (1- n) (cdr ancestors))))) (defun backchain-limit-reachedp (n ancestors) ; Here n is the backchain-limit currently in effect; n must be either nil ; or a nonnegative integer, the former case being treated as infinity. ; Ancestors is the ancestors argument to relieve-hyp. Its length is ; the number of times we have backchained already. We decide whether ; the backchain limit has been reached (or exceeded). This function ; is thus equivalent to (<= n (length ancestors)) for integers n. (and n (backchain-limit-reachedp1 n ancestors))) (defun new-backchain-limit (new-offset old-limit ancestors) ; We are getting ready to relieve one of the hypotheses of a rule. ; New-offset is the backchain-limit associated with that hypothesis, ; old-limit is the current backchain-limit, and the length of ; ancestors is how many times we have already backchained. (cond ((null new-offset) ; Since the hypothesis allows unlimited backchaining, we impose no ; new limits. old-limit) ((null old-limit) ; Since we had been allowing unlimited backchaining, any new ; restrictions come from new-offset (which is known to be a ; non-negative integer). Consider an example: ; ancestors is a list of length 3, and new-offset is 2. ; Here, we have backchained three times and wish to limit it ; to two more times, so we return 3 + 2 = 5. (+ (length ancestors) new-offset)) (t (min (+ (length ancestors) new-offset) old-limit)))) (defproxy oncep-tp (* *) => *) (defun oncep-tp-builtin (rune wrld) (declare (ignore rune wrld) (xargs :mode :logic :guard t)) nil) (defattach (oncep-tp oncep-tp-builtin) :skip-checks t) (mutual-recursion (defun type-set-rec (x force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) ; X is a term and type-alist is a type alist mapping terms to their type-sets ; (and some ttrees) and thus encoding the current assumptions. In a break with ; nqthm, the ACL2 type-set function tracks dependencies among the entries on ; the type-alist. In particular, the type-alist here contains pairs of the ; form (term ts . ttree), where ttree is a tag-tree generally containing 'PT ; tags. (There may be other tags, e.g., 'LEMMA and maybe even 'FC-DERIVATION ; during forward- chaining.) In nqthm, the type-alist contained pairs of the ; form (term . ts). The ttree argument to type-set is an accumulator onto ; which we add all of the ttrees attached to the facts we use from the ; type-alist and the wrld. We return two results, the final type set of term ; and a ttree. ; Note: If ancestors is t it means: don't backchain. Act as though ; the literal we're backchaining on is worse than everything in sight. ; This is called the ``t-ancestors hack'' and is commented upon below. ; Performance Notes: ; Type set was first made to track dependencies -- after a fashion -- ; during the coding of forward chaining in April, 1990. At that time, ; type-set had an option under which it would not track dependencies ; and that option was often used in rewrite; indeed, the only time ; dependencies were tracked was during setting up of the type-alist ; and forward chaining activations in simplify-clause. In addition, ; compound recognizer lemmas were never tracked. The paragraph below ; was written of that version of a ``dependency tracking'' type-set. ; Experiments show that (at the time of this writing) this type-set is ; roughly 30% slower than the type-set ACL2 had immediately before ; this addition. That data was obtained by collecting roughly 70,000 ; external calls of type-set that occurred during the proof that ; TAUTOLOGYP is sound and then timing their re-evaluation in both ; versions of ACL2 (with appropriate modifications of the type-alists ; being passed in). That same experiment led to the surprising fact ; that type set may represent 50-75% of the time spent in the prover! ; We have since added full dependency tracking to type-set and have ; adopted the invariants on type-alist requiring canonical forms. How ; much these changes slow things down is anyone's guess. Stay tuned. ; The DWP flag ; or ; Performance Notes on the "Type-Set Objective" Idea and Double Whammy ; "DWP" stands for "double whammy flag". It does not affect the ; soundness of the result returned but, when t, makes type-set "try ; harder" to get a narrow type. It was added Feb 7, 1995 and replaces ; a heuristic hack controlling the "double whammy" idea. The ; historical comment below explains. ; I have tried an experiment in which type-set gets the type of x in ; two ways and intersects them. The first way is to look x up on the ; type-alist. The second way is to compute it from scratch. Call ; this the "double whammy" approach. Double whammy is the simplest ; case of a more sophisticated type-set that computes a type for x ; relative to some specified "expected type." The idea of that design ; was to have the caller of type-set supply the type it wanted x to ; have and type-set then did a double whammy if the type-alist type ; wasn't a subset of the expected type. But that "conditional double ; whammy" idea is hard to implement. For example, suppose you want (- ; a) to have a type-set of non-negative rational. Then when you get ; the type-set of a you should want it to have type non-positive ; rational. Rather than implement that fine tuned use of the expected ; type-set, I decided simply to implement the double whammy method, ; i.e., always using both ways and intersecting the results. To my ; surprise, the double whammy method is 3 times slower than the ; ordinary type-set. That number was obtained by running nqthm.lisp, ; which ordinarily takes 3460 seconds but which takes 10300 seconds ; under double whammy. I then backed off the full blown double whammy ; and limited its use to the special case where (a) the type-set ; computed from the type-alist is negative and (b) forcing is allowed. ; Under these two cases, type-set is about 6% slower than without any ; double whammy processing. ; Why these two cases? Here is the rationale I had, for what it is ; worth. Condition (a) often indicates the type was "non-nil" or ; "anything but a negative", as when you assume (< 0 a) without ; knowing a is rational. Presumably, forcing was disallowed when this ; negative binding was created, since we probably have forced ; hypotheses around to ensure that the guards are met. Therefore, if ; condition (b) is also met, the chances are good that a "from ; scratch" computation will force now and we'll get a better answer. ; The hit rate even with these restrictions is quite unimpressive: ; double whammy changes the looked up type in only 1% of the cases ; where we try it! Of the roughly 4.4 million calls of type-set in ; the nqthm.lisp proofs, 39% of them (1.7 million) find a binding on ; the type-alist and therefore check conditions (a) and (b). In ; roughly 90% of the those cases, either the found type-set is ; negative or forcing is disallowed and so double whammy is not used. ; In only 176000 calls is double whammy even tried. That is only 4% ; of the total number of type-set calls. But of those 176000 attempts ; to use double whammy, in only 2254 cases did it help. That's a 1% ; hit rate on double whammy. Not really impressive. However, one of ; those hits is the case that Bishop reported we couldn't prove and ; now we can. That example is given below just for future reference. ;;; (progn (defstub foo (x) t) ;;; (defun bar-p (x) ;;; (consp x)) ;;; (in-theory (disable bar-p)) ;;; (defaxiom foo->=-0 ;;; (implies ;;; (force (bar-p x)) ;;; (and (integerp (foo x)) ;;; (>= (foo x) 0))) ;;; :rule-classes :type-prescription) ;;; (defaxiom foo-bound ;;; (implies ;;; (force (bar-p x)) ;;; (< (foo x) 2)) ;;; :rule-classes (:linear :rewrite))) ;;; (defthm test-foo-too ;;; (not (< 1 (foo '(1 . 1)))))|# ; The problem is that (foo '(1 . 1)) is given the type-set ; non-negative anything when we construct the type-alist for the ; linear pot, because we do not force the (bar-p '(1 . 1)). But ; later, when we are in linear we ask whether (foo '(1 . 1)) is an ; integer and, because of double whammy, force the previously unforced ; bar-p. ; Thus ends the historical comment. Below, where (not dwp) is tested, ; we used to test ; (or (null force-flg) ;;;(b) ; (>= (the-type-set ts0) 0)) ;;;(a) ; This heuristic control on when to double whammy is thwarted by the ; example below. Consider the clause shown below. It ought to be ; decidable by type-set alone because if i is an integerp then (binary-+ '1 i) ; is too. But the "irrelevant" hypothesis that (binary-+ '1 i) is a rationalp ; messes things up. Let 1+i stand for the binary-+ expression. ; (type-alist-clause '((not (rationalp (binary-+ '1 i))) ; lit 1 ; (not (integerp i)) ; lit 2 ; (integerp (binary-+ '1 i))) ; lit 3 ; nil nil nil (ens state) (w state)) ; We process lit 3 in a type-alist generated by assuming lits 1 and 2 ; false. In that type-alist, 1+i is assumed rational and i is assumed ; integral. When we process lit 3, we get the type of 1+i. Because of ; lit 1, we find it on the type-alist with rational type. Because the ; type is non-negative we do not double whammy. Thus, we miss the ; chance to use lit 2 in computing the type of 1+i. We thus return a ; type-alist in which 1+i is assumed to be a ratio (non-integer ; rational) and i is integral. ; However, by arranging for assume-true-false always to call type-set ; with dwp = t, we make it use double whammy when assuming things. ; That allows us to catch this. Our hope is that it does not slow us ; down too much. (mv-let (ts0 ttree0) (cond ((and (consp dwp) (eq (car dwp) :SKIP-LOOKUP)) ; This case arises from reconsider-type-alist. We found that by avoiding this ; lookup, we can avoid quadratic blow-up (distinct from the quadratic blow-up ; mentioned in a comment in reconsider-type-alist). With this change, we saw a ; decrease of 20.1% in time spent for an example from Dave Greve. (mv (cadr dwp) (cddr dwp))) (t (assoc-type-alist x type-alist w))) (cond ((and ts0 (or (not dwp) (and (consp dwp) (not (eq (car dwp) :SKIP-LOOKUP)) ; Dwp is a recognizer-tuple for some recognizer, fn; see the self-recursive ; call of type-set-rec below, just above the call of type-set-recognizer, for ; relevant explanation. We are satisfied if this record decides whether an ; object of type ts0 definitely does, or does not, satisfy fn. Objects whose ; type is disjoint from that of :true-ts must falsify fn, while those whose ; type is disjoint from that of :false-ts must satisfy fn; see ; recognizer-tuple. (or (ts-disjointp ts0 (access recognizer-tuple dwp :true-ts)) (ts-disjointp ts0 (access recognizer-tuple dwp :false-ts)))))) (mv ts0 (cons-tag-trees ttree ttree0))) ((variablep x) ; Warning: You may be tempted to change ttree below to nil on the ; grounds that we are not using any information about x to say its ; type is unknown. But the specification for type-set is that ttree ; is an accumulator. Our caller may have put a lot of work into ; deriving x and passed the associated ttree to type-set in good ; faith. We are obliged to pass it on. (type-set-finish x ts0 ttree0 *ts-unknown* ttree type-alist)) ((fquotep x) (type-set-finish x ts0 ttree0 (type-set-quote (cadr x)) ttree type-alist)) ((flambda-applicationp x) ; Once upon a time, we tried to do this by using subcor-var to replace ; the actuals by the formals and then take the type-set of the body. ; The old code is shown, commented out, below. But that was bad ; because it duplicated the actuals so often. We now take the ; type-set of the body under a type-alist obtained from the actuals. ; We have to be careful to avoid forcing and use of the pot-lst. (mv-let (ts1 ttree1) (type-set-rec (lambda-body (ffn-symb x)) nil ; avoid forcing in lambda-body context nil ; dwp (zip-variable-type-alist (lambda-formals (ffn-symb x)) (type-set-lst (fargs x) force-flg nil ; dwp type-alist ancestors ens w pot-lst pt backchain-limit)) ; Here is the motivation of the t-ancestors hack. We cannot compare subterms ; of the lambda body to terms on the current ancestors because the lambda ; body should be instantiated with the actuals and it is not. For example ; we might know (p x) on ancestors and go into ((lambda (x) (p x)) a) and ; mistakenly think the body is true because it is on ancestors. (Ancestors ; is not used that way, but the point is made.) So when ancestors is ; set to t it means ignore ancestors and don't backchain. The type-set ; clique is the only nest of functions that treats ancestors that way. ; Here is the one place we initiate the t-ancestors hack. t ;;; t-ancestors hack ens w ttree ; The pot-lst is not valid in the current context, because the body is not ; instantiated with the actuals. So we replace it with nil. nil pt backchain-limit) (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist))) ; ((flambda-applicationp x) ; Note: Once upon a time, assumptions only recorded the term forced and not the ; type-alist involved. We could get away with that because rewrite-atm would ; recover all the assumptions and force a case split on the whole clause to ; handle them. During those simple days, we treated lambda expressions ; efficiently here, by computing the type-set of the lambda-body under a ; type-alist binding the lambda-formals to the types of the actuals. ; Afterwards, we swept the ttree and appropriately instantiated the forced ; terms with the actuals. But when we introduced assumption records, with the ; type-alists recorded in them, this design became problematic: we would have ; had to instantiate the :type-alists too. Rather than do so, we abandoned ; efficiency here and did the obvious: we expanded lambda applications ; by substituting actuals for formals in the body and computed the type-set ; of the result. This survived until Version 2.6. ;;; (mv-let (ts1 ttree1) ;;; (type-set-rec (subcor-var (lambda-formals (ffn-symb x)) ;;; (fargs x) ;;; (lambda-body (ffn-symb x))) ;;; force-flg ;;; nil ; dwp ;;; type-alist ;;; ancestors ;;; ens w ttree pot-lst pt backchain-limit) ;;; (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist)) ; When we introduced the nu-rewriter, we made clausify no longer ; expand lambdas so aggressively. This meant that type-set began to ; see lot more lambdas. In that environment, the expansion of lambdas ; here was taking lot of time and generating a lot of conses. So now ; we take the efficient AND braindead approach of saying we simply ; don't know anything about a lambda application. ; (type-set-finish x ts0 ttree0 *ts-unknown* ttree type-alist)) ((eq (ffn-symb x) 'not) (mv-let (ts1 ttree1) (type-set-rec (fargn x 1) force-flg nil ; dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (mv-let (ts1 ttree1) (type-set-not ts1 ttree1 ttree) (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist)))) (t (let* ((fn (ffn-symb x)) (recog-tuple (most-recent-enabled-recog-tuple fn (global-val 'recognizer-alist w) ens)) (dwp (if (and (consp dwp) (eq (car dwp) :SKIP-LOOKUP)) nil dwp))) (cond (recog-tuple (mv-let (ts1 ttree1) (type-set-rec (fargn x 1) force-flg (and (or force-flg dwp) ; The example below, essentially from Jared Davis, motivates our passing a ; non-nil value of dwp here. ; (progn ; (defstub foo-p (x) t) ; (defstub bar-p (x) t) ; (defstub foo->field (x) t) ; (defaxiom foo-p-type ; (booleanp (foo-p x)) ; :rule-classes :type-prescription) ; (defaxiom bar-p-type ; (booleanp (bar-p x)) ; :rule-classes :type-prescription) ; (defaxiom foo->field-type ; (implies (force (foo-p x)) ; (or (stringp (foo->field x)) ; (not (foo->field x)))) ; :rule-classes :type-prescription) ; (defaxiom bar-p-when-stringp ; (implies (stringp x) ; (bar-p x)))) ; We then would like to see a forcing round for the following. But we don't ; get that if we use dwp=nil, as was done through Version_4.1. ; (thm (implies (foo->field x) ; (bar-p (foo->field x)))) ; But we do not pass recog-tuple in every case -- see (or force-flg dwp) above ; -- because that causes significant slowdown. Here are some statistics on the ; regression suite, using a development version of ACL2 in early November, ; 2010, running with 'make' option "-j 4" on a 2-core multi-threaded MacBook ; Pro i7. ; Before any change: ; real 83m26.179s ; user 276m28.269s ; sys 17m16.883s ; New version: ; real 83m11.087s ; user 277m13.043s ; sys 17m21.616s ; Using dwp=t here, with Version_4.1 code just under assoc-type-alist near the ; top of this function: ; real 104m24.803s ; user 303m7.784s ; sys 17m59.079s ; Using dwp=recog-tuple here, with the same new code just under ; assoc-type-alist near the top of this function, but without conditionalizing ; on (or force-flg dwp). (Did two timings in case the machine went to sleep ; during the first.) ; real 103m17.474s / 102m48.885s ; user 299m40.781s / 300m32.856s ; sys 18m5.536s / 18m6.812s ; Finally, we remark on why we conditionalize above on the disjunction (or ; force-flg dwp). A non-nil value of dwp seems an appropriate reason to ; propagate a double-whammy heuristic to the argument of a recognizer. But ; that alone doesn't suffice for Jared's example; however, force-flg does. The ; dwp and force-flg parameters seem like the obvious conditions for enabling ; the use of recog-tuple for dwp, and proofs don't noticeably slow down when ; using their disjunction; so that is what we do. recog-tuple) type-alist ancestors ens w ttree pot-lst pt backchain-limit) (mv-let (ts1 ttree1) (type-set-recognizer recog-tuple ts1 ttree1 ttree) (mv-let (ts ttree) (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist) ; At this point, ts is the intersection of the type-alist information and the ; recog information. Unless ts is t or nil we also try any type-prescription ; rules we have about this function symbol. (cond ((or (ts= ts *ts-t*) (ts= ts *ts-nil*)) (mv ts ttree)) (t ; WARNING: There is another call of type-set-with-rules below, in the normal ; case. This call is for the recognizer function case. If you change this ; one, change that one! (mv-let (ts2 ttree2) (type-set-with-rules (getprop fn 'type-prescriptions nil 'current-acl2-world w) x force-flg dwp ; see comment in rewrite-atm about "use of dwp" type-alist ancestors ens w *ts-unknown* ttree pot-lst pt backchain-limit) (mv (ts-intersection ts ts2) ttree2)))))))) ((eq fn 'if) ; It is possible that the if-expression x is on the type-alist. It ; would get there if we had gone through an earlier assume-true-false ; on x, i.e., if we were processing b in (if (if x1 x2 x3) b c). So ; we will compute the type-set of x directly based on its structure ; and then finish as usual by anding it with ts0, the type-set of x ; itself as recorded on the type-alist, as appropriate. (mv-let (ts1 ttree1) (mv-let (must-be-true must-be-false true-type-alist false-type-alist ttree1) (assume-true-false-rec (fargn x 1) nil force-flg nil ; dwp type-alist ancestors ens w pot-lst pt nil backchain-limit) ; If must-be-true or must-be-false is set, then ttree1 explains the ; derivation of that result. If neither is derived, then ttree1 is ; nil and we can ignore it. (cond (must-be-true (type-set-rec (fargn x 2) force-flg nil ; dwp true-type-alist ancestors ens w (cons-tag-trees ttree1 ttree) pot-lst pt backchain-limit)) (must-be-false (type-set-rec (fargn x 3) force-flg nil ; dwp false-type-alist ancestors ens w (cons-tag-trees ttree1 ttree) pot-lst pt backchain-limit)) (t (mv-let (ts1 ttree) (type-set-rec (fargn x 2) force-flg nil ; dwp true-type-alist ancestors ens w ttree pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn x 3) force-flg nil ; dwp false-type-alist ancestors ens w ttree pot-lst pt backchain-limit) (mv (ts-union ts1 ts2) ttree)))))) (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist))) ((member-eq fn *expandable-boot-strap-non-rec-fns*) ; For these particular functions we actually substitute the actuals ; for the formals into the guarded body and dive into the body to get ; the answer. Typically we will not ever encounter these functions in ; proofs because they will have been expanded away. However, we will ; encounter them during the early type-prescription work and ; pre-verify-guard work, and so think it is worthwhile to handle them. (mv-let (ts1 ttree1) (type-set-rec (subcor-var (formals fn w) (fargs x) (body fn t w)) force-flg nil ; dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist))) (t ; Otherwise, we apply all known type-prescriptions and conclude with ; whatever is built in about fn. ; Note: We do not know that 'type-prescriptions is non-nil. Once upon ; a time we insisted that every fn have a type-prescription. This ; complicated the defun principle because one might encounter the ; unadmitted function symbol in the termination proofs (e.g., ; mc-flatten). So now we are lenient. This happens to be what Nqthm ; does too, and now we know why! ; WARNING: There is another call of type-set-with-rules above, in the ; recog-tuple case. If you change this one, change that one! (mv-let (ts1 ttree1) (type-set-with-rules (getprop fn 'type-prescriptions nil 'current-acl2-world w) x force-flg dwp ; see comment in rewrite-atm about "use of dwp" type-alist ancestors ens w *ts-unknown* ttree pot-lst pt backchain-limit) (type-set-finish x ts0 ttree0 ts1 ttree1 type-alist))))))))) (defun type-set-lst (x force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit) ; This function computes the type-set of each element of x, obtaining for each ; a ts and a ttree. It conses the two together and makes a list of those pairs ; in 1:1 correspondence with x. That is, if x is (x1 ... xn) then the answer, ; ans, is ((ts1 . ttree1) ... (tsn . ttreen)). (Strip-cars ans) will return a ; list of the type sets and (strip-cdrs ans) will return a list of the ttrees. ; Furthermore, if x is a list of variables, (zip-variable-type-alist x ans) ; will return a type-alist! (cond ((null x) nil) (t (mv-let (ts ttree) (type-set-rec (car x) force-flg dwp type-alist ancestors ens w nil pot-lst pt backchain-limit) (cons (cons ts ttree) (type-set-lst (cdr x) force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit)))))) (defun type-set-relieve-hyps-free (term typ rest-type-alist rune target hyps backchain-limit-lst force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr+1) (assert$ rest-type-alist (mv-let (lookup-hyp-ans alist+ ttree rest-type-alist) (search-type-alist-with-rest term typ rest-type-alist alist ttree wrld) (cond ((null lookup-hyp-ans) (mv nil type-alist ttree0)) ((null rest-type-alist) ; optimization (prefer this tail call) (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist+ type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr+1)) (t ; lookup-hyp-ans and non-nil new value of rest-type-alist (mv-let (relieve-hyps-ans type-alist ttree) (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist+ type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr+1) (cond (relieve-hyps-ans (mv relieve-hyps-ans type-alist ttree)) (t (type-set-relieve-hyps-free term typ rest-type-alist rune target hyps backchain-limit-lst force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr+1))))))))) (defun type-set-relieve-hyps1 (hyp forcep rune target hyps backchain-limit-lst force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr) (mv-let (not-flg atm) (strip-not hyp) (mv-let (atm1 quotep-atm1 ttree) (sublis-var! alist atm ens wrld ttree) ; Note that we stripped off the force (or case-split) symbol if it was ; present. We don't have to do that (given that the type-set of ; (force x) and of (case-split x) is known to be that of x) but it ought ; to speed things up slightly. Note that we also stripped off the NOT ; if it was present and have not-flg and the instantiated atom, atm1, ; to work on. We work on the atom so that we can record its type-set ; in the final type-alist, rather than recording the type-set of (NOT ; atm1). (cond (quotep-atm1 (cond ((eq not-flg (equal atm1 *nil*)) (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0)))) (t (mv-let (on-ancestorsp assumed-true) ; Here is the one place where we (potentially) use the t-ancestors ; hack to abort early. (if (eq ancestors t) (mv t nil) (ancestors-check (if not-flg (mcons-term* 'not atm1) atm1) ancestors (list rune))) (cond (on-ancestorsp (cond (assumed-true (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0)))) ((backchain-limit-reachedp backchain-limit ancestors) (mv-let (force-flg ttree) (cond ((not (and force-flg forcep)) (mv nil ttree)) (t (force-assumption rune target (if not-flg (mcons-term* 'not atm1) atm1) type-alist nil (immediate-forcep (ffn-symb (car hyps)) ens) force-flg ttree))) (cond (force-flg (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0))))) (t (mv-let (flg type-alist ttree2) (with-accumulated-persistence rune (flg type-alist ttree2) flg (mv-let (ts1 ttree1) (type-set-rec atm1 force-flg dwp type-alist ; We know ancestors is not t here, by the tests above. (push-ancestor (if not-flg atm1 (mcons-term* 'not atm1)) (list rune) ancestors) ens wrld ttree pot-lst pt (new-backchain-limit (car backchain-limit-lst) backchain-limit ancestors)) (let ((ts (if not-flg (cond ((ts= ts1 *ts-nil*) *ts-t*) ((ts-intersectp ts1 *ts-nil*) *ts-boolean*) (t *ts-nil*)) ts1))) (cond ((ts= ts *ts-nil*) (mv nil type-alist ttree0)) ((ts-intersectp *ts-nil* ts) (mv-let (force-flg ttree) (cond ((not (and force-flg forcep)) (mv nil ttree)) (t (force-assumption rune target (if not-flg (mcons-term* 'not atm1) atm1) type-alist nil (immediate-forcep (ffn-symb (car hyps)) ens) force-flg ttree))) (cond (force-flg (mv t type-alist ttree)) (t (mv nil type-alist ttree0))))) (t (mv t type-alist ttree1))))) bkptr) (cond (flg (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree2 ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree2)))))))))))) (defun type-set-relieve-hyps (rune target hyps backchain-limit-lst force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr) ; Hyps is a list of terms, implicitly conjoined. Alist is a substitution ; mapping variables in hyps to terms governed by type-alist. Consider the ; result, hyps', of substituting alist into each hyp in hyps. We wish to know ; whether, by type-set reasoning alone, we can get that hyps' are all true in ; the context of type-alist. We do the substitution one hyp at a time, so we ; don't pay the price of consing up instances beyond the first hyp that fails. ; While we are at it, we record in an extension of type-alist the type computed ; for each hyp', so that if subsequent rules need that information, they can ; get it quickly. ; No Change Loser for ttree, but not for type-alist. (cond ((null hyps) (mv t type-alist (cons-tag-trees ttree ttree0))) (t (mv-let (forcep bind-flg) (binding-hyp-p (car hyps) alist wrld) (let ((hyp (if forcep (fargn (car hyps) 1) (car hyps)))) (cond (bind-flg (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp (cons (cons (fargn hyp 1) (fargn hyp 2)) alist) type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv-let (term typ) (term-and-typ-to-lookup hyp wrld) (mv-let (lookup-hyp-ans alist+ ttree rest-type-alist) (search-type-alist-with-rest term typ type-alist alist ttree wrld) (cond (lookup-hyp-ans (cond ((and rest-type-alist (not (eq (caar alist) (caar alist+))) ; free vars (not (oncep-tp rune wrld))) (let ((bkptr+1 (1+ bkptr))) (mv-let (relieve-hyps-ans type-alist ttree) (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist+ type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr+1) (cond (relieve-hyps-ans (mv relieve-hyps-ans type-alist ttree)) (t (type-set-relieve-hyps-free term typ rest-type-alist rune target hyps backchain-limit-lst force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit bkptr+1)))))) (t (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist+ type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))))) ((free-varsp hyp alist) (let ((fully-bound-alist (if (and forcep force-flg) (bind-free-vars-to-unbound-free-vars (all-vars hyp) alist) alist))) ; Fully-bound-alist is an extension of alist in which all vars occurring ; in hyp are bound to something. A var v that occurs freely in hyp wrt ; alist is bound in fully-bound-alist to UNBOUND-FREE-v. But we only ; compute the all-vars and fully-bound-alist if we're going to use it ; below. For sanity, fully-bound-alist is just alist if we're not ; actually intending to use it. (mv-let (force-flg ttree) (cond ((not (and forcep force-flg)) (mv nil ttree)) (t (force-assumption rune target (sublis-var fully-bound-alist hyp) type-alist nil (immediate-forcep (ffn-symb (car hyps)) ens) force-flg ttree))) ; Note that force-flg has been rebound by the mv-let above. Think of ; it as just a temporary variable meaning (and forcep force-flg) and ; we use it only if it is true. (cond (force-flg (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp fully-bound-alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0)))))) (t (mv-let (not-flg atm) (strip-not hyp) (mv-let (atm1 quotep-atm1 ttree) (sublis-var! alist atm ens wrld ttree) ; Note that we stripped off the force (or case-split) symbol if it was ; present. We don't have to do that (given that the type-set of ; (force x) and of (case-split x) is known to be that of x) but it ought ; to speed things up slightly. Note that we also stripped off the NOT ; if it was present and have not-flg and the instantiated atom, atm1, ; to work on. We work on the atom so that we can record its type-set ; in the final type-alist, rather than recording the type-set of (NOT ; atm1). (cond (quotep-atm1 (cond ((eq not-flg (equal atm1 *nil*)) (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0)))) (t (mv-let (on-ancestorsp assumed-true) ; Here is the one place where we (potentially) use the t-ancestors ; hack to abort early. (if (eq ancestors t) (mv t nil) (ancestors-check (if not-flg (mcons-term* 'not atm1) atm1) ancestors (list rune))) (cond (on-ancestorsp (cond (assumed-true (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0)))) ((backchain-limit-reachedp backchain-limit ancestors) (mv-let (force-flg ttree) (cond ((not (and force-flg forcep)) (mv nil ttree)) (t (force-assumption rune target (if not-flg (mcons-term* 'not atm1) atm1) type-alist nil (immediate-forcep (ffn-symb (car hyps)) ens) force-flg ttree))) (cond (force-flg (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree0))))) (t (mv-let (flg type-alist ttree2) (with-accumulated-persistence rune (flg type-alist ttree2) flg (mv-let (ts1 ttree1) (type-set-rec atm1 force-flg dwp type-alist ; We know ancestors is not t here, by the tests above. (push-ancestor (if not-flg atm1 (mcons-term* 'not atm1)) (list rune) ancestors) ens wrld ttree pot-lst pt (new-backchain-limit (car backchain-limit-lst) backchain-limit ancestors)) (let ((ts (if not-flg (cond ((ts= ts1 *ts-nil*) *ts-t*) ((ts-intersectp ts1 *ts-nil*) *ts-boolean*) (t *ts-nil*)) ts1))) (cond ((ts= ts *ts-nil*) (mv nil type-alist ttree0)) ((ts-intersectp *ts-nil* ts) (mv-let (force-flg ttree) (cond ((not (and force-flg forcep)) (mv nil ttree)) (t (force-assumption rune target (if not-flg (mcons-term* 'not atm1) atm1) type-alist nil (immediate-forcep (ffn-symb (car hyps)) ens) force-flg ttree))) (cond (force-flg (mv t type-alist ttree)) (t (mv nil type-alist ttree0))))) (t (mv t type-alist ttree1))))) bkptr) (cond (flg (type-set-relieve-hyps rune target (cdr hyps) (cdr backchain-limit-lst) force-flg dwp alist type-alist ancestors ens wrld ttree2 ttree0 pot-lst pt backchain-limit (1+ bkptr))) (t (mv nil type-alist ttree2)))))))))))))))))))))) (defun extend-type-alist-with-bindings (alist force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) ; Alist is an alist that pairs variables in some rule with terms. We compute ; the type-set of each term in the range of alist and extend type-alist with ; new entries that pair each term to its type-set. (cond ((null alist) type-alist) (t (extend-type-alist-with-bindings (cdr alist) force-flg dwp (cond ((assoc-equal (cdr (car alist)) type-alist) type-alist) (t (mv-let (ts ttree1) (type-set-rec (cdr (car alist)) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (extend-type-alist ;;*** -simple (cdr (car alist)) ts ttree1 type-alist w)))) ancestors ens w ttree pot-lst pt backchain-limit)))) (defun type-set-with-rule (tp term force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) ; We apply the type-prescription, tp, to term, if possible, and return a ; type-set, an extended type-alist and a ttree. If the rule is inapplicable, ; the type-set is *ts-unknown* and the ttree is unchanged. Recall that ; type-set treats its ttree argument as an accumulator and we are obliged to ; return an extension of the input ttree. ; Note that the specification of this function is that if we were to take the ; resulting type-alist and cons the input ttree on to each ttree in that ; type-alist, the resulting type-alist would be "appropriate". In particular, ; we don't put ttree into the type-alist, but instead we assume that our caller ; will compensate appropriately. (declare (ignore dwp)) (cond ((enabled-numep (access type-prescription tp :nume) ens) (mv-let (unify-ans unify-subst) (one-way-unify (access type-prescription tp :term) term) (cond (unify-ans (with-accumulated-persistence (access type-prescription tp :rune) (ts type-alist-out ttree-out) (not (ts= *ts-unknown* ts)) (let* ((hyps (access type-prescription tp :hyps)) (type-alist (cond ((null hyps) type-alist) (t (extend-type-alist-with-bindings unify-subst force-flg nil ; dwp type-alist ancestors ens w ; We lie here by passing in the nil tag-tree, so that we can avoid ; contaminating the resulting type-alist with a copy of ttree. We'll make sure ; that ttree gets into the answer returned by type-alist-with-rules, which is ; the only function that calls type-set-with-rule. nil pot-lst pt backchain-limit))))) (mv-let (relieve-hyps-ans type-alist ttree) (type-set-relieve-hyps (access type-prescription tp :rune) term hyps (access type-prescription tp :backchain-limit-lst) force-flg nil ; dwp unify-subst type-alist ancestors ens w ; We pass in nil here to avoid contaminating the type-alist returned by this ; call of type-set-relieve-hyps. nil ttree pot-lst pt backchain-limit 1) (cond (relieve-hyps-ans (with-accumulated-persistence (access type-prescription tp :rune) (ts type-alist ttree) (ts= ts *ts-unknown*) (type-set-with-rule1 unify-subst (access type-prescription tp :vars) force-flg nil ; dwp type-alist ancestors ens w (access type-prescription tp :basic-ts) (push-lemma (access type-prescription tp :rune) ttree) pot-lst pt backchain-limit) :conc hyps)) (t (mv *ts-unknown* type-alist ttree))))))) (t (mv *ts-unknown* type-alist ttree))))) (t (mv *ts-unknown* type-alist ttree)))) (defun type-set-with-rule1 (alist vars force-flg dwp type-alist ancestors ens w basic-ts ttree pot-lst pt backchain-limit) ; Alist is an alist that maps variables to terms. The terms are in the context ; described by type-alist. Vars is a list of variables. We map over the pairs ; in alist unioning into basic-ts the type-sets of those terms whose ; corresponding vars are in vars. We accumulate the ttrees into ttree and ; ultimately return the final basic-ts, type-alist and ttree. The alist ; contains successive formals paired with actuals. ; We are about to return from type-set-with-rule with the type-set of ; a term, say (foo x), as indicated by a :type-prescription rule, say ; ts-foo, but we are not quite done yet. On the initial entry to this ; function, basic-ts and vars are from the corresponding fields of ; ts-foo. Vars is a (possibly empty) subset of the variables in ; ts-foo. The meaning of ts-foo is that the type-set of (foo x) is ; the union of basic-ts and the types of (the terms bound to) vars. ; See the definition of a type-prescription rule and surrounding ; discussion. (Search for ``defrec type-prescription'' in this file.) (cond ((null alist) (mv basic-ts type-alist ttree)) ((member-eq (caar alist) vars) (mv-let (ts ttree) (type-set-rec (cdar alist) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-with-rule1 (cdr alist) vars force-flg dwp type-alist ancestors ens w (ts-union ts basic-ts) ttree pot-lst pt backchain-limit))) (t (type-set-with-rule1 (cdr alist) vars force-flg dwp type-alist ancestors ens w basic-ts ttree pot-lst pt backchain-limit)))) (defun type-set-with-rules (tp-lst term force-flg dwp type-alist ancestors ens w ts ttree pot-lst pt backchain-limit) ; We try to apply each type-prescription in tp-lst, intersecting ; together all the type sets we get and accumulating all the ttrees. ; However, if a rule fails to change the accumulating type-set, we ; ignore its ttree. (cond ((null tp-lst) (mv-let (ts1 ttree1) (type-set-primitive term force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (let ((ts2 (ts-intersection ts1 ts))) (mv ts2 (if (ts= ts2 ts) ttree ttree1))))) ((ts-subsetp ts (access type-prescription (car tp-lst) :basic-ts)) ; Our goal is to make the final type-set, ts, as small as possible by ; intersecting it with the type-sets returned to the various rules. If ts is ; already smaller than or equal to the :basic-ts of a rule, there is no point ; in trying that rule: the returned type-set will be at least as large as ; :basic-ts (it has the :vars types unioned into it) and then when we intersect ; ts with it we'll just get ts back. The original motivation for this ; short-cut was to prevent the waste of time caused by the ; pre-guard-verification type-prescription if the post-guard-verification rule ; is present. (type-set-with-rules (cdr tp-lst) term force-flg dwp type-alist ancestors ens w ts ttree pot-lst pt backchain-limit)) (t (mv-let (ts1 type-alist1 ttree1) (type-set-with-rule (car tp-lst) term force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (let ((ts2 (ts-intersection ts1 ts))) (type-set-with-rules (cdr tp-lst) term force-flg dwp type-alist1 ancestors ens w ts2 (if (and (ts= ts2 ts) (equal type-alist type-alist1)) ttree ttree1) pot-lst pt backchain-limit)))))) ;; RAG - I added an entry for floor1, which is the only primitive ;; non-recognizer function we added for the reals. [Ruben added entries for ;; some other non-standard primitives too.] (defun type-set-primitive (term force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) ; Note that we call our initial ttree ttree0 and we extend it below to ttree as ; we get the types of the necessary arguments. This function should handle ; every non-recognizer function handled in *primitive-formals-and-guards*, ; ev-fncall, and cons-term1, though like cons-term1, we also handle NOT. ; Exception: Since code-char is so simple type-theoretically, we handle its ; type set computation with rule code-char-type in axioms.lisp. It is ; perfectly acceptable to handle function symbols here that are not handled by ; the functions above. For example, we compute a type-set for length in a ; special manner below, but cons-term1 and the others do not know about ; length. (case (ffn-symb term) (cons (mv-let (ts2 ttree) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-cons ts2 ttree ttree0))) (equal (cond ((equal (fargn term 1) (fargn term 2)) (mv *ts-t* ttree0)) (t (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-equal ts1 ts2 ttree ttree0)))))) (unary-- (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-unary-- ts1 ttree ttree0))) (unary-/ (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-unary-/ ts1 ttree ttree0))) #+:non-standard-analysis (floor1 (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-floor1 ts1 ttree ttree0))) (denominator (mv *ts-positive-integer* (puffert ttree0))) (numerator (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-numerator ts1 ttree ttree0))) #+:non-standard-analysis (standardp (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-standardp ts1 ttree ttree0))) #+:non-standard-analysis (standard-part (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-standard-part ts1 ttree ttree0))) #+:non-standard-analysis (i-large-integer (mv *ts-positive-integer* (puffert ttree0))) (car (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-car ts1 ttree ttree0))) (cdr (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-cdr ts1 ttree ttree0))) (symbol-name (mv *ts-string* (puffert ttree0))) (symbol-package-name (mv *ts-string* (puffert ttree0))) (intern-in-package-of-symbol (mv-let (ts1 ttree1) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree2) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) ; Note that ttree1 and ttree2 both have ttree0 in them, but ttree2 does not ; have ttree1 in it! (type-set-intern-in-package-of-symbol ts1 ts2 ttree1 ttree2 ttree0)))) (pkg-witness (mv *ts-symbol* (puffert ttree0))) (coerce (mv-let (ts1 ttree1) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree2) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) ; Note that ttree1 and ttree2 both have ttree0 in them, but ttree2 does not ; have ttree1 in it! (type-set-coerce term ts1 ts2 ttree1 ttree2 ttree0)))) (length (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-length ts1 ttree ttree0))) (binary-+ (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-binary-+ term ts1 ts2 ttree ttree0)))) (binary-* (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-binary-* ts1 ts2 ttree ttree0)))) (< (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-< (fargn term 1) (fargn term 2) ts1 ts2 type-alist ttree ttree0 pot-lst pt)))) (not (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-not ts1 ttree ttree0))) (realpart (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-realpart ts1 ttree ttree0))) (imagpart (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-imagpart ts1 ttree ttree0))) (complex (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn term 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (type-set-complex ts1 ts2 ttree ttree0)))) (char-code (mv-let (ts1 ttree) (type-set-rec (fargn term 1) force-flg dwp type-alist ancestors ens w ttree0 pot-lst pt backchain-limit) (type-set-char-code ts1 ttree ttree0))) (otherwise (mv *ts-unknown* ttree0)))) ; Mini-Essay on Assume-true-false-if and Implies ; or ; How Strengthening One Part of a Theorem Prover Can Weaken the Whole. ; Normally, when ACL2 rewrites one of the branches of an IF expression, ; it ``knows'' the truth or falsity (as appropriate) of the test. More ; precisely, if x is a term of the form (IF test true-branch false-branch), ; when ACL2 rewrites true-branch, it can determine that test is true by ; type reasoning alone, and when it rewrites false-branch it can determine ; that test is false by type reasoning alone. This is certainly what one ; would expect. ; However, previously, if test was such that it would print as (AND ; test1 test2) -- so that x would print as (IF (AND test1 test2) ; true-branch false-branch) -- when ACL2 rewrote true-branch it only ; knew that (AND test1 test2) was true --- it did not know that test1 ; was true nor that test2 was true, but only that their conjunction ; was true. There were also other situations in which ACL2's ; reasoning was weak about the test of an IF expression. ; The function assume-true-false-if was written to correct this problem ; but it caused other problems of its own. This mini-essay records ; one of the difficulties encountered and its solution. ; In initial tests with the new assume-true-false-if, more than three- ; fourths of the community books failed to certify. Upon ; examination it turned out that ACL2 was throwing away many of the ; :use hints as well as some of the results from generalization ; rules. Let us look at a particular example (from inequalities.lisp ; in the arithmetic library): ;;; (defthm <-*-right-cancel ;;; (implies (and (rationalp x) ;;; (rationalp y) ;;; (rationalp z)) ;;; (iff (< (* x z) (* y z)) ;;; (cond ;;; ((< 0 z) ;;; (< x y)) ;;; ((equal z 0) ;;; nil) ;;; (t (< y x))))) ;;; :hints (("Goal" :use ;;; ((:instance (:theorem ;;; (implies (and (rationalp a) ;;; (< 0 a) ;;; (rationalp b) ;;; (< 0 b)) ;;; (< 0 (* a b)))) ;;; (a (abs (- y x))) ;;; (b (abs z))))))) ; This yields the subgoal: ;;; (IMPLIES (IMPLIES (AND (RATIONALP (ABS (+ Y (- X)))) ;;; (< 0 (ABS (+ Y (- X)))) ;;; (RATIONALP (ABS Z)) ;;; (< 0 (ABS Z))) ;;; (< 0 (* (ABS (+ Y (- X))) (ABS Z)))) ;;; (IMPLIES (AND (RATIONALP X) ;;; (RATIONALP Y) ;;; (RATIONALP Z)) ;;; (IFF (< (* X Z) (* Y Z)) ;;; (COND ((< 0 Z) (< X Y)) ;;; ((EQUAL Z 0) NIL) ;;; (T (< Y X)))))) ; Previously, the preprocess-clause ledge of the waterfall would ; see this as ;;; ((NOT (IMPLIES (IF (RATIONALP (ABS (BINARY-+ Y (UNARY-- X)))) ;;; (IF (< '0 (ABS (BINARY-+ Y (UNARY-- X)))) ;;; (IF (RATIONALP (ABS Z)) ;;; (< '0 (ABS Z)) ;;; 'NIL) ;;; 'NIL) ;;; 'NIL) ;;; (< '0 ;;; (BINARY-* (ABS (BINARY-+ Y (UNARY-- X))) ;;; (ABS Z))))) ;;; (IMPLIES (IF (RATIONALP X) ;;; (IF (RATIONALP Y) (RATIONALP Z) 'NIL) ;;; 'NIL) ;;; (IFF (< (BINARY-* X Z) (BINARY-* Y Z)) ;;; (IF (< '0 Z) ;;; (< X Y) ;;; (IF (EQUAL Z '0) 'NIL (< Y X)))))) ; and return ;;; (((NOT (IF (IF (RATIONALP (ABS (BINARY-+ Y (UNARY-- X)))) ;;; (IF (< '0 (ABS (BINARY-+ Y (UNARY-- X)))) ;;; (IF (RATIONALP (ABS Z)) ;;; (< '0 (ABS Z)) ;;; 'NIL) ;;; 'NIL) ;;; 'NIL) ;;; (IF (< '0 ;;; (BINARY-* (ABS (BINARY-+ Y (UNARY-- X))) ;;; (ABS Z))) ;;; 'T ;;; 'NIL) ;;; 'T)) ;;; (NOT (RATIONALP X)) ;;; (NOT (RATIONALP Y)) ;;; (NOT (RATIONALP Z)) ;;; (IF (< (BINARY-* X Z) (BINARY-* Y Z)) ;;; (IF (IF (< '0 Z) ;;; (< X Y) ;;; (IF (EQUAL Z '0) 'NIL (< Y X))) ;;; 'T ;;; 'NIL) ;;; (IF (IF (< '0 Z) ;;; (< X Y) ;;; (IF (EQUAL Z '0) 'NIL (< Y X))) ;;; 'NIL ;;; 'T)))) ; Now, under the old regime, when rewrite got hold of the conclusion ; of the :use hint, ; (< '0 ; (BINARY-* (ABS (BINARY-+ Y (UNARY-- X))) ; (ABS Z))) ; it would would know that X, Y, and Z were rational and that the IF ; expression ; (IF (RATIONALP (ABS (BINARY-+ Y (UNARY-- X)))) ; (IF (< '0 (ABS (BINARY-+ Y (UNARY-- X)))) ; (IF (RATIONALP (ABS Z)) ; (< '0 (ABS Z)) ; 'NIL) ; 'NIL) ; 'NIL) ; was true. But it would not know, for instance, that ; (< '0 (ABS (BINARY-+ Y (UNARY-- X)))) was true. ; With the introduction of assume-true-false-if, however, ACL2 would ; know that each element of the conjunction represented by the IF ; expression was true, and so would be able to determine that the ; conclusion of the :use hint was true by type-reasoning alone (since ; the original :theorem was so proved). Thus, the whole hint rewrites ; to true and is removed. Bummer. ; Previously, the conclusion of a :use hint or of a fact derived from ; a generalization rule would, in affect, be rewritten (the first ; time) under the type-alist of the overall goal to be proved. We now ; handle IMPLIES differently in order to return to this original ; behavior. ; The basic idea is not to expand IMPLIES except within rewrite where ; we can easily maintain the proper type-alists. Two simple exceptions ; to this rule are in tautologyp and distribute-first-if. We expand ; IMPLIES within tautologyp for obvious reasons and within distribute- ; first-if because earlier tests (presumably still valid) showed that ; this was both faster and more fruitful. Note that this second ; exception implies that an IMPLIES will never show up due to a user ; defined function. ; A slightly more subtle exception is in preprocess-clause where we ; expand an IMPLIES if it is derived from the original theorem to be ; proved. This is necessary to provide a context for rewrite (See the ; comments in preprocess-clause beginning ``Note: Once upon a time (in ; Version 1.5)'' for more on this. ;; RAG - In this function, I relaxed the tests for rational to include ;; realp as well. (defun assume-true-false-if (not-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit) ; X is an IF-expression we have been asked to assume-true-false. We ; return the standard tuple through the standard calls to mv-atf. ; The original motivation for this function lay in the fact that, within ; ACL2's logic, conjunctions and disjunctions are encoded as IF ; expressions. In the simplest (conceptually) case, x is of the form ; (IF a b 'nil) which is the translation of (AND a b). This function ; then ensures that the truth of both a and b are recorded in the ; true-type-alist returned. It is, however, written to handle the ; general case. (let ((test (fargn x 1)) (true-branch (fargn x 2)) (false-branch (fargn x 3))) ; We start by recurring on the test. (mv-let (test-mbt test-mbf test-tta test-fta test-ttree) (assume-true-false-rec test xttree force-flg dwp type-alist ancestors ens w pot-lst pt nil backchain-limit) ; In the first two branches, we know that test must be true or that test ; must be false. We recur on the true branch or the false branch ; respectively. Test-ttree is a (possibly trivial) extension of xttree ; containing any assumptions made when calling assume-true-false on test, ; so we use it on the recursive calls. (cond (test-mbt (mv-let (mbt mbf tta fta ttree) (assume-true-false-rec true-branch test-ttree force-flg dwp test-tta ancestors ens w pot-lst pt nil backchain-limit) (mv-atf not-flg mbt mbf tta fta ttree nil))) (test-mbf (mv-let (mbt mbf tta fta ttree) (assume-true-false-rec false-branch test-ttree force-flg dwp test-fta ancestors ens w pot-lst pt nil backchain-limit) (mv-atf not-flg mbt mbf tta fta ttree nil))) (t ; We do not know whether test must be true or test must be false. We ; recur on both true-branch and false-branch, using test-tta and test-fta ; respectively. These two type-alists are proper extensions of type-alist ; which record the assumed type of test. We use xttree since test-ttree ; is nil. (mv-let (tb-mbt tb-mbf tb-tta tb-fta tb-ttree) (assume-true-false-rec true-branch xttree force-flg dwp test-tta ancestors ens w pot-lst pt nil backchain-limit) (mv-let (fb-mbt fb-mbf fb-tta fb-fta fb-ttree) (assume-true-false-rec false-branch xttree force-flg dwp test-fta ancestors ens w pot-lst pt nil backchain-limit) (cond ((and tb-mbf fb-mbf) ; Since both branches must be false, x must be false. It is probably ; not very useful, but we record this fact in the returned type-alist. ; Tb-ttree and fb-ttree record any assumptions made, so we must record ; them in the type-alist also. (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts= x-ts *ts-nil*) (mv-atf not-flg nil t nil type-alist xttree x-ts-ttree)) ((ts-disjointp x-ts *ts-nil*) (mv (er hard 'assume-true-false-if "We did not believe that this could ~ happen. Please send the authors of ~ ACL2 a replayable transcript of ~ this problem if possible, so that ~ we can see what went wrong.") nil nil nil nil)) (t (mv-atf not-flg nil t nil (extend-type-alist-simple x *ts-nil* (cons-tag-trees tb-ttree fb-ttree) type-alist) ; Observe that we let mv-atf cons these two ttrees together. We do this ; repeatedly in the calls of mv-atf below. tb-ttree fb-ttree))))) ((and tb-mbt fb-mbt) ; Since both branches must be true, x must be true (non-nil). (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts-disjointp x-ts *ts-nil*) (mv-atf not-flg t nil type-alist nil xttree x-ts-ttree)) ((ts= x-ts *ts-nil*) (mv (er hard 'assume-true-false-if "We did not believe that this could ~ happen. Please send the authors of ~ ACL2 a replayable transcript of ~ this problem if possible, so that ~ we can see what went wrong.") nil nil nil nil)) (t (mv-atf not-flg t nil (extend-type-alist-simple x ; The ts= test above ensures that this intersection is not the empty type. ; We also know that x-ts is not equal to (ts-intersection x-ts *ts-non-nil*) ; because of the (not (ts-intersectp x-ts *ts-nil*)) test above. (ts-intersection x-ts *ts-non-nil*) (cons-tag-trees x-ts-ttree (cons-tag-trees tb-ttree fb-ttree)) type-alist) nil ; We do not need to use x-ts-ttree here, because x-ts is only being ; used to (possibly) tighten up the type of x stored in the type-alist. ; It is not being used in the determination that x must-be-true. tb-ttree fb-ttree))))) ((and tb-mbt fb-mbf) ; Since the true branch must be true and the false branch must be false ; we know that ; a) The true type-alist we return should include that test and the ; true branch are both true. ; b) The false type-alist we return should include that test and the ; false branch are both false. ; Note that tb-tta and fb-fta contain exactly this information. ; However, we must infect any new entries with the ttrees which record ; any assumptions made in deriving these conclusions. (mv-atf not-flg nil nil (infect-new-type-alist-entries tb-tta type-alist (cons-tag-trees tb-ttree fb-ttree)) (infect-new-type-alist-entries fb-fta type-alist (cons-tag-trees tb-ttree fb-ttree)) nil nil)) ((and tb-mbf fb-mbt) (mv-atf not-flg nil nil (infect-new-type-alist-entries fb-tta type-alist (cons-tag-trees tb-ttree fb-ttree)) (infect-new-type-alist-entries tb-fta type-alist (cons-tag-trees tb-ttree fb-ttree)) nil nil)) (tb-mbt ; Since the true-branch must be true, the only way we can get a ; false-type-alist is if the test is false and the false-branch is false. ; The false-branch false-type-alist contains this information. ; We know little about a true-type-alist however. (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts= x-ts *ts-nil*) (mv-atf not-flg nil t nil (infect-new-type-alist-entries fb-fta type-alist tb-ttree) xttree x-ts-ttree)) ((ts-disjointp x-ts *ts-nil*) (mv-atf not-flg t nil type-alist nil xttree x-ts-ttree)) (t (mv-atf not-flg nil nil (extend-type-alist-simple x (ts-intersection x-ts *ts-non-nil*) (cons-tag-trees x-ts-ttree xttree) type-alist) (infect-new-type-alist-entries fb-fta type-alist tb-ttree) nil nil))))) (tb-mbf (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts= x-ts *ts-nil*) (mv-atf not-flg nil t nil type-alist xttree x-ts-ttree)) ((ts-disjointp x-ts *ts-nil*) (mv-atf not-flg t nil (infect-new-type-alist-entries fb-tta type-alist tb-ttree) nil xttree x-ts-ttree)) (t (mv-atf not-flg nil nil (infect-new-type-alist-entries fb-tta type-alist tb-ttree) (extend-type-alist-simple x *ts-nil* xttree type-alist) nil nil))))) (fb-mbt (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts= x-ts *ts-nil*) (mv-atf not-flg nil t nil (infect-new-type-alist-entries tb-fta type-alist fb-ttree) xttree x-ts-ttree)) ((ts-disjointp x-ts *ts-nil*) (mv-atf not-flg t nil type-alist nil xttree x-ts-ttree)) (t (mv-atf not-flg nil nil (extend-type-alist-simple x (ts-intersection x-ts *ts-non-nil*) (cons-tag-trees x-ts-ttree xttree) type-alist) (infect-new-type-alist-entries tb-fta type-alist fb-ttree) nil nil))))) (fb-mbf (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts= x-ts *ts-nil*) (mv-atf not-flg nil t nil type-alist xttree x-ts-ttree)) ((ts-disjointp x-ts *ts-nil*) (mv-atf not-flg t nil (infect-new-type-alist-entries tb-tta type-alist fb-ttree) nil xttree x-ts-ttree)) (t (mv-atf not-flg nil nil (infect-new-type-alist-entries tb-tta type-alist fb-ttree) (extend-type-alist-simple x *ts-nil* xttree type-alist) nil nil))))) (t (mv-let (x-ts x-ts-ttree) (look-in-type-alist x type-alist w) (cond ((ts= x-ts *ts-nil*) (mv-atf not-flg nil t nil type-alist xttree x-ts-ttree)) ((ts-disjointp x-ts *ts-nil*) (mv-atf not-flg t nil type-alist nil xttree x-ts-ttree)) (t (mv-atf not-flg nil nil (extend-type-alist-simple x (ts-intersection x-ts *ts-non-nil*) (cons-tag-trees x-ts-ttree xttree) type-alist) (extend-type-alist-simple x *ts-nil* xttree type-alist) nil nil))))))))))))) (defun assume-true-false-rec (x xttree force-flg dwp type-alist ancestors ens w pot-lst pt ignore0 backchain-limit) ; We assume x both true and false, extending type-alist as appropriate. ; Xttree is the ttree with which we are to tag all the entries added to ; type-alist when assuming x. Generally speaking, xttree will contain ; a 'PT tag whose value is a parent tree for x. ; We return five values. ; must-be-true - t iff x is definitely true under type-alist and w. ; must-be-false - t iff x is definitely false under type-alist and w. ; true-type-alist - an extension of type-alist encoding the assumption ; that x is true; valid only if not must-be-false. ; false-type-alist - an extension of type-alist encoding the assumption ; that x is false; valid only if not must-be-true. ; ttree - a ttree recording all of the facts used to establish ; x definitely true or definitely false. This result is ; nil if must-be-true and must-be-false are both nil. ; Ttree will always include xttree when ttree is non-nil. ; That is, if we decide the question of x's truth or ; falsity we report a dependency on xttree just as we ; would if we had assumed it and then asked about it. ; Input ignore0 is generally nil, but can be :tta or :fta if we will ignore the ; resulting true-type-alist or false-type-alist, respectively. The following ; example, essentially from Dave Greve, shows a roughly 4X speedup using these ; flags, and saves nearly a billion bytes forcons cells (!), in an Allegro ; Common Lisp run. ;;; (progn ;;; (defstub kstate-p (k) nil) ;;; (defstub aamp-st-p (st) nil) ;;; (defstub kstate-u (k) nil) ;;; (defstub kstate-k (k) nil) ;;; (defstub pred (k st) nil) ;;; ;;; (defaxiom rule ;;; (implies ;;; (equal (kstate-k k) 0) ;;; (equal (pred k st) 1)))) ;;; ;;; (defun gen-forms (n acc) ;;; (declare (xargs :mode :program)) ;;; (if (zp n) ;;; acc ;;; (gen-forms (1- n) ;;; (cons `(NOT (EQUAL (KSTATE-U K) ,n)) ;;; acc)))) ;;; ;;; (defmacro mac () ;;; `(AND (KSTATE-P K) ;;; (AAMP-ST-P ST) ;;; ,@(gen-forms 560 nil) ;;; (NOT (EQUAL (KSTATE-U K) -1)) ;;; (EQUAL (KSTATE-K K) 0))) ;;; ;;; (time$ ; optional timing from Matt K. ;;; (thm (IMPLIES (mac) ;;; (EQUAL (pred k st) ;;; 1)) ;;; ;; optional from Matt K.: ;;; :hints (("Goal" :do-not '(preprocess))))) (mv-let (xnot-flg x) ; Rockwell Addition: This is a minor improvement. ; The following is just (strip-not term) except it also recognizes (IF ; x NIL T) as (NOT x). The former comes up when we expand (ATOM x) ; using its body. (cond ((nvariablep x) ; Warning: Actually x might be a quoted constant here. But we ask ; if the ffn-symb is either NOT or IF and if it is QUOTE we will ; fail those tests anyway. So this is a delicate but legitimate ; violation of our term abstract data type. (cond ((eq (ffn-symb x) 'NOT) (mv t (fargn x 1))) ((and (eq (ffn-symb x) 'IF) (equal (fargn x 2) *nil*) (equal (fargn x 3) *t*)) (mv t (fargn x 1))) (t (mv nil x)))) (t (mv nil x))) (cond ((variablep x) (assume-true-false1 xnot-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit)) ((fquotep x) (if (equal x *nil*) (mv-atf xnot-flg nil t nil type-alist nil xttree) (mv-atf xnot-flg t nil type-alist nil nil xttree))) ((flambda-applicationp x) (assume-true-false1 xnot-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit)) (t (let ((recog-tuple (most-recent-enabled-recog-tuple (ffn-symb x) (global-val 'recognizer-alist w) ens)) (ignore (adjust-ignore-for-atf xnot-flg ignore0))) (cond (recog-tuple ; Before v2-8, we did not check whether x is already explicitly true or false ; in the given type-alist. Here is an example of what can go wrong, ; contributed (in essence) by Eric Smith. ;;; (defund mod4 (n) ;;; (if (not (integerp n)) 0 (mod n 4))) ;;; ;;; (defun even-aux (x) ;;; (if (zp x) ;;; t ;;; (if (eql 1 x) nil (even-aux (+ -2 x))))) ;;; ;;; (defund even (x) ;;; (if (not (integerp x)) ;;; nil ;;; (if (< x 0) ;;; (even-aux (- x)) ;;; (even-aux x)))) ;;; ;;; (skip-proofs ;;; (defthm mod4-is-0-fw-to-even ;;; (implies (and (equal 0 (mod4 n)) ;;; (integerp n)) ;;; (even n)) ;;; :rule-classes (:forward-chaining))) ;;; ;;; ; succeeds ;;; (thm (implies (and (equal 0 (mod4 n)) ;;; (integerp n)) ;;; (even n))) ;;; ;;; (skip-proofs ;;; (defthm even-means-integerp ;;; (implies (even x) (integerp x)) ;;; :rule-classes ;;; (:compound-recognizer))) ;;; ;;; ; fails (but succeeds after the change for v2-8, where we do the ;;; ; assoc-type-alist below) ;;; (thm (implies (and (equal 0 (mod4 n)) ;;; (integerp n)) ;;; (even n))) (let ((strongp (access recognizer-tuple recog-tuple :strongp))) (mv-let (ts ttree) (cond (strongp ; We do not put expect to put calls of strong recognizers in the type-alist. ; See the discussion of strongp above the calls of ; extend-with-proper/improper-cons-ts-tuple below. (mv nil nil)) (t (assoc-type-alist x type-alist w))) (cond ((and ts (ts= ts *ts-nil*)) (mv-atf xnot-flg nil t nil type-alist ttree xttree)) ((and ts (ts-disjointp ts *ts-nil*)) (mv-atf xnot-flg t nil type-alist nil ttree xttree)) (t (mv-let (ts ttree) (type-set-rec (fargn x 1) force-flg dwp type-alist ancestors ens w nil pot-lst pt backchain-limit) (let ((t-int (ts-intersection ts (access recognizer-tuple recog-tuple :true-ts))) (f-int (ts-intersection ts (access recognizer-tuple recog-tuple :false-ts))) (rune (access recognizer-tuple recog-tuple :rune))) (cond ((ts= t-int *ts-empty*) (mv-atf xnot-flg nil t nil type-alist (push-lemma rune ttree) xttree)) ((ts= f-int *ts-empty*) (mv-atf xnot-flg t nil type-alist nil (push-lemma rune ttree) xttree)) (t ; At this point we know that we can't determine whether (recog arg) is ; true or false. We therefore will be returning two type-alists which ; restrict arg's type according to the two intersections computed ; above. Both of these restrictions will depend upon rune (be- ; cause we used the rule to determine the types recognized by recog), ; ttree (because that was the previously known type of arg and was ; involved in the intersections), and xttree (because by contract we ; are obliged to make all new tuples depend on it). So we construct ; shared-ttree below so we don't have to recreate this ttree twice (once ; for the tta and once for the fta). (let ((shared-ttree (push-lemma rune (cons-tag-trees ttree xttree)))) ; The two calls of extend-with-proper/improper-cons-ts-tuple below can be ; thought of as simply extending a type-alist with (list* arg int ; shared-ttree). They actually do some reasoning about true-lists but the ; effect of adding the tuples they produce to tta and fta is just to restrict ; the type of arg. Now if recog is strongp, then the truth and falsity of ; (recog arg) is determined by the appropriate restriction on the type of arg ; and there is no need to add anything else to type-alist to form tta and fta. ; But if recog is not strongp, we need to record the additional assumptions ; that (recog arg) is true or false, appropriately. That is why there are ; ``if'' expressions in the calls of extend-with-proper/improper-cons-ts-tuple ; below: it determines whether we add the arg restriction to type-alist itself ; or to an extension of type-alist that restricts (recog arg) appropriately. ; The assumption that (recog arg) is true depends both on xttree (by contract) ; and the rune, since we are exploiting the fact that recog is Boolean. The ; assumption that (recog arg) is false only depends on xttree. (mv-atf xnot-flg nil nil (and (not (eq ignore :tta)) (extend-with-proper/improper-cons-ts-tuple (fargn x 1) t-int shared-ttree force-flg dwp type-alist ancestors ens (if strongp type-alist (extend-type-alist-simple x *ts-t* (push-lemma rune xttree) type-alist)) w pot-lst pt backchain-limit)) (and (not (eq ignore :fta)) (extend-with-proper/improper-cons-ts-tuple (fargn x 1) f-int shared-ttree force-flg dwp type-alist ancestors ens (if strongp type-alist (extend-type-alist-simple x *ts-nil* xttree type-alist)) w pot-lst pt backchain-limit)) nil nil))))))))))) ((member-eq (ffn-symb x) *expandable-boot-strap-non-rec-fns*) ; Why do we expand these functions? The original motivating example involved ; guards and stemmed from the pre-1.8 days. However, here is a distillation of ; that example for Version 1.8 that illustrates that expansion may help. ; The bottom line in the example is that assuming (= x 0) false is less ; helpful than assuming (equal x 0) false. ; What is the type of the following expression? One way to experiment is ; to define (foo n) to be the expression below. ; (if (and (integerp n) (>= n 0)) ; (if (equal n 0) ; t ; (if (> n 0) t nil)) ; t) ; The answer is that it always returns t. That is because if we know n is ; in *ts-non-negative-integer* and then we learn that (equal n 0) is nil, ; then we know that n is in *ts-positive-integer* and so (> n 0) is true. ; Now what is the type of ; (if (and (integerp n) (>= n 0)) ; (if (= n 0) ; t ; (if (> n 0) t nil)) ; t) ; If the (= n 0) is not expanded into an equal, the answer reported is Boolean. ; We do not learn from the falsity of (= n 0) that n is non-0. (mv-let (mbt mbf tta fta ttree) (assume-true-false-rec (subcor-var (formals (ffn-symb x) w) (fargs x) (body (ffn-symb x) t w)) xttree force-flg dwp type-alist ancestors ens w pot-lst pt ignore backchain-limit) (if xnot-flg (mv mbf mbt fta tta ttree) (mv mbt mbf tta fta ttree)))) ((eq (ffn-symb x) 'equal) (let ((arg1 (fargn x 1)) (arg2 (fargn x 2))) (cond ((equal arg1 arg2) (mv-atf xnot-flg t nil type-alist nil nil ; We could just as well use (push-lemma '(:executable-counterpart equal) ; xttree) here instead. But in order to maintain analogy with the general case ; of an equivalence relation, we'll add the fake rune for type-set instead. As ; noted elsewhere, if we ever give runic names to the theorems establishing ; equivalence-relation-hood and track those names through geneqvs, then we ; ought to push the appropriate rune here, rather than use puffert, which was ; intended for primitives and is thus here somewhat misused unless perhaps ; equiv is 'equal. (puffert xttree))) ((and (quotep arg1) (quotep arg2)) (mv-atf xnot-flg nil t nil type-alist nil (push-lemma '(:executable-counterpart equal) xttree))) (t (mv-let (occursp1 canonicalp1 arg1-canon ttree1) (canonical-representative 'equal arg1 type-alist) ; Recall from the comment in the definition of canonical-representative that if ; occursp equals nil, then term-canon equals term. It follows that in the ; present context, where arg1 and arg2 are distinct and are not both quoteps, ; the next two COND tests remain logically the same if we remove the occursp1 ; conjuncts. But we have put in the occursp1 conjuncts just in order to gain, ; we think, just a bit of efficiency. They could of course be omitted. We ; take a similar step a bit further below, with occursp2. (cond ((and occursp1 (equal arg1-canon arg2)) (mv-atf xnot-flg t nil type-alist nil nil ; Since we know that mv-atf will make the call of cons-tag-trees, we go ahead ; and do it now so that puffert can be called on the outside, thus perhaps ; eliminating an unnecessary cons. We pull such a stunt a number of other ; times in calls of mv-atf. (puffert (cons-tag-trees ttree1 xttree)))) ((and occursp1 (quotep arg1-canon) (quotep arg2)) (mv-atf xnot-flg nil t nil type-alist nil (push-lemma '(:executable-counterpart equal) (puffert (cons-tag-trees ttree1 xttree))))) (t (mv-let (occursp2 canonicalp2 arg2-canon ttree2) (canonical-representative 'equal arg2 type-alist) (cond ((and occursp2 (equal arg1-canon arg2-canon)) (mv-atf xnot-flg t nil type-alist nil nil (puffert (cons-tag-trees xttree (cons-tag-trees ttree1 ttree2))))) ((and occursp2 (quotep arg1-canon) (quotep arg2-canon)) (mv-atf xnot-flg nil t nil type-alist nil (push-lemma '(:executable-counterpart equal) (puffert (cons-tag-trees xttree (cons-tag-trees ttree1 ttree2)))))) (t (let ((temp-temp (assoc-equiv 'equal arg1-canon arg2-canon type-alist))) (cond (temp-temp (cond ((ts= (cadr temp-temp) *ts-t*) ; Arg1-canon and arg2-canon are both supposed to be canonical, so this case ; can't happen! It would be sound to return: ; (mv-atf xnot-flg t nil type-alist nil ; nil ; (puffert ; (cons-tag-trees ; (cddr temp-temp) ; (cons-tag-trees ; ttree1 ; (cons-tag-trees ttree2 xttree))))) ; here, but let's see if we really understand what is going on! (mv-atf (er hard 'assume-true-false "Please send the authors of ACL2 a ~ replayable transcript of this ~ problem if possible, so that they ~ can see what went wrong in the ~ function assume-true-false. The ~ offending call was ~x0. The ~ surprising type-set arose from a ~ call of ~x1." (list 'assume-true-false (kwote x) ' force-flg (kwote type-alist) ' ') (list 'assoc-equiv ''equal (kwote arg1-canon) (kwote arg2-canon) ')) nil nil nil nil nil nil)) ((ts= (cadr temp-temp) *ts-nil*) (mv-atf xnot-flg nil t nil type-alist nil (if (and canonicalp1 canonicalp2) ; ... then ttree1 and ttree2 are nil (see comment in canonical-representative), ; and also there's no reason to puffert (cons-tag-trees (cddr temp-temp) xttree) (puffert (cons-tag-trees (cddr temp-temp) (cons-tag-trees ttree1 (cons-tag-trees ttree2 xttree))))))) (t (let ((erp (assume-true-false-error type-alist x (cadr temp-temp)))) (mv erp nil nil nil nil))))) (t (mv-let (ts1 ttree) (type-set-rec arg1 force-flg dwp type-alist ancestors ens w nil pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec arg2 force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) ; Observe that ttree records the dependencies of both args. (let ((int (ts-intersection ts1 ts2))) (cond ((ts= int *ts-empty*) (mv-atf xnot-flg nil t nil type-alist nil (puffert (cons-tag-trees ttree xttree)))) ((and (ts= ts1 ts2) (member ts1 *singleton-type-sets*)) (mv-atf xnot-flg t nil type-alist nil nil (puffert (cons-tag-trees ttree xttree)))) (t (let* ((swap-flg (term-order arg1-canon arg2-canon)) (shared-ttree ; We could just use (cons-tag-trees ttree xttree) here, but let's save a cons ; if we don't need that tag-tree. (cond ((or (not (ts= ts1 int)) (not (ts= ts2 int)) (member ts2 *singleton-type-sets*) (member ts1 *singleton-type-sets*)) (cons-tag-trees ttree xttree)) (t nil))) (xttree+ (if (and canonicalp1 canonicalp2) ; ... then ttree1 and ttree2 are nil (see comment in canonical-representative), ; and also there's no reason to puffert xttree (puffert (cons-tag-trees ttree1 (cons-tag-trees ttree2 xttree))))) (true-type-alist1 (and (not (eq ignore :tta)) (extend-type-alist1 'equal occursp1 occursp2 (and canonicalp1 canonicalp2) arg1-canon arg2-canon swap-flg x *ts-t* xttree+ type-alist))) (true-type-alist2 (and (not (eq ignore :tta)) (cond ((ts= ts1 int) true-type-alist1) (t (extend-with-proper/improper-cons-ts-tuple arg1 int shared-ttree force-flg dwp type-alist ancestors ens true-type-alist1 w pot-lst pt backchain-limit))))) (true-type-alist3 (and (not (eq ignore :tta)) (cond ((ts= ts2 int) true-type-alist2) (t (extend-with-proper/improper-cons-ts-tuple arg2 int shared-ttree force-flg dwp type-alist ancestors ens true-type-alist2 w pot-lst pt backchain-limit))))) (false-type-alist1 (and (not (eq ignore :fta)) (extend-type-alist1 'equal occursp1 occursp2 (and canonicalp1 canonicalp2) arg1-canon arg2-canon swap-flg x *ts-nil* xttree+ type-alist))) (false-type-alist2 (and (not (eq ignore :fta)) (cond ((member ts2 *singleton-type-sets*) (extend-with-proper/improper-cons-ts-tuple arg1 (ts-intersection ts1 (ts-complement ts2)) shared-ttree force-flg dwp type-alist ancestors ens false-type-alist1 w pot-lst pt backchain-limit)) (t false-type-alist1)))) (false-type-alist3 (and (not (eq ignore :fta)) (cond ((member ts1 *singleton-type-sets*) (extend-with-proper/improper-cons-ts-tuple arg2 (ts-intersection ts2 (ts-complement ts1)) shared-ttree force-flg dwp type-alist ancestors ens false-type-alist2 w pot-lst pt backchain-limit)) (t false-type-alist2))))) (mv-atf xnot-flg nil nil true-type-alist3 false-type-alist3 nil nil)))))))))))))))))))) ((eq (ffn-symb x) '<) (mv-let (ts0 ttree) (type-set-rec x force-flg dwp type-alist ancestors ens w nil pot-lst pt backchain-limit) (cond ((ts= ts0 *ts-nil*) (mv-atf xnot-flg nil t nil type-alist ttree xttree)) ((ts-disjointp ts0 *ts-nil*) (mv-atf xnot-flg t nil type-alist nil ttree xttree)) (t (mv-let (ts1 ttree) (type-set-rec (fargn x 1) force-flg dwp type-alist ancestors ens w nil pot-lst pt backchain-limit) (mv-let (ts2 ttree) (type-set-rec (fargn x 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) ; In the mv-let below we effectively implement the facts that, when x ; is of type *ts-integer* (< x 1) is ~(< 0 x), and (< -1 x) is ~(< x ; 0). By normalizing such inequalities around 0 we can more easily ; recognize the ones covered by our built in types. ; WARNING: A bug once lurked here, so beware. The term we are ; assuming is represented by xnot-flg and x. We are about to ; re-represent it in terms of not-flg, arg1 and arg2. Do not ; accidentally use not-flg with x or xnot-flg with (< arg1 arg2)! In ; the old code, we had only one name for these two flgs. (mv-let (not-flg arg1 arg2 ts1 ts2) (cond ((and (equal (fargn x 2) *1*) (ts-subsetp ts1 (ts-union (ts-complement *ts-acl2-number*) *ts-integer*))) (mv (not xnot-flg) *0* (fargn x 1) *ts-zero* ts1)) ((and (equal (fargn x 1) *-1*) (ts-subsetp ts2 (ts-union (ts-complement *ts-acl2-number*) *ts-integer*))) (mv (not xnot-flg) (fargn x 2) *0* ts2 *ts-zero*)) (t (mv xnot-flg (fargn x 1) (fargn x 2) ts1 ts2))) ; Foreshadow 1: Note that if neither of the newly bound arg1 nor arg2 ; is *0* then not-flg is xnot-flg and arg1 and arg2 are the corresponding ; arguments of x. That is because on the first two of the three branches ; of the cond above, one of the two args is set to *0*. We use this curious ; fact below. ; In the mv-let below we effectively implement the fact that, when x is of type ; *ts-integer* (< 0 (+ 1 x)) is ~(< x 0). The symmetric equivalence of (< (+ ; -1 x) 0) to ~(< 0 x) is also handled. ; We will assume that the binary-+ has been commuted so that the constant arg, ; if any, is the first. ; Note that the output of this transformation is not subject to the first ; transformation, above, so we do not have to consider repeating that ; transformation. However, it is conceivable that the output of this ; transformation is subject to its symmetric counterpart. In particular, if we ; composed this transformation with itself we might reduce (< 0 (+ 1 (+ -1 x))) ; to (< 0 x). We prefer instead to take the position that some arithmetic ; simplifier will reduce the +-expressions. (mv-let (not-flg arg1 arg2 ts1 ts2 ttree) (cond ((and (equal arg1 *0*) (ts-subsetp ts2 ; It is sound to use (ts-intersection ts2 *ts-acl2-number*) in place of ts2 ; above, but since below we see that arg2 is a call of binary-+, we know that ; ts2 is already contained in *ts-acl2-number*. *ts-integer*) (nvariablep arg2) (not (fquotep arg2)) (eq (ffn-symb arg2) 'binary-+) (equal (fargn arg2 1) *1*)) ; So the term is of the form (< 0 (+ 1 x)) and we know x is some integer (or a ; non-number). We transform it to ~(< x 0). But we must determine the ; type-set of x. It cannot be done merely by inverting the type-set of (+ 1 ; x): the latter might be *ts-integer* and x could either be ; *ts-non-positive-integer* or *ts-integer*, or even a non-number. Some cases ; we could invert: if (+ 1 x) is non-positive, then we know x must be strictly ; negative. But rather than invert, we just call type-set on x, accreting onto ; the existing ttree. (mv-let (tsx ttree) (type-set-rec (fargn arg2 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (mv (not not-flg) (fargn arg2 2) *0* tsx *ts-zero* ttree))) ((and (equal arg2 *0*) (ts-subsetp ts1 *ts-integer*) (nvariablep arg1) (not (fquotep arg1)) (eq (ffn-symb arg1) 'binary-+) (equal (fargn arg1 1) *-1*)) (mv-let (tsx ttree) (type-set-rec (fargn arg1 2) force-flg dwp type-alist ancestors ens w ttree pot-lst pt backchain-limit) (mv (not not-flg) *0* (fargn arg1 2) *ts-zero* tsx ttree))) (t (mv not-flg arg1 arg2 ts1 ts2 ttree))) ; Foreshadow 2: Observe that if, at this point, neither the newly bound arg1 ; nor the newly bound arg2 is *0*, then the newly bound not-flg, arg1 and arg2 ; are all equal to their old values (outside this mv-let). That is because the ; cond above, which determines the new values of not-flg, arg1 and arg2 here, ; has the property that on the two branches that changes the not-flg, one of ; the two args is set to *0*. If neither arg is *0* then we could have only ; come out on the last clause of the cond above and not-flg etc are thus ; unchanged. We use this curious property below. ; The transformations just carried out have the possibly odd effect of ; assuming (< 0 x) false when asked to assume (< x 1) true, for integral x. ; This effectively sets x's type-set to the non-positives. One might ; then ask, what happens if we later decide to get the type-set of (< ; x 1). We would hope that, having assumed it, we would realize it ; was true! Indeed we do, but only because type-set-< makes the same ; normalization of (< x 1). This raises the question: Could we reduce ; the size of our code by doing the normalization in only one place, ; either here in assume-true-false or there in type-set- (SEARCH-TYPE-ALIST (< X '0) ; Attempt to find that (< X 0) ;;; 64 ; is false. Note: ;;; ; (decode-type-set 64) = '*TS-NIL* ;;; ((X 7)) ; Type-alist: X is a non-negative ;;; ; rational. Note: ;;; ; (decode-type-set 7) = ;;; ; *TS-NON-NEGATIVE-RATIONAL* ;;; ((Y . Y)) ; unify-subst ;;; NIL)> ; ttree ;;; <1 (SEARCH-TYPE-ALIST NIL ((Y . Y)) NIL)> ; failed to relieve hyp ; As seen below, assume-true-false had failed to put the inequality ; explicitly on the type-alist. ;;; 1> (ASSUME-TRUE-FALSE (< X '0) ; condition assumed true or false ;;; NIL ; a tag-tree ;;; NIL ; force-flg ;;; NIL ; never mind this one... ;;; ((X 31)) ; type-alist: X is rational ;;; NIL ; ancestors ;;; |some-enabled-structure| ;;; |current-acl2-world|)> ;;; <1 (ASSUME-TRUE-FALSE NIL ; must-be-true ;;; NIL ; must-be-false ;;; ((X 24) (X 31)) ; true-type-alist: ;;; ; X is negative rational ;;; ((X 7) (X 31)) ; false-type-alist: ;;; ; X is non-negative rational ;;; NIL)> ; tag-tree ; But wait, there's more! Robert subsequently sent an example showing ; that it is not enough to put the current inequality with 0, e.g., ; (mcons-term* '< *0* arg2), on the type-alist. The original equality ; may need to be there as well. Here is his example, which ACL2 can ; now prove (see mv-atf-2). ;;; (defstub foo (x) t) ;;; ;;; (defaxiom test ;;; (implies (and (<= 1 x) ;;; (integerp x)) ;;; (foo y))) ;;; ;;; (thm ;;; (implies (and (integerp x) ;;; (<= 1 x)) ;;; (foo y))) ; Start old comment regarding the case that (ts-intersectp ts2 ; *ts-complex-rational*). ; Long comment on why we extend the true-type-alist to accommodate complex ; numbers. ; For an example that illustrates why we need to put (mcons-term* '< *0* arg2) ; on the true-type-alist explicitly in this case, try the following. ;;; (encapsulate ;;; (((foo *) => *)) ;;; (local (defun foo (x) (<= 0 x))) ;;; (defthm foo-type (implies (<= 0 x) (equal (foo x) t)) ;;; :rule-classes :type-prescription)) ;;; ;;; (thm (implies (<= 0 x) (equal (foo x) t))) ; If we simply use true-type-alist here, we'll lose the information that (< 0 ; arg2). That is, we desire that the true-type-alist is sufficient for ; deducing what we are assuming true; but if arg2 can be a complex number, we ; will not be able to make that determination. So, we put this inequality on ; the type-alist, explicitly. We do so in the order shown for two reasons, ; probably neither of them particularly important (but at least, we document ; what they are). For one, we want type-set to find the explicit inequality ; first, in case it ever tries to decide it. Although we do not expect ; type-set to have any trouble even if we bury the inequality after an entry ; for arg2, this coding seems more robust. More importantly, however, we want ; to call extend-type-alist, which is a bit complicated, on as short a ; type-alist as possible. ; End old comment regarding the case that (ts-intersectp ts2 ; *ts-complex-rational*). (mv-atf-2 not-flg true-type-alist false-type-alist (mcons-term* '< *0* arg2) xnot-flg x shared-ttree xttree ignore))))) ((equal arg2 *0*) (cond ((ts-subsetp ts1 #+:non-standard-analysis *ts-negative-real* #-:non-standard-analysis *ts-negative-rational*) (mv-atf not-flg t nil type-alist nil ttree xttree)) ((ts-subsetp ts1 (ts-union (ts-complement *ts-acl2-number*) #+:non-standard-analysis *ts-non-negative-real* #-:non-standard-analysis *ts-non-negative-rational*)) (mv-atf not-flg nil t nil type-alist ttree xttree)) (t (let* ((shared-ttree (cons-tag-trees ttree xttree)) (ignore (adjust-ignore-for-atf not-flg ignore0)) (true-type-alist (and (not (eq ignore :tta)) (extend-type-alist ;;*** -simple arg1 (ts-intersection ts1 #+:non-standard-analysis (ts-union *ts-negative-real* *ts-complex*) #-:non-standard-analysis (ts-union *ts-negative-rational* *ts-complex-rational*)) shared-ttree type-alist w))) (false-type-alist (and (not (eq ignore :fta)) (extend-type-alist ;;*** -simple arg1 (ts-intersection ts1 (ts-complement #+:non-standard-analysis *ts-negative-real* #-:non-standard-analysis *ts-negative-rational*)) shared-ttree type-alist w)))) (mv-atf-2 not-flg true-type-alist false-type-alist (mcons-term* '< arg1 *0*) xnot-flg x shared-ttree xttree ignore))))) (t (mv-let (mbt mbf tta fta dttree) (assume-true-false1 xnot-flg ; = not-flg x ; = (mcons-term* '< arg1 arg2) ; Once upon a time we had (mcons-term* '< arg1 arg2), above, instead of x. ; But we claim that not-flg is xnot-flg and that arg1 and arg2 are the ; corresponding arguments of x so that x is equal to (mcons-term* '< arg1 arg2). ; The proof is as follows. We are in the t clause of a cond. The preceding ; tests establish that neither arg1 nor arg2 is *0* here. Hence, by ; Foreshadow 2 above we conclude that not-flg, arg1 and arg2 are ; unchanged from their values at Foreshadow 1. But at Foreshadow 1 we ; see that if neither arg is *0* not-flg is xnot-flg and arg1 and arg2 are ; the corresponding components of x. Q.E.D. xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit) ; Inefficiency: It is somewhat troubling that we are holding ts1 and ; ts2 in our hands while invoking assume-true-false1 on (< arg1 arg2), ; knowing full-well that it will recompute ts1 and ts2. Sigh. It ; would be nice to avoid this duplication of effort. ; We could now return (mv mbt mbf tta fta dttree) as the answer. But, in the ; case that mbt and mbf are both nil we want to tighten up the returned ; type-alists a little if we can. Suppose we are dealing with (< a1 a2) and a1 ; is known to be positive. Then a2 is (even more) positive. We can add that ; to the tta, if it changes the type-set of a2. (cond ((or mbt mbf) ; Just return the already computed answers if we've settled the ; question. (mv mbt mbf tta fta dttree)) (t (let ((tta (and (not (eq ignore0 :tta)) (assume-true-false-< not-flg arg1 arg2 ts1 ts2 tta ttree xttree w))) (fta (and (not (eq ignore0 :fta)) (assume-true-false-< (not not-flg) arg1 arg2 ts1 ts2 fta ttree xttree w)))) (mv nil nil tta fta nil))))))))))))))) ((equivalence-relationp (ffn-symb x) w) (let ((arg1 (fargn x 1)) (arg2 (fargn x 2))) (cond ((equal arg1 arg2) (mv-atf xnot-flg t nil type-alist nil nil (puffert xttree))) (t (let ((equiv (ffn-symb x))) (mv-let (occursp1 canonicalp1 arg1-canon ttree1) (canonical-representative equiv arg1 type-alist) (cond ((and occursp1 ; See comment in the 'equal case for an explanation of this use of occursp1 ; and a similar use of occursp2 below. (equal arg1-canon arg2)) (mv-atf xnot-flg t nil type-alist nil nil (puffert (cons-tag-trees ttree1 xttree)))) (t (mv-let (occursp2 canonicalp2 arg2-canon ttree2) (canonical-representative equiv arg2 type-alist) (cond ((and occursp2 (equal arg1-canon arg2-canon)) (mv-atf xnot-flg t nil type-alist nil nil (puffert (cons-tag-trees xttree (cons-tag-trees ttree1 ttree2))))) (t (let ((temp-temp (assoc-equiv equiv arg1-canon arg2-canon type-alist))) (cond (temp-temp (cond ((ts= (cadr temp-temp) *ts-t*) ; See comment in corresponding place in the 'equal case. (mv-atf (er hard 'assume-true-false "Please send the authors of ~ ACL2 a replayable transcript ~ of this problem if possible, ~ so that they can see what ~ went wrong in the function ~ assume-true-false. The ~ offending call was ~x0. The ~ surprising type-set arose ~ from a call of ~x1." (list 'assume-true-false (kwote x) ' force-flg (kwote type-alist) ' ') (list 'assoc-equiv (kwote equiv) (kwote arg1-canon) (kwote arg2-canon) ')) nil nil nil nil nil nil)) ((ts= (cadr temp-temp) *ts-nil*) (mv-atf xnot-flg nil t nil type-alist nil (if (and canonicalp1 canonicalp2) (cons-tag-trees (cddr temp-temp) xttree) (puffert (cons-tag-trees (cddr temp-temp) (cons-tag-trees ttree1 (cons-tag-trees ttree2 xttree))))))) (t (let ((erp (assume-true-false-error type-alist x (cadr temp-temp)))) (mv erp nil nil nil nil))))) (t (let ((swap-flg (term-order arg1-canon arg2-canon)) (xttree+ (if (and canonicalp1 canonicalp2) xttree (puffert (cons-tag-trees ttree1 (cons-tag-trees ttree2 xttree)))))) (mv-atf xnot-flg nil nil (and (not (eq ignore :tta)) (extend-type-alist1 equiv occursp1 occursp2 (and canonicalp1 canonicalp2) arg1-canon arg2-canon swap-flg x *ts-t* xttree+ type-alist)) (and (not (eq ignore :fta)) (extend-type-alist1 equiv occursp1 occursp2 (and canonicalp1 canonicalp2) arg1-canon arg2-canon swap-flg x *ts-nil* xttree+ type-alist)) nil nil)))))))))))))))) ((or (eq (ffn-symb x) 'car) (eq (ffn-symb x) 'cdr)) ; In this comment we assume (ffn-symb x) is car but everything we say is true ; for the cdr case as well. Suppose xnot-flg is nil. Then after the ; assume-true-false1 below, tta is the result of assuming (car arg) non-nil. ; But if (car arg) is non-nil, then arg is non-nil too. That is, (implies (car ; arg) arg) is a theorem: Pf. Consider the contrapositive, (implies (not arg) ; (not (car arg))). Q.E.D. So we assume arg onto tta as well as (car arg). ; Fta, on the other hand, is the result of assuming (car arg) nil. That tells ; us nothing about arg, e.g., arg could be nil, a cons (whose car is nil) or ; anything violating car's guard. Summarizing this case: if xnot-flg is nil, ; then we assume both (car arg) and arg non-nil onto tta and assume only (car ; arg) nil onto fta. ; Now on the other hand, suppose xnot-flg is t. Then tta contains ; the assumption that (car arg) is nil and fta contains the ; assumption that (car arg) is non-nil. We can add to fta the ; assumption that arg is non-nil. Observe that the two cases are ; symmetric if we simply swap the role of tta and fta before we start ; and after we are done. The first swap is done by the let below. ; The second is done by mv-atf. (mv-let (mbt mbf tta fta ttree) (assume-true-false1 xnot-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit) (cond ((or mbt mbf) (mv mbt mbf tta fta ttree)) (t (let ((tta (if xnot-flg fta tta)) (fta (if xnot-flg tta fta))) (mv-let (mbt1 mbf tta1 fta1 ttree) (assume-true-false-rec (fargn x 1) xttree force-flg dwp tta ancestors ens w pot-lst pt :fta backchain-limit) (declare (ignore mbt1 fta1)) (mv-atf xnot-flg mbt mbf tta1 fta ttree nil))))))) ((eq (ffn-symb x) 'IF) (assume-true-false-if xnot-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit)) (t (assume-true-false1 xnot-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit)))))))) (defun assume-true-false1 (not-flg x xttree force-flg dwp type-alist ancestors ens w pot-lst pt backchain-limit) ; Roughly speaking, this is the simple assume-true-false, which just ; computes the type-set of x and announces that x must be t, must be ; f, or else announces neither and creates two new type-alists with x ; bound to its type minus *ts-t* and to *ts-nil*. It returns the ; standard 5 results of assume-true-false. It puts xttree into the ; type-alist entries for x, if any. ; See assume-true-false. ; NOTE: This function should not be called when x could be a call of an ; equivalence relation, since otherwise it may destroy the third invariant on ; type-alists. (mv-let (ts ttree) (type-set-rec x force-flg dwp type-alist ancestors ens w nil pot-lst pt backchain-limit) ; If we can decide x on the basis of ts, do so and report use of ttree. ; Xttree will be put in by mv-atf. (cond ((ts= ts *ts-nil*) (mv-atf not-flg nil t nil type-alist ttree xttree)) ((ts-disjointp ts *ts-nil*) (mv-atf not-flg t nil type-alist nil ttree xttree)) (t ; We produce two new type-alists. In the one in which x is assumed ; true, we annotate the entry with a ttree that includes both ; xttree and ttree, the initial type-set of x. In the one in ; which x is assumed false, we annotate the entry with the ttree that ; includes just xttree. The true entry depends on the old type ; because we intersect the old and non-nil. But the false entry is ; just the nil type and doesn't depend on the old one. (mv-atf not-flg nil nil (extend-with-proper/improper-cons-ts-tuple x (ts-intersection ts *ts-non-nil*) (cons-tag-trees ttree xttree) force-flg dwp type-alist ancestors ens type-alist w pot-lst pt backchain-limit) ; It is legal to call extend-type-alist-simple below because we know that x is ; not the call of an equivalence relation. However, the call of ; extend-with-proper/improper-cons-ts-tuple above cannot quite be safely ; simplified, because perhaps x is a final CDR that is the call of an ; equivalence relation. (extend-type-alist-simple x *ts-nil* xttree type-alist) nil nil))))) (defun proper/improper-cons-ts-tuple (term ts ttree force-flg dwp type-alist ancestors ens wrld pot-lst pt backchain-limit) ; We return a tuple of the form (mv term' ts' ttree') that asserts the ; assumption that term has type set ts (with the given ttree ; attached). Most often, term', ts' and ttree' are just term, ts and ; ttree. However, if term is of the form (cons a x) we do certain ; auxiliary processing related to the existence of *ts-true-list* and ; its subtypes. We guarantee that ttree' includes ttree. ; We make various implicit assumptions about term and ts, all ; summarized by the restriction that this function can only be called ; by assume-true-false after checking the current type-set of term and ; failing to decide the question. ; We start with two examples. Suppose term is (cons a1 x) and ts is ; *ts-true-list*. Then the "various implicit assumptions" are ; violated because assume-true-false would never ask us to do this: ; the type-set of term is, at worst, the union of *ts-proper-cons* and ; *ts-improper-cons*, and certainly doesn't include *ts-nil*. But ; assume-true-false always asks us to assume a type-set that is a ; subset of the current type-set. ; So suppose we are asked to assume that (cons a1 x) is of type ; *ts-proper-cons*. Then we can deduce that x is of type ; *ts-true-list*. Indeed, these two are equivalent because if we ; store the latter we can compute the former with type-set. But ; because x is a subterm of (cons a1 x) we prefer to store the ; assumption about x because it will find greater use. However, we ; may already know something about the type of x. For example, x may ; be known to be non-nil. So we are obliged to intersect the old type ; of x with the newly derived type if we want to keep maximizing what ; we know. Because of the "implicit assumptions" this intersection ; will never produce the empty type set: if it is impossible for x to ; have the required type, then assume-true-false better not ask us to ; make the assumption. For example, if x is known not to be a ; true-list, then assume-true-false would never ask us to assume that ; (cons a1 x) is proper. ; The example above is based on the theorem ; (proper-consp (cons a1 x)) <-> (true-listp x). ; We similarly build in the theorem ; (improper-consp (cons a1 x)) <-> (not (true-listp x)). (cond ((and (nvariablep term) (not (fquotep term)) (eq (ffn-symb term) 'cons) (or (ts= ts *ts-proper-cons*) (ts= ts *ts-improper-cons*))) (let* ((x (non-cons-cdr term))) ; Can x be an explicit value? If it can, then we'd be in trouble because ; we return a type-alist binding that sets the value of x. But in fact ; x cannot be an explicit value. If it were, then assume-true-false ; would have decided whether (cons a x) was proper or improper. (mv-let (tsx ttreex) (type-set-rec x force-flg dwp type-alist ancestors ens wrld ttree pot-lst pt backchain-limit) (cond ((ts= ts *ts-proper-cons*) (mv x (ts-intersection tsx *ts-true-list*) ttreex)) (t (mv x (ts-intersection tsx (ts-complement *ts-true-list*)) ttreex)))))) (t (mv term ts ttree)))) (defun extend-with-proper/improper-cons-ts-tuple (term ts ttree force-flg dwp type-alist ancestors ens type-alist-to-be-extended wrld pot-lst pt backchain-limit) ; Programming Note: ; Our convention is to call this function to extend the type-alist unless we ; know that the supplied ts is neither *ts-proper-cons* nor *ts-improper-cons*. ; That is, we use this function to construct type-alist entries in only some of ; the cases. See also extend-type-alist-simple and extend-type-alist. (mv-let (term ts ttree) (proper/improper-cons-ts-tuple term ts ttree force-flg dwp type-alist ancestors ens wrld pot-lst pt backchain-limit) (extend-type-alist term ts ttree type-alist-to-be-extended wrld))) ) (defun type-set (x force-flg dwp type-alist ens w ttree pot-lst pt) ":Doc-Section Miscellaneous how type information is encoded in ACL2~/ To help you experiment with type-sets we briefly note the following utility functions. ~c[(type-set-quote x)] will return the type-set of the object ~c[x]. For example, ~c[(type-set-quote \"test\")] is ~c[2048] and ~c[(type-set-quote '(a b c))] is ~c[512]. ~c[(type-set 'term nil nil nil (ens state) (w state) nil nil nil)] will return the type-set of ~c[term]. For example, ~bv[] (type-set '(integerp x) nil nil nil (ens state) (w state) nil nil nil) ~ev[] will return (mv 192 nil). 192, otherwise known as ~c[*ts-boolean*], is the type-set containing ~c[t] and ~c[nil]. The second result may be ignored in these experiments. ~c[Term] must be in the ~c[translated], internal form shown by ~c[:]~ilc[trans]. ~l[trans] and ~pl[term]. ~c[(type-set-implied-by-term 'x nil 'term (ens state)(w state) nil)] will return the type-set deduced for the variable symbol ~c[x] assuming the ~c[translated] term, ~c[term], true. The second result may be ignored in these experiments. For example, ~bv[] (type-set-implied-by-term 'v nil '(integerp v) (ens state) (w state) nil) ~ev[] returns ~c[11]. ~c[(convert-type-set-to-term 'x ts (ens state) (w state) nil)] will return a term whose truth is equivalent to the assertion that the term ~c[x] has type-set ~c[ts]. The second result may be ignored in these experiments. For example ~bv[] (convert-type-set-to-term 'v 523 (ens state) (w state) nil) ~ev[] returns a term expressing the claim that ~c[v] is either an integer or a non-~c[nil] true-list. ~c[523] is the ~c[logical-or] of ~c[11] (which denotes the integers) with ~c[512] (which denotes the non-~c[nil] true-lists).~/ The ``actual primitive types'' of ACL2 are listed in ~c[*actual-primitive-types*], whose elements are shown below. Each actual primitive type denotes a set ~-[] sometimes finite and sometimes not ~-[] of ACL2 objects and these sets are pairwise disjoint. For example, ~c[*ts-zero*] denotes the set containing 0 while ~c[*ts-positive-integer*] denotes the set containing all of the positive integers. ~bv[] *TS-ZERO* ;;; {0} *TS-POSITIVE-INTEGER* ;;; positive integers *TS-POSITIVE-RATIO* ;;; positive non-integer rationals *TS-NEGATIVE-INTEGER* ;;; negative integers *TS-NEGATIVE-RATIO* ;;; negative non-integer rationals *TS-COMPLEX-RATIONAL* ;;; complex rationals *TS-NIL* ;;; {nil} *TS-T* ;;; {t} *TS-NON-T-NON-NIL-SYMBOL* ;;; symbols other than nil, t *TS-PROPER-CONS* ;;; null-terminated non-empty lists *TS-IMPROPER-CONS* ;;; conses that are not proper *TS-STRING* ;;; strings *TS-CHARACTER* ;;; characters ~ev[] The actual primitive types were chosen by us to make theorem proving convenient. Thus, for example, the actual primitive type ~c[*ts-nil*] contains just ~c[nil] so that we can encode the hypothesis ``~c[x] is ~c[nil]'' by saying ``~c[x] has type ~c[*ts-nil*]'' and the hypothesis ``~c[x] is non-~c[nil]'' by saying ``~c[x] has type complement of ~c[*ts-nil*].'' We similarly devote a primitive type to ~c[t], ~c[*ts-t*], and to a third type, ~c[*ts-non-t-non-nil-symbol*], to contain all the other ACL2 symbols. Let ~c[*ts-other*] denote the set of all Common Lisp objects other than those in the actual primitive types. Thus, ~c[*ts-other*] includes such things as floating point numbers and CLTL array objects. The actual primitive types together with ~c[*ts-other*] constitute what we call ~c[*universe*]. Note that ~c[*universe*] is a finite set containing one more object than there are actual primitive types; that is, here we are using ~c[*universe*] to mean the finite set of primitive types, not the infinite set of all objects in all of those primitive types. ~c[*Universe*] is a partitioning of the set of all Common Lisp objects: every object belongs to exactly one of the sets in ~c[*universe*]. Abstractly, a ``type-set'' is a subset of ~c[*universe*]. To say that a term, ~c[x], ``has type-set ~c[ts]'' means that under all possible assignments to the variables in ~c[x], the value of ~c[x] is a member of some member of ~c[ts]. Thus, ~c[(cons x y)] has type-set ~c[{*ts-proper-cons* *ts-improper-cons*}]. A term can have more than one type-set. For example, ~c[(cons x y)] also has the type-set ~c[{*ts-proper-cons* *ts-improper-cons* *ts-nil*}]. Extraneous types can be added to a type-set without invalidating the claim that a term ``has'' that type-set. Generally we are interested in the smallest type-set a term has, but because the entire theorem-proving problem for ACL2 can be encoded as a type-set question, namely, ``Does ~c[p] have type-set complement of ~c[*ts-nil*]?,'' finding the smallest type-set for a term is an undecidable problem. When we speak informally of ``the'' type-set we generally mean ``the type-set found by our heuristics'' or ``the type-set assumed in the current context.'' Note that if a type-set, ~c[ts], does not contain ~c[*ts-other*] as an element then it is just a subset of the actual primitive types. If it does contain ~c[*ts-other*] it can be obtained by subtracting from ~c[*universe*] the complement of ~c[ts]. Thus, every type-set can be written as a (possibly complemented) subset of the actual primitive types. By assigning a unique bit position to each actual primitive type we can encode every subset, ~c[s], of the actual primitive types by the nonnegative integer whose ith bit is on precisely if ~c[s] contains the ith actual primitive type. The type-sets written as the complement of ~c[s] are encoded as the ~c[twos-complement] of the encoding of ~c[s]. Those type-sets are thus negative integers. The bit positions assigned to the actual primitive types are enumerated from ~c[0] in the same order as the types are listed in ~c[*actual-primitive-types*]. At the concrete level, a type-set is an integer between ~c[*min-type-set*] and ~c[*max-type-set*], inclusive. For example, ~c[*ts-nil*] has bit position ~c[6]. The type-set containing just ~c[*ts-nil*] is thus represented by ~c[64]. If a term has type-set ~c[64] then the term is always equal to ~c[nil]. The type-set containing everything but ~c[*ts-nil*] is the twos-complement of ~c[64], which is ~c[-65]. If a term has type-set ~c[-65], it is never equal to ~c[nil]. By ``always'' and ``never'' we mean under all, or under no, assignments to the variables, respectively. Here is a more complicated example. Let ~c[s] be the type-set containing all of the symbols and the natural numbers. The relevant actual primitive types, their bit positions and their encodings are: ~bv[] actual primitive type bit value *ts-zero* 0 1 *ts-positive-integer* 1 2 *ts-nil* 6 64 *ts-t* 7 128 *ts-non-t-non-nil-symbol* 8 256 ~ev[] Thus, the type-set ~c[s] is represented by ~c[(+ 1 2 64 128 256)] = ~c[451]. The complement of ~c[s], i.e., the set of all objects other than the natural numbers and the symbols, is ~c[-452]." ; See type-set-rec. (type-set-rec x force-flg dwp type-alist nil ; ancestors ens w ttree pot-lst pt (backchain-limit w :ts))) (defun assume-true-false (x xttree force-flg dwp type-alist ens w pot-lst pt ignore0) (assume-true-false-rec x xttree force-flg dwp type-alist nil ; ancestors ens w pot-lst pt ignore0 (backchain-limit w :ts))) (defun ok-to-force-ens (ens) (and (enabled-numep *force-xnume* ens) t)) ; Here is the function used to add an assumption to a ttree. In principle, ; add-linear-assumption is called by type-set when it needs to force a ; hypothesis; but we don't want to make it mutually recursive with type-set and ; something like it is open coded in type-set. (defun add-linear-assumption (target term type-alist ens immediatep force-flg wrld tag-tree) ; Adds the assumption term to the assumptions component of tag-tree, except ; that if the assumption is known to be true or known to be false under the ; type alist or is already in the tree, it is not added. ; This function returns (mv flg tag-tree'), where tag-tree' is the new ; tag-tree and flg is ; a) :known-true if the assumption is known to be true (non-nil); ; b) :known-false if the assumption is known to be false; ; c) :added if the assumption is not known to be true or false and ; the assumption was added; and ; d) :failed if the assumption is not known to be true or false, but ; the assumption was not added because force-flg did not permit it. (mv-let (ts ttree) (type-set term force-flg nil type-alist ens wrld nil nil nil) ; Note that force-flg may be t above. In that case, we force the ; guards of term. For example, suppose term were (acl2-numberp (+ x y)), ; where x is known rational and y is unknown (and we use + for binary-+). ; Then ts will come back *ts-t* and the ttree will contain a ; (acl2-numberp y) 'assumption. If we ultimately rely on ts then we ; must add ttree to the incoming tag-tree. But if we ultimately just ; assume term, we can ignore ttree. (cond ((ts= ts *ts-nil*) (mv :known-false (cons-tag-trees ttree tag-tree))) ((ts-disjointp ts *ts-nil*) (mv :known-true (cons-tag-trees ttree tag-tree))) (t (mv-let (forced tag-tree) (cond ((not force-flg) (mv nil tag-tree)) (t (force-assumption 'equal target term type-alist nil immediatep force-flg tag-tree))) (cond ((not forced) ; Since we cannot determine that the proposed assumption is non-nil, ; but we are not allowed to force, we signal failure instead. (mv :failed tag-tree)) (t (mv :added tag-tree)))))))) ; Historical note on the evolution of type-alist-clause. ; Early on, we had a simple version of type-alist-clause that simply looped ; through the literals in the clause, calling assume-true-false for each one. ; With this approach, before we eliminated guards from the logic, the result ; could be very sensitive to the order of the literals in the clause. Consider ; the following theorem, which was not proved before we made this change: ; (implies (and (< 1 (+ 1 n)) ; (< 0 n) ; (integerp n)) ; (equal (< 1 (+ 1 n)) (< 0 n))) ; The problem with this theorem was that the first two hypotheses ; weren't known to be Boolean unless we knew (for example) the third ; hypothesis. A more low-level example could be obtained by applying ; type-alist-clause to ((< n '0) (not (integerp n)) (equal n '0)), and ; then checking the type-set of n in the resulting type-alist. ; One possible solution was to order the clause heuristically on the ; way in to type-alist-clause. One nice thing about that idea is that ; the clause is only rearranged locally, so the change will not be ; pervasive. However, the completeness of this process is ; questionable since the process is not iterative. ; So our next idea, through Version 1.7, was to iterate, calling ; type-alist-clause repeatedly until the answer stabilizes. Actually we were ; slightly more clever than that. The main trick was that when we applied ; assume-true-false to a literal, we did so with the force-flg set so that we ; were allowed to generate assumptions. Then we checked if any assumptions ; were generated as a result. If so, we delayed consideration of that literal ; and all other such literals until there was no further delay possible. Note: ; if force-flg was actually t, then no change was necessary. All this was ; simply to handle the case that force-flg is nil. ; Here are more details on that earlier approach. ; Stage 1: Repeat the following process: Loop through all the ; literals, allowing forcing to take place, but if any assumptions are ; generated then delay consideration of that literal. As long as at ; least one literal seems to contribute to the evolving type-alist in ; a given pass through the loop, we'll pass through the loop again. ; Stage 2: We may now assume that none of the remaining literals has ; been processed, because they all cause splitting. We now process ; them all with the force-flg off so that we can be sure that no ; assumptions are generated. ; Stage 3 (perhaps overkill): Repeat stage 1, but this time give up ; when we make it all the way through the loop without success. ; Starting with Version 1.8, with guards eliminated from the logic, we saw ; no reason to be so clever. So, we reverted once again to a more ; straightforward algorithm. (defun return-type-alist (hitp car-type-alist rest-type-alist original-type-alist wrld) ; Hitp should be t or nil, not 'contradiction. We simply return (cons ; car-type-alist rest-type-alist), except that if hitp is nil then we ; assume that this is equal to original-type-alist and save ourselves a cons. (if hitp (mv-let (ts ttree) (assoc-type-alist (car car-type-alist) rest-type-alist wrld) (let* ((ts (or ts *ts-unknown*)) (int (ts-intersection ts (cadr car-type-alist)))) (cond ((ts= int ts) rest-type-alist) ((ts= int (cadr car-type-alist)) (extend-type-alist (car car-type-alist) (cadr car-type-alist) (cddr car-type-alist) rest-type-alist wrld)) (t (extend-type-alist (car car-type-alist) int (cons-tag-trees ttree (cddr car-type-alist)) rest-type-alist wrld))))) original-type-alist)) (defun type-alist-equality-loop1 (type-alist top-level-type-alist ens w) ; Return (mv hitp type-alist ttree), where hitp is 'contradiction, t (meaning ; "hit"), or nil. If hitp is 'contradiction, then ttree explains why; ; otherwise, ttree is irrelevant. We map down type-alist (which is initially ; the top-level-type-alist) and milk each (EQUAL arg1 arg2) assumed true on it ; by setting the types of arg1 and arg2 each to the intersection of their ; top-level types. This same intersection process was performed when the ; EQUALity was assumed true, but we might have since learned more about the ; types of the two arguments. (cond ((null type-alist) (mv nil nil nil)) (t (mv-let (hitp rest-type-alist rest-ttree) (type-alist-equality-loop1 (cdr type-alist) top-level-type-alist ens w) (cond ((eq hitp 'contradiction) (mv hitp rest-type-alist rest-ttree)) ((and (nvariablep (caar type-alist)) (not (fquotep (caar type-alist))) (eq (ffn-symb (caar type-alist)) 'equal) (ts= (cadar type-alist) *ts-t*)) (let ((arg1 (fargn (caar type-alist) 1)) (arg2 (fargn (caar type-alist) 2)) (ttree0 (cddar type-alist))) ; The following code is very similar to code in the 'equal case in ; assume-true-false (mv-let (ts1 ttree) (type-set arg1 nil nil top-level-type-alist ens w nil nil nil) (mv-let (ts2 ttree) (type-set arg2 nil nil top-level-type-alist ens w ttree nil nil) ; Observe that ttree records the dependencies of both args. (let ((int (ts-intersection ts1 ts2))) (cond ((ts= int *ts-empty*) (mv 'contradiction nil (puffert ttree))) ((ts= ts1 ts2) (mv hitp (return-type-alist hitp (car type-alist) rest-type-alist type-alist w) nil)) (t ; We return now with hitp = t. But the returned type-alist could still equal ; the input type-alist, because when extend-with-proper/improper-cons-ts-tuple ; preserves the type-alist invariants, it can do so by refusing to extend ; type-alist. (mv t (let ((shared-ttree (puffert (cons-tag-trees ttree0 ttree))) (type-alist (return-type-alist hitp (car type-alist) rest-type-alist type-alist w)) (backchain-limit (backchain-limit w :ts))) (cond ((ts= ts1 int) (extend-with-proper/improper-cons-ts-tuple arg2 int shared-ttree nil nil top-level-type-alist nil ens type-alist w nil nil backchain-limit)) ((ts= ts2 int) (extend-with-proper/improper-cons-ts-tuple arg1 int shared-ttree nil nil top-level-type-alist nil ens type-alist w nil nil backchain-limit)) (t (extend-with-proper/improper-cons-ts-tuple arg2 int shared-ttree nil nil top-level-type-alist nil ens (extend-with-proper/improper-cons-ts-tuple arg1 int shared-ttree nil nil top-level-type-alist nil ens type-alist w nil nil backchain-limit) w nil nil backchain-limit)))) nil)))))))) (t (mv hitp (return-type-alist hitp (car type-alist) rest-type-alist type-alist w) nil))))))) (defun clean-up-alist (alist ans) ; Remove duplicate (mod equal) key entries from alist, accumulating the final ; answer onto ans (which is assumed to be nil initially). We keep the first of ; each duplicate binding and thus we do not change the value of assoc-equal on ; the alist. However, the order of the pairs in the returned alist is the ; reverse of that in the initial alist. (cond ((null alist) ans) ((assoc-equal (caar alist) ans) (clean-up-alist (cdr alist) ans)) (t (clean-up-alist (cdr alist) (cons (car alist) ans))))) (defun duplicate-keysp (alist) ; Determine whether there is a key bound twice (mod equal) in alist. We return ; the first pair whose key is bound twice, so that we can extract the key from ; that pair in an error report if we like. (We could return the car of the ; first pair, but if it were nil then we could not distinguish from the error ; case.) (cond ((null alist) nil) ((assoc-equal (caar alist) (cdr alist)) (car alist)) (t (duplicate-keysp (cdr alist))))) (defun clean-type-alist (type-alist) ; We obtained a 12.4% decrease in the time for an example from Dave Greve, by ; avoiding the expense of duplicate-keysp in the commented code below. In that ; example we found a type-alist with 234 members; thus, the speedup is the ; result of eliminating the quadratic behavior of duplicate-keysp and/or ; clean-up-alist (probably the former). The regression suite did not slow down ; as a result of eliminating this "optimization", which presumably had been ; intended to keep the type-alist small by eliminating duplicate keys (though ; it seems quite possible that such duplication was infrequent). ; (if (duplicate-keysp type-alist) ; (reverse (clean-up-alist type-alist nil)) ; type-alist)) type-alist) (defun type-alist-equality-loop-exit (type-alist) (er hard 'type-alist-equality-loop-exit "We're apparently in an infinite type-alist-equality-loop! The ~ offending type-alist is:~%~x0" type-alist)) (defconst *type-alist-equality-loop-max-depth* 10) (defun type-alist-equality-loop (type-alist0 ens w n) ; Returns (mv contradictionp type-alist ttree), where type-alist has no ; duplicate keys and all the (EQUAL arg1 arg2) assumed true in it have been ; milked so that arg1 and arg2 have equal type-sets. (let ((type-alist (clean-type-alist type-alist0))) (mv-let (hitp type-alist ttree) (type-alist-equality-loop1 type-alist type-alist ens w) (cond ((eq hitp 'contradiction) (mv t nil ttree)) ((= n 0) (if (or (not hitp) ; It is possible, even with hitp, for (equal type-alist type-alist0) to be ; true. There is a comment to this effect, regarding type-alist invariants, in ; type-alist-equality-loop1. We discovered this in Version_2.7 during ; regression tests, specifically, with the last form in the comunity book ; books/workshops/2000/manolios/pipeline/pipeline/deterministic-systems/128/top/ma128-isa128. ; This function was being called differently because of a change in in ; built-in-clausep to use forward-chaining. (equal type-alist type-alist0)) (mv nil type-alist nil) (mv nil (type-alist-equality-loop-exit type-alist) nil))) (hitp (type-alist-equality-loop type-alist ens w (1- n))) (t (mv nil type-alist nil)))))) (defun put-assoc-equal-ts (term ts ttree type-alist) (declare (xargs :guard (alistp type-alist))) (cond ((endp type-alist) (list (list* term ts ttree))) ((equal term (caar type-alist)) (let ((ts1 (ts-intersection ts (cadar type-alist)))) (cond ((ts= ts1 (cadar type-alist)) type-alist) (t (cons (list* term ts ttree) (cdr type-alist)))))) (t (cons (car type-alist) (put-assoc-equal-ts term ts ttree (cdr type-alist)))))) (defun reconsider-type-alist (type-alist xtype-alist force-flg ens w pot-lst pt) ; We return (mv contradictionp xtype-alist' ttree) where either contradictionp ; is t and ttree explains, or else those two are nil and xtype-alist' is a ; strengthening of xtype-alist obtained by retyping every term in it, under ; type-alist, using double whammy. ; Through Version_4.1, we accumulated an argument, seen, that accumulated keys ; of type-alist in order to avoid considering a key more than once. However, ; the apparent gain can be offset by the quadratic nature of a corresponding ; test, (member-equal (caar type-alist) seen). Indeed, we decreased the time ; by 13.7% in our preliminary removal of "seen", in an example from Dave Greve, ; without noticeable impact on the regression suite. (cond ((null type-alist) (mv nil xtype-alist nil)) ((and (nvariablep (caar type-alist)) (not (fquotep (caar type-alist))) (eq (ffn-symb (caar type-alist)) 'IF)) ; Through Version_2.5 we retyped IF expressions. But with the introduction ; of assume-true-false-if it became both prohibitively expensive and ; practically unnecessary. So we don't do it anymore. (reconsider-type-alist (cdr type-alist) xtype-alist force-flg ens w pot-lst pt)) (t (mv-let (ts ttree) (type-set (caar type-alist) force-flg (cons :SKIP-LOOKUP ; See type-set-rec for a discussion of :SKIP-LOOKUP. Warning: Do not change ; this car to another value without seeing the warning in recognizer-tuple. (cdar type-alist)) xtype-alist ens w nil pot-lst pt) ; We are looking at a triple (term1 ts1 . ttree1). So we obtain the type-set ; of term1, ts, using the double whammy. That guarantees to intersect ts1 with ; the directly computed type-set of term1 and to cons together ttree1 and the ; directly computed ttree. Proof: type-set will see this very triple (because ; seen ensures this is the first such one) and type-set-finish always conses in ; the old ttree. ; If ts is empty then a contradiction has been detected. If ts is the same as ; ts1 we haven't learned anything and don't waste time and space adding the ; "new" binding of term1. (cond ((ts= ts *ts-empty*) (mv t nil ttree)) (t (reconsider-type-alist (cdr type-alist) (cond ((ts-subsetp (cadar type-alist) ts) xtype-alist) (t (put-assoc-equal-ts (caar type-alist) ts ttree xtype-alist))) force-flg ens w pot-lst pt))))))) (defun type-alist-clause-finish1 (lits ttree-lst force-flg type-alist ens wrld) ; Assume the falsity of every literal in lits, extending type-alist. Return ; (mv contradictionp type-alist' ttree), where either contradictionp is t and ; ttree explains the contradiction or else those two results are nil and ; type-alist' is an extension of type-alist. ; This function is very sensitive to the order in which the literals are ; presented. For example, if lits is ((rationalp (binary-+ '1 i)) (not ; (integerp i))) you will get back a type-alist in which (binary-+ '1 i) is ; assumed non-rational but i is assumed integral. If the two literals are ; reversed you will get back the contradiction signal because if i is known ; integral then (binary-+ '1 i) is known integral. It is the role of ; reconsider-type-alist to improve the first case. (cond ((null lits) (mv nil type-alist nil)) (t (mv-let (mbt mbf tta fta ttree) (assume-true-false (car lits) (car ttree-lst) force-flg nil type-alist ens wrld nil nil :tta) (declare (ignore tta)) (cond (mbt (mv t nil ttree)) (mbf (type-alist-clause-finish1 (cdr lits) (cdr ttree-lst) force-flg type-alist ens wrld)) (t (type-alist-clause-finish1 (cdr lits) (cdr ttree-lst) force-flg fta ens wrld))))))) (defun type-alist-clause-finish (lits ttree-lst force-flg type-alist ens wrld pot-lst pt) ; Assume the falsity of every literal in lits, extending type-alist. Return ; (mv contradictionp type-alist' ttree), where either contradictionp is t and ; ttree explains the contradiction or else those two results are nil and ; type-alist' is an extension of type-alist. This function is not as sensitive ; to order as the "single pass" version, type-alist-clause-finish1, because we ; reconsider the resulting type-alist and try to strengthen each type with a ; double whammy type-set. (mv-let (contradictionp type-alist ttree) (type-alist-clause-finish1 lits ttree-lst force-flg type-alist ens wrld) (cond (contradictionp (mv contradictionp type-alist ttree)) (t (mv-let (contradictionp new-type-alist ttree) (reconsider-type-alist type-alist type-alist force-flg ens wrld pot-lst pt) (cond (contradictionp (mv contradictionp new-type-alist ttree)) ((or (equal new-type-alist type-alist) (null pot-lst)) (mv contradictionp new-type-alist ttree)) ; As of v2-8, we reconsider-type-alist a second time if reconsidering ; once changed the type-alist and the pot-lst is not empty. When we ; first constructed the type-alist, we did not use the pot-lst. Thus, ; this second call to reconsider-type-alist performes much the same ; purpose relative to the pot-lst that the first (and originally, only) ; call plays with respect to the type-alist. This type of heuristic ; is intimately tied up with the treatment of the DWP flag. (t (reconsider-type-alist new-type-alist new-type-alist force-flg ens wrld pot-lst pt)))))))) ; Essay on Repetitive Typing ; Suppose you are asked to assume (not (integerp (1+ i))) and then are asked to ; assume (integerp i). This might happen if you are building a type-alist for ; a sequence of literals and the literals appear in the order presented above. ; When it happens in that way, it is handled by type-alist-clause. We have ; tried a variety of solutions to this problem, named and sketched below. ; naive: don't do anything. The system will fail to note the contradiction ; above. This has actually burned Bishop in "real" theorems. ; force-based iteration in type-alist-clause: In Version 1.7 we had an ; iteration scheme in type-alist-clause based on the fact that (in that ; version of the system) (1+ i) forced the hyp that i is a number and, by ; noting the attempted force and skipping the literal, we delayed the ; processing of the first literal until we had processed the others. But when ; Version 1.8 came along and forcing became much less common, this approach ; was removed and (without much thought) we reverted back to the naive ; approach and burned Bishop. ; repetitious assume-true-false: The first fix to the situation just described ; was to define a new version of assume-true-false, called ; repetitious-assume-true-false, which noted when it added a pair on the ; type-alist that bound a term which occurred as a proper subterm of some ; previously bound term. Thus, when (integerp i) was assumed in the context ; of (not (integerp (1+ i))), the binding of i provoked ; repetitious-assume-true-false to recompute the type of the previously bound ; (1+ i). To do this, repetitious-assume-true-false had to insist that the ; recomputation use the "double whammy" idea (so as not to be fooled into just ; looking up the previously assumed type) and we added the "dwp" flag to the ; type-set clique. Repetitious-assume-true-false was used in place of ; assume-true-false everywhere in the code from rewrite.lisp onwards (i.e., ; type-set still used assume-true-false, as did normalize and ; distribute-first-if). Note that this is more general than an attack at the ; type-alist-clause level because it affects any assumption, not just those ; made at the clause level. But we found this inefficient and have abandoned ; it. ; reconsider-type-alist (1): As an alternative to the repetitious ; assume-true-false, we reverted to the idea of working at the ; type-alist-clause level, but basing the iteration on something besides ; forcing. The idea was to do a simple assume-true-false on each literal of ; the clause (taking the false type-alist always) and then to reconsider the ; final type-alist by computing the type of each bound term (with dwp) in the ; context of the final type-alist. Observe that we only make one ; reconsideration pass. As of this writing (Feb 10, 1995) Version 1.8 uses ; this style of type-alist-clause. ; Here are some performance measures. ; The simplest example is one which fails unless we do some kind of iterated ; typing. Here is Bishop's example: ; (thm (IMPLIES (AND (< 0 (+ (- I) N)) ; (NOT (INTEGERP (+ (- I) N))) ; (INTEGERP N) ; (INTEGERP I) ; ) ; nil)) ; The naive method fails to prove this. Force-based iteration catches it if (- ; I) forces something, but that is not the case in Version 1.8 and so that kind ; of iteration doesn't prove this. The other two iterative schemes do catch ; it. In experimenting though be sure to notice whether it is proved by ; type-alist-clause or something more expensive such as linear arithmetic. ; A good performance test is ; (defthm ordered-symbol-alistp-delete-assoc-eq-test ; (implies (and (ordered-symbol-alistp l) ; (symbolp key) ; (assoc-eq key l)) ; (ordered-symbol-alistp (delete-assoc-eq key l))) ; :hints (("Goal" :in-theory ; (disable ordered-symbol-alistp-delete-assoc-eq)))) ; The naive approach does this in about 3.4 seconds (prove time, on Rana, a ; Sparc 2). The repetitious approach takes 5.6 seconds. The reconsidering ; approach takes 3.6. Analysis of the repetitious data show that in this ; example repetitious-assume-true-false is called 5606 times but the repetition ; changes the type-alist only 92 times. When it does change, the newly bound ; subterm was a variable symbol 80% of the 92 times. and it was (CAR var) or ; (CDR var) in the remaining 20%. The low hit rate of the repetitious ; assume-true-false encouraged us to reconsider the idea. ; But the following test convinced us that repetitious assume-true-false is ; doomed. Consider processing the Nqthm package proofs. The naive approach ; took about 1683 seconds. The repetitious approach never completed. See the ; comment in REWRITE-IF about repetitious-assume-true-false for an explanation. ; The reconsidering approach took 1654 seconds. Only six events had times more ; than 1 second greater than in the naive approach (and overall, it is about 30 ; seconds faster). (defun type-alist-clause (cl ttree-lst force-flg type-alist ens wrld pot-lst pt) ; We construct an extension of type-alist in which every literal of cl is ; assumed false. Ttree-lst is a list of ttrees in (weak) 1:1 correspondence ; with cl. The 'pt tags in the tree corresponding to a given literal indicates ; the parents of that literal. (By "weak" we allow ttree-lst to be shorter ; than cl and for all "excess literals" of cl to have the nil ttree, i.e., we ; just cdr ttree-lst as we go through cl and use car of the car as a (possibly ; nil) ttree whenever we need a ttree.) We return three values. The first is ; t or nil and indicates whether we found a contradiction, i.e., that some ; literal of cl is true. The second is the resulting type-alist (or nil if we ; got a contradiction). The third is a ttree explaining the contradiction (or ; nil if we got no contradiction). The type-alist entries generated for a ; given literal contain the corresponding ttree from ttree-lst. ; Warning: It is probably silly to call this function with force-flg = ; t except for heuristic use, e.g., to see what the probable types of ; some terms are. The reason is that since we process the lits of cl ; one at a time, we may well add 'assumptions that are in fact denied ; by later literals, causing loops because the case split generates ; the original clause again. If called with force-flg = t, then the ; type-alist we generate may have 'assumptions in it. This type-alist ; must be handled with care so that if those assumptions are raised ; the necessary case splits are done. ; Note: Because force-flg can make provisionally false terms look like ; false terms, we sometimes appear simply to have dropped a literal of ; cl. For example, consider the clause {...(not (acl2-numberp ; (binary-+ x y))) ...}. Because of force-flg, that literal looks ; false, i.e., binary-+ "always" returns an acl2-numberp. Assuming ; the literal raises the 'assumptions that x and y are both ; acl2-numberps but sets mbf below to t. We therefore skip the ; literal, ignoring also its 'assumptions. If we look at the clause ; as a formula: (implies (and ... (acl2-numberp (+ x y)) ...) ...) ; and imagine ourselves working on the conclusion, it is as though we ; have simply dropped the hypothesis. This is sound. More generally, ; we can throw away any pair from a type-alist. But this is an ; acceptable thing to do here because, first, (acl2-numberp (+ x y)) ; tells us nothing about x and y -- they may both be orange trees for ; all we know. Second (pretending that we are in a pre-1.8 version of ; ACL2, in which the guard for + was part of its defining axiom), if (+ x y) ; occurs in the conjecture anywhere we will assume it is numeric anyway and ; deal with the 'assumptions that its args are acl2-numberps. So in some ; sense, a hypothesis like (acl2-numberp (+ x y)) is irrelevant because it is ; always given and we pay for it only when it helps us. (if force-flg (type-alist-clause-finish cl ttree-lst force-flg type-alist ens wrld pot-lst pt) (mv-let (contradictionp type-alist0 ttree0) (type-alist-clause-finish cl ttree-lst nil type-alist ens wrld pot-lst pt) (cond (contradictionp (mv t nil ttree0)) (t (type-alist-equality-loop type-alist0 ens wrld *type-alist-equality-loop-max-depth*)))))) (defun known-whether-nil (x type-alist ens force-flg dwp wrld ttree) ; This function determines whether we know, from type-set reasoning, ; whether x is nil or not. It returns three values. The first is the ; answer to the question "Do we know whether x is nil or not?" If the ; answer to that question is yes, the second value is the answer to ; the question "Is x nil?" and the third value is a ttree that extends ; the input ttree and records the 'assumptions and dependencies of our ; derivation. If the answer to the first question is no, the second ; and third values are nil. Note that this function may generate ; 'assumptions and so splitting has to be considered. ; Note: This note ought to be plastered all over this code. Beware ; the handling of ttree. A bug was found in this function (11/9/92) ; because in the case that x was a quoted constant it ignored the ; incoming ttree and reported ``I know whether x is nil and I don't ; need any help!'' But we must always keep in mind that the ttree ; argument to many functions is an accumulator; the incoming ttree is ; responsible for the derivation of x itself and must be preserved. ; The original comment, above, is accurate: the outgoing ttree must be ; an extension of the incoming one when successful. (cond ((quotep x) (mv t (equal x *nil*) ttree)) (t (mv-let (ts ttree) (type-set x force-flg dwp type-alist ens wrld ttree nil nil) (cond ((ts= ts *ts-nil*) (mv t t ttree)) ((ts-intersectp ts *ts-nil*) (mv nil nil nil)) (t (mv t nil ttree))))))) (defun ts-booleanp (term ens wrld) (mv-let (ts ttree) (type-set term nil nil nil ens wrld nil nil nil) (cond ((tagged-objectsp 'assumption ttree) (er hard 'ts-booleanp "It was thought impossible for a call of type-set with ~ force-flg = nil to produce an 'assumption, but ~ ts-booleanp did it on ~x0." term)) (t (ts-subsetp ts *ts-boolean*))))) (defun weak-cons-occur (x y) ; Both x and y are terms. In addition, x is known to be non-quoted ; and not a CONS expression. Consider the binary tree obtained by ; viewing the term y as a CONS tree. We return t iff x is a tip of ; that tree. (cond ((variablep y) (eq x y)) ((fquotep y) nil) ((eq (ffn-symb y) 'cons) (or (weak-cons-occur x (fargn y 1)) (weak-cons-occur x (fargn y 2)))) (t (equal x y)))) (defun equal-x-cons-x-yp (lhs rhs) ; We answer the question ``Is (EQUAL lhs rhs) definitely nil?'' If ; our result is t, then the equality is definitely nil, without ; further qualification. If we say we don't know, i.e., nil, nothing ; is claimed. ; However, we know some things about lhs and rhs that allow us to ; make this function answer ``I don't know'' more quickly and more ; often than it might otherwise. We assume tht lhs and rhs are not ; identical terms and we know they are not both quoted constants ; (though either may be) and we know that their type sets have a ; non-empty intersection. ; We make our positive decision based on structural reasoning. For ; example, (EQUAL x (CONS x &)), is NIL because x occurs properly ; within the CONS tree. This observation does not depend on type-sets or ; anything else. ; However, we don't want to do too much work exploring the two terms. ; For example, if they are both large explicit values we don't want to ; look for them in eachother. We know that we will eventually apply ; the CONS-EQUAL axiom, which will rewrite the equality of two conses ; (constants or otherwise) to the conjoined equalities of their ; components. Thus, if both lhs and rhs are CONS expressions (i.e., a ; consityp) or quoted list constants, we just return nil and let the ; :REWRITE rules take care of it. ; One more minor optimization: if one of our args is a consityp and ; the other is a quoted constant then the constant must be a consp or ; else the type sets wouldn't intersect. ; Further Work: ; If this function is improved, also consider similar improvements to ; almost-quotep, which, like this function, currently only works on ; cons terms but could be generalized. ; This function has an arithmetic analog. For example: ; (EQUAL x (+ a (+ b (+ x c)))) ; is NIL if x has type set *ts-acl2-number* and the type set of the ; sum of the other addends, (+ a (+ b c)), does not intersect *ts-zero*. ; We will eventually add :REWRITE rules to normalize + and do cancellation. ; That will make it easier to find x and may in fact subsume the kind of ; reasoning done here. On the other hand, heuristically it is probably ; good to put every ounce of knowledge we have about these elementary ; things every place we can. ; Similarly, (EQUAL x (* a (* b (* x c)))), is false if x is a non-*ts-zero* ; *ts-acl2-number* and (* a (* b c)) is a non-*ts-integer*. ; Similarly, (EQUAL x (- x)) is nil if x is a non-*ts-zero* *ts-acl2-number*. ; Similarly, (EQUAL x (/ x)) is nil if x is a non-*ts-zero* *ts-ratio* ; or a *ts-complex-rational*. (cond ((variablep lhs) (cond ((consityp rhs) (or (weak-cons-occur lhs (fargn rhs 1)) (weak-cons-occur lhs (fargn rhs 2)))) (t nil))) ((fquotep lhs) nil) ((eq (ffn-symb lhs) 'cons) (cond ((variablep rhs) (or (weak-cons-occur rhs (fargn lhs 1)) (weak-cons-occur rhs (fargn lhs 2)))) ((fquotep rhs) nil) ((eq (ffn-symb rhs) 'cons) nil) (t (or (weak-cons-occur rhs (fargn lhs 1)) (weak-cons-occur rhs (fargn lhs 2)))))) ((consityp rhs) (or (weak-cons-occur lhs (fargn rhs 1)) (weak-cons-occur lhs (fargn rhs 2)))) (t nil))) (defun not-ident (term1 term2 type-alist ens wrld) ; We return two results. The first is t iff (equal term1 term2) is ; false. The second is a ttree that justifies the answer. If the ; first result is nil, so is the second. This function does not ; generate 'assumptions, so it is "weak." I.e., it will not decide ; (equal (+ x y) (cons x y)) unless x and y are both acl2-numberps; one ; might have expected it to say the equality is false and to raise the ; assumptions that x and y are acl2-numberps. The only place this function ; is used, presently, is in normalization, which does not raise ; assumptions. (cond ((and (quotep term1) (quotep term2)) (mv (not (equal term1 term2)) nil)) (t (mv-let (ts1 ttree) (type-set term1 nil nil type-alist ens wrld nil nil nil) (mv-let (ts2 ttree) (type-set term2 nil nil type-alist ens wrld ttree nil nil) (cond ((ts-disjointp ts1 ts2) (mv t ttree)) ((equal-x-cons-x-yp term1 term2) ; Observe that we claim term1 is not term2 without any dependency on ; the type-set ttrees. Even though the heuristic reasonableness of ; equal-x-cons-x-yp depends on the two having intersecting type-sets ; (otherwise, equal-x-cons-x-yp could do a little more work and decide ; the question), the correctness of its positive answer doesn't depend ; on anything. (mv t nil)) (t (mv nil nil)))))))) (defun first-if (args i) ; This function searches the top level of the list args for an ; top-level IF expression. If it does not find one, it returns ; 2 nils. Otherwise, it returns the position of the first one ; it finds and the IF expression found. (cond ((null args) (mv nil nil)) ((and (nvariablep (car args)) (not (quotep (car args))) (eq (ffn-symb (car args)) 'if)) (mv i (car args))) (t (first-if (cdr args) (1+ i))))) (defun all-variablep (lst) (cond ((null lst) t) (t (and (variablep (car lst)) (all-variablep (cdr lst)))))) (defun normalize-with-type-set (term iff-flg type-alist ens wrld ttree) ; The args to this function are as in normalize, below. We return a ; term and a ttree. The term is equivalent (mod iff-flg and ; type-alist) to term. We base our answer on type-set reasoning ; alone. No 'assumptions are generated. (mv-let (ts new-ttree) (type-set term nil nil type-alist ens wrld ttree nil nil) (let ((new-term (cond ((ts-intersectp ts *ts-nil*) (cond ((ts= ts *ts-nil*) *nil*) (t term))) (iff-flg *t*) ((ts= ts *ts-t*) *t*) ((ts= ts *ts-zero*) *0*) (t term)))) (mv new-term (if (equal term new-term) ttree new-ttree))))) (mutual-recursion ; Note: The following function does not normalize IFs that occur in ; lambda arguments. I once tried ``fixing'' that oversight and found ; that it cost a lot on big lambda examples in the Rockwell suite. ; NOTE: Type-set and assume-true-false must be called with force-flg = nil, ; since normalize can recur on a lambda-body whose variables are not the ; variables of the top-level environment. (defun normalize (term iff-flg type-alist ens wrld ttree) ; This function normalizes the if structure of term, simplifying with ; type-set reasoning as it goes. We return two results, a term and a ; ttree. The term is equivalent to term (propositionally equivalent, ; if the iff-flg is t) under the assumptions in type-alist. No ; 'assumption tags are generated. The ttree records the lemmas we ; used as recovered from the type-alist and elsewhere and is an ; extension of the input ttree. ; We expand some calls of members of ; *expandable-boot-strap-non-rec-fns* as we go. The heuristic we use ; is that the expansion not introduce splits on the guards of the ; expanded fns. ; This function combines three more or less separate ideas: if ; normalization, expanding boot-strap non-rec fns, and simplifying ; with type-set information. The reason we do all at once is to ; prevent explosions that would occur if we did them individually. (cond ((variablep term) (normalize-with-type-set term iff-flg type-alist ens wrld ttree)) ((fquotep term) (mv (cond ((and iff-flg (not (equal term *nil*))) *t*) (t term)) ttree)) ((flambda-applicationp term) (mv-let (normal-args ttree) (normalize-lst (fargs term) nil type-alist ens wrld ttree) ; We normalize the body of the lambda (under a type-alist determined ; from the normalized arguments). But we leave a lambda application ; in place. (mv-let (normal-body ttree) (normalize (lambda-body (ffn-symb term)) iff-flg (zip-variable-type-alist (lambda-formals (ffn-symb term)) (type-set-lst normal-args nil ; see note above on force-flg nil type-alist nil ens wrld nil nil (backchain-limit wrld :ts))) ens wrld ttree) (mv (mcons-term (list 'lambda (lambda-formals (ffn-symb term)) normal-body) normal-args) ttree)))) ((eq (ffn-symb term) 'if) (mv-let (t1 ttree) (normalize (fargn term 1) t type-alist ens wrld ttree) (let ((t2 (fargn term 2)) (t3 (fargn term 3))) (mv-let (mbt mbf tta fta ttree1) (assume-true-false t1 nil nil ; see note above on force-flg nil type-alist ens wrld nil nil nil) (cond (mbt (normalize t2 iff-flg type-alist ens wrld (cons-tag-trees ttree1 ttree))) (mbf (normalize t3 iff-flg type-alist ens wrld (cons-tag-trees ttree1 ttree))) ; If mbt and mbf are both nil, then ttree1 is nil and we ignore it ; below. (Actually, we use the same variable name to hold a different ; ttree.) ((and (nvariablep t1) (not (fquotep t1)) (eq (ffn-symb t1) 'if)) (let ((t11 (fargn t1 1)) (t12 (fargn t1 2)) (t13 (fargn t1 3))) (normalize (mcons-term* 'if t11 (mcons-term* 'if t12 t2 t3) (mcons-term* 'if t13 t2 t3)) iff-flg type-alist ens wrld ttree))) (t (mv-let (t2 ttree) (normalize t2 iff-flg tta ens wrld ttree) (mv-let (t3 ttree) (normalize t3 iff-flg fta ens wrld ttree) (cond ((equal t2 t3) (mv t2 ttree)) ((and (equal t1 t2) (equal t3 *nil*)) (mv t1 ttree)) ((and (equal t2 *t*) (equal t3 *nil*)) ; If t1 is Boolean and t2 and t3 are t and nil respectively, we can normalize ; to t1. Similarly, if t1 is not Boolean but we are working in iff-mode, ; we can normalize to t1. At one time, we handled the iff-flg case separately ; and generalized the (equal t2 *t*) test to known-whether-nil. That is ; unnecessary. If iff-flg is set then t2 will have been normalized in iff ; mode. Thus, if it is non-nilp t2 would be *t*. (cond (iff-flg (mv t1 ttree)) (t (mv-let (ts1 ttree1) (type-set t1 ; see note above on force-flg nil nil type-alist ens wrld nil nil nil) (cond ((ts-subsetp ts1 *ts-boolean*) (mv t1 (cons-tag-trees ttree1 ttree))) (t (mv (mcons-term* 'if t1 t2 t3) ttree))))))) (t (mv (mcons-term* 'if t1 t2 t3) ttree))))))))))) (t (mv-let (normal-args ttree) (normalize-lst (fargs term) nil type-alist ens wrld ttree) (let ((term (cons-term (ffn-symb term) normal-args))) (cond ((fquotep term) (mv term ttree)) ((eq (ffn-symb term) 'equal) (cond ((equal (fargn term 1) (fargn term 2)) (mv *t* ttree)) (t (mv-let (not-ident ttree1) (not-ident (fargn term 1) (fargn term 2) type-alist ens wrld) (cond (not-ident (mv *nil* (cons-tag-trees ttree1 ttree))) (t (distribute-first-if term iff-flg type-alist ens wrld ttree))))))) (t (distribute-first-if term iff-flg type-alist ens wrld ttree)))))))) (defun normalize-lst (args iff-flg type-alist ens wrld ttree) (cond ((null args) (mv nil ttree)) (t (mv-let (normal-arg ttree) (normalize (car args) iff-flg type-alist ens wrld ttree) (mv-let (normal-args ttree) (normalize-lst (cdr args) iff-flg type-alist ens wrld ttree) (mv (cons normal-arg normal-args) ttree)))))) (defun normalize-or-distribute-first-if (term iff-flg type-alist ens wrld ttree) (cond ((or (variablep term) (fquotep term)) (normalize term iff-flg type-alist ens wrld ttree)) ((eq (ffn-symb term) 'equal) (cond ((equal (fargn term 1) (fargn term 2)) (mv *t* ttree)) (t (mv-let (not-ident ttree1) (not-ident (fargn term 1) (fargn term 2) type-alist ens wrld) (cond (not-ident (mv *nil* (cons-tag-trees ttree1 ttree))) (t (distribute-first-if term iff-flg type-alist ens wrld ttree))))))) (t (distribute-first-if term iff-flg type-alist ens wrld ttree)))) (defun distribute-first-if (term iff-flg type-alist ens wrld ttree) ; Term is known to be a non-variable non-quotep term in which all the ; args are in normal form. We look for an if among its arguments and ; distribute the first one we find over the function symbol of term. ; In addition, this is the "bottoming out" code for normalize, at ; which we do anything else that is always done to non-IF terms. In ; particular, we consider expanding certain non-rec fns. ; Rockwell Addition: We will get rid of certain functions like THE and ; HARD-ERROR in terms being processed by the theorem prover. See ; remove-guard-holders and the Essay on the Removal of Guard Holders before it. ; That code will eliminate prog2$ (more accurately and more generally, ; return-last) from terms before normalize ever sees it. So I dropped the ; first case of the old code here. (mv-let (n if-expr) (first-if (fargs term) 0) (cond ((null n) ; There is no if at the top-level of term, and since all the args are ; normalized, we know there are no ifs at all. We are thus at the ; bottom of the IF tree and type-alist has on it everything we know. ; We now expand the expandable boot-strap non-rec fns if we can do it ; without introducing their guards. (cond ((member-eq (ffn-symb term) *expandable-boot-strap-non-rec-fns*) (normalize (subcor-var (formals (ffn-symb term) wrld) (fargs term) (body (ffn-symb term) t wrld)) iff-flg type-alist ens wrld ttree)) (t ; In this case the fn isn't expandable. So we just take advantage of ; whatever type info we have and quit. (normalize-with-type-set term iff-flg type-alist ens wrld ttree)))) ; And here is the code after which this function was named. We have ; found an if-expr in the args of term at location n. Since that if ; is in normal form, its test is not an if. We split on that test and ; distribute the if. ; Note: In nqthm, instead of using subst-for-nth-arg as below, we used ; subst-expr and hence hit not only the top-level occurrence of the ; bad if but every occurrence of it in the term. This seems better ; because it doesn't search the term for (unlikely) occurrences and ; doesn't cons up a copy of the term. However, if proofs don't work, ; reconsider this decision. (t (let ((t1 (fargn if-expr 1))) (mv-let (mbt mbf tta fta ttree1) (assume-true-false t1 nil nil ; see note above on force-flg nil type-alist ens wrld nil nil nil) (cond (mbt (normalize-or-distribute-first-if (cons-term (ffn-symb term) (subst-for-nth-arg (fargn if-expr 2) n (fargs term))) iff-flg type-alist ens wrld (cons-tag-trees ttree1 ttree))) (mbf (normalize-or-distribute-first-if (cons-term (ffn-symb term) (subst-for-nth-arg (fargn if-expr 3) n (fargs term))) iff-flg type-alist ens wrld (cons-tag-trees ttree1 ttree))) (t (mv-let (t2 ttree) (normalize-or-distribute-first-if (cons-term (ffn-symb term) (subst-for-nth-arg (fargn if-expr 2) n (fargs term))) iff-flg tta ens wrld ttree) (mv-let (t3 ttree) (normalize-or-distribute-first-if (cons-term (ffn-symb term) (subst-for-nth-arg (fargn if-expr 3) n (fargs term))) iff-flg fta ens wrld ttree) (cond ((equal t2 t3) (mv t2 ttree)) ((and (equal t1 t2) (equal t3 *nil*)) (mv t1 ttree)) ((and (equal t2 *t*) (equal t3 *nil*)) (cond (iff-flg (mv t1 ttree)) (t (mv-let (ts1 ttree1) (type-set t1 nil nil type-alist ens wrld nil nil nil) (cond ((ts-subsetp ts1 *ts-boolean*) (mv t1 (cons-tag-trees ttree1 ttree))) (t (mv (mcons-term* 'if t1 t2 t3) ttree))))))) (t (mv (mcons-term* 'if t1 t2 t3) ttree))))))))))))) ) ; The following functions are used only for debugging purposes. (defun decode-type-set1 (ts alist) (cond ((ts= ts *ts-empty*) nil) ((null alist) (list ts)) ((ts-subsetp (cdar alist) ts) (cons (caar alist) (decode-type-set1 (ts-intersection ts (ts-complement (cdar alist))) (cdr alist)))) (t (decode-type-set1 ts (cdr alist))))) (defun decode-type-set (ts) ; This function converts a type-set into an untranslated term in the ACL2 ; coding world. For example, 1536 is converted into *TS-CONS* (which is the ; (TS-UNION *TS-PROPER-CONS* *TS-IMPROPER-CONS*)). We do this only so that we ; can look at computed type-sets symbolically. (cond ((ts= ts *ts-unknown*) '*ts-unknown*) ((ts= ts *ts-empty*) '*ts-empty*) ((ts-complementp ts) (list 'ts-complement (decode-type-set (ts-complement ts)))) (t (let ((lst (decode-type-set1 ts *code-type-set-alist*))) (cond ((null (cdr lst)) (car lst)) (t (cons 'ts-union lst))))))) (defmacro dts (term type-alist) ; A typical interaction with this macro is: ; ACL2 |>(dts '(denominator x) (list (cons 'x *ts-rational*))) ; (mv *TS-POSITIVE-INTEGER* ttree) `(mv-let (ts ttree) (type-set ,term nil nil ,type-alist (ens state) (w state) nil nil nil) (mv (decode-type-set ts) ttree))) ; It is convenient to be able to get your hands on the global enabled ; structure for testing fns that take an ens arg: (defun ens (state) (f-get-global 'global-enabled-structure state)) (defmacro git (sym prop) `(getprop ,sym ,prop nil 'current-acl2-world (w state))) acl2-sources/workshops.html0000664002132200015000000001557512153207460015515 0ustar kaufmannacl2 ACL2 Workshops and UT ACL2 Seminar

The ACL2 Workshop Series

We hold regular workshops. In 2010, the ACL2 Workshop did a one-time merger with TPHOLs to form the first International Conference on Interactive Theorem Proving (ITP). Such a merger may occur again, but whether or not that happens, the ACL2 community is encouraged to participate in ITP. Other conferences of particular interest to the ACL2 community include CAV (Computer Aided Verification) and FMCAD (Formal Methods in Computer Aided Design).

Here is a list of past ACL2 Workshop slogans.

  • 1999: It ain't over til the last Q.E.D.
  • 2000: Just prove it.
  • 2002: Accumulated Persistence
  • 2003: No software too trivial. No error too obscure.
  • 2004: Defun starts here
  • 2006: ACM Software Systems Award Winner!
  • 2007: Save the world. Use make-event.
  • 2009: None
  • 2011: We aim to prove
  • 2013: Pain is temporary; theorems are forever.

Jared Davis has graciously supplied a listing of bibtex entries for the 2000 through 2004 ACL2 workshops..

ACL2 input files (certifiable books) from the preceding workshops are available from the links above. WARNING: The above links point to the original versions of those books. In order to obtain up-to-date versions of those books that will certify in the latest version of ACL2, you should download workshops.tar.gz to the acl2-sources/books/ subdirectory of your ACL2 distribution, and then gunzip and extract it. On a Unix/Linux system you can then certify all the books by standing in the acl2-sources/ directory and issuing the command make regression.

ACL2 Seminar at UT

An ACL2 seminar meets regularly at the University of Texas. A list of past talks, generally accompanied by abstracts and sometimes slides, may be found on the UT ACL2 seminar page.

ACL2 Course Materials

The links listed below will take you to materials for some courses that involve ACL2. This list is loosely maintained and incomplete, and is given in no particular order. We strongly encourage you to send email to Matt Kaufmann and J Strother Moore if you have additional such links to contribute.







acl2-sources/acl2-customization-files/0002775002132200015000000000000012222333764017410 5ustar kaufmannacl2acl2-sources/acl2-customization-files/parallel-full.lisp0000664002132200015000000000004211722771312023026 0ustar kaufmannacl2(set-waterfall-parallelism :full) acl2-sources/acl2-customization-files/parallel-resource-based.lisp0000664002132200015000000000005411722771321024772 0ustar kaufmannacl2(set-waterfall-parallelism :resource-based) acl2-sources/acl2-customization-files/parallel-top-level.lisp0000664002132200015000000000004711722771326024005 0ustar kaufmannacl2(set-waterfall-parallelism :top-level) acl2-sources/acl2-customization-files/pseudo-parallel.lisp0000664002132200015000000000005511722771331023370 0ustar kaufmannacl2(set-waterfall-parallelism :pseudo-parallel) acl2-sources/acl2-customization-files/README0000664002132200015000000000514012122147303020255 0ustar kaufmannacl2The following five files are useful for running regressions with ACL2(p): parallel-full.lisp parallel-resource-based.lisp parallel-top-level.lisp pseudo-parallel.lisp serial.lisp For example: (time nice make -j 8 regression-fresh ACL2_CENTAUR=skip ACL2=/projects/acl2/devel/ccl-saved_acl2p ACL2_CUSTOMIZATION=/projects/acl2/devel/acl2-customization-files/parallel-full.lisp) >& logs/make-regression-par-ccl-j-8-feb27.log& To find all calls of set-waterfall-parallelism, issue the following command in your books directory. grep -l "set-waterfall-parallelism" `find . -name "*.acl2" -o -name "*.lisp"` Note: Sometimes parallelism is defeated or modified for individual books. To find some of these occurrences, you can issue the following command in your books directory. grep -l acl2-par `find . -name "*.acl2"` For example, hints/basic-tests.acl2 has this: #+acl2-par ; computed hints that modify state (set-waterfall-parallelism nil) And models/jvm/m5/apprentice.acl2 has this: #+acl2-par (set-waterfall-parallelism t) Many books use a stylized make-event inside the actual file to disable parallelism. To find these, issue: cd books grep -l "Disabling waterfall parallelism" `find . -name "*.lisp"` For example, centaur/4v-sexpr/sexpr-rewrites.lisp has the following. (make-event ; Disabling waterfall parallelism because this book allegedly uses memoization ; while performing its proofs. (if (and (hons-enabledp state) (f-get-global 'parallel-execution-enabled state)) (er-progn (set-waterfall-parallelism nil) (value '(value-triple nil))) (value '(value-triple nil)))) Here is a note relevant to ACL2(p) from David Rager. Some of the books will perform better under :resource-based waterfall parallelism than :full waterfall parallelism. This is because these books have so many subgoals and such a strange dependency tree between those subgoals that the waterfall parallelism resources become exhausted and the proofs must execute serially at times to maintain a stable system. As such, it may be good to override any :full setting that a customization file provides and use :resource-based waterfall parallelism instead -- just for these few books. Note that even without overriding the waterfall parallelism setting, the system will remain stable. Changing the waterfall parallelism mode is just a performance optimization. As of April 2012, the books to which this statement may apply are the following: models/jvm/m5/apprentice.lisp coi/termination/assuming/complex.lisp concurrent-programs/bakery/stutter2.lisp acl2-sources/acl2-customization-files/serial.lisp0000664002132200015000000000004011722771336021555 0ustar kaufmannacl2(set-waterfall-parallelism nil) acl2-sources/doc/0002775002132200015000000000000012222334002013310 5ustar kaufmannacl2acl2-sources/doc/acl2-code-size.txt0000664002132200015000000000043012222333562016557 0ustar kaufmannacl2 CODE LINES: 113739 lines, 4996452 characters COMMENT LINES: 64062 lines, 3785153 characters BLANK LINES (excluding documentation): 27307 lines, 27205 characters DOCUMENTATION LINES: 93602 lines, 4206856 characters TOTAL: 298710 lines, 13015666 characters acl2-sources/doc/create-acl2-code-size0000775002132200015000000000426512122335750017217 0ustar kaufmannacl2#!/bin/sh if [ -z "${ACL2_SOURCES}" ] ; then \ echo "ERROR: Environment variable ACL2_SOURCES must have a non-empty string" echo " as its value. Perhaps you intended to call" echo " $0" echo " from GNUmakefile using \"make STATS\"." exit 1 ;\ fi echo 'Computing code size statistics.' echo '; Lines / Characters' > doc/acl2-wc.txt echo '; Code lines (whitespace followed by non-whitespace other than ; ):' >> doc/acl2-wc.txt # The following commands assume that the only whitespace characters # are newlines and spaces. We check for that in write-acl2-code-size. # We also assume that there are no multi-line comments, but we don't # check this because we have one in a :doc string, as displayed just # below. We could perhaps, with some effort, count those and also # count them in :doc strings, and make sure the difference is 0; but # we haven't done so. For now, one can do something like the # following, manually: # ~/acl2/devel$ grep '^[^;]*#|' *.lisp # other-events.lisp: multi-line comments (~c[#|..|#]). These will be ignored if preceded by a # ~/acl2/devel$ (cat ${ACL2_SOURCES} | grep '^[ ]*[^ ;]' | wc -lc) >> doc/acl2-wc.txt echo '; Comment lines ( ; preceded only by whitespace):' >> doc/acl2-wc.txt (cat ${ACL2_SOURCES} | grep '^[ ]*;' | wc -lc) >> doc/acl2-wc.txt echo '; Blank lines (only whitespace):' >> doc/acl2-wc.txt (cat ${ACL2_SOURCES} | grep '^[ ]*$' | wc -lc) >> doc/acl2-wc.txt echo '; TOTAL:' >> doc/acl2-wc.txt (cat ${ACL2_SOURCES} | wc -lc) >> doc/acl2-wc.txt cd doc echo '(value :q)' > workxxx.create-acl2-code-size echo '(lp)' >> workxxx.create-acl2-code-size echo '(certify-book "write-acl2-code-size")' >> workxxx.create-acl2-code-size echo '(acl2::write-acl2-code-size "acl2-wc.txt" "acl2-code-size.txt")' >> workxxx.create-acl2-code-size echo ':q' >> workxxx.create-acl2-code-size ${ACL2} < workxxx.create-acl2-code-size # We could remove acl2-wc.txt at this point, but it seems harmless to leave it. rm -f workxxx.create-acl2-code-size echo '' echo 'Finished computing code size statistics.' acl2-sources/doc/create-acl2-html0000775002132200015000000000545311641636665016317 0ustar kaufmannacl2#! /bin/sh # This script should be run by executing # make DOC # or # make HTML # from the main ACL2 directory. The environment variable ACL2 is used # in this script; it is typically set in ../GNUmakefile. # It is our convention to keep the .fig and .gif files in graphics/ # (under the main ACL2 source directory), but only in the development # directory at UT (not distributed with releases). As part of this # creation, we copy the *.gif files from that source to the doc/HTML # subdirectory where they will reside for convenient HTML access. We # delete the copies on doc/HTML every time we rebuild, because the # definitive copies are in graphics/. rm -f doc/HTML/*.gif # The next two non-blank lines should be commented out if we don't want to # destroy the preceding version. rm -rf doc/HTML-old mv -f doc/HTML doc/HTML-old mkdir doc/HTML chmod 775 doc/HTML # Now copy the definitive .gif files over to doc/HTML. cp -p graphics/*.gif doc/HTML # Copy the license down to the doc/HTML so the home page can reference it. cp LICENSE doc/HTML/LICENSE # Copy down the files we are allowed to change without rebuilding. cp new.html doc/HTML/new.html mkdir doc/HTML/installation cp installation/installation.html doc/HTML/installation/ cp installation/misc.html doc/HTML/installation/ cp installation/obtaining-and-installing.html doc/HTML/installation/ cp installation/requirements.html doc/HTML/installation/ cp installation/using.html doc/HTML/installation/ cp installation/windows7.html doc/HTML/installation/ cp installation/windows-gcl-jared.html doc/HTML/installation/ cp installation/installing-make.html doc/HTML/installation/ cp other-releases.html doc/HTML/other-releases.html cp workshops.html doc/HTML/workshops.html # Ok, now we are ready! cd doc/HTML # Some ACL2 images start up inside LP; some don't. (value :q) always gets us # out of the loop, and should also be harmless when executed in raw Lisp. # However, note that Lispworks requires that the two forms not all be on the # same line. echo '(value :q)' > workxxx echo '(lp)' >> workxxx echo '(certify-book "../write-acl2-html")' >> workxxx if [ "${ACL2_DOC_UNDOCUMENTED_FILE}" != "" ] ; then \ (echo '(write-html-file :file "acl2-doc" :undocumented-file t)' >> workxxx) ; \ else \ (echo '(write-html-file :file "acl2-doc")' >> workxxx) ; \ fi echo ':q' >> workxxx ${ACL2} < workxxx rm -f workxxx # Make all files world-readable. chmod a+r * # To debug bad documentation: # cd /projects/acl2/v2-9/doc/HTML # ../../large-saved_acl2 # (certify-book "/projects/acl2/v2-9/doc/write-acl2-html") # or if it hasn't been changed since it was certified, just # (include-book "/projects/acl2/v2-9/doc/write-acl2-html") # Redefine whatever you need in the image, e.g., *home-page*. # Apply DEFDOC to fix bad documentation. # (write-html-file :file "acl2-doc") acl2-sources/doc/create-acl2-tex0000775002132200015000000000376712206437211016141 0ustar kaufmannacl2#! /bin/sh # A. Flatau 9-Jul-1993; modified by M. Kaufmann many times thereafter. # This file was originally part of create-acl2-texinfo, which created # both Emacs Info files and a .ps file. Now, create-acl2-texinfo # creates Emacs Info files and create-acl2-tex creates a .ps file. # Keep the two files in sync. # This script should be run by executing # make DOC # or # make TEX # from the main ACL2 directory. The environment variable ACL2 is used # in this script; it is typically set in ../GNUmakefile. rm -f doc/workxxx.tex rm -rf doc/TEX mkdir doc/TEX chmod 775 doc/TEX # Some ACL2 images start up inside LP; some don't. (Value :q) always gets us # out of the loop, and should also be harmless when executed in raw Lisp. # However, note that Lispworks requires that the two forms not all be on the # same line. echo '(value :q)' > doc/workxxx.tex echo '(lp)' >> doc/workxxx.tex echo '(include-book "doc/write-acl2-texinfo")' >> doc/workxxx.tex # Note that we do not support ACL2_DOC_UNDOCUMENTED_FILE=t here. We # looked into it a bit (January 2011) and it seemed awkward to provide # that support, unlike the case of HTML. echo '(write-tex-file :dir-string "doc/TEX/" :file "acl2-book")' >> doc/workxxx.tex echo ':q' >> doc/workxxx.tex ${ACL2} < doc/workxxx.tex cd doc/TEX # Note: At UT CS we typically use an old version of texinfo.tex stored # at /projects/acl2/devel-misc/doc/texinfo.tex in order to build the # documentation, which seems to result in more pages with an arguably # more attractive result. But anyone can build it using the default # texinfo.tex. if [ -e ../texinfo.tex ] ; then \ cp ../texinfo.tex . ;\ fi texi2dvi acl2-book.tex # We do it again because otherwise some index entries are wrong. texi2dvi acl2-book.tex dvips acl2-book.dvi -o acl2-book.ps gzip acl2-book.dvi gzip acl2-book.ps # Make all files world-readable after changing back to doc/. cd .. rm -f workxxx.tex chmod a+r TEX/* # See notes at the bottom of create-acl2-texinfo for how to debug bad documentation. acl2-sources/doc/create-acl2-texinfo0000775002132200015000000000447611641640351017016 0ustar kaufmannacl2#! /bin/sh # A. Flatau 9-Jul-1993; modified by M. Kaufmann many times thereafter. # This file (create-acl2-texinfo) originally created both Emacs Info # files and a .ps file. Now, this file creates Emacs Info files and # create-acl2-tex creates a .ps file. Keep the two files in sync. # This script should be run by executing # make DOC # or # make TEXINFO # from the main ACL2 directory. The environment variable ACL2 is used # in this script; it is typically set in ../GNUmakefile. rm -f doc/workxxx.texinfo # The following lines should be commented out if we don't want to # destroy what's in the saved/ subdirectories. rm -rf doc/EMACS-old mv -f doc/EMACS doc/EMACS-old mkdir doc/EMACS chmod 775 doc/EMACS # Some ACL2 images start up inside LP; some don't. (Value :q) always gets us # out of the loop, and should also be harmless when executed in raw Lisp. # However, note that Lispworks requires that the two forms not all be on the # same line. echo '(value :q)' > doc/workxxx.texinfo echo '(lp)' >> doc/workxxx.texinfo echo '(certify-book "doc/write-acl2-texinfo")' >> doc/workxxx.texinfo # Note that we do not support ACL2_DOC_UNDOCUMENTED_FILE=t here. We # looked into it a bit (January 2011) and it seemed awkward to provide # that support, unlike the case of HTML. echo '(write-texinfo-file :dir-string "doc/EMACS/" :file "acl2-doc-emacs" :tex-only-flg nil :non-lucid-flg t)' >> doc/workxxx.texinfo echo ':q' >> doc/workxxx.texinfo ${ACL2} < doc/workxxx.texinfo ## emacs -batch "doc/EMACS/acl2-doc-emacs.texinfo" -l "../make-texinfo.el" -f texinfo-format-buffer-and-save # Replacement for "emacs -batch ..." command above: # cd doc/EMACS ; makeinfo --force acl2-doc-emacs.texinfo ; cd ../.. echo "" echo "Creating EMACS Info files...." cd doc/EMACS ; makeinfo acl2-doc-emacs.texinfo ; cd ../.. echo "Done creating EMACS Info files." echo "" rm -f doc/workxxx.texinfo chmod a+r doc/EMACS/* # To debug bad documentation: # Start up ACL2 in the source directory. # (include-book "doc/write-acl2-texinfo") # Apply DEFDOC to fix bad documentation. # (write-texinfo-file :dir-string "doc/EMACS/" :file "acl2-doc-emacs" :tex-only-flg nil :non-lucid-flg t) # One can write others too: # ## # (write-texinfo-file :dir-string "doc/LEMACS/" :file "acl2-doc-lemacs" :tex-only-flg nil) # (write-tex-file :dir-string "doc/TEX/" :file "acl2-book") acl2-sources/doc/make-texinfo.el0000664002132200015000000000013011605147051016223 0ustar kaufmannacl2(defun texinfo-format-buffer-and-save () (texinfo-format-buffer nil) (save-buffer)) acl2-sources/doc/README0000664002132200015000000000015711605147051014203 0ustar kaufmannacl2See the DOCUMENTATION section of installation.html for an explanation of how to access the ACL2 User's Manual. acl2-sources/doc/write-acl2-code-size.lisp0000664002132200015000000003073011605147051020045 0ustar kaufmannacl2(in-package "ACL2") ; We compute information below to help in the reporting of ACL2 code size. ; We assume that the commands in file create-acl2-code-size have already been ; run that create the infile argument for write-acl2-code-size, which in this ; case is acl2-wc.txt. That file records output from the wc (Linux) command. ; Why not just use our Lisp approach for everything, instead of only for ; counting lines and characters in :doc strings as we do below? We could, by ; first reading each source file into a string (say, by reading a character and ; then writing it with princ$ into a channel to a string, finally calling ; get-output-stream-string$). But we started with the wc command, so we simply ; proceeded by taking advantage of that. If it becomes critical somehow to do ; this task entirely within ACL2, say because of problems with grep, then we ; may reconsider. (program) (set-state-ok t) ;;; We have considered using fixnums. According to the Essay on Fixnum ;;; Declarations, only CLISP and Lispworks have bounds close to our current ;;; total size of about 10.6MB. However, even GCL seems to handle this ;;; easily, without fixnum declarations. (defabbrev inc-for-char (c count) (cond ((eql c #\") (+ 2 count)) (t (1+ count)))) (defun doc-sizes1 (str name i max nlp lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total) ; Nlp is, as the name suggests, related to newlines. But really, it represents ; the following state of our accumulation. When we hit a newline, nlp becomes ; 1, and it increments for each succeeding space. Thus, a positive integer ; value of newline, k, is the number of characters since the most recent ; newline (inclusive), and indicates that we have seen only spaces since that ; newline. (We do not like tabs in our sources, so if we see a tab we cause an ; error.) Once we have hit a non-whitespace character after a newline, we set ; nlp to 'comm if that character is #\;, else to 'code. ; We compute lines-total and chars-total simplistically, without considering ; nlp but instead just accumulating immediately. This way we can check totals ; against the parts at the end if we like, to gain extra confidence. (cond ((int= i max) (cond ((integerp nlp) ; If nlp is an integer then the final " would cause the last line of the string ; to be counted as code by our use of wc. So we should count it as code here. (mv (1+ lines-code) (+ nlp chars-code) lines-comm chars-comm lines-blank chars-blank lines-total chars-total)) (t (mv lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total)))) (t (let* ((c (char str i)) (chars-total (inc-for-char c chars-total))) (cond ((eql c #\Newline) (let ((lines-total (1+ lines-total))) (cond ((integerp nlp) (doc-sizes1 str name (1+ i) max 1 lines-code chars-code lines-comm chars-comm (1+ lines-blank) (+ nlp chars-blank) lines-total chars-total)) (t (doc-sizes1 str name (1+ i) max 1 lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total))))) ((eql c #\Tab) (mv (er hard 'doc-sizes1 "Found Tab at position ~x0 of the doc string for ~x1." i name) 0 0 0 0 0 0 0)) ; don't-care ((eq nlp 'comm) (doc-sizes1 str name (1+ i) max 'comm lines-code chars-code lines-comm (inc-for-char c chars-comm) lines-blank chars-blank lines-total chars-total)) ((eq nlp 'code) (doc-sizes1 str name (1+ i) max 'code lines-code (inc-for-char c chars-code) lines-comm chars-comm lines-blank chars-blank lines-total chars-total)) (t (assert$ (integerp nlp) (cond ((eql c #\Space) (doc-sizes1 str name (1+ i) max (1+ nlp) lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total)) ((eql c #\;) (doc-sizes1 str name (1+ i) max 'comm lines-code chars-code (1+ lines-comm) (+ 1 nlp chars-comm) lines-blank chars-blank lines-total chars-total)) (t (doc-sizes1 str name (1+ i) max 'code (1+ lines-code) (inc-for-char c (+ nlp chars-code)) lines-comm chars-comm lines-blank chars-blank lines-total chars-total)))))))))) (defun doc-sizes (doc-alist lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total) ; We accumulate into lines-total and chars-total the number of lines and ; characters in the :doc strings in doc-alist, as they would appear in the ; source files. Thus, since the " character is written in a :doc string as \", ; a it contributes 2 to chars-total. ; We also accumulate into lines-code through chars-blank the counts that :doc ; strings contribute to the output of wc on the source code, so that we can ; subtract away the effect of :doc strings when counting source code. If we ; think of a deflabel, which is concluded by a :doc string, or a defun, which ; is not, we arrive at the same conclusion: we should add 1 to the number of ; newlines in the code and total line counts for the string. Consider for ; example: ; (deflabel foo ; ".. ; ..") ; Without the :doc string, this would have been written as: ; (deflabel foo) ; So the :doc string is responsible for two more newlines of code and two more ; newlines total. Similarly for defun: ; (defun foo (x) ; ".. ; .." ; ...) ; Wart: We should perhaps subtract the entirety of a defdoc, not just its ; string; perhaps similarly for deflabel. But maybe it's not unreasonable to ; consider those events as infrastructure for managing documentation. (cond ((endp doc-alist) (prog2$ (cond ((not (equal lines-total (+ lines-code lines-comm lines-blank))) (er hard 'doc-sizes "Something is wrong with the doc-sizes algorithm: ~ expected ~x0 but got ~x1." '(equal lines-total (+ lines-code lines-comm lines-blank)) `(not (equal ,lines-total (+ ,lines-code ,lines-comm ,lines-blank))))) ((not (equal chars-total (+ chars-code chars-comm chars-blank))) (er hard 'doc-sizes "Something is wrong with the doc-sizes algorithm: ~ expected ~x0 but got ~x1." '(equal chars-total (+ chars-code chars-comm chars-blank)) `(not (equal ,chars-total (+ ,chars-code ,chars-comm ,chars-blank))))) (t nil)) (mv lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total))) (t (mv-let (lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total) (let ((str (nth 3 (car doc-alist))) (name (nth 0 (car doc-alist)))) (doc-sizes1 str name 0 (length str) 'code (1+ lines-code) chars-code lines-comm chars-comm lines-blank chars-blank (1+ lines-total) chars-total)) (doc-sizes (cdr doc-alist) lines-code chars-code lines-comm chars-comm lines-blank chars-blank lines-total chars-total))))) (defun write-acl2-code-size-fn (infile outfile ctx state) (mv-let (channel state) (open-input-channel infile :object state) (cond ((null channel) (pprogn (warning$ ctx nil "Unable to open file ~x0 for input. Skipping ~ computation of code size." infile) (value nil))) (t (er-let* ((lines-code (read-object channel state)) (chars-code (read-object channel state)) (lines-comm (read-object channel state)) (chars-comm (read-object channel state)) (lines-blank (read-object channel state)) (chars-blank (read-object channel state)) (lines-total (read-object channel state)) (chars-total (read-object channel state))) (let ((state (close-input-channel channel state))) (mv-let (ch state) (open-output-channel outfile :character state) (cond ((null ch) (er soft ctx "Unable to open file ~x0 for output." outfile)) (t (mv-let (lines-doc-code chars-doc-code lines-doc-comm chars-doc-comm lines-doc-blank chars-doc-blank lines-doc-total chars-doc-total) (doc-sizes (global-val 'documentation-alist (w state)) 0 0 0 0 0 0 0 0) (pprogn (fms "CODE LINES:~| ~c0 lines, ~c1 characters" (list (cons #\0 (cons (- lines-code lines-doc-code) 7)) (cons #\1 (cons (- chars-code chars-doc-code) 9))) ch state nil) (fms "COMMENT LINES:~| ~c0 lines, ~c1 characters" (list (cons #\0 (cons (- lines-comm lines-doc-comm) 7)) (cons #\1 (cons (- chars-comm chars-doc-comm) 9))) ch state nil) (fms "BLANK LINES (excluding documentation):~| ~c0 lines, ~c1 ~ characters" (list (cons #\0 (cons (- lines-blank lines-doc-blank) 7)) (cons #\1 (cons (- chars-blank chars-doc-blank) 9))) ch state nil) (fms "DOCUMENTATION LINES:~| ~c0 lines, ~c1 characters" (list (cons #\0 (cons lines-doc-total 7)) (cons #\1 (cons chars-doc-total 9))) ch state nil) (fms "TOTAL:~| ~c0 lines, ~c1 characters" (list (cons #\0 (cons lines-total 7)) (cons #\1 (cons chars-total 9))) ch state nil) (newline ch state) (close-output-channel ch state) (value t)))))))))))) (defmacro write-acl2-code-size (infile outfile) ; See comments at the top of the file. This macro is called in shell script ; create-acl2-code-size. `(time$ (write-acl2-code-size-fn ,infile ,outfile 'write-acl2-code-size state) :msg "~%The execution of WRITE-ACL2-CODE-SIZE took ~st seconds of ~ real time and ~sc seconds of run time (cpu time), and ~ allocated ~sa bytes.~%")) acl2-sources/doc/write-acl2-html.lisp0000664002132200015000000016257612222120044017132 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 was produced by modifying ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTES-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of Version 2 of the GNU General Public License as ; published by the Free Software Foundation. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Sciences ; University of Texas at Austin ; Austin, TX 78712-1188 U.S.A. ; Modified 3/2001 for Version_2.6 by Michael N. Bogomolny, to generate ; a distinct HTML file for each doc topic, in support of his search engine. ; Updated 11/2002 for Version_2.7 by Michael N. Bogomolny, to fix a bug ; related to deeply nested doc topics. ; Html driver for ACL2 documentation. Sample usage: ; (write-html-file "acl2-doc") or even (write-html-file). ; NOTE: This could perhaps be improved by putting "next" and "previous" links ; by the "Parent topic" link. ; This file defines the macro write-html-file, which writes an html file. ; It takes all the documentation strings that are available via the ACL2 :DOC ; command and creates the html file. ; Suppose you get an error like this: #|| ACL2 p!>(write-html-file :topics '(STR::STR) :pkg "STR") Writing html file acl2-doc.html, default package "STR". HARD ACL2 ERROR in FMT-VAR: Unbound Fmt Variable. The tilde directive at location 13 in the fmt string below uses the variable #\c. But this variable is not bound in the association list, ((#\p . "SUBSTR") (#\s . "SUBSTR") (#\t . "substr") (#\T . "SUBSTR")), supplied with the fmt string. "see ~st" ||# ; What this kind of error typically means is that there was a link (~l or ~pl ; or ~il) in a :doc string to SUBSTR, yet SUBSTR wasn't documented. With a ; grep you can perhaps figure out what's wrong, e.g., perhaps ~l[substr] was ; supposed to be ~l[substrp]. (in-package "ACL2-USER") (acl2::set-state-ok t) (acl2::program) ; The idea of node names: first apply the character substitution table, then ; deal with the colon. (defconst *html-doc-char-subst-table* '((#\& #\& #\a #\m #\p #\;) (#\< #\& #\l #\t #\;) (#\> #\& #\g #\t #\;)) "Table with entries (char . char-list) for substituting characters.") (defconst *html-doc-char-subst-table-for-anchors* (cons '(#\Space #\-) *html-doc-char-subst-table*) "Table with entries (char . char-list) for substituting characters; also ~ for use with anchors.") (defconst *filename-char-subst-table* '((#\/ #\_ #\s #\l #\a #\s #\h #\_) (#\\ #\_ #\b #\a #\c #\k #\s #\l #\a #\s #\h #\_) (#\& #\_ #\a #\m #\p #\e #\r #\s #\a #\n #\d #\_) (#\Space #\_) (#\: #\_ #\c #\o #\l #\o #\n #\_) (#\< #\_ #\l #\t #\_) (#\> #\_ #\g #\t #\_) (#\* #\_ #\s #\t #\a #\r #\_) (#\? #\_ #\q #\m #\_) (#\@ #\_ #\a #\t #\_) (#\! #\_ #\b #\a #\n #\g #\_) (#\( #\_ #\l #\p #\a #\r #\e #\n #\_) (#\) #\_ #\r #\p #\a #\r #\e #\n #\_) (#\_ #\_ #\u #\n #\d #\e #\r #\s #\c #\o #\r #\e #\_)) "Table with entries (char . char-list) for substituting characters in ~ filenames.") (defconst *html-vp* ; see print-doc-string-part1 '(("BV" "BF") . ("EV" "EF"))) (defun html-string-node-name (pkg-name name) (cond ((equal pkg-name "KEYWORD") (concatenate 'string ":" name)) (pkg-name (concatenate 'string pkg-name "::" name)) (t name))) (defun substring (str lower upper) (acl2::subseq str lower upper)) (defun html-parse-string (x) ; Returns, for x a symbol-name "PKG:NAME" or "PKG::NAME" the pair (mv "PKG" ; "NAME"), where if PKG is "" then the "PKG" returned is "KEYWORD". (let* ((len (length x)) (posn (position #\: x))) (cond ((null posn) (mv nil x)) ((eql posn 0) (mv "KEYWORD" (substring x 1 len))) (t (mv (substring x 0 posn) (substring x (if (eql (char x (+ 1 posn)) #\:) (+ 2 posn) (+ 1 posn)) len)))))) (defun html-node-name (x spack) ; If x is a string, we simply return x. ; If x is a symbol then spack is a symbol. If the symbol-package-names of x ; and agree and are not the keyword package, then we return the symbol-name of ; x. Otherwise we prepend to that name the symbol-package-name of x followed ; by "::", except that we only prepend ":" if that symbol-package-name is ; "KEYWORD". (cond ((stringp x) x) ((symbolp x) (let ((name (symbol-name x)) (pkg-name (acl2::symbol-package-name x))) (cond ((or (equal pkg-name "KEYWORD") (not (eq (acl2::intern-in-package-of-symbol name spack) x))) (html-string-node-name pkg-name name)) (t (html-string-node-name nil name))))) (t (acl2::er acl2::hard 'html-node-name "Apparent attempt to document topic that is neither a string ~ nor a symbol, ~p0." x)))) (defun html-node-name-lst (lst spack) (if lst (cons (html-node-name (car lst) spack) (html-node-name-lst (cdr lst) spack)) nil)) (defun doc-alist1 (raw-doc-alist spack ans) ; Raw-doc-alist is a doc-alist, i.e., a list with entries of the form (name ; section-symbol cites doc). This function returns a corresponding list with ; entries for which name, section-symbol, and the elements of cites have been ; converted by applying html-node-name. (cond ((null raw-doc-alist) (reverse ans)) (t (doc-alist1 (cdr raw-doc-alist) spack (cons (list* (html-node-name (caar raw-doc-alist) spack) (html-node-name (cadar raw-doc-alist) spack) (html-node-name-lst (caddar raw-doc-alist) spack) (cdddar raw-doc-alist)) ans))))) (defun filter-topics1 (topics doc-alist acc) ; At one time we exploited the "fact" that if every topic's section appears ; earlier in doc-alist than it does, in order to guarantee that we include the ; entire cone underneath a topic. But this "fact" isn't true, e.g., for ; arrays and aref1. ; This function is presumably a no-op when writing out the full ACL2 ; documentation. But for a subtree of that documentation, or perhaps user-book ; documentation, this function "installs" new top-level sections. See a ; further comment on this issue in filter-topics. ; !! Should this deal with cites field? (cond ((null doc-alist) (reverse acc)) ((acl2::member-equal (cadar doc-alist) topics) ; section exists (filter-topics1 topics (cdr doc-alist) (cons (car doc-alist) acc))) ((acl2::member-equal (caar doc-alist) topics) ; The section does not exist, so the current topic should be viewed as a ; section. (filter-topics1 topics (cdr doc-alist) (cons (list* (caar doc-alist) (caar doc-alist) (cddar doc-alist)) acc))) (t (filter-topics1 topics (cdr doc-alist) acc)))) (defun extend-with-immediate-subtopics (topics doc-alist changedp) ; !! Should this deal with cites field? (cond ((null doc-alist) (mv topics changedp)) ((acl2::member-equal (caar doc-alist) topics) (extend-with-immediate-subtopics topics (cdr doc-alist) changedp)) ((acl2::member-equal (cadar doc-alist) topics) (extend-with-immediate-subtopics (cons (caar doc-alist) topics) (cdr doc-alist) t)) (t (extend-with-immediate-subtopics topics (cdr doc-alist) changedp)))) (defun all-subtopics (topics doc-alist) (mv-let (extended-topics changedp) (extend-with-immediate-subtopics topics doc-alist nil) (if changedp (all-subtopics extended-topics doc-alist) topics))) (defun filter-topics (topics doc-alist) ; This function is presumably a no-op when writing out a full documentation ; tree. But when topics specifies a subtree of that documentation, this ; function "installs" new top-level sections by replacing the section-symbol of ; an entry (name section-symbol cites doc) with name when that section-symbol ; isn't in the transitive downward closure of topics with respect to ; doc-alist. (if (null topics) doc-alist (filter-topics1 (all-subtopics topics doc-alist) doc-alist nil))) (defun doc-alist (spack topics state) ; Recall that state-global 'documentation-alist is a list with entries of the ; form (name section-symbol cites doc). This function returns a corresponding ; list with entries for which name, section-symbol, and the elements of cites ; have been converted by applying html-node-name with the indicated spack ; argument, a symbol which symbol-package-name is considered the current ; package for purposes of whether a symbol naming a documentation topic needs a ; package prefix. (doc-alist1 (filter-topics topics (acl2::global-val 'acl2::documentation-alist (acl2::w state))) spack nil)) (defun version-subdirectory (acl2-version) ; From "ACL2 Version 1.9" we compute "v1-9". More generally, ; from "ACL2 Version xxx.yyy/zzz..." we compute "vxxx-yyySzzz..." (let* ((expected-initial-str "ACL2 Version ") (k (1- (length expected-initial-str)))) (cond ((not (acl2::terminal-substringp acl2-version expected-initial-str k k)) (acl2::er acl2::hard 'version-subdirectory "State global 'acl2::acl2-version is not of the expected form!")) (t (coerce (cons #\v (coerce (acl2::apply-char-subst-table1 (cdr (nthcdr k (coerce acl2-version 'list))) nil '((#\. #\-) (#\/ #\S))) 'list)) 'string))))) (defconst *html-doc-markup-table* '(("-" nil . "--") ("B" nil . "~st") ("BF" nil . "~%
")
    ("BID" nil .    "")      ;begin implementation dependent
    ("BPAR" nil .  "

") ("BQ" nil . "

~%

") ("BV" nil . "~%

")
    ("C" nil .  "~st")
    ("EF" nil .  "
~%") ("EID" nil . "") ;end implementation dependent ("EM" nil . "~st") ;emphasis ("EPAR" nil . "

") ("EQ" nil . "

~%

") ("EV" nil . "

~%") ("GIF" nil . "") ;gif files; e.g., ~gif[\"foo.gif\" align=top] ("I" nil . "~st") ("ID" nil . "") ;implementation dependent ;("IL" t . "~st") ;("ILC" t . "~st") ;("L" t . "See ~st") ("IL" t . "~st") ("ILC" t . "~st") ("L" t . "See ~st") ("NL" nil . "
~%") ("PAR" nil . "

~%~%") ;("PL" t . "see ~st") ("PL" t . "see ~st") ("SC" nil . "~sT") ("ST" nil . "~st") ;strong emphasis ("T" nil . "~st") ("TERMINAL" nil . "") ; terminal only, ignore ("WARN" nil . "") ;("CLICK-HERE" t . "Click here") ;("PCLICK-HERE" t . "click here") ("CLICK-HERE" t . "Click here") ("PCLICK-HERE" t . "click here") ;("FLY" t . "") ;("WALK" t . "") ("FLY" t . "") ("LARGE-FLY" t . "") ("WALK" t . "") ("LARGE-WALK" t . "") ("URL" nil . "~st") ) "Table for use in printing documentation strings, when printing to an html file. See :DOC markup") (defun apply-html-subst-table (name anchorp) (acl2::apply-char-subst-table name (if anchorp *html-doc-char-subst-table-for-anchors* *html-doc-char-subst-table*) nil)) (defun apply-filename-subst-table (name) (acl2::apply-char-subst-table name *filename-char-subst-table* nil)) (defun write-header (current-name title level channel state) (declare (ignore current-name)) (pprogn (princ$ "" channel state) ;(princ$ "" channel state) (princ$ (apply-html-subst-table title nil) channel state) ;(princ$ "" channel state) (princ$ "" channel state))) (defun write-trailer (html-file index-file channel state) (pprogn (princ$ "


" channel state) (cond ((null index-file) ; then channel is to the index file state) (t (pprogn (princ$ " " channel state)))) (newline channel state) ; (princ$ "



" channel state) ; Each of the strings below contains 12
's, so we're putting out 72
's ; (princ$ "











" channel state) ; (princ$ "











" channel state) ; (princ$ "











" channel state) ; (princ$ "











" channel state) ; (princ$ "











" channel state) ; (princ$ "











" channel state) ; (newline channel state) (princ$ "" channel state) (newline channel state) (princ$ "" channel state) (newline channel state))) (defun get-html-filename (name doc-fmt-alist html-file) (if (equal name "MAJOR-TOPICS") html-file (or (cdr (assoc #\c (cdr (assoc-equal name doc-fmt-alist)))) "nonexistent-acl2-doc-file.html"))) (defun doc-url (name spack html-file undocumented-file state) (if (equal name "MAJOR-TOPICS") html-file (let* ((str (html-node-name name spack)) (temp (assoc-equal str (acl2::doc-fmt-alist state)))) (cond (temp (acl2::concatenate 'string (cdr (assoc #\c (cdr temp))) ;"#" ;(apply-html-subst-table str t))) )) (undocumented-file) (t (acl2::er acl2::hard 'doc-url "Nonexistent documentation topic, ~p0 (i.e., ~p1)" name str)))))) (defun doc-url-lst (vars lst spack html-file undocumented-file state) (cond ((null lst) nil) (t (cons (cons (car vars) (doc-url (car lst) spack html-file undocumented-file state)) (doc-url-lst (cdr vars) (cdr lst) spack html-file undocumented-file state))))) (defun print-name-as-link (name doc-fmt-alist html-file major-topics-file channel state) (let ((subst-name (apply-html-subst-table name nil))) (if (equal subst-name "MAJOR-TOPICS") (pprogn (princ$ "" channel state) (princ$ "ACL2 Documentation" channel state) (princ$ "" channel state)) (pprogn (princ$ "" channel state) (princ$ subst-name channel state) (princ$ "" channel state))))) (defun write-doc-menu-item (name str html-file major-topics-file enumerate-flg channel state undocumented-file) (let ((doc-fmt-alist (acl2::doc-fmt-alist state))) (pprogn (if enumerate-flg (princ$ "
  • " channel state) (princ$ "" channel state)) (print-name-as-link name doc-fmt-alist html-file major-topics-file channel state) (princ$ " -- " channel state) (acl2::print-doc-string-part 0 str "
    " *html-doc-markup-table* *html-doc-char-subst-table* doc-fmt-alist channel name nil undocumented-file nil state) (if enumerate-flg (pprogn (princ$ "

    " channel state) (newline channel state) (princ$ "
  • " channel state)) (princ$ "

    " channel state)) (newline channel state)))) (defun write-doc-menu1 (lst doc-alist html-file major-topics-file enumerate-flg toc-alist channel state undocumented-file) ; Lst is a list of names, each of which is a string. If toc-alist is non-nil ; here it is the toc-alist, i.e., it contains pairs of the form ("letter" ; . "name"), where "name" is an element of lst. When toc-alist is non-nil we ; do two unusual things. First, we print the Letter before we see name, ; dividing the list into sections by letter. Second, we print a local anchor ; around each letter so the index line at the top of the file can jump to the ; corresponding letter. (cond ((null lst) state) ((equal (car lst) (cdar toc-alist)) ; We just entered the section of the index starting with (letter . name), where ; name is (car lst). We print letter first. (pprogn (acl2::fms "

    ~sa

    ~%" (list (cons #\a (caar toc-alist))) channel state nil) (write-doc-menu-item (car lst) (cadddr (assoc-equal (car lst) doc-alist)) html-file major-topics-file enumerate-flg channel state undocumented-file) (newline channel state) (write-doc-menu1 (cdr lst) doc-alist html-file major-topics-file enumerate-flg (cdr toc-alist) channel state undocumented-file))) ((and (null (cdar toc-alist)) (acl2::assoc-equal-cdr (car lst) toc-alist)) ; The next name to be printed starts a new letter section, but not the next ; one in the toc-alist. That means the next one in the toc alist is nil (which ; we check above simply to avoid the assoc-equal-cdr when it couldn't possibly ; be true). We print an empty letter section for this entry. (pprogn (acl2::fms "

    ~sa

    ~%" (list (cons #\a (caar toc-alist))) channel state nil) (newline channel state) (write-doc-menu1 lst doc-alist html-file major-topics-file enumerate-flg (cdr toc-alist) channel state undocumented-file))) (t (pprogn (write-doc-menu-item (car lst) (cadddr (assoc-equal (car lst) doc-alist)) html-file major-topics-file enumerate-flg channel state undocumented-file) (newline channel state) (write-doc-menu1 (cdr lst) doc-alist html-file major-topics-file enumerate-flg toc-alist channel state undocumented-file))))) (defun append0 (x y) (if (null y) x (append x y))) (defun write-doc-menu (doc-tuple doc-alist html-file major-topics-file enumerate-flg index-flg channel state undocumented-file) "Writes a menu with entries from the list LST of names." (cond ((null (caddr doc-tuple)) state) (t (let* ((main-subitems (strip-cars (cddddr doc-tuple))) (other-menu-items (if main-subitems (acl2::set-difference-equal (caddr doc-tuple) main-subitems) (caddr doc-tuple)))) (pprogn (newline channel state) (princ$ "

    Some Related Topics

    " channel state) (newline channel state) (princ$ "
      " channel state) (newline channel state) (write-doc-menu1 (acl2::merge-sort-alpha-< (append0 main-subitems other-menu-items)) doc-alist html-file major-topics-file enumerate-flg index-flg channel state undocumented-file) (princ$ "
    " channel state) (newline channel state) (newline channel state)))))) (defun write-a-doc-section (doc-tuple parent-name level doc-alist html-file major-topics-file index-file channel state undocumented-file) ; In fact doc-tuple may really be a doc-tree, but that's ok. (let ((doc-fmt-alist (acl2::doc-fmt-alist state))) (pprogn (write-header (car doc-tuple) (cond ((equal parent-name "Pages Written Especially for the Tours") (acl2::get-one-liner-as-string (cadddr doc-tuple))) (t (car doc-tuple))) level channel state) (if (equal parent-name "Pages Written Especially for the Tours") state (acl2::print-doc-string-part 0 (cadddr doc-tuple) " " *html-doc-markup-table* *html-doc-char-subst-table* doc-fmt-alist channel (car doc-tuple) t undocumented-file nil ; vp is relevant only for :par state)) (if (equal parent-name "Pages Written Especially for the Tours") state (pprogn (princ$ "
    " channel state)
            (princ$ "Major Section:  " channel state)
            (print-name-as-link parent-name doc-fmt-alist html-file
                                major-topics-file
                                channel state)
            (newline channel state)
            (princ$ "

    " channel state))) (newline channel state) (mv-let (ln state) (acl2::print-doc-string-part-mv 1 (cadddr doc-tuple) "" *html-doc-markup-table* *html-doc-char-subst-table* doc-fmt-alist channel (car doc-tuple) :par undocumented-file *html-vp* state) (pprogn (newline channel state) (write-doc-menu doc-tuple doc-alist html-file major-topics-file t nil channel state undocumented-file) (acl2::print-doc-string-part 2 (cadddr doc-tuple) "" *html-doc-markup-table* *html-doc-char-subst-table* doc-fmt-alist channel (car doc-tuple) ln ; :par or :par-off undocumented-file *html-vp* state) (write-trailer html-file index-file channel state)))))) (mutual-recursion (defun write-doc-tree-lst (doc-trees parent-name level doc-alist html-file major-topics-file index-file filename-alist state undocumented-file) (cond ((null doc-trees) state) (t (let ((filename (cdr (assoc-equal (caar doc-trees) filename-alist)))) (pprogn (if filename (pprogn (if (acl2::f-get-global 'doc-tree-channel state) (acl2::close-output-channel (acl2::f-get-global 'doc-tree-channel state) state) state) (mv-let (channel state) (acl2::open-output-channel filename :character state) (pprogn (princ$ "" channel state) (newline channel state) (princ$ "" channel state) (princ$ filename channel state) (princ$ " -- " channel state) (princ$ (acl2::f-get-global 'acl2::acl2-version state) channel state) (princ$ "" channel state) (newline channel state) (princ$ "" channel state) (newline channel state) (acl2::f-put-global 'doc-tree-channel channel state)))) state) (write-doc-tree (car doc-trees) parent-name level doc-alist html-file major-topics-file index-file filename-alist state undocumented-file) (write-doc-tree-lst (cdr doc-trees) parent-name level doc-alist html-file major-topics-file index-file filename-alist state undocumented-file)))))) (defun write-doc-tree (doc-tree parent-name level doc-alist html-file major-topics-file index-file filename-alist state undocumented-file) (pprogn (write-a-doc-section doc-tree parent-name level doc-alist html-file major-topics-file index-file (acl2::f-get-global 'doc-tree-channel state) state undocumented-file) (write-doc-tree-lst (cddddr doc-tree) (car doc-tree) (1+ level) doc-alist html-file major-topics-file index-file filename-alist state undocumented-file))) ) (defun html-fmt-alist (doc-trees filename filename-alist acc) (cond ((null doc-trees) acc) (t (let ((filename (or (cdr (assoc-equal (caar doc-trees) filename-alist)) filename))) (if (null filename) (acl2::er acl2::hard 'html-fmt-alist "Something wrong here...") (html-fmt-alist (cdr doc-trees) filename filename-alist (html-fmt-alist (cddddr (car doc-trees)) filename filename-alist (cons (list (caar doc-trees) (cons #\p (apply-html-subst-table (caar doc-trees) t)) ; Here, and below in default-html-fmt-alist, I used to bind #\A, but I now see no ; reason to do so and thus don't. Also, in reading the comment in lookup-fmt-alist ; in the main source code, I suggest that I need to bind #\s as well as #\p. But ; again I see no need and so don't. ; (cons #\s ... why?) ; (cons #\A ... why?) (cons #\c filename)) acc)))))))) (defun write-home-page (html-file spack vsubdir major-topics-file index-file state undocumented-file) ; When this function is called, (acl2::doc-alist state) is set so that doc-url ; can be used to find any ACL2 documentation topic. (mv-let (n state) (acl2::read-idate state) (let* ((date-list (acl2::decode-idate n)) (day (cadddr date-list)) (month (nth (1- (car (cddddr date-list))) '("January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December"))) (yr (+ 1900 (cadr (cddddr date-list)))) (warn-href (cdr (assoc #\w (cdr (assoc-equal "" (acl2::doc-fmt-alist state))))))) (mv-let (channel state) (acl2::open-output-channel html-file :character state) (pprogn (acl2::fms acl2::*home-page* (append (list (cons #\0 (acl2::f-get-global 'acl2::acl2-version state)) (cons #\1 major-topics-file) (cons #\2 index-file) (cons #\3 vsubdir) (cons #\4 month) (cons #\5 day) (cons #\6 yr) (cons #\7 warn-href)) (doc-url-lst '(#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z #\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z) acl2::*home-page-references* spack html-file undocumented-file state)) channel state nil) (newline channel state) (acl2::close-output-channel channel state)))))) (defun write-major-topics (doc-trees doc-alist html-file major-topics-file index-file state undocumented-file) (mv-let (channel state) (acl2::open-output-channel major-topics-file :character state) (pprogn (acl2::fms "~%~sv~%~ ~%~

    Documentation for ~sv

    ~%~

    The ACL2 Documentation is ~ divided into the following Major Topics

    " (list (cons #\v (acl2::f-get-global 'acl2::acl2-version state))) channel state nil) (princ$ "
      " channel state) (write-doc-menu1 (strip-cars doc-trees) doc-alist html-file major-topics-file t nil channel state undocumented-file) (princ$ "
    " channel state) (write-trailer html-file index-file channel state) (acl2::close-output-channel channel state)))) ; Recall that a doc tuple of of the form ; (node-name section-name menu-items doc-string). ; A doc tree is a node of a similar form, extended by children ; (node-name section-name menu-items doc-string . children) ; where each member of children is a doc tree. In fact section-name ; is the parent: so, if the node-name in a doc tuple is the same as ; its section-name, we replace the section-name with "MAJOR-TOPICS". ; The top-level doc tree is ; ("MAJOR-TOPICS" nil "The top level node." ; . ) (defun extend-assoc-equal (key val alist) (cond ((null alist) (list (cons key (list val)))) ((equal key (caar alist)) (cons (cons key (cons val (cdar alist))) (cdr alist))) (t (cons (car alist) (extend-assoc-equal key val (cdr alist)))))) (defun section-alist (doc-alist ans) ; Associates each section name with the doc-tuples in its section. (cond ((null doc-alist) ans) (t (let ((doc-tuple (car doc-alist))) (section-alist (cdr doc-alist) (extend-assoc-equal (cadr doc-tuple) doc-tuple ans)))))) (defun find-node-val (val alist acc) ; Returns (cons entry rest-alist), where val is a member-equal of (strip-cars ; (cdr entry)), entry is a member-equal of alist, and rest-alist is the result ; of deleting the first occurrence of entry from alist. Except, if there is no ; such entry, then nil is returned. Called with acc = NIL. (cond ((null alist) nil) ((assoc-equal val (cdar alist)) (cons (car alist) (revappend acc (cdr alist)))) (t (find-node-val val (cdr alist) (cons (car alist) acc))))) (defun sort-section-alist (section-alist ans) ; When we see an entry (section . doc-tuples), we want to be sure that section ; appears in ans before we add it, unless section is "MAJOR-TOPICS". That way, we are ; sure that every section key in ans appears as a doc-tuple later in ans. ; If the "graph" is circular in section-alist, this will not terminate! (cond ((null section-alist) ans) (t (let ((new-section-alist (find-node-val (caar section-alist) (cdr section-alist) (list (car section-alist))))) (if new-section-alist (sort-section-alist new-section-alist ans) (sort-section-alist (cdr section-alist) (cons (car section-alist) ans))))))) (defun build-doc-tree-lst (doc-tuples section-alist) ; Here, if a doc-tuple represents a section, then that section should already ; be associated with a list of doc-trees in section-alist. (cond ((null doc-tuples) nil) (t (cons (append0 (car doc-tuples) (cdr (assoc-equal (caar doc-tuples) section-alist))) (build-doc-tree-lst (cdr doc-tuples) section-alist))))) (defun merge-car-alpha-< (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((acl2::alpha-< (car (car l1)) (car (car l2))) (cons (car l1) (merge-car-alpha-< (cdr l1) l2))) (t (cons (car l2) (merge-car-alpha-< l1 (cdr l2)))))) (defun merge-sort-car-alpha-< (l) (cond ((null (cdr l)) l) (t (merge-car-alpha-< (merge-sort-car-alpha-< (acl2::evens l)) (merge-sort-car-alpha-< (acl2::odds l)))))) (defun expand-section-alist (section-alist acc) ; Replaces each doc tuple in section-alist with a doc tree. We assume that ; section-alist is sorted "bottom up", i.e., each section's doc tuple appears ; later in section-alist than does its entry. The resulting expanded section ; alist is returned in reverse order, so that "MAJOR-TOPICS" is at the front. We ; sort the newly-appended children alphabetically. (cond ((null section-alist) acc) (t (expand-section-alist (cdr section-alist) (cons (cons (caar section-alist) (build-doc-tree-lst (merge-sort-car-alpha-< (cdar section-alist)) acc)) acc))))) (defun insert-MAJOR-TOPICS-in-doc-alist (doc-alist ans) (cond ((null doc-alist) (reverse ans)) ((equal (caar doc-alist) (cadar doc-alist)) (insert-MAJOR-TOPICS-in-doc-alist (cdr doc-alist) (cons (list* (caar doc-alist) "MAJOR-TOPICS" (cddar doc-alist)) ans))) (t (insert-MAJOR-TOPICS-in-doc-alist (cdr doc-alist) (cons (car doc-alist) ans))))) (defun top-doc-trees (doc-alist) (cdr (car (expand-section-alist (sort-section-alist (section-alist (insert-MAJOR-TOPICS-in-doc-alist doc-alist nil) nil) nil) nil)))) (defun remove1-assoc (key alist) (cond ((null alist) nil) ((eql key (caar alist)) (cdr alist)) (t (cons (car alist) (remove1-assoc key (cdr alist)))))) (defun all-doc-topics (doc-trees acc) ; I have found this a handy function to have around, even though in some past ; versions of the code it isn't used. (cond ((null doc-trees) acc) (t (all-doc-topics (cdr doc-trees) (all-doc-topics (cddddr (car doc-trees)) (cons (caar doc-trees) acc)))))) (defun apply-html-subst-table-to-doc-topic-strings (doc-alist ans) (cond ((endp doc-alist) ans) (t (let ((new-name (apply-html-subst-table (car (car doc-alist)) t))) (apply-html-subst-table-to-doc-topic-strings (cdr doc-alist) (cons (if (symbolp new-name) (symbol-name new-name) new-name) ans)))))) (defun find-all-duplicatesp-equal (lst ans) (cond ((endp lst) ans) ((acl2::member-equal (car lst) (cdr lst)) (find-all-duplicatesp-equal (cdr lst) (cons (car lst) ans))) (t (find-all-duplicatesp-equal (cdr lst) ans)))) (defun chk-uniqueness-after-applying-subst (spack state) (let ((anchor-strings (apply-html-subst-table-to-doc-topic-strings (doc-alist spack nil state) nil))) (cond ((acl2::no-duplicatesp-equal anchor-strings) state) (t (let ((err (acl2::er acl2::hard 'acl2::write-html-file "Duplications appear in the list of all ~ document topic names after the names are ~ converted to strings and ~ apply-html-subst-table is applied. The list ~ of all such duplicated strings is ~x0." (find-all-duplicatesp-equal anchor-strings nil)))) (declare (ignore err)) state))))) (defun get-first-char-xcode (str) ; Imagine the alphabet consisting of the letters #\A through #\Z plus the ; "letter" 'SIGNS and the letter 'ACL2-PC::. The xcode of a normal letter is ; its char-code, e.g., (char-code #\A) = 65, (char-code #\Z)=90. The xcode of ; SIGNS is 64 and the xcode of ACL2-PC:: is 91. We say str "starts with" ; ACL2-PC:: if that is the initial substring. It starts with a sign if it ; doesn't start with ACL2-PC:: or a normal letter. We return the xcode of the ; extended letter with which it starts. (cond ((acl2::string-matchp '(#\A #\C #\L #\2 #\- #\P #\C #\: #\:) str 0 (length str) nil nil) 91) ((member (char str 0) '(#\A #\B #\C #\D #\E #\F #\G #\H #\I #\J #\K #\L #\M #\N #\O #\P #\Q #\R #\S #\T #\U #\V #\W #\X #\Y #\Z)) (char-code (char str 0))) ((member (char str 0) '(#\a #\b #\c #\d #\e #\f #\g #\h #\i #\j #\k #\l #\m #\n #\o #\p #\q #\r #\s #\t #\u #\v #\w #\x #\y #\z)) (char-code (char-upcase (char str 0)))) (t 64))) (defun html-filename (filename n) (declare (ignore n)) ;(if (equal filename "-") "_hyphen_.html" (if (acl2::string-matchp '(#\-) filename 0 1 nil nil) (acl2::concatenate 'acl2::string "_hyphen_" (apply-filename-subst-table (coerce (cdr (coerce filename 'list)) 'string)) ".html") (acl2::concatenate 'acl2::string (apply-filename-subst-table filename) ".html"))) (defun xcode-char (xcode) ; Given the xcode of an extended character we return the character. See ; get-first-char-xcode. (cond ((= xcode 64) "Signs") ((= xcode 91) "ACL2-PC::") (t (coerce (list (code-char xcode)) 'string)))) (defun pair-letter-with-entry (xcode lst acc) ; Lst is the alphabetized list of items (strings) in the index. We assume that ; first in lst are all those items starting with signs, such as "+" and ">=", ; then come the rest in simple alphabetical order, except for those in the ; ACL2-PC package which are last. Each name in lst will be listed somewhere ; in the index and they will occur in the order listed in lst. Each index ; entry will have an anchor of the same name in this document. E.g., the entry ; for REWRITE will be embedded in .... So R will be a link in this document that will show a ; highlighted "R" and when you click on it you will go to the index entry for ; REWRITE. Of course, we need an anchor only for the first entry with a ; given first letter, but it is easier to give ourselves an anchor for every ; entry. (cond ((> xcode 91) (reverse acc)) ((null lst) (pair-letter-with-entry (1+ xcode) nil (cons (cons (xcode-char xcode) nil) acc))) (t (let ((xltr (get-first-char-xcode (car lst)))) (cond ((equal xcode xltr) (pair-letter-with-entry (1+ xcode) (cdr lst) (cons (cons (xcode-char xcode) (car lst)) acc))) ((< xcode xltr) (pair-letter-with-entry (1+ xcode) lst (cons (cons (xcode-char xcode) nil) acc))) (t (pair-letter-with-entry xcode (cdr lst) acc))))))) (defun write-html-index-toc1 (alist channel state) ; Alist contains entries of the form ("letter" . "name"), where "letter" ; is usually a single character but might be "ACL2-PC::" or the like. (cond ((null alist) (pprogn (newline channel state) (newline channel state))) ((null (cdar alist)) (pprogn (princ$ (caar alist) channel state) (princ$ " " channel state) (write-html-index-toc1 (cdr alist) channel state))) (t (pprogn (princ$ "" channel state) (princ$ (caar alist) channel state) (princ$ " " channel state) (write-html-index-toc1 (cdr alist) channel state))))) (defun write-html-index-toc (toc-alist channel state) ; Toc stands for "table of contents". The idea is to generate a short listing ; of the letters of the alphabet so that when you click on a letter you get ; sent to that part of the index that starts with that letter. E.g., clicking ; on R will take you to the indexed topics that begin with the letter R. ; The elements of toc-alist are ; '(("Signs" . "*STANDARD-CI*") ; ("A" . "ACL2-TUTORIAL") ; ... ; ("Z" . "ZERO-TEST-IDIOMS") ; ("ACL2-PC::" . "ACL2-PC::=")) ; where the cadrs are actually the string names of the first entries in ; lst with the given first (extended) character. (pprogn (princ$ "
    " channel state)
       (newline channel state)
       (write-html-index-toc1 toc-alist channel state)
       (princ$ "
    " channel state))) (defun merge-indexed-names (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((acl2::string-matchp '(#\A #\C #\L #\2 #\- #\P #\C #\: #\:) (car l1) 0 (length (car l1)) nil nil) (cond ((acl2::string-matchp '(#\A #\C #\L #\2 #\- #\P #\C #\: #\:) (car l2) 0 (length (car l2)) nil nil) (cond ((acl2::alpha-< (car l1) (car l2)) (cons (car l1) (merge-indexed-names (cdr l1) l2))) (t (cons (car l2) (merge-indexed-names l1 (cdr l2)))))) (t (cons (car l2) (merge-indexed-names l1 (cdr l2)))))) ((acl2::string-matchp '(#\A #\C #\L #\2 #\- #\P #\C #\: #\:) (car l2) 0 (length (car l2)) nil nil) (cons (car l1) (merge-indexed-names (cdr l1) l2))) ((acl2::alpha-< (car l1) (car l2)) (cons (car l1) (merge-indexed-names (cdr l1) l2))) (t (cons (car l2) (merge-indexed-names l1 (cdr l2)))))) (defun merge-sort-indexed-names (l) (cond ((null (cdr l)) l) (t (merge-indexed-names (merge-sort-indexed-names (acl2::evens l)) (merge-sort-indexed-names (acl2::odds l)))))) (defun write-html-index (index-file html-file major-topics-file doc-alist state undocumented-file) (let* ((indexed-names (merge-sort-indexed-names (strip-cars doc-alist))) (toc-alist (pair-letter-with-entry 64 indexed-names nil))) (mv-let (channel state) (acl2::open-output-channel index-file :character state) (pprogn (acl2::fms "~%~sv~%~ ~%" (list (cons #\v (acl2::f-get-global 'acl2::acl2-version state))) channel state nil) (write-header "Index" "Index" 1 channel state) (newline channel state) (newline channel state) ;; Print description (acl2::fms "This index lists all documented topics in ACL2, arranged ~ into sections. The first section is devoted to those ~ whose names begin with signs (and digits), such as ~ *STANDARD-CI* and 1+. Thereafter we have one section ~ for each of the 26 letters of the alphabet. The last ~ section is devoted to those topics in the ACL2-PC ~ package. By clicking on the appropriate entry of the ~ line below you can go to the corresponding section of ~ the index.~%~%You may use Find to search the ~ Index.~%~%We also provide an index based on " nil channel state nil) (princ$ "" channel state) (princ$ "Major Topics" channel state) (princ$ "." channel state) (newline channel state) (write-html-index-toc toc-alist channel state) (write-doc-menu1 indexed-names doc-alist html-file major-topics-file nil toc-alist channel state undocumented-file) (newline channel state) (newline channel state) (newline channel state) (write-trailer html-file nil channel state))))) (mutual-recursion (defun doc-tree-doc-length (doc-tree acc) (doc-tree-doc-length-lst (cddddr doc-tree) (+ acc (length (cadddr doc-tree))))) (defun doc-tree-doc-length-lst (doc-trees acc) (cond ((null doc-trees) acc) (t (doc-tree-doc-length-lst (cdr doc-trees) (doc-tree-doc-length (car doc-trees) acc))))) ) #| ;;; No need to maintain these anymore, since every doctopic now has its ;;; own filename (defun html-subsection-filename-alist (subsections) (if (null subsections) nil (cons (cons (car subsections) (html-filename (car subsections) 0)) (html-subsection-filename-alist (cdr subsections))))) (defun html-section-filename-alist (file-root doc-trees acc counter acc-size max-size) ; When acc-size is nil, it means that we're just now entering this function, ; and we should start a new section. (declare (xargs :guard (equal file-root file-root))) (cond ((null doc-trees) (mv counter acc)) ;;; added November, 2000, when doc topics began being stored in separate ;;; filenames. subsections now need to be put in the alist as well. ((not (null (caddr (car doc-trees)))) (html-section-filename-alist file-root (cdr doc-trees) (append (html-subsection-filename-alist (caddr (car doc-trees))) (cons (cons (caar doc-trees) (html-filename (caar doc-trees) counter)) acc)) (1+ counter) 0 max-size)) ;;; the following should now always be true, since I'm making max-size -1 ((or (null acc-size) (<= max-size acc-size)) (html-section-filename-alist file-root (cdr doc-trees) (cons (cons (caar doc-trees) (html-filename (caar doc-trees) counter)) acc) (1+ counter) 0 max-size)) (t (html-section-filename-alist file-root (cdr doc-trees) acc counter (doc-tree-doc-length (car doc-trees) acc-size) max-size)))) (defun html-filename-alist (file-root doc-trees acc counter max-size) (declare (xargs :guard (equal file-root file-root))) (cond ((null doc-trees) acc) ((< max-size (doc-tree-doc-length (car doc-trees) 0)) ;; then create separate files for some sections (mv-let (new-counter new-acc) (html-section-filename-alist file-root (cddddr (car doc-trees)) (cons (cons (caar doc-trees) (html-filename (caar doc-trees) counter)) acc) (1+ counter) nil max-size) (html-filename-alist file-root (cdr doc-trees) new-acc new-counter max-size))) (t (html-filename-alist file-root (cdr doc-trees) (cons (cons (caar doc-trees) (html-filename (caar doc-trees) counter)) acc) (1+ counter) max-size)))) |# (defun html-filename-alist (doctopic-names) (if (endp doctopic-names) nil (cons (cons (car doctopic-names) (html-filename (car doctopic-names) 0)) (html-filename-alist (cdr doctopic-names))))) (defun default-html-fmt-alist (doc-alist undocumented-filename acc) (cond ((null doc-alist) acc) (t (default-html-fmt-alist (cdr doc-alist) undocumented-filename (cons (list (caar doc-alist) (cons #\p (apply-html-subst-table (caar doc-alist) t)) ; (cons #\s ... why?) ; (cons #\A ... why?) (cons #\c undocumented-filename)) acc))))) (defun maybe-write-html-undocumented-file (undocumented-file state) (if undocumented-file (mv-let (channel state) (acl2::open-output-channel undocumented-file :character state) (pprogn (princ$ "

    Apparently this topic has not been documented.

    " channel state) (newline channel state))) state)) (defun write-html-file-fn (file-root spack vsubdir topics home-page-p-orig undocumented-file state) (let ((vsubdir (or vsubdir (version-subdirectory (acl2::f-get-global 'acl2::acl2-version state)))) (home-page-p (or home-page-p-orig (null topics))) (undocumented-file (if (eq undocumented-file t) (acl2::string-append file-root "-undocumented.html") undocumented-file))) (acl2::state-global-let* ((acl2::fmt-hard-right-margin 500 acl2::set-fmt-hard-right-margin) (acl2::fmt-soft-right-margin 480 acl2::set-fmt-soft-right-margin) (doc-tree-channel nil)) (let* ((html-file (acl2::string-append file-root ".html")) (major-topics-file (acl2::string-append file-root "-major-topics.html")) (index-file (acl2::string-append file-root "-index.html")) (doc-alist (doc-alist spack topics state)) (doc-trees (top-doc-trees doc-alist)) (filename-alist (html-filename-alist (all-doc-topics doc-alist nil)))) (pprogn (acl2::set-debugger-enable t) (chk-uniqueness-after-applying-subst spack state) (acl2::fms "Writing html file ~s0, default package ~p1.~%" (list (cons #\0 html-file) (cons #\1 (acl2::symbol-package-name spack))) (acl2::standard-co state) state nil) (acl2::state-global-let* ((acl2::doc-fmt-alist (html-fmt-alist doc-trees nil filename-alist nil))) ; Now we rebind doc-fmt-alist, adding an entry for the empty string. This ; string is looked up when we handle ~WARN[] (note the empty string inside the ; brackets). We have to do it this way because we cannot compute the url for ; |A Tiny Warning Sign| until we've set the doc-fmt-alist as above. ; Originally, we tried to replace the two nils above with this singleton ; initial value but the doc-url failed because the doc-fmt-alist it needs was ; nil. (pprogn (if (acl2::assoc-equal (html-node-name 'ACL2::|A Tiny Warning Sign| spack) (acl2::doc-fmt-alist state)) (acl2::f-put-global 'acl2::doc-fmt-alist (cons (cons "" ; For use in ~WARN[] when we lookup "" (list (cons #\w (doc-url 'ACL2::|A Tiny Warning Sign| spack html-file nil state)))) (acl2::doc-fmt-alist state)) state) state) (if home-page-p (write-home-page html-file spack vsubdir major-topics-file index-file state (and home-page-p-orig undocumented-file)) state) (write-major-topics doc-trees doc-alist html-file major-topics-file index-file state undocumented-file) (write-html-index index-file html-file major-topics-file doc-alist state undocumented-file) (maybe-write-html-undocumented-file undocumented-file state) (write-doc-tree-lst doc-trees "MAJOR-TOPICS" 1 doc-alist html-file major-topics-file index-file filename-alist state undocumented-file) (princ$ " done." (acl2::standard-co state) state) (newline (acl2::standard-co state) state) (acl2::value :invisible)))))))) (defmacro acl2::write-html-file (&key (file '"acl2-doc") (vsubdir 'nil) (topics 'nil) (pkg '"ACL2") (home-page-p 'nil) undocumented-file) ; This macro dumps the current ACL2 documentation tree, restricted to the ; indicated topics if that parameter is non-nil, in HTML format in the current ; directory. If you want to dump the entire tree, just execute ; (acl2::write-html-file). ; File specifies the top-level file, which is the ACL2 home page and is written ; only if topics is nil (the default) or home-page-p is t. In that case, ; vsubdir specifies the version subdirectory, which is set appropriately by ; default when necessary and can probably be ignored by users. ; Topics is nil by default, meaning that every documented topic (including ; those that come documented with ACL2) should be included. Otherwise, topics ; is a true list of topic names, e.g., '(FOO BAR "MY-PKG"). ; The pkg argument is used much like the current package in the LP ; read-eval-print loop, as follows. Any topic name in that package will ; correspond to itself, without its package prefix. But a topic name in ; another package will have the package prefix. For example, if the topic is ; 'BAR::DO-THIS, then the topic name is "BAR::DO-THIS" except that if however ; pkg is "BAR" then the topic name is "DO-THIS". ; If home-page-p and topics are both nil, then we insist that all topics be ; present under the home page. Set home-page-p explicitly to t to defeat that ; check (in which case links will go to the undocumented-file; see above). ; If undocumented-file is nil, then it is an error when writing out a link to ; an undocumented doc topics. ; Otherwise, undocumented-file is the name of the file to which undocumented ; topics link, where t is an abbreviation for FILE-undocumented.html. (list 'write-html-file-fn file (list 'quote (acl2::pkg-witness pkg)) vsubdir topics home-page-p undocumented-file 'state)) acl2-sources/doc/write-acl2-texinfo.lisp0000664002132200015000000012227112222120055017630 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 was produced by modifying ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTES-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of Version 2 of the GNU General Public License as ; published by the Free Software Foundation. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Sciences ; University of Texas at Austin ; Austin, TX 78712-1188 U.S.A. ; Texinfo driver for ACL2 documentation. Sample usage: ; (write-texinfo-file "acl2-doc") or even ; (write-texinfo-file) ; Art Flatau and Matt Kaufmann. ; This file defines the macro write-texinfo-file, which writes a texinfo file. ; It takes all the documentation strings that are available via the ACL2 :DOC ; command and creates the texinfo file. An earlier version written by Art ; Flatau created an info file directly. ; A subtelty: How do are links printed in info? That is, how is @xref{foo} ; interpreted in the info setting? It is literally printed as "*Note foo::" ; in the info files, BUT in Lucid emacs there is a variable, Info-footnote-tag, ; that allows "Note" to be replaced by another string. By default, ; Info-footnote-tag is "See". HOWEVER, emacs 19 does not seem to have this ; capability. Now in the printed version a "See" is introduced; therefore, we ; would like to have "See" in the info version. We do *not* want to use the ; present facility to print "See", because then the printed version will say ; "See See"! So, we will simply print out the @xref, and let texinfo turn that ; into *Note, and assume that either the user uses Lucid emacs (which changes ; *Note to *See) or uses emacs 19 (and the human reads "*Note" as "See" or ; "see" in the text). It is easy enough to use tags-query-replace to create a ; version of the info files that replaces "*Note" by "See *Note"; this in fact ; gives a pretty good emacs 19 solution. ; One other subtlety: This code assumes that when references to topics ; containing the special characters @, {, and } are used in ~l[..], then there ; is a comma or period after the right brace. There should also _not_ be such ; a punctuation mark after ~pl, but it's probably rather harmless to have it ; (the result probably being one extra punctuation mark). In fact the ; documenation for texinfo makes such punctuation requirements absolute, but ; when the referenced topic does not contain special characters, we seem to be ; able to relax that restriction completely. (in-package "ACL2-USER") ;; We need to be in :program mode, as we have not tried to reason ;; about functions in this file. (acl2::set-state-ok t) (acl2::program) ; We want to link in the dpANS documentation to the info documentation. (defconst *gcl-info-top* "/cli/project/gcl/info/gcl.info") ;; First some macros so we don't have to have acl2:: all over the place. ; (defmacro true-listp (x) ; `(acl2::true-listp ,x)) ; (defmacro pprogn (&rest lst) (cons 'acl2::pprogn lst)) ; (defmacro princ$ (x channel state-state) ; `(acl2::princ$ ,x ,channel ,state-state)) ; (defmacro newline (channel state-state) ; `(acl2::newline ,channel ,state-state)) ; (defmacro strip-cars (x) `(acl2::strip-cars ,x)) ; (defmacro assoc-equal (x alist) `(acl2::assoc-equal ,x ,alist)) ; Now for some additional utilities. (defconst *texinfo-doc-char-subst-table* '((#\@ #\@ #\@) (#\{ #\@ #\{) (#\} #\@ #\})) "Table for substituting characters for texinfo.") ; Replace `|' by `:' to get a version of this that creates truly nice tex ; output. (defun texinfo-string-node-name (pkg-name name tex-only-flg) ; Node names in texinfo had better not contain #\@. So, we do not use ; acl2::apply-char-subst-table here. If we need to escape some characters, ; we'll do that elsewhere, e.g. in printing menus and sections. (cond ((equal pkg-name "KEYWORD") (if tex-only-flg (concatenate 'string "{\tt\char'72}" name) (concatenate 'string ":" name))) (pkg-name (concatenate 'string pkg-name "||" name)) (t name))) (defun upcase-substring1 (str lower weak-upper ans) ; Returns substring of str between lower and weak-upper, inclusive, for ans=nil. (cond ((< weak-upper lower) (coerce ans 'string)) (t (upcase-substring1 str lower (1- weak-upper) (cons (char-upcase (char str weak-upper)) ans))))) (defun upcase-substring (str lower upper) (upcase-substring1 str lower (1- upper) nil)) (defun texinfo-parse-string (x) (let* ((len (length x)) (posn (acl2::posn-char-stringp #\: x (1- (length x))))) (cond ((null posn) (mv nil x)) ((= posn 0) (mv "KEYWORD" (upcase-substring x 1 len))) (t (mv (upcase-substring x 0 (if (eql (char x (1- posn)) #\:) (1- posn) posn)) (upcase-substring x (+ 1 posn) len)))))) (defun texinfo-node-name (x spack tex-only-flg) (cond ((stringp x) (if (equal x "Top") x (mv-let (pkg n) (texinfo-parse-string x) (cond ((or (not (equal (acl2::symbol-package-name spack) pkg)) (equal pkg "KEYWORD")) (texinfo-string-node-name pkg n tex-only-flg)) (t (texinfo-string-node-name nil n tex-only-flg)))))) ((symbolp x) (let ((name (symbol-name x)) (pkg-name (acl2::symbol-package-name x))) (cond ((or (equal pkg-name "KEYWORD") (not (eq (acl2::intern-in-package-of-symbol name spack) x))) (texinfo-string-node-name pkg-name name tex-only-flg)) (t (texinfo-string-node-name nil name tex-only-flg))))) (t (acl2::er acl2::hard 'texinfo-node-name "Apparent attempt to document topic that is neither a string ~ nor a symbol, ~p0" x)))) (defun texinfo-node-name-lst (lst spack tex-only-flg) (if lst (cons (texinfo-node-name (car lst) spack tex-only-flg) (texinfo-node-name-lst (cdr lst) spack tex-only-flg)) nil)) (defun doc-alist1 (raw-doc-alist spack tex-only-flg ans) (cond ((null raw-doc-alist) (reverse ans)) (t (doc-alist1 (cdr raw-doc-alist) spack tex-only-flg (cons (list* (texinfo-node-name (caar raw-doc-alist) spack tex-only-flg) (texinfo-node-name (cadar raw-doc-alist) spack tex-only-flg) (texinfo-node-name-lst (caddar raw-doc-alist) spack tex-only-flg) (cdddar raw-doc-alist)) ans))))) (defun filter-topics1 (topics doc-alist acc) ; At one time we exploited the "fact" that if every topic's section appears ; earlier in doc-alist than it does, in order to guarantee that we include the ; entire cone underneath a topic. But this "fact" isn't true, e.g., for ; arrays and aref1. (cond ((null doc-alist) (reverse acc)) ((acl2::member-equal (cadar doc-alist) topics) ;; then, section exists (filter-topics1 topics (cdr doc-alist) (cons (car doc-alist) acc))) ((acl2::member-equal (caar doc-alist) topics) ;; otherwise, the current topic should be viewed as a section (filter-topics1 topics (cdr doc-alist) (cons (list* (caar doc-alist) (caar doc-alist) (cddar doc-alist)) acc))) (t (filter-topics1 topics (cdr doc-alist) acc)))) (defun extend-with-immediate-subtopics (topics doc-alist) (cond ((null doc-alist) topics) ((acl2::member-equal (caar doc-alist) topics) (extend-with-immediate-subtopics topics (cdr doc-alist))) ((acl2::member-equal (cadar doc-alist) topics) (extend-with-immediate-subtopics (cons (caar doc-alist) topics) (cdr doc-alist))) (t (extend-with-immediate-subtopics topics (cdr doc-alist))))) (defun all-subtopics (topics doc-alist) (let ((extended-topics (extend-with-immediate-subtopics topics doc-alist))) (if (equal extended-topics topics) topics (all-subtopics extended-topics doc-alist)))) (defun filter-topics (topics doc-alist) (if (null topics) doc-alist (filter-topics1 (all-subtopics topics doc-alist) doc-alist nil))) (defun doc-alist (spack tex-only-flg topics state) (doc-alist1 (filter-topics topics (acl2::global-val 'acl2::documentation-alist (acl2::w state))) spack tex-only-flg nil)) (defconst *top-node* '("Top" nil nil "")) ; First we print a prelude in the file, then we go down the list of documented ; symbols (a la what :docs ** does) and print the documentation (similar to ; what PRINT-DOC does). (defconst *prelude-string* "\\input texinfo @c -*-texinfo-*- @comment %**start of header (This is for running Texinfo on a region.) @setfilename ~sf @settitle ~sv @paragraphindent 0 @comment %**end of header (This is for running Texinfo on a region.) @iftex @finalout @parindent 0mm @end iftex @ifinfo This is documentation for ~sv@* Copyright @copyright{} 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify@* it under the terms of Version 2 of the GNU General Public License as@* published by the Free Software Foundation. This program is distributed in the hope that it will be useful,@* but WITHOUT ANY WARRANTY; without even the implied warranty of@* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the@* GNU General Public License for more details. You should have received a copy of the GNU General Public License@* along with this program; if not, write to the Free Software@* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore@* Department of Computer Sciences@* University of Texas at Austin@* Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math@* START-INFO-DIR-ENTRY@* * acl2: (acl2-doc-emacs). Applicative Common Lisp@* END-INFO-DIR-ENTRY @end ifinfo @setchapternewpage odd @titlepage @sp 11 @center @titlefont{ACL2} @sp 2 @center by Matt Kaufmann and J Strother Moore @sp 2 @center ~sv @sp 2 @center ~sm ~fy @page @vskip 0pt plus 1filll Copyright @copyright{} 2013 University of Texas at Austin Distributed under the terms of the GNU General Public License. @sp 2 This is documentation for ~sv @* @sp 2 Published by University of Texas at Austin. Department of Computer Sciences @* University of Texas at Austin @* Austin, TX 78712-1188 U.S.A. @* @end titlepage @page @iftex NOTE: The ACL2 documentation is in hypertext form. It is best read with an appropriate browser and is available in both HTML and Emacs Texinfo formats. See the file doc/README. If you are using the documentation as an introduction to ACL2, start with the chapter entitled ``ACL2-TUTORIAL''. This is a Postscript version of the hypertext document. While somewhat awkward to use since the links are ``dead'', it may serve as a reference manual. In this Postscript version, links to other nodes will be implicitly indicated with the mark ^. For example, you may see books (BOOKS^) --- this indicates that there is documentation present on BOOKS. The table of contents, as well as the index, will show you where to find the node named ``BOOKS''. Some links are followed by the sign ``<>''. This is our way of denoting the ``Warning Sign'' used in the HTML version of the guided tours. Such links lead from material that is on a guided tour into material that is not on a guided tour. If you are trying to follow a guided tour with this Postscript version of the documentation, we advise you not to follow such links. The guided tours are best read with an appropriate hypertext browser. @page @end iftex @node Top, , , (DIR) @ifinfo This manual documents ~sv. @end ifinfo " "FMT string for prelude: v = version, m = month, d = day of month, y = year and f = file-name") (defconst *section-types* '("chapter" "section" "subsection" "subsubsection") "List of sections types used by texinfo.") (defconst *texinfo-doc-markup-table* '(("-" nil ."---") ("B" nil ."@b{~st}") ("BF" nil ."~%@example") ;We'd prefer @format, but it doesn't ; seem to work for info ("BID" nil . "") ;begin implementation dependent ("BQ" nil ."~%@quotation") ("BV" nil ."~%@example") ("C" nil ."@t{~st}") ;originally @code, but we don't want `' in info file ("EF" nil ."@end example~%") ("EID" nil . "") ;end implementation dependent ("EM" nil ."@emph{~st}") ;emphasis ("EQ" nil ."~%@end quotation~%") ;we need leading line break to ; avoid problems with @refill ("EV" nil ."@end example~%") ("GIF" nil . "") ;gif file (currently only for HTML) ("I" nil ."@i{~st}") ("ID" nil . "") ;implementation dependent ("IL" t ."~st") ("ILC" t . "@t{~st}") ;originally @code, but we don't want `' in ; info file ("L" t ."@xref{~sL}") ;used to start a sentence with a crossref ("NL" nil ."@*~%") ("PAR" nil . "") ;paragraph mark, of no significance for texinfo ("PL" t ."@pxref{~sL}") ;used for parenthetical crossrefs ("SC" nil ."@sc{~st}") ;small caps ("ST" nil ."@strong{~st}") ;strong emphasis ("T" nil ."@t{~st}") ("TERMINAL" nil . "") ; terminal only, ignore ("WARN" nil . "<>") ("CLICK-HERE" t . "@xref{~sL}") ("PCLICK-HERE" t . "@pxref{~sL}") ("FLY" t . "Next on the Flying Tour: @pxref{~sL}") ("LARGE-FLY" t . "Next on the Flying Tour: @pxref{~sL}") ("WALK" t . "Next on the Walking Tour: @pxref{~sL}") ("LARGE-WALK" t . "Next on the Walking Tour: @pxref{~sL}") ("URL" nil ."@uref{~st}") ) "Table for use in printing documentation strings, when printing to a TeXinfo file to be used to create an info file. See also *tex-doc-markup-table* and *non-lucid-texinfo-doc-markup-table*.") (defconst *tex-doc-markup-table* '(("-" nil ."---") ("B" nil ."@b{~st}") ("BF" nil ."~%@format") ("BID" nil . "") ;begin implementation dependent ("BQ" nil ."~%@quotation") ("BV" nil ."~%@example") ("C" nil ."@t{~st}") ;originally @code, but we don't want `' in info file ("EF" nil ."@end format~%") ("EID" nil . "") ;end implementation dependent ("EM" nil ."@emph{~st}") ;emphasis ("EQ" nil ."~%@end quotation~%") ;we need leading line break to avoid problems with @refill ("EV" nil ."@end example~%") ("GIF" nil . "") ;gif file (currently only for HTML) ("I" nil ."@i{~st}") ("ID" nil . "") ;implementation dependent ("IL" t ."~st (~sp^)") ("ILC" t . "@t{~st} (~sp^)") ;originally @code, but we don't want `' in info file ("L" t ."See @ref{~sL}") ;used to start a sentence with a crossref ("NL" nil ."@*~%") ("PAR" nil . "") ;paragraph mark, of no significance for texinfo ("PL" t ."@pxref{~sL}") ;used for parenthetical crossrefs ("SC" nil ."@sc{~st}") ;small caps ("ST" nil ."@strong{~st}") ;strong emphasis ("T" nil ."@t{~st}") ("TERMINAL" nil . "") ; terminal only, ignore ("WARN" nil . "<>") ("CLICK-HERE" t . "See @ref{~sL}") ("PCLICK-HERE" t . "@pxref{~sL}") ("FLY" t . "Next on the Flying Tour: @pxref{~sL}") ("LARGE-FLY" t . "Next on the Flying Tour: @pxref{~sL}") ("LARGE-WALK" t . "Next on the Walking Tour: @pxref{~sL}") ("WALK" t . "Next on the Walking Tour: @pxref{~sL}") ("URL" nil ."@t{~st}") ) "Table for use in printing documentation strings, when printing to a TeX file. See also *texinfo-doc-markup-table* and *non-lucid-texinfo-doc-markup-table*. We highlight invisible links by suffixing them with ^.") (defconst *non-lucid-texinfo-doc-markup-table* '(("-" nil ."---") ("B" nil ."@b{~st}") ("BF" nil ."~%@example") ;We'd prefer @format, but it doesn't seem to work for info ("BID" nil . "") ;begin implementation dependent ("BQ" nil ."~%@quotation") ("BV" nil ."~%@example") ("C" nil ."@t{~st}") ;originally @code, but we don't want `' in info file ("EF" nil ."@end example~%") ("EID" nil . "") ;end implementation dependent ("EM" nil ."@emph{~st}") ;emphasis ("EQ" nil ."~%@end quotation~%") ;we need leading line break to avoid problems with @refill ("EV" nil ."@end example~%") ("GIF" nil . "") ;gif file (currently only for HTML) ("I" nil ."@i{~st}") ("ID" nil . "") ;implementation dependent ("IL" t ."~st") ("ILC" t . "@t{~st}") ;originally @code, but we don't want `' in info file ("L" t ."See @ref{~sL}") ;used to start a sentence with a crossref ("NL" nil ."@*~%") ("PAR" nil . "") ;paragraph mark, of no significance for texinfo ("PL" t ."see @pxref{~sL}") ;used for parenthetical crossrefs ("SC" nil ."@sc{~st}") ;small caps ("ST" nil ."@strong{~st}") ;strong emphasis ("T" nil ."@t{~st}") ("TERMINAL" nil . "") ; terminal only, ignore ("WARN" nil . "<>") ("CLICK-HERE" t . "See @ref{~sL}") ("PCLICK-HERE" t . "see @pxref{~sL}") ("LARGE-FLY" t . "Next on the Flying Tour: @pxref{~sL}") ("FLY" t . "Next on the Flying Tour: @pxref{~sL}") ("LARGE-WALK" t . "Next on the Walking Tour: @pxref{~sL}") ("WALK" t . "Next on the Walking Tour: @pxref{~sL}") ("URL" nil ."@uref{~st}") ) "Table for use in printing documentation strings, when printing to a TeXinfo file to be used to create an info file. See also *tex-doc-markup-table* and *texinfo-doc-markup-table*.") (defun texinfo-doc-markup-table (state) (acl2::f-get-global 'texinfo-doc-markup-table state)) (defun write-prelude (info-file channel state) "Writes the prelude to CHANNEL." (mv-let (n state) (acl2::read-idate state) (let* ((date-list (acl2::decode-idate n)) (month (nth (1- (car (cddddr date-list))) '("January" "February" "March" "April" "May" "June" "July" "August" "September" "October" "November" "December"))) (yr (+ 1900 (cadr (cddddr date-list))))) (pprogn (mv-let (col state) (acl2::fmt1 *prelude-string* (list (cons #\v (acl2::f-get-global 'acl2::acl2-version state)) (cons #\d (cadddr date-list)) (cons #\m month) (cons #\y yr) (cons #\f info-file)) 0 channel state nil) (declare (ignore col)) (newline channel state)) (newline channel state))))) (defun apply-texinfo-subst-table (name) (acl2::apply-char-subst-table name *texinfo-doc-char-subst-table* nil)) (defconst *texinfo-doc-link-subst-table* '((#\@ #\a #\t #\s #\i #\g #\n) (#\{ #\l #\e #\f #\t #\b #\r #\a #\c #\e) (#\} #\r #\i #\g #\h #\t #\b #\r #\a #\c #\e)) "Table for substituting characters into linknames for texinfo.") (defun bad-chars-p (name) (let ((len (1- (length name)))) (or (acl2::member-char-stringp #\@ name len) (acl2::member-char-stringp #\{ name len) (acl2::member-char-stringp #\} name len)))) (defun apply-texinfo-link-subst-table (name) (acl2::apply-char-subst-table name *texinfo-doc-link-subst-table* nil)) (defun get-link-string (name) (if (equal name "Top") "Top" (acl2::apply-char-subst-table name *texinfo-doc-link-subst-table* nil))) (defun write-header (current next previous up section-type tex-only-flg channel state) "Writes node header information for node CURRENT, Next: NEXT, Previous: PREVIOUS, Up: UP" (pprogn ;; The first form is here to correct an apparent bug in ;; texinfo-format-buffer, in both emacs 19.22 and Lucid Emacs 19.10, which ;; comes up when a doc string ends with "~l[...]." on a line by itself. (if tex-only-flg state (pprogn (princ$ "@*" channel state) (newline channel state))) (princ$ "@node " channel state) (princ$ (get-link-string (car current)) channel state) (if next (pprogn (princ$ ", " channel state) (princ$ (get-link-string (car next)) channel state)) (princ$ ", " channel state)) (if previous (pprogn (princ$ ", " channel state) (princ$ (get-link-string (car previous)) channel state)) (princ$ ", " channel state)) (if up (pprogn (princ$ ", " channel state) (princ$ (get-link-string (car up)) channel state)) (princ$ ", " channel state)) (newline channel state) (princ$ "@comment node-name, next, previous, up" channel state) (newline channel state) (if section-type (let ((name (apply-texinfo-subst-table (car current)))) (pprogn (newline channel state) (princ$ "@iftex" channel state) (newline channel state) (princ$ "@" channel state) (princ$ section-type channel state) (princ$ " " channel state) (princ$ name channel state) (newline channel state) (princ$ "@end iftex" channel state) (newline channel state) (princ$ "@cindex " channel state) (princ$ name channel state) (newline channel state))) state))) (defun write-menu-header (tex-only-flg channel state) (pprogn (newline channel state) (if tex-only-flg (princ$ "@format" channel state) (princ$ "@menu" channel state)) (newline channel state))) (defun write-doc-menu-item (name str channel state) (pprogn (princ$ "* " channel state) (princ$ (get-link-string name) channel state) (princ$ ":: " channel state) (if (bad-chars-p name) (pprogn (princ$ "(" channel state) (princ$ (apply-texinfo-subst-table name) channel state) (princ$ ") " channel state)) state) (acl2::print-doc-string-part 0 str "" (texinfo-doc-markup-table state) *texinfo-doc-char-subst-table* (acl2::doc-fmt-alist state) channel name nil nil ; undocumented-file nil ; vp state))) (defun write-doc-menu1 (lst doc-alist tex-only-flg channel state) ;; lst is a list of names (cond ((null lst) state) (t (pprogn (write-doc-menu-item (car lst) (cadddr (assoc-equal (car lst) doc-alist)) channel state) (if tex-only-flg state (newline channel state)) (write-doc-menu1 (cdr lst) doc-alist tex-only-flg channel state))))) (defun write-doc-menu-aux (doc-tuple doc-alist tex-only-flg channel state) "Writes a menu with entries from the list LST of names." (cond ((null (caddr doc-tuple)) state) (t (let* ((main-subitems (strip-cars (cddddr doc-tuple))) (other-menu-items (acl2::merge-sort-alpha-< (if main-subitems (acl2::set-difference-equal (caddr doc-tuple) main-subitems) (caddr doc-tuple))))) (pprogn (write-menu-header tex-only-flg channel state) (write-doc-menu1 main-subitems doc-alist tex-only-flg channel state) (cond (other-menu-items (pprogn (newline channel state) (princ$ "Related topics other than immediate subtopics:" channel state) (newline channel state) (write-doc-menu1 other-menu-items doc-alist tex-only-flg channel state))) (t state)) (if tex-only-flg (princ$ "@end format" channel state) (princ$ "@end menu" channel state)) (newline channel state) (newline channel state)))))) (defun write-doc-menu (doc-tuple doc-alist tex-only-flg channel state) (pprogn (write-doc-menu-aux doc-tuple doc-alist nil channel state) (if tex-only-flg (write-doc-menu-aux doc-tuple doc-alist t channel state) state))) (defun write-a-doc-section (doc-tuple next previous up section-type doc-alist tex-only-flg channel state) ; In fact doc-tuple may really be a doc-tree, but that's ok. (pprogn (write-header doc-tuple next previous up section-type tex-only-flg channel state) (princ$ "@format" channel state) (newline channel state) (princ$ (apply-texinfo-subst-table (car doc-tuple)) channel state) (princ$ "@t{ }" channel state) (acl2::print-doc-string-part 0 (cadddr doc-tuple) "" (texinfo-doc-markup-table state) *texinfo-doc-char-subst-table* (acl2::doc-fmt-alist state) channel (car doc-tuple) nil nil ; undocumented-file nil ; vp state) (princ$ "@end format" channel state) (newline channel state) (acl2::print-doc-string-part 1 (cadddr doc-tuple) "" (texinfo-doc-markup-table state) *texinfo-doc-char-subst-table* (acl2::doc-fmt-alist state) channel (car doc-tuple) nil nil ; undocumented-file nil ; vp state) (newline channel state) (write-doc-menu doc-tuple doc-alist tex-only-flg channel state) (acl2::print-doc-string-part 2 (cadddr doc-tuple) "" (texinfo-doc-markup-table state) *texinfo-doc-char-subst-table* (acl2::doc-fmt-alist state) channel (car doc-tuple) t nil ; undocumented-file nil ; vp state) (newline channel state))) (mutual-recursion (defun write-doc-tree-lst (doc-trees previous up section-types doc-alist tex-only-flg channel state) (cond ((null doc-trees) state) (t (pprogn (write-doc-tree (car doc-trees) (cadr doc-trees) previous up section-types doc-alist tex-only-flg channel state) (write-doc-tree-lst (cdr doc-trees) (car doc-trees) up section-types doc-alist tex-only-flg channel state))))) (defun write-doc-tree (doc-tree next previous up section-types doc-alist tex-only-flg channel state) ;; (doc-tuple next previous up section-type channel state) (pprogn (write-a-doc-section doc-tree next previous up (car section-types) doc-alist tex-only-flg channel state) (write-doc-tree-lst (cddddr doc-tree) doc-tree doc-tree (cdr section-types) doc-alist tex-only-flg channel state))) ) (defun write-texinfo-file1 (doc-trees info-file doc-alist tex-only-flg channel state) (pprogn (write-prelude info-file channel state) (pprogn (write-menu-header nil channel state) (newline channel state) (write-doc-menu1 (strip-cars doc-trees) doc-alist nil channel state) (princ$ "* Index:: An item for each documented ACL2 item." channel state) (newline channel state) (princ$ "@end menu" channel state) (newline channel state)) (newline channel state) (write-doc-tree-lst doc-trees *top-node* *top-node* *section-types* doc-alist tex-only-flg channel state) (newline channel state) ;; See comment in write-header (if tex-only-flg state (pprogn (princ$ "@*" channel state) (newline channel state))) (princ$ "@node Index, , , Top" channel state) (newline channel state) (princ$"@comment node-name, next,previous, up" channel state) (newline channel state) (princ$ "@unnumbered Index" channel state) (newline channel state) (newline channel state) (princ$ "@printindex cp" channel state) (newline channel state) (princ$ "@contents" channel state) (newline channel state) (princ$ "@bye" channel state) (newline channel state))) ; Recall that a doc tuple of of the form ; (node-name section-name menu-items doc-string). ; A doc tree is a node of a similar form, extended by children ; (node-name section-name menu-items doc-string . children) ; where each member of children is a doc tree. In fact section-name ; is the parent: so, if the node-name in a doc tuple is the same as ; its section-name, we replace the section-name with "Top". ; The top-level doc tree is ; ("Top" nil "The top level node." ; . ) (defun extend-assoc-equal (key val alist) (cond ((null alist) (list (cons key (list val)))) ((equal key (caar alist)) (cons (cons key (cons val (cdar alist))) (cdr alist))) (t (cons (car alist) (extend-assoc-equal key val (cdr alist)))))) (defun section-alist (doc-alist ans) ; Associates each section name with the doc-tuples in its section. (cond ((null doc-alist) ans) (t (let ((doc-tuple (car doc-alist))) (section-alist (cdr doc-alist) (extend-assoc-equal (cadr doc-tuple) doc-tuple ans)))))) (defun find-node-val (val alist acc) ; Returns (cons entry rest-alist), where val is a member-equal of (strip-cars ; (cdr entry)), entry is a member-equal of alist, and rest-alist is the result ; of deleting the first occurrence of entry from alist. Except, if there is no ; such entry, then nil is returned. Called with acc = NIL. (cond ((null alist) nil) ((assoc-equal val (cdar alist)) (cons (car alist) (revappend acc (cdr alist)))) (t (find-node-val val (cdr alist) (cons (car alist) acc))))) (defun sort-section-alist (section-alist ans) ; When we see an entry (section . doc-tuples), we want to be sure that section ; appears in ans before we add it, unless section is "Top". That way, we are ; sure that every section key in ans appears as a doc-tuple later in ans. ; If the "graph" is circular in section-alist, this will not terminate! (cond ((null section-alist) ans) (t (let ((new-section-alist (find-node-val (caar section-alist) (cdr section-alist) (list (car section-alist))))) (if new-section-alist (sort-section-alist new-section-alist ans) (sort-section-alist (cdr section-alist) (cons (car section-alist) ans))))))) (defun append0 (x y) (if (null y) x (append x y))) (defun build-doc-tree-lst (doc-tuples section-alist) ; Here, if a doc-tuple represents a section, then that section should already ; be associated with a list of doc-trees in section-alist. (cond ((null doc-tuples) nil) (t (cons (append0 (car doc-tuples) (cdr (assoc-equal (caar doc-tuples) section-alist))) (build-doc-tree-lst (cdr doc-tuples) section-alist))))) (defun merge-car-alpha-< (l1 l2) (cond ((null l1) l2) ((null l2) l1) ((acl2::alpha-< (car (car l1)) (car (car l2))) (cons (car l1) (merge-car-alpha-< (cdr l1) l2))) (t (cons (car l2) (merge-car-alpha-< l1 (cdr l2)))))) (defun merge-sort-car-alpha-< (l) (cond ((null (cdr l)) l) (t (merge-car-alpha-< (merge-sort-car-alpha-< (acl2::evens l)) (merge-sort-car-alpha-< (acl2::odds l)))))) (defun expand-section-alist (section-alist acc) ; Replaces each doc tuple in section-alist with a doc tree. We assume that ; section-alist is sorted "bottom up", i.e., each section's doc tuple appears ; later in section-alist than does its entry. The resulting expanded section ; alist is returned in reverse order, so that "Top" is at the front. We ; sort the newly-appended children alphabetically. (cond ((null section-alist) acc) (t (expand-section-alist (cdr section-alist) (cons (cons (caar section-alist) (build-doc-tree-lst (merge-sort-car-alpha-< (cdar section-alist)) acc)) acc))))) (defun insert-top-in-doc-alist (doc-alist ans) (cond ((null doc-alist) (reverse ans)) ((equal (caar doc-alist) (cadar doc-alist)) (insert-top-in-doc-alist (cdr doc-alist) (cons (list* (caar doc-alist) "Top" (cddar doc-alist)) ans))) (t (insert-top-in-doc-alist (cdr doc-alist) (cons (car doc-alist) ans))))) (defun top-doc-trees (doc-alist) ; The idea, as I recall (documenting this way after the code was written) is ; that we take (cdr (car ...)) in order to obtain the sections immediately ; under "Top", where the entry for "Top" appears first. (cdr (car (expand-section-alist (sort-section-alist (section-alist (insert-top-in-doc-alist doc-alist nil) nil) nil) nil)))) (defun texinfo-fmt-alist (doc-alist spack acc) (cond ((null doc-alist) acc) (t (texinfo-fmt-alist (cdr doc-alist) spack (let ((raw-name (texinfo-node-name (caar doc-alist) spack t))) (cons (list* raw-name (cons #\T (string-upcase raw-name)) ;presumably already upper case (cond ((bad-chars-p raw-name) (let ((link-name (apply-texinfo-link-subst-table raw-name))) (list (cons #\l link-name) (cons #\L (concatenate 'string link-name ",," (apply-texinfo-subst-table raw-name)))))) (t (list (cons #\l raw-name) (cons #\L raw-name))))) acc)))))) (defmacro my-state-global-let* (bindings form) ; Like acl2::state-global-let*, but where form returns state. `(mv-let (erp val state) (acl2::state-global-let* ,bindings (pprogn ,form (acl2::value nil))) (declare (ignore erp val)) state)) (defun write-texinfo-file-fn (texinfo-file info-file spack tex-only-flg tex-only-flg-supplied-p non-lucid-flg topics state) (pprogn (acl2::set-debugger-enable t) (acl2::f-put-global 'texinfo-doc-markup-table (cond (tex-only-flg *tex-doc-markup-table*) (non-lucid-flg *non-lucid-texinfo-doc-markup-table*) (t *texinfo-doc-markup-table*)) state) (acl2::fms "Writing texinfo file ~s0, default package ~p1.~%" (list (cons #\0 texinfo-file) (cons #\1 (acl2::symbol-package-name spack))) (acl2::standard-co state) state nil) (my-state-global-let* ((acl2::bar-sep-p t) ; print :: as || in node names (acl2::write-for-read t) (acl2::fmt-soft-right-margin 100000 acl2::set-fmt-soft-right-margin) (acl2::fmt-hard-right-margin 200000 acl2::set-fmt-hard-right-margin) (acl2::doc-fmt-alist (texinfo-fmt-alist (acl2::global-val 'acl2::documentation-alist (acl2::w state)) spack nil))) (let ((doc-alist (doc-alist spack tex-only-flg topics state))) (mv-let (channel state) (acl2::open-output-channel texinfo-file :character state) (write-texinfo-file1 (top-doc-trees doc-alist) info-file doc-alist tex-only-flg channel state)))) (acl2::fms "Done.~%" nil (acl2::standard-co state) state nil) (if (not tex-only-flg) (acl2::fms "In emacs, use meta-x texinfo-format-buffer on file ~s0 to ~ create info file ~s1.~%" (list (cons #\0 texinfo-file) (cons #\1 info-file)) (acl2::standard-co state) state nil) state) (if (or tex-only-flg (not tex-only-flg-supplied-p)) (acl2::fms "At the shell, invoke~%~%texi2dvi ~s0~%~%to create .dvi file.~%" (list (cons #\0 texinfo-file)) (acl2::standard-co state) state nil) state) (acl2::value :invisible))) (defmacro acl2::write-texinfo-file (&key (dir-string '"") (file '"acl2-doc") (sym 'acl2::rewrite) (pkg '"ACL2") (tex-only-flg 'nil tex-only-flg-supplied-p) (non-lucid-flg 'nil) (topics 'nil)) (if (and tex-only-flg non-lucid-flg) '(acl2::er acl2::soft "It makes no sense to supply non-nil values for both ~ tex-only-flg and non-lucid-flg.") (list 'if `(equal (acl2::symbol-package-name ',sym) ,pkg) (list 'write-texinfo-file-fn (acl2::concatenate 'string dir-string file (if tex-only-flg ".tex" ".texinfo")) (acl2::string-append file ".info") (list 'quote sym) tex-only-flg tex-only-flg-supplied-p non-lucid-flg topics 'state) `(acl2::er acl2::soft 'acl2::write-texinfo-file "The second argument of WRITE-TEXINFO-FILE needs to be in ~ the package specified in the third argument (default: ~ \"ACL2\"). However, the symbol ~p0 is in package ~p1, ~ not in package ~p2." ',sym (acl2::symbol-package-name ',sym) ,pkg)))) (defmacro acl2::write-tex-file (&key (dir-string '"") (file '"acl2-doc") (sym 'acl2::rewrite) (pkg '"ACL2") (topics 'nil)) `(acl2::write-texinfo-file :dir-string ,dir-string :file ,file :sym ,sym :pkg ,pkg :tex-only-flg t :topics ,topics)) acl2-sources/doc/EMACS/0002775002132200015000000000000012222333765014157 5ustar kaufmannacl2acl2-sources/doc/EMACS/acl2-doc-emacs.info0000664002132200015000000013625212222333542017506 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  Indirect: acl2-doc-emacs.info-1: 1042 acl2-doc-emacs.info-2: 296041 acl2-doc-emacs.info-3: 594532 acl2-doc-emacs.info-4: 891094 acl2-doc-emacs.info-5: 1190722 acl2-doc-emacs.info-6: 1490069 acl2-doc-emacs.info-7: 1788891 acl2-doc-emacs.info-8: 2086831 acl2-doc-emacs.info-9: 2384624 acl2-doc-emacs.info-10: 2682723 acl2-doc-emacs.info-11: 2978561 acl2-doc-emacs.info-12: 3278154 acl2-doc-emacs.info-13: 3566414 acl2-doc-emacs.info-14: 3846955 acl2-doc-emacs.info-15: 4143845  Tag Table: (Indirect) Node: Top1042 Node: ABOUT-ACL22809 Node: ACL2-TUTORIAL3604 Node: ACL2-AS-STANDALONE-PROGRAM6995 Node: ACL2-SEDAN9947 Node: ADVANCED-FEATURES11086 Node: ALTERNATIVE-INTRODUCTION25587 Node: ANNOTATED-ACL2-SCRIPTS54510 Node: SOLUTION-TO-SIMPLE-EXAMPLE57720 Node: TUTORIAL1-TOWERS-OF-HANOI59670 Node: TUTORIAL2-EIGHTS-PROBLEM68484 Node: TUTORIAL3-PHONEBOOK-EXAMPLE72629 Node: TUTORIAL4-DEFUN-SK-EXAMPLE102476 Node: TUTORIAL5-MISCELLANEOUS-EXAMPLES106643 Node: FILE-READING-EXAMPLE107297 Node: FUNCTIONAL-INSTANTIATION-EXAMPLE109759 Node: GUARD-EXAMPLE114950 Node: MUTUAL-RECURSION-PROOF-EXAMPLE124592 Node: EMACS130965 Node: INTERESTING-APPLICATIONS131517 Node: INTRODUCTION-TO-THE-THEOREM-PROVER138732 Node: ARCHITECTURE-OF-THE-PROVER159693 Node: DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS163629 Node: EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES171022 Node: EXAMPLE-INDUCTION-SCHEME-BINARY-TREES173827 Node: EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2174908 Node: EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION176518 Node: EXAMPLE-INDUCTION-SCHEME-ON-LISTS178070 Node: EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES179418 Node: EXAMPLE-INDUCTION-SCHEME-UPWARDS180467 Node: EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS182027 Node: EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS184784 Node: EXAMPLE-INDUCTIONS186569 Node: FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS189381 Node: FURTHER-INFORMATION-ON-REWRITING219361 Node: GENERALIZING-KEY-CHECKPOINTS225734 Node: INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS229582 Node: INTRODUCTION-TO-HINTS237677 Node: INTRODUCTION-TO-KEY-CHECKPOINTS242600 Node: INTRODUCTION-TO-REWRITE-RULES-PART-1251579 Node: INTRODUCTION-TO-REWRITE-RULES-PART-2259868 Node: INTRODUCTION-TO-THE-DATABASE273214 Node: INTRODUCTORY-CHALLENGE-PROBLEM-1284280 Node: INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER285142 Node: INTRODUCTORY-CHALLENGE-PROBLEM-2286564 Node: INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER287220 Node: INTRODUCTORY-CHALLENGE-PROBLEM-3288617 Node: INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER289641 Node: INTRODUCTORY-CHALLENGE-PROBLEM-4293793 Node: INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER296041 Node: INTRODUCTORY-CHALLENGES319164 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED320760 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE328284 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS329206 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION330968 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF332438 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE339497 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS340846 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER349411 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER353198 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER357490 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING358696 Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY367828 Node: POST-INDUCTION-KEY-CHECKPOINTS371030 Node: PRACTICE-FORMULATING-STRONG-RULES373262 Node: PRACTICE-FORMULATING-STRONG-RULES-1375056 Node: PRACTICE-FORMULATING-STRONG-RULES-2379089 Node: PRACTICE-FORMULATING-STRONG-RULES-3381029 Node: PRACTICE-FORMULATING-STRONG-RULES-4385555 Node: PRACTICE-FORMULATING-STRONG-RULES-5386823 Node: PRACTICE-FORMULATING-STRONG-RULES-6389182 Node: PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED392872 Node: SPECIAL-CASES-FOR-REWRITE-RULES405266 Node: SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES407441 Node: STRONG-REWRITE-RULES411555 Node: STARTUP415245 Node: TIDBITS417922 Node: TIPS421449 Node: BDD441484 Node: BDD-ALGORITHM443145 Node: BDD-INTRODUCTION466268 Node: IF*474503 Node: SHOW-BDD481189 Node: BOOKS483504 Node: BOOK-COMPILED-FILE487806 Node: BOOK-CONTENTS493121 Node: BOOK-EXAMPLE496235 Node: BOOK-MAKEFILES504576 Node: BOOK-NAME504731 Node: BOOKS-CERTIFICATION508788 Node: BOOKS-CERTIFICATION-CLASSIC520021 Node: CBD531430 Node: CERTIFICATE536486 Node: CERTIFY-BOOK540620 Node: CERTIFYING-BOOKS552357 Node: COMMUNITY-BOOKS552522 Node: FULL-BOOK-NAME553267 Node: KEEP555113 Node: PATHNAME556544 Node: PORTCULLIS558768 Node: PROVISIONAL-CERTIFICATION562425 Node: REGRESSION577168 Node: SET-CBD577326 Node: UNCERTIFIED-BOOKS578357 Node: BREAK-REWRITE581588 Node: BREAK-LEMMA594532 Node: BRR597978 Node: BRR-COMMANDS601710 Node: BRRatsign603894 Node: MONITOR609825 Node: MONITORED-RUNES618245 Node: OK-IF618613 Node: UNMONITOR620459 Node: DOCUMENTATION621258 Node: *TERMINAL-MARKUP-TABLE*630043 Node: ARGS631684 Node: DOC632165 Node: DOC!635000 Node: DOC-STRING635378 Node: DOCS646861 Node: HELP648826 Node: MARKUP649078 Node: MORE659790 Node: MORE!661621 Node: MORE-DOC662389 Node: NQTHM-TO-ACL2663271 Node: EVENTS671259 Node: ADD-CUSTOM-KEYWORD-HINT676649 Node: ASSERT-EVENT680113 Node: COMP681921 Node: COMP-GCL687525 Node: DEFABBREV688162 Node: DEFABSSTOBJ691376 Node: DEFABSSTOBJ-MISSING-EVENTS721531 Node: DEFATTACH722687 Node: DEFAXIOM744791 Node: DEFCHOOSE745983 Node: CONSERVATIVITY-OF-DEFCHOOSE751643 Node: DEFCONG770958 Node: DEFCONST772550 Node: DEFDOC774892 Node: DEFEQUIV777482 Node: DEFEVALUATOR778892 Node: DEFEXEC783275 Node: DEFINE-TRUSTED-CLAUSE-PROCESSOR791701 Node: DEFLABEL802761 Node: DEFMACRO803826 Node: DEFMACRO-LAST807327 Node: DEFN807629 Node: DEFND807782 Node: DEFPKG807940 Node: HIDDEN-DEATH-PACKAGE812912 Node: HIDDEN-DEFPKG815443 Node: MANAGING-ACL2-PACKAGES815657 Node: DEFPROXY815977 Node: DEFPUN826664 Node: DEFREFINEMENT827936 Node: DEFSTOBJ829107 Node: DEFSTUB848653 Node: DEFTHEORY850406 Node: DEFTHEORY-STATIC853117 Node: DEFTHM856153 Node: DEFTHMD858555 Node: DEFTTAG859437 Node: DEFUN871544 Node: DEFUN-INLINE882585 Node: DEFUN-NOTINLINE887349 Node: DEFUN-NX888277 Node: DEFUN-SK891094 Node: DEFUN-SK-EXAMPLE903473 Node: EXISTS907153 Node: FORALL907595 Node: QUANTIFIER-TUTORIAL908036 Node: QUANTIFIERS934300 Node: QUANTIFIERS-USING-DEFUN-SK936263 Node: QUANTIFIERS-USING-DEFUN-SK-EXTENDED938059 Node: QUANTIFIERS-USING-RECURSION940679 Node: DEFUND941604 Node: DEFUND-INLINE943133 Node: DEFUND-NOTINLINE943487 Node: ENCAPSULATE943859 Node: REDUNDANT-ENCAPSULATE955493 Node: EVISC-TABLE959234 Node: IN-ARITHMETIC-THEORY962824 Node: IN-THEORY964738 Node: INCLUDE-BOOK966956 Node: LOCAL976177 Node: MAKE-EVENT977937 Node: MAKE-EVENT-DETAILS1007378 Node: MEMOIZE1018416 Node: MUTUAL-RECURSION1029847 Node: PROFILE1034297 Node: PROGN1036357 Node: PROGN!1037754 Node: REGENERATE-TAU-DATABASE1043085 Node: REMOVE-CUSTOM-KEYWORD-HINT1046598 Node: RESTORE-MEMOIZATION-SETTINGS1047236 Node: SAVE-AND-CLEAR-MEMOIZATION-SETTINGS1047819 Node: SET-BODY1048549 Node: TABLE1049868 Node: USING-TABLES-EFFICIENTLY1059772 Node: THEORY-INVARIANT1062937 Node: UNMEMOIZE1068472 Node: VALUE-TRIPLE1069522 Node: VERIFY-GUARDS1071217 Node: VERIFY-GUARDS+1084886 Node: VERIFY-TERMINATION1087388 Node: FORWARD-CHAINING-REPORTS1094749 Node: FC-REPORT1107518 Node: RESET-FC-REPORTING1107942 Node: SET-FC-CRITERIA1108548 Node: SET-FC-REPORT-ON-THE-FLY1111694 Node: SHOW-FC-CRITERIA1112662 Node: HISTORY1113039 Node: GCS1115777 Node: GET-COMMAND-SEQUENCE1115919 Node: OOPS1116739 Node: PBT1120634 Node: PC1121478 Node: PCB1126884 Node: PCB!1128213 Node: PCS1128816 Node: PE1129722 Node: PE!1131513 Node: PF1131708 Node: PL1132325 Node: PL21134353 Node: PR1135631 Node: PR!1137193 Node: PUFF1138129 Node: PUFF*1144924 Node: RESET-KILL-RING1147614 Node: TAU-DATA1148853 Node: TAU-DATABASE1150103 Node: U1150671 Node: UBT1151027 Node: UBT!1152855 Node: UBT-PREHISTORY1153353 Node: UBU1153770 Node: UBU!1154965 Node: HONS-AND-MEMOIZATION1155480 Node: CLEAR-HASH-TABLES1175137 Node: CLEAR-MEMOIZE-STATISTICS1175609 Node: CLEAR-MEMOIZE-TABLE1176101 Node: CLEAR-MEMOIZE-TABLES1176572 Node: CONS-SUBTREES1177098 Node: FAST-ALIST-FREE1177510 Node: FAST-ALIST-FREE-ON-EXIT1178642 Node: FAST-ALIST-LEN1179881 Node: FAST-ALIST-SUMMARY1180670 Node: FAST-ALISTS1181383 Node: FLUSH-HONS-GET-HASH-TABLE-LINK1183889 Node: HONS1184270 Node: HONS-ACONS1185386 Node: HONS-ACONS!1188234 Node: HONS-ASSOC-EQUAL1190722 Node: HONS-CLEAR1191538 Node: HONS-COPY1192864 Node: HONS-COPY-PERSISTENT1194319 Node: HONS-EQUAL1195156 Node: HONS-EQUAL-LITE1196255 Node: HONS-GET1197295 Node: HONS-NOTE1198125 Node: HONS-RESIZE1201269 Node: HONS-SHRINK-ALIST1204096 Node: HONS-SHRINK-ALIST!1206875 Node: HONS-SUMMARY1207683 Node: HONS-WASH1208591 Node: MAKE-FAST-ALIST1210063 Node: MEMOIZE-SUMMARY1211078 Node: MEMSUM1211613 Node: NEVER-MEMOIZE1212037 Node: NORMED1212762 Node: NUMBER-SUBTREES1215626 Node: SLOW-ALIST-WARNING1216222 Node: WITH-FAST-ALIST1217428 Node: WITH-STOLEN-ALIST1219835 Node: INTRODUCTION-TO-THE-TAU-SYSTEM1220853 Node: DEALING-WITH-TAU-PROBLEMS1232176 Node: FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM1238983 Node: IO1243815 Node: *STANDARD-CI*1256448 Node: *STANDARD-CO*1257073 Node: *STANDARD-OI*1259055 Node: CHARACTER-ENCODING1259676 Node: EVISC-TUPLE1261311 Node: EVISCERATE-HIDE-TERMS1263569 Node: EXTERNAL-FORMAT1264175 Node: IPRINT1264334 Node: IPRINTING1264464 Node: OPEN-OUTPUT-CHANNEL!1264602 Node: OUTPUT-TO-FILE1268891 Node: PRINT-CONTROL1271069 Node: PRINTING-TO-STRINGS1276028 Node: RESET-PRINT-CONTROL1278936 Node: SET-EVISC-TUPLE1279105 Node: SET-FMT-HARD-RIGHT-MARGIN1284306 Node: SET-FMT-SOFT-RIGHT-MARGIN1285221 Node: SET-IPRINT1285535 Node: SET-PRINT-BASE1293827 Node: SET-PRINT-CASE1294926 Node: SET-PRINT-CIRCLE1296418 Node: SET-PRINT-ESCAPE1296577 Node: SET-PRINT-LENGTH1296738 Node: SET-PRINT-LEVEL1296898 Node: SET-PRINT-LINES1297056 Node: SET-PRINT-RADIX1297213 Node: SET-PRINT-READABLY1298397 Node: SET-PRINT-RIGHT-MARGIN1298567 Node: WITHOUT-EVISC1298739 Node: MISCELLANEOUS1300968 Node: &ALLOW-OTHER-KEYS1312091 Node: &BODY1312248 Node: &KEY1312384 Node: &OPTIONAL1312511 Node: &REST1312643 Node: &WHOLE1312773 Node: A!1312897 Node: ABORT!1313742 Node: ACKNOWLEDGMENTS1314012 Node: ACL2S1321108 Node: APROPOS1321245 Node: BACKCHAIN-LIMIT1321395 Node: BACKCHAIN-LIMIT-RW1327526 Node: BACKTRACK1327721 Node: BIBLIOGRAPHY1327895 Node: BIND-FREE1328156 Node: BIND-FREE-EXAMPLES1340405 Node: BY1347743 Node: CASE-SPLIT1347885 Node: CASE-SPLIT-LIMITATIONS1350051 Node: CASES1350804 Node: CLAUSE-IDENTIFIER1350975 Node: COMMAND1352973 Node: COMMAND-DESCRIPTOR1354013 Node: COMMON-LISP1358403 Node: DEFUN-MODE-CAVEAT1360575 Node: GENERALIZED-BOOLEANS1364905 Node: COMPUTED-HINTS1368643 Node: CONSTRAINT1376446 Node: COPYRIGHT1395616 Node: COROLLARY1396638 Node: CURRENT-PACKAGE1396992 Node: CUSTOM-KEYWORD-HINTS1399621 Node: SHOW-CUSTOM-KEYWORD-HINT-EXPANSION1400109 Node: DEFAULT-BACKCHAIN-LIMIT1400933 Node: DEFAULT-DEFUN-MODE1401415 Node: DEFAULT-HINTS1403158 Node: DEFAULT-PRINT-PROMPT1403900 Node: DEFAULT-RULER-EXTENDERS1405411 Node: DEFUN-MODE1406180 Node: DEFUNS1411814 Node: DISABLE-FORCING1412692 Node: DISABLE-IMMEDIATE-FORCE-MODEP1413417 Node: DISABLEDP1414304 Node: DO-NOT1415538 Node: DO-NOT-INDUCT1415695 Node: DOUBLE-REWRITE1415871 Node: EMBEDDED-EVENT-FORM1425952 Node: ENABLE-FORCING1431466 Node: ENABLE-IMMEDIATE-FORCE-MODEP1432249 Node: ENTER-BOOT-STRAP-MODE1433133 Node: ESCAPE-TO-COMMON-LISP1434368 Node: EXECUTABLE-COUNTERPART1434842 Node: EXIT-BOOT-STRAP-MODE1438403 Node: EXPAND1439121 Node: EXTENDED-METAFUNCTIONS1439298 Node: FAILED-FORCING1455168 Node: FAILURE1461826 Node: FIND-RULES-OF-RUNE1464500 Node: FINDING-DOCUMENTATION1465763 Node: FNCALL-TERM1466916 Node: FORCE1467071 Node: FORCED1473780 Node: FORCING-ROUND1473910 Node: FUNCTIONAL-INSTANTIATION-IN-ACL2R1479186 Node: GAG-MODE1480306 Node: GC$1480634 Node: GC-VERBOSE1481610 Node: GCL1482488 Node: GET-INTERNAL-TIME1487086 Node: GET-WORMHOLE-STATUS1489259 Node: GOAL-SPEC1490069 Node: GUARD1494145 Node: EXTRA-INFO1496501 Node: GUARD-DEBUG1496800 Node: GUARD-EVALUATION-EXAMPLES-LOG1501091 Node: GUARD-EVALUATION-EXAMPLES-SCRIPT1522762 Node: GUARD-EVALUATION-TABLE1528455 Node: GUARD-INTRODUCTION1534049 Node: GUARD-MISCELLANY1536421 Node: GUARD-QUICK-REFERENCE1540603 Node: GUARDS-AND-EVALUATION1543209 Node: GUARDS-FOR-SPECIFICATION1559413 Node: GUARD-HINTS1561755 Node: HANDS-OFF1561919 Node: HIDE1562078 Node: HINTS1566392 Node: HINTS-AND-THE-WATERFALL1600059 Node: I-AM-HERE1611440 Node: IF-INTRO1612442 Node: IGNORED-ATTACHMENT1612588 Node: IMMED-FORCED1616560 Node: IMMEDIATE-FORCE-MODEP1616726 Node: INDUCT1618099 Node: KEYWORD1618262 Node: KEYWORD-COMMANDS1618401 Node: LAMBDA1620779 Node: LAST-PROVER-STEPS1620923 Node: LD-ERROR-ACTION1623181 Node: LD-ERROR-TRIPLES1628762 Node: LD-EVISC-TUPLE1629824 Node: LD-MISSING-INPUT-OK1630921 Node: LD-POST-EVAL-PRINT1631796 Node: LD-PRE-EVAL-FILTER1635063 Node: LD-PRE-EVAL-PRINT1636385 Node: LD-PROMPT1638056 Node: LD-QUERY-CONTROL-ALIST1639884 Node: LD-REDEFINITION-ACTION1642888 Node: LD-SKIP-PROOFSP1651980 Node: LD-VERBOSE1657042 Node: LEMMA-INSTANCE1658277 Node: LINEAR-ARITHMETIC1663948 Node: LOCAL-INCOMPATIBILITY1667280 Node: LOGICAL-NAME1671976 Node: LOOP-STOPPER1675293 Node: LP1683286 Node: MACRO-ARGS1686679 Node: MAKE-WORMHOLE-STATUS1689562 Node: MEASURE1690065 Node: META-EXTRACT1690235 Node: META-EXTRACT-CONTEXTUAL-FACT1705393 Node: META-EXTRACT-FORMULA1705588 Node: META-EXTRACT-GLOBAL-FACT1705787 Node: META-EXTRACT-GLOBAL-FACT+1705987 Node: META-EXTRACT-RW+-TERM1706189 Node: MODE1706367 Node: NAME1706521 Node: NIL-GOAL1708662 Node: NO-THANKS1710839 Node: NON-EXECUTABLE1711005 Node: NON-LINEAR-ARITHMETIC1711194 Node: NONLINEARP1719580 Node: NORMALIZE1719757 Node: NU-REWRITER1719922 Node: OBDD1723625 Node: ORDINALS1723829 Node: OTF-FLG1730453 Node: OVERRIDE-HINTS1732191 Node: P!1739584 Node: PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS1740843 Node: PARALLEL1742553 Node: PARALLELISM-BUILD1742763 Node: PRINT-DOC-START-COLUMN1742998 Node: PROMPT1743856 Node: PROOF-OF-WELL-FOUNDEDNESS1744793 Node: PROOF-SUPPORTERS-ALIST1752020 Node: REDEF1752200 Node: REDEF!1752754 Node: REDEF+1753276 Node: REDEF-1754473 Node: REDEFINED-NAMES1755025 Node: REDUNDANT-EVENTS1756827 Node: REORDER1770830 Node: RESTRICT1770992 Node: REWRITE-STACK-LIMIT1771159 Node: SAVING-AND-RESTORING1772544 Node: SET-WORMHOLE-DATA1772724 Node: SET-WORMHOLE-ENTRY-CODE1773439 Node: SHOW-BODIES1774178 Node: SIGNATURE1775157 Node: SIMPLE1782194 Node: SPECIOUS-SIMPLIFICATION1783576 Node: SPLITTER1788891 Node: SPLITTER-OUTPUT1795904 Node: STOBJS1796478 Node: SUBVERSIVE-INDUCTIONS1796649 Node: SUBVERSIVE-RECURSIONS1796884 Node: SYNTAX1804402 Node: SYNTAXP1804915 Node: SYNTAXP-EXAMPLES1815120 Node: TERM1821427 Node: THE-METHOD1830377 Node: TIME-TRACKER-TAU1835557 Node: TRUST-TAG1838098 Node: TTAGS-SEEN1838244 Node: TTREE1840082 Node: TYPE-SET1841270 Node: TYPESPEC-CHECK1848159 Node: USE1848305 Node: USING-COMPUTED-HINTS1848465 Node: USING-COMPUTED-HINTS-11849605 Node: USING-COMPUTED-HINTS-21850857 Node: USING-COMPUTED-HINTS-31854942 Node: USING-COMPUTED-HINTS-41859701 Node: USING-COMPUTED-HINTS-51865692 Node: USING-COMPUTED-HINTS-61868048 Node: USING-COMPUTED-HINTS-71877712 Node: USING-COMPUTED-HINTS-81886606 Node: USING-ENABLED-RULES1888475 Node: VERSION1891435 Node: WATERFALL1896178 Node: WHY-BRR1896328 Node: WORLD1899290 Node: WORMHOLE1905518 Node: WORMHOLE-DATA1926061 Node: WORMHOLE-ENTRY-CODE1926444 Node: WORMHOLE-EVAL1926851 Node: WORMHOLE-IMPLEMENTATION1930426 Node: WORMHOLE-P1934572 Node: WORMHOLE-STATUSP1934921 Node: XARGS1935301 Node: OTHER1941803 Node: ACCUMULATED-PERSISTENCE1945171 Node: ACCUMULATED-PERSISTENCE-SUBTLETIES1964182 Node: ACL2-DEFAULTS-TABLE1969171 Node: ACL2-HELP1981668 Node: CERTIFY-BOOK!1982122 Node: COMMAND-LINE1983084 Node: CW-GSTACK1983651 Node: DEAD-EVENTS1985843 Node: DISASSEMBLE$1989263 Node: DMR1991445 Node: DYNAMICALLY-MONITOR-REWRITES1999680 Node: EXIT1999833 Node: GOOD-BYE1999995 Node: GUARD-OBLIGATION2001111 Node: IN-PACKAGE2003897 Node: LD2004532 Node: CALLING-LD-IN-BAD-CONTEXTS2018123 Node: PRINT-GV2019935 Node: PROPS2024992 Node: PSO2025273 Node: PSO!2025715 Node: PSOF2026172 Node: PSOG2027076 Node: PSTACK2027451 Node: VERBOSE-PSTACK2029789 Node: Q2030637 Node: QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP2031434 Node: QUIT2033265 Node: REBUILD2033442 Node: REDO-FLAT2035601 Node: RESET-LD-SPECIALS2040011 Node: SAVE-EXEC2041177 Node: SET-ACCUMULATED-PERSISTENCE2053236 Node: SHARP-BANG-READER2053426 Node: SHARP-COMMA-READER2054317 Node: SHARP-DOT-READER2054598 Node: SHARP-U-READER2055910 Node: SHOW-ACCUMULATED-PERSISTENCE2057498 Node: SKIP-PROOFS2057689 Node: THM2062675 Node: TOP-LEVEL2063324 Node: TRANS2065671 Node: TRANS!2067011 Node: TRANS12067460 Node: VERIFY-GUARDS-FORMULA2067997 Node: WALKABOUT2069117 Node: WITH-PROVER-STEP-LIMIT2071735 Node: WITH-PROVER-TIME-LIMIT2077963 Node: WOF2082654 Node: PARALLELISM2083409 Node: COMPILING-ACL2P2085709 Node: PARALLEL-PROGRAMMING2086831 Node: DEFLOCK2089853 Node: EARLY-TERMINATION2093005 Node: ERROR-TRIPLES-AND-PARALLELISM2095148 Node: GRANULARITY2097665 Node: PAND2102177 Node: PARALLEL-EXECUTION2104924 Node: PARALLELISM-AT-THE-TOP-LEVEL2105257 Node: PARALLELISM-PERFORMANCE2107585 Node: PARALLELISM-TUTORIAL2109475 Node: PARGS2120365 Node: PLET2121894 Node: POR2123446 Node: SPEC-MV-LET2125267 Node: WITH-OUTPUT-LOCK2128129 Node: PARALLEL-PROOF2131055 Node: ACL2P-KEY-CHECKPOINTS2132705 Node: DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT2134595 Node: PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION2134938 Node: UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES2137432 Node: WATERFALL-PARALLELISM2145176 Node: WATERFALL-PRINTING2145465 Node: UNSUPPORTED-PARALLELISM-FEATURES2145704 Node: WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION2147774 Node: PROGRAMMING2149120 Node: ACL2-BUILT-INS2151355 Node: *2171141 Node: +2171551 Node: -2171855 Node: /2172311 Node: /=2172847 Node: 1+2173356 Node: 1-2173597 Node: <2173837 Node: <=2174742 Node: =2175030 Node: >2175500 Node: >=2175773 Node: atsign2176069 Node: ABS2176884 Node: ACL2-COUNT2177533 Node: ACL2-NUMBER-LISTP2178280 Node: ACL2-NUMBERP2178605 Node: ACONS2178857 Node: ADD-TO-SET2179338 Node: ADD-TO-SET-EQ2180833 Node: ADD-TO-SET-EQL2180989 Node: ADD-TO-SET-EQUAL2181152 Node: ALISTP2181310 Node: ALLOCATE-FIXNUM-RANGE2181628 Node: ALPHA-CHAR-P2182460 Node: ALPHORDER2183000 Node: AND2184131 Node: APPEND2184536 Node: ASH2185230 Node: ASSERT$2185837 Node: ASSIGN2186432 Node: ASSOC2187756 Node: ASSOC-EQ2189223 Node: ASSOC-EQUAL2189356 Node: ASSOC-KEYWORD2189500 Node: ASSOC-STRING-EQUAL2190006 Node: ATOM2190524 Node: ATOM-LISTP2190907 Node: BINARY-*2191254 Node: BINARY-+2191827 Node: BINARY-APPEND2192455 Node: BOOLE$2192825 Node: BOOLEANP2194456 Node: BREAK$2194894 Node: BREAKS2195336 Node: BUTLAST2197417 Node: CAAAAR2198412 Node: CAAADR2198584 Node: CAAAR2198754 Node: CAADAR2198922 Node: CAADDR2199092 Node: CAADR2199262 Node: CAAR2199428 Node: CADAAR2199592 Node: CADADR2199761 Node: CADAR2199931 Node: CADDAR2200099 Node: CADDDR2200269 Node: CADDR2200439 Node: CADR2200605 Node: CANONICAL-PATHNAME2200781 Node: CAR2202607 Node: CASE2203028 Node: CASE-MATCH2204348 Node: CDAAAR2207253 Node: CDAADR2207428 Node: CDAAR2207598 Node: CDADAR2207766 Node: CDADDR2207936 Node: CDADR2208106 Node: CDAR2208272 Node: CDDAAR2208436 Node: CDDADR2208605 Node: CDDAR2208775 Node: CDDDAR2208943 Node: CDDDDR2209113 Node: CDDDR2209283 Node: CDDR2209449 Node: CDR2209610 Node: CEILING2210016 Node: CHAR2210991 Node: CHAR-CODE2211490 Node: CHAR-DOWNCASE2211889 Node: CHAR-EQUAL2212471 Node: CHAR-UPCASE2213027 Node: CHAR<2213595 Node: CHAR<=2214061 Node: CHAR>2214546 Node: CHAR>=2215013 Node: CHARACTER-ALISTP2215515 Node: CHARACTER-LISTP2215827 Node: CHARACTERP2216156 Node: CHARACTERS2216366 Node: CHECK-SUM2218900 Node: CLOSE-INPUT-CHANNEL2220917 Node: CLOSE-OUTPUT-CHANNEL2221082 Node: CODE-CHAR2221248 Node: COERCE2221825 Node: COMPLEX2222671 Node: COMPLEX-RATIONALP2224419 Node: COMPLEX/COMPLEX-RATIONALP2224948 Node: CONCATENATE2225593 Node: COND2226868 Node: CONJUGATE2227295 Node: CONS2227719 Node: CONSP2228014 Node: COUNT2228193 Node: CPU-CORE-COUNT2229337 Node: CW2230165 Node: CW!2233429 Node: DELETE-ASSOC2233799 Node: DELETE-ASSOC-EQ2235347 Node: DELETE-ASSOC-EQUAL2235515 Node: DENOMINATOR2235685 Node: DIGIT-CHAR-P2236058 Node: DIGIT-TO-CHAR2236872 Node: E0-ORD-<2237494 Node: E0-ORDINALP2238318 Node: EC-CALL2239122 Node: EIGHTH2244876 Node: ENDP2245055 Node: EQ2245640 Node: EQL2246511 Node: EQLABLE-ALISTP2247067 Node: EQLABLE-LISTP2247446 Node: EQLABLEP2247802 Node: EQUAL2248248 Node: ER2248548 Node: ER-PROGN2251081 Node: ERROR12252869 Node: EVENP2254091 Node: EXPLODE-NONNEGATIVE-INTEGER2254604 Node: EXPT2255327 Node: F-GET-GLOBAL2256010 Node: F-PUT-GLOBAL2256939 Node: FIFTH2258157 Node: FIRST2258339 Node: FIX2258512 Node: FIX-TRUE-LIST2258982 Node: FLET2259345 Node: FLOOR2263898 Node: FMS2264855 Node: FMS!2265088 Node: FMS!-TO-STRING2265536 Node: FMS-TO-STRING2265696 Node: FMT2265854 Node: FMT!2284207 Node: FMT!-TO-STRING2284655 Node: FMT-TO-COMMENT-WINDOW2284823 Node: FMT-TO-STRING2285766 Node: FMT12285932 Node: FMT1!2286189 Node: FMT1!-TO-STRING2286655 Node: FMT1-TO-STRING2286819 Node: FOURTH2286983 Node: GET-OUTPUT-STREAM-STRING$2287190 Node: GETENV$2287351 Node: GETPROP2288094 Node: GOOD-ATOM-LISTP2288564 Node: HARD-ERROR2288993 Node: IDENTITY2290353 Node: IF2290625 Node: IFF2291003 Node: IFIX2291328 Node: ILLEGAL2291821 Node: IMAGPART2292744 Node: IMPLIES2293089 Node: IMPROPER-CONSP2293418 Node: INT=2293836 Node: INTEGER-LENGTH2294223 Node: INTEGER-LISTP2294865 Node: INTEGERP2295180 Node: INTERN2295380 Node: INTERN$2297072 Node: INTERN-IN-PACKAGE-OF-SYMBOL2297729 Node: INTERSECTION$2299795 Node: INTERSECTION-EQ2302198 Node: INTERSECTION-EQUAL2302368 Node: INTERSECTP2302538 Node: INTERSECTP-EQ2303853 Node: INTERSECTP-EQUAL2304011 Node: KEYWORD-VALUE-LISTP2304181 Node: KEYWORDP2304595 Node: KWOTE2305315 Node: KWOTE-LST2305667 Node: LAST2306013 Node: LEN2306589 Node: LENGTH2307114 Node: LET2307542 Node: LET*2312476 Node: LEXORDER2314200 Node: LIST2314900 Node: LIST*2315281 Node: LISTP2315670 Node: LOGAND2316074 Node: LOGANDC12316652 Node: LOGANDC22317212 Node: LOGBITP2317774 Node: LOGCOUNT2318323 Node: LOGEQV2318768 Node: LOGIOR2319359 Node: LOGNAND2319950 Node: LOGNOR2320428 Node: LOGNOT2320927 Node: LOGORC12321491 Node: LOGORC22322059 Node: LOGTEST2322629 Node: LOGXOR2323184 Node: LOWER-CASE-P2323781 Node: MAKE-CHARACTER-LIST2324334 Node: MAKE-LIST2324644 Node: MAKE-ORD2325205 Node: MAX2326345 Node: MBE2326755 Node: MBE12332420 Node: MBT2332677 Node: MEMBER2336589 Node: MEMBER-EQ2338041 Node: MEMBER-EQUAL2338179 Node: MIN2338317 Node: MINUSP2338736 Node: MOD2339153 Node: MOD-EXPT2339778 Node: MSG2340696 Node: MUST-BE-EQUAL2345180 Node: MV2345928 Node: MV-LET2346867 Node: MV-LIST2352050 Node: MV-NTH2353595 Node: MV?2355336 Node: MV?-LET2356080 Node: NAT-LISTP2357343 Node: NATP2357650 Node: NFIX2358221 Node: NINTH2358739 Node: NO-DUPLICATESP2358922 Node: NO-DUPLICATESP-EQ2360209 Node: NO-DUPLICATESP-EQUAL2360387 Node: NON-EXEC2360562 Node: NONNEGATIVE-INTEGER-QUOTIENT2363549 Node: NOT2364343 Node: NTH2364734 Node: NTHCDR2365221 Node: NULL2365997 Node: NUMERATOR2366473 Node: O-FINP2366815 Node: O-FIRST-COEFF2367283 Node: O-FIRST-EXPT2367933 Node: O-INFP2368558 Node: O-P2368787 Node: O-RST2375005 Node: O<2375627 Node: O<=2378779 Node: O>2378994 Node: O>=2379195 Node: OBSERVATION2379422 Node: OBSERVATION-CW2382313 Node: ODDP2382463 Node: OPEN-INPUT-CHANNEL2382909 Node: OPEN-INPUT-CHANNEL-P2383067 Node: OPEN-OUTPUT-CHANNEL2383242 Node: OPEN-OUTPUT-CHANNEL-P2383419 Node: OR2383580 Node: ORACLE-APPLY2384624 Node: ORACLE-APPLY-RAW2388597 Node: ORACLE-FUNCALL2389776 Node: PAIRLIS2390800 Node: PAIRLIS$2391090 Node: PEEK-CHAR$2391751 Node: PKG-IMPORTS2391888 Node: PKG-WITNESS2394076 Node: PLUSP2394786 Node: POSITION2395211 Node: POSITION-EQ2396763 Node: POSITION-EQUAL2396911 Node: POSP2397058 Node: PPROGN2397579 Node: PRINC$2398712 Node: PRINT-OBJECT$2400587 Node: PROG2$2400723 Node: PROGN$2402993 Node: PROOFS-CO2403634 Node: PROPER-CONSP2404216 Node: PSEUDO-TERMP2404592 Node: PUT-ASSOC2407585 Node: PUT-ASSOC-EQ2409205 Node: PUT-ASSOC-EQL2409356 Node: PUT-ASSOC-EQUAL2409514 Node: PUTPROP2409669 Node: QUOTE2410126 Node: R-EQLABLE-ALISTP2410331 Node: R-SYMBOL-ALISTP2410720 Node: RANDOM$2411089 Node: RASSOC2412112 Node: RASSOC-EQ2413775 Node: RASSOC-EQUAL2413913 Node: RATIONAL-LISTP2414062 Node: RATIONALP2414395 Node: READ-BYTE$2414636 Node: READ-CHAR$2414773 Node: READ-OBJECT2414912 Node: READ-RUN-TIME2415055 Node: REAL/RATIONALP2415762 Node: REALFIX2416325 Node: REALPART2416841 Node: REM2417177 Node: REMOVE2417813 Node: REMOVE-DUPLICATES2419313 Node: REMOVE-DUPLICATES-EQ2421522 Node: REMOVE-DUPLICATES-EQUAL2421715 Node: REMOVE-EQ2421903 Node: REMOVE-EQUAL2422058 Node: REMOVE12422200 Node: REMOVE1-EQ2423655 Node: REMOVE1-EQUAL2423798 Node: REST2423941 Node: RETURN-LAST2424213 Node: REVAPPEND2443921 Node: REVERSE2444739 Node: RFIX2445197 Node: ROUND2445702 Node: SEARCH2446758 Node: SECOND2449399 Node: SET-DIFFERENCE$2449588 Node: SET-DIFFERENCE-EQ2451302 Node: SET-DIFFERENCE-EQUAL2451482 Node: SETENV$2451657 Node: SEVENTH2452459 Node: SIGNED-BYTE-P2452650 Node: SIGNUM2453266 Node: SIXTH2453994 Node: STANDARD-CHAR-LISTP2454184 Node: STANDARD-CHAR-P2454627 Node: STANDARD-CO2455272 Node: STANDARD-OI2456151 Node: STANDARD-STRING-ALISTP2457291 Node: STATE-GLOBAL-LET*2457801 Node: STRING2461352 Node: STRING-APPEND2461957 Node: STRING-DOWNCASE2462534 Node: STRING-EQUAL2463102 Node: STRING-LISTP2463720 Node: STRING-UPCASE2464034 Node: STRING<2464584 Node: STRING<=2465407 Node: STRING>2466086 Node: STRING>=2466589 Node: STRINGP2467268 Node: STRIP-CARS2467456 Node: STRIP-CDRS2467932 Node: SUBLIS2468402 Node: SUBSEQ2469116 Node: SUBSETP2470127 Node: SUBSETP-EQ2471476 Node: SUBSETP-EQUAL2471619 Node: SUBST2471763 Node: SUBSTITUTE2472370 Node: SYMBOL-<2473115 Node: SYMBOL-ALISTP2473768 Node: SYMBOL-LISTP2474126 Node: SYMBOL-NAME2474438 Node: SYMBOL-PACKAGE-NAME2474806 Node: SYMBOLP2475751 Node: SYS-CALL2475948 Node: SYS-CALL+2478959 Node: SYS-CALL-STATUS2481269 Node: TAKE2482042 Node: TENTH2483008 Node: TERM-ORDER2483187 Node: THE2489972 Node: THIRD2491512 Node: TIME$2491685 Node: TRUE-LIST-LISTP2497810 Node: TRUE-LISTP2498241 Node: TRUNCATE2498575 Node: UNARY--2499726 Node: UNARY-/2500225 Node: UNION$2500792 Node: UNION-EQ2502942 Node: UNION-EQUAL2503077 Node: UNSIGNED-BYTE-P2503224 Node: UNTRANSLATE2503823 Node: UPDATE-NTH2503994 Node: UPPER-CASE-P2505070 Node: WITH-LIVE-STATE2505624 Node: WRITE-BYTE$2507009 Node: XOR2507147 Node: ZEROP2507449 Node: ZIP2508219 Node: ZP2509167 Node: ZPF2510170 Node: ACL2-USER2510601 Node: ARRAYS2512486 Node: AREF12533673 Node: AREF22534483 Node: ARRAY1P2535293 Node: ARRAY2P2535754 Node: ARRAYS-EXAMPLE2536224 Node: ASET12539088 Node: ASET22540647 Node: COMPRESS12542209 Node: COMPRESS22544141 Node: DEFAULT2545525 Node: DIMENSIONS2546282 Node: FLUSH-COMPRESS2547178 Node: HEADER2552567 Node: MAXIMUM-LENGTH2553051 Node: SLOW-ARRAY-WARNING2553800 Node: COMPILATION2558866 Node: DECLARE2560863 Node: TYPE-SPEC2563349 Node: EQUALITY-VARIANTS2566734 Node: EQUALITY-VARIANTS-DETAILS2571489 Node: IGNORABLE2578223 Node: IGNORE2578364 Node: IRRELEVANT-FORMALS2578503 Node: OPTIMIZE2581599 Node: REDEFINING-PROGRAMS2581752 Node: SINGLE-THREADED-OBJECTS2587875 Node: STATE2588043 Node: ERROR-TRIPLES2597066 Node: PROGRAMMING-WITH-STATE2598270 Node: TIME-TRACKER2620242 Node: TYPE2632346 Node: ZERO-TEST-IDIOMS2632482 Node: PROOF-CHECKER2640112 Node: DEFINE-PC-HELP2642778 Node: DEFINE-PC-MACRO2644007 Node: DEFINE-PC-META2645476 Node: INSTRUCTIONS2645995 Node: MACRO-COMMAND2665970 Node: PROOF-CHECKER-COMMANDS2667248 Node: ACL2-PC||=2675705 Node: ACL2-PC||ACL2-WRAP2679931 Node: ACL2-PC||ADD-ABBREVIATION2680379 Node: ACL2-PC||AL2682430 Node: ACL2-PC||APPLY-LINEAR2682723 Node: ACL2-PC||BASH2687486 Node: ACL2-PC||BDD2688585 Node: ACL2-PC||BK2689272 Node: ACL2-PC||BOOKMARK2689976 Node: ACL2-PC||CASESPLIT2690659 Node: ACL2-PC||CG2693161 Node: ACL2-PC||CHANGE-GOAL2693639 Node: ACL2-PC||CL-PROC2694428 Node: ACL2-PC||CLAIM2694715 Node: ACL2-PC||CLAUSE-PROCESSOR2696827 Node: ACL2-PC||COMM2697653 Node: ACL2-PC||COMMANDS2699859 Node: ACL2-PC||COMMENT2700889 Node: ACL2-PC||CONTRADICT2701360 Node: ACL2-PC||CONTRAPOSE2701584 Node: ACL2-PC||DEMOTE2702374 Node: ACL2-PC||DIVE2703507 Node: ACL2-PC||DO-ALL2704938 Node: ACL2-PC||DO-ALL-NO-PROMPT2705879 Node: ACL2-PC||DO-STRICT2706473 Node: ACL2-PC||DROP2707039 Node: ACL2-PC||DV2707745 Node: ACL2-PC||ELIM2710013 Node: ACL2-PC||EQUIV2710497 Node: ACL2-PC||EX2712720 Node: ACL2-PC||EXIT2713091 Node: ACL2-PC||EXPAND2716185 Node: ACL2-PC||FAIL2717132 Node: ACL2-PC||FINISH2717693 Node: ACL2-PC||FORWARDCHAIN2718393 Node: ACL2-PC||FREE2719547 Node: ACL2-PC||GENEQV2719936 Node: ACL2-PC||GENERALIZE2721331 Node: ACL2-PC||GOALS2723119 Node: ACL2-PC||HELP2723577 Node: ACL2-PC||HELP!2725978 Node: ACL2-PC||HELP-LONG2726337 Node: ACL2-PC||HYPS2726663 Node: ACL2-PC||ILLEGAL2728787 Node: ACL2-PC||IN-THEORY2729297 Node: ACL2-PC||INDUCT2731939 Node: ACL2-PC||LEMMAS-USED2732997 Node: ACL2-PC||LISP2733245 Node: ACL2-PC||MORE2735019 Node: ACL2-PC||MORE!2735342 Node: ACL2-PC||NEGATE2735724 Node: ACL2-PC||NIL2736203 Node: ACL2-PC||NOISE2736658 Node: ACL2-PC||NX2737344 Node: ACL2-PC||ORELSE2737993 Node: ACL2-PC||P2738583 Node: ACL2-PC||P-TOP2739264 Node: ACL2-PC||PL2740153 Node: ACL2-PC||PP2740808 Node: ACL2-PC||PR2741237 Node: ACL2-PC||PRINT2741892 Node: ACL2-PC||PRINT-ALL-CONCS2742799 Node: ACL2-PC||PRINT-ALL-GOALS2743217 Node: ACL2-PC||PRINT-MAIN2743609 Node: ACL2-PC||PRO2743892 Node: ACL2-PC||PROMOTE2744324 Node: ACL2-PC||PROTECT2745327 Node: ACL2-PC||PROVE2745944 Node: ACL2-PC||PSO2747223 Node: ACL2-PC||PSO!2747950 Node: ACL2-PC||PSOG2748708 Node: ACL2-PC||PUT2749459 Node: ACL2-PC||QUIET2750863 Node: ACL2-PC||R2751225 Node: ACL2-PC||REDUCE2751482 Node: ACL2-PC||REDUCE-BY-INDUCTION2752604 Node: ACL2-PC||REMOVE-ABBREVIATIONS2753734 Node: ACL2-PC||REPEAT2754826 Node: ACL2-PC||REPEAT-REC2755370 Node: ACL2-PC||REPLAY2755587 Node: ACL2-PC||RESTORE2756752 Node: ACL2-PC||RETAIN2757501 Node: ACL2-PC||RETRIEVE2758093 Node: ACL2-PC||REWRITE2758945 Node: ACL2-PC||RUN-INSTR-ON-GOAL2764512 Node: ACL2-PC||RUN-INSTR-ON-NEW-GOALS2764756 Node: ACL2-PC||RUNES2765003 Node: ACL2-PC||S2765742 Node: ACL2-PC||S-PROP2768339 Node: ACL2-PC||SAVE2768948 Node: ACL2-PC||SEQUENCE2769860 Node: ACL2-PC||SHOW-ABBREVIATIONS2773736 Node: ACL2-PC||SHOW-LINEARS2774928 Node: ACL2-PC||SHOW-REWRITES2775590 Node: ACL2-PC||SHOW-TYPE-PRESCRIPTIONS2777108 Node: ACL2-PC||SKIP2777833 Node: ACL2-PC||SL2778151 Node: ACL2-PC||SLS2778849 Node: ACL2-PC||SPLIT2779364 Node: ACL2-PC||SR2780823 Node: ACL2-PC||ST2781159 Node: ACL2-PC||SUCCEED2781510 Node: ACL2-PC||TH2781972 Node: ACL2-PC||THEN2782940 Node: ACL2-PC||TOP2783585 Node: ACL2-PC||TYPE-ALIST2784237 Node: ACL2-PC||UNDO2786967 Node: ACL2-PC||UNSAVE2787842 Node: ACL2-PC||UP2788564 Node: ACL2-PC||USE2789402 Node: ACL2-PC||WRAP2790266 Node: ACL2-PC||WRAP-INDUCT2791183 Node: ACL2-PC||WRAP12792065 Node: ACL2-PC||X2793447 Node: ACL2-PC||X-DUMB2795409 Node: RETRIEVE2795803 Node: TOGGLE-PC-MACRO2796231 Node: UNSAVE2796903 Node: VERIFY2797304 Node: PROOF-TREE2798550 Node: CHECKPOINT-FORCED-GOALS2802754 Node: PROOF-TREE-DETAILS2803499 Node: PROOF-TREE-EXAMPLES2805628 Node: START-PROOF-TREE2815549 Node: STOP-PROOF-TREE2816222 Node: Pages Written Especially for the Tours2816918 Node: A Flying Tour of ACL22826719 Node: A Sketch of How the Rewriter Works2828390 Node: A Tiny Warning Sign2829644 Node: A Trivial Proof2830331 Node: A Typical State2830517 Node: A Walking Tour of ACL22831383 Node: ACL2 Characters2832503 Node: ACL2 Conses or Ordered Pairs2833349 Node: ACL2 Strings2834854 Node: ACL2 Symbols2835695 Node: ACL2 System Architecture2838232 Node: ACL2 as an Interactive Theorem Prover2839410 Node: ACL2 as an Interactive Theorem Prover (cont)2839954 Node: ACL2 is an Untyped Language2841421 Node: About Models2842548 Node: About Types2843369 Node: About the ACL2 Home Page2845497 Node: About the Admission of Recursive Definitions2846765 Node: About the Prompt2848363 Node: An Example Common Lisp Function Definition2851847 Node: An Example of ACL2 in Use2853117 Node: Analyzing Common Lisp Models2855364 Node: Common Lisp2856789 Node: Common Lisp as a Modeling Language2858797 Node: Conversion2860292 Node: Corroborating Models2861204 Node: Evaluating App on Sample Input2863288 Node: Flawed Induction Candidates in App Example2864354 Node: Free Variables in Top-Level Input2865069 Node: Functions for Manipulating these Objects2866816 Node: Guards2867968 Node: Guessing the Type of a Newly Admitted Function2869724 Node: Guiding the ACL2 Theorem Prover2870694 Node: Hey Wait! Is ACL2 Typed or Untyped(Q)2871750 Node: How Long Does It Take to Become an Effective User(Q)2872787 Node: How To Find Out about ACL2 Functions2873884 Node: How To Find Out about ACL2 Functions (cont)2875112 Node: Modeling in ACL22876515 Node: Models in Engineering2877348 Node: Models of Computer Hardware and Software2877964 Node: Name the Formula Above2879019 Node: Nontautological Subgoals2879381 Node: Numbers in ACL22879899 Node: On the Naming of Subgoals2882179 Node: Other Requirements2882695 Node: Overview of the Expansion of ENDP in the Base Case2883552 Node: Overview of the Expansion of ENDP in the Induction Step2884062 Node: Overview of the Final Simplification in the Base Case2884806 Node: Overview of the Proof of a Trivial Consequence2885332 Node: Overview of the Simplification of the Base Case to T2887062 Node: Overview of the Simplification of the Induction Conclusion2888023 Node: Overview of the Simplification of the Induction Step to T2888722 Node: Perhaps2889923 Node: Popping out of an Inductive Proof2890268 Node: Proving Theorems about Models2890717 Node: Revisiting the Admission of App2892073 Node: Rewrite Rules are Generated from DEFTHM Events2893355 Node: Running Models2894352 Node: Subsumption of Induction Candidates in App Example2895464 Node: Suggested Inductions in the Associativity of App Example2896254 Node: Symbolic Execution of Models2897042 Node: The Admission of App2897745 Node: The Associativity of App2899244 Node: The Base Case in the App Example2900618 Node: The End of the Flying Tour2901199 Node: The End of the Proof of the Associativity of App2901647 Node: The End of the Walking Tour2902516 Node: The Event Summary2903723 Node: The Expansion of ENDP in the Induction Step (Step 0)2905989 Node: The Expansion of ENDP in the Induction Step (Step 1)2906656 Node: The Expansion of ENDP in the Induction Step (Step 2)2907384 Node: The Falling Body Model2908095 Node: The Final Simplification in the Base Case (Step 0)2908905 Node: The Final Simplification in the Base Case (Step 1)2909539 Node: The Final Simplification in the Base Case (Step 2)2910149 Node: The Final Simplification in the Base Case (Step 3)2910679 Node: The First Application of the Associativity Rule2911324 Node: The Induction Scheme Selected for the App Example2912125 Node: The Induction Step in the App Example2912977 Node: The Instantiation of the Induction Scheme2913880 Node: The Justification of the Induction Scheme2914467 Node: The Proof of the Associativity of App2914942 Node: The Q.E.D. Message2917490 Node: The Rules used in the Associativity of App Proof2917882 Node: The Simplification of the Induction Conclusion (Step 0)2918672 Node: The Simplification of the Induction Conclusion (Step 1)2919344 Node: The Simplification of the Induction Conclusion (Step 10)2920470 Node: The Simplification of the Induction Conclusion (Step 11)2921259 Node: The Simplification of the Induction Conclusion (Step 12)2921969 Node: The Simplification of the Induction Conclusion (Step 2)2922636 Node: The Simplification of the Induction Conclusion (Step 3)2923427 Node: The Simplification of the Induction Conclusion (Step 4)2924151 Node: The Simplification of the Induction Conclusion (Step 5)2925067 Node: The Simplification of the Induction Conclusion (Step 6)2925944 Node: The Simplification of the Induction Conclusion (Step 7)2926792 Node: The Simplification of the Induction Conclusion (Step 8)2927613 Node: The Simplification of the Induction Conclusion (Step 9)2928506 Node: The Summary of the Proof of the Trivial Consequence2929447 Node: The Theorem that App is Associative2929967 Node: The Time Taken to do the Associativity of App Proof2931345 Node: The Tours2932131 Node: The WARNING about the Trivial Consequence2934201 Node: Undocumented Topic2935100 Node: Using the Associativity of App to Prove a Trivial Consequence2935411 Node: What Is ACL2(Q)2936192 Node: What is Required of the User(Q)2937185 Node: What is a Mathematical Logic(Q)2938225 Node: What is a Mechanical Theorem Prover(Q)2939146 Node: What is a Mechanical Theorem Prover(Q) (cont)2940070 Node: You Must Think about the Use of a Formula as a Rule2940649 Node: REAL2941766 Node: I-CLOSE2944362 Node: I-LARGE2944654 Node: I-LIMITED2944953 Node: I-SMALL2945241 Node: REAL-LISTP2945533 Node: STANDARD-PART2945844 Node: STANDARDP2946212 Node: RELEASE-NOTES2947244 Node: NOTE-2-02950981 Node: NOTE-2-12952181 Node: NOTE-2-22952574 Node: NOTE-2-32955309 Node: NOTE-2-42957417 Node: NOTE-2-52958902 Node: NOTE-2-5(R)2974167 Node: NOTE-2-62974500 Node: NOTE-2-6-GUARDS2975745 Node: NOTE-2-6-NEW-FUNCTIONALITY2978561 Node: NOTE-2-6-OTHER2987078 Node: NOTE-2-6-PROOF-CHECKER2992276 Node: NOTE-2-6-PROOFS2993162 Node: NOTE-2-6-RULES2998669 Node: NOTE-2-6-SYSTEM3001768 Node: NOTE-2-6(R)3003830 Node: NOTE-2-73004193 Node: NOTE-2-7-BUG-FIXES3008328 Node: NOTE-2-7-GUARDS3021826 Node: NOTE-2-7-NEW-FUNCTIONALITY3022703 Node: NOTE-2-7-OTHER3030278 Node: NOTE-2-7-PROOF-CHECKER3038366 Node: NOTE-2-7-PROOFS3038699 Node: NOTE-2-7-RULES3044050 Node: NOTE-2-7-SYSTEM3044947 Node: NOTE-2-7(R)3047919 Node: NOTE-2-83048656 Node: NOTE-2-8-BUG-FIXES3056410 Node: NOTE-2-8-GUARDS3072907 Node: NOTE-2-8-NEW-FUNCTIONALITY3073208 Node: NOTE-2-8-ORDINALS3080690 Node: NOTE-2-8-OTHER3080921 Node: NOTE-2-8-PROOF-CHECKER3083216 Node: NOTE-2-8-PROOFS3084819 Node: NOTE-2-8-RULES3088910 Node: NOTE-2-8-SYSTEM3090578 Node: NOTE-2-8(R)3091629 Node: NOTE-2-93092127 Node: NOTE-2-9(R)3112854 Node: NOTE-2-9-13113216 Node: NOTE-2-9-23120596 Node: NOTE-2-9-33129447 Node: NOTE-2-9-3-PPR-CHANGE3139077 Node: NOTE-2-9-43147804 Node: NOTE-2-9-53163801 Node: NOTE-3-03175404 Node: NOTE-3-0(R)3176369 Node: NOTE-3-0-13176711 Node: NOTE-3-0-1(R)3188719 Node: NOTE-3-0-23189075 Node: NOTE-3-13204123 Node: NOTE-3-1(R)3204579 Node: NOTE-3-23204923 Node: NOTE-3-2(R)3224543 Node: NOTE-3-2-13225431 Node: NOTE-3-2-1(R)3233676 Node: NOTE-3-33233918 Node: NOTE-3-3(R)3251011 Node: NOTE-3-43251245 Node: NOTE-3-4(R)3277571 Node: NOTE-3-53278154 Node: NOTE-3-5(R)3310648 Node: NOTE-3-63313574 Node: NOTE-3-6(R)3327880 Node: NOTE-3-6-13328114 Node: NOTE-4-03329994 Node: NOTE-4-0-WORMHOLE-CHANGES3366861 Node: NOTE-4-0(R)3369252 Node: NOTE-4-13369477 Node: NOTE-4-1(R)3375775 Node: NOTE-4-23376005 Node: NOTE-4-2(R)3397690 Node: NOTE-4-33397918 Node: NOTE-4-3(R)3425496 Node: NOTE-5-03425721 Node: NOTE-6-03470013 Node: NOTE-6-13488447 Node: NOTE-6-23498387 Node: NOTE-6-33514368 Node: NOTE13527987 Node: NOTE23528802 Node: NOTE33530288 Node: NOTE43535720 Node: NOTE53542089 Node: NOTE63553264 Node: NOTE73558226 Node: NOTE83566414 Node: NOTE8-UPDATE3580521 Node: NOTE93582610 Node: RULE-CLASSES3585571 Node: BUILT-IN-CLAUSE3603585 Node: CLAUSE-PROCESSOR3608809 Node: COMPOUND-RECOGNIZER3622169 Node: CONGRUENCE3631977 Node: DEFINITION3638411 Node: ELIM3647623 Node: EQUIVALENCE3654274 Node: FORWARD-CHAINING3663206 Node: FREE-VARIABLES3671072 Node: FREE-VARIABLES-EXAMPLES3681110 Node: FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING3681959 Node: FREE-VARIABLES-EXAMPLES-REWRITE3688044 Node: FREE-VARIABLES-TYPE-PRESCRIPTION3706133 Node: GENERALIZE3709405 Node: INDUCTION3710900 Node: LINEAR3719028 Node: META3726444 Node: EVALUATOR-RESTRICTIONS3745048 Node: REFINEMENT3757430 Node: REWRITE3759513 Node: TAU-SYSTEM3766480 Node: BOUNDERS3782911 Node: IN-TAU-INTERVALP3798745 Node: MAKE-TAU-INTERVAL3802110 Node: TAU-INTERVAL-DOM3804353 Node: TAU-INTERVAL-HI3804901 Node: TAU-INTERVAL-HI-REL3805550 Node: TAU-INTERVAL-LO3806227 Node: TAU-INTERVAL-LO-REL3806879 Node: TAU-INTERVALP3807554 Node: TYPE-PRESCRIPTION3810316 Node: TYPE-SET-INVERTER3819654 Node: WELL-FOUNDED-RELATION3822135 Node: SERIALIZE3829442 Node: SERIALIZE-ALTERNATIVES3830625 Node: SERIALIZE-IN-BOOKS3831393 Node: SERIALIZE-READ3833137 Node: SERIALIZE-WRITE3834791 Node: SET-SERIALIZE-CHARACTER3835699 Node: WITH-SERIALIZE-CHARACTER3835899 Node: STOBJ3838742 Node: DECLARE-STOBJS3844799 Node: NESTED-STOBJS3846955 Node: RESIZE-LIST3870828 Node: STOBJ-EXAMPLE-13871359 Node: STOBJ-EXAMPLE-1-DEFUNS3880171 Node: STOBJ-EXAMPLE-1-IMPLEMENTATION3884821 Node: STOBJ-EXAMPLE-1-PROOFS3887571 Node: STOBJ-EXAMPLE-23892970 Node: STOBJ-EXAMPLE-33896040 Node: STOBJ-LET3904740 Node: WITH-LOCAL-STATE3904889 Node: WITH-LOCAL-STOBJ3907941 Node: SWITCHES-PARAMETERS-AND-MODES3911020 Node: ACL2-CUSTOMIZATION3919479 Node: ADD-BINOP3923467 Node: ADD-DEFAULT-HINTS3923790 Node: ADD-DEFAULT-HINTS!3925800 Node: ADD-DIVE-INTO-MACRO3926340 Node: ADD-INCLUDE-BOOK-DIR3926812 Node: ADD-INVISIBLE-FNS3929193 Node: ADD-LD-KEYWORD-ALIAS3930240 Node: ADD-LD-KEYWORD-ALIAS!3930447 Node: ADD-MACRO-ALIAS3930653 Node: ADD-MACRO-FN3931474 Node: ADD-MATCH-FREE-OVERRIDE3933466 Node: ADD-NTH-ALIAS3937392 Node: ADD-OVERRIDE-HINTS3938011 Node: ADD-OVERRIDE-HINTS!3939170 Node: DEFAULT-HINTS-TABLE3939592 Node: DEFAULT-VERIFY-GUARDS-EAGERNESS3940451 Node: DELETE-INCLUDE-BOOK-DIR3940693 Node: DIVE-INTO-MACROS-TABLE3941855 Node: FINALIZE-EVENT-USER3943556 Node: INITIALIZE-EVENT-USER3948184 Node: INVISIBLE-FNS-TABLE3950786 Node: LD-KEYWORD-ALIASES3952723 Node: LOGIC3956173 Node: MACRO-ALIASES-TABLE3957366 Node: NTH-ALIASES-TABLE3959938 Node: PRINT-SUMMARY-USER3961010 Node: PROGRAM3961200 Node: PUSH-UNTOUCHABLE3962760 Node: REMOVE-BINOP3964395 Node: REMOVE-DEFAULT-HINTS3964736 Node: REMOVE-DEFAULT-HINTS!3966210 Node: REMOVE-DIVE-INTO-MACRO3966776 Node: REMOVE-INVISIBLE-FNS3967351 Node: REMOVE-MACRO-ALIAS3968216 Node: REMOVE-MACRO-FN3968866 Node: REMOVE-NTH-ALIAS3969536 Node: REMOVE-OVERRIDE-HINTS3970161 Node: REMOVE-OVERRIDE-HINTS!3971176 Node: REMOVE-UNTOUCHABLE3971664 Node: RESET-PREHISTORY3975227 Node: RETURN-LAST-TABLE3976934 Node: RULER-EXTENDERS3978257 Node: SET-ABSSTOBJ-DEBUG3993738 Node: SET-BACKCHAIN-LIMIT3997534 Node: SET-BOGUS-DEFUN-HINTS-OK3999573 Node: SET-BOGUS-MUTUAL-RECURSION-OK4000218 Node: SET-CASE-SPLIT-LIMITATIONS4002071 Node: SET-CHECKPOINT-SUMMARY-LIMIT4008949 Node: SET-COMPILE-FNS4011225 Node: SET-COMPILER-ENABLED4013425 Node: SET-DEBUGGER-ENABLE4013621 Node: SET-DEFAULT-BACKCHAIN-LIMIT4019166 Node: SET-DEFAULT-HINTS4022174 Node: SET-DEFAULT-HINTS!4024437 Node: SET-DEFERRED-TTAG-NOTES4024978 Node: SET-ENFORCE-REDUNDANCY4026363 Node: SET-GAG-MODE4029575 Node: SET-GUARD-CHECKING4034796 Node: SET-IGNORE-DOC-STRING-ERROR4043830 Node: SET-IGNORE-OK4045394 Node: SET-INHIBIT-OUTPUT-LST4046800 Node: SET-INHIBIT-WARNINGS4049315 Node: SET-INHIBIT-WARNINGS!4051500 Node: SET-INHIBITED-SUMMARY-TYPES4052058 Node: SET-INVISIBLE-FNS-TABLE4053535 Node: SET-IRRELEVANT-FORMALS-OK4055819 Node: SET-LD-KEYWORD-ALIASES4056827 Node: SET-LD-KEYWORD-ALIASES!4057048 Node: SET-LD-REDEFINITION-ACTION4057271 Node: SET-LD-SKIP-PROOFS4057497 Node: SET-LD-SKIP-PROOFSP4057708 Node: SET-LET*-ABSTRACTION4057910 Node: SET-LET*-ABSTRACTIONP4058122 Node: SET-MATCH-FREE-DEFAULT4059788 Node: SET-MATCH-FREE-ERROR4062248 Node: SET-MEASURE-FUNCTION4064179 Node: SET-NON-LINEAR4065509 Node: SET-NON-LINEARP4065698 Node: SET-NU-REWRITER-MODE4066231 Node: SET-OVERRIDE-HINTS4068293 Node: SET-OVERRIDE-HINTS!4069106 Node: SET-PARALLEL-EXECUTION4069528 Node: SET-PRINT-CLAUSE-IDS4071467 Node: SET-PROVER-STEP-LIMIT4073366 Node: SET-RAW-MODE4079260 Node: ADD-RAW-ARITY4085609 Node: REMOVE-RAW-ARITY4087119 Node: SET-RAW-MODE-ON!4087499 Node: SET-RAW-PROOF-FORMAT4087994 Node: SET-REWRITE-STACK-LIMIT4088932 Node: SET-RULER-EXTENDERS4090244 Node: SET-RW-CACHE-STATE4090449 Node: SET-RW-CACHE-STATE!4093799 Node: SET-SAVED-OUTPUT4094180 Node: SET-SPLITTER-OUTPUT4097012 Node: SET-STATE-OK4098613 Node: SET-TAU-AUTO-MODE4101453 Node: SET-TOTAL-PARALLELISM-WORK-LIMIT4107557 Node: SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR4109832 Node: SET-VERIFY-GUARDS-EAGERNESS4112046 Node: SET-WATERFALL-PARALLELISM4114445 Node: SET-WATERFALL-PARALLELISM-HACKS-ENABLED4119728 Node: SET-WATERFALL-PARALLELISM-HACKS-ENABLED!4120964 Node: SET-WATERFALL-PRINTING4121306 Node: SET-WELL-FOUNDED-RELATION4124547 Node: SET-WRITE-ACL2X4126002 Node: TAU-STATUS4135878 Node: TERM-TABLE4137568 Node: UNTRANS-TABLE4138799 Node: USER-DEFINED-FUNCTIONS-TABLE4139558 Node: VERIFY-GUARDS-EAGERNESS4142740 Node: WITH-GUARD-CHECKING4142971 Node: WITH-OUTPUT4143845 Node: THEORIES4150653 Node: ACTIVE-RUNEP4159856 Node: CURRENT-THEORY4160459 Node: DISABLE4162140 Node: E/D4163265 Node: ENABLE4164438 Node: EXECUTABLE-COUNTERPART-THEORY4165549 Node: FUNCTION-THEORY4166723 Node: GROUND-ZERO4167817 Node: INCOMPATIBLE4168403 Node: INTERSECTION-THEORIES4168989 Node: MINIMAL-THEORY4169787 Node: RULE-NAMES4170681 Node: RUNE4170974 Node: SET-DIFFERENCE-THEORIES4177849 Node: THEORIES-AND-PRIMITIVES4178866 Node: THEORY4182692 Node: THEORY-FUNCTIONS4183405 Node: UNION-THEORIES4185700 Node: UNIVERSAL-THEORY4186460 Node: TRACE4188191 Node: BREAK-ON-ERROR4189765 Node: CLOSE-TRACE-FILE4191796 Node: OPEN-TRACE-FILE4192269 Node: SET-TRACE-EVISC-TUPLE4192811 Node: TRACE!4195223 Node: TRACE$4202168 Node: UNTRACE$4224247 Node: WET4224942 Node: Index4232250  End Tag Table acl2-sources/doc/EMACS/acl2-doc-emacs.info-10000664002132200015000000110215112222333541017633 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: Top, Up: (DIR) This manual documents ACL2 Version 6.3. * Menu: * ABOUT-ACL2:: about ACL2 * ACL2-TUTORIAL:: tutorial introduction to ACL2 * BDD:: ordered binary decision diagrams with rewriting * BOOKS:: files of ACL2 event forms * BREAK-REWRITE:: the read-eval-print loop entered to monitor rewrite rules * DOCUMENTATION:: functions that display documentation * EVENTS:: functions that extend the logic * FORWARD-CHAINING-REPORTS:: to see reports about the forward chaining process * HISTORY:: functions that display or change history * HONS-AND-MEMOIZATION:: hash cons, function memoization, and applicative hash tables * INTRODUCTION-TO-THE-TAU-SYSTEM:: a decision procedure for runtime types * IO:: input/output facilities in ACL2 * MISCELLANEOUS:: a miscellany of documented functions and concepts (often cited in more accessible documentation) * OTHER:: other commonly used top-level functions * PARALLELISM:: experimental extension for parallel execution and proofs * PROGRAMMING:: programming in ACL2 * PROOF-CHECKER:: support for low-level interaction * PROOF-TREE:: proof tree displays * Pages Written Especially for the Tours:: Pages Written Especially for the Tours * REAL:: ACL2(r) support for real numbers * RELEASE-NOTES:: pointers to what has changed * RULE-CLASSES:: adding rules to the database * SERIALIZE:: routines for saving ACL2 objects to files, and later restoring them * STOBJ:: single-threaded objects or ``von Neumann bottlenecks'' * SWITCHES-PARAMETERS-AND-MODES:: a variety of ways to modify the ACL2 environment * THEORIES:: sets of runes to enable/disable in concert * TRACE:: tracing functions in ACL2 * Index:: An item for each documented ACL2 item.  File: acl2-doc-emacs.info, Node: ABOUT-ACL2, Next: ACL2-TUTORIAL, Prev: Top, Up: Top ABOUT-ACL2 about ACL2 This is ACL2 Version 6.3, copyright (C) 2013, Regents of the University of Texas, authored by Matt Kaufmann and J Strother Moore. For past versions, see `http://www.cs.utexas.edu/users/moore/acl2/current/other-releases.html'. For statistics on ACL2 code size, see file doc/acl2-code-size.txt. See *note DOCUMENTATION:: for how to access the user's manual. See the home page at `http://www.cs.utexas.edu/users/moore/acl2/' for additional information including tutorials, applications, mailing lists, related publications, libraries, ACL2 workshops and seminars, installation instructions, and acknowledgements. See *note COPYRIGHT:: for license and copyright information.  File: acl2-doc-emacs.info, Node: ACL2-TUTORIAL, Next: BDD, Prev: ABOUT-ACL2, Up: Top ACL2-TUTORIAL tutorial introduction to ACL2 To learn about ACL2, read at least the following two links. * Industrial Applications of ACL2 (10 minutes) to help you understand what sophisticated users can do; * A Flying Tour (10 minutes) to get an overview of the system and what skills the user must have. If you want to learn _how to use_ ACL2, we recommend that you read a selection of the materials referenced below, depending on your learning style, and do suggested exercises. * "A Walking Tour" (1 hour) provides an overview of the theorem prover. * The external site `http://tryacl2.org' provides interactive lessons to get you started using ACL2. * "Introduction to the Theorem Prover" (10-40 hours) provides instruction on how to interact with the system. Unlike the three documents above, this document expects you to _think_! It cites the necessary background pages on programming in ACL2 and on the logic and then instructs you in The Method, which is how expert users use ACL2. It concludes with some challenge problems for the ACL2 beginner (including solutions) and an FAQ. Most users will spend several hours a day for several days working through this material. * The book "Computer-Aided Reasoning: An Approach" (see `http://www.cs.utexas.edu/users/moore/publications/acl2-books/car/index.html' is worth a careful read, as you work exercises and learn "The Method." * "Annotated ACL2 Scripts and Demos" contains relatively elementary proof scripts that have been annotated to help train the newcomer. * Many files ("books") in the ACL2 community books (see *note COMMUNITY-BOOKS::) are extensively annotated. See the link to "Lemma Libraries and Utilities" on the ACL2 home page (`http://www.cs.utexas.edu/users/moore/acl2'). * An "Alternative Introduction" document, while largely subsumed by the topic "Introduction to the Theorem Prover" mentioned above, still might be useful because it covers much of the tutorial material in a different way. At this point you are probably ready to use ACL2 on your own _small_ projects. A common mistake for beginners is to browse the documentation and then try to do something that is too big! Think of a very small project and then simplify it! Note that ACL2 has a very supportive user network. See the link to "Mailing Lists" on the ACL2 home page (`http://www.cs.utexas.edu/users/moore/acl2'). The topics listed below are a hodge podge, developed over time. Although some of these are not mentioned above, you might find some to be useful as well. * Menu: * ACL2-AS-STANDALONE-PROGRAM:: Calling ACL2 from another program * ACL2-SEDAN:: ACL2 Sedan interface * ADVANCED-FEATURES:: some advanced features of ACL2 * ALTERNATIVE-INTRODUCTION:: introduction to ACL2 * ANNOTATED-ACL2-SCRIPTS:: examples of ACL2 scripts * EMACS:: emacs support for ACL2 * INTERESTING-APPLICATIONS:: some industrial examples of ACL2 use * INTRODUCTION-TO-THE-THEOREM-PROVER:: how the theorem prover works -- level 0 * STARTUP:: How to start using ACL2; the ACL2 command loop * TIDBITS:: some basic hints for using ACL2 * TIPS:: some hints for using the ACL2 prover  File: acl2-doc-emacs.info, Node: ACL2-AS-STANDALONE-PROGRAM, Next: ACL2-SEDAN, Prev: ACL2-TUTORIAL, Up: ACL2-TUTORIAL ACL2-AS-STANDALONE-PROGRAM Calling ACL2 from another program ACL2 is intended for interactive use. It is generally unrealistic to expect it to prove theorems fully automatically; see *note THE-METHOD::, and see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed tutorial. Nevertheless, here we describe an approach for how to call the ACL2 theorem prover noninteractively. These steps can of course be modified according to your needs. Here, we illustrate how to call ACL2 from another Lisp program (or an arbitrary program) to attempt to prove an arithmetic theorem. === STEP 1: === Build a suitable ACL2 image by starting ACL2 and then executing the following forms. In particular, these define a macro, try-thm, that causes ACL2 to exit with with an exit status indicating success or failure of a proof attempt. (include-book "arithmetic-5/top" :dir :system) (defmacro try-thm (&rest args) `(mv-let (erp val state) (with-prover-time-limit 3 (thm ,@args)) (declare (ignore val)) (prog2$ (if erp (exit 1) (exit 0)) state)))) (reset-prehistory) ; optional :q (save-exec "arith-acl2" "Included arithmetic-4/top") If you prefer, above you can replace 3 by some other number of seconds as a time limit for the prover. Also, you can replace (with-prover-time-limit 3 (thm ,@args)) by (with-output :off :all (with-prover-time-limit 3 (thm ,@args))) if you want to turn off output. It may be best to leave the output on, instead eliminating it in the calling program (see Step 3 below). === STEP 2: === Try a little test. In that same directory try this: echo '(try-thm (equal x x))' | ./arith-acl2 echo $? The exit status should be 0, indicating success. Now try this: echo '(try-thm (not (equal x x)))' | ./arith-acl2 echo $? The exit status should be 1, indicating failure. === STEP 3: === Create a shell script that automates Step 2, for example: #!/bin/sh (echo "(try-thm $1)" | ./arith-acl2) >& /dev/null exit $? === STEP 4: === Try your script from a Lisp program, if you like. Here is how you can do it in SBCL, for example. (Different Lisps have different ways to do this, as summarized in function system-call in ACL2 source file acl2-init.lisp.) (defun provable? (x) (let ((status (process-exit-code (sb-ext:run-program "./try-thm.sh" (list (format nil "~s" x)) :output t :search t)))) (eql status 0))) Then here is a log: * (provable? '(equal x y)) NIL * (provable? '(equal x x)) T * Certainly refinements are possible - for example the above doesn't distinguish between unprovable and ill-formed input. But it's a start.  File: acl2-doc-emacs.info, Node: ACL2-SEDAN, Next: ADVANCED-FEATURES, Prev: ACL2-AS-STANDALONE-PROGRAM, Up: ACL2-TUTORIAL ACL2-SEDAN ACL2 Sedan interface Many successful ACL2 users run in an shell under Emacs; see *note EMACS::. However, those not familiar with Emacs may prefer to start with an Eclipse-based interface initiallly developed by Peter Dillinger and Pete Manolios called the "ACL2 Sedan", or "ACL2s". As of this writing, the home page for ACL2s is `http://acl2s.ccs.neu.edu/acl2s/doc/'. ACL2 sessions in the ACL2 Sedan can utilize non-standard extensions and enhancements, especially geared toward new users, termination reasoning, and attaching rich user interfaces. These extensions are generally available as certifiable ACL2 books, and can be downloaded from `http://acl2s.ccs.neu.edu/acl2s/src/acl2-extensions'. (Some code originating from this project has been migrated to the ACL2 community books, but only after it was quite stable.) Thanks to Peter Dillinger, Pete Manolios, Daron Vroon, and Harsh Raju Chamarthi for their work on the ACL2 Sedan and for making their books available to ACL2 users.  File: acl2-doc-emacs.info, Node: ADVANCED-FEATURES, Next: ALTERNATIVE-INTRODUCTION, Prev: ACL2-SEDAN, Up: ACL2-TUTORIAL ADVANCED-FEATURES some advanced features of ACL2 Maybe you've been using ACL2 for awhile, and you wonder if there are lesser-known features that you might find useful. Then this topic is for you. We present below a "laundry list" of some such features, with brief descriptions and links to documentation topics. Although the list below is long, it is not intended to be complete, and indeed some topics have been deliberately excluded. Some have fallen out of use, perhaps for good reason, such as NU-REWRITER and OBDD. Others are already likely to be discovered when needed, such as GETENV$ and perhaps DOUBLE-REWRITE. Some topics are referenced by documentation for others in the list, such as MBT, which is referenced by MBE. Some utilities such as PSTACK and VERBOSE-PSTACK seem too low-level to be worthy of inclusion below. For an extensive introduction to using the prover, which may include some aspects new to you, see *note INTRODUCTION-TO-THE-THEOREM-PROVER::. A shorter topic contains highlights for efficient prover usage: see *note TIPS::. Also see *note ACL2-SEDAN:: for an extension of ACL2 (written by others), ACL2s, that includes an Eclipse-based interface, more powerful and automatic termination reasoning, and other features. We now move on to the list. ======================================== Top-level commands and utilities: ======================================== o See *note A!:: and see *note P!:: to abort or pop. o See *note ACL2-CUSTOMIZATION:: for initial commands to run at startup. o See *note KEYWORD-COMMANDS:: for how keyword commands are processed. o See *note LD:: for many ways to control the top-level loop. o See *note COMPILATION:: for a discussion of set-compiler-enabled and other compiler-related utilities. o For useful reader macros `#!', `#.', and `#u', see *note SHARP-BANG-READER::, see *note SHARP-DOT-READER::, and see *note SHARP-U-READER::. o To save and use an ACL2 executable, see *note ACL2-AS-STANDALONE-PROGRAM:: and see *note SAVE-EXEC::. o For utilities related to timing, see *note TIME$::, see *note WITH-PROVER-TIME-LIMIT::, see *note WITH-PROVER-STEP-LIMIT::, and see *note SET-PROVER-STEP-LIMIT::. o To query and manage the database, see *note HISTORY:: (which discusses many useful utilities, such as :PBT and :PL), and see *note DEAD-EVENTS::. o See *note ADD-INCLUDE-BOOK-DIR:: for linking keyword for :dir argument of LD and INCLUDE-BOOK. o See *note REBUILD:: for a fast way to load a file without waiting for proofs. o For parallel certification, see *note BOOKS-CERTIFICATION:: for use of the -j option of `make'; also see *note PROVISIONAL-CERTIFICATION::. ======================================== Some relatively less common events: ======================================== o See *note RESET-PREHISTORY:: to reset the prehistory. o See *note ASSERT-EVENT:: to assert that a given form returns a non-nil value. o See *note DEFATTACH:: to execute constrained functions using corresponding attached functions. o See *note DEFUN-SK:: to define a function whose body has an outermost quantifier. o See *note DEFCHOOSE:: to define a Skolem (witnessing) function. o For efficiency consider using defconst-fast; see *note DEFCONST::. o See *note SET-VERIFY-GUARDS-EAGERNESS:: to specify when guard verification is tried by default. ======================================== Output and its control (see *note IO:: for additional information): ======================================== o See *note WITH-OUTPUT:: to suppress or turn on specified output for an event. o See *note EVISC-TABLE:: for support for abbreviated output. o See *note NTH-ALIASES-TABLE:: for a table used to associate names for NTH/UPDATE-NTH printing. o See *note OUTPUT-TO-FILE:: to redirect output to a file. o See *note PRINT-CONTROL:: to control ACL2 printing. o See *note SET-EVISC-TUPLE:: to control suppression of details when printing. o See *note SET-INHIBIT-OUTPUT-LST:: to control output by type. o See *note SET-IPRINT:: to allow abbreviated output to be read back in. o See *note SET-PRINT-BASE:: to control the radix in which numbers are printed. o See *note SET-PRINT-RADIX:: to control whether the radix of a number is printed. o See *note SET-PRINT-CASE:: to control whether symbols are printed in upper case or in lower case. ======================================== On proving termination for definitions: ======================================== o See *note ORDINALS:: for a discussion of ordinals in ACL2. o See *note RULER-EXTENDERS:: for a control on ACL2's termination and induction analyses. o See *note SET-WELL-FOUNDED-RELATION:: to set the default well-founded relation for termination analysis. o See *note ACL2-SEDAN:: for a related tool that provides extra automation for termination proofs. ======================================== Proof debugging and output control: ======================================== o See *note ACCUMULATED-PERSISTENCE:: to get statistics on which runes are being tried. o See *note ADD-MACRO-FN:: and see *note ADD-MACRO-ALIAS:: to associate a function name with a macro name. o See *note BREAK-REWRITE:: for how to monitor rewrite rules. o See *note DMR:: for dynamic monitoring of rewriting and other prover activity. o See *note FORWARD-CHAINING-REPORTS:: to see reports about the forward chaining process. o See *note GUARD-DEBUG:: to generate markers to indicate sources of guard proof obligations. o See *note PROOF-CHECKER:: for support for low-level interaction. o See *note REDO-FLAT:: for redo on failure of a PROGN, ENCAPSULATE, or CERTIFY-BOOK. o See *note SET-GAG-MODE:: and see *note PSO:: to abbreviate or restore proof output. o See *note SET-INHIBIT-OUTPUT-LST::, see *note SET-INHIBIT-WARNINGS::, and see *note SET-INHIBITED-SUMMARY-TYPES:: to inhibit various types of output. o See *note SET-RAW-PROOF-FORMAT:: to make proof output display lists of runes. o See *note SKIP-PROOFS:: to skip proofs for a given form. ======================================== Program debugging: ======================================== o See *note BREAK$:: to cause an immediate Lisp break. o See *note BREAK-ON-ERROR:: to break when encountering a hard or soft error caused by ACL2. o See *note DISASSEMBLE$:: to disassemble a function. o See *note PRINT-GV:: to print a form whose evaluation caused a guard violation. o See *note PROFILE:: to turn on profiling for one function. o See *note TRACE$:: and see *note OPEN-TRACE-FILE:: to trace function evaluations, possibly sending trace output to a file. o See *note WET:: to evaluate a form and print a subsequent error trace. ======================================== Programming and evaluation idioms, support, utilities (also see *note PROGRAMMING:: for more utilities, e.g., RANDOM$) ======================================== o See *note ARRAYS:: and See *note DEFSTOBJ:: for introductions to ACL2 arrays and single-threaded objects (stobjs), respectively, each of which provides efficient destructive operations in an applicative setting. Also see *note WITH-LOCAL-STOBJ:: for a way to create local stobjs. o See *note ASSERT$:: to cause a hard error if the given test is false. o See *note CANONICAL-PATHNAME:: to obtain the true absolute filename, with soft links resolved. o See *note CASE-MATCH:: for a utility providing pattern matching and destructuring. o See *note DEFPUN:: to define a tail-recursive function symbol. o See *note EC-CALL:: to execute a call in the ACL2 logic instead of raw Lisp. o See *note ER:: to print an error message and "cause an error". o See *note FLET:: to provide local binding of function symbols. o See *note GC$:: to invoke the garbage collector. o See *note MBE:: to attach code for execution. o See *note MV-LIST:: to convert a multiple-valued result to a single-valued list. o See *note MV?:: to return one or more values. o For non-executable code, see *note DEFUN-NX:: and see *note NON-EXEC::. o See *note PROG2$:: and see *note PROGN$:: to execute two or more forms and return the value of the last one. o See *note PROGRAMMING-WITH-STATE:: for how to program using the von Neumannesque ACL2 state object. o See *note TOP-LEVEL:: to evaluate a top-level form as a function body. o See *note WITH-GUARD-CHECKING:: to suppress or enable guard-checking for a form. o For ways to fake access to the state see *note WORMHOLE::, see *note WITH-LOCAL-STATE::, see *note CW::, see *note CW!::, see *note PRINTING-TO-STRINGS::, see *note OBSERVATION-CW::, and (dangerous!) see *note WITH-LIVE-STATE::. ======================================== Connecting with the underlying host Lisp, and doing other evil: ======================================== o See *note DEFTTAG:: to introduce a trust tag (ttag). o See *note DEFMACRO-LAST:: to define a macro that returns its last argument, but with side effects. o See *note PROGN!:: to evaluate forms that are not necessarily events. o See *note RETURN-LAST:: to return the last argument, perhaps with side effects. o See *note SET-RAW-MODE:: to enter or exit "raw mode," a raw Lisp environment. o See *note SYS-CALL:: to make a system call to the host operating system. ======================================== Macros and related utilities: ======================================== o See *note DEFABBREV:: for a convenient form of macro definition for simple expansions. o See *note MACRO-ARGS:: for the formals list of a macro definition (see *note DEFMACRO::). o See *note MAKE-EVENT:: for a sort of extension of DEFMACRO that allows access to the state, by evaluating (expanding) a given form and then evaluate the result of that expansion. o See *note TRANS::, see *note TRANS!::, and see *note TRANS1:: to print the macroexpansion of a form. ======================================== Experimental extensions: ======================================== o See *note HONS-AND-MEMOIZATION:: for ACL2(h); in particular, see *note MEMOIZE:: for efficient function memoization and see *note PROFILE:: for profiling. o See *note REAL:: for ACL2(r), which supports the real numbers. o See *note PARALLELISM:: for ACL2(p), which supports parallel evaluation and proof. ======================================== Database control and query: ======================================== o See *note DISABLEDP:: to determine whether a given name or rune is disabled. o For redefinition support see *note REDEF::, see *note REDEF!::, see *note REDEF+::, see *note REDEF-::, and see *note REDEFINED-NAMES::. o See *note TABLE:: for user-managed tables. o See *note VERIFY-GUARDS-FORMULA:: to view a guard proof obligation without doing the proof. ======================================== Prover control ======================================== o For congruence-based reasoning see *note DEFCONG::, see *note CONGRUENCE::, see *note EQUIVALENCE::, see *note DEFEQUIV::, and see *note DEFREFINEMENT::. o For meta rules and clause processors see *note META::, see *note DEFEVALUATOR::, see *note CLAUSE-PROCESSOR::, see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR:: (for connecting with external tools, such as SAT solvers), and See *note EXTENDED-METAFUNCTIONS:: (for state and context-sensitive metafunctions). o For theory control, see *note THEORIES:: for detailed information, but in particular see *note DEFTHEORY::, see *note THEORY-FUNCTIONS::, see *note IN-ARITHMETIC-THEORY:: (and see *note NON-LINEAR-ARITHMETIC::), and see *note THEORY-INVARIANT::. o See *note HINTS:: for a complete list of prover hints, including some of the more obscure ones such as :restrict, :clause-processor, :nonlinearp, :backchain-limit-rw, :reorder, and :backtrack. Also see *note HINTS-AND-THE-WATERFALL:: for an explanation of how hints interact with the ACL2 proof process. For other topics related to hints, see *note OVERRIDE-HINTS::, see *note ADD-CUSTOM-KEYWORD-HINT::, see *note DEFAULT-HINTS::, and see *note COMPUTED-HINTS:: (also see *note USING-COMPUTED-HINTS:: and for other topics USING-COMPUTED-HINTS-xxx see *note MISCELLANEOUS::. o See *note BIND-FREE:: to bind free-variables of a rewrite or linear rule. o See *note CASE-SPLIT:: for a utility like FORCE that immediately splits the top-level goal on the indicated hypothesis. o See *note CASE-SPLIT-LIMITATIONS:: for a way to the number of cases produced at once o See *note DEFAULT-BACKCHAIN-LIMIT:: to specify the backchain limit for a rule. o See *note FORCE:: for an identity function used to force a hypothesis. o See *note OTF-FLG:: for a way to push more than one initial subgoal for induction. o See *note RULE-CLASSES:: to add various kinds of rules to the database, including more unusual sorts such as :built-in-clause rules and :induction rules. o See *note SET-BACKCHAIN-LIMIT:: to set the backchain-limit used by the type-set and rewriting mechanisms. o See *note SET-BODY:: to set an alternate definition body for :expand hints. o See *note SET-REWRITE-STACK-LIMIT:: to set the rewrite stack depth used by the rewriter. o See *note SYNTAXP:: to attach a heuristic filter on a :rewrite, :meta, or :linear rule.  File: acl2-doc-emacs.info, Node: ALTERNATIVE-INTRODUCTION, Next: ANNOTATED-ACL2-SCRIPTS, Prev: ADVANCED-FEATURES, Up: ACL2-TUTORIAL ALTERNATIVE-INTRODUCTION introduction to ACL2 This section contains introductory material on ACL2 including what ACL2 is, how to get started using the system, how to read the output, and other introductory topics. It was written almost entirely by Bill Young of Computational Logic, Inc. You might also find CLI Technical Report 101 helpful, especially if you are familiar with Nqthm. If you would like more familiarity with Nqthm, we suggest CLI Technical Report 100. _OVERVIEW_ ACL2 is an automated reasoning system developed (for the first 9 years) at Computational Logic, Inc. and (from January, 1997) at the University of Texas at Austin. It is the successor to the Nqthm (or Boyer-Moore) logic and proof system and its Pc-Nqthm interactive enhancement. The acronym ACL2 actually stands for "A Computational Logic for Applicative Common Lisp". This title suggests several distinct but related aspects of ACL2. We assume that readers of the ACL2 documentation have at least a very slight familiarity with some Lisp-like language. We will address the issue of prerequisites further, in "ABOUT THIS TUTORIAL" below. As a logic, ACL2 is a formal system with rigorously defined syntax and semantics. In mathematical parlance, the ACL2 logic is a first-order logic of total recursive functions providing mathematical induction on the ordinals up to epsilon-0 and two extension principles: one for recursive definition and one for constrained introduction of new function symbols, here called encapsulation. The syntax of ACL2 is that of Common Lisp; ACL2 specifications are "also" Common Lisp programs in a way that we will make clear later. In less formal language, the ACL2 logic is an integrated collection of rules for defining (or axiomatizing) recursive functions, stating properties of those functions, and rigorously establishing those properties. Each of these activities is mechanically supported. As a specification language, ACL2 supports modeling of systems of various kinds. An ACL2 function can equally be used to express purely formal relationships among mathematical entities, to describe algorithms, or to capture the intended behavior of digital systems. For digital systems, an ACL2 specification is a mathematical model that is intended to formalize relevant aspects of system behavior. Just as physics allows us to model the behavior of continuous physical systems, ACL2 allows us to model digital systems, including many with physical realizations such as computer hardware. As early as the 1930's Church, Kleene, Turing and others established that recursive functions provide an expressive formalism for modeling digital computation. Digital computation should be understood in a broad sense, covering a wide variety of activities including almost any systematic or algorithmic activity, or activity that can be reasonably approximated in that way. This ranges from the behavior of a digital circuit to the behavior of a programming language compiler to the behavior of a controller for a physical system (as long as the system can be adequately modeled discretely). All of these have been modeled using ACL2 or its predecessor Nqthm. ACL2 is a computational logic in at least three distinct senses. First, the theory of recursive functions is often considered the mathematics of computation. Church conjectured that any "effective computation" can be modeled as a recursive function. Thus, ACL2 provides an expressive language for modeling digital systems. Second, many ACL2 specifications are executable. In fact, recursive functions written in ACL2 are Common Lisp functions that can be submitted to any compliant Common Lisp compiler and executed (in an environment where suitable ACL2-specific macros and functions are defined). Third, ACL2 is computational in the sense that calculation is heavily integrated into the reasoning process. Thus, an expression with explicit constant values but no free variables can be simplified by calculation rather than by complex logical manipulations. ACL2 is a powerful, automated theorem prover or proof checker. This means that a competent user can utilize the ACL2 system to discover proofs of theorems stated in the ACL2 logic or to check previously discovered proofs. The basic deductive steps in an ACL2-checked proof are often quite large, due to the sophisticated combination of decision procedures, conditional rewriting, mathematical and structural induction, propositional simplification, and complex heuristics to orchestrate the interactions of these capabilities. Unlike some automated proof systems, ACL2 does not produce a formal proof. However, we believe that if ACL2 certifies the "theoremhood" of a given conjecture, then such a formal proof exists and, therefore, the theorem is valid. The ultimate result of an ACL2 proof session is a collection of "events," possibly grouped into "books," that can be replayed in ACL2. Therefore, a proof can be independently validated by any ACL2 user. ACL2 may be used in purely automated mode in the shallow sense that conjectures are submitted to the prover and the user does not interact with the proof attempt (except possibly to stop it) until the proof succeeds or fails. However, any non-trivial proof attempt is actually interactive, since successful proof "events" influence the subsequent behavior of the prover. For example, proving a lemma may introduce a rule that subsequently is used automatically by the prover. Thus, any realistic proof attempt, even in "automatic" mode, is really an interactive dialogue with the prover to craft a sequence of events building an appropriate theory and proof rules leading up to the proof of the desired result. Also, ACL2 supports annotating a theorem with "hints" designed to guide the proof attempt. By supplying appropriate hints, the user can suggest proof strategies that the prover would not discover automatically. There is a "proof-tree" facility (see *note PROOF-TREE::) that allows the user to monitor the progress and structure of a proof attempt in real-time. Exploring failed proof attempts is actually where heavy-duty ACL2 users spend most of their time. ACL2 can also be used in a more explicitly interactive mode. The "proof-checker" subsystem of ACL2 allows exploration of a proof on a fairly low level including expanding calls of selected function symbols, invoking specific rewrite rules, and selectively navigating around the proof. This facility can be used to gain sufficient insight into the proof to construct an automatic version, or to generate a detailed interactive-style proof that can be replayed in batch mode. Because ACL2 is all of these things -- computational logic, specification language, programming system, and theorem prover -- it is more than the sum of its parts. The careful integration of these diverse aspects has produced a versatile automated reasoning system suitable for building highly reliable digital systems. In the remainder of this tutorial, we will illustrate some simple uses of this automated reasoning system. _ABOUT THIS TUTORIAL_ ACL2 is a complex system with a vast array of features, bells and whistles. However, it is possible to perform productive work with the system using only a small portion of the available functionality. The goals of this tutorial are to: familiarize the new user with the most basic features of and modes of interaction with ACL2; familiarize her with the form of output of the system; and work through a graduated series of examples. The more knowledge the user brings to this system, the easier it will be to become proficient. On one extreme: the ideal user of ACL2 is an expert Common Lisp programmer, has deep understanding of automated reasoning, and is intimately familiar with the earlier Nqthm system. Such ideal users are unlikely to need this tutorial. However, without some background knowledge, the beginning user is likely to become extremely confused and frustrated by this system. We suggest that a new user of ACL2 should: (a) have a little familiarity with Lisp, including basic Lisp programming and prefix notation (a Lisp reference manual such as Guy Steele's "Common Lisp: The Language" is also helpful); (b) be convinced of the utility of formal modeling; and (c) be willing to gain familiarity with basic automated theorem proving topics such as rewriting and algebraic simplification. We will not assume any deep familiarity with Nqthm (the so-called "Boyer-Moore Theorem Prover"), though the book "A Computational Logic Handbook" by Boyer and Moore (Academic Press, 1988) is an extremely useful reference for many of the topics required to become a competent ACL2 user. We'll refer to it as ACLH below. As we said in the introduction, ACL2 has various facets. For example, it can be used as a Common Lisp programming system to construct application programs. In fact, the ACL2 system itself is a large Common Lisp program constructed almost entirely within ACL2. Another use of ACL2 is as a specification and modeling tool. That is the aspect we will concentrate on in the remainder of this tutorial. _GETTING STARTED_ This section is an abridged version of what's available elsewhere; feel free to see *note STARTUP:: for more details. How you start ACL2 will be system dependent, but you'll probably type something like "acl2" at your operating system prompt. Consult your system administrator for details. When you start up ACL2, you'll probably find yourself inside the ACL2 command loop, as indicated by the following prompt. ACL2 !> If not, you should type (LP). See *note LP::, which has a lot more information about the ACL2 command loop. There are two "modes" for using ACL2, :logic and :program. When you begin ACL2, you will ordinarily be in the :logic mode. This means that any new function defined is not only executable but also is axiomatically defined in the ACL2 logic. (See *note DEFUN-MODE:: and see *note DEFAULT-DEFUN-MODE::.) Roughly speaking, :program mode is available for using ACL2 as a programming language without some of the logical burdens necessary for formal reasoning. In this tutorial we will assume that we always remain in :logic mode and that our purpose is to write formal models of digital systems and to reason about them. Now, within the ACL2 command loop you can carry out various kinds of activities, including the folllowing. (We'll see examples later of many of these.) define new functions (see *note DEFUN::); execute functions on concrete data; pose and attempt to prove conjectures about previously defined functions (see *note DEFTHM::); query the ACL2 "world" or database (e.g., see *note PE::); and numerous other things. In addition, there is extensive on-line documentation, of which this tutorial introduction is a part. _INTERACTING WITH ACL2_ The standard means of interacting with ACL2 is to submit a sequence of forms for processing by the ACL2 system. These forms are checked for syntactic and semantic acceptability and appropriately processed by the system. These forms can be typed directly at the ACL2 prompt. However, most successful ACL2 users prefer to do their work using the Emacs text editor, maintaining an Emacs "working" buffer in which forms are edited. Those forms are then copied to the ACL2 interaction buffer, which is often the "*shell*" buffer. In some cases, processing succeeds and makes some change to the ACL2 "logical world," which affects the processing of subsequent forms. How can this processing fail? For example, a proposed theorem will be rejected unless all function symbols mentioned have been previously defined. Also the ability of ACL2 to discover the proof of a theorem may depend on the user previously having proved other theorems. Thus, the order in which forms are submitted to ACL2 is quite important. Maintaining forms in an appropriate order in your working buffer will be helpful for re-playing the proof later. One of the most common events in constructing a model is introducing new functions. New functions are usually introduced using the defun form; we'll encounter some exceptions later. Proposed function definitions are checked to make sure that they are syntactically and semantically acceptable (e.g., that all mentioned functions have been previously defined) and, for recursive functions, that their recursive calls terminate. A recursive function definition is guaranteed to terminate if there is some some "measure" of the arguments and a "well-founded" ordering such that the arguments to the function get smaller in each recursive call. See *note WELL-FOUNDED-RELATION::. For example, suppose that we need a function that will append two lists together. (We already have one in the ACL2 append function; but suppose perversely that we decide to define our own.) Suppose we submit the following definition (you should do so as well and study the system output): (defun my-app (x y) (if (atom x) y (cons (car x) (my-app x y)))) The system responds with the following message: ACL2 Error in ( DEFUN MY-APP ...): No :MEASURE was supplied with the definition of MY-APP. Our heuristics for guessing one have not made any suggestions. No argument of the function is tested along every branch and occurs as a proper subterm at the same argument position in every recursive call. You must specify a :MEASURE. See :DOC defun. This means that the system could not find an expression involving the formal parameters x and y that decreases under some well-founded order in every recursive call (there is only one such call). It should be clear that there is no such measure in this case because the only recursive call doesn't change the arguments at all. The definition is obviously flawed; if it were accepted and executed it would loop forever. Notice that a definition that is rejected is not stored in the system database; there is no need to take any action to have it "thrown away." Let's try again with the correct definition. The interaction now looks like (we're also putting in the ACL2 prompt; you don't type that): ACL2 !>(defun my-app (x y) (if (atom x) y (cons (car x) (my-app (cdr x) y)))) The admission of MY-APP is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We observe that the type of MY-APP is described by the theorem (OR (CONSP (MY-APP X Y)) (EQUAL (MY-APP X Y) Y)). We used primitive type reasoning. Summary Form: ( DEFUN MY-APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.07 seconds (prove: 0.00, print: 0.00, other: 0.07) MY-APP Notice that this time the function definition was accepted. We didn't have to supply a measure explicitly; the system inferred one from the form of the definition. On complex functions it may be necessary to supply a measure explicitly. (See *note XARGS::.) The system output provides several pieces of information. The revised definition is acceptable. The system realized that there is a particular measure (namely, (acl2-count x)) and a well-founded relation (o<) under which the arguments of my-app get smaller in recursion. Actually, the theorem prover proved several theorems to admit my-app. The main one was that when (atom x) is false the acl2-count of (cdr x) is less than (in the o< sense) the acl2-count of x. Acl2-count is the most commonly used measure of the "size" of an ACL2 object. o< is the ordering relation on ordinals less than epsilon-0. On the natural numbers it is just ordinary "<". The observation printed about "the type of MY-APP" means that calls of the function my-app will always return a value that is either a cons pair or is equal to the second parameter. The summary provides information about which previously introduced definitions and lemmas were used in this proof, about some notable things to watch out for (the Warnings), and about how long this event took to process. Usually, it's not important to read this information. However, it is a good habit to scan it briefly to see if the type information is surprising to you or if there are Warnings. We'll see an example of them later. After a function is accepted, it is stored in the database and available for use in other function definitions or lemmas. To see the definition of any function use the :pe command (see *note PE::). For example, ACL2 !>:pe my-app L 73:x(DEFUN MY-APP (X Y) (IF (ATOM X) Y (CONS (CAR X) (MY-APP (CDR X) Y)))) This displays the definition along with some other relevant information. In this case, we know that this definition was processed in :logic mode (the "L") and was the 73rd command processed in the current session. We can also try out our newly defined function on some sample data. To do that, just submit a form to be evaluated to ACL2. For example, ACL2 !>(my-app '(0 1 2) '(3 4 5)) (0 1 2 3 4 5) ACL2 !>(my-app nil nil) NIL ACL2 !> Now suppose we want to prove something about the function just introduced. We conjecture, for example, that the length of the append of two lists is the sum of their lengths. We can formulate this conjecture in the form of the following ACL2 defthm form. (defthm my-app-length (equal (len (my-app x y)) (+ (len x) (len y)))) First of all, how did we know about the functions len and +, etc.? The answer to that is somewhat unsatisfying -- we know them from our past experience in using Common Lisp and ACL2. It's hard to know that a function such as len exists without first knowing some Common Lisp. If we'd guessed that the appropriate function was called length (say, from our knowledge of Lisp) and tried :pe length, we would have seen that length is defined in terms of len, and we could have explored from there. Luckily, you can write a lot of ACL2 functions without knowing too many of the primitive functions. Secondly, why don't we need some "type" hypotheses? Does it make sense to append things that are not lists? Well, yes. ACL2 and Lisp are both quite weakly typed. For example, inspection of the definition of my-app shows that if x is not a cons pair, then (my-app x y) always returns y, no matter what y is. Thirdly, would it matter if we rewrote the lemma with the equality reversed, as follows? (defthm my-app-length2 (equal (+ (len x) (len y)) (len (my-app x y)))). The two are logically equivalent, but...yes, it would make a big difference. Recall our remark that a lemma is not only a "fact" to be proved; it also is used by the system to prove other later lemmas. The current lemma would be stored as a rewrite rule. (See *note RULE-CLASSES::.) For a rewrite rule, a conclusion of the form (EQUAL LHS RHS) means to replace instances of the LHS by the appropriate instance of the RHS. Presumably, it's better to rewrite (len (my-app x y)) to (+ (len x) (len y)) than the other way around. The reason is that the system "knows" more about + than it does about the new function symbol my-app. So let's see if we can prove this lemma. Submitting our preferred defthm to ACL2 (do it!), we get the following interaction: -------------------------------------------------- ACL2 !>(defthm my-app-length (equal (len (my-app x y)) (+ (len x) (len y)))) Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. These merge into two derived induction schemes. However, one of these is flawed and so we are left with one viable candidate. We will induct according to a scheme suggested by (LEN X), but modified to accommodate (MY-APP X Y). If we let (:P X Y) denote *1 above then the induction scheme we'll use is (AND (IMPLIES (NOT (CONSP X)) (:P X Y)) (IMPLIES (AND (CONSP X) (:P (CDR X) Y)) (:P X Y))). This induction is justified by the same argument used to admit LEN, namely, the measure (ACL2-COUNT X) is decreasing according to the relation O< (which is known to be well-founded on the domain recognized by O-P). When applied to the goal at hand the above induction scheme produces the following two nontautological subgoals. Subgoal *1/2 (IMPLIES (NOT (CONSP X)) (EQUAL (LEN (MY-APP X Y)) (+ (LEN X) (LEN Y)))). But simplification reduces this to T, using the :definitions of FIX, LEN and MY-APP, the :type-prescription rule LEN, the :rewrite rule UNICITY-OF-0 and primitive type reasoning. Subgoal *1/1 (IMPLIES (AND (CONSP X) (EQUAL (LEN (MY-APP (CDR X) Y)) (+ (LEN (CDR X)) (LEN Y)))) (EQUAL (LEN (MY-APP X Y)) (+ (LEN X) (LEN Y)))). This simplifies, using the :definitions of LEN and MY-APP, primitive type reasoning and the :rewrite rules COMMUTATIVITY-OF-+ and CDR-CONS, to Subgoal *1/1' (IMPLIES (AND (CONSP X) (EQUAL (LEN (MY-APP (CDR X) Y)) (+ (LEN Y) (LEN (CDR X))))) (EQUAL (+ 1 (LEN (MY-APP (CDR X) Y))) (+ (LEN Y) 1 (LEN (CDR X))))). But simplification reduces this to T, using linear arithmetic, primitive type reasoning and the :type-prescription rule LEN. That completes the proof of *1. Q.E.D. Summary Form: ( DEFTHM MY-APP-LENGTH ...) Rules: ((:REWRITE UNICITY-OF-0) (:DEFINITION FIX) (:REWRITE COMMUTATIVITY-OF-+) (:DEFINITION LEN) (:REWRITE CDR-CONS) (:DEFINITION MY-APP) (:TYPE-PRESCRIPTION LEN) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:FAKE-RUNE-FOR-LINEAR NIL)) Warnings: None Time: 0.30 seconds (prove: 0.13, print: 0.05, other: 0.12) MY-APP-LENGTH -------------------------------------------------- Wow, it worked! In brief, the system first tried to rewrite and simplify as much as possible. Nothing changed; we know that because it said "Name the formula above *1." Whenever the system decides to name a formula in this way, we know that it has run out of techniques to use other than proof by induction. The induction performed by ACL2 is structural or "Noetherian" induction. You don't need to know much about that except that it is induction based on the structure of some object. The heuristics infer the structure of the object from the way the object is recursively decomposed by the functions used in the conjecture. The heuristics of ACL2 are reasonably good at selecting an induction scheme in simple cases. It is possible to override the heuristic choice by providing an :induction hint (see *note HINTS::). In the case of the theorem above, the system inducts on the structure of x as suggested by the decomposition of x in both (my-app x y) and (len x). In the base case, we assume that x is not a consp. In the inductive case, we assume that it is a consp and assume that the conjecture holds for (cdr x). There is a close connection between the analysis that goes on when a function like my-app is accepted and when we try to prove something inductively about it. That connection is spelled out well in Boyer and Moore's book "A Computational Logic," if you'd like to look it up. But it's pretty intuitive. We accepted my-app because the "size" of the first argument x decreases in the recursive call. That tells us that when we need to prove something inductively about my-app, it's a good idea to try an induction on the size of the first argument. Of course, when you have a theorem involving several functions, it may be necessary to concoct a more complicated induction schema, taking several of them into account. That's what's meant by "merging" the induction schemas. The proof involves two cases: the base case, and the inductive case. You'll notice that the subgoal numbers go down rather than up, so you always know how many subgoals are left to process. The base case (Subgoal *1/2) is handled by opening up the function definitions, simplifying, doing a little rewriting, and performing some reasoning based on the types of the arguments. You'll often encounter references to system defined lemmas (like unicity-of-0). You can always look at those with :pe; but, in general, assume that there's a lot of simplification power under the hood that's not too important to understand fully. The inductive case (Subgoal *1/1) is also dispatched pretty easily. Here we assume the conjecture true for the cdr of the list and try to prove it for the entire list. Notice that the prover does some simplification and then prints out an updated version of the goal (Subgoal *1/1'). Examining these gives you a pretty good idea of what's going on in the proof. Sometimes one goal is split into a number of subgoals, as happened with the induction above. Sometimes after some initial processing the prover decides it needs to prove a subgoal by induction; this subgoal is given a name and pushed onto a stack of goals. Some steps, like generalization (see ACLH), are not necessarily validity preserving; that is, the system may adopt a false subgoal while trying to prove a true one. (Note that this is ok in the sense that it is not "unsound." The system will fail in its attempt to establish the false subgoal and the main proof attempt will fail.) As you gain facility with using the prover, you'll get pretty good at recognizing what to look for when reading a proof script. The prover's proof-tree utility helps with monitoring an ongoing proof and jumping to designated locations in the proof (see *note PROOF-TREE::). See *note TIPS:: for a number of useful pointers on using the theorem prover effectively. When the prover has successfully proved all subgoals, the proof is finished. As with a defun, a summary of the proof is printed. This was an extremely simple proof, needing no additional guidance. More realistic examples typically require the user to look carefully at the failed proof log to find ways to influence the prover to do better on its next attempt. This means either: proving some rules that will then be available to the prover, changing the global state in ways that will affect the proof, or providing some hints locally that will influence the prover's behavior. Proving this lemma (my-app-length) is an example of the first. Since this is a rewrite rule, whenever in a later proof an instance of the form (LEN (MY-APP X Y)) is encountered, it will be rewritten to the corresponding instance of (+ (LEN X) (LEN Y)). Disabling the rule by executing the command (in-theory (disable my-app-length)), is an example of a global change to the behavior of the prover since this rewrite will not be performed subsequently (unless the rule is again enabled). Finally, we can add a (local) disable "hint" to a defthm, meaning to disable the lemma only in the proof of one or more subgoals. For example: (defthm my-app-length-commutativity (equal (len (my-app x y)) (len (my-app y x))) :hints (("Goal" :in-theory (disable my-app-length)))) In this case, the hint supplied is a bad idea since the proof is much harder with the hint than without it. Try it both ways. By the way, to undo the previous event use :u (see *note U::). To undo back to some earlier event use :ubt (see *note UBT::). To view the current event use :pe :here. To list several events use :pbt (see *note PBT::). Notice the form of the hint in the previous example (see *note HINTS::). It specifies a goal to which the hint applies. "Goal" refers to the top-level goal of the theorem. Subgoals are given unique names as they are generated. It may be useful to suggest that a function symbol be disabled only for Subgoal 1.3.9, say, and a different function enabled only on Subgoal 5.2.8. Overuse of such hints often suggests a poor global proof strategy. We now recommend that you visit documentation on additional examples. See *note ANNOTATED-ACL2-SCRIPTS::.  File: acl2-doc-emacs.info, Node: ANNOTATED-ACL2-SCRIPTS, Next: EMACS, Prev: ALTERNATIVE-INTRODUCTION, Up: ACL2-TUTORIAL ANNOTATED-ACL2-SCRIPTS examples of ACL2 scripts Beginning users may find these annotated scripts useful. We suggest that you read these in the following order: Tutorial1-Towers-of-Hanoi Tutorial2-Eights-Problem Tutorial3-Phonebook-Example Tutorial4-Defun-Sk-Example Tutorial5-Miscellaneous-Examples The page `http://www.cs.utexas.edu/users/moore/publications/tutorial/rev3.html' contains a script that illustrates how it feels to use The Method to prove an unusual list reverse function correct. The screen shots of ACL2's proof output are outdated - in the version shown, ACL2 does not print Key Checkpoints, but the concept of key checkpoint is clear in the discussion and the behavior of the user. See `http://www.cs.utexas.edu/users/moore/acl2/contrib/POLISHING-PROOFS-TUTORIAL.html' for a tutorial on becoming successful at approaching a formalization and proof problem in ACL2. That tutorial, written by Shilpi Goel and Sandip Ray, has two parts: it illustrates how to guide the theorem prover to a successful proof, and it shows how to clean up the proof in order to facilitate maintenance and extension of the resulting book (see *note BOOKS::). At `http://www.cs.utexas.edu/users/moore/publications/tutorial/kaufmann-TPHOLs08/index.html' is the demo given by Matt Kaufmann at TPHOLs08, including all the scripts. There is a gzipped tar file containing the entire contents of the demos. At `http://www.cs.utexas.edu/users/moore/publications/tutorial/sort-equivalence' is a collection of scripts illustrating both high-level strategy and lower-level tactics dealing with the functional equivalence of various list sorting algorithms. Start with the README on that directory. There is also a gzipped tar file containing all the scripts, at `http://www.cs.utexas.edu/users/moore/publications/tutorial/sort-equivalence.tgz'. When you feel you have read enough examples, you might want to try the following very simple example on your own. (See *note SOLUTION-TO-SIMPLE-EXAMPLE:: for a solution, after you work on this example.) First define the notion of the "fringe" of a tree, where we identify trees simply as cons structures, with atoms at the leaves. For example: ACL2 !>(fringe '((a . b) c . d)) (A B C D) Next, define the notion of a "leaf" of a tree, i.e., a predicate leaf-p that is true of an atom if and only if that atom appears at the tip of the tree. Define this notion without referencing the function fringe. Finally, prove the following theorem, whose proof may well be automatic (i.e., not require any lemmas). (defthm leaf-p-iff-member-fringe (iff (leaf-p atm x) (member-equal atm (fringe x)))) * Menu: * SOLUTION-TO-SIMPLE-EXAMPLE:: solution to a simple example * TUTORIAL1-TOWERS-OF-HANOI:: The Towers of Hanoi Example * TUTORIAL2-EIGHTS-PROBLEM:: The Eights Problem Example * TUTORIAL3-PHONEBOOK-EXAMPLE:: A Phonebook Specification * TUTORIAL4-DEFUN-SK-EXAMPLE:: example of quantified notions * TUTORIAL5-MISCELLANEOUS-EXAMPLES:: miscellaneous ACL2 examples  File: acl2-doc-emacs.info, Node: SOLUTION-TO-SIMPLE-EXAMPLE, Next: TUTORIAL1-TOWERS-OF-HANOI, Prev: ANNOTATED-ACL2-SCRIPTS, Up: ANNOTATED-ACL2-SCRIPTS SOLUTION-TO-SIMPLE-EXAMPLE solution to a simple example To see a statement of the problem solved below, see *note ANNOTATED-ACL2-SCRIPTS:: (in particular the end of that topic). Here is a sequence of ACL2 events that illustrates the use of ACL2 to make definitions and prove theorems. We will introduce the notion of the fringe of a tree, as well as the notion of a leaf of a tree, and then prove that the members of the fringe are exactly the leaves. We begin by defining the fringe of a tree, where we identify trees simply as cons structures, with atoms at the leaves. The definition is recursive, breaking into two cases. If x is a cons, then the fringe of x is obtained by appending together the fringes of the car and cdr (left and right child) of x. Otherwise, x is an atom and its fringe is the one-element list containing only x. (defun fringe (x) (if (consp x) (append (fringe (car x)) (fringe (cdr x))) (list x))) Now that fringe has been defined, let us proceed by defining the notion of an atom appearing as a "leaf", with the goal of proving that the leaves of a tree are exactly the members of its fringe. (defun leaf-p (atm x) (if (consp x) (or (leaf-p atm (car x)) (leaf-p atm (cdr x))) (equal atm x))) The main theorem is now as follows. Note that the rewrite rule below uses the equivalence relation iff (see *note EQUIVALENCE::) rather than equal, since member returns the tail of the given list that begins with the indicated member, rather than returning a Boolean. (Use :pe member to see the definition of member.) (defthm leaf-p-iff-member-fringe (iff (leaf-p atm x) (member-equal atm (fringe x))))  File: acl2-doc-emacs.info, Node: TUTORIAL1-TOWERS-OF-HANOI, Next: TUTORIAL2-EIGHTS-PROBLEM, Prev: SOLUTION-TO-SIMPLE-EXAMPLE, Up: ANNOTATED-ACL2-SCRIPTS TUTORIAL1-TOWERS-OF-HANOI The Towers of Hanoi Example This example was written almost entirely by Bill Young of Computational Logic, Inc. We will formalize and prove a small theorem about the famous "Towers of Hanoi" problem. This problem is illustrated by the following picture. | | | | | | --- | | ----- | | ------- | | A B C We have three pegs -- a, b, and c -- and n disks of different sizes. The disks are all initially on peg a. The goal is to move all disks to peg c while observing the following two rules. 1. Only one disk may be moved at a time, and it must start and finish the move as the topmost disk on some peg; 2. A disk can never be placed on top of a smaller disk. Let's consider some simple instances of this problem. If n = 1, i.e., only one disk is to be moved, simply move it from a to c. If n = 2, i.e., two disks are to be moved, the following sequence of moves suffices: move from a to b, move from a to c, move from b to c. In this doc topic we will show an ACL2 function that generates a suitable list of moves for a tower of n disks. Then we will use ACL2 to prove that the number of moves is (- (expt 2 n) 1). For an ACL2 script that proves the correctness of (a version of) this function, see the community book "misc/hanoi.lisp" in the books directory of your ACL2 sources. In general, this problem has a straightforward recursive solution. Suppose that we desire to move n disks from a to c, using b as the intermediate peg. For the basis, we saw above that we can always move a single disk from a to c. Now if we have n disks and assume that we can solve the problem for n-1 disks, we can move n disks as follows. First, move n-1 disks from a to b using c as the intermediate peg; move the single disk from a to c; then move n-1 disks from b to c using a as the intermediate peg. In ACL2, we can write a function that will return the sequence of moves. One such function is as follows. Notice that we have two base cases. If (zp n) then n is not a positive integer; we treat that case as if n were 0 and return an empty list of moves. If n is 1, then we return a list containing the single appropriate move. Otherwise, we return the list containing exactly the moves dictated by our recursive analysis above. (defun move (a b) (list 'move a 'to b)) (defun hanoi (a b c n) (if (zp n) nil (if (equal n 1) (list (move a c)) (append (hanoi a c b (1- n)) (cons (move a c) (hanoi b a c (1- n))))))) Notice that we give hanoi four arguments: the three pegs, and the number of disks to move. It is necessary to supply the pegs because, in recursive calls, the roles of the pegs differ. In any execution of the algorithm, a given peg will sometimes be the source of a move, sometimes the destination, and sometimes the intermediate peg. After submitting these functions to ACL2, we can execute the hanoi function on various specific arguments. For example: ACL2 !>(hanoi 'a 'b 'c 1) ((MOVE A TO C)) ACL2 !>(hanoi 'a 'b 'c 2) ((MOVE A TO B) (MOVE A TO C) (MOVE B TO C)) ACL2 !>(hanoi 'a 'b 'c 3) ((MOVE A TO C) (MOVE A TO B) (MOVE C TO B) (MOVE A TO C) (MOVE B TO A) (MOVE B TO C) (MOVE A TO C)) From the algorithm it is clear that if it takes m moves to transfer n disks, it will take (m + 1 + m) = 2m + 1 moves for n+1 disks. From some simple calculations, we see that we need the following number of moves in specific cases: Disks 0 1 2 3 4 5 6 7 ... Moves 0 1 3 7 15 31 63 127 ... The pattern is fairly clear. To move n disks requires (2^n - 1) moves. Let's attempt to use ACL2 to prove that fact. First of all, how do we state our conjecture? Recall that hanoi returns a list of moves. The length of the list (given by the function len) is the number of moves required. Thus, we can state the following conjecture. (defthm hanoi-moves-required-first-try (equal (len (hanoi a b c n)) (1- (expt 2 n)))) When we submit this to ACL2, the proof attempt fails. Along the way we notice subgoals such as: Subgoal *1/1' (IMPLIES (NOT (< 0 N)) (EQUAL 0 (+ -1 (EXPT 2 N)))). This tells us that the prover is considering cases that are uninteresting to us, namely, cases in which n might be negative. The only cases that are really of interest are those in which n is a non-negative natural number. Therefore, we revise our theorem as follows: (defthm hanoi-moves-required (implies (and (integerp n) (<= 0 n)) ;; n is at least 0 (equal (len (hanoi a b c n)) (1- (expt 2 n))))) and submit it to ACL2 again. Again the proof fails. Examining the proof script we encounter the following text. (How did we decide to focus on this goal? Some information is provided in ACLH, and the ACL2 documentation on tips may be helpful. But the simplest answer is: this was the first goal suggested by the "proof-tree" tool below the start of the proof by induction. See *note PROOF-TREE::.) Subgoal *1/5'' (IMPLIES (AND (INTEGERP N) (< 0 N) (NOT (EQUAL N 1)) (EQUAL (LEN (HANOI A C B (+ -1 N))) (+ -1 (EXPT 2 (+ -1 N)))) (EQUAL (LEN (HANOI B A C (+ -1 N))) (+ -1 (EXPT 2 (+ -1 N))))) (EQUAL (LEN (APPEND (HANOI A C B (+ -1 N)) (CONS (LIST 'MOVE A 'TO C) (HANOI B A C (+ -1 N))))) (+ -1 (* 2 (EXPT 2 (+ -1 N)))))) It is difficult to make much sense of such a complicated goal. However, we do notice something interesting. In the conclusion is a term of the following shape. (LEN (APPEND ... ...)) We conjecture that the length of the append of two lists should be the sum of the lengths of the lists. If the prover knew that, it could possibly have simplified this term earlier and made more progress in the proof. Therefore, we need a rewrite rule that will suggest such a simplification to the prover. The appropriate rule is: (defthm len-append (equal (len (append x y)) (+ (len x) (len y)))) We submit this to the prover, which proves it by a straightforward induction. The prover stores this lemma as a rewrite rule and will subsequently (unless we disable the rule) replace terms matching the left hand side of the rule with the appropriate instance of the term on the right hand side. We now resubmit our lemma hanoi-moves-required to ACL2. On this attempt, the proof succeeds and we are done. One bit of cleaning up is useful. We needed the hypotheses that: (and (integerp n) (<= 0 n)). This is an awkward way of saying that n is a natural number; natural is not a primitive data type in ACL2. We could define a function naturalp, but it is somewhat more convenient to define a macro as follows: (defmacro naturalp (x) (list 'and (list 'integerp x) (list '<= 0 x))) Subsequently, we can use (naturalp n) wherever we need to note that a quantity is a natural number. See *note DEFMACRO:: for more information about ACL2 macros. With this macro, we can reformulate our theorem as follows: (defthm hanoi-moves-required (implies (naturalp n) (equal (len (hanoi a b c n)) (1- (expt 2 n))))). Another interesting (but much harder) theorem asserts that the list of moves generated by our hanoi function actually accomplishes the desired goal while following the rules. When you can state and prove that theorem, you'll be a very competent ACL2 user. By the way, the name "Towers of Hanoi" derives from a legend that a group of Vietnamese monks works day and night to move a stack of 64 gold disks from one diamond peg to another, following the rules set out above. We're told that the world will end when they complete this task. From the theorem above, we know that this requires 18,446,744,073,709,551,615 moves: ACL2 !>(1- (expt 2 64)) 18446744073709551615 ACL2 !> We're guessing they won't finish any time soon.  File: acl2-doc-emacs.info, Node: TUTORIAL2-EIGHTS-PROBLEM, Next: TUTORIAL3-PHONEBOOK-EXAMPLE, Prev: TUTORIAL1-TOWERS-OF-HANOI, Up: ANNOTATED-ACL2-SCRIPTS TUTORIAL2-EIGHTS-PROBLEM The Eights Problem Example This example was written almost entirely by Bill Young of Computational Logic, Inc. This simple example was brought to our attention as one that Paul Jackson has solved using the NuPrl system. The challenge is to prove the theorem: for all n > 7, there exist naturals i and j such that: n = 3i + 5j. In ACL2, we could phrase this theorem using quantification. However we will start with a constructive approach, i.e., we will show that values of i and j exist by writing a function that will construct such values for given n. Suppose we had a function (split n) that returns an appropriate pair (i . j). Our theorem would be as follows: (defthm split-splits (let ((i (car (split n))) (j (cdr (split n)))) (implies (and (integerp n) (< 7 n)) (and (integerp i) (<= 0 i) (integerp j) (<= 0 j) (equal (+ (* 3 i) (* 5 j)) n))))) That is, assuming that n is a natural number greater than 7, (split n) returns values i and j that are in the appropriate relation to n. Let's look at a few cases: 8 = 3x1 + 5x1; 11 = 3x2 + 5x1; 14 = 3x3 + 5x1; ... 9 = 3x3 + 5x0; 12 = 3x4 + 5x0; 15 = 3x5 + 5x0; ... 10 = 3x0 + 5x2; 13 = 3x1 + 5x2; 16 = 3x2 + 5x2; ... Maybe you will have observed a pattern here; any natural number larger than 10 can be obtained by adding some multiple of 3 to 8, 9, or 10. This gives us the clue to constructing a proof. It is clear that we can write split as follows: (defun bump-i (x) ;; Bump the i component of the pair ;; (i . j) by 1. (cons (1+ (car x)) (cdr x))) (defun split (n) ;; Find a pair (i . j) such that ;; n = 3i + 5j. (if (or (zp n) (< n 8)) nil ;; any value is really reasonable here (if (equal n 8) (cons 1 1) (if (equal n 9) (cons 3 0) (if (equal n 10) (cons 0 2) (bump-i (split (- n 3)))))))) Notice that we explicitly compute the values of i and j for the cases of 8, 9, and 10, and for the degenerate case when n is not a natural or is less than 8. For all naturals greater than n, we decrement n by 3 and bump the number of 3's (the value of i) by 1. We know that the recursion must terminate because any integer value greater than 10 can eventually be reduced to 8, 9, or 10 by successively subtracting 3. Let's try it on some examples: ACL2 !>(split 28) (6 . 2) ACL2 !>(split 45) (15 . 0) ACL2 !>(split 335) (110 . 1) Finally, we submit our theorem split-splits, and the proof succeeds. In this case, the prover is "smart" enough to induct according to the pattern indicated by the function split. For completeness, we'll note that we can state and prove a quantified version of this theorem. We introduce the notion split-able to mean that appropriate i and j exist for n. (defun-sk split-able (n) (exists (i j) (equal n (+ (* 3 i) (* 5 j))))) Then our theorem is given below. Notice that we prove it by observing that our previous function split delivers just such an i and j (as we proved above). (defthm split-splits2 (implies (and (integerp n) (< 7 n)) (split-able n)) :hints (("Goal" :use (:instance split-able-suff (i (car (split n))) (j (cdr (split n))))))) Unfortunately, understanding the mechanics of the proof requires knowing something about the way defun-sk works. See *note DEFUN-SK:: or see *note TUTORIAL4-DEFUN-SK-EXAMPLE:: for more on that subject.  File: acl2-doc-emacs.info, Node: TUTORIAL3-PHONEBOOK-EXAMPLE, Next: TUTORIAL4-DEFUN-SK-EXAMPLE, Prev: TUTORIAL2-EIGHTS-PROBLEM, Up: ANNOTATED-ACL2-SCRIPTS TUTORIAL3-PHONEBOOK-EXAMPLE A Phonebook Specification The other tutorial examples are rather small and entirely self contained. The present example is rather more elaborate, and makes use of a feature that really adds great power and versatility to ACL2, namely: the use of previously defined collections of lemmas, in the form of "books." This example was written almost entirely by Bill Young of Computational Logic, Inc. This example is based on one developed by Ricky Butler and Sally Johnson of NASA Langley for the PVS system, and subsequently revised by Judy Crow, et al, at SRI. It is a simple phone book specification. We will not bother to follow their versions closely, but will instead present a style of specification natural for ACL2. The idea is to model an electronic phone book with the following properties. Our phone book will store the phone numbers of a city. It must be possible to retrieve a phone number, given a name. It must be possible to add and delete entries. Of course, there are numerous ways to construct such a model. A natural approach within the Lisp/ACL2 context is to use "association lists" or "alists." Briefly, an alist is a list of pairs (key . value) associating a value with a key. A phone book could be an alist of pairs (name . pnum). To find the phone number associated with a given name, we merely search the alist until we find the appropriate pair. For a large city, such a linear list would not be efficient, but at this point we are interested only in modeling the problem, not in deriving an efficient implementation. We could address that question later by proving our alist model equivalent, in some desired sense, to a more efficient data structure. We could build a theory of alists from scratch, or we can use a previously constructed theory (book) of alist definitions and facts. By using an existing book, we build upon the work of others, start our specification and proof effort from a much richer foundation, and hopefully devote more of our time to the problem at hand. Unfortunately, it is not completely simple for the new user to know what books are available and what they contain. We hope later to improve the documentation of the growing collection of community books that are typically downloaded with ACL2; for now, the reader is encouraged to look in the README.html file in the books' top-level directory. For present purposes, the beginning user can simply take our word that a book exists containing useful alist definitions and facts. These definitions and lemmas can be introduced into the current theory using the command: (include-book "data-structures/alist-defthms" :dir :system) This book has been "certified," which means that the definitions and lemmas have been mechanically checked and stored in a safe manner. (See *note BOOKS:: and see *note INCLUDE-BOOK:: for details.) Including this book makes available a collection of functions including the following: (ALISTP A) ; is A an alist (actually a primitive ACL2 function) (BIND X V A) ; associate the key X with value V in alist A (BINDING X A) ; return the value associated with key X in alist A (BOUND? X A) ; is key X associated with any value in alist A (DOMAIN A) ; return the list of keys bound in alist A (RANGE A) ; return the list of values bound to keys in alist A (REMBIND X A) ; remove the binding of key X in alist A Along with these function definitions, the book also provides a number of proved lemmas that aid in simplifying expressions involving these functions. (See *note RULE-CLASSES:: for the way in which lemmas are used in simplification and rewriting.) For example, (defthm bound?-bind (equal (bound? x (bind y v a)) (or (equal x y) (bound? x a)))) asserts that x will be bound in (bind y v a) if and only if: either x = y or x was already bound in a. Also, (defthm binding-bind (equal (binding x (bind y v a)) (if (equal x y) v (binding x a)))) asserts that the resulting binding will be v, if x = y, or the binding that x had in a already, if not. Thus, the inclusion of this book essentially extends our specification and reasoning capabilities by the addition of new operations and facts about these operations that allow us to build further specifications on a richer and possibly more intuitive foundation. However, it must be admitted that the use of a book such as this has two potential limitations: the definitions available in a book may not be ideal for your particular problem; it is (extremely) likely that some useful facts (especially, rewrite rules) are not available in the book and will have to be proved. For example, what is the value of binding when given a key that is not bound in the alist? We can find out by examining the function definition. Look at the definition of the binding function (or any other defined function), using the :pe command: ACL2 !>:pe binding d 33 (INCLUDE-BOOK "/slocal/src/acl2/v1-9/books/public/alist-defthms") >V d (DEFUN BINDING (X A) "The value bound to X in alist A." (DECLARE (XARGS :GUARD (ALISTP A))) (CDR (ASSOC-EQUAL X A))) This tells us that binding was introduced by the given include-book form, is currently disabled in the current theory, and has the definition given by the displayed defun form. We see that binding is actually defined in terms of the primitive assoc-equal function. If we look at the definition of assoc-equal: ACL2 !>:pe assoc-equal V -489 (DEFUN ASSOC-EQUAL (X ALIST) (DECLARE (XARGS :GUARD (ALISTP ALIST))) (COND ((ENDP ALIST) NIL) ((EQUAL X (CAR (CAR ALIST))) (CAR ALIST)) (T (ASSOC-EQUAL X (CDR ALIST))))) we can see that assoc-equal returns nil upon reaching the end of an unsuccessful search down the alist. So binding returns (cdr nil) in that case, which is nil. Notice that we could also have investigated this question by trying some simple examples. ACL2 !>(binding 'a nil) NIL ACL2 !>(binding 'a (list (cons 'b 2))) NIL These definitions aren't ideal for all purposes. For one thing, there's nothing that keeps us from having nil as a value bound to some key in the alist. Thus, if binding returns nil we don't always know if that is the value associated with the key in the alist, or if that key is not bound. We'll have to keep that ambiguity in mind whenever we use binding in our specification. Suppose instead that we wanted binding to return some error string on unbound keys. Well, then we'd just have to write our own version of binding. But then we'd lose much of the value of using a previously defined book. As with any specification technique, certain tradeoffs are necessary. Why not take a look at the definitions of other alist functions and see how they work together to provide the ability to construct and search alists? We'll be using them rather heavily in what follows so it will be good if you understand basically how they work. Simply start up ACL2 and execute the form shown earlier, but substituting our directory name for the top-level ACL2 directory with yours. Alternatively, just (include-book "data-structures/alist-defthms" :dir :system) Then, you can use :pe to look at function definitions. You'll soon discover that almost all of the definitions are built on definitions of other, more primitive functions, as binding is built on assoc-equal. You can look at those as well, of course, or in many cases visit their documentation. The other problem with using a predefined book is that it will seldom be "sufficiently complete," in the sense that the collection of rewrite rules supplied won't be adequate to prove everything we'd like to know about the interactions of the various functions. If it were, there'd be no real reason to know that binding is built on top of assoc-equal, because everything we'd need to know about binding would be nicely expressed in the collection of theorems supplied with the book. However, that's very seldom the case. Developing such a collection of rules is currently more art than science and requires considerable experience. We'll encounter examples later of "missing" facts about binding and our other alist functions. So, let's get on with the example. Notice that alists are mappings of keys to values; but, there is no notion of a "type" associated with the keys or with the values. Our phone book example, however, does have such a notion of types; we map names to phone numbers. We can introduce these "types" by explicitly defining them, e.g., names are strings and phone numbers are integers. Alternatively, we can partially define or axiomatize a recognizer for names without giving a full definition. A way to safely introduce such "constrained" function symbols in ACL2 is with the encapsulate form. For example, consider the following form. (encapsulate ;; Introduce a recognizer for names and give a ``type'' lemma. (((namep *) => *)) ;; (local (defun namep (x) ;; This declare is needed to tell ;; ACL2 that we're aware that the ;; argument x is not used in the body ;; of the function. (declare (ignore x)) t)) ;; (defthm namep-booleanp (booleanp (namep x)))) This encapsulate form introduces the new function namep of one argument and one result and constrains (namep x) to be Boolean, for all inputs x. More generally, an encapsulation establishes an environment in which functions can be defined and theorems and rules added without necessarily introducing those functions, theorems, and rules into the environment outside the encapsulation. To be admissible, all the events in the body of an encapsulate must be admissible. But the effect of an encapsulate is to assume only the non-local events. The first "argument" to encapsulate, ((namep (x) t)) above, declares the intended signatures of new function symbols that will be "exported" from the encapsulation without definition. The local defun of name defines name within the encapsulation always to return t. The defthm event establishes that namep is Boolean. By making the defun local but the defthm non-local this encapsulate constrains the undefined function namep to be Boolean; the admissibility of the encapsulation establishes that there exists a Boolean function (namely the constant function returning t). We can subsequently use namep as we use any other Boolean function, with the proviso that we know nothing about it except that it always returns either t or nil. We use namep to "recognize" legal keys for our phonebook alist. We wish to do something similar to define what it means to be a legal phone number. We submit the following form to ACL2: (encapsulate ;; Introduce a recognizer for phone numbers. (((pnump *) => *)) ;; (local (defun pnump (x) (not (equal x nil)))) ;; (defthm pnump-booleanp (booleanp (pnump x))) ;; (defthm nil-not-pnump (not (pnump nil)))). This introduces a Boolean-valued recognizer pnump, with the additional proviso that the constant nil is not a pnump. We impose this restriction to guarantee that we'll never bind a name to nil in our phone book and thereby introduce the kind of ambiguity described above regarding the use of binding. Now a legal phone book is an alist mapping from nameps to pnumps. We can define this as follows: (defun name-phonenum-pairp (x) ;; Recognizes a pair of (name . pnum). (and (consp x) (namep (car x)) (pnump (cdr x)))) (defun phonebookp (l) ;; Recognizes a list of such pairs. (if (not (consp l)) (null l) (and (name-phonenum-pairp (car l)) (phonebookp (cdr l))))) Thus, a phone book is really a list of pairs (name . pnum). Notice that we have not assumed that the keys of the phone book are distinct. We'll worry about that question later. (It is not always desirable to insist that the keys of an alist be distinct. But it may be a useful requirement for our specific example.) Now we are ready to define some of the functions necessary for our phonebook example. The functions we need are: (IN-BOOK? NM BK) ; does NM have a phone number in BK (FIND-PHONE NM BK) ; find NM's phone number in phonebook BK (ADD-PHONE NM PNUM BK) ; give NM the phone number PNUM in BK (CHANGE-PHONE NM PNUM BK) ; change NM's phone number to PNUM in BK (DEL-PHONE NM PNUM) ; remove NM's phone number from BK Given our underlying theory of alists, it is easy to write these functions. But we must take care to specify appropriate "boundary" behavior. Thus, what behavior do we want when, say, we try to change the phone number of a client who is not currently in the book? As usual, there are numerous possibilities; here we'll assume that we return the phone book unchanged if we try anything "illegal." Possible definitions of our phone book functions are as follows. (Remember, an include-book form such as the ones shown earlier must be executed in order to provide definitions for functions such as bound?.) (defun in-book? (nm bk) (bound? nm bk)) (defun find-phone (nm bk) (binding nm bk)) (defun add-phone (nm pnum bk) ;; If nm already in-book?, make no change. (if (in-book? nm bk) bk (bind nm pnum bk))) (defun change-phone (nm pnum bk) ;; Make a change only if nm already has a phone number. (if (in-book? nm bk) (bind nm pnum bk) bk)) (defun del-phone (nm bk) ;; Remove the binding from bk, if there is one. (rembind nm bk)) Notice that we don't have to check whether a name is in the book before deleting, because rembind is essentially a no-op if nm is not bound in bk. In some sense, this completes our specification. But we can't have any real confidence in its correctness without validating our specification in some way. One way to do so is by proving some properties of our specification. Some candidate properties are: 1. A name will be in the book after we add it. 2. We will find the most recently added phone number for a client. 3. If we change a number, we'll find the change. 4. Changing and then deleting a number is the same as just deleting. 5. A name will not be in the book after we delete it. Let's formulate some of these properties. The first one, for example, is: (defthm add-in-book (in-book? nm (add-phone nm pnum bk))). You may wonder why we didn't need any hypotheses about the "types" of the arguments. In fact, add-in-book is really expressing a property that is true of alists in general, not just of the particular variety of alists we are dealing with. Of course, we could have added some extraneous hypotheses and proved: (defthm add-in-book (implies (and (namep nm) (pnump pnum) (phonebookp bk)) (in-book? nm (add-phone nm pnum bk)))), but that would have yielded a weaker and less useful lemma because it would apply to fewer situations. In general, it is best to state lemmas in the most general form possible and to eliminate unnecessary hypotheses whenever possible. The reason for that is simple: lemmas are usually stored as rules and used in later proofs. For a lemma to be used, its hypotheses must be relieved (proved to hold in that instance); extra hypotheses require extra work. So we avoid them whenever possible. There is another, more important observation to make about our lemma. Even in its simpler form (without the extraneous hypotheses), the lemma add-in-book may be useless as a rewrite rule. Notice that it is stated in terms of the non-recursive functions in-book? and add-phone. If such functions appear in the left hand side of the conclusion of a lemma, the lemma may not ever be used. Suppose in a later proof, the theorem prover encountered a term of the form: (in-book? nm (add-phone nm pnum bk)). Since we've already proved add-in-book, you'd expect that this would be immediately reduced to true. However, the theorem prover will often "expand" the non-recursive definitions of in-book? and add-phone using their definitions before it attempts rewriting with lemmas. After this expansion, lemma add-in-book won't "match" the term and so won't be applied. Look back at the proof script for add-in-proof and you'll notice that at the very end the prover warned you of this potential difficulty when it printed: Warnings: Non-rec Time: 0.18 seconds (prove: 0.05, print: 0.00, other: 0.13) ADD-IN-BOOK The "Warnings" line notifies you that there are non-recursive function calls in the left hand side of the conclusion and that this problem might arise. Of course, it may be that you don't ever plan to use the lemma for rewriting or that your intention is to disable these functions. Disabled functions are not expanded and the lemma should apply. However, you should always take note of such warnings and consider an appropriate response. By the way, we noted above that binding is disabled. If it were not, none of the lemmas about binding in the book we included would likely be of much use for exactly the reason we just gave. For our current example, let's assume that we're just investigating the properties of our specifications and not concerned about using our lemmas for rewriting. So let's go on. If we really want to avoid the warnings, we can add :rule-classes nil to each defthm event; see *note RULE-CLASSES::. Property 2 is: we always find the most recently added phone number for a client. Try the following formalization: (defthm find-add-first-cut (equal (find-phone nm (add-phone nm pnum bk)) pnum)) and you'll find that the proof attempt fails. Examining the proof attempt and our function definitions, we see that the lemma is false if nm is already in the book. We can remedy this situation by reformulating our lemma in at least two different ways: (defthm find-add1 (implies (not (in-book? nm bk)) (equal (find-phone nm (add-phone nm pnum bk)) pnum))) (defthm find-add2 (equal (find-phone nm (add-phone nm pnum bk)) (if (in-book? nm bk) (find-phone nm bk) pnum))) For technical reasons, lemmas such as find-add2, i.e., which do not have hypotheses, are usually slightly preferable. This lemma is stored as an "unconditional" rewrite rule (i.e., has no hypotheses) and, therefore, will apply more often than find-add1. However, for our current purposes either version is all right. Property 3 says: If we change a number, we'll find the change. This is very similar to the previous example. The formalization is as follows. (defthm find-change (equal (find-phone nm (change-phone nm pnum bk)) (if (in-book? nm bk) pnum (find-phone nm bk)))) Property 4 says: changing and then deleting a number is the same as just deleting. We can model this as follows. (defthm del-change (equal (del-phone nm (change-phone nm pnum bk)) (del-phone nm bk))) Unfortunately, when we try to prove this, we encounter subgoals that seem to be true, but for which the prover is stumped. For example, consider the following goal. (Note: endp holds of lists that are empty.) Subgoal *1/4 (IMPLIES (AND (NOT (ENDP BK)) (NOT (EQUAL NM (CAAR BK))) (NOT (BOUND? NM (CDR BK))) (BOUND? NM BK)) (EQUAL (REMBIND NM (BIND NM PNUM BK)) (REMBIND NM BK))). Our intuition about rembind and bind tells us that this goal should be true even without the hypotheses. We attempt to prove the following lemma. (defthm rembind-bind (equal (rembind nm (bind nm pnum bk)) (rembind nm bk))) The prover proves this by induction, and stores it as a rewrite rule. After that, the prover has no difficulty in proving del-change. The need to prove lemma rembind-bind illustrates a point we made early in this example: the collection of rewrite rules supplied by a previously certified book will almost never be everything you'll need. It would be nice if we could operate purely in the realm of names, phone numbers, and phone books without ever having to prove any new facts about alists. Unfortunately, we needed a fact about the relation between rembind and bind that wasn't supplied with the alists theory. Hopefully, such omissions will be rare. Finally, let's consider our property 5 above: a name will not be in the book after we delete it. We formalize this as follows: (defthm in-book-del (not (in-book? nm (del-phone nm bk)))) This proves easily. But notice that it's only true because del-phone (actually rembind) removes all occurrences of a name from the phone book. If it only removed, say, the first one it encountered, we'd need a hypothesis that said that nm occurs at most once in bk. Ah, maybe that's a property you hadn't considered. Maybe you want to ensure that any name occurs at most once in any valid phonebook. To complete this example, let's consider adding an invariant to our specification. In particular, suppose we want to assure that no client has more than one associated phone number. One way to ensure this is to require that the domain of the alist is a "set" (has no duplicates). (defun setp (l) (if (atom l) (null l) (and (not (member-equal (car l) (cdr l))) (setp (cdr l))))) (defun valid-phonebookp (bk) (and (phonebookp bk) (setp (domain bk)))) Now, we want to show under what conditions our operations preserve the property of being a valid-phonebookp. The operations in-book? and find-phone don't return a phone book, so we don't really need to worry about them. Since we're really interested in the "types" of values preserved by our phonebook functions, let's look at the types of those operations as well. (defthm in-book-booleanp (booleanp (in-book? nm bk))) (defthm in-book-namep (implies (and (phonebookp bk) (in-book? nm bk)) (namep nm)) :hints (("Goal" :in-theory (enable bound?)))) (defthm find-phone-pnump (implies (and (phonebookp bk) (in-book? nm bk)) (pnump (find-phone nm bk))) :hints (("Goal" :in-theory (enable bound? binding)))) Note the ":hints" on the last two lemmas. Neither of these would prove without these hints, because once again there are some facts about bound? and binding not available in our current context. Now, we could figure out what those facts are and try to prove them. Alternatively, we can enable bound? and binding and hope that by opening up these functions, the conjectures will reduce to versions that the prover does know enough about or can prove by induction. In this case, this strategy works. The hints tell the prover to enable the functions in question when considering the designated goal. Below we develop the theorems showing that add-phone, change-phone, and del-phone preserve our proposed invariant. Notice that along the way we have to prove some subsidiary facts, some of which are pretty ugly. It would be a good idea for you to try, say, add-phone-preserves-invariant without introducing the following four lemmas first. See if you can develop the proof and only add these lemmas as you need assistance. Then try change-phone-preserves-invariant and del-phone-preserves-invariant. They will be easier. It is illuminating to think about why del-phone-preserves-invariant does not need any "type" hypotheses. (defthm bind-preserves-phonebookp (implies (and (phonebookp bk) (namep nm) (pnump num)) (phonebookp (bind nm num bk)))) (defthm member-equal-strip-cars-bind (implies (and (not (equal x y)) (not (member-equal x (strip-cars a)))) (not (member-equal x (strip-cars (bind y z a)))))) (defthm bind-preserves-domain-setp (implies (and (alistp bk) (setp (domain bk))) (setp (domain (bind nm num bk)))) :hints (("Goal" :in-theory (enable domain)))) (defthm phonebookp-alistp (implies (phonebookp bk) (alistp bk))) (defthm ADD-PHONE-PRESERVES-INVARIANT (implies (and (valid-phonebookp bk) (namep nm) (pnump num)) (valid-phonebookp (add-phone nm num bk))) :hints (("Goal" :in-theory (disable domain-bind)))) (defthm CHANGE-PHONE-PRESERVES-INVARIANT (implies (and (valid-phonebookp bk) (namep nm) (pnump num)) (valid-phonebookp (change-phone nm num bk))) :hints (("Goal" :in-theory (disable domain-bind)))) (defthm remove-equal-preserves-setp (implies (setp l) (setp (remove-equal x l)))) (defthm rembind-preserves-phonebookp (implies (phonebookp bk) (phonebookp (rembind nm bk)))) (defthm DEL-PHONE-PRESERVES-INVARIANT (implies (valid-phonebookp bk) (valid-phonebookp (del-phone nm bk)))) As a final test of your understanding, try to formulate and prove an invariant that says that no phone number is assigned to more than one name. The following hints may help. 1. Define the appropriate invariant. (Hint: remember the function range.) 2. Do our current definitions of add-phone and change-phone necessarily preserve this property? If not, consider what hypotheses are necessary in order to guarantee that they do preserve this property. 3. Study the definition of the function range and notice that it is defined in terms of the function strip-cdrs. Understand how this defines the range of an alist. 4. Formulate the correctness theorems and attempt to prove them. You'll probably benefit from studying the invariant proof above. In particular, you may need some fact about the function strip-cdrs analogous to the lemma member-equal-strip-cars-bind above. Below is one solution to this exercise. Don't look at the solution, however, until you've struggled a bit with it. Notice that we didn't actually change the definitions of add-phone and change-phone, but added a hypothesis saying that the number is "new." We could have changed the definitions to check this and return the phonebook unchanged if the number was already in use. (defun pnums-in-use (bk) (range bk)) (defun phonenums-unique (bk) (setp (pnums-in-use bk))) (defun new-pnump (pnum bk) (not (member-equal pnum (pnums-in-use bk)))) (defthm member-equal-strip-cdrs-rembind (implies (not (member-equal x (strip-cdrs y))) (not (member-equal x (strip-cdrs (rembind z y)))))) (defthm DEL-PHONE-PRESERVES-PHONENUMS-UNIQUE (implies (phonenums-unique bk) (phonenums-unique (del-phone nm bk))) :hints (("Goal" :in-theory (enable range)))) (defthm strip-cdrs-bind-non-member (implies (and (not (bound? x a)) (alistp a)) (equal (strip-cdrs (bind x y a)) (append (strip-cdrs a) (list y)))) :hints (("Goal" :in-theory (enable bound?)))) (defthm setp-append-list (implies (setp l) (equal (setp (append l (list x))) (not (member-equal x l))))) (defthm ADD-PHONE-PRESERVES-PHONENUMS-UNIQUE (implies (and (phonenums-unique bk) (new-pnump pnum bk) (alistp bk)) (phonenums-unique (add-phone nm pnum bk))) :hints (("Goal" :in-theory (enable range)))) (defthm member-equal-strip-cdrs-bind (implies (and (not (member-equal z (strip-cdrs a))) (not (equal z y))) (not (member-equal z (strip-cdrs (bind x y a)))))) (defthm CHANGE-PHONE-PRESERVES-PHONENUMS-UNIQUE (implies (and (phonenums-unique bk) (new-pnump pnum bk) (alistp bk)) (phonenums-unique (change-phone nm pnum bk))) :hints (("Goal" :in-theory (enable range))))  File: acl2-doc-emacs.info, Node: TUTORIAL4-DEFUN-SK-EXAMPLE, Next: TUTORIAL5-MISCELLANEOUS-EXAMPLES, Prev: TUTORIAL3-PHONEBOOK-EXAMPLE, Up: ANNOTATED-ACL2-SCRIPTS TUTORIAL4-DEFUN-SK-EXAMPLE example of quantified notions This example illustrates the use of defun-sk and defthm events to reason about quantifiers. See *note DEFUN-SK::. For a more through, systematic beginner's introduction to quantification in ACL2, see *note QUANTIFIER-TUTORIAL::. Many users prefer to avoid the use of quantifiers, since ACL2 provides only very limited support for reasoning about quantifiers. Here is a list of events that proves that if there are arbitrarily large numbers satisfying the disjunction (OR P R), then either there are arbitrarily large numbers satisfying P or there are arbitrarily large numbers satisfying R. ; Introduce undefined predicates p and r. (defstub p (x) t) (defstub r (x) t) ; Define the notion that something bigger than x satisfies p. (defun-sk some-bigger-p (x) (exists y (and (< x y) (p y)))) ; Define the notion that something bigger than x satisfies r. (defun-sk some-bigger-r (x) (exists y (and (< x y) (r y)))) ; Define the notion that arbitrarily large x satisfy p. (defun-sk arb-lg-p () (forall x (some-bigger-p x))) ; Define the notion that arbitrarily large x satisfy r. (defun-sk arb-lg-r () (forall x (some-bigger-r x))) ; Define the notion that something bigger than x satisfies p or r. (defun-sk some-bigger-p-or-r (x) (exists y (and (< x y) (or (p y) (r y))))) ; Define the notion that arbitrarily large x satisfy p or r. (defun-sk arb-lg-p-or-r () (forall x (some-bigger-p-or-r x))) ; Prove the theorem promised above. Notice that the functions open ; automatically, but that we have to provide help for some rewrite ; rules because they have free variables in the hypotheses. The ; ``witness functions'' mentioned below were introduced by DEFUN-SK. (thm (implies (arb-lg-p-or-r) (or (arb-lg-p) (arb-lg-r))) :hints (("Goal" :use ((:instance some-bigger-p-suff (x (arb-lg-p-witness)) (y (some-bigger-p-or-r-witness (max (arb-lg-p-witness) (arb-lg-r-witness))))) (:instance some-bigger-r-suff (x (arb-lg-r-witness)) (y (some-bigger-p-or-r-witness (max (arb-lg-p-witness) (arb-lg-r-witness))))) (:instance arb-lg-p-or-r-necc (x (max (arb-lg-p-witness) (arb-lg-r-witness)))))))) ; And finally, here's a cute little example. We have already ; defined above the notion (some-bigger-p x), which says that ; something bigger than x satisfies p. Let us introduce a notion ; that asserts that there exists both y and z bigger than x which ; satisfy p. On first glance this new notion may appear to be ; stronger than the old one, but careful inspection shows that y and ; z do not have to be distinct. In fact ACL2 realizes this, and ; proves the theorem below automatically. (defun-sk two-bigger-p (x) (exists (y z) (and (< x y) (p y) (< x z) (p z)))) (thm (implies (some-bigger-p x) (two-bigger-p x))) ; A technical point: ACL2 fails to prove the theorem above ; automatically if we take its contrapositive, unless we disable ; two-bigger-p as shown below. That is because ACL2 needs to expand ; some-bigger-p before applying the rewrite rule introduced for ; two-bigger-p, which contains free variables. The moral of the ; story is: Don't expect too much automatic support from ACL2 for ; reasoning about quantified notions. (thm (implies (not (two-bigger-p x)) (not (some-bigger-p x))) :hints (("Goal" :in-theory (disable two-bigger-p))))  File: acl2-doc-emacs.info, Node: TUTORIAL5-MISCELLANEOUS-EXAMPLES, Prev: TUTORIAL4-DEFUN-SK-EXAMPLE, Up: ANNOTATED-ACL2-SCRIPTS TUTORIAL5-MISCELLANEOUS-EXAMPLES miscellaneous ACL2 examples The following examples are more advanced examples of usage of ACL2. They are included largely for reference, in case someone finds them useful. * Menu: * FILE-READING-EXAMPLE:: example of reading files in ACL2 * FUNCTIONAL-INSTANTIATION-EXAMPLE:: a small proof demonstrating functional instantiation * GUARD-EXAMPLE:: a brief transcript illustrating guards in ACL2 * MUTUAL-RECURSION-PROOF-EXAMPLE:: a small proof about mutually recursive functions  File: acl2-doc-emacs.info, Node: FILE-READING-EXAMPLE, Next: FUNCTIONAL-INSTANTIATION-EXAMPLE, Prev: TUTORIAL5-MISCELLANEOUS-EXAMPLES, Up: TUTORIAL5-MISCELLANEOUS-EXAMPLES FILE-READING-EXAMPLE example of reading files in ACL2 This example illustrates the use of ACL2's IO primitives to read the forms in a file. See *note IO::. This example provides a solution to the following problem. Let's say that you have a file that contains s-expressions. Suppose that you want to build a list by starting with nil, and updating it "appropriately" upon encountering each successive s-expression in the file. That is, suppose that you have written a function update-list such that (update-list obj current-list) returns the list obtained by "updating" current-list with the next object, obj, encountered in the file. The top-level function for processing such a file, returning the final list, could be defined as follows. Notice that because it opens a channel to the given file, this function modifies state and hence must return state. Thus it actually returns two values: the final list and the new state. (defun process-file (filename state) (mv-let (channel state) (open-input-channel filename :object state) (mv-let (result state) (process-file1 nil channel state) ;see below (let ((state (close-input-channel channel state))) (mv result state))))) The function process-file1 referred to above takes the currently constructed list (initially, nil), together with a channel to the file being read and the state, and returns the final updated list. Notice that this function is tail recursive. This is important because many Lisp compilers will remove tail recursion, thus avoiding the potential for stack overflows when the file contains a large number of forms. (defun process-file1 (current-list channel state) (mv-let (eofp obj state) (read-object channel state) (cond (eofp (mv current-list state)) (t (process-file1 (update-list obj current-list) channel state))))) As an exercise, you might want to add guards to the functions above and verify the guards (see *note VERIFY-GUARDS::). See *note ARGS:: or make a call of the form (guard 'your-function nil (w state)) to see the guard of an existing function.  File: acl2-doc-emacs.info, Node: FUNCTIONAL-INSTANTIATION-EXAMPLE, Next: GUARD-EXAMPLE, Prev: FILE-READING-EXAMPLE, Up: TUTORIAL5-MISCELLANEOUS-EXAMPLES FUNCTIONAL-INSTANTIATION-EXAMPLE a small proof demonstrating functional instantiation The example below demonstrates the use of functional instantiation, that is, the use of a generic result in proving a result about specific functions. In this example we constrain a function to be associative and commutative, with an identity or "root," on a given domain. Next, we define a corresponding function that applies the constrained associative-commutative function to successive elements of a list. We then prove that the latter function gives the same value when we first reverse the elements of the list. Finally, we use functional instantiation to derive the corresponding result for the function that multiplies successive elements of a list. The details of the proof (such as the in-theory event and particulars of the lemmas) are not the point here. Rather, the point is to demonstrate the interaction of encapsulate events and :functional-instance lemma-instances. Of course, if you are interested in details then you may find it helpful to run these events through ACL2. Also see *note CONSTRAINT:: for more about :functional-instance and see *note LEMMA-INSTANCE:: for general information about the use of previously-proved lemmas. (in-package "ACL2") (encapsulate (((ac-fn * *) => *) ((ac-fn-domain *) => *) ((ac-fn-root) => *)) (local (defun ac-fn (x y) (+ x y))) (local (defun ac-fn-root () 0)) (local (defun ac-fn-domain (x) (acl2-numberp x))) (defthm ac-fn-comm (equal (ac-fn x y) (ac-fn y x))) (defthm ac-fn-assoc (equal (ac-fn (ac-fn x y) z) (ac-fn x (ac-fn y z)))) (defthm ac-fn-id (implies (ac-fn-domain x) (equal (ac-fn (ac-fn-root) x) x))) (defthm ac-fn-closed (and (ac-fn-domain (ac-fn x y)) (ac-fn-domain (ac-fn-root))))) ;;;;;;;;;;;;;;;;;;;;;;; ; End of encapsulate. ; ;;;;;;;;;;;;;;;;;;;;;;; ; Define a ``fold'' function that iteratively applies ac-fn over a list. (defun ac-fn-list (x) (if (atom x) (ac-fn-root) (ac-fn (car x) (ac-fn-list (cdr x))))) ; Recognize lists all of whose elements are in the intended domain. (defun ac-fn-domain-list (x) (if (atom x) t (and (ac-fn-domain (car x)) (ac-fn-domain-list (cdr x))))) ; Define a list reverse function. (defun rev (x) (if (atom x) nil (append (rev (cdr x)) (list (car x))))) ; The following is needed for proving ac-fn-list-append, which is ; needed for proving ac-fn-list-rev. (defthm ac-fn-list-closed (ac-fn-domain (ac-fn-list x))) ; Needed for proving ac-fn-list-rev: (defthm ac-fn-list-append (implies (and (ac-fn-domain-list x) (ac-fn-domain-list y)) (equal (ac-fn-list (append x y)) (ac-fn (ac-fn-list x) (ac-fn-list y))))) ; Needed for proving ac-fn-list-rev: (defthm ac-fn-domain-list-rev (equal (ac-fn-domain-list (rev x)) (ac-fn-domain-list x))) ; The following is a good idea because without it, the proof attempt ; for ac-fn-list-rev (see just below) fails with the term (HIDE ; (AC-FN-LIST NIL)). It is often a good idea to disable ; executable-counterparts of functions that call constrained ; functions. (in-theory (disable (:executable-counterpart ac-fn-list))) (defthm ac-fn-list-rev (implies (ac-fn-domain-list x) (equal (ac-fn-list (rev x)) (ac-fn-list x)))) ; Our goal now is to apply functional instantiation to ac-fn-list-rev ; in order to prove the corresponding theorem, times-list-rev, based ; on * instead of ac-fn. (defun times-list (x) (if (atom x) 1 (* (car x) (times-list (cdr x))))) (defun acl2-number-listp (x) (if (atom x) t (and (acl2-numberp (car x)) (acl2-number-listp (cdr x))))) ; The following relies on the following built-in rules for * (whose ; statements correspond directly to their names): commutativity-of-*, ; associativity-of-*, and unicity-of-1. (defthm times-list-rev (implies (acl2-number-listp x) (equal (times-list (rev x)) (times-list x))) :hints (("Goal" :use ((:functional-instance ac-fn-list-rev ;; Instantiate the generic functions: (ac-fn *) (ac-fn-root (lambda () 1)) (ac-fn-domain acl2-numberp) ;; Instantiate the other relevant functions: (ac-fn-list times-list) (ac-fn-domain-list acl2-number-listp))))))  File: acl2-doc-emacs.info, Node: GUARD-EXAMPLE, Next: MUTUAL-RECURSION-PROOF-EXAMPLE, Prev: FUNCTIONAL-INSTANTIATION-EXAMPLE, Up: TUTORIAL5-MISCELLANEOUS-EXAMPLES GUARD-EXAMPLE a brief transcript illustrating guards in ACL2 This note addresses the question: what is the use of guards in ACL2? Although we recommend that beginners try to avoid guards for a while, we hope that the summary here is reasonably self-contained and will provide a reasonable introduction to guards in ACL2. For a more systematic discussion, see *note GUARD::. For a summary of that topic, see *note GUARD-QUICK-REFERENCE::. Before we get into the issue of guards, let us note that there are two important "modes": defun-mode -- "Does this defun add an axiom (`:logic mode') or not (`:program mode')?" (See *note DEFUN-MODE::.) Only :logic mode functions can have their "guards verified" via mechanized proof; see *note VERIFY-GUARDS::. set-guard-checking -- "Should runtime guard violations signal an error (:all, and usually with t or :nowarn) or go undetected (nil, :none)? Equivalently, are expressions evaluated in Common Lisp or in the logic?" (See *note SET-GUARD-CHECKING::.) _Prompt examples_ Here some examples of the relation between the ACL2 prompt and the "modes" discussed above. Also see *note DEFAULT-PRINT-PROMPT::. The first examples all have ld-skip-proofsp nil; that is, proofs are _not_ skipped. ACL2 !> ; logic mode with guard checking on ACL2 > ; logic mode with guard checking off ACL2 p!> ; program mode with guard checking on ACL2 p> ; program mode with guard checking off Here are some examples with default-defun-mode of :logic. ACL2 > ; guard checking off, ld-skip-proofsp nil ACL2 s> ; guard checking off, ld-skip-proofsp t ACL2 !> ; guard checking on, ld-skip-proofsp nil ACL2 !s> ; guard checking on, ld-skip-proofsp t _Sample session_ ACL2 !>(+ 'abc 3) ACL2 Error in TOP-LEVEL: The guard for the function symbol BINARY-+, which is (AND (ACL2-NUMBERP X) (ACL2-NUMBERP Y)), is violated by the arguments in the call (+ 'ABC 3). ACL2 !>:set-guard-checking nil ;;;; verbose output omitted here ACL2 >(+ 'abc 3) 3 ACL2 >(< 'abc 3) T ACL2 >(< 3 'abc) NIL ACL2 >(< -3 'abc) T ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(defun sum-list (x) (declare (xargs :guard (integer-listp x) :verify-guards nil)) (cond ((endp x) 0) (t (+ (car x) (sum-list (cdr x)))))) The admission of SUM-LIST is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We observe that the type of SUM-LIST is described by the theorem (ACL2-NUMBERP (SUM-LIST X)). We used primitive type reasoning. Summary Form: ( DEFUN SUM-LIST ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, proof tree: 0.00, other: 0.03) SUM-LIST ACL2 !>(sum-list '(1 2 3)) ACL2 Warning [Guards] in TOP-LEVEL: Guard-checking will be inhibited on recursive calls of the executable counterpart (i.e., in the ACL2 logic) of SUM-LIST. To check guards on all recursive calls: (set-guard-checking :all) To leave behavior unchanged except for inhibiting this message: (set-guard-checking :nowarn) 6 ACL2 !>(sum-list '(1 2 abc 3)) ACL2 Error in TOP-LEVEL: The guard for the function symbol BINARY-+, which is (AND (ACL2-NUMBERP X) (ACL2-NUMBERP Y)), is violated by the arguments in the call (+ 'ABC 3). ACL2 !>:set-guard-checking nil ;;;; verbose output omitted here ACL2 >(sum-list '(1 2 abc 3)) 6 ACL2 >(defthm sum-list-append (equal (sum-list (append a b)) (+ (sum-list a) (sum-list b)))) << Starting proof tree logging >> Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. Subsumption reduces that number to two. However, one of these is flawed and so we are left with one viable candidate. ... That completes the proof of *1. Q.E.D. _Guard verification vs. defun_ Declare Form Guards Verified? (declare (xargs :mode :program ...)) no (declare (xargs :guard g)) yes (declare (xargs :guard g :verify-guards nil)) no (declare (xargs ......)) no ACL2 >:pe sum-list l 8 (DEFUN SUM-LIST (X) (DECLARE (XARGS :GUARD (INTEGER-LISTP X) :VERIFY-GUARDS NIL)) (COND ((ENDP X) 0) (T (+ (CAR X) (SUM-LIST (CDR X)))))) ACL2 >(verify-guards sum-list) The non-trivial part of the guard conjecture for SUM-LIST, given the :type-prescription rule SUM-LIST, is Goal (AND (IMPLIES (AND (INTEGER-LISTP X) (NOT (CONSP X))) (EQUAL X NIL)) (IMPLIES (AND (INTEGER-LISTP X) (NOT (ENDP X))) (INTEGER-LISTP (CDR X))) (IMPLIES (AND (INTEGER-LISTP X) (NOT (ENDP X))) (ACL2-NUMBERP (CAR X)))). ... ACL2 >:pe sum-list lv 8 (DEFUN SUM-LIST (X) (DECLARE (XARGS :GUARD (INTEGER-LISTP X) :VERIFY-GUARDS NIL)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(sum-list '(1 2 abc 3)) ACL2 Error in TOP-LEVEL: The guard for the function symbol SUM-LIST, which is (INTEGER-LISTP X), is violated by the arguments in the call (SUM-LIST '(1 2 ABC ...)). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>:set-guard-checking nil ;;;; verbose output omitted here ACL2 >(sum-list '(1 2 abc 3)) 6 ACL2 >:comp sum-list Compiling gazonk0.lsp. End of Pass 1. End of Pass 2. Finished compiling gazonk0.lsp. Loading gazonk0.o start address -T 1bbf0b4 Finished loading gazonk0.o Compiling gazonk0.lsp. End of Pass 1. End of Pass 2. Finished compiling gazonk0.lsp. Loading gazonk0.o start address -T 1bc4408 Finished loading gazonk0.o SUM-LIST ACL2 >:q Exiting the ACL2 read-eval-print loop. ACL2>(trace sum-list) (SUM-LIST) ACL2>(lp) ACL2 Version 1.8. Level 1. Cbd "/slocal/src/acl2/v1-9/". Type :help for help. ACL2 >(sum-list '(1 2 abc 3)) 6 ACL2 >(sum-list '(1 2 3)) 1> (SUM-LIST (1 2 3))> 2> (SUM-LIST (2 3))> 3> (SUM-LIST (3))> 4> (SUM-LIST NIL)> <4 (SUM-LIST 0)> <3 (SUM-LIST 3)> <2 (SUM-LIST 5)> <1 (SUM-LIST 6)> 6 ACL2 >:pe sum-list-append 9 (DEFTHM SUM-LIST-APPEND (EQUAL (SUM-LIST (APPEND A B)) (+ (SUM-LIST A) (SUM-LIST B)))) ACL2 >(verify-guards sum-list-append) The non-trivial part of the guard conjecture for SUM-LIST-APPEND, given the :type-prescription rule SUM-LIST, is Goal (AND (TRUE-LISTP A) (INTEGER-LISTP (APPEND A B)) (INTEGER-LISTP A) (INTEGER-LISTP B)). ... ****** FAILED ******* See :DOC failure ****** FAILED ****** ACL2 >(defthm common-lisp-sum-list-append (if (and (integer-listp a) (integer-listp b)) (equal (sum-list (append a b)) (+ (sum-list a) (sum-list b))) t) :rule-classes nil) << Starting proof tree logging >> By the simple :rewrite rule SUM-LIST-APPEND we reduce the conjecture to Goal' (IMPLIES (AND (INTEGER-LISTP A) (INTEGER-LISTP B)) (EQUAL (+ (SUM-LIST A) (SUM-LIST B)) (+ (SUM-LIST A) (SUM-LIST B)))). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. ;;;; summary omitted here ACL2 >(verify-guards common-lisp-sum-list-append) The non-trivial part of the guard conjecture for COMMON-LISP-SUM-LIST-APPEND, given the :type-prescription rule SUM-LIST, is Goal (AND (IMPLIES (AND (INTEGER-LISTP A) (INTEGER-LISTP B)) (TRUE-LISTP A)) (IMPLIES (AND (INTEGER-LISTP A) (INTEGER-LISTP B)) (INTEGER-LISTP (APPEND A B)))). ... Q.E.D. That completes the proof of the guard theorem for COMMON-LISP-SUM-LIST-APPEND. COMMON-LISP-SUM-LIST-APPEND is compliant with Common Lisp. ;;;; Summary omitted here. ACL2 >(defthm foo (consp (mv x y))) ... Q.E.D. ACL2 >(verify-guards foo) ACL2 Error in (VERIFY-GUARDS FOO): The number of values we need to return is 1 but the number of values returned by the call (MV X Y) is 2. > (CONSP (MV X Y)) ACL2 Error in (VERIFY-GUARDS FOO): The guards for FOO cannot be verified because the theorem has the wrong syntactic form. See :DOC verify-guards.  File: acl2-doc-emacs.info, Node: MUTUAL-RECURSION-PROOF-EXAMPLE, Prev: GUARD-EXAMPLE, Up: TUTORIAL5-MISCELLANEOUS-EXAMPLES MUTUAL-RECURSION-PROOF-EXAMPLE a small proof about mutually recursive functions Sometimes one wants to reason about mutually recursive functions. Although this is possible in ACL2, it can be a bit awkward. This example is intended to give some ideas about how one can go about such proofs. For an introduction to mutual recursion in ACL2, see *note MUTUAL-RECURSION::. We begin by defining two mutually recursive functions: one that collects the variables from a term, the other that collects the variables from a list of terms. We actually imagine the term argument to be a pseudo-termp; see *note PSEUDO-TERMP::. (mutual-recursion (defun free-vars1 (term ans) (cond ((atom term) (add-to-set-eq term ans)) ((fquotep term) ans) (t (free-vars1-lst (cdr term) ans)))) (defun free-vars1-lst (lst ans) (cond ((atom lst) ans) (t (free-vars1-lst (cdr lst) (free-vars1 (car lst) ans))))) ) Now suppose that we want to prove the following theorem. (defthm symbol-listp-free-vars1-try-1 (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans)))) Often ACL2 can generate a proof by induction based on the structure of definitions of function symbols occurring in the conjecture. In this case, ACL2 chooses to use an induction scheme suggested by (symbol-listp ans), and sadly, that doesn't work. If one were doing this proof with pencil and paper, one would be more likely to prove a combination of the conjecture above and an analogous conjecture about free-vars1-lst. Feel free to try a pencil and paper proof! Or you can read on, to see how one can get ACL2 to do such a proof after all. The trick is to define a function that suggests an appropriate induction. The induction suggested is based on the if-then-else structure of the function's definition, where inductive hypotheses are generated for recursive calls -- below we explain how that works for this function. (defun symbol-listp-free-vars1-induction (x ans) (if (atom x) ; then we just make sure x and ans aren't considered irrelevant: (list x ans) (list (symbol-listp-free-vars1-induction (car x) ans) (symbol-listp-free-vars1-induction (cdr x) ans) (symbol-listp-free-vars1-induction (cdr x) (free-vars1 (car x) ans))))) The if-then-else structure of this function generates two cases. In one case, (atom x) is true, and the theorem to be proved should be proved under no additional hypotheses except for (atom x); in other words, (atom x) gives us the base case of the induction. In the other case, (not (atom x)) is assumed together with three instances of the theorem to be proved, one for each recursive call. So, one instance substitutes (car x) for x; one substitutes (cdr x) for x; and the third substitutes (cdr x) for x and (free-vars1 (car x) ans) for ans. If you think about how you would go about a hand proof of the theorem to follow, you'll likely come up with a similar scheme. We now prove the two theorems together as a conjunction, because the inductive hypotheses for one are sometimes needed in the proof of the other (even when you do this proof on paper!). (defthm symbol-listp-free-vars1 (and (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans))) (implies (and (pseudo-term-listp x) (symbol-listp ans)) (symbol-listp (free-vars1-lst x ans)))) :hints (("Goal" :induct (symbol-listp-free-vars1-induction x ans)))) The above works, but we conclude by illustrating a more efficient approach, in which we restrict to appropriate inductive hypotheses for each case. (ubt 'symbol-listp-free-vars1-induction) (defun symbol-listp-free-vars1-induction (flg x ans) ; Flg is nil if we are ``thinking'' of a single term. (if (atom x) ; whether x is a single term or a list of terms (list x ans) (if flg ; i.e., if x is a list of terms (list (symbol-listp-free-vars1-induction nil (car x) ans) (symbol-listp-free-vars1-induction t (cdr x) (free-vars1 (car x) ans))) (symbol-listp-free-vars1-induction t (cdr x) ans)))) We now state the theorem as a conditional, so that it can be proved nicely using the induction scheme that we have just coded. The prover will not store an IF term as a rewrite rule, but that's OK (provided we tell it not to try), because we're going to derive the corollaries of interest later and make them into rewrite rules. (defthm symbol-listp-free-vars1-flg (if flg (implies (and (pseudo-term-listp x) (symbol-listp ans)) (symbol-listp (free-vars1-lst x ans))) (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans)))) :hints (("Goal" :induct (symbol-listp-free-vars1-induction flg x ans))) :rule-classes nil) And finally, we may derive the theorems we are interested in as immediate corollaries. (defthm symbol-listp-free-vars1 (implies (and (pseudo-termp x) (symbol-listp ans)) (symbol-listp (free-vars1 x ans))) :hints (("Goal" :by (:instance symbol-listp-free-vars1-flg (flg nil))))) (defthm symbol-listp-free-vars1-lst (implies (and (pseudo-term-listp x) (symbol-listp ans)) (symbol-listp (free-vars1-lst x ans))) :hints (("Goal" :by (:instance symbol-listp-free-vars1-flg (flg t))))) You may find community books (see *note COMMUNITY-BOOKS:: that help you to automate this kind of reasoning about mutually recursive functions. See for example the community book tools/flag.lisp.  File: acl2-doc-emacs.info, Node: EMACS, Next: INTERESTING-APPLICATIONS, Prev: ANNOTATED-ACL2-SCRIPTS, Up: ACL2-TUTORIAL EMACS emacs support for ACL2 Many successful ACL2 users run in an shell under the Emacs editor. If you do so, then you may wish to load the distributed file emacs/emacs-acl2.el. The file begins with considerable comments describing what it offers. It is intended to work both with GNU Emacs and XEmacs. If you are not comfortable with Emacs, you may prefer to use an Eclipse-based interface; see *note ACL2-SEDAN::.  File: acl2-doc-emacs.info, Node: INTERESTING-APPLICATIONS, Next: INTRODUCTION-TO-THE-THEOREM-PROVER, Prev: EMACS, Up: ACL2-TUTORIAL INTERESTING-APPLICATIONS some industrial examples of ACL2 use ACL2 is an interactive system in which you can model digital artifacts and guide the system to mathematical proofs about the behavior of those models. It has been used at such places as AMD, Centaur, IBM, and Rockwell Collins to verify interesting properties of commercial designs. It has been used to verify properties of models of microprocessors, microcode, the Sun Java Virtual Machine, operating system kernels, other verifiers, and interesting algorithms. Here we list just a few of the industrially-relevant results obtained with ACL2. Reading the list may help you decide you want to learn how to use ACL2. If you do decide you want to learn more, we recommend that you take The Tours after you leave this page. ACL2 was used at Motorola Government Systems to certify several microcode programs for the Motorola CAP digital signal processor, including a comparator sort program that is particularly subtle. In the same project, ACL2 was used to model the CAP at both the pipelined architectural level and the instruction set level. The architectural model was bit- and cycle-accurate: it could be used to predict every bit of memory on every cycle. The models were proved equivalent under certain hypotheses, the most important being a predicate that analyzed the microcode for certain pipeline hazards. This predicate defined what the hazards were, syntactically, and the equivalence of the two models established the correctness of this syntactic characterization of hazards. Because ACL2 is a functional programming language, the ACL2 models and the hazard predicate could be executed. ACL2 executed microcode interpretr several times faster than the hardware simulator could execute it - with assurance that the answers were equivalent. In addition, the ACL2 hazard predicate was executed on over fifty microcode programs written by Motorola engineers and extracted from the ROM mechanically. Hazards were found in some of these. (See, for example, Bishop Brock and Warren. A. Hunt, Jr. "Formal analysis of the motorola CAP DSP." In Industrial-Strength Formal Methods. Springer-Verlag, 1999.) ACL2 was used at Advanced Micro Devices (AMD) to verify the compliance of the AMD Athon's (TM) elementary floating point operations with their IEEE 754 specifications. This followed ground-breaking work in 1995 when ACL2 was used to prove the correctness of the microcode for floating-point division on the AMD K5. The AMD Athlon work proved addition, subtraction, multiplication, division, and square root compliant with the IEEE standard. Bugs were found in RTL designs. These bugs had survived undetected in hundreds of millions of tests but were uncovered by ACL2 proof attempts. The RTL in the fabricated Athlon FPU has been mechanically verified by ACL2. Similar ACL2 proofs have been carried out for every major AMD FPU design fabricated since the Athlon. (See for example, David Russinoff. "A mechanically checked proof of correctness of the AMD5K86 floating-point square root microcode". Formal Methods in System Design Special Issue on Arithmetic Circuits, 1997.) ACL2 was used at IBM to verify the floating point divide and square root on the IBM Power 4. (See Jun Sawada. "Formal verification of divide and square root algorithms using series calculation". In Proceedings of the ACL2 Workshop 2002, Grenoble, April 2002.) ACL2 was used to verify floating-point addition/subtraction instructions for the media unit from Centaur Technology's 64-bit, X86-compatible microprocessor. This unit implements over one hundred instructions, with the most complex being floating-point addition/subtraction. The media unit can add/subtract four pairs of floating-point numbers every clock cycle with an industry-leading two-cycle latency. The media unit was modeled by translating its Verilog design into an HDL deeply embedded in the ACL2 logic. The proofs used a combination of AIG- and BDD-based symbolic simulation, case splitting, and theorem proving. (See Warren A. Hunt, Jr. and Sol Swords. "Centaur Technology Media Unit Verification". In CAV '09: Proceedings of the 21st International Conference on Computer Aided Verification, pages 353-367, Berlin, Heidelberg, 2009. Springer-Verlag.) Rockwell Collins used ACL2 to prove information flow properties about its Advanced Architecture MicroProcessor 7 Government Version (AAMP7G), a Multiple Independent Levels of Security (MILS) device for use in cryptographic applications. The AAMP7G provides MILS capability via a verified secure hardware-based separation kernel. The AAMP7G's design was proved to achieve MILS using ACL2, in accordance with the standards set by EAL-7 of the Common Criteria and Rockwell Collins has received National Security Agency (NSA) certification for the device based on this work. (See David S. Hardin, Eric W. Smith, and William. D. Young. "A robust machine code proof framework for highly secure applications". In Proceedings of the sixth international workshop on the ACL2 theorem prover and its applications, pages 11-20, New York, NY, USA, 2006. ACM.) Key properties of the Sun Java Virtual Machine and its bytecode verifier were verified in ACL2. Among the properties proved were that certain invariants are maintained by class loading and that the bytecode verifier insures that execution is safe. In addition, various JVM bytecode programs have been verified using this model of the JVM. (See Hanbing Liu. Formal Specification and Verification of a JVM and its Bytecode Verifier. PhD thesis, University of Texas at Austin, 2006.) The Boyer-Moore fast string searching algorithm was verified with ACL2, including a model of the JVM bytecode for the search algorithm itself (but not the preprocessing). (See J S. Moore and Matt Martinez. "A mechanically checked proof of the correctness of the Boyer-Moore fast string searching algorithm." In Engineering Methods and Tools for Software Safety and Security pages 267-284. IOS Press, 2009.) ACL2 was used to verify the fidelity between an ACL2-like theorem prover and a simple ("trusted by inspection") proof checker, thereby establishing (up to the soundness of ACL2) the soundness of the ACL2-like theorem prover. This project was only part of a much larger project in which the resulting ACL2 proof script was then hand-massaged into a script suitable for the ACL2-like theorem prover to process, generating a formal proof of its fidelity that has been checked by the trusted proof checker. (See Jared Davis. Milawa: A Self-Verifying Theorem Prover. Ph.D. Thesis, University of Texas at Austin, December, 2009.) These are but a few of the interesting projects carried out with ACL2. Many of the authors mentioned above have versions of the papers on their web pages. In addition, see the link to "Books and Papers about ACL2 and Its Applications" on the ACL2 home page (`http://www.cs.utexas.edu/users/moore/acl2'). Also see the presentations in each of the workshops listed in the link to "ACL2 Workshops" on the ACL2 home page.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-THE-THEOREM-PROVER, Next: STARTUP, Prev: INTERESTING-APPLICATIONS, Up: ACL2-TUTORIAL INTRODUCTION-TO-THE-THEOREM-PROVER how the theorem prover works - level 0 Software is complex, and ACL2 is a piece of software that is used to analyze software - adding another layer of complexity. Furthermore, instead of being limited to static analysis for certain fixed properties, ACL2 allows you - indeed, forces you - to formalize the problem and the questions. It "knows" nothing inherent about your problem before you start to interact with it. But it can be used to help answer the most complicated questions you can ask about software. All this is to say that it is not the kind of tool that you just install and then start to use effectively. So OK, you've installed it or confirmed that you can invoke it. Good for you. Now you have to learn how to use it! Your success ultimately comes down to your understanding of your problem domain and your appropriate exploitation of ACL2's strengths and avoidance of its weaknesses. So put aside the idea of sitting down and interacting with it. Instead, learn about it. We assume you know some of the industrial applications of ACL2. Realizing that such things can be done may sustain you during the long learning curve! We also assume you have taken both the Flying Tour and the Walking Tour. The tours give you a good overview of the whole system where this tutorial focuses on how to use the prover itself. If you haven't visited these links, please do so now. This tutorial will take you several hours - maybe several days - to work through. Do not breeze through it as you might a blog. Think your way through it. Remember what you read. Do not take short cuts. If you start to use ACL2 before you really know how, it will only frustrate you. We recommend that you read this tutorial with an HTML browser so that you can see which links you've followed and which you have not. To give you a sense of what is in store, here is a map of this document. But don't navigate through it from here! Read it linearly, following the links when the text directs you to. INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-REWRITE-RULES-PART-1 SPECIAL-CASES-FOR-REWRITE-RULES EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES INTRODUCTION-TO-KEY-CHECKPOINTS DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS GENERALIZING-KEY-CHECKPOINTS POST-INDUCTION-KEY-CHECKPOINTS INTRODUCTION-TO-REWRITE-RULES-PART-2 STRONG-REWRITE-RULES PRACTICE-FORMULATING-STRONG-RULES PRACTICE-FORMULATING-STRONG-RULES-1 PRACTICE-FORMULATING-STRONG-RULES-2 PRACTICE-FORMULATING-STRONG-RULES-3 PRACTICE-FORMULATING-STRONG-RULES-4 PRACTICE-FORMULATING-STRONG-RULES-5 PRACTICE-FORMULATING-STRONG-RULES-6 SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES FURTHER-INFORMATION-ON-REWRITING INTRODUCTION-TO-THE-DATABASE INTRODUCTION-TO-HINTS INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS INTRODUCTORY-CHALLENGES INTRODUCTORY-CHALLENGE-PROBLEM-1 INTRODUCTORY-CHALLENGE-PROBLEM-2 INTRODUCTORY-CHALLENGE-PROBLEM-3 (there are others but at least do a few) FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS If any of the links above are marked as "visited" by your browser, use your browser's tools menu to mark all links as unvisited. As you can see, we really think you'll get the most out of this document if you take it seriously. As you read, you will see some links to "advanced" topics. These are marked with a tiny warning sign, "<>". They lead out of this linear tutorial and into ACL2's hypertext reference manual. We recommend that you not visit any of these advanced links until you have read the entire tutorial at least once. After you finish this tutorial material, we recommend that you look at the ACL2 Demos, at the "Demos" link of the ACL2 home page, `http://www.cs.utexas.edu/users/moore/acl2'. Most users of ACL2 have bought the book Computer-Aided Reasoning: An Approach, Kaufmann, Manolios, and Moore, Kluwer Academic Publishers, June, 2000 which is available in paperback from Lulu for approximately $20 (as of 2010). See `http://www.lulu.com/content/1746161'. That book contains hundreds of exercises in programming, proof, and using The Method described here to prove theorems. Solutions to the exercises are online. For more information about the book and a companion one (also available on Lulu) about industrial applications of ACL2, see `http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html#Books' Using ACL2 is akin to having a partner in the theorem proving enterprise. It will do some of the work and you will do some of the work. It can't really be any other way because theorem proving is undecidable. You bring a quirkly, error-prone, creative insight to the problem, and ACL2 brings accuracy, logic, and perserverance. Here we describe a "model" of how the system works and introduce some of the ideas and terminology you'll use repeatedly when interacting with it. This article is about the theorem prover itself, not the programming language and not the logic. We assume you know enough about the ACL2 programming language that you can define simple functions, run them, and read and write ACL2 constants and terms. For some examples of what we'll take for granted about ACL2 programming, see *note PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED::. We also assume you know enough about logic to understand, for example, the words we use to talk about formulas and proofs. To see some examples of what we'll take for granted about your knowledge of logic terminology, see *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED::. When you give the theorem prover a goal formula to prove, it tries to prove it by breaking it down into subgoals, each of which must be proved in order to prove the original goal. This breaking apart of the goal is done by various proof techniques built into ACL2. Different proof techniques break the formula apart in different ways. For example, the simplifier rips apart the propositional structure to isolate the various cases and applies rewrite rules to simplify the subterms of the formula, while the induction engine will attempt to break the goal into some base cases and some induction steps. The theorem prover's behavior is affected by a database of rules derived from axioms, definitions, and previously proved theorems. The database also records the enabled status of each rule; only enabled rules are seen by the prover and you can set the status of a rule. There are many other user-settable switches and parameters affecting the behavior of the prover; you'll learn about some of them later. You guide the theorem prover most of the time simply by identifying lemmas for it to prove. (A lemma is just a theorem that you think is useful in the proofs of other theorems.) Why does this guide the theorem prover? Because every time you get the system to prove a theorem, it turns the theorem into a rule (unless you tell it not to) and stores the rule in the database. That changes how the prover behaves subsequently. But you determine the kind of rule ACL2 stores. To learn to "drive" the theorem prover you have to learn how various rules affect the system's behavior and how it turns proved formulas into rules. But before we discuss this, we discuss a more mathematical activity: how do you figure out the lemmas ACL2 will need in order for it to prove interesting theorems? ACL2 can often help you in this activity, if you use it in the right way. Here is the way we recommend you use ACL2. The Method. (1) you present ACL2 with the goal conjecture to prove (2) typically, it fails to prove it (or you abort its attempt), but it prints some Key Checkpoints (3) you look at the Key Checkpoints and decide that you know a fact that will help; this tutorial will present some helpful questions to keep in mind (4) you formalize your knowledge as a formula, along with directions for how ACL2 should turn the formula into a rule; this tutorial will tell you about the most commonly used rule, the rewrite rule (5) you recursively apply The Method to get ACL2 to prove your formula and to store it as the kind of rule you specified (6) go to step (1) Caveat: This approach simply won't work on some theorems! Basically, this is a "troubleshooting" approach, where you're letting ACL2 determine the basic strategy and you're just helping with the subgoals. But for some theorems, ACL2's approach will be misguided and no amount of troubleshooting will salvage its strategy. You'll have a sense that this is happening when it happens because the formulas ACL2 presents to you will raise issues that feel irrelevant to you. The basic truth is that if you think a formula is always true there are usually strong intuitive reasons behind your belief. If you were asked to defend your belief, you'd start to explain your reasons and with training you can turn that into a proof. So when ACL2's formulas present you with things you haven't thought of either (a) you'll have an "Ah ha!" moment when you realize you hadn't considered that case or (b) you'll realize that ACL2's approach is different from your intuitive "proof." But, surprisingly often, the troubleshooting approach to finding proofs works quite well, especially as you rein in your expectations and develop a sense of what ACL2 can handle on its own. Of course, if you can decompose the proof into a couple of main lemmas before you start, so much the better: write down your sequence of lemmas, thinking about the rules you want them to generate, and get ACL2 to prove each of them before giving it the main theorem. This proof planning approach will gradually become an integral part of your use of The Method. The only mechanized help we can offer with The Method, aside from the theorem prover itself, are tools to help you manage the stack of subgoals it generates when, in step (5) you recursively apply The Method to a lemma. There are both Emacs and Eclipse tools available. To use The Method you have to read the Key Checkpoints printed at the very end of failed proof attempts, just after the line that reads: The key checkpoint goals, below, may help you to debug this failure. Most users do not read the output from successful proofs and do not read the output during a proof - they just let it stream by as a sort of gestalt meter on the theorem prover's progress or lack thereof. For example, you'll be able to tell it is in a loop and needs to be interrupted. You will respond to most Key Checkpoints by formulating new lemmas for the system to prove and store as rules designed by you to alter ACL2's behavior so that it proves the Key Checkpoints. You will give each lemma a name and write some formula to express the mathematical idea. You'll command ACL2 to prove it by typing: (defthm name formula ...) In the "..." you may provide two other kinds of information: hints for how to prove formula and directives, called rule-classes, for how to convert formula into a rule after it has proved formula. Note that you are in charge of determining what kind of rule ACL2 generates! There are over a dozen different types of rules with many opportunities to specialize each type. But the most common kind of rule you'll want to generate is a rewrite rule. We recommend that you read the following topics in the following order, without skipping anything but links into the reference manual, which are marked by the little warning sign, "<>". (1) See *note INTRODUCTION-TO-REWRITE-RULES-PART-1:: to read about the use and design of rewrite rules. (2) See *note INTRODUCTION-TO-KEY-CHECKPOINTS:: to see how to use The Method to help you design rules. (3) See *note INTRODUCTION-TO-REWRITE-RULES-PART-2:: for general guidance on how to turn formulas into effective rules. (4) See *note INTRODUCTION-TO-THE-DATABASE:: to see how to issue commands that build different kinds of rules and that affect the enabled status of existing rules. (5) See *note INTRODUCTION-TO-HINTS:: to see how to give the prover hints for how to prove specific theorems or even subgoals of specific proof attempts. (6) See *note INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS:: for a few words about system aspects of interacting with ACL2. (7) See *note INTRODUCTORY-CHALLENGES:: for a graduated sequence of good challenge problems for the new user to tackle. Do not skip this section! It is here that you really learn how to use ACL2 - by using it. (8) See *note FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS:: for a list of questions that new users frequently ask, answered mainly by providing links into the reference manual. We recommend that you skim through these questions and remember that you can find the answers here later. We are very interested in receiving suggestions for how to improve this FAQ and this tutorial. See the ACL2 home page, specifically the link "Mailing Lists". Please read all of the material cited above (skipping only the reference-manual links ("<>")) before you try to use ACL2 on problems of your own. By this point you should have read, at least, the following topics from this tutorial introduction to the theorem prover: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-REWRITE-RULES-PART-1 SPECIAL-CASES-FOR-REWRITE-RULES EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES INTRODUCTION-TO-KEY-CHECKPOINTS DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS GENERALIZING-KEY-CHECKPOINTS POST-INDUCTION-KEY-CHECKPOINTS INTRODUCTION-TO-REWRITE-RULES-PART-2 STRONG-REWRITE-RULES PRACTICE-FORMULATING-STRONG-RULES PRACTICE-FORMULATING-STRONG-RULES-1 PRACTICE-FORMULATING-STRONG-RULES-2 PRACTICE-FORMULATING-STRONG-RULES-3 PRACTICE-FORMULATING-STRONG-RULES-4 PRACTICE-FORMULATING-STRONG-RULES-5 PRACTICE-FORMULATING-STRONG-RULES-6 SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES FURTHER-INFORMATION-ON-REWRITING INTRODUCTION-TO-THE-DATABASE INTRODUCTION-TO-HINTS INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS INTRODUCTORY-CHALLENGES INTRODUCTORY-CHALLENGE-PROBLEM-1 INTRODUCTORY-CHALLENGE-PROBLEM-2 INTRODUCTORY-CHALLENGE-PROBLEM-3 (there are others but at least do a few) FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS We also recommend that you look at the ACL2 Demos, at the "demos" link of the ACL2 home page `http://www.cs.utexas.edu/users/moore/acl2'. Most users of ACL2 have bought the book Computer-Aided Reasoning: An Approach, Kaufmann, Manolios, and Moore, Kluwer Academic Publishers, June, 2000 which is available in paperback from Lulu for approximately $20 (as of 2010). See `http://www.lulu.com/content/1746161'. That book contains hundreds of exercises in programming, proof, and using The Method to prove theorems. Solutions to the exercises are online. For more information about the book and a companion one (also available on Lulu) about industrial applications of ACL2, see `http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html#Books' Thank you for spending the time to get acquainted with the basics of the ACL2 theorem prover. Don't hesitate to send further questions to the ACL2 Help address on the "Mailing Lists" link of the ACL2 home page. End of Tutorial Introduction to the Theorem Prover Below is a list of all of the topics cited on this page. * Menu: * ARCHITECTURE-OF-THE-PROVER:: a simple overview of how the prover works * DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS:: how to get rid of key combinations of function symbols * EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES:: logically equivalent formulas can generate radically different rules * EXAMPLE-INDUCTION-SCHEME-BINARY-TREES:: induction on binary trees * EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2:: induction downwards 2 steps at a time * EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION:: induction on natural numbers * EXAMPLE-INDUCTION-SCHEME-ON-LISTS:: induction on lists * EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES:: induction on several variables * EXAMPLE-INDUCTION-SCHEME-UPWARDS:: induction upwards * EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS:: induction scheme with accumulators * EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS:: induction scheme with more than one induction step * EXAMPLE-INDUCTIONS:: some examples of induction schemes in ACL2 * FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS:: some questions newcomers frequently ask * FURTHER-INFORMATION-ON-REWRITING:: a grab bag of advice and information on rewriting * GENERALIZING-KEY-CHECKPOINTS:: getting rid of unnecessary specificity * INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS:: the mechanics of interaction with the theorem prover * INTRODUCTION-TO-HINTS:: how to provide hints to the theorem prover * INTRODUCTION-TO-KEY-CHECKPOINTS:: What questions to ask at key checkpoints * INTRODUCTION-TO-REWRITE-RULES-PART-1:: introduction to ACL2's notion of rewrite rules * INTRODUCTION-TO-REWRITE-RULES-PART-2:: how to arrange rewrite rules * INTRODUCTION-TO-THE-DATABASE:: how to update the database * INTRODUCTORY-CHALLENGE-PROBLEM-1:: challenge problem 1 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER:: answer to challenge problem 1 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-2:: challenge problem 2 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER:: answer to challenge problem 2 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-3:: challenge problem 3 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER:: answer to challenge problem 3 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-4:: challenge problem 4 for the new user of ACL2 * INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER:: answer to challenge problem 4 for the new user of ACL2 * INTRODUCTORY-CHALLENGES:: challenge problems for the new ACL2 user * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED:: background knowledge in ACL2 logic for theorem prover tutorial * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE:: a brief explanation of base cases * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS:: substitution of equals for equals * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION:: evaluation during proofs * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: a brief explanation of induction * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE:: a brief explanation of substitution instances * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS:: a brief explanation of propositional calculus * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER:: the inductive step of the rev-rev proof -- Answer to Question 1 * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER:: the inductive step of the rev-rev proof -- Answer to Question 2 * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER:: the inductive step of the rev-rev proof -- Answer to Question 2 * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING:: a brief explanation of rewriting from the logical perspective * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY:: further information on expanding definitions via rewriting * POST-INDUCTION-KEY-CHECKPOINTS:: reading post-induction key checkpoints * PRACTICE-FORMULATING-STRONG-RULES:: a few simple checkpoints suggesting strong rules * PRACTICE-FORMULATING-STRONG-RULES-1:: rules suggested by (TRUE-LISTP (APPEND (FOO A) (BAR B))) * PRACTICE-FORMULATING-STRONG-RULES-2:: rules suggested by (TRUE-LISTP (REV (FOO A))) * PRACTICE-FORMULATING-STRONG-RULES-3:: rules suggested by (MEMBER (FOO A) (APPEND (BAR B) (MUM C))) * PRACTICE-FORMULATING-STRONG-RULES-4:: rules suggested by (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C)) * PRACTICE-FORMULATING-STRONG-RULES-5:: rules suggested by (SUBSETP (FOO A) (APPEND (BAR B) (MUM C))) * PRACTICE-FORMULATING-STRONG-RULES-6:: rules suggested by (MEMBER (FOO A) (NATS-BELOW (BAR B))) * PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED:: background knowledge in ACL2 programming for theorem prover tutorial * SPECIAL-CASES-FOR-REWRITE-RULES:: convenient short forms for rewrite rule formulas * SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES:: advice about how to handle commonly occurring formulas as rewrite rules * STRONG-REWRITE-RULES:: formulating good rewrite rules  File: acl2-doc-emacs.info, Node: ARCHITECTURE-OF-THE-PROVER, Next: DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS, Prev: INTRODUCTION-TO-THE-THEOREM-PROVER, Up: INTRODUCTION-TO-THE-THEOREM-PROVER ARCHITECTURE-OF-THE-PROVER a simple overview of how the prover works Six built-in proof techniques are used by ACL2 to decompose the goal formula into subgoals. simplification - decision procedures and rewriting with previously proved rules, but actually including a host of other techniques under your control. Simplification is the only proof technique that can reduce a formula to 0 subgoals (i.e., prove it) rather than just transform it to other formulas. The predominant activity in most proofs is simplification. There are many ways you can affect what the simplifier does to your formulas. Good users spend most of their time thinking about how to control the simplifier. destructor elimination - getting rid of "destructor terms" like (CAR X) and (CDR X) by replacing a variable, e.g., X, by a "constructor" term, e.g., (CONS A B). But you can tell ACL2 about new destructor/constructor combinations. cross-fertilization - using an equivalence hypothesis by substituting one side for the other into the conclusion and then throwing the hypothesis away. This is a heuristic that helps use an inductive hypothesis and prepare for another induction. generalization - replacing a term by a new variable and restricting the new variable to have some of the properties of the term. You can control the restrictions imposed on the new variable. This is a heuristic that prepares the goal for another induction. elimination of irrelevance - throwing away unnecessary hypotheses. This is a heuristic that prepares the goal for another induction. induction - selecting an induction scheme to prove a formula. Inductions are "suggested" by the recursive functions appearing in the formula. But you can control what inductions are suggested by terms. But you can add additional techniques, called clause processors. The various techniques are tried in turn, with simplification first and induction last. Each technique reports one of three outcomes: it found nothing to change (i.e., the technique doesn't apply to that subgoal), it decided to abort the proof attempt (typically because there is reason to believe the proof is failing), or it decomposed the goal into k subgoals. The last outcome has a special case: if k is 0 then the technique proved the goal. Whenever k is non-0, the process starts over again with simplification on each of the k subgoals. However, it saves up all the subgoals for which induction is the only proof technique left to try. That way you see how it performs on every base case and induction step of one induction before it launches into another induction. It runs until you or one of the proof techniques aborts the proof attempt or until all subgoals have been proved. Note that if simplification produces a subgoal, that subgoal is re-simplified. This process continues until the subgoal cannot be simplified further. Only then is the next proof technique is tried. Such suboals are said to be stable under simplification. While this is happening, the prover prints an English narrative describing the process. Basically, after each goal is printed, the system prints an English paragraph that names the next applicable proof technique, gives a brief description of what that technique does to the subgoal, and says how many new subgoals are produced. Then each subgoal is dealt with in turn. If the proof is successful, you could read this log as a proof of the conjecture. But output from successful proofs is generally never read because it is not important to The Method described in introduction-to-the-theorem-prover. The output of an unsuccessful proof attempt concludes with some key checkpoints which usually bear looking at.  File: acl2-doc-emacs.info, Node: DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS, Next: EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES, Prev: ARCHITECTURE-OF-THE-PROVER, Up: INTRODUCTION-TO-THE-THEOREM-PROVER DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS how to get rid of key combinations of function symbols Suppose REV reverses a list, MEMBER checks that its first argument is an element of its second, and SQUARES-PLUS-3P is some complicated predicate. Suppose you're proving some Main Theorem that involves those concepts and the theorem prover presents you with the following hideous formula as a key checkpoint. What action should you take? Hint: Don't read the formula "for sense," i.e., don't try to understand what this formula is saying! Just look at every subterm involving a nest of two function symbols and ask if you know something about those two symbols that allows you to simplify that one subterm. (IMPLIES (AND (CONSP X) (MEMBER (+ 3 (* I I)) (REV X)) (LIST-OF-INTEGERS X) (INTEGERP I) (<= 0 I) (INTEGERP K) (<= 0 K) (< I K) (SQUARES-PLUS-3P K X) (NOT (EQUAL (CAR X) (+ 3 (* I I)))) (NOT (MEMBER (+ 3 (* I I)) X))) (SQUARES-PLUS-3P K (REV X)))? The experienced ACL2 user will stop reading at the second hypothesis! (MEMBER (+ 3 (* I I)) (REV X)) The combination of MEMBER and REV can be simplified. The question "is e a member of (REV x)" can be answered by asking "is e a member of x". The two questions are equivalent. This insight comes from your intuition about the semantics of REV - it just reorders the elements but doesn't add or delete any. The second question is simpler since it doesn't mention REV, so this is a good transformation to make. And the theorem that they are equivalent is simpler than the key checkpoint above because it involves fewer functions and smaller expressions. You might formalize this insight as (equal (member e (rev x)) (member e x)) But this conjecture is not a theorem, because (member e x) returns the cdr of x that begins with e, not just a Boolean (t or nil) indicating whether e is an element of x. The location of the first e in (rev x) is generally different than the location in x. So when we say the two questions are "equivalent" we don't mean they are equal. We mean that they're propositionally equivalent: both nil or both non-nil. This sense of equivalence is called "if and only if" and is checked by the function iff. So our intuitive insight can be phrased as this theorem: (iff (member e (rev x)) (member e x)) Suggesting that this formulation of the insight is "obvious" begs many questions. Mathematically, we could have avoided iff and just written two implications: (and (implies (member e x) (member e (rev x))) (implies (member e (rev x)) (member e x))). or (and (implies (member e x) (member e (rev x))) (implies (not (member e x)) (not (member e (rev x))))). Or we could have used iff but "oriented" it the other way: (iff (member e x) (member e (rev x))) We choose to write (iff (member e (rev x)) (member e x)) because of our knowledge of how ACL2 turns formulas into rules! We deal with this at greater length later. But just to drive the point home, if we issue the command: (defthm member-rev (iff (member e (rev x)) (member e x))) ACL2 will build in a rule that causes every propositional occurrence of (MEMBER e (REV x)) to be replaced by (MEMBER e x). (By "propositional occurrence" we mean an occurrence in which the value is tested, as by IF or the propositional connectives. Remember, one might use member to determine the location of an element too.) Note carefully: if you do not tell ACL2 how to make a rule from a theorem, it makes a rewrite rule. Rewrite rules always replace instances of the left-hand side by the corresponding instances of the right-hand side. That is, when interpreted as a rewrite rule, (iff alpha beta) makes ACL2 replace alpha by beta. Probably the biggest mistake new users make is forgetting that every theorem they prove creates a very specific rule. You must remember that you are programming ACL2 with these rules. Being careless in your statement of theorems is tantamount to being careless in your programming. What you get is a mess. Had we proved the same equivalence, but with the iff commuted, we would be giving ACL2 bad advice. We would be telling it "replace instances of (MEMBER e x) by the corresponding instances of (MEMBER e (REV x))"! If ACL2 had that rule and ever tried to simplify any member expression, e.g., (MEMBER A B), it would get into an infinite loop, e.g., producing the following sequence of transformations: (MEMBER A B) (MEMBER A (REV B)) (MEMBER A (REV (REV B))) ... until it eventually exhausted some resource. Recall that we entertained the idea of phrasing our insight about member and rev with implications rather than iff. Generally speaking, implications produce weaker rules - rules that apply less often. We discuss that later. Now suppose we've proved member-rev, oriented so as to rewrite (member e (rev x)) to (member e x), and built it in as a rewrite rule. Then suppose we repeated the attempt to prove our Main Theorem. This time, when the prover is processing the hideous Key Checkpoint printed above, our new lemma, member-rev, will hit it. It will transform the formula to: (IMPLIES (AND (CONSP X) (MEMBER (+ 3 (* I I)) X) ; <-- the hyp has simplified (LIST-OF-INTEGERS X) (INTEGERP I) (<= 0 I) (INTEGERP K) (<= 0 K) (< I K) (SQUARES-PLUS-3P K X) (NOT (EQUAL (CAR X) (+ 3 (* I I)))) (NOT (MEMBER (+ 3 (* I I)) X))) (SQUARES-PLUS-3P K (REV X)))? and then that will collapse to T, since the IMPLIES has contradictory hypotheses (note the last hypothesis above). By proving member-rev we proved the hideous checkpoint. We never had to look at the rest of the formula or think about why it is a theorem. Furthermore, attacking the main theorem again, from scratch, with member-rev in the database, may eliminate other checkpoints that came up the last time we tried to prove our main goal. So we recommend addressing one checkpoint at a time. This example illustrates that purely local thinking - looking for simplifiable combinations of function symbols - can sometimes lead to proofs and should always be your first reaction to a key checkpoint: what local fact do you know that would clean up the formula? Don't think about deep questions like "why is this true?" until you can't see any way to make it simpler. It is important to train yourself to see combinations of function symbols and to create strong rules for eliminating them. We will give you opportunities to practice this later in the tutorial. If you have been reading the tutorial introduction to the theorem prover, use your browser's Back Button now to return to INTRODUCTION-TO-KEY-CHECKPOINTS.  File: acl2-doc-emacs.info, Node: EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES, Next: EXAMPLE-INDUCTION-SCHEME-BINARY-TREES, Prev: DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES logically equivalent formulas can generate radically different rules Consider the rewrite rules that would be generated from the three commands below. In all three cases, the fact being stated relates the nth element of the reverse of x to the nth element of x. In fact, the three formulas are simple rearrangements of each other and are all equivalent. The theorem prover treats all three formulas equivalently when proving them. But the rules generated from them are very different. (defthm nth-rev-1 (implies (and (natp n) (< n (len x))) (equal (nth n (rev x)) (nth (- (len x) (+ 1 n)) x)))) (defthm nth-rev-2 (implies (and (natp n) (< n (len x))) (equal (nth (- (len x) (+ 1 n)) x) (nth n (rev x))))) (defthm nth-rev-3 (implies (and (natp n) (not (equal (nth n (rev x)) (nth (- (len x) (+ 1 n)) x)))) (not (< n (len x))))) Here are the three rewrite rules: nth-rev-1: Replace instances of (NTH n (REV x)) by (NTH (- (LEN x) (+ 1 n)) x), if you can establish that n is a natural number less than the length of x. nth-rev-2: Replace instances of (NTH (- (LEN x) (+ 1 n)) x) by (NTH n (REV x)), if you can establish that n is a natural number less than the length of x. nth-rev-3: Replace instances of (< n (LEN x)) by NIL if you can establish that n is a natural number and that (NTH n (REV x)) is different from (NTH (- (LEN x) (+ 1 n)) x). As the driver of ACL2, you have to decide which rule you want when you give the command to prove it. If you tell the theorem prover to use both nth-rev-1 and nth-rev-2, ACL2 will enter an infinite loop when it sees any term matching either NTH expression. Most users would choose form nth-rev-1 of the rule. It eliminates rev from the problem - at the expense of introducing some arithmetic. But arithmetic is so fundamental it is rarely possible to avoid it and it is likely to be in the problem already since you're indexing into (rev x). The nth-rev-2 form of the rule is "bad" because it introduces rev into a problem where it might not have appeared. The nth-rev-3 version is "bad" because it makes the theorem prover shift its attention from a simple arithmetic inequality to a complicated property of nth and rev, which might not be in the problem. Use your browser's Back Button now to return to introduction-to-rewrite-rules-part-1.  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-BINARY-TREES, Next: EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2, Prev: EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-BINARY-TREES induction on binary trees See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Classical Induction on Binary Trees: To prove (p x), for all x, by classical induction on binary tree structures, prove each of the following: Base Case: (implies (atom x) (p x)) Induction Step: (implies (and (not (atom x)) (p (car x)) (p (cdr x))) (p x)) An argument analogous to that given in example-induction-scheme-on-lists should convince you that (p x) holds for every object. A function that suggests this induction is: (defun flatten (x) (if (atom x) (list x) (app (flatten (car x)) (flatten (cdr x))))).  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2, Next: EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION, Prev: EXAMPLE-INDUCTION-SCHEME-BINARY-TREES, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2 induction downwards 2 steps at a time See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Classical Induction on Natural Numbers Preserving Parity: Here is another way to decompose natural numbers. To prove (p n), for all n, prove each of the following: Base Case 1: (implies (zp n) (p n)) Base Case 2: (implies (equal n 1) (p n)) Induction Step: (implies (and (not (zp n)) (not (equal n 1)) (p (- n 2))) (p n)) Base Case 1 establishes that p holds for 0 (and all objects other than positive naturals). Base Case 2 establishes that p holds for 1. The Induction Step establishes that if n is a natural number greater than 1, and if p holds for n-2, then p holds for n. Note that we have thus proved that (p n) holds, for all n. For example, (p -7), (p 'abc), and (p 0) are all established by Base Case 1. (p 1) is established by Base Case 2. (p 2) is established from (p 0) and the Induction Step. Think about it! (p 3) is established form (p 1) and the Induction Step, etc. A function that suggests this induction is: (defun parity (n) (if (zp n) 'even (if (equal n 1) 'odd (parity (- n 2))))).  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION, Next: EXAMPLE-INDUCTION-SCHEME-ON-LISTS, Prev: EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION induction on natural numbers See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Classical Induction on Natural Numbers: Induction is familiar in the arithmetic setting. To prove (p n), for all n, by classical induction on the construction of the natural numbers, prove each of the following: Base Case: (implies (zp n) (p n)) Induction Step: (implies (and (not (zp n)) (p (- n 1))) (p n)) The Base Case establishes that p holds for 0. In fact, because of the definition of zp <>, it establishes that (p n) holds when n is 0 and it holds when n is not a natural number. The Induction Step establishes that if n is a natural number other than 0, and if p holds for n-1, then p holds for n. The hypothesis (p (- n 1)) above is called the induction hypothesis. A function that suggests this induction is (defun nat-recursion (n) (if (zp n) n (nat-recursion (- n 1)))) Similarly, the term (fact n) suggests this induction if fact is defined: (defun fact (k) (if (zp k) 1 (* k (fact (- k 1))))). even though the formal parameter of this definition of fact is k, not n.  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-ON-LISTS, Next: EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES, Prev: EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-ON-LISTS induction on lists See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Classical Induction on Lists: To prove (p x), for all x, by classical induction on the linear list structure, prove each of the following: Base Case: (implies (endp x) (p x)) Induction Step: (implies (and (not (endp x)) (p (cdr x))) (p x)) An argument analogous to that given for natural numbers, example-induction-scheme-nat-recursion, establishes (p x) for every x. For example, (p -7), (p 'abc), and (p nil) are all established by the Base Case. (p '(Friday)) follows from (p nil), given the Induction Step. That sentence bears thinking about! Think about it! Similarly, (p '(Yellow)) holds for the same reason. (p '(Thursday Friday)) follows from (p '(Friday)) and the Induction Step, etc. A function that suggests this induction is (defun app (x y) (if (endp x) y (cons (car x) (app (cdr x) y)))).  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES, Next: EXAMPLE-INDUCTION-SCHEME-UPWARDS, Prev: EXAMPLE-INDUCTION-SCHEME-ON-LISTS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES induction on several variables See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Induction on Several Variables To (p n x) for all n and all x, prove each of the following: Base Case 1: (implies (endp x) (p n x)) Base Case 2: (implies (and (not (endp x)) (zp n)) (p n x)) Induction Step: (implies (and (not (endp x)) (not (zp n)) (p (- n 1) (cdr x))) (p n x)) A function that suggests this induction is (defun nth (n x) (if (endp x) nil (if (zp n) (car x) (nth (- n 1) (cdr x))))).  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-UPWARDS, Next: EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS, Prev: EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-UPWARDS induction upwards See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Induction Upwards: To (p i max) for all i and all max, prove each of the following: Base Case: (implies (not (and (natp i) (natp max) (< i max))) (p i max)) Induction Step: (implies (and (natp i) (natp max) (< i max) (p (+ i 1) max)) (p i max)) Note that the induction hypothesis is about an i that is bigger than the i in in the conclusion. In induction, as in recursion, the sense of one thing being "smaller" than another is determined by an arbitrary measure of all the variables, not the magnitude or extent of some particular variable. A function that suggests this induction is shown below. ACL2 has to be told the measure, namely the difference between max and i (coerced to a natural number to insure that the measure is an ordinal). (defun count-up (i max) (declare (xargs :measure (nfix (- max i)))) (if (and (natp i) (natp max) (< i max)) (cons i (count-up (+ 1 i) max)) nil)).  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS, Next: EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS, Prev: EXAMPLE-INDUCTION-SCHEME-UPWARDS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS induction scheme with accumulators See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. To prove (p x a) for all x and all a, prove each of the following: Base Case: (implies (endp x) (p x a)) Induction Step: (implies (and (not (endp x)) (p (cdr x) (cons (car x) a))) (p x a)) Note that in the induction hypothesis we assume p for a smaller x but a larger a. In fact, we could include as many induction hypotheses as we want and use any terms we want in the a position as long as the x position is occupied by a smaller term. A function that suggests this particular induction is shown below. (defun rev1 (x a) (if (endp x) a (rev1 (cdr x) (cons (car x) a)))). A function that suggests a similar induction in which three induction hypotheses are provided, one in which the a position is occupied by (cons (car x) a), another in which the a position is occupied by some arbitrary term b, and a third in which the a position is occupied by a, is suggested by the term (rev1-modified x a b) where (defun rev1-modified (x a b) (if (endp x) (list x a b) (list (rev1-modified (cdr x) (cons (car x) a) b) (rev1-modified (cdr x) b b) (rev1-modified (cdr x) a b)))) Remember that the value of this term or function is irrelevant to the induction suggested. Because ACL2's definitional principle insists that all the formal parameters play a role in the computation (at least syntactically), it is common practice when defining functions for their induction schemes to return the list of all the formals (to insure all variables are involved) and to combine recursive calls on a given branch with list (to avoid introducing additional case analysis as would happen if and or or or other propositional functions are used). If you tried to prove (p x a) and suggested the induct hint (rev1-modified x a (fact k)), as by (thm (p x a) :hints (("Goal" :induct (rev1-modified x a (fact k))))) the inductive argument would be: Base Case: (implies (endp x) (p x a)) Inductive Step: (implies (and (not (endp x)) (p (cdr x) (cons (car x) a)) (p (cdr x) (fact k)) (p (cdr x) a)) (p x a))  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS, Next: EXAMPLE-INDUCTIONS, Prev: EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS induction scheme with more than one induction step See *note LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF:: for an explanation of what we mean by the induction suggested by a recursive function or a term. Several Induction Steps: To (p x i a) for all x, i, and a, prove each of the following: Base Case 1: (implies (zp i) (p x i a)) Induction Step 1: (implies (and (not (zp i)) (equal (parity i) 'even) (p (* x x) (floor i 2) a)) (p x i a)) Induction Step 2: (implies (and (not (zp i)) (not (equal (parity i) 'even)) (p x (- i 1) (* x a))) (p x i a)) A function that suggests this induction is the binary exponentiation function for natural numbers. (defun bexpt (x i a) (cond ((zp i) a) ((equal (parity i) 'even) (bexpt (* x x) (floor i 2) a)) (t (bexpt x (- i 1) (* x a) )))). In order to admit this function it is necessary to know that (floor i 2) is smaller than i in the case above. This can be proved if the community book "arithmetic-5/top" has been included from the ACL2 system directory, i.e., (include-book "arithmetic-5/top" :dir :system) should be executed before defining bexpt.  File: acl2-doc-emacs.info, Node: EXAMPLE-INDUCTIONS, Next: FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS, Prev: EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER EXAMPLE-INDUCTIONS some examples of induction schemes in ACL2 Here are some pages illustrating various induction schemes suggested by recursive functions. Classical Induction on Natural Numbers: see *note EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION::. Induction Preserving Even/Odd Parity or Induction Downwards by 2 or Induction with Multiple Base Cases: see *note EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2:: for an induction in which the induction hypothesis decreases the induction variable by an amount other than 1. This illustrates that the induction hypothesis can be about whatever term or terms are needed to explain how the formula recurs. The example also illustrates an induction with more than one Base Case. Classical Induction on Lists: see *note EXAMPLE-INDUCTION-SCHEME-ON-LISTS:: for an induction over linear lists, in which we inductively assume the conjecture for (cdr x) and prove it for x. It doesn't matter whether the list is nil-terminated or not; the Base Case addresses all the possibilities. Classical Induction on Binary (Cons) Trees: see *note EXAMPLE-INDUCTION-SCHEME-BINARY-TREES:: for an induction over the simplest form of binary tree. Here the Induction Step provides two hypotheses, one about the left subtree and one about the right subtree. Induction on Several Variables: see *note EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES:: for an induction in which several variables participate in the case analysis and induction hypotheses. Induction Upwards: see *note EXAMPLE-INDUCTION-SCHEME-UPWARDS:: for an induction scheme in which the induction hypothesis is about something "bigger than" the induction conclusion. This illustrates that the sense in which the hypothesis is about something "smaller" than the conclusion is determined by a measure of all the variables, not the magnitude or extent of some single variable. Induction with Auxiliary Variables or Induction with Accumulators: see *note EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS:: for an induction scheme in which one variable "gets smaller" but another is completely arbitrary. Such schemes are common when dealing with tail-recursive functions that accumulate partial results in auxiliary variables. This example also shows how to provide several arbitrary terms in a non-inductive variable of a scheme. Induction with Multiple Induction Steps: see *note EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS:: for an induction in which we make different inductive hypotheses depending on which case we're in. This example also illustrates the handling of auxiliary variables or accumulators.  File: acl2-doc-emacs.info, Node: FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS, Next: FURTHER-INFORMATION-ON-REWRITING, Prev: EXAMPLE-INDUCTIONS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS some questions newcomers frequently ask This FAQ is for people who've read through all the other sections of the tutorial introduction to the theorem prover (see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: and all the links from it that are not marked with the little warning sign ("<>"). Do not expect to understand our answers if you haven't taken the time to read through the tutorial. In the answers below you will see more links into the hypertext reference manual. While such links were marked "<>" in the tutorial, they are not marked that way here. When you enter the reference manual be prepared to explore and assemble a mini-course on the topic of interest, not a quick fix. Q. How do I find something in the ACL2 documentation? A. Try going to the Search link on the ACL2 home page. Q. How does the theorem prover work? A. We really don't think you need to know much about the inner workings of the prover to become an effective user. That doesn't mean the system is self-explanatory! It means that stuff you need to learn is not how the theorem prover works but how to interact with it! That is what introduction-to-the-theorem-prover is about. However, if you want the most basic overview of the prover, see *note ARCHITECTURE-OF-THE-PROVER::. Q. How do I define a new function? A. See *note DEFUN::. Q. How do I define a new predicate? A. See *note DEFUN::. Q. How do I define a new relation? A. See *note DEFUN::. Q. How do I define a function or predicate that takes a varying number of arguments? A. You can't. However, see *note DEFMACRO:: to learn how to define a macro that takes a varying number of arguments and expands into an arbitrary term that you compute. Q. How do I define a macro that is sensitive to the state? A. You can't. However, advanced users should consider make-event. Q. How do I define mutually recursive functions? A. See *note MUTUAL-RECURSION::. However, you should realize that when two functions, say f and g, are mutually recursive, properties of f generally have to be stated simultaneously with properties of g, since inductive proofs about one function require inductive hypotheses about the other. Furthermore, ACL2 does not know how to do inductions for mutually recursive functions and must be told. See *note MUTUAL-RECURSION-PROOF-EXAMPLE::. Q. How do I declare the type signature of a function? A. You can't. ACL2 is a syntactically untyped language and all functions are defined on all objects in the ACL2 universe. We recommend that the new user get used to this and only then explore the use of ACL2 to express and enforce a type regime. In ACL2, the guard of a function is akin to the type signature of a function in typed languages. However, ACL2 guards may be arbitrary terms, not just type conditions, they only concern the inputs to the function (not its result), and do not affect the axiom defining the function - all functions are defined on every combination of objects. You may, of course, prove theorems that establish that every function called in a definition or theorem is supplied with input satisfying its guards (which necessarily involves describe the outputs too). These formulas are called guard conjectures and the process of proving them is called guard verification. Since guards are arbitrary ACL2 formulas, the "type regimes" one tends to enforce in ACL2 can be much for flexible and expressive than those in most programming languages. However, that expressibility also means guard verification can be challenging (indeed undecidable). On the other hand, if one limits oneself to simple type-like guards, lemmas can be proved that make most guard verification fully automatic and one can configure ACL2 to do guard verification automatically at defun-time. One may also delay guard verification until "the right" lemmas have been proved. By doing guard verification one can make functions execute faster by allowing the code to avoid runtime checks. This is especially valuable to industrial users who have large models used both in verification and as simulation engines for designed artifacts. In addition, guard verification can give you the assurance that you are using your functions within their intended domains and hence is a form of functional specification and verification. However, all these advantages aside, it is remarkably easy to drift away from the simplest type regimes and write a guard that raises deep mathematical problems. New users should not try to confront these problems until they are comfortable with the theorem prover and with lemma development. Therefore, we strongly recommend that you forget about types and guards and get used to reasoning about total functions. When you do decide to learn about them, be prepared for a complex story involving specification, execution efficiency, and proof management. See *note GUARD::. Q. How do I tell defun what measure to use? A. See *note XARGS::, specifically :measure. Q. I specified a measure that always returns a natural number but ACL2 is acting like it's not a natural number. A. There are two likely problems. The most likely one is that your measure isn't really always a natural! Suppose the formals of your defun are x and y and your measure is (m x y). Suppose the recursive calls of your function are protected by tests that insure that x and y are naturals. Then you might assume x and y are naturals in the measure. But ACL2 has to prove (o-p (m x y)), where o-p is the predicate that recognizes ordinals (and naturals are ordinals). Note that the theorem doesn't have any hypotheses! You might intuitively think that your measure has to be an ordinal just under the conditions that lead to recursive calls. That's not what ACL2 enforces. It has to be an ordinal, always. So you must change your specified measure. For example, consider wrapping nfix around it or around its uses of x and y to coerce those quantities to naturals. The second most likely explanation is that your measure returns a natural, always, but ACL2 doesn't know that and it takes induction to prove. This might happen if m involves some recursive functions. In this case, prove (natp (m x y)) before your defun. Perhaps you should consider making the natp lemma a :type-prescription lemma to make ACL2's typing algorithm aware of it. Q. How do I tell defun what well-founded relation to use? A. See *note XARGS::, specifically :well-founded-relation. Q. How do I show that a relation is well-founded? A. Prove a theorem establishing that there is an order preserving embedding into the ordinals and store it with :rule-classes :well-founded-relation. Q. What is an ordinal? What does it mean to be well-founded? A. Ordinals are an extension of the natural numbers used to insure that a process can't go on forever. Like naturals, they can be added, multiplied, and exponentiated. There is a sense of one ordinal being less than another. Unlike the naturals, each of which is finite, the ordinals include infinite objects. Now imagine "standing" on an ordinal and "stepping" to a smaller one. Like the naturals, this "walk down the ordinals" can't go on forever, even if you start on an infinite ordinal. That is because the ordinals are well-founded. See o-p for more information about ordinals in ACL2 and about well-foundedness. See *note ORDINALS:: for a deeper discussion and a discussion of books that can help configure ACL2 to reason about ordinals. Q. How can provide hints for the termination proofs in defun? A. See *note XARGS::, specifically :hints (for the termination proofs) and :guard-hints (for the guard verification proofs). Q. How do I define a constant (something like a global variable)? A. See *note DEFCONST::. But remember that as an applicative programming language, ACL2 does not have global variables! You can define a symbol to have a fixed value and use the symbol sort of like a global variable in function definitions: you may refer to the value of the symbol in your functions without passing the variable in as formal parameter. But you may not ever change the value of the symbol! Q. How do I save the value of a top-level computation for future use? A. See *note ASSIGN:: and see *note @: atsign. Q. How do I introduce new syntactic form or abbreviation? A. See *note DEFMACRO::. Q. How can create and modify an array? A. ACL2 is a functional language, so it is impossible to destructively modify an existing object; technically, all "updates" to objects must be implemented by "copy-on-write" semantics. That said, ACL2 provides support for arrays, provided you use them in a restricted way. They give you constant-time access and change under the use restrictions. Q. How do I read from or write to a file? How do I do IO? A. To manipulate files, your function must have state as an argument, so you should read about the restrictions that imposes. For input/output facilities, see *note IO::. Q. How do I define a structure that can be destructively modified? A. ACL2 is an applicative programming language. You can't modify objects arbitrarily! You basically have to "copy on write," which means you construct new objects from old ones, making the changes you want in the new one. If the car of some object is 1 at one moment and 2 later, then the basic logical axiom (car x) = (car x) is violated! However, if the only reference to the old object, e.g., x, was to pass it to the code that copied and "changed" it, then ACL2 can re-use the old object to produce the new one and the axioms would not object. Such syntactic restrictions can make x a modifiable structure but they will impose a heavy burden on you as a programmer: if pass such an x to a function and the function modifies it, then you must pass x only to that function and you must return the modified value and use it henceforth. Such objects are said to be single threaded. See *note DEFSTOBJ::. Q. How do I write a universal quantifier? An existential quantifier? How can I say "for all" or "there exists"? A You can't literally write quantifiers. But ACL2 has the power of full first order logic with quantification. See *note QUANTIFIERS::. Q. How do I introduce an undefined or uninterpreted function symbol? Can I constrain it to have certain properties? A. See *note ENCAPSULATE::. Q. How can I hide a lemma? I want to prove a lemma temporarily to use in another proof but I don't want the lemma around thereafter. A. One way to get a similar effect is to prove the lemma and then disable it with an (in-theory (disable ...)) event; see *note IN-THEORY::. Another way is to put the lemma and the theorem that needs it into an encapsulate and wrap a local around the lemma. Q. What is an event? A. An event is a command that adds information to the ACL2 database (the "logical world"), like defun or defthm. See *note EVENTS::. Q. How do I say that I do not want a rewrite rule generated from a theorem? A. The command (defthm name formula :rule-classes nil) will attempt to prove formula and, if successful, give formula the name name, which you may use in hints as a theorem, but it will build no rules from the formula. Q. How do I say that I want a formula to be stored as a rewrite rule? A. The command (defthm name formula) will attempt to prove formula and, if successful, it will give formula the name name and generate a rewrite rule from it, with the runic name (:rewrite name)]. It could happen that formula generates more than one rewrite rule, e.g., this happens if the conclusion is an AND. In this case, each conjunctive branch through the conclusion generates a rule named (:rewrite name . i), for i=1,2,... For more details, see *note REWRITE::. Q. How do I say that I want a formula to be stored as a rewrite rule and some other kinds of rules? A. The command (defthm name formula :rule-classes (:class1 ... classk)) will attempt to prove formula and, if successful, it will give formula the name name and generate a rule of each :classi specified. Each :classi should either be a keyword, like :REWRITE or :GENERALIZE, naming a rule class (see *note RULE-CLASSES::), or else should be a list that starts with a rule class and then lists the relevant modifiers. Be sure to include :REWRITE among the rule classes if you want the formula to generate a rewrite rule. It doesn't do that automatically if you explicitly specify :rule-classes! Q. How do I rearrange the shape of a formula before generating a rule from it? A. See *note RULE-CLASSES:: and read about the :corollary modifier. Q. What is a type-prescription? A. ACL2 has an algorithm for determining the type of object returned by a term, where a type is one of the Common Lisp primitive datatypes such as natural, integer, Boolean, cons, true-listp, etc. Rules provided by you can influence this algorithm. See *note TYPE-PRESCRIPTION::. Q. How do rewrite rules work? A. Re-read the tutorial sections: introduction-to-rewrite-rules-part-1 and introduction-to-rewrite-rules-part-2. Q. How do I see what's in the database? A. You can't look at the entire database with user-friendly tools. You can print the command that introduced a particular name, print the entire sequence of user commands, print the commands in the region between two commands, print all the rewrite rules that apply to a given term or function symbol, and many other options. See *note HISTORY::. If you have loaded a book from another user, you might wish to read the source file. For example, the source file for (include-book "arithmetic-5/top" :dir :system) is the file named arithmetic-5/top.lisp on the acl2-sources/books/ directory where ever your ACL2 sources are installed. Often, books are well-documented by comments by their authors. Some books have Readme or README files on the same directory. Q. How do I undo a command? A. See *note HISTORY::, especially see *note U:: ("undo") and see *note UBT:: ("undo back through"). Q. What rewrite rules match a given term? A. See *note PL::. Q. What were those questions to ask when looking at key checkpoints? A. See *note INTRODUCTION-TO-KEY-CHECKPOINTS::. Q. How do I figure out why a rewrite rule won't fire? A. If you activate rewrite rule monitoring (see *note BRR::) and then install a monitor (see *note MONITOR::) on the rule in question, ACL2 will enter an interactive break whenever the pattern of the rule is matched against a target in a proof. So after installing the monitor, re-try the proof and then interact with the rewriter via break commands (see *note BRR-COMMANDS::). Like all trace and break packages, you have to know what you're doing to use the break rewrite facility, so read the material in the reference manual. If no interactive break happens after you've installed the monitor on your rule and re-tried the proof, it means no suitable target ever arose. Don't forget to turn off monitoring when you're done as it slows down the system. Q. Why is a proof taking so long? A. Unexpectedly poor performance on simple problems is usually a sign of cyclic rewriting or combinatoric explosion. See *note DMR:: and see *note ACCUMULATED-PERSISTENCE::. Q. How do I tell ACL2 what induction to do for a particular formula? A. When issuing the defthm command for the formula, supply an :induct hint: (defthm name formula :hints (("Goal" :induct (f x1 ... xn)))) where f is a function that recurs the way you want the induction to unfold and x1 ... xn are the relevant variables in formula. You usually have to define f appropriately for each formula that needs an induct hint, though sometimes you can re-use an earlier f or one of the functions in the formula itself. It doesn't matter what value (f x1 ... xn) returns. All that matters is how it recurs. The termination analysis for f justifies the induction done. See *note HINTS::, especially the section on :induct hints; also see *note INDUCTION::. Q. ACL2 doesn't know simple arithmetic that can simplify the term (+ 1 -1 x). A. You should load an arithmetic book whenever you're dealing with an arithmetic problem. The simplest arithmetic book is typically loaded with the event (include-book "arithmetic/top-with-meta" :dir :system). If you're using floor and mod or non-linear arithmetic like (* x y) you should use (include-book "arithmetic-5/top" :dir :system). See also the discussion of arithmetic books under the "Lemma Libraries and Utilities" link of the ACL2 home page, and see *note COMMUNITY-BOOKS::. Q. ACL2 is not using an arithmetic lemma that I proved. A. Lemmas concluding with arithmetic inequalities, like (implies (member e x) (< (len (delete e x)) (len x))) are not good rewrite rules because they rarely match targets because of intervening arithmetic operators. For example, the above conclusion doesn't match (< (LEN (DELETE E X)) (+ 1 (LEN X))). You should store such lemmas as :linear rules by using the command: (defthm len-delete (implies (member e x) (< (len (delete e x)) (len x))) :rule-classes :linear) See *note LINEAR::. Q. What is a linear rule? A. See *note LINEAR::. Q. How do I make ACL2 treat a relation as an equality? A. We assume you mean to treat the relation as an equivalence, i.e., replace one term by another when they are equivalent under your relation. See *note EQUIVALENCE::, see *note CONGRUENCE::, and see *note REFINEMENT::. Q. One of my rewrite rules has a hypothesis that doesn't rewrite to true. What do I do? A. Prove a rewrite rule that establishes that hypothesis under the relevant assumptions from the context of the intended target. Alternatively, undo the rule and restate it with a force around the problematic hypothesis, making ACL2 assume the hypothesis when the rule is applied and raising the truth of the hypothesis as an explicit subgoal of the proof. See also case-split. Of course, you should always state the strongest rewrite rules you can think of, eliminating hypotheses or shifting them to the right-hand side inside of IFs; see *note STRONG-REWRITE-RULES::. Q. How do I make ACL2 "guess" the right instantiation of a free variable? A. You can provide a :restrict hint that names the problematic lemma and provides an instantiation of the free variable. See *note HINTS::, specifically :restrict. You could alternatively give a hint that :uses the rule and provides the appropriate instantiation in full. See *note HINTS::, specifically :use. Recall that ACL2 guesses free variable instantiations by matching the problematic hypothesis to the assumptions in the context of the target. If the appropriate assumption is present but ACL2 is finding another one, try undoing the problematic rule and proving it again, specifying the :match-free :all modifier of the :rewrite or :linear rule class. See *note RULE-CLASSES::. Alternatively, undo and prove the problematic rule again and use a bind-free form to compute the appropriate instantiation. Q. How can I make ACL2 do a case split to prove a certain subgoal? A. See *note HINTS::, specifically :cases. Q. How can I prevent ACL2 from using a rewrite rule? A. See *note HINTS::, specifically :in-theory (disable ...). If the use of the rule is problematic in only one subgoal and the lemma is needed in other subgoals, disable the lemma only in the problematic subgoal by specifying the subgoal name (e.g., "Subgoal 1/3.2.1") as the goal specifier in the hint. If the rule isn't needed anywhere in the proof, you could use the specifier "Goal". If you don't think the rule will ever be needed for a while, you could globally disable it with the event (in-theory (disable ...)) (see *note IN-THEORY::) executed before the first problematic proof. If the rule has never been used or must always be disabled, undo it and prove it again with :rule-classes nil. Q. How can I prevent ACL2 from running a definition on constants? I tried disabling the function but that didn't work. A. If you have a function named f then disabling f will disable the definitional axiom about f. But ACL2 has another kind of rule about f, telling it how to evaluate f. The rune of this rule is (:executable-counterpart f). Try disabling that, as in the :hints ((... :in-theory (disable (:executable-counterpart f)) ...c[)). Q. How can I make ACL2 use a rule in a proof? A. See *note HINTS::, specifically :use. Q. How can I make ACL2 expand a function call in a proof? A. You can give an :See *note EXPAND:: hint. Q. ACL2 sometimes aborts the proof attempt before showing me all of the subgoals. How can I make it just keep going instead of aborting early? A. See *note OTF-FLG::, which stands for Onward Thru the Fog FLaG. Q. How can I compute when I want a rule to fire? A. See *note SYNTAXP::. Q. How can I add pragmatic advice to a lemma after it has been proved? For example, how can add a forced hypothesis, a backchain limit, or a syntaxp test? A. You can't. You can undo the lemma, restate it appropriately, and prove it again. This produces the cleanest database. Alternatively, you can prove the restated lemma under a new name - a task that should be easy since the old version is in the database and will presumably make the proof trivial - and then disable the old name. Q. How can I stop ACL2 from rewriting a term? A. If you need rewriting done but want to prevent some particular terms from being rewritten, see *note HINTS::, specifically :hands-off. Alternatively, consider embedding the problematic term in a hide. Users sometime develop special theories (see *note THEORY::) containing just the rules they want and then use hints to switch to those theories on the appropriate subgoals. Q. Can I compute which subgoals a hint refers to? A. Yes, see *note COMPUTED-HINTS::. This topic is for advanced users but knowing that it is possible might come in handy someday. Q. I want the rewriter to always use one theory and then switch to another so that it doesn't use my most complicated before until doing the simple things. A. This is possible but is something for the advanced user. It can be done using a form of computed-hints. See *note USING-COMPUTED-HINTS-7::. Q. Is there a way to attach the same hint to every defthm? A. See *note DEFAULT-HINTS::. Q. How can I just tell ACL2 the proof steps? A. See *note VERIFY:: and see *note PROOF-CHECKER::. Q. How can I write my own simplifier? A. See *note META::. Q. How can I add an axiom or just assume some lemma without proof? A. This is very dangerous but is a good strategy for exploring whether or not a certain set of lemmas (and their rules) is sufficient to prove your goal. See *note DEFAXIOM:: and see *note SKIP-PROOFS::. Q. How can redefine a user-defined function? A. This is tricky. What if you've already proved theorems about the old definition and then wish to change it? There are several options. See *note LD-REDEFINITION-ACTION:: (and note specifically the discussion of updater function for it, set-ld-redefinition-action); also see *note REDEF::, see *note REDEF!::, see *note REDEF+::, and see *note REDEF-::. Q. How do I change a function from :program mode to :logic mode? A. See *note VERIFY-TERMINATION::. Q. How do I change the guards on a function? A. You can't. Undo it and redefine it. Q. What is program mode? A. See *note PROGRAM::. Q. What does the ACL2 prompt mean? A. See *note INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS:: or, specifically, see *note PROMPT::. Q. What is logic mode? A. See *note LOGIC::. Q. How do I get into or out of :program mode? :Logic mode? A. See *note PROGRAM:: and see *note LOGIC::. You can enter these modes temporarily for a particular defun by using (declare (xargs :mode :program)) or (declare (xargs :mode :logic)) after the list of formal parameters to the definition. Q. How do I quit from ACL2? A. This varies depending on the interface you're using. See *note INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS::. Q. How do I load a file of definitions and theorems created by someone else? A. See *note INCLUDE-BOOK::. Q. How do I create my own book of definitions and theorems? A. See *note BOOKS::. Q. Where are the books referenced by :dir :system on my machine? A. If your ACL2 is installed on the directory dir/acl2-sources and you follow the standard installation instructions, then the books are typically the files under the directory dir/acl2-sources/books/. Q. How can I find out what books are available? A. Go to the ACL2 home page, http://www.cs.utexas.edu/u/moore/acl2/ and look at the link labeled "Lemma Libraries and Utilities." Q. How do I produce my own book? A. See *note BOOKS::. Q. What is a decision procedure? What decision procedures does ACL2 have? A. A decision procedure is an algorithm that given enough time and space can determine, for all the formulas in a certain syntactic class of formulas, whether the formula is a theorem or not. The most well-known decision procedure is for propositional calculus: by testing a formula under all the combinations Boolean assignments to the variables, one can decide if a propositional formula is a theorem. The syntactic class consists of all formulas you can write using just variables, constants, and compositions of the functions and, or, not, implies, iff, and if. There are, of course, an exponential number of different assignments so the algorithm can be slow. ACL2 contains a propositional decision procedure based on symbolic normalization that can be faster than trying all the combinations of truthvalues - but is not guaranteed to be faster. ACL2 also contains an optional bdd procedure. ACL2 also contains a decision procedure for rational arithmetic involving variables, rational constants, addition, multiplication by rational constants, equality, and the various versions of arithmetic inequality (<, <=, >=, and >). It can be extended with :linear lemmas. ACL2 is complete for equality over uninterpreted (e.g., undefined and unconstrained) function symbols using an algorithm based on transitive closure of equivalence classes under functional substitutivity. Finally, you can make ACL2 use other decision procedures, even ones implemented outside of ACL2; see *note CLAUSE-PROCESSOR::. Q. ACL2 has the change of variable trick (destructor elimination) that it does to get rid of (CAR X) and (CDR X) by replacing x by (CONS A B). Is there a way to make ACL2 do that for other terms? A. Yes. See *note ELIM::. Q. How can I prevent destructor elimination? A. See *note HINTS::, specifically :do-not '(eliminate-destructors). Q. How can I prevent cross-fertilization? A. See *note HINTS::, specifically :do-not '(fertilize). Q. How can I prevent generalization? A. See *note HINTS::, specifically :do-not '(generalize). Q. How can I make ACL2 impose a restriction on a new variable introduced by destructor elimination or generalization? A. See *note GENERALIZE::. Q. What rule classes are there? A. See *note RULE-CLASSES::. Q. What is a theory? A. See *note THEORIES::. Q. How are hints inherited by the children of a subgoal? A. See *note HINTS-AND-THE-WATERFALL::. Q. How do I use ACL2 under Emacs? A. See *note EMACS::. Q. How do I use ACL2 under Eclipse? A. See *note ACL2-SEDAN::. Q. How do I interrupt the prover? A. The keyboard sequence for interrupting a running process depends your operating system, host Common Lisp, and user interface (e.g., Emacs, Eclipse, etc.). But perhaps a few examples will help you discover what you need to know. If your host Common Lisp is GCL or Allegro and you are typing directly to the Common Lisp process, then you can interrupt a computation by typing "ctrl-c" (hold down the Control key and hit the "c" key once). But other Lisps may require some other control sequence. If you are typing to an Emacs process which is running the GCL or Allegro Common Lisp process in a shell buffer, you should type ctrl-c ctrl-c - that is, you have to type the previously mentioned sequence twice in succession. If you are running the ACL2 Sedan, you can use the Interrupt Session button on the tool bar. The environment you enter when you interrupt depends on various factors and basically you should endeavor to get back to the top level ACL2 command loop, perhaps by typing some kind of Lisp depenent "abort" command like A or :q, or :abort!. You can usually determine what environment you're in by paying attention to the prompt. Q. What is the ACL2 loop? A. That is the name given to the interactive environment ACL2 provides, a "read-eval-print loop" in which the user submits successive commands by typing ACL2 expressions and keyword commands. See *note INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS::. Q. What is raw lisp? A. That is our name for the host Common Lisp in which ACL2 is implemented. See *note INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS::. There is an ACL2 mode named raw mode which is different from "raw lisp." See *note SET-RAW-MODE::. Q. Can I get a tree-like view of a proof? A. See *note PROOF-TREE:: for an Emacs utility that displays proof trees and allows you to navigate through a proof from the proof tree. The ACL2 Sedan also supports proof trees and you should see the ACL2s documention on that topic. Q. I used the earlier Boyer-Moore theorem prover, Nqthm. How is ACL2 different? A. See *note NQTHM-TO-ACL2::. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.  File: acl2-doc-emacs.info, Node: FURTHER-INFORMATION-ON-REWRITING, Next: GENERALIZING-KEY-CHECKPOINTS, Prev: FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER FURTHER-INFORMATION-ON-REWRITING a grab bag of advice and information on rewriting In the following paragraphs we give some links to advanced topics, marked with "<>". If you are reading this topic as part of the tutorial on the theorem prover, do not follow these links upon your first reading. Just take note of the existence of the facilities and ideas mentioned. Arithmetic: If your goal theorem involves even trivial arithmetic, such as adding or subtracting 1, we recommend that you do (include-book "arithmetic/top-with-meta" :dir :system) which loads into ACL2 all the rules in one of the so-called ACL2 "community books". (Books are certified files of definitions, lemmas, etc., usually prepared by other ACL2 users and explicitly shared with the community. The ACL2 installation instructions suggest downloading the community books.) The book "top-with-meta" is the most elementary and most widely used arithmetic book. Other community books include "arithmetic-5/top" and various hardware and floating-point arithmetic books. Rules Concluding with Arithmetic Inequalities: If you are tempted to create a rewrite rule with an arithmetic inequality as its conclusion or left-hand side, think again. Inequalities such as (<= (len (delete e x)) (len x)) make poor left-hand sides for rewrite rules. For example, the inequality above does not match the target (<= (LEN (DELETE E X)) (+ 1 (LEN X))) even though it is sufficient to prove the target (given some simple arithmetic). We recommend that if you have a theorem that establishes an arithmetic inequality, you make it a linear rule. See *note LINEAR:: <>. Rearranging Formulas Before Making Rules: It is possible to rearrange the propositional structure of a proved formula before processing it as a rule. This allows you to state a theorem one way "for publication" and rearrange it to be stored as a more effective rule. See *note INTRODUCTION-TO-THE-DATABASE:: (a tutorial topic you'll come to later) and its discussion of the concept of corollary. Also, see the discussion of corollary in rule-classes <>. Rewriting with New Equivalence Relations: You may introduce new equivalence relations, like "set-equal" or "is-a-permutation" and cause the rewriter to replace equivalents by equivalents in suitable contexts, where you use congruence rules to inform ACL2 of where these more relaxed notions of equivalence may be used; see *note EQUIVALENCE:: <> and see *note CONGRUENCE:: <>. Pragmatic Advice to Control Rules: You may attach various pragmas to a rule that allow you rather fine heuristic control over whether and how the rule is applied. For example, you may mark a hypothesis to be forced (see *note FORCE:: <>) meaning that the rule is to be applied even if that hypothesis is not relieved - but if the proof is successful the system will turn its attention to all forced subgoals. You may similarly mark a hypothesis so as to cause a case split, allowing the relief of the hypothesis on one branch and spawning another branch explicitly denying the hypothesis; see *note CASE-SPLIT:: <>. You may add a bogus hypothesis that looks at the intended application of the rule and decides whether to apply the rule or not, performing an arbitrary computation on the syntactic context of the application; see *note SYNTAXP:: <>. By providing a :match-free modifier to the :rewrite rule declaration in your rule-classes, you may tell ACL2 to try all or only the first free variable value it guesses (see *note RULE-CLASSES:: <>). You may provide a bogus hypothesis that computes from the syntactic environment the values to guess for the free variables in a rule; see *note BIND-FREE:: <>. You may mark a term so that the rewriter does not dive into it; see *note HIDE:: <>. Programming Your Own Rewriter: If you cannot find a way to use rewrite rules to make the transformations you desire, you might investigate the use of metafunctions. A metafunction is just a little theorem prover of your own design. It takes as input a list structure representing a term and returns a list structure representing a term. If you can prove that the meaning of the input and output terms are equivalent, you can extend the ACL2 simplifier to call your metafunction. See *note META:: <>. The Order in which Targets are Rewritten: The rewriter sweeps through terms "inside-out" otherwise known as "left-most innermost first". Thus, before trying to apply rewrite rules to (f a1 ... an), rules are applied to the ai. This has the good effect of normalizing the ai. This fact might help you understand why sometimes your rules "don't seem to fire." For example, suppose you have a rule for rewriting (len (rev x)) to (len x) and suppose you wish to prove a theorem about (LEN (REV (CONS A B))). Suppose rev is defined in terms of append, as shown in programming-knowledge-taken-for-granted. Then you might see a checkpoint in which the (LEN (REV ...)) above has been simplified to (LEN (APPEND (REV B) (LIST A))) instead of to (LEN (CONS A B)). Why wasn't your rule about (len (rev x)) applied? The reason is that (REV (CONS A B)) rewrote to (APPEND (REV B) (LIST A)) before rules were applied to (LEN (REV ...)). You need a rule about (len (append x y)), as you will see from the checkpoint. The Order in which Rules are Tried: The rewriter tries the most recently proved rules first. For example, suppose f, g, and h are functions defined so that the following two theorems are provable and suppose you executed the following two events in the order shown: (defthm rule1 (equal (f (g x)) (h 1 x))) (defthm rule2 (equal (f (g x)) (h 2 X))) Then if rewrite rules are applied to (F (G A)), the result will be (H 2 A), because the latter rule, rule2, is applied first. It is generally best not to have conflicting rules or, at least, to understand how such conflicts are resolved. The system will warn you when you propose a rule that conflicts with an existing one. If you were reading this topic as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-rewrite-rules-part-2.  File: acl2-doc-emacs.info, Node: GENERALIZING-KEY-CHECKPOINTS, Next: INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS, Prev: FURTHER-INFORMATION-ON-REWRITING, Up: INTRODUCTION-TO-THE-THEOREM-PROVER GENERALIZING-KEY-CHECKPOINTS getting rid of unnecessary specificity Suppose MEMBER determines whether its first argument is a member of its second, and SUBSETP determines whether every element of its first argument is a member of its second. Suppose that you're trying to prove some Main Theorem and are told the formula below is a key checkpoint. What should you do? Key Checkpoint: (IMPLIES (AND (CONSP A) (INTEGERP (CAR A)) (MEMBER (CAR A) B) (SUBSETP (CDR A) B) (NOT (SUBSETP (CDR A) (APPEND B C)))) (MEMBER (CAR A) C)) The key observation is that the third hypothesis implies the negation of the fourth. Writing it in the contrapositive: (IMPLIES (AND ... (SUBSETP (CDR A) B) ...) (SUBSETP (CDR A) (APPEND B C))) In fact, that is more complicated than it needs to be. A "correct" response to the key checkpoint above is to prove (defthm subsetp-append (implies (subsetp a b) (subsetp a (append b c)))). A still better response is to prove this: (defthm subsetp-append-2 (implies (or (subsetp a b) (subsetp a c)) (subsetp a (append b c)))). This version is better because it handles both of the possibilities regarding whether a is a subset of the arguments of the append. It would be nice if we could think of a "strong" version, one in which (SUBSETP a (APPEND b c)) is rewritten to some clearly simpler term, but nothing comes to mind. In any case, if you cannot see any obvious simplification of the individual terms in the Key Checkpoint, you should ask yourself whether there are connections beween the various propositional parts (as here, with the third and fourth hypotheses). It is a good heuristic to look for relations between parts with the same top-level function symbol (as here, with SUBSETP). It is also a good heuristic to throw out parts of the formula that seem disconnected (as here, with the terms involving (CAR A)). This discussion suggests several "modes" of looking at key checkpoints and the idea of trying the modes in sequence: (1) look for simplifiable combinations of function symbols within propositional components, (2) look for relations between propositional components, and (3) throw out irrelevant or weakly connected components. In all cases you are bringing to bear your intuitions about the semantics of the terms. That is, you're not just slinging symbols. You should be looking out for obvious truths about individual parts of the checkpoints... truths that are obvious to you but not to ACL2! Ultimately the three "modes" are the same and we do not really recommend adhering to the given sequence. You're just looking for simpler theorems that, in combination, imply the checkpoint. Keeping the "modes" in mind may help focus your attention so you consider all the plausible possibilities. After a little experience you'll find yourself looking for all these things simultaneously as part "cleaning up" the checkpoints. When your main goal theorems are harder than these, your main concern will be looking at a Key Checkpoint and asking yourself "why is this true?" But you don't want to do that until you've cleaned it up "locally" as much as possible and sometimes - more often than you might think - local considerations can prove many of your checkpoints. If you have been working your way through the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-key-checkpoints.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS, Next: INTRODUCTION-TO-HINTS, Prev: GENERALIZING-KEY-CHECKPOINTS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS the mechanics of interaction with the theorem prover ACL2 is implemented in Common Lisp. There are many different Common Lisps and they differ in details relating to interacting with the system. We sometimes refer to the host Common Lisp as "raw Lisp." The new user is advised not to operate in raw Lisp as it is possible to, say, redefine ACL2 system faclities like defthm. Most people use Emacs (see *note EMACS:: <>) or the ACL2 Sedan (Eclipse) interface (see *note ACL2-SEDAN:: <>). They provide protection against certain common mistakes, e.g., trying to edit a block of input text after the operating system has buffered it up and sent it to the Lisp reader which is parsing it as you type. More on this below. In addition, the Sedan provides helpful syntax checking and a disciplined approach to the stack of lemmas generated by The Method. But the variety of interfaces to the variety of Lisps mean that there is great variation in what one must type to interact with ACL2. The best example is perhaps trying to interrupt a running proof. If your host Common Lisp is GCL or Allegro and you are typing directly to the Common Lisp process, then you can interrupt a computation by typing "ctrl-c" (hold down the Control key and hit the "c" key once). But other Lisps may require some other control sequence. If you are typing to an Emacs process which is running the GCL or Allegro Common Lisp process in a shell buffer, you should type ctrl-c ctrl-c. If you are running the ACL2 Sedan, you can use the Interrupt Session button on the tool bar. The environment you enter when you interrupt depends on various factors and basically you should endeavor to get back to the top level ACL2 command loop, perhaps by typing some kind of Lisp depenent "abort" command like A or :q, or :abort!. You can usually determine what environment you're in by paying attention to the prompt, which we discuss below. The ACL2 "interactive loop" is called LP (<>) and is generally invoked automatically from your Common Lisp when you start up the ACL2 process. LP is just a special case of an ACL2 function called LD <>, which the user can call from within the ACL2 interactive loop to enter the loop recursively. New users don't have to know this except that it helps explain why some commands have the string "-ld-" in their names! ACL2 presents itself as a "read-eval-print" loop: you're repeatedly prompted for some type-in, which is read, evaluated, and may cause some printing. The prompt tells you something about ACL2's state. In the standard environment, the prompt is ACL2 !> The "ACL2" tells you that the symbols you use in your command are those defined in the standard ACL2 namespace (or, in the jargon of Lisp, the "current package," see *note CURRENT-PACKAGE:: <>). You could create a new namespace (see *note DEFPKG:: <>) and set the current package to it (see *note IN-PACKAGE:: <>). The next part of the prompt above ("!"), the exclamation mark) tells you that before ACL2 evaluates your type-in it will check to see whether guards (<>) are respected, i.e., whether the functions used in your command are being supplied arguments in their "expected domains." If evaluation is allowed by the guards, it proceeds exactly according to the ACL2 axioms; if evaluation is not allowed, an error is signaled. ACL2 event commands check their arguments thoroughly at run-time, regardless of Lisp's notion of "expected domains." If the exclamation mark is missing from the prompt, ACL2 > then evaluation occurs strictly according to the ACL2 axioms, without regard for any declared guards. You can switch between these two prompts by typing ACL2 !>:set-guard-checking nil to turn guard checking off and ACL2 >:set-guard-checking t to turn it on. Try typing (car 7) to each prompt. If there is a "p" in the prompt, ACL2 p!> with or without the exclamation mark: ACL2 p> it means you are in :program (<>) mode rather than :logic (<>) mode. In :program mode, defun just defines Common Lisp programs that you can evaluation but it adds no axioms and you cannot use such defined functions in theorems or invoke defthm. :Program mode is often used to prototype a model. Most commands are just typical parenthesized Lisp expressions, like ACL2 !>(defthm rev-rev (implies (true-listp x) (equal (rev (rev x)) x))) but some are typed as keywords followed by a certain number of arguments. For example, to undo the last event you may type ACL2 !>:u or to undo back through the introduction of rev you may type ACL2 !>:ubt rev The first is equivalent to evaluating (u) and the second is equivalent to evaluating (ubt 'rev). See *note KEYWORD-COMMANDS:: <>. So if you see a sentence like "to turn on the break rewrite facility, execute :brr t," we mean type ACL2 !>:brr t or equivalently ACL2 !>(brr t) If you see a prompt that doesn't look like those above you are probably not typing commands to the standard ACL2 read-eval-print loop! If you've somehow called LD recursively, the prompt "gets deeper," e.g., ACL2 !>> and you can pop out one level with :q <> (for "quit") or pop to the outermost ACL2 loop with :abort! <>. If you are in the outermost call of the ACL2 interactive loop and you type :q, you pop out into raw lisp. The prompt there is generally different from the ACL2 prompt but that is outside our our control and varies from Lisp to Lisp. We have arranged for many (but not all) Lisps to use a raw lisp prompt involving the string "[RAW LISP]". To get back into the ACL2 interactive loop from raw lisp, evaluate (LP). If you see a prompt that looks like an ACL2 prompt but has a number in front of it, e.g., 1 ACL2 > then you're talking to the break rewrite facility (and you are 1 level deep in the example above). Presumably at earlier time in this session you enabled that facility, with :brr t, installed a monitor on some rule, invoked the prover, entered the break, and forgot. Everything you have done (e.g., lemmas you might have proved with defthm) inside that break will be lost when you exit the break. Since the break rewrite facility is "ours" we can tell you how to exit it! To exit our breaks and return to the top-level ACL2 loop, execute :abort!. If you discover you've been working in a brr break, exit, turn off the break facility wih :brr nil, and redo whatever defuns and defthms you did while in that break. Users of the Emacs interface may occasionally type commands directly in the *shell* buffer running ACL2. This can be "dangerous" for two reasons. One is that if you type an event, like a defun or defthm, directly to the shell, it will not be recorded in your "script" buffer and you may forget it in your final script. The other is that if you attempt to edit a multi-line command on any but the most recent line, e.g., to correct the spelling of defthm below after you've typed the "(implies (true-listp x)" you will confuse the Lisp parser because it has already read "(defth rev-rev". ACL2 !>(defth rev-rev (implies (true-listp x) This usually provokes the raw Lisp to enter a low level error break from which you must abort, possibly reenter the ACL2 loop, and re-type the corrected command from scratch. Another common mistake when using interfaces other than the ACL2 Sedan is to type an ill-formed ACL2 expression containing dots or commas, which also often provokes a break into the raw Lisp's error handler. The fundamental lesson is that you should pay attention to the prompt and learn what the different prompts mean - or use the ACL2 Sedan. If you have been working your way through the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-HINTS, Next: INTRODUCTION-TO-KEY-CHECKPOINTS, Prev: INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-HINTS how to provide hints to the theorem prover We assume you've read introduction-to-rewrite-rules-part-1, introduction-to-key-checkpoints, and introduction-to-the-database. You may give the theorem prover a hint that is specific to a particular subgoal generated during a proof attempt. Of course, you learn the name of the subgoal by inspecting the key checkpoints or other proof output. You are not expected to anticipate the need for hints at specific subgoals; instead, you usually deduce that a hint is required because the subgoals is not proved but you see that existing rules and context make it provable. The most common hint is to enable and/or disable a particular rule on some particular subgoal. (defthm name formula :hints (("Subgoal *1/3.2''" :in-theory (disable nth-nthcdr)))) The hint above tells the rewriter that just before it begins to work on Subgoal *1/3.2" it should switch to a local theory in which all of the rules generated from the event nth-nthcdr are disabled. That local theory remains the one in use for all descendent subgoals generated from the one named, until and unless one of those descendent subgoals has an :in-theory hint associated with it. There are many kinds of hints besides :in-theory and in general, after each subgoal name, you can give various forms of advice and list various adjustments you wish to make to the context in which the prover is operating when it begins addressing the subgoal named. The top-level goal is always named Goal. Thus (defthm name formula :hints (("Goal" ...hints1...) ("Subgoal *1/3.2''" ...hints2...))) has the effect of using hints1 for the top-level goal and all of its children throughout the entire proof, except for Subgoal *1/3.2" and its children, where hints2 is used instead. There are a few hints which "take effect" exactly on the subgoal to which they are attached and are not inherited by their descendents. Here is an incomplete list of some of the more common hints; we note the ones that do not pass on their effects to their descendents. We recommend that you not follow the advanced links (marked "<>") below until you have read the entire tutorial. See *note IN-THEORY:: <> for how to enable and/or disable rules. The new theory is computed by a "theory expression" (see *note THEORIES:: <>) such as (disable nth-nthcdr) and typically makes adjustments such as additions or deletions to the global current theory. All the relevant new theories are computed before the proof begins. Thus, in (defthm name formula :hints (("Goal" :in-theory (disable rule1)) ("Subgoal *1/3.2''" (disable rule2)))) the theory mentioned for Goal is the global current theory minus rule1, while the theory mentioned for its descendent, Subgoal *1/3.2", is the global current theory minus rule2. In particular, if both rule1 and rule2 are enabled in the global current theory, then rule1 is enabled during the processing of Subgoal *1/3.2" because it was not removed explicitly there. See *note USE:: <> for how to force the theorem prover to take note of particular instances of particular theorems; in particular, the instances are created and added as hypotheses to the subgoal in question. The hint is not inherited by the descendents of the subgoal (since the formula being proved has been modified by the hint). If the rule you are using (with a :use hint) is an enabled rewrite rule, it might interfere with the added hypothesis - by rewriting itself to T - and thus often you will both :use ... and :in-theory (disable ...). See *note EXPAND:: <> for how to tell the theorem prover to expand one or more function calls whenever encountered. See *note CASES:: <> for how to force the theorem prover to do a case split to prove the subgoal under each of an exhaustive list of cases given in the hint. This hint takes action specifically at the named subgoal and is not passed down to its children. See *note INDUCT:: <> for how to tell the theorem prover to go immediately into induction for the subgoal in question, and to use the induction scheme suggested by the hint rather than the one suggested by the terms in the subgoal itself. This hint is not inherited by its descendents. See *note HINTS:: <> for a complete list of all hints, and see *note HINTS-AND-THE-WATERFALL:: <> for a more thorough description of how the effects of hints at a subgoal are inherited by the descendents. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-KEY-CHECKPOINTS, Next: INTRODUCTION-TO-REWRITE-RULES-PART-1, Prev: INTRODUCTION-TO-HINTS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-KEY-CHECKPOINTS What questions to ask at key checkpoints We assume you've read about rewrite rules; see *note INTRODUCTION-TO-REWRITE-RULES-PART-1::. When a proof attempt fails, ACL2 prints some key checkpoints. These are formulas that we think you should look at. There are two kinds printed: key checkpoints before an induction, and key checkpoints under a top-level induction. (Key checkpoints under deeper inductions and checkpoints that aren't considered "key" may exist in the proof attempt, but ACL2 doesn't print them at the end of failed proofs because you shouldn't be distracted by them.) Below is a list of questions to ask yourself about the key checkpoints. Initially, we recommend just picking one key checkpoint before an induction (perhaps the simplest looking one) and asking these questions. These questions may lead you to look at other key checkpoints. As you gain more experience you'll elaborate and generalize this advice. (1) Do you believe this formula is a theorem? If you don't think it is, it's pointless to try to prove it! You should reconsider your top-level formula in light of the special case suggested by this key checkpoint. (2) Can it be simplified? Is there some combination of function symbols in it that could be eliminated or simplified by exploiting some simpler fact? By a "simpler fact" we mean a theorem about a few of the symbols in this formula. For an example of this see *note DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS::. Don't think about the deep question "how can I prove the checkpoint?" until you've got it into its simplest form. (3) Is the simpler fact already in the database? If there is some simpler fact that would help clean up the checkpoint but you believe the simpler fact is already in the database, you can use :pl <>, :pc <>, :pbt <>, and other history commands to inspect the database; (see *note HISTORY:: <>). But if you find the allegedly relevant simpler fact in the database, you must ask: why wasn't it used? There are four principal reasons: (3a) it is disabled - so enable it; you'll learn how when you read the coming sections on introduction-to-the-database and introduction-to-hints. (3b) its left-hand side doesn't match the target - so improve the rule by generalizing its left-hand side or prove a new rule for this situation; if you decide to remove the old rule from the database, see undo commands in history <>. (3c) it is an IFF rule but the target doesn't occur propositionally - so see if you you can strengthen the rule to an EQUAL rule or weaken the context of the target by changing the conjecture to use the target propositionally; if you decide to remove the old rule from the database, see undo commands in history <>. (3d) the hypotheses of the rule cannot be relieved for this occurrence of the target; this can be caused by the rule's hypotheses being too strong (requiring more than they should), or by the hypotheses of the current conjecture being too weak (you forgot some key hypothesis), or by ACL2 not having the rules it needs to prove that the conjecture's hypotheses really do imply the rule's. Tools are available (:see *note BRR:: <>) help you figure out why the rule failed, so use them and improve the rule, or the current conjecture, or the database as appropriate. (4) If the simpler fact is not already known, prove it. This means you must create a new defthm event with appropriate rule-classes to store your new theorem so that it will be used. See *note DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS::. Then you must start using The Method recursively to prove your new lemma. (5) Otherwise, is this formula something you'd prove by induction? If you can't simplify it, it may be because you "need this fact to prove this fact," in which case, induction is the right thing to do. But first, remember that in order for a formulas to be provable by induction, it must be very general. Why must it be general? Because in an inductive proof, the main thing you have to work with is the induction hypothesis, which is an instance of the theorem you're proving. If the theorem is not general enough, you won't be able to assume an instance that will help you. ACL2 may try induction even on formulas that are not general enough. Don't assume that the formula is ripe for induction just because ACL2 found an induction to do! Before you "approve" a formula for induction, ask whether it is perhaps a special case of some more general theorem. See *note GENERALIZING-KEY-CHECKPOINTS:: now and then come back here. If you found a generalization, you should probably be proving that formula instead of this one. So formulate the appropriate defthm and use The Method recursively to prove it. (6) If the formula is right for induction, did ACL2 do an induction for it? You can answer that without looking at the proof. Just see if there are any key checkpoints after induction. If not, why didn't ACL2 induct? Perhaps you told ACL2 not to induct! Perhaps no term in the conjecture suggests an appropriate induction? You could remedy this by extending ACL2's induction analysis by adding to the database. Or you could just tell ACL2 what induction to do for this formula. You'll learn about both later (when you read coming sections of the tutorial). (7) If ACL2 did do an induction, was it the right one? You can find the induction scheme used by reading the first induction message in the output log after you submitted the conjecture to ACL2. But most often you will realize the "wrong" induction was done just by looking at the post-induction key checkpoints, keeping in mind that each is supposed to be a natural special case of the theorem you're proving. Is the case analysis inappropriate? Are induction hypotheses missing? If so, you should look at the induction scheme. If you determine the wrong induction was done, extend ACL2's induction analysis or tell it which induction to do, which you'll learn about in the coming sections of the tutorial. For more advice about looking at post-induction key checkpoints, see *note POST-INDUCTION-KEY-CHECKPOINTS:: now and then come back here. (8) If the post-induction key checkpoints seems plausible, then repeat the questions above for each one of them, perhaps starting with the simplest. In any case, after successfully taking whatever action you've decided on, e.g., proving some new lemma and adding it as a rule: Start over trying to prove your main conjecture. This is important! Do not just scroll back to the key checkpoints generated the last time you tried to prove it. Instead, re-generate them in the context of your new, improved database and hints. You will be following this general outline almost all of the time that you're interacting with ACL2. You will not often be asking "Why is ACL2 making me think about this subgoal? What did ACL2 do to get here? How does ACL2 work?" Two other ideas are helpful to keep in mind. Is a key checkpoint unexpectedly complicated? Pay special attention to the case where the complication seems to be the introduction of low-level details you thought you'd dealt with or by the introduction of symbols you didn't expect to see in this proof. These can be signs that you ought to disable some rules in the database (e.g., a definition of a complicated function about which you've proved all the necessary lemmas or some lemma that transforms the problem as was appropriate for some other proof). Does the theorem prover just hang up, printing nothing? If this happens, you must interrupt it. How you interrupt the prover is dependent on which Common Lisp and which interface you're using. But most Common Lisps treat control-c as a console interrupt. If you're in Emacs running ACL2 as a shell process, you must type control-c control-c. If you're in ACL2s, hit the Interrupt Session button. Interrupting ACL2 can leave you in an interactive loop similar in appearance but different from ACL2's top-level! So pay careful attention to the prompt and see *note BREAKS:: <>. Once you've regained control from the "runaway" theorem prover, there are several tools you can use to find out what it is doing in real-time. Generally speaking, when the theorem prover goes silent for a very long time it is either in some kind of rewrite loop caused by rules that cause it to flip back and forth between various supposedly normal forms, or else it has split the problem into a huge number of cases and suffering a combinatoric explosion. See *note DMR:: <> and, perhaps, see *note ACCUMULATED-PERSISTENCE:: <>. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-REWRITE-RULES-PART-1, Next: INTRODUCTION-TO-REWRITE-RULES-PART-2, Prev: INTRODUCTION-TO-KEY-CHECKPOINTS, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-REWRITE-RULES-PART-1 introduction to ACL2's notion of rewrite rules Rewrite rules make ACL2 replace one term by another. This is done by the rewriter, which is part of ACL2's simplifier. The rewriter sweeps through the goal formula trying all the rewrite rules it knows. Here's an example. Just pretend that you have made a rewrite rule from the formula below. (implies (and (natp i) (< i (len a))) (equal (put i v (append a b)) (append (put i v a) b))) Then every time the rewriter sees a target term that matches (put i v (append a b)) it considers the rule, with the variables i, v, a, and b of the rule bound to whatever terms matched them in the target, say the terms i, v, a, and b. To consider the rule, the rewriter first tries to establish ("relieve") the hypotheses. In particular, it rewrites: (natp i) ; hyp 1 and (< i (len a)). ; hyp 2 If both hyptheses rewrite to true, then the rule fires and replaces the target by: (append (put i v a) b). In short, rewrite rules direct ACL2 to rearrange the terms in the goal formulas. We are more precise later, but for now we turn to the question of how do you make a rewrite rule from a formula? The answer is, you prove the formula with the defthm command. Recall that (defthm name formula ...) commands ACL2 to try to prove formula and, if successful, build it into the database as a rule according to your specification in the rule-classes argument of the ... part of the command. To make it easy for you to generate rewrite rules, defthm has a simple heuristic: if you don't tell it what kind of rule to generate from formula, it generates a rewrite rule! Thus, if this command (defthm name formula) is successful, ACL2 will have a new rewrite rule in the database, even though you did not explicitly tell it to add a rule. A common mistake for the new users is to forget that the above command adds a rewrite rule. This often results in a tangle of rules that lead nowhere or to infinite rewriting that you will have to interrupt. It is also good to remember that the command only adds a rule. It does not magically make ACL2 aware of all the mathematical consequences of the formula: it just makes a rewrite rule. When you prove a theorem with defthm you are programming ACL2. Being careless in your statement of lemmas is tantamount to being careless in your programming. ACL2 can generate rewrite rules from formulas that look like this: (IMPLIES (AND hyp1 ... hypk) (eqv lhs rhs)) where eqv is either EQUAL or IFF, and lhs is not a variable symbol, not a constant, and not a call of the function IF, and not a call of an abbreviation ("macro") that expands to any of these. So illegal lhs include X, 0, (IF X Y Y), and (OR p q). The last is illegal because OR is just an abbreviation for a certain IF-expression. Technical Note: This tutorial introduction to the theorem prover takes liberties with the truth! We are trying to give you a useful predictive model of the system without burdening you with all the details, which are discussed in the reference manual. For example, using directives in the rule-classes you can rearrange the proved formula into the form you want your rule to take, and you can make ACL2 take advantage of equivalence relations eqv other than just EQUAL and IFF. But we'll ignore these fine points for now. We call the hyp terms the hypotheses of the rule. We call lhs the left-hand side of the rule, and we call rhs the right-hand side of the rule. If the conclusion of the rule is an EQUAL term we call it an equality rule. Otherwise, it is a propositional equivalence rule. If there are no hypotheses, k=0, we say the rule is an unconditional rewrite rule; otherwise it is conditional. ACL2 allows several special cases of the shapes above. See *note SPECIAL-CASES-FOR-REWRITE-RULES::, but come back here and continue. A rewrite rule makes ACL2 seek out occurrences of terms that match the left-hand side of the rule and replace those occurrences using the right-hand side, provided all the hypotheses rewrite to true in the context of the application of the rule. That is, the left-hand side is treated as a pattern that triggers the rule. The hypotheses are conditions that have to be proved in order for the rule to fire. The right-hand side is the replacement and is put into the formula where the pattern occurred. Now for some clarifications. ACL2 only considers enabled rules. And ACL2 will use a propositional rule to replace a target only if the target occurs in a propositional place in the formula. Generally that means it occurs in the argument of a propositional connective, like AND, OR, NOT, IMPLIES, and IFF, or in the test of an IF. When we say that the left-hand side of the rule must match the target we mean that we can instantiate the variables in the rule to make the left-hand side identical to the target. To relieve or establish the hypotheses of the rule, ACL2 just applies other rewrite rules to try to prove the instantiated hypotheses from the assumptions governing the occurrence of the target. When ACL2 replaces the target, it replaces it with the instantiated right-hand side of the rule and then applies rewrite rules to that. If a hypothesis has variables that do not occur in the left-hand side of the rule, then the pattern matching process won't find values for those variables. We call those free variables. They must be instantiated before ACL2 can relieve that hypothesis. To instantiate them, ACL2 has to guess values that would make the hypothesis true in this context, i.e., true given the assumptions of the goal theorem. So if you're trying to prove (IMPLIES (AND (TRUE-LISTP A) (MEMBER (CAR P) A) (MEMBER (CDR P) A)) ...) and the target you're rewriting is in the "..." part of the formula, the rewriter knows that (TRUE-LISTP A) (MEMBER (CAR P) A) and (MEMBER (CDR P) A) are true. So if a rewrite rule is considered and the rule has (member e x) as a hypothesis, where e is a free variable but x was bound to A in the pattern matching, then it will guess that e must be (CAR P) or (CDR P), even though there are many other possibilities that would make (MEMBER e A) true. Of course, whatever guess it makes must also satisfy all the other hypotheses that mention e in the rule. It simply isn't very imaginative at guessing! The most predictable rewrite rules have no free variables. You can add pragmatic advice to help ACL2 with free variables, telling it to try all the possibilities it finds, to try just the first, or even to compute a "creative" guess. It is possible to make the rewriting process loop forever, e.g., by rewriting alpha to beta with one set of rules and rewriting beta to alpha with another. Even a single rule can make the process loop; we'll show you an example of that later in the tutorial. ACL2 can handle commutativity rules without looping. It uses (equal (+ x y) (+ y x)) to replace (+ B A) by (+ A B), but not vice versa. (It is sensitive to alphabetic ordering when dealing with permutative rules.) Logically equivalent formulas can generate radically different rewrite rules! Rearranging the propositional structure of the formula or swapping the left and right sides of an equality - while having no effect on the mathematical meaning of a formula - can have a drastic impact on the pragmatic meaning as a rule. To see an illustration of this, see *note EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES::. Developing an effective set of rewrite rules is key to success at using ACL2. We'll look more at this later in the tutorial. If you are working your way through the tutorial for the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover. If you are reading just about how to make effective rewrite rules, go on to introduction-to-rewrite-rules-part-2.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-REWRITE-RULES-PART-2, Next: INTRODUCTION-TO-THE-DATABASE, Prev: INTRODUCTION-TO-REWRITE-RULES-PART-1, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-REWRITE-RULES-PART-2 how to arrange rewrite rules You should design your rewrite rules to canonicalize the terms in your problem, that is, your rules should drive terms into some normal form so that different but equivalent terms are rewritten into the preferred shape, making equivalent terms identical. You are very familiar with this idea from algebra, where you learned to normalize polynomials. Thus, when you see (2x + 6)(3x - 9) you automaticaly normalize it, by "multiplying out and collecting like terms," to get (6x^2 - 54). This normalization strategy allows you to recognize equivalent terms presented differently, such as 6(x^2 - 9). The ACL2 user is responsible for making up the rules. (Standard "books" - files of ACL2 definitions and theorems - can often provide rules for some sets of functions, e.g., arithmetic.) This is a heavy burden on you but it means you are in charge of your own normal forms. For example, if you use the function nthcdr, which returns the nth cdr of a list, you might see both (cdr (nthcdr i x)) and (nthcdr i (cdr x)). These two expressions are equivalent but not identical. You will want to decide which you want to see and prove the rewrite rule that converts the other to that preferred form. Most good users develop an implicit ordering on terms and rewrite "heavy" terms to "lighter" ones. This insures that there are no loops in their rewrite rules. But this ordering changes according to the user and the problem. Generally, the lightest terms are primitives such as IF, EQUAL, arithmetic, etc. Functions defined without explicit recursion tend to be ignored because they are just expanded away (but see below). Recursively defined functions tend to be heavier than any other recursive function used in their definitions, so, for example, if rev is defined in terms of append, rev is heavier than append. But the size and subtlety of recursively defined functions also affects their place in the ordering. But rewrite rules are surprisingly subtle. Recall that a rewrite rule can be made from a formula of this form: (IMPLIES (AND hyp1 ... hypk) (eqv lhs rhs)) where eqv is either EQUAL or IFF, and lhs is a call of a function other than IF. In such a rule, lhs is the pattern responsible for triggering the rule, the hypi are conditions which must be satisfied by the context of the target being rewritten, and rhs is the replacement. The replacement only happens if the rule is enabled, the pattern matches, the conditions are satisfied, and (in the case of an IFF rule) the target occurs propositionally. There are other heuristic restrictions that we won't discuss here. So how should you phrase a theorem in order to make it an effective rule? General Principles: * Strengthen the Formula: The fewer hypotheses a formula has the better the rewrite rule formed from it. The more general the left-hand side the better the rule. The fewer free variables in the hypothesis, the better. The idea is to form a rule that fires predictably. Later in this tutorial you'll get some practice formulating strong rules. * Choosing the Conclusion: If a lemma is an implication, you have to choose what the conclusion will be. (You'll also have to "orient" that conclusion by choosing a left-hand side and a right-hand side, but we discuss that below). You can swap the conclusion and any hypothesis by negating both, producing a different conclusion. There are generally two (possibly conflicting) heuristics for deciding which part of the formula should be the conclusion: Choosing the Conclusion Heuristic 1: Can you make the conclusion be an EQUAL or IFF expression that has a "heavy term" on one side? That will make a rule that replaces the heavy term with a lighter one. We discuss this situation more below. Choosing the Conclusion Heuristic 2: Can you make the conclusion be a non-propositional term that contains all the variables mentioned in the hypotheses? By "non-propositional" we mean a term that is not just the propositional combination (e.g., with AND or OR) of other terms but instead some call of some "heavy" function? If your conclusion contains all the variables mentioned in the hypotheses, matching it will instantiate all the variables in the hypotheses. That way ACL2 will not have to guess instantiations of unbound variables when it tries to relieve the hypotheses. It is not very good at guessing. * Orienting the Conclusion: If the conclusion is an EQUAL or an IFF, you have to decide which is the left-hand side and which is the right. If the conclusion is (NOT lhs), then the left-hand side is lhs and the right-hand side is NIL. If the conclusion is not an EQUAL, an IFF, or a NOT then the conclusion itself will be the left-hand side and the right-hand side will be T. If your lemma was created by looking a Key Checkpoints while using The Method, the left-hand side should match some term in that checkpoint. Remember, the left-hand side is the "trigger" that will make the rule fire. It is the pattern that ACL2 will be looking for. * Pay attention to the free variables: Look at the variables that occur in the pattern (the left-hand side) and compare them to the variables that occur in the hypotheses. Does some hypothesis contain a variable, say v, that is not in the pattern? We call v a free variable because it will not be assigned a value ("bound") by the process of pattern matching. ACL2 will have to guess a value for v. If some hypothesis contains v as a free variable, ask whether more than one hypothesis contains v? ACL2 uses the first hypothesis containing a free v to guide its guess for v. To "guess" a value for v, ACL2 uses that hypothesis as a pattern and tries to match it against the assumptions in the checkpoint formula being proved. This means that key hypothesis must be in normal form, to match the rewritten assumptions of the goal. It also means that you should reorder the hypotheses to put the most unusual hypothesis containing a free v first in the list of conjuncts. For example, if v is free in two hypotheses, (natp v) and (member (nthcdr v a) b), then we recommend putting the member term first. There are likely to be many terms in the goal satisfying the natp hypothesis - or none if natp has expanded to an integer inequality - while there are likely to be few terms satisfying the member hypothesis, especially if a and b are bound by the left-hand side of the rule. Here are some (possibly conflicting) heuristics for choosing the left-hand side: Choose a Left-Hand Side that Occurs in a Key Checkpoint: If you use the Method you will tend to do this anyway, because you'll see terms in the Key Checkpoints that you want to get rid of. But many moderately experienced users "look ahead" to how the proof will go and formulate a few anticipatory rules with the idea of guiding ACL2 down the preferred path to the proof. When you do that, you risk choosing left-hand sides that won't actually arise in the problem. So when you formulate anticipatory rules, pay special attention to the functions and terms you put in the left-hand sides. The next few paragraphs deal with specific cases. Avoid Non-Recursive Functions in the Left-Hand Side: If the left-hand side contains a call of a defined function whose definition is not recursive, then it will almost never match any target in the formula being rewritten unless the function is disabled. Suppose for example you have defined SQ so that (SQ x) is (* x x). Suppose you considered choosing a left-hand side like (+ (SQ x) (SQ y)). Suppose you hoped it would hit the target (+ (SQ A) (SQ B)) in some formula. But when ACL2 simplifies the formula, it will first rewrite that target to (+ (* A A) (* B B)) by expanding the definition of SQ, since it could do so without introducing any recursive calls. But now the target won't match your rule. By choosing a left-hand side that occurs in a Key Checkpoint (and is not one of a handful of abbreviations ACL2 uses in its output like AND, NOT), you'll avoid this problem since SQ will have already been expanded before the Key Checkpoint is printed. Disable Non-Recursive Functions: If you insist on a left-hand side that contains calls of non-recursive functions, remember to disable those non-recursive functions after you've proved all the rules you want about them. By disabling SQ you can prevent ACL2 from expanding the definition as it did above. Sometimes you will define a function non-recursively to formalize some concept that is common in your application and you will want to create a sort of algebra of rules about the function. By all means do so, so you can conduct your formal reasoning in terms of the concepts you're informally manipulating. But after proving the required laws, disable the non-recursive concept so that ACL2 just uses your laws and not the messy definition. Choose a Left-Hand Side Already in Simplest Form: This is a generalization of the advice above. If any rule will rewrite your left-hand side, it will prevent your rule from matching any target. For example, if you write a left-hand side like (foo (car (cons x y))) then it would never match any target! The reason is that even if (FOO (CAR (CONS A B))) did occur in some goal formula, before ACL2 would try your rule about foo it will use the obvious rule about CAR and CONS to transform your imagined target to (FOO A). Thus, your rule would not match. So you have to keep in mind all your other rules when you choose a left-hand side (and when you choose the hypotheses to guide free variable selection). If you always choose a pattern that matches a term in a Key Checkpoint, you avoid this problem. Make Sure the Left-Hand Side is "Heavier" than the Right: Sometimes this is obvious, as when you choose (REV (REV x)) for the left-hand side and x for the right. But what do you about (REV (APPEND x y)) versus (APPEND (REV y) (REV x))? Most of the time we choose to drive the heaviest function (in this case REV) down toward the variables, lifting the lighter function (APPEND) up so that we can reason about the lighter function's interaction with the surrounding "matrix" of the formula. So we'd rewrite (REV (APPEND x y)) to (APPEND (REV y) (REV x)), not vice versa. Alternative Ways to Talk About the Same Thing: If your problem and specification use two different ways to talk about the same thing, choose one form and rewrite the other into that form. For example, the ACL2 built-in nth returns the nth element of a list, and the built-in function nthcdr returns the nth cdr of a list. They are defined independently. But (nth n x) is the same thing as (car (nthcdr n x)). Since nth can be expressed in terms of nthcdr but not vice versa, it is clear we should prove (equal (nth n x) (car (nthcdr n x))) as a rewrite rule if both nth and nthcdr are involved in the problem. Don't Let Computational Efficiency Dictate the Terms: If you have two functions that are equivalent (perhaps one was defined to be computationally more efficient), prove their equivalence as a rewrite rule that eliminates the more complicated function. An extreme example would be a model that uses a sophisticated data structure (like a balanced binary tree, red-black tree, ordered array, or hash table) to implement something simple like an association of keys to values. By proving the equivalence as stated you can eliminate the messy function early and do the bulk of your reasoning in terms of its simple specification. The best ACL2 users become very good at keeping all these things in mind when designing their rewrite rules. Practice makes perfect. Don't be afraid during your learning of ACL2 to undo the rules you first invented and try to make better ones. Finally, be patient! There will be times when you think to yourself "Why should I spend my time thinking of rules that guide ACL2? I know the proof!" There are two reasons. First, you may "know" the proof but you may well be wrong and part-way through this whole exercise you may realize that you're missing a major hypothesis or special case that breaks your whole conception of the problem. The proof is in the details. Second, most of the time the library of rules you develop in this process will be used over and over again on variants of the main problem in the months and years ahead. This is sometimes called the proof maintenance problem. Theorems don't suffer bit rot! But the artifacts you're modeling change and you will need to prove new versions of old theorems. A good general purpose library makes this much easier. We now recommend that you practice inventing strong rules; see *note STRONG-REWRITE-RULES::. For advice on handling specific kinds of formulas and definitions, see *note SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES::. For more information about the rewriter works and how rules are created, see *note FURTHER-INFORMATION-ON-REWRITING::. If you are working your way through the tutorial introduction to the theorem prover, use your browser's Back Button to return to introduction-to-the-theorem-prover.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-THE-DATABASE, Next: INTRODUCTORY-CHALLENGE-PROBLEM-1, Prev: INTRODUCTION-TO-REWRITE-RULES-PART-2, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTION-TO-THE-DATABASE how to update the database We assume you've read introduction-to-rewrite-rules-part-1 and introduction-to-key-checkpoints. The theorem prover's heuristics are influenced by the database of rules and the enabled/disabled status of the rules. You can think of the database as a global hint, potentially affecting all parts of a proof attempt. However, in addition to the "global hint," it is possible to give local hints that affect the theorem prover's behavior on specific subgoals. We discuss the database here and discuss local hints later in the tutorial. The theorem prover's "database" is called the ACL2 world. You change the world by issuing commands called events. The most common events are defun for defining new functions (and predicates) and defthm for proving new theorems. Both add rules to the database. Here are some commonly used events. We recommend that upon the first reading of this tutorial you do not follow the links shown below! The links take you into the hypertext reference manual, where it is easy to get lost unless you're looking for detail about one specific feature. See *note DEFUN:: <> to define a new function or predicate symbol. Definitional axioms are just a kind of rewrite rule, but defun may also add rules affecting the determination of the type of a term and rules affecting the induction analysis. When you issue a defun command you will always supply the name of the function or predicate, the list of formal parameters, v1,...vn, and the body: (defun name (v1 ... vn) body) If the event is accepted, a definitional axiom is added to the world, (name v1...vn)=body, stored as a special kind of unconditional rewrite rule. However, the defun event may require theorems to be proved. Specifically, measure theorems must be proved to establish that recursively defined functions always terminate, by proving that an ordinal measure of the formal parameters decreases in a well-founded way in every recursive call. In addition, if guards are being used to declare the expected domain of the newly defined function, guard theorems might be proved to establish that all functions stay within their expected domains. In any case, you may provide additional information to the defun event, coded as part of the declaration that Common Lisp allows: (defun name (v1 ... vn) (declare (xargs ...)) body) The xargs ("extra arguments to defun") entry may specify, among other things, the measure to use in the termination proof, hints for use by the prover during the termination proof, the guard of the new function, and hints for use by the prover during the guard verification step. See *note DEFTHM:: <> to prove a theorem and to add it as a rule of one or more specified rule-classes. When you issue a defthm command you always specify a name for the theorem you're trying to prove and a formula stating the theorem. You may optionally supply some local hints as we describe later in the tutorial. You may also optionally supply some rule classes indicating how you want your formula stored as a rule, after it is proved. We discuss the defthm rule classes below. See *note IN-THEORY:: <> to enable or disable rules. Rules have names derived from the names you give to functions and theorems, e.g., (:REWRITE LEFT-IDENTITY-OF-FOO . 2) for the second rewrite rule you created from the theorem named LEFT-IDENTITY-OF-FOO. Rule names are called runes. A theory is just a set (list) of runes. The current theory is the list of enabled runes and the in-theory event can add runes to or delete runes from the current theory. See *note INCLUDE-BOOK:: <> to change the world by loading a certified file of other events. The most common use of include-book is to load "community books" - books written by other ACL2 users who have released them for distribution to the community. The most common books loaded are probably the arithmetic books: ; * for the most elementary arithmetic, needed for any problem ; that involves even simple addition and multiplication like ; (+ x (* 2 y) -3): (include-book "arithmetic/top-with-meta" :dir :system) ; * for more complicated arithmetic involving non-linear terms like ; (* x y), (expt x (+ i j)), and floor and mod (include-book "arithmetic-5/top" :dir :system) But for a complete list of system books, see *note BOOKS:: <>. See *note CERTIFY-BOOK:: <> to certify a file of events for reuse later. See *note DEFCONST:: <> to define a new constant, allowing you to write a symbol, e.g., *weekdays* in place of some object, e.g., '(MON TUE WED THU FRI) in formulas. See *note DEFMACRO:: <> to define a new syntactic abbreviation. The macro facility in Lisp is quite powerful, allowing you to compute the form to which some type-in expands. For example, the primitive macro COND is defined so that (COND ((P X) 1)((Q X) 2)(T 3)) expands to (IF (P X) 1 (IF (Q X) 2 3)). See *note DEFSTOBJ:: <> to introduce a single-threaded object that your functions may modify "destructively" provided they follow strict syntactic rules. See *note EVENTS:: <> for a complete list of the ACL2 events. There are events to allow mutually recursive definitions, to introduce some new function symbols constrained to satisfy given axioms, to allow the temporary introduction of a "local" event to help prove some goal theorem and then disappear, to provide the power of first-order quantification and a choice operator, and many other features. There are also commands that allow you to inspect the world, e.g., to print the command that introduced a given name, to show all the commands back to a certain one, undo the last command or more generally roll-back to an earlier command. See *note HISTORY:: <>. The Defthm Rule-Classes We've already discussed the key role that rules play in controlling the behavior of the system. New rules are introduced primiarily with the defthm event, though defun and other events may introduce rules. To prove formula and generate, say a :rewrite rule and a :generalize rule from it, you would write (defthm name formula :rule-classes (:rewrite :generalize)) If you wanted to rearrange the shape of the formula before generating the :rewrite rule you could provide a :corollary modifier to the :rewrite rule class: (defthm name formula :rule-classes ((:rewrite :corollary ...) :generalize)). There are many classes of rules, affecting different parts of the system. Each class is denoted by a keyword, e.g., :REWRITE, :LINEAR, etc. You are responsible for specifying the class(es) of rules to be generated from a given formula and several different rules (possibly of different classes) may be derived from a single formula. Each class admits optional modifiers that allow you finer control over each rule. Each class admits the :corollary modifier with which you can rearrange the formula before a rule of that class is generated. This allows you to state a theorem in its most elegant form for publication purposes but store it as a rule with the most appropriate hypotheses and conclusion. Other modifiers tend to be specific to certain rule classes, but for example, :rewrite rule modifiers include an optional limit on the depth of backchaining and options for handling free variables. We give some links below to other classes of rules. However, we recommend that you not follow these links upon your first reading of this tutorial! See *note REWRITE:: <> for a description of how to create a rewrite rule. See *note LINEAR:: <> for a description of how to store theorems concluding with arithmetic inequalities. The trouble with storing (<= (len (delete e x)) (len x)) as a rewrite rule is that it only matches instances of that inequality and thus fails to match (<= (LEN (DELETE E X)) (+ 1 (LEN X))) ACL2 contains an extensible linear arithmetic decision procedure and by storing inequalities as :linear rules you can make that decision procedure aware of the basic inequalities between non-primitive numerically valued terms. See *note EQUIVALENCE:: <>, see *note CONGRUENCE:: <>, and see *note REFINEMENT:: <> to learn how to introduce a new equivalence relation to the rewriter. For example, suppose you define set-equal so that it returns t precisely if its two arguments are lists containing the same elements, regardless of order or number of occurrences. Note that under this sense of "equivalence", (rev x) is the identity function and append is commutative, for example. (set-equal (rev x) x) (set-equal (append x y) (append y x)) You can make ACL2 use these two theorems as :rewrite rules to replace instances of (REV x) and (APPEND x y) by set-equal terms, even though the results are not actually EQUAL. This is possible provided the target occurs in a context admitting set-equal as a congruence relation. For example, the :congruence rule: (implies (set-equal a b) (iff (member e a) (member e b))) gives the rewriter permission to use the above set-equal rules as rewrite rules in the second argument of any member expression being used in a propositional way. See *note ELIM:: <> for a description of how to make the system adopt a "change of variable" tactic that can trade in destructor functions for constructor functions. In analogy with how ACL2 eliminates (CAR X) and (CDR X) by replacing X with (CONS A B), you can make it eliminate other destructors. For example, the community book "arithmetic-5/top" provides an elim rule that eliminates (floor x y) and (mod x y) by replacing x by (+ r (* y q)), so that the floor expression becomes q and the mod expression becomes r. When introducing your own elim rules you will probably also need to introduce generalize rules (see below) so that the new variables are appropriately constrained. See *note GENERALIZE:: <> for a description of how you can make ACL2 restrict the new variables it introduces when generalizing. ACL2 will sometimes replace a term by a new variable and with generalize rules you can insure that the new variable symbol has certain properties of the term it replaces. See *note INDUCTION:: <> for a description of how to tailor the inductions suggested by a term. Most of the time when ACL2 chooses the "wrong" induction, the easiest fix is with a local :induct hint (see below). But if the same problem arises repeatedly in several theorems, you might want to "educate" ACL2's induction heuristic. For a complete list of rule-classes, See *note RULE-CLASSES:: <>. If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-1, Next: INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER, Prev: INTRODUCTION-TO-THE-DATABASE, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-1 challenge problem 1 for the new user of ACL2 Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1 which will undo everything since the first user event. Then define this function: (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) Then use The Method to prove: (defthm triple-rev (equal (rev (rev (rev x))) (rev x))) When you've solved this problem, compare your answer to ours; see *note INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER::. Then, use your browser's Back Button to return to introductory-challenges.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER, Next: INTRODUCTORY-CHALLENGE-PROBLEM-2, Prev: INTRODUCTORY-CHALLENGE-PROBLEM-1, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER answer to challenge problem 1 for the new user of ACL2 This answer is in the form of an ACL2 script sufficient to lead ACL2 to a proof. (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) ; Trying triple-rev at this point produces a key checkpoint containing ; (REV (APPEND (REV (CDR X)) (LIST (CAR X)))), which suggests: (defthm rev-append (equal (rev (append a b)) (append (rev b) (rev a)))) ; And now triple-rev succeeds. (defthm triple-rev (equal (rev (rev (rev x))) (rev x))) ; An alternative, and more elegant, solution is to prove the rev-rev ; instead of rev-append: ; (defthm rev-rev ; (implies (true-listp x) ; (equal (rev (rev x)) x))) ; Rev-rev is also discoverable by The Method because it is ; suggested by the statement of triple-rev itself: rev-rev ; simplifies a simpler composition of the functions in triple-rev. ; Both solutions produce lemmas likely to be of use in future proofs ; about rev. Use your browser's Back Button now to return to introductory-challenge-problem-1.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-2, Next: INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER, Prev: INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-2 challenge problem 2 for the new user of ACL2 Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1. Use The Method to prove (defthm subsetp-reflexive (subsetp x x)) When you've solved this problem, compare your answer to ours; see *note INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER::. Then, use your browser's Back Button to return to introductory-challenges.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER, Next: INTRODUCTORY-CHALLENGE-PROBLEM-3, Prev: INTRODUCTORY-CHALLENGE-PROBLEM-2, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER answer to challenge problem 2 for the new user of ACL2 This answer is in the form of a script sufficient to lead ACL2 to a proof. ; Trying subsetp-reflexive at this point produces the key checkpoint: ; (IMPLIES (AND (CONSP X) ; (SUBSETP (CDR X) (CDR X))) ; (SUBSETP (CDR X) X)) ; which suggests the generalization: (defthm subsetp-cdr (implies (subsetp a (cdr b)) (subsetp a b))) ; And now subsetp-reflexive succeeds. (defthm subsetp-reflexive (subsetp x x)) ; A weaker version of the lemma, namely the one in which we ; add the hypothesis that b is a cons, is also sufficient. ; (defthm subsetp-cdr-weak ; (implies (and (consp b) ; (subsetp a (cdr b))) ; (subsetp a b))) ; But the (consp b) hypothesis is not really necessary in ; ACL2's type-free logic because (cdr b) is nil if b is ; not a cons. For the reasons explained in the tutorial, we ; prefer the strong version. Use your browser's Back Button now to return to introductory-challenge-problem-2.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-3, Next: INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER, Prev: INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-3 challenge problem 3 for the new user of ACL2 Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1. Define the following functions and use The Method to prove the theorem at the bottom: (defun rev (x) (if (endp x) nil (append (rev (cdr x)) (list (car x))))) (defun dupsp (x) ; does x contain duplicate elements? (if (endp x) nil (if (member (car x) (cdr x)) t (dupsp (cdr x))))) (defthm dupsp-rev (equal (dupsp (rev x)) (dupsp x))) When you've solved this problem, compare your answer to ours; see *note INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER::. Then, use your browser's Back Button to return to introductory-challenges.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER, Next: INTRODUCTORY-CHALLENGE-PROBLEM-4, Prev: INTRODUCTORY-CHALLENGE-PROBLEM-3, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER answer to challenge problem 3 for the new user of ACL2 This answer is in the form of a script sufficient to lead ACL2 to a proof. ; Trying dupsp-rev at this point produces the key checkpoint: ; (IMPLIES (AND (CONSP X) ; (NOT (MEMBER (CAR X) (CDR X))) ; (EQUAL (DUPSP (REV (CDR X))) ; (DUPSP (CDR X)))) ; (EQUAL (DUPSP (APPEND (REV (CDR X)) (LIST (CAR X)))) ; (DUPSP (CDR X)))) ; which suggests the lemma ; (defthm dupsp-append ; (implies (not (member e x)) ; (equal (dupsp (append x (list e))) ; (dupsp x)))) ; However, attempting to prove that, produces a key checkpoint ; containing (MEMBER (CAR X) (APPEND (CDR X) (LIST E))). ; So we prove the lemma: (defthm member-append (iff (member e (append a b)) (or (member e a) (member e b)))) ; Note that we had to use iff instead of equal since member is not a ; Boolean function. ; Having proved this lemma, we return to dupsp-append and succeed: (defthm dupsp-append (implies (not (member e x)) (equal (dupsp (append x (list e))) (dupsp x)))) ; So now we return to dups-rev, expecting success. But it fails ; with the same key checkpoint: ; (IMPLIES (AND (CONSP X) ; (NOT (MEMBER (CAR X) (CDR X))) ; (EQUAL (DUPSP (REV (CDR X))) ; (DUPSP (CDR X)))) ; (EQUAL (DUPSP (APPEND (REV (CDR X)) (LIST (CAR X)))) ; (DUPSP (CDR X)))) ; Why wasn't our dupsp-append lemma applied? We have two choices here: ; (1) Think. (2) Use tools. ; Think: When an enabled rewrite rule doesn't fire even though the left-hand ; side matches the target, the hypothesis couldn't be relieved. The dups-append ; rule has the hypothesis (not (member e x)) and after the match with the left-hand side, ; e is (CAR X) and x is (REV (CDR X)). So the system couldn't rewrite ; (NOT (MEMBER (CAR X) (REV (CDR X)))) to true, even though it knows that ; (NOT (MEMBER (CAR X) (CDR X))) from the second hypothesis of the checkpoint. ; Obviously, we need to prove member-rev below. ; Use tools: We could enable the ``break rewrite'' facility, with ; ACL2 !>:brr t ; and then install an unconditional monitor on the rewrite rule ; dupsp-append, whose rune is (:REWRITE DUPSP-APPEND), with: ; :monitor (:rewrite dupsp-append) t ; Then we could re-try our main theorem, dupsp-rev. At the resulting ; interactive break we type :eval to evaluate the attempt to relieve the ; hypotheses of the rule. ; (1 Breaking (:REWRITE DUPSP-APPEND) on ; (DUPSP (BINARY-APPEND (REV #) (CONS # #))): ; 1 ACL2 >:eval ; 1x (:REWRITE DUPSP-APPEND) failed because :HYP 1 rewrote to ; (NOT (MEMBER (CAR X) (REV #))). ; Note that the report above shows that hypothesis 1 of the rule ; did not rewrite to T but instead rewrote to an expression ; involving (member ... (rev ...)). Thus, we're led to the ; same conclusion that Thinking produced. To get out of the ; interactive break we type: ; 1 ACL2 >:a! ; Abort to ACL2 top-level ; and then turn off the break rewrite tool since we won't need it ; again right now, with: ; ACL2 !>:brr nil ; In either case, by thinking or using tools, we decide to prove: (defthm member-rev (iff (member e (rev x)) (member e x))) ; which succeeds. Now when we try to prove dups-rev, it succeeds. (defthm dupsp-rev (equal (dupsp (rev x)) (dupsp x))) Use your browser's Back Button now to return to introductory-challenge-problem-3.  File: acl2-doc-emacs.info, Node: INTRODUCTORY-CHALLENGE-PROBLEM-4, Next: INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER, Prev: INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER, Up: INTRODUCTION-TO-THE-THEOREM-PROVER INTRODUCTORY-CHALLENGE-PROBLEM-4 challenge problem 4 for the new user of ACL2 Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1. This problem is much more open ended than the preceding ones. The challenge is to define a function that collects exactly one copy of each element of a list and to prove that it returns a subset of the list with no duplications. Hint: We recommend that you read this hint to align your function names with our solution, to make comparisons easier. Our answer is shown in see *note INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER::. In that page you'll see a definition of a function collect-once and the proofs of two theorems: (defthm main-theorem-1-about-collect-once (subsetp (collect-once x) x)) (defthm main-theorem-2-about-collect-once (not (dupsp (collect-once x)))) The function dupsp is as defined in see *note INTRODUCTORY-CHALLENGE-PROBLEM-3::. This is quite easy. Then, we define a tail-recursive version of the method based on the pseudo-code: a = nil; while (x not empty) { a = if (member (car x) a) then a else (cons (car x) a); x = (cdr x); } return a; We formalize this with the function while-loop-version, where (while-loop-version x nil) is the "semantics" of the code above. I.e., the function while-loop-version captures the while loop in the pseudo-code above and returns the final value of a, and it should be invoked with the initial value of a being nil. We prove (while-loop-version x nil) returns a subset of x that contains no duplications. Furthermore, we do it two ways: first "indirectly" by relating while-loop-version to collect-once, and second ("directly") without using collect-once. Both of these proofs are much harder than the collect-once approach, involving about a dozen lemmas each. Compare your solutions to ours at see *note INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER::. Then, use your browser's Back Button to return to introductory-challenges. acl2-sources/doc/EMACS/acl2-doc-emacs.info-100000664002132200015000000110366012222333541017721 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: ACL2-PC||APPLY-LINEAR, Next: ACL2-PC||BASH, Prev: ACL2-PC||AL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||APPLY-LINEAR (primitive) apply a linear rule Examples: (apply-linear foo) -- apply the linear rule `foo' (apply-linear (:linear foo)) -- same as above (apply-linear 2) -- apply the second linear rule, as displayed by show-linears rewrite -- apply the first rewrite rule, as displayed by show-rewrites (apply-linear foo ((y 7))) -- apply the linear rule foo with the substitution that associates 7 to the ``free variable'' y (apply-linear foo ((x 2) (y 3)) t) -- apply the linear rule foo by substituting 2 and 3 for free variables x and y, respectively, and also binding all other free variables possible by using the current context (hypotheses and governors) General Form: (apply-linear &optional rule-id substitution instantiate-free) Add a new top-level hypothesis by applying a linear rule to the current subterm. The new hypothesis will be created according to the information provided by the show-linears (sls) command. A short name for this command is al. We assume familiarity with the proof-checker's rewrite (r) command. In brief, the apply-linear command is an analogue of the rewrite command, but for linear rules in place of rewrite rules. There is a significant difference: for the apply-linear command, instead of rewriting the current subterm as is done by the rewrite command, the conclusion of the applicable linear rule, suitably instantiated, is added as a new (and last) top-level hypothesis of the goal. There is another significant difference: the automatic application of linear rules in the theorem prover is somewhat more complex than the automatic application of rewrite rules, so the apply-linear command may not correspond as closely to the prover's automatic use of a linear rule as the rewrite command corresponds to the prover's automatic use of a rewrite rule. Below, we refer freely to the documentation for the proof-checker's rewrite command. The rule-id is treated just as it is by the rewrite command. If rule-id is a positive integer n, then the nth rule as displayed by show-linears is the one that is applied. If rule-id is nil or is not supplied, then it is treated as the number 1. Otherwise, rule-id should be either a symbol or else a :linear rune. If a symbol is supplied, then any linear rule of that name may be used. Consider the following example. Suppose that the current subterm is (< (g (h y)) y) and that foo is the name of the following linear rule. (implies (true-listp x) (< (g x) 15)) Then the instruction (apply-linear foo) applies foo by adding a new hypothesis (< (g (h y)) 15). In addition, a new goal with conclusion (true-listp y) is created unless the current context (top-level hypotheses and governors) implies (true-listp y) using only "trivial reasoning", just as for the rewrite command. If the rule-id argument is a number or is not supplied, then the system will store an instruction of the form (apply-linear name ...), where name is the name of a linear rule; this is in order to make it easier to replay instructions when there have been changes to the history. Except: instead of the name (whether the name is supplied or calculated), the system stores the rune if there is any chance of ambiguity. (Formally, "ambiguity" here means that the rune being applied is of the form (:rewrite name . index), where index is not nil.) Speaking in general, then, an apply-linear instruction works as follows. First, a linear rule is selected according to the arguments of the instruction. The selection is made as explained under "General Form" above. Next, a trigger term of the rule (see *note LINEAR::) is matched with the current subterm, i.e., a substitution unify-subst is found such that if one instantiates that trigger term of the rule with unify-subst, then one obtains the current subterm. If this match fails, then the instruction fails. Next, an attempt is made to relieve (discharge) the hypotheses, possibly handling free variables (see *note FREE-VARIABLES::), exactly as is done with hypotheses when applying the proof-checker command, rewrite (r). Finally, the instruction is applied exactly as the rewrite instruction is applied, except instead of replacing the current subterm, the rule's instantiated conclusion is added to the end of the list of top-level hypotheses of the goal. Note that as for the rewrite command, the substitution argument should be a list whose elements have the form (variable term), where term may contain abbreviations.  File: acl2-doc-emacs.info, Node: ACL2-PC||BASH, Next: ACL2-PC||BDD, Prev: ACL2-PC||APPLY-LINEAR, Up: PROOF-CHECKER-COMMANDS ACL2-PC||BASH (atomic macro) call the ACL2 theorem prover's simplifier Examples: bash -- attempt to prove the current goal by simplification alone (bash ("Subgoal 2" :by foo) ("Subgoal 1" :use bar)) -- attempt to prove the current goal by simplification alone, with the indicated hints General Form: (bash &rest hints) Call the theorem prover's simplifier, creating a subgoal for each resulting goal. Notice that unlike prove, the arguments to bash are spread out, and are all hints. Bash is similar to reduce in that neither of these allows induction. But bash only allows simplification, while reduce allows processes eliminate-destructors, fertilize, generalize, and eliminate-irrelevance. *Remark:* All forcing rounds will be skipped (unless there are more than 15 subgoals generated in the first forcing round, an injustice that should be rectified, but might remain unless there is pressure to fix it).  File: acl2-doc-emacs.info, Node: ACL2-PC||BDD, Next: ACL2-PC||BK, Prev: ACL2-PC||BASH, Up: PROOF-CHECKER-COMMANDS ACL2-PC||BDD (atomic macro) prove the current goal using bdds Examples: bdd (bdd :vars nil :bdd-constructors (cons) :prove t :literal :all) The general form is as shown in the latter example above, but with any keyword-value pairs omitted and with values as described for the :bdd hint; see *note HINTS::. This command simply calls the theorem prover with the indicated bdd hint for the top-level goal. Note that if :prove is t (the default), then the proof will succeed entirely using bdds or else it will fail immediately. See *note BDD::.  File: acl2-doc-emacs.info, Node: ACL2-PC||BK, Next: ACL2-PC||BOOKMARK, Prev: ACL2-PC||BDD, Up: PROOF-CHECKER-COMMANDS ACL2-PC||BK (atomic macro) move backward one argument in the enclosing term Example and General Form: bk For example, if the conclusion is (= x (* (- y) z)) and the current subterm is (* (- y) z), then after executing bk, the current subterm will be x. Move to the previous argument of the enclosing term. This is the same as up followed by (dive n-1), where n is the position of the current subterm in its parent term in the conclusion. Thus in particular, the nx command fails if one is already at the top of the conclusion. See also up, dive, top, and bk.  File: acl2-doc-emacs.info, Node: ACL2-PC||BOOKMARK, Next: ACL2-PC||CASESPLIT, Prev: ACL2-PC||BK, Up: PROOF-CHECKER-COMMANDS ACL2-PC||BOOKMARK (macro) insert matching "bookends" comments Example: (bookmark final-goal) General Form: (bookmark name &rest instruction-list) Run the instructions in instruction-list (as though this were a call of do-all; see the documentation for do-all), but first insert a begin bookend with the given name and then, when the instructions have been completed, insert an end bookend with that same name. See the documentation of comm for an explanation of bookends and how they can affect the display of instructions.  File: acl2-doc-emacs.info, Node: ACL2-PC||CASESPLIT, Next: ACL2-PC||CG, Prev: ACL2-PC||BOOKMARK, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CASESPLIT (primitive) split into two cases Example: (casesplit (< x y)) -- assuming that we are at the top of the conclusion, add (< x y) as a new top-level hypothesis in the current goal, and create a subgoal identical to the current goal except that it has (not (< x y)) as a new top-level hypothesis General Form: (casesplit expr &optional use-hyps-flag do-not-flatten-flag) When the current subterm is the entire conclusion, this instruction adds expr as a new top-level hypothesis, and create a subgoal identical to the existing current goal except that it has the negation of expr as a new top-level hypothesis. See also claim. The optional arguments control the use of governors and the "flattening" of new hypotheses, as we now explain. The argument use-hyps-flag is only of interest when there are governors. (To read about governors, see the documentation for the command hyps). In that case, if use-hyps-flag is not supplied or is nil, then the description above is correct; but otherwise, it is not expr but rather it is (implies govs expr) that is added as a new top-level hypothesis (and whose negation is added as a top-level hypothesis for the new goal), where govs is the conjunction of the governors. If do-not-flatten-flag is supplied and not nil, then that is all there is to this command. Otherwise (thus this is the default), when the claimed term (first argument) is a conjunction (and) of terms and the claim instruction succeeds, then each (nested) conjunct of the claimed term is added as a separate new top-level hypothesis. Consider the following example, assuming there are no governors. (casesplit (and (and (< x y) (integerp a)) (equal r s)) t) Three new top-level hypotheses are added to the current goal, namely (< x y), (integerp a), and (equal r s). In that case, only one hypothesis is added to create the new goal, namely the negation of (and (< x y) (integerp a) (equal r s)). If the negation of this term had been claimed, then it would be the other way around: the current goal would get a single new hypothesis while the new goal would be created by adding three hypotheses. *Remark:* It is allowed to use abbreviations in the hints.  File: acl2-doc-emacs.info, Node: ACL2-PC||CG, Next: ACL2-PC||CHANGE-GOAL, Prev: ACL2-PC||CASESPLIT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CG (macro) change to another goal. Examples: (cg (main . 1)) -- change to the goal (main . 1) cg -- change to the next-to-top goal General Form: (CG &OPTIONAL goal-name) Same as (change-goal goal-name t), i.e. change to the indicated and move the current goal to the end of the goal stack.  File: acl2-doc-emacs.info, Node: ACL2-PC||CHANGE-GOAL, Next: ACL2-PC||CL-PROC, Prev: ACL2-PC||CG, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CHANGE-GOAL (primitive) change to another goal. Examples: (change-goal (main . 1)) -- change to the goal (main . 1) change-goal -- change to the next-to-top goal General Form: (change-goal &optional goal-name end-flg) Change to the goal with the name goal-name, i.e. make it the current goal. However, if goal-name is nil or is not supplied, then it defaults to the next-to-top goal, i.e., the second goal in the stack of goals. If end-flg is supplied and not nil, then move the current goal to the end of the goal stack; else merely swap it with the next-to-top goal. Also see documentation for cg.  File: acl2-doc-emacs.info, Node: ACL2-PC||CL-PROC, Next: ACL2-PC||CLAIM, Prev: ACL2-PC||CHANGE-GOAL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CL-PROC (macro) same as clause-processor See the documentation for proof-checker command clause-processor, which is identical to cl-proc.  File: acl2-doc-emacs.info, Node: ACL2-PC||CLAIM, Next: ACL2-PC||CLAUSE-PROCESSOR, Prev: ACL2-PC||CL-PROC, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CLAIM (atomic macro) add a new hypothesis Examples: (claim (< x y)) -- attempt to prove (< x y) from the current top-level hypotheses and if successful, then add (< x y) as a new top-level hypothesis in the current goal (claim (< x y) :otf-flg t :hints (("Goal" :induct t))) -- as above, but call the prover using the indicated values for the otf-flg and hints (claim (< x y) 0) -- as above, except instead of attempting to prove (< x y), create a new subgoal with the same top-level hypotheses as the current goal that has (< x y) as its conclusion (claim (< x y) :hints :none) -- same as immediately above General Form: (claim expr &rest rest-args) This command creates a new subgoal with the same top-level hypotheses as the current goal but with a conclusion of expr. If rest-args is a non-empty list headed by a non-keyword, then there will be no proof attempted for the new subgoal. With that possible exception, rest-args should consist of keyword arguments. The keyword argument :do-not-flatten controls the "flattening" of new hypotheses, just as with the casesplit command (as described in its documentation). The remaining rest-args are used with a call the prove command on the new subgoal, except that if :hints is a non-nil atom, then the prover is not called -- rather, this is the same as the situation described above, where rest-args is a non-empty list headed by a non-keyword. *Remarks:* (1) Unlike the casesplit command, the claim command is completely insensitive to governors. (2) It is allowed to use abbreviations in the hints. (3) The keyword :none has the special role as a value of :hints that is shown clearly in an example above.  File: acl2-doc-emacs.info, Node: ACL2-PC||CLAUSE-PROCESSOR, Next: ACL2-PC||COMM, Prev: ACL2-PC||CLAIM, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CLAUSE-PROCESSOR (atomic macro) use a clause-processor Example: (cl-proc :function note-fact-clause-processor :hint '(equal a a)) -- Invoke the indicated clause processor function with the indicated hint argument (see the beginning of community book books/clause-processors/basic-examples.lisp. General Form: (cl-proc &rest cl-proc-args) Invoke a clause-processor as indicated by cl-proc-args, which is a list of arguments that can serve as the value of a :clause-processor hint; see *note HINTS::. This command calls the prove command, and hence should only be used at the top of the conclusion.  File: acl2-doc-emacs.info, Node: ACL2-PC||COMM, Next: ACL2-PC||COMMANDS, Prev: ACL2-PC||CLAUSE-PROCESSOR, Up: PROOF-CHECKER-COMMANDS ACL2-PC||COMM (macro) display instructions from the current interactive session Examples: comm (comm 10) General Form: (comm &optional n) Prints out instructions in reverse order. This is actually the same as (commands n t) -- or, (commands nil t) if n is not supplied. As explained in the documentation for commands, the final argument of t causes suppression of instructions occurring between so-called "matching bookends," which we now explain. A "begin bookend" is an instruction of the form (COMMENT :BEGIN x . y). Similarly, an "end bookend" is an instruction of the form (COMMENT :END x' . y'). The "name" of the first bookend is x and the "name" of the second bookend is x'. When such a pair of instructions occurs in the current state-stack, we call them "matching bookends" provided that they have the same name (i.e. x equals x') and if no other begin or end bookend with name x occurs between them. The idea now is that comm hides matching bookends together with the instructions they enclose. Here is a more precise explanation of this "hiding"; probably there is no value in reading on! A comm instruction hides bookends in the following manner. (So does a comment instruction when its second optional argument is supplied and non-nil.) First, if the first argument n is supplied and not nil, then we consider only the last n instructions from the state-stack; otherwise, we consider them all. Now the resulting list of instructions is replaced by the result of applying the following process to each pair of matching bookends: the pair is removed, together with everything in between the begin and end bookend of the pair, and all this is replaced by the "instruction" ("***HIDING***" :COMMENT :BEGIN name ...) where (comment begin name ...) is the begin bookend of the pair. Finally, after applying this process to each pair of matching bookends, each begin bookend of the form (comment begin name ...) that remains is replaced by ("***UNFINISHED***" :COMMENT :BEGIN name ...) .  File: acl2-doc-emacs.info, Node: ACL2-PC||COMMANDS, Next: ACL2-PC||COMMENT, Prev: ACL2-PC||COMM, Up: PROOF-CHECKER-COMMANDS ACL2-PC||COMMANDS (macro) display instructions from the current interactive session Examples: commands (commands 10 t) General Forms: commands or (commands nil) Print out all the instructions (in the current state-stack) in reverse order, i.e. from the most recent instruction to the starting instruction. (commands n) [n a positive integer] Print out the most recent n instructions (in the current state-stack), in reverse order. (commands x abbreviate-flag) Same as above, but if abbreviate-flag is non-NIL, then do not display commands between ``matching bookends''. See documentation for comm for an explanation of matching bookends. *Remark*: If there are more than n instructions in the state-stack, then (commands n) is the same as commands (and also, (commands n abb) is the same as (commands nil abb)).  File: acl2-doc-emacs.info, Node: ACL2-PC||COMMENT, Next: ACL2-PC||CONTRADICT, Prev: ACL2-PC||COMMANDS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||COMMENT (primitive) insert a comment Example: (comment now begin difficult final goal) General Form: (comment &rest x) This instruction makes no change in the state except to insert the comment instruction. Some comments can be used to improve the display of commands; see documentation for comm.  File: acl2-doc-emacs.info, Node: ACL2-PC||CONTRADICT, Next: ACL2-PC||CONTRAPOSE, Prev: ACL2-PC||COMMENT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CONTRADICT (macro) same as contrapose see documentation for contrapose  File: acl2-doc-emacs.info, Node: ACL2-PC||CONTRAPOSE, Next: ACL2-PC||DEMOTE, Prev: ACL2-PC||CONTRADICT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||CONTRAPOSE (primitive) switch a hypothesis with the conclusion, negating both Example: (contrapose 3) General Form: (contrapose &optional n) The (optional) argument n should be a positive integer that does not exceed the number of hypotheses. Negate the current conclusion and make it the nth hypothesis, while negating the current nth hypothesis and making it the current conclusion. If no argument is supplied then the effect is the same as for (contrapose 1). *Remark:* By "negate" we mean an operation that replaces nil by t, x by nil for any other explicit value x, (not x) by x, and any other x by (not x).  File: acl2-doc-emacs.info, Node: ACL2-PC||DEMOTE, Next: ACL2-PC||DIVE, Prev: ACL2-PC||CONTRAPOSE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DEMOTE (primitive) move top-level hypotheses to the conclusion Examples: demote -- demote all top-level hypotheses (demote 3 5) -- demote hypotheses 3 and 5 For example, if the top-level hypotheses are x and y and the conclusion is z, then after execution of demote, the conclusion will be (implies (and x y) z) and there will be no (top-level) hypotheses. General Form: (demote &rest hyps-indices) Eliminate the indicated (top-level) hypotheses, but replace the conclusion conc with (implies hyps conc) where hyps is the conjunction of the hypotheses that were eliminated. If no arguments are supplied, then all hypotheses are demoted, i.e. demote is the same as (demote 1 2 ... n) where n is the number of top-level hypotheses. *Remark*: You must be at the top of the conclusion in order to use this command. Otherwise, first invoke top. Also, demote fails if there are no top-level hypotheses or if indices are supplied that are out of range.  File: acl2-doc-emacs.info, Node: ACL2-PC||DIVE, Next: ACL2-PC||DO-ALL, Prev: ACL2-PC||DEMOTE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DIVE (primitive) move to the indicated subterm Examples: (DIVE 1) -- assign the new current subterm to be the first argument of the existing current subterm (DIVE 1 2) -- assign the new current subterm to be the result of first taking the 1st argument of the existing current subterm, and then the 2nd argument of that For example, if the current subterm is (* (+ a b) c), then after (dive 1) it is (+ a b). If after that, then (dive 2) is invoked, the new current subterm will be b. Instead of (dive 1) followed by (dive 2), the same current subterm could be obtained by instead submitting the single instruction (dive 1 2). General Form: (dive &rest naturals-list) If naturals-list is a non-empty list (n_1 ... n_k) of natural numbers, let the new current subterm be the result of selecting the n_1-st argument of the current subterm, and then the n_2-th subterm of that, ..., finally the n_k-th subterm. *Remark:* Dive is related to the command pp, in that the diving is done according to raw (translated, internal form) syntax. Use the command dv if you want to dive according to the syntax displayed by the command p. Note that (dv n) can be abbreviated by simply n.  File: acl2-doc-emacs.info, Node: ACL2-PC||DO-ALL, Next: ACL2-PC||DO-ALL-NO-PROMPT, Prev: ACL2-PC||DIVE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DO-ALL (macro) run the given instructions Example: (do-all induct p prove) General Form: (do-all &rest instruction-list) Run the indicated instructions until there is a hard "failure". The instruction "succeeds" if and only if each instruction in instruction-list does. (See the documentation for sequence for an explanation of "success" and "failure.") As each instruction is executed, the system will print the usual prompt followed by that instruction, unless the global state variable pc-print-prompt-and-instr-flg is nil. *Remark:* If do-all "fails", then the failure is hard if and only if the last instruction it runs has a hard "failure". Obscure point: For the record, (do-all ins_1 ins_2 ... ins_k) is the same as (sequence (ins_1 ins_2 ... ins_k)).  File: acl2-doc-emacs.info, Node: ACL2-PC||DO-ALL-NO-PROMPT, Next: ACL2-PC||DO-STRICT, Prev: ACL2-PC||DO-ALL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DO-ALL-NO-PROMPT (macro) run the given instructions, halting once there is a "failure" Example: (do-all-no-prompt induct p prove) General Form: (do-all-no-prompt &rest instruction-list) Do-all-no-prompt is the same as do-all, except that the prompt and instruction are not printed each time, regardless of the value of pc-print-prompt-and-instr-flg. Also, restoring is disabled. See the documentation for do-all.  File: acl2-doc-emacs.info, Node: ACL2-PC||DO-STRICT, Next: ACL2-PC||DROP, Prev: ACL2-PC||DO-ALL-NO-PROMPT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DO-STRICT (macro) run the given instructions, halting once there is a "failure" Example: (do-strict induct p prove) General Form: (do-strict &rest instruction-list) Run the indicated instructions until there is a (hard or soft) "failure". In fact do-strict is identical in effect to do-all, except that do-all only halts once there is a hard "failure". See the documentation for do-all.  File: acl2-doc-emacs.info, Node: ACL2-PC||DROP, Next: ACL2-PC||DV, Prev: ACL2-PC||DO-STRICT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DROP (primitive) drop top-level hypotheses Examples: (drop 2 3) -- drop the second and third hypotheses drop -- drop all top-level hypotheses General Forms: (drop n1 n2 ...) -- Drop the hypotheses with the indicated indices. drop -- Drop all the top-level hypotheses. *Remark:* If there are no top-level hypotheses, then the instruction drop will fail. If any of the indices is out of range, i.e. is not an integer between one and the number of top-level hypotheses (inclusive), then (drop n1 n2 ...) will fail.  File: acl2-doc-emacs.info, Node: ACL2-PC||DV, Next: ACL2-PC||ELIM, Prev: ACL2-PC||DROP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||DV (atomic macro) move to the indicated subterm Examples: (dv 1) -- assign the new current subterm to be the first argument of the existing current subterm (dv 1 2) -- assign the new current subterm to be the result of first taking the 1st argument of the existing current subterm, and then the 2nd argument of that For example, if the current subterm is (* (+ a b) c), then after (dv 1) it is (+ a b). If after that, then (dv 2) is invoked, the new current subterm will be b. Instead of (dv 1) followed by (dv 2), the same current subterm could be obtained by instead submitting the single instruction (dv 1 2). General Form: (dv &rest naturals-list) If naturals-list is a non-empty list (n_1 ... n_k) of natural numbers, let the new current subterm be the result of selecting the n_1-st argument of the current subterm, and then the n_2-th subterm of that, ..., finally the n_k-th subterm. *Remark:* (dv n) may be abbreviated by simply n, so we could have typed 1 instead of (dv 1) in the first example above. *Remark:* See also dive, which is related to the command pp, in that the diving is done according to raw (translated, internal form) syntax. Use the command dv if you want to dive according to the syntax displayed by the command p. Thus, the command "up" is the inverse of dive, not of dv. The following example illustrates this point. ACL2 !>(verify (equal (* a b c) x)) ->: p ; print user-level term (EQUAL (* A B C) X) ->: pp ; print internal-form (translated) term (EQUAL (BINARY-* A (BINARY-* B C)) X) ->: exit Exiting.... NIL ACL2 !>(verify (equal (* a b c) x)) ->: p (EQUAL (* A B C) X) ->: 1 ; same as (dv 1) ->: p ; print user-level term (* A B C) ->: pp ; print internal-form (translated) term (BINARY-* A (BINARY-* B C)) ->: 3 ; dive to third argument of (* A B C) ->: p C ->: up ; go up one level in (BINARY-* A (BINARY-* B C)) ->: p (* B C) ->: pp (BINARY-* B C) ->:  File: acl2-doc-emacs.info, Node: ACL2-PC||ELIM, Next: ACL2-PC||EQUIV, Prev: ACL2-PC||DV, Up: PROOF-CHECKER-COMMANDS ACL2-PC||ELIM (atomic macro) call the ACL2 theorem prover's elimination process Example and General Form: elim Upon running the elim command, the system will create a subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof, where *only* elimination is used; not even simplification is used!  File: acl2-doc-emacs.info, Node: ACL2-PC||EQUIV, Next: ACL2-PC||EX, Prev: ACL2-PC||ELIM, Up: PROOF-CHECKER-COMMANDS ACL2-PC||EQUIV (primitive) attempt an equality (or congruence-based) substitution Examples: (equiv (* x y) 3) -- replace (* x y) by 3 everywhere inside the current subterm, if their equality is among the top-level hypotheses or the governors (equiv x t iff) -- replace x by t everywhere inside the current subterm, where only propositional equivalence needs to be maintained at each occurrence of x General form: (equiv old new &optional relation) Substitute new for old everywhere inside the current subterm, provided that either (relation old new) or (relation new old) is among the top-level hypotheses or the governors (possibly by way of backchaining and/or refinement; see below). If relation is nil or is not supplied, then it defaults to equal. See also the command =, which is much more flexible. Note that this command fails if no substitution is actually made. *Remark:* No substitution takes place inside explicit values. So for example, the instruction (equiv 3 x) will cause 3 to be replaced by x if the current subterm is, say, (* 3 y), but not if the current subterm is (* 4 y) even though 4 = (1+ 3). The following remarks are quite technical and mostly describe a certain weak form of "backchaining" that has been implemented for equiv in order to support the = command. In fact neither the term (relation old new) nor the term (relation new old) needs to be *explicitly* among the current "assumptions", i.e., the top-level hypothesis or the governors. Rather, there need only be such an assumption that "tells us" (r old new) or (r new old), for *some* equivalence relation r that *refines* relation. Here, "tells us" means that either one of the indicated terms is among those assumptions, or else there is an assumption that is an implication whose conclusion is one of the indicated terms and whose hypotheses (gathered up by appropriately flattening the first argument of the implies term) are all among the current assumptions.  File: acl2-doc-emacs.info, Node: ACL2-PC||EX, Next: ACL2-PC||EXIT, Prev: ACL2-PC||EQUIV, Up: PROOF-CHECKER-COMMANDS ACL2-PC||EX (macro) exit after possibly saving the state Example and General Form: ex Same as exit, except that first the instruction save is executed. If save queries the user and is answered negatively, then the exit is aborted.  File: acl2-doc-emacs.info, Node: ACL2-PC||EXIT, Next: ACL2-PC||EXPAND, Prev: ACL2-PC||EX, Up: PROOF-CHECKER-COMMANDS ACL2-PC||EXIT (meta) exit the interactive proof-checker Examples: exit -- exit the interactive proof-checker (exit t) -- exit after printing a bogus defthm event (exit append-associativity) -- exit and create a defthm event named append-associativity General Forms: exit -- Exit without storing an event. (exit t) -- Exit after printing a bogus defthm event, showing :INSTRUCTIONS. (exit event-name &optional rule-classes do-it-flg) -- Exit, and perhaps store an event The command exit returns you to the ACL2 loop. At a later time, (verify) may be executed to get back into the same proof-checker state, as long as there hasn't been an intervening use of the proof-checker (otherwise see save). When given one or more arguments as shown above, exit still returns you to the ACL2 loop, but first, if the interactive proof is complete, then it attempts create a defthm event with the specified event-name and rule-classes (which defaults to (:rewrite) if not supplied). The event will be printed to the terminal, and then normally the user will be queried whether an event should really be created. However, if the final optional argument do-it-flg is supplied and not nil, then an event will be made without a query. For example, the form (exit top-pop-elim (:elim :rewrite) t) causes a defthm event named top-pop-elim to be created with rule-classes (:elim :rewrite), without a query to the user (because of the argument t). *Remark:* it is permitted for event-name to be nil. In that case, the name of the event will be the name supplied during the original call of verify. (See the documentation for verify and commands.) Also in that case, if rule-classes is not supplied then it defaults to the rule-classes supplied in the original call of verify. Comments on "success" and "failure". An exit instruction will always "fail", so for example, if it appears as an argument of a do-strict instruction then none of the later (instruction) arguments will be executed. Moreover, the "failure" will be "hard" if an event is successfully created or if the instruction is simply exit; otherwise it will be "soft". See the documentation for sequence for an explanation of hard and soft "failures". An obscure but potentially important fact is that if the "failure" is hard, then the error signal is a special signal that the top-level interactive loop can interpret as a request to exit. Thus for example, a sequencing command that turns an error triple (mv erp val state) into (mv t val state) would never cause an exit from the interactive loop. If the proof is not complete, then (exit event-name ...) will not cause an exit from the interactive loop. However, in that case it will print out the original user-supplied goal (the one that was supplied with the call to verify) and the current list of instructions.  File: acl2-doc-emacs.info, Node: ACL2-PC||EXPAND, Next: ACL2-PC||FAIL, Prev: ACL2-PC||EXIT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||EXPAND (primitive) expand the current function call without simplification Examples: expand -- expand and do not simplify. For example, if the current subterm is (append a b), then after (expand t) the current subterm will be the term: (if (true-listp x) (if x (cons (car x) (append (cdr x) y)) y) (apply 'binary-append (list x y))) regardless of the top-level hypotheses and the governors. General Form: (expand &optional do-not-expand-lambda-flg) Expand the function call at the current subterm, and do not simplify. The options have the following meanings: do-not-expand-lambda-flg: default is nil; otherwise, the result should be a lambda expression See also x, which allows simplification.  File: acl2-doc-emacs.info, Node: ACL2-PC||FAIL, Next: ACL2-PC||FINISH, Prev: ACL2-PC||EXPAND, Up: PROOF-CHECKER-COMMANDS ACL2-PC||FAIL (macro) cause a failure Examples: fail (fail t) General Form: (fail &optional hard) This is probably only of interest to writers of macro commands. The only function of fail is to fail to "succeed". The full story is that fail and (fail nil) simply return (mv nil nil state), while (fail hard) returns (mv hard nil state) if hard is not nil. See also do-strict, do-all, and sequence.  File: acl2-doc-emacs.info, Node: ACL2-PC||FINISH, Next: ACL2-PC||FORWARDCHAIN, Prev: ACL2-PC||FAIL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||FINISH (macro) require completion of instructions; save error if inside :hints Example: (finish induct prove bash) General Form: (finish &rest instructions) Run the indicated instructions, stopping at the first failure. If there is any failure, or if any new goals are created and remain at the end of the indicated instructions, then consider the call of finish to be a failure. See *note PROOF-CHECKER-COMMANDS:: and visit the documentation for sequence for a discussion of the notion of "failure" for proof-checker commands.  File: acl2-doc-emacs.info, Node: ACL2-PC||FORWARDCHAIN, Next: ACL2-PC||FREE, Prev: ACL2-PC||FINISH, Up: PROOF-CHECKER-COMMANDS ACL2-PC||FORWARDCHAIN (atomic macro) forward chain from an implication in the hyps Example: (forwardchain 2) ; Second hypothesis should be of the form ; (IMPLIES hyp concl), and the result is to replace ; that hypothesis with concl. General Forms: (forwardchain hypothesis-number) (forwardchain hypothesis-number hints) (forwardchain hypothesis-number hints quiet-flg) This command replaces the hypothesis corresponding to given index, which should be of the form (IMPLIES hyp concl), with its consequent concl. In fact, the given hypothesis is dropped, and the replacement hypothesis will appear as the final hypothesis after this command is executed. The prover must be able to prove the indicated hypothesis from the other hypotheses, or else the command will fail. The :hints argument is used in this prover call, and should have the usual syntax of hints to the prover. Output is suppressed if quiet-flg is supplied and not nil.  File: acl2-doc-emacs.info, Node: ACL2-PC||FREE, Next: ACL2-PC||GENEQV, Prev: ACL2-PC||FORWARDCHAIN, Up: PROOF-CHECKER-COMMANDS ACL2-PC||FREE (atomic macro) create a "free variable" Example: (free x) General Form: (free var) Mark var as a "free variable". Free variables are only of interest for the put command; see its documentation for an explanation.  File: acl2-doc-emacs.info, Node: ACL2-PC||GENEQV, Next: ACL2-PC||GENERALIZE, Prev: ACL2-PC||FREE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||GENEQV (macro) show the generated equivalence relation maintained at the current subterm General Forms: geneqv ; show list of equivalence relations being maintained (geneqv t) ; as above, but pair each relation with a justifying rune This is an advanced command, whose effect is to print the so-called "generated equivalence relation" (or "geneqv") that is maintained at the current subterm of the conclusion. That structure is a list of equivalence relations, representing the transitive closure E of the union of those relations, such that it suffices to maintain E at the current subterm: if that subterm, u, is replaced in the goal's conclusion, G, by another term equivalent to u with respect to E, then the resulting conclusion is Boolean equivalent to G. Also see *note DEFCONG::. The command `geneqv' prints the above list of equivalence relations, or more precisely, the list of function symbols for those relations. If however geneqv is given a non-nil argument, then a list is printed whose elements are each of the form (s r), where s is the symbol for an equivalence relation and r is a :congruence rune justifying the inclusion of s in the list of equivalence relations being maintained at the current subterm.  File: acl2-doc-emacs.info, Node: ACL2-PC||GENERALIZE, Next: ACL2-PC||GOALS, Prev: ACL2-PC||GENEQV, Up: PROOF-CHECKER-COMMANDS ACL2-PC||GENERALIZE (primitive) perform a generalization Example: (generalize ((and (true-listp x) (true-listp y)) 0) ((append x y) w)) General Form: (generalize &rest substitution) Generalize using the indicated substitution, which should be a non-empty list. Each element of that list should be a two-element list of the form (term variable), where term may use abbreviations. The effect of the instruction is to replace each such term in the current goal by the corresponding variable. This replacement is carried out by a parallel substitution, outside-in in each hypothesis and in the conclusion. More generally, actually, the "variable" (second) component of each pair may be nil or a number, which causes the system to generate a new name of the form _ or _n, with n a natural number; more on this below. However, when a variable is supplied, it must not occur in any goal of the current proof-checker state. When the "variable" above is nil, the system will treat it as the variable |_| if that variable does not occur in any goal of the current proof-checker state. Otherwise it treats it as |_0|, or |_1|, or |_2|, and so on, until one of these is not among the variables of the current proof-checker state. If the "variable" is a non-negative integer n, then the system treats it as |_n| unless that variable already occurs among the current goals, in which case it increments n just as above until it obtains a new variable. *Remark:* The same variable may not occur as the variable component of two different arguments (though nil may occur arbitrarily many times, as may a positive integer).  File: acl2-doc-emacs.info, Node: ACL2-PC||GOALS, Next: ACL2-PC||HELP, Prev: ACL2-PC||GENERALIZE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||GOALS (macro) list the names of goals on the stack Example and General Form: goals Goals lists the names of all goals that remain to be proved. They are listed in the order in which they appear on the stack of remaining goals, which is relevant for example to the effect of a change-goal instruction.  File: acl2-doc-emacs.info, Node: ACL2-PC||HELP, Next: ACL2-PC||HELP!, Prev: ACL2-PC||GOALS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||HELP (macro) proof-checker help facility Examples: (help all) -- list all proof-checker commands (help rewrite) -- partial documentation on the rewrite command; the rest is available using more or more! (help! rewrite) -- full documentation on the rewrite command help, help! -- this documentation (in part, or in totality, respectively) General Forms: (help &optional command) (help! &optional command) more more! The proof checker supports the same kind of documentation as does ACL2 proper. The main difference is that you need to type (help command) in a list rather than :doc command. So, to get all the documentation on command, type (help! command) inside the interactive loop, but to get only a one-line description of the command together with some examples, type (help command). In the latter case, you can get the rest of the help by typing more!; or type more if you don't necessarily want all the rest of the help at once. (Then keep typing more if you want to keep getting more of the help for that command.) An exception is (help all), which prints the documentation topic proof-checker-commands, to show you all possible proof-checker commands. So for example, when you see ACL2-PC::USE in that list, you can then submit (help use) or (help! use) to get documentation for the proof-checker use command. But summarizing for other than the case of all: as with ACL2, you can type either of the following: more, more! -- to obtain more (or, all the rest of) the documentation last requested by help (or, outside the proof-checker's loop, :doc) It has been arranged that the use of (help command) will tell you just about everything you could want to know about command, almost always by way of examples. For more details about a command, use help!, more, or more!. We use the word "command" to refer to the name itself, e.g. rewrite. We use the word "instruction" to refer to an input to the interactive system, e.g. (rewrite foo) or (help split). Of course, we allow commands with no arguments as instructions in many cases, e.g. rewrite. In such cases, command is treated identically to (command).  File: acl2-doc-emacs.info, Node: ACL2-PC||HELP!, Next: ACL2-PC||HELP-LONG, Prev: ACL2-PC||HELP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||HELP! (macro) proof-checker help facility Same as help, except that the entire help message is printed without any need to invoke more! or more. Invoke help for documentation about the proof-checker help facility.  File: acl2-doc-emacs.info, Node: ACL2-PC||HELP-LONG, Next: ACL2-PC||HYPS, Prev: ACL2-PC||HELP!, Up: PROOF-CHECKER-COMMANDS ACL2-PC||HELP-LONG (macro) same as help! See the documentation for help!. Help-long has been included in addition to help! for historical reasons. (Such a command is included in Pc-Nqthm).  File: acl2-doc-emacs.info, Node: ACL2-PC||HYPS, Next: ACL2-PC||ILLEGAL, Prev: ACL2-PC||HELP-LONG, Up: PROOF-CHECKER-COMMANDS ACL2-PC||HYPS (macro) print the hypotheses Examples: hyps -- print all (top-level) hypotheses (hyps (1 3) (2 4)) -- print hypotheses 1 and 3 and governors 2 and 4 (hyps (1 3) t) -- print hypotheses 1 and 3 and all governors General Form: (hyps &optional hyps-indices govs-indices) Print the indicated top-level hypotheses and governors. (The notion of "governors" is defined below.) Here, hyps-indices and govs-indices should be lists of indices of hypotheses and governors (respectively), except that the atom t may be used to indicate that one wants all hypotheses or governors (respectively). The list of "governors" is defined as follows. Actually, we define here the notion of the governors for a pair of the form ]; we're interested in the special case where the term is the conclusion and the address is the current address. If the address is nil, then there are no governors, i.e., the list of governors is nil. If the term is of the form (if x y z) and the address is of the form (2 . rest) or (3 . rest), then the list of governors is the result of consing x or its negation (respectively) onto the list of governors for the pair or the pair (respectively). If the term is of the form (implies x y) and the address is of the form (2 . rest), then the list of governors is the result of consing x onto the list of governors for the pair . Otherwise, the list of governors for the pair is exactly the list of governors for the pair where argn is the nth argument of term. If all goals have been proved, a message saying so will be printed. (as there will be no current hypotheses or governors!). The hyps command never causes an error. It "succeeds" (in fact its value is t) if the arguments (when supplied) are appropriate, i.e. either t or lists of indices of hypotheses or governors, respectively. Otherwise it "fails" (its value is nil).  File: acl2-doc-emacs.info, Node: ACL2-PC||ILLEGAL, Next: ACL2-PC||IN-THEORY, Prev: ACL2-PC||HYPS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||ILLEGAL (macro) illegal instruction Example: (illegal -3) General Form: (illegal instruction) Probably not of interest to most users; always "fails" since it expands to the fail command. The illegal command is used mainly in the implementation. For example, the instruction 0 is "read" as (illegal 0), since dive expects positive integers.  File: acl2-doc-emacs.info, Node: ACL2-PC||IN-THEORY, Next: ACL2-PC||INDUCT, Prev: ACL2-PC||ILLEGAL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||IN-THEORY (primitive) set the current proof-checker theory Example: (in-theory (union-theories (theory 'minimal-theory) '(true-listp binary-append))) General Form: (in-theory &optional atom-or-theory-expression) If the argument is not supplied, then this command sets the current proof-checker theory (see below for explanation) to agree with the current ACL2 theory. Otherwise, the argument should be a theory expression, and in that case the proof-checker theory is set to the value of that theory expression. The current proof-checker theory is used in all calls to the ACL2 theorem prover and rewriter from inside the proof-checker. Thus, the most recent in-theory instruction in the current state-stack has an effect in the proof-checker totally analogous to the effect caused by an in-theory hint or event in ACL2. All in-theory instructions before the last are ignored, because they refer to the current theory in the ACL2 state, not to the existing proof-checker theory. For example: ACL2 !>:trans1 (enable bar) (UNION-THEORIES (CURRENT-THEORY :HERE) '(BAR)) ACL2 !>:trans1 (CURRENT-THEORY :HERE) (CURRENT-THEORY-FN :HERE WORLD) ACL2 !> Thus (in-theory (enable bar)) modifies the current theory of the current ACL2 world. So for example, suppose that foo is disabled outside the proof checker and you execute the following instructions, in this order. (in-theory (enable foo)) (in-theory (enable bar)) Then after the second of these, bar will be enabled in the proof-checker, but foo will be disabled. The reason is that (in-theory (enable bar)) instructs the proof-checker to modify the current theory (from outside the proof-checker, not from inside the proof-checker) by enabling bar. Note that in-theory instructions in the proof-checker have no effect outside the proof-checker's interactive loop. If the most recent in-theory instruction in the current state of the proof-checker has no arguments, or if there is no in-theory instruction in the current state of the proof-checker, then the proof-checker will use the current ACL2 theory. This is true even if the user has interrupted the interactive loop by exiting and changing the global ACL2 theory. However, if the most recent in-theory instruction in the current state of the proof-checker had an argument, then global changes to the current theory will have no effect on the proof-checker state.  File: acl2-doc-emacs.info, Node: ACL2-PC||INDUCT, Next: ACL2-PC||LEMMAS-USED, Prev: ACL2-PC||IN-THEORY, Up: PROOF-CHECKER-COMMANDS ACL2-PC||INDUCT (atomic macro) generate subgoals using induction Examples: induct, (induct t) -- induct according to a heuristically-chosen scheme, creating a new subgoal for each base and induction step (induct (append (reverse x) y)) -- as above, but choose an induction scheme based on the term (append (reverse x) y) rather than on the current goal General Form: (induct &optional term) Induct as in the corresponding :induct hint given to the theorem prover, creating new subgoals for the base and induction steps. If term is t or is not supplied, then use the current goal to determine the induction scheme; otherwise, use that term. *Remark:* As usual, abbreviations are allowed in the term. *Remark:* Induct actually calls the prove command with all processes turned off. Thus, you must be at top of the goal for an induct instruction.  File: acl2-doc-emacs.info, Node: ACL2-PC||LEMMAS-USED, Next: ACL2-PC||LISP, Prev: ACL2-PC||INDUCT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||LEMMAS-USED (macro) print the runes (definitions, lemmas, ...) used This is just an alias for runes.  File: acl2-doc-emacs.info, Node: ACL2-PC||LISP, Next: ACL2-PC||MORE, Prev: ACL2-PC||LEMMAS-USED, Up: PROOF-CHECKER-COMMANDS ACL2-PC||LISP (meta) evaluate the given form in Lisp Example: (lisp (assign xxx 3)) General Form: (lisp form) Evaluate form. The lisp command is mainly of interest for side effects. See also print, skip, and fail. The rest of the documentation for lisp is of interest only to those who use it in macro commands. If the Lisp evaluation (by trans-eval) of form returns an error triple (see *note ERROR-TRIPLES::) of the form (mv erp ((NIL NIL STATE) . (erp-1 val-1 &)) state), then the lisp command returns the appropriate error triple (mv (or erp erp-1) val-1 state) . Otherwise, the trans-eval of form must return an error triple of the form (mv erp (cons stobjs-out val) &), and the lisp command returns the appropriate error triple (mv erp val state). Note that the output signature of the form has been lost. The user must know the signature in order to use the output of the lisp command. Trans-eval, which is undocumented except by comments in the ACL2 source code, has replaced, in val, any occurrence of the current state or the current values of stobjs by simple symbols such as REPLACED-STATE. The actual values of these objects may be recovered, in principle, from the state returned and the user-stobj-alist within that state. However, in practice, the stobjs cannot be recovered because the user is denied access to user-stobj-alist. The moral is: do not try to write macro commands that manipulate stobjs. Should the returned val contain REPLACED-STATE the value may simply be ignored and state used, since that is what REPLACED-STATE denotes.  File: acl2-doc-emacs.info, Node: ACL2-PC||MORE, Next: ACL2-PC||MORE!, Prev: ACL2-PC||LISP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||MORE (macro) proof-checker help facility Continues documentation of last proof-checker command visited with help. Invoke help for documentation about the proof-checker help facility.  File: acl2-doc-emacs.info, Node: ACL2-PC||MORE!, Next: ACL2-PC||NEGATE, Prev: ACL2-PC||MORE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||MORE! (macro) proof-checker help facility Continues documentation of last proof-checker command visited with help, until all documentation on that command is printed out. Invoke help for documentation about the proof-checker help facility.  File: acl2-doc-emacs.info, Node: ACL2-PC||NEGATE, Next: ACL2-PC||NIL, Prev: ACL2-PC||MORE!, Up: PROOF-CHECKER-COMMANDS ACL2-PC||NEGATE (macro) run the given instructions, and "succeed" if and only if they "fail" Example: (negate prove) General form: (negate &rest instruction-list) Run the indicated instructions exactly in the sense of do-all, and "succeed" if and only if they "fail". *Remark:* Negate instructions will never produce hard "failures".  File: acl2-doc-emacs.info, Node: ACL2-PC||NIL, Next: ACL2-PC||NOISE, Prev: ACL2-PC||NEGATE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||NIL (macro) used for interpreting control-d Example and General form: nil (or, control-d). The whole point of this command is that in some Lisps (including akcl), if you type control-d then it seems, on occasion, to get interpreted as nil. Without this command, one seems to get into an infinite loop.  File: acl2-doc-emacs.info, Node: ACL2-PC||NOISE, Next: ACL2-PC||NX, Prev: ACL2-PC||NIL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||NOISE (meta) run instructions with output Example: (noise induct prove) General Form: (noise &rest instruction-list) Run the instruction-list through the top-level loop with output. In fact, having output is the default. Noise is useful inside a surrounding call of quiet, when one temporarily wants output. For example, if one wants to see output for a prove command immediately following an induct command but before an s command, one may want to submit an instruction like (quiet induct (noise prove) s). See also quiet.  File: acl2-doc-emacs.info, Node: ACL2-PC||NX, Next: ACL2-PC||ORELSE, Prev: ACL2-PC||NOISE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||NX (atomic macro) move forward one argument in the enclosing term Example and General Form: nx For example, if the conclusion is (= x (* (- y) z)) and the current subterm is x, then after executing nx, the current subterm will be (* (- y) z). This is the same as up followed by (dive n+1), where n is the position of the current subterm in its parent term in the conclusion. Thus in particular, the nx command fails if one is already at the top of the conclusion. See also up, dive, top, and bk.  File: acl2-doc-emacs.info, Node: ACL2-PC||ORELSE, Next: ACL2-PC||P, Prev: ACL2-PC||NX, Up: PROOF-CHECKER-COMMANDS ACL2-PC||ORELSE (macro) run the first instruction; if (and only if) it "fails", run the second Example: (orelse top (print "Couldn't move to the top")) General form: (orelse instr1 instr2) Run the first instruction. Then if it "fails", run the second instruction also; otherwise, stop after the first. This instruction "succeeds" if and only if either instr1 "succeeds", or else instr2 "succeeds". If it "fails", then the failure is soft.  File: acl2-doc-emacs.info, Node: ACL2-PC||P, Next: ACL2-PC||P-TOP, Prev: ACL2-PC||ORELSE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||P (macro) prettyprint the current term Example and General Form: p Prettyprint the current term. The usual user syntax is used, so that for example one would see (and x y) rather than (if x y 'nil). (See also pp.) Also, abbreviations are inserted where appropriate; see add-abbreviation. The "current term" is the entire conclusion unless dive commands have been given, in which case it may be a subterm of the conclusion. If all goals have been proved, a message saying so will be printed (as there will be no current term!).  File: acl2-doc-emacs.info, Node: ACL2-PC||P-TOP, Next: ACL2-PC||PL, Prev: ACL2-PC||P, Up: PROOF-CHECKER-COMMANDS ACL2-PC||P-TOP (macro) prettyprint the conclusion, highlighting the current term Example and General Form: p-top For example, if the conclusion is (equal (and x (p y)) (foo z)) and the current subterm is (p y), then p-top will print (equal (and x (*** (p y) ***)) (foo z)). Prettyprint the the conclusion, highlighting the current term. The usual user syntax is used, as with the command p (as opposed to pp). This is illustrated in the example above, where one would *not* see (equal (if x (*** (p y) ***) 'nil) (foo z)). *Remark* (obscure): In some situations, a term of the form (if x t y) occurring inside the current subterm will not print as (or x y), when x isn't a call of a boolean primitive. There's nothing incorrect about this, however.  File: acl2-doc-emacs.info, Node: ACL2-PC||PL, Next: ACL2-PC||PP, Prev: ACL2-PC||P-TOP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PL (macro) print the rules for a given name Examples: pl (pl foo) General Form: (pl &optional x) This command simply invokes the corresponding command of the top-level ACL2 loop; see *note PL::. If no argument is given, or if the argument is nil, then the current subterm should be a call of a function symbol, and the argument is taken to be that symbol. If you want information about applying rewrite rules to the current subterm, consider the show-rewrites (or equivalently, sr) command.  File: acl2-doc-emacs.info, Node: ACL2-PC||PP, Next: ACL2-PC||PR, Prev: ACL2-PC||PL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PP (macro) prettyprint the current term Example and General Form: pp This is the same as p (see its documentation), except that raw syntax (internal form) is used. So for example, one would see (if x y 'nil) rather than (and x y). Abbreviations are however still inserted, as with p.  File: acl2-doc-emacs.info, Node: ACL2-PC||PR, Next: ACL2-PC||PRINT, Prev: ACL2-PC||PP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PR (macro) print the rules for a given name Examples: pr (pr foo) General Form: (pr &optional x) This command simply invokes the corresponding command of the top-level ACL2 loop; see *note PR::. If no argument is given, or if the argument is nil, then the current subterm should be a call of a function symbol, and the argument is taken to be that symbol. If you want information about applying rewrite rules to the current subterm, consider the show-rewrites (or equivalently, sr) command.  File: acl2-doc-emacs.info, Node: ACL2-PC||PRINT, Next: ACL2-PC||PRINT-ALL-CONCS, Prev: ACL2-PC||PR, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PRINT (macro) print the result of evaluating the given form Example: (print (append '(a b) '(c d))) Print the list (a b c d) to the terminal General Forms: (print form) (print form t) Prettyprints the result of evaluating form. The evaluation of form should return a single value that is not state or a single-threaded object (see *note STOBJ::). The optional second argument causes printing to be done without elision (so-called "evisceration"; see *note EVISC-TUPLE::). If the form you want to evaluate does not satisfy the criterion above, you should create an appropriate call of the lisp command instead. Notice that this command always returns (mv nil nil state) where the second result will always be REPLACED-STATE.  File: acl2-doc-emacs.info, Node: ACL2-PC||PRINT-ALL-CONCS, Next: ACL2-PC||PRINT-ALL-GOALS, Prev: ACL2-PC||PRINT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PRINT-ALL-CONCS (macro) print all the conclusions of (as yet unproved) goals Example and General Form: print-all-concs Prints all the conclusions of goals that remain to be proved, in a pleasant format. See also the proof-checker command print-all-goals.  File: acl2-doc-emacs.info, Node: ACL2-PC||PRINT-ALL-GOALS, Next: ACL2-PC||PRINT-MAIN, Prev: ACL2-PC||PRINT-ALL-CONCS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PRINT-ALL-GOALS (macro) print all the (as yet unproved) goals Example and General Form: print-all-goals Prints all the goals that remain to be proved, in a pleasant format. See also the proof-checker command print-all-concs.  File: acl2-doc-emacs.info, Node: ACL2-PC||PRINT-MAIN, Next: ACL2-PC||PRO, Prev: ACL2-PC||PRINT-ALL-GOALS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PRINT-MAIN (macro) print the original goal Example and General Form: print-main Print the goal as originally entered.  File: acl2-doc-emacs.info, Node: ACL2-PC||PRO, Next: ACL2-PC||PROMOTE, Prev: ACL2-PC||PRINT-MAIN, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PRO (atomic macro) repeatedly apply promote Example and General Form: pro Apply the promote command until there is no change. This command "succeeds" exactly when at least one call of promote "succeeds". In that case, only a single new proof-checker state will be created.  File: acl2-doc-emacs.info, Node: ACL2-PC||PROMOTE, Next: ACL2-PC||PROTECT, Prev: ACL2-PC||PRO, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PROMOTE (primitive) move antecedents of conclusion's implies term to top-level hypotheses Examples: promote (promote t) For example, if the conclusion is (implies (and x y) z), then after execution of promote, the conclusion will be z and the terms x and y will be new top-level hypotheses. General Form: (promote &optional do-not-flatten-flag) Replace conclusion of (implies hyps exp) or (if hyps exp t) with simply exp, adding hyps to the list of top-level hypotheses. Moreover, if hyps is viewed as a conjunction then each conjunct will be added as a separate top-level hypothesis. An exception is that if do-not-flatten-flag is supplied and not nil, then only one top-level hypothesis will be added, namely hyps. *Remark*: You must be at the top of the conclusion in order to use this command. Otherwise, first invoke top.  File: acl2-doc-emacs.info, Node: ACL2-PC||PROTECT, Next: ACL2-PC||PROVE, Prev: ACL2-PC||PROMOTE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PROTECT (macro) run the given instructions, reverting to existing state upon failure Example: (protect induct p prove) General Form: (protect &rest instruction-list) Protect is the same as do-strict, except that as soon as an instruction "fails", the state-stack reverts to what it was before the protect instruction began, and restore is given the same meaning that it had before the protect instruction began. See the documentation for do-strict.  File: acl2-doc-emacs.info, Node: ACL2-PC||PROVE, Next: ACL2-PC||PSO, Prev: ACL2-PC||PROTECT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PROVE (primitive) call the ACL2 theorem prover to prove the current goal Examples: prove -- attempt to prove the current goal (prove :otf-flg t :hints (("Subgoal 2" :by foo) ("Subgoal 1" :use bar))) -- attempt to prove the current goal, with the indicated hints and with OTF-FLG set General Form: (prove &rest rest-args) Attempt to prove the current goal, where rest-args is as in the keyword arguments to defthm except that only :hints and :otf-flg are allowed. The command succeeds exactly when the corresponding defthm would succeed, except that it is all right for some goals to be given "bye"s. Each goal given a "bye" will be turned into a new subgoal. (See *note HINTS:: for an explanation of :by hints.) *Remark:* Use (= t) instead if you are not at the top of the conclusion. Also note that if there are any hypotheses in the current goal, then what is actually attempted is a proof of (implies hyps conc), where hyps is the conjunction of the top-level hypotheses and conc is the goal's conclusion. *Remark:* It is allowed to use abbreviations in the hints.  File: acl2-doc-emacs.info, Node: ACL2-PC||PSO, Next: ACL2-PC||PSO!, Prev: ACL2-PC||PROVE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PSO (macro) print the most recent proof attempt from inside the proof-checker Example and General Form: pso Print the most recent proof attempt from inside the proof-checker assuming you are in gag-mode or have saved output (see *note SET-SAVED-OUTPUT::). This includes all calls to the prover, including for example proof-checker commands induct, split, and bash, in addition to prove. So for example, you can follow (quiet prove) with pso to see the proof, including proof-tree output, if it failed. See also documentation for related proof-checker commands psog and pso!.  File: acl2-doc-emacs.info, Node: ACL2-PC||PSO!, Next: ACL2-PC||PSOG, Prev: ACL2-PC||PSO, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PSO! (macro) print the most recent proof attempt from inside the proof-checker Example and General Form: pso! Print the most recent proof attempt from inside the proof-checker, including proof-tree output, assuming you are in gag-mode or have saved output (see *note SET-SAVED-OUTPUT::). This includes all calls to the prover, including for example proof-checker commands induct, split, and bash, in addition to prove. So for example, you can follow (quiet prove) with pso! to see the proof, including proof-tree output, if it failed. See also documentation for related proof-checker commands pso and psog.  File: acl2-doc-emacs.info, Node: ACL2-PC||PSOG, Next: ACL2-PC||PUT, Prev: ACL2-PC||PSO!, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PSOG (macro) print the most recent proof attempt from inside the proof-checker Example and General Form: psog Print the most recent proof attempt from inside the proof-checker, including goal names, assuming you are in gag-mode or have saved output (see *note SET-SAVED-OUTPUT::). This includes all calls to the prover, including for example proof-checker commands induct, split, and bash, in addition to prove. So for example, you can follow (quiet prove) with psog to see the proof, including proof-tree output, if it failed. See also documentation for related proof-checker commands pso and pso!.  File: acl2-doc-emacs.info, Node: ACL2-PC||PUT, Next: ACL2-PC||QUIET, Prev: ACL2-PC||PSOG, Up: PROOF-CHECKER-COMMANDS ACL2-PC||PUT (macro) substitute for a "free variable" Example: (put x 17) General Form: (put var expr) Substitute expr for the "free variable" var, as explained below. A "free variable" is, for our purposes, a variable var such that the instruction (free var) has been executed earlier in the state-stack. What (free var) really does is to let var be an abbreviation for the term (hide var) (see documentation for add-abbreviation). What (put var expr) really does is to unwind the state-stack, replacing that free instruction with the instruction (add-abbreviation var expr), so that future references to (? var) become reference to expr rather than to (hide var), and then to replay all the other instructions that were unwound. Because hide was used, the expectation is that in most cases, the instructions will replay successfully and put will "succeed". However, if any replayed instruction "fails", then the entire replay will abort and "fail", and the state-stack will revert to its value before the put instruction was executed. If (put var expr) "succeeds", then (remove-abbreviation var) will be executed at the end. *Remark*: The restore command will revert the state-stack to its value present before the put instruction was executed.  File: acl2-doc-emacs.info, Node: ACL2-PC||QUIET, Next: ACL2-PC||R, Prev: ACL2-PC||PUT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||QUIET (meta) run instructions without output Example: (quiet induct prove) General Form: (quiet &rest instruction-list) Run the instruction-list through the top-level loop with no output. See also noise.  File: acl2-doc-emacs.info, Node: ACL2-PC||R, Next: ACL2-PC||REDUCE, Prev: ACL2-PC||QUIET, Up: PROOF-CHECKER-COMMANDS ACL2-PC||R (macro) same as rewrite Example: (r 3) See the documentation for rewrite, as r and rewrite are identical.  File: acl2-doc-emacs.info, Node: ACL2-PC||REDUCE, Next: ACL2-PC||REDUCE-BY-INDUCTION, Prev: ACL2-PC||R, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REDUCE (atomic macro) call the ACL2 theorem prover's simplifier Examples: reduce -- attempt to prove the current goal without using induction (reduce ("Subgoal 2" :by foo) ("Subgoal 1" :use bar)) -- attempt to prove the current goal without using induction, with the indicated hints General Form: (reduce &rest hints) Attempt to prove the current goal without using induction, using the indicated hints (if any). A subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof. Notice that unlike prove, the arguments to reduce are spread out, and are all hints. Reduce is similar to bash in that neither of these allows induction. But bash only allows simplification, while reduce allows processes eliminate-destructors, fertilize, generalize, and eliminate-irrelevance. *Remark:* Induction will be used to the extent that it is ordered explicitly in the hints.  File: acl2-doc-emacs.info, Node: ACL2-PC||REDUCE-BY-INDUCTION, Next: ACL2-PC||REMOVE-ABBREVIATIONS, Prev: ACL2-PC||REDUCE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REDUCE-BY-INDUCTION (macro) call the ACL2 prover without induction, after going into induction Examples: reduce-by-induction -- attempt to prove the current goal after going into induction, with no further inductions (reduce-by-induction ("Subgoal 2" :by foo) ("Subgoal 1" :use bar)) -- attempt to prove the current goal after going into induction, with no further inductions, using the indicated hints General Form: (reduce-by-induction &rest hints) A subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof, except that the proof begins with a top-level induction. Notice that unlike prove, the arguments to reduce-by-induction are spread out, and are all hints. See also prove, reduce, and bash. *Remark*: Induction and the various processes will be used to the extent that they are ordered explicitly in the :induct and :do-not hints.  File: acl2-doc-emacs.info, Node: ACL2-PC||REMOVE-ABBREVIATIONS, Next: ACL2-PC||REPEAT, Prev: ACL2-PC||REDUCE-BY-INDUCTION, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REMOVE-ABBREVIATIONS (primitive) remove one or more abbreviations Examples: remove-abbreviations -- remove all abbreviations (remove-abbreviations v w) -- assuming that V and W currently abbreviate terms, then they are ``removed'' in the sense that they are no longer considered to abbreviate those terms General Forms: (remove-abbreviations &rest vars) If vars is not empty (i.e., not nil), remove the variables in vars from the current list of abbreviations, in the sense that each variable in vars will no longer abbreviate a term. *Remark:* The instruction fails if at least one of the arguments fails to be a variable that abbreviates a term. See also the documentation for add-abbreviation, which contains a discussion of abbreviations in general, and show-abbreviations.  File: acl2-doc-emacs.info, Node: ACL2-PC||REPEAT, Next: ACL2-PC||REPEAT-REC, Prev: ACL2-PC||REMOVE-ABBREVIATIONS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REPEAT (macro) repeat the given instruction until it "fails" Example: (repeat promote) General Form: (repeat instruction) The given instruction is run repeatedly until it "fails". *Remark:* There is nothing here in general to prevent the instruction from being run after all goals have been proved, though this is indeed the case for primitive instructions.  File: acl2-doc-emacs.info, Node: ACL2-PC||REPEAT-REC, Next: ACL2-PC||REPLAY, Prev: ACL2-PC||REPEAT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REPEAT-REC (macro) auxiliary to repeat See documentation for repeat.  File: acl2-doc-emacs.info, Node: ACL2-PC||REPLAY, Next: ACL2-PC||RESTORE, Prev: ACL2-PC||REPEAT-REC, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REPLAY (macro) replay one or more instructions Examples: REPLAY -- replay all instructions in the current session (i.e., state-stack) (REPLAY 5) -- replay the most recent 5 instructions (REPLAY 5 (COMMENT deleted dive command here)) -- replace the 5th most recent instruction with the indicated comment instruction, and then replay it followed by the remaining 4 instructions General Form: (REPLAY &OPTIONAL n replacement-instruction) Replay the last n instructions if n is a positive integer; else n should be nil or not supplied, and replay all instructions. However, if replacement-instruction is supplied and not nil, then before the replay, replace the nth instruction (from the most recent, as shown by commands) with replacement-instruction. If this command "fails", then the restore command will revert the state-stack to its value present before the replay instruction was executed.  File: acl2-doc-emacs.info, Node: ACL2-PC||RESTORE, Next: ACL2-PC||RETAIN, Prev: ACL2-PC||REPLAY, Up: PROOF-CHECKER-COMMANDS ACL2-PC||RESTORE (meta) remove the effect of an UNDO command Example and General Form: restore Restore removes the effect of an undo command. This always works as expected if restore is invoked immediately after undo, without intervening instructions. However, other commands may also interact with restore, notably "sequencing" commands such as do-all, do-strict, protect, and more generally, sequence. *Remark:* Another way to control the saving of proof-checker state is with the save command; see the documentation for save. The restore command always "succeeds"; it returns (mv nil t state).  File: acl2-doc-emacs.info, Node: ACL2-PC||RETAIN, Next: ACL2-PC||RETRIEVE, Prev: ACL2-PC||RESTORE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||RETAIN (atomic macro) drop all *but* the indicated top-level hypotheses Example: (RETAIN 2 3) -- keep the second and third hypotheses, and drop the rest General Form: (retain &rest args) Drop all top-level hypotheses *except* those with the indicated indices. There must be at least one argument, and all must be in range (i.e. integers between one and the number of top-level hypotheses, inclusive).  File: acl2-doc-emacs.info, Node: ACL2-PC||RETRIEVE, Next: ACL2-PC||REWRITE, Prev: ACL2-PC||RETAIN, Up: PROOF-CHECKER-COMMANDS ACL2-PC||RETRIEVE (macro) re-enter the proof-checker Examples: (retrieve associativity-of-permutationp) retrieve General Form: (retrieve &optional name) Must be used from outside the interactive proof-checker loop. If name is supplied and not nil, this causes re-entry to the interactive proof-checker loop in the state at which save was last executed for the indicated name. (See documentation for save.) If name is nil or is not supplied, then the user is queried regarding which proof-checker state to re-enter. The query is omitted, however, if there only one proof-checker state is present that was saved with save, in which case that is the one that is used. See also unsave.  File: acl2-doc-emacs.info, Node: ACL2-PC||REWRITE, Next: ACL2-PC||RUN-INSTR-ON-GOAL, Prev: ACL2-PC||RETRIEVE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||REWRITE (primitive) apply a rewrite rule Examples: (rewrite reverse-reverse) -- apply the rewrite rule `reverse-reverse' (rewrite (:rewrite reverse-reverse)) -- same as above (rewrite 2) -- apply the second rewrite rule, as displayed by show-rewrites rewrite -- apply the first rewrite rule, as displayed by show-rewrites (rewrite transitivity-of-< ((y 7))) -- apply the rewrite rule transitivity-of-< with the substitution that associates 7 to the ``free variable'' y (rewrite foo ((x 2) (y 3)) t) -- apply the rewrite rule foo by substituting 2 and 3 for free variables x and y, respectively, and also binding all other free variables possible by using the current context (hypotheses and governors) General Form: (rewrite &optional rule-id substitution instantiate-free) Replace the current subterm with a new term by applying a rewrite or definition rule. The replacement will be done according to the information provided by the show-rewrites (sr) command. See also the linear command, for an analogous command to use with linear rules. If rule-id is a positive integer n, then the nth rule as displayed by show-rewrites is the one that is applied. If rule-id is nil or is not supplied, then it is treated as the number 1. Otherwise, rule-id should be either a symbol or else a :rewrite or :definition rune. If a symbol is supplied, then any (:rewrite or :definition) rule of that name may be used. We say more about this, and describe the other optional arguments, below. Consider first the following example. Suppose that the current subterm is (reverse (reverse y)) and that there is a rewrite rule called reverse-reverse of the form (implies (true-listp x) (equal (reverse (reverse x)) x)) . Then the instruction (rewrite reverse-reverse) causes the current subterm to be replaced by y and creates a new goal with conclusion (true-listp y). An exception is that if the top-level hypotheses imply (true-listp y) using only "trivial reasoning" (more on this below), then no new goal is created. If the rule-id argument is a number or is not supplied, then the system will store an instruction of the form (rewrite name ...), where name is the name of a rewrite rule; this is in order to make it easier to replay instructions when there have been changes to the history. Except: instead of the name (whether the name is supplied or calculated), the system stores the rune if there is any chance of ambiguity. (Formally, "ambiguity" here means that the rune being applied is of the form (:rewrite name . index), where index is not nil.) Speaking in general, then, a rewrite instruction works as follows: First, a rewrite or definition rule is selected according to the arguments of the rewrite instruction. The selection is made as explained under "General Form" above. Next, the left-hand side of the rule is matched with the current subterm, i.e., a substitution unify-subst is found such that if one instantiates the left-hand side of the rule with unify-subst, then one obtains the current subterm. If this match fails, then the instruction fails. Next, an attempt is made to relieve (discharge) the hypotheses, much as the theorem prover relieves hypotheses except that there is no call to the rewriter. First, the substitution unify-subst is extended with the substitution argument, which may bind free variables (see *note FREE-VARIABLES::). Each hypothesis of the rule is then considered in turn, from first to last. For each hypothesis, first the current substitution is applied, and then the system checks whether the hypothesis is "clearly" true in the current context. If there are variables in the hypotheses of the rule that are not bound by the current substitution, then a weak attempt is made to extend that substitution so that the hypothesis is present in the current context (see the documentation for the proof-checker hyps command under proof-checker-commands), much as would be done by the theorem prover's rewriter. If in the process above there are free variables (see *note FREE-VARIABLES::), but the proof-checker can see how to bind them to relieve all hypotheses, then it will do so in both the show-rewrites (sr) and rewrite commands. But normally, if even one hypothesis remains unrelieved, then no automatic extension of the substitution is made. Except, if instantiate-free is not nil, then that extension to the substitution is kept. (Technical note: in the case of an unrelieved hypothesis and a non-nil value of instantiate-free, if a bind-free hypothesis produces a list of binding alists, then the last of those alists is the one that is used to extend the substitution.) Finally, the instruction is applied as follows. The current subterm is replaced by applying the final substitution described above to the right-hand side of the selected rule. And, one new subgoal is created for each unrelieved hypothesis of the rule, whose top-level hypotheses are the governors and top-level hypotheses of the current goal and whose conclusion and current subterm are the instance, by that same final substitution, of that unrelieved hypothesis. *Remark:* The substitution argument should be a list whose elements have the form (variable term), where term may contain abbreviations.  File: acl2-doc-emacs.info, Node: ACL2-PC||RUN-INSTR-ON-GOAL, Next: ACL2-PC||RUN-INSTR-ON-NEW-GOALS, Prev: ACL2-PC||REWRITE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||RUN-INSTR-ON-GOAL (macro) auxiliary to THEN See documentation for then.  File: acl2-doc-emacs.info, Node: ACL2-PC||RUN-INSTR-ON-NEW-GOALS, Next: ACL2-PC||RUNES, Prev: ACL2-PC||RUN-INSTR-ON-GOAL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||RUN-INSTR-ON-NEW-GOALS (macro) auxiliary to then See documentation for then.  File: acl2-doc-emacs.info, Node: ACL2-PC||RUNES, Next: ACL2-PC||S, Prev: ACL2-PC||RUN-INSTR-ON-NEW-GOALS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||RUNES (macro) print the runes (definitions, lemmas, ...) used Examples and general forms: (runes t) ; print all runes used during this interactive proof (runes nil) ; print all runes used by the most recent command (runes) ; same as (runes nil) runes ; same as (runes nil) This command does not change the proof-checker state. Rather, it simply reports runes (see *note RUNE::) that have participated in the interactive proof. Note that (runes nil) will show the runes used by the most recent primitive or macro command (as displayed by :comm).  File: acl2-doc-emacs.info, Node: ACL2-PC||S, Next: ACL2-PC||S-PROP, Prev: ACL2-PC||RUNES, Up: PROOF-CHECKER-COMMANDS ACL2-PC||S (primitive) simplify the current subterm Examples: S -- simplify the current subterm (S :backchain-limit 2 :normalize t :expand (append x z)) -- simplify the current subterm, but during the rewriting process first ``normalize'' it by pushing IFs to the top-level, and also force the term (append x z) to be expanded during the rewriting process General Form: (s &key rewrite normalize backchain-limit repeat in-theory hands-off expand) Simplify the current subterm according to the keyword parameters supplied. First if-normalization is applied (unless the normalize argument is nil), i.e., each subterm of the form (f ... (if test x y) ...) is replaced by the term (if test (f ... x ...) (f ... y ...)) except, of course, when f is if and the indicated if subterm is in the second or third argument position. Then rewriting is applied (unless the rewrite argument is nil). Finally this pair of actions is repeated -- until the rewriting step causes no change in the term. A description of each parameter follows. :rewrite -- default t When non-nil, instructs the system to use ACL2's rewriter (or, something close to it) during simplification. :normalize -- default t When non-nil, instructs the system to use if-normalization (as described above) during simplification. :backchain-limit -- default 0 Sets the number of recursive calls to the rewriter that are allowed for backchaining. Even with the default of 0, some reasoning is allowed (technically speaking, type-set reasoning is allowed) in the relieving of hypotheses. The value should be nil or a non-negative integer, and limits backchaining only for rewriting, not for type-set reasoning. :repeat -- default 0 Sets the number of times the current term is to be rewritten. If this value is t, then the default is used (as specified by the constant *default-s-repeat-limit*). :in-theory, :hands-off, :expand These have their usual meaning; see *note HINTS::. *Remark:* if conditional rewrite rules are used that cause case splits because of the use of force, then appropriate new subgoals will be created, i.e., with the same current subterm (and address) but with each new (forced) hypothesis being negated and then used to create a corresponding new subgoal. In that case, the current goal will have all such new hypotheses added to the list of top-level hypotheses.  File: acl2-doc-emacs.info, Node: ACL2-PC||S-PROP, Next: ACL2-PC||SAVE, Prev: ACL2-PC||S, Up: PROOF-CHECKER-COMMANDS ACL2-PC||S-PROP (atomic macro) simplify propositionally Example: s-prop General Form: (s-prop &rest names) Simplify, using the default settings for s (which include if-normalization and rewriting without real backchaining), but with respect to a theory in which only basic functions and rules (the ones in (theory 'minimal-theory)), together with the names (or parenthesized names) in the &rest argument names, are enabled. See also the documentation for s.  File: acl2-doc-emacs.info, Node: ACL2-PC||SAVE, Next: ACL2-PC||SEQUENCE, Prev: ACL2-PC||S-PROP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SAVE (macro) save the proof-checker state (state-stack) Example: (save lemma3-attempt) General Form: (save &optional name do-it-flg) Saves the current proof-checker state by "associating" it with the given name. Submit (retrieve name) to Lisp to get back to this proof-checker state. If verify was originally supplied with an event name, then the argument can be omitted in favor of that name as the default. *Remark* that if a save has already been done with the indicated name (or the default event name), then the user will be queried regarding whether to go ahead with the save -- except, if do-it-flg is supplied and not nil, then there will be no query and the save will be effected. See also the documentation for retrieve and unsave.  File: acl2-doc-emacs.info, Node: ACL2-PC||SEQUENCE, Next: ACL2-PC||SHOW-ABBREVIATIONS, Prev: ACL2-PC||SAVE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SEQUENCE (meta) run the given list of instructions according to a multitude of options Example: (sequence (induct p prove) t) See also the definitions of commands do-all, do-strict, protect, and succeed. General Form: (sequence instruction-list &optional strict-flg protect-flg success-expr no-prompt-flg no-restore-flg) Each instruction in the list instruction-list is run, and the instruction "succeeds" if every instruction in instruction-list "succeeds". However, it might "succeed" even if some instructions in the list "fail"; more generally, the various arguments control a number of aspects of the running of the instructions. All this is explained in the paragraphs below. First we embark on a general discussion of the instruction interpreter, including the notions of "succeed" and "fail". *Remark:* The arguments are *not* evaluated, except (in a sense) for success-expr, as described below. Each primitive and meta instruction can be thought of as returning an error triple, say (erp val state); see *note ERROR-TRIPLES::. An instruction (primitive or meta) "succeeds" if erp is nil and val is not nil; otherwise it "fails". (When we use the words "succeed" or "fail" in this technical sense, we'll always include them in double quotes.) If an instruction "fails," we say that that the failure is "soft" if erp is nil; otherwise the failure is "hard". The sequence command gives the user control over how to treat "success" and "failure" when sequencing instructions, though we have created a number of handy macro commands for this purpose, notably do-all, do-strict and protect. Here is precisely what happens when a sequence instruction is run. The instruction interpreter is run on the instructions supplied in the argument instruction-list (in order). The interpreter halts the first time there is a hard "failure." except that if strict-flg is supplied and not nil, then the interpreter halts the first time there is any "failure." The error triple (erp val state) returned by the sequence instruction is the triple returned by the last instruction executed (or, the triple (nil t state) if instruction-list is nil), except for the following provision. If success-expr is supplied and not nil, then it is evaluated with the state global variables pc-erp and pc-val (in the "ACL2" package) bound to the corresponding components of the error triple returned (as described above). At least two values should be returned, and the first two of these will be substituted for erp and val in the triple finally returned by sequence. For example, if success-expr is (mv erp val), then no change will be made to the error triple, and if instead it is (mv nil t), then the sequence instruction will "succeed". That concludes the description of the error triple returned by a sequence instruction, but it remains to explain the effects of the arguments protect-flg and no-prompt-flg. If protect-flg is supplied and not nil and if also the instruction "fails" (i.e., the error component of the triple is not nil or the value component is nil), then the state is reverted so that the proof-checker's state (including the behavior of restore) is set back to what it was before the sequence instruction was executed. Otherwise, unless no-restore-flg is set, the state is changed so that the restore command will now undo the effect of this sequence instruction (even if there were nested calls to sequence). Finally, as each instruction in instruction-list is executed, the prompt and that instruction will be printed, unless the global state variable pc-print-prompt-and-instr-flg is unbound or nil and the parameter no-prompt-flg is supplied and not nil.  File: acl2-doc-emacs.info, Node: ACL2-PC||SHOW-ABBREVIATIONS, Next: ACL2-PC||SHOW-LINEARS, Prev: ACL2-PC||SEQUENCE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SHOW-ABBREVIATIONS (macro) display the current abbreviations Examples: (show-abbreviations v w) -- assuming that v and w currently abbreviate terms, then this instruction displays them together with the terms they abbreviate show-abbreviations -- display all abbreviations See also add-abbreviation and remove-abbreviations. In particular, the documentation for add-abbreviation contains a general discussion of abbreviations. General Form: (show-abbreviations &rest vars) Display each argument in vars together with the term it abbreviates (if any). If there are no arguments, i.e. the instruction is simply show-abbreviations, then display all abbreviations together with the terms they abbreviate. If the term abbreviated by a variable, say v, contains a proper subterm that is also abbreviate by (another) variable, then both the unabbreviated term and the abbreviated term (but not using (? v) to abbreviate the term) are displayed with together with v.  File: acl2-doc-emacs.info, Node: ACL2-PC||SHOW-LINEARS, Next: ACL2-PC||SHOW-REWRITES, Prev: ACL2-PC||SHOW-ABBREVIATIONS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SHOW-LINEARS (macro) display the applicable linear rules Example: show-linears General Form: (show-linears &optional rule-id enabled-only-flg) This command displays linear rules with a trigger term that matches the current subterm, and shows how they can be applied. This command is analogous to the show-rewrites proof-checker command; see its documentation for details. Also see the documentation for proof-checker command apply-linear for how to apply linear rules.  File: acl2-doc-emacs.info, Node: ACL2-PC||SHOW-REWRITES, Next: ACL2-PC||SHOW-TYPE-PRESCRIPTIONS, Prev: ACL2-PC||SHOW-LINEARS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SHOW-REWRITES (macro) display the applicable rewrite rules Example: show-rewrites General Form: (show-rewrites &optional rule-id enabled-only-flg) This command displays rewrite rules whose left-hand side matches the current subterm, and shows how that command can be applied. For each rule displayed, hypotheses are shown that would need to be proved after the rule is applied. Note that hypotheses are omitted from the display when the system can trivially verify that they hold; to see all hypotheses for each rule in a display that is independent of the arguments of the current subterm, use the pl or pr command. Here are details on the arguments and the output. If rule-id is supplied and is a name (non-nil symbol) or a :rewrite or :definition rune, then only the corresponding rewrite rule(s) will be displayed, while if rule-id is a positive integer n, then only the nth rule that would be in the list is displayed. In each case, the display will point out when a rule is currently disabled (in the interactive environment), except that if enabled-only-flg is supplied and not nil, then disabled rules will not be displayed at all. Finally, among the free variables of any rule (see *note FREE-VARIABLES::), those that would remain free if the rule were applied will be displayed. Also see *note REWRITE::.  File: acl2-doc-emacs.info, Node: ACL2-PC||SHOW-TYPE-PRESCRIPTIONS, Next: ACL2-PC||SKIP, Prev: ACL2-PC||SHOW-REWRITES, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SHOW-TYPE-PRESCRIPTIONS (macro) display the applicable type-prescription rules Example: show-type-prescriptions General Form: (show-type-prescriptions &optional rule-id) Display type-prescription rules that apply to the current subterm. If rule-id is supplied and is a name (non-nil symbol) or a :rewrite or :definition rune, then only the corresponding rewrite rule(s) will be displayed. In each case, the display will point out when a rule is currently disabled (in the interactive environment). Also see *note TYPE-PRESCRIPTION::.  File: acl2-doc-emacs.info, Node: ACL2-PC||SKIP, Next: ACL2-PC||SL, Prev: ACL2-PC||SHOW-TYPE-PRESCRIPTIONS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SKIP (macro) "succeed" without doing anything Example and General Form: skip Make no change in the state-stack, but "succeed". Same as (sequence nil).  File: acl2-doc-emacs.info, Node: ACL2-PC||SL, Next: ACL2-PC||SLS, Prev: ACL2-PC||SKIP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SL (atomic macro) simplify with lemmas Examples: sl (sl 3) General Form: (sl &optional backchain-limit) Simplify, but with all function definitions disabled (see *note FUNCTION-THEORY:: in the top-level ACL2 loop), except for a few basic functions (the ones in (theory 'minimal-theory)). The backchain-limit has a default of 0, but if is supplied and not nil, then it should be a nonnegative integer; see the documentation for s. WARNING: This command completely ignores in-theory commands that are executed inside the proof-checker.  File: acl2-doc-emacs.info, Node: ACL2-PC||SLS, Next: ACL2-PC||SPLIT, Prev: ACL2-PC||SL, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SLS (macro) same as SHOW-LINEARS Example: srs General Form: (srs &optional rule-id enabled-only-flg) See the documentation for show-linears, as sls and show-linears are identical. NOTE: In analogy to the sr abbreviation for show-rewrites, one might expect this command to be sl; but that name was taken ("simplify with lemmas") before sls was implemented.  File: acl2-doc-emacs.info, Node: ACL2-PC||SPLIT, Next: ACL2-PC||SR, Prev: ACL2-PC||SLS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SPLIT (atomic macro) split the current goal into cases Example: split For example, if the current goal has one hypothesis (or x y) and a conclusion of (and a b), then split will create four new goals: one with hypothesis X and conclusion A one with hypothesis X and conclusion B one with hypothesis Y and conclusion A one with hypothesis Y and conclusion B. General Form: SPLIT Replace the current goal by subgoals whose conjunction is equivalent (primarily by propositional reasoning) to the original goal, where each such goal cannot be similarly split. *Remark:* The new goals will all have their hypotheses promoted; in particular, no conclusion will have a top function symbol of implies. Also note that split will fail if there is exactly one new goal created and it is the same as the existing current goal. The way split really works is to call the ACL2 theorem prover with only simplification (and preprocessing) turned on, and with only a few built-in functions (especially, propositional ones) enabled, namely, the ones in the list (theory 'minimal-theory). However, because the prover is called, type-set reasoning can be used to eliminate some cases. For example, if (true-listp x) is in the hypotheses, then probably (true-listp (cdr x)) will be reduced to t.  File: acl2-doc-emacs.info, Node: ACL2-PC||SR, Next: ACL2-PC||ST, Prev: ACL2-PC||SPLIT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SR (macro) same as SHOW-REWRITES Example: sr General Form: (sr &optional rule-id enabled-only-flg) See the documentation for show-rewrites, as sr and show-rewrites are identical.  File: acl2-doc-emacs.info, Node: ACL2-PC||ST, Next: ACL2-PC||SUCCEED, Prev: ACL2-PC||SR, Up: PROOF-CHECKER-COMMANDS ACL2-PC||ST (macro) same as SHOW-TYPE-PRESCRIPTIONS Example: sr General Form: (st &optional rule-id) See the documentation for show-type-prescriptions, as st and show-type-prescriptions are identical.  File: acl2-doc-emacs.info, Node: ACL2-PC||SUCCEED, Next: ACL2-PC||TH, Prev: ACL2-PC||ST, Up: PROOF-CHECKER-COMMANDS ACL2-PC||SUCCEED (macro) run the given instructions, and "succeed" Example: (succeed induct p prove) General Form: (succeed &rest instruction-list) Run the indicated instructions until there is a hard "failure", and "succeed". (See the documentation for sequence for an explanation of "success" and "failure".)  File: acl2-doc-emacs.info, Node: ACL2-PC||TH, Next: ACL2-PC||THEN, Prev: ACL2-PC||SUCCEED, Up: PROOF-CHECKER-COMMANDS ACL2-PC||TH (macro) print the top-level hypotheses and the current subterm Examples: th -- print all (top-level) hypotheses and the current subterm (th (1 3) (2 4)) -- print hypotheses 1 and 3 and governors 2 and 4, and the current subterm (th (1 3) t) -- print hypotheses 1 and 3 and all governors, and the current subterm General Form: (th &optional hyps-indices govs-indices) Print hypotheses and the current subterm. The printing of hypotheses (and perhaps governors) are controlled as in the hyps command; see its documentation. Historical note: The name th is adapted from the Gypsy Verification Environment, where th abbreviates the command theorem, which says to print information on the current goal.  File: acl2-doc-emacs.info, Node: ACL2-PC||THEN, Next: ACL2-PC||TOP, Prev: ACL2-PC||TH, Up: PROOF-CHECKER-COMMANDS ACL2-PC||THEN (macro) apply one instruction to current goal and another to new subgoals Example: (then induct prove) General Form: (then first-instruction &optional completion must-succeed-flg) Run first-instruction, and then run completion (another instruction) on each subgoal created by first-instruction. If must-succeed-flg is supplied and not nil, then halt at the first "failure" and remove the effects of the invocation of completion that "failed". The default for completion is reduce.  File: acl2-doc-emacs.info, Node: ACL2-PC||TOP, Next: ACL2-PC||TYPE-ALIST, Prev: ACL2-PC||THEN, Up: PROOF-CHECKER-COMMANDS ACL2-PC||TOP (atomic macro) move to the top of the goal Example and General Form: top For example, if the conclusion is (= x (* (- y) z)) and the current subterm is y, then after executing top, the current subterm will be the same as the conclusion, i.e., (= x (* (- y) z)). Top is the same as (up n), where n is the number of times one needs to execute up in order to get to the top of the conclusion. The top command fails if one is already at the top of the conclusion. See also up, dive, nx, and bk.  File: acl2-doc-emacs.info, Node: ACL2-PC||TYPE-ALIST, Next: ACL2-PC||UNDO, Prev: ACL2-PC||TOP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||TYPE-ALIST (macro) display the type-alist from the current context Examples: (type-alist t t) ; display type-alist based on conclusion and governors (type-alist t t t) ; as above, but also display forward-chaining report type-alist ; same as (type-alist nil t) -- governors only (type-alist nil) ; same as (type-alist nil t) -- governors only (type-alist t) ; same as (type-alist t nil) -- conclusion only (type-alist nil nil) ; display type-alist without considering ; conclusion or governors General Form: (type-alist &optional concl-flg govs-flg fc-report-flg) where if govs-flg is omitted then it defaults to (not concl-flg), and concl-flg and fc-report-flg default to nil. Display the current assumptions as a type-alist. Note that this display includes the result of forward chaining. When fc-report-flg is supplied a non-nil value, the display also includes a forward-chaining report; otherwise,the presence or absence of such a report is controlled by the usual global settings (see *note FORWARD-CHAINING-REPORTS::). There are two basic reasons contemplated for using this command. 1. The theorem prover has failed (either outside the proof-checker or using a proof-checker command such as bash or reduce and you want to debug by getting an idea of what the prover knows about the context. a. You really are interested in the context for the current term. Include hypotheses and governors (i.e., accounting for tests of surrounding if-expressions that must be true or false) but not the current conclusion (which the theorem prover's heuristics would generally ignore for contextual information). Command: (type-alist nil t) ; equivalently, type-alist or (type-alist nil) b. You are not thinking in particular about the current term; you just want to get an idea of the context that the prover would build at the top-level, for forward-chaining. Incorporate the conclusion but not the governors. Command: (type-alist t nil) ; equivalently, (type-alist t) 2. You intend to use one of the proof-checker-commands that does simplification, such as s or x, and you want to see the context. Then include the surrounding if-term governors but not the goal's conclusion. Command: (type-alist nil t) ; equivalently, type-alist or (type-alist nil) See *note TYPE-SET:: (also see *note TYPE-PRESCRIPTION::) for information about ACL2's type system, which can assist in understanding the output of the type-alist command.  File: acl2-doc-emacs.info, Node: ACL2-PC||UNDO, Next: ACL2-PC||UNSAVE, Prev: ACL2-PC||TYPE-ALIST, Up: PROOF-CHECKER-COMMANDS ACL2-PC||UNDO (meta) undo some instructions Examples: (undo 7) undo General Forms: (undo n) -- Undo the last n instructions. The argument n should be a positive integer. undo -- Same as (undo 1). *Remark:* To remove the effect of an undo command, use restore. See the documentation for details. *Remark:* If the argument n is greater than the total number of interactive instructions in the current session, then (undo n) will simply take you back to the start of the session. The undo meta command always "succeeds"; it returns (mv nil t state) unless its optional argument is supplied and of the wrong type (i.e. not a positive integer) or there are no instructions to undo.  File: acl2-doc-emacs.info, Node: ACL2-PC||UNSAVE, Next: ACL2-PC||UP, Prev: ACL2-PC||UNDO, Up: PROOF-CHECKER-COMMANDS ACL2-PC||UNSAVE (macro) remove a proof-checker state Example: (unsave assoc-of-append) General Form: (unsave &optional name) Eliminates the association of a proof-checker state with name, if name is supplied and not nil. The name may be nil or not supplied, in which case it defaults to the event name supplied with the original call to verify (if there is one -- otherwise, the instruction "fails" and there is no change). The ACL2 function unsave may also be executed outside the interactive loop, with the same syntax. See also documentation for save and retrieve.  File: acl2-doc-emacs.info, Node: ACL2-PC||UP, Next: ACL2-PC||USE, Prev: ACL2-PC||UNSAVE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||UP (primitive) move to the parent (or some ancestor) of the current subterm Examples: if the conclusion is (= x (* (- y) z)) and the current subterm is y, then we have: up or (up 1) -- the current subterm becomes (- y) (up 2) -- the current subterm becomes (* (- y) z) (up 3) -- the current subterm becomes the entire conclusion (up 4) -- no change; can't go up that many levels General Form: (up &optional n) Move up n levels in the conclusion from the current subterm, where n is a positive integer. If n is not supplied or is nil, then move up 1 level, i.e., treat the instruction as (up 1). See also dive, top, nx, and bk.  File: acl2-doc-emacs.info, Node: ACL2-PC||USE, Next: ACL2-PC||WRAP, Prev: ACL2-PC||UP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||USE (atomic macro) use a lemma instance Example: (USE true-listp-append (:instance assoc-of-append (x a) (y b) (z c))) -- Add two top-level hypotheses, one the lemma called true-listp-append, and the other an instance of the lemma called assoc-of-append by the substitution in which x is assigned a, y is assigned b, and z is assigned c. General Form: (use &rest args) Add the given lemma instances to the list of top-level hypotheses. See *note HINTS:: for the syntax of :use hints in defthm, which is essentially the same as the syntax here (see the example above). This command calls the prove command, and hence should only be used at the top of the conclusion.  File: acl2-doc-emacs.info, Node: ACL2-PC||WRAP, Next: ACL2-PC||WRAP-INDUCT, Prev: ACL2-PC||USE, Up: PROOF-CHECKER-COMMANDS ACL2-PC||WRAP (atomic macro) execute the indicated instructions and combine all the new goals Example: (wrap induct) ; induct, then replace first new goal by the conjunction of all ; the new goals, and drop all new goals after the first General Form: (wrap &rest instrs) First the instructions in instrs are executed, as in do-all. If this "fails" then no additional action is taken. Otherwise, the current goal after execution of instrs is conjoined with all "new" goals, in the sense that their names are not among the names of goals at the time instrs was begun. This conjunction becomes the new current goal and those "new" goals are dropped. See the code for the proof-checker command wrap-induct for an example of the use of wrap.  File: acl2-doc-emacs.info, Node: ACL2-PC||WRAP-INDUCT, Next: ACL2-PC||WRAP1, Prev: ACL2-PC||WRAP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||WRAP-INDUCT (atomic macro) same as induct, but create a single goal Examples: wrap-induct (wrap-induct t) (wrap-induct (append (reverse x) y)) General Form: (wrap-induct &optional term) The wrap-induct command is identical to the proof-checker induct command, except that only a single goal is created: the conjunction of the base and induction steps. Note: The output will generally indicate that more than goal has been created, e.g.: Creating two new goals: (MAIN . 1) and (MAIN . 2). However, wrap-induct always creates a unique goal (when it succeeds). A subsequent message clarifies this, for example: NOTE: Created ONLY one new goal, which is the current goal: (MAIN . 1)  File: acl2-doc-emacs.info, Node: ACL2-PC||WRAP1, Next: ACL2-PC||X, Prev: ACL2-PC||WRAP-INDUCT, Up: PROOF-CHECKER-COMMANDS ACL2-PC||WRAP1 (primitive) combine goals into a single goal Examples: ; Keep (main . 1) and (main . 2) if they exist, as well as the current goal; ; and for each other goal, conjoin it into the current goal and delete it: (wrap1 ((main . 1) (main . 2))) ; As explained below, conjoin all subsequent siblings of the current goal ; into the current goal, and then delete them: (wrap1) General Form: (wrap1 &optional kept-goal-names) If kept-goal-names is not nil, the current goal is replaced by conjoining it with all goals other than the current goal and those indicated by kept-goal-names, and those other goals are deleted. If kept-goal-names is omitted, then the the current goal must be of the form (name . n), and the goals to conjoin into the current goal (and delete) are those with names of the form (name . k) for k >= n. NOTE: Wrap1 always "succeeds", even if there are no other goals to conjoin into the current goal (a message is printed in that case), and it always leaves you with no hypotheses at the top of the current goal's conclusion (as though top and demote had been executed, if necessary). Also see proof-checker documentation for wrap (see *note PROOF-CHECKER-COMMANDS::).  File: acl2-doc-emacs.info, Node: ACL2-PC||X, Next: ACL2-PC||X-DUMB, Prev: ACL2-PC||WRAP1, Up: PROOF-CHECKER-COMMANDS ACL2-PC||X (atomic macro) expand and (maybe) simplify function call at the current subterm Examples: x -- expand and simplify. For example, if the current subterm is (append a b), then after x the current subterm will probably be (cons (car a) (append (cdr a) b)) if (consp a) and (true-listp a) are among the top-level hypotheses and governors. If there are no top-level hypotheses and governors, then after x the current subterm will probably be: (if (true-listp x) (if x (cons (car x) (append (cdr x) y)) y) (apply 'binary-append (list x y))). General Form: (X &key rewrite normalize backchain-limit in-theory hands-off expand) Expand the function call at the current subterm, and simplify using the same conventions as with the s command (see documentation for s). Unlike s, it is permitted to set both :rewrite and :normalize to nil, which will result in no simplification; see x-dumb. *Remark* (obscure): On rare occasions the current address may be affected by the use of x. For example, suppose we have the definition (defun g (x) (if (consp x) x 3)) and then we enter the proof-checker with (verify (if (integerp x) (equal (g x) 3) t)) . Then after invoking the instruction (dive 2 1), so that the current subterm is (g x), followed by the instruction x, we would expect the conclusion to be (if (integerp x) (equal 3 3) t). However, the system actually replaces (equal 3 3) with t (because we use the ACL2 term-forming primitives), and hence the conclusion is actually (if (integerp x) t t). Therefore, the current address is put at (2) rather than (2 1). In such cases, a warning "NOTE" will be printed to the terminal. The other primitive commands to which the above "truncation" note applies are equiv, rewrite, and s.  File: acl2-doc-emacs.info, Node: ACL2-PC||X-DUMB, Prev: ACL2-PC||X, Up: PROOF-CHECKER-COMMANDS ACL2-PC||X-DUMB (atomic macro) expand function call at the current subterm, without simplifying General Form: x-dumb: expand without simplification. Same as (expand t new-goals-flg keep-all-guards-flg). See documentation for expand. See also x, which allows simplification.  File: acl2-doc-emacs.info, Node: RETRIEVE, Next: TOGGLE-PC-MACRO, Prev: PROOF-CHECKER-COMMANDS, Up: PROOF-CHECKER RETRIEVE re-enter a (specified) proof-checker state Examples: (retrieve associativity-of-permutationp) retrieve General Form: (retrieve &optional name) See *note ACL2-PC||RETRIEVE::, or use (help retrieve) inside the interactive proof-checker loop. Also see *note UNSAVE::.  File: acl2-doc-emacs.info, Node: TOGGLE-PC-MACRO, Next: UNSAVE, Prev: RETRIEVE, Up: PROOF-CHECKER TOGGLE-PC-MACRO change an ordinary macro command to an atomic macro, or vice-versa Example: (toggle-pc-macro pro) Change pro from an atomic macro command to an ordinary one (or vice-versa, if pro happens to be an ordinary macro command) General Form: (toggle-pc-macro name &optional new-tp) If name is an atomic macro command then this turns it into an ordinary one, and vice-versa. However, if new-tp is supplied and not nil, then it should be the new type (the symbol macro or atomic-macro, in any package), or else there is no change.  File: acl2-doc-emacs.info, Node: UNSAVE, Next: VERIFY, Prev: TOGGLE-PC-MACRO, Up: PROOF-CHECKER UNSAVE remove a proof-checker state Example: (unsave assoc-of-append) General Form: (unsave name) Eliminates the association of a proof-checker state with name. See *note UNSAVE:: or see *note ACL2-PC||UNSAVE::. Also see *note ACL2-PC||SAVE:: and see *note RETRIEVE::.  File: acl2-doc-emacs.info, Node: VERIFY, Prev: UNSAVE, Up: PROOF-CHECKER VERIFY enter the interactive proof checker For proof-checker command summaries, see *note PROOF-CHECKER::. Examples: (VERIFY (implies (and (true-listp x) (true-listp y)) (equal (append (append x y) z) (append x (append y z))))) -- Attempt to prove the given term interactively. (VERIFY (p x) :event-name p-always-holds :rule-classes (:rewrite :generalize) :instructions ((rewrite p-always-holds-lemma) change-goal)) -- Attempt to prove (p x), where the intention is to call the resulting DEFTHM event by the name p-always-holds, with rule-classes as indicated. The two indicated instructions will be run immediately to start the proof. (VERIFY) -- Re-enter the proof-checker in the state at which is was last left. General Form: (VERIFY &OPTIONAL raw-term &KEY event-name rule-classes instructions) Verify is the function used for entering the proof-checker's interactive loop.  File: acl2-doc-emacs.info, Node: PROOF-TREE, Next: Pages Written Especially for the Tours, Prev: PROOF-CHECKER, Up: Top PROOF-TREE proof tree displays A view of ACL2 proofs may be obtained by way of "proof tree displays," which appear in proof output (see *note PROOFS-CO::) when proof-tree output is enabled (see below) When ACL2 starts a proof and proof-tree output is enabled, the proof output begins with the following string. << Starting proof tree logging >> Then for each goal encountered during the proof, a corresponding proof tree display (as described below) is printed into the proof output: first the characters in the constant string *proof-tree-start-delimiter* are printed, then the proof tree display, and finally the characters in the constant string *proof-tree-end-delimiter*. External tools may present proof tree displays in a separate window. In particular, a tool distributed with the ACL2 community books customizes the emacs environment to provide window-based proof tree displays together with commands for traversing the proof transcript; see the discussion of "ACL2 proof-tree support" in file emacs/emacs-acl2.el distributed with ACL2. The command :start-proof-tree enables proof-tree output, while :stop-proof-tree disables proof-tree output; see *note START-PROOF-TREE:: and see *note STOP-PROOF-TREE::. * Menu: * CHECKPOINT-FORCED-GOALS:: Cause forcing goals to be checkpointed in proof trees * PROOF-TREE-DETAILS:: proof tree details not covered elsewhere * PROOF-TREE-EXAMPLES:: proof tree example * START-PROOF-TREE:: start displaying proof trees during proofs * STOP-PROOF-TREE:: stop displaying proof trees during proofs Here is an example of a proof tree display, with comments. Lines marked with "c" are considered "checkpoints," i.e., goals whose scrutiny may be of particular value. ( DEFTHM PLUS-TREE-DEL ...) ;currently proving PLUS-TREE-DEL 1 Goal preprocess ;"Goal" creates 1 subgoal by preprocessing 2 | Goal' simp ;"Goal'" creates 2 subgoals by simplification c 0 | | Subgoal 2 PUSH *1 ;"Subgoal 2" pushes "*1" for INDUCT ++++++++++++++++++++++++++++++ ;first pass thru waterfall completed c 6 *1 INDUCT ;Proof by induction of "*1" has | <5 more subgoals> ; created 6 top-level subgoals. At ; this point, one of those 6 has been ; proved, and 5 remain to be proved. ; We are currently working on the ; first of those 5 remaining goals. See *note PROOF-TREE-EXAMPLES:: for many examples that contain proof tree displays. But first, we summarize the kinds of lines that may appear in a proof tree display. The simplest form of a proof tree display is a header showing the current event, followed by list of lines, each having one of the following forms. n ... Says that the indicated goal created n subgoals using the indicated process. Here "..." refers to possible additional information. c n ... As above, but calls attention to the fact that this goal is a "checkpoint" in the sense that it may be of particular interest. Some displays may overwrite "c" with ">" to indicate the current checkpoint being shown in the proof transcript. | ... | | Indicates that the goal just above this line, which is pointed to by the rightmost vertical bar ("|"), has k subgoals, none of which have yet been processed. | ... | | As above, except that some subgoals have already been processed. ++++++++++++++++++++++++++++++ Separates successive passes through the "waterfall". Thus, this "fencepost" mark indicates the start of a new proof by induction or of a new forcing round. See *note PROOF-TREE-EXAMPLES:: for detailed examples. See *note CHECKPOINT-FORCED-GOALS:: to learn how to mark goals as checkpoints that force the creation of goals in forcing rounds. Finally, see *note PROOF-TREE-DETAILS:: for some points not covered elsewhere.  File: acl2-doc-emacs.info, Node: CHECKPOINT-FORCED-GOALS, Next: PROOF-TREE-DETAILS, Prev: PROOF-TREE, Up: PROOF-TREE CHECKPOINT-FORCED-GOALS Cause forcing goals to be checkpointed in proof trees Example forms: (checkpoint-forced-goals t) (checkpoint-forced-goals nil) Also see *note PROOF-TREE::. By default, goals are not marked as checkpoints by a proof tree display (as described elsewhere; see *note PROOF-TREE::) merely because they force some hypotheses, thus possibly contributing to a forcing round. However, some users may want such behavior, which will occur once the command (checkpoint-forced-goals t) has been executed. To return to the default behavior, use the command (checkpoint-forced-goals nil).  File: acl2-doc-emacs.info, Node: PROOF-TREE-DETAILS, Next: PROOF-TREE-EXAMPLES, Prev: CHECKPOINT-FORCED-GOALS, Up: PROOF-TREE PROOF-TREE-DETAILS proof tree details not covered elsewhere See *note PROOF-TREE:: for an introduction to proof trees, and for a list of related topics. Here we present some details not covered elsewhere. 1. When proof tree display is enabled (because the command :stop-proof-tree has not been executed, or has been superseded by a later :start-proof-tree command), then time summaries will include the time for proof tree display. This time includes the time spent computing with proof trees, such as the pruning process described briefly above. Even when proof trees are not displayed, such as when their display is turned off in the middle of a proof, this time will be printed if it is not 0. 2. When a goal is given a :bye in a proof (see *note HINTS::), it is treated for the purpose of proof tree display just as though it had been proved. 3. Several state global variables affect proof tree display. (@ proof-tree-indent) is initially the string "| ": it is the string that is laid down the appropriate number of times to effect indentation. (@ proof-tree-buffer-width) is initially the value of (fmt-soft-right-margin state), and is used to prevent printing of the annotation "(forced ...)" in any greater column than this value. However, (assign proof-tree-buffer-width nil) to avoid any such suppression. Finally, (@ checkpoint-processors) is a list of processors from the constant list *preprocess-clause-ledge*, together with :induct. You may remove elements of (@ checkpoint-processors) to limit which processes are considered checkpoints, but note that this can affect what is printed by gag-mode (see *note SET-GAG-MODE::). 4. When :otf-flg is not set to t in a proof, and the prover then decides to revert to the original goal and prove it by induction, the proof tree display will reflect this fact as shown here: c 0 | | Subgoal 2 PUSH (reverting) 5. The usual failure message is printed as part of the prooftree display when a proof has failed.  File: acl2-doc-emacs.info, Node: PROOF-TREE-EXAMPLES, Next: START-PROOF-TREE, Prev: PROOF-TREE-DETAILS, Up: PROOF-TREE PROOF-TREE-EXAMPLES proof tree example See *note PROOF-TREE:: for an introduction to proof trees, and for a list of related topics. Here we present a detailed example followed by a shorter example that illustrates proof by induction. Consider the guard proof for the definition of a function cancel_equal_plus; the body of this definition is of no importance here. The first proof tree display is: ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess | <18 subgoals> This is to be read as follows. At this stage of the proof we have encountered the top-level goal, named "Goal", which generated 18 subgoals using the "preprocess" process. We have not yet begun to work on those subgoals. The corresponding message from the ordinary prover output is: By case analysis we reduce the conjecture to the following 18 conjectures. Note that the field just before the name of the goal ("Goal"), which here contains the number 18, indicates the number of cases (children) created by the goal using the indicated process. This number will remain unchanged as long as this goal is displayed. The next proof tree display is: ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 18 simp | | <1 subgoal> | <17 more subgoals> which indicates that at this point, the prover has used the simplification ("simp") process on Subgoal 18 to create one subgoal ("<1 subgoal>"). The vertical bar ("|") below "Subgoal 18", accompanied by the line below it, signifies that there are 17 siblings of Subgoal 18 that remain to be processed. The next proof tree displayed is: ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 18 simp c 2 | | Subgoal 18' ELIM | | | <2 subgoals> | <17 more subgoals> Let us focus on the fourth line of this display: c 2 | | Subgoal 18' ELIM The "c" field marks this goal as a "checkpoint", i.e., a goal worthy of careful scrutiny. In fact, any goal that creates children by a process other than "preprocess" or "simp" is marked as a checkpoint. In this case, the destructor-elimination ("ELIM") process has been used to create subgoals of this goal. The indentation shows that this goal, Subgoal 18', is a child of Subgoal 18. The number "2" indicates that 2 subgoals have been created (by ELIM). Note that this information is consistent with the line just below it, which says "<2 subgoals>". Finally, the last line of this proof tree display, | <17 more subgoals> is connected by vertical bars ("|") up to the string "Subgoal 18", which suggests that there are 17 immediate subgoals of Goal remaining to process after Subgoal 18. Note that this line is indented one level from the second line, which is the line for the goal named "Goal". The display is intended to suggest that the subgoals of Goal that remain to be proved consist of Subgoal 18 together with 17 more subgoals. The next proof tree display differs from the previous one only in that now, Subgoal 18' has only one more subgoal to be processed. ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 18 simp c 2 | | Subgoal 18' ELIM | | | <1 more subgoal> | <17 more subgoals> Note that the word "more" in "<1 more subgoal>" tells us that there was originally more than one subgoal of Subgoal 18. In fact that information already follows from the line above, which (as previously explained) says that Subgoal 18' originally created 2 subgoals. The next proof tree display occurs when the prover completes the proof of that "1 more subgoal" referred to above. ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess | <17 more subgoals> Then, Subgoal 17 is processed and creates one subgoal, by simplification: ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 1 | Subgoal 17 simp | | <1 subgoal> | <16 more subgoals> ... and so on. Later in the proof one might find the following successive proof tree displays. ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess | <9 more subgoals> ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 9 simp (FORCED) | <8 more subgoals> These displays tell us that Subgoal 9 simplified to t (note that the "0" shows clearly that no subgoals were created), but that some rule's hypotheses were forced. Although this goal is not checkpointed (i.e., no "c" appears on the left margin), one can cause such goals to be checkpointed; see *note CHECKPOINT-FORCED-GOALS::. In fact, the proof tree displayed at the end of the "main proof"(the 0-th forcing round) is as follows. ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 9 simp (FORCED) 0 | Subgoal 8 simp (FORCED) 0 | Subgoal 7 simp (FORCED) 0 | Subgoal 6 simp (FORCED) 0 | Subgoal 4 simp (FORCED) 0 | Subgoal 3 simp (FORCED) This is followed by the following proof tree display at the start of the forcing round. 18 Goal preprocess 0 | Subgoal 9 simp (FORCED [1]Subgoal 4) 0 | Subgoal 8 simp (FORCED [1]Subgoal 6) 0 | Subgoal 7 simp (FORCED [1]Subgoal 1) 0 | Subgoal 6 simp (FORCED [1]Subgoal 3) 0 | Subgoal 4 simp (FORCED [1]Subgoal 5) 0 | Subgoal 3 simp (FORCED [1]Subgoal 2) ++++++++++++++++++++++++++++++ 6 [1]Goal FORCING-ROUND 2 | [1]Subgoal 6 preprocess | | <2 subgoals> | <5 more subgoals> This display shows which goals to "blame" for the existence of each goal in the forcing round. For example, Subgoal 9 is to blame for the creation of [1]Subgoal 4. Actually, there is no real goal named "[1]Goal". However, the line 6 [1]Goal FORCING-ROUND appears in the proof tree display to suggest a "parent" of the six top-level goals in that forcing round. As usual, the numeric field before the goal name contains the original number of children of that (virtual, in this case) goal -- in this case, 6. In our example proof, Subgoal 6 eventually gets proved, without doing any further forcing. At that point, the proof tree display looks as follows. ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 9 simp (FORCED [1]Subgoal 4) 0 | Subgoal 7 simp (FORCED [1]Subgoal 1) 0 | Subgoal 6 simp (FORCED [1]Subgoal 3) 0 | Subgoal 4 simp (FORCED [1]Subgoal 5) 0 | Subgoal 3 simp (FORCED [1]Subgoal 2) ++++++++++++++++++++++++++++++ 6 [1]Goal FORCING-ROUND | <5 more subgoals> Notice that the line for Subgoal 8, 0 | Subgoal 8 simp (FORCED [1]Subgoal 6) no longer appears. That is because the goal [1]Subgoal 6 has been proved, along with all its children; and hence, the proof of Subgoal 8 no longer depends on any further reasoning. The final two proof tree displays in our example are as follows. ( DEFUN CANCEL_EQUAL_PLUS ...) 18 Goal preprocess 0 | Subgoal 7 simp (FORCED [1]Subgoal 1) ++++++++++++++++++++++++++++++ 6 [1]Goal FORCING-ROUND 2 | [1]Subgoal 1 preprocess 1 | | [1]Subgoal 1.1 preprocess 1 | | | [1]Subgoal 1.1' simp c 3 | | | | [1]Subgoal 1.1'' ELIM | | | | | <1 more subgoal> ( DEFUN CANCEL_EQUAL_PLUS ...) <> The explanation for the empty proof tree is simple: once [1]Subgoal 1.1.1 was proved, nothing further remained to be proved. In fact, the much sought-after "Q.E.D." appeared shortly after the final proof tree was displayed. Let us conclude with a final, brief example that illustrates proof by induction. Partway through the proof one might come across the following proof tree display. ( DEFTHM PLUS-TREE-DEL ...) 1 Goal preprocess 2 | Goal' simp c 0 | | Subgoal 2 PUSH *1 | | <1 more subgoal> This display says that in the attempt to prove a theorem called plus-tree-del, preprocessing created the only child Goal' from Goal, and Goal' simplified to two subgoals. Subgoal 2 is immediately pushed for proof by induction, under the name "*1". In fact if Subgoal 1 simplifies to t, then we see the following successive proof tree displays after the one shown above. ( DEFTHM PLUS-TREE-DEL ...) 1 Goal preprocess 2 | Goal' simp c 0 | | Subgoal 2 PUSH *1 ( DEFTHM PLUS-TREE-DEL ...) 1 Goal preprocess 2 | Goal' simp c 0 | | Subgoal 2 PUSH *1 ++++++++++++++++++++++++++++++ c 6 *1 INDUCT | <5 more subgoals> The separator "+++++..." says that we are beginning another trip through the waterfall. In fact this trip is for a proof by induction (as opposed to a forcing round), as indicated by the word "INDUCT". Apparently *1.6 was proved immediately, because it was not even displayed; a goal is only displayed when there is some work left to do either on it or on some goal that it brought (perhaps indirectly) into existence. Once a proof by induction is completed, the "PUSH" line that refers to that proof is eliminated ("pruned"). So for example, when the present proof by induction is completed, the line c 0 | | Subgoal 2 PUSH *1 is eliminated, which in fact causes the lines above it to be eliminated (since they no longer refer to unproved children). Hence, at that point one might expect to see: ( DEFTHM PLUS-TREE-DEL ...) <> However, if the proof by induction of *1 necessitates further proofs by induction or a forcing round, then this "pruning" will not yet be done.  File: acl2-doc-emacs.info, Node: START-PROOF-TREE, Next: STOP-PROOF-TREE, Prev: PROOF-TREE-EXAMPLES, Up: PROOF-TREE START-PROOF-TREE start displaying proof trees during proofs Also see *note PROOF-TREE:: and see *note STOP-PROOF-TREE::. Note that :start-proof-tree works by removing 'proof-tree from the inhibit-output-lst; see *note SET-INHIBIT-OUTPUT-LST::. Explanations of proof tree displays may be found elsewhere: see *note PROOF-TREE::. In a nutshell: :start-proof-tree causes proof tree display to be turned on, once it has been turned off by :stop-proof-tree. Do not attempt to invoke start-proof-tree during an interrupt in the middle of a proof.  File: acl2-doc-emacs.info, Node: STOP-PROOF-TREE, Prev: START-PROOF-TREE, Up: PROOF-TREE STOP-PROOF-TREE stop displaying proof trees during proofs Also see *note PROOF-TREE:: and see *note START-PROOF-TREE::. Note that :stop-proof-tree works by adding 'proof-tree to the inhibit-output-lst; see *note SET-INHIBIT-OUTPUT-LST::. Proof tree displays are explained in the documentation for proof-tree. :Stop-proof-tree causes proof tree display to be turned off. It is permissible to submit the form (stop-proof-tree) during a break. Thus, you can actually turn off proof tree display in the middle of a proof by interrupting ACL2 and submitting the form (stop-proof-tree) in raw Lisp.  File: acl2-doc-emacs.info, Node: Pages Written Especially for the Tours, Next: REAL, Prev: PROOF-TREE, Up: Top Pages Written Especially for the Tours Pages Written Especially for the Tours The ACL2 Home Page is generated from ACL2's online documentation strings. (How else could we achieve the total integration of ACL2's online documentation with the home page?) This page is just an artifact of the structure of our documentation strings: each string must belong to a "major section" of the documentation database. This page is not structured to be used by a person browsing via the Web. It contains, in an arbitrary order, the pages written specificially for the Web user. Furthermore, browsing the pages below via the ACL2 :DOC command or via TexInfo is often unsatisfying because those browsers do not support gif files and the notion of going "back" to a node just visited. If you wish to look at the pages below, we strongly recommend that you do so via a HTML-based Web browser. Indeed, you should simply visit ACL2's Home Page and take one of the Tours. * Menu: * A Flying Tour of ACL2:: A Flying Tour of ACL2 * A Sketch of How the Rewriter Works:: A Sketch of How the Rewriter Works * A Tiny Warning Sign:: A Tiny Warning Sign * A Trivial Proof:: A Trivial Proof * A Typical State:: A Typical State * A Walking Tour of ACL2:: A Walking Tour of ACL2 * ACL2 Characters:: ACL2 Characters * ACL2 Conses or Ordered Pairs:: ACL2 Conses or Ordered Pairs * ACL2 Strings:: ACL2 Strings * ACL2 Symbols:: ACL2 Symbols * ACL2 System Architecture:: ACL2 System Architecture * ACL2 as an Interactive Theorem Prover:: ACL2 as an Interactive Theorem Prover * ACL2 as an Interactive Theorem Prover (cont):: ACL2 as an Interactive Theorem Prover (cont) * ACL2 is an Untyped Language:: ACL2 is an Untyped Language * About Models:: About Models * About Types:: About Types * About the ACL2 Home Page:: About the ACL2 Home Page * About the Admission of Recursive Definitions:: About the Admission of Recursive Definitions * About the Prompt:: About the Prompt * An Example Common Lisp Function Definition:: An Example Common Lisp Function Definition * An Example of ACL2 in Use:: An Example of ACL2 in Use * Analyzing Common Lisp Models:: Analyzing Common Lisp Models * Common Lisp:: Common Lisp * Common Lisp as a Modeling Language:: Common Lisp as a Modeling Language * Conversion:: Conversion to Uppercase * Corroborating Models:: Corroborating Models * Evaluating App on Sample Input:: Evaluating App on Sample Input * Flawed Induction Candidates in App Example:: Flawed Induction Candidates in App Example * Free Variables in Top-Level Input:: Free Variables in Top-Level Input * Functions for Manipulating these Objects:: Functions for Manipulating these Objects * Guards:: Guards * Guessing the Type of a Newly Admitted Function:: Guessing the Type of a Newly Admitted Function * Guiding the ACL2 Theorem Prover:: Guiding the ACL2 Theorem Prover * Hey Wait! Is ACL2 Typed or Untyped(Q):: Hey Wait! Is ACL2 Typed or Untyped? * How Long Does It Take to Become an Effective User(Q):: How Long Does It Take to Become an Effective User? * How To Find Out about ACL2 Functions:: How To Find Out about ACL2 Functions * How To Find Out about ACL2 Functions (cont):: How To Find Out about ACL2 Functions (cont) * Modeling in ACL2:: Modeling in ACL2 * Models in Engineering:: Models in Engineering * Models of Computer Hardware and Software:: Models of Computer Hardware and Software * Name the Formula Above:: Name the Formula Above * Nontautological Subgoals:: Prover output omits some details * Numbers in ACL2:: Numbers in ACL2 * On the Naming of Subgoals:: On the Naming of Subgoals * Other Requirements:: Other Requirements * Overview of the Expansion of ENDP in the Base Case:: Overview of the Expansion of ENDP in the Base Case * Overview of the Expansion of ENDP in the Induction Step:: Overview of the Expansion of ENDP in the Induction Step * Overview of the Final Simplification in the Base Case:: Overview of the Final Simplification in the Base Case * Overview of the Proof of a Trivial Consequence:: Overview of the Proof of a Trivial Consequence * Overview of the Simplification of the Base Case to T:: Overview of the Simplification of the Base Case to T * Overview of the Simplification of the Induction Conclusion:: Overview of the Simplification of the Induction Conclusion * Overview of the Simplification of the Induction Step to T:: Overview of the Simplification of the Induction Step to T * Perhaps:: Perhaps * Popping out of an Inductive Proof:: Popping out of an Inductive Proof * Proving Theorems about Models:: Proving Theorems about Models * Revisiting the Admission of App:: Revisiting the Admission of App * Rewrite Rules are Generated from DEFTHM Events:: Rewrite Rules are Generated from DEFTHM Events * Running Models:: Running Models * Subsumption of Induction Candidates in App Example:: Subsumption of Induction Candidates in App Example * Suggested Inductions in the Associativity of App Example:: Suggested Inductions in the Associativity of App Example * Symbolic Execution of Models:: Symbolic Execution of Models * The Admission of App:: The Admission of App * The Associativity of App:: The Associativity of App * The Base Case in the App Example:: The Base Case in the App Example * The End of the Flying Tour:: The End of the Flying Tour * The End of the Proof of the Associativity of App:: The End of the Proof of the Associativity of App * The End of the Walking Tour:: The End of the Walking Tour * The Event Summary:: The Event Summary * The Expansion of ENDP in the Induction Step (Step 0):: The Expansion of ENDP in the Induction Step (Step 0) * The Expansion of ENDP in the Induction Step (Step 1):: The Expansion of ENDP in the Induction Step (Step 1) * The Expansion of ENDP in the Induction Step (Step 2):: The Expansion of ENDP in the Induction Step (Step 2) * The Falling Body Model:: The Falling Body Model * The Final Simplification in the Base Case (Step 0):: the Final Simplification in the Base Case (Step 0) * The Final Simplification in the Base Case (Step 1):: the Final Simplification in the Base Case (Step 1) * The Final Simplification in the Base Case (Step 2):: the Final Simplification in the Base Case (Step 2) * The Final Simplification in the Base Case (Step 3):: the Final Simplification in the Base Case (Step 3) * The First Application of the Associativity Rule:: The First Application of the Associativity Rule * The Induction Scheme Selected for the App Example:: The Induction Scheme Selected for the App Example * The Induction Step in the App Example:: The Induction Step in the App Example * The Instantiation of the Induction Scheme:: The Instantiation of the Induction Scheme * The Justification of the Induction Scheme:: The Justification of the Induction Scheme * The Proof of the Associativity of App:: The Proof of the Associativity of App * The Q.E.D. Message:: The Q.E.D. Message * The Rules used in the Associativity of App Proof:: The Rules used in the Associativity of App Proof * The Simplification of the Induction Conclusion (Step 0):: the Simplification of the Induction Conclusion (Step 0) * The Simplification of the Induction Conclusion (Step 1):: the Simplification of the Induction Conclusion (Step 1) * The Simplification of the Induction Conclusion (Step 10):: the Simplification of the Induction Conclusion (Step 10) * The Simplification of the Induction Conclusion (Step 11):: the Simplification of the Induction Conclusion (Step 11) * The Simplification of the Induction Conclusion (Step 12):: the Simplification of the Induction Conclusion (Step 12) * The Simplification of the Induction Conclusion (Step 2):: the Simplification of the Induction Conclusion (Step 2) * The Simplification of the Induction Conclusion (Step 3):: the Simplification of the Induction Conclusion (Step 3) * The Simplification of the Induction Conclusion (Step 4):: the Simplification of the Induction Conclusion (Step 4) * The Simplification of the Induction Conclusion (Step 5):: the Simplification of the Induction Conclusion (Step 5) * The Simplification of the Induction Conclusion (Step 6):: the Simplification of the Induction Conclusion (Step 6) * The Simplification of the Induction Conclusion (Step 7):: the Simplification of the Induction Conclusion (Step 7) * The Simplification of the Induction Conclusion (Step 8):: the Simplification of the Induction Conclusion (Step 8) * The Simplification of the Induction Conclusion (Step 9):: the Simplification of the Induction Conclusion (Step 9) * The Summary of the Proof of the Trivial Consequence:: The Summary of the Proof of the Trivial Consequence * The Theorem that App is Associative:: The Theorem that App is Associative * The Time Taken to do the Associativity of App Proof:: The Time Taken to do the Associativity of App Proof * The Tours:: The Tours * The WARNING about the Trivial Consequence:: The WARNING about the Trivial Consequence * Undocumented Topic:: Undocumented Topic * Using the Associativity of App to Prove a Trivial Consequence:: Using the Associativity of App to Prove a Trivial Consequence * What Is ACL2(Q):: What Is ACL2? * What is Required of the User(Q):: What is Required of the User? * What is a Mathematical Logic(Q):: What is a Mathematical Logic? * What is a Mechanical Theorem Prover(Q):: What is a Mechanical Theorem Prover? * What is a Mechanical Theorem Prover(Q) (cont):: What is a Mechanical Theorem Prover? (cont) * You Must Think about the Use of a Formula as a Rule:: You Must Think about the Use of a Formula as a Rule Generally, the topics listed above will not be of use to the ACL2 user.  File: acl2-doc-emacs.info, Node: A Flying Tour of ACL2, Next: A Sketch of How the Rewriter Works, Prev: Pages Written Especially for the Tours, Up: Pages Written Especially for the Tours A Flying Tour of ACL2 A Flying Tour of ACL2 Next on the Flying Tour: *note About the ACL2 Home Page:: On this tour you will learn a little about what ACL2 is for rather than how ACL2 works. At the top and bottom bottom of the "page" there are "flying tour" icons. Click on either icon to go to the next page of the tour. The tour visits the following topics sequentially. But on your first reading, don't navigate through the tour by clicking on these links; they are shown as live links only so that later you can determine what you've visited. Instead, just use the flying tour icons. The Flight Plan * This Documentation * What is ACL2? * Mathematical Logic * Mechanical Theorem Proving * Mathematical Models in General * Mathematical Models of Computing Machines Formalizing Models Running Models Symbolic Execution of Models Proving Theorems about Models * Requirements of ACL2 The User's Skills Training Host System On your first reading, don't explore other links you see in the tour. Some of them lead to the Walking Tour, which you can take coherently when you finish this tour. Others lead into the extensive hyptertext documentation and you are liable to get lost there unless you're trying to answer a specific question. We intend the tour to take about 10 minutes of your time. Next on the Flying Tour: *note About the ACL2 Home Page::  File: acl2-doc-emacs.info, Node: A Sketch of How the Rewriter Works, Next: A Tiny Warning Sign, Prev: A Flying Tour of ACL2, Up: Pages Written Especially for the Tours A Sketch of How the Rewriter Works A Sketch of How the Rewriter Works Below we show the first target term, extracted from the current conjecture. Below it we show the associativity rule. The variables of the rewrite rule are instantiated so that the left-hand side of the rule matches the target: variable term from target a x1 b x2 c (app x3 x4) Then the target is replaced by the instantiated right-hand side of the rule. Sometimes rules have hypotheses. To make a long story short, if the rule has hypotheses, then after matching the left-hand side, the rewriter instantiates the hypotheses and rewrites them recursively. This is called backchaining. If they all rewrite to true, then the target is replaced as above. We discuss the rewriter in more detail in the extended introduction to how to use the theorem prover, see *note INTRODUCTION-TO-THE-THEOREM-PROVER::, which we will recommend you work through after you have finished the two tours.  File: acl2-doc-emacs.info, Node: A Tiny Warning Sign, Next: A Trivial Proof, Prev: A Sketch of How the Rewriter Works, Up: Pages Written Especially for the Tours A Tiny Warning Sign A Tiny Warning Sign This warning sign, which usually appears as "", indicates that the link it marks takes you into ACL2's online documentation. The documentation is a vast graph of documented topics intended to help the _user_ of ACL2 rather than the _potential user_. If you are exploring ACL2's home page to learn about the system, perhaps you should go back rather than follow the link marked with this sign. But you are welcome to explore the online documentation as well. Good luck.  File: acl2-doc-emacs.info, Node: A Trivial Proof, Next: A Typical State, Prev: A Tiny Warning Sign, Up: Pages Written Especially for the Tours A Trivial Proof A Trivial Proof  File: acl2-doc-emacs.info, Node: A Typical State, Next: A Walking Tour of ACL2, Prev: A Trivial Proof, Up: Pages Written Especially for the Tours A Typical State A Typical State Next on the Flying Tour: *note Functions for Manipulating these Objects:: Observe that the states in typical models talk about booleans integers vectors records caches bits symbols arrays stacks files characters strings sequences tables directories These objects are discrete rather than continuous; furthermore they are built incrementally or inductively by repeatedly using primitive operations to put together smaller pieces. The functions we need to manipulate these objects do things like concatenate, reverse, sort, search, count, etc. Next on the Flying Tour: *note Functions for Manipulating these Objects::  File: acl2-doc-emacs.info, Node: A Walking Tour of ACL2, Next: ACL2 Characters, Prev: A Typical State, Up: Pages Written Especially for the Tours A Walking Tour of ACL2 A Walking Tour of ACL2 Next on the Walking Tour: *note Common Lisp:: On this tour you will learn a little more about the ACL2 logic, the theorem prover, and the user interface. This time we will stick with really simple things, such as the associativity of list concatenation. We assume you have taken the Flying Tour but that you did not necessarily follow all the "off-tour" links because we encouraged you not to. With the Walking Tour we encourage you to visit off-tour links -- provided they are not marked with the tiny warning sign (<>). But they are "branches" in the tour that lead to "dead ends." When you reach a dead end, remember to use your browser's Back Button to return to the Walking Tour to continue. When you get to the end of the tour we'll give you a chance to repeat quickly both the Flying and the Walking Tours to visit any off-tour links still of interest. Next on the Walking Tour: *note Common Lisp::  File: acl2-doc-emacs.info, Node: ACL2 Characters, Next: ACL2 Conses or Ordered Pairs, Prev: A Walking Tour of ACL2, Up: Pages Written Especially for the Tours ACL2 Characters ACL2 Characters ACL2 accepts 256 distinct characters, which are the characters obtained by applying the function code-char <> to each integer from 0 to 255. Among these, Common Lisp designates certain ones as *standard-characters*, namely those of the form (code-char n) where n is from 33 to 126, together with #\Newline and #\Space. The actual standard characters may be viewed by evaluating the constant expression *standard-chars*. The standard character constants are written by writing a hash mark followed by a backslash (#\) followed by the character. The function characterp <> recognizes characters. For more details, See *note CHARACTERS:: <>.  File: acl2-doc-emacs.info, Node: ACL2 Conses or Ordered Pairs, Next: ACL2 Strings, Prev: ACL2 Characters, Up: Pages Written Especially for the Tours ACL2 Conses or Ordered Pairs ACL2 Conses or Ordered Pairs The function cons <> creates an ordered pair. Car <> and cdr <> return the first and second components, respectively, of an ordered pair. The function consp <> recognizes ordered pairs. Ordered pairs are used to represent lists and trees. See any Common Lisp documentation for a discussion of how list constants are written and for the many list processing functions available. Also, see *note PROGRAMMING:: <> where we list all the ACL2 primitive functions. Here are some examples of list constants to suggest their syntax. '(a . b) ; a pair whose car is 'a and cdr is 'b '(a . nil) ; a pair whose car is 'a and cdr is nil '(a) ; another way to write the same thing '(a b) ; a pair whose car is 'a and cdr is '(b) '(a b c) ; a pair whose car is 'a and cdr is '(b c) ; i.e., a list of three symbols, a, b, and c. '((a . 1) (b . 2)) ; a list of two pairs It is useful to distinguish "proper" conses from "improper" ones, the former being those cons trees whose right-most branch terminates with nil. A "true list" (see *note TRUE-LISTP:: <>) is either nil or a proper cons. (A b c . 7) is an improper cons and hence not a true list.  File: acl2-doc-emacs.info, Node: ACL2 Strings, Next: ACL2 Symbols, Prev: ACL2 Conses or Ordered Pairs, Up: Pages Written Especially for the Tours ACL2 Strings ACL2 Strings Strings of ACL2 characters are written as sequences of characters delimited by "double quotation marks" ("). To put a double quotation mark in a string (or, any other character such as backslash or newline that seems to cause problems), escape it by preceding it with a backslash (\). The function stringp <> recognizes strings and char <> will fetch the nth character of a string. There are many other primitives for handling strings, such as string< <> for comparing two strings lexicographically. We suggest you See *note PROGRAMMING:: <> where we list all of the primitive ACL2 functions. Alternatively, see any Common Lisp language documentation.  File: acl2-doc-emacs.info, Node: ACL2 Symbols, Next: ACL2 System Architecture, Prev: ACL2 Strings, Up: Pages Written Especially for the Tours ACL2 Symbols ACL2 Symbols Common Lisp's symbols are a data type representing words. They are frequently regarded as atomic objects in the sense that they are not frequently broken down into their constituents. Often the only important properties of symbols is that they are not numbers, characters, strings, or lists and that two symbols are not equal if they look different (!). Examples of symbols include PLUS and SMITH::ABC. All function and variable names in ACL2 are symbols. When symbols are used as constants they must be quoted, as in 'PLUS. The symbol T is commonly used as the Boolean "true." The symbol NIL is commonly used both as the Boolean "false" and as the "empty list." Despite sometimes being called the "empty list" NIL is a symbol not an "empty cons." Unlike other symbols, T and NIL may be used as constants without quoting them. Usually, symbols are written as sequences of alphanumeric characters other than those denoting numbers. Thus, A12, +1A and 1+ are symbols but +12 is a number. Roughly speaking, when symbols are read lower case characters are converted to upper case, so we frequently do not distinguish ABC from Abc or abc. See *note Conversion:: for information about case conversion when symbols are read. However, any character can be used in a symbol, but some characters must be "escaped" to allow the Lisp reader to parse the sequence as a symbol. For example, |Abc| is a symbol whose first character is capitalized and whose remaining characters are in lower case. |An odd duck| is a symbol containing two #\Space characters. See any Common Lisp documentation for the syntactic rules for symbols. Technically, a symbol is a special kind of pair consisting of a package name (which is a string) and a symbol name (which is also a string). (See *note SYMBOL-PACKAGE-NAME:: <> and see *note SYMBOL-NAME:: <>.) The symbol SMITH::ABC is said to be in package "SMITH" and to have the symbol name "ABC". The symbol ABC in package "SMITH" is generally not equal to the symbol ABC in package "JONES". However, it is possible to "import" symbols from one package into another one, but in ACL2 this can only be done when the package is created. (See *note DEFPKG:: <>.) If the current-package <> is "SMITH" then SMITH::ABC may be more briefly written as just ABC. Intern <> "creates" a symbol of a given name in a given package.  File: acl2-doc-emacs.info, Node: ACL2 System Architecture, Next: ACL2 as an Interactive Theorem Prover, Prev: ACL2 Symbols, Up: Pages Written Especially for the Tours ACL2 System Architecture ACL2 System Architecture Next on the Walking Tour: *note Rewrite Rules are Generated from DEFTHM Events:: The user interacts with the theorem prover by giving it definitions, theorems and advice. Most often the advice is about how to store each proved theorem as a rule. Sometimes the advice is about how to prove a specific theorem. The database consists of all the rules ACL2 "knows." It is possible to include in the database all of the rules in some certified file of other events. Such certified files are called books <>. Interesting proofs are usually built on top of many books, some of which are written especially for that problem domain and others of which are about oft-used domains, like arithmetic or list processing. ACL2's distribution includes many books written by users. See the "books" link under the Lemma Libraries and Utilities <> link of the ACL2 home page. Next on the Walking Tour: *note Rewrite Rules are Generated from DEFTHM Events::  File: acl2-doc-emacs.info, Node: ACL2 as an Interactive Theorem Prover, Next: ACL2 as an Interactive Theorem Prover (cont), Prev: ACL2 System Architecture, Up: Pages Written Especially for the Tours ACL2 as an Interactive Theorem Prover ACL2 as an Interactive Theorem Prover The ACL2 theorem prover finds proofs in the ACL2 logic. It can be automatic. But most often the user must help it. The user usually guides ACL2 by suggesting that it first prove key lemmas. Lemmas are just theorems used in the proofs of other theorems.  File: acl2-doc-emacs.info, Node: ACL2 as an Interactive Theorem Prover (cont), Next: ACL2 is an Untyped Language, Prev: ACL2 as an Interactive Theorem Prover, Up: Pages Written Especially for the Tours ACL2 as an Interactive Theorem Prover (cont) ACL2 as an Interactive Theorem Prover (cont) Next on the Walking Tour: *note ACL2 System Architecture:: When ACL2 proves a lemma, it is converted into one or more rules and stored in a database. The theorem prover is rule-driven. By proving lemmas you can configure ACL2 to behave in certain ways when it is trying to prove formulas in a certain problem domain. The expert user can make ACL2 do amazingly "smart" looking things. But it would be wrong to think that ACL2 knows the mathematical content of a formula just because it has proved it. What ACL2 knows -- all ACL2 knows -- is what is encoded in its rules. There are many types of rules (see *note RULE-CLASSES:: <>). Many formulas can be effectively coded as rules. But by the same token, it is possible to encode a formula as a rule that is so ineffective it cannot even prove itself! The way a formula is stored as a rule is entirely up to the user. That is, you determine how ACL2 should use each formula that it proves. The most common kind of rule is the rewrite rule. It is so common that if you don't tell ACL2 how to store a formula, it stores it as a rewrite rule. Next on the Walking Tour: *note ACL2 System Architecture::  File: acl2-doc-emacs.info, Node: ACL2 is an Untyped Language, Next: About Models, Prev: ACL2 as an Interactive Theorem Prover (cont), Up: Pages Written Especially for the Tours ACL2 is an Untyped Language ACL2 is an Untyped Language The example ACL2 !>(app '(a b c) 27) (A B C . 27) illustrates the fact that ACL2's logic is untyped (see *note About Types:: for a brief discussion of the typed versus untyped nature of the logic). The definition of app makes no restriction of the arguments to lists. The definition says that if the first argument satisfies endp <> then return the second argument. In this example, when app has recursed three times down the cdr of its first argument, '(a b c), it reaches the final nil, which satisfies endp, and so 27 is returned. It is naturally consed into the emerging list as the function returns from successive recursive calls (since cons does not require its arguments to be lists, either). The result is an "improper" list, (a b c . 27). You can think of (app x y) as building a binary tree by replacing the right-most tip of the tree x with the tree y.  File: acl2-doc-emacs.info, Node: About Models, Next: About Types, Prev: ACL2 is an Untyped Language, Up: Pages Written Especially for the Tours About Models About Models Next on the Flying Tour: *note Models of Computer Hardware and Software:: ACL2 is used to construct mathematical models of computer hardware and software (i.e., "digital systems"). A mathematical model is a set of mathematical formulas used to predict the behavior of some artifact. The use of mathematical models allows faster and cheaper delivery of better systems. Models need not be complete or perfectly accurate to be useful to the trained engineer. See *note Models in Engineering:: for more discussion of these assertions in an engineering context. Next on the Flying Tour: *note Models of Computer Hardware and Software::  File: acl2-doc-emacs.info, Node: About Types, Next: About the ACL2 Home Page, Prev: About Models, Up: Pages Written Especially for the Tours About Types About Types The universe of ACL2 objects includes objects of many different types. For example, t is a "symbol" and 3 is an "integer." Roughly speaking the objects of ACL2 can be partitioned into the following types: Numbers 3, -22/7, #c(3 5/2) Characters #\A, #\a, #\Space Strings "This is a string." Symbols 'abc, 'smith::abc Conses (or Ordered Pairs) '((a . 1) (b . 2)) When proving theorems it is important to know the types of object returned by a term. ACL2 uses a complicated heuristic algorithm, called type-set <>, to determine what types of objects a term may produce. The user can more or less program the type-set algorithm by proving type-prescription <> rules. ACL2 is an "untyped" logic in the sense that the syntax is not typed: It is legal to apply a function symbol of n arguments to any n terms, regardless of the types of the argument terms. Thus, it is permitted to write such odd expressions as (+ t 3) which sums the symbol t and the integer 3. Common Lisp does not prohibit such expressions. We like untyped languages because they are simple to describe, though proving theorems about them can be awkward because, unless one is careful in the way one defines or states things, unusual cases (like (+ t 3)) can arise. To make theorem proving easier in ACL2, the axioms actually define a value for such terms. The value of (+ t 3) is 3; under the ACL2 axioms, non-numeric arguments to + are treated as though they were 0. You might immediately wonder about our claim that ACL2 is Common Lisp, since (+ t 3) is "an error" (and will sometimes even "signal an error") in Common Lisp. It is to handle this problem that ACL2 has guards. We will discuss guards later in the Walking Tour. However, many new users simply ignore the issue of guards entirely and that is what we recommend for now. You should now return to the Walking Tour.  File: acl2-doc-emacs.info, Node: About the ACL2 Home Page, Next: About the Admission of Recursive Definitions, Prev: About Types, Up: Pages Written Especially for the Tours About the ACL2 Home Page About the ACL2 Home Page Next on the Flying Tour: *note What Is ACL2(Q):: The ACL2 Home Page is integrated into the ACL2 online documentation. Over 4 megabytes of hypertext is available here. The vast majority of the text is user-level documentation. For example, to find out about rewrite <> rules you could click on the link. (If you do that, remember to use your browser's Back Button to come back here.) The tiny warning signs <> mark links that lead out of the introductory-level material and into the user documentation. We advise against following such links upon your first reading of the documentation. At the end of the tours you will have a chance to revisit them quickly to explore alternative paths more fully. Finally, every page contains two icons at the bottom. The ACL2 icon leads you back to the ACL2 Home Page. The Index icon allows you to browse an alphabetical listing of all the topics in ACL2's online documentation. But both icons take you off the main route of the tour. Next on the Flying Tour: *note What Is ACL2(Q)::  File: acl2-doc-emacs.info, Node: About the Admission of Recursive Definitions, Next: About the Prompt, Prev: About the ACL2 Home Page, Up: Pages Written Especially for the Tours About the Admission of Recursive Definitions About the Admission of Recursive Definitions You can't just add any formula as an axiom or definition and expect the logic to stay sound! For example, if we were permitted to define (APP X Y) so that it was equal to (NOT (APP X Y)) then we could prove anything. The purported "definition" of APP must have several properties to be admitted to the logic as a new axiom. The key property a recursive definition must have is that the recursion terminate. This, along with some syntactic criteria, ensures us that there exists a function satisfying the definition. Termination must be proved before the definition is admitted. This is done in general by finding a measure of the arguments of the function and a well-founded relation such that the arguments "get smaller" every time a recursive branch is taken. For app the measure is the "size" of the first argument, x, as determined by the primitive function acl2-count <>. The well-founded relation used in this example is o-p <>, which is the standard ordering on the ordinals less than "epsilon naught." These particular choices for app were made "automatically" by ACL2. But they are in fact determined by various "default" settings. The user of ACL2 can change the defaults or specify a "hint" to the defun <> command to specify the measure and relation. You should now return to the Walking Tour.  File: acl2-doc-emacs.info, Node: About the Prompt, Next: An Example Common Lisp Function Definition, Prev: About the Admission of Recursive Definitions, Up: Pages Written Especially for the Tours About the Prompt About the Prompt The string "ACL2 !>" is the ACL2 prompt. The prompt tells the user that an ACL2 command <>is expected. In addition, the prompt tells us a little about the current state of the ACL2 command interpreter. We explain the prompt briefly below. But first we talk about the command interpreter. An ACL2 command is generally a Lisp expression to be evaluated. There are some unusual commands (such as :q <> for quitting ACL2) which cause other behavior. But most commands are read, evaluated, and then have their results printed. Thus, we call the command interpreter a "read-eval-print loop." The ACL2 command interpreter is named LD <> (after Lisp's "load"). When a command is read, all the symbols in it are converted to uppercase. Thus, typing (defun app ...) is the same as typing (DEFUN APP ...) or (defun App ...). There are ways to force lowercase case characters into symbols but we won't discuss them here. A consequence of Common Lisp's default uppercasing is that you'll see a general lack of concern over the case used when symbols are displayed in this documentation. In addition, symbols "belong" to "packages" which give the user a way to control namespaces. The prompt tells us which package is the default one, namely "ACL2". That means when we call car, for example, we are invoking the standard definition of that symbol. If the packager were "JONES" then car would refer to the definition of that symbol in that package (which may or may not be different depending on what symbols were imported into that package. A command like (defun app (x y) ...) causes ACL2 to evaluate the defun <> function on app, (x y) and .... When that command is evaluated it prints some information to the terminal explaining the processing of the proposed definition. It returns the symbol APP as its value, which is printed by the command interpreter. (Actually, defun is not a function but a macro <> which expands to a form that involves state <>, a necessary precondition to printing output to the terminal and to "changing" the set of axioms. But we do not discuss this further here.) The defun command is an example of a special kind of command called an "event." Events <> are those commands that change the "logical world" by adding such things as axioms or theorems to ACL2's database. See *note WORLD:: <>. But not every command is an event command. A command like (app '(1 2 3) '(4 5 6 7)) is an example of a non-event. It is processed the same general way: the function app is applied to the indicated arguments and the result is printed. The function app does not print anything and does not change the "world." A third kind of command is one that display information about the current logical world or that "roll back" to previous versions of the world. Such commands are called "history" <> commands. What does the ACL2 prompt tell us about the read-eval-print loop? The prompt "ACL2 !>" tells us that the command will be read with current-package <> set to "ACL2", that guard checking (see *note SET-GUARD-CHECKING:: <>) is on ("!"), and that we are at the top-level (there is only one ">"). For more about the prompt, see *note DEFAULT-PRINT-PROMPT:: <>. You should now return to the Walking Tour.  File: acl2-doc-emacs.info, Node: An Example Common Lisp Function Definition, Next: An Example of ACL2 in Use, Prev: About the Prompt, Up: Pages Written Especially for the Tours An Example Common Lisp Function Definition An Example Common Lisp Function Definition Next on the Walking Tour: *note An Example of ACL2 in Use:: Consider the binary trees x and y below. In Lisp, x is written as the list '(A B) or, equivalently, as '(A B . NIL). Similarly, y may be written '(C D E). Suppose we wish to replace the right-most tip of x by the entire tree y. This is denoted (app x y), where app stands for "append". We can define app with: (defun app (x y) ; Concatenate x and y. (declare (type (satisfies true-listp) x)); We expect x to end in NIL. (cond ((endp x) y) ; If x is empty, return y. (t (cons (car x) ; Else, copy first node (app (cdr x) y))))) ; and recur into next. If you defined this function in some Common Lisp, then to run app on the x and y above you could then type (app '(A B) '(C D E)) and Common Lisp will print the result (A B C D E). Next on the Walking Tour: *note An Example of ACL2 in Use::  File: acl2-doc-emacs.info, Node: An Example of ACL2 in Use, Next: Analyzing Common Lisp Models, Prev: An Example Common Lisp Function Definition, Up: Pages Written Especially for the Tours An Example of ACL2 in Use An Example of ACL2 in Use Next on the Walking Tour: *note How To Find Out about ACL2 Functions:: To introduce you to ACL2 we will consider the app function discussed in the Common Lisp page, except we will omit for the moment the declare form, which in ACL2 is called a guard. Guards are arbitrary ACL2 terms that express the "intended domain" of functions. In that sense, guards are akin to type signatures. However, Common Lisp and ACL2 are untyped programming languages: while the language supports several different data types and the types of objects can be determined by predicates at runtime, any type of object may be passed to any function. Thus, guards are "extra-logical." Recognizing both the practical and intellectual value of knowing that your functions are applied to the kinds of objects you intend, ACL2 imposes guards on Common Lisp and provides a means of proving that functions are used as intended. But the story is necessarily complicated and we do not recommend it to the new user. Get used to the fact that any ACL2 function may be applied to any objects and program accordingly. Read about guards later. Here is the definition again (defun app (x y) (cond ((endp x) y) (t (cons (car x) (app (cdr x) y))))) The next few stops along the Walking Tour will show you * how to use the ACL2 documentation, * what happens when the above definition is submitted to ACL2, * what happens when you evaluate calls of app, * what one simple theorem about app looks like, * how ACL2 proves the theorem, and * how that theorem can be used in another proof. Along the way we will talk about the definitional principle, types, the ACL2 read-eval-print loop, and how the theorem prover works. When we complete this part of the tour we will return briefly to the notion of guards and revisit several of the topics above in that context. Next on the Walking Tour: *note How To Find Out about ACL2 Functions::  File: acl2-doc-emacs.info, Node: Analyzing Common Lisp Models, Next: Common Lisp, Prev: An Example of ACL2 in Use, Up: Pages Written Especially for the Tours Analyzing Common Lisp Models Analyzing Common Lisp Models To analyze a model you must be able to reason about the operations and relations involved. Perhaps, for example, some aspect of the model depends upon the fact that the concatenation operation is associative. In any Common Lisp you can confirm that (app '(A B) (app '(C D) '(E F))) and (app (app '(A B) '(C D)) '(E F))) both evaluate to the same thing, (A B C D E F). But what distinguishes ACL2 (the logic) from applicative Common Lisp (the language) is that in ACL2 you can prove that the concatenation function app is associative when its arguments are true-lists, whereas in Common Lisp all you can do is test that proposition. That is, in ACL2 it makes sense to say that the following formula is a "theorem." Theorem Associativity of App (implies (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c)))) Theorems about the properties of models are proved by symbolically manipulating the operations and relations involved. If the concatenation of sequences is involved in your model, then you may well need the theorem above in order to that your model has some particular property.  File: acl2-doc-emacs.info, Node: Common Lisp, Next: Common Lisp as a Modeling Language, Prev: Analyzing Common Lisp Models, Up: Pages Written Especially for the Tours Common Lisp Common Lisp Next on the Walking Tour: *note An Example Common Lisp Function Definition:: The logic of ACL2 is based on Common Lisp. Common Lisp is the standard list processing programming language. It is documented in: Guy L. Steele, Common Lisp The Language, Digital Press, 12 Crosby Drive, Bedford, MA 01730, 1990. See also `http://www.cs.cmu.edu/Web/Groups/AI/html/cltl/cltl2.html'. ACL2 formalizes only a subset of Common Lisp. It includes such familiar Lisp functions as cons, car and cdr for creating and manipulating list structures, various arithmetic primitives such as +, *, expt and <=, and intern and symbol-name for creating and manipulating symbols. Control primitives include cond, case and if, as well as function call, including recursion. New functions are defined with defun and macros with defmacro. See *note PROGRAMMING:: <> for a list of the Common Lisp primitives supported by ACL2. ACL2 supports five of Common Lisp's datatypes: * the precisely represented, unbounded numbers (integers, rationals, and the complex numbers with rational components, called the "complex rationals" here), * the characters with ASCII codes between 0 and 255 * strings of such characters * symbols (including packages) * conses ACL2 is a very small subset of full Common Lisp. ACL2 does not include the Common Lisp Object System (CLOS), higher order functions, circular structures, and other aspects of Common Lisp that are non-applicative. Roughly speaking, a language is applicative if it follows the rules of function application. For example, f(x) must be equal to f(x), which means, among other things, that the value of f must not be affected by "global variables" and the object x must not change over time. Next on the Walking Tour: *note An Example Common Lisp Function Definition::  File: acl2-doc-emacs.info, Node: Common Lisp as a Modeling Language, Next: Conversion, Prev: Common Lisp, Up: Pages Written Especially for the Tours Common Lisp as a Modeling Language Common Lisp as a Modeling Language In ACL2 we have adopted Common Lisp as the basis of our modeling language. If you have already read our brief note on Common Lisp and recall the example of app, please proceed. Otherwise see *note Common Lisp:: for an exceedingly brief introduction to Common Lisp and then come back here. In Common Lisp it is very easy to write systems of formulas that manipulate discrete, inductively constructed data objects. In building a model you might need to formalize the notion of sequences and define such operations as concatenation, length, whether one is a permutation of the other, etc. It is easy to do this in Common Lisp. Furthermore, if you have a Common Lisp "theory of sequences" you can run the operations and relations you define. That is, you can execute the functions on concrete data to see what results your formulas produce. If you define the function app as shown above and then type (app '(A B) '(C D E)) in any Common Lisp, the answer will be computed and will be (A B C D E). The executable nature of Common Lisp and thus of ACL2 is very handy when producing models. But executability is not enough for a modeling language because the purpose of models is to permit analysis. See *note Analyzing Common Lisp Models:: to continue.  File: acl2-doc-emacs.info, Node: Conversion, Next: Corroborating Models, Prev: Common Lisp as a Modeling Language, Up: Pages Written Especially for the Tours Conversion Conversion to Uppercase When symbols are read by Common Lisp they are converted to upper case. Note carefully that this remark applies to the characters in _symbols_. The characters in strings are not converted upper case. To type a symbol containing lower case characters you can enclose the symbol in vertical bars, as in |AbC| or you can put a "backslash" before each lower case character you wish to preserve, as in A\bC. |AbC| and A\bC are two different ways of writing the same symbol (just like 2/4 and 1/2 are two different ways of writing the same rational and 123 and 0123 are two different ways to write the same natural number). The symbol has three characters in its name, the middle one of which is a lower case b.  File: acl2-doc-emacs.info, Node: Corroborating Models, Next: Evaluating App on Sample Input, Prev: Conversion, Up: Pages Written Especially for the Tours Corroborating Models Corroborating Models Next on the Flying Tour: *note Models of Computer Hardware and Software:: After producing a model, it must be corroborated against reality. The Falling Body Model has been corroborated by a vast number of experiments in which the time and distance were measured and compared according to the formula. In general all models must be corroborated by experiment. The Falling Body Model can be derived from deeper models, namely Newton's laws of motion and the assertion that, over the limited distances concerned, graviation exerts a constant acceleration on the object. When the model in question can be derived from other models, it is the other models that are being corroborated by our experiments. Because nature is not formal, we cannot prove that our models of it are correct. All we can do is test our models against nature's behavior. Such testing often exposes restrictions on the applicability of our models. For example, the Falling Body Model is inaccurate if air resistance is significant. Thus, we learn not to use that model to predict how long it takes a feather to fall from a 200 foot tower in the earth's atmosphere. In addition, attempts at corroboration might reveal that the model is actually incorrect. Careful measurements might expose the fact that the gravitational force increases as the body falls closer to earth. Very careful measurements might reveal relativistic effects. Technically, the familiar Falling Body Model is just wrong, even under excessive restrictions such as "in a perfect vacuum" and "over small distances." But it is an incredibly useful model nonetheless. There are several morals here. Models need not be complete to be useful. Models need not be perfectly accurate to be useful. The user of a model must understand its limitations. Next on the Flying Tour: *note Models of Computer Hardware and Software::  File: acl2-doc-emacs.info, Node: Evaluating App on Sample Input, Next: Flawed Induction Candidates in App Example, Prev: Corroborating Models, Up: Pages Written Especially for the Tours Evaluating App on Sample Input Evaluating App on Sample Input Next on the Walking Tour: *note The Associativity of App:: ACL2 !>(app nil '(x y z)) (X Y Z) ACL2 !>(app '(1 2 3) '(4 5 6 7)) (1 2 3 4 5 6 7) ACL2 !>(app '(a b c d e f g) '(x y z)) ; see *note Conversion:: for an explanation (A B C D E F G X Y Z) ACL2 !>(app (app '(1 2) '(3 4)) '(5 6)) (1 2 3 4 5 6) ACL2 !>(app '(1 2) (app '(3 4) '(5 6))) (1 2 3 4 5 6) ACL2!>(let ((a '(1 2)) (b '(3 4)) (c '(5 6))) (equal (app (app a b) c) (app a (app b c)))) T As we can see from these examples, ACL2 functions can be executed more or less as Common Lisp. The last three examples suggest an interesting property of app. Next on the Walking Tour: *note The Associativity of App::  File: acl2-doc-emacs.info, Node: Flawed Induction Candidates in App Example, Next: Free Variables in Top-Level Input, Prev: Evaluating App on Sample Input, Up: Pages Written Especially for the Tours Flawed Induction Candidates in App Example Flawed Induction Candidates in App Example Induction on a is unflawed: every occurrence of a in the conjecture (equal (app (app a b) c) (app a (app b c))) is in a position being recursively decomposed! Now look at the occurrences of b. The first (shown in bold below) is in a position that is held constant in the recursion of (app a b). It would be "bad" to induct on b here. (equal (app (app a b) c) (app a (app b c)))  File: acl2-doc-emacs.info, Node: Free Variables in Top-Level Input, Next: Functions for Manipulating these Objects, Prev: Flawed Induction Candidates in App Example, Up: Pages Written Especially for the Tours Free Variables in Top-Level Input Free Variables in Top-Level Input ACL2 !>(equal (app (app a b) c) (app a (app b c)))) ACL2 Error in TOP-LEVEL: Global variables, such as C, B, and A, are not allowed. See :DOC ASSIGN and :DOC @. ACL2 does not allow "global variables" in top-level input. There is no "top-level binding environment" to give meaning to these variables. Thus, expressions involving no variables can generally be evaluated, ACL2 !>(equal (app (app '(1 2) '(3 4)) '(5 6)) (app '(1 2) (app '(3 4) '(5 6)))) (1 2 3 4 5 6) but expressions containing variables cannot. There is an exception to this rule. References to "single-threaded objects" may appear in top-level forms. See *note STOBJ:: <>. A single-threaded object is an ACL2 object, usually containing many fields, whose use is syntactically restricted so that it may be given as input only to certain functions and must be returned as output by certain functions. These restrictions allow single- threaded objects to be efficiently manipulated. For example, only a single copy of the object actually exists, even though from a logical perspective one might expect the object to be "copied on write." The most commonly used single-threaded object in ACL2 is the ACL2 system state, whose current value is always held in the variable state <>. ACL2 provides a way for you to use state to save values of computations at the top-level and refer to them later. See assign <> and @ <>.  File: acl2-doc-emacs.info, Node: Functions for Manipulating these Objects, Next: Guards, Prev: Free Variables in Top-Level Input, Up: Pages Written Especially for the Tours Functions for Manipulating these Objects Functions for Manipulating these Objects Next on the Flying Tour: *note Modeling in ACL2:: Consider a typical "stack" of control frames. Suppose the model required that we express the idea of "the most recent frame whose return program counter points into MAIN." The natural expression of this notion involves function application -- "fetch the return-pc of this frame" case analysis -- "if the pc is MAIN, then ..." iteration or recursion -- "pop this frame off and repeat." The designers of ACL2 have taken the position that a programming language is the natural language in which to define such notions, provided the language has a mathematical foundation so that models can be analyzed and properties derived logically. Common Lisp is the language supported by ACL2. To be precise, a small applicative subset of Common Lisp is the language supported by ACL2. Next on the Flying Tour: *note Modeling in ACL2::  File: acl2-doc-emacs.info, Node: Guards, Next: Guessing the Type of a Newly Admitted Function, Prev: Functions for Manipulating these Objects, Up: Pages Written Especially for the Tours Guards Guards Common Lisp functions are partial; they are not defined for all possible inputs. But ACL2 functions are total. Roughly speaking, the logical function of a given name in ACL2 is a completion of the Common Lisp function of the same name obtained by adding some arbitrary but "natural" values on arguments outside the "intended domain" of the Common Lisp function. ACL2 requires that every ACL2 function symbol have a "guard," which may be thought of as a predicate on the formals of the function describing the intended domain. The guard on the primitive function car <>, for example, is (or (consp x) (equal x nil)), which requires the argument to be either an ordered pair or nil. We will discuss later how to specify a guard for a defined function; when one is not specified, the guard is t which is just to say all arguments are allowed. But guards are entirely extra-logical: they are not involved in the axioms defining functions. If you put a guard on a defined function, the defining axiom added to the logic defines the function on all arguments, not just on the guarded domain. So what is the purpose of guards? The key to the utility of guards is that we provide a mechanism, called "guard verification," for checking that all the guards in a formula are true. See *note VERIFY-GUARDS::. This mechanism will attempt to prove that all the guards encountered in the evaluation of a guarded function are true every time they are encountered. For a thorough discussion of guards, see the paper [km97] in the ACL2 bibliography.  File: acl2-doc-emacs.info, Node: Guessing the Type of a Newly Admitted Function, Next: Guiding the ACL2 Theorem Prover, Prev: Guards, Up: Pages Written Especially for the Tours Guessing the Type of a Newly Admitted Function Guessing the Type of a Newly Admitted Function When a function is admitted to the logic, ACL2 tries to "guess" what type of object it returns. This guess is codified as a term that expresses a property of the value of the function. For app the term is (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)) which says that app returns either a cons or its second argument. This formula is added to ACL2's rule base as a type-prescription <> rule. Later we will discuss how rules are used by the ACL2 theorem prover. The point here is just that when you add a definition, the database of rules is updated, not just by the addition of the definitional axiom, but by several new rules. You should now return to the Walking Tour.  File: acl2-doc-emacs.info, Node: Guiding the ACL2 Theorem Prover, Next: Hey Wait! Is ACL2 Typed or Untyped(Q), Prev: Guessing the Type of a Newly Admitted Function, Up: Pages Written Especially for the Tours Guiding the ACL2 Theorem Prover Guiding the ACL2 Theorem Prover Next on the Walking Tour: *note ACL2 as an Interactive Theorem Prover (cont):: Now that you have seen the theorem prover in action you might be curious as to how you guide it. Look at the picture above. It is meant to suggest that Q is an important lemma needed for the proof of P. Note that to lead the prover to the proof of P the user first proves Q. In a way, the formulation and proof of Q is a hint to the prover about how to prove P. The user usually doesn't think of Q or recognize the need to prove it separately until he or she sees the theorem prover fail to prove P without it "knowing" Q. The way the user typically discovers the need for Q is to look at failed proofs. Next on the Walking Tour: *note ACL2 as an Interactive Theorem Prover (cont)::  File: acl2-doc-emacs.info, Node: Hey Wait! Is ACL2 Typed or Untyped(Q), Next: How Long Does It Take to Become an Effective User(Q), Prev: Guiding the ACL2 Theorem Prover, Up: Pages Written Especially for the Tours Hey Wait! Is ACL2 Typed or Untyped(Q) Hey Wait! Is ACL2 Typed or Untyped? The example ACL2 !>(app 7 27) ACL2 Error in TOP-LEVEL: The guard for the function symbol ENDP, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (ENDP 7). illustrates the fact that while ACL2 is an untyped language the ACL2 evaluator can be configured so as to check "types" at runtime. We should not say "types" here but "guards." See *note Undocumented Topic:: for a discussion of guards. The guard on endp <> requires its argument to be a true list. Since 7 is not a true list, and since ACL2 is checking guards in this example, an error is signaled by ACL2. How do you know ACL2 is checking guards? Because the prompt tells us (see *note About the Prompt::) with its "!".  File: acl2-doc-emacs.info, Node: How Long Does It Take to Become an Effective User(Q), Next: How To Find Out about ACL2 Functions, Prev: Hey Wait! Is ACL2 Typed or Untyped(Q), Up: Pages Written Especially for the Tours How Long Does It Take to Become an Effective User(Q) How Long Does It Take to Become an Effective User? Next on the Flying Tour: *note Other Requirements:: We expect that a talented undergraduate majoring in computer science (or perhaps mathematics) will probably take several weeks to become an effective ACL2 user. The time will depend, of course, on that student's familiarity with logic (or formal methods) and Lisp programming, as well as willingness to read and study the ACL2 User's Manual. Of course, it is critical to do some projects in order to gain proficiency. (Hence access to an ACL2 implementation is also a requirement, for example by downloading and installing following links from the ACL2 home page.) But it is critical to start with "toy" projects before tackling a "grand challenge." Next on the Flying Tour: *note Other Requirements::  File: acl2-doc-emacs.info, Node: How To Find Out about ACL2 Functions, Next: How To Find Out about ACL2 Functions (cont), Prev: How Long Does It Take to Become an Effective User(Q), Up: Pages Written Especially for the Tours How To Find Out about ACL2 Functions How To Find Out about ACL2 Functions Next on the Walking Tour: *note How To Find Out about ACL2 Functions (cont):: Most ACL2 primitives are documented. Here is the definition of app again, with the documented topics highlighted. <> All of the links below lead into the ACL2 reference manual. So follow these links if you wish, but use your Back Button to return here! (defun app (x y) (cond ((endp x) y) (t (cons (car x) (app (cdr x) y))))) By following the link on endp <> we see that it is a Common Lisp function and is defined to be the same as atom <>, which recognizes non-conses. But endp has a guard. Since we are ignorning guards for now, we'll ignore the guard issue on endp. So this definition reads "to app x and y: if x is an atom, return y; otherwise, app (cdr x) and y and then cons (car x) onto that." Next on the Walking Tour: *note How To Find Out about ACL2 Functions (cont)::  File: acl2-doc-emacs.info, Node: How To Find Out about ACL2 Functions (cont), Next: Modeling in ACL2, Prev: How To Find Out about ACL2 Functions, Up: Pages Written Especially for the Tours How To Find Out about ACL2 Functions (cont) How To Find Out about ACL2 Functions (cont) Next on the Walking Tour: *note The Admission of App:: You can always use the Index <> icon below to find the documentation of functions. Try it. Click on the Index icon below. Then use the Find command of your browser to find "endp" in that document and follow the link. But remember to come back here. The ACL2 documentation is also available via Emacs' TexInfo, allowing you to explore the hyperlinked documentation in the comfort of a text editor that can also interact with ACL2. In addition, runtime images of ACL2 have the hyperlinked text as a large ACL2 data structure that can be explored with ACL2's :doc command. If you have ACL2 running, try the command :doc endp. Another way to find out about ACL2 functions, if you have an ACL2 image available, is to use the command :args <> which prints the formals, type, and guard of a function symbol. Of course, the ACL2 documentation can also be printed out as a very long book but we do not recommend that! See the ACL2 Home Page to download the Postscript. Now let's continue with app. Next on the Walking Tour: *note The Admission of App::  File: acl2-doc-emacs.info, Node: Modeling in ACL2, Next: Models in Engineering, Prev: How To Find Out about ACL2 Functions (cont), Up: Pages Written Especially for the Tours Modeling in ACL2 Modeling in ACL2 Next on the Flying Tour: *note Running Models:: Below we define mc(s,n) to be the function that single-steps n times from a given starting state, s. In Common Lisp, "mc(s,n)" is written (mc s n). (defun mc (s n) ; To step s n times: (if (zp n) ; If n is 0 s ; then return s (mc (single-step s) (- n 1)))) ; else step single-step(s) n-1 times. This is an example of a formal model in ACL2. Next on the Flying Tour: *note Running Models::  File: acl2-doc-emacs.info, Node: Models in Engineering, Next: Models of Computer Hardware and Software, Prev: Modeling in ACL2, Up: Pages Written Especially for the Tours Models in Engineering Models in Engineering Frequently, engineers use mathematical models. Use of such models frequently lead to better designs, faster completion of acceptable products, and reduced overall cost, because models allow the trained user to study the design before it is built and analyze its properties. Usually, testing and analyzing a model is cheaper and faster than fabricating and refabricating the product.  File: acl2-doc-emacs.info, Node: Models of Computer Hardware and Software, Next: Name the Formula Above, Prev: Models in Engineering, Up: Pages Written Especially for the Tours Models of Computer Hardware and Software Models of Computer Hardware and Software Next on the Flying Tour: *note A Typical State:: Computing machines, whether hardware or software or some combintation, are frequently modeled as "state machines." To so model a computing machine we must represent its states as objects in our mathematical framework. Transitions are functions or relations on state objects. In what language shall we define these objects, functions, and relations? The mathematical languages we were taught in high school algebra, geometry, trignometry, and calculus are often inappropriate for modeling digital systems. They primarily let us talk about numbers and continuous functions. To see what kind of expressive power we need, take a closer look at what a typical state contains. Next on the Flying Tour: *note A Typical State::  File: acl2-doc-emacs.info, Node: Name the Formula Above, Next: Nontautological Subgoals, Prev: Models of Computer Hardware and Software, Up: Pages Written Especially for the Tours Name the Formula Above Name the Formula Above When the theorem prover explicitly assigns a name, like *1, to a formula, it has decided to prove the formula by induction.  File: acl2-doc-emacs.info, Node: Nontautological Subgoals, Next: Numbers in ACL2, Prev: Name the Formula Above, Up: Pages Written Especially for the Tours Nontautological Subgoals Prover output omits some details The theorem prover's proof output is intended to suggest an outline of the reasoning process employed by its proof engine, which is virtually always more than is necessary for the ACL2 user. In particular, the output often omits subgoals that are sufficiently trivial, including tautologies.  File: acl2-doc-emacs.info, Node: Numbers in ACL2, Next: On the Naming of Subgoals, Prev: Nontautological Subgoals, Up: Pages Written Especially for the Tours Numbers in ACL2 Numbers in ACL2 ACL2 numbers are precisely represented and unbounded. They can be partitioned into the following subtypes: Rationals Integers Positive integers 3 Zero 0 Negative Integers -3 Non-Integral Rationals Positive Non-Integral Rationals 19/3 Negative Non-Integral Rationals -22/7 Complex Rational Numbers #c(3 5/2) ; = 3+(5/2)i Signed integer constants are usually written (as illustrated above) as sequences of decimal digits, possibly preceded by + or -. Decimal points are not allowed. Integers may be written in binary, as in #b1011 (= 23) and #b-111 (= -7). Octal may also be used, #o-777 = -511. Non-integral rationals are written as a signed decimal integer and an unsigned decimal integer, separated by a slash. Complex rationals are written as #c(rpart ipart) where rpart and ipart are rationals. Of course, 4/2 = 2/1 = 2 (i.e., not every rational written with a slash is a non-integer). Similarly, #c(4/2 0) = #c(2 0) = 2. The common arithmetic functions and relations are denoted by +, -, *, /, =, <, <=, > and >=. However there are many others, e.g., floor, ceiling, and lognot. We suggest you see *note PROGRAMMING:: <> where we list all of the primitive ACL2 functions. Alternatively, see any Common Lisp language documentation. The primitive predicates for recognizing numbers are illustrated below. The following ACL2 function will classify an object, x, according to its numeric subtype, or else return 'NaN (not a number). We show it this way just to illustrate programming in ACL2. (defun classify-number (x) (cond ((rationalp x) (cond ((integerp x) (cond ((< 0 x) 'positive-integer) ((= 0 x) 'zero) (t 'negative-integer))) ((< 0 x) 'positive-non-integral-rational) (t 'negative-non-integral-rational))) ((complex-rationalp x) 'complex-rational) (t 'NaN)))  File: acl2-doc-emacs.info, Node: On the Naming of Subgoals, Next: Other Requirements, Prev: Numbers in ACL2, Up: Pages Written Especially for the Tours On the Naming of Subgoals On the Naming of Subgoals Subgoal *1/2 is the induction step from the scheme, obtained by instantiating the scheme with our conjecture. We number the cases "backward", so this is case "2" of the proof of "*1". We number them backward so you can look at a subgoal number and get an estimate for how close you are to the end.  File: acl2-doc-emacs.info, Node: Other Requirements, Next: Overview of the Expansion of ENDP in the Base Case, Prev: On the Naming of Subgoals, Up: Pages Written Especially for the Tours Other Requirements Other Requirements Next on the Flying Tour: *note The End of the Flying Tour:: ACL2 is distributed on the Web without fee. There is a license agreement based on the 3-clause BSD license. See the file LICENSE in the ACL2 distribution. ACL2 currently runs on Unix, Linux, Windows, and Macintosh OS X operating systems. It can be built in any of the following Common Lisps: * Allegro Common Lisp, * CCL (formerly OpenMCL) * CLISP, * CMU Common Lisp, * GCL (Gnu Common Lisp), * LispWorks, and * SBCL (Steel Bank Common Lisp) Next on the Flying Tour: *note The End of the Flying Tour::  File: acl2-doc-emacs.info, Node: Overview of the Expansion of ENDP in the Base Case, Next: Overview of the Expansion of ENDP in the Induction Step, Prev: Other Requirements, Up: Pages Written Especially for the Tours Overview of the Expansion of ENDP in the Base Case Overview of the Expansion of ENDP in the Base Case Subgoal *1/1 is the Base Case of our induction. It simplifies to Subgoal *1/1' by expanding the ENDP term in the hypothesis, just as we saw in the earlier proof of Subgoal *1/2.  File: acl2-doc-emacs.info, Node: Overview of the Expansion of ENDP in the Induction Step, Next: Overview of the Final Simplification in the Base Case, Prev: Overview of the Expansion of ENDP in the Base Case, Up: Pages Written Especially for the Tours Overview of the Expansion of ENDP in the Induction Step Overview of the Expansion of ENDP in the Induction Step In this message the system is saying that Subgoal *1/2 has been rewritten to the Subgoal *1/2', by expanding the definition of endp. This is an example of simplification, one of the main proof techniques used by the theorem prover. See *note The Expansion of ENDP in the Induction Step (Step 0):: if you would like to step through the simplification of Subgoal *1/2.  File: acl2-doc-emacs.info, Node: Overview of the Final Simplification in the Base Case, Next: Overview of the Proof of a Trivial Consequence, Prev: Overview of the Expansion of ENDP in the Induction Step, Up: Pages Written Especially for the Tours Overview of the Final Simplification in the Base Case Overview of the Final Simplification in the Base Case The But is our signal that the goal is proved. See *note The Final Simplification in the Base Case (Step 0):: to step through the proof. It is very simple.  File: acl2-doc-emacs.info, Node: Overview of the Proof of a Trivial Consequence, Next: Overview of the Simplification of the Base Case to T, Prev: Overview of the Final Simplification in the Base Case, Up: Pages Written Especially for the Tours Overview of the Proof of a Trivial Consequence Overview of the Proof of a Trivial Consequence Next on the Walking Tour: *note The End of the Walking Tour:: ACL2 !>(defthm trivial-consequence (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7) (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7)))))) ACL2 Warning [Subsume] in ( DEFTHM TRIVIAL-CONSEQUENCE ...): The previously added rule ASSOCIATIVITY-OF-APP subsumes the newly proposed :REWRITE rule TRIVIAL-CONSEQUENCE, in the sense that the old rule rewrites a more general target. Because the new rule will be tried first, it may nonetheless find application. By the simple :rewrite rule ASSOCIATIVITY-OF-APP we reduce the conjecture to Goal' (EQUAL (APP X1 (APP X2 (APP X3 (APP X4 (APP X5 (APP X6 X7)))))) (APP X1 (APP X2 (APP X3 (APP X4 (APP X5 (APP X6 X7))))))). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. Summary Form: ( DEFTHM TRIVIAL-CONSEQUENCE ...) Rules: ((:REWRITE ASSOCIATIVITY-OF-APP) (:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: Subsume Time: 0.20 seconds (prove: 0.02, print: 0.00, other: 0.18) TRIVIAL-CONSEQUENCE You might explore the links before moving on. Next on the Walking Tour: *note The End of the Walking Tour::  File: acl2-doc-emacs.info, Node: Overview of the Simplification of the Base Case to T, Next: Overview of the Simplification of the Induction Conclusion, Prev: Overview of the Proof of a Trivial Consequence, Up: Pages Written Especially for the Tours Overview of the Simplification of the Base Case to T Overview of the Simplification of the Base Case to T Next on the Walking Tour: *note The End of the Proof of the Associativity of App:: Subgoal *1/1 (IMPLIES (ENDP A) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). By the simple :definition ENDP we reduce the conjecture to Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). But simplification reduces this to T, using the :definition APP and primitive type reasoning. Next on the Walking Tour: *note The End of the Proof of the Associativity of App::  File: acl2-doc-emacs.info, Node: Overview of the Simplification of the Induction Conclusion, Next: Overview of the Simplification of the Induction Step to T, Prev: Overview of the Simplification of the Base Case to T, Up: Pages Written Especially for the Tours Overview of the Simplification of the Induction Conclusion Overview of the Simplification of the Induction Conclusion In this message the system is saying that Subgoal *1/2' has been rewritten to T using the rules noted. The word "But" at the beginning of the sentence is a signal that the goal has been proved. See *note The Simplification of the Induction Conclusion (Step 0):: to step through the proof of Subgoal *1/2'.  File: acl2-doc-emacs.info, Node: Overview of the Simplification of the Induction Step to T, Next: Perhaps, Prev: Overview of the Simplification of the Induction Conclusion, Up: Pages Written Especially for the Tours Overview of the Simplification of the Induction Step to T Overview of the Simplification of the Induction Step to T Next on the Walking Tour: *note Overview of the Simplification of the Base Case to T:: Subgoal *1/2 (IMPLIES (AND (NOT (ENDP A)) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). By the simple :definition ENDP we reduce the conjecture to Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). But simplification reduces this to T, using the :definition APP, the :rewrite rules CDR-CONS and CAR-CONS and primitive type reasoning. Next on the Walking Tour: *note Overview of the Simplification of the Base Case to T::  File: acl2-doc-emacs.info, Node: Perhaps, Next: Popping out of an Inductive Proof, Prev: Overview of the Simplification of the Induction Step to T, Up: Pages Written Especially for the Tours Perhaps Perhaps The theorem prover's proof is printed in real time. At the time it prints "Perhaps" it does not know the proof will succeed.  File: acl2-doc-emacs.info, Node: Popping out of an Inductive Proof, Next: Proving Theorems about Models, Prev: Perhaps, Up: Pages Written Especially for the Tours Popping out of an Inductive Proof Popping out of an Inductive Proof Recall that our induction scheme (see *note The Proof of the Associativity of App:: to revisit it) had two cases, the induction step (Subgoal *1/2) and the base case (Subgoal *1/1). Both have been proved!  File: acl2-doc-emacs.info, Node: Proving Theorems about Models, Next: Revisiting the Admission of App, Prev: Popping out of an Inductive Proof, Up: Pages Written Especially for the Tours Proving Theorems about Models Proving Theorems about Models Next on the Flying Tour: *note What is Required of the User(Q):: But ACL2 is a logic. We can prove theorems about the model. Theorem. MC 'mult is a multiplier (implies (and (natp x) (natp y)) (equal (lookup 'z (mc (s 'mult x y) (mclk x))) (* x y))). This theorem says that a certain program running on the mc machine will correctly multiply any two natural numbers. It is a statement about an infinite number of test cases! We know it is true about the model because we proved it. Of course, models of actual machines usually only accept a finite number of different inputs. For example, engineers at Advanced Micro Devices (AMD), Centaur, and IBM have ACL2 models of floating point units that operate on double precision IEEE floating point numbers. These are finite models. But the size of their inputs is sufficiently large that they are verified by the same mathematical methods used to prove theorems about infinite state systems like our little mc. Next on the Flying Tour: *note What is Required of the User(Q)::  File: acl2-doc-emacs.info, Node: Revisiting the Admission of App, Next: Rewrite Rules are Generated from DEFTHM Events, Prev: Proving Theorems about Models, Up: Pages Written Especially for the Tours Revisiting the Admission of App Revisiting the Admission of App Next on the Walking Tour: *note Evaluating App on Sample Input:: Here is the definition of app again with certain parts highlighted. If you are taking the Walking Tour, please read the text carefully and click on each of the links below, except those marked <>. Then come back here. ACL2 !>(defun app (x y) (cond ((endp x) y) (t (cons (car x) (app (cdr x) y))))) The admission of APP is trivial, using the relation O< <> (which is known to be well-founded on the domain recognized by O-P <>) and the measure (ACL2-COUNT <> X). We observe that the type of APP is described by the theorem (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)). We used primitive type reasoning. Summary Form: ( DEFUN APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, other: 0.03) APP Next on the Walking Tour: *note Evaluating App on Sample Input::  File: acl2-doc-emacs.info, Node: Rewrite Rules are Generated from DEFTHM Events, Next: Running Models, Prev: Revisiting the Admission of App, Up: Pages Written Especially for the Tours Rewrite Rules are Generated from DEFTHM Events Rewrite Rules are Generated from DEFTHM Events Next on the Walking Tour: *note You Must Think about the Use of a Formula as a Rule:: By reading the documentation of defthm <> (and especially of its :rule-classes <> argument) you would learn that when we submitted the command (defthm associativity-of-app (equal (app (app a b) c) (app a (app b c)))) we not only command the system to prove that app is an associative function but * we commanded it to use that fact as a rewrite rule. That means that every time the system encounters a term of the form (app (app x y) z) it will replace it with (app x (app y z))! Next on the Walking Tour: *note You Must Think about the Use of a Formula as a Rule::  File: acl2-doc-emacs.info, Node: Running Models, Next: Subsumption of Induction Candidates in App Example, Prev: Rewrite Rules are Generated from DEFTHM Events, Up: Pages Written Especially for the Tours Running Models Running Models Next on the Flying Tour: *note Symbolic Execution of Models:: Suppose the machine being modeled is some kind of arithmetic unit. Suppose the model can be initialized so as to multiply x times y and leave the answer in z. Then if we initialize s to multiply with x=5 and y=7 and run the machine long enough, we can read the answer 35 in the final state. Because ACL2 is a programming language, our model can be run or executed. If you defined the model in ACL2 and then typed (lookup 'z (mc (s 'mult 5 7) 29)) then ACL2 would compute 35. (Here we assume that the function s creates a state ready to run a given application on given inputs x and y.) You can emulate or test the model of your machine. This is obvious because ACL2 is Common Lisp; and Common Lisp is a programming language. Next on the Flying Tour: *note Symbolic Execution of Models::  File: acl2-doc-emacs.info, Node: Subsumption of Induction Candidates in App Example, Next: Suggested Inductions in the Associativity of App Example, Prev: Running Models, Up: Pages Written Especially for the Tours Subsumption of Induction Candidates in App Example Subsumption of Induction Candidates in App Example After collecting induction suggestions from these three terms (app a b) (app b c) (app a (app b c)) the system notices that the first and last suggest the same decomposition of a: case split on whether a is empty (i.e., (endp a)), and in the case where it is not empty, recursively process (cdr a). So we are left with two ideas about how to induct: Decompose a as we would to unwind (app a b). Decompose b as we would to unwind (app b c).  File: acl2-doc-emacs.info, Node: Suggested Inductions in the Associativity of App Example, Next: Symbolic Execution of Models, Prev: Subsumption of Induction Candidates in App Example, Up: Pages Written Especially for the Tours Suggested Inductions in the Associativity of App Example Suggested Inductions in the Associativity of App Example To find a plausible induction argument, the system studies the recursions exhibited by the terms in the conjecture. Roughly speaking, a call of a recursive function "suggests" an induction if the argument position decomposed in recursion is occupied by a variable. In this conjecture, three terms suggest inductions: (app a b) (app b c) (app a (app b c)) The variable recursively decomposed is indicated in bold.  File: acl2-doc-emacs.info, Node: Symbolic Execution of Models, Next: The Admission of App, Prev: Suggested Inductions in the Associativity of App Example, Up: Pages Written Especially for the Tours Symbolic Execution of Models Symbolic Execution of Models Next on the Flying Tour: *note Proving Theorems about Models:: But ACL2 is more than a programming language. Initialize x to 5 and let y be any legal value. Because ACL2 is a mathematical language, we can simplify the expression (lookup 'z (mc (s 'mult 5 y) 29)) and get (+ y y y y y). This is symbolic execution because not all of the parameters are known. Next on the Flying Tour: *note Proving Theorems about Models::  File: acl2-doc-emacs.info, Node: The Admission of App, Next: The Associativity of App, Prev: Symbolic Execution of Models, Up: Pages Written Especially for the Tours The Admission of App The Admission of App Next on the Walking Tour: *note Revisiting the Admission of App:: Here is what it looks like to submit the definition of app to ACL2: ACL2 !>(defun app (x y) (cond ((endp x) y) (t (cons (car x) (app (cdr x) y))))) The admission of APP is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We observe that the type of APP is described by the theorem (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)). We used primitive type reasoning. Summary Form: ( DEFUN APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, other: 0.03) APP The text between the lines above is one interaction with the ACL2 command loop. Interacting with the latest version of ACL2 may not produce the very same output, but we trust you'll recognize the basics. Above you see the user's input and how the system responds. This little example shows you what the syntax looks like and is a very typical successful interaction with the definitional principle. Let's look at it a little more closely. Next on the Walking Tour: *note Revisiting the Admission of App::  File: acl2-doc-emacs.info, Node: The Associativity of App, Next: The Base Case in the App Example, Prev: The Admission of App, Up: Pages Written Especially for the Tours The Associativity of App The Associativity of App Next on the Walking Tour: *note The Theorem that App is Associative:: ACL2!>(let ((a '(1 2)) (b '(3 4)) (c '(5 6))) (equal (app (app a b) c) (app a (app b c)))) T Observe that, for the particular a, b, and c above, (app (app a b) c) returns the same thing as (app a (app b c)). Perhaps app is associative. Of course, to be associative means that the above property must hold for all values of a, b, and c, not just the ones tested above. Wouldn't it be cool if you could type ACL2!>(equal (app (app a b) c) (app a (app b c))) and have ACL2 compute the value T? Well, you can't! If you try it, you'll get an error message! The message says we can't evaluate that form because it contains free variables, i.e., variables not given values. See *note Free Variables in Top-Level Input:: to see the message. We cannot evaluate a form on an infinite number of cases. But we can prove that a form is a theorem and hence know that it will always evaluate to true. Next on the Walking Tour: *note The Theorem that App is Associative::  File: acl2-doc-emacs.info, Node: The Base Case in the App Example, Next: The End of the Flying Tour, Prev: The Associativity of App, Up: Pages Written Especially for the Tours The Base Case in the App Example The Base Case in the App Example This formula is the Base Case. It consists of two parts, a test identifying the non-inductive case and the conjecture to prove. (IMPLIES (ENDP A) ; Test (:P A B C)) ; Conjecture When we prove this we can assume * A is empty and we have to prove the conjecture for A.  File: acl2-doc-emacs.info, Node: The End of the Flying Tour, Next: The End of the Proof of the Associativity of App, Prev: The Base Case in the App Example, Up: Pages Written Especially for the Tours The End of the Flying Tour The End of the Flying Tour This completes the Flying Tour. We recommend that you now take A Walking Tour of ACL2. Thanks. Matt Kaufmann and J Moore Next on the Walking Tour: *note A Walking Tour of ACL2::  File: acl2-doc-emacs.info, Node: The End of the Proof of the Associativity of App, Next: The End of the Walking Tour, Prev: The End of the Flying Tour, Up: Pages Written Especially for the Tours The End of the Proof of the Associativity of App The End of the Proof of the Associativity of App Next on the Walking Tour: *note Guiding the ACL2 Theorem Prover:: That completes the proof of *1. Q.E.D. Summary Form: ( DEFTHM ASSOCIATIVITY-OF-APP ...) Rules: ((:REWRITE CDR-CONS) (:REWRITE CAR-CONS) (:DEFINITION NOT) (:DEFINITION ENDP) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:DEFINITION APP)) Warnings: None Time: 0.27 seconds (prove: 0.10, print: 0.05, other: 0.12) ASSOCIATIVITY-OF-APP Next on the Walking Tour: *note Guiding the ACL2 Theorem Prover::  File: acl2-doc-emacs.info, Node: The End of the Walking Tour, Next: The Event Summary, Prev: The End of the Proof of the Associativity of App, Up: Pages Written Especially for the Tours The End of the Walking Tour The End of the Walking Tour This completes the Walking Tour. We intend to document many other parts of the system this way, but we just haven't gotten around to it. To start the two tours over again from the beginning, click on the icons below. If you are really interested in learning how to use ACL2, we recommend that you repeat each tour at least once more to explore branches of the tour that you might have missed. If you want to learn how to use the theorem prover, we now recommend that you devote the time necessary to work your way through the extended introduction to how to use the prover. See *note INTRODUCTION-TO-THE-THEOREM-PROVER::. This will explain how to interact with ACL2 and has some sample problems for you to solve including some challenge proofs to make ACL2 find. We hope you enjoy ACL2. We do. Matt Kaufmann and J Strother Moore Next on the Flying Tour: *note A Flying Tour of ACL2:: Next on the Walking Tour: *note A Walking Tour of ACL2::  File: acl2-doc-emacs.info, Node: The Event Summary, Next: The Expansion of ENDP in the Induction Step (Step 0), Prev: The End of the Walking Tour, Up: Pages Written Especially for the Tours The Event Summary The Event Summary At the conclusion of most events (see *note About the Prompt:: for a brief discussion of events or see *note EVENTS:: <>), ACL2 prints a summary. The summary for app is: Summary Form: ( DEFUN APP ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Warnings: None Time: 0.03 seconds (prove: 0.00, print: 0.00, other: 0.03) APP The "rules" listed are those used in function admission or proof summarized. What is actually listed are "runes" (see *note RUNE::) <>) which are list-structured names for rules in the ACL2 database or "world" <>. Using theories <> you can "enable" and "disable" rules so as to make them available (or not) to the ACL2 theorem prover. The "warnings" mentioned (none are listed for app) remind the reader whether the event provoked any warnings. The warnings themselves would have been printed earlier in the processing and this part of the summary just names the earlier warnings printed. The "time" indicates how much processing time was used and is divided into three parts: the time devoted to proof, to printing, and to syntactic checks, pre-processing and database updates. Despite the fact that ACL2 is an applicative language it is possible to measure time with ACL2 programs. The state <> contains a clock. The times are printed in decimal notation but are actually counted in integral units. Note that by default, each time is a runtime, also known as a cpu time, as opposed to being a real time, also known as a wall clock time. The final APP is the value of the defun command and was printed by the read-eval-print loop. The fact that it is indented one space is a subtle reminder that the command actually returned an "error triple", consisting of a flag indicating (in this case) that no error occurred, a value (in this case the symbol APP), and the final state <>). See *note LD-POST-EVAL-PRINT:: <> for some details. If you really want to follow that link, however, you might see *note LD:: <> first. You should now return to the Walking Tour.  File: acl2-doc-emacs.info, Node: The Expansion of ENDP in the Induction Step (Step 0), Next: The Expansion of ENDP in the Induction Step (Step 1), Prev: The Event Summary, Up: Pages Written Especially for the Tours The Expansion of ENDP in the Induction Step (Step 0) The Expansion of ENDP in the Induction Step (Step 0) Subgoal *1/2 (IMPLIES (AND (NOT (ENDP A)) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). Click on the link above (the open parenthesis before ENDP) to replace (ENDP A) by its definition.  File: acl2-doc-emacs.info, Node: The Expansion of ENDP in the Induction Step (Step 1), Next: The Expansion of ENDP in the Induction Step (Step 2), Prev: The Expansion of ENDP in the Induction Step (Step 0), Up: Pages Written Especially for the Tours The Expansion of ENDP in the Induction Step (Step 1) The Expansion of ENDP in the Induction Step (Step 1) Subgoal *1/2 (IMPLIES (AND (NOT (NOT (CONSP A))) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). The bold text is the instantiated definition of ENDP. Now click on the link above to simplify (NOT (NOT (CONSP A)))  File: acl2-doc-emacs.info, Node: The Expansion of ENDP in the Induction Step (Step 2), Next: The Falling Body Model, Prev: The Expansion of ENDP in the Induction Step (Step 1), Up: Pages Written Especially for the Tours The Expansion of ENDP in the Induction Step (Step 2) The Expansion of ENDP in the Induction Step (Step 2) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). Note that this is Subgoal *1/2'. You may see *note Overview of the Simplification of the Induction Step to T:: to return to the main proof.  File: acl2-doc-emacs.info, Node: The Falling Body Model, Next: The Final Simplification in the Base Case (Step 0), Prev: The Expansion of ENDP in the Induction Step (Step 2), Up: Pages Written Especially for the Tours The Falling Body Model The Falling Body Model One particularly famous and very simple model is the equation of a falling body: the distance d an object falls is proportional to the square of the time t. If the time is measured in seconds and the distance in feet, the equation relating these two is 2 d = 16t This equation is a model of falling objects. It can be used to predict how long it takes a cannonball to fall from the top of a 200 foot tower (3.5 seconds). This might be important if your product is designed to drop cannonballs on moving targets.  File: acl2-doc-emacs.info, Node: The Final Simplification in the Base Case (Step 0), Next: The Final Simplification in the Base Case (Step 1), Prev: The Falling Body Model, Up: Pages Written Especially for the Tours The Final Simplification in the Base Case (Step 0) the Final Simplification in the Base Case (Step 0) Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). Click on the link above to replace (APP A B) by its definition. Note that the hypothesis (NOT (CONSP A)) allows us to simplify the IF in APP to its false branch this time.  File: acl2-doc-emacs.info, Node: The Final Simplification in the Base Case (Step 1), Next: The Final Simplification in the Base Case (Step 2), Prev: The Final Simplification in the Base Case (Step 0), Up: Pages Written Especially for the Tours The Final Simplification in the Base Case (Step 1) the Final Simplification in the Base Case (Step 1) Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP B C) (APP A (APP B C)))). Click on the link above to expand the definition of APP. Again, we come out through the false branch because of the hypothesis.  File: acl2-doc-emacs.info, Node: The Final Simplification in the Base Case (Step 2), Next: The Final Simplification in the Base Case (Step 3), Prev: The Final Simplification in the Base Case (Step 1), Up: Pages Written Especially for the Tours The Final Simplification in the Base Case (Step 2) the Final Simplification in the Base Case (Step 2) Subgoal *1/1' (IMPLIES (NOT (CONSP A)) (EQUAL (APP B C) (APP B C))). Click on the link above to use the Axiom (EQUAL x x) = t  File: acl2-doc-emacs.info, Node: The Final Simplification in the Base Case (Step 3), Next: The First Application of the Associativity Rule, Prev: The Final Simplification in the Base Case (Step 2), Up: Pages Written Especially for the Tours The Final Simplification in the Base Case (Step 3) the Final Simplification in the Base Case (Step 3) Subgoal *1/1' (IMPLIES (NOT (CONSP A)) T) Now that its conclusion is identically T the IMPLIES will simplify to T (not shown) and we are done with Subgoal *1/1'. You may see *note Overview of the Simplification of the Base Case to T:: to return to the main proof.  File: acl2-doc-emacs.info, Node: The First Application of the Associativity Rule, Next: The Induction Scheme Selected for the App Example, Prev: The Final Simplification in the Base Case (Step 3), Up: Pages Written Especially for the Tours The First Application of the Associativity Rule The First Application of the Associativity Rule So here we see our associativity rule being used! The rewriter sweeps the conjecture in a leftmost innermost fashion, applying rewrite rules as it goes. The associativity rule is used many times in this sweep. The first "target" is highlighted below. Click on it to see what happens: Current Conjecture: (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7) (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7)))))  File: acl2-doc-emacs.info, Node: The Induction Scheme Selected for the App Example, Next: The Induction Step in the App Example, Prev: The First Application of the Associativity Rule, Up: Pages Written Especially for the Tours The Induction Scheme Selected for the App Example The Induction Scheme Selected for the App Example (AND (IMPLIES (AND (NOT (ENDP A)) ; Induction Step: test (:P (CDR A) B C)) ; and induction hypothesis (:P A B C)) ; implies induction conclusion. (IMPLIES (ENDP A) (:P A B C))) ; Base Case The formula beginning with this parenthesis is the induction scheme suggested by (APP A B) applied to (P A B C). It is a conjunction (AND <>) of two formulas. The first is the induction step and the second is the base case.  File: acl2-doc-emacs.info, Node: The Induction Step in the App Example, Next: The Instantiation of the Induction Scheme, Prev: The Induction Scheme Selected for the App Example, Up: Pages Written Especially for the Tours The Induction Step in the App Example The Induction Step in the App Example This formula is the Induction Step. It basically consists of three parts, a test identifying the inductive case, an induction hypothesis and an induction conclusion. (IMPLIES (AND (NOT (ENDP A)) ; Test (:P (CDR A) B C)) ; Induction Hypothesis (:P A B C)) ; Induction Conclusion When we prove this we can assume * A is not empty, and that * the associativity conjecture holds for a ``smaller'' version of A, namely, (CDR A). Under those hypotheses we have to prove the associativity conjecture for A itself.  File: acl2-doc-emacs.info, Node: The Instantiation of the Induction Scheme, Next: The Justification of the Induction Scheme, Prev: The Induction Step in the App Example, Up: Pages Written Especially for the Tours The Instantiation of the Induction Scheme The Instantiation of the Induction Scheme The induction scheme just shown is just an abbreviation for our real goal. To obtain our actual goals we have to replace the schema :P by the associativity conjecture (instantiated as shown in the scheme). This produces two actual goals, the induction step and the base case.  File: acl2-doc-emacs.info, Node: The Justification of the Induction Scheme, Next: The Proof of the Associativity of App, Prev: The Instantiation of the Induction Scheme, Up: Pages Written Especially for the Tours The Justification of the Induction Scheme The Justification of the Induction Scheme This paragraph explains why the induction selected is legal. The explanation is basically the same as the explanation for why the recursion in (APP A B) terminates.  File: acl2-doc-emacs.info, Node: The Proof of the Associativity of App, Next: The Q.E.D. Message, Prev: The Justification of the Induction Scheme, Up: Pages Written Especially for the Tours The Proof of the Associativity of App The Proof of the Associativity of App Next on the Walking Tour: *note Overview of the Simplification of the Induction Step to T:: Here is the theorem prover's output when it processes the defthm command for the associativity of app. We have highlighted text for which we offer some explanation, and broken the presentation into several pages. (The most recent version of ACL2 may print slightly different output but the basics are the same.) Just follow the Walking Tour after exploring the explanations. However, before exploring this output you should understand that ACL2 users rarely read successful proofs! Instead, they look at certain subgoals printed in failed proofs, figure whether and how those subgoals can be proved, and give ACL2 directions for proving them, usually by simply proving other lemmas. Furthermore, to be a good user of ACL2 you do not have to understand how the theorem prover works. You just have to understand how to interact with it. We explain this in great detail later. But basically all new users are curious to know how ACL2 works and this little tour attempts to give some answers, just to satisfy your curiosity. ACL2!>(defthm associativity-of-app (equal (app (app a b) c) (app a (app b c)))) Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. Subsumption reduces that number to two. However, one of these is flawed and so we are left with one viable candidate. We will induct according to a scheme suggested by (APP A B). If we let (:P A B C) denote *1 above then the induction scheme we'll use is (AND (IMPLIES (AND (NOT (ENDP A)) (:P (CDR A) B C)) (:P A B C)) (IMPLIES (ENDP A) (:P A B C))). This induction is justified by the same argument used to admit APP, namely, the measure (ACL2-COUNT A) is decreasing according to the relation O< (which is known to be well-founded on the domain recognized by O-P). When applied to the goal at hand the above induction scheme produces the following two nontautological subgoals. Next on the Walking Tour: *note Overview of the Simplification of the Induction Step to T::  File: acl2-doc-emacs.info, Node: The Q.E.D. Message, Next: The Rules used in the Associativity of App Proof, Prev: The Proof of the Associativity of App, Up: Pages Written Especially for the Tours The Q.E.D. Message The Q.E.D. Message Q.E.D. stands for "quod erat demonstrandum" which is Latin for "which was to be demonstrated" and is the signal that a proof is completely done.  File: acl2-doc-emacs.info, Node: The Rules used in the Associativity of App Proof, Next: The Simplification of the Induction Conclusion (Step 0), Prev: The Q.E.D. Message, Up: Pages Written Especially for the Tours The Rules used in the Associativity of App Proof The Rules used in the Associativity of App Proof Note that under Rules we list the runes <> of all the rules used in the proof. This list says that we used the rewrite rules CAR-CONS and CDR-CONS, the definitions of the functions NOT, ENDP and APP, and primitive type reasoning (which is how we simplified the IF and EQUAL terms). For what it is worth, IMPLIES and AND are actually macros <> that are expanded into IF expressions before the proof ever begins. The use of macros is not reported among the rules.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 0), Next: The Simplification of the Induction Conclusion (Step 1), Prev: The Rules used in the Associativity of App Proof, Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 0) the Simplification of the Induction Conclusion (Step 0) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP A B) C) (APP A (APP B C)))). Click on the link above to replace (APP A B) by its definition.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 1), Next: The Simplification of the Induction Conclusion (Step 10), Prev: The Simplification of the Induction Conclusion (Step 0), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 1) the Simplification of the Induction Conclusion (Step 1) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (IF (CONSP A) (CONS (CAR A) (APP (CDR A) B)) B) C) (APP A (APP B C)))). Note that the IF expression above is the simplified body of APP. But we know the test (CONSP A) is true, by the first hypothesis. Click on the link above to replace the test by T. Actually this step and several subsequent ones are done during the simplification of the body of APP but we want to illustrate the basic principles of simplification without bothering with every detail.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 10), Next: The Simplification of the Induction Conclusion (Step 11), Prev: The Simplification of the Induction Conclusion (Step 1), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 10) the Simplification of the Induction Conclusion (Step 10) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))). Click on the link above to use the Induction Hypothesis (which is the second of the two hypotheses above and which is identical to the rewritten conclusion).  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 11), Next: The Simplification of the Induction Conclusion (Step 12), Prev: The Simplification of the Induction Conclusion (Step 10), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 11) the Simplification of the Induction Conclusion (Step 11) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) T) Click on the link above to use the definition of IMPLIES. Since the conclusion of the implication is now identically T, the implication simplifies to T.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 12), Next: The Simplification of the Induction Conclusion (Step 2), Prev: The Simplification of the Induction Conclusion (Step 11), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 12) the Simplification of the Induction Conclusion (Step 12) Subgoal *1/2' T So, indeed, Subgoal *1/2' does simplify to T! You can see that even in an example as simple as this one, quite a lot happens in simplification. You may see *note Overview of the Simplification of the Induction Step to T:: to return to the main proof.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 2), Next: The Simplification of the Induction Conclusion (Step 3), Prev: The Simplification of the Induction Conclusion (Step 12), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 2) the Simplification of the Induction Conclusion (Step 2) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (IF T (CONS (CAR A) (APP (CDR A) B)) B) C) (APP A (APP B C)))). Click on the link above to apply the Axiom (IF T x y) = x.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 3), Next: The Simplification of the Induction Conclusion (Step 4), Prev: The Simplification of the Induction Conclusion (Step 2), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 3) the Simplification of the Induction Conclusion (Step 3) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (APP (CONS (CAR A) (APP (CDR A) B)) C) (APP A (APP B C)))). Click on the link above to expand the definition of APP here.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 4), Next: The Simplification of the Induction Conclusion (Step 5), Prev: The Simplification of the Induction Conclusion (Step 3), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 4) the Simplification of the Induction Conclusion (Step 4) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF (CONSP (CONS (CAR A) (APP (CDR A) B))) (CONS (CAR (CONS (CAR A) (APP (CDR A) B))) (APP (CDR (CONS (CAR A) (APP (CDR A) B))) C)) C) (APP A (APP B C)))). Click on the link above to apply the Axiom (CONSP (CONS x y)) = T.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 5), Next: The Simplification of the Induction Conclusion (Step 6), Prev: The Simplification of the Induction Conclusion (Step 4), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 5) the Simplification of the Induction Conclusion (Step 5) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF T (CONS (CAR (CONS (CAR A) (APP (CDR A) B))) (APP (CDR (CONS (CAR A) (APP (CDR A) B))) C)) C) (APP A (APP B C)))). Click on the link above to apply the Axiom (CAR (CONS x y)) = x.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 6), Next: The Simplification of the Induction Conclusion (Step 7), Prev: The Simplification of the Induction Conclusion (Step 5), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 6) the Simplification of the Induction Conclusion (Step 6) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF T (CONS (CAR A) (APP (CDR (CONS (CAR A) (APP (CDR A) B))) C)) C) (APP A (APP B C)))). Click on the link above to apply the Axiom (CDR (CONS x y)) = y.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 7), Next: The Simplification of the Induction Conclusion (Step 8), Prev: The Simplification of the Induction Conclusion (Step 6), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 7) the Simplification of the Induction Conclusion (Step 7) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (IF T (CONS (CAR A) (APP (APP (CDR A) B) C)) C) (APP A (APP B C)))). Click on the link above to apply the Axiom (IF T x y) = x.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 8), Next: The Simplification of the Induction Conclusion (Step 9), Prev: The Simplification of the Induction Conclusion (Step 7), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 8) the Simplification of the Induction Conclusion (Step 8) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (CONS (CAR A) (APP (APP (CDR A) B) C)) (APP A (APP B C)))). Click on the link above to expand the definition of APP here. This time, we'll do the whole expansion at once, including the simplification of the resulting IF. This is how ACL2 actually does it.  File: acl2-doc-emacs.info, Node: The Simplification of the Induction Conclusion (Step 9), Next: The Summary of the Proof of the Trivial Consequence, Prev: The Simplification of the Induction Conclusion (Step 8), Up: Pages Written Especially for the Tours The Simplification of the Induction Conclusion (Step 9) the Simplification of the Induction Conclusion (Step 9) Subgoal *1/2' (IMPLIES (AND (CONSP A) (EQUAL (APP (APP (CDR A) B) C) (APP (CDR A) (APP B C)))) (EQUAL (CONS (CAR A) (APP (APP (CDR A) B) C)) (CONS (CAR A) (APP (CDR A) (APP B C))))). Click on the link above to apply the Axiom that (EQUAL (CONS x y) (CONS u v)) is equal to the conjunction of (EQUAL x u) and (EQUAL y v). In this case, (EQUAL x u) is trivial, (EQUAL (CAR A) (CAR A)).  File: acl2-doc-emacs.info, Node: The Summary of the Proof of the Trivial Consequence, Next: The Theorem that App is Associative, Prev: The Simplification of the Induction Conclusion (Step 9), Up: Pages Written Especially for the Tours The Summary of the Proof of the Trivial Consequence The Summary of the Proof of the Trivial Consequence Note that at the conclusion of the proof, the system reminds you of the earlier Warning. It is a good idea, when the Q.E.D. flys by, to see if there were any Warnings.  File: acl2-doc-emacs.info, Node: The Theorem that App is Associative, Next: The Time Taken to do the Associativity of App Proof, Prev: The Summary of the Proof of the Trivial Consequence, Up: Pages Written Especially for the Tours The Theorem that App is Associative The Theorem that App is Associative Next on the Walking Tour: *note The Proof of the Associativity of App:: ACL2!>(defthm associativity-of-app (equal (app (app a b) c) (app a (app b c)))) The formula above says app is associative. The defthm <> command instructs ACL2 to prove the formula and to name it associativity-of-app. Actually, the defthm command also builds the formula into the database as a rewrite <> rule, but we won't go into that just yet. What we will consider is how the ACL2 theorem prover proves this formula. If you proceed you will find the actual output of ACL2 in response to the command above. Some of the text is highlighted for the purposes of the tour. ACL2 does not highlight its output. You will note that we sometimes highlight a single open parenthesis. This is our way of drawing your attention to the subformula that begins with that parenthesis. By clicking on the parenthesis you will get an explanation of the subformula or its processing. Next on the Walking Tour: *note The Proof of the Associativity of App::  File: acl2-doc-emacs.info, Node: The Time Taken to do the Associativity of App Proof, Next: The Tours, Prev: The Theorem that App is Associative, Up: Pages Written Especially for the Tours The Time Taken to do the Associativity of App Proof The Time Taken to do the Associativity of App Proof The time it took us to explain this proof may leave the impression that the proof is complicated. In a way, it is. But it happens quickly. The time taken to do this proof is about 1/10 second. The rest of the time (about 2/10 seconds) is spent in pre- and post-processing. Basically, this proof flashes across your screen before you can read it; you see the Q.E.D. and don't bother to scroll back to read it. You have more important things to do than read successful proofs.  File: acl2-doc-emacs.info, Node: The Tours, Next: The WARNING about the Trivial Consequence, Prev: The Time Taken to do the Associativity of App Proof, Up: Pages Written Especially for the Tours The Tours The Tours ACL2 is a very large, multipurpose system. You can use it as a programming language, a specification language, a modeling language, a formal mathematical logic, or a semi-automatic theorem prover, just to name its most common uses. It has been used on a number of industrial applications. If you're uncertain as to whether your project is appropriate for ACL2 we urge you to look over this list or contact the ACL2 developers. This home page includes all of ACL2's online documentation, which is quite extensive (over 4 megabytes). To help ease your introduction to ACL2, we have built two tours through this documentation. If you are familiar with at least some of the industrial applications of ACL2, then you will understand the distance between the simple examples we talk about in these tours and the kinds of things ACL2 users do with the system. Newcomers to ACL2 should first take the "Flying Tour." Then, if you want to know more, take the "Walking Tour." On your first reading, follow the two Tours linearly, clicking only on the icon of the Tour you're on. Beware of other links, which might jump you from one tour to the other or into the reference manual! Once you've had a coherent overview of the system, you might quickly repeat both Tours to see if there are unvisited links you're interested in, using your brower's Back Button to return to your starting points. If after all this you want to learn how to use the theorem prover (!), see *note INTRODUCTION-TO-THE-THEOREM-PROVER::. To start a tour, click on the appropriate icon below. Next on the Flying Tour: *note A Flying Tour of ACL2:: Next on the Walking Tour: *note A Walking Tour of ACL2:: For readers using our :DOC or our TexInfo format in Emacs: The tours will probably be unsatisfying because we use gif files and assume you can navigate "back."  File: acl2-doc-emacs.info, Node: The WARNING about the Trivial Consequence, Next: Undocumented Topic, Prev: The Tours, Up: Pages Written Especially for the Tours The WARNING about the Trivial Consequence The WARNING about the Trivial Consequence This Warning alerts us to the fact that when treated as a rewrite rule, the new rule TRIVIAL-CONSEQUENCE, rewrites terms of the same form as a rule we have already proved, namely ASSOCIATIVITY-OF-APP. When you see this warning you should think about your rules! In the current case, it would be a good idea not to make TRIVIAL-CONSEQUENCE a rule at all. We could do this with :rule-classes <> nil. ACL2 proceeds to try to prove the theorem, even though it printed some warnings. The basic assumption in ACL2 is that the user understands what he or she is doing but may need a little reminding just to manage a complicated set of facts.  File: acl2-doc-emacs.info, Node: Undocumented Topic, Next: Using the Associativity of App to Prove a Trivial Consequence, Prev: The WARNING about the Trivial Consequence, Up: Pages Written Especially for the Tours Undocumented Topic Undocumented Topic This topic has not yet been documented. Sorry  File: acl2-doc-emacs.info, Node: Using the Associativity of App to Prove a Trivial Consequence, Next: What Is ACL2(Q), Prev: Undocumented Topic, Up: Pages Written Especially for the Tours Using the Associativity of App to Prove a Trivial Consequence Using the Associativity of App to Prove a Trivial Consequence Next on the Walking Tour: *note Overview of the Proof of a Trivial Consequence:: If we have proved the associativity-of-app rule, then the following theorem is trivial: (defthm trivial-consequence (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7) (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7)))))) Below we show the proof Next on the Walking Tour: *note Overview of the Proof of a Trivial Consequence::  File: acl2-doc-emacs.info, Node: What Is ACL2(Q), Next: What is Required of the User(Q), Prev: Using the Associativity of App to Prove a Trivial Consequence, Up: Pages Written Especially for the Tours What Is ACL2(Q) What Is ACL2? Next on the Flying Tour: *note What is a Mathematical Logic(Q):: ACL2 is a mathematical logic together with a mechanical theorem prover to help you reason in the logic. The logic is just a subset of applicative Common Lisp. (This link takes you off the main route of the tour. You'll see some Common Lisp on the tour, so visit this later!) The theorem prover is an "industrial strength" version of the Boyer-Moore theorem prover, Nqthm. Models of all kinds of computing systems can be built in ACL2, just as in Nqthm, even though the formal logic is Lisp. Once you've built an ACL2 model of a system, you can run it. You can also use ACL2 to prove theorems about the model. Next on the Flying Tour: *note What is a Mathematical Logic(Q)::  File: acl2-doc-emacs.info, Node: What is Required of the User(Q), Next: What is a Mathematical Logic(Q), Prev: What Is ACL2(Q), Up: Pages Written Especially for the Tours What is Required of the User(Q) What is Required of the User? Next on the Flying Tour: *note How Long Does It Take to Become an Effective User(Q):: It is not easy to build ACL2 models of complex systems. To do so, the user must understand * the system being modeled, and * ACL2, at least as a programming language. It is not easy to get ACL2 to prove hard theorems. To do so, the user must understand * the model, * ACL2 as a mathematical logic, and * be able to construct a proof (in interaction with ACL2). ACL2 will help construct the proof but its primary role is to prevent logical mistakes. The creative burden -- the mathematical insight into why the model has the desired property -- is the user's responsibility. Next on the Flying Tour: *note How Long Does It Take to Become an Effective User(Q)::  File: acl2-doc-emacs.info, Node: What is a Mathematical Logic(Q), Next: What is a Mechanical Theorem Prover(Q), Prev: What is Required of the User(Q), Up: Pages Written Especially for the Tours What is a Mathematical Logic(Q) What is a Mathematical Logic? Next on the Flying Tour: *note What is a Mechanical Theorem Prover(Q):: A mathematical logic is a formal system of formulas (axioms) and rules for deriving other formulas, called theorems. A proof is a derivation of a theorem. To see a concrete proof tree, see *note A Trivial Proof::. Why should you care? The neat thing about Theorems is that they are "true." More precisely, if all the axioms are valid and the rules are validity preserving, then anything derived from the axioms via the rules is valid. So, if you want to determine if some formula is true, prove it. Next on the Flying Tour: *note What is a Mechanical Theorem Prover(Q)::  File: acl2-doc-emacs.info, Node: What is a Mechanical Theorem Prover(Q), Next: What is a Mechanical Theorem Prover(Q) (cont), Prev: What is a Mathematical Logic(Q), Up: Pages Written Especially for the Tours What is a Mechanical Theorem Prover(Q) What is a Mechanical Theorem Prover? Next on the Flying Tour: *note What is a Mechanical Theorem Prover(Q) (cont):: A mechanical theorem prover is a computer program that finds proofs of theorems. The ideal mechanical theorem prover is automatic: you give it a formula and it gives you a proof of that formula or tells you there is no proof. Unfortunately, automatic theorem provers can be built only for very simple logics (e.g., propositional calculus) and even then practical considerations (e.g., how many centuries you are willing to wait) limit the problems they can solve. Next on the Flying Tour: *note What is a Mechanical Theorem Prover(Q) (cont)::  File: acl2-doc-emacs.info, Node: What is a Mechanical Theorem Prover(Q) (cont), Next: You Must Think about the Use of a Formula as a Rule, Prev: What is a Mechanical Theorem Prover(Q), Up: Pages Written Especially for the Tours What is a Mechanical Theorem Prover(Q) (cont) What is a Mechanical Theorem Prover? (cont) Next on the Flying Tour: *note About Models:: To get around this, mechanical theorem provers often require help from the user. See *note ACL2 as an Interactive Theorem Prover:: to continue downward. Next on the Flying Tour: *note About Models::  File: acl2-doc-emacs.info, Node: You Must Think about the Use of a Formula as a Rule, Prev: What is a Mechanical Theorem Prover(Q) (cont), Up: Pages Written Especially for the Tours You Must Think about the Use of a Formula as a Rule You Must Think about the Use of a Formula as a Rule Next on the Walking Tour: *note Using the Associativity of App to Prove a Trivial Consequence:: This is good and bad. The good news is that you can program ACL2's simplifier. The bad news is that when you command ACL2 to prove a theorem you must give some thought to how that theorem is to be used as a rule! For example, if after proving associativity-of-app as previously shown, you engaged in the mathematically trivial act of proving it again but with the equality reversed, you would have programmed ACL2's rewriter to loop forever. You can avoid adding any rule by using the command: (defthm associativity-of-app (equal (app (app a b) c) (app a (app b c))) :rule-classes nil) Next on the Walking Tour: *note Using the Associativity of App to Prove a Trivial Consequence::  File: acl2-doc-emacs.info, Node: REAL, Next: RELEASE-NOTES, Prev: Pages Written Especially for the Tours, Up: Top REAL ACL2(r) support for real numbers ACL2 supports rational numbers but not real numbers. However, starting with Version 2.5, a variant of ACL2 called "ACL2(r)" supports the real numbers by way of non-standard analysis. ACL2(r) was conceived and first implemented by Ruben Gamboa in his Ph.D. dissertation work, supervised by Bob Boyer with active participation by Matt Kaufmann. ACL2(r) has the same source files as ACL2. After you download ACL2, you can build ACL2(r) by executing the following command on the command line in your acl2-sources directory, replacing with a path to your Lisp executable: make large-acl2r LISP= This will create an executable in your acl2-sources directory named saved_acl2r. Note that if you download community books as tarfiles, then you should be sure to download the `nonstd' books, from `http://acl2-books.googlecode.com/files/nonstd-6.3.tar.gz'. Then certify them from your acl2-sources directory, shown here as
    : make regression-nonstd ACL2=/saved_acl2r To check that you are running ACL2(r), see if the prompt includes the string "(r)", e.g.: ACL2(r) !> Or, look at (@ acl2-version) and see if "(r)" is a substring. In ACL2 (as opposed to ACL2(r)), when we say "real" we mean "rational." * Menu: * I-CLOSE:: ACL2(r) test for whether two numbers are infinitesimally close * I-LARGE:: ACL2(r) recognizer for infinitely large numbers * I-LIMITED:: ACL2(r) recognizer for limited numbers * I-SMALL:: ACL2(r) recognizer for infinitesimal numbers * REAL-LISTP:: ACL2(r) recognizer for a true list of real numbers * STANDARD-PART:: ACL2(r) function mapping limited numbers to standard numbers * STANDARDP:: ACL2(r) recognizer for standard objects Caution: ACL2(r) should be considered experimental: although we (Kaufmann and Moore) have carefully completed Gamboa's integration of the reals into the ACL2 source code, our primary concern has been to ensure unchanged behavior when ACL2 is compiled in the default manner, i.e., without the non-standard extensions. As for every release of ACL2, at the time of a release we are unaware of soundness bugs in ACL2 or ACL2(r). There is only limited documentation on the non-standard features of ACL2(r). We hope to provide more documentation for such features in future releases. Please feel free to query the authors if you are interested in learning more about ACL2(r). Gamboa's dissertation may also be helpful.  File: acl2-doc-emacs.info, Node: I-CLOSE, Next: I-LARGE, Prev: REAL, Up: REAL I-CLOSE ACL2(r) test for whether two numbers are infinitesimally close (I-close x y) is true if and only if x-y is an infinitesimal number. This predicate is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: I-LARGE, Next: I-LIMITED, Prev: I-CLOSE, Up: REAL I-LARGE ACL2(r) recognizer for infinitely large numbers (I-large x) is true if and only if x is non-zero and 1/x is an infinitesimal number. This predicate is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: I-LIMITED, Next: I-SMALL, Prev: I-LARGE, Up: REAL I-LIMITED ACL2(r) recognizer for limited numbers (I-limited x) is true if and only if x is a number that is not infinitely large. This predicate is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: I-SMALL, Next: REAL-LISTP, Prev: I-LIMITED, Up: REAL I-SMALL ACL2(r) recognizer for infinitesimal numbers (I-small x) is true if and only if x is an infinitesimal number (possibly 0). This predicate is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: REAL-LISTP, Next: STANDARD-PART, Prev: I-SMALL, Up: REAL REAL-LISTP ACL2(r) recognizer for a true list of real numbers The predicate real-listp tests whether its argument is a true list of real numbers. This predicate is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: STANDARD-PART, Next: STANDARDP, Prev: REAL-LISTP, Up: REAL STANDARD-PART ACL2(r) function mapping limited numbers to standard numbers (Standard-part x) is, for a given i-limited number x, the unique real number infinitesimally close (see *note I-CLOSE::) to x. This function is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: STANDARDP, Prev: STANDARD-PART, Up: REAL STANDARDP ACL2(r) recognizer for standard objects (Standardp x) is true if and only if x is a "standard" object. This notion of "standard" comes from non-standard analysis and is discussed in Ruben Gamboa's dissertation. In brief, all the familiar objects are standard: e.g., the familiar real numbers are standard, but non-zero infinitesimals are not standard, and the familiar integers are standard, but not those that exceed every integer that you can express in the usual way (1, 2, 3, and so on). Similarly, the familiar lists are standard, but not so a list that contains a large number of integers, where "large" means more than the standard integers. The set of standard numbers is closed under the usual arithmetic operations, hence the sum of a standard number and a non-zero infinitesimal is not standard, though it is what is called "limited" (see *note I-LIMITED::). This predicate is only defined in ACL2(r) (see *note REAL::).  File: acl2-doc-emacs.info, Node: RELEASE-NOTES, Next: RULE-CLASSES, Prev: REAL, Up: Top RELEASE-NOTES pointers to what has changed * Menu: * NOTE-2-0:: ACL2 Version 2.0 (July, 1997) Notes * NOTE-2-1:: ACL2 Version 2.1 (December, 1997) Notes * NOTE-2-2:: ACL2 Version 2.2 (August, 1998) Notes * NOTE-2-3:: ACL2 Version 2.3 (October, 1998) Notes * NOTE-2-4:: ACL2 Version 2.4 (August, 1999) Notes * NOTE-2-5:: ACL2 Version 2.5 (June, 2000) Notes * NOTE-2-5(R):: ACL2 Version 2.5(r) (June, 2000) Notes * NOTE-2-6:: ACL2 Version 2.6 (November, 2001) Notes * NOTE-2-6(R):: ACL2 Version 2.6(r) (November, 2001) Notes * NOTE-2-7:: ACL2 Version 2.7 (November, 2002) Notes * NOTE-2-7(R):: ACL2 Version 2.7(r) (November, 2002) Notes * NOTE-2-8:: ACL2 Version 2.8 (March, 2004) Notes * NOTE-2-8(R):: ACL2 Version 2.8(r) (March, 2003) Notes * NOTE-2-9:: ACL2 Version 2.9 (October, 2004) Notes * NOTE-2-9(R):: ACL2 Version 2.9(r) (October, 2004) Notes * NOTE-2-9-1:: ACL2 Version 2.9.1 (December, 2004) Notes * NOTE-2-9-2:: ACL2 Version 2.9.2 (April, 2005) Notes * NOTE-2-9-3:: ACL2 Version 2.9.3 (August, 2005) Notes * NOTE-2-9-4:: ACL2 Version 2.9.4 (February, 2006) Notes * NOTE-2-9-5:: Changes in Version 3.0 since Version 2.9.4 * NOTE-3-0:: ACL2 Version 3.0 (June, 2006) Notes * NOTE-3-0(R):: ACL2 Version 3.0(r) (June, 2006) Notes * NOTE-3-0-1:: ACL2 Version 3.0.1 (August, 2006) Notes * NOTE-3-0-1(R):: ACL2 Version 3.0.1(r) (August, 2006) Notes * NOTE-3-0-2:: ACL2 Version 3.0.2 (December, 2006) Notes * NOTE-3-1:: ACL2 Version 3.1 (December, 2006) Notes * NOTE-3-1(R):: ACL2 Version 3.1(r) (December, 2006) Notes * NOTE-3-2:: ACL2 Version 3.2 (April, 2007) Notes * NOTE-3-2(R):: ACL2 Version 3.2(r) (April, 2007) Notes * NOTE-3-2-1:: ACL2 Version 3.2.1 (June, 2007) Notes * NOTE-3-2-1(R):: ACL2 Version 3.2.1(r) (June, 2007) Notes * NOTE-3-3:: ACL2 Version 3.3 (November, 2007) Notes * NOTE-3-3(R):: ACL2 Version 3.3(r) (November, 2007) Notes * NOTE-3-4:: ACL2 Version 3.4 (August, 2008) Notes * NOTE-3-4(R):: ACL2 Version 3.4(r) (August, 2008) Notes * NOTE-3-5:: ACL2 Version 3.5 (May, 2009) Notes * NOTE-3-5(R):: ACL2 Version 3.5(r) (May, 2009) Notes * NOTE-3-6:: ACL2 Version 3.6 (August, 2009) Notes * NOTE-3-6(R):: ACL2 Version 3.6(r) (August, 2009) Notes * NOTE-3-6-1:: ACL2 Version 3.6.1 (September, 2009) Notes * NOTE-4-0:: ACL2 Version 4.0 (July, 2010) Notes * NOTE-4-0(R):: ACL2 Version 4.0(r) (July, 2010) Notes * NOTE-4-1:: ACL2 Version 4.1 (September, 2010) Notes * NOTE-4-1(R):: ACL2 Version 4.1(r) (September, 2010) Notes * NOTE-4-2:: ACL2 Version 4.2 (January, 2011) Notes * NOTE-4-2(R):: ACL2 Version 4.2(r) (January, 2011) Notes * NOTE-4-3:: ACL2 Version 4.3 (July, 2011) Notes * NOTE-4-3(R):: ACL2 Version 4.3(r) (July, 2011) Notes * NOTE-5-0:: ACL2 Version 5.0 (August, 2012) Notes * NOTE-6-0:: ACL2 Version 6.0 (December, 2012) Notes * NOTE-6-1:: ACL2 Version 6.1 (February, 2013) Notes * NOTE-6-2:: ACL2 Version 6.2 (June, 2013) Notes * NOTE-6-3:: ACL2 Version 6.3 (October, 2013) Notes * NOTE1:: Acl2 Version 1.1 Notes * NOTE2:: Acl2 Version 1.2 Notes * NOTE3:: Acl2 Version 1.3 Notes * NOTE4:: Acl2 Version 1.4 Notes * NOTE5:: Acl2 Version 1.5 Notes * NOTE6:: Acl2 Version 1.6 Notes * NOTE7:: ACL2 Version 1.7 (released October 1994) Notes * NOTE8:: ACL2 Version 1.8 (May, 1995) Notes * NOTE8-UPDATE:: ACL2 Version 1.8 (Summer, 1995) Notes * NOTE9:: ACL2 Version 1.9 (Fall, 1996) Notes This section of the online documentation contains notes on the changes that distinguish successive released versions of ACL2. The current version of ACL2 is the value of the constant (@ acl2-version).  File: acl2-doc-emacs.info, Node: NOTE-2-0, Next: NOTE-2-1, Prev: RELEASE-NOTES, Up: RELEASE-NOTES NOTE-2-0 ACL2 Version 2.0 (July, 1997) Notes This is the first version of ACL2 released under the copyright of the University of Texas (UT). Future releases of ACL2 will be made from UT rather than Computational Logic, Inc. (CLI). Version 2.0 is just Version 1.9 as released by CLI, with a few bugs fixed. A bug causing an infinite loop was fixed in functional instantiation. The bug manifested itself when two conditions occurred simultaneously: First, the functional substitution replaces a function symbol, e.g., FOO, with a LAMBDA expression containing a free variable (a variable not among in the LAMBDA formals). And, second, in one of the constraints being instantiated there is a call of the function symbol FOO within the scope of another LAMBDA expression. Unless you used such a functional substitution, this bug fix will not affect you. Less important notes: The implementation of PRINC$ was changed so that it was no longer sensitive to the external setting of *print-base* and other Common Lisp special variables. Typographical errors were fixed in the documentation.  File: acl2-doc-emacs.info, Node: NOTE-2-1, Next: NOTE-2-2, Prev: NOTE-2-0, Up: RELEASE-NOTES NOTE-2-1 ACL2 Version 2.1 (December, 1997) Notes The identity function case-split has been added. It is similar to force but causes an immediate split of the top-level goal on whether the indicated hypothesis is true. Less important notes: Minor bugs in the documentation were fixed.  File: acl2-doc-emacs.info, Node: NOTE-2-2, Next: NOTE-2-3, Prev: NOTE-2-1, Up: RELEASE-NOTES NOTE-2-2 ACL2 Version 2.2 (August, 1998) Notes Important changes: A bug was fixed in the compile command, :comp. The compiled code produced by :comp in previous versions could be wildly incorrect because of a confusion between the printer and the reader regarding what was the current Lisp *package*. This bug could manifest itself only if you used the :comp command to compile previously uncompiled functions while the current package was different from "ACL2". What happened in that situation depended upon what symbols were imported into your current package. The most likely behavior is that the compiler would break or complain or the resulting compiled code would call functions that did not exist. There have been no other important changes to the code. However, this release contains some useful new books, notably those on the books subdirectories cli-misc and ihs. Both have README files. The ihs books provide support for integer hardware specifications. These books were crucial to Bishop Brock's successful modeling of the Motorola CAP. We thank Bishop for producing them and we thank all those who worked so hard to get these books released. We highly recommend the ihs books to those modeling ALUs and other arithmetic components of microprocessors or programming languages. In previous versions of ACL2, the arithmetic books, found on books/arithmetic/, included the addition of several unproved axioms stating properties of the rationals that we believed could be derived from our "official" axioms but which we had not mechanically proved. The axioms were found in the book rationals-with-axioms.lisp, which was then used in the uppermost arithmetic books top.lisp and top-with-meta.lisp. John Cowles has now provided us with ACL2 proofs of those "axioms" and so in this release you will find both rationals-with-axioms.lisp and rationals-with-axioms-proved.lisp. The former is provided for compatibility's sake. The latter is identical but contains defthms where the former contains defaxioms. The top-most books have been rebuilt using "-axioms-proved" book. Thanks John. Less important notes: Bishop Brock found a bug in translated-acl2-unwind-protectp4. Jun Sawada reported a bug in linear arithmetic that caused us not to prove certain trivial theorems concluding with (not (equal i j)). We have fixed both. We now prohibit definitions that call certain event commands such as DEFTHM and TABLE because our Common Lisp implementations of them differ from their ACL2 meanings (so that compiled books can be loaded correctly and efficiently). Minor bugs in the documentation were fixed.  File: acl2-doc-emacs.info, Node: NOTE-2-3, Next: NOTE-2-4, Prev: NOTE-2-2, Up: RELEASE-NOTES NOTE-2-3 ACL2 Version 2.3 (October, 1998) Notes Important changes: Versions of ACL2 preceding this one contain a subtle soundness bug! We found a flaw in our detection of subversive-recursions. The bug allowed some subversive recursions to slip through undetected. We believe it would have been difficult to have exploited this flaw inadvertently. In particular, the following five conditions are necessary. (1) Introduce a constrained function, say f, via an encapsulate. (2) In the same encapsulation, define a clique of mutually recursive functions. This clique must be non-local and in :logic mode. (3) In that mutually recursive clique, use the constrained function f (perhaps indirectly) so that the termination argument for the clique depends on properties of the witness for f. Thus, f or some other function dependent upon f, must be used in an argument in a recursive call or in a term governing a recursive call. Furthermore, the use of f must be such that the termination proof cannot be done without exploiting properties of the witness for f. Other uses of the constrained functions in the clique are ok. (4) Fail to include the exploited properties of f among the constraints of the encapsulation. (5) Later, outside the encapsulation, explicitly use a functional instantiation in which f is replaced by a function not enjoying the crucial properties. See subversive-recursions for details. Less important notes: We have begun to write some introductory tutorial material for those who wish to learn to program in ACL2. Most of this material is HTML-based. See the Hyper-Card on the ACL2 home page. The documentation of verify-guards was improved to explain why one might wish to verify the "guards" of a defthm event. The missing documentation was noticed by John Cowles. A bug was fixed in cross fertilization. The bug caused the system to report that it had substituted one term for another when in fact no substitution occurred. The bug was noticed by Bill McCune.  File: acl2-doc-emacs.info, Node: NOTE-2-4, Next: NOTE-2-5, Prev: NOTE-2-3, Up: RELEASE-NOTES NOTE-2-4 ACL2 Version 2.4 (August, 1999) Notes Important changes: We corrected a soundness bug in Version 2.3 related to the handling of immediate-force-modep. The bad behavior was noticed by Robert Krug. Thanks! We corrected a bug that permitted verify-guards to accept a function even though a subfunction had not yet had its guards verified. Thanks to John Cowles for noticing this. User defined single-threaded objects are now supported. See stobj. Less important notes: We corrected a bug that prevented the intended expansion of some recursive function calls. We changed the handling of the primitive function ILLEGAL, which is logically defined to be nil but which is programmed to signal an error, so that when it is evaluated as part of a proof, it does not signal an error. The old handling of the function prevented some guard proofs involving THE or LETs with internal declarations. We corrected a bug that permitted some LOCAL DEFAXIOM events to slip into certified books. We corrected a bug that prevented the correct undoing of certain DEFPKG forms. Changes were made to support CMU Lisp. Pete Manolios helped with these changes. Changes were made to make the make files more compatible with Allegro Common Lisp. Jun Sawada, who has been a great help with keeping ACL2 up and running at UT on various platforms, was especially helpful. Thanks Jun.  File: acl2-doc-emacs.info, Node: NOTE-2-5, Next: NOTE-2-5(R), Prev: NOTE-2-4, Up: RELEASE-NOTES NOTE-2-5 ACL2 Version 2.5 (June, 2000) Notes Important Changes: Concurrent with the release of ACL2 Version 2.5 is the publication of two books about ACL2. See the "Books and Papers about ACL2 and Its Applications" on the ACL2 Home Page. The books subdirectory now contains many new certifiable books, including solutions to the exercises in the two published books and full scripts for the case studies. See books/README.html. Improved Unix Makefile support for book certification has also been written. See books/README.html. The list of symbols in *acl2-exports* has been considerably expanded. If you have packages built by importing *acl2-exports* you might want to look carefully at the new value of that constant. The new value includes all :logic mode functions as of Version 2.5, as well as all documented macros and all built-in theorem names. Include-book and certify-book were modified to have some additional keyword arguments. It is possible to certify a book containing defaxiom and/or skip-proofs events and get warning messages or errors signaled, according to the settings of these new flags. In addition, it is possible to specify in include-book whether the book must be certified (under penalty of error if not). The default values of these new arguments cause warnings to be printed rather than errors signaled. The above change involved altering the form of certificate files. When books certified under previous versions are included, more warnings will be generated because these books are considered possibly to contain defaxiom and/or skip-proofs events. We anticipate further changes to this aspect of books and consider the current mechanisms (for controlling whether warnings or errors are signaled) just a prototype. See also the discussion below of "soundness related" warnings. Your suggestions are welcome. A discrepancy between ACL2 and Common Lisp was fixed, having to do with declare ignore. In past versions of ACL2, a formal parameter of a defun was considered ignored if it was not used in the body, the guard or the measure of the defun. That meant that a variable used only in the guard could not be declared ignored in ACL2; but some Common Lisp compilers would complain because the variable was not used in the body. Now, ACL2 considers a variable ignored if it is not used in the body. ACL2 can now be built in releases 5.0 and later of Allegro Common Lisp. (Other releases of Allegro Common Lisp and of other lisps continue to be supported as well.) This includes Allegro Common Lisp running on Windows 98 platforms. John Cowles helped us do some testing and answered questions for us. Thanks John! We incorporated Ruben Gamboa's changes to allow the building of a variant, ACL2(r), of ACL2, in which the user can reason about the real numbers using non-standard analysis. See *note REAL::. Note that ACL2(r) and ACL2 have different underlying theories, and books certified in one system may not be included in the other. For backward compatibility and to ensure a smooth transition, ACL2 is built by default, not ACL2(r). This is a compile-time switch; see the makefile for instructions. There should be no changes to ACL2 resulting from the capability of building ACL2(r) from the same sources. Also see *note ACKNOWLEDGMENTS:: for more on the history of ACL2(r). A large number of bugs (some affecting soundness) were fixed, and many small new features were added. See below. Less Important Changes: Some warnings are now considered "soundness related," namely, those that advise you that an uncertified book has been included or that a book containing DEFAXIOM or SKIP-PROOFS events. (Technically, DEFAXIOMs do not imperil soundness in the proof- theoretic sense, though they may imperil the validity of theorems. But you sould know when a book has added an axiom to your logic!) In previous versions of ACL2, all warnings were inhibited if the token warning was included in the argument to set-inhibit-output-lst. Now, soundness related warnings are printed even if warnings have been inhibited. To inhibit all warnings, supply the token warning! to set-inhibit-output-lst. Several bugs in defstobj were fixed, relating to the possibility that some of the subfunctions introduced by the defstobj were already defined. :Puff no longer tries to expand defstobj events. Previously, the attempt would cause a hard error. A soundness bug was fixed. The bug might have been exercised if you had an alternative definition (implies hyps (equiv (fn ...) body)) in which equiv is an equivalence relation other than EQUAL. In this case, calls of fn might have been expanded to body in places that were not equiv-hittable. An obscure soundness bug was fixed. The bug was exercised only if you had a metafunction with a computed hypothesis (i.e., a "meta hypothesis function"), the hypothesis contained a free variable, i.e., a variable not involved in the term being rewritten, and the free variable occurred in the output of the metafunction. The possibility of this bug was brought to our attention by Robert Krug. We fixed a bug in the handling of hide related to the question of whether a variable symbol occurs in a term. The old code did not find the variable and could cause the system to throw away a hypothesis about it on the grounds that it was never mentioned. Rob Sumners helped discover this problem. The handling of :elim rules was generalized, permitting arbitrary known equivalence relations instead of merely equal in the concluding equality. The printing of runes (rule names; see *note RUNE::) used has been made "deterministic," both in proof output and in proof attempt summaries, by sorting the runes before printing. The handling of free variables has been improved for hypotheses such as (< 0 X), and more generally, any hypotheses involving a comparison with 0 (even for example (< X 1) where X is known to be an integer, which is handled as (<= X 0)). Thanks to Robert Krug for bringing relevant examples to our attention. A new value, :comp, has been implemented for the :load-compiled-file keyword of include-book. If this value is supplied, then a compiled file will always be loaded, even if that requires creating the compiled file first. The event include-book now generates a warning when a compiled file is expected but not found (see *note INCLUDE-BOOK::). Formerly, it only did so when executed at the top level; it failed to generate the warning when executed on behalf of a surrounding include-book command. Certain redefinition warnings generated by Allegro Common Lisp have been eliminated. A new key has been implemented for the acl2-defaults-table, :bogus-mutual-recursion-ok, set with :set-bogus-mutual-recursion-ok. Thanks to David Russinoff for pointing out the utility of such a key. A bug was fixed in defun-sk that prevented its generated events from being accepted when guard verification is being performed. Thanks to Bill Young for bringing this problem to our attention. A second bug was brought to our attention by Pete Manolios, which was causing certain defun-sk events to be rejected. That problem has been fixed, and an "Infected" warning has also been eliminated. The command good-bye now works with Allegro Common Lisp. A low-level bug was fixed that could, for example, cause an error such as "Error: Expected 5 args but received 4 args" when interrupting a local event. A bug has been fixed in the proof-checker related to definition expansion. Thanks to Pete Manolios for bringing this to our attention with a simple example. A bug has been fixed related to the :bdd hint in the presence of equivalence relations. Thanks to Pete Manolios for bringing this to our attention with a simple example. The functions position and position-equal formerly required the second argument to be a true list. In accordance with Common Lisp, we now also allow the second argument to be a string. This could cause earlier proofs about these functions to fail unless true-listp is known to hold where necessary. Robert Krug wrote a patch, which has been incorporated, to prevent certain infinite loops that can arise in linear arithmetic. Thanks, Robert! The macro let* no longer requires the bound variables to be distinct. An obscure bug was fixed related to congruence rules. The bug would sometimes cause ACL2 to behave as though no rules (other than equality) were available for some argument positions. Thanks to Pete Manolios for bringing this bug to our attention. Documentation topics have been added for hard-error and prog2$, and the documentation for illegal has been improved. Thanks to Rob Sumners for a useful suggestion in the examples in documentation for prog2$ and a fix in documentation for sublis. The event form certify-book was made more secure, in that it can now catch attempts to write a book to disk during its certification. Thanks to Rob Sumners for pointing out the insecurity of the existing mechanism. A Y2K problem was fixed with our applicative handling of dates. Accessors and updaters for stobjs have been made more efficient when the underlying lisp is Allegro Common Lisp, by the use of appropriate simple array declarations. A raw Lisp break had been possible when a certified book that had no guard verification was included in a session after (set-verify-guards-eagerness 2). This has been fixed. The keyword command :comp can now be used to compile only raw Lisp functions, excluding executable counterparts, by supplying the argument :raw. Rewrite rule nth-of-character-listp was removed from source file axioms.lisp since it is essentially subsumed by characterp-nth. Printing has been sped up. In one example the improvement was over 50% in both Allegro and GCL. We now allow printing in a "downcase" mode, where symbols are printed in lower case. All printing functions except print-object$ now print characters in lower case for a symbol when the ACL2 state global variable print-case has value :downcase and vertical bars are not necessary for printing that symbol. See *note IO:: for a discussion of the macros acl2-print-case and set-acl2-print-case. The default printing remains unchanged, i.e., symbols are printed in upper case when vertical bars are not required. A low-level printing function (prin1$) was modified so that it is not sensitive to various Common Lisp globals related to printing. So for example, the function fmt is no longer sensitive to the value of Common Lisp global *print-case*. (The preceding paragraph explains how to control the case for printing in ACL2.) The definition of array1p was fixed so that the :maximum-length of an array must be strictly greater than the number specified in the :dimensions field; they may no longer be equal. This was always the intention; the documentation (see *note ARRAYS::) has remained unchanged. The corresponding change was also made to array2p. Allegro Common Lisp formerly caused an error when compress1 was called on an array where the numbers above were equal; now, we get a guard violation instead, which is appropriate. In the context of theories, a name now represents not just the corresponding :definition rune, as it has done in earlier versions of ACL2, but also the corresponding :induction rune. See *note THEORIES:: for a discussion of runic designators. Most users will rarely, if ever, notice this change. One situation where this change will make a difference is after executing (in-theory (current-theory 'foo)) followed by (in-theory (enable bar)), where function bar is introduced after event foo, and bar is recursively defined. The latter in-theory form now enables the rune (:induction bar), which implies that the prover can use the induction scheme stored at definition time for bar. Formerly, the rune (:induction bar) was not enabled by (in-theory (enable bar)), and hence the induction scheme for bar was ignored even when explicit :induct hints were supplied. You may now supply xargs keyword pair :normalize nil in order to prevent certain definitions from "hanging" when there are many if-subexpressions. see *note DEFUN::. We now translate type declarations of real into guards, as we have already done for other types such as rational. For example, (declare (type real x)) generates the guard (rationalp x). See *note TYPE-SPEC::. The theorem prover now behaves reasonably under the combination of specifying a value of t both for :otf-flg and for a hint :do-not-induct. Previously, it aborted the first time it would have otherwise pushed a goal for induction, but now, it will continue and wait until all induction subgoals have been pushed before it aborts. We changed slightly the definition of round. However, we believe that the new definition is equivalent to the old. The definition of Common Lisp function substitute has been added. The following changes have been made in the use of file names within ACL2. We thank Warren Hunt and John Cowles for running some tests of these changes on Macintosh and Windows 98 platforms (respectively). (1) Names of directories and files now use a syntax like that used for Unix (trademark of AT&T), where directories are separated using the "/" character even when the operating system is not Unix or Linux. See *note PATHNAME::. ACL2 also continues to support its notion of _structured pathnames_ from Version 2.4 and before, but might not do so in future releases and hence no longer documents such syntax. (2) The command :set-cbd may now take a relative pathname as an argument. (3) When the macro ld is given a file name as a value for standard-oi, then if that file name is a relative pathname it refers to the result of prepending the connected book directory (see *note PATHNAME::, see *note CBD::, and see *note SET-CBD::) in order to obtain an absolute pathname. Simiarly for the ld specials standard-co and proofs-co. It is no longer necessary to issue :set-state-ok t if you include a stobj declaration for state, for example: (declare (xargs :stobjs state)) See *note DECLARE-STOBJS::. The proof-checker has been cleaned up a bit, including the documentation and the capability (once again) to define pc-macro commands (see *note DEFINE-PC-MACRO::) and proof-checker meta commands (see *note DEFINE-PC-META::). Recall that events generate summaries that include a line beginning with "Warnings:", which is followed (on the same line) by zero or more brief strings that summarize the warnings generated by that event. Formerly, this warnings summary for an encapsulate or include-book event did not include the summary strings for warnings generated by subsidiary events. This has been fixed. Macro cw has been documented and now expands to a call of a ;logic mode function. See *note CW:: for a way to print to the screen without having to involve the ACL2 state. Thanks to Rob Sumners for suggesting that we document this useful utility. Functions duplicates, add-to-set-equal, intersection-eq, evens, and odds are now :logic mode functions.  File: acl2-doc-emacs.info, Node: NOTE-2-5(R), Next: NOTE-2-6, Prev: NOTE-2-5, Up: RELEASE-NOTES NOTE-2-5(R) ACL2 Version 2.5(r) (June, 2000) Notes Important changes to non-standard version: Please see *note NOTE-2-5:: for changes to Version 2.5 of ACL2. We hope to write more documentation for ACL2(r) in the future.  File: acl2-doc-emacs.info, Node: NOTE-2-6, Next: NOTE-2-6(R), Prev: NOTE-2-5(R), Up: RELEASE-NOTES NOTE-2-6 ACL2 Version 2.6 (November, 2001) Notes Because of the large number of modifications, we have divided up the Version 2.6 notes into the following subtopics. o New functionality (see *note NOTE-2-6-NEW-FUNCTIONALITY::): o Changes in proof engine (see *note NOTE-2-6-PROOFS::): o Changes in rules and definitions (see *note NOTE-2-6-RULES::): o Guard-related changes (see *note NOTE-2-6-GUARDS::): o Proof-checker changes (see *note NOTE-2-6-PROOF-CHECKER::): o System-level changes (see *note NOTE-2-6-SYSTEM::): o Other (minor) changes (see *note NOTE-2-6-OTHER::): * Menu: * NOTE-2-6-GUARDS:: ACL2 Version 2.6 Notes on Guard-related Changes * NOTE-2-6-NEW-FUNCTIONALITY:: ACL2 Version 2.6 Notes on New Functionality * NOTE-2-6-OTHER:: ACL2 Version 2.6 Notes on Other (Minor) Changes * NOTE-2-6-PROOF-CHECKER:: ACL2 Version 2.6 Notes on Proof-checker Changes * NOTE-2-6-PROOFS:: ACL2 Version 2.6 Notes on Changes in Proof Engine * NOTE-2-6-RULES:: ACL2 Version 2.6 Notes on Changes in Rules and Constants * NOTE-2-6-SYSTEM:: ACL2 Version 2.6 Notes on System-level Changes  File: acl2-doc-emacs.info, Node: NOTE-2-6-GUARDS, Next: NOTE-2-6-NEW-FUNCTIONALITY, Prev: NOTE-2-6, Up: NOTE-2-6 NOTE-2-6-GUARDS ACL2 Version 2.6 Notes on Guard-related Changes When you declare that a function treats certain formals as :stobjs, the guard of the function is automatically extended to include the corresponding stobj-recognizer calls. For example, if a definition includes (declare (xargs :stobjs (ST))) then the guard of the function is changed by the addition of the conjunct (ST-P ST). One impact of this is that if you use the built-in ACL2 state as a formal parameter of a function, (STATE-P STATE) is added to the guard. This may introduce a guard where there was none in previous versions of the system. In older versions, therefore, no attempt would be made to verify-guards, while in the new version, we would attempt guard verification. You may wish to add (declare (xargs :verify-guards nil)) to such definitions. A related change affects users who do not use stobjs or state. In previous versions of the system -- as now -- a type declaration extended the guard you provided explicitly. Thus, if you wrote (declare (type integer n)) then (INTEGERP n) was added to your guard. This is still the case and :stobjs recognizers are similarly added. But in older versions of the system we "added" the conjuncts without checking whether they were already present in the guard you provided. This sometimes produced such guards as (and (integerp n) (integerp n)) where the first was produced by your type declaration and the second was your :guard. We now eliminate redundant conjuncts; this may rearrange the order of the conjuncts. The guard conjectures for functions using stobjs have been simplified somewhat by taking advantage of the syntactic restrictions checked for single-threaded objects. The following functions have been modified so that character and string arguments are restricted to standard characters. (See *note STANDARD-CHAR-P:: and see *note STANDARD-CHAR-LISTP::.) upper-case-p lower-case-p char-upcase char-downcase string-downcase1 string-downcase string-upcase1 string-upcase char-equal string-equal1 string-equal Also, function standard-string-alistp replaces function string-alistp, with concomitant changes in the guard to assoc-string-equal, and in variable *acl2-exports*. Also, lemma standard-string-alistp-forward-to-alistp replaces lemma string-alistp-forward-to-alistp. There is a new lemma standard-char-p-nth, which has also been added to *acl2-exports*. The guard had been inadvertently omitted from the definition of the function substitute (and its subroutine substitute-ac). This omission has been corrected; also, the guard is slightly stronger than the documentation had claimed (and that has been corrected). acl2-sources/doc/EMACS/acl2-doc-emacs.info-110000664002132200015000000111313312222333542017717 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: NOTE-2-6-NEW-FUNCTIONALITY, Next: NOTE-2-6-OTHER, Prev: NOTE-2-6-GUARDS, Up: NOTE-2-6 NOTE-2-6-NEW-FUNCTIONALITY ACL2 Version 2.6 Notes on New Functionality A fundamental change is the provision of the "nu-rewriter" for simplifying expressions composed of NTH, UPDATE-NTH, and UPDATE-NTH-ARRAY applications and LET expressions and other calls of non-recursive functions or LAMBDA expressions involving those symbols. The nu-rewriter applies the obvious rewrite rule for (NTH i (UPDATE-NTH j v s)) and the analogous rule for UPDATE-NTH-ARRAY. See *note NU-REWRITER:: The nu-rewriter can be enabled with set-nu-rewriter-mode. A new flag has been added to the xargs of defun permitting the declaration that the function is non-executable. The usage is (declare (xargs :non-executable t)) and the effect is that the function has no executable counterpart. On the positive side: the function is permitted to use single-threaded object names and functions arbitrarily, as in theorems rather than as in executable definitions. Such functions are not permitted to declare any names :stobjs but accessors, etc., may be used, just as in theorems. A new flag has been added to permit the system to abbreviate output by introducing LET* notation identifying common subterms. The formula being proved is not affected; this flag changes its displayed form only. See set-let*-abstractionp. A "raw mode" has been added, primarily for faster loading of applications. see *note SET-RAW-MODE::. Functions alphorder and lexorder have been put in :logic mode. Lexorder is now a total order ordering of the ACL2 universe, and theorems are included to that effect. Thanks to Pete Manolios for suggesting the idea and providing events to use, and to Rob Sumners for assistance with some modifications. See also the new book books/misc/total-order for an irreflexive total order. The ACL2 user can now make system calls to the host operating system. See *note SYS-CALL:: and see *note SYS-CALL-STATUS::. Thanks to Rob Sumners for working out this idea with Pete Manolios and Robert Krug, who we also thank, and for working out the implementation with us. It is no longer required to use absolute pathnames in include-book forms that have been executed before a certify-book. Any relative pathname strings in such contexts will be expanded into absolute pathnames before they are saved in the portcullis of the certificate of the book being certified. ACL2 can now be built on top of Allegro Common Lisp 6.0, and also on Windows platforms on top of Allegro Common Lisp and GCL. Thanks to Pete Manolios and Vinay K. Siddhavanahalli for their help with Windows. Rob Sumners has designed and provided an initial implementation for two improvements to defstobj (also see *note STOBJ::). First, array fields can now be resized. Resize and length functions are provided for array fields, which can be used to resize stobj array fields dynamically. The recognizers for array fields have been simplified to accommodate this change, so that they only check that each element of the array field has the specified type. Second, performance has been improved for stobjs with a large number of fields, by changing their Common Lisp implementation to store the fields in a simple vector instead of a list. Now stobjs may be bound locally; see *note WITH-LOCAL-STOBJ::. Thanks to Rob Sumners, who encouraged us to implement this capability, was an early user of it, and participated usefully in discussions on its design. New functions fms!, fmt!, and fmt1! are the same as their respective functions without the "!," except that the "!" functions are guaranteed to print forms that can be read back in (at a slight readability cost). We added extended-metafunctions, metafunctions which allow state and context sensitive rewriting to some extent. We thank Robert Krug for pushing for and on this idea. The documentation has been improved. In particular, a new documentation topic provides a gentle introduction to ACL2 arrays -- see *note ARRAYS-EXAMPLE:: -- and additional documentation has been provided for getting started with proof trees in emacs -- see *note PROOF-TREE::. New Makefile targets fasl and o have been added to the books/ directory of the distribution. For example, you might first certify books using an ACL2 built on top of GCL (which creates compiled files with suffix o). Then, when standing in the books/ directory, you might execute the command make fasl ACL2=my-allegro-acl2 which will create compiled (.fasl) files for Allegro Common Lisp, assuming that my-allegro-acl2 starts up an ACL2 built on that Common Lisp. The macro let* now allows variables to be declared ignored. See *note LET*:: and see *note LET::. The user may now control backchaining. This feature was designed and primarily implemented by Robert Krug (though the authors of ACL2 are resposible for any errors); thanks, Robert! See *note BACKCHAIN-LIMIT::. It is now possible to "slow down" the rate at which case splits are generated by the simplifier. See *note SET-CASE-SPLIT-LIMITATIONS::. Accesses to stobjs using nth or update-nth are now displayed using symbolic constants instead of numeric indices. For example, given the event (defstobj foo a b :renaming ((b c))) then the term (nth 0 foo) will be displayed (for example, during proofs) as (nth *a* foo) while (nth 1 foo) will be displayed as (nth *c* foo). The defstobj event now correspondingly introduces a defconst event for each field accessor function, introducing a constant whose name is obtained from the accessor's name by prefixing and suffixin a "*," as in the example above: accessor a generates (defconst *a* 0) and accessor c generates (defconst *c* 1). See *note NTH-ALIASES-TABLE:: for how to extend this feature for alternate names of stobjs. Computed hints have been improved. It is now possible to detect within a computed hint whether the goal clause is stable under simplification; it is also possible for a computed hint to change the list of available hints. See *note COMPUTED-HINTS::. It is now possible to provide "default hints" that are appended to the hints explicitly provided. See *note SET-DEFAULT-HINTS::. Using computed hints (see *note COMPUTED-HINTS::) and default hints (see *note SET-DEFAULT-HINTS::) it is possible to implement a book that supports "priority phased simplification." Using this book you can assign priorities to your rules and cause the theorem prover to simplify each goal maximally under all the rules of one priority before enabling rules of the next priority. See books/misc/priorities.lisp. The macro defabbrev has been improved to allow declare forms and documentation strings and to do more error-checking. Thanks to Rob Sumners for designing this enhancement and providing the first implementation. See *note DEFABBREV::. Further changes were made to support CMU Lisp. Wolfhard Buss helped with these changes. A new table was added that is used when printing proof output, so that nests of right-associated calls of a binary function are replaced by corresponding macro calls, as has been the case for binary-+ and +, binary-append and append, and so on. See *note ADD-BINOP::. Operators logand, logior, logxor, and logeqv are now macros (formerly, they were functions) that call corresponding binary functions (e.g., binary-logand) defined in source file "axioms.lisp". Thanks to Rob Sumners for this enhancement. Proof output will however continue to show calls of logand, logior, logxor, and logeqv. Function (allocate-fixnum-range fixnum-lo fixnum-hi) sets aside more "permanent" fixnums in GCL. ACL2 now runs under CLISP. Thanks to Wolfhard Buss and Sam Steingold for their assistance with the port. Michael "Bogo" Bogomolny has created a search engine, accessible from the ACL2 home page. For that purpose he modified the HTML translator to create one file per topic (a good idea in any case). Thanks, Bogo! An emacs file of potential (but optional) use for ACL2 users may be found in emacs/emacs-acl2.el. In particular, this file supports the use of proof trees (see *note PROOF-TREE::). Some books have been added or modified. In particular, Robert Krug has contributed books/arithmetic-2/, which provides an alternative to the existing collection of books about arithmetic, books/arithmetic/. For a discussion of the distributed books see the link to README.html in the installation instructions.  File: acl2-doc-emacs.info, Node: NOTE-2-6-OTHER, Next: NOTE-2-6-PROOF-CHECKER, Prev: NOTE-2-6-NEW-FUNCTIONALITY, Up: NOTE-2-6 NOTE-2-6-OTHER ACL2 Version 2.6 Notes on Other (Minor) Changes Warning strings are now case-insensitive. See *note SET-INHIBIT-WARNINGS::. ACL2 causes a warning when an in-theory hint or event causes a 0-ary function's definition to be disabled but its :executable-counterpart to be enabled. A minor modification has been made to defstobj that can have a positive impact on performance in Allegro Common Lisp. (For Lisp hackers: the stobj name was formerly declared special, and that was disabling Allegro's tail-merging routing for compilation of some recursive functions using stobjs.) The downside is that stobj names can no longer be evaluated in raw Lisp. However, raw Lisp is not the right place to be evaluating ACL2 forms anyhow; see *note SET-RAW-MODE::. We thank Rob Sumners for bringing this issue to our attention. Before Version 2.6, there has been the following problem with defstub and encapsulate in the case that the current package is not the ACL2 package. If a signature was specified using the symbol =>, then that symbol had have been imported into the current package from the ACL2 package when the current package was defined. There are no longer any package restrictions on the use of =>. Thanks to John Cowles for bringing this problem to our attention. Bugs in defun-sk have been fixed. Defun-sk forms introducing functions of no arguments were failing to be admitted, for example: (defun-sk always-p1 () (forall (x) (p1 x))). Thanks to John Cowles for bringing this problem to our attention. Also, defun-sk failed on an example in the documentation (see *note TUTORIAL4-DEFUN-SK-EXAMPLE::), as pointed out by Matyas Sustik; this bug has been fixed as well. The trace mechanism has been fixed to handle stobjs, and to avoid the printing of so-called _enabled structures_. The brr command :type-alist now produces more readable output. An include-book of an uncertified book no longer loads an associated compiled file. We added a few checks to make sure that the underlying lisp is suitable, for example checking that the reader is case-insensitive and reads in symbols with upper-case names where appropriate. We now warn when forcing (see *note FORCE::) or immediate force mode (see *note IMMEDIATE-FORCE-MODEP::) change state between enabled and disabled. Also see *note ENABLE-IMMEDIATE-FORCE-MODEP:: and see *note DISABLE-IMMEDIATE-FORCE-MODEP:: for information about these new macros, which may be used to control immediate force mode. We have eliminated the use of a low-level raw Lisp constant, *most-recent-multiplicity*. Our test suite saw a speed-up of approximately 2% as a result for an ACL2 image built on GCL (but no significant speed-up for an ACL2 image built on Allegro Common Lisp). We thank Rob Sumners for suggesting this improvement. Fixnum declarations are now realized as (signed-byte 29) instead of (signed-byte 27). We check that the underlying Common Lisp recognizes objects of type (signed-byte 29) as fixnums, with the exception of CLISP, which is said to have an efficient bignum implementation. A new documentation topic functional-instantiation-example illustrates functional instantiation. A bug has been fixed in the monitoring of runes (see *note MONITOR::). Thanks to Dave Greve for sending an example that clearly showed the problem. A warning is now issued when it is detected that a :type-prescription rule may not be as strong as it appears because it is not sufficient to prove itself by type reasoning. An error is caused for rules of class :meta when the function symbol IF is among the :trigger-fns. (IF was ignored anyhow; the point of this change is to avoid misleading the user.) A minor bug has been fixed in :pr, evident for example if this command was applied to IF. A minor hole in :set-bogus-mutual-recursion-ok did not permit the acceptance of mutual-recursion forms that include constant function definitions. This has been fixed. Thanks to Eric Smith for coming up with a simple example illustrating the problem. The temporary files "TMP.lisp" and "TMP1.lisp" written out by :comp are now written to the connected book directory (see *note CBD::). Previously, the Allegro compiler was not eliminating tail recursion for executable counterparts of functions, because of the way one of its flags had been set. As a result, calls of functions whose guards had not been verified could run out of stack space when this was not necessary. This situation has been fixed. Executable counterparts could have slow array accesses. This has been fixed (specifically, constants are no longer replaced with their values in the definitions of executable counterparts). Various improvements have been made to the documentation. Thanks in particular to Eric Smith for pointing out a numbers of places where fixes were in order. File "mcl-acl2-startup.lisp" has been updated, thanks to feedback from Philippe Georgelin. Inefficiencies in GCL fixnum computations were remedied for macros +f and *f. Thanks to Rob Sumners for pointing out this issue.  File: acl2-doc-emacs.info, Node: NOTE-2-6-PROOF-CHECKER, Next: NOTE-2-6-PROOFS, Prev: NOTE-2-6-OTHER, Up: NOTE-2-6 NOTE-2-6-PROOF-CHECKER ACL2 Version 2.6 Notes on Proof-checker Changes The proof-checker command =, when used with no arguments, now reports which hypothesis is being used. The output from proof-checker command type-alist has been improved. A slight change has been made to the proof-checker for commands promote, casesplit, equiv, and =, so that terms of the form (if x nil y) are recognized as conjunctions, (and (not x) y). Thanks to Pete Manolios for suggesting that we consider such a change. There is a new proof-checker command print-all-concs that prints all the conclusions of the unproved goals. A new proof-checker command, runes, has been added. It reports the runes that have participated in the interactive proof up to the current point.  File: acl2-doc-emacs.info, Node: NOTE-2-6-PROOFS, Next: NOTE-2-6-RULES, Prev: NOTE-2-6-PROOF-CHECKER, Up: NOTE-2-6 NOTE-2-6-PROOFS ACL2 Version 2.6 Notes on Changes in Proof Engine Certain optimizations are performed when converting terms to clausal form. For example, (< 0 1) is known to be t, (HARD-ERROR ctx str alist) is known to be nil, and (INTEGERP n) is known to imply (RATIONALP n). In earlier versions of ACL2, the conversion of a term to clausal form expanded LAMBDA applications. That may no longer occur. Some proofs may slow down (or fail) because your LAMBDA-expressions are not expanded away when you "expected" them to be. Robert Krug found a soundness bug in our linear arithmetic package. The bug was caused by the derivation of an equation from two inequalities without taking adequate precautions to ensure that both sides of the inequalities were numeric. Robert also kindly provided a fix which we adopted. Thanks Robert! We fixed a bug that could prevent the application of a metatheorem. A bug has been fixed that had caused bogus forcing rounds (see *note FORCING-ROUND::). The bug could occur when the hypothesis of a rule was forced (see *note FORCE::) before the prover decided to start over and prove the original goal by induction. Thanks to Rob Sumners for drawing our attention to this problem. Some low-level fixes have been made that prevent certain infinite loops, based on reports by users. We thank Yunja Choi, Matt Wilding, and Pete Manolios for reporting such problems. An obscure potential soundness hole has been fixed by redoing the way evaluation takes place in the ACL2 loop and during theorem proving. We expect that users will see no difference based on this change. (Those interested in the details can see the long comment "Essay on Evaluation in ACL2" in source file interface-raw.lisp.) A small change was made in computation for a heuristic that controls backchaining. This will speed up proofs dramatically in a very few cases but should have a very small impact in general. The simplifier has been modified to avoid eliminating hypotheses of goals that can be established by contextual (specifically, type-set) reasoning alone. We believe that this change will generally strengthen ACL2's reasoning engine, although on rare occasions a lemma that formerly was provable may require user assistance. Thanks to Robert Krug for suggesting this change and providing its implementation. Case splits are now limited, by default. This may allow some proof attempts to provide output where previously the prover would appear to "go out to lunch." For a more complete discussion, including instructions for how users can control case splitting, see *note SET-CASE-SPLIT-LIMITATIONS::. A bug has been fixed in the handling of :type-prescription rules by the bdd package. Thanks to Rob Sumners for discovering this bug and supplying a helpful example. ACL2 may now use the built-in induction scheme for a function symbol even if that function symbol is disabled. Formerly, if a function symbol was disabled then its induction scheme was only considered if an explicit induction hint was supplied, other than :induct t. We eliminated the rule-class linear-alias. This rule class was seldom used and complicated the linear arithmetic decision procedure in ways that made it difficult to extend to handle some non-linear special cases. The only use of the rule-class that we know of was in our own nqthm books, which were an attempt to provide an embedding of the Nqthm logic and theorem prover into ACL2. But that facility was also practically never used, as far as we know. So both linear-alias rules and the nqthm books have been eliminated. In earlier versions of ACL2, when the IF-form of (AND p q) was assumed true - as when rewriting the alpha expression in (IF (AND p q) alpha beta) - the assumption mechanism did not deduce that p and q are true, only that their conjunction, in its IF-form, is true. This has long been known as a deficiency in both ACL2 and the earlier Nqthm but it was tedious to do better when one considered the full range of IF-forms one might encounter in the test of another IF. Rather than code all the cases, we just waited until clausification got rid of them. Robert Krug developed a pretty nice treatment of the general case and we added it in this version. This also involved a surprising number of changes elsewhere in the system because the improved handling of assumptions caused the theorem prover often to "erase" hypotheses provided by :use hints because it could simplify them to t. Thank you Robert! In response to a suggestion from Robert Krug, we added mfc-ap so that extended metafunctions can take advantage of linear arithmetic. See *note EXTENDED-METAFUNCTIONS::. There is less delay in printing goals. In previous versions, a goal was not printed until its subgoals were created (or the goal was proved). Now, the goal is printed essentially as soon as it is created. A small technical change has been made in the function term-order, to give priority on the function symbol count over the weighting of constants. So for example, while previously the term (f) preceded the constant 2, that is no longer the case. If this change is noticed at all, it will probably be noticed in how so-called _permutative_ rewrite rules are applied; see *note LOOP-STOPPER::. Thanks to Robert Krug for suggesting this improvement and providing part of the implemtation.  File: acl2-doc-emacs.info, Node: NOTE-2-6-RULES, Next: NOTE-2-6-SYSTEM, Prev: NOTE-2-6-PROOFS, Up: NOTE-2-6 NOTE-2-6-RULES ACL2 Version 2.6 Notes on Changes in Rules and Constants The following symbols have been added to the list constant *common-lisp-specials-and-constants*: REPLACE, FILL, CHARACTER, =, BREAK, and PRIN1. This was done in support of ports to Allegro 6.0 and Windows platforms (see *note NOTE-2-6-NEW-FUNCTIONALITY::). The list of symbols in *acl2-exports* has been modified, for example to include show-accumulated-persistence and the legal arguments to set-inhibit-output-lst. Functions zp and zip are now handled slightly differently. They are are now disabled, but each comes with a :rewrite rule that allows their expansion on non-variable terms, and also with a :compound-recognizer rule that avoids the need for opening up these functions when applied to variables. The resulting behavior should be very similar to the behavior of previous versions, except that case splits will be avoided when these functions are applied to variables. Function standard-string-alistp replaces function string-alistp. For further discussion, see *note NOTE-2-6-GUARDS::. Rules of class :rewrite whose conclusion is a term of the form (equal lhs rhs) have always been stored in the expected way: lhs rewrites to rhs. This way of storing :rewrite rules has been extended to allow =, eq, or eql in place of equal. Rewrite rule nth-update-nth, in source file axioms.lisp, has been strengthened. A new rewrite rule equal-constant-+ has been added to the book arithmetic/equalities. This should generally be a beneficial change, but existing proofs involving the arithmetic books could conceivably be affected. Function symbol-package-name and constant *main-lisp-package-name* have undergone small changes. This change should rarely be noticed by users and is discussed elsewhere; see *note NOTE-2-6-SYSTEM::. We mention here that proofs involving stobjs may need to be modified because of changes in auxiliary functions generated by defstobj. (These changes were made in support of a new resizing capability, mentioned elsewhere in these release notes; see *note NOTE-2-6-NEW-FUNCTIONALITY::. In the distributed book directory books/arithmetic/, the book rationals-with-axioms-proved.lisp has been renamed rationals.lisp. (ACL2(r) only) Rewrite rules realp-+, realp-*, realp-unary-, and realp-unary-/ have been added in analogy to existing rules rationalp-+, rationalp-*, rationalp-unary-, and rationalp-unary-/. Thanks to Jun Sawada for suggesting this change. The definition of aref1 has been modified slightly. Previously, if *my-a* were an array then (aref1 'some-name *my-a* :header) would evaluate to the cdr of the header of *my-a* rather than to its default. See *note ARRAYS::. Changes have been made in the ihs books, based on suggestions from Jun Sawada, that support its use with ACL2(r) (see *note REAL::). The primary change is to replace calls of rationalp with calls of real/rationalp, which should have no effect on users of standard ACL2.  File: acl2-doc-emacs.info, Node: NOTE-2-6-SYSTEM, Prev: NOTE-2-6-RULES, Up: NOTE-2-6 NOTE-2-6-SYSTEM ACL2 Version 2.6 Notes on System-level Changes We modified the tracking of skip-proofs events and the use of state global ld-skip-proofsp in order to avoid some soundness issues. For example, skip-proofs events buried in locally-included books are now tracked. The "Essay on Skip-proofs" in source file axioms.lisp gives several examples of dicey behavior that is no longer supported. We fixed a problem with some of the makefiles, so that recursive invocations of `make' now use the version of `make' specified on the command line. Files were fixed to help non-Unix/Linux users with book certification. Thanks to John Cowles for finding some problems and suggesting fixes to books/certify-numbers.lisp, books/arithmetic/certify.lsp, and books/cowles/certify.lsp. We thank Scott Burson for noticing and fixing some other such problems. Moreover, a bdd test was being ignored entirely in Version 2.5; this problem has been fixed as well. A minor change in system function save-acl2-in-allegro will allow this function to continue to work in Allegro CL versions starting (someday) with 10.0. Thanks to Art Flatau for suggesting such a fix. The books/case-studies/ directory has been removed. These books are in support of the first (1998) ACL2 workshop, and are accessible via the ACL2 home page on the Web, `http://www.cs.utexas.edu/users/moore/acl2/'. Also, the books/cli-misc directory has been renamed books/misc, and the books/nqthm directory has been removed. The notion of ACL2 version has been slightly modified to catch unsoundness due to implementation dependencies. See *note VERSION::. Another change to eliminate such unsoundness is that built-in symbols now have a symbol-package-name of "COMMON-LISP"; formerly, this string was "LISP" for ACL2 images built on GCL. See *note SYMBOL-PACKAGE-NAME::. At a low level, the (undocumented) constant *main-lisp-package-name* is now "COMMON-LISP"; before, it was "LISP" for GCL.  File: acl2-doc-emacs.info, Node: NOTE-2-6(R), Next: NOTE-2-7, Prev: NOTE-2-6, Up: RELEASE-NOTES NOTE-2-6(R) ACL2 Version 2.6(r) (November, 2001) Notes Important changes to non-standard version: None since Version 2.5. Please see *note NOTE-2-6:: for changes to Version 2.6 of ACL2. We hope to write more documentation for ACL2(r) in the future.  File: acl2-doc-emacs.info, Node: NOTE-2-7, Next: NOTE-2-7(R), Prev: NOTE-2-6(R), Up: RELEASE-NOTES NOTE-2-7 ACL2 Version 2.7 (November, 2002) Notes The Version_2.7 notes are divided into the subtopics below. Here we give only a brief summary of a few of the changes that seem most likely to impact existing proofs. Not included in this brief summary, but included in the subtopics, are descriptions of improvements (including bug fixes and new functionality) that should not get in the way of existing proof efforts. In particular, please see *note NOTE-2-7-NEW-FUNCTIONALITY:: for discussion of a number of new features that you may find useful. Acknowledgements and elaboration, as well as other changes, can be found in the subtopics listed below. o Bug fixes (see *note NOTE-2-7-BUG-FIXES::): + Three soundness bugs were fixed. These bugs were probably rarely hit, so users may well not notice these changes. + Certify-book now requires :skip-proofs-ok t (respectively, :defaxioms-okp t) if there are skip-proofs (respectively, defaxiom) events in the book or any included sub-books. + When :by hints refer to a definition, they now use the original body of that definition rather than the simplfied ("normalized") body. + When ld is applied to a stringp file name, it now temporarily sets the connected book directory (see *note CBD::) to the directory of that file while evaluating forms in that file. o New functionality (see *note NOTE-2-7-NEW-FUNCTIONALITY::): + ACL2 now works harder to apply :rewrite and :linear rules with free variables in the hypotheses. See *note NOTE-2-7-NEW-FUNCTIONALITY::, in particular its first two paragraphs, for details. Forward-chaining also does more with free variables. o Changes in proof engine (see *note NOTE-2-7-PROOFS::): + Some prover heuristics have changed slightly. Among other consequences, this can cause subgoal hints to change. For example, suppose that the Version_2.6 proof of a particular theorem generated "Subgoal 2" and "Subgoal 1" while Version_2.7 only generates the second of these. Then a subgoal hint attached to "Subgoal 1" in Version_2.6 would have to be attached to "Goal'" in Version_2.7. (See *note GOAL-SPEC::.) The full topic has details (see *note NOTE-2-7-PROOFS::). o Changes in rules and definitions (see *note NOTE-2-7-RULES::): + The package name of a generated variable has changed for defcong. o Guard-related changes (see *note NOTE-2-7-GUARDS::): + Guard verification formerly succeeded in a few cases where it should have failed. + Guards generated from type declarations now use functions signed-byte-p and unsigned-byte-p, now defined in source file axioms.lisp and formerly defined rather similarly under books/ihs/. o Proof-checker changes (see *note NOTE-2-7-PROOF-CHECKER::): + See the above doc topic. o System-level changes (see *note NOTE-2-7-SYSTEM::): + See the above doc topic. o Other changes (see *note NOTE-2-7-OTHER::): + A new table, invisible-fns-table, takes the place of the handling of invisible functions in the acl2-defaults-table, + The theory-invariant event has been modified so that the default action is an error rather than a warning. + Proof output that reports destructor elimination no longer uses the word "generalizing". Again, please proceed to the subtopics for more thorough release notes. * Menu: * NOTE-2-7-BUG-FIXES:: ACL2 Version 2.7 Notes on Bug Fixes * NOTE-2-7-GUARDS:: ACL2 Version 2.7 Notes on Guard-related Changes * NOTE-2-7-NEW-FUNCTIONALITY:: ACL2 Version 2.7 Notes on New Functionality * NOTE-2-7-OTHER:: ACL2 Version 2.7 Notes on Miscellaneous Changes * NOTE-2-7-PROOF-CHECKER:: ACL2 Version 2.7 Notes on Proof-checker Changes * NOTE-2-7-PROOFS:: ACL2 Version 2.7 Notes on Changes in Proof Engine * NOTE-2-7-RULES:: ACL2 Version 2.7 Notes on Changes in Rules and Constants * NOTE-2-7-SYSTEM:: ACL2 Version 2.7 Notes on System-level Changes  File: acl2-doc-emacs.info, Node: NOTE-2-7-BUG-FIXES, Next: NOTE-2-7-GUARDS, Prev: NOTE-2-7, Up: NOTE-2-7 NOTE-2-7-BUG-FIXES ACL2 Version 2.7 Notes on Bug Fixes Francisco J. Martin-Mateos emailed us a soundness bug (!) in our handling of functional instantiation (for example see *note FUNCTIONAL-INSTANTIATION-EXAMPLE::). We are grateful for that email, which clearly illustrated the problem. It is included just below the definition of push-clause in ACL2 source file prove.lisp, where we have fixed the bug. This bug was fixed in a re-release of Version 2.6 in February, 2002. Rob Sumners emailed us a soundness bug (!) in function commutative-p1, which is used by the ACL2 bdd package. We are grateful for his help; his email gave a proof of nil and also pointed to the problem function. This bug was fixed in a re-release of Version 2.6 in February, 2002. We discovered and fixed a soundness bug illustrated by the book below, which was certifiable in Version 2.6 and ends in a proof of nil. The event (verify-guards foo) should have been rejected, because foo calls a function whose guards have not been verified, namely, bar. However, ACL2 did not notice the call of function bar in the body of foo because it was looking in the simplified (normalized) body of foo rather than in the original body of foo. During processing of the book below, the logical definition of zp is used before (verify-guards foo), and (zp -3) reduces to t in the logic. After (verify-guards foo), ACL2 simplifies (foo -3) by going into raw Lisp, where (zp -3) is evaluated and reduces to nil. (in-package "ACL2") (defun bar (x) (zp x)) (defthm zp-false-on-negatives (implies (< x 0) (bar x)) :rule-classes :type-prescription) (defun foo (x) (declare (xargs :guard (rationalp x) :verify-guards nil)) (if (< x 0) (if (bar x) 0 1) ; simplified body reduces this line to 0 17)) (defthm foo-of-minus-3-is-0 (equal (foo -3) 0) :rule-classes nil) (verify-guards foo) (defthm foo-of-minus-3-is-1 (equal (foo -3) 1) :rule-classes nil) (defthm bug nil :rule-classes nil :hints (("Goal" :use (foo-of-minus-3-is-0 foo-of-minus-3-is-1)))) The above bug exploited the fact that zp has a different definition in raw Lisp than in the logic for arguments that violate its guard). The following example caused a hard error in raw Lisp, though not a soundness error. (in-package "ACL2") (defun bar (x) (cons (car x) (car x))) (defun foo (x) (declare (xargs :guard t :verify-guards nil)) (if (bar x) x nil)) (verify-guards foo) (defthm bug (equal (foo 3) t) :rule-classes nil) We have made a minor change to the notion of the _formula_ of a function symbol, related to the change above, which however is unlikely to be noticeable. In order to make it harder to hit problems like the guard problem above, we have slighly modified the raw Lisp definition of zp. A break-rewrite command, :ancestors, was broken, but has been fixed. Thanks to Eric Smith for bringing the problem to our attention, and to Robert Krug for supplying the final part of the fix. Some proof-checker commands caused errors when all goals have already been proved. This has been fixed. Thanks to Matt Wilding for reporting this bug. Fixed a bug in :comp. When compiling uncompiled functions with very large definitions, ACL2 was inserted a backslash (\) character into generated files. Fixed the :type-alist :brr command (see *note BRR-COMMANDS::), whose output was difficult to read when typed after an :eval.. Fixed some clumsy handling of errors when including an uncertified book, for example, with the error message when including an uncertified book with a bad deftheory event. Thanks to Eric Smith for pointing out this problem. Two modifications to certify-book now cause it to reflect natural expectations with respect to soundness. First, it now has default values of nil instead of t for keyword arguments :skip-proofs-okp and :defaxioms-okp. Thanks to Robert Krug for suggesting this change and the ACL2 seminar at the University of Texas for discussing it. Second, when :skip-proofs-okp (respectively, :defaxioms-okp) is nil, either explicitly or by default, then skip-proofs commands (respectively, defaxiom events) are disallowed inside any included books, regardless of the keyword parameters passed to include-book. This had not been the case for previous versions of ACL2, regardless of the values of :skip-proofs-okp or :defaxioms-okp passed to include-book. Improved warnings and errors for certify-book and include-book to mention the portcullis as a possible source of skip-proofs and defaxioms. ACL2 formerly caused an error when hints in a :corollary were not well-formed. This situation could arise as follows when certifying a book. A lemma FOO is proved LOCALly to the book (or, is present in a sub-book that is included locally). The :corollary of a subsequent theorem, BAR, disables that rule in a hint. When BAR is proved, this is not a problem. But certify-book makes a second pass after processing the events in a book: it essentially does an include-book. During the include-book pass, FOO is not known (because it was local), and therefore ACL2 fails to process the disable of FOO in an in-theory hint. The fix is that during include-book, hints are ignored in corollaries just as they have been for the main theorem (or definition). It was possible for guard verification to succeed where it should have failed. We have fixed the bug (which was in source function (ironically named!) fcons-term-smart). Thanks to Robert Krug for sending us an example of bungled guard verification. It turns out that this bug was also present in Version_2.6. The proof-checker command = has been improved. Formerly, it could fail to apply when certain implies terms were in the context. Thanks to Pete Manolios for bringing this problem to our attention. The command add-binop failed to work. This has been fixed. Thanks to Rob Sumners for pointing out this problem. Also see *note NOTE-2-7-OTHER:: for a discussion of how this and another table are no longer part of the acl2-defaults-table. Book certification could cause a segmentation fault in cases where the certification world (see *note CERTIFY-BOOK::) has a very large number of events. This has been fixed. We now allow empty :use hints and empty hints, as requested by Eric Smith. Examples: ("Goal" :use ()) ("Goal") A large mutual-recursion nest could cause a stack overflow when executing either :pr FN, :pr! FN, or :monitor (:definition FN) t, where FN is in that large mutual recursion nest. This has been fixed (implementation detail: function actual-props has been made tail-recursive). NOTE: If you just want the definition of FN, :pf FN can be much faster than :pr FN if FN is in a large mutual-recursion. Hard Lisp errors could occur when including uncertified books. This has been fixed; ACL2 now does syntax-checking formerly omitted when including uncertified books. Previously, the evaluation of defstobj and mutual-recursion forms could cause "undefined" warnings when the form was compiled. This has been fixed. Thanks to Eric Smith for bring a mutual-recursion example to our attention. A bug has been fixed in the syntactic check for valid :loop-stopper values. Formerly, valid :loop-stopper values were erroneously restricted to lists of length at most 2 (a minor problem, since these lists typically have length 1), and the function symbol(s) need not have been defined in the current ACL2 world. Thanks to Eric Smith for sending an example to demonstrate the latter problem. Functions definitions that are :non-executable (see *note XARGS::) had never been recognized as redundant, but this has been fixed. Thanks to Vernon Austel for pointing out this problem. Compilation using :comp now compiles user-defined :program mode functions. Formerly only :logic mode functions could be compiled using :comp. Handling of :by hints has been improved in essentially three ways. The primary change is that now, when the current goal exactly matches the supplied lemma instance, the subsumption test will always succeeds (see *note HINTS::, in particular the discussion of :by). Second, certain proof failures involving :by hints were failing silently, with duplicate messages "As indicated by the hint, this goal is subsumed by...." This could happen when the original goal was among the goals generated by applying the hint. This problem has been fixed by no longer considering this proof step to be specious (see *note SPECIOUS-SIMPLIFICATION::). Third and finally, when the lemma-instance refers to a definition, the original body of that definition is used rather than the simplfied ("normalized") body. In addition to the obove, we now recognize more cases of specious simplification (see *note SPECIOUS-SIMPLIFICATION::). Thanks to Eric Smith for bringing this issue to our attention. Fixed building of ACL2 under CLISP so that (1) the appropriate ACL2 startup message is printed out when ACL2 starts up, and (2) the lisp process supplied to make, e.g., LISP=/usr/bin/clisp, is the one written out to the saved ACL2 file. Thanks to Dave Greve and Noah Friedman for suggesting (2). Also, ACL2 now works with CLISP 2.30. We have accommodated a change in CLISP's handling of streams and its package-locking mechanism, as well as certain non-standard characters that formerly could cause CLISP 2.30 to break, even when those characters are in comments. Eliminated compiler warnings for CMU Lisp. Fixed an incorrect error supplied when book certification proceeded so quickly that the file write dates of the book (.lisp file) and the corresponding compiled file are equal. Now that error only occurs if the compiled file has a strictly earlier write date, which probably should never happen. Fixed an infinite loop when executing make clean-books (and hence `make' with targets that call clean-books, namely, certify-books-fresh, regression-fresh, and regression-nonstd-fresh), which could occur when any subdirectories of books/ are missing -- even workshops/, which is intended to be optional. Thanks to Pete Manolios for pointing out this bug. The include-book command now works properly even when filenames, or their directories or parent directories (etc.) are links. Thanks to Matt Wilding for pointing out this problem. The commands :puff :puff* have been fixed. Formerly, there was a bug when :puff or :puff* caused the execution of an include-book for an absolute pathname, P, that was other than the current connected book directory (see *note CBD::). When including P, any subsidiary include-book with a relative pathname would be erroneously considered relative to the current cbd rather than relative to the directory of P. Thanks to Pete Manolios and Matt Wilding for pointing out this problem. It had been possible in a "large" ACL2 image to call verify-termination successfully on built-in function sys-call, with undesirable results. This hole has been plugged. Thanks to Rob Sumners for pointing out this problem. The new function gc$ must also stay in :program mode. ACL2 no longer warns when certifying a book based on local functions whose guards have not yet been verified. Thanks to Pete Manolios for pointing out this issue. An occasional "slow array warning" had been possible during proofs. The following sequence shows how to evoke that warning in previous versions. (in-theory (disable binary-append)) (in-theory (enable binary-append)) (in-theory (disable binary-append)) (ubt 2) (thm (equal (car (cons x y)) x)) (See *note NOTE-2-7-OTHER:: for a discussion of a change to compress1 in support of this fix; however, users should not need to read that discussion.) The raw Lisp code for defchoose had a small bug, which was only evidenced in CLISP implementations as far as we know. It has been fixed. When ld is applied to a stringp file name, it now temporarily sets the connected book directory (see *note CBD::) to the directory of that file while evaluating forms in that file. To see the effect of this change, imagine a subdirectory "sub" of the current directory, and imagine executing (ld "sub/foo.lisp"), where file foo.lisp contains the form (include-book "bar"). Presumably the intention was to consider the file bar.lisp in the same directory, sub/, as foo.lisp. Ld now honors that intention, but in previous versions "bar.lisp" would have been a reference to a file in the current directory, not in sub/. For users of run-acl2 [perhaps there are none!]: A fix has been provided by a Debian user via Camm Maguire so that acl2-mode anyone using that?] will work in Xemacs, which apparently uses variable lisp-mode-shared-map rather than shared-lisp-mode-map. ACL2 has, for a long time (always?), had a mechanism for avoiding re-proving constraints generated by :functional-instance lemma-instances in :use and :by hints. But this mechanism had not applied to defined (as opposed to constrained) functions. This has been fixed. Thanks to Francisco J. Martin-Mateos (ChesKo) for pointing out this problem by sending a clear example.  File: acl2-doc-emacs.info, Node: NOTE-2-7-GUARDS, Next: NOTE-2-7-NEW-FUNCTIONALITY, Prev: NOTE-2-7-BUG-FIXES, Up: NOTE-2-7 NOTE-2-7-GUARDS ACL2 Version 2.7 Notes on Guard-related Changes It was possible for guard verification to succeed where it should have failed. See the discussion under note-2-7-bug-fixes. There have been changes in the guards generated from type declarations for the following cases. Thanks to Dave Greve and Matt Wilding for suggesting such changes. (type (signed-byte n) val) (type (unsigned-byte n) val) (type (integer m n) val) The following examples illustrate the changes. (type (signed-byte 4) x) ==> [old] (AND (INTEGERP X) (<= -8 X) (<= X 7)) ==> [new] (SIGNED-BYTE-P 4 X) (type (unsigned-byte 4) x) ==> [old] (AND (INTEGERP X) (<= 0 X) (<= X 15)) ==> [new] (UNSIGNED-BYTE-P 4 X)  File: acl2-doc-emacs.info, Node: NOTE-2-7-NEW-FUNCTIONALITY, Next: NOTE-2-7-OTHER, Prev: NOTE-2-7-GUARDS, Up: NOTE-2-7 NOTE-2-7-NEW-FUNCTIONALITY ACL2 Version 2.7 Notes on New Functionality ACL2 now has a more powerful technique for relieving a :rewrite or :linear rule's hypothesis that contains free variables. A new documentation section has been written describing the handling free variables in rules; see *note FREE-VARIABLES::. In brief, the primary change is that when a free-variable match for the current hypothesis fails to allow subsequent hypotheses to be relieved, then additional matches may be attempted until they have all been tried. Also see *note RULE-CLASSES:: (discussion of :match-free). Also see *note SET-MATCH-FREE-ERROR::, see *note SET-MATCH-FREE-DEFAULT::, and see *note ADD-MATCH-FREE-OVERRIDE:: for interfaces provided to the user for controlling the way ACL2 deals with free variables in hypotheses. We thank Rob Sumners for several helpful discussions about the designs of those interfaces, as well as Eric Smith and Robert Krug for helpful related discussions. Robert Krug also found a performance bug in a preliminary version, for which we are grateful. WARNING: Book certification attempts may take much longer now that, by default, ACL2 looks for more free variable matches (see paragraph just above). You can get the old behavior by inserting the form (set-match-free-default :once) just after the initial in-package form. However, rules from included books that have free variables can still slow down certification. This can be fixed by inserting (add-match-free-override :once t) before the first event in the file that generates a proof. Forward-chaining has been made more powerful in the presence of free variables (see *note FREE-VARIABLES::), thanks to a contribution by Erik Reeber. Both before and now, when an attempt is made to relieve (prove) a hypothesis of a :forward-chaining rule in the case that at least one variable in that hypothesis is not yet bound, ACL2 looks in the current context for an instance of that hypothesis. If it finds one, then it binds the unbound variables and continues to the next hyopothesis. What is new is that ACL2 can now looks for multiple instances of that hypothesis. Consider the following example; an explanation is below. (encapsulate (((op * *) => *)) (local (defun op (x y) (< x y))) (defthm transitivity-of-op (implies (and (op x y) (op y z)) (op x z)) :rule-classes :forward-chaining)) ; fails in Version_2.6; succeeds in in Version_2.7 (thm (implies (and (op a b) (op b c) (op b e)) (op a c))) Before Version_2.7, the proof of the thm above fails. When the :forward-chaining rule transitivity-of-op binds x to a and y to b, it then looks for an instance of (op y z) in the current context, with y bound to b but z unbound. It happens to find (op b e) before (op b c), and it then adds (op a e) to the context. But starting with Version_2.7, it continues to look for additional instances and finds (op b c) in the context as well, chaining forward to (op a c) and thus proving the theorem. A new macro, bind-free, provides a simple way to get much or most of the power of metafunctions. Thanks to Eric Smith for coming up with the idea and to Robert Krug for providing an implementation (which we modified only very slightly) and documentation. See *note BIND-FREE:: and see *note BIND-FREE-EXAMPLES::. With the addition of bind-free (mentioned above), syntaxp has become a macro, although that change should be transparent to the user. More importantly, the argument of syntaxp may now refer to variables mfc and state, giving syntaxp some of the power of extended metafunctions; see *note SYNTAXP:: and see *note EXTENDED-METAFUNCTIONS::. Thanks to Robert Krug for implementing that extension. Also, the argument of syntaxp may now include calls of :program mode functions. See *note SYNTAXP:: and see *note SYNTAXP-EXAMPLES:: (thanks to Robert Krug for updating the former and creating the latter documentation). The linear-arithmetic decision procedure (see *note LINEAR-ARITHMETIC::) has now been extended so that ACL2 can reason about non-linear arithmetic as well (see *note NON-LINEAR-ARITHMETIC:: for how to turn on this feature). We thank Robert Krug for the initial implementation of this, and Eric Smith for finding a couple of bugs in it. Some trace utilities have been made available in the ACL2 loop. o Function trace$ (and also untrace$) calls the corresponding underlying Lisp routine trace (and untrace), which however continues (as it has for some time) to be enhanced for GCL and Allegro CL. o Macro open-trace-file causes trace output to go to a specified file. Macro close-trace-file causes trace output to go to the screen (which is the default). o Macro with-error-trace (or, wet for short) causes a backtrace to be written out for many failures, including guard violations. See *note TRACE::, see *note TRACE$::, and see :DOC wet [** NOTE: eliminated after Version 3.3]. A new theory, minimal-theory has been provided (see *note THEORIES::). It can be particularly useful for speeding up proofs involving :use hints. New events defund and defthmd behave exactly like defun and defthm, respectively, except that these new events disable the new name. The new macro with-output can be used to suppress output that would normally result from evaluation of a form. The form (pstack) can give the user an idea of what the prover has been up to during a proof, or after a user-aborted proof. Moreover, by evaluating (verbose-pstack t) (see *note VERBOSE-PSTACK::) one can get trace-like information about prover functions, including time summaries, printed to the screen during a proof. Thanks to Bill Legato and Robert Krug for initiating this work and to Robert for providing some initial implementation. The new command :comp-gcl is identical in functionality, except that it always leaves .c and .h files when compiling in GCL. Thanks to Rob Sumners and Vernon Austel for suggesting such a capability. The macro e/d provides a convenient way to enable some rules and disable others. It was formerly in a book supplied with the distribution, books/ihs/ihs-init.lisp, written by Bishop Brock (who we thank for providing this useful macro). New distributed books include those in books/ordinals/, books/rtl/rel3/, and books/misc/simplify-defuns.lisp (which is documented in books/misc/simplify-defuns.txt). The :expand hint now accepts a special value, :LAMBDAS, that tells the ACL2 rewriter to expand all lambda applications (let expressions). See *note HINTS::. A new function zpf has been added as fast test against 0 for nonnegative fixnums. A new macro gc$ allows the user to call the garbage collector of the underlying Common Lisp. Thanks to Rob Sumners for suggesting this feature. It is now possible to monitor simple (abbreviation) rules. However, as a warning explains, they are still not considered monitored during preprocessing; see *note MONITOR::. Thanks to Robert Krug for providing this improvement. The second argument of certify-book, if supplied, formerly had to be either t or a non-negative integer. Now it can be the symbol ?, in the ACL2 package, indicating that the usual check should be suppressed on the number of commands that have been executed to create the world in which certify-book was called.  File: acl2-doc-emacs.info, Node: NOTE-2-7-OTHER, Next: NOTE-2-7-PROOF-CHECKER, Prev: NOTE-2-7-NEW-FUNCTIONALITY, Up: NOTE-2-7 NOTE-2-7-OTHER ACL2 Version 2.7 Notes on Miscellaneous Changes Made several minor documentation improvements. We are grateful to Eric Smith for suggesting (most of) these. Improved (show-bdd) (see *note BDD::) to give more useful feedback when there are "leaf" terms not known to be Boolean. Sped up processing of large mutual-recursion nests. In one large example the speedup was roughly two orders of magnitude. Modified event printing so that if both 'prove and 'event are inhibited, then events are no longer printed on behalf of certify-book, encapsulate, or defstobj. Thanks to Eric Smith for prompting consideration of such a change. The following technical change was made to support with-error-trace and wet (see *note NOTE-2-7-NEW-FUNCTIONALITY::), but may be of interest to those who do low-level programming using the ACL2 logical world. The 'unnormalized-body property is now stored not only for functions defined in :logic mode, but also for functions defined by the user in :program mode. (:Program mode Functions built into ACL2 still have their 'unnormalized-body property omitted, in order to save space.) The handling of "invisible" functions for purposes of controlling rewriting (see *note LOOP-STOPPER::) has been moved to a new table; see *note INVISIBLE-FNS-TABLE::. Macros that access and modify this table are called "...-invisible-fns-table" in place of their former names, "...-invisible-fns-alist." This feature was formerly implemented in the acl2-defaults-table, which prevented a book from exporting lists of invisible functions intended to work with the rewrite rules developed in the book. Thanks to Eric Smith and Rob Sumners for suggesting this change. See *note SET-INVISIBLE-FNS-TABLE:: (formerly set-invisible-fns-alist), and also see *note ADD-INVISIBLE-FNS:: and see *note REMOVE-INVISIBLE-FNS::, which provides ways to incrementally add to and remove from this table, respectively. The handling of printing binary function call nests using macros (See *note ADD-BINOP::) has also been moved out of the acl2-defaults-table as suggested by Eric and Rob, but this feature didn't work anyhow (see *note NOTE-2-7-BUG-FIXES::). Incidentally, the symbols binop-table, add-binop, and remove-binop have all been added to the list *acl2-exports* (see *note ACL2-USER::), add-invisible-fns and remove-invisible-fns have been added to that list, and set-invisible-fns-alist has been replaced in that list by set-invisible-fns-table. Function invisible-fns-alistp is no longer defined and has been removed from *acl2-exports*. We now enforce the stated restriction on the pairings in macro-aliases-table (see *note MACRO-ALIASES-TABLE::), namely, that it associates names of macros with names of funcions (with respect to the current ACL2 logical world). We make a similar requirement on invisible-fns-table. The theory-invariant event has been modified so that the default action is an error rather than a warning. Thanks to Eric Smith for suggesting this change. Also, the value returned upon successful execution of a theory-invariant event is now the key. Proof output that reports destructor elimination no longer uses the word "generalizing". This small change may help in browsing proof output, since now "generaliz" takes you to true uses of generalization. Thanks to Matyas Sustik for suggesting such a change. The command :pl now prints an abbreviated controller-alist for ;definition rules. Formerly the output from :pl could be overwhelming when the supplied function was part of a large mutual-recursion nest. The defaults for keyword parameters of certify-book have changed. See *note NOTE-2-7-BUG-FIXES::, in particular, the discussion there of two modifications to certify-book. Technical changes have been made to compress1 and compress2 that should usually be invisible to users. The next paragraph describes them in detail, only for competeness (i.e., that description can be ignored by most users). But first, here is an example showing an effect on users. The slow array warning was not there previously. Notice that the warning only arises if the event form is changed. The solution is to be sure that redundant defconst forms are syntactically identical. ACL2 !>(defconst *a* (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (1 . one) (0 . zero)))) Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) *A* ACL2 !>(aref1 'demo *a* 0) ZERO ACL2 !>(defconst *a* (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (1 . one) (0 . zero)))) This event is redundant. See :DOC redundant-events. Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :REDUNDANT ACL2 !>(aref1 'demo *a* 0) ZERO ACL2 !>(defconst *a* (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero) (1 . one)))) This event is redundant. See :DOC redundant-events. Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :REDUNDANT ACL2 !>(aref1 'demo *a* 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ACL2 !> As before, the von Neumann structure stored in the 'acl2-array property of the array name contains the array list object in its car. However, previously it was the case that compress1 and compress2 did not update that car when its new value would be equal to its old value. This was done largely in support of some type-set tables defined using defconst in type-set-b.lisp. The new versions of compress1 and compress2 are simpler in that no such exception is made in the case of equal lists, although instead the entire compression process is short-circuited when the input array list object is eq to the car of the 'acl2-array property. This change was made because the equality test was causing a "slow array access" warning to be printed in rare cases during proofs, as described elswhere (see *note NOTE-2-7-BUG-FIXES::). We no longer distribute documentation specific to Lucid Emacs. The Info documentation in directory doc/EMACS/ works well both for Gnu Emacs and XEmacs. A little-advertised macro, value, has long been allowed for top-level forms in books; see *note EMBEDDED-EVENT-FORM::. This has been replaced by a new macro, value-triple. The two have the same semantics at the top-level of books, where state is "live". However, value-triple should be used at the top-level of a book, while value should be used in function definitions (as before). This change eliminates a warning put out by the Allegro Common Lisp compiler for top-level value forms in books.  File: acl2-doc-emacs.info, Node: NOTE-2-7-PROOF-CHECKER, Next: NOTE-2-7-PROOFS, Prev: NOTE-2-7-OTHER, Up: NOTE-2-7 NOTE-2-7-PROOF-CHECKER ACL2 Version 2.7 Notes on Proof-checker Changes Output from the proof-checker can now be inhibited by supplying the symbol proof-checker in the list given to set-inhibit-output-lst.  File: acl2-doc-emacs.info, Node: NOTE-2-7-PROOFS, Next: NOTE-2-7-RULES, Prev: NOTE-2-7-PROOF-CHECKER, Up: NOTE-2-7 NOTE-2-7-PROOFS ACL2 Version 2.7 Notes on Changes in Proof Engine An improvement in the linear arithmetic heuristics has been provided by Robert Krug. For information about this change, search for the comment in add-linear-lemma (file rewrite.lisp) that begins as follows. ; Previous to Version_2.7, we just went ahead and used the result of Thanks, Robert! Also thanks to Eric Smith for providing a motivating example. The non-linear-arithmetic addition (see *note NON-LINEAR-ARITHMETIC::) led to several small changes in the linear-arithmetic decision procedure (see *note LINEAR-ARITHMETIC::). Two of these changes could affect existing proofs. First, when we are setting up the initial arithmetic database (which we call the "pot-lst"), we have always scanned it to see if there were any pairs of inequalities from which we could derive a previously unknown equality. In some cases we added this equality to the clause and in others we used it to rewrite the clause, substituting one side of the equality for the other throughout the clause. Previously, the heuristics that we used to determine whether we performed the substitution differed from those used in several other places in the code. This has now been regularized, and similar heuristics are now used throughout the code. The second change to the linear-arithmetic decision procedure is that we now explicitly add inequalities derived from type reasoning to the pot-lst. Previously, we performed cancellations against these inequalities without adding them to the pot-lst. This change results in there being more inequalities in the pot-lst than before, and so more chances for there to be a pair of inequalities from which an equality can be derived. In effect, certain simple consequences of the current goal (see *note TYPE-SET::) may now be added as hypotheses of the goal or used to peform equality substitutions. A slight improvement has been made to the way certain rewrite rules are stored. It was already the case that a rewrite rule rule whose conclusion C is not a call of a known equivalence relation (or eq, eql, or =) is stored as (iff C t), except that if ACL2 can determine (using its type-set mechanism) that C is Boolean, then the rule is stored as (equal C t). The iprovement is that if C and C' are Boolean, then a rule stated as (iff C C') is stored as (equal C C'). Thanks to Pete Manolios for providing an example that led us to consider this improvement. The heuristic use of equalities (fertilization) has been modified. Previously, ACL2 would sometimes substitute using an equality but keep the equality, and then undo the substitution by using the equality again. Now, when ACL2 keeps an equality after using it, it puts the equality inside a call of hide. Descendents of that goal that are unchanged by simplification will have this call of hide removed so that the equality can once again contribute to the proof. This change can cause some proofs to succeed that otherwise would fail. In the unlikely event that a proof fails that formerly succeeded, the following hint on "Goal" may fix the problem (see *note HINTS::): :expand ((:free (x) (hide x))) We have refined the heuristics employed when an IF form is assumed true or false. Our previous attempt (see note-2-6-proofs for the original announcement) was not as general as we had believed. We have also improved some low-level code responsible for rewriting IF expressions. In earlier versions of ACL2, it was possible to have the truth or falsity of an IF expression explicitly recorded in the type-alist, and yet not use this information during rewriting. This problem has been corrected. Thanks to Robert Krug for noticing this problem and implementing the fix. We have sped up the rewriter in some cases where there are large collections of mutually-recursive functions (see *note MUTUAL-RECURSION::). (Implementation notes: technically, we have modified the way function being-openedp operates on the fnstack, and we have modified *current-acl2-world-key-ordering* as described in the essay above its definition.) Forward-chaining is now done in the preprocessing phase of proof attempts (see the discussion of :DO-NOT -- see *note HINTS::). This is part of a technical change, made in support of translation of type declarations to guards (see *note NOTE-2-7-GUARDS::). Previously, whenever ACL2 checked for built-in-clauses, it then looked for a contradiction using type-set reasoning if it did not find a suitable built-in clause. The change is to perform forward-chaining in such cases (i.e., when a built-in clause is not found). A couple of changes have been made in the generation of goals for forcing-rounds. Thanks to Eric Smith for bringing issues to our attention that led to these changes. For one, guards are no longer relevant in such goal generation. Formerly, the addition of a guard could make a proof fail that otherwise succeeded. Secondly, contextual information is now always kept when it involves a constrained constant, i.e., a zero-ary function introduced in the signature of an encapsulate.  File: acl2-doc-emacs.info, Node: NOTE-2-7-RULES, Next: NOTE-2-7-SYSTEM, Prev: NOTE-2-7-PROOFS, Up: NOTE-2-7 NOTE-2-7-RULES ACL2 Version 2.7 Notes on Changes in Rules and Constants The defcong macro has been slightly changed. The difference is that the variable generated with suffix -EQUIV will now be in the same package as the name of the variable from which it is generated, rather than always belonging to the ACL2 package. Thanks to Hanbing Liu for suggesting this change. (Note that a couple of books have been modified to accommodate this change, e.g., books/finite-set-theory/set-theory.) In Version_2.6, a change was made for rules of class :rewrite whose conclusion is a term of the form (EQV lhs rhs), where EQV is =, eq, or eql: the rule was stored as though EQV were equal. (See *note NOTE-2-6-RULES::.) This change has been extended to rules of class :definition.  File: acl2-doc-emacs.info, Node: NOTE-2-7-SYSTEM, Prev: NOTE-2-7-RULES, Up: NOTE-2-7 NOTE-2-7-SYSTEM ACL2 Version 2.7 Notes on System-level Changes ACL2 now runs (once again) under LispWorks, specifically, LispWorks 4.2.0. However, we needed a patch, which presumably will be unnecessary after 4.2.7. From LispWorks support: Users with LispWorks4.2.7 should ask us at lisp-support@xanalys.com for the transform-if-node patch. It will be helpful if they quote (Lisp Support Call #11372) when doing so. Also, they must send a bug form generated from their LispWorks image: instructions at http://www.lispworks.com/support/bug-report.html. File books/Makefile-generic has been improved so that failed attempts to certify a book will cause the `make' to fail. Previously, an existing .cert file was left in place, and that sufficed for the `make' to be considered a success. Now, the old .cert file is first removed when recertification is found to be necessary. A change has been made to source file acl2.lisp to accommodate GCL 2.4.3. (ACL2 Version 2.6 does not work with some versions of GCL 2.4.3.) The error message has been improved when certain forms are typed to raw Lisp and the ACL2 loop has never been entered (with (LP)). The following symbols in the ACL2 package have been made untouchable, meaning that they are not available to the user: ev-fncall, ev, ev-lst, ev-acl2-unwind-protect, ev-fncall!, and user-stobj-alist-safe. The reason is that these functions can not be called safely except under certain restrictions. If you want to call the ACL2 evaluator, consider using the built-in system functions trans-eval or simple-translate-and-eval. CLISP Version_2.30 implements a notion of "locking" the "LISP" package that is incompatible with building ACL2. (CLISP Version_2.27 does not appear to have had this feature.) We have gotten around this problem by unlocking the "LISP" package in ACL2 images built on such CLISPs. Automatic proclaiming for GCL, which has (for a long time) been done for functions in compiled books, has been improved. Formerly, the only time a non-trivial output type (i.e., other than t) was inferred was when macroexpansion produced an explicit call of the. Now, if expressions can also generate non-t output types. Consider the following example. (defmacro the-fixnum (n) (list 'the '(signed-byte 29) n)) (defmacro 1+f (x) (list 'the-fixnum (list '1+ (list 'the-fixnum x)))) (defun foo (x) (declare (type (unsigned-byte 27) x)) (if (zp x) 0 (1+f (foo (1-f x))))) Formerly, the proclaim forms for foo, before and after this improvement, are as shown below. (PROCLAIM '(FTYPE (FUNCTION ((UNSIGNED-BYTE 27)) T) FOO)) ;old (PROCLAIM '(FTYPE (FUNCTION ((UNSIGNED-BYTE 27)) (SIGNED-BYTE 29)) FOO)) ;new Compiler info messages sent to error stream were eliminated for CMUCL.  File: acl2-doc-emacs.info, Node: NOTE-2-7(R), Next: NOTE-2-8, Prev: NOTE-2-7, Up: RELEASE-NOTES NOTE-2-7(R) ACL2 Version 2.7(r) (November, 2002) Notes In source file axioms.lisp, in order for proofs to succeed, (make proofs), the definitions of acl2-count and explode-atom have been modified slightly, and lemma standard-numberp-one [modified after Version_3.4 to become standardp-one] has been given :rule-classes nil. All skip-proofs forms have been eliminated from the nonstd books, thanks to Ruben Gamboa. The directory books/sqrt/, which was intended for ACL2(r), has been moved to books/nonstd/sqrt/ and added as appropriate to books/nonstd/Makefile. Please see *note NOTE-2-7:: for changes to Version_2.7 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-2-8, Next: NOTE-2-8(R), Prev: NOTE-2-7(R), Up: RELEASE-NOTES NOTE-2-8 ACL2 Version 2.8 (March, 2004) Notes BRIEF SUMMARY. The Version_2.8 notes are divided into the indicated subtopics. Here we give only a brief summary of just a few of the major new features and changes that seem most likely to impact existing proofs. Not included in this brief summary, but included in the subtopics, are descriptions of many improvements (including bug fixes and new functionality) that should not get in the way of existing proof efforts. In the description below we also omit discussion of changes that will become clear by way of error messages if they affect you. In particular, please see *note NOTE-2-8-NEW-FUNCTIONALITY:: for discussion of a number of new features that you may find useful. Acknowledgements and elaboration, as well as other changes, can be found in the subtopics listed below. o Some of the bug fixes (see *note NOTE-2-8-BUG-FIXES::): + Some soundness bugs were fixed. + The handling of free variables in hypotheses (see *note FREE-VARIABLES::) of rewrite and linear rules had a bug that prevented some proofs from going through. Now that this bug has been fixed, you may find some proofs running much more slowly than before. You can use accumulated-persistence and add-match-free-override to remedy this situation; see *note NOTE-2-8-BUG-FIXES:: for details. + The default-hints in the current logical world are no longer ignored by verify-guards. + Forms violating guard-checking such as (defconst *silly* (car 3)) are now allowed in books. o Some of the new functionality (see *note NOTE-2-8-NEW-FUNCTIONALITY::): + WARNING: You may find that control-d (in emacs, control-c control-d) can throw you completely out of Lisp where it had not formerly done so. + ACL2 now starts up inside the ACL2 loop -- that is, (LP) is executed automatically -- when built on CLISP or Allegro CL. This was already the case for GCL and CMUCL, and it still is not true for LispWorks. + See *note NOTE-2-8-ORDINALS:: for a discussion of a significant change in ordinal represtation, and in particular, for how to preserve existing proofs that depend on the previous ordinal representation. + Macros mbe ("must be equal"), mbt ("must be true"), and defexec have been introduced, which allow the user to attach alternate executable definitions to functions. + The user can now control multiple matching for free variables in hypotheses for :forward-chaining rules, as has already been supported for :rewrite and :linear rules. + It is no longer necessary to specify (set-match-free-error nil) in order to avoid errors when a rule with free variables in its hypotheses is missing the :match-free field. + The form (break-on-error) causes, at least for most Lisps, entry into the Lisp debugger whenever ACL2 causes an error. + A new table has been provided so that advanced users can override the built-in untranslate functionality. See *note USER-DEFINED-FUNCTIONS-TABLE::. + The pstack (`process [prover] stack") mechanism, formerly denoted checkpoints, has been improved. One of these improvements is to show actual parameters with (pstack t) rather than formals. + The defstobj event is now allowed to take an :inline argument, which can speed up execution. + Macro cw-gstack no longer takes arguments for the gstack or state. To print terms in full rather than abbreviated: (cw-gstack :evisc-tuple nil). + The include-book event now has an additional (optional) keyword, :dir. In particular, (include-book "foo/bar" :dir :system) will include the indicated book after prepending the path of the built-in books/ directory. You will probably not find :dir :system to be useful if you move the executable image or distributed books; see *note INCLUDE-BOOK::, in particular its "soundness warning". + The printing of results in raw mode (see *note SET-RAW-MODE::) may now be partially controlled by the user: see *note ADD-RAW-ARITY::. + For those using Unix/Linux `make': A cert.acl2 file can contain forms to be evaluated before an appropriate certify-book command is invoked automatically (not included in cert.acl2). o Some of the changes in the proof engine (see *note NOTE-2-8-PROOFS::): + ACL2 now prevents certain rewriting loops; see *note REWRITE-STACK-LIMIT::. + Small changes have been made to heuristics for controlling rewriting during proofs by induction and in handling certain "weak" compound-recognizer rules. + The handling of free variables in a hypothesis of a rewrite rule (see *note FREE-VARIABLES::) has been improved in the case that the hypothesis is of the form (equiv x y), where equiv is a known equivalence relation (see *note EQUIVALENCE::). + We have modified how the ACL2 simplifier handles the application of a defined function symbol to constant arguments, by avoiding the introduction of hide when evaluation fails if the term can be rewritten. + The generation of "Goal" for recursive (and mutually-recursive) definitions now uses the subsumption/replacement limitation (default 500). See *note CASE-SPLIT-LIMITATIONS::. + Default hints now apply to hints given in definitions, not just theorems. See *note DEFAULT-HINTS::. + Linear arithmetic now uses the conclusions of forward-chaining rules, and type-set now uses a small amount of linear reasoning when deciding inequalities. o Some of the changes in rules, definitions, and constants (see *note NOTE-2-8-RULES::): + See the above doc topic. o Guard-related changes are described in see *note NOTE-2-8-BUG-FIXES::. o Some of the proof-checker changes (see *note NOTE-2-8-PROOF-CHECKER::): + Added new proof-checker commands wrap1, wrap, and wrap-induct, to combine multiple conjuncts or goals. + The type-alist command now takes optional arguments that control whether or not the governors and/or conclusion are used in computing the context. o Some of the system-level changes (see *note NOTE-2-8-SYSTEM::): + ACL2 now runs on OpenMCL and on MCL 5.0. o Some of the other changes (see *note NOTE-2-8-OTHER::): + Emacs file emacs/emacs-acl2.el has been updated (see *note NOTE-2-8-OTHER:: for details). + When :pl is given a term other than a symbol, it will print all rewrite rules that match that term. + A new function, pkg-witness, returns a symbol in the given package. + The list constant *acl2-exports* has been extended. + A new release of the rtl library has been included: books/rtl/rel4/. See the README file in that directory. Again, please proceed to the subtopics for more thorough release notes. * Menu: * NOTE-2-8-BUG-FIXES:: ACL2 Version 2.8 Notes on Bug Fixes * NOTE-2-8-GUARDS:: ACL2 Version 2.8 Notes on Guard-related Changes * NOTE-2-8-NEW-FUNCTIONALITY:: ACL2 Version 2.8 Notes on New Functionality * NOTE-2-8-ORDINALS:: ACL2 Version 2.8 Notes on Changes to the Ordinals * NOTE-2-8-OTHER:: ACL2 Version 2.8 Notes on Miscellaneous Changes * NOTE-2-8-PROOF-CHECKER:: ACL2 Version 2.8 Notes on Proof-checker Changes * NOTE-2-8-PROOFS:: ACL2 Version 2.8 Notes on Changes in Proof Engine * NOTE-2-8-RULES:: ACL2 Version 2.8 Notes on Changes in Rules, Definitions, and Constants * NOTE-2-8-SYSTEM:: ACL2 Version 2.8 Notes on System-level Changes  File: acl2-doc-emacs.info, Node: NOTE-2-8-BUG-FIXES, Next: NOTE-2-8-GUARDS, Prev: NOTE-2-8, Up: NOTE-2-8 NOTE-2-8-BUG-FIXES ACL2 Version 2.8 Notes on Bug Fixes We have fixed a soundness bug in the tautology checker's handling of expressions of the form (not (not x)). This bug has gone back at least as far as Version_2.4. All of the regression tests passed after the fix, without modification. So we hope that this bug has rarely bitten anyone. Thanks to Qiang Zhang for sending us a proof of nil that led us to this fix: (thm (equal (and p q) (not (or (not p) (not q))))). And thanks to Matyas Sustik for an observation that led to an improvement of our initial fix. The preceding version (2.7) introduced a soundness bug in handling of ACL2 arrays, in which functions compress1 and compress2 were returning the input alist rather than compressing it appropriately. Here is a proof of nil that no longer succeeds, based on a bug report from Warren Hunt, who we thank for bringing this problem to our atttention. (defthm bad (not (let* ((ar2 (aset1 'my-array ar1 3 10)) (ar3 (compress1 'my-array ar2)) (ar4 (reverse (reverse ar2))) (ar5 (compress1 'my-array ar4))) (and (equal ar2 ar4) (not (equal ar3 ar5))))) :rule-classes nil) (defthm contradiction nil :rule-classes nil :hints (("Goal" :use ((:instance bad (ar1 (compress1 'my-array '((3 . 5) (:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 0 :NAME MY-ARRAY))))))))) On a related note, a new function flush-compress can be used for subtle control of under-the-hood raw Lisp support for fast array access, although we expect it to be very rare that users need this extra support. Previous versions have had two soundness bugs that can occur when using the proof-checker: o The first bug pertains to the expand command, and hence x and x-dumb commands (which call expand); see *note PROOF-CHECKER-COMMANDS::. The bug can occur when applying the above commands when the current term is a call of a constrained function symbol for which there is a :definition rule. Now, the expand command will succeed only when the function symbol of the current term is a defined function symbol, in which case the original definition is always used, in analogy to how the :expand hint works in the prover; see *note HINTS::. Thanks to John Erickson for sending an example that led us to wonder if there might be a soundness problem. o The second bug pertains to the s command (and commands that call it, e.g., s-prop). The proof-checker forms a context out of the top-level hypotheses and the if-terms governing the current term. If there is a contradiction in the top-level hypotheses, the proof-checker can appropriately consider the goal to be proved, and it does so. But formerly, the criterion was weaker: the contradiction could involve the combination of the top-level hypotheses and if-term governors. Thanks to Rob Sumners for noticing this bug. A soundness bug could be provoked in some Lisps by applying defpkg to the empty string. This has been disallowed. We fixed a soundness bug related to packages caused by a failure to track axioms introduced locally on behalf of defpkg events. See *note HIDDEN-DEATH-PACKAGE::. We fixed a soundness bug caused by a failure to check that a :type-prescription rule can be processed when proofs are skipped or under a defequiv event. The former case can occur when processing an encapsulate or include-book event, where the rule could depend on a local :compound-recognizer rule preceding the proposed :type-prescription rule under the same encapsulate or include-book event. See *note LOCAL-INCOMPATIBILITY:: for such an example. We fixed a potential soundness bug relating to reclassifying a :program mode function to :logic mode (as done by verify-termination or the submission of an appropriate "redundant" definition) without adequate checking that stobj usage was identical. Allegedly redundant definitions must now preserve the stobjs declaration as well as the formals, body, guard and type declarations. We thank Vernon Austel for pointing out this problem. It was possible to get a raw Lisp error by introducing a locally defined function with guard verification inhibited and then subsequently introducing the same definition non-locally without that inhibition. The following example will clarify. (encapsulate nil (local (defun foo (x) (declare (xargs :guard t :verify-guards nil)) (car x))) (defun foo (x) (declare (xargs :guard t)) (car x))) ; The following causes a raw lisp error because ACL2 runs the Common Lisp ; definition of foo, because it thinks that foo's guard of t was verified. (thm (equal (foo 3) xxx)) Thanks to Jared Davis for bringing this problem to our attention. We are particularly grateful to Jared because his example exploited this bug by applying it to a function defined using mbe (introduced in this same version, 2.8), in order to prove nil! The sort of error message shown below can legitimately occur when certifying a book in a certification world where there was an include-book command with a relative pathname (see *note PATHNAME::). However, it was occurring more often than necessary. This has been fixed. ACL2 Error in (CERTIFY-BOOK "foo" ...): The certification world has include-book commands for book "bar" that correspond to different full pathnames, namely "/u/dir1/bar" and "/u/dir2/bar". ACL2 cannot currently certify a book in such a world. To work around this problem, use an absolute pathname for at least one of these books (see :DOC pathname). Bugs were fixed in with-output, in particular related to the use of values :all. Also, documentation for with-output has been improved. Thanks to Vernon Austel for pointing out the bugs. Fixed a lisp error occurring when bash proof-checker command was given illegal syntax, e.g., (bash (("Goal" :in-theory (enable binary-append)))) instead of (bash ("Goal" :in-theory (enable binary-append))). We added an appropriate guard to find-rules-of-rune, which will avoid hard lisp errors when this function is called on non-rune arguments. Thanks to Eric Smith for pointing out this issue. It was possible for a redundant include-book form (see *note REDUNDANT-EVENTS::) to leave a command in the ACL2 logical world and to cause (re-)loading of a compiled file. These behaviors have been fixed. In particular, if book1 has already been included in the current ACL2 world and (include-book "book1") occurs in book2, then the compiled file for book1 will not be loaded again when book2 is included. Thanks to Dave Greve for bringing our attention to these problems, and to Eric Smith for bringing up a special case earlier (where "//" occurred in the book name). The summary printed at the end of a proof had not listed :induction rules used in a proof. This has been corrected. The use of proof trees in emacs redefined `control-c control-c' in such a way that in telnet mode, the telnet session was interrupted and perhaps could not be continued. This has been fixed. Source function load-theory-into-enabled-structure contained a guard-violating call of compress1. Thanks to Vernon Austel for bringing this problem to our attention; even though this bug was benign (as he pointed out), we like keeping the source code free of guard violations. A number of proof-checker atomic macros caused a hard error when all goals have already been proved. This has been fixed. Thanks to John Erickson for sending an example of the issue. A bug has been fixed in add-match-free-override. Formerly, a table guard violation occurred when calling add-match-free-override more than once with first argument other than :clear. Defininitions of functions involving large constants could cause stack overflows. This has been fixed, at least in some of the most egregious cases (by making a source function fn-count-evg tail-recursive). Thanks to Jared Davis for bringing this problem to our attention. Evaluation of computed hints could cause stack overflows. This has been fixed. Thanks to Eric Smith for bringing this problem to our attention. Evaluation of :monitor on :definition runes is now fast even if the specified function is part of a very large mutual-recursion nest. Thanks to Eric Smith for sending an example showing that this wasn't always the case. Fixed a bug in books/bdd/cbf.lisp that was causing certification of distributed bdd books to fail when the connected book directory (see *note CBD::) differs from the current working directory. Thanks to Scott Guthery for bringing this bug to our attention and supplying a helpful log. Duplicate rule names have been eliminated from warnings generated upon the use of enabled :rewrite or :definition rules. Thanks to Eric Smith for pointing out this problem. The trace utilities (see *note TRACE::), as modified for GCL and Allegro Common Lisp, had failed to show more than the first return value for so-called "*1*" functions (essentially, executable-counterpart functions) when they were returning multiple values (via mv). This has been fixed. Thanks to Erik Reeber for pointing out this problem. Also, it is now possible to refer to arglist in trace$ forms when ACL2 is built on GCL, not just when ACL2 is built on Allegro Common Lisp. Uses of hide introduced during proofs by failed attempts to evaluate constrained functions (see *note HIDE::) are now tracked, so that the rune (:DEFINITION HIDE) will show up in the summary. The following bug, introduced back in Version 2.7, has been fixed. The bug applied only to GCL and may well not have affected anyone. But the function proclamation computed by ACL2 for compilation usually had an output type of nil where it should have been t. The macro gc$ had a bug exhibited when it was supplied one or more arguments. This has been fixed. The macro defabbrev broke when supplied a string and no documentation, e.g., (defabbrev foo () ""). Thanks to Rob Sumners for noticing this problem and providing a fix, which we have incorporated. For ACL2 executables built on Allegro Common Lisp, a Lisp error occurred when trace$ was called on other than a defined function symbol. Now ACL2 prints a more useful error message. The proof-checker no longer accepts a (verify) command when some function symbol in the original goal no longer exists in the current ACL2 logical world. Thanks to John Erickson for bringing this issue to our attention. The function ld-redefinition-action may now be called by the user. Thanks to Vernon Austel for suggesting that we remove this symbol from the list of so-called untouchables. The handling of free variables in hypotheses (see *note FREE-VARIABLES::) of rewrite and linear rules had a bug that prevented some proofs from going through. Here is a simple example, essentially provided by Diana Moisuc, who we thank for bringing this issue to our attention. The proof of the thm below had failed, but now will succeed. This particular bug prevented, for example, the :all behavior from occurring when the first hypothesis of the rule does not have free variables. NOTE: Now that this bug has been fixed, you may find some proofs running much more slowly than before. You can use accumulated-persistence to locate rules that are slowing down your proofs because of excessive attention to free variables, and then execute add-match-free-override for those rules (or, just change the rules themselves to specify :once in the :rule-classes). (defstub foo1 (* ) => *) (skip-proofs (defthm aux-foo1 (implies (and (integerp a) (integerp i) (equal (foo1 0) (list 0 i))) (equal (foo1 a) (list 0 (+ a i)))) :rule-classes ((:rewrite :match-free :all)))) (thm (implies (and (integerp i) (integerp a) (equal (foo1 0) (list 0 i))) (equal (foo1 a) (list 0 (+ a i))))) Formerly, creation of large arrays could cause an error in the underlying Common Lisp implementation without helpful messages for the user. Now, we check Common Lisp restrictions on arrays and print a helpful error message if they are violated, namely: each dimension must be less than the value of Common Lisp constant array-dimension-limit, and the product of the dimensions must be less than the value of Common Lisp constant array-total-size-limit. Thanks to Warren Hunt for bringing this issue to our attention. Note: this change also removes a former restriction of stobj array fields to size smaller than 2^28-1, provided the underlying Lisp can support larger arrays. The default-hints in the current logical world were ignored by verify-guards. This has been fixed. Thanks to Jared Davis for pointing out this bug and sending a helpful example. The brr mechanism has been cleaned up in order to avoid hard errors and infinite loops that can arrive when typing interrupts (control-c) or end-of-files (control-d) inside the brr loop. Thanks to Dave Greve, Olga Matlin, Eric Smith, and Serita Van Groningen for bringing this issue to our attention. As a byproduct, if you type control-d (or if inside emacs, control-c control-d), you may now quit entirely out of ACL2 and lisp (see *note GOOD-BYE::) in some cases where you formerly would not have, for example when sitting at the ACL2 prompt (which formerly, in Allegro Common Lisp for example, would merely take you into raw Lisp rather than quitting everything). We have eliminated structural flaws in the HTML documentation pages that could make them unreadable in some browsers. Thanks to Bill Young for bringing this issue to our attention and to Joe Hendrix for diagnosing the problem. The proof-checker could run very slowly after many instructions in a given session. This has been fixed; thanks to Art Flatau for bringing this problem to our attention. (Implementation detail: We now keep tag-trees duplicate-free when we accumulate them into state. This change could have minor speed advantages for some top-level proofs too, not just in the proof-checker.) The printing of accesses to stobjs using nth or update-nth has been done using symbolic constants since ACL2 Version_2.6. However, there was a bug that prevented this feature from working for update-nth except at a top-level call. This has been fixed. Thanks to Julien Schmaltz for bringing this problem to our attention. For example, consider these events: (defstobj st field0 field1) (thm (equal (nth 1 (update-nth 0 17 st)) (car (cons xxx yyy))) :hints (("Goal" :in-theory (disable nth update-nth)))) Before the fix, the proof attempt of the above silly thm printed the following. (NTH 1 (UPDATE-NTH *FIELD0* 17 ST)) After the fix, we instead see the following. (NTH *FIELD1* (UPDATE-NTH *FIELD0* 17 ST)) It is now possible to certify and subsequently include books that require guard-checking to be off. For example, the book can contain the form (defconst *silly* (car 3)) even though 3 fails to satisfy the guard of car. Formerly, it was necessary to execute :set-guard-checking nil before a certify-book or include-book in order for such a form to be handled without error. Thanks to Hanbing Liu for bringing this problem to our attention. Fixed a proof-checker bug that could cause probably cause strange error, "Attempt to access the plist field". Thanks to Bill Young for bringing this problem to our attention. Fixed a proof-checker bug that was failing to record applications of rewrite rules using the proof-checker's :rewrite command, causing the proof summary to omit mention of that rule (for example, when using the proof-checker's :exit command to generate an :instructions hint). Thanks to Bill Young for pointing out this bug. Modernized some of the proof-tree emacs and infix printing stuff, thanks to suggestions made by Camm Maguire.  File: acl2-doc-emacs.info, Node: NOTE-2-8-GUARDS, Next: NOTE-2-8-NEW-FUNCTIONALITY, Prev: NOTE-2-8-BUG-FIXES, Up: NOTE-2-8 NOTE-2-8-GUARDS ACL2 Version 2.8 Notes on Guard-related Changes All the guard-related changes may be found elsewhere; in particular, see *note NOTE-2-8-BUG-FIXES::.  File: acl2-doc-emacs.info, Node: NOTE-2-8-NEW-FUNCTIONALITY, Next: NOTE-2-8-ORDINALS, Prev: NOTE-2-8-GUARDS, Up: NOTE-2-8 NOTE-2-8-NEW-FUNCTIONALITY ACL2 Version 2.8 Notes on New Functionality WARNING: You may find that control-d (in emacs, control-c control-d) can throw you completely out of Lisp where it had not formerly done so. (CLISP and Allegro CL only) ACL2 now starts up inside the ACL2 loop -- that is, (LP) is executed automatically -- when built on CLISP or Allegro CL. This was already the case for GCL and CMUCL, and it still is not true for LispWorks. Thanks to Joe Corneli for bringing the CLISP command-line option "-i" to our attention, which led to this CLISP change and inspired reconsideration of how to do this for Allegro CL. Pete Manolios and Daron Vroon have changed the representation of ordinals in ACL2, defined algorithms for ordinal arithmetic, and created a library of theorems to reason about ordinal arithmetic. We thank them for these nice contributions. See *note NOTE-2-8-ORDINALS:: for details, in particular, for how to preserve existing proofs that depend on the previous ordinal representation. Sometimes users create rules of class :rewrite that cause an infinite loop in the ACL2 rewriter. This has lead to Lisp stack overflows and even segmentation faults. Now, the depth of calls of functions in the ACL2 rewriter is limited, and under user control. See *note REWRITE-STACK-LIMIT::. Macros mbe ("must be equal") and mbt ("must be true") have been introduced, which allow the user to attach fast executable definitions to (presumably slower) :logic mode functions. Thanks to Vernon Austel for a key idea. Also provided is a macro defexec, which employs mbe but enforces the requirement that the executable definition also terminates. Thanks to Jose Luis Ruiz Reina for collaborating in the design and development of defexec, and for useful comments from a number of others as well in the development of mbe including Joe Hendrix and Rob Sumners. Definitions have been added for functions rassoc-eq and rassoc-equal, which are like rassoc but use different tests and have different guards. (Compare assoc-eq and assoc-equal, which are in similar relation to assoc.) The user can now control multiple matching for free variables in hypotheses for :forward-chaining rules, as has already been supported for :rewrite and :linear rules. For :forward-chaining rules, "free variables" are those in the hypotheses not bound by a given trigger term. As for :rewrite and :linear rules, free-variable matching may be limited to the first successful attempt by specifying :match-free :once with :forward-chaining in the :rule-classes, and add-match-free-override may be used to modify the behavior of an existing rule. Thanks to Erik Reeber for most of the implementation of these new capabilities, as well as significant assistance with a corresponding new documentation topic (see *note FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING::). It is no longer necessary to specify (set-match-free-error nil) in order to avoid errors when a rule with free variables in its hypotheses is missing the :match-free field. (This was already true during book certification, but now it is the case in interactive sessions as well.) The form (break-on-error) causes, at least for most Lisps, entry into the Lisp debugger whenever ACL2 causes an error. See *note BREAK-ON-ERROR::. Thanks to John Erickson for providing encouragement to provide this feature. A new table has been provided so that advanced users can override the built-in untranslate functionality. See *note USER-DEFINED-FUNCTIONS-TABLE::. The pstack mechanism (formerly denoted checkpoints) has been improved. The "process [prover] stack," or pstack, is automatically printed when proofs abort. Evaluation of function calls on explicit arguments during proofs is now tracked. Actual parameters are shown with (pstack t) rather than formals. Thanks to Bill Legato for suggesting the first two of these improvements and, in general, encouraging changes that make ACL2 easier to use. The defstobj event is now allowed to take an :inline argument, which can speed up execution. Thanks to Rob Sumners for suggesting and implementing this new feature. Macro assert$ has been added in order to make it easy to write assertions in one's code. Semantically, (assert$ test form) is the same as form, but it causes a hard error (using illegal) if test evaluates to nil. Macro cw-gstack no longer takes arguments for the gstack or state. However, it now takes a keyword argument (which is optional), :evisc-tuple, that can be used to control how it prints terms. In particular, cw-gstack abbreviates large terms by default, but (cw-gstack :evisc-tuple nil) causes terms to be printed in full. Thanks to Robert Krug and Eric Smith for requesting this improvement. The advanced user now has more control over the evisceration of terms. See *note LD-EVISC-TUPLE::, in particular the new paragraph on "The printing of error messages and warnings." The include-book event now has an additional (optional) keyword, :dir. The value of :dir should be a keyword that is associated with an absolute directory pathname to be used in place of the current book directory (see *note CBD::) for resolving the first argument of include-book to an absolute pathname. At start-up, the only such keyword is :system, so that for example (include-book "arithmetic/top" :dir :system) will include the book "arithmetic/top" under the "books/" directory of your ACL2 installation. But you can associate "projects" with keywords using add-include-book-dir, e.g., (add-include-book-dir :my-project "/u/smith/project0/"). See *note ADD-INCLUDE-BOOK-DIR:: and also see *note DELETE-INCLUDE-BOOK-DIR:: and see *note INCLUDE-BOOK::. Note: You will probably not find :dir :system to be useful if the distributed books are not placed in the path of their original location, pointed to by :dir :system, which will often happen if the executable image is obtained from another site. Also see *note INCLUDE-BOOK::, in particular its "soundness warning". The printing of results in raw mode (see *note SET-RAW-MODE::) may now be partially controlled by the user: see *note ADD-RAW-ARITY::. Also, newlines are printed when necessary before the value is printed. For those using Unix/Linux `make': A cert.acl2 file can contain forms to be evaluated before an appropriate certify-book command is invoked automatically (not included in cert.acl2). Jared Davis has contributed a new set of books for ordered finite set theory to the standard distribution, books/finite-set-theory/osets-0.81/. See the README file in that directory. Thanks, Jared. Robert Krug has contributed two related changes (thanks, Robert!) in support of stronger arithmetic reasoning. First, one can now enable and disable nonlinear arithmetic with a :nonlinearp hint, which will override the default provided by set-non-linearp (initially, nil). See *note HINTS::. Second, computed-hints can now have access to the HISTORY, PSPV, and CTX variables of the waterfall, which (for example) allows the writing of a hint which will enable nonlinear arithmetic on precisely those goals that are stable-under-simplificationp. See *note COMPUTED-HINTS::. Robert Krug has contributed a new set of arithmetic books to the standard distribution, books/arithmetic-3/. See the README file in that directory. Thanks, Robert.  File: acl2-doc-emacs.info, Node: NOTE-2-8-ORDINALS, Next: NOTE-2-8-OTHER, Prev: NOTE-2-8-NEW-FUNCTIONALITY, Up: NOTE-2-8 NOTE-2-8-ORDINALS ACL2 Version 2.8 Notes on Changes to the Ordinals Please see *note ORDINALS::.  File: acl2-doc-emacs.info, Node: NOTE-2-8-OTHER, Next: NOTE-2-8-PROOF-CHECKER, Prev: NOTE-2-8-ORDINALS, Up: NOTE-2-8 NOTE-2-8-OTHER ACL2 Version 2.8 Notes on Miscellaneous Changes Execution of table events has been sped up in many cases by avoiding excessive consing. ACL2 now warns if :rewrite (or :definition) rules contain free variables on the right-hand side. Thanks to Dave Greve for raising this issue. Emacs file emacs/emacs-acl2.el has been updated to better comprehend the notion of the "ACL2 shell", which is the buffer to which ACL2 forms are written by commands defined in the above file. Thus, command control-t e has been modified always to write to the ACL2 shell (which is "*shell*" by default), and the following new commands have been defined. o control-t c Set the ACL2 shell to the current buffer. o control-t b Change to the ACL2 shell. The commands :pl and :pr may now be given a macro name that corresponds via the macro-aliases-table to a function name, so that for example :pl append is treated the same as :pl binary-append. A more interesting improvement, for :pl only, is that :pl may now take any term. When :pl is given a term other than a symbol, it will print all rewrite rules that match that term. Thanks to David Russinoff, Robert Krug, and Bill Legato for getting this going. A new function, pkg-witness, returns a symbol in the given package. The installation instructions have been updated, for example to give more guidance on obtaining Lisp implementations and to mention the acl2-help mailing list. Jared Davis has suggested some symbols to be added to *acl2-exports*, and we have done so. Thanks, Jared. o MFC (used in syntaxp and extended-metafunctions; thanks also to Robert Krug for this one) o ID, CLAUSE, WORLD, and STABLE-UNDER-SIMPLIFICATIONP (used in computed-hints) o SET-DEFAULT-HINTS The command :pe has been improved so that when the event is inside an included book, the path of included books (from the top-level book down to the one containing the event) is shown. Thanks to Eric Smith (perhaps among others) for pointing out the utility of this improvement. A new release of the rtl library has been included: books/rtl/rel4/. See the README file in that directory.  File: acl2-doc-emacs.info, Node: NOTE-2-8-PROOF-CHECKER, Next: NOTE-2-8-PROOFS, Prev: NOTE-2-8-OTHER, Up: NOTE-2-8 NOTE-2-8-PROOF-CHECKER ACL2 Version 2.8 Notes on Proof-checker Changes Added new proof-checker commands wrap1, wrap, and wrap-induct. Wrap replaces multiple goals by their conjunction: (wrap instr1 instr2 ...) employs wrap1 so that the indicated instructions create only at most one new goal. Wrap-induct is a simple example of the use of wrap, so that induction creates only one goal (the conjunction of the base and induction steps). Wrap1 can be used immediately after a prover call (bash, prove, reduce, bdd, or induct) to collapse the new goals into one. See *note PROOF-CHECKER-COMMANDS::. The proof-checker command = failed to work as expected when a governing IF-test of the current term is T. This has been fixed (by fixing source function conjuncts-of). Thanks to Yoann Padioleau for bringing this problem to our attention. The type-alist command now takes optional arguments that control whether or not the governors and/or conclusion are used in computing the context that is printed (see *note PROOF-CHECKER-COMMANDS::, specifically subtopic type-alist). Thanks to Rob Sumners for suggesting this improvement. The macro toggle-pc-macro has always taken an optional second argument of atomic-macro or macro. However, this was not clearly documented, and those two symbols had to be in the ACL2 package. Both of these problems have been remedied. Thanks to John Erickson for bringing the lack of documentation of the second argument to our attention.  File: acl2-doc-emacs.info, Node: NOTE-2-8-PROOFS, Next: NOTE-2-8-RULES, Prev: NOTE-2-8-PROOF-CHECKER, Up: NOTE-2-8 NOTE-2-8-PROOFS ACL2 Version 2.8 Notes on Changes in Proof Engine ACL2 now prevents certain rewriting loops; see *note REWRITE-STACK-LIMIT::. During the computation of constraints for functional instantiation, (prog2$ term1 term2) and (the type term2) are now treated as term2. A change has been made in heuristics for controlling rewriting during proofs by induction. Formerly, during induction proofs, ACL2 suppressed rewriting of certain "induction hypothesis" terms, and forced expansion of certain "induction conclusion" terms, until rewriting had stabilized. This meddling with the rewriter is still turned off when rewriting has stabilized, but it is now turned off earlier once an ancestor has been through the rewriter and the current goal is free of "induction conclusion" terms. Thanks to Dave Greve and Matt Wilding for providing an example and associated analysis that led us to look for a heuristic modification. A change has been made in the heuristics for handling certain "weak" compound-recognizer rules when building contexts. Those who want to dig deeply into this change are welcome to look at the code following the call of most-recent-enabled-recog-tuple in the code for function assume-true-false in the ACL2 sources. The handling of free variables in a hypothesis of a rewrite rule (see *note FREE-VARIABLES::) has been improved in the case that the hypothesis is of the form (equiv x y), where equiv is a known equivalence relation (see *note EQUIVALENCE::). Previously, if the rewriter was attempting to rewrite the hypothesis (equiv x y) of a rewrite rule, in a context where x' is an instance of x, then the rewriter could fail to notice a term (equiv x' y') true in the current context where y' is an instance of y, in the case that x' precedes y' in the term-order. This has been remedied. This improvement applies regardless of whether x, y, or (we believe) both are already fully instantiated in the present context. Thanks to Joe Hendrix for bringing up an example and to Vernon Austel for providing another, simple example. A very minor change has been made to the rewriter in the case that an equality appears on the left-hand side of a :rewrite rule. Formerly, when such an equality (equal x y) was commuted to (equal y x) in order for the rule to match the current term, then all equalities on the instantiated right-hand side of the rule were commuted, except for those occurring inside another equality. The instantiated right-hand side is no longer modified. It seems very unlikely that this change will cause proofs to fail, though we cannot completely rule out that possibility. We have modified how the ACL2 simplifier handles the application of a defined function symbol to constant arguments in certain cases, which we now describe. As before, ACL2 attempts to simplify such a function application by evaluation, provided the :executable-counterpart of the function is enabled. And as before, if that evaluation fails due to a subroutine call of a constrained function (introduced by encapsulate), ACL2 may wrap a call of hide around this function application. (See *note HIDE::.) But now, ACL2 attempts to apply definitions and rewrite rules in the case that this evaluation fails, and only if the resulting term is unchanged does ACL2 wrap hide around this function application. Thanks to Matt Wilding for bringing up the idea of this modification. The generation of "Goal" for recursive (and mutually-recursive) definitions now uses the subsumption/replacement limitation (default 500). See *note CASE-SPLIT-LIMITATIONS::. Default hints now apply to hints given in definitions, not just theorems. See *note DEFAULT-HINTS::. Thanks to Robert Krug for implementing the following two improvements involving linear arithmetic reasoning: linear arithmetic now uses the conclusions of forward-chaining rules, and type-set now uses a small amount of linear reasoning when deciding inequalities.  File: acl2-doc-emacs.info, Node: NOTE-2-8-RULES, Next: NOTE-2-8-SYSTEM, Prev: NOTE-2-8-PROOFS, Up: NOTE-2-8 NOTE-2-8-RULES ACL2 Version 2.8 Notes on Changes in Rules, Definitions, and Constants The theory minimal-theory has been changed by adding the definition rune for mv-nth to the theory. A corresponding change has been made to the theory warning mechanism, which was failing to warn if the definition of mv-nth is disabled, even though calls of mv-nth can be expanded by special-purpose code in the rewriter. Thanks to Serita Van Groningen for pointing out this problem with the theory warning mechanism. The defevaluator event has been modified so that in the body of the evaluator function, to add a new case (ATOM X) (returning nil) has been inserted immediately after the case (EQ (CAR X) 'QUOTE). This is a no-op semantically but may speed up proofs. Thanks to Warren Hunt for suggesting this change. A new form of :compound-recognizer rule is now allowed: (if (fn x) concl1 concl2) This is equivalent to an existing form: (and (implies (fn x) concl1) (implies (not (fn x)) concl2)) Thanks to Josh Purinton for bringing this to our attention. Rewrite rules realpart-+ and imagpart-+ have been added in order to simplify the realpart and imagpart (respectively) of a sum. They follow from a theorem add-def-complex that equates a sum with the complex number formed by adding real and imaginary parts. All three of these theorems may be found in source file axioms.lisp. Thanks to Eric Smith for raising a question leading to these additions, as well as to Joe Hendrix and Vernon Austel for helpful suggestions.  File: acl2-doc-emacs.info, Node: NOTE-2-8-SYSTEM, Prev: NOTE-2-8-RULES, Up: NOTE-2-8 NOTE-2-8-SYSTEM ACL2 Version 2.8 Notes on System-level Changes ACL2 now runs on OpenMCL, "an opensourced Common Lisp implementation, derived from Digitool's Macintosh Common Lisp product." Thanks to Greg Wright and Robert Krug for doing most of the work for this port. When (LP) is first executed, the underlying raw Lisp package will change to "ACL2" (if that is not already the current package in raw Lisp). This is a minor change that will probably not be noticed, since up to now it has probably been the case that the ACL2 executable starts up with "ACL2" as the underlying raw Lisp package. But this change was made because we have been informed that ACL2 executables based on OpenMCL need not start up with "ACL2" as the underlying raw Lisp package. ACL2 now runs on MCL 5.0. Thanks to Pascal Costanza for updates to the instructions in file mcl-acl2-startup.lisp and for an update to the ACL2 sources (parameter *compiled-file-extension*).  File: acl2-doc-emacs.info, Node: NOTE-2-8(R), Next: NOTE-2-9, Prev: NOTE-2-8, Up: RELEASE-NOTES NOTE-2-8(R) ACL2 Version 2.8(r) (March, 2003) Notes The Makefile has been modified by adding a new target, clean-links. This can be used in order to remove all soft links, which is useful if the directory is copied or moved to a new location or if there are file system changes that cause problems with link pathnames. Please also see *note NOTE-2-8:: for changes to Version_2.8 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-2-9, Next: NOTE-2-9(R), Prev: NOTE-2-8(R), Up: RELEASE-NOTES NOTE-2-9 ACL2 Version 2.9 (October, 2004) Notes *TABLE OF CONTENTS.* ============================== BUG FIXES. NEW FUNCTIONALITY. CHANGES IN PROOF ENGINE. GUARD-RELATED CHANGES. PROOF-CHECKER CHANGES. SYSTEM-LEVEL CHANGES. BOOK CHANGES. MISCELLANEOUS CHANGES. ============================== *BUG FIXES.* We fixed a soundness bug due to a conflict between user-supplied package names and internal package names (obtained by prepending a Lisp constant, *1*-package-prefix*) and user-supplied package names. For example, the form (defpkg "ACL2_*1*_MYPKG" ()) is no longer legal. Thanks to Robert Krug for asking a question that led directly to the discovery of this bug. We fixed a soundness bug that allows :logic mode functions to call :program mode functions. The fix furthermore prevents functions with guards verified from calling functions with guards not verified. We had thought we already prevented all this, but there was a problem with the interaction of local definitions and redundancy checking (see *note REDUNDANT-EVENTS::). We fixed a soundness bug that could occur when built-in functions were called during macroexpansion (a hole in so-called "safe-mode"). Fixed a minor bug in system functions genvar1 and genvar, where eq had been used in place of eql. This bug was discovered during the plugging of a hole in safe-mode, mentioned just above. We fixed handling of the :inline keyword for defstobj, which previously could cause raw Lisp errors in OpenMCL and CMU Common Lisp. Thanks to John Matthews for bringing this issue to our attention. Calls of include-book could result in a state for which some function definitions were not compiled that should have been. The result could be performance degradation or even stack overflows. This situation could arise in the following two ways. o Inclusion of a book with an absolute pathname that differs from the absolute pathname at certification time, presumably because of the use of soft links. Thanks to Bob Boyer and Warren Hunt for bringing a stack overflow to our attention that led us to this fix. o Large mutual-recursion nests (more than 20 functions) are executed in a superior book. We fixed some performance bugs that can increase the speed of include-book calls by a factor of close to 2. Thanks to Eric Smith for asking if we could speed up include-book processing; we have done so in the past, but primarily focusing on large mutual-recursion nests (which have nothing in particular to do with the current improvements). Also, thanks to Rob Sumners for a very useful remark early in the process that kept us from drawing an incorrect conclusion at that point. We fixed :pl so that it can be run on a form that returns multiple values, which it could not do previouslly. Thanks to Eric Smith for pointing out this problem. Fixed a bug in the Allegro ACL2 trace utility (see *note TRACE$::) that was causing "10>" to be printed as "9>", "11>" to be printed as "10 >", "12>" to be printed as "11 >", and so on. Fixed a proof-checker bug that was preventing the use of the DV command (or a numerical command) on let expressions. Thanks to Bill Young for pointing out this problem. Fixed a bug in a comment on how to set ACL2_BOOKS_DIR in the makefile. Thanks to Dave Greve for pointing out this problem. Fixed a potential soundness bug in the linear arithmetic routines. Thanks to Jared Davis for noticing this problem and to Robert Krug for implementing the fix. (Technical details: We had been assuming that polynomials were being normalized - see the definition of good-polyp in linear-a.lisp - but this assumption was false.) When the macro open-trace-file is opened twice in succession, it now automatically closes the first trace output channel before opening another. It is now possible to use `make' to build ACL2 on Windows systems that support `make'. Thanks to Pete Manolios and Mike Thomas for pointing out the problem, to Jared Davis and Mike for helping us to analyze the problem, and to Jared for testing the fix. Fixed a bug in the guard of with-output that was causing a needless guard violation. *NEW FUNCTIONALITY.* The new events add-default-hints and remove-default-hints allow one to append to or subtract from the current list of default hints. The event set-default-hints continues to set the list of default hints, discarding the previous value of the default-hints. Note that set-default-hints is still local to the encapsulate or book in which it occurs, and add-default-hints has the same property, although neither is implemented any longer using the acl2-defaults-table. New events add-default-hints!, remove-default-hints!, and set-default-hints! are the same as add-default-hints, remove-default-hints, and set-default-hints, respectively, except that the former three events are not local to their enclosing encapsulate or book. Thanks to Jared Davis for suggesting these enhancements. OpenMCL's tracing routines have been modified in a similar manner as those of Allegro CL. Thanks to Robert Krug for providing this enhancement. Guard-checking can now be caused to happen on recursive calls. See "GUARD-RELATED CHANGES" below for details. Advanced users can now inhibit compilation of so-called "*1* functions" with the :comp command; see *note COMP::. Thanks to Rob Sumners for suggesting this enhancement. Added new legal argument hard? for the er macro, which is now documented. See *note ER::. Thanks to Rob Sumners for a discussion leading to this change. Also, the three legal first arguments to er -- hard, hard?, and soft -- may now be in any package (thanks to Jared Davis for bringing this issue to our attention). We have removed the requirement that for a rule's hypothesis (bind-free term var-list), at least one variable must occur free in term. For example, the expression (bind-free (bind-divisor a b) (x)) was legal because a and b occur free in (bind-divisor a b); but (bind-free (foo (bar)) (x)) was not legal. The latter is no longer disallowed. (Technical note: this allows bind-free to be used to create explicit substitutions in metafunction hypotheses.) The following two enhancements have been implemented for rules of class :meta. Thanks to Eric Smith for requesting more control of reasoning with :meta rules, which led to these enhancements, and to him and Robert Krug for helpful discussions. o It is now possible to control backchaining in rules of class :meta by providing a :backchain-limit-lst argument, as was already allowed for rules of class :rewrite and :linear. See *note RULE-CLASSES::. However, unlike those other two rule classes, the value for :backchain-limit-lst is prohibited from being a non-empty list; it must be either nil or a non-negative integer. o (For advanced users.) It is now legal for hypothesis metafunctions to generate, in essense, calls of syntaxp and bind-free, handled essentially as they are handled in hypotheses of :rewrite and :linear rules. We say "essentially" primarily because both syntaxp and bind-free are actually macros, but hypothesis metafunctions must generate translated terms (see *note TERM::). The enterprising advanced user can call :trans to see examples of translated terms corresponding to calls of syntaxp and bind-free. A new command :trans! has been added, which is like :trans except that :trans! ignored issues of single-threadedness. See *note TRANS!::. Thanks to Eric Smith for suggesting this addition. The :pf command now works when the argument is the name of a macro associated with a function by macro-aliases-table. *CHANGES IN PROOF ENGINE.* The simplifier has been changed slightly in order to avoid using forward-chaining facts derived from a literal (essentially, a top-level hypothesis or conclusion) that has been rewritten. As a practical matter, this may mean that the user should not expect forward-chaining to take place on a term that can be rewritten for any reason (generally function expansion or application of rewrite rules). Formerly, the restriction was less severe: forward-chaining facts from a hypothesis could be used as long as the hypothesis was not rewritten to t. Thanks to Art Flatau for providing an example that led us to make this change; see the comments in source function rewrite-clause for details. The rewriter has been modified to work slightly harder in relieving hypotheses. Thanks to Eric Smith for providing an example that inspired the following, which illustrates the issue. Suppose we introduce functions foo and bar with the (non-local) properties shown below. (encapsulate (((foo *) => *) ((bar *) => *)) (local (defun foo (x) (declare (ignore x)) t)) (local (defun bar (x) (declare (ignore x)) t)) (defthm foo-holds (implies x (equal (foo x) t))) (defthm bar-holds-propositionally (iff (bar x) t))) Consider what happens when ACL2's rewriter is used to prove the following theorem. (thm (foo (bar y))) With ACL2's inside-out rewriting, (bar y) is first considered, but rewrite rule bar-holds-propositionally does not apply because the context requires preserving equality, not mere Boolean (iff) equivalence. Then the rewriter moves its attention outward and sees the term (foo (bar y)). It attempts to apply the rule foo-holds, in a context created by binding its variable x to the term (bar y). It then attempts to relieve the hypothesis x of rule foo-holds in that context. Before this change, ACL2 basically assumed that since rewriting was inside out, then (bar y) had already been rewritten as much as possible, so the rewrite of x in the aforementioned context (binding x to (bar y)) simply returned (bar y), and the attempt to relieve the hypothesis of foo-holds failed. The change is essentially for ACL2's rewriter to make a second pass through the rewriter when the attempt fails to rewrite a variable to t, this time using the fact that we are in a Boolean context. (We mention that source function rewrite-solidify-plus implements this idea, for those who want to dig deeply into this issue.) In our example, that means that the rewriter considers (bar y) in a Boolean context, where it may apply the rule bar-holds-propositionally to relieve the hypothesis successfully. When (set-non-linearp t) has been executed, non-linear-arithmetic can now be applied in some cases for which it previously was not. Thanks to Robert Krug for supplying this modification and to Julien Schmaltz for providing a motivating example. We modified the rewriter to avoid certain infinite loops caused by an interaction of the opening of recursive functions with equality reasoning. (This change is documented in detail in the source code, in particular functions rewrite-fncall and fnstack-term-member.) Thanks to Fares Fraij for sending us an example that led us to make this change. The :executable-counterpart of function hide is now disabled when ACL2 starts up. This removes an anomoly, for example that (thm (not (equal 1 (* (hide 0) a)))) succeeded while (thm (equal (foo (equal 1 (* (hide 0) a))) (foo nil))) failed. Now both fail. The theory *s-prop-theory* is no longer used by the proof-checker; it has been replaced by (theory 'minimal-theory. We have left the constant *s-prop-theory* defined in the source code in support of existing books, however. This change eliminates annoying theory warnings printed upon invocation of proof-checker commands s-prop, sl, and split. Terms are now kept in an internal form that avoids calls of primitive functions (built-ins without explicit definitions; see code for cons-term for details), in favor of the constants that result from evlaluating those calls. So for example, the internal form for (cons 1 2) is (quote (1 . 2)). This change was made at around the same time as changes in support of bind-free; see above. One consequence is that the splitting of goals into cases (technically, source function clausify and even more technically, source function call-stack) has been modified, which can cause subgoal numbers to change. *GUARD-RELATED CHANGES.* Guard-checking can now be caused to happen on recursive calls, where this was formerly not the case for :program mode functions and, perhaps more important, for :logic mode functions whose guards have not been verified. Moreover, a warning is printed when ACL2 does not rule out the exclusion of guard-checking on recursive calls. See *note SET-GUARD-CHECKING::. Thanks to David Rager for bringing this issue to our attention, and to Rob Sumners and the Univ. of Texas ACL2 seminar in general for their feedback. Guard violations are reported with less of the offending term hidden. Thanks to Jared Davis for suggesting that we look at this issue. *PROOF-CHECKER CHANGES.* We fixed the proof-checker so that diving works as you might expect for a macro call (op a b c) representing (binary-op a (binary-op b c)). In the past, if the current term was of the form (append t1 t2 t3), then (DV 3) (and 3) would dive to t3 even though the corresponding function call is (binary-append t1 (binary-append t2 t3)). This is still the case, but now this behavior holds for any macro associated with a function in binop-table (see *note ADD-BINOP::). Moreover, users can now write customized diving functions; see *note DIVE-INTO-MACROS-TABLE::, and also see books/misc/rtl-untranslate.lisp for example calls to add-dive-into-macro. Of course, the old behavior can still be obtained using the proof-checker's DIVE command; see *note PROOF-CHECKER-COMMANDS::. The runes command in the proof-checker now shows only the runes used by the most recent primitive or macro command (as shown by :comm), unless it is given a non-nil argument. Also, proof-checker command lemmas-used has been added as, in essence, an alias for runes. (The following two items are also mentioned above under "BUG FIXES.") Fixed a proof-checker bug that was preventing the use of the DV command (or a numerical command) on let expressions. Thanks to Bill Young for pointing out this problem. The theory *s-prop-theory* is no longer used by the proof-checker; it has been replaced by (theory 'minimal-theory. We have left the constant *s-prop-theory* defined in the source code in support of existing books, however. This change eliminates annoying theory warnings printed upon invocation of proof-checker commands s-prop, sl, and split. *SYSTEM-LEVEL CHANGES.* Fixed a problem with building ACL2 on CMUCL in some systems (source function save-acl2-in-cmulisp). Thanks to Bill Pase for bringing this to our attention. The installation instructions have been extended to include instructions for building on GCL in Mac OS X. Thanks to Jun Sawada and Camm Maguire. Initial pre-allocation of space has been updated for GCL to reflect more current GCL executables (we considered GCL 2.6.1-38). This can help avoid running out of memory for large ACL2 sessions. The main Makefile has been replaced by GNUmakefile, in order to enforce the use of GNU `make'. If you use another `make' program, you'll get an error message that may help you proceed. (GCL only) SGC is no longer turned on for GCL 2.6 sub-versions through 2.6.3 if si::*optimize-maximum-pages* is bound to T, due to an apparent issue with their interaction in those sub-versions. Also, we have eliminated preallocation for all versions after 2.6.1 because GCL doesn't need it (comments are in source function save-acl2-in-akcl). Thanks to Camm Maguire for excellent GCL help and guidance, and to Camm and Bob Boyer for useful discussions. We have removed support for so-called "small" images. Thus, :doc, :pe and :pc, verify-termination, and other commands are fully supported in ACL2 saved images. Because of this and other changes in the generation of the so-called "*1*" logical functions, related to guards (as described above in -GUARD-RELATED CHANGES", and related to the discussion of safe-mode in "BUG FIXES" above), image sizes have increased substantially. We no longer time or run "nice" the certification of individual books. The file books/Makefile-generic had done these by default, and some individual distributed and workshop book directories had Makefiles that did so as well. Thanks to Mike Thomas, who pointed out the lack of nice on some Windows systems (and we decided on this simple solution). Overall targets in books/Makefile still time their runs by default, and the partiular time program is now controlled by a Makefile variable. Failures during make certify-books or make regression now show up in the log as "**CERTIFICATION FAILED**", regardless of the operating system (as long as it supports `make'). Formerly, one searched for "**" but this did not appear in openMCL runs. We have eliminated "Undefined function" warnings that could occur in OpenMCL. *BOOK CHANGES.* Reconciled the definitions of firstn in book/misc/csort.lisp, books/bdd/bdd-primitives.lisp, books/ordinals/ordinal-definitions.lisp, and books/data-structures/list-defuns.lisp. Thanks to Ray Richards for bringing this issue to our attention. Distributed book books/misc/defpun now can handle stobjs where it did not previously. Thanks to John Matthews for bringing this issue to our attention. The "make" variable COMPILE_FLG in file books/Makefile-generic formerly only had an effect if there was a cert.acl2 file present. That oversight has been remedied. File "books/arithmetic/certify.lsp" was missing a certify-book command for "natp-posp". Thanks to John Cowles for noticing this deficiency and supplying a fix. (This file is of use to those who want to certify the "books/arithmetic/" books without using "make".) A few small changes have been made to "books/rtl/rel4". Small changes were made to books misc/symbol-btree and misc/rtl-untranslate. In particular, the definition of symbol-btreep was strengthened. We made a minor fix to books/ordinals/e0-ordinal.lisp, adding (verify-guards ob+) and hence (verify-guards ocmp) as well. This was necessitated by the fix prohibiting functions with guards verified from calling functions with guards not verified (see also the related discussion under "BUG FIXES" above). *MISCELLANEOUS CHANGES.* Further sped up processing of large mutual-recursion nests (extending what was done for Version_2.7), perhaps by a factor of two in some cases. As promised in Version_2.5 (see *note NOTE-2-5::), structured pathnames are no longer supported. So for example, the argument to include-book must now be a string constant. Some documentation has been improved, for stobjs thanks to suggestions by John Matthews and much of the rest thanks to feedback from Eric Smith. The function current-package is now available to users (it has been taken off the list of so-called "untouchables"). Thanks to Jared Davis for bringing this issue to our attention. The documentation for topic using-computed-hints-7 has been improved. Thanks to Doug Harper and Eric Smith for inspiring this improvement. We added several symbols to *acl2-exports*: cw, er, intern$, set-case-split-limitations, and set-difference-eq. Thanks to Jared Davis for suggesting most of these. Now, a table event that sets the value for a key, (table tbl key val :put), is redundant (see *note REDUNDANT-EVENTS::) when it does not change the value associated with an existing key of the table. In particular, define-pc-macro is now fully redundant when it does not change an existing proof-checker macro-command definition. Thanks to Bill Young for bringing the latter issue to our attention. The definitions of unused system functions ev-w and ev-w-lst have been deleted. ACL2 now prints a warning if a defpkg event introduces a package name with lower-case letters, since there is opportunity for later confusion in that case. Thanks to Frederic Peschanski for bringing this problem to our attention and Sandip Ray for encouragement. ACL2 now works in Version 19 of CMU Common Lisp. The function sys-call has been modified so that for ACL2 built on Allegro Common Lisp in Unix or Linux, the existing environment is used. Thanks to Erik Reeber for bringing this issue to our attention. The function disabledp can now be given a macro name that has a corresponding function; see *note MACRO-ALIASES-TABLE::. Also, disabledp now has a guard of t but causes a hard error on an inappropriate argument.  File: acl2-doc-emacs.info, Node: NOTE-2-9(R), Next: NOTE-2-9-1, Prev: NOTE-2-9, Up: RELEASE-NOTES NOTE-2-9(R) ACL2 Version 2.9(r) (October, 2004) Notes No changes have been made for support of non-standard analysis, other than a minor modification or two in books/nonstd/ books. Please also see *note NOTE-2-9:: for changes to Version_2.9 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-2-9-1, Next: NOTE-2-9-2, Prev: NOTE-2-9(R), Up: RELEASE-NOTES NOTE-2-9-1 ACL2 Version 2.9.1 (December, 2004) Notes (GCL only) A bug in symbol-package-name has been fixed that could be exploited to prove nil, and hence is a soundness bug. Thanks to Dave Greve for sending us an example of a problem with defcong (see below) that led us to this discovery. ACL2 now warns when defcong specifies equal as the first equivalence relation, e.g., (defcong equal iff (member x y) 2). The warning says that the rule has no effect because equal already refines all other equivalence relations. Formerly, this caused an error unless :event-name was supplied (see *note DEFCONG::), and in fact the error was a nasty raw Lisp error on GCL platforms due to some mishandling of packages by ACL2 that has been fixed (see the paragraph about symbol-package-name above). Thanks to Dave Greve for sending a helpful example in his report of this problem. (GCL only) The build process was broken for GCL 2.6.0 (and perhaps some earlier versions), and has been fixed. Thanks to Jose Luis Ruiz-Reyna for bringing this problem to our attention. (GCL only) We have increased the hole size to at least 20% of max-pages, which may eliminate some garbage collection at the expense of larger virtual memory (not larger resident memory or larger image). Thanks to Camm Maguire for helpful explanations on this topic. We have clarified the guard warning message that is printed during evaluation of recursively-defined functions whose guards have not been verified, for example: ACL2 Warning [Guards] in TOP-LEVEL: Guard-checking may be inhibited on some recursive calls of executable counterparts (i.e., in the ACL2 logic), including perhaps EVENLP. To check guards on all recursive calls: (set-guard-checking :all) To leave behavior unchanged except for inhibiting this message: (set-guard-checking :nowarn) And, ACL2 no longer prints that message when the guard was unspecified for the function or was specified as T. Thanks to Serita Nelesen for bringing the latter issue to our attention. Finally, ACL2 now prints such a warning at most once during the evaluation of any top-level form; thanks to Bill Young for pointing out this issue. The function verbose-pstack has been enhanced to allow specified prover functions *not* to be traced. See *note VERBOSE-PSTACK::. Added lp, wet, and set-non-linearp to *acl2-exports*, and hence to the "ACL2-USER" package. The distributed book books/arithmetic-3/bind-free/integerp.lisp has been modified in order to prevent potential looping; specifically, the definition of function reduce-integerp-+-fn-1. Thanks to Robert Krug for providing this change. A small improvement was made in the wet failure message when the error occurs during translation to internal form. Thanks to Jared Davis for pointing out the obscurity of some wet error messages. We have improved ACL2's evaluation mechanism for the function bad-atom<=, which now is specified to return nil if neither argument is a so-called "bad atom" (as recognized by function bad-atom). The following events had caused a hard error, for example. (We're sorry that bad-atom and bad-atom<= are not documented, but we also consider it unlikely that anyone needs such documentation; otherwise, please contact the implementors.) (defun foo (x y) (declare (xargs :guard t)) (bad-atom<= x y)) (defun bar (x y) (declare (xargs :guard t)) (foo x y)) (thm (equal (bar 3 4) 7)) We have also changed the guard on alphorder to require both arguments to be atoms. For forms (local x) that are skipped during include-book, or during the second pass of certify-book or encapsulate, ACL2 had nevertheless checked that x is a legal event form. This is no longer the case. The proof-checker now does non-linear arithmetic when appropriate. It had formerly ignored set-non-linearp executed in the ACL2 command loop. Incremental releases are now supported. See *note VERSION:: and {obsolete after Version 4.3} set-tainted-okp. Thanks to Hanbing Liu for discovering a flaw in our original design. The pattern-matching algorithm for :rewrite rules has been made slightly more restrictive, thanks to a suggestion and examples from Robert Krug. For example, previously one could get an infinite loop as follows. (defstub foo (x) t) (defaxiom foo-axiom (equal (foo (+ 1 x)) (foo x))) (thm (foo 0)) ; or replace 0 by any integer! That is because the term (foo 0) was considered to match against the pattern (foo (+ 1 x)), with x bound to -1. While such matching is sound, it leads to an infinite loop since it allows foo-axiom to rewrite (foo 0) to (foo -1), and then (foo -1) to (foo -2), and so on. The fix is to insist that the new value, in this case -1, is no larger in size according to acl2-count than the old value, in this case 0. Since that test fails, the match is considered to fail and the loop no longer occurs. An analogous fix has been made for multiplication, where now we only match when the new term is still a non-zero integer. That change avoids a loop here. (defstub foo (x) t) (defaxiom foo-axiom (equal (foo (* 2 x)) (foo x))) (thm (foo 0)) ; or try (thm (foo 4)) Added macro find-lemmas in books/misc/find-lemmas.lisp (see brief documentation there) for finding all lemmas that mention all function symbols in a given list. :Restrict hints now work for :definition rules, though they continue to be ignored by the preprocessor and hence you may want to use :do-not '(preprocess) with any restrict hints. Thanks to John Matthews for pointing out the lack of support for :definition rules in :restrict hints. Some books have been updated. In particular, there is a new directory books/workshops/2004/ in workshops distribution, for the 2004 ACL2 workshop. There is also a new version of Jared Davis's ordered sets library, formerly in books/finite-set-theory/osets-0.81/ but now in books/finite-set-theory/osets/. Fixed a bug in the (under-the-hood) raw Lisp definition of defchoose, which had been causing a warning in CMU Common Lisp. [Technical improvements related to the use of "make dependencies" for certifying distributed books:] File books/Makefile-generic now does a better job with "make dependencies," specifically with respect to handling *.acl2 files and handling include-book commands with :dir :system. Regarding the latter, suppose for example that book basic.lisp contains the line: (include-book "arithmetic/top-with-meta" :dir :system) Then make dependencies would generate the following line: basic.cert: $(ACL2_SRC_BOOKS)/arithmetic/top-with-meta.cert Thus, if :dir :system is used with include-book, the corresponding Makefile should define the variable ACL2_SRC_BOOKS. A standard Makefile header for a books directory could thus be as follows. # The following variable should represent the ACL2 source directory. It is the # only variable in this Makefile that may need to be edited. ACL2_SRC = ../../../../../.. ACL2_SRC_BOOKS = $(ACL2_SRC)/books include $(ACL2_SRC_BOOKS)/Makefile-generic ACL2 = $(ACL2_SRC)/saved_acl2 Finally, the "-s" flag may now be omitted when running "make dependencies."  File: acl2-doc-emacs.info, Node: NOTE-2-9-2, Next: NOTE-2-9-3, Prev: NOTE-2-9-1, Up: RELEASE-NOTES NOTE-2-9-2 ACL2 Version 2.9.2 (April, 2005) Notes Also see *note NOTE-2-9-1:: for other changes since the last non-incremental release (Version_2.9). There was a bug in non-linear arithmetic (see *note NON-LINEAR-ARITHMETIC::) that caused the following error: ACL2 !>(include-book "rtl/rel4/lib/top" :dir :system) .... ACL2 !>(set-non-linearp t) T ACL2 !>(thm (implies (and (bvecp a 77) (bvecp b 50)) (bvecp (fl (/ (* a b) (expt 2 23))) 104)) :hints (("Goal" :in-theory (enable bvecp)))) [Note: A hint was supplied for our processing of the goal above. Thanks!] By the simple :definition BVECP, the :executable-counterparts of EXPT and UNARY-/ and the simple :rewrite rule ASSOCIATIVITY-OF-* we reduce the conjecture to Goal' (IMPLIES (AND (INTEGERP A) (<= 0 A) (< A 151115727451828646838272) (INTEGERP B) (<= 0 B) (< B 1125899906842624)) (BVECP (FL (* A B 1/8388608)) 104)). HARD ACL2 ERROR in VARIFY: This should not have happened. The supposed variable, '1/8388608, is instead a constant. ACL2 !> Thanks to Robert Krug for providing a fix for the above error. Guard-checking was being inhibited (since v2-9) for calls of built-in primitives on explicit values, e.g., (car 3). This has been fixed. Guard-related warnings could be printed during proofs (this bug was introduced in Version_2.9.1). These warnings have been eliminated. Compound-recognizer rules natp-compound-recognizer and posp-compound-recognizer are now built into ACL2 for predicates natp and posp, and hence have been deleted from book natp-posp.lisp (where they were called natp-cr and posp-cr, respectively). The function file-clock-p, which recognizes a component of the ACL2 state, is now defined using natp instead of integerp. Thanks to Jared Davis for suggesting this change. (Technical explanation about functions in ACL2 source file axioms.lisp: With a file-clock of -1, the call of make-input-channel in open-input-channel will create a channel that can't be closed; see the guard of close-input-channel.) (Allegro CL users only) Support is now provided for building an Allegro CL application, provided you have an Allegro CL dynamic runtime license. (Our belief is that with such a license, many users can use the same application, rather than each user needing a separate license.) See new GNUmakefile target allegro-app and file build-allegro-exe.cl for more information. The new home page now contains a link to a new page other-releases.html, which contains information about other ACL2 releases. (This is in one's local home page, but may not show up on the central ACL2 home page until the next non-incremental release.) Thanks to Warren Hunt for suggesting this addition. We thank Erik Reeber for suggesting a solution to output redirection using sys-call, which we have described at the end of its documentation. A new documentation topic fixes the flawed argument for conservativity of the defchoose event that appears in Appendix B of Kaufmann and Moore's paper, "Structured Theory Development for a Mechanized Logic" (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203). See *note CONSERVATIVITY-OF-DEFCHOOSE::. Thanks to John Cowles and Ruben Gamboa for helpful feedback on drafts of this note. The solution to exercise 6.15 in books/textbook/chap6/solutions.txt has been fixed. Thanks to Aaron Smith for pointing out the problem. A new documentation topic defun-sk-example gives a little more help in using defun-sk effectively. Thanks to Julien Schmaltz for presenting this example as a challenge. (GCL only) There is now a way to speed up GCL builds of ACL2, at the cost of perhaps a percent or so in performance of the resulting image. Using `make' one supplies the following. LISP='gcl -eval "(defparameter user::*fast-acl2-gcl-build* t)" Various makefiles have been improved in several ways. (1) Parallel book certification, using GNU make's -j option, can be used. (2) Book certifications now stops at the first failure if books/Makefile or books/Makefile-generic is used, and returns non-zero exit status. However, the various make targets in the ACL2 source directory (regression, certify-books, etc.) still continue past failures unless you provide ACL2_IGNORE=' ' on the `make' command line. (3) The build process has been modified (file GNUmakefile) so that it stops upon a failed compile or a failed initialization. (4) The automatic dependency generation (from "make dependencies" has been improved so that commands of the form (ld "my-book.lisp") in .acl2 files cause the appropriate depedencies to be generated. Thanks to comments from several users that led to the above Makefile improvements: Ray Richards, Doug Harper, and the Rockwell ACL2 users for (1) and (2) (and inspiring (4)), and David Rager for (2) and (3). In particular, Doug Harper sent a replacement for the .date mechanism, which was interfering with make -n; so, these files are no longer written. A mechanism has been added for saving output. In particular, you can now call ld on a file with output turned off, for efficiency, and yet when a proof fails you can then display the proof attempt for the failed (last) event. See *note SET-SAVED-OUTPUT::. Another new command -- see *note SET-PRINT-CLAUSE-IDS:: -- causes subgoal numbers to be printed during proof attempts when output is inhibited. Documentation has been added for using ACL2's makefile support to automate the certification of collections of books. See *note BOOKS-CERTIFICATION-CLASSIC::. Fixed a bug in sys-call-status that was causing hard Lisp errors. Improved cw-gstack to allow a :frames argument to specify a range of one or more frames to be printed. see *note CW-GSTACK::. Fixed a bug in proof-checker command forwardchain. Thanks to Ming-Hsiu Wang for bringing this bug to our attention. We have provided a mechanism for saving an executable image. See *note SAVING-AND-RESTORING:: and see *note SAVE-EXEC::. We have eliminated obsolete functions note-lib and make-lib. Modified the ground-zero theory so that it contains all of the built-in rules (in ACL2 source file axioms.lisp). It had formerly failed to include rules from some definitions and theorems near the end of axioms.lisp. A new event, set-enforce-redundancy, allows the enforcement of defthm, defun, and most other events during book development. See *note SET-ENFORCE-REDUNDANCY::. A bug has been fixed that had allowed deftheory events to cause a hard Lisp error when calling union-theories on ill-formed theories after, for example: :set-guard-checking nil (in-theory (union-theories '((:rewrite no-such-rule)) (current-theory 'ground-zero))) The handling of guard checking has been modified somewhat in a way that should only very rarely affect users. (An "Essay on Guard Checking" in the ACL2 source code explains this point to anyone interested in implementation details.) (GCL ONLY) Removed the -dir setting in the ACL2 wrapper script for GCL. This should generally have no effect for most users, but it eliminates a potential source of error down the road. Several interesting new definitions and lemmas have been added to the rtl library developed at AMD, and incorporated into books/rtl/rel4/lib/. Other book changes include a change to lemma truncate-rem-elim in books/ihs/quotient-remainder-lemmas.lisp, as suggested by Jared Davis. The macro real/rationalp may now be referred to in in-theory events and hints, thanks to a new add-macro-alias event. Thanks to Jared Davis for this suggestion. ACL2 terms of the form (if p 'nil 't) are now printed as (not p), where in some setting they had been printed as (and (not p) t). Thanks to Robert Krug for this improvement. (GCL ONLY) Added profiling support, based heavily on code supplied by Camm Maguire. See file save-gprof.lsp for instructions. Thanks to Camm, and also to David Hardin for inspiring this addition. Added support for preprocessing before printing (untranslating) a term. See *note USER-DEFINED-FUNCTIONS-TABLE::, in particular the discussion of untranslate-preprocess. Thanks to Jared Davis for inspiring this addition, and for providing a book that takes advantage of it (books/misc/untranslate-patterns.lisp). The documentation has been improved for explaining how runes are assigned; see *note RUNE::. Thanks to Robert Krug for pointing out inaccuracies in the existing documentation.  File: acl2-doc-emacs.info, Node: NOTE-2-9-3, Next: NOTE-2-9-4, Prev: NOTE-2-9-2, Up: RELEASE-NOTES NOTE-2-9-3 ACL2 Version 2.9.3 (August, 2005) Notes Also see *note NOTE-2-9-1:: and see *note NOTE-2-9-2:: for other changes since the last non-incremental release (Version_2.9). We fixed a soundness bug that exploited the ability to define :program mode functions that are improperly guarded, and then to use those functions in defconst forms. The fix is to evaluate defconst forms using the same "safe-mode" that is used in macroexpansion (see *note GUARDS-AND-EVALUATION::). Here is a proof of nil that succeeded in Allegro Common Lisp (but not, for example, GCL). See also a long comment in source function defconst-fn for an example that does not require the use of :set-guard-checking. :set-guard-checking nil ; execute before certifying the book below (in-package "ACL2") (encapsulate () (local (defun f1 () (declare (xargs :mode :program)) (char-upcase (code-char 224)))) (local (defconst *b* (f1))) (defun f1 () (char-upcase (code-char 224))) (defconst *b* (f1)) (defthm bad (not (equal *b* (code-char 224))) :rule-classes nil)) (defthm ouch nil :hints (("Goal" :use bad)) :rule-classes nil) We fixed a soundness hole due to the fact that the "LISP" package does not exist in OpenMCL. We now explicitly disallow this package name as an argument to defpkg. Thanks to Bob Boyer and Warren Hunt for bringing an issue to our attention that led to this fix. ACL2 now requires all package names to consist of standard characters (see *note STANDARD-CHAR-P::, none of which is lower case. The reason is that we have seen at least one lisp implementation that does not handle lower case package names correctly. Consider for example the following raw lisp log (some newlines omitted). >(make-package "foo") #<"foo" package> >(package-name (symbol-package 'FOO::A)) "foo" >(package-name (symbol-package '|FOO|::A)) "foo" > Distributed book books/textbook/chap10/compiler, as well as workshop books in directory books/workshops/2004/cowles-gamboa/support/, were modified to accommodate the above change. Added newline, add-to-set-eql, the-fixnum, and the-fixnum! to *acl2-exports*. Thanks to Jared Davis for bringing these to our attention. Added a line to acl2.lisp to support CMUCL running on Mac OSX, thanks to a suggestion from Fabricio Chalub Barbosa do Rosario. The executable scripts for saved ACL2 images now include $*, so that command-line arguments will be passed along. (For GCL profiling only) Fixed a colon (:) that should have been a semicolon (;) in file save-gprof.lsp. Thanks to David Hardin for pointing out this bug. The documentation for :elim rules has been expanded and improved, thanks to useful feedback from Hanbing Liu. Fixed a bug in the guard for function include-book-dir. For those who want to experiment with an alternate implementation of mv and mv-let, there is now support for under-the-hood implementation of these in terms of raw Lisp functions values and multiple-value-bind, respectively. The regression suite has seen about a 10% speed-up in Allegro CL and about an 8% slowdown in GCL for builds with this change. See the makefile (GNUmakefile) for examples of how to build ACL2 by including the feature, :acl2-mv-as-values. Source file init.lsp has been renamed to init.lisp in support of this change (technical detail: otherwise GCL loads the init file too soon, before its -eval argument is evaluated). Thanks to David Rager for inspiring this change, by pointing out the problematic use of globals by the existing mv implementation from the standpoint of supporting parallel evaluation. This capability is experimental: there is likely to be some remaining work to be done on it. A change related to the one just above is that we now limit the maximum number of arguments to any call of mv to 32. Thanks to Bob Boyer for raising a question that lead to this change. Eliminated some compiler warnings in OpenMCL. In the rtl library (books/rtl/rel4/), functions bits and setbits have had their guards improved (as they had been too restrictive, especially for setbits). A new function time$ permits timing of forms, by using (under the hood) the host Common Lisp's time utility. We fixed an infinite loop that could occur during destructor elimination (see *note ELIM::). Thanks to Sol Swords to bringing this to our attention and sending a nice example, and to Doug Harper for sending a second example that we also found useful. The method of speeding up GCL-based builds (see *note NOTE-2-9-2::) has changed slightly from Version_2.9.2. Now, in the `make' command: LISP='gcl -eval "(defparameter user::*fast-acl2-gcl-build* t)" We improved the pretty-printer's handling of keywords. For example, before this change one might see the following printed by ACL2. (MODIFY TH S :KEY1 VAL1 :KEY2 (IF (IF X Y Z) AAAAAAAAAA BBBBBBB)) Now, the above might print as follows. Notice that we have avoided breaking after a keyword (see *note KEYWORDP::) that is preceded by other forms on the same line. (MODIFY TH S :KEY1 VAL1 :KEY2 (IF (IF X Y Z) AAAAAAAAAA BBBBBBB)) See *note NOTE-2-9-3-PPR-CHANGE:: for a detailed discussion of this change. (GCL ONLY) Evaluation in a break is no longer inhibited by ACL2 when built on top of GCL, so GCL now matches other Common Lisps in this respect. For ACL2 built on most host Common Lisps, you will see the string [RAW LISP] in the prompt, at least at a break, to emphasize that one is inside a break and hence should probably quit from the break. See *note BREAKS::. Jared Davis suggested improvements to lemmas len-update-nth (in source file axioms.lisp) and append-true-listp-type-prescription (in books/meta/term-defuns.lisp), which have been incorporated. The former required a change in books/workshops book 2004/ruiz-et-al/support/q-dag-unification.cert, which has been made. The proof-checker command rewrite allows further binding of free variables in hypotheses, with new optional argument instantiate-free. Proof-checker command show-rewrites (sr) gives corresponding additional information. Documentation for these commands has been improved; see *note PROOF-CHECKER-COMMANDS::. Thanks to John Matthews and Bill Young for suggestions and feedback leading to these improvements. Fixed downcase printing so that the package name of a symbol is also downcased. For example, after execution of (defpkg "FOO" nil) and (set-acl2-print-case :downcase), 'foo::ab will print back as the same, rather than as 'FOO::ab. It is now possible to control the output so that numbers are printed in binary, octal, or hex, though the default is still radix 10. See *note SET-PRINT-BASE::. Note that in support of this change, built-in functions explode-nonnegative-integer and explode-atom now take an extra print-base argument. Different support for radix conversion may be found in a book newly contributed by Jun Sawada, books/misc/radix.lisp. Built-in axiom car-cdr-elim is now only an :elim rule. It was formerly both an :elim rule and a :rewrite rule. A new rule, cons-car-cdr, takes the place of the old :rewrite rule, but is instead a hypothesis-free rule that can cause a case split (see source file axioms.lisp). Thanks to Jared Davis for suggesting this change. Lemmas about alphorder (alphorder-reflexive, alphorder-transitive, alphorder-anti-symmetric, and alphorder-total) are now available. (They had been local in source file axioms.lisp.) Thanks to Serita Nelesen for bringing this issue to our attention. ACL2 has, for some time, printed a space in the event summary after the open parenthesis for a defthm event, in order to ease backward searching for the original form, for example (defthm bar ...): Form: ( DEFTHM BAR ...) The intention was that this extra space should be printed for every event form; but it was missing in some cases, for example, for verify-guards. This has been fixed. In analogy to include-book, now ld takes the (optional) keyword argument :dir. Thanks to Jared Davis for providing an implementation of this feature and to Eric Smith and Jeff Marshall for requesting this feature. We fixed a bug in include-book that could cause an error when redefinition is on, for example: (set-ld-redefinition-action '(:warn! . :overwrite) state) (include-book "/u/acl2/books/arithmetic/top") The behavior of include-book now matches the documentation: handling of compiled files for uncertified books will follow the same rules as for certified books. In particular, if you create an object file in raw Lisp for some book, then including that book will load that object file. Thanks to Jared Davis for bringing this issue to our attention. New documentation explains the interaction of redefinition and redundancy. See *note REDUNDANT-EVENTS:: -- the "Note About Unfortunate Redundancies" is new. Thanks to Grant Passmore for providing examples that led us to write this additional documentation. Solutions to exercises in "How To Prove Theorems Formally" (`http://www.cs.utexas.edu/users/moore/publications/how-to-prove-thms') are now available in distributed book books/misc/how-to-prove-thms.lisp. Also in that directory may be found a new book hanoi.lisp that contains a solution to the Towers of Hanoi problem. * Menu: * NOTE-2-9-3-PPR-CHANGE:: change in pretty-printing for ACL2 Version_2.9.3  File: acl2-doc-emacs.info, Node: NOTE-2-9-3-PPR-CHANGE, Prev: NOTE-2-9-3, Up: NOTE-2-9-3 NOTE-2-9-3-PPR-CHANGE change in pretty-printing for ACL2 Version_2.9.3 We have improved pretty-printing in ACL2 Version_2.9.3 to handle keywords a little differently. To see a discussion of the basics of this change, see *note NOTE-2-9-3::. In this note we describe it in considerable detail. Those who wish to understand the ACL2 pretty-printer's implementation can now find considerably more comments on it in the source code. In this note, we do not focus on the implementation. Rather, we motivate the change and show how the improved prettyprinter performs. Why do we want better keyword handling? Imagine a macro that builds a new state from an old state by changing the values in the affected fields, leaving everything else unchanged. One could write (modify th s :key1 val1 :key2 val2 :key3 val3) where the three keys identify fields in the state. To make it easier to read new concrete states, we may have a function that prints them "relative" to a given base state, expressing the new state as a modification of the given base state. So we may find ourselves prettyprinting modify forms like that above. The previous prettyprinter will sometimes print the form above as follows. (modify th s :key1 val1 :key2 val2 :key3 val3) This can be unpleasant to read, because of the way :key1 and val1 are separated. Here is an example of the old prettyprinter and the new one, both printing an expression from the ACL2 source code in a width of 40: Old: (ADD-TO-TAG-TREE 'ASSUMPTION (MAKE ASSUMPTION :TYPE-ALIST TYPE-ALIST :TERM TERM :REWRITTENP REWRITTENP :IMMEDIATEP IMMEDIATEP :ASSUMNOTES (LIST (MAKE ASSUMNOTE :CL-ID NIL :RUNE RUNE :TARGET TARGET))) TTREE) New: (ADD-TO-TAG-TREE 'ASSUMPTION (MAKE ASSUMPTION :TYPE-ALIST TYPE-ALIST :TERM TERM :REWRITTENP REWRITTENP :IMMEDIATEP IMMEDIATEP :ASSUMNOTES (LIST (MAKE ASSUMNOTE :CL-ID NIL :RUNE RUNE :TARGET TARGET))) TTREE) Basically the change we made forces the prettyprinter to print each :key on a new line unless they all fit on a single line. So we would now get either (modify th s :key1 val1 :key2 :val2 :key3 val3) or (modify th s :key1 val1 :key2 val2 :key3 val3) Furthermore, we fixed it so that if val1 (say) is a big s-expression we may still print it on the same line as its key. The old prettyprinter enforced the rule that if you wanted to print (foo a b) and b gets broken up into several lines, then it has to start on a new line. Thus, we'd never print (foo a (bbb (mum x))) but would print instead (foo a (bbb (mum x))) Now, if a is a keyword, we can print the first way. So here are some nice examples of prettyprinted keyword forms. All of these are printed for a page of width 40. <-- 40 chars -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (MODIFY TH S :KEY1 V1 :KEY2 V2) (MODIFY TH S :KEY1 V1 :KEY2 V2 :KEY3 V3) (MODIFY TH S1 ; Because of the extra char :KEY1 V1 ; in S1 the flat size exceeds :KEY2 V2 ; 40 and we break it. :KEY3 V3) The old ppr would have printed this as: (MODIFY TH S1 :KEY1 V1 :KEY2 V2 :KEY3 V3) Returning to new examples: <-- 40 chars -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (MODIFY TH S :KEY1 (IF (IF X Y Z) AAAA BBBB) :KEY2 VAL2 :KEY3 VAL3) Now we extend AAAA and BBBB by one char each, so it would overflow the right margin if printed as above, and we get: <-- 40 chars -> xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx (MODIFY TH S :KEY1 (IF (IF X Y Z) AAAAX BBBBX) :KEY2 VAL2 :KEY3 VAL3) If we make these names even longer we force the value off the line containing :key1: (MODIFY TH S :KEY1 (IF (IF X Y Z) AAAAXXXXX BBBBXXXXX) :KEY2 VAL2 :KEY3 VAL3) Here are some examples from the ACL2 source code, printed in 40 characters: (DEFTHM ALPHORDER-ANTI-SYMMETRIC (IMPLIES (AND (NOT (CONSP X)) (NOT (CONSP Y)) (ALPHORDER X Y) (ALPHORDER Y X)) (EQUAL X Y)) :HINTS (("Goal" :IN-THEORY (UNION-THEORIES '(STRING< SYMBOL-<) (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY)) :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X) (S2 Y)) (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y)) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C X))))) :RULE-CLASSES ((:FORWARD-CHAINING :COROLLARY (IMPLIES (AND (ALPHORDER X Y) (NOT (CONSP X)) (NOT (CONSP Y))) (IFF (ALPHORDER Y X) (EQUAL X Y))) :HINTS (("Goal" :IN-THEORY (DISABLE ALPHORDER)))))) Here is that same one, printed in a width of 60. (DEFTHM ALPHORDER-ANTI-SYMMETRIC (IMPLIES (AND (NOT (CONSP X)) (NOT (CONSP Y)) (ALPHORDER X Y) (ALPHORDER Y X)) (EQUAL X Y)) :HINTS (("Goal" :IN-THEORY (UNION-THEORIES '(STRING< SYMBOL-<) (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY)) :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X) (S2 Y)) (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y)) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C X))))) :RULE-CLASSES ((:FORWARD-CHAINING :COROLLARY (IMPLIES (AND (ALPHORDER X Y) (NOT (CONSP X)) (NOT (CONSP Y))) (IFF (ALPHORDER Y X) (EQUAL X Y))) :HINTS (("Goal" :IN-THEORY (DISABLE ALPHORDER)))))) Just for comparison, here is the above printed in 60 columns by the old prettyprinter. (DEFTHM ALPHORDER-ANTI-SYMMETRIC (IMPLIES (AND (NOT (CONSP X)) (NOT (CONSP Y)) (ALPHORDER X Y) (ALPHORDER Y X)) (EQUAL X Y)) :HINTS (("Goal" :IN-THEORY (UNION-THEORIES '(STRING< SYMBOL-<) (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY)) :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X) (S2 Y)) (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y)) (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C X))))) :RULE-CLASSES ((:FORWARD-CHAINING :COROLLARY (IMPLIES (AND (ALPHORDER X Y) (NOT (CONSP X)) (NOT (CONSP Y))) (IFF (ALPHORDER Y X) (EQUAL X Y))) :HINTS (("Goal" :IN-THEORY (DISABLE ALPHORDER)))))) Of course, given that you cannot tell for sure whether the keywords you're seeing are part of a keyword/value parameter list or part of some constant containing random keywords, the prettyprinter can't solve the problem perfectly. We just tried to make it work nicely on well-formed keyword/value parameter lists. For example, here is a form printed by the each prettyprinter. Old: (MEMBER X '(:MONDAY :MON :TUESDAY :TUES :WEDNESDAY :WED :THURSDAY :THURS :FRIDAY :FRI :SATURDAY :SAT :SUNDAY :SUN)) New: (MEMBER X '(:MONDAY :MON :TUESDAY :TUES :WEDNESDAY :WED :THURSDAY :THURS :FRIDAY :FRI :SATURDAY :SAT :SUNDAY :SUN)) The new way is not how one would print it by hand! But then, neither is the old way.  File: acl2-doc-emacs.info, Node: NOTE-2-9-4, Next: NOTE-2-9-5, Prev: NOTE-2-9-3, Up: RELEASE-NOTES NOTE-2-9-4 ACL2 Version 2.9.4 (February, 2006) Notes Also see *note NOTE-2-9-1::, see *note NOTE-2-9-2::, and see *note NOTE-2-9-3:: for other changes since the last non-incremental release (Version_2.9). A soundness bug has been fixed that was due to inadequate checking of :meta rules in the presence of local events. Specifically, a local defevaluator event is insufficient for supporting a :meta rule (an example is shown in source function chk-acceptable-rules). Thanks to Dave Greve and Jared Davis for bringing this bug to our attention, by sending a proof of nil that exploited this bug. The fix is to check legality of :meta rules even when skipping proofs during an include-book event or the second pass of an encapsulate event. Fixed problem with parallel make for workshop books by adding a dependency line to books/workshops/2003/Makefile. Default hints (see *note SET-DEFAULT-HINTS::) no longer prevent the use of :INSTRUCTIONS (see *note PROOF-CHECKER::). Thanks to Jared Davis for pointing out this problem. New functions remove-eq and remove-equal have been defined, in analogy to remove. These two symbols have also been added to *acl2-exports*. Thanks to David Rager for pointing out that remove-equal was missing. Moreover, the definitions of delete1-eq and delete1-equal have been eliminated. Function remove1-eq, now in :logic mode in source file axioms.lisp, serves in place of delete1-eq, with corresponding new function definitions for remove1 and remove1-equal. The symbol assert$ has been added to *acl2-exports*. Thanks to Jared Davis for the suggestion. Added SBCL support. Thanks to Juho Snellman for significant assistance with the port. Thanks to Bob Boyer for suggesting the use of feature :acl2-mv-as-values with SBCL, which can allow thread-level parallelism in the underlying lisp; we have done so when feature :sb-thread is present. We have continued to incorporate suggestions for wording improvements in documentation and error messages. Thanks to all who send these suggestions, especially to Eric Smith, who has suggested the vast majority of them. Made a small improvement to errors and warnings caused on behalf of set-enforce-redundancy, to indicate when an event of the same name already exists. Fixed a bug in books/misc/rtl-untranslate.lisp that was causing a guard violation when adding a new entry for an existing key. Fixed a bug in translation to internal form that caused defun-sk and defchoose to have difficulties handling ignored variables in let forms. Thanks to Sandip Ray to bringing this issue to our attention with a helpful example. The form (push :acl2-mv-as-values *features*) has been added in source file acl2-init.lisp for SBCL and OpenMCL only, in order to support parallel execution (looking to the future...). Default-hints (see *note SET-DEFAULT-HINTS::) were being ignored inside the proof-checker, but no longer. Thanks to John Erickson for bringing this problem to our attention and providing a simple example of it. Modified the TAGS "make" target (specifically, function make-tags) so that it is gracefully skipped if the etags program is not found. Thanks to David Rager for pointing out this issue. Sandip Ray has re-worked the supporting materials for his ACL2 Workshop 2003 talk (originally with John Matthews and Mark Tuttle), to run in a few minutes. The result is in workshops/2003/ray-matthews-tuttle/support/ and is included in the full ACL2 regression suite. Thanks, Sandip. Debian releases of ACL2 had created superfluous .cert.final files when certifying books. This has been fixed; thanks to Jared Davis for noticing this problem. Jared Davis has pointed out that "If you add a :backchain-limit-lst 0 to a rewrite rule whose hypotheses are all forced, then ACL2 `really assumes them' without trying to relieve them right there through rewriting." Relevant documentation has been added for :backchain-limit-lst; see *note RULE-CLASSES::. A new version of the rtl library has been included in books/rtl/rel5/. Thanks to David Russinoff for contributing hand proofs for the new lemmas, and to Matt Kaufmann for carrying out their mechanization. Fixed a bug in save-exec that was failing to set the initial cbd according to the current directory when starting up ACL2. Thanks to Camm Maguire for bringing our attention to this problem. Variables introduced during let* abstraction are now in the current package. Thanks to Jared Davis for suggesting such a change. See *note SET-LET*-ABSTRACTIONP::. It is now allowed for two definitions to be considered the same from the standpoint of redundancy (see *note REDUNDANT-EVENTS::) when one specifies a :guard of t and the other has no explicit :guard (hence, the guard is implicitly t). Thanks to Jared Davis for bringing this issue to our attention. (For users of emacs/emacs-acl2.el) There have been a few enhancements to distributed file emacs/emacs-acl2. el (skip this paragraph if you don't use that file): o Control-t q continues to compare windows ignoring whitespace, but now, a prefix argument can be given to control case is also ignored (ignore case if positive, else use case). o Control-t Control-l has been defined to be similar to Control-t l, except that proofs are skipped and output is suppressed. o Control-t u has been defined to print, to the shell buffer, a :ubt! form for the command containing the cursor. o Control-t Control-f buries the current buffer. o Meta-x new-shell now puts the new shell buffer in shell-mode (thanks to David Rager for noticing this issue). Linear arithmetic has been modified so that we do not generate the equality (equal term1 term2) from the pair of inequalities (<= term1 term2) and (<= term2 term1) in the case that we would have to force both term1 and term2 to be acl2-numberps. Thanks to Dave Greve for providing a motivating example and to Robert Krug for providing a fix. The event delete-include-book-dir had not been allowed inside books and encapsulate forms. This was an oversight, and has been fixed. Sandip Ray has contributed a new library of books to support proofs of partial and total correctness of sequential programs based on assertional reasoning, in books/symbolic/. This work is based on the paper J. Matthews, J S. Moore, S. Ray, and D. Vroon, "A Symbolic Simulation Approach to Assertional Program Verification," currently in draft form. In particular, the books include the macro defsimulate, which automatically transforms inductive assertion proofs of correctness of sequential programs to the corresponding interpreter proofs. See the README in that directory. We have changed the implementation of :dir :system for ld and include-book. This change will not affect you if you build an ACL2 executable in the normal manner, leaving in place the books/ subdirectory of the source directory; nor will it affect you if you download a GCL Debian binary distribution. The change is that if environment variable ACL2_SYSTEM_BOOKS is set, then it specifies the distributed books directory, i.e., the directory determined by :dir :system. You may find it convenient to set this variable in your ACL2 script file (typically, saved_acl2). If it is set when you build ACL2, the generated script for running ACL2 will begin by setting ACL2_SYSTEM_BOOKS to that value. Thanks to various people who have discussed this issue, in particular Jared Davis who sent an email suggesting consideration of the use of an environment variable, and to Eric Smith who helped construct this paragraph. (Note that this use of ACL2_SYSTEM_BOOKS replaces the use of ACL2_SRC_BOOKS described previously; see *note NOTE-2-9-1::.) ACL2 now automatically deletes files TMP*.lisp created during the build process and created by :comp. If you want these to be saved, evaluate (assign keep-tmp-files t) in the ACL2 loop or in raw Lisp. The clean target for the standard `make' process for certifying books (see *note BOOKS-CERTIFICATION-CLASSIC::) will however delete all files TMP*.*. The TMP files discussed just above now generally include the current process ID in their names, e.g., TMP@16388@1.lisp instead of TMP1.lisp. Thanks to Bob Boyer for suggesting this measure, which will reduce the possibility that two different processes will attempt to access the same temporary file. Now, :pe will print the information formerly printed by :pe!, slightly enhanced to work for logical names that are strings, not just symbols. Thanks to Warren Hunt for leading us to this change by suggesting that :pe nth print the definition of nth. We eliminated spurious warnings that could occur in raw mode in OpenMCL or CMUCL when stobjs are present. We thank Juho Snellman for pointing out the relevant bug and appropriate fix. Mfc-rw now takes a third argument that can specify an arbitrary known equivalence relation; see *note EXTENDED-METAFUNCTIONS::. Thanks to Dave Greve for discussions suggesting this improvement. A small modification to a symbol-reading function allows documentation string processing on Windows systems that use CR/LF for line breaks. Thanks to William Cook for bringing this issue to our attention. The documentation has been improved on how to control the printing of ACL2 terms. See *note USER-DEFINED-FUNCTIONS-TABLE::. Thanks to Sandip Ray for asking a question that led to the example presented there. We fixed an inefficiency that could cause an ld command to seem to hang at its conclusion. Thanks to Sandip Ray for pointing out this problem. We checked that ACL2 runs under LispWorks 4.4.5, and have inhibited redefinition warnings. Two changes have been made on behalf of congruence-based reasoning. Thanks to Dave Greve for examples and discussions that have led to these changes, and to Eric Smith and Vernon Austel, who also sent relevant examples. o When a call of the new unary function double-rewrite is encountered by the rewriter, its argument will be rewritten twice. This solves certain problems encountered in congruence-based rewriting. Warnings for :rewrite and :linear rules will suggest when calls of double-rewrite on variables in hypotheses are likely to be a good idea. See *note DOUBLE-REWRITE::. o Hypotheses of the form (equiv var (double-rewrite term)), where equiv is a known equivalence relation and var is a free variable (see *note FREE-VARIABLES::), will bind var to the result of rewriting term twice. Previously, hypotheses of the form (equal var term) would bind a free variable var, but the call had to be of equal rather than of an arbitrary known equivalence relation. The following improvements were made in support of ACL2 on top of OpenMCL. o New versions of OpenMCL that do not have :mcl in Lisp variable *features* will now work with ACL2. Thanks to David Rager for bringing this issue to our attention. o Added support for OpenMCL 1.0 for 64-bit DarwinPPC/MacOS X, thanks to Robert Krug. o Fixed tracing in OpenMCL so that the level is reset to 1 even if there has been an abort. o Added support in OpenMCL for WET. o Incorporated suggestions from Gary Byers for printing the "Welcome to OpenMCL" prompt before initially entering the ACL2 loop and, and for setting useful environment variable CCL_DEFAULT_DIRECTORY in the ACL2 script. Fixed a long-standing bug in forward-chaining, where variable-free hypotheses were being evaluated even if the executable-counterparts of their function symbols had been disabled. Thanks to Eric Smith for bringing this bug to our attention by sending a simple example that exhibited the problem. Improved reporting by the break-rewrite utility upon failure to relieve hypotheses in the presence of free variables, so that information is shown about the attempting bindings. See *note FREE-VARIABLES-EXAMPLES-REWRITE::. Thanks to Eric Smith for requesting this improvement. Also improved the break-rewrite loop so that terms, in particular from unifying substitutions, are printed without hiding subterms by default. The user can control such hiding ("evisceration"); see :DOC set-brr-term-evisc-tuple. A new directory books/defexec/ contains books that illustrate the use of mbe and defexec. Thanks to the contributors of those books (see the README file in that directory). The directories books/rtl/rel2 and books/rtl/rel3 are no longer distributed. They are still available by email request. (Subdirectory rel1/ supports some of the optional workshop/ books, so it is still distributed.) Added book books/misc/sticky-disable.lisp to manage theories that might otherwise be modified adversely by include-book. Thanks to Ray Richards for a query that led to our development of this tool. The commands (exit) and (quit) may now be used to quit ACL2 and Lisp completely; in fact they macroexpand to calls of the same function as does good-bye (which is now a macro). Thanks to Jared Davis for suggesting the new aliases. (OpenMCL-only comment:) These all work for OpenMCL even inside the ACL2 loop. The macro wet now hides structure by default on large expressions. However, a new optional argument controls this behavior, for example avoiding such hiding if that argument is nil. Thanks to Hanbing Liu for pointing out that wet was not helpful for very large terms. We have fixed a bug in the forward-chaining mechanism that, very rarely, could cause a proof to be aborted needlessly with an obscure error message. Thanks to Jared Davis for sending us an example that evoked this bug. Fixed a bug that was causing proof output on behalf of :functional-instance to be confusing, because it failed to mention that the number of constraints may be different from the number of subgoals generated. Thanks to Robert Krug for pointing out this confusing output. The fix also causes the reporting of rules used when silently simplifying the constraints to create the subgoals. Fixed a bug in handling of leading ./ in pathnames, as in: (include-book "./foo"). Thanks to Jared Davis for bringing this bug to our attention. Made a small fix for handling of free variables of :forward-chaining rules, which had erroneously acted as though a hypothesis (equal var term) can bind the variable var. A small change has been made for :type-prescription rules for hypotheses of the form (equal var term), where var is a free variable and no variable of term is free (see *note FREE-VARIABLES::). As with :rewrite and :linear rules, we now bind var to term even if (equal u term) happens to be known in the current context for some term u. Also as with :rewrite and :linear rules, similar handling is given to hypotheses (equiv var (double-rewrite term)) where equiv is a known equivalence relation. We changed the handling of free variables in hypotheses of :rewrite rules being handled by the proof-checker's rewrite (r) command, in complete analogy to the change described just above for :type-prescription rules. The installation instructions have been updated for obtaining GCL on a Macintosh. Thanks to Robert Krug for supplying this information and to Camm Maguire for simplifying the process by eliminating the gettext dependency. The macro comp is now an event, so it may be placed in books. Previously, a save-exec call could fail because of file permission issues, yet ACL2 (and the underlying Lisp) would quit anyhow. This has been fixed. Thanks to Peter Dillinger for bringing this problem to our attention. Jared Davis, with assistance from David Rager, has updated his ordered sets library, books/finite-set-theory/osets/. See file CHANGES.html in that directory. A new function, reset-kill-ring, has been provided for the rare user who encounters memory limitations. See *note RESET-KILL-RING::.  File: acl2-doc-emacs.info, Node: NOTE-2-9-5, Next: NOTE-3-0, Prev: NOTE-2-9-4, Up: RELEASE-NOTES NOTE-2-9-5 Changes in Version 3.0 since Version 2.9.4 Fixed a bug in cw-gstack that was causing a hard error when attempting to report on a forced assumption. Thanks to Jared Davis for pointing this out and sending an example that helped us to determine a fix. Added set-backchain-limit to the set of legal events that can be placed in encapsulate forms and books. Thanks to John Cowles for bringing this issue to our attention. Fixed a bug that broke wet. Thanks to David Rager for bringing this bug to our attention. Guard verification now evaluates ground subexpressions (those with no free variables) when computing the guard conjecture for the body of a function. Thanks to Jared Davis for useful conversations leading to this change. See *note VERIFY-GUARDS::, in particular its "Note on computation of guard conjectures and evaluation" near the end of that topic, for more details. Added a warning when a theory-invariant is redefined. Thanks to Jared Davis for suggesting a warning in this case and providing an informative example. Also, theory-invariants are now maintained more completely, as they are checked at the end of every event except for events executed on behalf of an include-book or the second pass of an encapsulate. Fixed the handling of runic designators to match their specification (see *note THEORIES::), so that disabling the name of a defthm event disables all rules generated for that event. (For those who do numerous builds using feature :acl2-mv-as-values, currently only OpenMCL and multi-threaded SBCL by default:) You can speed up builds by adding the following parameter to `make', under conditions described in GNUmakefile: USE_ACL2_PROCLAIMS=:REUSE. Arranged that traced functions (see *note TRACE$::) are automatically untraced when events are undone (for example see *note UBT::), at least for most underlying Common Lisp implementations. The macro defun-sk now creates non-executable functions, which allows stobjs to be used where they had previously been prohibited. More generally, the user now has control over declare forms to be used by the underlying defun'd function; see *note DEFUN-SK::. Thanks to Sandip Ray for pointing out the need for such a modification. :Definition rules are now treated, at least by default, as truly first-class definitions. In particular, :expand hints use the latest :definition rule by default. You may specify :install-body nil to get the previous behavior of :definition rules; See *note DEFINITION::, and you may choose a previously-installed :definition rule to provide the current body; see *note SET-BODY::. Also see *note RULE-CLASSES:: for details of the :install-body field, and see *note HINTS:: to see a new :with directive for controlling expansion. The :with directive for :expand hints can even direct the use of a :rewrite rule for expansion! Thanks to various people, including Sandip Ray and Rob Sumners, for discussions on the issue of the applicability of :definition rules for :expand hints. Constraints for functional instantiation now use the original definition rather than a simplified ("normalized") version of it. Fixed a bug that caused the prompt to stay the same when guard-checking is off (see *note SET-GUARD-CHECKING::) and raw-mode is changed (see *note SET-RAW-MODE::). Lemma names in directory books/ordinals have been changed by replacing /\ with & and replacing \/ with V. We made this change because backslash is an escape character and hence disappears unless it is itself escaped. Fixed proof-tree output so that failed non-proof events do not cause the proof-tree to be re-printed. Thus for example, if you have already advanced the checkpoint marker, it will not be reset by subequent failed non-proof events. Thanks to Pete Manolios and Peter Dillinger for bringing this bug to our attention. Fixed a bug that was preventing the printing of stobj fields as constants instead of numbers in certain cases. (Note that this bug only affected printing, not soundness.) Thanks to Eric Smith for bringing this problem to our attention and providing the following example (which now works fine). (defstobj st fld1 fld2) (in-theory (disable update-nth)) (defund run (st) (declare (xargs :stobjs (st))) ;adding this didn't seem to help.. st) ;works great; *fld1* prints as *fld1* (thm (equal (update-nth *fld1* 'abc st) (car (cons x y)))) ;*fld1* gets printed as 0, presumably because the call to run intervenes. (thm (equal (update-nth *fld1* 'abc (run st)) (car (cons x y)))) The macro progn now allows the use of macros defined within its bodies even when at the event level, as illustrated by the following example. (progn (defmacro my-defun (&rest args) `(defun ,@args)) (my-defun g (x) x)) Thanks to Anna Slobodova for bringing this issue to our attention. A related change is that all arguments of progn must now be embedded event forms (see *note EMBEDDED-EVENT-FORM::), so use er-progn instead if this is not the case. The change to progn mentioned above also fixes a bug in handling local events inside a progn that is inside an encapsulate or in a book. For example, the following form formerly caused an error. (encapsulate () (defun foo (x) x) (progn (local (defun bar (x) x)) (defun abc (x) x))) We fixed two bugs in :puff and :puff*. The first, brought to our attention by Eric Smith (who we thank), caused a cryptic error message when puffing a command with no subsidiary stored events; try, for example, (encapsulate () (value-triple 3)). The second was due to a failure to restore the acl2-defaults-table. Suppose for example that we have certified the book foo.lisp, which contains (program) followed by some definitions and/or theorems. Now suppose we start ACL2 and execute the following. (include-book "foo") (defthm test-thm (equal x x) :rule-classes nil) If we now execute :puff 1, ACL2 will roll back the world to before the include-book; then "puff" the include-book, which will leave us in :program mode; and finally skip re-execution of the defthm because such events are skipped in :program mode. The fix is to re-install the acl2-defaults-table immediately after the include-book to its pre-include-book value. A new event, make-event, provides something like macros that take state. For example, one can use it to put tests into certified books, do proof search, and generate new function names. Many examples appear in directory books/make-event/. See *note MAKE-EVENT::. Thanks to Bob Boyer and Jared Davis for useful feedback and to Warren Hunt, David Rager, and Sandip Ray for helpful discussions leading to some of the examples in directory books/make-event/. In support of make-event, which is described in the preceding paragraph, certify-book has a new keyword argument, :save-expansion, that controls whether the result of expanding make-event forms is written out to a file. See *note CERTIFY-BOOK::; and for a discussion of book expansion files, see *note MAKE-EVENT::. We fixed a soundness bug that did not correctly detect local events. For example, the following event was admitted. (encapsulate () (local (encapsulate () (local (progn (program))) ; or, (local (with-output :off summary (program))) (set-irrelevant-formals-ok t) (defun foo (x) (declare (xargs :measure (acl2-count x))) (1+ (foo x))))) (defthm inconsistent nil :hints (("Goal" :use foo)) :rule-classes nil)) A new value for guard checking, :none, is now allowed. If you execute :set-guard-checking :none, then no guard checking will take place (but raw Lisp code will not be executed in this case). As a result, you should never see a guard violation, even for calls of :program mode functions. We thank Pete Manolios, who has long wanted this feature, and also Peter Dillinger, for asking for it. New documentation explains the interaction between the defun-mode and the value supplied to :set-guard-checking. See *note GUARD-EVALUATION-TABLE::, see *note GUARD-EVALUATION-EXAMPLES-SCRIPT::, and see *note GUARD-EVALUATION-EXAMPLES-LOG::. In the course of adding the guard-checking value :none described in the paragraph above, we eliminated an optimization that eliminated guard checking for some recursive calls of :logic mode mutually-recursive functions that have not had their guards verified. But we doubt that this change will be noticed by any users!) The ACL2 hyper-card has been enhanced, thanks to David Rager, with a listing of "Useful EMACS Commands" to match comments in emacs/emacs-acl2.el. Users contributed books following the Readme.lsp methodology: data-structures/memories and unicode (Jared Davis), proofstyles (Sandip Ray and J Moore). Made some improvements to books/Makefile-generic (a file discussed elsewhere; see *note BOOKS-CERTIFICATION-CLASSIC::). In particular, improved handling of .acl2 files for dependencies target. (Only OpenMCL and, with feature :acl2-mv-as-values, GCL) Fixed a bug that was causing proclaiming to fail when definitions are submitted interactively. The default stack size has been increased for several lisps. (Very technical) A restriction has been weakened on the use of local stobjs under a call of an ACL2 evaluator (trans-eval or simple-translate-and-eval). Now, the error can only take place for stobj names that occur in the term being evaluated. Thanks to Erik Reeber for bringing this issue to our attention. The notion of "ancestor" has been changed slightly. This notion is used by extended metafunctions and break-rewrite (see *note EXTENDED-METAFUNCTIONS:: and see *note BRR@: BRRatsign.), and also with backchain limits (see *note BACKCHAIN-LIMIT:: and see *note SET-BACKCHAIN-LIMIT::). Basically, each time a hypothesis is encountered during application of a rewrite rule, that hypothesis is pushed (after instantiating and negating) onto the current list of ancestors before it is rewritten. However, hypotheses of the form (equal var term), where var is free (see *note FREE-VARIABLES::), had not been included in the ancestors (similarly for (equiv var (double-rewrite term)) where equiv is a known equivalence relation). Now such "binding hypotheses" are included in a special way in ancestors data structures. In particular, (null (mfc-ancestors mfc)) will now be true if and only if the term being rewritten is part of the current goal as opposed to a hypothesis from a rule encountered during backchaining, even if that hypothesis is a binding hypothesis. Thanks to Dave Greve for bringing this issue to our attention. Termination and induction analysis now continue through both arguments of prog2$, not just the second. (Normally, the gathering up of if tests stops at function calls; but it continued through the second argument of prog2$, and now it will continue through both arguments.) Thanks to Sol Swords for discussion leading to this change. The ACL2 distribution is now kept on the http server rather than the ftp server (but the home page has not been moved). Thanks to Robert Krug for letting us know that some ACL2 users have found it inconvenient to fetch ACL2 using ftp. The file books/README.html has been renamed to books/Readme.html, since some browsers don't show the former in the directory listing.  File: acl2-doc-emacs.info, Node: NOTE-3-0, Next: NOTE-3-0(R), Prev: NOTE-2-9-5, Up: RELEASE-NOTES NOTE-3-0 ACL2 Version 3.0 (June, 2006) Notes Please see *note NOTE-2-9-5:: for a description of changes since Version 2.9.4. These include the new make-event feature, a soundness bug fix, an improvement for :expand hints, evaluation in the logic by way of :set-guard-checking :none, and many other improvements. More generally, there have been several incremental releases since Version 2.9: see *note NOTE-2-9-1::, see *note NOTE-2-9-2::, see *note NOTE-2-9-3::, see *note NOTE-2-9-4::, and see *note NOTE-2-9-5::. A very few users have contributed books following the instructions on the web. We expect that when more contributions come in, we will give more attention to the question of how to organize the distributed and workshop books. For now, we have simply added the new contributions according to the old-style distribution methodology.  File: acl2-doc-emacs.info, Node: NOTE-3-0(R), Next: NOTE-3-0-1, Prev: NOTE-3-0, Up: RELEASE-NOTES NOTE-3-0(R) ACL2 Version 3.0(r) (June, 2006) Notes No significant changes have been made since Version 2.9 for support of non-standard analysis in particular. Please also see *note NOTE-3-0:: for changes to Version 3.0 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-0-1, Next: NOTE-3-0-1(R), Prev: NOTE-3-0(R), Up: RELEASE-NOTES NOTE-3-0-1 ACL2 Version 3.0.1 (August, 2006) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Fixed a soundness bug, introduced in the previous release, due to a failure to disallow table events that set the acl2-defaults-table in a local context. Here is a proof of nil that exploits the bug. (encapsulate () (local (program)) (defun foo () (declare (xargs :measure 17)) (+ 1 (foo)))) (thm nil :hints (("Goal" :in-theory (disable foo (foo)) :use foo))) Fixed a bug in the alternatives to good-bye, which are the exit and quit commands. Thanks to Jared Davis and Peter Dillinger for pointing this out right away. The definition of len has been highly optimized in raw Lisp. Thanks to Bob Boyer and Warren Hunt for suggesting such an improvement and providing a lot of help in coming up with the current implementation. The clause subsumption algorithms have been improved, both to improve efficiency during warnings for :rewrite rules and to punt when the subsumption computation for induction appears to be blowing up. Thanks to Robert Krug for bringing this issue to our attention and supplying a useful example. A bug has been fixed that prevented time$ from working properly in OpenMCL and multi-threaded SBCL (actually, in any ACL2 image where feature :acl2-mv-as-values is present). Thanks to Sol Swords for bringing this problem to our attention. A type-spec of the form (satisfies pred) carries the requirement that pred be a unary function symbol in the current ACL2 world; otherwise, it is illegal. Thanks to Dave Greve for pointing out that Common Lisp has this requirement. Installed a fix provided by Gary Byers (for ACL2 source function install-new-raw-prompt), for OpenMCL, that fixes an issue exposed in some versions of OpenMCL when compiler optimization is off. Fixed a bug in contributed book misc/untranslate-patterns.lisp that was causing calls of add-untranslate-pattern to be rejected in books. Thanks to Ray Richards for pointing out this bug and to Jared Davis for assisting in the fix. Fixed a bug in defstobj when keywords :initially and :resizable are both supplied. In this case, the definition of the resizing function mistakenly failed to quote the :initially value, even though this value is not to be evaluated. One could even get an error in this case, as in the following example supplied by Erik Reeber, whom we thank for bringing this bug to our attention: (defstobj $test (test-x :type (array t (5)) :initially (0) :resizable t)) A new feature, with-prover-time-limit, allows the setting of time limits during proofs. This is *not* a general-purpose time-limit utility, nor is it guaranteed to implement a strict bound; it only attempts to limit time approximately during proofs. Thanks to Pete Manolios and Daron Vroon, who made the most recent request for such a feature, and to Robert Krug for a helpful discussion. (GCL only) Fixed a bug in the procedure for building a profiling image. Thanks to Sol Swords for bringing this bug to our attention and to Eric Smith for bringing a subsequent problem to our attention. Handling of theories can now use significantly less time and space. A regression suite run took about 25% longer before this change than it did after making this change (and also the ones in the next two paragraphs). Thanks to Vernon Austel for bringing this issue to our attention and for supplying code, quite some time ago, that provided detailed, useful implementation suggestions. Also thanks to the folks at Rockwell Collins, Inc. for pushing the limits of the existing implementation, thus encouraging this improvement. Fixed a performance bug in obtaining executable counterpart symbols. We now avoid certain computations made on behalf of warnings, when such warnings are disabled. We have relaxed the checks made when including an uncertified book, to match the checks made when including a certified book. Thanks to Eric Smith for suggesting this change. Fixed a bug in :pso (see *note SET-SAVED-OUTPUT::) that caused an error when printing the time summary. Made fixes to avoid potential hard Lisp errors caused by the use of :program mode functions. The fix was to use a "safe mode," already in use to prevent such errors during macroexpansion; see *note GUARDS-AND-EVALUATION::. However, such errors were possible during evaluation of macro guards, for example as follows: (defun foo (x) (declare (xargs :mode :program)) (car x)) (defmacro mac (x) (declare (xargs :guard (foo 3))) x) (defun g (x) (mac x)) A similar issue existed for calls of defpkg, in-theory, table, make-event, and value-triple, but has been fixed for all but in-theory and make-event, where technical issues have caused us to defer this change. Fixed a bug in wet that caused problems in OpenMCL, and perhaps other Lisp implementations, when the argument to wet calls, or depends on, certain built-ins including prog2$, time$, mbe, and must-be-equal. Thanks to David Rager for bringing this problem to our attention. The file books/Makefile-generic has been improved so that when book certification fails with `make', the failure message contains the book filename. Documentation has been written to explain how to avoid an expensive immediate rewrite of the result of applying a :rewrite or :meta rule. See *note META::. Thanks to Robert Krug for supplying this trick, and to Eric Smith and Dave Greve for useful discussions. (OpenMCL only) OpenMCL-based ACL2 image names formerly had extension ".dppccl", which was correct only for some platforms (including 32-bit Darwin PPC). That has been fixed, thanks to a suggestion from Gary Byers. It is now legal to attach both a :use and a :cases hint at the same goal. Thanks to Eric Smith for (most recently) requesting this feature. It is now permissible to include the same symbol more than once in the imports list of a defpkg form (i.e., its second argument). Also, the evaluation of defpkg forms with long import lists now uses a reasonably efficient sorting routine to check for two different symbols with the same name (see also books/misc/sort-symbols.lisp). If you currently call a function like remove-duplicates-eql for your imports list, as had been suggested by a defpkg error message, then you may experience some speed-up by removing that call. Thanks to Eric Smith for helping to discover this issue through profiling. Made miscellaneous efficiency improvements not listed above (for example, following a suggestion of Eric Smith to avoid checking for so-called "bad Lisp objects" during include-book, which saved almost 3% in time on one large example). Modified the notion of "untouchable" to separate the notion of untouchable functions and macros from the notion of untouchable state global variables. See *note PUSH-UNTOUCHABLE::. Thanks to Bob Boyer for sending an example, (put-global 'ld-evisc-tuple t state), that suggested to us the need for more restrictive handling of untouchables. In particular, many ld specials (see *note LD::) are now untouchable. You may be able to work around this restriction by calling ld; see for example the change to books/misc/expander.lisp. But please contact the ACL2 implementors if this sort of workaround does not appear to be sufficient for your purposes. Fixed a bug in function set-standard-oi (see *note STANDARD-OI::). Fixed a bug in the use of ld-evisc-tuple. The bad behavior was an improper use of the print-level and print-length components of the tuple (specifically, taking its caddr and cadddr instead of taking its cadr and caddr). Thanks to Bob Boyer for bringing this bug to our attention. A new argument to the compile-flg argument of certify-book, :all, causes creation of a file to be compiled in place of the given book, where that file contains not only a copy of the book (with make-event forms expanded) but also contains definitions of the so-called "executable counterparts" of the functions defined in the book. Then, functions defined in the book will be run compiled when including the book, even for functions whose guards have not been verified, or are in :program mode and running in so-called "safe mode" (for example, during expansion of macros). The default behavior, value t of compile-flg, is unchanged. Moreover, a new :comp! argument of include-book now compiles the executable counterparts when creating the book's compiled file, and unlike :comp, always deletes the old compiled file first so that one always gets a fresh compile. Now, certify-book gives a "Guards" warning only for :logic mode functions that are defined in the given book but have not had their guards verified. Previously, it also warned about such functions that were defined in the certification world or in sub-books. A new command, redo-flat, facilitates the debugging of failed encapsulate and progn forms by evaluating preliminary forms in order to leave one at the point of failure. See *note REDO-FLAT::. Thanks to Ray Richards and others for asking for such a utility, and to Sandip Ray for useful discussions. We have changed the automatic declaration of of function types (still done in GCL and OpenMCL only, for now). Our motivation was to avoid the assumption that Common Lisp functions return one value when ACL2 says that they do; thanks to Bob Boyer for bringing this issue to our attention with the example of defining (foo x y) to be (floor x y). ACL2 was saying that foo returns a single value, but because floor returns two values in raw Lisp, so does foo. Other changes to automatic declaration include comprehending defund, not just defun. A new function, mod-expt, computes (mod (expt base exp) m), and does so efficiently in some implementations (currently only in GCL 2.7.0, which is not yet released). Thanks to Warren Hunt for suggesting such an addition. New functions getenv$ and setenv$ have been made available for reading and writing environment variables. Thanks to Jun Sawada for requesting these utilities. The query utility :pl has been improved in several ways. As before, :meta rules are only printed if the argument is a symbol; but the information printed for them is now more appropriate. The following are changes for the case that the argument is not a symbol, but rather, a term. (1) Rules are displayed that have equivalence relations other than equal. (2) All matching :definition rules are displayed, where previously :definition rules were only shown if they were "simple" rules (sometimes known as "abbreviations"); see *note SIMPLE::. (3) The "Equiv" field is printed for terms, not just symbols. (4) The substitution is shown that, when applied to the left-hand side of the rule, will yield the specified term. Thanks to Eric Smith for suggesting these changes. The proof-checker command ;show-rewrites has been improved to match the changes described above for :pl. In particular, :definition rules that are not "simple" are now displayed by the proof-checker's show-rewrites (and sr) command, and the proof-checker's rewrite command has been correspondingly modified to accept these :definition rules. Fixed `make' targets copy-distribution, copy-workshops, and copy-nonstd so that they should also work for non-developers. Fixed a bug that was causing :pr to display syntaxp hypotheses oddly in some cases, in particular (syntaxp (let ...)). (The problem was in the "untranslate" display of the internal form of syntaxp calls.) Thanks to Robert Krug for bringing this problem to our attention. We also removed the restriction on bind-free that its argument could not be a variable, a constant, or (more interestingly) a lambda application (i.e., a let or mv-let expression).  File: acl2-doc-emacs.info, Node: NOTE-3-0-1(R), Next: NOTE-3-0-2, Prev: NOTE-3-0-1, Up: RELEASE-NOTES NOTE-3-0-1(R) ACL2 Version 3.0.1(r) (August, 2006) Notes No significant changes have been made since Version 3.0 for support of non-standard analysis in particular. Please also see *note NOTE-3-0-1:: for changes to Version 3.0.1 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-0-2, Next: NOTE-3-1, Prev: NOTE-3-0-1(R), Up: RELEASE-NOTES NOTE-3-0-2 ACL2 Version 3.0.2 (December, 2006) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Fixed soundness bugs in the handling of primitive function pkg-witness, and improved its documentation. (The executable counterpart returned an incorrect default value, and the axiom symbol-package-name-pkg-witness-name needed pkg-name to be other than "" in order to avoid the default value of "ACL2".) As fallout, a new built-in :forward-chaining rule, symbol-package-name-of-symbol-is-not-empty-string, now asserts that the symbol-package-name of a symbol is never "". Thanks to Mike Gordon for bringing these soundness bugs to our attention by attempting to prove translations of ACL2 axioms in HOL4. Fixed a soundness bug in linear arithmetic, due to incomplete tracking of forced assumptions while deriving inequalities. Thanks to Robert Krug for providing a fix and a proof of nil before the fix. Fixed a soundness bug in the redundancy criterion for defun events, which has been modified; see *note REDUNDANT-EVENTS::. This bug is illustrated below. Thanks to Peter Dillinger and Jared Davis for contributions to an email thread that led us to discover this bug. The solution is that for a definition to be redundant with an earlier definition, ACL2 no longer ignores :measure xargs except when skipping proofs (e.g., during include-book). However, a new "measure", (:? v1 ... vk), is supported, for specifying a measured subset of the set of formals, i.e., a set of formals that serves as the set of parameters for some valid measure. (encapsulate () (local (defun foo (x y) (declare (xargs :measure (acl2-count y))) (if (and (consp x) (consp y)) (foo (cons x x) (cdr y)) y))) ; So the following is redundant -- but it guesses a measure ; of (acl2-count x), which isn't right! (defun foo (x y) (if (and (consp x) (consp y)) (foo (cons x x) (cdr y)) y))) ; end of encapsulate ; Now we prove a non-theorem by exploiting the bug above, ; erroneously replacing formal y by a constant in the induction ; scheme hinted below. (This should not be allowed, as y should be ; labeled as a measured formal.) (defthm bad (atom x) :rule-classes nil :hints (("Goal" :induct (foo x '(3))))) ; It's easy to get a contradiction by instantiating the ; non-theorem just above. (defthm contradiction nil :rule-classes nil :hints (("Goal" :use ((:instance bad (x '(7))))))) Fixed a bug in :pl and the proof-checker's show-rewrites (sr) command that was causing a Lisp break. For :pl, also improved the display of unifying substitutions, modified output to take binding hypotheses (equal var term) into account properly, and arranged for inclusion of meta rules that modify the given term. Thanks to Eric Smith for bringing these issues to our attention. Introduced new utilities for undoing commands, :ubu and :ubu!, which are analogous to :ubt and :ubt! (respectively) except that they only undo back up to, but not including, the indicated command. Fixed a performance bug, pointed out by Eric Smith, that was negating efforts made for the preceding release to avoid computation for disabled warnings. Added time$ and value-triple to *acl2-exports*. Thanks to Bob Boyer and Erik Reeber (respectively) for bringing these issues to our attention. Improved the automatic proclaiming of function types for GCL and OpenMCL, specifically to use an output format consistent with the Common Lisp spec. Thanks to Bob Boyer for bringing this issue to our attention. Added books/misc/transfinite.lisp, which deals with transfinite induction in ACL2. Thanks to Eric Smith for contributing this book. Added books/misc/process-book-readme.lisp to the distribution. Thanks to Sandip Ray for pointing out its omission. Added contributions books/concurrent-programs/bakery/ and books/concurrent-programs/german-protocol/. These contributions can be used as tutorials, especially by new ACL2 users, for learning how to model concurrent protocols in ACL2 and the steps involved in reasoning about their correctness. Thanks to Sandip Ray for these contributions. See the Readme.lsp files in these directories. Theory invariants may now involve the variable ENS instead of the variable THEORY. The practical effect of this change is that any expression of the form (MEMBER-EQUAL rune THEORY) occurring in a theory-invariant expression should be replaced by (ACTIVE-RUNEP rune). See *note THEORY-INVARIANT::. Thanks to Eric Smith and Dave Greve for pointing out an inefficiency in the handling of theory invariants that led to this change, which can speed up their handling by orders of magnitude on large examples, and to Eric for testing this change and pointing out problems with an early implementation of it. Theory invariants (see *note THEORY-INVARIANT::) are no longer checked on theories defined by deftheory events. After all, one can define a theory with deftheory that is not intended to be used as the current theory, but rather is intended to be combined with other theories (see *note THEORY-FUNCTIONS::). Thanks to Eric Smith for bringing this issue to our attention. Theory-invariant errors had been reported with very little detail when warnings were inhibited. This problem has been fixed; thanks to Eric Smith for bringing it to our attention and providing an example. We have also improved the handling of redundancy for theory-invariant events. The macro defun-sk now has a new optional keyword, rewrite, that can be used to change the form of the :rewrite rule generated when the quantifier is forall. Thanks to Eric Smith and Sandip Ray for useful discussions on this topic. We have also slightly modified the hints for the defthm event underneath a defun-sk in order to make the proof more reliably efficient. A new event, reset-prehistory, allows setting of a barrier before which undoing is illegal. An argument to this macro allows the barrier to be made permanent; otherwise, it can be removed with :ubt-prehistory. Thanks to Peter Dillinger for useful conversations leading to the addition of reset-prehistory. A new query, (wormhole-p state), allows users to determine whether or not they are in a wormhole. Thanks to Peter Dillinger for providing this utility. Value-triple no longer evaluates its form during include-book, and in raw Lisp its calls trivially macroexpand to nil, without any consideration of its argument. This change avoids errors and warnings when stobj names occur in the argument. We fixed what could be considered a soundness hole that could occur by exploiting redefinition in a particular way. Thanks to Peter Dillinger for raising a question that led to discovery of this hole. A bug has been fixed in handling of illegal theory expressions. Thanks to Eric Smith, who reported this problem and provided the example (in-theory '((:definition natp) (:rewrite doesntexist))) to show how a hard error could occur. Improved error reporting by certify-book when the certification world contains inadmissible forms. Modified defchoose to add two new keyword arguments. There is now a :doc keyword argument; previously, an optional documentation string (see *note DOC-STRING::) was to be placed just before the body, without a keyword. There is also a :strengthen argument that strengthens the axiom added, which allows for the definition of "fixing" functions for equivalence relations that choose canonical representatives of equivalence classes. See *note DEFCHOOSE::. Thanks for Dave Greve for useful discussions that led us to this :strengthen enhancement. Added books/misc/bash.lisp, which provides utilities for simplifying a goal into a list of subgoals (as documented at the top of that file). Thanks to Dave Greve for requesting this utility and suggesting refinements to its functionality, which have been incorporated. (For Emacs users only) The command meta-x new-shell provided by file emacs/emacs-acl2.el now puts you in shell-mode, which for example supports directory tracking. Thanks to Jared Davis for suggesting this change. Fixed some mishandling of stobjs by make-event expansion. Introduced a new event, defttag, that introduces a "trust tag" ("ttag") allowing for extensions of ACL2 and for the use of generally unsafe ACL2 constructs. Thanks to Peter Dillinger, Sandip Ray, and Erik Reeber for useful discussions on defttag and the following related items. A new event, remove-untouchable, can be used to give users access to system functions and data structures. We also fixed a bug in push-untouchable; and, it no longer is a no-op in :program mode. Thanks to Peter Dillinger for proposing remove-untouchable and suggesting that it and push-untouchable be functional in :program mode. Raw-mode (see *note SET-RAW-MODE::) no longer disables certify-book. However, set-raw-mode is now disallowed unless there is an active ttag (see *note DEFTTAG::). If you want to execute (set-raw-mode t) and there is no active ttag, consider executing (set-raw-mode-on!) instead. Redefinition of system functions is disallowed unless there is an active ttag. However, redef! now introduces (defttag :redef!) in order to allow redefinition of system functions. A new event, progn!, is a legal embedded event form that can go in books and both encapsulate and progn forms (see *note EMBEDDED-EVENT-FORM::), and is similar to progn except that it allows arbitrary forms. Thus, a progn! form is potentially dangerous and can only be evaluated if there is an active ttag. See *note TTAGS-SEEN:: for information about how to find the ttags known in the current ACL2 world, and for related caveats. A new book created with Peter Dillinger, books/misc/hacker.lisp (added after Version_3.3: now books/hacking/hacker.lisp), uses progn! to define utiliities with-raw-mode and with-redef-allowed, which respectively allow raw Lisp evaluation and redefinition to take place within a certifiable book (!). Macro with-output is no longer allowed in function bodies because it does not have (and has never had) any effect in raw Lisp. See *note WITH-OUTPUT:: for a workaround. Fixed a bug in redundancy of defstobj in raw Lisp, which caused an error when certifying a book with a redundant defstobj event whose stobj had already been modified. Here is an example: (defstobj st fld) (update-fld 3 st) (certify-book "foo" 1) ; where foo.lisp contains (defstobj st fld) New books illustrating make-event have been contributed in directory books/make-event/: dotimes.lisp (David Rager), stobj-test.lisp, and logical-tangent.lisp (Peter Dillinger). Modified print-object$ (see *note IO::) so that it no longer prints an extra space at the end. Replaced the "draconian restriction to avoid capture" that had prevented some :functional-instance hints from being legal. The corresponding check now only requires that no variable free in the functional substitution is captured by a let or mv-let (or lambda) binding. See *note LEMMA-INSTANCE::. Added new extended metafunction, mfc-rw+, which is equivalent to mfc-rw except that it takes an alist argument, which may be useful for efficiency. See *note EXTENDED-METAFUNCTIONS::. Thanks to Robert Krug for suggesting this more efficient variant of mfc-rw. Added support for the ignorable declare form. We now cause an error on a call of open-input-channel (see *note IO::) with an argument string whose first character is the | character. Thanks to Bob Boyer for providing an example (several months ago) showing the danger of such calls, namely that the following command would log you out and kill all of your processes when running on top of GCL in Linux: (open-input-channel "|kill -9 -1" :object state) Restricted the use of make-event to contexts in which it can be tracked properly, under legal events (see *note EMBEDDED-EVENT-FORM::). Thanks to Peter Dillinger for bringing an example to our attention that led to this fix. Fixed a bug that was avoiding guard-checking for the functions compress1 and compress2. Thanks to David Rager for bringing this bug to our attention. Added an error message when a defun or mutual-recursion event fails, to clarify whether failure is for the measure conjecture or for the guard conjecture. Thanks to David Rager for requesting clarification for such failures. Fixed a bug in reporting of guard violations (hard Lisp error) when certain macros (for example, cond) are used in the guard. Thanks to Jared Davis for bringing this problem to our attention and providing assistance with the solution, in particular by providing a helpful example. Grant Passmore has contributed a resolution/paramodulation prover written in ACL2, in directory books/deduction/passmore/. Thanks, Grant. Improved the error message when illegal theories are encountered. Improved the suppression of output for inhibit-output arguments of routines in the book books/misc/expander.lisp. Thanks to Qiang Zhang for pointing out the possibility for improvement here. Added a new directory books/arithmetic-3/extra/ that extends books/arithmetic-3 with additional rules, contributed by Alex Spiridonov with guidance from Robert Krug. WARNING: This directory is under development. It may undergo large changes in future releases, so please consider it experimental and subject to change. Feedback is welcomed. As part of the work mentioned just above, Robert Krug and Alex Spiridonov contributed improvements to books/arithmetic-3/: o A new rule |(* (/ x) (/ (expt x n)))| in bind-free/collect.lisp, which is important for reducing collect-* expressions though it slowed down one proof (see comment above this rule in bind-free/collect.lisp). o Slight improvements of rules integerp-mod and rationalp-mod in floor-mod/floor-mod.lisp. o To avoid conflict with books/rtl/rel6/arithmetic/, renamed rule mod-minus to mod-neg in floor-mod/floor-mod.lisp, and renamed integerp-+-reduce-leading-constant to integerp-+-reduce-leading-rational-constant in bind-free/integerp.lisp. (GCL on Windows only) Made a low-level change to avoid multiplying stacks for GCL on Windows, since GCL 2.6.6 broke while doing this. Fixed bugs in linear arithmetic (rarely evidenced, it seems) involving using < to compare complex rational constants. Thanks to Robert Krug for helping with the fixes. Added a new event, assert-event, for checking that forms evaluate to non-nil values. Thanks to Peter Dillinger for suggesting and collaborating on this addition.  File: acl2-doc-emacs.info, Node: NOTE-3-1, Next: NOTE-3-1(R), Prev: NOTE-3-0-2, Up: RELEASE-NOTES NOTE-3-1 ACL2 Version 3.1 (December, 2006) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Please see *note NOTE-3-0-2:: for a description of changes since Version 3.0.1, and also see *note NOTE-3-0-1:: for additional changes since Version 3.0.  File: acl2-doc-emacs.info, Node: NOTE-3-1(R), Next: NOTE-3-2, Prev: NOTE-3-1, Up: RELEASE-NOTES NOTE-3-1(R) ACL2 Version 3.1(r) (December, 2006) Notes No significant changes have been made since Version 3.0 for support of non-standard analysis in particular. Please also see *note NOTE-3-1:: for changes to Version 3.1 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-2, Next: NOTE-3-2(R), Prev: NOTE-3-1(R), Up: RELEASE-NOTES NOTE-3-2 ACL2 Version 3.2 (April, 2007) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Before this release, a raw Lisp error could put the ACL2 user into the debugger of the host Common Lisp. Now, such cases will generally put the user back at the top-level loop after an informative message. For details, see *note SET-DEBUGGER-ENABLE::; also see *note BREAK$::. Fixed a soundness bug that was allowing unknown packages to sneak into a book and wreak havoc. Thanks to Peter Dillinger for sending an interesting example that led us to an exploration resulting in finding this bug. (A comment in the source code for note-3-2 shows such an example.) That example led us to fix a couple of other bugs related to packages. See *note HIDDEN-DEATH-PACKAGE:: if you are generally interested in such issues, and for associated examples, see comments in note-3-2 in the ACL2 source code. Fixed subtle soundness bugs related to :meta rules by restricting evaluators (see *note DEFEVALUATOR::), as discussed in a new documentation topic: see *note EVALUATOR-RESTRICTIONS::. Fixed a soundness bug that was allowing redefinition from :logic to :program mode. This prohibition had been in ACL2 for awhile but was accidentally removed in the preceding version. Fixed a soundness bug related to trace$. Thanks to Peter Dillinger for bringing it to our attention and for useful discussions, and providing a proof of nil, the essence of which is illustrated as follows: (value-triple (trace$ (bar :entry (defun foo () 17)))) Thus, trace$ could be used to cause destructive raw Lisp behavior. Now, trace$ fails unless it is either given a list of symbols or else there is an active trust tag (see *note DEFTTAG::); otherwise, consider using trace! instead. Closed a loophole that could be viewed as compromising soundness. It was possible to write files during book certification by exploiting make-event expansion, but that is no longer the case by default. A new function open-output-channel! is identical as a function to open-output-channel, except that the new function may be called even during make-event expansion and clause-processor hints, but requires that there is an active trust tag (see *note DEFTTAG::). Thanks to Peter Dillinger for producing a convincing example (forging a certificate during book certification; see *note OPEN-OUTPUT-CHANNEL!::) and to him, Sandip Ray, and Jared Davis for useful discussions on the topic. Added book books/defexec/reflexive/reflexive.lisp to illustrate reflexive functions. ACL2 now generate scripts that invoke the saved image with exec. (Previously this was only done for GCL and CLISP.) The benefit of this change can be to avoid the lingering of ACL2 processes after enclosing processes have exited. Thanks to Peter Dillinger for pointing out this issue. ACL2 has a better implementation of (good-bye) (hence of synonyms (quit) and (exit)). As a result, you should now be able to exit ACL2 and Lisp from within the ACL2 read-eval-print loop with any of the above; formerly, this was not supported for some Lisp implementations, and was slow in OpenMCL. Thanks to SBCL developer Harald Hanche-Olsen for useful advice. Fixed a bug in raw-mode (see *note SET-RAW-MODE::) that was causing hard errors when evaluating calls of er-progn, or of macros expanding to such calls. Fixed a few Makefile dependencies, necessary only for parallel `make'. A new book, misc/defpun-exec-domain-example.lisp, provides an example showing how partial functions which return a unique value for arguments in a specified domain can be efficiently executed with ACL2. Execution is achieved using the mbe construct. Thanks to Sandip Ray for providing this example. Existing function mod-expt computes (mod (expt base exp) mod) with great efficiency in GCL, but not in other Lisps. Now, the book arithmetic-3/floor-mod/mod-expt-fast.lisp defines a function mod-expt-fast that should provide significantly improved performance for such expressions in other Lisps as well, though still probably not as fast as when using mod-expt in GCL. Thanks to Warren Hunt, with contributions from Robert Krug, for providing this book, Modified macro break-on-error to print of an error message before entering a break, and to cause a hard error if the underlying Lisp cannot handle it (formerly, a raw Lisp break would occur). Thanks to Bob Boyer for bringing these issues to our attention. The book books/misc/defpun.lisp, as well as other books related to the defpun macro, has been modified to avoid namespace collisions by prefixing function symbol names with "DEFPUN-"; for example base has been replaced by defpun-base. Thanks to Dave Greve for providing a first version of this update to defpun.lisp. A theory, base, in books/arithmetic-3/bind-free/top.lisp, has been renamed arithmetic-3-bind-free-base, to avoid potential name conflicts. Fixed books/arithmetic-3/bind-free/banner.lisp to print (as before) a message about how to turn on non-linear arithmetic, by modifying the call of value-triple to use :on-skip-proofs t. Thanks to Robert Krug for bringing this issue to our attention. Modified books/Makefile-subdirs and books/Makefile-psubdirs so that they can be used with books/Makefile-generic. Thus, one can set things up so that `make' can be used to certify books both in the current directory and subdirectories, for example as follows. ACL2 = ../../saved_acl2 arith-top: top all all: top DIRS = pass1 bind-free floor-mod include ../Makefile-subdirs include ../Makefile-generic top.cert: top.lisp top.cert: bind-free/top.cert top.cert: floor-mod/floor-mod.cert top.cert: floor-mod/mod-expt-fast.cert An experimental extension of ACL2 is under development by Bob Boyer and Warren Hunt to support function memoization, hash conses, and an applicative version of hash tables. The default build of ACL2 does not include this extension, other than simple logic definitions of functions in new source file hons.lisp. Future versions of ACL2 may fully incorporate this experimental extension. The defevaluator event macro has been modified primarily by adding a new constraint as follows, where evl is the evaluator. The idea is that for the evaluation of a function call, one may replace each argument by the quotation of its evaluation and then also replace the alist environment with nil. (DEFTHMD UNHIDE-evl-CONSTRAINT-0 (IMPLIES (AND (CONSP X) (SYNTAXP (NOT (EQUAL A ''NIL))) (NOT (EQUAL (CAR X) 'QUOTE))) (EQUAL (evl X A) (evl (CONS (CAR X) (KWOTE-LST (UNHIDE-evl-LIST (CDR X) A))) NIL)))) In order to support this change, there is another change: an evaluator maps nil to nil (note (AND X (CDR (ASSOC-EQ X A))) in place of (CDR (ASSOC-EQ X A)) below). (DEFTHM UNHIDE-evl-CONSTRAINT-1 (IMPLIES (SYMBOLP X) (EQUAL (UNHIDE-evl X A) (AND X (CDR (ASSOC-EQ X A)))))) With the new defevaluator, Dave Greve has been able to do a proof about beta reduction that seemed impossible before (see books/misc/beta-reduce.lisp). Thanks to Dave for suggesting an initial version of this change. Explicit compilation is now avoided for OpenMCL, resulting in fewer files to manage (no more files resulting from compilation) and, according to some tests, slightly faster run times. See *note COMPILATION::. Thanks to Bob Boyer and Warren Hunt for suggesting this possibility. Now, the term-evisc-tuple (see *note LD-EVISC-TUPLE::) is overridden by state global user-term-evisc-tuple in all cases. Formerly, this was only the case when term-evisc-tuple was called with non-nil first argument. Symbols with the dot (.) character are generally no longer printed with vertical bars. For example, before this change: ACL2 !>'ab.c |AB.C| ACL2 !> After this change: ACL2 !>'ab.c AB.C ACL2 !> Thanks to Jared Davis for suggesting this improvement. Fixed bugs in guard verification for theorems. The following examples illustrate these bugs. If either theorem's body is executed in raw Lisp there is likely to be a hard Lisp error, even though verify-guards was supposed to ensure against that behavior. ; Example: Verify-guards failed to check that all functions in the theorem ; had already been guard-verified. (defun my-car (x) (car x)) (defthm my-car-compute-example (equal (my-car 3) (my-car 3))) (verify-guards my-car-compute-example) ; Example: Verify guards of a theorem whose body uses state improperly. (defthm bad-state-handler (if (state-p state) (equal (car state) (car state)) t) :rule-classes nil) (verify-guards bad-state-handler) See *note GCL:: for an example, developed with Warren Hunt and Serita Nelesen, that shows how to get fast fixnum (small integer) arithmetic operations in GCL. Fixnum declarations are now realized as (signed-byte 30) and (unsigned-byte 29) instead of what was generally (signed-byte 29) and (unsigned-byte 28). MCL users may thus find better performance if they switch to OpenMCL. Note that some definitions have changed correspondingly; for example, zpf now declares its argument to be of type (unsigned-byte 29) instead of (unsigned-byte 28). A few books may thus need to be adjusted; for example, changes were made to books in books/data-structures/memories/. ACL2's rewriter now avoids removing certain true hypotheses and false conclusions. When a hypothesis rewrites to true or a conclusion rewrites to false, ACL2 formerly removed that hypothesis or conclusion. Now, it only does such removal when the hypothesis or conclusion is either a call of equal or an equivalence relation (see *note EQUIVALENCE::), or else is sufficiently trivial (roughly, either redundant with another hypothesis or conclusion or else trivially true without considering the rest of the goal). A specific example may be found in source file simplify.lisp; search for "; But we need to do even more work". Thanks to Robert Krug for providing the idea for this improvement and its initial implementation. As is common with heuristic changes, you may find it necessary on occasion to rename some subgoals in your hints. And in this case, you might also find it necessary on rare occasions to add :do-not '(generalize) hints. A new function, mfc-relieve-hyp, allows (for example) for more powerful bind-free hypotheses, by providing an interface to the rewriter's routine for relieving hypotheses. See *note EXTENDED-METAFUNCTIONS::. Thanks to Robert Krug for providing the idea for this feature and its initial implementation. Two improvements have been made to non-linear arithmetic (see *note NON-LINEAR-ARITHMETIC::). One allows for deducing strict inequality (<) for the result of certain polynomial multiplications, where previously only non-strict inequality (<=) was deduced. A second allows the use of the product of two polynomials when at least one of them is known to be rational. We had previously restricted the use of the product to the case where both were known to be rational. Thanks to Robert Krug for these improvements. (OpenMCL and Allegro CL only) Fixed ACL2's redefinitions of raw Lisp trace and untrace in OpenMCL and Allegro CL so that when given no arguments, they return the list of traced functions. For trace, this is an ANSI spec requirement. Note that trace$ and untrace$ continue to return nil in the ACL2 loop. Fixed a bug that was allowing the symbol &whole to appear in other than the first argument position for a defmacro event, in violation of the Common Lisp spec (and leading to potentially surprising behavior). Thanks to Peter Dillinger for bringing this bug to our attention. It had been illegal to use make-event under some calls of ld. This has been fixed. Thanks to Jared Davis for bringing this issue to our attention with a simple example, in essence: (ld '((defmacro my-defun (&rest args) `(make-event '(defun ,@args))) (my-defun f (x) x))) ACL2 no longer prohibits certain make-event forms when including uncertified books. Thanks to Peter Dillinger for first bringing this issue to our attention. Hard errors arose when using break-rewrite stack display commands, in particular :path and :frame, from inside the proof-checker. This has been fixed. Fixed a bug that could cause functions that call system built-ins f-put-global, f-get-global, or f-boundp-global to cause a raw Lisp error even when proving theorems. Thanks to Peter Dillinger, for reporting such a failure for the form (thm (w '(1 2 3))). Renamed the formal parameters of function set-equal in distributed book books/arithmetic-3/bind-free/normalize.lisp so that more distributed books can be included together in the same session. In particular books books/data-structures/set-theory and books/arithmetic-3/extra/top-ext can now be included together. Thanks to Carl Eastlund for bringing this problem to our attention and to Robert Krug for suggesting the formals renaming as a fix. Metafunctions must now be executable. See *note META::. New utilities allow for user-defined simplifiers at the goal level, both verified and unverified ("trusted"), where the latter can even be defined by programs outside ACL2. See *note CLAUSE-PROCESSOR::, which points to a new directory books/clause-processors/ that contains examples of these new utilities, including for example a system ("SULFA") contributed by Erik Reeber that implements a decision procedure (thanks, Erik). Also see *note PROOF-CHECKER-COMMANDS:: for the new proof-checker command clause-processor (or for short, cl-proc). The rewriter has been tweaked to run faster in some cases involving very large terms. Thanks to Eric Smith and Jared Davis for providing a helpful example that helped us locate the source of this inefficiency. Added books/make-event/defspec.lisp. This book shows how one can mimic certain limited forms of higher-order statements in ACL2 by use of macros, make-event, and table events. Thanks to Sandip Ray for his contribution. A new release of the RTL library, books/rtl/rel7/, replaces the previous version, books/rtl/rel6/. Thanks to Hanbing Liu and David Russinoff for providing this new version. We thank David Russinoff for providing a proof of the law of quadratic reciprocity. See books/quadratic-reciprocity/Readme.lsp. Eliminated a slow array warning (see *note SLOW-ARRAY-WARNING::) that could occur when exiting a wormhole after executing an in-theory event in that wormhole. Thanks to Dave Greve for bringing this problem to our attention. A new accessor, (mfc-rdepth mfc), provides a new field, the remaining rewrite stack depth, which has been added to metafunction context structures; see *note EXTENDED-METAFUNCTIONS::. Thanks to Eric Smith for suggesting this addition. The algorithms were modified for collecting up rule names and other information used in proofs, into so-called "tag-trees". Tag-trees are now free of duplicate objects, and this change can dramatically speed up some proofs that involve many different rules. Thanks to Eric Smith for doing some profiling that brought this issue to our attention, and for reporting that this change reduced proof time on an example by about 47% (from 3681.46 reported seconds down to 1954.69). All legal xargs keywords may now be used in verify-termination events. In particular, this is the case for :normalize. (SBCL and CMUCL only) Fixed a problem with stobj array resizing functions that was causing a hard error in ACL2 images built on SBCL or CMUCL. A new table, evisc-table, allows you to introduce print abbreviations, for example for large constants. Moreover, a new reader macro -- #, -- makes it convenient to reference constants even inside a quote. See *note EVISC-TABLE::. Thanks to Bob Boyer and Warren Hunt for useful discussions leading to this feature. The macros in books/misc/expander.lisp now have a new keyword argument, :simplify-hyps-p. The default behavior is as before, but now case splitting from hypothesis simplification can be avoided. For details, evaluate (include-book "misc/expander" :dir :system) and then :doc! defthm? and :doc! symsym. Thanks to Daron Vroon for sending a question that prompted this additional functionality. ACL2 failed to apply :restrict hints to rules of class :definition, except for the simplest sorts (see *note SIMPLE::). This has been fixed. Thanks to Jared Davis for pointing out this bug by sending a small example. Added a new :msg argument to assert-event; see *note ASSERT-EVENT::. The implementation of value-triple has been modified to support this change. Fixed a bug in macro io? that now allows the commentp argument to be t. This provides a way other than cw to print without modifying state, for example as follows. (Warning: Certain errors may leave you in a wormhole, in which case use :a! to abort.) ACL2 !>(prog2$ (io? event t state () (fms "Howdy~%" nil *standard-co* state nil)) (+ 3 4)) Howdy 7 ACL2 !>:set-inhibit-output-lst (proof-tree event) (PROOF-TREE EVENT) ACL2 !>(prog2$ (io? event t state () (fms "Howdy~%" nil *standard-co* state nil)) (+ 3 4)) 7 ACL2 !> ACL2 now disallows calls of progn! inside function bodies, just as it already disallowed such calls of progn, since in both cases the Common Lisp meaning differs from the ACL2 meaning. Redefinition of system functions now always requires an active trust tag (see *note DEFTTAG::). This restriction was intended before, but there was a hole that allowed a second redefinition without an active trust tag. Thanks to Peter Dillinger for pointing out this bug. Verify-termination has been disabled for a few more built-in functions that are in :program mode. (If you are curious about which ones they are, evaluate (f-get-global 'built-in-program-mode-fns state).) [Note added for Version_3.4: This state global has been changed to 'program-fns-with-raw-code.] Moreover, such functions now will execute only their raw Lisp code, so for example they cannot be called during macroexpansion. Thanks to Peter Dillinger and Sandip Ray for useful discussions on details of the implementation of this restriction. New untouchable state global variables, temp-touchable-vars and temp-touchable-fns, can control the enforcement of untouchability. See *note REMOVE-UNTOUCHABLE::. Thanks to Peter Dillinger for suggesting these features. The "TTAG NOTE" string was being printed by encapsulate events whenever an active trust tag was already in effect (see *note DEFTTAG::), even if the encapsulate event contained no defttag event. This has been fixed. Thanks to Peter Dillinger for a query leading to this fix. Fixed a bug in progn! that could leave the user in raw-mode (see *note SET-RAW-MODE::). This could occur when certifying a book with a compile-flg value of t (see *note CERTIFY-BOOK::), when that book contained a progn! event setting raw-mode to t without setting raw-mode back to nil: (progn! (set-raw-mode t) ...)  File: acl2-doc-emacs.info, Node: NOTE-3-2(R), Next: NOTE-3-2-1, Prev: NOTE-3-2, Up: RELEASE-NOTES NOTE-3-2(R) ACL2 Version 3.2(r) (April, 2007) Notes Changed the default distributed books directory for ACL2(r) from books/ to books/nonstd/. See *note INCLUDE-BOOK::, in particular the discussion of "Distributed Books Directory". Added directory books/arithmetic-3/ and its subdirectories to books/nonstd/. (But a chunk of theorems from arithmetic-3/extra/ext.lisp are "commented out" using #-:non-standard-analysis because they depend on books/rtl/rel7/, which is not yet in books/nonstd/; feel free to volunteer to remedy this!) Incorporated changes from Ruben Gamboa to some (linear and non-linear) arithmetic routines in the theorem prover, to comprehend the reals rather than only the rationals. Please also see *note NOTE-3-2:: for changes to Version 3.2 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-2-1, Next: NOTE-3-2-1(R), Prev: NOTE-3-2(R), Up: RELEASE-NOTES NOTE-3-2-1 ACL2 Version 3.2.1 (June, 2007) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. (OpenMCL and multi-threaded SBCL only) Fixed a soundness bug in the evaluation of mbe forms in the presence of Lisp feature :acl2-mv-as-values. Thanks to Sol Swords for reporting this problem and sending a simple proof of nil (which can be found in a comment in the ACL2 sources, in (deflabel note-3-2-1 ...)). Added a new utility, dmr (Dynamicaly Monitor Rewrites), for watching the activity of the rewriter and some other proof processes. See *note DMR::. We thank Robert Krug for useful contributions. Fixed a bug in evaluation of calls of with-prover-time-limit. Fixed the writing of executable scripts when building ACL2, so that the build-time value of environment variable ACL2_SYSTEM_BOOKS is no longer written there. Thanks to Dave Greve for discussing this change. Fixed bugs in :pl (which are similarly present in the proof-checker's sr (show-rewrites) command. The first bug was evident from the following forms sent by Robert Krug, which caused an error. (include-book "arithmetic-3/floor-mod/floor-mod" :dir :system) :pl (mod (+ 1 x) n) The second bug was due to a failure to take note of which rules are disabled, and could be seen by executing the following (very slow!). (defstub modulus () t) (include-book "arithmetic-3/floor-mod/floor-mod" :dir :system) :pl (mod (+ x y) (modulus)) Modified certify-book so that by default, all executable-counterpart functions (sometimes called "*1* functions") are compiled. This is the behavior that was already supported with a compile-flg argument of :all; the change is that argument t now has this behavior as well (and :all is supported only for legacy purposes). A new value for compile-flg, :raw, gives the behavior formerly produced by value t, namely where executable-counterpart functions are not compiled. The above changes are irrelevant if compilation is suppressed; see *note COMPILATION::. Finally, if environment variable ACL2_COMPILE_FLG is set, then after converting to upper-case this environment variable's value of "T", "NIL", or ":RAW" will determine the value of the optional compile-flg argument to be t, nil, or :raw, respectively, when this argument is not explicitly supplied. Modified include-book so that :comp argument now acts like :comp!, i.e., compiling a file that includes the file together with all executable counterpart (so-called "*1*") functions. A new argument, :comp-raw, has the behavior that :comp had formerly, i.e., compiling the actual book only. The function nonnegative-integer-quotient is now computed in raw Lisp by calling floor on its arguments. This change was suggested by Peter Dillinger, in order to avoid stack overflows such as reported by Daron Vroon. A new book, books/misc/misc2/misc.lisp, contains a proof of equivalence of nonnegative-integer-quotient and floor, and serves as a repository for other miscellaeous proofs, including those justifying ACL2 modifications such as this one. Enhanced accumulated-persistence to break down results by useful vs. useless rule applications. In particular, this provides information about which rules were ever applied successfully, as requested by Bill Young. Added coverage of :meta rules to the accumulated-persistence statistics. Fixed a bug that was causing a :clause-processor hint to fire on a subgoal of the goal to which it was attached, when the original application didn't change the clause. Thanks to Dave Greve for pointing out this bug and providing a useful example. Fixed a bug in handling of computed hints related to the stable-under-simplificationp parameter (see *note COMPUTED-HINTS::). There were actually two bugs. A minor but confusing bug was that the same goal was printed twice upon application of such a hint. The major bug was that :use hints (as well as other "top" hints: :by, :cases, and :clause-processor) were not being applied properly. Thanks to Jared Davis for sending an example some time ago that showed the duplicate printing, and to Dave Greve for sending an example showing mis-application of :clause-processor hints. Note that you may find that existing computed hints using the stable-under-simplificationp parameter no longer have the same behavior; see a comment about computed hints in note-3-2-1, ACL2 source file ld.lisp, for an example of how you might want to fix such computed hints. David Russinoff has contributed an updated version of books/quadratic-reciprocity/ including minor modifications of the treatment of prime numbers and a proof that there exist infinitely many primes. Thanks to David for contributing this work, and to Jose Luis Ruiz-Reina for posing the challenge. Reduced the sizes of some certificate (.cert) files by relaxing the test that allows original defpkg events to be placed there, rather than evaluating the import list term into an explicit list of symbols. Improved execution efficiency slightly for function rcdp in file books/misc/records.lisp, by using mbe to introduce a tail-recursive body. The executable script generated by save-exec (and by the normal build process) now includes a time stamp as a comment. Thanks to Jared Davis for suggesting this change in order to support his use of omake. In the process, we also arranged that the startup banner for an executable created by save-exec shows all of the build (save) times, not just the one for the original image. Sped up most redundant defpkg events by avoiding evaluation and sorting of the imports list in the case of identical event forms. And, for defpkg events that are not redundant, sped up their processing in Allegro CL (and perhaps other Lisps, but apparently not GCL) by using our own import function. Modified add-include-book-dir so that it refuses to associate a keyword with a different directory string than one it is already bound to. See *note DELETE-INCLUDE-BOOK-DIR:: for how to remove the existing binding first. Thanks to Robert Krug for pointing out that without this change, one can find it difficult to debug a failure due to rebinding a keyword with add-include-book-dir. Added a new value for the :do-not-induct hint (see *note HINTS::), :otf-flg-override, which causes ACL2 to ignore the :otf-flg when considering whether to abort the proof because of a :do-not-induct hint. Thanks to Daron Vroon for suggesting such an addition. Modified the printing of messages for entering and exiting raw mode (see *note SET-RAW-MODE::), so that in particular they are inhibited during include-book or whenever observations are inhibited (see *note SET-INHIBIT-OUTPUT-LST::). Thanks to Peter Dillinger for suggesting such a change. (For system hackers only.) The handling of events of the form (progn! (state-global-let* ...)) had a bug that was causing bindings to be evaluated twice. Moreover, the use of system function state-global-let* is suspect in raw Lisp. We have eliminated special treatment of state-global-let* by progn! in favor of a new keyword argument, state-global-bindings, that provides the intended functionality. See *note PROGN!::. Moreover, special handling that allowed make-event forms under state-global-let* has been removed; the desired effect can be obtained using (progn! :state-global-bindings ...). Thanks to Peter Dillinger for pointing out the above bug and collaborating on these changes. Incorporated backward-compatible enhancements to books/misc/expander.lisp from Daron Vroon (documented near the top of that file). The specification of :backchain-limit-lst had required that only a single (:rewrite, :linear, or :meta) rule be generated. We have weakened this restriction to allow more than one rule provided that each rule has the same list of hypotheses. For example, the rule class (:rewrite :backchain-limit-lst 1) is now legal for the corollary formula (implies (f x) (and (g x) (h x))), where this was not formerly the case. Thanks to Dave Greve for bringing this issue to our attention.  File: acl2-doc-emacs.info, Node: NOTE-3-2-1(R), Next: NOTE-3-3, Prev: NOTE-3-2-1, Up: RELEASE-NOTES NOTE-3-2-1(R) ACL2 Version 3.2.1(r) (June, 2007) Notes Please also see *note NOTE-3-2-1:: for changes to Version 3.2.1 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-3, Next: NOTE-3-3(R), Prev: NOTE-3-2-1(R), Up: RELEASE-NOTES NOTE-3-3 ACL2 Version 3.3 (November, 2007) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.2.1 into new features, bug fixes, prover algorithm enhancements, and miscellaneous. Also see *note NOTE-3-2-1:: for other changes since Version 3.2. *NEW FEATURES* A new "gag-mode" provides less verbose, more helpful feedback from the theorem prover, in support of The Method (see *note THE-METHOD::). See *note SET-GAG-MODE::. We recommend the use of gag-mode, which may become the default in future ACL2 releases, and we welcome suggestions for improvement. We thank Robert Krug and Sandip Ray for helpful feedback in the design of gag-mode. Note that when proofs fail, then even without gag-mode and even if proof output is inhibited, the summary will contain a useful listing of so-called "key checkpoints" (see *note SET-GAG-MODE::). Added support for a leading `~' in filenames. Thanks to Bob Boyer for suggesting this enhancement. Note that since `~/' depends on the user, it is disallowed in books to be certified (see *note CERTIFY-BOOK::), since otherwise an include-book form in a book, b, could have a different meaning at certification time than at the time include-book is later executed on book b. Made a change to allow (time$ FORM) and (with-prover-time-limit TIME FORM) when FORM includes make-event calls that change the ACL2 world. Thanks to Jared Davis for requesting such support for time$. Computed hints (see *note COMPUTED-HINTS::) may now produce a so-called "error triple", i.e., a result of the form (mv erp val state), where a non-nil erp causes an error, and otherwise val is the value of the hint. It remains legal for a computed hint to return a single ordinary value; indeed, the symbol form of a computed hint must still be a function that returns an ordinary single value. New hints provide additional control of the theorem prover, as follows. See *note HINTS:: for more details, and see new distributed book directory books/hints/ for examples, in particular file basic-tests.lisp in that directory for simple examples. o The hint :OR (hints-1 ... hints-k) causes an attempt to prove the specified goal using each hints-i in turn, until the first of these succeeds. If none succeeds, then the prover proceeds after heuristically choosing the "best" result, taking into account the goals pushed in each case for proof by induction. o A custom hint is a keyword that the user associates with a corresponding hint-generating function by invoking add-custom-keyword-hint. Thus, a custom hint may be viewed as a convenient sort of computed hint. o A custom hint, :MERGE, is implemented in distributed book books/hints/merge.lisp. It is useful for combining hints. o A sophisticated yet useful custom hint is the :CONSIDER hint implemented in distributed book books/hints/consider-hint.lisp. With this hint, you can for example give the equivalent of a :USE hint without the need to supply an instantiation. Include that book in order to see documentation online with :doc consideration, and see the book books/hints/consider-hint-tests.lisp for examples. A new hint, :reorder, allows the specification of which subgoals are to be considered first. Thanks to Sandip Ray for putting forward this idea. Enhanced set-saved-output by supporting a second argument of :same, which avoids changing which output is inhibited. Added macros thm? and not-thm? to distributed book books/make-event/eval.lisp, so that it's easy to test within a certified book that a proof attempt succeeds or that it fails. Added printing function cw!, which is analogous to cw just as fmt! is to fmt, i.e., printing so that the result can be read back in. Thanks to Jared Davis for suggesting this enhancement (after doing his own implementation). The ACL2 customization file can now be specified using environment variable ACL2-CUSTOMIZATION [note: starting with Version_4.0, ACL2_CUSTOMIZATION]. See *note ACL2-CUSTOMIZATION::. Thanks to Peter Dillinger for requesting this feature. Added new emacs capabilities for proof trees (all documented in emacs): o New function start-proof-tree-noninteractive, for example (start-proof-tree-noninteractive "*shell*") o C-z o Switch to another frame o C-z b Switch to prooftree buffer o C-z B Switch to prooftree buffer in "prooftree-frame" frame Added Common Lisp function, search, as a macro in logic mode, with limited support for keyword arguments. Thanks to Warren Hunt for requesting this addition. Sandip Ray has contributed a book, books/make-event/defrefine.lisp, that provides a collection of macros to aid in reasoning about ACL2 functions via refinement. Wrote and incorporated new utility for listing all the theorems in an included book. See books/misc/book-thms.lisp. Thanks to Jared Davis for requesting this functionality. The new distributed book misc/defp.lisp generalizes the defpun macro to allow more general forms of tail recursion. (Low-level printing improvement) A new function, set-ppr-flat-right-margin, allows the right margin for certain kinds of "flat" printing to exceed column 40. Thanks to Jared Davis for pointing out that state global variables 'fmt-hard-right-margin and 'fmt-soft-right-margin are not alone sufficient to extend the right margin in all cases. The event add-include-book-dir can now take a relative pathname as an argument. Formerly, it required an absolute pathname. A new book, books/misc/defopener.lisp, provides a utility creating a theorem that equates a term with its simplification. ACL2 now provides limited support for the Common Lisp primitive FLET, which supports local function bindings. See *note FLET::. Thanks to Warren Hunt for requesting this feature. Added a definition of boole$, a close analogue of Common Lisp function boole. Thanks to Bob Boyer for providing an initial implementation. *BUG FIXES* Fixed defstobj to inhibit a potentially useless theory warning. Fixed a bug in the application of certify-book to relative pathnames for files in other than the current directory. Thanks to Amr Helmy for bringing this bug to our attention. Fixed a bug in :pl and :pr for displaying rules of class :meta. Thanks to Jared Davis for finding this bug and providing a fix. Formerly, set-default-backchain-limit was not a legal event form for encapsulate forms and books. This has been fixed. Thanks to Robert Krug and Sandip Ray for bringing this bug to our attention. Fixed the handling of hints in proof-checker commands for the prover, such as bash -- see *note PROOF-CHECKER-COMMANDS:: -- so that the user can override the default settings of hints, in particular of :do-not and :do-not-induct hints attached to "Goal". This fix also applies to the distributed book misc/bash.lisp, where Robert Krug noticed that he got an error with :hints (("Goal" :do-not '(preprocess))); we thank Robert for pointing out this problem. Fixed a bug in handling of stobjs occurring in guards of functions whose guards have been verified. In such cases, a raw Lisp error was possible when proving theorems about non-"live" stobjs. We thank Daron Vroon for sending us an example that highlighted this bug. The following (simpler) example causes such an error in previous versions of ACL2. (defstobj st fld) (defun foo (st) (declare (xargs :stobjs st :guard (fld st))) st) (thm (equal (foo '(3)) '(3))) The dmr feature for dynamic monitoring of rewrites had a bug, where the file used for communicating with emacs was the same for all users, based on who built the ACL2 executable image. This has been fixed. Thanks to Robert Krug for bringing this bug to our attention. Fixed a bug in some warnings, in particular the warning for including an uncertified book, that was giving an incorrect warning summary string. Inclusion of uncertified books erroneously re-used make-event expansions that were stored in stale certificates. This is no longer the case. Thanks to Jared Davis for bringing this bug to our attention. Fixed a bug that was disallowing calls of with-output in events that were executing before calling certify-book. Modified the functionality of binop-table so other than binary function symbols are properly supported (hence with no action based on right-associated arguments). See *note ADD-BINOP::. Fixed small proof-checker issues related to packages. Emacs commands ctrl-t d and ctrl-t ctrl-d now work properly with colon (`:') and certain other punctuation characters. The p-top command now prints "***" regardless of the current package. Fixed a bug that allowed certify-book to succeed without specifying value t for keyword argument :skip-proofs-okp, even with include-book events in the certification world depending on events executed under skip-proofs. Improved show-accumulated-persistence in the following two ways. Thanks to Robert Krug and Bill Young for requesting these improvements and for providing useful feedback. o It can provide more complete information when aborting a proof. o The :frames reported for a rule are categorized as "useful" and "useless" according to whether they support "useful" or "useless" :tries of that rule, respectively. See *note ACCUMULATED-PERSISTENCE:: for further explanation. Modified make-event so that the reported time and warnings include those from the expansion phase. In analogy with encapsulate and progn, the rules reported still do not include those from subsidiary events (including the expansion phase). A related change to ld avoids resetting summary information (time, warnings) with each top-level form evaluation; events already handle this information themselves. Fixed set-inhibit-output-lst so that all warnings are inhibited when warning! but not warning is included in the list. Formerly, only soundness-related warnings were inhibited in this case. Thanks to Eric Smith for bringing this bug to our attention. Distributed directory doc/HTML/ now again includes installation instructions (which was missing in Version_3.2.1), in doc/HTML/installation/installation.html. Some fixes have been made for proof-tree support. o Proof-tree output is no longer inhibited automatically during certify-book, though it continues to be inhibited by default (i.e., ACL2 continues to start up as though set-inhibit-output-lst has been called with argument '(proof-tree)). o Fixed a bug in Xemacs support for proof-tree help keys C-z h and C-z ?. o Fixed a bug in proof-trees that was failing to deal with the case that a goal pushed for proof by induction is subsumed by such a goal to be proved later. Now, the proof-tree display regards such subsumed goals as proved, as is reported in the theorem prover's output. Fixed a bug that was disallowing value-triple forms inside encapsulate forms in a certification world (see *note PORTCULLIS::). If the :load-compiled-file argument of a call of include-book is :comp, then an existing compiled file will be loaded, provided it is more recent than the corresponding book (i.e., .lisp file). A bug was causing the compiled file to be deleted and then reconstructed in the case of :comp, where this behavior was intended only for :comp!. Fixed a bug that was avoiding compilation of some executable counterparts (sometimes called "*1* functions") during certify-book, and also during include-book with :load-compiled-file value of :comp or :comp!). Thanks to Eric Smith for sending a small example to bring this bug to our attention. Incorporated a fix from Eric Smith for a typo (source function ancestors-check1) that could cause hard Lisp errors. Thanks, Eric! Fixed the following issues with packages and book certificates. See *note HIDDEN-DEATH-PACKAGE:: if you are generally interested in such issues, and for associated examples, see comments on "Fixed the following issues with packages" in note-3-3 in the ACL2 source code. o Reduced the size of .cert files by eliminating some unnecessary defpkg events generated for the portcullis. o Fixed a bug that has caused errors when reading symbols from a portcullis that are in undefined packages defined in locally included books. o Fixed a bug that could lead to failure of include-book caused by a subtle interaction between set-ignore-ok and defpkg events generated for the portcullis of a certificate. *PROVER ALGORITHM ENHANCEMENTS* Non-linear arithmetic (see *note NON-LINEAR-ARITHMETIC::) has been improved to be more efficient or more powerful in some cases. Thanks to Robert Krug for contributing these improvements. Improved certain (so-called "type-set") reasoning about whether or not expressions denote integers. Thanks to Robert Krug for contributing code to implement this change, along with examples illustrating its power that are now distributed in the book books/misc/integer-type-set-test.lisp. Improved ACL2's heuristics for relieving hypotheses, primarily to use linear reasoning on conjuncts and disjuncts of the test of an if expression. For example, given a hypothesis of the form (if (or term1 term2) ...), ACL2 will now use linear reasoning to attempt to prove both term1 and term2, not merely for term2. Thanks to Robert Krug for supplying examples illustrating the desirability of such an improvement and for useful discussions about the fix. Made a slight heuristic change, so that when a hypothesis with let or mv-let subterms (i.e. lambda subterms) rewrites to t, then that hypothesis is necessarily eliminated. Thanks to Jared Davis for sending an example that led us to develop this change, and thanks to Robert Krug for a helpful discussion. *MISCELLANEOUS* Added documentation on how to use make-event to avoid duplicating expensive computations, thanks to Jared Davis. See *note USING-TABLES-EFFICIENTLY::. Modified the error message for calls of undefined functions to show the arguments. Thanks to Bob Boyer for requesting this enhancement. Modified utilies :pr, :pr!, :pl, and :show-bodies to incorporate code contributed by Jared Davis. That code defines low-level source functions info-for-xxx that collect information about rules, which is thus available to advanced users. Dynamic monitoring of rewrites (see *note DMR::) has been improved in the following ways, as suggested by Robert Krug. o Some stale entries from the rewrite stack are no longer printed, in particular above ADD-POLYNOMIAL-INEQUALITIES. o An additional rewrite stack entry is made when entering non-linear arithmetic (see *note NON-LINEAR-ARITHMETIC::). o An ADD-POLYNOMIAL-INEQUALITIES entry is printed with a counter, to show how often this process is called. Modified save-exec so that the newly-saved image will have the same raw Lisp package as the existing saved image. This is a very technical change that will likely not impact most users; for example, the package in the ACL2 read-eval-print loop (see *note LP::) had already persisted from the original to newly-saved image. Thanks to Jared Davis for suggesting this change. Changed make-event expansion so that changes to set-saved-output, set-print-clause-ids, set-fmt-soft-right-margin, and set-fmt-hard-right-margin will persist after being evaluated during make-event expansion. (Specifically, *protected-system-state-globals* has been modified; see *note MAKE-EVENT-DETAILS::.) Thanks to Jared Davis for bringing this issue to our attention. Output from the proof-checker is now always enabled when invoking verify, even if it is globally inhibited (see *note SET-INHIBIT-OUTPUT-LST::). Improved the message printed when an :induct hint fails, to give more information in some cases. Thanks to Rex Page for suggesting where an improvement could be made and providing useful feedback on an initial improvement. Added a warning for congruence rules (see *note DEFCONG::) that specify iff as the second equivalence relation when equal can be used instead. Those who heed these warnings can eliminate certain subsequent double-rewrite warnings for rewrite rules with conclusions of the form (iff term1 term2), and hence implicitly for Boolean conclusions term1 that are interpreted as (iff term1 t). Thanks to Sarah Weissman for sending us an example that highlighted the need for such a warning. Modified macro :redef! (which is for system implementors) so that it eliminates untouchables. Several improvements have been made to the experimental hons/memoization version of ACL2. See *note HONS-AND-MEMOIZATION::. The distributed books directory, (@ distributed-books-dir), is now printed in the start-up message.  File: acl2-doc-emacs.info, Node: NOTE-3-3(R), Next: NOTE-3-4, Prev: NOTE-3-3, Up: RELEASE-NOTES NOTE-3-3(R) ACL2 Version 3.3(r) (November, 2007) Notes Please also see *note NOTE-3-3:: for changes to Version 3.3 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-4, Next: NOTE-3-4(R), Prev: NOTE-3-3(R), Up: RELEASE-NOTES NOTE-3-4 ACL2 Version 3.4 (August, 2008) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.3 into changes to existing features, new features, bug fixes, new and updated books, and Emacs support. Each change is described just once, though of course many changes could be placed in more than one category. *CHANGES TO EXISTING FEATURES* Fixed a long-standing potential infinite loop in the rewriter. Thanks to Sol Swords for sending a concise example illustrating the looping behavior. (Those interested in details are welcome to look at the comment about loop behavior in source function rewrite-equal.) Incorporated a slight strengthening of non-linear arithmetic contributed by Robert Krug (thanks, Robert). With non-linear arithmetic enabled, the problem was essentially that ACL2 made the following "optimization": given inequalities (< a u) and (< b v), for positive rational constants a and b terms u and v of which at least one is known to be rational, infer (< (* a b) (* u v)). Without this optimization, however, ACL2 now infers the stronger inequality obtained by direct multiplication of the two given inequalities. To see the effect of this change, submit the event (set-non-linearp t) followed by: (thm (implies (and (rationalp x) (< 3 x) (rationalp y) (< 4 y)) (< 0 (+ 12 (* -4 x) (* -3 y) (* x y))))) The utility set-checkpoint-summary-limit has been modified in several ways: it now takes a single argument (no longer takes state as an argument); a natural number n abbreviates the pair (n . n); the argument is no longer evaluated, but it still optionally may be quoted; and a new value, t, suppresses all printing of the checkpoint summary. Thanks to Jared Davis for suggesting most of these improvements. There was formerly a restriction on mbe that the :exec argument may not contain a call of mbe. This restriction has been removed, thanks to a request from Jared Davis and Sol Swords. Thanks also to Sandip Ray, who pointed out that this restriction may have been in place in order that defexec can guarantee termination using the :exec code; its documentation has therefore been updated to clarify this situation. Rules of class :rewrite are now stored by performing certain logical simplifications on the left side of the conclusion: (prog2$ X Y) is replaced by Y, (mbe :logic X :exec Y) is replaced by X (more precisely, the analogous change is made to the generated call of must-be-equal); and (the TYPE X) is replaced by X (again, the change is actually made on the macroexpanded form). Thanks to Jared Davis and Sol Swords for requesting this change. An analogous change has also been made for rules of class :forward-chaining. The trace$ utility has been reimplemented to work independently of the underlying Lisp trace. It thus works the same for every host Lisp, except as provided by an interface to the underlying host Lisp trace (the :native option). Note that the host Lisp trace continues to be modified for GCL, Allegro CL, and CCL (OpenMCL); see *note TRACE::. See *note TRACE$:: for updated detailed documentation on tracing options, many of which are new, for example an :evisc-tuple option that can be set to :no-print if you want the function traced without the usual entry and exit printing. The previous trace$ had some issues, including the following, which have all been fixed. Thanks to Peter Dillinger for assistance in determining desired functionality of the new trace$ and for helping to test it. Recursive calls were not always shown in the trace for two reasons. (1) Compiler inlining could prevent recursive calls from being shown in the trace, in particular in CCL (OpenMCL). Thanks to Jared Davis and Warren Hunt for pointing out this issue and requesting a fix, and to Bob Boyer and Gary Byers for relevant helpful discussions. (2) ACL2's algorithm for producing executable counterparts prevented tracing of recursive calls even after (set-guard-checking :none). Thanks to Peter Dillinger for requesting a fix. It was possible to exploit a bug in the interaction of multiple values and trace to prove a contradiction. An example is in a comment in (deflabel note-3-4 ...) in the ACL2 source code. Certain large structures could cause expensive computations for printing even when a :cond condition was specified and evaluated to nil. Trace! now suppresses printing of the event summary, and returns the value that would be returned (if there is an active trust tag) by the corresponding call of trace$. Some bugs have been fixed in the underlying native trace installed by ACL2 for GCL, Allegro CL, and CCL (OpenMCL), including the following. In GCL it had been impossible to use the variable ARGLIST in a :cond expression. In Allegro CL and CCL, a trace$ bug mishandled tracing non-ACL2 functions when directives such as :entry and :exit were supplied. GCL trace now hides the world even when tracing non-ACL2 functions. Tracing in CCL no longer causes a Lisp error when untracing a traced function defined outside the ACL2 loop; for example (trace$ len1) followed by (untrace$ len1) no longer causes an error. The macro wet has been changed, for the better we think. see *note WET::. The generation of goals for forcing-rounds has been changed to avoid dropping assumptions formerly deemed "irrelevant". (A simple example may be found in a comment in source function unencumber-assumption, source file prove.lisp.) Thanks to Jared Davis for sending us an example that led us to make this change. Modified the implementation of make-event so that in the certificate of a book, local events arising from make-event forms are elided. For example, if (make-event ) expands to (local ), then where the latter had been stored in the certificate, now instead (local (value-triple :ELIDED)) will be stored. Thanks to Eric Smith for requesting this improvement. He has reported that a preliminary version of this improvement shrunk a couple of his .cert files from perhaps 40MB each to about 140K each. Now, a table event that sets the entire table, (table tbl nil alist :clear), is redundant (see *note REDUNDANT-EVENTS::) when the supplied alist is equal to the current value of the table. Thanks to Peter Dillinger for requesting this change. The event constructor progn! now returns the value that is returned by evaluation of its final form if no error occurs, except that it still returns nil if the that final evaluation leaves ACL2 in raw-mode. :Pso and :psog have been improved so that they show the key checkpoint summary at the end of a failed proof. (For a discussion of key checkpoints, see *note SET-GAG-MODE::.) As a result, a call of set-checkpoint-summary-limit now affects subsequent evaluation of :pso and :psog. In particular, you no longer need to reconstruct a proof (by calling thm or defthm) in order to see key checkpoints that were omitted due to the limit; just call set-checkpoint-summary-limit and then run :pso or :psog. The proof-checker behaves a little differently under gag-mode. Now, proof-checker commands that call the theorem prover to create new proof-checker goals, such as bash and induct (see *note PROOF-CHECKER-COMMANDS::), will show key checkpoints when in gag-mode. As before, proof-checker commands pso and pso! (and now, also psog) -- see *note PSO::, see *note PSOG::, and see *note PSO!:: -- can then show the unedited proof log. However, unlike proof attempts done in the ACL2 loop, such proof attempts will not show a summary of key checkpoints at the end, because from a prover perspective, all such goals were considered to be temporarily "proved" by giving them "byes", to be dispatched by later proof-checker commands. A little-known feature had been that a measure of 0 was treated as though no measure was given. This has been changed so that now, a measure of nil is treated as though no measure was given. Expanded *acl2-exports* to include every documented symbol whose name starts with "SET-". Thanks to Jared Davis for remarking that set-debugger-enable was omitted from *acl2-exports*, which led to this change. The trace mechanism has been improved so that the :native and :multiplicity options can be used together for Lisps that support the trace :exit keyword. These Lisps include GCL and Allegro CL, whose native trace utilities have been modified for ACL2. For SBCL and CCL (OpenMCL), which use the built-in Lisp mechanism for returning multiple values in ACL2 (see *note MV::), the use of :multiplicity with :native remains unnecessary and will be ignored. In support of this change, the modification of native Allegro CL tracing for ACL2 was fixed to handle :exit forms correctly that involve mv. *NEW FEATURES* The command :redef! is just like :redef, but prints a warning rather than doing a query. The old version of :redef! was for system hackers and has been renamed to :redef+. Introduced a new utility for evaluating a function call using the so-called executable counterpart -- that is, executing the call in the logic rather than in raw Lisp. See *note EC-CALL::. Thanks to Sol Swords for requesting this utility and participating in its high-level design. See *note PRINT-GV:: for a new utility that assists with debugging guard violations. Thanks to Jared Davis for requesting more tool assistance for debugging guard violations. Improved the guard violation error message to show the positions of the formals, following to a suggestion of Peter Dillinger. Added new guard-debug capability to assist in debugging failed attempts at guard verification. See *note GUARD-DEBUG::. Thanks to Jared Davis for requesting a tool to assist in such debugging and to him, Robert Krug, and Sandip Ray for useful discussions. New utilities provide the formula to be proved by verify-guards. See *note VERIFY-GUARDS-FORMULA:: and see *note GUARD-OBLIGATION::, Thanks to Mark Reitblatt for making a request leading to these utilities. These utilities can be applied to a term, not just an event name; thanks to Peter Dillinger for correspondence that led to this extension. A new utility causes runes to be printed as lists in proof output from simplification, as is done already in proof summaries. See *note SET-RAW-PROOF-FORMAT::. Thanks to Jared Davis for requesting this utility. An experimental capability allows for parallel evaluation. See *note PARALLELISM::. Thanks to David Rager for providing an initial implementation of this capability. Defined xor in analogy to iff. Thanks to Bob Boyer, Warren Hunt, and Sol Swords for providing this definition. Improved distributed file doc/write-acl2-html.lisp so that it can now be used to build HTML documentation files for documentation strings in user books. See the comment in the definition of macro acl2::write-html-file at the end of that file. Thanks to Dave Greve and John Powell for requesting this improvement. It is now possible to specify :hints for non-recursive function definitions (which can be useful when definitions are automatically generated). See *note SET-BOGUS-DEFUN-HINTS-OK::. Thanks to Sol Swords for requesting such a capability. Keyword argument :dir is now supported for rebuild just as it has been for ld. We relaxed the criteria for functional substitutions, so that a function symbol can be bound to a macro symbol that corresponds to a function symbol in the sense of macro-aliases-table. So for example, a functional substitution can now contain the doublet (f +), where previously it would have been required instead to contain (f binary-+). We now allow arbitrary packages in raw mode (see *note SET-RAW-MODE::) -- thanks to Jared Davis for requesting this enhancement -- and more than that, we allow arbitrary Common Lisp in raw mode. Note however that for arbitrary packages, you need to be in raw mode when the input is read, not just when the input form is evaluated. Two new keywords are supported by the with-output macro. A :gag-mode keyword argument suppresses some prover output as is done by set-gag-mode. Thanks to Jared Davis for asking for a convenient way to set gag-mode inside a book, in particular perhaps for a single theorem; this keyword provides that capability. A :stack keyword allows sub-events of progn or encapsulate to "pop" the effect of a superior with-output call. Thanks to Peter Dillinger for requesting such a feature. See *note WITH-OUTPUT::. The command good-bye and its aliases exit and quit now all take an optional status argument, which provides the Unix exit status for the underlying process. Thanks to Florian Haftmann for sending a query to the ACL2 email list that led to this enhancement. Keyword commands now work for macros whose argument lists have lambda list keywords. For a macro with a lambda list keyword in its argument list, the corresponding keyword command reads only the minimum number of required arguments. See *note KEYWORD-COMMANDS::. It is now legal to declare variables ignorable in let* forms, as in (let* ((x (+ a b)) ...) (declare (ignorable x)) ...). Thanks to Jared Davis for requesting this enhancement. Added a warning when more than one hint is supplied explicitly for the same goal. It continues to be the case that only the first hint applicable to a given goal will be applied, as specified in the user-supplied list of :hints followed by the default-hints-table. Thanks to Mark Reitblatt for sending a question that led both to adding this clarification to the documentation and to adding this warning. You may now use set-non-linear, set-let*-abstraction, set-tainted-ok, and set-ld-skip-proofs in place of their versions ending in "p". Thanks to Jared Davis for suggesting consideration of such a change. All "set-" utilites now have a version without the final "p" (and most do not have a version with the final "p"). Added a "Loop-Stopper" warning when a :rewrite rule is specified with a :loop-stopper field that contains illegal entries that will be ignored. Thanks to Jared Davis for recommending such a warning. Added a substantial documentation topic that provides a beginner's guide to the use of quantification with defun-sk in ACL2. Thanks to Sandip Ray for contributing this guide, to which we have made only very small modifications. See *note QUANTIFIER-TUTORIAL::. Defun-sk now allows the keyword option :strengthen t, which will generate the extra constraint that that is generated for the corresponding defchoose event; see *note DEFCHOOSE::. Thanks to Dave Greve for suggesting this feature. *BUG FIXES* Fixed a soundness bug related to the use of mbe inside encapsulate events. An example proof of nil (before the fix) is in a comment in (deflabel note-3-4 ...) in the ACL2 source code. We therefore no longer allow calls of mbe inside encapsulate events that have non-empty signatures. Fixed a bug related to the definition of a function supporting the macro value-triple. Although this bug was very unlikely to affect any user, it could be carefully exploited to make ACL2 unsound: (defthm fact (equal (caadr (caddr (value-triple-fn '(foo 3) nil nil))) 'value) ; but it's state-global-let* in the logic :rule-classes nil) (defthm contradiction nil :hints (("Goal" :use fact :in-theory (disable (value-triple-fn)))) :rule-classes nil) Non-LOCAL definitions of functions or macros are no longer considered redundant with built-ins when the built-ins have special raw Lisp code, because ACL2 was unsound without this restriction! A comment about redundant definitions in source function chk-acceptable-defuns shows how one could prove nil without this new restriction. Note that system utility :redef+ removes this restriction. Although ACL2 already prohibited the use of certain built-in :program mode functions for verify-termination and during macroexpansion, we have computed a much more complete list of functions that need such restrictions, the value of constant *primitive-program-fns-with-raw-code*. Modified what is printed when a proof fails, to indicate more clearly which event failed. Fixed a problem with dmr in CCL (OpenMCL) that was causing a raw Lisp break after an interrupt in some cases. Thanks to Gary Byers for a suggestion leading to this fix. Fixed bugs in proof-checker code for dealing with free variables in hypotheses. Upon an abort, the printing of pstack and gag-mode summary information for other than GCL was avoided when inside a call of ld. This has been fixed. (Windows only) Fixed bugs for ACL2 built on SBCL on Windows, including one that prevented include-book parameters :dir :system from working, and one that prevented certain compilation. Thanks to Peter Dillinger for bringing these to our attention and supplying a fix for the second. Thanks also to Andrew Gacek for bringing include-book issues to our attention. Also, fixed writing of file saved_acl2 at build time so that for Windows, Unix-style pathnames are used. Fixed a hard Lisp error that could occur with keywords as table names, e.g., (table :a :a nil :put). Thanks to Dave Greve for bringing this problem to our attention and providing this example. Fixed handling of :OR hints so that proof attempts under an :OR hint do not abort (reverting to induction on the original input conjecture) prematurely. Thanks to Robert Krug for pointing out this problem and pointing to a possible initial fix. (SBCL and CLISP only) It is now possible to read symbols in the "COMMON-LISP" package inside the ACL2 command loop (see *note LP::). This could cause a raw Lisp error in previous versions of ACL2 whose host Common Lisp was SBCL or CLISP. Thanks to Peter Dillinger for bringing this issue to our attention. Fixed a bug that was preventing certain hints, such as :do-not hints, from being used after the application of an :or hint. Thanks to Robert Krug for bringing this bug to our attention. (Hons version only) Fixed a bug in the interaction of memoize (hons version only) with event processing, specifically in interaction with failures inside a call of progn or encapsulate. Thanks to Jared Davis for bringing this bug to our attention and sending an example. A simplified example may be found in a comment in source function table-cltl-cmd, source file history-management.lisp; search for "Version_3.3" there. Fixed cw-gstack so that its :evisc-tuple is applied to the top clause, instead of using (4 5 nil nil) in all cases. If no :evisc-tuple is supplied then (term-evisc-tuple t state) is used for the top clause, as it is already used for the rest of the stack. Fixed a bug in the interaction of proof-trees with :induct hint value :otf-flg-override. Thanks to Peter Dillinger for reporting this bug and sending an example that evokes it. Fixed bugs in :pr and find-rules-of-rune for the case of rule class :elim. Thanks to Robert Krug and Jared Davis for bringing these related bugs to our attention. Improved failure messages so that the key checkpoints are printed only once when there is a proof failure. Formerly, a proof failure would cause the key checkpoints to be printed for every encapsulate or certify-book superior to the proof attempt. Fixed a bug in generation of guards for calls of pkg-witness. Thanks to Mark Reitblatt for sending an example showing this bug. The bug can be in play when you see the message: "HARD ACL2 ERROR in MAKE-LAMBDA-APPLICATION: Unexpected unbound vars ("")". A distillation of Mark's example that causes this hard error is as follows. (defun foo (x) (declare (xargs :guard t)) (let ((y x)) (pkg-witness y))) The cond macro now accepts test/value pairs of the form (T val) in other than the last position, such as the first such pair in (cond (t 1) (nil 2) (t 3)). Thanks to Jared Davis for sending this example and pointing out that ACL2 was sometimes printing goals that have such a form, and hence cannot be submitted back to ACL2. A few macros corresponding to cond in some books under books/rtl and books/bdd were similarly modified. (A second change will probably not be noticeable, because it doesn't affect the final result: singleton cond clauses now generate a call of or in a single step of macroexpansion, not of if. For example, (cond (a) (b x) (t y)) now expands to (OR A (IF B X Y)) instead of (IF A A (IF B X Y)). See the source code for cond-macro for a comment about this change.) Fixed a bug in the interaction of proof-checker command DV, including numeric ("diving") commands, with the add-binop event. Specifically, if you executed (add-binop mac fn) with fn having arity other than 2, a proof-checker command such as 3 or (dv 3) at a call of mac could have the wrong effect. We also fixed a bug in diving with DV into certain AND and OR calls. Thanks for Mark Reitblatt for bringing these problems to our attention with helpful examples. Fixed a couple of bugs that were causing an error, "HARD ACL2 ERROR in RENEW-NAME/OVERWRITE". Thanks to Sol Swords for bringing the first of these bugs to our attention. Fixed a bug that could cause certify-book to fail in certain cases where there are local make-event forms. Fixed a bug in start-proof-tree that could cause Lisp to hang or produce an error. Thanks to Carl Eastlund for sending an example to bring this bug to our attention. Fixed a bug in the proof output, which was failing to report cases where the current goal simplifies to itself or to a set including itself (see *note SPECIOUS-SIMPLIFICATION::). Fixed a bug in with-prover-time-limit that was causing a raw Lisp error for a bad first argument. Thanks to Peter Dillinger for pointing out this bug. The following was claimed in :doc note-3-3, but was not fixed until the present release: Distributed directory doc/HTML/ now again includes installation instructions, in doc/HTML/installation/installation.html. In certain Common Lisp implementations -- CCL (OpenMCL) and LispWorks, at least -- an interrupt could leave you in a break such that quitting the break would not show the usual summary of key checkpoints. This has been fixed. *NEW AND UPDATED BOOKS* Updated books/clause-processors/SULFA/ with a new version from Erik Reeber; thanks, Erik. Added new books directory tools/ from Sol Swords. See books/tools/Readme.lsp for a summary of what these books provide. The distributed book books/misc/file-io.lisp includes a new utility, write-list!, which is like write-list except that it calls open-output-channel! instead of open-output-channel. Thanks to Sandip Ray for requesting this utility and assisting with its implementation. Added record-update macro supplied by Sandip Ray to distributed book books/misc/records.lisp. Sandip Ray has contributed books that prove soundness and completeness of different proof strategies used in sequential program verification. Distributed directory books/proofstyles/ has three new directories comprising that contribution: soundness/, completeness/, and counterexamples/. The existing books/proofstyles/ directory has been moved to its subdirectory invclock/. Jared Davis has contributed a profiling utility for ACL2 built on CCL (OpenMCL). See books/misc/oprof.lisp. Thanks, Jared. ACL2 utilities getprop and putprop take advantage of under-the-hood Lisp (hashed) property lists. The new book books/misc/getprop.lisp contains an example showing how this works. Added the following new book directories: books/paco/, which includes a small ACL2-like prover; and books/models/jvm/m5, which contains the definition of one of the more elaborate JVM models, M5, along with other files including JVM program correctness proofs. See files Readme.lsp in these directories, and file README in the latter. Added books about sorting in books/sorting. See Readme.lsp in that directory for documentation. Added book books/misc/computed-hint-rewrite.lisp to provide an interface to the rewriter for use in computed hints. Thanks to Jared Davis for requesting this feature. Jared Davis has provided a pseudorandom number generator, in books/misc/random.lisp. Robert Krug has contributed a new library, books/arithmetic-4/, for reasoning about arithmetic. He characterizes it as being more powerful than its predecessor, books/arithmetic-3/, and without its predecessor's rewriting loops, but significantly slower than its predecessor on some theorems. Incorporated changes from Peter Dillinger to verify guards for functions in books/ordinals/lexicographic-ordering.lisp (and one in ordinal-definitions.lisp in that directory). A new directory, books/hacking/, contains a library for those who wish to use trust tags to modify or extend core ACL2 behavior. Thanks to Peter Dillinger for contributing this library. Obsolete version books/misc/hacker.lisp has been deleted. Workshop contribution books/workshops/2007/dillinger-et-al/code/ is still included with the workshops/ tar file, but should be considered deprecated. In books/make-event/assert.lisp, changed assert! and assert!-stobj to return (value-triple :success) upon success instead of (value-triple nil), following a suggestion from Jared Davis. *EMACS SUPPORT* Changed emacs/emacs-acl2.el so that the fill column default (for the right margin) is only set (still to 79) in lisp-mode. Modified Emacs support in file emacs/emacs-acl2.el so that names of events are highlighted just as defun has been highlighted when it is called. Search in the above file for font-lock-add-keywords for instructions on how to eliminate this change. The name of the temporary file used by some Emacs utilities defined in file emacs/emacs-acl2.el has been changed to have extension .lsp instead of .lisp; thus it is now temp-emacs-file.lsp. Also, `make' commands to `clean' books will delete such files (specifically, books/Makefile-generic has been changed to delete temp-emacs-file.lsp).  File: acl2-doc-emacs.info, Node: NOTE-3-4(R), Next: NOTE-3-5, Prev: NOTE-3-4, Up: RELEASE-NOTES NOTE-3-4(R) ACL2 Version 3.4(r) (August, 2008) Notes Please also see *note NOTE-3-4:: for changes to Version 3.4 of ACL2. Fixed makefiles, books/nonstd/Makefile and GNUmakefile. The old set-up seemed to work fine as long as all books certified, but it was really broken, for example only certifying some of the books in books/nonstd/nsa/, and then only when required by books in other directories. Also fixed the "clean" target to clean links rather than to make links. acl2-sources/doc/EMACS/acl2-doc-emacs.info-120000664002132200015000000106502612222333542017727 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: NOTE-3-5, Next: NOTE-3-5(R), Prev: NOTE-3-4(R), Up: RELEASE-NOTES NOTE-3-5 ACL2 Version 3.5 (May, 2009) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.4 into the following categories: changes to existing features, new features, heuristic improvements, bug fixes, new and updated books, Emacs support, and experimental hons version. Each change is described in just one category, though of course many changes could be placed in more than one category. *CHANGES TO EXISTING FEATURES* Many improvements have been made to ACL2's "evisceration" mechanism for hiding substructures of objects before they are printed, and to related documentation: o A new documentation topic explains evisc-tuples. See *note EVISC-TUPLE::. o A new interface, set-evisc-tuple, has been provided for setting the four global evisc-tuples. See *note SET-EVISC-TUPLE::. o A new mode, "iprinting", allows eviscerated output to be read back in. See *note SET-IPRINT::. o Function default-evisc-tuple has been deprecated and will probably be eliminated in future releases; use abbrev-evisc-tuple instead. Also eliminated is the brr-term-evisc-tuple (also the user-brr-term-evisc-tuple). The term-evisc-tuple controls printing formerly controlled by the brr-term-evisc-tuple or user-brr-term-evisc-tuple. o ACL2 output is done in a more consistent manner, respecting the intention of those four global evisc-tuples. In particular, more proof output is sensitive to the term-evisc-tuple. Again, see *note SET-EVISC-TUPLE::. o A special value, :DEFAULT, may be provided to set-evisc-tuple in order to restore these evisc-tuples to their original settings. o (Details for heavy users of the evisc-tuple mechanism) (1) There are no longer state globals named user-term-evisc-tuple or user-default-evisc-tuple. (2) Because of the above-mentioned :DEFAULT, if you have referenced state globals directly, you should use accessors instead, for example (abbrev-evisc-tuple state) instead of (@ abbrev-evisc-tuple). (3) For uniformity, set-trace-evisc-tuple now takes a second argument, state. Improved break-on-error in several ways. First, it breaks earlier in a more appropriate place. Thanks to Dave Greve for highlighting this problem with the existing implementation. Also, break-on-error now breaks on hard errors, not only soft errors (see *note ER::, options hard and hard?). Thanks to Warren Hunt and Anna Slobodova for sending an example that showed a flaw in an initial improvement. Finally, new options cause printing of the call stack for some host Common Lisps. See *note BREAK-ON-ERROR::. Thanks to Bob Boyer for requesting this feature. Trace! may now be used in raw Lisp (though please note that all soundness claims are off any time you evaluate forms in raw Lisp!). Thanks to Bob Boyer for feedback that led to this enhancement. ACL2 now searches for file acl2-customization.lsp in addition to (and just before) its existing search for acl2-customization.lisp; See *note ACL2-CUSTOMIZATION::. Thanks to Jared Davis for suggesting this change, which supports the methodology that files with a .lisp extension are certifiable books (thus avoiding the need to set the BOOKS variable in makefiles; see *note BOOKS-CERTIFICATION-CLASSIC::). Improved the error message for illegal declare forms of the form (type (satisfies ...)). Thanks to Dave Greve for sending an example highlighting the issue. If trace output is going to a file (because open-trace-file has been executed), then a note will be printed to that effect at the time that a call of trace$ or trace! is applied to one or more trace specs. The notion of redundancy (see *note REDUNDANT-EVENTS::) has been made more restrictive for mutual-recursion events. Now, if either the old or new event is a mutual-recursion event, then redundancy requires that both are mutual-recursion events that define the same set of function symbols. Although we are not aware of any soundness bugs fixed by this modification, nevertheless we believe that it reduces the risk of soundness bugs in the future. The definition of trace* has been moved to a book, misc/trace1.lisp. A new version, used in ACL2s, is in book misc/trace-star.lisp. Trace utilities trace$ and trace! are still built into ACL2. [Note: File misc/trace1.lisp was deleted after Version 4.2.] Certain certificate files will now be much smaller, by printing in a way that takes advantage of structure sharing. Certifying the following example produces a .cert file of over 3M before this change, but less than 1K after the change. (defun nest (i) ;; Makes an exponentially-sized nest of conses i deep. (if (zp i) nil (let ((next (nest (1- i)))) (cons next next)))) (make-event `(defconst *big* ',(nest 20))) Thanks to Sol Swords for providing the above example and to him as well as to Bob Boyer, Jared Davis, and Warren Hunt for encouraging development of this improvement. We have also applied this improvement to the printing of function definitions to files on behalf of certify-book and comp. Names of symbols are now printed with vertical bars according to the Common Lisp spec. Formerly, if the first character of a symbol name could be the first character of the print representation of a number, then the symbol was printed using vertical bars (|..|) around its name. Now, a much more restrictive test for "potential numbers" is used, which can result in fewer such vertical bars. Base 16 is now carefully considered as well; see *note SET-PRINT-BASE::. Thanks to Bob Boyer for requesting this improvement. Note that macros set-acl2-print-base and set-acl2-print-case have been replaced by functions; see *note SET-PRINT-BASE:: and see *note SET-PRINT-CASE::. The ACL2 reader now supports `#.' syntax in place of the `#, syntax formerly supported. Thanks to Bob Boyer for requesting this change. See *note SHARP-DOT-READER::. NOTE that because of this change, `#.' no longer causes an abort; instead please use (a!) or optionally, if in the ACL2 loop, :a!; see *note A!::. Some small changes have been made related to gag-mode: o Gag-mode now suppresses some messages that were being printed upon encountering disjunctive splits from :OR hints. Thanks to Sol Swords for suggesting this improvement. o ACL2 had printed "Q.E.D." with all output suppressed and gag-mode enabled. Now, "Q.E.D." will be suppressed when PROVE and SUMMARY output are suppressed, even if gag-mode is enabled. o The use of set-gag-mode had drastic effects on the inhibited output (see *note SET-INHIBIT-OUTPUT-LST::), basically inhibiting nearly all output (even most warnings) when turning on gag-mode and enabling all output except proof-tree output when turning off gag-mode. Now, set-gag-mode only inhibits or enables proof (PROVE) output, according to whether gag-mode is being turned on or off (respectively). The related utility set-saved-output has also been modified, basically to eliminate :all as a first argument and to allow t and :all as second arguments, for inhibiting prover output or virtually all output, respectively (see *note SET-SAVED-OUTPUT::). A defstub event signature specifying output of the form (mv ...) now introduces a :type-prescription rule asserting that the new function returns a true-listp result. Thanks to Bob Boyer for sending the following example, which motivated this change. (defstub census (*) => (mv * *)) (defn foo (x) (mv-let (a1 a2) (census x) (list a1 a2))) Improved the efficiency of string-append so that in raw Lisp, it calls concatenate. Thanks to Jared Davis for suggesting this change, including the use of mbe. A minor change was made to the definition of concatenate to support this change, and the lemma append-to-nil was added (see below). The checksum algorithm used for certificate files of books has been changed. Thanks to Jared Davis for contributing the new code. This change will likely not be noticed unless one is using the experimental hons version of ACL2, where it can greatly speed up book certification and inclusion because of function memoization (of source function fchecksum-obj). Fewer calls are made to the checksum algorithm on behalf of certify-book and a few other operations. Thanks to Jared Davis for providing data that helped lead us to these changes. Formatted printing directives ~p, ~q, ~P, and ~Q are deprecated, though still supported. See *note FMT::. Instead, please use ~x, ~y, ~X, and ~Y (respectively). As a by-product, rule names in proof output are no longer hyphenated. A new keyword, :multiplicity, is available for tracing raw Lisp functions using the ACL2 trace utility. See *note TRACE$::. Users may now control whether or not a slow array access results in a warning printed to the screen (which is the default, as before), and if so, whether or not the warning is followed by a break. See *note SLOW-ARRAY-WARNING::. On linux-like systems (including Mac OS X and SunOS), :comp will now write its temporary files into the "/tmp" directory, which is the value of state global 'tmp-dir. You can change that directory with (assign tmp-dir ""). The messages printed for uncertified books have been enhanced. Thanks to Jared Davis for requesting such an improvement. A function definition that would be redundant if in :logic mode is now considered redundant even if it (the new definition) is in :program mode. That is, if a definition is "downgraded" from :logic to :program mode, the latter (:program mode) definition is considered redundant. Previously, such redundancy was disallowed, but we have relaxed that restriction because of a scenario brought to our attention by Jared Davis: include a book with the :logic mode definition, and then include a book with the :program mode definition followed by verify-termination. Thanks, Jared. The ACL2 reader no longer accepts characters other than those recognized by standard-char-p except for #\Tab, #\Page, and #\Rubout (though it still accepts strings containing such characters). As a result, no make-event expansion is allowed to contain any such unacceptable character or string. Thanks to Sol Swords for sending an example that led us to make this restriction. A simple example is the following book: (in-package "ACL2") (defconst *my-null* (code-char 0)) (make-event `(defconst *new-null* ,*my-null*)) For this book, a call of certify-book formerly broke during the compilation phase, but if there was no compilation, then a call of include-book broke. Now, the error occurs upon evaluation of the make-event form. ACL2 now collects up guards from declare forms more as a user might expect, without introducing an unexpected ordering of conjuncts. We thank Jared Davis for sending us the following illustrative example, explained below. (defun f (x n) (declare (xargs :guard (and (stringp x) (natp n) (= (length x) n))) (type string x) (ignore x n)) t) Formerly, a guard was generated for this example by unioning the conjuncts from the :guard onto a list containing the term (string x) generated from the type declaration, resulting in an effective guard of: (and (natp n) (= (length x) n) (stringp x)) The guard of this guard failed to be verified because (stringp x)) now comes after the call (length x). With the fix, contributions to the guards are collected up in the order in which they appear. So in the above example, the effective guard is the specified :guard; the contribution (stringp x) comes later, and is thus dropped since it is redundant. NOTE by the way that if :guard and :stobjs are specified in the same xargs form, then for purposes of collecting up the effective guard as described above, :stobjs will be treated as through it comes before the :guard. Modified close-output-channel to try to do a better job flushing buffers. Thanks to Bob Boyer for helpful correspondence. The notion of "subversive recursion" has been modified so that some functions are no longer marked as subversive. See *note SUBVERSIVE-RECURSIONS::, in particular the discussion elaborating on the notion of "involved in the termination argument" at the end of that documentation topic. Formerly, :type-prescription rules for new definitions inside encapsulate forms were sometimes added as constraints. This is no longer the case. See also discussion of the "soundness bug in the forming of constraints", which is related. *NEW FEATURES* It is now possible to affect ACL2's termination analysis (and resulting induction analysis). Thanks to Peter Dillinger for requesting this feature. The default behavior is essentially unchanged. But for example, the following definition is accepted by ACL2 because of the use of the new :ruler-extenders features; See *note RULER-EXTENDERS::. (defun f (x) (declare (xargs :ruler-extenders :all)) (cons 3 (if (consp x) (f (cdr x)) nil))) The following lemma was added in support of the improvement to string-append described above: (defthm append-to-nil (implies (true-listp x) (equal (append x nil) x))) A mechanism has been provided for users to contribute documentation. See *note MANAGING-ACL2-PACKAGES:: for an example, which contains a link to an external web page on effective use of ACL2 packages, kindly provided by Jared Davis. ACL2 documentation strings may now link to external web pages using the new symbol, ~url; see *note MARKUP::. Of course, those links appear in the web version of the documentation, but you made need to take a bit of action in order for these to appear as links in the Emacs Info version; see *note DOCUMENTATION::. Added intersectp (similar to intersectp-eq and intersectp-equal). The user now has more control over how ACL2 prints forms; See *note PRINT-CONTROL::. Thanks to Bob Boyer for useful discussions leading to this enhancement. Some Common Lisp implementations only allow the syntax pkg-name::expression when expression is a symbol. The ACL2 reader has been modified to support a package prefix for arbitrary expressions; see *note SHARP-BANG-READER::. Thanks to Hanbing Liu for a query that led to this feature and to Pascal J. Bourguignon for suggesting an implmentation. Ill-formed documentation strings need not cause an error. See *note SET-IGNORE-DOC-STRING-ERROR::. Thanks to Bob Boyer for requesting this feature. Type declarations are now permitted in let* forms; see *note LET*::, see *note DECLARE::, and see *note TYPE-SPEC::. (For Lisp programmers) Macro with-live-state has been provided for programmers who refer to free variable STATE, for example with macros that generate uses of STATE, and want to avoid compiler warnings when evaluating in raw Lisp. For example, the following form can be submitted either inside or outside the ACL2 loop to get the desired effect (see *note DOC-STRING::): (with-live-state (f-put-global 'doc-prefix " " state)). For another example use of this macro, see the definition of trace$ (ACL2 source file other-events.lisp). (System hackers only) Added :redef- to undo the effect of :redef+. See *note REDEF-::. Function random$ is a built-in random number generator. See *note RANDOM$::. Thanks to Sol Swords for requesting this feature and providing an initial implementation. *HEURISTIC IMPROVEMENTS* Sped up guard generation for some functions with large if-then-else structures in their bodies. Thanks to Sol Swords for sending an illustrative example. Sped up guard generation in some cases by evaluating ground (variable-free) subexpressions. Thanks to Bob Boyer for sending a motivating example: (defn foo (x) (case x ((1 2) 1) ((3 4) 3) ... ((999 1000) 999))). Modified slightly a heuristic association of "size" with constants, which can result in significant speed-ups in proofs involving constants that are very large cons trees. Added a restriction in the linear arithmetic procedure for deleting polynomial inequalities from the linear database. Formerly, an inequality could be deleted if it was implied by another inequality. Now, such deletion requires that certain heuristic "parent tree" information is at least as restrictive for the weaker inequality as for the stronger. Thanks to Dave Greve for bringing a relevant example to our attention and working with us to figure out some surprising behavior, and to Robert Krug for making a key observation leading to the fix. (GCL especially) Improved compiled code slightly by communicating to raw Lisp the output type when a function body is of the form (the character ...). This tiny improvement will probably only be observed in GCL, if at all. Applied a correction suggested by Robert Krug to the variant of term-order used in parts of ACL2's arithmetic reasoning. *BUG FIXES* Fixed bugs in the handling of flet expressions, one of which had the capability of rendering ACL2 unsound. Thanks to Sol Swords for pointing out two issues and sending examples. One example illustrated how ACL2 was in essence throwing away outer flet bindings when processing an inner flet. We have exploited that example to prove a contradiction, as follows: this book was certifiable before this fix. (in-package "ACL2") (defun a (x) (list 'c x)) ; Example from Sol Swords, which failed to be admitted (claiming that ; function A is undefined) without the above definition of A. (defun foo1 (x y) (flet ((a (x) (list 'a x))) (flet ((b (y) (list 'b y))) (b (a (list x y)))))) (defthm not-true (equal (foo1 3 4) '(b (c (3 4)))) :hints (("Goal" :in-theory (disable (:executable-counterpart foo1)))) :rule-classes nil) (defthm contradiction nil :hints (("Goal" :use not-true)) :rule-classes nil) Sol's second example, below, pointed to a second bug related to computing output signatures in the presence of nested flet expressions, which we have also fixed: this form failed before the fix. :trans (flet ((foo (a) (list (flet ((bar (b) b)) a)))) x) Fixed a subtle soundness bug in the forming of constraints from deduced type prescriptions. As a result, when ACL2 prints a warning message labeling encapsulated functions as "subversive", ACL2 will no longer deduce :type-prescription rules for those functions. Examples that exploit the bug in ACL2 Version_3.4 may be found in comments in ACL2 source function convert-type-set-to-term (file other-processes.lisp) and especially in function putprop-type-prescription-lst (file defuns.lisp). For more on the general issue of "subversive recursions," see *note SUBVERSIVE-RECURSIONS::.) Fixed a soundness bug in the handling of inequalities by the type-set mechanism, which was using the inequality database inside the body of a lambda. Fixed a long-standing soundness bug in compress1 and compress2, whose raw Lisp code gave the logically incorrect result in the case of a single entry other than the header, where that entry mapped an index to the default value. Also fixed soundness bugs in compress1, in the case of :order >, where the raw Lisp code could drop the header from the result or, when the input alist had entries in ascending order, fail to return an alist in descending order. For example, the following book certified successfully. (in-package "ACL2") (defthm true-formula-1 (equal (compress1 'a '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT 1 :NAME A :ORDER <) (1 . 1))) '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT 1 :NAME A :ORDER <))) :hints (("Goal" :in-theory (disable (compress1)))) :rule-classes nil) (defthm ouch-1 nil :hints (("Goal" :use true-formula-1)) :rule-classes nil) (defthm true-formula-2 (equal (compress1 'a '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT NIL :NAME A :ORDER >) (1 . 1) (2 . 2))) '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5 :DEFAULT NIL :NAME A :ORDER >) (2 . 2) (1 . 1))) :hints (("Goal" :in-theory (disable (compress1)))) :rule-classes nil) (defthm ouch-2 nil :hints (("Goal" :use true-formula-2)) :rule-classes nil) (defthm true-formula-3 (equal (compress1 'a '((:HEADER :DIMENSIONS (3) :MAXIMUM-LENGTH 4 :NAME A :ORDER >) (1 . B) (0 . A))) '((:HEADER :DIMENSIONS (3) :MAXIMUM-LENGTH 4 :NAME A :ORDER >) (1 . B) (0 . A))) :hints (("Goal" :in-theory (disable (compress1)))) :rule-classes nil) (defthm ouch-3 nil :hints (("Goal" :use true-formula-3)) :rule-classes nil) Fixed a soundness bug involving measured subsets and verify-termination, by changing verify-termination so that it uses make-event. See *note VERIFY-TERMINATION::, in particular the discussion about make-event near the end of that documentation topic. Peter Dillinger first raised the idea to us of making such a change; when we found this soundness bug, we were certainly happy to do so! Fixed a bug that could cause a hard Lisp error but not, apparently, unsoundness. The bug was in the lack of attention to the order of guard and type declarations when checking for redundancy. In the following example, the second definition was redundant during the first pass of the encapsulate form. The second definition, however, was stored on the second pass with a guard of (and (consp (car x)) (consp x)), which caused a hard Lisp error when evaluating (foo 3) due to a misguided attempt to evaluate (car 3) in raw Lisp. The fix is to restrict redundancy of definitions so that the guard and type declarations must be in the same order for the two definitions. (encapsulate () (local (defun foo (x) (declare (xargs :guard (consp x))) (declare (xargs :guard (consp (car x)))) x)) (defun foo (x) (declare (xargs :guard (consp (car x)))) (declare (xargs :guard (consp x))) x)) ; Now we get a hard Lisp error from evaluation of the guard of foo: (foo 3) Fixed a bug in the guard violation report for function intern-in-package-of-symbol. Thanks to Dave Greve for bringing this bug to our attention. Made a change to allow certain hints, in particular certain :clause-processor hints, that had previously caused errors during termination proofs by viewing the function being defined as not yet existing. Thanks to Sol Swords for bringing this issue to our attention with a nice example. ACL2 now properly handles interrupts (via control-c) issued during printing of the checkpoint summary. Previously it was possible on some platforms to make ACL2 hang when interrupting both during a proof and during the ensuing printing of the checkpoint summary. Thanks to Jared Davis and Sol Swords for bringing this problem to our attention. Fixed a bug that was preventing, inside some book "b", the use of a :dir argument to include-book that refers to a directory defined using add-include-book-dir earlier in the book "b". (We found this ourselves, but we thank John Cowles for observing it independently and sending us a nice example.) (GCL and CCL only) Fixed a bug in certain under-the-hood type inferencing. Thanks to Sol Swords for sending an example using stobjs defined with the :inline keyword, along with a helpful backtrace in CCL, and to Gary Byers for his debugging help. Fixed a bug in print-gv, which was mishandling calls of functions with more than one argument. Fixed a bug in the handling of AND and OR terms by the proof-checker command DV, including numeric ("diving") commands. Thanks for Mark Reitblatt for bringing this problems to our attention with a helpful example. Fixed printing of goal names resulting from the application of :OR hints so that they aren't ugly when working in other than the "ACL2" package. Thanks to Sol Swords for bringing this issue to our attention. Fixed proof-tree printing so that interrupts will not cause problems with hiding ordinary output because of incomplete proof-tree output. Thanks to Peter Dillinger for pointing out this issue. Fixed a hard error that could be caused by mishandling a forced hypothesis during forward-chaining. Thanks to Peter Dillinger for bringing this bug to our attention by sending a useful example. Fixed a bug that could cause simplifications to fail because of alleged "specious simplification." This bug could appear when deriving an equality from the linear arithmetic database, and then attempting to add this equality to the current goal's hypotheses when it was already present. Thanks to Eric Smith for sending a helpful example (in July 2005!) that helped us debug this issue. Fixed a bug in processing of :type-set-inverter rules. Fixed a bug that was causing an error, at least for an underlying Lisp of CCL (OpenMCL), when ec-call was applied to a term returning multiple values. Thanks to Sol Swords for sending an example that brought this bug to our attention. Fixed handling of array orders to treat keyword value :order :none correctly from an array's header. Previously, there were two problems. One problem was that :order :none was treated like the default for :order, <, while :order nil was treated in the manner specified by :order :none (see *note ARRAYS::). Now, both :order :none and :order nil are treated as :order nil had been treated, i.e., so that there is no reordering of the alist by compress1. The other problem with this case of :order was that the :maximum-length field of the header was not being respected: the length could grow without bound. Now, as previously explained (but not previously implemented) -- see *note ARRAYS:: -- a compress1 call made on behalf of aset1 causes a hard error if the header of the supplied array specifies an :order of :none or nil. An ignorable declare form had caused an error in some contexts when it should have been allowed. In particular, this problem could arise when using an ignorable declaration at the top level in a defabbrev form. It could also arise upon calling verify-termination when the corresponding defun form contained an ignorable declaration at the top level. These bugs have been fixed. Contrary to existing documentation (see *note MAKE-EVENT-DETAILS::), the value of "ld special variable" ld-skip-proofsp was always set to nil during make-event expansion, not merely when the make-event form has a non-nil value for keyword parameter :check-expansion. This has been fixed. Thanks to Sol Swords for bringing this issue to our attention. We have disallowed the certification of a book when not at the top-level, either directly in the top-level loop or at the top level of ld. Before this restriction, the following would certify a book with a definition such as (defun foo (x) (h x)) that calls function h before defining it, if the certification was by way of the form such as: (er-progn (defun h (x) x) (certify-book "my-book")) But a subsequent include-book of "my-book" would then fail, because h is not defined at the top level. Printing with fmt directive ~c now works properly even when the print-base is other than 10. Thanks to Sol Swords for reporting this bug and providing a fix for it. (SBCL, CMUCL, and CCL only) Fixed a bug in sys-call-status in the case that the underlying Common Lisp is SBCL, CMUCL, or CCL (OpenMCL). Thanks to Jun Sawada for bringing this bug to our attention and providing a fix. Fixed a bug that was preventing local defstobj events in encapsulate events. Thanks to Jared Davis for bringing this bug to our attention. Fixed a bug evidenced by error message "Unexpected form in certification world", which could result from attempting to certify a book after evaluating an encapsulate form with a local defmacro. Thanks to Jared Davis for pointing out this bug and sending the example: (encapsulate () (local (defmacro foo (x) `(table foo 'bar ,x))) (local (foo 3))) Formerly, evaluating a trace$ form inside a wormhole such as the break-rewrite loop could leave the user in a bad state after returning to the top level, in which that function could not be untraced. This has been fixed. Note however that when you proceed from a break in the break-rewrite loop, the tracing state will be the same as it was when you entered that break: all effects of calling trace$ and untrace$ are erased when you proceed from the break. A :guard of (and) is no longer ignored. Thanks to Sol Swords for bringing this bug to our attention. A bug has been fixed that could result in needlessly weak induction schemes in the case that a recursive call is made in the first argument of prog2$. This has been fixed by including prog2$ as a default ruler-extender in the new ruler-extenders feature (see above, and see *note RULER-EXTENDERS::). For details on this bug see Example 11 in distributed book books/misc/misc2/ruler-extenders-tests.lisp. (For CCL/OpenMCL on Windows) ACL2 should now build on CCL (OpenMCL) on Windows. Thanks to David Rager for bringing this issue to our attention and helping with a fix that worked for CCL 1.2, and to the CCL team for improving handling of Windows-style filenames in CCL 1.3. *NEW AND UPDATED BOOKS* See `http://code.google.com/p/acl2-books/wiki/BooksSince34' for a list of books in Version 3.5 of ACL2 but not Version 3.4. Run the shell command svn log -r 94:HEAD to see all changes to books/ since the release of Version 3.4. Here are just a few highlights. Thanks largely to Jared Davis, many Makefiles have been improved to do automatic dependency analysis. See *note BOOKS-CERTIFICATION-CLASSIC:: for how to get your own Makefiles to do this by adding a line: -include Makefile-deps. Libraries books/arithmetic-4/ and books/rtl/rel7/ have been eliminated from the default book certification (make regression), in favor of new libraries books/arithmetic-5/ and books/rtl/rel8/ contributed by Robert Krug and Hanbing Liu, respectively. They and Jun Sawada have arranged the compatibility of these libraries; i.e., it is possible to evaluate both of the following in the same session: (include-book "arithmetic-5/top" :dir :system) (include-book "rtl/rel8/lib/top" :dir :system) Library books/rtl/rel1/ is no longer certified by default (though it is still distributed in support of ACL2(r); see *note REAL::). *EMACS SUPPORT* Slightly modified Control-t e (defined in emacs/emacs-acl2.el) to comprehend the notion of an "ACL2 scope", and added Control-t o to insert a superior encapsulate defining such a scope. See the Emacs documentation for Control-t e (generally obtained after typing Control-h k). Modified distributed file emacs/emacs-acl2.el so that if you put the following two forms in your ~/.emacs file above the form that loads emacs/emacs-acl2.el, then Emacs will not start up a shell. Thanks to Terry Parks for leading us to this modification. (defvar acl2-skip-shell nil) (setq acl2-skip-shell t) *EXPERIMENTAL HONS VERSION* Bob Boyer and others have contributed numerous changes for the experimental "hons" version of ACL2 (see *note HONS-AND-MEMOIZATION::). The ACL2 state can now be queried with (@ hons-enabled) so that a result of t says that one is in the experimental hons version, while nil says the opposite.  File: acl2-doc-emacs.info, Node: NOTE-3-5(R), Next: NOTE-3-6, Prev: NOTE-3-5, Up: RELEASE-NOTES NOTE-3-5(R) ACL2 Version 3.5(r) (May, 2009) Notes Please also see *note NOTE-3-5:: for changes in Version 3.5 of ACL2. This release incorporates improvements from Ruben Gamboa in support for non-standard analysis in ACL2(r), in the following ways: ACL2(r) now supports non-classical objects that are not also numeric, e.g., non-classical cons pairs. Consequently, the built-in standard-numberp has been replaced with standardp. If f is a classical function, the value (f x1 ... xn) is guaranteed to be standard when the xi are standard. ACL2(r) can now recognize this fact automatically, using defun-std. For example, the following can be used to assert that the square root of 2 is a standard value. (defthm-std sqrt-2-rational (standardp (acl2-sqrt 2))) More generally, the expression (f x1 ... xn) can contain free variables, but the result is guaranteed to be standard when the variables take on standard variables, as in the following: (defthm-std sqrt-x-rational (implies (standardp x) (standardp (acl2-sqrt x)))) A potential soundness bug in encapsulate was fixed. Specifically, when a classical, constrained function is instantiated with a lambda expression containing free variables, it may produce non-standard values depending on the values of the free variables. This means that the functional instantiation cannot be used to justify a non-classical theorem. For example, consider the following sequence: (encapsulate ((f (x) t)) (local (defun f (x) x))) (defthm-std f-x-standard (implies (standardp x) (standardp (f x)))) (defthm plus-x-standard (implies (standardp x) (standardp (+ x y))) :hints (("Goal" :use ((:functional-instance f-x-standard (f (lambda (x) (+ x y)))))))) (defthm plus-x-eps-not-standard (implies (standardp x) (not (standardp (+ x (/ (i-large-integer))))))) (defthm nil-iff-t nil :hints (("Goal" :use ((:instance plus-x-standard (y (/ (i-large-integer)))) (:instance plus-x-eps-not-standard))))) ACL2(r) also supports the introduction of non-classical functions with defchoose. These behave just as normal functions introduced with defchoose, but they have a non-classical choice property. Finally, ACL2(r) now comes with a revamped library supporting non-standard analysis, still distributed separately as books/nonstd/. The new library uses defchoose to state more natural and useful versions of the IVT, MVT, etc. It also supports the introduction of inverse functions, e.g., logarithms. Finally, the library has much more extensive support for differentiation.  File: acl2-doc-emacs.info, Node: NOTE-3-6, Next: NOTE-3-6(R), Prev: NOTE-3-5(R), Up: RELEASE-NOTES NOTE-3-6 ACL2 Version 3.6 (August, 2009) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.5 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental hons-and-memoization version. Each change is described in just one category, though of course many changes could be placed in more than one category. Note that (starting with ACL2 Version 3.5) LispWorks is no longer supported as a platform for ACL2, as the LispWorks compiler could not handle the ACL2 sources; see comments in the ACL2 sources about "function size" being "too large". *CHANGES TO EXISTING FEATURES* In the xargs declare form, the function symbol (or symbols, plural, in the case of mutual-recursion) being defined may now be used in the specified :measure and :guard terms. Note that, the definition(s) of the new symbol(s) are still not used in the termination proof. Thanks to Daron Vroon for discussions leading to this addition for the measure and to Dave Greve for requesting such an enhancement for the guard. Processing of the :guard-hints in an xargs declare form is now delayed until the start of guard verification. As a result, the function symbol(s) being defined may now be used as one might expect in such hints, for example in an :in-theory form. Thanks to Jared Davis for suggesting that we make such an improvement and providing an example. Made a low-level change to make-event in support of the ACL2s utility "dynamic-make-event". Thanks to Peter Dillinger for explaining the issue leading to this change. Modified the failure message printed when a measure conjecture fails to be proved, to indicate whether or not the hint :ruler-extenders :all would create a different measure conjecture. Thanks to Peter Dillinger for suggesting such a modification. A call of add-default-hints had added hints to the end of the current list of default hints. Now, it adds them to the beginning of that list, so that they are tried first. However, add-default-hints now takes a keyword argument, :at-end. If that argument is supplied and evaluates to other than nil, then the previous behavior occurs. When save-exec is used to save ACL2 images, the build dates are now printed on separate lines at startup and in the executable script. Thanks To Bob Boyer for requesting some newlines. Forward-chaining rules are now generated so that every let (also let* and lambda) expression is expanded away, as is every call of a so-called "guard holder" (must-be-equal, prog2$, ec-call, the). These were formerly only expanded away in the conclusion. Thanks to Konrad Slind for a helpful email leading to this change. Current-theory now causes a hard error when applied to a name not found in the current ACL2 logical world, rather than simply returning nil. When the underlying Common Lisp is GCL, ACL2 no longer uses the #n= reader macro when writing out certain files, including certificate files. In all other Lisps, it now uses the #n= "print-circle" feature not only for certificate files and "expansion.lsp" files written for example in support of make-event, but also for files written in support of :comp. This is all managed with new state global variable print-circle-files; see *note PRINT-CONTROL::. Thanks to Dave Greve for pointing out that GCL is limited by default to 1024 indices for #n=. *NEW FEATURES* A documentation topic explains in some detail how hints work with the ACL2 proof "waterfall": see *note HINTS-AND-THE-WATERFALL::. This topic may be useful to those who write computed hints (see *note COMPUTED-HINTS::) or other advanced hints. Added a new hint keyword, :no-thanks, that avoids printing the usual "Thanks" message for hints. Thanks to Peter Dillinger for requesting this feature. Added a new hint keyword, :backtrack, that checks the goals produced by processing a goal, and can cause the goal to be re-processed using a new hint. See *note HINTS::. Thanks to Pete Manolios for a conversation that led to the idea of this hint. Added a new class of hints, override-hints, that is similar to default-hints, except that override-hints are always applied, even if the user has supplied a hint explicitly for the goal. See *note OVERRIDE-HINTS::. Thanks to Pete Manolios and Harsh Raju Chamarthi for useful discussions on this topic, including its application to testing. When a goal ready to be pushed for proof by induction is given the new hint ":do-not-induct :otf", it is indeed pushed for proof by induction, rather than causing immediate failure as in the case of the hint ":do-not-induct t". Instead, the proof fails when the goal is later picked to be proved by induction. Thanks to Peter Dillinger for discussions leading to this feature. Related to computed hints only: Each history entry in the list stored in variable HIST (see *note COMPUTED-HINTS::) now has a :CLAUSE field, which provide's access to a goal's parent, parent's parent, and so on (within the same induction and forcing round only). It is now possible to inhibit warnings produced by in-theory events and hints that occur when certain built-in definitions and executable-counterparts are disabled: just evaluate (assign verbose-theory-warning nil). Thanks to Jared Davis (and probably others in the past) for requesting such a mechanism. *HEURISTIC IMPROVEMENTS* A source function (linearize-lst) was replaced by tail-recursive code, which can avoid a stack overflow. Thanks to Dave Greve for sending a helpful example. The heuristics for limiting forward-chaining have been slightly relaxed, to allow derivations based on the occurrence of all arguments of the forward-chaining rule's conclusion in the goal (after stripping leading calls of NOT). Thanks to Dave Greve for contributing this improvement and providing a motivating example. We simplified induction schemes by eliminating each hypothesis of the form (not (equal term (quote const))) for which some other hypothesis in the same case equates term with some (presumably other) quoted constant. Thanks to Dave Greve for requesting an improvement of this sort. *BUG FIXES* Fixed a soundness bug related to redundancy of encapsulate events (see *note REDUNDANT-EVENTS::) and ruler-extenders. A proof of nil in ACL2 Version 3.5 appears in a comment in (deflabel note-3-6 ...) in the ACL2 source code. The fix is to insist that in order for one encapsulate event to be redundant with another, they must be evaluated with the same default-ruler-extenders. Analogous to this issue of default-ruler-extenders for encapsulates is an issue of the default-verify-guards-eagerness, which has similarly been fixed. Fixed soundness bugs related to the handling of subversive-recursions for constraints. Proofs of nil in ACL2 Version 3.5 appear in a comment in (deflabel note-3-6 ...) in the ACL2 source code. Fixed a bug that could cause the following error during calls of certify-book in the presence of calls of skip-proofs in the book: ACL2 Warning [Skip-proofs] in HARD ACL2 ERROR in FMT0: Illegal Fmt Syntax. The tilde-@ directive at position 0 of the string below is illegal because its variable evaluated to 0, which is neither a string nor a list. "~@0" Thanks to Dave Greve for reporting this bug and making available a very helpful test case. The :corollary of a rule (see *note RULE-CLASSES::) failed to use the default-hints of the logical world. This has been fixed. (CCL only) We removed a call, for CCL 1.3 (and beyond) only, of foreign function sync in the closing of output channels. Thanks to Daron Vroon for reporting issues with such a call on a non-Intel platform. Fixed a bug in reporting failures when monitoring rewrite rules with free variables in the hypotheses, that could cause a hard Lisp error (from which ACL2 continues, however). Thanks to Eric Smith for sending a very helpful example with his bug report. Fixed the handling of :induct hints, which had thrown away hint information from parent goals. For example, the thm form below failed to prove even though the second hint is in some sense superfluous; induction occurs automatically at "Goal'" even without the hint on that. The failure was due to discarding the hint information on "Goal". (in-theory (disable append)) (thm (equal (cdr (cons a (append (append x y) z))) (append x y z)) :hints (("Goal" :in-theory (enable append)) ("Goal'" :induct t) ; failed unless this line is commented out )) Fixed a bug in the args command that was failing to show the formals of primitives (built-in functions like consp that do not come with explicit definitions). Thanks to John Cowles for pointing out this bug. (At a lower level, the bug was that primitives failed to have 'stobjs-in or 'stobjs-out properties.) Fixed bugs in the utility supporting moving directories of certified books, sometimes used in Debian builds (as described in source function make-certificate-file). Thanks to Alan Dunn for pointing out such a bug, in paths associated with defpkg events in portcullis commands in certificates (which are used for error reporting). There were also bugs, now fixed, that prevented renaming some book paths. Please note that this mechanism is not guaranteed to be sound; in particular, it can probably misbehave when macros are used to generate portcullis events. However, it seems likely that such issues will be very rare. Eliminated warnings that could arise when tracing a function with trace$. Now, when trace$ is applied to a function without option :native, that function's declarations and documentation are discarded. Fixed a bug that could cause a failure when building an executable image using SBCL as the underlying Common Lisp. Thanks to Jun Sawada for reporting this failure. We made a similar fix for CMUCL. Fixed a bug in save-exec in the case that an absolute pathnanme is supplied. Thanks to Jared Davis for bringing this bug to our attention. Fixed a bug in the use of trace$ with the :native and :multiplicity options that caused hard errors for some underlying Lisps. Fixed a bug in the interaction of trace$ and :comp, which caused error as comp tried to re-trace functions that it temporarily untraced. *NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE* See `http://code.google.com/p/acl2-books/wiki/BooksSince35' for a list of books in Version 3.6 of ACL2 but not Version 3.5. For _changes_ to existing books rather than _additions_, see the log entries at `http://code.google.com/p/acl2-books/source/list' starting with revision r286 up through revision r329. It is no longer required to specify a value for environment (or `make') variable ACL2_SYSTEM_BOOKS when running `make' in the distributed book directory, even when that directory is not under the directory containing the ACL2 executable. Thanks to Peter Dillinger for providing this improvement, by modifying books/Makefile-generic and, as needed, distributed book Makefiles. Thanks to Peter Dillinger, some books in support of the ACL2 Sedan (ACL2s) are more easily available for ACL2 users who do not use ACL2s. See *note ACL2-SEDAN::. *EMACS SUPPORT* If the following form is evaluated before the load of emacs/emacs-acl2.el, then variables are now set to reflect the directory containing that file. (if (boundp '*acl2-sources-dir*) (makunbound '*acl2-sources-dir*)) Fixed info-dir-entry line in generated info file (by patching doc/write-acl2-texinfo.lisp). Thanks to Alan Dunn for providing this patch. *EXPERIMENTAL HONS VERSION* Bob Boyer and others have contributed numerous changes for the experimental "hons" version of ACL2 (see *note HONS-AND-MEMOIZATION::). A number of these have been crafted to work specifically with CCL (Clozure Common Lisp, formerly OpenMCL), which is now required as the underlying Lisp for the "hons" version of ACL2. A heuristic (source function too-many-ifs has been made more scalable (for the non-HONS version as well, in fact), but with no functional change. Thanks to Jared Davis for noticing performance issues and suggesting fixes. Other changes including the following, quoting Bob Boyer: The CCL CASE macro now does better than a dumb linear search in some CASEes. SH and CSH are functions to talk to the underlying Gnu-Linux from CCL. Good for repeated calling when you simply cannot afford the copying cost of a FORK because you are using, say, a dozen gigabytes. Added CCL compiler-macros for IF and OR, to support some 'coverage' analysis, cf. IF-REPORT, extending the profiling. Introduced the type 'mfixnum' so that things like counting honses and counting calls to memoized or profiled functions can run fast in CCL 64 bits and yet still run at all under 32 bits. Moved all HONSes to CCL's newish static space, which permits the address of a cons to be used as a hash key, as in most Lisps. (CCL moves most conses and most everything when it does a compacting-gc.) Quite a few changes in the memoize-fn reporting. Added a timer facility, cf. call-with-timeout. Good for running under throttle some gross thoughts like 'Is it true that you can't fit 12 pigeons into 11 holes' on some propositional calculus systems/functions. Added rwx-size, pid-owner-cmdlines, rss, and proc-stat to help see what is really going on virtually in Gnu-Linux. Fixed at least one bug in compact-print-file and helped make its integration into ACL2's read-object$ a little more sound. Still worried some about *print-readably* vs. readtable-case. Does anyone else stay awake late at night worrying about readtable-case? Revised how the *watch-dog-process* interrupts the main process for the thousandth time, cf. watch. Haven't changed it in weeks, which means that (a) it is getting tolerable or (b) I've run out of gas.  File: acl2-doc-emacs.info, Node: NOTE-3-6(R), Next: NOTE-3-6-1, Prev: NOTE-3-6, Up: RELEASE-NOTES NOTE-3-6(R) ACL2 Version 3.6(r) (August, 2009) Notes Please also see *note NOTE-3-6:: for changes in Version 3.6 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-3-6-1, Next: NOTE-4-0, Prev: NOTE-3-6(R), Up: RELEASE-NOTES NOTE-3-6-1 ACL2 Version 3.6.1 (September, 2009) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. The essential changes to ACL2 since Version 3.6 are the two bug fixes described below. There was also some functionality-neutral code refactoring, as requested by Daron Vroon in support of the ACL2 Sedan (see *note ACL2-SEDAN::). Also see `http://code.google.com/p/acl2-books/wiki/BooksSince36' for a list of books in Version 3.6.1 of ACL2 but not Version 3.6. For _changes_ to existing books rather than _additions_, see the log entries at `http://code.google.com/p/acl2-books/source/list' starting with revision r329 up through revision 350. Fixed a soundness bug in the handling of ruler-extenders, specifically in the handling of LET-expressions. Thanks to Pete Manolios, who sent us a proof of nil, essentially as follows. In the termination proof for foo below, the binding of x to (cons t x) was not substituted into the recursive call of foo for purposes of the termination proof. (defun foo (x) (declare (xargs :ruler-extenders :all)) (let ((x (cons t x))) (if (endp (cdr x)) x (cons t (foo (cdr x)))))) (defthm foo-bad nil :hints (("Goal" :use ((:instance foo (x '(3)))) :in-theory (disable foo (foo)))) :rule-classes nil) Fixed a typo in code supporting ruler-extenders (specifically, swapped arguments in a recursive call of ACL2 source function get-ruler-extenders2, which could cause problems for functions defined using mutual-recursion). Thanks to Daron Vroon for bringing this bug to our attention, pointing out the swapped arguments.  File: acl2-doc-emacs.info, Node: NOTE-4-0, Next: NOTE-4-0(R), Prev: NOTE-3-6-1, Up: RELEASE-NOTES NOTE-4-0 ACL2 Version 4.0 (July, 2010) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 3.6.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. Also see *note NOTE-3-6-1:: for other changes since Version 3.6. *CHANGES TO EXISTING FEATURES* There have been extensive changes to the handling of compiled files for books, which may generally be invisible to most users. The basic idea is that when compiled files are loaded on behalf of include-book, they are now loaded before events in the book are processed, not afterwards. This can speed up calls of include-book, especially if the underlying Lisp compiles all definitions on the fly (as is the case for CCL and SBCL). One functional change is that for keyword argument :load-compiled-file of include-book, the values :comp-raw, :try, and :comp! are no longer legal. (Note that the handling of :comp-raw was actually broken, so it seems that this value wasn't actually used by anyone; also, the handling of :comp! formerly could cause an error in some Lisp platforms, including SBCL.) Another change is that if include-book is called with :load-compiled-file t, then each sub-book must have a compiled file or a so-called "expansion file"; see *note BOOK-COMPILED-FILE::. In the unlikely event that this presents a problem, the makefile provides a way to build with compilation disabled; see *note COMPILATION::. Users of raw mode (see *note SET-RAW-MODE::) will be happy to know that include-book now works if there an up-to-date compiled file for the book, since portcullis commands are now incorporated into that compiled file. The mechanism for saving expansion files has changed, and the :save-expansion argument of certify-book has been eliminated; see *note CERTIFY-BOOK::. More discussion of ACL2's new handling of book compilation is described in a new documentation topic; see *note BOOK-COMPILED-FILE::. It was possible to get a hard Lisp error when certifying a book with a redundant defconst form whose term is not identical to the existing defconst form for the same name. The following example illustrates the problem, which has been fixed (as part of the change in handling of compiled files for books, described above). Imagine that after the initial (in-package "ACL2") form, file foo.lisp has just the form (defconst *a* (append nil nil)). Then before the fix, we could have: ACL2 !>(defconst *a* nil) [[output omitted]] ACL2 !>(certify-book "foo" 1) [[initial output omitted] * Step 5: Compile the functions defined in "/v/joe/foo.lisp". Compiling /v/joe/foo.lisp. End of Pass 1. End of Pass 2. OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3 Finished compiling /vjoe/foo.lisp. Loading /v/joe/foo.lisp Error: Illegal attempt to redeclare the constant *A*. The wormhole facility has been changed to repair a bug that allowed guard violations to go undetected. The major change has to do with the treatment of what used to be called the "pseudo-flag" argument which has been replaced by a quoted lambda expression. See *note NOTE-4-0-WORMHOLE-CHANGES:: for help in converting calls of wormhole. Also see see *note WORMHOLE:: and see *note WORMHOLE-EVAL::. The function assign-wormhole-output has been eliminated but its functionality can be provided by wormhole-eval. The ACL2 tutorial has been greatly expanded, for example to include a self-contained discussion of rewriting. See *note ACL2-TUTORIAL::. Formerly, the mbe macro and must-be-equal function were disallowed in any definition within an encapsulate having a non-empty signature. Now, these are allowed provided the definition has been declared to be non-executable (see *note DEFUN-NX::). As a result, defevaluator events may now include must-be-equal among the function symbols known by the evaluator; this had not previously been allowed. Thanks to Sol Swords for discussions leading to this relaxation for defevaluator. Princ$ now prints strings more efficiently. Thanks to Jared Davis for suggesting the improvements to princ$. The use of xargs declaration :non-executable t no longer requires the absence of state or declared stobjs among the formal parameters of a function definition. As before, the use of :non-executable t turns off single-threadedness checking for the body, and also as before, attempts to execute the function will fail. Thanks to Sol Swords for requesting this relaxation (for automatic generation of so-called "flag functions" for definitions using mutual-recursion). The documentation has been improved for explaining to advanced users the details of the ACL2 hint mechanism; see *note HINTS-AND-THE-WATERFALL::, and see the example about nonlinearp-default-hint in distributed book books/hints/basic-tests.lisp. Thanks to Robert Krug for useful discussions, in particular suggesting the above example as one to be explained with the documentation. The time$ macro has been enhanced to allow user control of the timing message that is printed, and of when it is printed. See *note TIME$::. Thanks to Jared Davis for providing the essential design, helpful documentation (largely incorporated), and an initial implementation for raw Lisp. The :ttags argument to include-book had been required when including a certified book that uses trust tags. This is no longer the case: essentially, :ttags defaults to :all except that warnings will be printed. Thanks to Jared Davis for requesting such a relaxation, and to him and Sandip Ray for useful discussions. The definition of mv-let has been modified so that the single-step macroexpansion (see *note TRANS1::) of its calls can be evaluated. Thanks to Pete Manolios for bringing this evaluation issue to our attention and ensuing discussions. All calls of so-called "guard-holders" -- prog2$, must-be-equal (from calls of see *note MBE::), ec-call, and mv-list -- are now removed before storing hypotheses of rules of class :rewrite or :linear. Thanks to Sol Swords for requesting this enhancement and sending the following example in the case of rewrite rules. (defthm foo (prog2$ (cw "asdf") (and (equal (car (cons x y)) x) (equal (cdr (cons x y)) y)))) The handling of fmt directive ~s has been modified so that if the argument is a symbol that would normally be printed using vertical bars (|), then that symbol is printed as with ~f. Thanks to Jared Davis for providing the following example showing that treatment of ~s was a bit unexpected: (cw "~s0.~%" '|fo\|o|). Error messages have been improved for ill-formed terms (in ACL2's so-called "translation" routines). Thanks to Jared Davis for requesting such an enhancement. Modified defun-sk so that it executes in :logic mode. Previously, evaluation of a defun-sk event in :program mode caused a somewhat inscrutable error, but now, :program mode is treated the same as :logic mode for purposes of defun-sk. The "system hacker" commands (redef+) and (redef-) are now embedded event forms (see *note EMBEDDED-EVENT-FORM::), hence may be used in books as well as in progn and encapsulate events. Also, these two commands are now no-ops in raw Lisp. The function symbol worldp (in the "ACL2" package) has been renamed to plist-worldp. The function gc$-fn (resulting from macroexpansion of gc$) is now in :logic mode. Thanks to Jared Davis for requesting this change. The user now has control over whether compilation is used, for example whether or not certify-book compiles by default, using function set-compiler-enabled. See *note COMPILATION::. Modified the conversion of relative to absolute pathnames in portcullis commands for book certification. Now, more pathnames remain as relative pathnames. The "Ttags" warning that can be printed by include-book is now given even if set-inhibit-output-lst has specified `warning'. To suppress it, specify warning! instead, for example, (set-inhibit-output-lst '(acl2::warning! acl2::proof-tree)). On occasion, ACL2 prints the message "Flushing current installed world" as it cleans up when certain actions (installing a world) are interrupted. This operation has been sped up considerably. If your session includes many events, you can probably speed up any such operation further by invoking reset-prehistory. Thanks to Jared Davis for sending a query that led us to make this improvement. Calls of the form (ec-call (must-be-equal logic exec)) are no longer allowed, since we do not have confidence that they would be handled correctly. The underlying function for good-bye (and hence for exit and quit) is now in :logic mode. Thanks to Jared Davis for requesting this enhancement. We now require that every function symbol in the signature of an encapsulate event have a :logic mode definition at the end of the first pass, not merely a :program mode definition (which formerly was sufficient). You can still define such a function in :program mode, provided it is followed by a :logic mode definition (where of course both definitions are local, since we are discussing functions is introduced in the signature). Thanks to Carl Eastlund for bringing this issue to our attention. (Note: An analogous modification has been made for :bdd hints as well.) The following functions now have raw Lisp implementations that may run faster than their ACL2 definitions: assoc-eq, assoc-equal, member-eq, member-equal, subsetp-eq, subsetp-equal, remove-eq, remove-equal, position-eq, and position-equal. Thanks to Jared Davis for suggesting that we consider such an improvement. We now avoid infinite loops caused when tracing functions that implement trace$. Thanks to Rob Sumners and Eric Smith for useful discussions. The implementation of trace! has been modified slightly, to accommodate the fix for "some holes in the handling of trust tags" described later, below. This item applies unless the host Lisp is GCL. An interrupt (control-c) will now cause a proof to exit normally in most cases, by simulating a timeout, as though with-prover-time-limit had been called with a time-limit of 0. If the first interrupt doesn't terminate the proof, a second one should do so (because a different, more "severe" mechanism is used after the first attempt). As a result, redo-flat should work as one might expect even if a proof is interrupted. Thanks to Dave Greve for requesting this enhancement to redo-flat. Technical note: for reasons related to this change, time-limits are no longer checked in evaluator functions (ev-fncall, ev, ev-lst, ev-fncall-w, ev-w, and ev-w-lst). It is now legal for proof-checker macro-commands to appear in :instructions that are used in place of :hints. Thanks to Sandip Ray for (most recently) requesting this feature. The value of :command-conventions for ld special variable ld-post-eval-print is now treated as though it were t if the value ld special variable ld-error-triples is nil. The following example illustrates this change. ACL2 !>(ld-post-eval-print state) ; default :COMMAND-CONVENTIONS ACL2 !>(ld-error-triples state) ; default T ACL2 !>(set-ld-error-triples nil state) *** Then, before the change: ACL2 !>(mv t 3 state) 3 *** Instead, after the change: ACL2 !>(mv t 3 state) (T 3 ) The default behavior of ld has been changed. Formerly when an error occurred that halted a subsidiary call of ld, then the parent ld would continue. That is no longer the case. Consider the following example. (ld '((ld '((defun f (x) x) (defun bad (x)) ; ERROR -- missing the body )) (defun g (x) x))) Formerly, g would be defined in the resulting logical world. Now, the error halts not only the inner ld but also the outer ld. See *note LD::, and for details of the new default value for :ld-error-action, :RETURN!, see see *note LD-ERROR-ACTION::. Also see the paragraph below about a new utility, :p!. Thanks to Robert Krug and Sandip Ray for helpful discussions. Environment variable ACL2-CUSTOMIZATION has been replaced by ACL2_CUSTOMIZATION -- that is, the hyphen has been replaced by an underscore -- so that it can be set conveniently in the bash shell. See *note ACL2-CUSTOMIZATION::. The "Warnings:" summary is now omitted when there are no warnings, where formerly "Warnings: None" was printed. Thanks to Jared Davis for suggesting this change. We have modified the generation of constraints for encapsulate events in two primary ways, neither of them likely to affect many users. One change is that the virtual movement of definitions and theorems to in front of an encapsulate event, or of definitions to behind that event, is no longer inhibited in the case of nested encapsulates with non-empty signatures. The following example illustrates the other change, as discussed below. (encapsulate ((f (x) t)) (local (defun f (x) x)) (defun g (x) (cons x (f x))) (defun h (x) (g x)) (defthm h-is-f (equal (car (h x)) x))) Previously, the constraint on f and h was essentially the conjunction of the definition of h and the theorem h-is-f. Now, the definition of g is conjoined as well; moreover, g receives the same constraint as do f and h, where previously g was only constrained by its definition. While we are not aware of a soundness bug caused by the previous approach, the new approach follows more precisely the intended notion of constraint. The use of trace$ (or trace!) option :multiplicity had been required when option :native was supplied. This is no longer the case. Also, a bug has been fixed that had prevented :multiplicity from working properly in GCL and Allegro CL. Several errors have been eliminated that formerly occurred when the constraints for a function symbol were unknown because it was constrained using a dependent clause-processor (see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR::. Now, it is assumed that the supporters argument in a define-trusted-clause-processor event is such that every ancestor of any function symbol constrained by the "promised encapsulate" of that event among, or ancestral in, those supporters. Thanks to Sol Swords, Sandip Ray, and Jared Davis for helpful discussions. The notion of constraint for functions introduced by defun has been modified slightly. No longer do we remove from the body of the definition calls of so-called "guard-holders": prog2$, must-be-equal, ec-call, and mv-list, and uses of the-error generated by the. Also, we now expand calls of the-error with the same aggressive heuristics applied to a number of other functions (technically, adding it to the list *expandable-boot-strap-non-rec-fns*). *NEW FEATURES* A new event, defattach, allows evaluation of calls of constrained (encapsulated) functions. In particular, users can now, in principle, soundly modify ACL2 source code; please feel free to contact the ACL2 implementors if you are interested in doing so. See *note DEFATTACH::. Eric Smith has noticed that if you exit the break-rewrite loop using :a! during an ld of a file, then all changes to the logical world are discarded that were made during that call of ld. A new utility, :p!, pops just one level instead, and avoids discarding that work. (This change is related to an item above, "The default behavior of ld has been changed.") Thanks to Eric for pointing out this issue. New function mv-list is the identity function logically, but converts multiple values to lists. The first argument is the number of values, so an example form is as follows, where foo returns three values: (mv-list 3 (foo x y)). Thanks to Sol Swords for requesting this feature and for reporting a soundness bug in one of our preliminary implementations. A new state global variable, host-lisp, has as its value a keyword whose value depends on the underlying Common Lisp implementation. Use (@ host-lisp) to see its value. It is now possible to write documentation for HTML without error when there are links to nonexistent documentation topics. See the comments in macro acl2::write-html-file at the end of file doc/write-acl2-html.lisp. When there are such errors, they should be easier to understand than previously. Thanks to Alan Dunn for providing the initial modifications. It is now possible to inhibit specified parts of the Summary printed at the conclusion of an event. See *note SET-INHIBITED-SUMMARY-TYPES::. Also see *note WITH-OUTPUT::, in particular the discussion of the new :summary keyword. Thanks to Sol Swords for requesting more control over the Summary. A new :hints keyword, :case-split-limitations, can override the default case-split-limitations settings (see *note SET-CASE-SPLIT-LIMITATIONS::) in the simplifier. Thanks to Ian Johnson for requesting this addition and providing an initial implementation. It is now possible to defer and avoid some ttag-related output; see *note SET-DEFERRED-TTAG-NOTES::. Thanks to Jared Davis for requesting less verbosity from ttag-related output. A new command, :pl2, allows you to restrict the rewrite rules printed that apply to a given term. See *note PL2::. Thanks to Robert Krug for requesting such a capability. ACL2 now provides a utility for canonicalizing filenames, so that soft links are resolved; see *note CANONICAL-PATHNAME::. Moreover, ACL2 uses this utility in its own sources, which can eliminate some issues. In particular, include-book with argument :ttags :all no longer breaks when given a book name differing from the book name that was used at certification time; thanks to Sol Swords for reporting that problem. Also, certain errors have been eliminated involving the combination of packages in the certification world and trust tags; thanks to Jared Davis for sending an example of that problem. You can now suppress or enable guard-checking for an individual form; see *note WITH-GUARD-CHECKING::. Thanks to Sol Swords for requesting this feature. The walkabout utility has been documented (thanks to Rob Sumners for suggesting this documentation). This utility can make it easy to explore a large cons tree. New interactive commands (pp n) and (pp print-level print-length) have been added to restrict how much of the current object is displayed. See *note WALKABOUT::. Rules of class :type-prescription may now be provided a :backchain-limit-lst keyword. The default behavior is unchanged, but now type-set is sensitive not only to the new :backchain-limit-lst of a :type-prescription rule (if supplied) but to the default-backchain-limit of the current logical world. Setting of backchain-limits can now specify either the new (type-set) limit or the old limit (for rewriting); see *note SET-DEFAULT-BACKCHAIN-LIMIT:: and see *note SET-BACKCHAIN-LIMIT::. Moreover, the functions default-backchain-limit and backchain-limit now take a second argument of :ts or :rewrite to specify which backchain-limit is desired. *HEURISTIC IMPROVEMENTS* The so-called "too-many-ifs" heuristic has been modified. Such a heuristic has been employed in ACL2 (and previous Boyer-Moore provers) for many years, in order to limit the introduction of calls of IF by non-recursive functions. Most users need not be concerned with this change, but two proofs in the regression suite (out of thousands) needed trivial adjustment, so user proofs could need tweaking. In one application, this modification sped up proofs by 15%; but the change in runtime for the regression suite is negligible, so such speedups may vary. Thanks to Sol Swords for providing a test from ACL2 runs at Centaur Technology, which was useful in re-tuning this heuristic. Guard proof obligations could have size quadratic in the number of clauses in a case statement. This inefficiency has been removed with a change that eliminates a hypothesis of the form (not (eql term constant)) when there is already a stronger hypothesis, equating the same term with a different constant. Thanks to Sol Swords for bringing this problem to our attention and suggesting an alternate approach to solving it, which we may consider in the future if related efficiency problems persist. We adjusted the heuristics for determining induction schemes in the presence of ruler-extenders, when handling calls of a function symbol that is a ruler-extender, in either of two cases: either the function takes only one argument; or the function is prog2$ or ec-call, and the first argument contains no recursive call. These cases are treated more directly as though the ruler-extender call is replaced by the unique (in the case of prog2$ and ec-call, the second) argument. A new :type-prescription rule, true-listp-append, has been added: (implies (true-listp b) (true-listp (append a b))) If you are interested in the motivation for adding this rule, see comments in true-listp-append in ACL2 source file axioms.lisp. The use of :forward-chaining lemmas has been improved slightly. In previous versions, a conclusion derived by forward chaining was discarded if it was derivable by type-set reasoning, since it was "already provable." But this heuristic prevented the conclusion from triggering further forward chaining. This has been fixed. Thanks to Dave Greve for pointing out this problem. The fundamental utility that turns an IF expression into a set of clauses has been optimized to better handle tests of the form (equal x 'constant) and their negations. This eliminates an exponential explosion in large case analyses. But it comes at the inconveience of sometimes reordering the clauses produced. The latter aspect of this change may require you to change some Subgoal numbers in proof hints. We apologize for the inconvenience. Certification can now run faster (specifically, the compilation phase) for books with very large structures generated by make-event, when there is significant sharing of substructure, because of a custom optimization of the Lisp reader. Thanks to Sol Swords for bringing this efficiency issue to our attention. Jared Davis reported inefficiency in certain make-event evaluation due to a potentially expensive "bad lisp object" check on the expansion produced by the make-event. This check has been eliminated except in the case that the expansion introduces packages (for example, by including a book during the expansion phase that introduces packages). Thanks to Jared for providing a helpful example. The application of rules of class :induction had the potential to loop (as commented in ACL2 source function apply-induction-rule). This has been fixed. Thanks to Daron Vroon and Pete Manolios for sending nice examples causing the loop. Heuristics have been tweaked so that false goals may be simplified to nil that had formerly been left unchanged by simplification, perhaps resulting in useless and distracting proofs by induction. Thanks to Pete Manolios for pointing out this issue by sending the following example: (thm (<= (+ 1 (acl2-count x)) 0)). (Technical explanation: When every literal in a clause simplifies to nil, even though we might not normally delete one or more such literals, we will replace the entire clause by the false clause.) Improved the efficiency of the built-in function, take. Thanks to Bob Boyer for suggesting this improvement. ACL2 can now use evaluation to relieve hypotheses when applying :type-prescription rules. Thanks to Peter Dillinger and Dave Greve for requesting this enhancement, and to Robert Krug for a relevant discussion long ago. Evaluation has been sped up during theorems for calls of mv-let, by avoiding repeated evaluation of the expression to which its variables are bound. Thanks to Sol Swords for requesting this improvement and sending an illustrative example. Modified a heuristic to avoid the opening up non-recursive function calls on calls of hide involving if-expressions. For example, the thm form below is now admitted (defun bar (x) (cons x x)) (thm (equal (bar (hide (if a b c))) (cons (hide (if a b c)) (hide (if a b c))))) *BUG FIXES* Fixed a soundness bug in destructor elimination, which was preventing some cases from being generated. Thanks to Eric Smith for reporting this bug and sending a helpful example. (Technical detail: the fixes were in ACL2 source functions apply-instantiated-elim-rule and eliminate-destructors-clause1, and comments in the former contain Eric's example.) Fixed a bug that supported a proof of nil by exploiting the fact that portcullis commands were not included in check-sum computations in a book's certificate. For such a proof of nil, see the relevant comment in the ACL2 source file ld.lisp under (deflabel note-4-0 ...). Changed the implementation of add-include-book-dir. The previous implementation could allow relative pathnames to be stored in the portcullis commands of certificates of books, which perhaps could lead to unsoundness (though we did not try to exploit this to prove nil). Thanks to Jared Davis for reporting a bug in our first new implementation. An additional change to both add-include-book-dir and delete-include-book-dir is that these now work in raw-mode (see *note SET-RAW-MODE::). (Thanks to Dave Greve for suggesting a reduction in the warnings we produced related to raw-mode.) Note that it is no longer permitted to make a direct call of the form (table acl2-defaults-table :include-book-dir-alist ...); use add-include-book-dir instead. Fixed a soundness bug related to xargs keyword :non-executable. New macros, defun-nx and defund-nx, have been provided for declaring functions to be non-executable; see *note DEFUN-NX::. While we expect this bug to occur only rarely if at all in practice, the following example shows how it could be evoked. ;;;;;;;;;;;;;;;;;;;; ;;; Book sub.lisp ;;;;;;;;;;;;;;;;;;;; (in-package "ACL2") (defun f () (declare (xargs :guard t :non-executable t)) (mv-let (a b c) (mv 3 4) (declare (ignore a b)) c)) (defun g () (declare (xargs :guard t)) (prog2$ (mv-let (x y z) (mv 2 3 4) (declare (ignore x y z)) nil) (f))) (defthm g-nil (equal (g) nil) :hints (("Goal" :in-theory (disable (f)))) :rule-classes nil) ;;;;;;;;;;;;;;;;;;;; ;;; Book top.lisp ;;;;;;;;;;;;;;;;;;;; (in-package "ACL2") (include-book "sub") (defthm contradiction nil :hints (("Goal" :use g-nil)) :rule-classes nil) The modification described above pertaining to defun-nx also prevents execution of non-executable functions that have been traced. The following example illustrates the problem; now, the following defun of g is illegal, and the problem disappears if defun-nx is used instead. (defun g (x) ; Use defun-nx to avoid an error after Version_3.6.1. (declare (xargs :guard t :non-executable t)) x) (g 3) ; causes error, as expected (trace$ g) (g 3) ; returned 3 before the bug fix; after fix, causes error as expected A hard error was possible when attempting to include an uncertified book containing events of the form (make-event '(local ...)). This has been fixed. Thanks to Sol Swords for bringing this issue to our attention. Fixed a bug in the heuristic improvement described for Version_3.6 (see *note NOTE-3-6::) as "We simplified induction schemes...." The bug had prevented, in unusual cases such as the following (notice the impossible case), a proof by induction. (defun foo (a x) (and (consp x) (case a (0 (foo (car x) (cdr x))) (1 (foo (cdr x) (car x))) (0 (foo a (cons x x)))))) (in-theory (disable (:type-prescription foo))) (thm (atom (foo a x))) Macro cw-gstack did not work with an :evisc-tuple argument. This has been fixed by changing cw-gstack so that it now evaluates its arguments. Thanks to Sol Swords for bringing this bug to our attention. Fixed a bug in :pso during the printing of failure messages for termination proofs. Fixed a bug in the handling of #. (see *note SHARP-DOT-READER::). Thanks to Bob Boyer for bringing this bug to our attention. Replaced a hard Lisp error with a clean error, in certain cases that a :hints value is erroneously supplied as a non-nil atom. Example: (thm (equal x x) :hints 3). Fixed a bug in the interaction of function tracing with conversion of a function from :program to :logic mode. The following example illustrates what had been wrong. (defun f (x) (declare (xargs :mode :program)) (car x)) (f 3) ; raw Lisp hard error (trace$ f) (f 3) ; raw Lisp hard error (still) (defun f (x) (car x)) ; upgrade f to :logic mode (f 3) ; clean guard violation; f is no longer traced (trace$) ; uh oh - f is shown as traced (untrace$ f) (f 3) ; OUCH: hard Lisp error because old :program mode definition of ; the executable counterpart (sometimes called *1*f) was restored! Made a fix so that when building ACL2 with `make' option ACL2_SAFETY=3, there will no longer be any safety-0 compiled code generated. Thanks to Gary Byers for bringing this bug to our attention. Fixed a bug in the handling of override-hints that generate custom keyword hints (see *note CUSTOM-KEYWORD-HINTS::) involving the variable stable-under-simplificationp. Thanks to Ian Johnson for bringing this bug to our attention with explanation that included a helpful example, included as comment in the ACL2 source code for function apply-override-hint. The saved_acl2 script in CLISP could contain unexpected characters where simple newlines were expected. Dave Greve found this in a Cygwin environment on Windows. Thanks to Dave for reporting this bug and experimenting with a fix, and thanks to the CLISP folks for providing helpful information. Fixed a bug that could make :oops cause an error. Also, the oops command can no longer take you back before a reset-prehistory event. (GCL only) Fixed a bug that could occur when calling trace in raw Lisp in GCL. Proof summaries have been improved, so that they account for runes used in rewriting that takes place when generating goals to be proved in a forcing round. Thanks to Jared Davis for sending us an example illustrating this issue. Fixed a bug that (at least in CCL) could put extra backslashes (`\') in a pathname that ACL2 writes out to the executable script created by a build. Thanks to Gary Byers for explaining that the CCL behavior is legal (for our previous use of Common Lisp function merge-pathnames). We closed some holes in the handling of trust tags (also known as "ttags"; see *note DEFTTAG::) by include-book. The following example illustrates this rather subtle situation. Consider the following book. (in-package "ACL2") (make-event (er-progn (encapsulate () (defttag :foo) (value-triple "Imagine something bad here!")) (value '(value-triple :some-value))) :check-expansion t) Formerly, the following commands succeeded. (certify-book "test3" 0 t :ttags :all) :u (include-book "test3" :ttags nil) But because of make-event keyword argument :check-expansion t, we know that the event (defttag :foo) is evaluated by the above include-book form, and hence the :ttags argument of include-book, above, should have specified :foo. The problem was that defttag forms evaluated during make-event expansion did not contribute to the trust tag information stored in the book's certificate. Note: Because of this change, one should avoid using make-event with :check-expansion t when the expansion would introduce a defttag event during include-book but not certify-book time. For an example illustrating this issue, see *note MAKE-EVENT-DETAILS::, specifically the new version of the section labeled "A note on ttags" at the end of that documentation topic. Closed a small loophole that had the potential, in rare circumstances, to violate atomicity of under-the-hood updates for ACL2 arrays. The following example was formerly allowed, but resulted in a guard-verified function (here, g) whose guard proof obligation is not a theorem outside the encapsulate event. We now disallow guard verification for functions introduced non-locally inside an encapsulate event unless we determine that the proof obligations hold outside the encapsulate event as well. (encapsulate ((f (x) t)) (local (defun f (x) (declare (xargs :guard t)) (consp x))) ;; ERROR! (defun g (x) (declare (xargs :guard (f x))) (car x))) The use of :comp on stobj functions had potentially caused a hard Lisp error; for example, this could occur when (defstobj foo fld) was followed by :comp foop. This has been fixed. Fixed a bug that could cause a raw Lisp error when the first argument of with-local-stobj is not a symbol. It had been possible to use the reserved keyword :computed-hints-replacement as the name of a custom keyword hint (see *note CUSTOM-KEYWORD-HINTS::). This has been fixed. Thanks to Dave Greve, who pointed out a confusing hint error message (which has also been fixed) that led us to this issue. Fixed a bug that could cause a hard Lisp error, instead of a graceful ACL2 error, if keyword :backchain-limit-lst in a rule class is given a cons that is not a true list, such as (1 . 1). Eliminated an error that could occur when redefining a function as a macro and then compiling, as in the example below. (defun foo (x) x) :redef! (defmacro foo (x) x) :comp t Thanks to Eric Smith for sending the above example in his bug report. Fixed a bug that could result in an assertion when a clause-processor causes an error. *NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE* See `http://code.google.com/p/acl2-books/source/list' for a record of books changed or added since the preceding release, with log entries. We note in particular the new system/ directory, which begins to specify ACL2 system code in anticipation of opening the architecture of ACL2 (see *note DEFATTACH:: for a relevant tool). Some system functions were changed slightly (but with the expectation of not generally affecting ACL2 behavior) in support of the development of this directory. Those interested in contributing to further such efforts are invited to contact the ACL2 implementors. New utilities have been provided for certifying most of the distributed books with more `make'-level parallelism. For example, we have obtained close to a 12x reduction in time by using `make -j 24 regression-fast' on a 24-processor machine. For more information see books/make-targets, or to include the books/workshops in the regression run, see books/regression-targets. Thanks to Sol Swords for providing these nice utilities. The top-level makefile, GNUmakefile, has been fixed so that the build processes (which are inherently sequential) will ignore the -j option of `make'. Note that regressions can still, however, be done in parallel, as the -j option will be passed automatically to the appropriate `make' command. *EMACS SUPPORT* *EXPERIMENTAL VERSIONS* The HONS version, supported primarily by Bob Boyer and Warren Hunt (see *note HONS-AND-MEMOIZATION::), has undergone numerous improvements. For example, keyword argument :FORGET is now supported when calling memoize from within the ACL2 loop, and system function worse-than is memoized with the :condition that both terms are function applications (clearing the memo-table after each prover invocation). Thanks to Jared Davis and Sol Swords for investigating the memoization of worse-than, and with suitable condition. Thanks also to Jared Davis for contributing structural modifications to the implementation of hons. David Rager contributed modifications to the parallel version (see *note PARALLELISM::), which include taking advantage of atomic increments available at least since Version 1.0.21 of SBCL and Version 1.3 of CCL. * Menu: * NOTE-4-0-WORMHOLE-CHANGES:: how to convert calls of wormhole for Version 4.0  File: acl2-doc-emacs.info, Node: NOTE-4-0-WORMHOLE-CHANGES, Prev: NOTE-4-0, Up: NOTE-4-0 NOTE-4-0-WORMHOLE-CHANGES how to convert calls of wormhole for Version 4.0 Here we describe how to convert an "old-style" call of wormhole -- that is, a call suitable for ACL2 versions preceding 4.0 -- in which the pseudo-flag was t. In order to convert such a call (wormhole t 'name input form ...) to a new-style call, the following steps must be carried out. Note that the wormhole name must always be quoted now. First, eliminate the first argument, t, and add a new second argument that is the quoted lambda expression '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) Setting the entry code to :ENTER is not necessary if you maintain the invariant (after initialization) that it is always :ENTER. In that case, the simpler quoted lambda will suffice: '(lambda (whs) whs) Second, change the form argument so that instead of talking about the state-global variable wormhole-output it talks about the state-global variable wormhole-status. Look for (@ wormhole-output), (assign wormhole-output ...), (f-get-global 'wormhole-output ...) and (f-put-global 'wormhole-output ...) in form and replace them with expressions involving wormhole-status. However, remember that the old data stored in wormhole-output is now in the wormhole-data component of the wormhole-status. Thus, for example, an old use of (@ wormhole-output) will typically be replaced by (wormhole-data (@ wormhole-status)) and an old use of (assign wormhole-output ...) will typically be replaced by (assign wormhole-status (set-wormhole-data (@ wormhole-status) ...)) In summary, an old-style call like (wormhole t 'name input '(...1 (@ wormhole-output) ...2 ...3 (assign wormhole-output ...4) ...5) ...6) can become (wormhole 'name '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) input '(...1 (wormhole-data (@ wormhole-status)) ...2 ...3 (assign wormhole-status (set-wormhole-data (@ wormhole-status) ...4) ...5) ...6) In any case, and especially if your wormhole call had a pseudo-flag other than t, we recommend that you see *note WORMHOLE::.  File: acl2-doc-emacs.info, Node: NOTE-4-0(R), Next: NOTE-4-1, Prev: NOTE-4-0, Up: RELEASE-NOTES NOTE-4-0(R) ACL2 Version 4.0(r) (July, 2010) Notes Please see *note NOTE-4-0:: for changes in Version 4.0 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-4-1, Next: NOTE-4-1(R), Prev: NOTE-4-0(R), Up: RELEASE-NOTES NOTE-4-1 ACL2 Version 4.1 (September, 2010) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 4.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. *CHANGES TO EXISTING FEATURES* The guard associated with calls of the macro, search, has been weakened so that now, given strings are no longer restricted to contain only standard characters unless the :test argument is char-equal. Modified the writing of "hidden defpkg" forms into certificate files (see *note HIDDEN-DEFPKG::), to support moving certificate files for distributed books, as is done by ACL2s (see *note ACL2-SEDAN::) and Debian releases of ACL2. Thanks to Camm Maguire for reporting a problem with Debian releases of ACL2 that led to this change. Expanded the constant *acl2-exports* by adding intersection-equal to the list. Thanks to Jared Davis for requesting this change. The :comp utility now compiles functions that have code conditionalized for raw Lisp only (presumably because a trust tag was active when they were defined). Previously, this was not the case when :comp was applied to more than a single function symbol. *NEW FEATURES* A new macro, top-level, allows evaluation directly in the top level loop for forms that normally need to be evaluated inside function bodies, such as with-local-stobj. See *note TOP-LEVEL::. Thanks to Jared Davis for requesting such a utility. Added count, a Common Lisp function, to ACL2. In support of that addition, also added rewrite rule eqlablep-nth. *HEURISTIC IMPROVEMENTS* [None this time.] *BUG FIXES* We fixed a soundness bug that could occur when a function that returns multiple values that is called in its own guard. Thanks to Sol Swords for reporting this bug and sending a small self-contained example, which is included in a comment in the function chk-acceptable-defuns1 in ACL2 source file defuns.lisp. It was possible to cause an error when giving theory hints during redefinition of functions. This has been fixed. Thanks to Ian Johnson for sending an example that nicely illustrated this problem. Fixed system function io? for the case that formal parameter commentp is t and vars is non-empty. Thanks to David Rager for bringing to our attention the fact that io? was broken for such a combination of parameters. Not exactly a bug fix, but: defun-sk was breaking when a :guard is specified, so we have improved the documentation (see *note DEFUN-SK::) to explain how to provide verified guards for a function introduced by defun-sk. Thanks to Jared Davis for bringing this issue to our attention. Made a fix to the handling of interrupts, which in rare cases might have left one in a state where all subsequent proof attempts were labeled as "Aborting due to an interrupt". Fixed :pso and related utilities, so that when proof output is redirected to a file, all summary output goes to that file rather than to the terminal. (GCL on Windows only) Removed an inappropriate check, resulting in an error about "pathname-device," that could prevent Windows GCL builds of ACL2. Thanks to Camm Maguire for reporting this problem and a helpful discussion. (Windows only) Modified the computation of canonical pathnames to avoid issues of case-insensitivity, in particular for the drive (e.g., "C:" vs. "c:"). Thanks to Harsh Raju Chamarthi for reporting this issue and helping with its debugging. (Windows only) The value of (@ distributed-books-dir) no longer will be missing the Windows drive prefix, for example, "C:". Thanks to Harsh Raju Chamarthi for reporting this issue and helping with its debugging. *NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE* See `http://code.google.com/p/acl2-books/source/list' for a record of books changed or added since the preceding release, with log entries. Modified books/Makefile-generic by adding a new BOOKS_SKIP_COMP variable, which is used in Makefiles in some subdirectories of books/, in order to avoid errors when compiling certified books for multiple Lisps. *EMACS SUPPORT* Distributed file emacs/emacs-acl2.el has been modified so that the forms control-t e and control-t control-e now pick up package markers (see *note SHARP-BANG-READER::), in the following sense: if the top-level form is preceded by a line starting with #!, then that line is included in the inserted string. Thanks to Jared Davis for suggesting this enhancement and providing a preliminary implementation. *EXPERIMENTAL VERSIONS* For the HONS version there have been some changes to memoize: Memoize accepts a new keyword, :recursive, that is a synonym for the existing keyword :inline. Thanks to Sol Swords for requesting this addition. Moreover, it is now enforced that these keywords have Boolean values. Memoize may now be called on :program mode functions. Thanks to Sol Swords for requesting this enhancement. A bug has been fixed. Now, if memoize is called with a :condition-fn (with value other than nil or t), then the guard of the memoized function and the :condition-fn must be the same. Previously, one could exploit the lack of such a check to get a hard Lisp error, for example as follows. (defun f (x) (declare (xargs :guard t)) x) (defun cf (x) (declare (xargs :guard (consp x))) (car x)) (memoize 'f :condition-fn 'cf) (f 3) Memoization is now illegal for built-in functions that use underlying raw Lisp in their implementations. To see why, consider the form (gc$), which is a macro call expanding to (gc$-fn nil). Previously, after evaluation of (memoize 'gc$-fn), a call of gc$ would no longer call the garbage collector, which had been invoked by raw Lisp code. Now, evaluation of (memoize 'gc$-fn) causes an error.  File: acl2-doc-emacs.info, Node: NOTE-4-1(R), Next: NOTE-4-2, Prev: NOTE-4-1, Up: RELEASE-NOTES NOTE-4-1(R) ACL2 Version 4.1(r) (September, 2010) Notes Please see *note NOTE-4-1:: for changes in Version 4.1 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-4-2, Next: NOTE-4-2(R), Prev: NOTE-4-1(R), Up: RELEASE-NOTES NOTE-4-2 ACL2 Version 4.2 (January, 2011) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 4.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. *CHANGES TO EXISTING FEATURES* The accumulated-persistence utility can now do finer-grained tracking, providing data for individual hypotheses and the conclusion of a rule. See *note ACCUMULATED-PERSISTENCE::. To try this out, evaluate the form (accumulated-persistence :all); then see *note ACCUMULATED-PERSISTENCE:: for a discussion of display options using show-accumulated-persistence. Thanks to Dave Greve for suggesting this new capability and collaborating on its design and implementation. The defattach utility now permits the use of :program mode functions, though this requires the use of a trust tag (see *note DEFTTAG::). See *note DEFATTACH:: and for discussion of the new capability, see *note DEFPROXY::, which explains how part of this change involves allowing :program mode functions to be declared non-executable. Redefinition (see *note LD-REDEFINITION-ACTION::) is no longer permitted for functions that have attachments (see *note DEFATTACH::). In such cases, the attachment must be removed first, e.g. with (defattach foo nil). Made small changes to mv-nth and defun-sk in order to permit guard verification of functions introduced with more than one quantified variable in a defun-sk form. The change to mv-nth is to weaken the guard by eliminating the requirement that the second argument satisfy true-listp, and replacing the call of endp in the definition body by a corresponding call of atom. The new definition of mv-nth is thus logically equivalent to the old definition, but with a weaker guard. Thanks to Sol Swords for sending the following example, for which the final verify-guards form had failed but now succeeds. (defstub foo (a b c) nil) (defun-sk forall-a-b-foo (c) (forall (a b) (foo a b c)) :witness-dcls ((declare (Xargs :guard t :verify-guards nil)))) (verify-guards forall-a-b-foo) The implementations of prog2$, time$, with-prover-time-limit, with-guard-checking, mbe (and must-be-equal), and ec-call have changed. See the discussion below of the new utility, return-last. A consequence is that trace$ is explicitly disallowed for these and related symbols, which formerly could cause hard Lisp errors, because they are now macros. Tracing of return-last is also disallowed. Another consequence is that time$ now prints a more abbreviated message by default, but a version of the old behavior can be obtained with :mintime nil. The following utilities no longer print an observation about raw-mode transitions: set-raw-mode-on, set-raw-mode-on!, set-raw-mode, and set-raw-mode-off. Thanks to Jared Davis for suggestion this change in the case of include-book (which proved awkward to restrict to that case). The system function translate-and-test now permits its LAMBDA form to refer to the variable WORLD, which is bound to the current ACL2 logical world. Modified abort handling to avoid talking about an interrupt when the error was caused by a Lisp error rather than an interrupt. The value of the constant *acl2-exports*, which is still a list, has been extended significantly, though only with the addition of symbols that one might reasonably have expected all along to belong to this list. A new distributed book, books/misc/check-acl2-exports.lisp, checks (at certification time) that no documented constant, macro, or function symbol in the "ACL2" package has been accidentally omitted from *acl2-exports*. Thanks to Dave Greve for helpful discussions related to this change. Improved the built-in `untranslate' functions to produce let* expressions when appropriate (more to help with tools that call untranslate and the like, than to help with proof output). The utility redo-flat now works for certify-book failures, just as it continues to work for failures of encapsulate and progn. The following only affects users who use trust tags to add to list values of either of the state global variables program-fns-with-raw-code or logic-fns-with-raw-code. For functions that belong to either of the above two lists, trace$ will supply a default value of :fncall to keyword :notinline, to avoid discarding raw-Lisp code for the function. The guard of the macro intern has been strengthened so that its second argument may no longer be either the symbol *main-lisp-package-name* or the string "COMMON-LISP". That change supports another change, namely that the following symbols in the "COMMON-LISP" package are no longer allowed into ACL2: symbols in that package that are not members of the list constant *common-lisp-symbols-from-main-lisp-package* yet are imported into the "COMMON-LISP" package from another package. See *note PKG-IMPORTS:: and see *note SYMBOL-PACKAGE-NAME::. To see why we made that change, consider for example the following theorem, which ACL2 was able to prove when the host Lisp is GCL. (let ((x "ALLOCATE") (y 'car)) (implies (and (stringp x) (symbolp y) (equal (symbol-package-name y) "COMMON-LISP")) (equal (symbol-package-name (intern-in-package-of-symbol x y)) "SYSTEM"))) Now suppose that one includes a book with this theorem (with :rule-classes nil), using an ACL2 built on top of a different host Lisp, say CCL, that does not import the symbol SYSTEM::ALLOCATE into the "COMMON-LISP" package. Then then one can prove nil by giving this theorem as a :use hint. The axioms introduced by defpkg have changed. See the discussion of pkg-imports under "NEW FEATURES" below. The error message for free variables (e.g., in definition bodies and guards) now supplies additional information when there are governing IF conditions. Thanks to Jared Davis for requesting this enhancement and collaborating in its design. The command :redef- now turns off redefinition. Improved proof output in the case of a :clause-processor hint that proves the goal, so that the clause-processor function name is printed. The proof-checker command `then' now stops at the first failure (if any). It is no longer permitted to submit definitions in :logic mode for merely part of an existing mutual-recursion event. Such an action left the user in an odd state and seemed a potential soundness hole. The function break$ is now in :logic mode. Thanks to Jared Davis for requesting this enhancement. The macro verify-termination now provides clearer output in the case that it is redundant. More important perhaps, as a courtesy it now causes an error when applied to a constrained function, since presumably such an application was unintended (as the constrained function could never have been in :program mode). Note that if one desires different behavior, one can create one's own version of verify-termination (but with a different name). Improved the guards for the following functions, often weakening them, to reflect more precisely the requirements for calling eq: alist-difference-eq, intersection-eq, intersection1-eq, intersectp-eq, not-in-domain-eq, set-difference-assoc-eq, set-equalp-eq, and union-eq. Thanks to Jared Davis for pointing out this issue for intersectp-eq. (CCL only) Made a change that can reduce the size of a compiled file produced by certify-book when the host Lisp is CCL, by discarding source information (for example, discarding local events). *NEW FEATURES* See the discussion above about new statistics that can be gathered by the accumulated-persistence utility. A new hint, :instructions, allows use of the proof-checker at the level of hints to the prover. Thanks to Pete Manolios for requesting this feature (in 2001!). See *note INSTRUCTIONS::. (For system hackers) There are new versions of system functions translate1 and translate, namely translate1-cmp and translate-cmp respectively, that do not take or return state. See the Essay on Context-message Pairs for relevant information. Thanks to David Rager for collaborating on this enhancement. A new utility, return-last, is now the unique ACL2 function that can pass back a multiple value result from one of its arguments. Thus, now the following are macros whose calls ultimately expand to calls of return-last: prog2$, time$, with-prover-time-limit, with-guard-checking, mbe (and must-be-equal), and ec-call. With an active trust tag, an advanced user can now write code that has side effects in raw Lisp; see *note RETURN-LAST::. Thanks to Jared Davis for requesting this feature. A new function, pkg-imports, specifies the list of symbols imported into a given package. The axioms for defpkg have been strengthened, taking advantage of this function. Now one can prove theorems using ACL2 that we believe could not previously be proved using ACL2, for example the following. (equal (symbol-package-name (intern-in-package-of-symbol str t)) (symbol-package-name (intern-in-package-of-symbol str nil))) Thanks to Sol Swords for a helpful report, which included the example above. See *note PKG-IMPORTS:: and see *note DEFPKG::. Added function no-duplicatesp-eq. Added a new hint keyword, :backchain-limit-rw, to control the level of backchaining for rewrite, meta, and linear rules. This overrides, for the current goal and (as with :in-theory hints) descendent goals, the default backchain-limit (see *note SET-BACKCHAIN-LIMIT::). Thanks to Jared Davis for requesting this feature. Support is now provided for creating and certifying books that do not depend on trust tags, in the case that the only use of trust tags is during make-event expansion. See *note SET-WRITE-ACL2X::. Thanks to Sol Swords for reporting a couple of bugs in a preliminary implementation. Function (file-write-date$ filename state) has been added, giving the write date of the given file. See *note FORWARD-CHAINING-REPORTS:: for how to get new reports on the forward chaining activity occurring in your proof attempts. Thanks to Dave Greve for inspiring the addition of this utility. It is now possible to use ACL2's printing utilities to return strings, by opening output channels to the keyword :STRING rather than to filenames. See *note IO::. Thanks to Jared Davis for a helpful conversation that led us to add this feature. *HEURISTIC IMPROVEMENTS* We have slightly improved the handling of :forward-chaining rules that contain free variables. Formerly, such rules might fire only once, when the first match for a free variable is discovered, and would not fire again even if subsequent forward chaining made available another match. This made it difficult to predict whether a rule with free variables would fire or not, depending as it did on the order in which newly derived conclusions were added. The new handling is a little slower but more predictable. Thanks to Dave Greve for sending a helpful example that led us to consider making such an improvement. We have slightly improved the so-called "type-set" heuristics to work a bit harder on terms of the form (rec term), where rec is a so-called "compound-recognizer" function, that is, a function with a corresponding enabled :compound-recognizer rule. Thanks to Jared Davis for sending a helpful example (found, in essence, in the modified function type-set-rec, source file type-set-b.lisp). We made three heuristic improvements in the way contexts (so-called "type-alists") are computed from goals ("clauses"). Although these changes did not noticeably affect timing results for the ACL2 regression suite, they can be very helpful for goals with many hypotheses. Thanks to Dave Greve for sending a useful example (one where we found a goal with 233 hypotheses!). The algorithm for substituting alists into terms was modified. This change is unlikely to affect many users, but in one example it resulted in a speed-up of about 21%. Thanks to Dave Greve for supplying that example. Sped up include-book a bit by memoizing checksums of symbols. (This change pertains to "normal" ACL2 only, not the hons version (see *note HONS-AND-MEMOIZATION::, where such memoization already occurred.) We found about a 23% speed-up on an example from Dave Greve. Made a small change to the algorithm used to prove hypotheses of :type-prescription rules (ACL2 source function type-set-relieve-hyps). One change avoids a linear walk through the context (the "type-alist" structure), while the other could avoid storing unnecessary forced assumptions (into the so-called "tag-tree"). *BUG FIXES* Fixed a long-standing soundness bug caused by the interaction of forced hypotheses with destructor elimination. The fix was to avoid using forcing when building the context (so-called "type-alist") when the goal is considered for destructor elimination; those who are interested can see a discussion in source function eliminate-destructors-clause1, which includes a proof of nil that no longer succeeds. A similar fix was made for generalization, though we have not exploited the previous code to prove nil in that case. Fixed a bug that allowed book certification to ignore skip-proofs around encapsulate events. Thus, a book could contain an event of the form (skip-proofs (encapsulate ...)), and a call of certify-book on that book could succeed even without supplying keyword :skip-proofs-okp t. This bug was introduced in Version 3.5 (May, 2009). Fixed a bug that could occur when including a book that attempts to redefine a function as a macro, or vice-versa. (For details of the issue, see the comment in the definition of variable *hcomp-fn-macro-restore-ht* in source file other-events.lisp.) (Windows only) Fixed handling of the Windows drive so that an executable image saved on one machine can be used on another, even with a different drive. Thanks to Harsh Raju Chamarthi for reporting this issue and doing a lot of testing and collaboration to help us get this right. Made a change to avoid possible low-level errors, such as bus errors, when quitting ACL2 by calling good-bye or its synonyms. This was occurring in CCL, and we are grateful to Gary Byers for helping us find the source of those errors (which basically was that ACL2 was attempting to quit while already in the process of quitting). Fixed a bug in with-guard-checking, which was being ignored in function bodies. Fixed a bug in top-level, which was not reverting the logical world when an error resulted from evaluation of the given form. Thanks to Jared Davis for bringing this bug to our attention. Fixed a long-standing bug (back through Version 2.7) that was discarding changes to the connected book directory (see *note CBD::) when exiting and then re-entering the top-level ACL2 loop (with lp). In some host Lisps, it has been possible to be in a situation where it is impossible to interrupt checkpoint printing during the summary. We had thought this solved when the host Lisp was CCL, but Sol Swords sent us an example (for which we are grateful) illustrating that this behavior could occur. This has been fixed. Fixed a bug in a proof obligation generated for :meta and :clause-processor rules, that the guard on the metafunction or clause-processor function, fn, holds under suitable assumptions. Those assumptions include not only that the first argument of fn satisfies pseudo-termp, but also that all stobj inputs satisfy the corresponding stobj recognizer predicates. We had erroneously considered stobj outputs of fn instead of stobj inputs. Thanks to Sol Swords for bringing this bug to our attention with a simple example, and correctly pointing us to the bug in our code. Fixed the following bugs in defattach. We hadn't always been applying the full functional substitution when generating guard proof obligations. We had been able to hit an assertion when reattaching to more than one function. Attachment was permitted in the case of an untouchable function (see *note REMOVE-UNTOUCHABLE::). Finally, the guard proof obligation could fail in the case that the two functions have different formal parameter lists, as in the following example. (encapsulate ((foo (x) x :guard (symbolp x))) (local (defun foo (x) x))) (defun bar (x2) (declare (xargs :guard (symbolp x2))) x2) (defattach foo bar) Fixed a raw Lisp error that could be caused by including a book using make-event to define a function symbol in a locally-introduced package. An example appears in a comment in ACL2 source function write-expansion-file. Made a change that can prevent an error near the end of book certification when the underlying Host Lisp is Allegro Common Lisp, in the case that environment variable ACL2_SYSTEM_BOOKS has been set to the name of a directory with a parent that is a soft link. Thanks to Dave Greve for supplying an example to led us to this fix, which involves avoiding Allegro CL's implementation of the Common Lisp function, truename. Fixed a bug that was failing to substitute fully using bindings of free variables in forced hypotheses. A related change is that instead of binding such a free variable to a new variable of the form ???-Y, the new variable is now of the form UNBOUND-FREE-Y. Fixed a bug that could inhibit the printing of certain theory warnings (and probably, in the other direction, cause inappropriate such printing). We eliminated excessive "Raw-mode" warnings about add-include-book-dir that could be generated by the use of raw-mode during include-book. Thanks to Dave Greve for bringing this issue to our attention. Fixed the printing of results from forms within an encapsulate, so that they are abbreviated according to the ld-evisc-tuple. It is now possible to evaluate stobj-related forms after evaluating :set-guard-checking :none or :set-guard-checking nil, even in cases where such evaluation formerly caused a guard violation due to a bug in ACL2. Here is an example of an error that no longer occurs. ACL2 !>(defstobj st fld) Summary Form: ( DEFSTOBJ ST ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ST ACL2 !>(set-guard-checking :none) Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fld st) ACL2 Error in TOP-LEVEL: The guard for the function call (FLD ST), which is (STP ST), is violated by the arguments in the call (FLD ST). [... etc. ...] You can understand how things now work by imagining that when a function introduced by defstobj is called, guard-checking values of :none or nil are temporarily converted to t. Thanks to Pete Manolios, Ian Johnson, and Harsh Raju Chamarthi for requesting this improvement. Fixed a bug in which the wrong attachment could be made when the same function has an attachment in a book and another in the certification world of that book (possibly even built into ACL2), if the load of a compiled file is aborted because a sub-book's compiled file is missing. The bug has been present since the time that defattach was added (Version_4.0). An example may be found in a comment in the deflabel for note-4-2 (ACL2 source file ld.lisp). The :doc and related utilities now cause a clean error when provided other than a symbol. Thanks to Jared Davis for pointing out the raw Lisp error that had occurred in such cases. It had been the case that in raw-mode (see *note SET-RAW-MODE::), it was possible to confuse include-book when including a book in a directory different from the current directory. This has been fixed. Thanks to Hanbing Liu for bringing this problem to our attention with a small example. *NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE* Many changes have been made to the distributed books, thanks to an active ACL2 community. You can contribute books and obtain updates between ACL2 releases by visiting the acl2-books project web page, `http://acl2-books.googlecode.com/'. There is new Makefile support for certifying just some of the distributed books. See *note BOOKS-CERTIFICATION-CLASSIC::, in particular discussion of the variable ACL2_BOOK_DIRS. Thanks to Sandip Ray for requesting this enhancement. The documentation for make-event now points to a new book, books/make-event/defrule.lisp, that shows how make-event can be used to do macroexpansion before generating events. Thanks to Carl Eastlund for useful interaction on the acl2-help mailing list that led us to add this example. *EMACS SUPPORT* Incorporated a version of changes from Jared Davis to the control-t f emacs utility (distributed file emacs/emacs-acl2.el), so that one can fill a format string from anywhere within the string. *EXPERIMENTAL VERSIONS* We refrain from listing changes here to experimental versions, other than an enhancement to the HONS version that can reduce sizes of certificate files, by applying hons-copy to introduce structure sharing (ACL2 source function make-certificate-file1).  File: acl2-doc-emacs.info, Node: NOTE-4-2(R), Next: NOTE-4-3, Prev: NOTE-4-2, Up: RELEASE-NOTES NOTE-4-2(R) ACL2 Version 4.2(r) (January, 2011) Notes Please see *note NOTE-4-2:: for changes in Version 4.2 of ACL2.  File: acl2-doc-emacs.info, Node: NOTE-4-3, Next: NOTE-4-3(R), Prev: NOTE-4-2(R), Up: RELEASE-NOTES NOTE-4-3 ACL2 Version 4.3 (July, 2011) Notes NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here. Below we roughly organize the changes since Version 4.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level and to distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. *CHANGES TO EXISTING FEATURES* Significant changes have been made for list-processing primitives such as member and assoc; see *note EQUALITY-VARIANTS::. In summary: instead of separate functions based on eq, eql, and equal, there is essentially just one function, which is based on equal; the eq and eql variants are logically just the equal variant. For example, member-eq and member are macros that generate corresponding calls of member-equal in the logic, although in raw Lisp they will execute using tests eq and eql, respectively. References to any of these in logical contexts such as theories are now references to the function based on equal; for example, the hint :in-theory (disable member) is completely equivalent to the hint :in-theory (disable member-equal). Distributed books have been modified as necessary to accommodate this change. While the need for such changes was relatively infrequent, changes were for example needed in contexts where terms are manipulated directly; for example, defevaluator needs to mention member-equal rather than member, just as it was already the case to mention, say, binary-append rather than append. Again, see *note EQUALITY-VARIANTS:: for more information about equality variants. A few improvements were made in support of the modified treatment of equality variants discussed above. The changes include the following. o We now allow the use of macro aliases (see *note MACRO-ALIASES-TABLE:: in :trigger-fns of rules (see *note RULE-CLASSES::). o We now remove so-called "guard holders" (including calls of return-last, hence of mbe) in :trigger-terms of rules. o We also remove guard holders in formulas of :congruence and type-prescription rules. o Macros union-eq and intersection-eq can now take any positive number of arguments, and union-eq can take zero arguments. (Thanks to Jared Davis for requesting this enhancement.) The same can be said for new macros union$ and intersection$, respectively. o A few changes were made to built-in theorems from source file axioms.lisp, in particular disabling :type-prescription rule consp-assoc-equal (formerly two enabled rules, consp-assoc-eq and consp-assoc) but adding this theorem as a :forward-chaining rule, and similarly for true-list-listp-forward-to-true-listp-assoc-equal (and eliminating rule true-list-listp-forward-to-true-listp-assoc-eq; and disabling rule true-listp-cadr-assoc-eq-for-open-channels-p. Also, theorem all-boundp-preserves-assoc has been renamed to all-boundp-preserves-assoc-equal and also strengthened. o Some guards were slightly improved (logically weaker or the same). Improved get-output-stream-string$ to allow for a context and to do genuine error printing instead of using cw. See *note IO::. Added the symbols flet and with-local-stobj to *acl2-exports*. A small change was made to the processing of more than one :guard declaration for the same function. In particular, a guard of t is essentially ignored. Attachments are now allowed during evaluation of the first argument of prog2$ even in contexts (such as proofs) during which the use of attachments is normally prohibited. More generally, the second of the three arguments of return-last has this property, except in the case of mbe (or related macros like mbe1), where the exec argument may provide the value. Thanks to Sol Swords for useful discussions leading us to implement this enhancement. The restriction has been removed that prohibited the use of mbe inside encapsulate events with a non-empty signature. This restriction was introduced in Version 3.4, but has not been necessary since Version 4.0, when we first started disallowing guard verification for functions introduced non-locally inside such encapsulate events. We weakened the checks involving common ancestors for evaluator and meta (and clause-processor) functions (see *note EVALUATOR-RESTRICTIONS::) so that except in the mbe case, the next-to-last argument of return-last is not considered. Thanks to Sol Swords for bringing this issue to our attention. The macro append no longer requires at least two arguments. Thanks to Dave Greve for requesting this enhancement. (Mostly for system hackers) Now, break-on-error breaks at a more appropriate (earlier) time for certain system functions that do not return state, such as translate11. Thanks to David Rager for requesting this improvement. Show-accumulated-persistence may take a new argument, :runes, which simply causes an alphabetical list of runes to be printed out. Improved trace$ so that :entry, :exit, and :cond forms may reference state even if the function being traced does not include state as a formal. The system function acl2x-expansion-alist now takes a second argument, namely state. This change allows for more flexibility in the sorts of attachments that can be made to this function (see *note DEFATTACH::). Thanks to Jared Davis and Sol Swords for requesting this enhancement and providing a preliminary implementation. An obscure proof-checker change, unlikely to affect users, replaces the state global variables erp, val, print-macroexpansion-flg, and print-prompt-and-instr-flg by pc-erp, pc-val, pc-print-macroexpansion-flg, and pc-print-prompt-and-instr-flg, respectively. State globals fmt-hard-right-margin and fmt-soft-right-margin are now untouchable (see *note SET-FMT-HARD-RIGHT-MARGIN:: and see *note PUSH-UNTOUCHABLE::). If you bind these state globals with state-global-let*, then you will need to do so with appropriate setters to restore their values, for example as follows. (state-global-let* ((fmt-hard-right-margin 500 set-fmt-hard-right-margin) (fmt-soft-right-margin 480 set-fmt-soft-right-margin)) ...) The error message has been improved for the case of evaluating an undefined function that has an attachment (see *note DEFATTACH::). Thanks to Jared Davis for sending the following example, which illustrates the additional part of the message. ACL2 !>(defstub foo (x) t) [[... output omitted ...]] ACL2 !>(defattach foo identity) [[... output omitted ...]] ACL2 !>(defconst *x* (foo 3)) ACL2 Error in ( DEFCONST *X* ...): ACL2 cannot ev the call of undefined function FOO on argument list: (3) Note that because of logical considerations, attachments (including IDENTITY) must not be called in this context. [[... additional output omitted ...]] The directory string supplied to add-include-book-dir no longer must terminate with the `/' character, as had been required in some Lisp implementations. Thanks to Sol Swords for bringing this issue to our attention. We no longer print induction schemes with gag-mode; use :pso if you want to see them. Thanks to Dave Greve for this suggestion. It is now legal to supply a constant for a stobj array dimension. See *note DEFSTOBJ::. Thanks to Warren Hunt for requesting this enhancement. We cleaned up a few issues with defpkg. o It is no longer illegal to submit a defpkg form in raw-mode (see *note SET-RAW-MODE::). Thanks to Jun Sawada for reporting an example in which an include-book form submitted in raw-mode caused an error because of a `hidden' defpkg form (see *note HIDDEN-DEFPKG::). There will no longer be an error in such cases. o It had been the case that locally including a book could make it possible to use a package defined by that book. Consider for example the following book, foo.lisp. (in-package "ACL2") (local (include-book "arithmetic/top" :dir :system)) After certifying this book, it had been possible to admit the following events in a new session. (include-book "foo") (defconst acl2-asg::*foo* 3) (defconst *c* 'acl2-asg::xyz) In Version_4.3, neither of these defconst events is admitted. o A hard Lisp error is now avoided that had been possible in rare cases when including books with hidden packages (see *note HIDDEN-DEFPKG::). An example may be found in a comment in the deflabel for note-4-3 (in ACL2 source file ld.lisp). The undocumented (but sometimes useful) functions packn1 and packn are now guard-verified :logic mode functions. Thanks to Sandip Ray for requesting this enhancement. It had been the case that when including a book, functions defined in the book's certification world (that is, in its portcullis commands) were typically not given compiled code. That has been fixed. The commands :pl and :pl2 have been improved, primarily by printing information for more rule classes. See *note PL:: and see *note PL2::. See also the item below about the new proof-checker command, show-type-prescriptions. *NEW FEATURES* New macros mv?-let and mv? extend the funtionality of mv-let and mv (respectively) to the case of a single value. Macro with-local-state is available for system programmers who wish bind state locally, essentially using with-local-stobj. But this should only be done with extreme care, and it requires an active trust tag; see *note WITH-LOCAL-STATE::. Formatted printing functions now have analogues that print to strings and do not take an output channel or state as arguments. See *note PRINTING-TO-STRINGS::. The system function ancestors-check is now available for verified modification by users, i.e., attachment using (defattach ancestors-check ). Thanks to Robert Krug for providing the necessary proof support, which we modified only in small ways. New macros, observation-cw and warning$-cw, provide formatted printing of observations and warnings (respectively) without state. Thanks to Harsh Raju Chamarthi and David Rager for requests leading to these utilities. Observation-cw is now used in some of the distributed books (thanks to Robert Krug for useful interaction for that). The proof-checker command type-alist (see *note PROOF-CHECKER-COMMANDS::) now takes an optional third argument that causes the production of forward-chaining reports (see *note FORWARD-CHAINING-REPORTS::). Thanks to Dave Greve for requesting such an enhancement. The reports generated by forward-chaining, forward-chaining-reports, have been changed to indicate when a conclusion reached by forward chaining is REDUNDANT with respect to the type information already known. Thanks to Dave Greve for suggesting this enhancement. The utility with-prover-time-limit is now legal for events (see *note EMBEDDED-EVENT-FORM::). For example, the following is now legal. (encapsulate () (with-prover-time-limit 2 (defthm append-assoc (equal (append (append x y) z) (append x (append y z)))))) The new utility with-prover-step-limit is analogous to the utility with-prover-time-limit, but counts "prover steps" rather than checking for time elapsed. See *note WITH-PROVER-STEP-LIMIT::. Also see *note SET-PROVER-STEP-LIMIT:: to provide a default step-limit. Note that just as with-prover-time-limit may now be used to create events, as discussed just above, with-prover-step-limit may also be used to create events. Thanks to Carl Eastlund for requesting support for step-limits. The macro progn$ is analogous to prog2$, but allows an arbitrary number of arguments. For example: ACL2 !>:trans1 (progn$ (f1 x) (f2 x) (f3 x)) (PROG2$ (F1 X) (PROG2$ (F2 X) (F3 X))) ACL2 !> Thanks to David Rager for contributing this macro. The macro defattach may now be supplied the argument :skip-checks :cycles. In this case, as with argument :skip-checks t, a trust tag is reuired (see *note DEFTTAG::), and no logical claims are made. The effect is to avoid the usual check that the extended ancestor relation has no cycles (see *note DEFATTACH::). Thanks to Dave Greve for requesting this feature. You can now limit the printing of subgoal names when using :set-gag-mode :goals. See *note SET-PRINT-CLAUSE-IDS::. Thanks to Karl Hoech for a suggestion leading to this enhancement. A new proof-checker command, show-type-prescriptions, or st for short, provides information about :type-prescription rules that match a given term. Thanks to Dave Greve for requesting this enhancement. See also the item above about related improvements to commands :pl and :pl2. *HEURISTIC IMPROVEMENTS* ACL2 now avoids some repeated attempts to rewrite hypotheses of rewrite rules. See *note SET-RW-CACHE-STATE:: for a discussion of this behavior and how to avoid it. The default behavior has been observed to reduce by 11% the overall time required to complete a regression. Here are the directories that had the top three time decreases and top three time increases, shown in seconds. -368 coi/gacc (1064 down to 696: decrease of 35%) -220 workshops/1999/ste (664 down to 444: decrease of 33%) -148 unicode (331 down to 183: decrease of 45%) .... +7 workshops/2002/cowles-flat/support (229 up to 236: increase of 3%) +8 workshops/1999/ivy/ivy-v2/ivy-sources (508 up to 516: increase of 2%) +12 workshops/2009/hardin/deque-stobj (78 up to 91: increase of 17%) The so-called "ancestors check," which is used to limit backchaining, has been strengthened so that two calls of equal are considered the same even if their arguments appear in the opposite order. Thanks to Robert Krug for providing an implementation and a useful discussion. The check for irrelevant-formals in processing of defuns has been made more efficient. Thanks to Eric Smith for reporting this issue in 2001 (!) and thanks to Warren Hunt for recently sending an example. For that example, we have seen the time for the irrelevant-formals check reduced from about 10 seconds to about 0.04 seconds. (GCL only) The macro mv has been modified so that certain fixnum boxing can be avoided. (Allegro CL only) We have set to nil four Allegro CL variables that otherwise enable storing of certain source information (for details, see the discussion of "cross-referencing" in ACL2 source file acl2-init.lisp). As a result of this change we have about a 6% speedup on the regression suite, but a 27% time reduction on an example that includes a lot of books. Exhaustive matching for the case of free-variables has been extended to type-prescription rules, in analogy to the default setting :match-free :all already in place for rewrite, linear, and forward-chaining rules. See *note FREE-VARIABLES-TYPE-PRESCRIPTION::. Thanks to Dave Greve for requesting this enhancement. *BUG FIXES* A soundness bug was fixed in some raw-Lisp code implementing the function, take. Thanks to Sol Swords for pointing out this bug with (essentially) the following proof of nil. (defthmd take-1-nil-logic (equal (take 1 nil) '(nil)) :hints(("Goal" :in-theory (disable (take))))) (thm nil :hints (("Goal" :use take-1-nil-logic))) Calls of mbe in "safe-mode" situations -- i.e., during evaluation of defconst, value-triple, and defpkg forms, and during macroexpansion -- are now guard-checked. Thus, in these situations both the :logic and :exec forms will be evaluated, with an error if the results are not equal. Formerly, only the :logic form was evaluated, which was a soundness bug that could be exploited to prove nil. For a such a proof and a bit of further explanation, see the example at the top of the comments for (deflabel note-4-3 ..) in ACL2 source file ld.lisp. It had been possible to prove nil by proving the following theorem using ACL2 built on CCL and then proving its negation using ACL2 built on a different host Lisp. (defthm host-lisp-is-ccl (equal (cdr (assoc 'host-lisp *initial-global-table*)) :ccl) :rule-classes nil) This hole has been plugged by moving the setting of 'host-lisp out of the constant *initial-global-table*. Fixed trace$ for arguments that are stobj accessors or updaters. It also gives an informative error in this case when the accessor or updater is a macro (because the introducing defstobj event specified :inline t). Avoided a potential error that could occur when no user home directory is located. Our previous solution for Windows simply avoided looking for ACL2 customization files (see *note ACL2-CUSTOMIZATION::) and acl2-init.lsp files in a user's home directory. With this change, we handle such files the same for Windows as for non-Windows systems: we always look for ACL2 customization files (see *note ACL2-CUSTOMIZATION::) and acl2-init.lsp files in a user's home directory, but only if such a directory exists. Thanks to Hanbing Liu for reporting this issue. (GCL only) Fixed a bug that prevented the use of get-output-stream-string$ when the host Lisp is GCL. Fixed with-live-state to work properly for executable counterparts (so-called "*1*" functions). Fixed a bug in the error message caused by violating the guard of a macro call. Fixed a bug in an error message that one could get when calling defattach with argument :skip-checks t to attach to a :program mode function symbol that was introduced with defun. (This is indeed an error, but the message was confusing.) Thanks to Robert Krug for bringing this bug to our attention. Fixed a bug in the loop-checking done on behalf of defattach, which could miss a loop. For an example, see the comment about loop-checking in the comments for (deflabel note-4-3 ..) in ACL2 source file ld.lisp. Terms of the form (hide ) without free variables could be simplified, contrary to the purpose of hide. This is no longer the case, Thanks to Dave Greve for reporting this issue. An infinite loop could occur when an error was encountered in a call of wormhole-eval, for example with the following form, and this has been fixed. (wormhole-eval 'demo '(lambda () (er hard 'my-top "Got an error!")) nil) Fixed a bug in detection of package redefinition. While we have no example demonstrating this as a soundness bug, we cannot rule it out. Fixed a bug in the message produced by an erroneous call of flet. Thanks to Jared Davis for reporting this bug and sending a helpful example. For a failed defaxiom or defthm event, we now avoid printing runes that are used only in processing proposed rules to be stored, but not in the proof itself. Thanks to Dave Greve for sending us an example that led us to make this fix. ACL2 did not reliably enforce the restriction against non-local include-book events inside encapsulate events, as illustrated by the following examples. ; not permitted (as expected) (encapsulate () (include-book "foo")) ; permitted (as expected) (encapsulate () (local (include-book "foo"))) ; formerly permitted (surprisingly); now, not permitted (local (encapsulate () (include-book "foo"))) Moreover, the corresponding error message has been fixed. Thanks to Jared Davis and Sandip Ray for relevant discussions. When include-book is given a first argument that is not a string, a more graceful error now occurs, where previously an ugly raw Lisp error had occurred. Thanks to Eric Smith for bringing this bug to our attention. Fixed a bug in an error message that was printed when an unexpected expression has occurred where a declare form is expected. (Since all functions are compiled when the host Lisp is CCL or SBCL, the following bug fix did not occur for those host Lisps.) After evaluation of (set-compile-fns t), all defined functions are expected to run with compiled code; but this was not the case for functions exported from an encapsulate event. This has been fixed. It had been the case that the :puff command was broken for include-book form whose book had been certified in a world with an add-include-book-dir event. This has been fixed. Evaluation of stobj updaters (see *note DEFSTOBJ::) may no longer use attachments (see *note DEFATTACH::). This is a subtle point that will likely not affect many users. Thanks to Jared Davis for bringing this issue to our attention; a slight variant of his example appears in a comment in ACL2 source function oneify-cltl-code. It had been the case that even when a stobj creator function was declared to be untouchable (see *note PUSH-UNTOUCHABLE::), a with-local-stobj form based on that same stobj was permitted. Now, such forms are not admitted. Thanks to Jared Davis for a query leading to this fix. Fixed a buggy message upon guard violations, which was suggesting the use of (set-guard-checking :none) in some cases when guard-checking was already set to :none. It had been possible to get a hard Lisp error when computing with ec-call in books. The following is an example of such a book, whose certification no longer causes an error. (in-package "ACL2") (defun f (x) x) (defconst *c* (ec-call (f 3))) (defun g (x) (cons x x)) The command :pl2, and also the proof-checker commands rewrite and show-rewrites (and hence their respective aliases r and sr), now take rule-id arguments that can be :definition runes. These commands dealt with definition rules already, e.g. :pl2 (append x y) binary-append but they did not allow explicit specification of :definition runes, e.g.: :pl2 (append x y) (:definition binary-append) The following example illustrates a bug in the processing of (admittedly obscure) hints of the form :do-not-induct name, where name is not t, :otf-flg-override, :otf, or nil. In this example, ACL2 had essentially ignored the hint and reverted to prove the original goal by induction, rather than to skip the goal temporarily as is expected for such hints. Thanks to David Rager for a helpful discussion. (thm (and (equal (append (append x y) z) (append x y z)) (equal (append (append x2 y2) z2) (append x2 y2 z2))) :hints (("Subgoal 1" :do-not-induct some-name))) Fixed a slight bug in the definitions of built-in theories. For example, in a fresh ACL2 session the value of the following form is nil, but formerly included several :definition runes. (let ((world (w state))) (set-difference-theories (function-theory :here) (function-theory 'ground-zero))) *CHANGES AT THE SYSTEM LEVEL AND TO DISTRIBUTED BOOKS* Many changes have been made to the distributed books, as recorded in svn logs under the `Source' and 'Updates' links at `http://acl2-books.googlecode.com/'. Here we list some of the more significant changes. o A large library has been graciously contributed by the formal verification group at Centaur Technology. See books/centaur/ and, in particular, file books/centaur/README, which explains how the library depends on the experimental HONS extension (see *note HONS-AND-MEMOIZATION::). o Among the new books is an illustration of defattach, books/misc/defattach-example.lisp, as well as a variant of defattach that avoids the need for guard verification, books/misc/defattach-bang.lisp. o Distributed book books/misc/trace1.lisp has been deleted. It had provided slightly more friendly trace output for new users, but distributed book books/misc/trace-star.lisp may be better suited for that purpose. ACL2 can once again be built on LispWorks (i.e., as the host Lisp), at least with LispWorks 6.0. Thanks to David Rager for useful conversations. Several changes have been made from previous LispWorks-based ACL2 executables: o ACL2 now starts up in its read-eval-print loop. o You can save an image with save-exec. o Multiprocessing is not enabled. o The stack size is managed using a LispWorks variable that causes the stack to grow as needed. o When ACL2 is built a script file is written, as is done for other host Lisps. Thus, (assuming that no PREFIX is specified), saved_acl2 is just a small text file that invokes a binary executable, which for Lispworks is saved_acl2.lw. The HTML documentation no longer has extra newlines in
    environments.
    
    Statistics on ACL2 code size may be found in distributed file
    doc/acl2-code-size.txt.  This file and other information can be found in
    a new documentation topic, about-acl2.
    
    Fixed the build process to pay attention to environment variable
    ACL2_SYSTEM_BOOKS (which may be supplied as a command-line argument to
    `make').  An ACL2 executable can thus now be built even when there is no
    books/ subdirectory if a suitable replacement directory is supplied.
    
    Some warnings from the host Lisp are now suppressed that could formerly
    appear.  For example, the warnings shown below occurs in Version  4.2
    using Allegro CL, but not in Version  4.3.
    
         ACL2 !>(progn (set-ignore-ok t)
                       (set-irrelevant-formals-ok t)
                       (defun bar (x y)
                         x))
         [[.. output omitted ..]]
          BAR
         ACL2 !>:comp bar
         ; While compiling BAR:
         Warning: Variable Y is never used.
         ; While compiling (LABELS ACL2_*1*_ACL2::BAR ACL2_*1*_ACL2::BAR):
         Warning: Variable Y is never used.
          BAR
         ACL2 !>
    
    *EMACS SUPPORT*
    
    The distributed Emacs file emacs/emacs-acl2.el now indents calls of
    er@par and warning$@par the same way that calls of defun are indented.
    
    *EXPERIMENTAL VERSIONS*
    
    The parallel version (see *note PARALLELISM::) now supports parallel
    evaluation of the "waterfall" part of the ACL2 prover; see *note
    SET-WATERFALL-PARALLELISM::.  Thanks to David Rager for doing the
    primary design and implementation work.
    
    A new macro, spec-mv-let, supports speculative and parallel execution
    in the parallel version, courtesy of David Rager.
    
    Among the enhancements for the HONS version (see *note
    HONS-AND-MEMOIZATION::) are the following.
    
         Memoized functions may now be traced (see *note TRACE$::).  Thanks
         to Sol Swords for requesting this enhancement.
    
         Memoize-summary and clear-memoize-statistics are now :logic mode
         functions that return nil.  Thanks to Sol Swords for this
         enhancement.
    
         Memoize is now explicitly illegal for constrained functions.
         (Already such memoization was ineffective.)
    
         A new keyword argument, :AOKP, controls whether or not to allow
         memoization to take advantage of attachments; see *note MEMOIZE::
         and for relevant background, see *note DEFATTACH::.
    
         Memoize is now illegal by default for :logic mode functions that
         have not had their guards verified.  See *note MEMOIZE:: (keyword
         :ideal-okp) and see *note ACL2-DEFAULTS-TABLE:: (key
         :memoize-ideal-okp) for and explanation of this restriction and
         how to avoid it.
    
         History commands such as :pe and :pbt now display "M" or "m" to
         indicate memoized functions.  See *note PC::.
    
    
    
    File: acl2-doc-emacs.info,  Node: NOTE-4-3(R),  Next: NOTE-5-0,  Prev: NOTE-4-3,  Up: RELEASE-NOTES
    
    NOTE-4-3(R)    ACL2 Version  4.3(r) (July, 2011) Notes
    
    Please see *note NOTE-4-3:: for changes in Version  4.3 of ACL2.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE-5-0,  Next: NOTE-6-0,  Prev: NOTE-4-3(R),  Up: RELEASE-NOTES
    
    NOTE-5-0    ACL2 Version  5.0 (August, 2012) Notes
    
    NOTE!  New users can ignore these release notes, because the
    documentation has been updated to reflect all changes that are recorded
    here.
    
    Below we roughly organize the changes since Version  4.3 into the
    following categories of changes: existing features, new features,
    heuristic improvements, bug fixes, changes at the system level and to
    distributed books, Emacs support, and experimental versions.  Each
    change is described in just one category, though of course many changes
    could be placed in more than one category.
    
    NOTE: ACL2 is now distributed under Version 2 of the GNU General Public
    License.  [Added later: The license has changed since Version_5.0.  See
    LICENSE.]  Formerly, any later version had been acceptable.  Moreover,
    books are no longer distributed from a University of Texas website, but
    rather, from Google Code at
    `http://code.google.com/p/acl2-books/downloads/'.
    
    *CHANGES TO EXISTING FEATURES*
    
    A fatal error now occurs if environment variable ACL2_CUSTOMIZATION has
    a value other than NONE or the empty string, but is not the name of an
    existing file.  Thanks to Harsh Raju Chamarthi for requesting such a
    change.
    
    Functions read-acl2-oracle (and read-acl2-oracle@par), read-run-time,
    and main-timer are no longer untouchable (see *note
    REMOVE-UNTOUCHABLE::).
    
    We now avoid certain duplicate conjuncts in the constraint stored for
    encapsulate events.  For example, the constraint stored for the
    following event formerly included (EQUAL (FOOP (CONS X Y)) (FOOP Y)) and
    (BOOLEANP (FOOP X)) twice each, but no more.
    
         (encapsulate
          ((foop (x) t))
          (local (defun foop (x) (declare (ignore x)) t))
          (defthm foop-constraints
            (and (booleanp (foop x))
                 (equal (foop (cons x y)) (foop y)))
            :rule-classes
            ((:type-prescription :corollary (booleanp (foop x)))
             (:rewrite :corollary (equal (foop (cons x y)) (foop y))))))
    
    The :guard for a constrained function (see *note SIGNATURE::) may now
    mention function symbols introduced in the same encapsulate event that
    introduces that function.  Thanks to Nathan Wetzler for a helpful
    discussion leading to this improvement.
    
    The test for redundancy (see *note REDUNDANT-EVENTS::) of encapsulate
    events has been improved in cases involving redefinition (see *note
    LD-REDEFINITION-ACTION::).  Thanks to Jared Davis for providing the
    following example, which illustrates the problem.
    
           (redef!)
    
           (encapsulate ()
             (defun g (x)
               (+ 3 x)))
    
           (g 0) ; 3, as expected
    
           (encapsulate ()
             (defun g (x)
               (+ 4 x)))
    
           (g 0) ; 4, as expected
    
           ; Unfortunately, the following was flagged as redundant because it agreed
           ; with the first encapsulate above.  That has been fixed; now, it is
           ; recognized as not being redundant.
           (encapsulate ()
             (defun g (x)
               (+ 3 x)))
    
    The test for redundancy of defun and defconst events has been improved
    in the case that redefinition is active.  In that case, redundancy now
    additionally requires that the "translated" body is unchanged, i.e.,
    even after expanding macro calls and replacing constants (defined by
    defconst) with their values.  Thanks to Sol Swords for requesting this
    enhancement, and to Jared Davis for pointing out a bug in a preliminary
    change.  See *note REDUNDANT-EVENTS::, in particular the "Note About
    Unfortunate Redundancies".  Note that this additional requirement was
    already in force for redundancy of defmacro events.
    
    The macro defmacro-last and the table return-last-table have been
    modified so that when they give special treatment to a macro mac and
    its raw Lisp counterpart mac-raw, a call (return-last 'mac-raw ...)
    can be made illegal when encountered directly in the top level loop, as
    opposed to inside a function body.  See *note RETURN-LAST::.  Thanks to
    Harsh Raju Chamarthi for showing us an example that led us to make this
    improvement.
    
    We removed a barrier to admitting function definitions, as we explain
    using the following example.
    
         (defun foo (m state)
           (declare (xargs :stobjs state))
           (if (consp m)
               (let ((state (f-put-global 'last-m m state)))
                 (foo (cdr m) state))
             state))
    
    Previously, ACL2 complained that it could not determine the outputs of
    the LET form, as is necessary in order to ensure that STATE is returned
    by it.  ACL2 now works harder to solve this problem as well as the
    analogous problem for MV-LET and, more generally for mutual-recursion.
    (The main idea is to reverse the order of processing the IF branches if
    necessary.)  We thank Sol Swords for contributing a version of the
    above example and requesting this improvement.
    
    It is no longer the case that break-on-error causes a Lisp break when
    encountering an error during translation of user input into internal
    (translated) form (see *note TERM::).  The reason is that an
    improvement to the translation process, specifically the one described
    in the preceding paragraph, allows certain backtracking from "errors",
    which are intended to be silent rather than causing breaks into raw
    Lisp.  Thanks to Jared Davis for sending an example leading to this
    change.
    
    (CCL and SBCL only) When the host Lisp is CCL or SBCL, then since all
    functions are compiled, a certify-book command will no longer load the
    newly-compiled file (and similarly for include-book with argument
    :load-compiled-file :comp).
    
    Set-write-acl2x now returns an error triple and can take more values,
    some of which automatically allow including uncertified books when
    certify-book is called with argument :acl2x t.
    
    The environment variable COMPILE_FLG has been renamed ACL2_COMPILE_FLG;
    see *note CERTIFY-BOOK::.
    
    The macros defthmd and defund no longer return an error triple with
    value :SKIPPED when proofs are being skipped.  Rather, the value
    returned is the same as would be returned on success when proofs are not
    skipped.
    
    For those who use set-write-acl2x: now, when certify-book is called
    without a :ttagsx argument supplied, then the value of :ttagsx defaults
    to the (explicit or default) value of the :ttags argument.
    
    The :pl and :pl2 commands can now accept terms that had previously been
    rejected.  For example, the command :pl (member a (append x y)) had
    caused an error, but now it works as one might reasonably expect,
    treating member as member-equal (see *note EQUALITY-VARIANTS:: for
    relevant background).  Thanks to Jared Davis for reporting this problem
    by sending the above example.
    
    We have eliminated some hypotheses in built-in rewrite rules
    characterp-nth and ordered-symbol-alistp-delete-assoc-eq.
    
    Added the symbols f-get-global, f-put-global, and state-global-let* to
    *acl2-exports*.
    
    Added to the guards of push-untouchable and remove-untouchable the
    requirement that the second argument must be a Boolean.  Thanks to
    Jared Davis for sending an example that led to this change.
    
    The built-in function string-for-tilde-@-clause-id-phrase has been put
    into :logic mode and had its guards verified, as have some subsidiary
    functions.  A few new rules have been added in support of this work;
    search for string-for-tilde-@-clause-id-phrase in ACL2 source file
    boot-strap-pass-2.lisp if interested.  Thanks to David Rager for
    contributing an initial version of this improvement.
    
    All trust tags are now in the keyword package.  The defttag event may
    still take a symbol in an arbitrary package, but the trust tag created
    will be in the keyword package (with the same symbol-name as the symbol
    provided).  Similarly, non-nil symbols occurring in the :ttags argument
    of an include-book or certify-book command will be converted to
    corresponding keywords.  See *note DEFTTAG::.
    
    There have been several changes to gag-mode.  It is now is initially set
    to :goals, suppressing most proof commentary other than key checkpoints;
    see *note SET-GAG-MODE::.  (As before, see *note PSO:: for how to
    recover the proof output.)  Also, top-level induction schemes are once
    again printed when gag-mode is on, though these as well as printing of
    guard conjectures can be abbreviated ("eviscerated") with a new
    evisc-tuple; see *note SET-EVISC-TUPLE::, in particular the discussion
    there of :GAG-MODE.  Finally, the commentary printed within gag-mode
    that is related to forcing-rounds is now less verbose.  Thanks to Dave
    Greve and David Rager for discussions leading to the change in the
    printing of induction schemes under gag-mode; thanks to Warren Hunt for
    an email that led us to similar handling for printing of guard
    conjectures; and thanks to Robert Krug for a suggestion that led us to
    restore, in abbreviated form, important information about the sources
    of forcing round goals.
    
    An error now occurs if ld is called while loading a compiled book.  See
    *note CALLING-LD-IN-BAD-CONTEXTS::.  Thanks to David Rager for
    reporting a low-level assertion failure that led us to make this change.
    
    The proof-checker interactive loop is more robust: most errors will
    leave you in that loop, rather than kicking you out of the
    proof-checker and thus back to the main ACL2 read-eval-print loop.
    Thanks to David Hardin for suggesting this improvement in the case of
    errors arising from extra right parentheses.
    
    The summary at the end of a proof now prints the following note when
    appropriate:
    
         [NOTE: A goal of NIL was generated.  See :DOC nil-goal.]
    
    See *note NIL-GOAL::.
    
    Improved dmr to show the function being called in the case of explicit
    evaluation: "(EV-FNCALL function-being-called)".
    
    It is now permitted to bind any number of stobjs to themselves in the
    bindings of a LET expression.  But if any stobj is bound to other than
    itself in LET bindings, then there still must be only one binding in
    that LET expression.  The analogous relaxation holds for LAMBDA
    expressions.  Thanks to Sol Swords for requesting such a change, which
    was needed for some code generated by macro calls.
    
    The macro top-level now returns without error; See *note TOP-LEVEL::.
    Formerly, this macro always returned an error triple (mv t .. state),
    which meant that normal calls of ld would stop after encountering a
    call of top-level.  Thanks to Jared Davis for bringing this issue to our
    attention.
    
    It is no longer the case that when you specify xargs keyword
    :non-executable t in a defun form rather than using defun-nx, then the
    form of the body need match only the shape (prog2$ (throw-nonexec-error
    ... ...) ...).  We now require that the body of the definition of a
    function symbol, fn, with formals (x1 ... xk), be of the form (prog2$
    (throw-nonexec-error 'fn (list x1 ... xk)) ...).  This fixes the
    following odd behavior, which could be considered a bug.  Consider a
    book that contains the following two events.
    
         (defun foo (x)
           (declare (xargs :guard t :non-executable t :mode :logic))
           (prog2$ (throw-nonexec-error 'bar (list x))
                   (cons 3 x)))
         (defn h (x)
           (foo x))
    
    After certifying this book and then including it in a new session, the
    behavior occurred that is displayed below; notice the mention of BAR.
    However, if the two forms were submitted directly in the loop, then the
    error message had mentioned FOO instead of BAR.  This discrepancy has
    been eliminated, by rejecting the proposed definition of foo because
    the name in the first argument of throw-nonexec-error was 'bar where
    now it must be 'foo.
    
         ACL2 !>(h 3)
    
    
         ACL2 Error in TOP-LEVEL:  ACL2 cannot ev the call of undefined function
         BAR on argument list:
    
         (3)
    
         To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.
    
         ACL2 !>
    
    A tautology checker used in the ACL2 sources (function if-tautologyp)
    has been limited somewhat in the effort it makes to recognize a
    tautology.  While we expect it to be rare for the effect of this change
    to be noticeable, we thank Sol Swords for sending us an example that
    motivated this change: a guard verification that took about 5 seconds
    in Version_4.3 now takes, on the same machine, about 0.07 seconds.
    
    The behavior of backquote (`) has been changed slightly to be compatible
    with its behavior in raw Lisp.  The change is to allow the use of
    comma-atsign (,@) at the end of a list, as in the following example.
    
         (let ((x 3) (y 2) (z 7)) `(,x ,y ,@z))
    
    Formerly, evaluation of this form had caused a guard violation in the
    ACL2 loop unless guard-checking was off (i.e., set-guard-checking was
    invoked with nil or :none), in which case it returned (3 2).  But we
    observed evaluation of this form to return (3 2 . 7) in every host Lisp
    on which ACL2 runs (Allegro CL, CCL, CLISP, CMUCL, GCL, LispWorks, and
    SBCL).  Now, ACL2 behaves like these Lisps.
    
    A call of the theory macro had previously returned nil when applied to
    other than the name of name of a previously executed deftheory event.
    Now, a hard error occurs.
    
    The table binop-table has been replaced by the table untrans-table.
    However, add-binop and remove-binop continue to have the same effect as
    before.  See *note ADD-MACRO-FN::, which is a new feature discussed
    below.
    
    The function booleanp is now defined using eq instead of equal, which
    may increase its efficiency.  Thanks to Jared Davis for this change.
    
    For pairs (key . val) in the macro-aliases-table, there had been a
    requirement that val is a known function symbol.  Now, it only needs to
    be a symbol.  (This change was made to support the new feature,
    defun-inline, described elsewhere in these release notes.)
    
    *NEW FEATURES*
    
    A new "tau system" provides a kind of "type checker."  See *note
    TAU-SYSTEM::.  Thanks to Dave Greve for supplying a motivating example
    (on which this system can provide significant speedup), and to Sol
    Swords for sending a very helpful bug report on a preliminary
    implementation.
    
    Users may now arrange for additional summary information to be printed
    at the end of events.  [Note added at Version_6.1: Formerly we pointed
    here to print-summary-user, but now, see *note FINALIZE-EVENT-USER::;
    also see *note NOTE-6-1::].  Thanks to Harsh Raju Chamarthi for
    requesting this feature and participating in a design discussion.
    
    A new, advanced proof-checker command, geneqv, shows the generated
    equivalence relation at the current subterm.  Thanks to Dave Greve for
    an inquiry leading to this enhancement.
    
    A new reader macro, #u, permits the use of underscore characters in a
    number.  See *note SHARP-U-READER::.  Thanks to Jared Davis for
    requesting this capability.
    
    New proof-checker commands pl and pr provide interfaces to the ACL2
    commands :pl and :pr, respectively.  These can be useful if you want to
    see trivially-proved hypotheses, as now clarified in the proof-checker
    documentation for its show-rewrites command.  See *note
    PROOF-CHECKER-COMMANDS::.  Thanks to Pete Manolios for suggesting such
    clarification and capability.
    
    It is now legal to call non-executable functions without the usual
    signature restrictions imposed on executable code.  For example, the
    third event below was not admissible, but now it is.
    
         (defstobj foo fld)
         (defun-nx id (x)
           x)
         (defun f (foo)
           (declare (xargs :stobjs foo :verify-guards nil))
           (cons 3 (id foo)))
    
    Thanks to Jared Davis for requesting this enhancement, in particular for
    calling non-executable functions in the :logic part of an mbe call.
    Here is Jared's example, which is admissible now but formerly was not.
    
         (defstobj foo (fld))
         (defun-nx my-identity (x) x)
         (defun my-fld (foo)
           (declare (xargs :stobjs foo))
           (mbe :logic (my-identity foo)
                :exec (let ((val (fld foo)))
                        (update-fld val foo))))
    
    A new macro, non-exec, allows the use of non-executable code, for
    example inside ordinary function definitions.  Thanks to Sol Swords for
    requesting this enhancement.
    
    A new "provisional certification" process is supported that can allow
    books to be certified before their included sub-books have been
    certified, thus allowing for potentially much greater `make'-level
    parallelism.  See *note PROVISIONAL-CERTIFICATION::.  Thanks to Jared
    Davis for requesting this feature and for helpful discussions, based in
    part on rudimentary provisional certification schemes that he developed
    first at Rockwell Collins and later for his `Milawa' project.  Also,
    thanks to Jared and to Sol Swords for testing this feature and for
    providing a fix for a bug in a preliminary implementation, and thanks
    to Sol for providing performance feedback and a crucial suggestion that
    led to an improved implementation.
    
    Event summaries now show the names of events that were mentioned in
    hints of type :use, :by, or :clause-processor.  See *note
    SET-INHIBITED-SUMMARY-TYPES::.  Thanks to Francisco J. Martin Mateos for
    requesting such an enhancement (actually thanks to the community, as his
    request is the most recent but this has come up from time to time
    before).
    
    ACL2 now stores a data structure representing the relation "Event A is
    used in the proof of Event B."  See *note DEAD-EVENTS::, which explains
    this data structure and mentions one application: to identify dead code
    and unused theorems.  Thanks to Shilpi Goel for requesting such a
    feature and for helpful feedback.
    
    A new documentation topic provides a guide to programming with state;
    see *note PROGRAMMING-WITH-STATE::.  Thanks to Sarah Weissman for
    suggesting that such a guide might be useful, and to David Rager for
    helpful feedback on a preliminary version.  There also has been some
    corresponding reorganization of the documentation as well as creation
    of additional documentation (e.g., see *note STATE-GLOBAL-LET*::).
    Now, most built-in functions and macros commonly used in programs (as
    opposed to events like defun, for example) are subtopics of a new topic
    -- see *note ACL2-BUILT-INS:: -- which is a subtopic of programming, a
    topic that in turn has considerably fewer direct subtopics than before.
    
    It is now possible to bind extra variables in a :USE hint, thus avoiding
    the error message: "The formula you wish to instantiate, ..., mentions
    only the variable(s) ...".  See *note LEMMA-INSTANCE::, in particular
    the discussion of keyword :extra-bindings-ok.  Thanks to Sol Swords for
    requesting such an enhancement.
    
    The function read-object-suppress is like read-object except that it
    avoids errors and discards the value read.  See *note IO::.
    
    A stobj may now be passed as an argument where another stobj is expected
    if the two are "congruent".  See *note DEFSTOBJ::, in particular, its
    discussion of the new :congruent-to keyword of defstobj.  Thanks to Sol
    Swords for requesting this enhancement and for useful discussions
    contributing to its design.
    
    A new top-level utility has been provided that shows the assembly
    language for a defined function symbol; see *note DISASSEMBLE$::.
    Thanks to Jared Davis for requesting such a utility and to Shilpi Goel
    for pointing out an inconvenience with the initial implementation.
    Note that it uses the distributed book books/misc/disassemble.lisp,
    which users are welcome to modify (see
    `http://www.cs.utexas.edu/users/moore/acl2/').
    
    The macro set-accumulated-persistence is an alias for
    accumulated-persistence.  Thanks to Robert Krug for suggesting this
    addition.
    
    A new documentation topic lists lesser-known and advanced ACL2 features,
    intended for those with prior ACL2 experience who wish to extend their
    knowledge of ACL2 capabilities.  See *note ADVANCED-FEATURES::.  Thanks
    to Warren Hunt and Anna Slobodova for requesting such information.
    
    A new macro, deftheory-static, provides a variant of deftheory such
    that the resulting theory is the same at include-book time as it was at
    certify-book time.  Thanks to Robert Krug for helpful discussions on
    this new feature and for updating his books/arithmetic-5/ distributed
    books to use this feature.
    
    A new event, defabsstobj, provides a new way to introduce
    single-threaded objects (see *note STOBJ:: and see *note DEFSTOBJ::).
    These so-called "abstract stobjs" permit user-provided logical
    definitions for primitive operations on stobjs, for example using an
    alist-based representation instead of a list-based representation for
    array fields.  Moreover, the proof obligations guarantee that the
    recognizer is preserved; hence the implementation avoids executing the
    recognizer, which may be an arbitrarily complex invariant that
    otherwise would be an expensive part of guard checks.  Thanks to Warren
    Hunt for a request leading us to design and implement this new feature,
    and thanks to Rob Sumners for a request leading us to implement a
    related utility, defabsstobj-missing-events.  See *note DEFABSSTOBJ::.
    Also thanks to Sol Swords for sending an example exhibiting a bug in
    the initial implementation, which has been fixed.
    
    A new command, :psof , is like :pso but directs proof replay
    output to the specified file.  For large proofs, :psof may complete
    much more quickly than :pso.  see *note PSOF::.  More generally, a new
    utility, wof (an acronym for "With Output File"), directs standard
    output and proofs output to a file; see *note WOF::.
    
    The new macro defnd defines a function with :guard t and disables that
    function, in analogy to how defund defines with defun and then
    disables.  Thanks to Shilpi Goel for requesting this feature.
    
    The :pl2 command now shows :linear rules; and a new proof-checker
    command, show-linears (equivalently, sls), is an analogue of the
    proof-checker show-rewrites (sr) command, but for linear rules.  Thanks
    to Shilpi Goel for requesting this new proof-checker command.  Finally,
    a corresponding new proof-checker command, apply-linear (al), is an
    analogue of the proof-checker rewrite (r) command, but for linear rules.
    
    The macros add-macro-fn and remove-macro-fn replace macros add-binop
    and remove-binop, respectively, though the latter continue to work.
    The new macros allow you to decide whether or not to display calls of
    binary macros as flat calls for right-associated arguments, e.g.,
    (append x y z) rather than (append x (append y z)).  See *note
    ADD-MACRO-FN::.
    
    It is now possible to request that the host Lisp compiler inline calls
    of specified functions, or to direct that the host Lisp compiler not
    inline such calls.  See *note DEFUN-INLINE:: and see *note
    DEFUN-NOTINLINE::.  We thank Jared Davis for several extensive,
    relevant conversations, and for finding a bug in a preliminary
    implementation.  We also thank others who have engaged in discussions
    with us about inlining for ACL2; besides Jared Davis, we recall such
    conversations with Rob Sumners, Dave Greve, and Shilpi Goel.
    
    *HEURISTIC IMPROVEMENTS*
    
    Reading of ACL2 arrays (see *note AREF1::, see *note AREF2::) has been
    made more efficient (as tested with CCL as the host Lisp) in the case
    of consecutive repeated reads of the same named array.  Thanks to Jared
    Davis and Sol Swords for contributing this improvement.
    
    Slightly modified the induction schemes stored, so that calls of
    so-called "guard-holders" (such as mbe and prog2$ -- indeed, any call
    of return-last -- and the) are expanded away.  In particular, calls of
    equality variants such as member are treated as their corresponding
    function calls, e.g., member-equal; see *note EQUALITY-VARIANTS::.
    Guard-holders are also now expanded away before storing constraints for
    encapsulate events, which can sometimes result in simpler constraints.
    
    Improved the performance of dmr (technical note: by modifying raw Lisp
    code for function dmr-flush, replacing finish-output by force-output).
    
    We now avoid certain rewriting loops.  A long comment about this change,
    including an example of a loop that no longer occurs, may be found in
    source function expand-permission-result.
    
    Slightly strengthened type-set reasoning at the level of literals (i.e.,
    top-level hypotheses and conclusions).  See the comment in ACL2 source
    function rewrite-atm about the "use of dwp = t" for an example of a
    theorem provable only after this change.
    
    Strengthened the ability of type-set reasoning to make deductions about
    terms being integers or non-integer rationals.  The following example
    illustrates the enhancement: before the change, no simplification was
    performed, but after the change, the conclusion simplifies to (foo t).
    Thanks to Robert Krug for conveying the problem to us and outlining a
    solution.
    
         (defstub foo (x) t)
         (thm ; should reduce conclusion to (foo t)
          (implies (and (rationalp x)
                        (rationalp y)
                        (integerp (+ x (* 1/3 y))))
                   (foo (integerp (+ y (* 3 x))))))
    
    *BUG FIXES*
    
    Fixed a class of soundness bugs involving each of the following
    functions: getenv$, get-wormhole-status, cpu-core-count, wormhole-p,
    random$, file-write-date$, and serialize-read-fn, and (for the HONS
    version of ACL2) clear-memoize-table and clear-memoize-tables as well
    as (possible soundness bug) serialize-write-fn.  For example, we were
    able to admit the following events, but that is no longer the case
    (neither for getenv$ as shown, nor analogously for other functions
    listed above).
    
         (defthm not-true
           (stringp (cadr (getenv$ "PWD" (build-state))))
           :rule-classes nil)
    
         (defthm contradiction
           nil
           :hints (("Goal"
                    :in-theory (disable (getenv$))
                    :use not-true))
           :rule-classes nil)
    
    Fixed a soundness bug involving with-live-state, which could cause an
    error in the use of add-include-book-dir or delete-include-book-dir in
    a book or its portcullis commands.  See *note WITH-LIVE-STATE::, as the
    documentation for this macro has been updated; in particular it is now
    untouchable (see *note REMOVE-UNTOUCHABLE::) and is intended only for
    system hackers.  Thanks to Jared Davis for reporting a bug in the use
    of add-include-book-dir after our first attempt at a fix.
    
    Fixed a soundness bug based on the use of skip-proofs together with the
    little-used argument k=t for certify-book.  An example proof of nil
    appears in a comment in the ACL2 sources, in (deflabel note-5-0 ...).
    
    Fixed a soundness bug that allowed users to define new proof-checker
    primitive commands.  Before this fix, a book proving nil could be
    certified, as shown in a comment now in the introduction of the table
    pc-command-table in source file proof-checker-a.lisp.
    
    (Technical change, primarily related to make-event:) Plugged a security
    hole that allowed books' certificates to be out-of-date with respect to
    make-event expansions, but not recognized as such.  The change is to
    include the so-called expansion-alist in the certificate's checksum.
    An example appears in a comment in the ACL2 sources, in (deflabel
    note-5-0 ...).
    
    Fixed a bug in guard verification due to expanding calls of primitives
    when translating user-level terms to internal form, so called
    "translated terms" (see *note TERM::).  While we have not observed a
    soundness hole due to this bug, we have not ruled it out.  Before the
    bug fix, the following event was admissible, as guard verification
    succeeded (but clearly should not have).
    
         (defun f ()
           (declare (xargs :guard t))
           (car (identity 3)))
    
    For those who want details about this bug, we analyze how ACL2 generates
    guard proof obligations for this example.  During that process, it
    evaluates ground subexpressions.  Thus, (identity '3) is first
    simplified to '3; so a term must be built from the application of car
    to '3.  Guard-checking is always turned on when generating guard proof
    obligations, so now, ACL2 refuses to simplify (car '3) to 'nil.
    However, before this bug fix, when ACL2 was building a term by applying
    car to argument '3, it did so directly without checking guards; source
    code function cons-term is `smart' that way.  After the fix, such
    term-building reduction is only performed when the primitive's guard is
    met.
    
    While calls of many event macros had been prohibited inside executable
    code, others should have been but were not.  For example, the following
    was formerly allowed.
    
         (defun foo (state)
           (declare (xargs :mode :program :stobjs state))
           (add-custom-keyword-hint :my-hint (identity nil)))
         (foo state) ; Caused hard raw Lisp error!
    
    Thus, several event macros (including for example
    add-custom-keyword-hint) may no longer be called inside executable code.
    
    Fixed an assertion that could occur, for example, after reverting to
    prove the original goal by induction and generating a goal of NIL.
    Thanks to Jared Davis for sending us a helpful example to bring this
    bug to our attention.
    
    It was possible for defstobj to generate raw Lisp code with excessively
    restrictive type declarations.  This has been fixed.  Thanks to Warren
    Hunt for reporting this bug and sending an example that illustrates it.
    See *note STOBJ-EXAMPLE-2:: for examples of such raw Lisp code; now,
    one finds (and fixnum (integer 0 *)) where formerly the type was
    restricted to (integer 0 268435455).
    
    Fixed a bug in that was ignoring the use of :computed-hint-replacement
    in certain cases involving a combination of computed hints and custom
    keyword hints.  Thanks to Robert Krug for reporting this bug and
    sending a very helpful example.
    
    Fixed a bug in the output from defattach, which was failing to list
    previous events in the message about "bypassing constraints that have
    been proved when processing the event(s)".
    
    (GCL only) Fixed a bug in set-debugger-enable (which was only a bug in
    GCL, not an issue for other host Lisps).
    
    Fixed ACL2 trace output to indent properly for levels above 99 (up to
    9999).  Thanks to Warren Hunt for bringing this bug to our attention.
    
    Fixed a bug in the reporting of times in event summaries -- probably one
    that has been very long-standing!  The times reported had often been too
    small in the case of compound events, notably include-book.  Thanks to
    everyone who reported this problem (we have a record of emails from
    Eric Smith and Jared Davis on this issue).
    
    Fixed a bug in :expand hints, where the use of :lambdas could prevent
    other parts of such a hint.  For example, the following invocation of
    thm failed before this fix was made.
    
         (defund foo (x) (cons x x))
         (thm (equal (car (foo x)) x)
         :hints (("Goal" :expand (:lambdas (foo x)))))
    
    Certain "program-only" function calls will now cause hard Lisp errors.
    (The rather obscure reason for this fix is to support logical modeling
    of the ACL2 evaluator.  A relevant technical discussion may be found in
    source function oneify-cltl-code, at the binding of variable
    fail_program-only-safe.)
    
    There was an unnecessary restriction that FLET-bound functions must
    return all stobjs among their inputs.  For example, the following
    definition was rejected because state was not among the outputs of h.
    This restriction has been removed.
    
         (defun foo (state)
           (declare (xargs :stobjs state))
           (flet ((h (state) (f-boundp-global 'x state)))
             (h state)))
    
    We fixed a bug, introduced in the preceding release (Version  4.3), in
    the check for irrelevant formals (see *note IRRELEVANT-FORMALS::).
    That check had been too lenient in its handling of lambda (LET)
    expressions, for example allowing the following definition to be
    admitted in spite of its first formal parameter obviously being
    irrelevant.
    
         (defun foo (x clk)
           (if (zp clk)
               :diverge
             (let ((clk (1- clk)))
               (foo x clk))))
    
    Fixed a bug in the mini-proveall target in GNUmakefile.  The fix
    includes a slight change to the :mini-proveall command (an extra event
    at the end).  Thanks to Camm Maguire for reporting this bug.
    
    Fixed a bug that occurred when certify-book was called after using
    set-fmt-soft-right-margin or set-fmt-hard-right-margin to set a small
    right margin.
    
    Fixed set-inhibit-warnings so that it takes effect for a subsequent
    include-book event.  Thanks to Jared Davis and David Rager for queries
    that led to this fix.
    
    Hard Lisp errors are now avoided for certain :rewrite rules: those
    whose equivalence relation is other than equal when the rule is
    originally processed, but is no longer a known equivalence relation
    when the rule is to be stored.  Thanks to Jared Davis for sending a
    useful example, a minor variant of which is included in a comment in
    source function interpret-term-as-rewrite-rule (file defthm.lisp).
    
    Fixed a bug in the ACL2 evaluator (source function raw-ev-fncall), which
    was unlikely to be exhibited in practice.
    
    Fixed a hard Lisp error that could occur for ill-formed :meta
    rule-classes, e.g., (:meta :trigger-fns '(foo)).
    
    It is now an error to include a stobj name in the :renaming alist (see
    *note DEFSTOBJ::).
    
    Some bogus warnings about non-recursive function symbols have been
    eliminated for rules of class :type-prescription.
    
    (Allegro CL host Lisp only) Fixed an obsolete setting of compiler
    variable comp:declared-fixnums-remain-fixnums-switch, which may have
    been responsible for intermittent (and infrequent) checksum errors
    encountered while including books during certification of the
    regression suite.
    
    Fixed a proof-checker bug that could result in duplicate goal names in
    the case of forced hypotheses.  An example showing this bug, before the
    fix, appears in a comment in the ACL2 sources, in (deflabel note-5-0
    ...).
    
    We fixed a bug in a prover routine involved in type-set computations
    involving linear arithmetic.  This bug has been around since at least
    as far back as Version_3.3 (released November, 2007).  We are not aware
    of any resulting unsoundness, though it did have the potential to
    weaken the prover.  For example, the following is proved now, but was
    not proved before the bug was fixed.
    
         (thm
          (implies (and (rationalp x)
                        (rationalp y)
                        (integerp (+ (* 1/3 y) x)))
                   (integerp (+ y (* 3 x))))
          :hints (("Goal" :in-theory (disable commutativity-of-+))))
    
    Although all bets are off when using redefinition (see *note
    LD-REDEFINITION-ACTION::), we wish to minimize negative effects of its
    use, especially raw Lisp errors.  The examples below had caused raw Lisp
    errors, but no longer.
    
         (defstobj st fld :inline t)
         (redef!)
         (defstobj st new0 fld)
         (u)
         (fld st) ; previously an error, which is now fixed
    
         ; Fresh ACL2 session:
         (redef!)
         (defun foo (x) x)
         (defmacro foo (x) `(quote ,x))
         (u)
    
         ; Fresh ACL2 session:
         (redef!)
         (defmacro foo (x) (cons 'list x))
         (defun foo (x) x)
    
    Fixed a bug that could cause hard Lisp errors in an encapsulate event.
    Thanks to Sol Swords for sending an example that exhibited this bug.
    Here is a simpler such example; the bug was in how it was checked
    whether the guard for a guard-verified function (here, g) depends on
    some function introduced in the signature of the encapsulate (here, the
    function f).
    
         (encapsulate
          ((f (x) t))
          (local (defun f (x) (declare (xargs :guard t)) x))
          (defun g (x)
            (declare (xargs :guard (if (integerp x) (f x) t)))
            x))
    
    Fixed a bug in mfc-relieve-hyp that we believe could prohibit its use on
    the last hypothesis.  Thanks to Sol Swords for reporting this bug and
    providing a fix.
    
    The syntax #! (see *note SHARP-BANG-READER::) was broken after a skipped
    readtime conditional.  For example, the following input line caused an
    error.
    
         #+skip #!acl2(quote 3)
    
    This bug has been fixed.
    
    Fixed a bug in the break-rewrite utility, which was evidenced by error
    messages that could occur when dealing with free variables.  An example
    of such an error message is the following; we thank Robert Krug for
    sending us an example that produced this error and enabled us to
    produce a fix.
    
         HARD ACL2 ERROR in TILDE-@-FAILURE-REASON-PHRASE1:  Unrecognized failure
         reason, ((MEM-ARRAY . X86) (ADDR QUOTE 9)).
    
    We fixed an obscure bug that we believe could interfere with defproxy
    because of an incorrect (declaim (notinline )) form.
    
    *CHANGES AT THE SYSTEM LEVEL AND TO DISTRIBUTED BOOKS*
    
    Improvements have been made related to the reading of characters.  In
    particular, checks are now done for ASCII encoding and for the expected
    char-code values for Space, Tab, Newline, Page, and Rubout.  Also, an
    error no longer occurs with certain uses of non-standard characters.
    For example, it had caused an error to certify a book after a single
    portcullis command of (make-event `(defconst *my-null* ,(code-char
    0))); but this is no longer an issue.  Thanks to Jared Davis for
    helpful correspondence that led us to make these improvements.
    
    The character encoding for reading from files has been fixed at
    iso-8859-1.  See *note CHARACTER-ENCODING::.  Thanks to Jared Davis for
    bringing this portability issue to our attention (as this change arose
    in order to deal with a change in the default character encoding for
    the host Lisp, CCL), and pointing us in the right direction for dealing
    with it.  In many cases, the character encoding for reading from the
    terminal is also iso-8859-1; but this is not guaranteed.  In
    particular, when the host Lisp is SBCL this may not be the case.
    
    Although the HTML documentation is distributed with ACL2, it had not
    been possible for users to build that documentation without omitting
    graphics, for example on the ACL2 home page.  That has been fixed, as
    files graphics/*.gif are now distributed.
    
    Compiler warnings are suppressed more completely than they had been
    before.  For example, the following had produced a compiler warning
    when the host Lisp is CCL, but no longer does so.
    
         (defun f () (car 3))
         (trace$ f)
    
    Removed support for "tainted" certificates.  One reason is that there
    are rarely incremental releases.  A stronger reason is that for the
    compatibility of a new release is with the previous non-incremental
    release, it's not particularly relevant whether or not the new release
    is incremental.
    
    The `make' variable BOOKS can now be defined above the line that
    includes Makefile-generic.  (For relevant background, see *note
    BOOKS-CERTIFICATION-CLASSIC::.)
    
    (SBCL only) ACL2 images built on SBCL now have an option,
    -dynamic-space-size 2000, that can avoid space problems that could
    previously have caused the session to die.
    
    The default value for variable LISP in file GNUmakefile is now ccl.
    Thus, if you use `make' in the standard way to build an ACL2
    executable, the default host Lisp is ccl rather than gcl.
    
    *EMACS SUPPORT*
    
    *EXPERIMENTAL VERSIONS*
    
    For the version supporting the reals, ACL2(r) (see *note REAL::), the
    supporting function floor1 has been defined in raw Lisp.  This avoids
    an error such as in the following case.
    
         (defun f () (declare (xargs :guard t)) (floor1 8/3))
         (f) ; had caused raw Lisp error, before the fix
    
    Among the enhancements for the parallel version, ACL2(p) (see *note
    PARALLELISM::), are the following.  We thank David Rager for his work
    in developing ACL2(p) and these improvements in particular.
    
         The macro set-parallel-evaluation has been renamed
         set-parallel-execution.
    
         Calls of the macro set-waterfall-printing are no longer events, so
         may not be placed at the top level of books.  However, it is easy
         to create events that have these effects; see *note
         SET-WATERFALL-PRINTING::.  Note that now, :ubt and similar
         commands do not change the settings for either
         waterfall-parallelism or waterfall-printing.
    
         The implementation of deflock has been improved.  Now, the macro it
         defines can provide a lock when invoked inside a guard-verified or
         :program mode function.  Previously, this was only the case if the
         function definition was loaded from raw Lisp, typically via a
         compiled file.
    
         The underlying implementation for waterfall parallelism (see *note
         SET-WATERFALL-PARALLELISM::) has been improved.  As a result, even
         the largest proofs in the regression suite can be run efficiently
         in :resource-based waterfall parallelism mode.  Among these
         improvements is one that can prevent machines from rebooting
         because operating system limits have been exceeded; thanks to
         Robert Krug for bringing this issue to our attention.
    
         There is also a new flag for configuring the way waterfall
         parallelism behaves once underlying system resource limits are
         reached.  This flag is most relevant to :full waterfall
         parallelism.  see *note SET-TOTAL-PARALLELISM-WORK-LIMIT:: for
         more information.
    
         The dmr utility has the same behavior in ACL2(p) as it has in ACL2
         unless waterfall-parallelism has been set to a non-nil value (see
         *note SET-WATERFALL-PARALLELISM::), in which case statistics about
         parallel execution are printed instead of the usual information.
    
         The user can now build the regression suite using waterfall
         parallelism.  See the distributed file
         acl2-customization-files/README for details, and see *note
         UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES:: for a disclaimer
         related to building the regression suite using waterfall
         parallelism.
    
         When building ACL2 with both the hons and parallelism extensions
         (what is called "ACL2(hp)"), the functions that are automatically
         memoized by the hons extension are now automatically unmemoized
         and memoized when the user toggles waterfall parallelism on and
         off, respectively.
    
         Calling set-waterfall-parallelism with a flag of t now results in
         the same settings as if it were called with a flag of
         :resource-based, which is now the recommended mode for waterfall
         parallelism.  Thanks to Shilpi Goel for requesting this feature.
    
         The prover now aborts in a timely way in response to interrupts
         issued during a proof with waterfall parallelism enabled.  (This
         had often not been the case.)  Thanks to Shilpi Goel for
         requesting this improvement.
    
    
    Among the enhancements for the HONS extension (see *note
    HONS-AND-MEMOIZATION::) are the following.
    
         The compact-print code has been replaced by new serialization
         routines contributed by Jared Davis.  This may improve performance
         when including books that contain make-events that expand to very
         large constants.  You can also now save objects to disk without
         going into raw lisp; see *note SERIALIZE:: for details.
    
         Printing of certain messages has been sped up (by using Lisp
         function force-output in place of finish-output).  Thanks to Jared
         Davis for contributing this improvement.
    
         Stobj array writes are perhaps twice as fast.
    
         It is now permitted to memoize functions that take user-defined
         stobjs as inputs, provided that no stobjs are returned.  Even if
         stobjs are returned, memoization is permitted provided the
         condition is nil, as when profiling (see *note PROFILE::).  Thanks
         to Sol Swords for an observation that led to this improvement and
         for useful conversations, including follow-up leading us to
         improve our initial implementation.
    
         Fixes have been made for memoizing with a non-nil value of
         :ideal-okp.  Errors had occurred when memoizing with a :condition
         other than t for a :logic mode function that had not been
         guard-verified, even with a non-nil value of :ideal-okp; and after
         successfully memoizing such a function (without such :condition),
         it had not been possible to unmemoize it.  Thanks to Sol Swords for
         reporting issues with the :ideal-okp argument of memoize.
    
         If a book defined a function that was subsequently memoized in that
         book, the function would no longer behaves as memoized upon
         completion of book certification (unless that certify-book command
         was undone and replaced by evaluation of a corresponding
         include-book command).  This has been fixed.  Thanks to David
         Rager for pointing out the problem by sending an example.
    
         We now support ACL2(h) built not only on 64-bit CCL but also on
         all supported host Ansi Common Lisps (i.e., all supported host
         Lisps except GCL).  Thanks to Jared Davis for doing much of the
         work to make this improvement.  Note that performance will likely
         be best for 64-bit CCL; for some Lisps, performance may be much
         worse, probably depending in part on the underlying implementation
         of hash tables.
    
    
    
    File: acl2-doc-emacs.info,  Node: NOTE-6-0,  Next: NOTE-6-1,  Prev: NOTE-5-0,  Up: RELEASE-NOTES
    
    NOTE-6-0    ACL2 Version  6.0 (December, 2012) Notes
    
    NOTE!  New users can ignore these release notes, because the
    documentation has been updated to reflect all changes that are recorded
    here.
    
    Below we roughly organize the changes since Version  5.0 into the
    following categories of changes: existing features, new features,
    heuristic improvements, bug fixes, changes at the system level, Emacs
    support, and experimental versions.  Each change is described in just
    one category, though of course many changes could be placed in more
    than one category.
    
    NOTE.  But we start with one major change that is outside the usual
    categories:
    
    *LICENSE change*
    
    The ACL2 license has been changed from GPL Version 2 to a 3-clause BSD
    license, found in the LICENSE file distributed with ACL2.
    
    *CHANGES TO EXISTING FEATURES*
    
    Function fmt-to-string and similar functions (see *note
    PRINTING-TO-STRINGS::) now use the default right margin settings;
    formerly the right margin had been set at 10,000.  If you want the
    former behavior, you can use the :fmt-control-alist, as illustrated
    below.
    
         (fmt-to-string "~x0"
                        (list (cons #\0 (make-list 30)))
                        :fmt-control-alist
                        `((fmt-soft-right-margin . 10000)
                          (fmt-hard-right-margin . 10000)))
    
    The use of attachments (see *note DEFATTACH::) has been made more
    efficient, avoiding some low-level checks (Common Lisp `boundp'
    checks).  Thanks to Shilpi Goel for constructing an example that we
    used to help direct us to remove inefficiency.  The following results
    for that example -- a Fibonacci program run on a machine interpreter in
    raw-mode (see *note SET-RAW-MODE::) -- give a sense of the potential
    speedup, though we note that a full ACL2(h) regression showed no
    significant speedup.
    
         ; Time before the change:
         ; 0.89 seconds realtime, 0.90 seconds runtime
    
         ; Time after the change:
         ; 0.75 seconds realtime, 0.75 seconds runtime
    
         ; Time when cheating to avoid the cost of attachments, by redefining a
         ; function to BE its attachment (so, this gives a lower bound on possible
         ; execution time):
         ; 0.72 seconds realtime, 0.72 seconds runtime
    
    Functions read-acl2-oracle and read-acl2-oracle@par are no longer
    untouchable (see *note REMOVE-UNTOUCHABLE::).  We reported this change
    for Version_5.0 but it was not made; thanks to Jared Davis for bringing
    this to our attention.  Function get-timer also is no longer
    untouchable.
    
    The function butlast now behaves more reasonably on arguments violating
    its guard.  For example, (butlast '(1 2 3) -1) is now provably equal to
    (1 2 3) instead of to (1 2 3 nil).  Thanks to Jared Davis for
    suggesting a change to the definition of butlast.
    
    The utilities mfc-ts and mfc-ap (see *note EXTENDED-METAFUNCTIONS::)
    formerly never used forcing (see *note FORCE::).  Now, by default,
    forcing is allowed during execution of these functions if and only if
    it is permitted in the rewriting environment where they are called.
    Moreover, these and the mfc-xx utilities -- mfc-rw, mfc-rw+, and
    mfc-relieve-hyp -- are now macros that take (optional) keyword
    arguments :forcep and :ttreep.  The :forcep argument is :same by
    default, providing the forcing behavior inherited from the environment
    (as described above); but it can be the symbol t or nil, indicating
    that forcing is to be enabled or disabled, respectively.  The :ttree
    argument is nil by default, but when it is t, then a second value is
    returned, which is a tag-tree.  See *note EXTENDED-METAFUNCTIONS::.
    
    Many improvements have been made to the tau-system (see *note
    TAU-SYSTEM::), including support for arithmetic intervals bounded by
    constants.  Thus, for example, (and (<= 0 x) (<= x 15)) is a tau
    predicate.  The documentation has also been improved (see *note
    INTRODUCTION-TO-THE-TAU-SYSTEM::). Also see *note TIME-TRACKER-TAU:: for
    discussion of how the new time-tracker utility can help discover ways
    to detect slowdown related to the tau-system.
    
    The defthm events printed by defabsstobj, namely those that remain to
    be proved, are now given with :rule-classes nil since there is probably
    no intention to use them as rules.  Thanks to Robert Krug for
    suggesting that we consider this change.
    
    The formal parameters for a macro definition (see *note DEFMACRO::) may
    now include state and user-defined stobjs.  (However, macro formals may
    not be declared as stobjs; see *note XARGS::.)  Thanks to Jose Luis
    Ruiz-Reina for raising this issue and to Rob Sumners for helpful
    conversations -- both of these nearly 10 years ago!
    
    The utilities defun-inline, defun-notinline, defund-inline, and
    defund-notinline have been simplified, by taking advantage of the
    lifting of restrictions on formal parameters of macro definitions
    mentioned above (involving symbols that happen to be stobj names).
    Now, when any of the above four utilities is called with a given set of
    formal parameters, those formals will be used not only for the
    generated defun event but also for the generated defmacro event.
    (Previously, they had been renamed for the defmacro event in order to
    respect the stobj name restriction that no longer exists.)  Thanks to
    Jared Davis for pointing out the value of making this change.
    
    The events add-invisible-fns and remove-invisible-fns now convert
    arguments as appropriate using the macro-aliases-table.  For example,
    the event (add-invisible-fns append car) is now legal (though probably
    not a good idea), because add-invisible-fns is now sensitive to the
    fact that append maps to binary-append in the macro-aliases-table.
    
    When :pe is applied to a built-in function that does not have a
    defining event, such as symbolp, :pe now gives more useful output that
    points to the documentation instead of printing a call of
    ENTER-BOOT-STRAP-MODE.  Thanks to Anthony Knape for bringing this issue
    to our attention.
    
    The macros memoize and unmemoize now cause a warning rather than an
    error in ACL2 (and work as before in ACL2(h)).
    
    Terms are now parsed into :type-prescription rules in a manner that
    removes let bindings both at the top level and in the conclusion (but
    still not in the hypotheses of the rule).  See *note
    TYPE-PRESCRIPTION::.  Thanks to Jared Davis for requesting such an
    enhancement.
    
    Printing of numbers is now appropriately sensitive to the print radix;
    see *note SET-PRINT-RADIX::.  Thanks to Shilpi Goel for requesting this
    enhancement.
    
    The system function explode-atom no longer includes the radix indicator.
    The new function explode-atom+ may be used for that purpose.
    
    *NEW FEATURES*
    
    Among the new features for system hackers are analogues of system
    function simple-translate-and-eval that do not return state.  (Thanks to
    David Rager for requesting this feature and helpful conversations on its
    implementation.)  This and other low-level changes are typically
    documented in comments in the corresponding release note event, which
    in this case is (deflabel note-6-0 ...).
    
    More built-in functions are now guard-verified (and in :logic mode).
    Furthermore, a mechanism exists for marking yet more built-in functions
    as guard-verified based on books contributed by users; see Part II of
    `http://www.cs.utexas.edu/users/moore/acl2/open-architecture/'.  The
    current state of that enterprise may be viewed by evaluating the
    constant *system-verify-guards-alist*, which associates a community
    book name with a list of functions.  When ACL2 is built in the normal
    way, each of those functions is marked as guard-verified when ACL2 is
    started up; but a special developer build can be used to check that the
    indicated book, together with its sub-books, proves that those
    functions are guard-verified.
    
    Metatheorems (see *note META::) may now have additional hypotheses,
    called "meta-extract hypotheses", that allow metafunctions to depend on
    the validity of certain terms extracted from the context or the logical
    world.  See *note META-EXTRACT::.  Thanks to Sol Swords for providing
    an initial implementation, together with very helpful discussions as
    well as a community book,
    books/clause-processors/meta-extract-user.lisp, that extends the power
    of meta-extract hypotheses.
    
    New utilities oracle-funcall, oracle-apply, and oracle-apply-raw call a
    function argument on specified arguments.  Thanks to Jared Davis for
    requesting this utility.
    
    A new utility makes it convenient to track time spent inside specified
    function calls or, more generally, during specified evaluation.  See
    *note TIME-TRACKER::.
    
    New runic designators make it easy to refer to macro names when building
    theories.  Thus, for example, the object (:i append) may be used in
    theory expressions to designate the rune (:induction binary-append).
    See *note THEORIES::.  Thanks to Jared Davis for a useful discussion
    leading to this enhancement.
    
    Defabsstobj events now take an optional :congruent-to keyword argument,
    much like defstobj.  Thanks to Sol Swords for requesting this feature
    and for suggesting a very nice optimization that avoids the need to
    prove additional lemmas.
    
    Flet may now include inline and notinline declarations.  Thanks to
    Jared Davis for requesting this feature.
    
    The utility gc-verbose controls printing of messages by the garbage
    collector, for certain host Lisps.  See *note GC-VERBOSE::.  Thanks to
    Shilpi Goel for requesting this utility.
    
    Added definitions of functions nat-listp and acl2-number-listp.  Thanks
    to Harsh Raju Chamarthi for requesting these additions.  Many community
    books had varying definitions of these functions; these additions
    guarantee that all books must agree on how these two functions are
    defined.  (Some community books have been changed in order that they
    remain certifiable, given these additions.)  Note that a few built-in
    :forward-chaining rules were modified in order to accommodate these
    additions, and the definition of integer-listp was modified to call eq
    instead of equal, like the other such definitions.
    
    See *note GET-COMMAND-SEQUENCE:: for a new utility that returns a list
    of commands between two given command descriptors.
    
    *HEURISTIC IMPROVEMENTS*
    
    We obtained a substantial speedup -- 13% observed for the regression
    suite, and 8% observed for the ACL2(h) regression suite -- by tweaking
    the break-rewrite implementation to eliminate virtually all of its
    overhead when it is not in use (the default, which holds until :brr t is
    evaluated).  Thanks to David Rager for a conversation involving ACL2(p)
    performance statistics that suggested looking at changing break-rewrite
    to boost performance.
    
    The heuristics for automatically expanding recursive function calls
    have been changed during proofs by induction.  Now, during induction,
    more terms that suggested the induction scheme are automatically
    expanded.  Thanks to David Rager for providing an example and having
    discussions with us that spurred us to develop this heuristic
    improvement.
    
    *BUG FIXES*
    
    Fixed a soundness bug in defabsstobj based on guards that violated
    single-threadedness restrictions.  Thanks to Sol Swords for bringing
    this bug to our attention and supplying a proof of nil, which we include
    as a comment in source file ld.lisp, in (deflabel note-6-0 ...).  We
    also thank Sol for helpful discussions about guards of functions
    introduced by defabsstobj, which has led us to enhance the
    documentation; see *note DEFABSSTOBJ::.
    
    Fixed a soundness bug in defabsstobj based on interrupted updates of
    abstract stobjs.  As part of the fix a new keyword, :PROTECT, has been
    introduced for defabsstobj exports, along with a new top-level
    defabsstobj keyword, :PROTECT-DEFAULT; see *note DEFABSSTOBJ::.  We do
    some analysis that we expect will avoid the use of :PROTECT in many
    cases, which is fortunate since the use of :PROTECT t may cause a slight
    slowdown in (abstract) stobj updates.  Thanks to Sol Swords for
    bringing this bug to our attention and supplying a proof of nil, which
    we include as a comment in source file other-events.lisp, in the
    definition of function set-absstobj-debug.
    
    Fixed a raw Lisp error that occurred when tracing a stobj resize
    function, thanks to an error report from Warren Hunt, Marijn Heule, and
    Nathan Wetzler.
    
    Fixed a raw Lisp error that occurred for certain ill-formed signatures,
    as in the following example.
    
         ACL2 !>(encapsulate
                    (((f (*) => * :guard t)))
                    (local (defun f (x) (consp x))))
    
         ***********************************************
         ************ ABORTING from raw Lisp ***********
         Error:  value (F (*) => * :GUARD T) is not of the expected type SYMBOL.
         ***********************************************
    
    The notion of "error triple" (see *note ERROR-TRIPLES::) had been
    implemented ambiguously, with the result that for a stobj, st, the
    result of evaluating the following two forms was the same: (mv nil st
    state) and (mv t st state).  Of course, these are just examples; in
    general, a result of (mv erp val state) was sometimes treated as an
    error triple even when val is a stobj.  Now, (mv erp val state) is an
    error triple only when erp and val are ordinary (non-stobj) values.
    Thanks to Warren Hunt and Marijn Heule for bringing this problem to our
    attention.
    
    The "with-error-trace" utility, wet, now works in the non-error case
    when given a form that returns multiple values.  (Note however that
    STATE will be printed as REPLACED-STATE; and similarly, a user-defined
    stobj, say ST, will be printed as REPLACED-ST.)
    
    Some possible error messages for defabsstobj have been fixed that had
    been ill-formed.  Thanks to Sol Swords for bringing this bug to our
    attention.
    
    Fixed a bug that sometimes caused the times displayed in the summary for
    certify-book to be smaller than the actual times.
    
    Fixed a bug in the guards to system functions fmt-char and fmt-var,
    which are no longer :logic-mode, guard-verified functions.
    
    (GCL only) Fixed a bug present in Gnu Common Lisp for #u (see *note
    SHARP-U-READER::).
    
    *CHANGES AT THE SYSTEM LEVEL*
    
    The state global variable 'distributed-books-dir has been renamed
    'system-books-dir.  On a related note, the documentation now refers to
    "community books" rather than "distributed books", and there is a
    corresponding new documentation topic; see *note COMMUNITY-BOOKS::.
    
    Fixed a bug in the implementation of wet (which is actually in the
    community book books/misc/wet.lisp).
    
    A directory, interface/, is no longer part of the ACL2 distribution.
    Rather, it is a subdirectory of the ACL2 community books.  Thus, if you
    fetch those books in the usual way (see the installation instructions
    on the ACL2 home page), you will find a directory books/interface/.
    Subdirectory emacs/ of that interface directory provides Emacs support
    for proof-trees as well an acl2-mode.  This change has been reflected in
    ACL2 file emacs/emacs-acl2.el, so users will probably not be impacted if
    they load that file into Emacs.
    
    The community books file books/Makefile-generic now causes, by default,
    a backtrace to be printed when there is a raw Lisp error.
    
    Some changes have been made to how regressions are run, i.e., to how the
    community books are certified.  (1) The standard regression now includes
    community books directory books/centaur.  To skip these (for example, a
    Windows system has encountered difficulty with them even after
    installing Perl), include ACL2_CENTAUR=skip with your `make' command.
    (2) A new `make' (or environment) variable, ACL2_JOBS, specifies the
    number of parallel jobs to run, serving as a replacement for the -j
    argument of `make' that works for all community books, including those
    under directory centaur; see *note BOOKS-CERTIFICATION-CLASSIC::.  (3)
    It is no longer necessary to do an ACL2(h) regression in order to build
    a copy of the documentation generated by Jared Davis's xdoc utility at
    books/xdoc-impl/manual/preview.html; a vanilla ACL2 regression will
    build this manual.  (4) It is no longer necessary to set the ACL2
    environment variable for ACL2(h) regressions if you want to use the
    executable saved_acl2h in the ACL2 sources directory.
    
    The ACL2 home page now has a search utility for documentation and books.
    Thanks to Shilpi Goel and David Rager for feedback on a preliminary
    version of this utility.
    
    (only for SBCL with 64-bit ACL2(h)) The value of SBCL command line
    option -dynamic-space-size for ACL2(h) on 64-bit platforms has been
    increased from 2000 to 16000 (as explained in a comment in the ACL2
    source definition of *sbcl-dynamic-space-size*).
    
    *EMACS SUPPORT*
    
    *EXPERIMENTAL/ALTERNATE VERSIONS*
    
    Among the enhancements for ACL2(r) (see *note REAL::) are the following.
    
         Thanks to Ruben Gamboa for his helpful role in making the following
         improvements made with Ruben Gamboa in support for non-standard
         analysis in ACL2(r).
    
         Constrained functions can now be introduce as non-classical.  See
         *note SIGNATURE::.
    
         Defun-sk now takes a new keyword argument, :CLASSICALP, that
         determines whether or not the named function is classical.  See
         *note DEFUN-SK::.
    
         Incorporated a bug fix from Ruben Gamboa for ceiling.  The default
         (for `bad' arguments) had been 1, but now we follow normal ACL2
         practice by returning 0 in that case.
    
    
    Among the enhancements for the HONS extension (see *note
    HONS-AND-MEMOIZATION::) are the following.
    
         Macros with-fast-alist, with-stolen-alist, and
         fast-alist-free-on-exit are now defined in ACL2(h), rather than
         being defined in the community book
         "books/centaur/misc/hons-extra.lisp".  Thanks to Jared Davis and
         Sol Swords for donating this code, and thanks to Jared for helpful
         discussions leading to this change.
    
    
    Among the enhancements for ACL2(p) (see *note PARALLELISM::) are the
    following.  We thank David Rager for his work in developing ACL2(p) and
    for his helpful role in these improvements.
    
         A bug has been fixed that could leave one in a wormhole, awaiting
         input, after an error, such as an error in an :in-theory hint
         during a proof.  Thanks to Shilpi Goel for bringing this bug to
         our attention.
    
         A key checkpoint for a given goal is now printed only once.
         Previously, if a key checkpoint led to more than one goal pushed
         for proof by induction, the key checkpoint would be printed once
         for each such goal during the proof, and also once for each such
         goal in the summary at the end.
    
    
    
    File: acl2-doc-emacs.info,  Node: NOTE-6-1,  Next: NOTE-6-2,  Prev: NOTE-6-0,  Up: RELEASE-NOTES
    
    NOTE-6-1    ACL2 Version  6.1 (February, 2013) Notes
    
    NOTE!  New users can ignore these release notes, because the
    documentation has been updated to reflect all changes that are recorded
    here.
    
    Below we roughly organize the changes since Version  6.0 into the
    following categories of changes: existing features, new features,
    heuristic improvements, bug fixes, changes at the system level, Emacs
    support, and experimental versions.  Each change is described in just
    one category, though of course many changes could be placed in more
    than one category.
    
    *CHANGES TO EXISTING FEATURES*
    
    More system functions are in :logic mode, guard-verified.  Evaluate
    
         (strip-cars (cdr (assoc-equal "system/top" *system-verify-guards-alist*)))
    
    for the list of functions checked to be guard-verifiable in the
    community books.  Thanks to those who have contributed to this effort,
    as shown in file headers in directory system/ of the community books.
    
    The macro defund now avoids an error when :mode :program has been
    specified in an xargs form of a declare form, for example: (defund f
    (x) (declare (xargs :mode :program)) x).  It does this by avoiding the
    generation of in-theory events in such cases.  Thanks to David Rager
    and Jared Davis for requesting such a change, and for ensuing helpful
    discussions.
    
    Added a field :UNIFY-SUBST to metafunction contexts (see *note
    EXTENDED-METAFUNCTIONS::), accessed with function mfc-unify-subst.
    Thanks to Sol Swords for requesting this enhancement.
    
    The functions sys-call and sys-call-status are now guard-verified
    :logic-mode functions.
    
    It had been the case that if any supporter of a dependent clause
    processor (see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR::) is among the
    ancestors of a given formula, then it was illegal to apply functional
    instantiation (see *note LEMMA-INSTANCE::) to that formula.  Now, this
    is illegal only if some such supporter is in the domain of the
    functional substitution.
    
    The tau system (see *note TAU-SYSTEM::, or if you are unfamiliar with
    the tau system, see *note INTRODUCTION-TO-THE-TAU-SYSTEM::) now allows
    the user to define and verify functions that compute bounds on
    arithmetic expressions.  See *note BOUNDERS::.
    
    The utility print-summary-user has been replaced by
    finalize-event-user, which is described below.  If you previously
    attached a function to print-summary-user, say my-print-summary-user,
    then you can get the effect you had previously as follows.
    
         (defun my-finalize-event-user (state)
           (declare (xargs :mode :logic :stobjs state))
           (prog2$ (my-print-summary-user state)
                   state))
         (defattach finalize-event-user my-finalize-event-user)
    
    It had been the case that when you LD a file, the connected book
    directory (see *note CBD::) was set to the canonical pathname of that
    file's directory for the duration of the LD call.  This could cause
    problems, however, if the file is actually a soft link: an include-book
    form in the book with a relative pathname for the book would be
    resolved with respect to the absolute pathname for that link, which is
    probably not what was intended.  So soft links are no longer followed
    when computing the above connected book directory.  The following
    example, which is how we discovered this problem, may clarify.  We
    attempted to execute the form (ld "top.lisp") using ACL2(r) (see *note
    REAL::) in community books directory nonstd/arithmetic/, where all of
    the .lisp files are soft links to files in arithmetic/.  Thus, the form
    (include-book "equalities") attempted to include arithmetic/equalities
    instead of nonstd/arithmetic/equalities, which caused an error.
    
    We no longer document the use of value :START for
    with-prover-step-limit.  This value has always been used by the ACL2
    implementation and may have semantics that change with new ACL2
    versions.  If you have reason to use this value, please contact the
    ACL2 implementors.
    
    *NEW FEATURES*
    
    By default, the prover now gives information about case splits.  See
    *note SPLITTER::.  Thanks to many ACL2 users, most recently David
    Rager, for requesting such a capability.  Also thanks to David Rager
    and Jared Davis for helpful discussions, and thanks to Robert Krug for
    feedback on the initial implementation and documentation that led us to
    make improvements.
    
    New utilities initialize-event-user and finalize-event-user allow the
    user to run state-modifying code at the start and end of events.
    Thanks to Harsh Raju Chamarthi for requesting these capabilities.  Note
    that finalize-event-user replaces print-summary-user.
    
    *HEURISTIC IMPROVEMENTS*
    
    Several heuristic improvements have been made to the tau system, even
    if you do not explicitly use the new capability for computing bounds on
    arithmetic expressions, mentioned above.  See *note TAU-SYSTEM::, or if
    you are unfamiliar with the tau system, see *note
    INTRODUCTION-TO-THE-TAU-SYSTEM::.
    
    *BUG FIXES*
    
    A soundness bug has been fixed that exploited the use of expansion files
    (see *note BOOK-COMPILED-FILE::) together with defstobj.  For an example
    illustrating this bug, see the comment about "Expansion/Defstobj Bug" in
    the form (deflabel note-6-1 ...) in ACL2 source file ld.lisp.
    
    We fixed a soundness bug involving system function canonical-pathname
    and (most likely) other functions in the former value of constant
    *unattachable-primitives*.  Thanks to Jared Davis and Sol Swords for
    bringing this bug to our attention by way of an example.  We include a
    very slight variant of that example in a comment within the form
    (deflabel note-6-1 ...) in ACL2 source file ld.lisp.
    
    There was a soundness bug that allowed attachments to prove nil in a
    consistent logical world involving defaxiom events.  This has been
    fixed, by requiring that no function symbol ancestral in a defaxiom
    formula is allowed to get an attachment.  See *note DEFATTACH::, in
    particular discussion of "a restriction based on a notion of a function
    symbol syntactically supporting an event", which concludes with a proof
    of nil that is no longer possible.
    
    (ACL2(h) only) We fixed a soundness bug in the interaction of
    memoization with congruent stobjs, in cases where the :congruent-to
    field of defstobj was not the canonical representative in the
    congruence class.  For an example illustrating this bug, see the
    comment about "memoize/congruent stobj bug" in the form (deflabel
    note-6-1 ...)  in ACL2 source file ld.lisp.
    
    Functions defined by defstobj had failed to be compiled when certifying
    books, except in host Lisps that compile on-the-fly (CCL, SBCL).  This
    has been fixed for all host Lisps.  A related change, probably less
    significant, was made for defabsstobj.  Thanks to Sol Swords for
    reporting bugs that turned out to be mistakes in a preliminary
    implementation of this change.
    
    Fixed an assertion error involving linear arithmetic.  Thanks to Sol
    Swords for sending an example illustrating the bug (now appearing as a
    comment in ACL2 source function linearize1).
    
    Fixed a bug that was breaking the ACL2s build mechanism (see *note
    ACL2-SEDAN::) by causing certain needless evaluation of "hidden defpkg"
    forms in certificate files when executing a call of include-book.  The
    bug could also affect rare error messages arising from ill-formed
    certificate files.  Thanks to Harsh Raju Chamarthi for bringing this bug
    to our attention by sending us an example script of the sort that was
    breaking during an ACL2s build.
    
    Fixed handling of pathnames by some low-level code (system function
    our-truename) that could cause errors, for example for host-Lisp GCL on
    some platforms when environment variable HOME points to a non-existent
    directory.  Thanks to Camm Maguire for bringing this issue to our
    attention and helping with the debugging.
    
    Fixed a coding bug in generation of stobj resizing functions for a stobj
    named OLD.  The following example illustrates the bug.
    
         (defstobj old
           (fld :type (array (unsigned-byte 31) (8))
                 :initially 0 :resizable t))
         (resize-fld 10 old)
         ; The following returned 8 but should have returned 10:
         (fld-length old)
    
    Fixed a bug in defabsstobj-missing-events (which macroexpanded
    incorrectly).  Thanks to Sol Swords for bringing this bug to our
    attention.
    
    Fixed two bugs in the handling of step-limits.  Thanks to Hanbing Liu
    for bringing the main such bug to our attention, which was that ACL2
    could report a step-limit violation during certify-book (in fact,
    during any compound event such as a call of encapsulate or progn), even
    without direct user involvement in managing step-limits (see *note
    SET-PROVER-STEP-LIMIT:: and see *note WITH-PROVER-STEP-LIMIT::).  The
    other bug was that a bad argument to set-prover-step-limit could result
    in a raw Lisp error, for example: (progn (set-prover-step-limit '(a
    b))).
    
    *CHANGES AT THE SYSTEM LEVEL*
    
    The books/ directory no longer needs to exist in order to build an ACL2
    executable.  Thanks to Robert Krug for pointing out that the
    installation instructions had suggested that this was already the case.
    
    Many changes have been made to the community books (see *note
    COMMUNITY-BOOKS::).  For example, some community books now include
    std/lists/rev.lisp, which contains the rule revappend-removal, which
    may cause some proofs involving revappend to fail where they formerly
    succeeded, or vice-versa.  When a proof fails that formerly succeeded,
    it may be useful for you to look over the runes printed in the event
    summary.
    
    *EMACS SUPPORT*
    
    *EXPERIMENTAL/ALTERNATE VERSIONS*
    
    For ACL2(p), wormhole-eval is now locked by default; thanks to David
    Rager for suggesting this change.  But there is a way to avoid the lock;
    see *note WORMHOLE-EVAL::.  In particular, the lock is avoided in the
    implementations of accumulated-persistence and
    forward-chaining-reports, which are not supported in ACL2(p) (see *note
    UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES::).
    
    
    File: acl2-doc-emacs.info,  Node: NOTE-6-2,  Next: NOTE-6-3,  Prev: NOTE-6-1,  Up: RELEASE-NOTES
    
    NOTE-6-2    ACL2 Version  6.2 (June, 2013) Notes
    
    NOTE!  New users can ignore these release notes, because the
    documentation has been updated to reflect all changes that are recorded
    here.
    
    Below we roughly organize the changes since Version 6.1 into the
    following categories of changes: existing features, new features,
    heuristic improvements, bug fixes, changes at the system level, Emacs
    support, and experimental versions.  Each change is described in just
    one category, though of course many changes could be placed in more
    than one category.
    
    *CHANGES TO EXISTING FEATURES*
    
    The macro top-level has been changed, so that evaluation of a form
    (top-level x) results in an error when evaluation of x results in an
    error.  Thanks to Jared Davis for observing that when evaluating a file
    using ld, an interrupt of a call of a top-level call in that file would
    not prevent evaluation of later forms in the file.
    
    The macro THE no longer causes an error when guard-checking is :NONE.
    For example, it had been the case that evaluation of (the integer t)
    always caused an error; but now, there is no error after executing
    command :set-guard-checking :NONE.  Thanks to Jared Davis for asking
    for a way to avoid such errors.
    
    The error printed when attempting to "reincarnate" a package -- that is,
    to define a package that is not part of the ACL2 logical world but
    exists in raw Lisp because it was once part of the world -- is now much
    more instructive.  In particular, it shows pathnames for the previous
    and proposed defpkg events, and it shows the symbols that are imported
    by one but not the other.  Thanks to Jared Davis for requesting this
    improvement.
    
    Functions open-input-channel and open-output-channel no longer cause an
    error when failing to open a channel because of a permissions problem,
    but instead return (mv nil state).  Thanks to Jared Davis for
    requesting this change.  (Note: this change does not apply if the host
    Lisp is non-ANSI, i.e., if the host Lisp is non-ANSI GCL.)
    
    The advanced meta-extract mechanisms, provided for using facts from the
    world or metafunction context, have been enhanced in the following
    ways, in collaboration with Sol Swords.  See *note META-EXTRACT:: for
    more details.
    
         It is now permissible to use calls of meta-extract-global-fact in
         hypotheses of clause-processor rules, much as they are used in
         hypotheses of meta rules.  See *note META-EXTRACT::.  Thanks to
         Sol Swords for requesting this feature.
    
         The utility meta-extract-global-fact is now a macro, which expands
         to a corresponding call of the new function,
         meta-extract-global-fact+.  This new function takes an alternate,
         extra state as an argument; it is not to be executed, and it
         operates on the alternate state, whose logical world is intended
         to be the same as that of the "live" (usual) state.
    
         A new sort of value for the obj argument is supported for
         meta-extract-global-fact (and meta-extract-global-fact+), which
         results in a term equating a function application to its result.
         See *note META-EXTRACT::, in particular the discussion of :fncall.
    
    
    It is now possible for trace$ to avoid printing prefixes of the form
    "n> " and ").  Now, forcing will take place as expected; see *note
         FORCE::.  Thanks to Robert Krug for bringing this issue to our
         attention and sending an example, which we include as a comment in
         the ACL2 source code (see (deflabel note-6-2 ...)).
    
         The heuristic is now delayed until after we check whether the
         hypothesis is already known, using type-set reasoning alone (in
         particular, not using rewriting), to be true or to be false.  We
         believe that this is now the "right" order for those two
         operations.  We saw a slight speed up in the regression tests
         (about a percent) with this change, but that might be in the noise.
    
         A technical change makes the heuristic slightly less aggressive in
         preventing backchaining.  Roughly speaking, ordering checks based
         on function symbol counts could suffice to permit backchaining,
         where now variable counts also suffice.  Thanks to Robert Krug for
         showing us an example where backchaining led to a term with no
         free variables that was nevertheless subject to the ancestors
         check, preventing it from being rewritten.
    
         (For those who use defattach to attach to ancestors-check)  We have
         used defrec to introduce an `ancestor' data structure.  A new
         function, strip-ancestor-literals, should be used to obtain the
         literals from a list of ancestors, although strip-cars will still
         work at this time.
    
    
    When we rewrite the current literal of the current clause we assume the
    falsity of the other literals and of the conclusions produced by forward
    chaining.  We have changed the order in which those assumptions are
    made, which affects the type-alist used during rewriting.  This has
    three effects: the new type-alist, which is sometimes stronger than the
    old one, may allow additional rules to fire, the choice of free vars
    may be different, and the order of the literals in forced subgoals may
    be different.  Should "legacy" proofs fail under the new type-alist, we
    recommend looking for rules that are fired in the new proof that were
    not fired (on that same subgoal) in the old one.  Thanks to Dave Greve
    for sending us an example that led us to make this change.
    
    *BUG FIXES*
    
    We fixed a soundness bug that could be exploited by calling system
    functions acl2-magic-mfc or acl2-magic-canonical-pathname.  Thanks to
    Sol Swords for bringing this bug to our attention.
    
    We fixed a soundness bug in the handling of stobjs, in which strings
    were recognized as stobjs in raw Lisp.  Thanks to Jared Davis for
    sending us a proof of nil that exploited this bug.  We now have a much
    simpler example of this bug, as follows.
    
         (defstobj st fld)
         (defthm bad (stp "abc") :rule-classes nil)
         (defthm contradiction
           nil
           :hints (("Goal" :in-theory (disable (stp)) :use bad))
           :rule-classes nil)
    
    We fixed bugs in extended metafunctions (see *note
    EXTENDED-METAFUNCTIONS::).  The macro mfc-ap no longer takes a :TTREEP
    keyword argument, because this argument could allow returning a tag
    tree that does not properly account for forcing.  The remaining mfc-xx
    macros -- mfc-relieve-hyp, mfc-rw+, mfc-rw, and mfc-ts -- still take a
    :TTREEP keyword argument, but the corresponding functions when :TTREEP
    is t -- mfc-relieve-hyp-ttree, mfc-rw+-ttree, mfc-rw-ttree, and
    mfc-ts-ttree -- were introduced with incorrect output signatures.  A
    complication is that mfc-relieve-hyp-ttree was improperly defined in raw
    Lisp in a way that actually matched the incorrect signature!  All of
    these bugs have been fixed.  Perhaps any of them could have made it
    possible to prove nil, though we have not tried to do so.
    
    (Windows only) On Windows, it had been possible for ACL2 not to
    consider two pathnames to name the same file when the only difference
    is the case of the drive, e.g., `C:' vs. `c:'.  This has been fixed.
    Thanks to Sol Swords for reporting this issue.
    
    Fixed a bug in the storing of rules for the tau system; see *note
    TAU-SYSTEM::.  (The error message mentions
    PARTITION-SIGNATURE-HYPS-INTO-TAU-ALIST-AND-OTHERS.)  Thanks to Sol
    Swords for reporting this bug and sending a simple example to
    illustrate it.
    
    It had been possible to admit the missing defthm events printed by
    defabsstobj, and yet get an error when subsequently submitting the same
    defabsstobj event, stating: "Note discrepancy with existing formula".
    The problem could occur when an expression of the form (or X Y) occurred
    in one of those missing events, because ACL2 created it from the term
    (if X 't Y) but then translated (or X Y) to (if X X Y), resulting in a
    mismatch.  This has been fixed.  Thanks to Jared Davis for reporting
    this bug using a simple example.
    
    A hard Lisp error was possible for certain illegal functional
    substitutions (see *note LEMMA-INSTANCE::).  Thanks to Sol Swords for
    reporting this bug.
    
    We fixed a bug in the case that an exported function of a defabsstobj
    event had a guard of t.  Thanks to Jared Davis for sending a simple
    example when reporting this bug.
    
    We now avoid an infinite loop that could occur when attempting to close
    the standard character output channel (see *note STANDARD-CO::).
    Instead, an error message explains how to accomplish what was probably
    intended.  Thanks to Shilpi Goel for bringing this issue to our
    attention.
    
    (Windows only) Fixed a bug that was causing a hard error on Windows
    when ACL2 encountered filenames starting with the tilde character (~),
    for example, (ld "~/acl2-customization.lsp").  Thanks to Sol Swords for
    bringing this bug to our attention.  Also thanks to Harsh Raju
    Chamarthi for a useful conversation that led to a better fix than our
    first one.
    
    *CHANGES AT THE SYSTEM LEVEL*
    
    ACL2 may now be built on recent versions of a new host Lisp, ANSI Gnu
    Common Lisp (GCL).  Traditional (non-ANSI) GCL was the original host
    Lisp underlying ACL2, and we are grateful for GCL support that we
    received from the late Bill Schelter and, more recently and
    particularly for ANSI GCL, from Camm Maguire.
    
    The `make' process suggested for book certification has changed
    substantially, thanks in large part to contributions from Jared Davis
    and Sol Swords.  We have seen the new process provide better
    performance on machines with many cores, and we expect maintenance
    advantages such as eliminating the need for Makefiles in individual
    book directories.  The "classic" process, which was based on community
    books file books/Makefile-generic, is still supported (see *note
    BOOKS-CERTIFICATION-CLASSIC::) but may disappear in a future release of
    ACL2.  See *note BOOKS-CERTIFICATION::.  Most changes should be
    invisible to the user, other than improved `make'-level parallelism,
    with the exception of the following.
    
         o Variable ACL2_JOBS is no longer supported, nor is it necessary;
         simply use `make' option `-j' instead.
    
         o Regressions now use `make' option -k by default, which causes the
         regression to keep going after errors, rather than -i, which
         ignores errors.  If you encounter problems because of this change,
         use ACL2_IGNORE=-i with your `make' command.
    
         o The `regression' target works for the experimental extension,
         ACL2(h) (see *note HONS-AND-MEMOIZATION::); target
         `regression-hons' no longer exists.
    
    
    Please let us know if you run into problems with the new
    infrastructure, as we consider the legacy infrastructure to be
    deprecated and we will probably eliminate much of it in the future.  In
    particular, circular dependencies were formerly prohibited at the
    directory level, but that is no longer the case, and we expect such
    cycles to occur in the future.
    
    Although ACL2 users don't typically modify raw Lisp variables, we have
    arranged to reset Lisp variable *default-pathname-defaults* if necessary
    at startup so that it will not interfere with ACL2, in particular by
    messing up the initial connected book directory (see *note CBD::).
    Thanks to Jared Davis, Sol Swords, and Raymond Toy for helping us to
    identify this issue.
    
    *EMACS SUPPORT*
    
    *EXPERIMENTAL/ALTERNATE VERSIONS*
    
    In ACL2(h), print-object$ no longer uses the serialize printer except
    in system applications as before (e.g., write out .cert files).  Thanks
    to Dave Greve for bringing this issue to our attention.
    
    Jared Davis contributed changes related to the memoize utility of
    ACL2(h), including some low-level changes as well as the following.
    
    o Never-memoize specifies that a given function should never be
    memoized.
    
    o Removed memoize-let, which may never have ever been used.
    
    o Removed the :inline keyword option to memoize, which was just an alias
     for the :recursive option.
    
    For ACL2(p), some anomalous behavior may no longer occur because prover
    calls (more specifically, trips through the ACL2 "waterfall") will
    return only after all sub-computations (threads) have finished.  Thanks
    to David Rager for contributing this improvement.
    
    ACL2(pr), which includes parallelism (as for ACL2(p)) and non-standard
    analysis support for the reals (as for ACL2(r)), now builds and can
    certify the community nonstd/ books.  Thanks to David Rager for his
    contribution to this capability.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE-6-3,  Next: NOTE1,  Prev: NOTE-6-2,  Up: RELEASE-NOTES
    
    NOTE-6-3    ACL2 Version  6.3 (October, 2013) Notes
    
    NOTE!  New users can ignore these release notes, because the
    documentation has been updated to reflect all changes that are recorded
    here.
    
    Below we roughly organize the changes since Version  6.2 into the
    following categories of changes: existing features, new features,
    heuristic improvements, bug fixes, changes at the system level, Emacs
    support, and experimental versions.  Each change is described in just
    one category, though of course many changes could be placed in more
    than one category.
    
    *CHANGES TO EXISTING FEATURES*
    
    The evaluation of a term from a bind-free hypothesis had been expected
    to produce an alist binding free variables to terms.  While that is
    still legal, it is also legal for that evaluation to produce a list of
    such alists: then each is considered, until one of them permits all
    remaining hypotheses to be relieved.  See *note BIND-FREE::.  Thanks to
    Sol Swords for requesting this enhancement.
    
    ACL2 continues to provide a way to specify keyword command
    abbreviations for the top-level loop; see *note LD-KEYWORD-ALIASES::.
    However, ld-keyword-aliases is now a table rather than a state global;
    it is thus no longer a so-called LD special.  The functionality of
    set-ld-keyword-aliases has essentially been preserved, except that it is
    now an event (see *note EVENTS::), hence it may appear in a book; it is
    local to a book (or encapsulate event); and the state argument is
    optional, and deprecated.  A non-local version (set-ld-keyword-aliases!)
    has been added, along with corresponding utilities add-keyword-alias and
    add-keyword-alias! for adding a single keyword alias.  See *note
    LD-KEYWORD-ALIASES::.  Thanks to Jared Davis for correspondence that
    led us to make this change.
    
    The proof-checker command (exit t) now exits without a query (but still
    prints an event to show the :INSTRUCTIONS).  Thanks to Warren Hunt for
    feedback leading us to make this change.
    
    We made the following minor changes to the behavior or dmr; see *note
    DMR::.  First, if dmr monitoring is enabled, then (dmr-start) will have
    no effect other than to print a corresponding observation, and if
    monitoring is disabled, then (dmr-stop) will have no effect other than
    to print a corresponding observation.  Second, it had been the case
    that when (dmr-start) is invoked, the debugger was always automatically
    enabled with value t (see *note SET-DEBUGGER-ENABLE::), and the
    debugger remained enabled when (dmr-stop) was invoked.  Now, the
    debugger is only enabled by (dmr-start) if it is not already enabled
    and does not have setting :never.  Moreover, if such automatic enabling
    takes place, then the old setting for the debugger is restored by
    (dmr-stop) unless set-debugger-enable has first been called after that
    automatic enabling.  Finally, if the value of state global variable
    'debugger-enable is :bt, then the new value will be :break-bt, not t.
    
    When a call of progn is executed in the ACL2 loop, its constituent
    events and their results are printed, just as was already done for calls
    of encapsulate.  Thanks to Jared Davis for a conversation causing us to
    consider this change.
    
    (CCL only) When set-debugger-enable is invoked with an argument that
    prints a backtrace and CCL is the host Lisp, the backtrace will be
    limited to 10,000 stack frames.  (We have seen more than 65,000 stack
    frames before this change.)  This limit is the value of raw Lisp
    variable *ccl-print-call-history-count*, which may be assigned another
    positive integer value to serve as the maximum number of stack frames
    to be printed.
    
    Improvements have been made pertaining to the disabling (inhibiting) of
    individual types of warning.  Now, inhibited warnings are implemented
    in a straightforward way using a separate table for this purpose, the
    inhibit-warnings-table, rather than using the acl2-defaults-table.  See
    *note SET-INHIBIT-WARNINGS::, and see *note SET-INHIBIT-WARNINGS!:: for
    a variant that is not local to an encapsulate or a book in which it
    occurs.  Thanks to Sol Swords for sending examples showing how
    set-inhibit-warnings did not always behave as one might reasonably
    expect when books are involved.
    
    It had been the case that lp took a single argument, 'raw.  This
    argument was not documented and also caused an error, so it has been
    eliminated.
    
    The functionality of make-event has been significantly expanded.
    First: if the expansion is of the form (:OR e1 e2 ...), then event forms
    e1, e2, and so on are evaluated, in order, until the evaluation of some
    ek completes without error.  In that case, the expansion is treated
    simply as ek.  With this capability, alternative expansions can be
    attempted and the successful one does not need to be evaluated again.
    See the new version of community book
    books/make-event/proof-by-arith.lisp for an example.  Second, an
    expansion may be of the form (:DO-PROOFS e), in which case the event e
    is evaluated with proofs *not* skipped; see *note LD-SKIP-PROOFSP::.
    Third, new keyword :EXPANSION? can be used to avoid storing expansions
    in certificate files.  See *note MAKE-EVENT::.
    
    When a defun event prints a failure message in the summary, that
    message now indicates when the failure is due to a failed proof of guard
    verification or a failed proof of the measure theorem.  Thanks to
    Shilpi Goel for requesting this enhancement.
    
    *NEW FEATURES*
    
    ACL2 can now be instructed to time activities using real time (wall
    clock time) instead of run time (typically, cpu time).  See *note
    GET-INTERNAL-TIME::.  Thanks to Jared Davis for asking to be able to
    obtain real-time reports in event summaries.
    
    A new utility, sys-call+, is similar to existing utility sys-call in
    that it executes a command.  Unlike sys-call, however, sys-call+
    returns values that include output from the command (in addition to the
    exit status), rather than simply printing the command.  See *note
    SYS-CALL+::.
    
    The new macro verify-guards+ extends the functionality of verify-guards
    by permitting macro-aliases (see *note MACRO-ALIASES-TABLE::).  See
    *note VERIFY-GUARDS+::.  Thanks to Jared Davis for requesting this
    feature and suggesting the use of make-event in its implementation.  We
    have also modified verify-guards to print a friendlier error message
    when its argument is a macro-alias.
    
    See *note LAST-PROVER-STEPS:: for a new utility that returns the number
    of prover steps most recently taken.
    
    *HEURISTIC IMPROVEMENTS*
    
    The processing of :use and :by hints has been changed in the following
    two rather subtle ways, thanks to suggestions from Sol Swords.
    
         o For :by hints, the simplest check was an equality check, rather
         than a more general subsumption check.  That equality check was
         made after removing so-called "guard holders" (must-be-equal,
         prog2$, ec-call, the) from both the previous theorem and the
         purported theorem.  Now, guard-holder removal has been
         strengthened, so that the results are also put into so-called
         quote-normal form, for example replacing (cons '3 '4) by '(3 . 4).
    
         o For a lemma-instance provided to a :use or :by hint that is a
         :functional-instance, if a :do-not hint (see *note HINTS::) has
         specified that preprocess-clause is not to be used, then
         preprocessing will not be used on the constraints.
    
    We eliminated certain warnings about being "weak" for every
    :type-prescription rule whose conclusion designates that the function
    call can be equal to one of its arguments, e.g., (or (integerp (foo y))
    (equal (foo y) y)).  In many cases (such as the one above), such
    warnings about "weak" simply aren't correct.
    
    *BUG FIXES*
    
    Fixed a soundness bug that was permitting a stobj to be bound by a let
    or mv-let form, without being among the outputs of that form.  Thanks
    to Jen Davis and Dave Greve for reporting this bug.  Their report
    included an example which forms the basis for a proof of nil, included
    as a comment in the form (deflabel note-6-3 ...) in ACL2 source file
    ld.lisp.
    
    (GCL only) Fixed an obscure soundness bug due to an error in the GCL
    implementation of set-debugger-enable.  For details, see the relevant
    comment in the ACL2 source code under (deflabel note-6-3 ...).
    
    Fixed a bug in the case of a field of a (concrete) stobj that is an
    abstract stobj (see *note NESTED-STOBJS::).  Thanks to David Rager for
    bringing this bug to our attention.
    
    Splitter output for type if-intro (see *note SPLITTER::) could formerly
    occur even when at most one subgoal is generated.  This has been fixed.
    
    Fixed a bug in wof, hence in psof (which uses wof), that was causing
    the printing of a bogus error message.
    
    A small logical bug has been fixed in the logical definition of
    sys-call-status.  Formerly it always returned (mv nil state) whenever
    the oracle of the state is non-empty (see *note STATE::).
    
    Fixed a bug that was causing an error upon evaluation of the form
    (set-prover-step-limit nil).  Thanks to David Russinoff for reporting
    this error.
    
    The :measure (if supplied) is now ignored when checking redundancy with
    respect to a non-recursive definition that is not defined within a
    mutual-recursion.  (See *note REDUNDANT-EVENTS:: and see *note
    XARGS::.)  It had been possible to get a low-level ACL2 error in this
    situation.  Thanks to Jared Davis for reporting this bug with a helpful
    example.
    
    Eliminated a potential error when using comp to compile an uncompiled
    function defined under progn!, which we observed in LispWorks.
    
    *CHANGES AT THE SYSTEM LEVEL*
    
    The ACL2 sources are now publicly available between ACL2 releases,
    using svn; see the new "acl2-devel" project hosted by Google code at
    `http://acl2-devel.googlecode.com'.  Although such a copy of ACL2 is
    likely to work well with the latest svn (trunk) revision of the ACL2
    community books (see *note COMMUNITY-BOOKS::), please take seriously
    the warning message printed at startup: "The authors of ACL2 consider
    svn distributions to be experimental; they may be incomplete, fragile,
    and unable to pass our own regression."  That message also provides
    instructions for bug reports.  If you decide to use svn versions of
    either the community books or ACL2, then you should use both, as they
    tend to be kept in sync.  We fully expect ACL2 releases to continue
    from time to time, as usual.  Thanks to Jared Davis for his efforts in
    setting up the new acl2-devel project and svn repository, and to him
    and David Rager for convincing us to distribute ACL2 sources via svn
    between releases.
    
    Thanks to a suggestion from Jared Davis, over 30 built-in functions are
    now declared to be inline in order to boost performance.  (The list may
    be found by searching ACL2 source file axioms.lisp for "(declaim
    (inline".)
    
    Better support has been provided for command line arguments, especially
    those supplied directly by the user when calling ACL2.  For one,
    problems with quoting have been solved using "$@" in place of $*.
    Also, the function save-exec now allows specification of arguments,
    both for the host Lisp as well as "inert" arguments that can be passed
    along to calls of programs (as with sys-call).  A keyword argument,
    :return-from-lp, specifies a form to evaluate before quitting the
    read-eval-print loop at startup.  See *note SAVE-EXEC::.  Also see the
    source function user-args-string and its comments, source file
    acl2-init.lisp, for more information.  Thanks to Jared Davis for
    suggesting the use of "$@", as well as modifications to save-exec and
    helpful conversations about that.
    
    A rather extensive overhaul has taken place for the function proclaiming
    mechanism.  As before, this is only used when the host Lisp is GCL.
    However, building an executable is now faster for some Lisps, including
    GCL, by avoiding repeated recompilation and perhaps repeated
    initialization.
    
    (CCL only) We increased stack sizes when the host Lisp is CCL.  The
    default for recent CCL versions is equivalent to specifying `-Z 2M' on
    the command line, but saved ACL2 scripts (including experimental
    versions ACL2(h), ACL2(p), ACL2(r), and combinations of them) to `-Z
    64M', representing a 32-fold increase.  Thanks to Jared Davis for
    pointing us to community books file books/centaur/ccl-config.lsp and to
    Sol Swords for helpful discussions.
    
    (SBCL only) Fixed save-exec for host Lisp SBCL to provide the same
    export of variable SBCL_HOME that was provided in the original
    saved_acl2 script.
    
    (GCL only) We made changes, following suggestions from Camm Maguire
    (whom we thank for these suggestions), to support ACL2 builds on recent
    versions of GCL (2.6.8 and 2.6.10; we recommend against using GCL
    2.6.9, since issues there were fixed in 2.6.10).  Specifically, we no
    longer set the hole size, and we allocate contiguous pages sufficient
    to run an ACL2 regression without failing due to memory limitations.
    
    *EMACS SUPPORT*
    
    Modified file emacs/emacs-acl2.el to eliminate some warnings that were
    appearing in a recent Emacs version, replacing (end-of-buffer) by
    (goto-char (point-max)) and next-line by forward-line.  Thanks to
    Warren Hunt for bringing the warnings to our attention.
    
    *EXPERIMENTAL/ALTERNATE VERSIONS*
    
    (Allegro CL only) ACL2(h) now avoids blow-ups in hash table sizes that
    could be caused by hons-shrink-alist.  Thanks to Jared Davis for
    helping to debug this problem, and to David Rager for contributing the
    community book books/parsers/earley/earley-parser.lisp, which
    highlighted this problem.
    
    (SBCL only) Fixed a bug that was causing a Lisp break after turning on
    waterfall-parallelism.  Thanks to David Rager for confirming that our
    proposed fix is correct.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE1,  Next: NOTE2,  Prev: NOTE-6-3,  Up: RELEASE-NOTES
    
    NOTE1    Acl2 Version 1.1 Notes
    
    The new features are extensively documented.  The relevant topics are:
    
    * Menu:
    
    
    Related topics other than immediate subtopics:
    * BOOKS:: files of ACL2 event forms
    
    * GUARD:: restricting the domain of a function
    
    * MORE:: your response to :doc or :more's ``(type :more...)''
    
    * REDUNDANT-EVENTS:: allowing a name to be introduced ``twice''
    
    It is especially important to read all of of the documentation for
    books before trying to use books.  However, the new :more keyword
    command is so handy for reading long documentation strings that we
    recommend you start with :doc more if reading at the terminal.  Some
    documentation has been written for guards which you might find
    interesting.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE2,  Next: NOTE3,  Prev: NOTE1,  Up: RELEASE-NOTES
    
    NOTE2    Acl2 Version 1.2 Notes
    
    Hacker mode has been eliminated and programming mode has been added.
    Programming mode is unsound but does syntax checking and permits
    redefinitions of names.  See :doc load-mode and :doc g-mode.
    
    The arguments to ld have changed.  Ld is now much more sophisticated.
    See *note LD::.
    
    For those occasions on which you wish to look at a large list structure
    that you are afraid to print, try (walkabout x state), where x is an
    Acl2 expression that evaluates to the structure in question.  I am
    afraid there is no documentation yet, but it is similar in spirit to
    the Interlisp structure editor.  You are standing on an object and
    commands move you around in it.  E.g., 1 moves you to its first
    element, 2 to its second, etc.; 0 moves you up to its parent; nx and bk
    move you to its next sibling and previous sibling; pp prettyprints it;
    q exits returning nil; = exits returning the thing you're standing on;
    (= symb) assigns the thing you're standing on to the state global
    variable symb.
    
    Several new hints have been implemented, including :by and :do-not.
    The old :do-not-generalize has been scrapped in favor of such new hints
    as :do-not (generalize elim).  :By lets you say "this goal is subsumed
    by" a given lemma instance.  The :by hint also lets you say "this goal
    can't be proved yet but skip it and see how the rest of the proof
    goes." See *note HINTS::.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE3,  Next: NOTE4,  Prev: NOTE2,  Up: RELEASE-NOTES
    
    NOTE3    Acl2 Version 1.3 Notes
    
    Programming mode has been eliminated.  Instead, all functions have a
    "color" which indicates what can be done with the function.  For
    example, :red functions can be executed but have no axioms describing
    them.  Thus, :red functions can be introduced after passing a simple
    syntactic check and they can be redefined without undoing.  But nothing
    of consequence can be proved about them.  At the other extreme are
    :gold functions which can be executed and which also have passed both
    the termination and the guard verification proofs.  The color of a
    function can be specified with the new xargs keyword, :color, which, if
    omitted defaults to the global setting of ld-color.  Ld-color replaces
    load-mode.  Setting ld-color to :red causes behavior similar to the old
    :g-mode.  Setting ld-color to :gold causes behavior similar to the old
    :v-mode.  It is possible to prototype your system in :red and then
    convert :red functions to :blue individually by calling
    verify-termination on them.  They can then be converted to :gold with
    verify-guards.  This allows us to undertake to verify the termination
    and guards of system functions.  See :doc color for an introduction to
    the use of colors.
    
    Type prescription rules have been added.  Recall that in Nqthm, some
    rewrite rules were actually stored as "type-prescriptions."  Such rules
    allow the user to inform Nqthm's primitive type mechanism as to the
    kinds of shells returned by a function.  Earlier versions of Acl2 did
    not have an analogous kind of rule because Acl2's type mechanism is
    complicated by guards.  Version 1.3 supports type-prescription rules.
    See *note TYPE-PRESCRIPTION::.
    
    Three more new rule-classes implement congruence-based rewriting.  It
    is possible to identify a binary relation as an equivalence relation
    (see *note EQUIVALENCE::), to show that one equivalence relation
    refines another (see *note REFINEMENT::) and to show that a given
    equivalence relation is maintained when rewriting a given function
    call, e.g., (fn ...xk...), by maintaining another equivalence relation
    while rewriting the kth argument (see *note CONGRUENCE::).  If r has
    been shown to be an equivalence relation and then (implies hyps (r (foo
    x) (bar x))) is proved as a :rewrite rule, then instances of (foo x)
    will be replaced by corresponding instances of (bar x) provided the
    instance occurs in a slot where the maintainence of r-equivalence is
    known to be sufficient and hyps can be established as usual.
    
    In Version 1.2, rule-classes were simple keywords, e.g., :rewrite or
    :elim.  In Version 1.3, rule-classes have been elaborated to allow you
    to specify how the theorem ought to be used as a rule.  That is, the
    new rule-classes allows you to separate the mathematical statement of
    the formula from its interpretation as a rule.  See *note
    RULE-CLASSES::.
    
    Rules used to be named by symbols, e.g., car and car-cons were the
    names of rules.  Unfortunately, this was ambiguous because there are
    three rules associated with function symbols: the symbolic definition,
    the executable counterpart, and the type-prescription; many different
    rules might be associated with theorems, depending on the rule classes.
    In Version 1.3 rules are named by "runes" (which is just short hand for
    "rule names").  Example runes are (:definition car),
    (:executable-counterpart car), and (:type-prescription car . 1).  Every
    rule added by an event has a different name and you can enable and
    disable them independently.  See *note RUNE:: and see *note THEORIES::.
    
    The identity function force, of one argument, has been added and given
    a special interpretation by the functions responsible for establishing
    hypotheses in backchaining: When the system fails to establish some
    hypothesis of the form (force term), it simply assumes it is true and
    goes on, delaying until later the establishment of term.  In
    particular, pushes a new subgoal to prove term in the current context.
    When that subgoal is attacked, all of the resources of the theorem
    prover, not just rewriting, are brought to bear.  Thus, for example, if
    you wish to prove the rule (implies (good-statep s) (equal (exec s n)
    s')) and it is your expectation that every time exec appears its first
    argument is a good-statep then you might write the rule as (implies
    (force (good-statep s)) (equal (exec s n) s')).  This rule is
    essentially an unconditional rewrite of (exec s n) to s' that spawns
    the new goal (good-statep s).  See *note FORCE::.  Because you can now
    specify independently how a theorem is used as a rule, you need not
    write the force in the actual theorem proved.  See *note RULE-CLASSES::.
    
    Version 1.3 supports a facility similar to Nqthm's break-lemma.  See
    *note BREAK-REWRITE::.  You can install "monitors" on runes that will
    cause interactive breaks under certain conditions.
    
    Acl2 also provides "wormholes" which allow you to write functions that
    cause interaction with the user but which do not require that you have
    access to state.  See *note WORMHOLE::.
    
    The rewriter now automatically backchains to stronger recognizers.
    There is no user hook to this feature but it may simplify some proofs
    with which older versions of Acl2 had trouble.  For example, if the
    rewriter is trying to prove (rationalp (foo a b c)) it is now smart
    enough to try lemmas that match with (integerp (foo a b c)).
    
    
    File: acl2-doc-emacs.info,  Node: NOTE4,  Next: NOTE5,  Prev: NOTE3,  Up: RELEASE-NOTES
    
    NOTE4    Acl2 Version 1.4 Notes
    
    Once again ld only takes one required argument, as the bind-flg has
    been deleted.
    
    Three commands have been added in the spirit of :pe.  :Pe! is similar
    to :pe but it prints all events with the given name, rather than just
    the most recent.  The command :pf prints the corollary formula
    corresponding to a name or rune.  The command :pl (print lemmas) prints
    rules whose top function symbol is the given name.  See *note PE!::,
    see *note PF::, and see *note PL::.
    
    Book naming conventions have been changed somewhat.  The once-required
    .lisp extension is now prohibited!  Directories are supported,
    including a notion of "connected book directory".  See *note
    BOOK-NAME::.  Also, the second argument of certify-book is now
    optional, defaulting to 0.
    
    Compilation is now supported inside the Acl2 loop.  See *note COMP::
    and see *note SET-COMPILE-FNS::.
    
    The default color is now part of the Acl2 world; see :doc
    default-color.  Ld-color is no longer an ld special.  Instead, colors
    are events; see the documentation for red, pink, blue, and gold.
    
    A table exists for controlling whether Acl2 prints comments when it
    forces hypotheses of rules; see :doc force-table.  Also, it is now
    possible to turn off the forcing of assumptions by disabling the
    definition of force; see *note FORCE::.
    
    The event defconstant is no longer supported, but a very similar event,
    defconst, has been provided in its place.  See *note DEFCONST::.
    
    The event for defining congruence relations is now defcong (formerly,
    defcon).
    
    Patterns are now allowed in :expand hints.  See the documentation for
    :expand inside the documentation for hints.
    
    We have improved the way we report rules used by the simplifier.  All
    runes of the same type are reported together in the running commentary
    associated with each goal, so that for example, executable counterparts
    are listed separately from definitions, and rewrite rules are listed
    separately from linear rules.  The preprocessor now mentions "simple"
    rules; see *note SIMPLE::.
    
    The mechanism for printing warning messages for new rewrite rules,
    related to subsumption, now avoids worrying about nonrecursive function
    symbols when those symbols are disabled.  These messages have also been
    eliminated for the case where the old rule is a :definition rule.
    
    Backquote has been modified so that it can usually provide predictable
    results when used on the left side of a rewrite rule.
    
    Time statistics are now printed even when an event fails.
    
    The Acl2 trace package has been modified so that it prints using the
    values of the Lisp globals *print-level* and *print-length*
    (respectively).
    
    Table has been modified so that the :clear option lets you replace the
    entire table with one that satisfies the val and key guards (if any);
    see *note TABLE::.
    
    We have relaxed the translation rules for :measure hints to defun, so
    that the the same rules apply to these terms that apply to terms in
    defthm events.  In particular, in :measure hints mv is treated just
    like list, and state receives no special handling.
    
    The loop-stopper test has been relaxed.  The old test required that
    every new argument be strictly less than the corresponding old argument
    in a certain term-order.  The new test uses a lexicographic order on
    term lists instead.  For example, consider the following rewrite rule.
    
           (equal
            (variable-update var1
                             val1 (variable-update var2 val2 vs))
            (variable-update var2
                             val2 (variable-update var1 val1 vs)))
    
    This rule is permutative.  Now imagine that we want to apply this rule
    to the term
    
           (variable-update u y (variable-update u x vs)).
    
    Since the actual corresponding to both var1 and var2 is u, which is not
    strictly less than itself in the term-order, this rule would fail to be
    applied in this situation when using the old test.  However, since the
    pair (u x) is lexicographically less than the pair (u y) with respect
    to our term-order, the rule is in fact applied using our new test.
    
    Messages about events now contain a space after certain left
    parentheses, in order to assist emacs users.  For example, the event
    
           (defthm abc (equal (+ (len x) 0) (len x)))
    
    leads to a summary containing the line
    
           Form:  ( DEFTHM ABC ...)
    
    and hence, if you search backwards for "(defthm abc", you won't stop at
    this message.
    
    More tautology checking is done during a proof; in fact, no goal
    printed to the screen, except for the results of applying :use and :by
    hints or the top-level goals from an induction proof, are known to Acl2
    to be tautologies.
    
    The ld-query-control-alist may now be used to suppress printing of
    queries; see *note LD-QUERY-CONTROL-ALIST::.
    
    Warning messages are printed with short summary strings, for example
    the string "Use" in the following message.
    
           Acl2 Warning [Use] in DEFTHM:  It is unusual to :USE an enabled
           :REWRITE or :DEFINITION rule, so you may want to consider
           disabling FOO.
    
    At the end of the event, just before the time is printed, all such
    summary strings are printed out.
    
    The keyword command :u has been introduced as an abbreviation for :ubt
    :max.  Printing of query messages is suppressed by :u.
    
    The keyword :cheat is no longer supported by any event form.
    
    Some irrelevant formals are detected; see *note IRRELEVANT-FORMALS::.
    
    A bug in the application of metafunctions was fixed: now if the output
    of a metafunction is equal to its input, the application of the
    metafunction is deemed unsuccessful and the next metafunction is tried.
    
    An example has been added to the documentation for equivalence to
    suggest how to make use of equivalence relations in rewriting.
    
    The following Common Lisp functions have been added to Acl2:
    alpha-char-p, upper-case-p, lower-case-p, char-upcase, char-downcase,
    string-downcase, string-upcase, and digit-charp-p.
    
    A documentation section called proof-checker has been added for the
    interactive facility, whose documentation has been slightly improved.
    See in particular the documentation for proof-checker, verify, and
    macro-command.
    
    A number of events that had been inadvertently disallowed in books are
    now permitted in books.  These are:  defcong, defcor, defequiv,
    defrefinement, defstub, and verify-termination.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE5,  Next: NOTE6,  Prev: NOTE4,  Up: RELEASE-NOTES
    
    NOTE5    Acl2 Version 1.5 Notes
    
    Acl2 now allows "complex rationals," which are complex numbers whose
    real parts are rationals and whose imaginary parts are non-zero
    rationals.  See *note COMPLEX::.
    
    A new way of handling forced hypotheses has been implemented.  Rather
    than cause a case split at the time the force occurs, we complete the
    main proof and then embark on one or more "forcing rounds" in which we
    try to prove the forced hypotheses.  See *note FORCING-ROUND::.  To
    allow us to compare the new handling of force with the old, Version 1.5
    implements both and uses a flag in state to determine which method
    should be used.  Do (assign old-style-forcing t) if you want force to
    be handled as it was in Version 1.4.  However, we expect to eliminate
    the old-style forcing eventually because we think the new style is more
    effective.  To see the difference between the two approaches to
    forcing, try proving the associativity of append under both settings of
    old-style-forcing.  To get the new behavior invoke:
    
         (thm (implies (and (true-listp a) (true-listp b))
                       (equal (append (append a b) c)
                              (append a (append b c)))))
    
    Then (assign old-style-forcing t) and invoke the thm command above
    again.
    
    A new :cases hints allows proof by cases.  See *note HINTS::.
    
    Include-book and encapsulate now restore the acl2-defaults-table when
    they complete.  See *note INCLUDE-BOOK:: and see *note ENCAPSULATE::.
    
    The guards on many Acl2 primitives defined in axioms.lisp have been
    weakened to permit them to be used in accordance with lisp custom and
    tradition.
    
    It is possible to attach heuristic filters to :rewrite rules to limit
    their applicability.  See *note SYNTAXP::.
    
    A tutorial has been added (but as of Version_3.6.1 it has become
    obsolete).
    
    Events now print the Summary paragraph listing runes used, time, etc.,
    whether they succeed or fail.  The format of the "failure banner" has
    been changed but still has multiple asterisks in it.  Thm also prints a
    Summary, whether it succeeds or fails; but thm is not an event.
    
    A new event form skip-proofs has been added; see *note SKIP-PROOFS::.
    
    A user-specific customization facility has been added in the form of a
    book that is automatically included, if it exists on the current
    directory.  See *note ACL2-CUSTOMIZATION::.
    
    A facility for conditional metalemmas has been implemented; see *note
    META::.
    
    The acceptable values for ld-skip-proofsp have changed.  In the old
    version (Version 1.4), a value of t meant that proofs and local events
    are to be skipped.  In Version 1.5, a value of t means proofs (but not
    local events) are to be skipped.  A value of 'include-book means proofs
    and local events are to be skipped.  There are two other, more obscure,
    acceptable values.  See *note LD-SKIP-PROOFSP::.
    
    In order to turn off the forcing of assumptions, one should now disable
    the :executable-counterpart of force (rather than the :definition of
    force, as in the previous release); see *note FORCE::.
    
    The macros enable-forcing and disable-forcing make it convenient to
    enable or disable forcing.  See *note ENABLE-FORCING:: and see *note
    DISABLE-FORCING::.
    
    The new commands :pr and :pr! print the rules created by an event or
    command.  See *note PR:: and see *note PR!::.
    
    The new history commands :puff and :puff* will replace a compound
    command such as an encapsulate or include-book by the sequence of
    events in it.  That is, they "puff up" or "lift" the subevents of a
    command to the command level, eliminating the formerly superior command
    and lengthening the history.  This is useful if you want to "partially
    undo" an encapsulate or book or other compound command so you can
    experiment.  See *note PUFF:: and see *note PUFF*::.
    
    Theory expressions now are allowed to use the free variable world and
    prohibited from using the free variable state.  See *note THEORIES::,
    although it is essentially the same as before except it mentions world
    instead of state.  See *note WORLD:: for a discussion of the Acl2
    logical world.  Allowing in-theory events to be state-sensitive
    violated an important invariant about how books behaved.
    
    Table keys and values now are allowed to use the free variable world
    and prohibited from using the free variable state.  See the note above
    about theory expressions for some explanation.
    
    The macro for minus, -, used to expand (- x 3) to (+ x -3) and now
    expands it to (+ -3 x) instead.  The old macro, if used in the
    left-hand sides of rewrite rules, produced inapplicable rules because
    the constant occurs in the second argument of the +, but potential
    target terms generally had the constant in the first argument position
    because of the effect of commutativity-of-+.
    
    A new class of rule, :linear-alias rules, allows one to implement the
    nqthm package and similar hacks in which a disabled function is to be
    known equivalent to an arithmetic function.
    
    A new class of rule, :built-in-clause rules, allows one to extend the
    set of clauses proved silently by defun during measure and guard
    processing.  See *note BUILT-IN-CLAUSE::.
    
    The new command pcb! is like pcb but sketches the command and then
    prints its subsidiary events in full.  See *note PCB!::.
    
    :Rewrite class rules may now specify the :loop-stopper field.  See
    *note RULE-CLASSES:: and see *note LOOP-STOPPER::.
    
    The rules for how loop-stoppers control permutative rewrite rules have
    been changed.  One effect of this change is that now when the built-in
    commutativity rules for + are used, the terms a and (- a) are permuted
    into adjacency.  For example, (+ a b (- a)) is now normalized by the
    commutativity rules to (+ a (- a) b); in Version 1.4, b was considered
    syntactically smaller than (- a) and so (+ a b (- a)) is considered to
    be in normal form.  Now it is possible to arrange for unary functions
    be be considered "invisible" when they are used in certain contexts.
    By default, unary- is considered invisible when its application appears
    in the argument list of binary-+.  See *note LOOP-STOPPER:: and see
    :DOC set-invisible-fns-table.
    
    Extensive documentation has been provided on the topic of Acl2's "term
    ordering."  See *note TERM-ORDER::.
    
    Calls of ld now default ld-error-action to :return rather than to the
    current setting.
    
    The command descriptor :x has been introduced and is synonymous with
    :max, the most recently executed command.  History commands such as
    :pbt print a :x beside the most recent command, simply to indicate that
    it *is* the most recent one.
    
    The command descriptor :x-23 is synonymous with (:x -23).  More
    generally, every symbol in the keyword package whose first character is
    #\x and whose remaining characters parse as a negative integer is
    appropriately understood.  This allows :pbt :x-10 where :pbt (:max -10)
    or :pbt (:here -10) were previously used.  The old forms are still
    legal.
    
    The order of the arguments to defcong has been changed.
    
    The simplifier now reports the use of unspecified built-in type
    information about the primitives with the phrase "primitive type
    reasoning."  This phrase may sometimes occur in situations where
    "propositional calculus" was formerly credited with the proof.
    
    The function pairlis has been replaced in the code by a new function
    pairlis$, because Common Lisp does not adequately specify its pairlis
    function.
    
    Some new Common Lisp functions have been added, including logtest,
    logcount, integer-length, make-list, remove-duplicates, string, and
    concatenate.  The source file /slocal/src/acl2/axioms.lisp is the
    ultimate reference regarding Common Lisp functions in Acl2.
    
    The functions defuns and theory-invariant have been documented.  See
    *note DEFUNS:: and see *note THEORY-INVARIANT::.
    
    A few symbols have been added to the list *acl2-exports*.
    
    A new key has been implemented for the acl2-defaults-table,
    :irrelevant-formals-ok.  See *note SET-IRRELEVANT-FORMALS-OK::.
    
    The connected book directory, cbd, must be nonempty and begin and end
    with a slash.  It is set (and displayed) automatically upon your first
    entry to lp.  You may change the setting with set-cbd.  See *note CBD::.
    
    :oops will undo the last :ubt.  See *note OOPS::.
    
    Documentation has been written about the ordinals.  See :DOC e0-ordinalp
    and see :DOC e0-ord-<.  [Note added later: Starting with Version_2.8,
    instead see *note O-P:: and see *note O<::.
    
    The color events -- (red), (pink), (blue), and (gold) -- may no longer
    be enclosed inside calls of local, for soundness reasons.  In fact,
    neither may any event that sets the acl2-defaults-table.  See *note
    EMBEDDED-EVENT-FORM::.
    
    See *note LD-KEYWORD-ALIASES:: for an example of how to change the exit
    keyword from :q to something else.
    
    The attempt to install a monitor on :rewrite rules stored as simple
    abbreviations now causes an error because the application of
    abbreviations is not tracked.
    
    A new message is sometimes printed by the theorem prover, indicating
    that a given simplification is "specious" because the subgoals it
    produces include the input goal.  In Version 1.4 this was detected but
    not reported, causing behavior some users found bizarre.  See *note
    SPECIOUS-SIMPLIFICATION::.
    
    :Definition rules are no longer always required to specify the :clique
    and :controller-alist fields; those fields can be defaulted to
    system-determined values in many common instances.  See *note
    DEFINITION::.
    
    A warning is printed if a macro form with keyword arguments is given
    duplicate keyword values.  Execute (thm t :doc nil :doc "ignored") and
    read the warning printed.
    
    A new restriction has been placed on encapsulate.  Non-local recursive
    definitions inside the encapsulate may not use, in their tests and
    recursive calls, the constrained functions introduced by the
    encapsulate.  See *note SUBVERSIVE-RECURSIONS::.  (Note added in
    Version  2.3:  Subversive recursions were first recognized by us here
    in Version 1.5, but our code for recognizing them was faulty and the
    bug was not fixed until Version  2.3.)
    
    The events defequiv, defcong, defrefinement, and defevaluator have been
    reimplemented so that they are just macros that expand into appropriate
    defthm or encapsulate events; they are no longer primitive events.  See
    the documentation of each affected event.
    
    The defcor event, which was a shorthand for a defthm that established a
    corollary of a named, previously proved event, has been eliminated
    because its implementation relied on a technique we have decided to ban
    from our code.  If you want the effect of a defcor in Version 1.5 you
    must submit the corresponding defthm with a :by hint naming the
    previously proved event.
    
    Error reporting has been improved for inappropriate in-theory hints and
    events, and for syntax errors in rule classes, and for non-existent
    filename arguments to ld.
    
    Technical Note:  We now maintain the Third Invariant on type-alists, as
    described in the Essay on the Invariants on Type-alists, and
    Canonicality.  This change will affect some proofs, for example, by
    causing a to rewrite more quickly to c when (equiv a b) and (equiv b c)
    are both known and c is the canonical representative of the three.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE6,  Next: NOTE7,  Prev: NOTE5,  Up: RELEASE-NOTES
    
    NOTE6    Acl2 Version 1.6 Notes
    
    A new key has been implemented for the acl2-defaults-table, :ignore-ok.
    See *note SET-IGNORE-OK::.
    
    It is now legal to have color events, such as (red), in the portcullis
    of a book.  More generally, it is legal to set the acl2-defaults-table
    in the portcullis of a book.  For example, if you execute :red and then
    certify a book, the event (red) will show up in the portcullis of that
    book, and hence the definitions in that book will all be red (except
    when overridden by appropriate declarations or events).  When that book
    is included, then as always, its portcullis must first be "raised," and
    that will cause the default color to become red before the events in
    the book are executed.  As always, the value of acl2-defaults-table
    immediately after execution of an include-book, certify-book, or
    encapsulate form will be the same as it was immediately before
    execution (and hence, so will the default color).  See *note
    PORTCULLIS:: and, for more about books, see *note BOOKS::.
    
    A theory ground-zero has been defined to contain exactly those rules
    that are enabled when Acl2 starts up.  See *note GROUND-ZERO::.
    
    The function nth is now enabled, correcting an oversight from Version
    1.5.
    
    Customization files no longer need to meet the syntactic restrictions
    put on books; rather, they can contain arbitrary Acl2 forms.  See *note
    ACL2-CUSTOMIZATION::.
    
    Structured directory names and structured file names are supported; see
    especially the documentation for pathname, book-name, and cbd.
    
    Acl2 now works with some Common Lisp implementations other than akcl,
    including Lucid, Allegro, and MCL.
    
    A facility has been added for displaying proof trees, especially using
    emacs; see *note PROOF-TREE::.
    
    There is a considerable amount of new documentation, in particular for
    the printing functions fmt, fmt1, and fms, and for the notion of Acl2
    term (see *note TERM::).
    
    It is possible to introduce new well-founded relations, to specify
    which relation should be used by defun, and to set a default relation.
    See *note WELL-FOUNDED-RELATION::.
    
    It is possible to make functions suggest new inductions.  See *note
    INDUCTION::.
    
    It is possible to change how Acl2 expresses type-set information; in
    particular, this affects what clauses are proved when forced
    assumptions are generated.  See *note TYPE-SET-INVERTER::.
    
    A new restriction has been added to defpkg, having to do with undoing.
    If you undo a defpkg and define the same package name again, the
    imports list must be identical to the previous imports or else an
    explanatory error will occur.  See *note
    PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS::.
    
    Theory-invariant and set-irrelevant-formals-ok are now embedded event
    forms.
    
    The command :good-bye may now be used to quit entirely out of Lisp,
    thus losing your work forever.  This command works in akcl but may not
    work in every Common Lisp.
    
    A theory ground-zero has been added that contains exactly the enabled
    rules in the startup theory.  See *note GROUND-ZERO::.
    
    Define-pc-macro and define-pc-atomic-macro now automatically define
    :red functions.  (It used to be necessary, in general, to change color
    to :red before invoking these.)
    
    For a proof of the well-foundedness of e0-ord-< on the e0-ordinalps,
    see *note PROOF-OF-WELL-FOUNDEDNESS::.  [Note added later: Starting with
    Version_2.8, o< and o-p replace e0-ord-< and e0-ordinalp, respectively.]
    
    Free variables are now handled properly for hypotheses of
    :type-prescription rules.
    
    When the system is loaded or saved, state is now bound to
    *the-live-state*.
    
    Certify-book has been modified so that when it compiles a file, it
    loads that object file.
    
    Defstub has been modified so that it works when the color is hot (:red
    or :pink).
    
    Several basic, but not particularly commonly used, events have been
    added or changed.  The obscure axiom symbol-name-intern has been
    modified.  The definition of firstn has been changed.  Butlast is now
    defined.  The definition of integer-length has been modified.  The
    left-hand side of the rewrite rule rational-implies2 has been changed
    from (* (numerator x) (/ (denominator x))) to (* (/ (denominator x))
    (numerator x)), in order to respect the fact that unary-/ is invisible
    with respect to binary-*.  See *note LOOP-STOPPER::.
    
    The `preprocess' process in the waterfall (see *note HINTS:: for a
    discussion of the :do-not hint) has been changed so that it works to
    avoid case-splitting.  The `simplify' process refuses to force (see
    *note FORCE::) when there are if terms, including and and or terms, in
    the goal being simplified.
    
    The function apply is no longer introduced automatically by translation
    of user input to internal form when functions are called on
    inappropriate explicit values, e.g., (car 3).
    
    The choice of which variable to use as the measured variable in a
    recursive definition has been very slightly changed.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE7,  Next: NOTE8,  Prev: NOTE6,  Up: RELEASE-NOTES
    
    NOTE7    ACL2 Version 1.7 (released October 1994) Notes
    
    Include-book now takes (optionally) an additional keyword argument,
    indicating whether a compiled file is to be loaded.  The default
    behavior is unchanged, except that a warning is printed when a compiled
    file is not loaded.  See *note INCLUDE-BOOK::.
    
    A markup language for documentation strings has been implemented, and
    many of the source files have been marked up using this language
    (thanks largely to the efforts of Laura Lawless).  See *note MARKUP::.
    Moreover, there are translators that we have used to provide versions
    of the ACL2 documentation in info (for use in emacs), html (for
    Mosaic), and tex (for hardcopy) formats.
    
    A new event defdoc has been implemented.  It is like deflabel, but
    allows redefinition of doc strings and has other advantages.  See *note
    DEFDOC::.
    
    We used to ignore corollaries when collecting up the axioms introduced
    about constrained functions.  That bug has been fixed.  We thank John
    Cowles for bringing this bug to our attention.
    
    The macro defstub now allows a :doc keyword argument, so that
    documentation may be attached to the name being introduced.
    
    A new command nqthm-to-acl2 has been added to help Nqthm users to make
    the transition to ACL2.  See *note NQTHM-TO-ACL2::, which also includes
    a complete listing of the relevant tables.
    
    Many function names, especially of the form "foo-lst", have been
    changed in order to support the following convention, for any "foo":
    
         (foo-listp lst) represents the notion (for x in lst always foop x).
    
    A complete list of these changes may be found at the end of this note.
    All of them except symbolp-listp and list-of-symbolp-listp have the
    string "-lst" in their names.  Note also that keyword-listp has been
    renamed keyword-value-listp.
    
    Accumulated persistence has been implemented.  It is not connected to
    :brr or rule monitoring.  See *note ACCUMULATED-PERSISTENCE::.
    
    :Trigger-terms has been added for :linear rule classes, so you can hang
    a linear rule under any addend you want.  See *note LINEAR::, which has
    been improved and expanded.
    
    ACL2 now accepts 256 characters and includes the Common Lisp functions
    code-char and char-code.  However, ACL2 controls the lisp reader so
    that #\c may only be used when c is a single standard character or one
    of Newline, Space, Page, Rubout, Tab.  If you want to enter other
    characters use code-char, e.g., (coerce (list (code-char 7) (code-char
    240) #a) 'string).  See *note CHARACTERS::.  Note:  our current
    handling of characters makes the set of theorems different under
    Macintosh Common Lisp (MCL) than under other Common Lisps.  We hope to
    rectify this situation before the final release of ACL2.
    
    A new table, macro-aliases-table, has been implemented, that associates
    macro names with function names.  So for example, since append is
    associated with binary-append, the form (disable append) it is
    interpreted as though it were (disable binary-append).  See *note
    MACRO-ALIASES-TABLE::, see *note ADD-MACRO-ALIAS:: and see *note
    REMOVE-MACRO-ALIAS::.
    
    The implementation of conditional metalemmas has been modified so that
    the metafunction is applied before the hypothesis metafunction is
    applied.  See *note META::.
    
    The Common Lisp functions acons and endp have been defined in the ACL2
    logic.
    
    We have added the symbol declare to the list *acl2-exports*, and hence
    to the package "ACL2-USER".
    
    A new hint, :restrict, has been implemented.  See *note HINTS::.
    
    It used to be that if :ubt were given a number that is greater than the
    largest current command number, it treated that number the same as
    :max.  Now, an error is caused.
    
    The table :force-table has been eliminated.
    
    A command :disabledp (and macro disabledp) has been added; see *note
    DISABLEDP::.
    
    Compilation via :set-compile-fns is now suppressed during include-book.
    In fact, whenever the state global variable ld-skip-proofsp has value
    'include-book.
    
    Here are some less important changes, additions, and so on.
    
    Unlike previous releases, we have not proved all the theorems in
    axioms.lisp; instead we have simply assumed them.  We have deferred
    such proofs because we anticipate a fairly major changed in Version 1.8
    in how we deal with guards.
    
    We used to (accidentally) prohibit the "redefinition" of a table as a
    function.  That is no longer the case.
    
    The check for whether a corollary follows tautologically has been sped
    up, at the cost of making the check less "smart" in the following
    sense:  no longer do we expand primitive functions such as implies
    before checking this propositional implication.
    
    The command ubt! has been modified so that it never causes or reports
    an error.  See *note UBT!::.
    
    ACL2 now works in Harlequin LispWorks.
    
    The user can now specify the :trigger-terms for :linear rules.  See
    *note LINEAR::.
    
    The name of the system is now "ACL2"; no longer is it "Acl2".
    
    The raw lisp counterpart of theory-invariant is now defined to be a
    no-op as is consistent with the idea that it is just a call of table.
    
    A bug was fixed that caused proof-checker instructions to be executed
    when ld-skip-proofsp was t.
    
    The function rassoc has been added, along with a corresponding function
    used in its guard, r-eqlable-alistp.
    
    The in-theory event and hint now print a warning not only when certain
    "primitive" :definition rules are disabled, but also when certain
    "primitive" :executable-counterpart rules are disabled.
    
    The modified version of trace provided by ACL2, for use in raw Lisp,
    has been modified so that the lisp special variable *trace-alist* is
    consulted.  This alist associates, using eq, values with their print
    representations.  For example, initially *trace-alist* is a one-element
    list containing the pair (cons state '|*the-live-state*|).
    
    The system now prints an observation when a form is skipped because the
    default color is :red or :pink.  (Technically:  when-cool has been
    modified.)
    
    Additional protection exists when you submit a form to raw Common Lisp
    that should only be submitted inside the ACL2 read-eval-print loop.
    
    Here is a complete list of the changes in function names described near
    the top of this note, roughly of the form
    
         foo-lst --> foo-listp
    
    meaning:  the name "foo-lst" has been changed to "foo-listp."
    
         symbolp-listp    --> symbol-listp
         list-of-symbolp-listp  --> symbol-list-listp
                                {for consistency with change to symbol-listp}
         rational-lst     --> rational-listp
                              {which in fact was already defined as well}
         integer-lst      --> integer-listp
         character-lst    --> character-listp
         stringp-lst      --> string-listp
         32-bit-integer-lst   --> 32-bit-integer-listp
         typed-io-lst     --> typed-io-listp
         open-channel-lst --> open-channel-listp
         readable-files-lst   --> readable-files-listp
         written-file-lst --> written-file-listp
         read-file-lst    --> read-file-listp
         writeable-file-lst   --> writable-file-listp
                              {note change in spelling of ``writable''}
         writeable-file-lst1  --> writable-file-listp1
         pseudo-termp-lst     --> pseudo-term-listp
         hot-termp-lst --> hot-term-listp {by analogy with pseudo-term-listp}
         weak-termp-lst   --> weak-term-listp
         weak-termp-lst-lst   --> weak-termp-list-listp
         ts-builder-case-lstp -> ts-builder-case-listp
         quotep-lst       --> quote-listp
         termp-lst        --> term-listp
         instr-lst        --> instr-listp
         spliced-instr-lst    --> spliced-instr-listp
         rewrite-fncallp-lst  --> rewrite-fncallp-listp
         every-occurrence-equiv-hittablep1-lst -->
                     every-occurrence-equiv-hittablep1-listp
         some-occurrence-equiv-hittablep1-lst  -->
                     some-occurrence-equiv-hittablep1-listp
                     {by analogy with the preceding, even though it's a
                      ``some'' instead of ``all'' predicate]
         almost-quotep1-lst   --> almost-quotep1-listp
         ffnnames-subsetp-lst --> ffnnames-subsetp-listp
         boolean-lstp     --> boolean-listp
         subst-expr1-lst-okp  --> subst-expr1-ok-listp
    
    acl2-sources/doc/EMACS/acl2-doc-emacs.info-130000664002132200015000000104575712222333542017741 0ustar  kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from
    acl2-doc-emacs.texinfo.
    
    This is documentation for ACL2 Version 6.3
    Copyright (C) 2013  University of Texas at Austin
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of Version 2 of the GNU General Public License as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Written by:  Matt Kaufmann and J Strother Moore
    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.
    
    INFO-DIR-SECTION Math
    START-INFO-DIR-ENTRY
    * acl2: (acl2-doc-emacs). Applicative Common Lisp
    END-INFO-DIR-ENTRY
    
    
    File: acl2-doc-emacs.info,  Node: NOTE8,  Next: NOTE8-UPDATE,  Prev: NOTE7,  Up: RELEASE-NOTES
    
    NOTE8    ACL2 Version 1.8 (May, 1995) Notes
    
    See *note NOTE8-UPDATE:: for yet more recent changes.
    
    Guards have been eliminated from the ACL2 logic.  A summary is
    contained in this brief note.  Also see *note DEFUN-MODE:: and see
    *note SET-GUARD-CHECKING::.
    
    Guards may be included in defuns as usual but are ignored from the
    perspective of admission to the logic: functions must terminate on all
    arguments.
    
    As in Nqthm, primitive functions, e.g., + and car, logically default
    unexpected arguments to convenient values.  Thus, (+ 'abc 3) is 3 and
    (car 'abc) is nil.  See *note PROGRAMMING::, and see the documentation
    for the individual primitive functions.
    
    In contrast to earlier versions of ACL2, Version 1.8 logical functions
    are executed at Nqthm speeds even when guards have not been verified.
    In versions before 1.8, such functions were interpreted by ACL2.
    
    Colors have been eliminated.  Two "defun-modes" are supported, :program
    and :logic.  Roughly speaking, :program does what :red used to do,
    namely, allow you to prototype functions for execution without any
    proof burdens.  :Logic mode does what :blue used to do, namely, allow
    you to add a new definitional axiom to the logic.  A global
    default-defun-mode is comparable to the old default color.  The system
    comes up in :logic mode.  To change the global defun-mode, type
    :program or :logic at the top-level.  To specify the defun-mode of a
    defun locally use
    
         (declare (xargs :mode mode)).
    
    The prompt has changed.  The initial prompt, indicating :logic mode, is
    
         ACL2 !>
    
    If you change to :program mode the prompt becomes
    
         ACL2 p!>
    
    Guards can be seen as having either of two roles: (a) they are a
    specification device allowing you to characterize the kinds of inputs a
    function "should" have, or (b) they are an efficiency device allowing
    logically defined functions to be executed directly in Common Lisp.  If
    a guard is specified, as with xargs :guard, then it is "verified" at
    defun-time (unless you also specify xargs :verify-guards nil).  Guard
    verification means what it always has: the input guard is shown to
    imply the guards on all subroutines in the body.  If the guards of a
    function are verified, then a call of the function on inputs satisfying
    the guard can be computed directly by Common Lisp.  Thus, verifying the
    guards on your functions will allow them to execute more efficiently.
    But it does not affect their logical behavior and since you will
    automatically get Nqthm speeds on unverified logical definitions, most
    users will probably use guards either as a specification device or only
    use them when execution efficiency is extremely important.
    
    Given the presence of guards in the system, two issues are unavoidable.
    Are guards verified as part of the defun process?  And are guards
    checked when terms are evaluated?  We answer both of those questions
    below.
    
    Roughly speaking, in its initial state the system will try to verify
    the guards of a defun if a :guard is supplied in the xargs and will not
    try otherwise.  However, guard verification in defun can be inhibited
    "locally" by supplying the xargs :verify-guards nil.  "Global"
    inhibition can be obtained via the :set-verify-guards-eagerness.  If
    you do not use the :guard xargs, you will not need to think about guard
    verification.
    
    We now turn to the evaluation of expressions.  Even if your functions
    contain no guards, the primitive functions do and hence you have the
    choice: when you submit an expression for evaluation do you mean for
    guards to be checked at runtime or not?  Put another way, do you mean
    for the expression to be evaluated in Common Lisp (if possible) or in
    the logic?  Note: If Common Lisp delivers an answer, it will be the
    same as in the logic, but it might be erroneous to execute the form in
    Common Lisp.  For example, should (car 'abc) cause a guard violation
    error or return nil?
    
    The top-level ACL2 loop has a variable which controls which sense of
    execution is provided.  To turn "guard checking on," by which we mean
    that guards are checked at runtime, execute the top-level form
    :set-guard-checking t.  To turn it off, do :set-guard-checking nil.
    The status of this variable is reflected in the prompt.
    
         ACL2 !>
    
    means guard checking is on and
    
         ACL2 >
    
    means guard checking is off.  The exclamation mark can be thought of as
    "barring" certain computations.  The absence of the mark suggests the
    absence of error messages or unbarred access to the logical axioms.
    Thus, for example
    
         ACL2 !>(car 'abc)
    
    will signal an error, while
    
         ACL2 >(car 'abc)
    
    will return nil.
    
    Note that whether or not guards are checked at runtime is independent
    of whether you are operating in :program mode or :logic mode and
    whether theorems are being proved or not.  (Although it must be added
    that functions defined in :program mode cannot help but check their
    guards because no logical definition exists.)
    
    Version 1.8 permits the verification of the guards of theorems, thus
    insuring that all instances of the theorem will evaluate without error
    in Common Lisp.  To verify the guards of a theorem named name execute
    the event
    
         (verify-guards name).
    
    If a theorem's guards have been verified, the theorem is guaranteed to
    evaluate without error to non-nil in Common Lisp (provided resource
    errors do not arise).
    
    Caveat about verify-guards: implies is a function symbol, so in the
    term (implies p q), p cannot be assumed true when q is evaluated; they
    are both evaluated "outside."  Hence, you cannot generally verify the
    guards on a theorem if implies is used to state the hypotheses.  Use if
    instead.  In a future version of ACL2, implies will likely be a macro.
    
    See sum-list-example.lisp for a nice example of the use of Version 1.8.
    This is roughly the same as the documentation for guard-example.
    
    We have removed the capability to do "old-style-forcing" as existed
    before Version 1.5.  See *note NOTE5::.
    
    NOTE:  Some low level details have, of course, changed.  One such
    change is that there are no longer two distinct type prescriptions
    stored when a function is admitted with its guards verified.  So for
    example, the type prescription rune for binary-append is now
    
         (:type-prescription binary-append)
    
    while in Versions 1.7 and earlier, there were two such runes:
    
         (:type-prescription binary-append . 1)
         (:type-prescription binary-append . 2)
    
    Nqthm-style forcing on linear arithmetic assumptions is no longer
    executed when forcing is disabled.
    
    Functional instantiation now benefits from a trick also used in Nqthm:
    once a constraint generated by a :functional-instance lemma instance
    (see *note LEMMA-INSTANCE::) has been proved on behalf of a successful
    event, it will not have to be re-proved on behalf of a later event.
    
    1+ and 1- are now macros in the logic, not functions.  Hence, for
    example, it is "safe" to use them on left-hand sides of rewrite rules,
    without invoking the common warning about the presence of nonrecursive
    function symbols.
    
    A new documentation section file-reading-example illustrates how to
    process forms in a file.
    
    A new proof-checker command forwardchain has been added; see *note
    ACL2-PC||FORWARDCHAIN::.
    
    It is now possible to use quantifiers.  See *note DEFUN-SK:: and see
    *note DEFCHOOSE::.
    
    There is a new event set-inhibit-warnings, which allows the user to
    turn off warnings of various types.  see *note SET-INHIBIT-WARNINGS::.
    
    An unsoundness relating encapsulate and :functional-instance hints has
    been remedied, with a few small effects visible at the user level.  The
    main observable effect is that defaxiom and non-local include-book
    events are no longer allowed in the scope of any encapsulate event that
    has a non-empty signature.
    
    When certify-book is called, we now require that the default defun-mode
    (see *note DEFAULT-DEFUN-MODE::) be :logic.  On a related note, the
    default defun-mode is irrelevant to include-book; the mode is always
    set to :logic initially, though it may be changed within the book and
    reverts to its original value at the conclusion of the include-book.  A
    bug in include-book prevented it from acting this way even though the
    documentation said otherwise.
    
    The documentation has been substantially improved.  A new section
    "Programming" contains documentation of many useful functions provided
    by ACL2; see *note PROGRAMMING::.  Also, the documentation has been
    "marked up" extensively.  Thus in particular, users of Mosaic will find
    many links in the documentation.
    
    The symbols force, mv-nth, and acl2-count have been added to the list
    *acl2-exports*.
    
    We now permit most names from the main Lisp package to be used as
    names, except for names that define functions, macros, or constants.
    See *note NAME::.
    
    We have changed the list of imports from the Common Lisp package to
    ACL2, i.e., the list *common-lisp-symbols-from-main-lisp-package*, to
    be exactly those external symbols of the Common Lisp package as
    specified by the draft Common Lisp standard.  In order to accommodate
    this change, we have renamed some ACL2 functions as shown below, but
    these and other ramifications of this change should be transparent to
    most ACL2 users.
    
         warning      --> warning$
         print-object --> print-object$
    
    Proof trees are no longer enabled by default.  To start them up,
    :start-proof-tree.
    
    We have added the capability of building smaller images.  The easiest
    way to do this on a Unix (trademark of AT&T) system is: make small.
    
    Here we will put some less important changes, additions, and so on.
    
    We have added definitions for the Common Lisp function position (for
    the test eql), as well as corresponding versions position-equal and
    position-eq that use tests equal and eq, respectively.  See *note
    POSITION::, see *note POSITION-EQUAL::, and see *note POSITION-EQ::.
    
    The defthm event rational-listp-implies-rationalp-car no longer exists.
    
    We fixed a bug in the hint mechanism that applied :by, :cases, and :use
    hints to the first induction goal when the prover reverted to proving
    the original goal by induction.
    
    We fixed a bug in the handling of (set-irrelevant-formals-ok :warn).
    
    In support of removing the old-style forcing capability, we deleted the
    initialization of state global old-style-forcing and deleted the
    definitions of recover-assumptions, recover-assumptions-from-goal,
    remove-assumptions1, remove-assumptions, and split-on-assumptions, and
    we renamed split-on-assumptions1 to split-on-assumptions.
    
    The special value 'none in the proof-checker commands claim and = has
    been replaced by :none.
    
    A bug in the handling of hints by subgoals has been fixed.  For
    example, formerly a :do-not hint could be "erased" by a :use hint on a
    subgoal.  Thanks go to Art Flatau for noticing the bug.
    
    The functions weak-termp and weak-term-listp have been deleted, and
    their calls have been replaced by corresponding calls of pseudo-termp
    and pseudo-term-listp.  The notion of pseudo-termp has been slightly
    strenthened by requiring that terms of the form (quote ...) have length
    2.
    
    Performance has been improved in various ways.  At the prover level,
    backchaining through the recognizer alist has been eliminated in order
    to significantly speed up ACL2's rewriter.  Among the other prover
    changes (of which there are several, all technical):  we no longer
    clausify the input term when a proof is interrupted in favor of
    inducting on the input term.  At the IO level, we have improved
    performance somewhat by suitable declarations and proclamations.  These
    include technical modifications to the macros mv and mv-let, and
    introduction of a macro the-mv analogous to the macro the but for forms
    returning multiple values.
    
    The function spaces now takes an extra argument, the current column.
    
    A bug in the proof-checker equiv command was fixed.
    
    The function intersectp has been deleted, because it was essentially
    duplicated by the function intersectp-equal.
    
    We now proclaim functions in AKCL and GCL before compiling books.  This
    should result in somewhat increased speed.
    
    The function repeat has been eliminated; use make-list instead.
    
    The proof-checker command expand has been fixed so that it eliminates
    let (lambda) expressions when one would expect it to.
    
    A new primitive function, mv-nth, has been introduced.  Mv-nth is
    equivalent to nth and is used in place of nth in the translation of
    mv-let expressions.  This allows the user to control the simplification
    of mv-let expressions without affecting how nth is treated.  In that
    spirit, the rewriter has been modified so that certain mv-nth
    expressions, namely those produced in the translation of (mv-let (a b
    c)(mv x y z) p), are given special treatment.
    
    A minor bug in untranslate has been fixed, which for example will fix
    the printing of conjunctions.
    
    Translate now takes a logicp argument, which indicates whether it
    enforces the restriction that :program mode functions do not occur in
    the result.
    
    The modified version of trace provided by ACL2, for use in raw Lisp,
    has been modified so that the lisp special variable *trace-alist* has a
    slightly different functionality.  This alist associates, using eq,
    symbols with the print representations of their values.  For example,
    initially *trace-alist* is a one-element list containing the pair (cons
    'state '|*the-live-state*|).  Thus, one may cons the pair (cons '*foo*
    "It's a FOO!") on to *trace-alist*; then until *foo* is defined, this
    change will have no effect, but after for example
    
         (defconst *foo* 17)
    
    then trace will print 17 as "It's a FOO!".
    
    Trace also traces the corresponding logic function.
    
    Proof-tree display has been improved slightly in the case of successful
    proofs and certain event failures.
    
    The function positive-integer-log2 has been deleted.
    
    The macro skip-proofs now prints a warning message when it is
    encountered in the context of an encapsulate event or a book.  See
    *note SKIP-PROOFS::.
    
    Some functions related to the-fn and wormhole1 now have defun-mode
    :program, but this change is almost certain to be inconsequential to
    all users.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE8-UPDATE,  Next: NOTE9,  Prev: NOTE8,  Up: RELEASE-NOTES
    
    NOTE8-UPDATE    ACL2 Version 1.8 (Summer, 1995) Notes
    
    ACL2 can now use Ordered Binary Decision Diagram technology.  See *note
    BDD::.  There is also a proof-checker bdd command.
    
    ACL2 is now more respectful of the intention of the function hide.  In
    particular, it is more careful not to dive inside any call of hide
    during equality substitution and case splitting.
    
    The ld special (see *note LD::) ld-pre-eval-print may now be used to
    turn off printing of input forms during processing of encapsulate and
    certify-book forms, by setting it to the value :never, i.e.,
    (set-ld-pre-eval-print :never state).  See *note LD-PRE-EVAL-PRINT::.
    
    The TUTORIAL documentation section (now obsolete) has, with much help
    from Bill Young, been substantially improved to a bona fide
    introduction.
    
    The term pretty-printer has been modified to introduce (<= X Y) as an
    abbreviation for (not (< Y X)).
    
    Forward chaining and linear arithmetic now both benefit from the
    evaluation of ground subterms.
    
    A new macro set-inhibit-output-lst has been defined.  This should be
    used when setting the state global inhibit-output-lst; see *note
    SET-INHIBIT-OUTPUT-LST:: and see *note PROOF-TREE::.
    
    The test for redundancy in definitions includes the guard and type
    declarations.  See *note REDUNDANT-EVENTS::.
    
    See *note GENERALIZED-BOOLEANS:: for a discussion of a potential
    soundness problem for ACL2 related to the question:  Which Common Lisp
    functions are known to return Boolean values?
    
    Here we will put some less important changes, additions, and so on.
    
    A bug has been fixed so that now, execution of :comp t (see *note
    COMP::) correctly handles non-standard characters.
    
    A bug in digit-char-p has been fixed, so that the "default" is nil
    rather than 0.
    
    True-listp now tests the final cdr against nil using eq instead of
    equal, for improved efficiency.  The logical meaning is, however,
    unchanged.
    
    Put-assoc-equal has been added to the logic (it used to have
    :defun-mode :program, and has been documented.
    
    
    File: acl2-doc-emacs.info,  Node: NOTE9,  Prev: NOTE8-UPDATE,  Up: RELEASE-NOTES
    
    NOTE9    ACL2 Version 1.9 (Fall, 1996) Notes
    
    By default, when the system is started it is illegal to use the
    variable STATE as a formal parameter of a function definition.  The aim
    is to prevent novice users from stumbling into the Byzantine syntactic
    restrictions on that variable symbol.  Use
    
         :set-state-ok t
    
    or, equivalently,
    
         (set-state-ok t)
    
    to switch back to the old default mode.  See *note SET-STATE-OK::
    
    Set-state-ok is an event that affects the ACL2 defaults table (see
    *note ACL2-DEFAULTS-TABLE::).  Recall that when books are included, the
    defaults table is restored to its pre-inclusion state.  Thus, while a
    set-state-ok form will permit the book to define a state-using
    function, it will not permit the user of the book to make such a
    definition.  We recommend putting (set-state-ok t) in any book that
    defines a state using function.
    
    Books certified under Version 1.8 must be recertified under Version
    1.9.  See :DOC version.
    
    The simplifier has been made to look out for built-in clauses, whereas
    in past versions such clauses were only noticed by the "preprocessor"
    at the top of the waterfall.  THIS CHANGE MAY PREVENT OLD SCRIPTS FROM
    REPLAYING!  The undesirable side-effect is caused by the fact that
    :HINTS require you to refer to clauses by their exact name (see *note
    GOAL-SPEC::) and because the new simplifier proves more clauses than
    before, the goals produced have different names.  Thus, if a script
    uses :HINTS that refer to clauses other than "Goal", e.g., "Subgoal
    1.3" then the hint may be applied to a different subgoal than
    originally intended.
    
    The use of built-in-clauses has been made more efficient.  If a set of
    clauses arise often in a piece of work, it might be advantageous to
    build them in even if that results in a large set (hundreds?) of
    built-in clauses.  See *note BUILT-IN-CLAUSE::
    
    Wormholes can now be used in :logic mode functions. See *note WORMHOLE::
    
    It is now possible to provide "computed hints."  For example, have you
    ever wished to say "in all goals with a name like this, :use that" or
    "if this term is in the subgoal, then :use that"?  Well, see *note
    COMPUTED-HINTS:: and the extraordinarily long example in see *note
    USING-COMPUTED-HINTS::.
    
    Hide terms may be rewritten with :rewrite rules about hide.  See *note
    HIDE::, where we also now explain why hide terms are sometimes
    introduced into your proof attempts.
    
    A bug that sometimes caused the "non-lazy IF" hard error message was
    fixed.
    
    A bug that sometimes caused a hard error in forward chaining was fixed.
    
    A bug in print-rules (:pr) was fixed.
    
    We report the use of :executable-counterparts in the evaluation of
    SYNTAXP forms.
    
    Some documentation errors were fixed.
    
    A bug in parent-tree tracking in add-literal-and-pt was fixed.
    
    A bug in ok$, go$ and eval$ was fixed.
    
    Clausify now optimizes (mv-nth 'k (list x0 ... xk ... xn)) to xk.
    
    
    File: acl2-doc-emacs.info,  Node: RULE-CLASSES,  Next: SERIALIZE,  Prev: RELEASE-NOTES,  Up: Top
    
    RULE-CLASSES    adding rules to the database
    
         Example Form (from community book finite-set-theory/total-ordering.lisp):
         (defthm <<-trichotomy
           (implies (and (ordinaryp x)
                         (ordinaryp y))
                    (or (<< x y)
                        (equal x y)
                        (<< y x)))
           :rule-classes
           ((:rewrite :corollary
                      (implies (and (ordinaryp x)
                                    (ordinaryp y)
                                    (not (<< x y))
                                    (not (equal x y)))
                               (<< y x)))))
    
         General Form:
         a true list of rule class objects as defined below
    
         Special Cases:
         a symbol abbreviating a single rule class object
    
    When defthm is used to prove a named theorem, rules may be derived from
    the proved formula and stored in the database.  The user specifies which
    kinds of rules are to be built, by providing a list of rule class names
    or, more generally, rule class objects, which name the kind of rule to
    build and optionally specify varioius attributes of the desired rule.
    The rule class names are :REWRITE, :BUILT-IN-CLAUSE, :CLAUSE-PROCESSOR,
    :COMPOUND-RECOGNIZER, :CONGRUENCE, :DEFINITION, :ELIM, :EQUIVALENCE,
    :FORWARD-CHAINING, :GENERALIZE, :INDUCTION, :LINEAR, :META,
    :REFINEMENT, :TAU-SYSTEM, :TYPE-PRESCRIPTION, :TYPE-SET-INVERTER, and
    :WELL-FOUNDED-RELATION.  Some classes require the user-specification of
    certain class-specific attributes.  Each class of rule affects the
    theorem prover's behavior in a different way, as discussed in the
    corresponding documentation topic.  In this topic we discuss the
    various attributes that may be attached to rule classes.
    
    A rule class object is either one of the :class keywords or else is a
    list of the form shown below.  Those fields marked with "(!)"  are
    required when the :class is as indicated.
    
         (:class
           :COROLLARY term
           :TRIGGER-FNS (fn1 ... fnk) ; provided :class = :META (!)
           :TRIGGER-TERMS (t1 ... tk) ; provided :class = :FORWARD-CHAINING
                                      ;       or :class = :LINEAR
           :TYPE-SET n                ; provided :class = :TYPE-SET-INVERTER
           :TYPED-TERM term           ; provided :class = :TYPE-PRESCRIPTION
           :CLIQUE (fn1 ... fnk)      ; provided :class = :DEFINITION
           :CONTROLLER-ALIST alist    ; provided :class = :DEFINITION
           :INSTALL-BODY directive    ; provided :class = :DEFINITION
           :LOOP-STOPPER alist        ; provided :class = :REWRITE
           :PATTERN term              ; provided :class = :INDUCTION (!)
           :CONDITION term            ; provided :class = :INDUCTION
           :SCHEME term               ; provided :class = :INDUCTION (!)
           :MATCH-FREE all-or-once    ; provided :class = :REWRITE
                                              or :class = :LINEAR
                                              or :class = :FORWARD-CHAINING
           :BACKCHAIN-LIMIT-LST limit ; provided :class = :REWRITE
                                              or :class = :META
                                              or :class = :LINEAR
                                              or :class = :TYPE-PRESCRIPTION
           :HINTS hints               ; provided instrs = nil
           :INSTRUCTIONS instrs       ; provided  hints = nil
           :OTF-FLG flg)
    
    When rule class objects are provided by the user, most of the fields are
    optional and their values are computed in a context sensitive way.
    When a :class keyword is used as a rule class object, all relevant
    fields are determined contextually.  Each rule class object in
    :rule-classes causes one or more rules to be added to the database.
    The :class keywords are documented individually under the following
    names.  Note that when one of these names is used as a :class, it is
    expected to be in the keyword package (i.e., the names below should be
    preceded by a colon but the ACL2 documentation facilities do not permit
    us to use keywords below).
    
    * Menu:
    
    * BUILT-IN-CLAUSE:: to build a clause into the simplifier
    
    * CLAUSE-PROCESSOR:: make or apply a :clause-processor rule (goal-level simplifier)
    
    * COMPOUND-RECOGNIZER:: make a rule used by the typing mechanism
    
    * CONGRUENCE:: the relations to maintain while simplifying arguments
    
    * DEFINITION:: make a rule that acts like a function definition
    
    * ELIM:: make a destructor elimination rule
    
    * EQUIVALENCE:: mark a relation as an equivalence relation
    
    * FORWARD-CHAINING:: make a rule to forward chain when a certain trigger arises
    
    * FREE-VARIABLES:: free variables in rules
    
    * GENERALIZE:: make a rule to restrict generalizations
    
    * INDUCTION:: make a rule that suggests a certain induction
    
    * LINEAR:: make some arithmetic inequality rules
    
    * META:: make a :meta rule (a hand-written simplifier)
    
    * REFINEMENT:: record that one equivalence relation refines another
    
    * REWRITE:: make some :rewrite rules (possibly conditional ones)
    
    * TAU-SYSTEM:: make a rule for the ACL2 ``type checker''
    
    * TYPE-PRESCRIPTION:: make a rule that specifies the type of a term
    
    * TYPE-SET-INVERTER:: exhibit a new decoding for an ACL2 type-set
    
    * WELL-FOUNDED-RELATION:: show that a relation is well-founded on a set
    
    See also force, case-split, syntaxp, and bind-free for "pragmas" one
    can wrap around individual hypotheses of certain classes of rules to
    affect how the hypothesis is relieved.
    
    Before we get into the discussion of rule classes, let us return to an
    important point.  In spite of the large variety of rule classes
    available, at present we recommend that new ACL2 users rely almost
    exclusively on (conditional) rewrite rules.  A reasonable but slightly
    bolder approach is to use :type-prescription and :forward-chaining
    rules for "type-theoretic" rules, especially ones whose top-level
    function symbol is a common one like true-listp or consp; see *note
    TYPE-PRESCRIPTION:: and see *note FORWARD-CHAINING::.  However, the
    rest of the rule classes are really not intended for widespread use,
    but rather are mainly for experts.
    
    We expect that we will write more about the question of which kind of
    rule to use.  For now: when in doubt, use a :rewrite rule.
    
    :Rule-classes is an optional keyword argument of the defthm (and
    defaxiom) event.  In the following, let name be the name of the event
    and let thm be the formula to be proved or added as an axiom.
    
    If :rule-classes is not specified in a defthm (or defaxiom) event, it
    is as though what was specified was to make one or more :rewrite rules,
    i.e., as though :rule-classes ((:rewrite)) had been used.  Use
    :rule-classes nil to specify that no rules are to be generated.
    
    If :rule-classes class is specified, where class is a non-nil symbol,
    it is as though :rule-classes ((class)) had been used.  Thus,
    :rule-classes :forward-chaining is equivalent to :rule-classes
    ((:forward-chaining)).
    
    We therefore now consider :rule-classes as a true list.  If any element
    of that list is a keyword, replace it by the singleton list containing
    that keyword.  Thus, :rule-classes (:rewrite :elim) is the same as
    :rule-classes ((:rewrite) (:elim)).
    
    Each element of the expanded value of :rule-classes must be a true list
    whose car is one of the rule class keyword tokens listed above, e.g.,
    :rewrite, :elim, etc., and whose cdr is a "keyword alist" alternately
    listing keywords and values.  The keywords in this alist must be taken
    from those shown below.  They may be listed in any order and most may
    be omitted, as specified below.
    
         :Corollary -- its value, term, must be a term.  If omitted, this
         field defaults to thm.  The :corollary of a rule class object is
         the formula actually used to justify the rule created and thus
         determines the form of the rule.  Nqthm provided no similar
         capability: each rule was determined by thm, the theorem or axiom
         added.  ACL2 permits thm to be stated "elegantly" and then allows
         the :corollary of a rule class object to specify how that elegant
         statement is to be interpreted as a rule.  For the rule class
         object to be well-formed, its (defaulted) :corollary, term, must
         follow from thm.  Unless term follows trivially from thm using
         little more than propositional logic, the formula (implies thm
         term) is submitted to the theorem prover and the proof attempt
         must be successful.  During that proof attempt the values of
         :hints, :instructions, and :otf-flg, as provided in the rule class
         object, are provided as arguments to the prover.  Such auxiliary
         proofs give the sort of output that one expects from the prover.
         However, as noted above, corollaries that follow trivially are not
         submitted to the prover; thus, such corollaries cause no prover
         output.
    
         Note that before term is stored, all calls of macros in it are
         expanded away.  See *note TRANS::.
    
         :Hints, :instructions, :otf-flg -- the values of these fields must
         satisfy the same restrictions placed on the fields of the same
         names in defthm.  These values are passed to the recursive call of
         the prover used to establish that the :corollary of the rule class
         object follows from the theorem or axiom thm.
    
         :Type-set -- this field may be supplied only if the :class is
         :type-set-inverter.  When provided, the value must be a type-set,
         an integer in a certain range.  If not provided, an attempt is
         made to compute it from the corollary.  See *note
         TYPE-SET-INVERTER::.
    
         :Typed-term -- this field may be supplied only if the :class is
         :type-prescription.  When provided, the value is the term for which
         the :corollary is a type-prescription lemma.  If no :typed-term is
         provided in a :type-prescription rule class object, we try to
         compute heuristically an acceptable term.  See *note
         TYPE-PRESCRIPTION::.
    
         :Trigger-terms -- this field may be supplied only if the :class is
         :forward-chaining or :linear.  When provided, the value is a list
         of terms, each of which is to trigger the attempted application of
         the rule.  If no :trigger-terms is provided, we attempt to compute
         heuristically an appropriate set of triggers.  See *note
         FORWARD-CHAINING:: or see *note LINEAR::.
    
         :Trigger-fns -- this field must (and may only) be supplied if the
         :class is :meta.  Its value must be a list of function symbols
         (except that a macro alias can stand in for a function symbol; see
         *note ADD-MACRO-ALIAS::).  Terms with these symbols trigger the
         application of the rule.  See *note META::.
    
         :Clique and :controller-alist -- these two fields may only be
         supplied if the :class is :definition.  If they are omitted, then
         ACL2 will attempt to guess them.  Suppose the :corollary of the
         rule is (implies hyp (equiv (fn a1 ... an) body)).  The value of
         the :clique field should be a true list of function symbols, and if
         non-nil must include fn.  These symbols are all the members of the
         mutually recursive clique containing this definition of fn.  That
         is, a call of any function in :clique is considered a "recursive
         call" for purposes of the expansion heuristics.  The value of the
         :controller-alist field should be an alist that maps each function
         symbol in the :clique to a list of t's and nil's of length equal
         to the arity of the function.  For example, if :clique consists of
         just two symbols, fn1 and fn2, of arities 2 and 3 respectively,
         then ((fn1 t nil) (fn2 nil t t)) is a legal value of
         :controller-alist.  The value associated with a function symbol in
         this alist is a "mask" specifying which argument slots of the
         function "control" the recursion for heuristic purposes.  Sloppy
         choice of :clique or :controller-alist can result in infinite
         expansion and stack overflow.
    
         :Install-body -- this field may only be supplied if the :class is
         :definition.  Its value must be t, nil, or the default,
         :normalize.  A value of t or :normalize will cause ACL2 to install
         this rule as the new body of the function being "defined" (fn in
         the paragraph just above); hence this definition will be installed
         for future :expand hints.  Furthermore, if this field is omitted
         or the value is :normalize, then this definition will be
         simplified using the so-called "normalization" procedure that is
         used when processing definitions made with defun.  You must
         explicitly specify :install-body nil in the following cases: fn
         (as above) is a member of the value of constant
         *definition-minimal-theory*, the arguments are not a list of
         distinct variables, equiv (as above) is not equal, or there are
         free variables in the hypotheses or right-hand side (see *note
         FREE-VARIABLES::).  However, supplying :install-body nil will not
         affect the rewriter's application of the :definition rule, other
         than to avoid using the rule to apply :expand hints.  If a
         definition rule equates (f a1 ... ak) with body but there are
         hypotheses, hyps, then :expand hints will replace terms (f term1
         ... termk) by corresponding terms (if hyps body (hide (f term1 ...
         termk))).
    
         :Loop-stopper -- this field may only be supplied if the class is
         :rewrite.  Its value must be a list of entries each consisting of
         two variables followed by a (possibly empty) list of functions,
         for example ((x y binary-+) (u v foo bar)).  It will be used to
         restrict application of rewrite rules by requiring that the list
         of instances of the second variables must be "smaller" than the
         list of instances of the first variables in a sense related to the
         corresponding functions listed; see *note LOOP-STOPPER::.  The
         list as a whole is allowed to be nil, indicating that no such
         restriction shall be made.  Note that any such entry that contains
         a variable not being instantiated, i.e., not occurring on the left
         side of the rewrite rule, will be ignored.  However, for
         simplicity we merely require that every variable mentioned should
         appear somewhere in the corresponding :corollary formula.
    
         :Pattern, :Condition, :Scheme -- the first and last of these
         fields must (and may only) be supplied if the class is :induction.
         :Condition is optional but may only be supplied if the class is
         :induction.  The values must all be terms and indicate,
         respectively, the pattern to which a new induction scheme is to be
         attached, the condition under which the suggestion is to be made,
         and a term which suggests the new scheme.  See *note INDUCTION::.
    
         :Match-free -- this field must be :all or :once and may be
         supplied only if the :class is either :rewrite, :linear, or
         :forward-chaining.  (This field is not implemented for other rule
         classes, including the :type-prescription rule class.)  See *note
         FREE-VARIABLES:: for a description of this field.  Note: Although
         this field is intended to be used for controlling retries of
         matching free variables in hypotheses, it is legal to supply it
         even if there are no such free variables.  This can simplify the
         automated generation of rules, but note that when :match-free is
         supplied, the warning otherwise provided for the presence of free
         variables in hypotheses will be suppressed.
    
         :Backchain-limit-lst -- this field may be supplied only if the
         :class is either :rewrite, :meta, :linear, or :type-prescription.
         It is further required either only one rule is generated from the
         formula or, at least, every such rule has the same list of
         hypotheses.  The value for :backchain-limit-lst must be nil; a
         non-negative integer; or, except in the case of :meta rules, a true
         list each element of which is either nil or a non-negative
         integer.  If it is a list, its length must be equal to the number
         of hypotheses of the rule and each item in the list is the
         "backchain limit" associated with the corresponding hypothesis.
         If backchain-limit-lst is a non-negative integer, it is defaulted
         to a list of the appropriate number of repetitions of that
         integer.  The backchain limit of a hypothesis is used to limit the
         effort that ACL2 will expend when relieving the hypothesis.  If it
         is NIL, no new limits are imposed; if it is an integer, the
         hypothesis will be limited to backchaining at most that many
         times.  Note that backchaining may be further limited by a global
         backchain-limit; see *note BACKCHAIN-LIMIT:: for details.  For
         different ways to reign in the rewriter, see *note
         REWRITE-STACK-LIMIT:: and see *note SET-PROVER-STEP-LIMIT::.
         Jared Davis has pointed out that you can set the
         :backchain-limit-lst to 0 to avoid any attempt to relieve forced
         hypotheses, which can lead to a significant speed-up in some cases.
    
    Once thm has been proved (in the case of defthm) and each rule class
    object has been checked for well-formedness (which might require
    additional proofs), we consider each rule class object in turn to
    generate and add rules.  Let :class be the class keyword token of the
    ith class object (counting from left to right).  Generate the rune
    (:class name . x), where x is nil if there is only one class and
    otherwise x is i.  Then, from the :corollary of that object, generate
    one or more rules, each of which has the name (:class name . x).  See
    the :doc entry for each rule class to see how formulas determine rules.
    Note that it is in principle possible for several rules to share the
    same name; it happens whenever a :corollary determines more than one
    rule.  This in fact only occurs for :rewrite, :linear, and
    :forward-chaining class rules and only then if the :corollary is
    essentially a conjunction.  (See the documentation for rewrite, linear,
    or forward-chaining for details.)
    
    
    File: acl2-doc-emacs.info,  Node: BUILT-IN-CLAUSE,  Next: CLAUSE-PROCESSOR,  Prev: RULE-CLASSES,  Up: RULE-CLASSES
    
    BUILT-IN-CLAUSE    to build a clause into the simplifier
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example:
         (defthm acl2-count-abl
           (and (implies (and (true-listp x)
                              (not (equal x nil)))
                         (< (acl2-count (abl x))
                            (acl2-count x)))
                (implies (and (true-listp x)
                              (not (equal nil x)))
                         (< (acl2-count (abl x))
                            (acl2-count x))))
           :rule-classes :built-in-clause)
    
    A :built-in-clause rule can be built from any formula other than
    propositional tautologies.  Roughly speaking, the system uses the list
    of built-in clauses as the first method of proof when attacking a new
    goal.  Any goal that is subsumed by a built in clause is proved
    "silently."
    
    ACL2 maintains a set of "built-in" clauses that are used to
    short-circuit certain theorem proving tasks.  We discuss this at length
    below.  When a theorem is given the rule class :built-in-clause ACL2
    flattens the implies and and structure of the :corollary formula so as
    to obtain a set of formulas whose conjunction is equivalent to the given
    corollary.  It then converts each of these to clausal form and adds each
    clause to the set of built-in clauses.
    
    The example above (regardless of the definition of abl) will build in
    two clauses,
    
         {(not (true-listp x))
          (equal x nil)
          (< (acl2-count (abl x)) (acl2-count x))}
    
    and
    
         {(not (true-listp x))
          (equal nil x)
          (< (acl2-count (abl x)) (acl2-count x))}.
    
    We now give more background.
    
    Recall that a clause is a set of terms, implicitly representing the
    disjunction of the terms.  Clause c1 is "subsumed" by clause c2 if some
    instance of c2 is a subset c1.
    
    For example, let c1 be
    
         {(not (consp l))
          (equal a (car l))
          (< (acl2-count (cdr l)) (acl2-count l))}.
    
    Then c1 is subsumed by c2, shown below,
    
         {(not (consp x))
          ; second term omitted here
          (< (acl2-count (cdr x)) (acl2-count x))}
    
    because we can instantiate x in c2 with l to obtain a subset of c1.
    
    Observe that c1 is the clausal form of
    
         (implies (and (consp l)
                       (not (equal a (car l))))
                  (< (acl2-count (cdr l)) (acl2-count l))),
    
    c2 is the clausal form of
    
         (implies (consp l)
                  (< (acl2-count (cdr l)) (acl2-count l)))
    
    and the subsumption property just means that c1 follows trivially from
    c2 by instantiation.
    
    The set of built-in clauses is just a set of known theorems in clausal
    form.  Any formula that is subsumed by a built-in clause is thus a
    theorem.  If the set of built-in theorems is reasonably small, this
    little theorem prover is fast.  ACL2 uses the "built-in clause check"
    in four places: (1) at the top of the iteration in the prover - thus if
    a built-in clause is generated as a subgoal it will be recognized when
    that goal is considered, (2) within the simplifier so that no built-in
    clause is ever generated by simplification, (3) as a filter on the
    clauses generated to prove the termination of recursively defun'd
    functions and (4) as a filter on the clauses generated to verify the
    guards of a function.
    
    The latter two uses are the ones that most often motivate an extension
    to the set of built-in clauses.  Frequently a given formalization
    problem requires the definition of many functions which require
    virtually identical termination and/or guard proofs.  These proofs can
    be short-circuited by extending the set of built-in clauses to contain
    the most general forms of the clauses generated by the definitional
    schemes in use.
    
    The attentive user might have noticed that there are some recursive
    schemes, e.g., recursion by cdr after testing consp, that ACL2 just
    seems to "know" are ok, while for others it generates measure clauses
    to prove.  Actually, it always generates measure clauses but then
    filters out any that pass the built-in clause check.  When ACL2 is
    initialized, the clause justifying cdr recursion after a consp test is
    added to the set of built-in clauses.  (That clause is c2 above.)
    
    Note that only a subsumption check is made; no rewriting or
    simplification is done.  Thus, if we want the system to "know" that cdr
    recursion is ok after a negative atom test (which, by the definition of
    atom, is the same as a consp test), we have to build in a second
    clause.  The subsumption algorithm does not "know" about commutative
    functions.  Thus, for predictability, we have built in commuted
    versions of each clause involving commutative functions.  For example,
    we build in both
    
         {(not (integerp x))
          (< 0 x)
          (= x 0)
          (< (acl2-count (+ -1 x)) (acl2-count x))}
    
    and the commuted version
    
         {(not (integerp x))
          (< 0 x)
          (= 0 x)
          (< (acl2-count (+ -1 x)) (acl2-count x))}
    
    so that the user need not worry whether to write (= x 0) or (= 0 x) in
    definitions.
    
    :built-in-clause rules added by the user can be enabled and disabled.
    
    
    File: acl2-doc-emacs.info,  Node: CLAUSE-PROCESSOR,  Next: COMPOUND-RECOGNIZER,  Prev: BUILT-IN-CLAUSE,  Up: RULE-CLASSES
    
    CLAUSE-PROCESSOR    make or apply a :clause-processor rule (goal-level simplifier)
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example (which we'll return to, below):
         (defthm correctness-of-note-fact-clause-processor
           (implies (and (pseudo-term-listp cl)
                         (alistp a)
                         (evl0 (conjoin-clauses
                                (note-fact-clause-processor cl term))
                               a))
                    (evl0 (disjoin cl) a))
           :rule-classes :clause-processor)
    
    Also see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR:: for documentation of
    an analogous utility that does not require the clause-processor to be
    proved correct.  But please read the present documentation before
    reading about that utility.  Both utilities designate functions
    "clause-processors".  Such functions must be executable -- hence not
    constrained by virtue of being introduced in the signature of an
    encapsulate -- and must respect stobj and output arity restrictions.
    For example, something like (car (mv ...)) is illegal; also see *note
    SIGNATURE::.
    
    We begin this documentation with an introduction, focusing on an
    example, and then conclude with details.  You might find it most useful
    simply to look at the examples in community books directory
    books/clause-processors/; see file Readme.lsp in that directory.
    
    A :clause-processor rule installs a simplifier at the level of goals,
    where a goal is represented as a _clause_: a list of terms that is
    implicitly viewed as a disjunction (the application of OR).  For
    example, if ACL2 prints a goal in the form (implies (and p q) r), then
    the clause might be the one-element list containing the internal
    representation of this term -- (implies (if p q 'nil) r) -- but more
    likely, the corresponding clause is ((not p) (not q) r).  Note that the
    members of a clause are _translated_ terms; see *note TERM::.  For
    example, they do not contains calls of the macro AND, and constants are
    quoted.
    
    Note that clause-processor simplifiers are similar to metafunctions, and
    similar efficiency considerations apply.  See *note META::, in
    particular the discussion on how to "make a metafunction maximally
    efficient."
    
    Unlike rules of class :meta, rules of class :clause-processor must be
    applied by explicit :clause-processor hints; they are not applied
    automatically (unless by way of computed hints; see *note
    COMPUTED-HINTS::).  But :clause-processor rules can be useful in
    situations for which it is more convenient to code a simplifier that
    manipulates the entire goal clause rather than individual subterms of
    terms in the clause.
    
    We begin with a simple illustrative example: a clause-processor that
    assumes an alleged fact (named term in the example) and creates a
    separate goal to prove that fact.  We can extend the hypotheses of the
    current goal (named cl in the example) with a term by adding the
    negation of that term to the clause (disjunctive) representation of
    that goal.  So the following returns a list of two clauses: the result
    of adding term as a hypothesis to the input clause, as just described,
    and a second clause consisting only of that term.  This list of two
    clauses can be viewed as the conjunction of the first clause and the
    second clause (where again, each clause is viewed as a disjunction).
    
    
         (defun note-fact-clause-processor (cl term)
           (declare (xargs :guard t)) ; optional, for better efficiency
           (list (cons (list 'not term)
                       cl)
                 (list term)))
    
    As with :meta rules, we need to introduce a suitable evaluator; see
    *note DEFEVALUATOR:: if you want details.  Since we expect to reason
    about the function NOT, because of its role in
    note-fact-clause-processor as defined above, we include NOT in the set
    of functions known to this evaluator.  We also include IF, as is often
    a good idea.
    
    
         (defevaluator evl0 evl0-list
           ((not x) (if x y z)))
    
    ACL2 can now prove the following theorem automatically.  (This is the
    example displayed at the outset of this documentation topic.)  Of
    course, :clause-processor rules about clause-processor functions less
    trivial than note-fact-clause-processor may require lemmas to be proved
    first!  The function disjoin takes a clause and returns its disjunction
    (the result of applying OR to its members), and conjoin-clauses applies
    disjoin to every element of a given list of clauses and then conjoins
    (applies AND) to the corresponding list of resulting terms.
    
    
         (defthm correctness-of-note-fact-clause-processor
           (implies (and (pseudo-term-listp cl)
                         (alistp a)
                         (evl0 (conjoin-clauses
                                (note-fact-clause-processor cl term))
                               a))
                    (evl0 (disjoin cl) a))
           :rule-classes :clause-processor)
    
    Now let us submit a silly but illustrative example theorem to ACL2, to
    show how a corresponding :clause-processor hint is applied.  The hint
    says to apply the clause-processor function,
    note-fact-clause-processor, to the current goal clause and a "user
    hint" as the second argument of that function, in this case (equal a
    a).  Thus, a specific variable, clause, is always bound to the current
    goal clause for the evaluation of the :clause-processor hint, to
    produce a list of clauses.  Since two subgoals are created below, we
    know that this list contained two clauses.  Indeed, these are the
    clauses returned when note-fact-clause-processor is applied to two
    arguments:  the current clause, which is the one-element list ((equal
    (car (cons x y)) x)), and the user hint, (equal a a).
    
    
         ACL2 !>(thm (equal (car (cons x y))
                            x)
                     :hints
                     (("Goal"
                       :clause-processor
                       (note-fact-clause-processor clause '(equal a a)))))
    
         [Note:  A hint was supplied for our processing of the goal above.
         Thanks!]
    
         We now apply the verified :CLAUSE-PROCESSOR function NOTE-FACT-CLAUSE-
         PROCESSOR to produce two new subgoals.
    
         Subgoal 2
         (IMPLIES (EQUAL A A)
                  (EQUAL (CAR (CONS X Y)) X)).
    
         But we reduce the conjecture to T, by the :executable-counterpart of
         IF and the simple :rewrite rule CAR-CONS.
    
         Subgoal 1
         (EQUAL A A).
    
         But we reduce the conjecture to T, by primitive type reasoning.
    
         Q.E.D.
    
         Summary
         Form:  ( THM ...)
         Rules: ((:EXECUTABLE-COUNTERPART IF)
                 (:EXECUTABLE-COUNTERPART NOT)
                 (:FAKE-RUNE-FOR-TYPE-SET NIL)
                 (:REWRITE CAR-CONS))
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
         Proof succeeded.
         ACL2 !>
    
    That concludes our introduction to clause-processor rules and hints.
    We turn now to detailed documentation.
    
    The signature of a clause-processor function, CL-PROC, must have one of
    the following forms.  Here, each st_i is a stobj (possibly state) while
    the other parameters and results are not stobjs (see *note STOBJ::).
    Note that there need not be input stobjs in [3] -- i.e., k can be 0 --
    and even if there are, there need not be output stobjs.
    
         [1]  ((CL-PROC cl) => cl-list)
    
         [2]  ((CL-PROC cl hint) => cl-list)
    
         [3]  ((CL-PROC cl hint st_1 ... st_k) => (mv erp cl-list st_i1 ... st_in))
    
    In [3], we think of the first component of the result as an error flag.
    Indeed, a proof will instantly abort if that error flag is not nil.
    
    We next discuss the legal forms of :clause-processor rules, followed
    below by a discussion of :clause-processor hints.  In the discussion
    below, we use lower-case names to represent specific symbols, for
    example implies, and we use upper-case names to represent more
    arbitrary pieces of syntax (which we will describe), for example, CL.
    
    If a :rule-classes specification includes :clause-processor, then the
    corresponding term must have the following form.  (Additional
    "meta-extract" hypotheses, not shown or discussed below, may be
    included as desired in order to use facts from the logical world to
    help prove the rule; see *note META-EXTRACT:: for explanation of this
    advanced feature.)
    
         (implies (and (pseudo-term-listp CL)
                       (alistp A)
                       (EVL (conjoin-clauses )
                             B))
                  (EVL (disjoin CL) A))
    
    Here EVL is a known evaluator; CL and A are distinct non-stobj
    variables; and  is an expression representing the clauses
    returned by the clause-processor function CL-PROC, whose form depends on
    the signature of that function, as follows.  Typically B is A, but it
    can be any term (useful when generalization is occurring; see the
    example "Test generalizing alist" in community book
    books/clause-processors/basic-examples.lisp).  For cases [1] and [2]
    above,  is of the form (CL-PROC CL) or (CL-PROC CL HINT),
    respectively, where in the latter case HINT is a non-stobj variable
    distinct from the variables CL and A.  For case [3],  is of
    the form
    
         (clauses-result (CL-PROC CL HINT st_1 ... st_k))
    
    where the st_i are the specific stobj names mentioned in [3].
    Logically, clauses-result returns the cadr if the car is NIL, and
    otherwise (for the error case) returns a list containing the empty
    (false) clause.  So in the non-error case, clauses-result picks out the
    second result, denoted cl-list in [3] above, and in the error case the
    implication above trivially holds.
    
    In the above theorem, we are asked to prove (EVL (disjoin CL) A)
    assuming that the conjunction of all clauses produced by the clause
    processor evaluates to a non-nil value under some alist B.  In fact, we
    can choose B so as to allow us to assume evaluations of the generated
    clauses over many different alists.  This technique is discussed in the
    community book books/clause-processors/multi-env-trick.lisp, which
    introduces some macros that may be helpful in accomplishing proofs of
    this type.
    
    The clause-processor function, CL, must have a guard that ACL2 can
    trivially prove from the hypotheses that the first argument of CL is
    known to be a pseudo-term-listp and any stobj arguments are assumed to
    satisfy their stobj predicates.
    
    Next we specify the legal forms for :clause-processor hints.  These
    depend on the signature as described in [1] through [3] above.  Below,
    as above, CL-PROC is the clause-processor function, and references to
    "clause" refer to that exact variable (not, for example, to cl).  In
    each of the three cases, the forms shown for that case are equivalent;
    in particular, the :function syntax is simply a convenience for the
    final form in each case.
    
    Signature [1], ((cl-proc cl) => cl-list):
    
         :clause-processor CL-PROC
         :clause-processor (:function CL-PROC)
         :clause-processor (CL-PROC clause)
    
    or any term macroexpanding to (CL-PROC clause).
    
    Signature [2], ((cl-proc cl hint) => cl-list):
    
         :clause-processor (:function CL-PROC :hint HINT)
         :clause-processor (CL-PROC clause HINT)
    
    or any term macroexpanding to (CL-PROC clause HINT), where HINT is any
    term with at most CLAUSE free.
    
    Signature [3], ((CL-PROC cl hint ...) => (mv erp cl-list ...))
    
         :clause-processor (:function CL-PROC :hint HINT)
         :clause-processor (CL-PROC clause HINT st_1 ... st_k)
    
    or any term macroexpanding to (CL-PROC clause HINT st_1 ... st_k), where
    HINT is any term with at most CLAUSE free.
    
    A :clause-processor hint causes the proof to abort if the result
    returned by evaluating the suitable CL-PROC call, as above, is not a
    list of clauses, i.e., a list of (translated) term lists.  The proof
    also aborts if in case [3] the first (erp) value returned is not nil,
    in which case erp is used for printing an error message as follows: if
    it is a string, then that string is printed; but if it is a non-empty
    true list whose first element is a string, then it is printed as though
    by (fmt ~@0 (list (cons #\0 erp)) ...) (see *note FMT::).  Otherwise, a
    non-nil erp value causes a generic error message to be printed.
    
    If there is no error as above, but the CL-PROC call returns clause list
    whose single element is equal to the input clause, then the hint is
    ignored since we are left with the goal with which we started.  In that
    case, the other prover processes are then applied as usual.
    
    You can see all current :clause-processor rules by issuing the following
    command: (print-clause-processor-rules).
    
    The following paper discusses ACL2 clause-processors at a high level
    suitable for a non-ACL2 audience:
    
         M. Kaufmann, J S. Moore, S. Ray, and E. Reeber, "Integrating
         External Deduction Tools with ACL2."  _Journal of Applied Logic_
         (Special Issue: Empirically Successful Computerized Reasoning),
         Volume 7, Issue 1, March 2009, pp. 3-25.  Also published online
         (DOI 10.1016/j.jal.2007.07.002).  Preliminary version in:
         Proceedings of the 6th International Workshop on the
         Implementation of Logics (IWIL 2006) (C. Benzmueller, B. Fischer,
         and G. Sutcliffe, editors), CEUR Workshop Proceedings Vol. 212,
         Phnom Penh, Cambodia, pp. 7-26, November 2006,
         `http://ceur-ws.org/Vol-212/'.
    
    
    File: acl2-doc-emacs.info,  Node: COMPOUND-RECOGNIZER,  Next: CONGRUENCE,  Prev: CLAUSE-PROCESSOR,  Up: RULE-CLASSES
    
    COMPOUND-RECOGNIZER    make a rule used by the typing mechanism
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Examples:
         (defthm alistp-implies-true-listp-compound-recognizer
           (implies (alistp x)                 ; When (alistp x) is assumed true, add
                    (true-listp x))            ; the additional hypothesis that x is
           :rule-classes :compound-recognizer) ; of primitive type true-listp.
    
         (defthm natp-compound-recognizer      ; See discussion below.
           (equal (natp x)
                  (and (integerp x)
                       (<= 0 x)))
           :rule-classes :compound-recognizer)
    
    Before presenting the General Forms, we start with a motivating
    example: the second defthm form above, which provides a nice example of
    a :compound-recognizer rule that is built into ACL2.  To see how this
    rule might be useful, consider the following (admittedly very simple)
    events.
    
         (defun triple (x)
           (* 3 x))
    
         (defthm triple-preserves-integerp
           (implies (integerp x)
                    (integerp (triple x))))
    
         (in-theory (disable triple natp))
    
    If the above :compound-recognizer rule is disabled, then the following
    trivial theorem fails as shown; we explain below.
    
         (thm (implies (natp x)
                       (integerp (triple x)))
           :hints (("Goal" :in-theory (disable natp-compound-recognizer))))
    
    The problem is that when ACL2 tries to rewrite the term (integerp
    (triple x)) using the :rewrite rule triple-preserves-integerp, it needs
    to rewrite the hypothesis (integerp x) to t, but instead what is known
    is (natp x).  If we remove the hint, then the proof succeeds because
    the above :compound-recognizer rule tells ACL2 that when assuming (natp
    x) to be true, it should actually assume both (integerp x) and (<= 0 x)
    to be true.
    
         General Forms:
         (implies (fn x) concl)               ; (1)
         (implies (not (fn x)) concl)         ; (2)
         (and (implies (fn x) concl1)         ; (3)
              (implies (not (fn x)) concl2))
         (if (fn x) concl1 concl2)            ; (4)
         (iff (fn x) concl)                   ; (5)
         (equal (fn x) concl)                 ; (6)
    
    where fn is a Boolean valued function of one argument, x is a variable
    symbol, and the system can deduce some restriction on the primitive
    type of x from the assumption that concl holds.  The last restriction
    is vague but one way to understand it is to weaken it a little to "and
    concl is a non-tautological conjunction or disjunction of the primitive
    type recognizers listed below."
    
    The primitive ACL2 types and a suitable primitive recognizing
    expression for each are listed below.
    
           type                suitable primitive recognizer
    
           zero                (equal x 0)
           negative integers   (and (integerp x) (< x 0))
           positive integers   (and (integerp x) (> x 0))
           negative ratio      (and (rationalp x)
                                    (not (integerp x))
                                    (< x 0))
           positive ratio      (and (rationalp x)
                                    (not (integerp x))
                                    (> x 0))
           complex rational    (complex-rationalp x)
           nil                 (equal x nil)
           t                   (equal x t)
           other symbols       (and (symbolp x)
                                    (not (equal x nil))
                                    (not (equal x t)))
           proper conses       (and (consp x)
                                    (true-listp x))
           improper conses     (and (consp x)
                                    (not (true-listp x)))
           strings             (stringp x)
           characters          (characterp x)
    
    Thus, a suitable concl to recognize the naturals would be (or (equal x
    0) (and (integerp x) (> x 0))) but it turns out that we also permit
    (and (integerp x) (>= x 0)).  Similarly, the true-lists could be
    specified by
    
         (or (equal x nil) (and (consp x) (true-listp x)))
    
    but we in fact allow (true-listp x).  When time permits we will document
    more fully what is allowed or implement a macro that permits direct
    specification of the desired type in terms of the primitives.
    
    There are essentially four forms of :compound-recognizer rules, as the
    forms labeled (3) and (4) above are equivalent, as are those labeled (5)
    and (6).  We explain how such rules are used by considering the
    individual forms.
    
    Consider form (1), (implies (fn x) concl).  The effect of such a rule is
    that when the rewriter assumes (fn x) true, as it would while diving
    through (if (fn x) xxx ...) to rewrite xxx, it restricts the type of x
    as specified by concl.  For example, if concl is the term (integerp x),
    then when rewriting xxx, x will be assumed to be an integer.  However,
    when assuming (fn x) false, as necessary in (if (fn x) ... xxx), the
    rule permits no additional assumptions about the type of x.  For
    example, if fn is primep, i.e., the predicate that recognizes prime
    numbers, then (implies (primep x) (and (integerp x) (>= x 0))) is a
    compound recognizer rule of the first form.  When (primep x) is assumed
    true, the rewriter gains the additional information that x is a natural
    number.  When (primep x) is assumed false, no additional information is
    gained -- since x may be a non-prime natural or may not even be a
    natural.
    
    Form (2) is the symmetric case, when assuming (fn x) false permits type
    restrictions on x but assuming (fn x) true permits no such
    restrictions.  For example, if we defined exprp to be the recognizer for
    well-formed expressions for some language in which all symbols, numbers,
    character objects and strings were well-formed -- e.g., the
    well-formedness rules only put restrictions on expressions represented
    by consps -- then the theorem (implies (not (exprp x)) (consp x)) is a
    rule of the second form.  Assuming (exprp x) true tells us nothing
    about the type of x; assuming it false tells us x is a consp.
    
    Forms (3) and (4), which are really equivalent, address themselves to
    the case where one type may be deduced from (fn x) and a generally
    unrelated type may be deduced from its negation.  If we modified the
    expression recognizer above so that character objects are illegal, then
    rules of the forms (3) and (4) are
    
         (and (implies (exprp x) (not (characterp x)))
              (implies (not (exprp x)) (or (consp x) (characterp x)))).
         (if (exprp x)
             (not (characterp x))
           (or (consp x) (characterp x)))
    
    Finally, rules of forms (5) and (6) address the case where fn recognizes
    all and only the objects whose type is described.  In these cases, fn is
    really just a new name for some "compound recognizers."  The classic
    example is (booleanp x), which is just a handy combination of two
    primitive types:
    
         (iff (booleanp x) (or (equal x t) (equal x nil))).
    
    Often it is best to disable fn after proving that it is a compound
    recognizer, since otherwise the term (fn x) will be expanded and thus
    disappear.
    
    Every time you prove a new compound recognizer rule about fn it
    overrides all previously proved compound recognizer rules about fn.
    Thus, if you want to establish the type implied by (fn x) and you want
    to establish the type implied by (not (fn x)), you must prove a
    compound recognizer rule of the third, fourth, fifth, or sixth forms.
    Proving a rule of the first form followed by one of the second only
    leaves the second fact in the database.
    
    Compound recognizer rules can be disabled with the effect that older
    rules about fn, if any, are exposed.
    
    If you prove more than one compound recognizer rule for a function, you
    may see a *warning* message to the effect that the new rule is not as
    "restrictive" as the old.  That is, the new rules do not give the
    rewriter strictly more type information than it already had.  The new
    rule is stored anyway, overriding the old, if enabled.  You may be
    playing subtle games with enabling or rewriting.  But two other
    interpretations are more likely, we think.  One is that you have
    forgotten about an earlier rule and should merely print it out to make
    sure it says what you intend, and then discard your new rule.  The
    other is that you meant to give the system more information and the
    system has simply been unable to extract the intended type information
    from the term you placed in the conclusion of the new rule.  Given our
    lack of specificity in saying how type information is extracted from
    rules, you can hardly blame yourself for this problem.  Sorry.  If you
    suspect you've been burned this way, you should rephrase the new rule in
    terms of the primitive recognizing expressions above and see if the
    warning is still given.  It would also be helpful to let us see your
    example so we can consider it as we redesign this stuff.
    
    Compound recognizer rules are similar to :forward-chaining rules in
    that the system deduces new information from the act of assuming
    something true or false.  If a compound recognizer rule were stored as
    a forward chaining rule it would have essentially the same effect as
    described, when it has any effect at all.  The important point is that
    :forward-chaining rules, because of their more general and expensive
    form, are used "at the top level" of the simplification process: we
    forward chain from assumptions in the goal being proved.  But compound
    recognizer rules are built in at the bottom-most level of the
    simplifier, where type reasoning is done.
    
    All that said, compound recognizer rules are a rather fancy, specialized
    mechanism.  It may be more appropriate to create :forward-chaining
    rules instead of :compound-recognizer rules.
    
    
    File: acl2-doc-emacs.info,  Node: CONGRUENCE,  Next: DEFINITION,  Prev: COMPOUND-RECOGNIZER,  Up: RULE-CLASSES
    
    CONGRUENCE    the relations to maintain while simplifying arguments
    
    See *note RULE-CLASSES:: for a general discussion of rule classes and
    how they are used to build rules from formulas.  An example :corollary
    formula from which a :congruence rule might be built is:
    
         Example:
         (defthm set-equal-implies-iff-memb-2
           (implies (set-equal x y)
                    (iff (memb e x) (memb e y)))
           :rule-classes :congruence)
    
    Also see *note DEFCONG:: and see *note EQUIVALENCE::.
    
         General Form:
         (implies (equiv1 xk xk-equiv)
                  (equiv2 (fn x1... xk       ...xn)
                          (fn x1... xk-equiv ...xn)))
    
    where equiv1 and equiv2 are known equivalence relations, fn is an n-ary
    function symbol and the xi and xk-equiv are all distinct variables.
    The effect of such a rule is to record that the equiv2-equivalence of
    fn-expressions can be maintained if, while rewriting the kth argument
    position, equiv1-equivalence is maintained.  See *note EQUIVALENCE::
    for a general discussion of the issues.  We say that equiv2, above, is
    the "outside equivalence" in the rule and equiv1 is the "inside
    equivalence for the kth argument"
    
    The macro form (defcong equiv1 equiv2 (fn x1 ... x1) k) is an
    abbreviation for a defthm of rule-class :congruence that attempts to
    establish that equiv2 is maintained by maintaining equiv1 in fn's kth
    argument.  The defcong macro automatically generates the general
    formula shown above.  See *note DEFCONG::.
    
    The memb example above tells us that (memb e x) is propositionally
    equivalent to (memb e y), provided x and y are set-equal.  The outside
    equivalence is iff and the inside equivalence for the second argument
    is set-equal.  If we see a memb expression in a propositional context,
    e.g., as a literal of a clause or test of an if (but not, for example,
    as an argument to cons), we can rewrite its second argument maintaining
    set-equality.  For example, a rule stating the commutativity of append
    (modulo set-equality) could be applied in this context.  Since equality
    is a refinement of all equivalence relations, all equality rules are
    always available.  See *note REFINEMENT::.
    
    All known :congruence rules about a given outside equivalence and fn
    can be used independently.  That is, consider two :congruence rules
    with the same outside equivalence, equiv, and about the same function
    fn.  Suppose one says that equiv1 is the inside equivalence for the
    first argument and the other says equiv2 is the inside equivalence for
    the second argument.  Then (fn a b) is equiv (fn a' b') provided a is
    equiv1 to a' and b is equiv2 to b'.  This is an easy consequence of the
    transitivity of equiv.  It permits you to think independently about the
    inside equivalences.
    
    Furthermore, it is possible that more than one inside equivalence for a
    given argument slot will maintain a given outside equivalence.  For
    example, (length a) is equal to (length a') if a and a' are related
    either by list-equal or by string-equal.  You may prove two (or more)
    :congruence rules for the same slot of a function.  The result is that
    the system uses a new, "generated" equivalence relation for that slot
    with the result that rules of both (or all) kinds are available while
    rewriting.
    
    :Congruence rules can be disabled.  For example, if you have two
    different inside equivalences for a given argument position and you
    find that the :rewrite rules for one are unexpectedly preventing the
    application of the desired rule, you can disable the rule that
    introduced the unwanted inside equivalence.
    
    _Remark on Replacing IFF by EQUAL._ You may encounter a warning
    suggesting that a congruence rule "can be strengthened by replacing the
    second equivalence relation, IFF, by EQUAL."  Suppose for example that
    this warning occurs when you submit the following rule:
    
         (defcong equiv1 iff (fn x y) 2)
    
    which is shorthand for the following:
    
         (defthm equiv1-implies-iff-fn-2
                (implies (equiv1 y y-equiv)
                         (iff (fn x y) (fn x y-equiv)))
                :rule-classes (:congruence))
    
    The warning is telling you that ACL2 was able to deduce that fn always
    returns a Boolean, and hence a trivial but useful consequence is
    obtained by replacing iff by equal --
    
         (defcong equiv1 equal (fn x y) 2)
    
    -- which is shorthand for the following:
    
         (defthm equiv1-implies-equal-fn-2
                (implies (equiv1 y y-equiv)
                         (equal (fn x y) (fn x y-equiv)))
                :rule-classes (:congruence))
    
    If you have difficulty proving the latter directly, you can derive it
    from the former by giving a suitable hint, minimally as follows.
    
         (defcong equiv1 equal (fn x y) 2
           :hints (("Goal"
                    :use equiv1-implies-iff-fn-2
                    :in-theory
                    (union-theories '((:type-prescription fn))
                                    (theory 'minimal-theory)))))
    
    By heeding this warning, you may avoid unnecessary double-rewrite
    warnings later.  We now explain why, but see *note DOUBLE-REWRITE:: for
    relevant background material.
    
    For example, suppose you have proved the "iff" version of the
    congruence rule above, and later you submit the following rewrite rule.
    
         (defthm equal-list-perm
           (implies (equiv1 x y)
                    (fn x y)))
    
    Since fn is known to return a Boolean, ACL2 performs an optimization
    that stores this rule as though it were the following.
    
         (defthm equal-list-perm
           (implies (equiv1 x y)
                    (equal (fn x y) t)))
    
    Thus, if ACL2's rewriter sees a term (fn a b) in a context where the
    equivalence relation iff is not being maintained, then it cannot use
    rule equiv1-implies-iff-fn-2, so it rewrites argument a without the
    benefit of knowing that it suffices to maintain equiv1; and then it
    caches the result.  When ACL2 subsequently attempts to relieve the
    hypothesis (equiv1 x y), it will rewrite x simply by returning the
    rewritten value of a from the result cache.  This is unfortunate if a
    could have been rewritten more completely under maintainance of the
    equivalence relation equiv1 -- which is legal in the hypothesis since a
    is an argument of equiv1, which is an equivalence relation.  The user
    who observes the warning from rule equiv1-implies-iff-fn-2, and
    replaces it with equiv1-implies-equal-fn-2, will avoid this unfortunate
    case.
    
    
    File: acl2-doc-emacs.info,  Node: DEFINITION,  Next: ELIM,  Prev: CONGRUENCE,  Up: RULE-CLASSES
    
    DEFINITION    make a rule that acts like a function definition
    
    See *note RULE-CLASSES:: for a general discussion of rule classes and
    how they are used to build rules from formulas.  An example :corollary
    formula from which a :definition rule might be built is:
    
         Examples:
         (defthm open-len-twice
           (implies (true-listp x)
                    (equal (len x)
                           (if (null x)
                               0
                             (if (null (cdr x))
                                 1
                               (+ 2 (len (cddr x)))))))
           :rule-classes :definition)
    
         ; Same as above, with :controller-alist made explicit:
         (defthm open-len-twice
           (implies (true-listp x)
                    (equal (len x)
                           (if (null x)
                               0
                             (if (null (cdr x))
                                 1
                               (+ 2 (len (cddr x)))))))
           :rule-classes ((:definition :controller-alist ((len t)))))
    
         General Form:
         (implies hyp (equiv (fn a1 ... an) body))
    
    where equiv is an equivalence relation and fn is a function symbol
    other than if, hide, force or case-split.  Such rules allow
    "alternative" definitions of fn to be proved as theorems but used as
    definitions.  These rules are not true "definitions" in the sense that
    they (a) cannot introduce new function symbols and (b) do not have to be
    terminating recursion schemes.  They are just conditional rewrite rules
    that are controlled the same way we control recursive definitions.  We
    call these "definition rules" or "generalized definitions".
    
    Consider the general form above.  Generalized definitions are stored
    among the :rewrite rules for the function "defined," fn above, but the
    procedure for applying them is a little different.  During rewriting,
    instances of (fn a1 ... an) are replaced by corresponding instances of
    body provided the hyps can be established as for a :rewrite rule and
    the result of rewriting body satisfies the criteria for function
    expansion.  There are two primary criteria, either of which permits
    expansion.  The first is that the "recursive" calls of fn in the
    rewritten body have arguments that already occur in the goal
    conjecture.  The second is that the "controlling" arguments to fn are
    simpler in the rewritten body.
    
    The notions of "recursive call" and "controllers" are complicated by the
    provisions for mutually recursive definitions.  Consider a "clique" of
    mutually recursive definitions.  Then a "recursive call" is a call to
    any function defined in the clique and an argument is a "controller" if
    it is involved in the measure that decreases in all recursive calls.
    These notions are precisely defined by the definitional principle and
    do not necessarily make sense in the context of generalized
    definitional equations as implemented here.
    
    But because the heuristics governing the use of generalized definitions
    require these notions, it is generally up to the user to specify which
    calls in body are to be considered recursive and what the controlling
    arguments are.  This information is specified in the :clique and
    :controller-alist fields of the :definition rule class.
    
    The :clique field is the list of function symbols to be considered
    recursive calls of fn.  In the case of a non-recursive definition, the
    :clique field is empty; in a singly recursive definition, it should
    consist of the singleton list containing fn; otherwise it should be a
    list of all of the functions in the mutually recursive clique with this
    definition of fn.
    
    If the :clique field is not provided it defaults to nil if fn does not
    occur as a function symbol in body and it defaults to the singleton
    list containing fn otherwise.  Thus, :clique must be supplied by the
    user only when the generalized definition rule is to be treated as one
    of several in a mutually recursive clique.
    
    The :controller-alist is an alist that maps each function symbol in the
    :clique to a mask specifying which arguments are considered controllers.
    The mask for a given member of the clique, fn, must be a list of t's
    and nil's of length equal to the arity of fn.  A t should be in each
    argument position that is considered a "controller" of the recursion.
    For a function admitted under the principle of definition, an argument
    controls the recursion if it is one of the arguments measured in the
    termination argument for the function.  But in generalized definition
    rules, the user is free to designate any subset of the arguments as
    controllers.  Failure to choose wisely may result in the "infinite
    expansion" of definitional rules but cannot render ACL2 unsound since
    the rule being misused is a theorem.
    
    If the :controller-alist is omitted it can sometimes be defaulted
    automatically by the system.  If the :clique is nil, the
    :controller-alist defaults to nil.  If the :clique is a singleton
    containing fn, the :controller-alist defaults to the controller alist
    computed by (defun fn args body).  (The user can obtain some control
    over this analysis by setting the default ruler-extenders; see *note
    RULER-EXTENDERS::.)  If the :clique contains more than one function,
    the user must supply the :controller-alist specifying the controllers
    for each function in the clique.  This is necessary since the system
    cannot determine and thus cannot analyze the other definitional
    equations to be included in the clique.
    
    For example, suppose fn1 and fn2 have been defined one way and it is
    desired to make "alternative" mutually recursive definitions available
    to the rewriter.  Then one would prove two theorems and store each as a
    :definition rule.  These two theorems would exhibit equations
    "defining" fn1 and fn2 in terms of each other.  No provision is here
    made for exhibiting these two equations as a system of equations.  One
    is proved and then the other.  It just so happens that the user intends
    them to be treated as mutually recursive definitions.  To achieve this
    end, both :definition rules should specify the :clique (fn1 fn2) and
    should specify a suitable :controller-alist.  If, for example, the new
    definition of fn1 is controlled by its first argument and the new
    definition of fn2 is controlled by its second and third (and they each
    take three arguments) then a suitable :controller-alist would be ((fn1
    t nil nil) (fn2 nil t t)).  The order of the pairs in the alist is
    unimportant, but there must be a pair for each function in the clique.
    
    Inappropriate heuristic advice via :clique and :controller-alist can
    cause "infinite expansion" of generalized definitions, but cannot render
    ACL2 unsound.
    
    Note that the actual definition of fn1 has the runic name (:definition
    fn1).  The runic name of the alternative definition is (:definition
    lemma), where lemma is the name given to the event that created the
    generalized :definition rule.  This allows theories to switch between
    various "definitions" of the functions.
    
    By default, a :definition rule establishes the so-called "body" of a
    function.  The body is used by :expand hints, and it is also used
    heuristically by the theorem prover's preprocessing (the initial
    simplification using "simple" rules that is controlled by the
    preprocess symbol in :do-not hints), induction analysis, and the
    determination for when to warn about non-recursive functions in rules.
    The body is also used by some heuristics involving whether a function is
    recursively defined, and by the expand, x, and x-dumb commands of the
    proof-checker.
    
    See *note RULE-CLASSES:: for a discussion of the optional field
    :install-body of :definition rules, which controls whether a
    :definition rule is used as described in the paragraph above.  Note
    that even if :install-body nil is supplied, the rewriter will still
    rewrite with the :definition rule; in that case, ACL2 just won't
    install a new body for the top function symbol of the left-hand side of
    the rule, which for example affects the application of :expand hints as
    described in the preceding paragraph.  Also see *note SET-BODY:: and
    see *note SHOW-BODIES:: for how to change the body of a function symbol.
    
    Note only that if you prove a definition rule for function foo, say,
    foo-new-def, you will need to refer to that definition as foo-new-def
    or as (:DEFINITION foo-new-def).  That is because a :definition rule
    does not change the meaning of the symbol foo for :use hints, nor does
    it change the meaning of the symbol foo in theory expressions; see
    *note THEORIES::, in particular the discussion there of runic
    designators.  Similarly :pe foo and :pf foo will still show the
    original definition of foo.
    
    The definitional principle, defun, actually adds :definition rules.
    Thus the handling of generalized definitions is exactly the same as for
    "real" definitions because no distinction is made in the implementation.
    Suppose (fn x y) is defun'd to be body.  Note that defun (or defuns or
    mutual-recursion) can compute the clique for fn from the syntactic
    presentation and it can compute the controllers from the termination
    analysis.  Provided the definition is admissible, defun adds the
    :definition rule (equal (fn x y) body).
    
    
    File: acl2-doc-emacs.info,  Node: ELIM,  Next: EQUIVALENCE,  Prev: DEFINITION,  Up: RULE-CLASSES
    
    ELIM    make a destructor elimination rule
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
    The following example of an :elim rule is an important one, and is built
    into ACL2.
    
         (defaxiom car-cdr-elim
           (implies (consp x)
                    (equal (cons (car x) (cdr x)) x))
           :rule-classes :elim)
    
    The class of :elim rules is fundamentally quite different from the more
    common class of :rewrite rules.  Briefly put, a :rewrite rule replaces
    instances of its left-hand side with corresponding instances of its
    right-hand side.  But an :elim rule, on the other hand, has the effect
    of generalizing so-called "destructor" function applications to
    variables.  In essence, applicability of a :rewrite rule is based on
    matching its left-hand side, while applicability of an :elim rule is
    based on the presence of at least one destructor term.
    
    For example, a conjecture about (car x) and (cdr x) can be replaced by
    a conjecture about new variables x1 and x2, as shown in the following
    example.  (Run the command :mini-proveall and search for CAR-CDR-ELIM
    to see the full proof containing this excerpt.)
    
         Subgoal *1/1'
         (IMPLIES (AND (CONSP X)
                       (TRUE-LISTP (REV (CDR X))))
                  (TRUE-LISTP (APP (REV (CDR X)) (LIST (CAR X))))).
    
         The destructor terms (CAR X) and (CDR X) can be eliminated by using
         CAR-CDR-ELIM to replace X by (CONS X1 X2), (CAR X) by X1 and (CDR X)
         by X2.  This produces the following goal.
    
         Subgoal *1/1''
         (IMPLIES (AND (CONSP (CONS X1 X2))
                       (TRUE-LISTP (REV X2)))
                  (TRUE-LISTP (APP (REV X2) (LIST X1)))).
    
         This simplifies, using primitive type reasoning, to
    
         Subgoal *1/1'''
         (IMPLIES (TRUE-LISTP (REV X2))
                  (TRUE-LISTP (APP (REV X2) (LIST X1)))).
    
    The resulting conjecture is often simpler and hence more amenable to
    proof.
    
    The application of an :elim rule thus replaces a variable by a term that
    contains applications of so-called "destructor" functions to that
    variable.  The example above is typical: the variable x is replaced by
    the term (cons (car x) (cdr x)), which applies a so-called "constructor"
    function, cons, to applications (car x) and (cdr x) of destructor
    functions car and cdr to that same variable, x.  But that is only part
    of the story.  ACL2 then generalizes the destructor applications (car
    x) and (cdr x) to new variables x1 and x2, respectively, and ultimately
    the result is a simpler conjecture.
    
    More generally, the application of an :elim rule replaces a variable by
    a term containing applications of destructors; there need not be a
    clear-cut notion of "constructor."  But the situation described above
    is typical, and we will focus on it, giving full details when we
    introduce the "General Form" below.
    
    Notice that the situation can be complicated a bit by a rule's
    hypotheses.  For example, the replacement specified by the rule
    car-cdr-elim (shown near the beginning of this discussion) is only
    valid if the variable being replaced is a cons structure.  Thus, when
    ACL2 applies car-cdr-elim to replace a variable v, it will split into
    two cases: one case in which (consp v) is true, in which v is replaced
    by (cons (car v) (cdr v)) and then (car v) and (cdr v) are generalized
    to new variables; and one case in which (consp v) is false.  In
    practice, (consp v) is often provable, perhaps even literally present
    as a hypotheses; then of course there is no need to introduce the second
    case.  That is why there is no such second case in the example above.
    
    You might find :elim rules to be useful whenever you have in mind a data
    type that can be built up from its fields with a "constructor" function
    and whose fields can be accessed by corresponding "destructor"
    functions.  So for example, if you have a "house" data structure that
    represents a house in terms of its address, price, and color, you might
    have a rule like the following.
    
         Example:
         (implies (house-p x)
                  (equal (make-house (address x)
                                     (price x)
                                     (color x))
                         x))
    
    The application of such a rule is entirely analogous to the application
    of the rule car-cdr-elim discussed above.  We discuss such rules and
    their application more carefully below.
    
         General Form:
         (implies hyp (equiv lhs x))
    
    where equiv is a known equivalence relation (see *note DEFEQUIV::); x
    is a variable symbol; and lhs contains one or more terms (called
    "destructor terms") of the form (fn v1 ... vn), where fn is a function
    symbol and the vi are distinct variable symbols, v1, ..., vn include
    all the variable symbols in the formula, no fn occurs in lhs in more
    than one destructor term, and all occurrences of x in lhs are inside
    destructor terms.
    
    To use an :elim rule, the theorem prover waits until a conjecture has
    been maximally simplified.  It then searches for an instance of some
    destructor term (fn v1 ... vn) in the conjecture, where the instance for
    x is some variable symbol, vi, and every occurrence of vi outside the
    destructor terms is in an equiv-hittable position.  If such an instance
    is found, then the theorem prover instantiates the :elim formula as
    indicated by the destructor term matched; splits the conjecture into two
    goals, according to whether the instantiated hypothesis, hyp, holds; and
    in the case that it does hold, generalizes all the instantiated
    destructor terms in the conjecture to new variables and then replaces
    vi in the conjecture by the generalized instantiated lhs.  An
    occurrence of vi is "equiv-hittable" if sufficient congruence rules
    (see *note DEFCONG::) have been proved to establish that the
    propositional value of the clause is not altered by replacing that
    occurrence of vi by some equiv-equivalent term.
    
    If an :elim rule is not applied when you think it should have been, and
    the rule uses an equivalence relation, equiv, other than equal, it is
    most likely that there is an occurrence of the variable that is not
    equiv-hittable.  Easy occurrences to overlook are those in the
    governing hypotheses.  If you see an unjustified occurrence of the
    variable, you must prove the appropriate congruence rule to allow the
    :elim to fire.
    
    Further examples of how ACL2 :elim rules are used may be found in the
    corresponding discussion of "Elimation of Destructors" for Nqthm, in
    Section 10.4 of A Computational Logic Handbook.
    
    
    File: acl2-doc-emacs.info,  Node: EQUIVALENCE,  Next: FORWARD-CHAINING,  Prev: ELIM,  Up: RULE-CLASSES
    
    EQUIVALENCE    mark a relation as an equivalence relation
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example:
         (defthm r-equal-is-an-equivalence ; assumes that r-equal has been defined
           (and (booleanp (r-equal x y))
                (r-equal x x)
                (implies (r-equal x y) (r-equal y x))
                (implies (and (r-equal x y)
                              (r-equal y z))
                         (r-equal x z)))
           :rule-classes :equivalence)
    
    Also see *note DEFEQUIV::.
    
         General Form:
         (and (booleanp (equiv x y))
              (equiv x x)
              (implies (equiv x y) (equiv y x))
              (implies (and (equiv x y)
                            (equiv y z))
                       (equiv x z)))
    
    except that the order of the conjuncts and terms and the choice of
    variable symbols is unimportant.  The effect of such a rule is to
    identify equiv as an equivalence relation.  Note that only Boolean
    2-place function symbols can be treated as equivalence relations.  See
    *note CONGRUENCE:: and see *note REFINEMENT:: for closely related
    concepts.
    
    The macro form (defequiv equiv) is an abbreviation for a defthm of
    rule-class :equivalence that establishes that equiv is an equivalence
    relation.  It generates the formula shown above.  See *note DEFEQUIV::.
    
    When equiv is marked as an equivalence relation, its reflexivity,
    symmetry, and transitivity are built into the system in a deeper way
    than via :rewrite rules.  More importantly, after equiv has been shown
    to be an equivalence relation, lemmas about equiv, e.g.,
    
         (implies hyps (equiv lhs rhs)),
    
    when stored as :rewrite rules, cause the system to rewrite certain
    occurrences of (instances of) lhs to (instances of) rhs.  Roughly
    speaking, an occurrence of lhs in the kth argument of some
    fn-expression, (fn ... lhs' ...), can be rewritten to produce (fn ...
    rhs' ...), provided the system "knows" that the value of fn is
    unaffected by equiv-substitution in the kth argument.  Such knowledge
    is communicated to the system via "congruence lemmas."
    
    For example, suppose that r-equal is known to be an equivalence
    relation.  The :congruence lemma
    
         (implies (r-equal s1 s2)
                  (equal (fn s1 n) (fn s2 n)))
    
    informs the rewriter that, while rewriting the first argument of
    fn-expressions, it is permitted to use r-equal rewrite-rules.  See
    *note CONGRUENCE:: for details about :congruence lemmas.
    Interestingly, congruence lemmas are automatically created when an
    equivalence relation is stored, saying that either of the equivalence
    relation's arguments may be replaced by an equivalent argument.  That
    is, if the equivalence relation is fn, we store congruence rules that
    state the following fact:
    
         (implies (and (fn x1 y1)
                       (fn x2 y2))
                  (iff (fn x1 x2) (fn y1 y2)))
    
    Another aspect of equivalence relations is that of "refinement."  We
    say equiv1 "refines" equiv2 iff (equiv1 x y) implies (equiv2 x y).
    :refinement rules permit you to establish such connections between your
    equivalence relations.  The value of refinements is that if the system
    is trying to rewrite something while maintaining equiv2 it is permitted
    to use as a :rewrite rule any refinement of equiv2.  Thus, if equiv1 is
    a refinement of equiv2 and there are equiv1 rewrite-rules available,
    they can be brought to bear while maintaining equiv2.  See *note
    REFINEMENT::.
    
    The system initially has knowledge of two equivalence relations,
    equality, denoted by the symbol equal, and propositional equivalence,
    denoted by iff.  Equal is known to be a refinement of all equivalence
    relations and to preserve equality across all arguments of all
    functions.
    
    Typically there are five steps involved in introducing and using a new
    equivalence relation, equiv.
    
         (1) Define equiv,
    
         (2) prove the :equivalence lemma about equiv,
    
         (3) prove the :congruence lemmas that show where equiv can be used
         to maintain known relations,
    
         (4) prove the :refinement lemmas that relate equiv to known
         relations other than equal, and
    
         (5) develop the theory of conditional :rewrite rules that drive
         equiv rewriting.
    
    More will be written about this as we develop the techniques.  For now,
    here is an example that shows how to make use of equivalence relations
    in rewriting.
    
    Among the theorems proved below is
    
         (defthm insert-sort-is-id
           (perm (insert-sort x) x))
    
    Here perm is defined as usual with delete and is proved to be an
    equivalence relation and to be a congruence relation for cons and
    member.
    
    Then we prove the lemma
    
         (defthm insert-is-cons
           (perm (insert a x) (cons a x)))
    
    which you must think of as you would (insert a x) = (cons a x).
    
    Now prove (perm (insert-sort x) x).  The base case is trivial.  The
    induction step is
    
            (consp x)
          & (perm (insert-sort (cdr x)) (cdr x))
    
         -> (perm (insert-sort x) x).
    
    Opening insert-sort makes the conclusion be
    
            (perm (insert (car x) (insert-sort (cdr x))) x).
    
    Then apply the induction hypothesis (rewriting (insert-sort (cdr x)) to
    (cdr x)), to make the conclusion be
    
         (perm (insert (car x) (cdr x)) x)
    
    Then apply insert-is-cons to get (perm (cons (car x) (cdr x)) x).  But
    we know that (cons (car x) (cdr x)) is x, so we get (perm x x) which is
    trivial, since perm is an equivalence relation.
    
    Here are the events.
    
         (encapsulate (((lt * *) => *))
           (local (defun lt (x y) (declare (ignore x y)) nil))
           (defthm lt-non-symmetric (implies (lt x y) (not (lt y x)))))
    
         (defun insert (x lst)
           (cond ((atom lst) (list x))
                 ((lt x (car lst)) (cons x lst))
                 (t (cons (car lst) (insert x (cdr lst))))))
    
         (defun insert-sort (lst)
           (cond ((atom lst) nil)
                 (t (insert (car lst) (insert-sort (cdr lst))))))
    
         (defun del (x lst)
           (cond ((atom lst) nil)
                 ((equal x (car lst)) (cdr lst))
                 (t (cons (car lst) (del x (cdr lst))))))
    
         (defun mem (x lst)
           (cond ((atom lst) nil)
                 ((equal x (car lst)) t)
                 (t (mem x (cdr lst)))))
    
         (defun perm (lst1 lst2)
           (cond ((atom lst1) (atom lst2))
                 ((mem (car lst1) lst2)
                  (perm (cdr lst1) (del (car lst1) lst2)))
                 (t nil)))
    
         (defthm perm-reflexive
           (perm x x))
    
         (defthm perm-cons
           (implies (mem a x)
                    (equal (perm x (cons a y))
                           (perm (del a x) y)))
           :hints (("Goal" :induct (perm x y))))
    
         (defthm perm-symmetric
           (implies (perm x y) (perm y x)))
    
         (defthm mem-del
           (implies (mem a (del b x)) (mem a x)))
    
         (defthm perm-mem
           (implies (and (perm x y)
                         (mem a x))
                    (mem a y)))
    
         (defthm mem-del2
           (implies (and (mem a x)
                         (not (equal a b)))
                    (mem a (del b x))))
    
         (defthm comm-del
           (equal (del a (del b x)) (del b (del a x))))
    
         (defthm perm-del
           (implies (perm x y)
                    (perm (del a x) (del a y))))
    
         (defthm perm-transitive
           (implies (and (perm x y) (perm y z)) (perm x z)))
    
         (defequiv perm)
    
         (in-theory (disable perm
                             perm-reflexive
                             perm-symmetric
                             perm-transitive))
    
         (defcong perm perm (cons x y) 2)
    
         (defcong perm iff (mem x y) 2)
    
         (defthm atom-perm
           (implies (not (consp x)) (perm x nil))
           :rule-classes :forward-chaining
           :hints (("Goal" :in-theory (enable perm))))
    
         (defthm insert-is-cons
           (perm (insert a x) (cons a x)))
    
         (defthm insert-sort-is-id
           (perm (insert-sort x) x))
    
         (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y))
    
         (defun rev (x)
           (if (consp x) (app (rev (cdr x)) (list (car x))) nil))
    
         (defcong perm perm (app x y) 2)
    
         (defthm app-cons
           (perm (app a (cons b c)) (cons b (app a c))))
    
         (defthm app-commutes
           (perm (app a b) (app b a)))
    
         (defcong perm perm (app x y) 1
           :hints (("Goal" :induct (app y x))))
    
         (defthm rev-is-id (perm (rev x) x))
    
         (defun == (x y)
           (if (consp x)
               (if (consp y)
                   (and (equal (car x) (car y))
                        (== (cdr x) (cdr y)))
                   nil)
               (not (consp y))))
    
         (defthm ==-reflexive (== x x))
    
         (defthm ==-symmetric (implies (== x y) (== y x)))
    
         (defequiv ==)
    
         (in-theory (disable ==-symmetric ==-reflexive))
    
         (defcong == == (cons x y) 2)
    
         (defcong == iff (consp x) 1)
    
         (defcong == == (app x y) 2)
    
         (defcong == == (app x y) 1)
    
         (defthm rev-rev (== (rev (rev x)) x))
    
    
    File: acl2-doc-emacs.info,  Node: FORWARD-CHAINING,  Next: FREE-VARIABLES,  Prev: EQUIVALENCE,  Up: RULE-CLASSES
    
    FORWARD-CHAINING    make a rule to forward chain when a certain trigger arises
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Examples:
    
         (defthm p-and-r-forward           ; When (p a) appears in a formula to be
          (implies (and (p x) (r x))       ; simplified, try to establish (p a) and
                   (q (f x)))              ; (r a) and, if successful, add (q (f a))
          :rule-classes :forward-chaining) ; to the known assumptions.
    
         (defthm p-and-r-forward           ; as above with most defaults filled in
           (implies (and (p x) (r x))
                    (q (f x)))
           :rule-classes ((:forward-chaining :trigger-terms ((p x))
                                             :corollary (implies (and (p x) (r x))
                                                                 (q (f x)))
                                             :match-free :all)))
    
    To specify the triggering terms provide a non-empty list of terms as
    the value of the :trigger-terms field of the rule class object.
    
         General Form:
         Any theorem, provided an acceptable triggering term exists.
    
    The structure of this documentation is as follows.  First we give a
    brief overview of forward chaining and contrast it to backchaining
    (rewriting).  Then we lay out the syntactic restrictions on
    :forward-chaining rules.  Then we give more details about the process
    and point to a tool to assist you in debugging your :forward-chaining
    rules.
    
    _Overview and When to Use Forward Chaining_
    
    Forward chaining is performed as part of the simplification process:
    before the goal is rewritten a _context_ is established.  The context
    tells the theorem prover what may be assumed during rewriting, in
    particular, to establish hypotheses of rewrite rules.  Forward chaining
    is used to extend the context before rewriting begins.  For example,
    the :forward-chaining rule (implies (p x) (p1 x)) would add (p1 A) to
    the context, where A is some term, if (p A) is already in the context.
    
    Forward chaining and backchaining are duals.  If a rewrite rule
    requires that (p1 A) be established and (p A) is known, it could be
    done either by making (implies (p x) (p1 x)) a :forward-chaining rule
    or a :rewrite rule.  Which should you choose?
    
    As a rule of thumb, if a conclusion like (p1 A) is expected to be
    widely needed, it is better to derive it via forward chaining because
    then it is available "for free" during the rewriting after paying the
    one-time cost of forward chaining.  Alternatively, if (p1 A) is a rather
    special hypothesis of key importance to only a few rewrite rules, it is
    best to derive it only when needed.  Thus forward chaining is pro-active
    and backward chaining (rewriting) is reactive.
    
    _Syntactic Restrictions_
    
    Forward chaining rules are generated from the corollary term (see *note
    RULE-CLASSES::) as follows.  First, every let expression is expanded
    away (hence, so is every let* and lambda expression), as is every call
    of a so-called "guard holder," mv-list or return-last (the latter
    resulting from macroexpansion of calls of prog2$, must-be-equal or
    mbe), ec-call, and a few others), or `the'.  If the resulting term has
    the form (implies hyp concl), then concl is treated as a conjunction,
    with one forward chaining rule with hypothesis hyp created for each
    conjunct.  In the other case, where the corollary term is not an
    implies, we process it as we process the conclusion in the first case.
    
    Note that unlike rewrite rules, a nested implication is not folded into
    a single implication.  Consider for example the following term.
    
         (implies (p1 x)
                  (implies (p2 x)
                           (p3 x)))
    
    Although this term is parsed for a rewrite rule as (implies (and (p1 x)
    (p2 x)) (p3 x)), that is not the case when this term is parsed for a
    forward-chaining rule, in which case (p1 x) is treated as the
    hypothesis and (implies (p2 x) (p3 x)) is treated as the conclusion.
    
    The :trigger-terms field of a :forward-chaining rule class object
    should be a non-empty list of terms, if provided, and should have
    certain properties described below.  If the :trigger-terms field is not
    provided, it defaults to the singleton list containing the "atom" of
    the first hypothesis of the formula.  (The atom of (not x) is x; the
    atom of any other term is the term itself.)  If there are no hypotheses
    and no :trigger-terms were provided, an error is caused.
    
    A triggering term is acceptable if it is not a variable, a quoted
    constant, a lambda application, a let- (or let*-) expression, or a
    not-expression, and every variable symbol in the conclusion of the
    theorem either occurs in the hypotheses or occurs in the trigger.
    
    _More Details about Forward Chaining_
    
    :Forward-chaining rules are used by the simplifier _before_ it begins
    to rewrite the literals of the goal.  (Forward chaining is thus carried
    out from scratch for each goal.)  If any term in the goal is an
    instance of a trigger of some forward chaining rule, we try to
    establish the hypotheses of that forward chaining theorem (from the
    negation of the goal).  To relieve a hypothesis we only use type
    reasoning, evaluation of ground terms, and presence among our known
    assumptions.  We do not use rewriting.  So-called free variables in
    hypotheses are treated specially; see *note FREE-VARIABLES::.  If all
    hypotheses are relieved, and certain heuristics approve of the newly
    derived conclusion, we add the instantiated conclusion to our known
    assumptions.  Since this might introduce new terms into the assumptions,
    forward chaining is repeated.  Heuristic approval of each new addition
    is necessary to avoid infinite looping as would happen with the rule
    (implies (p x) (p (f x))), which might otherwise forward chain from (p
    A) to (p (f A)) to (p (f (f A))), etc.
    
    _Caution_.  Forward chaining does not actually add terms to the goals
    displayed during proof attempts.  Instead, it extends an associated
    _context_, called "assumptions" in the preceding paragraph, that ACL2
    builds from the goal currently being proved.  (For insiders: forward
    chaining extends the type-alist.)  The context starts out with "obvious"
    consequences of the negation of the goal.  For example, if the goal is
    
         (implies (and (p A) (q (f A)))
                  (c A))
    
    then the context notes that (p A) and (q (f A)) are non-nil and (c A)
    is nil.  Forward chaining is then used to expand the context.  For
    example, if a forward chaining rule has (f x) as a trigger term and has
    body (implies (p x) (r (f x))), then the context is extended by binding
    (r (f A)) to non-nil, provided the heuristics approve of this
    extension.  Note however that since (r (f A)) is put into the context,
    not the goal, you will not see it in the goal formula.  Furthermore, the
    assumption added to the context is just the instantiation of the
    conclusion of the rule, with no simplification or rewriting applied.
    Thus, for example, if it contains an enabled non-recursive function
    symbol it is unlikely ever to match a (rewritten) term arising during
    subsequent simplification of the goal.
    
    However, forward-chaining does support the linear arithmetic reasoning
    package.  For example, suppose that forward-chaining puts (< (f x) (g
    x)) into the context.  Then this inequality also goes into the linear
    arithmetic database, together with suitable instances of linear lemmas
    whose trigger term is a call of g.  See *note LINEAR::.
    
    Debugging :forward-chaining rules can be difficult since their effects
    are not directly visible on the goal being simplified.  Tools are
    available to help you discover what forward chaining has occurred see
    *note FORWARD-CHAINING-REPORTS::.
    
    
    File: acl2-doc-emacs.info,  Node: FREE-VARIABLES,  Next: GENERALIZE,  Prev: FORWARD-CHAINING,  Up: RULE-CLASSES
    
    FREE-VARIABLES    free variables in rules
    
    As described elsewhere (see *note RULE-CLASSES::), ACL2 rules are
    treated as implications for which there are zero or more hypotheses hj
    to prove.  In particular, rules of class :rewrite may look like this:
    
         (implies (and h1 ... hn)
                  (fn lhs rhs))
    
    Variables of hi are said to occur _free_ in the above :rewrite rule if
    they do not occur in lhs or in any hj with j *))
          (local (defun op (x y) (< x y)))
          (defthm transitivity-of-op
            (implies (and (op x y) (op y z)) (op x z))
            :rule-classes :forward-chaining))
    
         ; The following theorem is proved by forward chaining, using the above rule.
    
         (thm
          (implies (and (op u v) (op v w) (op v a))
                   (op u w)))
    
         ; The proof of the theorem just above succeeds because the term (op u v)
         ; triggers the application of forward-chaining rule transitivity-of-op,
         ; binding x to u and y to v.  Free variable z of that rule is bound to both w
         ; and to a, resulting in the addition of both (op u w) and (op u a) to the
         ; context.  However, (op v a) happens to be at the front of the context, so
         ; if only one free-variable binding had been allowed, then z would have only
         ; been bound to a, not to w, as we now illustrate.
    
         (add-match-free-override :once (:forward-chaining transitivity-of-op))
    
         (thm ; FAILS!
          (implies (and (op u v) (op v w) (op v a))
                   (op u w)))
    
         :ubt! 1
    
         ; Starting over, this time we prove transitivity-of-op as a :match-free :once
         ; forward-chaining rule.  Note that the presence of :match-free eliminates
         ; the free-variables warning that we got the first time.
    
         (encapsulate
          (((op * *) => *))
          (local (defun op (x y) (< x y)))
          (defthm transitivity-of-op
            (implies (and (op x y) (op y z)) (op x z))
            :rule-classes ((:forward-chaining :match-free :once))))
    
         (thm ; FAILS!
          (implies (and (op u v) (op v w) (op v a))
                   (op u w)))
    
         ; Notice that if we swap the order of the last two hypotheses the theorem
         ; goes through, because this time (op v w) is first in the context.
    
         (thm ; SUCCEEDS!
          (implies (and (op u v) (op v a) (op v w))
                   (op u w)))
    
         :u
    
         ; Now let's try setting the default to :once.
    
         (set-match-free-default :once)
    
         ; We still get a free-variables warning when we admit this forward-chaining rule.
    
         (encapsulate
          (((op * *) => *))
          (local (defun op (x y) (< x y)))
          (defthm transitivity-of-op
            (implies (and (op x y) (op y z)) (op x z))
            :rule-classes ((:forward-chaining))))
    
         ; This theorem fails--as it should.
    
         (thm ; FAILS!
          (implies (and (op u v) (op v w) (op v a))
                   (op u w)))
    
         ; But if we convert this rule (or here, all possible rules) to :all rules,
         ; then the proof succeeds.
    
         (add-match-free-override :all t)
    
         (thm ; SUCCEEDS!
          (implies (and (op u v) (op v w) (op v a))
                   (op u w)))
    
         ; Now let's test a relatively slow :all case (the next thm below).
    
         :ubt! 1
    
         (encapsulate
          (((op1 *) => *)
           ((op3 * * *) => *))
          (local (defun op1 (x) (declare (ignore x)) t))
          (local (defun op3 (x0 x1 x2)
                   (declare (ignore x0 x1 x2))
                   t))
          (defthm op1-op3-property
            (implies (and (op1 x0) (op1 x1) (op1 x2))
                     (op3 x0 x1 x2))
            :rule-classes ((:forward-chaining :match-free :all))))
    
         ; The following succeeds, but takes a little time (about a second in one run).
    
         (thm (implies
               (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5)
                    (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11)
                    (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16)
                    (op1 a17) (op1 a18) (op1 a19) (op1 a20))
               (op3 a5 a6 a0)))
    
         (add-match-free-override :once t)
    
         ; The same theorem now fails because of the add-match-free-override, but is
         ; more than an order of magnitude faster.
    
         (thm (implies
               (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5)
                    (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11)
                    (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16)
                    (op1 a17) (op1 a18) (op1 a19) (op1 a20))
               (op3 a5 a6 a0)))
    
         ; A slight variant succeeds in a negligible amount of time (still with the
         ; :once override above).
    
         (thm (implies
               (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5)
                    (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11)
                    (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16)
                    (op1 a17) (op1 a18) (op1 a19) (op1 a20))
               (op3 a5 a20 a20)))
    
         ; Reality check: This shouldn't give a free-variables warning, and everything
         ; should work great since there are no free variables with this trigger term.
    
         :ubt! 1
    
         (encapsulate
          (((op1 *) => *)
           ((op7 * * * * * * *) => *))
          (local (defun op1 (x)
                   (declare (ignore x))
                   t))
          (local (defun op7 (x0 x1 x2 x3 x4 x5 x6)
                   (declare (ignore x0 x1 x2 x3 x4 x5 x6))
                   t))
          (defthm op1-op7-property
            (implies (and (op1 x0) (op1 x1) (op1 x2)
                          (op1 x3) (op1 x4) (op1 x5) (op1 x6))
                     (op7 x0 x1 x2 x3 x4 x5 x6))
            :rule-classes ((:forward-chaining
                            :trigger-terms ((op7 x0 x1 x2 x3 x4 x5 x6))))))
    
         ; The following then succeeds, and very quickly.
    
         (thm (implies (and (op1 a0) (op1 a1) (op1 a2)
                            (op1 a3) (op1 a4) (op1 a5) (op1 a6))
                       (op7 a4 a6 a5 a6 a6 a6 a6)))
    
    
    File: acl2-doc-emacs.info,  Node: FREE-VARIABLES-EXAMPLES-REWRITE,  Prev: FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING,  Up: FREE-VARIABLES-EXAMPLES
    
    FREE-VARIABLES-EXAMPLES-REWRITE    examples pertaining to free variables in rewrite rules
    
    The following examples illustrate ACL2's handling of free variables in
    rewrite rules, as well as user control over how such free variables are
    handled.  See *note FREE-VARIABLES:: for a background discussion.
    
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         ;;; Example 1
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
         (defstub p2 (x y) t) ; introduce unconstrained function
    
         ; Get warning because of free variable.  This would be an error if you had
         ; first executed (set-match-free-error t) in order to force yourself to
         ; specify :match-free (illustrated later, below).
         (defaxiom p2-trans
           (implies (and (p2 x y)
                         (p2 y z))
                    (p2 x z)))
    
         ; Succeeds.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         ; The following causes an error because p2-trans is not a rune.
         (add-match-free-override :once p2-trans)
    
         ; After the following, the rewrite rule p2-trans will only allow one
         ; attempt per hypothesis to bind free variables.
         (add-match-free-override :once (:rewrite p2-trans))
    
         ; Now this same theorem fails to be proved.  Here's why.  The
         ; context for proving (p2 a d) happens to include the hypotheses in
         ; reverse order.  So when the first hypothesis of p2-trans, namely
         ; (p2 x y), is relieved, where x is bound to a (as we are attempting
         ; to rewrite the current literal (p2 a d)), we find (p2 a b) in the
         ; context before (p2 a c) and hence y is bound to b.  The
         ; instantiated second hypothesis of p2-trans is thus (p2 b d), and
         ; the proof fails.  Before the add-match-free-override form above,
         ; the proof succeeded because the rewriter was allowed to backtrack
         ; and find the other binding for the first hypothesis of p2-trans,
         ; namely, y bound to c.  Then the instantiated second hypothesis of
         ; p2-trans is (p2 c d), which is known to be true in the current
         ; context.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         ; Return to original behavior for binding free variables.
         (add-match-free-override :all t)
    
         ; Succeeds once again.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         (u) ; undo (add-match-free-override :all t)
    
         ; This is an error, since no further arguments should appear after
         ; :clear.
         (add-match-free-override :clear t)
    
         ; Return all rules to original behavior for binding free variables,
         ; regardless of which previous add-match-free-override forms have
         ; been executed.
         (add-match-free-override :clear)
    
         ; This succeeds just as it did originally.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         (ubt! 'p2-trans) ; back to the start, except retain the defstub
    
         ; Require that :match-free be specified for :linear and :rewrite rules with
         ; free variables.
         (set-match-free-error t)
    
         ; Fails because :match-free is missing.
         (defaxiom p2-trans
           (implies (and (p2 x y)
                         (p2 y z))
                    (p2 x z)))
    
         ; Fails because :match-free must be followed by :once or :all.
         (defaxiom p2-trans
           (implies (and (p2 x y)
                         (p2 y z))
                    (p2 x z))
           :rule-classes ((:rewrite :match-free nil)))
    
         ; Succeeds, this time with no warning at all.
         (defaxiom p2-trans
           (implies (and (p2 x y)
                         (p2 y z))
                    (p2 x z))
           :rule-classes ((:rewrite :match-free :once)))
    
         ; Fails because we only bind once (see earlier long comment).
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         ; Treat p2-trans as though `:match-free :all' had been specified.
         (add-match-free-override :all (:rewrite p2-trans))
    
         ; Succeeds since more than one binding is allowed for p2-trans.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         (u)
         (u)
    
         ; Specify that future :linear and :rewrite rules with free variables
         ; that do not have :match-free specified are treated as though
         ; `:match-free :once' were specified.
         (set-match-free-default :once)
    
         ; Succeeds without error since `:match-free' is specified, as described
         ; above.  But there is a warning, since :match-free is not specified for this
         ; :rewrite rule.
         (defaxiom p2-trans
           (implies (and (p2 x y)
                         (p2 y z))
                    (p2 x z)))
    
         ; Fails since only single bindings are allowed for p2-trans.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         ; Treat p2-trans as though `:match-free :all' had been specified.
         (add-match-free-override :all t)
    
         ; Succeeds.
         (thm (implies (and (p2 a c)
                            (p2 a b)
                            (p2 c d))
                       (p2 a d)))
    
         ; Test searching of ground units, i.e. rewrite rules without variables on the
         ; left side of the conclusion, for use in relieving hypotheses with free
         ; variables.  This is a very contrived example.
    
         (ubt! 1) ; back to the start
    
         (encapsulate
          (((p1 *) => *)
           ((p2 * *) => *)
           ((p3 *) => *)
           ((a) => *)
           ((b) => *))
          (local (defun p1 (x) x))
          (local (defun p2 (x y) (list x y)))
          (local (defun p3 (x) x))
          (local (defun a () 0))
          (local (defun b () 0)))
    
         ; Allow default of :match-free :all (form may be omitted).
         (set-match-free-error nil)
    
         (defaxiom ax1
           (implies (and (p2 x y)
                         (p1 y))
                    (p3 x)))
    
         (defaxiom p2-a-b
           (p2 (a) (b)))
    
         (defaxiom p2-a-a
           (p2 (a) (a)))
    
         (defaxiom p1-b
           (p1 (b)))
    
         ; Succeeds; see long comment below on next attempt to prove this
         ; theorem.
         (thm (implies (p2 (a) y)
                       (p3 (a))))
    
         ; Now ax1 will only relieve hypothesis (p2 x y) for one binding of y:
         (add-match-free-override :once t)
    
         ; Fails when ax1 attempts to rewrite the conclusion to true, because
         ; the most recent ground unit for hypothesis (p2 x y) with x bound
         ; to (a) is rule p2-a-a, which binds y to (a).  If more than one ground
         ; unit could be used then we would backtrack and apply rule p2-a-b,
         ; which binds y to (b) and hence hypothesis (p1 y) of ax1 is
         ; relieved by rule p1-b.
         (thm (implies (p2 (a) y)
                       (p3 (a))))
    
         ; Return rules to original :match-free behavior.
         (add-match-free-override :clear)
    
         ; Succeeds once again.
         (thm (implies (p2 (a) y)
                       (p3 (a))))
    
         ; Just for kicks, change the behavior of a built-in rule irrelevant
         ; to the proof at hand.
         (add-match-free-override :once (:rewrite string<-l-trichotomy))
    
         ; Still succeeds.
         (thm (implies (p2 (a) y)
                       (p3 (a))))
    
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         ;;; Example 2
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    The next example illustrates the use of the break-rewrite facility to
    get information about handling of free variables by the rewriter.
    Explanation is given after this (edited) transcript.  Input begins on
    lines with a prompt (search for "ACL2"); the rest is output.
    
         ACL2 !>(encapsulate
                 ((p1 (u x) t)
                  (bad (x) t)
                  (p2 (x y z) t)
                  (bar (x y) t)
                  (foo (x y) t)
                  (poo (x y) t)
                  (prop (u) t))
    
                 (local (defun p1 (u x) (declare (ignore u x)) nil))
                 (local (defun bad (x) (declare (ignore x)) nil))
                 (local (defun p2 (x y z) (declare (ignore x y z)) nil))
                 (local (defun bar (x y) (declare (ignore x y)) nil))
                 (local (defun foo (x y) (declare (ignore x y)) nil))
                 (local (defun poo (x y) (declare (ignore x y)) nil))
                 (local (defun prop (u) (declare (ignore u)) t))
    
                 (defthm foo-poo
                   (implies (syntaxp (equal y 'y3))
                            (equal (foo x y)
                                   (poo x y))))
    
                 (defthm lemma-1
                   (implies (and (p1 u x)
                                 (bad x)
                                 (p2 x y z)
                                 (bar x y)
                                 (equal x x) ; admittedly silly!
                                 (foo x y))
                            (prop u))
                   :rule-classes ((:rewrite :match-free :all))))
    
         ; [[ output omitted ]]
    
         Summary
         Form:  ( ENCAPSULATE ((P1 ...) ...) ...)
         Rules: NIL
         Warnings:  Subsume and Non-rec
         Time:  0.08 seconds (prove: 0.00, print: 0.01, other: 0.06)
          T
         ACL2 !>:brr t
         The monitored runes are:
         NIL
          T
         ACL2 !>:monitor (:rewrite lemma-1) t
         (((:REWRITE LEMMA-1) 'T))
         ACL2 !>(thm (implies (and (p1 u0 x1)
                                   (bad x1)
                                   (bad x3)
                                   (bar x3 y1)
                                   (bar x3 y3)
                                   (p1 u0 x2)
                                   (p1 u0 x3)
                                   (p2 x3 y1 z1)
                                   (p2 x3 y3 z1))
                              (prop u0)))
    
         (1 Breaking (:REWRITE LEMMA-1) on (PROP U0):
         1 ACL2 >:eval
    
         1x (:REWRITE LEMMA-1) failed because :HYP 1 contains free variables.
         The following display summarizes the attempts to relieve hypotheses
         by binding free variables; see :DOC free-variables.
    
             [1] X : X1
         Failed because :HYP 3 contains free variables Y and Z, for which no
         suitable bindings were found.
             [1] X : X2
         Failed because :HYP 2 rewrote to (BAD X2).
             [1] X : X3
                 [3] Z : Z1
                     Y : Y1
         Failed because :HYP 6 rewrote to (FOO X3 Y1).
                 [3] Z : Z1
                     Y : Y3
         Failed because :HYP 6 rewrote to (POO X3 Y3).
    
         1 ACL2 >:unify-subst
              U : U0
         1 ACL2 >
    
    The :eval command above asks the rewriter to attempt to apply the
    rewrite rule lemma-1 to the term (prop u0), shown just above the line
    with :eval.  As we can see at the end, the variable u in the conclusion
    of lemma-1 is being bound to the variable u0 in the conjecture.  The
    first hypothesis of lemma-1 is (p1 u x), so the rewriter looks for some
    x for which (p1 u0 x) is known to be true.  It finds x1, and then goes
    on to consider the second hypothesis, (bad x).  Since the theorem we
    are proving has (bad x1) in the hypothesis and x is currently bound to
    x1, the rewriter is satisfied and moves on to the third hypothesis of
    lemma-1, (p2 x y z).  However, x is bound to x1 and there are no
    instances of y and z for which (p2 x1 y z) is known in the current
    context.  All of the above analysis is summarized in the first part of
    the output from :eval above:
    
             [1] X : X1
         Failed because :HYP 3 contains free variables Y and Z, for which no
         suitable bindings were found.
    
    Thus, the binding of x to x1 on behalf of the first hypothesis has
    failed.
    
    The rewriter now backs up to look for other values of x that satisfy the
    first hypothesis, and finds x2 because our current theorem has a
    hypothesis of (p1 u0 x2).  But this time, the second hypothesis of
    lemma-1, (bad x), is not known to be true for x; that is, (bad x2) does
    not rewrite to t; in fact, it rewrites to itself.  That explains the
    next part of the output from :eval above:
    
             [1] X : X2
         Failed because :HYP 2 rewrote to (BAD X2).
    
    The rewriter now backs up again to look for other values of x that
    satisfy the first hypothesis, and finds x3 because our current theorem
    has a hypothesis of (p1 u0 x3).  This time, the second hypothesis of
    lemma-1 is not a problem, and moreover, the rewriter is able to bind y
    and z to y1 and z1, respectively, in order to satisfy the third
    hypothesis, (p2 x y z): that is, (p2 x2 y1 z1) is known in the current
    context.  That explains more of the above output from :eval:
    
             [1] X : X3
                 [3] Z : Z1
                     Y : Y1
    
    Unfortunately, the sixth hypothesis, (foo x y), rewrites to itself
    under the above bindings:
    
         Failed because :HYP 6 rewrote to (FOO X3 Y1).
    
    So the rewriter looks for other bindings to satisfy the third
    hypothesis and finds these.
    
                 [3] Z : Z1
                     Y : Y3
    
    This time, the sixth hypothesis can be rewritten under the above
    bindings, from (foo x3 y3) to (poo x3 y3) by lemma foo-poo, but still
    not to t.
    
         Failed because :HYP 6 rewrote to (POO X3 Y3).
    
    There are no more free variable bindings to try, so this concludes the
    output from :eval.
    
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
         ;;; Example 3
         ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    The next pair of examples illustrates so-called "binding hypotheses"
    (see *note FREE-VARIABLES::) and explores some of their subtleties.
    The first shows binding hypotheses in action on a simple example.  The
    second shows how binding hypotheses interact with equivalence relations
    and explains the role of double-rewrite.
    
    Our first example sets up a theory with two user-supplied rewrite
    rules, one of which has a binding hypothesis.  Below we explain how
    that binding hypothesis contributes to the proof.
    
         ; Define some unary functions.
         (defun f (x) (declare (ignore x)) t)
         (defun g (x) x)
         (defun h (x) x)
         (defun k (x) x)
    
         ; Prove some simple lemmas.  Note the binding hypothesis in g-rewrite.
         (defthm f-k-h
           (f (k (h x))))
         (defthm g-rewrite
                (implies (and (equal y (k x)) ; binding hypothesis
                              (f y))
                         (equal (g x) y)))
    
         ; Restrict to a theory that includes the above lemmas but avoids the above
         ; definitions.
         (in-theory (union-theories (theory 'minimal-theory)
                                    '(f-k-h g-rewrite)))
    
         ; Prove a theorem.
         (thm (equal (g (h a)) (k (h a))))
    
    Let us look at how ACL2 uses the above binding hypothesis in the proof
    of the preceding thm form.  The rewriter considers the term (g (h a))
    and finds a match with the left-hand side of the rule g-rewrite, binding
    x to (h a).  The first hypothesis binds y to the result of rewriting (k
    x) in the current context, where the variable x is bound to the term (h
    a); thus y is bound to (k (h a)).  The second hypothesis, (f y), is
    then rewritten under this binding, and the result is t by application
    of the rewrite rule f-k-h.  The rule g-rewrite is then applied under
    the already-mentioned binding of x to (h a).  This rule application
    triggers a recursive rewrite of the right-hand side of g-rewrite, which
    is y, in a context where y is bound (as discussed above) to (k (h a)).
    The result of this rewrite is that same term, (k (h a)).  The original
    call of equal then trivially rewrites to t.
    
    We move on now to our second example, which is similar but involves a
    user-defined equivalence relation.  You may find it helpful to review
    :equivalence rules; see *note EQUIVALENCE::.
    
    Recall that when a hypothesis is a call of an equivalence relation
    other than equal, the second argument must be a call of double-rewrite
    in order for the hypothesis to be treated as a binding hypothesis.
    That is indeed the case below; an explanation follows.
    
         ; Define an equivalence relation.
         (defun my-equiv (x y) (equal x y))
         (defequiv my-equiv) ; introduces rule MY-EQUIV-IS-AN-EQUIVALENCE
    
         ; Define some unary functions
         (defun f (x) (declare (ignore x)) t)
         (defun g (x) x)
         (defun h1 (x) x)
         (defun h2 (x) x)
    
         ; Prove some simple lemmas.  Note the binding hypothesis in lemma-3.
         (defthm lemma-1
           (my-equiv (h1 x) (h2 x)))
         (defthm lemma-2
           (f (h2 x)))
         (defthm lemma-3
                (implies (and (my-equiv y (double-rewrite x)) ; binding hypothesis
                              (f y))
                         (equal (g x) y)))
    
         ; Restrict to a theory that includes the above lemmas but avoids the above
         ; definitions.
         (in-theory (union-theories (theory 'minimal-theory)
                                    '(lemma-1 lemma-2 lemma-3
                                              my-equiv-is-an-equivalence)))
    
         ; Prove a theorem.
         (thm (equal (g (h1 a)) (h2 a)))
    
    The proof succeeds much as in the first example, but the following
    observation is key: when ACL2 binds y upon considering the first
    hypothesis of lemma-3, it rewrites the term (double-rewrite x) in a
    context where it need only preserve the equivalence relation my-equiv.
    At this point, x is bound by applying lemma-3 to the term (g (h1 a));
    so, x is bound to (h1 a).  The rule lemma-1 then applies to rewrite
    this occurrence of x to (h2 a), but only because it suffices to
    preserve my-equiv.  Thus y is ultimately bound to (h2 a), and the proof
    succeeds as one would expect.
    
    If we tweak the above example slightly by disabling the user's
    equivalence rune, then the proof of the thm form fails because the
    above rewrite of (double-rewrite x) is done in a context where it no
    longer suffices to preserve my-equiv as we dive into the second
    argument of my-equiv in the first hypothesis of lemma-3; so, lemma-1
    does not apply this time.
    
         (in-theory (union-theories (theory 'minimal-theory)
                                    '(lemma-1 lemma-2 lemma-3)))
    
         ; Proof fails in this case!
         (thm (equal (g (h1 a)) (h2 a)))
    
    
    File: acl2-doc-emacs.info,  Node: FREE-VARIABLES-TYPE-PRESCRIPTION,  Prev: FREE-VARIABLES-EXAMPLES,  Up: FREE-VARIABLES
    
    FREE-VARIABLES-TYPE-PRESCRIPTION    matching for free variable in type-prescription rules
    
    We assume familiarity with the issue of dealing with free variables in
    hypotheses; see *note FREE-VARIABLES::.
    
    By default, starting with Version  4.3, ACL2 attempts all possible
    matches for free variables.  Consider the following example.
    
         (defstub f1 (x) t)
         (defstub f2 (x y) t)
         (defstub f3 (y) t)
    
         (defaxiom f1-prop
           (implies (and (f2 x y) ; <-- y is free in this hypothesis
                         (f3 y))
                    (f1 x))       ; <-- (f1 x) is the type-term (type is `non-nil')
           :rule-classes :type-prescription)
    
         ; Succeeds:
         (thm (implies (and (f2 a b)
                            (f3 b))
                       (f1 a)))
    
         ; The following fails unless we try more than one match for free variables in
         ; hypotheses.
         (thm (implies (and (f2 a b)
                            (f2 a c)
                            (f2 a d)
                            (f3 b))
                       (f1 a)))
    
    There may be times when you want to match only the first free variable.
    In that case, you can write a function of two arguments, the
    type-prescription rune being applied and the current ACL2 world, that
    prohibits multiple matching for those times.  Your function is then
    `attached' to the built-in constrained function, oncep-ts.  The
    following examples are intended to explain how this works.
    
    First, let us disallow all mutliple matching of free variables (i.e.,
    implement the behavior often referred to as ":match-free :once"; see
    *note FREE-VARIABLES::).
    
         (defun oncep-tp-always (rune wrld)
           (declare (ignore rune wrld)
                    (xargs :mode :logic :guard t))
           t)
    
         (defattach oncep-tp oncep-tp-always)
    
    The second thm form above will now fail, because only one free-variable
    match is permitted for the first hypothesis of rule f1-prop above.
    
    Now suppose that instead, we want to disallow multiple matches for free
    variables in hypotheses of type-prescription rules _except_ for the
    rule f1-prop above.  With the following events, the second thm form
    above once again succeeds.
    
         (defun oncep-tp-always-except-f1-prop (rune wrld)
           (declare (ignore wrld)
                    (xargs :mode :logic :guard (and (consp rune)
                                                    (consp (cdr rune))
                                                    (symbolp (cadr rune)))))
           (not (eq (base-symbol rune) 'f1-prop)))
    
         (defattach oncep-tp oncep-tp-always-except-f1-prop)
    
    In general, your defattach event will attach a function symbol to
    oncep-tp.  The guard of that function symbol must be implied by the
    tuard of oncep-tp:
    
         ACL2 !>:args oncep-tp
    
         Function         ONCEP-TP
         Formals:         (RUNE WRLD)
         Signature:       (ONCEP-TP * *)
                          => *
         Guard:           (AND (PLIST-WORLDP WRLD)
                               (CONSP RUNE)
                               (CONSP (CDR RUNE))
                               (SYMBOLP (CADR RUNE)))
         Guards Verified: T
         Defun-Mode:      :logic
         Type:            built-in (or unrestricted)
    
          ONCEP-TP
         ACL2 !>
    
    
    File: acl2-doc-emacs.info,  Node: GENERALIZE,  Next: INDUCTION,  Prev: FREE-VARIABLES,  Up: RULE-CLASSES
    
    GENERALIZE    make a rule to restrict generalizations
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example:
         (defthm integer-listp-rev
           (implies (integer-listp x)
                    (integer-listp (rev x)))
           :rule-classes :generalize)
    
         General Form:
         any theorem
    
    To use a :generalize rule, the system waits until it has decided to
    generalize some term, term, by replacing it with some new variable v.
    If any :generalize formula can be instantiated so that some non-variable
    subterm becomes term, then that instance of the formula is added as a
    hypothesis.  Thus for the example above, if the term (rev x2) is
    generalized to the variable rv during a proof, then the following is
    added as a hypothesis when generalizing to a new goal.
    
         (implies (integer-listp x2)
                  (integer-listp rv))
    
    At the moment, the best description of how ACL2 :generalize rules are
    used may be found in the discussion of "Generalize Rules," page 248 of
    A Computational Logic Handbook, or "Generalization," page 132 of
    "Computer-Aided Reasoning: An Approach."  Also see *note
    INTRODUCTION-TO-THE-THEOREM-PROVER:: for detailed tutorial on using ACL2
    to prove theorems, which includes some discussion of generalization.
    
    
    File: acl2-doc-emacs.info,  Node: INDUCTION,  Next: LINEAR,  Prev: GENERALIZE,  Up: RULE-CLASSES
    
    INDUCTION    make a rule that suggests a certain induction
    
         Example:
         (defthm recursion-by-sub2-induction-rule
           t
           :rule-classes ((:induction :pattern (* 1/2 i)
                                      :condition (and (integerp i) (>= i 0))
                                      :scheme (recursion-by-sub2 i))))
    
    In ACL2, as in Nqthm, the functions in a conjecture "suggest" the
    inductions considered by the system.  Because every recursive function
    must be admitted with a justification in terms of a measure that
    decreases in a well-founded way on a given set of "controlling"
    arguments, every recursive function suggests a dual induction scheme
    that "unwinds" the function from a given application.
    
    For example, since append (actually binary-append, but we'll ignore the
    distinction here) decomposes its first argument by successive cdrs as
    long as it is a non-nil true list, the induction scheme suggested by
    (append x y) has a base case supposing x to be either not a true list
    or to be nil and then has an induction step in which the induction
    hypothesis is obtained by replacing x by (cdr x).  This substitution
    decreases the same measure used to justify the definition of append.
    Observe that an induction scheme is suggested by a recursive function
    application only if the controlling actuals are distinct variables, a
    condition that is sufficient to ensure that the "substitution" used to
    create the induction hypothesis is indeed a substitution and that it
    drives down a certain measure.  In particular, (append (foo x) y) does
    not suggest an induction unwinding append because the induction scheme
    suggested by (append x y) requires that we substitute (cdr x) for x and
    we cannot do that if x is not a variable symbol.
    
    Once ACL2 has collected together all the suggested induction schemes it
    massages them in various ways, combining some to simultaneously unwind
    certain cliques of functions and vetoing others because they "flaw"
    others.  We do not further discuss the induction heuristics here; the
    interested reader should see Chapter XIV of A Computational Logic
    (Boyer and Moore, Academic Press, 1979) which represents a fairly
    complete description of the induction heuristics of ACL2.
    
    However, unlike Nqthm, ACL2 provides a means by which the user can
    elaborate the rules under which function applications suggest induction
    schemes.  Such rules are called :induction rules.  The definitional
    principle automatically creates an :induction rule, named (:induction
    fn), for each admitted recursive function, fn.  It is this rule that
    links applications of fn to the induction scheme it suggests.  Disabling
    (:induction fn) will prevent fn from suggesting the induction scheme
    derived from its recursive definition.  It is possible for the user to
    create additional :induction rules by using the :induction rule class in
    defthm.
    
    Technically we are "overloading" defthm by using it in the creation of
    :induction rules because no theorem need be proved to set up the
    heuristic link represented by an :induction rule.  However, since
    defthm is generally used to create rules and rule-class objects are
    generally used to specify the exact form of each rule, we maintain that
    convention and introduce the notion of an :induction rule.  An
    :induction rule can be created from any lemma whatsoever.
    
         General Form of an :induction Lemma or Corollary:
         T
    
         General Form of an :induction rule-class:
         (:induction :pattern pat-term
                     :condition cond-term
                     :scheme scheme-term)
    
    where pat-term, cond-term, and scheme-term are all terms, pat-term is
    the application of a function symbol, fn, scheme-term is the
    application of a function symbol, rec-fn, that suggests an induction,
    and, finally, every free variable of cond-term and scheme-term is a
    free variable of pat-term.  We actually check that rec-fn is either
    recursively defined -- so that it suggests the induction that is
    intrinsic to its recursion -- or else that another :induction rule has
    been proved linking a call of rec-fn as the :pattern to some scheme.
    
    The induction rule created is used as follows.  When an instance of the
    :pattern term occurs in a conjecture to be proved by induction and the
    corresponding instance of the :condition term is known to be non-nil
    (by type reasoning alone), the corresponding instance of the :scheme
    term is created and the rule "suggests" the induction, if any,
    suggested by that term.  (Analysis of that term may further involve
    induction rules, though the applied rule is removed from consideration
    during that further analysis, in order to avoid looping.)  If rec-fn is
    recursive, then the suggestion is the one that unwinds that recursion.
    
    Consider, for example, the example given above,
    
         (:induction :pattern (* 1/2 i)
                     :condition (and (integerp i) (>= i 0))
                     :scheme (recursion-by-sub2 i)).
    
    In this example, we imagine that recursion-by-sub2 is the function:
    
         (defun recursion-by-sub2 (i)
           (if (and (integerp i)
                    (< 1 i))
               (recursion-by-sub2 (- i 2))
               t))
    
    Observe that this function recursively decomposes its integer argument
    by subtracting 2 from it repeatedly and stops when the argument is 1 or
    less.  The value of the function is irrelevant; it is its induction
    scheme that concerns us.  The induction scheme suggested by
    (recursion-by-sub2 i) is
    
         (and (implies (not (and (integerp i) (< 1 i)))   ; base case
                       (:p i))
              (implies (and (and (integerp i) (< 1 i))    ; induction step
                            (:p (- i 2)))
                       (:p i)))
    
    We can think of the base case as covering two situations.  The first is
    when i is not an integer.  The second is when the integer i is 0 or 1.
    In the base case we must prove (:p i) without further help.  The
    induction step deals with those integer i greater than 1, and
    inductively assumes the conjecture for i-2 while proving it for i.  Let
    us call this scheme "induction on i by twos."
    
    Suppose the above :induction rule has been added.  Then an occurrence
    of, say, (* 1/2 k) in a conjecture to be proved by induction would
    suggest, via this rule, an induction on k by twos, provided k was known
    to be a nonnegative integer.  This is because the induction rule's
    :pattern is matched in the conjecture, its :condition is satisfied, and
    the :scheme suggested by the rule is that derived from
    (recursion-by-sub2 k), which is induction on k by twos.  Similarly, the
    term (* 1/2 (length l)) would suggest no induction via this rule, even
    though the rule "fires" because it creates the :scheme
    (recursion-by-sub2 (length l)) which suggests no inductions unwinding
    recursion-by-sub2 (since the controlling argument of recursion-by-sub2
    in this :scheme is not a variable symbol).
    
    Continuing this example one step further illustrates the utility of
    :induction rules.  We could define the function recursion-by-cddr that
    suggests the induction scheme decomposing its consp argument two cdrs
    at a time.  We could then add the :induction rule linking (* 1/2
    (length x)) to (recursion-by-cddr x) and arrange for (* 1/2 (length l))
    to suggest induction on l by cddr.
    
    Observe that :induction rules require no proofs to be done.  Such a rule
    is merely a heuristic link between the :pattern term, which may occur in
    conjectures to be proved by induction, and the :scheme term, from which
    an induction scheme may be derived.  Hence, when an :induction
    rule-class is specified in a defthm event, the theorem proved is
    irrelevant.  The easiest theorem to prove is, of course, t.  Thus, we
    suggest that when an :induction rule is to be created, the following
    form be used:
    
         (defthm name T
           :rule-classes ((:induction :pattern pat-term
                                      :condition cond-term
                                      :scheme scheme-term)))
    
    The name of the rule created is (:induction name).  When that rune is
    disabled the heuristic link between pat-term and scheme-term is broken.
    
    
    File: acl2-doc-emacs.info,  Node: LINEAR,  Next: META,  Prev: INDUCTION,  Up: RULE-CLASSES
    
    LINEAR    make some arithmetic inequality rules
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example:
         (defthm length-member-leq-length       If inequality reasoning begins to
           (implies (and (eqlablep e)           consider how (length (member a b))
                         (true-listp x))        compares to any other term, add to
                    (<= (length (member e x))   the set of known inequalities the fact
                        (length x)))            that it is no larger than (length b),
           :rule-classes :linear)               provided (eqlablep a) and
                                                (true-listp b) rewrite to t.
    
         General Form:
         (and ...
              (implies (and ...hi...)
                       (implies (and ...hk...)
                                (and ...
                                     (rel lhs rhs)
                                     ...)))
              ...)
    
    Note: One :linear rule class object might create many linear arithmetic
    rules from the :corollary formula.  To create the rules, we first
    flatten the and and implies structure of the formula, transforming it
    into a conjunction of formulas, each of the form
    
         (implies (and h1 ... hn) (rel lhs rhs))
    
    where no hypothesis is a conjunction and rel is one of the inequality
    relations <, <=, =, >, or >=.  If necessary, the hypothesis of such a
    conjunct may be vacuous.  We create a :linear rule for each such
    conjunct, if possible, and otherwise cause an error.
    
    Each rule has one or more "trigger terms" which may be specified by the
    user using the :trigger-terms field of the rule class or which may be
    defaulted to values chosen by the system.  We discuss the determination
    of trigger terms after discussing how linear rules are used.
    
    :Linear rules are used by an arithmetic decision procedure during
    rewriting.  See *note LINEAR-ARITHMETIC:: and see *note
    NON-LINEAR-ARITHMETIC::.  Here we assume that the reader is familiar
    with the material described in linear-arithmetic.
    
    Recall that we eliminate the unknowns of an inequality in term-order,
    largest unknowns first.  (See *note TERM-ORDER::.)  In order to
    facilitate this strategy, we store the inequalities in "linear pots".
    For purposes of the present discussion, let us say that an inequality
    is "about" its largest unknown.  Then, all of the inequalities about a
    particular unknown are stored in the same linear pot, and the pot is
    said to be "labeled" with that unknown.  This storage layout groups all
    of the inequalities which are potential candidates for cancellation
    with each other into one place.  It is also key to the efficient
    operation of :linear rules.
    
    If the arithmetic decision procedure has stabilized and not yielded a
    contradiction, we scan through the list of linear pots examining each
    label as we go.  If the trigger term of some :linear rule can be
    instantiated to match the label, we so instantiate that rule and
    attempt to relieve the hypotheses with general-purpose rewriting.  If
    we are successful, we add the rule's instantiated conclusion to our set
    of inequalities.  This may let cancellation continue.
    
    Note: Problems may arise if you explicitly store a linear lemma under a
    trigger term that, when instantiated, is not the largest unknown in the
    instantiated concluding inequality.  Suppose for example you store the
    linear rule (<= (fn i j) (/ i (* j j))) under the trigger term (fn i j).
    Then when the system "needs" an inequality about (fn a b), (i.e.,
    because (fn a b) is the label of some linear pot, and hence the largest
    unknown in some inequality), it will appeal to the rule and deduce (<=
    (fn a b) (/ a (* b b))).  However, the largest unknown in this
    inequality is (/ a (* b b)) and hence it will be stored in a linear pot
    labeled with (/ a (* b b)).  The original, triggering inequality which
    is in a pot about (fn a b) will therefore not be cancelled against the
    new one.  It is generally best to specify as a trigger term one of the
    "maximal" terms of the polynomial, as described below.
    
    We now describe how the trigger terms are determined.  Most of the
    time, the trigger terms are not specified by the user and are instead
    selected by the system.  However, the user may specify the terms by
    including an explicit :trigger-terms field in the rule class, e.g.,
    
         General Form of a Linear Rule Class:
         (:LINEAR :COROLLARY formula
                  :TRIGGER-TERMS (term1 ... termk))
    
    Each termi must be a term and must not be a variable, quoted constant,
    lambda application, let-expression or if-expression.  In addition, each
    termi must be such that if all the variables in the term are
    instantiated and then the hypotheses of the corollary formula are
    relieved (possibly instantiating additional free variables), then all
    the variables in the concluding inequality are instantiated.  We
    generate a linear rule for each conjuctive branch through the corollary
    and store each rule under each of the specified triggers.  Thus, if the
    corollary formula contains several conjuncts, the variable restrictions
    on the termi must hold for each conjunct.
    
    If :trigger-terms is omitted the system computes a set of trigger terms.
    Each conjunct of the corollary formula may be given a unique set of
    triggers depending on the variables that occur in the conjunct and the
    addends that occur in the concluding inequality.  In particular, the
    trigger terms for a conjunct is the list of all "maximal addends" in
    the concluding inequality.
    
    The "addends" of (+ x y) and (- x y) are the union of the addends of x
    and y.  The addends of (- x) and (* n x), where n is a rational
    constant, is just {x}.  The addends of an inequality are the union of
    the addends of the left- and right-hand sides.  The addends of any
    other term, x, is {x}.
    
    A term is maximal for a conjunct (implies hyps concl) of the corollary
    if (a) the term is a non-variable, non-quote, non-lambda application,
    non-let and non-if expression, (b) the term contains enough variables
    so that when they are instantiated and the hypotheses are relieved
    (which may bind some free variables; see *note FREE-VARIABLES::) then
    all the variables in concl are instantiated, and (c) no other addend is
    always "bigger" than the term, in the technical sense described below.
    
    The technical notion referenced above depends on the notion of
    _fn-count_, the number of function symbols in a term, and
    _pseudo-fn-count_, which is essentially the number of function symbols
    implicit in a constant (see *note TERM-ORDER::, specifically the
    discussion of "pseudo-function application count" at the end).  We say
    term1 is always bigger than term2 if all instances of term1 have a
    larger fn-count (actually lexicographic order of fn-count and
    pseudo-fn-count) than the corresponding instances of term2.  This is
    equivalent to saying that the fn-count of term1 is larger than that of
    term2 (by "fn-count" here we mean the lexicographic order of fn-count
    and pseudo-fn-count) and the variable bag for term2 is a subbag of that
    for term1.  For example, (/ a (* b b)) is always bigger than (fn a b)
    because the first has two function applications and {a b} is a subbag
    of a b b, but (/ a (* b b)) is not always bigger than (fn a x).
    
    
    File: acl2-doc-emacs.info,  Node: META,  Next: REFINEMENT,  Prev: LINEAR,  Up: RULE-CLASSES
    
    META    make a :meta rule (a hand-written simplifier)
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
    Meta rules extend the ACL2 simplifier with hand-written code to
    transform certain terms to equivalent ones.  To add a meta rule, the
    :corollary formula must establish that the hand-written "metafunction"
    preserves the meaning of the transformed term.
    
         Examples:
         (defthm fn-correct-1                ; Modify the rewriter to use fn to
           (equal (evl x a)                  ; transform terms that are calls of
                  (evl (fn x) a))            ; nth or of foo.
           :rule-classes ((:meta :trigger-fns (nth foo))))
    
         (defthm fn-correct-2                ; As above, but this illustrates
           (implies (and (pseudo-termp x)    ; that without loss of generality we
                         (alistp a))         ; may restrict x to be shaped like a
                    (equal (evl x a)         ; term and a to be an alist.
                           (evl (fn x) a)))
           :rule-classes ((:meta :trigger-fns (nth foo))))
    
         (defthm fn-correct-3                ; As above (with or without the
           (implies (and (pseudo-termp x)    ; hypotheses on x and a), with the
                         (alistp a)          ; additional restriction that the
                         (evl (hyp-fn x) a)) ; meaning of (hyp-fn x) is true in
                    (equal (evl x a)         ; the current context.  That is, the
                           (evl (fn x) a)))  ; applicability of the transformation
           :rule-classes                     ; may be dependent upon some computed
           ((:meta :trigger-fns (nth foo)))) ; hypotheses.
    
    While our intention is that the set of ACL2 documentation topics is
    self-contained, readers might find it useful to see the following paper
    for an introduction to meta reasoning in ACL2.
    
         W. A. Hunt, Jr., R. B. Krug, M. Kaufmann, J S. Moore and E. W.
         Smith, "Meta Reasoning in ACL2."  TPHOLs 2005, ed. J. Hurd and T.
         F. Melham, LNCS 3603, Springer-Verlag, Berlin, 2005, pp. 163-178.
    
    A non-nil list of function symbols must be supplied as the value of the
    :trigger-fns field in a :meta rule class object (except that a macro
    alias can stand in for a function symbol; see *note ADD-MACRO-ALIAS::).
    
    * Menu:
    
    * EVALUATOR-RESTRICTIONS:: some restrictions on the use of evaluators in meta-level rules
    
         General Forms:
         (implies (and (pseudo-termp x)        ; this hyp is optional
                       (alistp a)              ; this hyp is optional
                       (ev (hyp-fn x ...) a)   ; this hyp is optional
                       ; meta-extract hyps may also be included (see below)
                       )
                  (equiv (ev x a)
                         (ev (fn x ...) a)))
    
    where equiv is a known equivalence relation, x and a are distinct
    variable names, and ev is an evaluator function (see below), and fn is
    a function symbol, as is hyp-fn when provided.  The arguments to fn and
    hyp-fn should be identical.  In the most common case, both take a
    single argument, x, which denotes the term to be simplified.  If fn
    and/or hyp-fn are guarded, their guards should be trivially implied by
    pseudo-termp.  We say the theorem above is a "metatheorem" or
    "metalemma" and fn is a "metafunction", and hyp-fn is a "hypothesis
    metafunction".
    
    If "..." is empty, i.e., the metafunctions take just one argument, we
    say they are "vanilla flavored."  If "..." is non-empty, we say the
    metafunctions are "extended."  Extended metafunctions can access state
    and context sensitive information to compute their results, within
    certain limits.  We discuss vanilla metafunctions here and recommend a
    thorough understanding of them before proceeding (at which time see
    *note EXTENDED-METAFUNCTIONS::).
    
    Additional hypotheses are supported, called "meta-extract hypotheses".
    These allow metafunctions to depend on the validity of certain terms
    extracted from the context or the logical world.  These hypotheses
    provide a relatively advanced form of metatheorem so we explain them
    elsewhere; see *note META-EXTRACT::.
    
    One might think that metafunctions and (if supplied) hypothesis
    metafunctions must be executable: that is, not constrained (i.e.,
    introduced in the signature of encapsulate events), and not declared
    :non-executable.  After all, there is no point in installing a
    simplifier that cannot be run!  However, such a restriction is not
    enforced, because one could introduce a metafunction using encapsulate
    and then use defattach to attach it to an executable function; see
    *note DEFATTACH::.
    
    We defer discussion of the case in which there is a hypothesis
    metafunction and for now address the case in which the other two
    hypotheses are present.
    
    In the discussion below, we refer to the argument, x, of fn and hyp-fn
    as a "term."  When these metafunctions are executed by the simplifier,
    they will be applied to (the quotations of) terms.  But during the
    proof of the metatheorem itself, x may not be the quotation of a term.
    If the pseudo-termp hypothesis is omitted, x may be any object.  Even
    with the pseudo-termp hypothesis, x may merely "look like a term" but
    use non-function symbols or function symbols of incorrect arity.  In
    any case, the metatheorem is stronger than necessary to allow us to
    apply the metafunctions to terms, as we do in the discussion below.  We
    return later to the question of proving the metatheorem.
    
    Suppose the general form of the metatheorem above is proved with the
    pseudo-termp and alistp hypotheses.  Then when the simplifier
    encounters a term, (h t1 ... tn), that begins with a function symbol,
    h, listed in :trigger-fns, it applies the metafunction, fn, to the
    quotation of the term, i.e., it evaluates (fn '(h t1 ... tn)) to obtain
    some result, which can be written as 'val.  If 'val is different from
    '(h t1 ... tn) and val is a term, then (h t1 ... tn) is replaced by
    val, which is then passed along for further rewriting.  Because the
    metatheorem establishes the correctness of fn for all terms (even
    non-terms!), there is no restriction on which function symbols are
    listed in the :trigger-fns.  Generally, of course, they should be the
    symbols that head up the terms simplified by the metafunction fn.  See
    *note TERM-TABLE:: for how one obtains some assistance towards
    guaranteeing that val is indeed a term.
    
    The "evaluator" function, ev, is a function that can evaluate a certain
    class of expressions, namely, all of those composed of variables,
    constants, and applications of a fixed, finite set of function symbols,
    g1, ..., gk.  Generally speaking, the set of function symbols handled
    by ev is chosen to be exactly the function symbols recognized and
    manipulated by the metafunctions being introduced.  For example, if fn
    manipulates expressions in which 'equal and 'binary-append occur as
    function symbols, then ev is generally specified to handle equal and
    binary-append.  The actual requirements on ev become clear when the
    metatheorem is proved.  The standard way to introduce an evaluator is to
    use the ACL2 macro defevaluator, though this is not strictly necessary.
    See *note DEFEVALUATOR:: if you want details.
    
    [Aside for the logic-minded.] Why are we justified in using
    metafunctions this way?  Suppose (fn 'term1) is 'term2.  What justifies
    replacing term1 by term2?  The first step is to assert that term1 is
    (ev 'term1 a), where a is an alist that maps 'var to var, for each
    variable var in term1.  This step is incorrect, because 'term1 may
    contain function symbols other than the ones, g1, ..., gk, that ev
    knows how to handle.  But we can grow ev to a "larger" evaluator, ev*,
    an evaluator for all of the symbols that occur in term1 or term2.  We
    can prove that ev* satisfies the constraints on ev, provided no
    defaxiom events are adding constraints to ev (or callers of ev, and
    recursively); ACL2 checks this additional property.  Hence, the
    metatheorem holds for ev* in place of ev, by functional instantiation.
    We can then carry out the proof of the equivalence of term1 and term2
    as follows: Fix a to be an alist that maps the quotations of the
    variables of term1 and term2 to themselves.  Then,
    
         term1 = (ev* 'term1 a)      ; (1) by construction of ev* and a
               = (ev* (fn 'term1) a) ; (2) by the metatheorem for ev*
               = (ev* 'term2 a)      ; (3) by evaluation of fn
               = term2               ; (4) by construction of ev* and a
    
    Note that in line (2) above, where we appeal to the (functional
    instantiation of the) metatheorem, we can relieve its (optional)
    pseudo-termp and alistp hypotheses by appealing to the facts that term1
    is a term and a is an alist by construction.  [End of Aside for the
    logic-minded.]
    
    There are subtleties related to the notion of "growing" ev to a
    "larger" evaluator, as mentioned in the paragraph just above.  For
    corresponding restrictions on :meta rules, see *note
    EVALUATOR-RESTRICTIONS::.
    
    Finally, we turn to the second case, in which there is a hypothesis
    metafunction.  In that case, consider as before what happens when the
    simplifier encounters a term, (h t1 ... tn), where h is listed in
    :trigger-fns.  This time, after it applies fn to '(h t1 ... tn) to
    obtain the quotation of some new term, 'val, it then applies the
    hypothesis metafunction, hyp-fn.  That is, it evaluates (hyp-fn '(h t1
    ... tn)) to obtain some result, which can be written as 'hyp-val.  If
    hyp-val is not in fact a term, the metafunction is not used.  Provided
    hyp-val is a term, the simplifier attempts to establish (by
    conventional backchaining) that this term is non-nil in the current
    context.  If this attempt fails, then the meta rule is not applied.
    Otherwise, (h t1...tn) is replaced by val as in the previous case
    (where there was no hypothesis metafunction).
    
    Why is it justified to make this extension to the case of hypothesis
    metafunctions?  First, note that the rule
    
         (implies (and (pseudo-termp x)
                       (alistp a)
                       (ev (hyp-fn x) a))
                  (equal (ev x a)
                         (ev (fn x) a)))
    
    is logically equivalent to the rule
    
         (implies (and (pseudo-termp x)
                       (alistp a))
                  (equal (ev x a)
                         (ev (new-fn x) a)))
    
    where (new-fn x) is defined to be (list 'if (hyp-fn x) (fn x) x).  (If
    we're careful, we realize that this argument depends on making an
    extension of ev to an evaluator ev* that handles if and the functions
    manipulated by hyp-fn.)  If we write 'term for the quotation of the
    present term, and if (hyp-fn 'term) and (fn 'term) are both terms, say
    hyp1 and term1, then by the previous argument we know it is sound to
    rewrite term to (if hyp1 term1 term).  But since we have established in
    the current context that hyp1 is non-nil, we may simplify (if hyp1
    term1 term) to term1, as desired.
    
    We now discuss the role of the pseudo-termp hypothesis.  (Pseudo-termp
    x) checks that x has the shape of a term.  Roughly speaking, it ensures
    that x is a symbol, a quoted constant, or a true list consisting of a
    lambda expression or symbol followed by some pseudo-terms.  Among the
    properties of terms not checked by pseudo-termp are that variable
    symbols never begin with ampersand, lambda expressions are closed, and
    function symbols are applied to the correct number of arguments.  See
    *note PSEUDO-TERMP::.
    
    There are two possible roles for pseudo-termp in the development of a
    metatheorem: it may be used as the guard of the metafunction and/or
    hypothesis metafunction and it may be used as a hypothesis of the
    metatheorem.  Generally speaking, the pseudo-termp hypothesis is
    included in a metatheorem only if it makes it easier to prove.  The
    choice is yours.  (An extreme example of this is when the metatheorem
    is invalid without the hypothesis!)  We therefore address ourselves the
    question: should a metafunction have a pseudo-termp guard?  A
    pseudo-termp guard for a metafunction, in connection with other
    considerations described below, improves the efficiency with which the
    metafunction is used by the simplifier.
    
    To make a metafunction maximally efficient you should (a) provide it
    with a pseudo-termp guard and exploit the guard when possible in coding
    the body of the function (see *note GUARDS-AND-EVALUATION::, especially
    the section on efficiency issues), (b) verify the guards of the
    metafunction (see *note VERIFY-GUARDS::), and (c) compile the
    metafunction (see *note COMP::).  When these three steps have been
    taken the simplifier can evaluate (fn 'term1) by running the compiled
    "primary code" (see *note GUARDS-AND-EVALUATION::) for fn directly in
    Common Lisp.  (Note however that explicit compilation may be
    suppressed; see *note COMPILATION::.)
    
    Before discussing efficiency issues further, let us review for a moment
    the general case in which we wish to evaluate (fn 'obj) for some :logic
    function.  We must first ask whether the guards of fn have been
    verified.  If not, we must evaluate fn by executing its logic
    definition.  This effectively checks the guards of every subroutine and
    so can be slow.  If, on the other hand, the guards of fn have been
    verified, then we can run the primary code for fn, provided 'obj
    satisfies the guard of fn.  So we must next evaluate the guard of fn on
    'obj.  If the guard is met, then we run the primary code for fn,
    otherwise we run the logic code.
    
    Now in the case of a metafunction for which the three steps above have
    been followed, we know the guard is (implied by) pseudo-termp and that
    it has been verified.  Furthermore, we know without checking that the
    guard is met (because term1 is a term and hence 'term1 is a
    pseudo-termp).  Hence, we can use the compiled primary code directly.
    
    We strongly recommend that you compile your metafunctions, as well as
    all their subroutines (unless explicit compilation is suppressed; see
    *note COMPILATION::).  Guard verification is also recommended.
    
    Finally, we present a very simple example of the use of :meta rules,
    based on one provided by Robert Krug.  This example illustrates a trick
    for avoiding undesired rewriting after applying a metafunction or any
    other form of rewriting.  To elaborate: in general, the term t2
    obtained by applying a metafunction to a term t1 is then handed
    immediately to the rewriter, which descends recursively through the
    arguments of function calls to rewrite t2 completely.  But if t2 shares
    a lot of structure with t1, then it might not be worthwhile to rewrite
    t2 immediately.  (A rewrite of t2 will occur anyhow the next time a
    goal is generated.)  The trick involves avoiding this rewrite by
    wrapping t2 inside a call of hide, which in turn is inside a call of a
    user-defined "unhiding" function, unhide.
    
         (defun unhide (x)
           (declare (xargs :guard t))
           x)
    
         (defthm unhide-hide
           (equal (unhide (hide x))
                  x)
           :hints (("Goal" :expand ((hide x)))))
    
         (in-theory (disable unhide))
    
         (defun my-plus (x y)
           (+ x y))
    
         (in-theory (disable my-plus))
    
         (defevaluator evl evl-list
           ((my-plus x y)
            (binary-+ x y)
            (unhide x)
            (hide x)))
    
         (defun meta-fn (term)
           (declare (xargs :guard (pseudo-termp term)))
           (if (and (consp term)
                    (equal (length term) 3)
                    (equal (car term) 'my-plus))
               `(UNHIDE (HIDE (BINARY-+ ,(cadr term) ,(caddr term))))
             term))
    
         (defthm my-meta-lemma
           (equal (evl term a)
                  (evl (meta-fn term) a))
           :hints (("Goal" :in-theory (enable my-plus)))
           :rule-classes ((:meta :trigger-fns (my-plus))))
    
    Notice that in the following (silly) conjecture, ACL2 initially does
    only does the simplification directed by the metafunction; a second
    goal is generated before the commuativity of addition can be applied.
    If the above calls of UNHIDE and HIDE had been stripped off, then Goal'
    would have been the term printed in Goal" below.
    
         ACL2 !>(thm
                 (equal (my-plus b a)
                        ccc))
    
         This simplifies, using the :meta rule MY-META-LEMMA and the :rewrite
         rule UNHIDE-HIDE, to
    
         Goal'
         (EQUAL (+ B A) CCC).
    
         This simplifies, using the :rewrite rule COMMUTATIVITY-OF-+, to
    
         Goal''
         (EQUAL (+ A B) CCC).
    
    The discussion above probably suffices to make good use of this (UNHIDE
    (HIDE ...)) trick.  However, we invite the reader who wishes to
    understand the trick in depth to evaluate the following form before
    submitting the thm form above.
    
         (trace$ (rewrite :entry (list (take 2 arglist))
                          :exit (list (car values)))
                 (rewrite-with-lemma :entry (list (take 2 arglist))
                                     :exit (take 2 values)))
    
    The following annotated subset of the trace output (which may appear a
    bit different depending on the underlying Common Lisp implementation)
    explains how the trick works.
    
             2> (REWRITE ((MY-PLUS B A) NIL))>
               3> (REWRITE-WITH-LEMMA
                       ((MY-PLUS B A)
                        (REWRITE-RULE (:META MY-META-LEMMA)
                                      1822
                                      NIL EQUAL META-FN NIL META NIL NIL)))>
    
         We apply the meta rule, then recursively rewrite the result, which is the
         (UNHIDE (HIDE ...)) term shown just below.
    
                 4> (REWRITE ((UNHIDE (HIDE (BINARY-+ B A)))
                              ((A . A) (B . B))))>
                   5> (REWRITE ((HIDE (BINARY-+ B A))
                                ((A . A) (B . B))))>
    
         The HIDE protects its argument from being touched by the rewriter.
    
                   <5 (REWRITE (HIDE (BINARY-+ B A)))>
                   5> (REWRITE-WITH-LEMMA
                           ((UNHIDE (HIDE (BINARY-+ B A)))
                            (REWRITE-RULE (:REWRITE UNHIDE-HIDE)
                                          1806 NIL EQUAL (UNHIDE (HIDE X))
                                          X ABBREVIATION NIL NIL)))>
    
         Now we apply UNHIDE-HIDE, then recursively rewrite its right-hand
         side in an environment where X is bound to (BINARY-+ B A).
    
                     6> (REWRITE (X ((X BINARY-+ B A))))>
    
         Notice that at this point X is cached, so REWRITE just returns
         (BINARY-+ B A).
    
                     <6 (REWRITE (BINARY-+ B A))>
                   <5 (REWRITE-WITH-LEMMA T (BINARY-+ B A))>
                 <4 (REWRITE (BINARY-+ B A))>
               <3 (REWRITE-WITH-LEMMA T (BINARY-+ B A))>
             <2 (REWRITE (BINARY-+ B A))>
    
    
    File: acl2-doc-emacs.info,  Node: EVALUATOR-RESTRICTIONS,  Prev: META,  Up: META
    
    EVALUATOR-RESTRICTIONS    some restrictions on the use of evaluators in meta-level rules
    
    Note: This topic, which explains some subtleties for evaluators, can
    probably be skipped by most readers.
    
    Rules of class :meta and of class :clause-processor are stated using
    so-called "evaluator" functions.  Here we explain some restrictions
    related to evaluators.  Below we refer primarily to :meta rules, but
    the discussion applies equally to :clause-processor rules.
    
    In a nutshell, we require that a rule's evaluator does not support other
    functions in the rule, and we require that the evaluator not be
    introduced under a non-trivial encapsulate.  We also require that no
    function has an attachment (see *note DEFATTACH::) that is both
    ancestral in the evaluator and also ancestral in the meta or
    clause-processor functions.  We explain these restrictions in detail
    below.
    
    An argument given elsewhere (see *note META::, in particular "Aside for
    the logic-minded") explains that the correctness argument for applying
    metatheoretic simplifiers requires that one be able to "grow" an
    evaluator (see *note DEFEVALUATOR::) to handle all functions in the
    current ACL2 world.  Then we may, in essence, functionally instantiate
    the original evaluator to the new ("grown") evaluator, provided that
    the new evaluator satisfies all of the axioms of the original.  We
    therefore require that the evaluator function does not support the
    formula of any defaxiom event.  This notion of "support" (sometimes
    denoted "is an ancestor of") is defined recursively as follows: a
    function symbol supports a formula if either it occurs in that formula,
    or else it supports the definition or constraint for some function
    symbol that occurs in that formula.  Moreover, we require that neither
    the evaluator function nor its list version support the definition or
    constraint for any other function symbol occurring in the proposed :meta
    theorem.
    
    We also require that the evaluator does not support the formula of a
    :meta rule's metafunction (nor, if there is one, hypothesis
    metafunction) or of a :clause-processor rule's clause-processor
    function.  This requirement, along with with the analogous requirement
    for defaxiom events stated above, are necessary in order to carry out
    the functional instantiation argument alluded to above, as follows
    (where the reader may find it useful to have some familiarity with the
    paper "Structured Theory Development for a Mechanized Logic" (Journal
    of Automated Reasoning 26, no. 2 (2001), pages 161-203).  By the usual
    conservativity argument, we know that the rule follows logically from
    the axiomatic events for its supporters.  This remains true if we
    functionally instantiate the evaluator with one corresponding to all
    the functions symbols of the current session, since none of the
    definitions of supporters of defaxioms or metafunctions are hit by that
    functional substitution.
    
    Notice though that the argument above depends on knowing that the rule
    is not itself an axiom about the evaluator!  Therefore, we also
    restrict evaluators so that they are not defined in the scope of a
    superior encapsulate event with non-empty signature, in order to avoid
    an even more subtle problem.  The aforementioned correctness argument
    depends on knowing that the rule is provable from the axioms on the
    evaluator and metafunction (and hypothesis metafunction, if any).  The
    additional restriction avoids unsoundness!  The following events, if
    allowed, produce a proof that (f x) equals t even though, as shown
    below, that does not follow logically from the axioms introduced.
    
         ; Introduce our metafunction.
         (defun my-cancel (term)
           (case-match term
             (('f ('g))
              *t*)
             (& term)))
    
         ; Introduce our evaluator and prove our meta rule, but in the same
         ; encapsulate!
         (encapsulate
          ((f (x) t))
    
          (local (defun f (x) (declare (ignore x)) t))
    
          (defevaluator evl evl-list
            ((f x)))
    
          (defthm correctness-of-my-cancel
            (equal (evl x a)
                   (evl (my-cancel x) a))
            :rule-classes ((:meta :trigger-fns (f)))))
    
         ; Prove that (f x) = t.
         (encapsulate
          ()
    
          (local (defstub c () t))
    
          (local (encapsulate
                  ()
                  (local (defun g () (c)))
                  (local (in-theory (disable g (g))))
                  (local (defthm f-g
                           (equal (f (g)) t)
                           :rule-classes nil))
                  (defthm f-c
                    (equal (f (c)) t)
                    :hints (("Goal" :use f-g
                             :in-theory (e/d (g) (correctness-of-my-cancel))))
                    :rule-classes nil)))
    
          (defthm f-t
            (equal (f x) t)
            :hints (("Goal" :by (:functional-instance
                                 f-c
                                 (c (lambda () x)))))
            :rule-classes nil))
    
    To see that the term (equal (f x) t) does not follow logically from the
    axiomatic events above, consider following the above definition of
    my-cancel with the following events instead.
    
         ; (defun my-cancel (term) ...) as before, then:
    
         (defun f (x)
           (not x))
    
         (defun g ()
           nil)
    
         (defevaluator evl evl-list
            ((f x) (g)))
    
    These events imply the axiomatic events above, because we still have the
    definition of my-cancel, we have a stronger defevaluator event, and we
    can now prove correctness-of-my-cancel exactly as it is stated above.
    So, the rule f-t is a logical consequence of the chronology of the
    current session.  However, in the current session we can also prove the
    following rule, which contradicts f-t.
    
         (defthm f-not-t
           (equal (f t) nil)
           :rule-classes nil)
    
    It follows that the current session logically yields a contradiction!
    
    Erik Reeber has taken the above example and modified it to prove nil in
    ACL2 Version_3.1, as follows.
    
    
         (in-package "ACL2")
    
         (defun my-cancel (term)
            (case-match term
              (('f ('g))
               *t*)
              (('f2 ('g2))
               *t*)
              (& term)))
    
         (defun f2 (x)
            (not x))
    
         (defun g2 ()
            nil)
    
         (encapsulate
           ((f (x) t))
    
           (local (defun f (x) (declare (ignore x)) t))
    
           (defevaluator evl evl-list
             ((f x)
              (f2 x)
              (g2)))
    
           (defthm correctness-of-my-cancel
             (equal (evl x a)
                    (evl (my-cancel x) a))
             :rule-classes ((:meta :trigger-fns (f)))))
    
         (encapsulate
           ()
    
           (local (defstub c () t))
    
           (local (encapsulate
                   ()
                   (local (defun g () (c)))
                   (local (in-theory (disable g (g))))
                   (local (defthm f-g
                            (equal (f (g)) t)
                            :rule-classes nil))
                   (defthm f-c
                     (equal (f (c)) t)
                     :hints (("Goal" :use f-g
                              :in-theory (e/d (g) (correctness-of-my-cancel))))
                     :rule-classes nil)))
    
           (defthm f-t
             (equal (f x) t)
             :hints (("Goal" :by (:functional-instance
                                  f-c
                                  (c (lambda () x)))))
             :rule-classes nil))
    
         (defun g ()
            nil)
    
         ; Below is the expansion of the following defevaluator, changed slightly as
         ; indicated by comments.
         ; (defevaluator evl2 evl2-list ((f x) (f2 x) (g) (g2)))
    
         (ENCAPSULATE
           (((EVL2 * *) => *)
            ((EVL2-LIST * *) => *))
           (SET-INHIBIT-WARNINGS "theory")
           (LOCAL (IN-THEORY *DEFEVALUATOR-FORM-BASE-THEORY*))
           (LOCAL
            (MUTUAL-RECURSION (DEFUN EVL2 (X A)
                                (DECLARE (XARGS :VERIFY-GUARDS NIL
                                                :MEASURE (ACL2-COUNT X)
                                                :WELL-FOUNDED-RELATION O<
                                                :MODE :LOGIC))
                                (COND ((SYMBOLP X) (CDR (ASSOC-EQ X A)))
                                      ((ATOM X) NIL)
                                      ((EQ (CAR X) 'QUOTE) (CAR (CDR X)))
                                      ((CONSP (CAR X))
                                       (EVL2 (CAR (CDR (CDR (CAR X))))
                                             (PAIRLIS$ (CAR (CDR (CAR X)))
                                                       (EVL2-LIST (CDR X) A))))
                                      ((EQUAL (CAR X) 'F) ; changed f to f2 just below
                                       (F2 (EVL2 (CAR (CDR X)) A)))
                                      ((EQUAL (CAR X) 'F2)
                                       (F2 (EVL2 (CAR (CDR X)) A)))
                                      ((EQUAL (CAR X) 'G) (G))
                                      ((EQUAL (CAR X) 'G2) (G2))
                                      (T NIL)))
                              (DEFUN EVL2-LIST (X-LST A)
                                (DECLARE (XARGS :MEASURE (ACL2-COUNT X-LST)
                                                :WELL-FOUNDED-RELATION O<))
                                (COND ((ENDP X-LST) NIL)
                                      (T (CONS (EVL2 (CAR X-LST) A)
                                               (EVL2-LIST (CDR X-LST) A)))))))
    
           (DEFTHM EVL2-CONSTRAINT-1
             (IMPLIES (SYMBOLP X)
                      (EQUAL (EVL2 X A)
                             (CDR (ASSOC-EQ X A)))))
           (DEFTHM EVL2-CONSTRAINT-2
             (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'QUOTE))
                      (EQUAL (EVL2 X A) (CADR X))))
           (DEFTHM EVL2-CONSTRAINT-3
             (IMPLIES (AND (CONSP X) (CONSP (CAR X)))
                      (EQUAL (EVL2 X A)
                             (EVL2 (CADDAR X)
                                   (PAIRLIS$ (CADAR X)
                                             (EVL2-LIST (CDR X) A))))))
           (DEFTHM EVL2-CONSTRAINT-4
             (IMPLIES (NOT (CONSP X-LST))
                      (EQUAL (EVL2-LIST X-LST A) NIL)))
           (DEFTHM EVL2-CONSTRAINT-5
             (IMPLIES (CONSP X-LST)
                      (EQUAL (EVL2-LIST X-LST A)
                             (CONS (EVL2 (CAR X-LST) A)
                                   (EVL2-LIST (CDR X-LST) A)))))
           (DEFTHM EVL2-CONSTRAINT-6
             (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F))
                      (EQUAL (EVL2 X A) ; changed f to f2 just below
                             (F2 (EVL2 (CADR X) A)))))
           (DEFTHM EVL2-CONSTRAINT-7
             (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F2))
                      (EQUAL (EVL2 X A)
                             (F2 (EVL2 (CADR X) A)))))
           (DEFTHM EVL2-CONSTRAINT-8
             (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G))
                      (EQUAL (EVL2 X A) (G))))
           (DEFTHM EVL2-CONSTRAINT-9
             (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G2))
                      (EQUAL (EVL2 X A) (G2)))))
    
         (defthm f2-t
            (equal (f2 x) t)
            :hints (("Goal" :by (:functional-instance
                                 f-t
                                 (f f2)
                                 (evl evl2)
                                 (evl-list evl2-list)))))
    
         (defthm bug-implies-nil
            nil
            :hints (("Goal" :use ((:instance f2-t (x t)))))
            :rule-classes nil)
    
    Finally, we also require that no function has an attachment (see *note
    DEFATTACH::) that is both ancestral in the evaluator and also ancestral
    in the meta or clause-processor functions.  (If you don't use defattach
    then you can ignore this condition.)  Without this restriction, the
    following events prove nil.
    
         (in-package "ACL2")
         (defstub f () t)
         (defevaluator evl evl-list
           ((f)))
         (defun my-meta-fn (x)
           (if (equal x '(f))
               (list 'quote (f))
             x))
         (defthm my-meta-fn-correct
           (equal (evl x a)
                  (evl (my-meta-fn x) a))
           :rule-classes ((:meta :trigger-fns (f))))
         (defun constant-nil ()
           (declare (xargs :guard t))
           nil)
         (defattach f constant-nil)
         (defthm f-is-nil
         ; proved using my-meta-fn-correct
           (equal (f) nil)
           :rule-classes nil)
         (defthm contradiction
           nil
           :hints (("Goal" :use ((:functional-instance
                                  f-is-nil
                                  (f (lambda () t))))))
           :rule-classes nil)
    
    To see why this restriction is sufficient, see a comment in the ACL2
    source code entitled "; Essay on Correctness of Meta Reasoning."
    
    
    File: acl2-doc-emacs.info,  Node: REFINEMENT,  Next: REWRITE,  Prev: META,  Up: RULE-CLASSES
    
    REFINEMENT    record that one equivalence relation refines another
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example:
         (defthm bag-equal-refines-set-equal
           (implies (bag-equal x y)
                    (set-equal y x))
           :rule-classes :refinement)
    
    Also see *note DEFREFINEMENT::.
    
         General Form:
         (implies (equiv1 x y) (equiv2 x y))
    
    Equiv1 and equiv2 must be known equivalence relations.  The effect of
    such a rule is to record that equiv1 is a refinement of equiv2.  This
    means that equiv1 :rewrite rules may be used while trying to maintain
    equiv2.  See *note EQUIVALENCE:: for a general discussion of the issues.
    
    The macro form (defrefinement equiv1 equiv2) is an abbreviation for a
    defthm of rule-class :refinement that establishes that equiv1 is a
    refinement of equiv2.  See *note DEFREFINEMENT::.
    
    Suppose we have the :rewrite rule
    
         (bag-equal (append a b) (append b a))
    
    which states that append is commutative modulo bag-equality.  Suppose
    further we have established that bag-equality refines set-equality.
    Then when we are simplifying append expressions while maintaining
    set-equality we use append's commutativity property, even though it was
    proved for bag-equality.
    
    Equality is known to be a refinement of all equivalence relations.  The
    transitive closure of the refinement relation is maintained, so if
    set-equality, say, is shown to be a refinement of some third sense of
    equivalence, then bag-equality will automatially be known as a
    refinement of that third equivalence.
    
    :refinement lemmas cannot be disabled.  That is, once one equivalence
    relation has been shown to be a refinement of another, there is no way
    to prevent the system from using that information.  Of course,
    individual :rewrite rules can be disabled.
    
    More will be written about this as we develop the techniques.
    
    
    File: acl2-doc-emacs.info,  Node: REWRITE,  Next: TAU-SYSTEM,  Prev: REFINEMENT,  Up: RULE-CLASSES
    
    REWRITE    make some :rewrite rules (possibly conditional ones)
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
    This doc topic discusses the rule-class :rewrite.  If you want a general
    discussion of how rewriting works in ACL2 and some guidance on how to
    construct effective rewrite rules, see *note
    INTRODUCTION-TO-REWRITE-RULES-PART-1:: and then see *note
    INTRODUCTION-TO-REWRITE-RULES-PART-2::.
    
         Examples:
    
         (defthm plus-commutes                 ; Replace (+ a b) by (+ b a) provided
           (equal (+ x y) (+ y x)))            ; certain heuristics approve the
                                               ; permutation.
    
         (defthm plus-commutes                 ; equivalent to the above
           (equal (+ x y) (+ y x))
           :rule-classes ((:rewrite :corollary (equal (+ x y) (+ y x))
                                    :loop-stopper ((x y binary-+))
                                    :match-free :all)))
    
         (defthm append-nil                    ; Replace (append a nil) by a, if
           (implies (true-listp x)             ; (true-listp a) rewrites to t.
                    (equal (append x nil) x)))
    
         (defthm append-nil                    ; as above, but with defaults and
           (implies (true-listp x)             ; a backchain limit
                    (equal (append x nil) x))
           :rule-classes ((:rewrite :corollary (implies (true-listp x)
                                                        (equal (append x nil) x))
                                    :backchain-limit-lst (3) ; or equivalently, 3
                                    :match-free :all)))
    
         (defthm member-append                 ; Replace (member e (append b c)) by
           (implies                            ; (or (member e b) (member e c) in
            (and                               ; contexts in which propositional
             (true-listp x)                    ; equivalence is sufficient, provided
             (true-listp y))                   ; b and c are true-lists.
            (iff (member e (append x y))
                 (or (member e x) (member e y)))))
    
         * Menu:
    
    
         Related topics other than immediate subtopics:
         * BIND-FREE:: to bind free variables of a rewrite, definition, or linear rule
    
         * CASE-SPLIT:: like force but immediately splits the top-level goal on the hypothesis
    
         * DOUBLE-REWRITE:: cause a term to be rewritten twice
    
         * FORCE:: identity function used to force a hypothesis
    
         * FREE-VARIABLES:: free variables in rules
    
         * SYNTAXP:: attach a heuristic filter on a rule
    
         General Form:
         (and ...
              (implies (and ...hi...)
                       (implies (and ...hk...)
                                (and ...
                                     (equiv lhs rhs)
                                     ...)))
              ...)
    
    Note: One :rewrite rule class object might create many rewrite rules
    from the :corollary formula.  To create the rules, we first translate
    the formula, expanding all macros (see *note TRANS::) and also
    expanding away calls of all so-called "guard holders," mv-list and
    return-last (the latter resulting for example from calls of prog2$,
    mbe, or ec-call), as well as expansions of the macro `the'.  Next, we
    eliminate all lambdas; one may think of this step as simply substituting
    away every let, let*, and mv-let in the formula.  We then flatten the
    AND and IMPLIES structure of the formula; for example, if the
    hypothesis or conclusion is of the form (and (and term1 term2) term3),
    then we replace that by the "flat" term (and term1 term2 term3).  (The
    latter is actually an abbreviation for the right-associated term (and
    term1 (and term2 term3)).)  The result is a conjunction of formulas,
    each of the form
    
         (implies (and h1 ... hn) concl)
    
    where no hypothesis is a conjunction and concl is neither a conjunction
    nor an implication.  If necessary, the hypothesis of such a conjunct
    may be vacuous.  We then further coerce each concl into the form (equiv
    lhs rhs), where equiv is a known equivalence relation, by replacing any
    concl not of that form by (iff concl t).  A concl of the form (not
    term) is considered to be of the form (iff term nil).  By these steps
    we reduce the given :corollary to a sequence of conjuncts, each of
    which is of the form
    
         (implies (and h1 ... hn)
                  (equiv lhs rhs))
    
    where equiv is a known equivalence relation.  See *note EQUIVALENCE::
    for a general discussion of the introduction of new equivalence
    relations.  At this point, we check whether lhs and rhs are the same
    term; if so, we cause an error, since this rule will loop.  (But this
    is just a basic check; the rule could loop in other cases, for example
    if rhs is an instance of lhs; see *note LOOP-STOPPER::.)
    
    We create a :rewrite rule for each such conjunct, if possible, and
    otherwise cause an error.  It is possible to create a rewrite rule from
    such a conjunct provided lhs is not a variable, a quoted constant, a
    let-expression, a lambda application, or an if-expression.
    
    A :rewrite rule is used when any instance of the lhs occurs in a
    context in which the equivalence relation is an admissible congruence
    relation.  First, we find a substitution that makes lhs equal to the
    target term.  Then we attempt to relieve the instantiated hypotheses of
    the rule.  Hypotheses that are fully instantiated are relieved by
    recursive rewriting.  Hypotheses that contain "free variables"
    (variables not assigned by the unifying substitution) are relieved by
    attempting to guess a suitable instance so as to make the hypothesis
    equal to some known assumption in the context of the target.  If the
    hypotheses are relieved, and certain restrictions that prevent some
    forms of infinite regress are met (see *note LOOP-STOPPER::), the
    target is replaced by the instantiated rhs, which is then recursively
    rewritten.
    
    ACL2's rewriting process has undergone some optimization.  In
    particular, when a term t1 is rewritten to a new term t2, the rewriter
    is then immediately applied to t2.  On rare occasions you may find that
    you do not want this behavior, in which case you may wish to use a
    trick involving hide; see *note META::, near the end of that
    documentation.
    
    In another optimization, when the hypotheses and right-hand side are
    rewritten, ACL2 does not really first apply the substitution and then
    rewrite; instead, it as it rewrites those terms it looks up the already
    rewritten values of the bound variables.  Sometimes you may want those
    bindings rewritten again, e.g., because the variables occur in slots
    that admit additional equivalence relations.  See double-rewrite.
    
    See *note INTRODUCTION-TO-REWRITE-RULES-PART-1:: and see *note
    INTRODUCTION-TO-REWRITE-RULES-PART-2:: for an extended discussion of how
    to create effective rewrite rules.
    
    
    File: acl2-doc-emacs.info,  Node: TAU-SYSTEM,  Next: TYPE-PRESCRIPTION,  Prev: REWRITE,  Up: RULE-CLASSES
    
    TAU-SYSTEM    make a rule for the ACL2 "type checker"
    
    This documentation topic describes the syntactic form of "tau-system"
    rules; these rules extend ACL2's "type checker."  For an introduction to
    the tau system, see *note INTRODUCTION-TO-THE-TAU-SYSTEM::.
    
    There happens to be a function named tau-system, defined as the
    identity function.  Its only role is to provide the rune
    (:EXECUTABLE-COUNTERPART TAU-SYSTEM), which is used to enable and
    disable the tau system.  Otherwise the function tau-system has no
    purpose and we recommend that you avoid using it so you are free to
    enable and disable the tau system.
    
    When in the default ("greedy") mode (see set-tau-auto-mode), every
    defun and every :corollary (see :rule-classes) of every defthm stored
    as a rule of any :rule-class is inspected to determine if it is of one
    of the forms below.  Rules of these forms are added to the tau
    database, even if they are not labeled as :tau-system rules, e.g., a
    :rewrite rule might contribute to the tau database!  To add a rule to
    the tau database without adding any other kind of rule, tag it with
    :rule-classes :tau-system.  If a theorem has :rule-classes nil, it is
    not considered for the tau database.
    
         General Forms:
         Boolean:
         (booleanp (p v))
    
         Eval:
         (p 'const) or
         (p *const*)
    
         Simple:
         (implies (p v) (q v))
    
         Conjunctive:
         (implies (and (p1 v) ... (pk v)) (q v)), ; Here k must exceed 1.
    
         Signature Form 1:
         (implies (and (p1 x1) (p2 x2) ...)
                  (q (fn x1 x2 ...)))
    
         Signature Form 2:
         (implies (and (p1 x1) (p2 x2) ...)
                  (q (mv-nth 'n (fn x1 x2 ...))))
    
         Bounder Form 1 (or Form 2):
         (implies (and (tau-intervalp i1)
                       ...
                       (or (equal (tau-interval-dom i1) 'dom1-1)
                           ...)
                       ...
                       (in-tau-intervalp x1 i1)
                       ...)
                  (and (tau-intervalp (bounder-fn i1 ...))
                       (in-tau-intervalp target
                                         (bounder-fn i1 ...))))
    
         where target is
         (fn x1 ... y1 ...)             in Form 1, and
         (mv-nth 'n (fn x1 ... y1 ...)) in Form 2
    
         Big Switch:
         (equal (fn . formals) body)
    
         MV-NTH Synonym:
         (equal (nth-alt x y) (mv-nth x y)) or
         (equal (mv-nth x y) (nth-alt x y))
    
    The symbols p, q, p1, etc., denote monadic (one-argument)
    Boolean-valued function symbols, or equalities in which one argument is
    constant, arithmetic comparisons in which one argument is a rational or
    integer constant, or the logical negations of such terms.  By
    "equalities" we allow EQUAL, EQ, EQL, and =.  By "arithmetic
    comparison" we mean <, <=, >=, or >.  Any of these tau predicates may
    appear negated.
    
    The notation (p v) above might stand for any one of:
    
         (INTEGERP X)
         (EQUAL V 'MONDAY)
         (<= I 16)
         (NOT (EQUAL X 'SUNDAY))
    
    The different rule forms above affect different aspects of the tau
    system.  We discuss each form in more detail below.
    
    * Menu:
    
    * BOUNDERS:: intervals, bounder functions, and bounder correctness
    
    * IN-TAU-INTERVALP:: Boolean membership in a tau interval
    
    * MAKE-TAU-INTERVAL:: make a tau interval
    
    * TAU-INTERVAL-DOM:: access the domain of a tau interval
    
    * TAU-INTERVAL-HI:: access the upper bound of a tau interval
    
    * TAU-INTERVAL-HI-REL:: access the upper bound relation of a tau interval
    
    * TAU-INTERVAL-LO:: access the lower bound of a tau interval
    
    * TAU-INTERVAL-LO-REL:: access the lower bound relation of a tau interval
    
    * TAU-INTERVALP:: Boolean recognizer for tau intervals
    
    The documentation below is written as though the tau system is in auto
    mode!  To insure that the only rules added to the tau system are those
    explicitly assigned to :rule-class :tau-system, you should use
    set-tau-auto-mode to select manual mode.
    
         General Form: Boolean:
         (booleanp (p v))
    
    Here p must be a function symbol and v must be a variable.  Such a
    :tau-system rule adds p to the list of tau predicates.  If p was
    recognized as Boolean when it was defined, there is no need to state
    this rule.  This form is needed if you define a monadic Boolean
    function in such a way that the system does not recognize that it is
    Boolean.
    
         General Form: Eval:
         (p 'const) or
         (p *const*)
    
    Here p must be a function symbol.  In addition, recall that these
    general tau predicate forms may appear negated.  So the form above
    includes such theorems as (NOT (GOOD-STATEP *INITIAL-STATE*)).  A
    theorem of this form thus records whether a named predicate is true or
    false on the given constant.
    
    Generally, when the tau system must determine whether an enabled tau
    predicate is true or false on a constant, it simply evaluates the
    predicate on the constant.  This can be impossible or very inefficient
    if p is not defined but constrained, or if p is defined in a
    hard-to-compute way (e.g., (defun p (x) (evenp (ack x x))) where ack is
    the Ackermann function), or perhaps if the constant is very large.  By
    proving a :tau-system rule of Eval form, you cause the tau system to
    note the value of the predicate on the constant and henceforth to look
    it up instead of evaluating the definition.
    
    A difficulty, however, is determining that a slow down is due to the
    evaluation of tau predicates and not some other reason.  The first step
    is determining that tau is slowing the proof down.  See time-tracker-tau
    for an explanation of TIME-TRACKER-NOTEs output during some proofs
    involving tau reasoning.  These notes can alert you to the fact that
    significant amounts of time are being spent in the tau system.
    Time-tracker-tau gives some ways of determining whether tau predicate
    evaluation is involved.  (If worse comes to worst, consider the
    following hack: In the ACL2 source file tau.lisp, immediately after the
    definition of the system function ev-fncall-w-tau-recog, there is a
    comment which contains some raw Lisp code that can be used to
    investigate whether tau's use of evaluation on constants is causing a
    problem.)  However, once a recognizer and the constants on which it is
    being evaluated are identified, the tau system can be sped up by
    proving Eval rules to pre-compute and store the values of the
    recognizer on those constants.  Alternatively, at the possible loss of
    some completeness in the tau system, the executable counterpart of the
    recognizer can be disabled.
    
         General Form: Simple:
         (implies (p v) (q v))
    
    Here v must be a variable symbol.  This rule builds-in the information
    that anything satisfying p must also satisfy q, i.e., the "type" q
    includes the "type" p.  Recall that the forms may be negated.  Most of
    the time, p and q will be predicate symbols but it is possible they
    will be equalities- or inequalities-with-constants.  Examples of Simple
    rules include the following, which are in fact built-in:
    
         (implies (natp x) (integerp x))
         (implies (integerp x) (rationalp x))
         (implies (integerp x) (not (true-listp x)))
         (implies (natp x) (not (< x 0)))
         (implies (symbol-alistp x) (alistp x))
    
    Because the tau system records the transitive closure of the Simple
    rules, any time a term is known to satisfy natp it is also known to
    satisfy integerp and rationalp, and known not to satisfy true-listp,
    and known to be non-negative.
    
         General Form: Conjunctive:
         (implies (and (p1 v) ... (pk v)) (q v)), ; Here k must exceed 1.
    
    The pi and q may be any tau predicates or their negations, v must be a
    variable symbol, and i must exceed 1 or else this is a Simple rule.  An
    obvious operational interpretation of this rule is that if an object is
    known to satisfy all of the pi, then it is known to satisfy q.
    However, the actual interpretation is more general.  For example, if an
    object is known to satisfy all but one of the pi and is known not to
    satisfy q, then the object is known not to satisfy the "missing" pi.
    
    For example, the following Conjunctive rule allows tau to conclude that
    if weekday D is not MON, TUE, THU or FRI, then it is WED:
    
         (implies (and (weekdayp d)
                       (not (eq d 'MON))
                       (not (eq d 'TUE))
                       (not (eq d 'WED))
                       (not (eq d 'THU)))
                  (eq d 'FRI))
    
    The tau database is not closed under conjunctive rules; they are
    applied dynamically.
    
         General Form: Signature Form 1:
         (implies (and (p1 x1) (p2 x2) ... (pn xn) dep-hyp)
                  (q (fn x1 x2 ... xn)))
    
    The pi and q may be any tau predicates or their negations, fn must be a
    function symbol of arity n, the xi must be distinct variable symbols
    and dep-hyp may be any term, provided it is not of the (pi xi) shape
    and the only the variables in it are the xi.
    
    The Signature form actually allows multiple tau predicates to be
    applied to each variable, e.g., x1 might be required to be both an
    INTEGERP and EVENP.  The Signature form allows there to be multiple
    hypotheses classified as dep-hyps, i.e., not fitting any of the
    previous shapes, and they are implicitly just conjoined.  The name
    "dep-hyp" is an abbreviation of "dependent hypothesis" and stems from
    the fact they often express relations between several of the function's
    inputs rather than type-like constraints on individual inputs.
    
    A Signature rule informs tau that the function fn returns an object
    satisfying q provided that the arguments satisfy the respective pi and
    provided that dep-hyp occurs in the current context.  Note: to be
    precise, dependent hypotheses are relieved only by applying ACL2's most
    primitive form of reasoning, type-set.  In particular, tau reasoning is
    not used to establish dependent hypotheses.  The presence of a dep-hyp
    in a signature rule may severely restrict its applicability.  We
    discuss this after showing a few mundane examples.
    
    An example Signature rule is
    
         (implies (and (integer-listp x)
                       (integer-listp y))
                  (integer-listp (append x y)))
    
    Of course, a function may have multiple signatures:
    
         (implies (and (symbol-listp x)
                       (symbol-listp y))
                  (symbol-listp (append x y)))
    
    Here is a Signature rule for the function pairlis$:
    
         (implies (and (symbol-listp x)
                       (integer-listp y))
                  (symbol-alistp (pairlis$ x y)))
    
    The tau system can consequently check this theorem by composing the
    last two rules shown and exploiting Simple rule stating that
    symbol-alists are also alists:
    
         (thm (implies (and (symbol-listp a)
                            (symbol-listp b)
                            (integer-listp y))
                       (alistp (pairlis$ (append a b) y))))
    
    Since a and b are known to be lists of symbols and a signature for
    append is that it preserves that predicate, the first argument to the
    pairlis$ expression is known to be a list of symbols.  This means the
    Signature rule for pairlis$ tells us the result is a symbol-alistp, but
    the previously mentioned Simple rule, (implies (symbol-alistp x)
    (alistp x)), tells us the result is also an alistp.
    
    When a Signature rule has an dep-hyp, that hypothesis is not an
    expression in the tau system.  Tau is not used to check that
    hypothesis.  Instead, tau uses the more primitive type-set mechanism of
    ACL2.  Here is an example of a Signature rule with a dep-hyp:
    
         (implies (and (natp n)
                       (integer-listp a)
                       (< n (len a)))
                  (integerp (nth n a)))
    
    Note that the last hypothesis is a dependent hypothesis: it is not a tau
    predicate but a relationship between n and a.  It is relieved by
    type-set.  If one is trying to compute the signature of an (nth n a)
    expression in a context in which (< n (len a)) is explicitly assumed,
    then this mechanism would establish the dependent hypothesis.  But one
    can easily imagine an almost identical context where, say (< n (len
    (rev a))) is explicitly assumed.  In that context, the Signature rule
    would not be fired because type-set cannot establish (< n (len a)) from
    (< n (len (rev a))), even though it would be easily proved by rewriting
    using the theorem (equal (len (rev a)) (len a)).
    
    Note also that if this signature could be phrased in a way that
    eliminates the dependency between n and a it would be more effective.
    For example, here is a related Signature rule without a dependent
    hypothesis:
    
         (implies (and (natp n)
                       (register-filep a)
                       (< n 16))
                  (integerp (nth n a)))
    
    In this theorem we require only that n be less than 16, which is a tau
    predicate and hence just an additional tau constraint on n.
    
         General Form: Signature Form 2:
         (implies (and (p1 x1) (p2 x2) ... (pn xn) dep-hyp)
    
                  (q (mv-nth 'n (fn x1 x2 ... xn))))
    
    This form of signature rule is just like form 1 except that it is
    useful for functions that return multiple-values and allows us to
    "type-check" their individual outputs.
    
         General Form: Bounder Forms 1 and 2:
         (implies (and (tau-intervalp i1)
                       ...
                       (or (equal (tau-interval-dom i1) 'dom1-1)
                           ...)
                       ...
                       (in-tau-intervalp x1 i1)
                       ...)
                  (and (tau-intervalp (bounder-fn i1 ...))
                       (in-tau-intervalp target
                                         (bounder-fn i1 ...))))
    
    where target is either (fn x1 ... y1 ...) in Form 1 or (mv-nth 'n (fn
    x1 ... y1 ...)) in Form 2.
    
    This form is for advanced users only and the schema given above is just
    a reminder of the general shape.  A "bounder" for a given function
    symbol, fn, is a function symbol bounder-fn that computes an interval
    containing (fn x1 ... y1 ...) (or its nth component in the case of Form
    2 rules) from the intervals containing certain of the arguments of fn.
    The correctness theorem for a bounder function informs the tau system
    that bounds for fn are computed by bounder-fn and sets up the
    correspondence between the relevant arguments, xi, of fn and the
    intervals containing those arguments, ii to which bounder-fn is
    applied.  When the tau system computes the tau for a call of fn, it
    computes the tau of the relevant arguments and applies the bounder to
    the intervals of those tau.  This provides a domain and upper and/or
    lower bounds for the value of the term.  The tau system then further
    augments that with signature rules.  See *note BOUNDERS:: for details
    on intervals, bounders, and bounder correctness theorems.
    
         General Form: Big Switch:
         (equal (fn . formals) body)
    
    In the Big Switch form, fn must be a function symbol, formals must be a
    list of distinct variable symbols, and body must be a "big switch"
    term, i.e., one that case splits on tau predicates about a single
    variable and produces a term not involving that variable.  An example
    of a Big Switch rule is
    
         (equal (conditional-type x y)
                (if (consp x)
                    (consp y)
                    (integerp y)))
    
    The idea is that the tau system can treat calls of conditional-type as
    a tau-predicate after determining the tau of an argument.
    
    Since equality-to-constants are tau predicates, a more common example
    of a Big Switch rule is
    
         (equal (dtypep x expr)
                (case x
                      (STMT (stmt-typep expr))
                      (EXPR (expr-typep expr))
                      (MODULE (module-typep expr))
                      (otherwise nil)))
    
    This is because (case x (STMT ...) ...) macroexpands in ACL2 to (if
    (eql x 'STMT) ... ...) and (eql x 'STMT) is a tau predicate about x.
    
    Big Switch rules are recognized when a function is defined (if tau is in
    automatic mode).  They generally do not have to be proved explicitly,
    though they might be when mutual recursion is involved.  Only the first
    detected Big Switch rule about a function fn is recognized.
    
         General Form: MV-NTH Synonym:
         (equal (nth-alt x y) (mv-nth x y)) or
         (equal (mv-nth x y) (nth-alt x y))
    
    Rules of this form just tell the tau system that the user-defined
    function nth-alt is synonymous with the ACL2 primitive function mv-nth.
    Because ACL2's rewriter gives special handling to mv-nth, users
    sometimes define their own versions of that function so they can
    disable them and control rewriting better.  By revealing to the tau
    system that such a synonym has been introduced you allow Signature
    rules of Form 2 to be used.
    
    
    File: acl2-doc-emacs.info,  Node: BOUNDERS,  Next: IN-TAU-INTERVALP,  Prev: TAU-SYSTEM,  Up: TAU-SYSTEM
    
    BOUNDERS    intervals, bounder functions, and bounder correctness
    
         Bounder Forms 1 and 2:
         (implies (and (tau-intervalp i1)
                       ...
                       (or (equal (tau-interval-dom i1) 'dom1-1)
                           ...)
                       ...
                       (in-tau-intervalp x1 i1)
                       ...)
                  (and (tau-intervalp (bounder-fn i1 ...))
                       (in-tau-intervalp target
                                         (bounder-fn i1 ...))))
    
    where target is either (fn x1 ... y1 ...) or (mv-nth 'n (fn x1 ... y1
    ...)), depending on whether we are in the Form 1 or Form 2 case,
    respectively.  However, the shape above is meant just as a reminder.
    Details are given below.
    
    This topic first explains the basic shape of Bounder Form 1.  Then it
    illustrates Bounder Form 2.  Finally, it deals briefly with proving
    bounder correctness theorems.  The community book
    tau-bounders/elementary-bounders contains bounders for various
    elementary functions including +, *, /, FLOOR, MOD, LOGAND, LOGNOT,
    LOGIOR, LOGORC1, LOGEQV, LOGXOR, and ASH.  You might look at or include
    this book to see more example theorems, to see how proofs of such
    theorems are managed, and to experiment with their effects on proving
    theorems involving arithmetic over finite or half-finite intervals.
    
    A bounder correctness theorem establishes that bounder-fn is a
    "bounder" for the function fn.  That means that when trying to compute
    a tau for a call of fn (or, in the case of Form 2, for the nth
    component of the multiple-value vector returned by a call of fn) the tau
    system can call bounder-fn on the intervals containing certain arguments
    of fn.
    
    Let us start with an example.  Let fn be the addition function, +
    (actually, binary-+).  Consider the target term (+ x y) and contemplate
    the question: if you know intervals containing x and y, say intx and
    inty respectively, what is an interval containing their sum?  The
    answer is pretty easy to state in English: the domain of the answer
    interval is the less restrictive of the domains of intx and inty.  The
    lower bound of the answer interval is the sum of the lower bounds of
    intx and inty, and the lower relation is the stronger of the lower
    relations of intx and inty.  Analogous comments define the upper bound
    and relation of the answer interval.  So for example, if x is an
    INTEGERP such that 0 <= x <= 10 and y is a RATIONALP such that 0 < y <=
    20, then (+ x y) is a RATIONALP such that 0 < (+ x y) <= 30.
    
    Defining this precisely is more tedious than describing it in English
    because one must make precise the notions of "less restrictive"
    domains, "weaker" relations, and the possibility that either or both of
    the bounds could be "infinite."  But we can easily imagine defining the
    function bounder-for-+ that returns the answer interval described,
    given intx and inty.
    
    Then the following Bounder Form 1 formula establishes the correctness of
    bounder-for-+ and allows the tau system to use it to produce bounds in
    the tau computed for +-expressions:
    
         (implies (and (tau-intervalp intx)
                       (tau-intervalp inty)
                       (in-tau-intervalp x intx)
                       (in-tau-intervalp y inty))
                  (and (tau-intervalp (bounder-for-+ intx inty))
                       (in-tau-intervalp (+ x y)
                                         (bounder-for-+ intx inty))))
    
    For example, suppose we have a formula with the following hypotheses
    
         (and (integerp a)
              (<= 0 a)
              (<= a 10)
              (rationalp b)
              (< 0 b)
              (<= b 20))
    
    and suppose the tau system encounters the term (+ a b).  When the term
    is enountered, the tau for a would include an INTEGERP interval such
    that 0 <= a <= 10 and the tau for b would include a RATIONALP interval
    such that 0 < b <= 20.  In its most primitive configuration, the tau
    system would only know that the tau for (+ a b) includes the recognizer
    RATIONALP (and all that it is known to imply).  But after the bounder
    theorem above is proved and available as a :tau-system rule the tau
    system would infer that (+ a b) was in the RATIONALP interval such that
    0 < (+ a b) <= 30.
    
    Thus, by defining bounder functions and proving them correct the user
    can give the tau system the ability to compute the bounds on function
    calls as a function of the known bounds on their actuals.
    
    It is sometimes useful to restrict the domains of the intervals to be
    considered.  For example, in bounding *-expressions it is simplifying
    to restrict one's attention to intervals over the integers or rationals
    (and thus exclude the complex rationals so one need not think about the
    getting negative bounds by multiplying two "positive" complex rationals
    or how to "round up" from complex bounds to the rationals required by
    our intervals).
    
    If we were to define bounder-for-* so that it works correctly to bound
    *-expressions, but only for integer or rational arguments, its
    correctness theorem would be:
    
         (implies (and (tau-intervalp intx)                             ; (a)
                       (tau-intervalp inty)
                       (or (equal (tau-interval-dom intx) 'INTEGERP)    ; (b)
                           (equal (tau-interval-dom intx) 'RATIONALP))
                       (or (equal (tau-interval-dom inty) 'INTEGERP)
                           (equal (tau-interval-dom inty) 'RATIONALP))
                       (in-tau-intervalp x intx)                        ; (c)
                       (in-tau-intervalp y inty))
                  (and (tau-intervalp (bounder-for-* intx inty))       ; (d)
                       (in-tau-intervalp (* x y)                        ; (e)
                                         (bounder-for-* intx inty))))
    
    In this case, bounder-for-* would be applied to the intervals for x and
    y only if those intervals were over the integers or the rationals.
    
    The above theorem for bounder-for-* begins to suggest the general form
    of a bounder theorem and we will use it to explain the general form.
    
    The hypotheses of a bounder theorem must be a conjunction and the
    conjuncts must be partitionable into three parts, (a), (b), and (c).
    The conclusion, must be a conjunction, must contain at least two
    conjuncts, (d) and (e), and is allowed to contain others that are
    simply ignored for purposes of bounders.  (See the note below about why
    we allow but ignore additional conjuncts in the conclusion.)
    
    Part (a) introduces some distinct "interval variables," here called
    "ivars," that are known to denote intervals; for the example above, the
    ivars are intx and inty.  Each hypothesis in part (a) is of the form
    (TAU-INTERVALP ivar).
    
    Part (b) allows us to restrict the domains of some of the intervals.
    Each hypothesis in part (b) must be a disjunction and each of the
    disjuncts must be of the form (EQUAL (TAU-INTERVAL-DOM ivar) 'dom),
    where ivar is one of the interval variables and dom is one of INTEGERP,
    RATIONALP, ACL2-NUMBERP, or NIL.  It is not necessary to restrict every
    interval variable.  Indeed, part (b) may be empty, as in the theorem for
    bounder-for-+ above.
    
    Part (c) consists of a set of (IN-TAU-INTERVALP avar ivar) hypotheses
    where each avar is a variable and no two hypotheses in part (c) use the
    same avar or ivar.  We call the set of all such avar the "actual
    variables" or "avars."  The avars and ivars must be distinct.  Part (c)
    sets up a correspondence between the avars and the ivars, each avar is
    in an interval denoted by one ivar.
    
    Part (d) introduces the name of the bounder function, here
    bounder-for-*, and the order of its ivar arguments.  We see that
    bounder-for-* takes two arguments and they correspond, in order, to the
    intervals containing x and y.  Part (d) also establishes that the
    bounder function always returns an interval under hypotheses (a), (b),
    and (c).  Note that it is sometimes useful to return the "universal
    interval" (one that contains everything) if you don't want to compute a
    better interval for some case; see tau-intervalp or in-tau-intervalp.
    
    Part (e) introduces the name of the function being bounded, here *, and
    the order of its arguments.  It establishes that the function being
    bounded really is bounded by the interval computed by the bounder
    function.  In general, the function being bounded may take additional
    arguments.  It is possible that the function being bounded takes some
    arguments that do not affect the bounds of its output.
    
    Thus, parts (c) and (e) together establish a mapping between the
    actuals of a call of the function being bounded and the intervals to be
    supplied to the bounder.
    
    The parts identified above may be presented in any order and the
    literals constituting those parts may be mingled.  Thus, for example,
    here is another version of the theorem above that generates the same
    bounding information for the tau system.  In this version, the
    hypotheses and conclusions are rearranged, bounder-for-* takes its
    arguments in the opposite order, and the theorem includes an additional
    conclusion.
    
         (implies (and (tau-intervalp intx)                             ; (a)
                       (or (equal (tau-interval-dom intx) 'INTEGERP)    ; (b)
                           (equal (tau-interval-dom intx) 'RATIONALP))
                       (in-tau-intervalp x intx)                        ; (c)
    
                       (tau-intervalp inty)                             ; (a)
                       (or (equal (tau-interval-dom inty) 'INTEGERP)    ; (b)
                           (equal (tau-interval-dom inty) 'RATIONALP))
                       (in-tau-intervalp y inty))
                  (and (in-tau-intervalp (* x y)                        ; (e)
                                         (bounder-for-* inty intx))
                       (tau-intervalp (bounder-for-* inty intx))        ; (d)))
    
                       (or (equal (tau-interval-dom (bounder-for-* inty intx))
                                  'INTEGERP)
                           (equal (tau-interval-dom (bounder-for-* inty intx))
                                  'RATIONALP))
    
    Note on why bounder forms allow additional conjuncts in the conclusion:
    It is often the case that one creates bounders by composing other
    bounders.  To prove compositional bounds correct one must often prove
    more than the mere correctness of the components.  For example, one
    might need to prove that the domain of the new bounding interval is
    INTEGERP or otherwise restricted.  We allow such "unnecessary"
    conclusions simply to save the user the burden of stating multiple
    theorems.
    
    An Illustration of Bounder Form 2: Suppose (quad i) is defined so that
    truncates the integer i to the largest multiple of 4 weakly below i
    and, additionally, returns the remainder.  For example, (quad 26)
    returns (mv 24 2).  Then here are bounders for each of its return
    values:
    
         (defun quad-bounds-0 (i)
           (cond ((and (tau-interval-lo i)
                       (<= 0 (tau-interval-lo i)))
                  (make-tau-interval 'integerp nil 0 nil (tau-interval-hi i)))
                 (t (make-tau-interval nil nil nil nil nil))))
    
         (defun quad-bounds-1 (i)
           (cond ((and (tau-interval-lo i)
                       (<= 0 (tau-interval-lo i)))
                  (make-tau-interval 'integerp nil 0 nil 3))
                 (t (make-tau-interval nil nil nil nil nil))))
    
    Note that the bounders assume i is an INTEGERP and return the universal
    interval when i is not a natural.
    
    As noted in the discussion below about how to prove bounder correctness
    theorems, proving these bounders correct will require an arithmetic
    book, e.g.,
    
         (include-book "arithmetic-5/top" :dir :system)
    
    Here then are two bounder correctness theorems of Form 2:
    
         (defthm quad-bounds-0-correct
           (implies (and (tau-intervalp i)
                         (equal (tau-interval-dom i) 'INTEGERP)
                         (in-tau-intervalp x i))
                    (and (tau-intervalp (quad-bounds-0 i))
                         (in-tau-intervalp (mv-nth 0 (quad x))
                                           (quad-bounds-0 i))))
           :rule-classes :tau-system)
    
         (defthm quad-bounds-1-correct
           (implies (and (tau-intervalp i)
                         (equal (tau-interval-dom i) 'INTEGERP)
                         (in-tau-intervalp x i))
                    (and (tau-intervalp (quad-bounds-1 i))
                         (in-tau-intervalp (mv-nth 1 (quad x)) (quad-bounds-1 i))))
           :rule-classes :tau-system)
    
    As noted above, if these bounders are to be used in constructing other
    bounders, we might include (in the first theorem) an additional
    concluding conjunct, such as
    
         (equal (tau-interval-dom (quad-bounds-0 i)) 'INTEGERP)
    
    so that we can keep quad-bounds-0 disabled to allow us to use
    quad-bounds-0-correct as a :rewrite or other rule and still relieve
    hypotheses about the domain of the interval it produces.  These
    hypotheses would arise if some other verified bounder was called on the
    produced interval.  In addition, as noted below, we might replace the
    :rule-classes above with
    
         :rule-classes
          ((:rewrite)
           (:forward-chaining :trigger-terms ((quad-bounds-0 i))))
    
    Since the theorem is being stored as some kind of rule and since it
    satisfies the Bounder Form 2 shape, it will additionally be stored as a
    :tau-system rule.
    
    Note on proving bounder theorems: Proving bounder theorems is just like
    proving any other arithmetic theorem and you will need whatever
    libraries are appropriate for the problem domain you are working in.
    Do not expect the tau system to be of much use in proving bounder
    theorems.  A typical bounder theorem might require you to prove a
    subgoal like  (< (fn x y) (g (tau-interval-hi int1) int2)).  But tau
    deals with inequalities relating terms to constants, e.g., (< ... 16).
    A bounder theorem is a sort of "metatheorem" about how to construct
    bounded intervals from other bounded intervals.  So when you undertake
    to define a bounder and prove it correct, go into the project with your
    eyes open!
    
    But bounder functions can be broadly divided into two classes, those
    defined in terms of arithmetic on the interval bounds and those defined
    in terms of other bounders.  For example, given that
    
         (LOGXOR x y) = (LOGNOT (LOGEQV x y))
    
    an interval for bounding LOGXOR can be constructed by composing the
    constructions of intervals for LOGEQV and LOGNOT.  So some bounder
    correctness proofs will involve direct manipulation of arithmetic
    inequalities and others might involve appeal to the correctness of other
    bounders, depending on how the new bounder is defined.
    
    Regardless of which style of bounder we are dealing with, we have found
    it useful to prove the basic theorems relating the tau interval
    accessors to MAKE-TAU-INTERVAL, e.g.,
    
         (equal (tau-interval-dom (make-tau-interval dom lo-rel lo hi-rel hi)) dom)
    
    and then disable those functions to avoid seeing excessive cars and
    cdrs.
    
    When dealing with bounders defined in the direct, arithmetic style, we
    tend to keep TAU-INTERVALP and IN-TAU-INTERVALP enabled so they unfold
    and expose the algebra.
    
    When dealing with bounders defined compositionally in terms of other
    verified bounders, we tend to keep TAU-INTERVALP and IN-TAU-INTERVALP
    disabled so we can rely on the previously proved bounder theorems as
    rewrite and forward chaining rules.
    
    Note that this last remark means that when you prove bounder correctness
    theorems you should include corollaries that are useful :rewrite and
    possibly :forward-chaining rules if you anticipate using that bounder in
    more complex ones.  We tend to trigger the forward chaining with the
    bounder expression itself, rather than one of the hypotheses.  For
    example in the rule above for bounder-for-* we would include
    (:forward-chaining :trigger-terms ((tau-bounder-expt2 int2))) and let
    the in-tau-intervalp hypotheses select the free variables x and y.
    
    
    File: acl2-doc-emacs.info,  Node: IN-TAU-INTERVALP,  Next: MAKE-TAU-INTERVAL,  Prev: BOUNDERS,  Up: TAU-SYSTEM
    
    IN-TAU-INTERVALP    Boolean membership in a tau interval
    
         General Form:
         (in-tau-intervalp e x)
    
    Here, x should be an interval (see tau-intervalp).  This function
    returns t or nil indicating whether e, which is generally but not
    necessarily a number, is an element of interval x.  By that is meant
    that e satisfies the domain predicate of the interval and lies between
    the two bounds.
    
    Suppose x is an interval with the components dom, lo-rel, lo, hi-rel
    and hi.  Suppose (= (demodulize a lst 'value ans) 0))
              (equal (demodulize a lst 'value ans) ans)))
           :rule-classes :type-prescription)
    
    To specify the term whose type (see *note TYPE-SET::) is described by
    the rule, provide that term as the value of the :typed-term field of
    the rule class object.
    
         General Form (after preprocessing; see below):
         (implies hyps
                  (or type-restriction1-on-pat
                      ...
                      type-restrictionk-on-pat
                      (equal pat var1)
                      ...
                      (equal pat varj)))
    
    where pat is the application of some function symbol to some arguments,
    each type-restrictioni-on-pat is a term involving pat and containing no
    variables outside of the occurrences of pat, and each vari is one of
    the variables of pat.  Generally speaking, the type-restriction terms
    ought to be terms that inform us as to the type of pat.  Ideally, they
    should be "primitive recognizing expressions" about pat; see *note
    COMPOUND-RECOGNIZER::.  We describe preprocessing at the end of this
    topic.
    
    If the :typed-term is not provided in the rule class object, it is
    computed heuristically by looking for a term in the conclusion whose
    type is being restricted.  An error is caused if no such term is found.
    
    Roughly speaking, the effect of adding such a rule is to inform the ACL2
    typing mechanism that pat has the type described by the conclusion, when
    the hypotheses are true.  In particular, the type of pat is within the
    union of the types described by the several disjuncts.  The "type
    described by" (equal pat vari) is the type of vari.
    
    More operationally, when asked to determine the type of a term that is
    an instance of pat, ACL2 will first attempt to establish the hypotheses.
    *This is done by type reasoning alone, not rewriting!*  However, if some
    hypothesis is a call of force, then forcing may occur, which may
    ultimately invoke the rewriter; see *note FORCE:: and see *note
    CASE-SPLIT::.  So-called free variables in hypotheses are treated
    specially; see *note FREE-VARIABLES::.  Provided the hypotheses are
    established by type reasoning, ACL2 then unions the types described by
    the type-restrictioni-on-pat terms together with the types of those
    subexpressions of pat identified by the vari.  The final type computed
    for a term is the intersection of the types implied by each applicable
    rule.  Type prescription rules may be disabled.
    
    You can limit the recursive establishment of hypotheses of rules; see
    *note SET-BACKCHAIN-LIMIT::.
    
    Because only type reasoning is used to establish the hypotheses of
    :type-prescription rules, some care must be taken with the hypotheses.
    Suppose, for example, that the non-recursive function my-statep is
    defined as
    
           (defun my-statep (x)
             (and (true-listp x)
                  (equal (len x) 2)))
    
    and suppose (my-statep s) occurs as a hypothesis of a
    :type-prescription rule that is being considered for use in the proof
    attempt for a conjecture with the hypothesis (my-statep s).  Since the
    hypothesis in the conjecture is rewritten, it will become the
    conjunction of (true-listp s) and (equal (len s) 2).  Those two terms
    will be assumed to have type t in the context in which the
    :type-prescription rule is tried.  But type reasoning will be unable to
    deduce that (my-statep s) has type t in this context.  Thus, either
    my-statep should be disabled (see *note DISABLE::) during the proof
    attempt or else the occurrence of (my-statep s) in the
    :type-prescription rule should be replaced by the conjunction into
    which it rewrites.
    
    While this example makes it clear how non-recursive predicates can cause
    problems, non-recursive functions in general can cause problems.  For
    example, if (mitigate x) is defined to be (if (rationalp x) (1- x) x)
    then the hypothesis (pred (mitigate s)) in the conjecture will rewrite,
    opening mitigate and splitting the conjecture into two subgoals, one in
    which (rationalp s) and (pred (1- x)) are assumed and the other in
    which (not (rationalp s)) and (pred x) are assumed.  But (pred
    (mitigate s)) will not be typed as t in either of these contexts.  The
    moral is: beware of non-recursive functions occuring in the hypotheses
    of :type-prescription rules.
    
    Because of the freedom one has in forming the conclusion of a
    type-prescription, we have to use heuristics to recover the pattern,
    pat, whose type is being specified.  In some cases our heuristics may
    not identify the intended term and the :type-prescription rule will be
    rejected as illegal because the conclusion is not of the correct form.
    When this happens you may wish to specify the pat directly.  This may
    be done by using a suitable rule class token.  In particular, when the
    token :type-prescription is used it means ACL2 is to compute pat with
    its heuristics; otherwise the token should be of the form
    (:type-prescription :typed-term pat), where pat is the term whose type
    is being specified.
    
    The defun event may generate a :type-prescription rule.  Suppose fn is
    the name of the function concerned.  Then (:type-prescription fn) is
    the rune given to the type-prescription, if any, generated for fn by
    defun.  (The trivial rule, saying fn has unknown type, is not stored,
    but defun still allocates the rune and the corollary of this rune is
    known to be t.)
    
    We close with a discussion of how, before a term is parsed into a
    :type-prescription rule, it is preprocessed.  We describe this
    preprocessing in some detail below, but first consider the following
    (contrived) example.
    
         (defthm append-tp-example
           (let ((result (append x y)))
             (implies (nat-listp x)
                      (implies (let ((second-hyp (integer-listp y)))
                                 second-hyp)
                               (true-listp result))))
           :rule-classes :type-prescription)
    
    This theorem is parsed into a type-prescription rule with the following
    hypotheses and conclusion.
    
         (nat-listp x) ; first hypothesis
         ((lambda (second-hyp) second-hyp) (integer-listp y)) ; second hypothesis
         (true-listp (binary-append x y)) ; conclusion
    
    Notice that the top-level LET was expanded, i.e., (append x y) was
    substituted for result -- more accurately, (binary-append x y) was
    substituted for result, since append is a macro that abbreviates
    binary-append.  Also notice that the two hypotheses were "flattened" in
    the sense that they were gathered up into a list.  Finally, notice that
    the LET in the second hypothesis was not expanded (it was merely
    translated to internal form, using LAMBDA).  If you actually submit the
    theorem above, you will get warnings, which you may choose to ignore;
    the application of type-prescription rules is somewhat subtle, so if
    you use them then you may wish to experiment to see which forms work
    best for you.
    
    Here is the detail promised above, for parsing a term into a
    :type-prescription rule.  There are two steps.  (1) ACL2 first
    translates the term, expanding all macros (see *note TRANS::) and also
    expanding away calls of all so-called "guard holders," mv-list and
    return-last (the latter resulting for example from calls of prog2$,
    mbe, or ec-call), as well as expansions of the macro `the'.  (2) Then
    the the translated term is traversed top-down, expanding away lambdas
    (let, let*, and mv-let expressions) and flattening the IMPLIES
    structure, until the conclusion is exposed; then the conclusion's
    lambdas are also expanded away.  The simplest way to understand (2) may
    be to look at the definition of ACL2 source function unprettyify-tp,
    which implements Step (2), say by evaluating :pe unprettyify-tp.
    
    
    File: acl2-doc-emacs.info,  Node: TYPE-SET-INVERTER,  Next: WELL-FOUNDED-RELATION,  Prev: TYPE-PRESCRIPTION,  Up: RULE-CLASSES
    
    TYPE-SET-INVERTER    exhibit a new decoding for an ACL2 type-set
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example Rule Class:
         (:type-set-inverter
           :corollary (equal (and (counting-number x) (not (equal x 0)))
                             (and (integerp x) (< 0 x)))
           :type-set 2)
    
         General Forms of Rule Class:
         :type-set-inverter, or
         (:type-set-inverter :type-set n)
    
         General Form of Theorem or Corollary:
         (EQUAL new-expr old-expr)
    
    where n is a type-set (see *note TYPE-SET::) and old-expr is the term
    containing x as a free variable that ACL2 currently uses to recognize
    type-set n.  For a given n, the exact form of old-expr is generated by
    
         (convert-type-set-to-term 'x n (ens state) (w state) nil)].
    
    If the :type-set field of the rule-class is omitted, we attempt to
    compute it from the right-hand side, old-expr, of the corollary.  That
    computation is done by type-set-implied-by-term (see *note TYPE-SET::).
    However, it is possible that the type-set we compute from lhs does not
    have the required property that when inverted with
    convert-type-set-to-term the result is lhs.  If you omit :type-set and
    an error is caused because lhs has the incorrect form, you should
    manually specify both :type-set and the lhs generated by
    convert-type-set-to-term.
    
    The rule generated will henceforth make new-expr be the term used by
    ACL2 to recognize type-set n.  If this rule is created by a defthm
    event named name then the rune of the rule is (:type-set-inverter name)
    and by disabling that rune you can prevent its being used to decode
    type-sets.
    
    Type-sets are inverted when forced assumptions are turned into formulas
    to be proved.  In their internal form, assumptions are essentially
    pairs consisting of a context and a goal term, which was forced.
    Abstractly a context is just a list of hypotheses which may be assumed
    while proving the goal term.  But actually contexts are alists which
    pair terms with type-sets, encoding the current hypotheses.  For
    example, if the original conjecture contained the hypothesis (integerp
    x) then the context used while working on that conjecture will include
    the assignment to x of the type-set *ts-integer*.
    
    
    File: acl2-doc-emacs.info,  Node: WELL-FOUNDED-RELATION,  Prev: TYPE-SET-INVERTER,  Up: RULE-CLASSES
    
    WELL-FOUNDED-RELATION    show that a relation is well-founded on a set
    
    See *note RULE-CLASSES:: for a general discussion of rule classes,
    including how they are used to build rules from formulas and a
    discussion of the various keywords in a rule class description.
    
         Example:
         (defthm lex2p-is-well-founded-relation
           (and (implies (pairp x) (o-p (ordinate x)))
                (implies (and (pairp x)
                              (pairp y)
                              (lex2p x y))
                         (o< (ordinate x) (ordinate y))))
           :rule-classes :well-founded-relation)
    
    The example above creates a :well-founded-relation rule, where of course
    the functions pairp, lex2p, and ordinate would have to be defined
    first.  It establishes that lex2p is a well-founded relation on pairps.
    We explain and give details below.
    
    Exactly two general forms are recognized:
    
         General Forms
         (AND (IMPLIES (mp x) (O-P (fn x)))              ; Property 1
              (IMPLIES (AND (mp x)                       ; Property 2
                            (mp y)
                            (rel x y))
                       (O< (fn x) (fn y)))),
    
    or
    
         (AND (O-P (fn x))                               ; Property 1
              (IMPLIES (rel x y)                         ; Property 2
                       (O< (fn x) (fn y))))
    
    where mp, fn, and rel are function symbols, x and y are distinct
    variable symbols, and no other :well-founded-relation theorem about fn
    has been proved.  When the second general form is used, we act as
    though the first form were used with mp being the function that ignores
    its argument and returns t.  The discussion below therefore considers
    only the first general form.
    
    Note: We are very rigid when checking that the submitted formula is of
    one of these forms.  For example, in the first form, we insist that all
    the conjuncts appear in the order shown above.  Thus, interchanging the
    two properties in the top-level conjunct or rearranging the hyptheses
    in the second conjunct both produce unrecognized forms.  The
    requirement that each fn be proved well-founded at most once ensures
    that for each well-founded relation, fn, there is a unique mp that
    recognizes the domain on which rel is well-founded.  We impose this
    requirement simply so that rel can be used as a short-hand when
    specifying the well-founded relations to be used in definitions;
    otherwise the specification would have to indicate which mp was to be
    used.
    
    We also insist that the new ordering be embedded into the ordinals as
    handled by o-p and o< and not some into previously admitted user-defined
    well-founded set and relation.  This restriction should pose no
    hardship.  If mp and rel were previously shown to be well-founded via
    the embedding fn, and you know how to embed some new set and relation
    into mp and rel, then by composing fn with your new embedding and using
    the previously proved well-founded relation lemma you can embed the new
    set and relation into the ordinals.
    
    Mp is a predicate that recognizes the objects that are supposedly
    ordered in a well-founded way by rel.  We call such an object an
    "mp-measure" or simply a "measure" when mp is understood.  Property 1
    tells us that every measure can be mapped into an ACL2 ordinal.  (See
    *note O-P::.)  This mapping is performed by fn.  Property 2 tells us
    that if the measure x is smaller than the measure y according to rel
    then the image of x under fn is a smaller than that of y, according to
    the well-founded relation o<.  (See *note O<::.)  Thus, the general
    form of a :well-founded-relation formula establishes that there exists a
    rel-order preserving embedding (namely via fn) of the mp-measures into
    the ordinals.  We can thus conclude that rel is well-founded on
    mp-measures.
    
    Such well-founded relations are used in the admissibility test for
    recursive functions, in particular, to show that the recursion
    terminates.  To illustrate how such information may be used, consider a
    generic function definition
    
         (defun g (x) (if (test x) (g (step x)) (base x))).
    
    If rel has been shown to be well-founded on mp-measures, then g's
    termination can be ensured by finding a measure, (m x), with the
    property that m produces a measure:
    
         (mp (m x)),                                     ; Defun-goal-1
    
    and that the argument to g gets smaller (when measured by m and
    compared by rel) in the recursion,
    
         (implies (test x) (rel (m (step x)) (m x))).    ; Defun-goal-2
    
    If rel is selected as the :well-founded-relation to be used in the
    definition of g, the definitional principal will generate and attempt
    to prove defun-goal-1 and defun-goal-2 to justify g.  We show later why
    these two goals are sufficient to establish the termination of g.
    Observe that neither the ordinals nor the embedding, fn, of the
    mp-measures into the ordinals is involved in the goals generated by the
    definitional principal.
    
    Suppose now that a :well-founded-relation theorem has been proved for
    mp and rel.  How can rel be "selected as the :well-founded-relation" by
    defun?  There are two ways.  First, an xargs keyword to the defun event
    allows the specification of a :well-founded-relation.  Thus, the
    definition of g above might be written
    
         (defun g (x)
          (declare (xargs :well-founded-relation (mp . rel)))
          (if (test x) (g (step x)) (base x)))
    
    Alternatively, rel may be specified as the
    :default-well-founded-relation in acl2-defaults-table by executing the
    event
    
         (set-well-founded-relation rel).
    
    When a defun event does not explicitly specify the relation in its
    xargs the default relation is used.  When ACL2 is initialized, the
    default relation is o<.
    
    Finally, though it is probably obvious, we now show that defun-goal-1
    and defun-goal-2 are sufficient to ensure the termination of g provided
    property-1 and property-2 of mp and rel have been proved.  To this end,
    assume we have proved defun-goal-1 and defun-goal-2 as well as
    property-1 and property-2 and we show how to admit g under the
    primitive ACL2 definitional principal (i.e., using only the ordinals).
    In particular, consider the definition event
    
         (defun g (x)
          (declare (xargs :well-founded-relation o<
                          :measure (fn (m x))))
          (if (test x) (g (step x)) (base x)))
    
    Proof that g is admissible:  To admit the definition of g above we must
    prove
    
         (o-p (fn (m x)))                        ; *1
    
    and
    
         (implies (test x)                               ; *2
                  (o< (fn (m (step x))) (fn (m x)))).
    
    But *1 can be proved by instantiating property-1 to get
    
         (implies (mp (m x)) (o-p (fn (m x)))),
    
    and then relieving the hypothesis with defun-goal-1, (mp (m x)).
    
    Similarly, *2 can be proved by instantiating property-2 to get
    
         (implies (and (mp (m (step x)))
                       (mp (m x))
                       (rel (m (step x)) (m x)))
                  (o< (fn (m (step x))) (fn (m x))))
    
    and relieving the first two hypotheses by appealing to two instances of
    defun-goal-1, thus obtaining
    
         (implies (rel (m (step x)) (m x))
                  (o< (fn (m (step x))) (fn (m x)))).
    
    By chaining this together with defun-goal-2,
    
         (implies (test x)
                  (rel (m (step x)) (m x)))
    
    we obtain *2.  Q.E.D.
    
    
    File: acl2-doc-emacs.info,  Node: SERIALIZE,  Next: STOBJ,  Prev: RULE-CLASSES,  Up: Top
    
    SERIALIZE    routines for saving ACL2 objects to files, and later restoring them
    
    This documentation topic relates to an experimental extension of ACL2
    that supports hons, memoization, and fast alists.  See *note
    HONS-AND-MEMOIZATION::.  Thanks to Jared Davis for contributing the
    "serialization" routines for saving ACL2 objects in files for later
    loading.
    
    We implement some routines for writing arbitrary ACL2 objects to files,
    and for loading those files later.  We usually call these ".sao" files,
    which stands for (S)erialized (A)CL2 (O)bject.
    
    Our serialization scheme uses a compact, binary format that preserves
    structure sharing in the original object.  We optimize for read
    performance.
    
    * Menu:
    
    * SERIALIZE-ALTERNATIVES:: alternatives to the serialize routines
    
    * SERIALIZE-IN-BOOKS:: using serialization efficiently in books
    
    * SERIALIZE-READ:: read a serialized ACL2 object from a file
    
    * SERIALIZE-WRITE:: write an ACL2 object into a file
    
    * SET-SERIALIZE-CHARACTER:: See *note WITH-SERIALIZE-CHARACTER::.
    
    * WITH-SERIALIZE-CHARACTER:: control output mode for print-object$
    
    
    File: acl2-doc-emacs.info,  Node: SERIALIZE-ALTERNATIVES,  Next: SERIALIZE-IN-BOOKS,  Prev: SERIALIZE,  Up: SERIALIZE
    
    SERIALIZE-ALTERNATIVES    alternatives to the serialize routines
    
    Hons users could previously use the routines compact-print-file and
    compact-read-file.  These are deprecated and are no longer built into
    ACL2.  However, they are still available by loading the new community
    book, serialize/compact-print.  Note that loading this book requires a
    ttag, and these routines are still only available in raw lisp.
    
    Another predecessor of the serialization routines were hons archives,
    which are still available in the hons-archive library.  The
    serialization routines are generally better and we recommend against
    using hons archives for new projects.
    
    
    File: acl2-doc-emacs.info,  Node: SERIALIZE-IN-BOOKS,  Next: SERIALIZE-READ,  Prev: SERIALIZE-ALTERNATIVES,  Up: SERIALIZE
    
    SERIALIZE-IN-BOOKS    using serialization efficiently in books
    
    Our serialize scheme was developed in order to allow very large ACL2
    objects to be loaded into books.  Ordinarily this is carried out using
    serialize-read within a make-event, e.g.,
    
           (make-event (mv-let (obj state)
                               (serialize-read "my-file")
                               (value `(defconst *my-file* ',obj))))
    
    But this scheme is not particularly efficient.
    
    During certify-book, the actual call of serialize-read is carried out,
    and this is typically pretty fast.  But then a number of possibly
    inefficient things occur.
    
         - The ACL2 function bad-lisp-object is run on the resulting
         object.  This is memoized for efficiency, but may still take
         considerable time when the file is very large.
    
         - The checksum of the resulting object is computed.  This is also
         memoized, but as before may still take some time.
    
         - The object that was just read is then written into book.cert,
         essentially with serialize-write.  This can take some time, and
         results in large certifiate files.
    
    Then, during include-book, the make-event expansion of is loaded.  This
    is now basically just a serialize-read.
    
    The moral of the story is that using serialize will only help your
    certify-book time, and it only impacts a portion of the overall time.
    
    To avoid this overhead, we have developed an UNSOUND alternative to
    serialize-read, which is available only by loading an additional book.
    So, if the above scheme is not performing well for you, you may wish to
    see the community book serialize/unsound-read.
    
    
    File: acl2-doc-emacs.info,  Node: SERIALIZE-READ,  Next: SERIALIZE-WRITE,  Prev: SERIALIZE-IN-BOOKS,  Up: SERIALIZE
    
    SERIALIZE-READ    read a serialized ACL2 object from a file
    
    General form:
    
          (serialize-read filename
                          [:hons-mode {:always, :never, :smart}]   ; :smart by default
                          [:verbosep  {t, nil}])                   ; nil by default
           -->
          (mv obj state)
    
    In the logic this is an oracle read.
    
    Under the hood, we try to read and return a serialized object from a
    file that was presumably created by serialize-write.  On success we
    return the contents of the file.  Any failures (e.g., file not found,
    bad file contents, etc.) will result in a hard Lisp error.
    
    The filename should be a string that gives the path to the file.
    
    The hons-mode controls how whether to use hons or cons to restore the
    object.  The default mode is :smart, which means that conses that were
    normed at the time of the file's creation should be restored with hons.
    But you can override this and insist that hons is to :always or :never
    be used, instead.
    
    Why would you use :never?  If your object previously had a lot of
    honses, but you no longer have any need for them to be normed, then
    using :never may sometimes be a lot faster since it can avoid hons
    calls.  On the other hand, if you are going to hons-copy some part of
    the file's contents, then it is likely faster to use :smart or :always
    instead of first creating normal conses and then copying them to build
    honses.
    
    The :verbosep flag just controls whether to print some low-level details
    related to timing and memory usage as the file is being read.
    
    
    File: acl2-doc-emacs.info,  Node: SERIALIZE-WRITE,  Next: SET-SERIALIZE-CHARACTER,  Prev: SERIALIZE-READ,  Up: SERIALIZE
    
    SERIALIZE-WRITE    write an ACL2 object into a file
    
    General form:
    
          (serialize-write filename obj
                           [:verbosep  {t, nil}])    ; nil by default
           -->
          state
    
    In the logic this carries out an oracle read.
    
    Under the hood, we try to save obj into the file indicated by filename,
    which must be a string.  The object can later be recovered with
    serialize-read.  We just return state, and any failures (e.g., file not
    openable) will result in a hard Lisp error.
    
    Writing objects to disk is generally slower than reading them back in
    since some analysis is required to convert an object into our
    serialized object format.
    
    The verbosep flag just says whether to print some low-level details
    related to timing and memory usage as the file is being read.
    
    
    File: acl2-doc-emacs.info,  Node: SET-SERIALIZE-CHARACTER,  Next: WITH-SERIALIZE-CHARACTER,  Prev: SERIALIZE-WRITE,  Up: SERIALIZE
    
    SET-SERIALIZE-CHARACTER    See *note WITH-SERIALIZE-CHARACTER::.
    
    
    File: acl2-doc-emacs.info,  Node: WITH-SERIALIZE-CHARACTER,  Prev: SET-SERIALIZE-CHARACTER,  Up: SERIALIZE
    
    WITH-SERIALIZE-CHARACTER    control output mode for print-object$
    
    This documentation topic relates to an experimental extension of ACL2
    that supports hons, memoization, and fast alists.  See *note
    HONS-AND-MEMOIZATION::.  See *note SERIALIZE:: for a discussion of
    "serialization" routines, contributed by Jared Davis for saving ACL2
    objects in files for later loading.
    
    The expression (with-serialize-character char form) evaluates to the
    value of form, but with the serialize-character of the state assigned to
    char, which should be one of nil, #\Y, or #\Z.  We describe the effect
    of that assignment below.  But note that if you are doing this because
    of one or more specific calls of print-object$, such as (print-object$
    x channel state), then you may wish instead to evaluate
    (print-object$-ser x serialize-character channel state), in which case
    you will not need to use with-serialize-character.  (Note however that
    serialize-character is treated as nil for other than a HONS version.)
    
         General forms:
         (with-serialize-character nil form)
         (with-serialize-character #Y form)
         (with-serialize-character #Z form)
    
    where form should evaluate to an error triple (see *note
    ERROR-TRIPLES::).
    
    Note that if you prefer to obtain the same behavior (as described below)
    globally, rather than only within the scope of with-serialize-character,
    then use set-serialize-character in a corresponding manner:
    
         (set-serialize-character nil state)
         (set-serialize-character #Y state)
         (set-serialize-character #Z state)
    
    In each case above, calls of print-object$ (see *note IO::) in form will
    produce an object that can be read by the HONS version of ACL2.  In the
    first case, that object is printed as one might expect at the terminal,
    as an ordinary Lisp s-expression.  But in the other cases, the object
    is printed by first laying down either #Y or #Z (as indicated) and then
    calling serialize-write (or more precisely, the underlying function
    called by serialize-write that prints to a stream).
    
    Consider what happens when the ACL2 reader encounters an object
    produced as described above (in the #Y or #Z case).  When the object
    was written, information was recorded on whether that object was a
    hons.  In the case of #Z, the object will be read as a hons if and only
    if it was originally written as a hons.  But in the case of #Y, it will
    never be read as a hons.  Thus, #Y and #Z will behave the same if the
    original written object was not a hons, creating an object that is not
    a hons.  For an equivalent explanation and a bit more discussion, see
    *note SERIALIZE-READ::, in particular the discussion of the hons-mode.
    The value :smart described there corresponds to #Z, while :never
    corresponds to #Y.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ,  Next: SWITCHES-PARAMETERS-AND-MODES,  Prev: SERIALIZE,  Up: Top
    
    STOBJ    single-threaded objects or "von Neumann bottlenecks"
    
    In ACL2, a "single-threaded object" is a data structure whose use is so
    syntactically restricted that only one instance of the object need ever
    exist and its fields can be updated by destructive assignments.
    
    Note: Novices are advised to avoid using single-threaded objects,
    perhaps instead using community book
    books/data-structures/structures.lisp.  At the least, consider using
    (set-verify-guards-eagerness 0) to avoid guard verification.
    
    The documentation in this section is laid out in the form of a tour that
    visits the documented topics in a reasonable order.  We recommend that
    you follow the tour the first time you read about stobjs.  The list of
    all stobj topics is shown below.  The tour starts immediately
    afterwards.  Also see *note DEFSTOBJ:: and, for so-called abstract
    stobjs, see *note DEFABSSTOBJ::.
    
    * Menu:
    
    * DECLARE-STOBJS:: declaring a formal parameter name to be a single-threaded object
    
    * NESTED-STOBJS:: using stobjs that contain stobjs
    
    * RESIZE-LIST:: list resizer in support of stobjs
    
    * STOBJ-EXAMPLE-1:: an example of the use of single-threaded objects
    
    * STOBJ-EXAMPLE-1-DEFUNS:: the defuns created by the counters stobj
    
    * STOBJ-EXAMPLE-1-IMPLEMENTATION:: the implementation of the counters stobj
    
    * STOBJ-EXAMPLE-1-PROOFS:: some proofs involving the counters stobj
    
    * STOBJ-EXAMPLE-2:: an example of the use of arrays in single-threaded objects
    
    * STOBJ-EXAMPLE-3:: another example of a single-threaded object
    
    * STOBJ-LET:: See *note NESTED-STOBJS::.
    
    * WITH-LOCAL-STATE:: locally bind state
    
    * WITH-LOCAL-STOBJ:: locally bind a single-threaded object
    
    
    Related topics other than immediate subtopics:
    * DEFABSSTOBJ:: define a new abstract single-threaded object
    
    * DEFSTOBJ:: define a new single-threaded object
    
    As noted, a "single-threaded object" is a data structure whose use is so
    syntactically restricted that only one instance of the object need ever
    exist.  Updates to the object must be sequentialized.  This allows us to
    update its fields with destructive assignments without wrecking the
    axiomatic semantics of update-by-copy.  For this reason,
    single-threaded objects are sometimes called "von Neumann bottlenecks."
    
    From the logical perspective, a single-threaded object is an ordinary
    ACL2 object, e.g., composed of integers and conses.  Logically
    speaking, ordinary ACL2 functions are defined to allow the user to
    "access" and "update" its fields.  Logically speaking, when fields in
    the object, obj, are "updated" with new values, a new object, obj', is
    constructed.
    
    But suppose that by syntactic means we could ensure that there were no
    more references to the "old" object, obj.  Then we could create obj' by
    destructively modifying the memory locations involved in the
    representation of obj.  The syntactic means is pretty simple but
    draconian: the only reference to obj is in the variable named OBJ.
    
    The consequences of this simple rule are far-reaching and require some
    getting used to.  For example, if OBJ has been declared as a
    single-threaded object name, then the following consequences ensue (but
    see the discussion of congruent stobjs below for a slight relaxation).
    
    o OBJ is a top-level global variable that contains the current object,
    obj.
    
    o If a function uses the formal parameter OBJ, the only "actual
    expression" that can be passed into that slot is the variable OBJ, not
    merely a term that "evaluates to an obj"; thus, such functions can only
    operate on the current object.  So for example, instead of (FOO
    (UPDATE-FIELD1 3 ST)) write (LET ((ST (UPDATE-FIELD1 3 ST))) (FOO ST)).
    
    o The accessors and updaters have a formal parameter named OBJ, so by
    the rule just above, those functions can only be applied to the current
    object.  The recognizer is the one exception to the rule: it may be
    applied either the OBJ or to an ordinary (non-stobj) object.
    
    o The ACL2 primitives, such as CONS, CAR and CDR, may not be applied to
    the variable OBJ.  Thus, for example, obj may not be consed into a list
    (which would create another pointer to it) or accessed or copied via
    "unapproved" means.
    
    o The updaters return a "new OBJ object", i.e., obj'; thus, when an
    updater is called, the only variable which can hold its result is OBJ.
    
    o If a function calls an OBJ updater, it must return an OBJ object
    (either as the sole value returned, or in (mv ... OBJ ...); see *note
    MV::).
    
    o When a top-level expression involving OBJ returns an OBJ object, that
    object becomes the new current value of OBJ.
    
    There are other functional languages supporting single-threadedness, for
    example Haskell's "monads" and Clean's "uniqueness type system".  Of
    course, ACL2 provides a theorem prover that can prove theorems that
    involve such constructs.
    
    Note that the syntactic restrictions noted above are enforced only when
    single-threaded objects are encountered directly in the top-level loop
    or are used in function definitions; the accessor and update functions
    for single-threaded objects may be used without restriction in formulas
    to be proved.  Since function evaluation is sometimes necessary during
    proofs, ACL2 must be able to evaluate these functions on logical
    constants representing the object, even when the constant is not "the
    current object."  Thus, ACL2 supports both the efficient von Neumann
    semantics and the clean applicative semantics, and uses the first in
    contexts where execution speed is paramount and the second during
    proofs.
    
    Defstobj and defabsstobj events introduce stobjs.  See *note DEFSTOBJ::
    for more details about stobjs.  In particular, a relatively advanced
    notion of "congruent stobjs" is discussed there.  The idea is to allow
    a stobj, st2, of the same "shape" as a given stobj, st1, to be used in
    place of st1.  Other defstobj keywords allow inlining and renaming of
    stobj accessors and updaters.
    
    But we are getting ahead of ourselves.  To start the stobj tour, see
    *note STOBJ-EXAMPLE-1::.
    
    
    File: acl2-doc-emacs.info,  Node: DECLARE-STOBJS,  Next: NESTED-STOBJS,  Prev: STOBJ,  Up: STOBJ
    
    DECLARE-STOBJS    declaring a formal parameter name to be a single-threaded object
    
    When a defun uses one of its formals as a single-threaded object
    (stobj), the defun _must_ include a declaration that the formal is to
    be so used.  An exception is the formal "state," which if not declared
    as explained below, may still be used provided an appropriate global
    "declaration" is issued: see *note SET-STATE-OK::.
    
    If the formal in question is counters then an appropriate declaration is
    
         (declare (xargs :stobjs counters))
    
    or, more generally,
    
         (declare (xargs :stobjs (... counters ...)))
    
    where all the single-threaded formals are listed.
    
    For such a declaration to be legal it must be the case that all the
    names have previously been defined as single-threaded objects with
    defstobj.
    
    When an argument is declared to be single-threaded the guard of the
    function is augmented by conjoining to it the condition that the
    argument satisfy the recognizer for the single-threaded object.
    Furthermore, the syntactic checks done to enforce the legal use of
    single-threaded objects are also sufficient to allow these guard
    conjuncts to be automatically proved.
    
    The obvious question arises:  Why does ACL2 insist that you declare
    stobj names before using them in defuns if you can only declare names
    that have already been defined with defstobj?  What would go wrong if a
    formal were treated as a single-threaded object if and only if it had
    already been so defined?
    
    Suppose that one user, say Jones, creates a book in which counters is
    defined as a single-threaded object.  Suppose another user, Smith,
    creates a book in which counters is used as an ordinary formal
    parameter.  Finally, suppose a third user, Brown, wishes to use both
    books.  If Brown includes Jones' book first and then Smith's, then
    Smith's function treats counters as single-threaded.  But if Brown
    includes Smith's book first, the argument is treated as ordinary.
    
    ACL2 insists on the declaration to ensure that the definition is
    processed the same way no matter what the context.
    
    acl2-sources/doc/EMACS/acl2-doc-emacs.info-140000664002132200015000000110571412222333542017730 0ustar  kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from
    acl2-doc-emacs.texinfo.
    
    This is documentation for ACL2 Version 6.3
    Copyright (C) 2013  University of Texas at Austin
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of Version 2 of the GNU General Public License as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Written by:  Matt Kaufmann and J Strother Moore
    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.
    
    INFO-DIR-SECTION Math
    START-INFO-DIR-ENTRY
    * acl2: (acl2-doc-emacs). Applicative Common Lisp
    END-INFO-DIR-ENTRY
    
    
    File: acl2-doc-emacs.info,  Node: NESTED-STOBJS,  Next: RESIZE-LIST,  Prev: DECLARE-STOBJS,  Up: STOBJ
    
    NESTED-STOBJS    using stobjs that contain stobjs
    
    For this topic we assume that you already understand the basics of
    single-threaded objects in ACL2.  See *note STOBJ::, and in particular,
    see *note DEFSTOBJ::.  The latter topic defers discussion of the
    ability to specify a stobj field that is itself a stobj or an array of
    stobjs.  That discussion is the subject of the present documentation
    topic.
    
    Our presentation is in four sections.  First we augment the
    documentation for defstobj by explaining how stobjs may be specified
    for fields in a new stobj definition.  Then we explain an aliasing
    problem, which accounts for a prohibition against making direct calls
    to accessors and updaters involving stobj fields of stobjs.  Next, we
    introduce an ACL2 primitive, stobj-let, which provides the only way to
    read and write stobj components of stobjs.  The final section provides
    precise documentation for stobj-let.
    
    See also ACL2 community book demos/modeling/nested-stobj-toy-isa.lisp
    for a worked example, which applies nested stobj structures to defining
    interpreters.  A variety of small additional examples may be found in
    ACL2 community book misc/nested-stobj-tests.lisp.  For further
    discussion, you are welcome to read the "Essay on Nested Stobjs", a
    long comment in ACL2 source file other-events.lisp.  However, this
    documentation topic is intended to be self-contained for those familiar
    with stobjs.
    
    SECTION: Extension of defstobj to permit stobjs within stobjs
    
    Recall that the :type keyword of a defstobj field descriptor can be a
    "type-indicator" that specifies the type of the field as a type-spec
    (see *note TYPE-SPEC::).  For example, the following specifies an
    integer field and a field that is an array of bytes.
    
           (defstobj st
             (int-field :type integer :initially 0)
             (ar-field :type (array unsigned-byte (10)) :initially 0))
    
    But the :type of a stobj field descriptor may instead be based on a
    stobj.  For example, the following sequence of three events is legal.
    The first field descriptor of top, named sub1-field, illustrates one
    new kind of value for :type: the name of a previously-introduced stobj,
    which here is sub1. The second field descriptor of top, named
    sub2-ar-field, illustrates the other new kind of value for :type: an
    array whose elements are specified by the name of a
    previously-introduced stobj, in this case, the stobj sub2.
    
           (defstobj sub1 fld1)
           (defstobj sub2 fld2)
           (defstobj top
             (sub1-field :type sub1)
             (sub2-ar-field :type (array sub2 (10))))
    
    The :initially keyword is illegal for fields whose :type is a stobj or
    an array of stobjs.  Each such initial value is provided by a
    corresponding call of the stobj creator for that stobj.  In particular,
    in the case of an array of stobjs, the stobj creator is called once for
    each element of the array, so that the array elements are distinct.
    For example, each element of sub2-ar-field in the example above is
    initially provided by a separate call of create-sub2.  Each initial
    element is thus unique, and in particular is distinct from the initial
    global value of the stobj.  Similarly, the initial global stobj for
    sub1 is distinct from the initial sub1-field field of the global stobj
    for top, as these result from separate calls of create-sub1.
    
    When a stobj is used in a field of another stobj, we may refer to the
    former field as a "child stobj" and the latter stobj as a "parent
    stobj".  So in the example above, sub1-field is a child stobj of type
    sub1 for parent stobj top, and sub2-ar-field is an array of child
    stobjs of type sub2 for parent stobj top.  A child stobj has the same
    structural shape as the global stobj of its type, but as explained
    above, these are distinct structures.  We follow standard terminology
    by saying "isomorphic" to indicate the same structural shape.  So for
    example, (the value of) sub1-field is isomorphic to sub1, though these
    are distinct structures.
    
    SECTION: An aliasing problem
    
    Before introducing stobj-let below, we provide motivatation for this
    ACL2 primitive.
    
    Consider the following events.
    
           (defstobj child fld)
           (defstobj parent
             (fld2 :type child))
    
    Now suppose we could evaluate the following code, to be run immediately
    after admitting the two defstobj events above.
    
           (let* ((child (fld2 parent))
                  (child (update-fld 3 child)))
             (mv child parent))
    
    Now logically there is no change to parent: parent is passed through
    unchanged.  We can indeed prove that fact!
    
           (thm (equal (mv-nth 1
                               (let* ((child (fld2 parent))
                                      (child (update-fld 3 child)))
                                 (mv child parent)))
                       parent))
    
    But recall that stobjs are updated with destructive assignments.  That
    is, we really are updating (fld2 parent) to be the new value of child,
    whether this is explained logically or not.  Thus, evaluation of the
    above let* form does in fact change the actual global stobj, parent.
    
    (Aside: Here is an explanation involving raw Lisp, for those who might
    find this useful.  We escape to raw Lisp and execute the following;
    note that *the-live-parent* is the Lisp variable representing the
    global value of parent.
    
         (let ((parent *the-live-parent*))
           (let* ((child (fld2 parent))
                  (child (update-fld 4 child)))
             (mv child parent)))
    
    Then, in raw Lisp, (fld (fld2 *the-live-parent*)) evaluates to 4,
    illustrating the destructive update.  End of Aside.)
    
    Such aliasing can permit a change to a child stobj to cause a
    logically-inexplicable change to the parent stobj.  Similarly,
    unfettered accessing of stobj fields can result in logically
    inexplicable changes to the child stobj when the parent stobj is
    changed.  Thus, ACL2 disallows direct calls of stobj accessors and
    updaters for fields whose :type is a stobj or an array of stobjs.
    Instead, ACL2 provides stobj-let for reading and writing such fields in
    a sound manner.
    
    SECTION: Accessing and updating stobj fields of stobjs using stobj-let
    
    ACL2 provides a primitive, stobj-let, to access and update stobj fields
    of stobjs, in a manner that avoids the aliasing problem discussed
    above.  In this section we provide an informal introduction to
    stobj-let, using examples, to be followed in the next section by
    precise documentation.
    
    We begin by returning to a slight variant of the example above.
    
           (defstobj child fld)
           (defstobj parent
             (fld2 :type child)
             fld3)
    
    The following form returns the result of updating the fld2 field of
    parent, which is a stobj isomorphic to child, to have a value of 3.
    Below we explain the terms "bindings", "producer variables",
    "producer", and "consumer", as well as how to understand this form.
    
           (stobj-let
            ((child (fld2 parent)))  ; bindings
            (child)                  ; producer variable(s)
            (update-fld 3 child)     ; producer
            (update-fld3 'a parent)) ; consumer
    
    The four lines under "stobj-let" just above can be understood as
    follows.
    
         o Bindings:
             Bind child to (fld2 parent).
         o Producer variable(s) and producer:
             Then bind the variable, child, to
             the value of the producer, (update-fld 3 child).
         o Implicit update of parent:
             Update fld2 of parent with the producer variable, child.
         o Consumer:
             Finally, return (update-fld3 'a parent).
    
    Thus, the logical expansion of the stobj-let form above is the following
    expression, though this is approximate (see below).
    
           (let ((child (fld2 parent))) ; bindings
             (let ((child (update-fld 3 child))) ; bind producer vars to producer
               (let ((parent (update-fld2 child parent))) ; implicit update of parent
                 (update-fld3 'a parent))))
    
    The bindings always bind distinct names to child stobjs of a unique
    parent stobj, where the child stobj corresponds to the :type associated
    with the indicated accessor in the defstobj form for the parent stobj.
    Thus in this case, for the unique binding, variable child is bound to
    the stobj of `type' child for accessor fld2 of the parent stobj, parent.
    We refer to child from the bindings as a "stobj-let-bound variable".
    Note also that the "implicit extra step" mentioned above is generated by
    macroexpansion of stobj-let; it logically updates the parent with new
    child values, just before calling the consumer.  Implementation note:
    Destructive updating in raw Lisp lets us omit this implicit extra step.
    
    The form above is equivalent to the form displayed just below, which
    differs only in specifying an explicit stobj updater corresponding to
    the stobj accessor, fld2.  Here we show the default updater name, whose
    name has "UPDATE-" prepended to the name of the accessor.  But if the
    :RENAMING field of the defstobj event specified a different updater
    name corresponding to fld2, then that would need to be included where we
    have added update-fld2 below.
    
           (stobj-let
            ((child (fld2 parent) update-fld2)) ; bindings, including updater(s)
            (child)                  ; producer variables
            (update-fld 3 child)     ; producer
            (update-fld3 'a parent)) ; consumer
    
    You can experiment using :trans1 to see the single-step macroexpansion
    of a stobj-let form in the logic.  For example, here is how that works
    for a stobj-let form that binds three fields and updates two of them.
    Notice that because more than one field is updated, an mv-let form is
    generated to bind the two fields to their values returned by the
    producer, rather than a let form as previously generated.  First, let's
    introduce some events.
    
         (defstobj child1 child1-fld)
         (defstobj child2 child2-fld)
         (defstobj child3 child3-fld)
         (defstobj mom
           (fld1 :type child1)
           (fld2 :type child2)
           (fld3 :type child3))
         ; Silly stub:
         (defun update-last-op (op mom)
           (declare (xargs :stobjs mom))
           (declare (ignore op))
           mom)
         (defun new-mom (mom)
           (declare (xargs :stobjs mom))
           (stobj-let
            ((child1 (fld1 mom))
             (child2 (fld2 mom))
             (child3 (fld3 mom)))
            (child1 child3)
            (let* ((child1 (update-child1-fld 'one child1))
                   (child3 (update-child3-fld 'three child3)))
              (mv child1 child3))
            (update-last-op 'my-compute mom)))
    
    Now let's look at the single-step macroexpansion of the above stobj-let
    form.
    
         ACL2 !>:trans1 (stobj-let
                         ((child1 (fld1 mom))
                          (child2 (fld2 mom))
                          (child3 (fld3 mom)))
                         (child1 child3)
                         (let* ((child1 (update-child1-fld 'one child1))
                                (child3 (update-child3-fld 'three child3)))
                           (mv child1 child3))
                         (update-last-op 'my-compute mom))
          (PROGN$
           (LET
            ((CHILD1 (FLD1 MOM))
             (CHILD2 (FLD2 MOM))
             (CHILD3 (FLD3 MOM)))
            (DECLARE (IGNORABLE CHILD1 CHILD2 CHILD3))
            (MV-LET
               (CHILD1 CHILD3)
               (CHECK-VARS-NOT-FREE (MOM)
                                    (LET* ((CHILD1 (UPDATE-CHILD1-FLD 'ONE CHILD1))
                                           (CHILD3 (UPDATE-CHILD3-FLD 'THREE CHILD3)))
                                          (MV CHILD1 CHILD3)))
               (LET* ((MOM (UPDATE-FLD1 CHILD1 MOM))
                      (MOM (UPDATE-FLD3 CHILD3 MOM)))
                     (CHECK-VARS-NOT-FREE (CHILD1 CHILD2 CHILD3)
                                          (UPDATE-LAST-OP 'MY-COMPUTE MOM))))))
         ACL2 !>
    
    If you try to evaluate a stobj-let form directly in the top-level loop,
    rather than from within a function body, you will get an error.  The
    example above illustrates how stobj-let may be used in function bodies;
    here is another example, presented using an edited log.
    
           ACL2 !>(defstobj child fld)
    
           Summary
           Form:  ( DEFSTOBJ CHILD ...)
           Rules: NIL
           Time:  0.02 seconds (prove: 0.00, print: 0.00, other: 0.02)
            CHILD
           ACL2 !>(defstobj parent
                    (fld2 :type child)
                    fld3)
    
           Summary
           Form:  ( DEFSTOBJ PARENT ...)
           Rules: NIL
           Time:  0.02 seconds (prove: 0.00, print: 0.00, other: 0.02)
            PARENT
           ACL2 !>(defun f (parent)
                    (declare (xargs :stobjs parent))
                    (stobj-let
                     ((child (fld2 parent)))   ; bindings
                     (child)                   ; producer variables
                     (update-fld 3 child)      ; producer
                     (update-fld3 'a parent))) ; consumer
           [[output omitted]]
            F
           ACL2 !>(f parent)
           
           ACL2 !>(defun check-f (parent)
                    ; returns the value of the field of the child stobj
                    (declare (xargs :stobjs parent))
                    (stobj-let
                     ((child (fld2 parent))) ; bindings
                     (val)                   ; producer variables
                     (fld child)             ; producer
                     val))                   ; consumer
           [[output omitted]]
            CHECK-F
           ACL2 !>(check-f parent)
           3
           ACL2 !>
    
    Notice that the second function defined above, check-f, uses a
    stobj-let form that returns an ordinary value: it reads a value from a
    child stobj, but does not write to the child stobj, as indicated by the
    lack of a child stobj among the producer variables.  So for that
    stobj-let form, there is no implicit extra step.
    
    We labeled a stobj-let expansion above as "approximate" for two
    reasons, which we give here informally.  (Now you know how to apply
    :trans1 to that stobj-let call to see the precise expansion.)  First,
    stobj-let declares the stobj-let-bound variables to be ignorable for
    the top let bindings.  Second, and more importantly, stobj-let imposes
    the following restrictions on the producer and consumer, to avoid the
    aliasing problem: it disallows references to the parent stobj in the
    producer and it also disallows references to any bound stobj (i.e.,
    bound in the bindings) in the consumer.
    
    We conclude this section with examples based on a slight variation of
    the nested stobj example from the first section above.  These events
    can also be found in ACL2 community book misc/nested-stobj-tests.lisp,
    immediately under the following comment:
    
         ; As promised in :doc stobj-let, we begin with an example from that :doc.
    
    Note that some lemmas were needed in order to complete the guard proof
    for the function update-top, which may be found in the above file; they
    are omitted below.
    
    First we introduce three stobjs.
    
           (defstobj kid1 fld1)
           (defstobj kid2 fld2)
           (defstobj mom
             (kid1-field :type kid1)
             (kid2-ar-field :type (array kid2 (5)))
             last-op)
    
    The next function takes a given index and a mom stobj, and swaps the
    value stored in the stobj in mom's kid2-ar-field array at that index
    with the value stored in the stobj in mom's kid1-field field.
    
           (defun mom-swap-fields (index mom)
             (declare (xargs :stobjs mom
                             :guard (and (natp index)
                                         (< index (kid2-ar-field-length mom)))))
             (stobj-let
              ((kid1 (kid1-field mom))
               (kid2 (kid2-ar-fieldi index mom)))
              (kid1 kid2)
              (let* ((val1 (fld1 kid1))
                     (val2 (fld2 kid2))
                     (kid1 (update-fld1 val2 kid1))
                     (kid2 (update-fld2 val1 kid2)))
                (mv kid1 kid2))
              (update-last-op 'swap mom)))
    
    Function mom.kid1-fld1 stores a given value in the given mom's
    kid1-fld1 field.
    
           (defun mom.kid1-fld1 (val mom)
             (declare (xargs :stobjs mom))
             (stobj-let
              ((kid1 (kid1-field mom)))
              (kid1)
              (update-fld1 val kid1)
              (update-last-op val mom)))
    
    We next combine the two functions above, according to an op argument, as
    indicated by the following definition.
    
           (defun update-mom (op mom)
             (declare (xargs :stobjs mom))
             (cond ((and (consp op)
                         (eq (car op) 'swap)
                         (natp (cdr op))
                         (< (cdr op) (kid2-ar-field-length mom)))
                    (mom-swap-fields (cdr op) mom))
                   (t (mom.kid1-fld1 op mom))))
    
    The following checker function uses a stobj-let form like the ones
    above, a major difference being that the producer variable is not a
    stobj, since it does not modify the input stobj, mom.
    
           (defun check-update-mom (index val1 val2 last-op mom)
               (declare (xargs :stobjs mom
                               :mode :program
                               :guard
                               (or (null index)
                                   (and (natp index)
                                        (< index (kid2-ar-field-length mom))))))
               (and (equal (last-op mom) last-op)
                    (stobj-let
                     ((kid1 (kid1-field mom))
                      (kid2 (kid2-ar-fieldi index mom)))
                     (val) ; producer variables
                     (and (equal val1 (fld1 kid1))
                          (equal val2 (fld2 kid2)))
                     val)))
    
    Now let us run our update function to populate some fields within the
    mom stobj.
    
           (let* ((mom ; set mom to (3 (x0 x1 x2 x3 x4))
                    (update-mom 3 mom))
                   (mom ; set mom to (x1 (x0 3 x2 x3 x4))
                    (update-mom '(swap . 1) mom))
                   (mom ; set mom to (7 (x0 3 x2 x3 x4))
                    (update-mom 7 mom))
                   (mom ; set mom to (x0 (7 3 x2 x3 x4))
                    (update-mom '(swap . 0) mom))
                   (mom ; set mom to (5 (7 3 x2 x3 x4))
                    (update-mom 5 mom))
                   (mom ; set mom to (7 (5 3 x2 x3 x4))
                    (update-mom '(swap . 0) mom)))
              mom)
    
    Are the above values of 7, 5, and 3 as expected, with a last operation
    being a swap?  Yes!
    
           ACL2 !>(and (check-update-mom 0 7 5 'swap mom)
                       (check-update-mom 1 7 3 'swap mom))
           T
           ACL2 !>
    
    Notice that above, we never tried to access two different entries of the
    array.  This can be done, but we need to bind two different stobjs to
    those fields.  Fortunately, congruent stobjs make this possible; see
    *note DEFSTOBJ::, in particular the discussion of congruent stobjs.
    Since we want to bind two stobjs to values in the array that are
    isomorphic to the stobj kid2, we introduce a stobj congruent to kid2.
    
           (defstobj kid2a fld2a :congruent-to kid2)
    
    Then we can define our swapping function as follows.  The guard proof
    obligation includes the requirement that the two indices be distinct,
    again to avoid an aliasing problem.
    
           (defun mom-swap-indices (i1 i2 mom)
             (declare (xargs :stobjs mom
                             :guard (and (natp i1)
                                         (< i1 (kid2-ar-field-length mom))
                                         (natp i2)
                                         (< i2 (kid2-ar-field-length mom))
                                         (not (equal i1 i2)))))
             (stobj-let
              ((kid2 (kid2-ar-fieldi i1 mom))
               (kid2a (kid2-ar-fieldi i2 mom)))
              (kid2 kid2a)
              (let* ((val2 (fld2 kid2))
                     (val2a (fld2 kid2a))
                     (kid2 (update-fld2 val2a kid2))
                     (kid2a (update-fld2 val2 kid2a)))
                (mv kid2 kid2a))
              mom))
    
    The aforementioned community book, misc/nested-stobj-tests.lisp,
    contains a corresponding checker immediately following this definition.
    
    SECTION: Precise documentation for stobj-let
    
         General Form:
         (stobj-let
          BINDINGS
          PRODUCER-VARIABLES
          PRODUCER
          CONSUMER)
    
    where PRODUCER-VARIABLES is a non-empty true list of legal variable
    names without duplicates, PRODUCER and CONSUMER are expressions, and
    BINDINGS is a list subject to the following requirements.
    
    BINDINGS is a non-empty true list of tuples, each of which has the form
    (VAR ACCESSOR) or (VAR ACCESSOR UPDATER).  There is a stobj name, ST,
    previously introduced by defstobj (not defabsstobj), such that each
    accessor is of the form (ACC ST) or (ACCi I ST), with the same stobj
    name (ST) for each binding.  In the case (ACC ST), ACC is the accessor
    for a non-array field of ST.  In the case (ACCi I ST), ACCi is the
    accessor for an array field of ST, and I is either a variable, a
    natural number, a list (quote N) where N is a natural number, or a
    symbol introduced by defconst.  If UPDATER is supplied, then it is a
    symbol that is the name of the stobj updater for the field of ST
    accessed by ACCESSOR.  If UPDATER is not supplied, then for the
    discussion below we consider it to be, implicitly, the symbol in the
    same package as the function symbol of ACCESSOR (i.e., ACC or ACCi),
    obtained by prepending the string "UPDATE-" to the symbol-name of that
    function symbol.  Finally, ACCESSOR has a signature specifying a return
    value that is either ST or is stobj that is congruent to ST.
    
    If the conditions above are met, then the General Form expands to the
    one of the following expressions, depending on whether the list
    PRODUCER-VARIABLES has one member or more than one member, respectively.
    (But see below for extra code that may be inserted if there are stobj
    array accesses in BINDINGS.)  Here we write STOBJ-LET-BOUND-VARS for the
    list of variables VAR discussed above, i.e., for (strip-cars BINDINGS).
    And, we write UPDATES for the result of mapping through
    PRODUCER-VARIABLES and, for each variable VAR that has a binding (VAR
    ACCESSOR UPDATER) in BINDINGS (where UPDATER may be implicit, as
    discussed above), collect into UPDATES the tuple (ST (UPDATER VAR ST)).
    
    For PRODUCER-VARIABLES = (PRODUCER-VAR):
    
           (let BINDINGS
             (declare (ignorable . STOBJ-LET-BOUND-VARIABLES))
             (let ((PRODUCER-VAR PRODUCER))
               (let* UPDATES
                 CONSUMER)))
    
    Otherwise:
    
           (let BINDINGS
             (declare (ignorable . STOBJ-LET-BOUND-VARIABLES))
             (mv-let PRODUCER-VARS
                     PRODUCER
                     (let* UPDATES
                       CONSUMER)))
    
    Moreover, ACL2 places restrictions on the resulting expression: ST must
    not occur free in PRODUCER, and every variable in
    STOBJ-LET-BOUND-VARIABLES must not occur free in CONSUMER.
    
    Stobj-let forms can be evaluated using ordinary objects in theorem
    contexts, much as any form.  They can also, of course, appear in
    function bodis.  However, a stobj-let form cannot be evaluated directly
    in the top-level loop or other top-level contexts for execution (such
    as during make-event expansion).
    
    Finally, let FORM denote the form displayed above (either case).  We
    explain how FORM is actually replaced by an expression of the form
    (PROGN$ ... FORM).  This expression generates an extra guard proof
    obligation, which guarantees that no aliasing occurs from binding two
    stobj-let-bound variables to the same array access.  So fix a stobj
    array accessor ACCi for which some stobj is bound to (ACCi I ST) in
    BINDINGS; we define an expression ACCi-CHECK as follows.  Collect up
    all such index expressions I, where if I is of the form (quote N) then
    replace I by N.  If the resulting list of index expressions for ACCi
    consists solely of distinct numbers, or if it is of length 1, then no
    extra check is generated for ACCi.  Otherwise, let ACCi-CHECK be the
    form (chk-no-duplicatesp (list I1 ... Ik)), where I1, ..., Ik are the
    index expressions for ACCi.  Note: chk-no-duplicatesp is a function
    that returns nil, but has a guard that its argument is an eqlable-listp
    that satisfies no-duplicatesp.  Finally, FORM is replaced by (PROGN$
    CHK1 ... CHKn FORM), where the CHKm range over all of the above
    ACCi-CHECK.
    
    
    File: acl2-doc-emacs.info,  Node: RESIZE-LIST,  Next: STOBJ-EXAMPLE-1,  Prev: NESTED-STOBJS,  Up: STOBJ
    
    RESIZE-LIST    list resizer in support of stobjs
    
    (Resize-list lst n default-value) takes a list, lst, and a desired
    length, n, for the result list, as well as a default-value to use for
    the extra elements if n is greater than the length of lst.
    
    Resize-list has a guard of t.  This function is called in the body of
    function, resize- where  is an array field of a stobj.  See *note
    STOBJ:: and see *note DEFSTOBJ::.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-EXAMPLE-1,  Next: STOBJ-EXAMPLE-1-DEFUNS,  Prev: RESIZE-LIST,  Up: STOBJ
    
    STOBJ-EXAMPLE-1    an example of the use of single-threaded objects
    
    Suppose we want to sweep a tree and (1) count the number of interior
    nodes, (2) count the number of tips and (3) keep a record of every tip
    we encounter that is an integer.  We could use a single-threaded object
    as our "accumulator".  Such an object would have three fields, one
    holding the number of nodes seen so far, one holding the number of
    tips, and one holding all the integer tips seen.
    
    The following event declares counters to be a single-threaded object.
    
         (defstobj counters
           (NodeCnt     :type integer :initially 0)
           (TipCnt      :type integer :initially 0)
           (IntTipsSeen :type t       :initially nil))
    
    It has three fields, NodeCnt, TipCnt, and IntTipsSeen.  (As always in
    ACL2, capitalization is irrelevant in simple symbol names, so the first
    name could be written nodecnt or NODECNT, etc.) Those are the name of
    the accessor functions for the object.  The corresponding update
    functions are named update-NodeCnt, update-TipCnt and
    update-IntTipsSeen.
    
    If you do not like the default function names chosen above, there is a
    feature in the defstobj event that allows you to specify other names.
    
    If you want to see the ACL2 definitions of all the functions defined by
    this event, look at stobj-example-1-defuns.
    
    If, after this event, we evaluate the top-level "global variable"
    counters in the ACL2 read-eval-print loop we get:
    
         ACL2 !>counters
         
    
    Note that the value printed is "".  Actually, the value of
    counters in the logic is (0 0 NIL).  But ACL2 always prints
    single-threaded objects in this non-informative way because they are
    usually so big that to do otherwise would be unpleasant.
    
    Had you tried to evaluate the "global variable" counters before
    declaring it a single-threaded object, ACL2 would have complained that
    it does not support global variables.  So a lesson here is that once
    you have declared a new single-threaded object your top-level forms can
    reference it.  In versions of ACL2 prior to Version  2.4 the only
    variable enjoying this status was STATE.  single-threaded objects are a
    straightforward generalization of the long-implemented von Neumann
    state feature of ACL2.
    
    We can access the fields of counters as with:
    
         ACL2 !>(NodeCnt counters)
         0
         ACL2 !>(IntTipsSeen counters)
         NIL
    
    and we can set the fields of counters as with:
    
         ACL2 !>(update-NodeCnt 3 counters)
         
         ACL2 !>(NodeCnt counters)
         3
    
    Observe that when we evaluate an expression that returns a counter
    object, that object becomes the "current value" of counters.
    
    Here is a function that "converts" the counters object to its
    "ordinary" representation:
    
         (defun show-counters (counters)
           (declare (xargs :stobjs (counters)))
           (list (NodeCnt counters)
                 (TipCnt counters)
                 (IntTipsSeen counters)))
    
    Observe that we _must_ declare, at the top of the defun, that we mean
    to use the formal parameter counters as a single-threaded object!  If
    we did not make this declaration, the body of show-counters would be
    processed as though counters were an ordinary object.  An error would
    be caused because the accessors used above cannot be applied to
    anything but the single-threaded object counters.  If you want to know
    why we insist on this declaration, see *note DECLARE-STOBJS::.
    
    When show-counters is admitted, the following message is printed:
    
         Since SHOW-COUNTERS is non-recursive, its admission is trivial.  We
         observe that the type of SHOW-COUNTERS is described by the theorem
         (AND (CONSP (SHOW-COUNTERS COUNTERS))
              (TRUE-LISTP (SHOW-COUNTERS COUNTERS))).
         We used primitive type reasoning.
    
         (SHOW-COUNTERS COUNTERS) => *.
    
         The guard conjecture for SHOW-COUNTERS is trivial to prove.
         SHOW-COUNTERS is compliant with Common Lisp.
    
    The line above containing the "=>" is called the "signature" of
    show-counters; it conveys the information that the first argument is
    the single-threaded object counters and the only result is an ordinary
    object.  Here is an example of another signature:
    
         (PROCESSOR * * COUNTERS) => (MV * COUNTERS)
    
    which indicates that the function PROCESSOR (which we haven't shown
    you) takes three arguments, the third of which is the COUNTERS stobj,
    and returns two results, the second of which is the modified COUNTERS.
    
    Returning to the admission of show-counters above, the last sentence
    printed indicates that the guard conjectures for the function were
    proved.  When some argument of a function is declared to be a
    single-threaded object via the xargs :stobj, we automatically add
    (conjoin) to the guard the condition that the argument satisfy the
    recognizer for that single-threaded object.  In the case of
    show-counters the guard is (countersp counters).
    
    Here is an example of show-counters being called:
    
         ACL2 !>(show-counters counters)
         (3 0 NIL)
    
    This is what we would see had we set the NodeCnt field of the initial
    value of counters to 3, as we did earlier in this example.
    
    We next wish to define a function to reset the counters object.  We
    could define it this way:
    
         (defun reset-counters (counters)
           (declare (xargs :stobjs (counters)))
           (let ((counters (update-NodeCnt 0 counters)))
             (let ((counters (update-TipCnt 0 counters)))
               (update-IntTipsSeen nil counters))))
    
    which "successively" sets the NodeCnt field to 0, then the TipCnt field
    to 0, and then the IntTipsSeen field to nil and returns the resulting
    object.
    
    However, the nest of let expressions is tedious and we use this
    definition instead.  This definition exploits a macro, here named "seq"
    (for "sequentially") which evaluates each of the forms given, binding
    their results successively to the stobj name given.
    
         (defun reset-counters (counters)
           (declare (xargs :stobjs (counters)))
           (seq counters
                (update-NodeCnt 0 counters)
                (update-TipCnt 0 counters)
                (update-IntTipsSeen nil counters)))
    
    This definition is syntactically identical to the one above, after macro
    expansion.  Our definition of seq is shown below and is not part of
    native ACL2.
    
         (defmacro seq (stobj &rest rst)
           (cond ((endp rst) stobj)
                 ((endp (cdr rst)) (car rst))
                 (t `(let ((,stobj ,(car rst)))
                      (seq ,stobj ,@(cdr rst))))))
    
    The signature printed for reset-counters is
    
         (RESET-COUNTERS COUNTERS) => COUNTERS.
    
    Here is an example.
    
         ACL2 !>(show-counters counters)
         (3 0 NIL)
         ACL2 !>(reset-counters counters)
         
         ACL2 !>(show-counters counters)
         (0 0 NIL)
    
    Here finally is a function that uses counters as a single-threaded
    accumulator to collect the desired information about the tree x.
    
         (defun sweep-tree (x counters)
           (declare (xargs :stobjs (counters)))
           (cond ((atom x)
                  (seq counters
                       (update-TipCnt (+ 1 (TipCnt counters)) counters)
                       (if (integerp x)
                           (update-IntTipsSeen (cons x (IntTipsSeen counters))
                                           counters)
                         counters)))
                 (t (seq counters
                         (update-NodeCnt (+ 1 (NodeCnt counters)) counters)
                         (sweep-tree (car x) counters)
                         (sweep-tree (cdr x) counters)))))
    
    We can paraphrase this definition as follows.  If x is an atom, then
    increment the TipCnt field of counters and _then_, if x is an integer,
    add x to the IntTipsSeen field, and return counters.  On the other
    hand, if x is not an atom, then increment the NodeCnt field of
    counters, and _then_ sweep the car of x and _then_ sweep the cdr of x
    and return the result.
    
    Here is an example of its execution.  We have displayed the input tree
    in full dot notation so that the number of interior nodes is just the
    number of dots.
    
         ACL2 !>(sweep-tree '((((a . 1) . (2 . b)) . 3)
                              . (4 . (5 . d)))
                            counters)
         
         ACL2 !>(show-counters counters)
         (7 8 (5 4 3 2 1))
         ACL2 !>(reset-counters counters)
         
         ACL2 !>(show-counters counters)
         (0 0 NIL)
    
    The counters object has two integer fields and a field whose type is
    unrestricted.  single-threaded objects support other types of fields,
    such as arrays.  We deal with that in the stobj-example-2.  But we
    recommend that you first consider the implementation issues for the
    counters example (in stobj-example-1-implementation) and then consider
    the proof issues (in stobj-example-1-proofs).
    
    To continue the stobj tour, see *note STOBJ-EXAMPLE-2::.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-EXAMPLE-1-DEFUNS,  Next: STOBJ-EXAMPLE-1-IMPLEMENTATION,  Prev: STOBJ-EXAMPLE-1,  Up: STOBJ
    
    STOBJ-EXAMPLE-1-DEFUNS    the defuns created by the counters stobj
    
    Consider the event shown in stobj-example-1:
    
         (defstobj counters
           (NodeCnt     :type integer :initially 0)
           (TipCnt      :type integer :initially 0)
           (IntTipsSeen :type t       :initially nil))
    
    Here is a complete list of the defuns added by the event.
    
    The careful reader will note that the counters argument below is _not_
    declared with the :stobjs xarg even though we insist that the argument
    be a stobj in calls of these functions.  This "mystery" is explained
    below.
    
         (defun NodeCntp (x)                 ;;; Recognizer for 1st field
           (declare (xargs :guard t :verify-guards t))
           (integerp x))
    
         (defun TipCntp (x)                  ;;; Recognizer for 2nd field
           (declare (xargs :guard t :verify-guards t))
           (integerp x))
    
         (defun IntTipsSeenp (x)             ;;; Recognizer for 3rd field
           (declare (xargs :guard t :verify-guards t) (ignore x))
           t)
    
         (defun countersp (counters)         ;;; Recognizer for object
           (declare (xargs :guard t :verify-guards t))
           (and (true-listp counters)
                (= (length counters) 3)
                (NodeCntp (nth 0 counters))
                (TipCntp (nth 1 counters))
                (IntTipsSeenp (nth 2 counters))
                t))
    
         (defun create-counters ()           ;;; Creator for object
           (declare (xargs :guard t :verify-guards t))
           (list '0 '0 'nil))
    
         (defun NodeCnt (counters)           ;;; Accessor for 1st field
           (declare (xargs :guard (countersp counters) :verify-guards t))
           (nth 0 counters))
    
         (defun update-NodeCnt (v counters)  ;;; Updater for 1st field
           (declare (xargs :guard
                           (and (integerp v)
                                (countersp counters))
                           :verify-guards t))
           (update-nth 0 v counters))
    
         (defun TipCnt (counters)            ;;; Accessor for 2nd field
           (declare (xargs :guard (countersp counters) :verify-guards t))
           (nth 1 counters))
    
         (defun update-TipCnt (v counters)   ;;; Updater for 2nd field
           (declare (xargs :guard
                           (and (integerp v)
                                (countersp counters))
                           :verify-guards t))
           (update-nth 1 v counters))
    
         (defun IntTipsSeen (counters)       ;;; Accessor for 3rd field
           (declare (xargs :guard (countersp counters) :verify-guards t))
           (nth 2 counters))
    
         (defun update-IntTipsSeen (v counters) ;;; Updater for 3rd field
           (declare (xargs :guard (countersp counters) :verify-guards t))
           (update-nth 2 v counters))
    
    Observe that there is a recognizer for each of the three fields and
    then a recognizer for the counters object itself.  Then, for each
    field, there is an accessor and an updater.
    
    Observe also that the functions are guarded so that they expect a
    countersp for their counters argument and an appropriate value for the
    new field values.
    
    You can see all of the defuns added by a defstobj event by executing
    the event and then using the :pcb! command on the stobj name.  E.g.,
    
         ACL2 !>:pcb! counters
    
    will print the defuns above.
    
    We now clear up the "mystery" mentioned above.  Note, for example in
    TipCnt, that the formal counters is used.  From the discussion in
    stobj-example-1 it has been made clear that TipCnt can only be called
    on the counters object.  And yet, in that same discussion it was said
    that an argument is so treated only if it it declared among the :stobjs
    in the definition of the function.  So why doesn't TipCnt include
    something like (declare (xargs :stobjs (counters)))?
    
    The explanation of this mystery is as follows.  At the time TipCnt was
    defined, during the introduction of the counters stobj, the name
    "counters" was not yet a single-threaded object.  The introduction of a
    new single-threaded object occurs in three steps: (1) The new primitive
    recognizers, accessors, and updaters are introduced as "ordinary
    functions," producing their logical axiomatizations.  (2) The
    executable counterparts are defined in raw Lisp to support destructive
    updating.  (3) The new name is declared a single-threaded object to
    ensure that all future use of these primitives respects the
    single-threadedness of the object.  The functions defined as part of
    the introduction of a new single-threaded object are the only functions
    in the system that have undeclared stobj formals other than state.
    
    You may return to stobj-example-1 here.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-EXAMPLE-1-IMPLEMENTATION,  Next: STOBJ-EXAMPLE-1-PROOFS,  Prev: STOBJ-EXAMPLE-1-DEFUNS,  Up: STOBJ
    
    STOBJ-EXAMPLE-1-IMPLEMENTATION    the implementation of the counters stobj
    
    the event
    
         (defstobj counters
           (NodeCnt     :type integer :initially 0)
           (TipCnt      :type integer :initially 0)
           (IntTipsSeen :type t       :initially nil))
    
    discussed in stobj-example-1, creates a Common Lisp object to represent
    the current value of counters.  That object is created by evaluating
    either of the following "raw" (non-ACL2) Common Lisp forms:
    
         (create-counters)
    
         (vector (make-array 1 :element-type 'integer
                               :initial-element '0)
                 (make-array 1 :element-type 'integer
                               :initial-element '0)
                 'nil)
    
    and the value is stored in the Common Lisp global variable named
    *the-live-counters*.
    
    Thus, the counters object is an array of length three.  The first two
    elements are arrays of size 1 and are used to hold the NodeCnt and
    TipCnt fields.  The third element is the IntTipsSeen field.  The first
    two fields are represented by arrays so that we can implement the
    integer type specification efficiently.  Generally, integers are
    "boxed" in some Common Lisp implementations, for example, GCL.
    Creating a new integer requires creating a new box to put it in.  But
    in some lisps, including GCL, the integers inside arrays of integers
    are not boxed.
    
    The function NodeCnt is defined in raw Lisp as:
    
         (defun NodeCnt (counters)
           (the integer
                (aref (the (simple-array integer (1))
                           (svref counters 0))
                      0)))
    
    Observe that the form (svref counters 0) is evaluated to get an array
    of size 1, which is followed by a call of aref to access the 0th
    element of that array.
    
    The function update-NodeCnt is defined in raw Lisp as:
    
         (defun update-NodeCnt (v counters)
           (declare (type integer v))
           (progn
            (setf (aref (the (simple-array integer (1))
                             (svref counters 0))
                        0)
                  (the integer v))
            counters))
    
    Note that when this function is called, it does not create a new vector
    of length three, but "smashes" the existing one.
    
    One way to see all the raw Lisp functions defined by a given defstobj is
    to evaluate the defstobj event and then evaluate, in the ACL2 loop, the
    expression (nth 4 (global-val 'cltl-command (w state))).  Those
    functions that contain (DECLARE (STOBJ-INLINE-FN T)) will generate
    defabbrev forms because the :inline keyword of defstobj was supplied the
    value t.  The rest will generate defuns.
    
    We now recommend that you look at stobj-example-1-proofs.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-EXAMPLE-1-PROOFS,  Next: STOBJ-EXAMPLE-2,  Prev: STOBJ-EXAMPLE-1-IMPLEMENTATION,  Up: STOBJ
    
    STOBJ-EXAMPLE-1-PROOFS    some proofs involving the counters stobj
    
    Consider again the event
    
         (defstobj counters
           (NodeCnt     :type integer :initially 0)
           (TipCnt      :type integer :initially 0)
           (IntTipsSeen :type t       :initially nil))
    
    discussed in stobj-example-1, followed by the definition
    
         (defun reset-counters (counters)
           (declare (xargs :stobjs (counters)))
           (seq counters
                (update-NodeCnt 0 counters)
                (update-TipCnt 0 counters)
                (update-IntTipsSeen nil counters)))
    
    which, because of the seq macro in stobj-example-1, is just syntactic
    sugar for
    
         (defun reset-counters (counters)
           (declare (xargs :stobjs (counters)))
           (let ((counters (update-NodeCnt 0 counters)))
             (let ((counters (update-TipCnt 0 counters)))
               (update-IntTipsSeen nil counters)))).
    
    Here is a simple theorem about reset-counters.
    
         (defthm reset-counters-is-constant
           (implies (countersp x)
                    (equal (reset-counters x)
                           '(0 0 nil))))
    
    Before we talk about how to prove this theorem, note that the theorem
    is unusual in two respects.
    
    First, it calls reset-counters on an argument other than the variable
    counters!  That is allowed in theorems; logically speaking, the stobj
    functions are indistinguishable from ordinary functions.  Their use is
    syntactically restricted only in defuns, which might be compiled and
    run in raw Lisp.  Those restrictions allow us to implement stobj
    modification destructively.  But logically speaking, reset-counters and
    other stobj "modifying" functions just create new objects,
    constructively.
    
    Second, the theorem above explicitly provides the hypothesis that
    reset-counters is being applied to an object satisfying countersp.
    Such a hypothesis is not always required: reset-counters is total and
    will do something no matter what x is.  But in this particular case,
    the result is not '(0 0 nil) unless x is, at least, a true-list of
    length three.
    
    To make a long story short, to prove theorems about stobj functions you
    behave in exactly the way you would to prove the same theorems about the
    same functions defined without the stobj features.
    
    How can we prove the above theorem?  Unfolding the definition of
    reset-counters shows that (reset-counters x) is equal to
    
         (update-IntTipsSeen nil
           (update-TipCnt 0
             (update-NodeCnt 0 x)))
    
    which in turn is
    
         (update-nth 2 nil
          (update-nth 1 0
           (update-nth 0 0 x))).
    
    Opening up the definition of update-nth reduces this to
    
         (list* 0 0 nil (cdddr x)).
    
    This is clearly equal to '(0 0 nil), provided we know that (cdddr x) is
    nil.
    
    Unfortunately, that last fact requires a lemma.  The most specific
    lemma we could provide is
    
         (defthm special-lemma-for-counters
           (implies (countersp x)
                    (equal (cdddr x) nil)))
    
    but if you try to prove that lemma you will find that it requires some
    reasoning about len and true-listp.  Furthermore, the special lemma
    above is of interest only for counters.
    
    The following lemma about len is the one we prefer.
    
         (defthm equal-len-n
           (implies (syntaxp (quotep n))
                    (equal (equal (len x) n)
                           (if (integerp n)
                               (if (< n 0)
                                   nil
                                 (if (equal n 0)
                                     (atom x)
                                   (and (consp x)
                                        (equal (len (cdr x)) (- n 1)))))
                             nil))))
    
    This lemma will simplify any equality in which a len expression is
    equated to any explicitly given constant _n_, e.g., 3, reducing the
    equation to a conjunction of consp terms about the first _n_ cdrs.
    
    If the above lemma is available then ACL2 immediately proves
    
         (defthm reset-counters-is-constant
           (implies (countersp x)
                    (equal (reset-counters x)
                           '(0 0 nil))))
    
    The point is presumably well made: proving theorems about
    single-threaded object accessors and updaters is no different than
    proving theorems about other recursively defined functions on lists.
    
    As we have seen, operations on stobjs turn into definitions involving
    nth and update-nth in the logic.  Here are two lemmas that are useful
    for simplifying terms involving nth and update-nth, which are therefore
    useful in reasoning about single-threaded objects.
    
         (defthm update-nth-update-nth-same
           (implies (equal (nfix i1) (nfix i2))
                    (equal (update-nth i1 v1 (update-nth i2 v2 l))
                           (update-nth i1 v1 l))))
    
         (defthm update-nth-update-nth-diff
           (implies (not (equal (nfix i1) (nfix i2)))
                    (equal (update-nth i1 v1 (update-nth i2 v2 l))
                           (update-nth i2 v2 (update-nth i1 v1 l))))
           :rule-classes ((:rewrite :loop-stopper ((i1 i2)))))
    
    These lemmas are due to Matt Wilding.  See *note NU-REWRITER:: for a
    discussion of the efficient simplification of terms of the form (nth n
    (update-nth key val lst)), which can be critical in settings involving
    sequential bindings that commonly arise in operations involving stobjs.
    
    We now recommend that you see *note STOBJ-EXAMPLE-2::.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-EXAMPLE-2,  Next: STOBJ-EXAMPLE-3,  Prev: STOBJ-EXAMPLE-1-PROOFS,  Up: STOBJ
    
    STOBJ-EXAMPLE-2    an example of the use of arrays in single-threaded objects
    
    The following event
    
         (defstobj ms
           (pcn  :type integer                  :initially 0)
           (mem  :type (array integer (100000)) :initially -1)
           (code :type t                        :initially nil))
    
    introduces a single-threaded object named ms (which stands for "machine
    state").  The object has three fields, a pcn or program counter, a mem
    or memory, and a code field.
    
    The mem field is occupied by an object initially of type (array integer
    (100000)).  Logically speaking, this is a list of length 100000, each
    element of which is an integer.  But in the underlying implementation
    of the ms object, this field is occupied by a raw Lisp array, initially
    of size 100000.
    
    You might expect the above defstobj to define the accessor function mem
    and the updater update-mem.  _That does not happen!_.
    
    The above event defines the accessor function memi and the updater
    update-memi.  These functions do not access/update the mem field of the
    ms object; they access/update the individual elements of the array in
    that field.
    
    In particular, the logical definitions of the two functions are:
    
         (defun memi (i ms)
           (declare (xargs :guard
                           (and (msp ms)
                                (integerp i)
                                (<= 0 i)
                                (< i (mem-length ms)))))
           (nth i (nth 1 ms)))
    
         (defun update-memi (i v ms)
           (declare (xargs :guard
                           (and (msp ms)
                                (integerp i)
                                (<= 0 i)
                                (< i (mem-length ms))
                                (integerp v))))
           (update-nth-array 1 i v ms))
    
    For example, to access the 511th (0-based) memory location of the
    current ms you could evaluate:
    
         ACL2 !>(memi 511 ms)
         -1
    
    The answer is -1 initially, because that is the above-specified initial
    value of the elements of the mem array.
    
    To set that element you could do
    
         ACL2 !>(update-memi 511 777 ms)
         
         ACL2 !>(memi 511 ms)
         777
    
    The raw Lisp implementing these two functions is shown below.
    
         (defun memi (i ms)
           (declare (type (and fixnum (integer 0 *)) i))
           (the integer
                (aref (the (simple-array integer (*))
                           (svref ms 1))
                      (the (and fixnum (integer 0 *)) i))))
    
         (defun update-memi (i v ms)
           (declare (type (and fixnum (integer 0 *)) i)
                    (type integer v))
           (progn
            (setf (aref (the (simple-array integer (*))
                             (svref ms 1))
                        (the (and fixnum (integer 0 *)) i))
                  (the integer v))
            ms))
    
    If you want to see the raw Lisp supporting a defstobj, execute the
    defstobj and then evaluate the ACL2 form (nth 4 (global-val
    'cltl-command (w state))).
    
    To continue the stobj tour, see *note STOBJ-EXAMPLE-3::.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-EXAMPLE-3,  Next: STOBJ-LET,  Prev: STOBJ-EXAMPLE-2,  Up: STOBJ
    
    STOBJ-EXAMPLE-3    another example of a single-threaded object
    
    The event
    
         (defstobj $s
           (x :type integer :initially 0)
           (a :type (array (integer 0 9) (3)) :initially 9 :resizable t))
    
    introduces a stobj named $S.  The stobj has two fields, X and A.  The A
    field is an array.  The X field contains an integer and is initially 0.
    The A field contains a list of integers, each between 0 and 9,
    inclusively.  (Under the hood, this "list" is actually implemented as
    an array.)  Initially, the A field has three elements, each of which is
    9.
    
    This event introduces the following sequence of function definitions:
    
         (DEFUN XP (X) ...)               ; recognizer for X field
         (DEFUN AP (X) ...)               ; recognizer of A field
         (DEFUN $SP ($S) ...)             ; top-level recognizer for stobj $S
         (DEFUN CREATE-$S NIL ...)        ; creator for stobj $S
         (DEFUN X ($S) ...)               ; accessor for X field
         (DEFUN UPDATE-X (V $S) ...)      ; updater for X field
         (DEFUN A-LENGTH ($S) ...)        ; length of A field
         (DEFUN RESIZE-A (K $S) ...)      ; resizer for A field
         (DEFUN AI (I $S) ...)            ; accessor for A field at index I
         (DEFUN UPDATE-AI (I V $S) ...)   ; updater for A field at index I
    
    Here is the definition of $SP:
    
         (DEFUN $SP ($S)
           (DECLARE (XARGS :GUARD T :VERIFY-GUARDS T))
           (AND (TRUE-LISTP $S)
                (= (LENGTH $S) 2)
                (XP (NTH 0 $S))
                (AP (NTH 1 $S))
                T))
    
    This reveals that in order to satisfy $SP an object must be a true list
    of length 2 whose first element satisfies XP and whose second satisfies
    AP.  By printing the definition of AP one learns that it requires its
    argument to be a true list, each element of which is an integer between
    0 and 9.
    
    The initial value of stobj $S is given by zero-ary "creator" function
    CREATE-$S.  Creator functions may only be used in limited contexts.
    See *note WITH-LOCAL-STOBJ::.
    
    Here is the definition of UPDATE-AI, the updater for the A field at
    index I:
    
         (DEFUN UPDATE-AI (I V $S)
           (DECLARE (XARGS :GUARD
                           (AND ($SP $S)
                                (INTEGERP I)
                                (<= 0 I)
                                (< I (A-LENGTH $S))
                                (AND (INTEGERP V) (<= 0 V) (<= V 9)))
                           :VERIFY-GUARDS T))
           (UPDATE-NTH-ARRAY 1 I V $S))
    
    By definition (UPDATE-NTH-ARRAY 1 I V $S) is (UPDATE-NTH 1 (UPDATE-NTH
    I V (NTH 1 $S)) $S).  This may be a little surprising but should be
    perfectly clear.
    
    First, ignore the guard, since it is irrelevant in the logic.  Reading
    from the inside out, (UPDATE-AI I V $S) extracts (NTH 1 $S), which is
    array a of $S.  (Recall that NTH is 0-based.)  The next higher
    expression in the definition above, (UPDATE-NTH I V a), "modifies" a by
    setting its Ith element to V.  Call this a'.  The next higher
    expression, (UPDATE-NTH 1 a' $S), "modifies" $S by setting its 1st
    component to a'.  Call this result $s'.  Then $s' is the result
    returned by UPDATE-AI.
    
    So the first useful observation is that from the perspective of the
    logic, the type "restrictions" on stobjs are irrelevant.  They are
    "enforced" by ACL2's guard mechanism, not by the definitions of the
    updater functions.
    
    As one might also imagine, the accessor functions do not really "care,"
    logically, whether they are applied to well-formed stobjs or not.  For
    example, (AI I $S) is defined to be (NTH I (NTH 1 $S)).
    
    Thus, you will not be able to prove that (AI 2 $S) is an integer.  That
    is,
    
         (integerp (AI 2 $S))
    
    is not a theorem, because $S may not be well-formed.
    
    Now (integerp (AI 2 $S)) will always evaluate to T in the top-level
    ACL2 command loop, because we insist that the current value of the
    stobj $S always satisfies $SP by enforcing the guards on the updaters,
    independent of whether guard checking is on or off; see *note
    SET-GUARD-CHECKING::.  But in a theorem $S is just another variable,
    implicitly universally quantified.
    
    So (integerp (AI 2 $S)) is not a theorem because it is not true when
    the variable $S is instantiated with, say,
    
         '(1 (0 1 TWO))
    
    because, logically speaking, (AI 2 '(1 (0 1 TWO))) evaluates to the
    symbol TWO.  That is,
    
         (equal (AI 2 '(1 (0 1 TWO))) 'TWO)
    
    is true.
    
    However,
    
         (implies (and ($SP $S) (< 2 (A-LENGTH $S))) (integerp (AI 2 $S)))
    
    is a theorem.  To prove it, you will have to prove a lemma about AP.
    The following will do:
    
         (defthm ap-nth
           (implies (and (AP x)
                         (integerp i)
                         (<= 0 i)
                         (< i (len x)))
                    (integerp (nth i x)))).
    
    Similarly,
    
         (implies (and (integerp i)
                       (<= 0 i)
                       (< i (A-LENGTH $S))
                       (integerp v)
                       (<= 0 v)
                       (<= v 9))
                  ($SP (UPDATE-AI i v $S)))
    
    is not a theorem until you add the additional hypothesis ($SP $S).  To
    prove the resulting theorem, you will need a lemma such as the
    following.
    
         (defthm ap-update-nth
           (implies (and (AP a)
                         (integerp v)
                         (<= 0 v)
                         (<= v 9)
                         (integerp i)
                         (<= 0 i)
                         (< i (len a)))
                    (AP (update-nth i v a))))
    
    The moral here is that from the logical perspective, you must provide
    the hypotheses that, as a programmer, you think are implicit on the
    structure of your stobjs, and you must prove their invariance.  This is
    a good area for further support, perhaps in the form of a library of
    macros.
    
    _Resizing Array Fields_
    
    Recall the specification of the array field, A for the stobj $S
    introduced above:
    
         (a :type (array (integer 0 9) (3)) :initially 9 :resizable t)
    
    Logically, this field is a list, initially of length 3.  Under the
    hood, this field is implemented using a Common Lisp array with 3
    elements.  In some applications, one may wish to lengthen an array
    field, or even (to reclaim space) to shrink an array field.  The
    defstobj event provides functions to access the current length of an
    array field and to change the array field, with default names obtained
    by suffixing the field name with "LENGTH-" or prefixing it with
    "RESIZE-," respectively.  The following log shows the uses of these
    fields in the above example.
    
         ACL2 !>(A-LENGTH $S)
         3
         ACL2 !>(RESIZE-A 10 $S) ; change length of A to 10
         <$s>
         ACL2 !>(A-LENGTH $S)
         10
         ACL2 !>(AI 7 $S)        ; new elements get value from :initially
         9
         ACL2 !>(RESIZE-A 2 $S)  ; truncate A down to first 2 elements
         <$s>
         ACL2 !>(A-LENGTH $S)
         2
         ACL2 !>(AI 7 $S)        ; error:  access past array bound
    
    
         ACL2 Error in TOP-LEVEL:  The guard for the function symbol AI, which
         is (AND ($SP $S) (INTEGERP I) (<= 0 I) (< I (A-LENGTH $S))), is violated
         by the arguments in the call (AI 7 $S).
    
         ACL2 !>
    
    Here are the definitions of the relevant functions for the above
    example; also see *note RESIZE-LIST::.
    
         (DEFUN A-LENGTH ($S)
           (DECLARE (XARGS :GUARD ($SP $S) :VERIFY-GUARDS T))
           (LEN (NTH 1 $S)))
    
         (DEFUN RESIZE-A (K $S)
           (DECLARE (XARGS :GUARD ($SP $S) :VERIFY-GUARDS T))
           (UPDATE-NTH 1
                       (RESIZE-LIST (NTH 1 $S) K 9)
                       $S))
    
    It is important to note that the implementation of array resizing in
    ACL2 involves copying the entire array into a newly allocated space and
    thus can be quite costly if performed often.  This approach was chosen
    in order to make array access and update as efficient as possible, with
    the suspicion that for most applications, array access and update are
    considerably more frequent than resizing (especially if the programmer
    is aware of the relative costs beforehand).
    
    It should also be noted that computations of lengths of stobj array
    fields should be fast (constant-time) in all or most Common Lisp
    implementations.
    
    Finally, if :resizable t is not supplied as shown above, then an
    attempt to resize the array will result in an error.  If you do not
    intend to resize the array, it is better to omit the :resizable option
    (or to supply :resizable nil), since then the length function will be
    defined to return a constant, namely the initial length, which can
    simplify guard proofs (compare with the definition of A-LENGTH above).
    
    This completes the tour through the documentation of stobjs.  However,
    you may now wish to read the documentation for the event that
    introduces a new single-threaded object; see *note DEFSTOBJ::.
    
    
    File: acl2-doc-emacs.info,  Node: STOBJ-LET,  Next: WITH-LOCAL-STATE,  Prev: STOBJ-EXAMPLE-3,  Up: STOBJ
    
    STOBJ-LET    See *note NESTED-STOBJS::.
    
    
    File: acl2-doc-emacs.info,  Node: WITH-LOCAL-STATE,  Next: WITH-LOCAL-STOBJ,  Prev: STOBJ-LET,  Up: STOBJ
    
    WITH-LOCAL-STATE    locally bind state
    
    This is an advanced topic, probably of interest only to system
    developers.
    
    Consider the following example form:
    
         (with-local-state
          (mv-let (result state)
                  (compute-with-state x state)
                  result))
    
    This is equivalent to the following form.
    
         (with-local-stobj
          state
          (mv-let (result state)
                  (compute-with-state x state)
                  result))
    
    By default, this form is illegal, because ACL2 does not have a way to
    unwind all changes to the ACL2 state; we say more on this issue below.
    There may however be situations where you are willing to manage or
    overlook this issue.  In that case you may execute the following form
    to enable the use of with-local-state, by enabling the use of
    with-local-stobj on state; but note that it requires an active trust
    tag (see *note DEFTTAG::).
    
         (remove-untouchable create-state t)
    
    Please be aware that no local state is actually created, however!  In
    particular, users of with-local-state need either to ensure that
    channels are closed and state global variables are returned to their
    original values, or else be willing to live with changes made to state
    that are not justified by the code that has been evaluated.  You are
    welcome to look in the the ACL2 source code at the definition of macro
    channel-to-string, which employs with-local-state to create a local
    state for the purpose of creating a string.
    
    Here is an example use of with-local-state.  Notice the use of defttag
    -- and indeed, please understand that we are just hacking here, and in
    general it takes significant effort to be sure that one is using
    with-local-state correctly!
    
         (defttag t)
    
         (remove-untouchable create-state t)
    
         (set-state-ok t)
    
         (defun foo (state)
           (declare (xargs :mode :program))
           (mv-let
            (channel state)
            (open-input-channel "my-file" :object state)
            (mv-let (eofp obj state)
                    (read-object channel state)
                    (declare (ignore eofp))
                    (let ((state (close-input-channel channel state)))
                      (mv obj state)))))
    
         (defun bar ()
           (declare (xargs :mode :program))
           (with-local-state (mv-let (result state)
                                     (foo state)
                                     result)))
    
         ; Multiple-value return version:
    
         (defun foo2 (state)
           (declare (xargs :mode :program))
           (mv-let
            (channel state)
            (open-input-channel "my-file" :object state)
            (mv-let (eofp obj state)
                    (read-object channel state)
                    (let ((state (close-input-channel channel state)))
                      (mv eofp obj state)))))
    
         (defun bar2 ()
           (declare (xargs :mode :program))
           (with-local-state (mv-let (eofp result state)
                                     (foo2 state)
                                     (mv eofp result))))
    
    
    File: acl2-doc-emacs.info,  Node: WITH-LOCAL-STOBJ,  Prev: WITH-LOCAL-STATE,  Up: STOBJ
    
    WITH-LOCAL-STOBJ    locally bind a single-threaded object
    
    See *note STOBJ:: for an introduction to single-threaded objects.
    
         Example Form:
         (with-local-stobj
          st
          (mv-let (result st)
                  (compute-with-st x st)
                  result))
    
    With-local-stobj can be thought of as a macro, where the example form
    above expands as follows.
    
         (mv-let (result st)
                 (let ((st (create-st)))
                   (compute-with-st x st))
                 (declare (ignore st))
                 result)
    
    However, ACL2 expects you to use with-local-stobj, not its expansion.
    More precisely, stobj creator functions are not allowed except
    (implicitly) via with-local-stobj and in logic-only situations (like
    theorems and hints).  Moreover, neither with-local-stobj nor its
    expansions are legal when typed directly at the top-level loop.  See
    *note TOP-LEVEL:: for a way to use with-local-stobj in the top-level
    loop.
    
         General Forms:
         (with-local-stobj stobj-name mv-let-form)
         (with-local-stobj stobj-name mv-let-form creator-name)
    
    where stobj-name is the name of a stobj, mv-let-form is a call of
    mv-let, and if creator-name is supplied then it should be the name of
    the creator function for stobj-name; see *note DEFSTOBJ::.  For the
    example form above, its expansion would use creator-name, if supplied,
    in place of create-st.  Note that stobj-name must not be state (the
    ACL2 state), except in special situations probably of interest only to
    system developers; see *note WITH-LOCAL-STATE::.
    
    With-local-stobj can be useful when a stobj is used to memoize
    intermediate results during a computation, yet it is desired not to
    make the stobj a formal parameter for the function and its callers.
    
    ACL2 can reason about these "local stobjs," and in particular about
    stobj creator functions.  For technical reasons, ACL2 will not allow
    you to enable the :EXECUTABLE-COUNTERPART rune of a stobj creator
    function.
    
    Finally, here is a small example concocted in order to illustrate that
    with-local-stobj calls can be nested.
    
         (defstobj st fld1)
    
         (defun foo ()
           (with-local-stobj
            st ; Let us call this the ``outer binding of st''.
            (mv-let (val10 val20 st)
              (let ((st (update-fld1 10 st)))
                ;; At this point the outer binding of st has fld1 = 10.
                (let ((result (with-local-stobj
                               st ; Let us call this the ``inner binding of st''.
                               (mv-let (val st)
                                 (let ((st (update-fld1 20 st)))
                                   ;; Now fld1 = 20 for the inner binding of st.
                                   (mv (fld1 st) st))
                                 val))))
                  ;; So result has been bound to 20 above, but here we are once again
                  ;; looking at the outer binding of st, where fld1 is still 10.
                  (mv (fld1 st) result st)))
              (mv val10 val20))))
    
         (thm (equal (foo) (mv 10 20))) ; succeeds
    
    
    File: acl2-doc-emacs.info,  Node: SWITCHES-PARAMETERS-AND-MODES,  Next: THEORIES,  Prev: STOBJ,  Up: Top
    
    SWITCHES-PARAMETERS-AND-MODES    a variety of ways to modify the ACL2 environment
    
    The beginning user might pay special attention to documentation for
    logic and program.  Other topics in this section can be read as one
    gains familiarity with ACL2.
    
    * Menu:
    
    * ACL2-CUSTOMIZATION:: file of initial commands for ACL2 to run at startup
    
    * ADD-BINOP:: associate a function name with a macro name
    
    * ADD-DEFAULT-HINTS:: add to the default hints
    
    * ADD-DEFAULT-HINTS!:: add to the default hints non-locally
    
    * ADD-DIVE-INTO-MACRO:: associate proof-checker diving function with macro name
    
    * ADD-INCLUDE-BOOK-DIR:: link keyword for :dir argument of ld and include-book
    
    * ADD-INVISIBLE-FNS:: make some unary functions invisible to the loop-stopper algorithm
    
    * ADD-LD-KEYWORD-ALIAS:: See *note LD-KEYWORD-ALIASES::.
    
    * ADD-LD-KEYWORD-ALIAS!:: See *note LD-KEYWORD-ALIASES::.
    
    * ADD-MACRO-ALIAS:: associate a function name with a macro name
    
    * ADD-MACRO-FN:: associate a function name with a macro name
    
    * ADD-MATCH-FREE-OVERRIDE:: set :match-free value to :once or :all in existing rules
    
    * ADD-NTH-ALIAS:: associate one symbol with another for printing of nth/update-nth terms
    
    * ADD-OVERRIDE-HINTS:: add to the override-hints
    
    * ADD-OVERRIDE-HINTS!:: add non-locally to the override-hints
    
    * DEFAULT-HINTS-TABLE:: a table used to provide hints for proofs
    
    * DEFAULT-VERIFY-GUARDS-EAGERNESS:: See *note SET-VERIFY-GUARDS-EAGERNESS::.
    
    * DELETE-INCLUDE-BOOK-DIR:: unlink keyword for :dir argument of ld and include-book
    
    * DIVE-INTO-MACROS-TABLE:: right-associated function information for the proof-checker
    
    * FINALIZE-EVENT-USER:: user-supplied code to complete events, e.g., with extra summary output
    
    * INITIALIZE-EVENT-USER:: user-supplied code to initiate events
    
    * INVISIBLE-FNS-TABLE:: functions that are invisible to the loop-stopper algorithm
    
    * LD-KEYWORD-ALIASES:: abbreviation of some keyword commands
    
    * LOGIC:: to set the default defun-mode to :logic
    
    * MACRO-ALIASES-TABLE:: a table used to associate function names with macro names
    
    * NTH-ALIASES-TABLE:: a table used to associate names for nth/update-nth printing
    
    * PRINT-SUMMARY-USER:: See *note FINALIZE-EVENT-USER::.
    
    * PROGRAM:: to set the default defun-mode to :program
    
    * PUSH-UNTOUCHABLE:: add name or list of names to the list of untouchable symbols
    
    * REMOVE-BINOP:: remove the association of a function name with a macro name
    
    * REMOVE-DEFAULT-HINTS:: remove from the default hints
    
    * REMOVE-DEFAULT-HINTS!:: remove from the default hints non-locally
    
    * REMOVE-DIVE-INTO-MACRO:: removes association of proof-checker diving function with macro name
    
    * REMOVE-INVISIBLE-FNS:: make some unary functions no longer invisible
    
    * REMOVE-MACRO-ALIAS:: remove the association of a function name with a macro name
    
    * REMOVE-MACRO-FN:: remove the association of a function name with a macro name
    
    * REMOVE-NTH-ALIAS:: remove a symbol alias for printing of nth/update-nth terms
    
    * REMOVE-OVERRIDE-HINTS:: delete from the list of override-hints
    
    * REMOVE-OVERRIDE-HINTS!:: delete non-locally from the list of override-hints
    
    * REMOVE-UNTOUCHABLE:: remove names from lists of untouchable symbols
    
    * RESET-PREHISTORY:: reset the prehistory
    
    * RETURN-LAST-TABLE:: install special raw Lisp behavior
    
    * RULER-EXTENDERS:: control for ACL2's termination and induction analyses
    
    * SET-ABSSTOBJ-DEBUG:: obtain debugging information upon atomicity violation for an abstract stobj
    
    * SET-BACKCHAIN-LIMIT:: sets the backchain-limit used by the type-set and rewriting mechanisms
    
    * SET-BOGUS-DEFUN-HINTS-OK:: allow unnecessary ``mutual recursion''
    
    * SET-BOGUS-MUTUAL-RECURSION-OK:: allow unnecessary ``mutual recursion''
    
    * SET-CASE-SPLIT-LIMITATIONS:: set the case-split-limitations
    
    * SET-CHECKPOINT-SUMMARY-LIMIT:: control printing of key checkpoints upon a proof's failure
    
    * SET-COMPILE-FNS:: have each function compiled as you go along.
    
    * SET-COMPILER-ENABLED:: See *note COMPILATION::.
    
    * SET-DEBUGGER-ENABLE:: control whether Lisp errors and breaks invoke the Lisp debugger
    
    * SET-DEFAULT-BACKCHAIN-LIMIT:: sets the default backchain-limit used when admitting a rule
    
    * SET-DEFAULT-HINTS:: set the default hints
    
    * SET-DEFAULT-HINTS!:: set the default hints non-locally
    
    * SET-DEFERRED-TTAG-NOTES:: modify the verbosity of TTAG NOTE printing
    
    * SET-ENFORCE-REDUNDANCY:: require most events to be redundant
    
    * SET-GAG-MODE:: modify the nature of proof output
    
    * SET-GUARD-CHECKING:: control checking guards during execution of top-level forms
    
    * SET-IGNORE-DOC-STRING-ERROR:: allow ill-formed documentation strings
    
    * SET-IGNORE-OK:: allow unused formals and locals without an ignore or ignorable declaration
    
    * SET-INHIBIT-OUTPUT-LST:: control output
    
    * SET-INHIBIT-WARNINGS:: control warnings
    
    * SET-INHIBIT-WARNINGS!:: control warnings non-locally
    
    * SET-INHIBITED-SUMMARY-TYPES:: control which parts of the summary are printed
    
    * SET-INVISIBLE-FNS-TABLE:: set the invisible functions table
    
    * SET-IRRELEVANT-FORMALS-OK:: allow irrelevant formals in definitions
    
    * SET-LD-KEYWORD-ALIASES:: See *note LD-KEYWORD-ALIASES::.
    
    * SET-LD-KEYWORD-ALIASES!:: See *note LD-KEYWORD-ALIASES::.
    
    * SET-LD-REDEFINITION-ACTION:: See *note LD-REDEFINITION-ACTION::.
    
    * SET-LD-SKIP-PROOFS:: See *note SET-LD-SKIP-PROOFSP::.
    
    * SET-LD-SKIP-PROOFSP:: See *note LD-SKIP-PROOFSP::.
    
    * SET-LET*-ABSTRACTION:: See *note SET-LET*-ABSTRACTIONP::.
    
    * SET-LET*-ABSTRACTIONP:: to shorten many prettyprinted clauses
    
    * SET-MATCH-FREE-DEFAULT:: provide default for :match-free in future rules
    
    * SET-MATCH-FREE-ERROR:: control error vs. warning when :match-free is missing
    
    * SET-MEASURE-FUNCTION:: set the default measure function symbol
    
    * SET-NON-LINEAR:: See *note SET-NON-LINEARP::.
    
    * SET-NON-LINEARP:: to turn on or off non-linear arithmetic reasoning
    
    * SET-NU-REWRITER-MODE:: to turn on and off the nu-rewriter
    
    * SET-OVERRIDE-HINTS:: set the override-hints
    
    * SET-OVERRIDE-HINTS!:: set the override-hints non-locally
    
    * SET-PARALLEL-EXECUTION:: for ACL2(p): enabling parallel execution for four parallelism primitives
    
    * SET-PRINT-CLAUSE-IDS:: cause subgoal numbers to be printed when 'prove output is inhibited
    
    * SET-PROVER-STEP-LIMIT:: sets the step-limit used by the ACL2 prover
    
    * SET-RAW-MODE:: enter or exit ``raw mode,'' a raw Lisp environment
    
    * SET-RAW-MODE-ON!:: enter ``raw mode,'' a raw Lisp environment
    
    * SET-RAW-PROOF-FORMAT:: print runes as lists in proof output from simplification
    
    * SET-REWRITE-STACK-LIMIT:: Sets the rewrite stack depth used by the rewriter
    
    * SET-RULER-EXTENDERS:: See *note RULER-EXTENDERS::.
    
    * SET-RW-CACHE-STATE:: set the default rw-cache-state
    
    * SET-RW-CACHE-STATE!:: set the default rw-cache-state non-locally
    
    * SET-SAVED-OUTPUT:: save proof output for later display with :pso or :pso!
    
    * SET-SPLITTER-OUTPUT:: turn on or off reporting of rules that may have caused case splits
    
    * SET-STATE-OK:: allow the use of STATE as a formal parameter
    
    * SET-TAU-AUTO-MODE:: turn on or off automatic (``greedy'') generation of :tau-system rules
    
    * SET-TOTAL-PARALLELISM-WORK-LIMIT:: for ACL2(p): set thread limit for parallelism primitives
    
    * SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR:: for ACL2(p): control the action taken when the thread limit is exceeded
    
    * SET-VERIFY-GUARDS-EAGERNESS:: the eagerness with which guard verification is tried.
    
    * SET-WATERFALL-PARALLELISM:: for ACL2(p): configuring the parallel execution of the waterfall
    
    * SET-WATERFALL-PARALLELISM-HACKS-ENABLED:: for ACL2(p): enable waterfall-parallelism hacks
    
    * SET-WATERFALL-PARALLELISM-HACKS-ENABLED!:: for ACL2(p): enabling waterfall parallelism hacks
    
    * SET-WATERFALL-PRINTING:: for ACL2(p): configuring the printing that occurs within the parallelized waterfall
    
    * SET-WELL-FOUNDED-RELATION:: set the default well-founded relation
    
    * SET-WRITE-ACL2X:: cause certify-book to write out a .acl2x file
    
    * TAU-STATUS:: query or set tau system status
    
    * TERM-TABLE:: a table used to validate meta rules
    
    * UNTRANS-TABLE:: associates a function symbol with a macro for printing user-level terms
    
    * USER-DEFINED-FUNCTIONS-TABLE:: an advanced table used to replace certain system functions
    
    * VERIFY-GUARDS-EAGERNESS:: See *note SET-VERIFY-GUARDS-EAGERNESS::.
    
    * WITH-GUARD-CHECKING:: suppressing or enable guard-checking for a form
    
    * WITH-OUTPUT:: suppressing or turning on specified output for an event
    
    
    File: acl2-doc-emacs.info,  Node: ACL2-CUSTOMIZATION,  Next: ADD-BINOP,  Prev: SWITCHES-PARAMETERS-AND-MODES,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ACL2-CUSTOMIZATION    file of initial commands for ACL2 to run at startup
    
    ACL2 provides a mechanism to load automatically a so-called "ACL2
    customization file," via ld, the first time lp is called in an ACL2
    session.  ACL2 looks for this file as follows.
    
         o If the host Lisp reads a non-empty value for the system's
         environment variable ACL2_CUSTOMIZATION, then that string value is
         used for the customization file name.  In this case, if the file
         does not exist or if the string is "NONE" then there is no
         customization file.  Notes.  (1) If the customization file name is
         a relative pathname (see *note PATHNAME::), then the pathname is
         considered relative to the connected book directory (see *note
         CBD::).  (2) If this variable is not already defined, then its
         value is set to NONE when the ACL2 makefile system is invoked
         (specifically, using community books file books/Makefile-generic),
         e.g., for a regression.
    
         o Otherwise (empty environment variable value), file
         "acl2-customization.lsp" or "acl2-customization.lisp" on the
         connected book directory (see *note CBD::), generally the current
         directory, is the customization file (in that order) if either
         exists.
    
         o Otherwise file "acl2-customization.lsp" or
         "acl2-customization.lisp" on your home directory is the
         customization file (in that order), if either exists (except, this
         case is skipped on Windows operating systems.
    
    Except for the fact that this ld command is not typed explicitly by
    you, it is a standard ld command, with one exception: any settings of
    ld specials are remembered once this call of ld has completed.  For
    example, suppose that you start your customization file with
    (set-ld-skip-proofsp t state), so that proofs are skipped as it is
    loaded with ld.  Then the ld special ld-skip-proofsp will remain t
    after the ld has completed, causing proofs to be skipped in your ACL2
    session, unless your customization file sets this variable back to nil,
    say with (set-ld-skip-proofsp nil state).
    
    If the customization file exists, it is loaded with ld using the usual
    default values for the ld specials (see *note LD::).  Thus, if an error
    is encountered, no subsequent forms in the file will be evaluated.
    
    To create a customization file it is recommended that you first give it
    a name other than "acl2-customization.lsp" or "acl2-customization.lisp"
    so that ACL2 does not try to include it prematurely when you next enter
    lp.  Then, while in the uncustomized lp, explicitly invoke ld on your
    evolving (but renamed) customization file until all forms are
    successfully evaluated.  The same procedure is recommended if for some
    reason ACL2 cannot successfully evaluate all forms in your
    customization file: temporarily rename your customization file so that
    ACL2 does not try to ld it automatically and then debug the new file by
    explicit calls to ld.
    
    WARNING!  If you certify a book after the (automatic) loading of a
    customization file, the forms in that file will be part of the
    portcullis of the books you certify!  That is, the forms in your
    customization file at certification time will be loaded whenever
    anybody uses the books you are certifying.  Since customization files
    generally contain idiosyncratic commands, you may not want yours to be
    part of the books you create for others.  Thus, if you have a
    customization file then you may want to invoke :ubt 1 before certifying
    any books; alternatively, see *note CERTIFY-BOOK!:: for automatic
    invocation of ubt.
    
    On the other hand, if you wish to prevent undoing commands from the
    customization file, see *note RESET-PREHISTORY::.
    
    Finally, we note that except on Windows-based systems, if there is a
    file acl2-init.lsp in your home directory, then it will be loaded into
    raw Lisp when ACL2 is invoked.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-BINOP,  Next: ADD-DEFAULT-HINTS,  Prev: ACL2-CUSTOMIZATION,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-BINOP    associate a function name with a macro name
    
    The form (add-binop macro macro-fn) is an abbreviation for the form
    (add-macro-fn macro macro-fn t).  See *note ADD-MACRO-FN::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-DEFAULT-HINTS,  Next: ADD-DEFAULT-HINTS!,  Prev: ADD-BINOP,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-DEFAULT-HINTS    add to the default hints
    
    
         Examples:
         (add-default-hints '((computed-hint-1 clause)
                              (computed-hint-2 clause
                                               stable-under-simplificationp)))
         (add-default-hints '((computed-hint-3 id clause world))
                            :at-end t)
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It is
    local to the book or encapsulate form in which it occurs (see *note
    ADD-DEFAULT-HINTS!:: for a corresponding non-local event).
    
         General Forms:
         (add-default-hints lst)
         (add-default-hints lst :at-end flg)
    
    where lst is a list.  Generally speaking, the elements of lst should be
    suitable for use as computed-hints.
    
    This event is completely analogous to set-default-hints, the difference
    being that add-default-hints appends the indicated hints to the front of
    the list of default hints, so that they are tried first -- or, if flg
    is supplied and evaluates to other than nil, at the end of the list, so
    that they are tried last -- rather than *replacing* the default hints
    with the indicated hints.  Each new hint is thus considered after each
    existing hints when both are applied to the same goal.  Also See *note
    SET-DEFAULT-HINTS::, see *note REMOVE-DEFAULT-HINTS::, and see *note
    DEFAULT-HINTS::.
    
    Finally, note that the effects of set-default-hints, add-default-hints,
    and remove-default-hints are local to the book in which they appear.
    Thus, users who include a book with such forms will not have their
    default hints affected by such forms.  In order to export the effect of
    setting the default hints, use set-default-hints!, add-default-hints!,
    or remove-default-hints!.
    
    For a related feature, which however is only for advanced system
    builders, see *note OVERRIDE-HINTS::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-DEFAULT-HINTS!,  Next: ADD-DIVE-INTO-MACRO,  Prev: ADD-DEFAULT-HINTS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-DEFAULT-HINTS!    add to the default hints non-locally
    
    Please see *note ADD-DEFAULT-HINTS::, which is the same as
    add-default-hints!  except that the latter is not local to the
    encapsulate or the book in which it occurs.  Probably add-default-hints
    is to be preferred unless you have a good reason for wanting to export
    the effect of this event outside the enclosing encapsulate or book.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-DIVE-INTO-MACRO,  Next: ADD-INCLUDE-BOOK-DIR,  Prev: ADD-DEFAULT-HINTS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-DIVE-INTO-MACRO    associate proof-checker diving function with macro name
    
         Examples:
         (add-dive-into-macro cat expand-address-cat)
    
    This feature is used so that the proof-checker's DV command and numeric
    diving commands (e.g., 3) will dive properly into subterms.  Please see
    *note DIVE-INTO-MACROS-TABLE::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-INCLUDE-BOOK-DIR,  Next: ADD-INVISIBLE-FNS,  Prev: ADD-DIVE-INTO-MACRO,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-INCLUDE-BOOK-DIR    link keyword for :dir argument of ld and include-book
    
         Example Form:
         (add-include-book-dir :smith "/u/smith/")
          ; For (include-book "foo" :dir :smith), prepend "/u/smith/" to "foo".
    
         General Form:
         (add-include-book-dir kwd dir)
    
    where kwd is a keywordp and dir is the pathname of a directory.  (If
    the final '/' is missing, ACL2 will add it for you.)  The effect of
    this event is to modify the meaning of the :dir keyword argument of
    include-book as indicated by the examples above, and similarly for ld,
    namely by associating the indicated directory with the indicated
    keyword for purposes of the :dir argument.  By the "indicated
    directory" we mean, in the case that the pathname is a relative
    pathname, the directory relative to the current connected book
    directory; see *note CBD::.  See *note DELETE-INCLUDE-BOOK-DIR:: for
    how to undo this effect.
    
    A keyword that is already associated with a directory string by an
    existing invocation of add-include-book-dir cannot be associated with a
    different directory string.  If that is your intention, first apply
    delete-include-book-dir to that keyword; see *note
    DELETE-INCLUDE-BOOK-DIR::.  If however the new directory string is
    identical with the old, then the call of add-include-book-dir will be
    redundant (see *note REDUNDANT-EVENTS::).
    
    The keyword :system can never be redefined.  It will always point to the
    absolute pathname of the system books directory, which by default is
    immediately under the directory where the ACL2 executable was originally
    built (see *note INCLUDE-BOOK::, in particular the discussion there of
    "books directory").
    
    This macro generates (in essence) a call (table acl2-defaults-table
    :include-book-dir-alist ...)  and hence is local to any books and
    encapsulate events in which it occurs.  See *note
    ACL2-DEFAULTS-TABLE::.  Even if you invoke add-include-book-dir before
    certifying a book, so that this event is among the book's portcullis
    commands rather than in the book itself, nevertheless that
    add-include-book-dir event will not be visible after the book is
    included.  (Note: The above behavior is generally preserved in raw-mode
    (see *note SET-RAW-MODE::),though by means other than a table.)
    
    
    File: acl2-doc-emacs.info,  Node: ADD-INVISIBLE-FNS,  Next: ADD-LD-KEYWORD-ALIAS,  Prev: ADD-INCLUDE-BOOK-DIR,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-INVISIBLE-FNS    make some unary functions invisible to the loop-stopper algorithm
    
         Examples:
         (add-invisible-fns binary-+ unary-- foo)
         (add-invisible-fns + unary-- foo)
    
    Each of the events above makes unary functions unary- and foo
    "invisible" for the purposes of applying permutative :rewrite rules to
    binary-+ trees.  Thus, arg and (unary- arg) will be given the same
    weight and will be permuted so as to be adjacent.
    
         General Form:
         (add-invisible-fns top-fn unary-fn1 ... unary-fnk)
    
    where top-fn is a function symbol and the unary-fni are unary function
    symbols, or more generally, these are all macro aliases for function
    symbols (see *note MACRO-ALIASES-TABLE::).
    
    For more information see *note INVISIBLE-FNS-TABLE::.  Also see *note
    SET-INVISIBLE-FNS-TABLE::, which explains how to set the entire table
    in a single event, and see *note REMOVE-INVISIBLE-FNS::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-LD-KEYWORD-ALIAS,  Next: ADD-LD-KEYWORD-ALIAS!,  Prev: ADD-INVISIBLE-FNS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-LD-KEYWORD-ALIAS    See *note LD-KEYWORD-ALIASES::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-LD-KEYWORD-ALIAS!,  Next: ADD-MACRO-ALIAS,  Prev: ADD-LD-KEYWORD-ALIAS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-LD-KEYWORD-ALIAS!    See *note LD-KEYWORD-ALIASES::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-MACRO-ALIAS,  Next: ADD-MACRO-FN,  Prev: ADD-LD-KEYWORD-ALIAS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-MACRO-ALIAS    associate a function name with a macro name
    
         Example:
         (add-macro-alias append binary-append)
    
    This example associates the function symbol binary-append with the
    macro name append.  As a result, the name append may be used as a runic
    designator (see *note THEORIES::) by the various theory functions.  See
    *note MACRO-ALIASES-TABLE:: for more details.  Also see *note
    ADD-MACRO-FN:: for an extension of this utility that also affects
    printing.
    
         General Form:
         (add-macro-alias macro-name function-name)
    
    This is a convenient way to add an entry to macro-aliases-table.  See
    *note MACRO-ALIASES-TABLE:: and also see *note REMOVE-MACRO-ALIAS::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-MACRO-FN,  Next: ADD-MATCH-FREE-OVERRIDE,  Prev: ADD-MACRO-ALIAS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-MACRO-FN    associate a function name with a macro name
    
         Examples:
         (add-macro-fn append binary-append)
         (add-macro-fn append binary-append t)
    
    These examples each associate the function symbol binary-append with
    the macro name append.  As a result, theory functions will understand
    that append refers to binary-append -- see *note ADD-MACRO-ALIAS:: --
    and moreover, proof output will be printed using append rather than
    binary-append.  In the first case, (append x (append y z)) is printed
    rather than (append x y z).  In the second case, right-associated
    arguments are printed flat: (append x y z).  Such right-association is
    considered only for binary function symbols; otherwise the optional
    third argument is ignored.
    
         General Forms:
         (add-macro-fn macro-name function-name)
         (add-macro-fn macro-name function-name nil) ; same as abov
         (add-macro-fn macro-name function-name t)
    
    This is a convenient way to add an entry to macro-aliases-table and at
    the same time extend the untrans-table.  As suggested by the example
    above, calls of a function in this table will be printed as
    corresponding calls of macros, with right-associated arguments printed
    flat in the case of a binary function symbol if the optional third
    argument is t.  In that case, for a binary function symbol fn
    associated with macro name mac, then a call (fn arg1 (fn arg2 (... (fn
    argk arg)))) will be displayed to the user as though the "term" were
    (mac arg1 arg2 ... argk arg).  For a call (f a1 ... ak) of a function
    symbol that is not binary, or the optional argument is not supplied as
    t, then the effect is simply to replace f by the corresponding macro
    symbol.  See *note ADD-MACRO-ALIAS::, which is invoked on the first two
    arguments.  Also see *note REMOVE-MACRO-ALIAS::, see *note
    UNTRANS-TABLE::, and see *note REMOVE-MACRO-FN::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-MATCH-FREE-OVERRIDE,  Next: ADD-NTH-ALIAS,  Prev: ADD-MACRO-FN,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-MATCH-FREE-OVERRIDE    set :match-free value to :once or :all in existing rules
    
         Example Forms:
         (add-match-free-override :once t)
             ; Try only the first binding of free variables when relieving hypotheses
             ; of any rule of class :rewrite, :linear, or :forward-chaining.
         (add-match-free-override :all (:rewrite foo) (:rewrite bar))
             ; For rewrite rules foo and bar, try all bindings of free variables when
             ; relieving hypotheses.
         (add-match-free-override :clear)
             ; Restore :match-free to what was originally stored for each rule (either
             ; :all or :once).
    
    As described elsewhere (see *note FREE-VARIABLES::), a rewrite, linear,
    or forward-chaining rule may have free variables in its hypotheses, and
    ACL2 can be directed either to try all bindings (":all") or just the
    first (":once") when relieving a hypothesis, as a basis for relieving
    subsequent hypotheses.  This direction is generally provided by
    specifying either :match-free :once or :match-free :all in the
    :rule-classes of the rule, or by using the most recent
    set-match-free-default event.  Also see *note RULE-CLASSES::.
    
    However, if a proof is going slowly, you may want to modify the
    behavior of some such rules so that they use only the first match for
    free variables in a hypothesis when relieving subsequent hypotheses,
    rather than backtracking and trying additional matches as necessary.
    (But note: add-match-free-override is not relevant for type-prescription
    rules.)  The event (add-match-free-override :once t) has that effect.
    Or at the other extreme, perhaps you want to specify all rules as :all
    rules except for a some specific exceptions.  Then you can execute
    (add-match-free-override :all t) followed by, say,
    (add-match-free-override :once (:rewrite foo) (:linear bar)).
    
         General Forms:
         (add-match-free-override :clear)
         (add-match-free-override flg t)
         (add-match-free-override flg rune1 rune2 ... runek)
    
    where flg is :once or :all and the runei are runes.  If :clear is
    specified then all rules will have the :all/:once behavior from when
    they were first stored.  The second general form causes all rewrite
    linear, and forward-chaining rules to have the behavior specified by
    flg (:all or :once).  Finally, the last of these, where runes are
    specified, is additive in the sense that only the indicated rules are
    affected; all others keep the behavior they had just before this event
    was executed (possible because of earlier add-match-free-override
    events).
    
    At the conclusion of this event, ACL2 prints out the list of all
    :linear, :rewrite, and :forward-chaining runes whose rules contain free
    variables in hypotheses that are to be bound :once, except that if
    there are no overrides (value :clear was used), then :clear is printed.
    
    This event only affects rules that exist at the time it is executed.
    Future rules are not affected by the override.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It
    uses the acl2-defaults-table, and hence its effect is local to the book
    or encapsulate form in which it occurs.
    
    _Remarks_
    
    Lists of the :rewrite, :linear, and :forward-chaining runes whose
    behavior was originally :once or :all are returned by the following
    forms, respectively.
    
         (free-var-runes :once (w state))
         (free-var-runes :all  (w state))
    
    The form
    
         (match-free-override (w state))
    
    evaluates to a pair, whose car is a number used by ACL2 to determine
    whether a rune is sufficiently old to be affected by the override, and
    whose cdr is the list of runes whose behavior is specified as :once by
    add-match-free-override; except, if no runes have been overridden, then
    the keyword :clear is returned.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-NTH-ALIAS,  Next: ADD-OVERRIDE-HINTS,  Prev: ADD-MATCH-FREE-OVERRIDE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-NTH-ALIAS    associate one symbol with another for printing of nth/update-nth terms
    
         Example:
         (add-nth-alias st0 st)
    
    This example associates the symbol st0 with the symbol st for purposes
    of printing certain terms of the form (nth n st0) and (update-nth n val
    st0).
    
         General Form:
         (add-nth-alias alias-name name)
    
    This is a convenient way to add an entry to nth-aliases-table.  See
    *note NTH-ALIASES-TABLE:: and also see *note REMOVE-NTH-ALIAS::.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-OVERRIDE-HINTS,  Next: ADD-OVERRIDE-HINTS!,  Prev: ADD-NTH-ALIAS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-OVERRIDE-HINTS    add to the override-hints
    
    See *note OVERRIDE-HINTS:: for a discussion of override-hints.  Here we
    describe how to extend the list of override-hints.  Note that the
    effects of add-override-hints events are local to the books or
    encapsulate events in which they reside; see *note
    ADD-OVERRIDE-HINTS!:: to avoid that restriction.  Also see *note
    SET-OVERRIDE-HINTS:: to set a new list of override-hints to it,
    ignoring the present list rather than adding to it.
    
         General Forms:
         (add-override-hints form)
         (add-override-hints form :at-end t)
         (add-override-hints form :at-end nil) ; default for :at-end
    
    where form evaluates to a list of computed hint forms.  The effect of
    this event is to extend the current list of override-hints by appending
    the result of that evaluation.  The default is to append the evaluation
    result to the front of the current list of override-hints, but if
    :at-end t is specified, then the evaluation result is appended to the
    end of the current list.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-OVERRIDE-HINTS!,  Next: DEFAULT-HINTS-TABLE,  Prev: ADD-OVERRIDE-HINTS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    ADD-OVERRIDE-HINTS!    add non-locally to the override-hints
    
    Add-override-hints! is the same as add-override-hints, except that the
    former is not local to books or encapsulate events in which it occurs.
    See *note ADD-OVERRIDE-HINTS::; also see *note SET-OVERRIDE-HINTS::.
    
    
    File: acl2-doc-emacs.info,  Node: DEFAULT-HINTS-TABLE,  Next: DEFAULT-VERIFY-GUARDS-EAGERNESS,  Prev: ADD-OVERRIDE-HINTS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    DEFAULT-HINTS-TABLE    a table used to provide hints for proofs
    
    Please see *note SET-DEFAULT-HINTS::, see *note ADD-DEFAULT-HINTS::, and
    see *note REMOVE-DEFAULT-HINTS:: for how to use this table.  For
    completeness, we mention here that under the hood, these events all
    update the default-hints-table by updating its key, t, for example as
    follows.
    
         (table default-hints-table t
                '((computed-hint-1 clause)
                  (computed-hint-2 clause
                                   stable-under-simplificationp)))
    
    The use of default hints is explained elsewhere; see *note
    SET-DEFAULT-HINTS::.
    
    Advanced users only: see *note OVERRIDE-HINTS:: for an advanced variant
    of default hints.
    
    
    File: acl2-doc-emacs.info,  Node: DEFAULT-VERIFY-GUARDS-EAGERNESS,  Next: DELETE-INCLUDE-BOOK-DIR,  Prev: DEFAULT-HINTS-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    DEFAULT-VERIFY-GUARDS-EAGERNESS    See *note SET-VERIFY-GUARDS-EAGERNESS::.
    
    
    File: acl2-doc-emacs.info,  Node: DELETE-INCLUDE-BOOK-DIR,  Next: DIVE-INTO-MACROS-TABLE,  Prev: DEFAULT-VERIFY-GUARDS-EAGERNESS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    DELETE-INCLUDE-BOOK-DIR    unlink keyword for :dir argument of ld and include-book
    
         Example Forms:
         (delete-include-book-dir :smith)
          ; Remove association of directory with :smith for include-book.
    
         General Form:
         (delete-include-book-dir kwd)
    
    where kwd is a keywordp.  The effect of this event is to modify the
    meaning of the :dir keyword argument of include-book and ld as
    indicated by the examples above, namely by removing association of any
    directory with the indicated keyword for purposes of the include-book
    (and ld) :dir argument.  Normally one would instead use
    add-include-book-dir to associate a new directory with that keyword;
    see *note ADD-INCLUDE-BOOK-DIR::.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
    This macro is local to any books and encapsulate events in which it
    occurs; see *note ADD-INCLUDE-BOOK-DIR:: for a discussion of this aspect
    of both macros.
    
    
    File: acl2-doc-emacs.info,  Node: DIVE-INTO-MACROS-TABLE,  Next: FINALIZE-EVENT-USER,  Prev: DELETE-INCLUDE-BOOK-DIR,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    DIVE-INTO-MACROS-TABLE    right-associated function information for the proof-checker
    
         Examples:
         ACL2 !>(dive-into-macros-table (w state))
         ((CAT . EXPAND-ADDRESS-CAT)
          (LXOR . EXPAND-ADDRESS-LXOR)
    
    This table associates macro names with functions used by the
    proof-checker's DV and numeric diving commands (e.g., 3) in order to
    dive properly into subterms.  See *note PROOF-CHECKER::, in particular
    the documentation for DV.
    
    This table can be extended easily.  See *note ADD-DIVE-INTO-MACRO:: and
    also see *note REMOVE-DIVE-INTO-MACRO::.
    
    The symbol associated with a macro should be a function symbol taking
    four arguments, in this order:
    
         car-addr ; the first number in the list given to the proof-checker's
                    DV command
         raw-term ; the untranslated term into which we will dive
         term     ; the translated term into which we will dive
         wrld     ; the current ACL2 logical world
    
    The function will normally return a list of positive integers,
    representing the (one-based) address for diving into term that
    corresponds to the single-address dive into raw-term by car-address.
    However, it can return (cons str alist), where str is a string suitable
    for fmt and args is the corresponding alist for fmt.
    
    Referring to the example above, expand-address-cat would be such a
    function, which will be called on raw-term values that are calls of
    cat.  See the community book books/misc/rtl-untranslate.lisp for the
    definition of such a function.
    
    See *note TABLE:: for a general discussion of tables.
    
    
    File: acl2-doc-emacs.info,  Node: FINALIZE-EVENT-USER,  Next: INITIALIZE-EVENT-USER,  Prev: DIVE-INTO-MACROS-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    FINALIZE-EVENT-USER    user-supplied code to complete events, e.g., with extra summary output
    
    This utility is intended for system hackers, not standard ACL2 users.
    
    ACL2 prints summaries at the conclusions of processing events (unless
    summaries are inhibited; see *note SET-INHIBIT-OUTPUT-LST:: and also
    see *note SET-INHIBITED-SUMMARY-TYPES::).  You may arrange for
    processing to take place just after the summary, by defining a function
    with argument list (ctx body state) that returns one value, namely
    state.  We describe ctx and body at the end below, but you may simply
    prefer to ignore these arguments.)  Your function should normally be a
    guard-verified :logic mode function with no guard other than that
    provided by the input requirement on state, that is, (state-p state);
    but later below we discuss how to avoid this requirement.  You then
    attach (see *note DEFATTACH::) your function to the function
    finalize-event-user.  The following example illustrates how this all
    works.
    
         (defun finalize-event-user-test (ctx body state)
           (declare (xargs :stobjs state)
                    (ignore ctx body))
           (cond ((and (boundp-global 'abbrev-evisc-tuple state)
                       (open-output-channel-p *standard-co*
                                              :character
                                              state))
                  (pprogn
                   (if (eq (f-get-global 'abbrev-evisc-tuple state) :DEFAULT)
                       (princ$ "Abbrev-evisc-tuple has its default value.~%"
                               *standard-co*
                               state)
                     (princ$ "Abbrev-evisc-tuple has been modified.~%"
                             *standard-co*
                             state))))
                 (t state)))
    
         (defattach finalize-event-user finalize-event-user-test)
    
    After admission of the two events above, an event summary will conclude
    with extra printout, for example:
    
         Note: Abbrev-evisc-tuple has its default value.
    
    If the attachment function (above, finalize-event-user-test) does not
    meet all the requirements stated above, then you can use the
    :skip-checks argument of defattach to get around the requirement, as
    illustrated by the following example.
    
         (defun finalize-event-user-test2 (state)
           (declare (xargs :stobjs state
                           :mode :program)
                    (ignore ctx body))
           (observation
            'my-test
            "~|Value of term-evisc-tuple: ~x0~|"
            (f-get-global 'term-evisc-tuple state)))
    
         (defttag t) ; needed for :skip-checks t
    
         (defattach (finalize-event-user finalize-event-user-test2)
                    :skip-checks t)
    
    So for example:
    
         ACL2 !>(set-term-evisc-tuple (evisc-tuple 2 7 nil nil) state)
          (:TERM)
         ACL2 !>(defconst *foo6* '(a b c))
    
         Summary
         Form:  ( DEFCONST *FOO6* ...)
         Rules: NIL
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
         ACL2 Observation in MY-TEST:
         Value of term-evisc-tuple: (NIL 2 7 NIL)
          *FOO6*
         ACL2 !>
    
    Note that (as of this writing) the macro observation expands to a call
    of a :program-mode function.  Thus, the trick shown above involving
    :skip-checks allows the use of :program-mode functions; for example,
    you can print with fmt.
    
    See community book books/misc/defattach-bang.lisp for a variant of
    defattach that uses ec-call to avoid issues of guard verification.
    
    Also see *note INITIALIZE-EVENT-USER::, which discusses the handling of
    state globals by that utility as well as by finalize-event-user.
    
    Finally, as promised above, we briefly describe the arguments ctx and
    body.  These are the arguments passed to the call of macro
    with-ctx-summarized under which finalize-event-user (or
    initialize-event-user) was called.  Thus, they are unevaluated
    expressions.  For example, system function defthm-fn1 has a body of the
    following form.
    
         (with-ctx-summarized
          (if (output-in-infixp state) event-form (cons 'defthm name))
          (let ((wrld (w state))
                (ens (ens state))
                .....
    
    Thus, when initialize-event-user and finalize-event-user are called on
    behalf of defthm, ctx is the s-expression
    
         (if (output-in-infixp state) event-form (cons 'defthm name))
    
    while body is the following s-expression (with most code elided).
    
          (let ((wrld (w state))
                (ens (ens state))
                .....
    
    You might find it helpful to use trace$ to get a sense of ctx and body,
    for example, (trace$ finalize-event-user).
    
    
    File: acl2-doc-emacs.info,  Node: INITIALIZE-EVENT-USER,  Next: INVISIBLE-FNS-TABLE,  Prev: FINALIZE-EVENT-USER,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    INITIALIZE-EVENT-USER    user-supplied code to initiate events
    
    This utility is intended for system hackers, not standard ACL2 users.
    
    See *note FINALIZE-EVENT-USER:: to see how to supply code to be run at
    the end of events.  We assume familiarity with finalize-event-user;
    here we focus on how to supply code for the beginning as well as the
    end of events.
    
    As with finalize-event-user, you attach your own function of argument
    list (ctx qbody state) to initialize-event-user.  (See *note
    FINALIZE-EVENT-USER:: for discussion of ctx and body.)  The attachment
    should return state and have a trivial guard, requiring (implicitly)
    only that state satisfies state-p unless you use trust tags to avoid
    that requirement.  For example:
    
         (defattach initialize-event-user initialize-event-user-test)
    
    Why would you want to do this?  Presumably you are building a system on
    top of ACL2 and you want to track your own data.  For example, suppose
    you want to save the time in some state global variable, my-time.  You
    could do the following.
    
         (defun my-init (ctx body state)
           (declare (xargs :stobjs state
                           :guard-hints
                           (("Goal" :in-theory (enable read-run-time))))
                    (ignore ctx body))
           (mv-let (seconds state)
                   (read-run-time state)
                   (f-put-global 'start-time seconds state)))
    
         (defun my-final (ctx body state)
           (declare (xargs :stobjs state
                           :guard-hints
                           (("Goal" :in-theory (enable read-run-time))))
                    (ignore ctx body))
           (mv-let (seconds state)
                   (read-run-time state)
                   (prog2$ (if (boundp-global 'start-time state)
                               (cw "Time: ~x0 seconds~%"
                                   (- seconds (fix (@ start-time))))
                             (cw "BIG SURPRISE!~%"))
                           (f-put-global 'end-time seconds state))))
    
         (defattach initialize-event-user my-init)
         (defattach finalize-event-user my-final)
    
    Here is an abbreviated log, showing the time being printed at the end.
    
         ACL2 !>(thm (equal (append (append x y) x y x y x y x y)
                            (append x y x y x y x y x y)))
    
         *1 (the initial Goal, a key checkpoint) is pushed for proof by induction.
         ....
         ACL2 Error in ( THM ...):  See :DOC failure.
    
         ******** FAILED ********
         Time: 869/100 seconds
         ACL2 !>
    
    
    File: acl2-doc-emacs.info,  Node: INVISIBLE-FNS-TABLE,  Next: LD-KEYWORD-ALIASES,  Prev: INITIALIZE-EVENT-USER,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    INVISIBLE-FNS-TABLE    functions that are invisible to the loop-stopper algorithm
    
         Examples:
         ACL2 !>(invisible-fns-table (w state))
         ((binary-+ unary--)
          (binary-* unary-/)
          (unary-- unary--)
          (unary-/ unary-/))
    
    Among other things, the setting above has the effect of making unary-
    "invisible" for the purposes of applying permutative :rewrite rules to
    binary-+ trees.  Also see *note ADD-INVISIBLE-FNS:: and see *note
    REMOVE-INVISIBLE-FNS::, which manage macro aliases (see *note
    MACRO-ALIASES-TABLE::), as well as see *note SET-INVISIBLE-FNS-TABLE::.
    
    See *note TABLE:: for a general discussion of tables.
    
    The "invisible functions table" is an alist with elements of the
    following form, where fn is a function symbol and the ufni are unary
    function symbols in the current ACL2 world, and k is at least 1.
    
         (fn ufn1 ufn2 ... ufnk)
    
    This table thus associates with certain function symbols, e.g., fn
    above, a set of unary functions, e.g., the ufni above.  The ufni
    associated with fn in the invisible functions table are said to be
    "invisible with respect to fn."  If fn is not the car of any pair in
    the alist, then no function is invisible for it.  Thus for example,
    setting the invisible functions alist to nil completely eliminates the
    consideration of invisibility.
    
    The notion of invisibility is involved in the use of the :loop-stopper
    field of :rewrite rules to prevent the indefinite application of
    permutative rewrite rules.  Roughly speaking, if rewrite rules are
    being used to permute arg and (ufni arg) inside of a nest of fn calls,
    and ufni is invisible with respect to fn, then arg and (ufni arg) are
    considered to have the same "weight" and will be permuted so as to end
    up as adjacent tips in the fn nest.  See *note LOOP-STOPPER::.
    
    
    File: acl2-doc-emacs.info,  Node: LD-KEYWORD-ALIASES,  Next: LOGIC,  Prev: INVISIBLE-FNS-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    LD-KEYWORD-ALIASES    abbreviation of some keyword commands
    
         Examples:
         (set-ld-keyword-aliases '((:q 0 q-fn)
                                   (:e 0 exit-acl2-macro))
                                 state)
         (ld-keyword-aliases state) ; current value of the ld-keyword-aliases table
    
    Ld-keyword-aliases is the name of a ACL2 table (see *note TABLE::) and
    also the name of a function of state that returns the value of this
    table.  That value must be an alist, each element of which is of the
    form (:keyword n fn), where :keyword is a keyword, n is a nonnegative
    integer, and fn is a function symbol of arity n, a macro symbol, or a
    lambda expression of arity n.  When keyword is typed as an ld command,
    n more forms are read, x1, ..., xn, and the form (fn 'x1 ... 'xn) is
    then evaluated.  The initial value of the ld-keyword-aliases table is
    nil.
    
    ACL2 provides functions to modify the ld-keyword-aliases table, as
    follows.
    
         (Set-ld-keyword-aliases val state): sets the table to val, which
         must be a legal alist as described above.  This is an event that
         may go into a book (see *note EVENTS::), but its effect will be
         local to that book.
    
         Set-ld-keyword-aliases! is the same as set-ld-keyword-aliases,
         except that its effect is not local.  Indeed, the form
         (set-ld-keyword-aliases val state) is equivalent to the form
         (local (set-ld-keyword-aliases! val state).
    
         (Add-ld-keyword-alias key val state): modifies the table by
         binding the keyword key to val, which must be a legal value as
         described above.  This is an event that may go into a book (see
         *note EVENTS::), but its effect will be local to that book.
    
         Add-ld-keyword-alias! is the same as add-ld-keyword-alias, except
         that its effect is not local.  Indeed, the form
         (add-ld-keyword-alias key val state) is equivalent to the form
         (local (add-ld-keyword-alias! key val state).
    
    
    Consider the first example above:
    
         (set-ld-keyword-aliases '((:q 0 q-fn)
                                   (:e 0 exit-acl2-macro))
                                 state)
    
    With this event, :q is redefined to have the effect of executing
    (q-fn), so for example if you have defined q-fn with
    
         (defmacro q-fn ()
           '(er soft 'q "You un-bound :q and now we have a soft error."))
    
    then :q will cause an error, and if you have defined
    
         (defmacro exit-acl2-macro () '(exit-ld state))
    
    then :e will cause the effect (it so happens) that :q normally has.  If
    you prefer :e to :q for exiting the ACL2 loop, you might even want to
    put such definitions of q-fn and exit-acl2-macro together with the
    set-ld-keyword-aliases form above into your "acl2-customization.lsp"
    file; see *note ACL2-CUSTOMIZATION::.
    
    The general-purpose ACL2 read-eval-print loop, ld, reads forms from
    standard-oi, evaluates them and prints the result to standard-co.
    However, there are various flags that control ld's behavior and
    ld-keyword-aliases is one of them.  Ld-keyword-aliases affects how
    keyword commands are parsed.  Generally speaking, ld's command
    interpreter reads ":fn x1 ... xn" as "(fn 'x1 ... 'xn)" when :fn is a
    keyword and fn is the name of an n-ary function; see *note
    KEYWORD-COMMANDS::.  But this parse is overridden, as described above,
    for the keywords bound in the ld-keyword-aliases table.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC,  Next: MACRO-ALIASES-TABLE,  Prev: LD-KEYWORD-ALIASES,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    LOGIC    to set the default defun-mode to :logic
    
         Example:
         ACL2 p!>:logic
         ACL2 !>
    
    Typing the keyword :logic sets the default defun-mode to :logic.
    
    Functions defined in :logic mode are logically defined.  See *note
    DEFUN-MODE::.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
    See *note DEFUN-MODE:: for a discussion of the defun-modes available
    and what their effects on the logic are.  See *note
    DEFAULT-DEFUN-MODE:: for a discussion of how the default defun-mode is
    used.  This event is equivalent to (table acl2-defaults-table
    :defun-mode :logic), and hence is local to any books and encapsulate
    events in which it occurs. See *note ACL2-DEFAULTS-TABLE::.
    
    Recall that the top-level form :logic is equivalent to (logic); see
    *note KEYWORD-COMMANDS::.  Thus, to change the default defun-mode to
    :logic in a book, use (logic), which is an embedded event form, rather
    than :logic, which is not a legal form for books.  See *note
    EMBEDDED-EVENT-FORM::.
    
    
    File: acl2-doc-emacs.info,  Node: MACRO-ALIASES-TABLE,  Next: NTH-ALIASES-TABLE,  Prev: LOGIC,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    MACRO-ALIASES-TABLE    a table used to associate function names with macro names
    
         Example:
         (table macro-aliases-table 'append 'binary-append)
    
    This example associates the function symbol binary-append with the
    macro name append.  As a result, the name append may be used as a runic
    designator (see *note THEORIES::) by the various theory functions.
    Thus, for example, it will be legal to write
    
         (in-theory (disable append))
    
    as an abbreviation for
    
         (in-theory (disable binary-append))
    
    which in turn really abbreviates
    
         (in-theory (set-difference-theories (current-theory :here)
                                             '(binary-append)))
    
         General Form:
    
         (table macro-aliases-table 'macro-name 'function-name)
    
    or very generally
    
         (table macro-aliases-table macro-name-form function-name-form)
    
    where macro-name-form and function-name-form evaluate, respectively, to
    a macro name and a symbol in the current ACL2 world.  See *note TABLE::
    for a general discussion of tables and the table event used to
    manipulate tables.
    
    Note that function-name-form (above) does not need to evaluate to a
    function symbol, but only to a symbol.  As a result, one can introduce
    the alias before defining a recursive function, as follows.
    
         (table macro-aliases-table 'mac 'fn)
         (defun fn (x)
           (if (consp x)
               (mac (cdr x))
             x))
    
    Although this is obviously contrived example, this flexibility can be
    useful to macro writers; see for example the definition of ACL2 system
    macro defun-inline.
    
    The table macro-aliases-table is an alist that associates macro symbols
    with function symbols, so that macro names may be used as runic
    designators (see *note THEORIES::).  For a convenient way to add
    entries to this table, see *note ADD-MACRO-ALIAS::.  To remove entries
    from the table with ease, see *note REMOVE-MACRO-ALIAS::.
    
    This table is used by the theory functions; see *note THEORIES::.  For
    example, in order that (disable append) be interpreted as (disable
    binary-append), it is necessary that the example form above has been
    executed.  In fact, this table does indeed associate many of the macros
    provided by the ACL2 system, including append, with function symbols.
    Loosely speaking, it only does so when the macro is "essentially the
    same thing as" a corresponding function; for example, (append x y) and
    (binary-append x y) represent the same term, for any expressions x and
    y.
    
    
    File: acl2-doc-emacs.info,  Node: NTH-ALIASES-TABLE,  Next: PRINT-SUMMARY-USER,  Prev: MACRO-ALIASES-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    NTH-ALIASES-TABLE    a table used to associate names for nth/update-nth printing
    
         Example:
         (table nth-aliases-table 'st0 'st)
    
    This example associates the symbol st0 with the symbol st.  As a
    result, when the theorem prover prints terms of the form (nth n st0) or
    (update-nth n val st0), where st is a stobj whose nth accessor function
    is f-n, then it will print n as *f-n*.
    
         General Form:
         (table nth-aliases-table 'alias-name 'name)
    
    This event causes alias-name to be treated like name for purposes of
    the printing of terms that are calls of nth and update-nth.  (Note
    however that name is not recursively looked up in this table.)  Both
    must be symbols other than state.  See *note TERM::, in particular the
    discussion there of untranslated terms.
    
    For a convenient way to add entries to this table, see *note
    ADD-NTH-ALIAS::.  To remove entries from the table with ease, see *note
    REMOVE-NTH-ALIAS::.
    
    
    File: acl2-doc-emacs.info,  Node: PRINT-SUMMARY-USER,  Next: PROGRAM,  Prev: NTH-ALIASES-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    PRINT-SUMMARY-USER    See *note FINALIZE-EVENT-USER::.
    
    
    File: acl2-doc-emacs.info,  Node: PROGRAM,  Next: PUSH-UNTOUCHABLE,  Prev: PRINT-SUMMARY-USER,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    PROGRAM    to set the default defun-mode to :program
    
         Example:
         ACL2 !>:program
         ACL2 p!>
    
    Typing the keyword :program sets the default defun-mode to :program.
    
    Functions defined in :program mode are logically undefined but can be
    executed on constants outside of deductive contexts.  See *note
    DEFUN-MODE::.
    
    Calls of the following macros are ignored (skipped) when in :program
    mode.
    
         local
         verify-guards
         verify-termination
         defaxiom
         defthm
         deftheory
         in-theory
         in-arithmetic-theory
         regenerate-tau-database
         theory-invariant
         defchoose
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
    See *note DEFUN-MODE:: for a discussion of the defun-modes available
    and what their effects on the logic are.  See *note
    DEFAULT-DEFUN-MODE:: for a discussion of how the default defun-mode is
    used.  This event is equivalent to (table acl2-defaults-table
    :defun-mode :program), and hence is local to any books and encapsulate
    events in which it occurs. See *note ACL2-DEFAULTS-TABLE::.
    
    Recall that the top-level form :program is equivalent to (program); see
    *note KEYWORD-COMMANDS::.  Thus, to change the default defun-mode to
    :program in a book, use (program), which is an embedded event form,
    rather than :program, which is not a legal form for books.  See *note
    EMBEDDED-EVENT-FORM::.
    
    
    File: acl2-doc-emacs.info,  Node: PUSH-UNTOUCHABLE,  Next: REMOVE-BINOP,  Prev: PROGRAM,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    PUSH-UNTOUCHABLE    add name or list of names to the list of untouchable symbols
    
         Examples:
         (push-untouchable my-var nil)
         (push-untouchable set-mem t)
    
         General Form:
         (push-untouchable name{s}  fn-p :doc doc-string)
    
    where name{s} is a non-nil symbol or a non-nil true list of symbols,
    fn-p is any value (but generally nil or t), and doc-string is an
    optional documentation string not beginning with ":Doc-Section ...".
    If name{s} is a symbol it is treated as the singleton list containing
    that symbol.  The effect of this event is to union the given symbols
    into the list of "untouchable variables" in the current world if fn-p is
    nil, else to union the symbols into the list of "untouchable
    functions".  This event is redundant if every symbol listed is already
    a member of the appropriate untouchables list (variables or functions).
    
    When a symbol is on the untouchables list it is syntactically illegal
    for any event to call a function or macro of that name, if fn-p is
    non-nil, or to change the value of a state global variable of that
    name, if fn-p is nil.  Thus, the effect of pushing a function symbol,
    name, onto untouchables is to prevent any future event from using that
    symbol as a function or macro, or as a state global variable (according
    to fn-p).  This is generally done to "fence off" some primitive
    function symbol from "users" after the developer has used the symbol
    freely in the development of some higher level mechanism.
    
    Also see *note REMOVE-UNTOUCHABLE::.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-BINOP,  Next: REMOVE-DEFAULT-HINTS,  Prev: PUSH-UNTOUCHABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-BINOP    remove the association of a function name with a macro name
    
    The form (remove-binop macro-fn) is an abbreviation for the form
    (remove-macro-fn macro-fn).  See *note REMOVE-MACRO-FN::.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-DEFAULT-HINTS,  Next: REMOVE-DEFAULT-HINTS!,  Prev: REMOVE-BINOP,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-DEFAULT-HINTS    remove from the default hints
    
    
         Examples:
         (remove-default-hints '((computed-hint-1 clause)
                                 (computed-hint-2 clause
                                                  stable-under-simplificationp)))
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It is
    local to the book or encapsulate form in which it occurs (see *note
    REMOVE-DEFAULT-HINTS!:: for a corresponding non-local event).
    
         General Form:
         (remove-default-hints lst)
    
    where lst is a list.  Generally speaking, the elements of lst should be
    suitable for use as computed-hints.  Also see *note ADD-DEFAULT-HINTS::.
    
    If some elements of the given list do not belong to the existing default
    hints, they will simply be ignored by this event.
    
    Also See *note SET-DEFAULT-HINTS::, see *note ADD-DEFAULT-HINTS::, and
    see *note DEFAULT-HINTS::.
    
    Finally, note that the effects of set-default-hints, add-default-hints,
    and remove-default-hints are local to the book in which they appear.
    Thus, users who include a book with such forms will not have their
    default hints affected by such forms.  In order to export the effect of
    setting the default hints, use set-default-hints!, add-default-hints!,
    or remove-default-hints!.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-DEFAULT-HINTS!,  Next: REMOVE-DIVE-INTO-MACRO,  Prev: REMOVE-DEFAULT-HINTS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-DEFAULT-HINTS!    remove from the default hints non-locally
    
    Please see *note REMOVE-DEFAULT-HINTS::, which is the same as
    remove-default-hints!  except that the latter is not local to the
    encapsulate or the book in which it occurs.  Probably
    remove-default-hints is to be preferred unless you have a good reason
    for wanting to export the effect of this event outside the enclosing
    encapsulate or book.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-DIVE-INTO-MACRO,  Next: REMOVE-INVISIBLE-FNS,  Prev: REMOVE-DEFAULT-HINTS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-DIVE-INTO-MACRO    removes association of proof-checker diving function with macro name
    
         Example:
         (remove-dive-into-macro logand)
    
    This feature undoes the effect of add-dive-into-macro, which is used so
    that the proof-checker's DV command and numeric diving commands (e.g.,
    3) will dive properly into subterms.  Please see *note
    ADD-DIVE-INTO-MACRO:: and especially see *note DIVE-INTO-MACROS-TABLE::.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-INVISIBLE-FNS,  Next: REMOVE-MACRO-ALIAS,  Prev: REMOVE-DIVE-INTO-MACRO,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-INVISIBLE-FNS    make some unary functions no longer invisible
    
         Examples:
         (remove-invisible-fns (binary-+ unary-- foo)
         (remove-invisible-fns (+ unary-- foo)
    
    The setting above has makes unary functions unary- and foo no longer
    "invisible" for the purposes of applying permutative :rewrite rules to
    binary-+ trees.
    
         General Form:
         (remove-invisible-fns top-fn unary-fn1 ... unary-fnk)
    
    where top-fn is a function symbol and the unary-fni are unary function
    symbols, or more generally, these are all macro aliases for function
    symbols (see *note MACRO-ALIASES-TABLE::).
    
    See *note ADD-INVISIBLE-FNS:: and also see *note INVISIBLE-FNS-TABLE::
    and see *note SET-INVISIBLE-FNS-TABLE::.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-MACRO-ALIAS,  Next: REMOVE-MACRO-FN,  Prev: REMOVE-INVISIBLE-FNS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-MACRO-ALIAS    remove the association of a function name with a macro name
    
         Example:
         (remove-macro-alias append)
    
         General Form:
         (remove-macro-alias macro-name)
    
    See *note MACRO-ALIASES-TABLE:: for a discussion of macro aliases; also
    see *note ADD-MACRO-ALIAS::.  This form sets macro-aliases-table to the
    result of deleting the key macro-name from that table.  If the name
    does not occur in the table, then this form still generates an event,
    but the event has no real effect.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-MACRO-FN,  Next: REMOVE-NTH-ALIAS,  Prev: REMOVE-MACRO-ALIAS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-MACRO-FN    remove the association of a function name with a macro name
    
         Example:
         (remove-macro-fn binary-append)
    
         General Form:
         (remove-macro-fn macro-fn)
    
    See *note ADD-MACRO-FN:: for a discussion of how to associate a macro
    name with a function name.  This form sets untrans-table to the result
    of deleting the association of a macro name with the given binary
    function name.  If the function name has no such association, then this
    form still generates an event, but the event has no real effect.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-NTH-ALIAS,  Next: REMOVE-OVERRIDE-HINTS,  Prev: REMOVE-MACRO-FN,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-NTH-ALIAS    remove a symbol alias for printing of nth/update-nth terms
    
         Example:
         (remove-nth-alias append)
    
         General Form:
         (remove-nth-alias alias-name)
    
    See *note NTH-ALIASES-TABLE:: for further discussion; also see *note
    ADD-NTH-ALIAS::.  This form sets nth-aliases-table to the result of
    deleting the key alias-name from that table.  If the name does not
    occur in the table, then this form still generates an event, but the
    event has no real effect.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-OVERRIDE-HINTS,  Next: REMOVE-OVERRIDE-HINTS!,  Prev: REMOVE-NTH-ALIAS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-OVERRIDE-HINTS    delete from the list of override-hints
    
    See *note OVERRIDE-HINTS:: for a discussion of override-hints.  Here we
    describe how to delete from the list of override-hints.  Note that the
    effects of remove-override-hints events are local to the books or
    encapsulate events in which they reside; see *note
    REMOVE-OVERRIDE-HINTS!:: to avoid that restriction.  Also see *note
    ADD-OVERRIDE-HINTS:: and see *note SET-OVERRIDE-HINTS::.
    
         General Form:
         (remove-override-hints form)
    
    where form should evaluate to a list of computed hint forms.  The effect
    of this event is to set the list of override-hints to the result of
    deleting each element of the evaluation result from the override-hints,
    if that element indeed belongs to the override-hints; no check is made
    that these elements are actually elements of the existing
    override-hints.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-OVERRIDE-HINTS!,  Next: REMOVE-UNTOUCHABLE,  Prev: REMOVE-OVERRIDE-HINTS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-OVERRIDE-HINTS!    delete non-locally from the list of override-hints
    
    Remove-override-hints! is the same as remove-override-hints, except
    that the former is not local to books or encapsulate events in which it
    occurs.  See *note REMOVE-OVERRIDE-HINTS::; also see *note
    ADD-OVERRIDE-HINTS:: and see *note SET-OVERRIDE-HINTS::.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-UNTOUCHABLE,  Next: RESET-PREHISTORY,  Prev: REMOVE-OVERRIDE-HINTS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    REMOVE-UNTOUCHABLE    remove names from lists of untouchable symbols
    
         Example Forms:
         (remove-untouchable my-var nil) ; then state global my-var is not untouchable
         (remove-untouchable set-mem t)  ; then function set-mem is not untouchable
    
    Also see *note PUSH-UNTOUCHABLE::.
    
    This documentation topic is directed at those who build systems on top
    of ACL2.  We first describe a means for removing restrictions related to
    so-called "untouchables": functions (or macros) that cannot be called,
    or state global variables that cannot be modified or unbound, without
    intervention that requires an active trust tag (see *note DEFTTAG::).
    Then we describe the remove-untouchable event.
    
    We begin by discussing untouchable state global variables
    temp-touchable-vars and temp-touchable-fns, which initially have value
    nil.  These can often be used in place of remove-untouchable.  When the
    value is t, no variable (respectively, no function or macro) is treated
    as untouchable, regardless of the set of initial untouchables or the
    remove-untouchable or push-untouchable events that have been admitted.
    Otherwise the value of each of these two variables is a symbol-listp,
    and no member of this list is treated as an untouchable variable (in
    the case of temp-touchable-vars) or as an untouchable function or macro
    (in the case of temp-touchable-fns).  These two state global variables
    can be set by set-temp-touchable-vars and set-temp-touchable-fns,
    respectively, provided there is an active trust tag (see *note
    DEFTTAG::).  Here is an illustrative example.  This macro executes the
    indicated forms in a context where there are no untouchable variables,
    but requires an active trust tag when invoked.
    
         (defmacro with-all-touchable (&rest forms)
           `(progn!
             :state-global-bindings
             ((temp-touchable-vars t set-temp-touchable-vars))
             (progn! ,@forms)))
    
    An equivalent version, which however is not recommended since
    state-global-let* may have surprising behavior in raw Lisp, is as
    follows.
    
         (defmacro with-all-touchable (&rest forms)
           `(progn!
             (state-global-let*
              ((temp-touchable-vars t set-temp-touchable-vars))
              (progn! ,@forms))))
    
    Finally, the value t for temp-touchable-vars removes the requirement
    that built-in state globals cannot be made unbound (with
    makunbound-global).
    
    We now turn to the remove-untouchable event, in case the approach above
    is for some reason not adequate.  This event is illegal by default,
    since it can be used to provide access to ACL2 internal functions and
    data structures that are intentionally made untouchable for the user.
    If you want to call it, you must first create an active trust tag; see
    *note DEFTTAG::.
    
         General Form:
         (remove-untouchable name{s}  fn-p :doc doc-string)
    
    where name{s} is a non-nil symbol or a non-nil true list of symbols,
    fn-p is any value (but generally nil or t), and doc-string is an
    optional documentation string not beginning with ":Doc-Section ...".
    If name{s} is a symbol it is treated as the singleton list containing
    that symbol.  The effect of this event is to remove the given symbols
    from the list of "untouchable variables" in the current world if fn-p
    is nil, else to remove the symbols into the list of "untouchable
    functions".  This event is redundant if no symbol listed is a member of
    the appropriate untouchables list (variables or functions).
    
    
    File: acl2-doc-emacs.info,  Node: RESET-PREHISTORY,  Next: RETURN-LAST-TABLE,  Prev: REMOVE-UNTOUCHABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    RESET-PREHISTORY    reset the prehistory
    
         Examples:
         (reset-prehistory)     ; restart command numbering at 0
         (reset-prehistory nil) ; same as above
         (reset-prehistory t)   ; as above except also disable ubt-prehistory
    
         General Forms:
         (reset-prehistory)
         (reset-prehistory permanent-p)
         (reset-prehistory permanent-p doc-string)
    
    where permanent-p is t or nil, and doc-string is an optional
    documentation string not beginning with ":Doc-Section ...".  After
    execution of this command, ACL2 will change the numbering provided by
    its history utilities so that this reset-prehistory command (or the
    top-level compound command containing it, which for example might be an
    include-book) is assigned the number 0.  The only way to undo this
    command is with command ubt-prehistory.  However, even that is
    disallowed if permanent-p is t.
    
    Note that the second argument of certify-book, which specifies the
    number of commands in the certification world (i.e., since
    ground-zero), is not sensitive to reset-prehistory; rather, it expects
    the number of commands since ground-zero.  To see such commands, :pbt
    :start.
    
    A reset-prehistory event with value nil for permanent-p (the default)
    is always skipped during certify-book and include-book (indeed,
    whenever ld-skip-proofsp is t).  Otherwise one would find the history
    numbering reset to 0 just by virtue of including (or certifying) a book
    -- probably not what was intended.
    
    See *note UBT-PREHISTORY:: for how to undo a reset-prehistory command
    that does not have a permanent-p of t.
    
    
    File: acl2-doc-emacs.info,  Node: RETURN-LAST-TABLE,  Next: RULER-EXTENDERS,  Prev: RESET-PREHISTORY,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    RETURN-LAST-TABLE    install special raw Lisp behavior
    
    Please first see *note RETURN-LAST:: for relevant background.
    
    This table is for advanced users only, and requires an active trust tag
    (see *note DEFTTAG::).  We recommend that you do not modify this table
    directly, but instead use the macro defmacro-last.  Here we augment
    that discussion with some highly technical observations that can
    probably be ignored if you use defmacro-last.
    
    This table has a :guard requiring that each key be a symbol defined in
    raw Lisp, generally as a macro, and requiring that each non-nil value
    be associated either with a symbol that names a macro defined in ACL2,
    or else with a list of one element containing such a symbol.  The table
    can only be modified when there is an active trust tag; see *note
    DEFTTAG::.  If a key is associated with the value nil, then that key is
    treated as though it were not in the table.
    
    Note that keys of this table are not eligible to be bound by flet.  The
    current value of this table may be obtained by evaluating the form
    (table-alist 'return-last-table (w state)).  The built-in constant
    *initial-return-last-table* holds the initial value of this table.
    
    
    File: acl2-doc-emacs.info,  Node: RULER-EXTENDERS,  Next: SET-ABSSTOBJ-DEBUG,  Prev: RETURN-LAST-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    RULER-EXTENDERS    control for ACL2's termination and induction analyses
    
    *Introduction*
    
    Consider the following recursive definition, which returns a list of
    threes of length one more than the length of x.
    
           (defun f (x)
             (cons 3
                   (if (consp x)
                       (f (cdr x))
                     nil)))
    
    One might expect ACL2's termination analysis to admit this function,
    since we know that (cdr x) is "smaller" than x if (consp x) is true.
    (By default, ACL2's notion of "smaller" is ordinary natural-number <,
    and the argument x is measured by applying function acl2-count to x.)
    However, that termination analysis does not consider IF tests, like
    (consp x) above, when they occur under calls of functions other than
    IF, such as CONS in the case above.
    
    One way to overcome this problem is to "lift" the IF test to the top
    level, as follows.
    
           (defun f (x)
             (if (consp x)
                 (cons 3 (f (cdr x)))
               (cons 3 nil)))
    
    But another way to overcome the problem is to tell ACL2 to extend its
    termination (and induction) analysis through calls of cons, as follows.
    
           (defun f (x)
             (declare (xargs :ruler-extenders (cons)))
             (cons 3
                   (if (consp x)
                       (f (cdr x))
                     nil)))
    
    You may even wish to provide value :all instead of an explicit list of
    ruler-extenders, so that no function call blocks the termination
    analysis:
    
           (defun f (x)
             (declare (xargs :ruler-extenders :all))
             (cons 3
                   (if (consp x)
                       (f (cdr x))
                     nil)))
    
    Alternatively, you can omit the XARGS :RULER-EXTENDERS form, instead
    modifying the global default set of ruler-extenders:
    
           (set-ruler-extenders :all)
    
           ; or, for example:
           (set-ruler-extenders '(cons return-last))
    
    You can call the function default-ruler-extenders as follows to see the
    current global default set of ruler-extenders:
    
         (default-ruler-extenders (w state))
    
    We conclude this introduction by considering the handling of LET
    expressions by termination analysis.  Consider the following example.
    
           (defun fact (n)
             (the (integer 1 *)
                  (if (posp n)
                      (* n (fact (1- n)))
                    1)))
    
    ACL2 treats the call of THE in the body of this definition as follows.
    
           (let ((var (if (posp n)
                          (* n (fact (1- n)))
                        1)))
             (if (and (integerp var) (<= 1 var))
                 var
               ))
    
    A LET expression, in turn, is treated as a LAMBDA application:
    
           ((lambda (var)
              (if (if (integerp var)
                      (not (< var 1))
                    nil)
                  var
                ))
            (if (posp n)
                (* n (fact (1- n)))
              1))
    
    Notice that the posp test, which governs the recursive call of fact, is
    inside an argument of a function application, namely the application of
    the LAMBDA expression.  So by default, ACL2 will not consider this posp
    test in its termination analysis.  The keyword :LAMBDAS in the list of
    ruler-extenders denotes all calls of lambda expressions, much as the
    inclusion of CONS in the ruler-extenders denotes all calls of CONS.
    The following definition is thus accepted by ACL2.
    
           (defun fact (n)
             (declare (xargs :ruler-extenders (:lambdas)))
             (the (integer 1 *)
                  (if (posp n)
                      (* n (fact (1- n)))
                    1)))
    
    As a convenience, ACL2 allows the symbol :lambdas in place of
    (:lambdas), and in fact the former will also include the default
    ruler-extenders: RETURN-LAST (which comes from macroexpansion of calls
    of PROG2$, EC-CALL, and others) and MV-LIST.
    
    IMPORTANT REMARKS.  (1) Notice that the argument to set-ruler-extenders
    is evaluated, but the argument to :RULER-EXTENDERS in XARGS is not
    evaluated.  (2) Do not put macro names in your list of ruler-extenders.
    For example, if you intend that + should not block the termination
    analysis, in analogy to cons in the example above, then the list of
    ruler-extenders should include binary-+, not +.  Of course, if you use
    :all then this is not an issue, but see the next remark.  (3) Also
    please note that by taking advantage of the ruler-extenders, you may be
    complicating the induction scheme stored for the function, whose
    computation takes similar advantage of the additional IF structure that
    you are specifying.
    
    Below we describe the notion of ruler-extenders in detail, as well as
    how to set its default using set-ruler-extenders.
    
    *Details*
    
    We begin by discussing how to set the ruler-extenders by using the macro
    set-ruler-extenders; below we will discuss the use of keyword
    :ruler-extenders in XARGS declare forms.
    
         Examples:
         (set-ruler-extenders :basic) ; return to default
         (set-ruler-extenders *basic-ruler-extenders*) ; same as immediately above
         (set-ruler-extenders :all) ; every governing IF test rules a recursive call
         (set-ruler-extenders :lambdas) ; LET does not block termination analysis
         (set-ruler-extenders (cons :lambdas *basic-ruler-extenders*))
                                        ; same as immediately above
         (set-ruler-extenders '(f g)) ; termination analysis goes past calls of f, g
    
         General Form:
         (set-ruler-extenders val)
    
    where val evaluates to one of :basic, :all, :lambdas, or a true list of
    symbols containing no keyword other than, optionally, :lambdas.
    
    When a recursive definition is submitted to ACL2 (in :logic mode), the
    recursion must be proved to terminate; see *note DEFUN::.  More
    precisely, ACL2 explores the IF structure of the body of the definition
    to accumulate the tests that "rule" any given recursive call.  The
    following example reviews how this works.  Suppose that f has already
    been defined.
    
           (defun g (x y)
             (declare (xargs :measure (+ (acl2-count x) (acl2-count y))))
             (if (consp x)
                 (g (cdr x) y)
               (if (consp y)
                   (f (g x (cdr y)))
                 (f (list x y)))))
    
    ACL2 makes the following response to this proposed definition.  Notice
    that the :measure proposed above must be proved to be an ACL2 ordinal --
    that is, to satisfy O-P -- and that the arguments to each recursive
    call must be smaller (in the sense of that measure and O<, which here
    reduces to the ordinary < relation) than the formals under the
    assumption of the ruling IF tests.  The first IMPLIES term below thus
    corresponds to the recursive call (g (cdr x) y), while the second
    corresponds to the recursive call (g x (cdr y)).
    
           For the admission of G we will use the relation O< (which is known
           to be well-founded on the domain recognized by O-P) and the measure
           (+ (ACL2-COUNT X) (ACL2-COUNT Y)).  The non-trivial part of the measure
           conjecture is
    
           Goal
           (AND (O-P (+ (ACL2-COUNT X) (ACL2-COUNT Y)))
                (IMPLIES (CONSP X)
                         (O< (+ (ACL2-COUNT (CDR X)) (ACL2-COUNT Y))
                             (+ (ACL2-COUNT X) (ACL2-COUNT Y))))
                (IMPLIES (AND (NOT (CONSP X)) (CONSP Y))
                         (O< (+ (ACL2-COUNT X) (ACL2-COUNT (CDR Y)))
                             (+ (ACL2-COUNT X) (ACL2-COUNT Y))))).
    
    Now consider the following alternate version of the above definition.
    
           (defun g (x y)
             (declare (xargs :measure (+ (acl2-count x) (acl2-count y))))
             (if (consp x)
                 (g (cdr x) y)
               (f (if (consp y)
                      (g x (cdr y))
                    (list x y)))))
    
    The first test, (consp x), still rules the first recursive call, (g
    (cdr x) y).  And the negation of that test, namely (not (consp x)),
    still rules the second recursive call (g x (cdr y)).  But the call of f
    blocks the top-down exploration of the IF structure of the body of g,
    so (consp y) does not rule that second recursive call, which (again) is
    (g x (cdr y)).  As a result, ACL2 fails to admit the above definition.
    
    Set-ruler-extenders is provided to overcome the sort of blocking
    described above.  Suppose for example that the following event is
    submitted:
    
           (set-ruler-extenders '(f))
    
    Then the alternate definition of g above is admissible, because the call
    of f no longer blocks the top-down exploration of the IF structure of
    the body of g: that is, (consp y) becomes a ruler of the recursive call
    (g x (cdr y)).  In this case, we say that f is a "ruler-extender".  The
    same result obtains if we first submit
    
           (set-ruler-extenders :all)
    
    as this removes all function calls as blockers of the top-down
    analysis.  In other words, with :all it is the case that for every
    recursive call, every test argument of a superior call of IF
    contributes a ruler of that recursive call.
    
    ACL2 handles LET (and LET*) expressions by translating them to LAMBDA
    expressions (see *note TERM::).  The next examples illustrates
    termination analysis involving such expressions.  First consider the
    following (admittedly inefficient) definition.
    
           (defun fact (n)
             (let ((k (if (natp n) n 0)))
               (if (equal k 0)
                   1
                 (* k (fact (+ -1 k))))))
    
    ACL2 translates the body of this definition to a LAMBDA application,
    essentially:
    
           ((lambda (k)
              (if (equal k 0)
                  1
                (* k (fact (+ -1 k)))))
            (if (natp n) n 0))
    
    As with the application of any function other than IF, the top-down
    termination analysis does not dive into arguments: the LAMBDA blocks the
    continuation of the analysis into its argument.  But here, the argument
    of the LAMBDA is (if (natp n) n 0), which has no recursive calls to
    consider anyhow.  What is more interesting: ACL2 does continue its
    termination analysis into the body of the LAMBDA, in an environment
    binding the LAMBDA formals to its actuals.  In this case, the
    termination analysis thus continues into the term
    
           (if (equal k 0)
               1
             (* k (fact (+ -1 k))))
    
    in the environment that binds k to the term (if (natp n) n 0).  Thus,
    the proof obligation is successfully discharged, as reported by ACL2:
    
           For the admission of FACT we will use the relation O< (which is known
           to be well-founded on the domain recognized by O-P) and the measure
           (ACL2-COUNT N).  The non-trivial part of the measure conjecture is
    
           Goal
           (IMPLIES (NOT (EQUAL (IF (NATP N) N 0) 0))
                    (O< (ACL2-COUNT (+ -1 (IF (NATP N) N 0)))
                        (ACL2-COUNT N))).
           .....
           Q.E.D.
    
           That completes the proof of the measure theorem for FACT.
    
    But now consider the following definition, in which the recursion takes
    place inside the argument of the LAMBDA rather than inside the LAMBDA
    body.
    
           (defun app (x y)
             (let ((result (if (endp x)
                               y
                             (cons (car x)
                                   (app (cdr x) y)))))
               (if (our-test result)
                   result
                 0)))
    
    Writing the body in LAMBDA notation:
    
           ((lambda (result)
              (if (our-test result)
                  result
                0))
            (if (endp x)
                y
              (cons (car x)
                    (app (cdr x) y))))
    
    By default, the LAMBDA call blocks the top-down termination analysis
    from proceeding into the term (if (endp x) ...).  To solve this, one can
    submit the event:
    
           (set-ruler-extenders :lambdas)
    
    The above definition of app is then admitted by ACL2, because the
    termination analysis is no longer blocked by the LAMBDA call.
    
    The example just above illustrates that the heuristically-chosen
    measure is suitably sensitive to the ruler-extenders.  Specifically:
    that measure is the application of acl2-count to the first formal
    parameter of the function that is tested along every branch of the
    relevant IF structure (as determined by the rulers) and occurs as a
    proper subterm at the same argument position in every recursive call.
    The heuristics for choosing the controller-alist for a definition rule
    are similarly sensitive to the ruler-extenders (see *note DEFINITION::).
    
    The remarks above for defun events are equally applicable when a
    definition sits inside a mutual-recursion event, except of course that
    in this case, a "recursive call" is a call of any function being
    defined by that mutual-recursion event.
    
    Rules of class :definition are sensitive to set-ruler-extenders in
    analogy to the case of defun events.
    
    This macro generates a call (table acl2-defaults-table :ruler-extenders
    val) and hence is local to any books and encapsulate events in which it
    occurs. See *note ACL2-DEFAULTS-TABLE::.  The current list of
    ruler-extenders may be obtained as
    
           (cdr (assoc-eq :ruler-extenders
                (table-alist 'acl2-defaults-table (w state))))
    
    or more conveniently, as:
    
           (default-ruler-extenders (w state))
    
    Note that evaluation of (set-ruler-extenders lst), where lst evaluates
    to a list, does not necessarily include the default ruler-extenders --
    i.e., those included for the argument, :basic -- which are the elements
    of the list constant *basic-ruler-extenders*, namely return-last and
    mv-list.  You may, of course, include these explicitly in your list
    argument.
    
    We conclude our discussion by noting that the set of ruler-extenders can
    affect the induction scheme that is stored with a recursive definition.
    The community book books/misc/misc2/ruler-extenders-tests.lisp explains
    how induction schemes are derived in this case.  Consider the following
    example.
    
           (defun tree-of-nils-p (x)
             (if (consp x)
                 (and (tree-of-nils-p (car x))
                      (tree-of-nils-p (cdr x)))
               (null x)))
    
    The above definition generates the following induction scheme.  Note
    that (and u v) expands to (if u v nil), which explains why the term
    (tree-of-nils-p (car x)) rules the recursive call (tree-of-nils-p (cdr
    x)), resulting in the hypothesis (tree-of-nils-p (car x)) in the final
    conjunct below.
    
           (AND (IMPLIES (NOT (CONSP X)) (:P X))
                (IMPLIES (AND (CONSP X)
                              (NOT (TREE-OF-NILS-P (CAR X)))
                              (:P (CAR X)))
                         (:P X))
                (IMPLIES (AND (CONSP X)
                              (TREE-OF-NILS-P (CAR X))
                              (:P (CAR X))
                              (:P (CDR X)))
                         (:P X)))
    
    Now consider the following variant of the above definition, in which a
    call of the function identity blocks the termination analysis.
    
           (defun tree-of-nils-p (x)
             (if (consp x)
                 (identity (and (tree-of-nils-p (car x))
                                (tree-of-nils-p (cdr x))))
               (null x)))
    
    This time the induction scheme is as follows, since only the top-level
    IF test contributes rulers to the termination analysis.
    
           (AND (IMPLIES (NOT (CONSP X)) (:P X))
                (IMPLIES (AND (CONSP X)
                              (:P (CAR X))
                              (:P (CDR X)))
                         (:P X)))
    
    But now suppose we first designate identity as a ruler-extender.
    
         (set-ruler-extenders '(identity))
    
    Then the induction scheme generated for the both of the above variants
    of tree-of-nils-p is the one shown for the first variant, which is
    reasonable because both definitions now produce essentially the same
    termination analysis.
    
    
    File: acl2-doc-emacs.info,  Node: SET-ABSSTOBJ-DEBUG,  Next: SET-BACKCHAIN-LIMIT,  Prev: RULER-EXTENDERS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-ABSSTOBJ-DEBUG    obtain debugging information upon atomicity violation for an abstract stobj
    
    This documentation topic assumes familiarity with abstract stobjs.  See
    *note DEFABSSTOBJ::.
    
    Below we explain what is meant by an error message such as the
    following.
    
         ACL2 Error in CHK-ABSSTOBJ-INVARIANTS:  Possible invariance violation
         for an abstract stobj!  See :DOC set-absstobj-debug, and PROCEED AT
         YOUR OWN RISK.
    
    The use of (set-absstobj-debug t) will make this error message more
    informative, as follows, at the cost of slower execution -- but in
    practice, the slowdown may be negligible (more on that below).
    
         ACL2 Error in CHK-ABSSTOBJ-INVARIANTS:  Possible invariance violation
         for an abstract stobj!  See :DOC set-absstobj-debug, and PROCEED AT
         YOUR OWN RISK.  Evaluation was aborted under a call of abstract stobj
         export UPDATE-FLD-NIL-BAD.
    
    You may be best off starting a new ACL2 session if you see one of the
    errors above.  But you can continue at your own risk.  With a trust tag
    (see *note DEFTTAG::), you can even fool ACL2 into thinking nothing is
    wrong, and perhaps you can fix up the abstract stobj so that indeed,
    nothing really is wrong.  See the community book
    books/misc/defabsstobj-example-4.lisp for how to do that.  That book
    also documents the :always keyword and a special value for the first
    argument, :RESET.
    
         Examples:
         (set-absstobj-debug t)                 ; obtain extra debug info, as above
         (set-absstobj-debug t :event-p t)      ; same as above
         (set-absstobj-debug t
                             :on-skip-proofs t) ; as above, but even in include-book
         (set-absstobj-debug t :event-p nil)    ; returns one value, not error triple
         (set-absstobj-debug nil)               ; avoid extra debug info (default)
    
         General Form:
         (set-absstobj-debug val
                             :event-p        event-p        ; default t
                             :always         always         ; default nil
                             :on-skip-proofs on-skip-proofs ; default nil
                             )
    
    where the keyword arguments are optional with defaults as indicated
    above, and all supplied arguments are evaluated except for
    on-skip-proofsp, which must be Boolean (if supplied).  Keyword
    arguments are discussed at the end of this topic.
    
    Recall (see *note DEFABSSTOBJ::) that for any exported function whose
    :EXEC function might (according to ACL2's heuristics) modify the
    concrete stobj non-atomically, one must specify :PROTECT t.  This
    results in extra code generated for the exported function, which
    provides a check that atomicity was not actually violated by a call of
    the exported function.  The extra code might slow down execution, but
    perhaps only negligibly in typical cases.  If you can tolerate a bit
    extra slow-down, then evaluate the form (set-absstobj-debug t).
    Subsequent such errors will provide additional information, as in the
    example displayed earlier in this documentation topic.
    
    Finally we document the keyword arguments, other than :ALWAYS, which is
    discussed in a book as mentioned above.  When the value of :EVENT-P is
    true, which it is by default, the call of set-absstobj-debug will expand
    to an event.  That event is a call of value-triple.  In that case,
    :ON-SKIP-PROOFS is passed to that call so that set-absstobj-debug has
    an effect even when proofs are being skipped, as during include-book.
    That behavior is the default; that is, :ON-SKIP-PROOFS is nil by
    default.  Also see *note VALUE-TRIPLE::.  The value of keyword
    :ON-SKIP-PROOFS must always be either t or nil, but other than that, it
    is ignored when EVENT-P is nil.
    
    
    File: acl2-doc-emacs.info,  Node: SET-BACKCHAIN-LIMIT,  Next: SET-BOGUS-DEFUN-HINTS-OK,  Prev: SET-ABSSTOBJ-DEBUG,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-BACKCHAIN-LIMIT    sets the backchain-limit used by the type-set and rewriting mechanisms
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
    This event sets the global backchain-limit used by the ACL2 type-set
    and rewriting mechanisms.  Its value may be a cons whose car and cdr
    are each either nil or a non-negative integer.  Its value x may also be
    nil or a non-negative integer, which is treated as a cons whose car and
    cdr are both x.
    
    The car is used to limit backchaining used by the ACL2 type-set
    mechanism, while the cdr is used to limit backchaining used by the
    rewriting mechanism.  See *note BACKCHAIN-LIMIT:: for details about how
    backchain-limits are used.  Rewrite backchain limits may also be
    installed at the level of hints; see *note HINTS:: for a discussion of
    :backchain-limit-rw.
    
         :set-backchain-limit nil  ; do not impose any additional limits
         :set-backchain-limit 0    ; allow only type-set reasoning for rewriting
                                   ; hypotheses
         :set-backchain-limit 500  ; allow backchaining to a depth of no more
                                   ; than 500 for rewriting hypotheses
         (set-backchain-limit 500) ; same as above
         :set-backchain-limit (500 500)
                                   ; same as above
         (set-backchain-limit '(500 500))
                                   ; same as above
         (set-backchain-limit '(3 500))
                                   ; allow type-set backchaining to a depth of no more
                                   ; than 3 and rewriter backchaining to a depth of no
                                   ; more than 500
    
    The default limit is (nil nil).
    
    
    File: acl2-doc-emacs.info,  Node: SET-BOGUS-DEFUN-HINTS-OK,  Next: SET-BOGUS-MUTUAL-RECURSION-OK,  Prev: SET-BACKCHAIN-LIMIT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-BOGUS-DEFUN-HINTS-OK    allow unnecessary "mutual recursion"
    
         General Forms:
         (set-bogus-defun-hints-ok t)
         (set-bogus-defun-hints-ok nil)
         (set-bogus-defun-hints-ok :warn)
    
    By default, ACL2 causes an error when the keyword :hints is supplied in
    an xargs declare form for a definition (see *note DEFUN::).  This
    behavior can be defeated with (set-bogus-defun-hints-ok t), or if you
    still want to see a warning in such cases, (set-bogus-defun-hints-ok
    :warn).
    
    
    File: acl2-doc-emacs.info,  Node: SET-BOGUS-MUTUAL-RECURSION-OK,  Next: SET-CASE-SPLIT-LIMITATIONS,  Prev: SET-BOGUS-DEFUN-HINTS-OK,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-BOGUS-MUTUAL-RECURSION-OK    allow unnecessary "mutual recursion"
    
         Examples:
         (set-bogus-mutual-recursion-ok t)
         (set-bogus-mutual-recursion-ok nil)
         (set-bogus-mutual-recursion-ok :warn)
    
    By default, ACL2 checks that when a "clique" of more than one function
    is defined simultaneously (using mutual-recursion or defuns), then
    every body calls at least one of the functions in the "clique."  Below,
    we refer to definitional events that fail this check as "bogus" mutual
    recursions.  The check is important because ACL2 does not store
    induction schemes for functions defined with other functions in a
    mutual-recursion or defuns event.  Thus, ACL2 may have difficulty
    proving theorems by induction that involve such functions.  Moreover,
    the check can call attention to bugs, since users generally intend that
    their mutual recursions are not bogus.
    
    Nevertheless, there are times when it is advantageous to allow bogus
    mutual recursions, for example when they are generated mechanically,
    even at the expense of losing stored induction schemes.  The first
    example above allows bogus mutual recursion.  The second example
    disallows bogus mutual recursion; this is the default.  The third
    example allows bogus mutual recursion, but prints an appropriate
    warning.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
         General Form:
         (set-bogus-mutual-recursion-ok flg)
    
    where flg is either t, nil, or :warn.
    
    
    File: acl2-doc-emacs.info,  Node: SET-CASE-SPLIT-LIMITATIONS,  Next: SET-CHECKPOINT-SUMMARY-LIMIT,  Prev: SET-BOGUS-MUTUAL-RECURSION-OK,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-CASE-SPLIT-LIMITATIONS    set the case-split-limitations
    
    
         Examples:
         (set-case-split-limitations '(500 100))
         (set-case-split-limitations 'nil)
         (set-case-split-limitations '(500 nil))
    
    The first of these prevents clausify from trying the
    subsumption/replacement (see below) loop if more than 500 clauses are
    involved.  It also discourages the clause simplifier from splitting
    into more than 100 cases at once.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
    See *note HINTS:: for discussion of a related hint,
    :case-split-limitations.  Also see *note SPLITTER:: for information
    about reports on rules that may be responsible for case splits.
    
         General Form:
         (set-case-split-limitations lst)
    
    where lst is either nil (denoting a list of two nils), or a list of
    length two, each element of which is either nil or a natural number.
    When nil is used as an element, it is treated as positive infinity.  The
    default setting is (500 100).
    
    The first number specifies the maximum number of clauses that may
    participate in the "subsumption/replacement" loop.  Because of the
    expensive nature of that loop (which compares every clause to every
    other in a way that is quadratic in the number of literals in the
    clauses), when the number of clauses exceeds about 1000, the system
    tends to "go into a black hole," printing nothing and not even doing
    many garbage collections (because the subsumption/replacement loop does
    not create new clauses so much as it just tries to delete old ones).
    The initial setting is lower than the threshold at which we see
    noticeably bad performance, so you probably will not see this behavior
    unless you have raised or disabled the limit.
    
    Why raise the subsumption/replacement limit?  The
    subsumption/replacement loop cleans up the set of subgoals produced by
    the simplifier.  For example, if one subgoal is
    
         (implies (and p q r) s)            [1]
    
    and another is
    
         (implies (and p (not q) r) s)      [2]
    
    then the subsumption/replacement loop would produce the single subgoal
    
         (implies (and p r) s)              [3]
    
    instead.  This cleanup process is simply skipped when the number of
    subgoals exceeds the subsumption/replacement limit.  But each subgoal
    must nonetheless be proved.  The proofs of [1] and [2] are likely to
    duplicate much work, which is only done once in proving [3].  So with a
    low limit, you may find the system quickly produces a set of subgoals
    but then takes a long time to prove that set.  With a higher limit, you
    may find the set of subgoals to be "cleaner" and faster to prove.
    
    Why lower the subsumption/replacement limit?  If you see the system go
    into a "black hole" of the sort described above (no output, and few
    garbage collections), it could due to the subsumption/replacement loop
    working on a large set of large subgoals.  You might temporarily lower
    the limit so that it begins to print the uncleaned set of subgoals.
    Perhaps by looking at the output you will realize that some function
    can be disabled so as to prevent the case explosion.  Then raise or
    disable the limit again!
    
    The second number in the case-split-limitations specifies how many case
    splits the simplifier will allow before it begins to shut down case
    splitting.  In normal operation, when a literal rewrites to a nest of
    IFs, the system simplifies all subsequent literals in all the contexts
    generated by walking through the nest in all possible ways.  This can
    also cause the system to "go into a black hole" printing nothing except
    garbage collection messages.  This "black hole" behavior is different
    from than mentioned above because space is typically being consumed at
    a prodigious rate, since the system is rewriting the literals over and
    over in many different contexts.
    
    As the simplifier sweeps across the clause, it keeps track of the
    number of cases that have been generated.  When that number exceeds the
    second number in case-split-limitations, the simplifier stops rewriting
    literals.  The remaining, unrewritten, literals are copied over into
    the output clauses.  IFs in those literals are split out, but the
    literals themselves are not rewritten.  Each output clause is then
    attacked again, by subsequent simplification, and eventually the
    unrewritten literals in the tail of the clause will be rewritten
    because the earlier literals will stabilize and stop producing case
    splits.
    
    The default setting of 100 is fairly low.  We have seen successful
    proofs in which thousands of subgoals were created by a simplification.
    By setting the second number to small values, you can force the system
    to case split slowly.  For example, a setting of 5 will cause it to
    generate "about 5" subgoals per simplification.
    
    You can read about how the simplifier works in the book Computer-Aided
    Reasoning: An Approach (Kaufmann, Manolios, Moore); also see *note
    INTRODUCTION-TO-THE-THEOREM-PROVER:: for a detailed tutorial on using
    the ACL2 prover.  If you think about it, you will see that with a low
    case limit, the initial literals of a goal are repeatedly simplified,
    because each time a subgoal is simplified we start at the left-most
    subterm.  So when case splitting prevents the later subterms from being
    fully split out, we revisit the earlier terms before getting to the
    later ones.  This can be good.  Perhaps it takes several rounds of
    rewriting before the earlier terms are in normal form and then the
    later terms rewrite quickly.  But it could happen that the earlier
    terms are expensive to rewrite and do not change, making the strategy
    of delayed case splits less efficient.  It is up to you.
    
    Sometimes the simplifier produces more clauses than you might expect,
    even with case-split-limitations in effect.  As noted above, once the
    limit has been exceeded, the simplifier does not rewrite subsequent
    literals.  But IFs in those literals are split out nonetheless.
    Furthermore, the enforcement of the limit is - as described above - all
    or nothing: if the limit allows us to rewrite a literal then we rewrite
    the literal fully, without regard for limitations, and get as many
    cases as "naturally" are produced.  It quite often happens that a
    single literal, when expanded, may grossly exceed the specified limits.
    
    If the second "number" is nil and the simplifier is going to produce
    more than 1000 clauses, a "helpful little message" to this effect is
    printed out.  This output is printed to the system's "comment window"
    not the standard proofs output.
    
    
    File: acl2-doc-emacs.info,  Node: SET-CHECKPOINT-SUMMARY-LIMIT,  Next: SET-COMPILE-FNS,  Prev: SET-CASE-SPLIT-LIMITATIONS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-CHECKPOINT-SUMMARY-LIMIT    control printing of key checkpoints upon a proof's failure
    
    See *note SET-GAG-MODE:: for a discussion of key checkpoints.
    
         Examples:
    
         ; (Default) When a proof fails, print all key checkpoints before
         ; induction but at most 3 key checkpoints after induction:
         (set-checkpoint-summary-limit (nil . 3))
    
         ; When a proof fails, print at most 3 key checkpoints before
         ; induction but all 3 key checkpoints after induction:
         (set-checkpoint-summary-limit (3 . nil))
    
         ; When a proof fails, print at most 3 key checkpoints before
         ; induction and at most 5 key checkpoints after induction:
         (set-checkpoint-summary-limit (3 . 5))
    
         ; When a proof fails, print at most 3 key checkpoints before
         ; induction and at most 3 key checkpoints after induction:
         (set-checkpoint-summary-limit (3 . 3))
         ; or equivalently,
         (set-checkpoint-summary-limit 3)
    
         ; When a proof fails, print all key checkpoints:
         (set-checkpoint-summary-limit (nil . nil))
         ; or equivalently,
         (set-checkpoint-summary-limit nil)
    
         ; When a proof fails, print no information at all about key checkpoints:
         (set-checkpoint-summary-limit t)
    
         General Forms:
         (set-checkpoint-summary-limit (n1 . n2))
         (set-checkpoint-summary-limit n)
         (set-checkpoint-summary-limit t)
    
    where each of n1 and n2 is a natural number or nil.  For the second
    form, n can be a natural number or nil and is treated as (n . n).  The
    value t inhibits all printing of checkpoint summary information.  The
    values n1 and n2 determine printing of key checkpoints generated before
    the first induction and generated after the first induction,
    respectively, where at most n1 or n2 (respectively) such key
    checkpoints are printed unless the value is nil, in which case there is
    no limitation.
    
    The argument x for set-checkpoint-summary-limit, as described above,
    may be quoted, i.e. supplied as 'x or (quote x).  Thus, you may use the
    keyword form (see *note KEYWORD-COMMANDS::) if you prefer, for example:
    
         :set-checkpoint-summary-limit (nil . 3)
    
    
    File: acl2-doc-emacs.info,  Node: SET-COMPILE-FNS,  Next: SET-COMPILER-ENABLED,  Prev: SET-CHECKPOINT-SUMMARY-LIMIT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-COMPILE-FNS    have each function compiled as you go along.
    
         Example Forms:
         (set-compile-fns t)    ; new functions compiled after DEFUN
         (set-compile-fns nil)  ; new functions not compiled after DEFUN
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
    Also see *note COMP::, because it may be more efficient in some Common
    Lisps to compile many functions at once rather than to compile each one
    as you go along.
    
         General Form:
         (set-compile-fns term)
    
    where term is a variable-free term that evaluates to t or nil.  This
    macro is equivalent to
    
         (table acl2-defaults-table :compile-fns term)
    
    and hence is local to any books and encapsulate events in which it
    occurs; see *note ACL2-DEFAULTS-TABLE::.  However, unlike the above
    simple call of the table event function (see *note TABLE::), no output
    results from a set-compile-fns event.
    
    Set-compile-fns may be thought of as an event that merely sets a flag
    to t or nil.  The flag's effect is felt when functions are defined, as
    with defun.  If the flag is t, functions are automatically compiled
    after they are defined, as are their executable counterparts (see *note
    EXECUTABLE-COUNTERPART::).  Otherwise, functions are not automatically
    compiled.  Exception: The flag has no effect when explicit compilation
    is suppressed; see *note COMPILATION::.
    
    Because set-compile-fns is an event, the old value of the flag is
    restored when a set-compile-fns event is undone.
    
    Even when :set-compile-fns t has been executed, functions are not
    individually compiled when processing an include-book event.  If you
    wish to include a book of compiled functions, we suggest that you first
    certify it with the compilation flag set (see *note CERTIFY-BOOK::) or
    else compile the book by supplying the appropriate load-compiled-file
    argument to include-book.  More generally, compilation via
    set-compile-fns is suppressed when the state global variable
    ld-skip-proofsp has value 'include-book.
    
    
    File: acl2-doc-emacs.info,  Node: SET-COMPILER-ENABLED,  Next: SET-DEBUGGER-ENABLE,  Prev: SET-COMPILE-FNS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-COMPILER-ENABLED    See *note COMPILATION::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-DEBUGGER-ENABLE,  Next: SET-DEFAULT-BACKCHAIN-LIMIT,  Prev: SET-COMPILER-ENABLED,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-DEBUGGER-ENABLE    control whether Lisp errors and breaks invoke the Lisp debugger
    
         Forms (see below for explanations and GCL exceptions):
    
         (set-debugger-enable t)         ; enable breaks into the raw Lisp debugger
         (set-debugger-enable :break)    ; same as above
         :set-debugger-enable t          ; same as above
         (set-debugger-enable :break-bt) ; as above, but print a backtrace first
         (set-debugger-enable :bt-break) ; as above, but print a backtrace first
         (set-debugger-enable :bt)       ; print a backtrace but do not enter debugger
         (set-debugger-enable :never)    ; disable all breaks into the debugger
         (set-debugger-enable nil)       ; disable debugger except when calling break$
    
    _Introduction._  Suppose we define foo in :program mode to take the car
    of its argument.  This can cause a raw Lisp error.  ACL2 will then
    return control to its top-level loop unless you enable the Lisp
    debugger, as shown below (except: the error message can take quite a
    different form in non-ANSI GCL).
    
           ACL2 !>(defun foo (x) (declare (xargs :mode :program)) (car x))
    
           Summary
           Form:  ( DEFUN FOO ...)
           Rules: NIL
           Warnings:  None
           Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
            FOO
           ACL2 !>(foo 3)
           ***********************************************
           ************ ABORTING from raw Lisp ***********
           Error:  Attempt to take the car of 3 which is not listp.
           ***********************************************
    
           If you didn't cause an explicit interrupt (Control-C),
           then the root cause may be call of a :program mode
           function that has the wrong guard specified, or even no
           guard specified (i.e., an implicit guard of t).
           See :DOC guards.
    
           To enable breaks into the debugger (also see :DOC acl2-customization):
           (SET-DEBUGGER-ENABLE T)
           ACL2 !>(SET-DEBUGGER-ENABLE T)
           
           ACL2 !>(foo 3)
           Error: Attempt to take the car of 3 which is not listp.
             [condition type: TYPE-ERROR]
    
           Restart actions (select using :continue):
            0: Abort entirely from this (lisp) process.
           [Current process: Initial Lisp Listener]
           [1] ACL2(1): [RAW LISP]
    
    _Details._  ACL2 usage is intended to take place inside the ACL2
    read-eval-print loop (see *note LP::).  Indeed, in most Lisp
    implementations ACL2 comes up inside that loop, as evidenced by the
    prompt:
    
         ACL2 !>
    
    However, one can occasionally hit a raw Lisp error.  Here is the above
    example again, this time for a GCL implementation, which unfortunately
    gives a slightly less aesthetic report.
    
           ACL2 !>(foo 3)
    
           Error: 3 is not of type LIST.
           Fast links are on: do (si::use-fast-links nil) for debugging
           Error signalled by CAR.
           Backtrace: funcall > system:top-level > lisp:lambda-closure > lp > acl2_*1*_acl2::foo > foo > car > system:universal-error-handler > system::break-level-for-acl2 > let* > UNLESS
           ACL2 !>
    
    Here, the user has defined foo in :program mode, with an implicit guard
    of t.  The ACL2 evaluator therefore called the Lisp evaluator, which
    expected nil or a consp argument to car.
    
    By default, ACL2 will return to its top-level loop (at the same level of
    LD) when there is a raw Lisp error, as though a call of ER with flag
    HARD has been evaluated.  If instead you want to enter the raw Lisp
    debugger in such cases, evaluate the following form.
    
         (set-debugger-enable t)
    
    You can subsequently return to the default behavior with:
    
         (set-debugger-enable nil)
    
    Either way, you can enter the Lisp debugger from within the ACL2 loop by
    evaluating (break$).  If you want break$ disabled, then evaluate the
    following, which disables entry to the Lisp debugger not only for Lisp
    errors but also when executing (break$).
    
         (set-debugger-enable :never)
    
    The discussion above also applies to interrupts (from Control-C) in
    some, but not all, host Common Lisps.
    
    It remains to discuss options :break, :bt, :break-bt, and :bt-break.
    Option :break is synonymous with option t, while option :bt prints a
    backtrace.  Options :break-bt and :bt-break are equivalent, and each
    has the combined effect of :bt and :break: a backtrace is printed and
    then the debugger is entered.
    
    Note that set-debugger-enable applies not only to raw Lisp errors, but
    also to ACL2 errors: those affected by break-on-error.  However, for
    ACL2 errors, entering the debugger is controlled only by break-on-error,
    not by set-debugger-enable.  For ACL2 errors encountered after
    evaluating (break-on-error t), the set-debugger-enable values of :bt,
    :break-bt, and :bt-break will result in the same effect: in many host
    LIsps, this effect will be to cause a backtrace to be printed.
    
    Remark for Common Lisp hackers (except for the case that the host Lisp
    is non-ANSI GCL).  You can customize the form of the backtrace printed
    by entering raw Lisp (with :q) and then redefining function
    print-call-history, whose definition immediately precedes that of
    break-on-error in ACL2 source file ld.lisp.  Of course, all bets are
    off when defining any function in raw Lisp, but as a practical matter
    you are probably fine as long as your books are ultimately certified
    with an unmodified copy of ACL2.  If you come up with improvements to
    print-call-history, please pass them along to the ACL2 implementors.
    
    
    File: acl2-doc-emacs.info,  Node: SET-DEFAULT-BACKCHAIN-LIMIT,  Next: SET-DEFAULT-HINTS,  Prev: SET-DEBUGGER-ENABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-DEFAULT-BACKCHAIN-LIMIT    sets the default backchain-limit used when admitting a rule
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
    This event sets the default backchain-limit used when a new rewrite,
    linear, meta, or type-prescription rule is admitted.  Its value may be
    a two-element list whose elements are each either nil or a non-negative
    integer.  Its value x may also be nil or a non-negative integer, which
    is treated as the two element list (x x).
    
    The first element of the list is used to limit backchaining for a rule
    of class type-prescription while the second element is used to limit
    backchaining for the other three classes of rules mentioned above.  See
    *note BACKCHAIN-LIMIT:: for details about how backchain-limits are
    used.  The examples below assume that a new rule doesn't itself specify
    a value for :backchain-limit-lst.
    
         :set-default-backchain-limit nil  ; do not impose backchain limits for the
                                           ; rule
         :set-default-backchain-limit 0    ; allow only type-set reasoning for
                                           ; relieving a new rule's hypotheses
         :set-default-backchain-limit 500  ; allow backchaining through a new rewrite,
                                           ; linear, or meta rule's hypotheses to a
                                           ; depth of no more than 500
         (set-default-backchain-limit 500) ; same as above
         :set-default-backchain-limit (nil 500)
                                           ; same as above
         (set-default-backchain-limit '(nil 500))
                                           ; same as above
         (set-default-backchain-limit '(3 500))
                                           ; for a new :type-prescription rule, allow
                                           ; type-set backchaining to a depth
                                           ; of no more than 3; for a new
                                           ; rule of class :rewrite, :linear,
                                           ; or :meta, allow backchaining to
                                           ; a depth of no more than 50
         (set-default-backchain-limit '(nil 500))
                                           ; do not limit backchaining for a
                                           ; new :type-prescription rule; for
                                           ; a new rule of class :rewrite,
                                           ; :linear, or :meta, allow
                                           ; backchaining to a depth of no
                                           ; more than 50
    
    The initial default backchain-limit is nil.
    
    
    File: acl2-doc-emacs.info,  Node: SET-DEFAULT-HINTS,  Next: SET-DEFAULT-HINTS!,  Prev: SET-DEFAULT-BACKCHAIN-LIMIT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-DEFAULT-HINTS    set the default hints
    
    
         Examples:
         (set-default-hints '((computed-hint-1 clause)
                              (computed-hint-2 clause
                                               stable-under-simplificationp)))
         (set-default-hints nil)
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It is
    local to the book or encapsulate form in which it occurs; see *note
    SET-DEFAULT-HINTS!:: for a corresponding non-local event.
    
         General Form:
         (set-default-hints lst)
    
    where lst is a list.  Generally speaking, the elements of lst should be
    suitable for use as computed-hints.
    
    Whenever a defthm or thm command is executed, the default hints are
    appended to the right of any explicitly provided :hints in the command.
    The same applies to defuns as well (:hints, :guard-hints, and (for
    ACL2(r)) :std-hints).  The hints are then translated and processed just
    as though they had been explicitly included.
    
    Technically, we do not put restrictions on lst, beyond that it is a
    true list.  It would be legal to execute
    
         (set-default-hints '(("Goal" :use lemma23)))
    
    with the effect that the given hint is added to subsequent hints
    supplied explicitly.  An explicit "Goal" hint would, however, take
    priority, as suggested by the mention above of "appended to the right."
    
    Note that set-default-hints sets the default hints as specified.  To
    add to or remove from the current default, see *note
    ADD-DEFAULT-HINTS:: and see *note REMOVE-DEFAULT-HINTS::.  To see the
    current default hints, see *note DEFAULT-HINTS::.
    
    Finally, note that the effects of set-default-hints, add-default-hints,
    and remove-default-hints are local to the book in which they appear.
    Thus, users who include a book with such forms will not have their
    default hints affected by such forms.  In order to export the effect of
    setting the default hints, use set-default-hints!, add-default-hints!,
    or remove-default-hints!.
    
    For a related feature, which however is only for advanced system
    builders, see *note OVERRIDE-HINTS::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-DEFAULT-HINTS!,  Next: SET-DEFERRED-TTAG-NOTES,  Prev: SET-DEFAULT-HINTS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-DEFAULT-HINTS!    set the default hints non-locally
    
    Please see *note SET-DEFAULT-HINTS::, which is the same as
    set-default-hints!  except that the latter is not local to the
    encapsulate or the book in which it occurs.  Probably set-default-hints
    is to be preferred unless you have a good reason for wanting to export
    the effect of this event outside the enclosing encapsulate or book.
    
    
    File: acl2-doc-emacs.info,  Node: SET-DEFERRED-TTAG-NOTES,  Next: SET-ENFORCE-REDUNDANCY,  Prev: SET-DEFAULT-HINTS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-DEFERRED-TTAG-NOTES    modify the verbosity of TTAG NOTE printing
    
         General Form:
         (set-deferred-ttag-notes val state)
    
    where val is t or nil.
    
    See *note DEFTTAG:: for a discussion of trust tags (ttags).  By
    default, a "TTAG NOTE" is printed in order to indicate reliance on a
    ttag.  When many such notes are printed, it may be desirable to avoid
    seeing them all.  Upon evaluation of the form
    
         (set-deferred-ttag-notes t state)
    
    ACL2 will enter a deferred mode for the printing of ttag notes.  In this
    mode, only the first ttag note is printed for each top-level command,
    and the remainder are summarized before the next top-level prompt (if
    any) is printed, hence before the next command is evaluated.  That
    summary is merely a list of ttags, but the summary explains that the
    full ttag notes may be printed with the command (show-ttag-notes).
    
    Note that (show-ttag-notes) spares you duplicate ttag notes.  If you
    want to see every ttag note as it would normally appear, for maximum
    security, do not evaluate the command (set-deferred-ttag-notes t
    state).  You can undo the effect of this command, returning to an
    immediate mode for printing ttag notes, by evaluating:
    
         (set-deferred-ttag-notes nil state)
    
    
    File: acl2-doc-emacs.info,  Node: SET-ENFORCE-REDUNDANCY,  Next: SET-GAG-MODE,  Prev: SET-DEFERRED-TTAG-NOTES,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-ENFORCE-REDUNDANCY    require most events to be redundant
    
         General Forms:
         (set-enforce-redundancy nil)   ; do not require redundancy (default)
         (set-enforce-redundancy t)     ; most events (see below) must be redundant
         (set-enforce-redundancy :warn) ; warn for most non-redundant events
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
         General Form:
         (set-enforce-redundancy flag)
    
    where flag is nil, t, or :warn, as indicated above.  This macro is
    essentially equivalent to
    
         (table acl2-defaults-table :enforce-redundancy flag)
    
    and hence is local to any books and encapsulate events in which it
    occurs; see *note ACL2-DEFAULTS-TABLE::.  However, unlike the above
    simple call of the table event function (see *note TABLE::), no output
    results from a set-enforce-redundancy event.
    
    Set-enforce-redundancy may be thought of as an event that merely sets a
    flag as indicated above, which determines whether most events, including
    defun and defthm events, are allowed to be redundant; see *note
    REDUNDANT-EVENTS::.  The exceptions are deflabel, defpkg, encapsulate,
    include-book, push-untouchable, remove-untouchable, set-body, and table
    events.  Any other type of non-redundant event will cause an error if
    flag is t and a warning if flag is nil, _except_ in the course of
    carrying out an include-book form.
    
    Note that because table events that set the acl2-defaults-table are
    implicitly local, set-enforce-redundancy events are ignored when
    including books.  However, the presence of the event
    (set-enforce-redundancy t) in a book guarantees that its subsequent
    definitions and theorems are redundant.  This can be a useful property
    to maintain in library development, as we now describe.
    
    An example of the use of this form can be found in the community books
    under directory books/rtl/rel4/.  The intention in that directory has
    been to put all the gory details in subdirectories support/ and
    arithmetic/, so that the books in subdirectory lib/ contain only the
    "exported" definitions and theorems.  This approach is useful for human
    readability.  Moreover, suppose we want to prove new theorems in lib/.
    Typically we wish to prove the new theorems using the existing books in
    lib/; however, our methodology demands that the proofs go into books in
    support/.  If every theorem in lib/ is redundant, then we can _develop_
    the proofs in lib/ but then when we are done, _move_ each book with
    such proofs into support/ as follows.  In any such book, we first
    replace include-book forms referring to books in lib/ by include-book
    forms referring to corresponding books in support/ and/or arithmetic/.
    Then, we add suitable in-theory events to get us back into the original
    lib/ proof environment.
    
    The default behavior of the system is as though the :enforce-redundancy
    value is nil.  The current behavior can be ascertained by evaluating the
    following form.
    
         (cdr (assoc-eq :enforce-redundancy (table-alist 'acl2-defaults-table wrld)))
    
    
    File: acl2-doc-emacs.info,  Node: SET-GAG-MODE,  Next: SET-GUARD-CHECKING,  Prev: SET-ENFORCE-REDUNDANCY,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-GAG-MODE    modify the nature of proof output
    
         Examples:
    
         :set-gag-mode t      ; enable gag-mode, suppressing most proof commentary
         (set-gag-mode t)     ; same as above
         :set-gag-mode :goals ; same as above, but print names of goals when produced
         :set-gag-mode nil    ; disable gag-mode
    
         General Forms:
         (set-gag-mode val)
         :set-gag-mode val
    
    where val is one of t, nil, or :goals.
    
    The basic idea of gag-mode is to avoid much of the verbose output from
    the theorem prover, leaving only output that is expected to be helpful.
    You are strongly encouraged to put the form
    
         (set-gag-mode t) ; or, (set-gag-mode :goals)
    
    in your ACL2 customization file; see *note ACL2-CUSTOMIZATION::.  The
    default value is :goals.
    
    The basic idea of gag-mode is to focus attention on so-called "key
    checkpoints".  By default, a checkpoint is a goal that cannot be
    simplified.  (Below we discuss how to change this default.)  A key
    checkpoint is a checkpoint that is not descended from another
    checkpoint.  (Technical point: "Descended" signifies that both goals
    are at the top level in the same forcing round, or are in the same
    proof by induction.)  Successful ACL2 users generally focus their
    attention on key checkpoints; for a discussion of how to use ACL2
    prover output in an effective manner, see *note THE-METHOD::, and see
    *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed
    tutorial.  In gag-mode, a key checkpoint is only displayed when ACL2 is
    unable to make any further progress on that goal or some descendent of
    it, other than with a proof by induction.
    
    Evaluation of set-gag-mode t enters gag-mode, so that only key
    checkpoints are printed.  Evaluation of set-gag-mode :goals also enters
    gag-mode, but will additionally cause the name of a goal to be printed
    as soon as it is generated (by invoking set-print-clause-ids).  The
    :goals setting is useful for cases in which the prover spends very
    little of its time generating goals to be proved by induction, yet you
    want to see that it is making progress.  For finer-grained feedback
    about the simplifier's activity, see *note DMR::.
    
    The current value of gag-mode is returned by a macro of the same name:
    
         (gag-mode) ; evaluates to t, nil, or :goals
    
    An alternative to gag-mode is to use proof-trees; see *note
    PROOF-TREE::.  With proof-trees it is not so important to avoid
    excessive prover output, since the proof-tree display provides
    structure that makes it easy to monitor proof attempts and navigate
    output for a proof that has failed or seems to be failing.  Still,
    output can take time to print, so you may get better performance with
    gag-mode.
    
    The intention of gag-mode is to show you only the parts of a proof
    attempt that are relevant for debugging a failure; additional output is
    generally more likely to be distracting than truly helpful.  But on
    occasion you may want to see the full proof output after an attempt
    made with gag-mode.  If so, then see *note PSO:: and see *note PSO!::.
    Since set-gag-mode takes responsibility for the saving of output,
    related utility set-saved-output is disabled when gag-mode is active.
    Also note that calling set-gag-mode erases the currently saved output,
    if any.
    
    You may notice that gag-mode tends to print relatively little
    information about goals pushed for proof by sub-induction -- i.e., a
    proof of *i.j, *i.j.k, etc.  The principle here is that sub-inductions
    that do not succeed should generally be avoided, not analyzed for ways
    to make them succeed.  Instead, the key checkpoint that generated the
    goal pushed for this induction is more appropriate to analyze.  In
    general, the "higher level" the checkpoint, the more worthy it is of
    attention.  Thus, we suggest that look at the top-level checkpoints
    before looking at those labeled "Key checkpoints under a top-level
    induction".
    
    We conclude with remarks for advanced users.
    
    The notion of "checkpoint" can be modified by the user.  The default, as
    discussed above, is for a checkpoint to be a goal that cannot be
    simplified.  Put differently, a checkpoint is acted on by one of the
    processes in the value of the form (@ checkpoint-processors); see *note
    @: atsign.  Any or all of the symbols eliminate-destructors-clause,
    fertilize-clause, generalize-clause, or eliminate-irrelevance-clause
    can be removed from this value in order that invocation of the
    corresponding proof process does not cause its input goal to be labeled
    a checkpoint.  For example, if you do not want destructor elimination
    to be treated differently from simplification for purposes of labeling
    checkpoints, you can evaluate the following form (see *note ASSIGN::):
    
         (assign checkpoint-processors
                 (remove 'eliminate-destructors-clause
                         (@ checkpoint-processors)))
    
    Note that the value of (@ checkpoint-processors) also affects the proof
    tree display; see *note PROOF-TREE-DETAILS::.  End of Remark.)
    
    See *note SET-EVISC-TUPLE::, in particular the discussion there of
    :GAG-MODE, for how to influence slightly just what is printed in
    gag-mode.
    
    
    File: acl2-doc-emacs.info,  Node: SET-GUARD-CHECKING,  Next: SET-IGNORE-DOC-STRING-ERROR,  Prev: SET-GAG-MODE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-GUARD-CHECKING    control checking guards during execution of top-level forms
    
    Detailed comments about the arguments of this function may be found
    elsewhere: see *note GUARD-EVALUATION-TABLE::.  Here we provide an
    introduction to the use of set-guard-checking.
    
    New users are encouraged to execute one of the following forms in order
    to avoid evaluation errors due to guards:
    
         (set-guard-checking :none)
         (set-guard-checking nil)
    
    The former avoids all guard-checking on user-defined functions and
    should generally work fine for new users, the only drawback being
    efficiency loss on compute-intensive problems.  All settings other than
    :none check guards, but a value of nil allows evaluation to continue in
    the logic when guards fail (avoiding the raw Lisp definition in that
    case).
    
    You may put one of the above forms in the "acl2-customization.lsp" file
    in your current directory (see *note CBD::) or your home directory; see
    *note ACL2-CUSTOMIZATION::.
    
    Note that guards are not part of the ACL2 logic, and hence new users can
    completely ignore the notion of guard (and the rest of this
    documentation section after this paragraph!).  For example, (car 3) and
    nil can be proved equal in the ACL2 logic, as follows, even though the
    guard on car requires its first argument to be a cons pair or nil.
    
         (thm (equal (car 3) nil))
    
    Moreover, unless your functions or top-level forms call built-in ACL2
    functions that are defined in :program mode, the following property
    will hold.
    
         Evaluation of (set-guard-checking :none) will allow evaluation of
         forms such as (car 3) to take place without error in the top level
         loop, not only when proving theorems.
    
    
    If you feel bold, then you may wish to read the rest of this
    documentation topic; also see *note GUARD::.
    
    See *note GUARD-EVALUATION-TABLE:: for a succinct table, with
    associated discussion, that covers in detail the material presented in
    the rest of the present topic.
    
    The top-level ACL2 loop has a variable which controls which sense of
    execution is provided.  To turn "guard checking on," by which we mean
    that guards are checked at runtime, execute the top-level form
    :set-guard-checking t.  To allow guard violations, do
    :set-guard-checking nil, or do :set-guard-checking :none to turn off
    all guard-checking, so that raw Lisp definitions of user-defined
    functions are avoided unless their guard is t. The status of
    guard-checking is reflected in the prompt.
    
         ACL2 !>
    
    means guard checking is on and
    
         ACL2 >
    
    means guard checking is off.  The exclamation mark can be thought of as
    "barring" certain computations.  The absence of the mark suggests the
    absence of error messages or unbarred access to the logical axioms.
    Thus, for example
    
         ACL2 !>(car 'abc)
    
    will signal an error, while
    
         ACL2 >(car 'abc)
    
    will return nil.
    
    We will return at the end of this documentation topic to discuss two
    other values, :all and :nowarn, for :set-guard-checking.  We also note
    that evaluation of built-in :program mode functions always takes place
    in raw Lisp.
    
    Whether guards are checked during evaluation is independent of the
    default-defun-mode.  We note this simply because it is easy to confuse
    ":program mode" with "evaluation in Common Lisp" and thus with "guard
    checking on;" and it is easy to confuse ":logic mode" with "evaluation
    in the logic" and with "guard checking off."  But the
    default-defun-mode determines whether newly submitted definitions
    introduce programs or add logical axioms.  That mode is independent of
    whether evaluation checks guards or not.  You can operate in :logic
    mode with runtime guard checking on or off.  Analogously, you can
    operate in :program mode with runtime guard checking on or off.
    
    For further discussion on evaluation and guards see *note
    GUARDS-AND-EVALUATION::, in particular the exception for safe-mode in
    the "Aside" there.  See *note GUARD:: for a general discussion of
    guards.
    
    Now we fulfill our promise above to discuss two other values for
    :set-guard-checking:
    
         :set-guard-checking :nowarn
         :set-guard-checking :all
    
    The meaning of these values is perhaps best described by the following
    example provided by David Rager.
    
         ACL2 !>(defun my-test (expr)
                  (declare (xargs :guard (true-listp expr)
                                  :verify-guards nil))
                  (if (atom expr)
                      expr
                    (cons (my-test (car expr))
                          (my-test (cdr expr)))))
    
         The admission of MY-TEST is trivial, using the relation O< (which is
         known to be well-founded on the domain recognized by O-P) and the measure
         (ACL2-COUNT EXPR).  We could deduce no constraints on the type of MY-
         TEST.  However, in normalizing the definition we used primitive type
         reasoning.
    
         Summary
         Form:  ( DEFUN MY-TEST ...)
         Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
         Warnings:  None
         Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
          MY-TEST
         ACL2 !>(my-test '(a b c))
    
         ACL2 Warning [Guards] in TOP-LEVEL:  Guard-checking will be inhibited
         on recursive calls of the executable counterpart (i.e., in the ACL2
         logic) of MY-TEST.  To check guards on all recursive calls:
           (set-guard-checking :all)
         To leave behavior unchanged except for inhibiting this message:
           (set-guard-checking :nowarn)
    
         (A B C)
         ACL2 !>
    
    If you think about evaluation of (my-test '(a b c)), you will see that
    it leads to the recursive call (my-test 'a), which one might expect to
    cause a guard violation since the symbol a is not a true-listp.
    However, as the warning above explains, we do not by default check
    guards on recursive calls.  The reason is efficiency -- imagine a
    simple definition with a guard that is slow to evaluate.  The values
    :nowarn and :all for :set-guard-checking have been introduced as ways
    of dealing with the above warning.  The value :nowarn simply turns off
    the warning above.  The value :all causes all guards to be checked,
    even on recursive calls and even on all calls of non-built-in :program
    mode functions -- unless, of course, a call is made of a function whose
    guard has been verified (see *note VERIFY-GUARDS::), where the
    arguments satisfy the guard, in which case the corresponding call is
    made in raw Lisp without subsidiary guard-checking.  We still say that
    "guard-checking is on" after :set-guard-checking is invoked with values
    t, :nowarn, and :all, otherwise (after value nil) we say
    "guard-checking is off.
    
    For technical reasons, :all does not have its advertised effect in the
    case of built-in :program-mode functions.  If you are interested in
    this technical detail, see the comment "In the boot-strap world..." in
    source function oneify-cltl-code.
    
    We conclude with a remark about the use of :set-guard-checking for
    experimenting with ACL2 as a logic or as a programming language.  If one
    views ACL2 as a logic, one may wish to use :set-guard-checking :none,
    while if instead one views ACL2 as a functional programming language,
    one may wish to use :set-guard-checking :all.  The following transcript
    illustrates this distinction by way of example.  Specifically, (car 3)
    is equal to nil in the ACL2 logic, but may be viewed as a programming
    error.  The default of :set-guard-checking t is problematic for learning
    ACL2 using :program mode functions, since one can get raw Lisp errors.
    In the example below, the raw Lisp error occurs because foo implicitly
    has a guard of t, hence (foo 3) is evaluated in raw Lisp, which leads
    to a raw Lisp call of c[(car 3)].
    
         ACL2 !>(defun foo (x)
                  (declare (xargs :mode :program))
                  (car x))
    
         Summary
         Form:  ( DEFUN FOO ...)
         Rules: NIL
         Warnings:  None
         Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
          FOO
         ACL2 !>(foo 3)
         Error: Attempt to take the car of 3 which is not listp.
           [condition type: TYPE-ERROR]
    
         Restart actions (select using :continue):
          0: Abort entirely from this (lisp) process.
         [Current process: Initial Lisp Listener]
         [1] ACL2(1): [RAW LISP] :pop
         ACL2 !>:set-guard-checking :none
    
         Turning off guard checking entirely.  To allow execution in raw Lisp
         for functions with guards other than T, while continuing to mask guard
         violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
         ACL2 >(foo 3)
         NIL
         ACL2 >:set-guard-checking :all
    
         Turning guard checking on, value :ALL.
    
         ACL2 !>(foo 3)
    
    
         ACL2 Error in TOP-LEVEL:  The guard for the function symbol CAR, which
         is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
         call (CAR 3).  See :DOC trace for a useful debugging utility.  See :DOC
         set-guard-checking for information about suppressing this check with
         (set-guard-checking :none), as recommended for new users.
    
         ACL2 !>
    
    
    File: acl2-doc-emacs.info,  Node: SET-IGNORE-DOC-STRING-ERROR,  Next: SET-IGNORE-OK,  Prev: SET-GUARD-CHECKING,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-IGNORE-DOC-STRING-ERROR    allow ill-formed documentation strings
    
         General Forms:
         (set-ignore-doc-string-error nil)   ; :doc strings must be well-formed
         (set-ignore-doc-string-error t)     ; ill-formed :doc strings are ignored
         (set-ignore-doc-string-error :warn) ; ill-formed :doc strings are ignored
                                             ;   except for causing a warning
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
         General Form:
         (set-ignore-doc-string-error flag)
    
    where flag is nil, t, or :warn, as indicated above.  This macro is
    essentially equivalent to
    
         (table acl2-defaults-table :ignore-doc-string-error flag)
    
    and hence is local to any books and encapsulate events in which it
    occurs; see *note ACL2-DEFAULTS-TABLE::.  However, unlike the above
    simple call of the table event function (see *note TABLE::), no output
    results from a set-ignore-doc-string-error event.
    
    Note that since defdoc events have the sole purpose of installing
    documentation strings, these require well-formed documentation strings
    even after executing a call of ignore-doc-string-error.
    
    The default behavior of the system is as though the
    :ignore-doc-string-error value is nil.  The current behavior can be
    ascertained by evaluating the following form.
    
         (ignore-doc-string-error (w state))
    
    
    File: acl2-doc-emacs.info,  Node: SET-IGNORE-OK,  Next: SET-INHIBIT-OUTPUT-LST,  Prev: SET-IGNORE-DOC-STRING-ERROR,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-IGNORE-OK    allow unused formals and locals without an ignore or ignorable declaration
    
         Examples:
         (set-ignore-ok t)
         (set-ignore-ok nil)
         (set-ignore-ok :warn)
    
    The first example above allows unused formals and locals, i.e.,
    variables that would normally have to be declared ignored or ignorable.
    The second example disallows unused formals and locals; this is the
    default.  The third example allows them, but prints an appropriate
    warning.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
         General Form:
         (set-ignore-ok flg)
    
    where flg is either t, nil, or :warn.
    
    One might find this event useful when one is generating function
    definitions by an automated procedure, when that procedure does not
    take care to make sure that all formals are actually used in the
    definitions that it generates.
    
    Note:  Defun will continue to report irrelevant formals even if
    :set-ignore-ok has been set to t, unless you also use
    set-irrelevant-formals-ok to instruct it otherwise.
    
    
    File: acl2-doc-emacs.info,  Node: SET-INHIBIT-OUTPUT-LST,  Next: SET-INHIBIT-WARNINGS,  Prev: SET-IGNORE-OK,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-INHIBIT-OUTPUT-LST    control output
    
         Examples:
         (set-inhibit-output-lst '(warning))
         (set-inhibit-output-lst '(proof-tree prove proof-checker))
         (set-inhibit-output-lst *valid-output-names*) ; inhibit all prover output
         :set-inhibit-output-lst (proof-tree prove)
    
         General Form:
         (set-inhibit-output-lst lst)
    
    where lst is a form (which may mention state) that evaluates to a list
    of names, each of which is the name of one of the following "kinds" of
    output produced by ACL2.
    
           error          error messages
           warning        warnings other than those related to soundness
           warning!       warnings (of all degrees of importance)
           observation    observations
           prove          commentary produced by the theorem prover
           proof-checker  commentary produced by the proof-checker
           event          non-proof commentary produced by events such as defun
                          and encapsulate
           expansion      commentary produced by make-event expansion
           summary        the summary at the successful conclusion of an event
           proof-tree     proof-tree output
    
    It is possible to inhibit each kind of output by putting the
    corresponding name into lst.  For example, if 'warning is included in
    (the value of) lst, then no warnings are printed except those related
    to soundness, e.g., the inclusion of an uncertified book.  Note that
    proof-tree output is affected by set-inhibit-output-lst; see *note
    PROOF-TREE::.
    
    See *note WITH-OUTPUT:: for a variant of this utility that can be used
    in books.  Also see *note SET-INHIBIT-WARNINGS:: for how to inhibit
    individual warning types and see *note SET-INHIBITED-SUMMARY-TYPES::
    for how to inhibit individual parts of the summary.
    
    Printing of events on behalf of certify-book and encapsulate is
    inhibited when both 'event and 'prove belong to lst.  Otherwise,
    printing of events is controlled by the ld special ld-pre-eval-print.
    
    _Note for advanced users._  By including warning! in lst, you are
    automatically including warning as well: all warnings will be inhibited.
    This is not the case if you modify value of state global variable
    'inhibit-output-lst directly (with assign or f-put-global); then, if
    you include warning! but not warning, then warnings not related to
    soundness will still be printed (which is probably not what was
    intended).
    
    
    File: acl2-doc-emacs.info,  Node: SET-INHIBIT-WARNINGS,  Next: SET-INHIBIT-WARNINGS!,  Prev: SET-INHIBIT-OUTPUT-LST,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-INHIBIT-WARNINGS    control warnings
    
         Examples:
         (set-inhibit-warnings "theory" "use")
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It is
    local to the book or encapsulate form in which it occurs; see *note
    SET-INHIBIT-WARNINGS!:: for a corresponding non-local event.  Indeed,
    (set-inhibit-warnings ...) is equivalent to (local
    (set-inhibit-warnings! ...)).
    
         General Form:
         (set-inhibit-warnings string1 string2 ...)
    
    where each string is considered without regard to case.  This macro is
    equivalent to (local (table inhibit-warnings-table nil 'lst :clear)),
    where lst is the list of strings supplied.  This macro is an event (see
    *note TABLE::), but no output results from a set-inhibit-warnings event.
    
    ACL2 prints warnings that may, from time to time, seem excessive to
    experienced users.  Each warning is "labeled" with a string identifying
    the type of warning.  Consider for example
    
         ACL2 Warning [Use] in ( THM ...):  It is unusual to :USE ....
    
    Here, the label is "Use".  The argument list for set-inhibit-warnings
    is a list of such labels, each of which is a string.  Any warning is
    suppressed if its label is a member of this list, where case is
    ignored, .  Thus, for example, the warning above will not be printed
    after a call of set-inhibit-warnings that contains the string, "Use"
    (or any string that is string-equal to "Use", such as "use" or "USE").
    In summary: the effect of this event is to suppress any warning whose
    label is a member of the given argument list, where case is ignored.
    
    The list of currently inhibited warnings is the list of keys in the
    table named inhibit-warnings-table.  (The values in the table are
    irrelevant.)  One way to get that value is to get the result from
    evaluating the following form: (table-alist 'inhibit-warnings-table (w
    state)).  Of course, if warnings are inhibited overall -- see *note
    SET-INHIBIT-OUTPUT-LST:: -- then this value is entirely irrelevant.
    
    
    File: acl2-doc-emacs.info,  Node: SET-INHIBIT-WARNINGS!,  Next: SET-INHIBITED-SUMMARY-TYPES,  Prev: SET-INHIBIT-WARNINGS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-INHIBIT-WARNINGS!    control warnings non-locally
    
    Please see *note SET-INHIBIT-WARNINGS::, which is the same as
    set-inhibit-warnings!  except that the latter is not local to the
    encapsulate or the book in which it occurs.  Probably
    set-inhibit-warnings is to be preferred unless you have a good reason
    for wanting to export the effect of this event outside the enclosing
    encapsulate or book.
    
    
    File: acl2-doc-emacs.info,  Node: SET-INHIBITED-SUMMARY-TYPES,  Next: SET-INVISIBLE-FNS-TABLE,  Prev: SET-INHIBIT-WARNINGS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-INHIBITED-SUMMARY-TYPES    control which parts of the summary are printed
    
         Example:
         (set-inhibited-summary-types '(rules time))
    
    Note: This is not an event.  Rather, it changes the state, in analogy to
    set-inhibit-output-lst.
    
         General Form:
         (set-inhibited-summary-types form)
    
    where form evaluates to a true-list of symbols, each of which is among
    the values of the constant *summary-types*, i.e.: header, form, rules,
    hint-events warnings, time, steps, value, and splitter-rules.  Each
    specified type inhibits printing of the corresponding portion of the
    summaries printed at the conclusions of events, where header refers to
    an initial newline followed by the line containing just the word
    Summary.
    
    Note the distinction between rules and hint-events.  Rules provides a
    record of automatic rule usage by the prover, while hint-events shows
    the names of events given to :USE or :BY hints, as well as
    clause-processor functions given to :CLAUSE-PROCESSOR hints that have
    an effect on the proof.
    
    Also see *note SET-INHIBIT-OUTPUT-LST::.  Note that
    set-inhibited-summary-types has no effect when summary is one of the
    types inhibited by set-inhibit-output-lst, because in that case none of
    the summary will be printed.
    
    To control summary types for a single event, see *note WITH-OUTPUT::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-INVISIBLE-FNS-TABLE,  Next: SET-IRRELEVANT-FORMALS-OK,  Prev: SET-INHIBITED-SUMMARY-TYPES,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-INVISIBLE-FNS-TABLE    set the invisible functions table
    
         Examples:
         (set-invisible-fns-table ((binary-+ unary--)
                                   (binary-* unary-/)
                                   (unary-- unary--)
                                   (unary-/ unary-/)))
         (set-invisible-fns-table t) ; restore original invisible-fns-table
    
    Among other things, the setting above has the effect of making unary-
    "invisible" for the purposes of applying permutative :rewrite rules to
    binary-+ trees.  Thus, arg and (unary- arg) will be given the same
    weight and will be permuted so as to be adjacent.  The form
    (invisible-fns-table (w state)) returns the current value of the
    invisible functions table.
    
    Also see *note ADD-INVISIBLE-FNS:: and see *note REMOVE-INVISIBLE-FNS::
    for events that add to and remove from the invisible functions table,
    while accounting for macro aliases (see *note MACRO-ALIASES-TABLE::).
    
         General Form:
         (set-invisible-fns-table alist)
    
    where alist is either t or a true list of pairs, each element of which
    is of the form (fn ufn1 ... ufnk), where fn is a function symbol and
    each ufni is a unary function symbol.  When alist is t, the initial
    value of this table is used in its place.  Modulo the replacement of
    alist by the default setting when alist is t, this macro is equivalent
    to
    
         (table invisible-fns-table nil 'alist :clear)
    
    which is also an event (see *note TABLE::).
    
    Note that set-invisible-fns-table does not evaluate its argument.
    However, you can call table directly for that purpose.  For example,
    
         (set-invisible-fns-table ((binary-+ unary--)
                                   (binary-* unary-/)
                                   (unary-- unary--)
                                   (unary-/ unary-/)))
    
    ie equivalent to the following; see *note TABLE::.
    
         (table invisible-fns-table nil
                (quote ((binary-+ unary--)
                        (binary-* unary-/)
                        (unary-- unary--)
                        (unary-/ unary-/)))
                :clear)
    
    See *note INVISIBLE-FNS-TABLE:: for a description of the invisible
    functions table.
    
    
    File: acl2-doc-emacs.info,  Node: SET-IRRELEVANT-FORMALS-OK,  Next: SET-LD-KEYWORD-ALIASES,  Prev: SET-INVISIBLE-FNS-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-IRRELEVANT-FORMALS-OK    allow irrelevant formals in definitions
    
         Examples:
         (set-irrelevant-formals-ok t)
         (set-irrelevant-formals-ok nil)
         (set-irrelevant-formals-ok :warn)
    
    The first example above allows irrelevant formals in definitions; see
    *note IRRELEVANT-FORMALS::.  The second example disallows irrelevant
    formals; this is the default.  The third example allows irrelevant
    formals, but prints an appropriate warning.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
         General Form:
         (set-irrelevant-formals-ok flg)
    
    where flg is either t, nil, or :warn.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LD-KEYWORD-ALIASES,  Next: SET-LD-KEYWORD-ALIASES!,  Prev: SET-IRRELEVANT-FORMALS-OK,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LD-KEYWORD-ALIASES    See *note LD-KEYWORD-ALIASES::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LD-KEYWORD-ALIASES!,  Next: SET-LD-REDEFINITION-ACTION,  Prev: SET-LD-KEYWORD-ALIASES,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LD-KEYWORD-ALIASES!    See *note LD-KEYWORD-ALIASES::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LD-REDEFINITION-ACTION,  Next: SET-LD-SKIP-PROOFS,  Prev: SET-LD-KEYWORD-ALIASES!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LD-REDEFINITION-ACTION    See *note LD-REDEFINITION-ACTION::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LD-SKIP-PROOFS,  Next: SET-LD-SKIP-PROOFSP,  Prev: SET-LD-REDEFINITION-ACTION,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LD-SKIP-PROOFS    See *note SET-LD-SKIP-PROOFSP::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LD-SKIP-PROOFSP,  Next: SET-LET*-ABSTRACTION,  Prev: SET-LD-SKIP-PROOFS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LD-SKIP-PROOFSP    See *note LD-SKIP-PROOFSP::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LET*-ABSTRACTION,  Next: SET-LET*-ABSTRACTIONP,  Prev: SET-LD-SKIP-PROOFSP,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LET*-ABSTRACTION    See *note SET-LET*-ABSTRACTIONP::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-LET*-ABSTRACTIONP,  Next: SET-MATCH-FREE-DEFAULT,  Prev: SET-LET*-ABSTRACTION,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-LET*-ABSTRACTIONP    to shorten many prettyprinted clauses
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
    When this flag is set to t, subterms that occur more than once in a
    clause are abstracted away with let*, generally shortening the
    displayed size of the clauses.  This flag only affects how clauses are
    printed.  It does not change what terms the theorem prover manipulates.
    
         :set-let*-abstractionp t ;;; or, (set-let*-abstractionp t)
    
    will cause the prettyprinter to do "let* abstraction" on clauses before
    they are printed.  The algorithm finds the maximal multiply-occuring
    subterm and extracts it, binding it to some new variable and replacing
    its occurrences by that variable.  This produces a let* form.  This
    process is iterated until no subterm occurs more than once.  This
    process generally takes a little time, but less time than to print
    large clauses.  The process can greatly reduce the amount of text
    produced by the prover.
    
    THIS ONLY AFFECTS HOW THE CLAUSES ARE PRINTED!  The unabstracted
    clauses are manipulated by the theorem prover.
    
         :set-let*-abstractionp nil
    
    restores normal clause printing.
    
    The mode is stored in the defaults table, See *note
    ACL2-DEFAULTS-TABLE::.  Thus, the mode may be set locally in books.
    
    
    File: acl2-doc-emacs.info,  Node: SET-MATCH-FREE-DEFAULT,  Next: SET-MATCH-FREE-ERROR,  Prev: SET-LET*-ABSTRACTIONP,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-MATCH-FREE-DEFAULT    provide default for :match-free in future rules
    
         General Forms:
         (set-match-free-default :once)
         (set-match-free-default :all)
         (set-match-free-default nil)
    
    Note: This utility does not apply to type-prescription rules; for a
    related topic pertinent to such rules, see *note
    FREE-VARIABLES-TYPE-PRESCRIPTION::.
    
    As described elsewhere (see *note FREE-VARIABLES::), a rewrite, linear,
    or forward-chaining rule may have free variables in its hypotheses, and
    ACL2 can be directed either to try all bindings (":all") or just the
    first (":once") when relieving that hypothesis, as a basis for
    relieving subsequent hypotheses.  This directing of :all or :once is
    generally provided by specifying either :match-free :once or
    :match-free :all in the :rule-classes of the rule.  If neither of these
    is specified, then the most recent set-match-free-default is used by
    ACL2 to fill in this missing :match-free field.  See *note
    RULE-CLASSES::.  Except: If the last set-match-free-default specifies
    nil, then ACL2 reverts to the behavior it had at start-up, as described
    in Remarks (2) and (3) below.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It
    uses the acl2-defaults-table, and hence its effect is local to the book
    or encapsulate form in which it occurs.
    
    Remarks.
    
    (1) The use of set-match-free-default has no effect on existing rules.
    In order to change the behavior of existing rules with respect to
    free-variable matching, see *note ADD-MATCH-FREE-OVERRIDE::.
    
    (2) If you submit a rewrite, linear, or forward-chaining rule with a
    free variable in a hypothesis, and no default setting was previously
    specified with set-match-free-default or the default setting is nil,
    and the rule is not within a book being processed with include-book,
    certify-book, or rebuild, then a warning or error is caused.  In order
    to make this an error instead of a warning, see *note
    SET-MATCH-FREE-ERROR::.
    
    (3) If you submit a rewrite, linear, or forward-chaining rule with a
    free variable in a hypothesis, and no default setting has been
    previously specified with set-match-free-default or the default setting
    is nil, and no error is caused (see (2) above), then the default :all
    is used.
    
    
    File: acl2-doc-emacs.info,  Node: SET-MATCH-FREE-ERROR,  Next: SET-MEASURE-FUNCTION,  Prev: SET-MATCH-FREE-DEFAULT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-MATCH-FREE-ERROR    control error vs. warning when :match-free is missing
    
         Legal Forms:
         (set-match-free-error nil)
         :set-match-free-error nil
         (set-match-free-error t)
         :set-match-free-error t
    
    As described elsewhere (see *note FREE-VARIABLES::), when a rewrite,
    linear, or forward-chaining rule has free variables in its hypotheses,
    the user can specify whether to try all bindings (":all") or just the
    first (":once") when relieving its hypotheses, as a basis for relieving
    subsequent hypotheses.  This direction of :all or :once is generally
    provided by specifying either :match-free :once or :match-free :all in
    the :rule-classes of the rule.
    
    But suppose that neither of these is specified for such a rule.  (Note:
    set-match-free-error is not relevant for type-prescription rules.)
    Also suppose that set-match-free-default has not specified a default of
    :once or :all (see *note SET-MATCH-FREE-DEFAULT::).  In this case a
    warning will occur except when in the context of include-book.  If you
    prefer to see an error in such cases, except in the context of
    certify-book, execute (set-match-free-error t).  If there is no error,
    then a default of :all is used.
    
    Note: This is NOT an event!  Instead, set-match-free-error sets the
    state global 'match-free-error (see *note STATE:: and see *note
    ASSIGN::).  Thus, this form cannot be put into a book.  If you are
    tempted to put it into a book, consider the fact that it really isn't
    needed there, since the absence of :match-free does not cause an error
    in the context of certify-book or include-book.  If you still feel the
    need for such a form, consider using set-match-free-default to provide
    a default, at least within the scope of the current book (if any); see
    *note SET-MATCH-FREE-DEFAULT::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-MEASURE-FUNCTION,  Next: SET-NON-LINEAR,  Prev: SET-MATCH-FREE-ERROR,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-MEASURE-FUNCTION    set the default measure function symbol
    
         Examples:
         (set-measure-function nqthm::count)
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
         General Form:
         (set-measure-function name)
    
    where name is a function symbol of one argument.  This macro is
    equivalent to (table acl2-defaults-table :measure-function 'name), and
    hence is local to any books and encapsulate events in which it occurs;
    see *note ACL2-DEFAULTS-TABLE::.  Although this is thus an event (see
    *note TABLE::), nevertheless no output results from a
    set-measure-function event.
    
    This event sets the default measure function to name.  Subsequently, if
    a recursively defined function is submitted to defun with no explicitly
    given :measure argument, defun "guesses" the measure (name var), where
    name is the then current default measure function and var is the first
    formal found to be tested along every branch and changed in every
    recursive call.
    
    Note that if (table acl2-defaults-table :measure-function 'name) has its
    default value of nil, then the default measure function is acl2-count.
    
    
    File: acl2-doc-emacs.info,  Node: SET-NON-LINEAR,  Next: SET-NON-LINEARP,  Prev: SET-MEASURE-FUNCTION,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-NON-LINEAR    See *note SET-NON-LINEARP::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-NON-LINEARP,  Next: SET-NU-REWRITER-MODE,  Prev: SET-NON-LINEAR,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-NON-LINEARP    to turn on or off non-linear arithmetic reasoning
    
         Examples:
         (set-non-linearp t)
         (set-non-linearp nil)
    
    See *note NON-LINEAR-ARITHMETIC::.   This event is equivalent to (table
    acl2-defaults-table :non-linearp ), and hence is local to any
    books and encapsulate events in which it occurs; see *note
    ACL2-DEFAULTS-TABLE::.
    
    The initial value is nil.
    
    
    File: acl2-doc-emacs.info,  Node: SET-NU-REWRITER-MODE,  Next: SET-OVERRIDE-HINTS,  Prev: SET-NON-LINEARP,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-NU-REWRITER-MODE    to turn on and off the nu-rewriter
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
    This event sets a flag that controls whether the ACL2 rewriter uses the
    special-purpose nth/update-nth rewriter (nu-rewriter).  The flag may
    have one of three values: nil, t, or :literals.
    
         :set-nu-rewriter-mode nil        ; do not use nu-rewriter
         :set-nu-rewriter-mode t          ; use nu-rewriter in rewriting
         :set-nu-rewriter-mode :literals  ; use nu-rewriter in rewriting after
                                          ;  a pre-pass through every literal
         (set-nu-rewriter-mode :literals) ; same as above
    
    The value nil prevents the use of the nu-rewriter.  The other two
    values allow the use of the nu-rewriter.
    
    When the flag is non-nil and the rewriter encounters a term that
    "begins with an nth", the nu-rewriter is applied.  By "begins with an
    nth" here we mean either the term is an application of nth or is an
    application of some nonrecursive function or lambda expression whose
    body contains an expression that begins with an nth.
    
    Note that the use of the nu-rewriter here described above is driven by
    the rewriter, i.e., the nu-rewriter is applied only to terms visited by
    the rewriter in its inside-out sweep.  When the flag is set to
    :literals the system makes a pre-pass through every goal clause and
    applies the nu-rewriter to every subterm.  The rewriter is then used on
    the output of that pre-pass.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
    We expect to write more documentation as we gain experience with the
    nu-rewriter.
    
    
    File: acl2-doc-emacs.info,  Node: SET-OVERRIDE-HINTS,  Next: SET-OVERRIDE-HINTS!,  Prev: SET-NU-REWRITER-MODE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-OVERRIDE-HINTS    set the override-hints
    
    See *note OVERRIDE-HINTS:: for a discussion of override-hints.  Here we
    describe how to set them.  Note that the effects of set-override-hints
    events are local to the books or encapsulate events in which they
    reside; see *note SET-OVERRIDE-HINTS!:: to avoid that restriction.  Also
    see *note ADD-OVERRIDE-HINTS:: to add to the list of override-hints,
    rather than setting a new list and ignoring the present list.
    
         General Form:
         (set-override-hints form)
    
    where form evaluates to a list of computed hint forms.  The effect of
    this event is to set the list of override-hints to the result of that
    evaluation.
    
    
    File: acl2-doc-emacs.info,  Node: SET-OVERRIDE-HINTS!,  Next: SET-PARALLEL-EXECUTION,  Prev: SET-OVERRIDE-HINTS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-OVERRIDE-HINTS!    set the override-hints non-locally
    
    Set-override-hints! is the same as set-override-hints, except that the
    former is not local to books or encapsulate events in which it occurs.
    See *note SET-OVERRIDE-HINTS::; also see *note ADD-OVERRIDE-HINTS::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-PARALLEL-EXECUTION,  Next: SET-PRINT-CLAUSE-IDS,  Prev: SET-OVERRIDE-HINTS!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-PARALLEL-EXECUTION    for ACL2(p): enabling parallel execution for four parallelism primitives
    
    This documentation topic relates to the experimental extension of ACL2
    supporting parallel execution and proof; see *note PARALLELISM::.  See
    *note PARALLELISM-TUTORIAL:: for an introduction to parallel execution
    in ACL2.
    
         General Forms:
         (set-parallel-execution nil) ; default for images not built for parallelism
         (set-parallel-execution t)   ; default for images built for parallelism
         (set-parallel-execution :bogus-parallelism-ok)
    
    Set-parallel-execution takes an argument that specifies the enabling or
    disabling of parallel execution for the primitives pand, por, plet, and
    pargs (but not spec-mv-let, whose parallel execution remains enabled).
    However, without using top-level, calls of parallelism primitives made
    explicitly in the ACL2 top-level loop, as opposed to inside function
    bodies, will never cause parallel execution; see *note
    PARALLELISM-AT-THE-TOP-LEVEL::.  Parallel execution is determined by
    the value of the argument to set-parallel-execution, as follows.
    
    Value t:
    All parallelism primitives used in bodies of function definitions are
    given the opportunity to execute in parallel.  However, the use of
    parallelism primitives directly in the ACL2 top-level loop causes an
    error.
    
    Value :bogus-parallelism-ok:
    Parallel execution is enabled, as for value t.  However, the use of
    parallelism primitives directly in the ACL2 top-level loop does not
    cause an error, but rather, simply results in serial execution for
    these primitives.
    
    Value nil:
    All parallelism primitives degrade to their serial equivalents,
    including their calls made directly in the ACL2 top-level loop.  Thus,
    uses of parallelism primitives do not in themselves cause errors.
    
    
    File: acl2-doc-emacs.info,  Node: SET-PRINT-CLAUSE-IDS,  Next: SET-PROVER-STEP-LIMIT,  Prev: SET-PARALLEL-EXECUTION,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-PRINT-CLAUSE-IDS    cause subgoal numbers to be printed when 'prove output is inhibited
    
         General Forms:
         (set-print-clause-ids t)
         :set-print-clause-ids t
         (set-print-clause-ids nil)
         :set-print-clause-ids nil
    
    This command affects output from the theorem prover only when 'prove
    output is inhibited (see *note SET-INHIBIT-OUTPUT-LST::) or gag-mode is
    on (but in that case the :goals setting issues this command
    automatically; see *note SET-GAG-MODE::).  Calling this macro with
    value t as shown above will cause subsequent proof attempts with 'prove
    output inhibited to print the subgoal number, so that you can see the
    progress of the proof; value nil reverts to the default behavior, where
    this is not the case.  On a related note, we point out that you can
    cause output to be saved for later display; see *note PSO:: and see
    *note PSO!::.
    
    If 'prove output is inhibited or gag-mode is on, and if you issue
    (set-print-clause-ids t) (either explicitly or with (set-gag-mode
    :goals)), then you can restrict when subgoal numbers are printed.  In
    the following example we restrict to subgoals that are no more than
    four inductions deep, no more than four casesplits deep, and no more
    than four single-subgoals deep.  For additional relevant explanation,
    see *note CLAUSE-IDENTIFIER:: and see *note DEFATTACH::.
    
         (defun print-clause-id-okp-level-4 (cl-id)
           (declare (xargs :mode :logic :guard (clause-id-p cl-id)))
           (and (<= (length (access clause-id cl-id :pool-lst))
                    4)
                (<= (length (access clause-id cl-id :case-lst))
                    4)
                (<= (access clause-id cl-id :primes)
                    4)))
    
         (defattach print-clause-id-okp print-clause-id-okp-level-4)
    
    
    File: acl2-doc-emacs.info,  Node: SET-PROVER-STEP-LIMIT,  Next: SET-RAW-MODE,  Prev: SET-PRINT-CLAUSE-IDS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-PROVER-STEP-LIMIT    sets the step-limit used by the ACL2 prover
    
    This event provides a way to limit the number of so-called "prover
    steps" permitted for an event.  See *note WITH-PROVER-STEP-LIMIT:: for
    a way to specify the limit on prover steps for a single event, rather
    than globally.  For a related utility based on time instead of prover
    steps, see *note WITH-PROVER-TIME-LIMIT::.  For examples of how step
    limits work, see the community book books/misc/misc2/step-limits.lisp.
    For a utility that returns an indicator of the number of prover steps
    most recently taken, see *note LAST-PROVER-STEPS::.
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    Moreover, its effect is to set the acl2-defaults-table, and hence its
    effect is local to the book or encapsulate form containing it; see
    *note ACL2-DEFAULTS-TABLE::.
    
         Example Forms:
         (set-prover-step-limit *default-step-limit*) ; no limit on prover steps
         (set-prover-step-limit nil)   ; abbreviation for the form just above
         (set-prover-step-limit 10000) ; allow at most 10,000 prover steps per event
    
         General Form:
         (set-prover-step-limit expr)
    
    where expr evaluates either to nil or else to a natural number not
    exceeding the value of *default-step-limit*.  If that value is nil or
    the value of *default-step-limit*, then no default limit is placed on
    the number of prover "steps" (see below) during processing of an event.
    Otherwise, that value is the maximum number of prover steps permitted
    before an error occurs.
    
    This event specifies the limit on the number of "steps" counted by the
    ACL2 prover during processing of an event.  Currently, a step is
    counted for each call of the system functions rewrite and
    expand-abbreviations.  However, the steps counted may change in future
    releases of ACL2, so users would probably be well served by avoiding
    the assumption that only the above two calls are counted as prover
    steps.
    
    Depending on the computer you are using, you may have less than a
    half-hour of time before the number of prover steps exceeds the maximum
    step-limit, which is one less than the value of *default-step-limit*.
    Note however the exception stated above: if the "limit" is nil or is
    the value of *default-step-limit*, then no limit is imposed.
    
    There is at best a loose connection between the counting of steps and
    with-prover-time-limit.  In particular, for a call of mfc-rw or any
    mfc- function (see *note EXTENDED-METAFUNCTIONS::), the steps taken
    during that call are forgotten when returning from that call.
    
    The limit is relevant for every event, as well as for calls of thm and
    certify-book -- and more generally, to any form that creates a "summary
    context" to print the usual event summary.  The limit is also put in
    force when entering the proof-checker.  A call of set-prover-step-limit
    applies to each subsequent form unless the call of
    set-prover-step-limit is within a summary context, in which case its
    effect disappears when exiting that summary context.
    
    The limit applies to each event, not just "atomic" events.  Consider the
    following example.
    
         (set-prover-step-limit 500)
    
         (encapsulate
           ()
           (defthm lemma-1 ; takes 380 steps
             (equal (append (append x y) z) (append x y z))
             :rule-classes nil)
           (defthm lemma-2 ; would take 319 steps
             (equal (len (append x y)) (+ (len x) (len y)))
             :rule-classes nil))
    
    The first defthm event, lemma-1 takes 380 steps (as of this writing),
    as shown in the summary:
    
         Prover steps counted:  380
         LEMMA-1
    
    The second defthm event, lemma-2, takes 319 steps (as of this writing)
    when evaluated at the top level.  However, in the context above, 380
    steps of the available 500 steps (from the set-prover-step-limit event
    above) have already been taken under the above encapsulate event.
    Thus, when the number of steps would exceed 120, the proof of lemma-2 is
    aborted:
    
         ACL2 Error in STEP-LIMIT:  The prover step-limit, which is 120 in the
         current context, has been exceeded.  See :DOC set-prover-step-limit.
    
    The summary for lemma-2 reflects that situation:
    
         Prover steps counted:  More than 120
    
    The summary for the encapsulate event then indicates that the available
    steps for that event have also been exceeded:
    
         Prover steps counted:  More than 500
    
    The discussion above applies to any event that contains other events,
    hence applies similarly to progn events.
    
    For those who use make-event, we note that prover steps in the
    expansion phase similarly contribute to the total number of steps
    counted.  For example, suppose that the limit is 500 prover steps as
    above, and you submit (make-event EXPR), where 300 prover steps take
    place during evaluation of EXPR, producing event EV.  Then evaluation
    of EV will cause an error if it takes more than 200 prover steps.  This
    observation actually can be used to count prover steps for sequences of
    forms that are not all legal events (see *note EMBEDDED-EVENT-FORM::),
    such as calls of thm.  For example, a small built-in ACL2 test suite
    that includes thm forms can be run by evaluating the form
    (mini-proveall), and the steps can be counted as shown below.  (Here we
    assume a fresh ACL2 session; an error would occur if first, we evaluate
    the event (set-prover-step-limit 500) displayed above.)
    
         ACL2 !>(make-event (er-progn (mini-proveall) (value '(value-triple nil))))
         [[... output omitted here ...]]
         Summary
         Form:  ( MAKE-EVENT (ER-PROGN ...))
         Rules: NIL
         Warnings:  Double-rewrite, Equiv, Subsume and Non-rec
         Time:  0.38 seconds (prove: 0.04, print: 0.29, other: 0.05)
         Prover steps counted:  41090
          NIL
         ACL2 !>
    
    
    File: acl2-doc-emacs.info,  Node: SET-RAW-MODE,  Next: SET-RAW-MODE-ON!,  Prev: SET-PROVER-STEP-LIMIT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-RAW-MODE    enter or exit "raw mode," a raw Lisp environment
    
    Below we discuss raw-mode.  In brief: The simplest way to turn raw-mode
    on is :SET-RAW-MODE-ON!, and to turn it off, :SET-RAW-MODE NIL.  Also
    see *note SET-RAW-MODE-ON!::.
    
    * Menu:
    
    * ADD-RAW-ARITY:: add arity information for raw mode
    
    * REMOVE-RAW-ARITY:: remove arity information for raw mode
    
    ACL2 users often find its careful syntax checking to be helpful during
    code development.  Sometimes it is even useful to do code development in
    :logic mode, where ACL2 can be used to check termination of (mutually)
    recursive functions, verify guards, or even prove properties of the
    functions.
    
    However, loading code using include-book is much slower than using
    Common Lisp load in raw Lisp, and in this sense ACL2 can get in the way
    of efficient execution.  Unfortunately, it is error-prone to use ACL2
    sources (or their compilations) in raw Lisp, primarily because a number
    of ACL2 primitives will not let you do so.  Perhaps you have seen this
    error message when trying to do so:
    
         HARD ACL2 ERROR in ACL2-UNWIND-PROTECT:  Apparently you have tried
         to execute a form in raw Lisp that is only intended to be executed
         inside the ACL2 loop.
    
    Even without this problem it is important to enter the ACL2 loop (see
    *note LP::), for example in order to set the cbd and (to get more
    technical) the readtable.
    
    ACL2 provides a "raw mode" for execution of raw Lisp forms.  In this
    mode, include-book reduces essentially to a Common Lisp load.  More
    generally, the ACL2 logical world is not routinely extended in raw mode
    (some sneaky tricks are probably required to make that happen).  To
    turn raw mode off or on:
    
         :set-raw-mode t   ; turn raw mode on
         :set-raw-mode nil ; turn raw mode off
    
    The way you can tell that you are in raw mode is by looking at the
    prompt (see *note DEFAULT-PRINT-PROMPT::), which uses a capital "P"
    (suggesting something like program mode, but more so).
    
         ACL2 P>
    
    Typical benefits of raw mode are fast loading of source and compiled
    files and the capability to hack arbitrary Common Lisp code in an
    environment with the ACL2 sources loaded (and hence with ACL2
    primitives available).  In addition, ACL2 hard errors will put you into
    the Lisp debugger, rather than returning you to the ACL2 loop, and this
    may be helpful for debugging; see *note HARD-ERROR:: and see *note
    ILLEGAL::, but also see *note BREAK-ON-ERROR::.  However, it probably
    is generally best to avoid raw mode unless these advantages seem
    important.  We expect the main benefit of raw mode to be in deployment
    of applications, where load time is much faster than the time required
    for a full-blown include-book, although in certain cases the fast
    loading of books and treatment of hard errors discussed above may be
    useful during development.
    
    Raw mode is also useful for those who want to build extensions of ACL2.
    For example, the following form can be put into a certifiable book to
    load an arbitrary Common Lisp source or compiled file.
    
         (progn (defttag my-application)
                (progn! (set-raw-mode t)
                        (load "some-file")))
    
    Also see with-raw-mode defined in community book
    books/hacking/hacker.lisp, see *note DEFTTAG::, and see *note PROGN!::.
    
    Below are several disadvantages to raw mode.  These should discourage
    users from using it for general code development, as :program mode is
    generally preferable.
    
         -- Forms are in essence executed in raw Lisp.  Hence:
            -- Syntax checking is turned off; and
            -- Guard checking is completely disabled.
         -- Table events, including logic, are ignored, as are many
            other events, including defthm and comp.
         -- Soundness claims are weakened for any ACL2 session in which raw
            mode was ever entered; see *note DEFTTAG::.
         -- The normal undoing mechanism (see *note UBT::) is not supported.
         -- Unexpected behavior may occur when you return from raw-mode.
            For example, if you redefine a :logic mode function whose guards
            have not been verified, you will not see the change inside the
            ACL2 loop because there, the raw Common Lisp definition is only
            executed after guards have been verified; see *note GUARDS-AND-EVALUATION::
            and see *note GUARD-EVALUATION-TABLE::.
    
    We conclude with some details.
    
    _Printing results_.  The rules for printing results are unchanged for
    raw mode, with one exception.  If the value to be printed would contain
    any Lisp object that is not a legal ACL2 object, then the print routine
    is used from the host Lisp, rather than the usual ACL2 printing
    routine.  The following example illustrates the printing used when an
    illegal ACL2 object needs to be printed.  Notice how that "command
    conventions" are observed (see *note LD-POST-EVAL-PRINT::); the "[Note"
    occurs one space over in the second example, and no result is printed
    in the third example.
    
         ACL2 P>(find-package "ACL2")
         [Note:  Printing non-ACL2 result.]
         #
         ACL2 P>(mv nil (find-package "ACL2") state)
          [Note:  Printing non-ACL2 result.]
         #
         ACL2 P>(mv t (find-package "ACL2") state)
         ACL2 P>(mv 3 (find-package "ACL2"))
         [Note:  Printing non-ACL2 result.]
         (3 #)
         ACL2 P>
    
    If you have trouble with large structures being printed out, you might
    want to execute appropriate Common Lisp forms in raw mode, for example,
    (setq *print-length* 5) and (setq *print-level* 5).
    
    _Include-book_.  The events add-include-book-dir and
    delete-include-book-dir have been designed to work with raw mode.
    However, if you enter raw mode and then evaluate such forms, then the
    effects of these forms will disappear when you exit raw mode, in which
    case you can expect to see a suitable warning.  Regarding
    _include-book_ itself: it should work in raw mode as you might expect,
    at least if a compiled file or expansion file was created when the book
    was certified; see *note CERTIFY-BOOK::.
    
    _Packages_.  Raw mode disallows the use of defpkg.  If you want to
    create a new package, first exit raw mode with :set-raw-mode nil; you
    can subsequently re-enter raw mode with :set-raw-mode t if you wish.
    
    
    File: acl2-doc-emacs.info,  Node: ADD-RAW-ARITY,  Next: REMOVE-RAW-ARITY,  Prev: SET-RAW-MODE,  Up: SET-RAW-MODE
    
    ADD-RAW-ARITY    add arity information for raw mode
    
    Technical note: This macro is a no-op, and is not necessary, when ACL2
    is built with #-acl2-mv-as-values.
    
    Users of raw mode (see *note SET-RAW-MODE::) can use arbitrary raw Lisp
    functions that are not known inside the usual ACL2 loop.  In such
    cases, ACL2 may not know how to display a multiple value returned by
    ACL2's mv macro.  The following example should make this clear.
    
         ACL2 P>(defun foo (x y) (mv y x))
         FOO
         ACL2 P>(foo 3 4)
    
         Note: Unable to compute number of values returned by this evaluation
         because function FOO is not known in the ACL2 logical world.  Presumably
         it was defined in raw Lisp or in raw mode.  Returning the first (perhaps
         only) value for calls of FOO.
         4
         ACL2 P>(add-raw-arity foo 2)
          RAW-ARITY-ALIST
         ACL2 P>(foo 3 4)
         (4 3)
         ACL2 P>
    
    The first argument of add-raw-arity should be a symbol, representing the
    name of a function, macro, or special form, and the second argument
    should either be a non-negative integer (denoting the number of values
    returned by ACL2) or else the symbol :LAST, meaning that the number of
    values returned by the call is the number of values returned by the last
    argument.
    
    The current arity assignments can be seen by evaluating (@
    raw-arity-alist).  See *note REMOVE-RAW-ARITY:: for how to undo a call
    of add-raw-arity.
    
    
    File: acl2-doc-emacs.info,  Node: REMOVE-RAW-ARITY,  Prev: ADD-RAW-ARITY,  Up: SET-RAW-MODE
    
    REMOVE-RAW-ARITY    remove arity information for raw mode
    
    Technical note: This macro is a no-op, and is not necessary, when ACL2
    is built with #-acl2-mv-as-values.
    
    The form (remove-raw-arity fn) undoes the effect of an earlier
    (remove-raw-arity fn val).  See *note ADD-RAW-ARITY::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-RAW-MODE-ON!,  Next: SET-RAW-PROOF-FORMAT,  Prev: SET-RAW-MODE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-RAW-MODE-ON!    enter "raw mode," a raw Lisp environment
    
    This is the same as (set-raw-mode t) except that it first introduces a
    so-called "trust tag" ("ttag") so that set-raw-mode will be legal.  See
    *note DEFTTAG:: for a discussion of ttags and how they affect
    certify-book and include-book.
    
    See *note SET-RAW-MODE:: for a discussion of raw-mode.
    
    
    File: acl2-doc-emacs.info,  Node: SET-RAW-PROOF-FORMAT,  Next: SET-REWRITE-STACK-LIMIT,  Prev: SET-RAW-MODE-ON!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-RAW-PROOF-FORMAT    print runes as lists in proof output from simplification
    
         General Forms:
         (set-raw-proof-format t)
         :set-raw-proof-format t
         (set-raw-proof-format nil)
         :set-raw-proof-format nil
    
    This command affects output from the theorem prover only when 'prove
    output is not inhibited (see *note SET-INHIBIT-OUTPUT-LST::) and
    gag-mode is off (see *note SET-GAG-MODE::).  Calling this macro with
    value t as shown above will cause simplification steps from proof
    output, including steps from preprocess (see *note SIMPLE::), to print
    the list of runes used in a list format, rather than in the English
    proof commentary.  This "raw" format can be handy when you want to use
    that list as a basis for hints that you construct for a subsequent
    proof attempt.
    
    
    File: acl2-doc-emacs.info,  Node: SET-REWRITE-STACK-LIMIT,  Next: SET-RULER-EXTENDERS,  Prev: SET-RAW-PROOF-FORMAT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-REWRITE-STACK-LIMIT    Sets the rewrite stack depth used by the rewriter
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
         Example Forms:
         (set-rewrite-stack-limit 30)                            ; set to small limit
         :set-rewrite-stack-limit 30                             ; same as above
         (set-rewrite-stack-limit *default-rewrite-stack-limit*) ; the default
         (set-rewrite-stack-limit (1- (expt 2 28)))              ; maximum legal limit
         :set-rewrite-stack-limit nil         ; same as above -- essentially, no limit
    
    This event sets the maximum stack depth for calls of certain functions
    that implement the ACL2 rewriter; see *note REWRITE-STACK-LIMIT::.  It
    must be a non-negative integer less than 2^28.  A call
    (set-rewrite-stack-limit limit) is equivalent to:
    
         (table acl2-defaults-table :rewrite-stack-limit limit).
    
    The use of acl2-defaults-table ensures that this event's effect is
    implicitly local to the book or encapsulate form in which it occurs.
    
    For a different but somewhat related concept, see *note
    BACKCHAIN-LIMIT::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-RULER-EXTENDERS,  Next: SET-RW-CACHE-STATE,  Prev: SET-REWRITE-STACK-LIMIT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-RULER-EXTENDERS    See *note RULER-EXTENDERS::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-RW-CACHE-STATE,  Next: SET-RW-CACHE-STATE!,  Prev: SET-RULER-EXTENDERS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-RW-CACHE-STATE    set the default rw-cache-state
    
    The ACL2 rewriter uses a data structure, called the rw-cache (rewriter
    cache), to save failed attempts to apply conditional rewrite rules.  The
    regression suite has taken approximately 11% less time with this
    mechanism.  The rw-cache is active by default but this event allows it
    to be turned off or modified.  Note that this event is local to its
    context (from encapsulate or include-book).  For a non-local version,
    use set-rw-cache-state!.
    
         Example forms:
         (set-rw-cache-state :atom)     ; default: rw-cache cleared for each literal
                                        ;   (i.e., hypothesis or conclusion of a goal)
         (set-rw-cache-state nil)       ; rw-cache is inactive
         (set-rw-cache-state t)         ; rw-cache persists beyond each literal
         (set-rw-cache-state :disabled) ; rw-cache is inactive, but the rw-cache-state
                                        ;   transitions to state t after
                                        ;   simplification takes place
    
         General Form:
         (set-rw-cache-state val)
    
    where val evaluates to one of the four values shown in "Example forms"
    above.  The default is :atom, which enables the rw-cache but clears it
    before rewriting a hypothesis or conclusion of any goal.  The value t is
    provides more aggresive use of the rw-cache, basically preserving the
    rw-cache when there is a single subgoal.  The value :disabled is the
    same as t, except that the rw-cache is initially inactive and only
    becomes active when some simplification has taken place.  We have seen
    a few cases where value t will make a proof fail but :disabled does not.
    
    The following example illustrates the rw-cache in action.  You will see
    a break during evaluation of the thm form.  Type :eval and you will see
    a failed rewriting attempt.  Type :go to continue, and at the next
    break type :eval again.  This time you will see the same failed
    rewriting attempt, but this time labeled with a notation saying that
    the failure was cached earlier, which indicates that this time the
    rewriter did not even attempt to prove the hypothesis of the rewrite
    rule f1->f2.
    
         (defstub f1 (x) t)
         (defstub f2 (x) t)
         (defaxiom f1->f2
                  (implies (f1 x) (equal (f2 x) t)))
         :brr t
         :monitor (:rewrite f1->f2) t
         (thm (equal (car (f2 a)) (cdr (f2 a))))
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.  It is
    local to the book or encapsulate form in which it occurs (see *note
    SET-RW-CACHE-STATE!:: for a corresponding non-local event).
    
    We also note that rw-cache-state changes may also be caused at the
    subgoal level; see *note HINTS::.
    
    We welcome you to experiment with different rw-cache states.  If the
    more aggressive values of t and :disabled cause proofs to fail, then you
    can revert to the default of :atom or even turn off the rw-cache using
    (set-rw-cache-state nil).  We don't expect users to need a deep
    knowledge of the rw-cache in order to do such experiments, but readers
    interested in details of the rw-cache implementation are invited to
    read the "Essay on Rw-cache" in the ACL2 source code.
    
    
    File: acl2-doc-emacs.info,  Node: SET-RW-CACHE-STATE!,  Next: SET-SAVED-OUTPUT,  Prev: SET-RW-CACHE-STATE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-RW-CACHE-STATE!    set the default rw-cache-state non-locally
    
    Please see *note SET-RW-CACHE-STATE::, which is the same as
    set-rw-cache-state!  except that the latter is not local to the
    encapsulate or the book in which it occurs.
    
    
    File: acl2-doc-emacs.info,  Node: SET-SAVED-OUTPUT,  Next: SET-SPLITTER-OUTPUT,  Prev: SET-RW-CACHE-STATE!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-SAVED-OUTPUT    save proof output for later display with :pso or :pso!
    
         Examples:
         (set-saved-output t t)    ; save proof output for later, but inhibit it now
         (set-saved-output t :all) ; save proof output for later, but inhibit all
                                   ;   output (except WARNING!, for critical warnings,
                                   ;   and ERROR, unless these are already inhibited)
         :set-saved-output t :all  ; same as the line above
         (set-saved-output t nil)  ; save proof output for later, but print it now too
         (set-saved-output nil t)  ; do not save proof output, and inhibit it
         (set-saved-output nil nil); do not save proof output or inhibit output
         (set-saved-output nil :same), (set-saved-output t :same)
                                   ; save proof output or not, as indicated, but do
                                   ;   not change which output is inhibited
         (set-saved-output nil :normal)
                                   ; the behavior when ACL2 first starts up: do not
                                   ;   save output, and only inhibit proof-tree output
         (set-saved-output t '(warning observation proof-tree prove))
                                   ; save proof output for later, and inhibit the
                                   ;   indicated kinds of output
    
         General Form:
         (set-saved-output save-flg inhibit-flg)
    
    Parameter save-flg is t to cause output to be saved for later display
    using pso or pso!; see *note PSO:: and see *note PSO!::, and see the
    documentation for proof-checker commands of the same names.  Set
    save-flg to nil to turn off this feature; except, it always stays on in
    proof-checker sessions entered with verify.  The other argument,
    inhibit-flg, controls whether output should be inhibited when it is
    created (normally, during a proof attempt).  So a common combination is
    to set both arguments to t, to indicate that output should be
    suppressed for now but saved for printing with pso or pso!.  The
    examples above give a good summary of the functionality for the second
    argument.
    
    Saved output is cleared at the start of every event, and also at the
    start of every proof-checker commands that invoke the prover.  Note that
    interactive proof-checker commands, that is, from a proof-checker
    session entered with verify, are always run with output saved.
    
    Also see *note SET-GAG-MODE::; and see *note SET-PRINT-CLAUSE-IDS::,
    which causes subgoal numbers to be printed during proof attempts when
    output is inhibited.
    
    See *note SET-INHIBIT-OUTPUT-LST:: if you want to inhibit certain
    output from the prover but not other output (e.g., not the summary),
    and you don't want to save any output.
    
    
    File: acl2-doc-emacs.info,  Node: SET-SPLITTER-OUTPUT,  Next: SET-STATE-OK,  Prev: SET-SAVED-OUTPUT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-SPLITTER-OUTPUT    turn on or off reporting of rules that may have caused case splits
    
         Examples:
         (set-splitter-output t)   ; enable  reports of ``splitter'' rules (default)
         (set-splitter-output nil) ; disable reports of ``splitter'' rules
    
    After evaluation of the form (set-splitter-output t) (the default), then
    whenever prove output is not inhibited (see *note
    SET-INHIBIT-OUTPUT-LST::), ACL2 will report rewrite and definition
    rules that may have reduced a goal to more than one subgoal.  See *note
    SPLITTER:: for how to interpret such reports.  We call such rules
    "splitter rules" for the goal that is being split.  This information
    can be useful in deciding how to eliminate large splits, for example of
    Goal into Subgoal 1000 through Subgoal 1, by disabling some splitter
    rules.  If you want to avoid the printing of such information, you can
    put the form (set-splitter-output t) in your customization file; see
    *note ACL2-CUSTOMIZATION::.
    
    Note that this command does not change the ACL2 world; it only modifies
    the state.  More precisely, it sets a state global to the indicated
    value.  (See *note STATE:: for discussion of the "global-table" of the
    state.)  When prove output is enabled (see *note
    SET-INHIBIT-OUTPUT-LST::), the value of that state global is the value
    of the form (splitter-output); otherwise the value of that form is nil.
    
    Again, see *note SPLITTER:: for the effects of turning on the reporting
    of splitter rules.
    
    
    File: acl2-doc-emacs.info,  Node: SET-STATE-OK,  Next: SET-TAU-AUTO-MODE,  Prev: SET-SPLITTER-OUTPUT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-STATE-OK    allow the use of STATE as a formal parameter
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
    In brief:  The variable symbol STATE has an unusual status in ACL2.  In
    order to use it, you either need to issue :set-state-ok t, as we
    explain below, or you need to declare it to be a stobj, as explained
    elsewhere (see *note DECLARE-STOBJS::).  Now we explain in more detail.
    
    Because the variable symbol STATE denotes the "current ACL2 state,"
    ACL2 treats the symbol very restrictively when it occurs as a formal
    parameter of a defined function.  The novice user, who is unlikely to
    be aware of the special status of that symbol, is likely to be confused
    when error messages about STATE are printed in response to the innocent
    choice of that symbol as a formal variable.  Therefore the top-level
    ACL2 loop can operate in a mode in which STATE is simply disallowed as
    a formal parameter.
    
    For a discussion of STATE, See *note STATE:: and see *note STOBJ::.
    Roughly speaking, at the top-level, the "current ACL2 state" is denoted
    by the variable symbol STATE.  Only the current state may be passed
    into a function expecting a state as an argument.  Furthermore, the
    name of the formal parameter into which the current state is passed
    must be STATE and nothing but the current state may be passed into a
    formal of that name.  Therefore, only certain access and change
    functions can use that formal -- namely those with a STATE formal --
    and if any such function produces a new state it becomes the "current
    state" and must be passed along in the STATE position thereafter.
    Thus, ACL2 requires that the state be single-threaded.  This, in turn,
    allows us to represent only one state at a time and to produce new
    states from it destructively in a von Neumaneque fashion.  The
    syntactic restrictions on the variable STATE are enforced by the
    translate mechanism (see *note TRANS:: and see *note TERM::) when terms
    are read.
    
    To prevent the novice user from seeing messages prohibiting certain
    uses of the variable symbol STATE ACL2 has a mode in which it simply
    disallows the use of that symbol as a formal parameter.  Use of the
    symbol causes a simple error message.  The system is initially in that
    mode.
    
    To get out of that mode, execute:
    
         :set-state-ok t ;;; or, (set-state-ok t)
    
    It is not recommended that you do this until you have read the
    documentation of STATE.
    
    To enter the mode in which use of state is prohibited as a formal
    parameter, do:
    
         :set-state-ok nil
    
    The mode is stored in the defaults table, See *note
    ACL2-DEFAULTS-TABLE::.  Thus, the mode may be set locally in books.
    
    
    File: acl2-doc-emacs.info,  Node: SET-TAU-AUTO-MODE,  Next: SET-TOTAL-PARALLELISM-WORK-LIMIT,  Prev: SET-STATE-OK,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-TAU-AUTO-MODE    turn on or off automatic ("greedy") generation of :tau-system rules
    
         Examples:
         (set-tau-auto-mode t)      ; select automatic (``greedy'') mode
         (set-tau-auto-mode nil)    ; select manual mode
    
    This event is equivalent to (table acl2-defaults-table :tau-auto-modep
    ), and hence is local to any books and encapsulate events in
    which it occurs; see *note ACL2-DEFAULTS-TABLE::.  See *note
    INTRODUCTION-TO-THE-TAU-SYSTEM:: for background details.
    
    The tau system gathers rules for its database in one of two ways:
    greedily or only at the explicit command of the user.  "Greedy" mode is
    officially called "automatic mode" and is the default.  The other mode
    is called "manual mode."
    
    In automatic mode, all rules processed by ACL2 are also considered for
    inclusion in the tau database: if the :corollary of some proved theorem
    happens to fit into one of the forms described in :tau-system, that
    rule is quietly added to the tau database regardless of what
    :rule-classes the user named for the :corollary.  Of course, such rules
    are also stored in the ways named by the user.  See the Design
    Philosophy section of introduction-to-the-tau-system for a discussion
    of why the tau system is greedy by default.  More details are given on
    automatic mode after we explain manual mode.
    
    To more tightly control the rules available to the tau system, the user
    may select manual mode by executing (set-tau-auto-mode nil).  In manual
    mode, the only events that create :tau-system rules are defthm events
    explicitly specifying the :tau-system rule class in the :rule-classes
    argument.  Of course, for a :tau-system rule to be created from a
    proved formula (or its specified :corollary), the formula must be of
    the appropriate shape (syntactic form). See *note TAU-SYSTEM::.  In
    manual mode, if the :tau-system rule class is specified but the formula
    is not of an appropriate form an error is signalled.  (Note: even in
    manual mode, monadic functions that are recognized as Boolean are
    classified as tau predicates; but no rules are created for them.)
    
    Returning to our discussion of automatic mode, a :tau-system rule may
    be created by any of the events below, provided the definition or
    formula proved is of an appropriate shape:
    
    (1) defun events introducing "big switch" or "mv-nth synonyms,"
    
    (2) defun events creating type-prescription rules that may be also
    represented as "signature rules" of form 1, and
    
    (3) any defthm event with a non-nil :rule-classes argument if no
    :tau-system rule is among the rule classes and the formula proved is in
     the shape of any tau-system rule.
    
    Of course, events such as defstobj and defevaluator may also add rules
    to the tau database when they execute the defun and defthm events
    implicit in their descriptions.  See *note TAU-SYSTEM:: for a
    description of the various shapes mentioned above.
    
    Note that any rule (of any rule class) created when the tau system is in
    manual mode is also created in automatic mode.  For example, if an event
    would create a :DEFINITION, :TYPE-PRESCRIPTION, FORWARD-CHAINING, or
    :REWRITE rule when the tau system is in manual mode, then the event
    will create that same rule when the tau system is in automatic mode.
    Automatic mode just means that some additional :tau-system rules may be
    created.
    
    Of course, if a defthm event explicitly specifies a :tau-system rule
    class, then even if the tau system is in automatic mode, that tau rule
    is created from the proved formula (or the specified :corollary) or
    else an error is caused.  But if the tau system is in automatic mode
    and a defthm event doesn't explicitly specify a :tau-system rule class,
    then the system quietly checks each specified :corollary -- or, in the
    absence of any :corollary, it checks the proved formula -- for whether
    it can be stored as a tau rule.  If so, then the system stores a tau
    rule, in addition to storing the specified rule.  Of course, no error
    is signalled if a proved formula of some non-:tau-system rule class
    fails to be of an appropriate shape for the tau system.
    
    In automatic mode, if the :rule-classes specified for defthm included
    several corollaries and any one of them is of class :tau-system then the
    only tau system rules created are those explicitly classed as
    :tau-system rules.  For example, suppose a defthm has one :corollary
    stored as a :rewrite rule and another :corollary stored as a :tau-system
    rule.  But suppose the :rewrite rule happens to also to fit the form of
    a :tau-system rule.  Is it added to the tau database or not?  The answer
    is no.  If you have taken the trouble to specify :tau-system corollaries
    for an event, then those corollaries are the only ones stored as tau
    sytem rules from that event.  Note that had both corollaries been
    classed as :rewrite rules (and been of acceptable :tau-system form)
    both would have also been made :tau-system rules.  This also allows you
    be in automatic mode and state a :rewrite or other non-:tau-system rule
    and prevent it from being also made a tau system rule:  just add a
    frivolous :tau-system :corollary like (booleanp (integerp x)).
    
    Recall that the use of tau rules is controlled by the rune
    (:EXECUTABLE-COUNTERPART TAU-SYSTEM).  When that rune is disabled, no
    tau rules are used in proofs.  However, the tau system continues to
    collect tau rules if the system is in automatic mode.  Thus, if and
    when the tau system is re-enabled, rules automatically generated while
    the tau system was disabled will be used as usual by the tau system.
    
    Finally, note that defthm events with :rule-classes nil do not create
    :tau-system rules even if the formula proved is of an appropriate
    shape, regardless of whether the tau system is in automatic or manual
    mode.
    
    The macro tau-status provides a convenient way to enable/disable the
    :executable-counterpart of tau-system and/or to switch between manual
    and automatic modes.  It may also be used to determine the current
    settings of those two flags.
    
    
    File: acl2-doc-emacs.info,  Node: SET-TOTAL-PARALLELISM-WORK-LIMIT,  Next: SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR,  Prev: SET-TAU-AUTO-MODE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-TOTAL-PARALLELISM-WORK-LIMIT    for ACL2(p): set thread limit for parallelism primitives
    
    This documentation topic relates to the experimental extension of ACL2
    supporting parallel execution and proof; see *note PARALLELISM::.
    While the most common use of the limit described below is in parallel
    proof (see *note PARALLEL-PROOF::), it also applies to all parallelism
    primitives (see *note PARALLEL-PROGRAMMING::) except spec-mv-let --
    though we expect that rather few programming applications will encouter
    this limit.
    
         General Forms:
         (set-total-parallelism-work-limit :none)     ; disable the limit
         (set-total-parallelism-work-limit ) ; set limit to 
    
    See *note PARALLELISM-TUTORIAL::, Section "Another Granularity Issue
    Related to Thread Limitations", for an explanation of how the host Lisp
    can run out of threads.  Also see *note
    SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR::.
    
    If the underlying runtime system (the Operating System, the host Lisp,
    etc.)  is unable to provide enough threads to finish executing the
    parallelism work given to it, the runtime system can crash in a very
    unpleasant manner (ranging from a Lisp error to completely freezing the
    machine).  To avoid this unpleasantness, ACL2(p) will attempt to avoid
    creating so much parallelism that the runtime system crashes.
    
    ACL2 initially uses a conservative estimate to limit the number of
    threads.  To tell ACL2(p) to use a different limit, call
    set-total-parallelism-work-limit with the new limit.  For example, if
    the current default limit is 10,000, then to allow 13,000 threads to
    exist, issue the following form at the top level.
    
         (set-total-parallelism-work-limit 13000)
    
    To disable this limit altogether, evaluate the following form:
    
         (set-total-parallelism-work-limit :none)
    
    The default value of total-parallelism-work-limit can be found by
    calling function default-total-parallelism-work-limit.  If the default
    value is too high for your system please notify the ACL2 maintainers
    with a limit that does work for your system, as they might then lower
    the default limit.
    
    
    File: acl2-doc-emacs.info,  Node: SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR,  Next: SET-VERIFY-GUARDS-EAGERNESS,  Prev: SET-TOTAL-PARALLELISM-WORK-LIMIT,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR    for ACL2(p): control the action taken when the thread limit is exceeded
    
    This documentation topic relates to the experimental extension of ACL2
    supporting parallel execution and proof; see *note PARALLELISM::.
    
         General Forms:
         (set-total-parallelism-work-limit-error t)   ; cause error (default)
         (set-total-parallelism-work-limit-error nil) ; disable error and
                                                      ; continue serially
    
    See *note PARALLELISM-TUTORIAL::, Section "Another Granularity Issue
    Related to Thread Limitations", for an explanation of how the host Lisp
    can run out of threads.  See *note SET-TOTAL-PARALLELISM-WORK-LIMIT::
    for further details, including an explanation of how to manage the
    limit that triggers this error.
    
    The value of state global total-parallelism-work-limit-error dictates
    what occurs when the underlying runtime system runs reaches a limit on
    the number of threads for parallel computation.  By default, when this
    limit is reached, the ACL2(p) user will receive an error and
    computation will halt.  At this point, the ACL2(p) user has the
    following options.
    
    (1) Remove the limit by evaluating the following form.
    
         (set-total-parallelism-work-limit :none)
    
    (2) Disable the error so that execution continues serially once the
    available thread resources have been exhausted.
    
         (set-total-parallelism-work-limit-error nil)
    
    (3) Increase the limit on number of threads that ACL2(p) is willing to
    create, in spite of potential risk (see *note
    SET-TOTAL-PARALLELISM-WORK-LIMIT::).  In this case, the following query
    returns the current limit.
    
         (f-get-global 'total-parallelism-work-limit)
    
    Then to increase that limit, evaluate the following form:
    
         (set-total-parallelism-work-limit )
    
    For example, suppose that the value of total-parallelism-work-limit was
    originally 10,000.  Then evaluation of the following form increases that
    limit to 13,000.
    
         (set-total-parallelism-work-limit 13000)
    
    
    File: acl2-doc-emacs.info,  Node: SET-VERIFY-GUARDS-EAGERNESS,  Next: SET-WATERFALL-PARALLELISM,  Prev: SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-VERIFY-GUARDS-EAGERNESS    the eagerness with which guard verification is tried.
    
         Example Forms:                        try guard verification?
         (set-verify-guards-eagerness 0) ; no, unless :verify-guards t
         (set-verify-guards-eagerness 1) ; yes if a guard or type is supplied
         (set-verify-guards-eagerness 2) ; yes, unless :verify-guards nil
    
    Note: This is an event!  It does not print the usual event summary but
    nevertheless changes the ACL2 logical world and is so recorded.
    
         General Form:
         (set-verify-guards-eagerness n)
    
    where n is a variable-free term that evaluates to 0, 1, or 2.  This
    macro is essentially equivalent to
    
         (table acl2-defaults-table :verify-guards-eagerness n)
    
    and hence is local to any books and encapsulate events in which it
    occurs; see *note ACL2-DEFAULTS-TABLE::.  However, unlike the above
    simple call of the table event function (see *note TABLE::), no output
    results from a set-verify-guards-eagerness event.
    
    Set-verify-guards-eagerness may be thought of as an event that merely
    sets a flag to 0, 1, or 2.  The flag is used by certain defun events to
    determine whether guard verification is tried.  The flag is irrelevant
    to those defun events in :program mode and to those defun events in
    which an explicit :verify-guards setting is provided among the xargs.
    In the former case, guard verification is not done because it can only
    be done when logical functions are being defined.  In the latter case,
    the explicit :verify-guards setting determines whether guard
    verification is tried.  So consider a :logic mode defun in which no
    :verify-guards setting is provided.  Is guard verification tried?  The
    answer depends on the eagerness setting as follows.  If the eagerness
    is 0, guard verification is not tried.  If the eagerness is 1, it is
    tried if and only if a guard is explicitly specified in the defun, in
    the following sense: there is an xargs keyword :guard or :stobjs or a
    type declaration.  If the eagerness is 2, guard verification is tried.
    
    The default behavior of the system is as though the
    :verify-guards-eagerness is 1.  The current behavior can be ascertained
    by evaluating the form (default-verify-guards-eagerness (w state)).
    
    
    File: acl2-doc-emacs.info,  Node: SET-WATERFALL-PARALLELISM,  Next: SET-WATERFALL-PARALLELISM-HACKS-ENABLED,  Prev: SET-VERIFY-GUARDS-EAGERNESS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-WATERFALL-PARALLELISM    for ACL2(p): configuring the parallel execution of the waterfall
    
    This documentation topic relates to the experimental extension of ACL2
    supporting parallel execution and proof; see *note PARALLELISM::.
    
         General Forms:
         (set-waterfall-parallelism nil)        ; never parallelize (serial execution)
         (set-waterfall-parallelism :full)      ; always parallelize
         (set-waterfall-parallelism :top-level) ; parallelize top-level subgoals
         (set-waterfall-parallelism             ; parallelize if sufficient resources
           :resource-based)                     ;   (recommended setting)
         (set-waterfall-parallelism t)          ; alias for :resource-based
         (set-waterfall-parallelism             ; parallelize if sufficient resources
           :resource-and-timing-based           ;   and suggested by prior attempts
         (set-waterfall-parallelism             ; never parallelize but use parallel
           :pseudo-parallel)                    ;   code base (a debug mode)
    
    Set-waterfall-parallelism evaluates its argument, which specifies the
    enabling or disabling of the parallel execution of ACL2's main proof
    process, the waterfall.
    
    It also sets state global waterfall-printing to an appropriate value.
    See *note SET-WATERFALL-PRINTING::.
    
    Note that not all ACL2 features are supported when
    waterfall-parallelism is set to non-nil (see *note
    UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES::).
    
    A value of t is treated the same as a value of :resource-based and is
    provided for user convenience.
    
    :Resource-based waterfall parallelism typically achieves the best
    performance in ACL2(p), while maintaining system stability, so
    :resource-based (or equivalently, t) is the recommended value.
    
    A value of nil indicates that ACL2(p) should never prove subgoals in
    parallel.
    
    A value of :full indicates that ACL2(p) should always prove independent
    subgoals in parallel.
    
    A value of :top-level indicates that ACL2(p) should prove each of the
    top-level subgoals in parallel but otherwise prove subgoals in a serial
    manner.  This mode is useful when the user knows that there are enough
    top-level subgoals, many of which take a non-trivial amount of time to
    be proved, such that proving them in parallel will result in a useful
    reduction in overall proof time.
    
    A value of :resource-based (or equivalently, t) indicates that ACL2(p)
    should use its built-in heuristics to determine whether CPU core
    resources are available for parallel execution.  Note that ACL2(p) does
    not hook into the operating system to determine the workload on the
    machine.  ACL2(p) works off the assumption that it is the only process
    using significant CPU resources, and it optimizes the amount of
    parallelism based on the number of CPU cores in the system.  (Note that
    ACL2(p) knows how to obtain the number of CPU cores from the operating
    system in CCL, but that, in SBCL and in Lispworks, a constant is used
    instead).
    
    During the first proof attempt of a given conjecture, a value of
    :resource-and-timing-based results in the same behavior as with
    :resource-based.  However, on subsequent proof attempts, the time it
    took to prove each subgoal will be considered when deciding whether to
    parallelize execution.  If a particular theorem's proof is already
    achieving satisfactory speedup via :resource-based parallelism, there
    is no reason to try this setting.  However, if the user wishes to
    experiment, the :resource-and-timing-based setting may improve
    performance.  Note that since the initial run does not have the subgoal
    proof times available, this mode will never be better than the
    :resource-based setting for non-interactive theorem proving.
    
    A value of :pseudo-parallel results in using the parallel waterfall
    code, but with serial execution.  This setting is useful for debugging
    the code base that supports parallel execution of the waterfall.  For
    example, you may wish to use this mode if you are an "ACL2 Hacker" who
    would like to see comprehensible output from tracing (see *note
    TRACE$::) the @par versions of the waterfall functions.
    
    The following remark pertains to those using the `HONS' experimental
    extension of ACL2 (see *note HONS-AND-MEMOIZATION::; in particular, see
    *note MEMOIZE::).  Since memoization is not supported when waterfall
    parallelism is enabled (see *note
    UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES::), then when
    set-waterfall-parallelism is called with a non-nil value, all memoized
    functions are unmemoized.  When set-waterfall-parallelism is again
    called with a nil value, those memoization settings are restored.
    
    Set-waterfall-parallelism is an embedded event form.  However, a call of
    this macro will not affect waterfall-parallelism when including a
    certified book that contains that call.  For such an effect, you may
    use the following make-event form.
    
         (make-event (er-progn (set-waterfall-parallelism :full)
                               (value '(value-triple nil)))
                     :check-expansion t)
    
    To enable waterfall parallelism for book certification using ACL2(p),
    see *note WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-WATERFALL-PARALLELISM-HACKS-ENABLED,  Next: SET-WATERFALL-PARALLELISM-HACKS-ENABLED!,  Prev: SET-WATERFALL-PARALLELISM,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-WATERFALL-PARALLELISM-HACKS-ENABLED    for ACL2(p): enable waterfall-parallelism hacks
    
    This documentation topic relates to the experimental extension of ACL2
    supporting parallel execution and proof; see *note PARALLELISM::.
    
         General Forms:
         (set-waterfall-parallelism-hacks-enabled t)
         (set-waterfall-parallelism-hacks-enabled nil)
    
    Some features (e.g., override-hints and clause-processors) of serial
    ACL2 are by default not available in ACL2(p) with waterfall parallelism
    enabled, because they offer a mechanism to modify state that is
    unsound.  To allow or (once again) disallow the use the these features
    in ACL2(p), call set-waterfall-parallelism-hacks-enabled with argument t
    or nil, respectively.
    
    Set-waterfall-parallelism-hacks-enabled requires the use of a trust tag
    (see *note DEFTTAG::).  One can call
    set-waterfall-parallelism-hacks-enabled!  instead, which will
    automatically install a trust tag named :waterfall-parallelism-hacks.
    
    See *note ERROR-TRIPLES-AND-PARALLELISM:: for further related
    discussion.
    
    
    File: acl2-doc-emacs.info,  Node: SET-WATERFALL-PARALLELISM-HACKS-ENABLED!,  Next: SET-WATERFALL-PRINTING,  Prev: SET-WATERFALL-PARALLELISM-HACKS-ENABLED,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-WATERFALL-PARALLELISM-HACKS-ENABLED!    for ACL2(p): enabling waterfall parallelism hacks
    
    See *note SET-WATERFALL-PARALLELISM-HACKS-ENABLED::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-WATERFALL-PRINTING,  Next: SET-WELL-FOUNDED-RELATION,  Prev: SET-WATERFALL-PARALLELISM-HACKS-ENABLED!,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-WATERFALL-PRINTING    for ACL2(p): configuring the printing that occurs within the parallelized waterfall
    
    This documentation topic relates to the experimental extension of ACL2
    supporting parallel execution and proof; see *note PARALLELISM::.
    
         General Forms:
         (set-waterfall-printing :full)    ; print everything
         (set-waterfall-printing :limited) ; print a subset that's thought to be useful
         (set-waterfall-printing :very-limited) ; print an even smaller subset
    
    Set-waterfall-printing evaluates its argument, which indicates how much
    printing should occur when executing ACL2 with the parallelized version
    of the waterfall.  It only affects the printing that occurs when
    parallelism mode is enabled for the waterfall (see *note
    SET-WATERFALL-PARALLELISM::).
    
    A value of :full is intended to print the same output as in serial mode.
    This output will be interleaved unless the waterfall-parallelism mode
    is one of nil or :pseudo-parallel.
    
    A value of :limited omits most of the output that occurs in the serial
    version of the waterfall.  Instead, the proof attempt prints key
    checkpoints (see *note ACL2P-KEY-CHECKPOINTS::).  The value of :limited
    also prints messages that indicate which subgoal is currently being
    proved, along with the wall-clock time elapsed since the theorem began
    its proof; and if state global 'waterfall-printing-when-finished has a
    non-nil value, then such a message will also be printed at the
    completion of each subgoal.  The function print-clause-id-okp may
    receive an attachment to limit such printing; see *note
    SET-PRINT-CLAUSE-IDS::.  Naturally, these subgoal numbers can appear
    out of order, because the subgoals can be proved in parallel.
    
    A value of :very-limited is treated the same as :limited, except that
    instead of printing subgoal numbers, the proof attempt prints a period
    (`.') each time it starts a new subgoal.
    
    Note that this form cannot be used at the top level of a book, or of a
    progn or encapsulate event.  Here is a workaround for use in such
    contexts; of course, you may replace :very-limited with any other legal
    argument for set-waterfall-printing.
    
         (make-event (er-progn (set-waterfall-printing :very-limited)
                               (value '(value-triple nil))))
    
    (For more about event contexts and the use of make-event, see *note
    MAKE-EVENT::, in particular the section "Restriction to Event
    Contexts.")
    
    The following form has the effect described above, except that it will
    affect waterfall-printing even when including a certified book that
    contains it.
    
         (make-event (er-progn (set-waterfall-printing :very-limited)
                               (value '(value-triple nil)))
                     :check-expansion t)
    
    Note that set-waterfall-printing is automatically called by
    set-waterfall-parallelism.
    
    To enable the printing of information when a subgoal is finished,
    assign a non-nil value to global waterfall-printing-when-finished.
    This can be accomplished by entering the following at the top level:
    
         (f-put-global 'waterfall-printing-when-finished t state)
    
    
    File: acl2-doc-emacs.info,  Node: SET-WELL-FOUNDED-RELATION,  Next: SET-WRITE-ACL2X,  Prev: SET-WATERFALL-PRINTING,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-WELL-FOUNDED-RELATION    set the default well-founded relation
    
         Examples:
         (set-well-founded-relation lex2)
    
    provided lex2 has been proved to be a well-founded relation (see *note
    WELL-FOUNDED-RELATION::).  Note: This is an event!  It does not print
    the usual event summary but nevertheless changes the ACL2 logical world
    and is so recorded.
    
         General Form:
         (set-well-founded-relation rel)
    
    where rel has been proved to be a well-founded relation on objects
    satisfying some predicate, mp; see *note WELL-FOUNDED-RELATION::.  This
    macro is equivalent to (table acl2-defaults-table
    :well-founded-relation 'rel), and hence is local to any books and
    encapsulate events in which it occurs; see *note ACL2-DEFAULTS-TABLE::.
    
    This event sets the default well-founded relation to be that imposed on
    mp-measures by the relation rel.  Subsequently, if a recursively
    defined function is submitted to defun with no explicitly given
    :well-founded-relation argument, defun uses the default relation, rel,
    and the associated domain predicate mp used in its well-foundedness
    theorem.  That is, the termination conditions generated will require
    proving that the measure used by the defun is an mp-measure and that in
    every recursive call the measure of the arguments decreases according
    to rel.
    
    
    File: acl2-doc-emacs.info,  Node: SET-WRITE-ACL2X,  Next: TAU-STATUS,  Prev: SET-WELL-FOUNDED-RELATION,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    SET-WRITE-ACL2X    cause certify-book to write out a .acl2x file
    
         Example Forms:
         (set-write-acl2x nil state)
         (set-write-acl2x t state)
         (set-write-acl2x '(nil) state) ; same as just above, but allow inclusion of
                                        ; uncertified books during certify-book
         (set-write-acl2x '(t) state)
         (set-write-acl2x '(include-book-with-locals) state)
    
         General Form:
         (set-write-acl2x val state)
    
    where val evaluates to t, nil, or a one-element list whose element is a
    legal value for the global 'ld-skip-proofsp; see *note
    LD-SKIP-PROOFSP::.  The value returned is an error triple, which in the
    non-error case is (mv nil v state), where v is the value of val and
    state is the result of updating the input state by assigning state
    global 'write-acl2x the value v.
    
    The command (set-write-acl2x val state) assigns the value of val to the
    state global variable 'write-acl2x, affecting whether or not
    certify-book writes out a file with extension acl2x, called a ".acl2x
    file" and pronounced "dot-acl2x file".  Such a file is read or written
    by certify-book when it is supplied with keyword argument :acl2x t.  By
    default, such a call of certify-book reads a .acl2x file; but if the
    value of state global variable 'write-acl2x is not nil, then
    certify-book writes a .acl2x file (in which case it is illegal to
    specify a non-nil value for certify-book keyword argument :pcert).
    Consider for example (certify-book "foo" 0 nil :acl2x t).  By default,
    this command reads file foo.acl2x, which supplies replacements for some
    forms in foo.lisp, as described later below.  But if the value of state
    global 'write-acl2x is not nil, then instead, this certify-book command
    writes such a file foo.acl2x.
    
    Before we discuss the function of .acl2x files, we first explain more
    about how a non-nil value of state global 'write-acl2x affects the
    behavior of a command (certify-book ... :acl2x t ...).  A significant
    effect on the behavior is that after processing events in the given
    book, ACL2 writes out a .acl2x file and then returns, skipping the other
    subsequent actions typically performed by certify-book: a
    local-incompatibility check, writing of a certificate file, and
    possibly compilation.  Another effect is that proofs may be skipped when
    processing events assuming that the the certify-book command does not
    explicitly specify :skip-proofs-okp nil, as we now explain.  A non-nil
    value of 'write-acl2x should either be t or a one-element list (x),
    where x is a legal value for the state global 'ld-skip-proofsp (see
    *note LD-SKIP-PROOFSP::).  In both cases, certify-book will process
    events to write out a .acl2x file as described above.  But in the
    latter (list) case, event processing will take place according to the
    value of x: in particular, proofs will be skipped when x is not nil,
    and if moreover x is the symbol include-book-with-locals, then only one
    pass will be made through each encapsulate form.  A third effect of a
    non-nil value of 'write-acl2x, which is restricted to the list case, is
    that include-book events encountered during event processing are
    allowed to succeed on uncertified books, something that is prohibited
    during most calls of certify-book.
    
    When certify-book is used to write out a .acl2x file, there is
    typically a subsequent run of certify-book that reads that file.
    Consider how this can work with a book foo.lisp.  In the first call of
    certify-book, a file foo.acl2x is written that contains all make-event
    expansions, but foo.cert is not written.  In the second call of
    certify-book, no make-event expansion typically takes place, because
    foo.acl2x supplies the expansions.  The command (set-write-acl2x t
    state) should be evaluated before the first certification (though
    another legal non-nil value may be used in place of t), setting the
    value of state global 'write-acl2x to t, to enable writing of
    foo.acl2x; and the command (set-write-acl2x nil state) may be evaluated
    before the second run (though this is not necessary in a fresh ACL2
    session) in order to complete the certification (writing out foo.cert)
    using foo.acl2x to supply the make-event expansions.
    
    When Certify-book is supplied with keyword argument :acl2x t it will
    read or write the book's .acl2x file; when supplied with :acl2x nil, it
    will not read or write that .acl2x file.  The value of :acl2x is nil by
    default.  The interaction of certify-book with the corresponding .acl2x
    file is as follows.
    
         o If :acl2x is t, then:
           - If set-write-acl2x has been (most recently) called with a
             value of t for its first argument, then ACL2 writes the
             corresponding .acl2x file.
           - If set-write-acl2x has been (most recently) called with a
             value of nil for its first argument, or not called at all,
             then ACL2 insists on a corresponding .acl2x file that is at
             least as recent as the corresponding .lisp file, causing an
             error otherwise.
         o If :acl2x is nil, then:
           - If set-write-acl2x has been (most recently) called with a
             value t for its first argument, or if argument :ttagsx
             is supplied, then an error occurs.
           - If the .acl2x file exists, then regardless of whether or how
             set-write-acl2x has been called, ACL2 ignores the .acl2x
             file but issues a warning about it.
    
    Suppose you use the two-runs approach: first write a .acl2x file, then
    certify using (reading) that .acl2x file.  Then with scripts such as
    makefiles, then you may wish to provide a single certify-book command
    to use for both runs.  For that purpose, certify-book supports the
    keyword argument :ttagsx.  If this argument is supplied and write-acl2x
    is true, then this argument is treated as the :ttags argument,
    overriding a :ttags argument if present.  That is, for the two runs,
    :ttagsx may be used to specify the trust tags used in the first
    certification while :ttags specifies the trust tags, if any (else
    :ttags may be omitted), used in the second certification.  Note: If the
    argument :ttagsx is not supplied, then its value defaults to the
    (explicit or default) value of the :ttags argument.
    
    The built-in ACL2 Makefile support automatically generates suitable
    dependencies if you create a .acl2 file with a certify-book call
    matching the following regular expression, case-independent:
    
           (certify-book[^;]*:acl2x t
    
    For an example .acl2 file with a certify-book call matching the above
    pattern, see community books file
    books/make-event/double-cert-test-1.acl2.
    
    Note that include-book is generally not affected by set-write-acl2x,
    other than through the indirect effect on certify-book.  More
    precisely: All expansions are stored in the certificate file, so when
    include-book is applied to a certified book, the .acl2x file is not
    consulted.
    
    An example of how to put this all together may be found in community
    book books/make-event/double-cert-test-1.lisp.  There, we see the
    following form.
    
         (make-event
          (progn (defttag :my-ttag)
                 (progn! (let ((val (sys-call "pwd" nil)))
                           (value (list 'defun 'foo () val))))))
    
    Imagine that in place of the binding computed using sys-call, which by
    the way requires a trust tag, is some computation of your choice (such
    as reading forms from a file) that is used to construct your own event,
    in place of the defun event constructed above.  The Makefile in that
    directory contains the following added dependency, so that file
    double-cert-test-1.acl2x will be created:
    
         double-cert-test-1.cert: double-cert-test-1.acl2x
    
    There is also the file double-cert-test-1.acl2 in that directory, which
    contains a single form as follows.
    
         (certify-book "double-cert-test-1" ? t :ttagsx :all :ttags nil)
    
    Thus, a call of `make' first creates file double-cert-test-1.acl2x,
    which uses the above :ttagsx argument in order to support the use of
    defttag during make-event expansion.  Then, `make' goes on to cause a
    second certification in which no trust tags are involved.  As a result,
    the parent book double-cert-test.lisp is ultimately certified without
    requiring any trust tags.
    
    The discussion above is probably sufficient for most users of the
    two-run approach it describes.  We conclude with further details for
    those who want more information.  Those who wish to see a yet
    lower-level explanation of how all this works are invited to read the
    comment in the ACL2 source code entitled "Essay on .acl2x Files (Double
    Certification).
    
    Consider the .acl2x file produced by the first run as described above.
    It contains a single expression, which is an association list whose
    keys are all positive integers, which occur in increasing order.  When
    the .acl2x file is present and at least as recent as the corresponding
    .lisp file, then for a subsequent certify-book with argument :acl2x t
    and the (default) value of nil for state global 'write-acl2x, that
    association list will be applied to the top-level events in the book, as
    follows.  Suppose the entry (n . ev) belongs to the association list in
    the .acl2x file.  Then n is a positive integer, and the nth top-level
    event in the book -- where the 0th event is the initial in-package form
    -- will be replaced by ev.  In practice, ev is the make-event expansion
    created during certification for the nth top-level event in the book;
    and this will always be the case if the .acl2x file is created by
    certify-book after execution of the form (set-write-acl2x t state).
    However, you are welcome to associate indices manually with any events
    you wish into the alist stored in the .acl2x file.
    
    Note: Also see the community book make-event/acl2x-help.lisp for a
    useful utility that can be used to skip proofs during the writing of
    .acl2x files.
    
    
    File: acl2-doc-emacs.info,  Node: TAU-STATUS,  Next: TERM-TABLE,  Prev: SET-WRITE-ACL2X,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    TAU-STATUS    query or set tau system status
    
         Examples:
         (tau-status)
         (tau-status :system t)
         (tau-status :auto-mode nil)
         (tau-status :system t :auto-mode nil)
    
         General Form:
         (tau-status :system a :auto-mode b)
    
    where a and b are Booleans.  Both keyword arguments are optional and
    they may be presented in either order.  Value a controls whether the
    tau-system is used during subsequent proofs.  Value b controls whether
    tau system rules are added automatically ("greedily") when rules of
    other rule-classes are added.  If no arguments are supplied, this is
    not an event and just returns an error-triple (see *note
    ERROR-TRIPLES::) indicating the current settings.  See *note
    INTRODUCTION-TO-THE-TAU-SYSTEM:: for background details.
    
    The two flags are independent.  For example, the tau system may be
    disabled in proof attempts even though it is automatically (and
    silently) extending its database as rules of other classes are added.
    
    Flag (a) is actually toggled by enabling or disabling the
    :executable-counterpart of tau-system.  Flag (b) is toggled with the
    function set-tau-auto-mode, which manipulates the acl2-defaults-table.
    
    This macro expands into zero, one, or two events, as required by the
    supplied values of flags a and b.
    
    If no arguments are supplied the form is not an event and simply
    returns (as an error triple (mv nil ans state); see *note
    ERROR-TRIPLES::) the current settings of the two flags.  For example:
    
         ACL2 !>(tau-system)
          ((:SYSTEM NIL) (:AUTO-MODE T))
    
    intended to be self-explanatory.
    
    
    File: acl2-doc-emacs.info,  Node: TERM-TABLE,  Next: UNTRANS-TABLE,  Prev: TAU-STATUS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    TERM-TABLE    a table used to validate meta rules
    
         Example:
         (table term-table t '((binary-+ x y) '3 'nil (car x)))
    
    See *note TABLE:: for a general discussion of tables and the table
    event used to manipulate tables.
    
    The "term-table" is used at the time a meta rule is checked for
    syntactic correctness.  Each proposed metafunction is run on each term
    in this table, and the result in each case is checked to make sure that
    it is a termp in the current world.  In each case where this test
    fails, a warning is printed.
    
    Whenever a metafunction is run in support of the application of a meta
    rule, the result must be a term in the current world.  When the result
    is not a term, a hard error arises.  The term-table is simply a means
    for providing feedback to the user at the time a meta rule is
    submitted, warning of the definite possibility that such a hard error
    will occur at some point in the future.
    
    The key used in term-table is arbitrary.  The top-most value is always
    the one that is used; it is the entire list of terms to be considered.
    Each must be a termp in the current ACL2 world.
    
    
    File: acl2-doc-emacs.info,  Node: UNTRANS-TABLE,  Next: USER-DEFINED-FUNCTIONS-TABLE,  Prev: TERM-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    UNTRANS-TABLE    associates a function symbol with a macro for printing user-level terms
    
         Examples:
         ACL2 !>(untrans-table (w state))
         ((BINARY-+ + . T)
          (BINARY-* * . T)
          (BINARY-APPEND APPEND . T)
          (BINARY-LOGAND LOGAND . T)
          (BINARY-LOGIOR LOGIOR . T)
          (BINARY-LOGXOR LOGXOR . T)
          (BINARY-LOGEQV LOGEQV . T)
          (BINARY-POR POR . T)
          (BINARY-PAND PAND . T))
    
    See *note TABLE:: for a general discussion of tables.
    
    See *note ADD-MACRO-FN:: for a more general discussion of this table
    and for a way to associate a macro name with a function name in theory
    events.
    
    
    File: acl2-doc-emacs.info,  Node: USER-DEFINED-FUNCTIONS-TABLE,  Next: VERIFY-GUARDS-EAGERNESS,  Prev: UNTRANS-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    USER-DEFINED-FUNCTIONS-TABLE    an advanced table used to replace certain system functions
    
         Examples:
         (table user-defined-functions-table 'untranslate-preprocess 'my-preprocess)
         (table user-defined-functions-table 'untranslate 'my-untranslate)
    
    This feature should perhaps only be used by advanced users who have a
    thorough understanding of the system functions being replaced.  There
    are currently two ways a user can affect the way ACL2 prints terms.
    
    The first example associates the user-defined function symbol
    my-preprocess with untranslate-preprocess.  As a result, when ACL2
    prints a term, say during a proof, using its so-called "untranslate"
    process the first thing it does is to call my-preprocess on two
    arguments: that term and the current ACL2 logical world.  If the call
    produces a non-nil result, then that result is passed to the untranslate
    process.
    
    The second example associates the user-defined function symbol
    my-untranslate with the built-in function symbol untranslate.  As a
    result, the code for my-untranslate will be run whenever the untranslate
    process is run.  The formals of the two functions must agree and must
    not contain any stobj names.  Note that these overrides fail to occur
    upon guard violations and some other evaluation errors.
    
    The untranslate-preprocess approach may suffice for most cases in which
    a user wants to modify the way output is produced by the theorem
    prover.  We present an example immediately below, but see community book
    books/misc/untranslate-patterns.lisp for a more elaborate example.  If
    the untranslate-preprocess approach does not seem sufficient for your
    purposes, you are invited to look at community book
    books/misc/rtl-untranslate.lisp for an example of user-defined
    untranslate (i.e., following the second example displayed above).
    
    Suppose you have a large constant that you would prefer not to see in
    proofs.  For example, you may have submitted the following definition
    (but imagine a much larger constant, say, a list of length 1,000,000).
    
         (defconst *a* '(a b c d))
    
    If you submit the following (silly) theorem
    
         (thm (equal (cons x *a*) (car (cons yyy zzz))))
    
    then you will see the following output:
    
         (EQUAL (CONS X '(A B C D)) YYY).
    
    If *a* had represented a much larger structure, we would wish we could
    see the following instead.
    
         (EQUAL (CONS X *A*) YYY)
    
    That can be accomplished as follows.  First we make the following
    definition.
    
         (defun my-preprocess (term wrld)
           (declare (ignore wrld))
           (if (equal term (list 'quote *a*))
               '*a*
             nil))
    
    Now we submit the following table event.
    
         (table user-defined-functions-table
                'untranslate-preprocess
                'my-preprocess)
    
    This will install my-preprocess as a preprocessor before the normal
    untranslation routine is applied to printing a term.  When the
    untranslation routine encounters the constant (QUOTE (A B C D)), it
    will replace it with *a*, and the usual untranlation routine will print
    this as *A*.
    
    
    File: acl2-doc-emacs.info,  Node: VERIFY-GUARDS-EAGERNESS,  Next: WITH-GUARD-CHECKING,  Prev: USER-DEFINED-FUNCTIONS-TABLE,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    VERIFY-GUARDS-EAGERNESS    See *note SET-VERIFY-GUARDS-EAGERNESS::.
    
    
    File: acl2-doc-emacs.info,  Node: WITH-GUARD-CHECKING,  Next: WITH-OUTPUT,  Prev: VERIFY-GUARDS-EAGERNESS,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    WITH-GUARD-CHECKING    suppressing or enable guard-checking for a form
    
         Example:
    
         ; Turn off all guard-checking for the indicated calls of append and car:
         (with-guard-checking :none
                              (car (append 3 4)))
    
         General Form:
         (with-guard-checking val form)
    
    where val evaluates to a legal guard-checking value (see *note
    SET-GUARD-CHECKING::, or evaluate *guard-checking-values* to see the
    list of such values), and form is a form to be evaluated as though we
    had first executed (set-guard-checking val).  Of course, this
    gaurd-checking setting is active only during evaluation of form, and is
    ignored once evaluation passes into raw Lisp functions (see *note
    GUARDS-AND-EVALUATION::).
    
    acl2-sources/doc/EMACS/acl2-doc-emacs.info-150000664002132200015000000067332112222333542017734 0ustar  kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from
    acl2-doc-emacs.texinfo.
    
    This is documentation for ACL2 Version 6.3
    Copyright (C) 2013  University of Texas at Austin
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of Version 2 of the GNU General Public License as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Written by:  Matt Kaufmann and J Strother Moore
    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.
    
    INFO-DIR-SECTION Math
    START-INFO-DIR-ENTRY
    * acl2: (acl2-doc-emacs). Applicative Common Lisp
    END-INFO-DIR-ENTRY
    
    
    File: acl2-doc-emacs.info,  Node: WITH-OUTPUT,  Prev: WITH-GUARD-CHECKING,  Up: SWITCHES-PARAMETERS-AND-MODES
    
    WITH-OUTPUT    suppressing or turning on specified output for an event
    
         Examples:
    
         ; Turn off all output during evaluation of the indicated thm form.
         (with-output
          :off :all
          :gag-mode nil
          (thm (equal (app (app x y) z) (app x (app y z)))))
    
         ; Prove the indicated theorem with the event summary turned off and
         ; using the :goals setting for gag-mode.
         (with-output
            :off summary
            :gag-mode :goals
            (defthm app-assoc (equal (app (app x y) z) (app x (app y z)))))
    
         ; Same effect as just above:
         (with-output
            :on summary
            :summary nil
            :gag-mode :goals
            (defthm app-assoc (equal (app (app x y) z) (app x (app y z)))))
    
         ; Turn on only the indicated parts of the summary.
         (with-output
            :on summary
            :summary (time rules)
            :gag-mode :goals  ; use gag-mode, with goal names printed
            (defthm app-assoc (equal (app (app x y) z) (app x (app y z)))))
    
         ; Same as specifying :off :all, but showing all output types:
         (with-output
          :off (error warning warning! observation prove proof-checker event expansion
                      summary proof-tree)
          :gag-mode nil
          (thm (equal (app (app x y) z) (app x (app y z)))))
    
         ; Same as above, but :stack :push says to save the current
         ; inhibit-output-lst, which can be restored in a subsidiary with-output call
         ; that specifies :stack :pop.
         (with-output
          :stack :push
          :off :all
          :gag-mode nil
          (thm (equal (app (app x y) z) (app x (app y z)))))
    
         General Form:
         (with-output :key1 val1 ... :keyk valk form)
    
    where each :keyi is either :off, :on, :stack, :summary, or :gag-mode;
    form evaluates to an error triple (see *note ERROR-TRIPLES::); and vali
    is as follows.  If :keyi is :off or :on, then vali can be :all, and
    otherwise is a symbol or non-empty list of symbols representing output
    types that can be inhibited; see *note SET-INHIBIT-OUTPUT-LST::.  If
    :keyi is :gag-mode, then vali is one of the legal values for
    :set-gag-mode.  If :keyi is :summary, then vali is either :all or a
    true-list of symbols each of which belongs to the list *summary-types*.
    Otherwise :keyi is :stack, in which case :vali is :push or :pop; for
    now assume that :stack is not specified (we'll return to it below).
    The result of evaluating the General Form above is to evaluate form,
    but in an environment where output occurs as follows.  If :on :all is
    specified, then every output type is turned on except as inhibited by
    :off; else if :off :all is specified, then every output type is
    inhibited except as specified by :on; and otherwise, the
    currently-inhibited output types are reduced as specified by :on and
    then extended as specified by :off.  But if :gag-mode is specified,
    then before modifying how output is inhibited, gag-mode is set for the
    evaluation of form as specified by the value of :gag-mode; see *note
    SET-GAG-MODE::.  If summary is among the output types that are turned
    on (not inhibited), then if :summary is specified, the only parts of
    the summary to be printed will be those specified by the value of
    :summary.  The correspondence should be clear, except perhaps that
    header refers to the line containing only the word Summary, and value
    refers to the value of the form printed during evaluation of sequences
    of events as for progn and encapsulate.
    
    Note that the handling of the :stack argument pays no attention to the
    :summary argument.
    
    Note: When the scope of with-output is exited, then all modifications
    are undone, reverting gag-mode and the state of output inhibition to
    those which were present before the with-output call was entered.
    
    The :stack keyword's effect is illustrated by the following example,
    where "(encapsulate nil)" may replaced by "(progn" without any change
    to the output that is printed.
    
         (with-output
          :stack :push :off :all
          (encapsulate ()
            (defun f1 (x) x)
            (with-output :stack :pop (defun f2 (x) x))
            (defun f3 (x) x)
            (with-output :stack :pop :off warning (in-theory nil))
            (defun f4 (x) x)))
    
    The outer with-output call saves the current output settings (as may
    have been modified by earlier calls of set-inhibit-output-lst), by
    pushing them onto a stack, and then turns off all output.  Each inner
    with-output call temporarily pops that stack, restoring the starting
    output settings, until it completes and undoes the effects of that pop.
    Unless event output was inhibited at the top level (see *note
    SET-INHIBIT-OUTPUT-LST::), the following output is shown:
    
         Since F2 is non-recursive, its admission is trivial.  We observe that
         the type of F2 is described by the theorem (EQUAL (F2 X) X).
    
    And then, if summary output was not inhibited at the top level, we get
    the rest of this output:
    
         Summary
         Form:  ( DEFUN F2 ...)
         Rules: NIL
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
         Summary
         Form:  ( IN-THEORY NIL)
         Rules: NIL
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Note that the use of :off warning supresses a "Theory" warning for the
    (in-theory nil) event, and that in no case will output be printed for
    definitions of f1, f3, or f4, or for the encapsulate event itself.
    
    The following more detailed explanation of :stack is intended only for
    advanced users.  After :gag-mode is handled (if present) but before :on
    or :off is handled, the value of :stack is handled as follows.  If the
    value is :push, then state global inhibit-output-lst-stack is modified
    by pushing the value of state global inhibit-output-lst onto the value
    of state global inhibit-output-lst-stack, which is nil at the top
    level.  If the value is :pop, then state global
    inhibit-output-lst-stack is modified only if non-nil, in which case its
    top element is popped and becomes the value of of state global
    inhibit-output-lst.
    
    Warning: With-output has no effect in raw Lisp, and hence is disallowed
    in function bodies.  However, you can probably get the effect you want
    as illustrated below, where  must return an error triple (mv erp
    val state); see *note LD:: and see *note ERROR-TRIPLES::.
    
         Examples avoiding with-output, for use in function definitions:
    
         ; Inhibit all output:
         (state-global-let*
          ((inhibit-output-lst *valid-output-names*))
          )
    
         ; Inhibit all warning output:
         (state-global-let*
          ((inhibit-output-lst
            (union-eq (f-get-global 'inhibit-output-lst state)
                      '(warning warning!))))
          )
    
    Note that with-output is allowed in books.  See *note
    EMBEDDED-EVENT-FORM::.
    
    
    File: acl2-doc-emacs.info,  Node: THEORIES,  Next: TRACE,  Prev: SWITCHES-PARAMETERS-AND-MODES,  Up: Top
    
    THEORIES    sets of runes to enable/disable in concert
    
         Example: '((:definition app) ; or (:d app)
                    (:executable-counterpart app)
                    (:i app)
                    rv
                    (rv)
                    assoc-of-app)
    
    See:
    
    * Menu:
    
    * ACTIVE-RUNEP:: check that a rune exists and is enabled
    
    * CURRENT-THEORY:: currently enabled rules as of logical name
    
    * DISABLE:: deletes names from current theory
    
    * E/D:: enable/disable rules
    
    * ENABLE:: adds names to current theory
    
    * EXECUTABLE-COUNTERPART-THEORY:: executable counterpart rules as of logical name
    
    * FUNCTION-THEORY:: function symbol rules as of logical name
    
    * GROUND-ZERO:: enabled rules in the startup theory
    
    * INCOMPATIBLE:: declaring that two rules should not both be enabled
    
    * INTERSECTION-THEORIES:: intersect two theories
    
    * MINIMAL-THEORY:: a minimal theory to enable
    
    * RULE-NAMES:: How rules are named.
    
    * RUNE:: a rule name
    
    * SET-DIFFERENCE-THEORIES:: difference of two theories
    
    * THEORIES-AND-PRIMITIVES:: warnings from disabling certain built-in functions
    
    * THEORY:: retrieve named theory
    
    * THEORY-FUNCTIONS:: functions for obtaining or producing theories
    
    * UNION-THEORIES:: union two theories
    
    * UNIVERSAL-THEORY:: all rules as of logical name
    
    
    Related topics other than immediate subtopics:
    * DEFTHEORY:: define a theory (to enable or disable a set of rules)
    
    * DEFTHEORY-STATIC:: define a `static' theory (to enable or disable a set of rules)
    
    * IN-THEORY:: designate ``current'' theory (enabling its rules)
    
    A theory is a list of "runic designators" as described below.  Each
    runic designator denotes a set of "runes" (see *note RUNE::) and by
    unioning together the runes denoted by each member of a theory we
    define the set of runes corresponding to a theory.  Theories are used
    to control which rules are "enabled," i.e., available for automatic
    application by the theorem prover.  There is always a "current" theory.
    A rule is enabled precisely if its rune is an element of the set of
    runes corresponding to the current theory.  At the top-level, the
    current theory is the theory selected by the most recent in-theory
    event, extended with the rule names introduced since then.  Inside the
    theorem prover, the :in-theory hint (see *note HINTS::) can be used to
    select a particular theory as current during the proof attempt for a
    particular goal.
    
    Theories are generally constructed by "theory expressions."  Formally, a
    theory expression is any term, containing at most the single free
    variable world, that when evaluated with world bound to the current ACL2
    world (see *note WORLD::) produces a theory.  ACL2 provides various
    functions for the convenient construction and manipulation of theories.
    These are called "theory functions"(see *note THEORY-FUNCTIONS::).  For
    example, the theory function union-theories takes two theories and
    produces their union.  The theory function universal-theory returns the
    theory containing all known rule names as of the introduction of a
    given logical name.  But a theory expression can contain constants,
    e.g.,
    
         '(len (len) (:rewrite car-cons) car-cdr-elim)
    
    and user-defined functions.  The only important criterion is that a
    theory expression mention no variable freely except world and evaluate
    to a theory.
    
    More often than not, theory expressions typed by the user do not
    mention the variable world.  This is because user-typed theory
    expressions are generally composed of applications of ACL2's theory
    functions.  These "functions" are actually macros that expand into
    terms in which world is used freely and appropriately.  Thus, the
    technical definition of "theory expression" should not mislead you into
    thinking that interestng theory expressions must mention world; they
    probably do and you just didn't know it!
    
    One aspect of this arrangement is that theory expressions cannot
    generally be evaluated at the top-level of ACL2, because world is not
    bound.  To see the value of a theory expression, expr, at the
    top-level, type
    
         ACL2 !>(LET ((WORLD (W STATE))) expr).
    
    However, because the built-in theories are quite long, you may be sorry
    you printed the value of a theory expression!
    
    A theory is a true list of runic designators and to each theory there
    corresponds a set of runes, obtained by unioning together the sets of
    runes denoted by each runic designator.  For example, the theory
    constant
    
            '(len (len) (:e nth) (:rewrite car-cons) car-cdr-elim)
    
    corresponds to the set of runes
    
            {(:definition len)
             (:induction len)
             (:executable-counterpart len)
             (:executable-counterpart nth)
             (:elim car-cdr-elim)
             (:rewrite car-cons)} .
    
    Observe that the theory contains five elements but its runic
    correspondent contains six.  That is because runic designators can
    denote sets of several runes, as is the case for the first designator,
    len.  If the above theory were selected as current then the six rules
    named in its runic counterpart would be enabled and all other rules
    would be disabled.
    
    We now precisely define the runic designators and the set of runes
    denoted by each.  When we refer below to the "macro-aliases dereference
    of" a symbol, symb, we mean the (function) symbol corresponding symb in
    the macro-aliases-table if there is such a symbol, else symb itself;
    see *note MACRO-ALIASES-TABLE::.  For example, the macro-aliases
    dereference of append is binary-append, and the macro-aliases
    dereference of nth is nth.
    
         o A rune is a runic designator and denotes the singleton set
         containing that rune.
    
         o Suppose that symb is a symbol and symb' is the macro-aliases
         dereference of symb, where symb' is a function symbol introduced
         with a defun (or defuns) event.  Then symb is a runic designator
         and denotes the set containing the runes (:definition symb') and
         (:induction symb'), omitting the latter if no such induction rune
         exists (presumably because the definition of symb' is not singly
         recursive).
    
         o Suppose that symb is a symbol and symb' is the macro-aliases
         dereference of symb, where symb' is a function symbol introduced
         with a defun (or defuns) event.  Then (symb) is a runic designator
         and denotes the singleton set containing the rune
         (:executable-counterpart symb').
    
         o If symb is the name of a defthm (or defaxiom) event that
         introduced at least one rule, then symb is a runic designator and
         denotes the set of the names of all rules introduced by the named
         event.
    
         o If str is the string naming some defpkg event and symb is the
         symbol returned by (intern str "ACL2"), then symb is a runic
         designator and denotes the singleton set containing (:rewrite
         symb), which is the name of the rule stating the conditions under
         which the symbol-package-name of (intern x str) is str.
    
         o If symb is the name of a deftheory event, then symb is a runic
         designator and denotes the runic theory corresponding to symb.
    
         o Finally, suppose that symb is a symbol and symb' is the
         macro-aliases dereference of symb.  Then (:KWD symb . rest) is a
         runic designator if (:KWD' symb' . rest) is a rune, where :KWD is
         one of :d, :e, :i, or :t, and correspondingly :KWD' is
         :definition, :executable-counterpart, :induction, or
         :type-prescription, respectively.  In this case, (:KWD symb . rest)
         denotes the runic theory corresponding to the rune (:KWD' symb' .
         rest).
    
    Note that including a function name, e.g., len, in the current theory
    enables that function but does not enable the executable counterpart.
    Similarly, including (len) or (:e len) enables the executable
    counterpart but not the symbolic definition.  And including the name of
    a proved lemma enables all of the rules added by the event.  Of course,
    one can include explicitly the runes naming the rules in question and
    so can avoid entirely the use of non-runic elements in theories.
    
    Because a rune is a runic designator denoting the set containing that
    rune, a list of runes is a theory and denotes itself.  We call such
    theories "runic theories."  To every theory there corresponds a runic
    theory obtained by unioning together the sets denoted by each
    designator in the theory.  When a theory is selected as "current" it is
    actually its runic correspondent that is effectively used.  That is, a
    rune is enabled iff it is a member of the runic correspondent of the
    current theory.  The value of a theory defined with deftheory is the
    runic correspondent of the theory computed by the defining theory
    expression.  The theory manipulation functions, e.g., union-theories,
    actually convert their theory arguments to their runic correspondents
    before performing the required set operation.  The manipulation
    functions always return runic theories.  Thus, it is sometimes
    convenient to think of (non-runic) theories as merely abbreviations for
    their runic correspondents, abbreviations which are "expanded" at the
    first opportunity by theory manipulation functions and the "theory
    consumer" functions such as in-theory and deftheory.
    
    
    File: acl2-doc-emacs.info,  Node: ACTIVE-RUNEP,  Next: CURRENT-THEORY,  Prev: THEORIES,  Up: THEORIES
    
    ACTIVE-RUNEP    check that a rune exists and is enabled
    
         Example:
         (active-runep '(:rewrite left-to-right))
    
         General Form:
         (active-runep rune)
    
    where rune has the shape of a rune.  This macro expands to an
    expression using the variables ens and state, and returns non-nil when
    the given rune exists and is enabled (according to the given "enabled
    structure," ens, and the current logical world of the given state).
    See *note THEORY-INVARIANT:: for how this macro can be of use.
    
    
    File: acl2-doc-emacs.info,  Node: CURRENT-THEORY,  Next: DISABLE,  Prev: ACTIVE-RUNEP,  Up: THEORIES
    
    CURRENT-THEORY    currently enabled rules as of logical name
    
         Examples:
         (current-theory :here)
         (current-theory 'lemma3)
    
    See *note LOGICAL-NAME::.
    
         General Form:
         (current-theory logical-name)
    
    Returns the current theory as it existed immediately after the
    introduction of logical-name provided it is evaluated in an environment
    in which the variable symbol WORLD is bound to the current ACL2 logical
    world, (w state).  Thus,
    
         ACL2 !>(current-theory :here)
    
    will cause an (unbound variable) error while
    
         ACL2 !>(let ((world (w state))) (current-theory :here))
    
    will return the current theory in world.
    
    See *note THEORIES:: and see *note LOGICAL-NAME:: for a discussion of
    theories in general and why the commonly used "theory functions" such
    as current-theory are really macros that expand into terms involving
    the variable world.
    
    The theory returned by current-theory is in fact the theory selected by
    the in-theory event most recently preceding logical name, extended by
    the rules introduced up through logical-name.
    
    You may experience a fencepost problem in deciding which logical name
    to use.  Deflabel can always be used to mark unambiguously for future
    reference a particular point in the development of your theory.  The
    order of events in the vicinity of an encapsulate is confusing.  See
    *note ENCAPSULATE::.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: DISABLE,  Next: E/D,  Prev: CURRENT-THEORY,  Up: THEORIES
    
    DISABLE    deletes names from current theory
    
         Example:
         (disable fact (fact) associativity-of-app)
    
         General Form:
         (disable name1 name2 ... namek)
    
    where each namei is a runic designator; see *note THEORIES::.  The
    result is the theory that contains all the names in the current theory
    except those listed.  Note that this is merely a function that returns
    a theory.  The result is generally a very long list of runes and you
    will probably regret printing it.
    
    The standard way to "disable" a fixed set of names, is as follows; see
    *note HINTS:: and see *note IN-THEORY::.
    
         :in-theory (disable name1 name2 ... namek)    ; in a hint
         (in-theory (disable name1 name2 ... namek))   ; as an event
         (local ; often desirable, to avoid exporting from the current context
          (in-theory (disable name1 name2 ... namek)))
    
    Note that all the names are implicitly quoted.  If you wish to disable
    a computed list of names, lst, use the theory expression
    (set-difference-theories (current-theory :here) lst).
    
    
    File: acl2-doc-emacs.info,  Node: E/D,  Next: ENABLE,  Prev: DISABLE,  Up: THEORIES
    
    E/D    enable/disable rules
    
    The macro e/d creates theory expressions for use in in-theory hints and
    events.  It provides a convenient way to enable and disable
    simultaneously, without having to write arcane theory expressions.
    
         Examples:
         (e/d (lemma1 lemma2))          ; equivalent to (enable lemma1 lemma2)
         (e/d () (lemma))               ; equivalent to (disable lemma)
         (e/d (lemma1) (lemma2 lemma3)) ; Enable lemma1 then disable lemma2, lemma3.
         (e/d () (lemma1) (lemma2))     ; Disable lemma1 then enable lemma2.
    
         General Form:
         (e/d enables-0 disables-0 ... enables-n disables-n)
    
    where each enables-i and disables-i is a list of runic designators; see
    *note THEORIES::, see *note ENABLE::, and see *note DISABLE::.
    
    The e/d macro takes any number of lists suitable for the enable and
    disable macros, and creates a theory that is equal to (current-theory
    :here) after executing the following commands.
    
    (in-theory (enable . enables-0)) (in-theory (disable . disables-0))
    [etc.]  (in-theory (enable . enables-n)) (in-theory (disable .
    disables-n))
    
    
    File: acl2-doc-emacs.info,  Node: ENABLE,  Next: EXECUTABLE-COUNTERPART-THEORY,  Prev: E/D,  Up: THEORIES
    
    ENABLE    adds names to current theory
    
         Example:
         (enable fact (fact) associativity-of-app)
    
         General Form:
         (enable name1 name2 ... namek)
    
    where each namei is a runic designator; see *note THEORIES::.  The
    result is the theory that contains all the names in the current theory
    plus those listed.  Note that this is merely a function that returns a
    theory.  The result is generally a very long list of runes and you will
    probably regret printing it.
    
    The standard way to "enable" a fixed set of names, is as follows; see
    *note HINTS:: and see *note IN-THEORY::.
    
         :in-theory (enable name1 name2 ... namek)  ; in a hint
         (in-theory (enable name1 name2 ... namek)) ; as an event
         (local ; often desirable, to avoid exporting from the current context
          (in-theory (enable name1 name2 ... namek)))
    
    Note that all the names are implicitly quoted.  If you wish to enable a
    computed list of names, lst, use the theory expression (union-theories
    (current-theory :here) lst).
    
    
    File: acl2-doc-emacs.info,  Node: EXECUTABLE-COUNTERPART-THEORY,  Next: FUNCTION-THEORY,  Prev: ENABLE,  Up: THEORIES
    
    EXECUTABLE-COUNTERPART-THEORY    executable counterpart rules as of logical name
    
         Examples:
         (executable-counterpart-theory :here)
         (executable-counterpart-theory 'lemma3)
    
    See *note LOGICAL-NAME::.
    
         General Form:
         (executable-counterpart-theory logical-name)
    
    Returns the theory containing all the :executable-counterpart runes,
    whether enabled or not, that existed immediately after logical-name was
    introduced.  See the documentation for theories, logical-name,
    executable-counterpart and function-theory.
    
    You may experience a fencepost problem in deciding which logical name
    to use.  Deflabel can always be used to mark unambiguously for future
    reference a particular point in the development of your theory.  The
    order of events in the vicinity of an encapsulate is confusing.  See
    *note ENCAPSULATE::.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: FUNCTION-THEORY,  Next: GROUND-ZERO,  Prev: EXECUTABLE-COUNTERPART-THEORY,  Up: THEORIES
    
    FUNCTION-THEORY    function symbol rules as of logical name
    
         Examples:
         (function-theory :here)
         (function-theory 'lemma3)
    
    See *note LOGICAL-NAME::.
    
         General Form:
         (function-theory logical-name)
    
    Returns the theory containing all the :definition runes, whether
    enabled or not, that existed immediately after logical-name was
    introduced.  See the documentation for theories, logical-name and
    executable-counterpart-theory.
    
    You may experience a fencepost problem in deciding which logical name
    to use.  Deflabel can always be used to mark unambiguously for future
    reference a particular point in the development of your theory.  The
    order of events in the vicinity of an encapsulate is confusing.  See
    *note ENCAPSULATE::.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: GROUND-ZERO,  Next: INCOMPATIBLE,  Prev: FUNCTION-THEORY,  Up: THEORIES
    
    GROUND-ZERO    enabled rules in the startup theory
    
    ACL2 concludes its initialization (boot-strapping) procedure by
    defining the theory ground-zero; see *note THEORIES::.  In fact, this
    theory is just the theory defined by (current-theory :here) at the
    conclusion of initialization; see *note CURRENT-THEORY::.
    
    Note that by evaluating the event
    
         (in-theory (current-theory 'ground-zero))
    
    you can restore the current theory to its value at the time you started
    up ACL2.
    
    
    File: acl2-doc-emacs.info,  Node: INCOMPATIBLE,  Next: INTERSECTION-THEORIES,  Prev: GROUND-ZERO,  Up: THEORIES
    
    INCOMPATIBLE    declaring that two rules should not both be enabled
    
         Example:
         (theory-invariant (incompatible (:rewrite left-to-right)
                                         (:rewrite right-to-left)))
    
         General Form:
         (incompatible rune1 rune2)
    
    where rune1 and rune2 are two specific runes.  The arguments are not
    evaluated.  Invariant is just a macro that expands into a term that
    checks that not both runes are enabled.  See *note THEORY-INVARIANT::.
    
    
    File: acl2-doc-emacs.info,  Node: INTERSECTION-THEORIES,  Next: MINIMAL-THEORY,  Prev: INCOMPATIBLE,  Up: THEORIES
    
    INTERSECTION-THEORIES    intersect two theories
    
         Example:
         (intersection-theories (current-theory :here)
                                (theory 'arith-patch))
    
         General Form:
         (intersection-theories th1 th2)
    
    where th1 and th2 are theories (see *note THEORIES::).  To each of the
    arguments there corresponds a runic theory.  This function returns the
    intersection of those two runic theories, represented as a list and
    ordered chronologically.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: MINIMAL-THEORY,  Next: RULE-NAMES,  Prev: INTERSECTION-THEORIES,  Up: THEORIES
    
    MINIMAL-THEORY    a minimal theory to enable
    
    This theory (see *note THEORIES::) enables only a few built-in
    functions and executable counterparts.  It can be useful when you want
    to formulate lemmas that rather immediately imply the theorem to be
    proved, by way of a :use hint (see *note HINTS::), for example as
    follows.
    
         :use (lemma-1 lemma-2 lemma-3)
         :in-theory (union-theories '(f1 f2) (theory 'minimal-theory))
    
    In this example, we expect the current goal to follow from lemmas
    lemma-1, lemma-2, and lemma-3 together with rules f1 and f2 and some
    obvious facts about built-in functions (such as the definition of
    implies and the :executable-counterpart of car).  The :in-theory hint
    above is intended to speed up the proof by turning off all inessential
    rules.
    
    
    File: acl2-doc-emacs.info,  Node: RULE-NAMES,  Next: RUNE,  Prev: MINIMAL-THEORY,  Up: THEORIES
    
    RULE-NAMES    How rules are named.
    
         Examples:
         (:rewrite assoc-of-app)
         (:linear delta-aref . 2)
         (:definition length)
         (:executable-counterpart length)
    
    See *note RUNE::.
    
    
    File: acl2-doc-emacs.info,  Node: RUNE,  Next: SET-DIFFERENCE-THEORIES,  Prev: RULE-NAMES,  Up: THEORIES
    
    RUNE    a rule name
    
         Examples:
         (:rewrite assoc-of-app)
         (:linear delta-aref . 2)
         (:definition length)
         (:executable-counterpart length)
    
    Note: This topic discusses a basic notion of "rule name", or "rune" for
    short.  Users often use abbrevitions for runes; for example, a theory
    expression (DISABLE APPEND) abbreviates the following set of runes:
    {(:DEFINITION BINARY-APPEND), (:INDUCTION BINARY-APPEND)}.  See *note
    THEORIES:: for a discussion of so-called "runic designators", which
    include expressions like APPEND (as above) as well as (APPEND) (for the
    executable-counterpart of BINARY-APPEND.  Runic designators can also be
    abbreviations including (:d APPEND), (:e APPEND), (:i APPEND), and (:t
    APPEND), which designate the definition, executable-counterpart,
    induction, and type-prescription rules for BINARY-APPEND.  For a
    complete description of runic designators, see *note THEORIES::; we
    return now to the more basic notion of a rune.
    
    Background: The theorem prover is driven from a database of rules.  The
    most common rules are :rewrite rules, which cause the simplifier to
    replace one term with another.  Events introduce rules into the
    database.  For example, a defun event may introduce runes for
    symbolically replacing a function call by its instantiated body, for
    evaluating the function on constants, for determining the type of a
    call of the function, and for the induction scheme introduced upon
    defining the function.  Defthm may introduce several rules, one for
    each of the :rule-classes specified (where one rule class is specified
    if :rule-classes is omitted, namely, :rewrite).
    
    Every rule in the system has a name.  Each name is a structured object
    called a "rune," which is short for "rule name".  Runes are always of
    the form (:token symbol . x), where :token is some keyword symbol
    indicating what kind of rule is named, symbol is the event name that
    created the rule (and is called the "base symbol" of the rune), and x
    is either nil or a natural number that makes the rule name distinct
    from that of rules generated by other events or by other :rule-classes
    within the same event.
    
    For example, an event of the form
    
         (defthm name thm
           :rule-classes ((:REWRITE :COROLLARY term1)
                          (:REWRITE :COROLLARY term2)
                          (:ELIM    :COROLLARY term3)))
    
    typically creates three rules, each with a unique rune.  The runes are
    
         (:REWRITE name . 1), (:REWRITE name . 2), and (:ELIM name).
    
    However, a given formula may create more than one rule, and all rules
    generated by the same :corollary formula will share the same rune.
    Consider the following example.
    
         (defthm my-thm
           (and (equal (foo (bar x)) x)
                (equal (bar (foo x)) x)))
    
    This is treated identically to the following.
    
         (defthm my-thm
           (and (equal (foo (bar x)) x)
                (equal (bar (foo x)) x))
           :rule-classes ((:rewrite
                           :corollary
                           (and (equal (foo (bar x)) x)
                                (equal (bar (foo x)) x)))))
    
    In either case, two rules are created: one rewriting (foo (bar x)) to
    x, and one rewriting (bar (foo x)) to x.  However, only a single rune
    is created, (:REWRITE MY-THM), because there is only one rule class.
    But now consider the following example.
    
         (defthm my-thm2
           (and (equal (foo (bar x)) x)
                (equal (bar (foo x)) x))
           :rule-classes ((:rewrite
                           :corollary
                           (and (equal (foo (bar x)) x)
                                (equal (bar (foo x)) x)))
                          (:rewrite
                           :corollary
                           (and (equal (foo (bar (foo x))) (foo x))
                                (equal (bar (foo (bar x))) (bar x))))))
    
    This time there are four rules created.  The first two rules are as
    before, and are assigned the rune (:REWRITE MY-THM . 1).  The other two
    rules are similarly generated for the second :corollary, and are
    assigned the rune (:REWRITE MY-THM . 2).
    
    The function corollary will return the corollary term associated with a
    given rune in a given world.  Example:
    
         (corollary '(:TYPE-PRESCRIPTION DIGIT-TO-CHAR) (w state))
    
    However, the preferred way to see the corollary term associated with a
    rune or a name is to use :pf; see *note PF::.
    
    The defun event creates as many as four rules.  (:definition fn) is the
    rune given to the equality axiom defining the function, fn.
    (:executable-counterpart fn) is the rune given to the rule for computing
    fn on known arguments.  A type prescription rule may be created under
    the name (:type-prescription fn), and an induction rule may be created
    under the name (:induction fn).
    
    Runes may be individually enabled and disabled, according to whether
    they are included in the current theory.  See *note THEORIES::.  Thus,
    it is permitted to disable (:elim name), say, while enabling the other
    rules derived from name.  Similarly, (:definition fn) may be disabled
    while (:executable-counterpart fn) and the type prescriptions for fn
    are enabled.
    
    Associated with most runes is the formula justifying the rule named.
    This is called the "corollary formula" of the rune and may be obtained
    via the function corollary, which takes as its argument a rune and a
    property list world.  Also see *note PF::.  The corollary formula for
    (:rewrite name . 1) after the defthm event above is term1.  The
    corollary formulas for (:definition fn) and (:executable-counterpart
    fn) are always identical: the defining axiom.  Some runes, e.g.,
    (:definition car), do not have corollary formulas.  Corollary returns
    nil on such runes.  In any case, the corollary formula of a rune, when
    it is non-nil, is a theorem and may be used in the :use and :by hints.
    
    Note: The system has many built in rules that, for regularity, ought to
    have names but don't because they can never be disabled.  One such rule
    is that implemented by the linear arithmetic package.  Because many of
    our subroutines are required by their calling conventions to return the
    justifying rune, we have invented the notion of "fake runes." Fake
    runes always have the base symbol nil, use a keyword token that
    includes the phrase "fake-rune", and are always enabled.  For example,
    (:fake-rune-for-linear nil) is a fake rune.  Occasionally the system
    will print a fake rune where a rune is expected.  For example, when the
    linear arithmetic fake rune is reported among the rules used in a
    proof, it is an indication that the linear arithmetic package was used.
    However, fake runes are not allowed in theories, they cannot be enabled
    or disabled, and they do not have associated corollary formulas.  In
    short, despite the fact that the user may sometimes see fake runes
    printed, they should never be typed.
    
    
    File: acl2-doc-emacs.info,  Node: SET-DIFFERENCE-THEORIES,  Next: THEORIES-AND-PRIMITIVES,  Prev: RUNE,  Up: THEORIES
    
    SET-DIFFERENCE-THEORIES    difference of two theories
    
         Example:
         (set-difference-theories (current-theory :here)
                                  '(fact (fact)))
    
         General Form:
         (set-difference-theories th1 th2)
    
    where th1 and th2 are theories (see *note THEORIES::).  To each of the
    arguments there corresponds a runic theory.  This function returns the
    set-difference of those two runic theories, represented as a list and
    ordered chronologically.  That is, a rune is in the result iff it is in
    the first runic theory but not in the second.
    
    The standard way to "disable" a theory, lst, is: (in-theory
    (set-difference-theories (current-theory :here) lst)).
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: THEORIES-AND-PRIMITIVES,  Next: THEORY,  Prev: SET-DIFFERENCE-THEORIES,  Up: THEORIES
    
    THEORIES-AND-PRIMITIVES    warnings from disabling certain built-in functions
    
    When you disable the definition or executable-counterpart of a built-in
    function, you may see a warning, for example as follows.
    
           ACL2 !>(in-theory (disable mv-nth))
    
           ACL2 Warning [Theory] in ( IN-THEORY (DISABLE ...)):  Although the
           theory expression (DISABLE MV-NTH) disables the :DEFINITION rule for
           MV-NTH, some expansions involving this function may still occur.  See
           :DOC theories-and-primitives.
    
    This warning can be eliminated by turning off all theory warnings (see
    *note SET-INHIBIT-WARNINGS::) or simply by evaluating the following
    form.
    
           (assign verbose-theory-warning nil)
    
    But before you eliminate such warnings, you may wish to read the
    following to understand their significance.
    
    First consider the following example, evaluated after the in-theory
    event displayed above.
    
           ACL2 !>(thm (equal (mv-nth 2 (list a b c d e)) c))
    
           Q.E.D.
    
           Summary
           Form:  ( THM ...)
           Rules: ((:DEFINITION MV-NTH)
                   (:FAKE-RUNE-FOR-TYPE-SET NIL))
           Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
           Prover steps counted:  19
    
           Proof succeeded.
           ACL2 !>
    
    Note that even though the definition of mv-nth had been disabled,
    nevertheless its definition rule was used in proving this theorem.  It
    is as though mv-nth had not been been disabled after all!  The warning
    is intended to indicate that expansion of mv-nth calls may be made by
    the theorem prover even when mv-nth is disabled.  Indeed, the prover
    has special-purpose code for simplifying certain mv-nth calls.
    
    A similar issue can arise for executable-counterpart rules, as the
    following log illustrates.
    
           ACL2 !>(in-theory (disable (:executable-counterpart symbolp)))
    
           ACL2 Warning [Theory] in ( IN-THEORY (DISABLE ...)):  Although the
           theory expression (DISABLE (:EXECUTABLE-COUNTERPART SYMBOLP)) disables
           the :EXECUTABLE-COUNTERPART rule for SYMBOLP, some calls involving
           this function may still be made.  See :DOC theories-and-primitives.
    
    
           Summary
           Form:  ( IN-THEORY (DISABLE ...))
           Rules: NIL
           Warnings:  Theory
           Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
            2921
           ACL2 !>(thm (symbolp 'a))
    
           Q.E.D.
    
           Summary
           Form:  ( THM ...)
           Rules: NIL
           Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
           Proof succeeded.
           ACL2 !>
    
    In general, ACL2 warns when in-theory events or hints leave you in a
    theory where a rule for a built-in function is disabled but may be
    applied in some cases nonetheless, because of special-purpose prover
    code for handling calls of that function.  The built-in function
    symbols with such definition rules or executable-counterpart rules are
    those in the following two lists, respectively.
    
           ACL2 !>*definition-minimal-theory*
           (MV-NTH IFF NOT
                   IMPLIES EQ ATOM EQL = /= NULL ENDP ZEROP
                   SYNP PLUSP MINUSP LISTP RETURN-LAST
                   MV-LIST THE-CHECK WORMHOLE-EVAL
                   FORCE CASE-SPLIT DOUBLE-REWRITE)
           ACL2 !>*built-in-executable-counterparts*
           (ACL2-NUMBERP BINARY-* BINARY-+ UNARY-- UNARY-/
                         < CAR CDR CHAR-CODE CHARACTERP CODE-CHAR
                         COMPLEX COMPLEX-RATIONALP COERCE
                         CONS CONSP DENOMINATOR EQUAL IF IMAGPART
                         INTEGERP INTERN-IN-PACKAGE-OF-SYMBOL
                         NUMERATOR PKG-WITNESS PKG-IMPORTS
                         RATIONALP REALPART STRINGP SYMBOL-NAME
                         SYMBOL-PACKAGE-NAME SYMBOLP NOT)
           ACL2 !>
    
    
    File: acl2-doc-emacs.info,  Node: THEORY,  Next: THEORY-FUNCTIONS,  Prev: THEORIES-AND-PRIMITIVES,  Up: THEORIES
    
    THEORY    retrieve named theory
    
         Example:
         (theory 'ground-zero)
    
    In the example above, the theory returned is the one in force when ACL2
    is started up (see *note GROUND-ZERO::).
    
         General Form:
         (theory name)
    
    where name is the name of a previously executed deftheory event
    (otherwise a hard error occurs).  Returns the named theory.  See *note
    THEORIES::.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: THEORY-FUNCTIONS,  Next: UNION-THEORIES,  Prev: THEORY,  Up: THEORIES
    
    THEORY-FUNCTIONS    functions for obtaining or producing theories
    
         Example Calls of Theory Functions:
         (universal-theory :here)
         (union-theories th1 th2)
         (set-difference-theories th1 th2)
    
    The theory functions are documented individually:
    
    * Menu:
    
    
    Related topics other than immediate subtopics:
    * CURRENT-THEORY:: currently enabled rules as of logical name
    
    * DISABLE:: deletes names from current theory
    
    * E/D:: enable/disable rules
    
    * ENABLE:: adds names to current theory
    
    * EXECUTABLE-COUNTERPART-THEORY:: executable counterpart rules as of logical name
    
    * FUNCTION-THEORY:: function symbol rules as of logical name
    
    * GROUND-ZERO:: enabled rules in the startup theory
    
    * INTERSECTION-THEORIES:: intersect two theories
    
    * MINIMAL-THEORY:: a minimal theory to enable
    
    * SET-DIFFERENCE-THEORIES:: difference of two theories
    
    * THEORY:: retrieve named theory
    
    * UNION-THEORIES:: union two theories
    
    * UNIVERSAL-THEORY:: all rules as of logical name
    
    The functions (actually, macros) mentioned above are convenient ways to
    produce theories.  (See *note THEORIES::.) Some, like universal-theory,
    take a logical name (see *note LOGICAL-NAME::) as an argument and
    return the relevant theory as of the time that name was introduced.
    Others, like union-theories, take two theories and produce a new one.
    See *note REDUNDANT-EVENTS:: for a caution about the use of logical
    names in theory expressions.
    
    Theory expressions are generally composed of applications of theory
    functions.  Formally, theory expressions are expressions that involve,
    at most, the free variable world and that when evaluated with world
    bound to the current ACL2 world (see *note WORLD::) return theories.
    The "theory functions" are actually macros that expand into forms that
    involve the free variable world.  Thus, for example (universal-theory
    :here) actually expands to (universal-theory-fn :here world) and when
    that form is evaluated with world bound to the current ACL2 world,
    universal-theory-fn scans the ACL2 property lists and computes the
    current universal theory.  Because the theory functions all implicitly
    use world, the variable does not generally appear in anything the user
    types.
    
    
    File: acl2-doc-emacs.info,  Node: UNION-THEORIES,  Next: UNIVERSAL-THEORY,  Prev: THEORY-FUNCTIONS,  Up: THEORIES
    
    UNION-THEORIES    union two theories
    
         Example:
         (union-theories (current-theory 'lemma3)
                         (theory 'arith-patch))
    
         General Form:
         (union-theories th1 th2)
    
    where th1 and th2 are theories (see *note THEORIES::).  To each of the
    arguments there corresponds a runic theory.  This function returns the
    union of those two runic theories, represented as a list and ordered
    chronologically.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    
    File: acl2-doc-emacs.info,  Node: UNIVERSAL-THEORY,  Prev: UNION-THEORIES,  Up: THEORIES
    
    UNIVERSAL-THEORY    all rules as of logical name
    
         Examples:
         (universal-theory :here)
         (universal-theory 'lemma3)
    
    See *note LOGICAL-NAME::.
    
         General Form:
         (universal-theory logical-name)
    
    Returns the theory consisting of all the runes that existed immediately
    after logical-name was introduced.  See *note THEORIES:: and see *note
    LOGICAL-NAME::.  The theory includes logical-name itself (if there is a
    rule by that name).  (Note that since some events do not introduce
    rules (e.g., defmacro, defconst or defthm with :rule-classes nil), the
    universal-theory does not necessarily include a rune for every event
    name.)  The universal-theory is very long and you will probably regret
    printing it.
    
    You may experience a fencepost problem in deciding which logical-name
    to use.  Deflabel can always be used to mark unambiguously for future
    reference a particular point in the development of your theory.  This
    is convenient because deflabel does not introduce any rules and hence
    it doesn't matter if you count it as being in the interval or not.  The
    order of events in the vicinity of an encapsulate is confusing.  See
    *note ENCAPSULATE::.
    
    This "function" is actually a macro that expands to a term mentioning
    the single free variable world.  When theory expressions are evaluated
    by in-theory or the :in-theory hint, world is bound to the current ACL2
    world.
    
    Also see *note CURRENT-THEORY::.  Current-theory is much more commonly
    used than universal-theory.  The former includes only the enabled runes
    as of the given logical-name, which is probably what you want, while
    the latter includes disabled ones as well.
    
    
    File: acl2-doc-emacs.info,  Node: TRACE,  Prev: THEORIES,  Up: Top
    
    TRACE    tracing functions in ACL2
    
    ACL2 provides a trace utility, trace$, with corresponding reverse
    operation untrace$.  These can be used without any dependence on the
    underlying Lisp utility, and are the tracing utilities of choice in
    ACL2; see *note TRACE$:: and see *note UNTRACE$::.
    
    However, for advanced users we note that the underlying host Lisp may
    also provide a trace utility, trace, and corresponding untrace.
    Moreover, these have been modified in the case that the host Lisp is
    GCL, Allegro CL, or CCL (OpenMCL), to provide limited support for
    :entry, :exit, and perhaps :cond keywords, to hide certain large data
    structures (world, enabled structure, rewrite constant), and to trace
    executable counterparts.  See source files *-trace.lisp.  For the above
    Lisps, you can invoke the original trace and untrace by invoking
    old-trace and old-untrace, respectively, in raw Lisp rather than in the
    normal ACL2 loop.
    
    * Menu:
    
    * BREAK-ON-ERROR:: break when encountering a hard or soft error caused by ACL2
    
    * CLOSE-TRACE-FILE:: stop redirecting trace output to a file
    
    * OPEN-TRACE-FILE:: redirect trace output to a file
    
    * SET-TRACE-EVISC-TUPLE:: set the trace evisc tuple
    
    * TRACE!:: trace the indicated functions after creating an active trust tag
    
    * TRACE$:: trace function evaluations
    
    * UNTRACE$:: untrace functions
    
    * WET:: evaluate a form and print subsequent error trace
    
    
    Related topics other than immediate subtopics:
    * TIME-TRACKER:: display time spent during specified evaluation
    
    
    File: acl2-doc-emacs.info,  Node: BREAK-ON-ERROR,  Next: CLOSE-TRACE-FILE,  Prev: TRACE,  Up: TRACE
    
    BREAK-ON-ERROR    break when encountering a hard or soft error caused by ACL2
    
         General forms:
         (break-on-error t)    ; installs a trace causing a continuable error (break)
                               ;   when an error is invoked by ACL2.
         (break-on-error)      ; same as above
         (break-on-error :all) ; same as above, but even when inside the prover
         (break-on-error nil)  ; uninstall any above trace
    
    (Break-on-error) generates a suitable trace of error functions.
    Evaluate (trace$) after (break-on-error) if you want to see the
    specific trace forms (which you can modify and then submit directly to
    trace$, if you wish).  This trace should cause entry to the Lisp
    debugger whenever ACL2 calls its error routines, except for certain
    errors when inside the theorem prover, and also at those times if
    option :all is supplied.
    
    NOTE: For technical reasons, you may see some error messages more than
    once.
    
    Finally, note that you are welcome to define your own version of
    break-on-error by modifying a copy of the source definition (search for
    "(defmacro break-on-error" in ACL2 source file other-events.lisp).
    Please feel free to send your version of break-on-error to the ACL2
    implementors, for possible inclusion into ACL2.
    
    Break-on-error is implmented using ACL2 trace$.  See *note TRACE!:: if
    you want an explanation of the "TTAG NOTE" that is printed.
    
    The argument, if supplied, is evaluated and must evaluate to t, nil, or
    :all.
    
    Also see *note SET-DEBUGGER-ENABLE:: for how to get raw-Lisp backtrace
    information when an error occurs as a result of break-on-error, or even
    of a raw Lisp error, by calling set-debugger-enable with argument :bt,
    :bt-break, or :break-bt.  Note that for ACL2 errors (as opposed to raw
    Lisp errors), i.e. errors affected by break-on-error, all three of
    those keyword values are treated equivalently (and, all are ignored for
    non-ANSI GCL; see *note SET-DEBUGGER-ENABLE::).
    
    
    File: acl2-doc-emacs.info,  Node: CLOSE-TRACE-FILE,  Next: OPEN-TRACE-FILE,  Prev: BREAK-ON-ERROR,  Up: TRACE
    
    CLOSE-TRACE-FILE    stop redirecting trace output to a file
    
         General Form:
         (close-trace-file) ; trace output is no longer redirected to a file
    
    Output from trace$ normally goes to the screen, or more precisely,
    standard-co.  It can be redirected to a file; see *note
    OPEN-TRACE-FILE::.  Use close-trace-file to redirect trace output to
    standard-co.
    
    
    File: acl2-doc-emacs.info,  Node: OPEN-TRACE-FILE,  Next: SET-TRACE-EVISC-TUPLE,  Prev: CLOSE-TRACE-FILE,  Up: TRACE
    
    OPEN-TRACE-FILE    redirect trace output to a file
    
         Example:
         (open-trace-file "foo") ; trace output will go to file foo
    
         General Form:
         (open-trace-file filename) ; trace output will go to file filename
    
    Output from trace$ normally goes to the screen, i.e., standard-co.  But
    it can be redirected to a file as shown above.  See *note
    CLOSE-TRACE-FILE:: for how to send trace output back to the screen.
    
    
    File: acl2-doc-emacs.info,  Node: SET-TRACE-EVISC-TUPLE,  Next: TRACE!,  Prev: OPEN-TRACE-FILE,  Up: TRACE
    
    SET-TRACE-EVISC-TUPLE    set the trace evisc tuple
    
    A trace evisc-tuple, which is set by this utility, provides a means to
    restrict printing during tracing.  See *note EVISC-TUPLE:: for an
    introduction to evisc-tuples; also see *note SET-EVISC-TUPLE:: and see
    *note SET-IPRINT::.
    
    By default the ACL2 trace mechanism, trace$, automatically deals with
    stobjs, the logical world, and certain other large structures.  See
    *note TRACE$::, in particular the documentation of trace$ option :hide.
    However, even with that default behavior you may want to restrict what
    is printed according to the print-level and print-length of an
    evisc-tuple; see *note EVISC-TUPLE::.
    
         Examples:
    
         ; Set trace evisc tuple to a standard value, using current Lisp *print-level*
         ; and *print-length* variables:
         (set-trace-evisc-tuple t state)
    
         ; Set trace evisc tuple back to its default:
         (set-trace-evisc-tuple nil state)
    
         ; Set trace evisc tuple to restrict print-level to 3 and print-length to 4,
         ; while hiding the logical world and suitably printing stobjs even if trace$
         ; option ``:hide nil'' is used.  (Note: calling trace-evisceration-alist
         ; directly requires removing this function as `untouchable', which requires a
         ; trust tag; see *note REMOVE-UNTOUCHABLE::.)
         (set-trace-evisc-tuple
          (evisc-tuple 3 4 (trace-evisceration-alist state) nil)
          state)
    
         General Forms:
    
         (set-trace-evisc-tuple nil state) ; trace evisc-tuple set to standard value
         (set-trace-evisc-tuple   t state) ; trace evisc-tuple set to hide the logical
                                           ;   world and deal with stobjs even when
                                           ;   trace$ option ``:hide nil'' is supplied
         (set-trace-evisc-tuple evisc-tuple state)
                                           ; tracing set to use indicated evisc-tuple
    
    See *note TRACE$:: for a discussion of ACL2 tracing.  The evisc-tuple
    used by that trace utility is the one last installed by
    set-trace-evisc-tuple (or by set-evisc-tuple for the trace-evisc-tuple)
    -- initially to the default of nil -- unless overriden by trace option
    :evisc-tuple.
    
    *Remark*.  If you use value t, then ACL2 will ensure that the logical
    world and stobjs are kept up-to-date in the trace evisc-tuple.
    
    
    File: acl2-doc-emacs.info,  Node: TRACE!,  Next: TRACE$,  Prev: SET-TRACE-EVISC-TUPLE,  Up: TRACE
    
    TRACE!    trace the indicated functions after creating an active trust tag
    
         Example:
         (trace! (fact :native t :entry *foo*))
    
         General Form:
         (trace! spec1 ... specn)
    
    where the fni are suitable arguments to trace$.
    
    Trace! is a version of trace$ that avoids the need for an
    already-active trust tag (or "ttag"; see *note DEFTTAG::), as explained
    below.  See *note TRACE$:: for when a trust tag can be necessary.
    
    See *note UNTRACE$:: for how to undo the effect of trace!.
    
    The evaluation of a trace! form causes temporary creation of an active
    trust tag, :trace!, followed by the corresponding trace$ form.  The
    trust tag will disappear when the call to trace! completes.  Even though
    trace! will remove its temporary ttag, it will still print a "TTAG
    NOTE", which indicates that the session is suspect.  See *note DEFTTAG::
    and see *note TTAGS-SEEN:: for further remarks on this issue.
    
    Because of the active trust tag, it is possible to do things with trace!
    that are useful but without logical justification.  Below is an example
    of how to use trace! to cause a function call to change state, even
    though the function does not take state as a parameter.
    
         ACL2 !>(defun fact (n)
                  (declare (xargs :guard (natp n) :verify-guards nil))
                  (if (zp n)
                      1
                    (* n (fact (1- n)))))
    
         The admission of FACT is trivial, using the relation O< (which is known
         to be well-founded on the domain recognized by O-P) and the measure
         (ACL2-COUNT N).  We observe that the type of FACT is described by the
         theorem (AND (INTEGERP (FACT N)) (< 0 (FACT N))).  We used the :compound-
         recognizer rule ZP-COMPOUND-RECOGNIZER and primitive type reasoning.
    
         Summary
         Form:  ( DEFUN FACT ...)
         Rules: ((:COMPOUND-RECOGNIZER ZP-COMPOUND-RECOGNIZER)
                 (:FAKE-RUNE-FOR-TYPE-SET NIL))
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
          FACT
         ACL2 !>(defun update-foo (n value state)
                  (declare (xargs :stobjs state :verify-guards nil))
                  (assign foo (cons (cons n value) (@ foo))))
    
         Since UPDATE-FOO is non-recursive, its admission is trivial.  We observe
         that the type of UPDATE-FOO is described by the theorem
         (AND (CONSP (UPDATE-FOO N VALUE STATE))
              (TRUE-LISTP (UPDATE-FOO N VALUE STATE))).
         We used primitive type reasoning.
    
         (UPDATE-FOO * * STATE) => (MV * * STATE).
    
         Summary
         Form:  ( DEFUN UPDATE-FOO ...)
         Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
          UPDATE-FOO
         ACL2 !>(trace! (fact :exit (update-foo n value state)))
    
         TTAG NOTE: Adding ttag :TRACE! from the top level loop.
          ((FACT :EXIT (UPDATE-FOO N VALUE STATE)))
         ACL2 !>(assign foo nil)
          NIL
         ACL2 !>(fact 7)
         1> (ACL2_*1*_ACL2::FACT 7)
           2> (ACL2_*1*_ACL2::FACT 6)
             3> (ACL2_*1*_ACL2::FACT 5)
               4> (ACL2_*1*_ACL2::FACT 4)
                 5> (ACL2_*1*_ACL2::FACT 3)
                   6> (ACL2_*1*_ACL2::FACT 2)
                     7> (ACL2_*1*_ACL2::FACT 1)
                       8> (ACL2_*1*_ACL2::FACT 0)
                       <8 NIL
                     <7 NIL
                   <6 NIL
                 <5 NIL
               <4 NIL
             <3 NIL
           <2 NIL
         <1 NIL
         5040
         ACL2 !>(@ foo)
         ((7 . 5040)
          (6 . 720)
          (5 . 120)
          (4 . 24)
          (3 . 6)
          (2 . 2)
          (1 . 1)
          (0 . 1))
         ACL2 !>(verify-guards fact)
    
         Computing the guard conjecture for FACT....
    
         The guard conjecture for FACT is trivial to prove, given the :compound-
         recognizer rules NATP-COMPOUND-RECOGNIZER and ZP-COMPOUND-RECOGNIZER,
         primitive type reasoning and the :type-prescription rule FACT.  FACT
         is compliant with Common Lisp.
    
         Summary
         Form:  ( VERIFY-GUARDS FACT)
         Rules: ((:COMPOUND-RECOGNIZER NATP-COMPOUND-RECOGNIZER)
                 (:COMPOUND-RECOGNIZER ZP-COMPOUND-RECOGNIZER)
                 (:FAKE-RUNE-FOR-TYPE-SET NIL)
                 (:TYPE-PRESCRIPTION FACT))
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
          FACT
         ACL2 !>(assign foo nil)
          NIL
         ACL2 !>(fact 7)
         1> (ACL2_*1*_ACL2::FACT 7)
           2> (FACT 7)
             3> (FACT 6)
               4> (FACT 5)
                 5> (FACT 4)
                   6> (FACT 3)
                     7> (FACT 2)
                       8> (FACT 1)
                         9> (FACT 0)
                         <9 NIL
                       <8 NIL
                     <7 NIL
                   <6 NIL
                 <5 NIL
               <4 NIL
             <3 NIL
           <2 NIL
         <1 NIL
         5040
         ACL2 !>(@ foo)
         ((7 . 5040)
          (7 . 5040)
          (6 . 720)
          (5 . 120)
          (4 . 24)
          (3 . 6)
          (2 . 2)
          (1 . 1)
          (0 . 1))
         ACL2 !>(trace! (fact :exit (progn (update-foo n value state)
                                           (cons traced-fn values))))
    
         TTAG NOTE: Adding ttag :TRACE! from the top level loop.
          ((FACT :EXIT (PROGN (UPDATE-FOO N VALUE STATE)
                              (CONS TRACED-FN VALUES))))
         ACL2 !>(assign foo nil)
          NIL
         ACL2 !>(fact 7)
         1> (ACL2_*1*_ACL2::FACT 7)
           2> (FACT 7)
             3> (FACT 6)
               4> (FACT 5)
                 5> (FACT 4)
                   6> (FACT 3)
                     7> (FACT 2)
                       8> (FACT 1)
                         9> (FACT 0)
                         <9 (FACT 1)
                       <8 (FACT 1)
                     <7 (FACT 2)
                   <6 (FACT 6)
                 <5 (FACT 24)
               <4 (FACT 120)
             <3 (FACT 720)
           <2 (FACT 5040)
         <1 (ACL2_*1*_ACL2::FACT 5040)
         5040
         ACL2 !>(@ foo)
         ((7 . 5040)
          (7 . 5040)
          (6 . 720)
          (5 . 120)
          (4 . 24)
          (3 . 6)
          (2 . 2)
          (1 . 1)
          (0 . 1))
         ACL2 !>
    
    Finally, we remark that the use trace! can cause errors in situations
    where tracing is automatically suspended and re-introduced.  This is
    likely to be a rare occurrence, but consider the following example.
    
         (trace! (lexorder :native t :multiplicity 1))
         (certify-book "foo" 0 t)
    
    If the certify-book causes compilation, you may see an error such as the
    following.
    
    
         ACL2 Error in (CERTIFY-BOOK "foo" ...):  The keyword :NATIVE cannot
         be used in a trace spec unless there is an active trust tag.  The trace
         spec (LEXORDER :NATIVE T :MULTIPLICITY 1) is thus illegal.  Consider
         using trace! instead.  The complete list of keywords that require a
         trust tag for use in a trace spec is: (:NATIVE :DEF :MULTIPLICITY).
    
    This error is harmless.  The function will appear, when calling
    (trace$), to remain traced, but in fact there will be no tracing
    behavior, so you may want to call untrace$ on the function symbol in
    question.
    
    
    File: acl2-doc-emacs.info,  Node: TRACE$,  Next: UNTRACE$,  Prev: TRACE!,  Up: TRACE
    
    TRACE$    trace function evaluations
    
         Examples:
         (trace$ foo bar)     ; trace foo and bar
         (trace$)             ; return current trace info (no new tracing specified)
         (trace$ (foo :entry  ; trace foo, printing first actual parameter upon entry
                      (car arglist)))
         (trace$ (foo :exit   ; trace foo, using fmt to print upon exit
                      (:fmt (msg "Exiting FOO with ~x0"
                                 value))))
         (trace$ (foo :native t))
    
         General Forms:
         (trace$ spec1 spec2 ... specn) ; n >= 1
         (trace$)
    
    where the speci are trace specs, as described below.
    
    Trace$ installs alternate code for the indicated functions that prints
    information upon entry to, and exit from, calls of the functions.  For
    an alternate tracing utility used for educational purposes in ACL2s
    (`http://acl2s.ccs.neu.edu/acl2s/doc/'), see community book
    books/misc/trace-star.lisp.
    
    From a logical perspective all trace printing is a fiction.  (But see
    *note TRACE!:: for a way to get around this and modify state.)  For a
    related fiction, see *note CW::.  (Trace$) returns the list of
    currently-active trace specs, while the application of trace$ to at
    least one argument returns the list of its arguments that are
    successfully acted on.
    
    Output from trace$ normally goes to the screen, i.e., to standard-co.
    But it can be redirected to a file; see *note OPEN-TRACE-FILE::.
    
    See *note UNTRACE$:: for how to undo the effect of trace$.  Also see
    *note TRACE:: for mention of modifications made to raw Lisp trace,
    which is accessible (as described below) using the :native keyword.
    
    Note that when trace$ is applied to a function without option :native,
    that function's declarations and documentation are discarded.
    
    Next, we introduce tracing with some examples.  After that, we provide
    reference documentation for individual trace options allowed in a trace
    spec.  Note that although our example focuses on user-defined
    functions, trace$ can also be applied to built-in functions, though
    perhaps only system hackers should take advantage of this observation.
    
    We begin by illustrating the simplest sort of trace spec: a function
    symbol.  For example, the form (trace$ foo bar) directs the tracing of
    functions foo and bar by virtue of the two trace specs foo and bar.  We
    can see tracing in action by first defining:
    
         (defun f (x)
           (cons x x))
    
         (defun g (x)
           (list (f x) 3))
    
    The following log then illustrates tracing of these two functions.
    Notice that before guards have been verified, the so-called "*1*"
    functions (sometimes called "executable counterpart functions" or "logic
    functions") are called but the corresponding raw Lisp functions are
    not; but after guard verification of f, the raw Lisp counterpart of f is
    indeed called.  (See *note GUARD:: and see *note
    GUARD-EVALUATION-EXAMPLES-LOG::.)
    
         ACL2 !>(trace$ f g)
          ((F) (G))
         ACL2 !>(g 7)
         1> (ACL2_*1*_ACL2::G 7)
           2> (ACL2_*1*_ACL2::F 7)
           <2 (ACL2_*1*_ACL2::F (7 . 7))
         <1 (ACL2_*1*_ACL2::G ((7 . 7) 3))
         ((7 . 7) 3)
         ACL2 !>(verify-guards f)
    
         Computing the guard conjecture for F....
    
         The guard conjecture for F is trivial to prove.  F is compliant with
         Common Lisp.
    
         Summary
         Form:  ( VERIFY-GUARDS F)
         Rules: NIL
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
          F
         ACL2 !>(g 7)
         1> (ACL2_*1*_ACL2::G 7)
           2> (ACL2_*1*_ACL2::F 7)
             3> (F 7)
             <3 (F (7 . 7))
           <2 (ACL2_*1*_ACL2::F (7 . 7))
         <1 (ACL2_*1*_ACL2::G ((7 . 7) 3))
         ((7 . 7) 3)
         ACL2 !>
    
    The following example introduces trace specs other than function
    symbols.  Consider the following definition.
    
         (defun fact (n)
           (declare (xargs :guard (natp n)))
           (if (zp n)
               1
             (* n (fact (1- n)))))
    
    The following log illustrates the use of trace options :cond (condition
    for entering trace), :entry (what to print on entry), and :exit (what
    to print on exit).  The reason for two calls on argument 4 is that we
    are seeing such calls for the executable counterpart of fact and also
    its raw Lisp function.
    
         ACL2 !>(trace$ (fact :cond (evenp (car arglist))
                              :entry (cons 'factorial-call arglist)
                              :exit (car values)))
          ((FACT :COND (EVENP (CAR ARGLIST))
                 :ENTRY (CONS 'FACTORIAL-CALL ARGLIST)
                 :EXIT (CAR VALUES)))
         ACL2 !>(fact 4)
         1> (FACTORIAL-CALL 4)
           2> (FACTORIAL-CALL 4)
             3> (FACTORIAL-CALL 2)
               4> (FACTORIAL-CALL 0)
               <4 1
             <3 2
           <2 24
         <1 24
         24
         ACL2 !>
    
    Notice that VALUES above is the list of all values returned, which is a
    one-element list unless mv return is used, as illustrated in the
    following example, after defining: (defun two-vals (x) (mv x 7)).
    
         ACL2 !>(trace$ two-vals)
          ((TWO-VALS))
         ACL2 !>(two-vals 3)
         1> (ACL2_*1*_ACL2::TWO-VALS 3)
         <1 (ACL2_*1*_ACL2::TWO-VALS 3 7)
         (3 7)
         ACL2 !>(verify-guards two-vals)
    
         Computing the guard conjecture for TWO-VALS....
    
         The guard conjecture for TWO-VALS is trivial to prove, given the :executable-
         counterpart of CONS.  TWO-VALS is compliant with Common Lisp.
    
         Summary
         Form:  ( VERIFY-GUARDS TWO-VALS)
         Rules: ((:EXECUTABLE-COUNTERPART CONS))
         Warnings:  None
         Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
          TWO-VALS
         ACL2 !>(two-vals 3)
         1> (ACL2_*1*_ACL2::TWO-VALS 3)
           2> (TWO-VALS 3)
           <2 (TWO-VALS 3 7)
         <1 (ACL2_*1*_ACL2::TWO-VALS 3 7)
         (3 7)
         ACL2 !>
    
    We now document all of the options that may appear in a trace spec.  A
    trace spec with options is of the form
    
         (fn :kwd1 val1 :kwd2 val2 ... :kwdn valn)
    
    and here we document each legal keyword :kwdi and corresponding expected
    value vali.  Note that trace$ is intended primarily for functions
    defined in the ACL2 command loop (see *note LP::).  If you want to
    trace a function that defined in raw Lisp, then you can use option
    :native (see below), but then many other trace$ options will not be
    available to you: all of them except :multiplicity and :native itself
    will be passed directly to the trace utility of the underlying Common
    Lisp.
    
    :COND, :ENTRY, and :EXIT
    
    *Introduction*.  For each of these three options, the value is a
    (user-level) term, except that for :entry and :exit the value can be of
    the form (:fmt u) or (:fmt! u), where u is a user-level term.  We skip
    these two latter cases for now and return to them later.  Then the
    indicated term is evaluated as indicated in the next paragraph, and if
    the :cond term is omitted or evaluates to non-nil, then the value of the
    :entry term is printed on entry and the value of the :exit term is
    printed on exit.  By default, where :entry is omitted or is specified as
    nil, the value printed for :entry is the list obtained by consing the
    calling function symbol onto the list of actual parameters: in the
    notation described below, this is (cons TRACED-FN ARGLIST).  Similarly,
    the default for printing at the exit of the function call, i.e. where
    :exit is omitted or is specified as nil, is (cons TRACED-FN VALUES)
    where VALUES is the list of values returned as described below.
    
    In the evaluations of the term described below upon a call of fn, each
    formal parameter of the definition of fn will be bound to the
    corresponding actual of the call, the variable ARGLIST will be bound to
    the list of actuals, and the variable TRACED-FN will be bound to the
    function being called (either fn or its executable counterpart function;
    see above).  Additionally in the case of :exit, the variable VALUES
    will be bound to the multiple values returned (thus, a one-element list
    if mv is not used in the return).  Also for :exit, we bind VALUE to the
    logical value returned, i.e., to the suitable list of values returned
    in the mv case and otherwise to the single value returned.  So in the
    mv case, VALUE is the same as VALUES, and otherwise VALUE is (car
    VALUES).  Other than these variables and STATE, no other variable may
    occur in the term, whose value must be a single non-stobj value, unless
    there is an active trust tag (see *note DEFTTAG::).
    
    Now suppose fn is called.  First: If :cond is supplied and the result
    of evaluating the :cond term is nil, then no tracing is done.
    Otherwise tracing proceeds as follows.  First the :entry form is
    evaluated, and the result is printed.  Then the call of fn is evaluated.
    Finally the :exit term is evaluated and the result is printed.  As
    indicated above, the default for the :entry term if omitted or
    explicitly nil is (cons TRACED-FN ARGLIST), and the default for the
    :exit term if omitted or explicitly nil is (cons TRACED-FN VALUES).
    
    Note that if the function has a formal named ARGLIST, then ARGLIST will
    nevertheless refer to the entire list of formals, not the single formal
    named ARGLIST; similarly for TRACED-FN, and additionally for VALUE and
    VALUES in the case of :exit.
    
    As mentioned above, for each of :entry and :exit, a value of nil
    specifies the default behavior.  If you really want a value of nil, use
    a non-nil form that evaluates to nil, for example (car nil) or 'nil.
    However, for :cond a value of nil means what it says: do not evaluate
    the :entry or :exit forms.
    
    Finally we discuss the case that the :entry or :exit term is of the
    form (:fmt u) or (:fmt! u).  In these cases, the term u is evaluated as
    described above to produce a value, say msg, but instead of printing
    msg directly, ACL2 calls fmt1 using the string "~@0" and the alist that
    binds just character #\0 to msg.  The following example illustrates
    this point, where fact is defined as above.  Also see *note FMT::.
    Note that (msg string . vals) produces a value suitable for a "~@"
    directive to the fmt family of print functions.
    
         ACL2 !>(trace$
                 (fact
                  :entry (:fmt (msg "Tracing ~x0 on ~x1" traced-fn arglist))
                  :exit (car values)))
          ((FACT :ENTRY (:FMT (MSG "Tracing ~x0 on ~x1" TRACED-FN ARGLIST))
                 :EXIT (CAR VALUES)))
         ACL2 !>(fact 3)
         1> Tracing ACL2_*1*_ACL2::FACT on (3)
           2> Tracing FACT on (3)
             3> Tracing FACT on (2)
               4> Tracing FACT on (1)
                 5> Tracing FACT on (0)
                 <5 1
               <4 1
             <3 2
           <2 6
         <1 6
         6
         ACL2 !>
    
    If :fmt! is used instead of :fmt, then indentation as is the prefix
    string, "n> " or "(trace$
                 (fact
                  :entry (:fmt! (msg "Tracing ~x0 on ~x1" traced-fn arglist))
                  :exit (:fmt! (msg "From input ~x0: ~x1"
                                    (car arglist) (car values)))))
          ((FACT :ENTRY (:FMT! (MSG "Tracing ~x0 on ~x1" TRACED-FN ARGLIST))
                 :EXIT (:FMT! (MSG "From input ~x0: ~x1" (CAR ARGLIST)
                                   (CAR VALUES)))))
         ACL2 !>(fact 3)
         Tracing ACL2_*1*_ACL2::FACT on (3)
         Tracing FACT on (3)
         Tracing FACT on (2)
         Tracing FACT on (1)
         Tracing FACT on (0)
         From input 0: 1
         From input 1: 1
         From input 2: 2
         From input 3: 6
         From input 3: 6
         6
         ACL2 !>
    
    Here is the same example, with user-managed indentation.
    
         ACL2 !>(trace$
                 (fact
                  :entry (:fmt! (msg "~t0Tracing ~x1 on ~x2"
                                     (+ 3 (* 2 (@ trace-level)))
                                     traced-fn arglist))
                  :exit (:fmt! (msg "~t0From input ~x1: ~x2"
                                    (1+ (* 2 (@ trace-level)))
                                    (car arglist) (car values)))))
          ((FACT :ENTRY (:FMT! (MSG "~t0Tracing ~x1 on ~x2"
                                    (+ 3 (* 2 (@ TRACE-LEVEL)))
                                    TRACED-FN ARGLIST))
                 :EXIT (:FMT! (MSG "~t0From input ~x1: ~x2"
                                   (1+ (* 2 (@ TRACE-LEVEL)))
                                   (CAR ARGLIST)
                                   (CAR VALUES)))))
         ACL2 !>(fact 3)
            Tracing ACL2_*1*_ACL2::FACT on (3)
              Tracing FACT on (3)
                Tracing FACT on (2)
                  Tracing FACT on (1)
                    Tracing FACT on (0)
                    From input 0: 1
                  From input 1: 1
                From input 2: 2
              From input 3: 6
            From input 3: 6
         6
         ACL2 !>
    
    *ADVANCED OPTIONS* (alphabetical list)
    
    :COMPILE
    
    The tracing of fn installs a substitute definition of fn that prints
    trace information.  If the :compile option is omitted or has value
    :same, then the new definition will be compiled if and only if the
    existing definition is already compiled.  Otherwise, the new definition
    will be compiled exactly when the value of :compile is not nil.
    
    :DEF, :MULTIPLICITY
    
    ACL2's trace$ mechanism often needs to know the number of outputs of a
    traced function, in the sense of mv.  If you trace a function that was
    not defined inside the ACL2 loop (hence you are using the :native
    option), or if you provide an alternative definition using option :def
    (see below) and the new definition changes the number of values
    returned, then a natural number value for :multiplicity informs the
    trace utility of the number of expected outputs of the function being
    traced.  In the case that :native is supplied, the effect of a non-nil
    :multiplicity value depends on the host Lisp.  In the case of Lisps for
    which ACL2 uses the built-in Lisp mechanism for returning multiple
    values (see *note MV::), which are CCL and threaded SBCL as of June,
    2010, :multiplicity is not needed and is ignored with :native t.  For
    GCL and Allegro CL, :multiplicity is used to generate a suitable :exit
    form if the :exit keyword was not already supplied.  For the other
    Lisps, the :multiplicity value is treated essentially as 1 whether it
    is supplied or not, because we do not know how to pass suitable
    information based on this value to the host Lisp's built-in tracing
    mechanism.
    
    Note that even supplying a :multiplicity option does not change the
    meaning of the variable values.  See the discussion of :native below.
    
    A useful option can be to supply a definition as the value of :def.
    (Again, note that if :native is used, then all options other than
    :multiplicity are passed directly to the underlying Lisp; in particular,
    :def will have no effect with :native except in the unlikely case that
    the raw Lisp provides some sort of support for :def.)  Note that this
    definition should be like a defun form, but without the leading defun
    symbol; and it should define the function symbol being traced, with the
    same formal parameter list.  However, tracing of the so-called
    "executable counterpart" of a function (sometimes referred to as the
    "*1* function", for evaluation in the ACL2 loop; see *note Guards:: for
    related discussion) is not sensitive to the :def option; rather, if a
    function has an executable counterpart then that executable counterpart
    is traced.
    
    :EVISC-TUPLE
    
    The printing described above is, by default, done using the current
    default trace evisc-tuple, which can be set using set-trace-evisc-tuple
    (for the shape of this tuple, see *note EVISC-TUPLE::); see *note
    SET-TRACE-EVISC-TUPLE::.  This tuple is based by default on the raw
    Lisp variables *print-level* and *print-length*, and will hide the ACL2
    world and handle stobjs appropriately.  You may override this default
    by supplying an evisc tuple with the :evisc-tuple argument in your
    trace spec.  Be careful to supply a valid evisc-tuple, or you may get a
    raw Lisp error!
    
    A special value, :print, is useful if you are doing system hacking that
    can produce objects that are not valid ACL2 objects, such as raw Lisp
    arrays or objects in supporting packages not visible in the ACL2
    read-eval-print loop.  If you supply :evisc-tuple :print, then the
    printing described above will be done with raw Lisp printing rather
    than ACL2 printing: specifically, with (format *trace-output* "s%" x),
    where x is the value to be printed.
    
    A second special value for :evisc-tuple, :no-print, avoids printing the
    values of the :entry and :exit forms (or their defaults, if not
    specified).  This option is of use for side effects; for an example see
    community book books/misc/wet.lisp.
    
    Note that if :evisc-tuple X is supplied, then the form X will be
    evaluated before the function body is entered.  You can thus pull some
    tricks to print extra information before the :entry form is evaluated,
    for example as follows for a factorial function, fact.
    
         ACL2 !>(trace$ (fact :evisc-tuple
                              (prog2$ (cw "~|**** HERE IS CW ****~|")
                                      nil)))
          ((FACT :EVISC-TUPLE (PROG2$ (CW "~|**** HERE IS CW ****~|")
                                      NIL)))
         ACL2 !>(fact 3)
         **** HERE IS CW ****
         1> (ACL2_*1*_ACL2::FACT 3)
         **** HERE IS CW ****
           2> (ACL2_*1*_ACL2::FACT 2)
         **** HERE IS CW ****
             3> (ACL2_*1*_ACL2::FACT 1)
         **** HERE IS CW ****
               4> (ACL2_*1*_ACL2::FACT 0)
               <4 (ACL2_*1*_ACL2::FACT 1)
             <3 (ACL2_*1*_ACL2::FACT 1)
           <2 (ACL2_*1*_ACL2::FACT 2)
         <1 (ACL2_*1*_ACL2::FACT 6)
         6
         ACL2 !>
    
    :FORMALS
    
    Normally ACL2 can figure out the formals for a given function.  This is
    always the case for functions defined in the ACL2 command loop and when
    option :def is supplied.  If neither of these cases applies then you can
    still trace a function (even without using the :native option) by
    supplying option :notinline :fncall, but you will still need to supply
    the list of formal parameters.  The value of the :formals option should
    be the list of formals in this case.
    
    :HIDE
    
    The default value for this advanced option is t, which causes stobjs
    and the logical world to be printed as single symbols, along with
    certain large structures of interest to developers (rewrite contants,
    enabled structures, and event and command index structures).  If
    however the value nil is supplied, then this default behavior is
    defeated.  In that case, you can still arrange to print the logical
    world as a symbol and to print stobjs without breaking the trace
    printing: see *note SET-TRACE-EVISC-TUPLE:: for how to do this
    globally, or similarly use the :evisc-tuple option to trace$ to do this
    with a single trace spec.  Note however that with value nil specified
    for :hide, such use of an evisc-tuple will not deal properly with local
    stobjs (see *note WITH-LOCAL-STOBJ::) or stobjs bound by stobj-let, or
    with the aforementioned large structures other than the logical world.
    
    :NATIVE
    
    If :native is supplied with a non-nil value, then the trace spec is
    passed to the native Lisp trace (after removing the :native option).  A
    trust tag (see *note DEFTTAG::) is required in order to use this
    option, because no syntactic check is made on the :cond, :entry, or
    :exit forms - arbitrary raw Lisp may occur in them!
    
    Note that by "native Lisp trace" we mean the currently installed trace.
    As discussed briefly elsewhere (see *note TRACE::), ACL2 has modified
    that trace to be more useful if the underlying host Lisp is GCL,
    Allegro CL, or CCL (OpenMCL).  If you need the original trace utility
    supplied for those Lisps, quit the ACL2 loop with :q and call old-trace
    and old-untrace in raw Lisp where you would otherwise call trace and
    untrace.  Note that the original trace utility supplied with a given
    Lisp will not hide the ACL2 logical world or give special treatment to
    stobjs.
    
    It is important to understand that if :native t is specified, then all
    other options are interpreted by the native Lisp trace.  For example,
    that trace probably has no understanding of the use of :fmt described
    above for :entry or :exit.  Indeed, the native trace may not even accept
    any of :cond, :entry or :exit, let alone any of the advanced options!
    Moreover, if :native t is specified, then even a :multiplicity option
    does not provide the meaning of the variable values that one might
    desire.  In GCL for example, in the case of an mv return of a function
    defined only in raw Lisp (not in ACL2), this variable will be bound to
    a list containing only the first result.
    
    :NOTINLINE
    
    By default, a new definition installed by trace$ will include a
    notinline declaration so that recursive calls will always be traced.  To
    avoid this declaration, supply value nil.
    
    A special value for :notinline, :fncall, will cause the traced function
    to call its original definition.  Without this special value, the new
    installed definition for the traced function will include the body of
    the original definition.  This :fncall behavior is the default only in
    the following cases:
    
         o for functions whose definitions are built into ACL2;
    
         o for functions that have been added (using a trust tag, an
         advanced feature, so most users can probably ignore this case) to
         either of the state global variables program-fns-with-raw-code or
         logic-fns-with-raw-code;
    
         o (`HONS' extension only; see *note HONS-AND-MEMOIZATION::) for
         memoized functions.
    
    The legal values for :notinline are t (the default for other than the
    cases displayed above), nil, and :fncall.  (Except: For the 'HONS'
    extension, only :fncall is legal.)
    
    *Remarks*.
    
    (1) If some of the given trace specs have errors, then trace$ will
    generally print error messages for those but will still process those
    that do not have errors.  The value returned will indicate the trace
    specs that were processed successfully.
    
    (2) If you certify or include a book that redundantly defines a
    function that is currently traced, then tracing behavior may disappear
    if a compiled definition is installed for the function or its
    in-the-logic (so-called `*1*') counterpart.
    
    (3) Some predefined functions are called during tracing.  In order to
    avoid infinite loops, such calls of traced predefined functions will be
    made using the original predefined functions, not using their code
    installed by trace$.
    
    
    File: acl2-doc-emacs.info,  Node: UNTRACE$,  Next: WET,  Prev: TRACE$,  Up: TRACE
    
    UNTRACE$    untrace functions
    
         Examples:
         (untrace$)         ; untrace all functions previously
                            ; traced (e.g. with trace$ or trace!)
         (untrace$ foo bar) ; as above, except only untrace foo and bar
    
         General Forms:
         (untrace$)                 ; untrace all (as noted above)
         (untrace$ fn1 fn2 ... fnk) ; untrace the indicated functions
    
    where the fni were previously traced (e.g. with trace$ or trace!).
    
    Untrace$ undoes the effect of trace$.  See *note TRACE$::.  The value
    returned by untrace$ gives the list of functions for which tracing is
    being removed.
    
    
    File: acl2-doc-emacs.info,  Node: WET,  Prev: UNTRACE$,  Up: TRACE
    
    WET    evaluate a form and print subsequent error trace
    
    The acronym "wet" stands for "with-error-trace".  Wet provides a
    convenient way to obtain a backtrace when evaluation causes a guard
    violation or other error.
    
    The basic idea is that (wet form) evaluates form and, if there is an
    error, shows a backtrace of calls that led to that error.  Note however
    that by default only calls of user-defined (not built-in) functions
    "supporting" form in the following sense will show up in the backtrace:
    those that occur in the macroexpansion of form or (recursively) support
    any of those functions.  So for example, since (make-event form)
    macroexpands to (make-event-fn (quote form) ...), calls of functions
    occurring in form will likely not show up in the backtrace by default.
    The option :fns all overrides this default, with potential loss of
    speed; more on this below.
    
    The following example explains the use of wet.  First, submit the
    following three definitions:
    
         (defun foo (x) (declare (xargs :guard (consp x))) (car x))
         (defun bar (x) (foo (cdr x)))
         (defun g (x) (bar (cdr x)))
    
    Now imagine you have obtained the following guard violation:
    
         ACL2 !>(g '(3 4))
    
    
         ACL2 Error in TOP-LEVEL:  The guard for the function call (FOO X),
         which is (CONSP X), is violated by the arguments in the call (FOO NIL).
         To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.  See
         :DOC set-guard-checking for information about suppressing this check
         with (set-guard-checking :none), as recommended for new users.
    
         ACL2 !>
    
    With wet, you can get a backtrace of user-defined functions.  The
    package prefixes shown below, ACL2_*1*_, indicate that the executable
    (logical) counterparts of the corresponding raw Lisp functions are
    being called; see *note GUARD::.  Don't forget to start with
    (include-book "misc/wet" :dir :system).
    
         ACL2 !>(wet (g '(3 4)))
         ; Fast loading /projects/acl2/devel/books/misc/wet.fasl
    
         TTAG NOTE: Adding ttag :TRACE! from the top level loop.
    
    
         ACL2 Error in WET:  The guard for the function call (FOO X), which
         is (CONSP X), is violated by the arguments in the call (FOO NIL).
         To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.  See
         :DOC set-guard-checking for information about suppressing this check
         with (set-guard-checking :none), as recommended for new users.
    
    
         Backtrace stack:
         ----------------
         1. (ACL2_*1*_ACL2::FOO NIL)
         2. (ACL2_*1*_ACL2::BAR (4))
         3. (ACL2_*1*_ACL2::G (3 4))
    
         ACL2 !>
    
    By default, large structures are hidden during the printing of the
    backtrace stack.  But you can supply a value for keyword argument
    :evisc-tuple to modify the printing: nil to avoid hiding, else a
    suitable evisc-tuple, as shown below (see *note EVISC-TUPLE::).
    
         ACL2 !>(wet (g '(3 4)) :evisc-tuple (evisc-tuple 1 1 nil nil))
         ; Fast loading /projects/acl2/devel/books/misc/wet.fasl
    
         TTAG NOTE: Adding ttag :TRACE! from the top level loop.
    
    
         ACL2 Error in WET:  The guard for the function call (FOO X), which
         is (CONSP X), is violated by the arguments in the call (FOO NIL).
         To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.  See
         :DOC set-guard-checking for information about suppressing this check
         with (set-guard-checking :none), as recommended for new users.
    
    
         Backtrace stack:
         ----------------
         1. (ACL2_*1*_ACL2::FOO ...)
         2. (ACL2_*1*_ACL2::BAR ...)
         3. (ACL2_*1*_ACL2::G ...)
    
         ACL2 !>
    
    For a backtrace as a data object, evaluate the form (@ wet-stack).  But
    note that this object may not be a legal ACL2 value, for example
    because of the "*1*" symbols shown above.
    
         General Form:
         (wet form           ; an arbitrary form
              :book bk-form  ; optional, not evaluated
              ;;; the rest are optional and evaluated:
              :evisc-tuple e ; an evisc-tuple
              :fns fns       ; :all, or a list of functions to show in a backtrace
              :compile c     ; :same, t, or nil; default :same (nil if :fns supplied)
    
    Form is evaluated.  If there is an error, a backtrace stack is printed
    to the standard output (*standard-co*), containing (by default) the
    user-defined function calls made before the error.  Such printing is
    controlled by the :evisc-tuple if supplied; otherwise, hiding of large
    structures will occur.  (Technical detail: by default the global
    abbrev-evisc-tuple is used, if bound; see *note SET-EVISC-TUPLE::.
    
    The :fns option.  As mentioned above, by default the wet backtrace
    shows user-defined functions that syntactically "support" the form being
    evaluated.  This default can be overridden by supplying an explicit
    list, fns, of functions, using option :fns fns; these will then be the
    functions whose calls are eligible for inclusion in the backtrace.  The
    special value :fns :all will allow all user-defined function calls in
    the backtrace.  This value can be useful when using oracle-apply, for
    example, since the function being applied isn't typically included as a
    syntactic supporter of the form being evaluated.
    
    The :compile option.  Wet uses the trace$ utility to modify the
    definitions of affected functions so that they can record information
    for the backtrace.  As described above, these affected functions are
    those that syntactically "support" the form unless specified by the
    :fns option.  As is the case for trace$ -- see *note TRACE$:: -- the
    new definitions of these affected functions may or may not be compiled.
    For trace$ and for wet, the default is to compile the new definition if
    and only if the original definition was compiled, except: For wet, if
    option :fns :all is provided, then the default is not to compile the
    affected definitions.  And for trace$ and wet, the :compile option
    overrides the default, to specify what will be compiled: value :same to
    compile each affected function if and only if its original definition
    was compiled, value t to compile all affected functions, and value nil
    to skip compilation.
    
    The :book option.  Wet actually works by temporarily including a
    community book,
    
         (include-book "misc/wet" :dir :system)
    
    and then passing its arguments to macro wet!, defined in that book.
    The keyword argument :book allows you to specify a different book that
    defines a macro wet! to which to pass its arguments.  If the value of
    :book is a string, then the book named by that string is temporarily
    included using include-book: (include-book "bk").  Otherwise :book
    should be a list of arguments, to be provided (unevaluated) to
    include-book, for example ("my-wet" :dir :my-utils).  Thus you can
    experiment by copying community book books/misc/wet.lisp to your own
    directory and making modifications to the copy.  If you make changes, we
    invite you to share them with the ACL2 community (see *note BOOKS::).
    Note that you can also supply :book nil, in which case the definition
    of wet! in your current session will be used without including a book.
    
    Also see *note TRACE$:: for a general tracing utility.  As mentioned
    above, wet is implemented using trace$.  Wet actually first applies
    untrace$; upon completion, wet then applies trace$ to re-trace any
    functions that it had untraced, using their original trace specs.
    
    
    File: acl2-doc-emacs.info,  Node: Index,  Up: Top
    
    Index
    *****
    
    [index]
    * Menu:
    
    * &ALLOW-OTHER-KEYS:                     &ALLOW-OTHER-KEYS.     (line 3)
    * &BODY:                                 &BODY.                 (line 3)
    * &KEY:                                  &KEY.                  (line 3)
    * &OPTIONAL:                             &OPTIONAL.             (line 3)
    * &REST:                                 &REST.                 (line 3)
    * &WHOLE:                                &WHOLE.                (line 3)
    * *:                                     *.                     (line 3)
    * *STANDARD-CI*:                         *STANDARD-CI*.         (line 3)
    * *STANDARD-CO*:                         *STANDARD-CO*.         (line 3)
    * *STANDARD-OI*:                         *STANDARD-OI*.         (line 3)
    * *TERMINAL-MARKUP-TABLE*:               *TERMINAL-MARKUP-TABLE*.
                                                                    (line 3)
    * +:                                     +.                     (line 3)
    * -:                                     -.                     (line 3)
    * /:                                     /.                     (line 3)
    * /=:                                    /=.                    (line 3)
    * 1+:                                    1+.                    (line 3)
    * 1-:                                    1-.                    (line 3)
    * <:                                     <.                     (line 3)
    * <=:                                    <=.                    (line 3)
    * =:                                     =.                     (line 3)
    * >:                                     >.                     (line 3)
    * >=:                                    >=.                    (line 3)
    * @:                                     atsign.                (line 3)
    * A Flying Tour of ACL2:                 A Flying Tour of ACL2. (line 3)
    * A Sketch of How the Rewriter Works:    A Sketch of How the Rewriter Works.
                                                                    (line 3)
    * A Tiny Warning Sign:                   A Tiny Warning Sign.   (line 3)
    * A Trivial Proof:                       A Trivial Proof.       (line 3)
    * A Typical State:                       A Typical State.       (line 3)
    * A Walking Tour of ACL2:                A Walking Tour of ACL2.
                                                                    (line 3)
    * A!:                                    A!.                    (line 3)
    * ABORT!:                                ABORT!.                (line 3)
    * About Models:                          About Models.          (line 3)
    * About the ACL2 Home Page:              About the ACL2 Home Page.
                                                                    (line 3)
    * About the Admission of Recursive Definitions: About the Admission of Recursive Definitions.
                                                                    (line 3)
    * About the Prompt:                      About the Prompt.      (line 3)
    * About Types:                           About Types.           (line 3)
    * ABOUT-ACL2:                            ABOUT-ACL2.            (line 3)
    * ABS:                                   ABS.                   (line 3)
    * ACCUMULATED-PERSISTENCE:               ACCUMULATED-PERSISTENCE.
                                                                    (line 3)
    * ACCUMULATED-PERSISTENCE-SUBTLETIES:    ACCUMULATED-PERSISTENCE-SUBTLETIES.
                                                                    (line 3)
    * ACKNOWLEDGMENTS:                       ACKNOWLEDGMENTS.       (line 3)
    * ACL2 as an Interactive Theorem Prover: ACL2 as an Interactive Theorem Prover.
                                                                    (line 3)
    * ACL2 as an Interactive Theorem Prover (cont): ACL2 as an Interactive Theorem Prover (cont).
                                                                    (line 3)
    * ACL2 Characters:                       ACL2 Characters.       (line 3)
    * ACL2 Conses or Ordered Pairs:          ACL2 Conses or Ordered Pairs.
                                                                    (line 3)
    * ACL2 is an Untyped Language:           ACL2 is an Untyped Language.
                                                                    (line 3)
    * ACL2 Strings:                          ACL2 Strings.          (line 3)
    * ACL2 Symbols:                          ACL2 Symbols.          (line 3)
    * ACL2 System Architecture:              ACL2 System Architecture.
                                                                    (line 3)
    * ACL2-AS-STANDALONE-PROGRAM:            ACL2-AS-STANDALONE-PROGRAM.
                                                                    (line 3)
    * ACL2-BUILT-INS:                        ACL2-BUILT-INS.        (line 3)
    * ACL2-COUNT:                            ACL2-COUNT.            (line 3)
    * ACL2-CUSTOMIZATION:                    ACL2-CUSTOMIZATION.    (line 3)
    * ACL2-DEFAULTS-TABLE:                   ACL2-DEFAULTS-TABLE.   (line 3)
    * ACL2-HELP:                             ACL2-HELP.             (line 3)
    * ACL2-NUMBER-LISTP:                     ACL2-NUMBER-LISTP.     (line 3)
    * ACL2-NUMBERP:                          ACL2-NUMBERP.          (line 3)
    * ACL2-PC||=:                            ACL2-PC||=.            (line 3)
    * ACL2-PC||ACL2-WRAP:                    ACL2-PC||ACL2-WRAP.    (line 3)
    * ACL2-PC||ADD-ABBREVIATION:             ACL2-PC||ADD-ABBREVIATION.
                                                                    (line 3)
    * ACL2-PC||AL:                           ACL2-PC||AL.           (line 3)
    * ACL2-PC||APPLY-LINEAR:                 ACL2-PC||APPLY-LINEAR. (line 3)
    * ACL2-PC||BASH:                         ACL2-PC||BASH.         (line 3)
    * ACL2-PC||BDD:                          ACL2-PC||BDD.          (line 3)
    * ACL2-PC||BK:                           ACL2-PC||BK.           (line 3)
    * ACL2-PC||BOOKMARK:                     ACL2-PC||BOOKMARK.     (line 3)
    * ACL2-PC||CASESPLIT:                    ACL2-PC||CASESPLIT.    (line 3)
    * ACL2-PC||CG:                           ACL2-PC||CG.           (line 3)
    * ACL2-PC||CHANGE-GOAL:                  ACL2-PC||CHANGE-GOAL.  (line 3)
    * ACL2-PC||CL-PROC:                      ACL2-PC||CL-PROC.      (line 3)
    * ACL2-PC||CLAIM:                        ACL2-PC||CLAIM.        (line 3)
    * ACL2-PC||CLAUSE-PROCESSOR:             ACL2-PC||CLAUSE-PROCESSOR.
                                                                    (line 3)
    * ACL2-PC||COMM:                         ACL2-PC||COMM.         (line 3)
    * ACL2-PC||COMMANDS:                     ACL2-PC||COMMANDS.     (line 3)
    * ACL2-PC||COMMENT:                      ACL2-PC||COMMENT.      (line 3)
    * ACL2-PC||CONTRADICT:                   ACL2-PC||CONTRADICT.   (line 3)
    * ACL2-PC||CONTRAPOSE:                   ACL2-PC||CONTRAPOSE.   (line 3)
    * ACL2-PC||DEMOTE:                       ACL2-PC||DEMOTE.       (line 3)
    * ACL2-PC||DIVE:                         ACL2-PC||DIVE.         (line 3)
    * ACL2-PC||DO-ALL:                       ACL2-PC||DO-ALL.       (line 3)
    * ACL2-PC||DO-ALL-NO-PROMPT:             ACL2-PC||DO-ALL-NO-PROMPT.
                                                                    (line 3)
    * ACL2-PC||DO-STRICT:                    ACL2-PC||DO-STRICT.    (line 3)
    * ACL2-PC||DROP:                         ACL2-PC||DROP.         (line 3)
    * ACL2-PC||DV:                           ACL2-PC||DV.           (line 3)
    * ACL2-PC||ELIM:                         ACL2-PC||ELIM.         (line 3)
    * ACL2-PC||EQUIV:                        ACL2-PC||EQUIV.        (line 3)
    * ACL2-PC||EX:                           ACL2-PC||EX.           (line 3)
    * ACL2-PC||EXIT:                         ACL2-PC||EXIT.         (line 3)
    * ACL2-PC||EXPAND:                       ACL2-PC||EXPAND.       (line 3)
    * ACL2-PC||FAIL:                         ACL2-PC||FAIL.         (line 3)
    * ACL2-PC||FINISH:                       ACL2-PC||FINISH.       (line 3)
    * ACL2-PC||FORWARDCHAIN:                 ACL2-PC||FORWARDCHAIN. (line 3)
    * ACL2-PC||FREE:                         ACL2-PC||FREE.         (line 3)
    * ACL2-PC||GENEQV:                       ACL2-PC||GENEQV.       (line 3)
    * ACL2-PC||GENERALIZE:                   ACL2-PC||GENERALIZE.   (line 3)
    * ACL2-PC||GOALS:                        ACL2-PC||GOALS.        (line 3)
    * ACL2-PC||HELP:                         ACL2-PC||HELP.         (line 3)
    * ACL2-PC||HELP!:                        ACL2-PC||HELP!.        (line 3)
    * ACL2-PC||HELP-LONG:                    ACL2-PC||HELP-LONG.    (line 3)
    * ACL2-PC||HYPS:                         ACL2-PC||HYPS.         (line 3)
    * ACL2-PC||ILLEGAL:                      ACL2-PC||ILLEGAL.      (line 3)
    * ACL2-PC||IN-THEORY:                    ACL2-PC||IN-THEORY.    (line 3)
    * ACL2-PC||INDUCT:                       ACL2-PC||INDUCT.       (line 3)
    * ACL2-PC||LEMMAS-USED:                  ACL2-PC||LEMMAS-USED.  (line 3)
    * ACL2-PC||LISP:                         ACL2-PC||LISP.         (line 3)
    * ACL2-PC||MORE:                         ACL2-PC||MORE.         (line 3)
    * ACL2-PC||MORE!:                        ACL2-PC||MORE!.        (line 3)
    * ACL2-PC||NEGATE:                       ACL2-PC||NEGATE.       (line 3)
    * ACL2-PC||NIL:                          ACL2-PC||NIL.          (line 3)
    * ACL2-PC||NOISE:                        ACL2-PC||NOISE.        (line 3)
    * ACL2-PC||NX:                           ACL2-PC||NX.           (line 3)
    * ACL2-PC||ORELSE:                       ACL2-PC||ORELSE.       (line 3)
    * ACL2-PC||P:                            ACL2-PC||P.            (line 3)
    * ACL2-PC||P-TOP:                        ACL2-PC||P-TOP.        (line 3)
    * ACL2-PC||PL:                           ACL2-PC||PL.           (line 3)
    * ACL2-PC||PP:                           ACL2-PC||PP.           (line 3)
    * ACL2-PC||PR:                           ACL2-PC||PR.           (line 3)
    * ACL2-PC||PRINT:                        ACL2-PC||PRINT.        (line 3)
    * ACL2-PC||PRINT-ALL-CONCS:              ACL2-PC||PRINT-ALL-CONCS.
                                                                    (line 3)
    * ACL2-PC||PRINT-ALL-GOALS:              ACL2-PC||PRINT-ALL-GOALS.
                                                                    (line 3)
    * ACL2-PC||PRINT-MAIN:                   ACL2-PC||PRINT-MAIN.   (line 3)
    * ACL2-PC||PRO:                          ACL2-PC||PRO.          (line 3)
    * ACL2-PC||PROMOTE:                      ACL2-PC||PROMOTE.      (line 3)
    * ACL2-PC||PROTECT:                      ACL2-PC||PROTECT.      (line 3)
    * ACL2-PC||PROVE:                        ACL2-PC||PROVE.        (line 3)
    * ACL2-PC||PSO:                          ACL2-PC||PSO.          (line 3)
    * ACL2-PC||PSO!:                         ACL2-PC||PSO!.         (line 3)
    * ACL2-PC||PSOG:                         ACL2-PC||PSOG.         (line 3)
    * ACL2-PC||PUT:                          ACL2-PC||PUT.          (line 3)
    * ACL2-PC||QUIET:                        ACL2-PC||QUIET.        (line 3)
    * ACL2-PC||R:                            ACL2-PC||R.            (line 3)
    * ACL2-PC||REDUCE:                       ACL2-PC||REDUCE.       (line 3)
    * ACL2-PC||REDUCE-BY-INDUCTION:          ACL2-PC||REDUCE-BY-INDUCTION.
                                                                    (line 3)
    * ACL2-PC||REMOVE-ABBREVIATIONS:         ACL2-PC||REMOVE-ABBREVIATIONS.
                                                                    (line 3)
    * ACL2-PC||REPEAT:                       ACL2-PC||REPEAT.       (line 3)
    * ACL2-PC||REPEAT-REC:                   ACL2-PC||REPEAT-REC.   (line 3)
    * ACL2-PC||REPLAY:                       ACL2-PC||REPLAY.       (line 3)
    * ACL2-PC||RESTORE:                      ACL2-PC||RESTORE.      (line 3)
    * ACL2-PC||RETAIN:                       ACL2-PC||RETAIN.       (line 3)
    * ACL2-PC||RETRIEVE:                     ACL2-PC||RETRIEVE.     (line 3)
    * ACL2-PC||REWRITE:                      ACL2-PC||REWRITE.      (line 3)
    * ACL2-PC||RUN-INSTR-ON-GOAL:            ACL2-PC||RUN-INSTR-ON-GOAL.
                                                                    (line 3)
    * ACL2-PC||RUN-INSTR-ON-NEW-GOALS:       ACL2-PC||RUN-INSTR-ON-NEW-GOALS.
                                                                    (line 3)
    * ACL2-PC||RUNES:                        ACL2-PC||RUNES.        (line 3)
    * ACL2-PC||S:                            ACL2-PC||S.            (line 3)
    * ACL2-PC||S-PROP:                       ACL2-PC||S-PROP.       (line 3)
    * ACL2-PC||SAVE:                         ACL2-PC||SAVE.         (line 3)
    * ACL2-PC||SEQUENCE:                     ACL2-PC||SEQUENCE.     (line 3)
    * ACL2-PC||SHOW-ABBREVIATIONS:           ACL2-PC||SHOW-ABBREVIATIONS.
                                                                    (line 3)
    * ACL2-PC||SHOW-LINEARS:                 ACL2-PC||SHOW-LINEARS. (line 3)
    * ACL2-PC||SHOW-REWRITES:                ACL2-PC||SHOW-REWRITES.
                                                                    (line 3)
    * ACL2-PC||SHOW-TYPE-PRESCRIPTIONS:      ACL2-PC||SHOW-TYPE-PRESCRIPTIONS.
                                                                    (line 3)
    * ACL2-PC||SKIP:                         ACL2-PC||SKIP.         (line 3)
    * ACL2-PC||SL:                           ACL2-PC||SL.           (line 3)
    * ACL2-PC||SLS:                          ACL2-PC||SLS.          (line 3)
    * ACL2-PC||SPLIT:                        ACL2-PC||SPLIT.        (line 3)
    * ACL2-PC||SR:                           ACL2-PC||SR.           (line 3)
    * ACL2-PC||ST:                           ACL2-PC||ST.           (line 3)
    * ACL2-PC||SUCCEED:                      ACL2-PC||SUCCEED.      (line 3)
    * ACL2-PC||TH:                           ACL2-PC||TH.           (line 3)
    * ACL2-PC||THEN:                         ACL2-PC||THEN.         (line 3)
    * ACL2-PC||TOP:                          ACL2-PC||TOP.          (line 3)
    * ACL2-PC||TYPE-ALIST:                   ACL2-PC||TYPE-ALIST.   (line 3)
    * ACL2-PC||UNDO:                         ACL2-PC||UNDO.         (line 3)
    * ACL2-PC||UNSAVE:                       ACL2-PC||UNSAVE.       (line 3)
    * ACL2-PC||UP:                           ACL2-PC||UP.           (line 3)
    * ACL2-PC||USE:                          ACL2-PC||USE.          (line 3)
    * ACL2-PC||WRAP:                         ACL2-PC||WRAP.         (line 3)
    * ACL2-PC||WRAP-INDUCT:                  ACL2-PC||WRAP-INDUCT.  (line 3)
    * ACL2-PC||WRAP1:                        ACL2-PC||WRAP1.        (line 3)
    * ACL2-PC||X:                            ACL2-PC||X.            (line 3)
    * ACL2-PC||X-DUMB:                       ACL2-PC||X-DUMB.       (line 3)
    * ACL2-SEDAN:                            ACL2-SEDAN.            (line 3)
    * ACL2-TUTORIAL:                         ACL2-TUTORIAL.         (line 3)
    * ACL2-USER:                             ACL2-USER.             (line 3)
    * ACL2P-KEY-CHECKPOINTS:                 ACL2P-KEY-CHECKPOINTS. (line 3)
    * ACL2S:                                 ACL2S.                 (line 3)
    * ACONS:                                 ACONS.                 (line 3)
    * ACTIVE-RUNEP:                          ACTIVE-RUNEP.          (line 3)
    * ADD-BINOP:                             ADD-BINOP.             (line 3)
    * ADD-CUSTOM-KEYWORD-HINT:               ADD-CUSTOM-KEYWORD-HINT.
                                                                    (line 3)
    * ADD-DEFAULT-HINTS:                     ADD-DEFAULT-HINTS.     (line 3)
    * ADD-DEFAULT-HINTS!:                    ADD-DEFAULT-HINTS!.    (line 3)
    * ADD-DIVE-INTO-MACRO:                   ADD-DIVE-INTO-MACRO.   (line 3)
    * ADD-INCLUDE-BOOK-DIR:                  ADD-INCLUDE-BOOK-DIR.  (line 3)
    * ADD-INVISIBLE-FNS:                     ADD-INVISIBLE-FNS.     (line 3)
    * ADD-LD-KEYWORD-ALIAS:                  ADD-LD-KEYWORD-ALIAS.  (line 3)
    * ADD-LD-KEYWORD-ALIAS!:                 ADD-LD-KEYWORD-ALIAS!. (line 3)
    * ADD-MACRO-ALIAS:                       ADD-MACRO-ALIAS.       (line 3)
    * ADD-MACRO-FN:                          ADD-MACRO-FN.          (line 3)
    * ADD-MATCH-FREE-OVERRIDE:               ADD-MATCH-FREE-OVERRIDE.
                                                                    (line 3)
    * ADD-NTH-ALIAS:                         ADD-NTH-ALIAS.         (line 3)
    * ADD-OVERRIDE-HINTS:                    ADD-OVERRIDE-HINTS.    (line 3)
    * ADD-OVERRIDE-HINTS!:                   ADD-OVERRIDE-HINTS!.   (line 3)
    * ADD-RAW-ARITY:                         ADD-RAW-ARITY.         (line 3)
    * ADD-TO-SET:                            ADD-TO-SET.            (line 3)
    * ADD-TO-SET-EQ:                         ADD-TO-SET-EQ.         (line 3)
    * ADD-TO-SET-EQL:                        ADD-TO-SET-EQL.        (line 3)
    * ADD-TO-SET-EQUAL:                      ADD-TO-SET-EQUAL.      (line 3)
    * ADVANCED-FEATURES:                     ADVANCED-FEATURES.     (line 3)
    * ALISTP:                                ALISTP.                (line 3)
    * ALLOCATE-FIXNUM-RANGE:                 ALLOCATE-FIXNUM-RANGE. (line 3)
    * ALPHA-CHAR-P:                          ALPHA-CHAR-P.          (line 3)
    * ALPHORDER:                             ALPHORDER.             (line 3)
    * ALTERNATIVE-INTRODUCTION:              ALTERNATIVE-INTRODUCTION.
                                                                    (line 3)
    * An Example Common Lisp Function Definition: An Example Common Lisp Function Definition.
                                                                    (line 3)
    * An Example of ACL2 in Use:             An Example of ACL2 in Use.
                                                                    (line 3)
    * Analyzing Common Lisp Models:          Analyzing Common Lisp Models.
                                                                    (line 3)
    * AND:                                   AND.                   (line 3)
    * ANNOTATED-ACL2-SCRIPTS:                ANNOTATED-ACL2-SCRIPTS.
                                                                    (line 3)
    * APPEND:                                APPEND.                (line 3)
    * APROPOS:                               APROPOS.               (line 3)
    * ARCHITECTURE-OF-THE-PROVER:            ARCHITECTURE-OF-THE-PROVER.
                                                                    (line 3)
    * AREF1:                                 AREF1.                 (line 3)
    * AREF2:                                 AREF2.                 (line 3)
    * ARGS:                                  ARGS.                  (line 3)
    * ARRAY1P:                               ARRAY1P.               (line 3)
    * ARRAY2P:                               ARRAY2P.               (line 3)
    * ARRAYS:                                ARRAYS.                (line 3)
    * ARRAYS-EXAMPLE:                        ARRAYS-EXAMPLE.        (line 3)
    * ASET1:                                 ASET1.                 (line 3)
    * ASET2:                                 ASET2.                 (line 3)
    * ASH:                                   ASH.                   (line 3)
    * ASSERT$:                               ASSERT$.               (line 3)
    * ASSERT-EVENT:                          ASSERT-EVENT.          (line 3)
    * ASSIGN:                                ASSIGN.                (line 3)
    * ASSOC:                                 ASSOC.                 (line 3)
    * ASSOC-EQ:                              ASSOC-EQ.              (line 3)
    * ASSOC-EQUAL:                           ASSOC-EQUAL.           (line 3)
    * ASSOC-KEYWORD:                         ASSOC-KEYWORD.         (line 3)
    * ASSOC-STRING-EQUAL:                    ASSOC-STRING-EQUAL.    (line 3)
    * ATOM:                                  ATOM.                  (line 3)
    * ATOM-LISTP:                            ATOM-LISTP.            (line 3)
    * BACKCHAIN-LIMIT:                       BACKCHAIN-LIMIT.       (line 3)
    * BACKCHAIN-LIMIT-RW:                    BACKCHAIN-LIMIT-RW.    (line 3)
    * BACKTRACK:                             BACKTRACK.             (line 3)
    * BDD:                                   BDD.                   (line 3)
    * BDD-ALGORITHM:                         BDD-ALGORITHM.         (line 3)
    * BDD-INTRODUCTION:                      BDD-INTRODUCTION.      (line 3)
    * BIBLIOGRAPHY:                          BIBLIOGRAPHY.          (line 3)
    * BINARY-*:                              BINARY-*.              (line 3)
    * BINARY-+:                              BINARY-+.              (line 3)
    * BINARY-APPEND:                         BINARY-APPEND.         (line 3)
    * BIND-FREE:                             BIND-FREE.             (line 3)
    * BIND-FREE-EXAMPLES:                    BIND-FREE-EXAMPLES.    (line 3)
    * BOOK-COMPILED-FILE:                    BOOK-COMPILED-FILE.    (line 3)
    * BOOK-CONTENTS:                         BOOK-CONTENTS.         (line 3)
    * BOOK-EXAMPLE:                          BOOK-EXAMPLE.          (line 3)
    * BOOK-MAKEFILES:                        BOOK-MAKEFILES.        (line 3)
    * BOOK-NAME:                             BOOK-NAME.             (line 3)
    * BOOKS:                                 BOOKS.                 (line 3)
    * BOOKS-CERTIFICATION:                   BOOKS-CERTIFICATION.   (line 3)
    * BOOKS-CERTIFICATION-CLASSIC:           BOOKS-CERTIFICATION-CLASSIC.
                                                                    (line 3)
    * BOOLE$:                                BOOLE$.                (line 3)
    * BOOLEANP:                              BOOLEANP.              (line 3)
    * BOUNDERS:                              BOUNDERS.              (line 3)
    * BREAK$:                                BREAK$.                (line 3)
    * BREAK-LEMMA:                           BREAK-LEMMA.           (line 3)
    * BREAK-ON-ERROR:                        BREAK-ON-ERROR.        (line 3)
    * BREAK-REWRITE:                         BREAK-REWRITE.         (line 3)
    * BREAKS:                                BREAKS.                (line 3)
    * BRR:                                   BRR.                   (line 3)
    * BRR-COMMANDS:                          BRR-COMMANDS.          (line 3)
    * BRR@:                                  BRRatsign.             (line 3)
    * BUILT-IN-CLAUSE:                       BUILT-IN-CLAUSE.       (line 3)
    * BUTLAST:                               BUTLAST.               (line 3)
    * BY:                                    BY.                    (line 3)
    * CAAAAR:                                CAAAAR.                (line 3)
    * CAAADR:                                CAAADR.                (line 3)
    * CAAAR:                                 CAAAR.                 (line 3)
    * CAADAR:                                CAADAR.                (line 3)
    * CAADDR:                                CAADDR.                (line 3)
    * CAADR:                                 CAADR.                 (line 3)
    * CAAR:                                  CAAR.                  (line 3)
    * CADAAR:                                CADAAR.                (line 3)
    * CADADR:                                CADADR.                (line 3)
    * CADAR:                                 CADAR.                 (line 3)
    * CADDAR:                                CADDAR.                (line 3)
    * CADDDR:                                CADDDR.                (line 3)
    * CADDR:                                 CADDR.                 (line 3)
    * CADR:                                  CADR.                  (line 3)
    * CALLING-LD-IN-BAD-CONTEXTS:            CALLING-LD-IN-BAD-CONTEXTS.
                                                                    (line 3)
    * CANONICAL-PATHNAME:                    CANONICAL-PATHNAME.    (line 3)
    * CAR:                                   CAR.                   (line 3)
    * CASE:                                  CASE.                  (line 3)
    * CASE-MATCH:                            CASE-MATCH.            (line 3)
    * CASE-SPLIT:                            CASE-SPLIT.            (line 3)
    * CASE-SPLIT-LIMITATIONS:                CASE-SPLIT-LIMITATIONS.
                                                                    (line 3)
    * CASES:                                 CASES.                 (line 3)
    * CBD:                                   CBD.                   (line 3)
    * CDAAAR:                                CDAAAR.                (line 3)
    * CDAADR:                                CDAADR.                (line 3)
    * CDAAR:                                 CDAAR.                 (line 3)
    * CDADAR:                                CDADAR.                (line 3)
    * CDADDR:                                CDADDR.                (line 3)
    * CDADR:                                 CDADR.                 (line 3)
    * CDAR:                                  CDAR.                  (line 3)
    * CDDAAR:                                CDDAAR.                (line 3)
    * CDDADR:                                CDDADR.                (line 3)
    * CDDAR:                                 CDDAR.                 (line 3)
    * CDDDAR:                                CDDDAR.                (line 3)
    * CDDDDR:                                CDDDDR.                (line 3)
    * CDDDR:                                 CDDDR.                 (line 3)
    * CDDR:                                  CDDR.                  (line 3)
    * CDR:                                   CDR.                   (line 3)
    * CEILING:                               CEILING.               (line 3)
    * CERTIFICATE:                           CERTIFICATE.           (line 3)
    * CERTIFY-BOOK:                          CERTIFY-BOOK.          (line 3)
    * CERTIFY-BOOK!:                         CERTIFY-BOOK!.         (line 3)
    * CERTIFYING-BOOKS:                      CERTIFYING-BOOKS.      (line 3)
    * CHAR:                                  CHAR.                  (line 3)
    * CHAR-CODE:                             CHAR-CODE.             (line 3)
    * CHAR-DOWNCASE:                         CHAR-DOWNCASE.         (line 3)
    * CHAR-EQUAL:                            CHAR-EQUAL.            (line 3)
    * CHAR-UPCASE:                           CHAR-UPCASE.           (line 3)
    * CHAR<:                                 CHAR<.                 (line 3)
    * CHAR<=:                                CHAR<=.                (line 3)
    * CHAR>:                                 CHAR>.                 (line 3)
    * CHAR>=:                                CHAR>=.                (line 3)
    * CHARACTER-ALISTP:                      CHARACTER-ALISTP.      (line 3)
    * CHARACTER-ENCODING:                    CHARACTER-ENCODING.    (line 3)
    * CHARACTER-LISTP:                       CHARACTER-LISTP.       (line 3)
    * CHARACTERP:                            CHARACTERP.            (line 3)
    * CHARACTERS:                            CHARACTERS.            (line 3)
    * CHECK-SUM:                             CHECK-SUM.             (line 3)
    * CHECKPOINT-FORCED-GOALS:               CHECKPOINT-FORCED-GOALS.
                                                                    (line 3)
    * CLAUSE-IDENTIFIER:                     CLAUSE-IDENTIFIER.     (line 3)
    * CLAUSE-PROCESSOR:                      CLAUSE-PROCESSOR.      (line 3)
    * CLEAR-HASH-TABLES:                     CLEAR-HASH-TABLES.     (line 3)
    * CLEAR-MEMOIZE-STATISTICS:              CLEAR-MEMOIZE-STATISTICS.
                                                                    (line 3)
    * CLEAR-MEMOIZE-TABLE:                   CLEAR-MEMOIZE-TABLE.   (line 3)
    * CLEAR-MEMOIZE-TABLES:                  CLEAR-MEMOIZE-TABLES.  (line 3)
    * CLOSE-INPUT-CHANNEL:                   CLOSE-INPUT-CHANNEL.   (line 3)
    * CLOSE-OUTPUT-CHANNEL:                  CLOSE-OUTPUT-CHANNEL.  (line 3)
    * CLOSE-TRACE-FILE:                      CLOSE-TRACE-FILE.      (line 3)
    * CODE-CHAR:                             CODE-CHAR.             (line 3)
    * COERCE:                                COERCE.                (line 3)
    * COMMAND:                               COMMAND.               (line 3)
    * COMMAND-DESCRIPTOR:                    COMMAND-DESCRIPTOR.    (line 3)
    * COMMAND-LINE:                          COMMAND-LINE.          (line 3)
    * Common Lisp:                           Common Lisp.           (line 3)
    * Common Lisp as a Modeling Language:    Common Lisp as a Modeling Language.
                                                                    (line 3)
    * COMMON-LISP:                           COMMON-LISP.           (line 3)
    * COMMUNITY-BOOKS:                       COMMUNITY-BOOKS.       (line 3)
    * COMP:                                  COMP.                  (line 3)
    * COMP-GCL:                              COMP-GCL.              (line 3)
    * COMPILATION:                           COMPILATION.           (line 3)
    * COMPILING-ACL2P:                       COMPILING-ACL2P.       (line 3)
    * COMPLEX:                               COMPLEX.               (line 3)
    * COMPLEX-RATIONALP:                     COMPLEX-RATIONALP.     (line 3)
    * COMPLEX/COMPLEX-RATIONALP:             COMPLEX/COMPLEX-RATIONALP.
                                                                    (line 3)
    * COMPOUND-RECOGNIZER:                   COMPOUND-RECOGNIZER.   (line 3)
    * COMPRESS1:                             COMPRESS1.             (line 3)
    * COMPRESS2:                             COMPRESS2.             (line 3)
    * COMPUTED-HINTS:                        COMPUTED-HINTS.        (line 3)
    * CONCATENATE:                           CONCATENATE.           (line 3)
    * COND:                                  COND.                  (line 3)
    * CONGRUENCE:                            CONGRUENCE.            (line 3)
    * CONJUGATE:                             CONJUGATE.             (line 3)
    * CONS:                                  CONS.                  (line 3)
    * CONS-SUBTREES:                         CONS-SUBTREES.         (line 3)
    * CONSERVATIVITY-OF-DEFCHOOSE:           CONSERVATIVITY-OF-DEFCHOOSE.
                                                                    (line 3)
    * CONSP:                                 CONSP.                 (line 3)
    * CONSTRAINT:                            CONSTRAINT.            (line 3)
    * Conversion:                            Conversion.            (line 3)
    * COPYRIGHT:                             COPYRIGHT.             (line 3)
    * COROLLARY:                             COROLLARY.             (line 3)
    * Corroborating Models:                  Corroborating Models.  (line 3)
    * COUNT:                                 COUNT.                 (line 3)
    * CPU-CORE-COUNT:                        CPU-CORE-COUNT.        (line 3)
    * CURRENT-PACKAGE:                       CURRENT-PACKAGE.       (line 3)
    * CURRENT-THEORY:                        CURRENT-THEORY.        (line 3)
    * CUSTOM-KEYWORD-HINTS:                  CUSTOM-KEYWORD-HINTS.  (line 3)
    * CW:                                    CW.                    (line 3)
    * CW!:                                   CW!.                   (line 3)
    * CW-GSTACK:                             CW-GSTACK.             (line 3)
    * DEAD-EVENTS:                           DEAD-EVENTS.           (line 3)
    * DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS: DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS.
                                                                    (line 3)
    * DEALING-WITH-TAU-PROBLEMS:             DEALING-WITH-TAU-PROBLEMS.
                                                                    (line 3)
    * DECLARE:                               DECLARE.               (line 3)
    * DECLARE-STOBJS:                        DECLARE-STOBJS.        (line 3)
    * DEFABBREV:                             DEFABBREV.             (line 3)
    * DEFABSSTOBJ:                           DEFABSSTOBJ.           (line 3)
    * DEFABSSTOBJ-MISSING-EVENTS:            DEFABSSTOBJ-MISSING-EVENTS.
                                                                    (line 3)
    * DEFATTACH:                             DEFATTACH.             (line 3)
    * DEFAULT:                               DEFAULT.               (line 3)
    * DEFAULT-BACKCHAIN-LIMIT:               DEFAULT-BACKCHAIN-LIMIT.
                                                                    (line 3)
    * DEFAULT-DEFUN-MODE:                    DEFAULT-DEFUN-MODE.    (line 3)
    * DEFAULT-HINTS:                         DEFAULT-HINTS.         (line 3)
    * DEFAULT-HINTS-TABLE:                   DEFAULT-HINTS-TABLE.   (line 3)
    * DEFAULT-PRINT-PROMPT:                  DEFAULT-PRINT-PROMPT.  (line 3)
    * DEFAULT-RULER-EXTENDERS:               DEFAULT-RULER-EXTENDERS.
                                                                    (line 3)
    * DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT:  DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT.
                                                                    (line 3)
    * DEFAULT-VERIFY-GUARDS-EAGERNESS:       DEFAULT-VERIFY-GUARDS-EAGERNESS.
                                                                    (line 3)
    * DEFAXIOM:                              DEFAXIOM.              (line 3)
    * DEFCHOOSE:                             DEFCHOOSE.             (line 3)
    * DEFCONG:                               DEFCONG.               (line 3)
    * DEFCONST:                              DEFCONST.              (line 3)
    * DEFDOC:                                DEFDOC.                (line 3)
    * DEFEQUIV:                              DEFEQUIV.              (line 3)
    * DEFEVALUATOR:                          DEFEVALUATOR.          (line 3)
    * DEFEXEC:                               DEFEXEC.               (line 3)
    * DEFINE-PC-HELP:                        DEFINE-PC-HELP.        (line 3)
    * DEFINE-PC-MACRO:                       DEFINE-PC-MACRO.       (line 3)
    * DEFINE-PC-META:                        DEFINE-PC-META.        (line 3)
    * DEFINE-TRUSTED-CLAUSE-PROCESSOR:       DEFINE-TRUSTED-CLAUSE-PROCESSOR.
                                                                    (line 3)
    * DEFINITION:                            DEFINITION.            (line 3)
    * DEFLABEL:                              DEFLABEL.              (line 3)
    * DEFLOCK:                               DEFLOCK.               (line 3)
    * DEFMACRO:                              DEFMACRO.              (line 3)
    * DEFMACRO-LAST:                         DEFMACRO-LAST.         (line 3)
    * DEFN:                                  DEFN.                  (line 3)
    * DEFND:                                 DEFND.                 (line 3)
    * DEFPKG:                                DEFPKG.                (line 3)
    * DEFPROXY:                              DEFPROXY.              (line 3)
    * DEFPUN:                                DEFPUN.                (line 3)
    * DEFREFINEMENT:                         DEFREFINEMENT.         (line 3)
    * DEFSTOBJ:                              DEFSTOBJ.              (line 3)
    * DEFSTUB:                               DEFSTUB.               (line 3)
    * DEFTHEORY:                             DEFTHEORY.             (line 3)
    * DEFTHEORY-STATIC:                      DEFTHEORY-STATIC.      (line 3)
    * DEFTHM:                                DEFTHM.                (line 3)
    * DEFTHMD:                               DEFTHMD.               (line 3)
    * DEFTTAG:                               DEFTTAG.               (line 3)
    * DEFUN:                                 DEFUN.                 (line 3)
    * DEFUN-INLINE:                          DEFUN-INLINE.          (line 3)
    * DEFUN-MODE:                            DEFUN-MODE.            (line 3)
    * DEFUN-MODE-CAVEAT:                     DEFUN-MODE-CAVEAT.     (line 3)
    * DEFUN-NOTINLINE:                       DEFUN-NOTINLINE.       (line 3)
    * DEFUN-NX:                              DEFUN-NX.              (line 3)
    * DEFUN-SK:                              DEFUN-SK.              (line 3)
    * DEFUN-SK-EXAMPLE:                      DEFUN-SK-EXAMPLE.      (line 3)
    * DEFUND:                                DEFUND.                (line 3)
    * DEFUND-INLINE:                         DEFUND-INLINE.         (line 3)
    * DEFUND-NOTINLINE:                      DEFUND-NOTINLINE.      (line 3)
    * DEFUNS:                                DEFUNS.                (line 3)
    * DELETE-ASSOC:                          DELETE-ASSOC.          (line 3)
    * DELETE-ASSOC-EQ:                       DELETE-ASSOC-EQ.       (line 3)
    * DELETE-ASSOC-EQUAL:                    DELETE-ASSOC-EQUAL.    (line 3)
    * DELETE-INCLUDE-BOOK-DIR:               DELETE-INCLUDE-BOOK-DIR.
                                                                    (line 3)
    * DENOMINATOR:                           DENOMINATOR.           (line 3)
    * DIGIT-CHAR-P:                          DIGIT-CHAR-P.          (line 3)
    * DIGIT-TO-CHAR:                         DIGIT-TO-CHAR.         (line 3)
    * DIMENSIONS:                            DIMENSIONS.            (line 3)
    * DISABLE:                               DISABLE.               (line 3)
    * DISABLE-FORCING:                       DISABLE-FORCING.       (line 3)
    * DISABLE-IMMEDIATE-FORCE-MODEP:         DISABLE-IMMEDIATE-FORCE-MODEP.
                                                                    (line 3)
    * DISABLEDP:                             DISABLEDP.             (line 3)
    * DISASSEMBLE$:                          DISASSEMBLE$.          (line 3)
    * DIVE-INTO-MACROS-TABLE:                DIVE-INTO-MACROS-TABLE.
                                                                    (line 3)
    * DMR:                                   DMR.                   (line 3)
    * DO-NOT:                                DO-NOT.                (line 3)
    * DO-NOT-INDUCT:                         DO-NOT-INDUCT.         (line 3)
    * DOC:                                   DOC.                   (line 3)
    * DOC!:                                  DOC!.                  (line 3)
    * DOC-STRING:                            DOC-STRING.            (line 3)
    * DOCS:                                  DOCS.                  (line 3)
    * DOCUMENTATION:                         DOCUMENTATION.         (line 3)
    * DOUBLE-REWRITE:                        DOUBLE-REWRITE.        (line 3)
    * DYNAMICALLY-MONITOR-REWRITES:          DYNAMICALLY-MONITOR-REWRITES.
                                                                    (line 3)
    * E/D:                                   E/D.                   (line 3)
    * E0-ORD-<:                              E0-ORD-<.              (line 3)
    * E0-ORDINALP:                           E0-ORDINALP.           (line 3)
    * EARLY-TERMINATION:                     EARLY-TERMINATION.     (line 3)
    * EC-CALL:                               EC-CALL.               (line 3)
    * EIGHTH:                                EIGHTH.                (line 3)
    * ELIM:                                  ELIM.                  (line 3)
    * EMACS:                                 EMACS.                 (line 3)
    * EMBEDDED-EVENT-FORM:                   EMBEDDED-EVENT-FORM.   (line 3)
    * ENABLE:                                ENABLE.                (line 3)
    * ENABLE-FORCING:                        ENABLE-FORCING.        (line 3)
    * ENABLE-IMMEDIATE-FORCE-MODEP:          ENABLE-IMMEDIATE-FORCE-MODEP.
                                                                    (line 3)
    * ENCAPSULATE:                           ENCAPSULATE.           (line 3)
    * ENDP:                                  ENDP.                  (line 3)
    * ENTER-BOOT-STRAP-MODE:                 ENTER-BOOT-STRAP-MODE. (line 3)
    * EQ:                                    EQ.                    (line 3)
    * EQL:                                   EQL.                   (line 3)
    * EQLABLE-ALISTP:                        EQLABLE-ALISTP.        (line 3)
    * EQLABLE-LISTP:                         EQLABLE-LISTP.         (line 3)
    * EQLABLEP:                              EQLABLEP.              (line 3)
    * EQUAL:                                 EQUAL.                 (line 3)
    * EQUALITY-VARIANTS:                     EQUALITY-VARIANTS.     (line 3)
    * EQUALITY-VARIANTS-DETAILS:             EQUALITY-VARIANTS-DETAILS.
                                                                    (line 3)
    * EQUIVALENCE:                           EQUIVALENCE.           (line 3)
    * EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES: EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES.
                                                                    (line 3)
    * ER:                                    ER.                    (line 3)
    * ER-PROGN:                              ER-PROGN.              (line 3)
    * ERROR-TRIPLES:                         ERROR-TRIPLES.         (line 3)
    * ERROR-TRIPLES-AND-PARALLELISM:         ERROR-TRIPLES-AND-PARALLELISM.
                                                                    (line 3)
    * ERROR1:                                ERROR1.                (line 3)
    * ESCAPE-TO-COMMON-LISP:                 ESCAPE-TO-COMMON-LISP. (line 3)
    * Evaluating App on Sample Input:        Evaluating App on Sample Input.
                                                                    (line 3)
    * EVALUATOR-RESTRICTIONS:                EVALUATOR-RESTRICTIONS.
                                                                    (line 3)
    * EVENP:                                 EVENP.                 (line 3)
    * EVENTS:                                EVENTS.                (line 3)
    * EVISC-TABLE:                           EVISC-TABLE.           (line 3)
    * EVISC-TUPLE:                           EVISC-TUPLE.           (line 3)
    * EVISCERATE-HIDE-TERMS:                 EVISCERATE-HIDE-TERMS. (line 3)
    * EXAMPLE-INDUCTION-SCHEME-BINARY-TREES: EXAMPLE-INDUCTION-SCHEME-BINARY-TREES.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2:    EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION: EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-ON-LISTS:     EXAMPLE-INDUCTION-SCHEME-ON-LISTS.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES: EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-UPWARDS:      EXAMPLE-INDUCTION-SCHEME-UPWARDS.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS: EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS.
                                                                    (line 3)
    * EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS: EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS.
                                                                    (line 3)
    * EXAMPLE-INDUCTIONS:                    EXAMPLE-INDUCTIONS.    (line 3)
    * EXECUTABLE-COUNTERPART:                EXECUTABLE-COUNTERPART.
                                                                    (line 3)
    * EXECUTABLE-COUNTERPART-THEORY:         EXECUTABLE-COUNTERPART-THEORY.
                                                                    (line 3)
    * EXISTS:                                EXISTS.                (line 3)
    * EXIT:                                  EXIT.                  (line 3)
    * EXIT-BOOT-STRAP-MODE:                  EXIT-BOOT-STRAP-MODE.  (line 3)
    * EXPAND:                                EXPAND.                (line 3)
    * EXPLODE-NONNEGATIVE-INTEGER:           EXPLODE-NONNEGATIVE-INTEGER.
                                                                    (line 3)
    * EXPT:                                  EXPT.                  (line 3)
    * EXTENDED-METAFUNCTIONS:                EXTENDED-METAFUNCTIONS.
                                                                    (line 3)
    * EXTERNAL-FORMAT:                       EXTERNAL-FORMAT.       (line 3)
    * EXTRA-INFO:                            EXTRA-INFO.            (line 3)
    * F-GET-GLOBAL:                          F-GET-GLOBAL.          (line 3)
    * F-PUT-GLOBAL:                          F-PUT-GLOBAL.          (line 3)
    * FAILED-FORCING:                        FAILED-FORCING.        (line 3)
    * FAILURE:                               FAILURE.               (line 3)
    * FAST-ALIST-FREE:                       FAST-ALIST-FREE.       (line 3)
    * FAST-ALIST-FREE-ON-EXIT:               FAST-ALIST-FREE-ON-EXIT.
                                                                    (line 3)
    * FAST-ALIST-LEN:                        FAST-ALIST-LEN.        (line 3)
    * FAST-ALIST-SUMMARY:                    FAST-ALIST-SUMMARY.    (line 3)
    * FAST-ALISTS:                           FAST-ALISTS.           (line 3)
    * FC-REPORT:                             FC-REPORT.             (line 3)
    * FIFTH:                                 FIFTH.                 (line 3)
    * FILE-READING-EXAMPLE:                  FILE-READING-EXAMPLE.  (line 3)
    * FINALIZE-EVENT-USER:                   FINALIZE-EVENT-USER.   (line 3)
    * FIND-RULES-OF-RUNE:                    FIND-RULES-OF-RUNE.    (line 3)
    * FINDING-DOCUMENTATION:                 FINDING-DOCUMENTATION. (line 3)
    * FIRST:                                 FIRST.                 (line 3)
    * FIX:                                   FIX.                   (line 3)
    * FIX-TRUE-LIST:                         FIX-TRUE-LIST.         (line 3)
    * Flawed Induction Candidates in App Example: Flawed Induction Candidates in App Example.
                                                                    (line 3)
    * FLET:                                  FLET.                  (line 3)
    * FLOOR:                                 FLOOR.                 (line 3)
    * FLUSH-COMPRESS:                        FLUSH-COMPRESS.        (line 3)
    * FLUSH-HONS-GET-HASH-TABLE-LINK:        FLUSH-HONS-GET-HASH-TABLE-LINK.
                                                                    (line 3)
    * FMS:                                   FMS.                   (line 3)
    * FMS!:                                  FMS!.                  (line 3)
    * FMS!-TO-STRING:                        FMS!-TO-STRING.        (line 3)
    * FMS-TO-STRING:                         FMS-TO-STRING.         (line 3)
    * FMT:                                   FMT.                   (line 3)
    * FMT!:                                  FMT!.                  (line 3)
    * FMT!-TO-STRING:                        FMT!-TO-STRING.        (line 3)
    * FMT-TO-COMMENT-WINDOW:                 FMT-TO-COMMENT-WINDOW. (line 3)
    * FMT-TO-STRING:                         FMT-TO-STRING.         (line 3)
    * FMT1:                                  FMT1.                  (line 3)
    * FMT1!:                                 FMT1!.                 (line 3)
    * FMT1!-TO-STRING:                       FMT1!-TO-STRING.       (line 3)
    * FMT1-TO-STRING:                        FMT1-TO-STRING.        (line 3)
    * FNCALL-TERM:                           FNCALL-TERM.           (line 3)
    * FORALL:                                FORALL.                (line 3)
    * FORCE:                                 FORCE.                 (line 3)
    * FORCED:                                FORCED.                (line 3)
    * FORCING-ROUND:                         FORCING-ROUND.         (line 3)
    * FORWARD-CHAINING:                      FORWARD-CHAINING.      (line 3)
    * FORWARD-CHAINING-REPORTS:              FORWARD-CHAINING-REPORTS.
                                                                    (line 3)
    * FOURTH:                                FOURTH.                (line 3)
    * Free Variables in Top-Level Input:     Free Variables in Top-Level Input.
                                                                    (line 3)
    * FREE-VARIABLES:                        FREE-VARIABLES.        (line 3)
    * FREE-VARIABLES-EXAMPLES:               FREE-VARIABLES-EXAMPLES.
                                                                    (line 3)
    * FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING: FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING.
                                                                    (line 3)
    * FREE-VARIABLES-EXAMPLES-REWRITE:       FREE-VARIABLES-EXAMPLES-REWRITE.
                                                                    (line 3)
    * FREE-VARIABLES-TYPE-PRESCRIPTION:      FREE-VARIABLES-TYPE-PRESCRIPTION.
                                                                    (line 3)
    * FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS: FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS.
                                                                    (line 3)
    * FULL-BOOK-NAME:                        FULL-BOOK-NAME.        (line 3)
    * FUNCTION-THEORY:                       FUNCTION-THEORY.       (line 3)
    * FUNCTIONAL-INSTANTIATION-EXAMPLE:      FUNCTIONAL-INSTANTIATION-EXAMPLE.
                                                                    (line 3)
    * FUNCTIONAL-INSTANTIATION-IN-ACL2R:     FUNCTIONAL-INSTANTIATION-IN-ACL2R.
                                                                    (line 3)
    * Functions for Manipulating these Objects: Functions for Manipulating these Objects.
                                                                    (line 3)
    * FURTHER-INFORMATION-ON-REWRITING:      FURTHER-INFORMATION-ON-REWRITING.
                                                                    (line 3)
    * FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM: FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM.
                                                                    (line 3)
    * GAG-MODE:                              GAG-MODE.              (line 3)
    * GC$:                                   GC$.                   (line 3)
    * GC-VERBOSE:                            GC-VERBOSE.            (line 3)
    * GCL:                                   GCL.                   (line 3)
    * GCS:                                   GCS.                   (line 3)
    * GENERALIZE:                            GENERALIZE.            (line 3)
    * GENERALIZED-BOOLEANS:                  GENERALIZED-BOOLEANS.  (line 3)
    * GENERALIZING-KEY-CHECKPOINTS:          GENERALIZING-KEY-CHECKPOINTS.
                                                                    (line 3)
    * GET-COMMAND-SEQUENCE:                  GET-COMMAND-SEQUENCE.  (line 3)
    * GET-INTERNAL-TIME:                     GET-INTERNAL-TIME.     (line 3)
    * GET-OUTPUT-STREAM-STRING$:             GET-OUTPUT-STREAM-STRING$.
                                                                    (line 3)
    * GET-WORMHOLE-STATUS:                   GET-WORMHOLE-STATUS.   (line 3)
    * GETENV$:                               GETENV$.               (line 3)
    * GETPROP:                               GETPROP.               (line 3)
    * GOAL-SPEC:                             GOAL-SPEC.             (line 3)
    * GOOD-ATOM-LISTP:                       GOOD-ATOM-LISTP.       (line 3)
    * GOOD-BYE:                              GOOD-BYE.              (line 3)
    * GRANULARITY:                           GRANULARITY.           (line 3)
    * GROUND-ZERO:                           GROUND-ZERO.           (line 3)
    * GUARD:                                 GUARD.                 (line 3)
    * GUARD-DEBUG:                           GUARD-DEBUG.           (line 3)
    * GUARD-EVALUATION-EXAMPLES-LOG:         GUARD-EVALUATION-EXAMPLES-LOG.
                                                                    (line 3)
    * GUARD-EVALUATION-EXAMPLES-SCRIPT:      GUARD-EVALUATION-EXAMPLES-SCRIPT.
                                                                    (line 3)
    * GUARD-EVALUATION-TABLE:                GUARD-EVALUATION-TABLE.
                                                                    (line 3)
    * GUARD-EXAMPLE:                         GUARD-EXAMPLE.         (line 3)
    * GUARD-HINTS:                           GUARD-HINTS.           (line 3)
    * GUARD-INTRODUCTION:                    GUARD-INTRODUCTION.    (line 3)
    * GUARD-MISCELLANY:                      GUARD-MISCELLANY.      (line 3)
    * GUARD-OBLIGATION:                      GUARD-OBLIGATION.      (line 3)
    * GUARD-QUICK-REFERENCE:                 GUARD-QUICK-REFERENCE. (line 3)
    * Guards:                                Guards.                (line 3)
    * GUARDS-AND-EVALUATION:                 GUARDS-AND-EVALUATION. (line 3)
    * GUARDS-FOR-SPECIFICATION:              GUARDS-FOR-SPECIFICATION.
                                                                    (line 3)
    * Guessing the Type of a Newly Admitted Function: Guessing the Type of a Newly Admitted Function.
                                                                    (line 3)
    * Guiding the ACL2 Theorem Prover:       Guiding the ACL2 Theorem Prover.
                                                                    (line 3)
    * HANDS-OFF:                             HANDS-OFF.             (line 3)
    * HARD-ERROR:                            HARD-ERROR.            (line 3)
    * HEADER:                                HEADER.                (line 3)
    * HELP:                                  HELP.                  (line 3)
    * Hey Wait!  Is ACL2 Typed or Untyped(Q): Hey Wait! Is ACL2 Typed or Untyped(Q).
                                                                    (line 3)
    * HIDDEN-DEATH-PACKAGE:                  HIDDEN-DEATH-PACKAGE.  (line 3)
    * HIDDEN-DEFPKG:                         HIDDEN-DEFPKG.         (line 3)
    * HIDE:                                  HIDE.                  (line 3)
    * HINTS:                                 HINTS.                 (line 3)
    * HINTS-AND-THE-WATERFALL:               HINTS-AND-THE-WATERFALL.
                                                                    (line 3)
    * HISTORY:                               HISTORY.               (line 3)
    * HONS:                                  HONS.                  (line 3)
    * HONS-ACONS:                            HONS-ACONS.            (line 3)
    * HONS-ACONS!:                           HONS-ACONS!.           (line 3)
    * HONS-AND-MEMOIZATION:                  HONS-AND-MEMOIZATION.  (line 3)
    * HONS-ASSOC-EQUAL:                      HONS-ASSOC-EQUAL.      (line 3)
    * HONS-CLEAR:                            HONS-CLEAR.            (line 3)
    * HONS-COPY:                             HONS-COPY.             (line 3)
    * HONS-COPY-PERSISTENT:                  HONS-COPY-PERSISTENT.  (line 3)
    * HONS-EQUAL:                            HONS-EQUAL.            (line 3)
    * HONS-EQUAL-LITE:                       HONS-EQUAL-LITE.       (line 3)
    * HONS-GET:                              HONS-GET.              (line 3)
    * HONS-NOTE:                             HONS-NOTE.             (line 3)
    * HONS-RESIZE:                           HONS-RESIZE.           (line 3)
    * HONS-SHRINK-ALIST:                     HONS-SHRINK-ALIST.     (line 3)
    * HONS-SHRINK-ALIST!:                    HONS-SHRINK-ALIST!.    (line 3)
    * HONS-SUMMARY:                          HONS-SUMMARY.          (line 3)
    * HONS-WASH:                             HONS-WASH.             (line 3)
    * How Long Does It Take to Become an Effective User(Q): How Long Does It Take to Become an Effective User(Q).
                                                                    (line 3)
    * How To Find Out about ACL2 Functions:  How To Find Out about ACL2 Functions.
                                                                    (line 3)
    * How To Find Out about ACL2 Functions (cont): How To Find Out about ACL2 Functions (cont).
                                                                    (line 3)
    * I-AM-HERE:                             I-AM-HERE.             (line 3)
    * I-CLOSE:                               I-CLOSE.               (line 3)
    * I-LARGE:                               I-LARGE.               (line 3)
    * I-LIMITED:                             I-LIMITED.             (line 3)
    * I-SMALL:                               I-SMALL.               (line 3)
    * IDENTITY:                              IDENTITY.              (line 3)
    * IF:                                    IF.                    (line 3)
    * IF*:                                   IF*.                   (line 3)
    * IF-INTRO:                              IF-INTRO.              (line 3)
    * IFF:                                   IFF.                   (line 3)
    * IFIX:                                  IFIX.                  (line 3)
    * IGNORABLE:                             IGNORABLE.             (line 3)
    * IGNORE:                                IGNORE.                (line 3)
    * IGNORED-ATTACHMENT:                    IGNORED-ATTACHMENT.    (line 3)
    * ILLEGAL:                               ILLEGAL.               (line 3)
    * IMAGPART:                              IMAGPART.              (line 3)
    * IMMED-FORCED:                          IMMED-FORCED.          (line 3)
    * IMMEDIATE-FORCE-MODEP:                 IMMEDIATE-FORCE-MODEP. (line 3)
    * IMPLIES:                               IMPLIES.               (line 3)
    * IMPROPER-CONSP:                        IMPROPER-CONSP.        (line 3)
    * IN-ARITHMETIC-THEORY:                  IN-ARITHMETIC-THEORY.  (line 3)
    * IN-PACKAGE:                            IN-PACKAGE.            (line 3)
    * IN-TAU-INTERVALP:                      IN-TAU-INTERVALP.      (line 3)
    * IN-THEORY:                             IN-THEORY.             (line 3)
    * INCLUDE-BOOK:                          INCLUDE-BOOK.          (line 3)
    * INCOMPATIBLE:                          INCOMPATIBLE.          (line 3)
    * INDUCT:                                INDUCT.                (line 3)
    * INDUCTION:                             INDUCTION.             (line 3)
    * INITIALIZE-EVENT-USER:                 INITIALIZE-EVENT-USER. (line 3)
    * INSTRUCTIONS:                          INSTRUCTIONS.          (line 3)
    * INT=:                                  INT=.                  (line 3)
    * INTEGER-LENGTH:                        INTEGER-LENGTH.        (line 3)
    * INTEGER-LISTP:                         INTEGER-LISTP.         (line 3)
    * INTEGERP:                              INTEGERP.              (line 3)
    * INTERESTING-APPLICATIONS:              INTERESTING-APPLICATIONS.
                                                                    (line 3)
    * INTERN:                                INTERN.                (line 3)
    * INTERN$:                               INTERN$.               (line 3)
    * INTERN-IN-PACKAGE-OF-SYMBOL:           INTERN-IN-PACKAGE-OF-SYMBOL.
                                                                    (line 3)
    * INTERSECTION$:                         INTERSECTION$.         (line 3)
    * INTERSECTION-EQ:                       INTERSECTION-EQ.       (line 3)
    * INTERSECTION-EQUAL:                    INTERSECTION-EQUAL.    (line 3)
    * INTERSECTION-THEORIES:                 INTERSECTION-THEORIES. (line 3)
    * INTERSECTP:                            INTERSECTP.            (line 3)
    * INTERSECTP-EQ:                         INTERSECTP-EQ.         (line 3)
    * INTERSECTP-EQUAL:                      INTERSECTP-EQUAL.      (line 3)
    * INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS: INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS.
                                                                    (line 3)
    * INTRODUCTION-TO-HINTS:                 INTRODUCTION-TO-HINTS. (line 3)
    * INTRODUCTION-TO-KEY-CHECKPOINTS:       INTRODUCTION-TO-KEY-CHECKPOINTS.
                                                                    (line 3)
    * INTRODUCTION-TO-REWRITE-RULES-PART-1:  INTRODUCTION-TO-REWRITE-RULES-PART-1.
                                                                    (line 3)
    * INTRODUCTION-TO-REWRITE-RULES-PART-2:  INTRODUCTION-TO-REWRITE-RULES-PART-2.
                                                                    (line 3)
    * INTRODUCTION-TO-THE-DATABASE:          INTRODUCTION-TO-THE-DATABASE.
                                                                    (line 3)
    * INTRODUCTION-TO-THE-TAU-SYSTEM:        INTRODUCTION-TO-THE-TAU-SYSTEM.
                                                                    (line 3)
    * INTRODUCTION-TO-THE-THEOREM-PROVER:    INTRODUCTION-TO-THE-THEOREM-PROVER.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-1:      INTRODUCTORY-CHALLENGE-PROBLEM-1.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER: INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-2:      INTRODUCTORY-CHALLENGE-PROBLEM-2.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER: INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-3:      INTRODUCTORY-CHALLENGE-PROBLEM-3.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER: INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-4:      INTRODUCTORY-CHALLENGE-PROBLEM-4.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER: INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER.
                                                                    (line 3)
    * INTRODUCTORY-CHALLENGES:               INTRODUCTORY-CHALLENGES.
                                                                    (line 3)
    * INVISIBLE-FNS-TABLE:                   INVISIBLE-FNS-TABLE.   (line 3)
    * IO:                                    IO.                    (line 3)
    * IPRINT:                                IPRINT.                (line 3)
    * IPRINTING:                             IPRINTING.             (line 3)
    * IRRELEVANT-FORMALS:                    IRRELEVANT-FORMALS.    (line 3)
    * KEEP:                                  KEEP.                  (line 3)
    * KEYWORD:                               KEYWORD.               (line 3)
    * KEYWORD-COMMANDS:                      KEYWORD-COMMANDS.      (line 3)
    * KEYWORD-VALUE-LISTP:                   KEYWORD-VALUE-LISTP.   (line 3)
    * KEYWORDP:                              KEYWORDP.              (line 3)
    * KWOTE:                                 KWOTE.                 (line 3)
    * KWOTE-LST:                             KWOTE-LST.             (line 3)
    * LAMBDA:                                LAMBDA.                (line 3)
    * LAST:                                  LAST.                  (line 3)
    * LAST-PROVER-STEPS:                     LAST-PROVER-STEPS.     (line 3)
    * LD:                                    LD.                    (line 3)
    * LD-ERROR-ACTION:                       LD-ERROR-ACTION.       (line 3)
    * LD-ERROR-TRIPLES:                      LD-ERROR-TRIPLES.      (line 3)
    * LD-EVISC-TUPLE:                        LD-EVISC-TUPLE.        (line 3)
    * LD-KEYWORD-ALIASES:                    LD-KEYWORD-ALIASES.    (line 3)
    * LD-MISSING-INPUT-OK:                   LD-MISSING-INPUT-OK.   (line 3)
    * LD-POST-EVAL-PRINT:                    LD-POST-EVAL-PRINT.    (line 3)
    * LD-PRE-EVAL-FILTER:                    LD-PRE-EVAL-FILTER.    (line 3)
    * LD-PRE-EVAL-PRINT:                     LD-PRE-EVAL-PRINT.     (line 3)
    * LD-PROMPT:                             LD-PROMPT.             (line 3)
    * LD-QUERY-CONTROL-ALIST:                LD-QUERY-CONTROL-ALIST.
                                                                    (line 3)
    * LD-REDEFINITION-ACTION:                LD-REDEFINITION-ACTION.
                                                                    (line 3)
    * LD-SKIP-PROOFSP:                       LD-SKIP-PROOFSP.       (line 3)
    * LD-VERBOSE:                            LD-VERBOSE.            (line 3)
    * LEMMA-INSTANCE:                        LEMMA-INSTANCE.        (line 3)
    * LEN:                                   LEN.                   (line 3)
    * LENGTH:                                LENGTH.                (line 3)
    * LET:                                   LET.                   (line 3)
    * LET*:                                  LET*.                  (line 3)
    * LEXORDER:                              LEXORDER.              (line 3)
    * LINEAR:                                LINEAR.                (line 3)
    * LINEAR-ARITHMETIC:                     LINEAR-ARITHMETIC.     (line 3)
    * LIST:                                  LIST.                  (line 3)
    * LIST*:                                 LIST*.                 (line 3)
    * LISTP:                                 LISTP.                 (line 3)
    * LOCAL:                                 LOCAL.                 (line 3)
    * LOCAL-INCOMPATIBILITY:                 LOCAL-INCOMPATIBILITY. (line 3)
    * LOGAND:                                LOGAND.                (line 3)
    * LOGANDC1:                              LOGANDC1.              (line 3)
    * LOGANDC2:                              LOGANDC2.              (line 3)
    * LOGBITP:                               LOGBITP.               (line 3)
    * LOGCOUNT:                              LOGCOUNT.              (line 3)
    * LOGEQV:                                LOGEQV.                (line 3)
    * LOGIC:                                 LOGIC.                 (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED:     LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING.
                                                                    (line 3)
    * LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY.
                                                                    (line 3)
    * LOGICAL-NAME:                          LOGICAL-NAME.          (line 3)
    * LOGIOR:                                LOGIOR.                (line 3)
    * LOGNAND:                               LOGNAND.               (line 3)
    * LOGNOR:                                LOGNOR.                (line 3)
    * LOGNOT:                                LOGNOT.                (line 3)
    * LOGORC1:                               LOGORC1.               (line 3)
    * LOGORC2:                               LOGORC2.               (line 3)
    * LOGTEST:                               LOGTEST.               (line 3)
    * LOGXOR:                                LOGXOR.                (line 3)
    * LOOP-STOPPER:                          LOOP-STOPPER.          (line 3)
    * LOWER-CASE-P:                          LOWER-CASE-P.          (line 3)
    * LP:                                    LP.                    (line 3)
    * MACRO-ALIASES-TABLE:                   MACRO-ALIASES-TABLE.   (line 3)
    * MACRO-ARGS:                            MACRO-ARGS.            (line 3)
    * MACRO-COMMAND:                         MACRO-COMMAND.         (line 3)
    * MAKE-CHARACTER-LIST:                   MAKE-CHARACTER-LIST.   (line 3)
    * MAKE-EVENT:                            MAKE-EVENT.            (line 3)
    * MAKE-EVENT-DETAILS:                    MAKE-EVENT-DETAILS.    (line 3)
    * MAKE-FAST-ALIST:                       MAKE-FAST-ALIST.       (line 3)
    * MAKE-LIST:                             MAKE-LIST.             (line 3)
    * MAKE-ORD:                              MAKE-ORD.              (line 3)
    * MAKE-TAU-INTERVAL:                     MAKE-TAU-INTERVAL.     (line 3)
    * MAKE-WORMHOLE-STATUS:                  MAKE-WORMHOLE-STATUS.  (line 3)
    * MANAGING-ACL2-PACKAGES:                MANAGING-ACL2-PACKAGES.
                                                                    (line 3)
    * MARKUP:                                MARKUP.                (line 3)
    * MAX:                                   MAX.                   (line 3)
    * MAXIMUM-LENGTH:                        MAXIMUM-LENGTH.        (line 3)
    * MBE:                                   MBE.                   (line 3)
    * MBE1:                                  MBE1.                  (line 3)
    * MBT:                                   MBT.                   (line 3)
    * MEASURE:                               MEASURE.               (line 3)
    * MEMBER:                                MEMBER.                (line 3)
    * MEMBER-EQ:                             MEMBER-EQ.             (line 3)
    * MEMBER-EQUAL:                          MEMBER-EQUAL.          (line 3)
    * MEMOIZE:                               MEMOIZE.               (line 3)
    * MEMOIZE-SUMMARY:                       MEMOIZE-SUMMARY.       (line 3)
    * MEMSUM:                                MEMSUM.                (line 3)
    * META:                                  META.                  (line 3)
    * META-EXTRACT:                          META-EXTRACT.          (line 3)
    * META-EXTRACT-CONTEXTUAL-FACT:          META-EXTRACT-CONTEXTUAL-FACT.
                                                                    (line 3)
    * META-EXTRACT-FORMULA:                  META-EXTRACT-FORMULA.  (line 3)
    * META-EXTRACT-GLOBAL-FACT:              META-EXTRACT-GLOBAL-FACT.
                                                                    (line 3)
    * META-EXTRACT-GLOBAL-FACT+:             META-EXTRACT-GLOBAL-FACT+.
                                                                    (line 3)
    * META-EXTRACT-RW+-TERM:                 META-EXTRACT-RW+-TERM. (line 3)
    * MIN:                                   MIN.                   (line 3)
    * MINIMAL-THEORY:                        MINIMAL-THEORY.        (line 3)
    * MINUSP:                                MINUSP.                (line 3)
    * MISCELLANEOUS:                         MISCELLANEOUS.         (line 3)
    * MOD:                                   MOD.                   (line 3)
    * MOD-EXPT:                              MOD-EXPT.              (line 3)
    * MODE:                                  MODE.                  (line 3)
    * Modeling in ACL2:                      Modeling in ACL2.      (line 3)
    * Models in Engineering:                 Models in Engineering. (line 3)
    * Models of Computer Hardware and Software: Models of Computer Hardware and Software.
                                                                    (line 3)
    * MONITOR:                               MONITOR.               (line 3)
    * MONITORED-RUNES:                       MONITORED-RUNES.       (line 3)
    * MORE:                                  MORE.                  (line 3)
    * MORE!:                                 MORE!.                 (line 3)
    * MORE-DOC:                              MORE-DOC.              (line 3)
    * MSG:                                   MSG.                   (line 3)
    * MUST-BE-EQUAL:                         MUST-BE-EQUAL.         (line 3)
    * MUTUAL-RECURSION:                      MUTUAL-RECURSION.      (line 3)
    * MUTUAL-RECURSION-PROOF-EXAMPLE:        MUTUAL-RECURSION-PROOF-EXAMPLE.
                                                                    (line 3)
    * MV:                                    MV.                    (line 3)
    * MV-LET:                                MV-LET.                (line 3)
    * MV-LIST:                               MV-LIST.               (line 3)
    * MV-NTH:                                MV-NTH.                (line 3)
    * MV?:                                   MV?.                   (line 3)
    * MV?-LET:                               MV?-LET.               (line 3)
    * NAME:                                  NAME.                  (line 3)
    * Name the Formula Above:                Name the Formula Above.
                                                                    (line 3)
    * NAT-LISTP:                             NAT-LISTP.             (line 3)
    * NATP:                                  NATP.                  (line 3)
    * NESTED-STOBJS:                         NESTED-STOBJS.         (line 3)
    * NEVER-MEMOIZE:                         NEVER-MEMOIZE.         (line 3)
    * NFIX:                                  NFIX.                  (line 3)
    * NIL-GOAL:                              NIL-GOAL.              (line 3)
    * NINTH:                                 NINTH.                 (line 3)
    * NO-DUPLICATESP:                        NO-DUPLICATESP.        (line 3)
    * NO-DUPLICATESP-EQ:                     NO-DUPLICATESP-EQ.     (line 3)
    * NO-DUPLICATESP-EQUAL:                  NO-DUPLICATESP-EQUAL.  (line 3)
    * NO-THANKS:                             NO-THANKS.             (line 3)
    * NON-EXEC:                              NON-EXEC.              (line 3)
    * NON-EXECUTABLE:                        NON-EXECUTABLE.        (line 3)
    * NON-LINEAR-ARITHMETIC:                 NON-LINEAR-ARITHMETIC. (line 3)
    * NONLINEARP:                            NONLINEARP.            (line 3)
    * NONNEGATIVE-INTEGER-QUOTIENT:          NONNEGATIVE-INTEGER-QUOTIENT.
                                                                    (line 3)
    * Nontautological Subgoals:              Nontautological Subgoals.
                                                                    (line 3)
    * NORMALIZE:                             NORMALIZE.             (line 3)
    * NORMED:                                NORMED.                (line 3)
    * NOT:                                   NOT.                   (line 3)
    * NOTE-2-0:                              NOTE-2-0.              (line 3)
    * NOTE-2-1:                              NOTE-2-1.              (line 3)
    * NOTE-2-2:                              NOTE-2-2.              (line 3)
    * NOTE-2-3:                              NOTE-2-3.              (line 3)
    * NOTE-2-4:                              NOTE-2-4.              (line 3)
    * NOTE-2-5:                              NOTE-2-5.              (line 3)
    * NOTE-2-5(R):                           NOTE-2-5(R).           (line 3)
    * NOTE-2-6:                              NOTE-2-6.              (line 3)
    * NOTE-2-6(R):                           NOTE-2-6(R).           (line 3)
    * NOTE-2-6-GUARDS:                       NOTE-2-6-GUARDS.       (line 3)
    * NOTE-2-6-NEW-FUNCTIONALITY:            NOTE-2-6-NEW-FUNCTIONALITY.
                                                                    (line 3)
    * NOTE-2-6-OTHER:                        NOTE-2-6-OTHER.        (line 3)
    * NOTE-2-6-PROOF-CHECKER:                NOTE-2-6-PROOF-CHECKER.
                                                                    (line 3)
    * NOTE-2-6-PROOFS:                       NOTE-2-6-PROOFS.       (line 3)
    * NOTE-2-6-RULES:                        NOTE-2-6-RULES.        (line 3)
    * NOTE-2-6-SYSTEM:                       NOTE-2-6-SYSTEM.       (line 3)
    * NOTE-2-7:                              NOTE-2-7.              (line 3)
    * NOTE-2-7(R):                           NOTE-2-7(R).           (line 3)
    * NOTE-2-7-BUG-FIXES:                    NOTE-2-7-BUG-FIXES.    (line 3)
    * NOTE-2-7-GUARDS:                       NOTE-2-7-GUARDS.       (line 3)
    * NOTE-2-7-NEW-FUNCTIONALITY:            NOTE-2-7-NEW-FUNCTIONALITY.
                                                                    (line 3)
    * NOTE-2-7-OTHER:                        NOTE-2-7-OTHER.        (line 3)
    * NOTE-2-7-PROOF-CHECKER:                NOTE-2-7-PROOF-CHECKER.
                                                                    (line 3)
    * NOTE-2-7-PROOFS:                       NOTE-2-7-PROOFS.       (line 3)
    * NOTE-2-7-RULES:                        NOTE-2-7-RULES.        (line 3)
    * NOTE-2-7-SYSTEM:                       NOTE-2-7-SYSTEM.       (line 3)
    * NOTE-2-8:                              NOTE-2-8.              (line 3)
    * NOTE-2-8(R):                           NOTE-2-8(R).           (line 3)
    * NOTE-2-8-BUG-FIXES:                    NOTE-2-8-BUG-FIXES.    (line 3)
    * NOTE-2-8-GUARDS:                       NOTE-2-8-GUARDS.       (line 3)
    * NOTE-2-8-NEW-FUNCTIONALITY:            NOTE-2-8-NEW-FUNCTIONALITY.
                                                                    (line 3)
    * NOTE-2-8-ORDINALS:                     NOTE-2-8-ORDINALS.     (line 3)
    * NOTE-2-8-OTHER:                        NOTE-2-8-OTHER.        (line 3)
    * NOTE-2-8-PROOF-CHECKER:                NOTE-2-8-PROOF-CHECKER.
                                                                    (line 3)
    * NOTE-2-8-PROOFS:                       NOTE-2-8-PROOFS.       (line 3)
    * NOTE-2-8-RULES:                        NOTE-2-8-RULES.        (line 3)
    * NOTE-2-8-SYSTEM:                       NOTE-2-8-SYSTEM.       (line 3)
    * NOTE-2-9:                              NOTE-2-9.              (line 3)
    * NOTE-2-9(R):                           NOTE-2-9(R).           (line 3)
    * NOTE-2-9-1:                            NOTE-2-9-1.            (line 3)
    * NOTE-2-9-2:                            NOTE-2-9-2.            (line 3)
    * NOTE-2-9-3:                            NOTE-2-9-3.            (line 3)
    * NOTE-2-9-3-PPR-CHANGE:                 NOTE-2-9-3-PPR-CHANGE. (line 3)
    * NOTE-2-9-4:                            NOTE-2-9-4.            (line 3)
    * NOTE-2-9-5:                            NOTE-2-9-5.            (line 3)
    * NOTE-3-0:                              NOTE-3-0.              (line 3)
    * NOTE-3-0(R):                           NOTE-3-0(R).           (line 3)
    * NOTE-3-0-1:                            NOTE-3-0-1.            (line 3)
    * NOTE-3-0-1(R):                         NOTE-3-0-1(R).         (line 3)
    * NOTE-3-0-2:                            NOTE-3-0-2.            (line 3)
    * NOTE-3-1:                              NOTE-3-1.              (line 3)
    * NOTE-3-1(R):                           NOTE-3-1(R).           (line 3)
    * NOTE-3-2:                              NOTE-3-2.              (line 3)
    * NOTE-3-2(R):                           NOTE-3-2(R).           (line 3)
    * NOTE-3-2-1:                            NOTE-3-2-1.            (line 3)
    * NOTE-3-2-1(R):                         NOTE-3-2-1(R).         (line 3)
    * NOTE-3-3:                              NOTE-3-3.              (line 3)
    * NOTE-3-3(R):                           NOTE-3-3(R).           (line 3)
    * NOTE-3-4:                              NOTE-3-4.              (line 3)
    * NOTE-3-4(R):                           NOTE-3-4(R).           (line 3)
    * NOTE-3-5:                              NOTE-3-5.              (line 3)
    * NOTE-3-5(R):                           NOTE-3-5(R).           (line 3)
    * NOTE-3-6:                              NOTE-3-6.              (line 3)
    * NOTE-3-6(R):                           NOTE-3-6(R).           (line 3)
    * NOTE-3-6-1:                            NOTE-3-6-1.            (line 3)
    * NOTE-4-0:                              NOTE-4-0.              (line 3)
    * NOTE-4-0(R):                           NOTE-4-0(R).           (line 3)
    * NOTE-4-0-WORMHOLE-CHANGES:             NOTE-4-0-WORMHOLE-CHANGES.
                                                                    (line 3)
    * NOTE-4-1:                              NOTE-4-1.              (line 3)
    * NOTE-4-1(R):                           NOTE-4-1(R).           (line 3)
    * NOTE-4-2:                              NOTE-4-2.              (line 3)
    * NOTE-4-2(R):                           NOTE-4-2(R).           (line 3)
    * NOTE-4-3:                              NOTE-4-3.              (line 3)
    * NOTE-4-3(R):                           NOTE-4-3(R).           (line 3)
    * NOTE-5-0:                              NOTE-5-0.              (line 3)
    * NOTE-6-0:                              NOTE-6-0.              (line 3)
    * NOTE-6-1:                              NOTE-6-1.              (line 3)
    * NOTE-6-2:                              NOTE-6-2.              (line 3)
    * NOTE-6-3:                              NOTE-6-3.              (line 3)
    * NOTE1:                                 NOTE1.                 (line 3)
    * NOTE2:                                 NOTE2.                 (line 3)
    * NOTE3:                                 NOTE3.                 (line 3)
    * NOTE4:                                 NOTE4.                 (line 3)
    * NOTE5:                                 NOTE5.                 (line 3)
    * NOTE6:                                 NOTE6.                 (line 3)
    * NOTE7:                                 NOTE7.                 (line 3)
    * NOTE8:                                 NOTE8.                 (line 3)
    * NOTE8-UPDATE:                          NOTE8-UPDATE.          (line 3)
    * NOTE9:                                 NOTE9.                 (line 3)
    * NQTHM-TO-ACL2:                         NQTHM-TO-ACL2.         (line 3)
    * NTH:                                   NTH.                   (line 3)
    * NTH-ALIASES-TABLE:                     NTH-ALIASES-TABLE.     (line 3)
    * NTHCDR:                                NTHCDR.                (line 3)
    * NU-REWRITER:                           NU-REWRITER.           (line 3)
    * NULL:                                  NULL.                  (line 3)
    * NUMBER-SUBTREES:                       NUMBER-SUBTREES.       (line 3)
    * Numbers in ACL2:                       Numbers in ACL2.       (line 3)
    * NUMERATOR:                             NUMERATOR.             (line 3)
    * O-FINP:                                O-FINP.                (line 3)
    * O-FIRST-COEFF:                         O-FIRST-COEFF.         (line 3)
    * O-FIRST-EXPT:                          O-FIRST-EXPT.          (line 3)
    * O-INFP:                                O-INFP.                (line 3)
    * O-P:                                   O-P.                   (line 3)
    * O-RST:                                 O-RST.                 (line 3)
    * O<:                                    O<.                    (line 3)
    * O<=:                                   O<=.                   (line 3)
    * O>:                                    O>.                    (line 3)
    * O>=:                                   O>=.                   (line 3)
    * OBDD:                                  OBDD.                  (line 3)
    * OBSERVATION:                           OBSERVATION.           (line 3)
    * OBSERVATION-CW:                        OBSERVATION-CW.        (line 3)
    * ODDP:                                  ODDP.                  (line 3)
    * OK-IF:                                 OK-IF.                 (line 3)
    * On the Naming of Subgoals:             On the Naming of Subgoals.
                                                                    (line 3)
    * OOPS:                                  OOPS.                  (line 3)
    * OPEN-INPUT-CHANNEL:                    OPEN-INPUT-CHANNEL.    (line 3)
    * OPEN-INPUT-CHANNEL-P:                  OPEN-INPUT-CHANNEL-P.  (line 3)
    * OPEN-OUTPUT-CHANNEL:                   OPEN-OUTPUT-CHANNEL.   (line 3)
    * OPEN-OUTPUT-CHANNEL!:                  OPEN-OUTPUT-CHANNEL!.  (line 3)
    * OPEN-OUTPUT-CHANNEL-P:                 OPEN-OUTPUT-CHANNEL-P. (line 3)
    * OPEN-TRACE-FILE:                       OPEN-TRACE-FILE.       (line 3)
    * OPTIMIZE:                              OPTIMIZE.              (line 3)
    * OR:                                    OR.                    (line 3)
    * ORACLE-APPLY:                          ORACLE-APPLY.          (line 3)
    * ORACLE-APPLY-RAW:                      ORACLE-APPLY-RAW.      (line 3)
    * ORACLE-FUNCALL:                        ORACLE-FUNCALL.        (line 3)
    * ORDINALS:                              ORDINALS.              (line 3)
    * OTF-FLG:                               OTF-FLG.               (line 3)
    * OTHER:                                 OTHER.                 (line 3)
    * Other Requirements:                    Other Requirements.    (line 3)
    * OUTPUT-TO-FILE:                        OUTPUT-TO-FILE.        (line 3)
    * OVERRIDE-HINTS:                        OVERRIDE-HINTS.        (line 3)
    * Overview of the Expansion of ENDP in the Base Case: Overview of the Expansion of ENDP in the Base Case.
                                                                    (line 3)
    * Overview of the Expansion of ENDP in the Induction Step: Overview of the Expansion of ENDP in the Induction Step.
                                                                    (line 3)
    * Overview of the Final Simplification in the Base Case: Overview of the Final Simplification in the Base Case.
                                                                    (line 3)
    * Overview of the Proof of a Trivial Consequence: Overview of the Proof of a Trivial Consequence.
                                                                    (line 3)
    * Overview of the Simplification of the Base Case to T: Overview of the Simplification of the Base Case to T.
                                                                    (line 3)
    * Overview of the Simplification of the Induction Conclusion: Overview of the Simplification of the Induction Conclusion.
                                                                    (line 3)
    * Overview of the Simplification of the Induction Step to T: Overview of the Simplification of the Induction Step to T.
                                                                    (line 3)
    * P!:                                    P!.                    (line 3)
    * PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS: PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS.
                                                                    (line 3)
    * Pages Written Especially for the Tours: Pages Written Especially for the Tours.
                                                                    (line 3)
    * PAIRLIS:                               PAIRLIS.               (line 3)
    * PAIRLIS$:                              PAIRLIS$.              (line 3)
    * PAND:                                  PAND.                  (line 3)
    * PARALLEL:                              PARALLEL.              (line 3)
    * PARALLEL-EXECUTION:                    PARALLEL-EXECUTION.    (line 3)
    * PARALLEL-PROGRAMMING:                  PARALLEL-PROGRAMMING.  (line 3)
    * PARALLEL-PROOF:                        PARALLEL-PROOF.        (line 3)
    * PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION: PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION.
                                                                    (line 3)
    * PARALLELISM:                           PARALLELISM.           (line 3)
    * PARALLELISM-AT-THE-TOP-LEVEL:          PARALLELISM-AT-THE-TOP-LEVEL.
                                                                    (line 3)
    * PARALLELISM-BUILD:                     PARALLELISM-BUILD.     (line 3)
    * PARALLELISM-PERFORMANCE:               PARALLELISM-PERFORMANCE.
                                                                    (line 3)
    * PARALLELISM-TUTORIAL:                  PARALLELISM-TUTORIAL.  (line 3)
    * PARGS:                                 PARGS.                 (line 3)
    * PATHNAME:                              PATHNAME.              (line 3)
    * PBT:                                   PBT.                   (line 3)
    * PC:                                    PC.                    (line 3)
    * PCB:                                   PCB.                   (line 3)
    * PCB!:                                  PCB!.                  (line 3)
    * PCS:                                   PCS.                   (line 3)
    * PE:                                    PE.                    (line 3)
    * PE!:                                   PE!.                   (line 3)
    * PEEK-CHAR$:                            PEEK-CHAR$.            (line 3)
    * Perhaps:                               Perhaps.               (line 3)
    * PF:                                    PF.                    (line 3)
    * PKG-IMPORTS:                           PKG-IMPORTS.           (line 3)
    * PKG-WITNESS:                           PKG-WITNESS.           (line 3)
    * PL:                                    PL.                    (line 3)
    * PL2:                                   PL2.                   (line 3)
    * PLET:                                  PLET.                  (line 3)
    * PLUSP:                                 PLUSP.                 (line 3)
    * Popping out of an Inductive Proof:     Popping out of an Inductive Proof.
                                                                    (line 3)
    * POR:                                   POR.                   (line 3)
    * PORTCULLIS:                            PORTCULLIS.            (line 3)
    * POSITION:                              POSITION.              (line 3)
    * POSITION-EQ:                           POSITION-EQ.           (line 3)
    * POSITION-EQUAL:                        POSITION-EQUAL.        (line 3)
    * POSP:                                  POSP.                  (line 3)
    * POST-INDUCTION-KEY-CHECKPOINTS:        POST-INDUCTION-KEY-CHECKPOINTS.
                                                                    (line 3)
    * PPROGN:                                PPROGN.                (line 3)
    * PR:                                    PR.                    (line 3)
    * PR!:                                   PR!.                   (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES:     PRACTICE-FORMULATING-STRONG-RULES.
                                                                    (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES-1:   PRACTICE-FORMULATING-STRONG-RULES-1.
                                                                    (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES-2:   PRACTICE-FORMULATING-STRONG-RULES-2.
                                                                    (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES-3:   PRACTICE-FORMULATING-STRONG-RULES-3.
                                                                    (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES-4:   PRACTICE-FORMULATING-STRONG-RULES-4.
                                                                    (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES-5:   PRACTICE-FORMULATING-STRONG-RULES-5.
                                                                    (line 3)
    * PRACTICE-FORMULATING-STRONG-RULES-6:   PRACTICE-FORMULATING-STRONG-RULES-6.
                                                                    (line 3)
    * PRINC$:                                PRINC$.                (line 3)
    * PRINT-CONTROL:                         PRINT-CONTROL.         (line 3)
    * PRINT-DOC-START-COLUMN:                PRINT-DOC-START-COLUMN.
                                                                    (line 3)
    * PRINT-GV:                              PRINT-GV.              (line 3)
    * PRINT-OBJECT$:                         PRINT-OBJECT$.         (line 3)
    * PRINT-SUMMARY-USER:                    PRINT-SUMMARY-USER.    (line 3)
    * PRINTING-TO-STRINGS:                   PRINTING-TO-STRINGS.   (line 3)
    * PROFILE:                               PROFILE.               (line 3)
    * PROG2$:                                PROG2$.                (line 3)
    * PROGN:                                 PROGN.                 (line 3)
    * PROGN!:                                PROGN!.                (line 3)
    * PROGN$:                                PROGN$.                (line 3)
    * PROGRAM:                               PROGRAM.               (line 3)
    * PROGRAMMING:                           PROGRAMMING.           (line 3)
    * PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED: PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED.
                                                                    (line 3)
    * PROGRAMMING-WITH-STATE:                PROGRAMMING-WITH-STATE.
                                                                    (line 3)
    * PROMPT:                                PROMPT.                (line 3)
    * PROOF-CHECKER:                         PROOF-CHECKER.         (line 3)
    * PROOF-CHECKER-COMMANDS:                PROOF-CHECKER-COMMANDS.
                                                                    (line 3)
    * PROOF-OF-WELL-FOUNDEDNESS:             PROOF-OF-WELL-FOUNDEDNESS.
                                                                    (line 3)
    * PROOF-SUPPORTERS-ALIST:                PROOF-SUPPORTERS-ALIST.
                                                                    (line 3)
    * PROOF-TREE:                            PROOF-TREE.            (line 3)
    * PROOF-TREE-DETAILS:                    PROOF-TREE-DETAILS.    (line 3)
    * PROOF-TREE-EXAMPLES:                   PROOF-TREE-EXAMPLES.   (line 3)
    * PROOFS-CO:                             PROOFS-CO.             (line 3)
    * PROPER-CONSP:                          PROPER-CONSP.          (line 3)
    * PROPS:                                 PROPS.                 (line 3)
    * Proving Theorems about Models:         Proving Theorems about Models.
                                                                    (line 3)
    * PROVISIONAL-CERTIFICATION:             PROVISIONAL-CERTIFICATION.
                                                                    (line 3)
    * PSEUDO-TERMP:                          PSEUDO-TERMP.          (line 3)
    * PSO:                                   PSO.                   (line 3)
    * PSO!:                                  PSO!.                  (line 3)
    * PSOF:                                  PSOF.                  (line 3)
    * PSOG:                                  PSOG.                  (line 3)
    * PSTACK:                                PSTACK.                (line 3)
    * PUFF:                                  PUFF.                  (line 3)
    * PUFF*:                                 PUFF*.                 (line 3)
    * PUSH-UNTOUCHABLE:                      PUSH-UNTOUCHABLE.      (line 3)
    * PUT-ASSOC:                             PUT-ASSOC.             (line 3)
    * PUT-ASSOC-EQ:                          PUT-ASSOC-EQ.          (line 3)
    * PUT-ASSOC-EQL:                         PUT-ASSOC-EQL.         (line 3)
    * PUT-ASSOC-EQUAL:                       PUT-ASSOC-EQUAL.       (line 3)
    * PUTPROP:                               PUTPROP.               (line 3)
    * Q:                                     Q.                     (line 3)
    * QUANTIFIER-TUTORIAL:                   QUANTIFIER-TUTORIAL.   (line 3)
    * QUANTIFIERS:                           QUANTIFIERS.           (line 3)
    * QUANTIFIERS-USING-DEFUN-SK:            QUANTIFIERS-USING-DEFUN-SK.
                                                                    (line 3)
    * QUANTIFIERS-USING-DEFUN-SK-EXTENDED:   QUANTIFIERS-USING-DEFUN-SK-EXTENDED.
                                                                    (line 3)
    * QUANTIFIERS-USING-RECURSION:           QUANTIFIERS-USING-RECURSION.
                                                                    (line 3)
    * QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP: QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP.
                                                                    (line 3)
    * QUIT:                                  QUIT.                  (line 3)
    * QUOTE:                                 QUOTE.                 (line 3)
    * R-EQLABLE-ALISTP:                      R-EQLABLE-ALISTP.      (line 3)
    * R-SYMBOL-ALISTP:                       R-SYMBOL-ALISTP.       (line 3)
    * RANDOM$:                               RANDOM$.               (line 3)
    * RASSOC:                                RASSOC.                (line 3)
    * RASSOC-EQ:                             RASSOC-EQ.             (line 3)
    * RASSOC-EQUAL:                          RASSOC-EQUAL.          (line 3)
    * RATIONAL-LISTP:                        RATIONAL-LISTP.        (line 3)
    * RATIONALP:                             RATIONALP.             (line 3)
    * READ-BYTE$:                            READ-BYTE$.            (line 3)
    * READ-CHAR$:                            READ-CHAR$.            (line 3)
    * READ-OBJECT:                           READ-OBJECT.           (line 3)
    * READ-RUN-TIME:                         READ-RUN-TIME.         (line 3)
    * REAL:                                  REAL.                  (line 3)
    * REAL-LISTP:                            REAL-LISTP.            (line 3)
    * REAL/RATIONALP:                        REAL/RATIONALP.        (line 3)
    * REALFIX:                               REALFIX.               (line 3)
    * REALPART:                              REALPART.              (line 3)
    * REBUILD:                               REBUILD.               (line 3)
    * REDEF:                                 REDEF.                 (line 3)
    * REDEF!:                                REDEF!.                (line 3)
    * REDEF+:                                REDEF+.                (line 3)
    * REDEF-:                                REDEF-.                (line 3)
    * REDEFINED-NAMES:                       REDEFINED-NAMES.       (line 3)
    * REDEFINING-PROGRAMS:                   REDEFINING-PROGRAMS.   (line 3)
    * REDO-FLAT:                             REDO-FLAT.             (line 3)
    * REDUNDANT-ENCAPSULATE:                 REDUNDANT-ENCAPSULATE. (line 3)
    * REDUNDANT-EVENTS:                      REDUNDANT-EVENTS.      (line 3)
    * REFINEMENT:                            REFINEMENT.            (line 3)
    * REGENERATE-TAU-DATABASE:               REGENERATE-TAU-DATABASE.
                                                                    (line 3)
    * REGRESSION:                            REGRESSION.            (line 3)
    * RELEASE-NOTES:                         RELEASE-NOTES.         (line 3)
    * REM:                                   REM.                   (line 3)
    * REMOVE:                                REMOVE.                (line 3)
    * REMOVE-BINOP:                          REMOVE-BINOP.          (line 3)
    * REMOVE-CUSTOM-KEYWORD-HINT:            REMOVE-CUSTOM-KEYWORD-HINT.
                                                                    (line 3)
    * REMOVE-DEFAULT-HINTS:                  REMOVE-DEFAULT-HINTS.  (line 3)
    * REMOVE-DEFAULT-HINTS!:                 REMOVE-DEFAULT-HINTS!. (line 3)
    * REMOVE-DIVE-INTO-MACRO:                REMOVE-DIVE-INTO-MACRO.
                                                                    (line 3)
    * REMOVE-DUPLICATES:                     REMOVE-DUPLICATES.     (line 3)
    * REMOVE-DUPLICATES-EQ:                  REMOVE-DUPLICATES-EQ.  (line 3)
    * REMOVE-DUPLICATES-EQUAL:               REMOVE-DUPLICATES-EQUAL.
                                                                    (line 3)
    * REMOVE-EQ:                             REMOVE-EQ.             (line 3)
    * REMOVE-EQUAL:                          REMOVE-EQUAL.          (line 3)
    * REMOVE-INVISIBLE-FNS:                  REMOVE-INVISIBLE-FNS.  (line 3)
    * REMOVE-MACRO-ALIAS:                    REMOVE-MACRO-ALIAS.    (line 3)
    * REMOVE-MACRO-FN:                       REMOVE-MACRO-FN.       (line 3)
    * REMOVE-NTH-ALIAS:                      REMOVE-NTH-ALIAS.      (line 3)
    * REMOVE-OVERRIDE-HINTS:                 REMOVE-OVERRIDE-HINTS. (line 3)
    * REMOVE-OVERRIDE-HINTS!:                REMOVE-OVERRIDE-HINTS!.
                                                                    (line 3)
    * REMOVE-RAW-ARITY:                      REMOVE-RAW-ARITY.      (line 3)
    * REMOVE-UNTOUCHABLE:                    REMOVE-UNTOUCHABLE.    (line 3)
    * REMOVE1:                               REMOVE1.               (line 3)
    * REMOVE1-EQ:                            REMOVE1-EQ.            (line 3)
    * REMOVE1-EQUAL:                         REMOVE1-EQUAL.         (line 3)
    * REORDER:                               REORDER.               (line 3)
    * RESET-FC-REPORTING:                    RESET-FC-REPORTING.    (line 3)
    * RESET-KILL-RING:                       RESET-KILL-RING.       (line 3)
    * RESET-LD-SPECIALS:                     RESET-LD-SPECIALS.     (line 3)
    * RESET-PREHISTORY:                      RESET-PREHISTORY.      (line 3)
    * RESET-PRINT-CONTROL:                   RESET-PRINT-CONTROL.   (line 3)
    * RESIZE-LIST:                           RESIZE-LIST.           (line 3)
    * REST:                                  REST.                  (line 3)
    * RESTORE-MEMOIZATION-SETTINGS:          RESTORE-MEMOIZATION-SETTINGS.
                                                                    (line 3)
    * RESTRICT:                              RESTRICT.              (line 3)
    * RETRIEVE:                              RETRIEVE.              (line 3)
    * RETURN-LAST:                           RETURN-LAST.           (line 3)
    * RETURN-LAST-TABLE:                     RETURN-LAST-TABLE.     (line 3)
    * REVAPPEND:                             REVAPPEND.             (line 3)
    * REVERSE:                               REVERSE.               (line 3)
    * Revisiting the Admission of App:       Revisiting the Admission of App.
                                                                    (line 3)
    * REWRITE:                               REWRITE.               (line 3)
    * Rewrite Rules are Generated from DEFTHM Events: Rewrite Rules are Generated from DEFTHM Events.
                                                                    (line 3)
    * REWRITE-STACK-LIMIT:                   REWRITE-STACK-LIMIT.   (line 3)
    * RFIX:                                  RFIX.                  (line 3)
    * ROUND:                                 ROUND.                 (line 3)
    * RULE-CLASSES:                          RULE-CLASSES.          (line 3)
    * RULE-NAMES:                            RULE-NAMES.            (line 3)
    * RULER-EXTENDERS:                       RULER-EXTENDERS.       (line 3)
    * RUNE:                                  RUNE.                  (line 3)
    * Running Models:                        Running Models.        (line 3)
    * SAVE-AND-CLEAR-MEMOIZATION-SETTINGS:   SAVE-AND-CLEAR-MEMOIZATION-SETTINGS.
                                                                    (line 3)
    * SAVE-EXEC:                             SAVE-EXEC.             (line 3)
    * SAVING-AND-RESTORING:                  SAVING-AND-RESTORING.  (line 3)
    * SEARCH:                                SEARCH.                (line 3)
    * SECOND:                                SECOND.                (line 3)
    * SERIALIZE:                             SERIALIZE.             (line 3)
    * SERIALIZE-ALTERNATIVES:                SERIALIZE-ALTERNATIVES.
                                                                    (line 3)
    * SERIALIZE-IN-BOOKS:                    SERIALIZE-IN-BOOKS.    (line 3)
    * SERIALIZE-READ:                        SERIALIZE-READ.        (line 3)
    * SERIALIZE-WRITE:                       SERIALIZE-WRITE.       (line 3)
    * SET-ABSSTOBJ-DEBUG:                    SET-ABSSTOBJ-DEBUG.    (line 3)
    * SET-ACCUMULATED-PERSISTENCE:           SET-ACCUMULATED-PERSISTENCE.
                                                                    (line 3)
    * SET-BACKCHAIN-LIMIT:                   SET-BACKCHAIN-LIMIT.   (line 3)
    * SET-BODY:                              SET-BODY.              (line 3)
    * SET-BOGUS-DEFUN-HINTS-OK:              SET-BOGUS-DEFUN-HINTS-OK.
                                                                    (line 3)
    * SET-BOGUS-MUTUAL-RECURSION-OK:         SET-BOGUS-MUTUAL-RECURSION-OK.
                                                                    (line 3)
    * SET-CASE-SPLIT-LIMITATIONS:            SET-CASE-SPLIT-LIMITATIONS.
                                                                    (line 3)
    * SET-CBD:                               SET-CBD.               (line 3)
    * SET-CHECKPOINT-SUMMARY-LIMIT:          SET-CHECKPOINT-SUMMARY-LIMIT.
                                                                    (line 3)
    * SET-COMPILE-FNS:                       SET-COMPILE-FNS.       (line 3)
    * SET-COMPILER-ENABLED:                  SET-COMPILER-ENABLED.  (line 3)
    * SET-DEBUGGER-ENABLE:                   SET-DEBUGGER-ENABLE.   (line 3)
    * SET-DEFAULT-BACKCHAIN-LIMIT:           SET-DEFAULT-BACKCHAIN-LIMIT.
                                                                    (line 3)
    * SET-DEFAULT-HINTS:                     SET-DEFAULT-HINTS.     (line 3)
    * SET-DEFAULT-HINTS!:                    SET-DEFAULT-HINTS!.    (line 3)
    * SET-DEFERRED-TTAG-NOTES:               SET-DEFERRED-TTAG-NOTES.
                                                                    (line 3)
    * SET-DIFFERENCE$:                       SET-DIFFERENCE$.       (line 3)
    * SET-DIFFERENCE-EQ:                     SET-DIFFERENCE-EQ.     (line 3)
    * SET-DIFFERENCE-EQUAL:                  SET-DIFFERENCE-EQUAL.  (line 3)
    * SET-DIFFERENCE-THEORIES:               SET-DIFFERENCE-THEORIES.
                                                                    (line 3)
    * SET-ENFORCE-REDUNDANCY:                SET-ENFORCE-REDUNDANCY.
                                                                    (line 3)
    * SET-EVISC-TUPLE:                       SET-EVISC-TUPLE.       (line 3)
    * SET-FC-CRITERIA:                       SET-FC-CRITERIA.       (line 3)
    * SET-FC-REPORT-ON-THE-FLY:              SET-FC-REPORT-ON-THE-FLY.
                                                                    (line 3)
    * SET-FMT-HARD-RIGHT-MARGIN:             SET-FMT-HARD-RIGHT-MARGIN.
                                                                    (line 3)
    * SET-FMT-SOFT-RIGHT-MARGIN:             SET-FMT-SOFT-RIGHT-MARGIN.
                                                                    (line 3)
    * SET-GAG-MODE:                          SET-GAG-MODE.          (line 3)
    * SET-GUARD-CHECKING:                    SET-GUARD-CHECKING.    (line 3)
    * SET-IGNORE-DOC-STRING-ERROR:           SET-IGNORE-DOC-STRING-ERROR.
                                                                    (line 3)
    * SET-IGNORE-OK:                         SET-IGNORE-OK.         (line 3)
    * SET-INHIBIT-OUTPUT-LST:                SET-INHIBIT-OUTPUT-LST.
                                                                    (line 3)
    * SET-INHIBIT-WARNINGS:                  SET-INHIBIT-WARNINGS.  (line 3)
    * SET-INHIBIT-WARNINGS!:                 SET-INHIBIT-WARNINGS!. (line 3)
    * SET-INHIBITED-SUMMARY-TYPES:           SET-INHIBITED-SUMMARY-TYPES.
                                                                    (line 3)
    * SET-INVISIBLE-FNS-TABLE:               SET-INVISIBLE-FNS-TABLE.
                                                                    (line 3)
    * SET-IPRINT:                            SET-IPRINT.            (line 3)
    * SET-IRRELEVANT-FORMALS-OK:             SET-IRRELEVANT-FORMALS-OK.
                                                                    (line 3)
    * SET-LD-KEYWORD-ALIASES:                SET-LD-KEYWORD-ALIASES.
                                                                    (line 3)
    * SET-LD-KEYWORD-ALIASES!:               SET-LD-KEYWORD-ALIASES!.
                                                                    (line 3)
    * SET-LD-REDEFINITION-ACTION:            SET-LD-REDEFINITION-ACTION.
                                                                    (line 3)
    * SET-LD-SKIP-PROOFS:                    SET-LD-SKIP-PROOFS.    (line 3)
    * SET-LD-SKIP-PROOFSP:                   SET-LD-SKIP-PROOFSP.   (line 3)
    * SET-LET*-ABSTRACTION:                  SET-LET*-ABSTRACTION.  (line 3)
    * SET-LET*-ABSTRACTIONP:                 SET-LET*-ABSTRACTIONP. (line 3)
    * SET-MATCH-FREE-DEFAULT:                SET-MATCH-FREE-DEFAULT.
                                                                    (line 3)
    * SET-MATCH-FREE-ERROR:                  SET-MATCH-FREE-ERROR.  (line 3)
    * SET-MEASURE-FUNCTION:                  SET-MEASURE-FUNCTION.  (line 3)
    * SET-NON-LINEAR:                        SET-NON-LINEAR.        (line 3)
    * SET-NON-LINEARP:                       SET-NON-LINEARP.       (line 3)
    * SET-NU-REWRITER-MODE:                  SET-NU-REWRITER-MODE.  (line 3)
    * SET-OVERRIDE-HINTS:                    SET-OVERRIDE-HINTS.    (line 3)
    * SET-OVERRIDE-HINTS!:                   SET-OVERRIDE-HINTS!.   (line 3)
    * SET-PARALLEL-EXECUTION:                SET-PARALLEL-EXECUTION.
                                                                    (line 3)
    * SET-PRINT-BASE:                        SET-PRINT-BASE.        (line 3)
    * SET-PRINT-CASE:                        SET-PRINT-CASE.        (line 3)
    * SET-PRINT-CIRCLE:                      SET-PRINT-CIRCLE.      (line 3)
    * SET-PRINT-CLAUSE-IDS:                  SET-PRINT-CLAUSE-IDS.  (line 3)
    * SET-PRINT-ESCAPE:                      SET-PRINT-ESCAPE.      (line 3)
    * SET-PRINT-LENGTH:                      SET-PRINT-LENGTH.      (line 3)
    * SET-PRINT-LEVEL:                       SET-PRINT-LEVEL.       (line 3)
    * SET-PRINT-LINES:                       SET-PRINT-LINES.       (line 3)
    * SET-PRINT-RADIX:                       SET-PRINT-RADIX.       (line 3)
    * SET-PRINT-READABLY:                    SET-PRINT-READABLY.    (line 3)
    * SET-PRINT-RIGHT-MARGIN:                SET-PRINT-RIGHT-MARGIN.
                                                                    (line 3)
    * SET-PROVER-STEP-LIMIT:                 SET-PROVER-STEP-LIMIT. (line 3)
    * SET-RAW-MODE:                          SET-RAW-MODE.          (line 3)
    * SET-RAW-MODE-ON!:                      SET-RAW-MODE-ON!.      (line 3)
    * SET-RAW-PROOF-FORMAT:                  SET-RAW-PROOF-FORMAT.  (line 3)
    * SET-REWRITE-STACK-LIMIT:               SET-REWRITE-STACK-LIMIT.
                                                                    (line 3)
    * SET-RULER-EXTENDERS:                   SET-RULER-EXTENDERS.   (line 3)
    * SET-RW-CACHE-STATE:                    SET-RW-CACHE-STATE.    (line 3)
    * SET-RW-CACHE-STATE!:                   SET-RW-CACHE-STATE!.   (line 3)
    * SET-SAVED-OUTPUT:                      SET-SAVED-OUTPUT.      (line 3)
    * SET-SERIALIZE-CHARACTER:               SET-SERIALIZE-CHARACTER.
                                                                    (line 3)
    * SET-SPLITTER-OUTPUT:                   SET-SPLITTER-OUTPUT.   (line 3)
    * SET-STATE-OK:                          SET-STATE-OK.          (line 3)
    * SET-TAU-AUTO-MODE:                     SET-TAU-AUTO-MODE.     (line 3)
    * SET-TOTAL-PARALLELISM-WORK-LIMIT:      SET-TOTAL-PARALLELISM-WORK-LIMIT.
                                                                    (line 3)
    * SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR: SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR.
                                                                    (line 3)
    * SET-TRACE-EVISC-TUPLE:                 SET-TRACE-EVISC-TUPLE. (line 3)
    * SET-VERIFY-GUARDS-EAGERNESS:           SET-VERIFY-GUARDS-EAGERNESS.
                                                                    (line 3)
    * SET-WATERFALL-PARALLELISM:             SET-WATERFALL-PARALLELISM.
                                                                    (line 3)
    * SET-WATERFALL-PARALLELISM-HACKS-ENABLED: SET-WATERFALL-PARALLELISM-HACKS-ENABLED.
                                                                    (line 3)
    * SET-WATERFALL-PARALLELISM-HACKS-ENABLED!: SET-WATERFALL-PARALLELISM-HACKS-ENABLED!.
                                                                    (line 3)
    * SET-WATERFALL-PRINTING:                SET-WATERFALL-PRINTING.
                                                                    (line 3)
    * SET-WELL-FOUNDED-RELATION:             SET-WELL-FOUNDED-RELATION.
                                                                    (line 3)
    * SET-WORMHOLE-DATA:                     SET-WORMHOLE-DATA.     (line 3)
    * SET-WORMHOLE-ENTRY-CODE:               SET-WORMHOLE-ENTRY-CODE.
                                                                    (line 3)
    * SET-WRITE-ACL2X:                       SET-WRITE-ACL2X.       (line 3)
    * SETENV$:                               SETENV$.               (line 3)
    * SEVENTH:                               SEVENTH.               (line 3)
    * SHARP-BANG-READER:                     SHARP-BANG-READER.     (line 3)
    * SHARP-COMMA-READER:                    SHARP-COMMA-READER.    (line 3)
    * SHARP-DOT-READER:                      SHARP-DOT-READER.      (line 3)
    * SHARP-U-READER:                        SHARP-U-READER.        (line 3)
    * SHOW-ACCUMULATED-PERSISTENCE:          SHOW-ACCUMULATED-PERSISTENCE.
                                                                    (line 3)
    * SHOW-BDD:                              SHOW-BDD.              (line 3)
    * SHOW-BODIES:                           SHOW-BODIES.           (line 3)
    * SHOW-CUSTOM-KEYWORD-HINT-EXPANSION:    SHOW-CUSTOM-KEYWORD-HINT-EXPANSION.
                                                                    (line 3)
    * SHOW-FC-CRITERIA:                      SHOW-FC-CRITERIA.      (line 3)
    * SIGNATURE:                             SIGNATURE.             (line 3)
    * SIGNED-BYTE-P:                         SIGNED-BYTE-P.         (line 3)
    * SIGNUM:                                SIGNUM.                (line 3)
    * SIMPLE:                                SIMPLE.                (line 3)
    * SINGLE-THREADED-OBJECTS:               SINGLE-THREADED-OBJECTS.
                                                                    (line 3)
    * SIXTH:                                 SIXTH.                 (line 3)
    * SKIP-PROOFS:                           SKIP-PROOFS.           (line 3)
    * SLOW-ALIST-WARNING:                    SLOW-ALIST-WARNING.    (line 3)
    * SLOW-ARRAY-WARNING:                    SLOW-ARRAY-WARNING.    (line 3)
    * SOLUTION-TO-SIMPLE-EXAMPLE:            SOLUTION-TO-SIMPLE-EXAMPLE.
                                                                    (line 3)
    * SPEC-MV-LET:                           SPEC-MV-LET.           (line 3)
    * SPECIAL-CASES-FOR-REWRITE-RULES:       SPECIAL-CASES-FOR-REWRITE-RULES.
                                                                    (line 3)
    * SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES: SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES.
                                                                    (line 3)
    * SPECIOUS-SIMPLIFICATION:               SPECIOUS-SIMPLIFICATION.
                                                                    (line 3)
    * SPLITTER:                              SPLITTER.              (line 3)
    * SPLITTER-OUTPUT:                       SPLITTER-OUTPUT.       (line 3)
    * STANDARD-CHAR-LISTP:                   STANDARD-CHAR-LISTP.   (line 3)
    * STANDARD-CHAR-P:                       STANDARD-CHAR-P.       (line 3)
    * STANDARD-CO:                           STANDARD-CO.           (line 3)
    * STANDARD-OI:                           STANDARD-OI.           (line 3)
    * STANDARD-PART:                         STANDARD-PART.         (line 3)
    * STANDARD-STRING-ALISTP:                STANDARD-STRING-ALISTP.
                                                                    (line 3)
    * STANDARDP:                             STANDARDP.             (line 3)
    * START-PROOF-TREE:                      START-PROOF-TREE.      (line 3)
    * STARTUP:                               STARTUP.               (line 3)
    * STATE:                                 STATE.                 (line 3)
    * STATE-GLOBAL-LET*:                     STATE-GLOBAL-LET*.     (line 3)
    * STOBJ:                                 STOBJ.                 (line 3)
    * STOBJ-EXAMPLE-1:                       STOBJ-EXAMPLE-1.       (line 3)
    * STOBJ-EXAMPLE-1-DEFUNS:                STOBJ-EXAMPLE-1-DEFUNS.
                                                                    (line 3)
    * STOBJ-EXAMPLE-1-IMPLEMENTATION:        STOBJ-EXAMPLE-1-IMPLEMENTATION.
                                                                    (line 3)
    * STOBJ-EXAMPLE-1-PROOFS:                STOBJ-EXAMPLE-1-PROOFS.
                                                                    (line 3)
    * STOBJ-EXAMPLE-2:                       STOBJ-EXAMPLE-2.       (line 3)
    * STOBJ-EXAMPLE-3:                       STOBJ-EXAMPLE-3.       (line 3)
    * STOBJ-LET:                             STOBJ-LET.             (line 3)
    * STOBJS:                                STOBJS.                (line 3)
    * STOP-PROOF-TREE:                       STOP-PROOF-TREE.       (line 3)
    * STRING:                                STRING.                (line 3)
    * STRING-APPEND:                         STRING-APPEND.         (line 3)
    * STRING-DOWNCASE:                       STRING-DOWNCASE.       (line 3)
    * STRING-EQUAL:                          STRING-EQUAL.          (line 3)
    * STRING-LISTP:                          STRING-LISTP.          (line 3)
    * STRING-UPCASE:                         STRING-UPCASE.         (line 3)
    * STRING<:                               STRING<.               (line 3)
    * STRING<=:                              STRING<=.              (line 3)
    * STRING>:                               STRING>.               (line 3)
    * STRING>=:                              STRING>=.              (line 3)
    * STRINGP:                               STRINGP.               (line 3)
    * STRIP-CARS:                            STRIP-CARS.            (line 3)
    * STRIP-CDRS:                            STRIP-CDRS.            (line 3)
    * STRONG-REWRITE-RULES:                  STRONG-REWRITE-RULES.  (line 3)
    * SUBLIS:                                SUBLIS.                (line 3)
    * SUBSEQ:                                SUBSEQ.                (line 3)
    * SUBSETP:                               SUBSETP.               (line 3)
    * SUBSETP-EQ:                            SUBSETP-EQ.            (line 3)
    * SUBSETP-EQUAL:                         SUBSETP-EQUAL.         (line 3)
    * SUBST:                                 SUBST.                 (line 3)
    * SUBSTITUTE:                            SUBSTITUTE.            (line 3)
    * Subsumption of Induction Candidates in App Example: Subsumption of Induction Candidates in App Example.
                                                                    (line 3)
    * SUBVERSIVE-INDUCTIONS:                 SUBVERSIVE-INDUCTIONS. (line 3)
    * SUBVERSIVE-RECURSIONS:                 SUBVERSIVE-RECURSIONS. (line 3)
    * Suggested Inductions in the Associativity of App Example: Suggested Inductions in the Associativity of App Example.
                                                                    (line 3)
    * SWITCHES-PARAMETERS-AND-MODES:         SWITCHES-PARAMETERS-AND-MODES.
                                                                    (line 3)
    * SYMBOL-<:                              SYMBOL-<.              (line 3)
    * SYMBOL-ALISTP:                         SYMBOL-ALISTP.         (line 3)
    * SYMBOL-LISTP:                          SYMBOL-LISTP.          (line 3)
    * SYMBOL-NAME:                           SYMBOL-NAME.           (line 3)
    * SYMBOL-PACKAGE-NAME:                   SYMBOL-PACKAGE-NAME.   (line 3)
    * Symbolic Execution of Models:          Symbolic Execution of Models.
                                                                    (line 3)
    * SYMBOLP:                               SYMBOLP.               (line 3)
    * SYNTAX:                                SYNTAX.                (line 3)
    * SYNTAXP:                               SYNTAXP.               (line 3)
    * SYNTAXP-EXAMPLES:                      SYNTAXP-EXAMPLES.      (line 3)
    * SYS-CALL:                              SYS-CALL.              (line 3)
    * SYS-CALL+:                             SYS-CALL+.             (line 3)
    * SYS-CALL-STATUS:                       SYS-CALL-STATUS.       (line 3)
    * TABLE:                                 TABLE.                 (line 3)
    * TAKE:                                  TAKE.                  (line 3)
    * TAU-DATA:                              TAU-DATA.              (line 3)
    * TAU-DATABASE:                          TAU-DATABASE.          (line 3)
    * TAU-INTERVAL-DOM:                      TAU-INTERVAL-DOM.      (line 3)
    * TAU-INTERVAL-HI:                       TAU-INTERVAL-HI.       (line 3)
    * TAU-INTERVAL-HI-REL:                   TAU-INTERVAL-HI-REL.   (line 3)
    * TAU-INTERVAL-LO:                       TAU-INTERVAL-LO.       (line 3)
    * TAU-INTERVAL-LO-REL:                   TAU-INTERVAL-LO-REL.   (line 3)
    * TAU-INTERVALP:                         TAU-INTERVALP.         (line 3)
    * TAU-STATUS:                            TAU-STATUS.            (line 3)
    * TAU-SYSTEM:                            TAU-SYSTEM.            (line 3)
    * TENTH:                                 TENTH.                 (line 3)
    * TERM:                                  TERM.                  (line 3)
    * TERM-ORDER:                            TERM-ORDER.            (line 3)
    * TERM-TABLE:                            TERM-TABLE.            (line 3)
    * THE:                                   THE.                   (line 3)
    * The Admission of App:                  The Admission of App.  (line 3)
    * The Associativity of App:              The Associativity of App.
                                                                    (line 3)
    * The Base Case in the App Example:      The Base Case in the App Example.
                                                                    (line 3)
    * The End of the Flying Tour:            The End of the Flying Tour.
                                                                    (line 3)
    * The End of the Proof of the Associativity of App: The End of the Proof of the Associativity of App.
                                                                    (line 3)
    * The End of the Walking Tour:           The End of the Walking Tour.
                                                                    (line 3)
    * The Event Summary:                     The Event Summary.     (line 3)
    * The Expansion of ENDP in the Induction Step (Step 0): The Expansion of ENDP in the Induction Step (Step 0).
                                                                    (line 3)
    * The Expansion of ENDP in the Induction Step (Step 1): The Expansion of ENDP in the Induction Step (Step 1).
                                                                    (line 3)
    * The Expansion of ENDP in the Induction Step (Step 2): The Expansion of ENDP in the Induction Step (Step 2).
                                                                    (line 3)
    * The Falling Body Model:                The Falling Body Model.
                                                                    (line 3)
    * The Final Simplification in the Base Case (Step 0): The Final Simplification in the Base Case (Step 0).
                                                                    (line 3)
    * The Final Simplification in the Base Case (Step 1): The Final Simplification in the Base Case (Step 1).
                                                                    (line 3)
    * The Final Simplification in the Base Case (Step 2): The Final Simplification in the Base Case (Step 2).
                                                                    (line 3)
    * The Final Simplification in the Base Case (Step 3): The Final Simplification in the Base Case (Step 3).
                                                                    (line 3)
    * The First Application of the Associativity Rule: The First Application of the Associativity Rule.
                                                                    (line 3)
    * The Induction Scheme Selected for the App Example: The Induction Scheme Selected for the App Example.
                                                                    (line 3)
    * The Induction Step in the App Example: The Induction Step in the App Example.
                                                                    (line 3)
    * The Instantiation of the Induction Scheme: The Instantiation of the Induction Scheme.
                                                                    (line 3)
    * The Justification of the Induction Scheme: The Justification of the Induction Scheme.
                                                                    (line 3)
    * The Proof of the Associativity of App: The Proof of the Associativity of App.
                                                                    (line 3)
    * The Q.E.D. Message:                    The Q.E.D. Message.    (line 3)
    * The Rules used in the Associativity of App Proof: The Rules used in the Associativity of App Proof.
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 0): The Simplification of the Induction Conclusion (Step 0).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 1): The Simplification of the Induction Conclusion (Step 1).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 10): The Simplification of the Induction Conclusion (Step 10).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 11): The Simplification of the Induction Conclusion (Step 11).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 12): The Simplification of the Induction Conclusion (Step 12).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 2): The Simplification of the Induction Conclusion (Step 2).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 3): The Simplification of the Induction Conclusion (Step 3).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 4): The Simplification of the Induction Conclusion (Step 4).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 5): The Simplification of the Induction Conclusion (Step 5).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 6): The Simplification of the Induction Conclusion (Step 6).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 7): The Simplification of the Induction Conclusion (Step 7).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 8): The Simplification of the Induction Conclusion (Step 8).
                                                                    (line 3)
    * The Simplification of the Induction Conclusion (Step 9): The Simplification of the Induction Conclusion (Step 9).
                                                                    (line 3)
    * The Summary of the Proof of the Trivial Consequence: The Summary of the Proof of the Trivial Consequence.
                                                                    (line 3)
    * The Theorem that App is Associative:   The Theorem that App is Associative.
                                                                    (line 3)
    * The Time Taken to do the Associativity of App Proof: The Time Taken to do the Associativity of App Proof.
                                                                    (line 3)
    * The Tours:                             The Tours.             (line 3)
    * The WARNING about the Trivial Consequence: The WARNING about the Trivial Consequence.
                                                                    (line 3)
    * THE-METHOD:                            THE-METHOD.            (line 3)
    * THEORIES:                              THEORIES.              (line 3)
    * THEORIES-AND-PRIMITIVES:               THEORIES-AND-PRIMITIVES.
                                                                    (line 3)
    * THEORY:                                THEORY.                (line 3)
    * THEORY-FUNCTIONS:                      THEORY-FUNCTIONS.      (line 3)
    * THEORY-INVARIANT:                      THEORY-INVARIANT.      (line 3)
    * THIRD:                                 THIRD.                 (line 3)
    * THM:                                   THM.                   (line 3)
    * TIDBITS:                               TIDBITS.               (line 3)
    * TIME$:                                 TIME$.                 (line 3)
    * TIME-TRACKER:                          TIME-TRACKER.          (line 3)
    * TIME-TRACKER-TAU:                      TIME-TRACKER-TAU.      (line 3)
    * TIPS:                                  TIPS.                  (line 3)
    * TOGGLE-PC-MACRO:                       TOGGLE-PC-MACRO.       (line 3)
    * TOP-LEVEL:                             TOP-LEVEL.             (line 3)
    * TRACE:                                 TRACE.                 (line 3)
    * TRACE!:                                TRACE!.                (line 3)
    * TRACE$:                                TRACE$.                (line 3)
    * TRANS:                                 TRANS.                 (line 3)
    * TRANS!:                                TRANS!.                (line 3)
    * TRANS1:                                TRANS1.                (line 3)
    * TRUE-LIST-LISTP:                       TRUE-LIST-LISTP.       (line 3)
    * TRUE-LISTP:                            TRUE-LISTP.            (line 3)
    * TRUNCATE:                              TRUNCATE.              (line 3)
    * TRUST-TAG:                             TRUST-TAG.             (line 3)
    * TTAGS-SEEN:                            TTAGS-SEEN.            (line 3)
    * TTREE:                                 TTREE.                 (line 3)
    * TUTORIAL1-TOWERS-OF-HANOI:             TUTORIAL1-TOWERS-OF-HANOI.
                                                                    (line 3)
    * TUTORIAL2-EIGHTS-PROBLEM:              TUTORIAL2-EIGHTS-PROBLEM.
                                                                    (line 3)
    * TUTORIAL3-PHONEBOOK-EXAMPLE:           TUTORIAL3-PHONEBOOK-EXAMPLE.
                                                                    (line 3)
    * TUTORIAL4-DEFUN-SK-EXAMPLE:            TUTORIAL4-DEFUN-SK-EXAMPLE.
                                                                    (line 3)
    * TUTORIAL5-MISCELLANEOUS-EXAMPLES:      TUTORIAL5-MISCELLANEOUS-EXAMPLES.
                                                                    (line 3)
    * TYPE:                                  TYPE.                  (line 3)
    * TYPE-PRESCRIPTION:                     TYPE-PRESCRIPTION.     (line 3)
    * TYPE-SET:                              TYPE-SET.              (line 3)
    * TYPE-SET-INVERTER:                     TYPE-SET-INVERTER.     (line 3)
    * TYPE-SPEC:                             TYPE-SPEC.             (line 3)
    * TYPESPEC-CHECK:                        TYPESPEC-CHECK.        (line 3)
    * U:                                     U.                     (line 3)
    * UBT:                                   UBT.                   (line 3)
    * UBT!:                                  UBT!.                  (line 3)
    * UBT-PREHISTORY:                        UBT-PREHISTORY.        (line 3)
    * UBU:                                   UBU.                   (line 3)
    * UBU!:                                  UBU!.                  (line 3)
    * UNARY-:                                UNARY--.               (line 3)
    * UNARY-/:                               UNARY-/.               (line 3)
    * UNCERTIFIED-BOOKS:                     UNCERTIFIED-BOOKS.     (line 3)
    * Undocumented Topic:                    Undocumented Topic.    (line 3)
    * UNION$:                                UNION$.                (line 3)
    * UNION-EQ:                              UNION-EQ.              (line 3)
    * UNION-EQUAL:                           UNION-EQUAL.           (line 3)
    * UNION-THEORIES:                        UNION-THEORIES.        (line 3)
    * UNIVERSAL-THEORY:                      UNIVERSAL-THEORY.      (line 3)
    * UNMEMOIZE:                             UNMEMOIZE.             (line 3)
    * UNMONITOR:                             UNMONITOR.             (line 3)
    * UNSAVE:                                UNSAVE.                (line 3)
    * UNSIGNED-BYTE-P:                       UNSIGNED-BYTE-P.       (line 3)
    * UNSUPPORTED-PARALLELISM-FEATURES:      UNSUPPORTED-PARALLELISM-FEATURES.
                                                                    (line 3)
    * UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES: UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES.
                                                                    (line 3)
    * UNTRACE$:                              UNTRACE$.              (line 3)
    * UNTRANS-TABLE:                         UNTRANS-TABLE.         (line 3)
    * UNTRANSLATE:                           UNTRANSLATE.           (line 3)
    * UPDATE-NTH:                            UPDATE-NTH.            (line 3)
    * UPPER-CASE-P:                          UPPER-CASE-P.          (line 3)
    * USE:                                   USE.                   (line 3)
    * USER-DEFINED-FUNCTIONS-TABLE:          USER-DEFINED-FUNCTIONS-TABLE.
                                                                    (line 3)
    * Using the Associativity of App to Prove a Trivial Consequence: Using the Associativity of App to Prove a Trivial Consequence.
                                                                    (line 3)
    * USING-COMPUTED-HINTS:                  USING-COMPUTED-HINTS.  (line 3)
    * USING-COMPUTED-HINTS-1:                USING-COMPUTED-HINTS-1.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-2:                USING-COMPUTED-HINTS-2.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-3:                USING-COMPUTED-HINTS-3.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-4:                USING-COMPUTED-HINTS-4.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-5:                USING-COMPUTED-HINTS-5.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-6:                USING-COMPUTED-HINTS-6.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-7:                USING-COMPUTED-HINTS-7.
                                                                    (line 3)
    * USING-COMPUTED-HINTS-8:                USING-COMPUTED-HINTS-8.
                                                                    (line 3)
    * USING-ENABLED-RULES:                   USING-ENABLED-RULES.   (line 3)
    * USING-TABLES-EFFICIENTLY:              USING-TABLES-EFFICIENTLY.
                                                                    (line 3)
    * VALUE-TRIPLE:                          VALUE-TRIPLE.          (line 3)
    * VERBOSE-PSTACK:                        VERBOSE-PSTACK.        (line 3)
    * VERIFY:                                VERIFY.                (line 3)
    * VERIFY-GUARDS:                         VERIFY-GUARDS.         (line 3)
    * VERIFY-GUARDS+:                        VERIFY-GUARDS+.        (line 3)
    * VERIFY-GUARDS-EAGERNESS:               VERIFY-GUARDS-EAGERNESS.
                                                                    (line 3)
    * VERIFY-GUARDS-FORMULA:                 VERIFY-GUARDS-FORMULA. (line 3)
    * VERIFY-TERMINATION:                    VERIFY-TERMINATION.    (line 3)
    * VERSION:                               VERSION.               (line 3)
    * WALKABOUT:                             WALKABOUT.             (line 3)
    * WATERFALL:                             WATERFALL.             (line 3)
    * WATERFALL-PARALLELISM:                 WATERFALL-PARALLELISM. (line 3)
    * WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION: WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION.
                                                                    (line 3)
    * WATERFALL-PRINTING:                    WATERFALL-PRINTING.    (line 3)
    * WELL-FOUNDED-RELATION:                 WELL-FOUNDED-RELATION. (line 3)
    * WET:                                   WET.                   (line 3)
    * What is a Mathematical Logic(Q):       What is a Mathematical Logic(Q).
                                                                    (line 3)
    * What is a Mechanical Theorem Prover(Q): What is a Mechanical Theorem Prover(Q).
                                                                    (line 3)
    * What is a Mechanical Theorem Prover(Q) (cont): What is a Mechanical Theorem Prover(Q) (cont).
                                                                    (line 3)
    * What Is ACL2(Q):                       What Is ACL2(Q).       (line 3)
    * What is Required of the User(Q):       What is Required of the User(Q).
                                                                    (line 3)
    * WHY-BRR:                               WHY-BRR.               (line 3)
    * WITH-FAST-ALIST:                       WITH-FAST-ALIST.       (line 3)
    * WITH-GUARD-CHECKING:                   WITH-GUARD-CHECKING.   (line 3)
    * WITH-LIVE-STATE:                       WITH-LIVE-STATE.       (line 3)
    * WITH-LOCAL-STATE:                      WITH-LOCAL-STATE.      (line 3)
    * WITH-LOCAL-STOBJ:                      WITH-LOCAL-STOBJ.      (line 3)
    * WITH-OUTPUT:                           WITH-OUTPUT.           (line 3)
    * WITH-OUTPUT-LOCK:                      WITH-OUTPUT-LOCK.      (line 3)
    * WITH-PROVER-STEP-LIMIT:                WITH-PROVER-STEP-LIMIT.
                                                                    (line 3)
    * WITH-PROVER-TIME-LIMIT:                WITH-PROVER-TIME-LIMIT.
                                                                    (line 3)
    * WITH-SERIALIZE-CHARACTER:              WITH-SERIALIZE-CHARACTER.
                                                                    (line 3)
    * WITH-STOLEN-ALIST:                     WITH-STOLEN-ALIST.     (line 3)
    * WITHOUT-EVISC:                         WITHOUT-EVISC.         (line 3)
    * WOF:                                   WOF.                   (line 3)
    * WORLD:                                 WORLD.                 (line 3)
    * WORMHOLE:                              WORMHOLE.              (line 3)
    * WORMHOLE-DATA:                         WORMHOLE-DATA.         (line 3)
    * WORMHOLE-ENTRY-CODE:                   WORMHOLE-ENTRY-CODE.   (line 3)
    * WORMHOLE-EVAL:                         WORMHOLE-EVAL.         (line 3)
    * WORMHOLE-IMPLEMENTATION:               WORMHOLE-IMPLEMENTATION.
                                                                    (line 3)
    * WORMHOLE-P:                            WORMHOLE-P.            (line 3)
    * WORMHOLE-STATUSP:                      WORMHOLE-STATUSP.      (line 3)
    * WRITE-BYTE$:                           WRITE-BYTE$.           (line 3)
    * XARGS:                                 XARGS.                 (line 3)
    * XOR:                                   XOR.                   (line 3)
    * You Must Think about the Use of a Formula as a Rule: You Must Think about the Use of a Formula as a Rule.
                                                                    (line 3)
    * ZERO-TEST-IDIOMS:                      ZERO-TEST-IDIOMS.      (line 3)
    * ZEROP:                                 ZEROP.                 (line 3)
    * ZIP:                                   ZIP.                   (line 3)
    * ZP:                                    ZP.                    (line 3)
    * ZPF:                                   ZPF.                   (line 3)
    
    
    acl2-sources/doc/EMACS/acl2-doc-emacs.info-20000664002132200015000000111101512222333541017633 0ustar  kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from
    acl2-doc-emacs.texinfo.
    
    This is documentation for ACL2 Version 6.3
    Copyright (C) 2013  University of Texas at Austin
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of Version 2 of the GNU General Public License as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Written by:  Matt Kaufmann and J Strother Moore
    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.
    
    INFO-DIR-SECTION Math
    START-INFO-DIR-ENTRY
    * acl2: (acl2-doc-emacs). Applicative Common Lisp
    END-INFO-DIR-ENTRY
    
    
    File: acl2-doc-emacs.info,  Node: INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER,  Next: INTRODUCTORY-CHALLENGES,  Prev: INTRODUCTORY-CHALLENGE-PROBLEM-4,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER    answer to challenge problem 4 for the new user of ACL2
    
    This answer is in the form of a script sufficient to lead ACL2 to a
    proof, with a brief prologue.
    
    We wish to collect one copy of each element in x.  We'll actually
    define the method two ways, primitive recursively and tail-recursively,
    the latter method being analogous to the program:
    
         a = nil;
         while (x not empty) {
           a = if (member (car x) a) then a else (cons (car x) a);
           x = (cdr x);
           }
         return a;
    
    We'll prove the two "equivalent" and we'll prove that they return a
    subset of x that contains no duplications.
    
    This page is organized into four sections.  (A) We will start by
    proving that the primitive recursive version correct: it returns a
    subset of its argument that is duplication free.  This will be
    straightforward.  (B) Then we'll define the while-loop version and we
    will prove it "equivalent" to the primitive recursive version.  This
    will be challenging primarily because the two methods collect their
    answers in different orders; even stating the relationship between the
    two is interesting.  Proving it will involve a few lemmas.  But once we
    prove their "equivalence" the correctness of the while-loop version
    will be straightforward from the correctness of the primitive recursive
    version.  (C) We will disable the rules we prove about the while-loop
    version and prove it correct directly, without exploiting the primitive
    recursive version.  This requires leading the theorem prover more
    carefully because reasoning about tail-recursive functions that
    accumulate results is sometimes delicate.  (D) Lessons learned - a
    narrative that summarizes what we learn from these examples.
    
    We follow The Method, which, recall, involves us in recursive attempts
    to prove lemmas.  We use a notation to indicate our sequence of proof
    attempts.  Here is an example (although in actual use we print things
    across multiple lines).  The number in bracket indicates our "stack
    depth".  The "key term" is some term from a Key Checkpoint in the
    failed proof which is responsible for our subsequent action.  Sometimes
    instead of a Key Term we just give an English explanation of what we're
    thinking.
    
         [0] (defthm main ...)     Failed!    Key Term: ...
         [1] (defthm lemma-1 ...)  Succeeded!
         [0] (defthm main ...)     Failed!    Key Term: ...
         [1] (defthm lemma-2 ...)  Failed!    Key Term: ...
         [2] (defthm lemma-2a ...) Succeeded!
         [2] (defthm lemma-2b ...) Succeeded!
         [1] (defthm lemma-2 ...)  Succeeded!
         [0] (defthm main ...)     Succeeded!
    
    The rest of this page is just a re-playable script.
    
         ; -----------------------------------------------------------------
         ; Section A:  The Primitive Recursive Version and Its Correctness
    
         ; The property of having duplications is defined as:
    
         (defun dupsp (x)
           (if (endp x)
               nil
               (if (member (car x) (cdr x))
                   t
                   (dupsp (cdr x)))))
    
         ; The primitive recursive method of collecting one copy of each element is:
    
         (defun collect-once (x)
           (if (endp x)
               nil
               (if (member (car x) (cdr x))
                   (collect-once (cdr x))
                   (cons (car x) (collect-once (cdr x))))))
    
         ; [0]
         (defthm main-theorem-1-about-collect-once
           (subsetp (collect-once x) x))
         ; Succeeded!
    
         ; [0]
         ; (defthm main-theorem-2-about-collect-once
         ;   (not (dupsp (collect-once x))))
         ; Failed!
         ; Key Term:  (MEMBER (CAR X) (COLLECT-ONCE (CDR X)))
    
         ; [1]
         (defthm member-collect-once
           (iff (member e (collect-once a))
                (member e a)))
         ; Succeeded!
    
         ; [0]
         (defthm main-theorem-2-about-collect-once
           (not (dupsp (collect-once x))))
         ; Succeeded!
    
         ; That was really easy!
    
         ;-----------------------------------------------------------------
         ; Section B:  The While-Loop Version and Its Correctness --
         ;  presented in two parts:  its equivalence to the primitive recursive
         ;  version and then its correctness proved via that equivalence
    
         ; The tail-recursive, or while-loop version, is defined as follows.  The
         ; function below is the loop itself and it ought to be called with a = nil to
         ; implement the initialization of a in the pseudo-code above.
    
         (defun while-loop-version (x a)
           (if (endp x)
               a
               (while-loop-version (cdr x)
                                   (if (member (car x) a)
                                       a
                                       (cons (car x) a)))))
    
         ; We wish to prove that the two are equivalent.  But they are actually
         ; very different.  For example,
    
         ; (collect-once '(2 4 1 3 1 2 3 4))           = (1 2 3 4)
         ; (while-loop-version '(2 4 1 3 1 2 3 4) nil) = (3 1 4 2)
    
         ; Things get a little more complicated if a is non-nil:
         ; (while-loop-version '(2 4 1 3 1 2 3 4) '(2 2 4 4)) = (3 1 2 2 4 4)
    
         ; Several observations help explain what is happening.  (1) Collect-once
         ; collects the last occurrence of each element, in the order of their last
         ; occurrences.  So, for example, since the last occurrence of 2 preceeds the
         ; last occurrence of 3 in '(2 4 1 3 1 2 3 4)), then the collected 2 preceeds
         ; the collected 3 in the answer.  But while-loop-version collects the first
         ; occurrence of each element, in the reverse order of that occurrence.  So it
         ; adds 2 to its accumulator first and adds 3 last, making 3 preceed 2 in the
         ; answer.
    
         ; (2) The while-loop-version does not collect anything already in a and indeed
         ; just adds stuff to the front of a, returning everything initially in a plus
         ; one occurrence of everything in x not in a.
    
         ; To state the relationship that holds between these two we have to define two
         ; other functions.
    
         ; This is our familiar list reverse function...
         (defun rev (x)
           (if (endp x)
               nil
               (append (rev (cdr x))
                       (list (car x)))))
    
         ; And this function ``removes'' from x all the elements in y, i.e., copies x
         ; while dropping the elements of y.
    
         (defun list-minus (x y)
           (if (endp x)
               nil
               (if (member (car x) y)
                   (list-minus (cdr x) y)
                   (cons (car x) (list-minus (cdr x) y)))))
    
         ; The specific equivalence we're really interested in is
         ; (equal (while-loop-version x nil)
         ;        (collect-once (rev x)))
    
         ; But we will not be able to prove that by induction because it has the
         ; constant nil where we need a variable, a, in order to admit an appropriate
         ; inductive instance.  So we will attack the most general problem.  What is
         ; (while-loop-version x a) equal to, in terms of collect-once?
    
         ; The most general relationship between the two collection functions is:
    
         ; (equal (while-loop-version x a)
         ;        (append (collect-once (list-minus (rev x) a)) a))
    
         ; This formula bears thinking about!  If you're like us, you won't believe it
         ; until it is proved!
    
         ; [0]
         ; (defthm general-equivalence
         ;   (equal (while-loop-version x a)
         ;          (append (collect-once (list-minus (rev x) a)) a)))
         ; Failed!
         ; Key term in checkpoint:
         ; (LIST-MINUS (APPEND (REV (CDR X)) (LIST (CAR X))) A)
    
         ; [1]
         (defthm list-minus-append
           (equal (list-minus (append a b) c)
                  (append (list-minus a c)
                          (list-minus b c))))
         ; Succeeded!
    
         ; [0]
         ; (defthm general-equivalence
         ;   (equal (while-loop-version x a)
         ;          (append (collect-once (list-minus (rev x) a)) a)))
         ; Failed!
         ; Key term in checkpoint:
         ; (COLLECT-ONCE (APPEND (LIST-MINUS (REV (CDR X)) A) (LIST (CAR X))))
    
         ; [1]
         ; (defthm collect-once-append
         ;   (equal (collect-once (append a b))
         ;          (append (list-minus (collect-once a) b)
         ;                  (collect-once b))))
         ; Failed!
         ; Key term:
         ; (MEMBER (CAR A) (APPEND (CDR A) B))
    
         ; [2]
         (defthm member-append
           (iff (member e (append a b))
                (or (member e a)
                    (member e b))))
         ; Succeeded!
    
         ; [1]
         (defthm collect-once-append
           (equal (collect-once (append a b))
                  (append (list-minus (collect-once a)
                                      b)
                          (collect-once b))))
         ; Succeeded!
    
         ; [0]
         ; (defthm general-equivalence
         ;   (equal (while-loop-version x a)
         ;          (append (collect-once (list-minus (rev x) a)) a)))
         ; Failed!
         ; Key term:
         ; (APPEND (APPEND (LIST-MINUS (COLLECT-ONCE (LIST-MINUS (REV (CDR X)) A))
    
         ; [1]
         (defthm assoc-append
           (equal (append (append a b) c)
                  (append a (append b c))))
         ; Succeeded!
    
         ; [0]
         ; (defthm general-equivalence
         ;   (equal (while-loop-version x a)
         ;          (append (collect-once (list-minus (rev x) a)) a)))
         ; Failed!
         ; Key term:
         ; (LIST-MINUS (COLLECT-ONCE (LIST-MINUS (REV (CDR X)) A)) ...)
    
         ; This key term makes us think of the lemma to move the LIST-MINUS inside the
         ; COLLECT-ONCE.  But when that's done, we will have two LIST-MINUS terms
         ; nestled together and we will want to combine them into one.  Call these two
         ; lemmas (a) and (b).
    
         ; [1] (a)
         ; (defthm list-minus-collect-once
         ;   (equal (list-minus (collect-once x) a)
         ;          (collect-once (list-minus x a))))
         ; Failed!
         ; Key term:
         ; (MEMBER (CAR X) (LIST-MINUS (CDR X) A))
    
         ; [2] (A pretty fact)
         (defthm member-list-minus
           (iff (member e (list-minus x a))
                (and (member e x)
                     (not (member e a)))))
         ; Succeeded!
    
         ; [1] (a)
         (defthm list-minus-collect-once
           (equal (list-minus (collect-once x) a)
                  (collect-once (list-minus x a))))
         ; Succeeded!
    
         ; [1] (b)
         (defthm list-minus-list-minus
           (equal (list-minus (list-minus x a) b)
                  (list-minus x (append b a))))
         ; Succeeded!
    
         ; [0]
         (defthm general-equivalence
           (equal (while-loop-version x a)
                  (append (collect-once (list-minus (rev x) a)) a)))
         ; Succeeded!
    
         ; That completes the proof of the ``equivalence'' of the two methods.
    
         ; Now we prove (1) that the result of while-loop-version is a subset, and (2)
         ; that it contains no duplications.  We prove the two conjuncts separately.
    
         ; [0]
         (defthm main-theorem-1-about-while-loop
           (subsetp (while-loop-version x nil) x))
         ; Succeeded!
    
         ; But the theorem prover works harder to do the proof above than one might have
         ; expected because it doesn't turn into an instance of
         ; main-theorem-1-about-collect-once because of the presence of the rev term.
         ; However, we're content that ACL2 managed to do the proof on its own.
    
         ; [0]
         (defthm main-theorem-2-about-while-loop
           (not (dupsp (while-loop-version x nil))))
    
         ; So we see that the proof of correctness of while-loop-version isn't hard,
         ; after we establish the relationship with the primitive recursive version.
         ; But finding and proving the relationship is fairly challenging.
    
         ; -----------------------------------------------------------------
         ; Section C:  A Direct Proof of the Correctness of the While-Loop Version
    
         ; Some would consider the proof in Section B ``indirect'' because we first showed
         ; how while-loop-version could be expressed as a collect-once and then proved
         ; our main theorems about while-loop-version, which means those main proofs
         ; were conducted in terms of collect-once, not while-loop-version.
    
         ; It is interesting to compare this proof with the ``direct'' one in which
         ; we don't use collect-once at all and reason only about while-loop-version.
    
         ; So to do that comparison, let's disable all the lemmas we've proved about
         ; while-loop-version and try to prove the two main theorems above about
         ; while-loop-version.
    
         (in-theory (disable general-equivalence
                             main-theorem-1-about-while-loop
                             main-theorem-2-about-while-loop))
    
    
         ; [0]
         ; (defthm main-theorem-1-about-while-loop-redux
         ;   (subsetp (while-loop-version x nil) x))
         ; Failed!  [Well, the truth is below...]
    
         ; We don't even submit this event above because we recognize that it is not
         ; general enough to permit proof by induction.  We need to deal with the nil in
         ; the second argument of while-loop-version.  Experience with induction tells
         ; us this should be a variable, so we can assume an appropriate inductive
         ; instance.  Therefore, we adopt this subgoal immediately:
    
         ; [1]
         ; (defthm main-lemma-1-about-while-loop-version
         ;   (subsetp (while-loop-version x a) (append x a)))
         ; Failed!
         ; Key Term:  Does the wrong induction.
    
         ; [1]
         ; (defthm main-lemma-1-about-while-loop-version
         ;   (subsetp (while-loop-version x a) (append x a))
         ;   :hints (("Goal" :induct (while-loop-version x a))))
         ; Failed!  Two key terms are suggested
         ; Key term: (IMPLIES (AND ... (SUBSETP (WHILE-LOOP-VERSION (CDR X) A) (APPEND (CDR X) A)))
         ;                    (SUBSETP (WHILE-LOOP-VERSION (CDR X) A) (CONS ... (APPEND (CDR X) A))))
         ; Key term: (SUBSETP A A)
         ; So we'll prove both before trying again.
         ; [2]
         (defthm subsetp-cons
           (implies (subsetp a b)
                    (subsetp a (cons e b))))
         ; Succeeded!
    
         ; [2]
         (defthm subsetp-reflexive
           (subsetp a a))
         ; Succeeded!
    
         ; [1]
         ; (defthm main-lemma-1-about-while-loop-version
         ;   (subsetp (while-loop-version x a) (append x a))
         ;   :hints (("Goal" :induct (while-loop-version x a))))
         ; Failed!
         ; Key Term:
         ; (IMPLIES (AND ...
         ;               (SUBSETP (WHILE-LOOP-VERSION (CDR X) (CONS (CAR X) A))
         ;                        (APPEND (CDR X) (CONS (CAR X) A))))
         ;          (SUBSETP (WHILE-LOOP-VERSION (CDR X) (CONS (CAR X) A))
         ;                   (CONS (CAR X) (APPEND (CDR X) A))))
    
         ; We'd be done if we could rewrite the
         ; (APPEND (CDR X) (CONS (CAR X) A))
         ; to
         ; (CONS (CAR X) (APPEND (CDR X) A))
         ; These two terms are not equal!  But they are ``set-equal'' and this kind of
         ; rewriting is possible using user-defined equivalences and congruence rules.
         ; But the new user should not dive into congruences yet.  So we will do this
         ; with ordinary lemmas:
    
         ; The plan then is to prove
         ; (iff (subsetp a (append b (cons e c)))
         ;      (subsetp a (cons e (append b c))))
    
         ; Consider the first half of this bi-implication:
         ; (implies (subsetp a (append b (cons e c)))            ; hyp1
         ;          (subsetp a (cons e (append b c))))           ; concl
         ; Notice that if we knew
         ; (subsetp (append b (cons e c)) (cons e (append b c))) ; hyp2
         ; then we could use hyp1 and hyp2 together with the transitivity of
         ; subsetp to get concl.
    
         ; The proof in the other direction is comparable but requires the
         ; (subsetp (cons e (append b c)) (append b (cons e c)))
    
         ; Thus, our plan is prove
         ; (a) transitivity of subsetp
         ; (b) (subsetp (append b (cons e c)) (cons e (append b c)))
         ; (c) (subsetp (cons e (append b c)) (append b (cons e c)))
    
         ; in order to prove
         ; (d) (iff (subsetp a (append b (cons e c)))
         ;         (subsetp a (cons e (append b c))))
    
         ; [2] (a)
         (defthm trans-subsetp
           (implies (and (subsetp a b)
                         (subsetp b c))
                    (subsetp a c)))
         ; Succeeded!
    
         ; [2] (b)
         (defthm append-cons-v-cons-append-1
           (subsetp (append b (cons e c))
                    (cons e (append b c))))
         ; Succeeded!
    
         ; [2] (c)
         (defthm append-cons-v-cons-append-2
           (subsetp (cons e (append b c))
                    (append b (cons e c))))
         ; Succeeded!
    
         ; [2] (d)
         (defthm subsetp-append-cons-cons-append
           (iff (subsetp a (append b (cons e c)))
                (subsetp a (cons e (append b c)))))
         ; Succeeded!
    
         ; [1]
         (defthm main-lemma-1-about-while-loop-version
           (subsetp (while-loop-version x a) (append x a))
           :hints (("Goal" :induct (while-loop-version x a))))
         ; Succeeded!
    
         ; [0]
         ; (defthm main-theorem-1-about-while-loop-version
         ;   (subsetp (while-loop-version x nil) x))
         ; Failed!  [But the truth is below...]
    
         ; But we don't submit this because we don't expect it to be proved
         ; from the main lemma just proved:  they don't match!  But
         ; note that if we instantiated the main lemma, replacing a by nil,
         ; we get:
    
         ; (subsetp (while-loop-version x nil) (append x nil))
    
         ; and we could simplify the (append x nil) to x in this context, with
         ; another congruence rule -- if we were using them.  So let's prove
         ; first that we can simplify (append x nil) inside a subsetp:
    
         ; [1]
         (defthm subsetp-append-nil
           (iff (subsetp x (append y nil))
                (subsetp x y)))
         ; Succeeded!
    
         ; and then just tell ACL2 how to use the lemma to get the main theorem.  Note
         ; that we give a hint to instantiate main-lemma-1... but we also disable
         ; main-lemma-1... because otherwise it will rewrite itself away!  Once the
         ; instance of main-lemma-1... is sitting around as a hypothesis,
         ; subsetp-append-nil will rewrite the (append x nil) to x for us and finish the
         ; proof.
    
         ; [0]
         (defthm main-theorem-1-about-while-loop-version
           (subsetp (while-loop-version x nil) x)
           :hints (("Goal"
                    :use (:instance main-lemma-1-about-while-loop-version
                                    (x x)
                                    (a nil))
                    :in-theory (disable main-lemma-1-about-while-loop-version))))
         ; Succeeded!
    
         ; Recall that the main-theorem-1... just proved is just half of what we want.
         ; We also want:
    
         ; [0]
         ; (defthm main-theorem-2-about-while-loop-version
         ;   (not (dupsp (while-loop-version x nil))))
         ; Failed!  [But the truth is below...]
    
         ; But, again, we don't submit that because the nil makes it not general enough for
         ; induction.  Instead we go immediately to:
    
         ; [1]
         (defthm main-lemma-2-about-while-loop-version
           (implies (not (dupsp a))
                    (not (dupsp (while-loop-version x a)))))
         ; Succeeded!
    
         ; This time we know our main-lemma-2... will match (there's no (append x nil)
         ; in there to mess things up) and so we can complete the proof with:
    
         ; [0]
         (defthm main-theorem-2-about-while-loop-version
           (not (dupsp (while-loop-version x nil))))
         ; Succeeded!
    
         ;-----------------------------------------------------------------
         ; Section D:  Lessons Learned
    
         ; The most obvious lesson is that it is easier to reason about the primitive
         ; recursive collect-once than about the while-loop-version.  Thus, if your only
         ; need is for a function that collects one occurrence of each element of a list
         ; and you don't care about the order in which you collect them and you don't
         ; need it to be very sparing of stack space when it executes, then use the
         ; primitive recursive definition and don't even think about while loops!
    
         ; So why might you be driven to while-loop-version?  One possibility is that
         ; the list you wish to process is very long and the primitive recursive version
         ; would produce a stack overflow.  In ACL2, that would mean the list would have
         ; to be several thousand long.  Is your application really so demanding?
    
         ; Another possibility is that you are modeling in Lisp a while loop expressed
         ; in some other programming language.  In that case, the fidelity of your model to
         ; the artifact being modeled is important and you should use while-loop-version.
    
         ; Another possibility is that for some reason order matters and you really are
         ; interested in collecting the first occurrence rather than the last.  Of
         ; course this is most likely to be relevant in more interesting applications
         ; where the occurrences are somehow distinguishable.
    
         ; If you are forced to deal with the while-loop-version the question is do you
         ; do an indirect proof as in Section B or a direct proof as in Section C?
         ; The indirect proof involved 10 theorems and the direct proof involved 11.
         ; That is not a significant difference.
    
         ; But our sense is that the indirect proof is easier to find, once you figure
         ; out the basic shape of the relation between while-loop-version collect-once.
         ; In particular, we had to give the theorem prover two hints in the direct
         ; proof (versus no hints in the indirect proof).  One of our hints was about
         ; what induction to do and the other was about how to use a previously proved
         ; instance of a lemma involving an accumulator.  Furthermore, we had to think
         ; carefully about the use of the transitivity of subsetp and we had to hack our
         ; way around rewriting (append a (cons e b)) to (cons e (append a b)) in a
         ; subsetp-expression.
    
         ; Some of these ``set'' problems could have been handled a lot more elegantly by
         ; defining set-equal as an equivalence relation and proving the congruence
         ; rules to allow the rewriting of set-equal terms to set-equal terms inside
         ; certain expressions like subsetp and member.  However, that involves a lot of
         ; overhead in the form of congruence rules showing that set-equality is
         ; maintained by replacement of set-equals by set-equals in various argument
         ; positions of the various functions.  See :doc congruence.  In general, we
         ; find congruence-based reasoning extremely neat and powerful when the
         ; appropriate infrastructure has been built up.  But because the infrastructure
         ; is ``heavy'' we tend not to invest in it for small projects.
    
         ; In summary, different users might take home different lessons about whether a
         ; direct or indirect proof is better here.  This is in part due to the
         ; complexity of the functional relationship between collect-once and
         ; while-loop-version, which additionall involved append, list-minus, and rev.
         ; Had the relationship been simpler, the indirect proof would have been
         ; preferred.
    
         ; An undeniable lesson, however, is that it is helpful to know both styles of
         ; proof and to be able to explore both as needed in your applications.
    
    Use your browser's Back Button now to return to
    introductory-challenge-problem-4.
    
    
    File: acl2-doc-emacs.info,  Node: INTRODUCTORY-CHALLENGES,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED,  Prev: INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    INTRODUCTORY-CHALLENGES    challenge problems for the new ACL2 user
    
    Do each of the problems.  In each case, start with a fresh ACL2 (or
    undo all effects of previous events with :ubt! 1).  This may require
    that you "re-discover" the same lemma more than once in different
    problems, but recognizing the need for something you used in some
    previous project is part of the training.
    
    We recommend that you follow The Method and consult the documentation as
    needed - but that you not look at our answers until you're well and
    truly baffled!
    
    See *note INTRODUCTORY-CHALLENGE-PROBLEM-1::  (Answer:
    introductory-challenge-problem-1-answer)
    
    See *note INTRODUCTORY-CHALLENGE-PROBLEM-2::  (Answer:
    introductory-challenge-problem-2-answer)
    
    See *note INTRODUCTORY-CHALLENGE-PROBLEM-3::  (Answer:
    introductory-challenge-problem-3-answer)
    
    See *note INTRODUCTORY-CHALLENGE-PROBLEM-4::  (Answer:
    introductory-challenge-problem-4-answer)
    
    In addition to these explicit challenge problems designed for
    beginners, the ACL2 documentation has many example solutions to
    problems (not always phrased in the question/answer format here).  If
    you are looking for other examples, you should consider
    
    annotated-acl2-scripts (Answer:  the answers are given in the examples)
    
    When you've done the problems and compared your solutions to ours, use
    your browser's Back Button now to return to
    introduction-to-the-theorem-prover.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE,  Prev: INTRODUCTORY-CHALLENGES,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED    background knowledge in ACL2 logic for theorem prover tutorial
    
    You might think that in order to use the theorem prover you have to know
    the mathematical logic supported by ACL2.  But you need to know a lot
    less about it than you might think.
    
    Technically, a theorem is a formula that can be derived from axioms by
    using rules of inference.  Thus, to do a proof you have to know (a) the
    syntax of formulas, (b) the axioms, and (c) the rules of inference.
    Traditionally, these things are spelled out in excruciating detail in
    treatments of mathematical logic - and for good reason.
    
    The whole point of proving theorems is that it is a way to determine
    that a formula is "always true" (under some model of the axioms).  By
    "always true" we actually mean what logicians mean when they say the
    formula is valid: true in the model, for all possible values of the
    variables.  Here by "model of the axioms" we mean an understanding of
    the meaning of the various function symbols so that the axioms are true
    for all values of the variables.  If the variables in your conjecture
    can take on an infinite number of values, proof is often the only way
    to determine that a conjecture is "always true."  So if proof is being
    used to determine that a questionable formula is always true the proof
    must be carried out flawlessly.  Thus, the (a) syntax, (b) axioms, and
    (c) rules of inference must be described precisely and followed to the
    letter.
    
    But formal mathematical logic was invented to explain how people
    reason.  To the extent that logic mimics human reasoning, proofs can be
    seen as just extremely carefully crafted arguments.  Given that ACL2 is
    responsible for following the rules "to the letter," your main job is
    "explain" the big leaps.
    
    To use the theorem prover you must understand (a) the syntax, because
    you must be able to write formulas flawlessly.  But you don't have to
    know (b) the axioms and (c) the rules of inference at nearly the same
    level of precision, as long as you understand the basic structure and
    language of proofs.
    
    Below is part of a proof of a certain theorem.  You ought to be able to
    understand the following.  Since what we describe is a proof of one
    case of the formula, we hope that you're convinced that the formula
    holds for that case.
    
    Read this and follow the links to confirm that you understand what
    happens.  Be sure to then use your browser's Back Button to return to
    this page and continue.
    
    An Annotated Proof of
    
         (implies (true-listp z)
                  (equal (rev (rev z)) z))
    
    "We will prove that reversing the reverse of a true-listp yields the
    original list.  The formula stating this is above.  We will prove it by
    induction on the list structure of z.
    
    The base case of the induction is:
    
         (implies (endp z)
                  (implies (true-listp z)
                           (equal (rev (rev z)) z))).
    
    This formula is equivalent, by propositional calculus, to
    
         (implies (and (endp z)
                       (true-listp z))
                  (equal (rev (rev z)) z))
    
    Rewriting with the definition of endp produces:
    
         (implies (and (not (consp z))
                       (true-listp z))
                  (equal (rev (rev z)) z))
    
    Rewriting repeatedly starting with the definition of true-listp
    produces:
    
         (implies (and (not (consp z))
                       (equal z nil))
                  (equal (rev (rev z)) z))
    
    Then using the second hypothesis, just substituting equals for equals,
    we get
    
         (implies (and (not (consp z))
                       (equal z nil))
                  (equal (rev (rev nil)) nil))
    
    Since the conclusion involves no variables, we can evaluate it, getting
    
         (implies (and (not (consp z))
                       (equal z nil))
                  T)
    
    But this is an instance of the tautology (implies p T).  Thus, the base
    case is proved."
    
    Now it is time for a little quiz.  There are just three questions.
    
    Q1:  The case above was the Base Case of an inductive proof of
    
         (implies (true-listp z)
                  (equal (rev (rev z)) z))
    
    in which we did induction on the structure of the linear list z.  What
    is the Induction Step?  That is, what do you have to prove besides the
    Base Case to complete this inductive proof?
    
    Below are four commonly given answers; choose one.  Then look here to
    find out if you're right.
    
         Induction Step - Choice (i):
         (implies (not (endp z))
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
         Induction Step - Choice (ii):
         (implies (true-listp (cdr z))
                  (equal (rev (rev (cdr z))) (cdr z)))
    
         Induction Step - Choice (iii):
         (implies (and (not (endp z))
                       (equal (rev (rev (cdr x))) (cdr x)))
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
         Induction Step - Choice (iv):
         (implies (and (not (endp z))
                       (implies (true-listp (cdr z))
                                (equal (rev (rev (cdr z))) (cdr z))))
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    Q2: To prove the Induction Step we must prove one or more of the goals
    below.
    
    Which combinations are sufficient to imply the Induction Step?  Decide
    what is required and then look here to find out if you're right.  To
    help you, the Induction Step is of the form:
    
         Induction Step:
         (implies (and c
                       (implies p' q'))
                  (implies p q))
    
    and beside each candidate subgoal we show its structure in those terms.
    
         Subgoal (i):
         (implies (and (not (endp z))                        ; (implies (and c
                       (true-listp z))                       ;               p)
                  (true-listp (cdr z)))                      ;          p')
    
         Subgoal (ii):
         (implies (and (not (endp z))                        ; (implies (and c
                       (true-listp z)                        ;               p
                       (equal (rev (rev (cdr z))) (cdr z)))  ;               q')
                  (equal (rev (rev z)) z))                   ;          q)
    
         Subgoal (iii):
         (implies (and (not (endp z))                        ; (implies (and c
                       (equal (rev (rev (cdr z))) (cdr z)))  ;               q')
                  (equal (rev (rev z)) z))                   ;          q)
    
         Subgoal (iv):
         (implies (and (not (endp z))                        ; (implies (and c
                       (true-listp (cdr z))                  ;               p'
                       (equal (rev (rev (cdr z))) (cdr z)))  ;               q')
                  (equal (rev (rev z)) z))                   ;          q)
    
    Q3: Suppose you know the theorem
    
         Theorem:
         (implies (p (f x))
                  (equal (g (h x))
                         x))
    
    and you wish to rewrite the target (g (h a)) to a in
    
         Goal Conjecture:
         (implies (and (q (f a))
                       (r a))
                  (s (g (h a))))
    
    What must you prove to relieve the hypothesis of Theorem?
    
    After you've thought about it, look here for our answer.
    
    End of the Quiz
    
    If this page made sense, you're ready to read the introduction to the
    theorem prover.
    
    If you are reading this as part of the tutorial introduction to the
    theorem prover, use your browser's Back Button now to return to
    introduction-to-the-theorem-prover.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE    a brief explanation of base cases
    
    According to the sentence, the conjecture being proved is "reversing
    the reverse of a true-listp yields the original list."  The formula
    corresponding to this conjecture is:
    
         (implies (true-listp z)
                  (equal (rev (rev z)) z)).
    
    We're also told that this is an inductive proof.  Evidently we're doing
    an induction on the structure of the list z.  Then the Base Case is the
    formula:
    
         (implies (endp z)
                  (implies (true-listp z)
                           (equal (rev (rev z)) z))).
    
    Now use your browser's Back Button to return to the example proof in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS    substitution of equals for equals
    
    Anytime you have an equality hypothesis relating two terms, e.g.,
    
         (equal lhs rhs)
    
    it is legal to substitute one for the other anyplace else in the
    formula.  Doing so does not change the truthvalue of the formula.
    
    You can use a negated equality this way provided it appears in the
    conclusion.  For example, it is ok to transform
    
         (implies (true-listp x)
                  (not (equal x 23)))
    
    to
    
         (implies (true-listp 23)
                  (not (equal x 23)))
    
    by substitutions of equals for equals.  That is because, by
    propositional calculus, we could rearrange the formulas into their
    contrapositive forms:
    
         (implies (equal x 23)
                  (not (true-listp x)))
    
    and
    
         (implies (equal x 23)
                  (not (true-listp 23)))
    
    and see the equality as a hypothesis and the substitution of 23 for x as
    sensible.
    
    Sometimes people speak loosely and say "substitution of equals for
    equals" when they really mean "substitutions of equivalents for
    equivalents."  Equality, as tested by EQUAL, is only one example of an
    equivalence relation.  The next most common is propositional
    equivalence, as tested by IFF.  You can use propositional equivalence
    hypotheses to substitute one side for the other provided the target
    term occurs in a propositional place, as discussed at the bottom of
    propositional calculus.
    
    Now use your browser's Back Button to return to the example proof in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION    evaluation during proofs
    
    Any time you are proving a formula and see a subterm in the formula
    that contains no variables, you can just evaluate the subterm.
    
    This is familiar from algebra:  It is not uncommon to rearrange a
    polynominal to collect all the constants and then add them up:
    
         (3x + 2 + 7y + 2)
         =
         (3x + 7y + (2 + 2))
         =
         (3x + 7y + 4).
    
    That last step is just evaluation.
    
    It happens often in ACL2 proofs because theorems involve constants and
    defined functions and when those constants "drift into the maw" of a
    function, the function call can be eliminated and replaced by a new
    constant.  ACL2 does this automatically; you don't have to tell it.  In
    fact, there are a few occasions where you might prefer it not evaluate
    and those are the ones you have to look out for!  They'll be obvious
    when they happen because you'll see a mysterious constant crop up in
    the proof.
    
    Evaluation is legal because it is just the repeated use of
    unconditional rewriting to replace definitions by their instantiated
    bodies until no function calls remain.
    
    Now use your browser's Back Button to return to the example proof in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF    a brief explanation of induction
    
    We start by showing classical induction on the natural numbers in an
    ACL2 setting before turning to a more general treatment of induction.
    
    Classical Induction on Natural Numbers: Induction is familiar in the
    arithmetic setting.  Let (p n) denote some formula involving the
    variable n (and perhaps some other variables which we don't exhibit).
    Then to prove (p n), for all n, by classical induction on the
    construction of the natural numbers, prove each of the following:
    
         Base Case:
         (implies (zp n) (p n))
    
         Induction Step:
         (implies (and (not (zp n))
                       (p (- n 1)))
                  (p n))
    
    The Base Case establishes that p holds for 0.  In fact, because of the
    definition of zp <>, it establishes that (p n) holds when n is 0 and it
    holds when n is not a natural number.
    
    The Induction Step establishes that if n is a natural number other than
    0, and if p holds for n-1, then p holds for n.  The hypothesis (p (- n
    1)) above is called the induction hypothesis.
    
    Note that if the Base Case and Induction Step are valid, then we know
    (p n), for all n.  You can convince yourself of this by picking any
    object and asking "how do I know p holds for this object?"  For example,
    (p -7), (p 'abc), and (p 0) are all established by the Base Case.  What
    about (p 1)?  That follows from (p 0), given the Induction Step.  Why?
    To prove (p 1) using the Induction Step, you have to establish (not (zp
    1)), which is true, and (p (- 1 1)), which is (p 0), which is true by
    the Base Case.  So (p 1) is true.  Similar reasoning proves (p 2) from
    from (p 1), etc.  Clearly, for every natural number other than 0 we
    could reason like this to show that p holds.  Since the Base Case
    handled all the objects that are not natural numbers, and handled 0, we
    know (p n), for all n.
    
    There is a duality between recursion and induction that ACL2 exploits.
    The fact that the Base and Induction steps above are sufficient to
    prove p for all objects is related to the fact that the following
    recursion defines a total, terminating function:
    
         (defun nat-recursion (n)
           (if (zp n)
               n
               (nat-recursion (- n 1))))
    
    When this function is admitted we have to prove that if (zp n) does not
    hold, then (- n 1) is smaller, in some sense, than n.  This sense of
    "smaller" is determined by some measure of the arguments.  That measure
    must return an ordinal (ordinals <>), but the most common measures
    return natural numbers, which are among the ordinals.  Furthermore,
    that measure should insure that the terms in the recursive calls are
    smaller than the formals, i.e., the measure of (- n 1) must be smaller
    than the measure of n, when the recursive branches are taken.  This
    sense of "smaller" must be well-founded:  it must be impossible to have
    an infinitely descending chain of smaller things.  This is true of the
    less-than relation on the ordinals (see o< <>).  Well-foundedness means
    that eventually any recursion must "bottom out" because things can't
    keep getting smaller forever.
    
    The recursion in nat-recursion suggests the induction shown above: the
    Base Case is defined by the if branch that does not lead to a recursive
    call.  The Induction Step is defined by the other branch.  The
    induction hypothesis is defined by what we recur on, i.e., (- n 1).  The
    theorems proved when nat-recursion is introduced justify the classical
    induction scheme noted above.
    
    Every recursively defined ACL2 function suggests a legal induction and
    vice versa.
    
    Furthermore, every call of a recursively defined function on distinct
    variable symbols also suggests a legal induction:  just take the
    induction suggested by the function's recursive definition after
    renaming the formal parameters to be the variables in the call.
    
    For example, it should be clear that (nat-recursion a) suggests a Base
    Case defined by (zp a), and induction step defined by (not (zp a)) and
    an induction hypothesis about (- a 1).
    
    Note that the term (fact n) suggests the same classical induction on
    natural numbers shown above, where fact is defined as follows (even
    though we've used the formal parameter k below).
    
         (defun fact (k)
           (if (zp k)
               1
               (* k (fact (- k 1)))))
    
    The induction suggested by a term like (fact n) is insensitive to the
    name of the formal parameter used in the defun.
    
    The induction suggested by a function or term is insensitive to the
    value returned by the function or term.
    
    It doesn't matter what the function returns in its "base case" (e.g., 1
    in fact) or what the function "does" to its recursive call (e.g.,
    multiply by k in the defun of fact).
    
    All that matters is (i) how the if structure breaks down the cases on
    k, (ii) which branches lead to recursion, and (iii) what arguments are
    passed to the recursive calls.  Those things determine (i) the case
    analysis of the induction scheme, (ii) which cases are Base Cases and
    which are Induction Steps, and (iii) what the induction hypotheses are.
    
    For a selection of common inductions schemes in ACL2 (e.g., on the
    structure of natural numbers, lists, and trees and on several variables
    at once, multiple base cases, multiple induction hypotheses, multiple
    induction steps, etc.)  check this link.
    
    Every legal ACL2 induction corresponds to an admissible recursive
    function and vice versa.  Similarly, every legal ACL2 induction
    corresponds to a call of a recursively defined function on distinct
    variables.
    
    ACL2 chooses which induction to do by looking at the terms that occur
    in the conjecture.  For many elementary theorems, ACL2 chooses the
    right induction by itself.
    
    You may occasionally need to tell it what induction to do.  You do that
    by showing it a term that suggests the induction you want.  We'll
    explain how you communicate this to ACL2 later.  If you understand how
    recursive functions suggest inductions, then you know what you need to
    know to use ACL2.
    
    The main point of this discussion of induction is familiarize you with
    the basic terms:  Base Case (of which there may be several), Induction
    Step (of which there may be several), Induction Hypothesis (of which
    there may be several in each Induction Step), measure and well-founded
    relation justifying an induction, and the induction suggested by a term
    or recursive function definition.  Furthermore, every Induction
    Hypothesis is always an instance of the conjecture being proved: each
    induction hypothesis is obtained from the conjecture being proved by
    applying a substitution replacing variables by terms.
    
    If you are reviewing the material taken for granted about logic while
    working your way through the introduction to the theorem prover, please
    use your browser's Back Button to return to the example proof in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE    a brief explanation of substitution instances
    
    Let p and q be terms or formulas (there is no difference in ACL2).
    Then we say p is an instance or substitution instance of q if and only
    if p can be obtained from q by uniformly replacing the variables of q
    by terms.  Sometimes we call p the target and q the pattern because by
    choosing appropriate replacements we can make the pattern match many
    different targets.
    
    For example, the following target is an instance of the given pattern:
    
         target:      (APP (APP (REV A) (REV B)) (REV C))
         pattern:     (APP (APP   x       y    ) (REV z))
    
    The replacement or substitution used in this match of the pattern to the
    target is:
    
         variable in pattern          replacement term
         x                              (REV A)
         y                              (REV B)
         z                              C
    
    Such substitutions are usually written this way in ACL2:
    
         ((x  (REV A))
          (y  (REV B))
          (z  C)).
    
    Please use your browser's Back Button to return to the page that
    mentioned "instance."
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS    a brief explanation of propositional calculus
    
    It is impossible in this short introduction to teach you propositional
    calculus if you don't already know it!
    
    A typical use of propositional calculus is to observe that
    
         (implies (endp z)
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    is equivalent to:
    
          (implies (and (endp z)
                        (true-listp z))
                   (equal (rev (rev z)) z))
    
    If this is surprising and you know propositional calculus, then the
    problem might be our notation.  We're exploiting the tautology
    
         (p --> (q --> r)) <--> ((p & q) --> r)
    
    where --> and <--> are meant to be the traditional arrows denoting
    logical implication and logical equivalence.
    
    If you don't know propositional calculus, we'll say just a few things
    to help ease your journey.
    
    A propositional formula, in ACL2, is any formula written entirely in
    terms of variable symbols, T, NIL, and the propositional functions AND,
    OR, NOT, IMPLIES, and IFF.  The "tautology" above in traditional
    notation is this propositional formula in ACL2:
    
         (IFF (IMPLIES P (IMPLIES Q R))
              (IMPLIES (AND P Q) R)).
    
    If you have a formula like
    
         (implies hyp
                  concl)
    
    then we say that formula is an implication, that hyp is the hypothesis,
    and that concl is the conclusion.  If the hypothesis is an and
    expression, as in
    
         (implies (and hyp1
                       hyp2
                       ...)
                  concl)
    
    then we call hyp1 is the first hypothesis, hyp2 is the second
    hypothesis, etc.
    
    If a term is of the form
    
         (and term1 term2 ...)
    
    we say it is a conjunction and that term1 is the first conjunct, term2
    is the second conjunct, etc.  We treat an or-term analogously but call
    it a disjunction and its arguments are disjuncts.
    
    A tautology is any propositional formula that can be proved by testing
    it under all combinations of Boolean assignments to its variables.  We
    give an example of such a truth-table proof below, but hasten to add
    that ACL2 does not generally use truth tables to recognize tautologies.
    It primarily uses IF-normalization and BDDs to recognize tautologies,
    which can be seen as a mix of symbolic manipulation and case analysis.
    
    Many tautologies have names, but ACL2 doesn't refer to them by name
    because it derives them from first principles.  We list a few here
    because we sometimes use the names in our documentation; more
    importantly, you should look at these formulas and convince yourself
    that they're always true for all Boolean values of the variables:
    
         Double Negation:
         (iff (not (not p)) p)
    
         DeMorgan:
         (iff (not (and p q))
              (or (not p) (not q)))
    
         Distributivity:
         (iff (and p (or q r))
              (or (and p q)
                  (and p r)))
    
         Promotion:
         (iff (implies p (implies q r))
              (implies (and p q) r))
    
         Implicative Disjunction:
         (iff (implies p q)
              (or (not p) q))
    
         Contrapositive:
         (iff (implies p q)
              (implies (not q) (not p)))
    
         Generalized Contrapositive:
         (iff (implies (and p r) q)
              (implies (and p (not q)) (not r)))
    
    There are, of course, many others, even with these same names!  For
    example, there is a dual version of DeMorgan showing how not
    distributes over or, a dual version of Distributivity for or over and,
    etc.
    
    Dealing with propositional calculus will not generally be a problem for
    you because it is decidable and ACL2 has procedures that decide
    propositional formulas.  However, propositional calculus can lead to
    exponential explosion and can thus explain why ACL2 has "gone out to
    lunch."  In addition, sometimes if you are curious as to why ACL2 is
    working on a certain subgoal the reason can be traced back to
    propositional calculus.
    
    The most common example of this is that to prove a formula of the form
    
         (implies (implies p1 q1)
                  (implies p2 q2))
    
    propositional calculus will convert it to
    
         (and (implies (and p2 (not p1)) q2)
              (implies (and p2 q1) q2))
    
    Many users are surprised that the first conjunct above does not have q1
    as a hypothesis.  If you ever stare at an ACL2 goal and say to yourself
    "A hypothesis is missing!" the chances are that propositional calculus
    is "to blame."  In particular, if you are trying to prove that (implies
    p1 q1) implies something, you must deal with the case that (implies p1
    q1) is true because p1 is false.  Think about it.
    
    Now we illustrate the truth table method for deciding tautologies, even
    though that is not what ACL2 generally uses.  Consider the formula
    called Promotion above:
    
         (IFF (IMPLIES P (IMPLIES Q R))
              (IMPLIES (AND P Q) R))
    
    The formula above is a tautology.  It contains three variables, P, Q,
    and R, and so there are 8 combinations of Boolean assignments to them.
    If we let
    
         formula1:  (IMPLIES P (IMPLIES Q R))
         formula2:  (IMPLIES (AND P Q) R)
    
    then we wish to test the formula (IFF formula1 formula2):
    
         P   Q   R       formula1   formula2   (IFF formula1 formula2)
         ---------
         T   T   T            T         T       T
         T   T   NIL          NIL       NIL     T
         T   NIL T            T         T       T
         T   NIL NIL          T         T       T
         NIL T   T            T         T       T
         NIL T   NIL          T         T       T
         NIL NIL T            T         T       T
         NIL NIL NIL          T         T       T
    
    So we see that the formula always returns T and is thus a tautology.
    
    Recall that in the original example at the top of this page we were
    trying to prove the formula
    
         (implies (endp z)
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    This formula is an instance of
    
         (implies p (implies q r)).
    
    The substitution required by the match is
    
         sigma:
         ((p    (endp z))
          (q    (true-listp z))
          (r    (equal (rev (rev z)) z)))
    
    Since we know the tautology:
    
         (iff (implies p (implies q r))
              (implies (and p q) r)).
    
    is always true no matter what Boolean values p, q, and r have, then we
    know this instance of it (obtained by applying the substitution sigma
    above) is always true:
    
         (iff (implies (endp z)                            formula1'
                       (implies (true-listp z)
                                (equal (rev (rev z)) z)))
              (implies (and (endp z)                       formula2'
                            (true-listp z))
                       (equal (rev (rev z)) z))).
    
    Thus, if we're trying to prove formula1' it is permitted to try to to
    prove formula2' instead, because they return the same truthvalue.
    
    This sketch of propositional reasoning in ACL2 is a little suspect
    because we didn't address the possibility that the substitution might
    replace the propositional variables by non-propositional terms.  But
    the tautology was verified only on Boolean values for those variables.
    This actually works out because in ACL2 all propositional testing is
    done against nil and any non-nil value, including t, is as good as
    another.  However, the tautology allows us to replace one formula by
    the other only in contexts in which we just care about propositional
    truth, i.e., whether the formula is nil or not.  When we prove a
    formula in ACL2 we are really establishing that it never returns nil,
    i.e., no matter what the values of the variables, the value of the
    formula is non-nil.
    
    A very simple example of this is with Double Negation.
    
         (iff (not (not p)) p)
    
    is a tautology.  This means that if we were trying to prove
    
         (implies (not (not p)) ...)
    
    we could transform it to
    
         (implies p ...).
    
    But if we were trying to prove:
    
         (equal (not (not p)) p)
    
    we could not prove it by using Double Negation!  The formula above
    claims that (not (not p)) and p have identical values.  They do not!
    For example, (not (not 3)) is t, not 3.  However, (not (not 3)) and t
    are propositionally equivalent (i.e., satisfy iff) because one is as
    good as the other in a test.  That is what Double Negation says.
    
    As long as you only use propositional formulas in propositional places
    this aspect of ACL2 should not affect you.
    
    Now please use your browser's Back Button to return to the example
    proof in logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER    the inductive step of the rev-rev proof - Answer to Question 1
    
    The correct answer to Question 1 in logic-knowledge-taken-for-granted
    is Choice (iv).
    
    The Induction Step of the inductive proof of
    
         (implies (true-listp z)
                  (equal (rev (rev z)) z))
    
    for an induction on the linear list z is:
    
         Induction Step:
         (implies (and (not (endp z))
                       (implies (true-listp (cdr z))
                                (equal (rev (rev (cdr z))) (cdr z))))
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    The second hypothesis above is the the induction hypothesis.  The
    conclusion above is the formula we are trying to prove.  Each induction
    hypothesis is always an instance of the formula being proved, i.e., it
    is obtained from the formula being proved by uniformly replacing the
    variables in the formula with terms.  Notice how the induction
    hypothesis above is the same as the induction conclusion, except that
    all the zs have been replaced by (cdr z).
    
    If you thought the right answer was
    
         Induction Step - Choice (i):
         (implies (not (endp z))
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    then perhaps you didn't understand that we're doing an inductive proof.
    Certainly if you prove the Base Case already discussed and you prove
    Choice (i) above, then you will have proved the goal conjecture, but you
    would have done it by simple case analysis:  prove it when (endp z) and
    prove it when (not (endp z)).  While logically valid, you probably can't
    prove Choice (i) directly because you have no induction hypothesis to
    work with.
    
    If you thought the right answer was:
    
         Induction Step - Choice (ii):
         (implies (true-listp (cdr z))
                  (equal (rev (rev (cdr z))) (cdr z)))
    
    then perhaps you misunderstand the difference between the Induction Step
    and the Induction Hypothesis.  The Induction Step is the "other half"
    of the main proof, balancing the Base Case.  The Induction Hypothesis is
    just a hypothesis you get to use during the Induction Step.  The
    question Q1 asked what is the Induction Step.
    
    If you thought the right answer was:
    
         Induction Step - Choice (iii):
         (implies (and (not (endp z))
                       (equal (rev (rev (cdr x))) (cdr x))) ; "induction hyp"
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    then you are making the most common mistake newcomers make to
    induction.  You are giving yourself an "induction hypothesis" that is
    not an instance of the conjecture you're proving.  This alleged
    induction hypothesis says that (rev (rev (cdr x))) is (cdr x), whereas
    the correct induction hypothesis says those two terms are equal if
    (true-listp (cdr x)).  This alleged induction hypothesis is a stronger
    claim than we're trying to prove.  It turns out that by making this
    mistake you can "prove" conjectures that are not always true!
    Remember:  the induction hypothesis is always an instance of the
    conjecture you're proving, not just some piece of it.  Of course, ACL2
    "knows" this and will never make this mistake.  But we remind you of it
    because there may be times when you intuit a different hypothesis and
    don't understand why ACL2 doesn't use it.
    
    If this doesn't make sense, perhaps you should read about induction
    again.
    
    When you understand why Choice (iv) is the correct answer, use your
    browser's Back Button to return to logic-knowledge-taken-for-granted
    and go to question Q2.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER    the inductive step of the rev-rev proof - Answer to Question 2
    
    The correct answer to Question 2 in logic-knowledge-taken-for-granted
    is Subgoal (i) plus any one of the other other three.  For your
    reference, the four choices were:
    
         Subgoal (i):
         (implies (and (not (endp z))
                       (true-listp z))
                  (true-listp (cdr z)))
    
         Subgoal (ii):
         (implies (and (not (endp z))
                       (true-listp z)
                       (equal (rev (rev (cdr z))) (cdr z)))
                  (equal (rev (rev z)) z))
    
         Subgoal (iii):
         (implies (and (not (endp z))
                       (equal (rev (rev (cdr z))) (cdr z)))
                  (equal (rev (rev z)) z))
    
         Subgoal (iv):
         (implies (and (not (endp z))
                       (true-listp (cdr z))
                       (equal (rev (rev (cdr z))) (cdr z)))
                  (equal (rev (rev z)) z))
    
    In particular, it is wrong to think the Induction Step of the proof of
    
         (implies (true-listp z)
                  (equal (rev (rev z)) z))
    
    can be established by proving just Subgoal (ii), Subgoal (iii), Subgoal
    (iv), or combinations of those three.  You must also prove Subgoal (i)
    or something like it!
    
    The Inductive Step for the conjecture above is
    
         Induction Step:
         (implies (and (not (endp z))
                       ; Induction Hypothesis:
                       (implies (true-listp (cdr z))
                                (equal (rev (rev (cdr z))) (cdr z))))
                  ; Induction Conclusion:
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    Note that the Inductive Hypothesis is an implication:
    
         (implies (true-listp (cdr z))
                  (equal (rev (rev (cdr z))) (cdr z)))
    
    This hypothesis can be true two different ways.  The "normal" way - the
    way everybody remembers - is that (true-listp (cdr z)) is true and thus
    (equal (rev (rev (cdr z))) (cdr z)) is true.  But the way many people
    forget is that (true-listp (cdr z)) is false.  You must prove the
    Induction Step even in this "forgetable" case.
    
    In this case, the Induction Step simplifies to
    
         Induction Step:
         (implies (and (not (endp z))
                       (not (true-listp (cdr z))))
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
    
    By Promotion (see the list of tautologies in our discussion of
    propositional calculus) this is
    
         Induction Step':
         (implies (and (not (endp z))
                       (not (true-listp (cdr z)))
                       (true-listp z))
                  (equal (rev (rev z)) z))
    
    Using the Contrapositive and rearranging the order of the hypotheses
    (see propositional calculus again), this is
    
         Induction Step":
         (implies (and (not (endp z))
                       (true-listp z)
                       (not (equal (rev (rev z)) z)))
                  (true-listp (cdr z)))
    
    Notice that Subgoal (i) implies Induction Step":
    
         Subgoal (i):
         (implies (and (not (endp z))
                       (truelistp z))
                  (truelistp (cdr z)))
    
    Every inductive proof of an implication raises a case like this.  If we
    denote the conjecture (implies p q) as p --> q, then the Induction Step
    will look like this:
    
           ( c  &  (p'  --->  q'))
         --->
           (p ---> q)
    
    where c is the test that determines the inductive step, (e.g., (not
    (endp z))) and p' and q' are inductive instances of p and q.  Promotion
    produces
    
           ( c  & p & (p'  --->  q'))
         --->
           q
    
    It is then very common to prove that p implies p',
    
         (i):
         (c & p) ---> p'
    
    and then prove that q' implies q,
    
         (ii):
         (c & p & q') ---> q
    
    These correspond exactly to our choices Subgoal (i) and Subgoal (ii).
    
    It is sometimes helpful to remember this diagram:
    
         (c  &  (p'  --->  q')
                 ^         |
                 |         |
                 |         v
          -->   (p   --->  q )
    
    When you understand why Subgoals (i) and (ii) are sufficient, use your
    browser's Back Button to return to logic-knowledge-taken-for-granted
    and go to question Q3.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER    the inductive step of the rev-rev proof - Answer to Question 2
    
    The correct answer to Question 3 in logic-knowledge-taken-for-granted
    is that you need to prove
    
         Subgoal to Relieve Hyp 1:
         (implies (and (q (f a))
                       (r a))
                  (p (f a)))
    
    in order to use
    
         Theorem:
         (implies (p (f x))
                  (equal (g (h x))
                         x))
    
    to rewrite the target (g (h a)) to a in
    
         Goal Conjecture:
         (implies (and (q (f a))
                       (r a))
                  (s (g (h a))))
    
    If you don't see why, re-read the discussion of rewriting again.
    Forgetting about the need to relieve hypotheses is a common mistake in
    informal proofs.  ACL2 won't forget to relieve them.  But if you forget
    about the need to do it, you may be confused when ACL2 doesn't see the
    "proof" you see!
    
    Now use your browser's Back Button to return to the end of quiz in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING,  Next: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING    a brief explanation of rewriting from the logical perspective
    
    First we give two examples of rewriting.  Then we give a rather detailed
    description.  We recommend you read the description, even if you
    understand the two examples, just so that you learn our terminology.
    
    Example 1:  Suppose your goal conjecture is:
    
         Goal Conjecture:
         (implies (and (endp z)
                       (true-listp z))
                  (equal (rev (rev z)) z))
    
    Then you can use the following theorem (actually the definitional axiom
    introduced by the defun of endp):
    
         Definitional Axiom: endp
         (equal (endp x)
                (not (consp x))).
    
    to rewrite the Goal Conjecture to
    
         Rewritten Goal Conjecture:
         (implies (and (not (consp z))
                       (true-listp z))
                  (equal (rev (rev z)) z))
    
    Note that in this example, rewriting replaced the call of endp by its
    body after instantiating its body with the actuals from the call.  This
    is sometimes just called expanding the definition of endp.  (The
    notions of formal, body, call, and actuals are discussed in
    programming-knowledge-taken-for-granted.)
    
    Expanding a definition is an example of unconditional rewriting.  All
    definitions in ACL2 are just bare equalities relating a call of the
    function on its formals to its body.  Any time you use an equality
    theorem, whether a definitional equality or something more general like
    
         (equal (append (append x y) z)
                (append x (append y z)))
    
    to replace an instance of one side by the corresponding instance of the
    other in a goal conjecture, we call that unconditional rewriting with
    the equality.
    
    Example 2:  Suppose your goal conjecture is:
    
         Goal Conjecture:
         (implies (and (subsetp a b)
                       (true-listp b)
                       (member e a))
                  (< (len (rm e b)) (len b))).
    
    This conjecture may be read "if a is a subset of the true-listp b and e
    is a member of a, then the result of removing e from b has a shorter
    length than b."
    
    You can use the following theorem:
    
         Theorem:
         (implies (member u v)
                  (equal (len (rm u v))
                         (- (len v) 1)))
    
    to rewrite the Goal Conjecture to
    
         Rewritten Goal Conjecture:
         (implies (and (subsetp a b)
                       (true-listp b)
                       (member e a))
                  (< (- (len b) 1) (len b))).
    
    To do this you must know that the following subgoal is provable:
    
         Subgoal to Relieve Hyp 1:
         (implies (and (subsetp a b)
                       (true-listp b)
                       (member e a))
                  (member e b)).
    
    This is an example of conditional rewriting.  In order to use the
    Theorem we had to establish that its hypotheses are satisfied.  That is
    called relieving the hypotheses and was done by proving the Subgoal to
    Relieve Hyp 1.  Conditional rewriting is the most commonly used proof
    technique in ACL2.
    
    Unconditional rewriting is just a special case, where there are no
    hypotheses to relieve.
    
    Expanding a definition is just another special case, where there are no
    hypotheses to relieve and the pattern is easy to match because it is a
    call of a function on distinct variables.
    
    This page discusses rewriting from the logical perspective.  It is
    important that you are familiar with the notions of a pattern term being
    an instance of a target term.  We often say the pattern matches the
    target.  These notions involve a corresponding substitution of terms
    for variables.  All these notions are discussed in the link for
    "instance" above and we recommend you read it before continuing.  Then
    use your browser's Back Button to come back here.
    
    You should also be aware of the terms introduced in our discussion of
    propositional calculus.
    
    Rewriting is a fundamental rule of inference in our system.  The rule
    allows you to use a theorem, i.e., an axiom, lemma, or definition, to
    replace one term by another in the goal conjecture you're trying to
    prove.
    
    Suppose you have a theorem that is of the form (or can be put into the
    form):
    
         Theorem:
         (implies (and hyp1
                       ...
                       hypk)
                  (equal pattern
                         replacement))
    
    From the logical perspective we don't care how the theorem was actually
    written when it was proved.  It might have no hypotheses (in which case
    the hypi could just be t), or it could have been written in a different
    but equivalent propositional style, (or (not hyp1) ...), or the
    equality could have been written the other way around, (equal
    replacement pattern).  Such syntactic details don't matter.  Just take
    a theorem and use propositional calculus to rearrange it equivalently
    into this form for the purposes of this one rewrite step.
    
    Suppose pattern is an instance of some target term, target that occurs
    in your goal conjecture.  Let the corresponding substitution be sigma.
    If sigma does not contain a binding for every variable that occurs in
    Theorem, then extend sigma to sigma' by adding one binding for each
    such variable.  (This is necessary only if pattern does not contain
    every variable in Theorem.)
    
    Let replacement' be the result of instantiating replacement with sigma'.
    Let hypi' be the result of instantiating hypi with sigma'.
    
    Then the Rewrite Rule of Inference tells us it is permitted to replace
    that occurrence of target in the goal by replacement' - if you can prove
    each hypi' in this context.  We make this last condition clear in a
    moment.
    
    The justification for this is that Theorem is true for all values of the
    variables.  Hence, it is true for the values specified by sigma'.  If
    the hypi' are true, then the target is really equal to replacement'.
    But it is always permitted to replace something by something it's equal
    to.
    
    Rewriting thus involves several steps:
    
    (1) Finding a target and a theorem to use to rewrite it to some more
    desirable replacement.
    
    (2) Instantiating pattern in the (rearranged) theorem to match target.
    
    (3) Extending sigma to sigma' to bind all the variables in Theorem.
    
    (4) Establishing that the sigma' instances of each of the hypi hold.
    This is called relieving the hypotheses of the theorem and is discussed
    in greater detail below.
    
    (5) Replacing the occurrence of target in the goal conjecture by the
    sigma' instance of replacement, provided all the hypotheses are
    relieved.
    
    Step (4) above, relieving the hypotheses, requires first identifying the
    "context" of the target in the goal conjecture.  To do this, use
    propositional calculus to rearrange the goal conjecture so the
    occurrence of target is in the conclusion and let context be the
    hypothesis.
    
         Rearranged Conjecture:
         (implies context
                  (... target ...))
    
    To relieve the hypotheses you must then prove each of the following
    subgoals:
    
         Subgoal to Relieve Hyp i:
         (implies context hypi').
    
    It is important to note that this description of rewriting with Theorem
    describes the process from a strictly logical perspective.  The syntax
    of the theorem and the goal don't matter.  You're free to use
    propositional calculus to rearrange them to put them into the
    appropriate forms to fit the descriptions given.  Clearly, if you have
    a candidate Theorem in the "wrong" form and but it can be rearranged
    with propositional calculus into the "right" form, then that rearranged
    theorem is also a Theorem and can be used as described.  But in the
    actual implementation of ACL2, the syntactic form of a proved Theorem
    affects how it is used by rewriting.  If a proved theorem takes the
    form of an implication concluding with an equality, ACL2 treats the
    left-hand side of the equality as pattern and the right-hand side as
    replacement, unless you tell it otherwise.  We'll discuss this later.
    
    Furthermore, being from the logical perspective this discussion of
    rewriting does not address (a) how you extend simga to sigma' - any
    extension will do provided it allows you to relieve the hypotheses.
    The ACL2 theorem prover uses certain heuristics which we'll discuss
    later, including methods by which you can guide the system in the
    selection.
    
    Crucially, it does not discuss whether it is a good idea to do a
    particular rewrite!  For example, the definitional equality:
    
         (equal (len x)
                (if (endp x)
                    0
                    (+ 1 (len (cdr x)))))
    
    may be used repeatedly, endlessly, to replace (len a) by an ever
    growing sequence of terms:
    
         (len a)
         =
         (if (endp a)
             0
             (+ 1 (len (cdr a))))
         =
         (if (endp a)
             0
             (+ 1 (if (endp (cdr a))
                      0
                      (+ 1 (len (cdr (cdr a)))))))
         = ...
    
    The ACL2 implmentation of rewriting uses certain heuristics and the you
    can guide the system in its choices.  We'll discuss this later.
    
    Now use your browser's Back Button to return to the example proof in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY,  Next: POST-INDUCTION-KEY-CHECKPOINTS,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY    further information on expanding definitions via rewriting
    
    We assume you've read about "instances" and picked up our basic
    terminology including the ideas of matching a pattern term to a target
    term, obtaining a substitution and how to instantiate a term with a
    substitution.  We use these notions below without further citation.
    
    In addition, we assume you've read about "rewriting" where we
    introduced the idea of treating a theorem (axiom, definition, or lemma)
    as a conditional rewrite rule and replaced one term by an equivalent one
    provided we can relieve the hypotheses.
    
    Suppose you're trying to prove formula1 and you transform it to
    formula2 by rewriting.  What happened?
    
         formula1:
         (implies (and (not (consp z))
                       (true-listp z))
                  (equal (rev (rev z)) z))
    
         formula2:
         (implies (and (not (consp z))
                       (equal z nil))
                  (equal (rev (rev z)) z))
    
    Evidently we replaced (true-listp z) by (equal z nil).  But how did
    that happen?  What really happened was the sequential application of
    several unconditional rewrites and the use of replacement of equals by
    equals.
    
    The definition of true-listp is:
    
         (defun true-listp (x)
           (if (consp x)
               (true-listp (cdr x))
               (equal x nil))).
    
    By rewriting once with the definition of true-listp, we transform
    formula1 to:
    
         formula1':
         (implies (and (not (consp z))
                       (if (consp z)
                           (true-listp (cdr z))
                           (equal z nil)))
                  (equal (rev (rev z)) z)).
    
    Note how the call of true-listp has been replaced by the entire body of
    true-listp.
    
    Next, note that the first hypothesis above is that (consp z) is false.
    That is, (not (consp z)) is the same as (equal (consp z) nil).  Thus,
    replacement of equals by equals means we can transform formula1' to
    
         formula1":
         (implies (and (not (consp z))
                       (if nil
                           (true-listp (cdr z))
                           (equal z nil)))
                  (equal (rev (rev z)) z)).
    
    (We will explore replacement of equals by equals later.)
    
    Furthermore, we know the basic axiom about if:
    
         Axiom if-nil:
         (if nil x y) = y.
    
    Rewriting with this particular axiom, using (if nil x y) as the pattern
    and y as the replacement, will transform formula1" to
    
         formula2:
         (implies (and (not (consp z))
                       (equal z nil))
                  (equal (rev (rev z)) z)).
    
    Often when we say we derived one formula from another by "expansion"
    and or by "rewriting" we take many rewrite steps, as here.  We
    typically use hypotheses of the formula without noting "replacement of
    equals by equals" as when we replaced (consp z) by nil, and we
    typically omit to mention the use of basic axioms like if-nil above.
    
    Now use your browser's Back Button to return to the example proof in
    logic-knowledge-taken-for-granted.
    
    
    File: acl2-doc-emacs.info,  Node: POST-INDUCTION-KEY-CHECKPOINTS,  Next: PRACTICE-FORMULATING-STRONG-RULES,  Prev: LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    POST-INDUCTION-KEY-CHECKPOINTS    reading post-induction key checkpoints
    
    Each post-induction key checkpoint is a theorem if and only if the
    original conjecture was a theorem.  The reason is that each subgoal
    produced by induction concludes with the original formula and
    simplification preserves equivalence.
    
    So if you see a post-induction key checkpoint that is not a theorem,
    stop looking at the checkpoints!  Your original conjecture is not a
    theorem!  Fix it.
    
    If you're convinced all the post-induction conjectures are theorems, ask
    whether each has the hypotheses you'd need to prove it.  If the case
    analysis feels inappropriate or induction hypotheses seem to be missing,
    then ACL2 might have done the wrong induction.  Find the induction
    scheme it did by reading the first induction message printed after the
    conjecture was submitted.  If it is wrong, then extend ACL2's induction
    analysis or tell ACL2 what induction to do, as explained shortly.
    
    But before you decide the induction hypothesis is missing, look closely
    for contradictions among the hypotheses of the checkpoint formula.  For
    example, perhaps one of the hypotheses is (MEMBER e x) and another is
    (NOT (MEMBER e' x')) where e, x, e', and x' are possibly complicated
    expressions.
    
    Is it possible that e and e' are equal and x and x' are equal?  If so,
    then the two hypotheses are contradictory and the checkpoint would be
    proved if you could find rules that would simplify those expressions to
    their common forms.  So look for theorems about those subexpressions.
    
    Or maybe you can get e and e' to reduce to some common d but but find
    that x and x' are really different.  Then ask whether
    
         (implies (member d x) (member d x'))
    
    If you could prove that, the key checkpoint would be proved.  Of course,
    you may need other hypotheses from the checkpoint to state your
    theorems.
    
    If you have been working your way through the tutorial introduction to
    the theorem prover, use your browser's Back Button now to
    introduction-to-key-checkpoints.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES,  Next: PRACTICE-FORMULATING-STRONG-RULES-1,  Prev: POST-INDUCTION-KEY-CHECKPOINTS,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES    a few simple checkpoints suggesting strong rules
    
    Consider these definitions:
    
         (defun rev (x)
           (if (endp x)
               nil
               (append (rev (cdr x)) (list (car x)))))
    
         (defun nats-below (j)
           (if (zp j)
               '(0)
               (cons j (nats-below (- j 1)))))
    
    We assume you are familiar with such ACL2 built-ins as append, member,
    subsetp and true-listp.  When we use throw-away names like FOO, BAR,
    and MUM below we mean to suggest some arbitrary function you shouldn't
    think about!  We're just trying to train your eye to ignore irrelevant
    things.
    
    Below are some terms that should suggest rewrite rules to you.  Imagine
    that each of these terms occurs in some Key Checkpoint.  What rules
    come to mind?  Try to think of the strongest rules you can.
    
    Term 1:
    (TRUE-LISTP (APPEND (FOO A) (BAR B)))
    
    Answers:  See *note PRACTICE-FORMULATING-STRONG-RULES-1::
    
    Term 2:
    (TRUE-LISTP (REV (FOO A)))
    
    Answers:  See *note PRACTICE-FORMULATING-STRONG-RULES-2::
    
    Term 3:
    (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))
    
    Answers:  See *note PRACTICE-FORMULATING-STRONG-RULES-3::
    
    Term 4:
    (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))
    
    Answers:  See *note PRACTICE-FORMULATING-STRONG-RULES-4::
    
    Term 5:
    (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))
    
    Answers:  See *note PRACTICE-FORMULATING-STRONG-RULES-5::
    
    Term 6:
    (MEMBER (FOO A) (NATS-BELOW (BAR B)))
    
    Answers:  See *note PRACTICE-FORMULATING-STRONG-RULES-6::
    
    We recommend doing all of these little exercises.  When you're
    finished, use your browser's Back Button to return to
    strong-rewrite-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES-1,  Next: PRACTICE-FORMULATING-STRONG-RULES-2,  Prev: PRACTICE-FORMULATING-STRONG-RULES,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES-1    rules suggested by (TRUE-LISTP (APPEND (FOO A) (BAR B)))
    
    What rules come to mind when looking at the following subterm of a Key
    Checkpoint?  Think of strong rules (see *note STRONG-REWRITE-RULES::).
    
         (TRUE-LISTP (APPEND (FOO A) (BAR B)))
    
    Obviously, you must think about the conditions under which (APPEND x y)
    returns a true-list.  Recall that APPEND concatentates x and y, with y
    being the terminal sublist.  Its definition is equivalent to
    
         (defun append (x y)
           (if (endp x)
               y
               (cons (car x)
                     (append (cdr x) y))))
    
    Technical Note:  Append is really a macro that permits you to write
    calls of append with more than two arguments.
    
    In a sense, append "expects" its arguments to be lists ending in nil,
    so-called true-listps.  (Such expectations are formalized in ACL2 by
    the notion of the "guard" <> of the function, but we strongly recommend
    not investigating guards until you're good at using the system.)
    
    New users frequently start every new theorem by listing all their
    expectations on the arguments of functions in the problem.  For
    example, if the new user wants to make some statement about when
    (append x y) is a true-listp, it is not uncommon for him or her first
    to write:
    
         (implies (and (true-listp x)
                       (true-listp y))
                  ...)
    
    to get "comfortable."  Then, thinking about when (append x y) is a
    true-listp is easy:  it always returns a true-listp.  It's always a
    true-listp."  This thinking produces the theorem:
    
         (defthm true-listp-append-really-weak
           (implies (and (true-listp x)
                         (true-listp y))
                    (true-listp (append x y))))
    
    You'll note we gave it a name suggesting it is "really weak."
    
    One sense in which it is weak is that it has an unnecessary hypothesis.
    If y is a true-listp, then (append x y) is too, whether x is a
    true-listp or not.  In ACL2, all functions are total.  Logically
    speaking, it doesn't matter whether endp expects its argument to be a
    true-listp or not, it behaves.  (Append x y) either returns y or a cons
    whose second argument is generated by append.  Thus, if y is a
    true-listp, the answer is too.  So here is an improved version of the
    rule:
    
         (defthm true-listp-append-weak
           (implies (true-listp y)
                    (true-listp (append x y))))
    
    We still think of it as "weak" because it has a hypothesis that limits
    its applicability.
    
    The strong version of the rule is
    
         (defthm true-listp-append-strong
           (equal (true-listp (append x y))
                  (true-listp y))).
    
    That is, append returns a true-listp precisely when its second argument
    is a true-listp.  We recommend that the strong version be made a
    :rewrite <> rule.
    
    The weak version of the rule allows us to reduce (TRUE-LISTP (APPEND x
    y)) to true if we can show that (TRUE-LISTP y) is true.  But suppose
    (TRUE-LISTP y) is actually false.  Then (TRUE-LISTP (APPEND x y)) would
    not simplify under the weak version of the rule.  But under the strong
    version it would simplify to NIL.
    
    Technical Note: The weak version of the rule is a useful
    :type-prescription <> rule.  The type mechanism cannot currently
    exploit the strong version of the rule.
    
    The strategy of "getting comfortable" by adding a bunch of hypotheses
    before you know you need them is not conducive to creating strong
    rules.  We tend to state the main relationship that we intuit about
    some function and then add the hypotheses we need to make it true.  In
    this case, there were no necessary hypotheses.  But if there are, we
    first identify them and then we ask "what can I say about the function
    if these hypotheses aren't true?"  and try to strengthen the statement
    still further.
    
    Use your browser's Back Button now to return to
    practice-formulating-strong-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES-2,  Next: PRACTICE-FORMULATING-STRONG-RULES-3,  Prev: PRACTICE-FORMULATING-STRONG-RULES-1,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES-2    rules suggested by (TRUE-LISTP (REV (FOO A)))
    
    What rules come to mind when looking at the following subterm of a Key
    Checkpoint?  Think of strong rules (see *note STRONG-REWRITE-RULES::).
    
         (TRUE-LISTP (REV (FOO A)))
    
    The definition of rev in this problem is
    
         (defun rev (x)
           (if (endp x)
               nil
               (append (rev (cdr x)) (list (car x)))))
    
    Since the definition terminates with an endp test and otherwise cdrs
    the argument, the author of rev was clearly expecting x to be a
    true-listp.  (Indeed, the "guard" <> for rev must require include
    (true-listp x) since that is endp's guard.)  So you're naturally
    justified in limiting your thoughts about (rev x) to x that are
    true-lists.  This gives rise to the theorem:
    
         (defthm true-listp-rev-weak
           (implies (true-listp x)
                    (true-listp (rev x))))
    
    This is the kind of thinking illustrated in the earlier append example
    (see *note PRACTICE-FORMULATING-STRONG-RULES-1::) and, to paraphrase Z
    in Men in Black, it exemplifies "everything we've come to expect from
    years of training with typed languages."
    
    But logically speaking, the definition of rev does not require x to be
    a true-listp.  It can be any object at all: ACL2 functions are total.
    Rev either returns nil or the result of appending a singleton list onto
    the right end of its recursive result.  That append always returns a
    true-listp since the singleton list is a true list.  (See *note
    PRACTICE-FORMULATING-STRONG-RULES-1::.)
    
    So this is a theorem and a very useful :rewrite <> rule:
    
         (defthm true-listp-rev-strong
           (true-listp (rev x))).
    
    Use your browser's Back Button now to return to
    practice-formulating-strong-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES-3,  Next: PRACTICE-FORMULATING-STRONG-RULES-4,  Prev: PRACTICE-FORMULATING-STRONG-RULES-2,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES-3    rules suggested by (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))
    
    What rules come to mind when looking at the following subterm of a Key
    Checkpoint?  Think of strong rules (see *note STRONG-REWRITE-RULES::).
    
         (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))
    
    Since (append x y) contains all the members of x and all the members of
    y, e is a member of (append x y) precisely when e is a member of x or
    of y.  So a strong statement of this is:
    
         (defthm member-append-strong-false
           (equal (member e (append x y))
                  (or (member e x)
                      (member e y))))
    
    However, this is not a theorem because member is not Boolean.  (Member
    e x), for example, returns the first tail of x that starts with e, or
    else nil.  To see an example of this formula that evaluates to nil, let
    
         e = 3
         x = '(1 2 3)
         y = '(4 5 6).
    
    Then the left-hand side, (member e (append x y)) evaluates to (3 4 5 6)
    while the right-hand side evaluates to (3).
    
    However, the two sides are propositionally equivalent (both either nil
    or non-nil together).  So this is a useful :rewrite <> rule:
    
         (defthm member-append-strong
           (iff (member e (append x y))
                (or (member e x)
                    (member e y)))).
    
    It tells the system that whenever it encounters an instance of (MEMBER
    e (APPEND x y)) in a propositional occurrence (where only its
    truthvalue is relevant), it should be replaced by this disjunction of
    (MEMBER e x) and (MEMBER e y).
    
    The following two formulas are true but provide much weaker rules and
    we would not add them:
    
         (implies (member e x) (member e (append x y)))
    
         (implies (member e y) (member e (append x y)))
    
    because they each cause the system to backchain upon seeing (MEMBER e
    (APPEND x y)) expressions and will not apply unless one of the two
    side-conditions can be established.
    
    There is a rewrite rule that is even stronger than member-append-strong.
    It is suggested by the counterexample, above, for the EQUAL version of
    the rule.
    
         (defthm member-append-really-strong
           (equal (member e (append x y))
                  (if (member e x)
                      (append (member e x) y)
                      (member e y))))
    
    While member-append-strong only rewrites member-append expressions
    occurring propositionally, the -really-strong version rewrites every
    occurrence.
    
    However, this rule will be more useful than member-append-strong only
    if you have occurrences of member in non-propositional places.  For
    example, suppose you encountered a term like:
    
         (CONS (MEMBER e (APPEND x y)) z).
    
    Then the -strong rule does not apply but the -really-strong rule does.
    
    Furthermore, the -really-strong rule, by itself, is not quite as good as
    the -strong rule in propositional settings!  For example, if you have
    proved the -really-strong rule, you'll notice that the system still has
    to use induction to prove
    
         (IMPLIES (MEMBER E A)
                  (MEMBER E (APPEND B A))).
    
    The -really-strong rule would rewrite it to
    
         (IMPLIES (MEMBER E A)
                  (IF (MEMBER E A)
                      (APPEND (MEMBER E A) B)
                      (MEMBER E B)))
    
    which would further simplify to
    
         (IMPLIES (MEMBER E A)
                  (APPEND (MEMBER E A) B))
    
    What lemma does this suggest?  The answer is the rather odd:
    
         (implies x (append x y))
    
    which rewrites propositional occurrences of (APPEND x y) to T if x is
    non-nil.  This is an inductive fact about append.
    
    A problem with the -really-strong rule is that it transforms even
    propositional occurrences of member into mixed propositional and
    non-propositional occurrences.
    
         (defthm member-append-really-strong
           (equal (member e (append x y))      ; <-- even if this is a propositional occurrence
                  (if (member e x)
                      (append (member e x) y)  ; <-- the member in here is not!
                      (member e y))))
    
    So if you are using the -really-strong lemma in a situation in which
    all your member expressions are used propositionally, you'll suddenly
    find yourself confronted with non-propositional uses of member.
    
    Our advice is not to use the -really-strong version unless your
    application is inherently using member in a non-propositional way.
    
    Use your browser's Back Button now to return to
    practice-formulating-strong-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES-4,  Next: PRACTICE-FORMULATING-STRONG-RULES-5,  Prev: PRACTICE-FORMULATING-STRONG-RULES-3,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES-4    rules suggested by (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))
    
    What rules come to mind when looking at the following subterm of a Key
    Checkpoint?  Think of strong rules (see *note STRONG-REWRITE-RULES::).
    
         (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))
    
    When is (append x y) a subset of z?  When everything in x is in z and
    everything in y is in z.  We would make it a rewrite rule:
    
         (defthm subsetp-append-1-strong
           (equal (subsetp (append x y) z)
                  (and (subsetp x z)
                       (subsetp y z))))
    
    We put the "-1-" in the name because there is a comparable theorem for
    when the append is in the second argument of the subsetp; see *note
    PRACTICE-FORMULATING-STRONG-RULES-5::.
    
    This strong rule is better than the conditional rule;
    
         (defthm subsetp-append-1-weak
           (implies (and (subsetp x z)
                         (subsetp y z))
                    (subsetp (append x y) z)))
    
    for all the usual reasons.
    
    Use your browser's Back Button now to return to
    practice-formulating-strong-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES-5,  Next: PRACTICE-FORMULATING-STRONG-RULES-6,  Prev: PRACTICE-FORMULATING-STRONG-RULES-4,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES-5    rules suggested by (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))
    
    What rules come to mind when looking at the following subterm of a Key
    Checkpoint?  Think of strong rules (see *note STRONG-REWRITE-RULES::).
    
         (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))
    
    When is x a subset of (append y z)?  Clearly it is if x is a subset of y
    or x is a subset of z.  We could write that:
    
         (defthm subsetp-append-2-weak
           (implies (or (subsetp x y)
                        (subsetp x z))
                    (subsetp x (append y z))))
    
    The rule generated from this is:  "if you ever encounter (an instance
    of) (SUBSETP x (APPEND y z)), backchain to the or above and try to
    establish it.  If you can establish it, replace the target by T."
    
    This does not fully characterize the situation though.  For example,
    '(1 2 3 4) is a subset of (append '(1 3) '(2 4)) without being a subset
    of either argument of the append.
    
    However, no obvious equivalence comes to mind - indeed, to express any
    of the ideas floating around here requires defining and introducing more
    functions, which is not recommended unless those functions are already
    in the problem.
    
    For example, if you defined the concept of "set-minus" so that
    (set-minus x y) consists of those elements of x not in y, then you
    could prove:
    
         (defthm subset-append-2-strong-but-messy
           (equal (subsetp x (append y z))
                  (and (subsetp (set-minus x z) y)
                       (subsetp (set-minus x y) z))))
    
    But this rewrite rule would "trade" append away and introduce set-minus.
    That might be a good strategy if set-minus were already in the problem.
    But if it were not, it might not be.  We wouldn't recommend this rule
    unless it were helpful in normalizing the expressions in the problem.
    
    We recommend sticking with the weak version of the rule,
    
         (defthm subsetp-append-2-weak
           (implies (or (subsetp x y)
                        (subsetp x z))
                    (subsetp x (append y z)))).
    
    This illustrates the fact that sometimes there is no strong version of
    a rule!
    
    Use your browser's Back Button now to return to
    practice-formulating-strong-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PRACTICE-FORMULATING-STRONG-RULES-6,  Next: PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED,  Prev: PRACTICE-FORMULATING-STRONG-RULES-5,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PRACTICE-FORMULATING-STRONG-RULES-6    rules suggested by (MEMBER (FOO A) (NATS-BELOW (BAR B)))
    
    What rules come to mind when looking at the following subterm of a Key
    Checkpoint?  Think of strong rules (see *note STRONG-REWRITE-RULES::).
    
         (MEMBER (FOO A) (NATS-BELOW (BAR B)))
    
    The definition of NATS-BELOW is
    
         (defun nats-below (j)
           (if (zp j)
               '(0)
               (cons j (nats-below (- j 1)))))
    
    Thus, (nats-below 7) is (7 6 5 4 3 2 1 0).  So when is k a member of
    (nats-below j)?
    
    The weakest version is
    
         (defthm member-nats-below-weak
           (implies (and (natp k)
                         (natp j)
                         (<= k j))
                    (member k (nats-below j))))
    
    But clearly we could improve this to:
    
         (defthm member-nats-below-weak-better
           (implies (and (natp k)
                         (natp j))
                    (iff (member k (nats-below j))
                         (<= k j))))
    
    or even
    
         (defthm member-nats-below-weak-better
           (implies (natp j)
                    (iff (member k (nats-below j))
                         (and (natp k)
                              (<= k j)))))
    
    Clearly though, we'd like to get rid of the (natp j) hypothesis and the
    neatest plausible version is:
    
         (defthm member-nats-below-weak-neatest
           (iff (member k (nats-below j))
                (and (natp j)
                     (natp k)
                     (<= k j))))
    
    But it is not a theorem!  For example, if j is -1 and k is 0, then the
    left-hand side above returns t, because (nats-below j) is (0), but the
    right-hand side is nil.
    
    But this suggests a strategy for dealing with necessary hypotheses,
    like (natp j).  We can move them into an IF on the right-hand side!
    Something like this might be a useful rewrite rule:
    
         (iff (member k (nats-below j))
              (if (natp j)
                  (and (natp k)
                       (<= k j))
                  ...)).
    
    We know, from member-nats-below-weak-better, that if (natp j) is true,
    the member is equivalent to (and (natp k) (<= k j)).  So now consider
    what we know if (natp j) is false.  If we can think of some term it's
    equivalent to nd that term is simpler than the member expression, we
    have a strong rule.
    
    But by inspection of the definition of nats-below, we see that when
    (natp j) is false, (nats-below j) is the list (0) because (zp j) is t.
    That is, nats-below treats all non-natural arguments like they were 0.
    Thus, when (natp j) is false, (member k (nats-below j)) is (member k
    '(0)), which is (equal k 0).
    
    So the strong version is
    
         (defthm member-nats-below-strong
            (iff (member k (nats-below j))
                 (if (natp j)
                     (and (natp k)
                          (<= k j))
                     (equal k 0))))
    
    This is a great :rewrite <> rule.  It gets rid of the member and
    nats-below and introduces arithmetic.
    
    This example illustrates the idea of putting an if on the
    right-hand-side of the equivalence.  Many users tend to limit
    themselves to propositional forms inside iff or to simple expressions
    inside of equal.  But it is quite natural to use if to express what the
    answer is:  if j is a natural, then k is in (nats-below j) precisely if
    k is a natural less than or equal to j; if j is not a natural, then k
    is in (nats-below j) precisely if k is 0.
    
    Use if to lay out the cases you must consider, if you can think of a
    simpler, equivalent expression for every possible case.
    
    Use your browser's Back Button now to return to
    practice-formulating-strong-rules.
    
    
    File: acl2-doc-emacs.info,  Node: PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED,  Next: SPECIAL-CASES-FOR-REWRITE-RULES,  Prev: PRACTICE-FORMULATING-STRONG-RULES-6,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED    background knowledge in ACL2 programming for theorem prover tutorial
    
    This brief review of the programming language is presented as a
    sequence of questions and answers meant to test your knowledge of the
    ACL2 programming language.  If you want a gentle introduction to the
    programming language, see
    `http://www.cs.utexas.edu/users/moore/publications/gentle-intro-to-acl2-programming.html'.
    
    Before we get started with the programming drill, let us remind you
    that all we're interested in here is the language, not the "program
    development environment."  It's impossible to program in ACL2 or any
    other language without a decent environment, one that at the very least
    includes a way to prepare and edit files of programs.  The two most
    popular program development environments among ACL2 users are Emacs <>
    and the Eclipse-based ACL2-Sedan <>.  The Sedan provides the most
    support for the new user, including real-time syntax checking and a
    facility for testing among many other features.  But in this drill
    we're not interested in the program development environment, we're
    interested in your understanding of the ACL2 language.
    
    Q: What do you think this command does?
    
         (defun rev (x)
           (if (endp x)
               nil
               (append (rev (cdr x)) (list (car x)))))
    
    A:  It defines a function named rev that takes one argument, treats it
    like a list, and reverses the order of the elements in that list.  To
    figure this out from the definition, you have to know that append
    concatenates two lists.  Logically speaking, the defun of rev adds the
    axiom:
    
         (rev x)
         =
         (if (endp x)
             nil
             (append (rev (cdr x)) (list (car x)))),
    
    implicitly quantified for all x.
    
    Q:  Given the defun of rev above, what are the formal parameters?  What
    is the body of the definition?  Write down a call of append that occurs
    in the body of rev.  What are the actuals of that call?  A:  The
    formals of rev is the list of variables after the first rev in the
    defun, namely (x).  We say x is the first (and only) formal here.  The
    body of rev is the entire if-expression.  The only call of append in
    the body is
    
         (append (rev (cdr x)) (list (car x)))
    
    and the actuals of that call are, respectively, (rev (cdr x)) and (list
    (car x)).
    
    Q: What do you get if you evaluate (rev '(a b c d))?  A: (D C B A).
    
    Q: How did rev change the case of the elements, e.g., lowercase a was
    in the input list but uppercase A was in the output?  A: This is a
    trick question.  Rev doesn't change the case of the elements.  ACL2 is
    case-insensitive when dealing with symbols.  The symbol a is read in as
    the symbol A.  Thus, when writing function names, for example, we can
    write rev, Rev, REV, or even ReV and always be referring to the
    function REV.  By default, ACL2 prints symbols in uppercase.
    
    Q: What does (rev '((a b c) "Abc" "a" b #\c)) return?  A: (#\c B "a"
    "Abc" (A B C)).  If you thought the answer was any of these, then you
    need to think or read more carefully:
    
         (#\C B "A" "ABC" (A B C))
    
         (#\C B "A" "ABC" (C B A))
    
    The first wrong answer above is wrong because Lisp is "case
    insensitive" only for symbols, not for character objects like #\c (the
    lowercase character c) or for strings.   Furthermore, "A" is a string,
    not a symbol; it is different from A.  The second wrong answer above is
    wrong because rev does not go into the individual elements of the list,
    it just reverses the order of the elements.  So it doesn't change the
    element (A B C) to (C B A).
    
    Q: In the question about what (rev '(a b c d)) returns, we put a quote
    mark before the (a b c d) but not before the answer, (D C B A).  Why?
    A:  The phrase "x evaluates to y" treats x as a term to be evaluated
    and y as an object.  (Rev '(a b c d)) is a term to be evaluated and
    denotes a call of the function rev on the value of the argument term
    '(a b c d).  The value of that argument term is the object (a b c d).
    The value of the call of rev is the object (d c b a).  If you have an
    object, obj, and you wish to create a term whose value is obj, then you
    put a quote mark in front of it, 'obj.
    
    Q: Can rev be applied to something other than a list?  A:  Yes, every
    ACL2 function can be applied to any object.  ACL2 is an untyped
    programming language:  every variable ranges over the entire universe of
    objects.  In normal usage, rev is applied to lists but there is nothing
    about the syntax of the language that prevents it being applied to
    non-lists.
    
    Q: So what does (rev 23) evaluate to?  A:  Nil.
    
    Q: Why?  A:  Because (endp 23) is t, because endp is defined:
    
         (defun endp (x) (not (consp x)))
    
    Thus, if rev is applied to anything that is not a cons, it returns nil.
    
    Q: So what does (rev '(a b c . d)) evaluate to?  A:  (c b a).  To
    explain why requires demonstrating that you know what (a b c . d)
    means.  It is the object computed by evaluating:
    
         (cons 'a
               (cons 'b
                     (cons 'c
                           'd))).
    
    That is, it is a list whose "terminal marker" is the atom D.  Rev
    treats that list exactly as it treats the nil-terminated list of the
    same elements, (a b c), because (endp 'D) = (endp nil) = t.
    
    Q: What does (rev 1 2 3) evaluate to?  A: That's a trick question.  Rev
    takes one argument, not three.  So (rev 1 2 3) is an ill-formed term.
    
    Q: What does (rev '(a b c . d . nil)) evaluate to?  A:  That is a trick
    question.  There is no such object.  In Lisp's "dot notation" every dot
    must be followed by a well-formed object and then a close parenthesis.
    Usually that "well-formed object" is an atom.  If it is not an atom,
    i.e., if it is a cons, then the entire expression could have been
    written without that dot.  For example, (a b c . (d e)) is an object,
    but it could be written (a b c d e).
    
    Q: Do (rev (rev x)) and x always evaluate to the same object?  A: No.
    (Rev (rev 23)) evaluates to nil, not 23.
    
    Q: Do (rev (rev x)) and x always evaluate to the same object when x is
    a cons?  A:  No.  (rev (rev '(a b c . d))) evaluates to (a b c), not (a
    b c . d).
    
    Q: When are (rev (rev x)) and x equal?  A: When the terminal marker of
    x is nil.
    
    Q: Can you define a Lisp function that recognizes nil-terminated lists?
    A:  Yes, but it is not necessary for the user to define that concept
    because Common Lisp provides such a function which is logically defined
    as follows:
    
         (defun true-listp (x)
           (if (consp x)
               (true-listp (cdr x))
               (equal x nil))).
    
    This can be paraphrased: (true-listp x) means that if x is a cons, its
    cdr is a true-listp and if x is not a cons, it must be nil.  Thus,
    (true-listp '(a b c)) is t and (true-listp '(a b c . d)) is nil.
    
    Q: Can you write a Lisp formula that says "If z is a nil-terminated
    list then reversing the result of reversing z is z"?
    
    A:  Yes:
    
         (implies (true-listp z)
                  (equal (rev (rev z)) z)).
    
    Q:  Is this all there is to ACL2 programming?  A:  No!  ACL2 provides
    many other features.  For a full list of all the primitive functions in
    ACL2 see *note PROGRAMMING:: <>.  Some highlights for the beginner are
    mentioned below, but all of the links below ought to be tagged with the
    <> sign.
    
    * list:  build a nil-terminated list from the values of n terms, e.g.,
    (list x (+ 1 x) (+ 2 x)) returns (3 4 5) if x is 3.
    
    * list*: build a non-nil terminated list of n objects from the values
    of n+1 terms, e.g., (list* x (+ 1 x) (+ 2 x) (* -1 x)) returns the list
    (3 4 5 . -3) if x is 3.
    
    * and, or, not, implies, iff:  The propositional connectives.  And and
    or are allowed to take a varying number of arguments, e.g., (and p q r)
    is just an abbreviation for (and p (and q r)).  In Lisp, and returns
    nil if any of its arguments evaluates to nil; otherwise it returns the
    value of the last argument!  Thus, (and t t 3) returns 3!  If you
    object to the idea that and is not Boolean, don't give it non-Boolean
    arguments!  Similarly, or returns the value of the first argument that
    evaluates to non-nil, or nil if they all evaluate to nil.  Both and and
    or can be thought of as "lazy" in that they don't always have to
    evaluate all their arguments.  This is really accomplished by treating
    and and or as abbrevations for if nests.
    
    * +, *, -, /, floor, mod, <, <=, >=, >: the Lisp elementary arithmetic
    operators.  Both + and * allow varying numbers of arguments.  All the
    arithmetic operators default non-numeric arguments to 0.  If you don't
    like the idea that (+ 1 2 t) is 3, don't ask + to add t to something!
    
    * natp, integerp, rationalp, characterp, stringp, symbolp, consp:  the
    recognizers for the primitive data types.  The first three recognize
    subsets of the ACL2 numeric universe.  The naturals are a subset of the
    integers, the integers are a subset of the rationals, and the rationals
    are a subset of the objects recognized by acl2-numberp, which also
    includes the complex-rationalps.  The other recognizers listed above
    recognize characters, strings, symbols, and conses.
    
    * cond:  a convenient way to write a cascading nest of ifs, e.g.,
    
         (cond ((not (natp x)) 'non-natural)
               ((equal x 0) 'zero)
               ((evenp x) 'positive-even)
               (t 'positive-odd))
    
    abbreviates
    
         (if (not (natp x))
             'non-natural
             (if (equal x 0)
                 'zero
                 (if (evenp x)
                     'positive-even
                     'positive-odd))).
    
    * case:  a convenient way to case split on the identity of an object.
    
         (case key
           (non-natural -1)
           (zero 0)
           ((positive-even positive-odd) 'positive-natural)
           (otherwise 'unknown))
    
    abbreviates
    
         (cond ((eql key 'non-natural) -1)
               ((eql key 'zero) 0)
               ((member key '(positive-even positive-odd))
                'positive-natural)
               (t 'unknown)).
    
    * user defined macros:  using defmacro <> you can introduce your own
    abbreviations.  We recommend you not do this until you're good at list
    processing since macros are functions that build objects representing
    terms.
    
    * mutual-recursion:  allows you to define mutually-recursive functions.
    
    * mv and mv-let:  allow functions to return "multiple-values".  In
    Lisp, such functions return vectors of values, the vectors are
    represented as lists of values, but the implementations are generally
    more efficient.  For example, (mv x y z) returns a "vector" consisting
    of the values of x, y, and z.
    
         (mv-let (a b c)
                 (foo x)
                 (bar a b c x))
    
    evaluates (foo x), treats the result as a vector of three values, binds
    the variables a, b, and c to those three values, and evaluates and
    returns (bar a b c x).
    
    ACL2 also provides many other features, such as single-threaded objects
    which may be "destructively modified" (see *note STOBJ:: <>, including
    a very special single-threaded object that records the state <> of the
    ACL2 system), file input and output (see *note IO:: <>), applicative
    arrays (see *note ARRAYS:: <>) and property lists (see *note GETPROP::
    <>) and other facilities necessary for it to be a practical programming
    language.  However, we strongly recommend that as a new user you stay
    away from these features until you are good at proving theorems about
    elementary list processing!
    
    If this little drill made sense to you, you know enough of the
    programming language to get started. Use your browser's Back Button now
    to return to introduction-to-the-theorem-prover.
    
    If you are uncomfortable with ACL2 programming, we recommend that you
    study
    `http://www.cs.utexas.edu/users/moore/publications/gentle-intro-to-acl2-programming.html'
    and
    `http://www.cs.utexas.edu/users/moore/publications/acl2-programming-exercises1.html'.
    
    However, we strongly recommend that you first invest in learning either
    the Emacs or Eclipse-based ACL2-Sedan program development environments,
    since it is foolish to try to learn how to program in a stand-alone
    read-eval-print loop!
    
    While getting started, many users find the Hyper-Card a handy index
    into the documentation for the ACL2 language:
    
    `http://www.cs.utexas.edu/users/moore/publications/hyper-card.html'
    
    Once you are comfortable with the ACL2 programming language, use your
    browser's Back Button to return to introduction-to-the-theorem-prover.
    
    
    File: acl2-doc-emacs.info,  Node: SPECIAL-CASES-FOR-REWRITE-RULES,  Next: SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES,  Prev: PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    SPECIAL-CASES-FOR-REWRITE-RULES    convenient short forms for rewrite rule formulas
    
    In principle, every rewrite rule is made from a formula of this shape:
    
         (IMPLIES (AND hyp1 ... hypk)
                  (eqv lhs rhs))
    
    where eqv is either EQUAL or IFF and the result of expanding any
    abbreviations in lhs is the application of some function symbol other
    than IF.
    
    * In the special case where there is only one hyp term, i.e., k=1, the
    (AND hyp1) can be written hyp1.
    
    * In the special case where there are no hyp terms, k=0, the (AND) term
    is logically just T and the whole IMPLIES can be dropped; such a
    formula may be written as an unconditional EQUAL or IFF term.
    
    * If you build a rewrite rule from a formula that concludes with (NOT
    x), it is treated as though it were (EQUAL x NIL), which is logically
    equivalent to what you typed.
    
    * If you build a rewrite rule from a formula that concludes with an
    AND, ACL2 will build a rewrite rule for each conjunct of the AND.  This
    is because
    
         (IMPLIES hyp (AND concl1 concl2))
    
    is propositionally equivalent to
    
         (AND (IMPLIES hyp concl1)
              (IMPLIES hyp concl2)).
    
    However, if you use an OR-expression as a hypothesis, ACL2 does not do
    the dual transformation.  Thus, (IMPLIES (OR hyp1 hyp2) concl)
    generates one rewrite rule.
    
    * Finally, if you build a rewrite rule from a formula that does not
    conclude with an EQUAL, an IFF, a NOT, or an AND, but with some other
    term, say, lhs, then ACL2 acts like you typed (IFF lhs T), which is
    logically equivalent to what you typed.
    
    Thus, regardless of what you type, every rule has k hypotheses.  For
    unconditional rules, k is 0 and the hypotheses are vacuously true.
    Whether or not you write an EQUAL or an IFF in the conclusion, every
    rule is either an equality or a propositional equivalence, every rule
    has a left-hand side, and every rule has a right-hand side.
    
    Use your browser's Back Button now to return to
    introduction-to-rewrite-rules-part-1.
    
    
    File: acl2-doc-emacs.info,  Node: SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES,  Next: STRONG-REWRITE-RULES,  Prev: SPECIAL-CASES-FOR-REWRITE-RULES,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES    advice about how to handle commonly occurring formulas as rewrite rules
    
    Below we give you some guidelines for handling specific, commonly
    occurring situations.
    
    * Associativity:  If a function f is associative, prove
    
         (equal (f (f x y) z) (f x (f y z)))
    
    ACL2 will use this to flatten f-nests "to the right."
    
    * Commutativity:  If a function f is commutative, prove both
    
         (equal (f x y) (f y x))
    
    and
    
         (equal (f x (f y z)) (f y (f x z)))
    
    ACL2's heuristics will use these rules to order the arguments
    alphabetically, so that (f B (f D (f A C))) becomes (f A (f B (f C D))).
    
    * Distributivity:  If you have a pair of functions f and g so that (f x
    (g y z)) is (g (f x y) (f x z)) or some other form of distributivity is
    provable, arrange your rules to move the lighter function symbol up and
    the heavier one toward the variable symbols.  For example, our
    arithmetic libraries drive multiplication through addition, producing
    sums of products rather than products of sums.
    
    * Identity and Other Laws: Prove the obvious identity and zero laws (or
    at least anticipate that you might need them down the road) so as to
    eliminate operators.
    
    * Get Rid of Tail Recursive Functions: A corollary to the above advice
    concerns tail recursive functions that use auxiliary variables.  New
    users often define concepts using tail recursions, accumulating partial
    results in auxiliary variables, because creating such functions is
    similar to programming with while loops.  Expert users will use tail
    recursion when necessary for execution efficiency.  But tail recursive
    functions are messy to reason about: their auxiliary variables have to
    be properly initialized to make the functions compute the expected
    results, but to state inductively provable properties of tail recursive
    functions you must identify the invariants on those auxiliary
    variables.  This problem tends not to happen with primitive recursive
    functions.  A primitive recursive function is one that recurs down one
    variable and holds all the other variables constant in recursion.  Most
    tail-recursive functions can be written elegantly as primitive
    recursive functions, though one might have to ignore the programmer's
    desire to make things efficient and define auxiliary functions to
    appropriately transform the value returned by the recursive call.  The
    classic example is reverse defined in terms of the auxiliary function
    append versus reverse defined tail recursively with an accumulator.  By
    introducing append you introduce a concept about which you can state
    lemmas and decompose the proofs of properties of reverse.  So if your
    problem involves tail recursive functions with auxiliary variables,
    define the primitive recursive version, prove that the tail recursive
    function is equivalent to the primitive recursive one, and arrange the
    rewrite rule to eliminate the tail recursive function.
    
    * Get Rid of Mutually Recursive Functions: Similarly, if you have used
    mutual-recursion to introduce a clique of mutually recursive functions,
    f1, f2, ..., you will find that to reason about any one function in the
    nest you have to reason about all of them.  Any mutually recursive
    function can be defined in a singly recursive way.  So do that and then
    prove a rewrite rule that gets rid of all the mutually recursive
    functions by proving
    
         (and (equal (f1 ...) (g1 ...))
              (equal (f2 ...) (g2 ...))
              ...)
    
    where the gi are singly recursive.  You may need to appeal to a trick to
    define the gi: define a singly recursive function that takes a flag
    argument and mimics whichever mutually recursive function the flag
    specifies.  See *note MUTUAL-RECURSION:: <> and see *note
    MUTUAL-RECURSION-PROOF-EXAMPLE:: <>.
    
    If you got to this documentation page from the tutorial discussion of
    rewrite rules, use your browser's Back Button now to return to
    introduction-to-rewrite-rules-part-2.
    
    
    File: acl2-doc-emacs.info,  Node: STRONG-REWRITE-RULES,  Prev: SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES,  Up: INTRODUCTION-TO-THE-THEOREM-PROVER
    
    STRONG-REWRITE-RULES    formulating good rewrite rules
    
    Suppose you notice the following term in a Key Checkpoint:
    
         (MEMBER (CAR A) (REV B)).
    
    You might think of two theorems for handling this term, which we'll
    call the "weak" and "strong" version of member-rev.
    
         (defthm member-rev-weak
           (implies (member e b)
                    (member e (rev b)))).
    
         (defthm member-rev-strong
           (iff (member e (rev b))
                (member e b))).
    
    The "strong" version logically implies the "weak" version and so
    deserves the adjective.  (Recall that "p <--> q" is just "p --> q" and
    "q --> p."  So the strong version quite literally says everything the
    weak version does and then some.)
    
    But the "strong" version also produces a better rewrite rule.  Here are
    the rules generated from these two formulas, phrased as directives to
    ACL2's simplifier:
    
    member-rev-weak: If you ever see an instance of (MEMBER e (REV b)),
    backchain to (MEMBER e b) (i.e., turn your attention to that term) and
    if you can show that it is true, replace (MEMBER e (REV b)) by T.
    
    member-rev-strong:  If you ever see an instance of (MEMBER e (REV b)),
    replace it by (MEMBER e b).
    
    Technical Note: Actually, both rules concern propositional occurrences
    of the "target" expression, (MEMBER e (REV b)), i.e., occurrences of
    the target in which its truthvalue is the only thing of relevance.
    (Recall that (MEMBER x y) returns the tail of y starting with x!
    Evaluate some simple MEMBER expressions, like (MEMBER 3 '(1 2 3 4 5))
    to see what we mean.)  Both theorems tell us about circumstances in
    which the target is non-NIL (i.e., "true") without telling us its
    identity.  But ACL2 keeps track of when the target is in a
    propositional occurrence and can use such rules to rewrite the target
    to propositionally equivalent terms.
    
    So the strong version is better because it will always eliminate
    (MEMBER e (REV b)) in favor of (MEMBER e b).  That simpler expression
    may be further reduced if the context allows it.  But in any case, the
    strong version eliminates REV from the problem.  The weak version only
    eliminates REV when a side-condition can be proved.
    
    While either version may permit us to prove the Key Checkpoint that
    "suggested" the rule, the strong version is a better rule to have in the
    database going forward.
    
    For example, suppose NATS-BELOW returns the list of natural numbers
    below its argument.  Imagine two alternative scenarios in which a key
    checkpoint is about to arise involving this term:
    
         (MEMBER K (REV (NATS-BELOW J)))
    
    Scenario 1 is when you've got the strong version in your database: it
    will rewrite the key checkpoint term to
    
         (MEMBER K (NATS-BELOW J))
    
    and that's what you'll see in the printed checkpoint unless other rules
    reduce it further.
    
    Scenario 2 is when you have only the weak version in your database: the
    weak rule will attempt to reduce the term above to T and will, if there
    are sufficient rules and hypotheses about K's membership in (NATS-BELOW
    J).  But if no such rules or hypotheses are available, you'll be
    confronted with a key checkpoint involving
    
         (MEMBER K (REV (NATS-BELOW J)))
    
    and it will not be obvious that the problem would go away if you could
    establish that K is in (NATS-BELOW J).  Clearly, Scenario 1 is better.
    
    We recommend that you now practice formulating strong versions of rules
    suggested by terms you might see.  See *note
    PRACTICE-FORMULATING-STRONG-RULES::.
    
    When you are finished with that, use your browser's Back Button to
    return to introduction-to-key-checkpoints.
    
    
    File: acl2-doc-emacs.info,  Node: STARTUP,  Next: TIDBITS,  Prev: INTRODUCTION-TO-THE-THEOREM-PROVER,  Up: ACL2-TUTORIAL
    
    STARTUP    How to start using ACL2; the ACL2 command loop
    
    When you start up ACL2, you'll probably find yourself inside the ACL2
    command loop, as indicated by the following prompt.
    
    
           ACL2 !>
    
    If not, you should type (LP).  See *note LP::, which has a lot more
    information about the ACL2 command loop.
    
    You should now be in ACL2.  The current "default-defun-mode" is :logic;
    the other mode is :program, which would cause the letter p to be
    printed in the prompt.  :Logic means that any function we define is not
    only executable but also is axiomatically defined in the ACL2 logic.
    See *note DEFUN-MODE:: and see *note DEFAULT-DEFUN-MODE::.  For example
    we can define a function my-cons as follows.  (You may find it useful
    to start up ACL2 and submit this and other commands below to the ACL2
    command loop, as we won't include output below.)
    
    
           ACL2 !>(defun my-cons (x y) (cons x y))
    
    An easy theorem may then be proved:  the car of (my-cons a b) is A.
    
    
           ACL2 !>(defthm car-my-cons (equal (car (my-cons a b)) a))
    
    You can place raw Lisp forms to evaluate at start-up into file
    acl2-init.lsp in your home directory, except on Windows systems.  For
    example, if you put the following into acl2-init.lsp, then ACL2 will
    print "HI" when it starts up.
    
         (print "HI")
    
    But be careful; all bets are off when you submit forms to raw Lisp, so
    this capability should only be used when you are hacking or when you
    are setting some Lisp parameters (e.g., (setq si::*notify-gbc* nil) to
    turn off garbage collection notices in GCL).
    
    Notice that unlike Nqthm, the theorem command is defthm rather than
    prove-lemma.  See *note DEFTHM::, which explains (among other things)
    that the default is to turn theorems into rewrite rules.
    
    Various keyword commands are available to query the ACL2 "world", or
    database.  For example, we may view the definition of my-cons by
    invoking a command to print events, as follows.
    
    
           ACL2 !>:pe my-cons
    
    Also see *note PE::.  We may also view all the lemmas that rewrite
    terms whose top function symbol is car by using the following command,
    whose output will refer to the lemma car-my-cons proved above.
    
    
           ACL2 !>:pl car
    
    Also see *note PL::.  Finally, we may print all the commands back
    through the initial world as follows.
    
    
           ACL2 !>:pbt 0
    
    See *note HISTORY:: for a list of commands, including these, for
    viewing the current ACL2 world.
    
    Continue with the documentation for annotated-acl2-scripts to see a
    simple but illustrative example in the use of ACL2 for reasoning about
    functions.
    
    
    File: acl2-doc-emacs.info,  Node: TIDBITS,  Next: TIPS,  Prev: STARTUP,  Up: ACL2-TUTORIAL
    
    TIDBITS    some basic hints for using ACL2
    
    See *note BOOKS:: for a discussion of books.  Briefly, a book is a file
    whose name ends in ".lisp" that contains ACL2 events; see *note
    EVENTS::.
    
    See *note HISTORY:: for a list of useful commands.  Some examples:
    
    
           :pbt :here      ; print the current event
           :pbt (:here -3) ; print the last four events
           :u              ; undo the last event
           :pe append      ; print the definition of append
    
    See *note DOCUMENTATION:: to learn how to print documentation to the
    terminal.  There are also versions of the documentation for Mosaic,
    Emacs Info, and hardcopy.
    
    There are quite a few kinds of rules allowed in ACL2 besides :rewrite
    rules, though we hope that beginners won't usually need to be aware of
    them.  See *note RULE-CLASSES:: for details.  In particular, there is
    support for congruence rewriting.  See *note RUNE:: ("RUle NamE") for a
    description of the various kinds of rules in the system.  Also see
    *note THEORIES:: for a description of how to build theories of runes,
    which are often used in hints; see *note HINTS::.
    
    A "programming mode" is supported; see *note PROGRAM::, see *note
    DEFUN-MODE::, and see *note DEFAULT-DEFUN-MODE::.  It can be useful to
    prototype functions after executing the command :program, which will
    cause definitions to be syntaxed-checked only.
    
    ACL2 supports mutual recursion, though this feature is not tied into
    the automatic discovery of induction schemas and is often not the best
    way to proceed when you expect to be reasoning about the functions.
    See *note DEFUNS::; also see *note MUTUAL-RECURSION::.
    
    See *note LD:: for discussion of how to load files of events.  There
    are many options to ld, including ones to suppress proofs and to
    control output.
    
    The :otf-flg (Onward Thru the Fog FLaG) is a useful feature that Nqthm
    users have often wished for.  It prevents the prover from aborting a
    proof attempt and inducting on the original conjecture.  See *note
    OTF-FLG::.
    
    ACL2 supports redefinition and redundancy in events; see *note
    LD-REDEFINITION-ACTION:: and see *note REDUNDANT-EVENTS::.
    
    A proof-tree display feature is available for use with Emacs.  This
    feature provides a view of ACL2 proofs that can be much more useful
    than reading the stream of characters output by the theorem prover as
    its "proof."  See *note PROOF-TREE::.
    
    An interactive feature similar to Pc-Nqthm is supported in ACL2.  See
    *note VERIFY:: and see *note PROOF-CHECKER::.
    
    ACL2 allows you to monitor the use of rewrite rules.  See *note
    BREAK-REWRITE::.
    
    See *note ARRAYS:: to read about applicative, fast arrays in ACL2.
    
    To quit the ACL2 command loop, or (in akcl) to return to the ACL2
    command loop after an interrupt, type :q.  To continue (resume) after
    an interrupt (in akcl), type :r.  To cause an interrupt (in akcl under
    Unix (trademark of AT&T)), hit control-C (twice, if inside Emacs).  To
    exit ACL2 altogether, first type :q to exit the ACL2 command loop, and
    then exit Lisp (by typing (user::bye) in akcl).
    
    See *note STATE:: to read about the von Neumannesque ACL2 state object
    that records the "current state" of the ACL2 session.  Also see *note
    @: atsign, and see *note ASSIGN::, to learn about reading and setting
    global state variables.
    
    If you want your own von Neumannesque object, e.g., a structure that
    can be "destructively modified" but which must be used with some
    syntactic restrictions, see *note STOBJ::.
    
    
    File: acl2-doc-emacs.info,  Node: TIPS,  Prev: TIDBITS,  Up: ACL2-TUTORIAL
    
    TIPS    some hints for using the ACL2 prover
    
    We present here some tips for using ACL2 effectively.  Though this
    collection is somewhat _ad hoc_, we try to provide some organization,
    albeit somewhat artificial:  for example, the sections overlap, and no
    particular order is intended.  This material has been adapted by Bill
    Young from a very similar list for Nqthm that appeared in the
    conclusion of:  "Interaction with the Boyer-Moore Theorem Prover: A
    Tutorial Study Using the Arithmetic-Geometric Mean Theorem," by Matt
    Kaufmann and Paolo Pecchiari, CLI Technical Report 100, June, 1995.  We
    also draw from a similar list in Chapter 13 of "A Computational Logic
    Handbook" by R.S. Boyer and J S. Moore (Academic Press, 1988).  We'll
    refer to this as "ACLH" below.
    
    These tips are organized roughly as follows.
    
         A. ACL2 Basics
    
         B. Strategies for creating events
    
         C. Dealing with failed proofs
    
         D. Performance tips
    
         E. Miscellaneous tips and knowledge
    
         F. Some things you DON'T need to know
    
    
    _ACL2 BASICS_
    
    A1. The ACL2 logic.
    This is a logic of total functions.  For example, if A and B are less
    than or equal to each other, then we need to know something more in
    order to conclude that they are equal (e.g., that they are numbers).
    This kind of twist is important in writing definitions; for example, if
    you expect a function to return a number, you may want to apply the
    function fix or some variant (e.g., nfix or ifix) in case one of the
    formals is to be returned as the value.
    
    ACL2's notion of ordinals is important on occasion in supplying
    "measure hints" for the acceptance of recursive definitions.  Be sure
    that your measure is really an ordinal.  Consider the following
    example, which ACL2 fails to admit (as explained below).
    
    
           (defun cnt (name a i x)
             (declare (xargs :measure (+ 1 i)))
             (cond ((zp (+ 1 i))
                    0)
                   ((equal x (aref1 name a i))
                    (1+ (cnt name a (1- i) x)))
                   (t (cnt name a (1- i) x))))
    
    One might think that (+ 1 i) is a reasonable measure, since we know
    that (+ 1 i) is a positive integer in any recursive call of cnt, and
    positive integers are ACL2 ordinals (see *note O-P::).  However, the
    ACL2 logic requires that the measure be an ordinal unconditionally, not
    just under the governing assumptions that lead to recursive calls.  An
    appropriate fix is to apply nfix to (+ 1 i), i.e., to use
    
    
           (declare (xargs :measure (nfix (+ 1 i))))
    
    in order to guarantee that the measure will always be an ordinal (in
    fact, a positive integer).
    
    For more about admissibility of recursive definitions, see *note
    DEFUN::, in particular the discussion of termination.
    
    A2. Simplification.
    The ACL2 simplifier is basically a rewriter, with some "linear
    arithmetic" thrown in.  One needs to understand the notion of
    conditional rewriting.  See *note REWRITE::.
    
    A3. Parsing of rewrite rules.
    ACL2 parses rewrite rules roughly as explained in ACLH, _except_ that
    it never creates "unusual" rule classes.  In ACL2, if you want a
    :linear rule, for example, you must specify :linear in the
    :rule-classes.  See *note RULE-CLASSES::, and also see *note REWRITE::
    and see *note LINEAR::.
    
    A4. Linear arithmetic.
    On this subject, it should suffice to know that the prover can handle
    truths about + and -, and that linear rules (see above) are somehow
    "thrown in the pot" when the prover is doing such reasoning.  Perhaps
    it's also useful to know that linear rules can have hypotheses, and
    that conditional rewriting is used to relieve those hypotheses.
    
    A5. Events.
    Over time, the expert ACL2 user will know some subtleties of its
    events.  For example, in-theory events and hints are important, and
    they distinguish between a function and its executable counterpart.
    
    _B. STRATEGIES FOR CREATING EVENTS_
    
    In this section, we concentrate on the use of definitions and rewrite
    rules.  There are quite a few kinds of rules allowed in ACL2 besides
    rewrite rules, though most beginning users probably won't usually need
    to be aware of them.  See *note RULE-CLASSES:: for details.  In
    particular, there is support for congruence rewriting.  Also see *note
    RUNE:: ("RUle NamE") for a description of the various kinds of rules in
    the system.
    
    B1. Use high-level strategy.
    Decompose theorems into "manageable" lemmas (admittedly, experience
    helps here) that yield the main result "easily."  It's important to be
    able to outline non-trivial proofs by hand (or in your head).  In
    particular, avoid submitting goals to the prover when there's no reason
    to believe that the goal will be proved and there's no "sense" of how
    an induction argument would apply.  It is often a good idea to avoid
    induction in complicated theorems unless you have a reason to believe
    that it is appropriate.
    
    B2. Write elegant definitions.
    Try to write definitions in a reasonably modular style, especially
    recursive ones.  Think of ACL2 as a programming language whose
    procedures are definitions and lemmas, hence we are really suggesting
    that one follow good programming style (in order to avoid duplication
    of "code," for example).
    
    When possible, complex functions are best written as compositions of
    simpler functions.  The theorem prover generally performs better on
    primitive recursive functions than on more complicated recursions (such
    as those using accumulating parameters).
    
    Avoid large non-recursive definitions which tend to lead to large case
    explosions.  If such definitions are necessary, try to prove all
    relevant facts about the definitions and then disable them.
    
    Whenever possible, avoid mutual recursion if you care to prove anything
    about your functions.  The induction heuristics provide essentially no
    help with reasoning about mutually defined functions.  Mutually
    recursive functions can usually be combined into a single function with
    a "flag" argument.  (However, see *note
    MUTUAL-RECURSION-PROOF-EXAMPLE:: for a small example of proof involving
    mutually recursive functions.)
    
    B3. Look for analogies.
    Sometimes you can easily edit sequences of lemmas into sequences of
    lemmas about analogous functions.
    
    B4. Write useful rewrite rules.
    As explained in A3 above, every rewrite rule is a directive to the
    theorem prover, usually to replace one term by another.  The directive
    generated is determined by the syntax of the defthm submitted.  Never
    submit a rewrite rule unless you have considered its interpretation as
    a proof directive.
    
    B4a.  Rewrite rules should simplify.
    Try to write rewrite rules whose right-hand sides are in some sense
    "simpler than" (or at worst, are variants of) the left-hand sides.
    This will help to avoid infinite loops in the rewriter.
    
    B4b.  Avoid needlessly expensive rules.
    Consider a rule whose conclusion's left-hand side (or, the entire
    conclusion) is a term such as (consp x) that matches many terms
    encountered by the prover.  If in addition the rule has complicated
    hypotheses, this rule could slow down the prover greatly.  Consider
    switching the conclusion and a complicated hypothesis (negating each)
    in that case.
    
    B4c. The "Knuth-Bendix problem".
    Be aware that left sides of rewrite rules should match the "normalized
    forms", where "normalization" (rewriting) is inside out.  Be sure to
    avoid the use of nonrecursive function symbols on left sides of rewrite
    rules, except when those function symbols are disabled, because they
    tend to be expanded away before the rewriter would encounter an
    instance of the left side of the rule.  Also assure that subexpressions
    on the left hand side of a rule are in simplified form.
    
    B4d. Avoid proving useless rules.
    Sometimes it's tempting to prove a rewrite rule even before you see how
    it might find application.  If the rule seems clean and important, and
    not unduly expensive, that's probably fine, especially if it's not too
    hard to prove.  But unless it's either part of the high-level strategy
    or, on the other hand, intended to get the prover past a particular
    unproved goal, it may simply waste your time to prove the rule, and
    then clutter the database of rules if you are successful.
    
    B4e. State rules as strongly as possible, usually.
    It's usually a good idea to state a rule in the strongest way possible,
    both by eliminating unnecessary hypotheses and by generalizing
    subexpressions to variables.
    
    Advanced users may choose to violate this policy on occasion, for
    example in order to avoid slowing down the prover by excessive
    attempted application of the rule.  However, it's a good rule of thumb
    to make the strongest rule possible, not only because it will then
    apply more often, but also because the rule will often be easier to
    prove (see also B6 below).  New users are sometimes tempted to put in
    extra hypotheses that have a "type restriction" appearance, without
    realizing that the way ACL2 handles (total) functions generally lets it
    handle trivial cases easily.
    
    B4f. Avoid circularity.
    A stack overflow in a proof attempt almost always results from circular
    rewriting.  Use brr to investigate the stack; see *note BREAK-LEMMA::.
    Because of the complex heuristics, it is not always easy to define just
    when a rewrite will cause circularity.  See the very good discussion of
    this topic in ACLH.
    
    See *note BREAK-LEMMA:: for a trick involving use of the forms brr t
    and (cw-gstack) for inspecting loops in the rewriter.
    
    B4g. Remember restrictions on permutative rules.
    Any rule that permutes the variables in its left hand side could cause
    circularity.  For example, the following axiom is automatically
    supplied by the system:
    
    
           (defaxiom commutativity-of-+
                     (equal (+ x y) (+ y x))).
    
    This would obviously lead to dangerous circular rewriting if such
    "permutative" rules were not governed by a further restriction.  The
    restriction is that such rules will not produce a term that is
    "lexicographically larger than" the original term (see *note
    LOOP-STOPPER::).  However, this sometimes prevents intended rewrites.
    See Chapter 13 of ACLH for a discussion of this problem.
    
    B5. Conditional vs. unconditional rewrite rules.
    It's generally preferable to form unconditional rewrite rules unless
    there is a danger of case explosion.  That is, rather than pairs of
    rules such as
    
    
         (implies p
                  (equal term1 term2))
    
    and
    
    
         (implies (not p)
                  (equal term1 term3))
    
    consider:
    
    
         (equal term1
                (if p term2 term3))
    
    However, sometimes this strategy can lead to case explosions: IF terms
    introduce cases in ACL2.  Use your judgment.  (On the subject of IF:
    COND, CASE, AND, and OR are macros that abbreviate IF forms, and
    propositional functions such as IMPLIES quickly expand into IF terms.)
    
    B6. Create elegant theorems.
    Try to formulate lemmas that are as simple and general as possible.
    For example, sometimes properties about several functions can be
    "factored" into lemmas about one function at a time.  Sometimes the
    elimination of unnecessary hypotheses makes the theorem easier to
    prove, as does generalizing first by hand.
    
    B7. Use defaxioms temporarily to explore possibilities.
    When there is a difficult goal that seems to follow immediately (by a
    :use hint or by rewriting) from some other lemmas, you can create those
    lemmas as defaxiom events (or, the application of skip-proofs to defthm
    events) and then double-check that the difficult goal really does
    follow from them.  Then you can go back and try to turn each defaxiom
    into a defthm.  When you do that, it's often useful to disable any
    additional rewrite rules that you prove in the process, so that the
    "difficult goal" will still be proved from its lemmas when the process
    is complete.
    
    Better yet, rather than disabling rewrite rules, use the local
    mechanism offered by encapsulate to make temporary rules completely
    local to the problem at hand.  See *note ENCAPSULATE:: and see *note
    LOCAL::.
    
    B9. Use books.
    Consider using previously certified books, especially for arithmetic
    reasoning.  This cuts down the duplication of effort and starts your
    specification and proof effort from a richer foundation.  See *note
    COMMUNITY-BOOKS::.
    
    _C. DEALING WITH FAILED PROOFS_
    
    C1. Look in proof output for goals that can't be further simplified.
    Use the "proof-tree" utility to explore the proof space.  However, you
    don't need to use that tool to use the "checkpoint" strategy.  The idea
    is to think of ACL2 as a "simplifier" that either proves the theorem or
    generates some goal to consider.  That goal is the first "checkpoint,"
    i.e., the first goal that does not further simplify.  Exception:  it's
    also important to look at the induction scheme in a proof by induction,
    and if induction seems appropriate, then look at the first checkpoint
    _after_ the induction has begun.
    
    Consider whether the goal on which you focus is even a theorem.
    Sometimes you can execute it for particular values to find a
    counterexample.
    
    When looking at checkpoints, remember that you are looking for any
    reason at all to believe the goal is a theorem.  So for example,
    sometimes there may be a contradiction in the hypotheses.
    
    Don't be afraid to skip the first checkpoint if it doesn't seem very
    helpful.  Also, be willing to look a few lines up or down from the
    checkpoint if you are stuck, bearing in mind however that this practice
    can be more distracting than helpful.
    
    C2. Use the "break rewrite" facility.
    Brr and related utilities let you inspect the "rewrite stack."  These
    can be valuable tools in large proof efforts.  See *note BREAK-LEMMA::
    for an introduction to these tools, and see *note BREAK-REWRITE:: for
    more complete information.
    
    The break facility is especially helpful in showing you why a
    particular rewrite rule is not being applied.
    
    C3. Use induction hints when necessary.  Of course, if you can define
    your functions so that they suggest the correct inductions to ACL2, so
    much the better!  But for complicated inductions, induction hints are
    crucial.  See *note HINTS:: for a description of :induct hints.
    
    C4. Use the "Proof Checker" to explore.
    The verify command supplied by ACL2 allows one to explore problem areas
    "by hand."  However, even if you succeed in proving a conjecture with
    verify, it is useful to prove it without using it, an activity that
    will often require the discovery of rewrite rules that will be useful
    in later proofs as well.
    
    C5. Don't have too much patience.
    Interrupt the prover fairly quickly when simplification isn't
    succeeding.
    
    C6. Simplify rewrite rules.
    When it looks difficult to relieve the hypotheses of an existing
    rewrite rule that "should" apply in a given setting, ask yourself if
    you can eliminate a hypothesis from the existing rewrite rule.  If so,
    it may be easier to prove the new version from the old version (and
    some additional lemmas), rather than to start from scratch.
    
    C7. Deal with base cases first.
    Try getting past the base case(s) first in a difficult proof by
    induction.  Usually they're easier than the inductive step(s), and
    rules developed in proving them can be useful in the inductive step(s)
    too.  Moreover, it's pretty common that mistakes in the statement of a
    theorem show up in the base case(s) of its proof by induction.
    
    C8. Use :expand hints.  Consider giving :expand hints.  These are
    especially useful when a proof by induction is failing.  It's almost
    always helpful to open up a recursively defined function that is
    supplying the induction scheme, but sometimes ACL2 is too timid to do
    so; or perhaps the function in question is disabled.
    
    _D. PERFORMANCE TIPS_
    
    D1. Disable rules.
    There are a number of instances when it is crucial to disable rules,
    including (often) those named explicitly in :use hints.  Also, disable
    recursively defined functions for which you can prove what seem to be
    all the relevant properties.  The prover can spend significant time
    "behind the scenes" trying to open up recursively defined functions,
    where the only visible effect is slowness.
    
    D2. Turn off the "break rewrite" facility.  Remember to execute :brr
    nil after you've finished with the "break rewrite" utility (see *note
    BREAK-REWRITE::), in order to bring the prover back up to full speed.
    
    _E. MISCELLANEOUS TIPS AND KNOWLEDGE_
    
    E1. Order of application of rewrite rules.
    Keep in mind that the most recent rewrite rules in the history are
    tried first.
    
    E2. Relieving hypotheses is not full-blown theorem proving.
    Relieving hypotheses on rewrite rules is done by rewriting and linear
    arithmetic alone, not by case splitting or by other prover processes
    "below" simplification.
    
    E3. "Free variables" in rewrite rules.
    The set of "free variables" of a rewrite rule is defined to contain
    those variables occurring in the rule that do not occur in the left-hand
    side of the rule.  It's often a good idea to avoid rules containing
    free variables because they are "weak," in the sense that hypotheses
    containing such variables can generally only be proved when they are
    "obviously" present in the current context.  This weakness suggests
    that it's important to put the most "interesting" (specific) hypotheses
    about free variables first, so that the right instances are considered.
    For example, suppose you put a very general hypothesis such as (consp
    x) first.  If the context has several terms around that are known to be
    consps, then x may be bound to the wrong one of them.  For much more
    information on free variables, see *note FREE-VARIABLES::.
    
    E4. Obtaining information
    Use :pl foo to inspect rewrite rules whose left hand sides are
    applications of the function foo.  Another approach to seeing which
    rewrite rules apply is to enter the proof-checker with verify, and use
    the show-rewrites or sr command.
    
    E5. Consider esoteric rules with care.
    If you care to see *note RULE-CLASSES:: and peruse the list of
    subtopics (which will be listed right there in most versions of this
    documentation), you'll see that ACL2 supports a wide variety of rules
    in addition to :rewrite rules.  Should you use them?  This is a complex
    question that we are not ready to answer with any generality.  Our
    general advice is to avoid relying on such rules as long as you doubt
    their utility.  More specifically:  be careful not to use conditional
    type prescription rules, as these have been known to bring ACL2 to its
    knees, unless you are conscious that you are doing so and have reason
    to believe that they are working well.
    
    _F. SOME THINGS YOU DON'T NEED TO KNOW_
    
    Most generally:  you shouldn't usually need to be able to predict too
    much about ACL2's behavior.  You should mainly just need to be able to
    react to it.
    
    F1. Induction heuristics.
    Although it is often important to read the part of the prover's output
    that gives the induction scheme chosen by the prover, it is not
    necessary to understand how the prover made that choice.  (Granted,
    advanced users may occasionally gain minor insight from such knowledge.
    But it's truly minor in many cases.)  What _is_ important is to be able
    to tell it an appropriate induction when it doesn't pick the right one
    (after noticing that it doesn't).  See C3 above.
    
    F2. Heuristics for expanding calls of recursively defined functions.
    As with the previous topic, the important thing isn't to understand
    these heuristics but, rather, to deal with cases where they don't seem
    to be working.  That amounts to supplying :expand hints for those calls
    that you want opened up, which aren't.  See also C8 above.
    
    F3. The "waterfall".
    As discussed many times already, a good strategy for using ACL2 is to
    look for checkpoints (goals stable under simplification) when a proof
    fails, perhaps using the proof-tree facility.  Thus, it is reasonable
    to ignore almost all the prover output, and to avoid pondering the
    meaning of the other "processes" that ACL2 uses besides simplification
    (such as elimination, cross-fertilization, generalization, and
    elimination of irrelevance).  For example, you don't need to worry
    about prover output that mentions "type reasoning" or "abbreviations,"
    for example.
    
    
    File: acl2-doc-emacs.info,  Node: BDD,  Next: BOOKS,  Prev: ACL2-TUTORIAL,  Up: Top
    
    BDD    ordered binary decision diagrams with rewriting
    
    Ordered binary decision diagrams (OBDDs, often simply called BDDs) are
    a technique, originally published by Randy Bryant, for the efficient
    simplification of Boolean expressions.  In ACL2 we combine this
    technique with rewriting to handle arbitrary ACL2 terms that can
    represent not only Boolean values, but non-Boolean values as well.  In
    particular, we provide a setting for deciding equality of bit vectors
    (lists of Boolean values).
    
    * Menu:
    
    * BDD-ALGORITHM:: summary of the BDD algorithm in ACL2
    
    * BDD-INTRODUCTION:: examples illustrating the use of BDDs in ACL2
    
    * IF*:: for conditional rewriting with BDDs
    
    * SHOW-BDD:: inspect failed BDD proof attempts
    
    An introduction to BDDs for the automated reasoning community may be
    found in "Introduction to the OBDD Algorithm for the ATP Community" by
    J Moore, Journal of Automated Reasoning (1994), pp. 33-45.  (This paper
    also appears as Technical Report #84 from Computational Logic, Inc.)
    
    Further information about BDDs in ACL2 can be found in the subtopics of
    this documentation section.  In particular, see *note
    BDD-INTRODUCTION:: for a good starting place that provides a number of
    examples.
    
    See *note HINTS:: for a description of :bdd hints.  For quick
    reference, here is an example; but only the :vars part of the hint is
    required, as explained in the documentation for hints.  The values
    shown are the defaults.
    
         (:vars nil :bdd-constructors (cons) :prove t :literal :all)
    
    We suggest that you next visit the documentation topic BDD-INTRODUCTION.
    
    
    File: acl2-doc-emacs.info,  Node: BDD-ALGORITHM,  Next: BDD-INTRODUCTION,  Prev: BDD,  Up: BDD
    
    BDD-ALGORITHM    summary of the BDD algorithm in ACL2
    
    The BDD algorithm in ACL2 uses a combination of manipulation of IF
    terms and unconditional rewriting.  In this discussion we begin with
    some relevant mathematical theory.  This is followed by a description
    of how ACL2 does BDDs, including concluding discussions of soundness,
    completeness, and efficiency.
    
    We recommend that you read the other documentation about BDDs in ACL2
    before reading the rather technical material that follows.  See *note
    BDD::.
    
    Here is an outline of our presentation.  Readers who want a user
    perspective, without undue mathematical theory, may wish to skip to
    Part (B), referring to Part (A) only on occasion if necessary.
    
    (A) *Mathematical Considerations*
    
         (A1) BDD term order
    
         (A2) BDD-constructors and BDD terms, and their connection with
         aborting the BDD algorithm
    
         (A3) Canonical BDD terms
    
         (A4) A theorem stating the equivalence of provable and syntactic
         equality for canonical BDD terms
    
    
    (B) *Algorithmic Considerations*
    
         (B1) BDD rules (rules used by the rewriting portion of the ACL2 BDD
         algorithm)
    
         (B2) Terms "known to be Boolean"
    
         (B3) An "IF-lifting" operation used by the algorithm, as well as an
         iterative version of that operation
    
         (B4) The ACL2 BDD algorithm
    
         (B5) Soundness and Completeness of the ACL2 BDD algorithm
    
         (B6) Efficiency considerations
    
    
    (A) *Mathematical Considerations*
    
    (A1) _BDD term order_
    
    Our BDD algorithm creates a total "BDD term order" on ACL2 terms, on
    the fly.  We use this order in our discussions below of IF-lifting and
    of canonical BDD terms, and in the algorithm's use of commutativity.
    The particular order is unimportant, except that we guarantee (for
    purposes of commutative functions) that constants are smaller in this
    order than non-constants.
    
    (A2) _BDD-constructors_ (assumed to be '(cons)) and _BDD terms_
    
    We take as given a list of function symbols that we call the
    "BDD-constructors."  By default, the only BDD-constructor is cons,
    although it is legal to specify any list of function symbols as the
    BDD-constructors, either by using the acl2-defaults-table (see *note
    ACL2-DEFAULTS-TABLE::) or by supplying a :BDD-CONSTRUCTORS hint (see
    *note HINTS::).  Warning: this capability is largely untested and may
    produce undesirable results.  Henceforth, except when explicitly stated
    to the contrary, we assume that BDD-constructors is '(cons).
    
    Roughly speaking, a BDD term is the sort of term produced by our BDD
    algorithm, namely a tree with all cons nodes lying above all non-CONS
    nodes.  More formally, a term is said to be a BDD term if it contains
    *no* subterm of either of the following forms, where f is not CONS.
    
         (f ... (CONS ...) ...)
    
         (f ... 'x ...)  ; where (consp x) = t
    
    We will see that whenever the BDD algorithm attempts to create a term
    that is not a BDD term, it aborts instead.  Thus, whenever the
    algorithm completes without aborting, it creates a BDD term.
    
    (A3) _Canonical BDD terms_
    
    We can strengthen the notion of "BDD term" to a notion of "canonical
    BDD term" by imposing the following additional requirements, for every
    subterm of the form (IF x y z):
    
         (a) x is a variable, and it precedes (in the BDD term order) every
         variable occurring in y or z;
    
         (b) y and z are syntactically distinct; and,
    
         (c) it is not the case that y is t and z is nil.
    
    
    We claim that it follows easily from our description of the BDD
    algorithm that every term it creates is a canonical BDD term, assuming
    that the variables occurring in all such terms are treated by the
    algorithm as being Boolean (see (B2) below) and that the terms contain
    no function symbols other than IF and CONS.  Thus, under those
    assumptions the following theorem shows that the BDD algorithm never
    creates distinct terms that are provably equal, a property that is
    useful for completeness and efficiency (as we explain in (B5) and (B6)
    below).
    
    (A4) _Provably equal canonical BDD terms are identical_
    
    We believe that the following theorem and proof are routine extensions
    of a standard result and proof to terms that allow calls of CONS.
    
    *Theorem*.  Suppose that t1 and t2 are canonical BDD terms that contain
    no function symbols other than IF and CONS.  Also suppose that (EQUAL
    t1 t2) is a theorem.  Then t1 and t2 are syntactically identical.
    
    Proof of theorem:  By induction on the total number of symbols
    occurring in these two terms.  First suppose that at least one term is
    a variable; without loss of generality let it be t1.  We must prove
    that t2 is syntactically the same as t1.  Now it is clearly consistent
    that (EQUAL t1 t2) is false if t2 is a call of CONS (to see this,
    simply let t1 be an value that is not a CONSP).  Similarly, t2 cannot
    be a constant or a variable other than t1.  The remaining possibility
    to rule out is that t2 is of the form (IF t3 t4 t5), since by
    assumption its function symbol must be IF or CONS and we have already
    handled the latter case.  Since t2 is canonical, we know that t3 is a
    variable.  Since (EQUAL t1 t2) is provable, i.e.,
    
         (EQUAL t1 (if t3 t4 t5))
    
    is provable, it follows that we may substitute either t or nil for t3
    into this equality to obtain two new provable equalities.  First,
    suppose that t1 and t3 are distinct variables.  Then these
    substitutions show that t1 is provably equal to both t4 and t5 (since
    t3 does not occur in t4 or t5 by property (a) above, as t2 is
    canonical), and hence t4 and t5 are provably equal to each other, which
    implies by the inductive hypothesis that they are the same term -- and
    this contradicts the assumption that t2 is canonical (property (b)).
    Therefore t1 and t3 are the same variable, i.e., the equality displayed
    above is actually (EQUAL t1 (if t1 t4 t5)).  Substituting t and then
    nil for t1 into this provable equality lets us prove (EQUAL t t4) and
    (EQUAL nil t5), which by the inductive hypothesis implies that t4 is
    (syntactically) the term t and t5 is nil.  That is, t2 is (IF t1 t
    nil), which contradicts the assumption that t2 is canonical (property
    (c)).
    
    Next, suppose that at least one term is a call of IF.  Our first
    observation is that the other term is also a call of IF.  For if the
    other is a call of CONS, then they cannot be provably equal, because
    the former has no function symbols other than IF and hence is Boolean
    when all its variables are assigned Boolean values.  Also, if the other
    is a constant, then both branches of the IF term are provably equal to
    that constant and hence these branches are syntactically identical by
    the inductive hypothesis, contradicting property (b).  Hence, we may
    assume for this case that both terms are calls of IF; let us write them
    as follows.
    
         t0:  (IF t1 t2 t3)
         u0:  (IF u1 u2 u3)
    
    Note that t1 and u1 are variables, by property (a) of canonical BDD
    terms.  First we claim that t1 does not strictly precede u1 in the BDD
    term order.  For suppose t1 does strictly precede u1.  Then property
    (a) of canonical BDD terms guarantees that t1 does not occur in u0.
    Hence, an argument much like one used above shows that u0 is provably
    equal to both t2 (substituting t for t1) and t3 (substituting nil for
    t1), and hence t2 and t3 are provably equal.  That implies that they
    are identical terms, by the inductive hypothesis, which then
    contradicts property (b) for t0.  Similarly, u1 does not strictly
    precede t1 in the BDD term order.  Therefore, t1 and u1 are the same
    variable.  By substituting t for this variable we see that t2 and u2
    are provably equal, and hence they are equal by the inductive
    hypothesis.  Similarly, by substituting nil for t1 (and u1) we see that
    t3 and u3 are provably, hence syntactically, equal.
    
    We have covered all cases in which at least one term is a variable or
    at least one term is a call of IF.  If both terms are constants, then
    provable and syntactic equality are clearly equivalent.  Finally, then,
    we may assume that one term is a call of CONS and the other is a
    constant or a call of CONS.  The constant case is similar to the CONS
    case if the constant is a CONSP, so we omit it; while if the constant
    is not a CONSP then it is not provably equal to a call of CONS; in fact
    it is provably _not_ equal!
    
    So, we are left with a final case, in which canonical BDD terms (CONS
    t1 t2) and (CONS u1 u2) are provably equal, and we want to show that t1
    and u1 are syntactically equal as are t2 and u2.  These conclusions are
    easy consequences of the inductive hypothesis, since the ACL2 axiom
    CONS-EQUAL (which you can inspect using :PE) shows that equality of the
    given terms is equivalent to the conjunction of (EQUAL t1 t2) and
    (EQUAL u1 u2).  Q.E.D.
    
    (B) *Algorithmic Considerations*
    
    (B1) _BDD rules_
    
    A rule of class :rewrite (see *note RULE-CLASSES::) is said to be a
    "BDD rewrite rule" if and only if it satisfies the following criteria.
    (1) The rule is enabled.  (2) Its equivalence relation is equal.  (3)
    It has no hypotheses.  (4) Its :loop-stopper field is nil, i.e., it is
    not a permutative rule.  (5) All variables occurring in the rule occur
    in its left-hand side (i.e., there are no "free variables"; see *note
    REWRITE::).  A rule of class :definition (see *note RULE-CLASSES::) is
    said to be a "BDD definition rule" if it satisfies all the criteria
    above (except (4), which does not apply), and moreover the top function
    symbol of the left-hand side was not recursively (or mutually
    recursively) defined.  Technical point:  Note that this additional
    criterion is independent of whether or not the indicated function
    symbol actually occurs in the right-hand side of the rule.
    
    Both BDD rewrite rules and BDD definition rules are said to be "BDD
    rules."
    
    (B2) _Terms "known to be Boolean"_
    
    We apply the BDD algorithm in the context of a top-level goal to prove,
    namely, the goal at which the :BDD hint is attached.  As we run the BDD
    algorithm, we allow ourselves to say that a set of terms is "known to
    be Boolean" if we can verify that the goal is provable from the
    assumption that at least one of the terms is not Boolean.
    Equivalently, we allow ourselves to say that a set of terms is "known
    to be Boolean" if we can verify that the original goal is provably
    equivalent to the assertion that if all terms in the set are Boolean,
    then the goal holds.  The notion "known to be Boolean" is conservative
    in the sense that there are generally sets of terms for which the above
    equivalent criteria hold and yet the sets of terms are not noted as as
    being "known to be Boolean."  However, ACL2 uses a number of tricks,
    including type-set reasoning and analysis of the structure of the
    top-level goal, to attempt to establish that a sufficiently inclusive
    set of terms is known to be Boolean.
    
    From a practical standpoint, the algorithm determines a set of terms
    known to be Boolean; we allow ourselves to say that each term in this
    set is "known to be Boolean."  The algorithm assumes that these terms
    are indeed Boolean, and can make use of that assumption.  For example,
    if t1 is known to be Boolean then the algorithm simplifies (IF t1 t
    nil) to t1; see (iv) in the discussion immediately below.
    
    (B3) _IF-lifting_ and the _IF-lifting-for-IF loop_
    
    Suppose that one has a term of the form (f ... (IF test x y) ...),
    where f is a function symbol other than CONS.  Then we say that
    "IF-lifting" test "from" this term produces the following term, which
    is provably equal to the given term.
    
         (if test
             (f ... x ...)  ; resulting true branch
             (f ... y ...)) ; resulting false branch
    
    Here, we replace each argument of f of the form (IF test .. ..), for
    the same test, in the same way.  In this case we say that "IF-lifting
    applies to" the given term, "yielding the test" test and with the
    "resulting two branches" displayed above.  Whenever we apply
    IF-lifting, we do so for the available test that is least in the BDD
    term order (see (A1) above).
    
    We consider arguments v of f that are "known to be Boolean" (see above)
    to be replaced by (IF v t nil) for the purposes of IF-lifting, i.e.,
    before IF-lifting is applied.
    
    There is one special case, however, for IF-lifting.  Suppose that the
    given term is of the form (IF v y z) where v is a variable and is the
    test to be lifted out (i.e., it is least in the BDD term order among
    the potential tests).  Moroever, suppose that neither y nor z is of the
    form (IF v W1 W2) for that same v.  Then IF-lifting does not apply to
    the given term.
    
    We may now describe the IF-lifting-for-IF loop, which applies to terms
    of the form (IF test tbr fbr) where the algorithm has already produced
    test, tbr, and fbr.  First, if test is nil then we return fbr, while if
    test is a non-nil constant or a call of CONS then we return tbr.
    Otherwise, we see if IF-lifting applies.  If IF-lifting does not apply,
    then we return (IF test tbr fbr).  Otherwise, we apply IF-lifting to
    obtain a term of the form (IF x y z), by lifting out the appropriate
    test.  Now we recursively apply the IF-lifting-for-IF loop to the term
    (IF x y z), unless any of the following special cases apply.
    
         (i) If y and z are the same term, then return y.
    
         (ii) Otherwise, if x and z are the same term, then replace z by
         nil before recursively applying IF-lifting-for-IF.
    
         (iii) Otherwise, if x and y are the same term and y is known to be
         Boolean, then replace y by t before recursively applying
         IF-lifting-for-IF.
    
         (iv) If z is nil and either x and y are the same term or x is
         "known to be Boolean" and y is t, then return x.
    
    
    NOTE:  When a variable x is known to be Boolean, it is easy to see that
    the form (IF x t nil) is always reduced to x by this algorithm.
    
    (B4) _The ACL2 BDD algorithm_
    
    We are now ready to present the BDD algorithm for ACL2.  It is given an
    ACL2 term, x, as well as an association list va that maps variables to
    terms, including all variables occurring in x.  We maintain the
    invariant that whenever a variable is mapped by va to a term, that term
    has already been constructed by the algorithm, except:  initially va
    maps every variable occurring in the top-level term to itself.  The
    algorithm proceeds as follows.  We implicitly ordain that whenever the
    BDD algorithm attempts to create a term that is not a BDD term (as
    defined above in (A2)), it aborts instead.  Thus, whenever the
    algorithm completes without aborting, it creates a BDD term.
    
         If x is a variable, return the result of looking it up in va.
    
         If x is a constant, return x.
    
         If x is of the form (IF test tbr fbr), then first run the
         algorithm on test with the given va to obtain test'.  If test' is
         nil, then return the result fbr' of running the algorithm on fbr
         with the given va.  If test' is a constant other than nil, or is a
         call of CONS, then return the result tbr' of running the algorithm
         on tbr with the given va.  If tbr is identical to fbr, return tbr.
         Otherwise, return the result of applying the IF-lifting-for-IF
         loop (described above) to the term (IF test' tbr' fbr').
    
         If x is of the form (IF* test tbr fbr), then compute the result
         exactly as though IF were used rather than IF*, except that if
         test' is not a constant or a call of CONS (see paragraph above),
         then abort the BDD computation.  Informally, the tests of IF*
         terms are expected to "resolve."  NOTE:  This description shows
         how IF* can be used to implement conditional rewriting in the BDD
         algorithm.
    
         If x is a LAMBDA expression ((LAMBDA vars body) . args) (which
         often corresponds to a LET term; see *note LET::), then first form
         an alist va' by binding each v in vars to the result of running
         the algorithm on the corresponding member of args, with the
         current alist va.  Then, return the result of the algorithm on
         body in the alist va'.
    
         Otherwise, x is of the form (f x1 x2 ... xn), where f is a
         function symbol other than IF or IF*.  In that case, let xi' be
         the result of running the algorithm on xi, for i from 1 to n,
         using the given alist va.  First there are a few special cases.
         If f is EQUAL then we return t if x1' is syntactically identical
         to x2' (where this test is very fast; see (B6) below); we return
         x1' if it is known to be Boolean and x2' is t; and similarly, we
         return x2' if it is known to be Boolean and x1' is t.  Next, if
         each xi' is a constant and the :executable-counterpart of f is
         enabled, then the result is obtained by computation.  Next, if f
         is BOOLEANP and x1' is known to be Boolean, t is returned.
         Otherwise, we proceed as follows, first possibly swapping the
         arguments if they are out of (the BDD term) order and if f is
         known to be commutative (see below).  If a BDD rewrite rule (as
         defined above) matches the term (f x1'... xn'), then the most
         recently stored such rule is applied.  If there is no such match
         and f is a BDD-constructor, then we return (f x1'... xn').
         Otherwise, if a BDD definition rule matches this term, then the
         most recently stored such rule (which will usually be the original
         definition for most users) is applied.  If none of the above
         applies and neither does IF-lifting, then we return (f x1'...
         xn').  Otherwise we apply IF-lifting to (f x1'... xn') to obtain a
         term (IF test tbr fbr); but we aren't done yet.  Rather, we run
         the BDD algorithm (using the same alist) on tbr and fbr to obtain
         terms tbr' and fbr', and we return (IF test tbr' fbr') unless tbr'
         is syntactically identical to fbr', in which case we return tbr'.
    
    
    When is it the case that, as said above, "f is known to be
    commutative"?  This happens when an enabled rewrite rule is of the form
    (EQUAL (f X Y) (f Y X)).  Regarding swapping the arguments in that
    case:  recall that we may assume very little about the BDD term order,
    essentially only that we swap the two arguments when the second is a
    constant and the first is not, for example, in (+ x 1).  Other than
    that situation, one cannot expect to predict accurately when the
    arguments of commutative operators will be swapped.
    
    (B5) Soundness and Completeness of the ACL2 BDD algorithm
    
    Roughly speaking, "soundness" means that the BDD algorithm should give
    correct answers, and "completeness" means that it should be powerful
    enough to prove all true facts.  Let us make the soundness claim a
    little more precise, and then we'll address completeness under suitable
    hypotheses.
    
    *Claim* (Soundness).  If the ACL2 BDD algorithm runs to completion on
    an input term t0, then it produces a result that is provably equal to
    t0.
    
    We leave the proof of this claim to the reader.  The basic idea is
    simply to check that each step of the algorithm preserves the meaning
    of the term under the bindings in the given alist.
    
    Let us start our discussion of completeness by recalling the theorem
    proved above in (A4).
    
    *Theorem*.  Suppose that t1 and t2 are canonical BDD terms that contain
    no function symbols other than IF and CONS.  Also suppose that (EQUAL
    t1 t2) is a theorem.  Then t1 and t2 are syntactically identical.
    
    Below we show how this theorem implies the following completeness
    property of the ACL2 BDD algorithm.  We continue to assume that CONS is
    the only BDD-constructor.
    
    *Claim* (Completeness).  Suppose that t1 and t2 are provably equal
    terms, under the assumption that all their variables are known to be
    Boolean.  Assume further that under this same assumption, top-level
    runs of the ACL2 BDD algorithm on these terms return terms that contain
    only the function symbols IF and CONS.  Then the algorithm returns the
    same term for both t1 and t2, and the algorithm reduces (EQUAL t1 t2)
    to t.
    
    Why is this claim true?  First, notice that the second part of the
    conclusion follows immediately from the first, by definition of the
    algorithm.  Next, notice that the terms u1 and u2 obtained by running
    the algorithm on t1 and t2, respectively, are provably equal to t1 and
    t2, respectively, by the Soundness Claim.  It follows that u1 and u2
    are provably equal to each other.  Since these terms contain no
    function symbols other than IF or CONS, by hypothesis, the Claim now
    follows from the Theorem above together with the following lemma.
    
    *Lemma*.  Suppose that the result of running the ACL2 BDD algorithm on
    a top-level term t0 is a term u0 that contains only the function
    symbols IF and CONS, where all variables of t0 are known to be Boolean.
    Then u0 is a canonical BDD term.
    
    Proof:  left to the reader.  Simply follow the definition of the
    algorithm, with a separate argument for the IF-lifting-for-IF loop.
    
    Finally, let us remark on the assumptions of the Completeness Claim
    above.  The assumption that all variables are known to be Boolean is
    often true; in fact, the system uses the forward-chaining rule
    boolean-listp-forward (you can see it using :pe) to try to establish
    this assumption, if your theorem has a form such as the following.
    
         (let ((x (list x0 x1 ...))
               (y (list y0 y1 ...)))
           (implies (and (boolean-listp x)
                         (boolean-listp y))
                    ...))
    
    Moreover, the :BDD hint can be used to force the prover to abort if it
    cannot check that the indicated variables are known to be Boolean; see
    *note HINTS::.
    
    Finally, consider the effect in practice of the assumption that the
    terms resulting from application of the algorithm contain calls of IF
    and CONS only.  Typical use of BDDs in ACL2 takes place in a theory
    (see *note THEORIES::) in which all relevant non-recursive function
    symbols are enabled and all recursive function symbols possess enabled
    BDD rewrite rules that tell them how open up.  For example, such a rule
    may say how to expand on a given function call's argument that has the
    form (CONS a x), while another may say how to expand when that argument
    is nil).  (See for example the rules append-cons and append-nil in the
    documentation for IF*.)  We leave it to future work to formulate a
    theorem that guarantees that the BDD algorithm produces terms
    containing calls only of IF and CONS assuming a suitably "complete"
    collection of rewrite rules.
    
    (B6) _Efficiency considerations_
    
    Following Bryant's algorithm, we use a graph representation of terms
    created by the BDD algorithm's computation.  This representation enjoys
    some important properties.
    
         (Time efficiency) The test for syntactic equality of BDD terms is
         very fast.
    
         (Space efficiency) Equal BDD data structures are stored identically
         in memory.
    
    
    _Implementation note._  The representation actually uses a sort of hash
    table for BDD terms that is implemented as an ACL2 1-dimensional array.
    See *note ARRAYS::.  In addition, we use a second such hash table to
    avoid recomputing the result of applying a function symbol to the
    result of running the algorithm on its arguments.  We believe that
    these uses of hash tables are standard.  They are also discussed in
    Moore's paper on BDDs; see *note BDD:: for the reference.
    
    
    File: acl2-doc-emacs.info,  Node: BDD-INTRODUCTION,  Next: IF*,  Prev: BDD-ALGORITHM,  Up: BDD
    
    BDD-INTRODUCTION    examples illustrating the use of BDDs in ACL2
    
    See *note BDD:: for a brief introduction to BDDs in ACL2 and for
    pointers to other documentation on BDDs in ACL2.  Here, we illustrate
    the use of BDDs in ACL2 by way of some examples.  For a further
    example, see *note IF*::.
    
    Let us begin with a really simple example.  (We will explain the :bdd
    hint (:vars nil) below.)
    
    
         ACL2 !>(thm (equal (if a b c) (if (not a) c b))
                     :hints (("Goal" :bdd (:vars nil)))) ; Prove with BDDs
    
         [Note:  A hint was supplied for our processing of the goal above.
         Thanks!]
    
         But simplification with BDDs (7 nodes) reduces this to T, using the
         :definitions EQUAL and NOT.
    
         Q.E.D.
    
         Summary
         Form:  ( THM ...)
         Rules: ((:DEFINITION EQUAL) (:DEFINITION NOT))
         Warnings:  None
         Time:  0.18 seconds (prove: 0.05, print: 0.02, other: 0.12)
    
         Proof succeeded.
         ACL2 !>
    
    The :bdd hint (:vars nil) indicates that BDDs are to be used on the
    indicated goal, and that any so-called "variable ordering" may be used:
    ACL2 may use a convenient order that is far from optimal.  It is beyond
    the scope of the present documentation to address the issue of how the
    user may choose good variable orderings.  Someday our implementation of
    BDDs may be improved to include heuristically-chosen variable orderings
    rather than rather random ones.
    
    Here is a more interesting example.
    
         (defun v-not (x)
         ; Complement every element of a list of Booleans.
           (if (consp x)
               (cons (not (car x)) (v-not (cdr x)))
             nil))
    
         ; Now we prove a rewrite rule that explains how to open up v-not on
         ; a consp.
         (defthm v-not-cons
           (equal (v-not (cons x y))
                  (cons (not x) (v-not y))))
    
         ; Finally, we prove for 7-bit lists that v-not is self-inverting.
         (thm
          (let ((x (list x0 x1 x2 x3 x4 x5 x6)))
            (implies (boolean-listp x)
                     (equal (v-not (v-not x)) x)))
          :hints (("Goal" :bdd
                          ;; Note that this time we specify a variable order.
                          (:vars (x0 x1 x2 x3 x4 x5 x6)))))
    
    It turns out that the variable order doesn't seem to matter in this
    example; using several orders we found that 30 nodes were created, and
    the proof time was about 1/10 of a second on a (somewhat enhanced)
    Sparc 2.  The same proof took about a minute and a half without any
    :bdd hint!  This observation is a bit misleading perhaps, since the
    theorem for arbitrary x,
    
         (thm
          (implies (boolean-listp x)
                   (equal (v-not (v-not x)) x)))
    
    only takes about 1.5 times as long as the :bdd proof for 7 bits, above!
    Nevertheless, BDDs can be very useful in reducing proof time,
    especially when there is no regular structure to facilitate proof by
    induction, or when the induction scheme is so complicated to construct
    that significant user effort is required to get the proof by induction
    to go through.
    
    Finally, consider the preceding example, with a :bdd hint of (say)
    (:vars nil), but with the rewrite rule v-not-cons above disabled.  In
    that case, the proof fails, as we see below.  That is because the BDD
    algorithm in ACL2 uses hypothesis-free :rewrite rules,
    :executable-counterparts, and nonrecursive definitions, but it does not
    use recursive definitions.
    
    Notice that when we issue the (show-bdd) command, the system's response
    clearly shows that we need a rewrite rule for simplifying terms of the
    form (v-not (cons ...)).
    
         ACL2 !>(thm
                 (let ((x (list x0 x1 x2 x3 x4 x5 x6)))
                   (implies (boolean-listp x)
                            (equal (v-not (v-not x)) x)))
                 :hints (("Goal" :bdd (:vars nil)
                          :in-theory (disable v-not-cons))))
    
         [Note:  A hint was supplied for our processing of the goal above.
         Thanks!]
    
    
         ACL2 Error in ( THM ...):  Attempted to create V-NOT node during BDD
         processing with an argument that is a call of a bdd-constructor,
         which would produce a non-BDD term (as defined in :DOC
         bdd-algorithm).  See :DOC show-bdd.
    
    
         Summary
         Form:  ( THM ...)
         Rules: NIL
         Warnings:  None
         Time:  0.58 seconds (prove: 0.13, print: 0.00, other: 0.45)
    
         ******** FAILED ********  See :DOC failure  ******** FAILED ********
         ACL2 !>(show-bdd)
    
         BDD computation on Goal yielded 17 nodes.
         ==============================
    
         BDD computation was aborted on Goal, and hence there is no
         falsifying assignment that can be constructed.  Here is a backtrace
         of calls, starting with the top-level call and ending with the one
         that led to the abort.  See :DOC show-bdd.
    
         (LET ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
              (IMPLIES (BOOLEAN-LISTP X)
                       (EQUAL (V-NOT (V-NOT X)) X)))
           alist: ((X6 X6) (X5 X5) (X4 X4) (X3 X3) (X2 X2) (X1 X1) (X0 X0))
    
         (EQUAL (V-NOT (V-NOT X)) X)
           alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
    
         (V-NOT (V-NOT X))
           alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
    
         (V-NOT X)
           alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
         ACL2 !>
    
    The term that has caused the BDD algorithm to abort is thus (V-NOT X),
    where X has the value (LIST X0 X1 X2 X3 X4 X5 ...), i.e., (CONS X0
    (LIST X1 X2 X3 X4 X5 ...)).  Thus, we see the utility of introducing a
    rewrite rule to simplify terms of the form (V-NOT (CONS ...)).  The
    moral of this story is that if you get an error of the sort shown
    above, you may find it useful to execute the command (show-bdd) and use
    the result as advice that suggests the left hand side of a rewrite rule.
    
    Here is another sort of failed proof.  In this version we have omitted
    the hypothesis that the input is a bit vector.  Below we use show-bdd
    to see what went wrong, and use the resulting information to construct
    a counterexample.  This failed proof corresponds to a slightly modified
    input theorem, in which x is bound to the 4-element list (list x0 x1 x2
    x3).
    
         ACL2 !>(thm
                 (let ((x (list x0 x1 x2 x3)))
                   (equal (v-not (v-not x)) x))
                 :hints (("Goal" :bdd
                          ;; This time we do not specify a variable order.
                          (:vars nil))))
    
         [Note:  A hint was supplied for our processing of the goal above.
         Thanks!]
    
    
         ACL2 Error in ( THM ...):  The :BDD hint for the current goal has
         successfully simplified this goal, but has failed to prove it.
         Consider using (SHOW-BDD) to suggest a counterexample; see :DOC
         show-bdd.
    
    
         Summary
         Form:  ( THM ...)
         Rules: NIL
         Warnings:  None
         Time:  0.18 seconds (prove: 0.07, print: 0.00, other: 0.12)
    
         ******** FAILED ********  See :DOC failure  ******** FAILED ********
         ACL2 !>(show-bdd)
    
         BDD computation on Goal yielded 73 nodes.
         ==============================
    
         Falsifying constraints:
         ((X0 "Some non-nil value")
          (X1 "Some non-nil value")
          (X2 "Some non-nil value")
          (X3 "Some non-nil value")
          ((EQUAL 'T X0) T)
          ((EQUAL 'T X1) T)
          ((EQUAL 'T X2) T)
          ((EQUAL 'T X3) NIL))
    
         ==============================
    
         Term obtained from BDD computation on Goal:
    
         (IF X0
             (IF X1
                 (IF X2 (IF X3 (IF # # #) (IF X3 # #))
                     (IF X2 'NIL (IF X3 # #)))
                 (IF X1 'NIL
                     (IF X2 (IF X3 # #) (IF X2 # #))))
             (IF X0 'NIL
                 (IF X1 (IF X2 (IF X3 # #) (IF X2 # #))
                     (IF X1 'NIL (IF X2 # #)))))
    
         ACL2 Query (:SHOW-BDD):  Print the term in full?  (N, Y, W or ?):
         n ; I've seen enough.  The assignment shown above suggests
           ; (though not conclusively) that if we bind x3 to a non-nil
           ; value other than T, and bind x0, x1, and x2 to t, then we
           ; this may give us a counterexample.
         ACL2 !>(let ((x0 t) (x1 t) (x2 t) (x3 7))
                  (let ((x (list x0 x1 x2 x3)))
                    ;; Let's use LIST instead of EQUAL to see how the two
                    ;; lists differ.
                    (list (v-not (v-not x)) x)))
         ((T T T T) (T T T 7))
         ACL2 !>
    
    See *note IF*:: for another example.
    
    
    File: acl2-doc-emacs.info,  Node: IF*,  Next: SHOW-BDD,  Prev: BDD-INTRODUCTION,  Up: BDD
    
    IF*    for conditional rewriting with BDDs
    
    The function IF* is defined to be IF, but it is used in a special way
    by ACL2's BDD package.
    
    As explained elsewhere (see *note BDD-ALGORITHM::), ACL2's BDD
    algorithm gives special treatment to terms of the form (IF* TEST TBR
    FBR).  In such cases, the algorithm simplifies TEST first, and the
    result of that simplification must be a constant (normally t or nil,
    but any non-nil explicit value is treated like t here).  Otherwise, the
    algorithm aborts.
    
    Thus, IF* may be used to implement a sort of conditional rewriting for
    ACL2's BDD package, even though this package only nominally supports
    unconditional rewriting.  The following contrived example should make
    this point clear.
    
    Suppose that we want to prove that (nthcdr (length x) (append x y)) is
    equal to y, but that we would be happy to prove this only for lists
    having length 4.  We can state such a theorem as follows.
    
         (let ((x (list x0 x1 x2 x3)))
           (equal (nthcdr (length x) (append x y))
                  y))
    
    If we want to prove this formula with a :BDD hint, then we need to have
    appropriate rewrite rules around.  First, note that LENGTH is defined
    as follows (try :PE LENGTH):
    
         (length x)
          =
         (if (stringp x)
             (len (coerce x 'list))
             (len x))
    
    Since BDD-based rewriting is merely very simple unconditional rewriting
    (see *note BDD-ALGORITHM::), we expect to have to prove a rule reducing
    STRINGP of a CONS:
    
         (defthm stringp-cons
           (equal (stringp (cons x y))
                  nil))
    
    Now we need a rule to compute the LEN of X, because the definition of
    LEN is recursive and hence not used by the BDD package.
    
         (defthm len-cons
           (equal (len (cons a x))
                  (1+ (len x))))
    
    We imagine this rule simplifying (LEN (LIST X0 X1 X2 X3)) in terms of
    (LEN (LIST X1 X2 X3)), and so on, and then finally (LEN nil) should be
    computed by execution (see *note BDD-ALGORITHM::).
    
    We also need to imagine simplifying (APPEND X Y), where still X is
    bound to (LIST X0 X1 X2 X3).  The following two rules suffice for this
    purpose (but are needed, since APPEND, actually BINARY-APPEND, is
    recursive).
    
         (defthm append-cons
           (equal (append (cons a x) y)
                  (cons a (append x y))))
    
         (defthm append-nil
           (equal (append nil x)
                  x))
    
    Finally, we imagine needing to simplify calls of NTHCDR, where the
    first argument is a number (initially, the length of (LIST X0 X1 X2
    X3), which is 4).  The second lemma below is the traditional way to
    accomplish that goal (when not using BDDs), by proving a conditional
    rewrite rule.  (The first lemma is only proved in order to assist in
    the proof of the second lemma.)
    
         (defthm fold-constants-in-+
           (implies (and (syntaxp (quotep x))
                         (syntaxp (quotep y)))
                    (equal (+ x y z)
                           (+ (+ x y) z))))
    
         (defthm nthcdr-add1-conditional
           (implies (not (zp (1+ n)))
                    (equal (nthcdr (1+ n) x)
                           (nthcdr n (cdr x)))))
    
    The problem with this rule is that its hypothesis makes it a
    conditional rewrite rule, and conditional rewrite rules are not used by
    the BDD package.  (See *note BDD-ALGORITHM:: for a discussion of "BDD
    rules.")  (Note that the hypothesis cannot simply be removed; the
    resulting formula would be false for n = -1 and x = '(a), for example.)
    We can solve this problem by using IF*, as follows; comments follow.
    
         (defthm nthcdr-add1
           (equal (nthcdr (+ 1 n) x)
                  (if* (zp (1+ n))
                       x
                       (nthcdr n (cdr x)))))
    
    How is nthcdr-add1 applied by the BDD package?  Suppose that the BDD
    computation encounters a term of the form (NTHCDR (+ 1 N) X).  Then the
    BDD package will apply the rewrite rule nthcdr-add1.  The first thing
    it will do when attempting to simplify the right hand side of that rule
    is to attempt to simplify the term (ZP (1+ N)).  If N is an explicit
    number (which is the case in the scenario we envision), this test will
    reduce (assuming the executable counterparts of ZP and BINARY-+ are
    enabled) to t or to nil.  In fact, the lemmas above (not including the
    lemma nthcdr-add1-conditional) suffice to prove our goal:
    
         (thm (let ((x (list x0 x1 x2 x3)))
                (equal (nthcdr (length x) (append x y))
                       y))
              :hints (("Goal" :bdd (:vars nil))))
    
    If we execute the following form that disables the definition and
    executable counterpart of the function ZP
    
         (in-theory (disable zp (zp)))
    
    before attempting the proof of the theorem above, we can see more
    clearly the point of using IF*.  In this case, the prover makes the
    following report.
    
         ACL2 Error in ( THM ...):  Unable to resolve test of IF* for term
    
         (IF* (ZP (+ 1 N)) X (NTHCDR N (CDR X)))
    
         under the bindings
    
         ((X (CONS X0 (CONS X1 (CONS X2 #)))) (N '3))
    
         -- use SHOW-BDD to see a backtrace.
    
    If we follow the advice above, we can see rather clearly what happened.
    See *note SHOW-BDD::.
    
         ACL2 !>(show-bdd)
    
         BDD computation on Goal yielded 21 nodes.
         ==============================
    
         BDD computation was aborted on Goal, and hence there is no
         falsifying assignment that can be constructed.  Here is a backtrace
         of calls, starting with the top-level call and ending with the one
         that led to the abort.  See :DOC show-bdd.
    
         (LET ((X (LIST X0 X1 X2 X3)))
              (EQUAL (NTHCDR (LENGTH X) (APPEND X Y)) Y))
           alist: ((Y Y) (X3 X3) (X2 X2) (X1 X1) (X0 X0))
    
         (NTHCDR (LENGTH X) (APPEND X Y))
           alist: ((X (LIST X0 X1 X2 X3)) (Y Y))
    
         (IF* (ZP (+ 1 N)) X (NTHCDR N (CDR X)))
           alist: ((X (LIST* X0 X1 X2 X3 Y)) (N 3))
         ACL2 !>
    
    Each of these term-alist pairs led to the next, and the test of the
    last one, namely (ZP (+ 1 N)) where N is bound to 3, was not simplified
    to t or to nil.
    
    What would have happened if we had used IF in place of IF* in the rule
    nthcdr-add1?  In that case, if ZP and its executable counterpart were
    disabled then we would be put into an infinite loop!  For, each time a
    term of the form (NTHCDR k V) is encountered by the BDD package (where
    k is an explicit number), it will be rewritten in terms of (NTHCDR k-1
    (CDR V)).  We would prefer that if for some reason the term (ZP (+ 1
    N)) cannot be decided to be t or to be nil, then the BDD computation
    should simply abort.
    
    Even if there were no infinite loop, this kind of use of IF* is useful
    in order to provide feedback of the form shown above whenever the test
    of an IF term fails to simplify to t or to nil.
    
    
    File: acl2-doc-emacs.info,  Node: SHOW-BDD,  Prev: IF*,  Up: BDD
    
    SHOW-BDD    inspect failed BDD proof attempts
    
    Attempts to use BDDs (see *note BDD::), using :bdd hints, can fail for
    various reasons.  Sometimes it is useful to explore such failures.  To
    do so, one may simply execute the form
    
         (show-bdd)
    
    inside the ACL2 loop.  The system's response is generally
    self-explanatory.  Perhaps you have already seen show-bdd used in some
    examples (see *note BDD-INTRODUCTION:: and see *note IF*::).  Here we
    give some details about show-bdd.
    
    (Show-bdd) prints the goal to which the BDD procedure was applied and
    reports the number of nodes created during the BDD computation,
    followed by additional information depending on whether or not the
    computation ran to completion or aborted (for reasons explained
    elsewhere; see *note BDD-ALGORITHM::).  If the computation did abort, a
    backtrace is printed that should be useful in understanding where the
    problem lies.  Otherwise, (show-bdd) prints out "falsifying
    constraints."  This list of pairs associates terms with values and
    suggests how to construct a binding list for the variables in the
    conjecture that will falsify the conjecture.  It also prints out the
    term that is the result of simplifying the input term.  In each of
    these cases, parts of the object may be hidden during printing, in
    order to avoid creating reams of uninteresting output.  If so, the user
    will be queried about whether he wishes to see the entire object (alist
    or term), which may be quite large.  The following responses are legal:
    
           w -- Walk around the object with a structure editor
    
         t -- Print the object in full
    
         nil -- Do not print any more of the object
    
    
    Show-bdd actually has four optional arguments, probably rarely used.
    The general form is
    
         (show-bdd goal-name goal-ans falsifying-ans term-ans)
    
    where goal-name is the name of the goal on which the :bdd hint was used
    (or, nil if the system should find such a goal), goal-ans is the answer
    to be used in place of the query for whether to print the input goal in
    full, falsifying-ans is the answer to be used in place of the query for
    whether to print the falsifying constraints in full, and term-ans is
    the answer to be used in place of the query for whether to print the
    resulting term in full.
    
    
    File: acl2-doc-emacs.info,  Node: BOOKS,  Next: BREAK-REWRITE,  Prev: BDD,  Up: Top
    
    BOOKS    files of ACL2 event forms
    
    This documentation topic is about ACL2 input files.  However, there are
    two traditional (paper) books published about ACL2: a textbook and a
    case studies book.  Further information on those two paper books is
    available by following links from the ACL2 home page,
    `http://www.cs.utexas.edu/users/moore/acl2/'.
    
    A "book" is a file of ACL2 events that have been certified as
    admissible.  Using include-book you can construct a new logical world
    by assuming the events in any number of mutually compatible books.
    Relevant documented topics are listed below.  Following this list is a
    "guided tour" through the topics.
    
    You can contribute books to the ACL2 community and obtain updates
    inbetween ACL2 releases by visiting the acl2-books project web page,
    `http://acl2-books.googlecode.com/'.  Also see *note COMMUNITY-BOOKS::.
    
    * Menu:
    
    * BOOK-COMPILED-FILE:: creating and loading of compiled and expansion files for books
    
    * BOOK-CONTENTS:: restrictions on the forms inside books
    
    * BOOK-EXAMPLE:: how to create, certify, and use a simple book
    
    * BOOK-MAKEFILES:: See *note BOOKS-CERTIFICATION::.
    
    * BOOK-NAME:: conventions associated with book names
    
    * BOOKS-CERTIFICATION:: certifying ACL2 community books
    
    * BOOKS-CERTIFICATION-CLASSIC:: classic ACL2 `make'-based certification of books
    
    * CBD:: connected book directory string
    
    * CERTIFICATE:: how a book is known to be admissible and where its defpkgs reside
    
    * CERTIFY-BOOK:: how to produce a certificate for a book
    
    * CERTIFYING-BOOKS:: See *note BOOKS-CERTIFICATION::.
    
    * COMMUNITY-BOOKS:: books contributed by the ACL2 community
    
    * FULL-BOOK-NAME:: book naming conventions assumed by ACL2
    
    * KEEP:: how we know if include-book read the correct files
    
    * PATHNAME:: introduction to filename conventions in ACL2
    
    * PORTCULLIS:: the gate guarding the entrance to a certified book
    
    * PROVISIONAL-CERTIFICATION:: certify a book in stages for improved parallelism
    
    * REGRESSION:: See *note BOOKS-CERTIFICATION::.
    
    * SET-CBD:: to set the connected book directory
    
    * UNCERTIFIED-BOOKS:: invalid certificates and uncertified books
    
    
    Related topics other than immediate subtopics:
    * INCLUDE-BOOK:: load the events in a file
    
    _Introduction._
    
    A "book" is a file of ACL2 forms.  Books are prepared entirely by the
    user of the system, i.e., they are "source" files not "object" files.
    Some of the forms in a book are marked local and the others are
    considered "non-local."
    
    Include-book lets you load a book into any ACL2 world.  If completed
    without error, the inclusion of a book extends the logic of the host
    world by the addition of just the non-local events in the book.  You
    may extend the world by successively including a variety of books to
    obtain the desired collection of definitions and rules.  Unless name
    conflicts occur (which are detected and signalled) inclusion of a book
    is consistency preserving provided the book itself is consistent as
    discussed later.  However, include-book merely assumes the validity of
    the events in a book; if you include a book that contains an
    inconsistency (e.g., an inadmissible definition) then the resulting
    theory is inconsistent.
    
    It is possible to "certify" a book, with certify-book, guaranteeing
    that the error-free inclusion of the certified forms will produce a
    consistent extension of a consistent logic.  Certification processes
    both the local and non-local forms, so you can mark as local those
    events you need for certification that you want to hide from users of
    the book (e.g., hacks, crocks, and kludges on the way to a good set of
    :rewrite rules).  Certification can also "compile" a book, thereby
    speeding up the execution of the functions defined within it.  The
    desire to compile books is largely responsible for the restrictions we
    put on the forms allowed in books.
    
    Extensive documentation is available on the various aspects of books.
    We recommend that you read it all before using books.  It has been
    written so as to make sense when read in a certain linear sequence,
    called the "guided tour", though in general you may browse through it
    randomly.  If you are on the guided tour, you should next read the
    documentation on book-example (see *note BOOK-EXAMPLE::).
    
    
    File: acl2-doc-emacs.info,  Node: BOOK-COMPILED-FILE,  Next: BOOK-CONTENTS,  Prev: BOOKS,  Up: BOOKS
    
    BOOK-COMPILED-FILE    creating and loading of compiled and expansion files for books
    
    An effect of compilation is to speed up the execution of the functions
    defined in a book.  Compilation can also remove tail recursion, thus
    avoiding stack overflows.  The presence of compiled code for the
    functions in the book should not otherwise affect the performance of
    ACL2.  See *note GUARD:: for a discussion; also See *note COMPILATION::.
    
    By default, the certify-book command compiles the book that it
    certifies.  see *note CERTIFY-BOOK:: for how to control this behavior.
    
    By default, the include-book command loads the compiled file for the
    book.  The details of how this loading works are subtle, and do not
    need to be understood by most users.  The ACL2 source code contains an
    "Essay on Hash Table Support for Compilation" that explains such
    details for those interested.  All that users should generally need to
    know about this is that the compiled file is always the result of
    compiling a so-called "expansion file", which contains certain
    additional code besides the book itself.  The relevance to users of the
    expansion file is that it can be loaded if the compiled file is missing
    (except when :load-compiled-file t is specified by the include-book
    form), and its existence is required in order for include-book to
    create a book's compiled file, as described below.
    
    Most users can skip the remainder of this documentation topic, which
    addresses the uncommon activity of using include-book to compile books.
    
    Include-book can be made to compile a book by supplying its keyword
    argument :load-compiled-file the value :comp.  However, a compiled file
    can only be produced if there is already an _expansion file_ that is at
    least as recent as the book's certificate.  Such a file, whose name
    happens to be the result of concatenating the string "@expansion.lsp"
    to the book name (without the ".lisp" suffix), is created by
    certify-book when state global variable 'save-expansion-file has a
    non-nil value.  That will be the case if ACL2 started up when
    environment variable ACL2_SAVE_EXPANSION was t (or any value that is
    not the empty string and whose string-upcase is not "NIL"), until the
    time (if any) that 'save-expansion-file is assigned a different value by
    the user.  In most respects, the :comp setting is treated exactly the
    same as :warn; but after all events in the book are processed, the
    expansion file is compiled if a compiled file was not loaded, after
    which the resulting compiled file is loaded.
    
    One can thus, for example, compile books for several different host
    Lisps -- useful when installing ACL2 executables at the same site that
    are built on different host Lisps.  A convenient way to do this in an
    environment that provides Gnu `make' is to certify the community books
    using the shell command "make regression" in the acl2-sources/
    directory, after setting environment variable ACL2_SAVE_EXPANSION to t,
    and then moving to the books directory and executing the appropriate
    `make' commands to compile the books (targets fasl, o, and so on,
    according to the compiled file extension for the host Lisp).
    
    We conclude by saying more about the :load-compiled-file argument of
    include-book.  We assume that state global 'compiler-enabled has a
    non-nil value; otherwise :load-compiled-file is always treated as nil.
    
    We do not consider raw mode below (see *note SET-RAW-MODE::), which
    presents a special case: ACL2 will attempt to load the book itself
    whenever it would otherwise load the expansion or compiled file, but
    cannot (either because the :load-compiled-file argument is nil, or for
    each of the expansion and compiled files, either it does not exist or
    it is out of date with respect to the .cert file).
    
    The :load-compiled-file argument is not recursive: calls of
    include-book that are inside the book supplied to include-book use
    their own :load-compiled-file arguments.  However, those subsidiary
    include-book calls can nevertheless be sensitive to the
    :load-compiled-file arguments of enclosing include-book calls, as
    follows.  If :load-compiled-file has value t, then every subsidiary
    include-book is required to load a compiled file.  Moreover, if a book's
    compiled file or expansion file is loaded in raw Lisp, then an attempt
    will be made to load the compiled file or expansion file for any
    include-book form encountered during that load.  If that attempt fails,
    then that load immediately aborts, as does its parent load, and so on
    up the chain.  If, when going up the chain, an include-book is aborted
    for which keyword argument :load-compiled-file has value t, then an
    error occurs.
    
    When loading a book's compiled file or expansion file, FILE, it is
    possible to encounter an include-book form for a book that has no
    suitable compiled file or expansion file.  In that case, the load of
    FILE is aborted at that point.  Similarly, the load of FILE is aborted
    in the case that this include-book form has a suitable compiled file or
    expansion file whose load is itself aborted.  Thus, whenever any
    include-book aborts, so do all of its parent include-books, up the
    chain.  Such an abort causes an error when the include-book form
    specifies a :load-compiled-file value of t.
    
    
    File: acl2-doc-emacs.info,  Node: BOOK-CONTENTS,  Next: BOOK-EXAMPLE,  Prev: BOOK-COMPILED-FILE,  Up: BOOKS
    
    BOOK-CONTENTS    restrictions on the forms inside books
    
         Example Book:
    
         ; This book defines my app function and the theorem that it is
         ; associative.  One irrelevant help lemma is proved first but
         ; it is local and so not seen by include-book.  I depend on the
         ; inferior book "weird-list-primitives" from which I get
         ; definitions of hd and tl.
    
         (in-package "MY-PKG")
    
         (include-book "weird-list-primitives")
    
         (defun app (x y) (if (consp x) (cons (hd x) (app (tl x) y)) y))
    
         (local
          (defthm help-lemma
            (implies (true-listp x) (equal (app x nil) x))))
    
         (defthm app-is-associative
           (equal (app (app a b) c) (app a (app b c))))
    
    The first form in a book must be (in-package "pkg") where "pkg" is some
    package name known to ACL2 whenever the book is certified.  The rest of
    the forms in a book are embedded event forms, i.e., defuns, defthms,
    etc., some of which may be marked local.  See *note
    EMBEDDED-EVENT-FORM::.  The usual Common Lisp commenting conventions
    are provided.  Note that since a book consists of embedded event forms,
    we can talk about the "local" and "non-local" events of a book.
    
    Because in-package is not an embedded event form, the only in-package
    in a book is the initial one.  Because defpkg is not an embedded event
    form, a book can never contain a defpkg form.  Because include-book is
    an embedded event form, books may contain references to other books.
    This makes books structured objects.
    
    When the forms in a book are read from the file, they are read with
    current-package set to the package named in the in-package form at the
    top of the file.  The effect of this is that all symbols are interned
    in that package, except those whose packages are given explicitly with
    the "::" notation.  For example, if a book begins with (in-package
    "ACL2-X") and then contains the form
    
           (defun fn (x)
             (acl2::list 'car x))
    
    then defun, fn, x, and car are all interned in the "ACL2-X" package.
    I.e., it is as though the following form were read instead:
    
           (acl2-x::defun acl2-x::fn (acl2-x::x)
               (acl2::list 'acl2-x::car acl2-x::x)).
    
    Of course, acl2-x::defun would be the same symbol as acl2::defun if the
    "ACL2-X" package imported acl2::defun.
    
    If each book has its own unique package name and all the names defined
    within the book are in that package, then name clashes between books
    are completely avoided.  This permits the construction of useful
    logical worlds by the successive inclusion of many books.  Although it
    is often too much trouble to manage several packages, their judicious
    use is a way to minimize name clashes.  Often, a better way is to use
    local; see *note LOCAL::.
    
    How does include-book know the definitions of the packages used in a
    book, since defpkgs cannot be among the forms?  More generally, how do
    we know that the forms in a book will be admissible in the host logical
    world of an include-book?  See *note CERTIFICATE:: for answers to these
    questions.
    
    
    File: acl2-doc-emacs.info,  Node: BOOK-EXAMPLE,  Next: BOOK-MAKEFILES,  Prev: BOOK-CONTENTS,  Up: BOOKS
    
    BOOK-EXAMPLE    how to create, certify, and use a simple book
    
    Suppose you have developed a sequence of admissible events which you
    want to turn into a book.  We call this "publishing" the book.  This
    note explains how to do that.
    
    A key idea of books is that they are "incremental" in the sense that
    when you include a book in a host logical world, the world is
    incrementally extended by the results established in that book.  This
    is allowed only if every name defined by the incoming book is either
    new or is already identically defined.  See *note REDUNDANT-EVENTS::.
    This is exactly the same problem faced by a programmer who wishes to
    provide a utility to other people: how can he make sure he doesn't
    create name conflicts?  The solution, in Common Lisp, is also the same:
    use packages.  While books and packages have a very tenuous formal
    connection (every book must start with an in-package), the creation of
    a book is intimately concerned with the package issue.  Having
    motivated what would otherwise appear as an unnecessary fascination
    with packages below, we now proceed with a description of how to
    publish a book.
    
    Just to be concrete, let's suppose you have already gotten ACL2 to
    accept the following sequence of commands, starting in the ACL2 initial
    state.
    
            (defpkg "ACL2-MY-BOOK"
                    (union-eq *common-lisp-symbols-from-main-lisp-package*
                              *acl2-exports*))
            (in-package "ACL2-MY-BOOK")
            (defun app (x y)
              (if (consp x) (cons (car x) (app (cdr x) y)) y))
            (defun rev (x)
              (if (consp x) (app (rev (cdr x)) (list (car x))) nil))
            (defthm rev-app-hack
              (equal (rev (app a (list x))) (cons x (rev a))))
            (defthm rev-rev
              (implies (acl2::true-listp x) (equal (rev (rev x)) x)))
    
    Observe that the first form above defines a package (which imports the
    symbols defined in CLTL such as if and cons and the symbols used to
    command ACL2 such as defun and defthm).  The second form selects that
    package as the current one.  All subsequent forms are read into that
    package.  The remaining forms are just event forms: defuns and defthms
    in this case.
    
    Typically you would have created a file with Emacs containing these
    forms and you will have submitted each of them interactively to ACL2 to
    confirm that they are all admissible.  That interactive verification
    should start in ACL2's initial world -- although you might, of course,
    start your sequence of events with some include-books to build a more
    elaborate world.
    
    The first step towards publishing a book containing the results above
    is to create a file that starts with the in-package and then contains
    the rest of the forms.  Let's call that file "my-book.lisp".  The name
    is unimportant, except it must end with ".lisp".  If there are events
    that you do not wish to be available to the user of the book -- e.g.,
    lemmas you proved on your way toward proving the main ones -- you may
    so mark them by enclosing them in local forms.  See *note LOCAL::.  Let
    us suppose you wish to hide rev-app-hack above.  You may also add
    standard Lisp comments to the file.  The final content of "my-book.lisp"
    might be:
    
          ; This book contains my app and rev functions and the theorem
          ; that rev is its own inverse.
    
            (in-package "ACL2-MY-BOOK")
            (defun app (x y)
              (if (consp x) (cons (car x) (app (cdr x) y)) y))
            (defun rev (x)
              (if (consp x) (app (rev (cdr x)) (list (car x))) nil))
    
          ; The following hack is not exported.
            (local (defthm rev-app-hack
              (equal (rev (app a (list x))) (cons x (rev a)))))
    
            (defthm rev-rev
              (implies (acl2::true-listp x) (equal (rev (rev x)) x)))
    
    The file shown above *is* the book.  By the time this note is done you
    will have seen how to certify that the book is correct, how to compile
    it, and how to use it in other host worlds.  Observe that the defpkg is
    not in the book.  It cannot be: Common Lisp compilers disagree on how
    to treat new package definitions appearing in files to be compiled.
    
    Since a book is just a source file typed by the user, ACL2 provides a
    mechanism for checking that the events are all admissible and then
    marking the file as checked.  This is called certification.  To certify
    "my-book.lisp" you should first get into ACL2 with an initial world.
    Then, define the package needed by the book, by typing the following
    defpkg to the ACL2 prompt:
    
         ACL2 !>(defpkg "ACL2-MY-BOOK"
                        (union-eq *common-lisp-symbols-from-main-lisp-package*
                                  *acl2-exports*))
    
    Then execute the command:
    
         ACL2 !>(certify-book "my-book" 1 t) ; the `t' is in fact the default
    
    Observe that you do not type the ".lisp" part of the file name.  For
    purposes of books, the book's name is "my-book" and by the time all is
    said and done, there will be several extensions in addition to the
    ".lisp" extension associated with it.
    
    The 1 tells certify-book that you acknowledge that there is one command
    in this "certification world" (namely the defpkg).  To use the book,
    any prospective host world must be extended by the addition of whatever
    commands occurred before certification.  It would be a pity to certify
    a book in a world containing junk because that junk will become the
    "portcullis" guarding entrance to the book.  The t above tells
    certify-book that you wish to compile "my-book.lisp" also (but see
    *note COMPILATION:: for an exception).  Certify-book makes many checks
    but by far the most important and time-consuming one is that it
    "proves" every event in the file.
    
    When certify-book is done it will have created two new files.  The
    first will be called "my-book.cert" and contains the "certificate"
    attesting to the admissibility of the events in "my-book.lisp".  The
    certificate contains the defpkg and any other forms necessary to
    construct the certification world.  It also contains various check sums
    used to help you keep track of which version of "my-book.lisp" was
    certified.
    
    The second file that may be created by certify-book is the compiled
    version of "my-book.lisp" and will have a name that is assigned by the
    host compiler (e.g., "my-book.o" in GCL, "my-book.fasl" in SBCL).
    Certify-book will also load this object file.  When certify-book is
    done, you may throw away the logical world it created, for example by
    executing the command :u.
    
    To use the book later in any ACL2 session, just execute the event
    (include-book "my-book").  This will do the necessary defpkg, load the
    non-local events in "my-book.lisp" and then may load the compiled code
    for the non-local functions defined in that file.  Checks are made to
    ensure that the certificate file exists and describes the version of
    "my-book.lisp" that is read.  The compiled code is loaded if and only
    if it exists and has a later write date than the source file (but see
    *note COMPILATION:: for an exception).
    
    Since include-book is itself an event, you may put such forms into
    other books.  Thus it is possible for the inclusion of a single book to
    lead to the inclusion of many others.  The check sum information
    maintained in certificates helps deal with the version control problem
    of the referenced books.  I.e., if this version of "my-book" is used
    during the certification of "your-book", then the certificate for
    "your-book" includes the check sum of this version of "my-book".  If a
    later (include-book "your-book") finds a version of "my-book" with a
    different check sum, an error is signalled.  But check sums are not
    perfect and the insecurity of the host file system prevents ACL2 from
    guaranteeing the logical soundness of an include-book event, even for a
    book that appears to have a valid certificate (they can be forged,
    after all).  (See *note CERTIFICATE:: for further discussion.)
    
    This concludes the example of how to create, certify and use a book.
    If you wish, you could now review the documentation for book-related
    topics (see *note BOOKS::) and browse through them.  They'll probably
    make sense in this context.  Alternatively, you could continue the
    "guided tour" through the rest of the documentation of books.  See
    *note BOOK-NAME::, following the pointer given at the conclusion.
    
    
    File: acl2-doc-emacs.info,  Node: BOOK-MAKEFILES,  Next: BOOK-NAME,  Prev: BOOK-EXAMPLE,  Up: BOOKS
    
    BOOK-MAKEFILES    See *note BOOKS-CERTIFICATION::.
    
    
    File: acl2-doc-emacs.info,  Node: BOOK-NAME,  Next: BOOKS-CERTIFICATION,  Prev: BOOK-MAKEFILES,  Up: BOOKS
    
    BOOK-NAME    conventions associated with book names
    
         Examples:
         "list-processing"
         "/usr/home/smith/my-arith"
    
    Book names are string constants that can be elaborated into file names.
    We elaborate book names by concatenating the "connected book directory"
    (see *note CBD::) string on the left and some "extension," such as
    ".lisp", on the right.  However, the connected book directory is not
    added if the book name itself already represents an absolute file name.
    Furthermore, include-book and certify-book temporarily reset the
    connected book directory to be the directory of the book being
    processed.  This allows include-book forms to use file names without
    explicit mention of the enclosing book's directory.  This in turn allows
    books (together with those that they include, using include-book) to be
    moved between directories while maintaining their certification and
    utility.
    
    You may wish to read elsewhere for details of ACL2 file name
    conventions (see *note PATHNAME::), for a discussion of the filename
    that is the result of the elaboration described here (see *note
    FULL-BOOK-NAME::), and for details of the concept of the connected book
    directory (see *note CBD::).  For details of how include-book (see
    *note INCLUDE-BOOK::) and certify-book (see *note CERTIFY-BOOK::) use
    these concepts, see below.
    
    Often a book name is simply the familiar name of the file.  (See *note
    FULL-BOOK-NAME:: for discussion of the notions of "directory string,"
    "familiar name," and "extension".  These concepts are not on the guided
    tour through books and you should read them separately.)  However, it
    is permitted for book names to include a directory or part of a
    directory name.  Book names never include the extension, since ACL2
    must routinely tack several different extensions onto the name during
    include-book.  For example, include-book uses the ".lisp", ".cert" and
    possibly the ".o" or ".lbin" extensions of the book name.
    
    Book names are elaborated into full file names by include-book and
    certify-book.  This elaboration is sensitive to the "connected book
    directory." The connected book directory is an absolute filename string
    (see *note PATHNAME::) that is part of the ACL2 state.  (You may wish
    to see *note CBD:: and to see *note SET-CBD:: -- note that these are
    not on the guided tour).  If a book name is an absolute filename
    string, ACL2 elaborates it simply by appending the desired extension to
    the right.  If a book name is a relative filename string, ACL2 appends
    the connected book directory on the left and the desired extension on
    the right.
    
    Note that it is possible that the book name includes some partial
    specification of the directory.  For example, if the connected book
    directory is "/usr/home/smith/" then the book name
    "project/task-1/arith" is a book name that will be elaborated to
    
         "/usr/home/smith/project/task-1/arith.lisp".
    
    Observe that while the events in this "arith" book are being processed
    the connected book directory will temporarily be set to
    
         "/usr/home/smith/project/task-1/".
    
    Thus, if the book requires other books, e.g.,
    
         (include-book "naturals")
    
    then it is not necessary to specify the directory on which they reside
    provided that directory is the same as the superior book.
    
    This inheritance of the connected book directory and its use to
    elaborate the names of inferior books makes it possible to move books
    and their inferiors to new directories, provided they maintain the same
    relative relationship.  It is even possible to move with ease whole
    collections of books to different filesystems that use a different
    operating system than the one under which the original certification
    was performed.
    
    The ".cert" extension of a book, if it exists, is presumed to contain
    the most recent certificate for the book.  See *note CERTIFICATE:: (or,
    if you are on the guided tour, wait until the tour gets there).
    
    See *note BOOK-CONTENTS:: to continue the guided tour.
    
    
    File: acl2-doc-emacs.info,  Node: BOOKS-CERTIFICATION,  Next: BOOKS-CERTIFICATION-CLASSIC,  Prev: BOOK-NAME,  Up: BOOKS
    
    BOOKS-CERTIFICATION    certifying ACL2 community books
    
    For background on the ACL2 community books, see *note
    COMMUNITY-BOOKS::.  Here we explain how to certify those books, or some
    of those books, with ACL2.  We thank Bishop Brock, Jared Davis, and Sol
    Swords for their substantial contributions to this methodology.  See
    books/Makefile, in the community books, for more about "Credits and
    History", and for additional technical details not covered in this
    topic (for example, how to build a local copy of the xdoc manual for
    those who may wish to do that).
    
    For more information about installing ACL2, see the installation
    instructions, either by following a link from the ACL2 home page or by
    going directly to the page
    `http://www.cs.utexas.edu/users/moore/acl2/current/installation/installation.html'.
    For information about so-called "classic ACL2 `make'-based
    certification", which provides support for certifying directories of
    books but may disappear in a future ACL2 release, see *note
    BOOKS-CERTIFICATION-CLASSIC::.
    
    *The Basics*
    
    We make the following assumptions.
    
         o Gnu `make' is available on your system via the `make' command
         (rather than some other flavor of `make').  (Execute `make
         -version to verify this.)
    
         o You have built or obtained an ACL2 executable.
    
         o The ACL2 community books are installed in the books/
         subdirectory of your ACL2 distribution, as is the case when you
         have followed the standard installation instructions.
    
         o All commands shown below are issued in the top-level (ACL2
         sources) directory of your ACL2 distribution.
    
    By default the ACL2 executable is file saved_acl2 in your ACL2 sources
    directory, and you can issue the following command to the shell in
    order to do a "regression run" that certifies all of the community
    books using that executable.
    
         make regression
    
    Better yet, save a log file in case there are problems, for example as
    follows.
    
         (make regression) >& make-regression.log
    
    or perhaps better yet:
    
         (time nice make regression) >& make-regression.log
    
    For the sake of brevity, below we'll skip mentioning any of `time',
    `nice', or `>& make-regression.log'.  But saving a log file, in
    particular, is useful in case you encounter problems to report.
    
    If you fetched the community books using svn, then you will have a
    directory books/workshops/ that is not necessary for certifying the
    other books.  If you want to skip certification of the books under
    books/workshops/, use target `certify-books' instead of target
    `regression', for example as follows.
    
         (time nice make certify-books) >& make-certify-books.log
    
    Whether you use target `regression' or target `certify-books', then for
    each book foo.lisp whose certification is attempted, a file
    foo.cert.out in the same directory will contain the output from the
    book's certification attempt.
    
    A regression run may take a few hours, but if you have a multiprocessing
    computer, you can speed it up by certifying some books in parallel, by
    providing a value for `make' option -j.  For example, if you have 8
    hardware threads then you might want to issue the following command.
    
         make regression -j 8
    
    *Specifying the ACL2 Executable*
    
    If your ACL2 executable is not file saved_acl2 in the ACL2 sources
    directory, then you will need to specify that executable.  You can do
    that by setting variable ACL2, either as an environment variable or, as
    displayed below, as a `make' variable.  Either way, you will need to
    avoid relative pathnames.  For example, the first two forms below are
    legal, but the third is not, assuming that my-acl2 is on your PATH in a
    Unix-like environment (e.g., linux or MacOS) and that my-saved_acl2 is
    just a pathname relative to your ACL2 sources directory, which is not
    on your path.
    
         make regression -j 8 ACL2=my-acl2
         make regression -j 8 ACL2=/u/smith/bin/acl2
         # The following only works if my-saved_acl2 is on your path (see above).
         make regression -j 8 ACL2=my-saved_acl2
    
    *Cleaning*
    
    You can delete files generated by book certification (including .cert
    files, .out files, compiled files, and more) by issuing the following
    command (again, in your ACL2 sources directory).
    
         make clean-books
    
    If you want to cause such deletion and then do a regression, simply
    replace the `regression' or `certify-books' target by `regression-fresh'
    or `certify-books-fresh', respectively, for example as follows.
    follows.
    
         make -j 4 regression-fresh
         make -j 4 certify-books-fresh
    
    If however you only want to clean up generated files residing under a
    given directory (or its subdirectories, and recursively), you can issue
    the following command while standing in that directory, where DIR is a
    pathname of your books directory.
    
         DIR/clean.pl
    
    For example, to clean up generated files under books/arithmetic, you
    could do the following.
    
         cd books/arithmetic
         ../clean.pl
         cd - # to return to the ACL2 sources directory, if you wish to do so
    
    *Restricting to Specific Directories and Books*
    
    You can specify which books you want certified by using any or all of
    the variables EXCLUDED_PREFIXES, ACL2_BOOK_CERTS, or ACL2_BOOK_DIRS.
    First, the set of desired .cert files is restricted to those that do not
    start with any string that is one of the words in the value of
    EXCLUDED_PREFIXES.  Then ACL2_BOOK_CERTS and ACL2_BOOK_DIRS, if
    supplied, specify which books should be certified, as illustrated by the
    following example.
    
         make -j 8 regression-fresh \
          ACL2_BOOK_DIRS="symbolic paco" \
          ACL2_BOOK_CERTS=" \
           workshops/2006/cowles-gamboa-euclid/Euclid/ed6a.cert \
           workshops/2006/cowles-gamboa-euclid/Euclid/ed4bb.cert \
           "
    
    Then all book in directories symbolic and paco will be certified, as
    will the books workshops/2006/cowles-gamboa-euclid/Euclid/ed6a.lisp and
    workshops/2006/cowles-gamboa-euclid/Euclid/ed4bb.lisp.  Note that all
    pathnames should be relative to your community books directory; in
    particular, they should not be absolute pathnames.  Also notice the
    .cert extension used in files supplied for ACL2_BOOK_CERTS.
    
    Alternatively, you may wish to invoke books/cert.pl while standing in a
    directory under which you want to certify books.  This will certify not
    only those books, but all supporting books -- even those not under the
    current directory -- that do not have up-to-date .cert files.  The
    following is a simple command to invoke that will certify all books in
    the current directory, where if the books/ directory is not on your
    path, you will need to provide a suitable filename, e.g. ../../cert.pl
    or ~/acl2/books/cert.pl.
    
         cert.pl -j 4 *.lisp
    
    Here is a more complex command, which illustrates a way to certify
    books in subdirectories (as well as the current directory), the use of
    provisional certification (see *note PROVISIONAL-CERTIFICATION::), and
    `make'-level parallelism (in this case specifying four parallel
    processes).
    
         ACL2_PCERT=t cert.pl -j 4 `find . -name '*.lisp'`
    
    Note that with this approach, unlike classic ACL2 `make'-based
    certification (see *note BOOKS-CERTIFICATION-CLASSIC::, out-of-date
    .cert files that are not under the current directory will also be
    built.  For documentation of cert.pl invoke:
    
         cert.pl -h
    
    See the top of cert.pl for authorship and copyright information.
    
    Finally, we give a brief summary of how to use so-called "classic ACL2
    `make'-based certification" for community books; see *note
    BOOKS-CERTIFICATION-CLASSIC:: for details.  Note that support for this
    approach might be eliminated in a future ACL2 release.  We welcome
    comments from the ACL2 community about whether or not that would be a
    good thing to do.  See the discussion above about ACL2_BOOK_DIRS for
    the "modern" way to accomplish the same thing.
    
    Many community book directories have a Makefile.  If you modify books
    only in such a directory, you can recertify by standing in that
    directory and issuing a `make' command.  This command can optionally
    specify an ACL2 executable as well as parallelism, for example as
    follows, where the first line (make clean) is optional.
    
         make clean
         (time nice make -j 8 ACL2=my-acl2)
    
    *ACL2 Customization Files*
    
    By default, your acl2-customization file (see *note
    ACL2-CUSTOMIZATION::) is ignored by all flavors of "make regression".
    However, you can specify the use of an acl2-customization file by
    setting the value of environment variable ACL2_CUSTOMIZATION to the
    empty string, indicating a default such file, or to the desired
    absolute pathname.  For example:
    
         make regression ACL2_CUSTOMIZATION=''
         make regression ACL2_CUSTOMIZATION='~/acl2-customization.lisp'
    
    *Regressions for Experimental Extensions of ACL2*
    
    The instructions are unchanged if you are using ACL2(h) (see *note
    HONS-AND-MEMOIZATION::).  However, note that the default executable
    (when ACL2 is not specified) remains saved_acl2 in your ACL2 sources
    directory, not saved_acl2h as one might expect for ACL2(h)(p),
    respectively.  So probably you'll want to supply "ACL2="
    explicitly with your `make' command.
    
    The comments above also pertain pertain to using ACL2(p) (see *note
    PARALLEL::); the default is saved_acl2 rather than saved_acl2p.
    However, we recommend that you use ACL2, not ACL2(p), for your
    regression.  Then you can use ACL2(p) for your own proof developments.
    (The analogous comment applies to ACL2(hp), which combines capabilities
    of ACL2(h) and ACL2(p), i.e., we recommend that you use ACl2(h) for
    your regression in that case.)  However, if you want to use ACL2(p) or
    ACL2(hp) for your regression, see *note
    WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION::.
    
    If you intend to certify books in the nonstd subdirectory of the
    community books, you will want to use ACL2(r) (see *note REAL::).  In
    that case, substitute target regression-nonstd for target regression.
    The default executable (when ACL2 is not specified) will be file
    saved_acl2r in your ACL2 sources directory, rather than saved_acl2.
    
    *Provisional Certification*
    
    To use provisional certification (see *note
    PROVISIONAL-CERTIFICATION::), supply ACL2_PCERT=t with your `make'
    command.  Here is an example.
    
         time nice make regression -j 4 ACL2_BOOK_DIRS=deduction ACL2_PCERT=t
    
    *Miscellany*
    
    Other control of the certification process may be found by perusing
    community books file books/make_cert.  In particular, the INHIBIT
    variable may be set to a call of set-inhibit-output-lst, for example as
    follows to obtain the output one would get by default in an
    (interactive) ACL2 session.
    
         time nice make regression -j 4 ACL2_BOOK_DIRS=arithmetic \
           INHIBIT='(set-inhibit-output-lst proof-tree)'
    
    *Troubleshooting*
    
    If you run into problems, you can get help by joining the acl2-help
    email list (follow the link from the ACL2 home page) and sending a
    message to that list.  Also consider trying another version of GNU
    `make'; for example, we have found that versions 3.81 and 3.82
    sometimes cause errors on Linux where version 3.80 does not.  Note
    however that Version 3.80 does not print certain informational messages
    that are printed by later versions.
    
    
    File: acl2-doc-emacs.info,  Node: BOOKS-CERTIFICATION-CLASSIC,  Next: CBD,  Prev: BOOKS-CERTIFICATION,  Up: BOOKS
    
    BOOKS-CERTIFICATION-CLASSIC    classic ACL2 `make'-based certification of books
    
    This documentation topic explains an approach to certifying directories
    of books, which we call "classic ACL2 `make'-based certification".
    
    Warning: The capability described in this section might be replaced at
    any time by a capability based on corresponding support for community
    books (see *note BOOKS-CERTIFICATION::).  If you think that would be a
    hardship, please contact the ACL2 implementors.
    
    This topic discusses a way to certify a directory of books other than
    the ACL2 community books.  See *note BOOKS-CERTIFICATION:: for how to
    certify the set of ACL2 community books.  There is also a section in
    that documentation topic, "Restricting to Specific Directories and
    Books", that provides an alternative to classic ACL2 `make'-based
    certification (as discussed in the present topic) for certifying
    specified sets of books.
    
    We assume here a familiarity with Unix/Linux `make'.  We also assume
    that you are using GNU `make' rather than some other flavor of `make'.
    And finally, we assume, as is typically the case by following the
    standard installation instructions, that you install the ACL2 community
    books in the books/ subdirectory of your ACL2 distribution.  We will
    refer below to that directory as BOOKS.
    
    In summary: to use `make' to certify books under a given directory, you
    may create a simple Makefile in that directory (as explained below) so
    that when you stand in that directory, you can submit the command,
    `make', to certify those books.  If you have a multi-processor machine
    or the like, then you can use the `-j flag `make'-level parallelism by
    specifying the number of concurrent processes.  For example:
    
         make -j 4
    
    For each book foo.lisp, a file foo.out in the same directory as
    foo.lisp will contain the output from the corresponding certification
    attempt.  If you have previously executed such a command, then you might
    first want to delete certificate files and other generated files by
    executing the following command.
    
         make clean
    
    Note that when you run `make', then by default, the first error will
    cause the process to stop.  You can use make -i to force `make' to
    ignore errors, thus continuing past them.  Or, use make -k to keep
    going, but skipping certification for any book that includes another
    whose certification has failed.
    
    By default, your acl2-customization file (see *note
    ACL2-CUSTOMIZATION::) is ignored by such `make' commands.  However, you
    can specify the use of an acl2-customization file by setting the value
    of environment variable ACL2_CUSTOMIZATION to the empty string,
    indicating a default such file, or to the desired absolute pathname.
    For example:
    
         make ACL2_CUSTOMIZATION=''
         make ACL2_CUSTOMIZATION='~/acl2-customization.lisp'
    
    We now discuss how to create makefiles to support `make' commands as
    discussed above.
    
    First we give five steps for creating a Makefile to support
    certification of a directory of books, without subdirectories.  For
    examples of such Makefiles you can look in community book directories
    (which, however, might disappear in future versions of ACL2).
    
         1. Include the file Makefile-generic from the books/ subdirectory
         of your ACL2 sources directory, but first perhaps define the
         variable `ACL2'.  Consider the following example.
    
              ACL2 ?= /Users/john_doe/acl2/acl2-sources/saved_acl2
              include /Users/john_doe/acl2/acl2-sources/books/Makefile-generic
    
         In this example, you can omit the first line, because the default
         ACL2 executable is file saved_acl2 in the directory immediately
         above the directory of the specified Makefile-generic file.
         Indeed, that is the common case.  Note the use of ?= instead of =
         or :=, so that ACL2 can instead be defined by the environment or
         provided on the command line as part of the `make' command.
    
         2. (Optional; usually skipped.)  Set the INHIBIT variable if you
         want to see more than the summary output.  For example, if you
         want to see the same output as you would normally see at the
         terminal, put this line in your Makefile after the `include' lines.
    
              INHIBIT = (assign inhibit-output-lst (list (quote proof-tree)))
    
         For other values to use for INHIBIT, see *note
         SET-INHIBIT-OUTPUT-LST:: and see the original setting of INHIBIT
         in books/Makefile-generic.
    
         3. Specify the books to be certified.  Normally, every file with
         extension .lisp will be a book that you want to certify, in which
         case you can skip this step.  Otherwise, put a line in your
         Makefile after the ones above that specifies the books to be
         certified.  The following example, from an old version of
         community books file books/finite-set-theory/osets/Makefile,
         should make this clear.
    
              BOOKS = computed-hints fast instance map membership outer primitives \
                      quantify set-order sets sort
    
         But better yet, use the extension .lsp for any Lisp or ACL2 files
         that are not to be certified, so that the definition of BOOKS can
         be omitted.
    
         4. Create .acl2 files for books that are to be certified in other
         than the initial ACL2 world (see *note PORTCULLIS::).  For
         example, if you look in community books file
         books/arithmetic/equalities.acl2 you will see defpkg forms
         followed by a certify-book command, because it was determined that
         defpkg forms were necessary in the certification world in order to
         certify the equalities book.  In general, for each
         .lisp whose certification requires a non-initial
         certification world, you will need a corresponding
         .acl2 file that ends with the appropriate certify-book
         command.
    
         You also have the option of creating a file cert.acl2 that has a
         special role.  When file .lisp is certified, if there
         is no file .acl2 but there is a file cert.acl2, then
         cert.acl2 will be used as .acl2 would have been used,
         as described in the preceding paragraph, except that the
         appropriate certify-book command will be generated automatically.
         Thus, no certify-book command should occur in cert.acl2.
    
         It is actually allowed to put raw lisp forms in a .acl2 file
         (presumably preceded by :q or (value :q) and followed by (lp)).
         But this is not recommended; we make no guarantees about
         certification performed any time after raw Lisp has been entered
         in the ACL2 session.
    
         5. Generally, the next step is to include the following line after
         the `include' of Makefile-generic (see the first step above).
    
              -include Makefile-deps
    
         This will cause `make' to create and then include a file
         Makefile-deps that contains "dependency" lines needed by `make'.
         If those dependencies are somehow flawed, it may be because you
         have include-book forms that are not truly including books, for
         example in multi-line comments (#|..|#).  These will be ignored if
         preceded by a semicolon (;), or if you add a line break after
         "include-book."  But instead of adding the `-include' line above,
         you can create dependency lines yourself by running the command
    
              make dependencies
    
         and pasting the result into the end of your Makefile, and editing
         as you see fit.
    
    This concludes the basic instructions for creating a Makefile in a
    directory including books.  Here are some other capabilities offered by
    community books file books/Makefile-subdirs.  Not included below is a
    discussion of how to increase parallelism by avoiding the need to
    certify included books before certifying a given book; see *note
    PROVISIONAL-CERTIFICATION::.
    
    *Subdirectory Support*
    
    There is support for using `make' to certify books in subdirectories.
    Consider the following example.
    
         DIRS = pass1 bind-free floor-mod
         include ../Makefile-subdirs
    
    This indicates that we are to run `make' in subdirectories pass1/,
    bind-free/, and floor-mod/ of the current directory.
    
    You can combine this subdirectory support with the support already
    discussed for certifying books in the top-level directory.  Here is an
    example, which as of this writing is in community books file
    books/arithmetic-3/Makefile contains the following lines.
    
         arith-top: top all
         all: top
    
         DIRS = pass1 bind-free floor-mod
         include ../Makefile-subdirs
         include ../Makefile-generic
    
         -include Makefile-deps
    
    The `top' target is defined in ../Makefile-subdirs to call `make' in
    each subdirectory specified in DIRS.  We have set the default target in
    the example above to a new name, arith-top, that makes that top target
    before making the `all' target which, in turn, is the default target in
    any Makefile-generic, and is responsible for certifying books in the
    current directory as discussed in the five steps displayed above.
    
    Use Makefile-psubdirs instead of Makefile-subdirs if certification of a
    book in a subdirectory never depends on certification of a book in a
    different subdirectory, because then the -j option of `make' can allow
    subdirectories to be processed in parallel.
    
    *Cleaning Up*
    
    We note that there is a clean target.  Thus,
    
         make clean
    
    will remove generated files including .cert, .out files, and compiled
    files.
    
    *System Books*
    
    An environment variable ACL2_SYSTEM_BOOKS is generally set
    automatically, so you can probably skip reading the following paragraph
    unless your attempt to certify books fails to locate those books
    properly.
    
    The environment variable ACL2_SYSTEM_BOOKS can be set to the top-level
    directory of the ACL2 community books.  A Unix-style pathname, typically
    ending in books/ or books, is permissible.  In most cases, your ACL2
    executable is a small script in which you can set this environment
    variable just above the line on which the actual ACL2 image is invoked,
    for example:
    
         export ACL2_SYSTEM_BOOKS
         ACL2_SYSTEM_BOOKS=/home/acl2/v3-2/acl2-sources/books
    
    However, you can also set ACL2_SYSTEM_BOOKS as a `make' variable, by
    setting it in your Makefile before the first target definition, e.g.:
    
         ACL2_SYSTEM_BOOKS ?= /home/acl2/v3-2/acl2-sources/books
    
    *Compilation Support*
    
    The file books/Makefile-generic provides support for compiling books
    that are already certified (but see *note COMPILATION:: for an
    exception).  For example, suppose that you have certified books using
    GCL as the host Lisp, resulting in compiled files with the .o
    extension.  Now suppose you would like to compile the books for Allegro
    Common Lisp, whose compiled files have the .fasl extension.  The
    following command will work if you have included books/Makefile-generic
    in your Makefile.
    
         make fasl
    
    In general, the compiled file extension for a Lisp supported by ACL2
    will be a target name for building compiled files for all your books
    (after certifying the books, if not already up-to-date on
    certification).
    
    If you run into problems, you can get help by joining the acl2-help
    email list (follow the link from the ACL2 home page) and sending a
    message to that list.  Also consider trying another version of GNU
    `make'; for example, we have found that versions 3.81 and 3.82
    sometimes cause errors on Linux where version 3.80 does not.
    
    
    File: acl2-doc-emacs.info,  Node: CBD,  Next: CERTIFICATE,  Prev: BOOKS-CERTIFICATION-CLASSIC,  Up: BOOKS
    
    CBD    connected book directory string
    
         Example:
         ACL2 !>:cbd
         "/usr/home/smith/"
    
    The connected book directory is a nonempty string that specifies a
    directory as an absolute pathname.  (See *note PATHNAME:: for a
    discussion of file naming conventions.)  When include-book is given a
    relative book name it elaborates it into a full book name, essentially
    by appending the connected book directory string to the left and
    ".lisp" to the right.  (For details, see *note BOOK-NAME:: and also see
    *note FULL-BOOK-NAME::.)  Furthermore, include-book temporarily sets
    the connected book directory to the directory string of the resulting
    full book name so that references to inferior books in the same
    directory may omit the directory.  See *note SET-CBD:: for how to set
    the connected book directory string.
    
         General Form:
         (cbd)
    
    This is a macro that expands into a term involving the single free
    variable state.  It returns the connected book directory string.
    
    The connected book directory (henceforth called the "cbd") is used by
    include-book to elaborate the supplied book name into a full book name
    (see *note FULL-BOOK-NAME::).  For example, if the cbd is
    "/usr/home/smith/" then the elaboration of the book-name
    "project/task-1/arith" (to the ".lisp" extension) is
    "/usr/home/smith/project/task-1/arith.lisp".  That full-book-name is
    what include-book opens to read the source text for the book.
    
    The cbd may be changed using set-cbd (see *note SET-CBD::).
    Furthermore, during the processing of the events in a book,
    include-book sets the cbd to be the directory string of the
    full-book-name of the book.  Thus, if the cbd is "/usr/home/smith/"
    then during the processing of events by
    
         (include-book "project/task-1/arith")
    
    the cbd will be set to "/usr/home/smith/project/task-1/".  Note that if
    "arith" recursively includes a subbook, say "naturals", that resides on
    the same directory, the include-book event for it may omit the
    specification of that directory.  For example, "arith" might contain
    the event
    
           (include-book "naturals").
    
    In general, suppose we have a superior book and several inferior books
    which are included by events in the superior book.  Any inferior book
    residing on the same directory as the superior book may be referenced
    in the superior without specification of the directory.
    
    We call this a "relative" as opposed to "absolute" naming.  The use of
    relative naming is preferred because it permits books (and their
    accompanying inferiors) to be moved between directories while
    maintaining their certificates and utility.  Certified books that
    reference inferiors by absolute file names are unusable (and rendered
    uncertified) if the inferiors are moved to new directories.
    
    _Technical Note and a Challenge to Users:_
    
    After elaborating the book name to a full book name, include-book opens
    a channel to the file to process the events in it.  In some host Common
    Lisps, the actual file opened depends upon a notion of "connected
    directory" similar to our connected book directory.  Our intention in
    always elaborating book names into absolute filename strings (see *note
    PATHNAME:: for terminology) is to circumvent the sensitivity to the
    connected directory.  But we may have insufficient control over this
    since the ultimate file naming conventions are determined by the host
    operating system rather than Common Lisp (though, we do check that the
    operating system "appears" to be one that we "know" about).  Here is a
    question, which we'll pose assuming that we have an operating system
    that calls itself "Unix."  Suppose we have a file name, filename, that
    begins with a slash, e.g., "/usr/home/smith/...".  Consider two
    successive invocations of CLTL's
    
         (open filename :direction :input)
    
    separated only by a change to the operating system's notion of
    connected directory.  Must these two invocations produce streams to the
    same file?  A candidate string might be something like
    "/usr/home/smith/*/usr/local/src/foo.lisp" which includes some
    operating system-specific special character to mean "here insert the
    connected directory" or, more generally, "here make the name dependent
    on some non-ACL2 aspect of the host's state."  If such "tricky" name
    strings beginning with a slash exist, then we have failed to isolate
    ACL2 adequately from the operating system's file naming conventions.
    Once upon a time, ACL2 did not insist that the cbd begin with a slash
    and that allowed the string "foo.lisp" to be tricky because if one were
    connected to "/usr/home/smith/" then with the empty cbd "foo.lisp" is a
    full book name that names the same file as "/usr/home/smith/foo.lisp".
    If the actual file one reads is determined by the operating system's
    state then it is possible for ACL2 to have two distinct "full book
    names" for the same file, the "real" name and the "tricky" name.  This
    can cause ACL2 to include the same book twice, not recognizing the
    second one as redundant.
    
    
    File: acl2-doc-emacs.info,  Node: CERTIFICATE,  Next: CERTIFY-BOOK,  Prev: CBD,  Up: BOOKS
    
    CERTIFICATE    how a book is known to be admissible and where its defpkgs reside
    
    A book, say "arith", is said to have a "certificate" if there is a file
    named "arith.cert".  Certificates are created by the function
    certify-book and inspected by include-book.  Check sums are used to
    help ensure that certificates are legitimate and that the corresponding
    book has not been modified since certification.  But because the file
    system is insecure and check sums are not perfect it is possible for
    the inclusion of a book to cause inconsistency even though the book
    carries an impeccable certificate.
    
    The certificate includes the version number of the certifying ACL2.  A
    book is considered uncertified if it is included in an ACL2 with a
    different version number.
    
    The presence of a "valid" certificate file for a book attests to two
    things: all of the events of the book are admissible in a certain
    extension of the initial ACL2 logic, and the non-local events of the
    book are independent of the local ones (see *note
    LOCAL-INCOMPATIBILITY::).  In addition, the certificate contains the
    commands used to construct the world in which certification occurred.
    Among those commands, of course, are the defpkgs defining the packages
    used in the book.  When a book is included into a host world, that
    world is first extended by the commands listed in the certificate for
    the book.  Unless that causes an error due to name conflicts, the
    extension ensures that all the packages used by the book are
    identically defined in the host world.
    
    _Security:_
    
    Because the host file system is insecure, there is no way ACL2 can
    guarantee that the contents of a book remain the same as when its
    certificate was written.  That is, between the time a book is certified
    and the time it is used, it may be modified.  Furthermore, certificates
    can be counterfeited.  Check sums (see *note CHECK-SUM::) are used to
    help detect such problems.  But check sums provide imperfect security:
    two different files can have the same check sum.
    
    Therefore, from the strictly logical point of view, one must consider
    even the inclusion of certified books as placing a burden on the user:
    
         The non-erroneous inclusion of a certified book is consistency
         preserving provided (a) the objects read by include-book from the
         certificate were the objects written there by a certify-book and
         (b) the forms read by include-book from the book itself are the
         forms read by the corresponding certify-book.
    
    We say that a given execution of include-book is "certified" if a
    certificate file for the book is present and well-formed and the check
    sum information contained within it supports the conclusion that the
    events read by the include-book are the ones checked by certify-book.
    When an uncertified include-book occurs, warnings are printed or errors
    are caused.  But even if no warning is printed, you must accept burdens
    (a) and (b) if you use books.  These burdens are easier to live with if
    you protect your books so that other users cannot write to them, you
    abstain from running concurrent ACL2 jobs, and you abstain from
    counterfeiting certificates.  But even on a single user uniprocessor,
    you can shoot yourself in the foot by using the ACL2 io primitives to
    fabricate an inconsistent book and the corresponding certificate.
    
    Note that part (a) of the burden described above implies, in
    particular, that there are no guarantees when a certificate is copied.
    When books are renamed (as by copying them), it is recommended that
    their certificates be removed and the books be recertified.  The
    expectation is that recertification will go through without a hitch if
    relative pathnames are used.  See *note PATHNAME::, which is not on the
    guided tour.
    
    Certificates essentially contain two parts, a portcullis and a keep.
    There is a third part, an expansion-alist, in order to record
    expansions if make-event has been used, but the user need not be
    concerned with that level of detail.
    
    See *note PORTCULLIS:: to continue the guided tour through books.
    
    
    File: acl2-doc-emacs.info,  Node: CERTIFY-BOOK,  Next: CERTIFYING-BOOKS,  Prev: CERTIFICATE,  Up: BOOKS
    
    CERTIFY-BOOK    how to produce a certificate for a book
    
         Examples:
         (certify-book "my-arith")          ; certify in a world with 0 commands
         (certify-book "my-arith" 3)        ; ... in a world with 3 commands
         (certify-book "my-arith" ?)        ; ... in a world without checking the
                                              ;     number of commands
         (certify-book "my-arith" 0 nil)    ; ... without compilation
         (certify-book "my-arith" 0 t)      ; ... with compilation (default)
         (certify-book "my-arith" 0 t :ttags (foo))
                                              ; ... allowing trust tag (ttag) foo
         (certify-book "my-arith" 0 t :ttags :all)
                                              ; ... allowing all trust tags (ttags)
         (certify-book "my-arith" t)        ; ... from world of old certificate
         (certify-book "my-arith" 0 nil :acl2x t)
                                              ; ... writing or reading a .acl2x file
    
         General Form:
         (certify-book book-name
                       k                           ; [default 0]
                       compile-flg                 ; [default t]
                       :defaxioms-okp t/nil        ; [default nil]
                       :skip-proofs-okp t/nil      ; [default nil]
                       :ttags ttags                ; [default nil]
                       :acl2x t/nil                ; [default nil]
                       :ttagsx ttags               ; [default nil]
                       :write-port t/nil           ; [default t]
                       :pcert pcert                ; [default nil]
                       )
    
    where book-name is a book name (see *note BOOK-NAME::), k is used to
    indicate your approval of the "certification world," and compile-flg
    can control whether the book is to be compiled.  The defaults for
    compile-flg, skip-proofs-okp, acl2x, write-port, and pcert can be
    affected by environment variables.  All of these arguments are
    described in detail below, except for :pcert.  (We assume below that
    the value of :pcert is nil (and environment variable ACL2_PCERT_ARG is
    unset or the empty string).  For a discussion of this argument, see
    *note PROVISIONAL-CERTIFICATION::.)
    
    Certification occurs in some logical world, called the "certification
    world."  That world must contain the defpkgs needed to read and execute
    the forms in the book.  The commands necessary to recreate that world
    from the ACL2 initial world are called the "portcullis commands," and
    will be copied into the certificate created for the book.  Those
    commands will be re-executed whenever the book is included, to ensure
    that the appropriate packages (and all other names used in the
    certification world) are correctly defined.  The certified book will be
    more often usable if the certification world is kept to a minimal
    extension of the ACL2 initial world (for example, to prevent name
    clashes with functions defined in other books).  Thus, before you call
    certify-book for the first time on a book, you may wish to get into the
    initial ACL2 world (e.g., with :ubt 1 or just starting a new version of
    ACL2), defpkg the desired packages, and then invoke certify-book.
    
    The k argument to certify-book must be either a nonnegative integer or
    else one of the symbols t or ? in any package.  If k is an integer,
    then it must be the number of commands that have been executed after
    the initial ACL2 world to create the world in which certify-book was
    called.  One way to obtain this number is by doing :pbt :start to see
    all the commands back to the first one.
    
    If k is t (or any symbol whose symbol-name is "T"), it means that
    certify-book should use the same world used in the last certification
    of this book.  K may have such a value only if you call certify-book in
    the initial ACL2 world and there is a certificate on file for the book
    being certified.  (Of course, the certificate is probably invalid.)  In
    this case, certify-book reads the old certificate to obtain the
    portcullis commands and executes them to recreate the certification
    world.
    
    Finally, k may be ? (or any symbol whose symbol-name is "?"), in which
    case there is no check made on the certification world.  That is, if k
    is such a value then no action related to the preceding two paragraphs
    is performed, which can be a nice convenience but at the cost of
    eliminating a potentially valuable check that the certification world
    may be as expected.
    
    We next describe the meaning of compile-flg and how it defaults.  If
    explicit compilation has been suppressed by (set-compiler-enabled nil
    state), then compile-flg is coerced to nil; see *note COMPILATION::.
    Otherwise compile-flg may be given the value of t (or :all, which is
    equivalent to t except during provisional certification; see *note
    PROVISIONAL-CERTIFICATION::), indicating that the book is to be
    compiled, or else nil.  (Note that compilation initially creates a
    compiled file with a temporary file name, and then moves that temporary
    file to the final compiled file name obtained by adding a suitable
    extension to the book name.  Thus, a compiled file will appear
    atomically in its intended location.)  Finally, suppose that
    compile-flg is not supplied (or is :default).  If environment variable
    ACL2_COMPILE_FLG is defined and not the empty string, then its value
    should be T, NIL, or ALL after converting to upper case, in which case
    compile-flg is considered to have value t, nil, or :all (respectively).
    Otherwise compile-flg defaults to t.  Note that the value :all is
    equivalent to t except for during the Convert procedure of provisional
    certification; see *note PROVISIONAL-CERTIFICATION::.
    
    Two keyword arguments, :defaxioms-okp and :skip-proofs-okp, determine
    how the system handles the inclusion of defaxiom events and skip-proofs
    events, respectively, in the book.  The value t allows such events, but
    prints a warning message.  The value nil causes an error if such an
    event is found.  Nil is the default unless keyword argument :acl2x t is
    provided and state global 'write-acl2x is a cons (see *note
    SET-WRITE-ACL2X::), in which case the default is t.
    
    The keyword argument :ttags may normally be omitted.  A few constructs,
    used for example if you are building your own system based on ACL2, may
    require it.  See *note DEFTTAG:: for an explanation of this argument.
    
    When book B is certified with value t (the default) for keyword
    argument :write-port, a file B.port is written by certification
    process.  This file contains all of the portcullis commands for B,
    i.e., all user commands present in the ACL2 logical world at the time
    certify-book is called.  if B.lisp later becomes uncertified, say
    because events from that file or an included book have been edited,
    then (include-book "B") will consult B.port to evaluate forms in that
    file before evaluating the events in B.lisp.  On the other hand, B.port
    is ignored when including B if B is certified.
    
    If you use guards, please note certify-book is executed as though
    (set-guard-checking nil) has been evaluated; see *note
    SET-GUARD-CHECKING::.  If you want guards checked, consider using ld
    instead, or in addition; see *note LD::.
    
    For a general discussion of books, see *note BOOKS::.  Certify-book is
    akin to what we have historically called a "proveall": all the forms in
    the book are "proved" to guarantee their admissibility.  More precisely,
    certify-book (1) reads the forms in the book, confirming that the
    appropriate packages are defined in the certification world; (2) does
    the full admissibility checks on each form (proving termination of
    recursive functions, proving theorems, etc.), checking as it goes that
    each form is an embedded event form (see *note EMBEDDED-EVENT-FORM::);
    (3) rolls the world back to the initial certification world and does an
    include-book of the book to check for local incompatibilities (see
    *note LOCAL-INCOMPATIBILITY::); (4) writes a certificate recording not
    only that the book was certified but also recording the commands
    necessary to recreate the certification world (so the appropriate
    packages can be defined when the book is included in other worlds) and
    the check sums of all the books involved (see *note CERTIFICATE::); (5)
    compiles the book if so directed (and then loads the object file in
    that case).  The result of executing a certify-book command is the
    creation of a single new event, which is actually an include-book
    event.  If you don't want its included events in your present world,
    simply execute :ubt :here afterwards.
    
    A utility is provided to assist in debugging failures of certify-book;
    see *note REDO-FLAT::.)
    
    Certify-book requires that the default defun-mode (see *note
    DEFAULT-DEFUN-MODE::) be :logic when certification is attempted.  If
    the mode is not :logic, an error is signalled.
    
    An error will occur if certify-book has to deal with any uncertified
    book other than the one on which it was called.  For example, if the
    book being certified includes another book, that subbook must already
    have been certified.
    
    If you have a certified book that has remained unchanged for some time
    you might well not remember the appropriate defpkgs for it, though they
    are stored in the certificate file and (by default) also in the .port
    file.  If you begin to change the book, don't throw away its certificate
    file just because it has become invalid!  It is an important historical
    document until the book is re-certified.  More important, don't throw
    away the .port file, as it will provide the portcullis commands when
    including the book as an uncertified book; see *note INCLUDE-BOOK::.
    
    When certify-book is directed to produce a compiled file, it calls the
    Common Lisp function compile-file on the original source file.  This
    creates a compiled file with an extension known to ACL2, e.g., if the
    book is named "my-book" then the source file is "my-book.lisp" and the
    compiled file under GCL will be "my-book.o" while under SBCL it will be
    "my-book.fasl".  The compiled file is then loaded.  When include-book
    is used later on "my-book" it will automatically load the compiled
    file, provided the compiled file has a later write date than the source
    file.  The only effect of such compilation and loading is that the
    functions defined in the book execute faster.  See *note GUARD:: for a
    discussion of the issues, and if you want more details about books and
    compilation, see *note BOOK-COMPILED-FILE::.
    
    When certify-book is directed not to produce a compiled file, it will
    delete any existing compiled file for the book, so as not to mislead
    include-book into loading the now outdated compiled file.  Otherwise,
    certify-book will create a temporary "expansion file" to compile,
    obtained by appending the string "@expansion.lsp" to the end of the book
    name.  Remark: Users may ignore that file, which is automatically
    deleted unless state global variable 'save-expansion-file has been set,
    presumably by a system developer, to a non-nil value; see *note
    BOOK-COMPILED-FILE:: for more information about hit issue, including the
    role of environment variable ACL2_SAVE_EXPANSION.
    
    After execution of a certify-book form, the value of
    acl2-defaults-table is restored to what it was immediately before that
    certify-book form was executed.  See *note ACL2-DEFAULTS-TABLE::.
    
    Those who use the relatively advanced features of trust tags (see *note
    DEFTTAG::) and make-event may wish to know how to create a certificate
    file that avoids dependence on trust tags that are used only during
    make-event expansion.  For this, including documentation of the :acl2x
    and :ttagsx keyword arguments for certify-book, see *note
    SET-WRITE-ACL2X::.
    
    This completes the tour through the documentation of books.
    
    
    File: acl2-doc-emacs.info,  Node: CERTIFYING-BOOKS,  Next: COMMUNITY-BOOKS,  Prev: CERTIFY-BOOK,  Up: BOOKS
    
    CERTIFYING-BOOKS    See *note BOOKS-CERTIFICATION::.
    
    
    File: acl2-doc-emacs.info,  Node: COMMUNITY-BOOKS,  Next: FULL-BOOK-NAME,  Prev: CERTIFYING-BOOKS,  Up: BOOKS
    
    COMMUNITY-BOOKS    books contributed by the ACL2 community
    
    For background on ACL2 books, which can contain useful definitions and
    theorems, see *note BOOKS::.
    
    The ACL2 "community books" is a collection of books developed since the
    early 1990s by members of the ACL2 community.  The installation
    instructions suggest installing these books in the books/ subdirectory
    of your local ACL2 installation.  You can contribute books to the ACL2
    community and obtain updates inbetween ACL2 releases by visiting the
    acl2-books project web page, `http://acl2-books.googlecode.com/'.
    
    To certify the community books, see *note REGRESSION::.
    
    
    File: acl2-doc-emacs.info,  Node: FULL-BOOK-NAME,  Next: KEEP,  Prev: COMMUNITY-BOOKS,  Up: BOOKS
    
    FULL-BOOK-NAME    book naming conventions assumed by ACL2
    
    For this discussion we assume that the resident operating system is
    Unix (trademark of AT&T), but analogous remarks apply to other
    operating systems supported by ACL2, in particular, the Macintosh
    operating system where `:' plays roughly the role of `/' in Unix; see
    *note PATHNAME::.
    
    ACL2 defines a "full book name" to be an "absolute filename string,"
    that may be divided into contiguous sections:  a "directory string", a
    "familiar name" and an "extension".  See *note PATHNAME:: for the
    definitions of "absolute," "filename string," and other notions
    pertaining to naming files.  Below we exhibit the three sections of one
    such string:
    
         "/usr/home/smith/project/arith.lisp"
    
         "/usr/home/smith/project/"           ; directory string
                                 "arith"      ; familiar name
                                      ".lisp" ; extension
    
    The sections are marked by the rightmost slash and rightmost dot, as
    shown below.
    
         "/usr/home/smith/project/arith.lisp"
                                 |     |
                                 slash dot
                                 |     |
         "/usr/home/smith/project/"           ; directory string
                                 "arith"      ; familiar name
                                      ".lisp" ; extension
    
    The directory string includes (and terminates with) the rightmost
    slash.  The extension includes (and starts with) the rightmost dot.
    The dot must be strictly to the right of the slash so that the familiar
    name is well-defined and nonempty.
    
    If you are using ACL2 on a system in which file names do not have this
    form, please contact the authors and we'll see what we can do about
    generalizing ACL2's conventions.
    
    
    File: acl2-doc-emacs.info,  Node: KEEP,  Next: PATHNAME,  Prev: FULL-BOOK-NAME,  Up: BOOKS
    
    KEEP    how we know if include-book read the correct files
    
    The certificate (see *note CERTIFICATE:: for general information) of a
    certified file is divided into two parts, a portcullis and a keep.
    These names come from castle lore.  The keep is the strongest and
    usually tallest tower of a castle from which the entire courtyard can
    be surveyed by the defenders.  The keep of a book is a list of file
    names and check sums used after the book has been included, to
    determine if the files read were (up to check sum) those certified.
    
    Once the portcullis is open, include-book can enter the book and read
    the event forms therein.  The non-local event forms are in fact
    executed, extending the host theory.  That may read in other books.
    When that has been finished, the keep of the certificate is inspected.
    The keep is a list of the book names which are included (hereditarily
    through all subbooks) in the certified book (including the certified
    book itself) together with the check sums of the objects in those books
    at the time of certification.  We compare the check sums of the books
    just included to the check sums of the books stored in the keep.  If
    differences are found then we know that the book or one of its subbooks
    has been changed since certification.
    
    See *note INCLUDE-BOOK:: to continue the guided tour through books.
    
    
    File: acl2-doc-emacs.info,  Node: PATHNAME,  Next: PORTCULLIS,  Prev: KEEP,  Up: BOOKS
    
    PATHNAME    introduction to filename conventions in ACL2
    
    The notion of pathname objects from Common Lisp is not supported in
    ACL2, nor is the function pathname.  However, ACL2 supports file
    operations, using conventions for naming files based on those of the
    Unix (trademark of AT&T) operating system, so that the character / is
    used to terminate directory names.  Some file names are "absolute"
    (complete) descriptions of a file or directory; others are "relative"
    to the current working directory or to the connected book directory
    (see *note CBD::).  We emphasize that even for users of Windows-based
    systems or Macintosh computers, ACL2 file names are in the Unix style.
    We will call these _ACL2 pathnames_, often omitting the "ACL2."
    
    Pathnames starting with the directory separator (/) or the tilde
    character (~) are absolute pathnames.  All other pathnames are relative
    pathnames.  An exception is in the Microsoft Windows operating system,
    where it is illegal for the pathname to start with a tilde character
    but the drive may be included, e.g., "c:/home/smith/acl2/book-1.lisp".
    In fact, the drive _must_ be included in the portcullis of a book; see
    *note PORTCULLIS::.  Note also that some host Common Lisps will not
    support pathnames starting with "~", for example ~smith, though ACL2
    will generally support those starting with "~/" regardless of the host
    Common Lisp.
    
    Consider the following examples.  The filename string
    
         "/home/smith/acl2/book-1.lisp"
    
    is an absolute pathname, with top-level directory "home", under that
    the directory "smith" and then the directory "acl2", and finally,
    within that directory the file "book-1.lisp".  If the connected book
    directory is "/home/smith/" (see *note CBD::), then the filename string
    above also corresponds to the relative filename string
    "acl2/book1.lisp".
    
    Finally, we note that (on non-Windows systems) the pathname "~" and
    pathnames starting with "~/" are illegal in books being certified.
    Otherwise, a subsidiary include-book form would have a different
    meaning at certification time than at a later time when the certified
    book is included by a different user.
    
    
    File: acl2-doc-emacs.info,  Node: PORTCULLIS,  Next: PROVISIONAL-CERTIFICATION,  Prev: PATHNAME,  Up: BOOKS
    
    PORTCULLIS    the gate guarding the entrance to a certified book
    
    The certificate (see *note CERTIFICATE:: for general information) of a
    certified file is divided into two parts, a portcullis and a keep.
    These names come from castle lore.  The portcullis of a castle is an
    iron grate that slides up through the ceiling of the tunnel-like
    entrance.  The portcullis of a book ensures that include-book does not
    start to read the book until the appropriate context has been created.
    
    Technically, the portcullis consists of the version number of the
    certifying ACL2, a list of commands used to create the "certification
    world" and an alist specifying the check sums of all the books included
    in that world.  The portcullis is constructed automatically by
    certify-book from the world in which certify-book is called, but that
    world must have certain properties described below.  After listing the
    properties we discuss the issues in a more leisurely manner.
    
    Each command in the portcullis must be either a defpkg form or an
    embedded event form (see *note EMBEDDED-EVENT-FORM::).
    
    Consider a book to be certified.  The book is a file containing event
    forms.  Suppose the file contains references to such symbols as
    my-pkg::fn and acl2-arith::cancel, but that the book itself does not
    create the packages.  Then a hard Lisp error would be caused merely by
    the attempt to read the expressions in the book.  The corresponding
    defpkgs cannot be written into the book itself because the book must be
    compilable and Common Lisp compilers differ on the rules concerning the
    inline definition of new packages.  The only safe course is to make all
    defpkgs occur outside of compiled files.
    
    More generally, when a book is certified it is certified within some
    logical world.  That "certification world" contains not only the
    necessary defpkgs but also, perhaps, function and constant definitions
    and maybe even references to other books.  When certify-book creates
    the certificate for a file it recovers from the certification world the
    commands used to create that world from the initial ACL2 world.  Those
    commands become part of the portcullis for the certified book.  In
    addition, certify-book records in the portcullis the check sums (see
    *note CHECK-SUM::) of all the books included in the certification world.
    
    Include-book presumes that it is impossible even to read the contents
    of a certified book unless the portcullis can be "raised." To raise the
    portcullis we must be able to execute (possibly redundantly, but
    certainly without error), all of the commands in the portcullis and
    then verify that the books thus included were identical to those used
    to build the certification world (up to check sum).  This raising of
    the portcullis must be done delicately since defpkgs are present: we
    cannot even read a command in the portcullis until we have successfully
    executed the previous ones, since packages are being defined.
    
    Clearly, a book is most useful if it is certified in the most
    elementary extension possible of the initial logic.  If, for example,
    your certification world happens to contain a defpkg for "MY-PKG" and
    the function foo, then those definitions become part of the portcullis
    for the book.  Every time the book is included, those names will be
    defined and will have to be either new or redundant (see *note
    REDUNDANT-EVENTS::).  But if those names were not necessary to the
    certification of the book, their presence would unnecessarily restrict
    the utility of the book.
    
    See *note KEEP:: to continue the guided tour of books.
    
    
    File: acl2-doc-emacs.info,  Node: PROVISIONAL-CERTIFICATION,  Next: REGRESSION,  Prev: PORTCULLIS,  Up: BOOKS
    
    PROVISIONAL-CERTIFICATION    certify a book in stages for improved parallelism
    
    Provisional certification is a process that can increase parallelism at
    the system level, typically using `make', when certifying a collection
    of books.  We got this idea from Jared Davis, who developed rudimentary
    provisional certification schemes first at Rockwell Collins and later
    for his `Milawa' project.  Our design has also benefited from
    conversations with Sol Swords.
    
    To invoke provisional certification, see *note BOOKS-CERTIFICATION::.
    For example, you could issue the following command.
    
         ACL2_PCERT=t cert.pl -j 4 `find . -name '*.lisp'`
    
    Alternatively, see *note BOOKS-CERTIFICATION-CLASSIC:: for a discussion
    of classic ACL2 `make'-based certification (which may disappear in a
    future ACL2 release); here we extend those instructions to show how to
    use provisional certification.  (Also, you may wish to look at
    community books file books/system/pcert/Makefile for an example.)  We
    begin by describing a few ways to do that.  A simple way is to add the
    line `ACL2_PCERT=t' to a `make' command that you use for book
    certification, for example as follows.
    
         make -j 4 ACL2_PCERT=t
    
    The following works too, in a bash shell.
    
         (export ACL2_PCERT=t ; time make -j 4)
    
    Alternatively, add the line
    
         ACL2_PCERT ?= t
    
    to the Makefile residing in the directory, placed above the line that
    specifies the `include' of file Makefile-generic.  A successful `make'
    will result in creating the desired certificate (.cert) files.
    
    Warning: If you put the line "ACL2_PCERT ?= t" below the include of
    Makefile-generic, it might have no effect.  For example, try editing
    community books file books/system/pcert/Makefile by moving the line
    "ACL2_PCERT ?= t" to the bottom of the file, and watch "make top.cert"
    fail to invoke provisional certification.
    
    The description above may be sufficient for you to use provisional
    certification.  We provide additional documentation below for the
    reader who wants to understand more about this process, for example
    when not using `make'.  Below we assume prior familiarity with books,
    in particular certify-book and include-book.  The remainder of this
    documentation topic is divided into sections: Summary, Correctness Claim
    and Issues, Combining Pcertify and Convert into Pcertify+, and Further
    Information.
    
    *Summary*
    
    Recall that certification of a book, bk, produces a certificate file
    bk.cert.  The provisional certification process produces this file as
    well, but as the last of the following three steps.  All of these steps
    are carried out by calls of certify-book using its :pcert keyword
    argument.  We typically call these steps "procedures", to distinguish
    them from the steps of an individual call of certify-book.
    
         o The "Pcertify" procedure (sometimes called the "Create"
         procedure) is invoked by calling certify-book with keyword argument
         :pcert :create.  It produces a file bk.pcert0, sometimes called the
         ".pcert0" file (pronounced "dot pee cert zero").  Proofs are
         skipped during this procedure, which can be viewed as an elaborate
         syntax check, augmented by compilation if specified (as it is by
         default).
    
         o The "Convert" procedure is invoked by calling certify-book with
         keyword argument :pcert :convert.  It creates file bk.pcert1 from
         bk.pcert0, by doing proofs for all events in bk.lisp.  Note that
         the third argument (the `compile-flg' argument) is ignored by such
         a call of certify-book unless its value is :all (either explicitly
         or by way of environment variable ACL2_COMPILE_FLG).  A subtlety
         is that if there is a compiled file at least as recent as the
         corresponding .pcert0 file, then that compiled file's write date
         will be updated to the current time at the end of this procedure.
         The usual local-incompatibility check at the end of certify-book
         is omitted for the Convert procedure, since it was performed
         towards the end of the Create procedure.
    
         o The "Complete" procedure is invoked by calling certify-book with
         keyword argument :pcert :complete.  It checks that every included
         book (including every one that is locally included) has a .cert
         file that is at least as recent as the corresponding book.  The
         effect is to move bk.pcert1 to bk.cert.  Note that all arguments
         of certify-book other than the :pcert argument are ignored for
         this procedure, other than for some trivial argument checking.
    
    You can combine the Pcertify and Convert procedures into a single
    procedure, Pcertify+, which may be useful for books that contain
    expensive include-book events but do few proofs.  We defer discussion of
    that feature to the section below, "Combining Pcertify and Convert into
    Pcertify+".
    
    The main idea of provisional certification is to break sequential
    dependencies caused by include-book, that is, so that a book's proofs
    are carried out even when it includes books (sometimes called
    "sub-books") that have not themselves been fully certified.  For
    example, suppose that a proof development consists of books A.lisp,
    B.lisp, and C.lisp, where file A.lisp contains the form (include-book
    "B") and file B.lisp contains the form (include-book "C").  Normally
    one would first certify C, then B, and finally, A.  However, the
    provisional certification process can speed up the process on a
    multi-core machine, as follows: the Pcertify (pronounced "pee certify")
    procedure respects this order but (one hopes) is fast since proofs are
    skipped; the Convert procedure essentially completes the certification
    in parallel by doing proofs and creating .pcert1 files based on .pcert0
    files; and the Complete procedure respects book order when quickly
    renaming .pcert1 files to .cert files.  In our example, the steps would
    be as follows, but note that we need not finish all Pcertify steps
    before starting some Convert steps, nor need we finish all Convert
    steps before starting some Complete steps, as explained further below.
    
         o Pcertify books "C", "B",and then "A", sequentially, skipping
         proofs but doing compilation.
    
         o Do proofs in parallel for the books using the Convert procedure,
         where each book relies on the existence of its own .pcert0 file as
         well as a .cert, .pcert0, or .pcert1 file for each of its included
         sub-books.  Write out a .pcert1 file for the book when the proof
         succeeds.
    
         o Rename the .pcert1 file to a corresponding .cert file when a
         .cert file exists and is up-to-date for each included book.
    
    The Convert step can begin for bk.lisp any time after bk.pcert0 is
    built.  The Complete step can begin for this book any time after
    bk.pcert1 and every sub-bk.cert are built, for sub-bk a sub-book of bk.
    
    The new procedures -- Pcertify, Convert, and Complete -- are invoked by
    supplying a value for the keyword argument :pcert of certify-book,
    namely :create, :convert, or :complete, respectively.  Typically, and
    by default, the compile-flg argument of certify-book is t for the
    Pcertify procedure, so that compilation can take full advantage of
    parallelism.  This argument is treated as nil for the other procedures
    except when its value is :all in the Convert procedure, as mentioned
    above.
    
    For those who use make-event, we note that expansion is done in the
    Pcertify procedure; the later steps use the expansion resulting from
    that procedure.  The reason is that although a call of make-event is
    similar to a macro call, a difference is that the expansion of a
    make-event form can depend on the state.  Therefore, we insist on doing
    such an expansion only once, so that all books involved agree on the
    expansion.  We say more about make-event below.
    
    *Correctness Claim and Issues*
    
    The Basic Claim for certification is the same whether or not the
    provisional certification process is employed: all books should be
    certified from scratch, with no files written to the directories of the
    books except by ACL2.  Moreover, no trust tags should be used (see
    *note DEFTTAG::), or else it is the responsibility of the user to
    investigate every occurrence of "TTAG NOTE" that is printed to standard
    output.
    
    But common practice is to certify a set of books in stages: certify a
    few books, fix some books, re-certify changed books, certify some more
    books, and so on.  In practice, we expect this process to be sound even
    though it does not meet the preconditions for the Basic Claim above.
    In particular, we expect that the use of checksums in certificates will
    make it exceedingly unlikely that a book is still treated as certified
    after any events in the book or any sub-book, or any portcullis
    commands of the book or any sub-book, have been modified.
    
    Provisional certification makes it a bit easier for a determined user to
    subvert correctness.  For example, the Complete procedure only checks
    write dates to ensure that each sub-book's .cert file is no older than
    the corresponding .lisp file, but it does not look inside .cert files of
    sub-books; in particular it does not look at their checksum
    information.  Of course, the automatic dependency analysis provided by
    classic ACL2 `make'-based certification avoids accidental problems of
    this sort.  And, checksum information will indeed be applied at
    include-book time, at least for sub-books included non-locally.
    
    In short: while we believe that the provisional certification process
    can be trusted, we suggest that for maximum trust, it is best for all
    books in a project to be certified from scratch without the provisional
    certification process.
    
    *Combining Pcertify and Convert into Pcertify+*
    
    You can combine the Pcertify and Convert procedure into a single
    procedure, Pcertify+, which may be useful for books that contain
    expensive include-book events but do few proofs.  If you are using
    `make' to do provisional certification as described above, just set
    `make' variable ACL2_BOOKS_PCERT_ARG_T to the list of books for which
    you want the Pcertify+ procedure performed instead of separate Pcertify
    and Convert procedures.  Either of two common methods may be used to
    set this variable, as illustrated below for the case that books
    sub.lisp and mid.lisp are the ones on which you want Pcertify+
    performed.  One method is to add the following to your directory's
    Makefile, above the include of Makefile-generic.
    
         ACL2_BOOKS_PCERT_ARG_T = sub mid
    
    Alternatively, you can specify the desired books on the command line,
    for example as follows.
    
         make -j 4 ACL2_BOOKS_PCERT_ARG_T='sub mid'
    
    Note that the books are given without their .lisp extensions.
    
    At the ACL2 level, the Pcertify+ procedure is performed when the value t
    is supplied to the :pcert keyword argument of certify-book.  Thus,
    :pcert t can be thought of as a combination of :pcert :create and
    :pcert :convert.  However, what ACL2 actually does is to perform the
    Pcertify step without skipping proofs, and at the end of the
    certify-book run, it writes out both the .pcert0 and .pcert1 file, with
    essentially the same contents.  (We say "essentially" because the
    implementation writes :PCERT-INFO :PROVED to the end of the .pcert0
    file, but not to the .pcert1 file.)
    
    *Further Information*
    
    Some errors during provisional certification cannot be readily solved.
    For example, if there are circular directory dependencies (for example,
    some book in directory D1 includes some book in directory D2 and
    vice-versa), then classic ACL2 `make'-based certification will quite
    possibly fail.  For another example, perhaps your directory's Makefile
    is awkward to convert to one with suitable dependencies.  When no fix
    is at hand, it might be best simply to avoid provisional certification.
    If you are using classic ACL2 `make'-based certification, you can
    simply add the following line to your directory's Makefile, or use
    "ACL2_PCERT= " on the `make' command line, to avoid provisional
    certification.
    
         override ACL2_PCERT =
    
    We invite anyone who has troubleshooting tips to contact the ACL2
    developers with suggestions for adding such tips to this section.
    
    Our next remark is relevant only to users of make-event, and concerns
    the interaction of that utility with state global ld-skip-proofsp.
    Normally, the global value of ld-skip-proofsp is unchanged during
    make-event expansion, except that it is bound to nil when the
    make-event form has a non-nil :check-expansion argument.  But during
    the Pcertify procedure (not the Pcertify+ procedure), ld-skip-proofsp
    is always bound to nil at the start of make-event expansion.  To see
    why, consider for example the community book
    books/make-event/proof-by-arith.lisp.  This book introduces a macro,
    proof-by-arith, that expands to a call of make-event.  This make-event
    form expands by trying to prove a given theorem using a succession of
    included arithmetic books, until the proof succeeds.  Now proofs are
    skipped during the Pcertify procedure, and if proofs were also skipped
    during make-event expansion within that procedure, the first arithmetic
    book's include-book form would always be saved because the theorem's
    proof "succeeded" (as it was skipped!).  Of course, the theorem's proof
    could then easily fail during the Convert step.  If you really want to
    inhibit proofs during make-event expansion in the Pcertify step,
    consider using a form such as the following: (state-global-let*
    ((ld-skip-proofsp nil)) ...).
    
    Finally, we describe what it means for there to be a valid certificate
    file for including a certified book.  Normally, this is a file with
    extension .cert.  However, if that .cert file does not exist, then ACL2
    looks for a .pcert0 file instead; and if that also does not exist, it
    looks for a .pcert1 file.  (To see why does the .pcert0 file take
    priority over the .pcert1 file, note that the Convert procedure copies
    a .pcert0 file to a .pcert1 file, so both might exist -- but the
    .pcert1 file might be incomplete if copying is in progress.)  Once the
    candidate certificate file is thus selected, it must be valid in order
    for the book to be considered certified (see *note CERTIFICATE::).  For
    the certificate file as chosen above, then in order for a compiled file
    to be loaded, it must be at least as recent as that certificate file.
    
    Again, as discussed above, a .pcert0 or .pcert1 file may serve as a
    valid certificate file when the .cert file is missing.  But when that
    happens, a warning may be printed that a "provisionally certified" book
    has been included.  No such warning occurs if environment variable
    ACL2_PCERT has a non-empty value, or if that warning is explicitly
    inhibited (see *note SET-INHIBIT-WARNINGS:: and see *note
    SET-INHIBIT-OUTPUT-LST::).
    
    
    File: acl2-doc-emacs.info,  Node: REGRESSION,  Next: SET-CBD,  Prev: PROVISIONAL-CERTIFICATION,  Up: BOOKS
    
    REGRESSION    See *note BOOKS-CERTIFICATION::.
    
    
    File: acl2-doc-emacs.info,  Node: SET-CBD,  Next: UNCERTIFIED-BOOKS,  Prev: REGRESSION,  Up: BOOKS
    
    SET-CBD    to set the connected book directory
    
         Example Forms:
         ACL2 !>:set-cbd "/usr/home/smith/"
         ACL2 !>:set-cbd "my-acl2/books"
    
    See *note CBD:: for a description of the connected book directory.
    
         General Form:
         (set-cbd str)
    
    where str is a nonempty string that represents the desired directory
    (see *note PATHNAME::).  This command sets the connected book directory
    (see *note CBD::) to the string representing the indicated directory.
    Thus, this command may determine which files are processed by
    include-book and certify-book commands typed at the top-level.
    However, the cbd is also temporarily set by those two book processing
    commands.
    
    IMPORTANT:  Pathnames in ACL2 are in the Unix (trademark of AT&T)
    style.  That is, the character "/" separates directory components of a
    pathname, and pathnames are absolute when they start with this
    character, and relative otherwise.  See *note PATHNAME::.
    
    
    File: acl2-doc-emacs.info,  Node: UNCERTIFIED-BOOKS,  Prev: SET-CBD,  Up: BOOKS
    
    UNCERTIFIED-BOOKS    invalid certificates and uncertified books
    
    For relevant background see *note BOOKS::, see *note CERTIFICATE::, and
    see *note PORTCULLIS::.
    
    Include-book has a special provision for dealing with an uncertified
    book, i.e., a file with no certificate or an invalid certificate (i.e.,
    one whose check sums describe files other than the ones actually read).
    In this case, a warning is printed and the book is otherwise processed
    much as though it were certified and had an open portcullis.
    
    If a book B.lisp is uncertified and a file B.port exists, then the
    forms in B.port are evaluated before the forms in B.lisp.  Such a file
    B.port is typically created calling certify-book on book "B" with
    argument :write-port t, so that B.port contains the portcullis commands
    for B (the commands present in the world when that certification was
    attempted).
    
    Inclusion of uncertified books can be handy, but it can have disastrous
    consequences.
    
    The provision allowing uncertified books to be included can have
    disastrous consequences, ranging from hard lisp errors, to damaged
    memory, to quiet logical inconsistency.
    
    It is possible for the inclusion of an uncertified book to render the
    logic inconsistent.  For example, one of its non-local events might be
    (defthm t-is-nil (equal t nil)).  It is also possible for the inclusion
    of an uncertified book to cause hard errors or breaks into raw Common
    Lisp.  For example, if the file has been edited since it was certified,
    it may contain too many open parentheses, causing Lisp to read past
    "end of file." Similarly, it might contain non-ACL2 objects such as
    3.1415 or ill-formed event forms that cause ACL2 code to break.
    
    Even if a book is perfectly well formed and could be certified (in a
    suitable extension of ACL2's initial world), its uncertified inclusion
    might cause Lisp errors or inconsistencies!  For example, it might
    mention packages that do not exist in the host world, especially if the
    .port file (discussed above) does not exist from an earlier
    certification attempt.  The portcullis of a certified book ensures that
    the correct defpkgs have been admitted, but if a book is read without
    actually raising its portcullis, symbols in the file, e.g.,
    acl2-arithmetic::fn, could cause "unknown package" errors in Common
    Lisp.  Perhaps the most subtle disaster occurs if the host world does
    have a defpkg for each package used in the book but the host defpkg
    imports different symbols than those required by the portcullis.  In
    this case, it is possible that formulas which were theorems in the
    certified book are non-theorems in the host world, but those formulas
    can be read without error and will then be quietly assumed.
    
    In short, if you include an uncertified book, *all bets are off*
    regarding the validity of the future behavior of ACL2.
    
    That said, it should be noted that ACL2 is pretty tough and if errors
    don't occur, the chances are that deductions after the inclusion of an
    uncertified book are probably justified in the (possibly inconsistent)
    logical extension obtained by assuming the admissibility and validity
    of the definitions and conjectures in the book.
    
    
    File: acl2-doc-emacs.info,  Node: BREAK-REWRITE,  Next: DOCUMENTATION,  Prev: BOOKS,  Up: Top
    
    BREAK-REWRITE    the read-eval-print loop entered to monitor rewrite rules
    
    ACL2 allows the user to monitor the application of rewrite rules.  When
    monitored rules are about to be tried by the rewriter, an interactive
    break occurs and the user is allowed to watch and, in a limited sense,
    control the attempt to apply the rule.  This interactive loop, which is
    technically just a call of the standard top-level ACL2 read-eval-print
    loop, ld, on a "wormhole state" (see *note WORMHOLE::), is called
    "break-rewrite."  While in break-rewrite, certain keyword commands are
    available for accessing information about the context in which the
    lemma is being tried.  These keywords are called break-rewrite
    "commands."
    
    Also see *note DMR:: (Dynamically Monitor Rewrites) for a related
    utility, which allows you to watch progress of the rewriter in real
    time.
    
    To abort from inside break-rewrite at any time, execute
    
    For further information, see the related :doc topics listed below.
    
    * Menu:
    
    * BREAK-LEMMA:: a quick introduction to breaking rewrite rules in ACL2
    
    * BRR:: to enable or disable the breaking of rewrite rules
    
    * BRR-COMMANDS:: Break-Rewrite Commands
    
    * BRRatsign:: (BRR@) to access context sensitive information within break-rewrite
    
    * MONITOR:: to monitor the attempted application of a rule name
    
    * MONITORED-RUNES:: print the monitored runes and their break conditions
    
    * OK-IF:: conditional exit from break-rewrite
    
    * UNMONITOR:: to stop monitoring a rule name
    
    
    Related topics other than immediate subtopics:
    * DMR:: dynamically monitor rewrites and other prover activity
    
    As explained in the documentation for monitor, it is possible to cause
    the ACL2 rewriter to monitor the attempted application of selected
    rules.  When such a rule is about to be tried, the rewriter evaluates
    its break condition and if the result is non-nil, break-rewrite is
    entered.
    
    Break-rewrite permits the user to inspect the current state by
    evaluating break-rewrite commands.  Type :help in break-rewrite to see
    what the break-rewrite commands are.  However, break-rewrite is actually
    just a call of the general ACL2 read-eval-print loop, ld, on a certain
    state and the break-rewrite commands are simply aliases provided by
    ld-keyword-aliases table (see *note LD-KEYWORD-ALIASES::).  See *note
    LD:: for details about this read-eval-print loop.  Thus, with a few
    exceptions, anything you can do at the ACL2 top-level can be done
    within break-rewrite.  For example, you can evaluate arbitrary
    expressions, use the keyword command hack, access documentation, print
    events, and even define functions and prove theorems.  However, the
    "certain state" upon which ld was called is a "wormhole state" (see
    *note WORMHOLE::) because break-rewrite is not allowed to have any
    effect upon the behavior of rewrite.  What this means, very roughly but
    understandably, is that break-rewrite operates on a copy of the state
    being used by rewrite and when break-rewrite exits the wormhole closes
    and the state "produced" by break-rewrite disappears.  Thus,
    break-rewrite lets you query the state of the rewriter and even do
    experiments involving proofs, etc., but these experiments have no
    effect on the ongoing proof attempt.  In particular:
    
    Note that the output from break-rewrite is sometimes abbreviated by
    default, such as for the term causing the break.  This can be
    controlled by setting the :term evisc-tuple; see *note
    SET-EVISC-TUPLE::.  (Another option: use iprinting.  See *note
    SET-IPRINT::.)  But as noted above, if you use set-evisc-tuple from
    inside the break-rewrite wormhole, its effect will disappear when you
    exit the break.  So you might want to issue a set-evisc-tuple command
    from the top level, outside break-rewrite.
    
    When you first enter break-rewrite a simple herald is printed such as:
    
         (3 Breaking (:rewrite lemma12) on (delta a (+ 1 j)):
    
    The integer after the open parenthesis indicates the depth of nested
    break-rewrite calls.  In this discussion we use 3 consistently for this
    integer.  Unless you abort or somehow enter unbalanced parentheses into
    the script, the entire session at a given depth will be enclosed in
    balanced parentheses, making it easy to skip over them in Emacs.
    
    You then will see the break-rewrite prompt:
    
         3 ACL2 !>
    
    The leading integer is, again, the depth.  Because breaks often occur
    recursively it is convenient always to know the level with which you
    are interacting.
    
    You may type arbitrary commands as in the top-level ACL2 loop.  For
    example, you might type:
    
         3 ACL2 !>:help
    
    or
    
         3 ACL2 !>:pe lemma12
    
    More likely, upon entering break-rewrite you will determine the context
    of the attempted application.  Here are some useful commands:
    
         3 ACL2 >:target           ; the term being rewritten
         3 ACL2 >:unify-subst      ; the unifying substitution
         3 ACL2 >:path             ; the stack of goals pursued by the rewriter
                                   ; starting at the top-level clause being simplified
                                   ; and ending with the current application
    
    At this point in the interaction the system has not yet tried to apply
    the monitored rule.  That is, it has not tried to establish the
    hypotheses, considered the heuristic cost of backchaining, rewritten
    the right-hand side of the conclusion, etc.  When you are ready for it
    to try the rule you can type one of several different "proceed"
    commands.  The basic proceed commands are :ok, :go, and :eval.
    
         :ok
    
    exits break-rewrite without further interaction.  When break-rewrite
    exits it prints "3)", closing the parenthesis that opened the level 3
    interaction.
    
         :go
    
    exits break-rewrite without further interaction, but prints out the
    result of the application attempt, i.e., whether the application
    succeeded, if so, what the :target term was rewritten to, and if not
    why the rule was not applicable.
    
         :eval
    
    causes break-rewrite to attempt to apply the rule but interaction at
    this level of break-rewrite resumes when the attempt is complete.  When
    control returns to this level of break-rewrite a message indicating the
    result of the application attempt (just as in :go) is printed, followed
    by the prompt for additional user input.
    
    Generally speaking, :ok and :go are used when the break in question is
    routine or uninteresting and :eval is used when the break is one that
    the user anticipates is causing trouble.  For example, if you are
    trying to determine why a lemma isn't being applied to a given term and
    the :target of the current break-rewrite is the term in question, you
    would usually :eval the rule and if break-rewrite reports that the rule
    failed then you are in a position to determine why, for example by
    carefully inspecting the :type-alist of governing assumptions or why
    some hypothesis of the rule could not be established.
    
    It is often the case that when you are in break-rewrite you wish to
    change the set of monitored runes.  This can be done by using :monitor
    and :unmonitor as noted above.  For example, you might want to monitor
    a certain rule, say hyp-reliever, just when it is being used while
    attempting to apply another rule, say main-lemma.  Typically then you
    would monitor main-lemma at the ACL2 top-level, start the
    proof-attempt, and then in the break-rewrite in which main-lemma is
    about to be tried, you would install a monitor on hyp-reliever.  If
    during the ensuing :eval hyp-reliever is broken you will know it is
    being used under the attempt to apply main-lemma.
    
    However, once hyp-reliever is being monitored it will be monitored even
    after main-lemma has been tried.  That is, if you let the proof attempt
    proceed then you may see many other breaks on hyp-reliever, breaks that
    are not "under" the attempt to apply main-lemma.  One way to prevent
    this is to :eval the application of main-lemma and then :unmonitor
    hyp-reliever before exiting.  But this case arises so often that ACL2
    supports several additional "flavors" of proceed commands.
    
    :Ok!, :go!, and :eval! are just like their counterparts (:ok, :go, and
    :eval, respectively), except that while processing the rule that is
    currently broken no runes are monitored.  When consideration of the
    current rule is complete, the set of monitored runes is restored to its
    original setting.
    
    :Ok$, :go$, and :eval$ are similar but take an additional argument
    which must be a list of runes.  An example usage of :eval$ is
    
         3 ACL2 !>:eval$ ((:rewrite hyp-reliever))
    
    These three commands temporarily install unconditional breaks on the
    runes listed, proceed with the consideration of the currently broken
    rule, and then restore the set of monitored rules to its original
    setting.
    
    Thus, there are nine ways to proceed from the initial entry into
    break-rewrite although we often speak as though there are two, :ok and
    :eval, and leave the others implicit.  We group :go with :ok because in
    all their flavors they exit break-rewrite without further interaction
    (at the current level).  All the flavors of :eval require further
    interaction after the rule has been tried.
    
    To abort a proof attempt and return to the top-level of ACL2 you may at
    any time type (a!) followed by a carriage return.  If you are not in a
    raw Lisp break, you may type :a! instead.  The utility p! is completely
    analogous to a! except that it pops up only one ld level.  If you have
    just entered the break-rewrite loop, this will pop you out of that loop,
    back to the proof.  See *note A!:: and see *note P!::.
    
    We now address ourselves to the post-:eval interaction with
    break-rewrite.  As noted, that interaction begins with break-rewrite's
    report on the results of applying the rule: whether it worked and
    either what it produced or why it failed.  This information is also
    printed by certain keyword commands available after :eval, namely
    :wonp, :rewritten-rhs, and :failure-reason.  In addition, by using brr@
    (see *note BRR@: BRRatsign.) you can obtain this information in the
    form of ACL2 data objects.  This allows the development of more
    sophisticated "break conditions"; see *note MONITOR:: for examples.  In
    this connection we point out the macro form (ok-if term).  See *note
    OK-IF::.  This command exits break-rewrite if term evaluates to non-nil
    and otherwise does not exit.  Thus it is possible to define macros that
    provide other kinds of exits from break-rewrite.  The only way to exit
    break-rewrite after :eval is :ok (or, equivalently, the use of ok-if).
    
    ACL2 users who wish to know more about break-rewrite so that they can
    develop more convenient ways to monitor rules are encouraged to speak
    to J Moore.
    
    The rest of this documentation discusses a few implementation details
    of break-rewrite and may not be interesting to the typical user.
    
    There is no ACL2 function named break-rewrite.  It is an illusion
    created by appropriate calls to two functions named brkpt1 and brkpt2.
    As previously noted, break-rewrite is ld operating on a wormhole state.
    One might therefore wonder how break-rewrite can apply a rule and then
    communicate the results back to the rewriter running in the external
    state.  The answer is that it cannot.  Nothing can be communicated
    through a wormhole.  In fact, brkpt1 and brkpt2 are each calls of ld
    running on wormhole states.  Brkpt1 implements the pre-:eval
    break-rewrite and brkpt2 implements the post-:eval break-rewrite.  The
    rewriter actually calls brkpt1 before attempting to apply a rule and
    calls brkpt2 afterwards.  In both cases, the rewriter passes into the
    wormhole the relevant information about the current context.  Logically
    brkpt1 and brkpt2 are no-ops and rewrite ignores the nil they return.
    But while control is in them the execution of rewrite is suspended and
    cannot proceed until the break-rewrite interactions complete.
    
    This design causes a certain anomoly that might be troubling.  Suppose
    that inside break-rewrite before :evaling a rule (i.e., in the brkpt1
    wormhole state) you define some function, foo.  Suppose then you :eval
    the rule and eventually control returns to break-rewrite (i.e., to
    brkpt2 on a wormhole state with the results of the application in it).
    You will discover that foo is no longer defined!  That is because the
    wormhole state created during your pre-:eval interaction is lost when
    we exit the wormhole to resume the proof attempt.  The post-:eval
    wormhole state is in fact identical to the initial pre-:eval state
    (except for the results of the application) because rewrite did not
    change the external state and both wormhole states are copies of it.  A
    similar issue occurs with the use of trace utilities: all effects of
    calling trace$ and untrace$ are erased when you proceed from a break in
    the break-rewrite loop.
    
    There is a lot more to know about break-rewrite, most of which is
    fairly easy to learn from looking at the code, since it is all
    expressed in ACL2.  Feel free to ask questions of J Moore.
    
    acl2-sources/doc/EMACS/acl2-doc-emacs.info-30000664002132200015000000110520412222333541017637 0ustar  kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from
    acl2-doc-emacs.texinfo.
    
    This is documentation for ACL2 Version 6.3
    Copyright (C) 2013  University of Texas at Austin
    
    This program is free software; you can redistribute it and/or modify
    it under the terms of Version 2 of the GNU General Public License as
    published by the Free Software Foundation.
    
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    
    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    
    Written by:  Matt Kaufmann and J Strother Moore
    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.
    
    INFO-DIR-SECTION Math
    START-INFO-DIR-ENTRY
    * acl2: (acl2-doc-emacs). Applicative Common Lisp
    END-INFO-DIR-ENTRY
    
    
    File: acl2-doc-emacs.info,  Node: BREAK-LEMMA,  Next: BRR,  Prev: BREAK-REWRITE,  Up: BREAK-REWRITE
    
    BREAK-LEMMA    a quick introduction to breaking rewrite rules in ACL2
    
         Example:
         :brr t                          ; if you haven't done that yet
         :monitor (:rewrite lemma12) t   ; to install a break point on the
                                         ; rule named (:rewrite lemma12)
    
    ACL2 does not support Nqthm's break-lemma but supports a very similar
    and more powerful break facility.  Suppose some proof is failing;
    apparently some particular rule is not being used and you wish to learn
    why.  Then you need the ACL2 break-rewrite facility.  See *note
    BREAK-REWRITE:: and all of its associated :doc topics for details.  The
    following basic steps are required.
    
    (1) To enable the "break rewrite" feature, you must first execute
    
         ACL2 !>:brr t
    
    at the top-level of ACL2.  Equivalently, evaluate (brr t).
    Break-rewrite stays enabled until you disable it with (brr nil).  When
    break-rewrite is enabled the ACL2 rewriter will run slower than normal
    but you will be able to monitor the attempts to apply specified rules.
    
    (2) Decide what runes (see *note RUNE::) you wish to monitor.  For
    example, you might want to know why (:rewrite lemma12 . 2) is not being
    used in the attempted proof.  That, by the way, is the name of the
    second rewrite rule generated from the event named lemma12.
    
    The command
    
         ACL2 !>:monitor (:rewrite lemma12 . 2) t
    
    will install an "unconditional" break point on that rule.  The "t" at
    the end of the command means it is unconditional, i.e., a break will
    occur every time the rule is tried.  ACL2 supports conditional breaks
    also, in which case the t is replaced by an expression that evaluates
    to non-nil when you wish for a break to occur.  See *note MONITOR::.
    The above keyword command is, of course, equivalent to
    
         ACL2 !>(monitor '(:rewrite lemma12 . 2) t)
    
    which you may also type.  You may install breaks on as many rules as
    you wish.  You must use monitor on each rule.  You may also change the
    break condition on a rule with monitor.  Use unmonitor (see *note
    UNMONITOR::) to remove a rule from the list of monitored rules.
    
    (3) Then try the proof again.  When a monitored rule is tried by the
    rewriter you will enter an interactive break, called break-rewrite.
    See *note BREAK-REWRITE:: for a detailed description.  Very simply,
    break-rewrite lets you inspect the context of the attempted application
    both before and after the attempt.  When break-rewrite is entered it
    will print out the "target" term being rewritten.  If you type :go
    break-rewrite will try the rule and then exit, telling you (a) whether
    the rule was applied, (b) if so, how the target was rewritten, and (c)
    if not, why the rule failed.  There are many other commands.  See *note
    BRR-COMMANDS::.
    
    (4) When you have finished using the break-rewrite feature you should
    disable it to speed up the rewriter.  You can disable it with
    
         ACL2 !>:brr nil
    
    The list of monitored rules and their break conditions persists but is
    ignored.  If you enable break-rewrite later, the list of monitored
    rules will be displayed and will be used again by rewrite.
    
    You should disable the break-rewrite feature whenever you are not
    intending to use it, even if the list of monitored rules is empty,
    because the rewriter is slowed down as long as break-rewrite is enabled.
    
    If you get a stack overflow, see *note CW-GSTACK::.
    
    
    File: acl2-doc-emacs.info,  Node: BRR,  Next: BRR-COMMANDS,  Prev: BREAK-LEMMA,  Up: BREAK-REWRITE
    
    BRR    to enable or disable the breaking of rewrite rules
    
         Example:
         :brr t       ; enable
         :brr nil     ; disable
    
         General Form:
         (brr flg)
    
    where flg evaluates to t or nil.  This function modifies state so that
    the attempted application of certain rewrite rules are "broken." "Brr"
    stands for "break-rewrite" and can be thought of as a mode with two
    settings.  The normal mode is "disabled."
    
    When brr mode is "enabled" the ACL2 rewriter monitors the attempts to
    apply certain rules and advises the user of those attempts by entering
    an interactive wormhole break.  From within this break the user can
    watch selected application attempts.  See *note BREAK-REWRITE::.  The
    user can also interact with the system during brr breaks via
    brr-commands.
    
    The rules monitored are selected by using the monitor and unmonitor
    commands.  It is possible to break a rune "conditionally" in the sense
    that an interactive break will occur only if a specified predicate is
    true of the environment at the time of the attempted application.  See
    *note MONITOR:: and see *note UNMONITOR::.
    
    Even if a non-empty set of rules has been selected, no breaks will occur
    unless brr mode is enabled.  Thus, the first time in a session that you
    wish to monitor a rewrite rule, use :brr t to enable brr mode.
    Thereafter you may select runes to be monitored with monitor and
    unmonitor with the effect that whenever monitored rules are tried (and
    their break conditions are met) an interactive break will occur.  Be
    advised that when brr mode is enabled the rewriter is somewhat slower
    than normal.  Furthermore, that sluggishness persists even if no runes
    are monitored.  You may regain normal performance -- regardless of what
    runes are monitored -- by disabling brr mode with :brr nil.
    
    Why isn't brr mode disabled automatically when no runes are monitored?
    More generally, why does ACL2 have brr mode at all?  Why not just test
    whether there are monitored runes?  If you care about the answers, see
    *note WHY-BRR::.
    
    BRR Mode and Console Interrupts: If the system is operating in brr mode
    and you break into raw Lisp (as by causing a console interrupt or
    happening upon a signalled Lisp error; see *note BREAKS::), you can
    return to the ACL2 top-level, outside any brr environment, by executing
    (abort!).  Otherwise, the normal way to quit from such a break (for
    example :q in GCL, :reset in Allegro CL, and q in CMU CL) will return
    to the innermost ACL2 read-eval-print loop, which may or may not be the
    top-level of your ACL2 session!  In particular, if the break happens to
    occur while ACL2 is within the brr environment (in which it is
    preparing to read brr-commands), the abort will merely return to that
    brr environment.  Upon exiting that environment, normal theorem proving
    is continued (and the brr environment may be entered again in response
    to subsequent monitored rule applications).  Before returning to the brr
    environment, ACL2 "cleans up" from the interrupted brr processing.
    However, it is not possible (given the current implementation) to clean
    up perfectly.  This may have two side-effects.  First, the system may
    occasionally print the self-explanatory "Cryptic BRR Message 1" (or 2),
    informing you that the system has attempted to recover from an aborted
    brr environment.  Second, it is possible that subsequent brr behavior
    in that proof will be erroneous because the cleanup was done
    incorrectly.  The moral is that you should not trust what you learn
    from brr if you have interrupted and aborted brr processing during the
    proof.  These issues do not affect the behavior or soundness of the
    theorem prover.
    
    
    File: acl2-doc-emacs.info,  Node: BRR-COMMANDS,  Next: BRRatsign,  Prev: BRR,  Up: BREAK-REWRITE
    
    BRR-COMMANDS    Break-Rewrite Commands
    
         :a!             abort to ACL2 top-level
         :p!             pop one level (exits a top-level break-rewrite loop)
         :target         term being rewritten
         :unify-subst    substitution making :lhs equal :target
         :hyps           hypotheses of the rule
         :hyp i          ith hypothesis of the rule
         :lhs            left-hand side of rule's conclusion
         :rhs            right-hand side of rule's conclusion
         :type-alist     type assumptions governing :target
         :initial-ttree  ttree before :eval (see *note TTREE::)
         :ancestors      negations of backchaining hypotheses being pursued
         :wonp           indicates if application succeed (after :eval)
         :rewritten-rhs  rewritten :rhs (after :eval)
         :final-ttree    ttree after :eval (see *note TTREE::)
         :failure-reason reason rule failed (after :eval)
         :path           rewrite's path from top clause to :target
         :frame i        ith frame in :path
         :top            top-most frame in :path
         :ok             exit break
         :go             exit break, printing result
         :eval           try rule and re-enter break afterwards
         :ok!            :ok but no recursive breaks
         :go!            :go but no recursive breaks
         :eval!          :eval but no recursive breaks
         :ok$ runes      :ok with runes monitored during recursion
         :go$ runes      :go with runes monitored during recursion
         :eval$ runes    :eval with runes monitored during recursion
         :help           this message
         :standard-help  :help message from ACL2 top-level
    
    Break-rewrite is just a call of the standard ACL2 read-eval-print loop,
    ld, on a "wormhole" state.  Thus, you may execute most commands you
    might normally execute at the top-level of ACL2.  However, all state
    changes you cause from within break-rewrite are lost when you exit or
    :eval the rule.  You cannot modify stobjs from within the break.  See
    *note BREAK-REWRITE:: for more details and see *note LD:: for general
    information about the standard ACL2 read-eval-print loop.
    
    
    File: acl2-doc-emacs.info,  Node: BRRatsign,  Next: MONITOR,  Prev: BRR-COMMANDS,  Up: BREAK-REWRITE
    
    BRR@    to access context sensitive information within break-rewrite
    
         Example:
         (brr@ :target)      ; the term being rewritten
         (brr@ :unify-subst) ; the unifying substitution
    
         General Form:
         (brr@ :symbol)
    
    where :symbol is one of the following keywords.  Those marked with *
    probably require an implementor's knowledge of the system to use
    effectively.  They are supported but not well documented.  More is said
    on this topic following the table.
    
         :symbol             (brr@ :symbol)
         -------             ---------------------
    
         :target             the term to be rewritten.  This term is an
                             instantiation of the left-hand side of the
                             conclusion of the rewrite-rule being broken.
                             This term is in translated form!  Thus, if
                             you are expecting (equal x nil) -- and your
                             expectation is almost right -- you will see
                             (equal x 'nil); similarly, instead of (cadr a)
                             you will see (car (cdr a)).  In translated
                             forms, all constants are quoted (even nil, t,
                             strings and numbers) and all macros are
                             expanded.
    
         :unify-subst        the substitution that, when applied to :target,
                             produces the left-hand side of the rule being
                             broken.  This substitution is an alist pairing
                             variable symbols to translated (!) terms.
    
         :wonp               t or nil indicating whether the rune was
                             successfully applied.  (brr@ :wonp) returns
                             nil if evaluated before :EVALing the rule.
    
         :rewritten-rhs      the result of successfully applying the rule
                             or else nil if (brr@ :wonp) is nil.  The result
                             of successfully applying the rule is always a
                             translated (!) term and is never nil.
    
         :failure-reason     some non-nil lisp object indicating why the rule
                             was not applied or else nil.  Before the rule is
                             :EVALed, (brr@ :failure-reason) is nil.  After
                             :EVALing the rule, (brr@ :failure-reason) is nil
                             if (brr@ :wonp) is t.  Rather than document the
                             various non-nil objects returned as the failure
                             reason, we encourage you simply to evaluate
                             (brr@ :failure-reason) in the contexts of interest.
                             Alternatively, study the ACL2 function tilde-@-
                             failure-reason-phrase.
    
         :lemma           *  the rewrite rule being broken.  For example,
                             (access rewrite-rule (brr@ :lemma) :lhs) will
                             return the left-hand side of the conclusion
                             of the rule.
    
         :type-alist      *  a display of the type-alist governing :target.
                             Elements on the displayed list are of the form
                             (term type), where term is a term and type
                             describes information about term assumed to hold
                             in the current context.  The type-alist may be
                             used to determine the current assumptions, e.g.,
                             whether A is a CONSP.
    
         :ancestors       *  a stack of frames indicating the backchain history
                             of the current context.  The theorem prover is in
                             the process of trying to establish each hypothesis
                             in this stack.  Thus, the negation of each hypothesis
                             can be assumed false.  Each frame also records the
                             rules on behalf of which this backchaining is being
                             done and the weight (function symbol count) of the
                             hypothesis.  All three items are involved in the
                             heuristic for preventing infinite backchaining.
                             Exception:  Some frames are ``binding hypotheses''
                             (equal var term) or (equiv var (double-rewrite term))
                             that bind variable var to the result of rewriting
                             term.
    
         :gstack          *  the current goal stack.  The gstack is maintained
                             by rewrite and is the data structure printed as the
                             current ``path.''  Thus, any information derivable
                             from the :path brr command is derivable from gstack.
                             For example, from gstack one might determine that
                             the current term is the second hypothesis of a
                             certain rewrite rule.
    
    In general brr@-expressions are used in break conditions, the
    expressions that determine whether interactive breaks occur when
    monitored runes are applied.  See *note MONITOR::.  For example, you
    might want to break only those attempts in which one particular term is
    being rewritten or only those attempts in which the binding for the
    variable a is known to be a consp.  Such conditions can be expressed
    using ACL2 system functions and the information provided by brr@.
    Unfortunately, digging some of this information out of the internal
    data structures may be awkward or may, at least, require intimate
    knowledge of the system functions.  But since conditional expressions
    may employ arbitrary functions and macros, we anticipate that a set of
    convenient primitives will gradually evolve within the ACL2 community.
    It is to encourage this evolution that brr@ provides access to the *'d
    data.
    
    
    File: acl2-doc-emacs.info,  Node: MONITOR,  Next: MONITORED-RUNES,  Prev: BRRatsign,  Up: BREAK-REWRITE
    
    MONITOR    to monitor the attempted application of a rule name
    
         Example:
         (monitor '(:rewrite assoc-of-app) 't)
         :monitor (:rewrite assoc-of-app) t
         :monitor (:definition app) (equal (brr@ :target) '(app c d))
    
         General Form:
         (monitor rune term)
    
    where rune is a rune and term is a term, called the "break condition."
    Rune must be either a :rewrite rune or a :definition rune.
    
    When a rune is monitored any attempt to apply it may result in an
    interactive break in an ACL2 "wormhole state." There you will get a
    chance to see how the application proceeds.  See *note BREAK-REWRITE::
    for a description of the interactive loop entered.  Whether an
    interactive break occurs depends on the value of the break condition
    expression associated with the monitored rune.
    
    NOTE: Some :rewrite rules are considered "simple abbreviations"; see
    *note SIMPLE::.  These can be be monitored, but only at certain times
    during the proof.  Monitoring is carried out by code inside the
    rewriter but abbreviation rules may be applied by a special purpose
    simplifier inside the so-called _preprocess_ phase of a proof.  If you
    desire to monitor an abbreviation rule, a warning will be printed
    suggesting that you may want to supply the hint :DO-NOT '(PREPROCESS);
    see *note HINTS::.  Without such a hint, an abbreviation rule can be
    applied during the preprocess phase of a proof, and no such application
    will cause an interactive break.
    
    To remove a rune from the list of monitored runes, use unmonitor.  To
    see which runes are monitored and what their break conditions are,
    evaluate (monitored-runes).
    
    Monitor, unmonitor and monitored-runes are macros that expand into
    expressions involving state.  While these macros appear to return the
    list of monitored runes this is an illusion.  They all print monitored
    rune information to the comment window and then return error triples
    (see *note ERROR-TRIPLES::) instructing ld to print nothing.  It is
    impossible to return the list of monitored runes because it exists only
    in the wormhole state with which you interact when a break occurs.
    This allows you to change the monitored runes and their conditions
    during the course of a proof attempt without changing the state in
    which the the proof is being constructed.
    
    Unconditional break points are obtained by using the break condition t.
    We now discuss conditional break points.  The break condition, expr,
    must be a term that contains no free variables other than state and
    that returns a single non-state result.  In fact, the result should be
    nil, t, or a true list of commands to be fed to the resulting
    interactive break.  Whenever the system attempts to use the associated
    rule, expr is evaluated in the wormhole interaction state.  A break
    occurs only if the result of evaluating expr is non-nil.  If the result
    is a true list, that list is appended to the front of standard-oi and
    hence is taken as the initial user commands issued to the interactive
    break.
    
    In order to develop effective break conditions it must be possible to
    access context sensitive information, i.e., information about the
    context in which the monitored rune is being tried.  The brr@ macro may
    be used in break conditions to access such information as the term
    being rewritten and the current governing assumptions.  This
    information is not stored in the proof state but is transferred into
    the wormhole state when breaks occur.  The macro form is (brr@ :sym)
    where :sym is one of several keyword symbols, including :target (the
    term being rewritten), :unify-subst (the substitution that instantiates
    the left-hand side of the conclusion of the rule so that it is the
    target term), and :type-alist (the governing assumptions).  See *note
    BRR@: BRRatsign.
    
    For example,
    
         ACL2 !>:monitor (:rewrite assoc-of-app)
                         (equal (brr@ :target) '(app a (app b c)))
    
    will monitor (:rewrite assoc-of-app) but will cause an interactive
    break only when the target term, the term being rewritten, is (app a
    (app b c)).
    
    Because break conditions are evaluated in the interaction environment,
    the user developing a break condition for a given rune can test
    candidate break conditions before installing them.  For example,
    suppose an unconditional break has been installed on a rune, that an
    interactive break has occurred and that the user has determined both
    that this particular application is uninteresting and that many more
    such applications will likely occur.  An appropriate response would be
    to develop an expression that recognizes such applications and returns
    nil.  Of course, the hard task is figuring out what makes the current
    application uninteresting.  But once a candidate expression is
    developed, the user can evaluate it in the current context simply to
    confirm that it returns nil.
    
    Recall that when a break condition returns a non-nil true list that
    list is appended to the front of standard-oi.  For example,
    
         ACL2 !>:monitor (:rewrite assoc-of-app) '(:go)
    
    will cause (:rewrite assoc-of-app) to be monitored and will make the
    break condition be '(:go).  This break condition always evaluates the
    non-nil true list (:go).  Thus, an interactive break will occur every
    time (:rewrite assoc-of-app) is tried.  The break is fed the command
    :go.  Now the command :go causes break-rewrite to (a) evaluate the
    attempt to apply the lemma, (b) print the result of that attempt, and
    (c) exit from the interactive break and let the proof attempt continue.
    Thus, in effect, the above :monitor merely "traces" the attempted
    applications of the rune but never causes an interactive break
    requiring input from the user.
    
    It is possible to use this feature to cause a conditional break where
    the effective break condition is tested *after* the lemma has been
    tried.  For example:
    
         ACL2 !>:monitor (:rewrite lemma12)
                         '(:unify-subst
                           :eval$ nil
                           :ok-if (or (not (brr@ :wonp))
                                      (not (equal (brr@ :rewritten-rhs) '(foo a))))
                           :rewritten-rhs)
    
    causes the following behavior when (:rewrite lemma12) is tried.  A
    break always occurs, but it is fed the commands above.  The first,
    :unify-subst, causes break-rewrite to print out the unifying
    substitution.  Then in response to :eval$ nil the lemma is tried but
    with all runes temporarily unmonitored.  Thus no breaks will occur
    during the rewriting of the hypotheses of the lemma.  When the attempt
    has been made, control returns to break-rewrite (which will print the
    results of the attempt, i.e., whether the lemma was applied, if so what
    the result is, if not why it failed).  The next command, the :ok-if
    with its following expression, is a conditional exit command.  It means
    exit break-rewrite if either the attempt was unsuccessful, (not (brr@
    :wonp)), or if the result of the rewrite is any term other than (foo
    a).  If this condition is met, the break is exited and the remaining
    break commands are irrelevant.  If this condition is not met then the
    next command, :rewritten-rhs, prints the result of the application
    (which in this contrived example is known to be (foo a)).  Finally, the
    list of supplied commands is exhausted but break-rewrite expects more
    input.  Therefore, it begins prompting the user for input.  The end
    result, then, of the above :monitor command is that the rune in
    question is elaborately traced and interactive breaks occur whenever it
    rewrites its target to (foo a).
    
    We recognize that the above break condition is fairly arcane.  We
    suspect that with experience we will develop some useful idioms.  For
    example, it is straightforward now to define macros that monitor runes
    in the ways suggested by the following names:  trace-rune,
    break-if-target-is, and break-if-result-is.  For example, the last
    could be defined as
    
         (defmacro break-if-result-is (rune term)
           `(monitor ',rune
                     '(quote (:eval :ok-if
                                    (not (equal (brr@ :rewritten-rhs) ',term))))))
    
    (Note however that the submitted term must be in translated form.)
    
    Since we don't have any experience with this kind of control on lemmas
    we thought it best to provide a general (if arcane) mechanism and hope
    that the ACL2 community will develop the special cases that we find
    most convenient.
    
    
    File: acl2-doc-emacs.info,  Node: MONITORED-RUNES,  Next: OK-IF,  Prev: MONITOR,  Up: BREAK-REWRITE
    
    MONITORED-RUNES    print the monitored runes and their break conditions
    
         Example and General Form:
         :monitored-runes
    
    This macro prints a list, each element of which is of the form (rune
    expr), showing each monitored rune and its current break condition.
    
    
    File: acl2-doc-emacs.info,  Node: OK-IF,  Next: UNMONITOR,  Prev: MONITORED-RUNES,  Up: BREAK-REWRITE
    
    OK-IF    conditional exit from break-rewrite
    
         Example Form:
         :ok-if (null (brr@ :wonp))
    
         General Form:
         :ok-if expr
    
    where expr is a term involving no free variables other than state and
    returning one non-state result which is treated as Boolean.  This form
    is intended to be executed from within break-rewrite (see *note
    BREAK-REWRITE::).
    
    Consider first the simple situation that the (ok-if term) is a command
    read by break-rewrite.  Then, if the term is non-nil, break-rewrite
    exits and otherwise it does not.
    
    More generally, ok-if returns an ACL2 error triple (mv erp val state).
    (See *note LD:: or see *note PROGRAMMING-WITH-STATE:: for more on error
    triples.)  If any form being evaluated as a command by break-rewrite
    returns the triple returned by (ok-if term) then the effect of that
    form is to exit break-rewrite if term is non-nil.  Thus, one might
    define a function or macro that returns the value of ok-if expressions
    on all outputs and thus create a convenient new way to exit
    break-rewrite.
    
    The exit test, term, generally uses brr@ to access context sensitive
    information about the attempted rule application.  See *note BRR@:
    BRRatsign.  Ok-if is useful inside of command sequences produced by
    break conditions.  See *note MONITOR::.  :ok-if is most useful after an
    :eval command has caused break-rewrite to try to apply the rule because
    in the resulting break environment expr can access such things as
    whether the rule succeeded, if so, what term it produced, and if not,
    why.  There is no need to use :ok-if before :evaling the rule since the
    same effects could be achieved with the break condition on the rule
    itself.  Perhaps we should replace this concept with
    :eval-and-break-if?  Time will tell.
    
    
    File: acl2-doc-emacs.info,  Node: UNMONITOR,  Prev: OK-IF,  Up: BREAK-REWRITE
    
    UNMONITOR    to stop monitoring a rule name
    
         Examples:
         (unmonitor '(:rewrite assoc-of-app))
         :unmonitor (:rewrite assoc-of-app)
         :unmonitor :all
    
         General Forms:
         (unmonitor rune)
         (unmonitor :all)
    
    Here, rune is a rune that is currently among those with break points
    installed.  This function removes the break.
    
    Subtle point:  Because you may want to unmonitor a "rune" that is no
    longer a rune in the current ACL2 world, we don't actually check this
    about rune.  Instead, we simply check that rune is a consp beginning
    with a keywordp.  That way, you'll know you've made a mistake if you
    try to :unmonitor binary-append instead of :unmonitor (:definition
    binary-append), for example.
    
    
    File: acl2-doc-emacs.info,  Node: DOCUMENTATION,  Next: EVENTS,  Prev: BREAK-REWRITE,  Up: Top
    
    DOCUMENTATION    functions that display documentation
    
    This section explains the ACL2 online documentation system.  Thus, most
    of it assumes that you are typing at the terminal, inside an ACL2
    session.  If you are reading this description in another setting (for
    example, in a web browser, in Emacs info, or on paper), simply ignore
    the parts of this description that involve typing at the terminal.
    
    ACL2 users are welcome to contribute additional documentation.  See the
    web page `http://www.cs.utexas.edu/users/moore/acl2/contrib/'.
    
    For an introduction to the ACL2 online documentation system, type :more
    below.  Whenever the documentation system concludes with "(type :more
    for more, :more! for the rest)" you may type :more to see the next
    block of documentation.
    
    Topics related to documentation are documented individually:
    
    * Menu:
    
    * *TERMINAL-MARKUP-TABLE*:: a markup table used for printing to the terminal
    
    * ARGS:: args, guard, type, constraint, etc., of a function symbol
    
    * DOC:: brief documentation (type :doc name)
    
    * DOC!:: all the documentation for a name (type :doc! name)
    
    * DOC-STRING:: formatted documentation strings
    
    * DOCS:: available documentation topics (by section)
    
    * HELP:: brief survey of ACL2 features
    
    * MARKUP:: the markup language for ACL2 documentation strings
    
    * MORE:: your response to :doc or :more's ``(type :more...)''
    
    * MORE!:: another response to ``(type :more for more, :more! for the rest)''
    
    * MORE-DOC:: a continuation of the :doc documentation
    
    * NQTHM-TO-ACL2:: ACL2 analogues of Nqthm functions and commands
    
    To view the documentation in a web browser, open a browser to file
    doc/HTML/acl2-doc.html under your ACL2 source directory, or just go to
    the ACL2 home page at `http://www.cs.utexas.edu/users/moore/acl2/'.
    
    Alternatively, follow a link on the ACL2 home page to a manual, known
    as the xdoc manual, which incorporates (but rearranges) the ACL2
    documentation as well as documentation from many ACL2 community books.
    You can build a local copy of that manual; see for example the section
    "BUILDING THE XDOC MANUAL" in the community books Makefile for
    instructions.
    
    To use Emacs Info (inside Emacs), first load distributed file
    emacs/emacs-acl2.el (perhaps inside your .emacs file) and then execute
    meta-x acl2-info.  In order to see true links to external web pages,
    you may find the following addition to your .emacs file to be helpful.
    
         ; For emacs-version 22 or (presumably) later, you can probably set
         ; arrange that in Emacs Info, URLs become links, in the sense that
         ; if you hit  while standing on a URL, then you will be
         ; taken to that location in a web browser.  If this does not happen
         ; automatically, then evaluating the `setq' form below might work
         ; if you have firefox.  If that does not work, then you can probably
         ; figure out what to do as follows.  First type
         ;   control-h v browse-url-browser-function
         ; and then from the resulting help page,
         ; hit  on the link ``customize'' in:
         ; ``You can customize this variable''
         ; and then follow instructions.
         (setq browse-url-browser-function (quote browse-url-firefox))
    
    There is a print version of the documentation, though we recommend
    using one of the other methods (web, Emacs Info, or online) to browse
    it.  If you really want the print version, you can find it here:
    `http://www.cs.utexas.edu/users/moore/publications/acl2-book.ps.gz'.
    
    Below we focus on how to access the online documentation, but some of
    the discussion is relevant to other formats.
    
    The ACL2 online documentation feature allows you to see extensive
    documentation on many ACL2 functions and ideas.  You may use the
    documentation facilities to document your own ACL2 functions and
    theorems.
    
    If there is some name you wish to know more about, then type
    
         ACL2 !>:doc name
    
    in the top-level loop.  If the name is documented, a brief blurb will
    be printed.  If the name is not documented, but is "similar" to some
    documented names, they will be listed.  Otherwise, nil is returned.
    
    Every name that is documented contains a one-line description, a few
    notes, and some details.  :Doc will print the one-liner and the notes.
    When :doc has finished it stops with the message "(type :more for more,
    :more! for the rest)" to remind you that details are available.  If you
    then type
    
         ACL2 !>:more
    
    a block of the continued text will be printed, again concluding with
    "(type :more for more, :more! for the rest)" if the text continues
    further, or concluding with "*-" if the text has been exhausted.  By
    continuing to type :more until exhausting the text you can read
    successive blocks.  Alternatively, you can type :more! to get all the
    remaining blocks.
    
    If you want to get the details and don't want to see the elementary
    stuff typed by :doc name, type:
    
         ACL2 !>:MORE-DOC name
    
    We have documented not just function names but names of certain
    important ideas too.  For example, see *note REWRITE:: and see *note
    META:: to learn about :rewrite rules and :meta rules, respectively.
    See *note HINTS:: to learn about the structure of the :hints argument
    to the prover.  The deflabel event (see *note DEFLABEL::) is a way to
    introduce a logical name for no reason other than to attach
    documentation to it; also see *note DEFDOC::.
    
    How do you know what names are documented?  There is a documentation
    database which is querried with the :docs command.
    
    The documentation database is divided into sections.  The sections are
    listed by
    
         ACL2 !>:docs *
    
    Each section has a name, sect, and by typing
    
         ACL2 !>:docs sect
    
    or equivalently
    
         ACL2 !>:doc sect
    
    you will get an enumeration of the topics within that section.  Those
    topics can be further explored by using :doc (and :more) on them.  In
    fact the section name itself is just a documented name.  :more
    generally gives an informal overview of the general subject of the
    section.
    
         ACL2 !>:docs **
    
    will list all documented topics, by section.  This fills several pages
    but might be a good place to start.
    
    If you want documentation on some topic, but none of our names or brief
    descriptions seem to deal with that topic, you can invoke a command to
    search the text in the database for a given string.  This is like the
    GNU Emacs "apropos" command.
    
         ACL2 !>:docs "functional inst"
    
    will list every documented topic whose :doc or :more-doc text includes
    the substring "functional inst", where case and the exact number of
    spaces are irrelevant.
    
    If you want documentation on an ACL2 function or macro and the
    documentation database does not contain any entries for it, there are
    still several alternatives.
    
         ACL2 !>:args fn
    
    will print the arguments and some other relevant information about the
    named function or macro.  This information is all gleaned from the
    definition (not from the documentation database) and hence this is a
    definitive way to determine if fn is defined as a function or macro.
    
    You might also want to type:
    
         ACL2 !>:pc fn
    
    which will print the command which introduced fn.  You should see *note
    COMMAND-DESCRIPTOR:: for details on the kinds of input you can give the
    :pc command.
    
    The entire ACL2 documentation database is user extensible.  That is, if
    you document your function definitions or theorems, then that
    documentation is made available via the database and its query commands.
    
    The implementation of our online documentation system makes use of
    Common Lisp's "documentation strings." While Common Lisp permits a
    documentation string to be attached to any defined concept, Common Lisp
    assigns no interpretation to these strings.  ACL2 attaches special
    significance to documentation strings that begin with the characters
    ":Doc-Section".  When such a documentation string is seen, it is stored
    in the database and may be displayed via :doc, :more, :docs, etc.  Such
    documentation strings must follow rigid syntactic rules to permit their
    processing by our commands.  These are spelled out elsewhere; see *note
    DOC-STRING::.
    
    A description of the structure of the documentation database may also
    be found; see *note DOC-STRING::.
    
    Finally: To build the HTML documentation, proceed with the following
    sequence of steps.
    
         1. In the doc/ subdirectory of the ACL2 distribution, start ACL2
         and then evaluate (certify-book "write-acl2-html").
    
         2. Exit ACL2 and start it up again (or, evaluate :u).
    
         3. Include the documented books within your ACL2 loop using
         include-book.
    
         4. Evaluate (include-book "../doc/write-acl2-html" :dir :system).
    
         5. Call macro write-html-file, following the instructions at the
         end of distributed file doc/write-acl2-html.lisp.
    
    
    
    File: acl2-doc-emacs.info,  Node: *TERMINAL-MARKUP-TABLE*,  Next: ARGS,  Prev: DOCUMENTATION,  Up: DOCUMENTATION
    
    *TERMINAL-MARKUP-TABLE*    a markup table used for printing to the terminal
    
    The value of the ACL2 constant *terminal-markup-table* is an association
    list pairing markup keys with strings, to be used for printing to the
    terminal.  See *note MARKUP:: for a description of the ACL2 markup
    language.
    
    The entries in *terminal-markup-table* are of the form
    
         (key flag . fmt-string)
    
    where key is one of the doc-string tilde directives (see *note
    MARKUP::), flag is a Boolean as described below, and fmt-string is a
    string as expected by the ACL2 printing function fmt.  The system
    arranges that for any arg, when an expression ~key[arg] is encountered
    by the documentation printer, fmt will print fmt-string in an
    association list, binding keys based on arg as follows.
    
         #\p --- the `pointer'     ; only used if flag is t
         #\s --- the print name version of the pointer, e.g., |abc| or ABC
         #\c --- the parent file   ; only used if flag is t
         #\t --- the displayed text
         #\T --- uppercased displayed text
    
    The first three entries are used only when the flag associated with key
    is t, indicating that the argument arg of ~key is to be parsed as
    starting with a symbol; for example, ~key[foo bar] will bind #\p to the
    symbol FOO.
    
    The discussion of the above association list for printing fmt-string
    applies when printing documentation to other than the terminal as well.
    Such tools exist for Texinfo and for HTML; see files
    doc/write-acl2-html.lisp and doc/write-acl2-texinfo.lisp distributed
    with ACL2.
    
    
    File: acl2-doc-emacs.info,  Node: ARGS,  Next: DOC,  Prev: *TERMINAL-MARKUP-TABLE*,  Up: DOCUMENTATION
    
    ARGS    args, guard, type, constraint, etc., of a function symbol
    
         Example:
         :args assoc-eq
    
    Args takes one argument, a symbol which must be the name of a function
    or macro, and prints out the formal parameters, the guard expression,
    the output signature, the deduced type, the constraint (if any), and
    whether documentation about the symbol is available via :doc.
    
    
    File: acl2-doc-emacs.info,  Node: DOC,  Next: DOC!,  Prev: ARGS,  Up: DOCUMENTATION
    
    DOC    brief documentation (type :doc name)
    
    NOTE: The :doc command only makes sense at the terminal.  Most users
    will probably access the ACL2 documentation in other ways; see *note
    DOCUMENTATION::.  In particular, consider using the xdoc manual at the
    following location on the web, for topics documented in ACL2 community
    books as well as the ACL2 system (though the latter are rearranged):
    
    `http://fv.centtech.com/acl2/latest/doc/'
    
         Examples:
         ACL2 !>:doc DEFTHM          ; print documentation of DEFTHM
         ACL2 !>:doc logical-name    ; print documentation of LOGICAL-NAME
         ACL2 !>:doc "MY-PKG"        ; print documentation of "MY-PKG"
    
         Related Topics:
         :more                      ; continues last :doc or :more-doc text
         :more-doc name             ; prints more documentation for name
         :docs **                   ; lists all documented symbols
         :docs "compil"             ; documented symbols apropos "compil"
         :DOC documentation         ; describes how documentation works
    
         General Form:
         ACL2>:doc logical-name
    
    where logical-name is a logical name (see *note LOGICAL-NAME::) for
    which you hope there is documentation.  Chances are there is no
    documentation at the moment, but we are working on adding documentation
    strings for all the user level ACL2 functions.
    
    For a general discussion of our treatment of documentation strings, see
    *note DOCUMENTATION::.
    
    This is the first cut at online documentation.  Users can be
    particularly helpful by sending mail on the inadequacies of the system.
    Address it just to Moore and put Documentation in the subject line.
    There are several things that trouble me about what I've done here.
    
    First, many concepts aren't documented.  Ultimately, I'd like to .
    document (a) every CLTL primitive (e.g., case and coerce) and (b) every
    ACL2 extension (e.g., aref1 and getprop).  But so far I have focussed
    on documenting (c) the ACL2 system primitives (e.g., defthm and what
    hints look like).  My priorities are (c), then (b), and then (a),
    following the philosophy that the most unstable features should get
    online documentation in these early releases.  Having gotten the basic
    documentation in place, I'll document new things as they are added, and
    in response to your pleas I'll try to add documentation to old things
    that are widely regarded as important.
    
    Second, I worry that the existing documentation is unhelpful because it
    provides too much or too little detail, or it provides the detail too
    far away from where it is needed.  Please be on the lookout for this.
    Did you get what you needed when you appealed to :doc or :more-doc?  If
    not, what was it you needed?  Would more cross-references help?  Did
    you get lost in maze of cross-references?
    
    
    File: acl2-doc-emacs.info,  Node: DOC!,  Next: DOC-STRING,  Prev: DOC,  Up: DOCUMENTATION
    
    DOC!    all the documentation for a name (type :doc! name)
    
    NOTE:  The :doc! command only makes sense at the terminal.
    
         Examples:
         ACL2 !>:doc! defthm
         ACL2 !>:doc! certificate
    
    This command is like :doc name followed by :more!.  It prints all the
    documentation of name.
    
    
    File: acl2-doc-emacs.info,  Node: DOC-STRING,  Next: DOCS,  Prev: DOC!,  Up: DOCUMENTATION
    
    DOC-STRING    formatted documentation strings
    
         Examples:
         ":Doc-Section name
         one-liner~/notes~/details"
    
         ":Doc-Section name
         one-liner~/
         notes~/
         details~/
         :cite old-name1
         :cited-by old-name2"
    
    Use (get-doc-string 'name state) to see other examples.
    
    Documentation strings not beginning with ":Doc-Section" (case is
    irrelevant) are ignored.  See *note MARKUP:: for how to supply
    formatting information (such as fonts and displayed text) in
    documentation strings.
    
    ACL2 attaches special importance to documentation strings beginning
    with the header ":Doc-Section" (or any variant thereof obtained by
    changing case).  Any documentation string that does not begin with such
    a header is considered unformatted and is ignored.  For the rest of
    this discussion, we use the phrase "documentation string" as though it
    read "formatted documentation string."
    
    Documentation strings are always processed in the context of some
    symbol, name, being defined.  (Indeed, if an event defines no symbol,
    e.g., verify-guards or in-theory, then it is not permitted to have a
    formatted documentation string.)  The string will be associated with
    name in the "documentation database." The database is divided into
    "sections" and each section is named by a symbol.  Among the sections
    are events, documentation, history, other, and miscellaneous.  A
    complete list of the sections may be obtained by typing :docs * at the
    terminal.  You can create new sections.  The main purpose of sections
    is simply to partition the large set of names into smaller subsets
    whose contents can be enumerated separately.  The idea is that the user
    may remember (or recognize) the relevant section name and then read its
    contents to find interesting items.
    
    Within a section are "documentation tuples" which associate with each
    documented name its documentation string and a list of related
    documented names, called the "related names" of the name.  When :doc
    prints the documentation for name, it always lists the related names.
    
    When a formatted documentation string is submitted with the defining
    event of some name, the section name and an initial set of related
    names are parsed from the string.  In addition, the formatted string
    contains various "levels" of detail that are printed out at different
    times.  Finally, it is possible for a string to cause the newly
    documented name to be added to the related names of any previously
    documented name.  Thus, as new names are introduced they can be grouped
    with old ones.
    
    The general form of an ACL2 formatted documentation string is
    
         ":DOC-SECTION 
           ~/
           ~/
           
    ~/ :CITE ... :CITE :CITED-BY ... :CITED-BY " Before we explain this, let it be noted that (get-doc-string name state) will return the documentation string associated with name in the documentation database. You may want to call get-doc-string on 'pe and 'union-theories just to see some concrete documentation strings. This documentation string, which is rather long, is under 'doc-string. A formatted documentation string has five parts: the header and section-name (terminating in the first #\Newline), the , , and
    (each terminating in a tilde-slash ("~/") pair), and a citation part. These five parts are parsed into six components. is read as the name of a symbol, section-name. , , and
    are arbitrary sequences of characters (ignoring initial white space and not including the tilde-slash pairs which terminate them). The are read as symbols and assembled into a list called the "cite" symbols. The are read as symbols and assembled into a list called the "cited-by" symbols. See the warning below regarding the hackish nature of our symbol reader. Section-name must either be a previously documented symbol or else be name, the symbol being documented. To open a new section of the database, named section-name, you should define the logical name section-name (as by deflabel or any other event; also see *note DEFDOC::) and attach to it a documentation string for section section-name. You might wish to print out the documentation string we use for some of our section names, e.g., (get-doc-string 'events state). By forcing section names to be documented symbols, we permit sections themselves to have one line descriptions and discussions, presented by the standard documentation facilities like the facilities :doc and :more-doc that may be used at the terminal. Each of the ni's and mi's must be previously documented symbols. Both and
    must be non-empty, i.e., must contain some non-whitespace characters. may be empty. The :cites and :cited-bys pairs may be intermingled and may be separated by either newlines or spaces. The citation part may be empty. When the citation part is empty, the tilde-slash pair terminating the
    part may be omitted. Thus, the simplest form of a formatted documentation string is: ":Doc-Section ~/~/
    " Since white space at the front of , and
    is ignored, we often precede those parts by #\Newlines to make the strings easier to read in our source files. We also typically indent all of the text in the string by starting each line with a few spaces. (The Emacs commands for formatting Lisp get confused if you have arbitrary characters on the left margin.) We assume that every line in , , and
    starts with at least as many spaces as does, i.e., we assume they are all indented the same amount (or more). Let d be the number of spaces separating from the #\Newline preceding it. When the various parts are printed, we "de-indent" by stripping out the first d spaces following each #\Newline. However, we find that when documentation is printed flush against the left margin it is difficult to distinguish the documentation text from previous output. We therefore prefix each line we print by a special pad of characters. By default, this pad is "| " so that documentation text has a vertical bar running down the left margin. But the pad is just the value of the global variable doc-prefix and you may assign it any string you wish. To add such a string to the database under the symbol name we make a new entry in the section-name section of the database. The entry associates name with the string and uses the string's cites list as the initial value of the related names field. In addition, we add name to the related names field of each of the names listed in the string's cited-by list. We also add name to the related names field of its section-name. Observe that the cites list in a string is only the initial value of the related names of the names. Future documentation strings may add to it via :cited-by or :Doc-Section. Indeed, this is generally the case. We discuss this further below. When a brief description of name is required (as by :docs **), name and are printed. is usually printed starting in column 15 (however see *note PRINT-DOC-START-COLUMN::). Despite its name, need not be one line. It usually is one line, however. When you type :doc name at the terminal, the first response will be to print name and . Then :doc prints , if any. Then, if name is the name of a section, it prints the s for each of its related names. For example, try :doc events. If name is not a section name but does have some related names, they are merely listed but not explained. Try :doc theory-functions. :more-doc name prints
    . Our style is to let each new concept add itself to the related names of old concepts. To do otherwise increases the chances that documentation gets outdated because one often forgets to update supposedly complete lists of the relevant topics when new topics are invented. For example, :doc theory-functions lists each available theory function. But get-doc-string of 'theory-functions just shows a few examples and has an empty cites list. From where do we get the names of the theory functions listed by :doc? The answer is that each theory function has its own documentation string and those strings each specify :cited-by theory-functions. See for example get-doc-string of 'union-theories. So by the time the entire system is assembled, the related names of 'theory-functions contains all the (documented) theory functions. This makes it easy to add new theory functions without changing the general discussion in 'theory-functions. When an event or command form is printed, as by :pe or :pc, that contains a formatted documentation string, we do not print the actual documentation string (since they are usually large and distracting). Instead we print the string: "Documentation available via :doc" inviting you to use :doc and :more-doc (or get-doc-string) if you wish to see the documentation at the terminal. _Warning on Reading Symbols from Strings:_ When we read a symbol, such as the section-symbol, from a documentation string, we use a quick and dirty imitation of the much more powerful CLTL read program. In particular, we scan past any whitespace, collect all the characters we see until we get to more whitespace or the end of the string, convert the characters to upper case, make a string out of them, and intern that string. Thus, if you typed ":Doc-Section 123 ..." we would read the 123 as the symbol |123|. Observe that special characters, such as parentheses and escape characters, are not afforded their usual reverence by our hack. Furthermore, the question arises: in which package do we intern the symbol? The answer is, usually, the package containing the name being defined. I.e., if you are documenting my-pkg::name and you attach a documentation string that begins ":Doc-Section: Machines ..." then the section-symbol will be my-pkg::machines. We recognize two special cases. If the first character read is a colon, we use the keyword package. If the first five characters read are acl2:: then we intern in the "ACL2" package. Our own section names, e.g., events, are in the "ACL2" package. In a related area, when you ask for the documentation of a name, e.g., when you type :doc name at the terminal, that name is read with the full ACL2 reader, not the hack just described. That name is read into the current package. Thus, if you are operating in-package "MY-PKG" and type :doc events, what is read is my-pkg::events. The database may not contain an entry for this symbol. Before reporting that no documentation exists, we try acl2::events. One last note: defpkg permits a formatted documentation string, which is associated in the database with the name of the package. But the name of the package is a string, not a symbol. It is permitted to access the documentation of a string (i.e., package name). But there are no facilities for getting such a stringp name into the related names of another name nor of making such stringp names be section names. That is because we always read symbols from strings and never read strings from strings. I.e., if you did write "Doc-Section \"MY-PKG\" ..." it would read in as a weird symbol.  File: acl2-doc-emacs.info, Node: DOCS, Next: HELP, Prev: DOC-STRING, Up: DOCUMENTATION DOCS available documentation topics (by section) NOTE: The :docs command only makes sense at the terminal. When the :docs command is given a stringp argument it searches the text produced by :doc and :more-doc and lists all the documented topics whose text contains the given string. For purposes of this string matching we ignore distinctions of case and the amount and kind (but not presence) of white space. We also treat hyphen as whitespace. However, the following examples show how :docs can be used on other than string patterns. Examples: ACL2 !>:docs * ; lists documentation sections ACL2 !>:docs ** ; lists all documented topics within all sections ACL2 !>:docs events ; lists all topics in section EVENTS ACL2 !>:docs "compil" ; lists topics ``apropos'' The database of formatted documentation strings is structured into sections. Within a section are topics. Each topic has a one-liner, some notes, and some detailed discussions. The :docs command provides a view of the entire database. :docs takes one argument, as described below: arg effect * list all section headings in the database ** list all section headings and all topics within each section name list all topics in the section named name (where name is some symbol other than * and **). This is always the same as :doc name. pattern list all topics whose :doc or :more-doc text mentions the string pattern. For purposes of this string matching we ignore distinctions of case and the amount and kind (but not presence) of white space. We also treat hyphen as whitespace.  File: acl2-doc-emacs.info, Node: HELP, Next: MARKUP, Prev: DOCS, Up: DOCUMENTATION HELP brief survey of ACL2 features Example: ACL2 !>:help See *note LP:: for general information about the top-level command environment for ACL2.  File: acl2-doc-emacs.info, Node: MARKUP, Next: MORE, Prev: HELP, Up: DOCUMENTATION MARKUP the markup language for ACL2 documentation strings ACL2 documentation strings make special use of the tilde character (~). In particular, we describe here a "markup language" for which the tilde character plays a special role. The markup language is valuable if you want to write documentation that is to be displayed outside your ACL2 session. If you are not writing such documentation, and if also you do not use the character `~', then there is no need to read on. Three uses of the tilde character (~) in documentation strings are as follows. Below we explain the uses that constitute the ACL2 markup language. ~/ Indicates the end of a documentation *section*; see *note DOC-STRING::. ~~ Indicates the literal insertion of a tilde character (~). ~] This directive in a documentation string is effective only during the processing of part 2, the details (see *note DOC-STRING::), and controls how much is shown on each round of :more processing when printing to the terminal. If the system is not doing :more processing, then it acts as though the ~] is not present. Otherwise, the system put out a newline and halts documentation printing on the present topic, which can be resumed if the user types :more at the terminal. The other uses of the tilde character are of the following form. ~key[arg] Before launching into an explanation of how this works in detail, let us consider some small examples. Here is a word that is code: ~c[function-name]. Here is a phrase with an "emphasized" word, "not": Do ~em[not] do that. Here is the same phrase, but where "not" receives stronger emphasis (presumably boldface in a printed version): Do ~st[not] do that. Here is a passage that is set off as a display, in a fixed-width font: ~bv[] This passage has been set off as ``verbatim''. The present line starts just after a line break. Normally, printed text is formatted, but inside ~bv[]...~ev[], line breaks are taken literally. ~ev[] In general, the idea is to provide a "markup language" that can be reasonably interpreted not only at the terminal (via :doc), but also via translators into other languages. In fact, translators have been written into Texinfo and HTML. Let us turn to a more systematic consideration of how to mark text in documentation strings using expressions of the form ~key[arg], which we will call "doc-string tilde directives." The idea is that key informs the documentation printer (which could be the terminal, a hardcopy printer, or some hypertext tool) about the "style" used to display arg. The intention is that each such printer should do the best it can. For example, we have seen above that ~em[arg] tells the printer to emphasize arg if possible, using an appropriate display to indicate emphasis (italics, or perhaps surrounding arg with some character like _, or ...). For another example, the directive for bold font, ~b[arg], says that printed text for arg should be in bold if possible, but if there is no bold font available (such as at the terminal), then the argument should be printed in some other reasonable manner (for example, as ordinary text). The key part is case-insensitive; for example, you can use ~BV[] or ~Bv[] or ~bV[] in place of ~bv[]. Every form below may have any string as the argument (inside [..]), as long as it does not contain a newline (more on that below). However, when an argument does not make much sense to us, we show it below as the empty string, e.g., "~bv[]" rather than "~bv[arg]". ~-[] Print the equivalent of a dash ~b[arg] Print the argument in bold font, if available ~bid[arg] ``Begin implementation dependent'' -- Ignores argument at terminal. ~bf[] Begin formatted text (respecting spaces and line breaks), but in ordinary font (rather than, say, fixed-width font) if possible ~bq[] Begin quotation (indented text, if possible) ~bv[] Begin verbatim (print in fixed-width font, respecting spaces and line breaks) ~c[arg] Print arg as ``code'', such as in a fixed-width font ~ef[] End format; balances ~bf[] ~eid[arg] ``End implementation dependent'' -- Ignores argument at terminal. ~em[arg] Emphasize arg, perhaps using italics ~eq[] End quotation; balances ~bq[] ~ev[] End verbatim; balances ~bv[] ~i[arg] Print arg in italics font ~id[arg] ``Implementation dependent'' -- Ignores argument at terminal. ~il[arg] Print argument as is, but make it a link to another doc topic (for true hypertext environments). Note that the link argument must match the package name for the desired topic unless the package name is ``ACL2''. That is, to link to documentation on ``foo::topic'', one needs to use the full name for ``foo::topic'', including its package name. ~ilc[arg] Same as ~il[arg], except that arg should be printed as with ~c[arg] ~l[arg] Ordinary link to another doc topic; prints as ``See :DOC arg'' at the terminal (but also see ~pl below, which puts ``see'' in lower case) ~nl[] Print a newline ~par[] Paragraph mark, of no significance at the terminal (can be safely ignored; see also notes below) ~pl[arg] Parenthetical link (borrowing from Texinfo): same as ~l[arg], except that ``see'' is in lower case. This is typically used at other than the beginning of a sentence. ~sc[arg] Print arg in (small, if possible) capital letters ~st[arg] Strongly emphasize arg, perhaps using a bold font ~t[arg] Typewriter font; similar to ~c[arg], but leaves less doubt about the font that will be used. ~terminal[arg] Terminal only; arg is to be ignored except when reading documentation at the terminal, using :DOC. ~url[arg] Print arg as HTML hyperlink if possible (else print like ~c) _Style notes and further details_ It is not a good idea to put doc-string tilde directives inside verbatim environments, ~bv[] ... ~ev[]. Do not nest doc-string tilde directives; that is, do not write The ~c[~il[append] function ... but note that the "equivalent" expression The ~ilc[append] function ... is fine. The following phrase is also acceptable: ~bf[]This is ~em[formatted] text. ~ef[] because the nesting is only conceptual, not literal. We recommend that for displayed text, ~bv[] and ~ev[] should usually each be on lines by themselves. That way, printed text may be less encumbered with excessive blank lines. Here is an example. Here is some normal text. Now start a display: ~bv[] 2 + 2 = 4 ~ev[] And here is the end of that paragraph. Here is the start of the next paragraph. The analogous consideration applies to ~bf[] and ~ef[] as well as ~bq[] and ~eq[]. You may "quote" characters inside the arg part of ~key[arg], by preceding them with ~. This is, in fact, the only legal way to use a newline character or a right bracket (]) inside the argument to a doc-string tilde directive. Write your documentation strings without hyphens. Otherwise, you may find your text printed on paper (via TeX, for example) like this -- Here is a hyphe- nated word. even if what you had in mind was: Here is a hyphe- nated word. When you want to use a dash (as opposed to a hyphen), consider using ~-[], which is intended to be interpreted as a "dash." For example: This sentence ~-[] which is broken with dashes ~-[] is boring. would be written to the terminal (using :doc) by replacing ~-[] with two hyphen characters, but would presumably be printed on paper with a dash. Be careful to balance the "begin" and "end" pairs, such as ~bv[] and ~ev[]. Also, do not use two "begin" directives (~bf[], ~bq[], or ~bv[]) without an intervening "end" directive. It is permissible (and perhaps this is not surprising) to use the doc-string part separator ~/ while between such a begin-end pair. Because of a bug in texinfo (as of this writing), you may wish to avoid beginning a line with (any number of spaces followed by) the - character or ~-[]. The "paragraph" directive, ~par[], is rarely if ever used. There is a low-level capability, not presently documented, that interprets two successive newlines as though they were ~par[]. This is useful for the HTML driver. For further details, see the authors of ACL2. Emacs code is available for manipulating documentation strings that contain doc-string tilde-directives (for example, for doing a reasonable job filling such documentation strings). See the authors if you are interested. We tend to use ~em[arg] for "section headers," such as "Style notes and further details" above. We tend to use ~st[arg] for emphasis of words inside text. This division seems to work well for our Texinfo driver. Note that ~st[arg] causes arg to be printed in upper-case at the terminal (using :doc), while ~em[arg] causes arg to be printed at the terminal as though arg were not marked for emphasis. Our Texinfo and HTML drivers both take advantage of capabilities for indicating which characters need to be "escaped," and how. Unless you intend to write your own driver, you probably do not need to know more about this issue; otherwise, contact the ACL2 authors. We should probably mention, however, that Texinfo makes the following requirement: when using ~l[arg], where arg contains one of the special characters @, {, or }, you must immediately follow this use with a period or comma. Also, the Emacs "info" documentation that we generate by using our Texinfo driver has the property that in node names, : has been replaced by | (because of quirks in info); so for example, the "proof-checker" simplification command, s, is documented under acl2-pc||s rather than under acl2-pc::s. We have tried to keep this markup language fairly simple; in particular, there is no way to refer to a link by other than the actual name. So for example, when we want to make :doc an invisible link in "code" font, we write the following form, which indicates that : should be in that font and then doc should both be in that font and be an invisible link. ~c[:]~ilc[doc]  File: acl2-doc-emacs.info, Node: MORE, Next: MORE!, Prev: MARKUP, Up: DOCUMENTATION MORE your response to :doc or :more's "(type :more...)" NOTE: The command :more only makes sense at the terminal. Example: ACL2 !>:more will continue printing whatever documentation was started by :doc or :more-doc. When you type :doc name, for some documented name, the system responds by typing the one-liner and the notes sections of the documentation for name. It then types "(type :more for more, :more! for the rest)". If you then type ACL2 !>:more the system will start to print the details section of name. The same thing could be achieved by typing :more-doc name, but that requires you to type name again. Similarly, if you have typed :more-doc name, the system will print the first "block" of the details section and then print "(type :more for more, :more! for the rest)". Typing :more at that point will cause the next block of the details section to be printed. Eventually :more will conclude by printing "*-" which is the indicator that the text has been exhausted. What is a "block" of text? :More looks for the end of a paragraph (two adjacent newlines) after printing n lines. If it doesn't find one before it has printed k lines, it just stops there. N and k here are the values of the two state global variables 'more-doc-min-lines and 'more-doc-max-lines. You may use @ and assign to inspect and set these variables, e.g., (@ more-doc-max-lines) will return the current maximum number of lines printed by :more and (assign more-doc-max-lines 19) will set it to 19. On terminals having only 24 lines, we find min and max settings of 12 and 19 the most pleasant. If you want :more to print all of the details instead of feeding them to you one block at a time, type :more! instead.  File: acl2-doc-emacs.info, Node: MORE!, Next: MORE-DOC, Prev: MORE, Up: DOCUMENTATION MORE! another response to "(type :more for more, :more! for the rest)" NOTE: The command :more! only makes sense at the terminal. Example: ACL2 !>:more! will print all of the remaining documentation started by the last :doc or :more-doc. See *note MORE:: for some background. Typing :more! will print all remaining blocks of documentation. :More! is like :more except that it prints all the text at once. For example, if you type :doc name you will see some text followed by "(type :more for more, :more! for the rest)". If you then type simply :more! you will see all of the details, while if you type :more you will be fed the next block of details.  File: acl2-doc-emacs.info, Node: MORE-DOC, Next: NQTHM-TO-ACL2, Prev: MORE!, Up: DOCUMENTATION MORE-DOC a continuation of the :doc documentation NOTE: The :more-doc command only makes sense at the terminal. Examples: ACL2 !>:more-doc DEFTHM ACL2 !>:more-doc logical-name Often it is assumed in the text provided by :more-doc name that you have read the text provided by :doc name. :More-doc just continues spewing out at you the documentation string provided with a definition. If the user has done his job, :doc will probably remind you of the basics and :more-doc, if read after :doc, will address obscure details that are nevertheless worth noting. When :more-doc types "(type :more for more, :more! for the rest)" you can get the next block of the continuation by typing :more or all of the remaining blocks by typing :more!. See *note MORE::.  File: acl2-doc-emacs.info, Node: NQTHM-TO-ACL2, Prev: MORE-DOC, Up: DOCUMENTATION NQTHM-TO-ACL2 ACL2 analogues of Nqthm functions and commands Example Forms: :nqthm-to-acl2 prove-lemma ; Display ACL2 topic(s) and/or print ; information corresponding to Nqthm ; PROVE-LEMMA command. (nqthm-to-acl2 'prove-lemma) ; Same as above. General Form: (nqthm-to-acl2 name) where name is a notion documented for Nqthm: either a function in the Nqthm logic, or a command. If there is corresponding information available for ACL2, it will be printed in response to this command. This information is not intended to be completely precise, but rather, is intended to help those familiar with Nqthm to make the transition to ACL2. We close with two tables that contain all the information used by this nqthm-to-acl2 command. The first shows the correspondence between functions in the Nqthm logic and corresponding ACL2 functions (when possible); the second is similar, but for commands rather than functions. Nqthm functions --> ACL2 ---------------------------------------- ADD1 --> 1+ ADD-TO-SET --> ADD-TO-SET-EQUAL and ADD-TO-SET-EQ AND --> AND APPEND --> APPEND and BINARY-APPEND APPLY-SUBR --> No correspondent, but see the documentation for DEFEVALUATOR and META. APPLY$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. ASSOC --> ASSOC-EQUAL, ASSOC and ASSOC-EQ BODY --> No correspondent, but see the documentation for DEFEVALUATOR and META. CAR --> CAR CDR --> CDR CONS --> CONS COUNT --> ACL2-COUNT DIFFERENCE --> - EQUAL --> EQUAL, EQ, EQL and = EVAL$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. FALSE --> Nqthm's F corresponds to the ACL2 symbol NIL. FALSEP --> NOT and NULL FORMALS --> No correspondent, but see the documentation for DEFEVALUATOR and META. GEQ --> >= GREATERP --> > IDENTITY --> IDENTITY IF --> IF IFF --> IFF IMPLIES --> IMPLIES LEQ --> <= LESSP --> < LISTP --> CONSP LITATOM --> SYMBOLP MAX --> MAX MEMBER --> MEMBER-EQUAL, MEMBER and MEMBER-EQ MINUS --> - and UNARY-- NEGATIVEP --> MINUSP NEGATIVE-GUTS --> ABS NLISTP --> ATOM NOT --> NOT NUMBERP --> ACL2-NUMBERP, INTEGERP and RATIONALP OR --> OR ORDINALP --> O-P ORD-LESSP --> O< PACK --> See intern and coerce. PAIRLIST --> PAIRLIS$ PLUS --> + and BINARY-+ QUOTIENT --> / REMAINDER --> REM and MOD STRIP-CARS --> STRIP-CARS SUB1 --> 1- TIMES --> * and BINARY-* TRUE --> The symbol T. UNION --> UNION-EQUAL and UNION-EQ UNPACK --> See symbol-name and coerce. V&C$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. V&C-APPLY$ --> No correspondent, but see the documentation for DEFEVALUATOR and META. ZERO --> The number 0. ZEROP --> ZP ======================================== Nqthm commands --> ACL2 ---------------------------------------- ACCUMULATED-PERSISTENCE --> ACCUMULATED-PERSISTENCE ADD-AXIOM --> DEFAXIOM ADD-SHELL --> There is no shell principle in ACL2. AXIOM --> DEFAXIOM BACKQUOTE-SETTING --> Backquote is supported in ACL2, but not currently documented. BOOT-STRAP --> GROUND-ZERO BREAK-LEMMA --> MONITOR BREAK-REWRITE --> BREAK-REWRITE CH --> PBT See also :DOC history. CHRONOLOGY --> PBT See also :DOC history. COMMENT --> DEFLABEL COMPILE-UNCOMPILED-DEFNS --> COMP CONSTRAIN --> See :DOC encapsulate and :DOC local. DATA-BASE --> Perhaps the closest ACL2 analogue of DATA-BASE is PROPS. But see :DOC history for a collection of commands for querying the ACL2 database (``world''). Note that the notions of supporters and dependents are not supported in ACL2. DCL --> DEFSTUB DEFN --> DEFUN and DEFMACRO DEFTHEORY --> DEFTHEORY DISABLE --> DISABLE DISABLE-THEORY --> See :DOC theories. The Nqthm command (DISABLE-THEORY FOO) corresponds roughly to the ACL2 command (in-theory (set-difference-theories (current-theory :here) (theory 'foo))). DO-EVENTS --> LD DO-FILE --> LD ELIM --> ELIM ENABLE --> ENABLE ENABLE-THEORY --> See :DOC theories. The Nqthm command (ENABLE-THEORY FOO) corresponds roughly to the ACL2 command (in-theory (union-theories (theory 'foo) (current-theory :here))). EVENTS-SINCE --> PBT FUNCTIONALLY-INSTANTIATE --> ACL2 provides a form of the :USE hint that corresponds roughly to the FUNCTIONALLY-INSTANTIATE event of Nqthm. See :DOC lemma-instance. GENERALIZE --> GENERALIZE HINTS --> HINTS LEMMA --> DEFTHM MAINTAIN-REWRITE-PATH --> BRR MAKE-LIB --> There is no direct analogue of Nqthm's notion of ``library.'' See :DOC books for a description of ACL2's mechanism for creating and saving collections of events. META --> META NAMES --> NAME NOTE-LIB --> INCLUDE-BOOK PPE --> PE PROVE --> THM PROVEALL --> See :DOC ld and :DOC certify-book. The latter corresponds to Nqthm's PROVE-FILE,which may be what you're interested in,really. PROVE-FILE --> CERTIFY-BOOK PROVE-FILE-OUT --> CERTIFY-BOOK PROVE-LEMMA --> DEFTHM See also :DOC hints. R-LOOP --> The top-level ACL2 loop is an evaluation loop as well, so no analogue of R-LOOP is necessary. REWRITE --> REWRITE RULE-CLASSES --> RULE-CLASSES SET-STATUS --> IN-THEORY SKIM-FILE --> LD-SKIP-PROOFSP TOGGLE --> IN-THEORY TOGGLE-DEFINED-FUNCTIONS --> EXECUTABLE-COUNTERPART-THEORY TRANSLATE --> TRANS and TRANS1 UBT --> UBT and U UNBREAK-LEMMA --> UNMONITOR UNDO-BACK-THROUGH --> UBT UNDO-NAME --> See :DOC ubt. There is no way to undo names in ACL2 without undoing back through such names. However, see :DOC ld-skip-proofsp for information about how to quickly recover the state.  File: acl2-doc-emacs.info, Node: EVENTS, Next: FORWARD-CHAINING-REPORTS, Prev: DOCUMENTATION, Up: Top EVENTS functions that extend the logic * Menu: * ADD-CUSTOM-KEYWORD-HINT:: add a new custom keyword hint * ASSERT-EVENT:: assert that a given form returns a non-nil value * COMP:: compile some ACL2 functions * DEFABBREV:: a convenient form of macro definition for simple expansions * DEFABSSTOBJ:: define a new abstract single-threaded object * DEFABSSTOBJ-MISSING-EVENTS:: obtain the events needed to admit a defabsstobj event * DEFATTACH:: execute constrained functions using corresponding attached functions * DEFAXIOM:: add an axiom * DEFCHOOSE:: define a Skolem (witnessing) function * DEFCONG:: prove congruence rule * DEFCONST:: define a constant * DEFDOC:: add a documentation topic * DEFEQUIV:: prove that a function is an equivalence relation * DEFEVALUATOR:: introduce an evaluator function * DEFEXEC:: attach a terminating executable function to a definition * DEFINE-TRUSTED-CLAUSE-PROCESSOR:: define a trusted (unverified) goal-level simplifier * DEFLABEL:: build a landmark and/or add a documentation topic * DEFMACRO:: define a macro * DEFMACRO-LAST:: define a macro that returns its last argument, but with side effects * DEFN:: definition with guard t * DEFND:: disabled definition with guard t * DEFPKG:: define a new symbol package * DEFPROXY:: define a non-executable :program-mode function for attachment * DEFPUN:: define a tail-recursive function symbol * DEFREFINEMENT:: prove that equiv1 refines equiv2 * DEFSTOBJ:: define a new single-threaded object * DEFSTUB:: stub-out a function symbol * DEFTHEORY:: define a theory (to enable or disable a set of rules) * DEFTHEORY-STATIC:: define a `static' theory (to enable or disable a set of rules) * DEFTHM:: prove and name a theorem * DEFTHMD:: prove and name a theorem and then disable it * DEFTTAG:: introduce a trust tag (ttag) * DEFUN:: define a function symbol * DEFUN-INLINE:: define a potentially inlined function symbol and associated macro * DEFUN-NOTINLINE:: define a not-to-be-inlined function symbol and associated macro * DEFUN-NX:: define a non-executable function symbol * DEFUN-SK:: define a function whose body has an outermost quantifier * DEFUND:: define a function symbol and then disable it * DEFUND-INLINE:: define a potentially disabled, inlined function symbol and associated macro * DEFUND-NOTINLINE:: define a disabled, not-to-be-inlined function symbol and associated macro * ENCAPSULATE:: hide some events and/or constrain some functions * EVISC-TABLE:: support for abbreviated output * IN-ARITHMETIC-THEORY:: designate theory for some rewriting done for non-linear arithmetic * IN-THEORY:: designate ``current'' theory (enabling its rules) * INCLUDE-BOOK:: load the events in a file * LOCAL:: hiding an event in an encapsulation or book * MAKE-EVENT:: evaluate (expand) a given form and then evaluate the result * MEMOIZE:: turn on memoization for a specified function * MUTUAL-RECURSION:: define some mutually recursive functions * PROFILE:: turn on profiling for one function * PROGN:: evaluate some events * PROGN!:: evaluate some forms, not necessarily events * REGENERATE-TAU-DATABASE:: regenerate the tau database relative to the current enabled theory * REMOVE-CUSTOM-KEYWORD-HINT:: remove a custom keyword hint * RESTORE-MEMOIZATION-SETTINGS:: restore the saved memoization settings * SAVE-AND-CLEAR-MEMOIZATION-SETTINGS:: save and remove the current memoization settings * SET-BODY:: set the definition body * TABLE:: user-managed tables * THEORY-INVARIANT:: user-specified invariants on theories * UNMEMOIZE:: turn off memoization for the specified function * VALUE-TRIPLE:: compute a value, optionally checking that it is not nil * VERIFY-GUARDS:: verify the guards of a function * VERIFY-GUARDS+:: verify the guards of a function * VERIFY-TERMINATION:: convert a function from :program mode to :logic mode Any extension of the syntax of ACL2 (i.e., the definition of a new constant or macro), the axioms (i.e., the definition of a function), or the rule database (i.e., the proof of a theorem), constitutes a logical "event." Events change the ACL2 logical world (see *note WORLD::). Indeed, the only way to change the ACL2 world is via the successful evaluation of an event function. Every time the world is changed by an event, a landmark is left on the world and it is thus possible to identify the world "as of" the evaluation of a given event. An event may introduce new logical names. Some events introduce no new names (e.g., verify-guards), some introduce exactly one (e.g., defmacro and defthm), and some may introduce many (e.g., encapsulate ). ACL2 typically completes processing of an event by printing a summary. Unless proofs are skipped (see *note LD-SKIP-PROOFSP::) or summary output is inhibited (see *note SET-INHIBIT-OUTPUT-LST::), information about the proof attempt (if any) is printed that includes a list of rules used, a summary of warnings, and the number of "prover steps" (if any; see *note WITH-PROVER-STEP-LIMIT::). A breakdown of the time used is also printed, which by default is runtime (cpu time), but can be changed to realtime (wall clock time); see *note GET-INTERNAL-TIME::. See *note EMBEDDED-EVENT-FORM:: for a discussion of events permitted in books.  File: acl2-doc-emacs.info, Node: ADD-CUSTOM-KEYWORD-HINT, Next: ASSERT-EVENT, Prev: EVENTS, Up: EVENTS ADD-CUSTOM-KEYWORD-HINT add a new custom keyword hint Examples: (add-custom-keyword-hint :my-hint (my-hint-fn val ...)) (add-custom-keyword-hint :my-hint (my-hint-fn val ...) :checker (my-hint-checker-fn val ...)) General Form: (add-custom-keyword-hint :key term1 :checker term2) where :key is a keywordp not among the primitive keyword hints listed in *hint-keywords*, the :checker argument is optional, and term1 and (if supplied) term2 are terms with certain free-variable and signature restrictions described below. Henceforth, :key is treated as a custom keyword hint, e.g., the user can employ :key in hints to defthm, such as: (defthm name ... :hints (("Subgoal *1/1'" ... :key val ...))). Custom keyword hints are complicated. To use them you must understand state, multiple values (e.g., mv and mv-let), ACL2's notion of error triples (see *note PROGRAMMING-WITH-STATE::), how to generate "soft" errors with er, how to use fmt-strings to control output, how to use computed hints (see *note COMPUTED-HINTS::) and some aspects of ACL2's internal event processing. Furthermore, it is possible to implement a custom keyword hint that can make an event non-reproducible! So we recommend that these hints be developed by ACL2 experts. Basically the custom keyword feature allows the implementors and other experts to extend the hint facility without modifying the ACL2 sources. Term1 is called the "generator" term and term2 is called the "checker" term of the custom keyword hint :key. Together they specify the semantics of the new custom keyword hint :key. Roughly speaking, when a custom keyword hint is supplied by the user, as in (defthm name ... :hints (("Subgoal *1/1'" ... :my-hint val ...))). the checker term is evaluated on val to check that val is of the expected shape. Provided val passes the check, the generator term is used to compute a standard hint. Like computed hints, the generator of a custom keyword hint is allowed to inspect the actual clause on which it is being fired. Indeed, it is allowed to inspect the entire list of hints (standard and custom) supplied for that clause. Thus, in the most general case, a custom keyword hint is just a very special kind of computed hint. The generator, term1, must have no free variables other than: (val keyword-alist id clause world stable-under-simplificationp hist pspv ctx state). Moreover, either term1 must evaluate to a single non-stobj value, or else it must be single-threaded in state and have the standard error-triple output signature, (mv * * state). The restrictions on the checker, term2, are that it be single-threaded in state, have the standard error-triple output signature, (mv * * state), and have no free variables other than: (val world ctx state). For examples, see the community books directory books/hints/, in particular basic-tests.lisp. To delete a previously added custom keyword hint, see *note REMOVE-CUSTOM-KEYWORD-HINT::. The community book hints/merge-hint.lisp can be useful in writing custom keyword hints. See the examples near the of the file. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.  File: acl2-doc-emacs.info, Node: ASSERT-EVENT, Next: COMP, Prev: ADD-CUSTOM-KEYWORD-HINT, Up: EVENTS ASSERT-EVENT assert that a given form returns a non-nil value Examples: (assert-event (equal (+ 3 4) 7)) (assert-event (equal (+ 3 4) 7) :msg (msg "Error: ~x0" 'equal-check)) (assert-event (equal (+ 3 4) 7) :on-skip-proofs t) General Forms: (assert-event form) (assert-event form :on-skip-proofs t) Assert-event takes a ground form, i.e., one with no free variables; stobjs are allowed but only a single non-stobj value can be returned. The form is then evaluated and if the result is nil, then a so-called hard error (see *note ER::) results. This evaluation is however not done if proofs are being skipped, as during include-book (also see *note SKIP-PROOFS:: and see *note LD-SKIP-PROOFSP::), unless :on-skip-proofs t is supplied. Normally, if an assert-event call fails then a generic failure message is printed, showing the offending form. However, if keyword argument :msg is supplied, then the failure message is printed as with fmt argument ~@0; see *note FMT::. In particular, :msg is typically a string or a call (msg str arg-0 arg-1 ... arg-k), where str is a string and each arg-i is the value to be associated with #\i upon formatted printing (as with fmt) of the string str. This form may be put into a book to be certified (see *note BOOKS::), because assert-event is a macro whose calls expand to calls of value-triple (see *note EMBEDDED-EVENT-FORM::). When certifying a book, guard-checking is off, as though (set-guard-checking nil) has been evaluated; see *note SET-GUARD-CHECKING::. That, together with a "safe mode," guarantees that assert-event forms are evaluated in the logic without guard violations while certifying a book.  File: acl2-doc-emacs.info, Node: COMP, Next: DEFABBREV, Prev: ASSERT-EVENT, Up: EVENTS COMP compile some ACL2 functions NOTE: Comp is a no-op if explicit compilation is suppressed; see *note COMPILATION::. The documentation here assumes that this is not the case. Examples: :comp t ; compile all uncompiled ACL2 functions (comp t) ; same as above, but can be put into a book (comp :exec) ; compile all uncompiled logic (``*1*'') definitions :comp foo ; compile the defined function foo :comp (:raw foo) ; compile the raw Lisp version of the defined function foo but not the corresponding logic definition :comp (foo bar) ; compile the defined functions foo and bar :comp (foo (:raw bar)) ; compile the defined functions foo and bar, but for ; bar do not compile the corresponding logic definition General Form: :comp specifier where specifier is one of the following: t compile all user-defined ACL2 functions that are currently uncompiled (redefined built-in functions are not recompiled) :exec same as t, except that only logic versions are compiled (see below), not raw Lisp definitions :raw same as t, except that only raw Lisp definitions are compiled, not logic version (see below) (name-1 ... name-k) a non-empty list of names of functions defined by DEFUN in ACL2, except that each name-i can be of the form (:raw sym) or (:exec sym), where sym is the name of such a function name same as (name) When you define a function in ACL2, you are really causing two definitions to be made "under the hood" in Common Lisp: the definition is submitted explicitly to raw Lisp, but so is a corresponding "logic definition". If guards have not been verified, then only the logic definition will be evaluated; see *note GUARDS-AND-EVALUATION::, in particular the section titled "Guards and evaluation V: efficiency issues". Thus, if you are not verifying guards and you want the benefit of Lisp compilation for speed and space efficiency, then you may want to place the form (comp :exec) in your books. Generally it is not necessary to place the form (comp t), or the form (comp :raw), in a book, because certify-book compiles the raw Lisp definitions anyhow, by default. But you may wish to put (comp t) or (comp fn1 fn2 ... fnk) in a book when such a form precedes expensive calls of functions, for example for proofs involving calls of functions on large constants, or to support computationally expensive macroexpansion. As suggested by the examples above, if a function specifier is of the form (:raw fn), then fn will be compiled in raw Common Lisp but its corresponding logic definition will not be compiled; and for (:exec fn), it's the other way around. The use of :comp may create various files whose names start with "TMP*", but it then deletes them. If you want to save these files, evaluate (assign keep-tmp-files t). * Menu: * COMP-GCL:: compile some ACL2 functions leaving .c and .h files Also see *note SET-COMPILE-FNS:: for a way to compile each function as it is defined. But note that set-compile-fns is ignored during include-book. Note that if functions are traced (see *note TRACE$::), then comp will first untrace the functions that are to be compiled, then will do the compile(s), and finally will re-trace the functions that it untraced (using their original trace specs). In particular, if you have traced a function and then you compile it using :comp, the resulting traced function will be compiled as well unless you specified :compile nil in your trace spec; and after you untrace the function it will definitely run compiled. We conclude with a technical remark only for those who use trust tags to write raw Lisp code. :Comp generally creates files to compile unless it is given a single function to compile. Those files contain the ACL2 definitions of all functions to compile, omitting those in the lists obtained by evaluating the forms (@ logic-fns-with-raw-code) and (@ program-fns-with-raw-code). :Comp skips compilation for functions that are already compiled, as is typically the case when you redefine functions in raw Lisp using the utility include-raw defined in community book books/tools/include-raw.lisp. But if you define interpreted (as opposed to compiled) functions with raw Lisp code, say by using trust tags (see *note DEFTTAG::) and progn!, then you are advised to add all such symbols to one of the lists stored in the two state globals above: to logic-fns-with-raw-code if the function symbol is in :logic mode, else to program-fns-with-raw-code. Then, instead of the corresponding ACL2 definition (without raw Lisp code) being written to a file, the function symbol will be passed directly to the Lisp compile function. Note that the above two state globals are both untouchable, so you may need to deal with that before modifying them, for example as follows (also see *note REMOVE-UNTOUCHABLE::). (defttag t) (state-global-let* ((temp-touchable-vars t set-temp-touchable-vars)) (progn! (f-put-global 'logic-fns-with-raw-code (cons 'my-fn (@ logic-fns-with-raw-code)) state)))  File: acl2-doc-emacs.info, Node: COMP-GCL, Prev: COMP, Up: COMP COMP-GCL compile some ACL2 functions leaving .c and .h files Comp-gcl is for use by experts who want to examine the results of GCL compilation, and it may only be used with ACL2 implementations built on top of GCL. It takes exactly the same arguments as comp, and has the same basic functionality (see *note COMP::), but has two additional effects. First, files "TMP.lisp" and "TMP1.lisp" are always created, even when a single function is specified. Second, comp-gcl always leaves files "TMP.c", "TMP.h", "TMP1.c", and "TMP1.h" when compilation is complete.  File: acl2-doc-emacs.info, Node: DEFABBREV, Next: DEFABSSTOBJ, Prev: COMP, Up: EVENTS DEFABBREV a convenient form of macro definition for simple expansions Examples: (defabbrev snoc (x y) (append y (list x))) (defabbrev sq (x) (declare (type (signed-byte 8) x)) (* x x)) General Form: (defabbrev name (v1 ... vn) doc-string decl1 ... declk body) where name is a new function symbol, the vi are distinct variable symbols, and body is a term. The decli, if supplied, should be legal declare forms; see *note DECLARE::. Doc-string is an optional documentation string; see *note DOC-STRING::. Roughly speaking, the defabbrev event is akin to defining f so that (f v1 ... vn) = body. But rather than do this by adding a new axiom, defabbrev defines f to be a macro so that (f a1 ... an) expands to body, with the "formals," vi, replaced by the "actuals," ai. For example, if snoc is defined as shown in the first example above, then (snoc (+ i j) temp) is just an abbreviation for (append temp (list (+ i j))). In order to generate efficiently executable Lisp code, the macro that defabbrev introduces uses a let to bind the "formals" to the "actuals." Consider the second example above. Logically speaking, (sq (ack i j)) is an abbreviation for (* (ack i j) (ack i j)). But in fact the macro for sq introduced by defabbrev actually arranges for (sq (ack i j)) to expand to: (let ((x (ack i j))) (* x x)) which executes more efficiently than (* (ack i j) (ack i j)). In the theorem prover, the let above expands to ((lambda (x) (* x x)) (ack i j)) and thence to (* (ack i j) (ack i j)). It is important to note that the term in body should not contain a call of name -- i.e., defabbrev should not be used in place of defun when the function is recursive. ACL2 will not complain when the defabbrev form is processed, but instead ACL2 will more than likely go into an infinite loop during macroexpansion of any form that has a call of name. It is also important to note that the parameters of any call of a macro defined by defabbrev will, as is the case for the parameters of a function call, be evaluated before the body is evaluated, since this is the evaluation order of let. This may lead to some errors or unexpected inefficiencies during evaluation if the body contains any conditionally evaluted forms like cond, case, or if. Consider the following example. (defabbrev foo (x y) (if (test x) (bar y) nil)) Notice a typical one-step expansion of a call of foo (see *note TRANS1::): ACL2 !>:trans1 (foo expr1 expr2) (LET ((X EXPR1) (Y EXPR2)) (IF (TEST X) (BAR Y) NIL)) ACL2 !> Now imagine that expr2 is a complicated expression whose evaluation is intended only when the predicate test holds of expr1. The expansion above suggests that expr2 will always be evaluated by the call (foo expr1 expr2), which may be inefficient (since perhaps we only need that value when test is true of expr1). The evaluation of expr2 may even cause an error, for example in :program mode if the expression expr2 has been constructed in a manner that could cause a guard violation unless test holds of expr1.  File: acl2-doc-emacs.info, Node: DEFABSSTOBJ, Next: DEFABSSTOBJ-MISSING-EVENTS, Prev: DEFABBREV, Up: EVENTS DEFABSSTOBJ define a new abstract single-threaded object We assume familiarity with single-threaded objects; see *note STOBJ:: and see *note DEFSTOBJ::. The event defabsstobj defines a so-called "abstract stobj", a notion we introduce briefly now and then explain in more depth below. The evaluation of a defstobj event produces logical definitions for several functions: a recognizer, which characterizes the stobj in terms of lists; a creator, which produces an initial suitable list structure; and field accessors and updators, defined in terms of nth and update-nth. Defabsstobj provides a way to define alternate definitions for "stobj primitives" for a corresponding single-threaded object. These stobj primitives include a recognizer, a creator, and other "exported" functions. In essence, defabsstobj establishes interface functions, or "exports", on a new stobj that is a copy of an indicated "concrete" stobj that already exists. We begin below with an introduction to abstract stobjs. We then explain the defabsstobj event by way of an example. We conclude by giving summary documentation for the defabsstobj event. For another introduction to abstract stobjs, see the paper "Abstract Stobjs and Their Application to ISA Modeling" by Shilpi Goel, Warren A. Hunt, Jr., and Matt Kaufmann, in the proceedings of ACL2 Workshop 2013, `http://www.cs.uwyo.edu/~ruben/acl2-13'. *INTRODUCTION* We start with a brief review of stobjs and some potential problems with them, followed by an introduction to abstract stobjs and how they can avoid these problems. Prior experience with stobjs will probably help the reader to absorb the ideas below. Recall that single-threaded objects, or stobjs, provide a way for ACL2 users to stay within the ACL2 logic, where every data object is an atom or a cons of data objects, while obtaining the benefits of fast evaluation through destructive updates. Consider for example this very simple event. (defstobj st fld) This event introduces a recognizer, stp, and a creator, create-st, for a data structure consisting of a single field accessed and updated by functions fld and update-fld, respectively. Each of these four primitive functions has both a logical definition, which is used when the prover reasons about the function, and an executable definition, which is used in raw Lisp. In the logic, stp recognizes objects that have the requisite fields. In raw Lisp, there is a "live stobj", which is an array object whose fields correspond to those specified by the defstobj event, implemented as Lisp arrays. Here are the logical definition and the executable definition, respectively, that are introduced for the field accessor, fld, introduced above. Notice that since a stobj is represented in raw Lisp using an array, the raw Lisp accessor uses a raw Lisp array accessor, svref. (You can see all the logical and executable definitions by evaluating the form (trace$ defstobj-axiomatic-defs defstobj-raw-defs) before evaluating the defstobj form.) ; logical definition (defun fld (st) (declare (xargs :guard (stp st) :verify-guards t)) (nth 0 st)) ; executable (raw Lisp) definition (defun fld (st) (svref st 0)) Sophisticated programming with stobjs can provide efficient implementations of algorithms, but may require the preservation of a complex invariant. One can, of course, define a function to implement such an invariant after introducing the stobj, as follows. ; Introduce a stobj. (defstobj st fld1 ... fldk) ; Define an invariant on that stobj. (defun good-stp (st) (declare (xargs :stobjs st)) ...) ; Define some basic functions that update the stobj and preserve the ; invariant. (defun update-st (... st ...) (declare (xargs :stobjs st :guard (and (good-stp st) ...))) ...) ... ; Prove that the invariant is indeed preserved by those basic functions. (defthm good-stp-update-st (implies (and (good-stp st) ...) (good-stp (update-st ... st ...)))) ... ; Implement algorithms built on the basic functions. (defun foo (... st ...) (declare (xargs :stobjs st :guard (and (good-stp st) ...))) ... (update-st ... st ...) ...) ; Prove invariance theorems about these algorithms. (defthm good-stp-foo (implies (and (good-stp st) ...) (good-stp (foo ... st ...)))) ... ; Prove other properties of these algorithms. (defthm foo-is-correct (implies (and (good-stp st) ...) (some-property (foo ... st ...)))) ... But there are at least two potential difficulties in using stobjs as described above. 1. When foo is executed on concrete data in the ACL2 loop, the guard check may be expensive because (good-stp st) is expensive. 2. Reasoning about foo (using rules like foo-is-correct above) involves proving hypotheses of invariance theorems, which may be complicated for the user to manage or slow for the theorem prover. The defabsstobj event offers an opportunity to address these issues. It introduces a new stobj, which we call an "abstract stobj", which is associated with a corresponding "concrete stobj" introduced by an earlier defstobj event. The defabsstobj event specifies a logical (:LOGIC) and an executable (:EXEC) definition for each primitive operation, or "stobj primitive", involving that stobj. As is the case for defstobj, the logical definition is what ACL2 reasons about, and is appropriate to apply to an ACL2 object satisfying the logical definition of the recognizer function for the stobj. The executable definition is applied in raw Lisp to a live stobj, which is an array object associated with the given stobj name. We can picture a sequence of updates to corresponding abstract and concrete stobjs as follows. Initially in this picture, st$a0 and st$c0 are a corresponding abstract and concrete stobj (respectively). Then an update, u1, is applied with :LOGIC and :EXEC functions u$a1 and u$c1, respectively. The resulting abstract and concrete stobj, st$a1 and st$c1, correspond as before. Then a second update, u2, is applied with :LOGIC and :EXEC functions u$a2 and u$c2, respectively -- again preserving the correspondence. And so on. Abstract u$a1 u$a2 u$a3 (:logic) st$a0 --> st$a1 --> st$a2 --> ... ^ ^ ^ ^ Correspondence | | | ... | v v v v u$c1 u$c2 u$c3 Concrete st$c0 --> st$c1 --> st$c2 --> ... (:exec) We conclude this introduction with some remarks about implementation. Consider an abstract stobj st with corresponding concrete stobj st$c. The live stobjs for st and st$c have the same structure, but are distinct arrays. Indeed, the raw Lisp creator function for st$c is called to create a new initial live stobj for st. As we will see below, reads and writes in raw Lisp to the live stobj for st are ultimately performed using the primitive accessors and updaters defined for st$c. One might think of the live stobjs for st and st$c as being congruent stobjs (see *note DEFSTOBJ::), except that the stobjs themselves are not congruent: the stobj primitives introduced for st may be applied to st but not arbitrary field updaters of st$c, for example. As one might expect, the :EXEC function for an exported function is applied to the live stobj for st in raw Lisp. *EXAMPLE* We present examples, with detailed comments intended to explain abstract stobjs, in two community books: books/misc/defabsstobj-example-1.lisp and books/misc/defabsstobj-example-2.lisp. In this section we outline the first of these. We suggest that after you finish this documentation topic, you read through those two books. Here is the first of two closely related defabsstobj events from the book defabsstobj-example-1.lisp, but in expanded form. We will show the abbreviated form later, which omits most of data in the form that is immediately below. Thus most of the information shown here is default information. We believe that the comments below explain most or all of what you need to know in order to start using defabsstobj, and that you will learn the remainder when you see error messages. For example, we do not say in the comments below that every :LOGIC and :EXEC function must be guard-verified, but that is indeed a requirement. (defabsstobj st ; The new abstract stobj is named st. ; The concrete stobj corresponding to st is st$c: :concrete st$c ; The recognizer for the new abstract stobj is stp, which is defined to be ; st$ap in the logic, and is executed on the live stobj in raw Lisp using ; st$cp. :recognizer (stp :logic st$ap :exec st$cp) ; The initial stobj is defined as create-st (a function of no arguments), ; which is defined logically as create-st$a, though create-st$c is invoked to ; create the initial live stobj for st. The :correspondence and :preserved ; keywords refer to proof obligations, discussed below. :creator (create-st :logic create-st$a :exec create-st$c :correspondence create-st{correspondence} :preserved create-st{preserved}) ; Proof obligations are generated that involve a correspondence between the ; new abstract stobj and corresponding concrete stobj. The function ; st$corr, which need not be executable (see :DOC defun-nx), takes two ; arguments, a concrete stobj and an abstract stobj. This function symbol is ; used in the statements of the proof obligations. :corr-fn st$corr ; In this example we have four exports. In each case a new function is ; introduced that has the same signature as its :EXEC function, except that ; st$c is replaced by st. The :LOGIC and :EXEC functions are as specified, ; and the other keywords refer to proof obligations that we discuss below. :exports ((lookup :logic lookup$a :exec mem$ci :correspondence lookup{correspondence} :guard-thm lookup{guard-thm}) (update :logic update$a :exec update-mem$ci :correspondence update{correspondence} :preserved update{preserved} :guard-thm update{guard-thm}) (misc :logic misc$a :exec misc$c :correspondence misc{correspondence}) (update-misc :logic update-misc$a :exec update-misc$c :correspondence update-misc{correspondence} :preserved update-misc{preserved})) :doc nil) Note that all stobj primitives (recognizer, creator, and exported functions) are defined in the ACL2 loop in terms of their :LOGIC functions and in raw Lisp in terms of their :EXEC functions. In the ACL2 loop, a defun form defines a function, while in raw Lisp, a defmacro form defines a macro (for efficiency). We first illustrate how that works for the recognizer. (You can see all the logical and executable definitions by evaluating the form (trace$ defabsstobj-axiomatic-defs defabsstobj-raw-defs) before evaluating the defstobj form.) ; In the ACL2 loop: (defun stp (st) (declare (xargs :guard 't)) (st$ap st)) ; In raw Lisp: (defmacro stp (&rest args) (cons 'st$cp args)) The definitions are made similarly for exported functions, with guards derived from their :LOGIC functions as follows. Consider the exported function update in our example. Its :LOGIC function, update$a, has formals (k val st$a) and the following guard. (and (and (integerp k) (<= 0 k) (<= k 49)) (and (integerp val) (<= 0 val)) (st$ap st$a) (mem$c-entryp val)) The formals of update are obtained by starting with the formals of its :EXEC function, update-mem$ci -- which are (i v st$c) -- and replacing the concrete stobj name st$c by the new stobj name st. The formals of update are thus (i v st). The guard for update is obtained in two steps. The first step is to substitute the formals of update for the formals of update$a in the guard for update$a, to obtain the following. (and (and (integerp i) (<= 0 i) (<= i 49)) (and (integerp v) (<= 0 v)) (st$ap st) (mem$c-entryp v)) The second step is to replace, for each new stobj primitive p, the :LOGIC function for p by p itself. The only :LOGIC function occurring in the formula just above is st$ap, which is the :LOGIC funcction for stp. The guard for update is thus as follows. (and (and (integerp i) (<= 0 i) (<= i 49)) (and (integerp v) (<= 0 v)) (stp st) (mem$c-entryp v)) We turn now to the proof obligations, as promised above. There are three types: :CORRESPONDENCE, :PRESERVED, and :GUARD-THM. All required lemmas may be printed simply by defining the necessary :LOGIC and :EXEC functions and then submitting the defabsstobj event. (To advanced users: also see *note DEFABSSTOBJ-MISSING-EVENTS:: for a utility that returns the required formulas in translated form.) Although the defabsstobj event will fail if the required lemmas have not been proved, first it will print the defthm forms that must be admitted in order to complete submission of the defabsstobj event. The detailed theory explaining the need for these lemmas may be found in a comment in ACL2 source file other-events.lisp, in a comment entitled "Essay on the Correctness of Abstract Stobjs". Here, we give an informal sense of the importance of these lemmas as we present examples of them. Fundamental is the notion of evaluation in the logic versus evaluation using live stobjs, where one imagines tracking the current value of each abstract stobj during each of these two evaluations. We start with the :CORRESPONDENCE lemmas. These guarantee that evaluation in the logic agrees with evaluation using live stobjs, in the sense that the only difference is between a logical stobj and a live stobj, where the two correspond in the sense of the function specified by :CORR-FN. We start with the :CREATOR function where the statement is quite simple, stating that the :CORR-FN holds initially. (defthm create-st{correspondence} (st$corr (create-st$c) (create-st$a))) For the exported functions, there are essentially two cases. If an exported function returns other than the new abstract stobj, then the theorem asserts the equality of the results of applying the :LOGIC and :EXEC functions for the exported function. Hypotheses include the :CORR-FN correspondence followed by the guard for the :LOGIC function, which is stated in terms of the formal parameters of the :EXEC function except using the abstract stobj (here, st) in place of the concrete stobj (here, st$c). The conclusion uses the :EXEC formals, modified in the call of the :LOGIC function (here, lookup$a) to use the abstract stobj, as in the hypotheses. (defthm lookup{correspondence} (implies (and (st$corr st$c st) (integerp i) (<= 0 i) (<= i 49) (st$ap st)) (equal (mem$ci i st$c) (lookup$a i st))) :rule-classes nil) By contrast, if the exported function returns the new abstract stobj, then the conclusion uses the correspondence function insted of EQUAL, as in the following. (defthm update{correspondence} (implies (and (st$corr st$c st) (integerp i) (<= 0 i) (<= i 49) (integerp v) (<= 0 v) (st$ap st) (mem$c-entryp v)) (st$corr (update-mem$ci i v st$c) (update$a i v st))) :rule-classes nil) For exported functions that return multiple values, such conclusions are conjoined together over the returned values. The :PRESERVED lemmas guarantee that updates to the abstract stobj preserve its recognizer. The fact that every exported function has this property provides justification for an optimization performed by ACL2 during generation of proof obligations for guard verification, by assuming that the recognizer always holds. The :PRESERVED lemma for the :CREATOR shows that the recognizer holds initially. (defthm create-st{preserved} (st$ap (create-st$a))) Here is a typical such lemma, for the exported function update. Note that there is no such lemma for lookup, since lookup does not return st. (defthm update{preserved} (implies (and (integerp i) (<= 0 i) (<= i 49) (integerp v) (<= 0 v) (st$ap st) (mem$c-entryp v)) (st$ap (update$a i v st)))) Finally, we consider the :GUARD-THM lemmas. These serve to guarantee that the guard holds for each call of an :EXEC function. During guard verification, logical definitions are used; in particular, since each exported function is defined in the logic as the corresponding call of its :LOGIC function, guard verification shows that each call of the :LOGIC function for an exported function satisfies that function's guard. But why is this true for raw Lisp evaluation using live stobjs, where the :EXEC function is called for an exported function? The :GUARD-THM lemmas provide the answer, as they state that if the :LOGIC function's guard holds, then the :EXEC function's guard holds. Here is an example. Note that the hypotheses come from the correspondence of the concrete and abstract function as guaranteed by the :CORR function, together with the guard of the :LOGIC function; and the conclusion comes from the guard of the :EXEC function. (defthm lookup{guard-thm} (implies (and (st$corr st$c c) (integerp i) (<= 0 i) (<= i 49) (st$ap st)) (and (integerp i) (<= 0 i) (< i (mem$c-length st$c)))) :rule-classes nil) We conclude this EXAMPLE section by showing a short form for the defabsstobj form displayed above. (defabsstobj st :exports ((lookup :exec mem$ci) (update :exec update-mem$ci) misc update-misc)) *SUMMARY DOCUMENTATION* The General Form is as shown below, where the order of keywords is unimportant. Duplicate keywords are discouraged; while permitted, only the first (leftmost) occurrence of a given keyword is used. Only the :exports keyword is required. (defabsstobj st :concrete concrete :recognizer recognizer :creator creator :corr-fn corr-fn :congruent-to congruent-to :protect-default protect-default :exports (e1 ... ek) :doc doc) The keyword argument :EXPORTS must be supplied, and missing or nil keyword arguments have defaults as indicated below. All arguments must satisfy the conditions below. Before we describe the arguments, we define a notion of a "function spec" and its "completion". A function spec is either a symbol or else a list of the form (fn :kwd1 val1 ... :kwdn valn), that is, a symbol followed by a keyword-value-listp. We view the case of a symbol, s, as the function spec (s), with no keywords. There must be no duplicate keywords. In each case that we expect a function spec, the context provides a set of valid keywords for that function spec; it is an error to provide any other keyword in the function spec. Each function spec is interpreted as its "completion", obtained by extending the function spec with a default value for each valid keyword as indicated below. With that interpretation, the "exported function" of a function spec is its car, and that function symbol and each keyword value must be a guard-verified function symbol; and moreover, the :EXEC function must not include the new abstract stobj name, st, among its formals. We are ready to describe the arguments of defabsstobj. St is a symbol, which names the new abstract stobj. Concrete is the name of an existing stobj that is not an abstract stobj, i.e., was introduced with defstobj (not defabsstobj). Recognizer is a function spec (for the recognizer function). The valid keywords are :LOGIC and :EXEC. The default for recognizer is obtained by adding the suffix "P" to name. The default value for :LOGIC is formed by adding the suffix "$AP" to recognizer; for :EXEC, by adding the suffix "$CP". The :EXEC function must be the recognizer for the specified :CONCRETE stobj. Creator is a function spec (for the creator function). The valid keywords are :LOGIC and :EXEC. The default for creator is obtained by adding the prefix "CREATE-" to name. The default value for :LOGIC is formed by adding the suffix "$A" to creator; for :EXEC, by adding the suffix "$C". The :CREATOR function must be the creator for the specified :CONCRETE stobj, as ACL2 checks that the :CREATOR function takes no arguments and returns the :CONCRETE stobj. Corr-fn is a known function symbol that takes two arguments (for the correspondence theorems). The default for corr-fn is obtained by adding the suffix "$CORR" to name. Congruent-to should either be nil (the default) or the name of an abstract stobj previously introduced (by defabsstobj). In the latter case, the current and previous abstract stobj should have the same concrete stobj (not merely congruent concrete stobjs), and their :EXPORTS fields should have the same length and also correspond, as follows: the ith export of each should have the same :LOGIC and :EXEC symbols. See *note DEFSTOBJ:: for more about congruent stobjs. Note that if two names are congruent, then they are either both ordinary stobjs or both abstract stobjs. Protect-default should either be nil (the default) or t. It provides the value of keyword :PROTECT for each member of exports that does not explicitly specify :PROTECT. See the discussion of exports below. An important aspect of the congruent-to parameter is that if it is not nil, then the checks for lemmas -- {CORRESPONDENCE}, {GUARD-THM}, and {PRESERVED} -- are omitted. Thus, the values of keyword :CORR-FN, and the values of keywords :CORRESPONDENCE, :GUARD-THM, and :PRESERVED in each export (as we discuss next), are irrelevant; they are not inferred and they need not be supplied. The value of :EXPORTS is a non-empty true list. Each ei is a function spec (for an exported function). The valid keywords are :LOGIC, :EXEC, :CORRESPONDENCE, and :GUARD-THM, :PROTECT, and also :PRESERVED if and only if the specified :EXEC function returns the :CONCRETE stobj. The default values for all of these keywords except :PROTECT are obtained by respectively adding the suffix "$A" "$C", "{CORRESPONDENCE}", "{GUARD-THM}", or "{PRESERVED}". For :PROTECT, the default is nil unless the defabsstobj event specifies :PROTECT-DEFAULT t. Doc, if non-nil, is a documentation string (see *note DOC-STRING::). Not shown is the keyword, :MISSING; the effect of :missing t is to turn the call of defabsstobj into a corresponding call of defabsstobj-missing-events. Note that a defabsstobj event will fail if the required lemmas -- that is, those for valid keywords :CORRESPONDENCE, :GUARD-THM, and :PRESERVED -- have not been proved, unless proofs are being skipped. The exemption when skipping proofs allows the supporting lemmas to be local to books and encapsulate events. If the ld special ld-skip-proofsp is t, then the missing events are printed with a warning before the defabsstobj event is admitted; but if ld-skip-proofsp is the symbol INCLUDE-BOOK, then that warning is omitted. (Also see *note SKIP-PROOFS:: and see *note LD-SKIP-PROOFSP::.) If however proofs are not being skipped, then the defabsstobj event will fail after printing the missing events. Advanced users may wish to see *note DEFABSSTOBJ-MISSING-EVENTS:: for a utility that returns a data structure containing the missing lemmas. Let st be an abstract stobj with corresponding concrete stobj st$c. let f be an exported function for st and let f$a and f$c be the corresponding :LOGIC and :EXEC functions, respectively. The formals of f are obtained by taking the formals of f$c and replacing st$c by st. The guard for f is derived as follows from the guard of f$a. First, the formals of f$a are replaced by the formals of f in the guard of f$a, to obtain a term we denote here as guard-pre. Now for each exported function symbol g of st with corresponding :LOGIC function g$a, form a functional substitution by consing g$a with g. Finally, apply that functional substitution to guard-pre; the result is the guard of f. That guard must satisfy the usual conditions of a guard: thus, it must return a single non-stobj value and satisfy normal syntactic restrictions, including single-threadedness in its handling of stobjs. Remark. Because of how guards are created for exported functions, and in particular because :LOGIC functions are replaced as discussed above, a good discipline is to define :LOGIC functions that are not intended for general use, but are intended only for use as :LOGIC functions of corresponding stobj primitives. For example, suppose that you use length as the :LOGIC function for some stobj primitive, f (as opposed to using your own function, say, foo-length or foo$a). Then every call of length will be replaced by f when creating the guard of a stobj primitive from the guard of its :LOGIC function. This might not be what you intended if you were using length in that guard simply to compute the length of an ordinary list. There are a few additional restrictions, as follows. All exported function names must be new (unless redefinition is on; see *note LD-REDEFINITION-ACTION::), and there must be no duplicates among them. The :CONCRETE stobj name must be a formal parameter of the :EXEC fn of every function spec, except for the :CREATOR function spec. Also the input signatures of the :LOGIC and :EXEC function for a function spec must agree, except perhaps at the position of that :CONCRETE formal. For function specs other than the :CREATOR function spec, the output signatures of the :LOGIC and :EXEC functions must have the same length and must agree, except perhaps at position p_out of the :CONCRETE stobj in the :EXEC function's output. If p_in is the position of the :CONCRETE stobj in the :EXEC function's formals, then the :LOGIC function's output at position p_out should match the :LOGIC function's formal at position p_in. The :PROTECT keyword is something that you should ignore unless you get an error message about it, pertaining to modifying the concrete stobj non-atomically. In that case, you can eliminate the error by providing :PROTECT t in the function spec, or by providing defabsstobj keyword argument :PROTECT-DEFAULT t at the top level. The above explanation is probably all you need to know about :PROTECT, but just below is a more complete explanation for those who desire it. Further information is also available if you need it; see *note SET-ABSSTOBJ-DEBUG::, and see the example uses of these keywords in community book books/misc/defabsstobj-example-2.lisp. For those who are interested, here is a more detailed discussion of :PROTECT and :PROTECT-DEFAULT, as promised above. It applies to any function spec for an export (hence not to the :CREATOR function spec). If the :EXEC function is a stobj primitive, then clearly the following property holds: any execution of a call of that function can only update the concrete stobj at most once -- i.e., modification of the concrete stobj is atomic. ACL2 can deduce this property not only for stobj primitives but for many other functions as well. However, if ACL2 cannot deduce this property, then it will cause an error saying that the :EXEC function "appears capable of modifying the concrete stobj, , non-atomically." That message also explains how to eliminate this error: provide :PROTECT t for the function spec. Alternatively, all function specs without an explicit :PROTECT keyword can be implicitly supplied :PROTECT t by supplying the value t for the :PROTECT-DEFAULT keyword parameter of the defabsstobj event. However, beware that when :PROTECT is t, the generated raw Lisp code runs slightly less efficiently -- though perhaps with negligible efficiency loss if the :EXEC function is not trivial. Community books books/misc/defabsstobj-example-3.lisp and books/misc/defabsstobj-example-4.lisp provide related information. We conclude with some remarks. Unlike defstobj, there is no :renaming argument. Instead, the scheme described above provides a flexible way to assign names. Those who use the experimental extension ACL2(h), which includes function memoization (see *note MEMOIZE::), may be aware that the memo table for a function is flushed whenever it is the case that one of its stobj inputs is updated. In fact, such flushing happens even when a stobj that is congruent to one of its stobj inputs is updated. For that purpose, an abstract stobj is considered to be congruent to its corresponding concrete stobj.  File: acl2-doc-emacs.info, Node: DEFABSSTOBJ-MISSING-EVENTS, Next: DEFATTACH, Prev: DEFABSSTOBJ, Up: EVENTS DEFABSSTOBJ-MISSING-EVENTS obtain the events needed to admit a defabsstobj event We assume familiarity with defabsstobj. Defabsstobj-missing-events is a macro is for advanced users (who, for example, understand the role of the translate and untranslate functions), who want programmatic access to the defthm events required to admit a specific defabsstobj event. This macro has the same syntax as defabsstobj -- to use it, just replace a call of defabsstobj by a call of defabsstobj-missing-events on the same arguments. The result is an error triple (mv erp val state). If erp is nil, then val is the list of all objects (name formula . old-formula), where a defthm event named name remains to be admitted whose translated formula is formula, and where old-formula is nil unless the indicated event already exists (hence with a different formula), in which case old-formula is the existing translated formula. To build a defthm event from the above value, val, we suggest evaluating a form like (untranslate formula t (w state)).  File: acl2-doc-emacs.info, Node: DEFATTACH, Next: DEFAXIOM, Prev: DEFABSSTOBJ-MISSING-EVENTS, Up: EVENTS DEFATTACH execute constrained functions using corresponding attached functions This documentation topic is organized into the following sections: *Introductory example.* *Syntax and semantics of defattach.* *Three primary uses of defattach.* *Miscellaneous remarks, with discussion of possible user errors.* Please see *note ENCAPSULATE:: if you intend to use defattach but are not already familiar with the use of encapsulate to introduce constrained functions. See community book books/misc/defattach-example.lisp for a small example. it illustrates how defattach may be used to build something like "higher-order" programs, in which constrained functions may be refined to different executable functions. More uses of defattach may be found in the ACL2 source code, specifically, file boot-strap-pass-2.lisp. The argument :skip-checks t enables easy experimentation with defattach, by permitting use of :program mode functions and the skipping of semantic checks. Also permitted is :skip-checks nil (the default) and :skip-checks :cycles, which turns off only the update of the extended ancestor relation (see below) and hence the check for cycles in this relation; see below. We do not make any logical claims when the value of :skip-checks is non-nil; indeed, a trust tag is required in this case (see *note DEFTTAG::). Remark for those who use the experimental HONS extension (see *note HONS-AND-MEMOIZATION::): the interaction of memoization and attachments is not tracked for attachments introduced with a non-nil value of :skip-checks. For more discussion of :skip-checks t, see *note DEFPROXY::; we do not discuss :skip-checks further, here. *Introductory example.* We begin with a short log illustrating the use of defattach. Notice that after evaluating the event (defattach f g), a call of the constrained function f is evaluated by instead calling g on the arguments. ACL2 !>(encapsulate ((f (x) t :guard (true-listp x))) (local (defun f (x) x)) (defthm f-property (implies (consp x) (consp (f x))))) [... output omitted ...] T ACL2 !>(defun g (x) (declare (xargs :guard (or (consp x) (null x)))) (cons 17 (car x))) [... output omitted ...] G ACL2 !>(f '(3 4)) ; undefined function error ACL2 Error in TOP-LEVEL: ACL2 cannot ev the call of undefined function F on argument list: ((3 4)) To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !>(defattach f g) [... output omitted ...] :ATTACHMENTS-RECORDED ACL2 !>(f '(3 4)) ; f is evaluated using g (17 . 3) ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f '(3 4)) ; f is evaluated using g 1> (ACL2_*1*_ACL2::F (3 4)) 2> (ACL2_*1*_ACL2::G (3 4)) 3> (G (3 4)) <3 (G (17 . 3)) <2 (ACL2_*1*_ACL2::G (17 . 3)) <1 (ACL2_*1*_ACL2::F (17 . 3)) (17 . 3) ACL2 !>(defattach f nil) ; unattach f (remove its attachment) [... output omitted ...] :ATTACHMENTS-RECORDED ACL2 !>(f '(3 4)) ; undefined function error once again 1> (ACL2_*1*_ACL2::F (3 4)) ACL2 Error in TOP-LEVEL: ACL2 cannot ev the call of undefined function F on argument list: ((3 4)) To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !> *Syntax and semantics of defattach.* The log above shows that the event (defattach f g) allows g to be used for evaluating calls of f. From a logical perspective, the evaluation takes place in the addition to the current session of an "attachment equation" axiom (universally quantified over all x) for each defattach event: (equal (f x) (g x)) ;;; attachment equation axiom for (defattach f g) Below we explain defattach in some detail. But it is important to keep in mind that evaluation with the attachment equations takes place in an extension of the logical theory of the session. ACL2 guarantees that this so-called "evaluation theory" remains consistent, assuming the absence of defaxiom events from the user. This guarantee is a consequence of a more general guarantee: an ACL2 logical world exists in which (loosely speaking) the attachment equation for (defattach f g), as (defun f (...) (g ...)), takes the place of the original defining event for f, for each defattach event. This more general guarantee holds even if there are defaxiom events, though as explained below, no function symbol that syntactically supports a defaxiom formula is allowed to get an attachment. A deeper discussion of the logical issues is available (but not intended to be read by most users) in a long comment in the ACL2 source code labeled "Essay on Defattach." Example Forms: (defattach f g) ; call g in place of calling constrained function f (defattach (f g)) ; same as just above (defattach (f g :hints (("Goal" :in-theory (enable foo))))) ; equivalent to first form above, except with hints for the ; proof that the guard of f implies the guard of g (defattach (f g :hints (("Goal" :in-theory (enable foo))) :otf-flg t)) ; as above, except with an :otf-flg of t for the proof that ; the guard of f implies the guard of g (defattach (f g) :hints (("Goal" :use my-thm))) ; equivalent to first form above, except with hints for the ; proof that the constraints on f hold for g (defattach (f g) :hints (("Goal" :use my-thm)) :otf-flg t) ; as above, except with an :otf-flg of t for the proof that ; the constraints on f hold for g (defattach (f g) (h j)) ; Attach g to f and attach j to h (defattach (f g :attach nil) (h j)) ; Same as just above, including the same proof obligations, ; except for one difference: because of :attach nil, calls ; of f will not be evaluated, i.e., there will be no ; executable attachment of g to f (defattach (f nil) (h j)) ; Attach j to h and unattach f (defattach (f g :hints (("Goal" :in-theory (enable foo)))) (h j :hints (("Goal" :in-theory (enable bar)))) :hints (("Goal" :use my-thm))) ; Attach g to f and attach j to h, with hints: ; - For proving that the guard of f implies the guard of g, ; enable foo; ; - For proving that the guard of h implies the guard of j, ; enable bar; and ; - For proving that the constraints on f and h hold for ; g and j (respectively), use theorem my-thm. (defattach f nil) ; remove the attachment of f, if any (e.g., g above) (defattach (f nil)) ; same as just above General Forms: (defattach f g) ; single attach or, if g is nil, unattach (defattach (f1 g1 :kwd val ...) ... (fk gk :kwd' val' ...) :kwd'' val'' ...) where each indicated keyword-value pair is optional and each keyword is one of :ATTACH, :HINTS, :OTF-FLG, or :INSTRUCTIONS. The value of each :ATTACH keyword is either t or nil, with default t except that the value of :ATTACH at the "top level," after each entry (fi gi ...), is the default for each :ATTACH keyword supplied in such an entry. We discuss the :ATTACH keyword later in this documentation topic. The associated values for the other keywords have the usual meanings for the proof obligations described below: the guard proof obligation for keywords within each (fi gi ...) entry, and the constraint proof obligation for keywords at the top level. No keyword may occur twice in the same context, i.e., within the same (fi gi ...) entry or at the top level; and :INSTRUCTIONS may not occur in the same context with :HINTS or :OTF-FLG. The first General Form above is simply an abbreviation for the form (defattach (f g)), which is an instance of the second General Form above. For the second General Form we say that gi is "attached to" fi (by the defattach event) if gi is not nil, and otherwise we say that fi is "unattached" (by the defattach event). It is also convenient to refer to as an "attachment pair" (of the event) if gi is not nil. We may refer to the set of fi as the "attachment nest" of each fi. We start with a brief introduction to the first General Form in the case that g is not nil. This form arranges that during evaluation, with exceptions noted below, every call of the constrained function symbol f will in essence be replaced by a call of the function symbol g on the same arguments. We may then refer to g as the "attachment of" f, or say that "g is attached to f." Notable exceptions, where we do not use attachments during evaluation, are for macroexpansion, evaluation of defconst and defpkg terms, evaluation during table events, some stobj operations including all updates, and especially evaluation of ground terms (terms without free variables) during proofs. However, even for these cases we allow the use of attachments in the first argument of prog2$ and, more generally, the next-to-last (i.e., second) argument of return-last when its first argument is not of the form 'm for some macro, m. To see why attachments are disallowed during evaluation of ground terms during proofs (except for the prog2$ and return-last cases mentioned above), consider the following example. (defstub f (x) t) (defun g (x) (+ 3 x)) (defattach f g) If the form (f 2) is submitted at the ACL2 prompt, the result will be 5 because the attachment g of f is called on the argument, 2. However, during a proof the term (f 2) will not be simplified to 5, since that would be unsound, as there are no axioms about f that would justify such a simplification. For the case that g is nil in the first General Form above, the result is the removal of the existing attachment to f, if any. After this removal, calls of f will once again cause errors saying that "ACL2 cannot ev the call of undefined function f ...". In this case not only is the previous attachment to f removed; moreover, for every function symbol f' in the attachment nest of f in the defattach event that introduced the existing attachment to f, then f' is unattached. (An example near the end of this documentation topic shows why this unattachment needs to be done.) Such removal takes place before the current defattach is processed, but is restored if the new event fails to be admitted. We focus henceforth on the second General Form. There must be at least one attachment, i.e., i must be at least 1. All keywords are optional; their role is described below. The fi must be distinct constrained function symbols, that is, function symbols all introduced in signatures of encapsulate events (or macros such as defstub that generate encapsulate events). Each non-nil gi is a :logic-mode function symbol that has had its guards verified, with the same signature as fi (though formal parameters for fi and gi may have different names). (Note: The macro defattach!, defined in community book books/misc/defattach-bang, avoids this restriction.) This event generates proof obligations and an ordering check, both described below. The effect of this event is first to remove any existing attachments for all the function symbols fi, as described above for the first General Form, and then to attach each gi to fi. Proof obligations must be checked before making attachments. For this discussion we assume that each gi is non-nil (otherwise first remove all attachment pairs for which gi is nil). Let s be the functional substitution mapping each fi to gi. For any term u, we write u\s for the result of applying s to u; that is, u\s is the "functional instance" obtained by replacing each fi by gi in u. Let G_fi and G_gi be the guards of fi and gi, respectively. Let G_fi' be the result of replacing each formal of fi by the corresponding formal of gi in G_fi. ACL2 first proves, for each i (in order), the formula (implies G_fi' G_gi)\s. If this sequence of proofs succeeds, then the remaining formula to prove is the functional instance C\s of the conjunction C of the constraints on the symbols fi; see *note CONSTRAINT::. This last proof obligation is thus similar to the one generated by functional instantiation (see *note CONSTRAINT::). As with functional instantiation, ACL2 stores the fact that such proofs have been done so that they are avoided in future events (see *note LEMMA-INSTANCE::). Thus, you will likely avoid some proofs with the sequence (defattach f g) (defattach f nil) (defattach f g) (defattach f nil) ... rather than the sequence: (defattach f g) :u (defattach f g) :u ... It remains to describe an ordering check. We begin with the following motivating example. (defstub f (x) t) ; constrained function with no constraints (defun g (x) (declare (xargs :guard t)) (not (f x))) (defattach f g) ; ILLEGAL! Were the above defattach event to succeed, the evaluation theory (discussed above) would be inconsistent: (f x) equals (g x) by the new attachment equation, which in turn equals (not (f x)) by definition of g. The evaluation would therefore be meaningless. Also, from a practical perspective, there would be an infinite loop resulting from any call of f. We consider a function symbol g to be an "extended immediate ancestor of" a function symbol f if either of the following two criteria is met: (a) g occurs in the formula that introduces f (i.e., definition body or constraint) and g is introduced by an event different from (earlier than) the event introducing f; or (b) g is attached to f. For a proposed defattach event, we check that this relation has no cycles, where for condition (b) we include all attachment pairs that would result, including those remaining from earlier defattach events. Of course, a special case is that no function symbol may be attached to itself. Similarly, no function symbol may be attached to any of its "siblings" -- function symbols introduced by the same event -- as siblings are considered equivalent for purposes of the acyclicity check. *Three primary uses of defattach.* We anticipate three uses of defattach: (1) Constrained function execution (2) Sound modification of the ACL2 system (3) Program refinement We discuss these in turn. (1) The example at the beginning of this documentation illustrates constrained function execution. (2) ACL2 is written essentially in itself. Thus, there is an opportunity to attaching to system functions. For example, encapsulated function too-many-ifs-post-rewrite, in the ACL2 source code, receives an attachment of too-many-ifs-post-rewrite-builtin, which implements a heuristic used in the rewriter. To find all such examples, search the source code for the string `-builtin'. Over time, we expect to continue replacing ACL2 source code in a similar manner. We invite the ACL2 community to assist in this "open architecture" enterprise; feel free to email the ACL2 implementors if you are interested in such activity. (3) Recall that for an attachment pair , a proof obligation is (speaking informally) that g satisfies the constraint on f. Yet more informally speaking, g is "more defined" than f; we can think of g as "refining" f. With these informal notions as motivation, we can view defattach as providing refinement though the following formal observation: the evaluation theory extends the theory of the ACL2 session, specifically by the addition of all attachment equations. For the logic-inclined, it may be useful to think model-theoretically: The class of models of the evaluation theory is non-empty but is a subset of the class of models of the current session theory. *Miscellaneous remarks, with discussion of possible user errors.* We conclude with remarks on some details. A defattach event is never redundant (see *note REDUNDANT-EVENTS::); in that sense it is analogous to in-theory. As mentioned above, the use of attachments is disabled for evaluation of ground terms during proofs. However, attachments can be used on code during the proof process, essentially when the "program refinement" is on theorem prover code rather than on functions we are reasoning about. The attachment to too-many-ifs-post-rewrite described above provides one example of such attachments. Meta functions and clause-processor functions can also have attachments, with the restriction that no common ancestor with the evaluator can have an attachment; see *note EVALUATOR-RESTRICTIONS::. For an attachment pair , evaluation of f never consults the guard of f. Rather, control passes to g, whose guard is checked if necessary. The proof obligation related to guards, as described above, guarantees that any legal call of f is also a legal call of g. Thus for guard-verified code that results in calls of f in raw Lisp, it is sound to replace these calls with corresponding calls of g. Defattach events are illegal inside any encapsulate event with a non-empty signature unless they are local to the encapsulate. We next discuss a restriction based on a notion of a function symbol syntactically supporting an event. Function symbol f is _ancestral_ in event E if either f occurs in E, or (recursively) f occurs in an event E' that introduces some function symbol g that is ancestral in E. We require that no function symbol ancestral in the formula of a defaxiom event may have an attachment. Theoretical reasons are discussed in comments in the ACL2 source code, but here we give a little example showing the need for some such restriction: without it, we show how to prove nil! (defn g1 () 1) (defn g2 () 2) (defstub f1 () t) (defstub f2 () t) (defund p (x) (declare (ignore x)) t) (defevaluator evl evl-list ((p x))) (defaxiom f1-is-f2 (equal (f1) (f2))) (defun meta-fn (x) (cond ((equal (f1) (f2)) x) (t *nil*))) (defthm bad-meta-rule (equal (evl x a) (evl (meta-fn x) a)) :rule-classes ((:meta :trigger-fns (p)))) (defattach f1 g1) (defattach f2 g2) (defthm contradiction nil :hints (("Goal" :use ((:instance (:theorem (not (p x))) (x t))))) :rule-classes nil) To see all attachments: (all-attachments (w state)). (Note that attachments introduced with a non-nil value of :skip-checks will be omitted from this list.) Next we discuss the :ATTACH keyword. There is rarely if ever a reason to specify :ATTACH T, but the following (admittedly contrived) example shows why it may be necessary to specify :ATTACH NIL. First we introduce three new function symbols. (defstub f (x) t) (defun g (x) (f x)) (encapsulate ((h (x) t)) (local (defun h (x) (g x))) (defthm h-prop (equal (h x) (g x)))) Now suppose we want to attach the function acl2-numberp to both f and h. (defattach (f acl2-numberp) (h acl2-numberp)) Such an attempt fails, because the following constraint is generated but is not a theorem: (EQUAL (ACL2-NUMBERP X) (G X)). Clearly we also need to attach to g as well. (defattach (f acl2-numberp) (h acl2-numberp) (g acl2-numberp)) But this fails for a different reason, as explained by the error message: ACL2 Error in ( DEFATTACH (F ACL2-NUMBERP) ...): It is illegal to attach to function symbol G, because it was introduced with DEFUN. See :DOC defattach. That is: logically, we need to attach acl2-numberp to g, but we cannot actually attach to g because it was introduced with defun, not with encapsulate. So we specify :ATTACH NIL for the attachment to g, saying that no actual attachment should be made to the code for g, even though for logical purposes we should consider that g has been given the indicated attachment. (defattach (f acl2-numberp) (h acl2-numberp) (g acl2-numberp :attach nil)) Finally, we can check that f, g, and h execute as expected. ACL2 !>(assert-event (and (f 3) (not (f t)) (g 3) (not (g t)) (h 3) (not (h t)))) :PASSED ACL2 !> We conclude with an example promised above, showing why it is necessary in general to unattach all function symbols in an existing attachment nest when unattaching any one of those function symbols. Consider the following example. (defstub f1 () t) (encapsulate ((f2 () t)) (local (defun f2 () (f1))) (defthm f2=f1 (equal (f2) (f1)))) (encapsulate ((f3 () t)) (local (defun f3 () (f1))) (defthm f3=f1 (equal (f3) (f1)))) (defun four () (declare (xargs :guard t)) 4) (defun five () (declare (xargs :guard t)) 5) (defattach (f1 four) (f2 four)) (defattach (f1 five) (f3 five)) The second defattach replaces erases the existing attachment pair before installing the new attachment pairs and . After the second defattach, both (f1) and (f3) evaluate to 5. Now suppose that the attachment pair were not erased. Then we would have (f1) evaluating to 5 and (f2) evaluating to 4, contradicting the constraint f2=f1. The evaluation theory would thus be inconsistent, and at a more concrete level, the user might well be surprised by evaluation results if the code were written with the assumption specified in the constraint f2=f1.  File: acl2-doc-emacs.info, Node: DEFAXIOM, Next: DEFCHOOSE, Prev: DEFATTACH, Up: EVENTS DEFAXIOM add an axiom WARNING: We strongly recommend that you not add axioms. If at all possible you should use defun or mutual-recursion to define new concepts recursively or use encapsulate to constrain them constructively. If your goal is to defer a proof by using a top-down style, consider using skip-proofs; see the discussion on "Top-Down Proof" in Section B.1.2 of "Computer-Aided Reasoning: An Approach." Adding new axioms frequently renders the logic inconsistent. Example: (defaxiom sbar (equal t nil) :rule-classes nil :doc ":Doc-Section ...") General Form: (defaxiom name term :rule-classes rule-classes :doc doc-string) where name is a new symbolic name (see *note NAME::), term is a term intended to be a new axiom, and rule-classes and doc-string are as described in the corresponding documentation topics . The two keyword arguments are optional. If :rule-classes is not supplied, the list (:rewrite) is used; if you wish the axiom to generate no rules, specify :rule-classes nil.  File: acl2-doc-emacs.info, Node: DEFCHOOSE, Next: DEFCONG, Prev: DEFAXIOM, Up: EVENTS DEFCHOOSE define a Skolem (witnessing) function Examples: (defchoose choose-x-for-p1-and-p2 (x) (y z) (and (p1 x y z) (p2 x y z))) (defchoose choose-x-for-p1-and-p2 x (y z) ; equivalent to the above (and (p1 x y z) (p2 x y z))) ; The following is as above, but strengthens the axiom added to pick a sort ; of canonical witness, as described below. (defchoose choose-x-for-p1-and-p2 x (y z) (and (p1 x y z) (p2 x y z)) :strengthen t) (defchoose choose-x-and-y-for-p1-and-p2 (x y) (z) (and (p1 x y z) (p2 x y z))) * Menu: * CONSERVATIVITY-OF-DEFCHOOSE:: proof of conservativity of defchoose General Form: (defchoose fn (bound-var1 ... bound-varn) (free-var1 ... free-vark) body :doc doc-string :strengthen b), where fn is the symbol you wish to define and is a new symbolic name (see *note NAME::), (bound-var1 ... bound-varn) is a list of distinct `bound' variables (see below), (free-var1 ... free-vark) is the list of formal parameters of fn and is disjoint from the bound variables, and body is a term. The use of lambda-list keywords (such as &optional) is not allowed. The documentation string argument, :doc doc-string, is optional; for a description of the form of doc-string see *note DOC-STRING::. The :strengthen keyword argument is optional; if supplied, it must be t or nil. The system treats fn very much as though it were declared in the signature of an encapsulate event, with a single axiom exported as described below. If you supply a :use hint (see *note HINTS::), :use fn, it will refer to that axiom. No rule (of class :rewrite or otherwise; see *note RULE-CLASSES::) is created for fn. Defchoose is only executed in defun-mode :logic; see *note DEFUN-MODE::. Also see *note DEFUN-SK::. In the most common case, where there is only one bound variable, it is permissible to omit the enclosing parentheses on that variable. The effect is the same whether or not those parentheses are omitted. We describe this case first, where there is only one bound variable, and then address the other case. Both cases are discussed assuming :strengthen is nil, which is the default. We deal with the case :strengthen t at the end. The effect of the form (defchoose fn bound-var (free-var1 ... free-vark) body) is to introduce a new function symbol, fn, with formal parameters (free-var1 ... free-vark). Now consider the following axiom, which states that fn picks a value of bound-var so that the body will be true, if such a value exists: (1) (implies body (let ((bound-var (fn free-var1 ... free-vark))) body)) This axiom is "clearly conservative" under the conditions expressed above: the function fn simply picks out a "witnessing" value of bound-var if there is one. For a rigorous statement and proof of this conservativity claim, see *note CONSERVATIVITY-OF-DEFCHOOSE::. Next consider the case that there is more than one bound variable, i.e., there is more than one bound-var in the following. (defchoose fn (bound-var1 ... bound-varn) (free-var1 ... free-vark) body) Then fn returns a multiple value with n components, and formula (1) above is expressed using mv-let as follows: (implies body (mv-let (bound-var1 ... bound-varn) (fn free-var1 ... free-vark) body)) We now discuss the case that :strengthen t is supplied. For simplicity we return to our simplest case, with defchoose applied to function fn, a single free variable y, and a single bound variable bound-var. The idea is that if we pick the "smallest" witnessing bound-var for two different free variables y and y1, then either those two witnesses are the same, or else one is less than the other, in which case the smaller one is a witness for its free variable but not for the other. (See comments in source function defchoose-constraint-extra for more details.) Below, body1 is the result of replacing y by y1 in body. (2) (or (equal (fn y) (fn y1)) (let ((bound-var (fn y))) (and body (not body1))) (let ((bound-var (fn y1))) (and body1 (not body)))) An important application of this additional axiom is to be able to define a "fixing" function that picks a canonical representative of each equivalence class, for a given equivalence relation. The following events illustrate this point. (encapsulate ((equiv (x y) t)) (local (defun equiv (x y) (equal x y))) (defequiv equiv)) (defchoose efix (x) (y) (equiv x y) :strengthen t) (defthm equiv-implies-equal-efix-1 (implies (equiv y y1) (equal (efix y) (efix y1))) :hints (("Goal" :use efix)) :rule-classes (:congruence)) (defthm efix-fixes (equiv (efix x) x) :hints (("Goal" :use ((:instance efix (y x)))))) If there is more than one bound variable, then (2) is modified in complete analogy to (1) to use mv-let in place of let. Comment for logicians: As we point out in the documentation for defun-sk, defchoose is "appropriate," by which we mean that it is conservative, even in the presence of epsilon-0 induction. For a proof, See *note CONSERVATIVITY-OF-DEFCHOOSE::.  File: acl2-doc-emacs.info, Node: CONSERVATIVITY-OF-DEFCHOOSE, Prev: DEFCHOOSE, Up: DEFCHOOSE CONSERVATIVITY-OF-DEFCHOOSE proof of conservativity of defchoose This documentation topic provides underlying theory. It is of theoretical interest only; it has no relationship to the effective use of ACL2. The argument below for the conservativity of defchoose replaces the terse and somewhat misleading reference to a forcing argument in Appendix B of the paper by ACL2 authors Kaufmann and Moore, "Structured Theory Development for a Mechanized Logic" (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203). Our basic idea is to to take a (countable) first-order structure for ACL2, M, together with a function symbol, f, introduced by defchoose, and find a way to expand M with an interpretation of f (without changing the universe of M) so that e0-induction continues to hold in the expansion. A remark at the end of this documentation topic shows why care is necessary. A concept called "forcing", originally introduced by Paul Cohen for set theory, has long since been adapted by logicians (in a simplified form) to model theory. This simplified model-theoretic forcing provides the means for making our careful expansion. The forcing argument presented below is intended to be completely self-contained for those familiar with basic first-order logic and ACL2. No background in forcing (model-theoretic or otherwise) is expected, though we do expect a rudimentary background in first-order logic and familiarity with the following. Preliminaries. We write s[p<-p0] to denote the result of extending or modifying the assignment s by binding p to p0. Now let A be a subset of the universe U of a first-order structure M. A is said to be "first-order definable with parameters" in M if for some formula phi, variable x, and assignment s binding the free variables of phi except perhaps for x, A = {a \in U: M |= phi[s[x<-a]]. Note that we are writing "\in" to denote set membership. Finally, we indicate the end of a proof (or of a theorem statement, when the proof is omitted) with the symbol "-|". We gratefully acknowledge very helpful feedback from John Cowles, who found several errors in a draft of this note and suggested the exercises. We also thank Ruben Gamboa for helpful feedback, and we thank Jim Schmerl for an observation that led us directly to this proof in the first place. We are given a consistent first-order theory T, extending the ACL2 ground-zero theory, that satisfies the e0-induction scheme. We wish to show that the extension of T by the following arbitrary defchoose event is conservative, where g is a new function symbol. (defchoose g ) Note that by "the extension of T" here we mean the extension of T by not only the new defchoose axiom displayed just below, but also the addition of e0-induction axioms for formulas in the language with the new defchoose function symbol, g. -> (LET = g() in ) By definition of conservativity, since proofs are finite, it clearly suffices to consider an arbitrary finite subset of T. Then by the completeness, soundness, and downward Lowenheim-Skolem theorems of first-order logic, it suffices to show that an arbitrary countable model of T can be expanded (i.e., by interpreting the new symbol g without changing the universe of the model) to a model of the corresponding defchoose axiom above, in which all e0-induction axioms hold in the language of that model. Below, we will carry out a so-called _forcing_ construction, which allows us to expand any countable model M of T to a model M[G] that satisfies e0-induction and also satisfies the above axiom generated from the above defchoose event. The ideas in this argument are standard in model theory; no novelty is claimed here. Fix a countable model M of a theory T that satisfies e0-induction and extends the ACL2 ground-zero theory. Also fix the above defchoose axiom, where g is not in the language of T. We start by defining a partial order P as follows. Let Nb and Nf be the lengths of and , respectively. P consists of all fn in M such that the following formula is true in M. Roughly speaking, it says that fn is a finite function witnessing the above requirement for g. alistp(fn) & no-duplicatesp-equal(strip-cars(fn)) & (forall , . (member-equal(cons(,), fn) -> (length() = Nb & length() = Nf & ((exists . ) -> )))) P is ordered by subset, i.e., we say that p2 _extends_ p1 if p1 is a subset (not necessarily proper) of p2 (more precisely, M |= subsetp-equal(p1,p2)). Remark. The original argument in Appendix B of the aforementioned paper can essentially be salvaged, as we now show. The key observation is that the particular choice of P is nearly irrelevant for the argument that follows below. In particular, we can instead define P to consist of finite one-one functions with domain contained in the set of natural numbers. More precisely, consider the following definitions. (defun function-p (fn) (declare (xargs :guard t)) (and (alistp fn) (no-duplicatesp-equal (strip-cars fn)))) (defun nat-listp (l) (declare (xargs :guard t)) (cond ((atom l) (eq l nil)) (t (and (natp (car l)) (nat-listp (cdr l)))))) (defun nat-function-p (x) (and (function-p x) (nat-listp (strip-cars x)))) and define inverse as follows. (defun inverse (fn) (declare (xargs :guard (alistp fn))) (if (endp fn) nil (cons (cons (cdar fn) (caar fn)) (inverse (cdr fn))))) Then P may instead be defined to consist of those fn for which nat-function-p(fn) & function-p(inverse(fn)). With this alternate definition of P, the argument below then goes through virtually unchanged, and we get an expansion M[G] of M in which there is a definable enumeration of the universe. The conservativity of defchoose then follows easily because the function being introduced can be defined explicitly using that enumeration (namely, always pick the least witness in the sense of the enumeration). End of Remark. Next we present the relevant forcing concepts from model theory. A _dense_ subset of P is a subset D of P such that for every p \in P, there is d \in D such that d extends p. A subset G of P is _generic_ with respect to a collection Ds of dense subsets of P, also written "G is Ds-generic," if G is closed under subset (if p2 \in G and p2 extends p1 then p1 \in G), G is pairwise compatible (the union-equal of any two elements of G is in G), and every set in Ds has non-empty intersection with G. For p \in P, we say that a subset D of P is _dense beyond_ p if for all p1 extending p there exists p2 extending p1 such that p2 \in D. This notion makes sense even for D not a subset of P if we treat elements of D not in P as nil. Proposition 1. For any partial order P and countable collection Ds of dense subsets of P, there is a Ds-generic subset of P. Proof. Let Ds = {D0,D1,D2,...}. Define a sequence such that for all i, p_i \in Di and p_(i+1) extends p_i. Let G = {p \in P: for some i, pi extends p}. Then G is Ds-generic. -| Note that P is first-order definable (with parameters) in M. Let Df be the set of dense subsets of P that are first-order definable (with parameters) in M. A standard argument shows there are only countably many first-order definitions with parameters in a countable model M -- for example, we can Goedel number all terms and then all formulas -- hence, Df is countable. By Proposition 1, let G be Df-generic. Notice that for any list x of length Nb in M, the set of elements f of P for which x is in the domain of f is dense and first-order definable. We may thus define a function g0 as follows: g0(x_1,...,x_Nb) = y if there is some element of G containing the pair ((x_1 ... x_Nb) . y). It is easy to see that g0 is a total function on M. Let L be the language of T and let L[g] be the union of L with a set containing a single new function symbol, g. Let M[G] be the expansion of M to L[g] obtained by interpreting g to be g0 (see also Proposition 5 below). So now we have fixed M, P, Df, G, and g0, where G is Df-generic. Proposition 2. Let Df be the set of dense subsets of P that are first-order definable (with parameters) in M. Suppose that p \in G and D \in Df. Then for some q \in G extending p, q \in D. Proof. Let D0 be the set of p' \in D that either extend p or have no extension in D that extends p. We leave it as a straightforward exercise to show that D0 is dense, and D0 is clearly first-order definable (with parameters) in M. So by genericity of G, we may pick q \in D0 such that q \in G. Thus q \in D. By definition of generic, some extension q1 of both p and q belongs to G. Pick q2 \in D extending q1; thus q has an extension in D that extends p (namely, q2), so by definition of D0, q extends p. -| Definition of forcing. Let phi(x1,...,xk) be a first-order formula in L[g] and let p \in P. We define a formula of L, denoted "p ||- phi" ("p forces phi"), by recursion on phi (in the metatheory) as follows. (Here, we view "or" and "forall" as abbreviations.) If phi is atomic, then let phi'(A) be the result of replacing, inside-out, each subterm of the form g(x_1,...,x_Nb) with the term (cdr (assoc-equal (list x_1 ... x_Nb) A)), where A is neither p nor a variable occurring in phi. Then p ||- phi is defined as follows: "The set {A \in P: A extends p and phi'(A)} is dense beyond p". That is, p ||- phi is the following formula: (forall p1 \in P extending p) (exists p2 \in P extending p1) phi'(p2). p ||- ~phi is: (forall p' \in P extending p) ~(p' ||- phi) p ||- phi_1 & phi_2 is: (p ||- phi_1) & (p ||- phi_2) p ||- (exists x) phi is: (exists x) (p ||- phi) We will need the following definition later. Definition. p ||-w phi (p _weakly forces_ phi) is an abbreviation for p ||- ~~phi. The following exercises were suggested by John Cowles as a means for gaining familiarity with the definition of forcing. Exercise 1. Consider the formula (phi_1 OR phi_2) as an abbreviation for ~(~phi_1 & ~phi_2), Show that p ||- (phi_1 OR phi_2) is equivalent to the following. (forall p' \in P extending p) (exists p'' \in P extending p') ((p'' ||- phi_1) OR (p'' ||- phi_2)) Exercise 2. Consider the formula (forall x)phi as an abbreviation for ~(exists x)~phi, Show that p ||- (forall x)phi is equivalent to the following. (forall x) (forall p1 \in P extending p) (exists p2 \in P extending p1) (p2 ||- phi). Exercise 3. Prove that p ||-w phi is equivalent to the following. (forall p' \in P extending p) (exists p'' \in P extending p') (p'' ||- phi). Exercise 4. Let phi be a formula of L[g]. Prove: M |= (p ||- phi)[s[p<-p0]] implies M |= (p ||-w phi)[s[p<-p0]]. Exercise 5. Let phi be a formula of L[g]. Prove: M |= (p ||- ~phi)[s[p<-p0]] iff M |= (p ||-w ~phi)[s[p<-p0]]. [End of exercises.] The definition of forcing stipulates how to view "p ||- phi(x1,...,xk)" as a new formula theta(p,x1,...,xk). That is, "||-" transforms formulas, so for any first-order formula phi, "p ||- phi" is just another first-order formula. That observation shows that a formula such as ((p ||- phi) OR (p ||- ~phi)) is really just another first-order formula. The following proposition thus follows easily. Proposition 3. For any formula phi of L[g], {p0: M |= ((p ||- phi) OR (p ||- ~phi))[s[p<-p0]]]} is a dense subset of P, which (since it is first-order definable with parameters in M) intersects G. -| The following proposition is easily proved by a structural induction on phi, and is left to the reader. Proposition 4. Let phi be a formula of L[g]. Suppose p0 in P, p1 in P, M |= (p ||- phi)[s[p<-p0]] and p1 extends p0. Then M |= (p ||- phi)[s[p<-p1]]. -| We will also need the following. Proposition 5. The following is dense for any finite set S of Nb-tuples: {p \in P: for some \in S, (list x_1 ... x_Nb) \in strip-cars(p)}. Thus, the function g0 is a total function. -| The next lemma tells us that the sentences true in M[G] are those that are forced by an element of G. Truth Lemma. Let phi be a formula in L[g], let s be an assignment to the free variables of phi, and let p be a variable not in the domain of s. Then M[G] |= phi[s] iff for some p0 \in G, M |= (p ||- phi)[s[p<-p0]]. Proof. The proof is by induction on the structure of phi. First suppose phi is atomic. Let D* be the set of elements p0 \in P such that every assoc-equal evaluation from the definition of forcing phi returns a pair when A is bound to p0. (Intuitively, this means that p0 is a sufficiently large approximation from any G containing p0 to make sense of phi in M[G].) We make the following claim. (*) For all p0 \in G such that p0 \in D*, M[G] |= phi[s] iff M |= (p ||- phi)[s[p<-p0]]. To prove the claim, fix p0 in both G and D*, and recall the function g0 constructed from G in the definition of M[G]. Suppose that t_1, ..., t_Nb are terms and g(t_1, ..., t_Nb) is a subterm of phi. Then s assigns a value in M to each of the t_i. Let a_i be the value assigned by s to t_i. Then g0(a_1, ..., a_Nb) = (cdr (assoc-equal (list a_1 ... a_Nb) p0)), as the assoc-equal is a pair (since p0 \in D*) and has the indicated value (because p0 \in G). It follows by the definition of formula phi' in the definition of forcing: M[G] |= phi[s] iff M |= phi'(p)[s[p<-p0]] Moreover, because p0 \in D* it is clear that this holds if p0 is replaced by an arbitrary extension of p0. Then (*) easily follows. By Proposition 5, D* is dense, so there is some p0 in the intersection of D* and G. The forward direction of the conclusion then follows by (*). The reverse direction is clear from (*) by application of Proposition 2 to D* and Proposition 4. Next, suppose M[G] |= ~phi[x]. Then it is not the case that M[G] |= phi, so by the inductive hypothesis, there is no p0 \in G for which M |= (p ||- phi)[s[p<-p0]]. By Proposition 3, there is p0 \in G for which M |= (p ||- ~phi)[s[p<-p0]]. For the other direction, suppose it is not the case that M[G] |= ~phi[s]. So M[G] |= phi[s], and by the inductive hypothesis, there is p0 \in G for which M |= (p ||- phi)[s[p<-p0]]. It follows that there is no p1 \in G for which M |= (p ||- ~phi)[s[p<-p1]], since from such p1 we can find a common extension p2 of p0 and p1 (since G is generic), and since p2 extends p0 then by Proposition 4, M |= (p ||- phi)[s[p<-p2]], contradicting (by definition of forcing) M |= (p ||- ~phi)[s[p<-p1]] since p2 extends p1. The case (phi_1 & phi_2) follows easily from the inductive hypothesis. For the forward direction, apply Proposition 4 and the observation that by genericity, if p0 \in G and p1 \in G then p0 and p1 they have a common extension in G. Finally, the case (exists x) phi follows trivially from the inductive hypothesis. -| Truth Lemma Corollary. The Truth Lemma holds with ||-w replacing ||-. Proof. This is clear by applying the Truth Lemma to ~~phi. -| Here is our main theorem. Recall that all first-order theories in our ACL2 context satisfy the e0-induction scheme. Theorem. M[G] satisfies e0-induction. Proof. We consider an arbitrary instance of e0-induction in L[g], stated using a strict well-founded relation <| and a formula phi. We write phi(y) to indicate that y may be among the free variables of phi, and phi(y<-x) to denote the result of substituting x for y in phi. theta(y): (forall y) [((forall x <| y) phi(y<-x)) -> phi(y)] -> (forall y) phi(y) Our goal is to prove that theta holds in M[G]. Below, we abuse notation by leaving assignments implicit and by writing "p ||- phi(y0)" to signify that the formula (p ||- phi(y)) is true in M under the extension of the explicit assignment that binds y to y0. We believe that the intended meaning will be clear. Consider the following set D. D = {p \in P: either p ||-w phi(y0) for all y0, or else for some y0, p ||- ~phi(y0) and for all y1 <| y0 p ||-w phi(y1)}. The set D is clearly first-order definable (with parameters) in M. We claim that D is a dense subset of P. For suppose p0 \in P; we find p1 \in D extending p0, as follows. If p0 ||-w phi(y0) for all y0, then we may take p1 to be p0. Otherwise, by definition of ||-w and ||-, there is some y0 such that for some extension p0' of p0, p0' ||- ~phi(y0). Pick a <|-minimal such y0, and correspondingly pick p1 so that p1 extends p0 and p1 ||- ~phi(y0). In order to show that p1 \in D, it remains to show that for all y1 <| y0, p1 ||-w phi(y1), i.e., there is no q extending p1 such that q ||- ~phi(y1). This is indeed the case since otherwise q and y1 would contradict the <|-minimality of y0. Applying the genericity of G and just-proved density of D, pick p0 \in G such that p0 \in D. If p0 ||-w phi(y0) for all y0, then by the Truth Lemma Corollary, M[G] |= phi(y0) for all y0, and thus M[G] |= theta. Otherwise, since p0 \in D we may choose y0 such that p0 ||- ~phi(y0) and for all y1 <| y0, p0 ||-w phi(y1). By the Truth Lemma and its corollary, since p0 \in G we have: (1) M[G] |= ~phi(y0). (2) For all y1 <| y0, M[G] |= phi(y1). It follows that the antecedent of theta is false in M[G], as witnessed by y = y0; thus M[G] |= theta. -| Remark. We close by returning, as promised above, to the question of why so much care is necessary in constructing an expansion of M. We assume familiarity here with the notion of a "non-standard" natural number of M, i.e., one that is greater than the interpretation of any term that has the form (+ 1 1 1 ... 1). Here is a very simple example that illustrates the need for some care. Consider the following event, which introduces a function foo with the following property: for all x, if natp(x) then natp(foo(x)). (defchoose foo (y) (x) (implies (natp x) (natp y))) Certainly we can build a model of the above property from a model M of the ground-zero theory, by interpreting foo so that for all x for which M satisfies natp(x), foo(x) is also a natp in M. But suppose we start with a non-standard model M of the ground-zero theory, and we happen to define foo(x) to be 1 for all non-standard natural numbers x and 0 for all other x. The resulting expansion of M will not satisfy the e0-induction scheme or even the ordinary natural number induction scheme: foo(0)=0 holds in that expansion as does the implication foo(n)=0 => foo(n+1)=0 for every natural number n of M, standard or not; and yet foo(k)=0 fails for every non-standard natural number k of M.  File: acl2-doc-emacs.info, Node: DEFCONG, Next: DEFCONST, Prev: DEFCHOOSE, Up: EVENTS DEFCONG prove congruence rule Defcong is used to prove that one equivalence relation preserves another in a given argument position of a given function. Example: (defcong set-equal iff (memb x y) 2) is an abbreviation for (defthm set-equal-implies-iff-memb-2 (implies (set-equal y y-equiv) (iff (memb x y) (memb x y-equiv))) :rule-classes (:congruence)) See *note CONGRUENCE:: and also see *note EQUIVALENCE::. General Form: (defcong equiv1 equiv2 term k :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :event-name event-name :doc doc) where equiv1 and equiv2 are known equivalence relations, term is a call of a function fn on the correct number of distinct variable arguments (fn x1 ... xn), k is a positive integer less than or equal to the arity of fn, and other arguments are as specified in the documentation for defthm. The defcong macro expands into a call of defthm. The name of the defthm event is equiv1-implies-equiv2-fn-k unless an :event-name keyword argument is supplied for the name. The term of the theorem is (implies (equiv1 xk yk) (equiv2 (fn x1... xk ...xn) (fn x1... yk ...xn))). The rule-class :congruence is added to the rule-classes specified, if it is not already there. All other arguments to the generated defthm form are as specified by the keyword arguments above.  File: acl2-doc-emacs.info, Node: DEFCONST, Next: DEFDOC, Prev: DEFCONG, Up: EVENTS DEFCONST define a constant Examples: (defconst *digits* '(0 1 2 3 4 5 6 7 8 9)) (defconst *n-digits* (the unsigned-byte (length *digits*))) General Form: (defconst name term doc-string) where name is a symbol beginning and ending with the character *, term is a variable-free term that is evaluated to determine the value of the constant, and doc-string is an optional documentation string (see *note DOC-STRING::). When a constant symbol is used as a term, ACL2 replaces it by its value; see *note TERM::. Note that defconst uses a "safe mode" to evaluate its form, in order to avoids soundness issues but with an efficiency penalty (perhaps increasing the evaluation time by several hundred percent). If efficiency is a concern, or if for some reason you need the form to be evaluated without safe mode (e.g., you are an advanced system hacker using trust tags to traffic in raw Lisp code), consider using the macro defconst-fast instead, defined in community book books/make-event/defconst-fast.lisp, for example: (defconst-fast *x* (expensive-fn ...)) A more general utility may be found in community book books/tools/defconsts.lisp. Also see *note USING-TABLES-EFFICIENTLY:: for an analogous issue with table events. It may be of interest to note that defconst is implemented at the lisp level using defparameter, as opposed to defconstant. (Implementation note: this is important for proper support of undoing and redefinition.) We close with a technical remark, perhaps of interest only to users of ACL2(h), the experimental extension of ACL2 that supports hash cons, function memoization, and hash-table-based "fast alists"; see *note HONS-AND-MEMOIZATION::. For an event of the form (defconst *C* (quote OBJ)), i.e., (defconst *C* 'OBJ), then the value associated with *C* is OBJ; that is, the value of *C* is eq to the actual object OBJ occurring in the defconst form. So for example, if make-event is used to generate such a defconst event, as it is in the two books mentioned above, and OBJ is a fast alist (using ACL2(h)), then the value of *C* is a fast alist. This guarantee disappears if the term in the defconst form is not a quoted object, i.e., if it is not of the form (quote OBJ).  File: acl2-doc-emacs.info, Node: DEFDOC, Next: DEFEQUIV, Prev: DEFCONST, Up: EVENTS DEFDOC add a documentation topic Examples: (defdoc interp-section ":Doc-Section ...") General Form: (defdoc name doc-string) where name is a symbol or string to be documented and doc-string is a documentation string (see *note DOC-STRING::). This event adds the documentation string for symbol name to the :doc database. It may also be used to change the documentation for name if name already has documentation. The difference between this event and deflabel is that, unlike deflabel (but like table), it does not mark the current history with the name. But like deflabel, defdoc events are never considered redundant (see *note REDUNDANT-EVENTS::). See *note DEFLABEL:: for a means of attaching a documentation string to a name that marks the current history with that name. We now elaborate further on how defdoc may be useful in place of deflabel. It is usually sufficient to use deflabel when you might be tempted to use defdoc. However, unlike deflabel, defdoc does not mark the current history with name. Thus, defdoc is useful for introducing the documentation for a defun or deftheory event, for example, several events before the function or theory is actually defined. For example, suppose you want to define a theory (using deftheory). You need to prove the lemmas in that theory before executing the deftheory event. However, it is quite natural to define a :Doc-Section (see *note DOC-STRING::) whose name is the name of the theory to be defined, and put the documentation for that theory's lemmas into that :Doc-Section. Defdoc is ideal for this purpose, since it can be used to introduce the :Doc-Section, followed by the lemmas referring to that :Doc-Section, and finally concluded with a deftheory event of the same name. If deflabel were used instead of defdoc, for example, then the deftheory event would be disallowed because the name is already in use by the deflabel event. We also imagine that some users will want to use defdoc to insert the documentation for a function under development. This defdoc event would be followed by definitions of all the subroutines of that function, followed in turn by the function definition itself. Any time defdoc is used to attach documentation to an already-documented name, the name must not be attached to a new :Doc-Section. We make this requirement as a way of avoiding loops in the documentation tree. When documentation is redefined, a warning will be printed to the terminal.  File: acl2-doc-emacs.info, Node: DEFEQUIV, Next: DEFEVALUATOR, Prev: DEFDOC, Up: EVENTS DEFEQUIV prove that a function is an equivalence relation Example: (defequiv set-equal) is an abbreviation for (defthm set-equal-is-an-equivalence (and (booleanp (set-equal x y)) (set-equal x x) (implies (set-equal x y) (set-equal y x)) (implies (and (set-equal x y) (set-equal y z)) (set-equal x z))) :rule-classes (:equivalence)) See *note EQUIVALENCE::. General Form: (defequiv fn :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :event-name event-name :doc doc) where fn is a function symbol of arity 2, event-name, if supplied, is a symbol, and all other arguments are as specified in the documentation for defthm. The defequiv macro expands into a call of defthm. The name of the defthm is fn-is-an-equivalence, unless event-name is supplied, in which case event-name is the name used. The term generated for the defthm event states that fn is Boolean, reflexive, symmetric, and transitive. The rule-class :equivalence is added to the rule-classes specified, if it is not already there. All other arguments to the generated defthm form are as specified by the other keyword arguments above.  File: acl2-doc-emacs.info, Node: DEFEVALUATOR, Next: DEFEXEC, Prev: DEFEQUIV, Up: EVENTS DEFEVALUATOR introduce an evaluator function Example: (defevaluator evl evl-list ((length x) (member-equal x y))) See *note META::. General Form: (defevaluator ev ev-list ((g1 x1 ... xn_1) ... (gk x1 ... xn_k)) where ev and ev-list are new function symbols and g1, ..., gk are old function symbols with the indicated number of formals, i.e., each gi has n_i formals. This function provides a convenient way to constrain ev and ev-list to be mutually-recursive evaluator functions for the symbols g1, ..., gk. Roughly speaking, an evaluator function for a fixed, finite set of function symbols is a restriction of the universal evaluator to terms composed of variables, constants, lambda expressions, and applications of the given functions. However, evaluator functions are constrained rather than defined, so that the proof that a given metafunction is correct vis-a-vis a particular evaluator function can be lifted (by functional instantiation) to a proof that it is correct for any larger evaluator function. See *note META:: for a discussion of metafunctions. Defevaluator executes an encapsulate after generating the appropriate defun and defthm events. Perhaps the easiest way to understand what defevaluator does is to execute the keyword command :trans1 (defevaluator evl evl-list ((length x) (member-equal x y))) and inspect the output. This trick is also useful in the rare case that the event fails because a hint is needed. In that case, the output of :trans1 can be edited by adding hints, then submitted directly. Formally, ev is said to be an "evaluator function for g1, ..., gk, with mutually-recursive counterpart ev-list" iff ev and ev-list are constrained functions satisfying just the constraints discussed below. Ev and ev-list must satisfy constraints (0)-(4) and (k): (0) How to ev an arbitrary function application: (implies (and (consp x) (syntaxp (not (equal a ''nil))) (not (equal (car x) 'quote))) (equal (ev x a) (ev (cons (car x) (kwote-lst (ev-list (cdr x) a))) nil))) (1) How to ev a variable symbol: (implies (symbolp x) (equal (ev x a) (and x (cdr (assoc-equal x a))))) (2) How to ev a constant: (implies (and (consp x) (equal (car x) 'quote)) (equal (ev x a) (cadr x))) (3) How to ev a lambda application: (implies (and (consp x) (consp (car x))) (equal (ev x a) (ev (caddar x) (pairlis$ (cadar x) (ev-list (cdr x) a))))) (4) How to ev an argument list: (implies (consp x-lst) (equal (ev-list x-lst a) (cons (ev (car x-lst) a) (ev-list (cdr x-lst) a)))) (implies (not (consp x-lst)) (equal (ev-list x-lst a) nil)) (k) For each i from 1 to k, how to ev an application of gi, where gi is a function symbol of n arguments: (implies (and (consp x) (equal (car x) 'gi)) (equal (ev x a) (gi (ev x1 a) ... (ev xn a)))), where xi is the (cad...dr x) expression equivalent to (nth i x). Defevaluator defines suitable witnesses for ev and ev-list, proves the theorems about them, and constrains ev and ev-list appropriately. We expect defevaluator to work without assistance from you, though the proofs do take some time and generate a lot of output. The proofs are done in the context of a fixed theory, namely the value of the constant *defevaluator-form-base-theory*. (Aside: (3) above may seem surprising, since the bindings of a are not included in the environment that is used to evaluate the lambda body, (caddar x). However, ACL2 lambda expressions are all _closed_: in (lambda (v1 ... vn) body), the only free variables in body are among the vi. See *note TERM::.)  File: acl2-doc-emacs.info, Node: DEFEXEC, Next: DEFINE-TRUSTED-CLAUSE-PROCESSOR, Prev: DEFEVALUATOR, Up: EVENTS DEFEXEC attach a terminating executable function to a definition Suppose you define a function (fn x) with a guard of (good-input-p x), and you know that when the guard holds, the measure decreases on each recursive call. Unfortunately, the definitional principle (see *note DEFUN::) ignores the guard. For example, if the definition has the form (defun fn (x) (declare (xargs :guard (good-input-p x))) (if (not-done-yet x) (... (fn (destr x)) ...) ...)) then in order to admit this definition, ACL2 must prove the appropriate formula asserting that (destr x) is "smaller than" x under the assumption (not-done-yet x) but without the assumption (good-input-p x), even if (not-done-yet x) is true. In essence, it may be necessary to submit instead the following definition. (defun fn (x) (declare (xargs :guard (good-input-p x))) (if (good-input-p x) (if (not-done-yet x) (... (fn (destr x)) ...) ...) nil) But it is unfortunate that when calls of fn are evaluated, for example when fn is applied to an explicit constant during a proof, then a call of good-input-p must now be evaluated on each recursive call. Fortunately, defexec provides a way to keep the execution efficient. For the example above we could use the following form. (defexec fn (x) (declare (xargs :guard (good-input-p x))) (mbe :logic (if (good-input-p x) (if (not-done-yet x) (... (fn (destr x)) ...) ...) nil) :exec (if (not-done-yet x) (... (fn (destr x)) ...) ...))) Here "mbe" stands for "must be equal" and, roughly speaking, its call above is logically equal to the :logic form but is evaluated using the :exec form when the guard holds. See *note MBE::. The effect is thus to define fn as shown in the defun form above, but to cause execution of fn using the :exec body. The use of defexec instead of defun in the example above causes a termination proof to be performed, in order to guarantee that evaluation always theoretically terminates, even when using the :exec form for evaluation. Example: ; Some of the keyword arguments in the declarations below are irrelevant or ; unnecessary, but they serve to illustrate their use. (defexec f (x) (declare (xargs :measure (+ 15 (acl2-count x)) :ruler-extenders :basic :hints (("Goal" :in-theory (disable nth))) :guard-hints (("Goal" :in-theory (disable last))) :guard (and (integerp x) (<= 0 x) (< x 25))) (exec-xargs :test (and (integerp x) (<= 0 x)) :default-value 'undef ; defaults to nil :measure (nfix x) :ruler-extenders :basic :well-founded-relation o<)) (mbe :logic (if (zp x) 1 (* x (f (- x 1)))) :exec (if (= x 0) 1 (* x (f (- x 1)))))) The above example macroexpands to the following. (ENCAPSULATE () (LOCAL (ENCAPSULATE () (SET-IGNORE-OK T) (SET-IRRELEVANT-FORMALS-OK T) (LOCAL (DEFUN F (X) (DECLARE (XARGS :VERIFY-GUARDS NIL :HINTS (("Goal" :IN-THEORY (DISABLE NTH))) :MEASURE (NFIX X) :RULER-EXTENDERS :BASIC :WELL-FOUNDED-RELATION O<)) (IF (AND (INTEGERP X) (<= 0 X)) (IF (= X 0) 1 (* X (F (- X 1)))) 'UNDEF))) (LOCAL (DEFTHM F-GUARD-IMPLIES-TEST (IMPLIES (AND (INTEGERP X) (<= 0 X) (< X 25)) (AND (INTEGERP X) (<= 0 X))) :RULE-CLASSES NIL)))) (DEFUN F (X) (DECLARE (XARGS :MEASURE (+ 15 (ACL2-COUNT X)) :RULER-EXTENDERS :BASIC :HINTS (("Goal" :IN-THEORY (DISABLE NTH))) :GUARD-HINTS (("Goal" :IN-THEORY (DISABLE LAST))) :GUARD (AND (INTEGERP X) (<= 0 X) (< X 25)))) (MBE :LOGIC (IF (ZP X) 1 (* X (F (- X 1)))) :EXEC (IF (= X 0) 1 (* X (F (- X 1))))))) Notice that in the example above, the :hints in the local definition of F are inherited from the :hints in the xargs of the defexec form. We discuss such inheritance below. CAVEAT: Termination is not considered for calls of mbe under the top-level call. Moreover, the :exec part of an mbe call under the :logic part of any superior mbe call is completely ignored. General Form: (defexec fn (var1 ... varn) doc-string dcl ... dcl (mbe :LOGIC logic-body :EXEC exec-body)) where the syntax is identical to the syntax of defun where the body is a call of mbe, with the exceptions described below. Thus, fn is the symbol you wish to define and is a new symbolic name and (var1 ... varn) is its list of formal parameters (see *note NAME::). The first exception is that at least one dcl (i.e., declare form) must specify a :guard, guard. The second exception is that one of the dcls is allowed to contain an element of the form (exec-xargs ...). The exec-xargs form, if present, must specify a non-empty keyword-value-listp each of whose keys is one of :test, :default-value, or one of the standard xargs keys of :measure, :ruler-extenders, :well-founded-relation, :hints, or :stobjs. Any of these five standard xargs keys that is present in an xargs of some dcl but is not specified in the (possibly nonexistent) exec-xargs form is considered to be specified in the exec-xargs form, as illustrated in the example above for :hints. (So for example, if you want :hints in the final, non-local definition but not in the local definition, then specify the :hints in the xargs but specify :hints nil in the exec-xargs.) If :test is specified and not nil, let test be its value; otherwise let test default to guard. If :default-value is specified, let default-value be its value; else default-value is nil. Default-value should have the same signature as exec-body; otherwise the defexec form will fail to be admitted. The above General Form's macroexpansion is of the form (PROGN encap final-def), where encap and final-def are as follows. Final-def is simply the result of removing the exec-xargs declaration (if any) from its declare form, and is the result of evaluating the given defexec form, since encap is of the following form. ; encap (ENCAPSULATE () (set-ignore-ok t) ; harmless for proving termination (set-irrelevant-formals-ok t) ; harmless for proving termination (local local-def) (local local-thm)) The purpose of encap is to ensure the the executable version of name terminates on all arguments. Thus, local-def and local-thm are as follows, where the xargs of the declare form are the result of adding :VERIFY-GUARDS NIL to the result of removing the :test and (optional) :default-value from the exec-xargs. ; local-def (DEFUN fn formals (DECLARE (XARGS :VERIFY-GUARDS NIL ...)) (IF test exec-body default-value)) ; local-thm (DEFTHM fn-EXEC-GUARD-HOLDS (IMPLIES guard test) :RULE-CLASSES NIL) We claim that if the above local-def and local-thm are admitted, then all evaluations of calls of fn terminate. The concern is that the use of mbe in final-def allows for the use of exec-body for a call of fn, as well as for subsequent recursive calls, when guard holds and assuming that the guards have been verified for final-def. However, by local-thm we can conclude in this case that test holds, in which case the call of fn may be viewed as a call of the version of fn defined in local-def. Moreover, since guards have been verified for final-def, then guards hold for subsequent evaluation of exec-body, and in particular for recursive calls of fn, which can thus continue to be viewed as calls using local=def.  File: acl2-doc-emacs.info, Node: DEFINE-TRUSTED-CLAUSE-PROCESSOR, Next: DEFLABEL, Prev: DEFEXEC, Up: EVENTS DEFINE-TRUSTED-CLAUSE-PROCESSOR define a trusted (unverified) goal-level simplifier This documentation assumes familiarity with :clause-processor rules; see *note CLAUSE-PROCESSOR::. Briefly put, a _clause-processor_ is a user-defined function that takes as input the ACL2 representation of a goal -- a _clause_ -- and returns a list of goals (i.e., a list of clauses). A :clause-processor rule is a way to inform ACL2 that a clause-processor has been proved correct and now may be specified in :clause-processor hints. Here we describe a utility, define-trusted-clause-processor, that provides another way to inform ACL2 that a function is to be considered a clause-processor that can be specified in a :clause-processor hint. You can find examples of correct and incorrect use of this utility in community book books/clause-processors/basic-examples. Consider the simple example already presented for :clause-processor rules (again, see *note CLAUSE-PROCESSOR::), for a simple clause-processor named note-fact-clause-processor. Instead of introducing an evaluator and proving a correctness theorem with :rule-classes :clause-processor, we can simply inform ACL2 that we trust the function note-fact-clause-processor to serve as a clause-processor. (define-trusted-clause-processor note-fact-clause-processor nil :ttag my-ttag) A non-nil :ttag argument generates a defttag event in order to acknowledge the dependence of the ACL2 session on the (unproved) correctness of this clause-processor. That argument can be omitted if there is currently an active trust tag. See *note DEFTTAG::. Because we are trusting this clause-processor, rather than having proved it correct, we refer to it as a "trusted" clause-processor to contrast with a proved-correct, or "verified", clause-processor. Now that the event displayed above has established note-fact-clause-processor as a (trusted) clause-processor, we can use it in a :clause-processor hint, for example as follows. Notice that the output is identical to that for the corresponding example presented for the verified case (see *note CLAUSE-PROCESSOR::), except that the word "verified" has been replaced by the word "trusted". ACL2 !>(thm (equal (car (cons x y)) x) :hints (("Goal" :clause-processor (note-fact-clause-processor clause '(equal a a))))) [Note: A hint was supplied for our processing of the goal above. Thanks!] We now apply the trusted :CLAUSE-PROCESSOR function NOTE-FACT-CLAUSE- PROCESSOR to produce two new subgoals. Subgoal 2 (IMPLIES (EQUAL A A) (EQUAL (CAR (CONS X Y)) X)). But we reduce the conjecture to T, by the :executable-counterpart of IF and the simple :rewrite rule CAR-CONS. Subgoal 1 (EQUAL A A). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. Summary Form: ( THM ...) Rules: ((:EXECUTABLE-COUNTERPART IF) (:EXECUTABLE-COUNTERPART NOT) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:REWRITE CAR-CONS)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !> Indeed, if one runs this example first and subsequently verifies the clause-processor, one will see the word "trusted" change to "verified". The general form is as follows. (define-trusted-clause-processor cl-proc ;;; clause-processor function supporters ;;; see below &key label ;;; optional, but required if doc is non-nil doc ;;; optional ttag ;;; discussed above partial-theory ;;; optional encapsulate event ) If a :label LAB is supplied, then a subsidiary deflabel event will be generated with name LAB, which will enable you to to undo this define-trusted-clause-processor event using: :ubt LAB. If you supply a :label then you may supply a :doc argument to use with that generated deflabel event. We discussed the :ttag argument above. The entire form is considered redundant (skipped) if it is identical to one already executed in the current ACL2 world; but if it is not redundant, then cl-proc must not already have been similarly designated as a trusted clause-processor. Note that cl-proc may be defined either in :program-mode or :logic-mode. The supporters argument should be a true list of function symbols in the current ACL2 world. It is important that this list include user-defined functions whose definitions support the correctness of the clause-processor function. Otherwise, local definitions of those missing supporters can render the use of this clause-processor unsound, as discussed in the paper referenced at the end of the clause-processor documentation topic. Moreover, ACL2 assumes for dependent clause-processors (discussed below) that every function symbol constrained by the "promised encapsulate" of that event is either among those supporters or ancestral in one of them (i.e. a supporter of a supporter, a supporter of one of those, etc.). *Dependent clause-processors and promised encapsulates*: The :partial-theory argument Suppose you want to introduce a clause-processor to reason about a complex hardware simulator that is implemented outside ACL2. Sawada and Reeber had just such a problem, as reported in their FMCAD 2006 paper. Indeed, they used sys-call to implement a :program-mode function in ACL2 that can invoke that simulator. In principle one could code the simulator directly in ACL2; but it would be a tremendous amount of work that has no practical purpose, given the interface to the external simulator. So: In what sense can we have a clause-processor that proves properties about a simulator when that simulator is not fully axiomatized in ACL2? Our answer, in a nutshell, is this: The above :partial-theory argument provides a way to write merely some of the constraints on the external tool (or even no constraints at all), with the understanding that such constraints are present implicitly in a stronger "promised" encapsulate, for example by exporting the full definition. If a trusted clause-processor is introduced with a :partial-theory argument, we call it a "dependent" clause-processor, because its correctness is dependent on the constraints implicitly introduced by the :partial-theory encapsulate form. The implicit constraints should logically imply the constraints actually introduced by the explicit encapsulate, but they should also be sufficient to justify every possible invocation of the clause-processor in a :clause-processor hint. The user of a define-trusted-clause-processor form is making a guarantee -- or, is relying on a guarantee provided by the writer of that form -- that in principle, there exists a so-called "promised encapsulate": an encapsulate form with the same signature as the :partial-theory encapsulate form associated with the trusted clause-processor, but whose constraints introduced are the aforementioned implicit constraints. There are several additional requirements on a :partial-theory argument. First, it must be an encapsulate event with non-empty signature. Moreover, the functions introduced by that event must be exactly those specified in the signature, and no more. And further still, the define-trusted-clause-processor form cannot be executed inside any encapsulate form with non-empty signature; we can think of this situation as attempting to associate more than one encapsulate with the functions introduced in the inner encapsulate. The :partial-theory event will (in essence) be executed as part of the evaluation of the define-trusted-clause-processor form. Again, a critical obligation rests on the user who provides a :partial-theory: there must exist (in principle at least) a corresponding promised encapsulate form with the same signature that could logically be admitted, whenever the above define-trusted-clause-processor form is evaluated successfully, that justifies the designation of cl-proc as a clause-processor. See also the paper mentioned above for more about promised encapsulates. A key consequence is that the constraints are unknown for the functions introduced in (the signature of) a :partial-theory encapsulate form. Thus, functional instantiation (see *note FUNCTIONAL-INSTANTIATION-EXAMPLE::) is disabled for function in the signature of a :partial-theory form. *A remark on the underlying implementation* You can see all of the current trusted clause-processors by issuing the command (table trusted-clause-processor-table). Those that are dependent clause-processors will be associated in the resulting association list with a pair whose car is the list of supporters and whose cdr is t, i.e., with (supporters . t); the others will be associated just with (supporters). Thus, define-trusted-clause-processor is actually a macro that generates (among other things) a table event for a table named trusted-clause-processor-table; see *note TABLE::. You are invited to use :trans1 to see expansions of calls of this macro. *A technique for using raw Lisp to define a trusted clause-processor* The following code is intended to give an idea for how one might define the "guts" of a trusted clause-processor in raw Lisp. The idea is to stub out functions, such as acl2-my-prove below, that you want to define in raw Lisp; and then, load a raw Lisp file to overwrite any such function with the real code. But then we make any such overwritten function untouchable. (This last step is important because otherwise, one can prove nil using a :functional-instance :use hint, by exploiting the fact that this function has executable code for which there is no corresponding definitional axiom.) (defstub acl2-my-prove (term hint) t) (program) (defttag :my-cl-proc) (progn ; We wrap everything here in a single progn, so that the entire form is ; atomic. That's important because we want the use of push-untouchable to ; prevent anything besides my-clause-processor from calling acl2-my-prove. (progn! (set-raw-mode-on state) (load "my-hint-raw.lsp") ; defines my-prove in raw Lisp (defun acl2-my-prove (term hint) (my-prove term hint))) (defun my-clause-processor (cl hint) (declare (xargs :guard (pseudo-term-listp cl) :mode :program)) (if (acl2-my-prove (disjoin cl) hint) (disjoin-clause-segments-to-clause (pairlis$ (hint-to-termlist hint) nil) cl) (prog2$ (cw "~|~%NOTE: Unable to prove goal with ~ my-clause-processor and indicated hint.~|") (list cl)))) (push-untouchable acl2-my-prove t) )  File: acl2-doc-emacs.info, Node: DEFLABEL, Next: DEFMACRO, Prev: DEFINE-TRUSTED-CLAUSE-PROCESSOR, Up: EVENTS DEFLABEL build a landmark and/or add a documentation topic Examples: (deflabel interp-section :doc ":Doc-Section ...") General Form: (deflabel name :doc doc-string) where name is a new symbolic name (see *note NAME::) and doc-string is an optional documentation string (see *note DOC-STRING::). This event adds the documentation string for symbol name to the :doc database. By virtue of the fact that deflabel is an event, it also marks the current history with the name. Thus, even undocumented labels are convenient as landmarks in a proof development. For example, you may wish to undo back through some label or compute a theory expression (see *note THEORIES::) in terms of some labels. Deflabel events are never considered redundant. See *note REDUNDANT-EVENTS::. See *note DEFDOC:: for a means of attaching a documentation string to a name without marking the current history with that name.  File: acl2-doc-emacs.info, Node: DEFMACRO, Next: DEFMACRO-LAST, Prev: DEFLABEL, Up: EVENTS DEFMACRO define a macro Example Defmacros: (defmacro xor (x y) (list 'if x (list 'not y) y)) (defmacro git (sym key) (list 'getprop sym key nil '(quote current-acl2-world) '(w state))) (defmacro one-of (x &rest rst) (declare (xargs :guard (symbol-listp rst))) (cond ((null rst) nil) (t (list 'or (list 'eq x (list 'quote (car rst))) (list* 'one-of x (cdr rst)))))) Example Expansions: term macroexpansion (xor a b) (if a (not b) b) (xor a (foo b)) (if a (not (foo b)) (foo b)) (git 'car 'lemmas) (getprop 'car 'lemmas nil 'current-acl2-world (w state)) (one-of x a b c) (or (eq x 'a) (or (eq x 'b) (or (eq x 'c) nil))) (one-of x 1 2 3) ill-formed (guard violation) General Form: (defmacro name macro-args doc-string dcl ... dcl body) where name is a new symbolic name (see *note NAME::), macro-args specifies the formal parameters of the macro, and body is a term. The formal parameters can be specified in a much more general way than is allowed by ACL2 defun events; see *note MACRO-ARGS:: for a description of keyword (&key) and optional (&optional) parameters as well as other so-called "lambda-list keywords", &rest and &whole. Doc-string is an optional documentation string; see *note DOC-STRING::. Each dcl is an optional declaration (see *note DECLARE::) except that the only xargs keyword permitted by defmacro is :guard. For compute-intensive applications see the community book misc/defmac.lisp, which can speed up macroexpansion by introducing an auxiliary defun. For more information, evaluate the form (include-book "misc/defmac" :dir :system) and then evaluate :doc defmac. Macroexpansion occurs when a form is read in, i.e., before the evaluation or proof of that form is undertaken. To experiment with macroexpansion, see *note TRANS::. When a form whose car is name arises as the form is read in, the arguments are bound as described in CLTL pp. 60 and 145, the guard is checked, and then the body is evaluated. The result is used in place of the original form. In ACL2, macros do not have access to the ACL2 state state. (If state or any user-defined stobj (see *note STOBJ::) is a macro argument, it is treated as an ordinary variable, bound at macro-expansion time to a piece of syntax.) This is in part a reflection of CLTL, p. 143, "More generally, an implementation of Common Lisp has great latitude in deciding exactly when to expand macro calls with a program. ... Macros should be written in such a way as to depend as little as possible on the execution environment to produce a correct expansion." In ACL2, the product of macroexpansion is independent of the current environment and is determined entirely by the macro body and the functions and constants it references. It is possible, however, to define macros that produce expansions that refer to state or other single-threaded objects (see *note STOBJ::) or variables not among the macro's arguments. See the git example above. For a related utility that does have access to the ACL2 state, see *note MAKE-EVENT::.  File: acl2-doc-emacs.info, Node: DEFMACRO-LAST, Next: DEFN, Prev: DEFMACRO, Up: EVENTS DEFMACRO-LAST define a macro that returns its last argument, but with side effects This is an advanced feature that requires a trust tag. For explanation, including an example, see *note RETURN-LAST::.  File: acl2-doc-emacs.info, Node: DEFN, Next: DEFND, Prev: DEFMACRO-LAST, Up: EVENTS DEFN definition with guard t Defn is defun with guard t.  File: acl2-doc-emacs.info, Node: DEFND, Next: DEFPKG, Prev: DEFN, Up: EVENTS DEFND disabled definition with guard t Defnd is defund with guard t.  File: acl2-doc-emacs.info, Node: DEFPKG, Next: DEFPROXY, Prev: DEFND, Up: EVENTS DEFPKG define a new symbol package Example: (defpkg "MY-PKG" (union-eq *acl2-exports* *common-lisp-symbols-from-main-lisp-package*)) * Menu: * HIDDEN-DEATH-PACKAGE:: handling defpkg events that are local * HIDDEN-DEFPKG:: handling defpkg events that are local * MANAGING-ACL2-PACKAGES:: user-contributed documentation on packages General Form: (defpkg "name" term doc-string) where "name" is a non-empty string consisting of standard characters (see *note STANDARD-CHAR-P::), none of which is lower case, that names the package to be created; term is a variable-free expression that evaluates to a list of symbols, where no two distinct symbols in the list may have the same symbol-name, to be imported into the newly created package; and doc-string is an optional documentation string; see *note DOC-STRING::. The name of the new package must be "new": the host lisp must not contain any package of that name. There are two exceptions to this newness rule, discussed at the end of this documentation. (There is actually an additional argument, book-path, that is used for error reporting but has no logical content. Users should generally ignore this argument, as well as the rest of this sentence: a book-path will be specified for defpkg events added by ACL2 to the portcullis of a book's certificate; see *note HIDDEN-DEATH-PACKAGE::.) Defpkg forms can be entered at the top-level of the ACL2 command loop. They should not occur in books (see *note CERTIFY-BOOK::). After a successful defpkg it is possible to "intern" a string into the package using intern-in-package-of-symbol. The result is a symbol that is in the indicated package, provided the imports allow it. For example, suppose 'my-pkg::abc is a symbol whose symbol-package-name is "MY-PKG". Suppose further that the imports specified in the defpkg for "MY-PKG" do not include a symbol whose symbol-name is "XYZ". Then (intern-in-package-of-symbol "XYZ" 'my-pkg::abc) returns a symbol whose symbol-name is "XYZ" and whose symbol-package-name is "MY-PKG". On the other hand, if the imports to the defpkg does include a symbol with the name "XYZ", say in the package "LISP", then (intern-in-package-of-symbol "XYZ" 'my-pkg::abc) returns that symbol (which is uniquely determined by the restriction on the imports list above). See *note INTERN-IN-PACKAGE-OF-SYMBOL::. Upon admission of a defpkg event, the function pkg-imports is extended to compute a list of all symbols imported into the given package, without duplicates. Defpkg is the only means by which an ACL2 user can create a new package or specify what it imports. That is, ACL2 does not support the Common Lisp functions make-package or import. Currently, ACL2 does not support exporting at all. The Common Lisp function intern is weakly supported by ACL2; see *note INTERN::. A more general form of that function is also provided: see *note INTERN$::. We now explain the two exceptions to the newness rule for package names. The careful experimenter will note that if a package is created with a defpkg that is subsequently undone, the host lisp system will contain the created package even after the undo. Because ACL2 hangs onto worlds after they have been undone, e.g., to implement :oops but, more importantly, to implement error recovery, we cannot actually destroy a package upon undoing it. Thus, the first exception to the newness rule is that name is allowed to be the name of an existing package if that package was created by an undone defpkg and the newly proposed set of imports is identical to the old one. See *note PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS::. This exception does not violate the spirit of the newness rule, since one is disinclined to believe in the existence of undone packages. The second exception is that name is allowed to be the name of an existing package if the package was created by a defpkg with identical set of imports. That is, it is permissible to execute "redundant" defpkg commands. The redundancy test is based on the values of the two import forms (comparing them after sorting and removing duplicates), not on the forms themselves. Finally, we explain why we require the package name to contain standard characters, none of which is lower case. We have seen at least one implementation that handled lower-case package names incorrectly. Since we see no need for lower-case characters in package names, which can lead to confusion anyhow (note for example that foo::bar is a symbol whose symbol-package-name is "FOO", not "foo"), we simply disallow them. Since the notion of "lower case" is only well-specified in Common Lisp for standard characters, we restrict to these. NOTE: Also see *note MANAGING-ACL2-PACKAGES:: for contributed documentation on managing ACL2 packages.  File: acl2-doc-emacs.info, Node: HIDDEN-DEATH-PACKAGE, Next: HIDDEN-DEFPKG, Prev: DEFPKG, Up: DEFPKG HIDDEN-DEATH-PACKAGE handling defpkg events that are local This documentation topic explains a little bit about certain errors users may see when attempting to evaluate a defpkg event. In brief, if you see an error that refers you to this topic, you are probably trying to admit a defpkg event, and you should change the name of the package to be introduced by that event. Recall that defpkg events introduce axioms, for example as follows. ACL2 !>(defpkg "PKG0" '(a b)) Summary Form: ( DEFPKG "PKG0" ...) Rules: NIL Warnings: None Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) "PKG0" ACL2 !>:pr! "PKG0" Rune: (:REWRITE PKG0-PACKAGE) Status: Enabled Lhs: (SYMBOL-PACKAGE-NAME (INTERN-IN-PACKAGE-OF-SYMBOL X Y)) Rhs: "PKG0" Hyps: (AND (STRINGP X) (NOT (MEMBER-SYMBOL-NAME X '(A B))) (SYMBOLP Y) (EQUAL (SYMBOL-PACKAGE-NAME Y) "PKG0")) Equiv: EQUAL Backchain-limit-lst: NIL Subclass: BACKCHAIN Loop-stopper: NIL ACL2 !> Now, a defpkg event may be executed underneath an encapsulate or include-book form that is marked local. In that case, traces of the added axiom will disappear after the surrounding encapsulate or include-book form is admitted. This can cause inconsistencies. (You can take our word for it, or you can look at the example shown in the "Essay on Hidden Packages" in source file axioms.lisp.) In order to prevent unsoundness, then, ACL2 maintains the following invariant. Let us say that a defpkg event is "hidden" if it is in support of the current logical world but is not present in that world as an event, because it is local as indicated above. We maintain the invariant that all defpkg events, even if "hidden", are tracked under-the-hood in the current logical world. Sometimes this property causes defpkg events to be written to the portcullis of a book's certificate (see *note BOOKS::). At any rate, if you then try to define the package in a manner inconsistent with the earlier such definition, that is, with a different imports list, you will see an error because of the above-mentioned tracking. (By the way, this topic's name comes from Holly Bell, who heard "hidden death package" instead of "hidden defpkg". The description seemed to fit. Thanks, Holly!)  File: acl2-doc-emacs.info, Node: HIDDEN-DEFPKG, Next: MANAGING-ACL2-PACKAGES, Prev: HIDDEN-DEATH-PACKAGE, Up: DEFPKG HIDDEN-DEFPKG handling defpkg events that are local See *note HIDDEN-DEATH-PACKAGE::  File: acl2-doc-emacs.info, Node: MANAGING-ACL2-PACKAGES, Prev: HIDDEN-DEFPKG, Up: DEFPKG MANAGING-ACL2-PACKAGES user-contributed documentation on packages Jared Davis has contributed documentation on managing ACL2 packages. See `http://www.cs.utexas.edu/users/moore/acl2/contrib/managing-acl2-packages.html'.  File: acl2-doc-emacs.info, Node: DEFPROXY, Next: DEFPUN, Prev: DEFPKG, Up: EVENTS DEFPROXY define a non-executable :program-mode function for attachment This event is provided for those who want to experiment with defattach using :program mode functions, and without proof obligations or constraints on cycles in the extended ancestors graph; see *note DEFATTACH::. If you merely want to define a stub or a non-executable function, see *note DEFSTUB:: or see *note DEFUN-NX::, respectively. See community book books/misc/defproxy-test.lisp for an extended (but simple) example. Example Forms: (defproxy subr1 (* *) => *) (defproxy add-hash (* * hashtable) => (mv * hashtable)) General Form: (defproxy name args-sig => output-sig) where name is a new function symbol and (name . args-sig) => output-sig) is a signature; see *note SIGNATURE::. The macro defproxy provides a convenient way to introduce a "proxy": a :program mode function that can be given attachments for execution (see *note DEFATTACH::), assuming that there is an active trust tag (see *note DEFTTAG::). Thus, a defproxy calls expands to a defun form with the following xargs declare form: :non-executable :program. Note that verify-termination is not permitted for such a function. However, it is permitted to put the proxy function into :logic mode by use of an encapsulate event; indeed, this is the way to "upgrade" an attachment so that the normal checks are performed and no trust tag is necessary. In order to take advantage of a defproxy form, one provides a subsequent defattach form to attach an executable function to the defproxy-introduced function. When :skip-checks t is provided in a defattach form, the usual checks for defattach events are skipped, including proof obligations and the check that the extended ancestor relation has no cycles (see *note DEFATTACH::). There must be an active trust tag (see *note DEFTTAG::) in order to use :skip-checks t. In that case the use of :skip-checks t is permitted; but note that its use is in fact required if a :program mode function is involved, and even if a :logic mode function is involved that has not been guard-verified. The following log shows a simple use of defproxy. ACL2 !>(defproxy foo-stub (*) => *) Summary Form: ( DEFUN FOO-STUB ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FOO-STUB ACL2 !>(foo-stub '(3 4 5)) ACL2 Error in TOP-LEVEL: ACL2 cannot ev the call of undefined function FOO-STUB on argument list: ((3 4 5)) To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !>(defun foo-impl (x) (declare (xargs :mode :program :guard (or (consp x) (eq x nil)))) (car x)) Summary Form: ( DEFUN FOO-IMPL ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO-IMPL ACL2 !>(defttag t) TTAG NOTE: Adding ttag :T from the top level loop. T ACL2 !>(defattach (foo-stub foo-impl) :skip-checks t) Summary Form: ( DEFATTACH (FOO-STUB FOO-IMPL) ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :ATTACHMENTS-RECORDED ACL2 !>(foo-stub '(3 4 5)) 3 ACL2 !> One can replace this attachment with one that uses :logic mode functions and does not skip checks. The idea is to reintroduce the proxy function using an encapsulate form, which does not require redefinition (see *note LD-REDEFINITION-ACTION::) to be enabled, and either to put the attachment into :logic mode with the guard verified, as we do in the example below, or else to attach to a different guard-verified :logic mode function. ACL2 !>(defattach (foo-stub nil) :skip-checks t) ; remove attachment Summary Form: ( DEFATTACH (FOO-STUB NIL) ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) :ATTACHMENTS-RECORDED ACL2 !>(encapsulate ((foo-stub (x) t :guard (true-listp x))) (local (defun foo-stub (x) (cdr x))) (defthm foo-stub-reduces-acl2-count (implies (consp x) (< (acl2-count (foo-stub x)) (acl2-count x))))) [[ ... output omitted here ... ]] The following constraint is associated with the function FOO-STUB: (IMPLIES (CONSP X) (< (ACL2-COUNT (FOO-STUB X)) (ACL2-COUNT X))) Summary Form: ( ENCAPSULATE ((FOO-STUB ...) ...) ...) Rules: NIL Warnings: Non-rec Time: 0.02 seconds (prove: 0.01, print: 0.00, other: 0.01) T ACL2 !>(verify-termination foo-impl) Since FOO-IMPL is non-recursive, its admission is trivial. We could deduce no constraints on the type of FOO-IMPL. Computing the guard conjecture for FOO-IMPL.... The guard conjecture for FOO-IMPL is trivial to prove. FOO-IMPL is compliant with Common Lisp. Summary Form: ( DEFUN FOO-IMPL ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (VERIFY-TERMINATION-FN ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FOO-IMPL ACL2 !>(defttag nil) ; optional NIL ACL2 !>(defattach (foo-stub foo-impl)) The guard proof obligation is (IMPLIES (TRUE-LISTP X) (OR (CONSP X) (EQ X NIL))). But we reduce the conjecture to T, by primitive type reasoning. Q.E.D. This concludes the guard proof. We now prove that the attachment satisfies the required constraint. The goal to prove is (IMPLIES (CONSP X) (< (ACL2-COUNT (FOO-IMPL X)) (ACL2-COUNT X))). [[ ... output omitted here ... ]] Q.E.D. Summary Form: ( DEFATTACH (FOO-STUB FOO-IMPL)) Rules: ((:DEFINITION ACL2-COUNT) (:DEFINITION FOO-IMPL) (:ELIM CAR-CDR-ELIM) (:FAKE-RUNE-FOR-LINEAR NIL) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:REWRITE CAR-CONS) (:REWRITE CDR-CONS) (:TYPE-PRESCRIPTION ACL2-COUNT)) Time: 0.02 seconds (prove: 0.01, print: 0.01, other: 0.00) :ATTACHMENTS-RECORDED ACL2 !> We close with some remarks on the checking of guards in the case that defattach has been called with keyword argument :skip-checks t. We illustrate with examples, where we assume an attachment pair (f . g) created by an event (defattach ... (f g) ... :skip-checks t ...). A good model for the treatment of :skip-checks t is dependent on whether f was introduced with defproxy or with encapsulate: for defproxy, the normal guard-related checks are treated as skipped, while for encapsulate, they are assumed to hold. First suppose that f was introduced using defproxy, and consider the following example. (defproxy f (*) => *) (defun g (x) (car x)) ; not guard-verified; implicit guard of t is too weak (defttag t) ; trust tag needed for :skip-checks t (defattach (f g) :skip-checks t) If we try to evaluate the form (f 3) in ACL2, then the top-level so-called "executable counterpart" (i.e., the logically-defined funcction, also known as the "*1*" function) of f is invoked. It calls the executable counterpart of g, which calls the executable counterpart of car, which in turn checks the guard of car and causes a guard violation error (unless we first turn off guard-checking; see *note SET-GUARD-CHECKING::). ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f 3) 1> (ACL2_*1*_ACL2::F 3) 2> (ACL2_*1*_ACL2::G 3) ACL2 Error in TOP-LEVEL: The guard for the function call (CAR X), which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CAR 3). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> Little changes if we modify the example above by strengtheing the guard of g. (defproxy f (*) => *) (defun g (x) (declare (xargs :guard (consp x))) (car x)) (defttag t) ; trust tag needed for :skip-checks t (defattach (f g) :skip-checks t) The result of evaluating (f 3) is as before, except that this time the guard violation occurs at the time that g is called. ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f 3) 1> (ACL2_*1*_ACL2::F 3) 2> (ACL2_*1*_ACL2::G 3) ACL2 Error in TOP-LEVEL: The guard for the function call (G X), which is (CONSP X), is violated by the arguments in the call (G 3). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set- guard-checking for information about suppressing this check with (set- guard-checking :none), as recommended for new users. ACL2 !> Now consider a slight variation of the example just above, in which f is introduced using encapsulate instead of using defproxy. (encapsulate ( ((f *) => *) ) (local (defun f (x) x))) (defun g (x) (declare (xargs :guard (consp x))) (car x)) (defttag t) ; trust tag needed for :skip-checks t (defattach (f g) :skip-checks t) Since f was introduced by encapsulate instead of by defproxy, ACL2 assumes that the usual guard properties hold. In particular, it assumes that (informally speaking) the guard of f implies the guard of g; see *note DEFATTACH:: for details. So in this case, ACL2 proceeds under that assumption even though it's actually false, and the result is a raw Lisp error. ACL2 !>(trace$ f g) ((F) (G)) ACL2 !>(f 3) 1> (ACL2_*1*_ACL2::F 3) 2> (G 3) *********************************************** ************ ABORTING from raw Lisp *********** Error: Attempt to take the car of 3 which is not listp. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !> If you replace g by its definition in the first example of this series, i.e. with a guard (implicitly) of t, you will see the same error, this time because the defattach event assumed that g was guard-verified.  File: acl2-doc-emacs.info, Node: DEFPUN, Next: DEFREFINEMENT, Prev: DEFPROXY, Up: EVENTS DEFPUN define a tail-recursive function symbol Defpun is a macro developed by Pete Manolios and J Moore that allows tail-recursive definitions. It is defined in community book books/misc/defpun.lisp, so to use it, execute the following event. (include-book "misc/defpun" :dir :system) Details of defpun are provided by Manolios and Moore in the "Partial Functions in ACL2" published with the ACL2 2000 workshop; see `http://www.cs.utexas.edu/users/moore/acl2/workshop-2000/'. Also see `http://www.cs.utexas.edu/users/moore/publications/defpun/index.html'. A variant, defp, has been developed by Matt Kaufmann to allow more general forms of tail recursion. If defpun doesn't work for you, try defp by first executing the following event. (include-book "misc/defp" :dir :system) Sandip Ray has contributed a variant of defpun, defpun-exec, that supports executability. See community book books/defexec/defpun-exec/defpun-exec.lisp: (include-book "defexec/defpun-exec/defpun-exec" :dir :system) He has also contributed community book books/misc/misc2/defpun-exec-domain-example.lisp, for functions that are uniquely defined in a particular domain.  File: acl2-doc-emacs.info, Node: DEFREFINEMENT, Next: DEFSTOBJ, Prev: DEFPUN, Up: EVENTS DEFREFINEMENT prove that equiv1 refines equiv2 Example: (defrefinement equiv1 equiv2) is an abbreviation for (defthm equiv1-refines-equiv2 (implies (equiv1 x y) (equiv2 x y)) :rule-classes (:refinement)) See *note REFINEMENT::. General Form: (defrefinement equiv1 equiv2 :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :event-name event-name :doc doc) where equiv1 and equiv2 are known equivalence relations, event-name, if supplied, is a symbol and all other arguments are as specified in the documentation for defthm. The defrefinement macro expands into a call of defthm. The name supplied is equiv1-refines-equiv2, unless event-name is supplied, in which case it is used as the name. The term supplied states that equiv1 refines equiv2. The rule-class :refinement is added to the rule-classes specified, if it is not already there. All other arguments to the generated defthm form are as specified by the other keyword arguments above.  File: acl2-doc-emacs.info, Node: DEFSTOBJ, Next: DEFSTUB, Prev: DEFREFINEMENT, Up: EVENTS DEFSTOBJ define a new single-threaded object Note: Novices are advised to avoid defstobj, perhaps instead using community books books/cutil/defaggregate.lisp or books/data-structures/structures.lisp. At the least, consider using (set-verify-guards-eagerness 0) to avoid guard verification. On the other hand, after you learn to use defstobj, see *note DEFABSSTOBJ:: for another way to introduce single-threaded objects. Example: (defconst *mem-size* 10) ; for use of *mem-size* just below (defstobj st (reg :type (array (unsigned-byte 31) (8)) :initially 0) (p-c :type (unsigned-byte 31) :initially 555) halt ; = (halt :type t :initially nil) (mem :type (array (unsigned-byte 31) (*mem-size*)) :initially 0 :resizable t)) General Form: (defstobj name (field1 :type type1 :initially val1 :resizable b1) ... (fieldk :type typek :initially valk :resizable bk) :renaming alist :doc doc-string :inline flg :congruent-to old-stobj-name) where name is a new symbol, each fieldi is a symbol, each typei is either a type-indicator (a type-spec or stobj name) or of the form (ARRAY type-indicator max), each vali is an object satisfying typei, and each bi is t or nil. Each pair :initially vali and :resizable bi may be omitted; more on this below. The :renaming alist argument is optional and allows the user to override the default function names introduced by this event. The doc-string is also optional. The :inline flg Boolean argument is also optional and declares to ACL2 that the generated access and update functions for the stobj should be implemented as macros under the hood (which has the effect of inlining the function calls). The optional :congruent-to old-stobj-name argument specifies an existing stobj with exactly the same structure, and is discussed below. We describe further restrictions on the fieldi, typei, vali, and on alist below. We recommend that you read about single-threaded objects (stobjs) in ACL2 before proceeding; see *note STOBJ::. The effect of this event is to introduce a new single-threaded object (i.e., a "stobj"), named name, and the associated recognizers, creator, accessors, updaters, constants, and, for fields of ARRAY type, length and resize functions. _The Single-Threaded Object Introduced_ The defstobj event effectively introduces a new global variable, named name, which has as its initial logical value a list of k elements, where k is the number of "field descriptors" provided. The elements are listed in the same order in which the field descriptors appear. If the :type of a field is (ARRAY type-indicator (max)) then max is a non-negative integer or a symbol introduced by defconst) whose value is a non-negative integer, and the corresponding element of the stobj is initially of length specified by max. Whether the value :type is of the form (ARRAY type-indicator (max)) or, otherwise, just type-indicator, then type-indicator is typically a type-spec; see *note TYPE-SPEC::. However, type-indicator can also be the name of a stobj that was previously introduced (by defstobj or defabsstobj). We ignore this "nested stobj" case below; see *note NESTED-STOBJS:: for a discussion of stobjs within stobjs. The keyword value :initially val specifies the initial value of a field, except for the case of a :type (ARRAY type-indicator (max)), in which case val is the initial value of the corresponding array. Note that the actual representation of the stobj in the underlying Lisp may be quite different; see *note STOBJ-EXAMPLE-2::. For the moment we focus entirely on the logical aspects of the object. In addition, the defstobj event introduces functions for recognizing and creating the stobj and for recognizing, accessing, and updating its fields. For fields of ARRAY type, length and resize functions are also introduced. Constants are introduced that correspond to the accessor functions. _Restrictions on the Field Descriptions in Defstobj_ Each field descriptor is of the form: (fieldi :TYPE typei :INITIALLY vali) Note that the type and initial value are given in "keyword argument" format and may be given in either order. The typei and vali "arguments" are not evaluated. If omitted, the type defaults to t (unrestricted) and the initial value defaults to nil. Each typei must be either a type-spec or else a list of the form (ARRAY type-spec (max)). (Again, we are ignoring the case of nested stobjs, discussed elsewhere; see *note NESTED-STOBJS::.) The latter forms are said to be "array types." Examples of legal typei are: (INTEGER 0 31) (SIGNED-BYTE 31) (ARRAY (SIGNED-BYTE 31) (16)) (ARRAY (SIGNED-BYTE 31) (*c*)) ; where *c* has a non-negative integer value The typei describes the objects which are expected to occupy the given field. Those objects in fieldi should satisfy typei. We are more precise below about what we mean by "expected." We first present the restrictions on typei and vali. Non-Array Types When typei is a type-spec it restricts the contents, x, of fieldi according to the "meaning" formula given in the table for type-spec. For example, the first typei above restricts the field to be an integer between 0 and 31, inclusive. The second restricts the field to be an integer between -2^30 and (2^30)-1, inclusive. The initial value, vali, of a field description may be any ACL2 object but must satisfy typei. Note that vali is not a form to be evaluated but an object. A form that evaluates to vali could be written 'vali, but defstobj does not expect you to write the quote mark. For example, the field description (days-off :initially (saturday sunday)) describes a field named days-off whose initial value is the list consisting of the two symbols SATURDAY and SUNDAY. In particular, the initial value is NOT obtained by applying the function saturday to the variable sunday! Had we written (days-off :initially '(saturday sunday)) it would be equivalent to writing (days-off :initially (quote (saturday sunday))) which would initialize the field to a list of length two, whose first element is the symbol quote and whose second element is a list containing the symbols saturday and sunday. Array Types When typei is of the form (ARRAY type-spec (max)), the field is supposed to be a list of items, initially of length specified by max, each of which satisfies the indicated type-spec. Max must be a non-negative integer or a defined constant evaluating to a non-negative integer. Thus, each of (ARRAY (SIGNED-BYTE 31) (16)) (ARRAY (SIGNED-BYTE 31) (*c*)) ; given previous event (defconst *c* 16) restricts the field to be a list of integers, initially of length 16, where each integer in the list is a (SIGNED-BYTE 31). We sometimes call such a list an "array" (because it is represented as an array in the underlying Common Lisp). The elements of an array field are indexed by position, starting at 0. Thus, the maximum legal index of an array field one less than is specified by max. Note that the value of max must be less than the Common Lisp constant array-dimension-limit, and also (though this presumably follows) less than the Common Lisp constant array-total-size-limit. Note also that the ARRAY type requires that the max be enclosed in parentheses. This makes ACL2's notation consistent with the Common Lisp convention of describing the (multi-)dimensionality of arrays. But ACL2 currently supports only single dimensional arrays in stobjs. For array fields, the initial value vali must be an object satisfying the type-spec of the ARRAY description. The initial value of the field is a list of max repetitions of vali. Array fields can be "resized," that is, their lengths can be changed, if :resizable t is supplied as shown in the example and General Form above. The new length must satisfy the same restriction as does max, as described above. Each array field in a defstobj event gives rise to a length function, which gives the length of the field, and a resize function, which modifies the length of the field if :resizable t was supplied with the field when the defstobj was introduced and otherwise causes an error. If :resizable t was supplied and the resize function specifies a new length k, then: if k is less than the existing array length, the array is shortened simply by dropping elements with index at least k; otherwise, the array is extended to length k by mapping the new indices to the initial value (supplied by :initially, else default nil). Array resizing is relatively slow, so we recommend using it somewhat sparingly. _The Default Function Names_ To recap, in (defstobj name (field1 :type type1 :initially val1) ... (fieldk :type typek :initially valk) :renaming alist :doc doc-string :inline inline-flag) name must be a new symbol, each fieldi must be a symbol, each typei must be a type-spec or (ARRAY type-spec (max)), and each vali must be an object satisfying typei. Roughly speaking, for each fieldi, a defstobj introduces a recognizer function, an accessor function, and an updater function. The accessor function, for example, takes the stobj and returns the indicated component; the updater takes a new component value and the stobj and return a new stobj with the component replaced by the new value. But that summary is inaccurate for array fields. The accessor function for an array field does not take the stobj and return the indicated component array, which is a list of length specified by max. Instead, it takes an additional index argument and returns the indicated element of the array component. Similarly, the updater function for an array field takes an index, a new value, and the stobj, and returns a new stobj with the indicated element replaced by the new value. These functions -- the recognizer, accessor, and updater, and also length and resize functions in the case of array fields -- have "default names." The default names depend on the field name, fieldi, and on whether the field is an array field or not. For clarity, suppose fieldi is named c. The default names are shown below in calls, which also indicate the arities of the functions. In the expressions, we use x as the object to be recognized by field recognizers, i as an array index, v as the "new value" to be installed by an updater, and name as the single-threaded object. non-array field array field recognizer (cP x) (cP x) accessor (c name) (cI i name) updater (UPDATE-c v name) (UPDATE-cI i v name) length (c-LENGTH name) resize (RESIZE-c k name) Finally, a recognizer and a creator for the entire single-threaded object are introduced. The creator returns the initial stobj, but may only be used in limited contexts; see *note WITH-LOCAL-STOBJ::. If the single-threaded object is named name, then the default names and arities are as shown below. top recognizer (nameP x) creator (CREATE-name) For example, the event (DEFSTOBJ $S (X :TYPE INTEGER :INITIALLY 0) (A :TYPE (ARRAY (INTEGER 0 9) (3)) :INITIALLY 9)) introduces a stobj named $S. The stobj has two fields, X and A. The A field is an array. The X field contains an integer and is initially 0. The A field contains a list of integers, each between 0 and 9, inclusively. Initially, each of the three elements of the A field is 9. This event introduces the following sequence of definitions: (DEFUN XP (X) ...) ; recognizer for X field (DEFUN AP (X) ...) ; recognizer of A field (DEFUN $SP ($S) ...) ; top-level recognizer for stobj $S (DEFUN CREATE-$S () ...) ; creator for stobj $S (DEFUN X ($S) ...) ; accessor for X field (DEFUN UPDATE-X (V $S) ...) ; updater for X field (DEFUN A-LENGTH ($S) ...) ; length of A field (DEFUN RESIZE-A (K $S) ...) ; resizer for A field (DEFUN AI (I $S) ...) ; accessor for A field at index I (DEFUN UPDATE-AI (I V $S) ...) ; updater for A field at index I _Avoiding the Default Function Names_ If you do not like the default names listed above you may use the optional :renaming alist to substitute names of your own choosing. Each element of alist should be of the form (fn1 fn2), where fn1 is a default name and fn2 is your choice for that name. For example (DEFSTOBJ $S (X :TYPE INTEGER :INITIALLY 0) (A :TYPE (ARRAY (INTEGER 0 9) (3)) :INITIALLY 9) :renaming ((X XACCESSOR) (CREATE-$S MAKE$S))) introduces the following definitions (DEFUN XP (X) ...) ; recognizer for X field (DEFUN AP (X) ...) ; recognizer of A field (DEFUN $SP ($S) ...) ; top-level recognizer for stobj $S (DEFUN MAKE$S () ...) ; creator for stobj $S (DEFUN XACCESSOR ($S) ...) ; accessor for X field (DEFUN UPDATE-X (V $S) ...) ; updater for X field (DEFUN A-LENGTH ($S) ...) ; length of A field (DEFUN RESIZE-A (K $S) ...) ; resizer for A field (DEFUN AI (I $S) ...) ; accessor for A field at index I (DEFUN UPDATE-AI (I V $S) ...) ; updater for A field at index I Note that even though the renaming alist substitutes "XACCESSOR" for "X" the updater for the X field is still called "UPDATE-X." That is because the renaming is applied to the default function names, not to the field descriptors in the event. Use of the :renaming alist may be necessary to avoid name clashes between the default names and and pre-existing function symbols. _Constants_ Defstobj events also introduce constant definitions (see *note DEFCONST::). One constant is introduced for each accessor function by prefixing and suffixing a `*' character on the function name. The value of that constant is the position of the field being accessed. For example, if the accessor functions are a, b, and c, in that order, then the following constant definitions are introduced. (defconst *a* 0) (defconst *b* 1) (defconst *c* 2) These constants are used for certain calls of nth and update-nth that are displayed to the user in proof output. For example, for stobj st with accessor functions a, b, and c, in that order, the term (nth '2 st) would be printed during a proof as (nth *c* st). Also see *note TERM::, in particular the discussion there of untranslated terms, and see *note NTH-ALIASES-TABLE::. _Inspecting the Effects of a Defstobj_ Because the stobj functions are introduced as "sub-events" of the defstobj the history commands :pe and :pc will not print the definitions of these functions but will print the superior defstobj event. To see the definitions of these functions use the history command :pcb!. To see an s-expression containing the definitions what constitute the raw Lisp implementation of the event, evaluate the form (nth 4 (global-val 'cltl-command (w state))) _immediately after_ the defstobj event has been processed. A defstobj is considered redundant only if the name, field descriptors, renaming alist, and inline flag are identical to a previously executed defstobj. Note that a redundant defstobj does not reset the stobj fields to their initial values. _Inlining and Performance_ The :inline keyword argument controls whether or not accessor, updater, and length functions are inlined (as macros under the hood, in raw Lisp). If :inline t is provided then these are inlined; otherwise they are not. The advantage of inlining is potentially better performance; there have been contrived examples, doing essentially nothing except accessing and updating array fields, where inlining reduced the time by a factor of 10 or more; and inlining has sped up realistic examples by a factor of at least 2. Inlining may get within a factor of 2 of C execution times for such contrived examples, and within a few percent of C execution times on realistic examples. A drawback to inlining is that redefinition may not work as expected, much as redefinition may not work as expected for macros: defined functions that call a macro, or inlined stobj function, will not see a subsequent redefinition of the macro or inlined function. Another drawback to inlining is that because inlined functions are implemented as macros in raw Lisp, tracing (see *note TRACE$::) will not show their calls. These drawbacks are avoided by default, but the user who is not concerned about them is advised to specify :inline t. _Specifying Congruent Stobjs_ Two stobjs are may be considered to be "congruent" if they have the same structure, that is, their defstobj events are identical when ignoring field names. In particular, every stobj is congruent to itself. In order to tell ACL2 that a new stobj st2 is indeed to be considered as congruent to an existing stobj st1, the defstobj event introducing st2 is given the keyword argument :congruent-to st1. Congruence is an equivalence relation: when you specify a new stobj to be congruent to an old one, you are also specifying that the new stobj is congruent to all other stobjs that are congruent to the old one. Thus, continuing the example above, if you specify that st3 is :congruent-to st2, then st1, st2, and st3 will all be congruent to each other. When two stobjs are congruent, ACL2 allows you to substitute one for another in a function call. Any number of stobjs may be replaced with congruent stobjs in the call, provided no two get replaced with the same stobj. The return values are correspondingly modified: if stobj st1 is replaced by st2 at an argument position, and if st1 is returned in the output signature of the function, then st2 is returned in place of st1. The following example illustrates congruent stobjs. For more examples of how to take advantage of congruent stobjs, and also of how to misuse them, see community book books/misc/congruent-stobjs-test.lisp. (defstobj st1 fld1) (defstobj st2 fld2 :congruent-to st1) (defstobj st3 fld3 :congruent-to st2) ; equivalently, :congruent-to st1 (defun f (st1 st2 st3) (declare (xargs :stobjs (st1 st2 st3))) (list (fld2 st1) (fld3 st2) (fld1 st3))) (update-fld1 1 st1) (update-fld1 2 st2) ; notice use of update-fld1 on st2 (update-fld1 3 st3) ; notice use of update-fld1 on st3 (assert-event (equal (f st3 st2 st1) '(3 2 1))) The following example shows an error that occurs when stobj arguments are repeated, i.e., at least two stobj arguments (in this case, three) get replaced by the same stobj. ACL2 !>(f st1 st1 st1) ACL2 Error in TOP-LEVEL: The form ST1 is being used, as an argument to a call of F, where the single-threaded object ST2 was expected, even though these are congruent stobjs. See :DOC defstobj, in particular the discussion of congruent stobjs. Note: this error occurred in the context (F ST1 ST1 ST1). ACL2 !>  File: acl2-doc-emacs.info, Node: DEFSTUB, Next: DEFTHEORY, Prev: DEFSTOBJ, Up: EVENTS DEFSTUB stub-out a function symbol Examples: ACL2 !>(defstub subr1 (* * state) => (mv * state)) ACL2 !>(defstub add-hash (* * hashtable) => hashtable) General Forms: (defstub name args-sig => output-sig) (defstub name args-sig => output-sig :doc doc-string) Name is a new function symbol and (name . args-sig) => output-sig) is a signature. If the optional doc-string is supplied it should be a documentation string. See also the "Old Style" heading below. Defstub macro expands into an encapsulate event (see *note ENCAPSULATE::). Thus, no axioms are available about name but it may be used wherever a function of the given signature is permitted. Exception: if output-sig is of the form (mv ...), then a :type-prescription rule is introduced stating that name returns a value satisfying true-listp. Old Style: Old Style General Form: (defstub name formals output) (defstub name formals output :doc doc-string) where name is a new function symbol, formals is its list of formal parameters, and output is either a symbol (indicating that the function returns one result) or a term of the form (mv s1 ... sn), where each si is a symbol (indicating that the function returns n results). Whether and where the symbol state occurs in formals and output indicates how the function handles state. It should be the case that (name formals output) is in fact a signature (see *note SIGNATURE::). Note that with the old style notation it is impossible to stub-out a function that uses any single-threaded object other than state. The old style is preserved for compatibility with earlier versions of ACL2.  File: acl2-doc-emacs.info, Node: DEFTHEORY, Next: DEFTHEORY-STATIC, Prev: DEFSTUB, Up: EVENTS DEFTHEORY define a theory (to enable or disable a set of rules) Example: (deftheory interp-theory (set-difference-theories (universal-theory :here) (universal-theory 'interp-section))) General Form: (deftheory name term :doc doc-string) where name is a new symbolic name (see *note NAME::), term is a term that when evaluated will produce a theory (see *note THEORIES::), and doc-string is an optional documentation string (see *note DOC-STRING::). Except for the variable world, term must contain no free variables. Term is evaluated with world bound to the current world (see *note WORLD::) and the resulting theory is then converted to a _runic theory_ (see *note THEORIES::) and associated with name. Henceforth, this runic theory is returned as the value of the theory expression (theory name). The value returned is the length of the resulting theory. For example, in the following, the theory associated with 'FOO has 54 runes: ACL2 !>(deftheory foo (union-theories '(binary-append) (theory 'minimal-theory))) Summary Form: ( DEFTHEORY FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) 54 ACL2 !> Note that the theory being defined depends on the context. For example, consider the following (contrived) example book. (in-package "ACL2") (defund foo (x) (consp x)) ; defund disables foo (local (in-theory (enable foo))) (deftheory my-theory (current-theory :here)) (in-theory (disable foo)) (defthm foo-property (implies (consp x) (foo x)) :hints (("Goal" :in-theory (enable my-theory)))) At the time foo-property is proved admissible during book certification (see *note CERTIFY-BOOK::), the local in-theory event has previously been evaluated, so the definition of foo is enabled. Thus, the :in-theory hint on foo-property will enable foo, and the theorem proves. HOWEVER, when the book is later included (see *note INCLUDE-BOOK::), the local event is skipped, so the definition of foo is disabled at the time the theory my-theory is defined. Hence, unlike the case for the admissibility pass of the book's certification, that theory does not include the definition of foo when the book is included. There is, however, a way to ensure that a theory defined in a book is the same at include-book time as it was during the admissibility pass of the book's certification; see *note DEFTHEORY-STATIC::.  File: acl2-doc-emacs.info, Node: DEFTHEORY-STATIC, Next: DEFTHM, Prev: DEFTHEORY, Up: EVENTS DEFTHEORY-STATIC define a `static' theory (to enable or disable a set of rules) This macro provides a variant of deftheory, such that the resulting theory is the same at include-book time as it was at certify-book time. We assume that the reader is familiar with theories; see *note DEFTHEORY::. We begin here by illustrating how deftheory-static differs from deftheory. Suppose for example that the following events are the first two events in a book, where that book is certified in the initial ACL2 world (see *note GROUND-ZERO::). (deftheory my-theory (current-theory :here)) (deftheory-static my-static-theory (current-theory :here)) Now suppose we include that book after executing the following event. (in-theory (disable car-cons)) Suppose that later we execute (in-theory (theory 'my-theory)). Then the rule car-cons will be disabled, because it was disabled at the time the expression (current-theory :here) was evaluated when processing the deftheory of my-theory while including the book. However, if we execute (in-theory (theory 'my-static-theory)), then the rule car-cons will be enabled, because the value of the theory my-static-theory was saved at the time the book was certified. General Form: (deftheory-static name term :doc doc-string) The arguments are handled the same as for deftheory. Thus, name is a new symbolic name (see *note NAME::), term is a term that when evaluated will produce a theory (see *note THEORIES::), and doc-string is an optional documentation string (see *note DOC-STRING::). Except for the variable world, term must contain no free variables. Term is evaluated with world bound to the current world (see *note WORLD::) and the resulting theory is then converted to a _runic theory_ (see *note THEORIES::) and associated with name. Henceforth, this runic theory is returned as the value of the theory expression (theory name). As for deftheory, the value returned is the length of the resulting theory. We conclude with an optional discussion about the implementation of deftheory-static, for those familiar with make-event. The following macroexpansion of the deftheory-static form above shows how this works (see *note TRANS1::). ACL2 !>:trans1 (deftheory-static my-static-theory (current-theory :here)) (MAKE-EVENT (LET ((WORLD (W STATE))) (LIST 'DEFTHEORY 'MY-STATIC-THEORY (LIST 'QUOTE (CURRENT-THEORY :HERE))))) ACL2 !> The idea is that upon evaluation of this make-event form, the first step is to evaluate the indicated LET expression to obtain a form (deftheory my-theory '(...)), where "(...)" is a list of all runes in current theory. If this form is in a book being certified, then the resulting deftheory form is stored in the book's certificate, and is used when the book is included later.  File: acl2-doc-emacs.info, Node: DEFTHM, Next: DEFTHMD, Prev: DEFTHEORY-STATIC, Up: EVENTS DEFTHM prove and name a theorem Examples: (defthm assoc-of-app (equal (app (app a b) c) (app a (app b c)))) The following nonsensical example illustrates all the optional arguments but is illegal because not all combinations are permitted. See *note HINTS:: for a complete list of hints. (defthm main (implies (hyps x y z) (concl x y z)) :rule-classes (:REWRITE :GENERALIZE) :instructions (induct prove promote (dive 1) x (dive 2) = top (drop 2) prove) :hints (("Goal" :do-not '(generalize fertilize) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))))) :otf-flg t :doc "#0[one-liner/example/details]") General Form: (defthm name term :rule-classes rule-classes :instructions instructions :hints hints :otf-flg otf-flg :doc doc-string) where name is a new symbolic name (see *note NAME::), term is a term alleged to be a theorem, and rule-classes, instructions, hints, otf-flg and doc-string are as described in their respective documentation. The five keyword arguments above are all optional, however you may not supply both :instructions and :hints, since one drives the proof checker and the other drives the theorem prover. If :rule-classes is not specified, the list (:rewrite) is used; if you wish the theorem to generate no rules, specify :rule-classes nil. When ACL2 processes a defthm event, it first tries to prove the term using the indicated hints (see *note HINTS::) or instructions (see *note PROOF-CHECKER::). If it is successful, it stores the rules described by the rule-classes (see *note RULE-CLASSES::), proving the necessary corollaries.  File: acl2-doc-emacs.info, Node: DEFTHMD, Next: DEFTTAG, Prev: DEFTHM, Up: EVENTS DEFTHMD prove and name a theorem and then disable it Use defthmd instead of defthm when you want to disable a theorem immediately after proving it. This macro has been provided for users who prefer working in a mode where theorems are only enabled when explicitly directed by :in-theory. Specifically, the form (defthmd NAME TERM ...) expands to: (progn (defthmd NAME TERM ...) (with-output :off summary (in-theory (disable NAME))) (value-triple '(:defthmd NAME))). Note that defthmd commands are never redundant (see *note REDUNDANT-EVENTS::). Even if the defthm event is redundant, then the in-theory event will still be executed. The summary for the in-theory event is suppressed. See *note DEFTHM:: for documentation of defthm.  File: acl2-doc-emacs.info, Node: DEFTTAG, Next: DEFUN, Prev: DEFTHMD, Up: EVENTS DEFTTAG introduce a trust tag (ttag) *Introduction*. This event is intended for advanced users who, in essence, want to build extensions of ACL2. The typical intended use is to create books that extend the functionality of ACL2 in ways not allowed without a so-called "active trust tag". A trust tag thus represents a contract: The writer of such a book is guaranteeing that the book extends ACL2 in a "correct" way as defined by the writer of the book. The writer of the book will often have a small section of the book in the scope of an active trust tag that can be inspected by potential users of that book: (defttag :some-ttag) ; install :some-ttag as an active trust tag (defttag nil) ; remove active trust tag Why might trust tags be needed? The evaluation of certain functions can introduce bugs and even unsoundness, but can be useful in restricted ways that avoid such issues. For example, sys-call can be used in an unsafe way, for example to overwrite files, or worse; see *note SYS-CALL:: for a frightening example from Bob Boyer. The following example shows that the function sys-call is restricted by default, but can be called after installing an active trust tag. ACL2 !>(sys-call "pwd" nil) ACL2 Error in TOP-LEVEL: The SYS-CALL function cannot be called unless a trust tag is in effect. See :DOC defttag. ACL2 !>(defttag t) ; Install :T as an active trust tag. TTAG NOTE: Adding ttag :T from the top level loop. T ACL2 !>(sys-call "pwd" nil) ; print the current directory and return NIL /u/kaufmann NIL ACL2 !>(defttag nil) ; Remove the active trust tag (using value NIL). NIL ACL2 !>(sys-call "pwd" nil) ; Now we get the error again: ACL2 Error in TOP-LEVEL: The SYS-CALL function cannot be called unless a trust tag is in effect. See :DOC defttag. ACL2 !> Of course, using sys-call with the Linux command pwd is not likely to cause any soundness problems! So suppose we want to create a function that prints the working directory. We might put the following events into a book that is to be certified. (in-package "ACL2") (defttag :pwd-ttag) (defun print-working-dir () (declare (xargs :mode :program)) (sys-call "pwd" nil)) (defttag nil) ; optional (books end with this implicitly) We can certify this book with a specification that :pwd-ttag is a legal trust tag: (certify-book "pwd" 0 t :ttags (:pwd-ttag)) One can now use this book by executing include-book with keyword parameter :ttags (:pwd-ttag) and then calling function print-working-dir: (include-book "pwd" :ttags (:pwd-ttag)) (print-working-dir) ; working directory is printed to terminal *Detailed documentation.* General Forms: (defttag tag-name) (defttag tag-name :doc doc-string) where tag-name is a symbol. The :doc doc-string argument is optional; if supplied, then it must be a valid documentation string (see *note DOC-STRING::), and the defttag call will generate a corresponding defdoc event for tag-name. (For the rest of this discussion we ignore the :doc argument.) Note however that (other than the :doc argument), if tag-name is not nil then it is converted to a "corresponding keyword": a symbol in the "KEYWORD" package with the same symbol-name as tag-name. Thus, for example, (defttag foo) is equivalent to (defttag :foo). Moreover, a non-nil symbol with a symbol-name of "NIL" is illegal for trust tags; thus, for example, (defttag :nil) is illegal. This event introduces or removes a so-called active trust tag (or "ttag", pronounced "tee tag"). An active ttag is a keyword symbol that is associated with potentially unsafe evaluation. For example, calls of sys-call are illegal unless there is an active trust tag. An active trust tag can be installed using a defttag event. If one introduces an active ttag and then writes definitions that calls sys-call, presumably in a defensibly "safe" way, then responsibility for those calls is attributed to that ttag. This attribution (or blame!) is at the level of books; a book's certificate contains a list of ttags that are active in that book, or in a book that is included (possibly locally), or in a book included in a book that is included (either inclusion being potentially local), and so on. We explain all this in more detail below. (Defttag :tag-name) is essentially equivalent to (table acl2-defaults-table :ttag :tag-name) and hence is local to any books and encapsulate events in which it occurs; see *note ACL2-DEFAULTS-TABLE::. We say more about the scope of defttag forms below. Note: This is an event! It does not print the usual event summary but nevertheless executes the above table event and hence changes the ACL2 logical world, and is so recorded. Although no event summary is printed, it is important to note that the "TTAG NOTE", discussed below, is always printed for a non-nil :tag-name (unless deferred; see *note SET-DEFERRED-TTAG-NOTES::). *Active ttags.* Suppose tag-name is a non-nil symbol. Then (defttag :tag-name) sets :tag-name to be the (unique) "active ttag." There must be an active ttag in order for there to be any mention of certain function and macro symbols, including sys-call; evaluate the form (strip-cars *ttag-fns-and-macros*) to see the full list of such symbols. On the other hand, (defttag nil) removes the active ttag, if any; there is then no active ttag. The scope of a defttag form in a book being certified or included is limited to subsequent forms in the same book before the next defttag (if any) in that book. Similarly, if a defttag form is evaluated in the top-level loop, then its effect is limited to subsequent forms in the top-level loop before the next defttag in the top-level loop (if any). Moreoever, certify-book is illegal when a ttag is active; of course, in such a circumstance one can execute (defttag nil) in order to allow book certification. *Ttag notes and the "certifier."* When a defttag is executed with an argument other than nil, output is printed, starting on a fresh line with: TTAG NOTE. For example: ACL2 !>(defttag :foo) TTAG NOTE: Adding ttag :FOO from the top level loop. :FOO ACL2 !> If the defttag occurs in an included book, the message looks like this. TTAG NOTE (for included book): Adding ttag :FOO from file /u/smith/acl2/my-book.lisp. The "TTAG NOTE" message is always printed on a single line. The intention is that one can search the standard output for all such notes in order to find all defttag events. In a sense, defttag events can allow you to define your own system on top of ACL2 (for example, see *note PROGN!::). So in order for someone else (who we might call the "certifier") to be confident that your collection of books is meaningful, that certifier should certify all the user-supplied books from scratch and check either that no :ttags were supplied to certify-book, or else look for every TTAG NOTE in the standard output in order to locate all defttag events with non-nil tag name. In this way, the certifier can in principle decide whether to be satisfied that those defttag events did not allow inappropriate forms in the user-supplied books. In order to eliminate much of the output from TTAG NOTEs, see *note SET-DEFERRED-TTAG-NOTES::. Note however that the resulting security is somewhat less; therefore, a TTAG NOTE is printed when invoking set-deferred-ttag-notes to defer printing of ttag notes. *Allowed ttags when certifying and including books.* A defttag form may not be evaluated unless its argument is a so-called "allowed" ttag. All ttags are allowed in the interactive top-level loop. However, during certify-book and include-book, the set of allowed ttags is restricted according to the :ttags keyword argument. If this argument is omitted then no ttag is allowed, so a defttag call will fail during book certification or inclusion in this case. This restriction applies even to defttag forms already evaluated in the so-called certification world at the time certify-book is called. But note that (defttag nil) is always legal. A :ttags argument of certify-book and include-book can have value :all, indicating that every ttag is allowed, i.e., no restriction is being placed on the arguments, just as in the interactive top-level loop. In the case of include-book, an omitted :ttags argument or an argument of :default is treated as :all, except that warnings will occur when the book's certificate includes ttags; but for certify-book, an omitted ttags argument is treated as nil. Otherwise, if the :ttags argument is supplied but not :all, then its value is a true list of ttag specifications, each having one of the following forms, where sym is a non-nil symbol which is treated as the corresponding keyword. (1) :sym (2) (:sym) (3) (:sym x1 x2 ... xk), where k > 0 and each xi is a string, except that one xi may be nil. In Case (1), (defttag :sym) is allowed to occur in at most one book or else in the top-level loop (i.e., the certification world for a book under certification or a book being included). Case (2) allows (defttag :sym) to occur in an unlimited number of books. For case (3) the xi specify where (defttag :sym) may occur, as follows. The case that xi is nil refers to the top-level loop, while all other xi are filenames, where the ".lisp" extension is optional and relative pathnames are considered to be relative to the connected book directory (see *note CBD::). Note that the restrictions on (defttag :sym) apply equally to any equivalent for based on the notion of "corresponding keyword" discussed above, e.g., (defttag acl2::sym). An error message, as shown below, illustrates how ACL2 enforcess the notion of allowed ttags. Suppose that you call certify-book with argument :ttags (:foo), where you have already executed (defttag :foo) in the certification world (i.e., before calling certify-book). Then ACL2 immediately associates the ttag :foo with nil, where again, nil refers to the top-level loop. If ACL2 then encounters (defttag foo) inside that book, you will get the following error (using the full book name for the book, as shown): ACL2 Error in ( TABLE ACL2-DEFAULTS-TABLE ...): The ttag :FOO associated with file /u/smith/work/my-book.lisp is not among the set of ttags permitted in the current context, specified as follows: ((:FOO NIL)). See :DOC defttag. In general the structure displayed by the error message, which is ((:FOO NIL)) in this case, represents the currently allowed ttags with elements as discussed in (1) through (3) above. In this case, that list's unique element is (:FOO NIL), meaning that ttag :FOO is only allowed at the top level (as represented by NIL). *Associating ttags with books and with the top-level loop.* When a book is certified, each form (defttag tag) that is encountered for non-nil tag in that book or an included book is recorded in the generated certificate, which associates the keyword corresponding to tag with the full-book-name of the book containing that deftag. If such a defttag form is encountered outside a book, hence in the portcullis of the book being certified or one of its included books, then that keyword is associated with nil in the generated certificate. Note that the notion of "included book" here applies to the recursive notion of a book either included directly in the book being certified or else included in such a book, where we account even for locally included books. For examples of ways to take advantage of ttags, see community book books/hacking/hacker.lisp and see *note TTAGS-SEEN::, see *note PROGN!::, see *note REMOVE-UNTOUCHABLE::, see *note SET-RAW-MODE::, and see *note SYS-CALL::.  File: acl2-doc-emacs.info, Node: DEFUN, Next: DEFUN-INLINE, Prev: DEFTTAG, Up: EVENTS DEFUN define a function symbol Examples: (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y)) (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (zp n) 1 (* n (fact (1- n))))) General Form: (defun fn (var1 ... varn) doc-string dcl ... dcl body), where fn is the symbol you wish to define and is a new symbolic name (see *note NAME::), (var1 ... varn) is its list of formal parameters (see *note NAME::), and body is its body. The definitional axiom is logically admissible provided certain restrictions are met. These are sketched below. Note that ACL2 does not support the use of lambda-list keywords (such as &optional) in the formals list of functions. We do support some such keywords in macros and often you can achieve the desired syntax by defining a macro in addition to the general version of your function. See *note DEFMACRO::. The documentation string, doc-string, is optional; for a description of its form, see *note DOC-STRING::. The _declarations_ (see *note DECLARE::), dcl, are also optional. If more than one dcl form appears, they are effectively grouped together as one. Perhaps the most commonly used ACL2 specific declaration is of the form (declare (xargs :guard g :measure m)). This declaration in the defun of some function fn has the effect of making the "guard" for fn be the term g and the "measure" be the term m. The notion of "measure" is crucial to ACL2's definitional principle. The notion of "guard" is not, and is discussed elsewhere; see *note VERIFY-GUARDS:: and see *note SET-VERIFY-GUARDS-EAGERNESS::. Note that the :measure is ignored in :program mode; see *note DEFUN-MODE::. We now briefly discuss the ACL2 definitional principle, using the following definition form which is offered as a more or less generic example. (defun fn (x y) (declare (xargs :guard (g x y) :measure (m x y))) (if (test x y) (stop x y) (step (fn (d x) y)))) Note that in our generic example, fn has just two arguments, x and y, the guard and measure terms involve both of them, and the body is a simple case split on (test x y) leading to a "non-recursive" branch, (stop x y), and a "recursive" branch. In the recursive branch, fn is called after "decrementing" x to (d x) and some step function is applied to the result. Of course, this generic example is quite specific in form but is intended to illustrate the more general case. Provided this definition is admissible under the logic, as outlined below, it adds the following axiom to the logic. Defining Axiom: (fn x y) = (if (test x y) (stop x y) (step (fn (d x) y))) Note that the guard of fn has no bearing on this logical axiom. This defining axiom is actually implemented in the ACL2 system by a :definition rule, namely (equal (fn x y) (if (test a b) (stop a b) (step (fn (d a) b)))). See *note DEFINITION:: for a discussion of how definition rules are applied. Roughly speaking, the rule causes certain instances of (fn x y) to be replaced by the corresponding instances of the body above. This is called "opening up" (fn x y). The instances of (fn x y) opened are chosen primarily by heuristics which determine that the recursive calls of fn in the opened body (after simplification) are more desirable than the unopened call of fn. This discussion has assumed that the definition of fn was admissible. Exactly what does that mean? First, fn must be a previously unaxiomatized function symbol (however, see *note LD-REDEFINITION-ACTION::). Second, the formal parameters must be distinct variable names. Third, the guard, measure, and body should all be terms and should mention no free variables except the formal parameters. Thus, for example, body may not contain references to "global" or "special" variables; ACL2 constants or additional formals should be used instead. The final conditions on admissibility concern the termination of the recursion. Roughly put, all applications of fn must terminate. In particular, there must exist a binary relation, rel, and some unary predicate mp such that rel is well-founded on objects satisfying mp, the measure term m must always produce something satisfying mp, and the measure term must decrease according to rel in each recursive call, under the hypothesis that all the tests governing the call are satisfied. By the meaning of well-foundedness, we know there are no infinitely descending chains of successively rel-smaller mp-objects. Thus, the recursion must terminate. The only primitive well-founded relation in ACL2 is o< (see *note O<::), which is known to be well-founded on the o-ps (see *note O-P::). For the proof of well-foundedness, see *note PROOF-OF-WELL-FOUNDEDNESS::. However it is possible to add new well-founded relations. For details, see *note WELL-FOUNDED-RELATION::. We discuss later how to specify which well-founded relation is selected by defun and in the present discussion we assume, without loss of generality, that it is o< on the o-ps. For example, for our generic definition of fn above, with measure term (m x y), two theorems must be proved. The first establishes that m produces an ordinal: (o-p (m x y)). The second shows that m decreases in the (only) recursive call of fn: (implies (not (test x y)) (o< (m (d x) y) (m x y))). Observe that in the latter formula we must show that the "m-size" of (d x) and y is "smaller than" the m-size of x and y, provided the test, (test x y), in the body fails, thus leading to the recursive call (fn (d x) y). See *note O<:: for a discussion of this notion of "smaller than." It should be noted that the most commonly used ordinals are the natural numbers and that on natural numbers, o< is just the familiar "less than" relation (<). Thus, it is very common to use a measure m that returns a nonnegative integer, for then (o-p (m x y)) becomes a simple conjecture about the type of m and the second formula above becomes a conjecture about the less-than relationship of nonnegative integer arithmetic. The most commonly used measure function is acl2-count, which computes a nonnegative integer size for all ACL2 objects. See *note ACL2-COUNT::. Probably the most common recursive scheme in Lisp programming is when some formal is supposed to be a list and in the recursive call it is replaced by its cdr. For example, (test x y) might be simply (atom x) and (d x) might be (cdr x). In that case, (acl2-count x) is a suitable measure because the acl2-count of a cons is strictly larger than the acl2-counts of its car and cdr. Thus, "recursion by car" and "recursion by cdr" are trivially admitted if acl2-count is used as the measure and the definition protects every recursive call by a test insuring that the decremented argument is a consp. Similarly, "recursion by 1-" in which a positive integer formal is decremented by one in recursion, is also trivially admissible. See *note BUILT-IN-CLAUSE:: to extend the class of trivially admissible recursive schemes. We now turn to the question of which well-founded relation defun uses. It should first be observed that defun must actually select both a relation (e.g., o<) and a domain predicate (e.g., o-p) on which that relation is known to be well-founded. But, as noted elsewhere (see *note WELL-FOUNDED-RELATION::), every known well-founded relation has a unique domain predicate associated with it and so it suffices to identify simply the relation here. The xargs field of a declare permits the explicit specification of any known well-founded relation with the keyword :well-founded-relation. An example is given below. If the xargs for a defun specifies a well-founded relation, that relation and its associated domain predicate are used in generating the termination conditions for the definition. If no :well-founded-relation is specified, defun uses the :well-founded-relation specified in the acl2-defaults-table. See *note SET-WELL-FOUNDED-RELATION:: to see how to set the default well-founded relation (and, implicitly, its domain predicate). The initial default well-founded relation is o< (with domain predicate o-p). This completes the brief sketch of the ACL2 definitional principle. Optionally, see *note RULER-EXTENDERS:: for a more detailed discussion of the termination analysis and resulting proof obligations for admissibility, as well as a discussion of the relation to how ACL2 stores induction schemes. On very rare occasions ACL2 will seem to "hang" when processing a definition, especially if there are many subexpressions of the body whose function symbol is if (or which macroexpand to such an expression). In those cases you may wish to supply the following to xargs: :normalize nil. This is an advanced feature that turns off ACL2's usual propagation upward of if tests. The following example illustrates all of the available declarations and most hint keywords, but is completely nonsensical. For documentation, see *note XARGS:: and see *note HINTS::. (defun example (x y z a b c i j) (declare (ignore a b c) (type integer i j) (xargs :guard (symbolp x) :measure (- i j) :ruler-extenders :basic :well-founded-relation my-wfr :hints (("Goal" :do-not-induct t :do-not '(generalize fertilize) :expand ((assoc x a) (member y z)) :restrict ((<-trans ((x x) (y (foo x))))) :hands-off (length binary-append) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))))) :guard-hints (("Subgoal *1/3'" :use ((:instance assoc-of-append (x a) (y b) (z c))))) :mode :logic :normalize nil :verify-guards nil :non-executable t :otf-flg t)) (example-body x y z i j))  File: acl2-doc-emacs.info, Node: DEFUN-INLINE, Next: DEFUN-NOTINLINE, Prev: DEFUN, Up: EVENTS DEFUN-INLINE define a potentially inlined function symbol and associated macro You may be able to improve performance by replacing an event (defun f ...) with a corresponding event (defun-inline f ...), in order to encourage the host Lisp compiler to inline calls of f. Example Form: (defun-inline lng (x) (declare (xargs :guard (true-listp x))) (if (endp x) 0 (1+ (lng (cdr x))))) General Form: (defun-inline fn (var1 ... varn) doc-string dcl ... dcl body) satisfying the same requirements as in the General Form for defun. The effect is to define a macro fn and a function fn$inline (i.e., a symbol in the same package as fn but whose symbol-name has the suffix "$INLINE", such that each call of fn expands to a call of the function symbol fn$inline on the same arguments. If doc-string is supplied, then it is associated with fn, not with fn$inline. Moreover, table events are generated that allow the use of fn in theory expressions to represent fn$inline and that cause any untranslated (user-level) call of fn$inline to be printed as the corresponding call of fn. A form (defun-inline f ...) actually defines a function named f$inline and a corresponding macro named f whose calls expand to calls of f$inline, while providing the illusion that there is just the "function" f. For example, the Example Form above macroexpands in one step to the following form. (progn (defmacro lng (non-stobj-var0) (list 'lng$inline non-stobj-var0)) (add-macro-fn lng lng$inline) (defun lng$inline (x) (declare (xargs :guard (true-listp x))) (if (endp x) 0 (1+ (lng (cdr x)))))) Note that the above call of add-macro-fn generates the aforementioned two table events (see *note ADD-MACRO-FN::), which provide the illusion that we are just defining a function lng, as you can see in the following log: lng appears rather than lng$inline. ACL2 !>(set-gag-mode nil) ACL2 !>(thm (equal (lng (append x y)) (+ (lng x) (lng y))) :hints (("Goal" :in-theory (enable lng)))) [.. output omitted ..] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (LNG (APPEND (CDR X) Y)) (+ (LNG (CDR X)) (LNG Y)))) (EQUAL (LNG (APPEND X Y)) (+ (LNG X) (LNG Y)))). [.. output omitted ..] Under the hood, ACL2 arranges that every function symbol with suffix "$INLINE" is presented to the compiler as one whose calls we would prefer to inline. Technically: the Common Lisp form (declaim (inline f$inline)) is generated for a function symbol f$inline before that symbol's definition is submitted. However, the Common Lisp spec explicitly avoids requiring that the compiler respect this declaim form. Fortunately, Common Lisp implementations often do respect it. Also see *note DEFUND-INLINE::, see *note DEFUN-NOTINLINE::, and see *note DEFUND-NOTINLINE::. _Remarks_. (1) None of these macros (including defun-inline) is supported for use inside a mutual-recursion. (2) Every function symbol defined in ACL2 whose symbol-name has the suffix "$INLINE" is proclaimed to be inline; similarly for "$NOTINLINE" and notinline. (3) No special treatment for inlining (or notinlining) is given for function symbols locally defined by flet, with two exceptions: when explicitly declared inline or notinline by the flet form, and for symbols discussed in (1) and (2) above that, at some point in the current ACL2 session, were defined as function symbols in ACL2 (even if not currently defined because of undoing or being local). (4) The function symbol actually being defined by (defun-inline foo ...) is foo$inline. As mentioned above, one can be oblivious to this fact when writing theory expressions or perusing prover output. However, for other purposes (for example, verify-guards and defabsstobj :exports) you will need to supply the name of the function symbol rather than the name of the macro; e.g., for the above form (defun-inline foo ...), you may subsequently issue the event (verify-guards foo$inline) but not (verify-guards foo). (5) Obscure Remark. Suppose that you certify a book with compilation (the default) in one host Lisp, saving the expansion file. Suppose that you then compile in another host Lisp by using include-book with argument :load-compiled-file :comp. Then in subsequent sessions, including that book with the second host Lisp will not result in any inline or notinline behavior for functions defined in the book. This may be fixed in a future release if someone complains.  File: acl2-doc-emacs.info, Node: DEFUN-NOTINLINE, Next: DEFUN-NX, Prev: DEFUN-INLINE, Up: EVENTS DEFUN-NOTINLINE define a not-to-be-inlined function symbol and associated macro See *note DEFUN-INLINE:: for an analogous utility that supports inlining. The present utility is probably far less useful; it tells the compiler _not_ to inline calls of the function being defined. Also see *note DEFUND-NOTINLINE:: for a variant of this event that disables the newly-defined function symbol. Under the hood, (defun-inline f ...) and (defun-notinline f ...) cause evaluation of Common Lisp forms (declaim (inline f$inline)) and (declaim (notinline f$notinline)), respectively. According to the Common Lisp spec, the compiler need not respect the first of these (for inline), but it must respect the second of these (for notinline). Fortunately, Common Lisp implementations often do respect the first of these as well.  File: acl2-doc-emacs.info, Node: DEFUN-NX, Next: DEFUN-SK, Prev: DEFUN-NOTINLINE, Up: EVENTS DEFUN-NX define a non-executable function symbol Example: (set-state-ok t) (defun-nx foo (x state) (mv-let (a b c) (cons x state) (list a b c b a))) ; Note ``ill-formed'' call of foo just below. (defun bar (state y) (foo state y)) The macro defun-nx introduces definitions using the defun macro, always in :logic mode, such that the calls of the resulting function cannot be evaluated. Such a definition is admitted without enforcing syntactic restrictions for executability, in particular for single-threadedness (see *note STOBJ::) and multiple-values passing (see *note MV:: and see *note MV-LET::). After such a definition is admitted, the usual syntactic rules for state and user-defined stobjs are relaxed for calls of the function it defines. Also see *note NON-EXEC:: for a way to designate subterms of function bodies, or subterms of code to be executed at the top level, as non-executable. The syntax of defun-nx is identical to that of defun. A form (defun-nx name (x1 ... xk) ... body) expands to the following form. (defun name (x1 ... xk) (declare (xargs :non-executable t :mode :logic)) ... (prog2$ (throw-nonexec-error 'name (list x1 ... xk)) body)) Note that because of the insertion of the above call of throw-nonexec-error, no formal is ignored when using defun-nx. During proofs, the error is silent; it is "caught" by the proof mechanism and generally results in the introduction of a call of hide during a proof. If an error message is produced by evaluating a call of the function on a list of arguments that includes state or user-defined stobjs, these arguments will be shown as symbols such as || in the error message. In the case of a user-defined stobj bound by with-local-stobj or stobj-let, the symbol printed will include the suffix {instance}, for example, |{instance}|. It is harmless to include :non-executable t in your own xargs declare form; defun-nx will still lay down its own such declaration, but ACL2 can tolerate the duplication. Note that defund-nx is also available. It has an effect identical to that of defun-nx except that as with defund, it leaves the function disabled. If you use guards (see *note GUARD::), please be aware that even though syntactic restrictions are relaxed for defun-nx, guard verification proceeds exactly as for defun. If you want ACL2 to skip a form for purposes of generating guard proof obligations, use the macro non-exec, which generates a call of throw-nonexec-error that differs somewhat from the one displayed above. See *note NON-EXEC::. See *note DEFUN:: for documentation of defun. acl2-sources/doc/EMACS/acl2-doc-emacs.info-40000664002132200015000000111317612222333541017647 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: DEFUN-SK, Next: DEFUND, Prev: DEFUN-NX, Up: EVENTS DEFUN-SK define a function whose body has an outermost quantifier Examples: (defun-sk exists-x-p0-and-q0 (y z) (exists x (and (p0 x y z) (q0 x y z)))) (defun-sk exists-x-p0-and-q0 (y z) ; equivalent to the above (exists (x) (and (p0 x y z) (q0 x y z)))) (defun-sk forall-x-y-p0-and-q0 (z) (forall (x y) (and (p0 x y z) (q0 x y z))) :strengthen t) * Menu: * DEFUN-SK-EXAMPLE:: a simple example using defun-sk * EXISTS:: existential quantifier * FORALL:: universal quantifier * QUANTIFIER-TUTORIAL:: A Beginner's Guide to Reasoning about Quantification in ACL2 * QUANTIFIERS:: issues about quantification in ACL2 General Form: (defun-sk fn (var1 ... varn) body &key rewrite doc quant-ok skolem-name thm-name witness-dcls strengthen) where fn is the symbol you wish to define and is a new symbolic name (see *note NAME::), (var1 ... varn) is its list of formal parameters (see *note NAME::), and body is its body, which must be quantified as described below. The &key argument doc is an optional documentation string to be associated with fn; for a description of its form, see *note DOC-STRING::. In the case that n is 1, the list (var1) may be replaced by simply var1. The other arguments are explained below. For a simple example, see *note DEFUN-SK-EXAMPLE::. For a more elaborate example, see *note TUTORIAL4-DEFUN-SK-EXAMPLE::. See *note QUANTIFIER-TUTORIAL:: for a careful beginner's introduction that takes you through typical kinds of quantifier-based reasoning in ACL2. Also see *note QUANTIFIERS:: for an example illustrating how the use of recursion, rather than explicit quantification with defun-sk, may be preferable. Below we describe the defun-sk event precisely. First, let us consider the examples above. The first example, again, is: (defun-sk exists-x-p0-and-q0 (y z) (exists x (and (p0 x y z) (q0 x y z)))) It is intended to represent the predicate with formal parameters y and z that holds when for some x, (and (p0 x y z) (q0 x y z)) holds. In fact defun-sk is a macro that adds the following two events, as shown just below. The first event guarantees that if this new predicate holds of y and z, then the term shown, (exists-x-p0-and-q0-witness y z), is an example of the x that is therefore supposed to exist. (Intuitively, we are axiomatizing exists-x-p0-and-q0-witness to pick a witness if there is one. We comment below on the use of defun-nx; for now, consider defun-nx to be defun.) Conversely, the second event below guarantees that if there is any x for which the term in question holds, then the new predicate does indeed hold of y and z. (defun-nx exists-x-p0-and-q0 (y z) (let ((x (exists-x-p0-and-q0-witness y z))) (and (p0 x y z) (q0 x y z)))) (defthm exists-x-p0-and-q0-suff (implies (and (p0 x y z) (q0 x y z)) (exists-x-p0-and-q0 y z))) Now let us look at the third example from the introduction above: (defun-sk forall-x-y-p0-and-q0 (z) (forall (x y) (and (p0 x y z) (q0 x y z)))) The intention is to introduce a new predicate (forall-x-y-p0-and-q0 z) which states that the indicated conjunction holds of all x and all y together with the given z. This time, the axioms introduced are as shown below. The first event guarantees that if the application of function forall-x-y-p0-and-q0-witness to z picks out values x and y for which the given term (and (p0 x y z) (q0 x y z)) holds, then the new predicate forall-x-y-p0-and-q0 holds of z. Conversely, the (contrapositive of) the second axiom guarantees that if the new predicate holds of z, then the given term holds for all choices of x and y (and that same z). (defun-nx forall-x-y-p0-and-q0 (z) (mv-let (x y) (forall-x-y-p0-and-q0-witness z) (and (p0 x y z) (q0 x y z)))) (defthm forall-x-y-p0-and-q0-necc (implies (not (and (p0 x y z) (q0 x y z))) (not (forall-x-y-p0-and-q0 z)))) The examples above suggest the critical property of defun-sk: it indeed does introduce the quantified notions that it claims to introduce. Notice that the defthm event just above, forall-x-y-p0-and-q0-necc, may not be of optimal form as a rewrite rule. Users sometimes find that when the quantifier is forall, it is useful to state this rule in a form where the new quantified predicate is a hypothesis instead. In this case that form would be as follows: (defthm forall-x-y-p0-and-q0-necc (implies (forall-x-y-p0-and-q0 z) (and (p0 x y z) (q0 x y z)))) ACL2 will turn this into one :rewrite rule for each conjunct, (p0 x y z) and (q0 x y z), with hypothesis (forall-x-y-p0-and-q0 z) in each case. In order to get this effect, use :rewrite :direct, in this case as follows. (defun-sk forall-x-y-p0-and-q0 (z) (forall (x y) (and (p0 x y z) (q0 x y z))) :rewrite :direct) We now turn to a detailed description of defun-sk, starting with a discussion of its arguments as shown in the "General Form" above. The third argument, body, must be of the form (Q bound-vars term) where: Q is the symbol forall or exists (in the "ACL2" package), bound-vars is a variable or true list of variables disjoint from (var1 ... varn) and not including state, and term is a term. The case that bound-vars is a single variable v is treated exactly the same as the case that bound-vars is (v). The result of this event is to introduce a "Skolem function," whose name is the keyword argument skolem-name if that is supplied, and otherwise is the result of modifying fn by suffixing "-WITNESS" to its name. The following definition and one of the following two theorems (as indicated) are introduced for skolem-name and fn in the case that bound-vars (see above) is a single variable v. The name of the defthm event may be supplied as the value of the keyword argument :thm-name; if it is not supplied, then it is the result of modifying fn by suffixing "-SUFF" to its name in the case that the quantifier is exists, and "-NECC" in the case that the quantifier is forall. (defun-nx fn (var1 ... varn) (let ((v (skolem-name var1 ... varn))) term)) (defthm fn-suff ;in case the quantifier is EXISTS (implies term (fn var1 ... varn))) (defthm fn-necc ;in case the quantifier is FORALL (implies (not term) (not (fn var1 ... varn)))) In the forall case, however, the keyword pair :rewrite :direct may be supplied after the body of the defun-sk form, in which case the contrapositive of the above form is used instead: (defthm fn-necc ;in case the quantifier is FORALL (implies (fn var1 ... varn) term)) This is often a better choice for the "-NECC" rule, provided ACL2 can parse term as a :rewrite rule. A second possible value of the :rewrite argument of defun-sk is :default, which gives the same behavior as when :rewrite is omitted. Otherwise, the value of :rewrite should be the term to use as the body of the fn-necc theorem shown above; ACL2 will attempt to do the requisite proof in this case. If that term is weaker than the default, the properties introduced by defun-sk may of course be weaker than they would be otherwise. Finally, note that the :rewrite keyword argument for defun-sk only makes sense if the quantifier is forall; it is thus illegal if the quantifier is exists. Enough said about :rewrite! In the case that bound-vars is a list of at least two variables, say (bv1 ... bvk), the definition above (with no keywords) is the following instead, but the theorem remains unchanged. (defun-nx fn (var1 ... varn) (mv-let (bv1 ... bvk) (skolem-name var1 ... varn) term)) In order to emphasize that the last element of the list, body, is a term, defun-sk checks that the symbols forall and exists do not appear anywhere in it. However, on rare occasions one might deliberately choose to violate this convention, presumably because forall or exists is being used as a variable or because a macro call will be eliminating "calls of" forall and exists. In these cases, the keyword argument quant-ok may be supplied a non-nil value. Then defun-sk will permit forall and exists in the body, but it will still cause an error if there is a real attempt to use these symbols as quantifiers. The use of defun-nx above, rather than defun, disables certain checks that are required for evaluation, in particular the single-threaded use of stobjs. However, there is a price: calls of these defined functions cannot be evaluated; see *note DEFUN-NX::. Normally that is not a problem, since these notions involve quantifiers. But you are welcome to replace this declare form with your own, as follows: if you supply a list of declare forms to keyword argument :witness-dcls, these will become the declare forms in the generated defun. Note that if your value of witness-dcls does not contain the form (declare (xargs :non-executable t)), then the appropriate wrapper for non-executable functions will not be added automatically, i.e., defun will be used in place of defun-nx. Note also that if guard verification is attempted, then it will likely fail with an error message complaining that "guard verification may depend on local properties." In that case, you may wish to delay guard verification, as in the following example. (encapsulate () (defun-sk foo (x) (exists n (and (integerp n) (< n x))) :witness-dcls ((declare (xargs :guard (integerp x) :verify-guards nil)))) (verify-guards foo)) Defun-sk is a macro implemented using defchoose. Hence, it should only be executed in defun-mode :logic; see *note DEFUN-MODE:: and see *note DEFCHOOSE::. Advanced feature: If argument :strengthen t is passed to defun-sk, then :strengthen t will generate the extra constraint that that is generated for the corresponding defchoose event; see *note DEFCHOOSE::. You can use the command :pcb! to see the event generated by a call of the defun-sk macro. If you find that the rewrite rules introduced with a particular use of defun-sk are not ideal, even when using the :rewrite keyword discussed above (in the forall case), then at least two reasonable courses of action are available for you. Perhaps the best option is to prove the rewrite rules you want. If you see a pattern for creating rewrite rules from your defun-sk events, you might want to write a macro that executes a defun-sk followed by one or more defthm events. Another option is to write your own variant of the defun-sk macro, say, my-defun-sk, for example by modifying a copy of the definition of defun-sk from the ACL2 sources. If you want to represent nested quantifiers, you can use more than one defun-sk event. For example, in order to represent (forall x (exists y (p x y z))) you can use defun-sk twice, for example as follows. (defun-sk exists-y-p (x z) (exists y (p x y z))) (defun-sk forall-x-exists-y-p (z) (forall x (exists-y-p x z))) Some distracting and unimportant warnings are inhibited during defun-sk. Note for ACL2(r) users (see *note REAL::): In ACL2(r), the keyword :CLASSICALP is also supported. Its legal values are t (the default) and nil, and it determines whether or not (respectively) ACL2(r) will consider fn to be a classical function. It must be the case that the value is t (perhaps implicitly, by default) if and only if body is classical. Note that this way of implementing quantifiers is not a new idea. Hilbert was certainly aware of it 60 years ago! Also see *note CONSERVATIVITY-OF-DEFCHOOSE:: for a technical argument that justifies the logical conservativity of the defchoose event in the sense of the paper by Kaufmann and Moore entitled "Structured Theory Development for a Mechanized Logic" (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203).  File: acl2-doc-emacs.info, Node: DEFUN-SK-EXAMPLE, Next: EXISTS, Prev: DEFUN-SK, Up: DEFUN-SK DEFUN-SK-EXAMPLE a simple example using defun-sk For a more through, systematic beginner's introduction to quantification in ACL2, see *note QUANTIFIER-TUTORIAL::. The following example illustrates how to do proofs about functions defined with defun-sk. The events below can be put into a certifiable book (see *note BOOKS::). The example is contrived and rather silly, in that it shows how to prove that a quantified notion implies itself, where the antecedent and conclusion are defined with different defun-sk events. But it illustrates the formulas that are generated by defun-sk, and how to use them. Thanks to Julien Schmaltz for presenting this example as a challenge. (in-package "ACL2") (encapsulate (((p *) => *) ((expr *) => *)) (local (defun p (x) x)) (local (defun expr (x) x))) (defun-sk forall-expr1 (x) (forall (y) (implies (p x) (expr y)))) (defun-sk forall-expr2 (x) (forall (y) (implies (p x) (expr y))))) ; We want to prove the theorem my-theorem below. What axioms are there that ; can help us? If you submit the command ; :pcb! forall-expr1 ; then you will see the following two key events. (They are completely ; analogous of course for FORALL-EXPR2.) ; (DEFUN FORALL-EXPR1 (X) ; (LET ((Y (FORALL-EXPR1-WITNESS X))) ; (IMPLIES (P X) (EXPR Y)))) ; ; (DEFTHM FORALL-EXPR1-NECC ; (IMPLIES (NOT (IMPLIES (P X) (EXPR Y))) ; (NOT (FORALL-EXPR1 X))) ; :HINTS ; (("Goal" :USE FORALL-EXPR1-WITNESS))) ; We see that the latter has value when FORALL-EXPR1 occurs negated in a ; conclusion, or (therefore) positively in a hypothesis. A good rule to ; remember is that the former has value in the opposite circumstance: negated ; in a hypothesis or positively in a conclusion. ; In our theorem, FORALL-EXPR2 occurs positively in the conclusion, so its ; definition should be of use. We therefore leave its definition enabled, ; and disable the definition of FORALL-EXPR1. ; (thm ; (implies (and (p x) (forall-expr1 x)) ; (forall-expr2 x)) ; :hints (("Goal" :in-theory (disable forall-expr1)))) ; ; ; which yields this unproved subgoal: ; ; (IMPLIES (AND (P X) (FORALL-EXPR1 X)) ; (EXPR (FORALL-EXPR2-WITNESS X))) ; Now we can see how to use FORALL-EXPR1-NECC to complete the proof, by ; binding y to (FORALL-EXPR2-WITNESS X). ; We use defthmd below so that the following doesn't interfere with the ; second proof, in my-theorem-again that follows. (defthmd my-theorem (implies (and (p x) (forall-expr1 x)) (forall-expr2 x)) :hints (("Goal" :use ((:instance forall-expr1-necc (x x) (y (forall-expr2-witness x))))))) ; The following illustrates a more advanced technique to consider in such ; cases. If we disable forall-expr1, then we can similarly succeed by having ; FORALL-EXPR1-NECC applied as a :rewrite rule, with an appropriate hint in how ; to instantiate its free variable. See :doc hints. (defthm my-theorem-again (implies (and (P x) (forall-expr1 x)) (forall-expr2 x)) :hints (("Goal" :in-theory (disable forall-expr1) :restrict ((forall-expr1-necc ((y (forall-expr2-witness x))))))))  File: acl2-doc-emacs.info, Node: EXISTS, Next: FORALL, Prev: DEFUN-SK-EXAMPLE, Up: DEFUN-SK EXISTS existential quantifier The symbol exists (in the ACL2 package) represents existential quantification in the context of a defun-sk form. See *note DEFUN-SK:: and see *note FORALL::. See *note QUANTIFIERS:: for an example illustrating how the use of recursion, rather than explicit quantification with defun-sk, may be preferable.  File: acl2-doc-emacs.info, Node: FORALL, Next: QUANTIFIER-TUTORIAL, Prev: EXISTS, Up: DEFUN-SK FORALL universal quantifier The symbol forall (in the ACL2 package) represents universal quantification in the context of a defun-sk form. See *note DEFUN-SK:: and see *note EXISTS::. See *note QUANTIFIERS:: for an example illustrating how the use of recursion, rather than explicit quantification with defun-sk, may be preferable.  File: acl2-doc-emacs.info, Node: QUANTIFIER-TUTORIAL, Next: QUANTIFIERS, Prev: FORALL, Up: DEFUN-SK QUANTIFIER-TUTORIAL A Beginner's Guide to Reasoning about Quantification in ACL2 The initial version of this tutorial was written by Sandip Ray. Additions and revisions are welcome. Sandip has said: "This is a collection of notes that I wrote to remind myself of how to reason about quantifiers when I just started. Most users after they have gotten the hang of quantifiers probably will not need this and will be able to use their intuitions to guide them in the process. But since many ACL2 users are not used to quantification, I am hoping that this set of notes might help them to think clearly while reasoning about quantifiers in ACL2." Many ACL2 papers start with the sentence "ACL2 is a quantifier-free first-order logic of recursive functions." It is true that the _syntax_ of ACL2 is quantifier-free; every formula is assumed to be universally quantified over all free variables in the formula. But the _logic_ in fact does afford arbitrary first-order quantification. This is obtained in ACL2 using a construct called defun-sk. See *note DEFUN-SK::. Many ACL2 users do not think in terms of quantifiers. The focus is almost always on defining recursive functions and reasoning about them using induction. That is entirely justified, in fact, since proving theorems about recursive functions by induction plays to the strengths of the theorem prover. Nevertheless there are situations where it is reasonable and often useful to think in terms of quantifiers. However, reasoning about quantifiers requires that you get into the mindset of thinking about theorems in terms of quantification. This note is about how to do this effectively given ACL2's implementation of quantification. This does not discuss defun-sk in detail, but merely shows some examples. A detailed explanation of the implementation is in the ACL2 documentation (see *note DEFUN-SK::); also see *note CONSERVATIVITY-OF-DEFCHOOSE::. [Note: Quantifiers can be used for some pretty cool things in ACL2. Perhaps the most interesting example is the way of using quantifiers to introduce arbitrary tail-recursive equations; see the paper "Partial Functions in ACL2" by Panagiotis Manolios and J Strother Moore. This note does not address applications of quantifiers, but merely how you would reason about them once you think you want to use them.] Assume that you have some function P. I have just left P as a unary function stub below, since I do not care about what P is. (defstub P (*) => *) Now suppose you want to specify the concept that "there exists some x such that (P x) holds". ACL2 allows you to write that directly using quantifiers. (defun-sk exists-P () (exists x (P x))) If you submit the above form in ACL2 you will see that the theorem prover specifies two functions exists-p and exists-p-witness, and exports the following constraints: 1. (defun exists-P () (P (exists-P-witness))) 2. (defthm exists-P-suff (implies (p x) (exists-p))) Here exists-P-witness is a new function symbol in the current ACL2 theory. What do the constraints above say? Notice the constraint exists-p-suff. It says that if you can provide any x such that (P x) holds, then you know that exists-p holds. Think of the other constraint (definition of exists-p) as going the other way. That is, it says that if exists-p holds, then there is some x, call it (exists-p-witness), for which P holds. Notice that nothing else is known about exists-p-witness than the two constraints above. [Note: exists-p-witness above is actually defined in ACL2 using a special form called defchoose. See *note DEFCHOOSE::. This note does not talk about defchoose. So far as this note is concerned, think of exists-p-witness as a new function symbol that has been generated somehow in ACL2, about which nothing other than the two facts above is known.] Similarly, you can talk about the concept that "for all x (P x) holds." This can be specified in ACL2 by the form: (defun-sk forall-P () (forall x (P x))) This produces the following two constraints: 1. (defun forall-P () (P (forall-p-witness))) 2. (defthm forall-p-necc (implies (not (P x)) (not (forall-p)))) To understand these, think of for-all-p-witness as producing some x which does not satisfy P, if such a thing exists. The constraint forall-p-necc merely says that if forall-p holds then P is satisfied for every x. (To see this more clearly, just think of the contrapositive of the formula shown.) The other constraint (definition of forall-p) implies that if forall-p does not hold then there is some x, call it (forall-p-witness), which does not satisfy P. To see this, just consider the following formula which is immediately derivable from the definition. (implies (not (forall-p)) (not (P (forall-witness)))) The description above suggests that to reason about quantifiers, the following Rules of Thumb, familiar to most any student of logic, are useful. RT1: To prove (exists-p), construct some object A such that P holds for A and then use exists-P-suff. RT2: If you assume exists-P in your hypothesis, use the definition of exists-p to know that P holds for exists-p-witness. To use this to prove a theorem, you must be able to derive the theorem based on the hypothesis that P holds for something, whatever the something is. RT3: To prove forall-P, prove the theorem (P x) (that is, that P holds for an arbitrary x), and then simply instantiate the definition of forall-p, that is, show that P holds for the witness. RT4: If you assume forall-p in the hypothesis of the theorem, see how you can prove your conclusion if indeed you were given (P x) as a theorem. Possibly for the conclusion to hold, you needed that P holds for some specific set of x values. Then use the theorem forall-p-necc by instantiating it for the specific x values you care about. Perhaps the above is too terse. In the remainder of the note, we will consider several examples of how this is done to prove theorems in ACL2 that involve quantified notions. Let us consider two trivial theorems. Assume that for some unary function r, you have proved (r x) as a theorem. Let us see how you can prove that (1) there exists some x such that (r x) holds, and (2) for all x (r x) holds. We first model these things using defun-sk. Below, r is simply some function for which (r x) is a theorem. (encapsulate (((r *) => *)) (local (defun r (x) (declare (ignore x)) t)) (defthm r-holds (r x))) (defun-sk exists-r () (exists x (r x))) (defun-sk forall-r () (forall x (r x))) ACL2 does not have too much reasoning support for quantifiers. So in most cases, one would need :use hints to reason about quantifiers. In order to apply :use hints, it is preferable to keep the function definitions and theorems disabled. (in-theory (disable exists-r exists-r-suff forall-r forall-r-necc)) Let us now prove that there is some x such that (r x) holds. Since we want to prove exists-r, we must use exists-r-suff by RT1. We do not need to construct any instance here since r holds for all x by the theorem above. (defthm exists-r-holds (exists-r) :hints (("Goal" :use ((:instance exists-r-suff))))) Let us now prove the theorem that for all x, (r x) holds. By RT3, we must be able to prove it by definition of forall-r. (defthm forall-r-holds (forall-r) :hints (("Goal" :use ((:instance (:definition forall-r)))))) [Note: Probably no ACL2 user in his or her right mind would prove the theorems exists-r-holds and forall-r-holds above. The theorems shown are only for demonstration purposes.] For the remainder of this note we will assume that we have two stubbed out unary functions M and N, and we will look at proving some quantified properties of these functions. (defstub M (*) => *) (defstub N (*) => *) Let us now define the predicates all-M, all-N, ex-M, and ex-N specifying the various quantifications. (defun-sk all-M () (forall x (M x))) (defun-sk all-N () (forall x (N x))) (defun-sk some-M () (exists x (M x))) (defun-sk some-N () (exists x (N x))) (in-theory (disable all-M all-N all-M-necc all-N-necc)) (in-theory (disable some-M some-N some-M-suff some-N-suff)) Let us prove the classic distributive properties of quantification: the distributivity of universal quantification over conjunction, and the distributivity of existential quantification over disjunction. We can state these properties informally in "pseudo ACL2" notation as follows: 1. (exists x: (M x)) or (exists x: (N x)) <=> (exists x: (M x) or (N x)) 2. (forall x: (M x)) and (forall: x (N x)) <=> (forall x: (M x) and (N x)) To make these notions formal we of course need to define the formulas at the right-hand sides of 1 and 2. So we define some-MN and all-MN to capture these concepts. (defun-sk some-MN () (exists x (or (M x) (N x)))) (defun-sk all-MN () (forall x (and (M x) (N x)))) (in-theory (disable all-MN all-MN-necc some-MN some-MN-suff)) First consider proving property 1. The formal statement of this theorem would be: (iff (some-MN) (or (some-M) (some-N))). How do we prove this theorem? Looking at RT1-RT4 above, note that they suggest how one should reason about quantification when one has an "implication". But here we have an "equivalence". This suggests another rule of thumb. RT5: Whenever possible, prove an equivalence involving quantifiers by proving two implications. Let us apply RT5 to prove the theorems above. So we will first prove: (implies (some-MN) (or (some-M) (some-N))) How can we prove this? This involves assuming a quantified predicate (some-MN), so we must use RT2 and apply the definition of some-MN. Since the conclusion involves a disjunction of two quantified predicates, by RT1 we must be able to construct two objects A and B such that either M holds for A or N holds for B, so that we can then invoke some-M-suff and some-N-suff to prove the conclusion. But now notice that if some-MN is true, then there is already an object, in fact some-MN-witness, such that either M holds for it, or N holds for it. And we know this is the case from the definition of some-MN! So we will simply prove the theorem instantiating some-M-suff and some-N-suff with this witness. The conclusion is that the following event will go through with ACL2. (defthm le1 (implies (some-MN) (or (some-M) (some-N))) :rule-classes nil :hints (("Goal" :use ((:instance (:definition some-MN)) (:instance some-M-suff (x (some-MN-witness))) (:instance some-N-suff (x (some-MN-witness))))))) This also suggests the following rule of thumb: RT6: If a conjecture involves assuming an existentially quantified predicate in the hypothesis from which you are trying to prove an existentially quantified predicate, use the witness of the existential quantification in the hypothesis to construct the witness for the existential quantification in the conclusion. Let us now try to prove the converse of le1, that is: (implies (or (some-M) (some-N)) (some-MN)) Since the hypothesis is a disjunction, we will just prove each case individually instead of proving the theorem by a :cases hint. So we prove the following two lemmas. (defthm le2 (implies (some-M) (some-MN)) :rule-classes nil :hints (("Goal" :use ((:instance (:definition some-M)) (:instance some-MN-suff (x (some-M-witness))))))) (defthm le3 (implies (some-N) (some-MN)) :rule-classes nil :hints (("Goal" :use ((:instance (:definition some-N)) (:instance some-MN-suff (x (some-N-witness))))))) Note that the hints above are simply applications of RT6 as in le1. With these lemmas, of course the main theorem is trivial. (defthmd |some disjunction| (iff (some-MN) (or (some-M) (some-N))) :hints (("Goal" :use ((:instance le1) (:instance le2) (:instance le3))))) Let us now prove the distributivity of universal quantification over conjunction, that is, the formula: (iff (all-MN) (and (all-M) (all-N))) Applying RT5, we will again decompose this into two implications. So consider first the one-way implication: (implies (and (all-M) (all-N)) (all-MN)). Here we get to assume all-M and all-N. Thus by RT4 we can use all-M-necc and all-N-necc to think as if we are given the formulas (M x) and (N x) as theorems. The conclusion here is also a universal quantification, namely we have to prove all-MN. Then RT3 tells us to proceed as follows. Take any object y. Try to find an instantiation z of the hypothesis that implies (and (M y) (N y)). Then instantiate y with all-MN-witness. Note that the hypothesis lets us assume (M x) and (N x) to be theorems. Thus to justify we need to instantiate x with y, and in this case, therefore, with all-MN-witness. To make the long story short, the following event goes through with ACL2: (defthm lf1 (implies (and (all-M) (all-N)) (all-MN)) :rule-classes nil :hints (("Goal" :use ((:instance (:definition all-MN)) (:instance all-M-necc (x (all-MN-witness))) (:instance all-N-necc (x (all-MN-witness))))))) This suggests the following rule of thumb which is a dual of RT6: RT7: If a conjecture assumes some universally quantified predicate in the hypothesis and its conclusion asserts a universallly quantified predicate, then instantiate the "necessary condition" (forall-mn-necc) of the hypothesis with the witness of the conclusion to prove the conjecture. Applying RT7 now we can easily prove the other theorems that we need to show that universal quantification distributes over conjunction. Let us just go through this motion in ACL2. (defthm lf2 (implies (all-MN) (all-M)) :rule-classes nil :hints (("Goal" :use ((:instance (:definition all-M)) (:instance all-MN-necc (x (all-M-witness))))))) (defthm lf3 (implies (all-MN) (all-N)) :rule-classes nil :hints (("Goal" :use ((:instance (:definition all-N)) (:instance all-MN-necc (x (all-N-witness))))))) (defthmd |all conjunction| (iff (all-MN) (and (all-M) (all-N))) :hints (("Goal" :use ((:instance lf1) (:instance lf2) (:instance lf3))))) The rules of thumb for universal and existential quantification should make you realize the duality of their use. Every reasoning method about universal quantification can be cast as a way of reasoning about existential quantification, and vice versa. Whether you reason using universal and existential quantifiers depends on what is natural in a particular context. But just for the sake of completeness let us prove the duality of universal and existential quantifiers. So what we want to prove is the following: 3. (forall x (not (M x))) = (not (exists x (M x))) We first formalize the notion of (forall x (not (M x))) as a quantification. (defun-sk none-M () (forall x (not (M x)))) (in-theory (disable none-M none-M-necc)) So we now want to prove: (equal (none-M) (not (some-M))). As before, we should prove this as a pair of implications. So let us prove first: (implies (none-M) (not (some-M))). This may seem to assert an existential quantification in the conclusion, but rather, it asserts the _negation_ of an existential quantification. We are now trying to prove that something does not exist. How do we do that? We can show that nothing satisfies M by just showing that (some-M-witness) does not satisfy M. This suggests the following rule of thumb: RT8: When you encounter the negation of an existential quantification think in terms of a universal quantification, and vice-versa. Ok, so now applying RT8 and RT3 you should be trying to apply the definition of some-M. The hypothesis is just a pure (non-negated) universal quantification so you should apply RT4. A blind application lets us prove the theorem as below. (defthm nl1 (implies (none-M) (not (some-M))) :rule-classes nil :hints (("Goal" :use ((:instance (:definition some-M)) (:instance none-M-necc (x (some-M-witness))))))) How about the converse implication? I have deliberately written it as (implies (not (none-M)) (some-M)) instead of switching the left-hand and right-hand sides of nl1, which would have been equivalent. Again, RH8 tells us how to reason about it, in this case using RH2, and we succeed. (defthm nl2 (implies (not (none-M)) (some-M)) :rule-classes nil :hints (("Goal" :use ((:instance (:definition none-M)) (:instance some-M-suff (x (none-M-witness))))))) So finally we just go through the motions of proving the equality. (defthmd |forall not = not exists| (equal (none-M) (not (some-M))) :hints (("Goal" :use ((:instance nl1) (:instance nl2))))) Let us now see if we can prove a slightly more advanced theorem which can be stated informally as: If there is a natural number x which satisfies M, then there is a least natural number y that satisfies M. [Note: Any time I have had to reason about existential quantification I have had to do this particular style of reasoning and state that if there is an object satisfying a predicate, then there is also a "minimal" object satisfying the predicate.] Let us formalize this concept. We first define the concept of existence of a natural number satisfying x. (defun-sk some-nat-M () (exists x (and (natp x) (M x)))) (in-theory (disable some-nat-M some-nat-M-suff)) We now talk about what it means to say that x is the least number satisfying M. (defun-sk none-below (y) (forall r (implies (and (natp r) (< r y)) (not (M r)))))) (in-theory (disable none-below none-below-necc)) (defun-sk min-M () (exists y (and (M y) (natp y) (none-below y)))) (in-theory (disable min-M min-M-suff)) The predicate none-below says that no natural number less than y satisfies M. The predicate min-M says that there is some natural number y satisfying M such that none-below holds for y. So the formula we want to prove is: (implies (some-nat-M) (min-M)). Since the formula requires that we prove an existential quantification, RT1 tells us to construct some object satisfying the predicate over which we are quantifying. We should then be able to instantiate min-M-suff with this object. That predicate says that the object must be the least natural number that satisfies M. Since such an object is uniquely computable if we know that there exists some natural number satisfying M, let us just write a recursive function to compute it. This function is least-M below. (defun least-M-aux (i bound) (declare (xargs :measure (nfix (- (1+ bound) i)))) (cond ((or (not (natp i)) (not (natp bound)) (> i bound)) 0) ((M i) i) (t (least-M-aux (+ i 1) bound)))) (defun least-M (bound) (least-M-aux 0 bound)) Let us now reason about this function as one does typically. So we prove that this object is indeed the least natural number that satisfies M, assuming that bound is a natural number that satisfies M. (defthm least-aux-produces-an-M (implies (and (natp i) (natp bound) (<= i bound) (M bound)) (M (least-M-aux i bound)))) (defthm least-<=bound (implies (<= 0 bound) (<= (least-M-aux i bound) bound))) (defthm least-aux-produces-least (implies (and (natp i) (natp j) (natp bound) (<= i j) (<= j bound) (M j)) (<= (least-M-aux i bound) j))) (defthm least-aux-produces-natp (natp (least-M-aux i bound))) (defthmd least-is-minimal-satisfying-m (implies (and (natp bound) (natp i) (< i (least-M bound))) (not (M i))) :hints (("Goal" :in-theory (disable least-aux-produces-least least-<=bound) :use ((:instance least-<=bound (i 0)) (:instance least-aux-produces-least (i 0) (j i)))))) (defthm least-has-m (implies (and (natp bound) (m bound)) (M (least-M bound)))) (defthm least-is-natp (natp (least-M bound))) So we have done that, and hopefully this is all that we need about least-M. So we disable everything. (in-theory (disable least-M natp)) Now of course we note that the statement of the conjecture we are interested in has two quantifiers, an inner forall (from none-below) and an outer exists (from min-M). Since ACL2 is not very good with quantification, we hold its hands to reason with the quantifier part. So we will first prove something about the forall and then use it to prove what we need about the exists. RT9: When you face nested quantifiers, reason about each nesting separately. So what do we want to prove about the inner quantifier? Looking carefully at the definition of none-below we see that it is saying that for all natural numbers r < y, (M r) does not hold. Well, how would we want to use this fact when we want to prove our final theorem? We expect that we will instantiate min-M-suff with the object (least-M bound) where we know (via the outermost existential quantifier) that M holds for bound, and we will then want to show that none-below holds for (least-M bound). So let us prove that for any natural number (call it bound), none-below holds for (least-M bound). For the final theorem we only need it for natural numbers satisfying M, but note that from the lemma least-is-minimal-satisfying-m we really do not need that bound satisfies M. So we are now proving: (implies (natp bound) (none-below (least-M bound))). Well since this is a standard case of proving a universally quantified predicate, we just apply RT3. We have proved that for all naturals i < (least-M bound), i does not satisfy M (lemma least-is-minimal-satisfying-M), so we merely need the instantiation of that lemma with none-below-witness of the thing we are trying to prove, that is, (least-M bound). The theorem below thus goes through. (defthm least-is-minimal (implies (natp bound) (none-below (least-M bound))) :hints (("Goal" :use ((:instance (:definition none-below) (y (least-M bound))) (:instance least-is-minimal-satisfying-m (i (none-below-witness (least-M bound)))))))) Finally we are in the outermost existential quantifier, and are in the process of applying min-M-suff. What object should we instantiate it with? We must instantiate it with (least-M bound) where bound is an object which must satisfy M and is a natural. We have such an object, namely (some-nat-M-witness) which we know have all these qualities given the hypothesis. So the proof now is just RT1 and RT2. (defthm |minimal exists| (implies (some-nat-M) (min-M)) :hints (("Goal" :use ((:instance min-M-suff (y (least-M (some-nat-M-witness)))) (:instance (:definition some-nat-M)))))) If you are comfortable with the reasoning above, then you are comfortable with quantifiers and probably will not need these notes any more. In my opinion, the best way of dealing with ACL2 is to ask yourself why you think something is a theorem, and the rules of thumb above are simply guides to the questions that you need to ask when you are dealing with quantification. Here are a couple of simple exercises for you to test if you understand the reasoning process. *Exercise 1*. Formalize and prove the following theorem. Suppose there exists x such that (R x) and suppose that all x satisfy (P x). Then prove that there exists x such that (P x) & (R x). (See `http://www.cs.utexas.edu/users/moore/acl2/contrib/quantifier-exercise-1-solution.html' for a solution.) *Exercise 2*. Recall the example just before the preceding exercise, where we showed that if there exists a natural number x satisfying M then there is another natural number y such that y satisfies M and for every natural number z < y, z does not. What would happen if we remove the restriction of x, y, and z being naturals? Of course, we will not talk about < any more, but suppose you use the total order on all ACL2 objects (from community book "books/misc/total-order"). More concretely, consider the definition of some-M above. Let us now define two other functions: (include-book "misc/total-order" :dir :system) (defun-sk none-below-2 (y) (forall r (implies (<< r y) (not (M r))))) (defun-sk min-M2 () (exists y (and (M y) (none-below-2 y)))) The question is whether (implies (some-M) (min-M2)) is a theorem. Can you prove it? Can you disprove it?  File: acl2-doc-emacs.info, Node: QUANTIFIERS, Prev: QUANTIFIER-TUTORIAL, Up: DEFUN-SK QUANTIFIERS issues about quantification in ACL2 ACL2 supports first-order quantifiers exists and forall by way of the defun-sk event. However, proof support for quantification is quite limited. Therefore, you may prefer using recursion in place of defun-sk when possible (following common ACL2 practice). * Menu: * QUANTIFIERS-USING-DEFUN-SK:: quantification example * QUANTIFIERS-USING-DEFUN-SK-EXTENDED:: quantification example with details * QUANTIFIERS-USING-RECURSION:: recursion for implementing quantification For example, the notion "every member of x has property p" can be defined either with recursion or explicit quantification, but proofs may be simpler when recursion is used. We illustrate this point with two proofs of the same informal claim, one of which uses recursion which the other uses explicit quantification. Notice that with recursion, the proof goes through fully automatically; but this is far from true with explicit quantification (especially notable is the ugly hint). The informal claim for our examples is: If every member a of each of two lists satisfies the predicate (p a), then this holds of their append; and, conversely. See *note QUANTIFIERS-USING-RECURSION:: for a solution to this example using recursion. See *note QUANTIFIERS-USING-DEFUN-SK:: for a solution to this example using defun-sk. Also See *note QUANTIFIERS-USING-DEFUN-SK-EXTENDED:: for an elaboration on that solution. But perhaps first, see *note DEFUN-SK:: for an ACL2 utility to introduce first-order quantification in a direct way. Examples of the use of defun-sk are also available: see *note DEFUN-SK-EXAMPLE:: and see *note TUTORIAL4-DEFUN-SK-EXAMPLE:: for basic examples, and see *note QUANTIFIER-TUTORIAL:: for a more complete, careful beginner's introduction that takes you through typical kinds of quantifier-based reasoning in ACL2.  File: acl2-doc-emacs.info, Node: QUANTIFIERS-USING-DEFUN-SK, Next: QUANTIFIERS-USING-DEFUN-SK-EXTENDED, Prev: QUANTIFIERS, Up: QUANTIFIERS QUANTIFIERS-USING-DEFUN-SK quantification example See *note QUANTIFIERS:: for the context of this example. It should be compared to a corresponding example in which a simpler proof is attained by using recursion in place of explicit quantification; see *note QUANTIFIERS-USING-RECURSION::. (in-package "ACL2") ; We prove that if every member A of each of two lists satisfies the ; predicate (P A), then this holds of their append; and, conversely. ; Here is a solution using explicit quantification. (defstub p (x) t) (defun-sk forall-p (x) (forall a (implies (member a x) (p a)))) (defthm member-append (iff (member a (append x1 x2)) (or (member a x1) (member a x2)))) (defthm forall-p-append (equal (forall-p (append x1 x2)) (and (forall-p x1) (forall-p x2))) :hints (("Goal" ; ``should'' disable forall-p-necc, but no need :use ((:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x1))) (:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x2))) (:instance forall-p-necc (x x1) (a (forall-p-witness (append x1 x2)))) (:instance forall-p-necc (x x2) (a (forall-p-witness (append x1 x2)))))))) Also see *note QUANTIFIERS-USING-DEFUN-SK-EXTENDED:: for an elaboration on this example.  File: acl2-doc-emacs.info, Node: QUANTIFIERS-USING-DEFUN-SK-EXTENDED, Next: QUANTIFIERS-USING-RECURSION, Prev: QUANTIFIERS-USING-DEFUN-SK, Up: QUANTIFIERS QUANTIFIERS-USING-DEFUN-SK-EXTENDED quantification example with details See *note QUANTIFIERS-USING-DEFUN-SK:: for the context of this example. (in-package "ACL2") ; We prove that if every member A of each of two lists satisfies the ; predicate (P A), then this holds of their append; and, conversely. ; Here is a solution using explicit quantification. (defstub p (x) t) (defun-sk forall-p (x) (forall a (implies (member a x) (p a)))) ; The defun-sk above introduces the following axioms. The idea is that ; (FORALL-P-WITNESS X) picks a counterexample to (forall-p x) if there is one. ; (DEFUN FORALL-P (X) ; (LET ((A (FORALL-P-WITNESS X))) ; (IMPLIES (MEMBER A X) (P A)))) ; ; (DEFTHM FORALL-P-NECC ; (IMPLIES (NOT (IMPLIES (MEMBER A X) (P A))) ; (NOT (FORALL-P X))) ; :HINTS (("Goal" :USE FORALL-P-WITNESS))) ; The following lemma seems critical. (defthm member-append (iff (member a (append x1 x2)) (or (member a x1) (member a x2)))) ; The proof of forall-p-append seems to go out to lunch, so we break into ; directions as shown below. (defthm forall-p-append-forward (implies (forall-p (append x1 x2)) (and (forall-p x1) (forall-p x2))) :hints (("Goal" ; ``should'' disable forall-p-necc, but no need :use ((:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x1))) (:instance forall-p-necc (x (append x1 x2)) (a (forall-p-witness x2))))))) (defthm forall-p-append-reverse (implies (and (forall-p x1) (forall-p x2)) (forall-p (append x1 x2))) :hints (("Goal" :use ((:instance forall-p-necc (x x1) (a (forall-p-witness (append x1 x2)))) (:instance forall-p-necc (x x2) (a (forall-p-witness (append x1 x2)))))))) (defthm forall-p-append (equal (forall-p (append x1 x2)) (and (forall-p x1) (forall-p x2))) :hints (("Goal" :use (forall-p-append-forward forall-p-append-reverse))))  File: acl2-doc-emacs.info, Node: QUANTIFIERS-USING-RECURSION, Prev: QUANTIFIERS-USING-DEFUN-SK-EXTENDED, Up: QUANTIFIERS QUANTIFIERS-USING-RECURSION recursion for implementing quantification The following example illustrates the use of recursion as a means of avoiding proof difficulties that can arise from the use of explicit quantification (via defun-sk). See *note QUANTIFIERS:: for more about the context of this example. (in-package "ACL2") ; We prove that if every member A of each of two lists satisfies the ; predicate (P A), then this holds of their append; and, conversely. ; Here is a solution using recursively-defined functions. (defstub p (x) t) (defun all-p (x) (if (atom x) t (and (p (car x)) (all-p (cdr x))))) (defthm all-p-append (equal (all-p (append x1 x2)) (and (all-p x1) (all-p x2))))  File: acl2-doc-emacs.info, Node: DEFUND, Next: DEFUND-INLINE, Prev: DEFUN-SK, Up: EVENTS DEFUND define a function symbol and then disable it Use defund instead of defun when you want to disable a function immediately after its definition in :logic mode. This macro has been provided for users who prefer working in a mode where functions are only enabled when explicitly directed by :in-theory. Specifically, the form (defund NAME FORMALS ...) expands to: (progn (defun NAME FORMALS ...) (with-output :off summary (in-theory (disable NAME))) (value-triple '(:defund NAME))). Only the :definition rule (and, for recursively defined functions, the :induction rule) for the function are disabled. In particular, defund does not disable either the :type-prescription or the :executable-counterpart rule. Also, the summary for the in-theory event is suppressed. If the function is defined in :program mode, either because the default-defun-mode is :program or because :mode :program has been specified in an xargs form of a declare form, then no in-theory event is executed. (More precisely, in-theory events are ignored when the default-defun-mode is :program, and if :mode :program is specified then defund does not generate an in-theory event.) Note that defund commands are never redundant (see *note REDUNDANT-EVENTS::) when the default-defun-mode is :logic, because the in-theory event will always be executed. See *note DEFUN:: for documentation of defun.  File: acl2-doc-emacs.info, Node: DEFUND-INLINE, Next: DEFUND-NOTINLINE, Prev: DEFUND, Up: EVENTS DEFUND-INLINE define a potentially disabled, inlined function symbol and associated macro Defund-inline is a variant of defun-inline, the difference being that defund-inline disables the newly-defined function symbol. See *note DEFUN-INLINE::.  File: acl2-doc-emacs.info, Node: DEFUND-NOTINLINE, Next: ENCAPSULATE, Prev: DEFUND-INLINE, Up: EVENTS DEFUND-NOTINLINE define a disabled, not-to-be-inlined function symbol and associated macro Defund-notinline is a variant of defun-notinline, the difference being that defund-notinline disables the newly-defined function symbol. See *note DEFUN-NOTINLINE::.  File: acl2-doc-emacs.info, Node: ENCAPSULATE, Next: EVISC-TABLE, Prev: DEFUND-NOTINLINE, Up: EVENTS ENCAPSULATE hide some events and/or constrain some functions Encapsulate provides a way to execute a sequence of events and then hide some of the resulting effects. There are two kinds of encapsulations: "trivial" and "non-trivial". We discuss these briefly before providing detailed documentation. A trivial encapsulation is an event of the following form. (encapsulate () ; nil here indicates "trivial" ... ) We use the term "sub-events" to refer to through . Each sub-event may be "local", that is, of the form (local ); the other sub-events are called "non-local". When this encapsulate form is submitted to ACL2, it is processed in two passes. On the first pass, each sub-event is processed in sequence; admission of the encapsulate fails if any fails to be admitted. Then a second pass is made after rolling back the logical world to what it was just before executing the encapsulate form. In the second pass, only the non-local forms are evaluated, again in order, and proofs are skipped. For example, the following trivial encapsulation exports a single event, member-equal-reverse. The lemma member-revappend is used (as a rewrite rule) to prove member-equal-reverse on the first pass, but since member-revappend is local, it is ignored on the second (final) pass. (encapsulate () (local (defthm member-revappend (iff (member-equal a (revappend x y)) (or (member-equal a x) (member-equal a y))) :hints (("Goal" :induct (revappend x y))))) (defthm member-equal-reverse (iff (member-equal a (reverse x)) (member-equal a x)))) Of course, one might prefer to prove these events at the top level, rather than within an encapsulation; but the point here is to illustrate that you can have local events that do not become part of the logical world. (Such a capability is also provided at the level of books; in particular, see *note INCLUDE-BOOK::.) On the other hand, non-trivial encapsulations provide a way to introduce axioms about new function symbols, without introducing inconsistency and without introducing complete definitions. The following example illustrates how that works. (encapsulate ; The following list has a single signature, introducing a function foo of ; one argument that returns one value. (The list is non-empty, so we call ; this a "non-trivial" encapsulation.) ( ((foo *) => *) ) ; Introduce a ``witness'' (example) for foo, marked as local so that ; it is not exported: (local (defun foo (x) x)) ; Introduce a non-local property to be exported: (defthm foo-preserves-consp (implies (consp x) (consp (foo x)))) ) The form above introduces a new function symbol, foo, with the indicated property and no definition. In fact, the output from ACL2 concludes as follows. The following constraint is associated with the function FOO: (IMPLIES (CONSP X) (CONSP (FOO X))) To understand this example, we consider how non-trivial encapsulations are processed. The same two passes are made as for trivial encapsulations, and the (local) definition of foo is ignored on the second pass, and hence does not appear in the resulting ACL2 logical world. But before the second pass, each signature is stored in the world. Thus, when the theorem foo-preserves-consp is encountered in the second pass, foo is a known function symbol with the indicated signature. We turn now to more complete documentation. But discussion of redundancy for encapsulate events may be found elsewhere; see *note REDUNDANT-ENCAPSULATE::. Other Examples: (encapsulate (((an-element *) => *)) ; The list of signatures above could also be written ; ((an-element (lst) t)) (local (defun an-element (lst) (if (consp lst) (car lst) nil))) (local (defthm member-equal-car (implies (and lst (true-listp lst)) (member-equal (car lst) lst)))) (defthm thm1 (implies (null lst) (null (an-element lst)))) (defthm thm2 (implies (and (true-listp lst) (not (null lst))) (member-equal (an-element lst) lst)))) (encapsulate () ; empty signature: no constrained functions indicated (local (defthm hack (implies (and (syntaxp (quotep x)) (syntaxp (quotep y))) (equal (+ x y z) (+ (+ x y) z))))) (defthm nthcdr-add1-conditional (implies (not (zp (1+ n))) (equal (nthcdr (1+ n) x) (nthcdr n (cdr x)))))) * Menu: * REDUNDANT-ENCAPSULATE:: redundancy of encapsulate events General Form: (encapsulate (signature ... signature) ev1 ... evn) where each signature is a well-formed signature, each signature describes a different function symbol, and each evi is an embedded event form (See *note EMBEDDED-EVENT-FORM::). Also see *note SIGNATURE::, in particular for a discussion of how a signature can assign a guard to a function symbol. There must be at least one evi. The evi inside local special forms are called "local" events below. Events that are not local are sometimes said to be "exported" by the encapsulation. We make the further restriction that no defaxiom event may be introduced in the scope of an encapsulate (not even by encapsulate or include-book events that are among the evi). Furthermore, no non-local include-book event is permitted in the scope of any encapsulate with a non-empty list of signatures. To be well-formed, an encapsulate event must have the properties that each event in the body (including the local ones) can be successfully executed in sequence and that in the resulting theory, each function mentioned among the signatures was introduced via a local event and has the signature listed. (A utility is provided to assist in debugging failures of such execution; see *note REDO-FLAT::.) In addition, the body may contain no "local incompatibilities" which, roughly stated, means that the events that are not local must not syntactically require symbols defined by local events, except for the functions listed in the signatures. See *note LOCAL-INCOMPATIBILITY::. Finally, no non-local recursive definition in the body may involve in its suggested induction scheme any function symbol listed among the signatures. See *note SUBVERSIVE-RECURSIONS::. Observe that if the signatures list is empty, the resulting "trivial" encapsulate may still be useful for deriving theorems to be exported whose proofs require lemmas you prefer to hide (i.e., made local). Whether trivial or not (i.e., whether the signature is empty or not), encapsulate exports the results of evaluating its non-local events, but its local events are ignored for the resulting logical world. The result of a non-trivial encapsulate event is an extension of the logic in which, roughly speaking, the functions listed in the signatures are constrained to have the signatures listed and to satisfy the non-local theorems proved about them. In fact, other functions introduced in the encapsulate event may be considered to have "constraints" as well. (See *note CONSTRAINT:: for details, which are only relevant to functional instantiation.) Since the constraints were all theorems in the "ephemeral" or "local" theory, we are assured that the extension produced by encapsulate is sound. In essence, the local definitions of the constrained functions are just "witness functions" that establish the consistency of the constraints. Because those definitions are local, they are not present in the theory produced by encapsulation. After a non-trivial encapsulate event is admitted, theorems about the constrained function symbols may then be proved -- theorems whose proofs necessarily employ only the constraints. Thus, those theorems may be later functionally instantiated, as with the :functional-instance lemma instance (see *note LEMMA-INSTANCE::), to derive analogous theorems about different functions, provided the constraints (see *note CONSTRAINT::) can be proved about the new functions. The default-defun-mode for the first event in an encapsulation is the default defun-mode "outside" the encapsulation. But since events changing the defun-mode are permitted within the body of an encapsulate, the default defun-mode may be changed. However, defun-mode changes occurring within the body of the encapsulate are not exported. In particular, the acl2-defaults-table after an encapsulate is always the same as it was before the encapsulate, even though the encapsulate body might contain defun-mode changing events, :program and :logic. See *note DEFUN-MODE::. More generally, after execution of an encapsulate event, the value of acl2-defaults-table is restored to what it was immediately before that event was executed. See *note ACL2-DEFAULTS-TABLE::. We make some remarks on guards and evaluation. Calls of functions introduced in the signatures list cannot be evaluated in the ACL2 read-eval-print loop. See *note DEFATTACH:: for a way to overcome this limitation. Moreover, any :guard supplied in the signature is automatically associated in the world with its corresponding function symbol, with no requirement other than that the guard is a legal term all of whose function symbols are in :logic mode with their guards verified. In particular, there need not be any relationship between a guard in a signature and the guard in a local witness function. Finally, note that for functions introduced non-locally inside a non-trivial encapsulate event, guard verification is illegal unless ACL2 determines that the proof obligations hold outside the encapsulate event as well. (encapsulate ((f (x) t)) (local (defun f (x) (declare (xargs :guard t)) (consp x))) ;; ERROR! (defun g (x) (declare (xargs :guard (f x))) (car x))) The order of the events in the vicinity of an encapsulate is confusing. We discuss it in some detail here because when logical names are being used with theory functions to compute sets of rules, it is sometimes important to know the order in which events were executed. (See *note LOGICAL-NAME:: and see *note THEORY-FUNCTIONS::.) What, for example, is the set of function names extant in the middle of an encapsulation? If the most recent event is previous and then you execute an encapsulate constraining an-element with two non-local events in its body, thm1 and thm2, then the order of the events after the encapsulation is (reading chronologically forward): previous, thm1, thm2, an-element (the encapsulate itself). Actually, between previous and thm1 certain extensions were made to the world by the superior encapsulate, to permit an-element to be used as a function symbol in thm1. Remark for ACL2(r) (see *note REAL::). For ACL2(r), encapsulate can be used to introduce classical and non-classical functions, as determined by the signatures; see *note SIGNATURE::. Those marked as classical (respectively non-classical) must have classical (respectively, non-classical) local witness functions. A related requirement applies to functional instantiation; see *note LEMMA-INSTANCE::.  File: acl2-doc-emacs.info, Node: REDUNDANT-ENCAPSULATE, Prev: ENCAPSULATE, Up: ENCAPSULATE REDUNDANT-ENCAPSULATE redundancy of encapsulate events For this documentation topic we assume familiarity with encapsulate events and the notion of redundancy for events; see *note ENCAPSULATE:: and see *note REDUNDANT-EVENTS::. The typical way for an encapsulate event to be redundant is when a syntactically identical encapsulate has already been executed under the same default-defun-mode, default-ruler-extenders, and default-verify-guards-eagerness. But more generally, the encapsulate events need not be syntactically identical; for example, it suffices that they agree when the contents of local sub-events are ignored. The precise criterion for redundancy is given below, but let us first look at a consequence of the point just made about ignoring the contents of local sub-events. Consider the following sequence of two events. (encapsulate () (defun f (x) x) (local (defthm f-identity (equal (f x) x)))) (encapsulate () (defun f (x) x) (local (defthm false-claim (equal (f x) (not x))))) You might be surprised to learn that the second is actually redundant, even though false-claim is clearly not a theorem; indeed, its negation is a theorem! The following two points may soften the blow. First, this behavior is as specified above (and is specified more precisely below): the contents of local events are ignored when checking redundancy of encapsulate events. Second, this behavior is sound, because the logical meaning of an encapsulate event is taken from the events that it exports, which do not include events that are local to the encapsulate event. Some users, however, want to use encapsulate events for testing in a way that is thwarted by this ignoring of local sub-events. Consider the following sort of example. (encapsulate () (local (defthm test1 ...))) (encapsulate () (local (defthm test2 ...))) Since the contents of local events are ignored when checking redundancy of an encapsulate event, the second form just above is indeed redundant, presumably not as expected by whomever wrote these two tests. A solution is to add distinct non-local forms, for example as follows. (encapsulate () (value-triple "test1") (local (defthm test1 ...))) (encapsulate () (value-triple "test2") (local (defthm test2 ...))) A different solution is to use make-event for testing, as follows. (make-event (er-progn (defthm test1 ...) (value '(value-triple nil)))) (make-event (er-progn (defthm test2 ...) (value '(value-triple nil)))) Also see community books misc/eval.lisp, make-event/eval-check.lisp, and make-event/eval-tests.lisp for more ways to test in books. The precise criterion for redundancy of encapsulate events is that the existing and proposed encapsulate events contain the same signatures and the same number of top-level events -- let k be that number -- and for each i < k, the ith top-level events E1 and E2 from the earlier and current encapsulates have one of the following properties. o E1 and E2 are equal; or o E1 is of the form (record-expansion E2 ...); or else o E1 and E2 are equal after replacing each local sub-event by (local (value-triple :elided)), where a sub-event of an event E is either E itself, or a sub-event of a constituent event of E in the case that E is a call of with-output, with-prover-time-limit, with-prover-step-limit, record-expansion, time$, progn, progn!, or encapsulate itself.  File: acl2-doc-emacs.info, Node: EVISC-TABLE, Next: IN-ARITHMETIC-THEORY, Prev: ENCAPSULATE, Up: EVENTS EVISC-TABLE support for abbreviated output The evisc-table is an ACL2 table (see *note TABLE::) whose purpose is to modify the print representations of specified non-nil objects. When a key (some object) is associated with a string value, then that string is printed instead of that key (as an abbreviation). For example, the following log shows how to abbreviate the key (a b c) with the token . ACL2 !>(table evisc-table '(a b c) "") Summary Form: ( TABLE EVISC-TABLE ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) EVISC-TABLE ACL2 !>'(a b c) ACL2 !>'(4 5 a b c) (4 5 . ) ACL2 !> Every value in this table must be either a string or nil, where nil eliminates any association of the key with an abbreviation. Continuing with the log above: ACL2 !>(table evisc-table '(a b c) nil) Summary Form: ( TABLE EVISC-TABLE ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) EVISC-TABLE ACL2 !>'(a b c) (A B C) ACL2 !>'(4 5 a b c) (4 5 A B C) ACL2 !> It can be particularly helpful to use this table to abbreviate a constant introduced by defconst by prefixing the constant name with "#,", as we now describe. Consider first the following example. (defconst *abc* '(1 2 3 4 5 6 7 8)) (table evisc-table *abc* (concatenate 'string "#," (symbol-name '*abc*))) Then the constant *abc* is printed as follows -- very helpful if its associated structure is significantly larger than the 8-element list of numbers shown above! ACL2 !>*abc* #,*ABC* ACL2 !> What's more, the ACL2 reader will replace #,*C*, where *C* is defined by defconst, by its value, regardless of evisc-table; see *note SHARP-DOT-READER::. Continuing with the example above, we have: ACL2 !>(cdr (quote #,*ABC*)) (2 3 4 5 6 7 8) ACL2 !> Of course, more care needs to be taken if packages are involved (see *note DEFPKG::), as we now illustrate. ACL2 !>(defpkg "FOO" nil) Summary Form: ( DEFPKG "FOO" ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) "FOO" ACL2 !>(defconst foo::*a* '(1 2 3)) Summary Form: ( DEFCONST FOO::*A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO::*A* ACL2 !>(table evisc-table foo::*a* "#,foo::*a*") Summary Form: ( TABLE EVISC-TABLE ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) EVISC-TABLE ACL2 !>foo::*a* #,foo::*a* ACL2 !>'#,foo::*a* #,foo::*a* ACL2 !>(cdr '#,foo::*a*) (2 3) ACL2 !> We conclude by an example showing some extra care that may be important to consider taking. We start with: (defconst |*BaR*| '(3 4)) Then the following works just fine; but try it without the extra code for the may-need-slashes case and you'll see that the sharp-dot printing is missing. First: (table evisc-table |*BaR*| (let ((x (symbol-name '|*BaR*|))) (if (may-need-slashes x) (concatenate 'string "#.|" x "|") (concatenate 'string "#." x)))) Then: ACL2 !>|*BaR*| #,|*BaR*| ACL2 !>  File: acl2-doc-emacs.info, Node: IN-ARITHMETIC-THEORY, Next: IN-THEORY, Prev: EVISC-TABLE, Up: EVENTS IN-ARITHMETIC-THEORY designate theory for some rewriting done for non-linear arithmetic We assume familiarity with theories; in particular, see *note IN-THEORY:: for the normal way to set the current theory. Here, we discuss an analogous event that pertains only to non-linear arithmetic (see *note NON-LINEAR-ARITHMETIC::). Example: (in-arithmetic-theory '(lemma1 lemma2)) General Form: (in-arithmetic-theory term :doc doc-string) where term is a term that when evaluated will produce a theory (see *note THEORIES::), and doc-string is an optional documentation string not beginning with ":Doc-Section ...". Except for the variable world, term must contain no free variables. Term is evaluated with the variable world bound to the current world to obtain a theory and the corresponding runic theory (see *note THEORIES::) is then used by non-linear arithmetic (see *note NON-LINEAR-ARITHMETIC::). Warning: If term involves macros such as ENABLE and DISABLE you will probably not get what you expect! Those macros are defined relative to the CURRENT-THEORY. But in this context you might wish they were defined in terms of the "CURRENT-ARITHMETIC-THEORY" which is not actually a defined function. We do not anticipate that users will repeatedly modify the arithmetic theory. We expect term most often to be a constant list of runes and so have not provided "arithmetic theory manipulation functions" analogous to CURRENT-THEORY and ENABLE. Because no unique name is associated with an in-arithmetic-theory event, there is no way we can store the documentation string doc-string in our il[documentation] database. Hence, we actually prohibit doc-string from having the form of an ACL2 documentation string; see *note DOC-STRING::. See *note NON-LINEAR-ARITHMETIC::.  File: acl2-doc-emacs.info, Node: IN-THEORY, Next: INCLUDE-BOOK, Prev: IN-ARITHMETIC-THEORY, Up: EVENTS IN-THEORY designate "current" theory (enabling its rules) Example: (in-theory (set-difference-theories (universal-theory :here) '(flatten (:executable-counterpart flatten)))) General Form: (in-theory term :doc doc-string) where term is a term that when evaluated will produce a theory (see *note THEORIES::), and doc-string is an optional documentation string not beginning with ":Doc-Section ...". Because no unique name is associated with an in-theory event, there is no way we can store the documentation string doc-string in our documentation database. Hence, we actually prohibit doc-string from having the form of an ACL2 documentation string; see *note DOC-STRING::. Except for the variable world, term must contain no free variables. Term is evaluated with the variable world bound to the current world to obtain a theory and the corresponding runic theory (see *note THEORIES::) is then made the current theory. Thus, immediately after the in-theory, a rule is enabled iff its rule name is a member of the runic interpretation (see *note THEORIES::) of some member of the value of term. See *note THEORY-FUNCTIONS:: for a list of the commonly used theory manipulation functions. Note that it is often useful to surround in-theory events with local, that is, to use (local (in-theory ...)). This use of local in encapsulate events and books will prevent the effect of this theory change from being exported outside the context of that encapsulate or book. Also see *note HINTS:: for a discussion of the :in-theory hint, including some explanation of the important point that an :in-theory hint will always be evaluated relative to the current ACL2 logical world, not relative to the theory of a previous goal. In-theory returns an error triple (see *note ERROR-TRIPLES::). When the return is without error, the value is of the form (mv nil (:NUMBER-OF-ENABLED-RUNES k) state) where k is the length of the new current theory. This value of k is what is printed when an in-theory event evaluates without error at the top level.  File: acl2-doc-emacs.info, Node: INCLUDE-BOOK, Next: LOCAL, Prev: IN-THEORY, Up: EVENTS INCLUDE-BOOK load the events in a file Examples: (include-book "my-arith") (include-book "/home/smith/my-arith") (include-book "/../../my-arith") General Form: (include-book file :load-compiled-file action :uncertified-okp t/nil ; [default t] :defaxioms-okp t/nil ; [default t] :skip-proofs-okp t/nil ; [default t] :ttags ttags ; [default nil] :dir directory :doc doc-string) where file is a book name. See *note BOOKS:: for general information, see *note BOOK-NAME:: for information about book names, and see *note PATHNAME:: for information about file names. Action is one of t, nil, :default, :warn, or :comp; these values are explained below, and the default is :default. The three -okp keyword arguments, which default to t, determine whether errors or warnings are generated under certain conditions explained below; when the argument is t, warnings are generated. The dir argument, if supplied, is a keyword that represents an absolute pathname for a directory (see *note PATHNAME::), to be used instead of the current book directory (see *note CBD::) for resolving the given file argument to an absolute pathname. In particular, by default :dir :system resolves file using the books/ directory of your ACL2 installation, unless your ACL2 executable was built somewhere other than where it currently resides; please see the "Books Directory" below. To define other keywords that can be used for dir, see *note ADD-INCLUDE-BOOK-DIR::. Doc-string is an optional documentation string; see *note DOC-STRING::. If the book has no certificate, if its certificate is invalid or if the certificate was produced by a different version of ACL2, a warning is printed and the book is included anyway; see *note CERTIFICATE::. This can lead to serious errors, perhaps mitigated by the presence of a .port file from an earlier certification; see *note UNCERTIFIED-BOOKS::. If the portcullis of the certificate (see *note PORTCULLIS::) cannot be raised in the host logical world, an error is caused and no change occurs to the logic. Otherwise, the non-local events in file are assumed. Then the keep of the certificate is checked to ensure that the correct files were read; see *note KEEP::. A warning is printed if uncertified books were included. Even if no warning is printed, include-book places a burden on you; see *note CERTIFICATE::. If you use guards, please note include-book is executed as though (set-guard-checking nil) has been evaluated; see *note SET-GUARD-CHECKING::. If you want guards checked, please see *note LD:: and/or see *note REBUILD::. The value of :load-compiled-file controls whether a compiled file for the given file is loaded by include-book. Note that this keyword applies only to the given file, not to any included sub-books. In order to skip loading all compiled files, for the given file as well as all included sub-books -- for example, to avoid Lisp errors such as "Wrong FASL version" -- use (set-compiler-enabled nil state); see *note COMPILATION::. Otherwise, if keyword argument :load-compiled-file is missing or its value is the keyword :default, then it is treated as :warn. If its value is nil, no attempt is made to load the compiled file for the book provided. In order to load the compiled file, it must be more recent than the book's certificate (except in raw mode, where it must be more recent than the book itself; see *note SET-RAW-MODE::). For non-nil values of :load-compiled-file that do not result in a loaded compiled file, ACL2 proceeds as follows. Note that a load of a compiled file or expansion file aborts partway through whenever an include-book form is encountered for which no suitable compiled or expansion file can be loaded. For much more detail, see *note BOOK-COMPILED-FILE::. t: Cause an error if the compiled file is not loaded. This same requirement is imposed on every include-book form evaluated during the course of evaluation of the present include-book form, except that for those subsidiary include-books that do not themselves specify :load-compiled-file t, it suffices to load the expansion file (see *note BOOK-COMPILED-FILE::). :warn: An attempt is made to load the compiled file, and a warning is printed if that load fails to run to completion. :comp: A compiled file is loaded as with value t, except that if a suitable "expansion file" exists but the compiled file does not, then the compiled file is first created. See *note BOOK-COMPILED-FILE::. The three -okp arguments, :uncertified-okp, defaxioms-okp, and skip-proofs-okp, determine the system's behavior when the book or any subbook is found to be uncertified, when the book or any subbook is found to contain defaxiom events, and when the book or any subbook is found to contain skip-proofs events, respectively. All three default to t, which means it is "ok" for the condition to arise. In this case, a warning is printed but the processing to load the book is allowed to proceed. When one of these arguments is nil and the corresponding condition arises, an error is signaled and processing is aborted. *Exception*: :uncertified-okp is ignored if the include-book is being performed on behalf of a certify-book. The keyword argument :ttags may normally be omitted. A few constructs, used for example if you are building your own system based on ACL2, may require it. See *note DEFTTAG:: for an explanation of this argument. Include-book is similar in spirit to encapsulate in that it is a single event that "contains" other events, in this case the events listed in the file named. Include-book processes the non-local event forms in the file, assuming that each is admissible. Local events in the file are ignored. You may use include-book to load several books, creating the logical world that contains the definitions and theorems of all of them. If any non-local event of the book attempts to define a name that has already been defined -- and the book's definition is not syntactically identical to the existing definition -- the attempt to include the book fails, an error message is printed, and no change to the logical world occurs. See *note REDUNDANT-EVENTS:: for the details. When a book is included, the default defun-mode (see *note DEFAULT-DEFUN-MODE::) for the first event is always :logic. That is, the default defun-mode "outside" the book -- in the environment in which include-book was called -- is irrelevant to the book. Events that change the defun-mode are permitted within a book (provided they are not in local forms). However, such changes within a book are not exported, i.e., at the conclusion of an include-book, the "outside" default defun-mode is always the same as it was before the include-book. Unlike every other event in ACL2, include-book puts a burden on you. Used improperly, include-book can be unsound in the sense that it can create an inconsistent extension of a consistent logical world. A certification mechanism is available to help you carry this burden -- but it must be understood up front that even certification is no guarantee against inconsistency here. The fundamental problem is one of file system security. See *note CERTIFICATE:: for a discussion of the security issues. At the beginning of execution of an include-book form, even before executing portcullis commands, the value of acl2-defaults-table is restored to the value it had at startup. After execution of an include-book form, the value of acl2-defaults-table is restored to what it was immediately before that include-book form was executed. See *note ACL2-DEFAULTS-TABLE::. Books Directory. We refer to the "books directory" of an executable image as the full pathname string of the directory associated with include-book keyword option :dir :system for that image. By default, it is the books/ subdirectory of the directory where the sources reside and the executable image is thus built (except for ACL2(r) -- see *note REAL:: --, where it is books/nonstd/). If those books reside elsewhere, the environment variable ACL2_SYSTEM_BOOKS can be set to the books/ directory under which they reside (a Unix-style pathname, typically ending in books/ or books, is permissible). In most cases, your ACL2 executable is a small script in which you can set this environment variable just above the line on which the actual ACL2 image is invoked, for example: export ACL2_SYSTEM_BOOKS ACL2_SYSTEM_BOOKS=/home/acl2/4-0/acl2-sources/books If you follow suggestions in the installation instructions, these books will be the ACL2 community books; see *note COMMUNITY-BOOKS::. This concludes the guided tour through books. See *note SET-COMPILE-FNS:: for a subtle point about the interaction between include-book and on-the-fly compilation. See *note CERTIFY-BOOK:: for a discussion of how to certify a book. :cited-by Programming  File: acl2-doc-emacs.info, Node: LOCAL, Next: MAKE-EVENT, Prev: INCLUDE-BOOK, Up: EVENTS LOCAL hiding an event in an encapsulation or book Examples: (local (defthm hack1 (implies (and (acl2-numberp x) (acl2-numberp y) (equal (* x y) 1)) (equal y (/ x))))) (local (defun double-naturals-induction (a b) (cond ((and (integerp a) (integerp b) (< 0 a) (< 0 b)) (double-naturals-induction (1- a) (1- b))) (t (list a b))))) General Form: (local ev) where ev is an event form. If the current default defun-mode (see *note DEFAULT-DEFUN-MODE::) is :logic and ld-skip-proofsp is nil or t, then (local ev) is equivalent to ev. But if the current default defun-mode is :program or if ld-skip-proofsp is 'include-book, then (local ev) is a no-op. Thus, if such forms are in the event list of an encapsulate event or in a book, they are processed when the encapsulation or book is checked for admissibility in :logic mode but are skipped when extending the host world. Such events are thus considered "local" to the verification of the encapsulation or book. The non-local events are the ones "exported" by the encapsulation or book. See *note ENCAPSULATE:: for a thorough discussion. Also see *note LOCAL-INCOMPATIBILITY:: for a discussion of a commonly encountered problem with such event hiding: you can't make an event local if its presence is required to make sense of a non-local one. Note that events that change the default defun-mode, and in fact any events that set the acl2-defaults-table, are disallowed inside the scope of local. See *note EMBEDDED-EVENT-FORM::.  File: acl2-doc-emacs.info, Node: MAKE-EVENT, Next: MEMOIZE, Prev: LOCAL, Up: EVENTS MAKE-EVENT evaluate (expand) a given form and then evaluate the result Make-event is a utility for generating events. It provides a capability not offered by Lisp macros (see *note DEFMACRO::), as it allows access to the ACL2 state and logical world. In essence, the expression (make-event form) replaces itself with the result of evaluating form, say, ev, as though one had submitted ev instead of the make-event call. For example, (make-event (quote (defun f (x) x))) is equivalent to the event (defun f (x) x). We break this documentation into the following sections. *Introduction* *Detailed Documentation* *Error Reporting* *Restriction to Event Contexts* *Examples Illustrating How to Access State* *Advanced Expansion Control* We begin with an informal introduction, which focuses on examples and introduces the key notion of "expansion phase". *Introduction* Make-event is particularly useful for those who program using the ACL2 state; see *note PROGRAMMING-WITH-STATE::. That is because the evaluation of form may read and even modify the ACL2 state. Suppose for example that we want to define a constant *world-length*, that is the length of the current ACL2 world. A make-event form can accomplish this task, as follows. ACL2 !>(length (w state)) 98883 ACL2 !>(make-event (list 'defconst '*world-length* (length (w state)))) Summary Form: ( DEFCONST *WORLD-LENGTH* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (LIST ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) *WORLD-LENGTH* ACL2 !>*world-length* 98883 ACL2 !>(length (w state)) 98890 ACL2 !> How did this work? First, evaluation of the form (list 'defconst '*world-length* (length (w state))) returned the event form (defconst *world-length* 98883). Then that event form was automatically submitted to ACL2. Of course, that changed the ACL2 logical world, which is why the final value of (length (w state)) is greater than its initial value. The example above illustrates how the evaluation of a make-event call takes place in two phases. The first phase evaluates the argument of the call, in this case (list 'defconst '*world-length* (length (w state))), to compute an event form, in this case (defconst *world-length* 98883). We call this evaluation the "expansion" phase. Then the resulting event form is evaluated, which in this case defines the constant *world-length*. Now suppose we would like to introduce such a defconst form any time we like. It is common practice to define macros to automate such tasks. Now we might be tempted simply to make the following definition. ; WRONG! (defmacro define-world-length-constant (name state) (list 'defconst name (length (w state)))) But ACL2 rejects such a definition, because a macro cannot take the ACL2 state as a parameter; instead, the formal parameter to this macro named "STATE" merely represents an ordinary object. You can try to experiment with other such direct methods to define such a macro, but they won't work. Instead, however, you can use the approach illustrated by the make-event example above to define the desired macro, as follows. (defmacro define-world-length-constant (name) `(make-event (list 'defconst ',name (length (w state))))) Here are example uses of this macro. ACL2 !>(define-world-length-constant *foo*) Summary Form: ( DEFCONST *FOO* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (LIST ...)) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) *FOO* ACL2 !>*foo* 98891 ACL2 !>:pe *foo* 2:x(DEFINE-WORLD-LENGTH-CONSTANT *FOO*) > (DEFCONST *FOO* 98891) ACL2 !>(length (w state)) 98897 ACL2 !>(define-world-length-constant *bar*) Summary Form: ( DEFCONST *BAR* ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Summary Form: ( MAKE-EVENT (LIST ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) *BAR* ACL2 !>*bar* 98897 ACL2 !>:pe *bar* 3:x(DEFINE-WORLD-LENGTH-CONSTANT *BAR*) > (DEFCONST *BAR* 98897) ACL2 !>(length (w state)) 98903 ACL2 !> Finally, we note that the expansion phase can be used for computation that has side effects, generally by modifying state. Here is a modification of the above example that does not change the world at all, but instead saves the length of the world in a state global. (make-event (pprogn (f-put-global 'my-world-length (length (w state)) state) (value '(value-triple nil)))) Notice that this time, the value returned by the expansion phase is not an event form, but rather, is an error triple (see *note ERROR-TRIPLES::) whose value component is an event form, namely, the event form (value-triple nil). Evaluation of that event form does not change the ACL2 world (see *note VALUE-TRIPLE::). Thus, the sole purpose of the make-event call above is to change the state by associating the length of the current logical world with the state global named 'my-world-length. After evaluating this form, (@ my-world-length) provides the length of the ACL2 world, as illustrated by the following transcript. ACL2 !>:pbt 0 0:x(EXIT-BOOT-STRAP-MODE) ACL2 !>(length (w state)) 98883 ACL2 !>(make-event (pprogn (f-put-global 'my-world-length (length (w state)) state) (value '(value-triple nil)))) Summary Form: ( MAKE-EVENT (PPROGN ...)) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) NIL ACL2 !>(length (w state)) 98883 ACL2 !>:pbt 0 0:x(EXIT-BOOT-STRAP-MODE) ACL2 !> When make-event is invoked by a book, it is expanded during book certification but not, by default, when the book is included. So for the example (define-world-length-constant *foo*) given above, if that form is in a book, then the value of *foo* will be the length of the world at the time this form was invoked during book certification, regardless of world length at include-book time. (The expansion is recorded in the book's certificate, and re-used.) To overcome this default, you can specified keyword value :CHECK-EXPANSION t. This will cause an error if the expansion is different, but it can be useful for side effects. For example, if you insert the following form in a book, then the length of the world will be printed when the form is encountered, whether during certify-book or during include-book. (make-event (pprogn (fms "Length of current world: ~x0~|" (list (cons #\0 (length (w state)))) *standard-co* state nil) (value '(value-triple nil))) :check-expansion t) * Menu: * MAKE-EVENT-DETAILS:: details on make-event expansion *Detailed Documentation* Examples: ; Trivial example: evaluate (quote (defun foo (x) x)) to obtain ; (defun foo (x) x), which is then evaluated. (make-event (quote (defun foo (x) x))) ; Evaluate (generate-form state) to obtain (mv nil val state), and ; then evaluate val. (Generate-form is not specified here, but ; imagine for example that it explores the state and then generates ; some desired definition or theorem.) (make-event (generate-form state)) ; As above, but make sure that if this form is in a book, then when ; we include the book, the evaluation of (generate-form state) ; should return the same value as it did when the book was ; certified. (make-event (generate-form state) :CHECK-EXPANSION t) ; As above (where the :CHECK-EXPANSION value can be included or ; not), where if there is an error during expansion, then the error ; message will explain that expansion was on behalf of the indicated ; object, typically specified as the first argument. (make-event (generate-form state) :ON-BEHALF-OF (generate-form state)) General Form: (make-event form :CHECK-EXPANSION chk :ON-BEHALF-OF obj :EXPANSION? form) where chk is nil (the default), t, or the intended "expansion result" from the evaluation of form (as explained below); and if supplied, obj is an arbitrary ACL2 object, used only in reporting errors in expansion, i.e., in the evaluation of form. The :EXPANSION? keyword is discussed in the final section, on Advanced Expansion Control. We strongly recommend that you browse some .lisp files in the community books directory books/make-event/. You may even find it helpful, in order to understand make-event, to do so before continuing to read this documentation. You may also find it useful to browse community book books/misc/eval.lisp, which contains definitions of macros must-succeed and must-fail that are useful for testing and are used in many books in the books/make-event/ directory, especially eval-tests.lisp. Another example, books/make-event/defrule.lisp, shows how to use macros whose calls expand to make-event forms, which in turn can generate events. For more examples, see file books/make-event/Readme.lsp. Other than the examples, the explanations here should suffice for most users. If you want explanations of subtler details, see *note MAKE-EVENT-DETAILS::. Note that make-event may only be used at the "top level" or where an event is expected. See the section "Restriction to Event Contexts", below. Make-event is related to Lisp macroexpansion in the sense that its argument is evaluated to obtain an expansion result, which is evaluated again. Let us elaborate on each of these notions in turn: "is evaluated," "expansion result", and "evaluated again." The final section, on Advanced Expansion Control, will generalize these processes in a way that we ignore for now. "is evaluated" -- The argument can be any expression, which is evaluated as would be any expression submitted to ACL2's top level loop. Thus, state and user-defined stobjs may appear in the form supplied to make-event. Henceforth, we will refer to this evaluation as "expansion." Expansion is actually done in a way that restores ACL2's built-in state global variables, including the logical world, to their pre-expansion values (with a few exceptions -- see *note MAKE-EVENT-DETAILS:: -- and where we note that changes to user-defined state global variables (see *note ASSIGN::) are preserved). So, for example, events might be evaluated during expansion, but they will disappear from the logical world after expansion returns its result. Moreover, proofs are enabled by default at the start of expansion (see *note LD-SKIP-PROOFSP::) if keyword :CHECK-EXPANSION is supplied and has a non-nil value. "expansion result" -- The above expansion may result in an ordinary (non-state, non-stobj) value, which we call the "expansion result." Or, expansion may result in a multiple value of the form (mv erp val state), or, more generally, (mv erp val state stobj-1 ... stobj-k) where each stobj-i is a stobj; then the expansion result is val unless erp is not nil, in which case there is no expansion result, and the original make-event evaluates to a soft error. In either case (single or multiple value), either val is an embedded event form (see *note EMBEDDED-EVENT-FORM::), or else the original make-event evaluates to a soft error, printed as described under "Error Reporting" below. "evaluated again" -- the expansion result is evaluated in place of the original make-event. The expansion process can invoke subsidiary calls of make-event, and the expansion result can (perhaps after macroexpansion) be a call of make-event. It can be useful to track all these make-event calls. The state global variable make-event-debug may be set to a non-nil value, for example (assign make-event-debug t), in order to see a trace of the expansion process, where a level is displayed (as in "3>") to indicate the depth of subsidiary expansions. Expansion of a make-event call will yield an event that replaces the original make-event call. In particular, if you put a make-event form in a book, then in essence it is replaced by its expansion result, created during the proof pass of the certify-book process. We now elaborate on this idea of keeping the original expansion. A make-event call generates a "make-event replacement" that may be stored by the system. In the simplest case, this replacement is the expansion result. When a book is certified, these replacements are stored in a book's certificate (technically, in the :EXPANSION-ALIST field). Thus, although the book is not textually altered during certification, one may imagine a "book expansion" corresponding to the original book, in which events are substituted by replacements that were generated during the proof phase of certification. A subsequent include-book will then include the book expansion corresponding to the indicated book. When a book is compiled during certify-book, it is actually the corresponding book expansion, stored as a temporary file, that is compiled instead. That temporary file is deleted after compilation unless one first evaluates the form (assign keep-tmp-files t). Note however that all of the original forms must still be legal events; see *note EMBEDDED-EVENT-FORM::. So for example, if the first event in a book is (local (defmacro my-id (x) x)), and is followed by (my-id (make-event ...)), the final "include-book" pass of certify-book will fail because my-id is not defined when the my-id call is encountered. A make-event replacement might not be the expansion when either of the keyword arguments :CHECK-EXPANSION or :EXPANSION? is supplied. We deal with the latter in the final section, on Advanced Expansion Control. If :CHECK-EXPANSION t is supplied and the expansion is exp, then the replacement is obtained from the original make-event call, by substituting exp for t as the value of keyword :CHECK-EXPANSION. Such a make-event call -- during the second pass of an encapsulate or during event processing on behalf of include-book -- will do the expansion again and check that the expansion result is equal to the original expansion result, exp. In the unusual case that you know the expected expansion result, res, you can specify :CHECK-EXPANSION res in the first place, so that the check is also done during the initial evaluation of the make-event form. IMPORTANT BUT OBSCURE DETAIL: That expansion check is only done when processing events, not during a preliminary load of a book's compiled file. The following paragraph elaborates. (Here are details on the point made just above, for those who use the :CHECK-EXPANSION argument to perform side-effects on the state. When you include a book, ACL2 generally loads a compiled file before processing the events in the book; see *note BOOK-COMPILED-FILE::. While it is true that a non-nil :CHECK-EXPANSION argument causes include-book to perform expansion of the make-event form during event processing it does _not_ perform expansion when the compiled file (or expansion file; again, see *note BOOK-COMPILED-FILE::) is loaded.) ACL2 performs the following space-saving optimization: when the expansion result is a local event, then the make-event replacement is (local (value-triple :ELIDED)). The notion of "expansion" and "replacement" extend to the case that a call of make-event is found in the course of macroexpansion. The following example illustrates this point. (encapsulate () (defmacro my-mac () '(make-event '(defun foo (x) x))) (my-mac)) :pe :here The above call of pe shows that the form (my-mac) has a make-event expansion (and replacement) of (DEFUN FOO (X) X): (ENCAPSULATE NIL (DEFMACRO MY-MAC NIL '(MAKE-EVENT '(DEFUN FOO (X) X))) (RECORD-EXPANSION (MY-MAC) (DEFUN FOO (X) X))) *Error Reporting* Suppose that expansion produces a soft error as described above. That is, suppose that the argument of a make-event call evaluates to a multiple value (mv erp val state ...) where erp is not nil. If erp is a string, then that string is printed in the error message. If erp is a cons pair whose car is a string, then the error prints "~@0" with #\0 bound to that cons pair; see *note FMT::. Any other non-nil value of erp causes a generic error message to be printed. *Restriction to Event Contexts* A make-event call must occur either at the top level, or during make-event expansion, or as an argument of an event constructor. We explain in more detail below. This restriction is imposed to enable ACL2 to track expansions produced by make-event. The following examples illustrate this restriction. ; Legal: (progn (with-output :on summary (make-event '(defun foo (x) x)))) ; Illegal: (mv-let (erp val state) (make-event '(defun foo (x) x)) (mv erp val state)) More precisely: a make-event call that is not itself evaluated during make-event expansion is subject to the following requirement. After macroexpansion has taken place, such a make-event call must be in an "event context", defined recursively as follows. (All but the first two cases below correspond to similar cases for constructing events; see *note EMBEDDED-EVENT-FORM::.) o A form submitted at the top level, or more generally, supplied to a call of ld, is in an event context. o A form occurring at the top level of a book is in an event context. o If (LOCAL x1) is in an event context, then so is x1. o If (SKIP-PROOFS x1) is in an event context, then so is x1. o If (MAKE-EVENT x ...) is in an event context and its expansion x1 is an embedded event form, then x1 is in an event context. o If (WITH-OUTPUT ... x1), (WITH-PROVER-STEP-LIMIT ... x1 ...), or (WITH-PROVER-TIME-LIMIT ... x1) is in an event context, then so is x1. o For any call of PROGN or PROGN!, each of its arguments is in an event context. o For any call of ENCAPSULATE, each of its arguments except the first (the signature list) is in an event context. o If (RECORD-EXPANSION x1 x2) is in an event context, then x1 and x2 are in event contexts. Note: record-expansion is intended for use only by the implementation, which imposes the additional restriction that x1 and its subsidiary make-event calls (if any) must specify a :CHECK-EXPANSION argument that is a consp. Low-level remark, for system implementors. There is the one exception to the above restriction: a single state-global-let* form immediately under a progn! call. For example: (progn! (state-global-let* (make-event ...))) However, the following form may be preferable (see *note PROGN!::): (progn! :STATE-GLOBAL-BINDINGS (make-event ...)) Also see *note REMOVE-UNTOUCHABLE:: for an interesting use of this exception. *Examples Illustrating How to Access State* You can modify the ACL2 state by doing your state-changing computation during the expansion phase, before expansion returns the event that is submitted. Here are some examples. First consider the following. Notice that expansion modifies state global my-global during make-event expansion, and then expansion returns a defun event to be evaluated. (make-event (er-progn (assign my-global (length (w state))) (value '(defun foo (x) (cons x x))))) Then we get: ACL2 !>(@ my-global) 72271 ACL2 !>:pe foo L 1:x(MAKE-EVENT (ER-PROGN # #)) >L (DEFUN FOO (X) (CONS X X)) ACL2 !> Here's a slightly fancier example, where the computation affects the defun. In a new session, execute: (make-event (er-progn (assign my-global (length (w state))) (value `(defun foo (x) (cons x ,(@ my-global)))))) Then: ACL2 !>(@ my-global) 72271 ACL2 !>:pe foo L 1:x(MAKE-EVENT (ER-PROGN # #)) >L (DEFUN FOO (X) (CONS X 72271)) ACL2 !> Note that ACL2 table events may avoid the need to use state globals. For example, instead of the example above, consider this example in a new session. (make-event (let ((world-len (length (w state)))) `(progn (table my-table :STORED-WORLD-LENGTH ,world-len) (defun foo (x) (cons x ,world-len))))) Then: ACL2 !>(table my-table) ((:STORED-WORLD-LENGTH . 72271)) ACL2 !>:pe foo 1:x(MAKE-EVENT (LET # #)) >L (DEFUN FOO (X) (CONS X 72271)) ACL2 !> By the way, most built-in state globals revert after expansion. But your own global (like my-global above) can be set during expansion, and the new value will persist. *Advanced Expansion Control* We conclude this documentation section by discussing three kinds of additional control over make-event expansion. These are all illustrated in community book books/make-event/make-event-keywords-or-exp.lisp. The discussion below is split into the following three parts. (1) The value produced by expansion may have the form (:DO-PROOFS exp), which specifies exp as the expansion result, to be evaluated without skipping proofs even when including a book. (2) The value produced by expansion may have the form (:OR exp-1 ... exp-k), which specifies that the first form exp-i to evaluate without error is the expansion result. (3) The keyword argument :EXPANSION? can serve to eliminate the storing of make-event replacements, as described above for the "book expansion" of a book. We now elaborate on each of these. (1) :DO-PROOFS "call" produced by expansion. We have discussed the expansion result produced by the expansion phase of evaluating a make-event call. However, if the expansion phase produces an expression of the form (:DO-PROOFS exp), then the expansion result is actually exp. The :DO-PROOFS wrapper indicates that even if proofs are currently being skipped (see *note LD-SKIP-PROOFSP::), then evaluation of exp should take place with proofs not skipped. For example, proofs will be performed when evaluating the make-event expansion, namely the indicated defthm event, in the following example. (set-ld-skip-proofsp t state) (make-event '(:DO-PROOFS (defthm app-assoc (equal (append (append x y) z) (append x y z))))) Note that such use of :DO-PROOFS causes proofs to be performed when evaluating the expansion while including an uncertified book. But when including a certified book, then unless :CHECK-EXPANSION is supplied a non-nil value, the make-event replacement will just be the expansion, which does not include the :DO-PROOFS wrapper and hence will be evaluated with proofs skipped. (2) :OR "call" produced by expansion. There may be times where you want to try different expansions. For example, the community book books/make-event/proof-by-arith.lisp attempts to admit a given event, which we'll denote EV, by trying events of the following form as BOOK varies over different community books. (encapsulate () (local (include-book BOOK :DIR :SYSTEM)) EV) A naive implementation of this macro would evaluate all such encapsulate events until one succeeds, and then return that successful event as the expansion. Then that event would need to be evaluated again! With some hacking one could avoid that re-evaluation by using skip-proofs, but that won't work if you are trying to create a certified book without skipped proofs. Instead, the implementation creates an expansion of the form (:OR ev-1 ev-2 ... ev-k), where the list (ev-1 ev-2 ... ev-k) enumerates the generated encapsulate events. In general, for this "disjunctive case" of a result from expansion, each ev-i is evaluated in sequence, and the first that succeeds without error is considered to be the expansion result -- and a repeat evaluation is avoided. If evaluation of each ev-i results in an error, then so does the make-event call. This special use of :OR in a value produced by expansion is only supported at the top level. That is, the result can be (:OR ev-1 ev-2 ... ev-k) but then each ev-i must be a legal expansion result, without such further use of :OR -- except, ev-i may be (:DO-PROOFS ev-i'), where ev-i' then would serve as the expansion rather than ev-i. (3) The :EXPANSION? keyword argument. If keyword argument :EXPANSION? has a nonnil value, then the :CHECK-EXPANSION keyword must be omitted or have value nil or t, hence not a cons pair. The idea of the :EXPANSION? keyword is to give you a way to avoid storing expansion results in a book's certificate. Roughly speaking, when the expansion result matches the value of :EXPANSION?, then no expansion result is stored for the event by book certification; then when the book is later included, the value of :EXPANSION? is used as the expansion, thus bypassing the expansion phase. One could say that the event is its own make-event replacement, but it is more accurate to say that there is no make-event replacement at all, since nothing is stored in the certificate for this event. Below, we elaborate on make-event replacements when :EXPANSION is used and also discuss other properties of this keyword. We modify the notion of "expansion result" for make-event forms to comprehend the use of the :EXPANSION? keyword. For that purpose, let's consider a call of make-event to be "reducible" if it has an :EXPANSION? keyword with non-nil value, exp, and its :CHECK-EXPANSION keyword is missing or has value nil, in which case the "reduction" of this make-event call is defined to be exp. The expansion result as originally defined is modified by the following "recursive reduction" process: recur through the original expansion, passing through calls of local, skip-proofs, with-output, with-prover-step-limit, and with-prover-time-limit, and replacing (recursively) any reducible call of make-event by its reduction. Furthermore, we refer to two forms as "reduction equivalent" if their recursive reductions are equal. Note that the recursive reduction process does not pass through progn or encapsulate, but that process is applied to the computation of expansions for their subsidiary make-event calls. To explain further the effect of :EXPANSION? exp, we split into the following two cases. o Case 1: Evaluation is not taking place when including a book or evaluating the second pass of an encapsulate event; more precisely, the value of (ld-skip-proofsp state) is not the symbol INCLUDE-BOOK. There are two subcases. - Case 1a: The expansion result is not reduction-equivalent to exp. Then the make-event call is processed as though the :EXPANSION? keyword had been omitted. - Case 2a: The expansion result is reduction-equivalent to exp. Then there is no make-event replacement for this call of make-event; no replacement will be put into the certificate file for a book containing this make-event call. When that book is subsequently included, the original form will be evaluated in the manner described in the next case. o Case 2: Evaluation is taking place when including a book or evaluating the second pass of an encapsulate event; more precisely, the value of (ld-skip-proofsp state) is the symbol INCLUDE-BOOK. Then the expansion is exp. The expansion phase is skipped unless :CHECK-EXPANSION is t. The :EXPANSION? keyword can be particularly useful in concert with the disjunctive (":OR") case (2) discussed above. Suppose that expansion produces a value as discussed in (2) above, (:OR exp-1 ... exp-k). If one of these expressions exp-i is more likely than the others to be the expansion, then you may wish to specify :EXPANSION? exp-i, as this will avoid storing a make-event replacement in that common case. This could be useful if the expressions are large, to avoid enlarging the certificate file for a book containing the make-event call. It is legal to specify both :EXPANSION? exp and :CHECK-EXPANSION t. When either (ld-skip-proofsp state) is the symbol INCLUDE-BOOK, or evaluation is taking place in raw Lisp, then this combination is treated the same as if :EXPANSION? is omitted and the value of :CHECK-EXPANSION is exp. Otherwise, this combination is treated the same as :CHECK-EXPANSION t, modified to accommodate the effect of :EXPANSION? as discussed above: if the expansion is indeed the value of :EXPANSION?, then no make-event replacement is generated.  File: acl2-doc-emacs.info, Node: MAKE-EVENT-DETAILS, Prev: MAKE-EVENT, Up: MAKE-EVENT MAKE-EVENT-DETAILS details on make-event expansion The normal user of make-event can probably ignore this section, but we include it for completeness. We assume that the reader has read and understood the basic documentation for make-event (see *note MAKE-EVENT::), but we begin below with a summary of expansion. *Introduction* Here is a summary of how we handle expansion involving make-event forms. (make-event form :check-expansion nil) This shows the :check-expansion default of nil, and is typical user input. We compute the expansion exp of form, which is the expansion of the original make-event expression and is evaluated in place of that expression. (make-event form :check-expansion t) The user presumably wants it checked that the expansion doesn't change in the future, in particular during include-book. If the expansion of form is exp, then we will evaluate exp to obtain the value as before, but this time we record that the expansion of the original make-event expression is (make-event form :check-expansion exp) rather than simply exp. (make-event form :check-expansion exp) ; exp a cons This is generated for the case that :check-expansion is t, as explained above. Evaluation is handled as described in that above case, except here we check that the expansion result is the given exp. (Actually, the user is also allowed supply such a form.) The original make-event expression does not undergo any expansion (intuitively, it expands to itself). Now let us take a look at how we expand progn forms (encapsulate is handled similarly). (progn ... (make-event form :check-expansion nil) ...) The expansion is obtained by replacing the make-event form as follows. Let exp be the expansion of form, Then replace the above make-event form, which we denote as F, by (record-expansion F exp). Here, record-expansion is a macro that returns its second argument. (progn ... (make-event form :check-expansion t) ...) The expansion is of the form (record-expansion F exp) as in the nil case above, except that this time exp is (make-event form :check-expansion exp'), where exp' is the expansion of form. (progn ... (make-event form :check-expansion exp) ...) ; exp a cons No expansion takes place unless expansion takes place for at least one of the other subforms of the progn, in which case each such form F is replaced by (record-expansion F exp) where exp is the expansion of F. *Detailed semantics* In our explanation of the semantics of make-event, we assume familiarity with the notion of "embedded event form" (see *note EMBEDDED-EVENT-FORM::). Let's say that the "actual embedded event form" corresponding to a given form is the underlying call of an ACL2 event: that is, LOCALs are dropped when ld-skip-proofsp is 'include-book, and macros are expanded away, thus leaving us with a progn, a make-event, or an event form (possibly encapsulate), any of which might have surrounding local, skip-proofs, or with-output calls. Thus, such an actual embedded event form can be viewed as having the form (rebuild-expansion wrappers base-form) where base-form is a progn, a make-event, or an event form (possibly encapsulate), and wrappers are (as in ACL2 source function destructure-expansion) the result of successively removing the event form from the result of macroexpansion, leaving a sequence of (local), (skip-proofs), and (with-output ...) forms. In this case we say that the form "destructures into" the indicated wrappers and base-form, and that it can be "rebuilt from" those wrappers and base-form. Elsewhere we define the notion of the "expansion result" from an evaluation (see *note MAKE-EVENT::), and we mention that when expansion concludes, the ACL2 logical world and most of the state are restored to their pre-expansion values. Specifically, after evaluation of the argument of make-event (even if it is aborted), the ACL2 logical world is restored to its pre-evaluation value, as are all state global variables in the list *protected-system-state-globals*. Thus, assignments to user-defined state globals (see *note ASSIGN::) do persist after expansion, since they are not in that list. We recursively define the combination of evaluation and expansion of an embedded event form, as follows. We also simultaneously define the notion of "expansion takes place," which is assumed to propagate upward (in a sense that will be obvious), such that if no expansion takes place, then the expansion of the given form is considered to be itself. It is useful to keep in mind a goal that we will consider later: Every make-event subterm of an expansion result has a :check-expansion field that is a consp, where for this purpose make-event is viewed as a macro that returns its :check-expansion field. (Implementation note: The latest expansion of a make-event, progn, progn!, or encapsulate is stored in state global 'last-make-event-expansion, except that if no expansion has taken place for that form then 'last-make-event-expansion has value nil.) If the given form is not an embedded event form, then simply cause a soft error, (mv erp val state) where erp is not nil. Otherwise: If the evaluation of the given form does not take place (presumably because local events are being skipped), then no expansion takes place. Otherwise: Let x be the actual embedded event form corresponding to the given form, which destructures into wrappers W and base-form B. Then the original form is evaluated by evaluating x, and its expansion is as follows. If B is (make-event form :check-expansion val), then expansion takes place if and only if val is not a consp and no error occurs, as now described. Let R be the expansion result from protected evaluation of form, if there is no error. R must be an embedded event form, or it is an error. Then evaluate/expand R, where if val is not nil then state global 'ld-skip-proofsp is initialized to nil. (This initialization is important so that subsequent expansions are checked in a corresponding environment, i.e., where proofs are turned on in both the original and subsquent environments.) It is an error if this evaluation causes an error. Otherwise, the evaluation yields a value, which is the result of evaluation of the original make-event expression, as well as an expansion, E_R. Let E be rebuilt from W and E_R. The expansion of the original form is E if val is nil, and otherwise is the result of replacing the original form's :check-expansion field with E, with the added requirement that if val is not t (thus, a consp) then E must equal val or else we cause an error. If B is either (progn form1 form2 ...) or (encapsulate sigs form1 form2 ...), then after evaluating B, the expansion of the original form is the result of rebuilding from B, with wrappers W, after replacing each formi in B for which expansion takes place by (record-expansion formi formi'), where formi' is the expansion of formi. Note that these expansions are determined as the formi are evaluated in sequence (where in the case of encapsulate, this determination occurs only during the first pass). Except, if no expansion takes place for any formi, then the expansion of the original form is itself. Otherwise, the expansion of the original form is itself. Similarly to the progn and encapsulate cases above, book certification causes a book to be replaced by its so-called "book expansion." There, each event ev for which expansion took place during the proof pass of certification -- say, producing ev' -- is replaced by (record-expansion ev ev'). Implementation Note. The book expansion is actually implemented by way of the :expansion-alist field of its certificate, which associates 0-based positions of top-level forms in the book (not including the initial in-package form) with their expansions. Thus, the book's source file is not overwritten; rather, the certificate's expansion-alist is applied when the book is included or compiled. End of Implementation Note. It is straightforward by computational induction to see that for any expansion of an embedded event form, every make-event sub-event has a consp :check-expansion field. Here, by "sub-event" we mean to expand macros; and we also mean to traverse progn and encapsulate forms as well as :check-expansion fields of make-event forms. Thus, we will only see make-event forms with consp :check-expansion fields in the course of include-book forms, the second pass of encapsulate forms, and raw Lisp. This fact guarantees that an event form will always be treated as its original expansion. *Notes on ttags* See *note DEFTTAG:: for documentation of the notion of "trust tag" ("ttag"). We note here that even if an event (defttag tag-name) for non-nil tag-name is admitted only during the expansion phase of a make-event form, then such expansion will nevertheless still cause tag-name to be recorded in the logical world (assuming that the make-event form is admitted). So in order to certify such a book, a suitable :ttags argument must be supplied; see *note CERTIFY-BOOK::. ACL2 does provide a way to avoid the need for :ttags arguments in such cases. The idea is to certify a book twice, where the results of make-event expansion are saved from the first call of certify-book and provided to the second call. See *note SET-WRITE-ACL2X::. Finally, we discuss a very unusual case where certification does not involve trust tags but a subsequent include-book does involve trust tags: a make-event call specifying :check-expansion t, whose expansion generates a defttag event during include-book but not certify-book. Consider the following book. (in-package "ACL2") (make-event (er-progn (if (@ skip-notify-on-defttag) ; non-nil when including a certified book (pprogn (fms "Value of (@ skip-notify-on-defttag): ~x0~|" (list (cons #0 (@ skip-notify-on-defttag))) *standard-co* state nil) (encapsulate () (defttag :foo) (value-triple "Imagine something bad here!"))) (value nil)) (value '(value-triple :some-value))) :check-expansion t) This book certifies successfully without the need for a :ttags argument for certify-book. Indeed, the above book's certificate does not specify :foo as a trust tag associated with the book, because no defttag event was executed during book certification. Unfortunately, if we try to include this book without specifying a value of :ttags that allows :foo, book inclusion will cause executing of the above defttag. If that inclusion happens in the context of certifying some superior book and the appropriate :ttags arguments have not been provided, that certification will fail.  File: acl2-doc-emacs.info, Node: MEMOIZE, Next: MUTUAL-RECURSION, Prev: MAKE-EVENT, Up: EVENTS MEMOIZE turn on memoization for a specified function This documentation topic relates to an experimental extension of ACL2 under development by Bob Boyer and Warren Hunt. See *note HONS-AND-MEMOIZATION:: for a general discussion of memoization and the related features of hash consing and applicative hash tables. Examples: (memoize 'foo) ; remember the values of calls ; of foo (memoize 'foo :condition t) ; same as above (memoize 'foo :condition '(test x)) ; memoize for args satisfying ; the given condition (memoize 'foo :condition-fn 'test) ; memoize for args satisfying ; a call of the given function (memoize 'foo :recursive nil) ; don't memoize recursive calls (memoize 'foo :aokp t) ; attachments OK for stored results (memoize 'foo :ideal-okp t) ; memoize even if foo is in :logic mode ; but has not been guard-verified * Menu: Related topics other than immediate subtopics: * HONS-AND-MEMOIZATION:: hash cons, function memoization, and applicative hash tables General Form: (memoize fn ; memoizes fn and returns fn :condition condition ; optional (default t) :condition-fn condition-fn ; optional :hints hints ; optional, for verifying the ; guards of condition-fn :otf-flg otf-flg ; optional, for verifying the ; guards of condition-fn :recursive t/nil ; optional (default t) :commutative t/lemma-name ; optional (default nil) :forget t/nil ; optional (default nil) :memo-table-init-size size ; optional (default *mht-default-size*) :aokp t/nil ; optional (default nil) :ideal-okp t/:warn/nil ; optional (default nil) :verbose t/nil ; optional (default t) ) where fn evaluates to a user-defined function symbol; condition is either t (the default), 't, nil, or 'nil, or else evaluates to an expression whose free variables are among the formal parameters of fn; and condition-fn is either nil (the default) or else evaluates to a legal function symbol. Further restrictions and options are discussed below. Note that all arguments are evaluated (but for the special handling of value t for :commutative, the argument must literally be t; see below). Generally fn must evaluate to a defined function symbol. However, this value can be the name of a macro that is associated with such a function symbol; see *note MACRO-ALIASES-TABLE::. That associated function symbol is the one called "memoized" in the discussion below, but we make no more mention of this subtlety. In the most common case, memoize takes a single argument, which evaluates to a function symbol. We call this function symbol the "memoized function" because "memos" are saved and re-used, in the following sense. When a call of the memoized function is evaluated, the result is "memoized" by associating the call's arguments with that result, in a suitable table. But first an attempt is made to avoid such evaluation, by doing a lookup in that table on the given arguments for the result, as stored for a previous call on those arguments. If such a result is found, then it is returned without further computation. This paragraph also applies if :condition is supplied but is t or 't. If keyword argument :condition-fn is supplied, but :condition is not, then the result of evaluating :condition-fn must be a defined function symbol whose formal parameter list and guard are the same as for the function being memoized. If fn is in :logic mode, then guards must have been verified for :condition-fn. Such a "condition function" will be run whenever the memoized function is called, on the same parameters, and the lookup or table store described above are only performed if the result from the condition function call is non-nil. Suppose however that :condition is supplied. If the value supplied is t or 't, then the lookup and table store described above are always done. If the value is nil or 'nil, then this lookup and table store are never done, although statistics may be gathered; see *note PROFILE::. Now consider other values for :condition. An attempt will be made to define a condition function whose guard and formal parameters list are the same as those of the memoized function, and whose body is the result, r, of evaluating the given condition. The name of that condition function is the result of evaluating :condition-fn if supplied, else is the result of concatenating the string "-MEMOIZE-CONDITION" to the end of the name of the memoized function. The condition function will be defined with guard verification turned off, but that definition will be followed immediately by a verify-guards event; and this is where the optional :hints and :otf-flg are attached. At evaluation time the condition function is used as described in the preceding paragraph; so in effect, the condition (r, above) is evaluated, with its variables bound to the corresponding actuals of the memoized function call, and the memoized function attempts a lookup or table store if and only if the result of that evaluation is non-nil. Note that fn can be either a :logic mode function or a :program mode function. However, only the corresponding raw Lisp function is actually memoized, so guard violations can defeat memoization, and :logic mode functions without their guards verified will only be memoized when called by :program mode functions. (See *note GUARDS-AND-EVALUATION:: for more information about guards and evaluation in ACL2.) If fn is a :logic mode function and :condition is supplied and not t or nil, then the condition must be a guard-verified function. Calls of this macro generate events of the form (table memoize-table fn ((:condition-fn fn) ...)). When successful, the returned value is of the form (mv nil function-symbol state). Suppose that a function is already memoized. Then it is illegal to memoize that function. Moreover, if the function was memoized with an associated condition (i.e., was memoized with keyword :condition or :condition-fn having value other than t or nil), then it is also illegal to convert the function from :program to :logic mode (see *note VERIFY-TERMINATION::). To turn off memoization, see *note UNMEMOIZE::. Memoize is illegal for a function if its arguments include state or if it returns any stobjs. Also, memoize never allows attachments to be used (see *note DEFATTACH::); if an attachment is used during evaluation, then the evaluation result will not be stored. We conclude with by documenting keyword parameters not discussed above. Keyword parameter :recursive is t by default, which means that recursive calls of fn will be memoized just as "top-level" calls of fn. When :recursive is instead set to nil, memoization is only done at the top level. Using :recursive nil is similar to writing a wrapper function that just calls fn, and memoizing the wrapper instead of fn. If :trace has a non-nil value, then memoize also traces in a traditional Lisp style. If :trace has value notinline or notinline, then a corresponding declaration is added at the beginning of the new definition of fn. A non-nil value for :commutative can be supplied if fn is a binary function in :logic mode. If the memoize event is successful, then subsequently: whenever each argument to fn is either a rational number or a hons, then when the evaluation of fn on those arguments is memoized, the evaluation of fn on the swap of those arguments is, in essence, also memoized. If :commutative is supplied and is not nil or t, then it should be the name of a previously-proved theorem whose formula states the commutativity of fn, i.e., is the formula (equal (fn x y) (fn y x)) for a pair {x,y} of distinct variables. If :commutative is t -- but not merely an expression that evaluates to t -- then an attempt to prove such a lemma will be made on-the-fly. The name of the lemma is the symbol in the same package as fn, obtained by adding the suffix "-COMMUTATIVE" to the symbol-name of fn. If the proof attempt fails, then you may want first to prove the lemma yourself with appropriate hints and perhaps supporting lemmas, and then supply the name of that lemma as the value of :commutative. If :commutative is supplied, and a non-commutative condition is provided by :condition or :condition-fn, then although the results will be correct, the extra memoization afforded by :commutative is unspecified. If :memo-table-init-size is supplied, then it should be a positive integer specifying the initial size of an associated hash table. Argument :aokp is relevant only when attachments are used; see *note DEFATTACH:: for background on attachments. When :aokp is nil, the default, computed values are not stored when an attachment was used, or even when an attachment may have been used because a function was called that had been memoized using :aokp t. Otherwise, computed values are always stored, but saved values are not used except when attachments are allowed. To summarize: aokp=nil (default): ``Pure'', i.e., values do not depend on attachments - Fetch: always legal - Store: only store resulting value when attachments were not used aokp=t: ``Impure'', i.e., values may depend on attachments - Fetch: only legal when attachments are allowed (e.g., not during proofs) - Store: always legal If :ideal-okp is supplied and not nil, then it is permitted to memoize an "ideal-mode" function: one in :logic mode whose guards have not been verified. In general, it is ill-advised to memoize an ideal-mode function, because its calls are typically evaluated "in the logic" without calling a memoized "raw Lisp" version of the function. However, if the function is called by a :program mode function, evaluation can transfer to raw Lisp before reaching the call of the memoized function, in which case memoization will take place. For such situations you can provide value :warn or t for keyword parameter :ideal-okp. Both of these values allow memoization of ideal-mode functions, but if :warn is supplied then a warning will take place. Note that you may set the key :memoize-ideal-okp of the acl2-defaults-table to value t or :warn to change the default, but if parameter :ideal-okp is supplied, the acl2-defaults-table value is ignored. If :verbose is supplied, it should either be nil, which will inhibit proof, event, and summary output (see *note WITH-OUTPUT::), or else t (the default), which does not inhibit output. If the output baffles you, try :trans1 (memoize ...) to see the single-step macroexpansion of your memoize call. The default for :forget is nil. If :forget is supplied, and not nil, then it must be t, which causes all memoization done for a top-level call of fn to be forgotten when that top-level call exits.  File: acl2-doc-emacs.info, Node: MUTUAL-RECURSION, Next: PROFILE, Prev: MEMOIZE, Up: EVENTS MUTUAL-RECURSION define some mutually recursive functions Example: (mutual-recursion (defun evenlp (x) (if (consp x) (oddlp (cdr x)) t)) (defun oddlp (x) (if (consp x) (evenlp (cdr x)) nil))) General Form: (mutual-recursion def1 ... defn) where each defi is a call of defun, defund, defun-nx, or defund-nx. When mutually recursive functions are introduced it is necessary to do the termination analysis on the entire clique of definitions. Each defun form specifies its own measure, either with the :measure keyword xarg (see *note XARGS::) or by default to acl2-count. When a function in the clique calls a function in the clique, the measure of the callee's actuals must be smaller than the measure of the caller's formals -- just as in the case of a simply recursive function. But with mutual recursion, the callee's actuals are measured as specified by the callee's defun while the caller's formals are measured as specified by the caller's defun. These two measures may be different but must be comparable in the sense that o< decreases through calls. If you want to specify :hints or :guard-hints (see *note XARGS::), you can put them in the xargs declaration of any of the defun forms, as the :hints from each form will be appended together, as will the guard-hints from each form. You may find it helpful to use a lexicographic order, the idea being to have a measure that returns a list of two arguments, where the first takes priority over the second. Here is an example. (include-book "ordinals/lexicographic-ordering" :dir :system) (encapsulate () (set-well-founded-relation l<) ; will be treated as LOCAL (mutual-recursion (defun foo (x) (declare (xargs :measure (list (acl2-count x) 1))) (bar x)) (defun bar (y) (declare (xargs :measure (list (acl2-count y) 0))) (if (zp y) y (foo (1- y)))))) The guard analysis must also be done for all of the functions at the same time. If any one of the defuns specifies the :verify-guards xarg to be nil, then guard verification is omitted for all of the functions. Similarly, if any one of the defuns specifies the :non-executable xarg to be t, or if any of the definitions uses defun-nx or defund-nx, then every one of the definitions will be treated as though it specifies a :non-executable xarg of t. Technical Note: Each defi above must be a call of defun, defund, defun-nx, or defund-nx. In particular, it is not permitted for a defi to be an arbitrary form that macroexpands into a defun form. This is because mutual-recursion is itself a macro, and since macroexpansion occurs from the outside in, at the time (mutual-recursion def1 ... defk) is expanded the defi have not yet been macroexpanded. Suppose you have defined your own defun-like macro and wish to use it in a mutual-recursion expression. Well, you can't. (!) But you can define your own version of mutual-recursion that allows your defun-like form. Here is an example. Suppose you define (defmacro my-defun (&rest args) (my-defun-fn args)) where my-defun-fn takes the arguments of the my-defun form and produces from them a defun form. As noted above, you are not allowed to write (mutual-recursion (my-defun ...) ...). But you can define the macro my-mutual-recursion so that (my-mutual-recursion (my-defun ...) ... (my-defun ...)) expands into (mutual-recursion (defun ...) ... (defun ...)) by applying my-defun-fn to each of the arguments of my-mutual-recursion. (defun my-mutual-recursion-fn (lst) (declare (xargs :guard (alistp lst))) ; Each element of lst must be a consp (whose car, we assume, is always ; MY-DEFUN). We apply my-defun-fn to the arguments of each element and ; collect the resulting list of DEFUNs. (cond ((atom lst) nil) (t (cons (my-defun-fn (cdr (car lst))) (my-mutual-recursion-fn (cdr lst)))))) (defmacro my-mutual-recursion (&rest lst) ; Each element of lst must be a consp (whose car, we assume, is always ; MY-DEFUN). We obtain the DEFUN corresponding to each and list them ; all inside a MUTUAL-RECURSION form. (declare (xargs :guard (alistp lst))) (cons 'mutual-recursion (my-mutual-recursion-fn lst))).  File: acl2-doc-emacs.info, Node: PROFILE, Next: PROGN, Prev: MUTUAL-RECURSION, Up: EVENTS PROFILE turn on profiling for one function NOTE: An alternative to this utility, which has a lot less functionality but doesn't depend on the experimental extension of ACL2 mentioned just below, may be found in community book books/misc/profiling.lisp. This documentation topic relates to an experimental extension of ACL2 under development by Bob Boyer and Warren Hunt. See *note HONS-AND-MEMOIZATION:: for a general discussion of memoization, on which this profile utility is built, and the related features of hash consing and applicative hash tables. Profile can be useful in Common Lisp debugging and performance analysis, including examining the behavior of ACL2 functions. Example: (profile 'fn) ; keep count of the calls of fn (profile 'fn ; as above, with with some memoize options :trace t :forget t) (memsum) ; report statistics on calls of memoized functions (e.g., fn) (clear-memoize-statistics) ; clear memoization (and profiling) statistics Evaluation of (profile 'fn) redefines fn so that a count will be kept of the calls of FN. The information recorded may be displayed, for example, by invoking (memoize-summary) or (equivalently) (memsum). If you wish to gather fresh statistics for the evaluation of a top-level form, see *note CLEAR-MEMOIZE-STATISTICS::. Profile is just a macro that calls memoize to do its work. Profile gives the two keyword parameters :condition and :recursive of memoize the value nil. Other keyword parameters for memoize, which must not include :condition, :condition-fn, or :recursive, are passed through. To eliminate profiling, use unmemoize; for example, to eliminate profiling for function fn, evaluate (unmemoize 'fn). You may find raw Lisp functions profile-all and profile-acl2 to be useful. Please contact the ACL2 developers if you want versions of these functions to be executable from inside the ACL2 read-eval-print loop.  File: acl2-doc-emacs.info, Node: PROGN, Next: PROGN!, Prev: PROFILE, Up: EVENTS PROGN evaluate some events Example Form: (progn (defun foo (x) x) (defmacro my-defun (&rest args) (cons 'defun args)) (my-defun bar (x) (foo x))) General form: (progn event1 event2 ... eventk) where k >= 0 and each eventi is a legal embedded event form (see *note EMBEDDED-EVENT-FORM::). These events are evaluated in sequence. A utility is provided to assist in debugging failures of such execution; see *note REDO-FLAT::. NOTE: If the eventi above are not all legal embedded event forms (see *note EMBEDDED-EVENT-FORM::), consider using er-progn or (with great care!) progn! instead. For a related event form that does allow introduction of constraints and local events, see *note ENCAPSULATE::. ACL2 does not allow the use of progn in definitions. Instead, the macro er-progn can be used for sequencing state-oriented operations; see *note ER-PROGN:: and see *note STATE::. If you are using single-threaded objects (see *note STOBJ::) you may wish to define a version of er-progn that cascades the object through successive changes. ACL2's pprogn is the state analogue of such a macro. If your goal is simply to execute a sequence of top-level forms, for example a sequence of definitions, consider using ld instead; see *note LD::.  File: acl2-doc-emacs.info, Node: PROGN!, Next: REGENERATE-TAU-DATABASE, Prev: PROGN, Up: EVENTS PROGN! evaluate some forms, not necessarily events *WARNING!* This event is intended for advanced users who, in essence, want to build extensions of ACL2. See see *note DEFTTAG::, in particular, the "WARNING" there, and see the warning about stobjs at the end of this documentation topic. Progn! can be used like progn, even in books. But unlike progn, progn! does not require its constituent forms to be events (see *note EMBEDDED-EVENT-FORM::), except that the first form cannot be a symbol unless it is :state-global-bindings (advanced feature, described below). However, see *note MAKE-EVENT:: for a "Restriction to the Top Level" that still applies under a call of progn!. Because progn! allows non-events, it differs from progn in another important respect: progn! is illegal unless there is an active ttag; see *note DEFTTAG::. See community book books/hacking/hacker.lisp for two macros, with-raw-mode and with-redef-allowed, each defined in terms of progn!, that allow arbitrary forms in contexts that would normally require legal embedded event forms. Given a form (progn! form1 form2 ... formk), ACL2 will evaluate each formi in turn (for i from 1 to k). If a form returns more than one value (see *note MV::) where the first value returned is not nil, then no later form will be evaluated and the result returned by the progn! call will be (mv erp val state) for some non-nil value erp, signifying an error (see *note LD-ERROR-TRIPLES::). Otherwise the evaluation is considered to have succeeded, and will continue with later forms. The value returned by a call of progn! with no such error is of the form (mv nil v state), where v depends on the last form as follows. If the last form evaluates to a single value, then v is that value, except if the value is a stobj, say ST, then v is the symbol REPLACED-ST. Otherwise the last form evaluates to some (mv nil x ...), and v is x unless after the final form's evaluation we are in raw-mode (see *note SET-RAW-MODE::), in which case the progn! call returns nil (so that ACL2 can at least print the result -- imagine Lisp returning a pathname object from a load call, for example). The normal undoing mechanism does not generally apply to forms within a progn! that are not legal ACL2 events (see *note EMBEDDED-EVENT-FORM::). In particular, note that a non-local call of progn! in an encapsulate event will generally be evaluated twice: once on each pass. This fact is worth keeping in mind if you are using progn! to change the state of the system; ask yourself if it is acceptable to apply that state-changing operation more than once. Please note that progn! may differ from progn in the following sense: definitions within a call of progn! might not be compiled. For example, consider the following book. (in-package "ACL2") (defttag :test) (progn (defun f1 (x) x)) (progn! (defun f2 (x) x)) If the underlying Lisp is GCL 2.6.7, then after including this certified book (where the default certification took place, creating a compiled file), then f1 is a compiled function but f2 is not. For other Lisps supported by ACL2, both f1 and f2 are compiled, though we are not sure that every function under every call of progn! would similarly be compiled. We now describe, for system hackers only, a sophisticated extension of progn! not mentioned above: support for keyword argument :state-global-bindings. If the first argument of progn! is this keyword, then the second argument is treated as a list of bindings as expected by ACl2 system function state-global-let*. Thus, in the ACL2 loop, (progn! :state-global-bindings bindings form1 form2 ... formk) is treated as follows: (progn! (state-global-let* bindings (progn! form1 form2 ... formk))) However, in raw Lisp the former is just: (progn form1 form2 ... formk) Thus, one should use the :state-global-bindings argument with care, since the behavior in the ACL2 loop can differ from that in Common Lisp. The intention is that one bind only state global variables that are relevant to evaluation of the forms within the ACL2 loop and are harmlessly ignored for evaluation of those forms in raw Lisp. Here is a typical sort of example, as state global ld-redefinition-action is not relevant to the evaluation of defun in raw Lisp. (progn! (remove-untouchable 'ld-redefinition-action nil) (progn! :state-global-bindings ((ld-redefinition-action '(:doit . :overwrite))) (defun foo (x) (cons x x))) (push-untouchable 'ld-redefinition-action nil)) Finally, we point out a pitfall of progn! related to stobjs. The following book can cause a hard Lisp error, depending on the host Common Lisp, when certified with a non-nil value for compile-flg (see *note CERTIFY-BOOK::). (in-package "ACL2") (defstobj st fld) (defttag :my-ttag) (progn! (update-fld 3 st)) The problem is that the stobj variable st is not known to raw Lisp. The compilation problem disappears if the last form above is replaced with the following two forms. (include-book "hacking/hacker" :dir :system) (with-raw-mode (update-fld 3 *the-live-st*))  File: acl2-doc-emacs.info, Node: REGENERATE-TAU-DATABASE, Next: REMOVE-CUSTOM-KEYWORD-HINT, Prev: PROGN!, Up: EVENTS REGENERATE-TAU-DATABASE regenerate the tau database relative to the current enabled theory Example: (regenerate-tau-database) General Form: (regenerate-tau-database :doc doc-string) where doc-string is an optional documentation string not beginning with ":Doc-Section ...". Because no unique name is associated with a regenerate-tau-database event, there is no way we can store the documentation string doc-string in our il[documentation] database. Hence, we actually prohibit doc-string from having the form of an ACL2 documentation string; see *note DOC-STRING::. The tau database is regenerated by scanning the current logical world and re-processing every rule-generating event in it relative to the current enabled theory and current tau auto mode settings. See *note INTRODUCTION-TO-THE-TAU-SYSTEM:: for background details. This command was intended to allow the user to remove a fact from the tau database, by regenerating the database without the fact. But as the following discussion highlights, regenerate-tau-database does not really solve the problem. We regard it as a placeholder for a more sophisticated mechanism. However, we have trouble understanding why a user might wish to remove a fact from the database and are awaiting further user experiences before designing the more sophisticated mechanism. Suppose, for example, that you wanted to remove a signature rule provided by some rule with name rune. You could disable rune and regenerate the database. We discuss why you might -- or might not -- want to do this later. But suppose you did it. Unfortunately, the database you get will not be just like the one you started with minus the signature rule. The reason is that the database you started with was generated incrementally and the current theory might have evolved. To take a simple example, your starting database might have included a rule that has been disabled since it was first added. Thus, the part of your starting database built before the disabling was constructed with the rule enabled and the part built afterwards has the rule disabled. You are unlikely to get the same database whether you enable or disable that rule now. You might hope that the avoidance of in-theory events would eliminate the problem but it does not because even the ground-zero theory is constructed incrementally from the "pre-history" commands used to boot up ACL2. Those pre-history commands include some global in-theory commands. Since every session starts from the ground-zero state, the starting database is already "infected" with global in-theory commands. The reason we hope that it will not be necessary to remove tau facts is that the tau system is designed merely to be fast and benign (see Design Philosophy in introduction-to-the-tau-system). The tau system's coverage should grows monotonically with the addition of rules. According to this understanding of tau, adding a signature rule, for example, may allow tau to prove some additional goals but will not prevent it from proving goals it could prove previously. If this is understanding of tau is accurate, we see no fundamental reason to support the removal of a fact. This, of course, ignores the possibility that the user wishes to explore alternative proof strategies or measure performance. We welcome user observations and experience on this issue.  File: acl2-doc-emacs.info, Node: REMOVE-CUSTOM-KEYWORD-HINT, Next: RESTORE-MEMOIZATION-SETTINGS, Prev: REGENERATE-TAU-DATABASE, Up: EVENTS REMOVE-CUSTOM-KEYWORD-HINT remove a custom keyword hint Example Forms: (remove-custom-keyword-hint :my-hint) General Form: (remove-custom-keyword-hint keyword) where keyword is a keywordp. For an explanation of how custom keyword hints are processed, see *note CUSTOM-KEYWORD-HINTS::; also see *note ADD-CUSTOM-KEYWORD-HINT::. Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.  File: acl2-doc-emacs.info, Node: RESTORE-MEMOIZATION-SETTINGS, Next: SAVE-AND-CLEAR-MEMOIZATION-SETTINGS, Prev: REMOVE-CUSTOM-KEYWORD-HINT, Up: EVENTS RESTORE-MEMOIZATION-SETTINGS restore the saved memoization settings For background on memoization, see *note MEMOIZE::. General Form: (restore-memoization-settings) Calls of this macro restore the memoization settings saved by save-and-clear-memoization-settings. * Menu: Related topics other than immediate subtopics: * HONS-AND-MEMOIZATION:: hash cons, function memoization, and applicative hash tables  File: acl2-doc-emacs.info, Node: SAVE-AND-CLEAR-MEMOIZATION-SETTINGS, Next: SET-BODY, Prev: RESTORE-MEMOIZATION-SETTINGS, Up: EVENTS SAVE-AND-CLEAR-MEMOIZATION-SETTINGS save and remove the current memoization settings For background on memoization, see *note MEMOIZE::. General Form: (save-and-clear-memoization-settings) Calls of this macro achieve two changes. The first copies the current memoization settings into an ACL2 table, and the second unmemoizes all functions that were memoized by calls of memoize. Also see *note RESTORE-MEMOIZATION-SETTINGS::. * Menu: Related topics other than immediate subtopics: * HONS-AND-MEMOIZATION:: hash cons, function memoization, and applicative hash tables  File: acl2-doc-emacs.info, Node: SET-BODY, Next: TABLE, Prev: SAVE-AND-CLEAR-MEMOIZATION-SETTINGS, Up: EVENTS SET-BODY set the definition body Examples: (set-body foo (:definition foo)) ; restore original definition of foo (set-body foo foo) ; same as just above (set-body foo my-foo-def) ; use my-foo-def for the body of foo (set-body foo (:definition my-foo-def)) ; same as just above Rules of class :definition can install a new definition body, used for example by :expand hints. See *note DEFINITION:: and also see *note HINTS:: for a detailed discussion of the :install-body fields of :definition rules and their role in :expand hints. There may be several such definitions, but by default, the latest one is used by :expand hints. Although the :with keyword may be used in :expand hints to override this behavior locally (see *note HINTS::), it may be convenient to install a definition for expansion other than the latest one -- for example, the original definition. Set-body may be used for this purpose. General Form: (set-body function-symbol rule-name) where rule-name is either a :definition rune or is a function symbol, sym, which represents the rune (:definition sym). You can view all definitions available for expansion; see *note SHOW-BODIES::.  File: acl2-doc-emacs.info, Node: TABLE, Next: THEORY-INVARIANT, Prev: SET-BODY, Up: EVENTS TABLE user-managed tables Examples: (table tests 1 '(...)) ; set contents of tests[1] to '(...) (table tests 25) ; get contents of tests[25] (table tests) ; return table tests as an alist (table tests nil nil :clear) ; clear table tests (table tests nil '((foo . 7)) :clear) ; set table tests to ((foo . 7)) (table tests nil nil :guard) ; fetch the table guard (table tests nil nil :guard term) ; set the table guard * Menu: * USING-TABLES-EFFICIENTLY:: Notes on how to use tables efficiently General Form: (table table-name key-term value-term op term) where table-name is a symbol that is the name of a (possibly new) table, key-term and value-term, if present, are arbitrary terms involving (at most) the single variable world, op, if present, is one of the table operations below, and term, if present, is a term. Table returns an ACL2 "error triple" (see *note ERROR-TRIPLES::). The effect of table on state depends on op and how many arguments are presented. Some invocations actually have no effect on the ACL2 world and hence an invocation of table is not always an "event". We explain below, after giving some background information. Important Note: The table forms above are calls of a macro that expand to involve the special variable state. This will prevent you from accessing a table from within a hint or theory where you do not have the state variable. However, the form (table-alist 'tests world) returns the alist representation of the table named test in the given world. Often you have access to world. The ACL2 system provides "tables" by which the user can associate one object with another. Tables are in essence just conventional association lists -- lists of pairs -- but the ACL2 environment provides a means of storing these lists in the "ACL2 world" of the current state. The ACL2 user could accomplish the same ends by using ACL2 "global variables;" however, limitations on global variable names are imposed to ensure ACL2's soundness. By convention, no table is important to ACL2's soundness, even though some features of the system use tables, and the user is invited to make free use of tables. Because tables are stored in the ACL2 world they are restored by include-book and undone by :ubt. Many users of Nqthm requested a facility by which user data could be saved in Nqthm "lib files" and tables are ACL2's answer to that request. Abstractly, each table is an association list mapping "keys" to "values." In addition, each table has a ":guard," which is a term that must be true of any key and value used. By setting the :guard on a table you may enforce an invariant on the objects in the table, e.g., that all keys are positive integers and all values are symbols. Each table has a "name," which must be a symbol. Given a table name, the following operations can be performed on the table. :put -- associate a value with a key (possibly changing the value currently associated with that key). :get -- retrieve the value associated with a key (or nil if no value has been associated with that key). :alist -- return an alist showing all keys and non-nil values in the table. :clear -- clear the table (so that every value is nil), or if val is supplied then set table to that value (which must be an alist). :guard -- fetch or set the :guard of the table. When the operations above suggest that the table or its :guard are modified, what is actually meant is that the current state is redefined so that in it, the affected table name has the appropriate properties. in such cases, the table form is an event (see *note EVENTS::). In the :put case, if the key is already in the table and associated with the proposed value, then the table event is redundant (see *note REDUNDANT-EVENTS::). Table forms are commonly typed by the user while interacting with the system. :Put and :get forms are especially common. Therefore, we have adopted a positional syntax that is intended to be convenient for most applications. Essentially, some operations admit a "short form" of invocation. (table name key-term value-term :put) ; long form (table name key-term value-term) ; short form evaluates the key- and value-terms, obtaining two objects that we call key and value, checks that the key and value satisfy the :guard on the named table and then "modifies" the named table so that the value associated with key is value. When used like this, table is actually an event in the sense that it changes the ACL2 world. In general, the forms evaluated to obtain the key and value may involve the variable world, which is bound to the then-current world during the evaluation of the forms. However, in the special case that the table in question is named acl2-defaults-table, the key and value terms may not contain any variables. Essentially, the keys and values used in events setting the acl2-defaults-table must be explicitly given constants. See *note ACL2-DEFAULTS-TABLE::. (table name key-term nil :get) ; long form (table name key-term) ; short form evaluates the key-term (see note below), obtaining an object, key, and returns the value associated with key in the named table (or, nil if there is no value associated with key). When used like this, table is not an event; the value is simply returned. (table name nil nil :alist) ; long form (table name) ; short form returns an alist representing the named table; for every key in the table with a non-nil associated value, the alist pairs the key and its value. The order in which the keys are presented is unspecified. When used like this, table is not an event; the alist is simply returned. (table name nil val :clear) sets the named table to the alist val, making the checks that :put makes for each key and value of val. When used like this, table is an event because it changes the ACL2 world. (table name nil nil :guard) returns the translated form of the guard of the named table. (table name nil nil :guard term) Provided the named table is empty and has not yet been assigned a :guard and term (which is not evaluated) is a term that mentions at most the variables key, val and world, this event sets the :guard of the named table to term. Whenever a subsequent :put occurs, term will be evaluated with key bound to the key argument of the :put, val bound to the val argument of the :put, and world bound to the then current world. An error will be caused by the :put if the result of the evaluation is nil. Note that it is not allowed to change the :guard on a table once it has been explicitly set. Before the :guard is explicitly set, it is effectively just t. After it is set it can be changed only by undoing the event that set it. The purpose of this restriction is to prevent the user from changing the :guards on tables provided by other people or the system. The intuition behind the :guard mechanism on tables is to enforce invariants on the keys and values in a table, so that the values, say, can be used without run-time checking. But if the :guard of a table is sensitive to the ACL2 world, it may be possible to cause some value in the table to cease satisfying the :guard without doing any operations on the table. Consider for example the :guard "no value in this table is the name of an event." As described, that is enforced each time a value is stored. Thus, 'bang can be :put in the table provided there is no event named bang. But once it is in the table, there is nothing to prevent the user from defining bang as a function, causing the table to contain a value that could not be :put there anymore. Observe that not all state-sensitive :guards suffer this problem. The :guard "every value is an event name" remains invariant, courtesy of the fact that undoing back through an event name in the table would necessarily undo the :put of the name into the table. Table was designed primarily for convenient top-level use. Tables are not especially efficient. Each table is represented by an alist stored on the property list of the table name. :Get is just a getprop and assoc-equal. :Put does a getprop to the get the table alist, a put-assoc-equal to record the new association, and a putprop to store the new table alist -- plus the overhead associated with :guards and undoable events, and checking (for redundancy) if the key is already bound to its proposed value. Note that there are never duplicate keys in the resulting alist; in particular, when the operation :clear is used to install new alist, duplicate keys are removed from that alist. A table name may be any symbol whatsoever. Symbols already in use as function or theorem names, for example, may be used as table names. Symbols in use only as table names may be defined with defun, etc. Because there are no restrictions on the user's choice of table names, table names are not included among the logical names. Thus, :pe name will never display a table event (for a logical name other than :here). Either :pe name will display a "normal" event such as (defun name ...) or (defthm name ...) or else :pe name will cause an error indicating that name is not a logical name. This happens even if name is in use as a table name. Similarly, we do not permit table names to have documentation strings, since the same name might already have a documentation string. If you want to associate a documentation string with a table name that is being used no other way, define the name as a label and use the :doc feature of deflabel (see *note DEFLABEL::); also see *note DEFDOC::.  File: acl2-doc-emacs.info, Node: USING-TABLES-EFFICIENTLY, Prev: TABLE, Up: TABLE USING-TABLES-EFFICIENTLY Notes on how to use tables efficiently (Thanks to Jared Davis for contributing this documentation topic, to which we have made only minor modifications.) Suppose your book contains table events, or macros that expand into table events, of the following form: (table my-table 'my-field ) Then will be evaluated _twice_ during certify-book and _again_ every time you include the book with include-book. In some cases this overhead can be avoided using make-event. See also community book books/make-event/defconst-fast.lisp for an analogous trick involving defconst. * Menu: Related topics other than immediate subtopics: * MAKE-EVENT:: evaluate (expand) a given form and then evaluate the result As an example, suppose we want to store numbers in a table only if they satisfy some computationally expensive predicate. We'll introduce a new book, number-table.lisp, and create a table to store these numbers: (table number-table 'data nil) Instead of implementing a "computationally expensive predicate," we'll write a function that just prints a message when it is called and accepts even numbers: (defun expensive-computation (n) (prog2$ (cw "Expensive computation on ~x0.~%" n) (evenp n))) Now we'll implement a macro, add-number, which will add its argument to the table only if it satisfies the expensive predicate: (defmacro add-number (n) `(table number-table 'data (let ((current-data (cdr (assoc-eq 'data (table-alist 'number-table world))))) (if (expensive-computation ,n) (cons ,n current-data) current-data)))) Finally, we'll call add-number a few times to finish the book. (add-number 1) (add-number 2) (add-number 3) When we now invoke (certify-book "number-table"), we see the expensive predicate being called twice for each number: once in Step 2, the main pass, then again in Step 3, the admissibility check. Worse, the computation is performed again for each number when we use include-book to load number-table, e.g., ACL2 !>(include-book "number-table") Expensive computation on 1. Expensive computation on 2. Expensive computation on 3. To avoid these repeated executions, we can pull the test out of the table event using make-event. Here's an alternate implementation of add-number that won't repeat the computation: (defmacro add-number (n) `(make-event (if (expensive-computation ,n) '(table number-table 'data (cons ,n (cdr (assoc 'data (table-alist 'number-table world))))) '(value-triple :expensive-computation-failed)))) When we recertify number-table.lisp, we'll see the expensive computation is still called once for each number in Step 2, but is no longer called during Step 3. Similarly, the include-book no longer shows any calls of the expensive computation.  File: acl2-doc-emacs.info, Node: THEORY-INVARIANT, Next: UNMEMOIZE, Prev: TABLE, Up: EVENTS THEORY-INVARIANT user-specified invariants on theories Examples: (theory-invariant (not (and (active-runep '(:rewrite left-to-right)) (active-runep '(:rewrite right-to-left)))) :key my-invariant :error nil) ; Equivalent to the above: (theory-invariant (incompatible (:rewrite left-to-right) (:rewrite right-to-left)) :key my-invariant :error nil) General Form: (theory-invariant term &key key error) where: o term is a term that uses no variables other than ens and state; o key is an arbitrary "name" for this invariant (if omitted, an integer is generated and used); and o :error specifies the action to be taken when an invariant is violated -- either nil if a warning is to be printed, else t (the default) if an error is to be caused. Theory-invariant is an event that adds to or modifies the table of user-supplied theory invariants that are checked each time a theory expression is evaluated. The theory invariant mechanism is provided via a table (see *note TABLE::) named theory-invariant-table. In fact, the theory-invariant "event" is just a macro that expands into a use of the table event. More general access to the theory-invariant table is provided by table itself. For example, the table can be inspected or cleared with table; you can clear an individual theory invariant by setting the invariant to t, or eliminate all theory invariants with the command (table theory-invariant-table nil nil :clear). Theory-invariant-table maps arbitrary keys to records containing terms that mention, at most, the variables ens and state. Every time an alleged theory expression is evaluated, e.g., in the in-theory event or :in-theory hint, each of the terms in theory-invariant-table is evaluated with ens bound to a so-called "enabled structure" obtained from the theory expression and state bound to the current ACL2 state (see *note STATE::). Users generally need not know about the enabled structure, other than that it can be accessed using the macros active-runep and incompatible; see *note ACTIVE-RUNEP:: and see *note INCOMPATIBLE::. If the result is nil, a message is printed and an error occurs (except, only a warning occurs if :error nil is specified). Thus, the table can be thought of as a list of conjuncts. Each term in the table has a "name," which is just the key under which the term is stored. When a theory violates the restrictions specified by some term, both the name and the term are printed. By calling theory-invariant with a new term but the same name, you can overwrite that conjunct of the theory invariant; but see the Local Redefinition Caveat at the end of this note. You may want to avoid using explicit names, since otherwise the subsequent inclusion of another book that defines a theory invariant with the same name will override your theory invariant. Theory invariants are particularly useful in the context of large rule sets intended for re-use. Such sets often contain conflicting rules, e.g., rules that are to be enabled when certain function symbols are disabled, rules that rewrite in opposite directions and thus loop if simultaneously enabled, groups of rules which should be enabled in concert, etc. The developer of such rule sets understands these restrictions and probably documents them. The theory invariant mechanism allows the developer to codify his restrictions so that the user is alerted when they are violated. Since theory invariants are arbitrary terms, macros may be used to express commonly used restrictions. For example, executing the event (theory-invariant (incompatible (:rewrite left-to-right) (:rewrite right-to-left))) would subsequently cause an error any time the current theory contained both of the two runes shown. Of course, incompatible is just defined as a macro. Its definition may be inspected with :pe incompatible. In order for a theory-invariant event to be accepted, the proposed theory invariant must be satisfied by the current theory (see *note CURRENT-THEORY::). The value returned upon successful execution of the event is the key (whether user-supplied or generated). Local Redefinition Caveat. Care needs to be taken when redefining a theory invariant in a local context. Consider the following example. (theory-invariant (active-runep '(:definition binary-append)) :key app-inv) (encapsulate () (local (theory-invariant t :key app-inv)) (in-theory (disable binary-append)) (defthm ...)) The second pass of the encapsulate will fail, because the in-theory event violates the original theory-invariant and the local theory-invariant is skipped in the second pass of the encapsulate. Of course, local theory-invariants in books can cause the analogous problem in the second (include-book) pass of a certify-book. In both cases, though, the theory invariants are only checked at the conclusion of the (include-book or encapsulate) event. Indeed, theory invariants are checked at the end of every event related to theories, including defun, defthm, in-theory, encapsulate, and include-book, except for events executed on behalf of an include-book or the second pass of an encapsulate.  File: acl2-doc-emacs.info, Node: UNMEMOIZE, Next: VALUE-TRIPLE, Prev: THEORY-INVARIANT, Up: EVENTS UNMEMOIZE turn off memoization for the specified function Example: (unmemoize 'foo) ; turn off memoization of foo * Menu: Related topics other than immediate subtopics: * HONS-AND-MEMOIZATION:: hash cons, function memoization, and applicative hash tables General Form: (unmemoize fn) where fn evaluates to a function symbol that is currently memoized; see *note MEMOIZE::. An exception is that as with memoize, fn may evaluate to the name of a macro that is associated with such a function symbol; see *note MACRO-ALIASES-TABLE::. Calls of this macro generate events of the form (table memoize-table fn nil). When successful, the returned value is of the form (mv nil function-symbol state). To remove the effects of all memoize events, evaluate: (clear-memo-table). To save and restore memoization, see *note SAVE-AND-CLEAR-MEMOIZATION-SETTINGS:: and see *note RESTORE-MEMOIZATION-SETTINGS::.  File: acl2-doc-emacs.info, Node: VALUE-TRIPLE, Next: VERIFY-GUARDS, Prev: UNMEMOIZE, Up: EVENTS VALUE-TRIPLE compute a value, optionally checking that it is not nil Examples: (value-triple (+ 3 4)) (value-triple (cw "hi") :on-skip-proofs t) (value-triple (@ ld-pre-eval-print)) (value-triple (@ ld-pre-eval-print) :check t) General Form: (value-triple form :on-skip-proofs sp ; optional; nil by default :check chk ; optional; nil by default ) Value-triple provides a convenient way to evaluate a form in an event context, including progn and encapsulate and in books; see *note EVENTS::. The form should evaluate to a single, non-stobj value. Calls of value-triple are generally skipped when proofs are being skipped, in particular when ACL2 is performing the second pass through the events of an encapsulate form or during an include-book, or indeed any time ld-skip-proofsp is non-nil. If you want the call evaluated during those times as well, use a non-nil value for :on-skip-proofs. Note that the argument to :on-skip-proofs is not evaluated. If you expect the form to evaluate to a non-nil value and you want an error to occur when that is not the case, you can use :check t. More generally, the argument of :check can be a form that evaluates to a single, non-stobj value. If this value is not nil, then the aforementioned test is made (that the given form is not nil). If an error occurs and the value of :check is a string or indeed any "message" suitable for printing by fmt when supplied as a value for tilde-directive ~@, then that string or message is printed.  File: acl2-doc-emacs.info, Node: VERIFY-GUARDS, Next: VERIFY-GUARDS+, Prev: VALUE-TRIPLE, Up: EVENTS VERIFY-GUARDS verify the guards of a function See *note GUARD:: for a general discussion of guards. Before discussing the verify-guards event, we first discuss guard verification, which can take place at definition time or, later, using verify-guards. Typically, guard verification takes place at definition time if a guard (or type, or stobjs) has been supplied explicitly unless :verify-guards nil has been specified; see *note DEFUN:: and see *note XARGS::, and see *note SET-VERIFY-GUARDS-EAGERNESS:: for how to change this default. The point of guard verification is to ensure that during evaluation of an expression without free variables, no guard violation takes place. Technical note: the first argument of verify-guards must be a function symbol or the name of a defthm or defaxiom event, not a macro-alias for a function symbol (see *note MACRO-ALIASES-TABLE::). See *note VERIFY-GUARDS+:: for a utility that does not have this restriction. Guard verification is intended to guarantee that for any call of a given function, if its guard holds for that call then the guard will hold for every function call in the body of that function. Moreover, in order to avoid guard violations during evaluation of the function's guard itself, guard verification also is intended to guarantee that the guards are satisfied for all calls in the guard itself. Consider the following simple example. (defun f (x) (declare (xargs :guard (and (consp x) (integerp (car x))))) (if (rationalp (cdr x)) (+ (car x) (cdr x)) 17)) If you evaluate (f t), for example, in the top-level loop, you will (by default) get a guard error. The point of guard verification is to guarantee the absence of guard errors, and we start by using this example to illustrate the proof obligations that guarantee such absence. The body of the above definition has the following function calls, where the first is the entire body. (if (rationalp (cdr x)) (< (car x) (cdr x)) 17) (rationalp (cdr x)) ; the test of the top-level IF call (cdr x) ; from (rationalp (cdr x)) (< (car x) (cdr x)) ; the true branch of the top-level IF call (car x) ; from (< (car x) (cdr x)) (cdr x) ; from (< (car x) (cdr x)) We thus see potentially six conditions to prove, one for each call. The guards of the function symbols of those calls are t for if and rationalp, (or (consp x) (equal x nil)) for both (car x) and (cdr x), and finally that both arguments are rationals for <. Moreover, we can take advantage of "contextual assumptions": the if-test conditions and the top-level :guard. Thus, for verify-guards the proof obligation from the body of f is as follows. (implies (and (consp x) (integerp (car x))) ; from the :guard (and t ; from the top-level IF call t ; from (rationalp (cdr x)) (or (consp x) (equal x nil)) ; from the first (cdr x) (implies (rationalp (cdr x)) ; IF-test for calls in the true branch (and (or (consp x) (equal x nil)) ; from (car x) (or (consp x) (equal x nil)) ; from the second (cdr x) (and (rationalp (car x)) (rationalp (cdr x))) ; from the < call )))) But the :guard itself generates a similar sort of proof obligation. Note that the guard (and (consp x) (integerp (car x))) is really an abbreviation (i.e. via the macro AND) for the term (if (consp x) (integerp (car x)) nil). The guard proof obligation for the guard itself is thus as follows. (and t ; from (consp x) (implies (consp x) (and t ; from (integerp (car x)) ; (consp x) ; from (car x) ; ))) All of the above proof obligations are indeed theorems, and guard verification succeeds for the above definition of f. The example above illustrates the general procedure for generating the guard proof obligation. Each function call is considered in the body or guard of the function, and it is required that the guard is met for that call, under certain "contextual assumptions", which are as follows. In the case of the body of the named function, it is assumed that the guard holds for that function on its formal parameters. And in both cases -- the body of the named function and also its guard -- the governing tests from superior calls of IF are also assumed. As mentioned above, if the guard on a function is not t, then guard verification requires not only consideration of the body under the assumption that the guard is true, but also consideration of the guard itself. Thus, for example, guard verification fails in the following example, even though there are no proof obligations arising from the body, because the guard itself can cause a guard violation when evaluated for an arbitrary value of x: (defun foo (x) (declare (xargs :guard (car x))) x) We turn now to the verify-guards event as a way of verifying the guards for a function or theorem. Examples: (verify-guards flatten) (verify-guards flatten :hints (("Goal" :use (:instance assoc-of-app))) :otf-flg t :guard-debug t ; default = nil :doc "string") General Form: (verify-guards name :hints hints :otf-flg otf-flg :guard-debug t ; typically t, but any value is legal :doc doc-string) In the General Form above, name is the name of a :logic function (see *note DEFUN-MODE::) or of a theorem or axiom. In the most common case name is the name of a function that has not yet had its guards verified, each subroutine of which has had its guards verified. The values hints, otf-flg, and guard-debug are as described in the corresponding documentation entries; and doc-string, if supplied, is a string *not* beginning with ":Doc-Section". The four keyword arguments above are all optional. To admit this event, the conjunction of the guard proof obligations must be proved. If that proof is successful, name is considered to have had its guards verified. See *note VERIFY-GUARDS-FORMULA:: for a utility that lets you view the formula to be proved by verify-guards, but without creating an event. If name is one of several functions in a mutually recursive clique, verify-guards will attempt to verify the guards of all of the functions. If name is a theorem or axiom name, verify-guards verifies the guards of the associated formula. When a theorem has had its guards verified then you know that the theorem will evaluate to non-nil in all Common Lisps, without causing a runtime error (other than possibly a resource error). In particular, you know that the theorem's validity does not depend upon ACL2's arbitrary completion of the domains of partial Common Lisp functions. For example, if app is defined as (defun app (x y) (declare (xargs :guard (true-listp x))) (if (endp x) y (cons (car x) (app (cdr x) y)))) then we can verify the guards of app and we can prove the theorem: (defthm assoc-of-app (equal (app (app a b) c) (app a (app b c)))) However, if you go into almost any Common Lisp in which app is defined as shown and evaluate (equal (app (app 1 2) 3) (app 1 (app 2 3))) we get an error or, perhaps, something worse like nil! How can this happen since the formula is an instance of a theorem? It is supposed to be true! It happens because the theorem exploits the fact that ACL2 has completed the domains of the partially defined Common Lisp functions like car and cdr, defining them to be nil on all non-conses. The formula above violates the guards on app. It is therefore "unreasonable" to expect it to be valid in Common Lisp. But the following formula is valid in Common Lisp: (if (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c))) t) That is, no matter what the values of a, b and c the formula above evaluates to t in all Common Lisps (unless the Lisp engine runs out of memory or stack computing it). Furthermore the above formula is a theorem: (defthm guarded-assoc-of-app (if (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c))) t)) This formula, guarded-assoc-of-app, is very easy to prove from assoc-of-app. So why prove it? The interesting thing about guarded-assoc-of-app is that we can verify the guards of the formula. That is, (verify-guards guarded-assoc-of-app) succeeds. Note that it has to prove that if a and b are true lists then so is (app a b) to establish that the guard on the outermost app on the left is satisfied. By verifying the guards of the theorem we know it will evaluate to true in all Common Lisps. Put another way, we know that the validity of the formula does not depend on ACL2's completion of the partial functions or that the formula is "well-typed." One last complication: The careful reader might have thought we could state guarded-assoc-of-app as (implies (and (true-listp a) (true-listp b)) (equal (app (app a b) c) (app a (app b c)))) rather than using the if form of the theorem. We cannot! The reason is technical: implies is defined as a function in ACL2. When it is called, both arguments are evaluated and then the obvious truth table is checked. That is, implies is not "lazy." Hence, when we write the guarded theorem in the implies form we have to prove the guards on the conclusion without knowing that the hypothesis is true. It would have been better had we defined implies as a macro that expanded to the if form, making it lazy. But we did not and after we introduced guards we did not want to make such a basic change. Recall however that verify-guards is almost always used to verify the guards on a function definition rather than a theorem. We now return to that discussion. Because name is not uniquely associated with the verify-guards event (it necessarily names a previously defined function) the documentation string, doc-string, is not stored in the documentation database. Thus, we actually prohibit doc-string from having the form of an ACL2 documentation string; see *note DOC-STRING::. Verify-guards must often be used when the value of a recursive call of a defined function is given as an argument to a subroutine that is guarded. An example of such a situation is given below. Suppose app (read "append") has a guard requiring its first argument to be a true-listp. Consider (defun rev (x) (declare (xargs :guard (true-listp x))) (cond ((endp x) nil) (t (app (rev (cdr x)) (list (car x)))))) Observe that the value of a recursive call of rev is being passed into a guarded subroutine, app. In order to verify the guards of this definition we must show that (rev (cdr x)) produces a true-listp, since that is what the guard of app requires. How do we know that (rev (cdr x)) is a true-listp? The most elegant argument is a two-step one, appealing to the following two lemmas: (1) When x is a true-listp, (cdr x) is a true-listp. (2) When z is a true-listp, (rev z) is a true-listp. But the second lemma is a generalized property of rev, the function we are defining. This property could not be stated before rev is defined and so is not known to the theorem prover when rev is defined. Therefore, we might break the admission of rev into three steps: define rev without addressing its guard verification, prove some general properties about rev, and then verify the guards. This can be done as follows: (defun rev (x) (declare (xargs :guard (true-listp x) :verify-guards nil)) ; Note this additional xarg. (cond ((endp x) nil) (t (app (rev (cdr x)) (list (car x)))))) (defthm true-listp-rev (implies (true-listp x2) (true-listp (rev x2)))) (verify-guards rev) The ACL2 system can actually admit the original definition of rev, verifying the guards as part of the defun event. The reason is that, in this particular case, the system's heuristics just happen to hit upon the lemma true-listp-rev. But in many more complicated functions it is necessary for the user to formulate the inductively provable properties before guard verification is attempted. *Remark on computation of guard conjectures and evaluation*. When ACL2 computes the guard conjecture for the body of a function, it evaluates any ground subexpressions (those with no free variables), for calls of functions whose :executable-counterpart runes are enabled. Note that here, "enabled" refers to the current global theory, not to any :hints given to the guard verification process; after all, the guard conjecture is computed even before its initial goal is produced. Also note that this evaluation is done in an environment as though :set-guard-checking :all had been executed, so that we can trust that this evaluation takes place without guard violations; see *note SET-GUARD-CHECKING::. If you want to verify the guards on functions that are built into ACL2, you will first need to put them into :logic mode. See *note VERIFY-TERMINATION::, specifically the "Remark on system functions" in that documentation.  File: acl2-doc-emacs.info, Node: VERIFY-GUARDS+, Next: VERIFY-TERMINATION, Prev: VERIFY-GUARDS, Up: EVENTS VERIFY-GUARDS+ verify the guards of a function We assume familiarity with guard verification; see *note VERIFY-GUARDS::. Unlike verify-guards, the macro call (verify-guards+ mac ...) will verify guards for a function, fn, such that the macro mac is associated with the function symbol fn in macro-aliases-table (also see *note ADD-MACRO-ALIAS::). For example, if you define a macro app and list append function binary-app, and you associate macro app with function symbol binary-app in macro-aliases-table, then evaluation of the form (verify-guard+ app) will have the effect of evaluating (verify-guards fn). Note that in this setting, evaluation of (verify-guard app) would cause an error, because app is a macro and verify-guards, unlike verify-guards+, cannot handle macros. The rest of this documentation topic discusses why we do not simply arrange that verify-guards be permitted to take a macro alias. The following example shows a soundness issue in doing so. (encapsulate () (defun f1 (x) (declare (xargs :guard (consp x) :verify-guards nil)) (car x)) (defun f2 (x) (declare (xargs :guard t :verify-guards nil)) (cdr x)) (defmacro mac (x) x) (add-macro-alias mac f2) ; silly macro alias ; (local (add-macro-alias mac f1)) ; alternate silly macro alias ; (verify-guards mac)) If we were to allow macro aliases in verify-guards, this event would be admitted, because on the first pass we are verifying guards of f1. But after the encapsulate form completes evaluation, it would appear that f2 is guard-verified. That could of course cause a raw Lisp error. The enhanced functionality provided by verify-guards+ does not have the above problem, because it takes advantage of make-event to avoid taking advantage of the contradictory results produced by the two calls of add-macro-alias. See *note MAKE-EVENT::. If the specific example above is modified by replacing verify-guards with verify-guards+, then the first pass through the encapsulate form will expand the form (verify-guards+ mac) to (verify-guards f1). That same expansion will be used for the verify-guards+ call during the second pass through the encapsulate form, which is evaluated successfully and leaves us in a world where f1 is guard-verified and f2 is not.  File: acl2-doc-emacs.info, Node: VERIFY-TERMINATION, Prev: VERIFY-GUARDS+, Up: EVENTS VERIFY-TERMINATION convert a function from :program mode to :logic mode Example: (verify-termination fact) General Forms: (verify-termination fn dcl ... dcl) (verify-termination (fn1 dcl ... dcl) (fn2 dcl ... dcl) ...) where fn and the fni are function symbols having :program mode (see *note DEFUN-MODE::) and all of the dcls are either declare forms or documentation strings. The first form above is an abbreviation for (verify-termination (fn dcl ... dcl)) so we limit our discussion to the second form. Each of the fni must be in the same clique of mutually recursively defined functions, but not every function in the clique need be among the fni. Verify-termination attempts to establish the admissibility of the fni. Verify-termination retrieves their definitions, creates modified definitions using the dcls supplied above, and resubmits these definitions. You could avoid using verify-termination by typing the new definitions yourself. So in that sense, verify-termination adds no new functionality. But if you have prototyped your system in :program mode and tested it, you can use verify-termination to resubmit your definitions and change their defun-modes to :logic, addings hints without having to retype or recopy the code. The defun command executed by verify-termination is obtained by retrieving the defun (or mutual-recursion) command that introduced the clique in question and then possibly modifying each definition as follows. Consider a function, fn, in the clique. If fn is not among the fni above, its definition is left unmodified other than to add (declare (xargs :mode :logic)). Otherwise, fn is some fni and we modify its definition by inserting into it the corresponding dcls listed with fni in the arguments to verify-termination, as well as (declare (xargs :mode :logic)). In addition, we throw out from the old declarations in fn the :mode specification and anything that is specified in the new dcls. For example, suppose that fact was introduced with: (defun fact (n) (declare (type integer n) (xargs :mode :program)) (if (zp n) 1 (* n (fact (1- n))))). Suppose later we do (verify-termination fact). Then the following definition is submitted. (defun fact (n) (declare (type integer n)) (if (zp n) 1 (* n (fact (1- n))))). Observe that this is the same definition as the original one, except the old specification of the :mode has been deleted so that the defun-mode now defaults to :logic. Although the termination proof succeeds, ACL2 also tries to verify the guard, because we have (implicitly) provided a guard, namely (integerp n), for this function. (See *note GUARD:: for a general discussion of guards, and see *note TYPE-SPEC:: for a discussion of how type declarations are used in guards.) Unfortunately, the guard verification fails, because the subterm (zp n) requires that n be nonnegative, as can be seen by invoking :args zp. (For a discussion of termination issues relating to recursion on the naturals, see *note ZERO-TEST-IDIOMS::.) So we might be tempted to submit the following: (verify-termination fact (declare (xargs :guard (and (integerp n) (<= 0 n))))). However, this is considered a changing of the guard (from (integerp n)), which is illegal. If we instead change the guard in the earlier defun after undoing that earlier definition with :ubt fact, then (verify-termination fact) will succeed. *Remark on system functions.* There may be times when you want to apply verify-termination (and also, perhaps, verify-guards) to functions that are predefined in ACL2. It may be necessary in such cases to modify the system code first. See Part II of `http://www.cs.utexas.edu/users/moore/acl2/open-architecture/' for a discussion of the process for contributing updates to the system code and books with such verify-termination or verify-guards events, perhaps resulting in more system functions being built-in as guard-verified. To see which built-in functions have already received such treatment, see community books directory books/system/; or, evaluate the constant *system-verify-guards-alist*, each of whose entries associates the name of a community book with a list of functions whose guard-verification is proved by including that book. See the above URL for more details. Note that if fn1 is already in :logic mode, then the verify-termination call has no effect. It is generally considered to be redundant, in the sense that it returns without error; but if the fn1 is a constrained function (i.e., introduced in the signature of an encapsulate, or by defchoose), then an error occurs. This error is intended to highlight unintended uses of verify-termination; but if you do not want to see an error in this case, you can write and use your own macro in place of verify-termination. The following explanation of the implementation of verify-termination may help with such a task. We conclude with a discussion of the use of make-event to implement verify-termination. This discussion can be skipped; we include it only for those who want to create variants of verify-termination, or who are interested in seeing an application of make-event. Consider the following proof of nil, which succeeded up through Version_3.4 of ACL2. (encapsulate () (defun foo (x y) (declare (xargs :mode :program)) (if (or (zp x) (zp y)) (list x y) (foo (1+ x) (1- y)))) (local (defun foo (x y) (declare (xargs :measure (acl2-count y))) (if (or (zp x) (zp y)) (list x y) (foo (1+ x) (1- y))))) (verify-termination foo)) (defthm bad-lemma (zp x) :hints (("Goal" :induct (foo x 1))) :rule-classes nil) How did this work? In the first pass of the encapsulate, the second defun of foo promoted foo from :program to :logic mode, with y as the unique measured variable. The following call to verify-termination was then redundant. However, on the second pass of the encapsulate, the second (local) definition of foo was skipped, and the verify-termination event then used the first definition of foo to guess the measure, based (as with all guesses of measures) on a purely syntactic criterion. ACL2 incorrectly chose (acl2-count x) as the measure, installing x as the unique measured variable, which in turn led to an unsound induction scheme subsequently used to prove nil (lemma bad-lemma, above) Now, verify-termination is a macro whose calls expand to make-event calls. So in the first pass above, the verify-termination call generated a defun event identical to the local defun of foo, which was correctly identified as redundant. That expansion was recorded, and on the second pass of the encapsulate, the expansion was recalled and used in place of the verify-termination call (that is how make-event works). So instead of a measure being guessed for the verify-termination call on the second pass, the same measure was used as was used on the first pass, and a sound induction scheme was stored. The attempt to prove nil (lemma bad-lemma) then failed.  File: acl2-doc-emacs.info, Node: FORWARD-CHAINING-REPORTS, Next: HISTORY, Prev: EVENTS, Up: Top FORWARD-CHAINING-REPORTS to see reports about the forward chaining process Debugging forward-chaining rules can be hard because their effects are not directly visible on the goal. In this documentation we tell you how to get reports on the forward chaining activity occurring in your proof attempts. This documentation is written in several parts. The first part is an introduction for the first-time user of forward chaining reports. The next two parts describe how to read reports. The last part describes how to monitor forward chaining activity only for selected runes, etc. We recommend the new user of these reports read everything! * Menu: * FC-REPORT:: to report on the forward chaining activity in the most recent proof * RESET-FC-REPORTING:: reset the forward-chaining tracking state to its initial configuration * SET-FC-CRITERIA:: to set the tracking criteria for forward chaining reports * SET-FC-REPORT-ON-THE-FLY:: to determine when forward-chaining reports are printed * SHOW-FC-CRITERIA:: print the forward-chaining tracking criteria _A Quick Introduction to Forward Chaining Reports_ Caution: The reporting mechanism maintains some state and if you have already used forward chaining reporting in a session the directions below may not work as advertised! To return to the default forward chaining reporting state, execute this form at the top level: (reset-fc-reporting) You can get a report about all forward chaining activity in subsequent proofs by doing: (set-fc-criteria t) Options will be discussed later that allow you to monitor the activity caused by particular :forward-chaining rules or terms. Then do a proof that is expected to cause some forward chaining. In the proof output you will see lines like this: (Forward Chaining on behalf of PREPROCESS-CLAUSE: (FC-Report 1)) This is the only difference you should see in the proof output. After the proof attempt has terminated, you can execute: (fc-report k) for any k printed during the immediately preceding proof attempt. That will print a much longer report describing the activity that occurred during the kth use of forward chaining in that proof attempt. If you want to see these reports in real time (embedded in the proof output), do this before invoking the prover: (set-fc-report-on-the-fly t) Collecting the data used to generate these reports slows down the prover. If you no longer wish to see such reports, do (set-fc-criteria nil) _How To Read FC Reports_ The report printed by (fc-report k) is of the form: Forward Chaining Report _k_: Caller: _token_ Clause: (_lit1_ _lit2_ ... _litn_) Number of Rounds: _m_ Contradictionp: _bool_ Activations: (_act1_ _act2_ ...) This report means that the _k_th use of forward chaining in the most recent proof attempt was done on behalf of _token_ (see below). The initial context (set of assumptions) consisted of the negations of the literals listed in the clause shown and the initial candidate trigger terms are all those appearing in that clause. This invocation of forward chaining proceeded to do _m_ rounds of successive extensions of the initial context and ultimately either reached a contradiction (_bool_ = T) or returned an extended context (_bool_ = NIL). Note that reaching a contradiction from the negations of all the literals in a clause is "good" because it means the clause is true. The report concludes with the final status of all the forward chaining rules fired during the process. We explain how to read one of these activation reports in the next section. Forward chaining is done on behalf of many proof techniques in the system. Each is associated with a _token_. The main proof technique that uses forward chaining is SIMPLIFY-CLAUSE. This is the call of forward chaining that sets up the context used by the rewriter to relieve hypotheses during backchaining. Another common caller of forward chaining is PREPROCESS-CLAUSE, the first process in the ACL2 waterfall (see *note HINTS-AND-THE-WATERFALL::). Forward chaining often proves "near propositional" goals (those depending just on boolean implications between basic predicates). Other tokens you may see include INDUCT, which uses forward chaining to set up a context for applying :induction rules, and the definitional principle (and related utilities such as verify-termination and verify-guards) which uses forward chaining during the construction of both measure conjectures and guard conjectures. When used this way, the _token_ is defun-or-guard-verification. _How to Read Activation Reports_ The forward chaining report concludes with a list of activation reports. Activations: (_act1_ _act2_ ...) Each _acti_ is of the form: (_rune_ (:TRIGGER _inst-trig_) ((:UNIFY-SUBST _subst_) (:DISPOSITION _outcome-part1_ _outcome-part2_ _inst-term_)) ...) where the ... indicates that the rest of the report consists of more of those tuples listing a :UNIFY-SUBST and :DISPOSITION. We call each tuple a _disposition_ of the activation and each disposition describes a substitution _subst_ identifying the final instantiation of the rule and how the activation fared. Suppose there are _n_ dispositions. (If the rule in question contains no free variables, _n_ will be 1.) This activation report means that during the forward chaining process in question, the :forward-chaining rune _rune_ was fired due to the presence in the evolving context of the trigger term _inst-trig_. (Note that _inst-trig_ is an instantiation of the trigger term of the named rule. That is, the variable symbols you see in _inst-trig_ are those of the clause printed in the forward chaining report.) The activation of _rune_ by _inst-trig_ proceeded to split _n_ ways as different choices were made for the free-variables occuring among the hypotheses. Each of those _n_ choices gave rise to a different substitution _subst_, and each succeeded or failed as described by the corresponding :DISPOSITION. The :DISPOSITION of an activation is described in three parts, _outcome-part1_, _outcome-part2_, and _inst-term_. _Outcome-part1_ is either SUCCESS or BLOCKED, meaning that the instance given by _subst_ either succeeded in the sense that all of its instantiated hypotheses were found in the context, or failed because some instantiated hypothesis was not found. If outcome-part1 is SUCCESS then _inst-term_ is the instantiated conclusion produced by the rule. _Outcome-part2_ is APPROVED if the instantiated conclusion was acceptable to our heuristics designed to prevent looping and not already known in the evolving context. _Outcome-part2_ is REJECTED if the instantiated conclusion was not approved by our heuristics. _Outcome-part2_ is REDUNDANT if the instantiated conclusion was approved by the heuristics but already known true in the current evolving context. If APPROVED, the truth of the instantiated conclusion is added to the evolving context. Otherwise, it is not. If outcome-part1 is BLOCKED then outcome-part2 is one of three possible things: FALSE, in which case _inst-term_ is an instantiated hypothesis of the rule that is assumed false in the current context, UNRELIEVED-HYP, in which case _inst-term_ is an instantiated hypothesis whose truthvalue is not determined by the context, or UNRELIEVED-HYP-FREE, in which case _inst-term_ is an oddly instantiated hypothesis whose truthvalue is not determined by the context and which also contains free variables. In the last case, the "odd" instantiation was by the substitution _subst_ but extended so that free variables in the hypothesis are renamed to start with the prefix UNBOUND-FREE- to draw your attention to them. Note: All of the terms printed in the report are instantiated with the relevant unifying substitution (possibly extended to bind free variables). _Specifying the Tracking Criteria_ During a proof attempt, the forward chaining module stores information about the activations satisfying certain criteria. The _criteria_ is a list of triples. Each triple consists of a forward chaining rune, an instantiated trigger term, and an instantiated conclusion to watch for. However, any or all of the components of such a triple may be t and that is given special significance. An activation satisfies a criteria if it satisfies at least one of the triples. An activation satisfies a triple if it satisfies all three of the components. Every activation satisfies the component t. An activation satisfies a rune if the activation describes a firing of the named rule. An activation satisfies an instantiated trigger term if the activation was created by that trigger being present in the context. An activation satisfies an instantiated conclusion if the activation _could_ produce the instantiated conclusion (with the right choice of any free variables). Thus, the criteria is interpreted as a disjunction of conjunctions, making it possible to track a specific set of runes, triggers, and conclusions. For example, here is a triple that might appear in the criteria: ((:FORWARD-CHAINING ALISTP-FORWARD-TO-TRUE-LISTP) t t). This triple would cause every activation of the given rule to be tracked. However, the triple ((:FORWARD-CHAINING ALISTP-FORWARD-TO-TRUE-LISTP) (ALISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S)))) t) would only track activations of that rule fired by the specific term shown as the second element of the triple. Futhermore (t (ALISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S)))) t) would track any forward chaining rule triggered by that term, and (t t (TRUE-LISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S))))) would track any rule fired by any trigger that might lead to the specific term given as the third component above. Note: The condition on how an activation satisfies an instantiated conclusion is a little subtle. Consider the activation of the forward chaining rule (implies (and (symbol-listp x) (equal (len x) (len y))) (true-listp (make-bindings x y))) triggered by (SYMBOL-LISTP VARS) arising in the current context. This activation _could_ produce the specific conclusion shown in the last triple above, if it just happened that (TOP-N (OP1 INST) (STACK S)) were chosen as the binding of the free variable y. Thus, the activation of this rule triggered by (SYMBOL-LISTP VARS) satisfies the last triple above. Observe that the triple (t t t) is satisfied by every activation of any rule by any trigger term producing any conclusion. The function set-fc-criteria sets the criteria describing which activations are to be tracked. For example, if you execute: (set-fc-criteria ((:FORWARD-CHAINING LEMMA1) t t) ((:FORWARD-CHAINING LEMMA2) (ALISTP (BASIC-MAPPER A B)) t) (t t (TRUE-LISTP (DLT D)))), the system would track all activations of the forward-chaining rule LEMMA1, plus those activations of forward-chaining rule LEMMA2 triggered by the term given in the second triple, plus any activation of any rule that might derive (TRUE-LISTP (DLT D)). Because criteria generally mention variable symbols used in a specific conjecture, it is probably best to reconsider your criteria every time you want to track forward chaining. If the criteria is nil, then nothing is tracked. Setting the criteria to nil is the way you turn off tracking and reporting of forward chaining activity. You may do this either by (set-fc-criteria) or by (set-fc-criteria nil). (Technically the second form is an odd use of set-fc-criteria, which expects any supplied arguments to be triples; if the "triple" nil is the only one supplied, we take it to mean that the entire criteria should be nil.) To track every forward chaining activation you may set the criteria with either (set-fc-criteria (t t t)) or use the abbreviation (set-fc-criteria t). If, when you read a forward chaining report, you see no mention of an activation you have in mind, e.g., of a certain rune or deriving a certain conclusion, and you have set the criteria correctly, then the activation never happened. (This is akin to using :brr and :monitor to monitor the application of a rewrite rule and then seeing no interactive break.) For some relevant functions to help you manage criteria and when the full reports are printed see fc-report, show-fc-criteria, set-fc-criteria, reset-fc-reporting, and set-fc-report-on-the-fly.  File: acl2-doc-emacs.info, Node: FC-REPORT, Next: RESET-FC-REPORTING, Prev: FORWARD-CHAINING-REPORTS, Up: FORWARD-CHAINING-REPORTS FC-REPORT to report on the forward chaining activity in the most recent proof Example: (fc-report 15) General Form: (fc-report k) where k is the number of some forward chaining report printed in the most recent event. See *note FORWARD-CHAINING-REPORTS:: for details.  File: acl2-doc-emacs.info, Node: RESET-FC-REPORTING, Next: SET-FC-CRITERIA, Prev: FC-REPORT, Up: FORWARD-CHAINING-REPORTS RESET-FC-REPORTING reset the forward-chaining tracking state to its initial configuration Example: (reset-fc-reporting) This function erases all forward chaining tracking criteria and sets the on-the-fly reporting flag to nil. The next time you set the criteria (see *note SET-FC-CRITERIA::) the short form of reports, in which only the caller and the fc-report number is printed, will appear in your proof logs. See *note FORWARD-CHAINING-REPORTS:: for details.  File: acl2-doc-emacs.info, Node: SET-FC-CRITERIA, Next: SET-FC-REPORT-ON-THE-FLY, Prev: RESET-FC-REPORTING, Up: FORWARD-CHAINING-REPORTS SET-FC-CRITERIA to set the tracking criteria for forward chaining reports Examples: (set-fc-criteria) ; shut off all tracking (set-fc-criteria nil) (set-fc-criteria t) ; to track all forward chaining (set-fc-criteria (t t t)) (set-fc-criteria ((:FORWARD-CHAINING LEMMA1) ; track all uses of LEMMA1, t t) ((:FORWARD-CHAINING LEMMA2) ; uses of LEMMA2 triggered (ALISTP (BASIC-MAPPER A B)) ; by this specific ALISTP term t) (t t (TRUE-LISTP (DLT D)))) ; and every rule deriving ; this TRUE-LISTP term. General Forms: (set-fc-criteria nil) (set-fc-criteria t) (set-fc-criteria triple1 triple2 ...) where each triple is of the form (rune inst-trigger inst-concl). If rune is non-t is must be a forward chaining rune. The other two components, inst-trigger and inst-concl, if non-t, must be terms. The list of all the triples supplied is called the "criteria." In the form (set-fc-criteria nil), the criteria used is the empty list of triples. (Technically, supplying nil as a triple "ought" to be an error, but it is a more "natural" way to say the list of criteria is empty than to use the correct form (set-fc-criteria).) In the form (set-fc-criteria t) the criteria used is the list containing the single triple (t t t). This function sets the tracking criteria for forward chaining reporting. See *note FORWARD-CHAINING-REPORTS:: for a general discussion of tracking and reporting forward chaining activity. The forward chaining criteria is a list of triples. Think of it as representing a disjunction of conjunctions. The activation of a :forward-chaining rune by some triggering term in the current context _satisfies_ the criteria if it satisfies one of the triples. To satisfy the triple (rune inst-trigger inst-concl), the activation must satisfy each component of the triple. Any t component is always satisfied. If rune is non-t it is satisfied if the activation is for the given rune. If inst-trigger is non-t, it is satisfied if the activation is triggered by the given term. ("Inst-trigger" stands for "instantiated trigger." It is not the trigger term of the rule but is supposed to be an instance of that term that you believe will arise in some proof attempt you are debugging - an instance you want to "watch" as it fires the rule.) If inst-concl is non-t, it is satisfied if the activation could possibly derive the conclusion given. (Again, "inst-concl" stands for "instantiated conclusion" and shows the term in your problem that you expect to be derived by forward chaining.) Note that if the criteria is empty, it is never satisfied, so tracking is turned off. If the criteria is the singleton list containing just the triple (t t t), then every activation satisfies it and so all :forward chaining rules are tracked. See *note FORWARD-CHAINING-REPORTS:: for details.  File: acl2-doc-emacs.info, Node: SET-FC-REPORT-ON-THE-FLY, Next: SHOW-FC-CRITERIA, Prev: SET-FC-CRITERIA, Up: FORWARD-CHAINING-REPORTS SET-FC-REPORT-ON-THE-FLY to determine when forward-chaining reports are printed Examples: (set-fc-report-on-the-fly t) (set-fc-report-on-the-fly nil) If the flag is set to t, forward chaining tracking reports are printed when forward chaining occurs. If the flag is set to nil, very short reports (giving just the caller and the report number) are printed during forward chaining but you can use fc-report to see the full report afterwards. Since nothing is tracked when the criteria is nil, this function also prints out the current criteria to remind you of what it is. The flag manipulated by this function does not shut off tracking. It only determines whether reports are printed on-the-fly or not. To shut off tracking set-fc-criteria. See *note FORWARD-CHAINING-REPORTS:: for details.  File: acl2-doc-emacs.info, Node: SHOW-FC-CRITERIA, Prev: SET-FC-REPORT-ON-THE-FLY, Up: FORWARD-CHAINING-REPORTS SHOW-FC-CRITERIA print the forward-chaining tracking criteria Example: (show-fc-criteria) This function prints the list of triples being used to determine what is tracked during forward chaining. See *note FORWARD-CHAINING-REPORTS:: for details.  File: acl2-doc-emacs.info, Node: HISTORY, Next: HONS-AND-MEMOIZATION, Prev: FORWARD-CHAINING-REPORTS, Up: Top HISTORY functions that display or change history * Menu: * GCS:: See *note GET-COMMAND-SEQUENCE::. * GET-COMMAND-SEQUENCE:: return list of commands that are between two command descriptors * OOPS:: undo a :u or :ubt * PBT:: print the commands back through a command descriptor * PC:: print the command described by a command descriptor * PCB:: print the command block described by a command descriptor * PCB!:: print in full the command block described by a command descriptor * PCS:: print the sequence of commands between two command descriptors * PE:: print the events named by a logical name * PE!:: deprecated (see *note PE::) * PF:: print the formula corresponding to the given name * PL:: print the rules for the given name or term * PL2:: print rule(s) for the given form * PR:: print the rules stored by the event with the given name * PR!:: print rules stored by the command with a given command descriptor * PUFF:: replace a compound command by its immediate subevents * PUFF*:: replace a compound command by its subevents * RESET-KILL-RING:: save memory by resetting and perhaps resizing the kill ring used by oops * TAU-DATA:: to see what tau knows about a function symbol * TAU-DATABASE:: to see the tau database as a (very large) object * U:: undo last command, without a query * UBT:: undo the commands back through a command descriptor * UBT!:: undo commands, without a query or an error * UBT-PREHISTORY:: undo the commands back through the last reset-prehistory event * UBU:: undo the commands back up to (not including) a command descriptor * UBU!:: undo commands, without a query or an error ACL2 keeps track of the commands that you have executed that have extended the logic or the rule database, as by the definition of macros, functions, etc. Using the facilities in this section you can review the sequence of commands executed so far. For example, you can ask to see the most recently executed command, or the command 10 before that, or the command that introduced a given function symbol. You can also undo back through some previous command, restoring the logical world to what it was before the given command. The annotations printed in the margin in response to some of these commands (such as `P', `L', and `V') are explained in the documentation for :pc. Several technical terms are used in the documentation of the history commands. You must understand these terms to use the commands. These terms are documented via :doc entries of their own. See *note COMMAND::, see *note EVENTS::, see *note COMMAND-DESCRIPTOR::, and see *note LOGICAL-NAME::.  File: acl2-doc-emacs.info, Node: GCS, Next: GET-COMMAND-SEQUENCE, Prev: HISTORY, Up: HISTORY GCS See *note GET-COMMAND-SEQUENCE::.  File: acl2-doc-emacs.info, Node: GET-COMMAND-SEQUENCE, Next: OOPS, Prev: GCS, Up: HISTORY GET-COMMAND-SEQUENCE return list of commands that are between two command descriptors Examples: (get-command-sequence 4 12) :gcs 4 12 ; same as above (get-command-sequence 4 :x) :gcs 4 :x ; same as above See *note PCS:: for a utility that prints abbreviated information about the commands that are between two command descriptors. The utility get-command-sequence -- or simply gcs, so that you can just type :gcs at the prompt -- has the same syntax but instead of printing, it simply returns the corresponding list of commands. More precisely, it returns an error triple (mv erp val state) (see *note ERROR-TRIPLES::) such that if erp is not nil, then val is the desired list of commands.  File: acl2-doc-emacs.info, Node: OOPS, Next: PBT, Prev: GET-COMMAND-SEQUENCE, Up: HISTORY OOPS undo a :u or :ubt The keyword command :oops will undo the most recent :ubt (or :u, which we here consider just another :ubt). A second :oops will undo the next most recent :ubt, a third will undo the :ubt before that one, and a fourth :oops will return the logical world to its configuration before the first :oops. Consider the logical world (see *note WORLD::) that represents the current extension of the logic and ACL2's rules for dealing with it. The :ubt and :u commands "roll back" to some previous world (see *note UBT::). Sometimes these commands are used to inadvertently undo useful work and user's wish they could "undo the last undo." That is the function provided by :oops. :Oops is best described in terms of an implementation. Imagine a ring of four worlds and a marker (*) indicating the current ACL2 world: * w0 / \ w3 w1 \ / w2 This is called the "kill ring" and it is maintained as follows. When you execute an event the current world is extended and the kill ring is not otherwise affected. When you execute :ubt or :u, the current world marker is moved one step counterclockwise and that world in the ring is replaced by the result, say w0', of the :ubt or :u. w0 / \ *w0' w1 \ / w2 If you were to execute events at this point, w0' would be extended and no other changes would occur in the kill ring. When you execute :oops, the marker is moved one step clockwise. Thus the kill ring becomes * w0 / \ w0' w1 \ / w2 and the current ACL2 world is w0 once again. That is, :oops "undoes" the :ubt that produced w0' from w0. Similarly, a second :oops will move the marker to w1, undoing the undo that produced w0 from w1. A third :oops makes w2 the current world. Note however that a fourth :oops restores us to the configuration previously displayed above in which w0' has the marker. In general, the kill ring contains the current world and the three most recent worlds in which a :ubt or :u were done. While :ubt may appear to discard the information in the events undone, we can see that the world in which the :ubt occurred is still available. No information has been lost about that world. But :ubt does discard information! :Ubt discards the information necessary to recover from the third most recent ubt! An :oops, on the other hand, discards no information, it just selects the next available world on the kill ring and doing enough :oopses will return you to your starting point. We can put this another way. You can freely type :oops and inspect the world that you thus obtain with :pe, :pc, and other history commands. You can repeat this as often as you wish without risking the permanent loss of any information. But you must be more careful typing :ubt or :u. While :oops makes :ubt seem "safe" because the most recent :ubt can always be undone, information is lost when you execute :ubt. We note that :ubt and :u may remove compiled definitions (but note that in some Lisps, including CCL (OpenMCL) and SBCL, functions are always compiled). When the original world is restored using :oops, restored functions will not generally be compiled (except for Lisps as above), though the user can remedy this situation; see *note COMP::. Finally, we note that our implementation of oops can use a significant amount of memory, because of the saving of old logical worlds. Most users are unlikely to experience a memory problem, but if you do, then you may want to disable oops by evaluting (reset-kill-ring 0 state); see *note RESET-KILL-RING::.  File: acl2-doc-emacs.info, Node: PBT, Next: PC, Prev: OOPS, Up: HISTORY PBT print the commands back through a command descriptor Examples: :pbt :max ; print back through the most recent command :pbt :x ; print back through the most recent command :pbt fn ; print back through the introduction of fn :pbt 5 ; print back through the fifth command executed :pbt (:x -4) ; print back through the most recent five commands See *note COMMAND-DESCRIPTOR::. Pbt takes one argument, a command descriptor, and prints the commands from :max (aka :x) through the one described. See *note COMMAND-DESCRIPTOR:: for a description of what a command descriptor is. See *note PC:: for a description of the format used to display commands. Pbt will print the commands that ubt will undo.  File: acl2-doc-emacs.info, Node: PC, Next: PCB, Prev: PBT, Up: HISTORY PC print the command described by a command descriptor Examples: :pc 3 ; print the third command executed :pc :max ; print the most recent command :pc :x ; print the most recent command :pc fn ; print the command that introduced fn See *note COMMAND-DESCRIPTOR::. Pc takes one argument, a command descriptor, and prints the command identified by that descriptor. See *note COMMAND-DESCRIPTOR::. For example ACL2 !>:pc foo LVd 52 (DEFUN FOO (X) X) Pc always prints a space first, followed by three (possibly blank) characters ("LVd" above) explained below (four, in the experimental HONS version, as discussed further below). Then pc prints the command number, a number uniquely identifying the command's position in the sequence of commands since the beginning of the user's session. Finally, the command itself is printed. While pc always prints a space first, some history commands, for example :pcs and :pe, use the first column of output to delimit a region of commands or to point to a particular event within a command. For example, :pcs 52 54 will print something like /LVd 52 (DEFUN FOO (X) X) LV 53 (DEFUN BAR (X) (CONS X X)) \ 54 (DEFTHM FOO-BAR (EQUAL (CAR (BAR X)) (FOO X))) : ... 127 (DEFUN LATEST (X) X) Here, the two slash characters in the first column are intended to suggest a bracket delimiting commands 52 through 54. The last command printed by pcs is always the most recent command, i.e., the command at :here, and is separated from the rest of the display by an elipsis if some commands are omitted. Similarly, the :pe command will print a particular event within a command block and will indicate that event by printing a ">" in the first column. The symbol is intended to be an arrow pointing at the event in question. For example, :pe true-listp-app might print: 1 (INCLUDE-BOOK "list-book") \ > (DEFTHM TRUE-LISTP-APP (EQUAL (TRUE-LISTP (APP A B)) (TRUE-LISTP B))) using the arrow to indicate the event itself. The slash printed to connect the command, include-book, with the event, defthm, is intended to suggest a tree branch indicating that the event is inferior to (and part of) the command. The mysterious characters sometimes preceding a command have the following interpretations. The first two have to do with the function symbols introduced by the command and are blank if no symbols were introduced. At any time we can classify our function symbols into disjoint sets, which we will here name with characters. The "P" functions are those in :program mode. The "L" functions are those in :logic mode whose guards have not been verified. The "V" functions are those in :logic mode whose guards have been verified. You may also see the use of (lower-case) "v" to indicate functions introduced by encapsulate. Note that verify-termination and verify-guards cause function symbols to be reclassified. If a command introduces function symbols then the first mysterious character indicates the class of the symbols at the time of introduction and the second character indicates the current class of the symbols (if the current class is different from the introductory class). Thus, the display PLd 52 (DEFUN FOO (X) X) tells us that command 52 introduced a :program function but that some command after 52 changed its mode to :logic and that the guards of foo have not been verified. That is, foo's termination has been verified even though it was not verified as part of the command that introduced foo. Had a subsequent command verified the guards of foo, the display would contain a V where the L is. The display P d 52 (DEFUN FOO (X) X) indicates that foo was introduced in :program mode and still is in that mode. The third character indicates the enabled/disabled status of the runes introduced by the command. If the status character is blank then all the runes (if any) introduced are enabled. If the status character is "D" then some runes were introduced and they are all disabled. If the status character is "d" then at least one, but not all, of the runes introduced is disabled. Thus, in the display L d 52 (DEFUN FOO (X) X) we see that some rune introduced by command 52 is disabled. As noted in the documentation for rune, a defun command introduces many runes, e.g., the axiomatic definition rule, (:definition fn), the executable counterpart rule, (:executable-counterpart fn), and type-prescriptions, (:type-prescription fn). The display above does not say which of the runes based on foo is disabled, but it does tell us one of them is; see *note DISABLEDP:: for how to obtain the disabled runes for a given function symbol. Finally, for the experimental HONS version only (see *note HONS-AND-MEMOIZATION::), a fourth character is printed, indicating whether functions are memoized. A symbol may be memoized if it is a function symbol that is not constrained (i.e., introduced by defchoose or in the signature of an encapsulate event). If the command introduces no symbol that may be memoized, then a space is printed. Otherwise, if every memoizable symbol is memoized, an "M" is printed. Otherwise, an "m" is printed.  File: acl2-doc-emacs.info, Node: PCB, Next: PCB!, Prev: PC, Up: HISTORY PCB print the command block described by a command descriptor Examples: :pcb :max ; print the most recent command block :pcb :x ; print the most recent command block :pcb fn ; print the command block that introduced fn :pcb 5 ; print the fifth command block See *note COMMAND-DESCRIPTOR::. Pcb takes one argument, a command descriptor, and prints the command block of the command described. See *note COMMAND-DESCRIPTOR:: for details of command descriptors. See *note PC:: for description of the format in which commands are displayed. The command block of a command consists of the command itself and all of the events it created. If the command created a single event and that event is in fact the command (i.e., if the command typed was just an event such as a defun or defthm rather than a macro that expanded to some event forms), then pcb just prints the command. Pcb sketches command and all of the events it created, rather than printing them fully. If you wish to see just the command, in its entirety, use pc. If you wish to see one of the events within the block, in its entirety, use pe. If you wish to see the command sketched and all of the events it created, in their entirety, use pcb!.  File: acl2-doc-emacs.info, Node: PCB!, Next: PCS, Prev: PCB, Up: HISTORY PCB! print in full the command block described by a command descriptor Examples: :pcb! :max ; print the most recent command block :pcb! :x ; print the most recent command block :pcb! fn ; print the command block that introduced fn :pcb! 5 ; print the fifth command block See *note COMMAND-DESCRIPTOR::. Pcb! takes one argument, a command descriptor, and prints the command block of the command described. Unlike pcb, pcb! prints the event forms in full; see *note PCB:: for details.  File: acl2-doc-emacs.info, Node: PCS, Next: PE, Prev: PCB!, Up: HISTORY PCS print the sequence of commands between two command descriptors Examples: :pcs 1 5 ; print commands 1 through 5 :pcs 5 1 ; same as above :pcs :x (:x -3) ; print the 3 most recently executed commands :pcs fn assoc-of-fn ; print the commands between the one that introduced ; fn and the one that introduced assoc-of-fn Pcs takes two arguments, both of which are command descriptors, and prints the commands between them with pc. The order of the two descriptors is irrelevant. See *note COMMAND-DESCRIPTOR:: for a description of command descriptors. See *note PC:: for a description of the format in which commands are displayed. For a related utility that simply returns a sequence of commands, see *note GET-COMMAND-SEQUENCE::.  File: acl2-doc-emacs.info, Node: PE, Next: PE!, Prev: PCS, Up: HISTORY PE print the events named by a logical name Example: :pe fn ; sketches the command that introduced fn and ; prints in full the event within it that created fn. See *note LOGICAL-NAME::. Pe takes one argument, a logical name, and prints in full the event corresponding to the name. Pe also sketches the command responsible for that event if the command is different from the event itself. See *note PC:: for a description of the format used to display a command. To remind you that the event is inferior to the command, i.e., you can only undo the entire command, not just the event, the event is indented slightly from the command and a slash (meant to suggest a tree branch) connects them. If the given logical name corresponds to more than one event, then :pe will print the above information for every such event. Here is an example of such behavior. ACL2 !>:pe nth -4270 (ENCAPSULATE NIL ...) \ >V (VERIFY-TERMINATION NTH) Additional events for the logical name NTH: PV -4949 (DEFUN NTH (N L) "Documentation available via :doc" (DECLARE (XARGS :GUARD (AND (INTEGERP N) (>= N 0) (TRUE-LISTP L)))) (IF (ENDP L) NIL (IF (ZP N) (CAR L) (NTH (- N 1) (CDR L))))) ACL2 !> If you prefer to see only the formula for the given name, for example if it is part of a large mutual-recursion, see *note PF::.  File: acl2-doc-emacs.info, Node: PE!, Next: PF, Prev: PE, Up: HISTORY PE! deprecated (see *note PE::) Please see *note PE::. :Pe now has the functionality formerly provided by :pe!.  File: acl2-doc-emacs.info, Node: PF, Next: PL, Prev: PE!, Up: HISTORY PF print the formula corresponding to the given name Examples: :pf (:definition fn) ; prints the definition of fn as an equality :pf fn ; same as above :pf (:rewrite foo) ; prints the statement of the rewrite rule foo :pf foo ; same as above pf takes one argument, an event name or a rune, and prints the formula associated with name. If the argument is the name of a macro associated with a function name by macro-aliases-table, then the function name is used as the argument.  File: acl2-doc-emacs.info, Node: PL, Next: PL2, Prev: PF, Up: HISTORY PL print the rules for the given name or term Examples: :pl foo ; prints rules that rewrite some call of foo :pl (+ x y) ; prints rules that rewrite (+ x y) Also see *note PL2::, which restricts output to rules that you specify for a given term. Pl takes one argument, which should be a symbol or a term. First suppose that the argument is a symbol. Then it should be either a function symbol or else a macro alias for a function symbol (see *note MACRO-ALIASES-TABLE::), which is treated as the corresponding function symbol. In this case :pl displays rules that apply to terms whose top function symbol is the one specified, specifically, rules of class :rewrite, :definition, :meta, :linear, :type-prescription, :forward-chaining, :elim, and :induction. Otherwise the argument should be a term (in user syntax, so that for example macros are permitted). In this case, :pl displays the :rewrite, :definition, and :meta rules that rewrite the specified term, followed by the applicable :type-prescription rules. Each rule is displayed with additional information, such as the hypotheses that remain after applying some simple techniques to discharge them that are likely to apply in any context. Note that for :meta rules, only those are displayed that meet two conditions: the application of the metafunction returns a term different from the input term, and if there is a hypothesis metafunction then it also returns a term. (A subtlety: In the case of extended metafunctions (see *note EXTENDED-METAFUNCTIONS::), a trivial metafunction context is used for the application of the metafunction.) Note that some rule classes are not handled by :pl. In particular, if you want to see all :clause-processor rules, issue the command :print-clause-processor-rules, and for trusted clause-processors, (table trusted-clause-processor-table); see *note CLAUSE-PROCESSOR:: and see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR::.  File: acl2-doc-emacs.info, Node: PL2, Next: PR, Prev: PL, Up: HISTORY PL2 print rule(s) for the given form Examples: :pl2 (+ x y) nil ; prints rules that apply to (+ x y) :pl2 (+ x y) foo ; prints rules named foo that apply to (+ x y) :pl2 (+ x y) (:rewrite foo) ; if the rule with rune (:rewrite foo) applies ; to (+ x y), then print it :pl2 (+ x y) (:type-prescription foo) ; as above, but for the indicated ; type-prescription rule Pl2 takes two arguments. The first is a term. The second is either nil or a "rule-id" that is either a symbol or a rune. The result is to print rules of class :rewrite, :definition, :meta, :linear, and :type-prescription that apply to the given term. Indeed, :pl2 prints exactly what is printed by applying :pl to the first argument -- see *note PL:: -- except that if the second argument is not nil then it is used to filter the rules printed, as follows. If the rule-id is a symbol, then only rules whose name is that symbol will be printed. If the rule-id is a rune, then at most one rule will be printed: the rule named by that rune (if the rule would be printed by :pl).  File: acl2-doc-emacs.info, Node: PR, Next: PR!, Prev: PL2, Up: HISTORY PR print the rules stored by the event with the given name Examples: :pr fn ; prints the rules from the definition of fn (including any ; :type-prescription rule and :definition rule) :pr assoc-append ; if assoc-append is a rewrite rule, prints that rule Also see *note PR!::, which is similar but works at the command level instead of at the event level, and see *note PL::, which displays all rewrite rules for calls of fn, not just those introduced at definition time. Pr takes one argument, a logical name, and prints the rules associated with it. In each case it prints the rune, the current enabled/disabled status, and other appropriate fields from the rule. It may be helpful to read the documentation for various kinds of rules in order to understand the information printed by this command. For example, the information printed for a linear rule might be: Rune: (:LINEAR ABC) Enabled: T Hyps: ((CONSP X)) Concl: (< (ACL2-COUNT (CAR X)) (ACL2-COUNT X)) Max-term: (ACL2-COUNT (CAR X)) Backchain-limit-lst: (3) The hyps and concl fields for this rule are fairly self-explanatory, but it is useful to see *note LINEAR:: to learn about maximal terms (which, as one might guess, are stored under "Max-term"). Currently, this function does not print congruence rules or equivalence rules. The expert user might also wish to use find-rules-of-rune. See *note FIND-RULES-OF-RUNE::.  File: acl2-doc-emacs.info, Node: PR!, Next: PUFF, Prev: PR, Up: HISTORY PR! print rules stored by the command with a given command descriptor Examples: :pr! fn ; prints the rules from the definition of fn (including any ; :type-prescription rule and :definition rule), as well as all other ; rules created by the command that created by fn (which could be ; many rules if, for example, fn was defined by an include-book ; command). :pr! :max ; prints all the rules stored by the most recent command Also see *note PR::, which is similar but works at the event level instead of at the command level. Pr takes one argument, a command descriptor, and prints the rules created by the corresponding event. In each case it prints the rune, the current enabled/disabled status, and other appropriate fields from the rule. See *note PR:: for further details.  File: acl2-doc-emacs.info, Node: PUFF, Next: PUFF*, Prev: PR!, Up: HISTORY PUFF replace a compound command by its immediate subevents Example Forms: ACL2 !>:puff :max ACL2 !>:puff :x ACL2 !>:puff 15 ACL2 !>:puff "book" General Form: :puff cd where cd is a command descriptor (see *note COMMAND-DESCRIPTOR::) for a "puffable" command (see below). Puff replaces the command at cd by the immediate subevents of the command, executed as commands. Puff then prints, using pcs, the puffed region. We consider puff to be a sort of hack; it is generally robust and sound, but that is not guaranteed. If any existing ACL2 event resulted from puff, ACL2 considers proofs to have been skipped, and thus certify-book is disallowed until such events have been undone (see *note UBT::). A "puffable" command is an encapsulate command, an include-book command, or any command other than those consisting of a single primitive event. For example, since defun is a primitive event, a defun command is not puffable. But a macro form that expands into several defun events is puffable. The only primitive events that are puffable are calls of encapsulate or include-book. A puffable command contains (interesting) subevents, namely, the events in the body of the encapsulate, in the file of the book included, or in the command block. (Obscure exceptions. (1) An encapsulate command generated by the macro define-trusted-clause-processor is not puffable. (2) If a use of make-event results in local events in the scope of an encapsulate or include-book event within a command, then an attempt to puff that command will result in an error, leaving the world unchanged, if any such local event was necessary to support other events in the command. (3) An attempt to puff an include-book command may fail for a book that has been modified, as describe late in this documentation topic.) The puff command "lifts" the immediate subevents of the indicated command so that they become commands themselves. The command puff* recursively puffs the newly introduced commands. See *note PUFF*::, which also gives an example illustrating both puff and puff*. Puff undoes the command at cd and replaces it by its immediate subevents. Thus, in general the length of the history grows when a puff command is executed. If puff causes an error (see below), the logical world remains unchanged from its initial configuration. The intended use of puff is to allow the user access to the events "hidden" inside compound commands. For example, while trying to prove some theorem, p, about a constrained function, fn, one might find that the encapsulate, cd, that introduced fn failed to include an important constraint, q. Without puff, the only way to proceed is to undo back through cd, create a suitable encapsulate that proves and exports q as well as the old constraints, re-execute the new encapsulate, re-execute the events since cd, and then try p again. Unfortunately, it may be hard to prove q and additional events may have to be inserted into the encapsulate to prove it. It may also be hard to formulate the "right" q, i.e., one that is provable in the encapsulate and provides the appropriate facts for use in the proof of p. Using puff, the user can erase the encapsulate at cd, replacing it by the events in its body. Now the formerly constrained function, fn, is defined as its witness. The user can experiment with formulations and proofs of q suitable for p. Of course, to get into the ultimately desired state -- where fn is constrained rather than defined and q is exported by an encapsulate at cd -- the user must ultimately undo back to cd and carry out the more tedious program described above. But by using puff it is easier to experiment. Similar applications of puff allow the user of a book to expose the innards of the book as though they had all be typed as commands. The user might then "partially undo" the book, keeping only some of the events in it. Puff operates as follows. First, it determines the list of immediate subevents of the command indicated by cd. It causes an error if there is only one subevent and that subevent is identical to the command -- i.e., if the command at cd is a primitive. Next, puff undoes back through the indicated command. This not only erases the command at cd but all the commands executed after it. Finally, puff re-executes the subevents of (the now erased) cd followed by all the commands that were executed afterwards. Observe that the commands executed after cd will generally have higher command numbers than they did before the puff. For example, suppose 100 commands have been executed and that :puff 80 is then executed. Suppose command 80 contains 5 immediate subevents (i.e., is an encapsulation of five events). Then, after puffing, command 80 is the first event of the puffed command, command 81 is the second, and so on; 104 commands appear to have been executed. When puffing an encapsulate or include-book, the local commands are executed. Note that this will replace constrained functions by their witnesses. Finally, it is impossible to puff in the presence of include-book events for certified books that have been altered since they were included. (Note that this restriction only applies to include-book, not to certify-book.) To be specific, suppose "arith" is a certified book that has been included in a session. Suppose that after "arith" was included, the source file is modified. (This might happen if the user of "arith" is not its author and the author happens to be working on a new version of "arith" during the same time period.) Now suppose the user tries to puff the command that included "arith". The attempt to obtain the subevents in "arith" will discover that the check sum of "arith" has changed and an error will be caused. No change is made in the logical world. A similar error is caused if, in this same situation, the user tries to puff any command that occurred before the inclusion of "arith"! That is, puff may cause an error and leave the world unchanged even if the command puffed is not one involving the modified book. This happens because in order to reconstruct the world after the puffed command, puff must obtain the events in the book and if the book's source file has changed there is no assurance that the reconstructed world is the one the user intends. Warning: We do not detect changes to uncertified books that have been included and are then puffed or re-included! The act of including an uncertified book leaves no trace of the check sum of the book. Furthermore, the act prints a warning message disclaiming soundness. In light of this, :puff quietly "re-"executes the current contents of the book.  File: acl2-doc-emacs.info, Node: PUFF*, Next: RESET-KILL-RING, Prev: PUFF, Up: HISTORY PUFF* replace a compound command by its subevents Example Forms: ACL2 !>:puff* :max ACL2 !>:puff* :x ACL2 !>:puff* 15 ACL2 !>:puff* "book" General Form: :puff* cd where cd is a command descriptor (see *note COMMAND-DESCRIPTOR::) for a "puffable" command. See *note PUFF:: for the definition of "puffable" and for a description of the basic act of "puffing" a command. In particular, see *note PUFF:: for a discussion of a sense in which puff, and hence puff*, should be viewed as a hack. Puff* is just the recursive application of puff. Puff* prints the region puffed, using pcs. To puff a command is to replace it by its immediate subevents, each of which is executed as a command. To puff* a command is to replace the command by each of its immediate subevents and then to puff* each of the puffable commands among the newly introduced ones. For example, suppose "ab" is a book containing the following (in-package "ACL2") (include-book "a") (include-book "b") Suppose that book "a" only contained defuns for the functions a1 and a2 and that "b" only contained defuns for b1 and b2. Now consider an ACL2 state in which only two commands have been executed, the first being (include-book "ab") and the second being (include-book "c"). Thus, the relevant part of the display produced by :pbt 1 would be: 1 (INCLUDE-BOOK "ab") 2 (INCLUDE-BOOK "c") Call this state the "starting state" in this example, because we will refer to it several times. Suppose :puff 1 is executed in the starting state. Then the first command is replaced by its immediate subevents and :pbt 1 would show: 1 (INCLUDE-BOOK "a") 2 (INCLUDE-BOOK "b") 3 (INCLUDE-BOOK "c") Contrast this with the execution of :puff* 1 in the starting state. Puff* would first puff (include-book "ab") to get the state shown above. But then it would recursively puff* the puffable commands introduced by the first puff. This continues recursively as long as any puff introduced a puffable command. The end result of :puff* 1 in the starting state is 1 (DEFUN A1 ...) 2 (DEFUN A2 ...) 3 (DEFUN B1 ...) 4 (DEFUN B2 ...) 5 (INCLUDE-BOOK "c") Observe that when puff* is done, the originally indicated command, (include-book "ab"), has been replaced by the corresponding sequence of primitive events. Observe also that puffable commands elsewhere in the history, for example, command 2 in the starting state, are not affected (except that their command numbers grow as a result of the splicing in of earlier commands).  File: acl2-doc-emacs.info, Node: RESET-KILL-RING, Next: TAU-DATA, Prev: PUFF*, Up: HISTORY RESET-KILL-RING save memory by resetting and perhaps resizing the kill ring used by oops By default, ACL2 holds on to old logical worlds when you undo commands (see *note UBT::), as documented elswhere; see *note OOPS::. You can free up memory by deleting those old worlds using reset-kill-ring. Examples: (reset-kill-ring t state) ; replace each element of the kill ring by nil (reset-kill-ring 2 state) ; create a new kill ring of '(nil nil) (reset-kill-ring 0 state) ; create a new kill ring that is empty (reset-kill-ring nil state) ; just return the length of the kill ring General form: (reset-kill-ring n state) where n evaluates either to t, to nil, or to a nonnegative integer (a natp). If n evaluates to t, it is treated as the length of the current kill ring. If n is nil, then the length k of the current kill ring is returned as a value triple (mv nil k state). If n is a natp, then the kill ring is replaced with a list of n nils. In particular, use (reset-kill-ring 0 state) to avoid saving any old logical worlds, at the cost of disabling the effect of the oops command.  File: acl2-doc-emacs.info, Node: TAU-DATA, Next: TAU-DATABASE, Prev: RESET-KILL-RING, Up: HISTORY TAU-DATA to see what tau knows about a function symbol Examples: (tau-data binary-+) General Form: (tau-data fn) This macro returns a list structure that indicates what facts about the symbol fn are known to the tau system. Fn should either be a function symbol or else a macro associated with fn; see *note MACRO-ALIASES-TABLE::. See *note INTRODUCTION-TO-THE-TAU-SYSTEM:: for background details. The list structure should be self-explanatory given the following brief comments. The "index" of a function, when non-nil, means the function is a monadic Boolean function treated by the tau system as a tau predicate. The "positive" and "negative implicants" are conjunctions that indicate the tau implied by the given one or its negation. The "signatures" entry is a formula indicating all the known signatures. If the signatures formula is T it means there are no known signatures. (Signatures is the conjunction of all signature rules and the empty conjunction is T.) If you wish to see a long list of all the runes from which some tau information has been gleaned, evaluate (global-val 'tau-runes (w state)).  File: acl2-doc-emacs.info, Node: TAU-DATABASE, Next: U, Prev: TAU-DATA, Up: HISTORY TAU-DATABASE to see the tau database as a (very large) object Example: (tau-database (w state)) This function returns a large list object that shows in a human-readable way what the tau system knows about every function symbol. It is supposed to be self-explanatory. See *note INTRODUCTION-TO-THE-TAU-SYSTEM:: for background details. If the output is not self-explanatory, please contact the implementors and we will improve the output or the documentation.  File: acl2-doc-emacs.info, Node: U, Next: UBT, Prev: TAU-DATABASE, Up: HISTORY U undo last command, without a query Example: :u The keyword command :u is the same as :ubt :max, but with related queries suppressed appropriately. :Oops will undo the last :u. See *note UBT::, see *note UBU::, see *note UBT!::, and see *note UBU!::.  File: acl2-doc-emacs.info, Node: UBT, Next: UBT!, Prev: U, Up: HISTORY UBT undo the commands back through a command descriptor Examples: :ubt :max ; undo back through the most recent command ; (which just means undo the most recent command) :ubt :x ; same as :ubt :max :u ; same as :ubt :max with no questions asked :ubt fn ; undo back through the introduction of fn ; (including all the other events in fn's block) :ubt 5 ; undo back through the fifth command executed :ubt (:max -4) ; undo back through the most recent five commands :ubt (:x -4) ; undo back through the most recent five commands See *note COMMAND-DESCRIPTOR::. Ubt takes one argument, a command descriptor, and undoes the commands from :max (aka :x) through the one described. See *note COMMAND-DESCRIPTOR::. Pbt will print the commands that ubt will undo. :Oops will undo the undo. See *note OOPS::. Ubt can cause errors or queries. To avoid these, see *note UBT!::. It is important to remember that a command may create several events. That is, the command that introduces fn1 may also introduce fn2. Undoing the command that created either of these will undo them both. The events created by a command constitute the command's "block" and we can only undo entire blocks. Use pcb to print the command block of a command if you wish to see what will be lost by undoing the command. Ubt will not undo into "prehistory". :Ubt 1 will undo all of your commands. But :ubt -5 will cause an error, warning you that :ubt cannot undo system initialization. See *note U:: for how to undo just the latest command, and see *note UBU:: and see *note UBU!:: for how to undo back up to, but not including, the current command.  File: acl2-doc-emacs.info, Node: UBT!, Next: UBT-PREHISTORY, Prev: UBT, Up: HISTORY UBT! undo commands, without a query or an error Example: :ubt! :x-4 The keyword command :ubt! is the same as :ubt, but with related queries suppressed appropriately, and with a guarantee that it is "error-free." More precisely, the value returned by :ubt! will always be of the form (mv nil val state). :Oops will undo the last :ubt!. See *note UBT::, see *note UBU::, and see *note U::.  File: acl2-doc-emacs.info, Node: UBT-PREHISTORY, Next: UBU, Prev: UBT!, Up: HISTORY UBT-PREHISTORY undo the commands back through the last reset-prehistory event This command is only used to eliminate a reset-prehistory event. If your most recent reset-prehistory event does not have a flag argument of t, then :ubt-prehistory undoes all command back through, and including, that reset-prehistory event.  File: acl2-doc-emacs.info, Node: UBU, Next: UBU!, Prev: UBT-PREHISTORY, Up: HISTORY UBU undo the commands back up to (not including) a command descriptor Examples: :ubu :x-3 ; undo the last three commands (same as :ubt :x-2) :ubu (:x -3) ; same as above :ubu fn ; undo back up to, but not including the introduction of fn ; (so fn will continue to be defined) :ubu 5 ; undo back up to, but not including, the fifth command ; executed (leaving the first five commands in place) See *note COMMAND-DESCRIPTOR::. Ubu takes one argument, a command descriptor, and undoes the commands from :max (aka :x) up to, but not including, the indicated command. See *note COMMAND-DESCRIPTOR::. Ubu can cause errors or queries. To avoid these, see *note UBU!::. Also see *note UBT::, which is similar but also undoes the indicated command. As for :ubt, :oops will undo the undo (see *note OOPS::) and ubu will not undo into "prehistory". See *note U:: for how to undo just the latest command, and see *note UBT:: and see *note UBT!:: for how to undo back through (that is, including) the current command.  File: acl2-doc-emacs.info, Node: UBU!, Prev: UBU, Up: HISTORY UBU! undo commands, without a query or an error Example: :ubu! :x-4 The keyword command :ubu! is the same as :ubu, but with related queries suppressed appropriately, and with a guarantee that it is "error-free." More precisely, the error triple (see *note ERROR-TRIPLES::) returned by :ubu! will always be of the form (mv nil val state). :Oops will undo the last :ubu!. Also see *note UBU::, see *note UBT::, and see *note U::.  File: acl2-doc-emacs.info, Node: HONS-AND-MEMOIZATION, Next: INTRODUCTION-TO-THE-TAU-SYSTEM, Prev: HISTORY, Up: Top HONS-AND-MEMOIZATION hash cons, function memoization, and applicative hash tables Bob Boyer and Warren Hunt, and later Jared Davis and Sol Swords, have developed a canonical representation for ACL2 data objects and a function memoization mechanism to facilitate reuse of previously computed results. This facility includes procedures to read and print ACL2 expressions in such a way that repetition of some ACL2 objects is eliminated, thereby permitting a kind of on-the-fly file compression. The implementation does not alter the semantics of ACL2 except to add a handful of definitions. We give the name "ACL2(h)" to the resulting experimental extension of the ACL2 system, which includes hash cons, function memoization, and fast association lists (applicative hash tables). It is optimized for Clozure Common Lisp (CCL), but other ANSI-compliant host Lisp implementations may also work, provided there are sufficient machine resources. If you want to use ACL2(h), you might find it helpful to consult the document centaur/README.html in the ACL2 community books. An easy way is to build ACL2(h) is to include the following with a `make' command: ACL2_HONS=h So for example, to make an executable image and also documentation (which will appear in subdirectories doc/EMACS and doc/HTML): make large DOC ACL2_HONS=h * Menu: * CLEAR-HASH-TABLES:: deprecated feature * CLEAR-MEMOIZE-STATISTICS:: clears all profiling info displayed by (memoize-summary) * CLEAR-MEMOIZE-TABLE:: forget values remembered for the given function * CLEAR-MEMOIZE-TABLES:: forget values remembered for all the memoized functions * CONS-SUBTREES:: (cons-subtrees x nil) builds a fast alist that associates each subtree of X with T, without duplication. * FAST-ALIST-FREE:: (fast-alist-free alist) throws away the hash table associated with a fast alist. * FAST-ALIST-FREE-ON-EXIT:: Free a fast alist after the completion of some form. * FAST-ALIST-LEN:: (fast-alist-len alist) counts the number of unique keys in a fast alist. * FAST-ALIST-SUMMARY:: (fast-alist-summary) prints some basic statistics about any current fast alists. * FAST-ALISTS:: alists with hidden hash tables for faster execution * FLUSH-HONS-GET-HASH-TABLE-LINK:: deprecated feature * HONS:: (hons x y) returns a normed object equal to (cons x y). * HONS-ACONS:: (hons-acons key val alist) is the main way to create or extend fast-alists. * HONS-ACONS!:: (hons-acons! key val alist) is an alternative to hons-acons that produces normed, fast alists. * HONS-ASSOC-EQUAL:: (hons-assoc-equal key alist) is *not fast*; it serves as the logical definition for hons-get. * HONS-CLEAR:: (hons-clear gc) is a drastic garbage collection mechanism that clears out the underlying Hons Space. * HONS-COPY:: (hons-copy x) returns a normed object that is equal to X. * HONS-COPY-PERSISTENT:: (hons-copy-persistent x) returns a normed object that is equal to X and which will be re-normed after any calls to hons-clear. * HONS-EQUAL:: (hons-equal x y) is a recursive equality check that optimizes when parts of its arguments are normed. * HONS-EQUAL-LITE:: (hons-equal-lite x y) is a non-recursive equality check that optimizes if its arguments are normed. * HONS-GET:: (hons-get key alist) is the efficient lookup operation for fast-alists. * HONS-NOTE:: notes about HONS, especially pertaining to expensive resizing operations * HONS-RESIZE:: (hons-resize ...) can be used to manually adjust the sizes of the hash tables that govern which ACL2 Objects are considered normed. * HONS-SHRINK-ALIST:: (hons-shrink-alist alist ans) can be used to eliminate "shadowed pairs" from an alist or to copy fast-alists. * HONS-SHRINK-ALIST!:: (hons-shrink-alist! alist ans) is an alternative to hons-shrink-alist that produces a normed result. * HONS-SUMMARY:: (hons-summary) prints basic information about the sizes of the tables in the current Hons Space. * HONS-WASH:: (hons-wash) is like gc$ but can also garbage collect normed objects (CCL Only). * MAKE-FAST-ALIST:: (make-fast-alist alist) creates a fast-alist from the input alist, returning alist itself or, in some cases, a new object equal to it. * MEMOIZE-SUMMARY:: display all collected profiling and memoization table info * MEMSUM:: display all collected profiling and memoization info * NEVER-MEMOIZE:: Mark a function as unsafe to memoize. * NORMED:: Normed objects are ACL2 Objects that are "canonical" or "unique" in a certain sense. * NUMBER-SUBTREES:: (number-subtrees x) returns the number of distinct subtrees of X, in the sense of equal * SLOW-ALIST-WARNING:: warnings issued when fast-alists are used inefficiently * WITH-FAST-ALIST:: (with-fast-alist name form) causes name to be a fast alist for the execution of form. * WITH-STOLEN-ALIST:: (with-stolen-alist name form) ensures that name is a fast alist at the start of the execution of form. At the end of execution, it ensures that name is a fast alist if and only if it was originally. That is, if name was not a fast alist originally, its hash table link is freed, and if it was a fast alist originally but its table was modified during the execution of form, that table is restored. Note that any extended table created from the original fast alist during form must be manually freed. Related topics other than immediate subtopics: * MEMOIZE:: turn on memoization for a specified function * RESTORE-MEMOIZATION-SETTINGS:: restore the saved memoization settings * SAVE-AND-CLEAR-MEMOIZATION-SETTINGS:: save and remove the current memoization settings * UNMEMOIZE:: turn off memoization for the specified function Much of the documentation for the remainder of this topic is taken from the paper "Function Memoization and Unique Object Representation for ACL2 Functions" by Robert S. Boyer and Warren A. Hunt, Jr., which has appeared in the Sixth International Workshop on the ACL2 Theorem Prover and Its Applications, ACM Digital Library, 2006. In the implementation of the ACL2 logic, ACL2 data objects are represented by Common Lisp objects of the same type, and the ACL2 pairing operation is internally implemented by the Common Lisp cons function. In Common Lisp, cons is guaranteed to provide a new pair, distinct from any previously created pair. We have defined a new ACL2 function HONS that is logically identical to the ACL2 cons function, but whose implementation usually reuses an existing pair if its components are identical to the components of an existing pair. A record of ACL2 HONS objects is kept, and when an ACL2 function calls hons ACL2 searches for an existing identical pair before allocating a new pair; this operation been called "hash consing". It appears that hash consing was first conceived by A. P. Ershov in 1957, to speed up the recognition of common subexpressions. Ershov showed how to collapse trees to minimal DAGs by traversing trees bottom up, and he used hashing to eliminate the re-evaluation of common subexpressions. Later, Eiichi Goto implemented a Lisp system with a built-in hash consing operation: his h-CONS cells were rewrite protected and free of duplicate copies, and Goto used this hash consing operation to facilitate the implementation of a symbolic algebra system he developed. Memoizing functions also has a long history. In 1967, Donald Michie proposed using memoized functions to improve the performance of machine learning. Rote learning was improved by a learning function not forgetting what it had previously learned; this information was stored as memoized function values. The use of hash consing has appeared many times. For instance, Henry Baker used hash consing to improve the performance of the well-known Boyer rewriting benchmark. Baker used both hash consing and function memoization to improve the speed of the Takeuchi function, exactly in the spirit of our implementation, but without the automated, system-wide integration we report here. The ACL2 implementation permits memoization of user-defined functions. During execution a user may enable or disable function memoization on an individual function basis, may clear memoization tables, and may even keep a stack of memoization tables. This facility takes advantage of our implementation where we keep one copy of each distinct ACL2 data object. Due to the functional nature of ACL2, it is sufficient to have at most one copy of any data structure; thus, a user may arrange to keep data canonicalized. This implementation extends to the entire ACL2 system the benefits enjoyed by BDDs: canonicalization, memoization, and fast equality check. We have defined various algorithms using these features, and we have observed, in some cases, substantial performance increases. For instance, we have implemented unordered set intersection and union operations that operate in time roughly linear in the size of the sets. Without using arrays, we defined a canonical representation for Boolean functions using ACL2 objects. We have investigated the performance of rewriting and tree consensus algorithms to good effect, and we believe function memoization offers interesting opportunities to simplify algorithm definition while simultaneously providing performance improvements. We recommend that users focus at first on the logical definitions of hons and other primitives rather than their underlying Common Lisp implementations. Integrated with this memoization system is a performance monitoring system, which can provide real-time feedback on the operation and usefulness of hons and function memoization. For a more detailed description of these tools, please see the ACL2 2006 workshop paper mentioned above. The Fibonacci function is a small example that demonstrates the utility of function memoization. The following definition exhibits a runtime that is exponential in its input argument. (defun fib (x) (declare (xargs :guard (natp x))) (mbe :logic (cond ((zp x) 0) ((= x 1) 1) (t (+ (fib (- x 1)) (fib (- x 2))))) :exec (if (< x 2) x (+ (fib (- x 1)) (fib (- x 2)))))) Below we show how the ACL2 time$ utility can measure the time to execute a call to the fib function (with some editing to avoid noise from the underlying Common Lisp implementation). Memoize is actually an ACL2 macro that expands to a call of the ACL2 function used to identify a function for memoization; see *note MEMOIZE::. This function also accepts a well-formed term that must be true in order for the system to memoize a call of the memoized function; to ensure that an instance of the term is safe for evaluation in Common Lisp, a check is performed that if the guard of the memoized function is satisfied, then this instance will execute without error. ACL2 !>(time$ (fib 40)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.99 seconds realtime, 0.98 seconds runtime ; (1,296 bytes allocated). 102334155 ACL2 !>(memoize 'fib) Summary Form: ( TABLE MEMOIZE-TABLE ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) Summary Form: ( PROGN (TABLE MEMOIZE-TABLE ...) ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FIB ACL2 !>(time$ (fib 40)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.00 seconds realtime, 0.00 seconds runtime ; (2,864 bytes allocated). 102334155 ACL2 !>(time$ (fib 100)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.00 seconds realtime, 0.00 seconds runtime ; (7,024 bytes allocated). 354224848179261915075 ACL2 !>(unmemoize 'fib) We see that once the function fib is identified as a function for which function calls should be memoized, the execution times are substantially reduced. Finally, we can prevent fib from being further memoized; in fact, unmemoize erases the previously memoized values. The implementation of hash consing, memoization, and applicative hash tables involves several facets: canonical representation of ACL2 data, function memoization, and the use of Lisp hash tables to improve the performance of association list operations. We discuss each of these in turn, and we mention some subtle interrelationships. Although it is not necessary to understand the discussion in this section, it may permit some users to better use this implementation. This discussion may be confusing for some ACL2 users as it makes references to Lisp implementation features. The ACL2 system is actually implemented as a Lisp program that is layered on top of a Common Lisp system implementation. ACL2 data constants are implemented with their corresponding counterparts in Common Lisp; that is, ACL2 cons pairs, strings, characters, numbers, and symbols are implemented with their specific Common Lisp counterparts. This choice permits a number of ACL2 primitive functions to be implemented with their corresponding Common Lisp functions, but there are indeed differences. ACL2 is a logic, and as such, it does not specify anything to do with physical storage or execution performance. When ACL2 is used, the knowledgeable user can write functions to facilitate the reuse of some previously computed values. Recall the three mechanisms under discussion: hash consing, function memoization, and fast association list operations. The function memoization mechanism takes advantage of the canonical representation of data objects provided by the hons operation as does the fast association list operation mechanism. Each time hons is invoked, its arguments are themselves converted, if necessary, to uniquely represented objects. The ACL2 universe is recursively closed under the cons pairing operation and the atoms. Hash consing (hons) is logically identical to cons, but a set of tables is used to record each hons pair. When a hons pair is requested, the implementation checks, in the current set of tables, whether the requested pair already exists. If not, a new pair is created and a record of that pair is made; otherwise, a reference to the previously created pair is returned. Thus, any data object created with hons has a unique representation, as does every subcomponent. We also arrange for strings to have a unique representation -- only one copy of each different string is kept -- and when any previously unseen string is an argument to hons, we add the string to a unique table of strings. A system-wide benefit of having a canonical representation for data is that equality comparisons between any two data objects can be done in constant time. The definition of hons in no way changes the operation of cons -- hons creates a cons pair. When asked to create a hons, the implementation checks to see if there is a cons with the same car and cdr as the hons being requested. Thus, the only difference between the results of a hons call and a corresponding cons call is a notation in some invisible (to the ACL2 logic) tables where we record which cons elements are also hons elements. Since a hons is nothing but a cons, the operation of car and cdr is unchanged. In fact, the implementation is designed so that at any time it is safe to clear the table identifying which cons elements are also considered hons elements. User-defined functions with defined and verified guards can be memoized. When a function is memoized, a user-supplied condition restricts the domain when memoization is attempted. When the condition is satisfied, memoization is attempted (assuming that memoization for the function is presently enabled); otherwise, the function is just evaluated. Each memoized function has a hash table that is used to keep track of a unique list of function arguments paired with their values. If appropriate, for each function the corresponding table is checked to see if a previous call with exactly the same arguments already exists in the table: if so, then the associated value is returned; if not, then the function is evaluated and a new key-value pair is added to the table of memoized values so that some future call will benefit from the memoization. With ACL2 user functions memoization can be dynamically enabled and disabled. There is an ACL2 function that clears a specific memoization table. And finally, just as with the hons table, a stack of these function memoization tables is maintained; that is, it is possible to memoize a function, use it a bit, set the memoized values aside, start a new table, use it, and then return to the original table. We next discuss the fast lookup operation for association lists. When a pair is added to an association list using the functions hons-acons or hons-acons!, the system also records the key-value pair in an associated hash table. As long as a user only used these two functions when placing key-value pairs on an association list, the key-value pairs in the corresponding hash table will be synchronized with the key-value pairs in the association list. Later, if hons-get is used to look up a key, then instead of performing a linear search of the association list we consult the associated hash table. If a user does not strictly follow this discipline, then a linear search may be required. In some sense, these association lists are much like ACL2 arrays, but without the burden of explicitly naming the arrays. The ACL2 array compress1 function is analogous to the functions hons-shrink-alist and hons-shrink-alist!. There are user-level ACL2 functions that allow the associated hash tables to be cleared and removed. Finally, we would advise anyone who is using CCL in a research environment to stay plugged into the "trunk" or "bleeding edge" of CCL development. This is very easy to do by typing a few commands to a shell, for example standing above the target directory as follows, provided one has svn working. For linux: rm -rf ccl svn co http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx8664/ccl For an x86 Macintosh running the Darwin OS: svn co http://svn.clozure.com/publicsvn/openmcl/trunk/darwinx8664/ccl To keep up to date, you may find it sufficient to do: cd ccl svn update Whether obtaining a fresh CCL or just updating, finally issue these commands. ./lx86cl64 (rebuild-ccl :full t) (quit) ./lx86cl64 (rebuild-ccl :full t) (quit) REFERENCES Baker, Henry G., The Boyer Benchmark at Warp Speed. ACM Lisp Pointers V,3 (Jul-Sep 1992), pages 13-14. Baker, Henry G., A Tachy 'TAK'. ACM Lisp Pointers Volume 3, July-September, 1992, pages 22-23. Robert S. Boyer and Warren A. Hunt, Jr., Function Memoization and Unique Object Representation for ACL2 Functions, in the Sixth International Workshop on the ACL2 Theorem Prover and Its Applications, ACM Digital Library, 2006. A. P. Ershov. On Programming of Arithmetic Operations. In the Communications of the ACM, Volume 118, Number 3, August, 1958, pages 427-430. Eiichi Goto, Monocopy and Associative Algorithms in Extended Lisp, TR-74-03, University of Toyko, 1974. Richard P. Gabriel. Performance and Evaluation of Lisp Systems. MIT Press, 1985. Donald Michie. Memo functions: a Language Feature with Rote Learning Properties. Technical Report MIP-R-29, Department of Artificial Intelligence, University of Edinburgh, Scotland, 1967. Donald Michie. Memo Functions and Machine Learning. In Nature, Volumne 218, 1968, pages 19-22.  File: acl2-doc-emacs.info, Node: CLEAR-HASH-TABLES, Next: CLEAR-MEMOIZE-STATISTICS, Prev: HONS-AND-MEMOIZATION, Up: HONS-AND-MEMOIZATION CLEAR-HASH-TABLES deprecated feature This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Deprecated. Calls clear-memoize-tables and then hons-clear or hons-wash, whichever makes sense for the underlying Common Lisp.  File: acl2-doc-emacs.info, Node: CLEAR-MEMOIZE-STATISTICS, Next: CLEAR-MEMOIZE-TABLE, Prev: CLEAR-HASH-TABLES, Up: HONS-AND-MEMOIZATION CLEAR-MEMOIZE-STATISTICS clears all profiling info displayed by (memoize-summary) This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, this function just returns nil. It clears all profiling info displayed by (memoize-summary)  File: acl2-doc-emacs.info, Node: CLEAR-MEMOIZE-TABLE, Next: CLEAR-MEMOIZE-TABLES, Prev: CLEAR-MEMOIZE-STATISTICS, Up: HONS-AND-MEMOIZATION CLEAR-MEMOIZE-TABLE forget values remembered for the given function This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. This function returns its argument, fn, unchanged. The values memoized for fn are forgotten.  File: acl2-doc-emacs.info, Node: CLEAR-MEMOIZE-TABLES, Next: CONS-SUBTREES, Prev: CLEAR-MEMOIZE-TABLE, Up: HONS-AND-MEMOIZATION CLEAR-MEMOIZE-TABLES forget values remembered for all the memoized functions This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Clear-memoize-tables is a logical no-op. All memoized values are forgotten. It returns nil, invoking clear-memoize-table for each memoized function.  File: acl2-doc-emacs.info, Node: CONS-SUBTREES, Next: FAST-ALIST-FREE, Prev: CLEAR-MEMOIZE-TABLES, Up: HONS-AND-MEMOIZATION CONS-SUBTREES (cons-subtrees x nil) builds a fast alist that associates each subtree of X with T, without duplication. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::.  File: acl2-doc-emacs.info, Node: FAST-ALIST-FREE, Next: FAST-ALIST-FREE-ON-EXIT, Prev: CONS-SUBTREES, Up: HONS-AND-MEMOIZATION FAST-ALIST-FREE (fast-alist-free alist) throws away the hash table associated with a fast alist. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, this function is the identity; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, fast-alist-free removes the hash table associated with this alist, if one exists. This effectively converts the alist into an ordinary alist. Also see *note FAST-ALIST-FREE-ON-EXIT::. Because there is no automatic mechanism for freeing the hash tables used in fast alists, to avoid memory leaks you should manually free any alists that will no longer be used. You may find fast-alist-summary useful in tracking down alists that were not properly freed. It is safe to call fast-alist-free on any argument, including fast alists that have already been freed and objects which are not alists at all.  File: acl2-doc-emacs.info, Node: FAST-ALIST-FREE-ON-EXIT, Next: FAST-ALIST-LEN, Prev: FAST-ALIST-FREE, Up: HONS-AND-MEMOIZATION FAST-ALIST-FREE-ON-EXIT Free a fast alist after the completion of some form. Logically, (fast-alist-free-on-exit alist form) is the identity and returns form. Also see *note FAST-ALIST-FREE::. Under the hood, this essentially expands to: (prog1 form (fast-alist-free alist)) In other words, alist is not freed immediately, but instead remains a fast alist until the form completes. This may be useful when you are writing code that uses a fast alist but has many return points. See also the macro fast-alists-free-on-exit defined in the community book "books/centaur/misc/hons-extra.lisp", which can be used to free several alists. The community book "books/centaur/misc/hons-extra.lisp" extends the b* macro (defined in the community book "books/tools/bstar.lisp") with the free-on-exit pattern binder. That is, after executing (include-book "centaur/misc/hons-extra.lisp" :dir :system), the form (b* (... ((free-on-exit a b c)) ...) ...) causes a, b, and c to be freed when the b* completes, but they remain fast alists until then.  File: acl2-doc-emacs.info, Node: FAST-ALIST-LEN, Next: FAST-ALIST-SUMMARY, Prev: FAST-ALIST-FREE-ON-EXIT, Up: HONS-AND-MEMOIZATION FAST-ALIST-LEN (fast-alist-len alist) counts the number of unique keys in a fast alist. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically this function counts how many elements would remain in the alist were we to shrink it with hons-shrink-alist. Good discipline requires that the alist is a fast alist. Under the hood, when the alist is a fast alist we can simply call the underlying Common Lisp function hash-table-count on the associated hash table, which is very fast and doesn't require us to actually shrink the alist.  File: acl2-doc-emacs.info, Node: FAST-ALIST-SUMMARY, Next: FAST-ALISTS, Prev: FAST-ALIST-LEN, Up: HONS-AND-MEMOIZATION FAST-ALIST-SUMMARY (fast-alist-summary) prints some basic statistics about any current fast alists. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, fast-alist-summary just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, this function gathers and prints some basic statistics about the current fast alists. It may be useful for identifying fast alists that should have been freed with fast-alist-free.  File: acl2-doc-emacs.info, Node: FAST-ALISTS, Next: FLUSH-HONS-GET-HASH-TABLE-LINK, Prev: FAST-ALIST-SUMMARY, Up: HONS-AND-MEMOIZATION FAST-ALISTS alists with hidden hash tables for faster execution This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. The implementation of fast alists is, in many ways, similar to that of ACL2 arrays. Logically, hons-acons is just like acons, and hons-get is similar to assoc-equal. But under the hood, hash tables are associated with these alists, and, when a certain *discipline* is followed, these functions execute with hash table speeds. What is this discipline? Each hons-acons operation "steals" the hash table associated with the alist that is being extended. Because of this, one must be very conscious of which object is the most recent extension of an alist and use that extension exclusively. The only penalty for a failure to keep track of the most recent extension is a loss of execution speed, not of correctness, and perhaps the annoyance of some slow-alist-warnings. Maintaining discipline may require careful passing of a fast alist up and down through function calls, as with any single threaded object in an applicative setting, but there is *no syntactic enforcement* that insists you only use the most recent extension of an alist as there is for single threaded objects (stobjs). Also, even with perfectly proper code, discipline can sometimes be lost due to user interrupts and aborts. Performance Notes See *note HONS-ACONS:: for how the final cdr of a fast alist can be used as a size hint or as a name for reports printed by calling fast-alist-summary. The keys of fast alists are always normed. Why? In Common Lisp, equal-based hashing is relatively slow, so to allow the use of eql-based hash tables, hons-acons and hons-get always hons-copy the keys involved. Since alist keys are frequently atoms, this norming activity may often be so fast that you do not need to think about it. But if you are going to use large structures as the keys for your fast alist, this overhead can be significant, and you may want to ensure that your keys are normed ahead of time. There is no automatic mechanism for reclaiming the hash tables that are associated with alists. Because of this, to avoid memory leaks, you should call fast-alist-free to remove the hash table associated with alists that will no longer be used.  File: acl2-doc-emacs.info, Node: FLUSH-HONS-GET-HASH-TABLE-LINK, Next: HONS, Prev: FAST-ALISTS, Up: HONS-AND-MEMOIZATION FLUSH-HONS-GET-HASH-TABLE-LINK deprecated feature This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Deprecated. Alias for fast-alist-free.  File: acl2-doc-emacs.info, Node: HONS, Next: HONS-ACONS, Prev: FLUSH-HONS-GET-HASH-TABLE-LINK, Up: HONS-AND-MEMOIZATION HONS (hons x y) returns a normed object equal to (cons x y). This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. In the logic, hons is just cons; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, hons does whatever is necessary to ensure that its result is normed. What might this involve? Since the car and cdr of any normed cons must be normed, we need to hons-copy x and y. This requires little work if x and y are already normed, but can be expensive if x or y contain large, un-normed cons structures. After that, we need to check whether any normed cons equal to (x . y) already exists. If so, we return it; otherwise, we need to construct a new cons for (x . y) and install it as the normed version of (x . y). Generally speaking, these extra operations make hons much slower than cons, even when given normed arguments.  File: acl2-doc-emacs.info, Node: HONS-ACONS, Next: HONS-ACONS!, Prev: HONS, Up: HONS-AND-MEMOIZATION HONS-ACONS (hons-acons key val alist) is the main way to create or extend fast-alists. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, hons-acons is like acons except that its guard does not require alistp; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, two things are done differently. First, the key is copied with hons-copy; this lets us use EQL-based hash tables instead of EQUAL-based hash tables for better performance. Second, assuming there is a valid hash table associated with this alist, we destructively update the hash table by binding key to val. The hash table will no longer be associated with alist, but will instead be associated with the new alist returned by hons-acons. Hash Table Creation A new hash table is created by hons-acons whenever alist is an atom. Unlike ordinary acons, we do not require that alist be nil, and in fact you may wish to use a non-nil value for one of two reasons. 1. As a size hint By default, the new hash table will be given a quite modest default capacity of 60 elements. As more elements are added, the table may need to be resized to accommodate them, and this resizing has some runtime cost. When a natural number is used as a fast alist's name, we interpret it as a size hint. For example, (hons-acons 'foo 'bar 1000) instructs us to use 1000 as the initial size for this hash table and binds 'foo to 'bar. The resulting table should not need to be resized until more than 1000 elements are added. We ignore size hints that request fewer than 60 elements. Because of hash collisions, hash tables typically need to have a larger size than the actual number of elements they contain. The hash tables for fast alists are told to grow when they reach 70% full. So, an easy rule of thumb might be: multiply the expected number of elements you need by 1.5 to keep your hash tables about 2/3 full. 2. As an alist name We also frequently use a symbol for alist, and think of this symbol as the name of the new alist. We have found that naming alists can be valuable for two reasons: First, the name can be helpful in identifying fast alists that should have been freed, see fast-alist-summary. Second, names can sometimes be used to avoid a subtle and insidious table-stealing phenomenon that occurs when using fast-alists that are themselves normed; see hons-acons!. At the moment, there is no way to simultaneously name a fast alist and also give it a size hint. We may eventually allow strings to include embedded name and size components, but for now we have not implemented this capability.  File: acl2-doc-emacs.info, Node: HONS-ACONS!, Next: HONS-ASSOC-EQUAL, Prev: HONS-ACONS, Up: HONS-AND-MEMOIZATION HONS-ACONS! (hons-acons! key val alist) is an alternative to hons-acons that produces normed, fast alists. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, hons-acons! is like acons except that its guard does not require alistp; we leave it enabled and would think it odd to ever prove a theorem about it. Ordinarily, fast-alists are constructed with hons-acons instead of hons-acons!. In such alists, the keys are honsed, but the conses that make up the "spine" of the alist itself are ordinary conses. In other words, it is basically correct to say: (hons-acons key val alist) == (cons (cons (hons-copy key) val) alist) In contrast, when hons-acons! is used, the conses making up the alist itself are also normed. That is, (hons-acons! key val alist) == (hons (hons key val) alist) Generally, you *should not use* hons-acons! unless you really know what you're doing. Drawback 1. hons-acons! requires you to hons-copy all of the values that are being stored in the fast alist. If you are storing large values, this may be expensive. Drawback 2. It can be more difficult to maintain the proper discipline when using hons-acons!. For instance, consider the following: (let ((al1 (hons-acons 1 'one (hons-acons 2 'two nil))) (al2 (hons-acons 1 'one (hons-acons 2 'two nil)))) ...) Here, both al1 and al2 are valid fast alists and they can be extended independently without any trouble. But if these alists had instead been constructed with hons-acons!, then since both al1 and al2 are equal, normed conses, they will literally be eq and hence will refer to precisely the same hash table. In other words, hons-acons! makes it relatively easy to *inadvertently* steal the hash table associated with some other fast alist. This problem can be alleviated somewhat by uniquely naming alists; see the discussion in hons-acons for details. Despite these drawbacks, hons-acons! is the only way besides hons-shrink-alist! to generate a fast alist that is normed. It is not adequate to hons-copy a fast alist that was generated by ordinary hons-acons calls, because this would produce an EQUAL-but-not-EQ object, and this new object would not be associated with the fast alist's hash table. acl2-sources/doc/EMACS/acl2-doc-emacs.info-50000664002132200015000000111254512222333541017647 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: HONS-ASSOC-EQUAL, Next: HONS-CLEAR, Prev: HONS-ACONS!, Up: HONS-AND-MEMOIZATION HONS-ASSOC-EQUAL (hons-assoc-equal key alist) is *not fast*; it serves as the logical definition for hons-get. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. The definition of hons-assoc-equal is similar to that of assoc-equal, except that (1) it does not require alistp as a guard, and (2) it "skips over" any non-conses when its alist argument is malformed. We typically leave hons-get enabled and reason about hons-assoc-equal instead. One benefit of this approach is that it avoids certain "false" discipline warnings that might arise from execution during theorem proving.  File: acl2-doc-emacs.info, Node: HONS-CLEAR, Next: HONS-COPY, Prev: HONS-ASSOC-EQUAL, Up: HONS-AND-MEMOIZATION HONS-CLEAR (hons-clear gc) is a drastic garbage collection mechanism that clears out the underlying Hons Space. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, hons-clear just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, hons-clear brutally (1) clears all the tables that govern which conses are normed, then (2) optionally garbage collects, per the gc argument, then finally (3) re-norms the keys of fast-alists and persistently normed conses; see *note HONS-COPY-PERSISTENT::. Note. The hash tables making up the Hons Space retain their sizes after being cleared. If you want to shrink them, see hons-resize. Note. CCL users might prefer hons-wash, which is relatively efficient and allows for the garbage collection of normed conses without impacting their normed status. Note. It is not recommended to interrupt this function. Doing so may cause persistently normed conses and fast alist keys to become un-normed, which might lead to less efficient re-norming and/or violations of the fast-alist discipline.  File: acl2-doc-emacs.info, Node: HONS-COPY, Next: HONS-COPY-PERSISTENT, Prev: HONS-CLEAR, Up: HONS-AND-MEMOIZATION HONS-COPY (hons-copy x) returns a normed object that is equal to X. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. In the logic, hons-copy is just the identity function; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, hons-copy does whatever is necessary to produce a normed version of X. What might this involve? If X is a symbol, character, or number, then it is already normed and nothing is done. If X is a string, we check if any normed version of X already exists. If so, we return the already-normed version; otherwise, we install X as the normed version for all strings that are equal to X. If X is a cons, we must determine if there is a normed version of X, or recursively construct and install one. Norming large cons trees can become expensive, but there are a couple of cheap cases. In particular, if X is already normed, or if large subtrees of X are already normed, then not much needs to be done. The slowest case is norming some large ACL2 cons structure that has no subtrees which are already normed. Note that running hons-copy on an object that was created with cons is often slower than just using hons directly when constructing the object.  File: acl2-doc-emacs.info, Node: HONS-COPY-PERSISTENT, Next: HONS-EQUAL, Prev: HONS-COPY, Up: HONS-AND-MEMOIZATION HONS-COPY-PERSISTENT (hons-copy-persistent x) returns a normed object that is equal to X and which will be re-normed after any calls to hons-clear. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically hons-copy-persistent is the identity; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, hons-copy-persistent is virtually identical to hons-copy. The only difference is that when hons-clear is used, any persistently normed conses are automatically re-normed, and this re-norming can be carried out more efficiently than, say, an ordinary hons-copy.  File: acl2-doc-emacs.info, Node: HONS-EQUAL, Next: HONS-EQUAL-LITE, Prev: HONS-COPY-PERSISTENT, Up: HONS-AND-MEMOIZATION HONS-EQUAL (hons-equal x y) is a recursive equality check that optimizes when parts of its arguments are normed. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. In the logic, hons-equal is just equal; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, when hons-equal encounters two arguments that are both normed, it becomes a mere eql check, and hence avoids the overhead of recursively checking large cons structures for equality. Note. If hons-equal is given arguments that do not contain many normed objects, it can actually be much slower than equal! This is because it checks to see whether its arguments are normed at each recursive step, and so you are repeatedly paying the price of such checks. Also see *note HONS-EQUAL-LITE::, which only checks at the top level whether its arguments are normed.  File: acl2-doc-emacs.info, Node: HONS-EQUAL-LITE, Next: HONS-GET, Prev: HONS-EQUAL, Up: HONS-AND-MEMOIZATION HONS-EQUAL-LITE (hons-equal-lite x y) is a non-recursive equality check that optimizes if its arguments are normed. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. In the logic, hons-equal-lite is just equal; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, hons-equal-lite checks whether its arguments are normed, and if so it effectively becomes a eql check. Otherwise, it immediately calls equal to determine if its arguments are equal. The idea here is to strike a useful balance between equal and hons-equal. If both arguments happen to be normed, we get to use a very fast equality comparison. Otherwise, we just get out of the way and let equal do its work, without the extra overhead of recursively checking whether the subtrees of x and y are normed.  File: acl2-doc-emacs.info, Node: HONS-GET, Next: HONS-NOTE, Prev: HONS-EQUAL-LITE, Up: HONS-AND-MEMOIZATION HONS-GET (hons-get key alist) is the efficient lookup operation for fast-alists. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, hons-get is just an alias for hons-assoc-equal; we typically leave it enabled and prefer to reason about hons-assoc-equal instead. One benefit of this approach is that it helps to avoids "false" discipline warnings that might arise from execution during theorem proving. Under the hood, when alist is a fast alist that is associated with a valid hash table, hons-get first norms key using hons-copy, then becomes a gethash operation on the hidden hash table.  File: acl2-doc-emacs.info, Node: HONS-NOTE, Next: HONS-RESIZE, Prev: HONS-GET, Up: HONS-AND-MEMOIZATION HONS-NOTE notes about HONS, especially pertaining to expensive resizing operations This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Certain "Hons-Notes" are printed to the terminal to report implementation-level information about the management of HONS structures. Some of these may be low-level and of interest mainly to system developers. But below we discuss what users can learn from a particular hons-note, "ADDR-LIMIT reached". In short: If you are seeing a lot of such hons-notes, then you may be using a lot of memory. (Maybe you can reduce that memory; for certain BDD operations, for example (as defined in community books books/centaur/ubdds/), variable ordering can make a big difference.) By way of background: The ADDR-HT is the main hash table that lets us look up normed conses, i.e., honses. It is an ordinary Common Lisp hash table, so when it starts to get too full the Lisp will grow it. It can get really big. (It has been seen to take more than 10 GB of memory just for the hash table's array, not to mention the actual conses!) Hons-Notes may be printed when the ADDR-HT is getting really full. This growth can be expensive and can lead to memory problems. Think about what it takes to resize a hash table: (1) allocate a new, bigger array (2) rehash elements, copying them into the new array (3) free the old array Until you reach step (3) and a garbage collection takes place, you have to have enough memory to have both the old and new arrays allocated. If the old array was 10 GB and we want to allocate a new one that's 15 GB, this can get pretty bad. Also, rehashing the elements is expensive time-wise when there are lots of elements. Since resizing a hash table is expensive, we want to avoid it. There's a hons-resize command for this. You may want your books to start with something like the following. (value-triple (set-max-mem (* 30 (expt 2 30)))) ; 30 GB heap (value-triple (hons-resize :addr-ht #u_100_000_000)) ; 100M honses Basically, if you roughly know how many honses your book will need, you can just get them up front and then never to resize. The Hons-Notes about "ADDR-LIMIT reached" are basically there to warn you that the ADDR-HT is being resized. This can help you realize that your hons-resize command had too small of an ADDR-HT size, or might suggest that your book should have a hons-resize command. There are also commands like (hons-summary) and, defined in community book books/centaur/misc/memory-mgmt-logic.lisp, (hons-analyze-memory nil). These can show you how many honses you currently have, how much space they are taking, and that sort of thing. (A nice trick is to call hons-summary at the end of a book, to get an idea of how many honses you should ask for in your hons-resize command).  File: acl2-doc-emacs.info, Node: HONS-RESIZE, Next: HONS-SHRINK-ALIST, Prev: HONS-NOTE, Up: HONS-AND-MEMOIZATION HONS-RESIZE (hons-resize ...) can be used to manually adjust the sizes of the hash tables that govern which ACL2 Objects are considered normed. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. General form: (hons-resize [:str-ht size] [:nil-ht size] [:cdr-ht size] [:cdr-ht-eql size] [:addr-ht size] [:other-ht size] [:sbits size] [:fal-ht size] [:persist-ht size]) Example: (hons-resize :str-ht 100000 :addr-ht (expt 2 27)) Logically, hons-resize just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, hons-resize can be used to change the sizes of certain hash tables in the Hons Space. All arguments are optional. The size given to each argument should either be nil (the default) or a natural number. A natural number indicates the newly desired size for the hash table, whereas nil means "don't resize this table." Any non-natural argument is regarded as nil. You may wish to call this function for two reasons. 1. Improving Performance by Resizing Up Since the hash tables in the Hons Space automatically grow as new objects are normed, hons-resize is unnecessary. But automatic growth can be slow because it is incremental: a particular hash table might be grown dozens of times over the course of your application. Using hons-resize to pick a more appropriate initial size may help to reduce this overhead. 2. Reducing Memory Usage by Resizing Down Resizing can also be used to shrink the hash tables. This might possibly be useful immediately after a hons-clear to free up memory taken by the hash tables themselves. (Of course, the tables will grow again as you create new normed objects.) Advice for using hons-resize. Note that hash tables that are already close to the desired size, or which have too many elements to fit into the desired size, will not actually be resized. This makes resizing relatively "safe." Note that the hons-summary function can be used to see how large and how full your hash tables are. This may be useful in deciding what sizes you want to give to hons-resize. Note that hons-resize operates by (1) allocating new hash tables, then (2) copying everything from the old hash table into the new table. Because of this, for best performance you should ideally call it when the hash tables involved are minimally populated, i.e., at the start of your application, or soon after a hons-clear.  File: acl2-doc-emacs.info, Node: HONS-SHRINK-ALIST, Next: HONS-SHRINK-ALIST!, Prev: HONS-RESIZE, Up: HONS-AND-MEMOIZATION HONS-SHRINK-ALIST (hons-shrink-alist alist ans) can be used to eliminate "shadowed pairs" from an alist or to copy fast-alists. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. It assumes familiarity with fast alists; see *note FAST-ALISTS::. Logically, (hons-shrink-alist alist ans) is defined as follows: (cond ((atom alist) ans) ((atom (car alist)) (hons-shrink-alist (cdr alist) ans)) ((hons-assoc-equal (car (car alist)) ans) (hons-shrink-alist (cdr alist) ans)) (t (hons-shrink-alist (cdr alist) (cons (car alist) ans)))) The alist argument need not be a fast alist. Typically ans is set to nil or some other atom. In this case, shrinking produces a new, fast alist which is like alist except that (1) any "malformed," atomic entries have been removed, (2) all "shadowed pairs" have been removed, and (3) incidentally, the surviving elements have been reversed. This can be useful as a way to clean up any unnecessary bindings in alist, or as a way to obtain a "deep copy" of a fast alist that can extended independently from the original while maintaining discipline. Note that hons-shrink-alist is potentially expensive, for the following two reasons. o The alist is copied, dropping any shadowed pairs. This process will require a hash table lookup for each entry in the alist; and it will require creating a new alist, which uses additional memory. o Unless ans is a fast alist that is stolen (see below), a new hash table is created, which uses additional memory. This hash table is populated in time that is linear in the number of unique keys in the alist. When ans is not an atom, good discipline requires that it is a fast alist. In this case, hons-shrink-alist steals the hash table for ans and extends it with all of the bindings in alist that are not in ans. From the perspective of hons-assoc-equal, you can think of the resulting alist as being basically similar to (append ans alist), but in a different order. Note that when ans is not a fast alist (e.g., ans is an atom) then such stealing does not take place. A common idiom is to replace an alist by the result of shrinking it, in which case it is best to free the input alist, for example as follows. (let ((alist (fast-alist-free-on-exit alist (hons-shrink-alist alist nil)))) ...) See *note FAST-ALIST-FREE-ON-EXIT:: and see *note FAST-ALIST-FREE::.  File: acl2-doc-emacs.info, Node: HONS-SHRINK-ALIST!, Next: HONS-SUMMARY, Prev: HONS-SHRINK-ALIST, Up: HONS-AND-MEMOIZATION HONS-SHRINK-ALIST! (hons-shrink-alist! alist ans) is an alternative to hons-shrink-alist that produces a normed result. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically this function is just hons-shrink-alist; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, this is the same as hons-shrink-alist except that it uses something like hons-acons! instead of hons-acons. You generally *should not use* this function unless you really know what you're doing and understand the drawbacks discussed in hons-acons!.  File: acl2-doc-emacs.info, Node: HONS-SUMMARY, Next: HONS-WASH, Prev: HONS-SHRINK-ALIST!, Up: HONS-AND-MEMOIZATION HONS-SUMMARY (hons-summary) prints basic information about the sizes of the tables in the current Hons Space. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, hons-summary just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, this function gathers and prints some basic information about the sizes of the tables in the Hons Space. This information may be useful for deciding if you want to garbage collect using functions such as hons-clear or hons-wash. It may also be useful for determining good initial sizes for the Hons Space hash tables for your particular computation; see *note HONS-RESIZE::.  File: acl2-doc-emacs.info, Node: HONS-WASH, Next: MAKE-FAST-ALIST, Prev: HONS-SUMMARY, Up: HONS-AND-MEMOIZATION HONS-WASH (hons-wash) is like gc$ but can also garbage collect normed objects (CCL Only). This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, hons-wash just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it. Under the hood, in CCL, hons-wash runs a garbage collection after taking special measures to allow normed conses to be collected. In Lisps other than CCL, hons-wash does nothing. This is because the efficient implementation of hons-wash is specific to the "static honsing" scheme which requires CCL-specific features. Why is this function needed? Ordinarily, it is not possible to garbage collect any normed conses. This is because the Hons Space includes pointers to any normed conses, and hence Lisp's garbage collector thinks these objects are live. To correct for this, hons-wash (1) clears out these pointers, (2) runs a garbage collection, then (3) re-norms any previously-normed conses which have survived the garbage collection. Note. It is not recommended to interrupt this function. Doing so may cause persistently normed conses and fast alist keys to become un-normed, which might lead to less efficient re-norming and/or violations of the fast-alist discipline.  File: acl2-doc-emacs.info, Node: MAKE-FAST-ALIST, Next: MEMOIZE-SUMMARY, Prev: HONS-WASH, Up: HONS-AND-MEMOIZATION MAKE-FAST-ALIST (make-fast-alist alist) creates a fast-alist from the input alist, returning alist itself or, in some cases, a new object equal to it. Note: it is often better to use with-fast-alist; see *note WITH-FAST-ALIST::. Logically, make-fast-alist is the identity function. Under the hood, we construct and return an object that is equal to alist and which is a fast alist. If alist is already a fast alist, almost no work is required: we simply return it unchanged. When alist is not fast, we must minimally construct a hash table for its bindings. It is often possible to bind this new hash table to alist itself. But in certain cases when the alist keys are not normed, a new alist must be constructed, also, and so we may return an equal but not eq alist. (In these cases, we still try to avoid at least some consing by reusing the "longest normed tail" of the alist.)  File: acl2-doc-emacs.info, Node: MEMOIZE-SUMMARY, Next: MEMSUM, Prev: MAKE-FAST-ALIST, Up: HONS-AND-MEMOIZATION MEMOIZE-SUMMARY display all collected profiling and memoization table info This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, this function just returns nil, but it displays profiling and memoization table information. The profiling statistics may be cleared with (clear-memoize-statistics).  File: acl2-doc-emacs.info, Node: MEMSUM, Next: NEVER-MEMOIZE, Prev: MEMOIZE-SUMMARY, Up: HONS-AND-MEMOIZATION MEMSUM display all collected profiling and memoization info This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. This macro is an abbreviation for memoize-summary. Logically, it just returns nil.  File: acl2-doc-emacs.info, Node: NEVER-MEMOIZE, Next: NORMED, Prev: MEMSUM, Up: HONS-AND-MEMOIZATION NEVER-MEMOIZE Mark a function as unsafe to memoize. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Logically, this function just returns nil. But execution of (never-memoize fn) records that fn must never be memoized, so that any attempt to memoize fn will fail. Any function can be marked as unsafe to memoize; in fact, fn need not even be defined at the time it is marked. This is useful for prohibiting the memoization of functions that are known to involve destructive functions like nreverse.  File: acl2-doc-emacs.info, Node: NORMED, Next: NUMBER-SUBTREES, Prev: NEVER-MEMOIZE, Up: HONS-AND-MEMOIZATION NORMED Normed objects are ACL2 Objects that are "canonical" or "unique" in a certain sense. This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. In Common Lisp, we can tell whether an ACL2 object is *normed* or not, but there is no way for an ordinary ACL2 function to see whether an object is normed. Hence, whether or not an object is normed is an implementation-level concept. The fundamental property of normed objects is that if A and B are both normed, then (equal A B) holds if and only if (eql A B) holds. For strings and conses, (eql A B) holds only when (eq A B), so any normed conses or strings are equal precisely when they are eq. The potential benefits of having normed objects include: constant-time equality comparisons, reduced memory usage, fast association lists, and function memoization. Note that in our implementation, all symbols, characters, and numbers are automatically normed, and whenever a cons is normed its car and cdr must also be normed. The Meaning of Normed in Common Lisp. Recall that the ACL2 Objects are certain "good" Common Lisp symbols, characters, strings, and numbers, and the conses which can be recursively formed from these good atoms. Not all properties of these objects are visible in the ACL2 logic. An example of an invisible property is the pointer identity of an object. For example, suppose we write the following. (defconst *x* (cons 1 2)) (defconst *y* (cons 1 2)) In Common Lisp, cons is guaranteed to provide a new pair that is distinct from any previously created pair, so we know that *x* and *y* will be distinct objects and we will be able to tell them apart from one another using eq. But this difference is not visible in the ACL2 logic, and no ACL2 function can tell *x* apart from *y*. The normed-ness of an object is a similarly invisible property. In Common Lisp, invisible to ACL2, we maintain a data structure called a "Hons Space" that records which objects are normed. So, being normed is not an intrinsic property of an object, but instead is determined by whether the object is mentioned in this Hons Space. Notes about Garbage Collection. The Hons Space includes tables with pointers to every normed cons and string. One consequence of this is that normed objects cannot be reclaimed by Lisp's ordinary garbage collector, even after they have become unreachable from the perspective of an ACL2 program. For CCL users only, hons-wash is a special kind of garbage collection that allows normed conses to be reclaimed. For other Lisps, the only option is to occasionally, manually clear out these Hons Space tables with hons-clear.  File: acl2-doc-emacs.info, Node: NUMBER-SUBTREES, Next: SLOW-ALIST-WARNING, Prev: NORMED, Up: HONS-AND-MEMOIZATION NUMBER-SUBTREES (number-subtrees x) returns the number of distinct subtrees of X, in the sense of equal This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. In the logic, number-subtrees is defined as the length of cons-subtrees. Under the hood, we first hons-copy X to obtain a normed version, then count the number of unique conses in X using an EQ hash table.  File: acl2-doc-emacs.info, Node: SLOW-ALIST-WARNING, Next: WITH-FAST-ALIST, Prev: NUMBER-SUBTREES, Up: HONS-AND-MEMOIZATION SLOW-ALIST-WARNING warnings issued when fast-alists are used inefficiently This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see *note HONS-AND-MEMOIZATION::. Obtaining hash-table performance from hons-get requires one to follow a certain discipline. If this discipline is violated, you may see a "slow alist warning". This warning means that the alist you are extending or accessing does not have a valid hash table associated with it, and hence any accesses must be carried out with hons-assoc-equal instead of gethash. You can control whether or not you get a warning and, if so, whether or not a break (an error from which you can continue) ensues. For instance: (set-slow-alist-action :warning) ; warn on slow access (default) (set-slow-alist-action :break) ; warn and also call break$ (set-slow-alist-action nil) ; do not warn or break The above forms expand to table events, so they can be embedded in encapsulates and books, wrapped in local, and so on.  File: acl2-doc-emacs.info, Node: WITH-FAST-ALIST, Next: WITH-STOLEN-ALIST, Prev: SLOW-ALIST-WARNING, Up: HONS-AND-MEMOIZATION WITH-FAST-ALIST (with-fast-alist name form) causes name to be a fast alist for the execution of form. Logically, with-fast-alist just returns form. Under the hood, we cause alist to become a fast alist before executing form. If doing so caused us to introduce a new hash table, the hash table is automatically freed after form completes. More accurately, under the hood (with-fast-alist name form) essentially expands to something like: (if (already-fast-alist-p name) form (let (( (make-fast-alist name))) (prog1 form (fast-alist-free )))) Practically speaking, with-fast-alist is frequently a better choice then just using make-fast-alist, and is particularly useful for writing functions that can take either fast or slow alists as arguments. That is, consider the difference between: (defun bad (alist ...) (let* ((fast-alist (make-fast-alist alist)) (answer (expensive-computation fast-alist ...))) (prog2$ (fast-alist-free fast-alist) answer))) (defun good (alist ...) (with-fast-alist alist (expensive-computation alist ...))) Either approach is fine if the caller provides a slow alist. But if the input alist is already fast, bad will (perhaps unexpectedly) free it! On the other hand, good is able to take advantage of an already-fast argument and will not cause it to be inadvertently freed. See also the macro with-fast-alists defined in the community book "books/centaur/misc/hons-extra.lisp", which allows you to call with-fast-alist on several alists simultaneously. The community book "books/centaur/misc/hons-extra.lisp" extends the b* macro (defined in the community book "books/tools/bstar.lisp") with the with-fast pattern binder. That is, after executing (include-book "centaur/misc/hons-extra.lisp" :dir :system) you may write something like this: (b* (... ((with-fast a b c ...)) ...) ...) which causes a, b, and c to become fast alists until the completion of the b* form. Note that with-fast-alist will cause logically tail-recursive functions not to execute tail-recursively if its cleanup phase happens after the tail-recursive call returns.  File: acl2-doc-emacs.info, Node: WITH-STOLEN-ALIST, Prev: WITH-FAST-ALIST, Up: HONS-AND-MEMOIZATION WITH-STOLEN-ALIST (with-stolen-alist name form) ensures that name is a fast alist at the start of the execution of form. At the end of execution, it ensures that name is a fast alist if and only if it was originally. That is, if name was not a fast alist originally, its hash table link is freed, and if it was a fast alist originally but its table was modified during the execution of form, that table is restored. Note that any extended table created from the original fast alist during form must be manually freed. Logically, with-stolen-alist just returns form. Under the hood, we cause alist to become a fast alist before executing form, and we check the various conditions outlined above before returning the final value. Note that with-stolen-alist will cause logically tail-recursive functions not to execute tail-recursively if its cleanup phase happens after the tail-recursive call returns.  File: acl2-doc-emacs.info, Node: INTRODUCTION-TO-THE-TAU-SYSTEM, Next: IO, Prev: HONS-AND-MEMOIZATION, Up: Top INTRODUCTION-TO-THE-TAU-SYSTEM a decision procedure for runtime types This doc topic is the main source of information about the tau system and discusses the general idea behind the procedure and how to exploit it. A "Type-Checker" for an Untyped Language Because ACL2 is an untyped language it is impossible to type check it. All functions are total. An n-ary function may be applied to any combination of n ACL2 objects. The syntax of ACL2 stipulates that (fn a1...an) is a well-formed term if fn is a function symbol of n arguments and the ai are well-formed terms. No mention is made of the "types" of terms. That is what is meant by saying ACL2 is an untyped language. Nevertheless, the system provides a variety of monadic Boolean function symbols, like natp, integerp, alistp, etc., that recognize different "types" of objects at runtime. Users typically define many more such recognizers for domain-specific "types." Because of the prevalence of such "types," ACL2 must frequently reason about the inclusion of one "type" in another. It must also reason about the consequences of functions being defined so as to produce objects of certain "types" when given arguments of certain other "types." Because the word "type" in computer science tends to imply syntactic or semantic restrictions on functions, we avoid using that word henceforth. Instead, we just reason about monadic Boolean predicates. You may wish to think of "tau" as synonymous with "type" but without any suggestion of syntactic or semantic restrictions. * Menu: * DEALING-WITH-TAU-PROBLEMS:: some advice on dealing with problems caused by the tau system * FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM:: some tau system problems that we hope someone will address Related topics other than immediate subtopics: * REGENERATE-TAU-DATABASE:: regenerate the tau database relative to the current enabled theory * SET-TAU-AUTO-MODE:: turn on or off automatic (``greedy'') generation of :tau-system rules * TAU-DATA:: to see what tau knows about a function symbol * TAU-DATABASE:: to see the tau database as a (very large) object * TAU-STATUS:: query or set tau system status * TAU-SYSTEM:: make a rule for the ACL2 ``type checker'' Design Philosophy The following basic principles were kept in mind when developing tau checker and may help you exploit it. (1) The tau system is supposed to be a lightweight, fast, and helpful decision procedure for an elementary subset of the logic focused on monadic predicates and function signatures. (2) Most subgoals produced by the theorem prover are not in any decidable subset of the logic! Thus, decision procedures fail to prove the vast majority of the formulas they see and will be net time-sinks if tried too often no matter how fast they are. Tau reasoning is used by the prover as part of preprocess-clause, one of the first proof techniques the system tries. The tau system filters out "obvious" subgoals. The tau system is only tried when subgoals first enter the waterfall and when they are stable under simplification. (3) The tau system is "benign" in the sense that the only way it contributes to a proof is to eliminate (prove!) subgoals. It does not rewrite, simplify, or change formulas. Tau reasoning is not used by the rewriter. The tau system either eliminates a subgoal by proving it or leaves it unchanged. (4) It is impossible to infer automatically the relations between arbitrary recursively defined predicates and functions. Thus, the tau system's knowledge of tau relationships and function signatures is gleaned from theorems stated by the user and proved by the system. (5) Users wishing to build effective "type-checkers" for their models must learn how rules affect the tau system's behavior. There are two main forms of tau rules: those that reveal inclusion/exclusion relations between named tau predicates, e.g., that 16-bit naturals are also 32-bit naturals, (implies (n16p x) (n32p x)), and signatures for all relevant functions, e.g., writing a 32-bit natural to a legal slot in a register file produces a register file: (implies (and (natp n) (< n 16) (n32p val) (register-filep regs)) (register-filep (update-nth n val regs))). For a complete description of acceptable forms see :tau-system. (6) The tau system is "greedy" in its efforts to augment its database. Its database is potentially augmented when rules of any :rule-class (see :rule-classes) are proved. For example, if you make a :rewrite or :type-prescription rule which expresses a relationship between one tau and another (e.g., that (P x) implies (Q x)), ACL2 will build it into the tau database. The rule-class :tau-system can be used to add a rule to the tau database without adding any other kind of rule. (7) Greediness is forced into the design by benignity: the tau system may "know" some fact that the rewriter does not, and because tau reasoning is not used in rewriting, that missing fact must be conveyed to the rewriter through some other class of rule, e.g., a :rewrite or :type-prescription or :forward-chaining rule. By making the tau system greedy, we allow the user to program the rewriter and the tau system simultaneously while keeping them separate. However, this means you must keep in mind the effects of a rule on both the rewriter and the tau system and use :tau-system rules explicitly when you want to "talk" just to the tau system. (8) Tau rules are built into the database with as much preprocessing as possible (e.g., the system transitively closes inclusion/exclusion relationships at rule-storage time) so the checker can be fast. (9) For speed, tau does not track dependencies and is not sensitive to the enabled/disabled status (see enable and disable) of rules. Once a fact has been built into the tau database, the only way to prevent that fact from being used is by disabling the entire tau system, by disabling (:executable-counterpart tau-system). If any tau reasoning is used in a proof, the rune (:executable-counterpart tau-system) is reported in the summary. For a complete list of all the runes in the tau database, evaluate (global-val 'tau-runes (w state)). Any of these associated theorems could have been used. These design criteria are not always achieved! For example, the tau system's "greediness" can be turned off (see set-tau-auto-mode), the tau database can be regenerated from scratch to ignore disabled rules (see regenerate-tau-database), and disabling the executable-counterpart of a tau predicate symbol will prevent the tau system from trying to run the predicate on constants. The tau system's benignity can be frustrating since it might "know" something the rewriter does not. More problematically, the tau system is not always "fast" and not always "benign!" The typical way tau reasoning can slow a proof down is by evaulating expensive tau predicates on constants. The typical way tau reasoning can hurt a previously successful proof is by proving some subgoals (!) and thus causing the remaining subgoals to have different clause-identifiers, thus making explicit hints no longer applicable. We deal with such problems in dealing-with-tau-problems. Technical Details The tau system consists of both a database and an algorithm for using the database. The database contains theorems that match certain schemas allowing them to be stored in the tau database. Roughly speaking the schemas encode "inclusion" and "exclusion" relations, e.g., that natp implies integerp and that integerp implies not consp, and they encode "signatures" of functions, e.g., theorems that relate the output of a function to the input, provided only tau predicates are involved. By "tau predicates" we mean the application of a monadic Boolean-valued function symbol, the equality of something to a quoted constant, an arithmetic ordering relation between something and a rational constant, or the logical negation of such a term. Here are some examples of tau predicates: (natp i) (not (consp x)) (equal y 'MONDAY) (not (eql 23 k)) (< 8 max) (<= max 24) Synonyms for equal include =, eq, and eql. Note that negated equalites are also allowed. The arithmetic ordering relations that may be used are <, <=, >=, and >. One of the arguments to every arithmetic ordering relation must be an integer or rational constant for the term to be treated as a tau predicate. A "tau" is a data object representing a set of signed (positive or negative) tau predicates whose meaning is the conjunction of the literals in the set. When we say that a term "has" a given tau we mean the term satisfies all of the recognizers in that tau. The tau algorithm is a decision procedure for the logical theory described (only) by the rules in the database. The algorithm takes a term and a list of assumptions mapping subterms (typically variable symbols) to tau, and returns the tau of the given term. When the system is called upon to decide whether a term satisfies a given monadic predicate, it computes the tau of the term and asks whether the predicate is in that set. More generally, to determine if a term satisfies a tau, s, we compute a tau, r, for the term and ask whether s is a subset of r. To determine whether a constant, c, satisfies tau s we apply each of the literals in s to c. Evaluation might, of course, be time-consuming for complex user-defined predicates. The tau database contains rules derived from definitions and theorems stated by the user. See :tau-system for a description of the acceptable forms of tau rules. To shut off the greedy augmentation of the tau database, see *note SET-TAU-AUTO-MODE::. This may be of use to users who wish to tightly control the rules in the tau database. To add a rule to the tau database without adding any other kind of rule, use the rule class :tau-system. There are some slight complexities in the design related to how we handle events with both :tau-system corollaries and corollaries of other :rule-classes, see set-tau-auto-mode. To prevent tau reasoning from being used, disable the :executable-counterpart of tau-system, i.e., execute (in-theory (disable (:executable-counterpart tau-system))) or, equivalently, (in-theory (disable (tau-system))) To prevent tau from being used in the proof of a particular subgoal, locally disable the :executable-counterpart of tau-system with a local :in-theory hint (see *note HINTS::). The event command tau-status is a macro that can be used to toggle both whether tau reasoning is globally enabled and whether the tau database is augmented greedily. For example, the event (tau-status :system nil :auto-mode nil) prevents the tau system from being used in proofs and prevents the augmentation of the tau database by rules other than those explicitly labeled :tau-system. To see what the tau system "knows" about a given function symbol see *note TAU-DATA::. To see the entire tau database, see *note TAU-DATABASE::. To regenerate the tau database using only the runes listed in the current enabled theory, see *note REGENERATE-TAU-DATABASE::.  File: acl2-doc-emacs.info, Node: DEALING-WITH-TAU-PROBLEMS, Next: FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM, Prev: INTRODUCTION-TO-THE-TAU-SYSTEM, Up: INTRODUCTION-TO-THE-TAU-SYSTEM DEALING-WITH-TAU-PROBLEMS some advice on dealing with problems caused by the tau system For background on the tau system, see *note INTRODUCTION-TO-THE-TAU-SYSTEM::. The two most common problems caused by the tau system have to do with the system's interaction with "legacy" proof scripts. Such scripts may suffer because they were not designed to exploit tau reasoning and which may configure the tau database in quite incomplete and arbitrary ways. The two most common problems we have seen are (a) significant slow downs in a few proofs and (b) failed proof attempts due to hints being misapplied because the tau system caused subgoals to be renumbered. We discuss the rather limited means of dealing with these problems here. In future-work-related-to-the-tau-system we list some major inadequacies of the tau system. If the tau system contributes to a proof, the rune (:executable-counterpart tau-system) will be listed among the Rules in the Summary. However, merely by being attempted the tau system can slow down proofs in which it makes no contribution. The most brutal and fool-proof way to isolate a proof from the tau system is to disable the entire system. This can be done globally by (in-theory (disable (tau-system))) ; (:executable-counterpart tau-system) or locally with a subgoal specific hint: ... :hints (("...subgoal id..." :in-theory (disable (tau-system)))) Conducting a proof with and without the participation of the tau system can help you determine whether tau reasoning is helping or hurting. Dealing with Slowdowns The time-tracker utility was added to allow users to investigate whether excessive amounts of time are being spent in a given function. It was then used to annotate the code for the tau system as described in time-tracker-tau. The result is that if "excessive" time is spent in tau reasoning, messages to that effect will be printed to the proof log. The question is: aside from disabling the tau system how can the proof be sped up? There are two common causes of slowdown in the tau system. The first stems from the system's use of :executable-counterparts to determine whether a constant has a given tau. Recall that a tau is a conjunction of monadic predicates. To determine whether some constant satisfies the tau, the predicates are executed. If you have a hard-to-compute predicate this can be very slow. The most typical such predicates in ACL2 applications are those that check invariants, e.g., that recognize "good states" or "well-formed data." These are often written inefficiently because they are intended only for used in theorems and, before the tau system was added, they may have never been applied to constants. The most common constants tau predicates are applied to are 0, T, and NIL, although different models may stress other constants. To understand why NIL for example is frequently tested, if the test of an IF-expression is computed to have tau s then the next question we ask is "does nil satisfy s?" You may determine whether the tau system is spending time executing tau predicates by observing the rewriter -- see *note DMR:: -- or by interrupting the system and getting a backtrace (see *note SET-DEBUGGER-ENABLE::). If excessive time is being spent in a tau predicate, a draconian solution is to disable the :executable-counterpart of that predicate, for example in either of these equivalent ways. The tau system does not execute disabled :executable-counterparts. (in-theory (disable (:executable-counterpart foo))) (in-theory (disable (foo))) In either case above, you may prefer to provide local :in-theory :hints rather than :in-theory events. Disabling the executable counterpart of expensive tau predicates will weaken the tau system, probably only negligibly, because it can no longer run the predicates to determine whether they admits given constants. A more sophisticated solution is to make the tau system record values of the :logic-mode function in question, so that the system will look up the necessary values rather than running the function every time the question arises. It will look up recorded values whether the executable counterpart of the tau predicate is enabled or disabled. Here is an example of a lemma that can provide such a solution. See the discussion of the Eval form of :tau-system rules. (defthm lemma (and (foo 0) (foo 17) (foo t) (not (foo '(a b c)))) :rule-classes :tau-system) It might be difficult to determine which constants are being repeatedly tested, although tracing (trace$) suspected tau predicates will show what they are being called on. At the moment there are no better user-level tools to discover this. However, some users may wish to consider the following hack: In the ACL2 source file tau.lisp, immediately after the definition of the system function ev-fncall-w-tau-recog, there is a comment which contains some raw Lisp code that can be used to investigate whether tau's use of evaluation on constants is causing a problem and to determine which constants are involved. The second main cause of slowdowns by the tau system is that the system contains "too many" conjunctive rules (see the Conjunctive form in tau-system). Unfortunately, we have no tools for either identifying the problem or addressing it! That said, let us tell you what we do know! Conjunctive rules are used to "complete" each tau as it is built. Referring to the weekdayp example in tau-system, if a tau is constructed that recognizes weekdays but not MON, TUE, THU, or FRI, it is completed by adding that the tau recognizes (only) WED. This means that when we construct a tau we scan all known conjunctive rules and see whether all but one of the literals of any conjunctive rule are present. This can be expensive. To mitigate this expense, the tau system caches the computation on a per proof basis (the cache is cleared after every proof). To learn what conjunctive rules there are in your system, evaluate (assoc 'tau-conjunctive-rules (tau-database (w state))) Perhaps by sending the implementors that list, we can think of ways to index the conjunctive rules to save time. Dealing with Misapplied Hints The second common problem caused by the tau system in legacy proof scripts is that it can cause subgoals to be renumbered and thus cause hints to be missed. The only ways to address this problem is either to disable the tau system (locally or globally by disabling (:executable-counterpart tau-system)) or change the legacy hints to use the new subgoal names.  File: acl2-doc-emacs.info, Node: FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM, Prev: DEALING-WITH-TAU-PROBLEMS, Up: INTRODUCTION-TO-THE-TAU-SYSTEM FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM some tau system problems that we hope someone will address The tau system is inexpressive compared to modern polymorphic type systems -- something that may be unavoidable in an untyped first-order language. However, we believe its effectiveness could be greatly increased by the development of some additional tools. We also believe that most of these tools could be provided by ACL2 users creating certified community books that exploit the basic tools already provided. It is likely the initial attempts to create such books will show the inadequacy of some of the current algorithms and data structures in the tau system and might point the way to improvements. As implementors of ACL2 our preference is to support the improvement of the core algorithms and data structures and provide customizable hooks allowing users to exploit them by developing effective and convenient interfaces. However, we want the further elaboration and optimization of the tau system to be based on user experience not just speculation. Some tools we think might help are listed below. We invite volunteers and further suggestions. A tau-centric signature notation for use in function definitions, exploited by a macro replacing defun so that input-output relationships phrased in tau terms are proved as :tau-system rules immediately after functions are introduced: We have, for example, experimented with a book defining a macro that allows the definition of the function ap (append) accompanied by a signature rule. Subsequent defsig events can add other signatures in the same notation. (defsig ap (true-listp * true-listp ==> true-listp) (x y) (if (consp x) (cons (car x) (ap (cdr x) y)) y)) (defsig ap (integer-listp * integer-listp ==> integer-listp)) This experimental book provides succinct syntax for all tau signatures. For example, that book parses the signature (NATP (/= 3 5 7) (< 16) * TRUE-LISTP ==> BOOLEANP * INTEGER-LISTP * NATP) to be the signature of a function with two inputs and three outputs. The first input must be a natural number, different from 3, 5, and 7, and less than 16. The second input must be a true list. The first output is a boolean, the second a list of integers, and the third a natural. To express this signature for function fn as a formula requires significantly more typing by the user: (IMPLIES (AND (NATP X) (NOT (EQUAL X 3)) (NOT (EQUAL X 5)) (NOT (EQUAL X 7)) (< X 16) (TRUE-LISTP Y)) (AND (BOOLEANP (MV-NTH 0 (FN X Y))) (INTEGER-LISP (MV-NTH 1 (FN X Y))) (NATP (MV-NTH 2 (FN X Y))))) We suspect that the provision of some succinct tau-centric notation (not necessarily the one above) for signatures at definition-time will mean users get more benefit from the tau system. Some tau inference mechanisms: This phrase suggests two different improvements. One is to implement a mechanism that adds or completes signatures for function definitions by exploiting knowledge of commonly used recursive schemas and the signatures of the subroutines in the body. For example, the definition of ap above rather obviously has the signature (integer-listp * integer-listp ==> integer-listp) and many others just based on the two recursive schemas that (a) collect certain elements from lists and (b) check that all elements have a certain property. The other "tau inference" improvement is to implement a mechanism for inferring relations between user-defined Booleans, perhaps by exploiting example generation, testing, and knowledge of recursive schemas. For example, it is fairly obvious that symbol-alistp implies alistp. Making the user state these relations invites omissions that render the tau system very unpredictable. A tau assistant: It would be useful to have a way to find out what tau rules are missing. Given a term that the user believes should "obviously" have some tau ("type") what rules might be added to make the tau algorithm compute that expected tau? For example, if (g x) is known to satisfy P and (f x) is known to satisfy R when its argument satisfies S: g : T ==> P f : S ==> R then if the user asserts that (f (g x)) "ought" to have tau R, one plausible suggestion is the simple tau rule that (P x) implies (S x). Such assistance -- while still confronting an undecidable problem -- might be easier to implement within the tau framework than more generally in ACL2. (Many users have wanted such an assistant to suggest lemmas for the rewriter.)  File: acl2-doc-emacs.info, Node: IO, Next: MISCELLANEOUS, Prev: INTRODUCTION-TO-THE-TAU-SYSTEM, Up: Top IO input/output facilities in ACL2 Example: (mv-let (channel state) (open-input-channel "foo.lisp" :object state) (mv-let (eofp obj state) (read-object channel state) (. . (let ((state (close-input-channel channel state))) (mv final-ans state))..))) Also see *note FILE-READING-EXAMPLE::. For advanced ways to control printing, see *note PRINT-CONTROL::. For a discussion of formatted printing, see *note FMT::. To control ACL2 abbreviation ("evisceration") of objects before printing them, see *note SET-EVISC-TUPLE::, see *note WITHOUT-EVISC::, and see *note SET-IPRINT::. To redirect output to a file, see *note OUTPUT-TO-FILE::. * Menu: * *STANDARD-CI*:: an ACL2 character-based analogue of CLTL's *standard-input* * *STANDARD-CO*:: the ACL2 analogue of CLTL's *standard-output* * *STANDARD-OI*:: an ACL2 object-based analogue of CLTL's *standard-input* * CHARACTER-ENCODING:: how bytes are parsed into characters * EVISC-TUPLE:: control suppression of details when printing * EVISCERATE-HIDE-TERMS:: to print (hide ...) as * EXTERNAL-FORMAT:: See *note CHARACTER-ENCODING::. * IPRINT:: See *note SET-IPRINT::. * IPRINTING:: See *note SET-IPRINT::. * OPEN-OUTPUT-CHANNEL!:: when trust tags are needed to open output channels * OUTPUT-TO-FILE:: redirecting output to a file * PRINT-CONTROL:: advanced controls of ACL2 printing * PRINTING-TO-STRINGS:: printing to strings instead of files or standard output * RESET-PRINT-CONTROL:: See *note PRINT-CONTROL::. * SET-EVISC-TUPLE:: control suppression of details when printing * SET-FMT-HARD-RIGHT-MARGIN:: set the right margin for formatted output * SET-FMT-SOFT-RIGHT-MARGIN:: set the soft right margin for formatted output * SET-IPRINT:: control whether abbreviated output can be read back in * SET-PRINT-BASE:: control radix in which numbers are printed * SET-PRINT-CASE:: control whether symbols are printed in upper case or in lower case * SET-PRINT-CIRCLE:: See *note PRINT-CONTROL::. * SET-PRINT-ESCAPE:: See *note PRINT-CONTROL::. * SET-PRINT-LENGTH:: See *note PRINT-CONTROL::. * SET-PRINT-LEVEL:: See *note PRINT-CONTROL::. * SET-PRINT-LINES:: See *note PRINT-CONTROL::. * SET-PRINT-RADIX:: control printing of the radix for numbers * SET-PRINT-READABLY:: See *note PRINT-CONTROL::. * SET-PRINT-RIGHT-MARGIN:: See *note PRINT-CONTROL::. * WITHOUT-EVISC:: print output in full Related topics other than immediate subtopics: * PRINC$:: print an atom ACL2 supports input and output facilities equivalent to a subset of those found in Common Lisp. ACL2 does not support random access to files or bidirectional streams. In Common Lisp, input and output are to or from objects of type stream. In ACL2, input and output are to or from objects called "channels," which are actually symbols. Although a channel is a symbol, one may think of it intuitively as corresponding to a Common Lisp stream. Channels are in one of two ACL2 packages, "ACL2-INPUT-CHANNEL" and "ACL2-OUTPUT-CHANNEL". When one "opens" a file one gets back a channel whose symbol-name is the file name passed to "open," postfixed with -n, where n is a counter that is incremented every time an open or close occurs. There are three channels which are open from the beginning and which cannot be closed: acl2-input-channel::standard-character-input-0 acl2-input-channel::standard-object-input-0 acl2-input-channel::standard-character-output-0 All three of these are really Common Lisp's *standard-input* or *standard-output*, appropriately. For convenience, three global variables are bound to these rather tedious channel names: *standard-ci* *standard-oi* *standard-co* Common Lisp permits one to open a stream for several different kinds of io, e.g. character or byte. ACL2 permits an additional type called "object". In ACL2 an "io-type" is a keyword, either :character, :byte, or :object. When one opens a file, one specifies a type, which determines the kind of io operations that can be done on the channel returned. The types :character and :byte are familiar. Type :object is an abstraction not found in Common Lisp. An :object file is a file of Lisp objects. One uses read-object to read from :object files and print-object$ (or print-object$-ser) to print to :object files. (The reading and printing are really done with the Common Lisp read and print functions. For those familiar with read, we note that the recursive-p argument is nil.) The function read-object-suppress is logically the same as read-object except that read-object-suppress throws away the second returned value, i.e. the value that would normally be read, simply returning (mv eof state); under the hood, read-object-suppress avoids errors, for example those caused by encountering symbols in packages unknown to ACL2. File-names are strings. ACL2 does not support the Common Lisp type pathname. However, for the file-name argument of the output-related functions listed below, ACL2 supports a special value, :STRING. For this value, the channel connects (by way of a Common Lisp output string stream) to a string rather than to a file: as characters are written to the channel they can be retrieved by using get-output-stream-string$. Here are the names, formals and output descriptions of the ACL2 io functions. Input Functions: (open-input-channel (file-name io-type state) (mv channel state)) (open-input-channel-p (channel io-type state) boolean) (close-input-channel (channel state) state) (read-char$ (channel state) (mv char/nil state)) ; nil for EOF (peek-char$ (channel state) boolean) (read-byte$ (channel state) (mv byte/nil state)) ; nil for EOF (read-object (channel state) (mv eof-read-flg obj-read state)) (read-object-suppress (channel state) (mv eof-read-flg state)) Output Functions: (open-output-channel (file-name io-type state) (mv channel state)) (open-output-channel! (file-name io-type state) (mv channel state)) (open-output-channel-p (channel io-type state) boolean) (close-output-channel (channel state) state) (princ$ (obj channel state) state) (write-byte$ (byte channel state) state) (print-object$ (obj channel state) state) (print-object$-ser (obj serialize-character channel state) state) (fms (string alist channel state evisc-tuple) state) (fms! (string alist channel state evisc-tuple) state) (fmt (string alist channel state evisc-tuple) (mv col state)) (fmt! (string alist channel state evisc-tuple) (mv col state)) (fmt1 (string alist col channel state evisc-tuple) (mv col state)) (fmt1! (string alist col channel state evisc-tuple) (mv col state)) (cw (string arg0 arg1 ... argn) nil) (get-output-stream-string$ (channel state &optional (close-p 't) (ctx ''get-output-stream-string$)) (mv erp string state)) The "formatting" functions are particularly useful; see *note FMT:: and see *note CW::. In particular, cw prints to a "comment window" and does not involve the ACL2 state, so many may find it easier to use than fmt and its variants. The functions fms!, fmt!, and fmt1! are the same as their respective functions without the "!," except that the "!" functions are guaranteed to print forms that can be read back in (at a slight readability cost). When one enters ACL2 with (lp), input and output are taken from *standard-oi* to *standard-co*. Because these are synonyms for *standard-input* and *standard-output*, one can drive ACL2 io off of arbitrary Common Lisp streams, bound to *standard-input* and *standard-output* before entry to ACL2. The macro get-output-stream-string$ returns the string accumulated into the given channel. By default, a call of this macro closes the supplied output channel. However, a third argument is optional (default t), and if it evaluates to nil then the channel remains open. The fourth argument is an optional context, which generally evaluates to a symbol, for error reporting. The following example illustrates. ACL2 !> (mv-let (channel state) (open-output-channel :string :object state) (pprogn (print-object$-ser 17 nil channel state) (print-object$-ser '(a b (c d)) nil channel state) (er-let* ((str1 (get-output-stream-string$ channel state nil))) ; keep the channel open (pprogn (print-object$-ser 23 nil channel state) (print-object$-ser '((e f)) nil channel state) (er-let* ; close the channel ((str2 (get-output-stream-string$ channel state))) (value (cons str1 str2))))))) (" 17 (A B (C D))" . " 23 ((E F))") ACL2 !> Also see *note PRINTING-TO-STRINGS:: for a discussion of formatted printing functions such as fmt-to-string that do not take a channel or state argument and return a string. By default, symbols are printed in upper case when vertical bars are not required, as specified by Common Lisp. See *note SET-PRINT-CASE:: for how to get ACL2 to print symbols in lower case. By default, numbers are printed in radix 10 (base 10). See *note SET-PRINT-BASE:: for how to get ACL2 to print numbers in radix 2, 8, or 16. To see the guard of an IO function, or indeed any function, see *note ARGS:: or call the function guard; but some built-in functions (including some IO functions) will print the result using the variable STATE-STATE. While that is logically correct, if you want to execute the guard then you should replace that variable by STATE and also replace each built-in function symbol of the form xxx-p1 by corresponding function symbol xxx-p. Consider the following example. ACL2 !>:args princ$ Function PRINC$ Formals: (X CHANNEL STATE-STATE) Signature: (PRINC$ * * STATE) => STATE Guard: (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P1 STATE-STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P1 CHANNEL :CHARACTER STATE-STATE)) Guards Verified: T Defun-Mode: :logic Type: (CONSP (PRINC$ X CHANNEL STATE-STATE)) Documentation available via :DOC PRINC$ ACL2 !>(untranslate (guard 'princ$ nil (w state)) t (w state)) (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P1 STATE-STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P1 CHANNEL :CHARACTER STATE-STATE)) ACL2 !> If you want to execute the guard for princ$, then according to the suggestion above, you should consider the guard for (princ$ x channel state) to be as follows. (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P CHANNEL :CHARACTER STATE)) For example, we can check the guard for a given value and channel as follows. ACL2 !>(let ((x 3) (channel *standard-co*)) (AND (OR (ACL2-NUMBERP X) (CHARACTERP X) (STRINGP X) (SYMBOLP X)) (STATE-P STATE) (SYMBOLP CHANNEL) (OPEN-OUTPUT-CHANNEL-P CHANNEL :CHARACTER STATE))) T ACL2 !> Comment for advanced users: Function open-output-channel! is identical as a function to open-output-channel, except that the former may be called even during make-event expansion and clause-processor hints, but requires that there is an active trust tag (see *note DEFTTAG::). Finally, we note that the community book books/misc/file-io.lisp contains useful file io functions whose definitions illustrate some of the features described above.  File: acl2-doc-emacs.info, Node: *STANDARD-CI*, Next: *STANDARD-CO*, Prev: IO, Up: IO *STANDARD-CI* an ACL2 character-based analogue of CLTL's *standard-input* The value of the ACL2 constant *standard-ci* is an open character input channel that is synonymous to Common Lisp's *standard-input*. ACL2 character input from *standard-ci* is actually obtained by reading characters from the stream named by Common Lisp's *standard-input*. That is, by changing the setting of *standard-input* in raw Common Lisp you can change the source from which ACL2 reads on the channel *standard-ci*. See *note *STANDARD-CO*::.  File: acl2-doc-emacs.info, Node: *STANDARD-CO*, Next: *STANDARD-OI*, Prev: *STANDARD-CI*, Up: IO *STANDARD-CO* the ACL2 analogue of CLTL's *standard-output* The value of the ACL2 constant *standard-co* is an open character output channel that is synonymous to Common Lisp's *standard-output*. ACL2 character output to *standard-co* will go to the stream named by Common Lisp's *standard-output*. That is, by changing the setting of *standard-output* in raw Common Lisp you can change the actual destination of ACL2 output on the channel named by *standard-co*. Observe that this happens without changing the logical value of *standard-co* (which is some channel symbol). Changing the setting of *standard-output* in raw Common Lisp essentially just changes the map that relates ACL2 to the physical world of terminals, files, etc. To see the value of this observation, consider the following. Suppose you write an ACL2 function which does character output to the constant channel *standard-co*. During testing you see that the output actually goes to your terminal. Can you use the function to output to a file? Yes, if you are willing to do a little work in raw Common Lisp: open a stream to the file in question, set *standard-output* to that stream, call your ACL2 function, and then close the stream and restore *standard-output* to its nominal value. Similar observations can be made about the two ACL2 input channels, *standard-oi* and *standard-ci*, which are analogues of *standard-input*. Another reason you might have for wanting to change the actual streams associated with *standard-oi* and *standard-co* is to drive the ACL2 top-level loop, ld, on alternative input and output streams. This end can be accomplished easily within ACL2 by either calling ld on the desired channels or file names or by resetting the ACL2 state global variables 'standard-oi and 'standard-co which are used by ld. See *note STANDARD-OI:: and see *note STANDARD-CO::.  File: acl2-doc-emacs.info, Node: *STANDARD-OI*, Next: CHARACTER-ENCODING, Prev: *STANDARD-CO*, Up: IO *STANDARD-OI* an ACL2 object-based analogue of CLTL's *standard-input* The value of the ACL2 constant *standard-oi* is an open object input channel that is synonymous to Common Lisp's *standard-input*. ACL2 object input from *standard-oi* is actually obtained by reading from the stream named by Common Lisp's *standard-input*. That is, by changing the setting of *standard-input* in raw Common Lisp you can change the source from which ACL2 reads on the channel *standard-oi*. See *note *STANDARD-CO*::.  File: acl2-doc-emacs.info, Node: CHARACTER-ENCODING, Next: EVISC-TUPLE, Prev: *STANDARD-OI*, Up: IO CHARACTER-ENCODING how bytes are parsed into characters When the Common Lisp reader comes across bytes in a file or at the terminal, they are parsed into characters. The simplest case is when each byte that is read is a standard character (see *note STANDARD-CHAR-P::). It is actually quite common that each byte that is read corresponds to a single character. The parsing of bytes into characters is based on a _character encoding_, that is, a mapping that associates one or more bytes with each legal character. In order to help guarantee the portability of files (including books), ACL2 installs a common character encoding for reading files, often known as iso-8859-1 or latin-1. For some host Lisps this character encoding is also used for reading from the terminal; but, sadly, this may not hold for all host Lisps, and may not even be possible for some of them. The use of the above encoding could in principle cause problems if one's editor produces files using an encoding other than iso-8859-1, at least if one uses non-standard characters. In particular, the default Emacs buffer encoding may be utf-8. If your file has non-standard characters, then in Emacs you can evaluate the form (setq save-buffer-coding-system 'iso-8859-1) before saving the buffer into a file. This will happen automatically for users who load distributed file emacs/emacs-acl2.el into their Emacs sessions. For an example of character encodings in action, see the community book books/misc/character-encoding-test.lisp.  File: acl2-doc-emacs.info, Node: EVISC-TUPLE, Next: EVISCERATE-HIDE-TERMS, Prev: CHARACTER-ENCODING, Up: IO EVISC-TUPLE control suppression of details when printing ACL2 output is generally printed in full. However, ACL2 can be directed to abbreviate, or "eviscerate", objects before printing them. To "eviscerate" an object we replace certain substructures within it by strings that are printed in their stead. Such replacement is made relative to a so-called "evisc-tuple", which has four components: (evisc-tuple print-level print-length alist hiding-cars) is the same as the value of (list alist print-level print-length hiding-cars), and the components are used as follows (with priority order as discussed below). The alist component is used to replace any substructure occurring as a key by the corresponding string. The print-level and print-length are analogous to Common Lisp variables *print-level* and *print-length*, respectively, and cause replacement of substructures deeper than print-level by `#' and those longer than print-length by `...'. Finally, any consp x that starts with one of the symbols in hiding-cars is printed as . The following example illustrates the use of an evisc-tuple that limits the print-level to 3 -- only three descents into list structures are permitted before replacing a subexpression by `#' -- and limits the print-length to 4 -- only the first four elements of any list structure will be printed before replacing its tail by `...'. ACL2 !>(fms "~x0~%" (list (cons #\0 '((a b ((c d)) e f g) u v w x y))) *standard-co* state (evisc-tuple 3 4 nil nil)) ((A B (#) E ...) U V W ...) ACL2 !> Notice that it is impossible to read the printed value back into ACL2, since there is no way for the ACL2 reader to interpret `#' or `...'. To solve this problem, see *note SET-IPRINT::. In the above example we pass an evisc-tuple explicitly to a printing function, in this case, fms (see *note FMT::). But ACL2 also does its own printing, for example during a proof attempt. There are global evisc-tuples that control ACL2's printing; see *note SET-EVISC-TUPLE:: and see *note WITHOUT-EVISC::.  File: acl2-doc-emacs.info, Node: EVISCERATE-HIDE-TERMS, Next: EXTERNAL-FORMAT, Prev: EVISC-TUPLE, Up: IO EVISCERATE-HIDE-TERMS to print (hide ...) as Example: (assign eviscerate-hide-terms t) (assign eviscerate-hide-terms nil) Eviscerate-hide-terms is a state global variable whose value is either t or nil. The variable affects how terms are displayed by default (but not if you have set the term-evisc-tuple to other than its default; see *note SET-EVISC-TUPLE::). If t, terms of the form (hide ...) are printed as . Otherwise, they are printed normally.  File: acl2-doc-emacs.info, Node: EXTERNAL-FORMAT, Next: IPRINT, Prev: EVISCERATE-HIDE-TERMS, Up: IO EXTERNAL-FORMAT See *note CHARACTER-ENCODING::.  File: acl2-doc-emacs.info, Node: IPRINT, Next: IPRINTING, Prev: EXTERNAL-FORMAT, Up: IO IPRINT See *note SET-IPRINT::.  File: acl2-doc-emacs.info, Node: IPRINTING, Next: OPEN-OUTPUT-CHANNEL!, Prev: IPRINT, Up: IO IPRINTING See *note SET-IPRINT::.  File: acl2-doc-emacs.info, Node: OPEN-OUTPUT-CHANNEL!, Next: OUTPUT-TO-FILE, Prev: IPRINTING, Up: IO OPEN-OUTPUT-CHANNEL! when trust tags are needed to open output channels Use this function in place of open-output-channel if you want to open a channel for output at times this would otherwise be prohibited, for example during make-event expansion and clause-processor hints. If this functionality doesn't quite seem like what you need, take a look at the definition of open-output-channel! in axioms.lisp, specifically the binding of state global variable writes-okp. The following example, taken from community book books/hons-archive/hons-archive.lisp, illustrates the latter approach. (defmacro har-zip! (x filename &key sortp) "See :doc hons-archive" `(mv-let (erp val state) (progn! :state-global-bindings ((temp-touchable-vars t set-temp-touchable-vars)) (state-global-let* ((writes-okp t)) (let ((state (har-zip-fn ,x ,filename ,sortp state))) (mv nil nil state)))) (declare (ignore erp val)) state)) The book below illustrates the soundness loophole plugged in ACL2 Version_3.2 related to file writes during book certification. ; The following example is adapted (with only very slight changes) ; from one written by Peter Dillinger. It illustrates the prohibition ; against writing files enforced by with-output-channel during book ; certification (more specifically, during make-event expansion). ; This book certifies in ACL2 Version_3.1 before the fix discussed in the ; paragraph about it being ``possible to write files during book ; certification'' in :DOC NOTE-3-2. The fix was actually made to ACL2 ; function open-output-channel. ; After the fix, in order for certification to succeed one needs to do ; two things. First, in raw lisp: ; (push :after-writes-okp-fix *features*) ; Second, certify with this command: ; (certify-book "writes-okp" 0 nil :ttags (:writes-okp)) (in-package "ACL2") (local (defun write-objects-to-channel (obj-lst chan state) (declare (xargs :mode :program :stobjs state :guard (true-listp obj-lst))) (if (consp obj-lst) (pprogn (print-object$ (car obj-lst) chan state) (write-objects-to-channel (cdr obj-lst) chan state) state) state))) #+after-writes-okp-fix (defttag :writes-okp) (local (defun write-objects-to-file (obj-lst filename state) (declare (xargs :mode :program :stobjs state :guard (and (stringp filename) (true-listp obj-lst)))) (mv-let (chan state) #-after-writes-okp-fix (open-output-channel filename :object state) #+after-writes-okp-fix (open-output-channel! filename :object state) (if chan (pprogn (write-objects-to-channel obj-lst chan state) (close-output-channel chan state) (value :done)) (er soft 'write-object-to-file "Could not open for writing: ~x0" filename))))) (local (defconst *nil.lisp* '((in-package "ACL2") (defthm bad nil :rule-classes nil)))) (local (defconst *nil.cert* '((IN-PACKAGE "ACL2") "ACL2 Version 3.1" :BEGIN-PORTCULLIS-CMDS :END-PORTCULLIS-CMDS NIL (("/home/peterd/test/nil.lisp" "nil" "nil" ((:SKIPPED-PROOFSP) (:AXIOMSP) (:TTAGS)) . 134094174)) 62589544 ))) (local (make-event (er-progn (write-objects-to-file *nil.lisp* "nil.lisp" state) (write-objects-to-file *nil.cert* "nil.cert" state) (value '(value-triple :invisible))))) (local (include-book "nil" :load-compiled-file nil)) (defthm bad nil :rule-classes nil)  File: acl2-doc-emacs.info, Node: OUTPUT-TO-FILE, Next: PRINT-CONTROL, Prev: OPEN-OUTPUT-CHANNEL!, Up: IO OUTPUT-TO-FILE redirecting output to a file For a general discussion of ACL2 input/output and of the ACL2 read-eval-print loop, see *note IO:: and see *note LD:: (respectively). Here we use an example to illustrate how to use some of the options provided by ld to redirect ACL2 output to a file, other than the printing of the prompt (which continues to go to the terminal). There are two ld specials that control output from the ld command: proofs-co for proof output and standard-co for other output. The following example shows how to use these to redirect output to a file "tmp.out". The following command opens a character output channel to to the file "tmp.out" and redirects proof output to that channel, i.e., to file "tmp.out". (mv-let (chan state) (open-output-channel "tmp.out" :character state) (set-proofs-co chan state)) Next, we redirect standard output to that same channel. (set-standard-co (proofs-co state) state) Now we can load an input file, in this case file "tmp.lisp", and output will be redirected to file "tmp.out". (The use of :ld-pre-eval-print t is optional; see *note LD::.) (ld "tmp.lisp" :ld-pre-eval-print t) Having completed our load operation, we restore both proof output and standard output to the terminal, as follows. (set-standard-co *standard-co* state) (close-output-channel (proofs-co state) state) (set-proofs-co *standard-co* state) The following variant of the above example shows how to redirect output as above except without changing the global settings of the two ld specials, proofs-co and standard-co. This approach uses a notion of "global variables" stored in the ACL2 state; see *note ASSIGN:: and see *note @: atsign. (mv-let (chan state) (open-output-channel "tmp.out" :character state) (assign tmp-channel chan)) (ld "tmp.lisp" :ld-pre-eval-print t :proofs-co (@ tmp-channel) :standard-co (@ tmp-channel)) (close-output-channel (@ tmp-channel) state)  File: acl2-doc-emacs.info, Node: PRINT-CONTROL, Next: PRINTING-TO-STRINGS, Prev: OUTPUT-TO-FILE, Up: IO PRINT-CONTROL advanced controls of ACL2 printing See *note IO:: for a summary of printing in ACL2. Here we document some advanced ways to control what is printed by ACL2's primary printing routines. See *note SET-PRINT-BASE::, see *note SET-PRINT-RADIX::, and see *note SET-PRINT-CASE:: for discussions of the most common ways to control what is printed. Indeed, these are the only ways to control the behavior of princ$ and prin1$. The rest of this topic is for advanced users of ACL2. We refer to Common Lisp behavior, as described in any good Common Lisp documentation. *Print-control variables*. Set-print-base, set-print-radix, and set-print-case assign to corresponding so-called "state global variables" 'print-base, 'print-radix, and 'print-case, which can be accessed using the expressions (@ print-base), (@ print-radix), and (@ print-case), respectively; see *note ASSIGN::. Here is a table showing all print-control variables, their setters, and their defaults. print-base set-print-base 10 print-case set-print-case :upcase print-circle set-print-circle nil [but see remark on print-circle-files, below] print-escape set-print-escape t print-length set-print-length nil print-level set-print-level nil print-lines set-print-lines nil print-pretty set-print-pretty nil print-radix set-print-radix nil print-readably set-print-readably nil print-right-margin set-print-right-margin nil Each ACL2 print-control variable print-xxx can correspond in function to Common Lisp variable *PRINT-XXX*. Specifically, the evaluation of forms (set-print-base t), (set-print-radix t), and (set-print-case t) affects ACL2 printing functions in much the same way that setting to t Common Lisp variables *PRINT-BASE*, *PRINT-RADIX*, and *PRINT-CASE*, respectively, affects Common Lisp printing. The same is true for print-escape, except that this does not affect princ$ or prin1$, which correspond to Common Lisp functions princ and prin1: princ treats *PRINT-ESCAPE* as nil while prin1 treats *PRINT-ESCAPE* as t. Moreover, all print-control variables not mentioned in this paragraph are set to their defaults in princ$ and prin1$, as indicated by ACL2 constant *print-control-defaults*, except that print-readably is set to nil in princ$. Fmt and its related functions are sensitive to state globals 'print-base, 'print-radix, 'print-case, 'print-escape, and 'print-readably, in analogy with Common Lisp functions that don't fix *PRINT-ESCAPE* or *PRINT-READABLY*. But the fmt functions do not respect settings of other print-control variables; for example, they act as though 'print-circle is nil. Since ACL2 output is produced using the same underlying print routines as the fmt functions, it also is insensitive to all print-control variables except for the five above. To control the print-level and print-length used for producing ACL2 output, see *note SET-EVISC-TUPLE::. Print-object$ is sensitive to all of the print-control variables. Remark on print-circle-files: ACL2 typically binds 'print-circle to t before writing certificate files, or auxiliary files that are compiled when make-event forms are present in a book, or files in support of :comp commands. This binding allows for structure sharing that can keep these files from growing large. However, this behavior is defeated in GCL (Gnu Common Lisp), because of the small number of indices n available by default (1024) for the #n= reader macro. For the files described above, what actually happens is that 'print-circle is bound to the value of 'print-circle-files, which by default is t unless the underlying Lisp is GCL, in which case it is set to nil. See *note ASSIGN:: for how to set state globals such as 'print-circle-files. For example, if you build GCL with a larger number of #n= indices available, you may wish to restore the print-circle behavior for certificate files by following these instructions from Camm Maguire: This can trivially be revised to any larger constant by editing the following line of read.d and recompiling: #ifndef SHARP_EQ_CONTEXT_SIZE #define SHARP_EQ_CONTEXT_SIZE 500 #endif End of Remark. Evaluate (reset-print-control) to restore all print-control variables to their original settings, as stored in constant *print-control-defaults*. (Remark for those using ACL2 built on Gnu Common Lisp (GCL) versions that are non-ANSI, which as of October 2008 is all GCL versions recommended for ACL2: Note that Common Lisp variables *PRINT-LINES*, *PRINT-MISER-WIDTH*, *PRINT-READABLY*, *PRINT-PPRINT-DISPATCH*, and *PRINT-RIGHT-MARGIN* do not have any effect for such GCL versions.)  File: acl2-doc-emacs.info, Node: PRINTING-TO-STRINGS, Next: RESET-PRINT-CONTROL, Prev: PRINT-CONTROL, Up: IO PRINTING-TO-STRINGS printing to strings instead of files or standard output Each of ACL2's formatted printing functions, FM*, has an analoguous macro FM*-TO-STRING indicated below. These functions do not include a channel or state as an argument, and FM*-TO-STRING returns the string that FM* would print to the state, in place of state; see *note FMT::. The legal keyword arguments are described below. General Forms: result (fms-to-string string alist &key ...) ; string (fmt-to-string string alist &key ...) ; (mv col string) (fmt1-to-string string alist column &key ...) ; (mv col string) (fms!-to-string string alist &key ...) ; string (fmt!-to-string string alist &key ...) ; (mv col string) (fmt1!-to-string string alist column &key ...) ; (mv col string) The legal keyword arguments are as follows. They are all optional with a default of nil. Evisc-tuple is evaluated, and corresponds exactly to the evisc-tuple argument of the corresponding FM* function; see *note FMT::. Fmt-control-alist should typically evaluate to an alist that maps print-control variables to values; see *note PRINT-CONTROL::. Any alist mapping variables to values is legal, however. By default the print controls are set according to the value of constant *fmt-control-defaults*; fmt-control-alist overrides these defaults. For example, *fmt-control-defaults* sets the right margin just as it is set in the initial ACL2 state, by binding fmt-soft-right-margin and fmt-hard-right-margin to their respective defaults of *fmt-soft-right-margin-default* and *fmt-hard-right-margin-default*. The following example shows how you can override those defaults, in this case arranging to print flat by setting the right margin to a large number. (fmt-to-string "~x0" (list (cons #\0 (make-list 30))) :fmt-control-alist `((fmt-soft-right-margin . 10000) (fmt-hard-right-margin . 10000))) Iprint is typically nil, but t is also a legal value. See *note SET-IPRINT:: for the effect of value t, which however is local to this call of a FM*-TO-STRING function; the behavior if iprinting afterwards is not affected by this call. In particular, you will not be able to look at the values of iprint tokens printed by this call, so the value t is probably of limited utility at best. Also see *note IO:: for a discussion of the utility get-output-stream-string$, which allows for accumulating the results of more than one printing call into a single string but requires the use of state.  File: acl2-doc-emacs.info, Node: RESET-PRINT-CONTROL, Next: SET-EVISC-TUPLE, Prev: PRINTING-TO-STRINGS, Up: IO RESET-PRINT-CONTROL See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-EVISC-TUPLE, Next: SET-FMT-HARD-RIGHT-MARGIN, Prev: RESET-PRINT-CONTROL, Up: IO SET-EVISC-TUPLE control suppression of details when printing ACL2 output is generally printed in full. However, ACL2 can be directed to abbreviate, or "eviscerate", objects before printing them, though the use of a so-called "evisc-tuple". See *note EVISC-TUPLE:: for a discussion of evisc-tuples. The utility set-evisc-tuple modifies certain global evisc-tuples, as explained below, to affect the extent to which ACL2 eviscerates objects during printing, for example during proof output or when printing top-level evaluation results. General Form: (set-evisc-tuple evisc-tuple ; a legal evisc-tuple, or :DEFAULT :iprint val ; one of *iprint-actions* :sites sites ; either :ALL, or an element or a subset of the ; list (:TERM :LD :TRACE :ABBREV) where the value of :iprint is passed to set-iprint, :sites :all abbreviates :sites '(:TERM :LD :TRACE :ABBREV), and other documentation is provided below. Note that all arguments are evaluated. See *note WITHOUT-EVISC:: for how to avoid evisceration for ACL2 output. The following example illustrates an invocation of set-evisc-tuple that limits the print-level to 3 -- only three descents into list structures are permitted before eviscerating a subterm -- and limits the print-length to 4 -- only the first four elements of any list structure will be printed. ACL2 !>(set-evisc-tuple (evisc-tuple 3 ; print-level 4 ; print-length nil ; alist nil ; hiding-cars ) :iprint :same ; better yet, T :sites :all) (:TERM :LD :TRACE :ABBREV) ACL2 !>'((a b ((c d)) e f g) u v w x y) ((A B (#) E ...) U V W ...) ACL2 !> We recommend however using :iprint t so that eviscerated terms may be read back in; see *note SET-IPRINT::. Indeed, the :iprint argument is required as a reminder to the user to consider that issue, unless iprinting has been enabled at least once. If :sites or a required :iprint argument is omitted, however, ACL2 will query the user for the missing arguments rather than causing an error. ACL2 eviscerates by default only in a few cases, primarily in informational messages for errors, warnings, and queries (i.e., in the :EVISC case below). Users can modify the default behavior by supplying a suitable argument to set-evisc-tuple. The argument may be :default, which denotes the evisceration provided when ACL2 starts up. Otherwise that argument is an evisc-tuple, which is either nil (no evisceration) or as described above. Moreover, there are four evisc-tuple "evisceration contexts", each with its own evisceration control. The value returned by set-evisc-tuple indicates the evisceration contexts whose evisc-tuple has been set. The evisceration contexts are as follows, all of which use a default value of nil for the hiding-cars. Accessors are also shown for retrieving the corresponding evisc-tuple. o :TERM -- used for printing terms. The accessor is (term-evisc-tuple flg state), where flg is ignored if set-evisc-tuple has been called for :term with value other than :default, and otherwise (hence initially): a flg of nil indicates an evisc-tuple of nil, and otherwise the term-evisc-tuple has a print-level of 3 and print-length of 4. o :ABBREV -- used for printing informational messages for errors, warnings, and queries. Initially, the alist abbreviates the ACL2 world, print-level is 5, and print-level is 7. The accessor is (abbrev-evisc-tuple state). o :GAG-MODE -- used for printing induction schemes (and perhaps, in the future, for other printing) when gag-mode is on. If gag-mode is off, the value used for this evisc-tuple is (term-evisc-tuple nil state). But if gag-mode is on (i.e., (gag-mode) evaluates to a non-nil value), then with one exception, the value is an evisc-tuple or nil, to be used in gag-mode for printing of induction schemes and, during proofs, the "The non-trivial part of the guard conjecture". The exceptional value is t, which indicates that in gag-mode, no printing of induction schemes should be and the guard conjecture should be printed using (term-evisc-tuple t state). The accessor is (gag-mode-evisc-tuple state). o :LD -- used by the ACL2 read-eval-print loop. The accessor is (ld-evisc-tuple state). o :TRACE -- used for printing trace output. No accessor is available (though in raw Lisp, (trace-evisc-tuple) returns the trace-evisc-tuple). Each context ectx also has an updater, (set-ectx-evisc-tuple val state), where val is a legal value for set-evisc-tuple as described above: :default or an evisc-tuple (possibly nil). Note that the break-rewrite commands and the proof-checker generally do their printing using the term-evisc-tuple.  File: acl2-doc-emacs.info, Node: SET-FMT-HARD-RIGHT-MARGIN, Next: SET-FMT-SOFT-RIGHT-MARGIN, Prev: SET-EVISC-TUPLE, Up: IO SET-FMT-HARD-RIGHT-MARGIN set the right margin for formatted output In this documentation topic we discuss setting of both a "soft" and a "hard" right margin. Example Forms: (set-fmt-soft-right-margin 55 state) ; set soft right margin to 55 (set-fmt-hard-right-margin 68 state) ; set hard right margin to 68 Fmt and related functions insert linebreaks when lines get too long. A linebreak is inserted at an aesthetically appropriate point once the column exceeds the value of (@ fmt-soft-right-margin). If however the column exceeds the value of (@ fmt-hard-right-margin), then a linebreak is soon inserted. Such a "hard" linebreak follows the insertion of a backslash (\) character unless fmt!, fms!, or fmt1! is used, or state global write-for-read is true.  File: acl2-doc-emacs.info, Node: SET-FMT-SOFT-RIGHT-MARGIN, Next: SET-IPRINT, Prev: SET-FMT-HARD-RIGHT-MARGIN, Up: IO SET-FMT-SOFT-RIGHT-MARGIN set the soft right margin for formatted output See *note SET-FMT-HARD-RIGHT-MARGIN:: for a discussion of the soft and hard right margin for formatted output.  File: acl2-doc-emacs.info, Node: SET-IPRINT, Next: SET-PRINT-BASE, Prev: SET-FMT-SOFT-RIGHT-MARGIN, Up: IO SET-IPRINT control whether abbreviated output can be read back in When ACL2 pretty-prints large expressions using formatted printing (see *note FMT::), it may save time and space by printing tokens `#' or `...' in place of certain subexpressions. By default this only happens for a few settings such as error and warning messages; see *note SET-EVISC-TUPLE:: for controlling such elision in general. The full expression is unavailable to the user when `#' or `...' is printed, but that is easily solved by evaluating the form (set-iprint t) to enable a mode called "iprinting". Then, instead of printing `#' or `...', ACL2 prints `#@i#' for i = 1,2,3,... -- all in base 10. ACL2 can read back in such `#@i#' because under the hood, i is associated with its corresponding elided form. Thus the term "iprint" can be viewed as suggesting "interactive print" or "index print". We also think of "iprint" as suggesting "i printing", to suggest the printing of token `#@i#'. We call i the "iprint index" of that token. The following example should suffice to illustrate how to recover elided subexpressions. (Below this example we provide details that may be of interest only to advanced users.) Here we cause an error by defining a macro of one argument and then calling it with two arguments. By default, error messages abbreviate subexpressions deeper than level 5 with `#' and past the 7th element of a list with `...'. We see this behavior below. ACL2 !>(defmacro foo (x) x) Summary Form: ( DEFMACRO FOO ...) Rules: NIL Warnings: None Time: 0.02 seconds (prove: 0.00, print: 0.00, other: 0.02) FOO ACL2 !>(foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n)) ACL2 Error in macro expansion: Wrong number of args in macro expansion of (FOO ARG1 (A (B (C (D #))) H I J K L ...)). (See :DOC set-iprint to be able to see elided values in this message.) ACL2 !>(set-iprint t) ACL2 Observation in SET-IPRINT: Iprinting has been enabled. ACL2 !>(foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n)) ACL2 Error in macro expansion: Wrong number of args in macro expansion of (FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#)). ACL2 !>(acl2-count '(FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#))) 23 ACL2 !> Sometimes you may want to supply the abbreviated form not to compute with it, as in the acl2-count example just above, but so that you can see it. The macro without-evisc eliminates elision during printing. Below we show two ways to use this utility: first, we use it to print the elided form, and then, we use it instead on the original input form to print the entire error message. ACL2 !>(without-evisc '(FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#))) (FOO ARG1 (A (B (C (D (E (F (G)))))) H I J K L M N)) ACL2 !>(without-evisc (foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n))) ACL2 Error in macro expansion: Wrong number of args in macro expansion of (FOO ARG1 (A (B (C (D (E (F (G)))))) H I J K L M N)). ACL2 !> The documentation above probably suffices for most users. For those who want more details, below we detail all the ways to use the set-iprint utility. Example Forms: (set-iprint t) ; enable iprinting (elision with #@i@) (set-iprint nil) ; disable iprinting General Form: (set-iprint action ; t, nil, :reset, :reset-enable, or :same :soft-bound s ; initially 1000 :hard-bound h ; initially 10000) where all arguments are optional, but ACL2 queries for action if it is omitted. We defer the explanations of :soft-bound and :hard-bound. The values for action are as follows: T -- Enable iprinting. NIL -- Disable iprinting. :RESET -- Reset iprinting to its initial disabled state, so that when enabled, the first index i for which `#@i# is printed will be 1. Note that all stored information for existing iprint indices will be erased. :RESET-ENABLE -- Reset iprinting as with :reset, and then enable iprinting. :SAME -- Make no change to the iprinting state (other than setting the :soft-bound and/or :hard-bound if specified, as explained below). Immediately after a top-level form is read, hence before it is evaluated, a check is made for whether the latest iprint index exceeds a certain bound, (iprint-soft-bound state) -- 1000, by default. If so, then the (iprint-last-index state) is set back to 0. This soft bound can be changed to any positive integer k by calling set-iprint with :soft-bound k, typically (set-iprint :same :soft-bound k)]. The above "soft bound" is applied once for each top-level form, but you may want finer control by applying a bound after the pretty-printing of each individual form (since many forms may be pretty-printed between successive evaluations of top-level forms). That bound is (iprint-hard-bound state), and can be set with the :hard-bound argument in analogy to how :soft-bound is set, as described above. A "rollover" is the detection that the soft or hard bound has been exceeded, along with a state update so that the next iprint index will be 1. When a rollover occurs, any index beyond the latest iprint index is no longer available for reading. At the top level of the ACL2 read-eval-print loop, this works as follows: ACL2 reads the next top-level form according to the current iprint state, then handles a rollover if the latest iprint index exceeded the current soft bound. The following log illustrates a rollover, which follows the description above. ACL2 !>(set-iprint t :soft-bound 3) ACL2 Observation in SET-IPRINT: The soft-bound for iprinting has been set to 3. ACL2 Observation in SET-IPRINT: Iprinting has been enabled. ACL2 !>(set-evisc-tuple (evisc-tuple 2 3 nil nil) :sites :ld) (:LD) ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g)) ((A B C . #@1#) (A B C . #@2#) (A B C . #@3#)) ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g)) ((A B C . #@4#) (A B C . #@5#) (A B C . #@6#)) ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))) ((A B C D E F G) (A B C D E F G) (A B C D E F G)) ACL2 !>'(1 2 3 4 5) (1 2 3 . #@1#) ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g)) ((A B C . #@2#) (A B C . #@3#) (A B C . #@4#)) ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))) ((A B C D E F G) (A B C D E F G) (A B C D E F G)) ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))) *********************************************** ************ ABORTING from raw Lisp *********** Error: Out-of-bounds index in #@5#. See :DOC set-iprint. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !> We conclude by mentioning two cases where iprinting and evisc-tuples are ignored. (1) This is typically the case when printing results in raw Lisp outside the ACL2 loop. To use iprinting and evisc-tuples in raw Lisp, use raw-mode; see *note SET-RAW-MODE::. In raw-mode, results that are ACL2 objects will be printed in the same way that ACL2 would print those results if not in raw-mode. (2) Iprinting and evisc-tuples are ignored by print-object$, which however is sensitive to many settings that do not affect formatted (fmt etc.) printing; see *note PRINT-CONTROL::. The reader interested in design and implementation issues considered during the addition of iprinting to ACL2 is invited to read the paper "Abbreviated Output for Input in ACL2: An Implementation Case Study"; see the proceedings of ACL2 Workshop 2009, `http://www.cs.utexas.edu/users/moore/acl2/workshop-2009/'.  File: acl2-doc-emacs.info, Node: SET-PRINT-BASE, Next: SET-PRINT-CASE, Prev: SET-IPRINT, Up: IO SET-PRINT-BASE control radix in which numbers are printed By default, integers and ratios are printed in base 10. ACL2 also supports printing in radix 2, 8, or 16 by calling set-print-base with the desired radix (base). (set-print-base 10 state) ; Default printing (set-print-base 16 state) ; Print integers and ratios in hex Here is a sample log. ACL2 !>(list 25 25/3) (25 25/3) ACL2 !>(set-print-base 16 state) ACL2 !>(list 25 25/3) (19 19/3) ACL2 !> See *note SET-PRINT-RADIX:: for how to print the radix, for example, printing the decimal number 25 in print-base 16 as "#x25" rather than "25". Also see *note PRINT-CONTROL:: for other user-settable print controls. Note: ACL2 events and some other top-level commands (for example, thm, verify, and history commands such as :pe and :pbt) set the print base to 10 during their evaluation. So set-print-base has no effect while these forms are being processed.  File: acl2-doc-emacs.info, Node: SET-PRINT-CASE, Next: SET-PRINT-CIRCLE, Prev: SET-PRINT-BASE, Up: IO SET-PRINT-CASE control whether symbols are printed in upper case or in lower case By default, symbols are printed in upper case when vertical bars are not required, as specified by Common Lisp. As with Common Lisp, ACL2 supports printing in a "downcase" mode, where symbols are printed in lower case. Many printing functions (some details below) print characters in lower case for a symbol when the ACL2 state global variable print-case has value :downcase and vertical bars are not necessary for printing that symbol. (Thus, this state global functions in complete analogy to the Common Lisp global *print-case*.) The value print-case is returned by (print-case), and may be set using the function set-print-case as follows. (set-print-case :upcase state) ; Default printing (set-print-case :downcase state) ; Print symbols in lower case when ; vertical bars are not required The ACL2 user can expect that the :downcase setting will have an effect for formatted output (see *note FMT:: and see *note FMS::) when the directives are ~p, ~P, ~q, or ~Q, for built-in functions princ$ and prin1$, and the ppr family of functions, and _not_ for built-in function print-object$. For other printing functions, the effect of :downcase is unspecified. Also see *note PRINT-CONTROL:: for other user-settable print controls.  File: acl2-doc-emacs.info, Node: SET-PRINT-CIRCLE, Next: SET-PRINT-ESCAPE, Prev: SET-PRINT-CASE, Up: IO SET-PRINT-CIRCLE See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-PRINT-ESCAPE, Next: SET-PRINT-LENGTH, Prev: SET-PRINT-CIRCLE, Up: IO SET-PRINT-ESCAPE See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-PRINT-LENGTH, Next: SET-PRINT-LEVEL, Prev: SET-PRINT-ESCAPE, Up: IO SET-PRINT-LENGTH See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-PRINT-LEVEL, Next: SET-PRINT-LINES, Prev: SET-PRINT-LENGTH, Up: IO SET-PRINT-LEVEL See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-PRINT-LINES, Next: SET-PRINT-RADIX, Prev: SET-PRINT-LEVEL, Up: IO SET-PRINT-LINES See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-PRINT-RADIX, Next: SET-PRINT-READABLY, Prev: SET-PRINT-LINES, Up: IO SET-PRINT-RADIX control printing of the radix for numbers See *note SET-PRINT-BASE:: for background on how the print base affects the printing of numbers. set-print-radix affects whether a radix indicated when a number is printed. The radix is not indicated by default, or after evaluating (set-print-radix nil state). But if set-print-radix is called with a first argument that evaluates to a nonnil value -- for example, (set-print-radix t state) -- then the radix is shown when printing. (This behavior is consistent with the handling of Common Lisp global *print-radix*.) The following log illustrates how this works. ACL2 !>(list 25 25/3) (25 25/3) ACL2 !>(set-print-base 16 state) ACL2 !>(list 25 25/3) (19 19/3) ACL2 !>(set-print-radix t state) ACL2 !>(list 25 25/3) (#x19 #x19/3) ACL2 !>(set-print-base 10 state) ACL2 !>(list 25 25/3) (25. #10r25/3) ACL2 !>(set-print-radix nil state) ACL2 !>(list 25 25/3) (25 25/3) ACL2 !>  File: acl2-doc-emacs.info, Node: SET-PRINT-READABLY, Next: SET-PRINT-RIGHT-MARGIN, Prev: SET-PRINT-RADIX, Up: IO SET-PRINT-READABLY See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: SET-PRINT-RIGHT-MARGIN, Next: WITHOUT-EVISC, Prev: SET-PRINT-READABLY, Up: IO SET-PRINT-RIGHT-MARGIN See *note PRINT-CONTROL::.  File: acl2-doc-emacs.info, Node: WITHOUT-EVISC, Prev: SET-PRINT-RIGHT-MARGIN, Up: IO WITHOUT-EVISC print output in full General Form: (without-evisc form) where form is any expression to evaluate. The effect is to evaluate form as though the without-evisc wrapper were absent, except that expressions are printed in full for the ensuing output, regardless of the current evisc-tuples (see *note SET-EVISC-TUPLE::). See *note SET-IPRINT:: for an example. More precisely, without-evisc binds each of the term-evisc-tuple, ld-evisc-tuple, abbrev-evisc-tuple and gag-mode-evisc-tuple to nil (see *note SET-EVISC-TUPLE::). It does not modify the trace evisc-tuple, so trace output is not modified by without-evisc. Also note that calls of printing functions such as fmt that include explicit evisc-tuples will not have those evisc-tuples overridden. The following example illustrates this point. ACL2 !>(without-evisc (fms "~x0~%" (list (cons #0 '((a b ((c d)) e f g) u v w x y))) *standard-co* state (evisc-tuple 2 3 nil nil))) ((A B # ...) U V ...) ACL2 !> We conclude with two remarks. (1) A call of without-evisc on expression exp actually invokes a specialized call of ld on a one-element list containing exp, which prints the value returned by evaluation of exp but actually returns the useless value (mv nil :invisible state). So do not use without-evisc in programs; just use it at the top level of the ACL2 read-eval-print loop, or at least the top level of ld. (2) Even when using without-evisc, if the ACL2 logical world is part of the value returned, it will be printed in abbreviated form because the ACL2 read-eval-print loop always arranges for this to be the case, regardless of the ld-evisc-tuple. For example: ACL2 !>(without-evisc (w state)) ACL2 !> An alternative to the use of without-evisc is to explore large objects using the ACL2 function (walkabout object state). Some brief documentation is printed when you enter an interactive loop upon evaluating a call of walkabout. We may add documentation for walkabout if that is requested.  File: acl2-doc-emacs.info, Node: MISCELLANEOUS, Next: OTHER, Prev: IO, Up: Top MISCELLANEOUS a miscellany of documented functions and concepts (often cited in more accessible documentation) * Menu: * &ALLOW-OTHER-KEYS:: See *note MACRO-ARGS::. * &BODY:: See *note MACRO-ARGS::. * &KEY:: See *note MACRO-ARGS::. * &OPTIONAL:: See *note MACRO-ARGS::. * &REST:: See *note MACRO-ARGS::. * &WHOLE:: See *note MACRO-ARGS::. * A!:: to return to the top-level of ACL2's command loop * ABORT!:: to return to the top-level of ACL2's command loop * ACKNOWLEDGMENTS:: some contributors to the well-being of ACL2 * ACL2S:: See *note ACL2-SEDAN::. * APROPOS:: See *note FINDING-DOCUMENTATION::. * BACKCHAIN-LIMIT:: limiting the effort expended on relieving hypotheses * BACKCHAIN-LIMIT-RW:: hints keyword :BACKCHAIN-LIMIT-RW * BACKTRACK:: hints keyword :BACKTRACK * BIBLIOGRAPHY:: reports about ACL2 * BIND-FREE:: to bind free variables of a rewrite, definition, or linear rule * BY:: hints keyword :BY * CASE-SPLIT:: like force but immediately splits the top-level goal on the hypothesis * CASE-SPLIT-LIMITATIONS:: a list of two ``numbers'' limiting the number of cases produced at once * CASES:: hints keyword :CASES * CLAUSE-IDENTIFIER:: the internal form of a goal-spec * COMMAND:: forms you type at the top-level, but... * COMMAND-DESCRIPTOR:: an object describing a particular command typed by the user * COMMON-LISP:: relation to Common Lisp, including deviations from the spec * COMPUTED-HINTS:: computing advice to the theorem proving process * CONSTRAINT:: restrictions on certain functions introduced in encapsulate events * COPYRIGHT:: ACL2 copyright, license, sponsorship * COROLLARY:: the corollary formula of a rune * CURRENT-PACKAGE:: the package used for reading and printing * CUSTOM-KEYWORD-HINTS:: user-defined hints * DEFAULT-BACKCHAIN-LIMIT:: specifying the backchain limit for a rule * DEFAULT-DEFUN-MODE:: the default defun-mode of defun'd functions * DEFAULT-HINTS:: a list of hints added to every proof attempt * DEFAULT-PRINT-PROMPT:: the default prompt printed by ld * DEFAULT-RULER-EXTENDERS:: the default ruler-extenders for defun'd functions * DEFUN-MODE:: determines whether a function definition is a logical act * DEFUNS:: an alternative to mutual-recursion * DISABLE-FORCING:: to disallow forced case-splits * DISABLE-IMMEDIATE-FORCE-MODEP:: forced hypotheses are not attacked immediately * DISABLEDP:: determine whether a given name or rune is disabled * DO-NOT:: hints keyword :DO-NOT * DO-NOT-INDUCT:: hints keyword :DO-NOT-INDUCT * DOUBLE-REWRITE:: cause a term to be rewritten twice * EMBEDDED-EVENT-FORM:: forms that may be embedded in other events * ENABLE-FORCING:: to allow forced splits * ENABLE-IMMEDIATE-FORCE-MODEP:: forced hypotheses are attacked immediately * ENTER-BOOT-STRAP-MODE:: The first millisecond of the Big Bang * ESCAPE-TO-COMMON-LISP:: escaping to Common Lisp * EXECUTABLE-COUNTERPART:: a rule for computing the value of a function * EXIT-BOOT-STRAP-MODE:: the end of pre-history * EXPAND:: hints keyword :EXPAND * EXTENDED-METAFUNCTIONS:: state and context sensitive metafunctions * FAILED-FORCING:: how to deal with a proof failure in a forcing round * FAILURE:: how to deal with a proof failure * FIND-RULES-OF-RUNE:: find the rules named rune * FINDING-DOCUMENTATION:: searching the documentation * FNCALL-TERM:: See *note META-EXTRACT::. * FORCE:: identity function used to force a hypothesis * FORCED:: See *note FORCE::. * FORCING-ROUND:: a section of a proof dealing with forced assumptions * FUNCTIONAL-INSTANTIATION-IN-ACL2R:: additional requirements for :functional-instance hints in ACL2(r) * GAG-MODE:: verbosity of proof output * GC$:: invoke the garbage collector * GC-VERBOSE:: control printing from the garbage collector * GCL:: tips on building and using ACL2 based on Gnu Common Lisp * GET-INTERNAL-TIME:: runtime vs. realtime in ACL2 timings * GET-WORMHOLE-STATUS:: make a wormhole's status visible outside the wormhole * GOAL-SPEC:: to indicate where a hint is to be used * GUARD:: restricting the domain of a function * GUARD-HINTS:: xargs keyword :GUARD-HINTS * HANDS-OFF:: hints keyword :HANDS-OFF * HIDE:: hide a term from the rewriter * HINTS:: advice to the theorem proving process * HINTS-AND-THE-WATERFALL:: how hints fit into the ACL2 proof waterfall * I-AM-HERE:: a convenient marker for use with rebuild * IF-INTRO:: See *note SPLITTER::. * IGNORED-ATTACHMENT:: why attachments are sometimes not used * IMMED-FORCED:: See *note SPLITTER::. * IMMEDIATE-FORCE-MODEP:: when executable counterpart is enabled, forced hypotheses are attacked immediately * INDUCT:: hints keyword :INDUCT * KEYWORD:: See *note KEYWORDP::. * KEYWORD-COMMANDS:: how keyword commands are processed * LAMBDA:: See *note TERM::. * LAST-PROVER-STEPS:: the number of prover steps most recently taken * LD-ERROR-ACTION:: determines ld's response to an error * LD-ERROR-TRIPLES:: determines whether a form caused an error during ld * LD-EVISC-TUPLE:: determines whether ld suppresses details when printing * LD-MISSING-INPUT-OK:: determines which forms ld evaluates * LD-POST-EVAL-PRINT:: determines whether and how ld prints the result of evaluation * LD-PRE-EVAL-FILTER:: determines which forms ld evaluates * LD-PRE-EVAL-PRINT:: determines whether ld prints the forms to be eval'd * LD-PROMPT:: determines the prompt printed by ld * LD-QUERY-CONTROL-ALIST:: how to default answers to queries * LD-REDEFINITION-ACTION:: to allow redefinition without undoing * LD-SKIP-PROOFSP:: how carefully ACL2 processes your commands * LD-VERBOSE:: determines whether ld prints ``ACL2 Loading ...'' * LEMMA-INSTANCE:: an object denoting an instance of a theorem * LINEAR-ARITHMETIC:: A description of the linear arithmetic decision procedure * LOCAL-INCOMPATIBILITY:: when non-local events won't replay in isolation * LOGICAL-NAME:: a name created by a logical event * LOOP-STOPPER:: limit application of permutative rewrite rules * LP:: the Common Lisp entry to ACL2 * MACRO-ARGS:: the formals list of a macro definition * MAKE-WORMHOLE-STATUS:: creates a wormhole status object from given status, entry code, and data * MEASURE:: xargs keyword :MEASURE * META-EXTRACT:: meta reasoning using valid terms extracted from context or world * META-EXTRACT-CONTEXTUAL-FACT:: See *note META-EXTRACT::. * META-EXTRACT-FORMULA:: See *note META-EXTRACT::. * META-EXTRACT-GLOBAL-FACT:: See *note META-EXTRACT::. * META-EXTRACT-GLOBAL-FACT+:: See *note META-EXTRACT::. * META-EXTRACT-RW+-TERM:: See *note META-EXTRACT::. * MODE:: xargs keyword :MODE * NAME:: syntactic rules on logical names * NIL-GOAL:: how to proceed when the prover generates a goal of nil * NO-THANKS:: hints keyword :NO-THANKS * NON-EXECUTABLE:: xargs keyword :NON-EXECUTABLE * NON-LINEAR-ARITHMETIC:: Non-linear Arithmetic * NONLINEARP:: hints keyword :NONLINEARP * NORMALIZE:: xargs keyword :NORMALIZE * NU-REWRITER:: rewriting NTH/UPDATE-NTH expressions * OBDD:: ordered binary decision diagrams with rewriting * ORDINALS:: ordinals in ACL2 * OTF-FLG:: allow more than one initial subgoal to be pushed for induction * OVERRIDE-HINTS:: a list of hints given priority in every proof attempt * P!:: to pop up (at least) one level of ACL2's command loop * PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS:: re-defining undone defpkgs * PARALLEL:: evaluating forms in parallel * PARALLELISM-BUILD:: building an ACL2 executable with parallel execution enabled * PRINT-DOC-START-COLUMN:: printing the one-liner * PROMPT:: the prompt printed by ld * PROOF-OF-WELL-FOUNDEDNESS:: a proof that o< is well-founded on o-ps * PROOF-SUPPORTERS-ALIST:: See *note DEAD-EVENTS::. * REDEF:: a common way to set ld-redefinition-action * REDEF!:: a common way to set ld-redefinition-action * REDEF+:: system hacker's redefinition command * REDEF-:: turn off system hacker's redefinition command * REDEFINED-NAMES:: to collect the names that have been redefined * REDUNDANT-EVENTS:: allowing a name to be introduced ``twice'' * REORDER:: hints keyword :REORDER * RESTRICT:: hints keyword :RESTRICT * REWRITE-STACK-LIMIT:: limiting the stack depth of the ACL2 rewriter * SAVING-AND-RESTORING:: See *note SAVE-EXEC::. * SET-WORMHOLE-DATA:: sets the wormhole data object in a wormhole status object * SET-WORMHOLE-ENTRY-CODE:: sets the wormhole entry code in a wormhole status object * SHOW-BODIES:: show the potential definition bodies * SIGNATURE:: how to specify the arity of a constrained function * SIMPLE:: :definition and :rewrite rules used in preprocessing * SPECIOUS-SIMPLIFICATION:: nonproductive proof steps * SPLITTER:: reporting of rules whose application may have caused case splits * SPLITTER-OUTPUT:: status for reporting of splitter rules * STOBJS:: xargs keyword :STOBJS * SUBVERSIVE-INDUCTIONS:: why we restrict encapsulated recursive functions * SUBVERSIVE-RECURSIONS:: why we restrict encapsulated recursive functions * SYNTAX:: the syntax of ACL2 is that of Common Lisp * SYNTAXP:: attach a heuristic filter on a rule * TERM:: the three senses of well-formed ACL2 expressions or formulas * THE-METHOD:: how to find proofs * TIME-TRACKER-TAU:: messages about expensive use of the tau-system * TRUST-TAG:: See *note DEFTTAG::. * TTAGS-SEEN:: list some declared trust tags (ttags) * TTREE:: tag-trees * TYPE-SET:: how type information is encoded in ACL2 * TYPESPEC-CHECK:: See *note META-EXTRACT::. * USE:: hints keyword :USE * USING-COMPUTED-HINTS:: how to use computed hints * USING-COMPUTED-HINTS-1:: Driving Home the Basics * USING-COMPUTED-HINTS-2:: One Hint to Every Top-Level Goal in a Forcing Round * USING-COMPUTED-HINTS-3:: Hints as a Function of the Goal (not its Name) * USING-COMPUTED-HINTS-4:: Computing the Hints * USING-COMPUTED-HINTS-5:: Debugging Computed Hints * USING-COMPUTED-HINTS-6:: Using the computed-hint-replacement feature * USING-COMPUTED-HINTS-7:: Using the stable-under-simplificationp flag * USING-COMPUTED-HINTS-8:: Some Final Comments * USING-ENABLED-RULES:: avoiding :use hints for enabled :rewrite rules * VERSION:: ACL2 Version Number * WATERFALL:: See *note HINTS-AND-THE-WATERFALL::. * WHY-BRR:: an explanation of why ACL2 has an explicit brr mode * WORLD:: ACL2 property lists and the ACL2 logical database * WORMHOLE:: ld without state --- a short-cut to a parallel universe * WORMHOLE-DATA:: determines the wormhole data object from a wormhole status object * WORMHOLE-ENTRY-CODE:: determines the wormhole entry code from a wormhole status object * WORMHOLE-EVAL:: state-saving without state --- a short-cut to a parallel universe * WORMHOLE-IMPLEMENTATION:: notes on how wormholes are implemented * WORMHOLE-P:: predicate to determine if you are inside a wormhole * WORMHOLE-STATUSP:: predicate recognizing well-formed wormhole status object * XARGS:: extra arguments, for example to give hints to defun Perhaps as the system matures this section will become more structured.  File: acl2-doc-emacs.info, Node: &ALLOW-OTHER-KEYS, Next: &BODY, Prev: MISCELLANEOUS, Up: MISCELLANEOUS &ALLOW-OTHER-KEYS See *note MACRO-ARGS::.  File: acl2-doc-emacs.info, Node: &BODY, Next: &KEY, Prev: &ALLOW-OTHER-KEYS, Up: MISCELLANEOUS &BODY See *note MACRO-ARGS::.  File: acl2-doc-emacs.info, Node: &KEY, Next: &OPTIONAL, Prev: &BODY, Up: MISCELLANEOUS &KEY See *note MACRO-ARGS::.  File: acl2-doc-emacs.info, Node: &OPTIONAL, Next: &REST, Prev: &KEY, Up: MISCELLANEOUS &OPTIONAL See *note MACRO-ARGS::.  File: acl2-doc-emacs.info, Node: &REST, Next: &WHOLE, Prev: &OPTIONAL, Up: MISCELLANEOUS &REST See *note MACRO-ARGS::.  File: acl2-doc-emacs.info, Node: &WHOLE, Next: A!, Prev: &REST, Up: MISCELLANEOUS &WHOLE See *note MACRO-ARGS::.  File: acl2-doc-emacs.info, Node: A!, Next: ABORT!, Prev: &WHOLE, Up: MISCELLANEOUS A! to return to the top-level of ACL2's command loop When (a!) is evaluated inside of ACL2's command loop, the current computation is aborted and control returns to the top of the command loop, exactly as though the user had interrupted and aborted the current computation. (Note: Versions of ACL2 up to Version_3.4 provided `#.' for this purpose, but no longer; see *note SHARP-DOT-READER::.) If you are at an ACL2 prompt (as opposed to a raw Lisp break), then you may type :a! in place of (a!); see *note KEYWORD-COMMANDS::. For a related feature that only pops up one level, see *note P!::. Logically speaking, (a!) = nil. But imagine that it is defined in such a way that it causes a stack overflow or other resource exhaustion when called.  File: acl2-doc-emacs.info, Node: ABORT!, Next: ACKNOWLEDGMENTS, Prev: A!, Up: MISCELLANEOUS ABORT! to return to the top-level of ACL2's command loop This is an alias for a!; see *note A!::. For a related feature that only pops up one level, see *note P!::.  File: acl2-doc-emacs.info, Node: ACKNOWLEDGMENTS, Next: ACL2S, Prev: ABORT!, Up: MISCELLANEOUS ACKNOWLEDGMENTS some contributors to the well-being of ACL2 The development of ACL2 was initially made possible by funding from the U. S. Department of Defense, including ARPA and ONR. We thank all the organizations that have contributed support, including the following (in alphabetical order). o AMD, for providing significant time over several years for Matt Kaufmann to carry out ACL2 research, support, and development o Computational Logic, Inc. and its president, Don Good, where the first eight years of ACL2 development occurred o Centaur Technology o DARPA o Digital Equipment Corporation o EDS, which provided some time for Matt Kaufmann's ACL2 work 1998-1999 o ForrestHunt and, more generally, Warren A. Hunt, Jr. (see below) o IBM o NSF o ONR o Rockwell Collins o SRC o Sun Microsystems o University of Texas at Austin (in particular support to J Moore through the Admiral B. R. Inman Chair of Computing Theory) We are especially grateful to Warren A. Hunt, Jr. for his unrivaled efforts in securing support for the entire ACL2 research group at both Computational Logic, Inc., and the University of Texas at Austin. Without his efforts, we would have spent less time working on the system and fewer students would have been funded to apply it. ACL2 was started in August, 1989 by Boyer and Moore working together. They co-authored the first versions of axioms.lisp and basis.lisp, with Boyer taking the lead in the formalization of "state" and the most primitive io functions. Boyer also had a significant hand in the development of the early versions of the files interface-raw.lisp and translate.lisp. For several years, Moore alone was responsible for developing the ACL2 system code, though he consulted often with both Boyer and Kaufmann. In August, 1993, Kaufmann became jointly responsible with Moore for developing the system. Boyer has continued to provide valuable consulting on an informal basis. Bishop Brock was the heaviest early user of ACL2, and provided many suggestions for improvements. In particular, the :cases and :restrict hints were his idea; he developed an early version of congruence-based reasoning for Nqthm; and he helped in the development of some early books about arithmetic. In a demonstration of his courage and faith in us, he pushed for Computational Logic, Inc., to agree to the Motorola CAP contract - which required formalizing a commercial DSP in the untested ACL2 - and moved to Scottsdale, AZ, to do the work with the Motorola design team. His demonstration of ACL2's utility was an inspiration, even to those of us designing ACL2. John Cowles also helped in the development of some early books about arithmetic, and also provided valuable feedback and bug reports. Other early users of ACL2 at Computational Logic, Inc. helped influence its development. In particular, Warren Hunt helped with the port to Macintosh Common Lisp, and Art Flatau and Mike Smith provided useful general feedback. Mike Smith helped develop the Emacs portion of the implementation of proof trees. Bill Schelter made some enhancements to akcl (now gcl) that helped to enhance ACL2 performance in that Common Lisp implementation, and more generally, responded helpfully to our bug reports. Camm Maguire has since provided wonderful gcl support, and has created a Debian package for ACL2 built on GCL. We are also grateful to developers of other Common Lisp implementations. Kent Pitman helped in our interaction with the ANSI Common Lisp standardization committee, X3J13. John Cowles helped with the port to Windows (98) by answering questions and running tests. Ruben Gamboa created a modification of ACL2 to allow reasoning about the real numbers using non-standard analysis. His work has been incorporated into the ACL2 distribution; see *note REAL::. Rob Sumners has made numerous useful suggestions. In particular, he has designed and implemented improvements for stobjs and been key in our development of locally-bound stobjs; see *note NOTE-2-6::. Robert Krug has designed and implemented many changes in the vicinity of the linear arithmetic package and its connection to type-set and rewrite. He was also instrumental in the development of extended-metafunctions. Pete Manolios has made numerous useful suggestions. In particular, Pete helped us to organize the first workshop and was a wonderful equal partner with the two of us (Kaufmann and Moore) in producing the books that arose from that workshop. Pete and his student, Daron Vroon, provided the current implementation of ordinals. Jared Davis and Sol Swords have our gratitude for starting the acl2-books repository, `http://acl2-books.googlecode.com/'. We thank David L. Rager for contributing an initial version of the support for parallelism in an experimental extension of ACL2. Bob Boyer and Warren A. Hunt, Jr. developed a canonical representation for ACL2 data objects and a function memoization mechanism to facilitate reuse of previously computed results. We thank them for their extensive efforts for the corresponding experimental (as of 2008 and 2009) extension of ACL2; see *note HONS-AND-MEMOIZATION::. We also thank the contributors to the ACL2 workshops for some suggested improvements and for the extensive collection of publicly distributed benchmark problems. And we thank participants at the ACL2 seminar at the University of Texas for useful feedback. More generally, we thank the ACL2 community for feedback, contributed books (see *note COMMUNITY-BOOKS::), and their interest in the ACL2 project. _Regarding the documentation:_ Bill Young wrote significant portions of the original acl2-tutorial section of the ACL2 documentation, including what is now called alternative-introduction. This was an especially important task in the early years when there was no guide for how to use ACL2 and we are very grateful. He, Bishop Brock, Rich Cohen, and Noah Friedman read over considerable amounts of the documentation, and made many useful comments. Others, particularly Bill Bevier and John Cowles, have also made useful comments on the documentation. Art Flatau helped develop the ACL2 markup language and translators from that language to Texinfo and HTML. Michael "Bogo" Bogomolny created a search engine, beginning with Version 2.6, and for that purpose modified the HTML translator to create one file per topic (a good idea in any case). Laura Lawless provided many hours of help in marking up appropriate parts of the documentation in typewriter font. Noah Friedman developed an Emacs tool that helped us insert "invisible links" into the documentation, which improve the usability of that documentation under HTML readers such as Mosaic. Richard Stallman contributed a texinfo patch, to be found in the file doc/texinfo.tex.  File: acl2-doc-emacs.info, Node: ACL2S, Next: APROPOS, Prev: ACKNOWLEDGMENTS, Up: MISCELLANEOUS ACL2S See *note ACL2-SEDAN::.  File: acl2-doc-emacs.info, Node: APROPOS, Next: BACKCHAIN-LIMIT, Prev: ACL2S, Up: MISCELLANEOUS APROPOS See *note FINDING-DOCUMENTATION::.  File: acl2-doc-emacs.info, Node: BACKCHAIN-LIMIT, Next: BACKCHAIN-LIMIT-RW, Prev: APROPOS, Up: MISCELLANEOUS BACKCHAIN-LIMIT limiting the effort expended on relieving hypotheses Before ACL2 can apply a rule with hypotheses, it must establish that the hypotheses are true. (We ignore the relaxing of this requirement afforded by case-splits and forced hypotheses.) ACL2 typically establishes each hypothesis by backchaining -- instantiating the hypothesis and then rewriting it recursively. Here we describe how ACL2 allows the user to limit backchaining. At the end, below, we describe the function backchain-limit. Each hypothesis of a rewrite, meta, linear, or type-prescription rule is assigned a backchain-limit when the rule is stored. By default, this limit is nil, denoting infinity (no limit). However, the value used for the default may be set to a non-negative integer (or to nil) by the user; see *note SET-DEFAULT-BACKCHAIN-LIMIT::. The default is overridden when a :backchain-limit-lst is supplied explicitly with the rule; see *note RULE-CLASSES::. The number of recursive applications of backchaining starting with the hypothesis of a rule is limited to the backchain-limit associated with that hypothesis. Moreover, the user may set global backchain-limits that limit the total backchaining depth. See *note SET-BACKCHAIN-LIMIT::. One limit is for the use of rewrite, meta, and linear rules, while the other limit is for so-called "type-set reasoning", which uses rules of class type-prescription rules. The two limits operate independently. Below, we discuss the first kind of backchain limits, i.e., for other than type-prescription rules, except as otherwise indicated; but the mechanism for those rules is similar. Below we lay out the precise sense in which a global backchain-limit interacts with the backchain-limits of individual rules in order to limit backchaining. But first we note that when further backchaining is disallowed, ACL2 can still prove a hypothesis in a given context by using that contextual information. In fact, type-set reasoning may be used (except that a weaker version of it is used in the second case above, i.e., where we are already doing type-set reasoning). Thus, the relieving of hypotheses may be limited to the use of contextual information (without backchaining, i.e., without recursively rewriting hypotheses) by executing :set-backchain-limit 0. Recall that there are two sorts of backchain limits: those applied to hypotheses of individual rules, as assigned by their :rule-classes or else taken from the default (see *note SET-DEFAULT-BACKCHAIN-LIMIT::); and the global limit, initially nil (no limit) but settable with :set-backchain-limit. Here is how these two types of limits interact to limit backchaining, i.e., recursive rewriting of hypotheses. ACL2 maintains a current backchain limit, which is the limit on the depth of recursive calls to the rewriter, as well as a current backchain depth, which is initially 0 and is incremented each time ACL2 backchains (and is decremented when a backchain completes). When ACL2 begins to rewrite a literal (crudely, one of the "top-level" terms of the goal currently being worked on), it sets the current backchain-limit to the global value, which is initially nil but can be set using :set-backchain-limit. When ACL2 is preparing to relieve a hypothesis by backchaining (hence, after it has already tried type-set reasoning), it first makes sure that the current backchain limit is greater than the current backchain depth. If not, then it refuses to relieve that hypothesis. Otherwise, it increments the current backchain depth and calculates a new current backchain-limit by taking the minimum of two values: the existing current backchain-limit, and the sum of the current backchain depth and the backchain-limit associated with the hypothesis. Thus, ACL2 only modifies the current backchain-limit if it is necessary to decrease that limit in order to respect the backchain limit associated with the hypothesis. We illustrate with the following examples. ; We stub out some functions so that we can reason about them. (defstub p0 (x) t) (defstub p1 (x) t) (defstub p2 (x) t) (defstub p3 (x) t) ; Initially, the default-backchain-limit is nil, or infinite. (defaxiom p2-implies-p1-limitless (implies (p2 x) (p1 x))) ; The following rule will have a backchain-limit of 0. (defaxiom p1-implies-p0-limit-0 (implies (p1 x) (p0 x)) :rule-classes ((:rewrite :backchain-limit-lst 0))) ; We have (p2 x) ==> (p1 x) ==> (p0 x). We wish to establish that ; (p2 x) ==> (p0 x). Normally, this would be no problem, but here ; we fail because ACL2 cannot establish (p0 x) by type-set reasoning ; alone. (thm (implies (p2 x) (p0 x))) ; We set the default-backchain-limit (for rewriting) to 1. :set-default-backchain-limit 1 ; The following is more powerful than p1-implies-p0-limit-0 ; because it can use rewrite rules to establish (p1 x). (defaxiom p1-implies-p0-limit-1 (implies (p1 x) (p0 x))) ; This theorem will succeed: (thm (implies (p2 x) (p0 x))) ; We return the default-backchain-limit to its initial value. :set-default-backchain-limit nil ; Here is our last axiom. (defaxiom p3-implies-p2-limitless (implies (p3 x) (p2 x))) ; We now have (p3 x) ==> (p2 x) ==> (p1 x) ==> (p0 x). However the ; rule p1-implies-p0-limit-1 has a backchain-limit of 1; hence we ; are not allowed to backchain far enough back to use ; p3-implies-p2-limitless. We therefore lose. (defthm will-fail (implies (p3 x) (p0 x))) Finally, we remark that to see the current global backchain-limits, issue the following commands. (backchain-limit wrld :ts) ; backchain limit for type-set reasoning (backchain-limit wrld :rewrite) ; backchain limit for rewriting  File: acl2-doc-emacs.info, Node: BACKCHAIN-LIMIT-RW, Next: BACKTRACK, Prev: BACKCHAIN-LIMIT, Up: MISCELLANEOUS BACKCHAIN-LIMIT-RW hints keyword :BACKCHAIN-LIMIT-RW See *note HINTS::.  File: acl2-doc-emacs.info, Node: BACKTRACK, Next: BIBLIOGRAPHY, Prev: BACKCHAIN-LIMIT-RW, Up: MISCELLANEOUS BACKTRACK hints keyword :BACKTRACK See *note HINTS::.  File: acl2-doc-emacs.info, Node: BIBLIOGRAPHY, Next: BIND-FREE, Prev: BACKTRACK, Up: MISCELLANEOUS BIBLIOGRAPHY reports about ACL2 For a list of notes and reports about ACL2, see `http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html'.  File: acl2-doc-emacs.info, Node: BIND-FREE, Next: BY, Prev: BIBLIOGRAPHY, Up: MISCELLANEOUS BIND-FREE to bind free variables of a rewrite, definition, or linear rule Examples: (IMPLIES (AND (RATIONALP LHS) (RATIONALP RHS) (BIND-FREE (FIND-MATCH-IN-PLUS-NESTS LHS RHS) (X))) (EQUAL (EQUAL LHS RHS) (EQUAL (+ (- X) LHS) (+ (- X) RHS)))) (IMPLIES (AND (BIND-FREE (FIND-RATIONAL-MATCH-IN-TIMES-NESTS LHS RHS MFC STATE) (X)) (RATIONALP X) (CASE-SPLIT (NOT (EQUAL X 0)))) (EQUAL (< LHS RHS) (IF (< 0 X) (< (* (/ X) LHS) (* (/ X) RHS)) (< (* (/ X) RHS) (* (/ X) LHS))))) * Menu: * BIND-FREE-EXAMPLES:: examples pertaining to bind-free hypotheses General Forms: (BIND-FREE term var-list) (BIND-FREE term t) (BIND-FREE term) A rule which uses a bind-free hypothesis has similarities to both a rule which uses a syntaxp hypothesis and to a :meta rule. Bind-free is like syntaxp, in that it logically always returns t but may affect the application of a :rewrite, :definition, or :linear rule when it is called at the top-level of a hypothesis. It is like a :meta rule, in that it allows the user to perform transformations of terms under progammatic control. Note that a bind-free hypothesis does not, in general, deal with the meaning or semantics or values of the terms, but rather with their syntactic forms. Before attempting to write a rule which uses bind-free, the user should be familiar with syntaxp and the internal form that ACL2 uses for terms. This internal form is similar to what the user sees, but there are subtle and important differences. Trans can be used to view this internal form. Just as for a syntaxp hypothesis, there are two basic types of bind-free hypotheses. The simpler type of bind-free hypothesis may be used as the nth hypothesis in a :rewrite, :definition, or :linear rule whose :corollary is (implies (and hyp1 ... hypn ... hypk) (equiv lhs rhs)) provided term is a term, term contains at least one variable, and every variable occuring freely in term occurs freely in lhs or in some hypi, i (EQUAL (+ (- (EXPT A N)) 3 (EXPT A N) (FOO A C)) (+ (- (EXPT A N)) (BAR B) (EXPT A N))). Question: What is the internal form of this result? Hint: Use :trans. When this rule fires, it adds the negation of a common term to both sides of the equality by selecting a binding for the otherwise-free variable x, under programmatic control. Note that other mechanisms such as the binding of free-variables may also extend the substitution alist. Just as for a syntaxp test, a bind-free form signals failure by returning nil. However, while a syntaxp test signals success by returning true, a bind-free form signals success by returning an alist which is used to extend the current substitution alist. Because of this use of the alist, there are several restrictions on it -- in particular the alist must only bind variables, these variables must not be already bound by the substitution alist, and the variables must be bound to ACL2 terms. If term returns an alist and the alist meets these restrictions, we append the alist to the substitution alist and use the result as the new current substitution alist. This new current substitution alist is then used when we attempt to relieve the next hypothesis or, if there are no more, instantiate the right hand side of the rule. There is also a second, optional, var-list argument to a bind-free hypothesis. If provided, it must be either t or a list of variables. If it is not provided, it defaults to t. If it is a list of variables, this second argument is used to place a further restriction on the possible values of the alist to be returned by term: any variables bound in the alist must be present in the list of variables. We strongly recommend the use of this list of variables, as it allows some consistency checks to be performed at the time of the rule's admittance which are not possible otherwise. An extended bind-free hypothesis is similar to the simple type described above, but it uses two additional variables, mfc and state, which must not be bound by the left hand side or an earlier hypothesis of the rule. They must be the last two variables mentioned by term: first mfc, then state. These two variables give access to the functions mfc-xxx; see *note EXTENDED-METAFUNCTIONS::. As described there, mfc is bound to the so-called metafunction-context and state to ACL2's state. See *note BIND-FREE-EXAMPLES:: for examples of the use of these extended bind-free hypotheses. *SECTION*: Returning a list of alists. As promised above, we conclude with a discussion of the case that evaluation of the bind-free term produces a list of alists, x, rather than a single alist. In this case each member b of x is considered in turn, starting with the first and proceeding through the list. Each such b is handled exactly as discussed above, as though it were the result of evaluating the bind-free term. Thus, each b extends the current variable binding alist, and all remaining hypotheses are then relieved, as though b had been the value obtained by evaluating the bind-free term. As soon as one such b leads to successful relieving of all remaining hypotheses, the process of relieving hypotheses concludes, so no further members of x are considered. We illustrate with a simple pedagogical example. First introduce functions p1 and p2 such that a rewrite rule specifies that p2 implies p1, but with a free variable. (defstub p1 (x) t) (defstub p2 (x y) t) (defaxiom p2-implies-p1 (implies (p2 x y) (p1 x))) If we add the following axiom, then (p1 x) follows logically for all x. (defaxiom p2-instance (p2 v (cons v 4))) Unfortunately, evaluation of (thm (p1 a)) fails, because ACL2 fails to bind the free variable y in order to apply the rule p2-instance. Let's define a function that produces a list of alists, each binding the variable y. Of course, we know that only the middle one below is necessary in this simple example. In more complex examples, one might use heuristics to construct such a list of alists. (defun my-alists (x) (list (list (cons 'y (fcons-term* 'cons x ''3))) (list (cons 'y (fcons-term* 'cons x ''4))) (list (cons 'y (fcons-term* 'cons x ''5))))) The following rewrite rule uses bind-free to return a list of candidate alists binding y. (defthm p2-implies-p1-better (implies (and (bind-free (my-alists x) (y)) ; the second argument, (y), is optional (p2 x y)) (p1 x))) Now the proof succeeds for (thm (p1 a)). Why? When ACL2 applies the rewrite rule p2-implies-p1-better, it evaluates my-alists, as we can see from the following trace, to bind y in three different alists. ACL2 !>(thm (p1 a)) 1> (ACL2_*1*_ACL2::MY-ALISTS A) <1 (ACL2_*1*_ACL2::MY-ALISTS (((Y CONS A '3)) ((Y CONS A '4)) ((Y CONS A '5)))) Q.E.D. The first alist, binding y to (cons a '3), fails to allow the hypothesis (p2 x y) to be proved. But the next binding of y, to (cons a '4), succeeds: then the current binding alist is ((x . a) (y . (cons a '4))), for which the hypothesis (p2 x y) rewrites to true using the rewrite rule p2-instance.  File: acl2-doc-emacs.info, Node: BIND-FREE-EXAMPLES, Prev: BIND-FREE, Up: BIND-FREE BIND-FREE-EXAMPLES examples pertaining to bind-free hypotheses See *note BIND-FREE:: for a basic discussion of the use of bind-free to control rewriting. Note that the examples below all illustrate the common case in which a bind-free hypothesis generates a binding alist. See *note BIND-FREE::, in particular the final section, for a discussion of the case that instead a list of binding alists is generated. We give examples of the use of bind-free hypotheses from the perspective of a user interested in reasoning about arithmetic, but it should be clear that bind-free can be used for many other purposes also. EXAMPLE 1: Cancel a common factor. (defun bind-divisor (a b) ; If a and b are polynomials with a common factor c, we return a ; binding for x. We could imagine writing get-factor to compute the ; gcd, or simply to return a single non-invertible factor. (let ((c (get-factor a b))) (and c (list (cons 'x c))))) (defthm cancel-factor ;; We use case-split here to ensure that, once we have selected ;; a binding for x, the rest of the hypotheses will be relieved. (implies (and (acl2-numberp a) (acl2-numberp b) (bind-free (bind-divisor a b) (x)) (case-split (not (equal x 0))) (case-split (acl2-numberp x))) (iff (equal a b) (equal (/ a x) (/ b x))))) EXAMPLE 2: Pull integer summand out of floor. Note: This example has an _extended_ bind-free hypothesis, which uses the term (find-int-in-sum sum mfc state). (defun fl (x) ;; This function is defined, and used, in the IHS books. (floor x 1)) (defun int-binding (term mfc state) ;; The call to mfc-ts returns the encoded type of term. ; ;; Thus, we are asking if term is known by type reasoning to ; ;; be an integer. ; (declare (xargs :stobjs (state) :mode :program)) (if (ts-subsetp (mfc-ts term mfc state) *ts-integer*) (list (cons 'int term)) nil)) (defun find-int-in-sum (sum mfc state) (declare (xargs :stobjs (state) :mode :program)) (if (and (nvariablep sum) (not (fquotep sum)) (eq (ffn-symb sum) 'binary-+)) (or (int-binding (fargn sum 1) mfc state) (find-int-in-sum (fargn sum 2) mfc state)) (int-binding sum mfc state))) ; Some additional work is required to prove the following. So for ; purposes of illustration, we wrap skip-proofs around the defthm. (skip-proofs (defthm cancel-fl-int ;; The use of case-split is probably not needed, since we should ;; know that int is an integer by the way we selected it. But this ;; is safer. (implies (and (acl2-numberp sum) (bind-free (find-int-in-sum sum mfc state) (int)) (case-split (integerp int))) (equal (fl sum) (+ int (fl (- sum int))))) :rule-classes ((:rewrite :match-free :all))) ) ; Arithmetic libraries will have this sort of lemma. (defthm hack (equal (+ (- x) x y) (fix y))) (in-theory (disable fl)) (thm (implies (and (integerp x) (acl2-numberp y)) (equal (fl (+ x y)) (+ x (fl y))))) EXAMPLE 3: Simplify terms such as (equal (+ a (* a b)) 0) (defun factors (product) ;; We return a list of all the factors of product. We do not ;; require that product actually be a product. (if (eq (fn-symb product) 'BINARY-*) (cons (fargn product 1) (factors (fargn product 2))) (list product))) (defun make-product (factors) ;; Factors is assumed to be a list of ACL2 terms. We return an ;; ACL2 term which is the product of all the ellements of the ;; list factors. (cond ((atom factors) ''1) ((null (cdr factors)) (car factors)) ((null (cddr factors)) (list 'BINARY-* (car factors) (cadr factors))) (t (list 'BINARY-* (car factors) (make-product (cdr factors)))))) (defun quotient (common-factors sum) ;; Common-factors is a list of ACL2 terms. Sum is an ACL2 term each ;; of whose addends have common-factors as factors. We return ;; (/ sum (make-product common-factors)). (if (eq (fn-symb sum) 'BINARY-+) (let ((first (make-product (set-difference-equal (factors (fargn sum 1)) common-factors)))) (list 'BINARY-+ first (quotient common-factors (fargn sum 2)))) (make-product (set-difference-equal (factors sum) common-factors)))) (defun intersection-equal (x y) (cond ((endp x) nil) ((member-equal (car x) y) (cons (car x) (intersection-equal (cdr x) y))) (t (intersection-equal (cdr x) y)))) (defun common-factors (factors sum) ;; Factors is a list of the factors common to all of the addends ;; examined so far. On entry, factors is a list of the factors in ;; the first addend of the original sum, and sum is the rest of the ;; addends. We sweep through sum, trying to find a set of factors ;; common to all the addends of sum. (declare (xargs :measure (acl2-count sum))) (cond ((null factors) nil) ((eq (fn-symb sum) 'BINARY-+) (common-factors (intersection-equal factors (factors (fargn sum 1))) (fargn sum 2))) (t (intersection-equal factors (factors sum))))) (defun simplify-terms-such-as-a+ab-rel-0-fn (sum) ;; If we can find a set of factors common to all the addends of sum, ;; we return an alist binding common to the product of these common ;; factors and binding quotient to (/ sum common). (if (eq (fn-symb sum) 'BINARY-+) (let ((common-factors (common-factors (factors (fargn sum 1)) (fargn sum 2)))) (if common-factors (let ((common (make-product common-factors)) (quotient (quotient common-factors sum))) (list (cons 'common common) (cons 'quotient quotient))) nil)) nil)) (defthm simplify-terms-such-as-a+ab-=-0 (implies (and (bind-free (simplify-terms-such-as-a+ab-rel-0-fn sum) (common quotient)) (case-split (acl2-numberp common)) (case-split (acl2-numberp quotient)) (case-split (equal sum (* common quotient)))) (equal (equal sum 0) (or (equal common 0) (equal quotient 0))))) (thm (equal (equal (+ u (* u v)) 0) (or (equal u 0) (equal v -1))))  File: acl2-doc-emacs.info, Node: BY, Next: CASE-SPLIT, Prev: BIND-FREE, Up: MISCELLANEOUS BY hints keyword :BY See *note HINTS::.  File: acl2-doc-emacs.info, Node: CASE-SPLIT, Next: CASE-SPLIT-LIMITATIONS, Prev: BY, Up: MISCELLANEOUS CASE-SPLIT like force but immediately splits the top-level goal on the hypothesis Case-split is an variant of force, which has similar special treatment in hypotheses of rules for the same rule-classes as for force (see *note FORCE::). This treatment takes place for rule classes :rewrite, :linear, :type-prescription, :definition, :meta (actually in that case, the result of evaluating the hypothesis metafunction call), and :forward-chaining. When a hypothesis of a conditional rule (of one of the classes listed above) has the form (case-split hyp) it is logically equivalent to hyp. However it affects the application of the rule generated as follows: if ACL2 attempts to apply the rule but cannot establish that the required instance of hyp holds in the current context, it considers the hypothesis true anyhow, but (assuming all hypotheses are seen to be true and the rule is applied) creates a subgoal in which that instance of hyp is assumed false. (There are exceptions, noted below.) For example, given the rule (defthm p1->p2 (implies (case-split (p1 x)) (p2 x))) then an attempt to prove (implies (p3 x) (p2 (car x))) can give rise to a single subgoal: (IMPLIES (AND (NOT (P1 (CAR X))) (P3 X)) (P2 (CAR X))). Unlike force, case-split does not delay the "false case" to a forcing round but tackles it more or less immediately. The special "split" treatment of case-split can be disabled by disabling forcing: see *note FORCE:: for a discussion of disabling forcing, and also see *note DISABLE-FORCING::. Finally, we should mention that the rewriter is never willing to split when there is an if term present in the goal being simplified. Since and terms and or terms are merely abbreviations for if terms, they also prevent splitting. Note that if terms are ultimately eliminated using the ordinary flow of the proof (but see *note SET-CASE-SPLIT-LIMITATIONS::), so case-split will ultimately function as intended. When in the proof checker, case-split behaves like force.  File: acl2-doc-emacs.info, Node: CASE-SPLIT-LIMITATIONS, Next: CASES, Prev: CASE-SPLIT, Up: MISCELLANEOUS CASE-SPLIT-LIMITATIONS a list of two "numbers" limiting the number of cases produced at once Examples: ACL2 !>(case-split-limitations (w state)) (500 100) With the setting above, clausify will not try subsumption/replacement if more than 500 clauses are involved. Furthermore, the simplifier, as it sweeps over a clause, will inhibit further case splits when it has accumulated 100 subgoals. This inhibition is implemented by continuing to rewrite subsequent literals but not splitting out their cases. This can produce literals containing IFs. See *note SET-CASE-SPLIT-LIMITATIONS:: for a more general discussion.  File: acl2-doc-emacs.info, Node: CASES, Next: CLAUSE-IDENTIFIER, Prev: CASE-SPLIT-LIMITATIONS, Up: MISCELLANEOUS CASES hints keyword :CASES See *note HINTS::.  File: acl2-doc-emacs.info, Node: CLAUSE-IDENTIFIER, Next: COMMAND, Prev: CASES, Up: MISCELLANEOUS CLAUSE-IDENTIFIER the internal form of a goal-spec To each goal-spec, str, there corresponds a clause-identifier produced by (parse-clause-id str). For example, (parse-clause-id "[2]Subgoal *4.5.6/7.8.9'''") returns ((2 4 5 6) (7 8 9) . 3). The function string-for-tilde-@-clause-id-phrase inverts parse-clause-id in the sense that given a clause identifier it returns the corresponding goal-spec. As noted in the documentation for goal-spec, each clause printed in the theorem prover's proof attempt is identified by a name. When these names are represented as strings they are called "goal specs." Such strings are used to specify where in the proof attempt a given hint is to be applied. The function parse-clause-id converts goal-specs into clause identifiers, which are cons-trees containing natural numbers. Examples of goal-specs and their corresponding clause identifiers are shown below. parse-clause-id --> "Goal" ((0) NIL . 0) "Subgoal 3.2.1'" ((0) (3 2 1) . 1) "[2]Subgoal *4.5.6/7.8.9'''" ((2 4 5 6) (7 8 9) . 3) <-- string-for-tilde-@-clause-id-phrase The caar of a clause id specifies the forcing round, the cdar specifies the goal being proved by induction, the cadr specifies the particular subgoal, and the cddr is the number of primes in that subgoal. Internally, the system maintains clause ids, not goal-specs. The system prints clause ids in the form shown by goal-specs. When a goal-spec is used in a hint, it is parsed (before the proof attempt begins) into a clause id. During the proof attempt, the system watches for the clause id and uses the corresponding hint when the id arises. (Because of the expense of creating and garbage collecting a lot of strings, this design is more efficient than the alternative.)  File: acl2-doc-emacs.info, Node: COMMAND, Next: COMMAND-DESCRIPTOR, Prev: CLAUSE-IDENTIFIER, Up: MISCELLANEOUS COMMAND forms you type at the top-level, but... ...the word "command" usually refers to a top-level form whose evaluation produces a new logical world. Typical commands are: (defun foo (x) (cons x x)) (defthm consp-foo (consp (foo x))) (defrec pair (hd . tl) nil) The first two forms are examples of commands that are in fact primitive events. See *note EVENTS::. Defrec, on the other hand, is a macro that expands into a progn of several primitive events. In general, a world extending command generates one or more events. Both events and commands leave landmarks on the world that enable us to determine how the given world was created from the previous one. Most of your interactions will occur at the command level, i.e., you type commands, you print previous commands, and you undo back through commands. Commands are denoted by command descriptors. See *note COMMAND-DESCRIPTOR::.  File: acl2-doc-emacs.info, Node: COMMAND-DESCRIPTOR, Next: COMMON-LISP, Prev: COMMAND, Up: MISCELLANEOUS COMMAND-DESCRIPTOR an object describing a particular command typed by the user Examples: :max ; the command most recently typed by the user :x ; synonymous with :max (:x -1) ; the command before the most recent one (:x -2) ; the command before that :x-2 ; synonymous with (:x -2) 5 ; the fifth command typed by the user 1 ; the first command typed by the user 0 ; the last command of the system initialization -1 ; the next-to-last initialization command :min ; the first command of the initialization :start ; the last command of the initial ACL2 logical world fn ; the command that introduced the logical name fn (:search (defmacro foo-bar)) ; the first command encountered in a search from :max to ; 0 that either contains defmacro and foo-bar in the ; command form or contains defmacro and foo-bar in some ; event within its block. The recorded history of your interactions with the top-level ACL2 command loop is marked by the commands you typed that changed the logical world. Each such command generated one or more events, since the only way for you to change the logical world is to execute an event function. See *note COMMAND:: and see *note EVENTS::. We divide history into "command blocks," grouping together each world changing command and its events. A "command descriptor" is an object that can be used to describe a particular command in the history of the ongoing session. Each command is assigned a unique integer called its "command number" which indicates the command's position in the chronological ordering of all of the commands ever executed in this session (including those executed to initialize the system). We assign the number 1 to the first command you type to ACL2. We assign 2 to the second and so on. The non-positive integers are assigned to "prehistoric" commands, i.e., the commands used to initialize the ACL2 system: 0 is the last command of the initialization, -1 is the one before that, etc. The legal command descriptors are described below. We use n to denote any integer, sym to denote any logical name (see *note LOGICAL-NAME::), and cd to denote, recursively, any command descriptor. command command descriptor described :max -- the most recently executed command (i.e., the one with the largest command number) :x -- synonymous with :max :x-k -- synonymous with (:x -k), if k is an integer and k>0 :min -- the earliest command (i.e., the one with the smallest command number and hence the first command of the system initialization) :start -- the last command when ACL2 starts up n -- command number n (If n is not in the range :min<=n<=:max, n is replaced by the nearest of :min and :max.) sym -- the command that introduced the logical name sym (cd n) -- the command whose number is n plus the command number of the command described by cd (:search pat cd1 cd2) In this command descriptor, pat must be either an atom or a true list of atoms and cd1 and cd2 must be command descriptors. We search the interval from cd1 through cd2 for the first command that matches pat. Note that if cd1 occurs chronologically after cd2, the search is ``backwards'' through history while if cd1 occurs chronologically before cd2, the search is ``forwards''. A backwards search will find the most recent match; a forward search will find the chronologically earliest match. A command matches pat if either the command form itself or one of the events in the block contains pat (or all of the atoms in pat if pat is a list). (:search pat) the command found by (:search pat :max 0), i.e., the most recent command matching pat that was part of the user's session, not part of the system initialization.  File: acl2-doc-emacs.info, Node: COMMON-LISP, Next: COMPUTED-HINTS, Prev: COMMAND-DESCRIPTOR, Up: MISCELLANEOUS COMMON-LISP relation to Common Lisp, including deviations from the spec ACL2 is a logic, a theorem prover, and a programming language based on Common Lisp. A connection with Common Lisp is established with guards (see *note GUARD::). However, here we document potential deviations from Common Lisp semantics even in the presence of verified guards. Our view is that these deviations are extremely unlikely to manifest; indeed, as of this writing we are unaware of any cases in which these issues arise in practice. However, we feel obligated to acknowledge their possibility, which could result in surprises during evaluation or even proof. * Menu: * DEFUN-MODE-CAVEAT:: potential soundness issue for functions with defun-mode :program * GENERALIZED-BOOLEANS:: potential soundness issues related to ACL2 predicates The Common Lisp spec allows certain predicates to return what it calls "generalized Booleans," which are really arbitrary values that are to be viewed as either nil or non-nil. However, in ACL2 these functions are assumed to return nil or t. For details,see *note GENERALIZED-BOOLEANS::. The execution of forms with :program mode functions can result in calls of functions on arguments that do not satisfy their guards. In practice, this simply causes hard Lisp errors. But in principle one could imagine a damaged Lisp image that operates incorrectly. See *note DEFUN-MODE-CAVEAT::. The Common Lisp spec, specifically Section 3.2.2.3 of the Common Lisp HyperSpec, allows for undefined results when a function is "multiply defined" in a compiled file. ACL2 allows redundant defuns in a book, and in general books are compiled by certify-book (but see *note CERTIFY-BOOK:: and see *note COMPILATION:: for how to control such compilation). Moreover, ACL2 provides a redefinition capability (see *note LD-REDEFINITION-ACTION:: and see *note REDEF::), and the above section also allows for undefined results when a function is defined in a compiled file and then redefined, presumably (for example) because of inlining.  File: acl2-doc-emacs.info, Node: DEFUN-MODE-CAVEAT, Next: GENERALIZED-BOOLEANS, Prev: COMMON-LISP, Up: COMMON-LISP DEFUN-MODE-CAVEAT potential soundness issue for functions with defun-mode :program Technically speaking, in the current implementation, the execution of functions having defun-mode :program may damage the ACL2 system in a way that renders it unsound. In practice, we have never seen this happen; so, the explanations below can be viewed as extremely paranoid. Nevertheless, here we document this concern, even if it should be taken with more than a grain of salt. See *note DEFUN-MODE:: for a discussion of defun-modes. That discussion describes an imagined implementation that is slightly different from this one. This note explains that the current implementation is open to unsoundness. For discussion of a different soundness issue that is also related to function execution, see *note GENERALIZED-BOOLEANS::. The execution of a function having defun-mode :program may violate Common Lisp guards on the subroutines used. (This may be true even for calls of a function on arguments that satisfy its guard, because ACL2 has not verified that its guard is sufficient to protect its subroutines.) When a guard is violated at runtime all bets are off. That is, no guarantees are made either about the answer being "right" or about the continued rationality of the ACL2 system itself. For example, suppose you make the following defun: (defun crash (i) (declare (xargs :mode :program :guard (integerp i))) (car i)) Note that the declared guard does not in fact adequately protect the subroutines in the body of crash; indeed, satisfying the guard to crash will guarantee that the car expression is in violation of its guard. Because this function is admitted in :program-mode, no checks are made concerning the suitability of the guard. Furthermore, in the current ACL2 implementation, crash is executed directly in Common Lisp. Thus if you call crash on an argument satisfying its guard you will cause an erroneous computation to take place. ACL2 !>(crash 7) Error: Caught fatal error [memory may be damaged] ... There is no telling how much damage is done by this errant computation. In some lisps your ACL2 job may actually crash back to the operating system. In other lisps you may be able to recover from the "hard error" and resume ACL2 in a damaged but apparently functional image. THUS, HAVING A FUNCTION WITH DEFUN-MODE :PROGRAM IN YOUR SYSTEM ABSOLVES US, THE ACL2 IMPLEMENTORS, FROM RESPONSIBILITY FOR THE SOUNDNESS OF OUR SYSTEM. Furthermore ACL2 DOES NOT YET PROVIDE ANY MEANS OF REGAINING ASSURANCES OF SOUNDNESS AFTER THE INTRODUCTION OF A FUNCTION IN :PROGRAM MODE, EVEN IF IT IS ULTIMATELY CONVERTED TO :LOGIC MODE (since its execution could have damaged the system in a way that makes it possible to verify its termination and guards unsoundly). Finally, THE VAST MAJORITY OF ACL2 SYSTEM CODE IS IN :PROGRAM MODE AND SO ALL BETS ARE OFF FROM BEFORE YOU START! This hopeless state of current affairs will change, we think. We think we have defined our functions "correctly" in the sense that they can be converted, without "essential" modification, to :logic mode. We think it very unlikely that a mis-guarded function in :program mode (whether ours or yours) will cause unsoundness without some sort of hard lisp error accompanying it. We think that ultimately we can make it possible to execute your functions (interpretively) without risk to the system, even when some have :program mode. In that imagined implementation, code using functions having :program mode would run more slowly, but safely. These functions could be introduced into the logic ex post facto, whereupon the code's execution would speed up because Common Lisp would be allowed to execute it directly. We therefore ask that you simply pretend that this is that imagined implementation, introduce functions in :program mode, use them as convenient and perhaps ultimately introduce some of them in :logic mode and prove their properties. If you use the system this way we can develop (or dismiss) this style of formal system development. BUT BE ON THE LOOKOUT FOR SCREWUPS DUE TO DAMAGE CAUSED BY THE EXECUTION OF YOUR FUNCTIONS HAVING :PROGRAM MODE!  File: acl2-doc-emacs.info, Node: GENERALIZED-BOOLEANS, Prev: DEFUN-MODE-CAVEAT, Up: COMMON-LISP GENERALIZED-BOOLEANS potential soundness issues related to ACL2 predicates The discussion below outlines a potential source of unsoundness in ACL2. Although to our knowledge there is no existing Lisp implementation in which this problem can arise, nevertheless we feel compelled to explain this situation. Consider for example the question: What is the value of (equal 3 3)? According to the ACL2 axioms, it's t. And as far as we know, based on considerable testing, the value is t in every Common Lisp implementation. However, according the Common Lisp draft proposed ANSI standard, any non-nil value could result. Thus for example, (equal 3 3) is allowed by the standard to be 17. The Common Lisp standard specifies (or soon will) that a number of Common Lisp functions that one might expect to return Boolean values may, in fact, return arbitrary values. Examples of such functions are equal, rationalp, and <; a much more complete list is given below. Indeed, we dare to say that every Common Lisp function that one may believe returns only Booleans is, nevertheless, not specified by the standard to have that property, with the exceptions of the functions not and null. The standard refers to such arbitrary values as "generalized Booleans," but in fact this terminology makes no restriction on those values. Rather, it merely specifies that they are to be viewed, in an informal sense, as being either nil or not nil. This situation is problematic for ACL2, which axiomatizes these functions to return Booleans. The problem is that because (for efficiency and simplicity) ACL2 makes very direct use of compiled Common Lisp functions to support the execution of its functions, there is in principle a potential for unsoundness due to these "generalized Booleans." For example, ACL2's equal function is defined to be Common Lisp's equal. Hence if in Common Lisp the form (equal 3 3) were to evaluate to 17, then in ACL2 we could prove (using the :executable-counterpart of equal) (equal (equal 3 3) 17). However, ACL2 can also prove (equal (equal x x) t), and these two terms together are contradictory, since they imply (instantiating x in the second term by 3) that (equal 3 3) is both equal to 17 and to t. To make matters worse, the standard allows (equal 3 3) to evaluate to _different_ non-nil values every time. That is: equal need not even be a function! Fortunately, no existing Lisp implementation takes advantage of the flexibility to have (most of) its predicates return generalized Booleans, as far as we know. We may implement appropriate safeguards in future releases of ACL2, in analogy to (indeed, probably extending) the existing safeguards by way of guards (see *note GUARD::). For now, we'll sleep a bit better knowing that we have been up-front about this potential problem. The following list of functions contains all those that are defined in Common Lisp to return generalized Booleans but are assumed by ACL2 to return Booleans. In addition, the functions acl2-numberp and complex-rationalp are directly defined in terms of respective Common Lisp functions numberp and complexp. /= < = alpha-char-p atom char-equal char< char<= char> char>= characterp consp digit-char-p endp eq eql equal evenp integerp keywordp listp logbitp logtest lower-case-p minusp oddp plusp rationalp standard-char-p string-equal string< string<= string> string>= stringp subsetp symbolp upper-case-p zerop  File: acl2-doc-emacs.info, Node: COMPUTED-HINTS, Next: CONSTRAINT, Prev: COMMON-LISP, Up: MISCELLANEOUS COMPUTED-HINTS computing advice to the theorem proving process General Form of :hints: (hint1 hint2 ... hintk) Each element, hinti, must be either a common hint or a computed hint. A common hint is of the form (goal-spec :key1 val1 ... :keyn valn) where goal-spec is as specified in goal-spec and each :keyi and vali is as specified in hints. Among the "common hints" we include both the primitive hints and user-defined custom keyword hints (see *note CUSTOM-KEYWORD-HINTS::). A computed hint may be a function symbol, fn, of three, four or seven arguments. Otherwise, a computed hint is a term with the following properties: (a) the only free variables allowed in the term are ID, CLAUSE, WORLD, STABLE-UNDER-SIMPLIFICATIONP, HIST, PSPV, CTX, and STATE; (b) the output signature of the term is either (MV * * STATE), which is to be treated as an error triple (see below), or is *, denoting a single non-stobj value; and (c) in the former case of (b) above, the term is single-threaded in STATE. If a computed hint is a function symbol fn, whose arity n is therefore three, four, or seven, then it is treated as the term resulting from applying that fn to the first n variables shown in (a) above. Notice that it must then return a single non-stobj value, not an error triple, since state is not one of the first seven variables shown in (a). Note: Error triples are an ACL2 idiom for implementing "errors"; see *note ERROR-TRIPLES::. If a computation returns (mv erp val state) in a context in which ACL2 is respecting the error triple convention (see *note LD-ERROR-TRIPLES:: and see *note LD-ERROR-ACTION::), then an error is deemed to have occurred if erp is non-nil. The computation is expected to have printed an appropriate error message to state and further computation is aborted. On the other hand, if a computation returns an error triple in which erp is nil, then "value" of the computation is taken to be the second component, val, of the triple (along with the possibly modified state), and computation continues. For more information about programming with error triples, see *note PROGRAMMING-WITH-STATE::. The function symbol cases are treated as abbreviations of the term (fn ID CLAUSE WORLD), (fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP), or (fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX) as appropriate for the arity of fn. (Note that this tells you which argument of fn is which.) Moreover, in these cases the value returned must be a single ordinary (non-stobj) value, not an error triple. In the discussion below we assume all computed hints are of the term form. Indeed, we almost assume all computed hints are of the 3 and 4 argument forms. We only comment briefly on the 7 argument form in using-computed-hints-8. The semantics of a computed hint term is as follows. On every subgoal, the term is evaluated in an environment in which the variables mentioned in (a) above are bound to context-sensitive values explained below. Either the computed hint signals an error, in which the proof attempt aborts, or else it returns a value, val and a new state, state. Any changes to those parts of state that affect logical soundness are undone; more specifically, the values of symbols (sometimes called "state global variables") in the list *protected-system-state-globals* in the global table of the state (see *note STATE::) are restored when changed during evaluation. The value, val, of a non-erroneous computed hint calculation is either nil, which means the computed hint did not apply to the subgoal in question, or it is an alternating list of :keyi vali pairs as specified in hints. With one exception, those new hints are applied to the given subgoal as though the user had typed them explicitly. The exception is that the first keyword in the returned val is allowed to be :COMPUTED-HINT-REPLACEMENT. Its value should be nil, t, or a list of terms. If this keyword is not present, the default value of nil is provided. We explain :COMPUTED-HINT-REPLACEMENT below. The evaluation of a hint term is done with guard checking turned off (see *note SET-GUARD-CHECKING::); e.g., the form (car 23) in a computed hint returns nil as per the axioms. When a non-nil value is returned, the keyword/value pairs (other than the optional :COMPUTED-HINT-REPLACEMENT) are used as the hint for the subgoal in question. Thus, your job as the programmer of computed hints is either to cause an error, typically by invoking er, or to return a non-erroneous value triple whose value is the list of keys and values you would have typed had you supplied a common hint for the subgoal. (In particular, any theory expressions in it are evaluated with respect to the global current-theory, not whatever theory is active at the subgoal in question.) If the generated list of keywords and values is illegal, an error will be signaled and the proof attempt will be aborted. The purpose of the :COMPUTED-HINT-REPLACEMENT keyword and its value, chr, is to change the list of hints. If chr is nil, then the hint which was applied is removed from the list of hints that is passed down to the children of the subgoal in question. This is the default. If chr is t, then the hint is left in the list of hints. This means that the same hint may act on the children of the subgoal. Otherwise, chr must be a list of terms, each of which is treated as a computed hint. The hint which was applied is deleted from the list of hints and the hints in chr are added to the list of hints passed to the children of the subgoal. The ability to compute new hints and pass them down allows strange and wonderful behavior. For these purposes, the goals produced by induction and the top-level goals of forcing rounds are not considered children; all original hints are available to them. Only the first hint applicable to a goal, as specified in the user-supplied list of :hints followed by the default-hints-table, will be applied to that goal. (For an advanced exception, see *note OVERRIDE-HINTS::.) It remains only to describe the bindings of the free variables. Suppose the theorem prover is working on some clause, clause, named by some goal-spec, e.g., "Subgoal *1/2"'" in some logical world, world. Corresponding to the printed goal-spec is an internal data structure called a "clause identifier" id. See *note CLAUSE-IDENTIFIER::. In the case of a common hint, the hint applies if the goal-spec of the hint is the same as the goal-spec of the clause in question. In the case of a computed hint, the variable ID is bound to the clause id, the variable CLAUSE is bound to the (translated form of the) clause, and the variable WORLD is bound to the current ACL2 world. The variable STABLE-UNDER-SIMPLIFICATIONP is bound to t or nil. It is bound to t only if the clause is known to be stable under simplification. That is, the simplifier has been applied to the clause and did not change it. Such a clause is sometimes known as a "simplification checkpoint." It is frequently useful to inject hints (e.g., to enable a rule or provide a :use hint) only when the goal in question has stabilized. If a hint is provided, the processing of the clause starts over with simplification. As for CTX and STATE, they are provided so that you can pass them to the er macro to print error messages. We recommend not writing computed hints that otherwise change STATE! The remaining variables, HIST and PSPV are not documented yet. Only users familiar with the internals of ACL2 are likely to need them or understand their values. For some instruction about how to use computed hints, see *note USING-COMPUTED-HINTS::.  File: acl2-doc-emacs.info, Node: CONSTRAINT, Next: COPYRIGHT, Prev: COMPUTED-HINTS, Up: MISCELLANEOUS CONSTRAINT restrictions on certain functions introduced in encapsulate events Suppose that a given theorem, thm, is to be functionally instantiated using a given functional substitution, alist. (See *note LEMMA-INSTANCE::, or for an example, see *note FUNCTIONAL-INSTANTIATION-EXAMPLE::.) What is the set of proof obligations generated? It is the set obtained by applying alist to all terms, tm, such that (a) tm mentions some function symbol in the domain of alist, and (b) either (i) tm arises from the "constraint" on a function symbol ancestral in thm or in some defaxiom or (ii) tm is the body of a defaxiom. Here, a function symbol is "ancestral" in thm if either it occurs in thm, or it occurs in the definition of some function symbol that occurs in thm, and so on. The remainder of this note explains what we mean by "constraint" in the words above. In a certain sense, function symbols are introduced in essentially two ways. The most common way is to use defun (or when there is mutual recursion, mutual-recursion or defuns). There is also a mechanism for introducing "witness functions"; see *note DEFCHOOSE::. The documentation for these events describes the axioms they introduce, which we will call here their "definitional axioms." These definitional axioms are generally the constraints on the function symbols that these axioms introduce. However, when a function symbol is introduced in the scope of an encapsulate event, its constraints may differ from the definitional axioms introduced for it. For example, suppose that a function's definition is local to the encapsulate; that is, suppose the function is introduced in the signature of the encapsulate. Then its constraints include, at the least, those non-local theorems and definitions in the encapsulate that mention the function symbol. Actually, it will follow from the discussion below that if the signature is empty for an encapsulate, then the constraint on each of its new function symbols is exactly the definitional axiom introduced for it. Intuitively, we view such encapsulates just as we view include-book events. But the general case, where the signature is not empty, is more complicated. In the discussion that follows we describe in detail exactly which constraints are associated with which function symbols that are introduced in the scope of an encapsulate event. In order to simplify the exposition we make two cuts at it. In the first cut we present an over-simplified explanation that nevertheless captures the main ideas. In the second cut we complete our explanation by explaining how we view certain events as being "lifted" out of the encapsulate, resulting in a possibly smaller encapsulate, which becomes the target of the algorithm described in the first cut. At the end of this note we present an example showing why a more naive approach is unsound. Finally, before we start our "first cut," we note that any information you want "exported" outside an encapsulate event must be there as an explicit definition or theorem. For example, even if a function foo has output type (mv t t) in its signature, the system will not know (true-listp (foo x)) merely on account of this information. Thus, if you are using functions like foo (constrained mv functions), then you may find it useful to prove (inside the encapsulate, to be exported) a :type-prescription rule for the constrained function, for example, the :type-prescription rule (true-listp (foo x)). _First cut at constraint-assigning algorithm._ Quite simply, the formulas introduced in the scope of an encapsulate are conjoined, and each function symbol introduced by the encapsulate is assigned that conjunction as its constraint. Clearly this is a rather severe algorithm. Let us consider two possible optimizations in an informal manner before presenting our second cut. Consider the (rather artificial) event below. The function before1 does not refer at all, even indirectly, to the locally-introduced function sig-fn, so it is unfortunate to saddle it with constraints about sig-fn. (encapsulate (((sig-fn *) => *)) (defun before1 (x) (if (consp x) (before1 (cdr x)) x)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) ) We would like to imagine moving the definition of before1 to just in front of this encapsulate, as follows. (defun before1 (x) (if (consp x) (before1 (cdr x)) x)) (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) ) Thus, we will only assign the constraint (consp (sig-fn x)), from the theorem sig-fn-prop, to the function sig-fn, not to the function before1. More generally, suppose an event in an encapsulate event does not mention any function symbol in the signature of the encapsulate, nor any function symbol that mentions any such function symbol, and so on. (We might say that no function symbol from the signature is an "ancestor" of any function symbol occurring in the event.) Then we imagine moving the event, so that it appears in front of the encapsulate. We don't actually move it, but we pretend we do when it comes time to assign constraints. Thus, such definitions only introduce definitional axioms as the constraints on the function symbols being defined. In the example above, the event sig-fn-prop introduces no constraints on function before1. Once this first optimization is performed, we have in mind a set of "constrained functions." These are the functions introduced in the encapsulate that would remain after moving some of them in front, as indicated above. Consider the collection of all formulas introduced by the encapsulate, except the definitional axioms, that mention these constrained functions. So for example, in the event below, no such formula mentions the function symbol after1. (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) (defun after1 (x) (sig-fn x)) ) We can see that there is really no harm in imagining that we move the definition of after1 out of the encapsulate, to just after the encapsulate. Many subtle aspects of this rearrangement process have been omitted. For example, suppose the function fn uses sig-fn, the latter being a function in the signature of the encapsulation. Suppose a formula about fn is proved in the encapsulation. Then from the discussion above fn is among the constrained functions of the encapsulate: it cannot be moved before the encapsulate and it cannot be moved after the encapsulation. But why is fn constrained? The reason is that the theorem proved about fn may impose or express constraints on sig-fn. That is, the theorem proved about fn may depend upon properties of the witness used for sig-fn. Here is a simple example: (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (declare (ignore x)) 0)) (defun fn (lst) (if (endp lst) t (and (integerp (sig-fn (car lst))) (fn (cdr lst))))) (defthm fn-always-true (fn lst))) In this example, there are no defthm events that mention sig-fn explicitly. One might therefore conclude that it is completely unconstrained. But the witness we chose for it always returns an integer. The function fn uses sig-fn and we prove that fn always returns true. Of course, the proof of this theorem depends upon the properties of the witness for sig-fn, even though those properties were not explicitly "called out" in theorems proved about sig-fn. It would be unsound to move fn-always-true after the encapsulate. It would also be unsound to constrain sig-fn to satisfy just fn-always-true without including in the constraint the relation between sig-fn and fn. Hence both sig-fn and fn are constrained by this encapsulation and the constraint imposed on each is the same and states the relation between the two as characterized by the equation defining fn as well as the property that fn always returns true. Suppose, later, one proved a theorem about sig-fn and wished to functionally instantiate it. Then one must also functionally instantiate fn, even if it is not involved in the theorem, because it is only through fn that sig-fn inherits its constrained properties. This is a pathological example that illustrate a trap into which one may easily fall: rather than identify the key properties of the constrained function the user has foreshadowed its intended application and constrained those notions. Clearly, the user wishing to introduce the sig-fn above would be well-advised to use the following instead: (encapsulate (((sig-fn *) => *)) (local (defun sig-fn (x) (declare (ignore x)) 0)) (defthm integerp-sig-fn (integerp (sig-fn x)))) (defun fn (lst) (if (endp lst) t (and (integerp (sig-fn (car lst))) (fn (cdr lst))))) (defthm fn-always-true (fn lst))) Note that sig-fn is constrained merely to be an integer. It is the only constrained function. Now fn is introduced after the encapsulation, as a simple function that uses sig-fn. We prove that fn always returns true, but this fact does not constrain sig-fn. Future uses of sig-fn do not have to consider fn at all. Sometimes it is necessary to introduce a function such as fn within the encapsulate merely to state the key properties of the undefined function sig-fn. But that is unusual and the user should understand that both functions are being constrained. Another subtle aspect of encapsulation that has been brushed over so far has to do with exactly how functions defined within the encapsulation use the signature functions. For example, above we say "Consider the collection of all formulas introduced by the encapsulate, _except the definitional axioms_, that mention these constrained functions." We seem to suggest that a definitional axiom which mentions a constrained function can be moved out of the encapsulation and considered part of the "post-encapsulation" extension of the logical world, if the defined function is not used in any non-definitional formula proved in the encapsulation. For example, in the encapsulation above that constrained sig-fn and introduced fn within the encapsulation, fn was constrained because we proved the formula fn-always-true within the encapsulation. Had we not proved fn-always-true within the encapsulation, fn could have been moved after the encapsulation. But this suggests an unsound rule because whether such a function can be moved after the encapsulate depend on whether its _admission_ used properties of the witnesses! In particular, we say a function is "subversive" if any of its governing tests or the actuals in any recursive call involve a function in which the signature functions are ancestral. See *note SUBVERSIVE-RECURSIONS::. (Aside: The definition of fn in the first enapsulation above that defines fn, i.e., the encapsulation with fn-always-true inside, is subversive because the call of the macro AND expands to a call of IF that governs a recursive call of fn, in this case: (defun fn (lst) (if (endp lst) t (if (integerp (sig-fn (car lst))) (fn (cdr lst)) nil))). If we switch the order of conjuncts in fn, then the definition of fn is no longer subversive, but it still "infects" the constraint generated for the encapsulation, hence for sig-fn, because fn-always-true blocks the definition of fn from being moved back (to after the encapsulation). If we both switch the order of conjuncts and drop fn-always-true from the encapsulation, then the definition of fn is in essence moved back to after the encapsulation, and the constraint for sig-fn no longer includes the definition of fn. End of aside.) Another aspect we have not discussed is what happens to nested encapsulations when each introduces constrained functions. We say an encapsulate event is "trivial" if it introduces no constrained functions, i.e., if its signatures is nil. Trivial encapsulations are just a way to wrap up a collection of events into a single event. From the foregoing discussion we see we are interested in exactly how we can "rearrange" the events in a non-trivial encapsulation - moving some "before" the encapsulation and others "after" the encapsulation. We are also interested in which functions introduced by the encapsulation are "constrained" and what the "constraints" on each are. We may summarize the observations above as follows, after which we conclude with a more elaborate example. _Second cut at constraint-assigning algorithm._ First, we focus only on non-trivial encapsulations that neither contain nor are contained in non-trivial encapsulations. (Nested non-trivial encapsulations are not rearranged at all: do not put anything in such a nest unless you mean for it to become part of the constraints generated.) Second, in what follows we only consider the non-local events of such an encapsulate, assuming that they satisfy the restriction of using no locally defined function symbols other than the signature functions. Given such an encapsulate event, move, to just in front of it and in the same order, all definitions and theorems for which none of the signature functions is ancestral. Now collect up all formulas (theorems) introduced in the encapsulate other than definitional axioms. Add to this set any of those definitional equations that is either subversive or defines a function used in a formula in the set. The conjunction of the resulting set of formulas is called the "constraint" and the set of all the signature functions of the encapsulate together with all function symbols defined in the encapsulate and mentioned in the constraint is called the "constrained functions." Assign the constraint to each of the constrained functions. Move, to just after the encapsulate, the definitions of all function symbols defined in the encapsulate that have been omitted from the constraint. Implementation note. In the implementation we do not actually move events, but we create constraints that pretend that we did. Here is an example illustrating our constraint-assigning algorithm. It builds on the preceding examples. (encapsulate (((sig-fn *) => *)) (defun before1 (x) (if (consp x) (before1 (cdr x)) x)) (local (defun sig-fn (x) (cons x x))) (defthm sig-fn-prop (consp (sig-fn x))) (defun during (x) (if (consp x) x (cons (car (sig-fn x)) 17))) (defun before2 (x) (before1 x)) (defthm before2-prop (atom (before2 x))) (defthm during-prop (implies (and (atom x) (before2 x)) (equal (car (during x)) (car (sig-fn x))))) (defun after1 (x) (sig-fn x)) (defchoose after2 (x) (u) (and (< u x) (during x))) ) Only the functions sig-fn and during receive extra constraints. The functions before1 and before2 are viewed as moving in front of the encapsulate, as is the theorem before2-prop. The functions after1 and after2 are viewed as being moved past the encapsulate. The implementation reports the following. In addition to SIG-FN, we export AFTER2, AFTER1, BEFORE2, DURING and BEFORE1. The following constraint is associated with both of the functions DURING and SIG-FN: (AND (EQUAL (DURING X) (IF (CONSP X) X (CONS (CAR (SIG-FN X)) 17))) (CONSP (SIG-FN X)) (IMPLIES (AND (ATOM X) (BEFORE2 X)) (EQUAL (CAR (DURING X)) (CAR (SIG-FN X))))) Notice that the formula (consp (during x)) is not a conjunct of the constraint. During the first pass of the encapsulate, this formula is stored as a :type-prescription rule deduced during the definition of the function during. However, the rule is not exported because of a rather subtle soundness issue. (If you are interested in details, see the comments in source function putprop-type-prescription-lst.) We conclude by asking (and to a certain extent, answering) the following question: Isn't there an approach to assigning constraints that avoids over-constraining more simply than our "second cut" above? Perhaps it seems that given an encapsulate, we should simply assign to each locally defined function the theorems exported about that function. If we adopted that simple approach the events below would be admissible. (encapsulate (((foo *) => *)) (local (defun foo (x) x)) (defun bar (x) (foo x)) (defthm bar-prop (equal (bar x) x) :rule-classes nil)) (defthm foo-id (equal (foo x) x) :hints (("Goal" :use bar-prop))) ; The following event is not admissible in ACL2. (defthm ouch! nil :rule-classes nil :hints (("Goal" :use ((:functional-instance foo-id (foo (lambda (x) (cons x x)))))))) Under the simple approach we have in mind, bar is constrained to satisfy both its definition and bar-prop because bar mentions a function declared in the signature list of the encapsulation. In fact, bar is so-constrained in the ACL2 semantics of encapsulation and the first two events above (the encapsulate and the consequence that foo must be the identity function) are actually admissible. But under the simple approach to assigning constraints, foo is unconstrained because no theorem about it is exported. Under that approach, ouch! is provable because foo can be instantiated in foo-id to a function other than the identity function. It's tempting to think we can fix this by including definitions, not just theorems, in constraints. But consider the following slightly more elaborate example. The problem is that we need to include as a constraint on foo not only the definition of bar, which mentions foo explicitly, but also abc, which has foo as an ancestor. (encapsulate (((foo *) => *)) (local (defun foo (x) x)) (local (defthm foo-prop (equal (foo x) x))) (defun bar (x) (foo x)) (defun abc (x) (bar x)) (defthm abc-prop (equal (abc x) x) :rule-classes nil)) (defthm foo-id (equal (foo x) x) :hints (("Goal" :use abc-prop))) ; The following event is not admissible in ACL2. (defthm ouch! nil :rule-classes nil :hints (("Goal" :use ((:functional-instance foo-id (foo (lambda (x) (cons x x))) (bar (lambda (x) (cons x x))))))))  File: acl2-doc-emacs.info, Node: COPYRIGHT, Next: COROLLARY, Prev: CONSTRAINT, Up: MISCELLANEOUS COPYRIGHT ACL2 copyright, license, sponsorship ACL2 Version 6.3 - A Computational Logic for Applicative Common Lisp Copyright (C) 2013, Regents of the University of Texas This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0. This program is free software; you can redistribute it and/or modify it under the terms of the LICENSE file distributed with ACL2. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE for more details. Written by: Matt Kaufmann and J Strother Moore email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu Department of Computer Science University of Texas at Austin Austin, TX 78701 U.S.A. Please also see *note ACKNOWLEDGMENTS::.  File: acl2-doc-emacs.info, Node: COROLLARY, Next: CURRENT-PACKAGE, Prev: COPYRIGHT, Up: MISCELLANEOUS COROLLARY the corollary formula of a rune This is a low-level system function at the present time. See *note PR:: and see *note PR!:: instead. Also see *note RULE-CLASSES:: for the use of the symbol :corollary in specifying a rule class.  File: acl2-doc-emacs.info, Node: CURRENT-PACKAGE, Next: CUSTOM-KEYWORD-HINTS, Prev: COROLLARY, Up: MISCELLANEOUS CURRENT-PACKAGE the package used for reading and printing Current-package is an ld special (see *note LD::). The accessor is (current-package state) and the updater is (set-current-package val state), or more conventionally, (in-package val). The value of current-package is actually the string that names the package. (Common Lisp's "package" objects do not exist in ACL2.) The current package must be known to ACL2, i.e., it must be one of the initial packages or a package defined with defpkg by the user. When printing symbols, the package prefix is displayed if it is not the current-package and may be optionally displayed otherwise. Thus, if current-package is "ACL2" then the symbol 'ACL2::SYMB may be printed as SYMB or ACL2::SYMB, while 'MY-PKG::SYMB must be printed as MY-PKG::SYMB. But if current-package is "MY-PKG" then the former symbol must be printed as ACL2::SYMB while the latter may be printed as SYMB. In Common Lisp, current-package also affects how objects are read from character streams. Roughly speaking, read and print are inverses if the current-package is fixed, so reading from a stream produced by printing an object must produce an equal object. In ACL2, the situation is more complicated because we never read objects from character streams, we only read them from object "streams" (channels). Logically speaking, the objects in such a channel are fixed regardless of the setting of current-package. However, our host file systems do not support the idea of Lisp object files and instead only support character files. So when you open an object input channel to a given (character file) we must somehow convert it to a list of ACL2 objects. This is done by a deus ex machina ("a person or thing that appears or is introduced suddenly and unexpectedly and provides a contrived solution to an apparently insoluble difficulty," Webster's Ninth New Collegiate Dictionary). Roughly speaking, the deus ex machina determines what sequence of calls to read-object will occur in the future and what the current-package will be during each of those calls, and then produces a channel containing the sequence of objects produced by an analogous sequence of Common Lisp reads with *current-package* bound appropriately for each. A simple rule suffices to make sane file io possible: before you read an object from an object channel to a file created by printing to a character channel, make sure the current-package at read-time is the same as it was at print-time.  File: acl2-doc-emacs.info, Node: CUSTOM-KEYWORD-HINTS, Next: DEFAULT-BACKCHAIN-LIMIT, Prev: CURRENT-PACKAGE, Up: MISCELLANEOUS CUSTOM-KEYWORD-HINTS user-defined hints See *note ADD-CUSTOM-KEYWORD-HINT:: for a discussion of how advanced users can define their own hint keywords. For examples, see the community books directory books/hints/, in particular basic-tests.lisp. * Menu: * SHOW-CUSTOM-KEYWORD-HINT-EXPANSION:: print out custom keyword hints when they are expanded  File: acl2-doc-emacs.info, Node: SHOW-CUSTOM-KEYWORD-HINT-EXPANSION, Prev: CUSTOM-KEYWORD-HINTS, Up: CUSTOM-KEYWORD-HINTS SHOW-CUSTOM-KEYWORD-HINT-EXPANSION print out custom keyword hints when they are expanded Examples: (show-custom-keyword-hint-expansion t) (show-custom-keyword-hint-expansion nil) General Form: (show-custom-keyword-hint-expansion flg) If the value of flg is non-nil, then when custom keyword hints are expanded, the system prints the results of each expansion. This is sometimes useful for debugging custom keyword hints and, from time to time, may be useful in understanding how a custom hint affects some proof attempt. The default setting is nil. For an explanation of how custom keyword hints are processed, see *note CUSTOM-KEYWORD-HINTS::.  File: acl2-doc-emacs.info, Node: DEFAULT-BACKCHAIN-LIMIT, Next: DEFAULT-DEFUN-MODE, Prev: CUSTOM-KEYWORD-HINTS, Up: MISCELLANEOUS DEFAULT-BACKCHAIN-LIMIT specifying the backchain limit for a rule See *note BACKCHAIN-LIMIT::. The initial value is (nil nil). To inspect the current value (as explained elsewhere; see *note BACKCHAIN-LIMIT::): (default-backchain-limit wrld :ts) ; for type-set reasoning (default-backchain-limit wrld :rewrite) ; for rewriting  File: acl2-doc-emacs.info, Node: DEFAULT-DEFUN-MODE, Next: DEFAULT-HINTS, Prev: DEFAULT-BACKCHAIN-LIMIT, Up: MISCELLANEOUS DEFAULT-DEFUN-MODE the default defun-mode of defun'd functions When a defun is processed and no :mode xarg is supplied, the function default-defun-mode is used. To find the default defun-mode of the current ACL2 world, type (default-defun-mode (w state)). See *note DEFUN-MODE:: for a discussion of defun-modes. To change the default defun-mode of the ACL2 world, type one of the keywords :program or :logic. The default ACL2 prompt displays the current default defun-mode by showing the character p for :program mode, and omitting it for :logic mode; see *note DEFAULT-PRINT-PROMPT::. The default defun-mode may be changed using the keyword commands :program and :logic, which are equivalent to the commands (program) and (logic). Each of these names is documented separately: see *note PROGRAM:: and see *note LOGIC::. The default defun-mode is stored in the table acl2-defaults-table and hence may also be changed by a table command. See *note TABLE:: and also see *note ACL2-DEFAULTS-TABLE::. Both mode-changing commands are events. While events that change the default defun-mode are permitted within an encapsulate or the text of a book, their effects are local in scope to the duration of the encapsulation or inclusion. For example, if the default defun-mode is :logic and a book is included that contains the event (program), then subsequent events within the book are processed with the default defun-mode :program; but when the include-book event completes, the default defun-mode will still be :logic. Commands that change the default defun-mode are not permitted inside local forms.  File: acl2-doc-emacs.info, Node: DEFAULT-HINTS, Next: DEFAULT-PRINT-PROMPT, Prev: DEFAULT-DEFUN-MODE, Up: MISCELLANEOUS DEFAULT-HINTS a list of hints added to every proof attempt Examples: ACL2 !>(default-hints (w state)) ((computed-hint-1 clause) (computed-hint-2 clause stable-under-simplificationp)) The value returned by this function is added to the right of the :hints argument of every defthm and thm command, and to hints provided to defuns as well (:hints, :guard-hints, and (for ACL2(r)) :std-hints). See *note SET-DEFAULT-HINTS:: for a more general discussion. Advanced users only: see *note OVERRIDE-HINTS:: for an advanced variant of default hints that are not superseded by :hints arguments.  File: acl2-doc-emacs.info, Node: DEFAULT-PRINT-PROMPT, Next: DEFAULT-RULER-EXTENDERS, Prev: DEFAULT-HINTS, Up: MISCELLANEOUS DEFAULT-PRINT-PROMPT the default prompt printed by ld Example prompt: ACL2 p!s> The prompt printed by ACL2 displays the current package, followed by a space, followed by zero or more of the three characters as specified below, followed by the character > printed one or more times, reflecting the number of recursive calls of ld. The three characters in the middle are as follows: p ; when (default-defun-mode (w state)) is :program ! ; when guard checking is on s ; when (ld-skip-proofsp state) is t See *note DEFAULT-DEFUN-MODE::, see *note SET-GUARD-CHECKING::, and see *note LD-SKIP-PROOFSP::. Also see *note LD-PROMPT:: to see how to install your own prompt. Here are some examples with ld-skip-proofsp nil. ACL2 !> ; logic mode with guard checking on ACL2 > ; logic mode with guard checking off ACL2 p!> ; program mode with guard checking on ACL2 p> ; program mode with guard checking off Here are some examples with default-defun-mode of :logic. ACL2 > ; guard checking off, ld-skip-proofsp nil ACL2 s> ; guard checking off, ld-skip-proofsp t ACL2 !> ; guard checking on, ld-skip-proofsp nil ACL2 !s> ; guard checking on, ld-skip-proofsp t Finally, here is the prompt in raw mode (see *note SET-RAW-MODE::), regardless of the settings above: ACL2 P>  File: acl2-doc-emacs.info, Node: DEFAULT-RULER-EXTENDERS, Next: DEFUN-MODE, Prev: DEFAULT-PRINT-PROMPT, Up: MISCELLANEOUS DEFAULT-RULER-EXTENDERS the default ruler-extenders for defun'd functions When a defun is processed and no :ruler-extenders xarg is supplied, the function default-ruler-extenders is used to obtain the current ruler-extenders; see *note RULER-EXTENDERS::. To find the default ruler-extenders of the current ACL2 world, type (default-ruler-extenders (w state)). While events that change the default ruler-extenders are permitted within an encapsulate or the text of a book, their effects are local in scope to the duration of the encapsulation or inclusion. See *note DEFAULT-DEFUN-MODE:: for an analogous discussion for defun-modes.  File: acl2-doc-emacs.info, Node: DEFUN-MODE, Next: DEFUNS, Prev: DEFAULT-RULER-EXTENDERS, Up: MISCELLANEOUS DEFUN-MODE determines whether a function definition is a logical act Two "defun-modes" are supported, :program and :logic. Roughly speaking, :program mode allows you to prototype a function for execution without any proof burdens, while :logic mode allows you to add a new definitional axiom to the logic. The system comes up in :logic mode. Execution of functions whose defun-mode is :program may render ACL2 unsound! See *note DEFUN-MODE-CAVEAT::. Note that calls of local and of many events are skipped in :program mode; see *note PROGRAM::. When you define a function in the ACL2 logic, that function can be run on concrete data. But it is also possible to reason deductively about the function because each definition extends the underlying logic with a definitional axiom. To ensure that the logic is sound after the addition of this axiom, certain restrictions have to be met, namely that the recursion terminates. This can be quite challenging. Because ACL2 is a programming language, you often may wish simply to program in ACL2. For example, you may wish to define your system and test it, without any logical burden. Or, you may wish to define "utility" functions -- functions that are executed to help manage the task of building your system but functions whose logical properties are of no immediate concern. Such functions might be used to generate test data or help interpret the results of tests. They might create files or explore the ACL2 database. The termination arguments for such functions are an unnecessary burden provided no axioms about the functions are ever used in deductions. Thus, ACL2 introduces the idea of the "defun-mode" of a function. The :mode keyword of defun's declare xarg allows you to specify the defun-mode of a given definition. If no :mode keyword is supplied, the default defun-mode is used; see *note DEFAULT-DEFUN-MODE::. There are two defun-modes, each of which is written as a keyword: :program -- logically undefined but executable outside deductive contexts. :logic -- axiomatically defined as per the ACL2 definitional principle. It is possible to change the defun-mode of a function from :program to :logic. We discuss this below. We think of functions having :program mode as "dangerous" functions, while functions having :logic mode are "safe." The only requirement enforced on :program mode functions is the syntactic one: each definition must be well-formed ACL2. Naively speaking, if a :program mode function fails to terminate then no harm is done because no axiom is added (so inconsistency is avoided) and some invocations of the function may simply never return. This simplistic justification of :program mode execution is faulty because it ignores the damage that might be caused by "mis-guarded" functions. See *note DEFUN-MODE-CAVEAT::. We therefore implicitly describe an imagined implementation of defun-modes that is safe and, we think, effective. But please see *note DEFUN-MODE-CAVEAT::. The default defun-mode is :logic. This means that when you defun a function the system will try to prove termination. If you wish to introduce a function of a different defun-mode use the :mode xargs keyword. Below we show fact introduced as a function in :program mode. (defun fact (n) (declare (xargs :mode :program)) (if (or (not (integerp n)) (= n 0)) 1 (* n (fact (1- n))))) No axiom is added to the logic as a result of this definition. By introducing fact in :program mode we avoid the burden of a termination proof, while still having the option of executing the function. For example, you can type ACL2 !>(fact 3) and get the answer 6. If you type (fact -1) you will get a hard lisp error due to "infinite recursion." However, the ACL2 theorem prover knows no axioms about fact. In particular, if the term (fact 3) arises in a proof, the theorem prover is unable to deduce that it is 6. From the perspective of the theorem prover it is as though fact were an undefined function symbol of arity 1. Thus, modulo certain important issues (see *note DEFUN-MODE-CAVEAT::), the introduction of this function in :program mode does not imperil the soundness of the system -- despite the fact that the termination argument for fact was omitted -- because nothing of interest can be proved about fact. Indeed, we do not allow fact to be used in logical contexts such as conjectures submitted for proof. It is possible to convert a function from :program mode to :logic mode at the cost of proving that it is admissible. This can be done by invoking (verify-termination fact) which is equivalent to submitting the defun of fact, again, but in :logic mode. (defun fact (n) (declare (xargs :mode :logic)) (if (or (not (integerp n)) (= n 0)) 1 (* n (fact (1- n))))) This particular event will fail because the termination argument requires that n be nonnegative. A repaired defun, for example with = replaced by <=, will succeed, and an axiom about fact will henceforth be available. Technically, verify-termination submits a redefinition of the :program mode function. This is permitted, even when ld-redefinition-action is nil, because the new definition is identical to the old (except for its :mode and, possibly, other non-logical properties). See *note GUARD:: for a discussion of how to restrict the execution of functions. Guards may be "verified" for functions in :logic mode; see *note VERIFY-GUARDS::.  File: acl2-doc-emacs.info, Node: DEFUNS, Next: DISABLE-FORCING, Prev: DEFUN-MODE, Up: MISCELLANEOUS DEFUNS an alternative to mutual-recursion Example: (DEFUNS (evenlp (x) (if (consp x) (oddlp (cdr x)) t)) (oddlp (x) (if (consp x) (evenlp (cdr x)) nil))) General Form: (DEFUNS defuns-tuple1 ... defuns-tuplen) is equivalent to (MUTUAL-RECURSION (DEFUN . defuns-tuple1) ... (DEFUN . defuns-tuplen)) In fact, defuns is the more primitive of the two and mutual-recursion is just a macro that expands to a call of defun after stripping off the defun at the car of each argument to mutual-recursion. We provide and use mutual-recursion rather than defuns because by leaving the defuns in place, mutual-recursion forms can be processed by the Emacs tags program. See *note MUTUAL-RECURSION::.  File: acl2-doc-emacs.info, Node: DISABLE-FORCING, Next: DISABLE-IMMEDIATE-FORCE-MODEP, Prev: DEFUNS, Up: MISCELLANEOUS DISABLE-FORCING to disallow forced case-splits General Form: ACL2 !>:disable-forcing ; disallow forced case splits See *note FORCE:: and see *note CASE-SPLIT:: for a discussion of forced case splits, which are inhibited by this command. Disable-forcing is actually a macro that disables the executable counterpart of the function symbol force; see *note FORCE::. When you want to use hints to turn off forced case splits, use a form such as one of the following (these are equivalent). :in-theory (disable (:executable-counterpart force)) :in-theory (disable (force))  File: acl2-doc-emacs.info, Node: DISABLE-IMMEDIATE-FORCE-MODEP, Next: DISABLEDP, Prev: DISABLE-FORCING, Up: MISCELLANEOUS DISABLE-IMMEDIATE-FORCE-MODEP forced hypotheses are not attacked immediately General Form: ACL2 !>:disable-immediate-force-modep This event causes ACL2 to delay forced hypotheses to the next forcing round, rather than attacking them immediately. See *note IMMEDIATE-FORCE-MODEP::. Or for more basic information, first see *note FORCE:: for a discussion of forced case splits. Disable-immediate-force-modep is a macro that disables the executable counterpart of the function symbol immediate-force-modep. When you want to disable this mode in hints, use a form such as one of the following (these are equivalent). :in-theory (disable (:executable-counterpart immediate-force-modep)) :in-theory (disable (immediate-force-modep))  File: acl2-doc-emacs.info, Node: DISABLEDP, Next: DO-NOT, Prev: DISABLE-IMMEDIATE-FORCE-MODEP, Up: MISCELLANEOUS DISABLEDP determine whether a given name or rune is disabled Examples: :disabledp foo ; returns a list of all disabled runes whose base ; symbol is foo (see *note RUNE::) (disabledp 'foo) ; same as above (i.e., :disabledp foo) :disabledp (:rewrite bar . 1) ; returns t if the indicated rune is ; disabled, else nil (disabledp (:rewrite bar . 1)); same as immediately above Also see *note PR::, which gives much more information about the rules associated with a given event. Disabledp takes one argument, an event name (see *note EVENTS::) other than nil or a rune. In the former case it returns the list of disabled runes associated with that name, in the sense that the rune's "base symbol" is that name (see *note RUNE::) or, if the event named is a defmacro event, then the list of disabled runes associated with the function corresponding to that macro name, if any (see *note MACRO-ALIASES-TABLE::). In the latter case, where the argument is a rune, disabledp returns t if the rune is disabled, and nil otherwise.  File: acl2-doc-emacs.info, Node: DO-NOT, Next: DO-NOT-INDUCT, Prev: DISABLEDP, Up: MISCELLANEOUS DO-NOT hints keyword :DO-NOT See *note HINTS::.  File: acl2-doc-emacs.info, Node: DO-NOT-INDUCT, Next: DOUBLE-REWRITE, Prev: DO-NOT, Up: MISCELLANEOUS DO-NOT-INDUCT hints keyword :DO-NOT-INDUCT See *note HINTS::.  File: acl2-doc-emacs.info, Node: DOUBLE-REWRITE, Next: EMBEDDED-EVENT-FORM, Prev: DO-NOT-INDUCT, Up: MISCELLANEOUS DOUBLE-REWRITE cause a term to be rewritten twice Logically, double-rewrite is the identity function: (double-rewrite x) is equal to x. However, the ACL2 rewriter treats calls of double-rewrite in the following special manner. When it encounters a term (double-rewrite u), it first rewrites u in the current context, and then the rewriter rewrites the result. Such double-rewriting is rarely necessary, but it can be useful when rewriting under non-trivial equivalence relations (see *note EQUIVALENCE::). The following example will illustrate the issue. ; Define an equivalence relation. (defun my-equiv (x y) (equal x y)) (defequiv my-equiv) ; Define a unary function whose argument is preserved by my-equiv. (defun foo (x) (declare (ignore x)) t) (defcong my-equiv equal (foo x) 1) ; Define some other unary functions. (defun g (x) x) (defun h1 (x) x) (defun h2 (x) x) ; Prove some lemmas and then disable the functions above. (defthm lemma-1 (my-equiv (h1 x) (h2 x))) (defthm lemma-2 (foo (h2 x))) (defthm lemma-3 (implies (foo x) (equal (g x) x))) (in-theory (union-theories (theory 'minimal-theory) '(lemma-1 lemma-2 lemma-3 my-equiv-implies-equal-foo-1))) ; Attempt to prove a simple theorem that follows ``obviously'' from the ; events above. (thm (equal (g (h1 a)) (h1 a))) We might expect the proof of this final thm to succeed by the following reasoning. It is immediate from lemma-3 provided we can establish (foo (h1 a)). By the defcong event above, we know that (foo (h1 a)) equals (foo (h2 a)) provided (my-equiv (h1 a) (h2 a)); but this is immediate from lemma-1. And finally, (foo (h2 a)) is true by lemma-2. Unfortunately, the proof fails. But fortunately, ACL2 gives the following useful warning when lemma-3 is submitted: ACL2 Warning [Double-rewrite] in ( DEFTHM LEMMA-3 ...): In the :REWRITE rule generated from LEMMA-3, equivalence relation MY-EQUIV is maintained at one problematic occurrence of variable X in hypothesis (FOO X), but not at any binding occurrence of X. Consider replacing that occurrence of X in this hypothesis with (DOUBLE-REWRITE X). See :doc double- rewrite for more information on this issue. We can follow the warning's advice by changing lemma-3 to the following. (defthm lemma-3 (implies (foo (double-rewrite x)) (equal (g x) x))) With this change, the proof succeeds for the final thm above. In practice, it should suffice for users to follow the advice given in the "Double-rewrite" warnings, by adding calls of double-rewrite around certain variable occurrences. But this can cause inefficiency in large proof efforts. For that reason, and for completeness, it seems prudent to explain more carefully what is going on; and that is what we do for the remainder of this documentation topic. Optionally, also see the paper "Double Rewriting for Equivalential Reasoning in ACL2" by Matt Kaufmann and J Strother Moore, in the proceedings of the 2006 ACL2 Workshop (paper is published in ACM Digital Library, `http://portal.acm.org/toc.cfm?id=1217975'). *Suggesting congruence rules.* Sometimes the best way to respond to a "Double-rewrite" warning may be to prove a congruence rule. Consider for example this rule. (defthm insert-sort-is-id (perm (insert-sort x) x)) Assuming that perm has been identified as an equivalence relation (see *note DEFEQUIV::), we will get the following warning. ACL2 Warning [Double-rewrite] in ( DEFTHM INSERT-SORT-IS-ID ...): In a :REWRITE rule generated from INSERT-SORT-IS-ID, equivalence relation PERM is maintained at one problematic occurrence of variable X in the right-hand side, but not at any binding occurrence of X. Consider replacing that occurrence of X in the right-hand side with (DOUBLE-REWRITE X). See :doc double-rewrite for more information on this issue. The problem is that the second occurrence of x (the right-hand side of the rule insert-sort-is-id) is in a context where perm is to be maintained, yet in this example, the argument x of insert-sort on the left-hand side of that rule is in a context where perm will not be maintained. This can lead one to consider the possibility that perm could be maintained in that left-hand side occurrence of x, and if so, to prove the following congruence rule. (defcong perm perm (insert-sort x) 1) This will eliminate the above warning for insert-sort-is-id. More important, this defcong event would probably be useful, since it would allow rewrite rules with equivalence relation perm to operate on the first argument of any call of insert-sort whose context calls for maintaining perm. *Details on double-rewrite.* The reader who wants these details may first wish to see *note EQUIVALENCE:: for relevant review. The ACL2 rewriter takes a number of contextual arguments, including the generated equivalence relation being maintained (see *note CONGRUENCE::) and an association list that maps variables to terms. We call the latter alist the unify-subst because it is produced by unifying (actually matching) a pattern against a current term; let us explain this point by returning to the example above. Consider what happens when the rewriter is given the top-level goal of the thm above. (equal (g (h1 a)) (h1 a)) This rewrite is performed with the empty alist (unify-subst), and is begun by rewriting the first argument (in that same empty unify-subst): (g (h1 a)) Note that the only equivalence relation being maintained at this point is equal. Now, the rewriter notices that the left-hand side of lemma-3, which is (g x), matches (g (h1 a)). The rewriter thus creates a unify-subst binding x to (h1 a): ((x . (h1 a))). It now attempts to rewrite the hypothesis of lemma-3 to t under this unify-subst. Consider what happens now if the hypothesis of lemma-3 is (foo x). To rewrite this hypothesis under a unify-subst of ((x . (h1 a))), it will first rewrite x under this unify-subst. The key observation here is that this rewrite takes place simply by returning the value of x in the unify-subst, namely (h1 a). No further rewriting is done! The efficiency of the ACL2 rewriter depends on such caching of previous rewriting results. But suppose that, instead, the hypothesis of lemma-3 is (foo (double-rewrite x)). As before, the rewriter dives to the first argument of this call of foo. But this time the rewriter sees the call (double-rewrite x), which it handles as follows. First, x is rewritten as before, yielding (h1 a). But now, because of the call of double-rewrite, the rewriter takes (h1 a) and rewrites it under the empty unify-subst. What's more, because of the defcong event above, this rewrite takes place in a context where it suffices to maintain the equivalence relation my-equiv. This allows for the application of lemma-1, hence (h1 a) is rewritten (under unify-subst = nil) to (h2 a). Popping back up, the rewriter will now rewrite the call of foo to t using lemma-2. The example above explains how the rewriter treats calls of double-rewrite, but it may leave the unfortunate impression that the user needs to consider each :rewrite or :linear rule carefully, just in case a call of double-rewrite may be appropriate. Fortunately, ACL2 provides a "[Double-rewrite]" warning to inform the user of just this sort of situation. If you don't see this warning when you submit a (:rewrite or :linear) rule, then the issue described here shouldn't come up for that rule. Such warnings may appear for hypotheses or right-hand side of a :rewrite rule, and for hypotheses or full conclusion (as opposed to just the trigger term) of a :linear rule. If you do see a "[Double-rewrite]" warning, then should you add the indicated call(s) of double-rewrite? At the time of writing this documentation, the answer is not clear. Early experiments with double rewriting suggested that it may be too expensive to call double-rewrite in every instance where a warning indicates that there could be an advantage to doing so. And at the time of this writing, the ACL2 regression suite has about 1900 such warnings (but note that books were developed before double-rewrite or the "[Double-rewrite]" warning were implemented), which suggests that one can often do fine just ignoring such warnings. However, it seems advisable to go ahead and add the calls of double-rewrite indicated by the warnings unless you run across efficiency problems caused by doing so. Of course, if you decide to ignore all such warnings you can execute the event: (set-inhibit-warnings "Double-rewrite"). Finally, we note that it is generally not necessary to call double-rewrite in order to get its effect in the following case, where the discussion above might have led one to consider a call of double-rewrite: a hypothesis is a variable, or more generally, we are considering a variable occurrence that is a branch of the top-level IF structure of a hypothesis. The automatic handling of this case, by a form of double rewriting, was instituted in ACL2 Version_2.9 and remains in place with the introduction of double-rewrite. Here is a simple illustrative example. Notice that foo-holds applies to prove the final thm below, even without a call of double-rewrite in the hypothesis of foo-holds, and that there is no "[Double-rewrite]" warning when submitting foo-holds. (encapsulate (((foo *) => *) ((bar *) => *)) (local (defun foo (x) (declare (ignore x)) t)) (local (defun bar (x) (declare (ignore x)) t)) (defthm foo-holds (implies x (equal (foo x) t))) (defthm bar-holds-propositionally (iff (bar x) t))) (thm (foo (bar y)))  File: acl2-doc-emacs.info, Node: EMBEDDED-EVENT-FORM, Next: ENABLE-FORCING, Prev: DOUBLE-REWRITE, Up: MISCELLANEOUS EMBEDDED-EVENT-FORM forms that may be embedded in other events Examples: (defun hd (x) (if (consp x) (car x) 0)) (local (defthm lemma23 ...)) (progn (defun fn1 ...) (local (defun fn2 ...)) ...) General Form: An embedded event form is a term, x, such that: o x is a call of an event function other than DEFPKG (see *note EVENTS:: for a listing of the event functions); o x is of the form (LOCAL x1) where x1 is an embedded event form; o x is of the form (SKIP-PROOFS x1) where x1 is an embedded event form; o x is of the form (MAKE-EVENT &), where & is any term whose expansion is an embedded event (see *note MAKE-EVENT::); o x is of the form (WITH-OUTPUT ... x1), (WITH-PROVER-STEP-LIMIT ... x1 ...), or (WITH-PROVER-TIME-LIMIT ... x1), where x1 is an embedded event form; o x is a call of ENCAPSULATE, PROGN, PROGN!, or INCLUDE-BOOK; o x macroexpands to one of the forms above; or o [intended only for the implementation] x is (RECORD-EXPANSION x1 x2), where x1 and x2 are embedded event forms. An exception: an embedded event form may not set the acl2-defaults-table when in the context of local. Thus for example, the form (local (table acl2-defaults-table :defun-mode :program)) is not an embedded event form, nor is the form (local (program)), since the latter sets the acl2-defaults-table implicitly. An example at the end of the discussion below illustrates why there is this restriction. Only embedded event forms are allowed in a book after its initial in-package form. See *note BOOKS::. However, you may find that make-event allows you to get the effect you want for a form that is not an embedded event form. For example, you can put the following into a book, which assigns the value 17 to state global variable x: (make-event (er-progn (assign x 17) (value '(value-triple nil))) :check-expansion t) When an embedded event is executed while ld-skip-proofsp is 'include-book, those parts of it inside local forms are ignored. Thus, (progn (defun f1 () 1) (local (defun f2 () 2)) (defun f3 () 3)) will define f1, f2, and f3 when ld-skip-proofsp is nil or t, but will define only f1 and f3 when ld-skip-proofsp is 'include-book. _Discussion:_ Encapsulate, progn, and include-book place restrictions on the kinds of forms that may be processed. These restrictions ensure that the non-local events are indeed admissible provided that the sequence of local and non-local events is admissible when proofs are done, i.e., when ld-skip-proofs is nil. But progn! places no such restrictions, hence is potentially dangerous and should be avoided unless you understand the ramifications; so it is illegal unless there is an active trust tag (see *note DEFTTAG::). Local permits the hiding of an event or group of events in the sense that local events are processed when we are trying to establish the admissibility of a sequence of events embedded in encapsulate forms or in books, but are ignored when we are constructing the world produced by assuming that sequence. Thus, for example, a particularly ugly and inefficient :rewrite rule might be made local to an encapsulate that "exports" a desirable theorem whose proof requires the ugly lemma. To see why we can't allow just anything as an embedded event, consider allowing the form (if (ld-skip-proofsp state) (defun foo () 2) (defun foo () 1)) followed by (defthm foo-is-1 (equal (foo) 1)). When we process the events with ld-skip-proofsp is nil, the second defun is executed and the defthm succeeds. But when we process the events with ld-skip-proofsp 'include-book, the second defun is executed, so that foo no longer has the same definition it did when we proved foo-is-1. Thus, an invalid formula is assumed when we process the defthm while skipping proofs. Thus, the first form above is not a legal embedded event form. If you encounter a situation where these restrictions seem to prevent you from doing what you want to do, then you may find make-event to be helpful. See *note MAKE-EVENT::. Defpkg is not allowed because it affects how things are read after it is executed. But all the forms embedded in an event are read before any are executed. That is, (encapsulate nil (defpkg "MY-PKG" nil) (defun foo () 'my-pkg::bar)) makes no sense since my-pkg::bar must have been read before the defpkg for "MY-PKG" was executed. Finally, let us elaborate on the restriction mentioned earlier related to the acl2-defaults-table. Consider the following form. (encapsulate () (local (program)) (defun foo (x) (if (equal 0 x) 0 (1+ (foo (- x)))))) See *note LOCAL-INCOMPATIBILITY:: for a discussion of how encapsulate processes event forms. Briefly, on the first pass through the events the definition of foo will be accepted in defun mode :program, and hence accepted. But on the second pass the form (local (program)) is skipped because it is marked as local, and hence foo is accepted in defun mode :logic. Yet, no proof has been performed in order to admit foo, and in fact, it is not hard to prove a contradiction from this definition!  File: acl2-doc-emacs.info, Node: ENABLE-FORCING, Next: ENABLE-IMMEDIATE-FORCE-MODEP, Prev: EMBEDDED-EVENT-FORM, Up: MISCELLANEOUS ENABLE-FORCING to allow forced splits General Form: ACL2 !>:enable-forcing ; allowed forced case splits See *note FORCE:: and see *note CASE-SPLIT:: for a discussion of forced case splits, which are turned back on by this command. (See *note DISABLE-FORCING:: for how to turn them off.) Enable-forcing is actually a macro that enables the executable counterpart of the function symbol force; see *note FORCE::. When you want to use hints to turn on forced case splits, use a form such as one of the following (these are equivalent). :in-theory (enable (:executable-counterpart force)) :in-theory (enable (force))  File: acl2-doc-emacs.info, Node: ENABLE-IMMEDIATE-FORCE-MODEP, Next: ENTER-BOOT-STRAP-MODE, Prev: ENABLE-FORCING, Up: MISCELLANEOUS ENABLE-IMMEDIATE-FORCE-MODEP forced hypotheses are attacked immediately General Form: ACL2 !>:enable-immediate-force-modep This event causes ACL2 to attack forced hypotheses immediately instead of delaying them to the next forcing round. See *note IMMEDIATE-FORCE-MODEP::. Or for more basic information, first see *note FORCE:: for a discussion of forced case splits. Enable-immediate-force-modep is a macro that enables the executable counterpart of the function symbol immediate-force-modep. When you want to enable this mode in hints, use a form such as one of the following (these are equivalent). :in-theory (enable (:executable-counterpart immediate-force-modep)) :in-theory (enable (immediate-force-modep))  File: acl2-doc-emacs.info, Node: ENTER-BOOT-STRAP-MODE, Next: ESCAPE-TO-COMMON-LISP, Prev: ENABLE-IMMEDIATE-FORCE-MODEP, Up: MISCELLANEOUS ENTER-BOOT-STRAP-MODE The first millisecond of the Big Bang ACL2 functions, e.g., if, that show enter-boot-strap-mode as their defining command are in fact primitives. It is impossible for the system to display defining axioms about these symbols. Enter-boot-strap-mode is a Common Lisp function but not an ACL2 function. It magically creates from nil an ACL2 property list world that lets us start the boot-strapping process. That is, once enter-boot-strap-mode has created its world, it is possible to process the defconsts, defuns, and defaxioms, necessary to bring up the rest of the system. Before that world is created, the attempt by ACL2 even to translate a defun form, say, would produce an error because defun is undefined. Several ACL2 functions show enter-boot-strap-mode as their defining command. Among them are if, cons, car, and cdr. These functions are characterized by axioms rather than definitional equations -- axioms that in most cases are built into our code and hence do not have any explicit representation among the rules and formulas in the system.  File: acl2-doc-emacs.info, Node: ESCAPE-TO-COMMON-LISP, Next: EXECUTABLE-COUNTERPART, Prev: ENTER-BOOT-STRAP-MODE, Up: MISCELLANEOUS ESCAPE-TO-COMMON-LISP escaping to Common Lisp Example: ACL2 !>:Q There is no Common Lisp escape feature in the lp. This is part of the price of purity. To execute a form in Common Lisp as opposed to ACL2, exit lp with :q, submit the desired forms to the Common Lisp read-eval-print loop, and reenter ACL2 with (lp).  File: acl2-doc-emacs.info, Node: EXECUTABLE-COUNTERPART, Next: EXIT-BOOT-STRAP-MODE, Prev: ESCAPE-TO-COMMON-LISP, Up: MISCELLANEOUS EXECUTABLE-COUNTERPART a rule for computing the value of a function Examples: (:executable-counterpart length) which may be abbreviated in theories as (length) Every defun introduces at least two rules used by the theorem prover. Suppose fn is the name of a defun'd function. Then (:definition fn) is the rune (see *note RUNE::) naming the rule that allows the simplifier to replace calls of fn by its instantiated body. (:executable-counterpart fn) is the rune for the rule for how to evaluate the function on known constants. When typing theories it is convenient to know that (fn) is a runic designator that denotes (:executable-counterpart fn). See *note THEORIES::. If (:executable-counterpart fn) is enabled, then when applications of fn to known constants are seen by the simplifier they are computed out by executing the Common Lisp code for fn (with the appropriate handling of guards). Suppose fact is defined as the factorial function. If the executable counterpart rune of fact, (:executable-counterpart fact), is enabled when the simplifier encounters (fact 12), then that term will be "immediately" expanded to 479001600. Note that even if subroutines of fn have disabled executable counterparts, fn will call their Lisp code nonetheless: once an executable counterpart function is applied, no subsidiary enable checks are made. Such one-step expansions are sometimes counterproductive because they prevent the anticipated application of certain lemmas about the subroutines of the expanded function. Such computed expansions can be prevented by disabling the executable counterpart rune of the relevant function. For example, if (:executable-counterpart fact) is disabled, (fact 12) will not be expanded by computation. In this situation, (fact 12) may be rewritten to (* 12 (fact 11)), using the rule named (:definition fact), provided the system's heuristics permit the introduction of the term (fact 11). Note that lemmas about multiplication may then be applicable (while such lemmas would be inapplicable to 479001600). In many proofs it is desirable to disable the executable counterpart runes of certain functions to prevent their expansion by computation. See *note EXECUTABLE-COUNTERPART-THEORY::. Finally: What do we do about functions that are "constrained" rather than defined, such as the following? (See *note ENCAPSULATE::.) (encapsulate (((foo *) => *)) (local (defun foo (x) x))) Does foo have an executable counterpart? Yes: since the vast majority of functions have sensible executable counterparts, it was decided that *all* functions, even such "constrained" ones, have executable counterparts. We essentially "trap" when such calls are inappropriate. Thus, consider for example: (defun bar (x) (if (rationalp x) (+ x 1) (foo x))) If the term (bar '3) is encountered by the ACL2 rewriter during a proof, and if the :executable-counterpart of bar is enabled, then it will be invoked to reduce this term to '4. However, if the term (bar 'a) is encountered during a proof, then since 'a is not a rationalp and since the :executable-counterpart of foo is only a "trap," then this call of the :executable-counterpart of bar will result in a "trap." In that case, the rewriter will return the term (hide (bar 'a)) so that it never has to go through this process again. See *note HIDE::.  File: acl2-doc-emacs.info, Node: EXIT-BOOT-STRAP-MODE, Next: EXPAND, Prev: EXECUTABLE-COUNTERPART, Up: MISCELLANEOUS EXIT-BOOT-STRAP-MODE the end of pre-history Exit-boot-strap-mode is the last step in creating the ACL2 world in which the user lives. It has command number 0. Commands before it are part of the system initialization and extend all the way back to :min. Commands after it are those of the user. Exit-boot-strap-mode is a Common Lisp function but not an ACL2 function. It is called when every defconst, defun, etc., in our source code has been processed under ACL2 and the world is all but complete. exit-boot-strap-mode has only one job: to signal the completion of the boot-strapping.  File: acl2-doc-emacs.info, Node: EXPAND, Next: EXTENDED-METAFUNCTIONS, Prev: EXIT-BOOT-STRAP-MODE, Up: MISCELLANEOUS EXPAND hints keyword :EXPAND See *note HINTS::.  File: acl2-doc-emacs.info, Node: EXTENDED-METAFUNCTIONS, Next: FAILED-FORCING, Prev: EXPAND, Up: MISCELLANEOUS EXTENDED-METAFUNCTIONS state and context sensitive metafunctions General Form of an Extended :Meta theorem: (implies (and (pseudo-termp x) ; this hyp is optional (alistp a) ; this hyp is optional (ev (hyp-fn x mfc state) a) ; this hyp is optional ; meta-extract hyps may also be included (see below) ) (equiv (ev x a) (ev (fn x mfc state) a))) where the restrictions are as described in the documentation for meta where state is literally the symbol STATE, and x, a, mfc, and state are distinct variable symbols. A :meta theorem of the above form installs fn as a metatheoretic simplifier with hypothesis function hyp-fn, exactly as for vanilla metafunctions. The only difference is that when the metafunctions are applied, some contextual information is passed in via the mfc argument and the ACL2 state is made available. See *note META:: for a discussion of vanilla flavored metafunctions. This documentation assumes you are familiar with the simpler situation, in particular, how to define a vanilla flavored metafunction, fn, and its associated hypothesis metafunction, hyp-fn, and how to state and prove metatheorems installing such functions. Defining extended metafunctions requires that you also be familiar with many ACL2 implementation details. This documentation is sketchy on these details; see the ACL2 source code or email the acl2-help list if you need more help. Additional hypotheses are supported, called "meta-extract hypotheses", that allow metafunctions to depend on the validity of certain terms extracted from the context or the logical world. These hypotheses provide an even more advanced form of metatheorem so we explain them elsewhere; see *note META-EXTRACT::. The metafunction context, mfc, is a list containing many different data structures used by various internal ACL2 functions. We do not document the form of mfc. Your extended metafunction should just take mfc as its second formal and pass it into the functions mentioned below. The ACL2 state is well-documented (see *note STATE::). Below we present expressions below that can be useful in defining extended metafunctions. Some of these expressions involve keyword arguments, :forcep and :ttree, which are optional and in most cases are fine to omit, and which we explain after we present the useful expressions. (mfc-clause mfc): returns the current goal, in clausal form. A clause is a list of ACL2 terms, implicitly denoting the disjunction of the listed terms. The clause returned by mfc-clause is the clausal form of the translation (see *note TRANS::) of the goal or subgoal on which the rewriter is working. When a metafunction calls mfc-clause, the term being rewritten by the metafunction either occurs somewhere in this clause or, perhaps more commonly, is being rewritten on behalf of some lemma to which the rewriter has backchained while trying to rewrite a term in the clause. (mfc-ancestors mfc): returns an alist whose keys are the negations of the backchaining hypotheses being pursued. In particular, (null (mfc-ancestors mfc)) will be true exactly when rewriting is on part of the current goal. Exception: An element of this alist whose key is of the form (:binding-hyp hyp unify-subst) indicates that hyp has been encountered as a hypothesis of the form (equal var term) or (equiv var (double-rewrite-term)), in each case binding variable var to the result of rewriting term under unify-subst. (mfc-rdepth mfc): returns the remaining stack depth for calls of the rewriter (by default, *default-rewrite-stack-limit* at the top level; see *note REWRITE-STACK-LIMIT::). When this is 0, no further calls of the rewriter can be made without error. (mfc-type-alist mfc): returns the type-alist governing the occurrence of the term, x, being rewritten by the metafunction. A type-alist is an association list, each element of which is of the form (term ts . ttree). Such an element means that the term term has the type-set ts. The ttree component is probably irrelevant here. All the terms in the type-alist are in translated form (see *note TRANS::). The ts are numbers denoting finite Boolean combinations of ACL2's primitive types (see *note TYPE-SET::). The type-alist includes not only information gleaned from the conditions governing the term being rewritten but also that gleaned from forward chaining from the (negations of the) other literals in the clause returned by mfc-clause. (mfc-unify-subst mfc): returns nil except when evaluating a syntaxp or bind-free hypothesis, in which case, returns the unifying substitution present at the start of that evaluation. (mfc-world mfc): returns the ACL2 logical world. (mfc-ts term mfc state :forcep forcep :ttreep ttreep): returns the type-set of term in the current context; see *note TYPE-SET::. (mfc-rw term obj equiv-info mfc state): returns the result of rewriting term in the current context, mfc, with objective obj and the equivalence relation described by equiv-info. Obj should be t, nil, or ?, and describes your objective: try to show that term is true, false, or anything. Equiv-info is either nil, t, a function symbol fn naming a known equivalence relation, or a list of congruence rules. Nil means return a term that is equal to term. T means return a term that is propositionally equivalent (i.e., in the iff sense) to term, while fn means return a term fn-equivalent to term. The final case, which is intended only for advanced users, allows the specification of generated equivalence relations, as supplied to the geneqv argument of rewrite. Generally, if you wish to establish that term is true in the current context, use the idiom (equal (mfc-rw term t t mfc state) *t*) The constant *t* is set to the internal form of the constant term t, i.e., 't. (mfc-rw+ term alist obj equiv-info mfc state): if alist is nil then this is equivalent to (mfc-rw term obj equiv-info mfc state). However, the former takes an argument, alist, that binds variables to terms, and returns the result of rewriting term under that alist, where this rewriting is as described for mfc-rw above. The function mfc-rw+ can be more efficient than mfc-rw, because the terms in the binding alist have generally already been rewritten, and it can be inefficient to rewrite them again. For example, consider a rewrite rule of the following form. (implies (and ... (syntaxp (... (mfc-rw `(bar ,x) ...) ...)) ...) (equal (... x ...) ...)) Here, x is bound in the conclusion to the result of rewriting some term, say, tm. Then the above call of mfc-rw will rewrite tm, which is probably unnecessary. So a preferable form of the rule above may be as follows, so that tm is not further rewritten by mfc-rw+. (implies (and ... (syntaxp (... (mfc-rw+ '(bar v) `((v . ,x)) ...) ...)) ...) (equal (... x ...) ...)) However, you may find that the additional rewriting done by mfc-rw is useful in some cases. (mfc-ap term mfc state): returns t or nil according to whether linear arithmetic can determine that term is false. To the cognoscenti: returns the contradiction flag produced by linearizing term and adding it to the linear-pot-lst. (mfc-relieve-hyp hyp alist rune target bkptr mfc state): returns t or nil according to whether the indicated hypothesis term, hyp, can be relieved (proved) under the giving variable bindings, alist. Note that this function returns nil if hyp has free variables (see *note FREE-VARIABLES::). Here, hyp is the hypothesis of the indicated rune at (one-based) position bkptr, and target is an instantiated term to which rune is being applied. Note that no indication is returned for whether any assumptions have been generated by force or case-split. (If you need such a feature, feel free to request it of the ACL2 implementors.) We explain the :forcep and :ttreep keyword arguments provided in some expressions, as promised above. Their defaults are :same and nil, respectively. A value of :same for :forcep means that forcing will be allowed if and only if it is allowed in the current rewriting environment; see *note FORCE::. A value of t or nil for :forcep overrides this default and allows or disallows forcing, respectively. By default these functions return a single value, val, but if :ttreep is t then they return (mv val ttree), where ttree is the tag-tree (see *note TTREE::) returned by the indicated operation, with an input tag-tree of nil). During the execution of a metafunction by the theorem prover, the expressions above compute the results specified. Typically, you should imagine that there are no axioms about the mfc- function symbols: treat them as uninterpreted function symbols. There is an advanced feature, meta-extract hypotheses, that can avoid this logical weakness in some cases when proving :meta rules; see *note META-EXTRACT::. But we assume for the rest of the present documentation topic that you do not use meta-extract hypotheses. Thus, in the proof of the correctness of a metafunction, no information is available about the results of these functions: you should _use these functions for heuristic purposes only_. For example, your metafunction may use these functions to decide whether to perform a given transformation, but the transformation must be sound regardless of the value that your metafunction returns. We illustrate this below. It is sometimes possible to use the hypothesis metafunction, hyp-fn, to generate a sufficient hypothesis to justify the transformation. The generated hypothesis might have already been "proved" internally by your use of mfc-ts or mfc-rw, but the system will have to prove it "officially" by relieving it. We illustrate this below also. We conclude with a script that defines, verifies, and uses several extended metafunctions. This script is based on one provided by Robert Krug, who was instrumental in the development of this style of metafunction and whose help we gratefully acknowledge. ; Here is an example. I will define (foo i j) simply to be (+ i j). ; But I will keep its definition disabled so the theorem prover ; doesn't know that. Then I will define an extended metafunction ; that reduces (foo i (- i)) to 0 provided i has integer type and the ; expression (< 10 i) occurs as a hypothesis in the clause. ; Note that (foo i (- i)) is 0 in any case. (defun foo (i j) (+ i j)) (defevaluator eva eva-lst ((foo i j) (unary-- i) ; I won't need these two cases until the last example below, but I ; include them now. (if x y z) (integerp x))) (set-state-ok t) (defun metafn (x mfc state) (cond ((and (consp x) (equal (car x) 'foo) (equal (caddr x) (list 'unary-- (cadr x)))) ; So x is of the form (foo i (- i)). Now I want to check some other ; conditions. (cond ((and (ts-subsetp (mfc-ts (cadr x) mfc state) *ts-integer*) (member-equal `(NOT (< '10 ,(cadr x))) (mfc-clause mfc))) (quote (quote 0))) (t x))) (t x))) (defthm metafn-correct (equal (eva x a) (eva (metafn x mfc state) a)) :rule-classes ((:meta :trigger-fns (foo)))) (in-theory (disable foo)) ; The following will fail because the metafunction won't fire. ; We don't know enough about i. (thm (equal (foo i (- i)) 0)) ; Same here. (thm (implies (and (integerp i) (< 0 i)) (equal (foo i (- i)) 0))) ; But this will work. (thm (implies (and (integerp i) (< 10 i)) (equal (foo i (- i)) 0))) ; This won't, because the metafunction looks for (< 10 i) literally, ; not just something that implies it. (thm (implies (and (integerp i) (< 11 i)) (equal (foo i (- i)) 0))) ; Now I will undo the defun of metafn and repeat the exercise, but ; this time check the weaker condition that (< 10 i) is provable ; (by rewriting it) rather than explicitly present. (ubt 'metafn) (defun metafn (x mfc state) (cond ((and (consp x) (equal (car x) 'foo) (equal (caddr x) (list 'unary-- (cadr x)))) (cond ((and (ts-subsetp (mfc-ts (cadr x) mfc state) *ts-integer*) (equal (mfc-rw `(< '10 ,(cadr x)) t t mfc state) *t*)) ; The mfc-rw above rewrites (< 10 i) with objective t and iffp t. The ; objective means the theorem prover will try to establish it. The ; iffp means the theorem prover can rewrite maintaining propositional ; equivalence instead of strict equality. (quote (quote 0))) (t x))) (t x))) (defthm metafn-correct (equal (eva x a) (eva (metafn x mfc state) a)) :rule-classes ((:meta :trigger-fns (foo)))) (in-theory (disable foo)) ; Now it will prove both: (thm (implies (and (integerp i) (< 10 i)) (equal (foo i (- i)) 0))) (thm (implies (and (integerp i) (< 11 i)) (equal (foo i (- i)) 0))) ; Now I undo the defun of metafn and change the problem entirely. ; This time I will rewrite (integerp (foo i j)) to t. Note that ; this is true if i and j are integers. I can check this ; internally, but have to generate a hyp-fn to make it official. (ubt 'metafn) (defun metafn (x mfc state) (cond ((and (consp x) (equal (car x) 'integerp) (consp (cadr x)) (equal (car (cadr x)) 'foo)) ; So x is (integerp (foo i j)). Now check that i and j are ; ``probably'' integers. (cond ((and (ts-subsetp (mfc-ts (cadr (cadr x)) mfc state) *ts-integer*) (ts-subsetp (mfc-ts (caddr (cadr x)) mfc state) *ts-integer*)) *t*) (t x))) (t x))) ; To justify this transformation, I generate the appropriate hyps. (defun hyp-fn (x mfc state) (declare (ignore mfc state)) ; The hyp-fn is run only if the metafn produces an answer different ; from its input. Thus, we know at execution time that x is of the ; form (integerp (foo i j)) and we know that metafn rewrote ; (integerp i) and (integerp j) both to t. So we just produce their ; conjunction. Note that we must produce a translated term; we ; cannot use the macro AND and must quote constants! Sometimes you ; must do tests in the hyp-fn to figure out which case the metafn ; produced, but not in this example. `(if (integerp ,(cadr (cadr x))) (integerp ,(caddr (cadr x))) 'nil)) (defthm metafn-correct (implies (eva (hyp-fn x mfc state) a) (equal (eva x a) (eva (metafn x mfc state) a))) :rule-classes ((:meta :trigger-fns (integerp)))) (in-theory (disable foo)) ; This will not be proved. (thm (implies (and (rationalp x) (integerp i)) (integerp (foo i j)))) ; But this will be. (thm (implies (and (rationalp x) (integerp i) (integerp j)) (integerp (foo i j))))  File: acl2-doc-emacs.info, Node: FAILED-FORCING, Next: FAILURE, Prev: EXTENDED-METAFUNCTIONS, Up: MISCELLANEOUS FAILED-FORCING how to deal with a proof failure in a forcing round See *note FORCING-ROUND:: for a background discussion of the notion of forcing rounds. When a proof fails during a forcing round it means that the "gist" of the proof succeeded but some "technical detail" failed. The first question you must ask yourself is whether the forced goals are indeed theorems. We discuss the possibilities below. If you believe the forced goals are theorems, you should follow the usual methodology for "fixing" failed ACL2 proofs, e.g., the identification of key lemmas and their timely and proper use as rules. See *note FAILURE::, see *note GAG-MODE::, and see *note PROOF-TREE::. The rules designed for the goals of forcing rounds are often just what is needed to prove the forced hypothesis at the time it is forced. Thus, you may find that when the system has been "taught" how to prove the goals of the forcing round no forcing round is needed. This is intended as a feature to help structure the discovery of the necessary rules. If a hint must be provided to prove a goal in a forcing round, the appropriate "goal specifier" (the string used to identify the goal to which the hint is to be applied) is just the text printed on the line above the formula, e.g., "[1]Subgoal *1/3"". See *note GOAL-SPEC::. If you solve a forcing problem by giving explicit hints for the goals of forcing rounds, you might consider whether you could avoid forcing the assumption in the first place by giving those hints in the appropriate places of the main proof. This is one reason that we print out the origins of each forced assumption. An argument against this style, however, is that an assumption might be forced in hundreds of places in the main goal and proved only once in the forcing round, so that by delaying the proof you actually save time. We now turn to the possibility that some goal in the forcing round is not a theorem. There are two possibilities to consider. The first is that the original theorem has insufficient hypotheses to ensure that all the forced hypotheses are in fact always true. The "fix" in this case is to amend the original conjecture so that it has adequate hypotheses. A more difficult situation can arise and that is when the conjecture has sufficient hypotheses but they are not present in the forcing round goal. This can be caused by what we call "premature" forcing. Because ACL2 rewrites from the inside out, it is possible that it will force hypotheses while the context is insufficient to establish them. Consider trying to prove (p x (foo x)). We first rewrite the formula in an empty context, i.e., assuming nothing. Thus, we rewrite (foo x) in an empty context. If rewriting (foo x) forces anything, that forced assumption will have to be proved in an empty context. This will likely be impossible. On the other hand, suppose we did not attack (foo x) until after we had expanded p. We might find that the value of its second argument, (foo x), is relevant only in some cases and in those cases we might be able to establish the hypotheses forced by (foo x). Our premature forcing is thus seen to be a consequence of our "over eager" rewriting. Here, just for concreteness, is an example you can try. In this example, (foo x) rewrites to x but has a forced hypothesis of (rationalp x). P does a case split on that very hypothesis and uses its second argument only when x is known to be rational. Thus, the hypothesis for the (foo x) rewrite is satisfied. On the false branch of its case split, p simplies to (p1 x) which can be proved under the assumption that x is not rational. (defun p1 (x) (not (rationalp x))) (defun p (x y)(if (rationalp x) (equal x y) (p1 x))) (defun foo (x) x) (defthm foo-rewrite (implies (force (rationalp x)) (equal (foo x) x))) (in-theory (disable foo)) The attempt then to do (thm (p x (foo x))) forces the unprovable goal (rationalp x). Since all "formulas" are presented to the theorem prover as single terms with no hypotheses (e.g., since implies is a function), this problem would occur routinely were it not for the fact that the theorem prover expands certain "simple" definitions immediately without doing anything that can cause a hypothesis to be forced. See *note SIMPLE::. This does not solve the problem, since it is possible to hide the propositional structure arbitrarily deeply. For example, one could define p, above, recursively so that the test that x is rational and the subsequent first "real" use of y occurred arbitrarily deeply. Therefore, the problem remains: what do you do if an impossible goal is forced and yet you know that the original conjecture was adequately protected by hypotheses? One alternative is to disable forcing entirely. See *note DISABLE-FORCING::. Another is to disable the rule that caused the force. A third alternative is to prove that the negation of the main goal implies the forced hypothesis. For example, (defthm not-p-implies-rationalp (implies (not (p x (foo x))) (rationalp x)) :rule-classes nil) Observe that we make no rules from this formula. Instead, we merely :use it in the subgoal where we must establish (rationalp x). (thm (p x (foo x)) :hints (("Goal" :use not-p-implies-rationalp))) When we said, above, that (p x (foo x)) is first rewritten in an empty context we were misrepresenting the situation slightly. When we rewrite a literal we know what literal we are rewriting and we implicitly assume it false. This assumption is "dangerous" in that it can lead us to simplify our goal to nil and give up -- we have even seen people make the mistake of assuming the negation of what they wished to prove and then via a very complicated series of transformations convince themselves that the formula is false. Because of this "tail biting" we make very weak use of the negation of our goal. But the use we make of it is sufficient to establish the forced hypothesis above. A fourth alternative is to weaken your desired theorem so as to make explicit the required hypotheses, e.g., to prove (defthm rationalp-implies-main (implies (rationalp x) (p x (foo x))) :rule-classes nil) This of course is unsatisfying because it is not what you originally intended. But all is not lost. You can now prove your main theorem from this one, letting the implies here provide the necessary case split. (thm (p x (foo x)) :hints (("Goal" :use rationalp-implies-main)))  File: acl2-doc-emacs.info, Node: FAILURE, Next: FIND-RULES-OF-RUNE, Prev: FAILED-FORCING, Up: MISCELLANEOUS FAILURE how to deal with a proof failure When ACL2 gives up it does not mean that the submitted conjecture is invalid, even if the last formula ACL2 printed in its proof attempt is manifestly false. Since ACL2 sometimes generalizes the goal being proved, it is possible it adopted an invalid subgoal as a legitimate (but doomed) strategy for proving a valid goal. Nevertheless, conjectures submitted to ACL2 are often invalid and the proof attempt often leads the careful reader to the realization that a hypothesis has been omitted or that some special case has been forgotten. It is good practice to ask yourself, when you see a proof attempt fail, whether the conjecture submitted is actually a theorem. If you think the conjecture is a theorem, then you must figure out from ACL2's output what you know that ACL2 doesn't about the functions in the conjecture and how to impart that knowledge to ACL2 in the form of rules. The "key checkpoint" information printed at the end of the summary provides a fine place to start. See *note THE-METHOD:: for a general discussion of how to prove theorems with ACL2, and see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed tutorial. Also see *note SET-GAG-MODE:: for discussion of key checkpoints and an abbreviated output mode that focuses attention on them. You may find it most useful to start by focusing on key checkpoints that are not under a proof by induction, if any, both because these are more likely to suggest useful lemmas and because they are more likely to be theorems; for example, generalization may have occurred before a proof by induction has begun. If you need more information than is provided by the key checkpoints -- although this should rarely be necessary -- then you can look at the full proof, perhaps with the aid of certain utilities: see *note PROOF-TREE::, see *note SET-GAG-MODE::, and see *note SET-SAVED-OUTPUT::. For information on a tool to help debug failures of encapsulate and progn events, as well as certify-book failures, see *note REDO-FLAT::. Again, see *note THE-METHOD:: for a general discussion of how to prove theorems with ACL2, and see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed tutorial. See also the book "Computer-Aided Reasoning: An Approach" (Kaufmann, Manolios, Moore), as well as the discussion of how to read Nqthm proofs and how to use Nqthm rules in "A Computational Logic Handbook" by Boyer and Moore (Academic Press, 1988). If the failure occurred during a forcing round, see *note FAILED-FORCING::.  File: acl2-doc-emacs.info, Node: FIND-RULES-OF-RUNE, Next: FINDING-DOCUMENTATION, Prev: FAILURE, Up: MISCELLANEOUS FIND-RULES-OF-RUNE find the rules named rune General Form: (find-rules-of-rune rune wrld) This function finds all the rules in wrld with :rune rune. It returns a list of rules in their internal form (generally as described by the corresponding defrec). Decyphering these rules is difficult since one cannot always look at a rule object and decide what kind of record it is without exploiting many system invariants (e.g., that :rewrite runes only name rewrite-rules). At the moment this function returns nil if the rune in question is a :refinement rune, because there is no object representing :refinement rules. (:refinement rules cause changes in the 'coarsenings properties.) In addition, if the rune is an :equivalence rune, then congruence rules with that rune will be returned -- because :equivalence lemmas generate some congruence rules -- but the fact that a certain function is now known to be an equivalence relation is not represented by any rule object and so no such rule is returned. (The fact that the function is an equivalence relation is encoded entirely in its presence as a 'coarsening of equal.)  File: acl2-doc-emacs.info, Node: FINDING-DOCUMENTATION, Next: FNCALL-TERM, Prev: FIND-RULES-OF-RUNE, Up: MISCELLANEOUS FINDING-DOCUMENTATION searching the documentation The :doc and :doc! commands will display, at the terminal, documentation topics defined in ACL2 or in books that have already been included. The :docs command allows you to search the documentation at the terminal. See *note DOCS::. But how can you find documentation for books that are not included in the current ACL2 session? The xdoc manual is a combined manual for the ACL2 sources and the community books. It is built using documentation not only from the ACL2 sources (which is rearranged) but also from the community books (whether included in the current session or not), using an "xdoc processor" created by Jared Davis. The ACL2 home page (`http://www.cs.utexas.edu/users/moore/acl2/') has a link to this manual, which as of this writing may be found directly by visiting the following web page: `http://fv.centtech.com/acl2/latest/doc/'. You can build a local copy of this manual from the ACL2 Community Books, following instructions in their Makefile.  File: acl2-doc-emacs.info, Node: FNCALL-TERM, Next: FORCE, Prev: FINDING-DOCUMENTATION, Up: MISCELLANEOUS FNCALL-TERM See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: FORCE, Next: FORCED, Prev: FNCALL-TERM, Up: MISCELLANEOUS FORCE identity function used to force a hypothesis Force is the identity function: (force x) is equal to x. However, for rules of many classes (see *note RULE-CLASSES::), a hypothesis of the form (force term) is given special treatment, as described below. This treatment takes place for rule classes :rewrite, :linear, :type-prescription, :definition, :meta (actually in that case, the result of evaluating the hypothesis metafunction call), and :forward-chaining. When a hypothesis of a conditional rule (of one of the classes listed above) has the form (force hyp), it is logically equivalent to hyp but has a pragmatic effect. In particular, when the rule is considered, the needed instance of the hypothesis, hyp', may be assumed if the usual process fails to prove it or its negation. In that situation, if the rule is eventually applied, then a special case is generated, requiring the system to prove that hyp' is true in the current context. The proofs of all such "forced assumptions" are, by default, delayed until the successful completion of the main goal. See *note FORCING-ROUND:: and see *note IMMEDIATE-FORCE-MODEP::. Forcing is generally used on hypotheses that are always expected to be true, as is commonly the case for guards of functions. All the power of the theorem prover is brought to bear on a forced hypothesis and no backtracking is possible. Forced goals can be attacked immediately (see *note IMMEDIATE-FORCE-MODEP::) or in a subsequent forcing round (see *note FORCING-ROUND::). Also see *note CASE-SPLIT:: for a related utility. If the :executable-counterpart of the function force is disabled, then no hypothesis is forced. For more on enabling and disabling forcing, see *note ENABLE-FORCING:: and see *note DISABLE-FORCING::. It sometimes happens that a conditional rule is not applied because some hypothesis, hyp, could not be relieved, even though the required instance of hyp, hyp', can be shown true in the context. This happens when insufficient resources are brought to bear on hyp' at the time we try to relieve it. A sometimes desirable alternative behavior is for the system to assume hyp', apply the rule, and to generate explicitly a special case to show that hyp' is true in the context. This is called "forcing" hyp. It can be arranged by restating the rule so that the offending hypothesis, hyp, is embedded in a call of force, as in (force hyp). By using the :corollary field of the rule-classes entry, a hypothesis can be forced without changing the statement of the theorem from which the rule is derived. Technically, force is just a function of one argument that returns that argument. It is generally enabled and hence evaporates during simplification. But its presence among the hypotheses of a conditional rule causes case splitting to occur if the hypothesis cannot be conventionally relieved. Since a forced hypothesis must be provable whenever the rule is otherwise applicable, forcing should be used only on hypotheses that are expected always to be true. A particularly common situation in which some hypotheses should be forced is in "most general" type-prescription lemmas. If a single lemma describes the "expected" type of a function, for all "expected" arguments, then it is probably a good idea to force the hypotheses of the lemma. Thus, every time a term involving the function arises, the term will be given the expected type and its arguments will be required to be of the expected type. In applying this advice it might be wise to avoid forcing those hypotheses that are in fact just type predicates on the arguments, since the routine that applies type-prescription lemmas has fairly thorough knowledge of the types of all terms. Force can have the additional benefit of causing the ACL2 typing mechanism to interact with the ACL2 rewriter to establish the hypotheses of type-prescription rules. To understand this remark, think of the ACL2 type reasoning system as a rather primitive rule-based theorem prover for questions about Common Lisp types, e.g., "does this expression produce a consp?" "does this expression produce some kind of ACL2 number, e.g., an integerp, a rationalp, or a complex-rationalp?" etc. It is driven by type-prescription rules. To relieve the hypotheses of such rules, the type system recursively invokes itself. This can be done for any hypothesis, whether it is "type-like" or not, since any proposition, p, can be phrased as the type-like question "does p produce an object of type nil?" However, as you might expect, the type system is not very good at establishing hypotheses that are not type-like, unless they happen to be assumed explicitly in the context in which the question is posed, e.g., "If p produces a consp then does p produce nil?" If type reasoning alone is insufficient to prove some instance of a hypothesis, then the instance will not be proved by the type system and a type-prescription rule with that hypothesis will be inapplicable in that case. But by embedding such hypotheses in force expressions you can effectively cause the type system to "punt" them to the rest of the theorem prover. Of course, as already noted, this should only be done on hypotheses that are "always true." In particular, if rewriting is required to establish some hypothesis of a type-prescription rule, then the rule will be found inapplicable because the hypothesis will not be established by type reasoning alone. The ACL2 rewriter uses the type reasoning system as a subsystem. It is therefore possible that the type system will force a hypothesis that the rewriter could establish. Before a forced hypothesis is reported out of the rewriter, we try to establish it by rewriting. This makes the following surprising behavior possible: A type-prescription rule fails to apply because some true hypothesis is not being relieved. The user changes the rule so as to *force* the hypothesis. The system then applies the rule but reports no forcing. How can this happen? The type system "punted" the forced hypothesis to the rewriter, which established it. Finally, we should mention that the rewriter is never willing to force when there is an if term present in the goal being simplified. Since and terms and or terms are merely abbreviations for if terms, they also prevent forcing. Note that if terms are ultimately eliminated using the ordinary flow of the proof (but see *note SET-CASE-SPLIT-LIMITATIONS::), allowing force ultimately to function as intended. Moreover, forcing can be disabled, as described above; also see *note DISABLE-FORCING::.  File: acl2-doc-emacs.info, Node: FORCED, Next: FORCING-ROUND, Prev: FORCE, Up: MISCELLANEOUS FORCED See *note FORCE::.  File: acl2-doc-emacs.info, Node: FORCING-ROUND, Next: FUNCTIONAL-INSTANTIATION-IN-ACL2R, Prev: FORCED, Up: MISCELLANEOUS FORCING-ROUND a section of a proof dealing with forced assumptions If ACL2 "forces" some hypothesis of some rule to be true, it is obliged later to prove the hypothesis. See *note FORCE::. ACL2 delays the consideration of forced hypotheses until the main goal has been proved. It then undertakes a new round of proofs in which the main goal is essentially the conjunction of all hypotheses forced in the preceding proof. Call this round of proofs the "Forcing Round." Additional hypotheses may be forced by the proofs in the Forcing Round. The attempt to prove these hypotheses is delayed until the Forcing Round has been successfully completed. Then a new Forcing Round is undertaken to prove the recently forced hypotheses and this continues until no hypotheses are forced. Thus, there is a succession of Forcing Rounds. The Forcing Rounds are enumerated starting from 1. The Goals and Subgoals of a Forcing Round are printed with the round's number displayed in square brackets. Thus, "[1]Subgoal 1.3" means that the goal in question is Subgoal 1.3 of the 1st forcing round. To supply a hint for use in the proof of that subgoal, you should use the goal specifier "[1]Subgoal 1.3". See *note GOAL-SPEC::. When a round is successfully completed -- and for these purposes you may think of the proof of the main goal as being the 0th forcing round -- the system collects all of the assumptions forced by the just-completed round. Here, an assumption should be thought of as an implication, (implies context hyp), where context describes the context in which hyp was assumed true. Before undertaking the proofs of these assumptions, we try to "clean them up" in an effort to reduce the amount of work required. This is often possible because the forced assumptions are generated by the same rule being applied repeatedly in a given context. By delaying and collecting the forced assumptions until the completion of the "main goal" we gain two advantages. First, the user gets confirmation that the "gist" of the proof is complete and that all that remains are "technical details." Second, by delaying the proofs of the forced assumptions ACL2 can undertake the proof of each assumption only once, no matter how many times it was forced in the main goal. In order to indicate which proof steps of the previous round were responsible for which forced assumptions, we print a sentence explaining the origins of each newly forced goal. For example, [1]Subgoal 1, below, will focus on (GOOD-INPUTP (XTRANS I)), which was forced in Subgoal 14, above, by applying (:REWRITE PRED-CRUNCHER) to (PRED (XTRANS I) I), and Subgoal 28, above, by applying (:REWRITE PRED-CRUNCHER) to (PRED (XTRANS I) I). In this entry, "[1]Subgoal 1" is the name of a goal which will be proved in the next forcing round. On the next line we display the forced hypothesis, call it x, which is (good-inputp (xtrans i)) in this example. This term will be the conclusion of the new subgoal. Since the new subgoal will be printed in its entirety when its proof is undertaken, we do not here exhibit the context in which x was forced. The sentence then lists (possibly a succession of) a goal name from the just-completed round and some step in the proof of that goal that forced x. In the example above we see that Subgoals 14 and 28 of the just-completed proof forced (good-inputp (xtrans i)) by applying (:rewrite pred-cruncher) to the term (pred (xtrans i) i). If one were to inspect the theorem prover's description of the proof steps applied to Subgoals 14 and 28 one would find the word "forced" (or sometimes "forcibly") occurring in the commentary. Whenever you see that word in the output, you know you will get a subsequent forcing round to deal with the hypotheses forced. Similarly, if at the beginning of a forcing round a rune is blamed for causing a force in some subgoal, inspection of the commentary for that subgoal will reveal the word "forced" after the rule name blamed. Most forced hypotheses come from within the prover's simplifier. When the simplifier encounters a hypothesis of the form (force hyp) it first attempts to establish it by rewriting hyp to, say, hyp'. If the truth or falsity of hyp' is known, forcing is not required. Otherwise, the simplifier actually forces hyp'. That is, the x mentioned above is hyp', not hyp, when the forced subgoal was generated by the simplifier. Once the system has printed out the origins of the newly forced goals, it proceeds to the next forcing round, where those goals are individually displayed and attacked. At the beginning of a forcing round, the enabled structure defaults to the global enabled structure. For example, suppose some rune, rune, is globally enabled. Suppose in some event you disable the rune at "Goal" and successfully prove the goal but force "[1]Goal". Then during the proof of "[1]Goal", rune is enabled "again." The right way to think about this is that the rune is "still" enabled. That is, it is enabled globally and each forcing round resumes with the global enabled structure.  File: acl2-doc-emacs.info, Node: FUNCTIONAL-INSTANTIATION-IN-ACL2R, Next: GAG-MODE, Prev: FORCING-ROUND, Up: MISCELLANEOUS FUNCTIONAL-INSTANTIATION-IN-ACL2R additional requirements for :functional-instance hints in ACL2(r) This documentation topic relates to ACL2(r), the modification of ACL2 that supports the real numbers (see *note REAL::). See *note HINTS:: and see *note LEMMA-INSTANCE:: for a discussion of :use hints that employ the :functional-instance keyword. Here, we document additional requirements for such hints that applies to ACL2(r). We assume familiarity with lemma instances; see *note LEMMA-INSTANCE::. (1) When functionally instantiating a non-classical formula, it is illegal to use pseudo-lambda expressions in a lemma instance. (2) A classical function symbol must be bound either to a classical function symbol or to a lambda (or, if allowed, pseudo-lambda) expression with a classical body. Similarly, a non-classical function symbol must be bound either to a non-classical function symbol or to a lambda (or, if allowed, pseudo-lambda) expression with a non-classical body.  File: acl2-doc-emacs.info, Node: GAG-MODE, Next: GC$, Prev: FUNCTIONAL-INSTANTIATION-IN-ACL2R, Up: MISCELLANEOUS GAG-MODE verbosity of proof output Please see *note SET-GAG-MODE:: for an explanation of gag-mode, which can take any of the following values: (gag-mode) ; generally evaluates to t, nil, or :goals  File: acl2-doc-emacs.info, Node: GC$, Next: GC-VERBOSE, Prev: GAG-MODE, Up: MISCELLANEOUS GC$ invoke the garbage collector This function is provided so that the user can call the garbage collector of the host Lisp from inside the ACL2 loop. Specifically, a call of gc$ is translated into a call of a function below on the same arguments. Allegro CL: excl:gc CCL ccl::gc CLISP ext:gc CMU Common Lisp system::gc GCL si::gbc LispWorks hcl::gc-generation [default argument list: (7) for 64-bit OS, else (3)] SBCL sb-ext:gc The arguments, if any, are as documented in the underlying Common Lisp. It is up to the user to pass in the right arguments, although we may do some rudimentary checks. Also see *note GC-VERBOSE::. Evaluation of a call of this macro always returns nil.  File: acl2-doc-emacs.info, Node: GC-VERBOSE, Next: GCL, Prev: GC$, Up: MISCELLANEOUS GC-VERBOSE control printing from the garbage collector General Form: (gc-verbose arg) Garbage collection (gc) is performed by every Lisp implementation; see *note GC$::. However, different ACL2 builds might see more or fewer messages. This macro is intended to provide an interface for controlling the verbosity, which is off if the argument evaluates to nil and otherwise is on. The above functionality is only supported for the following host Common Lisp implementations: CCL, CMUCL, and GCL. Otherwise, the only effect of this macro is to print a notice that it is not supported. You are welcome to contact the ACL2 developers if you would like to help in adding such support for another host Common Lisp. Evaluation of a call of this macro always returns nil.  File: acl2-doc-emacs.info, Node: GCL, Next: GET-INTERNAL-TIME, Prev: GC-VERBOSE, Up: MISCELLANEOUS GCL tips on building and using ACL2 based on Gnu Common Lisp See the installation instructions for basic information about building ACL2 on top of GCL, including information about where to fetch GCL. Here, we provide some tips that may be useful. 1. You can place forms to evaluate at start-up into file init.lsp in the directory where you are starting ACL2 (GCL), or into file acl2-init.lsp in your home directory. For example, in order to evaluate both of the lisp forms mentioned in 2 below, you could put them both into init.lsp in the current directory or in ~/acl2-init.lsp (either way, without (lp) or :q): (setq si::*optimize-maximum-pages* nil) (si::allocate 'cons 75000 t) Note that if you want to put ACL2 patches in this file, you should precede them with (in-package "ACL2"). 2. Suppose you run out of space, for example with an error like this: Error: The storage for CONS is exhausted. Currently, 59470 pages are allocated. Use ALLOCATE to expand the space. The following suggestion from Camm Maguire will minimize the heap size, at the cost of more garbage collection time. :q ; exit the ACL2 loop (setq si::*optimize-maximum-pages* nil) (lp) ; re-enter the ACL2 loop A second thing to try, suggested by several people, is to preallocate more pages before the run, e.g.: :q ; exit the ACL2 loop (si::allocate 'cons 75000 t) (lp) ; re-enter the ACL2 loop Also see *note RESET-KILL-RING:: for a suggestion on how to free up space. 3. Windows users have seen this error: cc1.exe: unrecognized option `-fno-zero-initialized-in-bss' Camm Maguire suggests that a solution may be to evaluate the following in GCL before building ACL2. (in-package 'compiler) (let* ((x `-fno-zero-initialized-in-bss') (i (search x *cc*))) (setq *cc* (concatenate 'string (subseq *cc* 0 i) (subseq *cc* (+ i (length x)))))) 4. It is possible to profile using ACL2 built on GCL. See file save-gprof.lsp in the ACL2 source directory. 5. Some versions of GCL may have garbage-collector bugs that, on rare occasions, cause ACL2 (when built on GCL) to break. If you run into this, a solution may be to execute the following: :q (si::sgc-on nil) (lp) Alternatively, put (si::sgc-on nil) in your ~/acl2-init.lsp file. A full regression test and found that this decreased performance by about 10%. But even with that, GCL is probably one of the faster Common Lisp implementations for ACL2 on Linux. Performance figures may often be found by following the "Recent changes" link on the ACL2 home page. 6. GCL operations on numbers can sometimes be sped up, perhaps by up to two orders of magnitude, by suitable declare forms (also see *note TYPE-SPEC::). The following example, developed with Warren Hunt and Serita Nelesen, illustrates the use of such declarations. ; File iplus.lisp: ; Operations on naturals together with positive infinity (represented as -1). ; After (ld "iplus.lisp"), escape to raw Lisp with :q and then eavluate ; (disassemble 'big-test). You should see lots of arithmetic operations ; in C code, but no calls of C functions CMPmake_fixnum or number_plus. (in-package "ACL2") (defmacro i-max () (expt 2 (1- 28))) (defmacro i+ (x y) `(the (signed-byte 28) (let ((x ,x) (y ,y)) (declare (type (signed-byte 28) x y)) (cond ((or (< x 0) (< y 0)) -1) (t (let ((result (the (signed-byte 29) (+ x y)))) (declare (type (signed-byte 29) result)) (cond ((>= result (i-max)) -1) (t (the (signed-byte 28) result))))))))) (defmacro imin (x y) `(the (signed-byte 28) (let ((x ,x) (y ,y)) (declare (type (signed-byte 28) x y)) (cond ((< x 0) (cond ((< y 0) -1) (t y))) ((< y 0) x) (t (the (signed-byte 28) (min x y))))))) (defun big-test (x y z) (declare (type (signed-byte 28) x y z)) (imin (i+ x y) (i+ y (imin x z))))  File: acl2-doc-emacs.info, Node: GET-INTERNAL-TIME, Next: GET-WORMHOLE-STATUS, Prev: GCL, Up: MISCELLANEOUS GET-INTERNAL-TIME runtime vs. realtime in ACL2 timings The ACL2 system provides utilities that deal with elapsed time. The most visible of these is in the time summaries printed when completing evaluation of events. For others, see *note WITH-PROVER-TIME-LIMIT::, see *note READ-RUN-TIME::, see *note TIME-TRACKER::, see *note TIME-TRACKER-TAU::, and see *note PSTACK::. By default, these utilities all use an underlying notion of run time provided by the host Common Lisp implementation: specifically, Common Lisp function get-internal-run-time. However, Common Lisp also provides function get-internal-run-time, which returns the real time (wall clock time). While the latter is specified to measure elapsed time, the former is left to the implementation, which might well only measure time spent in the Lisp process. Consider the following example, which is a bit arcane but basically sleeps for 2 seconds. (defttag t) ; to allow sys-call (make-event (prog2$ (sys-call "sleep" '("2")) (value '(value-triple nil)))) A typical time summary might be as follows, drastically under-reporting the elapsed time. Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) However, you can instruct ACL2 to switch to using elapsed time (run time), in summaries and elsewhere, by evaluating the following form. (assign get-internal-time-as-realtime t) To return to using runtime: (assign get-internal-time-as-realtime nil) While the above example is rather silly, the issue becomes significant in time summaries for proofs that call out to external tools (see *note SYS-CALL:: and see *note CLAUSE-PROCESSOR::). Note that a function get-internal-time is defined in raw Lisp but is not available inside the ACL2 loop. However, the expression (read-run-time state) provides an interface to this function that is available inside the ACL2 loop; see *note READ-RUN-TIME::. We are open to changing the default to elapsed wall-clock time (realtime), and may do so in future ACL2 releases.  File: acl2-doc-emacs.info, Node: GET-WORMHOLE-STATUS, Next: GOAL-SPEC, Prev: GET-INTERNAL-TIME, Up: MISCELLANEOUS GET-WORMHOLE-STATUS make a wormhole's status visible outside the wormhole General Form: (get-wormhole-status name state) Name should be the name of a wormhole (see *note WORMHOLE::). This function returns an error triple (see *note ERROR-TRIPLES::) of the form (mv nil s state), where s is the status of the named wormhole. The status is obtained by reading the oracle in the ACL2 state. This function makes the status of a wormhole visible outside the wormhole. But since this function takes state and modifies it, the function may only be used in contexts in which you may change state. Otherwise, the wormhole status may stay in the wormhole. See wormhole-eval and wormhole. acl2-sources/doc/EMACS/acl2-doc-emacs.info-60000664002132200015000000111153012222333541017641 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: GOAL-SPEC, Next: GUARD, Prev: GET-WORMHOLE-STATUS, Up: MISCELLANEOUS GOAL-SPEC to indicate where a hint is to be used Examples: "Goal" "goal" "Subgoal *1/3''" "subgoal *1/3''" "[2]Subgoal *1/3''" When hints are given to the theorem prover, a goal-spec is provided to specify the goal to which the hints are to be applied. The hints provided are carried along innocuously until the named goal arises. When it arises, the hints are "activated" for that goal and its descendents. A legal goal specification may be extracted from the theorem prover's output. Certain lines clearly label formulas, as in Subgoal *1/3.2' (IMPLIES ... ...) and these lines all give rise to goal specifications. In general, these lines all start either with "Goal" or "Subgoal" or else with those words preceded by a number in square brackets, as in [1]Subgoal *1/3.2' (IMPLIES ... ...). A goal specification may be obtained by deleting any surrounding whitespace from such a line and embedding the text in string quotation marks. Thus "[1]Subgoal *1/3.2'" is the goal specifier for the goal above. As noted, a hint is applied to a goal when the hint's goal specification matches the name ACL2 assigns to the goal. The matching algorithm is case-insensitive. Thus, alternative goal specifications for the goal above are "[1]subgoal *1/3.2'" and "[1]SUBGOAL *1/3.2'". The matching algorithm does not tolerate non-case discrepancies. Thus, "[1]Subgoal*1/3.2'" and " [1]Subgoal *1/3.2'" are unacceptable. Sometimes a formula is given two names, e.g., Subgoal *1/14.2' (IMPLIES ... ...) Name the formula above *1.1. It is the first name (the one that starts with "Goal" or "Subgoal") and not the second which constitutes a legal goal-spec. Roughly speaking, when the system prints the line containing the goal specification, it activates any hints that are attached to that goal-spec. Consider the example above. Suppose Subgoal *1/14.2' could be proved by using a certain lemma instance. Then the appropriate entry in the hints would be: ("Subgoal *1/14.2'" :use ...) This might surprise you because the system appears to do nothing to *1/14.2' besides push it for a subsequent induction. But actually between the time the system printed the goal-spec line and the time it decides to push the goal, you can think of the system as trying everything it has. So a use hint activated when Subgoal *1/14.2' arises is just what you want. But what if you want to give an :induct hint? By the time induction is tried, the formula has been given the name *1.1. Well, this is one you just have to remember: ("Subgoal *1/14.2'" :induct ...). When the above hint is activated the :induct directive short-circuits the rest of the processing and sends immediately the formula into the pool of goals to prove by induction. The induct hint is attached to the formula in the pool and when the time comes to turn our attention to that goal, the induct advice is followed. We conclude by emphasizing a point made above, that a hint is applied to a goal when the hint's goal specification matches the name ACL2 assigns to the goal. If there is no such match, then the hint is ignored. Consider the following example. (thm (equal (append (append x y) z) (append x y z)) :hints (("Subgoal *1/" :in-theory nil))) Normally, :in-theory hints are inherited by subgoals (see *note HINTS-AND-THE-WATERFALL::), so you might expect that the empty theory is used in Subgoal *1/2 and Subgoal *1/1. But in fact, since there is no subgoal printed that is labeled Subgoal *1/, the above :in-theory hint is ignored. The above example is in contrast to the following, where the hint makes the proof fail, because there really is a Subgoal *1/ in the proof this time. (thm (implies (and (not (endp x)) (not (endp (cdr x)))) (equal (append (append x y) z) (append x y z))) :hints (("Subgoal *1/" :in-theory nil)))  File: acl2-doc-emacs.info, Node: GUARD, Next: GUARD-HINTS, Prev: GOAL-SPEC, Up: MISCELLANEOUS GUARD restricting the domain of a function The ACL2 system provides a mechanism for restricting a function to a particular domain. Such restrictions are called "guards." A definition's guard has no effect on the logical axiom added when that definition is accepted by ACL2, and novices are often best served by avoiding guards. However, guards can be useful as a specification device or for increasing execution efficiency. To make such a restriction, use the :guard keyword (see *note XARGS::) or, especially if you want the host Lisp compiler to use this information, use type declarations (see *note DECLARE::; also see *note XARGS:: for a discussion of the split-types keyword). The general issue of guards and guard verification is discussed in the topics listed below. * Menu: * EXTRA-INFO:: generate markers to indicate sources of guard proof obligations * GUARD-DEBUG:: generate markers to indicate sources of guard proof obligations * GUARD-EVALUATION-EXAMPLES-LOG:: log showing combinations of defun-modes and guard-checking * GUARD-EVALUATION-EXAMPLES-SCRIPT:: a script to show combinations of defun-modes and guard-checking * GUARD-EVALUATION-TABLE:: a table that shows combinations of defun-modes and guard-checking * GUARD-INTRODUCTION:: introduction to guards in ACL2 * GUARD-MISCELLANY:: miscellaneous remarks about guards * GUARD-QUICK-REFERENCE:: brief summary of guard checking and guard verification * GUARDS-AND-EVALUATION:: the relationship between guards and evaluation * GUARDS-FOR-SPECIFICATION:: guards as a specification device Related topics other than immediate subtopics: * GUARD-EXAMPLE:: a brief transcript illustrating guards in ACL2 * SET-GUARD-CHECKING:: control checking guards during execution of top-level forms * SET-VERIFY-GUARDS-EAGERNESS:: the eagerness with which guard verification is tried. * VERIFY-GUARDS:: verify the guards of a function To begin further discussion of guards, see *note GUARD-INTRODUCTION::. That section directs the reader to further sections for more details. To see just a summary of some commands related to guards, see *note GUARD-QUICK-REFERENCE::. For a discussion of the use of proof to verify the absence of guard violations, see *note VERIFY-GUARDS::.  File: acl2-doc-emacs.info, Node: EXTRA-INFO, Next: GUARD-DEBUG, Prev: GUARD, Up: GUARD EXTRA-INFO generate markers to indicate sources of guard proof obligations See *note GUARD-DEBUG:: for a discussion of this function, which is useful for debugging failures during guard verification.  File: acl2-doc-emacs.info, Node: GUARD-DEBUG, Next: GUARD-EVALUATION-EXAMPLES-LOG, Prev: EXTRA-INFO, Up: GUARD GUARD-DEBUG generate markers to indicate sources of guard proof obligations ACL2 guard verification (see *note GUARD::) is often best avoided by beginning users of ACL2. When guard verification is employed, it can generate numerous goals, some of which may not be theorems if the definition being processed has bugs. It can be difficult to find such bugs. This documentation topic explains a capability provided by ACL2 to help find such bugs. We begin with the following example. Although it is contrived and a bit simplistic, it illustrates how the guard-debug utility works. (defun length-repeat (x) (length (append x x))) (verify-guards length-repeat :guard-debug t) The output produces two top-level key checkpoints, as follows. Subgoal 2 (IMPLIES (EXTRA-INFO '(:GUARD (:BODY LENGTH-REPEAT)) '(APPEND X X)) (TRUE-LISTP X)) Subgoal 1 (IMPLIES (AND (EXTRA-INFO '(:GUARD (:BODY LENGTH-REPEAT)) '(LENGTH (APPEND X X))) (NOT (TRUE-LISTP (APPEND X X)))) (STRINGP (APPEND X X))) The upper subgoal (numbered 2) says that the body of the definition of length-repeat contains a call (APPEND X X), which is the source of the goal. In this case, that makes sense: the guard for a call (append arg1 arg2) is (true-listp arg1), which in this case is (TRUE-LISTP X). The lower subgoal (numbered 1) says that the same definition contains the call (LENGTH (APPEND X X)), which generates the proof obligation that if (APPEND X X) does not satisfy true-listp, then it must satisfy stringp. That proof obligation comes directly from the guard for length. Of course, the example above is a bit obvious. But for large definitional bodies such information can be very helpful. Note that guard-debug can be specified not only in verify-guards events but also in xargs declare forms of defun events. We now describe the guard-debug utility in some detail. (Extra-info x y) always returns t by definition. However, if guard verification takes place with a non-nil setting of guard-debug (see below), then the goals generated for guard verification can include hypotheses that are calls of extra-info. Typically, such a hypothesis is of the following form: (extra-info '(:guard (:body F)) '(G ARG1 ... ARGk)) The above form indicates that the goal in which it occurs was generated to verify that the guard of the function F is satisfied by the arguments ARG1 through ARGk, where the term (G ARG1 ... ARGk) occurs in the body of the function F whose guard verification is in progress. If however the above call of G occurs in the guard of F instead of the body of F, then :body is replaced by :guard above: (extra-info '(:guard (:guard F)) '(G ARG1 ... ARGk)) If the same proof obligation (goal clause) arises from more than one occurrence of the same call, then a single goal will be generated, which has several extra-info hypotheses added to show the multiple sources of that proof obligation. All rules (see *note RUNE::) associated with extra-info are disabled by default, so that hypotheses of the form described above are not simplified to t. These hypotheses are intended to ride along for free: you can generally expect the proof to have the same structure whether or not the :guard-debug option is supplied, though on rare occasions the proofs may diverge. It remains to explain the notion of a guard-debug setting of t, that is, to explain how to obtain the additional hypotheses described above. If guards are being verified during processing of a defun event (whether or not inside a call of mutual-recursion), then one specifies :guard-debug t in an xargs declaration of a declare form; see *note XARGS::. If however the guard verification is on behalf of a verify-guards call, whether for a definition or a theorem, then one specifies the keyword argument :guard-debug t. Also see *note PRINT-GV:: for a utility for debugging guard violations, in contrast to the above guard-debug mechanism, which is for debugging failed proofs arising from guard verification.  File: acl2-doc-emacs.info, Node: GUARD-EVALUATION-EXAMPLES-LOG, Next: GUARD-EVALUATION-EXAMPLES-SCRIPT, Prev: GUARD-DEBUG, Up: GUARD GUARD-EVALUATION-EXAMPLES-LOG log showing combinations of defun-modes and guard-checking See *note GUARD-EVALUATION-EXAMPLES-SCRIPT:: for a script that shows the interaction of defun-modes with the value set by set-guard-checking. Here, we present a log resulting from running this script. See *note SET-GUARD-CHECKING:: for discussion of the interaction between defun-modes and guard-checking that is illustrated by this script. Also see *note GUARD-EVALUATION-TABLE:: for a succinct table, with associated discussion, that covers in detail the interactions illustrated here. ACL2 !>(defun fact (x) (declare (xargs :guard (integerp x) :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) Summary Form: ( DEFUN FACT ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 !>(trace$ fact) ((FACT)) ACL2 !>:set-guard-checking t Guard-checking-on already has value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the :program function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the :program function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard t :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) Summary Form: ( DEFUN FACT ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard (integerp x) :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). The non-trivial part of the measure conjecture is [[output omitted here]] Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) [[Comment added to the log: Normally you would get a message about guard-checking being inhibited on recursive calls. However, when a function is traced the guard-checking is done on recursive calls unless the guards have been verified (see :DOC verify-guards). ]] 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) [[Comment added to the log: In spite of the warning above, guards are checked here on self-recursive calls, because the function is traced. ]] 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >(verify-guards fact) Computing the guard conjecture for FACT.... The guard conjecture for FACT is trivial to prove, given the :compound- recognizer rule POSP-COMPOUND-RECOGNIZER, primitive type reasoning and the :type-prescription rule FACT. FACT is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS FACT) Rules: ((:COMPOUND-RECOGNIZER POSP-COMPOUND-RECOGNIZER) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:TYPE-PRESCRIPTION FACT)) Warnings: None Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u L 1:x(DEFUN FACT (X) ...) ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard (integerp x) :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). The non-trivial part of the measure conjecture is [[output omitted here]] Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) ACL2 Error in TOP-LEVEL: The guard for the function symbol FACT, which is (INTEGERP X), is violated by the arguments in the call (FACT T). See :DOC trace for a useful debugging utility. See :DOC set-guard- checking for information about suppressing this check with (set-guard- checking :none), as recommended for new users. ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 >(defun fact (x) (declare (xargs :guard t :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) For the admission of FACT we will use the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). The non-trivial part of the measure conjecture is [[output omitted here]] Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT ACL2 >(trace$ fact) ((FACT)) ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (ACL2_*1*_ACL2::FACT 1) 3> (ACL2_*1*_ACL2::FACT 0) <3 (ACL2_*1*_ACL2::FACT 1) <2 (ACL2_*1*_ACL2::FACT 1) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >(verify-guards fact) Computing the guard conjecture for FACT.... The guard conjecture for FACT is trivial to prove, given the :compound- recognizer rule POSP-COMPOUND-RECOGNIZER and the :type-prescription rule FACT. FACT is compliant with Common Lisp. Summary Form: ( VERIFY-GUARDS FACT) Rules: ((:COMPOUND-RECOGNIZER POSP-COMPOUND-RECOGNIZER) (:TYPE-PRESCRIPTION FACT)) Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FACT [[Note added to log: No need to trace fact again after verify-guards.]] ACL2 >:set-guard-checking t Turning guard checking on, value T. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :all Leaving guard checking on, but changing value to :ALL. ACL2 !>(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 !>(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 !>:set-guard-checking :none Turning off guard checking entirely. To allow execution in raw Lisp for functions with guards other than T, while continuing to mask guard violations, :SET-GUARD-CHECKING NIL. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(fact 2) 1> (ACL2_*1*_ACL2::FACT 2) 2> (FACT 2) 3> (FACT 1) 4> (FACT 0) <4 (FACT 1) <3 (FACT 1) <2 (FACT 2) <1 (ACL2_*1*_ACL2::FACT 2) 2 ACL2 >(fact t) 1> (ACL2_*1*_ACL2::FACT T) 2> (FACT T) <2 (FACT 1) <1 (ACL2_*1*_ACL2::FACT 1) 1 ACL2 >  File: acl2-doc-emacs.info, Node: GUARD-EVALUATION-EXAMPLES-SCRIPT, Next: GUARD-EVALUATION-TABLE, Prev: GUARD-EVALUATION-EXAMPLES-LOG, Up: GUARD GUARD-EVALUATION-EXAMPLES-SCRIPT a script to show combinations of defun-modes and guard-checking Below is a script that illustrates the combination of defun-modes -- :program mode, :logic mode without guards verified, and :logic mode with guards verified -- with values from set-guard-checking -- t (the default), :all, :none, and nil. (It does not illustrate the value :nowarn, which is the same as t except for inhibiting a warning.) The script also illustrates cases where the guard is not, or is, t. See *note GUARD-EVALUATION-EXAMPLES-LOG:: for result of running this script. Before presenting the script below, we give some instructions in case you want to run it yourself. See *note SET-GUARD-CHECKING:: for discussion of the interaction between defun-modes and guard-checking that is illustrated by this script. Also see *note GUARD-EVALUATION-TABLE:: for a succinct table, with associated discussion, that covers in detail the interactions illustrated here. The script mentions the running of "Tracing Code". The code is the following sequence of commands. (trace$ fact) :set-guard-checking t (fact 2) (fact t) :set-guard-checking :all (fact 2) (fact t) :set-guard-checking :none (fact 2) (fact t) :set-guard-checking nil (fact 2) (fact t) If you want to run the script yourself, you may find it handy to use the following Emacs keyboard macro for running the tracing code in 2-window mode, with the cursor in the window with the script and ACL2 running in the other window. (fset 'step-guard-script [?C-a ?C- ?C-e ?M-w ?C-a ?C-n ?C-x ?o ?M-> ?C-y return ?C-x ?o]) ; Put it on a key (if you have defined the indicated keymap by using ; emacs/emacs-acl2.el): (define-key ctl-t-keymap "r" 'step-guard-script) The script follows. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Program mode ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun fact (x) (declare (xargs :guard (integerp x) :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. It shows execution in raw Lisp in the t and nil ; cases of :set-guard-checking, but not in the :all or :none cases. We get a ; guard violation for argument t in the case :set-guard-checking t. :u (defun fact (x) (declare (xargs :guard t :mode :program)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. It should give the same results as above, ; except that we no longer get a guard violation in the case ; :set-guard-checking t. :u ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Logic mode, guard other than t ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun fact (x) (declare (xargs :guard (integerp x) :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. It should give guard violations for (fact t) ; with guard-checking set to t or :all. It should never run in raw Lisp, ; because we have not verified guards. In the t case, we can get a warning ; about avoiding the guard check on recursive calls, but only if we do not ; trace the function, fact. (verify-guards fact) ; Run the Tracing Code here. The results should be as described just above, ; except that now we go into raw Lisp for (fact 2) with guard-checking other ; than :none. :u :u ; The following definition is the same as above, except that guards are ; verified. (defun fact (x) (declare (xargs :guard (integerp x) :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. We should get the same traces as in the ; immediately preceding case, since guards had been verified in both cases. :u ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Logic mode, guard t ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun fact (x) (declare (xargs :guard t :verify-guards nil :mode :logic)) (if (posp x) (* x (fact (1- x))) 1)) ; Run the Tracing Code here. We should never go in to raw Lisp, because ; guards have not been verified. We will see the same traces for (fact 2) as ; with the (integerp x) guard above with :verify-guards nil specified, except ; that even without tracing, there is no warning for :set-guard-checking t ; about recursive calls. And, there are no guard violations for (fact t), of ; course, since posp (necessarily, if we are to verify guards) has a guard of ; t. (verify-guards fact) ; Run the Tracing Code here. You shouldn't see any surprises. Note however ; that if we back up to the start (using :u :u) and then define fact as just ; above but without :verify-guards nil, then the :none setting will allow us ; to go into raw Lisp: although :none generally avoids execution of raw Lisp ; counterparts, it allows this when the guard is T and guards have been ; verified.  File: acl2-doc-emacs.info, Node: GUARD-EVALUATION-TABLE, Next: GUARD-INTRODUCTION, Prev: GUARD-EVALUATION-EXAMPLES-SCRIPT, Up: GUARD GUARD-EVALUATION-TABLE a table that shows combinations of defun-modes and guard-checking See *note SET-GUARD-CHECKING:: for an introduction to the topic discussed here. Also see *note GUARD:: for a general discussion of guards, and see *note GUARD-EVALUATION-EXAMPLES-SCRIPT:: for a script that illustrates combinations presented below. Note: The default setting for guard-checking (that is, the initial value for (@ guard-checking-on)) is T. The table below illustrates the interaction of the defun-mode with the value supplied to set-guard-checking. The first row considers functions defined in :program mode; the other two consider functions defined in :logic mode. The columns correspond to four values of state global 'guard-checking-on, as supplied to set-guard-checking. (A fifth value, :nowarn, is similar to t but suppresses warnings encountered with t (as explained in those warning messages), and is not considered here.) During proofs, 'guard-checking-on is set to nil regardless of how this variable has been set in the top-level loop. Below this table, we make some comments about its entries, ordered by row and then by column. For example, when we refer to "b2" we are discussing the execution of a :logic mode function whose guards have not been verified, after having executed :set-guard-checking :all. guard-checking-on: (1)t (2):all (3):none (4)nil (a) :program a1 a2 a3 a4 (b) guards not verified b1 b2 b3 b4 (c) guards verified c1 c2 c3 c4 a1. Check the guard upon entry, then use the raw Lisp code if the guard checks (else cause an error). This is a common setting when one wants a little guard checking but also wants the efficiency of raw Lisp. But note that you can get raw Lisp errors. For example, if you make the definition (defun foo (x) (car x)) in :program mode and execute :set-guard-checking t, and then execute (foo 3), you will likely get an error from the call (car 3) made in raw Lisp. a2. For built-in (predefined) functions, see a1 instead. Otherwise: Check the guard, without exception. Thus, we never run the raw Lisp code in this case. This can be useful when testing :program mode functions, but you may want to run :comp t or at least :comp :exec in this case, so that the execution is done using compiled code. a3. For built-in (predefined) functions, see a4 instead. Otherwise: Do not check the guard. For :program mode functions, we never run the raw Lisp code in this case; so if you care about efficiency, see the comment in a2 above about :comp. This combination is useful if you are using ACL2 as a programming language and do not want to prove theorems about your functions or suffer guard violations. In this case, you can forget about any connection between ACL2 and Common Lisp. a4. Run the raw Lisp code without checking guards at all. Thus, for :program mode functions, the nil setting is often preferable to the :none setting because you get the efficiency of raw Lisp execution. However, with nil you can therefore get hard Lisp errors as in a1 above. b1. Guards are checked at the top-level, though not at self-recursive calls. We never run the raw Lisp code in this case; guards would need to be verified first. b2. Unlike the t setting, guards are checked even on self-recursive calls. But like the t setting, we do not run the raw Lisp code. Use this setting if you want guards checked on each recursive call in spite of the cost of doing so. b3, b4. Execution avoids the raw Lisp code and never checks guards. The nil and :none settings behave the same in this case (i.e., for :logic mode functions whose guards have not been verified), except that recursive calls are never inlined for :none and tracing (see *note TRACE::) will show recursive calls for :none but not for nil. c1, c2. Guards are checked. If the checks pass, evaluation takes place using the raw Lisp code. If the checks fail, we get a guard violation. Either way, we do not execute "in the logic"; we only execute using the raw Lisp code. Note that t and :all behave the same in this case, (i.e. for :logic mode functions whose guards have been verified). c3, c4. For the :none and nil settings, :logic mode functions whose guards have been verified will never cause guard violations. However, with nil and for built-in functions in :logic mode, guards are still checked: if the check succeeds, then evaluation is done using the raw Lisp code, and if not, it is done by the "logic" code, including self-recursive calls (though unlike the t case, we will not see a warning about this). But with :none for user-defined functions, no guard checking is done, and the only time the raw Lisp code will be executed is when the guard is t and guards are verified at the time the executable counterpart of the function is defined (i.e., when the function is admitted unless it is later defined again and compiled using :comp). Thus, if you use :none and you want a function (foo x) with guard (g x) to execute using raw Lisp code, you can write a "wrapper"function with a guard of t: (defun foo-wrap (x) (declare (xargs :guard t)) (if (g x) (foo x) 'do-not-case)) If you want the speed of executing raw Lisp code and you have non-trivial guards on functions that you want to call at the top-level, use nil rather than :none.  File: acl2-doc-emacs.info, Node: GUARD-INTRODUCTION, Next: GUARD-MISCELLANY, Prev: GUARD-EVALUATION-TABLE, Up: GUARD GUARD-INTRODUCTION introduction to guards in ACL2 Most users can probably profit by avoiding dealing with guards most of the time. If they seem to get in the way, they can be "turned off" using the command :set-guard-checking nil; for more about this, see *note SET-GUARD-CHECKING::. For more about guards in general, see *note GUARD::. The guard on a function symbol is a formula about the formals of the function. To see the guard on a function, use the keyword command :args. See *note ARGS::. To specify the guard on a function at defun-time, use the :guard xarg (See *note XARGS::) or type declarations (see *note DECLARE::). The latter may be preferable if you want the host Lisp compiler to use this information. Guards can be seen as having either of two roles: (a) they are a specification device allowing you to characterize the kinds of inputs a function "should" have, or (b) they are an efficiency device allowing logically defined functions to be executed directly in Common Lisp. Briefly: If the guards of a function definition are "verified" (see *note VERIFY-GUARDS::), then the evaluation of a call of that function on arguments satisfying its guard will have the following property: All subsequent function calls during that evaluation will be on arguments satisfying the guard of the called function. The consequence of this fact for (a) is that your specification function is well-formed, in the sense that the values returned by this function on appropriate arguments only depend on the restrictions of the called functions to their intended domains. The consequence of this fact for (b) is that in the ACL2 system, when a function whose guards have been verified is called on arguments that satisfy its guard, then the raw lisp function defined by this function's defun event is used to evaluate the call. Note however that even when the user-supplied defun is not used, ACL2 uses a corresponding "executable counterpart" that generally performs, we expect, nearly as well as the raw lisp function. See *note COMP:: to see how compilation can speed up both kinds of execution. Let us turn next to the issue of the relationship between guards and evaluation. See *note GUARDS-AND-EVALUATION::.  File: acl2-doc-emacs.info, Node: GUARD-MISCELLANY, Next: GUARD-QUICK-REFERENCE, Prev: GUARD-INTRODUCTION, Up: GUARD GUARD-MISCELLANY miscellaneous remarks about guards The discussion of guards concludes here with a few miscellaneous remarks. (Presumably you found this documentation by following a link; see *note GUARDS-FOR-SPECIFICATION::.) For further information related to guards other than what you find under "guard," see any of the following documentation topics: guard-example, set-verify-guards-eagerness, set-guard-checking, verify-guards, and (for a discussion of keyword :SPLIT-TYPES) xargs. Defun can be made to try to verify the guards on a function. This is controlled by the "defun-mode" of the defun; see *note DEFUN-MODE::. The defun-mode is either as specified with the :mode xarg of the defun or else defaults to the default defun-mode. See *note DEFAULT-DEFUN-MODE::. If the defun-mode of the defun is :logic and either a guard is specified explicitly or :verify-guards t is specified in the xargs, then we attempt to verify the guards of the function. Otherwise we do not. (But see *note SET-VERIFY-GUARDS-EAGERNESS:: for how to modify this behavior.) It is sometimes impossible for the system to verify the guards of a recursive function at definition time. For example, the guard conjectures might require the invention and proof of some inductively derived property of the function (as often happens when the value of a recursive call is fed to a guarded subroutine). So sometimes it is necessary to define the function using :verify-guards nil then to state and prove key theorems about the function, and only then have the system attempt guard verification. Post-defun guard verification is achieved via the event verify-guards. See *note VERIFY-GUARDS::. It should be emphasized that guard verification affects only two things: how fast ACL2 can evaluate the function and whether the function is executed correctly by raw Common Lisp, without guard violations. Since ACL2 does not use the raw Common Lisp definition of a function to evaluate its calls unless that function's guards have been verified, the latter effect is felt only if you run functions in raw Common Lisp rather than via ACL2's command loop. Guard verification does not otherwise affect the theorem prover or the semantics of a definition. If you are not planning on running your function on "big" inputs and you don't care if your function runs correctly in raw Common Lisp (e.g., you have formalized some abstract mathematical property and just happened to use ACL2 as your language), there is no need to suffer through guard verification. Often users start by not doing guard verification and address that problem later. Sometimes you are driven to it, even in mathematical projects, because you find that you want to run your functions particularly fast or in raw Common Lisp. If certify-book is used to compile a file, and the file contains functions with unverified guard conjectures, then you will be warned that the compiled file cannot be loaded into raw Common Lisp with the expectation that the functions will run correctly. This is just the same point we have been making: ACL2 and Common Lisp agree only on the restricted domains specified by our guards. When guards are violated, Common Lisp can do anything. When you call a compiled function on arguments violating its guards, the chances are only increased that Common Lisp will go berserk, because compiled functions generally check fewer things at runtime and tend to be more fragile than interpreted ones. Finally, we note that ACL2 collects up guards from declare forms in order of appearance. So for example, the declare form (declare (xargs :guard (foo x)) (type string x) will generate the guard (and (foo x) (stringp x)), while the form (declare (type string x) (xargs :guard (foo x)) will generate the guard (and (stringp x) (foo x)). The only exception to this rule is the case that :guard and :stobjs are specified in the same xargs form, in which case the :stobjs form will be treated as through it comes before the :guard form.  File: acl2-doc-emacs.info, Node: GUARD-QUICK-REFERENCE, Next: GUARDS-AND-EVALUATION, Prev: GUARD-MISCELLANY, Up: GUARD GUARD-QUICK-REFERENCE brief summary of guard checking and guard verification For a careful introduction to guards, see *note GUARD::. I. GUARD CHECKING DURING EXECUTION _Effect_ Guards on definitions are checked at execution time (except for guards on subsidiary calls of recursive or mutually recursive functions). _When does it happen_ By default, guards are checked for all forms submitted at the top level. _To disable_ :set-guard-checking nil ; skip raw Lisp if there is a guard violation :set-guard-checking :none ; skip guard checking entirely _To (re-)enable_ :set-guard-checking t See *note SET-GUARD-CHECKING:: for more options. II. GUARD VERIFICATION _Effect_ A proof is attempted of the obligations arising from the guards of subsidiary functions in a defun, defthm, or defaxiom event. In the case of a defun, the guard itself is also verified (under an implicit guard of t). _When does it happen_ Only names of defined functions, defthms, and defaxioms are subject to guard verification. Guard verification may occur when functions are defined (using defun), but it requires an explicit call of verify-guards in order to verify guards for defthms and defaxioms. Constrained functions (see *note ENCAPSULATE::) may not have their guards verified. (verify-guards foo ...) causes guard verification for the defun, defthm, or defaxiom named by foo, if it has not already been successfully done. The default defun-mode (see *note DEFAULT-DEFUN-MODE::) must be :logic, or else this event is ignored. (defun foo ...) causes guard verification of foo if and only if the following two conditions are both met. First, foo is processed in :logic mode (either by setting mode :logic globally, or by including :mode :logic in the xargs declaration). Second, at least one of the following sub-conditions is met. Also see *note XARGS::, and see *note SET-VERIFY-GUARDS-EAGERNESS:: for how to change this behavior. 1. The xargs declaration specifies a :guard. 2. There is at least one type declaration (see *note DECLARE::). 3. The xargs declaration specifies :stobjs. 4. The xargs declaration specifies :verify-guards t. (verify-termination foo ...) causes guard verification of foo if foo is a function currently defined in :program mode and the criteria for guard verification of a defun form are met, as discussed above. The default defun-mode (see *note DEFAULT-DEFUN-MODE::) must be :logic, or else this event is ignored.  File: acl2-doc-emacs.info, Node: GUARDS-AND-EVALUATION, Next: GUARDS-FOR-SPECIFICATION, Prev: GUARD-QUICK-REFERENCE, Up: GUARD GUARDS-AND-EVALUATION the relationship between guards and evaluation The guard has no effect on the logical axiom added by the definition of a function. It does, however, have consequences for how calls of that function are evaluated in ACL2. We begin by explaining those consequences, when ACL2 is in its default "mode," i.e., as originally brought up. In subsequent discussion we'll consider other ways that guards can interact with evaluation. For more about guards in general, see *note GUARD::. For in-depth discussion of the interaction between the defun-mode and guard checking, see *note SET-GUARD-CHECKING::, see *note GUARD-EVALUATION-TABLE::, see *note GUARD-EVALUATION-EXAMPLES-SCRIPT::, and see *note GUARD-EVALUATION-EXAMPLES-LOG::. Also see *note GENERALIZED-BOOLEANS:: for discussion about a subtle issue in the evaluation of certain Common Lisp functions. _Guards and evaluation I: the default_ Consider the following very simple definition. (defun foo (x) (cons 1 (cdr x))) First consider how raw Common Lisp behaves when evaluating calls of this function. To evaluate (foo x) for some expression x, first x is evaluated to some value v, and then (cons 1 (cdr x)) is evaluated with x bound to v. For example, if v is (cons 'a 3), then Common Lisp computes (cons 1 3). But if (for example) v is a number, e.g., 7, then there is no way to predict what Common Lisp might do. Some implementations would cause "sensible" errors, others might return nonsense, still others might crash the host machine. The results tend toward the catastrophic if the call of foo in question is in compiled code. Now by default, ACL2 evaluates calls of foo exactly as Common Lisp does, except that it uses guards to check the "legality" of each function call. So for example, since (cdr x) has a guard of (or (consp x) (equal x nil)), the call (foo 7) would cause a "guard violation," as illustrated below. ACL2 !>(foo 7) ACL2 Error in TOP-LEVEL: The guard for the function symbol CDR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CDR 7). ACL2 !> Thus, the relation between evaluation in ACL2 and evaluation in Common Lisp is that the two produce the very same results, provided there is no guard violation. _Guards and evaluation II:_ :set-guard-checking. The ACL2 logic is a logic of total functions. That is, every application of a function defined has a completely specified result. See the documentation for each individual primitive for the specification of what it returns when its guard is violated; for example, see *note CDR::. The presence of guards thus introduces a choice in the sense of evaluation. When you type a form for evaluation do you mean for guards to be checked or not? Put another way, do you mean for the form to be evaluated in Common Lisp (if possible) or in the ACL2 logic? Note: If Common Lisp delivers an answer, it will be the same as in the logic, but it might be erroneous to execute the form in Common Lisp. For example, the ACL2 logic definition of cdr implies that the cdr of an atom is nil; see *note CDR::. So: should (cdr 7) cause a guard violation error or return nil? The top-level ACL2 loop has a variable which controls which sense of execution is provided. By default, "guard checking" is on, by which we mean that guards are checked at runtime, in the sense already described. To allow execution to proceed in the logic when there is a guard violation, do :set-guard-checking nil; or evaluate :set-guard-checking :none to skip guard checking entirely. To turn "guard checking" back on, execute the top-level form :set-guard-checking t. The status of guard checking reflected in the prompt; guard-checking is "on" when the prompt contains an exclamation mark (also see *note DEFAULT-PRINT-PROMPT::). For example, ACL2 !> means guard checking is on and ACL2 > means guard checking is off. The exclamation mark can be thought of as "barring" certain computations. The absence of the mark suggests the absence of error messages or unbarred access to the logical axioms. Thus, for example ACL2 !>(car 'abc) will signal an error, while ACL2 >(car 'abc) will return nil. To return to our previous example: with guard checking off, (foo 7) evaluates to (cons 1 nil). Also see *note SET-GUARD-CHECKING::. _Guards and evaluation III: guard verification_ Consider the defininition of foo given above, but modified so that a reasonable guard of (consp x) is specified, as shown below. (defun foo (x) (declare (xargs :guard (consp x))) (cons 1 (cdr x))) We say "reasonable guard" above because if x is such that (consp x) holds, then the call of cdr in the evaluation of (foo x) will not cause a guard violation. Thus, it "should" be legal to evaluate (foo x), for any such x, simply by evaluating this form in raw Common Lisp. The verify-guards event has been provided for this purpose. Details may be found elsewhere; see *note VERIFY-GUARDS::. Briefly, for any defined function fn, the event (verify-guards fn) attempts to check the condition discussed above, that whenever fn is called on arguments that satisfy its guard, the evaluation of this call will proceed without any guard violation. (Moreover, the guard itself should be evaluable without any guard violation.) If this check is successful, then future calls of this sort will be evaluated in raw Common Lisp. Returning to our example above, the (verify-guards foo) will succeed because the guard (consp x) of foo implies the guard generated from the call (cdr x) in the body of the definition, namely, (or (consp x) (equal x nil)) (see *note CDR::). Then the evaluation of (foo (cons 'a 3)) will take place in raw Common Lisp, because (cons 'a 3) satisfies the guard of foo. This ability to dive into raw Common Lisp hinges on the proof that the guards you attach to your own functions are sufficient to ensure that the guards encountered in the body are satisfied. This is called "guard verification." Once a function has had its guards verified, then ACL2 can evaluate the function somewhat faster (but see "Guards and evaluation V: efficiency issues" below). Perhaps more importantly, ACL2 can also guarantee that the function will be evaluated correctly by any implementation of Common Lisp (provided the guard of the function is satisfied on the input). That is, if you have verified the guards of a system of functions and you have determined that they work as you wish in your host ACL2 (perhaps by proving it, perhaps by testing), then they will work identically in any Common Lisp. There is a subtlety to our treatment of evaluation of calls of functions whose guards have been verified. If the function's guard is not satisfied by such a call, then no further attempt is made to evaluate any call of that function in raw lisp during the course of evaluation of that call. This is obvious if guard checking is on, because an error is signalled the first time its guard is violated; but in fact it is also true when guard checking is off. See *note GUARD-EXAMPLE:: for an example. _Guards and evaluation IV: functions having :program mode_ Strictly speaking, functions in :program mode (see *note DEFUN-MODE::) do not have definitions in the ACL2 logic. So what does it mean to evaluate calls of such functions in ACL2? In general we treat :program functions much as we treat :logic functions whose guards have been verified, except that when no error occurs then the corresponding raw Lisp function is always called. (We say "in general" because there are exceptions, discussed in the "Aside" just below.) Note that when the guard of a function in :logic mode is violated, there is still a value that the ACL2 logic proves is equal to the given call. But the same cannot be said for a function in :program mode. Nevertheless, for the sake of convenience we go ahead and evaluate the corresponding raw Lisp function except in the situation where the guard is violated and guard-checking is on, aside from the following: *Aside*. There are exceptions to the use of raw Lisp, discussed just above, to evaluate calls of :program mode functions. The primary one is that after :set-guard-checking :none, evaluation of user-defined :program mode function calls is done in the ACL2 logic, not in raw Lisp. The more obscure exception is that during expansion of macros and make-event forms, and during evaluation of defconst forms, ACL2 enters a "safe mode" in which this escape to raw Lisp is prevented. The following example illustrates how the user can experiment directly with safe mode, though it is preferred to use :set-guard-checking :none if you are happy to skip all guard checking and evaluate forms in the logic. ACL2 !>(defun foo (x) (declare (xargs :mode :program :guard t)) (car x)) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo 3) Error: Attempt to take the car of 3 which is not listp. [condition type: SIMPLE-ERROR] Restart actions (select using :continue): 0: Return to Top Level (an "abort" restart). 1: Abort entirely from this process. [1] ACL2(2): :pop ACL2 !>(assign safe-mode t) T ACL2 !>(foo 3) ACL2 Error in TOP-LEVEL: The guard for the function symbol CAR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CAR 3). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !>(assign safe-mode nil) NIL ACL2 !>(foo 3) Error: Attempt to take the car of 3 which is not listp. [condition type: SIMPLE-ERROR] Restart actions (select using :continue): 0: Return to Top Level (an "abort" restart). 1: Abort entirely from this process. [1] ACL2(2): The other exception occurs after set-guard-checking can be called with a value of :all; see *note SET-GUARD-CHECKING::. *End of aside.* Thus, as with :logic functions: when a guard has been satisfied on a call of a function with :program mode, no subsidiary guard checking will be done. Notice that by treating functions in :program mode like functions whose guards have been verified, we are using raw lisp to compute their values when their guards are met. We do not check guards any further once raw lisp is invoked. This can lead to hard lisp errors if the guards are not appropriate, as illustrated below. ACL2 >:program ACL2 p>(defun foo (x) (declare (xargs :guard t)) (cons 1 (cdr x))) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.02 seconds (prove: 0.00, print: 0.00, proof tree: 0.00, other: 0.02) FOO ACL2 p>(foo 3) Error: 3 is not of type LIST. Fast links are on: do (use-fast-links nil) for debugging Error signalled by CDR. Broken at COND. Type :H for Help. ACL2>> See *note DEFUN-MODE-CAVEAT::. However, here is a way to get ACL2 to do run-time guard checking for user-defined :program mode functions. With this method, ACL2 will evaluate calls of user-defined :program mode functions in a manner that follows their ACL2 definitions. Simply execute the following in the ACL2 loop to put ACL2 into a "safe mode." (f-put-global 'safe-mode t state) Let us revisit the example above, using safe mode. Notice that the guard of cdr is now being checked, because the executable counterpart of foo is being called even though the guard is t. ACL2 !>(f-put-global 'safe-mode t state) ACL2 !>:program ACL2 p!>(defun foo (x) (declare (xargs :guard t)) (cons 1 (cdr x))) Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 p!>(foo 3) ACL2 Error in TOP-LEVEL: The guard for the function symbol CDR, which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the call (CDR 3). See :DOC trace for a useful debugging utility. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 p!> If we go back into "unsafe" mode, then we once again see a raw Lisp error, as we now illustrate. ACL2 p!>(f-put-global 'safe-mode nil state) ACL2 p!>(foo 3) Error: 3 is not of type LIST. Fast links are on: do (si::use-fast-links nil) for debugging Error signalled by CDR. Broken at COND. Type :H for Help. ACL2>> _Guards and evaluation V: efficiency issues_ We have seen that by verifying the guards for a :logic function, we arrange that raw lisp is used for evaluation of calls of such functions when the arguments satisfy its guard. This has several apparent advantages over the checking of guards as we go. First, the savings is magnified as your system of functions gets deeper: the guard is checked upon the top-level entry to your system and then raw Common Lisp does all the computing. Second, if the raw Common Lisp is compiled (see *note COMPILATION::), enormous speed-ups are possible. Third, if your Common Lisp or its compiler does such optimizations as tail-recursion removal, raw Common Lisp may be able to compute your functions on input much "bigger" than ACL2 can. The first of these advantages is quite important if you have complicated guards. However, the other two advantages are probably not very important, as we now explain. When a function is defined in :logic mode, its defun is executed in raw Common Lisp. (We might call this the "primary" raw lisp definition of the function.) However, a corresponding "logic definition" is also executed. The "logic definition" is a defun in raw lisp that checks guards at runtime and escapes to the primary raw lisp definition if the guard holds of the arguments and the function has already had its guards verified. Otherwise the logic definition executes the body of the function by calling the logic definitions of each subroutine. Now it is true that compilation generally speeds up execution enormously. However, the :comp command (see *note COMP::) compiles both of the raw lisp definitions associated with a :logic function. Also, we have attempted to arrange that for every tail recursion removal done on the actual defun, a corresponding tail recursion removal is done on the "logic definition." We believe that in most cases, the logic definition executes almost as fast as the primary raw lisp definition, at least if the evaluation of the guards is fast. So, the main advantage of guard verification is probably that it lets you know that the function may be executed safely in raw lisp, returning the value predicted by the ACL2 logic, whenever its arguments satisfy its guard. We envision the development of systems of applicative lisp functions that have been developed and reasoned about using ACL2 but which are intended for evaluation in raw Common Lisp (perhaps with only a small "core" of ACL2 loaded), so this advantage of guard verification is important. Nevertheless, guard verification might be important for optimal efficiency when the functions make use of type declarations. For example, at this writing, the GCL implementation of Common Lisp can often take great advantage of declare forms that assign small integer types to formal parameters. Note that while type declarations contributed to the guard by default, they can be proved from the guard instead; see *note XARGS:: for a discussion of the :SPLIT-TYPES keyword. To continue the discussion of guards, see *note GUARDS-FOR-SPECIFICATION:: to read about the use of guards as a specification device.  File: acl2-doc-emacs.info, Node: GUARDS-FOR-SPECIFICATION, Prev: GUARDS-AND-EVALUATION, Up: GUARD GUARDS-FOR-SPECIFICATION guards as a specification device A use of guard verification that has nothing to do with efficiency is as a way to gain confidence in specifications. This use has the feel of "types" in many traditional programming languages, though guards allow much greater expressiveness than most systems of types (and unfortunately, as a result they are not syntactically checkable). For more discussion of guards in general, see *note GUARD::. Suppose you have written a collection of function definitions that are intended to specify the behavior of some system. Perhaps certain functions are only intended to be called on certain sorts of inputs, so you attach guards to those functions in order to "enforce" that requirement. And then, you verify the guards for all those functions. Then what have you gained, other than somewhat increased efficiency of execution (as explained above), which quite possibly isn't your main concern? You have gained the confidence that when evaluating any call of a (specification) function whose arguments satisfy that function's guard, all subsequent function calls during the course of evaluation will have this same property, that the arguments satisfy the guard of the calling function. In logical terms, we can say that the equality of the original call with the returned value is provable from weakened versions of the definitions, where each definitional axiom is replaced by an implication whose antecedent is the requirement that the arguments satisfy the guard and whose consequent is the original axiom. For example, (defun foo (x) (declare (xargs :guard (consp x))) (cons 1 (cdr x))) originally generates the axiom (equal (foo x) (cons 1 (cdr x))) but in fact, when evaluation involves no guard violation then the following weaker axiom suffices in the justification of the evaluation. (implies (consp x) (equal (foo x) (cons 1 (cdr x)))) If you are following links to read this documentation as a hypertext style document, then please see *note GUARD-MISCELLANY::. This concludes our discussion of guards with miscellaneous remarks, and also contains pointers to related topics.  File: acl2-doc-emacs.info, Node: GUARD-HINTS, Next: HANDS-OFF, Prev: GUARD, Up: MISCELLANEOUS GUARD-HINTS xargs keyword :GUARD-HINTS See *note XARGS::.  File: acl2-doc-emacs.info, Node: HANDS-OFF, Next: HIDE, Prev: GUARD-HINTS, Up: MISCELLANEOUS HANDS-OFF hints keyword :HANDS-OFF See *note HINTS::.  File: acl2-doc-emacs.info, Node: HIDE, Next: HINTS, Prev: HANDS-OFF, Up: MISCELLANEOUS HIDE hide a term from the rewriter Hide is actually the identity function: (hide x) = x for all x. However, terms of the form (hide x) are ignored by the ACL2 rewriter, except when explicit :expand hints are given for such terms (see *note HINTS::) or when rewrite rules explicitly about hide are available. An :expand hint that removes all calls of hide is: :expand ((:free (x) (hide x))) The above hint can be particularly useful when ACL2's equality heuristics apply hide to an equality after substituting it into the rest of the goal, if that goal (or a subgoal of it) fails to be proved. Hide terms are also ignored by the induction heuristics. Sometimes the ACL2 simplifier inserts hide terms into a proof attempt out of the blue, as it were. Why and what can you do about it? Suppose you have a constrained function, say constrained-fn, and you define another function, say another-fn, in terms of it, as in: (defun another-fn (x y z) (if (big-hairy-test x y z) (constrained-fn x y z) t)) Suppose the term (another-fn 'a 'b 'c) arises in a proof. Since the arguments are all constants, ACL2 will try to reduce such a term to a constant by executing the definition of another-fn. However, after a possibly extensive computation (because of big-hairy-test) the execution fails because of the unevaluable call of constrained-fn. To avoid subsequent attempts to evaluate the term, ACL2 embeds it in a hide expression, i.e., rewrites it to (hide (another-fn 'a 'b 'c)). You might think this rarely occurs since all the arguments of another-fn must be constants. You would be right except for one special case: if another-fn takes no arguments, i.e., is a constant function, then every call of it fits this case. Thus, if you define a function of no arguments in terms of a constrained function, you will often see (another-fn) rewrite to (hide (another-fn)). We do not hide the term if the executable counterpart of the function is disabled - because we do not try to evaluate it in the first place. Thus, to prevent the insertion of a hide term into the proof attempt, you can globally disable the executable counterpart of the offending defined function, e.g., (in-theory (disable (:executable-counterpart another-fn))). It is conceivable that you cannot afford to do this: perhaps some calls of the offending function must be computed while others cannot be. One way to handle this situation is to leave the executable counterpart enabled, so that hide terms are introduced on the calls that cannot be computed, but prove explicit :rewrite rules for each of those hide terms. For example, suppose that in the proof of some theorem, thm, it is necessary to leave the executable counterpart of another-fn enabled but that the call (another-fn 1 2 3) arises in the proof and cannot be computed. Thus the proof attempt will introduce the term (hide (another-fn 1 2 3)). Suppose that you can show that (another-fn 1 2 3) is (contrained-fn 1 2 3) and that such a step is necessary to the proof. Unfortunately, proving the rewrite rule (defthm thm-helper (equal (another-fn 1 2 3) (constrained-fn 1 2 3))) would not help the proof of thm because the target term is hidden inside the hide. However, (defthm thm-helper (equal (hide (another-fn 1 2 3)) (constrained-fn 1 2 3))) would be applied in the proof of thm and is the rule you should prove. Now to prove thm-helper you need to use the two "tricks" which have already been discussed. First, to eliminate the hide term in the proof of thm-helper you should include the hint :expand (hide (another-fn 1 2 3)). Second, to prevent the hide term from being reintroduced when the system tries and fails to evaluate (another-fn 1 2 3) you should include the hint :in-theory (disable (:executable-counterpart another-fn)). Thus, thm-helper will actually be: (defthm thm-helper (equal (hide (another-fn 1 2 3)) (constrained-fn 1 2 3)) :hints (("Goal" :expand (hide (another-fn 1 2 3)) :in-theory (disable (:executable-counterpart another-fn))))) See *note EVISCERATE-HIDE-TERMS:: for how to affect the printing of hide terms.  File: acl2-doc-emacs.info, Node: HINTS, Next: HINTS-AND-THE-WATERFALL, Prev: HIDE, Up: MISCELLANEOUS HINTS advice to the theorem proving process Examples: The following :hints value is nonsensical. Nevertheless, it illustrates all of the available hint keywords except the ``custom keywords'' (see *note CUSTOM-KEYWORD-HINTS::) definable by the user. :hints (("Goal" :do-not-induct t :do-not '(generalize fertilize) :expand ((assoc x a) :lambdas (:free (y) (:with member (member y z)))) :restrict ((<-trans ((x x) (y (foo x))))) :hands-off (length binary-append) :in-theory (set-difference-theories (current-theory :here) '(assoc)) :induct (and (nth n a) (nth n b)) :use ((:instance assoc-of-append (x a) (y b) (z c)) (:functional-instance (:instance p-f (x a) (y b)) (p consp) (f assoc))) :bdd (:vars (c a0 b0 a1 b1) :prove nil :bdd-constructors (cons)) :clause-processor (:function cl-proc :hint (my-hint clause)) :instructions (:x :prove) :cases ((true-listp a) (consp a)) :by (:instance rev-rev (x (cdr z))) :nonlinearp t :backchain-limit-rw 3 :reorder (4 7 2) :case-split-limitations (20 10) :no-op t :no-thanks t :error ("Bad value ~x0." 123) :or (hint-kwd-alist-1 ... hint-kwd-alist-k) :rw-cache-state nil :backtrack (my-computed-hint clause processor clause-list))) A very common hint is the :use hint, which in general takes as its value a list of "lemma instances" (see *note LEMMA-INSTANCE::) but which allows a single lemma name as a special case: :hints (("[1]Subgoal *1/1.2'" :use lemma23)) ACL2 also provides "custom keyword" hints (see *note CUSTOM-KEYWORD-HINTS::) and even more general "computed hints" for the advanced user (see *note COMPUTED-HINTS::). Only the first hint applicable to a goal, as specified in the user-supplied list of :hints followed by the default hints (see *note DEFAULT-HINTS-TABLE::), will be applied to that goal. For an advanced exception, see *note OVERRIDE-HINTS::. For a detailed discussion of how hints fit into the ACL2 waterfall, see *note HINTS-AND-THE-WATERFALL::. For examples of the sophisticated use of hints, primarily for experts, see community book books/hints/basic-tests.lisp. Background: Hints are allowed in all events that use the theorem prover. During defun events there are two different uses of the theorem prover: one to prove termination and another to verify the guards. To pass a hint to the theorem prover during termination proofs, use the :hints keyword in the defun's xargs declaration. To pass a hint to the theorem prover during the guard verification portion of admitting a defun, use the :guard-hints keyword in the defun's xargs declaration. The verify-guards event and the defthm event also use the theorem prover. To pass hints to them, use the :hints keyword argument to the event. General Form of Common :hints: ((goal-spec :key1 val1 ... :keyn valn) ... (goal-spec :key1 val1 ... :keyn valn)) where goal-spec is as described elsewhere (see *note GOAL-SPEC::) and the keys and their respective values are shown below with their interpretations. (We also provide "computed hints" but discuss them separately; see *note COMPUTED-HINTS::.) :DO-NOT-INDUCT Value is t, :otf-flg-override, :otf, name or nil, indicating whether induction is permitted under the specified goal. If value is t or :otf-flg-override, then the attempt to apply induction to the indicated goal or any subgoal under the indicated goal will immediately cause the theorem prover to report failure, except that if :otf-flg t is specified (see *note OTF-FLG::) and value is t, then the proof will continue until the time at which the goal pushed for induction is finally considered. The latter behavior is also what occurs if value is :otf. Thus, any non-nil value requires the indicated goal to be proved entirely by simplification, destructor elimination, and the other "waterfall" processes. Induction to prove the indicated goal (or any subgoal) is not permitted. See however the :induct hint below. If value is a symbol other than t, :otf-flg-override, :otf or nil, the theorem prover will give a "bye" to any subgoal that would otherwise be attacked with induction. This will cause the theorem prover to fail eventually but will collect the necessary subgoals. If value is nil, this hint means induction is permitted. Since that is the default, there is no reason to use the value nil. Note that a :do-not-induct hint is ignored for any goal on which an :induct hint is supplied. For an advanced example of the use of value :otf with override-hints, see community book books/hints/basic-tests.lisp. :DO-NOT Value is a term having at most the single free variable world, which when evaluated (with world bound to the current ACL2 logical world) produces a list of symbols that is a subset of the list (preprocess ;propositional logic, simple rules simplify ;as above plus rewriting, linear arithmetic eliminate-destructors fertilize ;use of equalities generalize eliminate-irrelevance). The hint indicates that the "processes" named should not be used at or below the goal in question. Thus, to prevent generalization and fertilization, say, include the hint :do-not '(generalize fertilize) If value is a single symbol, as in :do-not generalize, it is taken to be '(value). :EXPAND Value is a true list of terms, each of which is of one of the forms (let ((v1 t1)...) b) or (fn t1 ... tn), where fn is a defined function symbol with formals v1, ..., vn, and body b. Such a term is said to be "expandable:" it can be replaced by the result of substituting the ti's for the vi's in b. The terms listed in the :expand hint are expanded when they are encountered by the simplifier while working on the specified goal or any of its subgoals. We permit value to be a single such term instead of a singleton list. *Remarks*: (1) Allowed are "terms" of the form (:free (var1 var2 ... varn) pattern) where the indicated variables are distinct and pattern is a term. Such "terms" indicate that we consider the indicated variables to be instantiatable, in the following sense: whenever the simplifier encounters a term that can be obtained from pattern by instantiating the variables (var1 var2 ... varn), then it expands that term. (2) Also allowed are "terms" of the form (:with name term), where name is a function symbol, a macro name that denotes a function symbol (see *note MACRO-ALIASES-TABLE::), or a rune. The corresponding rule of class :rewrite, which is often a definition rule but need not be, is then used in place of the current body for the function symbol of term; see *note SHOW-BODIES:: and see *note SET-BODY::. If the rule is of the form (implies hyp (equiv lhs rhs)), then after matching lhs to the current term in a context that is maintaining equivalence relation equiv, ACL2 will replace the current term with (if hyp rhs (hide term)), or just rhs if the rule is just (equal lhs rhs). (3) A combination of both :free and :with, as described above, is legal. (4) The term :LAMBDAS is treated specially. It denotes the list of all lambda applications (i.e., let expressions) encountered during the proof. Conceptually, this use of :LAMBDAS tells ACL2 to treat lambda applications as a notation for substitutions, rather than as function calls whose opening is subject to the ACL2 rewriter's heuristics (specifically, not allowing lambda applications to open when they introduce "too many" if terms). :HANDS-OFF Value is a true list of function symbols or lambda expressions, indicating that under the specified goal applications of these functions are not to be rewritten. Note however that subterms will still be rewritten; see *note HIDE:: if that is not what is intended. (The community book books/clause-processors/autohide.lisp from Jared Davis may also be helpful in that case.) Value may also be a single function symbol or lambda expression instead of a list. :IN-THEORY Value is a "theory expression," i.e., a term having at most the single free variable world which when evaluated (with world bound to the current ACL2 logical world (see *note WORLD::)) will produce a theory to use as the current theory for the goal specified. See *note THEORIES::. Note that an :IN-THEORY hint will always be evaluated relative to the current ACL2 logical world, not relative to the theory of a previous goal. Consider the following example. (defthm prop (p (f (g x))) :hints (("Goal" :in-theory (disable f)) ("Subgoal 3" :in-theory (enable g)))) Consider in particular the theory in effect at Subgoal 3. This call of the enable macro enables g relative to the current-theory of the current logical world, _not_ relative to the theory produced by the hint at Goal. Thus, the disable of f on behalf of the hint at Goal will be lost at Subgoal 3, and f will be enabled at Subgoal 3 if was enabled globally when prop was submitted. :INDUCT Value is either t or a term containing at least one recursively defined function symbol; if t, this hint indicates that the system should proceed to apply its induction heuristic to the specified goal produced (without trying simplification, etc.); if value is a term other than t, then not only should the system apply induction immediately, but it should analyze value rather than the goal to generate its induction scheme. Merging and the other induction heuristics are applied. Thus, if value contains several mergeable inductions, the "best" will be created and chosen. E.g., the :induct hint (and (nth i a) (nth j a)) suggests simultaneous induction on i, j, and a. If both an :induct and a :do-not-induct hint are supplied for a given goal then the indicated induction is applied to the goal and the :do-not-induct hint is inherited by all subgoals generated. :USE Value is a lemma-instance or a true list of lemma-instances, indicating that the propositions denoted by the instances be added as hypotheses to the specified goal. See *note LEMMA-INSTANCE::. Note that :use makes the given instances available as ordinary hypotheses of the formula to be proved. The :instance form of a lemma-instance permits you to instantiate the free variables of previously proved theorems any way you wish; but it is up to you to provide the appropriate instantiations because once the instances are added as hypotheses their variables are no longer instantiable. These new hypotheses participate fully in all subsequent rewriting, etc. If the goal in question is in fact an instance of a previously proved theorem, you may wish to use :by below. Note that theories may be helpful when employing :use hints; see *note MINIMAL-THEORY::. Note that if the value is the name of a function symbol introduced by defun, then the "normalized" body of that definition is used, for which ACL2 has propagated IF tests upward. This behavior differs from that provided by a :by hint, where the original body of the definition is used. :BDD This hint indicates that ordered binary decision diagrams (BDDs) with rewriting are to be used to prove or simplify the goal. See *note BDD:: for an introduction to the ACL2 BDD algorithm. Value is a list of even length, such that every other element, starting with the first, is one of the keywords :vars, :bdd-constructors, :prove, or :literal. Each keyword that is supplied should be followed by a value of the appropriate form, as shown below; for others, a default is used. Although :vars must always be supplied, we expect that most users will be content with the defaults used for the other values. :vars -- A list of ACL2 variables, which are to be treated as Boolean variables. The prover must be able to check, using trivial reasoning (see *note TYPE-SET::), that each of these variables is Boolean in the context of the current goal. Note that the prover will use very simple heuristics to order any variables that do not occur in :vars (so that they are "greater than" the variables that do occur in :vars), and these heuristics are often far from optimal. In addition, any variables not listed may fail to be assumed Boolean by the prover, which is likely to seriously impede the effectiveness of ACL2's BDD algorithm. Thus, users are encouraged _not_ to rely on the default order, but to supply a list of variables instead. Finally, it is allowed to use a value of t for vars. This means the same as a nil value, except that the BDD algorithm is directed to fail unless it can guarantee that all variables in the input term are known to be Boolean (in a sense discussed elsewhere; see *note BDD-ALGORITHM::). :literal -- An indication of which part of the current goal should receive BDD processing. Possible values are: :all treat entire goal as a single literal (the default) :conc process the conclusion n process the hypothesis with index n (1, 2, ...) :bdd-constructors -- When supplied, this value should be a list of function symbols in the current ACL2 world; it is (cons) by default, unless :bdd-constructors has a value in the acl2-defaults-table by default, in which case that value is the default. We expect that most users will be content with the default. See *note BDD-ALGORITHM:: for information about how this value is used. :prove -- When supplied, this value should be t or nil; it is t by default. When the goal is not proved and this value is t, the entire proof will abort. Use the value nil if you are happy to the proof to go on with the simplified term. :CLAUSE-PROCESSOR Value specifies the application of a user-defined simplifier to the current goal. See *note CLAUSE-PROCESSOR::, which provides necessary background and hint syntax. Also see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR:: for a discussion of "trusted clause-processors": goal-level simplifiers that may be external to ACL2 and do not need to be proved correct in ACL2. You can see all current :clause-processor rules by issuing the command (print-clause-processor-rules), and you can see the names of all trusted clause-processors by issuing the command (table trusted-clause-processor-table). :INSTRUCTIONS Value is a list of proof-checker instructions; see *note INSTRUCTIONS::. Unlike other hint keywords described here, this one is actually a custom keyword hint (see *note CUSTOM-KEYWORD-HINTS::) that generates a suitable :clause-processor hint. :CASES Value is a non-empty list of terms. For each term in the list, a new goal is created from the current goal by assuming that term; and also, in essence, one additional new goal is created by assuming all the terms in the list false. We say "in essence" because if the disjunction of the terms supplied is a tautology, then that final goal will be a tautology and hence will in fact never actually be created. :BY Value is a lemma-instance, nil, or a new event name. If the value is a lemma-instance (see *note LEMMA-INSTANCE::), then it indicates that the goal (when viewed as a clause) is either equal to the proposition denoted by the instance, or is subsumed by that proposition when both are viewed as clauses. To view a formula as a clause, union together the negations of the hypotheses and add the conclusion. For example, (IMPLIES (AND (h1 t1) (h2 t2)) (c t1)) may be viewed as the clause {~(h1 t1) ~(h2 t2) (c t1)}. Clause c1 is "subsumed" by clause c2 iff some instance of c2 is a subset of c1. For example, the clause above is subsumed by {~(h1 x) (c x)}, which when viewed as a formula is (implies (h1 x) (c x)). Note that if the value is the name of a function symbol introduced by defun, then the original form of the body of that definition is used. This behavior differs from that provided by a :use hint, where the so-called "normalized" body, for which ACL2 has propagated IF tests upward. If the value is nil or a new name, the prover does not even attempt to prove the goal to which this hint is attached. Instead the goal is given a "bye", i.e., it is skipped and the proof attempt continues as though the goal had been proved. If the prover terminates without error then it reports that the proof would have succeeded had the indicated goals been proved and it prints an appropriate defthm form to define each of the :by names. The "name" nil means "make up a name." Here is an example (admittedly contrived for illustration purposes). ACL2 !>(thm (equal (append (append x y) z) (append x y z)) :hints (("Subgoal *1/2'" :by nil))) Name the formula above *1. [[... output omitted here ...]] [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/2' (IMPLIES (AND (CONSP X) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But we have been asked to pretend that this goal is subsumed by the yet-to-be-proved |THM Subgoal *1/2'|. Subgoal *1/1 [[... proof goes on; further output omitted here ...]] The system does not attempt to check the uniqueness of the :by names (supplied or made up), since by the time those goals are proved the namespace will be cluttered still further. Therefore, the final list of "appropriate" defthm forms may be impossible to admit without some renaming by the user. If you must invent new names, remember to substitute the new ones for the old ones in the :by hints themselves. :RESTRICT Warning: This is a sophisticated hint, suggested by Bishop Brock, that is intended for advanced users. In particular, :restrict hints are ignored by the preprocessor, so you might find it useful to give the hint :do-not '(preprocess) when using any :restrict hints, at least if the rules in question are abbreviations (see *note SIMPLE::). Value is an association list. Its members are of the form (x subst1 subst2 ...), where: x is either (1) a rune whose car is :rewrite or :definition or (2) an event name corresponding to one or more such runes; and (subst1 subst2 ...) is a non-empty list of substitutions, i.e., of association lists pairing variables with terms. First consider the case that x is a :rewrite or :definition rune. Recall that without this hint, the rule named x is used by matching its left-hand side (call it lhs) against the term currently being considered by the rewriter, that is, by attempting to find a substitution s such that the instantiation of lhs using s is equal to that term. If however the :restrict hint contains (x subst1 subst2 ...), then this behavior will be modified by restricting s so that it must extend subst1; and if there is no such s, then s is restricted so that it must extend subst2; and so on, until the list of substitutions is exhausted. If no such s is found, then the rewrite or definition rule named x is not applied to that term. Finally, if x is an event name corresponding to one or more :rewrite or :definition runes (that is, x is the "base symbol" of such runes; see *note RUNE::), say runes r1, ... rn, then the meaning is the same except that (x subst1 subst2 ...) is replaced by (ri subst1 subst2 ...) for each i. Once this replacement is complete, the hint may not contain two members whose car is the same rune. Note that the substitutions in :restrict hints refer to the variables actually appearing in the goals, not to the variables appearing in the rule being restricted. Here is an example, supplied by Bishop Brock. Suppose that the database includes the following rewrite rule, which is probably kept disabled. (We ignore the question of how to prove this rule.) cancel-<-*$free: (implies (and (rationalp x) (rationalp y) (rationalp z)) (equal (< y z) (if (< x 0) (> (* x y) (* x z)) (if (> x 0) (< (* x y) (* x z)) (hide (< y z)))))) Then ACL2 can prove the following theorem (unless other rules get in the way), essentially by multiplying both sides by x. (thm (implies (and (rationalp x) (< 1 x)) (< (/ x) 1)) :hints (("Goal" :in-theory (enable cancel-<-*$free) :restrict ((cancel-<-*$free ((x x) (y (/ x)) (z 1))))))) The :restrict hint above says that the variables x, y, and z in the rewrite rule cancel-<-*$free above should be instantiated respectively by x, (/ x), and 1. Thus (< y z) becomes (< (/ x) 1), and this inequality is replaced by the corresponding instance of the right-hand-side of cancel-<-*$free. Since the current conjecture assumes (< 1 x), that instance of the right-hand side simplifies to (< (* x (/ x)) (* x 1)) which in turn simplifies to (< 1 x), a hypothesis in the present theorem. :NONLINEARP Value is t or nil, indicating whether non-linear-arithmetic is active. The default value is nil. See *note NON-LINEAR-ARITHMETIC::. :BACKCHAIN-LIMIT-RW Value is a natural number or nil, indicating the level of backchaining for rewrite, meta, and linear rules. This overrides, for the current goal and (as with :in-theory hints) descendent goals, the default backchain-limit (see *note SET-BACKCHAIN-LIMIT::). :REORDER Value is a list of positive integers without duplicates, corresponding to the numbering of subgoals generated for the goal-spec "G", say "G.k" down to "G.1". Those subgoals are reordered so that if value is (n1 n2 ... nk), then the goal now numbered "G.k" will be the goal originally numbered "G.n1"; the goal now numbered "G.k-1" will be the goal formerly numbered "G.n2"; and so on, down the list of ni, after which the goals not yet printed are printed in their original order. Note that reordering for subgoals of a goal to be proved by induction, such as *1, is not supported. :CASE-SPLIT-LIMITATIONS Value is the same as for set-case-split-limitations. The simplifier will behave as though the value had instead been supplied to set-case-split-limitations; see *note SET-CASE-SPLIT-LIMITATIONS::. This behavior will persist through subgoals unless overridden by another :CASE-SPLIT-LIMITATIONS hint. :NO-OP Value is any object and is irrelevant. This hint does nothing. But empty hints, such as ("Goal"), are illegal and there are occasions, especially when writing custom keyword hints (see *note CUSTOM-KEYWORD-HINTS::) and computed hints (see *note COMPUTED-HINTS::) where it is convenient to be able to generate a non-empty no-op hint. The standard idiom is ("Goal" :NO-OP T) but the T is completely ignored. Unlike other hint keywords, multiple occurrences of the keyword :NO-OP are tolerated. :NO-THANKS Value is any object. This hint does nothing, except that if value is non-nil then the usual "[Note: A hint was supplied... Thanks!]" is not printed. :ERROR Value is typically a "fmt message" to be printed by the fmt tilde-directive ~@ but may be any object. The effect of this hint is to cause an error when the hint is translated. There is no reason to include an :ERROR hint in any user-typein, since it will only cause an error when the form is evaluated. :ERROR hints are useful in the definition of functions that generate custom keyword hints (custom-keyword-hints) and computed hints (computed-hints). For example, if you wish to define a custom keyword hint :my-hint val and you wish the hint to signal an error if there is something inappropriate about val in the context of the hint, use the following code to generate the hint (list :ERROR (cons "Your specified value, ~x0, is inappropriate" (list (cons #0 val)))) which is equivalent to (list :ERROR (msg "Your specified value, ~x0, is inappropriate" val)) which, if val has the value 123, would evaluate to the hint (:ERROR ("Your specified value, ~x0, is inappropriate" (#0 . 123))). Note that any time an :ERROR keyword is produced during hint processing, including iterations of the expansions of custom keyword hints or of override-hints, an error will occur. :OR Value is a list (kwd-val-listp-1 ... kwd-val-listp-k), where each kwd-val-listp-i is a list satisfying keyword-value-listp, i.e., an alternating list of keywords and values. This hint causes an attempt to prove the specified goal using hints kwd-val-listp-i in sequence (first kwd-val-listp-1, then kwd-val-listp-2, and so on), until the first of these succeeds. If none succeeds, then the prover proceeds after heuristically choosing the "best" result, taking into account the goals pushed in each case for proof by induction. The following (contrived but illustrative example illustrates how :or hints work. ACL2 !>(thm (f x) :hints (("Goal" :expand ((nth x 3)) :or ((:in-theory (disable car-cons)) (:use cdr-cons :in-theory (enable append))) :do-not '(generalize)))) [Note: A hint was supplied for our processing of the goal above. Thanks!] The :OR hint for Goal gives rise to two disjunctive branches. Proving any one of these branches would suffice to prove Goal. We explore them in turn, describing their derivations as we go. --- Subgoal D2 ( same formula as Goal ). The first disjunctive branch (of 2) for Goal can be created by applying the hint: ("Subgoal D2" :EXPAND ((NTH X 3)) :IN-THEORY (DISABLE CAR-CONS) :DO-NOT '(GENERALIZE)). [Note: A hint was supplied for our processing of the goal above. Thanks!] Normally we would attempt to prove this formula by induction. However, we prefer in this instance to focus on the original input conjecture rather than this simplified special case. We therefore abandon our previous work on this conjecture and reassign the name *1 to the original conjecture. (See :DOC otf-flg.) [Note: Thanks again for the hint.] --- Subgoal D1 ( same formula as Goal ). The second disjunctive branch (of 2) for Goal can be created by applying the hint: ("Subgoal D1" :EXPAND ((NTH X 3)) :USE CDR-CONS :IN-THEORY (ENABLE APPEND) :DO-NOT '(GENERALIZE)). [Note: A hint was supplied for our processing of the goal above. Thanks!] ACL2 Warning [Use] in ( THM ...): It is unusual to :USE an enabled :REWRITE or :DEFINITION rule, so you may want to consider disabling (:REWRITE CDR-CONS). We augment the goal with the hypothesis provided by the :USE hint. The hypothesis can be obtained from CDR-CONS. We are left with the following subgoal. Subgoal D1' (IMPLIES (EQUAL (CDR (CONS X Y)) Y) (F X)). By the simple :rewrite rule CDR-CONS we reduce the conjecture to Subgoal D1'' (F X). ... and so on. This example illustrates how ACL2 processes :or hints in general. For each i from 1 to k, a so-called "disjunctive" subgoal is created by splicing kwd-val-listp-i into the other hint values (if any) supplied for the given goal, in order. A corresponding subgoal is created for each i, numbered in the usual manner (hence, counting down) except that the "D" is prefixed to each resulting goal. :RW-CACHE-STATE Value is an element of the list constant *legal-rw-cache-states*: :atom (the default), nil, t, or :disabled. This hint applies to the indicated goal and all its descendents, to set the so-called "rw-cache-state" to the indicated value; see *note SET-RW-CACHE-STATE::. :BACKTRACK This is an advanced hint. You can probably accomplish its effect by the use of ordinary computed hints; see *note COMPUTED-HINTS::. But if you are an expert, read on. (See *note HINTS-AND-THE-WATERFALL:: for some relevant background.) Value is a computed hint, which is an expression that evaluates either to nil -- indicating that the :backtrack hint is to have no effect -- or to a non-empty alternating list of :keyi :vali pairs, as expected for a hint. However, unlike ordinary computed hints, :backtrack hints are evaluated *after* a goal has been processed to yield zero or more subgoals, not before. Moreover, variables PROCESSOR and CLAUSE-LIST are allowed, but variable STABLE-UNDER-SIMPLIFICATIONP is not. We explain in more detail below, but first consider the following simple example. First we define a standard list reversal function: (defun rev (x) (if (consp x) (append (rev (cdr x)) (cons (car x) nil)) nil)) Now we prove: (thm (true-listp (rev x))) The successful proof includes the following output. Subgoal *1/1' (IMPLIES (AND (CONSP X) (TRUE-LISTP (REV (CDR X)))) (TRUE-LISTP (APPEND (REV (CDR X)) (LIST (CAR X))))). The destructor terms (CAR X) and (CDR X) can be eliminated by using CAR-CDR-ELIM to replace X by (CONS X1 X2), (CAR X) by X1 and (CDR X) by X2. This produces the following goal. Subgoal *1/1'' (IMPLIES (AND (CONSP (CONS X1 X2)) (TRUE-LISTP (REV X2))) (TRUE-LISTP (APPEND (REV X2) (LIST X1)))). But suppose that we attach a :backtrack hint to the goal above at which destructor elimination was applied: (thm (true-listp (rev x)) :hints (("Subgoal *1/1'" :backtrack (quote (:do-not '(eliminate-destructors)))))) Then when ACL2 applies destructor elimination as displayed above, this time the :backtrack hint applies, evaluating to (:do-not '(eliminate-destructors)). Since this list is not nil, the prover decides not to keep the new subgoal, and instead supplies this :do-not hint before attacking the goal again. In this example, ACL2 happens to use a technique later in its "waterfall" arsenal than destructor elimination, namely, generalization: Subgoal *1/1' (IMPLIES (AND (CONSP X) (TRUE-LISTP (REV (CDR X)))) (TRUE-LISTP (APPEND (REV (CDR X)) (LIST (CAR X))))). [Note: A hint was supplied for our processing of the goal above, because of a :backtrack hint that is preventing destructor elimination. Thanks!] We generalize this conjecture, replacing (REV (CDR X)) by RV. This produces Subgoal *1/1'' (IMPLIES (AND (CONSP X) (TRUE-LISTP RV)) (TRUE-LISTP (APPEND RV (LIST (CAR X))))). We now provide a careful explanation of how :backtrack hints work, but we suggest that you keep the example above in mind. If ":backtrack form" is part of the hint that has been selected for a goal, then form is evaluated when one of ACL2's clause processors successfully applies to the current goal to produce a list of subgoals. This evaluation takes place in an environment just like that for any computed hint (see *note COMPUTED-HINTS::), with the following exceptions. First, the variable STABLE-UNDER-SIMPLIFICATIONP is not allowed to occur free in form, but instead the following new variables are allowed to occur free and are bound for this evaluation as follows: PROCESSOR is bound to the processor in the list *preprocess-clause-ledge* that has applied to the goal, and CLAUSE-LIST is bound to the list of clauses (each a list of literals that is implicitly disjoined) returned by that clause processor. Second, the variables HIST and PSPV are bound to the history and pspv returned by the clause processor, *not* the ones that were passed to the clause processor. If this evaluation returns an error, then the proof aborts, as for any computed hint whose evaluation returns an error. If this evaluation returns nil, then the :backtrack hint has no effect, and the goal is replaced by the list of goals (the value of CLAUSE-LIST described above), as usual. Otherwise, the clause processor is deemed to have failed, and the goal clause is tried again starting at the top of the waterfall after selecting the hint returned by the above evaluation. That hint will normally be an alternating list of hint keywords and their values, but if it is a custom keyword hint (see *note CUSTOM-KEYWORD-HINTS::), then it will be handled in the usual manner but with the first three variables above bound to the symbol :OMITTED. Of course, if the new hint includes a value for :BACKTRACK then this process can loop; care should be taken to keep that from happening. A final note about :BACKTRACK hints: since these are a form of computed hints, override-hints (if any) are applied to their evaluation result just as with any computed hint. That is, the backtrack hint is successively modified with each override-hint, to produce a final hint that is actually used (or, ignored if that final hint is nil). See *note OVERRIDE-HINTS::.  File: acl2-doc-emacs.info, Node: HINTS-AND-THE-WATERFALL, Next: I-AM-HERE, Prev: HINTS, Up: MISCELLANEOUS HINTS-AND-THE-WATERFALL how hints fit into the ACL2 proof waterfall Below we describe the flow of an ACL2 proof attempt, with special attention to how hints are applied during a proof. For most ACL2 users, only one point is important to take away from this documentation topic: you may specify hints during a proof (see *note HINTS::; perhaps also see *note COMPUTED-HINTS:: and see *note DEFAULT-HINTS::), and they can be expected to behave intuitively. See *note THE-METHOD:: for a summary of how to interact with the ACL2 prover; see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed tutorial; and see *note HINTS:: for an introduction to ACL2 hints, including detailed documentation for specific hint types. The remainder of this topic serves as a reference in case one needs a deeper understanding of the workings of ACL2's handling of hints. Also, for examples of the sophisticated use of hints, primarily for experts, see community book books/hints/basic-tests.lisp. First, we describe the ACL2 "waterfall", which handles each goal either by replacing it with a list (possibly empty) of child goals, or else by putting the goal into a "pool" for later proof by induction. Then, we describe how hints are handled by the waterfall. *The Waterfall.* Each goal considered by the ACL2 prover passes through a series of proof processes, called the "waterfall processes", as stored in the constant *preprocess-clause-ledge*. The top process applies top-level hints, including :use hints; the next is a lightweight "preprocess" simplifier for "simple" rules (see *note SIMPLE::); the next is the main ACL2 simplifier; and finally ACL2 attempts (in this order) destructor elimination, fertilization (heuristic use of equalities), generalization, and elimination of irrelevance. Each process may "hit", creating zero or more child goals that are each then handled at the top of the waterfall; or it may "miss", in which case the next process in the above sequence is considered. If all processes miss, then a "push" process defers the goal until it is later considered for proof by induction. When all goals have been thus handled, the goal most recently pushed for proof by induction is considered, and the process repeats. We next describe the two additional ways in which control can be returned to the top of the waterfall. When the simplification process is attempted unsuccessfully for a goal, the goal is deemed to have "settled down". In this case, and if no ancestor of the goal has settled down, then the "settled-down" process is deemed to have "hit" on the goal, the effect being that the goal makes a new pass through all the waterfall processes. (Other processes can then notice that settling down has occurred and modify their heuristics accordingly.) For example, if "Goal" simplifies to "Subgoal 2" (among others), and "Subgoal 2" simplifies to "Subgoal 2.3" (among others), which in turn is not further simplified, then the "settled-down" process hits on "Subgoal 2.3" but not on any of its children, their children, and so on. When simplification has missed (and thus the goal has settled down), the next proof process is normally destructor elimination. However, if a computed hint is suitable (in a sense described below; also see *note COMPUTED-HINTS::, especially the discussion of stable-under-simplificationp), then that hint is selected as control is returned to the top of the waterfall. A subtlety is that in this case, if the most recent hit had been from settling down, then the prover "changes its mind" and considers that the goal has not yet settled down after all as it continues through the waterfall. Each time a goal is considered at the top of the waterfall, then before passing through the proof processes as described above, ACL2 searches for a relevant hint to select unless it has already been provided a hint in the "stable-under-simplificationp" case mentioned above. We turn now to a more thorough discussion of how hints are selected and applied. *The handling of hints.* In the discussion below we will ignore forcing rounds, as each forcing round is simply treated as a new proof attempt that uses the list of hints provided at the start of the proof. When the theorem prover is called by thm or events such as defthm, defun, and verify-guards, it gathers up the hints that have been supplied, often provided as a :hints argument, but for example using a :guard-hints argument for guard verification proofs. (ACL2(r) users (see *note REAL::) may also employ :std-hints.) It then appends these to the front of the list of default hints (see *note DEFAULT-HINTS::). The resulting list becomes the initial value of the list of "pending hints", one of two critical lists maintained by the theorem prover to manage hints. The other critical list is a list of "hint settings"; the two lists are maintained as follows. When a goal is first considered, a hint is selected from the list of pending hints if any is found to apply, as described below. If a hint is selected, then it takes effect and is removed from the pending hints. Except: if the selected hint is a computed hint with value t specified for :computed-hint-replacement, then it is not removed; and if that value is a list of hints, then that list is appended to the front of the list of pending hints after the selected hint is removed (also see *note COMPUTED-HINTS::). The selected hint is also used to update the hint settings, as described below. The list of hint settings associates hint keywords with values. It is passed from the current goal to its children (and hence the children's children, and so on), though modified by hints selected from pending hints, as described below. This list is maintained so that when a goal is pushed for proof by induction, the hint settings are applied at the start of the proof by induction. Note that the list of hint settings is not re-applied to descendents of a goal in the current waterfall; a hint is applied only when it is selected (and also perhaps later as just described, through the stored hint settings at the start of a proof by induction). For example, if the hint selected for "Subgoal 3" includes :in-theory (enable foo), then the hint settings are correspondingly updated when processing "Subgoal 3", and they persist at subgoals such as "Subgoal 3.2" and "Subgoal 3.2.1" (unless overriden by hints on those goals); but the theory specifying foo is not re-installed at every such subgoal. When a hint is selected, the list of hint settings is updated so that for each keyword :kwd and associated value val from the hint, :kwd is associated with val in the hint settings, discarding any previous association of :kwd with a value in the hint settings. Except, certain "top-level" hints are never saved in the hint settings: :use, :cases, :by, :bdd, :or, and :clause-processor. For example, suppose that we specify the following hints, with no default hints. (("Goal" :expand ((bar x y))) ("Subgoal 3" :in-theory (enable foo))) These hints then become the initial list of pending hints. When the proof attempt begins, the prover encounters the top-level goal ("Goal") and pulls the "Goal" hint from the pending hints, so that the list of hint settings contains a value only for keyword :expand. This hint setting will remain for all children of the top-level goal as well, and their children, and so on, and will be inherited by induction -- in other words, it will remain throughout the entire proof. Now consider what happens when the proof reaches "Subgoal 3". At this point there is only one pending hint, which is in fact attached to that subgoal. Therefore, this hint is pulled from the pending hints (leaving that list empty), and the hint settings are extended by associating the :in-theory keyword with the theory represented by (enable foo). That theory is immediately installed until the prover finishes addressing "Subgoal 3", its children, their children, and so on; and until that completion is reached, the :in-theory keyword remains associated with the (enable foo) in the hint settings, although of course there is no re-installation of the theory at any ensuing child goal. When finally "Subgoal 3" and its descendents have been completed and the prover is about to consider "Subgoal 2", the :in-theory association is removed from the hint settings and the global theory is re-installed. However, the list of pending hints remains empty. It remains to describe how a hint is selected for a goal. When a goal is first considered (hence at the top of the waterfall), the list of pending hints is scanned, in order, until one of the hints is suitable for the goal. An explicit hint (goal-name :kwd1 val1 ... :kwdn valn) is suitable if goal-name is the name of the current goal and there is at least one keyword. A computed hint is suitable if it evaluates to a non-nil value. As indicated earlier in this documentation topic, an exception occurs when a computed hint is selected after simplification fails (the "stable-under-simplificationp" case): in that case, the goal returns to the top of the waterfall with that hint as the selected hint, and no additional search for a hint to select is made at that time. The following slightly tricky example illustrates handling of hints. ACL2 !>(set-default-hints '(("Goal" :do-not '(preprocess)))) (("Goal" :DO-NOT '(PREPROCESS))) ACL2 !>(thm (equal (append (append x y) z) (append x y z)) :hints (("Goal" :in-theory (disable car-cons)))) ACL2 Warning [Hints] in ( THM ...): The goal-spec "Goal" is explicitly associated with more than one hint. All but the first of these hints may be ignored. If you intended to give all of these hints, combine them into a single hint of the form ("Goal" :kwd1 val1 :kwd2 val2 ...). See :DOC hints-and-the-waterfall. [Note: A hint was supplied for our processing of the goal above. Thanks!] [Note: A hint was supplied for our processing of the goal above. Thanks!] Name the formula above *1. The warning above is printed because "Goal" is associated with two pending hints: one given by the set-default-hints call and one supplied by the :hints keyword of the thm form. The :in-theory hint is selected because user-supplied hints are ahead of default hints in the list of pending hints; we then get the first "Note" above. The goal progresses through the waterfall without any proof process applying to the goal; in particular, it cannot be further simplified. After the simplification process, a "settled-down" process applies, as discussed above, immediately causing another trip through the waterfall. Since the :in-theory hint was earlier removed from the list of pending hints when it was applied, the default (:do-not) hint is now the only pending hint. That hint is applied, resulting in the second "Note" above. Again, more examples may be found in the community book books/hints/basic-tests.lisp. A particularly tricky but informative example in that book is the one related to nonlinearp-default-hint. Also see *note OVERRIDE-HINTS:: for an advanced feature that allows modification of the hint selected for a goal.  File: acl2-doc-emacs.info, Node: I-AM-HERE, Next: IF-INTRO, Prev: HINTS-AND-THE-WATERFALL, Up: MISCELLANEOUS I-AM-HERE a convenient marker for use with rebuild Example Input File for Rebuild: (defun fn1 (x y) ...) (defthm lemma1 ...) (defthm lemma2 ...) (i-am-here) The following lemma won't go through. I started typing the hint but realized I need to prove a lemma first. See the failed proof attempt in foo.bar. I'm going to quit for the night now and resume tomorrow from home. (defthm lemma3 ... :hints (("Goal" :use (:instance ??? ... By putting an (i-am-here) form at the "frontier" of an evolving file of commands, you can use rebuild to load the file up to the (i-am-here). I-am-here simply returns an error triple (see *note ERROR-TRIPLES::) that indicates an error, and any form that "causes an error" will do the same job. Note that the text of the file after the (i-am-here) need not be machine readable.  File: acl2-doc-emacs.info, Node: IF-INTRO, Next: IGNORED-ATTACHMENT, Prev: I-AM-HERE, Up: MISCELLANEOUS IF-INTRO See *note SPLITTER::.  File: acl2-doc-emacs.info, Node: IGNORED-ATTACHMENT, Next: IMMED-FORCED, Prev: IF-INTRO, Up: MISCELLANEOUS IGNORED-ATTACHMENT why attachments are sometimes not used Attachments provide a way to execute constrained functions. But in some cases, ACL2 will not permit such execution. We discuss this issue briefly here. For more information about attachments, see *note DEFATTACH::. We illustrate this issue with the following example, discussed below. (encapsulate () (defstub foo () t) (defn foo-impl-1 () t) (defattach foo foo-impl-1) (defn foo-impl-2 () nil) (local (defattach foo foo-impl-2)) (defmacro mac () (foo)) ; nil in the first pass, t in the second pass (defun bar () (mac)) ; nil in the first pass, t in the second pass (defthm bar-is-nil (equal (bar) nil)) ) Here, a non-executable function foo is introduced with no constraints, and is provided two contradictory implementations, foo-impl-1 and foo-impl-2. A function, bar, is defined using a macro, mac, whose expansion depends on which of foo-impl-1 or foo-impl-2 is attached to foo. If ACL2 were to allow this, then as indicated by the comments above, (bar) would be defined to be nil on the first pass of the encapsulate form, where foo is attached to foo-impl-2; but (bar) would be defined to be t on the second pass, where foo is attached to foo-impl-1 because the second defattach call is local. Thus, after execution of the encapsulate form, (bar) would be provably equal to t even though there would be a theorem, bar-is-nil -- proved during the first pass of the encapsulate -- saying that (bar) is nil! Fortunately, ACL2 does not permit this to happen. The example above produces the following output. ACL2 !>>(DEFUN BAR NIL (MAC)) ACL2 Error in ( DEFUN BAR ...): In the attempt to macroexpand the form (MAC), evaluation of the macro body caused the following error: ACL2 cannot ev the call of undefined function FOO on argument list: NIL Note that because of logical considerations, attachments (including FOO-IMPL-2) must not be called in this context. We see, then, the importance of disallowing evaluation using attachments during macroexpansion. ACL2 is careful to avoid attachments in situations, like this one, where using attachments could be unsound. We conclude with an example illustrating how make-event can be used to work around the refusal of ACL2 to use attachments during macroexpansion. The idea is that make-event expansions are stored, and this avoids the issue of local attachments. In particular, for the example below, the second defattach affects the body of f2 even though that defattach is local, because the expansion of the corresponding make-event is saved during the first pass of certify-book, when full admissibility checks are done. Then even after including the book, the definition of f2 will be based on the second (local) defattach form below. (in-package "ACL2") (defun body-1 (name formals body) (declare (ignore name)) `(if (consp ,(car formals)) ,body nil)) (defun body-2 (name formals body) (declare (ignore name)) `(if (acl2-numberp ,(car formals)) ,body t)) (defmacro defun+ (name formals body) `(make-event (if (foo) ; attachable stub '(defun ,name ,formals ,(body-1 name formals body)) '(defun ,name ,formals ,(body-2 name formals body))))) ;;; (defun+ f1 (x y) (cons x y)) ; fails because foo has no attachment (defstub foo () t) (defn foo-true () t) (defn foo-false () nil) (defattach foo foo-true) (defun+ f1 (x y) (cons x y)) (local (defattach foo foo-false)) (defun+ f2 (x y) (cons x y)) (assert-event (equal (f1 3 t) nil)) (assert-event (equal (f2 3 t) (cons 3 t)))  File: acl2-doc-emacs.info, Node: IMMED-FORCED, Next: IMMEDIATE-FORCE-MODEP, Prev: IGNORED-ATTACHMENT, Up: MISCELLANEOUS IMMED-FORCED See *note SPLITTER::.  File: acl2-doc-emacs.info, Node: IMMEDIATE-FORCE-MODEP, Next: INDUCT, Prev: IMMED-FORCED, Up: MISCELLANEOUS IMMEDIATE-FORCE-MODEP when executable counterpart is enabled, forced hypotheses are attacked immediately Also see *note DISABLE-IMMEDIATE-FORCE-MODEP:: and see *note ENABLE-IMMEDIATE-FORCE-MODEP::. This function symbol is defined simply to provide a rune which can be enabled and disabled. Enabling (:executable-counterpart immediate-force-modep) causes ACL2 to attack forced hypotheses immediately instead of delaying them to the next forcing round. Example Hints :in-theory (disable (:executable-counterpart immediate-force-modep)) ; delay forced hyps to forcing round :in-theory (enable (:executable-counterpart immediate-force-modep)) ; split on forced hyps immediately See *note FORCE:: for background information. When a forced hypothesis cannot be established a record is made of that fact and the proof continues. When the proof succeeds a "forcing round" is undertaken in which the system attempts to prove each of the forced hypotheses explicitly. However, if the rune (:executable-counterpart immediate-force-modep) is enabled at the time the hypothesis is forced, then ACL2 does not delay the attempt to prove that hypothesis but undertakes the attempt more or less immediately.  File: acl2-doc-emacs.info, Node: INDUCT, Next: KEYWORD, Prev: IMMEDIATE-FORCE-MODEP, Up: MISCELLANEOUS INDUCT hints keyword :INDUCT See *note HINTS::.  File: acl2-doc-emacs.info, Node: KEYWORD, Next: KEYWORD-COMMANDS, Prev: INDUCT, Up: MISCELLANEOUS KEYWORD See *note KEYWORDP::.  File: acl2-doc-emacs.info, Node: KEYWORD-COMMANDS, Next: LAMBDA, Prev: KEYWORD, Up: MISCELLANEOUS KEYWORD-COMMANDS how keyword commands are processed Examples: user type-in form evaluated :pc 5 (ACL2::PC '5) :pcs app rev (ACL2::PCS 'app 'rev) :length (1 2 3) (ACL2::LENGTH '(1 2 3)) :quit (ACL2::QUIT) ; Note: avoid optional argument When a keyword, :key, is read as a command, ACL2 determines whether the symbol with the same name in the "ACL2" package, acl2::key, is a function or simple macro of n arguments. If so, ACL2 reads n more objects, obj1, ..., objn, and then acts as though it had read the following form (for a given key): (ACL2::key 'obj1 ... 'objn) Thus, by using the keyword command hack you avoid typing the parentheses, the "ACL2" package name, and the quotation marks. See *note LD-KEYWORD-ALIASES:: for how to customize this behavior. Note the generality of this hack. Any function or macro in the "ACL2" package can be so invoked, not just "commands." Indeed, there is no such thing as a distinguished class of commands. Users may take advantage of the keyword command hack by defining functions and macros in the "ACL2" package. The one caveat is that when the keyword hack is used to invoke a macro, only the required arguments for that macro are read before calling that macro: none of the &optional, &rest, &body, or &key arguments are read for that call. The macro is thus called with only its required arguments. The following log illustrates this caveat. ACL2 !>:set-iprint t ACL2 Query (:SET-IPRINT): Action (T, NIL, RESET, RESET-ENABLE, SAME, Q or ?): ACL2 Observation in SET-IPRINT: Iprinting has been enabled. ACL2 !> What happened? First, the command :set-iprint was read. Since the macro set-iprint has no required arguments, the ACL2 evaluator was then called on the form (set-iprint), that is, calling the macro on no arguments. Set-iprint is defined to query the ACL2 user when its first argument is omitted. The log shows that query, which is set up to read the next form from the input stream. That form was available immediately: the form t that had been supplied by the user. So the query returned immediately and the set-iprint call was completed.  File: acl2-doc-emacs.info, Node: LAMBDA, Next: LAST-PROVER-STEPS, Prev: KEYWORD-COMMANDS, Up: MISCELLANEOUS LAMBDA See *note TERM::.  File: acl2-doc-emacs.info, Node: LAST-PROVER-STEPS, Next: LD-ERROR-ACTION, Prev: LAMBDA, Up: MISCELLANEOUS LAST-PROVER-STEPS the number of prover steps most recently taken For discussions of prover step limits, See *note SET-PROVER-STEP-LIMIT:: and see *note WITH-PROVER-STEP-LIMIT::. The value of the form (last-prover-steps state) indicates the number of prover steps taken, in the sense described below, for the most recent context in which an event summary would normally be printed. Note that the value of (last-prover-steps state) is updated for all events, and for all other forms such as calls of thm or certify-book, that would print a summary -- regardless of whether or not such output is inhibited (see *note SET-INHIBIT-OUTPUT-LST:: and see *note SET-INHIBITED-SUMMARY-TYPES::). In particular, the value is updated (typically to nil) for table events, even when no summary is printed; for example, the value is updated to nil for table events such as (logic), (program), and even calls of set-prover-step-limit. The value of (last-prover-steps state) is determined as follows, based on the most recent summary context (as described above): nil, if no prover steps were taken; else, the (positive) number of steps taken, if the number of steps did not exceed the starting limit; else, the negative of the starting limit. We conclude with a remark for advanced users who wish to invoke last-prover-steps in the development of utilities that track prover steps. Suppose that you want to write a utility that takes some action based on the number of prover steps performed by the first defun event that is generated, among others, for example the number of prover steps taken to admit f1 in the following example. (progn (defun f1 ...) (defun f2 ...)) A solution is to record the steps taken by the first defun before executing subsequent events, as follows (see *note MAKE-EVENT::). (progn (defun f1 ...) (make-event (pprogn (f-put-global 'my-step-count (last-prover-steps state) state) (value '(value-triple nil)))) (defun f2 ...))  File: acl2-doc-emacs.info, Node: LD-ERROR-ACTION, Next: LD-ERROR-TRIPLES, Prev: LAST-PROVER-STEPS, Up: MISCELLANEOUS LD-ERROR-ACTION determines ld's response to an error Ld-error-action is an ld special (see *note LD::). The accessor is (ld-error-action state) and the updater is (set-ld-error-action val state). Ld-error-action must be :continue, :return, :return!, or :error. The initial value of ld-error-action is :continue, which means that the top-level ACL2 command loop will not exit when an error is caused by user-typein. But the default value for ld-error-action on calls of ld is :return!. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-error-action is one of them. Suppose that ld-error-triples is t and a form evaluates to an error triple (mv erp val state); see *note ERROR-TRIPLES::. If the "error component", erp, is non-nil, then an error is said to have occurred. If an error occurs, ld's action depends on ld-error-action. If it is :continue, ld just continues processing the forms in standard-oi. If it is :return or :return!, ld stops and returns as though it had emptied the channel. If it is :error, ld stops and returns, signalling an error to its caller by returning an error triple with non-nil error component, and reverting the logical world to its value just before that call of ld. To see this effect of :ERROR for ld-error-action, consider the following example. (ld '((defun f (x) x) (defun bad (x)) (defun g (x) x))) When the defun of bad fails (because its body is missing), evaluation of the ld call stops; thus, the defun of g is not evaluated. The definition of f will be removed from the logical world before the call of ld returns. However, by default each user call of ld is made with a ld-error-action of :RETURN! (not :ERROR). In the common case that all nested calls of ld inside the ACL2 loop are made this way, an error will not roll back the logical world. However, it will still halt evaluation of forms for the current call of ld and any parent calls of ld (other than the call made on behalf of lp that entered the ACL2 loop in the first place), as though there were no more forms to evaluate. We have already discussed the behavior of ld when an error occurs. But there is another case when ld can stop processing more forms: when ld-error-action is not :CONTINUE, ld-error-triples is t, and evaluation of a form returns an error triple (mv nil val state), where nil is the error component and whose "value component", val is a cons pair whose car is the symbol :STOP-LD. Let val be the pair (:STOP-LD . x). Then the call of ld returns the error triple (mv nil (:STOP-LD n . x) state), where n is the value of state global variable 'ld-level at the time of termination. The following example illustrates how this works. (ld '((defun f1 (x) x) (ld '((defun f2 (x) x) (mv nil '(:STOP-LD my-error more-info) state) (defun g2 (x) x))) (defun g1 (x) x))) The outer call of ld returns the value (:STOP-LD 2 3 MY-ERROR MORE-INFO) and leaves us in a world the includes definitions for f1 and f2, but no definition for g1 or g2 since neither of their two defun forms was evaluated. The value of state global 'ld-level is incremented from 1 to 2 when the outer ld is entered and then again to 3 when the inner ld is entered. When the inner ld escounters the error triple (mv nil (:STOP-LD my-error more-info) state), it sees :STOP-LD in the car of the value component and pushes the current value of 'ld-level, 3, onto the cdr of that value, to return the value triple (mv nil (:STOP-LD my-error 3 more-info) state). The outer of ld then sees this value and returns (mv nil (:STOP-LD my-error 2 3 more-info) state), since its current value of 'ld-level is 2 after the inner ld exits. That concludes our discussion of how these special :STOP-LD values are handled; but how are they created? While they can be created directly by evaluation results as suggested in the example above, that is not the standard way. Rather, ld returns an error triple (mv nil (:STOP-LD n) state), where n is the value of variable ld-level at the time of termination, when the following conditions hold: an error occurs, ld-error-action is RETURN! (which is the default), and ld-error-triples is t (the default). The following example, which is a bit similar to the preceding one, illustrates both creation and handling of the special :STOP-LD values. (ld '((defun f1 (x) x) (ld '((defun f2 (x) x) (ld '((defun f3 (x) x) (defun bad (x)) ; ERROR -- missing the body (defun g3 (x) x))) (defun g2 (x) x))) (defun g1 (x) x))) The result is that f1, f2, and f3 are defined, but none of g1, g2, or g3 is defined. Let's see why. The innermost call of ld has a default :ld-error-action of :RETURN! (as do the other calls). So when the definition of bad fails, then the innermost ld returns (mv nil (:STOP-LD 4) state). The middle ld sees this value, and since its :ld-error-action is not :CONTINUE (because it has the default value of :RETURN!), it returns before considering the definition of g2, with value (mv nil (:STOP-LD 3 4) state). The topmost call of ld similarly sees the :STOP-LD; stops evaluation of forms, without defining g1; and returns (mv nil (:STOP-LD 2 3 4) state).  File: acl2-doc-emacs.info, Node: LD-ERROR-TRIPLES, Next: LD-EVISC-TUPLE, Prev: LD-ERROR-ACTION, Up: MISCELLANEOUS LD-ERROR-TRIPLES determines whether a form caused an error during ld Ld-error-triples is an ld special (see *note LD::). The accessor is (ld-error-triples state) and the updater is (set-ld-error-triples val state). Ld-error-triples must be either t or nil. The initial value of ld-error-triples is t. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-error-triples is one of them. If this variable has the value t then when a form evaluates to an error triple (mv erp val state) (see *note ERROR-TRIPLES::) such that erp is non-nil, then an error is deemed to have occurred. When an error occurs in evaluating a form, ld rolls back the ACL2 world to the configuration it had at the conclusion of the last error-free form. Then ld takes the action determined by ld-error-action.  File: acl2-doc-emacs.info, Node: LD-EVISC-TUPLE, Next: LD-MISSING-INPUT-OK, Prev: LD-ERROR-TRIPLES, Up: MISCELLANEOUS LD-EVISC-TUPLE determines whether ld suppresses details when printing Ld-evisc-tuple is an ld special (see *note LD::). The accessor is (ld-evisc-tuple state) and an updater is (set-ld-evisc-tuple val state), although the use of set-evisc-tuple is preferred for updating. Ld-evisc-tuple must be either nil, which is its initial value, or a legal evisc-tuple: see *note SET-EVISC-TUPLE::. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-evisc-tuple is one of them. Ld may print the forms it is evaluating and/or the results of evaluation. Depending on the value of ld-evisc-tuple ld may "eviscerate" objects before printing them. See *note SET-EVISC-TUPLE:: for a discussion of evisceration and of how other evisc-tuples affect the printing of error messages and warnings, as well as other output not from ld.  File: acl2-doc-emacs.info, Node: LD-MISSING-INPUT-OK, Next: LD-POST-EVAL-PRINT, Prev: LD-EVISC-TUPLE, Up: MISCELLANEOUS LD-MISSING-INPUT-OK determines which forms ld evaluates ld-missing-input-ok is an ld special (see *note LD::). The accessor is (ld-missing-input-ok state) and the updater is (set-ld-missing-input-ok val state). ld-missing-input-ok must be either nil, t, or :warn. The initial value of ld-missing-input-ok is nil. The general-purpose ACL2 read-eval-print loop, ld, is controlled by various flags that control its behavior, and ld-missing-input-ok is one of them. In brief, the first argument of ld can indicate a file from which to read input. If the file does not exist, it is an error by default, but ld becomes essentially a no-op if t or :warn is supplied for :ld-missing-input-ok, where :warn prints a warning. Also see *note LD::.  File: acl2-doc-emacs.info, Node: LD-POST-EVAL-PRINT, Next: LD-PRE-EVAL-FILTER, Prev: LD-MISSING-INPUT-OK, Up: MISCELLANEOUS LD-POST-EVAL-PRINT determines whether and how ld prints the result of evaluation Ld-post-eval-print is an ld special (see *note LD::). The accessor is (ld-post-eval-print state) and the updater is (set-ld-post-eval-print val state). Ld-post-eval-print must be either t, nil, or :command-conventions. The initial value of ld-post-eval-print is :command-conventions. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-post-eval-print is one of them. If this global variable is t, ld prints the result. In the case of a form that produces a multiple value, ld prints the list containing all the values (which, logically speaking, is what the form returned). If ld-post-eval-print is nil, ld does not print the values. This is most useful when ld is used to load a previously processed file. Finally, if ld-post-eval-print is :command-conventions and also ld-error-triples is t, then ld prints the result but treats "error triples" specially. An "error triple" (see *note ERROR-TRIPLES::) is a result, (mv erp val state), that consists of two ordinary values and state. Many ACL2 functions use such triples to signal errors. The convention is that if erp (the first value) is nil, then the function is returning val (the second value) as its conventional single result and possibly side-effecting state (as with some output). If erp is t, then an error has been caused, val is irrelevant and the error message has been printed in the returned state. Example ACL2 functions that follow this convention include defun and in-package. If such "error producing" functions are evaluated while ld-post-eval-print is set to t, then you would see them producing lists of length 3. This is disconcerting to users accustomed to Common Lisp (where these functions produce single results but sometimes cause errors or side-effect state). For more information about error triples, see *note PROGRAMMING-WITH-STATE::. When ld-post-eval-print is :command-conventions and a form produces an error triple (mv erp val state) as its value, ld prints nothing if erp is non-nil and otherwise ld prints just val. Because it is a misrepresentation to suggest that just one result was returned, ld prints the value of the global variable 'triple-print-prefix before printing val. 'triple-print-prefix is initially " ", which means that when non-erroneous error triples are being abbreviated to val, val appears one space off the left margin instead of on the margin. In addition, when ld-post-eval-print is :command-conventions and the value component of an error triple is the keyword :invisible then ld prints nothing. This is the way certain commands (e.g., :pc) appear to return no value. By printing nothing when an error has been signalled, ld makes it appear that the error (whose message has already appeared in state) has "thrown" the computation back to load without returning a value. By printing just val otherwise, we suppress the fact that state has possibly been changed.  File: acl2-doc-emacs.info, Node: LD-PRE-EVAL-FILTER, Next: LD-PRE-EVAL-PRINT, Prev: LD-POST-EVAL-PRINT, Up: MISCELLANEOUS LD-PRE-EVAL-FILTER determines which forms ld evaluates Ld-pre-eval-filter is an ld special (see *note LD::). The accessor is (ld-pre-eval-filter state) and the updater is (set-ld-pre-eval-filter val state). Ld-pre-eval-filter must be either :all, :query, or a new name that could be defined (e.g., by defun or defconst). The initial value of ld-pre-eval-filter is :all. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-pre-eval-filter is one of them. If the filter is :all, then every form read is evaluated. If the filter is :query, then after a form is read it is printed to standard-co and the user is asked if the form is to be evaluated or skipped. If the filter is a new name, then all forms are evaluated until that name is introduced, at which time ld terminates normally. The :all filter is, of course, the normal one. :Query is useful if you want to replay selected the commands in some file. The new name filter is used if you wish to replay all the commands in a file up through the introduction of the given one.  File: acl2-doc-emacs.info, Node: LD-PRE-EVAL-PRINT, Next: LD-PROMPT, Prev: LD-PRE-EVAL-FILTER, Up: MISCELLANEOUS LD-PRE-EVAL-PRINT determines whether ld prints the forms to be eval'd Ld-pre-eval-print is an ld special (see *note LD::). The accessor is (ld-pre-eval-print state) and the updater is (set-ld-pre-eval-print val state). Ld-pre-eval-print must be either t, nil, or :never. The initial value of ld-pre-eval-print is nil. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-pre-eval-print is one of them. If this global variable is t, then before evaluating the form just read from standard-oi, ld prints the form to standard-co. If the variable is nil, no such printing occurs. The t option is useful if you are reading from a file of commands and wish to assemble a complete script of the session in standard-co. The value :never of ld-pre-eval-print is rarely used. During the evaluation of encapsulate and of certify-book forms, subsidiary events are normally printed, even if ld-pre-eval-print is nil. Thus for example, when the user submits an encapsulate form, all subsidiary events are generally printed even in the default situation where ld-pre-eval-print is nil. But occasionally one may want to suppress such printing. In that case, ld-pre-eval-print should be set to :never. As described elsewhere (see *note SET-INHIBIT-OUTPUT-LST::), another way to suppress such printing is to execute (set-inhibit-output-lst lst) where lst evaluates to a list including 'prove and 'event.  File: acl2-doc-emacs.info, Node: LD-PROMPT, Next: LD-QUERY-CONTROL-ALIST, Prev: LD-PRE-EVAL-PRINT, Up: MISCELLANEOUS LD-PROMPT determines the prompt printed by ld Ld-prompt is an ld special (see *note LD::). The accessor is (ld-prompt state) and the updater is (set-ld-prompt val state). Ld-prompt must be either nil, t, or a function symbol that, when given an open output character channel and state, prints the desired prompt to the channel and returns two values: the number of characters printed and the state. The initial value of ld-prompt is t. The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-prompt is one of them. Ld-prompt determines whether ld prints a prompt before reading the next form from standard-oi. If ld-prompt is nil, ld prints no prompt. If ld-prompt is t, the default prompt printer is used, which displays information that includes the current package, default defun-mode, guard checking status (on or off), and ld-skip-proofsp; see *note DEFAULT-PRINT-PROMPT::. If ld-prompt is neither nil nor t, then it should be a function name, fn, such that (fn channel state) will print the desired prompt to channel in state and return (mv col state), where col is the number of characters output (on the last line output). You may define your own prompt printing function. If you supply an inappropriate prompt function, i.e., one that causes an error or does not return the correct number and type of results, the following odd prompt will be printed instead: Bad Prompt See :DOC ld-prompt> which will lead you to this message. You should either call ld appropriately next time or assign an appropriate value to ld-prompt.  File: acl2-doc-emacs.info, Node: LD-QUERY-CONTROL-ALIST, Next: LD-REDEFINITION-ACTION, Prev: LD-PROMPT, Up: MISCELLANEOUS LD-QUERY-CONTROL-ALIST how to default answers to queries Ld-query-control-alist is an ld special (see *note LD::). The accessor is (ld-query-control-alist state) and the updater is (set-ld-query-control-alist val state). Roughly speaking, ld-query-control-alist is either nil (meaning all queries should be interactive), t (meaning all should default to the first accepted response), or an alist that pairs query ids to keyword responses. The alist may end in either t or nil, indicating the default value for all ids not listed explicitly. Formally, the ld-query-control-alist must satisfy ld-query-control-alistp. The initial ld-query-control-alist is nil, which means all queries are handled interactively. When an ACL2 query is raised, a unique identifying symbol is printed in parentheses after the word "Query". This symbol, called the "query id," can be used in conjunction with ld-query-control-alist to prevent the query from being handled interactively. By "handled interactively" we mean that the query is printed to *standard-co* and a response is read from *standard-oi*. The alist can be used to obtain a "default value" for each query id. If this value is nil, then the query is handled interactively. In all other cases, the system handles the query without interaction (although text may be printed to standard-co to make it appear that an interaction has occurred; see below). If the default value is t, the system acts as though the user responded to the query by typing the first response listed among the acceptable responses. If the default value is neither nil nor t, then it must be a keyword and one of the acceptable responses. In that case, the system acts as though the user responded with the given keyword. Next, we discuss how the ld-query-control-alist assigns a default value to each query id. It assigns each id the first value paired with the id in the alist, or, if no such pair appears in the alist, it assigns the final cdr of the alist as the value. Thus, nil assigns all ids nil. T assigns all ids t. '((:filter . nil) (:sysdef . :n) . t) assigns nil to the :filter query, :n to the :sysdef query, and t to all others. It remains only to discuss how the system prints text when the default value is not nil, i.e., when the query is handled without interaction. In fact, it is allowed to pair a query id with a singleton list containing a keyword, rather than a keyword, and this indicates that no printing is to be done. Thus for the example above, :sysdef queries would be handled noninteractively, with printing done to simulate the interaction. But if we change the example so that :sysdef is paired with (:n), i.e., if ld-query-control-alist is '((:filter . nil) (:sysdef :n) . t), then no such printing would take place for sysdef queries. Instead, the default value of :n would be assigned "quietly".  File: acl2-doc-emacs.info, Node: LD-REDEFINITION-ACTION, Next: LD-SKIP-PROOFSP, Prev: LD-QUERY-CONTROL-ALIST, Up: MISCELLANEOUS LD-REDEFINITION-ACTION to allow redefinition without undoing Ld-redefinition-action is an ld special (see *note LD::). The accessor is (ld-redefinition-action state) and the updater is (set-ld-redefinition-action val state). *WARNING!* If ld-redefinition-action is non-nil then ACL2 is liable to be made unsafe or unsound either by ill-considered definitions, or because redefining a macro or inlined function called in the body of a function, g, may not cause the new definition to be called by g. The keyword command :redef will set ld-redefinition-action to a convenient setting allowing unsound redefinition. See below. When ld-redefinition-action is nil, redefinition is prohibited. In that case, an error message is printed upon any attempt to introduce a name that is already in use. There is one exception to this rule. It is permitted to redefine a function symbol in :program mode to be a function symbol in :logic mode provided the formals and body remain the same. This is the standard way a function "comes into" logical existence. Throughout the rest of this discussion we exclude from our meaning of "redefinition" the case in which a function in :program mode is identically redefined in :logic mode. At one time, ACL2 freely permitted the signature-preserving redefinition of :program mode functions but it no longer does. See *note REDEFINING-PROGRAMS::. When ld-redefinition-action is non-nil, you are allowed to redefine a name that is already in use. *The system may be rendered unsound* by such an act. It is important to understand how dangerous redefinition is. Suppose fn is a function symbol that is called from within some other function, say g. Suppose fn is redefined so that its arity changes. Then the definition of g is rendered syntactically ill-formed by the redefinition. This can be devastating since the entire ACL2 system assumes that terms in its database are well-formed. For example, if ACL2 executes g by running the corresponding function in raw Common Lisp the redefinition of fn may cause raw lisp to break in irreparable ways. As Lisp programmers we live with this all the time by following the simple rule: after changing the syntax of a function don't run any function that calls it via its old syntax. This rule also works in the context of the evaluation of ACL2 functions, but it is harder to follow in the context of ACL2 deductions, since it is hard to know whether the database contains a path leading the theorem prover from facts about one function to facts about another. Finally, of course, even if the database is still syntactically well-formed there is no assurance that all the rules stored in it are valid. For example, theorems proved about g survive the redefinition of fn but may have crucially depended on the properties of the old fn. In summary, we repeat the warning: *all bets are off if you set* ld-redefinition-action to *non*-nil. ACL2 provides some enforcement of the concern above, by disabling certify-book if any world-changing events exist in the certification world that were executed with a non-nil value of 'ld-redefinition-action. (This value is checked at the end of each top-level command, but the value does not change during evaluation of embedded event forms; see *note EMBEDDED-EVENT-FORM::.) If at any point in a session you wish to see the list of all names that have been redefined, see *note REDEFINED-NAMES::. That said, we'll give you enough rope to hang yourself. When ld-redefinition-action is non-nil, it must be a pair, (a . b). The value of a determines how the system interacts with you when a redefinition is submitted. The value of b allows you to specify how the property list of the redefined name is to be "renewed" before the redefinition. There are several dimensions to the space of possibilities controlled by part a: Do you want to be queried each time you redefine a name, so you can confirm your intention? (We sometimes make typing mistakes or simply forget we have used that name already.) Do you want to see a warning stating that the name has been redefined? Do you want ACL2 system functions given special protection from possible redefinition? Below are the choices for part a: :query -- every attempt to redefine a name will produce a query. The query will allow you to abort the redefinition or proceed. It will will also allow you to specify the part b for this redefinition. :Query is the recommended setting for users who wish to dabble in redefinition. :warn -- any user-defined function may be redefined but a post-redefinition warning is printed. The attempt to redefine a system name produces a query. If you are prototyping and testing a big system in ACL2 this is probably the desired setting for part a. :doit -- any user-defined function may be redefined silently (without query or warning) but when an attempt is made to redefine a system function, a query is made. This setting is recommended when you start making massive changes to your prototyped system (and tire of even the warning messages issued by :warn). In support of our own ACL2 systems programming there are two other settings. We suggest ordinary users not use them. :warn! -- every attempt to redefine a name produces a warning but no query. Since ACL2 system functions can be redefined this way, this setting should be used by the only-slightly-less-than supremely confident ACL2 system hacker. :doit! -- this setting allows any name to be redefined silently (without query or warnings). ACL2 system functions are fair game. This setting is reserved for the supremely confident ACL2 system hacker. (Actually, this setting is used when we are loading massively modified versions of the ACL2 source files.) Part b of ld-redefinition-action tells the system how to "renew" the property list of the name being redefined. There are two choices: :erase -- erase all properties stored under the name, or :overwrite -- preserve existing properties and let the redefining overwrite them. It should be stressed that neither of these b settings is guaranteed to result in an entirely satisfactory state of affairs after the redefinition. Roughly speaking, :erase returns the property list of the name to the state it was in when the name was first introduced. Lemmas, type information, etc., stored under that name are lost. Is that what you wanted? Sometimes it is, as when the old definition is "completely wrong." But other times the old definition was "almost right" in the sense that some of the work done with it is still (intended to be) valid. In that case, :overwrite might be the correct b setting. For example if fn was a function and is being re-defun'd with the same signature, then the properties stored by the new defun should overwrite those stored by the old defun but the properties stored by defthms will be preserved. In addition, neither setting will cause ACL2 to erase properties stored under other symbols! Thus, if FOO names a rewrite rule which rewrites a term beginning with the function symbol BAR and you then redefine FOO to rewrite a term beginning with the function symbol BAZ, then the old version of FOO is still available (because the rule itself was added to the rewrite rules for BAR, whose property list was not cleared by redefining FOO). The b setting is only used as the default action when no query is made. If you choose a setting for part a that produces a query then you will have the opportunity, for each redefinition, to specify whether the property list is to be erased or overwritten. The keyword command :redef sets ld-redefinition-action to the pair (:query . :overwrite). Since the resulting query will give you the chance to specify :erase instead of :overwrite, this setting is quite convenient. But when you are engaged in heavy-duty prototyping, you may wish to use a setting such as :warn or even :doit. For that you will have to invoke a form such as: (set-ld-redefinition-action '(:doit . :overwrite) state) . Encapsulate causes somewhat odd interaction with the user if redefinition occurs within the encapsulation because the encapsulated event list is processed several times. For example, if the redefinition action causes a query and a non-local definition is actually a redefinition, then the query will be posed twice, once during each pass. C'est la vie. Finally, it should be stressed again that redefinition is dangerous because not all of the rules about a name are stored on the property list of the name. Thus, redefinition can render ill-formed terms stored elsewhere in the database or can preserve now-invalid rules. See *note REDUNDANT-EVENTS::, in particular the section "Note About Unfortunate Redundancies," for more discussion of potential pitfalls of redefinition.  File: acl2-doc-emacs.info, Node: LD-SKIP-PROOFSP, Next: LD-VERBOSE, Prev: LD-REDEFINITION-ACTION, Up: MISCELLANEOUS LD-SKIP-PROOFSP how carefully ACL2 processes your commands Examples: ACL2 !>(set-ld-skip-proofsp t state) T ACL2 !s>(set-ld-skip-proofsp nil state) NIL ACL2 !>(set-ld-skip-proofsp 'include-book state) INCLUDE-BOOK ACL2 !s> A global variable in the ACL2 state, called 'ld-skip-proofsp, determines the thoroughness with which ACL2 processes your commands. This variable may take on one of three values: t, nil or 'include-book. When ld-skip-proofsp is non-nil, the system assumes that which ought to be proved and is thus unsound. The form (set-ld-skip-proofsp flg state) is the general-purpose way of setting ld-skip-proofsp. This global variable is an "ld special," which is to say, you may call ld in such a way as to "bind" this variable for the dynamic extent of the ld. When ld-skip-proofsp is non-nil, the default prompt displays the character s. Thus, the prompt ACL2 !s> means that the default defun-mode is :logic (otherwise the character p, for :program, would also be printed; see *note DEFAULT-PRINT-PROMPT::) but "proofs are being skipped." Observe that there are two legal non-nil values, t and 'include-book. When ld-skip-proofsp is t, ACL2 skips all proof obligations but otherwise performs all other required analysis of input events. When ld-skip-proofsp is 'include-book, ACL2 skips not only proof obligations but all analysis except that required to compute the effect of successfully executed events. To explain the distinction, let us consider one particular event, say a defun. Very roughly speaking, a defun event normally involves a check of the syntactic well-formedness of the submitted definition, the generation and proof of the termination conditions, and the computation and storage of various rules such as a :definition rule and some :type-prescription rules. By "normally" above we mean when ld-skip-proofsp is nil. How does a defun behave when ld-skip-proofsp is non-nil? If ld-skip-proofsp is t, then defun performs the syntactic well-formedness checks and computes and stores the various rules, but it does not actually carry out the termination proofs. If ld-skip-proofsp is 'include-book, defun does not do the syntactic well-formedness check nor does it carry out the termination proof. Instead, it merely computes and stores the rules under the assumption that the checks and proofs would all succeed. Observe that a setting of 'include-book is "stronger" than a setting of t in the sense that 'include-book causes defun to assume even more about the admissibility of the event than t does. As one might infer from the choice of name, the include-book event sets ld-skip-proofsp to 'include-book when processing the events in a book being loaded. Thus, include-book does the miminal work necessary to carry out the effects of every event in the book. The syntactic checks and proof obligations were, presumably, successfully carried out when the book was certified. A non-nil value for ld-skip-proofsp also affects the system's output messages. Event summaries (the paragraphs that begin "Summary" and display the event forms, rules used, etc.) are not printed when ld-skip-proofsp is non-nil. Warnings and observations are printed when ld-skip-proofsp is t but are not printed when it is 'include-book. Intuitively, ld-skip-proofsp t means skip just the proofs and otherwise do all the work normally required for an event; while ld-skip-proofsp 'include-book is "stronger" and means do as little as possible to process events. In accordance with this intuition, local events are processed when ld-skip-proofsp is t but are skipped when ld-skip-proofsp is 'include-book. The ACL2 system itself uses only two settings, nil and 'include-book, the latter being used only when executing the events inside of a book being included. The ld-skip-proofsp setting of t is provided as a convenience to the user. For example, suppose one has a file of events. By loading it with ld with ld-skip-proofsp set to t, the events can all be checked for syntactic correctness and assumed without proof. This is a convenient way to recover a state lost by a system crash or to experiment with a modification of an events file. The foregoing discussion is actually based on a lie. ld-skip-proofsp is allowed two other values, 'initialize-acl2 and 'include-book-with-locals. The first causes behavior similar to t but skips local events and avoids some error checks that would otherwise prevent ACL2 from properly booting. The second is identical to 'include-book but also executes local events. These additional values are not intended for use by the user, but no barriers to their use have been erected. We close by reminding the user that ACL2 is potentially unsound if ld-skip-proofsp is ever set by the user. We provide access to it simply to allow experimentation and rapid reconstruction of lost or modified logical worlds.  File: acl2-doc-emacs.info, Node: LD-VERBOSE, Next: LEMMA-INSTANCE, Prev: LD-SKIP-PROOFSP, Up: MISCELLANEOUS LD-VERBOSE determines whether ld prints "ACL2 Loading ..." Ld-verbose is an ld special (see *note LD::). The accessor is (ld-verbose state) and the updater is (set-ld-verbose val state). Ld-verbose must be t, nil or a string or consp suitable for fmt printing via the ~@ command. The initial value of ld-verbose is a fmt message that prints the ACL2 version number, ld level and connected book directory. Before processing the forms in standard-oi, ld may print a header. The printing of this header is controlled by ld-verbose. If ld-verbose is nil, no header is printed. If it is t, ld prints the message ACL2 loading where is the input channel supplied to ld. A similar message is printed when ld completes. If ld-verbose is neither t nor nil then it is presumably a header and is printed with the ~@ fmt directive before ld begins to read and process forms. In this case the ~@ fmt directive is interpreted in an environment in which #\v is the ACL2 version string, #\l is the level of the current recursion in ld and/or wormhole, and #\c is the connected book directory (cbd).  File: acl2-doc-emacs.info, Node: LEMMA-INSTANCE, Next: LINEAR-ARITHMETIC, Prev: LD-VERBOSE, Up: MISCELLANEOUS LEMMA-INSTANCE an object denoting an instance of a theorem Lemma instances are the objects one provides via :use and :by hints (see *note HINTS::) to bring to the theorem prover's attention some previously proved or easily provable fact. A typical use of the :use hint is given below. The value specified is a list of five lemma instances. :use (reverse-reverse (:type-prescription app) (:instance assoc-of-app (x a) (y b) (z c)) (:functional-instance p-f (p consp) (f flatten)) (:instance (:theorem (equal x x)) (x (flatten a)))) Observe that an event name can be a lemma instance. The :use hint allows a single lemma instance to be provided in lieu of a list, as in: :use reverse-reverse or :use (:instance assoc-of-app (x a) (y b) (z c)) A lemma instance denotes a formula which is either known to be a theorem or which must be proved to be a theorem before it can be used. To use a lemma instance in a particular subgoal, the theorem prover adds the formula as a hypothesis to the subgoal before the normal theorem proving heuristics are applied. A lemma instance, or lmi, is of one of the following five forms: (1) name, where name names a previously proved theorem, axiom, or definition and denotes the formula (theorem) of that name. (2) rune, where rune is a rune (see *note RUNE::) denoting the :corollary justifying the rule named by the rune. (3) (:theorem term), where term is any term alleged to be a theorem. Such a lemma instance denotes the formula term. But before using such a lemma instance the system will undertake to prove term. (4) (:instance lmi (v1 t1) ... (vn tn)), where lmi is recursively a lemma instance, the vi's are distinct variables and the ti's are terms. Such a lemma instance denotes the formula obtained by instantiating the formula denoted by lmi, replacing each vi by ti. Normally ACL2 enforces the requirement that every variable vi must be bound in the formula denoted by lmi. However, the keyword :extra-bindings-ok may be inserted immediately after the lemma instance in order to remove that requirement: (:instance lmi :extra-bindings-ok (v1 t1) ... (vn tn)). (5) (:functional-instance lmi (f1 g1) ... (fn gn)), where lmi is recursively a lemma instance and each fi is an "instantiable" function symbol of arity ni and gi is a function symbol, a macro alias for a function symbol gi' (see *note MACRO-ALIASES-TABLE::) in which case we treat gi as gi', or a pseudo-lambda expression of arity ni. An instantiable function symbol is any defined or constrained function symbol except the primitives not, member, implies, and o<, and a few others, as listed by the constant *non-instantiable-primitives*. These are built-in in such a way that we cannot recover the constraints on them. (Special case: a function introduced in the :partial-theory of a dependent clause-processor is not instantiable; see *note DEFINE-TRUSTED-CLAUSE-PROCESSOR::.) A pseudo-lambda expression is an expression of the form (lambda (v1 ... vn) body) where the vi are distinct variable symbols and body is any term. No a priori relation is imposed between the vi and the variables of body, i.e., body may ignore some vi's and may contain "free" variables. However, we do not permit v to occur freely in body if the functional substitution is to be applied to any formula (lmi or the constraints to be satisfied) in a way that inserts v into the scope of a binding of v by let or mv-let (or, lambda). If you happen to violate this restriction, an informative error message will be printed. That message will list for you the potentially illegal choices for v in the context in which the functional substitution is offered. A :functional-instance lemma instance denotes the formula obtained by functionally instantiating the formula denoted by lmi, replacing fi by gi. However, before such a lemma instance can be used, the system will generate proof obligations arising from the replacement of the fi's by the gi's in constraints that "support" the lemma to be functionally instantiated; see *note CONSTRAINT::. One might expect that if the same instantiated constraint were generated on behalf of several events, then each of those instances would have to be proved. However, for the sake of efficiency, ACL2 stores the fact that such an instantiated constraint has been proved and avoids it in future events. Note that ACL2(r) (see *note REAL::) imposes additional requirements for functional instantiation. See *note FUNCTIONAL-INSTANTIATION-IN-ACL2R::. Obscure case for definitions. If the lemma instance refers to a :definition rune, then it refers to the corollary formula of that rune, which can be a simplified ("normalized") form of the original formula. However, if the hint is a :by hint and the lemma instance is based on a name (i.e., a symbol), rather than a rune, then the formula is the original formula of the event, as shown by :pe, rather than the normalized version, as shown by :pf. This is as one would expect: If you supply the name of an event, you expect it to refer to the original event. For :use hints we use the simplified (normalized) form instead, which is reasonable since one would expect simplification during the proof that re-traces the normalization done at the time the rule was created. See *note FUNCTIONAL-INSTANTIATION-EXAMPLE:: for an example of the use of :functional-instance (so-called "functional instantiation)."  File: acl2-doc-emacs.info, Node: LINEAR-ARITHMETIC, Next: LOCAL-INCOMPATIBILITY, Prev: LEMMA-INSTANCE, Up: MISCELLANEOUS LINEAR-ARITHMETIC A description of the linear arithmetic decision procedure We describe the procedure very roughly here. Fundamental to the procedure is the notion of a linear polynomial inequality. A "linear polynomial" is a sum of terms, each of which is the product of a rational constant and an "unknown." The "unknown" is permitted to be 1 simply to allow a term in the sum to be constant. Thus, an example linear polynomial is 3*x + 7*a + 2; here x and a are the (interesting) unknowns. However, the unknowns need not be variable symbols. For example, (length x) might be used as an unknown in a linear polynomial. Thus, another linear polynomial is 3*(length x) + 7*a. A "linear polynomial inequality" is an inequality (either < or <=) relation between two linear polynomials. Note that an equality may be considered as a pair of inequalities; e.q., 3*x + 7*a + 2 = 0 is the same as the conjunction of 3*x + 7*a + 2 <= 0 and 0 <= 3*x + 7*a + 2. Certain linear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third inequality with fewer unknowns. If this deduced inequality is manifestly false, a contradiction has been deduced from the assumed inequalities. For example, suppose we have two assumptions p1: 3*x + 7*a < 4 p2: 3 < 2*x and we wish to prove that, given p1 and p2, a < 0. As suggested above, we proceed by assuming the negation of our goal p3: 0 <= a. and looking for a contradiction. By cross-multiplying and adding the first two inequalities, (that is, multiplying p1 by 2, p2 by 3 and adding the respective sides), we deduce the intermediate result p4: 6*x + 14*a + 9 < 8 + 6*x which, after cancellation, is: p4: 14*a + 1 < 0. If we then cross-multiply and add p3 to p4, we get p5: 1 <= 0, a contradiction. Thus, we have proved that p1 and p2 imply the negation of p3. All of the unknowns of an inequality must be eliminated by cancellation in order to produce a constant inequality. We can choose to eliminate the unknowns in any order, but we eliminate them in term-order, largest unknowns first. (See *note TERM-ORDER::.) That is, two polys are cancelled against each other only when they have the same largest unknown. For instance, in the above example we see that x is the largest unknown in each of p1 and p2, and a in p3 and p4. Now suppose that this procedure does not produce a contradiction but instead yields a set of nontrivial inequalities. A contradiction might still be deduced if we could add to the set some additional inequalities allowing further cancellations. That is where :linear lemmas come in. When the set of inequalities has stabilized under cross-multiplication and addition and no contradiction is produced, we search the database of :linear rules for rules about the unknowns that are candidates for cancellation (i.e., are the largest unknowns in their respective inequalities). See *note LINEAR:: for a description of how :linear rules are used. See also non-linear-arithmetic for a description of an extension to the linear-arithmetic procedure described here.  File: acl2-doc-emacs.info, Node: LOCAL-INCOMPATIBILITY, Next: LOGICAL-NAME, Prev: LINEAR-ARITHMETIC, Up: MISCELLANEOUS LOCAL-INCOMPATIBILITY when non-local events won't replay in isolation Sometimes a "local incompatibility" is reported while attempting to embed some events, as in an encapsulate or include-book. This is generally due to the use of a locally defined name in a non-local event or the failure to make a witnessing definition local. Local incompatibilities may be detected while trying to execute the strictly non-local events of an embedding. For example, encapsulate operates by first executing all the events (local and non-local) with ld-skip-proofsp nil, to confirm that they are all admissible. Then it attempts merely to assume the non-local ones to create the desired theory, by executing the events with ld-skip-proofsp set to 'include-book. Similarly, include-book assumes the non-local ones, with the understanding that a previously successful certify-book has performed the admissiblity check. How can a sequence of events admitted with ld-skip-proofsp nil fail when ld-skip-proofsp is 'include-book? The key observation is that in the latter case only the non-local events are processed. The local ones are skipped and so the non-local ones must not depend upon them. Two typical mistakes are suggested by the detection of a local incompatibility: (1) a locally defined function or macro was used in a non-local event (and, in the case of encapsulate, was not included among the signatures) and (2) the witnessing definition of a function that was included among the signatures of an encapsulate was not made local. An example of mistake (1) would be to include among your encapsulated events both (local (defun fn ...)) and (defthm lemma (implies (fn ...) ...)). Observe that fn is defined locally but a formula involving fn is defined non-locally. In this case, either the defthm should be made local or the defun should be made non-local. An example of mistake (2) would be to include (fn (x) t) among your signatures and then to write (defun fn (x) ...) in your events, instead of (local (defun fn ...)). One subtle aspect of encapsulate is that if you constrain any member of a mutually recursive clique you must define the entire clique locally and then you must constrain those members of it you want axiomatized non-locally. Errors due to local incompatibility should never occur in the assumption of a fully certified book. Certification ensures against it. Therefore, if include-book reports an incompatibility, we assert that earlier in the processing of the include-book a warning was printed advising you that some book was uncertified. If this is not the case -- if include-book reports an incompatibility and there has been no prior warning about lack of certification -- please report it to us. When a local incompatibility is detected, we roll-back to the world in which we started the encapsulate or include-book. That is, we discard the intermediate world created by trying to process the events skipping proofs. This is clean, but we realize it is very frustrating because the entire sequence of events must be processed from scratch. Assuming that the embedded events were, once upon a time, processed as top-level commands (after all, at some point you managed to create this sequence of commands so that the local and non-local ones together could survive a pass in which proofs were done), it stands to reason that we could define a predicate that would determine then, before you attempted to embed them, if local incompatibilities exist. We hope to do that, eventually. We conclude with a subtle example of local incompatibility. The problem is that in order for foo-type-prescription to be admitted using the specified :typed-term (foo x), the conclusion (my-natp (foo x)) depends on my-natp being a compound-recognizer. This is fine on the first pass of the encapsulate, during which lemma my-natp-cr is admitted. But my-natp-cr is skipped on the second pass because it is marked local, and this causes foo-type-prescription to fail on the second pass. (defun my-natp (x) (declare (xargs :guard t)) (and (integerp x) (<= 0 x))) (defun foo (x) (nfix x)) (encapsulate () (local (defthm my-natp-cr (equal (my-natp x) (and (integerp x) (<= 0 x))) :rule-classes :compound-recognizer)) (defthm foo-type-prescription (my-natp (foo x)) :hints (("Goal" :in-theory (enable foo))) :rule-classes ((:type-prescription :typed-term (foo x)))))  File: acl2-doc-emacs.info, Node: LOGICAL-NAME, Next: LOOP-STOPPER, Prev: LOCAL-INCOMPATIBILITY, Up: MISCELLANEOUS LOGICAL-NAME a name created by a logical event Examples: assoc caddr + "ACL2-USER" "arith" "project/task-1/arith.lisp" :here A logical name is either a name introduced by some event, such as defun, defthm, or include-book, or else is the keyword :here, which refers to the most recent such event. See *note EVENTS::. Every logical name is either a symbol or a string. For the syntactic rules on names, see *note NAME::. The symbols name functions, macros, constants, axioms, theorems, labels, and theories. The strings name packages or books. We permit the keyword symbol :here to be used as a logical name denoting the most recently completed event. The logical name introduced by an include-book is the full book name string for the book (see *note FULL-BOOK-NAME::). Thus, under the appropriate setting for the current book directory (see *note CBD::) the event (include-book "arith") may introduce the logical name "/usr/home/smith/project/task-1/arith.lisp" . Under a different cbd setting, it may introduce a different logical name, perhaps "/local/src/acl2/library/arith.lisp" . It is possible that identical include-book events forms in a session introduce two different logical names because of the current book directory. A logical name that is a string is either a package name or a book name. If it is not a package name, we support various conventions to interpret it as a book name. If it does not end with the string ".lisp" we extend it appropriately. Then, we search for any book name that has the given logical name as a terminal substring. Suppose (include-book "arith") is the only include-book so far and that "/usr/home/smith/project/task-1/arith.lisp" is the source file it processed. Then "arith", "arith.lisp" and "task-1/arith.lisp" are all logical names identifying that include-book event (unless they are package names). Now suppose a second (include-book "arith") is executed and processes "/local/src/acl2/library/arith.lisp". Then "arith" is no longer a logical name, because it is ambiguous. However, "task-1/arith" is a logical name for the first include-book and "library/arith" is a logical name for the second. Indeed, the first can be named by "1/arith" and the second by "y/arith". Logical names are used primarily in the theory manipulation functions, e.g., universal-theory and current-theory with which you may obtain some standard theories as of some point in the historical past. The reference points are the introductions of logical names, i.e., the past is determined by the events it contains. One might ask, "Why not discuss the past with the much more flexible language of command descriptors?" (See *note COMMAND-DESCRIPTOR::.) The reason is that inside of such events as encapsulate or macro commands that expand to progns of events, command descriptors provide too coarse a grain. When logical names are used as referents in theory expressions used in books, one must consider the possibility that the defining event within the book in question becomes redundant by the definition of the name prior to the assumption of the book. See *note REDUNDANT-EVENTS::.  File: acl2-doc-emacs.info, Node: LOOP-STOPPER, Next: LP, Prev: LOGICAL-NAME, Up: MISCELLANEOUS LOOP-STOPPER limit application of permutative rewrite rules See *note RULE-CLASSES:: for a discussion of the syntax of the :loop-stopper field of :rewrite rule-classes. Here we describe how that field is used, and also how that field is created when the user does not explicitly supply it. For example, the built-in :rewrite rule commutativity-of-+, (implies (and (acl2-numberp x) (acl2-numberp y)) (equal (+ x y) (+ y x))), creates a rewrite rule with a loop-stopper of ((x y binary-+)). This means, very roughly, that the term corresponding to y must be "smaller" than the term corresponding to x in order for this rule to apply. However, the presence of binary-+ in the list means that certain functions that are "invisible" with respect to binary-+ (by default, unary- is the only such function) are more or less ignored when making this "smaller" test. We are much more precise below. Our explanation of loop-stopping is in four parts. First we discuss ACL2's notion of "term order." Next, we bring in the notion of "invisibility", and use it together with term order to define orderings on terms that are used in the loop-stopping algorithm. Third, we describe that algorithm. These topics all assume that we have in hand the :loop-stopper field of a given rewrite rule; the fourth and final topic describes how that field is calculated when it is not supplied by the user. ACL2 must sometimes decide which of two terms is syntactically simpler. It uses a total ordering on terms, called the "term order." Under this ordering constants such as '(a b c) are simpler than terms containing variables such as x and (+ 1 x). Terms containing variables are ordered according to how many occurrences of variables there are. Thus x and (+ 1 x) are both simpler than (cons x x) and (+ x y). If variable counts do not decide the order, then the number of function applications are tried. Thus (cons x x) is simpler than (+ x (+ 1 y)) because the latter has one more function application. Finally, if the number of function applications do not decide the order, a lexicographic ordering on Lisp objects is used. See *note TERM-ORDER:: for details. When the loop-stopping algorithm is controlling the use of permutative :rewrite rules it allows term1 to be moved leftward over term2 only if term1 is smaller, in a suitable sense. Note: The sense used in loop-stopping is *not* the above explained term order but a more complicated ordering described below. The use of a total ordering stops rules like commutativity from looping indefinitely because it allows (+ b a) to be permuted to (+ a b) but not vice versa, assuming a is smaller than b in the ordering. Given a set of permutative rules that allows arbitrary permutations of the tips of a tree of function calls, this will normalize the tree so that the smallest argument is leftmost and the arguments ascend in the order toward the right. Thus, for example, if the same argument appears twice in the tree, as x does in the binary-+ tree denoted by the term (+ a x b x), then when the allowed permutations are done, all occurrences of the duplicated argument in the tree will be adjacent, e.g., the tree above will be normalized to (+ a b x x). Suppose the loop-stopping algorithm used term order, as noted above, and consider the binary-+ tree denoted by (+ x y (- x)). The arguments here are in ascending term order already. Thus, no permutative rules are applied. But because we are inside a +-expression it is very convenient if x and (- x) could be given virtually the same position in the ordering so that y is not allowed to separate them. This would allow such rules as (+ i (- i) j) = j to be applied. In support of this, the ordering used in the control of permutative rules allows certain unary functions, e.g., the unary minus function above, to be "invisible" with respect to certain "surrounding" functions, e.g., + function above. Briefly, a unary function symbol fn1 is invisible with respect to a function symbol fn2 if fn2 belongs to the value of fn1 in invisible-fns-table; also see *note SET-INVISIBLE-FNS-TABLE::, which explains its format and how it can be set by the user. Roughly speaking, "invisible" function symbols are ignored for the purposes of the term-order test. Consider the example above, (+ x y (- x)). The translated version of this term is (binary-+ x (binary-+ y (unary- x))). The initial invisible-fns-table makes unary- invisible with repect to binary-+. The commutativity rule for binary-+ will attempt to swap y and (unary- x) and the loop-stopping algorithm is called to approve or disapprove. If term order is used, the swap will be disapproved. But term order is not used. While the loop-stopping algorithm is permuting arguments inside a binary-+ expression, unary- is invisible. Thus, insted of comparing y with (unary- x), the loop-stopping algorithm compares y with x, approving the swap because x comes before y. Here is a more precise specification of the total order used for loop-stopping with respect to a list, fns, of functions that are to be considered invisible. Let x and y be distinct terms; we specify when "x is smaller than y with respect to fns." If x is the application of a unary function symbol that belongs to fns, replace x by its argument. Repeat this process until the result is not the application of such a function; let us call the result x-guts. Similarly obtain y-guts from y. Now if x-guts is the same term as y-guts, then x is smaller than y in this order iff x is smaller than y in the standard term order. On the other hand, if x-guts is different than y-guts, then x is smaller than y in this order iff x-guts is smaller than y-guts in the standard term order. Now we may describe the loop-stopping algorithm. Consider a rewrite rule with conclusion (equiv lhs rhs) that applies to a term x in a given context; see *note REWRITE::. Suppose that this rewrite rule has a loop-stopper field (technically, the :heuristic-info field) of ((x1 y1 . fns-1) ... (xn yn . fns-n)). (Note that this field can be observed by using the command :pr with the name of the rule; see *note PR::.) We describe when rewriting is permitted. The simplest case is when the loop-stopper list is nil (i.e., n is 0); in that case, rewriting is permitted. Otherwise, for each i from 1 to n let xi' be the actual term corresponding to the variable xi when lhs is matched against the term to be rewritten, and similarly correspond yi' with y. If xi' and yi' are the same term for all i, then rewriting is not permitted. Otherwise, let k be the least i such that xi' and yi' are distinct. Let fns be the list of all functions that are invisible with respect to every function in fns-k, if fns-k is non-empty; otherwise, let fns be nil. Then rewriting is permitted if and only if yi' is smaller than xi' with respect to fns, in the sense defined in the preceding paragraph. It remains only to describe how the loop-stopper field is calculated for a rewrite rule when this field is not supplied by the user. (On the other hand, to see how the user may specify the :loop-stopper, see *note RULE-CLASSES::.) Suppose the conclusion of the rule is of the form (equiv lhs rhs). First of all, if rhs is not an instance of the left hand side by a substitution whose range is a list of distinct variables, then the loop-stopper field is nil. Otherwise, consider all pairs (u . v) from this substitution with the property that the first occurrence of v appears in front of the first occurrence of u in the print representation of rhs. For each such u and v, form a list fns of all functions fn in lhs with the property that u or v (or both) appears as a top-level argument of a subterm of lhs with function symbol fn. Then the loop-stopper for this rewrite rule is a list of all lists (u v . fns).  File: acl2-doc-emacs.info, Node: LP, Next: MACRO-ARGS, Prev: LOOP-STOPPER, Up: MISCELLANEOUS LP the Common Lisp entry to ACL2 To enter the ACL2 command loop from Common Lisp, call the Common Lisp program lp (which stands for "loop," as in "read-eval-print loop" or "command loop.") The ACL2 command loop is actually coded in ACL2 as the function ld (which stands for "load"). The command loop is just what you get by loading from the standard object input channel, *standard-oi*. Calling ld directly from Common Lisp is possible but fragile because hard lisp errors or aborts throw you out of ld and back to the top-level of Common Lisp. Lp calls ld in such a way as to prevent this and is thus the standard way to get into the ACL2 command loop. Also see *note ACL2-CUSTOMIZATION:: for information on the loading of an initialization file. All of the visible functionality of lp is in fact provided by ld, which is written in ACL2 itself. Therefore, you should see *note LD:: for detailed documentation of the ACL2 command loop. We sketch it below, for novice users. Every expression typed to the ACL2 top-level must be an ACL2 expression. Any ACL2 expression may be submitted for evaluation. Well-formedness is checked. Some well-formed expressions cannot be evaluated because they involve (at some level) undefined constrained functions (see *note ENCAPSULATE::). In addition, ACL2 does not allow "global variables" in expressions to be evaluated. Thus, (car '(a b c)) is legal and evaluates to A, but (car x) is not, because there is no "global context" or binding environment that gives meaning to the variable symbol x. There is an exception to the global variable rule outlined above: single-threaded objects (see *note STOBJ::) may be used as global variables in top-level expressions. The most commonly used such object is the ACL2 "current state," which is the value of the variable symbol state. This variable may occur in the top-level form to be evaluated, but must be passed only to ACL2 functions "expecting" state as described in the documentation for state and for stobjs in general. If the form returns a new state object as one of its values, then that is considered the new "current" state for the evaluation of the subsequent form. See *note STATE::. ACL2 provides some support for the functionality usually provided by global variables in a read-eval-print loop, namely the saving of the result of a computation for subsequent re-use in another expression. See *note ASSIGN:: and see *note @: atsign. If the form read is a single keyword, e.g., :pe or :ubt, then special procedures are followed. See *note KEYWORD-COMMANDS::. The command loop keeps track of the commands you have typed and allows you to review them, display them, and roll the logical state back to that created by any command. See *note HISTORY::. ACL2 makes the convention that if a top-level form returns three values, the last of which is an ACL2 state, then the first is treated as a flag that means "an error occurred," the second is the value to be printed if no error occurred, and the third is (of course) the new state. When "an error occurs" no value is printed. Thus, if you execute a top-level form that happens to return three such values, only the second will be printed (and that will only happen if the first is nil!). See *note LD:: for details.  File: acl2-doc-emacs.info, Node: MACRO-ARGS, Next: MAKE-WORMHOLE-STATUS, Prev: LP, Up: MISCELLANEOUS MACRO-ARGS the formals list of a macro definition Examples: (x y z) (x y z &optional max (base '10 basep)) (x y &rest rst) (x y &key max base) (&whole sexpr x y) The "lambda-list" of a macro definition may include simple formal parameter names as well as appropriate uses of the following lambda-list keywords from CLTL (pp. 60 and 145), respecting the order shown: &whole, &optional, &rest, &body, &key, and &allow-other-keys. ACL2 does not support &aux and &environment. In addition, we make the following restrictions: (1) initialization forms in &optional and &key specifiers must be quoted values; (2) &allow-other-keys may only be used once, as the last specifier; and (3) destructuring is not allowed. You are encouraged to experiment with the macro facility. One way to do so is to define a macro that does nothing but return the quotation of its arguments, e.g., (defmacro demo (x y &optional opt &key key1 key2) (list 'quote (list x y opt key1 key2))) You may then execute the macro on some sample forms, e.g., term value (demo 1 2) (1 2 NIL NIL NIL) (demo 1 2 3) (1 2 3 NIL NIL) (demo 1 2 :key1 3) error: non-even key/value arglist (because :key1 is used as opt) (demo 1 2 3 :key2 5) (1 2 3 NIL 5) In particular, Common Lisp specifies that if you use both &rest and &key, then both will be bound using the same list of arguments. The following example should serve to illustrate how this works. ACL2 !>(defmacro foo (&rest args &key k1 k2 k3) (list 'quote (list args k1 k2 k3))) Summary Form: ( DEFMACRO FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo :k1 3 :k2 4 :k3 5) ((:K1 3 :K2 4 :K3 5) 3 4 5) ACL2 !>(foo :k1 3 :k2 4) ((:K1 3 :K2 4) 3 4 NIL) ACL2 !>(foo :k1 3 :bad-key 7) ACL2 Error in macro expansion: Illegal key/value args (:BAD-KEY 7) in macro expansion of (FOO :K1 3 :BAD-KEY 7). The argument list for FOO is (&REST ARGS &KEY K1 K2 K3). ACL2 !> If we don't want to get the error above, we can use &allow-other-keys, as follows. ACL2 !>(defmacro bar (&rest args &key k1 k2 k3 &allow-other-keys) (list 'quote (list args k1 k2 k3))) Summary Form: ( DEFMACRO BAR ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) BAR ACL2 !>(bar :k1 3 :bad-key 7) ((:K1 3 :BAD-KEY 7) 3 NIL NIL) ACL2 !> Also see *note TRANS::.  File: acl2-doc-emacs.info, Node: MAKE-WORMHOLE-STATUS, Next: MEASURE, Prev: MACRO-ARGS, Up: MISCELLANEOUS MAKE-WORMHOLE-STATUS creates a wormhole status object from given status, entry code, and data General Form: (make-wormhole-status whs code data) See *note WORMHOLE::. Whs should be a well-formed wormhole status, code should be :ENTER or :SKIP, and data is arbitrary. This function returns a new status with the specified entry code and data, reusing whs if it is appropriate.  File: acl2-doc-emacs.info, Node: MEASURE, Next: META-EXTRACT, Prev: MAKE-WORMHOLE-STATUS, Up: MISCELLANEOUS MEASURE xargs keyword :MEASURE See *note XARGS::.  File: acl2-doc-emacs.info, Node: META-EXTRACT, Next: META-EXTRACT-CONTEXTUAL-FACT, Prev: MEASURE, Up: MISCELLANEOUS META-EXTRACT meta reasoning using valid terms extracted from context or world For this advanced topic, we assume familiarity with metatheorems and metafunctions (see *note META::), as well as extended metafunctions (see *note EXTENDED-METAFUNCTIONS::). The capability described here -- so-called "meta-extract hypotheses" for a :meta or a :clause-processor rule -- provides an advanced form of meta-level reasoning that was initially designed largely by Sol Swords, who also provided a preliminary implementation. A meta rule or clause-processor rule may have so-called "meta-extract" hypotheses that take forms displayed below. Here evl is the evaluator, obj is an arbitrary term, mfc is the metafunction context (which is a variable other than the symbol STATE that represents the metafunction context; see *note EXTENDED-METAFUNCTIONS::), state is literally the symbol STATE, a is the second argument of evl in both arguments of the conclusion of the rule, and aa is an arbitrary term. (evl (meta-extract-contextual-fact obj mfc state) a) (evl (meta-extract-global-fact obj state) aa)) ; equivalent to the next form (evl (meta-extract-global-fact+ obj state state) aa) (evl (meta-extract-global-fact+ obj st state) aa) The first form is only legal for :meta rules for which the metafunction is an extended metafunction. The remaining forms are legal for both :meta rules and :clause-processor rules. Sol Swords has contributed a community book, clause-processors/meta-extract-user.lisp, that uses a Skolemization trick to allow one to use at most one meta-extract-global-fact+ hypothesis and at most one meta-extract-contextual-fact hypothesis. These additional hypotheses may be necessary in order to prove a proposed metatheorem or (for the second type of hypothesis above) clause-processor rule, in particular when the correctness of the metafunction depends on the correctness of utilities extracting formulas from the logical world or (for the first type) facts from the metafunction context (mfc). After the rule is proved, however, the meta-extract hypotheses have no effect on how the rule is applied during a proof. An argument for correctness of using meta-extract hypotheses is given in the ACL2 source code within a comment entitled "Essay on Correctness of Meta Reasoning". In the documentation below, we focus primarily on :meta rules, since the use of meta-extract-global-fact hypotheses in :clause-processor rules is entirely analogous. (At the end, though, we discuss the last of the four forms displayed above.) And for :meta rules we focus not on the application of rules but, rather, on how the use of meta-extract hypotheses allow you to prove correctness of metafunctions that use facts from the logical world or the metafunction context (mfc). Below we describe properties of meta-extract-contextual-fact and meta-extract-global-fact, but only after we illustrate their utility with an example. But even before we present that example, we first give a sense of how to think about these functions by showing a theorem that one can prove about the first of them. If this snippet doesn't help your intuition, then just skip over it and start with the example. (defevaluator evl evl-list ((binary-+ x y) (typespec-check x y))) (thm (implies (not (bad-atom (cdr (assoc-equal 'x alist)))) (equal (evl (meta-extract-contextual-fact (list :typeset 'x) mfc state) alist) (not (equal 0 ; indicates non-empty intersection (logand (type-set-quote ; type-set of a constant (cdr (assoc-equal 'x alist))) (mfc-ts-fn 'x mfc state nil))))))) The following example comes from the community book, books/clause-processors/meta-extract-simple-test.lisp (after it defines the evaluator), which presents very basic (and contrived) examples that nevertheless illustrate meta-extract hypotheses. (defthm plus-identity-2-meta (implies (and (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp) state) (list (cons 'u (nthmeta-ev (cadr (cadr term)) a)))) (nthmeta-ev (meta-extract-contextual-fact `(:typeset ,(caddr term)) mfc state) a)) (equal (nthmeta-ev term a) (nthmeta-ev (plus-identity-2-metafn term mfc state) a))) :rule-classes ((:meta :trigger-fns (binary-+)))) The two hypotheses illustratate the two basic kinds of meta-extract hypotheses: applications of the evaluator to a call of meta-extract-global-fact and to a call of meta-extract-contextual-fact. Here is the definition of the metafunction used in the above rule, slightly simplified here from what is found in the above book (but adequate for proving the two events that follow it in the above book). (defun plus-identity-2-metafn (term mfc state) (declare (xargs :stobjs state :verify-guards nil)) (case-match term (('binary-+ ('bar &) y) (cond ((equal (meta-extract-formula 'bar-posp state) '(POSP (BAR U))) (if (ts= (mfc-ts y mfc state :forcep nil) *ts-character*) (cadr term) term)) (t term))) (& term))) This metafunction returns its input term unchanged except in the case that the term is of the form (binary-+ (bar x) y) and the following two conditions are met, in which case it returns (bar x). (1) (equal (meta-extract-formula 'bar-posp state) '(POSP (BAR U))) (2) (ts= (mfc-ts y mfc state :forcep nil) *ts-character*) So suppose that term is (list 'binary-+ (list 'bar x) y). We show how the meta-extract hypotheses together with (1) and (2) imply that the conclusion of the above :meta rule holds. Here is that conclusion after a bit of simplification. (equal (nthmeta-ev (list 'binary-+ (list 'bar x) y) a) (nthmeta-ev (list 'bar x) a)) This equality simplifies as follows using the evaluator properties of nthmeta-ev. (equal (binary-+ (bar (nthmeta-ev x a)) (nthmeta-ev y a)) (bar (nthmeta-ev x a))) Since a positive number plus a character is that number, it clearly suffices to show: (A) (posp (bar (nthmeta-ev x a))) (B) (characterp (nthmeta-ev y a)) It remains then to show that these follow from (1) and (2) together with the meta-extract hypotheses. First consider (A). We show that it is just a simplification of the first meta-extract hypothesis. (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp) state) (list (cons 'u (nthmeta-ev (cadr (cadr term)) a)))) = {by our assumption that term is (list 'binary-+ (list 'bar x) y)} (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp) state) (list (cons 'u (nthmeta-ev x a)))) = {by definition of meta-extract-global-fact, as discussed later} (nthmeta-ev (meta-extract-formula 'bar-posp state) (list (cons 'u (nthmeta-ev x a)))) = {by (1)} (nthmeta-ev '(posp (bar u)) (list (cons 'u (nthmeta-ev x a)))) = {by evaluator properties of nthmeta-ev} (posp (bar (nthmeta-ev x a))) Now consider (B). We show that it is just a simplification of the second meta-extract hypothesis. (nthmeta-ev (meta-extract-contextual-fact `(:typeset ,(caddr term)) mfc state) a) = {by our assumption that term is (list 'binary-+ (list 'bar x) y)} (nthmeta-ev (meta-extract-contextual-fact (list ':typeset y) mfc state) a) = {by definition of meta-extract-contextual-fact, as discussed later} (nthmeta-ev (list 'typespec-check (list 'quote (mfc-ts y mfc state :forcep nil)) y) a) = {by (2)} (nthmeta-ev (list 'typespec-check (list 'quote *ts-character*) y) a) = {by evaluator properties of nthmeta-ev} (typespec-check *ts-character* (nthmeta-ev y a)) = {by definition of typespec-check} (characterp (nthmeta-ev y a)) Note the use of :forcep nil above. All of the mfc-xx functions take a keyword argument :forcep. Calls of mfc-xx functions made on behalf of meta-extract-contextual-fact always use :forcep nil, so in order to reason about these calls in your own metafunctions, you will want to use :forcep nil. We have contemplated adding a utility like meta-extract-contextual-fact that allows forcing but returns a tag-tree (see *note TTREE::), and may do so if there is demand for it. Finally, we document what is provided logically by calls of meta-extract-global-fact and meta-extract-contextual-fact. Of course, you are invited to look at the definitions of these function in the ACL2 source code, or by using :pe. Note that both of these functions are non-executable (each of their bodies is inside a call of non-exec); their purpose is purely logical, not for execution. The functions return *t*, i.e., (quote t), in cases that they provide no information. First we consider the value of (meta-extract-global-fact obj state) for various values of obj. When we refer below to concepts like "body" and "evaluation", we refer to these with respect to the logical world of the input state. Case obj = (list :formula FN): The value reduces to the value of (meta-extract-formula FN state), which returns the "formula" of FN in the following sense. If FN is a function symbol with formals (X1 ... Xk), then the formula is the constraint on FN if FN is constrained or introduced by defchoose, and otherwise is (equal (FN X1 ... Xk) BODY), where BODY is the (unsimplified) body of the definition of FN. Otherwise, if FN is the name of a theorem, the formula is just what is stored for that theorem. Otherwise, the formula is *t*. Case obj = (list :lemma FN N): Assume N is a natural number; otherwise, treat N as 0. If FN is a function symbol with more than N associated lemmas -- "associated" in the sense of being either a :definition rule for FN or a :rewrite rule for FN whose left-hand side has a top function symbol of FN -- then the value is the Nth such lemma (with zero-based indexing). Otherwise the value is *t*. Case obj = (list :fncall FN ARGLIST): Assume that FN is a :logic-mode function symbol and that ARGLIST is a true list of values of the same length as list of formal parameters for FN (i.e., as the input arity of FN). Also assume that the application of FN to actual parameter list ARGLIST returns a result (mv nil x). Let (QARG1 ... QARGk) be the result of quoting each element of ARGLIST, i.e., replacing each y in ARGLIST by the two-element list (quote y). Then the value is the term (equal (FN QARG1 ... QARGk) (quote x)). For any other values of obj, the value is *t*. Finally, the value of (meta-extract-contextual-fact obj mfc state) is as follows for various values of obj. Note a difference from the semantics of meta-extract-global-fact: below, the relevant logical world is the one stored in the metafunction context, mfc, not in the input state. Case obj = (list :typeset TERM ...): The value is the value of (typespec-check ts TERM), where ts is the value of (mfc-ts TERM mfc state :forcep nil :ttreep nil), and where (typespec-check ts val) is defined to be true when val has type-set ts. (Exception: If val satisfies bad-atom then typespec-check is true when ts is negative.) Case obj = (list :rw+ TERM ALIST OBJ EQUIV ...): We assume below that EQUIV is a symbol that represents an equivalence relation, where nil represents equal, t represents iff, and otherwise EQUIV represents itself (an equivalence relation in the current logical world). For any other EQUIV the value is *t*. Now let rhs be the value of (mfc-rw+ TERM ALIST OBJ EQUIV mfc state :forcep nil :ttreep nil). Then the value is the term (list 'equv (sublis-var ALIST TERM) rhs), where equv is the equivalence relation represented by EQUIV, and sublis-var is defined to substitute a variable-binding alist into a term. Case obj = (list :rw TERM OBJ EQUIV ...): The value is the same as above but for an ALIST of nil, i.e., for the case that obj is (list :rw+ TERM nil OBJ EQUIV ...). Case obj = (list :ap TERM ...): The value is (list 'not TERM) if (mfc-ap TERM mfc state :forcep nil) is true, else is *t*. Case obj = (list :relieve-hyp HYP ALIST RUNE TARGET BKPTR ...): The value is (sublis-var alist hyp) -- see above for a discussion of sublis-var -- if the following is true. (mfc-relieve-hyp hyp alist rune target bkptr mfc state :forcep nil :ttreep nil) Otherwise the value is *t*. If no case above applies, then the value is *t*. We conclude by considering the fourth of the four forms above (and implicitly, its special case represented by the third form above): (evl (meta-extract-global-fact+ obj st state) aa) The discussion above is for the function meta-extract-global-fact+, but assumes that the logical worlds of st and state are equal; otherwise the value returned is *t*. Of course, since a call of meta-extract-global-fact expands to a corresponding call of meta-extract-global-fact+ in which the last two arguments are both state, that condition holds automatically for that case. But the state mentioned in the meta-extract hypotheses of a meta rule or clause-processor rule is in essence an initial state. In the case of a clause-processor rule, the clause-processor function may modify that initial state (say, by printing or modifying some state globals) without changing its world, and then pass that modified state to fncall-term. While fncall-term may produce a different result for this modified state than for the initial state, both are valid: the state used for heuristic purposes, such as determining whether guard-checking may cause an error. A useful instance of the hypothesis displayed above will be one in which st is that modified state.  File: acl2-doc-emacs.info, Node: META-EXTRACT-CONTEXTUAL-FACT, Next: META-EXTRACT-FORMULA, Prev: META-EXTRACT, Up: MISCELLANEOUS META-EXTRACT-CONTEXTUAL-FACT See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: META-EXTRACT-FORMULA, Next: META-EXTRACT-GLOBAL-FACT, Prev: META-EXTRACT-CONTEXTUAL-FACT, Up: MISCELLANEOUS META-EXTRACT-FORMULA See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: META-EXTRACT-GLOBAL-FACT, Next: META-EXTRACT-GLOBAL-FACT+, Prev: META-EXTRACT-FORMULA, Up: MISCELLANEOUS META-EXTRACT-GLOBAL-FACT See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: META-EXTRACT-GLOBAL-FACT+, Next: META-EXTRACT-RW+-TERM, Prev: META-EXTRACT-GLOBAL-FACT, Up: MISCELLANEOUS META-EXTRACT-GLOBAL-FACT+ See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: META-EXTRACT-RW+-TERM, Next: MODE, Prev: META-EXTRACT-GLOBAL-FACT+, Up: MISCELLANEOUS META-EXTRACT-RW+-TERM See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: MODE, Next: NAME, Prev: META-EXTRACT-RW+-TERM, Up: MISCELLANEOUS MODE xargs keyword :MODE See *note XARGS::.  File: acl2-doc-emacs.info, Node: NAME, Next: NIL-GOAL, Prev: MODE, Up: MISCELLANEOUS NAME syntactic rules on logical names Examples Counter-Examples PRIMEP 91 (not a symbolp) F-AC-23 :CHK-LIST (in KEYWORD package) 1AX *PACKAGE* (in the Lisp Package) |Prime Number| 1E3 (not a symbolp) Many different ACL2 entities have names, e.g., functions, macros, variables, constants, packages, theorems, theories, etc. Package names are strings. All other names are symbols and may not be in the "KEYWORD" package. Moreover, names of functions, macros, constrained functions, and constants, other than those that are predefined, must not be in the "main Lisp package" (which is ("LISP" or "COMMON-LISP", according to whether we are following CLTL1 or CLTL2). An analogous restriction on variables is given below. T, nil, and all names above except those that begin with ampersand (&) are names of variables or constants. T, nil, and those names beginning and ending with star (*) are constants. All other such names are variables. Note that names that start with ampersand, such as &rest, may be lambda list keywords and are reserved for such use whether or not they are in the CLTL constant lambda-list-keywords. (See pg 82 of CLTL2.) That is, these may not be used as variables. Unlike constants, variables may be in the main Lisp package as long as they are in the original list of imports from that package to ACL2, the list *common-lisp-symbols-from-main-lisp-package*, and do not belong to a list of symbols that are potentially "global." This latter list is the value of *common-lisp-specials-and-constants*. Our restrictions pertaining to the main Lisp package take into account that some symbols, e.g., lambda-list-keywords and boole-c2, are "special." Permitting them to be bound could have harmful effects. In addition, the Common Lisp language does not allow certain manipulations with many symbols of that package. So, we stay away from them, except for allowing certain variables as indicated above.  File: acl2-doc-emacs.info, Node: NIL-GOAL, Next: NO-THANKS, Prev: NAME, Up: MISCELLANEOUS NIL-GOAL how to proceed when the prover generates a goal of nil At the end of a failed proof, one typically sees so-called "key checkpoints" (see *note SET-GAG-MODE::). These may be annotated as follows. [NOTE: A goal of NIL was generated. See :DOC nil-goal.] This documentation topic gives some ideas about how to think about the situation described by that message: some goal has reduced to nil. Suppose then that you see the above NOTE. If you look back at the proof log, even with gag-mode enabled, you will see a message saying that a goal of NIL "has been generated". This may indicate that the original goal is not a theorem, since most of the prover's activity is to replace a goal by an equivalent conjunction of its child subgoals. However, if some ancestor of the nil goal has undergone a process other than simplification or destructor elimination -- fertilization (heuristic use of equalities), generalization, or elimination of irrelevance -- then it is quite possible that the prover got to the nil goal by replacing a goal by a stronger (and perhaps false) conjunction of child subgoals. At present, if you are using gag-mode (the default) then you will need to issue the command :pso ("Print Saved Output") if you want to see whether the situation above has occurred. However, that might not be necessary. A good rule of thumb is that if the nil goal is under more level of induction (e.g., with a prefix "*i.j" such as "Subgoal *1.1/2.2"), then there is some likelihood that the situation above did indeed occur, and you can spend your time and energy looking at the key checkpoints printed in the summary to see if they suggest useful rewrite rules to prove. On the other hand, if the nil goal is at the top level (e.g. with a name not starting with "*", such as "Subgoal 3.2"), then the original conjecture is probably not a theorem. If you do not quickly see why that is the case, then you might find it useful to issue the command :pso to see which case reduced to nil, in order to get insight about how the theorem might be falsified.  File: acl2-doc-emacs.info, Node: NO-THANKS, Next: NON-EXECUTABLE, Prev: NIL-GOAL, Up: MISCELLANEOUS NO-THANKS hints keyword :NO-THANKS See *note HINTS::.  File: acl2-doc-emacs.info, Node: NON-EXECUTABLE, Next: NON-LINEAR-ARITHMETIC, Prev: NO-THANKS, Up: MISCELLANEOUS NON-EXECUTABLE xargs keyword :NON-EXECUTABLE See *note XARGS::.  File: acl2-doc-emacs.info, Node: NON-LINEAR-ARITHMETIC, Next: NONLINEARP, Prev: NON-EXECUTABLE, Up: MISCELLANEOUS NON-LINEAR-ARITHMETIC Non-linear Arithmetic This documentation topic is divided into two parts. We first discuss the practical aspect of how to use the non-linear arithmetic extension to ACL2, and then the theory behind it. We assume that the reader is familiar with the material in linear-arithmetic and that on :linear rules. We begin our discussion of how to use non-linear arithmetic with a simple example. Assume that we wish to prove: (thm (implies (and (rationalp x) (rationalp y) (rationalp z) (< 0 y) (< x (* y z))) (< (floor x y) z))) Note that (floor x y) <= (/ x y). Note also that if we divide both sides of x < (* y z) by y, since 0 < y, we obtain (/ x y) < z. By chaining these two inequalities together, we get the inequality we desired to prove. We now proceed with our example session: (skip-proofs (progn ; Since the truth of this theorem depends on the linear properties ; of floor, we will need the linear lemma: (defthm floor-bounds-1 (implies (and (rationalp x) (rationalp y)) (and (< (+ (/ x y) -1) (floor x y)) (<= (floor x y) (/ x y)))) :rule-classes ((:linear :trigger-terms ((floor x y))))) ; We now disable floor, so that the linear lemma will be used. (in-theory (disable floor)) ; We create five rewrite rules which we will use during non-linear ; arithmetic. The necessity for these is due to one of the differences in ; ACL2's behaviour when non-linear arithmetic is turned on. Although ; the conclusions of linear lemmas have always been rewritten before ; they are used, now, when non-linear arithmetic is turned on, the ; conclusions are rewritten under a different theory than under ``normal'' ; rewriting. This theory is also used in other, similar, circumstances ; described below. (defthm |arith (* -1 x)| (equal (* -1 x) (- x))) (defthm |arith (* 1 x)| (equal (* 1 x) (fix x))) (defthm |arith (* x (/ x) y)| (equal (* x (/ x) y) (if (equal (fix x) 0) 0 (fix y)))) (defthm |arith (* y x)| (equal (* y x) (* x y))) (defthm |arith (fix x)| (implies (acl2-numberp x) (equal (fix x) x)))) ) ; End skip-proofs. ; We disable the above rewrite rules from normal use. (in-theory (disable |arith (* -1 x)| |arith (* 1 x)| |arith (* x (/ x) y)| |arith (* y x)| |arith (fix x)|)) ; We create an arithmetic-theory. Note that we must give a quoted ; constant for the theory --- none of the normal theory-functions ; are applicable to in-arithmetic-theory. (in-arithmetic-theory '(|arith (* -1 x)| |arith (* 1 x)| |arith (* x (/ x) y)| |arith (* y x)| |arith (fix x)|)) ; We turn non-linear arithmetic on. (set-non-linearp t) ; We can now go ahead and prove our theorem. (thm (implies (and (rationalp x) (rationalp y) (rationalp z) (< 0 y) (< x (* y z))) (< (floor x y) z))) The above example illustrates the two practical requirements for using non-linear arithmetic in ACL2. First, one must set up an arithmetic-theory. Usually, one would not set up an arithmetic-theory on one's own but would instead load a library book or books which do so. Second, one must turn the non-linear arithmetic extension on. This one must do explicitly -- no book can do this for you. For a brief discussion of why this is so, even though (set-non-linearp t) is an embeddable event, see *note ACL2-DEFAULTS-TABLE:: (in particular, the final paragraph). (Note that (set-non-linearp t) modifies the acl2-defaults-table.) Also see *note SET-NON-LINEARP::, see *note EMBEDDED-EVENT-FORM::, and see *note EVENTS::. You can also enable non-linear arithmetic with the hint :nonlinearp t. See *note HINTS::. We, in fact, recommend the use of a hint which will enable nonlinear arithmetic only when the goal has stabilized under rewriting. Using default-hints can make this easier. (defun nonlinearp-default-hint (stable-under-simplificationp hist pspv) (cond (stable-under-simplificationp (if (not (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp)) '(:computed-hint-replacement t :nonlinearp t) nil)) ((access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp) (if (not (equal (caar hist) 'SETTLED-DOWN-CLAUSE)) '(:computed-hint-replacement t :nonlinearp nil) nil)) (t nil))) (set-default-hints '((nonlinearp-default-hint stable-under-simplificationp hist pspv))) This has proven to be a helpful strategy which allows faster proof times. We now proceed to briefly describe the theory behind the non-linear extension to ACL2. In linear-arithmetic it was stated that, "[L]inear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third inequality...." That is, if 0 < poly1, 0 < poly2, and c and d are positive rational constants, then 0 < c*poly1 + d*poly2. Similarly, given the above, 0 < poly1*poly2. In the linear arithmetic case, we are taking advantage of the facts that multiplication by a positive rational constant does not change the sign of a polynomial and that the sum of two positive polynomials is itself positive. In the non-linear arithmetic case, we are using the fact that the product of two positive polynomials is itself positive. For example, suppose we have the three assumptions: p1: 3*x*y + 7*a < 4 p2: 3 < 2*x or p2': 0 < -3 + 2*x p3: 1 < y or p3': 0 < -1 + y, and we wish to prove that a < 0. As described elsewhere (see *note LINEAR-ARITHMETIC::), we proceed by assuming the negation of our goal: p4: 0 <= a, and looking for a contradiction. There are no cancellations which can be performed by linear arithmetic in the above situation. (Recall that two polynomials are cancelled against each other only when they have the same largest unknown.) However, p1 has a product as its largest unknown, and for each of the factors of that product there is a poly that has that factor as a largest unknown. When non-linear arithmetic is enabled, ACL2 will therefore multiply p1' and p2' obtaining p5: 0 < 3 + -2*x + -3*y + 2*x*y. The addition of this polynomial will allow cancelation to continue and, in this case, we will prove our goal. Thus, just as ACL2 adds two polynomials together when they have the same largest unknown of opposite signs in order to create a new "smaller" polynomial; so ACL2 multiplies polynomials together when the product of their largest unknowns is itself the largest unknown of another polynomial. As the use of :linear lemmas to further seed the arithmetic database may allow cancellation to proceed, so may the use of non-linear arithmetic. This multiplication of polynomials is the motivation for an arithmetic-theory distinct from than the normal one. Because this may be done so often, and because the individual factors have presumably already been rewritten, it is important that this be done in an efficient way. The use of a small, specialized, theory helps avoid the repeated application of rewrite rules to already stabilized terms.  File: acl2-doc-emacs.info, Node: NONLINEARP, Next: NORMALIZE, Prev: NON-LINEAR-ARITHMETIC, Up: MISCELLANEOUS NONLINEARP hints keyword :NONLINEARP See *note HINTS::.  File: acl2-doc-emacs.info, Node: NORMALIZE, Next: NU-REWRITER, Prev: NONLINEARP, Up: MISCELLANEOUS NORMALIZE xargs keyword :NORMALIZE See *note XARGS::.  File: acl2-doc-emacs.info, Node: NU-REWRITER, Next: OBDD, Prev: NORMALIZE, Up: MISCELLANEOUS NU-REWRITER rewriting NTH/UPDATE-NTH expressions The rewriter contains special provisions for rewriting expressions composed of nth, update-nth, update-nth-array, together with let expressions and other applications of non-recursive functions or lambda expressions. For details see the paper "Rewriting for Symbolic Execution of State Machine Models" by J Strother Moore. Also see *note SET-NU-REWRITER-MODE::. The "nu-rewriter" is a recent addition to the main rewrite engine in ACL2. Consider the expression (let ((s (update-nth 1 (new-a x s) s))) (let ((s (update-nth 2 (new-b x s) s))) (let ((s (update-nth 3 (new-c x s) s))) s))) If the lets in this expression are expanded, a very large expression results because of the duplicate occurrences of s: (update-nth 3 (new-c x (update-nth 2 (new-b x (update-nth 1 (new-a x s) s)) (update-nth 1 (new-a x s) s))) (update-nth 2 (new-b x (update-nth 1 (new-a x s) s)) (update-nth 1 (new-a x s) s))). This expansion of the let expression can be very expensive in space and time. In particular, the (new-a x s) expression might be rewritten many times. Now imagine asking what 2nd component of the structure is. That is, consider (nth 2 (let ((s (update-nth 1 (new-a x s) s))) (let ((s (update-nth 2 (new-b x s) s))) (let ((s (update-nth 3 (new-c x s) s))) s)))) The normal ACL2 rewrite engine would answer this question by first rewriting the arguments to the nth expression; in particular, it would expand the nested let expression to the large nested update-nth expression and then, using rules such as (defthm nth-update-nth (equal (nth m (update-nth n val l)) (if (equal (nfix m) (nfix n)) val (nth m l)))) would reduce the expression to (new-b x (update-nth 1 (new-a x s) s)). The purpose of the nu-rewriter is to allow simplifications like this without first expanding the lets. The "nu" in the name is an acronym for "nth/update-nth". The nu-rewriter knows how to move an nth into a let without expanding the let and how to simplify it if it nestles up against an update-nth. There are four characteristics of this problem: the presence of nth, the presence of update-nth, the use of let to provide "sequential" updates, and the use of constant indices. Nth and update-nth need not occur explicitly; they may be used inside of definitions of "wrapper" functions. Because the nu-rewriter changes the order in which things are rewritten, its routine use can make ACL2 unable to reproduce old proofs. It is therefore switched off by default. If your application exhibits the characteristics above, you might wish to switch the nu-rewriter on using set-nu-rewriter-mode. More will eventually be written about the nu-rewriter. However, it is described in detail in the paper cited at the beginning of this documentation topic.  File: acl2-doc-emacs.info, Node: OBDD, Next: ORDINALS, Prev: NU-REWRITER, Up: MISCELLANEOUS OBDD ordered binary decision diagrams with rewriting See *note BDD:: for information on this topic.  File: acl2-doc-emacs.info, Node: ORDINALS, Next: OTF-FLG, Prev: OBDD, Up: MISCELLANEOUS ORDINALS ordinals in ACL2 Ordinals are used in ACL2 for proving termination in the admission of recursive function definitions. For a proof that the ACL2 ordinals are well-founded, see *note PROOF-OF-WELL-FOUNDEDNESS::. The representation of ordinals changed in ACL2 Version_2.8, and is due to Pete Manolios and Daron Vroon. They have also defined algorithms for ordinal arithmetic, created a library of theorems to reason about ordinal arithmetic, and written the rest of this documentation in order to explain this change. We thank them for their efforts. Although they have provided the implementation and even modified the community books as needed, we have looked over their work and are maintaining it (and this documentation); if there are any bugs, they should be considered ours (Matt Kaufmann and J Moore). A book is included for compatibility with the representation before Version_2.8. For books that contain events relying on the previous ordinal implementation, insert the following lines before the first such event: (include-book "ordinals/e0-ordinal" :dir :system) (set-well-founded-relation e0-ord-<) The new ordinal representation is based on a slightly different version of Cantor Normal Form than that used by the old ordinals. An advantage of the new representation is that it is exponentially more succinct than the old representation. While pre-Version_2.8 ACL2 versions provided built-in functions for checking if an object is an ordinal and for comparing two ordinals, they did not provide support for reasoning about and constructing ordinals. The community books directory books/ordinals provides such support. First, it provides efficient algorithms for ordinal arithmetic (including addition, subtraction, multiplication, and exponentiation). The algorithms and their complexity are described in the following paper. Manolios, Panagiotis & Vroon, Daron. Algorithms for ordinal arithmetic. Baader, Franz (ed), 19th International Conference on Automated Deduction--CADE-19. Pages 243-257 of LNAI, vol. 2741. Springer-Verlag. Second, the algorithms are mechanically verified and libraries of theorems which can be used to automate reasoning involving the ordinals are provided. For details, see the following paper. Manolios, Panagiotis & Vroon, Daron. Ordinal arithmetic in ACL2. Kaufmann, Matt, & Moore, J Strother (eds). Fourth International Workshop on the ACL2 Theorem Prover and Its Applications (ACL2-2003), July, 2003. See `http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/'. We now describe aspects of the above mentioned books in more detail. The new ordering function is o< and the new ordinal recognizer is o-p. See also natp, posp, o<=, o>, o>=, o-first-expt, o-first-coeff, o-rst, make-ord, o-finp, and o-infp. The old ordinals were based on the following formulation of Cantor Normal Form: For any ordinal, a < epsilon-0, there exist natural numbers p and n, and ordinals a1 >= a2 >= ... >= an > 0 such that a > a1 and a = w^(a1) + w^(a2) + ... + w^(an) + p. Thus, a predicate recognizing ACL2's old ordinals is given by the following definition. (defun e0-ordinalp (x) (if (consp x) (and (e0-ordinalp (car x)) (not (equal (car x) 0)) (e0-ordinalp (cdr x)) (or (atom (cdr x)) (not (e0-ord-< (car x) (cadr x))))) (and (integerp x) (>= x 0)))) The new representation is based on a corollary to the above theorem, which we get by the left distributive property of ordinal multiplication over ordinal addition. Thus, w^a + w^a = (w^a)2, w^a + w^a + w^a = (w^a)3 and so forth. The corollary is as follows: For any ordinal, a < epsilon-0, there exist natural numbers p and n, positive integers x1, x2, ..., xn and ordinals a1 > a2 > ... > an > 0 such that a > a1 and a = w^(a1)x1 + w^(a2)x2 + ... + w^(an)xn + p. Instead of representing an ordinal as a list of non-increasing ordinals, we represent it as a list of exponent-coefficient pairs, such that the exponents are strictly decreasing (see *note O-P::). Note that this representation is exponentially more efficient than the old representation. The ordinal arithmetic functions: o+, o-, o*, and o^ are defined in the ordinals library (in the community books directory books/ordinals). To use them, include the book ordinals-without-arithmetic or ordinals, depending on whether you want the arithmetic books included or not (ordinals includes community book books/arithmetic/top-with-meta). To use the old ordinals, include the book e0-ordinal and run the command (set-well-founded-relation e0-ord-<) The community book books/arithmetic/natp-posp is a book for reasoning about posp and natp. We recommend using this book if you have to reason about posp and natp. It is included in community book books/arithmetic/top, which is included in community book books/arithmetic/top-with-meta, which is included in community book books/ordinals/ordinals. If you have a good reason to use the old definitions of the ordinals (e.g., because of legacy code and theorems), then we provide a convenient way to do this. The book ordinal-isomorphism proves that the new ordinals are order-isomorphic to the old ordinals and thus theorems proved in one context can be directly transferred to the other. For an example of how to do this, look at the book defmul in the community books directory books/workshops/2000/ruiz/multiset. The ordinals books have been used to prove non-trivial theorems. For a good example, see the books in the community books directory books/workshops/2003/sustik/support, where Matyas Sustik proves Dickson's lemma. Finally, many termination proofs can be carried out with weaker orderings than the ordinals up to epsilon-0. For example, many inductive theorem provers only know that the lexicographic ordering on natural numbers is well-founded. The book lexicographic-ordering contains a definition of such an ordering l< whose arguments are either a list of natural numbers, or a natural number. In the book we prove that l< is well-founded (that is, we prove a :well-founded-relation defthm and provide a macro llist to simplify the generation of measure functions. We also show how to use l< to prove that the famous Ackermann function terminates. Finally, since l< does something reasonable with natural numbers, it gets along with acl2-count, the default measure chosen by ACL2.  File: acl2-doc-emacs.info, Node: OTF-FLG, Next: OVERRIDE-HINTS, Prev: ORDINALS, Up: MISCELLANEOUS OTF-FLG allow more than one initial subgoal to be pushed for induction The value of this flag is normally nil. If you want to prevent the theorem prover from abandoning its initial work upon pushing the second subgoal, set :otf-flg to t. Suppose you submit a conjecture to the theorem prover and the system splits it up into many subgoals. Any subgoal not proved by other methods is eventually set aside for an attempted induction proof. But upon setting aside the second such subgoal, the system chickens out and decides that rather than prove n>1 subgoals inductively, it will abandon its initial work and attempt induction on the originally submitted conjecture. The :otf-flg (Onward Thru the Fog) allows you to override this chickening out. When :otf-flg is t, the system will push all the initial subgoals and proceed to try to prove each, independently, by induction. Even when you don't expect induction to be used or to succeed, setting the :otf-flg is a good way to force the system to generate and display all the initial subgoals. For defthm and thm, :otf-flg is a keyword argument that is a peer to :rule-classes and :hints. It may be supplied as in the following examples; also see *note DEFTHM::. (thm (my-predicate x y) :rule-classes nil :otf-flg t) (defthm append-assoc (equal (append (append x y) z) (append x (append y z))) :hints (("Goal" :induct t)) :otf-flg t) The :otf-flg may be supplied to defun via the xargs declare option. When you supply an :otf-flg hint to defun, the flag is effective for the termination proofs and the guard proofs, if any.  File: acl2-doc-emacs.info, Node: OVERRIDE-HINTS, Next: P!, Prev: OTF-FLG, Up: MISCELLANEOUS OVERRIDE-HINTS a list of hints given priority in every proof attempt This is an advanced feature, originally implemented to help system designers to create "modes" that control the way hints are supplied to the theorem prover. Please see *note DEFAULT-HINTS:: for the much more usual way to install hints that may be applied by default. * Menu: Related topics other than immediate subtopics: * ADD-OVERRIDE-HINTS:: add to the override-hints * ADD-OVERRIDE-HINTS!:: add non-locally to the override-hints * REMOVE-OVERRIDE-HINTS:: delete from the list of override-hints * REMOVE-OVERRIDE-HINTS!:: delete non-locally from the list of override-hints * SET-OVERRIDE-HINTS:: set the override-hints * SET-OVERRIDE-HINTS!:: set the override-hints non-locally Examples: ACL2 !>(override-hints (w state)) ((computed-hint-1 clause keyword-alist processor) (computed-hint-2 clause keyword-alist stable-under-simplificationp)) Override-hints returns a list of computed hints (see *note COMPUTED-HINTS::) which, unlike other computed hints, may mention the variable KEYWORD-ALIST. Before reading further, please see *note HINTS-AND-THE-WATERFALL:: to review the basics of how hints are applied during a proof. In particular, we assume familiarity with the notion of selecting a hint to be applied to the current goal. If there are override-hints, that hint selection is tentative, because if it reduced to nil after the application of override-hints, then that hint will be skipped and the attempt will continue for selecting an applicable hint. (Craft your override-hints so that :no-op t is returned in such cases instead of nil, if you don't want the hint to be skipped.) But we must explain what is meant by "the application of override-hints", and we do that now. Suppose that there are override-hints when a hint is selected for the current goal. That selected hint is a keyword-alist, which is an alternating list of hint keywords and their values, whose source is either an explicit hint (goal-name :key1 val1 ... :keyn valn) where the :keyi are allowed to be custom hint keywords (which are expanded away; see *note CUSTOM-KEYWORD-HINTS::), or else is the non-nil keyword-alist produced by evaluating a computed hint. Then the override-hints are applied to that keyword-alist as follows, one at a time, in order of their occurrence in the list of override-hints (as determined by the use of set-override-hints and add-override-hints). The first override-hint is evaluated, in the usual manner of evaluating computed hints but with the variable KEYWORD-ALIST bound to the above keyword-alist. That evaluation produces a result that should also be a keyword-alist, or else an error occurs. Any custom keyword hints are then eliminated from that keyword-alist. The resulting keyword-alist must not contain the :ERROR hint keyword and must not start with the :COMPUTED-HINT-REPLACEMENT keyword; otherwise an error occurs. With KEYWORD-ALIST bound to this result, the second override-hint is similarly evaluated. This process continues, and the keyword-alist returned by the final override-hint is the one used when processing the goal at hand. Except: If that keyword-alist is nil, then the next hint among the pending hints is tentatively selected and the process repeats, applying each override hint to that new tentative selection. Of course we might obtain nil again, in which case we tentatively select the next pending hint; and so on. If finally no hint is selected for the current goal, then KEYWORD-ALIST is bound to nil and the override-hints are applied as described above. But note that this final step is skipped if hint selection is being performed because stable-under-simplificationp has just become true, rather than at the top of the waterfall. (Otherwise the override-hints could easily keep firing uselessly yet putting us back at the top of the waterfall, with no change to the given goal, resulting in an infinite loop.) As mentioned above, the :COMPUTED-HINT-REPLACEMENT keyword is illegal for the value of an override-hint. But a selected hint may be a computed hint that evaluates to a keyword-alist beginning with prefix :COMPUTED-HINT-REPLACEMENT val. What value does ACL2 return for such a computed hint in the presence of override-hints? First, this prefix is stripped off before passing the resulting keyword-alist to the override-hints as described above. If the result of applying override-hints to that keyword-alist is not nil, then the prefix is put back on the front of that resulting keyword-alist after doing internal processing of the hint, including expansion of any custom keyword hints. Otherwise, the application of override-hints to the computed hint is nil, so this hint is not selected after all. *WARNING*: Unlike ordinary computed hints, a value of nil for an override-hint is not ignored. That is: When an ordinary computed hint evaluates to nil, it is deemed not to apply, and the next available hint is consulted. But when an override-hint is evaluated, the result is always supplied for the next binding of the variable KEYWORD-ALIST, even if that result is nil. If you want an override-hint to be a no-op, return as the expression the variable KEYWORD-ALIST rather than an expression that evaluates to nil. This feature can be used in order to implement a form of additive hints. Suppose for example that you want a hint that turns off generalization. A simple but inadequate solution is: (add-default-hints '((quote (:do-not '(generalize))))) The problem is that if there is any explicit hint supplied for a given goal, then it will be the one selected, and the above will be ignored. But suppose that the explicit hint supplied is of the form ("Subgoal x.y" :do-not '(fertilize)). What we would really want in this case is to generate the hint for the indicated subgoal that binds :do-not to a list indicating that both fertilization _and_ generalization are disabled for that goal. A solution is to merge, for example as follows. (The use of prog2$ and cw is of course optional, included here to provide debug printing.) (add-override-hints '((let* ((tmp (assoc-keyword :do-not KEYWORD-ALIST)) (new-keyword-alist (cond (tmp (list* :do-not `(cons 'generalize ,(cadr tmp)) (remove-keyword :do-not KEYWORD-ALIST))) (t (list* :do-not ''(generalize) KEYWORD-ALIST))))) (prog2$ (cw "New: ~x0~|" new-keyword-alist) new-keyword-alist)))) REMARKS (1) The utilities add-override-hints, add-override-hints!, set-override-hints, set-override-hints!, remove-override-hints, and remove-override-hints! are also available, in complete analogy to their default-hints versions. (2) The community book hints/basic-tests.lisp illustrates the use of override-hints and illuminates a number of corner cases; search in that file for "Test override-hints." (3) The community book hints/merge-hint.lisp provides support for merging hints that might be useful for writers of override-hint expressions (see the examples at the end of that file). (4) Override-hints are used in the processing of :BACKTRACK hints (see *note HINTS::).  File: acl2-doc-emacs.info, Node: P!, Next: PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS, Prev: OVERRIDE-HINTS, Up: MISCELLANEOUS P! to pop up (at least) one level of ACL2's command loop Logically speaking, (p!) = nil. If you are already at the top level of the ACL2 command loop, rather than being in a subsidiary call of ld, then the keyword then a call of (p!) returns nil and has no other effect. Otherwise, (p!) is evaluated inside a call of ld that was made inside ACL2's command loop. In that case, the current computation is aborted and treating as causing an error, and control returns to the superior call of ld. Here is a more detailed description of the effect of (p!) when not at the top level of the ACL2 command loop. The current call of LD is treated as though ld-error-action is :RETURN! (the default) and hence immediately returns control to the superior call of ld. If all calls of ld were made with the default ld-error-action of :RETURN!, then all superior calls of ld will then complete until you are back at top level of the ACL2 loop. For more information, see *note LD-ERROR-ACTION::. If you are at an ACL2 prompt (as opposed to a raw Lisp break), then you may type :p! in place of (p!); see *note KEYWORD-COMMANDS::.  File: acl2-doc-emacs.info, Node: PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS, Next: PARALLEL, Prev: P!, Up: MISCELLANEOUS PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS re-defining undone defpkgs Suppose (defpkg "pkg" imports) is the most recently executed successful definition of "pkg" in this ACL2 session and that it has since been undone, as by :ubt. Any future attempt in this session to define "pkg" as a package must specify an identical imports list. The restriction stems from the need to implement the reinstallation of saved logical worlds as in error recovery and the :oops command. Suppose that the new defpkg attempts to import some symbol, a::sym, not imported by the previous definition of "pkg". Because it was not imported in the original package, the symbol pkg::sym, different from a::sym, may well have been created and may well be used in some saved worlds. Those saved worlds are Common Lisp objects being held for you "behind the scenes." In order to import a::sym into "pkg" now we would have to unintern pkg::sym, rendering those saved worlds ill-formed. It is because of saved worlds that we do not actually clear out a package when it is undone. At one point we thought it was sound to allow the new defpkg to import a subset of the old. But that is incorrect. Suppose the old definition of "pkg" imported a::sym but the new one does not. Suppose we allowed that and implemented it simply by setting the imports of "pkg" to the new subset. Then consider the conjecture (eq a::sym pkg::sym). This ought not be a theorem because we did not import a::sym into "pkg". But in fact in AKCL it is a theorem because pkg::sym is read as a::sym because of the old imports.  File: acl2-doc-emacs.info, Node: PARALLEL, Next: PARALLELISM-BUILD, Prev: PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS, Up: MISCELLANEOUS PARALLEL evaluating forms in parallel See *note PARALLELISM::.  File: acl2-doc-emacs.info, Node: PARALLELISM-BUILD, Next: PRINT-DOC-START-COLUMN, Prev: PARALLEL, Up: MISCELLANEOUS PARALLELISM-BUILD building an ACL2 executable with parallel execution enabled See *note COMPILING-ACL2P::.  File: acl2-doc-emacs.info, Node: PRINT-DOC-START-COLUMN, Next: PROMPT, Prev: PARALLELISM-BUILD, Up: MISCELLANEOUS PRINT-DOC-START-COLUMN printing the one-liner Examples: (assign print-doc-start-column nil) (assign print-doc-start-column 17) This state global variable controls the column in which the "one-liner" of a formatted documentation string is printed. Generally, when :doc is used to print a documentation string, the name of the documented concept is printed and then :doc tabs over to print-doc-start-column and prints the one-liner. If the name extends past the desired column, :doc outputs a carriage return and then tabs over to the column. If print-doc-start-column is nil, :doc just starts the one-liner two spaces from the end of the name, on the same line. The initial value of print-doc-start-column is 15.  File: acl2-doc-emacs.info, Node: PROMPT, Next: PROOF-OF-WELL-FOUNDEDNESS, Prev: PRINT-DOC-START-COLUMN, Up: MISCELLANEOUS PROMPT the prompt printed by ld The prompt printed by ACL2 conveys information about various "modes." See *note DEFAULT-PRINT-PROMPT:: and see *note LD-PROMPT:: for details. The prompt during raw Lisp breaks is, with most Common Lisp implementations, adjusted by ACL2 to include the string "[RAW LISP"], in order to reminder users not to submit ACL2 forms there; see *note BREAKS::. For Lisps that seem to use the same code for printing prompts at the top-level as in breaks, the top-level prompt is similarly adjusted. For Lisps with the above prompt adjustment, The following forms may be executed in raw Lisp (i.e., after typing :q). (install-new-raw-prompt) ; install prompt with [RAW LISP] as described above (install-old-raw-prompt) ; revert to original prompt from host Common Lisp  File: acl2-doc-emacs.info, Node: PROOF-OF-WELL-FOUNDEDNESS, Next: PROOF-SUPPORTERS-ALIST, Prev: PROMPT, Up: MISCELLANEOUS PROOF-OF-WELL-FOUNDEDNESS a proof that o< is well-founded on o-ps The soundness of ACL2 rests in part on the well-foundedness of o< on o-ps. This can be taken as obvious if one is willing to grant that those concepts are simply encodings of the standard mathematical notions of the ordinals below epsilon-0 and its natural ordering relation. But it is possible to prove that o< is well-founded on o-ps without having to assert any connection to the ordinals and that is what we do here. The community book books/ordinals/proof-of-well-foundedness carries out the proof outlined below in ACL2, using only that the natural numbers are well-founded. Before outlining the above mentioned proof, we note that in the analogous documentation page of ACL2 Version_2.7, there is a proof of the well-foundedness of e0-ord-< on e0-ordinalps, the less-than relation and recognizer for the old ordinals (that is, for the ordinals appearing in ACL2 up through that version). Manolios and Vroon have given a proof in ACL2 Version_2.7 that the current ordinals (based on o< and o-p) are order-isomorphic to the old ordinals (based on e0-ord-< and e0-ordinalp). Their proof establishes that switching from the old ordinals to the current ordinals preserves the soundness of ACL2. For details see their paper: Manolios, Panagiotis & Vroon, Daron. Ordinal arithmetic in ACL2. Kaufmann, Matt, & Moore, J Strother (eds). Fourth International Workshop on the ACL2 Theorem Prover and Its Applications (ACL2-2003), July, 2003. See `http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/'. We now give an outline of the above mentioned proof of well-foundedness. We first observe three facts about o< on ordinals that have been proved by ACL2 using only structural induction on lists. These theorems can be proved by hand. (defthm transitivity-of-o< (implies (and (o< x y) (o< y z)) (o< x z)) :rule-classes nil) (defthm non-circularity-of-o< (implies (o< x y) (not (o< y x))) :rule-classes nil) (defthm trichotomy-of-o< (implies (and (o-p x) (o-p y)) (or (equal x y) (o< x y) (o< y x))) :rule-classes nil) These three properties establish that o< orders the o-ps. To put such a statement in the most standard mathematical nomenclature, we can define the macro: (defmacro o<= (x y) `(not (o< ,y ,x))) and then establish that o<= is a relation that is a simple, complete (i.e., total) order on ordinals by the following three lemmas, which have been proved: (defthm antisymmetry-of-o<= (implies (and (o-p x) (o-p y) (o<= x y) (o<= y x)) (equal x y)) :rule-classes nil :hints (("Goal" :use non-circularity-of-o<))) (defthm transitivity-of-o<= (implies (and (o-p x) (o-p y) (o<= x y) (o<= y z)) (o<= x z)) :rule-classes nil :hints (("Goal" :use transitivity-of-o<))) (defthm trichotomy-of-o<= (implies (and (o-p x) (o-p y)) (or (o<= x y) (o<= y x))) :rule-classes nil :hints (("Goal" :use trichotomy-of-o<))) Crucially important to the proof of the well-foundedness of o< on o-ps is the concept of ordinal-depth, abbreviated od: (defun od (l) (if (o-finp l) 0 (1+ (od (o-first-expt l))))) If the od of an o-p x is smaller than that of an o-p y, then x is o< y: (defun od-1 (x y) (if (o-finp x) (list x y) (od-1 (o-first-expt x) (o-first-expt y)))) (defthm od-implies-ordlessp (implies (and (o-p x) (< (od x) (od y))) (o< x y)) :hints (("Goal" :induct (od-1 x y)))) Remark. A consequence of this lemma is the fact that if s = s(1), s(2), ... is an infinite, o< descending sequence of o-ps, then od(s(1)), od(s(2)), ... is a "weakly" descending sequence of non-negative integers: od(s(i)) is greater than or equal to od(s(i+1)). _Lemma Main._ For each non-negative integer n, o< well-orders the set of o-ps with od less than or equal to n . Base Case. n = 0. The o-ps with 0 od are the non-negative integers. On the non-negative integers, o< is the same as <. Induction Step. n > 0. We assume that o< well-orders the o-ps with od less than n. If o< does not well-order the o-ps with od less than or equal to n, consider, D, the set of infinite, o< descending sequences of o-ps of od less than or equal to n. The first element of a sequence in D has od n. Therefore, the o-first-expt of the first element of a sequence in D has od n-1. Since o<, by IH, well-orders the o-ps with od less than n, the set of o-first-expts of first elements of the sequences in D has a minimal element, which we denote by B and which has od of n-1. Let k be the minimum integer such that for some infinite, o< descending sequence s of o-ps with od less than or equal to n, the first element of s has an o-first-expt of B and an o-first-coeff of k. Notice that k is positive. Having fixed B and k, let s = s(1), s(2), ... be an infinite, o< descending sequence of o-ps with od less than or equal to n such that s(1) has a o-first-expt of B and an o-first-coeff of k. We show that each s(i) has a o-first-expt of B and an o-first-coeff of k. For suppose that s(j) is the first member of s either with o-first-expt B and o-first-coeff m (m neq k) or with o-first-expt B' and o-first-coeff B' (B' neq B). If (o-first-expt s(j)) = B', then B' has od n-1 (otherwise, by IH, s would not be infinite) and B' is o< B, contradicting the minimality of B. If 0 < m < k, then the fact that the sequence beginning at s(j) is infinitely descending contradicts the minimality of k. If m > k, then s(j) is greater than its predecessor; but this contradicts the fact that s is descending. Thus, by the definition of o<, for s to be a decreasing sequence of o-ps, (o-rst s(1)), (o-rst s(2)), ... must be a decreasing sequence. We end by showing this cannot be the case. Let t = t(1), t(2), ... be an infinite sequence of o-ps such that t(i) = (o-rst s(i)). Then t is infinitely descending. Furthermore, t(1) begins with an o-p B' that is o< B. Since t is in D, t(1) has od n, therefore, B' has od n-1. But this contradicts the minimality of B. Q.E.D. Theorem. o< well-orders the o-ps. Proof. Every infinite, o< descending sequence of o-ps has the property that each member has od less than or equal to the od, n, of the first member of the sequence. This contradicts Lemma Main. Q.E.D.  File: acl2-doc-emacs.info, Node: PROOF-SUPPORTERS-ALIST, Next: REDEF, Prev: PROOF-OF-WELL-FOUNDEDNESS, Up: MISCELLANEOUS PROOF-SUPPORTERS-ALIST See *note DEAD-EVENTS::.  File: acl2-doc-emacs.info, Node: REDEF, Next: REDEF!, Prev: PROOF-SUPPORTERS-ALIST, Up: MISCELLANEOUS REDEF a common way to set ld-redefinition-action Example and General Form: ACL2 !>:redef This command sets ld-redefinition-action to '(:query . :overwrite). This command allows redefinition of functions and other events without undoing, but with a query that requires the user to acknowledge that the redefinition is intentional. To avoid that query, see *note REDEF!:: or for more options, see *note LD-REDEFINITION-ACTION::.  File: acl2-doc-emacs.info, Node: REDEF!, Next: REDEF+, Prev: REDEF, Up: MISCELLANEOUS REDEF! a common way to set ld-redefinition-action Example and General Form: ACL2 !>:redef! This command sets ld-redefinition-action to '(:warn! . :overwrite). This command allows redefinition of functions and other events without undoing, but with a warning. The command :redef is similar, but queries the user first before doing the redefinition. For more related options, see *note LD-REDEFINITION-ACTION::.  File: acl2-doc-emacs.info, Node: REDEF+, Next: REDEF-, Prev: REDEF!, Up: MISCELLANEOUS REDEF+ system hacker's redefinition command Example and General Form: ACL2 !>:redef+ ACL2 p!> This command is intended only for system hackers, not typical users. It sets ld-redefinition-action to '(:warn! . :overwrite), sets the default defun-mode to :program, and invokes set-state-ok with value t. It also introduces (defttag :redef+), so that redefinition of system functions will be permitted; see *note DEFTTAG::. Finally, it removes as untouchable (see *note PUSH-UNTOUCHABLE::) all variables and functions. WARNING: If the form (redef+) is used in a book, then including the book can leave you in a state in which dangerous actions are allowed, specifically: redefinition, and access to functions and variables normally prohibited because they are untouchable. To avoid this problem, insert the form (redef-) into your book after (redef+). To see the code for redef+, evaluate :trans1 (redef+). This command is intended for those who are modifying ACL2 source code definitions. Thus, note that even system functions can be redefined with a mere warning. Be careful!  File: acl2-doc-emacs.info, Node: REDEF-, Next: REDEFINED-NAMES, Prev: REDEF+, Up: MISCELLANEOUS REDEF- turn off system hacker's redefinition command Example and General Form: ACL2 !>:redef- ACL2 p!> This macro, for system hackers only, is a convenient way to reverse the effects of :redef+. See *note REDEF+::. We do not guarantee that :redef- will restore everything one might expect to its state before the earlier :redef+. To see exactly what :redef- does, look at its code, for example by evaluating :trans1 (redef-)  File: acl2-doc-emacs.info, Node: REDEFINED-NAMES, Next: REDUNDANT-EVENTS, Prev: REDEF-, Up: MISCELLANEOUS REDEFINED-NAMES to collect the names that have been redefined Example and General Forms: (redefined-names state) This function collects names that have been redefined in the current ACL2 state. :Program mode functions that were reclassified to :logic functions are not collected, since such reclassification cannot imperil soundness because it is allowed only when the new and old definitions are identical. Thus, if (redefined-names state) returns nil then no unsafe definitions have been made, regardless of ld-redefinition-action. See *note LD-REDEFINITION-ACTION::. WARNING: This function does not report names that are not explicitly redefined in the current logical world. Consider for example: (encapsulate () (defun foo () t) (local (defun foo () nil)) (defthm foo-prop (equal (foo) nil) :rule-classes nil)) If you attempt to call certify-book in the resulting world, ACL2 will appropriately refuse to do the certification: ACL2 Error in (CERTIFY-BOOK "foo" ...): At least one command in the current ACL2 world was executed while the value of state global variable 'LD-REDEFINITION-ACTION was not nil: (DEFUN FOO NIL T) Certification is therefore not allowed in this world. You can use :ubt to undo back through this command; see :DOC ubt. However, since the local redefinition of foo is gone in the certification world, (redefined-names state) will return nil. (Technical aside, to be ignored except for those interested in implementation details: ACL2 inhibits certify-book in this example because the definition of foo is the value of (global-val 'redef-seen (w state)).)  File: acl2-doc-emacs.info, Node: REDUNDANT-EVENTS, Next: REORDER, Prev: REDEFINED-NAMES, Up: MISCELLANEOUS REDUNDANT-EVENTS allowing a name to be introduced "twice" Sometimes an event will announce that it is "redundant", meaning that the the form is not evaluated because ACL2 determines that its effect is already incorporated into the logical world. Thus, when this happens, no change to the logical world takes place. This feature permits two independent books, each of which defines some name, to be included sequentially provided they use exactly the same definition. Note that by the definition above, a form can have no effect on the logical world yet not be considered to be redundant. Here is an example of such a form. (value-triple (cw "Hello world.~%")) When are two logical-name definitions considered "the same"? It depends upon the kind of name being defined. We consider these below in alphabetical order. See also the Notes below. A defabsstobj is redundant if there is already an identical defabsstobj event in the logical world. A defattach event is never redundant. Note that it doesn't define any name. A defaxiom or defthm event is redundant if there is already an axiom or theorem of the given name and both the formula (after macroexpansion) and the rule-classes are syntactically identical. Note that a defaxiom can make a subsequent defthm redundant, and a defthm can make a subsequent defaxiom redundant as well. A defconst is redundant if the name is already defined either with a syntactically identical defconst event or one that defines it to have the same value. A defdoc event is never redundant because it doesn't define any name. A deflabel event is never redundant. This means that if you have a deflabel in a book and that book has been included (without error), then references to that label denote the point in history at which the book introduced the label. See the note about shifting logical names, below. A defmacro event is redundant if there is already a macro defined with the same name and syntactically identical arguments, guard, and body. A defpkg event is redundant if a package of the same name with exactly the same imports has been defined. A defstobj event is redundant if there is already a defstobj event with the same name that has exactly the same field descriptors (see *note DEFSTOBJ::), in the same order, and with the same :renaming value if :renaming is supplied for either event. A defthm event is redundant according to the criterion given above in the discussion of defaxiom. A deftheory is never redundant. The "natural" notion of equivalent deftheory forms is that the names and values of the two theory expressions are the same. But since most theory expressions are sensitive to the context in which they occur, it seems unlikely to us that two deftheorys coming from two sequentially included books will ever have the same values. So we prohibit redundant theory definitions. If you try to define the same theory name twice, you will get a "name in use" error. A defun, defuns, or mutual-recursion event is redundant if for each function to be introduced, there has already been introduced a function with the same name, the same formals, and syntactically identical :guard, :measure, type declarations, stobjs declarations and body (before macroexpansion), and an appropriate mode (see the "Note About Appropriate Modes" below). Moreover, the order of the combined :guard and type declarations must be the same in both cases. Exceptions: (1) If either definition is declared :non-executable (see *note DEFUN-NX::), then the two events must be identical. (2) It is permissible for one definition to have a :guard of t and the other to have no explicit guard (hence, the guard is implicitly t). (3) The :measure check is avoided if the old definition is non-recursive (and not defined within a mutual-recursion) or we are skipping proofs (for example, during include-book). Otherwise, the new definition may have a :measure of (:? v1 ... vk), where (v1 ... vk) enumerates the variables occurring in the measure stored for the old definition. (4) If either the old or new event is a mutual-recursion event, then redundancy requires that both are mutual-recursion events that define the same set of function symbols. An encapsulate event is most commonly redundant when a syntactically identical encapsulate has already been executed under the same default-defun-mode, default-ruler-extenders, and default-verify-guards-eagerness. The full criterion for redundancy of encapsulate events is more complex, for example ignoring contents of local events; see *note REDUNDANT-ENCAPSULATE::. An in-theory event is never redundant. Note that it doesn't define any name. An include-book event is redundant if the book has already been included. A call of make-event is never redundant, as its argument is always evaluated to obtain the make-event expansion. However, that expansion may of course be redundant. A mutual-recursion event is redundant according to the criteria in the discussion above for the case of a defun event. A progn event is never redundant: its subsidiary events are always considered. Of course, its sub-events may themselves be redundant. If all of its sub-events are redundant -- or more generally, if none of the sub-events changes the logical world -- then the progn event also won't change the world. A push-untouchable event is redundant if every name supplied is already a member of the corresponding list of untouchable symbols. A regenerate-tau-database event is never redundant. Note that it doesn't define any name. A remove-untouchable event is redundant if no name supplied is a member of the corresponding list of untouchable symbols. A reset-prehistory event is redundant if it does not cause any change. A set-body event is redundant if the indicated body is already the current body. A table event not define any name. It is redundant when it sets the value already associated with a key of the table, or when it sets an entire table (using keyword :clear) to its existing value; see *note TABLE::. A verify-guards event is redundant if the function has already had its guards verified. _Note About Built-in (Predefined) Functions and Macros:_ Redundancy is restricted for built-in macros and functions that have special raw Lisp code. Such redundancy is only legal in the context of LOCAL. This restriction is needed for soundness, for technical reasons omitted here (details may be found in a long comment about redundant-events in source function chk-acceptable-defuns-redundancy). _Note About Appropriate Modes:_ Suppose a function is being redefined and that the formals, guards, types, stobjs, and bodies are identical. When are the modes (:program or :logic) "appropriate?" Identical modes are appropriate. But what if the old mode was :program and the new mode is :logic? This is appropriate, provided the definition meets the requirements of the logical definitional principle. That is, you may redefine "redundantly" a :program mode function as a :logic mode function provide the measure conjectures can be proved. This is what verify-termination does. Now consider the reverse style of redefinition. Suppose the function was defined in :logic mode and is being identically redefined in :program mode. ACL2 will treat the redefinition as redundant, provided the appropriate criteria are met (as though it were in :logic mode). _Note About Shifting Logical Names:_ Suppose a book defines a function fn and later uses fn as a logical name in a theory expression. Consider the value of that theory expression in two different sessions. In session A, the book is included in a world in which fn is not already defined, i.e., in a world in which the book's definition of fn is not redundant. In session B, the book is included in a world in which fn is already identically defined. In session B, the book's definition of fn is redundant. When fn is used as a logical name in a theory expression, it denotes the point in history at which fn was introduced. Observe that those points are different in the two sessions. Hence, it is likely that theory expressions involving fn will have different values in session A than in session B. This may adversely affect the user of your book. For example, suppose your book creates a theory via deftheory that is advertised just to contain the names generated by the book. But suppose you compute the theory as the very last event in the book using: (set-difference-theories (universal-theory :here) (universal-theory fn)) where fn is the very first event in the book and happens to be a defun event. This expression returns the advertised set if fn is not already defined when the book is included. But if fn were previously (identically) defined, the theory is larger than advertised. The moral of this is simple: when building books that other people will use, it is best to describe your theories in terms of logical names that will not shift around when the books are included. The best such names are those created by deflabel. _Note About Unfortunate Redundancies._ Notice that our syntactic criterion for redundancy of defun events may not allow redefinition to take effect unless there is a syntactic change in the definition. The following example shows how an attempt to redefine a function can fail to make any change. (set-ld-redefinition-action '(:warn . :overwrite) state) (defmacro mac (x) x) (defun foo (x) (mac x)) (defmacro mac (x) (list 'car x)) (set-ld-redefinition-action nil state) (defun foo (x) (mac x)) ; redundant, unfortunately; foo does not change (thm (equal (foo 3) 3)) ; succeeds, showing that redef of foo didn't happen The call of macro mac was expanded away before storing the first definition of foo for the theorem prover. Therefore, the new definition of mac does not affect the expansion of foo by the theorem prover, because the new definition of foo is ignored. One workaround is first to supply a different definition of foo, just before the last definition of foo above. Then that final definition will no longer be redundant. However, as a courtesy to users, we strengthen the redundancy check for function definitions when redefinition is active. If in the example above we remove the form (set-ld-redefinition-action nil state), then the problem goes away: (set-ld-redefinition-action '(:warn . :overwrite) state) (defmacro mac (x) x) (defun foo (x) (mac x)) (defmacro mac (x) (list 'car x)) (defun foo (x) (mac x)) ; no longer redundant (thm (equal (foo 3) 3)) ; fails, as we would like To summarize: If a defun form is submitted that meets the usual redundancy criteria, then it may be considered redundant even if a macro called in the definition has since been redefined. The analogous problem applie to constants, i.e., symbols defined by defconst that occur in the definition body. However, if redefinition is currently active the problem goes away: that is, the redundancy check is strengthened to check the "translated" body, in which macro calls and constants defined by defconst are expanded away. The above discussion for defun forms applies to defconst forms as well. However, for defmacro forms ACL2 always checks translated bodies, so such bogus redundancy does not occur. Here is more complex example illustrating the limits of redefinition, based on one supplied by Grant Passmore. (defun n3 () 0) (defun n4 () 1) (defun n5 () (> (n3) (n4))) ; body is normalized to nil (thm (equal (n5) nil)) ; succeeds, trivially (set-ld-redefinition-action '(:warn . :overwrite) state) (defun n3 () 2) (thm (equal (n5) nil)) ; still succeeds, sadly We may expect the final thm call to fail because of the following reasoning: (n5) = (> (n3) (n4)) = (> 2 1) = t. Unfortunatly, the body of n5 was simplified ("normalized") to nil when n5 was admitted, so the redefinition of n3 is ignored during the final thm call. (Such normalization can be avoided; see the brief discussion of :normalize in the documentation for defun.) So, given this unfortunate situation, one might expect at this point simply to redefine n5 using the same definition as before, in order to pick up the new definition of n3. Such "redefinition" would, however, be redundant, for the same reason as in the previous example: no syntactic change was made to the definition. Even with redefinition active, there is no change in the body of n5, even with macros and constants (defined by defconst) expanded; there are none such! The same workaround applies as before: redefine n5 to be something different, and then redefine n5 again to be as desired. A related phenomenon can occur for encapsulate. As explained above, an encapsulate event is redundant if it is identical to one already in the database. (But a weaker condition applies in general; see *note REDUNDANT-ENCAPSULATE::.) Consider then the following contrived example. (defmacro mac (x) x) (encapsulate () (defun foo (x) (mac x))) (set-ld-redefinition-action '(:warn . :overwrite) state) (defmacro mac (x) (list 'car x)) (encapsulate () (defun foo (x) (mac x))) The last encapsulate event is redundant because it meets the criterion for redundancy: it is identical to the earlier encapsulate event. Even though redefinition is active, and hence ACL2 "should" be able to see that the new defun of foo is not truly redundant, nevertheless the criterion for redundancy of encapsulate allows the new encapsulate form to be redundant. A workaround can be to add something trivial to the encapsulate, for example: (encapsulate () (deflabel try2) ; ``Increment'' to try3 next time, and so on. (defun foo (x) x))  File: acl2-doc-emacs.info, Node: REORDER, Next: RESTRICT, Prev: REDUNDANT-EVENTS, Up: MISCELLANEOUS REORDER hints keyword :REORDER See *note HINTS::.  File: acl2-doc-emacs.info, Node: RESTRICT, Next: REWRITE-STACK-LIMIT, Prev: REORDER, Up: MISCELLANEOUS RESTRICT hints keyword :RESTRICT See *note HINTS::.  File: acl2-doc-emacs.info, Node: REWRITE-STACK-LIMIT, Next: SAVING-AND-RESTORING, Prev: RESTRICT, Up: MISCELLANEOUS REWRITE-STACK-LIMIT limiting the stack depth of the ACL2 rewriter ACL2 users can create rules of class :rewrite (see *note RULE-CLASSES::) that have the potential of causing an infinite loop in the ACL2 rewriter. This could lead to Lisp stack overflows and even segmentation faults. For this reason, the depth of certain calls of functions in the ACL2 rewriter is limited by default using the value of the form (rewrite-stack-limit (w state)). To see the limit in action, execute the following forms. (defthm app-assoc-1 (equal (append (append x y) z) (append x y z))) (defthm app-assoc-2 (equal (append x y z) (append (append x y) z))) (thm (equal (append a b c) xxx) ; try without these hints to see a slightly different error message :hints (("Goal" :do-not '(preprocess)))) The ensuing error message shows a stack of one greater than the value of (rewrite-stack-limit (w state)), which by default is the value of the constant *default-rewrite-stack-limit*. The error message also explains how to use :brr t and (cw-gstack) to find looping rewrite rules. This limit can be changed; see *note SET-REWRITE-STACK-LIMIT::. For a related limit, see *note BACKCHAIN-LIMIT::.  File: acl2-doc-emacs.info, Node: SAVING-AND-RESTORING, Next: SET-WORMHOLE-DATA, Prev: REWRITE-STACK-LIMIT, Up: MISCELLANEOUS SAVING-AND-RESTORING See *note SAVE-EXEC::.  File: acl2-doc-emacs.info, Node: SET-WORMHOLE-DATA, Next: SET-WORMHOLE-ENTRY-CODE, Prev: SAVING-AND-RESTORING, Up: MISCELLANEOUS SET-WORMHOLE-DATA sets the wormhole data object in a wormhole status object General Form: (set-wormhole-data whs data) See *note WORMHOLE::. Whs should be a well-formed wormhole status; data is arbitrary. This function returns a new status with the same entry code as whs but with the new data. It avoids unnecessary consing if the data for whs is already set to data. This function does not affect state or a wormhole's hidden status. It just returns a (possibly) new status object suitable as the value of the lambda expressions in wormhole-eval and wormhole.  File: acl2-doc-emacs.info, Node: SET-WORMHOLE-ENTRY-CODE, Next: SHOW-BODIES, Prev: SET-WORMHOLE-DATA, Up: MISCELLANEOUS SET-WORMHOLE-ENTRY-CODE sets the wormhole entry code in a wormhole status object General Form: (set-wormhole-entry-code whs code) See *note WORMHOLE::. Whs should be a well-formed wormhole status and code should be :ENTER or :SKIP. This function returns a new status with the specified entry code but the same data as whs. It avoids unnecessary consing if the entry code for whs is already set to code. This function does not affect state or a wormhole's hidden status. It just returns a (possibly) new status object suitable as the value of the lambda expressions in wormhole-eval and wormhole.  File: acl2-doc-emacs.info, Node: SHOW-BODIES, Next: SIGNATURE, Prev: SET-WORMHOLE-ENTRY-CODE, Up: MISCELLANEOUS SHOW-BODIES show the potential definition bodies Examples: (show-bodies foo) :show-bodies foo A definition made using defun installs a so-called "body" of a function symbol, as do certain :definition rules. Such bodies are used in a number of ways, including the application of :expand hints; see *note DEFINITION::, in particular the discussion of "body" there, and see *note HINTS:: for a discussion of the :expand hint. Also see *note SET-BODY:: for how to change which of the available definitions (among the original definition and appropriate :definition rules) is the one that provides the body. The show-bodies command displays the available such bodies in an appropriate format, starting with the one that is currently used as the body. General Forms: (show-bodies function-symbol) :show-bodies function-symbol  File: acl2-doc-emacs.info, Node: SIGNATURE, Next: SIMPLE, Prev: SHOW-BODIES, Up: MISCELLANEOUS SIGNATURE how to specify the arity of a constrained function We start with a gentle introduction to signatures, where we pretend that there are no single-threaded objects (more on that below -- for now, if you don't know anything about single-threaded objects, that's fine!). Here are some simple examples of signatures. ((hd *) => *) ((pair * *) => *) ((foo * *) => (mv * * *)) The first of these says that hd is a function of one argument, while the other two say that pair and foo are functions that each take two arguments. The first two say that hd and pair return a single value. The third says that foo returns three values, much as the following definition returns three values: (defun bar (x y) (mv y x (cons x y))) Corresponding "old-style" signatures are as follows. In each case, a function symbol is followed by a list of formal parameters and then either t, to denote a single value return, or (mv t t t), to denote a multiple value return (in this case, returning three values). (hd (x) t) (pair (x y) t) (foo (x y) (mv t t t)) That concludes our gentle introduction. The documentation below is more general, for example covering single-threaded objects and keyword values such as :guard. When reading what follows below, it is sufficient to know about single-threaded objects (or "stobjs") that each has a unique symbolic name and that state is the name of the only built-in single-threaded object. All other stobjs are introduced by the user via defstobj or defabsstobj. An object that is not a single-threaded object is said to be "ordinary." For a discussion of single-threaded objects, see *note STOBJ::. Examples: ((hd *) => *) ((hd *) => * :formals (x) :guard (consp x)) ((printer * state) => (mv * * state)) ((mach * mach-state * state) => (mv * mach-state)) General Form: ((fn ...) => *) ((fn ...) => stobj) or ((fn ...) => (mv ...)) or for part1 and part2 as above, (part1 => part2 :kwd1 val1 ... :kwdn valn) where fn is the constrained function symbol, ... is a list of asterisks and/or the names of single-threaded objects, stobj is a single-threaded object name, and the optional :kwdi and :vali are as described below. ACL2 also supports an older style of signature, described below after we describe the preferred style. Signatures specify three syntactic aspects of a function symbol: (1) the "arity" or how many arguments the function takes, (2) the "multiplicity" or how many results it returns via MV, and (3) which of those arguments and results are single-threaded objects and which objects they are. A signature typically has the form ((fn x1 ... xn) => val). Such a signature has two parts, separated by the symbol "=>". The first part, (fn x1 ... xn), is suggestive of a call of the constrained function. The number of "arguments," n, indicates the arity of fn. Each xi must be a symbol. If a given xi is the symbol "*" then the corresponding argument must be ordinary. If a given xi is any other symbol, that symbol must be the name of a single-threaded object and the corresponding argument must be that object. No stobj name may occur twice among the xi. The second part, val, of a signature is suggestive of a term and indicates the "shape" of the output of fn. If val is a symbol then it must be either the symbol "*" or the name of a single-threaded object. In either case, the multiplicity of fn is 1 and val indicates whether the result is ordinary or a stobj. Otherwise, val is of the form (mv y1 ... yk), where k > 1. Each yi must be either the symbol "*" or the name of a stobj. Such a val indicates that fn has multiplicity k and the yi indicate which results are ordinary and which are stobjs. No stobj name may occur twice among the yi, and a stobj name may appear in val only if appears among the xi. A signature may have the form ((fn x1 ... xn) => val . k), where k is a keyword-value-listp, i.e., an alternating list of keywords and values starting with a keyword. In this case ((fn x1 ... xn) => val) must be a legal signature as described above. The legal keywords in k are :GUARD and :FORMALS (except that for ACL2(r), also see the remark about :CLASSICALP later in this topic). The value following :FORMALS is to be the list of formal parameters of fn, while the value following :GUARD is a term that is to be the guard of fn. Note that this guard is never actually evaluated, and is not subject to the guard verification performed on functions introduced by defun (see *note VERIFY-GUARDS::). Said differently: this guard need not itself have a guard of t. Indeed, the guard is only used for attachments; see *note DEFATTACH::. Note that if :GUARD is supplied then :FORMALS must also be supplied (in order to related the variables occurring in the guard to the parameters of fn). One final observation about guards: if the :GUARD keyword is omitted, then the guard defaults to T. Before ACL2 supported user-declared single-threaded objects there was only one single-threaded object: ACL2's built-in notion of state. The notion of signature supported then gave a special role to the symbol state and all other symbols were considered to denote ordinary objects. ACL2 still supports the old form of signature, but it is limited to functions that operate on ordinary objects or ordinary objects and state. Old-Style General Form: (fn formals result . k) where fn is the constrained function symbol, formals is a suitable list of formal parameters for it, k is an optional keyword-value-listp (see below), and result is either a symbol denoting that the function returns one result or else result is an mv expression, (mv s1 ... sn), where n>1, each si is a symbol, indicating that the function returns n results. At most one of the formals may be the symbol STATE, indicating that corresponding argument must be ACL2's built-in state. If state appears in formals then state may appear once in result. All "variable symbols" other than state in old style signatures denote ordinary objects, regardless of whether the symbol has been defined to be a single-threaded object name! The optional k is as described above for newer-style signatures, except that the user is also allowed to declare which symbols (besides state) are to be considered single-threaded object names. Thus :STOBJS is also a legal keyword. The form (fn formals result ... :stobjs names ...) specifies that names is either the name of a single-threaded object or else is a list of such names. Every name in names must have been previously defined as a stobj via defstobj or defabsstobj. As promised above, we conclude with a remark about an additional keyword, :CLASSICALP, that is legal for ACL2(r) (see *note REAL::). The value of this keyword must be t (the default) or nil, indicating respectively whether fn is classical or not.  File: acl2-doc-emacs.info, Node: SIMPLE, Next: SPECIOUS-SIMPLIFICATION, Prev: SIGNATURE, Up: MISCELLANEOUS SIMPLE :definition and :rewrite rules used in preprocessing Example of simple rewrite rule: (equal (car (cons x y)) x) Examples of simple definition: (defun file-clock-p (x) (integerp x)) (defun naturalp (x) (and (integerp x) (>= x 0))) The theorem prover output sometimes refers to "simple" definitions and rewrite rules. These rules can be used by the preprocessor, which is one of the theorem prover's "processes" understood by the :do-not hint; see *note HINTS::. The preprocessor expands certain definitions and uses certain rewrite rules that it considers to be "fast". There are two ways to qualify as fast. One is to be an "abbreviation", where a rewrite rule with no hypotheses or loop stopper is an "abbreviation" if the right side contains no more variable occurrences than the left side, and the right side does not call the functions if, not or implies. Definitions and rewrite rules can both be abbreviations; the criterion for definitions is similar, except that the definition must not be recursive. The other way to qualify applies only to a non-recursive definition, and applies when its body is a disjunction or conjunction, according to a perhaps subtle criterion that is intended to avoid case splits.  File: acl2-doc-emacs.info, Node: SPECIOUS-SIMPLIFICATION, Next: SPLITTER, Prev: SIMPLE, Up: MISCELLANEOUS SPECIOUS-SIMPLIFICATION nonproductive proof steps Occasionally the ACL2 theorem prover reports that the current goal simplifies to itself or to a set including itself. Such simplifications are said to be "specious" and are ignored in the sense that the theorem prover acts as though no simplification were possible and tries the next available proof technique. Specious simplifications are almost always caused by forcing. The simplification of a formula proceeds primarily by the local application of :rewrite, :type-prescription, and other rules to its various subterms. If no rewrite rules apply, the formula cannot be simplified and is passed to the next ACL2 proof technique, which is generally the elimination of destructors. The experienced ACL2 user pays special attention to such "maximally simplified" formulas; the presence of unexpected terms in them indicates the need for additional rules or the presence of some conflict that prevents existing rules from working harmoniously together. However, consider the following interesting possibility: local rewrite rules apply but, when applied, reproduce the goal as one of its own subgoals. How can rewrite rules apply and reproduce the goal? Of course, one way is for one rule application to undo the effect of another, as when commutativity is applied twice in succession to the same term. Another kind of example is when two rules conflict and undermine each other. For example, under suitable hypotheses, (length x) might be rewritten to (+ 1 (length (cdr x))) by the :definition of length and then a :rewrite rule might be used to "fold" that back to (length x). Generally speaking the presence of such "looping" rewrite rules causes ACL2's simplifier either to stop gracefully because of heuristics such as that described in the documentation for loop-stopper or to cause a stack overflow because of indefinite recursion. A more insidious kind of loop can be imagined: two rewrites in different parts of the formula undo each other's effects "at a distance," that is, without ever being applied to one another's output. For example, perhaps the first hypothesis of the formula is simplified to the second, but then the second is simplified to the first, so that the end result is a formula propositionally equivalent to the original one but with the two hypotheses commuted. This is thought to be impossible unless forcing or case-splitting occurs, but if those features are exploited (see *note FORCE:: and see *note CASE-SPLIT::) it can be made to happen relatively easily. Here is a simple example. Declare foo to be a function of one argument returning one result: (defstub p1 (x) t) Prove the following silly rule: (defthm bad (implies (case-split (p1 x)) (p1 x))) Now suppose we try the following. (thm (p1 x)) The rewrite rule bad will rewrite (p1 x) to t, but it will be unable to prove the hypothesis (case-split (p1 x)). As a result, the prover will spawn a new goal, to prove (p1 x). However, since this new goal is the same as the original goal, ACL2 will recognize the simplification as _specious_ and consider the attempted simplification to have failed. In other words, despite the rewriting, no progress was made! In more common cases, the original goal may simplify to a set of subgoals, one of which includes the original goal. If ACL2 were to adopt the new set of subgoals, it would loop indefinitely. Therefore, it checks whether the input goal is a member of the output subgoals. If so, it announces that the simplification is "specious" and pretends that no simplification occurred. "Maximally simplified" formulas that produce specious simplifications are maximally simplified in a very technical sense: were ACL2 to apply every applicable rule to them, no progress would be made. Since ACL2 can only apply every applicable rule, it cannot make further progress with the formula. But the informed user can perhaps identify some rule that should not be applied and make it inapplicable by disabling it, allowing the simplifier to apply all the others and thus make progress. When specious simplifications are a problem it might be helpful to disable all forcing (including case-splits) and resubmit the formula to observe whether forcing is involved in the loop or not. See *note FORCE::. The commands ACL2 !>:disable-forcing and ACL2 !>:enable-forcing disable and enable the pragmatic effects of both force and case-split. If the loop is broken when forcing is disabled, then it is very likely some forced hypothesis of some rule is "undoing" a prior simplification. The most common cause of this is when we force a hypothesis that is actually false but whose falsity is somehow temporarily hidden (more below). To find the offending rule, compare the specious simplification with its non-specious counterpart and look for rules that were speciously applied that are not applied in the non-specious case. Most likely you will find at least one such rule and it will have a forced hypothesis. By disabling that rule, at least for the subgoal in question, you may allow the simplifier to make progress on the subgoal. acl2-sources/doc/EMACS/acl2-doc-emacs.info-70000664002132200015000000110774612222333541017660 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: SPLITTER, Next: SPLITTER-OUTPUT, Prev: SPECIOUS-SIMPLIFICATION, Up: MISCELLANEOUS SPLITTER reporting of rules whose application may have caused case splits The application of a rule to a term may cause a goal to simplify to more than one subgoal. A rule with such an application is called a "splitter". Here, we explain the output produced for splitters when proof output is enabled (see *note SET-INHIBIT-OUTPUT-LST::) and such reporting is turned on (as it is by default) -- that is, when the value of (splitter-output) is true. See *note SET-SPLITTER-OUTPUT:: for how to turn off, or on, the reporting of splitters. Also see *note SET-CASE-SPLIT-LIMITATIONS:: for information on how to control case splits. We begin by describing three types of splitters. if-intro: The rule application may have introduced a call of IF, in the sense discussed at the end below. case-split: For the application of a rule with hypothesis of the form (case-split ), did not simplify to true or false. immed-forced: For the application of a rule with hypothesis of the form (force ), did not simplify to true or false, where immediate-force-modep is enabled (see *note IMMEDIATE-FORCE-MODEP::). These three annotations -- if-intro, case-split, and immed-forced -- may be used in proof output and summaries for describing rule applications, as discussed below. A fourth annotation, forced, maybe also be used in proof output to indicate the application of a rule with hypothesis of the form (force ) when did not simplify to true or false, where immediate-force-modep is disabled (see *note IMMEDIATE-FORCE-MODEP::). We don't consider such uses of force to be splitters, because they do not cause case splits (though they do produce goals to prove after lower-case "q.e.d." is printed); see *note FORCE::. There are three kinds of output affected by splitters, illustrated in turn below using examples. (a) During the proof, gag-mode off (b) During the proof, gag-mode on (c) Summary Of course, (a) and (b) are skipped if proof output is inhibited, which (c) is skipped if summary output is inhibited; see *note SET-INHIBIT-OUTPUT-LST::. (a) During the proof, gag-mode off With gag-mode off (or when using :pso, :psof, or :psog) one normally gets an English commentary. The following output indicates that at least one application of each rule F and G is of type if-intro, at least one application of rules G and R1 are of type case-split, and at least one application of rule R3 is of type immed-forced. If immediate-force-modep is off then "immed-forced" would be replaced by "forced". This simplifies, using the :definitions F (if-intro), G (case-split and if-intro) and H and the :rewrite rules R1, R2 (case-split), and R3 (immed-forced), to the following two conjectures. Note that any such printing of "forced" is done even if (splitter-output) is false. Such forcing indication is also made when raw proof format is used -- see *note SET-RAW-PROOF-FORMAT:: -- but in that case, no indication is made for splitters in the proof output. (b) During the proof, gag-mode on With gag-mode on the proof output is greatly abbreviated. However, "Splitter Notes" are printed so that even with gag-mode on, one can get important information to help control large case splits, by disabling splitter rules as appropriate. These are printed at the point when a goal splits into subgoals. Here, for example, is the Splitter Note that corresponds to the output shown in (a) above. It shows the goal whose simplification has produced a split into more than one subgoal, and it shows how many subgoals have been created. Splitter note (see :DOC splitter) for Subgoal *1/2.2.1' (2 subgoals). case-split: ((:DEFINITION G) (:REWRITE R2)) immed-forced: ((:REWRITE R3)) if-intro: ((:DEFINITION G) (:DEFINITION F)) No such splitter notes are printed for the use of force (when immediate-force-modep is off). (c) Summary Here is a possible summary corresponding to our running example. In the summary, "Splitter rules" is omitted if there are no splitter rules, and a splitter type is only mentioned if there is at least one corresponding splitter rule. Summary Form: ( THM ...) Rules: ((:DEFINITION F) (:DEFINITION G) (:DEFINITION H) (:REWRITE R1) (:REWRITE R2) (:REWRITE R3)) Splitter rules (see :DOC splitter): case-split: ((:DEFINITION G) (:REWRITE R2)) immed-forced: ((:REWRITE R3)) if-intro: ((:DEFINITION G) (:DEFINITION F)) Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.00) Prover steps counted: 145 No indication for "forced" is given for "Splitter rules". (As discussed earlier above, the forced hypotheses are not considered relevant for determining splitter rule applications unless immediate-force-modep is on.) We conclude by giving the criteria for a rewrite or definition rule application to be a splitter of type if-intro. o Reporting of splitter rules is on, i.e., the value of (splitter-output) is true. o At least two subgoals are created, even before considering subgoals generated by hypotheses that are calls of case-split or force. o The term to which the rule is applied is at the top level, rather than being encountered when trying to establish the hypothesis of a rule. o The rule is a rewrite rule, a definition rule, or a meta rule. o There is a call of the function symbol IF in the right-hand side of the rewrite rule; or, in the case of a definition rule, in the body of the definition; or, in the case of a meta rule, in the result of applying the metafunction. o There is a call of the function symbol IF in the result of rewriting: the right-hand side (for a rewrite rule), the definition body (for a definition rule), or the metafunction application (for a meta rule). Any rule application meeting the above criteria will be considered a splitter of type if-intro, even if the call does not actually cause a case split. For example, if you are proving (implies (hyp x) (conc x)) and rule R rewrites (hyp x) to (if (h1 x) (h2 x) nil), which is really the term (and (h1 x) (h2 x)), then R may be labelled as a splitter rule. If you want to find the causes of case-splitting, the list of if-intro splitters can help you narrow your search, but may include irrelevant rules as well. Finally, note that you may see splits not attributed to splitters. We believe that this will be uncommon during simplification, though it can occur for example when a call of IF is in the body of a LET expression, i.e., in a call of a LAMBDA expression. But splits caused by other processes, notably destructor elimination (see *note ELIM::), will typically not be attributed to splitters.  File: acl2-doc-emacs.info, Node: SPLITTER-OUTPUT, Next: STOBJS, Prev: SPLITTER, Up: MISCELLANEOUS SPLITTER-OUTPUT status for reporting of splitter rules See *note SPLITTER:: for a discussion of splitter rules. See *note SET-SPLITTER-OUTPUT:: for how to turn off, or on, the reporting of splitter rules. When splitter-output is off, because either prove output is inhibited (see *note SET-INHIBIT-OUTPUT-LST::) or (set-splitter-output nil) has been invoked, then the value of (splitter-output) is nil. Otherwise, such reporting is on and the value is non-nil.  File: acl2-doc-emacs.info, Node: STOBJS, Next: SUBVERSIVE-INDUCTIONS, Prev: SPLITTER-OUTPUT, Up: MISCELLANEOUS STOBJS xargs keyword :STOBJS See *note XARGS::.  File: acl2-doc-emacs.info, Node: SUBVERSIVE-INDUCTIONS, Next: SUBVERSIVE-RECURSIONS, Prev: STOBJS, Up: MISCELLANEOUS SUBVERSIVE-INDUCTIONS why we restrict encapsulated recursive functions See *note SUBVERSIVE-RECURSIONS::.  File: acl2-doc-emacs.info, Node: SUBVERSIVE-RECURSIONS, Next: SYNTAX, Prev: SUBVERSIVE-INDUCTIONS, Up: MISCELLANEOUS SUBVERSIVE-RECURSIONS why we restrict encapsulated recursive functions Subtleties arise when one of the "constrained" functions, f, introduced in the signature of an encapsulate event, is involved in the termination argument for a non-local recursively defined function, g, in that encapsulate. During the processing of the encapsulated events, f is locally defined to be some witness function, f'. Properties of f' are explicitly proved and exported from the encapsulate as the constraints on the undefined function f. But if f is used in a recursive g defined within the encapsulate, then the termination proof for g may use properties of f' -- the witness -- that are not explicitly set forth in the constraints stated for f. Such recursive g are said be "subversive" because if naively treated they give rise to unsound induction schemes or (via functional instantiation) recurrence equations that are impossible to satisfy. We illustrate what could go wrong below. Subversive recursions are not banned outright. Instead, they are treated as part of the constraint. That is, in the case above, the definitional equation for g becomes one of the constraints on f. This is generally a severe restriction on future functional instantiations of f. In addition, ACL2 removes from its knowledge of g any suggestions about legal inductions to "unwind" its recursion. What should you do? Often, the simplest response is to move the offending recursive definition, e.g., g, out of the encapsulate. That is, introduce f by constraint and then define g as an "independent" event. You may need to constrain "additional" properties of f in order to admit g, e.g., constrain it to reduce some ordinal measure. However, by separating the introduction of f from the admission of g you will clearly identify the necessary constraints on f, functional instantiations of f will be simpler, and g will be a useful function which suggests inductions to the theorem prover. Note that the functions introduced in the signature should not even occur ancestrally in the termination proofs for non-local recursive functions in the encapsulate. That is, the constrained functions of an encapsulate should not be reachable in the dependency graph of the functions used in the termination arguments of recursive functions in encapsulate. If they are reachable, their definitions become part of the constraints. The following event illustrates the problem posed by subversive recursions. (encapsulate (((f *) => *)) (local (defun f (x) (cdr x))) (defun g (x) (if (consp x) (not (g (f x))) t))) Suppose, contrary to how ACL2 works, that the encapsulate above were to introduce no constraints on f on the bogus grounds that the only use of f in the encapsulate is in an admissible function. We discuss the plausibility of this bogus argument in a moment. Then it would be possible to prove the theorem: (defthm f-not-identity (not (equal (f '(a . b)) '(a . b))) :rule-classes nil :hints (("Goal" :use (:instance g (x '(a . b)))))) simply by observing that if (f '(a . b)) were '(a . b), then (g '(a . b)) would be (not (g '(a . b))), which is impossible. But then we could functionally instantiate f-not-identity, replacing f by the identity function, to prove nil! This is bad. (defthm bad nil :rule-classes nil :hints (("Goal" :use (:functional-instance f-not-identity (f identity))))) This sequence of events was legal in versions of ACL2 prior to Version 1.5. When we realized the problem we took steps to make it illegal. However, our steps were insufficient and it was possible to sneak in a subversive function (via mutual recursion) as late as Version 2.3. We now turn to the plausibility of the bogus argument above. Why might one even be tempted to think that the definition of g above poses no constraint on f? Here is a very similar encapsulate. (encapsulate (((f *) => *)) (local (defun f (x) (cdr x))) (defun map (x) (if (consp x) (cons (f x) (map (cdr x))) nil))) Here map plays the role of g above. Like g, map calls the constrained function f. But map truly does not constrain f. In particular, the definition of map could be moved "out" of the encapsulate so that map is introduced afterwards. The difference between map and g is that the constrained function plays no role in the termination argument for the one but does for the other. As a "user-friendly" gesture, ACL2 implicitly moves map-like functions out of encapsulations; logically speaking, they are introduced after the encapsulation. This simplifies the constraint. When the constraint cannot be thus simplfied the user is advised, via the "infected" warning, to phrase the encapsulation in the simplest way possible. The lingering bug between Versions 1.5 and 2.3 mentioned above was due to our failure to detect the g-like nature of some functions when they were defined in mutually recursively cliques with other functions. The singly recursive case was recognized. The bug arose because our detection "algorithm" was based on the "suggested inductions" left behind by successful definitions. We failed to recall that mutually-recursive definitions do not, as of this writing, make any suggestions about inductions and so did not leave any traces of their subversive natures. We conclude by elaborating on the criterion "involved in the termination argument" mentioned at the outset. Suppose that function f is recursively defined in an encapsulate, where the body of f has no "ancestor" among the functions introduced in the signature of that encapsulate, i.e.: the body contains no call of a signature function, no call of a function whose body calls a signature function, and so on. Then ACL2 treats f as though it is defined in front of that encapsulate form; so f is not constrained by the encapsulate, and its definition is hence certainly not subversive in the sense discussed above. But suppose to the contrary that some function introduced in the signature is an ancestor of the body of f. Then the definition of f is subversive if moreover, its termination proof obligation has an ancestor among the signature functions. Now, that proof obligation involves terms from the first argument of selected calls of IF, as well as recursive calls; for a detailed discussion, see *note RULER-EXTENDERS::. The important point here is that for the recursive calls, only the arguments in "measured" positions are relevant to the termination proof obligation. Consider the following example. (encapsulate (((h *) => *)) (local (defun h (n) n)) (defun f (i n) (if (zp i) n (f (1- i) (h n))))) ACL2 heuristically picks the measure (acl2-count i) for the definition of f; thus, i is the only "measured formal" of f. Since i is the first formal of f, then for the recursive call of f, only the first argument contributes to the termination proof obligation: in this case, (1- i) but not (h n). Thus, even though h is a signature function, the definition of f is not considered to be subversive; an induction scheme is thus stored for f. (This restriction to measured formal positions of recursive calls, for determining subversive definitions, is new in Version_3.5 of ACL2.)  File: acl2-doc-emacs.info, Node: SYNTAX, Next: SYNTAXP, Prev: SUBVERSIVE-RECURSIONS, Up: MISCELLANEOUS SYNTAX the syntax of ACL2 is that of Common Lisp For the details of ACL2 syntax, see CLTL. For examples of ACL2 syntax, use :pe to print some of the ACL2 system code. For example: :pe assoc-equal :pe dumb-occur :pe var-fn-count :pe add-linear-variable-to-alist There is no comprehensive description of the ACL2 syntax yet, except that found in CLTL. Also see *note TERM::.  File: acl2-doc-emacs.info, Node: SYNTAXP, Next: TERM, Prev: SYNTAX, Up: MISCELLANEOUS SYNTAXP attach a heuristic filter on a rule A calls of syntaxp in the hypothesis of a :rewrite, :definition, or :linear rule is treated specially, as described below. Similar treatment is given to the evaluation of a :meta rule's hypothesis function call. For example, consider the :rewrite rule created from the following formula. Example: (IMPLIES (SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM)))) (EQUAL (LXD X) (LXD (NORM X)))). The syntaxp hypothesis in this rule will allow the rule to be applied to (lxd (trn a b)) but will not allow it to be applied to (lxd (norm a)). * Menu: * SYNTAXP-EXAMPLES:: examples pertaining to syntaxp hypotheses General Form: (SYNTAXP test) Syntaxp always returns t and so may be added as a vacuous hypothesis. However, when relieving the hypothesis, the test "inside" the syntaxp form is actually treated as a meta-level proposition about the proposed instantiation of the rule's variables and that proposition must evaluate to true (non-nil) to "establish" the syntaxp hypothesis. Note that the test of a syntaxp hypothesis does not, in general, deal with the meaning or semantics or values of the terms, but rather with their syntactic forms. In the example above, the syntaxp hypothesis allows the rule to be applied to every target of the form (lxd u), provided u is not of the form (norm v). Observe that without this syntactic restriction the rule above could loop, producing a sequence of increasingly complex targets (lxd a), (lxd (norm a)), (lxd (norm (norm a))), etc. An intuitive reading of the rule might be "norm the argument of lxd unless it has already been normed." Note also that a syntaxp hypothesis deals with the syntactic form used internally by ACL2, rather than that seen by the user. In some cases these are the same, but there can be subtle differences with which the writer of a syntaxp hypothesis must be aware. You can use :trans to display this internal representation. There are two types of syntaxp hypotheses. The simpler type may be a hypothesis of a :rewrite, :definition, or :linear rule provided test contains at least one variable but no free variables (see *note FREE-VARIABLES::). In particular, test may not use stobjs; any stobj name will be treated as an ordinary variable. The case of :meta rules is similar to the above, except that it applies to the result of applying the hypothesis metafunction. (Later below we will describe the second type, an _extended_ syntaxp hypothesis, which may use state.) We illustrate the use of simple syntaxp hypotheses by slightly elaborating the example given above. Consider a :rewrite rule: (IMPLIES (AND (RATIONALP X) (SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM))))) (EQUAL (LXD X) (LXD (NORM X)))) How is this rule applied to (lxd (trn a b))? First, we form a substitution that instantiates the left-hand side of the conclusion of the rule so that it is identical to the target term. In the present case, the substitution replaces x with (trn a b). (LXD X) ==> (LXD (trn a b)). Then we backchain to establish the hypotheses, in order. Ordinarily this means that we instantiate each hypothesis with our substitution and then attempt to rewrite the resulting instance to true. Thus, in order to relieve the first hypothesis above, we rewrite (RATIONALP (trn a b)). If this rewrites to true, we continue. Of course, many users are aware of some exceptions to this general description of the way we relieve hypotheses. For example, if a hypothesis contains a "free-variable" -- one not bound by the current substitution -- we attempt to extend the substitution by searching for an instance of the hypothesis among known truths. See *note FREE-VARIABLES::. Forced hypotheses are another exception to the general rule of how hypotheses are relieved. Hypotheses marked with syntaxp, as in (syntaxp test), are also exceptions. We instantiate such a hypothesis; but instead of rewriting the instantiated instance, we evaluate the instantiated test. More precisely, we evaluate test in an environment in which its variable symbols are bound to the quotations of the terms to which those variables are bound in the instantiating substitution. So in the case in point, we (in essence) evaluate (NOT (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))). This clearly evaluates to t. When a syntaxp test evaluates to true, we consider the syntaxp hypothesis to have been established; this is sound because logically (syntaxp test) is t regardless of test. If the test evaluates to nil (or fails to evaluate because of guard violations) we act as though we cannot establish the hypothesis and abandon the attempt to apply the rule; it is always sound to give up. The acute reader will have noticed something odd about the form (NOT (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))). When relieving the first hypothesis, (RATIONALP X), we substituted (trn a b) for X; but when relieving the second hypothesis, (SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM)))), we substituted the quotation of (trn a b) for X. Why the difference? Remember that in the first hypothesis we are talking about the value of (trn a b) -- is it rational -- while in the second one we are talking about its syntactic form. Remember also that Lisp, and hence ACL2, evaluates the arguments to a function before applying the function to the resulting values. Thus, we are asking "Is the list (trn a b) a consp and if so, is its car the symbol NORM?" The quotes on both (trn a b) and NORM are therefore necessary. One can verify this by defining trn to be, say cons, and then evaluating forms such as (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM)) (AND (CONSP (trn a b)) (EQ (CAR (trn a b)) NORM)) (AND (CONSP (trn 'a 'b)) (EQ (CAR (trn 'a 'b)) NORM)) (AND (CONSP '(trn a b)) (EQ '(CAR (trn a b)) ''NORM)) at the top-level ACL2 prompt. See *note SYNTAXP-EXAMPLES:: for more examples of the use of syntaxp. An extended syntaxp hypothesis is similar to the simple type described above, but it uses two additional variables, mfc and state, which must not be bound by the left hand side or an earlier hypothesis of the rule. They must be the last two variables mentioned by form; first mfc, then state. These two variables give access to the functions mfc-xxx; see *note EXTENDED-METAFUNCTIONS::. As described there, mfc is bound to the so-called metafunction-context and state to ACL2's state. See *note SYNTAXP-EXAMPLES:: for an example of the use of these extended syntaxp hypotheses. We conclude with an example illustrating an error that may occur if you forget that a syntaxp hypothesis will be evaluated in an environment where variables are bound to syntactic terms, not to values. Consider the following stobj introduction (see *note DEFSTOBJ::). (defstobj st (fld1 :type (signed-byte 3) :initially 0) fld2) The following syntaxp hypothesis is ill-formed for evaluation. Indeed, ACL2 causes an error because it anticipates that when trying to relieve the syntaxp hypothesis of this rule, ACL2 would be evaluating (fld1 st) where st is bound to a term, not to an actual stobj as required by the function fld1. The error message is intended to explain this problem. ACL2 !>(defthm bad (implies (syntaxp (quotep (fld1 st))) (equal (stp st) (and (true-listp st) (equal (len st) 2) (fld1p (car st)))))) ACL2 Error in ( DEFTHM BAD ...): The form (QUOTEP (FLD1 ST)), from a SYNTAXP hypothesis, is not suitable for evaluation in an environment where its variables are bound to terms. See :DOC SYNTAXP. Here is further explanation: The form ST is being used, as an argument to a call of FLD1, where the single-threaded object of that name is required. But in the current context, the only declared stobj name is STATE. Note: this error occurred in the context (FLD1 ST). Summary Form: ( DEFTHM BAD ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) ACL2 Error in ( DEFTHM BAD ...): See :DOC failure. ******** FAILED ******** ACL2 !> Presumably the intention was to rewrite the term (stp st) when the fld1 component of st is seen to be an explicit constant. As explained elsewhere (see *note FREE-VARIABLES::), we can obtain the result of rewriting (fld1 st) by binding a fresh variable to that term using EQUAL, as follows. (defthm good (implies (and (equal f (fld1 st)) (syntaxp (quotep f))) (equal (stp st) (and (true-listp st) (equal (len st) 2) (fld1p (car st)))))) The event above is admitted by ACL2. We can see it in action by disabling the definition of stp so that only the rule above, good, is available for reasoning about stp. (in-theory (disable stp)) Then the proof fails for the following, because the syntaxp hypothesis of the rule, good, fails: (quotep f) evaluates to nil when f is bound to the term (fld1 st). (thm (stp st)) However, the proof succeeds for the next form, as we explain below. (thm (stp (list 3 rest))) Consider what happens in that case when rule good is applied to the term (stp (list 3 rest)). (See *note FREE-VARIABLES:: for relevant background.) The first hypothesis of good binds the variable f to the result of rewriting (fld1 st), where st is bound to the (internal form of) the term (list 3 rest) -- and that result is clearly the term, '3. Then the syntaxp hypothesis is successfully relieved, because the evaluation of (quotep f) returns t in the environment that binds f to '3.  File: acl2-doc-emacs.info, Node: SYNTAXP-EXAMPLES, Prev: SYNTAXP, Up: SYNTAXP SYNTAXP-EXAMPLES examples pertaining to syntaxp hypotheses See *note SYNTAXP:: for a basic discussion of the use of syntaxp to control rewriting. A common syntactic restriction is (SYNTAXP (AND (CONSP X) (EQ (CAR X) 'QUOTE))) or, equivalently, (SYNTAXP (QUOTEP X)). A rule with such a hypothesis can be applied only if x is bound to a specific constant. Thus, if x is 23 (which is actually represented internally as (quote 23)), the test evaluates to t; but if x prints as (+ 11 12) then the test evaluates to nil (because (car x) is the symbol binary-+). We see the use of this restriction in the rule (implies (and (syntaxp (quotep c)) (syntaxp (quotep d))) (equal (+ c d x) (+ (+ c d) x))). If c and d are constants, then the executable-counterpart of binary-+ will evaluate the sum of c and d. For instance, under the influence of this rule (+ 11 12 foo) rewrites to (+ (+ 11 12) foo) which in turn rewrites to (+ 23 foo). Without the syntactic restriction, this rule would loop with the built-in rules ASSOCIATIVITY-OF-+ or COMMUTATIVITY-OF-+. We here recommend that the reader try the affects of entering expressions such as the following at the top level ACL2 prompt. (+ 11 23) (+ '11 23) (+ '11 '23) (+ ''11 ''23) :trans (+ 11 23) :trans (+ '11 23) :trans (+ ''11 23) :trans (+ c d x) :trans (+ (+ c d) x) We also recommend that the reader verify our claim above about looping by trying the affect of each of the following rules individually. (defthm good (implies (and (syntaxp (quotep c)) (syntaxp (quotep d))) (equal (+ c d x) (+ (+ c d) x)))) (defthm bad (implies (and (acl2-numberp c) (acl2-numberp d)) (equal (+ c d x) (+ (+ c d) x)))) on (the false) theorems: (thm (equal (+ 11 12 x) y)) (thm (implies (and (acl2-numberp c) (acl2-numberp d) (acl2-numberp x)) (equal (+ c d x) y))). One can use :brr, perhaps in conjunction with cw-gstack, to investigate any looping. Here is a simple example showing the value of rule good above. Without good, the thm form below fails. (defstub foo (x) t) (thm (equal (foo (+ 3 4 x)) (foo (+ 7 x)))) The next three examples further explore the use of quote in syntaxp hypotheses. We continue the examples of syntaxp hypotheses with a rule from community book books/finite-set-theory/set-theory.lisp. We will not discuss here the meaning of this rule, but it is necessary to point out that (ur-elementp nil) is true in this book. (defthm scons-nil (implies (and (syntaxp (not (equal a ''nil))) (ur-elementp a)) (= (scons e a) (scons e nil)))). Here also, syntaxp is used to prevent looping. Without the restriction, (scons e nil) would be rewritten to itself since (ur-elementp nil) is true. Question: Why the use of two quotes in "nil? Hints: Nil is a constant just as 23 is. Try :trans (cons a nil), :trans (cons 'a 'nil), and :trans (cons "a "nil). Also, don't forget that the arguments to a function are evaluated before the function is applied. The next two rules move negative constants to the other side of an inequality. (defthm |(< (+ (- c) x) y)| (implies (and (syntaxp (quotep c)) (syntaxp (< (cadr c) 0)) (acl2-numberp y)) (equal (< (+ c x) y) (< (fix x) (+ (- c) y))))) (defthm |(< y (+ (- c) x))| (implies (and (syntaxp (quotep c)) (syntaxp (< (cadr c) 0)) (acl2-numberp y)) (equal (< y (+ c x)) (< (+ (- c) y) (fix x))))) Questions: What would happen if (< (cadr c) '0) were used? What about (< (cadr c) "0)? One can also use syntaxp to restrict the application of a rule to a particular set of variable bindings as in the following taken from community book books/ihs/quotient-remainder-lemmas.lisp. (encapsulate () (local (defthm floor-+-crock (implies (and (real/rationalp x) (real/rationalp y) (real/rationalp z) (syntaxp (and (eq x 'x) (eq y 'y) (eq z 'z)))) (equal (floor (+ x y) z) (floor (+ (+ (mod x z) (mod y z)) (* (+ (floor x z) (floor y z)) z)) z))))) (defthm floor-+ (implies (and (force (real/rationalp x)) (force (real/rationalp y)) (force (real/rationalp z)) (force (not (equal z 0)))) (equal (floor (+ x y) z) (+ (floor (+ (mod x z) (mod y z)) z) (+ (floor x z) (floor y z)))))) ) We recommend the use of :brr to investigate the use of floor-+-crock. Another useful restriction is defined by (defun rewriting-goal-literal (x mfc state) ;; Are we rewriting a top-level goal literal, rather than rewriting ;; to establish a hypothesis from a rewrite (or other) rule? (declare (ignore x state)) (null (access metafunction-context mfc :ancestors))). We use this restriction in the rule (defthm |(< (* x y) 0)| (implies (and (syntaxp (rewriting-goal-literal x mfc state)) (rationalp x) (rationalp y)) (equal (< (* x y) 0) (cond ((equal x 0) nil) ((equal y 0) nil) ((< x 0) (< 0 y)) ((< 0 x) (< y 0)))))) which has been found to be useful, but which also leads to excessive thrashing in the linear arithmetic package if used indiscriminately. See *note EXTENDED-METAFUNCTIONS:: for information on the use of mfc and metafunction-context.  File: acl2-doc-emacs.info, Node: TERM, Next: THE-METHOD, Prev: SYNTAXP, Up: MISCELLANEOUS TERM the three senses of well-formed ACL2 expressions or formulas Examples of Terms: (cond ((caar x) (cons t x)) (t 0)) ; an untranslated term (if (car (car x)) (cons 't x) '0) ; a translated term (car (cons x y) 'nil v) ; a pseudo-term In traditional first-order predicate calculus a "term" is a syntactic entity denoting some object in the universe of individuals. Often, for example, the syntactic characterization of a term is that it is either a variable symbol or the application of a function symbol to the appropriate number of argument terms. Traditionally, "atomic formulas" are built from terms with predicate symbols such as "equal" and "member;" "formulas" are then built from atomic formulas with propositional "operators" like "not," "and," and "implies." Theorems are formulas. Theorems are "valid" in the sense that the value of a theorem is true, in any model of the axioms and under all possible assignments of individuals to variables. However, in ACL2, terms are used in place of both atomic formulas and formulas. ACL2 does not have predicate symbols or propositional operators as distinguished syntactic entities. The ACL2 universe of individuals includes a "true" object (denoted by t) and a "false" object (denoted by nil), predicates and propositional operators are functions that return these objects. Theorems in ACL2 are terms and the "validity" of a term means that, under no assignment to the variables does the term evaluate to nil. We use the word "term" in ACL2 in three distinct senses. We will speak of "translated" terms, "untranslated" terms, and "pseudo-" terms. _Translated Terms: The Strict Sense and Internal Form_ In its most strict sense, a "term" is either a legal variable symbol, a quoted constant, or the application of an n-ary function symbol or closed lambda expression to a true list of n terms. The legal variable symbols are symbols other than t or nil which are not in the keyword package, do not start with ampersand, do not start and end with asterisks, and if in the main Lisp package, do not violate an appropriate restriction (see *note NAME::). Quoted constants are expressions of the form (quote x), where x is any ACL2 object. Such expressions may also be written 'x. Closed lambda expressions are expressions of the form (lambda (v1 ... vn) body) where the vi are distinct legal variable symbols, body is a term, and the only free variables in body are among the vi. The function termp, which takes two arguments, an alleged term x and a logical world w (see *note WORLD::), recognizes terms of a given extension of the logic. Termp is defined in :program mode. Its definition may be inspected with :pe termp for a complete specification of what we mean by "term" in the most strict sense. Most ACL2 term-processing functions deal with terms in this strict sense and use termp as a guard. That is, the "internal form" of a term satisfies termp, the strict sense of the word "term." _Untranslated Terms: What the User Types_ While terms in the strict sense are easy to explore (because their structure is so regular and simple) they can be cumbersome to type. Thus, ACL2 supports a more sugary syntax that includes uses of macros and constant symbols. Very roughly speaking, macros are functions that produce terms as their results. Constants are symbols that are associated with quoted objects. Terms in this sugary syntax are "translated" to terms in the strict sense; the sugary syntax is more often called "untranslated." Roughly speaking, translation just implements macroexpansion, the replacement of constant symbols by their quoted values, and the checking of all the rules governing the strict sense of "term." More precisely, macro symbols are as described in the documentation for defmacro. A macro, mac, can be thought of as a function, mac-fn, from ACL2 objects to an ACL2 object to be treated as an untranslated term. For example, caar is defined as a macro symbol; the associated macro function maps the object x into the object (car (car x)). A macro form is a "call" of a macro symbol, i.e., a list whose car is the macro symbol and whose cdr is an arbitrary true list of objects, used as a term. Macroexpansion is the process of replacing in an untranslated term every occurrence of a macro form by the result of applying the macro function to the appropriate arguments. The "appropriate" arguments are determined by the exact form of the definition of the macro; macros support positional, keyword, optional and other kinds of arguments. See *note DEFMACRO::. In addition to macroexpansion and constant symbol dereferencing, translation implements the mapping of let and let* forms into applications of lambda expressions and closes lambda expressions containing free variables. Thus, the translation of (let ((x (1+ i))) (cons x k)) can be seen as a two-step process that first produces ((lambda (x) (cons x k)) (1+ i)) and then ((lambda (x k) (cons x k)) (1+ i) k) . Observe that the body of the let and of the first lambda expression contains a free k which is finally bound and passed into the second lambda expression. Translation also maps flet forms into applications of lambda expressions. See *note FLET::. When we say, of an event-level function such as defun or defthm, that some argument "must be a term" we mean an untranslated term. The event functions translate their term-like arguments. To better understand the mapping between untranslated terms and translated terms it is convenient to use the keyword command :trans to see examples of translations. See *note TRANS:: and also see *note TRANS1::. Finally, we note that the theorem prover prints terms in untranslated form. But there can be more than one correct untranslated term corresponding to a given translated term. For example, the translated term (if x y 'nil) can be untranslated as (if x y nil) and can also be untranslated as (and x y). The theorem prover attempts to print an untranslated term that is as helpful to the user as possible. In particular, consider a term of the form (nth k st) where st is a single-threaded object (see *note STOBJ::) and the kth accessor of st is, say, kn. The theorem prover typically would expand (kn st) to (nth k st). If k is large then it could be difficult for the user to make sense out of a proof transcript that mentions the expanded term. Fortunately, the untranslation of (nth k st) would be (nth *kn* st); here *kn* would be a constant (see *note DEFCONST::) added by the defstobj event introducing st, defined to have value k. The user can extend this user-friendly style of printing applications of nth to stobjs; see *note ADD-NTH-ALIAS::. These remarks about printing applications of function nth extend naturally to function update-nth. Moreover, the prover will attempt to treat terms as stobjs for the above purpose when appropriate. For example, if function foo has signature ((foo * st) => (mv * * * st)), where st is introduced with (defstobj st f0 f1), then the term (nth '1 (mv-nth '3 (foo x st0))) will be printed as (nth *f1* (mv-nth 3 (foo x st0))). _Pseudo-Terms: A Common Guard for Metafunctions_ Because termp is defined in :program mode, it cannot be used effectively in conjectures to be proved. Furthermore, from the perspective of merely guarding a term processing function, termp often checks more than is required. Finally, because termp requires the logical world as one of its arguments it is impossible to use termp as a guard in places where the logical world is not itself one of the arguments. For these reasons we support the idea of "pseudo-terms." A pseudo-term is either a symbol (but not necessarily one having the syntax of a legal variable symbol), a true list beginning with quote (but not necessarily well-formed), or the "application of" a symbol or pseudo lambda expression to a true list of pseudo-terms. A pseudo lambda expression is an expression of the form (lambda (v1 ... vn) body) where the vi are all symbols and body is a pseudo-term. Pseudo-terms are recognized by the unary function pseudo-termp. If (termp x w) is true, then (pseudo-termp x) is true. However, if x fails to be a (strict) term it may nevertheless still be a pseudo-term. For example, (car a b) is not a term, because car is applied to the wrong number of arguments, but it is a pseudo-term. The structures recognized by pseudo-termp can be recursively explored with the same simplicity that terms can be. In particular, if x is not a variablep or an fquotep, then (ffn-symb x) is the function (symbol or lambda expression) and (fargs x) is the list of argument pseudo-terms. A metafunction (see *note META::) or clause-processor (see *note CLAUSE-PROCESSOR::) may use pseudo-termp as the guard.  File: acl2-doc-emacs.info, Node: THE-METHOD, Next: TIME-TRACKER-TAU, Prev: TERM, Up: MISCELLANEOUS THE-METHOD how to find proofs Also see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed tutorial on how to prove theorems with ACL2. Many users develop proof scripts in an Emacs buffer and submit one event at a time to the theorem prover running in a *shell* buffer. The script buffer is logically divided into two regions: the events that have been accepted by the theorem prover and those that have not yet been accepted. An imaginary "barrier" divides these two regions. The region above the barrier describes the state of the *shell* buffer (and ACL2's logical world). The region below the barrier is the "to do" list. We usually start a proof project by typing the key lemmas, and main goal into the to do list. Definitions are here just regarded as theorems to prove (i.e., the measure conjectures). Then we follow "The Method." (1) Think about the proof of the first theorem in the to do list. Structure the proof either as an induction followed by simplification or just simplification. Have the necessary lemmas been proved? That is, are the necessary lemmas in the done list already? If so, proceed to Step 2. Otherwise, add the necessary lemmas at the front of the to do list and repeat Step 1. (2) Call the theorem prover on the first theorem in the to do list and let the output stream into the *shell* buffer. Abort the proof if it runs more than a few seconds. (3) If the theorem prover succeeded, advance the barrier past the successful command and go to Step 1. (4) Otherwise, inspect the failed proof attempt, starting from the beginning, not the end. Basically you should look for the first place the proof attempt deviates from your imagined proof. If your imagined proof was inductive, inspect the induction scheme used by ACL2. If that is ok, then find the first subsequent subgoal that is stable under simplification and think about why it was not proved by the simplifier. If your imagined proof was not inductive, then think about the first subgoal stable under simplification, as above. Modify the script appropriately. It usually means adding lemmas to the to do list, just in front of the theorem just tried. It could mean adding hints to the current theorem. In any case, after the modifications go to Step 1. We do not seriously suggest that this or any rotely applied algorithm will let you drive ACL2 to difficult proofs. Indeed, to remind you of this we call this "The Method" rather than "the method." That is, we are aware of the somewhat pretentious nature of any such advice. But these remarks have helped many users approach ACL2 in a constructive and disciplined way. We say much more about The Method in the ACL2 book. See the home page. Also see *note SET-GAG-MODE:: for a discussion of a way for ACL2 to help you to use The Method. And again, see *note INTRODUCTION-TO-THE-THEOREM-PROVER:: for a more detailed tutorial. Learning to read failed proofs is a useful skill. There are several kinds of "checkpoints" in a proof: (1) a formula to which induction is being (or would be) applied, (2) the first formula stable under simplification, (3) a formula that is possibly generalized, either by cross-fertilizing with and throwing away an equivalence hypothesis or by explicit generalization of a term with a new variable. At the induction checkpoint, confirm that you believe the formula being proved is a theorem and that it is appropriately strong for an inductive proof. Read the selected induction scheme and make sure it agrees with your idea of how the proof should go. At the post-simplification checkpoint, which is probably the most commonly seen, consider whether there are additional rewrite rules you could prove to make the formula simplify still further. Look for compositions of function symbols you could rewrite. Look for contradictions among hypotheses and prove the appropriate implications: for example, the checkpoint might contain the two hypotheses (P (F A)) and (NOT (Q (G (F A)))) and you might realize that (implies (p x) (q (g x))) is a theorem. Look for signs that your existing rules did not apply, e.g., for terms that should have been rewritten, and figure out why they were not. Possible causes include that they do not exactly match your old rules, that your old rules have hypotheses that cannot be relieved here - perhaps because some other rules are missing, or perhaps your old rules are disabled. If you cannot find any further simplifications to make in the formula, ask yourself whether it is valid. If so, sketch a proof. Perhaps the proof is by appeal to a combination of lemmas you should now prove? At the two generalization checkpoints -- where hypotheses are discarded or terms are replaced by variables -- ask yourself whether the result is a theorem. It often is not. Think about rewrite rules that would prove the formula. These are often restricted versions of the overly-general formulas created by the system's heuristics. See *note PROOF-TREE:: for a discussion of a tool to help you navigate through ACL2 proofs.  File: acl2-doc-emacs.info, Node: TIME-TRACKER-TAU, Next: TRUST-TAG, Prev: THE-METHOD, Up: MISCELLANEOUS TIME-TRACKER-TAU messages about expensive use of the tau-system This documentation topic explains messages printing by the theorem prover about the tau-system, as follows. During a proof you may see a message such as the following. TIME-TRACKER-NOTE [:TAU]: Elapsed runtime in tau is 4.95 secs; see :DOC time-tracker-tau. Just below a proof summary you may see a message such as the following. TIME-TRACKER-NOTE [:TAU]: For the proof above, the total runtime spent in the tau system was 30.01 seconds. See :DOC time-tracker-tau. Both of these messages are intended to let you know that certain prover heuristics (see *note TAU-SYSTEM::) may be slowing proofs down more than they are helping. If you are satisfied with the prover's performance, you may ignore these messages or even turn them off by disabling time-tracking entirely (see *note TIME-TRACKER::). Otherwise, here are some actions that you can take to solve such a performance problem. The simplest solution is to disable the tau-system, in either of the following equivalent ways. (in-theory (disable (:executable-counterpart tau-system))) (in-theory (disable (tau-system))) But if you want to leave the tau-system enabled, you could investigate the possibility is that the tau-system is causing an expensive :logic-mode function to be executed. You can diagnose that problem by observing the rewriter -- see *note DMR:: -- or by interrupting the system and getting a backtrace (see *note SET-DEBUGGER-ENABLE::). A solution in that case is to disable the executable-counterpart of that function, for example in either of these equivalent ways. (in-theory (disable (:executable-counterpart foo))) (in-theory (disable (foo))) As a result, the tau-system will be weakened, but perhaps only negligibly. In either case above, you may prefer to provide :in-theory hints rather than :in-theory events; see *note HINTS::. A more sophisticated solution is to record values of the :logic-mode function in question, so that the tau-system will look up the necessary values rather than calling the function, whether or not the executable-counterpart of that function is enabled. Here is an example of a lemma that can provide such a solution. See *note TAU-SYSTEM::. (defthm lemma (and (foo 0) (foo 17) (foo t) (not (foo '(a b c)))) :rule-classes :tau-system)  File: acl2-doc-emacs.info, Node: TRUST-TAG, Next: TTAGS-SEEN, Prev: TIME-TRACKER-TAU, Up: MISCELLANEOUS TRUST-TAG See *note DEFTTAG::.  File: acl2-doc-emacs.info, Node: TTAGS-SEEN, Next: TTREE, Prev: TRUST-TAG, Up: MISCELLANEOUS TTAGS-SEEN list some declared trust tags (ttags) General Forms: :ttags-seen (ttags-seen) Suppose the output is as follows. (T NIL) (FOO "/home/bob/bar.lisp" "/home/cindy/bar.lisp") Warning: This output is minimally trustworthy (see :DOC TTAGS-SEEN). This output indicates that the current logical world has seen the declaration of trust tag T at the top-level (see *note DEFTTAG::) and the declaration of trust tag FOO in the two books included from the listed locations. The warning emphasizes that this command cannot be used to validate the "purity" of an ACL2 session, because using a ttag renders enough power to hide from this or any other command the fact that the ttag was ever declared. As discussed elsewhere (see *note DEFTTAG::), the only reliable way to validate the "purity" of a session is to watch for "TTAG NOTE" output. Another shortcoming of this command is that it only checks the current logical world for ttag declarations. For example, one could execute a defttag event; then use progn! and set-raw-mode to replace system functions with corrupt definitions or to introduce inconsistent axioms in the ground-zero world; and finally, execute :ubt! 1 to remove all evidence of the ttag in the world while leaving in place the corrupt definitions or axioms. The base world is now tainted, meaning we could prove nil or certify a book that proves nil, but the resulting session or book would contain no trace of the ttag that tainted it! Despite shortcomings, this command might be useful to system hackers. It also serves to illustrate the inherent flaw in asking a session whether or how it is "tainted", justifying the "TTAG NOTE" approach (see *note DEFTTAG::).  File: acl2-doc-emacs.info, Node: TTREE, Next: TYPE-SET, Prev: TTAGS-SEEN, Up: MISCELLANEOUS TTREE tag-trees Many low-level ACL2 functions take and return "tag trees" or "ttrees" (pronounced "tee-trees") which contain various useful bits of information such as the lemmas used, the linearize assumptions made, etc. Abstractly a tag-tree represents a list of sets, each member set having a name given by one of the "tags" (which are symbols) of the ttree. The elements of the set named tag are all of the objects tagged tag in the tree. You are invited to browse the source code. Definitions of primitives are labeled with the comment "; Note: Tag-tree primitive". The rewriter, for example, takes a term and a ttree (among other things), and returns a new term, term', and new ttree, ttree'. Term' is equivalent to term (under the current assumptions) and the ttree' is an extension of ttree. If we focus just on the set associated with the tag LEMMA in the ttrees, then the set for ttree' is the extension of that for ttree obtained by unioning into it all the runes used by the rewrite. The set associated with LEMMA can be obtained by (tagged-objects 'LEMMA ttree).  File: acl2-doc-emacs.info, Node: TYPE-SET, Next: TYPESPEC-CHECK, Prev: TTREE, Up: MISCELLANEOUS TYPE-SET how type information is encoded in ACL2 To help you experiment with type-sets we briefly note the following utility functions. (type-set-quote x) will return the type-set of the object x. For example, (type-set-quote "test") is 2048 and (type-set-quote '(a b c)) is 512. (type-set 'term nil nil nil (ens state) (w state) nil nil nil) will return the type-set of term. For example, (type-set '(integerp x) nil nil nil (ens state) (w state) nil nil nil) will return (mv 192 nil). 192, otherwise known as *ts-boolean*, is the type-set containing t and nil. The second result may be ignored in these experiments. Term must be in the translated, internal form shown by :trans. See *note TRANS:: and see *note TERM::. (type-set-implied-by-term 'x nil 'term (ens state)(w state) nil) will return the type-set deduced for the variable symbol x assuming the translated term, term, true. The second result may be ignored in these experiments. For example, (type-set-implied-by-term 'v nil '(integerp v) (ens state) (w state) nil) returns 11. (convert-type-set-to-term 'x ts (ens state) (w state) nil) will return a term whose truth is equivalent to the assertion that the term x has type-set ts. The second result may be ignored in these experiments. For example (convert-type-set-to-term 'v 523 (ens state) (w state) nil) returns a term expressing the claim that v is either an integer or a non-nil true-list. 523 is the logical-or of 11 (which denotes the integers) with 512 (which denotes the non-nil true-lists). The "actual primitive types" of ACL2 are listed in *actual-primitive-types*, whose elements are shown below. Each actual primitive type denotes a set -- sometimes finite and sometimes not -- of ACL2 objects and these sets are pairwise disjoint. For example, *ts-zero* denotes the set containing 0 while *ts-positive-integer* denotes the set containing all of the positive integers. *TS-ZERO* ;;; {0} *TS-POSITIVE-INTEGER* ;;; positive integers *TS-POSITIVE-RATIO* ;;; positive non-integer rationals *TS-NEGATIVE-INTEGER* ;;; negative integers *TS-NEGATIVE-RATIO* ;;; negative non-integer rationals *TS-COMPLEX-RATIONAL* ;;; complex rationals *TS-NIL* ;;; {nil} *TS-T* ;;; {t} *TS-NON-T-NON-NIL-SYMBOL* ;;; symbols other than nil, t *TS-PROPER-CONS* ;;; null-terminated non-empty lists *TS-IMPROPER-CONS* ;;; conses that are not proper *TS-STRING* ;;; strings *TS-CHARACTER* ;;; characters The actual primitive types were chosen by us to make theorem proving convenient. Thus, for example, the actual primitive type *ts-nil* contains just nil so that we can encode the hypothesis "x is nil" by saying "x has type *ts-nil*" and the hypothesis "x is non-nil" by saying "x has type complement of *ts-nil*." We similarly devote a primitive type to t, *ts-t*, and to a third type, *ts-non-t-non-nil-symbol*, to contain all the other ACL2 symbols. Let *ts-other* denote the set of all Common Lisp objects other than those in the actual primitive types. Thus, *ts-other* includes such things as floating point numbers and CLTL array objects. The actual primitive types together with *ts-other* constitute what we call *universe*. Note that *universe* is a finite set containing one more object than there are actual primitive types; that is, here we are using *universe* to mean the finite set of primitive types, not the infinite set of all objects in all of those primitive types. *Universe* is a partitioning of the set of all Common Lisp objects: every object belongs to exactly one of the sets in *universe*. Abstractly, a "type-set" is a subset of *universe*. To say that a term, x, "has type-set ts" means that under all possible assignments to the variables in x, the value of x is a member of some member of ts. Thus, (cons x y) has type-set {*ts-proper-cons* *ts-improper-cons*}. A term can have more than one type-set. For example, (cons x y) also has the type-set {*ts-proper-cons* *ts-improper-cons* *ts-nil*}. Extraneous types can be added to a type-set without invalidating the claim that a term "has" that type-set. Generally we are interested in the smallest type-set a term has, but because the entire theorem-proving problem for ACL2 can be encoded as a type-set question, namely, "Does p have type-set complement of *ts-nil*?," finding the smallest type-set for a term is an undecidable problem. When we speak informally of "the" type-set we generally mean "the type-set found by our heuristics" or "the type-set assumed in the current context." Note that if a type-set, ts, does not contain *ts-other* as an element then it is just a subset of the actual primitive types. If it does contain *ts-other* it can be obtained by subtracting from *universe* the complement of ts. Thus, every type-set can be written as a (possibly complemented) subset of the actual primitive types. By assigning a unique bit position to each actual primitive type we can encode every subset, s, of the actual primitive types by the nonnegative integer whose ith bit is on precisely if s contains the ith actual primitive type. The type-sets written as the complement of s are encoded as the twos-complement of the encoding of s. Those type-sets are thus negative integers. The bit positions assigned to the actual primitive types are enumerated from 0 in the same order as the types are listed in *actual-primitive-types*. At the concrete level, a type-set is an integer between *min-type-set* and *max-type-set*, inclusive. For example, *ts-nil* has bit position 6. The type-set containing just *ts-nil* is thus represented by 64. If a term has type-set 64 then the term is always equal to nil. The type-set containing everything but *ts-nil* is the twos-complement of 64, which is -65. If a term has type-set -65, it is never equal to nil. By "always" and "never" we mean under all, or under no, assignments to the variables, respectively. Here is a more complicated example. Let s be the type-set containing all of the symbols and the natural numbers. The relevant actual primitive types, their bit positions and their encodings are: actual primitive type bit value *ts-zero* 0 1 *ts-positive-integer* 1 2 *ts-nil* 6 64 *ts-t* 7 128 *ts-non-t-non-nil-symbol* 8 256 Thus, the type-set s is represented by (+ 1 2 64 128 256) = 451. The complement of s, i.e., the set of all objects other than the natural numbers and the symbols, is -452.  File: acl2-doc-emacs.info, Node: TYPESPEC-CHECK, Next: USE, Prev: TYPE-SET, Up: MISCELLANEOUS TYPESPEC-CHECK See *note META-EXTRACT::.  File: acl2-doc-emacs.info, Node: USE, Next: USING-COMPUTED-HINTS, Prev: TYPESPEC-CHECK, Up: MISCELLANEOUS USE hints keyword :USE See *note HINTS::.  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS, Next: USING-COMPUTED-HINTS-1, Prev: USE, Up: MISCELLANEOUS USING-COMPUTED-HINTS how to use computed hints Computed hints (see *note COMPUTED-HINTS::) are extraordinarily powerful. We show a few examples here to illustrate their use. We recommend that the using-computed-hints-n topics be read in the order using-computed-hints-1, using-computed-hints-2, and so on. * Menu: Related topics other than immediate subtopics: * ADD-DEFAULT-HINTS:: add to the default hints * REMOVE-DEFAULT-HINTS:: remove from the default hints * SET-DEFAULT-HINTS:: set the default hints * USING-COMPUTED-HINTS-1:: Driving Home the Basics * USING-COMPUTED-HINTS-2:: One Hint to Every Top-Level Goal in a Forcing Round * USING-COMPUTED-HINTS-3:: Hints as a Function of the Goal (not its Name) * USING-COMPUTED-HINTS-4:: Computing the Hints * USING-COMPUTED-HINTS-5:: Debugging Computed Hints * USING-COMPUTED-HINTS-6:: Using the computed-hint-replacement feature * USING-COMPUTED-HINTS-7:: Using the stable-under-simplificationp flag * USING-COMPUTED-HINTS-8:: Some Final Comments  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-1, Next: USING-COMPUTED-HINTS-2, Prev: USING-COMPUTED-HINTS, Up: MISCELLANEOUS USING-COMPUTED-HINTS-1 Driving Home the Basics The common hint ("Subgoal 3.2.1''" :use lemma42) has the same effect as the computed hint (if (equal id '((0) (3 2 1) . 2)) '(:use lemma42) nil) which, of course, is equivalent to (and (equal id '((0) (3 2 1) . 2)) '(:use lemma42)) which is also equivalent to the computed hint my-special-hint provided the following defun has first been executed (defun my-special-hint (id clause world) (declare (xargs :mode :program) (ignore clause world)) (if (equal id '((0) (3 2 1) . 2)) '(:use lemma42) nil)) It is permitted for the defun to be in :LOGIC mode (see *note DEFUN-MODE::) also. Just to be concrete, the following three events all behave the same way (if my-special-hint is as above): (defthm main (big-thm a b c) :hints (("Subgoal 3.2.1''" :use lemma42))) (defthm main (big-thm a b c) :hints ((and (equal id '((0) (3 2 1) . 2)) '(:use lemma42)))) (defthm main (big-thm a b c) :hints (my-special-hint))  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-2, Next: USING-COMPUTED-HINTS-3, Prev: USING-COMPUTED-HINTS-1, Up: MISCELLANEOUS USING-COMPUTED-HINTS-2 One Hint to Every Top-Level Goal in a Forcing Round Suppose the main proof completes with a forcing round on three subgoals, "[1]Subgoal 3", "[1]Subgoal 2", and "[1]Subgoal 1". Suppose you wish to :use lemma42 in all top-level goals of the first forcing round. This can be done supplying the hint (if test '(:use lemma42) nil), where test is an expression that returns t when ID is one of the clause ids in question. goal-spec (parse-clause-id goal-spec) "[1]Subgoal 3" ((1) (3) . 0) "[1]Subgoal 2" ((1) (2) . 0) "[1]Subgoal 1" ((1) (1) . 0) Recall (see *note CLAUSE-IDENTIFIER::) that parse-clause-id maps from a goal spec to a clause id, so you can use that function on the goal specs printed in the failed proof attempt to determine the clause ids in question. So one acceptable test is (member-equal id '(((1) (3) . 0) ((1) (2) . 0) ((1) (1) . 0))) or you could use parse-clause-id in your computed hint if you don't want to see clause ids in your script: (or (equal id (parse-clause-id "[1]Subgoal 3")) (equal id (parse-clause-id "[1]Subgoal 2")) (equal id (parse-clause-id "[1]Subgoal 1"))) or you could use the inverse function (see *note CLAUSE-IDENTIFIER::): (member-equal (string-for-tilde-@-clause-id-phrase id) '("[1]Subgoal 3" "[1]Subgoal 2" "[1]Subgoal 1")) Recall that what we've shown above are the tests to use in the computed hint. The hint itself is (if test '(:use lemma42) nil) or something equivalent like (and test '(:use lemma42)). The three tests above are all equivalent. They suffer from the problem of requiring the explicit enumeration of all the goal specs in the first forcing round. A change in the script might cause more forced subgoals and the ones other than those enumerated would not be given the hint. You could write a test that recognizes all first round top-level subgoals no matter how many there are. Just think of the programming problem: how do I recognize all the clause id's of the form ((1) (n) . 0)? Often you can come to this formulation of the problem by using parse-clause-id on a few of the candidate goal-specs to see the common structure. A suitable test in this case is: (and (equal (car id) '(1)) ; forcing round 1, top-level (pre-induction) (equal (len (cadr id)) 1) ; Subgoal n (not Subgoal n.i ...) (equal (cddr id) 0)) ; no primes The test above is "overkill" because it recognizes precisely the clause ids in question. But recall that once a computed hint is used, it is (by default) removed from the hints available to the children of the clause. Thus, we can widen the set of clause ids recognized to include all the children without worrying that the hint will be applied to those children. In particular, the following test supplies the hint to every top-level goal of the first forcing round: (equal (car id) '(1)) You might worry that it would also supply the hint to the subgoal produced by the hint - the cases we ruled out by the "overkill" above. But that doesn't happen since the hint is unavailable to the children. You could even write: (equal (car (car id)) 1) which would supply the hint to every goal of the form "[1]Subgoal ..." and again, because we see and fire on the top-level goals first, we will not fire on, say, "[1]Subgoal *1.3/2", i.e., the id '((1 1 3) (2) . 0) even though the test recognizes that id. Finally, the following test supplies the hint to every top-level goal of every forcing round (except the 0th, which is the "gist" of the proof, not "really" a forcing round): (not (equal (car (car id)) 0)) Recall again that in all the examples above we have exhibited the test in a computed hint of the form (if test '(:key1 val1 ...) nil).  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-3, Next: USING-COMPUTED-HINTS-4, Prev: USING-COMPUTED-HINTS-2, Up: MISCELLANEOUS USING-COMPUTED-HINTS-3 Hints as a Function of the Goal (not its Name) Sometimes it is desirable to supply a hint whenever a certain term arises in a conjecture. For example, suppose we have proved (defthm all-swaps-have-the-property (the-property (swap x)) :rule-classes nil) and suppose that whenever (SWAP A) occurs in a goal, we wish to add the additional hypothesis that (THE-PROPERTY (SWAP A)). Note that this is equivalent supplying the hint (if test '(:use (:instance all-swaps-have-the-property (x A))) nil) where test answers the question "does the clause contain (SWAP A)?" That question can be asked with (occur-lst '(SWAP A) clause). Briefly, occur-lst takes the representation of a translated term, x, and a list of translated terms, y, and determines whether x occurs as a subterm of any term in y. (By "subterm" here we mean proper or improper, e.g., the subterms of (CAR X) are X and (CAR X).) Thus, the computed hint: (if (occur-lst '(swap a) clause) '(:use (:instance all-swaps-have-the-property (x A))) nil) will add the hypothesis (THE-PROPERTY (SWAP A)) to every goal containing (SWAP A) - except the children of goals to which the hypothesis was added. A COMMON MISTAKE users are likely to make is to forget that they are dealing with translated terms. For example, suppose we wished to look for (SWAP (LIST 1 A)) with occur-lst. We would never find it with (occur-lst '(SWAP (LIST 1 A)) clause) because that presentation of the term contains macros and other abbreviations. By using :trans (see *note TRANS::) we can obtain the translation of the target term. Then we can look for it with: (occur-lst '(SWAP (CONS '1 (CONS A 'NIL))) clause) Note in particular that you must * eliminate all macros and * explicitly quote all constants. We recommend using :trans to obtain the translated form of the terms in which you are interested, before programming your hints. An alternative is to use the expression (prettyify-clause clause nil nil) in your hint to convert the current goal clause into the s-expression that is actually printed. For example, the clause ((NOT (CONSP X)) (SYMBOLP Y) (EQUAL (CONS '1 (CAR X)) Y)) "prettyifies" to (IMPLIES (AND (CONSP X) (NOT (SYMBOLP Y))) (EQUAL (CONS 1 (CAR X)) Y)) which is what you would see printed by ACL2 when the goal clause is that shown. However, if you choose to convert your clauses to prettyified form, you will have to write your own explorers (like our occur-lst), because all of the ACL2 term processing utilities work on translated and/or clausal forms. This should not be taken as a terrible burden. You will, at least, gain the benefit of knowing what you are really looking for, because your explorers will be looking at exactly the s-expressions you see at your terminal. And you won't have to wade through our still undocumented term/clause utilities. The approach will slow things down a little, since you will be paying the price of independently consing up the prettyified term. We make one more note on this example. We said above that the computed hint: (if (occur-lst '(swap a) clause) '(:use (:instance all-swaps-have-the-property (x A))) nil) will add the hypothesis (THE-PROPERTY (SWAP A)) to every goal containing (SWAP A) - except the children of goals to which the hypothesis was added. It bears noting that the subgoals produced by induction and top-level forcing round goals are not children. For example, suppose the hint above fires on "Subgoal 3" and produces, say, "Subgoal 3'". Then the hint will not fire on "Subgoal 3'" even though it (still) contains (SWAP A) because "Subgoal 3'" is a child of a goal on which the hint fired. But now suppose that "Subgoal 3'" is pushed for induction. Then the goals created by that induction, i.e., the base case and induction step, are not considered children of "Subgoal 3'". All of the original hints are available. Alternatively, suppose that "Subgoal 3' is proved but forces some other subgoal, "[1]Subgoal 1" which is attacked in Forcing Round 1. That top-level forced subgoal is not a child. All the original hints are available to it. Thus, if it contains (SWAP A), the hint will fire and supply the hypothesis, producing "[1]Subgoal 1'". This may be unnecessary, as the hypothesis might already be present in "[1]Subgoal 1". In this case, no harm is done. The hint won't fire on "[1]Subgoal 1" because it is a child of "[1]Subgoal 1" and the hint fired on that.  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-4, Next: USING-COMPUTED-HINTS-5, Prev: USING-COMPUTED-HINTS-3, Up: MISCELLANEOUS USING-COMPUTED-HINTS-4 Computing the Hints So far we have used computed hints only to compute when a fixed set of keys and values are to be used as a hint. But computed hints can, of course, compute the set of keys and values. You might, for example, write a hint that recognizes when a clause "ought" to be provable by a :BDD hint and generate the appropriate hint. You might build in a set of useful lemmas and check to see if the clause is provable :BY one of them. You can keep all function symbols disabled and use computed hints to compute which ones you want to :EXPAND. In general, you can write a theorem prover for use in your hints, provided you can get it to do its job by directing our theorem prover. Suppose for example we wish to find every occurrence of an instance of (SWAP x) and provide the corresponding instance of ALL-SWAPS-HAVE-THE-PROPERTY. Obviously, we must explore the clause looking for instances of (SWAP x) and build the appropriate instances of the lemma. We could do this in many different ways, but below we show a general purpose set of utilities for doing it. The functions are not defined in ACL2 but could be defined as shown. Our plan is: (1) Find all instances of a given pattern (term) in a clause, obtaining a set of substitutions. (2) Build a set of :instance expressions for a given lemma name and set of substitutions. (3) Generate a :use hint for those instances when instances are found. The pair of functions below find all instances of a given pattern term in either a term or a list of terms. The functions each return a list of substitutions, each substitution accounting for one of the matches of pat to a subterm. At this level in ACL2 substitutions are lists of pairs of the form (var . term). All terms mentioned here are presumed to be in translated form. The functions take as their third argument a list of substitutions accumulated to date and add to it the substitutions produced by matching pat to the subterms of the term. We intend this accumulator to be nil initially. If the returned value is nil, then no instances of pat occurred. (mutual-recursion (defun find-all-instances (pat term alists) (declare (xargs :mode :program)) (mv-let (instancep alist) (one-way-unify pat term) (let ((alists (if instancep (add-to-set-equal alist alists) alists))) (cond ((variablep term) alists) ((fquotep term) alists) (t (find-all-instances-list pat (fargs term) alists)))))) (defun find-all-instances-list (pat list-of-terms alists) (declare (xargs :mode :program)) (cond ((null list-of-terms) alists) (t (find-all-instances pat (car list-of-terms) (find-all-instances-list pat (cdr list-of-terms) alists)))))) Caveat: The following aside has nothing to do with computed hints. Does an instance of (CAR (CDR x)) occur in ((LAMBDA (V) (CAR V)) (CDR A))? It does if one beta-reduces the lambda-expression to (CAR (CDR A)); the appropriate substitution is to replace x by A. But the definition of find-all-instances above does not find this instance because it does not do beta-reduction. We now turn our attention to converting a list of substitutions into a list of lemma instances, each of the form (:INSTANCE name (var1 term1) ... (vark termk)) as written in :use hints. In the code shown above, substitutions are lists of pairs of the form (var . term), but in lemma instances we must write "doublets." So here we show how to convert from one to the other: (defun pairs-to-doublets (alist) (declare (xargs :mode :program)) (cond ((null alist) nil) (t (cons (list (caar alist) (cdar alist)) (pairs-to-doublets (cdr alist)))))) Now we can make a list of lemma instances: (defun make-lemma-instances (name alists) (declare (xargs :mode :program)) (cond ((null alists) nil) (t (cons (list* :instance name (pairs-to-doublets (car alists))) (make-lemma-instances name (cdr alists)))))) Finally, we can package it all together into a hint function. The function takes a pattern, pat, which must be a translated term, the name of a lemma, name, and a clause. If some instances of pat occur in clause, then the corresponding instances of name are :USEd in the computed hint. Otherwise, the hint does not apply. (defun add-corresponding-instances (pat name clause) (declare (xargs :mode :program)) (let ((alists (find-all-instances-list pat clause nil))) (cond ((null alists) nil) (t (list :use (make-lemma-instances name alists)))))) The design of this particular hint function makes it important that the variables of the pattern be the variables of the named lemma and that all of the variables we wish to instantiate occur in the pattern. We could, of course, redesign it to allow "free variables" or some sort of renaming. We could now use this hint as shown below: (defthm ... ... :hints ((add-corresponding-instances '(SWAP x) 'ALL-SWAPS-HAVE-THE-PROPERTY clause))) The effect of the hint above is that any time a clause arises in which any instance of (SWAP x) appears, we add the corresponding instance of ALL-SWAPS-HAVE-THE-PROPERTY. So for example, if Subgoal *1/3.5 contains the subterm (SWAP (SWAP A)) then this hint fires and makes the system behave as though the hint: ("Subgoal *1/3.5" :USE ((:INSTANCE ALL-SWAPS-HAVE-THE-PROPERTY (X A)) (:INSTANCE ALL-SWAPS-HAVE-THE-PROPERTY (X (SWAP A))))) had been present.  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-5, Next: USING-COMPUTED-HINTS-6, Prev: USING-COMPUTED-HINTS-4, Up: MISCELLANEOUS USING-COMPUTED-HINTS-5 Debugging Computed Hints We have found that it is sometimes helpful to define hints so that they print out messages to the terminal when they fire, so you can see what hint was generated and which of your computed hints did it. To that end we have defined a macro we sometimes use. Suppose you have a :hints specification such as: :hints (computed-hint-fn (hint-expr id)) If you defmacro the macro below you could then write instead: :hints ((show-hint computed-hint-fn 1) (show-hint (hint-expr id) 2)) with the effect that whenever either hint is fired (i.e., returns non-nil), a message identifying the hint by the marker (1 or 2, above) and the non-nil value is printed. (defmacro show-hint (hint &optional marker) (cond ((and (consp hint) (stringp (car hint))) hint) (t `(let ((marker ,marker) (ans ,(if (symbolp hint) `(,hint id clause world stable-under-simplificationp) hint))) (if ans (prog2$ (cw "~%***** Computed Hint~#0~[~/ (from hint ~x1)~]~%~x2~%~%" (if (null marker) 0 1) marker (cons (string-for-tilde-@-clause-id-phrase id) ans)) ans) nil))))) Note that when show-hint is applied to a hint that is a symbol, e.g., computed-hint-fn, it applies the symbol to the four computed-hint arguments: id, clause, world, and stable-under-simplificationp. If computed-hint-fn is of arity 3 the code above would cause an error. One way to avoid it is to write :hints ((show-hints (computed-hint-fn id clause world) 1) (show-hint (hint-expr id) 2)). If you only use computed hints of arity 3, you might eliminate the occurrence of stable-under-simplificationp in the definition of show-hint above. Putting a show-hint around a common hint has no effect. If you find yourself using this utility let us know and we'll consider putting it into the system itself. But it does illustrate that you can use computed hints to do unusual things.  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-6, Next: USING-COMPUTED-HINTS-7, Prev: USING-COMPUTED-HINTS-5, Up: MISCELLANEOUS USING-COMPUTED-HINTS-6 Using the computed-hint-replacement feature So far none of our computed hints have used the :COMPUTED-HINT-REPLACEMENT feature. We now illustrate that. The :computed-hint-replacement feature can easily lead to loops. So as you experiment with the examples in this section and your own hints using this feature, be ready to interrupt the theorem prover and abort! A non-looping use of the :computed-hint-replacement feature would be a hint like this: (if (certain-terms-present clause) '(:computed-hint-replacement t :in-theory (enable lemma25)) '(:computed-hint-replacement t :in-theory (disable lemma25))) In this hint, if certain terms are present in clause, as determined by the function with the obvious name (here undefined), then this hint enables lemma25 and otherwise disables it. Lemma25 might be a very expensive lemma, e.g., one that matches frequently and has an expensive and rarely established hypothesis. One might wish it enabled only under certain conditions. Recall that theories are inherited by children. So once lemma25 is enabled it "stays" enabled for the children, until disabled; and vice versa. If the :computed-hint-replacement feature were not present and computed hints were always deleted after they had been used, then lemma25 would be left enabled (or disabled) for all the childen produced by the first firing of the hint. But with the arrangement here, every subgoal gets a theory deemed suitable by the hint, and the hint persists. Now we will set up a toy to allow us to play with computed hints to understand them more deeply. To follow the discussion it is best to execute the following events. (defstub wrapper (x) t) (defaxiom wrapper-axiom (wrapper x) :rule-classes nil) Now submit the following event and watch what happens. (thm (equal u v) :hints (`(:use (:instance wrapper-axiom (x a))))) The theorem prover adds (wrapper a) to the goal and then abandons the proof attempt because it cannot prove the subgoal. Since the computed hint is deleted upon use, the hint is not applied to the subgoal (i.e., the child of the goal). What happens if we do the following? (thm (equal u v) :hints (`(:computed-hint-replacement t :use (:instance wrapper-axiom (x a))))) As one might expect, this loops forever: The hint is applied to the child and adds the hypothesis again. When the hint fires, nothing is actually changed, since (wrapper a) is already in the subgoal. So let's change the experiment a little. Let's make the hint add the hypothesis (wrapper p) where p is the first literal of the clause. This is silly but it allows us to explore the behavior of computed hints a little more. (thm (equal u v) :hints (`(:use (:instance wrapper-axiom (x ,(car clause)))))) So in this case, the theorem prover changes the goal to (IMPLIES (WRAPPER (EQUAL U V)) (EQUAL U V)) which then simplifies to (IMPLIES (WRAPPER NIL) (EQUAL U V)) because the concluding equality can be assumed false in the hypothesis (e.g., think of the contrapositive version). Nothing else happens because the hint has been removed and so is not applicable to the child. Now consider the following - and be ready to interrupt it and abort! (thm (equal u v) :hints (`(:computed-hint-replacement t :use (:instance wrapper-axiom (x ,(car clause)))))) This time the hint is not removed and so is applied to the child. So from Goal we get Goal' (IMPLIES (WRAPPER (EQUAL U V)) (EQUAL U V)) and then Goal'' (IMPLIES (AND (WRAPPER (NOT (WRAPPER (EQUAL U V)))) (WRAPPER (EQUAL U V))) (EQUAL U V)) etc. First, note that the hint is repeatedly applied to its children. That is because we wrote :computed-hint-replacement t. But second, note that Goal' is not even being simplified before Goal" is produced from it. If it were being simplified, the (equal u v)'s in the hypotheses would be replaced by nil. This is a feature. It means after a computed hint has fired, other hints are given a chance at the result, even the hint itself unless it is removed from the list of hints. As an exercise, let's arrange for the hint to stay around and be applied indefinitely but with a simplification between each use of the the hint. To do this we need to pass information from one application of the hint to the next, essentially to say "stay around but don't fire." First, we will define a function to use in the hint. This is more than a mere convenience; it allows the hint to "reproduce itself" in the replacement. (defun wrapper-challenge (clause parity) (if parity `(:computed-hint-replacement ((wrapper-challenge clause nil)) :use (:instance wrapper-axiom (x ,(car clause)))) `(:computed-hint-replacement ((wrapper-challenge clause t))))) Note that this function is not recursive, even though it uses its own name. That is because the occurrence of its name is in a quoted constant. Now consider the following. What will it do? (thm (equal u v) :hints ((wrapper-challenge clause t))) First, observe that this is a legal hint because it is a term that mentions only the free variable CLAUSE. When defining hint functions you may sometimes think their only arguments are the four variables id, clause, world, and stable-under-simplificationp. That is not so. But in your hints you must call those functions so that those are the only free variables. Note also that the occurrence of clause inside the :computed-hint-replacement is not an occurrence of the variable clause but just a constant. Just store this note away for a moment. We'll return to it momentarily. Second, the basic cleverness of this hint is that every time it fires it reproduces itself with the opposite parity. When the parity is t it actually changes the goal by adding a hypothesis. When the parity is nil it doesn't change the goal and so allows simplification to proceed - but it swaps the parity back to t. What you can see with this simple toy is that we can use the computed hints to pass information from parent to child. Ok, so what happens when the event above is executed? Try it. You will see that ACL2 applied the hint the first time. It doesn't get around to printing the output because an error is caused before it can print. But here is a blow-by-blow description of what happens. The hint is evaluated on Goal with the clause ((equal u v)). It produces a hint exactly as though we had typed: ("Goal" :use (:instance wrapper-axiom (x (equal u v)))) which is applied to this goal. In addition, it produces the new hints argument :hints ((wrapper-challenge clause nil)). By applying the "Goal" hint we get the new subgoal Goal' (implies (wrapper (equal u v)) (equal u v)) but this is not printed because, before printing it, the theorem prover looks for hints to apply to it and finds (wrapper-challenge clause nil) That is evaluated and produces a hint exactly as though we had typed: ("Goal'" ) and the new hints argument: :hints ((wrapper-challenge clause nil)). But if you supply the hint ("Goal'" ), ACL2 will signal an error because it does not allow you to specify an empty hint! So the definition of wrapper-challenge above is almost correct but fatally flawed. We need a non-empty "no-op" hint. One such hint is to tell the system to expand a term that will always be expanded anyway. So undo wrapper-challenge, redefine it, and try the proof again. Now remember the observation about clause that we asked you to "store" above. The new definition of wrapper-challenge illustrates what we meant. Note that the first formal parameter of wrapper-challenge, below, is no longer named clause but is called cl instead. But the "call" of wrapper-challenge in the replacements is on clause. This may seem to violate the rule that a function definition cannot use variables other than the formals. But the occurrences of clause below are not variables but constants in an object that will eventually be treated as hint term. :ubt wrapper-challenge (defun wrapper-challenge (cl parity) (if parity `(:computed-hint-replacement ((wrapper-challenge clause nil)) :use (:instance wrapper-axiom (x ,(car cl)))) `(:computed-hint-replacement ((wrapper-challenge clause t)) :expand ((atom zzz))))) (thm (equal u v) :hints ((wrapper-challenge clause t))) This time, things go as you might have expected! Goal' is produced and simplified, to Goal'' (implies (wrapper nil) (equal u v)). Simplification gets a chance because when the new hint (wrapper-challenge clause nil) is fired it does not change the goal. But it does change the parity in the hints argument so that before Goal" is simplified again, the hint fires and adds the hypothesis: Goal''' (IMPLIES (AND (WRAPPER (NOT (WRAPPER NIL))) (WRAPPER NIL)) (EQUAL U V)). This simplifies, replacing the first (NOT (WRAPPER NIL)) by NIL, since (WRAPPER NIL) is known to be true here. Thus the goal simplifies to Goal'4' (IMPLIES (WRAPPER NIL) (EQUAL U V)). The process repeats indefinitely. So we succeeded in getting a hint to fire indefinitely but allow a full simplification between rounds.  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-7, Next: USING-COMPUTED-HINTS-8, Prev: USING-COMPUTED-HINTS-6, Up: MISCELLANEOUS USING-COMPUTED-HINTS-7 Using the stable-under-simplificationp flag A problem with the example in using-computed-hints-6 is that exactly one simplification occurs between each (effective) firing of the hint. Much more commonly we wish to fire a hint once a subgoal has become stable under simplification. A classic example of this is when we are dealing with an interpreter for some state machine. We typically do not want the "step" function to open up on the symbolic representation of a state until that state has been maximally simplified. We will illustrate with a simple state machine. Let us start by defining the step function, stp, and the corresponding run function that applies it a given number of times. (defun stp (s) (+ 1 s)) (defun run (s n) (if (zp n) s (run (stp s) (- n 1)))) The step function here is trivial: a state is just a number and the step function increments it. In this example we will not be interested in the theorems we prove but in how we prove them. The formula we will focus on is (thm (equal (run s 7) xxx)) This is not a theorem, of course. But we want to test our advice on non-theorems because we do not want the advice to work only for proofs that succeed. (In the past, we gave advice about using computed hints and that advice caused the theorem prover to run forever when given formulas that it couldn't prove - but most of the time the system is presented with formulas it cannot prove!) Furthermore, without some kind of additional rules, the (run s 7) expression in the conjecture above will not expand at all, because ACL2's heuristics do not approve. In fact, we do not want to take chances that run will be expanded - we want to control its expansion completely. Therefore, disable run. (in-theory (disable run)) Now, what do we want? (That is always a good question to ask!) We want (run s 7) to expand "slowly." In particular, we want it to expand once, to (run (stp s) 6). Then we want the stp to be expanded and fully simplified before the run expression is expanded again. That is, we want to force the expansion of run whenever the goal is stable under simplification. This is sometimes called "staged simplification." We can achieve staged simplification for any given function symbol by defining the functions shown below and then using a simple computed hint: (thm (equal (run s 7) xxx) :hints ((stage run))) By inspecting how stage is defined you can see how to extend it, but we explain as we go. To experiment, you can just paste the definitions (and defmacro) below into your ACL2 shell and then try the thm command. First, define this pair of mutually recursive functions. Find-first-call finds the first call of the function symbol fn in a given term. (mutual-recursion (defun find-first-call (fn term) ; Find the first call of fn in term. (cond ((variablep term) nil) ((fquotep term) nil) ((equal (ffn-symb term) fn) term) (t (find-first-call-lst fn (fargs term))))) (defun find-first-call-lst (fn lst) ; Find the first call of fn in a list of terms. (cond ((endp lst) nil) (t (or (find-first-call fn (car lst)) (find-first-call-lst fn (cdr lst))))))) We will arrange for the computed hint to generate an :EXPAND hint for the first call of fn, whenever the goal becomes stable under simplification. If no call is found, the hint will do nothing. To make sure the hint will not loop indefinitely (for example, by forcing fn to expand only to have the rewriter "fold" it back up again), we will provide the hint with a bound that stops it after some number of iterations. Here is the basic function that creates the expand hint and replaces itself to count down. (defun stage1 (fn max clause flg) ; If the clause is stable under simplification and there is a call of ; fn in it, expand it. But don't do it more than max times. (let ((temp (and flg (find-first-call-lst fn clause)))) (if temp (if (zp max) (cw "~%~%HINT PROBLEM: The maximum repetition count of ~ your STAGE hint been reached without eliminating ~ all of the calls of ~x0. You could supply a larger ~ count with the optional second argument to STAGE ~ (which defaults to 100). But think about what is ~ happening! Is each stage permanently eliminating a ~ call of ~x0?~%~%" fn) `(:computed-hint-replacement ((stage1 ',fn ,(- max 1) clause stable-under-simplificationp)) :expand (,temp))) nil))) Suppose that when stage1 is called, fn is the function we want to expand, max is the maximum number of iterations of this expansion, clause is the current goal clause, and flg is the value of the stable-under-simplificationp flag. Then if clause is stable and we can find a call of fn in it, we ask whether max is exhausted. If so, we print an "error message" to the comment window with cw and return nil (the value of cw). That nil means the hint does nothing. But if max is not yet exhausted, we return a new hint. As you can see above, the hint replaces itself with another stage1 hint with the same fn and a decremented max to be applied to the new clause and the then-current value of stable-under-simplificationp. The hint also contains an :expand directive for the call of fn found. Thus, if the computed hint was: (stage1 'run 5 clause stable-under-simplificationp) and (run s 7) occurs in the clause, then it will generate (:computed-hint-replacement ((stage1 'run 4 clause stable-under-simplificationp)) :expand ((run s 7))) which will in turn replace the old stage1 hint with the new one and will apply :expand ((run s 7)) to the current goal. We can make this more convenient by defining the macro: (defmacro stage (fn &optional (max '100)) `(stage1 ',fn ,max clause stable-under-simplificationp)) Note that the macro allows us to either provide the maximum bound or let it default to 100. Henceforth, we can type (thm (equal (run s 7) xxx) :hints ((stage run))) to stage the opening of run up to 100 times, or we can write (thm (equal (run s 7) xxx) :hints ((stage run 5))) to stage it only 5 times. In the latter example, the system with print a "error message" after the fifth expansion. Note that if we executed (set-default-hints '((stage run))) then we could attack all theorems (involving run) with staged simplification (up to bound 100), without typing an explicit hint. (thm (equal (run s 7) xxx)) Using techniques similar to those above we have implemented "priority phased simplification" and provided it as a book. See community book books/misc/priorities.lisp. This is an idea suggested by Pete Manolios, by which priorities may be assigned to rules and then the simplifier simplifies each subgoal maximally under the rules of a given priority before enabling the rules of the next priority level. The book above documents both how we implement it with computed hints and how to use it. Here is another example of using the stable-under-simplificationp flag to delay certain actions. It defines a default hint, see *note DEFAULT-HINTS::, which will enable non-linear-arithmetic on precisely those goals which are stable-under-simplificationp. It also uses the HISTORY and PSPV variables to determine when toggling non-linear-arithmetic is appropriate. These variables are documented only in the source code. If you start using these variables extensively, please contact the developers of ACL2 or Robert Krug (rkrug@cs.utexas.edu) and let us know how we can help. (defun nonlinearp-default-hint (stable-under-simplificationp hist pspv) (cond (stable-under-simplificationp (if (not (access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp)) '(:computed-hint-replacement t :nonlinearp t) nil)) ((access rewrite-constant (access prove-spec-var pspv :rewrite-constant) :nonlinearp) (if (not (equal (caar hist) 'SETTLED-DOWN-CLAUSE)) '(:computed-hint-replacement t :nonlinearp nil) nil)) (t nil)))  File: acl2-doc-emacs.info, Node: USING-COMPUTED-HINTS-8, Next: USING-ENABLED-RULES, Prev: USING-COMPUTED-HINTS-7, Up: MISCELLANEOUS USING-COMPUTED-HINTS-8 Some Final Comments None of the examples show the use of the variable WORLD, which is allowed in computed hints. There are some (undocumented) ACL2 utilities that might be useful in programming hints, but these utilities need access to the ACL2 logical world (see *note WORLD::). A very useful fact to know is that (table-alist name world) returns an alist representation of the current value of the table named name. The ACL2 source code is littered with :program mode functions for manipulating world. In our source code, the world is usually bound a variable named wrld; so searching our code for that name might be helpful. Using these utilities to look at the WORLD one can, for example, determine whether a symbol is defined recursively or not, get the body and formals of a defined function, or fetch the statement of a given lemma. Because these utilities are not yet documented, we do not expect users to employ WORLD in computed hints. But experts might and it might lead to the formulation of a more convenient language for computed hints. None of our examples illustrated the 7 argument form of a computed hint, (fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX). When used, the variables HIST, PSPV, and CTX, are bound to the clause history, the package of "special variables" governing the clause, and the "error message context." These variables are commonly used throughout our source code but are, unfortunately, undocumented. Again, we expect a few experts will find them useful in developing computed hints. If you start using computed hints extensively, please contact the developers of ACL2 and let us know what you are doing with them and how we can help.  File: acl2-doc-emacs.info, Node: USING-ENABLED-RULES, Next: VERSION, Prev: USING-COMPUTED-HINTS-8, Up: MISCELLANEOUS USING-ENABLED-RULES avoiding :use hints for enabled :rewrite rules Consider the following (admittedly silly) example. (thm (equal (append (append x y) z) (append x y z)) :hints (("Subgoal *1/1" :use cdr-cons))) ACL2's output includes the following warning. ACL2 Warning [Use] in ( THM ...): It is unusual to :USE an enabled :REWRITE or :DEFINITION rule, so you may want to consider disabling (:REWRITE CDR-CONS) in the hint provided for Subgoal *1/1. The warning is saying that if you leave the rewrite rule enabled, ACL2 may simplify away the hypothesis added by the :use hint. We now explain this danger in more detail and show how disabling the rule can solve this problem. Recall (see *note HINTS::) how :use hints work. Such a hint specifies a formula, F, which is based on an existing lemma. Then the indicated goal, G, is replaced by the implication (implies F G). The intention is that the truth of F will help in the simplification of G to T (true). The "[Use]" warning shown above is telling us of the danger that F may be rewritten to T, reducing the implication above to (implies T G) -- thus, sadly, F has disappeared and is not available to help with the simplification of G. Consider the following tiny example. (defun p (x) (cons x x)) (defthm car-p (equal (car (p x)) x)) (in-theory (disable p (:type-prescription p))) (thm (implies (equal (p x1) (p x2)) (equal x1 x2)) :hints (("Goal" :use ((:instance car-p (x x1)) (:instance car-p (x x2)))))) The proof of the final thm form fails, because the new hypotheses are rewritten to t using the :rewrite rule CAR-P, in the manner described above. The following proof log shows the new hypotheses and their disappearance via rewriting. We augment the goal with the hypotheses provided by the :USE hint. These hypotheses can be derived from CAR-P via instantiation. We are left with the following subgoal. Goal' (IMPLIES (AND (EQUAL (CAR (P X1)) X1) (EQUAL (CAR (P X2)) X2)) (IMPLIES (EQUAL (P X1) (P X2)) (EQUAL X1 X2))). By the simple :rewrite rule CAR-P we reduce the conjecture to Goal'' (IMPLIES (EQUAL (P X1) (P X2)) (EQUAL X1 X2)). When we disable the rule CAR-P as follows, the proof succeeds. (thm (implies (equal (p x1) (p x2)) (equal x1 x2)) :hints (("Goal" :use ((:instance car-p (x x1)) (:instance car-p (x x2))) :in-theory (disable car-p)))) In general, then, a solution is to disable the rewrite rule that you are supplying in a :use hint.  File: acl2-doc-emacs.info, Node: VERSION, Next: WATERFALL, Prev: USING-ENABLED-RULES, Up: MISCELLANEOUS VERSION ACL2 Version Number To determine the version number of your copy of ACL2, evaluate the form (@ acl2-version). The value will be a string. For example, ACL2 !>(@ acl2-version) "ACL2 Version 3.4" The part of the string after "ACL2 Version " is of the form x.y or x.y.z, optionally followed by a succession of values in parentheses, where x, y, and z are natural numbers. If z is omitted then it is implicitly 0. We refer to X, y, and z as the "major", "minor", and "incrl" fields, respectively. The incrl field is used for incremental releases. The discussion just below assumes that incremental releases are not employed at the user's site, i.e., the incrl fields are always 0. We remove this assumption when we discuss incremental releases at the end of this documenttation topic. Books are considered certified only in the same version of ACL2 in which the certification was done. The certificate file records the version number of the certifying ACL2 and include-book considers the book uncertified if that does not match the current version number. Thus, each time we release a new version of ACL2, previously certified books should be recertified. Note that there are over 150 constants in the system, most having to do with the fact that ACL2 is coded in ACL2. Many of these, for example *common-lisp-specials-and-constants* and *acl2-exports*, may change from version to version, and this can cause unsoundness. For example, the symbol 'set-difference-eq was added to *acl2-exports* in Version_2.9, so we can certify a book in Version_2.8 containing the following theorem, which is false in Version_2.9. (null (member 'set-difference-eq *acl2-exports*)) Therefore, we need to disallow inclusion of such a book in a Version_2.9 session, which otherwise would allow us to prove nil. Furthermore, it is possible that from one version of the system to another we might change, say, the default values on some system function or otherwise make "intentional" changes to the axioms. It is even possible one version of the system is discovered to be unsound and we release a new version to correct our error. Therefore we adopted the draconian policy that books are certified by a given version of ACL2 and "must" be recertified to be used in other versions. We put "must" in quotes because in fact, ACL2 allows a book that was certified in one ACL2 version to be included in a later version, using include-book. But ACL2 does not allow certify-book to succeed when such an include-book is executed on its behalf. Also, you may experience undesirable behavior if you avoid recertification when moving to a different version. Hence we recommend that you stick to the draconion policy of recertifying books when updating to a new ACL2 version. The string (@ acl2-version) can contain implementation-specific information in addition to the version number. For example, in Macintosh Common Lisp (MCL) (char-code #Newline) is 13, while as far as we know, it is 10 in every other Common Lisp. Our concern is that one could certify a book in an MCL-based ACL2 with the theorem (equal (char-code #Newline) 13) and then include this book in another Lisp and thereby prove nil. So, when a book is certified in an MCL-based ACL2, the book's certificate mentions "MCL" in its version string. Moreover, (@ acl2-version) similarly mentions "MCL" when the ACL2 image has been built on top of MCL. Thus, an attempt to include a book in an MCL-based ACL2 that was certified in a non-MCL-based ACL2, or vice-versa, will be treated like an attempt to include an uncertified book. _Incremental releases._ From time to time, so-called "incremental releases" of ACL2 are made available. These releases are thoroughly tested on at least two platforms; "normal" releases, on the other hand, are thoroughly tested on many more platforms (perhaps a dozen or so) and are accompanied by updates to the ACL2 home page. We provide incremental releases in order to provide timely updates for ACL2 users who want them, without imposing unnecessary burdens on either on the ACL2 implementors or on ACL2 users who prefer to update less frequently. The implementors expect users to update their copies of ACL2 when normal releases are made available, but not necessarily when incremental releases are made available. Incremental releases are accompanied by a bump in the incrl field of the version field, while normal releases are accompanied by a bump in the minor or (much less frequently) major field and zeroing out of the incrl field. Note that incremental releases are full-fledged releases.  File: acl2-doc-emacs.info, Node: WATERFALL, Next: WHY-BRR, Prev: VERSION, Up: MISCELLANEOUS WATERFALL See *note HINTS-AND-THE-WATERFALL::.  File: acl2-doc-emacs.info, Node: WHY-BRR, Next: WORLD, Prev: WATERFALL, Up: MISCELLANEOUS WHY-BRR an explanation of why ACL2 has an explicit brr mode Why isn't brr mode automatically disabled when there are no monitored runes? The reason is that the list of monitored runes is kept in a wormhole state. See *note WORMHOLE:: for more information on wormholes in general. But the fundamental property of the wormhole function is that it is a logical no-op, a constant function that does not take state as an argument. When entering a wormhole, arbitrary information can be passed in (including the external state). That information is used to construct a near copy of the external state and that "wormhole state" is the one with respect to which interactions occur during breaks. But no information is carried by ACL2 out of a wormhole -- if that were allowed wormholes would not be logical no-ops. The only information carried out of a wormhole is in the user's head. Break-rewrite interacts with the user in a wormhole state because the signature of the ACL2 rewrite function does not permit it to modify state. Hence, only wormhole interaction is possible. (This has the additional desirable property that the correctness of the rewriter does not depend on what the user does during interactive breaks within it; indeed, it is logically impossible for the user to affect the course of rewrite.) Now consider the list of monitored runes. Is that kept in the external state as a normal state global or is it kept in the wormhole state? If it is in the external state then it can be inspected within the wormhole but not changed. This is unacceptable; it is common to change the monitored rules as the proof attempt progresses, installing monitors when certain rules are about to be used in certain contexts. Thus, the list of monitored runes must be kept as a wormhole variable. Hence, its value cannot be determined outside the wormhole, where the proof attempt is ongoing. This raises another question: If the list of monitored runes is unknown to the rewriter operating on the external state, how does the rewriter know when to break? The answer is simple: it breaks every time, for every rune, if brr mode is enabled. The wormhole is entered (silently), computations are done within the wormhole state to determine if the user wants to see the break, and if so, interactions begin. For unmonitored runes and runes with false break conditions, the silent wormhole entry is followed by a silent wormhole exit and the user perceives no break. Thus, the penalty for running with brr mode enabled when there are no monitored runes is high: a wormhole is entered on every application of every rune and the user is simply unware of it. The user who has finally unmonitored all runes is therefore strongly advised to carry this information out of the wormhole and to do :brr nil in the external state when the next opportunity arises.  File: acl2-doc-emacs.info, Node: WORLD, Next: WORMHOLE, Prev: WHY-BRR, Up: MISCELLANEOUS WORLD ACL2 property lists and the ACL2 logical database The ACL2 logical world is a data structure that includes all logical content resulting from the commands evaluated, back through and including initialization, but not including commands that have been undone (see *note UBT::). Thus in particular, the world includes a represention of the current logical theory, as well as some extra-logical information such as the values of ACL2 tables. The rest of this topic focuses on the structure of the the ACL2 world and, more generally, the "world" data structure. A "world" is a list of triples, each of the form (sym prop . val), implementing the ACL2 notion of property lists. ACL2 permits the simultaneous existence of many property list worlds. "The world" is often used as a shorthand for "the ACL2 logical world" which is the particular property list world used within the ACL2 system to maintain a database that contiains rules, tables, and so on. Common Lisp provides the notion of "property lists" by which one can attach "properties" and their corresponding "values" to symbols. For example, one can arrange for the 'color property of the symbol 'box-14 to be 'purple and the 'color property of the symbol 'triangle-7 to be 'yellow. Access to property lists is given via the Common Lisp function get. Thus, (get 'box-14 'color) might return 'purple. Property lists can be changed via the special form setf. Thus, (setf (get 'box-14 'color) 'blue) changes the Common Lisp property list configuration so that (get 'box-14 'color) returns 'blue. It should be obvious that ACL2 cannot provide this facility, because Common Lisp's get "function" is not a function of its argument, but instead a function of some implicit state object representing the property list settings for all symbols. ACL2 provides the functions getprop and putprop which allow one to mimic the Common Lisp property list facility. However, ACL2's getprop takes as one of its arguments a list that is a direct encoding of what was above called the "state object representing the property list settings for all symbols." Because ACL2 already has a notion of "state" that is quite distinct from that used here, we call this property list object a "world." A world is just a true list of triples. Each triple is of the form (sym prop . val). This world can be thought of as a slightly elaborated form of association list and getprop is a slightly elaborated form of assoc that takes two keys. When getprop is called on a symbol, s, property p, and world, w, it scans w for the first triple whose sym is s and prop is p and returns the corresponding val. Getprop has two additional arguments, one of which that controls what it returns if no such sym and prop exist in w, and other other of which allows an extremely efficient implementation. To set some property's value for some symbol, ACL2 provides putprop. (putprop sym prop val w) merely returns a new world, w', in which (sym prop . val) has been consed onto the front of w, thus "overwriting" the prop value of sym in w to val and leaving all other properties in w unchanged. One aspect of ACL2's property list arrangment is that it is possible to have many different property list worlds. For example, 'box-14 can have 'color 'purple in one world and can have 'color 'yes in another, and these two worlds can exist simultaneously because getprop is explicitly provided the world from which the property value is to be extracted. The efficiency alluded to above stems from the fact that Common Lisp provides property lists. Using Common Lisp's provisions behind the scenes, ACL2 can "install" the properties of a given world into the Common Lisp property list state so as to make retrieval via getprop very fast in the special case that the world provided to getprop has been installed. To permit more than one installed world, each of which is permitted to be changed via putprop, ACL2 requires that worlds be named and these names are used to distinquish installed versions of the various worlds. At the moment we do not further document getprop and putprop. However, the ACL2 system uses a property list world, named 'current-acl2-world, in which to store the succession of user commands and their effects on the logic. This world is often referred to in our documentation as "the world" though it should be stressed that the user is permitted to have worlds and ACL2's is in no way distinguished except that the user is not permitted to modify it except via event commands. The ACL2 world is part of the ACL2 state and may be obtained via (w state). *Warning*: The ACL2 world is very large. Its length as of this writing (Version 2.5) is over 40,000 and it grows with each release. Furthermore, some of the values stored in it are pointers to old versions of itself. Printing (w state) is something you should avoid because you likely will not have the patience to await its completion. For these practical reasons, the only thing you should do with (w state) is provide it to getprop, as in the form (getprop sym prop default 'current-acl2-world (w state)) to inspect properties within it, or to pass it to ACL2 primitives, such as theory functions, where it is expected. Some ACL2 command forms, such as theory expressions (see *note THEORIES::) and the values to be stored in tables (see *note TABLE::), are permitted to use the variable symbol world freely with the understanding that when these forms are evaluated that variable is bound to (w state). Theoretically, this gives those forms complete knowledge of the current logical configuration of ACL2. However, at the moment, few world scanning functions have been documented for the ACL2 user. Instead, supposedly convenient macro forms have been created and documented. For example, (current-theory :here), which is the theory expression which returns the currently enabled theory, actually macroexpands to (current-theory-fn :here world). When evaluated with world bound to (w state), current-theory-fn scans the current ACL2 world and computes the set of runes currently enabled in it.  File: acl2-doc-emacs.info, Node: WORMHOLE, Next: WORMHOLE-DATA, Prev: WORLD, Up: MISCELLANEOUS WORMHOLE ld without state -- a short-cut to a parallel universe Example Form: ; The following form enters a recursive read-eval-print loop on a ; copy of the current state, allowing you to interact with that loop. ; Note that the form does not mention the ACL2 state variable! ; Evaluate the form below. Inside the resulting loop, define some function, ; e.g., with (defun foo (x) x). Then exit with :q and observe, ; e.g., with :pe foo, that the external state did not change. (wormhole 'foo '(lambda (whs) (set-wormhole-entry-code whs :ENTER)) nil '(list 'hello 'there)) General Form: (wormhole name entry-lambda input form :current-package ... ; known package name :ld-skip-proofsp ... ; nil, t or 'include-book :ld-redefinition-action ; nil or '(:a . :b) :ld-prompt ... ; nil, t, or some prompt printer fn :ld-missing-input-ok ... ; nil, t, :warn, or warning message :ld-pre-eval-filter ... ; :all, :query, or some new name :ld-pre-eval-print ... ; nil, t, or :never :ld-post-eval-print ... ; nil, t, or :command-conventions :ld-evisc-tuple ... ; nil or '(alist level length hiding-cars) :ld-error-triples ... ; nil or t :ld-error-action ... ; :return!, :return, :continue, or :error :ld-query-control-alist ; alist supplying default responses :ld-verbose ...) ; nil or t The keyword arguments above are exactly those of ld (see *note LD::) except that three of ld's keyword arguments are missing: the three that specify the channels standard-oi, standard-co and proofs-co, which default in wormhole to ACL2's comment window. There are two ways to create and enter a wormhole: wormhole as described here and the simpler wormhole-eval. We recommend you read this full account of wormholes before using wormhole-eval. Ignoring the use of entry-lambda, wormhole manufactures a named "wormhole state" and calls the general-purpose ACL2 read-eval-print loop ld on it. However, when ld exits, the wormhole evaporates and the function wormhole returns nil. The manufactured state is like the "current" ACL2 state except for two things. First, some information from the last wormhole state of this name is transferred into the new state; this allows a wormhole to maintain some state from one call to the next. Second, some information from the wormhole call itself is transferred into the new state; this allows the wormhole to be sensitive to context. These two changes to the current state are reflected in the settings (@ wormhole-status) and (@ wormhole-input) discussed in detail below. Note that wormhole may be called from environments in which state is not bound. It is still applicative because it always returns nil. There are some restrictions about what can be done inside a wormhole. As you may imagine, we really do not "copy the current state" but rather just keep track of how we modified it and undo those modifications upon exit. An error is signalled if you try to modify state in an unsupported way. For this same reason, wormholes do not allow updating of any user-defined single-threaded objects. See *note STOBJ::. One example wormhole is the implementation of the ACL2 accumulated-persistence facility for tracking the frequency with which rules are tried. To implement this feature directly the theorem prover would have to take the tracking data as an argument and pass it around so that updates could be accumulated. This would greatly clutter the code. Instead, the tracking data is maintained in a wormhole. The theorem prover enters the wormhole to update the data as rules are tried. When you request a display of the data, show-accumulated-persistence enters the wormhole and prints the data. But the data is never available outside that wormhole. The ACL2 system uses a second wormhole to implement the brr facility, allowing the user to interact with the rewriter as rules are applied. We now specify the arguments and behavior of wormhole. The name argument must be a quoted constant and is typically a symbol. It will be the "name" of the wormhole. A wormhole of that name will be created the first time either wormhole or wormhole-eval is called. Every wormhole name has a "status." The status of a wormhole is stored outside of ACL2; it is inaccessible to the ACL2 user except when in the named wormhole. But the status of a wormhole may be set by the user from within the wormhole. Upon the first call of wormhole or wormhole-eval on a name, the status of that name is nil. But in general you should arrange for the status to be a cons. The status is set by the quoted lambda every time wormhole is called; but it may also be set in the form argument (the first form evaluated in the interactive loop) by assigning to the state global variable wormhole-status, as with (assign wormhole-status ...) or even by the user interacting with the loop if you do not exit the loop with the first form. The car of the cons should be either :ENTER or :SKIP and is called the wormhole's "entry code." The entry code of nil or an unexpectedly shaped status is :ENTER. The cdr of the cons is arbitrary data maintained by you. When wormhole is invoked, the status of the specified name is incorporated into the manufactured wormhole state. In particular, inside the wormhole, the status is the value of the state global variable wormhole-status. That is, inside the wormhole, the status may be accessed by (@ wormhole-status) and set by (assign wormhole-status ...), f-get-global and f-put-global. When ld exits - typically because the form :q was read by ld - the then-current value of wormhole-status is hidden away so that it can be restored when this wormhole is entered again. The rest of the wormhole state is lost. This allows a sequence of entries and exits to a wormhole to maintain some history in the status and this information can be manipulated by ACL2 functions executing inside the wormhole. The second argument to wormhole must be a quoted lambda expression. We explain it later. The third argument, input, may be any term. The value of the term is passed into the manufactured wormhole state, allowing you to pass in information about the calling context. Inside the wormhole, the input is available via (@ wormhole-input). It could be reassigned via (assign wormhole-input ...), but there is no reason to do that. The fourth argument, form, may be any term; when ld is called on the manufactured wormhole state, the first form evaluated by ld will be the value of form. Note that form will be translated by ld. Errors, including guard violations, in the translation or execution of that first form will leave you in the interactive loop of the wormhole state. When used properly, the first form allows you to greet your user before reading the first interactive command or simply to do whatever computation you want to do inside the wormhole and exit silently. We give examples below. Manufacturing a wormhole state is relatively expensive; in addition, the forms executed by ld must be read, translated, and interpreted as with any user type-in. The entry-lambda offers a way to avoid this or, at least, to decide whether to incur that expense. Before the wormhole state is manufactured and entered, the entry-lambda is applied to the current wormhole status with wormhole-eval. That lambda application must produce a new wormhole status, which is stored as the wormhole's status. The entry code for the new status determines whether wormhole actually manufactures a wormhole state and calls ld. If the entry code for that new status is :ENTER the wormhole state is manufactured and entered; otherwise, the new status is simply saved as the most recent status but the wormhole state is not manufactured or entered. Note therefore that the entry-lambda may be used to perform two functions: (a) to determine if it is really necessary to manufacture a state and (b) to update the data in the wormhole status as a function of the old status without invoking ld. The entry-lambda must be a quoted lambda expression of at most one argument. Thus, the argument must be either '(lambda (whs) ) or '(lambda () ) Note the quote. If a formal, e.g., whs, is provided, it must be used as a variable in the lambda body. The lambda-expression may contain free variables, that is, the body may mention variables other than the lambda formal. These free variables are understood in the caller's environment. These conventions allow us to compile the entry-lambda application very efficiently when the guard has been verified. The guard on a call of wormhole is the conjunction of the guards on the arguments conjoined with the guard on the body of the entry-lambda. See *note WORMHOLE-EVAL:: for a discussion of the guard on the lambda-expression. The functions wormhole-statusp, wormhole-entry-code, wormhole-data, set-wormhole-entry-code, set-wormhole-data, and make-wormhole-status may be useful in manipulating entry codes and data in the entry-lambda. Note that you access and manipulate the wormhole's status in two different ways depending on whether you're "outside" of the wormhole applying the quoted lambda or "inside" the read-eval-print loop of the wormhole. OUTSIDE (wormhole-eval): access via the value of the lambda formal and set by returning the new status as the value of the lambda body. INSIDE (ld phase of wormhole): access via (@ wormhole-status), and set via (assign wormhole-status ...). Pragmatic Advice on Designing a Wormhole: Suppose you are using wormholes to implement some extra-logical utility. You must contemplate how you will use your wormhole's status to store hidden information. You might be tempted to exploit the entry code as part of the status. For example, you may think of :ENTER as indicating that your utility is "turned on" and :SKIP as indicating that your utility is "turned off." We advise against such a design. We recommend you base your decisions on the wormhole data. We recommend that you set but not read the wormhole entry code to signal whether you wish to enter a full-fledged wormhole. To use the entry code as a flag overloads it and invites confusion when your facility is "turned off" but you have to enter the wormhole for some reason. For a behind-the-scenes description of how wormholes work, See *note WORMHOLE-IMPLEMENTATION::. Here are some sample situations handled by wormhole-eval and wormhole. Let the wormhole in question be named DEMO. Initially its status is NIL. The functions below all maintain the convention that the status is either nil or of the form (:key . lst), where :key is either :SKIP or :ENTER and lst is a true-list of arbitrary objects. But since there is no way to prevent the user from entering the DEMO wormhole interactively and doing something to the status, this convention cannot be enforced. Thus, the functions below do what we say they do, e.g., remember all the values of x ever seen, only if they're the only functions messing with the DEMO status. On the other hand, the guards of all the functions below can be verified. We have explicitly declared that the guards on the functions below are to be verified, to confirm that they can be. Guard verification is optional but wormholes (and wormhole-eval in particular) are more efficient when guards have been verified. All of the functions defined below return nil. The examples below build on each other. If you really want to understand wormholes we recommend that you evaluate each of the forms below, in the order they are discussed. Q. How do I create a wormhole that prints its status to the comment window? (defun demo-status () (declare (xargs :verify-guards t)) (wormhole-eval 'demo '(lambda (whs) (prog2$ (cw "DEMO status:~%~x0~%" whs) whs)) nil)) Note above that after printing the status to the comment window we return the new (unchanged) status whs. Had we just written the call of cw, which returns nil, the function would print the status and then set it to nil! Q. How do I use a wormhole to collect every symbol, x, passed to the function? (defun demo-collect (x) (declare (xargs :verify-guards t)) (wormhole-eval 'demo '(lambda (whs) (make-wormhole-status whs (wormhole-entry-code whs) (if (symbolp x) (cons x (wormhole-data whs)) (wormhole-data whs)))) nil)) We could have also defined this function this way: (defun demo-collect (x) (declare (xargs :verify-guards t)) (if (symbolp x) (wormhole-eval 'demo '(lambda (whs) (set-wormhole-data whs (cons x (wormhole-data whs)))) nil) nil)) Both versions always return nil and both versions collect into the wormhole data field just the symbols x upon which demo-collect is called. Q. How do I use demo-collect? Below is a function that maps over a list and computes its length. But it has been annotated with a call to demo-collect on every element. (defun my-len (lst) (if (endp lst) 0 (+ 1 (prog2$ (demo-collect (car lst)) (my-len (cdr lst)))))) Thus, for example: ACL2 !>(my-len '(4 temp car "Hi" rfix)) 5 ACL2 !>(demo-status) DEMO status: (:ENTER RFIX CAR TEMP) NIL ACL2 !> Q. How do I set the entry code to :ENTER or :SKIP according to whether name is a member-equal of the list of things seen so far? Note that we cannot check this condition outside the wormhole, because it depends on the list of things collected so far. We make the decision inside the lambda-expression. Note that we explicitly check that the guard of member-equal is satisfied by the current wormhole status, since we cannot rely on the invariant that no other function interferes with the status of the DEMO wormhole. In the case that the status is "unexpected" we act like the status is nil and set it to (:SKIP . NIL). (defun demo-set-entry-code (name) (declare (xargs :verify-guards t)) (wormhole-eval 'demo '(lambda (whs) (if (true-listp (wormhole-data whs)) (set-wormhole-entry-code whs (if (member-equal name (wormhole-data whs)) :ENTER :SKIP)) '(:SKIP . NIL))) nil)) Thus ACL2 !>(demo-set-entry-code 'monday) NIL ACL2 !>(demo-status) DEMO status: (:SKIP RFIX CAR TEMP) NIL ACL2 !>(demo-set-entry-code 'rfix) NIL ACL2 !>(demo-status) DEMO status: (:ENTER RFIX CAR TEMP) NIL ACL2 !> Q. Suppose I want to collect every symbol and then, if the symbol has an ABSOLUTE-EVENT-NUMBER property in the ACL2 logical world, print the defining event with :pe and then enter an interactive loop; but if the symbol does not have an ABSOLUTE-EVENT-NUMBER, don't print anything and don't enter an interactive loop. Here it is not important to know what ABSOLUTE-EVENT-NUMBER is; this example just shows that we can use a wormhole to access the ACL2 logical world, even in a function that does not take the state as an argument. In the code below, we use wormhole instead of wormhole-eval, because we might have to access the logical world and enter an interactive loop. But for efficiency we do as much as we can inside the entry lambda, where we can check whether x is symbol and collect it into the data field of the wormhole status. Note that if we collect x, we also set the entry code to :ENTER. If we don't collect x, we set the entry code to :SKIP. (defun collect-symbols-and-print-events (x) (declare (xargs :guard t)) (wormhole 'demo '(lambda (whs) (if (symbolp x) (make-wormhole-status whs :ENTER (cons x (wormhole-data whs))) (set-wormhole-entry-code whs :SKIP))) ; The wormhole will not get past here is unless the entry code is ; :ENTER. If we get past here, we manufacture a state, put ; x into (@ wormhole-input) and call ld in such a way that the ; first form executed is the quoted if-expression below. x '(if (getprop (@ wormhole-input) 'absolute-event-number nil 'CURRENT-ACL2-WORLD (w state)) (er-progn (mv-let (col state) (fmt "~%Entering a wormhole on the event name ~x0~%" (list (cons #\0 (@ wormhole-input))) *standard-co* state nil) (declare (ignore col)) (value nil)) (pe (@ wormhole-input)) (set-ld-prompt 'wormhole-prompt state) (value :invisible)) (value :q)) :ld-verbose nil :ld-prompt nil)) The "first form" (the if) asks whether the wormhole-input (i.e., x) has an ABSOLUTE-EVENT-NUMBER property. If so, it enters an er-progn to perform a sequence of commands, each of which returns an ACL2 error triple (see *note PROGRAMMING-WITH-STATE::). The first form uses fmt to print a greeting. Since fmt returns (mv col state) and we must return an error triple, we embed the fmt term in an (mv-let (col state) ... (value nil)). The macro value takes an object and returns a "normal return" error triple. The second form in the er-progn uses the ACL2 history macro pe (see *note PE::) to print the defining event for a name. The third form sets the prompt of this read-eval-print loop to the standard function for printing the wormhole prompt. We silenced the printing of the prompt when we called ld, thanks to the :ld-prompt nil keyword option. More on this below. The fourth form returns the error triple value :invisible as the value of the first form. This prevents ld from printing the value of the first form. Since we have not exited ld, that function just continues by reading the next form from the comment window. The user perceives this as entering a read-eval-print loop. We continue in the loop until the user types :q. On the other branch of the if, if the symbol has no ABSOLUTE-EVENT-NUMBER property, we execute the form (value :q), which is the programming equivalent of typing :q. That causes the ld to exit. The ld special variables set in the call to wormhole and further manipulated inside the first form to ld may require explanation. By setting :ld-verbose to nil, we prevent ld from printing the familiar ACL2 banner when ld is called. If :ld-verbose nil is deleted, then you would see something like ACL2 Version 4.0. Level 2. ... Type (good-bye) to quit completely out of ACL2. before the first form is read and evaluated. By setting :ld-prompt to nil we prevent ld from printing the prompt before reading and evaluating the first form. As this example shows, to use full-blown wormholes you must understand the protocol for using wormhole status to control whether a wormhole state is manufactured for ld and you must also understand programming with state and the effects of the various ld "special variables." From the discussion above we see that wormholes can be used to create formatted output without passing in the ACL2 state. For examples see *note CW::, in particular the discussion at the end of that documentation topic.  File: acl2-doc-emacs.info, Node: WORMHOLE-DATA, Next: WORMHOLE-ENTRY-CODE, Prev: WORMHOLE, Up: MISCELLANEOUS WORMHOLE-DATA determines the wormhole data object from a wormhole status object General Form: (wormhole-data whs) See *note WORMHOLE::. Returns the wormhole data from a well-formed wormhole status whs. If whs is nil or not well-formed, the data is nil.  File: acl2-doc-emacs.info, Node: WORMHOLE-ENTRY-CODE, Next: WORMHOLE-EVAL, Prev: WORMHOLE-DATA, Up: MISCELLANEOUS WORMHOLE-ENTRY-CODE determines the wormhole entry code from a wormhole status object General Form: (wormhole-entry-code whs) See *note WORMHOLE::. Returns :ENTER or :SKIP given a well-formed wormhole status whs. If whs is nil or not well-formed, the entry code is :ENTER.  File: acl2-doc-emacs.info, Node: WORMHOLE-EVAL, Next: WORMHOLE-IMPLEMENTATION, Prev: WORMHOLE-ENTRY-CODE, Up: MISCELLANEOUS WORMHOLE-EVAL state-saving without state -- a short-cut to a parallel universe Example Form: (wormhole-eval 'demo '(lambda (whs) (set-wormhole-data whs (cons (cons name info) (wormhole-data whs)))) (prog2$ info name)) General Form: (wormhole-eval name lambda varterm) where name must be a quoted wormhole name and lambda must be a quoted lambda-expression. The lambda-expression must have at most one formal parameter but the body of the lambda-expression may contain other variables. Note that in the example form given above, the lambda has one formal, whs, and uses name and info freely. Note that the lambda is quoted. The third argument of wormhole-eval, varterm, is an arbitrary term that should mention all of the free variables in the lambda-expression. That term establishes your "right" to refer to those free variables in the environment in which the wormhole-eval expression occurs. The value of varterm is irrelevant and if you provide nil ACL2 will automatically provide a suitable term, namely a prog2$ form like the one shown in the example above. Aside: Exception for ACL2(p) (see *note PARALLELISM::) to the irrelevance of varterm. By default, calls of wormhole-eval employ a lock, *wormhole-lock*. To avoid such a lock, include the symbol :NO-WORMHOLE-LOCK in varterm; for example, you might replace a last argument of nil in wormhole-eval by :NO-WORMHOLE-LOCK. End of Aside. See *note WORMHOLE:: for a full explanation of wormholes. Most relevant here is that every wormhole has a name and a status. The status is generally a cons pair whose car is the keyword :ENTER or the keyword :SKIP and whose cdr is an arbitrary object used to store information from one wormhole call to the next. Here is a succinct summary of wormhole-eval. If the lambda-expression has a local variable, wormhole-eval applies the lambda-expression to the wormhole status of the named wormhole and remembers the value as the new wormhole status. If the lambda has no formal parameter, the lambda is applied to no arguments and the value is the new status. Wormhole-eval returns nil. Thus, the formal parameter of the lambda-expression, if provided, denotes the wormhole's hidden status information; the value of the lambda is the new status and is hidden away. The guard of a wormhole-eval call is the guard of the body of the lambda-expression, with a fresh variable symbol used in place of the formal so that no assumptions are possible about the hidden wormhole status. If the guard of a wormhole-eval is verified, the call is macroexpanded inline to the evaluation of the body in a suitable environment. Thus, it can be a very fast way to access and change hidden state information, but the results must remain hidden. To do arbitrary computations on the hidden state (i.e., to access the ACL2 state or logical world or to interact with the user) see *note WORMHOLE::. Functions that are probably useful in the body of the lambda or the guard of a function using wormhole-eval include the following: wormhole-statusp, wormhole-entry-code, wormhole-data, set-wormhole-entry-code, set-wormhole-data, and make-wormhole-status. See *note WORMHOLE:: for a series of example uses of wormhole-eval and wormhole. For a behind-the-scenes description of how wormholes work, See *note WORMHOLE-IMPLEMENTATION::.  File: acl2-doc-emacs.info, Node: WORMHOLE-IMPLEMENTATION, Next: WORMHOLE-P, Prev: WORMHOLE-EVAL, Up: MISCELLANEOUS WORMHOLE-IMPLEMENTATION notes on how wormholes are implemented What happens when you call wormhole? Recall that a typical call of the function looks like this: (wormhole 'name '(lambda (whs) ...) input form :ld-verbose ... ...) A brief recap of the advertised semantics for wormhole establishes our terminology: When the above wormhole is evaluated, the lambda-expression is applied to the wormhole's status and the result is stored as the new status. Then, if the entry-code of the new status is :ENTER, ld is invoked on a copy of the "current state" with the specified ld- "special variables;" output is directed to the comment window. In that copy of the state, the state-global variable wormhole-input is set to the value of input and the state-global variable wormhole-status is set to the (new) status computed by the lambda-expression. Thus, inside the wormhole, (@ wormhole-input) returns the list of inputs, (@ wormhole-status) returns the current status, and (assign wormhole-status ...) sets the wormhole's status. The first form executed by the ld is the value of form and unless that form returns (value :q), causing the ld to quit, the ld proceeds to take subsequent input from the comment window. Upon exiting from ld, the wormhole state "evaporates." The wormhole's status upon exit is remembered and restored the next time the wormhole is entered. Here is what really happens. Each wormhole's status is recorded in an alist stored in a Common Lisp global variable named *wormhole-status-alist*. This variable is not part of the ACL2 state. If you exit the ACL2 loop with :q you can inspect the value of *wormhole-status-alist*. When the lambda-expression is evaluated it is applied to the value associated with name in the alist and the result is stored back into that alist. This step is performed by wormhole-eval. To make things more efficient, wormhole-eval is just a macro that expands into a let that binds the lambda formal to the current status and whose body is the lambda body. Guard clauses are generated from the body, with one exception: the lambda formal is replaced by a new variable so that no prior assumptions are available about the value of the the wormhole status. If the newly computed status has an entry code of :ENTER ld will be invoked. But we don't really copy state, of course. Instead we will invoke ld on the live state, which is always available in the von Neumann world in which ACL2 is implemented. To give the illusion of copying state, we will undo changes to the state upon exiting. To support this, we do two things just before invoking ld: we bind a Common Lisp special variable is to t to record that ACL2 is in a wormhole, and we initialize an accumulator that will be used to record state changes made while in the wormhole. Then ld is invoked, with first argument, standard-oi, being set to (cons form *standard-oi*). According to the standard semantics of ld, this reads and evaluates form and then the forms in the specified channel. The standard channels are directed to and from the terminal, which is the physical realization of the comment window. All state modifying functions of ACL2 are sensitive to the special variable that indicates that evaluation is in a wormhole. Some ACL2 state-modifying functions (e.g., those that modify the file system like write-byte$) are made to cause an error if invoked inside a wormhole on a file other than the terminal. Others, like f-put-global (the function behind such features as assign and maintenance of the ACL2 logical world by such events as defun and defthm) are made to record the old value of the state component being changed; these records are kept in the accumulator initialized above. Upon exit from ld for any reason, the final value of (@ wormhole-status) is stored in *wormhole-status-alist* and then the accumulator is used to "undo" all the state changes. Wormhole always returns nil.  File: acl2-doc-emacs.info, Node: WORMHOLE-P, Next: WORMHOLE-STATUSP, Prev: WORMHOLE-IMPLEMENTATION, Up: MISCELLANEOUS WORMHOLE-P predicate to determine if you are inside a wormhole See *note WORMHOLE:: for a discussion of wormholes. (Wormhole-p state) returns (mv nil t state) when evaluated inside a wormhole, else (mv nil nil state).  File: acl2-doc-emacs.info, Node: WORMHOLE-STATUSP, Next: XARGS, Prev: WORMHOLE-P, Up: MISCELLANEOUS WORMHOLE-STATUSP predicate recognizing well-formed wormhole status object General Form: (wormhole-statusp whs) See *note WORMHOLE::. This predicate is useful in guards for wormholes. It checks whether whs is either nil or a cons whose car is :ENTER or :SKIP.  File: acl2-doc-emacs.info, Node: XARGS, Prev: WORMHOLE-STATUSP, Up: MISCELLANEOUS XARGS extra arguments, for example to give hints to defun Common Lisp's defun function does not easily allow one to pass extra arguments such as "hints". ACL2 therefore supports a peculiar new declaration (see *note DECLARE::) designed explicitly for passing additional arguments to defun via a keyword-like syntax. This declaration can also be used with defmacro, but only for xargs keyword :GUARD; so we restrict attention below to use of xargs in defun events. The following declaration is nonsensical but does illustrate all of the xargs keywords for defun (which are the members of the list *xargs-keywords*). (declare (xargs :guard (symbolp x) :guard-hints (("Goal" :in-theory (theory batch1))) :guard-debug t :hints (("Goal" :in-theory (theory batch1))) :measure (- i j) :ruler-extenders :basic :mode :logic :non-executable t :normalize nil :otf-flg t :stobjs ($s) :verify-guards t :split-types t :well-founded-relation my-wfr)) General Form: (xargs :key1 val1 ... :keyn valn) where the keywords and their respective values are as shown below. Note that once "inside" the xargs form, the "extra arguments" to defun are passed exactly as though they were keyword arguments. :GUARD Value is a term involving only the formals of the function being defined. The actual guard used for the definition is the conjunction of all the guards and types (see *note DECLARE::) declared. :GUARD-HINTS Value: hints (see *note HINTS::), to be used during the guard verification proofs as opposed to the termination proofs of the defun. :GUARD-DEBUG Value: nil by default, else directs ACL2 to decorate each guard proof obligation with hypotheses indicating its sources. See *note GUARD-DEBUG::. :HINTS Value: hints (see *note HINTS::), to be used during the termination proofs as opposed to the guard verification proofs of the defun. :MEASURE Value is a term involving only the formals of the function being defined. This term is indicates what is getting smaller in the recursion. The well-founded relation with which successive measures are compared is o<. Also allowed is a special case, (:? v1 ... vk), where (v1 ... vk) enumerates a subset of the formal parameters such that some valid measure involves only those formal parameters. However, this special case is only allowed for definitions that are redundant (see *note REDUNDANT-EVENTS::) or are executed when skipping proofs (see *note SKIP-PROOFS::). Another special case is :MEASURE nil, which is treated the same as if :MEASURE is omitted. :RULER-EXTENDERS For recursive definitions (possibly mutually recursive), value controls termination analysis and the resulting stored induction scheme. See *note RULER-EXTENDERS:: for a discussion of legal values and their effects. :MODE Value is :program or :logic, indicating the defun mode of the function introduced. See *note DEFUN-MODE::. If unspecified, the defun mode defaults to the default defun mode of the current world. To convert a function from :program mode to :logic mode, see *note VERIFY-TERMINATION::. :NON-EXECUTABLE Value is normally t or nil (the default). Rather than stating :non-executable t directly, which requires :logic mode and that the definitional body has a certain form, we suggest using the macro defun-nx or defund-nx; see *note DEFUN-NX::. A third value of :non-executable for advanced users is :program, which is generated by expansion of defproxy forms; see *note DEFPROXY::. For another way to deal with non-executability, see *note NON-EXEC::. :NORMALIZE Value is a flag telling defun whether to propagate if tests upward. Since the default is to do so, the value supplied is only of interest when it is nil. (See *note DEFUN::). :OTF-FLG Value is a flag indicating "onward through the fog" (see *note OTF-FLG::). :STOBJS Value is either a single stobj name or a true list of stobj names (see *note STOBJ:: and see *note DEFSTOBJ::, and perhaps see *note DEFABSSTOBJ::). Every stobj name among the formals of the function must be listed, if the corresponding actual is to be treated as a stobj. That is, if a function uses a stobj name as a formal parameter but the name is not declared among the :stobjs then the corresponding argument is treated as ordinary. The only exception to this rule is state: whether you include it or not, state is always treated as a single-threaded object. This declaration has two effects. One is to enforce the syntactic restrictions on single-threaded objects. The other is to strengthen the guard of the function being defined so that it includes conjuncts specifying that each declared single-threaded object argument satisfies the recognizer for the corresponding single-threaded object. :VERIFY-GUARDS Value is t or nil, indicating whether or not guards are to be verified upon completion of the termination proof. This flag should only be t if the :mode is unspecified but the default defun mode is :logic, or else the :mode is :logic. :SPLIT-TYPES Value is t or nil, indicating whether or not types are to be proved from the guards. The default is nil, indicating that type declarations (see *note DECLARE::) contribute to the guards. If the value is t, then instead, the expressions corresponding to the type declarations (see *note TYPE-SPEC::) are conjoined into a "split-type expression," and guard verification insists that this term is implied by the specified :guard. Suppose for example that a definition has the following declare form. (declare (xargs :guard (p x y) :split-types t) (type integer x) (type (satisfies good-bar-p) y)) Then for guard verification, (p x y) is assumed, and in addition to the usual proof obligations derived from the body of the definition, guard verification requires a proof that (p x y) implies both (integerp x) and (good-bar-p y). See community book demos/split-types-examples.lisp for small examples. :WELL-FOUNDED-RELATION Value is a function symbol that is known to be a well-founded relation in the sense that a rule of class :well-founded-relation has been proved about it. See *note WELL-FOUNDED-RELATION::.  File: acl2-doc-emacs.info, Node: OTHER, Next: PARALLELISM, Prev: MISCELLANEOUS, Up: Top OTHER other commonly used top-level functions * Menu: * ACCUMULATED-PERSISTENCE:: to get statistics on which runes are being tried * ACL2-DEFAULTS-TABLE:: a table specifying certain defaults, e.g., the default defun-mode * ACL2-HELP:: the acl2-help mailing list * CERTIFY-BOOK!:: a variant of certify-book * COMMAND-LINE:: handling of command-line arguments when ACL2 is invoked * CW-GSTACK:: debug a rewriting loop or stack overflow * DEAD-EVENTS:: using proof supporters to identify dead code and unused theorems * DISASSEMBLE$:: disassemble a function * DMR:: dynamically monitor rewrites and other prover activity * DYNAMICALLY-MONITOR-REWRITES:: See *note DMR::. * EXIT:: quit entirely out of Lisp * GOOD-BYE:: quit entirely out of Lisp * GUARD-OBLIGATION:: the guard proof obligation * IN-PACKAGE:: select current package * LD:: the ACL2 read-eval-print loop, file loader, and command processor * PRINT-GV:: print a form whose evaluation caused a guard violation * PROPS:: print the ACL2 properties on a symbol * PSO:: show the most recently saved output * PSO!:: show the most recently saved output, including proof-tree output * PSOF:: show the most recently saved output * PSOG:: show the most recently saved output in gag-mode * PSTACK:: seeing what the prover is up to * Q:: quit ACL2 (type :q) --- reenter with (lp) * QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP:: (advanced topic) controlling a heuristic in the prover's clausifier * QUIT:: quit entirely out of Lisp * REBUILD:: a convenient way to reconstruct your old state * REDO-FLAT:: redo on failure of a progn, encapsulate, or certify-book * RESET-LD-SPECIALS:: restores initial settings of the ld specials * SAVE-EXEC:: save an executable image and a wrapper script * SET-ACCUMULATED-PERSISTENCE:: See *note ACCUMULATED-PERSISTENCE::. * SHARP-BANG-READER:: package prefix that is not restricted to symbols * SHARP-COMMA-READER:: DEPRECATED read-time evaluation of constants * SHARP-DOT-READER:: read-time evaluation of constants * SHARP-U-READER:: allow underscore characters in numbers * SHOW-ACCUMULATED-PERSISTENCE:: See *note ACCUMULATED-PERSISTENCE::. * SKIP-PROOFS:: skip proofs for a given form --- a quick way to introduce unsoundness * THM:: prove a theorem * TOP-LEVEL:: evaluate a top-level form as a function body * TRANS:: print the macroexpansion of a form * TRANS!:: print the macroexpansion of a form without single-threadedness concerns * TRANS1:: print the one-step macroexpansion of a form * VERIFY-GUARDS-FORMULA:: view the guard proof obligation, without proving it * WALKABOUT:: explore an ACL2 cons tree * WITH-PROVER-STEP-LIMIT:: limit the number of steps for proofs * WITH-PROVER-TIME-LIMIT:: limit the time for proofs * WOF:: direct standard output and proofs output to a file Related topics other than immediate subtopics: * BREAK$:: cause an immediate Lisp break * CERTIFY-BOOK:: how to produce a certificate for a book * STATE:: the von Neumannesque ACL2 state object * TIME$:: time an evaluation * TRACE:: tracing functions in ACL2 This section contains an assortment of top-level functions that fit into none of the other categories and yet are suffiently useful as to merit "advertisement" in the :help command.  File: acl2-doc-emacs.info, Node: ACCUMULATED-PERSISTENCE, Next: ACL2-DEFAULTS-TABLE, Prev: OTHER, Up: OTHER ACCUMULATED-PERSISTENCE to get statistics on which runes are being tried Useful Forms: (accumulated-persistence t) ; Activate statistics gathering. (accumulated-persistence :all) ; As above, ``enhanced'' (see below) (show-accumulated-persistence :frames) ; Display statistics ordered by (show-accumulated-persistence :tries) ; frames built, times tried, (show-accumulated-persistence :ratio) ; or their ratio. (accumulated-persistence nil) ; Deactivate. Advanced forms: (show-accumulated-persistence :frames-s) ; The `s', `f', and `a' suffixes (show-accumulated-persistence :frames-f) ; stand for `success' (`useful'), (show-accumulated-persistence :frames-a) ; `failure' (`useless'), and `all', (show-accumulated-persistence :tries-s) ; respectively. The only effect of (show-accumulated-persistence :tries-f) ; the `s' and `f' versions is to (show-accumulated-persistence :tries-a) ; sort first by useful or useless ; applications, respectively (see ; below). The `a' versions avoid ; showing the useful/useless ; breakdown. (show-accumulated-persistence :runes) ; Just show runes alphabetically. * Menu: * ACCUMULATED-PERSISTENCE-SUBTLETIES:: some subtle aspects of the counting done by accumulated-persistence Related topics other than immediate subtopics: * DMR:: dynamically monitor rewrites and other prover activity Note: set-accumulated-persistence is equivalent to accumulated-persistence. See the end of this item for a discussion of "enhanced statistics gathering," which can be useful for more fine-grained proof debugging. Generally speaking, the more ACL2 knows, the slower it runs. That is because the search space grows with the number of alternative rules. Often, the system tries to apply rules that you have forgotten were even there, if you knew about them in the first place! "Accumulated-persistence" is a statistic (originally developed for Nqthm) that helps you identify the rules that are causing ACL2's search space to explode. For other proof debugging utilities, see *note BREAK-REWRITE:: and see *note DMR::. Accumulated persistence tracking can be turned on or off. It is generally off. When on, proofs may take perhaps 50% more time than otherwise! But some useful numbers are collected. When it is turned on, by ACL2 !>(accumulated-persistence t) an accumulation site is initialized and henceforth data about which rules are being tried is accumulated into that site. That accumulated data can be displayed with show-accumulated-persistence, as described in detail below. When accumulated persistence is turned off, with (accumulated-persistence nil), the accumulation site is wiped out and the data in it is lost. The "accumulated persistence" of a rune is the number of runes the system has attempted to apply (since accumulated persistence was last activated) while the given rune was being tried. Consider a :rewrite rule named rune. For simplicity, let us imagine that rune is tried only once in the period during which accumulated persistence is being monitored. Recall that to apply a rewrite rule we must match the left-hand side of the conclusion to some term we are trying to rewrite, establish the hypotheses of rune by rewriting, and, if successful, then rewrite the right-hand side of the conclusion. We say rune is "being tried" from the time we have matched its left-hand side to the time we have either abandoned the attempt or finished rewriting its right-hand side. (By "match" we mean to include any loop-stopper requirement; see *note LOOP-STOPPER::.) During that period of time other rules might be tried, e.g., to establish the hypotheses. The rules tried while rune is being tried are "billed" to rune in the sense that they are being considered here only because of the demands of rune. Thus, if no other rules are tried during that period, the accumulated persistence of rune is 1 -- we "bill" rune once for its own application attempt. If, on the other hand, we tried 10 rules on behalf of that application of rune, then rune's accumulated persistence would be 11. One way to envision accumulated persistence is to imagine that every time a rune is tried it is pushed onto a stack. The rules tried on behalf of a given application of a rune are thus pushed and popped on the stack above that rune. A lot of work might be done on its behalf -- the stack above the rune grows and shrinks repeatedly as the search continues for a way to use the rune. All the while, the rune itself "persists" in the stack, until we finish with the attempt to apply it, at which time we pop it off. The accumulated persistence of a rune application is thus the number of stack frames built while that rune was on the stack. Note that accumulated persistence is tallied whether or not the attempt to apply a rune is successful. Each of the rules tried on its behalf might have failed and the attempt to apply the rune might have also failed. The ACL2 proof script would make no mention of the rune or the rules tried on its behalf because they did not contribute to the proof. But time was spent pursuing the possible application of the rune and accumulated persistence is a measure of that time. A high accumulated persistence might come about in two extreme ways. One is that the rule causes a great deal of work every time it is tried. The other is that the rule is "cheap" but is tried very often. We therefore keep track of the number of times each rule is tried as well as its persistence. The ratio between the two is the average amount of work done on behalf of the rule each time it is tried. When the accumulated persistence totals are displayed by the function show-accumulated-persistence we sort them so that the most expensive runes are shown first. We can sort according to one of three basic keys: :frames - the number of frames built on behalf of the rune :tries - the number of times the rune was tried :ratio - frames built per try The key simply determines the order in which the information is presented. If no argument is supplied to show-accumulated-persistence, :frames is used. The display breaks each total into "useful" and "useless" subtotals. A "useful" rule try is one that is viewed as contributing to the progress of the proof, and the rest are "useless" rule applications. For example, if a :rewrite rule is tried but its hypotheses are not successfully relieved, then that rule application and all work done on behalf of those hypotheses is "useless" work. In general, an attempt to apply a rune is viewed as "useful" unless the attempt fails or the attempt is on the stack (as described above) for a rune application that ultimately fails. A large number of "useless" :frames or :tries along with correspondingly small "useful" counts may suggest runes to consider disabling (see *note DISABLE:: and see *note IN-THEORY::). Thus, here is a more complete list of the arguments that may be supplied to show-accumulated-persistence. Suffixes "s", "f", and "a" are intended to suggest "success" ("useful"), "failure" ("useless"), and "all". :frames - sort by the number of frames built on behalf of the rune :frames-s - as above, but sort by useful applications :frames-f - as above, but sort by useless applications :frames-a - as above, but inhibit display of ``useful'' and ``useless'' subtotals :tries - sort by the number of times the rune was tried :tries-s - as above, but sort by useful applications :tries-f - as above, but sort by useless applications :tries-a - as above, but inhibit display of ``useful'' and ``useless'' subtotals :ratio - sort by frames built per try :useless - show only the runes tried whose tries were all ``useless'' For a given line of the report, every frame credited to a "useful" (respectively, "useless") rule application is considered "useful" (respectively, "useless"). We illustrate with the following example. (progn (defstub hyp (x) t) (defstub concl (x) t) (defstub bad (x) t) (defstub good (x) t) (defaxiom good-ax (implies (good x) (hyp x))) (defaxiom bad-ax (implies (bad x) (hyp x))) (defaxiom hyp-implies-concl (implies (hyp x) (concl x))) ) (accumulated-persistence t) (thm (implies (good x) (concl x))) (show-accumulated-persistence) To prove the thm form, ACL2 attempts to rewrite (concl x) to true by applying rule hyp-implies-concl. It then attempts to establish (hyp x) first by trying rule bad-ax, which fails, and second by trying rule good-ax, which succeeds. As expected, the report labels as "useless" the failure of the attempt to establish the hypothesis, (bad x). -------------------------------- 1 1 ( 1.00) (:REWRITE BAD-AX) 0 0 [useful] 1 1 [useless] -------------------------------- Now consider the top-level application of rule hyp-implies-concl. Even though the above report shows the application of bad-ax as "useless", note that this rule was applied on behalf of the successful ("useful") application of hyp-implies-concl, and hence is incorporated into the "useful" line for hyp-implies-concl, as follows. -------------------------------- 3 1 ( 3.00) (:REWRITE HYP-IMPLIES-CONCL) 3 1 [useful] 0 0 [useless] -------------------------------- In summary: categorization of :frames as "useful" or "useless" is based on whether they support "useful" or "useless" :tries. Note that a rune with high accumulated persistence may not actually be the "culprit." For example, suppose rune1 is reported to have a :ratio of 101, meaning that on the average a hundred and one frames were built each time rune1 was tried. Suppose rune2 has a :ratio of 100. It could be that the attempt to apply rune1 resulted in the attempted application of rune2 and no other rune. Thus, in some sense, rune1 is "cheap" and rune2 is the "culprit" even though it costs less than rune1. If a proof is aborted, then in general, show-accumulated-persistence will only display totals for runes whose attempted application is complete: that is, if the rewriter was in the process of relieving hypotheses for a rule, then information for that rule will not be included in the tally. We say "in general" because, as indicated near the top of the output from show-accumulated-persistence when such incomplete information is omitted, you can get this information by using argument :frames-a or :tries-a. There are other subtleties in how rune applications are tallied, documented elsewhere: see *note ACCUMULATED-PERSISTENCE-SUBTLETIES::. We conclude with a discussion of "enhanced" statistics gathering, which is enabled by supplying accumulated-persistence the argument :ALL: (accumulated-persistence :all) At some additional performance expense (but probably well under a factor of 2 altogether), ACL2 then gathers additional statistics for individual hypotheses of rules as well as their conclusions. To understand how this works, suppose rn is a rune. Then we prepend the keyword :CONC to rn to form what we call its "conclusion xrune", and for its I-th hypothesis we prepend :HYP I to rn to form its I-th "hypothesis xrune." Here, "xrune" is pronounced "ex rune", and is mnemonic for "extended rune." For example, if (REWRITE FOO) is a rune then (:CONC REWRITE FOO) is its conclusion xrune, and (:HYP 2 REWRITE FOO) is a hypothesis xrune corresponding to the second hypothesis of the corresponding rewrite rule. With (accumulated-persistence :all), we instruct ACL2 to track not only runes but also xrunes. Then, (show-accumulated-persistence) will display information for all xrunes in a format that we consider to be "raw", in the sense that data for xrunes are displayed just as for runes. But a "merged" format is also available. Here is a summary of display commands, followed below by further discussion. (show-accumulated-persistence :frames t) ; t is optional, i.e., the default ; Display enhanced statistics sorted by frames, in a ``raw'' format. (show-accumulated-persistence :frames :merge) ; Display enhanced statistics sorted by frames, in a ``merged'' format. (show-accumulated-persistence :frames nil) ; Display regular statistics sorted by frames, without the enhancements. ; More generally, the descriptions just above apply for any legal first ; argument: (show-accumulated-persistence KEY t) (show-accumulated-persistence KEY :merge) (show-accumulated-persistence KEY nil) ; Note also these alternate forms, equivalent to the first of the two forms ; just above, i.e., the form with second argument of t: (show-accumulated-persistence KEY :raw) (show-accumulated-persistence KEY) There is a significant difference between how runes are tracked and how ACL2 tracks hypothesis and conclusion xrunes: unlike regular runes, these xrunes do not contribute to the accumulated :frames counts. Rather, they serve as accumulation sites without contributing their :tries to any accumulation. Consider for example the snippet below, taken from a report created with the :merge option (to be discussed further below), i.e., by evaluating the form (show-accumulated-persistence :frames :merge). :frames :tries :ratio rune -------------------------------- 462 211 ( 2.18) (:REWRITE PERM-MEM) 13 6 [useful] 449 205 [useless] ............................. 251 47 ( 5.34) (:HYP 2 :REWRITE PERM-MEM) 6 6 [useful] 245 41 [useless] ............................. 0 211 ( 0.00) (:HYP 1 :REWRITE PERM-MEM) 0 6 [useful] 0 205 [useless] ............................. 0 7 ( 0.00) (:CONC :REWRITE PERM-MEM) 0 6 [useful] 0 1 [useless] -------------------------------- Notice that while :tries are recorded for the xrune (:HYP 1 :REWRITE PERM-MEM), no :frames are recorded. This is because no stack frames were built for runes while this xrune was on the stack -- only for the xrune itself, which as we explained above is not accumulated into the total :frames counts. As it turns out, this lack of stack frames is explained by the fact that the rewrite rule PERM-MEM has a free variable in the first hypothesis. ACL2 !>:pe perm-mem 18 (DEFTHM PERM-MEM (IMPLIES (AND (PERM X Y) (MEM A X)) (MEM A Y)) :RULE-CLASSES ((:REWRITE :MATCH-FREE :ONCE))) ACL2 !> The second hypothesis, however, does cause additional rewriting in order to rewrite it to true, resulting in 251 stack frames for runes. We see that the conclusion does not lead to creation of any rune stack frames, which might seem to suggest that only 251 stack frames for runes were created on behalf of this rule application -- yet, we see that 462 frames were actually created. The difference is the 211 frames created for the rewrite rule itself. Even if the total had been a bit more than 462, one need not be surprised, as there could be some work recorded during application of the rewrite rule, such as type-prescription reasoning, that is not done during rewriting of a hypothesis or the conclusion. Now suppose we have executed (accumulated-persistence :all) and attempted some proofs, and now we are ready to see statistics. The form (show-accumulated-persistence) displays statistics exactly as described above, treating these extra xrunes just as though they are runes; similarly for the form (show-accumulated-persistence KEY), for any legal KEY. A second optional argument may however be supplied to show-accumulated-persistence. The default for that second argument is t, and a second argument of :raw is treated the same as t; thus, these arguments provide the behavior just described, where data for xrunes are displayed just as for runes. You may restrict output to runes, ignoring hypothesis and conclusion xrunes, by giving a second argument of nil. (This gives the same behavior as if we had started with the command (accumulated-persistence t) instead of the command (accumulated-persistence :all).) Finally, you may give a second argument of :merge, in which case output will be sorted and displayed as though only runes were tracked (not the extra xrunes), but each data item for a non-rune xrune will be merged so that it is displayed in suitable order just below its corresponding rune, as in the PERM-MEM example displayed above. We close by mentioning two aspects of enhanced statistics display for :CONC xrunes that have potential to be confusing. First consider the following example. :frames :tries :ratio rune -------------------------------- 14 4 ( 3.50) (:REWRITE DEFAULT-+-2) 0 0 [useful] 14 4 [useless] ............................. 10 4 ( 2.50) (:HYP 1 :REWRITE DEFAULT-+-2) 0 0 [useful] 10 4 [useless] -------------------------------- It may be surprising that no data is displayed for the corresponding :CONC xrune. The explanation, however, is simple: the hypothesis never rewrote to true, so the conclusion was never rewritten. This is consistent with the marking as "useless" of all :frames and :tries for the rune and the hypothesis xrune. Note by the way, once again, that the hypothesis xrune does not contribute to any :frames count. Another reason not to see data displayed for a :CONC xrune is that if a rule has no hypotheses, then no such data is collected. This decision was made because in the case of no hypotheses, we expect it to be very rare that information for the :CONC xrune will add any useful insight. On a final note: (show-accumulated-persistence :runes) may be used simply to see a list of all runes (or xrunes) displayed alphabetically. Users are encouraged to think about other meters we could install in ACL2 to help diagnose performance problems.  File: acl2-doc-emacs.info, Node: ACCUMULATED-PERSISTENCE-SUBTLETIES, Prev: ACCUMULATED-PERSISTENCE, Up: ACCUMULATED-PERSISTENCE ACCUMULATED-PERSISTENCE-SUBTLETIES some subtle aspects of the counting done by accumulated-persistence In this topic we cover the overcounting of "useful" and of recursive rune application attempts, and we describe how "useless" rune application attempts can actually be critical for a proof's success. _Overcounting of "useful" and of recursive rune application attempts._ Not every rune application may be necessary for a proof's success. Consider for example: (thm (equal (car (cons a (cdr (cons b x)))) a)) Then show-accumulated-persistence will tell us that :rewrite rules car-cons and cdr-cons each had one useful application. However, the rule cdr-cons is used to simplify (cdr (cons b x)) to x, and this simplification is unecessary for the proof. Indeed, the proof succeeds even when preceded by the event: (in-theory (disable cdr-cons)). We thus see that a rune application labeled as "useful" may be simplifying a term that is not relevant to the proof. As of this writing, we consider every :forward-chaining rule application to be "useful", for simplicity of the implementation. Moreover, our counting of these rules is such that a single rule may be counted more than once. Next we show how recursive rule applications are overcounted. Consider the following example. (defun mem (a x) (if (atom x) nil (or (equal a (car x)) (mem a (cdr x))))) Now suppose we consider the sequence of theorems (mem a (list a)), (mem a (list 1 a)), (mem a (list 1 2 a)), (mem a (list 1 2 3 a)), and so on. We will see that the :frames reported for each increases quadratically, even though the :tries increases linearly; so in this case the :tries statistics are more appropriate. Each time the definition of mem is applied, a new stack frame is pushed (see *note ACCUMULATED-PERSISTENCE::), and all subsequent applications of that definition are accumulated into the :frames count for that stack frame. The final :frames count will be the sum of the counts for those individual frames, which form a linear sequence whose sum is therefore quadratic in the number of applications of the definition of mem. _How "useless" attempts can be critical for a proof's success._ The command (accumulated-persistence :useless)] will list rules that did not contribute directly to the proof (see *note ACCUMULATED-PERSISTENCE::, in particular the discussion of "useless" there). However, a "useless" rule can on rare occasions be critical to the success of a proof. In the following example, we have a "bad" rule that can take the proof in the wrong direction, but a "useless" rule does a rewrite that prevents the succesful relieving of a hypothesis of the "bad" rule. In summary: ; Assume p0. We want to prove p1. ; Key rule: p0 -> p1 = t ; Bad rule that could ruin the proof: p3 -> p1 = p2 ; But unfortunately, we know p3: p0 -> p3 ; Important ``useless'' rule, preventing ``bad rule'' above from firing: p3 = p4 The following event captures the rules described above. (encapsulate ((p0 (x) t) (p1 (x) t) (p2 (x) t) (p3 (x) t) (p4 (x) t)) (local (defun p0 (x) x)) (local (defun p1 (x) x)) (local (defun p2 (x) x)) (local (defun p3 (x) x)) (local (defun p4 (x) x)) ; Key rule: (defthm p0-implies-p1 (implies (p0 x) (p1 x))) ; Bad rule that could ruin the proof: (defthm p3-implies-p1-is-p2 (implies (p3 x) (equal (p1 x) (p2 x)))) ; But unfortunately, we know p3: (defthm p0-implies-p3 (implies (p0 x) (p3 x))) ; Important ``useless'' rule, preventing p3-implies-p1-is-p2 from firing: (defthm p3-is-p4 (equal (p3 x) (p4 x)))) Now we can see that p3-is-p4 is labeled as "useless", by evaluating these commands. (accumulated-persistence t) (thm (implies (p0 x) (p1 x))) (show-accumulated-persistence) If instead we first evaluate (in-theory (disable p3-is-p4)) before the thm above, then the proof fails, even though p3-is-p4 was labeled as "useless"! Nevertheless, in general it is probably safe to disable rules reported as "useless" by (show-accumulated-persistence :useless), and doing so may speed up a proof considerably. Remark. The example above suggests a surprising fact: on rare occasions, a proof may fail when you give an :in-theory hint consisting of exactly the runes reported in a proof that succeeds. For, imagine a rule R that is needed in part of the proof but is "bad" in a second part, and that some other, "useless" rule prevents the application of R in that second part. The example above suggests that disabling this "useless" rule can allow the second application of R, thus preventing the proof.  File: acl2-doc-emacs.info, Node: ACL2-DEFAULTS-TABLE, Next: ACL2-HELP, Prev: ACCUMULATED-PERSISTENCE, Up: OTHER ACL2-DEFAULTS-TABLE a table specifying certain defaults, e.g., the default defun-mode Example Forms: (table acl2-defaults-table :defun-mode) ; current default defun-mode (table acl2-defaults-table :defun-mode :program) ; set default defun-mode to :program See *note TABLE:: for a discussion of tables in general. The legal keys for this table are shown below. They may be accessed and changed via the general mechanisms provided by tables. However, there are often more convenient ways to access and/or change the defaults. (See also the note below.) :defun-mode the default defun-mode, which must be :program or :logic. See *note DEFUN-MODE:: for a general discussion of defun-modes. The :defun-mode key may be conveniently set by keyword commands naming the new defun-mode, :program and :logic. See *note PROGRAM:: and see *note LOGIC::. :enforce-redundancy if t, cause ACL2 to insist that most events are redundant (see *note REDUNDANT-EVENTS::); if :warn, cause a warning instead of an error for such non-redundant events; else, nil. See *note SET-ENFORCE-REDUNDANCY::. :ignore-doc-string-error if t, cause ACL2 to ignore ill-formed documentation strings rather than causing an error; if :warn, cause a warning instead of an error in such cases; else, nil (the default). See *note SET-IGNORE-DOC-STRING-ERROR::. :verify-guards-eagerness an integer between 0 and 2 indicating how eager the system is to verify the guards of a defun event. See *note SET-VERIFY-GUARDS-EAGERNESS::. :compile-fns When this key's value is t, functions are compiled when they are defun'd; otherwise, the value is nil. (Except, this key's value is ignored when explicit compilation is suppressed; see *note COMPILATION::.) To set the flag, see *note SET-COMPILE-FNS::. :measure-function the default measure function used by defun when no :measure is supplied in xargs. The default measure function must be a function symbol of one argument. Let mfn be the default measure function and suppose no :measure is supplied with some recursive function definition. Then defun finds the first formal, var, that is tested along every branch and changed in each recursive call. The system then "guesses" that (mfn var) is the :measure for that defun. :well-founded-relation the default well-founded relation used by defun when no :well-founded-relation is supplied in xargs. The default well-founded relation must be a function symbol, rel, of two arguments about which a :well-founded-relation rule has been proved. See *note WELL-FOUNDED-RELATION::. :bogus-defun-hints-ok When this key's value is t, ACL2 allows :hints for nonrecursive function definitions. Otherwise, the value is the nil (the default) or :warn (which makes the check but merely warns when the check fails). See *note SET-BOGUS-DEFUN-HINTS-OK::. :bogus-mutual-recursion-ok When this key's value is t, ACL2 skips the check that every function in a mutual-recursion (or defuns) "clique" calls at least one other function in that "clique." Otherwise, the value is nil (the default) or :warn (which makes the check but merely warns when the check fails). See *note SET-BOGUS-MUTUAL-RECURSION-OK::. :irrelevant-formals-ok When this key's value is t, the check for irrelevant formals is bypassed; otherwise, the value is the keyword nil (the default) or :warn (which makes the check but merely warns when the check fails). See *note IRRELEVANT-FORMALS:: and see *note SET-IRRELEVANT-FORMALS-OK::. :ignore-ok When this key's value is t, the check for ignored variables is bypassed; otherwise, the value is the keyword nil (the default) or :warn (which makes the check but merely warns when the check fails). See *note SET-IGNORE-OK::. :bdd-constructors This key's value is a list of function symbols used to define the notion of "BDD normal form." See *note BDD-ALGORITHM:: and see *note HINTS::. :ttag This key's value, when non-nil, allows certain operations that extend the trusted code base beyond what is provided by ACL2. See *note DEFTTAG::. See *note DEFTTAG::. :state-ok This key's value is either t or nil and indicates whether the user is aware of the syntactic restrictions on the variable symbol STATE. See *note SET-STATE-OK::. :backchain-limit This key's value is a list of two "numbers." Either "number" may optionally be nil, which is treated like positive infinity. The numbers control backchaining through hypotheses during type-set reasoning and rewriting. See *note BACKCHAIN-LIMIT::. :default-backchain-limit This key's value is a list of two "numbers." Either "number" may optionally be nil, which is treated like positive infinity. The numbers are used respectively to set the backchain limit of a rule if one has not been specified. See *note BACKCHAIN-LIMIT::. :step-limit This key's value is either nil or a natural number not exceeding the value of *default-step-limit*. If the value is nil or the value of *default-step-limit*, there is no limit on the number of "steps" that ACL2 counts during a proof: currently, the number of top-level rewriting calls. Otherwise, the value is the maximum number of such calls allowed during evaluation of any event. See *note SET-PROVER-STEP-LIMIT::. :rewrite-stack-limit This key's value is a nonnegative integer less than (expt 2 28). It is used to limit the depth of calls of ACL2 rewriter functions. See *note REWRITE-STACK-LIMIT::. :let*-abstractionp This key affects how the system displays subgoals. The value is either t or nil. When t, let* expressions are introduced before printing to eliminate common subexpressions. The actual goal being worked on is unchanged. :nu-rewriter-mode This key's value is nil, t, or :literals. When the value is non-nil, the rewriter gives special treatment to expressions and functions defined in terms of nth and update-nth. See set-nu-rewriter-mode. :case-split-limitations This key's value is a list of two "numbers." Either "number" may optionally be nil, which is treated like positive infinity. The numbers control how the system handles case splits in the simplifier. See *note SET-CASE-SPLIT-LIMITATIONS::. :include-book-dir-alist This key's value is used by include-book's :DIR argument to associate a directory with a keyword. An exception is the keyword :SYSTEM for the books/ directory; see *note INCLUDE-BOOK::, in particular the section on "Books Directory." :match-free-default This key's value is either :all, :once, or nil. See *note SET-MATCH-FREE-DEFAULT::. :match-free-override This key's value is a list of runes. See *note ADD-MATCH-FREE-OVERRIDE::. :match-free-override-nume This key's value is an integer used in the implementation of add-match-free-override, so that only existing runes are affected by that event. :non-linearp This key's value is either t or nil and indicates whether the user wishes ACL2 to extend the linear arithmetic decision procedure to include non-linear reasoning. See *note NON-LINEAR-ARITHMETIC::. :tau-auto-modep This key's value is either t or nil and indicates whether the user wishes ACL2 to look for opportunities to create :tau-system rules from all suitable defuns and from all suitable defthms (with non-nil :rule-classes). See *note SET-TAU-AUTO-MODE::. :ruler-extenders This key's value may be a list of symbols, indicating those function symbols that are not to block the collection of rulers; see *note DEFUN::. Otherwise the value is :all to indicate all function symbols, i.e., so that no function symbol blocks the collection of rulers. If a list is specified (rather than :all), then it may contain the keyword :lambdas, which has the special role of specifying all lambda applications. No other keyword is permitted in the list. See *note RULER-EXTENDERS::. :memoize-ideal-okp This key is only legal in an experimental hons version (see *note HONS-AND-MEMOIZATION::). Its value must be either t, nil, or :warn. If the value is nil or not present, then it is illegal by default to memoize a :logic mode function that has not been guard-verified (see *note VERIFY-GUARDS::), sometimes called an "ideal-mode" function. This illegality is the default because such calls of such functions in the ACL2 loop are generally evaluated in the logic (using so-called "executable counterpart" definitions), rather than directly by executing calls of the corresponding (memoized) raw Lisp function. However, such a raw Lisp call can be made when the function is called by a :program mode function, so we allow you to override the default behavior by associating the value t or :warn with the key :memoize-ideal-okp, where with :warn you get a suitable warning. Note that you can also allow memoization of ideal-mode functions by supplying argument :ideal-okp to your memoization event (see *note MEMOIZE::), in which case the value of :memoize-ideal-okp in the acl2-defaults-table is irrelevant. Note: Unlike all other tables, acl2-defaults-table can affect the soundness of the system. The table mechanism therefore enforces on it a restriction not imposed on other tables: when table is used to update the acl2-defaults-table, the key and value must be variable-free forms. Thus, while (table acl2-defaults-table :defun-mode :program), (table acl2-defaults-table :defun-mode ':program), and (table acl2-defaults-table :defun-mode (compute-mode *my-data*)) are all examples of legal events (assuming compute-mode is a function of one non-state argument that produces a defun-mode as its single value), (table acl2-defaults-table :defun-mode (compute-mode (w state))) is not legal because the value form is state-sensitive. Consider for example the following three events which one might make into the text of a book. (in-package "ACL2") (table acl2-defaults-table :defun-mode (if (ld-skip-proofsp state) :logic :program)) (defun crash-and-burn (x) (car x)) The second event is illegal because its value form is state-sensitive. If it were not illegal, then it would set the :defun-mode to :program when the book was being certified but would set the defun-mode to :logic when the book was being loaded by include-book. That is because during certification, ld-skip-proofsp is nil (proof obligations are generated and proved), but during book inclusion ld-skip-proofsp is non-nil (those obligations are assumed to have been satisfied.) Thus, the above book, when loaded, would create a function in :logic mode that does not actually meet the conditions for such status. For similar reasons, table events affecting acl2-defaults-table are illegal within the scope of local forms. That is, the text (in-package "ACL2") (local (table acl2-defaults-table :defun-mode :program)) (defun crash-and-burn (x) (car x)) is illegal because acl2-defaults-table is changed locally. If this text were acceptable as a book, then when the book was certified, crash-and-burn would be processed in :program mode, but when the certified book was included later, crash-and-burn would have :logic mode because the local event would be skipped. The text (in-package "ACL2") (program) ;which is (table acl2-defaults-table :defun-mode :program) (defun crash-and-burn (x) (car x)) is acceptable and defines crash-and-burn in :program mode, both during certification and subsequent inclusion. We conclude with an important observation about the relation between acl2-defaults-table and include-book, certify-book, and encapsulate. Including or certifying a book never has an effect on the acl2-defaults-table, nor does executing an encapsulate event; we always restore the value of this table as a final act. (Also see *note INCLUDE-BOOK::, see *note ENCAPSULATE::, and see *note CERTIFY-BOOK::.) That is, no matter how a book fiddles with the acl2-defaults-table, its value immediately after including that book is the same as immediately before including that book. If you want to set the acl2-defaults-table in a way that persists, you need to do so using commands that are not inside books. It may be useful to set your favorite defaults in your acl2-customization file; see *note ACL2-CUSTOMIZATION::.  File: acl2-doc-emacs.info, Node: ACL2-HELP, Next: CERTIFY-BOOK!, Prev: ACL2-DEFAULTS-TABLE, Up: OTHER ACL2-HELP the acl2-help mailing list You can email questions about ACL2 usage to the acl2-help mailing list: acl2-help@utlists.utexas.edu. If you have more general questions about ACL2, for example, about projects completed using ACL2, you may prefer the acl2 mailing list, acl2@utlists.utexas.edu, which tends to have wider distribution.  File: acl2-doc-emacs.info, Node: CERTIFY-BOOK!, Next: COMMAND-LINE, Prev: ACL2-HELP, Up: OTHER CERTIFY-BOOK! a variant of certify-book Examples: (certify-book! "my-arith" 3) ;Certify in a world with 3 ; commands, starting in a world ; with at least 3 commands. (certify-book! "my-arith") ;Certify in the initial world. General Form: (certify-book! book-name k compile-flg) where book-name is a book name (see *note BOOK-NAME::), k is a nonnegative integer used to indicate the "certification world," and compile-flg indicates whether you wish to compile the (functions in the) book. This command is identical to certify-book, except that the second argument k may not be t in certify-book! and if k exceeds the current command number, then an appropriate ubt! will be executed first. See *note CERTIFY-BOOK:: and see *note UBT!::.  File: acl2-doc-emacs.info, Node: COMMAND-LINE, Next: CW-GSTACK, Prev: CERTIFY-BOOK!, Up: OTHER COMMAND-LINE handling of command-line arguments when ACL2 is invoked You may provide command-line arguments when invoking ACL2, which are passed to the host Lisp. For more information on this topic, along with a discussion of how to save an ACL2 executable that avoids passing command-line arguments to the host Lisp, see *note SAVE-EXEC::. * Menu: Related topics other than immediate subtopics: * SAVE-EXEC:: save an executable image and a wrapper script  File: acl2-doc-emacs.info, Node: CW-GSTACK, Next: DEAD-EVENTS, Prev: COMMAND-LINE, Up: OTHER CW-GSTACK debug a rewriting loop or stack overflow Example Forms: (cw-gstack) (cw-gstack :frames 10) ; show only the top 10 frames (cw-gstack :frames '(1 10)) ; same as above: show only frames 1 through 10 (cw-gstack :frames '(10 20)) ; show only frames 10 through 20 (cw-gstack :evisc-tuple (evisc-tuple 3 4 nil nil)) ; print with print-level 3 and print-length 4 (cw-gstack :evisc-tuple nil) ; print using default ``evisceration'', ; essentially the same as just above (cw-gstack :evisc-tuple '(nil 3 4 (hide))) ; same as just above General Form: (cw-gstack :frames frames :evisc-tuple evisc-tuple) where :frames and :evisc-tuple are optional, but if they are supplied, their values are evaluated. The value of frames should be either a natural number or a list of two natural numbers, the first less than the second; and the value of evisc-tuple should be an evisc-tuple (see *note EVISC-TUPLE::). If :evisc-tuple is omitted, then substructures deeper than 3 are replaced by "#" and those longer than 4 are replaced by "...", and terms of the form (hide ...) are printed as . Also see *note SET-IPRINT:: for an alternative to printing "#" and "...". Stack overflows may occur, perhaps caused by looping rewrite rules. In some Lisps, stack overflows may manifest themselves as segmentation faults, causing the entire ACL2 image to crash. Finding looping rewrite rules can be tricky, especially if you are using books supplied by other people. (However, see *note SET-REWRITE-STACK-LIMIT:: for a way to avoid stack overflows caused by rewriter loops.) Normally, a stack overflow will cause the printing of an error message that suggests how to proceed. Just follow those instructions, and you will generally be able to see what is causing the loop. Suggestion: Once you have found the loop and fixed it, you should execute the ACL2 command :brr nil, so that you don't slow down subsequent proof attempts.  File: acl2-doc-emacs.info, Node: DEAD-EVENTS, Next: DISASSEMBLE$, Prev: CW-GSTACK, Up: OTHER DEAD-EVENTS using proof supporters to identify dead code and unused theorems Below, when we talk about "an event A", we mean an event whose name is A. When event A is used in a proof performed to admit event B that you submit to ACL2, we say that A is a "proof-supporter" of B. ACL2 stores an association list such that for every event B with at least one proof-supporter, B is associated with a list of all of its proof-supporters, sorted by symbol-<. The following form evaluates to that alist, which is called the "proof-supporters-alist". (global-val 'proof-supporters-alist (w state)) By "used in a proof" above, we mean: applied as a rule or supplied explicitly via hints of type :use, :by, or :clause-processor. That is, the events "used in a proof" for admitting an event E are those listed in the summary printed at the conclusion of admitting E. Note that if proofs are skipped when admitting event E, say because the last admission of E was done by include-book (or certify-book, which ends with an include-book), then there will be no entry in that alist for E. (An exception is made however for encapsulate events, where proof-supporters are remembered from the first pass; see below.) So if you want the proof-supporters-alist to include supporters for events in a book, use ld rather than include-book or certify-book to process the events in that book. If however you are interested in the proof-supporters FROM a book that support a later event, then it is fine to include that book. The case for encapsulate is slightly tricky. Consider an example of the following form. A ; event preceding the encapsulate (encapsulate () B (local C) ; uses A and B in a proof D ; uses C in a proof ) At the conclusion of this encapsulate event, the proof-supporters-alist associates D with A and B, but not C (which has disappeared, since it is local). Note that this sort of "transitive closure" operation is only performed when necessary due to the disappearance of local events. For example, if we replace (local C) above by just C, then D is associated in the proof-supporters-alist only with C, not with A or B. If you want the transitive closure of the relation computed by the proof-supporters-alist, you have to compute it yourself. (This was a deliberate design decision, in order to avoid slowing down event processing.) However, there is help available on how to do such a computation: A community book, books/misc/dead-events.lisp, does such a transitive closure, and moreover uses that information to find "dead events" relative to a list of "desired" events. For example, suppose you use LD to process the events, with proofs, in a book intended to prove theorems MAIN-1 and MAIN-2. (Remember, certify-book will not save such information.) Suppose furthermore that the book begins with some include-book forms followed by (deflabel book-start). You could evaluate this form: (dead-events '(main-1 main-2) :start 'book-start) The result is a list of events that you probably can delete from the book without causing any proofs to fail. See the dead-events.lisp book for further documentation. You might also find the code in the above book to be helpful for writing your own utilities based on the proof-supporters-alist.  File: acl2-doc-emacs.info, Node: DISASSEMBLE$, Next: DMR, Prev: DEAD-EVENTS, Up: OTHER DISASSEMBLE$ disassemble a function The macro disassemble$ provides a convenient interface to the underlying disassemble utility of the host Common Lisp implementation, which prints assembly code for a given function symbol at the terminal. It works by including the community book books/misc/disassemble.lisp, which defines the supporting function disassemble$-fn, and then by calling that function. Note that the arguments to disassemble$ are evaluated. Also note that disassemble$ is intended as a top-level utility for the ACL2 loop, not to be called in code; for such a purpose, include the above book and call disassemble$-fn directly. Example Forms: (disassemble$ 'foo) (disassemble$ 'foo :recompile t) General Forms: (disassemble$ form) (disassemble$ form :recompile flg) where form evaluates to a function symbol and flg evaluates to any value. If flg is nil, then the existing definition of that function symbol is disassembled. But if flg is supplied and has a value other than nil or :default, and if that function symbol is defined in the ACL2 loop (not merely in raw Lisp; for example, see *note SET-RAW-MODE::), then the disassembly will be based on a recompilation of that ACL2 definition. Normally this recompilation is not necessary, but for some host Lisps, it may be useful; in particular, for CCL the above book arranges that source code information is saved, so that the output is annotated with such information. When recompilation takes place, the previous definition is restored after disassembly is complete. Finally, if flg is omitted or has the value :default -- i.e., in the default case -- then recompilation may take place or not, depending on the host Lisp. The values of (@ host-lisp) for which recompilation takes place by default may be found by looking at the above book, or by including it and evaluating the constant *host-lisps-that-recompile-by-default*. As of this writing, CCL is the only such Lisp (because that is the one for which we can obtain source annotation in the output by recompiling).  File: acl2-doc-emacs.info, Node: DMR, Next: DYNAMICALLY-MONITOR-REWRITES, Prev: DISASSEMBLE$, Up: OTHER DMR dynamically monitor rewrites and other prover activity In addition to utilities that allow you to set breakpoints or print rewriting information to the screen -- see *note BREAK-REWRITE:: -- ACL2 provides a utility for watching the activity of the rewriter and some other proof processes, in real time. This utility is called "dmr", which is an acronym for "dynamically monitor rewrites". The utility comes in two parts: an ACL2 component that frequently updates a file (the "dmr file") containing the relevant information, and an Emacs component that frequently updates the an Emacs buffer (the "dmr buffer") with the contents of that file. Other editors could, in principle, be programmed to display that file; anyone developing such a capability is invited to contribute it to the ACL2 community. The dmr utility can be extremely helpful for expensive proofs, especially when ACL2 is not providing any output to the terminal. The break-rewrite and accumulated-persistence utilities may be a bit easier to use, so you might want to try those first. But the dmr utility can be a very helpful debugging aide, as it can visually give you a sense of where ACL2 is spending its time. The Emacs portion of this utility is already loaded if you load the distributed Emacs file emacs/emacs-acl2.el. Otherwise, invoke the following Emacs command, say by typing Control-X Control-E after the right parenthesis, where DIR is the directory of your ACL2 distribution. (load "/emacs/monitor.el") ; absolute pathnames might work best You only need to do that once. Then each time you want to observe the rewriter in action, invoke the following to see it displayed in a buffer, which we call the "dmr buffer": Control-t 1 But first you will need to enable monitoring at the ACL2 level: (dmr-start) Monitoring has some cost. So if you have started it, then at some point you may want to turn it off when not using it. Any time the dmr buffer (generally called "acl2-dmr-") is not visible, Emacs monitoring is turned off. You can also turn off Emacs monitoring explicitly, with: Control-t 2 At the ACL2 level you can disable monitoring as follows: (dmr-stop) _Interpreting the dmr buffer display._ We explain the dmr buffer display by way of the following example. It is a snapshot of a dmr buffer taken from one of the community books, books/workshops/2004/legato/support/proof-by-generalization-mult.lisp. 0. (DEFTHM . WP-ZCOEF-G-MULTIPLIES) 1. SIMPLIFY-CLAUSE 2. Rewriting (to simplify) the atom of literal 18; argument(s) 1 4. Rewriting (to simplify) the expansion; argument(s) 3|2 7. Applying (:DEFINITION WP-ZCOEF-G) * 8. Rewriting (to simplify) the rewritten body; argument(s) 2|1|2|2 * 13. Applying (:REWRITE MOD-ZERO . 2) * 14. Rewriting (to establish) the atom of hypothesis 4 * 15. Applying (:META META-INTEGERP-CORRECT) Each line indicates an ACL2 activity that leads to the activity shown on the line just below it. Moreover, lines are sometimes collapsed to make the display more compact. Consider for example the first few lines. Above, we are proving a theorem named WP-ZCOEF-G-MULTIPLIES. Lines 1 and 2 show the clause simplification process invokes the rewriter on the 18th literal. (Recall that a clause is a disjunction of literals; for example the clause {(NOT A), (NOT B), C} would be displayed as (IMPLIES (AND A B) C).) This 18th literal mentioned on line 2 is a function call (f arg1 ...), and "argument(s) 1" indicates that the rewriter, which works inside-out, is considering the first argument ("arg1"). Thus the display could instead have shown the following. 2. Rewriting (to simplify) the atom of literal 18 3. Rewriting (to simplify) the first argument 4. Rewriting (to simplify) the expansion; argument(s) 3|2 But it saved space to combine lines 2 and 3. Line 4 suggests that the above arg1 is a function call that has been opened up because of an :expand hint or, perhaps, an expansion directed explicitly by the prover (as can happen during induction). The annotation "argument(s) 3|2" then indicates that the rewriter is diving into the third argument of the expansion, and then into the second argument of that. Let us call the result term7 (since it is the one to be considered on line 7). Now consider the next two lines: 7. Applying (:DEFINITION WP-ZCOEF-G) * 8. Rewriting (to simplify) the rewritten body; argument(s) 2|1|2|2 Line 7 is saying that term7 (defined above) is modified by applying the definition of WP-ZCOEF-G to it. Line 8 then says that the body of this definition has been rewritten (with its formals bound to the actuals from term7) and the rewriter is diving into the subterms of that rewritten body, as indicated. Notice also that line 8 is the first line marked with an asterisk ("*") in the margin. This line is the first that is different from what was shown the previous time the display was updated (about 1/10 second earlier, by default). When a line is marked with an asterisk, so are all the lines below it; so the lines without an asterisk are those that have been stable since the last display. In this example we may see line 7 marked without an asterisk for a while, which suggests that the rule (:DEFINITION WP-ZCOEF-G) is expensive. (Also see *note ACCUMULATED-PERSISTENCE::.) In general, a line that persists for awhile without a leading asterisk can suggest why the proof is taking a long time. Finally, note the indentation of line 14 relative to line 13. Extra indentation occurs when an attempt is being made to relieve a hypothesis (i.e., rewrite it to t). In particular, rewrites that will be incorporated directly into a (top-level) literal are all indented just two spaces, starting with the first rewrite directly under a process such as SIMPLIFY-CLAUSE (shown line 1 above). If the indentation is at least the value of raw Lisp variable *dmr-indent-max* (by default, 20), then indenttion is restricted to that column, but ACL2 prints {n} where n is the column that would have been used for indentation if there were no maximum. You can move the cursor around in the dmr buffer even while it is being updated. But emacs will attempt to keep the cursor no later than the first asterisk ("*") in the margin. Thus, you can move the cursor around in the stable part of the display, and emacs will keep the cursor in that stable part. WARNING: Things could go terribly wrong if the same user runs two different ACL2 sessions with dmr active, because the same file will be written by two different ACL2 processes. WARNING: For dmr to work, emacs and ACL2 need to be run on the same machine because the file used to communicate between ACL2 and emacs is under /tmp. Except, you can probably hack around that restriction by changing *dmr-file-name* in ACL2 (in raw Lisp) and correspondingly in Emacs file monitor.el. More generally, advanced users are welcome to search for the string User-settable dmr variables in ACL2 files interface-raw.lisp and emacs/monitor.el in order to customize their dmr environments. In order to update the dmr file with the latest stack information, interrupt ACL2 and then evaluate: (dmr-flush). In order to support resumption of the interrupted proof (assuming your host Common Lisp supports resumption), evaluation of (dmr-start) will automatically enable the debugger if it is not already enabled and not fully disabled with value :never (see *note SET-DEBUGGER-ENABLE::). If such automatic enabling takes place, then (dmr-stop) will restore the old setting of the debugger unless, after (dmr-start) enables the debugger but before (dmr-stop) is called, you call set-debugger-enable (or more precisely: function set-debugger-enable-fn is called). Note for users of the experimental extension ACL2(p) (see *note PARALLELISM::): when waterfall-parallelism has been set to a non-nil value (see *note SET-WATERFALL-PARALLELISM::), statistics about parallel execution are printed instead of the usual information.  File: acl2-doc-emacs.info, Node: DYNAMICALLY-MONITOR-REWRITES, Next: EXIT, Prev: DMR, Up: OTHER DYNAMICALLY-MONITOR-REWRITES See *note DMR::.  File: acl2-doc-emacs.info, Node: EXIT, Next: GOOD-BYE, Prev: DYNAMICALLY-MONITOR-REWRITES, Up: OTHER EXIT quit entirely out of Lisp Same as good-bye.  File: acl2-doc-emacs.info, Node: GOOD-BYE, Next: GUARD-OBLIGATION, Prev: EXIT, Up: OTHER GOOD-BYE quit entirely out of Lisp Examples: ACL2 !>(good-bye) ; [ACL2 is exited] ACL2 !>(good-bye 3) ; [ACL2 is exited with Unix exit status 3] Note: Your entire session will disappear forever when you evaluate (good-bye). The command (good-bye) quits not only out of the ACL2 command loop, but in fact quits entirely out of the underlying Lisp. Thus, there is no going back! You will *not* be able to re-enter the command loop after typing (good-bye)! All your work will be lost!!! This command may not work in some underlying Common Lisp implementations. In such cases, there is no harm in trying; ACL2 will let you know how to proceed if it cannot exit. In some systems, typing control-d at the top-level ACL2 prompt (control-c control-d if inside emacs) will call this function. If you give good-bye an argument, it should be a natural number, and it indicates the Unix (Linux) exit status. If you merely want to exit the ACL2 command loop, use :q instead (see *note Q::).  File: acl2-doc-emacs.info, Node: GUARD-OBLIGATION, Next: IN-PACKAGE, Prev: GOOD-BYE, Up: OTHER GUARD-OBLIGATION the guard proof obligation See *note VERIFY-GUARDS::, and see *note GUARD:: for a discussion of guards. Also see *note VERIFY-GUARDS-FORMULA:: for a utility provided for viewing the guard proof obligation, without proof. Guard-obligation is a lower level function for use in system programs, not typically appropriate for most ACL2 users. If you simply want to see the guard proof obligations, see *note VERIFY-GUARDS-FORMULA::. Example Form: (guard-obligation 'foo nil 'top-level state) (guard-obligation '(if (consp x) (foo (car x)) t) nil 'my-function state) General Forms: (guard-obligation name guard-debug ctx state) (guard-obligation term guard-debug ctx state) where the first argument is either the name of a function or theorem or is a non-variable term that may be in untranslated form; guard-debug is typically nil but may be t (see *note GUARD-DEBUG::); ctx is a context (typically, a symbol used in error and warning messages); and state references the ACL2 state. If you want to obtain the formula but you don't care about the so-called "tag tree": (mv-let (erp val state) (guard-obligation x guard-debug 'top-level state) (if erp ( .. code for handling error case, e.g., name is undefined .. ) (let ((cl-set (cadr val))) ; to be proved for guard verification ( .. code using cl-set, which is a list of clauses, implicitly conjoined, each of which is viewed as a disjunction .. )))) The form (guard-obligation x guard-debug ctx state) evaluates to a triple (mv erp val state), where erp is nil unless there is an error, and state is the ACL2 state. Suppose erp is nil. Then val is the keyword :redundant if the corresponding verify-guards event would be redundant; see *note REDUNDANT-EVENTS::. Otherwise, val is a tuple (list* names cl-set ttree), where: names is (cons :term xt) if x is not a variable, where xt is the translated form of x; and otherwise is a list containing x along with, if x is defined in a mutual-recursion, any other functions defined in the same mutual-recursion nest; cl-set is a list of lists of terms, viewed as a conjunction of clauses (each viewed (as a disjunction); and ttree is an assumption-free tag-tree that justifies cl-set. (The notion of "tag-tree" may probably be ignored except for system developers.) Guard-obligation is typically used for function names or non-variable terms, but as for verify-guards, it may also be applied to theorem names. See the source code for verify-guards-formula for an example of how to use guard-obligation.  File: acl2-doc-emacs.info, Node: IN-PACKAGE, Next: LD, Prev: GUARD-OBLIGATION, Up: OTHER IN-PACKAGE select current package Example: (in-package "MY-PKG") General Form: (in-package str) where str is a string that names an existing ACL2 package, i.e., one of the initial packages such as "KEYWORD" or "ACL2" or a package introduced with defpkg. For a complete list of the known packages created with defpkg, evaluate (strip-cars (known-package-alist state)). See *note DEFPKG::. An ACL2 book (see *note BOOKS::) must contain a single in-package form, which must be the first form in that book.  File: acl2-doc-emacs.info, Node: LD, Next: PRINT-GV, Prev: IN-PACKAGE, Up: OTHER LD the ACL2 read-eval-print loop, file loader, and command processor Examples: (LD "foo.lisp") ; read and evaluate each form in file ; "foo.lisp", in order (LD "foo.lisp" :ld-pre-eval-print t) ; as above, but print each form to standard ; character output just before it is evaluated General Form: (LD standard-oi ; open obj in channel, stringp file name ; to open and close, or list of forms ; Optional keyword arguments: :dir ... ; use this add-include-book-dir directory :standard-co ... ; open char out or file to open and close :proofs-co ... ; open char out or file to open and close :current-package ... ; known package name :ld-skip-proofsp ... ; nil, 'include-book, or t ; (see *note LD-SKIP-PROOFSP::) :ld-redefinition-action ... ; nil or '(:a . :b) :ld-prompt ... ; nil, t, or some prompt printer fn :ld-missing-input-ok ... ; nil, t, :warn, or warning message :ld-pre-eval-filter ... ; :all, :query, or some new name :ld-pre-eval-print ... ; nil, t, or :never :ld-post-eval-print ... ; nil, t, or :command-conventions :ld-evisc-tuple ... ; nil or '(alist nil nil level length) :ld-error-triples ... ; nil or t :ld-error-action ... ; :return!, :return, :continue or :error :ld-query-control-alist ... ; alist supplying default responses :ld-verbose ...) ; nil or t * Menu: * CALLING-LD-IN-BAD-CONTEXTS:: errors caused by calling ld in inappropriate contexts Ld is the top-level ACL2 read-eval-print loop. (When you call lp, a little initialization is done in raw Common Lisp and then ld is called.) Ld is also a general-purpose ACL2 file loader and a command interpreter. Ld is actually a macro that expands to a function call involving state. Ld returns an "error triple" (mv erp val state) as explained below. (For much more on error triples, see *note PROGRAMMING-WITH-STATE::.) See *note REBUILD:: for a variant of ld that skips proofs. See *note OUTPUT-TO-FILE:: for examples showing how to redirect output to a file. The arguments to ld, except for :dir, all happen to be global variables in state (see *note STATE:: and see *note PROGRAMMING-WITH-STATE::). For example, 'current-package and 'ld-verbose are global variables, which may be accessed via (@ current-package) and (@ ld-verbose). When ld is called, it "binds" these variables. By "binds" we actually mean the variables are globally set but restored to their old values on exit. Because ld provides the illusion of state global variables being bound, they are called "ld specials" (after the Lisp convention of calling a variable "special" if it is referenced freely after having been bound). Note that all arguments but the first are passed via keyword. Any variable not explicitly given a value in a call retains its pre-call value, with the exception of :ld-error-action, which defaults to :return! if not explicitly specified. Just as an example to drive the point home: If current-package is "ACL2" and you typed (ld *standard-oi* :current-package "MY-PKG") you would find yourself in (an inner) read-eval-print loop in which the current-package was "MY-PKG". You could operate there as long as you wished, changing the current package at will. But when you typed :q you would return to the outer read-eval-print loop where the current package would still be "ACL2". Roughly speaking, ld repeatedly reads a form from standard-oi, evaluates it, and prints its result to standard-co. It does this until the form is :q or evaluates to an error triple whose value component is :q, or until the input channel or list is emptied. However, ld has many bells and whistles controlled by the ld specials. Each such special is documented individually. For example, see the documentation for standard-oi, current-package, ld-pre-eval-print, etc. A more precise description of ld is as follows. In the description below we use the ld specials as variables, e.g., we say "a form is read from standard-oi." By this usage we refer to the current value of the named state global variable, e.g., we mean "a form is read from the current value of 'standard-oi." This technicality has an important implication: If while interacting with ld you change the value of one of the ld specials, e.g., 'standard-oi, you will change the behavior of ld, e.g., subsequent input will be taken from the new value. Three ld specials are treated as channels: standard-oi is treated as an object input channel and is the source of forms evaluated by ld; standard-co and proofs-co are treated as character output channels and various flavors of output are printed to them. However, the supplied values of these specials need not actually be channels; several special cases are recognized. If the supplied value of one of these is in fact an open channel of the appropriate type, that channel is used and is not closed by ld. If the supplied value of one of these specials is a string, the string is treated as a file name in (essentially) Unix syntax (see *note PATHNAME::) and a channel of the appropriate type is opened to/from that file. Any channel opened by ld during the binding of the ld specials is automatically closed by ld upon termination. If standard-co and proofs-co are equal strings, only one channel to that file is opened and is used for both. As a special convenience, when standard-oi is a string and the :dir argument provided and not nil, we look up :dir in the table of directories maintained by add-include-book-dir, and prepend this directory to standard-oi to create the filename. (In this case, however, we require that standard-oi is a relative pathname, not an absolute pathname.) For example, one can write (ld "arithmetic/top-with-meta.lisp" :dir :system) to ld that particular community books library. (Of course, you should almost always load books like arithmetic/top-with-meta using include-book instead of ld.) If :dir is not specified, then a relative pathname is resolved using the connected book directory; see *note CBD::. Several other alternatives are allowed for standard-oi. If standard-oi is a true list then it is taken as the list of forms to be processed. If standard-oi is a list ending in an open channel, then ld processes the forms in the list and then reads and processes the forms from the channel. Analogously, if standard-oi is a list ending a string, an object input channel from the named file is opened and ld processes the forms in the list followed by the forms in the file. That channel is closed upon termination of ld. In the cases that a string is to be converted to an object input channel -- that is, when standard-oi is a string or is a list ending in a string -- an error occurs by default if the conversion fails, presumably because the file named by the string does not exist. However, if keyword argument :ld-missing-input-ok is t, then ld immediately returns without error in this case, but without reading or executing any forms, as though standard-oi is nil and keyword arguments :ld-verbose and ld-prompt both have value nil. The other legal values for :ld-missing-input-ok are nil, which gives the default behavior, and :warn, which behaves the same as t except that a warning is printed, which contains the same information as would be printed for the default error described above. The remaining ld specials are handled more simply and generally have to be bound to one of a finite number of tokens described in the :doc entries for each ld special. Should any ld special be supplied an inappropriate value, an error message is printed. Next, if ld-verbose is t, ld prints the message "ACL2 loading name" where name names the file or channel from which forms are being read. At the conclusion of ld, it will print "Finished loading name" if ld-verbose is t. Finally, ld repeatedly executes the ACL2 read-eval-print step, which may be described as follows. A prompt is printed to standard-co if ld-prompt is non-nil. The format of the prompt is determined by ld-prompt. If it is t, the default ACL2 prompt is used. If it is any other non-nil value then it is treated as an ACL2 function that will print the desired prompt. See *note LD-PROMPT::. In the exceptional case where ld's input is coming from the terminal (*standard-oi*) but its output is going to a different sink (i.e., standard-co is not *standard-co*), we also print the prompt to the terminal. Ld then reads a form from standard-oi. If the object read is a keyword, ld constructs a "keyword command form" by possibly reading several more objects. See *note KEYWORD-COMMANDS::. This construction process is sensitive to the value of ld-keyword-aliases. See *note LD-KEYWORD-ALIASES::. Otherwise, the object read is treated as the command form. Ld next decides whether to evaluate or skip this form, depending on ld-pre-eval-filter. Initially, the filter must be either :all, :query, or a new name. If it is :all, it means all forms are evaluated. If it is :query, it means each form that is read is displayed and the user is queried. Otherwise, the filter is a name and each form that is read is evaluated as long as the name remains new, but if the name is ever introduced then no more forms are read and ld terminates. See *note LD-PRE-EVAL-FILTER::. If the form is to be evaluated, then ld first prints the form to standard-co, if ld-pre-eval-print is t. With this feature, ld can process an input file or form list and construct a script of the session that appears as though each form was typed in. See *note LD-PRE-EVAL-PRINT::. Ld then evaluates the form, with state bound to the current state. The result is some list of (multiple) values. If a state is among the values, then ld uses that state as the subsequent current state. Depending on ld-error-triples, ld may interpret the result as an "error." See *note LD-ERROR-TRIPLES::. We first discuss ld's behavior if no error signal is detected (either because none was sent or because ld is ignoring them because ld-error-triples is nil). In the case of a non-erroneous result, ld does two things: First, if the logical world in the now current state is different than the world before execution of the form, ld adds to the world a "command landmark" containing the form evaluated. See *note COMMAND-DESCRIPTOR::. Second, ld prints the result to standard-co, but only if ld-post-eval-print is not nil. The result is printed as a list of (multiple) values unless ld-post-eval-print is :command-conventions, ld-error-triples is t, and the result is an "error triple", i.e., of the form (mv * * state) (see *note ERROR-TRIPLES::). In that case, only the non-erroneous "value" component of the result is printed. See *note LD-POST-EVAL-PRINT::. Whenever ld prints anything (whether the input form, a query, or some results) it "eviscerates" it if ld-evisc-tuple is non-nil. Essentially, evisceration is a generalization of Common Lisp's use of *print-level* and *print-length* to hide large substructures. See *note EVISC-TUPLE:: and also see *note SET-IPRINT::. We now return to the case of a form whose evaluation signals an error. In this case, ld first restores the ACL2 logical world to what it was just before the erroneous form was evaluated. Thus, a form that partially changes the world (i.e., begins to store properties) and then signals an error, has no effect on the world. You may see this happen on commands that execute several events (e.g., an encapsulate or a progn of several defuns): even though the output makes it appear that the initial events were executed, if an error is signalled by a later event the entire block of events is discarded. After rolling back, ld takes an action determined by ld-error-action. If the action is :continue, ld merely iterates the read-eval-print step. Note that nothing suggestive of the value of the "erroneous" form is printed. If the action is :return, ld terminates normally; similarly if the action is :return!, but a special value is returned that can cause superior ld commands to terminate; see *note LD-ERROR-ACTION:: for details. If the action is :error, ld terminates signalling an error to its caller. If its caller is in fact another instance of ld and that instance is watching out for error signals, the entire world created by the inner ld will be discarded by the outer ld if the inner ld terminates with an error. Ld returns an error triple, (mv erp val state). Erp is t or nil indicating whether an error is being signalled. If no error is signalled, val is the "reason" ld terminated and is one of :exit (meaning :q was read), :eof (meaning the input source was exhausted), :error (meaning an error occurred but has been supressed), :filter (meaning the ld-pre-eval-filter terminated ld), or a cons pair whose first component is the symbol :STOP-LD, which typically indicates that an error occurred while the value of variable 'ld-error-action was :RETURN!. See *note LD-ERROR-ACTION:: for details of this last case.  File: acl2-doc-emacs.info, Node: CALLING-LD-IN-BAD-CONTEXTS, Prev: LD, Up: LD CALLING-LD-IN-BAD-CONTEXTS errors caused by calling ld in inappropriate contexts The macro ld was designed to be called directly in the top-level ACL2 loop, although there may be a few occasions for calling it from functions. ACL2 cannot cope with invocations of ld during the process of loading a compiled file for a book, so this is an error. To see how that can happen, consider the following book, where file const.lsp contains the single form (defconst *foo* '(a b)). (in-package "ACL2") (defttag t) (progn! (ld "const.lsp")) An attempt to certify this book will cause an error, but that particular error can be avoided, as discussed below. If the book is certified, however, with production of a corresponding compiled file (which is the default behavior for certify-book), then any subsequent call of include-book that loads this compiled file will cause an error. Again, this error is necessary because of how ACL2 is designed; specifically, this ld call would interfere with tracking of constant definitions when loading the compiled file for the book. Because including such a book (with a compiled file) causes an error, then as a courtesy to the user, ACL2 arranges that the certification will fail (thus avoiding a surprise later when trying to include the book). The error in that case will look as follows. ACL2 Error in LD: It is illegal to call LD in this context. See DOC calling-ld-in-bad-contexts. If you really think it is OK to avoid this error, you can get around it by setting state global variable ld-okp to t: (assign ld-okp t). You can then certify the book in the example above, but you will still not be able to include it with a compiled file.  File: acl2-doc-emacs.info, Node: PRINT-GV, Next: PROPS, Prev: LD, Up: OTHER PRINT-GV print a form whose evaluation caused a guard violation By default, ACL2 checks input constraints on functions, known as guards. When guards are violated, an informative message is printed; but sometimes it is useful to investigate why the guard check fails. The utility print-gv is provided for that purpose. (Alternatively, you may prefer to avoid guards entirely with (set-guard-checking :none); see *note SET-GUARD-CHECKING::.) Example Forms: (print-gv) (print-gv :evisc-tuple (evisc-tuple 4 4 nil nil)) (print-gv :evisc-tuple (evisc-tuple 4 ; print-level 5 ; print-length (world-evisceration-alist state nil) nil ; hiding-cars )) General Form: (print-gv :evisc-tuple x) where the :evisc-tuple argument is optional and defaults to one that hides only the ACL2 logical world, by printing instead of a very large structure. See *note EVISC-TUPLE:: for a discussion of evisc-tuples. To see how one might use print-gv, consider the following definition. (defun foo (x) (declare (xargs :guard (and (my-first-predicate x) (my-second-predicate x)))) (cdr x)) Now suppose we evaluate a call of foo and obtain a guard violation. ACL2 !>(foo 3) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X), which is (AND (MY-FIRST-PREDICATE X) (MY-SECOND-PREDICATE X)), is violated by the arguments in the call (FOO 3). To debug: See :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> We can obtain (in essence) the guard form that was false, as follows. ACL2 !>(print-gv) (LET ((X '3)) (DECLARE (IGNORABLE X)) (AND (MY-FIRST-PREDICATE X) (MY-SECOND-PREDICATE X))) ACL2 !> But which conjunct is to blame? With a bit of editing we can try each conjunct in turn. ACL2 !>(LET ((X '3)) (DECLARE (IGNORABLE X)) (MY-FIRST-PREDICATE X)) T ACL2 !>(LET ((X '3)) (DECLARE (IGNORABLE X)) (MY-SECOND-PREDICATE X)) NIL ACL2 !> Aha! Now we can investigate why the second predicate fails for 3. The following hack will give you access in raw Lisp to the form printed by (print-gv). After a guard violation, just submit this form to raw Lisp: (print-gv1 (wormhole-data (cdr (assoc 'ev-fncall-guard-er-wormhole *wormhole-status-alist*))) state) If you use local stobjs (see *note WITH-LOCAL-STOBJ::) or stobj fields of stobjs, you may need to edit the output of print-gv in order to evaluate it. Consider the following example. (defstobj st fld) (defun g (x st) (declare (xargs :guard (consp x) :stobjs st) (ignore x)) (fld st)) (defun test () (with-local-stobj st (mv-let (result st) (mv (g 3 st) st) result))) (test) Then :print-gv yields the result shown below. (FLET ((G{GUARD} (X ST) (DECLARE (IGNORABLE X ST)) (AND (STP ST) (CONSP X)))) (G{GUARD} '3 ||)) In this example you could replace "||" by "st" to obtain a result of nil. But similar cases may require the use of a local stobj that is no longer available, in which case you may need to be creative in order to take advantage of :print-gv. Here is such an example. (defstobj st2 fld2) (defun g2 (st2) (declare (xargs :guard (null (fld2 st2)) :stobjs st2)) (mv 0 st2)) (defun test2 () (with-local-stobj st2 (mv-let (result st2) (let ((st2 (update-fld2 17 st2))) (g2 st2)) result))) (test2) In this case, :print-gv yields the following. (FLET ((G2{GUARD} (ST2) (DECLARE (IGNORABLE ST2)) (AND (ST2P ST2) (NULL (FLD2 ST2))))) (G2{GUARD} ||)) But if you replace "||" by "st", the guard holds; it is only the local stobj, which is no longer available, that produced a guard violation (because its field had been updated to a cons). ACL2 !>(FLET ((G2{GUARD} (ST2) (DECLARE (IGNORABLE ST2)) (AND (ST2P ST2) (NULL (FLD2 ST2))))) (G2{GUARD} st2)) T ACL2 !> Finally, we note that while print-gv is a utility for debugging guard violations, in contrast, see *note GUARD-DEBUG:: for a utility to assist in debugging failed proofs arising from guard verification.  File: acl2-doc-emacs.info, Node: PROPS, Next: PSO, Prev: PRINT-GV, Up: OTHER PROPS print the ACL2 properties on a symbol Example: :props assoc-eq Props takes one argument, a symbol, and prints all of the properties that are on that symbol in the ACL2 world.  File: acl2-doc-emacs.info, Node: PSO, Next: PSO!, Prev: PROPS, Up: OTHER PSO show the most recently saved output Evaluate :pso in order to print output that was generated in an environment where output was being saved; see *note SET-SAVED-OUTPUT:: for details. However, proof-tree output will be suppressed; use :pso! if you want that output to be printed as well. Also see *note PSOG::, for printing saved output in gag-mode.  File: acl2-doc-emacs.info, Node: PSO!, Next: PSOF, Prev: PSO, Up: OTHER PSO! show the most recently saved output, including proof-tree output Evaluate :pso! in order to print output that was generated in an environment where output was being saved; see *note SET-SAVED-OUTPUT:: for details. Note that proof-tree will be included; use :pso if you want that output to be suppressed. Also see *note PSOG::, for printing saved output in gag-mode.  File: acl2-doc-emacs.info, Node: PSOF, Next: PSOG, Prev: PSO!, Up: OTHER PSOF show the most recently saved output For a similar utility, see *note PSO::. Like :pso, the :psof command prints output that was generated in an environment where output was being saved, typically gag-mode; also see *note SET-SAVED-OUTPUT::. But unlike :pso, :psof takes a filename argument and saves output to that file, instead of to the terminal. For large proofs, :psof may complete more quickly than :pso. Note that as with :pso, proof-tree output will be suppressed. The first line of output from :psof directs the Emacs editor to use auto-revert mode. You can change the frequency of auto-reverting the buffer connected to a file by evaluating a suitable command in Emacs. For example, the command (setq auto-revert-interval .1) arranges for auto-revert mode to update as needed every 1/10 of a second.  File: acl2-doc-emacs.info, Node: PSOG, Next: PSTACK, Prev: PSOF, Up: OTHER PSOG show the most recently saved output in gag-mode Evaluate :psog in order to print output in gag-mode that was generated in an environment where output was being saved; see *note SET-SAVED-OUTPUT:: for details. Also see *note PSO:: and see *note PSO!:: for printing the full output.  File: acl2-doc-emacs.info, Node: PSTACK, Next: Q, Prev: PSOG, Up: OTHER PSTACK seeing what the prover is up to General Forms: (pstack) ; inspect break (pstack t) ; inspect break, printing all calls in abbreviated form (pstack :all) ; as above, but only abbreviating the ACL2 world When the form (pstack) is executed during a break from a proof, or at the end of a proof that the user has aborted, a "process stack" (or "prover stack") will be printed that gives some idea of what the theorem prover has been doing. Moreover, by evaluating (verbose-pstack t) before starting a proof (see *note VERBOSE-PSTACK::) one can get trace-like information about prover functions, including time summaries, printed to the screen during a proof. This feature is currently quite raw and may be refined considerably as time goes on, based on user suggestions. For example, the usual control of printing given by set-inhibit-output-lst is irrelevant for printing the pstack. The use of (pstack t) or (pstack :all) should only be used by those who are comfortable looking at functions in the ACL2 source code. Otherwise, simply use (pstack). * Menu: * VERBOSE-PSTACK:: seeing what the prover is up to (for advanced users) Entries in the pstack include the following (listed here alphabetically, except for the first). preprocess-clause, simplify-clause, etc. (in general,xxx-clause): top-level processes in the prover "waterfall" clausify: splitting a goal into subgoals ev-fncall: evaluating a function on explicit arguments ev-fncall-meta: evaluating a metafunction forward-chain: building a context for the current goal using forward-chaining rules induct: finding an induction scheme pop-clause: getting the next goal to prove by induction process-assumptions: creating forcing rounds remove-built-in-clauses: removing built-in clauses (see *note BUILT-IN-CLAUSE::) process-equational-polys: deducing interesting equations remove-trivial-equivalences: removing trivial equalities (and equivalences) from the current goal rewrite-atm: rewriting a top-level term in the current goal setup-simplify-clause-pot-lst: building the linear arithmetic database for the current goal strip-branches, subsumption-replacement-loop: subroutines of clausify waterfall: top-level proof control  File: acl2-doc-emacs.info, Node: VERBOSE-PSTACK, Prev: PSTACK, Up: PSTACK VERBOSE-PSTACK seeing what the prover is up to (for advanced users) General Forms: (verbose-pstack t) ; get trace-like information on prover during proofs (verbose-pstack '(fn1 fn2 ...)) ; as above, but omit calls of the indicated functions (verbose-pstack nil) ; turn off trace-like information on prover For example, (verbose-pstack '(ev-fncall)) will provide a trace of various prover functions during proofs, except for the function ev-fncall. By evaluating (verbose-pstack t) one can get trace-like information during subsequent proofs about prover functions, including time summaries, printed to the screen during a proof. To turn off this feature, evaluate (verbose-pstack nil). Also See *note PSTACK::.  File: acl2-doc-emacs.info, Node: Q, Next: QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP, Prev: PSTACK, Up: OTHER Q quit ACL2 (type :q) -- reenter with (lp) Example: ACL2 !>:Q The keyword command :q typed at the top-level of the ACL2 loop will terminate the loop and return control to the Common Lisp top-level (or, more precisely, to whatever program invoked lp). To reenter the ACL2 loop, execute (acl2::lp) in Common Lisp. You will be in the same state as you were when you exited with :q, unless during your stay in Common Lisp you messed the data structures representating the ACL2 state (including files, property lists, and single-threaded objects). Unlike all other keyword commands, typing :q is not equivalent to invoking the function q. There is no function q.  File: acl2-doc-emacs.info, Node: QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP, Next: QUIT, Prev: Q, Up: OTHER QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP (advanced topic) controlling a heuristic in the prover's clausifier This topic is probably only of interest to those who are undertaking particularly complex proof developments. The ACL2 prover's handling of propositional logic uses a heuristic that we believe might cause the prover to slow down significantly or even to trigger a stack overflow. However, we believe these negative effects are only felt on very large formulas -- formulas that are likely to cause similar degradation of many other parts of ACL2. The following two events turn off that heuristic (by eliminating calls to source function quick-and-dirty-subsumption-replacement-step, after which this documentation topic is named). (defun quick-and-dirty-srs-off (cl1 ac) (declare (ignore cl1 ac) (xargs :mode :logic :guard t)) nil) (defattach quick-and-dirty-srs quick-and-dirty-srs-off) However, if you feel the need to try this out, please remember that the proof is likely to fail anyway since other parts of ACL2 will probably be stressed. A more basic problem with your proof strategy may exist: the formulas are getting extraordinarily large. A common approach for avoiding such problem include disabling functions (see *note IN-THEORY::). Other less common approaches might help if you are sufficiently desperate, such as defining your own IF function to use in place of the built-in IF. If you do find an example for which the above two events provide significant benefit, we (the ACL2 implementors) would be interested in hearing about it. To turn the heuristic back on: (defattach quick-and-dirty-srs quick-and-dirty-srs-builtin)  File: acl2-doc-emacs.info, Node: QUIT, Next: REBUILD, Prev: QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP, Up: OTHER QUIT quit entirely out of Lisp Same as good-bye.  File: acl2-doc-emacs.info, Node: REBUILD, Next: REDO-FLAT, Prev: QUIT, Up: OTHER REBUILD a convenient way to reconstruct your old state Examples: ACL2 !>(rebuild "project.lisp") ACL2 !>(rebuild "project.lisp" t) ACL2 !>(rebuild "project.lisp" t :dir :system) ACL2 !>(rebuild "project.lisp" :all) ACL2 !>(rebuild "project.lisp" :query) ACL2 !>(rebuild "project.lisp" 'lemma-22) Rebuild allows you to assume all the commands in a given file or list, supplied in the first argument. Because rebuild processes an arbitrary sequence of commands with ld-skip-proofsp t, it is unsound! However, if each of these commands is in fact admissible, then processing them with rebuild will construct the same logical state that you would be in if you typed each command and waited through the proofs again. Thus, rebuild is a way to reconstruct a state previously obtained by proving your way through the commands. The second, optional argument to rebuild is a "filter" (see *note LD-PRE-EVAL-FILTER::) that lets you specify which commands to process. You may specify t, :all, :query, or a new logical name. t and :all both mean that you expect the entire file or list to be processed. :query means that you will be asked about each command in turn. A new name means that all commands will be processed as long as the name is new, i.e., rebuild will stop processing commands immediately after executing a command that introduces name. Rebuild will also stop if any command causes an error. You may therefore wish to plant an erroneous form in the file, e.g., (mv t nil state), (see *note LD-ERROR-TRIPLES::), to cause rebuild to stop there. The form (i-am-here) is such a pre-defined form. If you do not specify a filter, rebuild will query you for one. Inspection of the definition of rebuild, e.g., via :pc rebuild-fn, will reveal that it is just a glorified call to the function ld. See *note LD:: if you find yourself wishing that rebuild had additional functionality. If you supply the above "filter" argument, then you may also supply the keyword argument :dir, which is then passed to ld; see *note LD::.  File: acl2-doc-emacs.info, Node: REDO-FLAT, Next: RESET-LD-SPECIALS, Prev: REBUILD, Up: OTHER REDO-FLAT redo on failure of a progn, encapsulate, or certify-book When one submits an encapsulate, progn, or certify-book command and there is a failure, ACL2 restores its logical world as though the command had not been run. But sometimes one would like to debug the failure by re-executing all sub-events that succeeded up to the point of failure, and then re-executing the failed sub-event. Said differently, imagine that the events under an encapsulate, progn, or certify-book form were flattened into a list of events that were then submitted to ACL2 up to the point of failure. This would put us in the state in which the original failed event had failed, so we could now replay that failed event and try modifying it, or first proving additional events, in order to get it admitted. Redo-flat is provided for this purpose. Consider the following (rather nonsensical) example, in which the defun of f3 fails (the body is y but the formal parameter list is (x)). (encapsulate () (defun f1 (x) x) (encapsulate () (local (defthm hack (equal (car (cons x y)) x)))) (encapsulate () (local (defthm hack (equal (+ x y) (+ y x))))) (encapsulate () (make-event '(defun f2 (x) x)) (progn (defthm foo (equal x x) :rule-classes nil) (defun f3 (x) y))) (defun f4 (x) x) (defun f5 (x) y)) After this attempt fails, you can evaluate the following form. (redo-flat) This will first lay down a deflabel event, (deflabel r), so that you can eventually remove your debugging work with (:ubt! r). Then the successful sub-events that preceded the failure will be executed with proofs skipped (so that this execution is fast). Then, the failed event will be executed. Finally, a :pbt command is executed so that you can see a summary of the events that executed successfully. You can eliminate some of the steps above by supplying keyword values, as follows. (redo-flat :succ succ ; Skip the successful sub-events if val is nil. :fail fail ; Skip the failed sub-event if val is nil. :label lab ; Skip deflabel if lab or succ is nil, else use (deflabel lab). :pbt val ; Skip the final :pbt if val, lab, or succ is nil. ) Also, you can avoid skipping proofs for the successful sub-events by supplying keyword :succ-ld-skip-proofsp with a valid value for ld-skip-proofsp; see *note LD-SKIP-PROOFSP::. For example, you might want to execute (redo-flat :succ-ld-skip-proofsp nil) if you use the must-fail utility from community book make-event/eval.lisp, since for example (must-fail (thm (equal x y))) normally succeeds but would cause an error if proofs are skipped. If you prefer only to see the successful and failed sub-events, without any events being re-executed, you may evaluate the following form instead. (redo-flat :show t) For the example above, this command produces the following output. List of events preceding the failure: ((DEFUN F1 (X) X) (ENCAPSULATE NIL (LOCAL (DEFTHM HACK (EQUAL (CAR (CONS X Y)) X)))) (ENCAPSULATE NIL (LOCAL (DEFTHM HACK (EQUAL (+ X Y) (+ Y X))))) (MAKE-EVENT '(DEFUN F2 (X) X)) (DEFTHM FOO (EQUAL X X) :RULE-CLASSES NIL)) Failed event: (DEFUN F3 (X) Y) ACL2 !> Redo-flat uses a scheme that should not cause spurious name conflicts for local events. Above, it is mentioned that events are "flattened"; now we clarify this notion. Each sub-event that succeeds and is an encapsulate or progn is left intact. Only such events that fail are replaced by their component events. Thus, in the example above, there is no conflict between the two local sub-events named "hack," because these are contained in successful encapsulate sub-events, which are therefore not flattened. The progn and two encapsulate events surrounding the definition of f3 are, however, flattened, because that definition failed to be admitted. Normally, redo-flat will have the desired effect even if you interrupted a proof (with control-c). However, redo-flat will not produce the desired result after an interrupt if you have enabled the debugger using (set-debugger-enable t),  File: acl2-doc-emacs.info, Node: RESET-LD-SPECIALS, Next: SAVE-EXEC, Prev: REDO-FLAT, Up: OTHER RESET-LD-SPECIALS restores initial settings of the ld specials Examples: (reset-ld-specials t) (reset-ld-specials nil) Roughly speaking, the ld specials are certain state global variables, such as current-package, ld-prompt, and ld-pre-eval-filter, which are managed by ld as though they were local variables. These variables determine the channels on which ld reads and prints and control many options of ld. See *note LD:: for the details on what the ld specials are. This function, reset-ld-specials, takes one Boolean argument, flg. The function resets all of the ld specials to their initial, top-level values, except for the three channel variables, standard-oi, standard-co, and proofs-co, which are reset to their initial values only if flg is non-nil. Of course, if you are in a recursive call of ld, then when you exit that call, the ld specials will be restored to the values they had at the time ld was called recursively. To see what the initial values are, inspect the value of the constant *initial-ld-special-bindings*.  File: acl2-doc-emacs.info, Node: SAVE-EXEC, Next: SET-ACCUMULATED-PERSISTENCE, Prev: RESET-LD-SPECIALS, Up: OTHER SAVE-EXEC save an executable image and a wrapper script Save-exec saves your ACL2 state so that you can immediately re-start later in that same state. This utility can be useful for a project with books to be included every time ACL2 is started, to avoid time taken to run include-book. Another use of save-exec is to save an executable that takes command-line arguments beyond those normally passed to the host Lisp executable. All arguments of a call of save-exec are evaluated. Examples: ; Save an executable script named my-saved_acl2, with the indicated message ; added to the start-up banner: (save-exec "my-saved_acl2" "This saved image includes Version 7 of Project Foo.") ; Same as above, but instead with a generic comment in the start-up banner: (save-exec "my-saved_acl2" nil) ; Arrange that the generated script passes the indicated arguments to be ; processed by the Lisp (ACL2) executable (where this example is specific to ; the case that CCL is the host Lisp): (save-exec "my-saved_acl2" nil :host-lisp-args "--no-init -Z 256M") ; Arrange that the generated script passes along the indicated arguments ; to Lisp (ACL2), but that they are not processed by Lisp other than to ; record the additional arguments (see (6) below). (save-exec "my-saved_acl2" nil :inert-args "abc xyz -i foo") ; Combining the preceding two examples: (save-exec "my-saved_acl2" nil :host-lisp-args "--no-init -Z 256M" :inert-args "abc xyz -i foo") ; Immediately exit the ACL2 read-eval-print loop after starting up. (save-exec "my-acl2" nil :return-from-lp t) ; Immediately exit the ACL2 read-eval-print loop after starting up and ; defining function FOO in the logic. (save-exec "my-acl2" "Start with foo defined." :return-from-lp '(with-output :off :all (defun foo (x) x))) ; Immediately exit the ACL2 read-eval-print loop after starting up and ; defining variable xxx in raw Lisp. (save-exec "my-acl2" "Start with xxx defined." :return-from-lp '(with-output :off :all (ld '((set-raw-mode-on!) (defvar xxx (make-list 10)) (set-raw-mode nil) (u))))) Each example above generates a file named "my-saved_acl2". That file is quite similar in form to the script generated when building ACL2 directly from source code; details are below. For example, here are the contents of that generated file if the host Lisp is CCL (but where dates and pathnames are specific to one's environment). Here, we break lines using `\', but the exec command is actually on a single line. #!/bin/sh # Saved August 16, 2013 23:06:49 # then August 17, 2013 11:01:56 export CCL_DEFAULT_DIRECTORY="/projects/acl2/lisps/ccl/15542/ccl" exec "/projects/ccl/lx86cl64" -I "/u/smith/my-saved_acl2.lx86cl64" \ -Z 64M -K ISO-8859-1 -e "(acl2::acl2-default-restart)" \ --no-init -Z 256M \ -- \ abc xyz -i foo \ "$@" General Form: (save-exec exec-filename extra-startup-string :host-lisp-args host-lisp-args :inert-args inert-args :return-from-lp return-from-lp) where the keyword arguments are optional, and arguments are as follows. Exec-filename is the filename of the proposed executable. Extra-startup-string is a non-empty string to be printed after the normal ACL2 startup message when you start up the saved image. However, extra-startup-string is allowed to be nil, in which case a generic string will be printed instead. Host-lisp-args can be nil (the default), but if it is a non-nil value, then it is a string to be inserted into the command line in the saved script, specifying additional arguments that are to be processed by the host Lisp executable. (Note for SBCL only: these are runtime options; for toplevel options, see (8) below.) Inert-args can be nil (the default), but if it is a non-nil value, then it is a string to be inserted into the command line in the saved script, specifying additional arguments that are not to be processed by the host Lisp executable. Return-from-lp is nil by default. Regardless of the value of return-from-lp, ACL2 starts up and enters its read-eval-print loop as usual; see *note LP::. Normally you'll stay inside that loop, but if return-from-lp is not nil, then it is evaluated in the loop, which is then exited, leaving you in raw Lisp. Evaluation of return-from-lp is done with ld options that minimize output; also see *note WITH-OUTPUT:: to minimize output. Suggestion: let return-from-lp be t if you simply want to exit the read-eval-print loop at startup, without evaluating any (nontrivial) form. The remainder of this documentation focuses on the options other than return-from-lp. *Details*: (1) You must first exit the ACL2 read-eval-print loop, typically by executing :q, before evaluating a save-exec call; otherwise an error occurs. (2) The image will be saved so that in the new image, the raw Lisp package and the package in the ACL2 read-eval-print loop (see *note LP::) will be the same as their respective values at the time save-exec is called. (3) Save-exec generates a small script file (e.g., "my-saved_acl2" in the examples above), similar in form (see (4) below) to the script generated when building ACL2 directly from source code, but with a comment line indicating the time at which the new script is written. Save-exec also saves an associated binary file. The binary file's name is obtained by putting a suffix on the script filename; for example, if the host Lisp is GCL running on a Linux or Darwin (MacOS) system, then that binary file has the name my-saved_acl2.gcl in the examples above. (4) If inert-args is nil (for example if keyword :inert-args is omitted), then when the generated ACL2 script is invoked with command line arguments, those arguments will be passed to the host Lisp; otherwise they will not. Thus for the example above, suppose we invoke the generated script as follows. my-saved_acl2 -a bcd -e fgh If my-saved_acl2 was generated using a save-exec command with a non-nil value specified for keyword :inert-args, then the arguments "-a bcd -e fgh" will not be passed to the host Lisp; otherwise, they will be. Note that for ACL2 executable scripts generated by an ordinary ACL2 build from sources, the latter case (i.e., without inert-args) takes place. (5) The generated script, which specifies execution with /bin/sh, will generally contain a line of one of the following forms. (But for SBCL, see (8) below.) In the examples that follow, ACL2_options is a suitable list of command-line arguments given to the ACL2 executable. The quoted string "$@" is intended to allow the user to pass additional command-line arguments to that executable. If host-lisp-args and inert-args are omitted (or nil): exec "$@" More generally, host-lisp-args is inserted immediately after , but only if it is non-nil (hence a string). If inert-args is nil, we thus get: exec host-lisp-args "$@" If host-lisp-args redefines a value from , then it is up to the host lisp which value to use. For example, experiments show that in CCL, if -Z appears twice, each with a legal value, then the second value is the one that is used (i.e. it does indeed override the original value written out by ACL2 in . But experiments also show that in LispWorks, where "-init -" is included in , then inclusion of "-init foo.lisp" in host-lisp-args is ignored. The remaining cases below are for a non-nil value of inert-args. In each case, if host-lisp-args is nil then it should be omitted from the displayed command. If inert-args is t then an additional argument, `-', indicates that when ACL2 is given command line arguments, these should not be processed by the host Lisp (other than recording them; see (6) below): exec host-lisp-args -- "$@" If inert-args is a string then the result is similar to the above, except that inert-args is added immediately after `-': exec host-lisp-args -- inert-args "$@" (6) See community books books/oslib/argv for a utility that returns a list of all inert-args from an invocation of ACL2. (7) Suppose that you invoke an ACL2 script, say "my-saved_acl2", that was generated by save-exec, and then optionally evaluate some forms. Then you may save a new ACL2 script with save-exec. The new script will contain comment lines that extend comment lines in "my-saved_acl2" with a new write date, but otherwise will be identical to the script that would have been generated by executing the new save-exec call after invoking the original ACL2 executable (built directly from ACL2 sources) instead of "my-saved_acl2". In other words, the options added by the earlier save-exec call that created "my-saved_acl2" are discarded by the new save-exec call. However, the .core file will built on top of the .core file that was consulted when "my-saved_acl2" was invoked. (8) The following note pertains only to the case that the host Lisp is SBCL. For SBCL, the scripts written are analogous to, but slightly different from, those shown above. Please note that for SBCL, the host-lisp-args are what the SBCL manual calls "runtime options". For SBCL only, an extra keyword argument, :toplevel-args, may be used for specifying what the SBCL manual calls "toplevel options. As with :host-lisp-args, this value, toplevel-args, should be nil (the default) or a string. Here is an example. (save-exec "my-saved_acl2" nil :host-lisp-args "--dynamic-space-size 12000" :toplevel-args "--eval '(print \"HELLO\")'" :inert-args "--my-option my-value") The script generated by this example call of save-exec contains a line such as the following (with the same convention for `\' as before) exec "/projects/sbcl-1.1.7-x86-64-linux/src/runtime/sbcl" \ --dynamic-space-size 2000 --control-stack-size 8 \ --core "/u/smith/my-saved_acl2.core" --dynamic-space-size 12000 \ --end-runtime-options \ --no-userinit --eval '(acl2::sbcl-restart)' \ --eval '(print "HELLO")' \ --end-toplevel-options \ --my-option my-value \ "$@" In general, the generated script is of one of the following forms (with the same convention for `\' as before). For the case that inert-args is nil: exec \ host-lisp-args --end-runtime-options \ host-lisp-args \ "$@" For the case that inert-args is non-nil: exec \ host-lisp-args --end-runtime-options \ host-lisp-args --end-toplevel-options \ inert-args "$@" Notice that as before, when the generated script is invoked (for example, at the shell), additional command-line arguments provided at that time are passed to Lisp if and only if inert-args is nil. For SBCL, when they are passed to Lisp they are passed as toplevel options, not as runtime options.  File: acl2-doc-emacs.info, Node: SET-ACCUMULATED-PERSISTENCE, Next: SHARP-BANG-READER, Prev: SAVE-EXEC, Up: OTHER SET-ACCUMULATED-PERSISTENCE See *note ACCUMULATED-PERSISTENCE::.  File: acl2-doc-emacs.info, Node: SHARP-BANG-READER, Next: SHARP-COMMA-READER, Prev: SET-ACCUMULATED-PERSISTENCE, Up: OTHER SHARP-BANG-READER package prefix that is not restricted to symbols Examples: ACL2 !>(defpkg "FOO" nil) Summary Form: ( DEFPKG "FOO" ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) "FOO" ACL2 !>'#!foo(a b) (FOO::A FOO::B) ACL2 !>'#!foo(a #!acl2 b) (FOO::A B) ACL2 !>'#!foo(#!acl2 a b) (A FOO::B) ACL2 !>'#!foo(#!"ACL2" a b) (A FOO::B) ACL2 !> The ACL2 reader supports the syntax #!pkg-name expr where pkg-name is a string or symbol that names a package known to ACL2. As illustrated above, this syntax nests as one might expect. In the special case that expr is a symbol, #!pkg-name expr is equivalent to pkg-name::expr.  File: acl2-doc-emacs.info, Node: SHARP-COMMA-READER, Next: SHARP-DOT-READER, Prev: SHARP-BANG-READER, Up: OTHER SHARP-COMMA-READER DEPRECATED read-time evaluation of constants The use of `#,' has been deprecated. Please use `#.' instead; see *note SHARP-DOT-READER::.  File: acl2-doc-emacs.info, Node: SHARP-DOT-READER, Next: SHARP-U-READER, Prev: SHARP-COMMA-READER, Up: OTHER SHARP-DOT-READER read-time evaluation of constants Example: ACL2 !>(defconst *a* '(a b c)) Summary Form: ( DEFCONST *A* ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) *A* ACL2 !>(quote (1 2 #.*a* 3 4)) (1 2 (A B C) 3 4) ACL2 !> The ACL2 reader supports the syntax #.*a* where *a* was defined by defconst. In this case, the reader treats #.*a* as though it were reading the value of *a*. This feature can be useful in conjunction with the use of evisc-table to abbreviate large constants, so that the abbreviation can be read back in; see *note EVISC-TABLE::. Remarks. (1) The ACL2 reader only supports `#.' as described above, unlike Common Lisp. Older versions (preceding 3.5) used `#.' to abort, but that functionality is now carried out by (a!); see *note A!::. For a related feature that only pops up one level, see *note P!::. (2) If you call certify-book on a book that contains a form `#.*foo*', the *foo* must already be defined in the world in which you issue the certify-book command. The reason is that certify-book reads the entire book before evaluating its forms.  File: acl2-doc-emacs.info, Node: SHARP-U-READER, Next: SHOW-ACCUMULATED-PERSISTENCE, Prev: SHARP-DOT-READER, Up: OTHER SHARP-U-READER allow underscore characters in numbers Example: ACL2 !>#ub1000_1000_1000_ 2184 ACL2 !>#b100010001000 2184 ACL2 !>#uo1_1 9 ACL2 !>#o11 9 ACL2 !>#u34_5 345 ACL2 !>#u345 345 ACL2 !>345 345 ACL2 !>#ux12_a 298 ACL2 !>#ux12a 298 ACL2 !>#u x12a 298 ACL2 !>#x12a 298 ACL2 !>#u123_456/7_919 123456/7919 ACL2 !> The ACL2 reader supports the use of #ub, #uo, and #ux where one would otherwise write #b, #o, and #x, respectively (for binary, octal, and hexadecimal numerals), but where underscore characters (`_') are allowed but ignored. Also supported is the prefix #u in front of a an expression that is a decimal numeral except that underscore characteres are allowed but ignored. The precise specification of #u is as follows. The Lisp reader reads one expression after the #u. If the result is a number, then that number is returned by the reader. Otherwise the result must be a symbol whose name begins with one of the characters `B', `O', or `X', or else a decimal digit (one of the characters `0, 1, ..., 9'). All underscores are removed from the name of that symbol to obtain a string and in the first three cases only, a `#' character is prepended to that string. The resulting string is then handed to the Lisp reader in order to obtain the final result, which must be a number or else an error occurs.  File: acl2-doc-emacs.info, Node: SHOW-ACCUMULATED-PERSISTENCE, Next: SKIP-PROOFS, Prev: SHARP-U-READER, Up: OTHER SHOW-ACCUMULATED-PERSISTENCE See *note ACCUMULATED-PERSISTENCE::.  File: acl2-doc-emacs.info, Node: SKIP-PROOFS, Next: THM, Prev: SHOW-ACCUMULATED-PERSISTENCE, Up: OTHER SKIP-PROOFS skip proofs for a given form -- a quick way to introduce unsoundness Example Form: (skip-proofs (defun foo (x) (if (atom x) nil (cons (car x) (foo (reverse (cdr x))))))) General Form: (skip-proofs form) where form is processed as usual except that the proof obligations usually generated are merely assumed. Normally form is an event; see *note EVENTS::. If you want to put skip-proofs around more than one event, consider the following (see *note PROGN::): (skip-proofs (progn event1 event2 ... eventk)). WARNING: Skip-proofs allows inconsistent events to be admitted to the logic. Use it at your own risk! Sometimes in the development of a formal model or proof it is convenient to skip the proofs required by a given event. By embedding the event in a skip-proofs form, you can avoid the proof burdens generated by the event, at the risk of introducing unsoundness. Below we list four illustrative situations in which you might find skip-proofs useful. 1. The termination argument for a proposed function definition is complicated. You presume you could admit it, but are not sure that your definition has the desired properties. By embedding the defun event in a skip-proofs you can "admit" the function and experiment with theorems about it before undoing (see *note UBT::) and then paying the price of its admission. Note however that you might still have to supply a measure. The set of formals used in some valid measure, known as the "measured subset" of the set of formals, is used by ACL2's induction heuristics and therefore needs to be suitably specified. You may wish to specify the special measure of (:? v1 ... vk), where (v1 ... vk) enumerates the measured subset. 2. You intend eventually to verify the guards for a definition but do not want to take the time now to pursue that. By embedding the verify-guards event in a skip-proofs you can get the system to behave as though the guards were verified. 3. You are repeatedly recertifying a book while making many experimental changes. A certain defthm in the book takes a very long time to prove and you believe the proof is not affected by the changes you are making. By embedding the defthm event in a skip-proofs you allow the theorem to be assumed without proof during the experimental recertifications. 4. You are constructing a proof top-down and wish to defer the proof of a defthm until you are convinced of its utility. You can embed the defthm in a skip-proofs. Of course, you may find later (when you attempt prove the theorem) that the proposed defthm is not a theorem. Unsoundness or Lisp errors may result if the presumptions underlying a use of skip-proofs are incorrect. Therefore, skip-proofs must be considered a dangerous (though useful) tool in system development. Roughly speaking, a defthm embedded in a skip-proofs is essentially a defaxiom, except that it is not noted as an axiom for the purposes of functional instantiation (see *note LEMMA-INSTANCE::). But a skipped defun is much more subtle since not only is the definitional equation being assumed but so are formulas relating to termination and type. The situation is also difficult to characterize if the skip-proofs events are within the scope of an encapsulate in which constrained functions are being introduced. In such contexts no clear logical story is maintained; in particular, constraints aren't properly tracked for definitions. A proof script involving skip-proofs should be regarded as work-in-progress, not as a completed proof with some unproved assumptions. A skip-proofs event represents a promise by the author to admit the given event without further axioms. In other words, skip-proofs should only be used when the belief is that the proof obligations are indeed theorems in the existing ACL2 logical world. ACL2 allows the certification of books containing skip-proofs events by providing the keyword argument :skip-proofs-okp t to the certify-book command. This is contrary to the spirit of certified books, since one is supposedly assured by a valid certificate that a book has been "blessed." But certification, too, takes the view of skip-proofs as "work-in-progress" and so allows the author of the book to promise to finish. When such books are certified, a warning to the author is printed, reminding him or her of the incurred obligation. When books containing skip-proofs are included into a session, a warning to the user is printed, reminding the user that the book is in fact incomplete and possibly inconsistent. This warning is in fact an error if :skip-proofs-okp is nil in the include-book form; see *note INCLUDE-BOOK::. We conclude with a technical note. Skip-proofs works by binding the ld special ld-skip-proofsp to t unless it is already bound to a non-nil value; see *note LD-SKIP-PROOFSP::.  File: acl2-doc-emacs.info, Node: THM, Next: TOP-LEVEL, Prev: SKIP-PROOFS, Up: OTHER THM prove a theorem Example: (thm (equal (app (app a b) c) (app a (app b c)))) Also see *note DEFTHM::. Unlike defthm, thm does not create an event; it merely causes the theorem prover to attempt a proof. General Form: (thm term :hints hints :otf-flg otf-flg :doc doc-string) where term is a term alleged to be a theorem, and hints, otf-flg and doc-string are as described in the corresponding :doc entries. The three keyword arguments above are all optional.  File: acl2-doc-emacs.info, Node: TOP-LEVEL, Next: TRANS, Prev: THM, Up: OTHER TOP-LEVEL evaluate a top-level form as a function body Some forms, such as calls of with-local-stobj, are illegal when supplied directly to the ACL2 top-level loop. The macro top-level provides a workaround in such cases, by defining a temporary :program-mode function named top-level-fn whose only argument is state and whose body is the form to be evaluated. When the call of top-level returns there is no change to the existing ACL2 logical world. The following edited log illustrates all of the above points. ACL2 !>:pbt 0 0 (EXIT-BOOT-STRAP-MODE) ACL2 !>(defstobj st fld) Summary Form: ( DEFSTOBJ ST ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) ST ACL2 !>(top-level (with-local-stobj st (mv-let (result st) (let ((st (update-fld 17 st))) (mv (fld st) st)) result))) 17 ACL2 !>(top-level (with-local-stobj st (mv-let (result st) (let ((st (update-fld 17 st))) (mv (fld st) st)) (mv nil result state)))) 17 ACL2 !>(top-level (with-local-stobj st (mv-let (result st) (let ((st (update-fld 17 st))) (mv (fld st) st)) (mv result state)))) (17 ) ACL2 !>:pbt 0 0 (EXIT-BOOT-STRAP-MODE) d 1:x(DEFSTOBJ ST FLD) ACL2 !> Each argument of top-level after the first should be a declare form or documentation string, as the list of these extra arguments will be placed before the first argument when forming the definition of top-level-fn. Top-level generates a call of ld, so that the value returned is printed in the normal way. The call of top-level itself actually evaluates to (mv erp :invisible state), where erp is t if and only evaluation of the call of top-level-fn caused an error, which normally results in no additional output. (For details about "caused an error", see the definition of top-level in the ACL2 source code, and see *note LD-ERROR-ACTION::.)  File: acl2-doc-emacs.info, Node: TRANS, Next: TRANS!, Prev: TOP-LEVEL, Up: OTHER TRANS print the macroexpansion of a form Examples: :trans (list a b c) :trans (caddr x) :trans (cond (p q) (r)) This function takes one argument, an alleged term, and translates it, expanding the macros in it completely. Either an error is caused or the formal meaning of the term is printed. We also print the "output signature" which indicates how many results are returned and which are single-threaded objects. For example, a term that returns one ordinary object (e.g., an object other than STATE or a user-defined single-threaded object (see *note DEFSTOBJ::)) has the output signature => * A term that returns the single-threaded object STATE has the output signature => STATE and a term that returns four results might have the output signature => (MV $MEM * * STATE) This signature indicates that the first result is the (user defined) single-threaded object $MEM, that the next two results are ordinary, and that the last result is STATE. See *note TRANS!:: for a corresponding command that does not enforce restrictions of single-threaded objects. It is sometimes more convenient to use trans1 which is like trans but which only does top-level macroexpansion. For more, see *note TERM::.  File: acl2-doc-emacs.info, Node: TRANS!, Next: TRANS1, Prev: TRANS, Up: OTHER TRANS! print the macroexpansion of a form without single-threadedness concerns Examples: :trans! (list a b c) :trans! (append x state) :Trans! is identical to :trans, except that unlike :trans, :trans! ignores single-threadedness restrictions. Thus, the second form above is legal for :trans!. Also see *note TRANS:: and see *note TRANS1::.  File: acl2-doc-emacs.info, Node: TRANS1, Next: VERIFY-GUARDS-FORMULA, Prev: TRANS!, Up: OTHER TRANS1 print the one-step macroexpansion of a form Examples: :trans1 (list a b c) :trans1 (caddr x) :trans1 (cond (p q) (r)) This function takes one argument, an alleged term, and expands the top-level macro in it for one step only. Either an error is caused, which happens when the form is not a call of a macro, or the result is printed. Also see *note TRANS::, which translates the given form completely.  File: acl2-doc-emacs.info, Node: VERIFY-GUARDS-FORMULA, Next: WALKABOUT, Prev: TRANS1, Up: OTHER VERIFY-GUARDS-FORMULA view the guard proof obligation, without proving it See *note VERIFY-GUARDS:: and see *note GUARD:: for a discussion of guards. This utility is provided for viewing a guard proof obligation, without doing a proof. Example Forms: (verify-guards-formula foo) (verify-guards-formula foo :guard-debug t) (verify-guards-formula foo :otf-flg dont-care :xyz whatever) (verify-guards-formula (+ (foo x) (bar y)) :guard-debug t) Verify-guards-formula allows all keywords, but only pays attention to :guard-debug, which has the same effect as in verify-guards (see *note GUARD-DEBUG::). Apply verify-guards-formula to a name just as you would use verify-guards, but when you only want to view the formula rather than creating an event. If the first argument is not a symbol, then it is treated as the body of a defthm event for which you want the guard proof obligation. See *note GUARD-OBLIGATION:: if you want to obtain guard proof obligations for use in a program.  File: acl2-doc-emacs.info, Node: WALKABOUT, Next: WITH-PROVER-STEP-LIMIT, Prev: VERIFY-GUARDS-FORMULA, Up: OTHER WALKABOUT explore an ACL2 cons tree By typing (walkabout x state) for an ACL2 term x whose value is a cons tree, you can walk around that tree. For example, ACL2 developers often use this utility to explore the ACL2 logical world. When you issue a walkabout command, a summary of commands will be printed before you enter an interactive loop. Commands: 0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q. In the interactive walkabout loop, a positive integer n takes you to the nth position, while 0 takes you up a level. The commands nx and bk take you to the next and previous position, respectively, at the same level. The command pp prints the current object in full, while (pp level length) hides sub-objects below the indicated level and past the indicated length, if non-nil; see *note EVISC-TUPLE::. The command (pp n) abbreviates (pp n n), so in particular (pp nil) is equivalent to pp. The following example illustrates the commands described above. ACL2 !>(walkabout (append '(a (b1 b2 b3)) '(c d e f)) state) Commands: 0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q. (A (B1 B2 B3) C ...) :2 (B1 B2 B3) :3 B3 :0 (B1 B2 B3) :nx C :nx D :0 (A (B1 B2 B3) C ...) :pp (A (B1 B2 B3) C D E F) :(pp 4) (A (B1 B2 B3) C D ...) :(pp 1 4) (A # C D ...) :(pp nil 2) (A (B1 B2 ...) ...) :q ACL2 !> Finally we describe the commands q, =, and (= symb), where symb is a symbol. The command q simply causes an exit from the walkabout loop. The command = also exits, but causes the current object to be printed in full. The command (= symb) saves an association of symb with the current object, which can be retrieved outside the walkabout loop using the macro walkabout=, as illustrated below. :2 (B1 B2 B3) :(= my-list) (walkabout= MY-LIST) is (B1 B2 B3) :q ACL2 !>(walkabout= MY-LIST) (B1 B2 B3) ACL2 !> Finally, we remark that for trees that are not true-lists, walkabout treats the dot as an object that can be "walked about". The following example illustrates this point. ACL2 !>(walkabout '(c d e . f) state) Commands: 0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q. (C D E . F) :3 E :nx . :nx F :0 (C D E . F) :4 . :0 (C D E . F) :5 F :  File: acl2-doc-emacs.info, Node: WITH-PROVER-STEP-LIMIT, Next: WITH-PROVER-TIME-LIMIT, Prev: WALKABOUT, Up: OTHER WITH-PROVER-STEP-LIMIT limit the number of steps for proofs Logically, (with-prover-step-limit expr form) is equivalent to form, except that if the number of "prover steps" executed during evaluation of form exceeds a bound specified by the value of expr, then that proof will abort. See *note SET-PROVER-STEP-LIMIT:: for a related utility that sets the limit on prover steps globally instead of setting it for just one form, and for a discussion of the notion of "prover steps", which could change in future ACL2 releases. For a related utility based on time instead of prover steps, see *note WITH-PROVER-TIME-LIMIT::; but as discussed in the documentation for set-prover-step-limit, there is at best a loose connection between the counting of steps and with-prover-time-limit. The arguments of (with-prover-step-limit expr form) are evaluated. However, the (optional) argument flg is not evaluated in (with-prover-step-limit expr flg form). Depending on the machine you are using, you may have less than a half-hour of time before the number of prover steps exceeds the maximum limit on prover steps that may be imposed, which is one less than the value of *default-step-limit*. But there is no limit unless you explicitly call with-prover-step-limit or set-prover-step-limit. For examples of how step-limits work besides those presented below, see the community book books/misc/misc2/step-limits.lisp. For a utility that returns an indicator of the number of prover steps most recently taken, see *note LAST-PROVER-STEPS::. Note that with-prover-step-limit may not be called inside definitions, and that it is simply the identity macro in raw Lisp. However, with-prover-step-limit! may be called in place of with-prover-step-limit, with the effect described here even in raw Lisp. Examples: ; Limit (mini-proveall) to 100,000 prover steps (which happens to suffice) (with-prover-step-limit 100000 (mini-proveall)) ; Same as above for the inner call of with-prover-step-limit; so the ; mini-proveall call completes, but then we get an error because the second ; argument of the outer with-prover-step-limit call took more than 200 ; steps. (with-prover-step-limit 200 (with-prover-step-limit 100000 (mini-proveall))) ; Same as preceding form just above, except that this time there is no error, ; because the inner call of with-prover-step-limit has an extra argument ; of t inserted into the second argument position, which specifies that this ; entire inner call is treated as though it uses no prover steps. (with-prover-step-limit 200 (with-prover-step-limit 100000 t (mini-proveall))) ; The inner call limits (mini-proveall) to 200 prover steps, which fails ; almost immediately. (with-prover-step-limit 100000 (with-prover-step-limit 200 (mini-proveall))) ; Do not limit the number of prover steps, regardless of such a limit imposed ; globally or by the surrounding context. (with-prover-step-limit nil (mini-proveall)) ; Same as just above (indeed, nil above is converted to ; *default-step-limit*): (with-prover-step-limit *default-step-limit* (mini-proveall)) ; Advanced example: Limit the indicated theorem to 100 steps, and when the ; proof does not complete, then put down a label instead. (mv-let (erp val state) (with-prover-step-limit 100 (thm (equal (append (append x x) x) (append x x x)))) (if erp (deflabel foo :doc "Attempt failed.") (value (list :succeeded-with val)))) ; Use extra argument of t to avoid ``charging'' steps for the indicated ; form. (with-prover-step-limit 500 (encapsulate () (with-prover-step-limit 500 t ; Don't charge prover steps for this first defthm. (defthm test1 (equal (append x (append y z)) (append (append x y) z)) :rule-classes nil)) (defthm test2 (equal (append x (append y z)) (append (append x y) z)) :rule-classes nil))) General Forms: (with-prover-step-limit expr form) (with-prover-step-limit expr flg form) where form is an arbitrary form to evaluate, and expr evaluates to one of the following: nil; a natural number not exceeding the value of *default-step-limit*; or the special value, :START. The optional extra argument in the second position, flg, must be Boolean if supplied. If the value of expr is a natural number less than the value of *default-step-limit*, then that value is the maximum number of prover steps permitted during evaluation of form before an error occurs. If however the value of expr is nil or is the value of *default-step-limit*, then no limit is placed on the number of prover steps during processing of form. Otherwise, the value of expr should be the keyword :START, which is intended for use by the ACL2 implementation and may have semantics that change with new ACL2 versions. Finally we describe the optional extra Boolean argument, flg. If flg is nil or omitted, then a running count of prover steps is maintained after form is evaluated; otherwise, that count is not affected by evaluation of form. To see how this works when flg is nil or omitted, consider an event of shape (progn form1 form2), where we are tracking prover steps (say, because of a superior call of with-prover-step-limit). If n is the number of prover steps available when the progn form is entered, and if s prover steps are executed while evaluating form1, then n-s steps are available for evaluation of form2 provided s does not exceed n; otherwise, an error occurs. In particular, this is the case if form1 is an event (with-prover-step-limit k form1'). However, if form1 is an event (with-prover-step-limit k t form1'), then because of the extra argument of t, no steps are "charged" to form; that is, all n steps, rather than n-s steps, are available for evaluation of form2.  File: acl2-doc-emacs.info, Node: WITH-PROVER-TIME-LIMIT, Next: WOF, Prev: WITH-PROVER-STEP-LIMIT, Up: OTHER WITH-PROVER-TIME-LIMIT limit the time for proofs Examples: ; Limit (mini-proveall) to about 1/4 second: (with-prover-time-limit 1/4 (mini-proveall)) ; Limit (mini-proveall) to about 1/4 second, even if surrounding call of ; with-prover-time-limit provides for a more restrictive bound: (with-prover-time-limit '(1/4) (mini-proveall)) ; Limit the indicated theorem to about 1/50 second, and if the proof does not ; complete or it fails, then put down a label instead. (mv-let (erp val state) (with-prover-time-limit 1/50 (thm (equal (append (append x x) x) (append x x x)))) (if erp (deflabel foo :doc "Attempt failed.") (value (list :succeeded-with val)))) General Form: (with-prover-time-limit time form) where time evaluates to a positive rational number or to a list containing such, and form is arbitrary. Logically, (with-prover-time-limit time form) is equivalent to form. However, if the time for evaluation of form exceeds the value specified by time, and if ACL2 notices this fact during a proof, then that proof will abort, for example like this: ACL2 Error in ( DEFTHM PERM-REFLEXIVE ...): Out of time in the rewriter. If there is already a surrounding call of with-prover-time-limit that has set up an expiration time, the inner with-prover-time-limit call is not allowed to push that time further into the future unless the inner time is specified as a list containing a rational, rather than as a rational. Note that by default, the time used is runtime (cpu time); to switch to realtime (elapsed time), see *note GET-INTERNAL-TIME::. For a related utility based on prover steps instead of time, see *note WITH-PROVER-STEP-LIMIT::; also see *note SET-PROVER-STEP-LIMIT::. Those utilities have the advantage of having platform-independent behavior, unlike time limits, which of course are generally less restrictive for faster processors. But note that the prover steps counted need not correspond closely to prover time. Although with-prover-time-limit behaves like an ACL2 function in the sense that it evaluates both its arguments, it is however actually a macro that behaves as follows. (1) The value of its first (time limit) argument affects the evaluation of its second argument (by causing an error during that evaluation if the time for completion is insufficient). (2) The second argument can return multiple values (see *note MV::), which are then returned by the call of with-prover-time-limit. (3) Thus, there is not a fixed number of values returned by with-prover-time-limit. If you find that the time limit appears to be implemented too loosely, it may be because the prover only checks the time elapsed at certain points during the proof process, for example at entry to the rewriter. For example, if you write your own clause-processor that does an expensive computation, the time is unlikely to be checked during its execution. If however you find the time limit seems to be ignored even during ordinary prover operation, you are encouraged to email an example to the ACL2 implementors with instructions on how to observe the undesirable behavior. This information can perhaps be used to improve ACL2 by the insertion of more checks for expiration of the time limit. The rest of this documentation topic explains the rather subtle logical story, and is not necessary for understanding how to use with-prover-time-limit. The ACL2 state object logically contains a field called the acl2-oracle, which is an arbitrary true list of objects. This field can be read by a function called read-acl2-oracle, which however is untouchable (see *note PUSH-UNTOUCHABLE::), meaning that it is cannot be called by ACL2 users. The acl2-oracle field is thus "secret". Our claim is that any ACL2 session makes sense for *some* value of acl2-oracle in the initial state for that session. Logically, with-prover-time-limit is a no-op, just returning its second value. But under the hood, it provides a "hint" for the acl2-oracle, so that (logically speaking) when its first element (car) is consulted by ACL2's prover to see if the time limit has expired, it gets the "right" answer (specifically, either nil if all is well or else a message to print if the time limit has expired). Logically, the acl2-oracle is then cdr'ed -- that is, its first element is popped off -- so that future results from read-acl2-oracle are independent of the one just obtained.  File: acl2-doc-emacs.info, Node: WOF, Prev: WITH-PROVER-TIME-LIMIT, Up: OTHER WOF direct standard output and proofs output to a file Example Form: (wof "tmp" (pso)) ; same as (psof "tmp") General Form: (wof filename form) where filename is a writable filename and form is any form that evaluates to an error triple (see *note PROGRAMMING-WITH-STATE::), that is, a multiple value of the form (mv erp val state). All output to channels standard-co and proofs-co will be directed to the indicated file. It is acceptable to replace filename with (quote filename). Note that so-called comment-window output (see *note CW:: and see *note OBSERVATION-CW::) is not redirected by wof to a file, nor is printing from a wormhole.  File: acl2-doc-emacs.info, Node: PARALLELISM, Next: PROGRAMMING, Prev: OTHER, Up: Top PARALLELISM experimental extension for parallel execution and proofs This documentation topic relates to an experimental extension of ACL2, ACL2(p), created initially by David L. Rager. See *note COMPILING-ACL2P:: for how to build an executable image that supports parallel execution. Also see community books directory books/parallel/ for examples. For a completely different sort of parallelism, at the system level, see *note PROVISIONAL-CERTIFICATION::. * Menu: * COMPILING-ACL2P:: compiling ACL2(p) * PARALLEL-PROGRAMMING:: parallel programming in ACL2(p) * PARALLEL-PROOF:: parallel proof in ACL2(p) * UNSUPPORTED-PARALLELISM-FEATURES:: ACL2 features not supported in ACL2(p) * WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION:: for ACL2(p): using waterfall parallelism during book certification IMPORTANT NOTE. We hope and expect that every evaluation result is correctly computed by ACL2(p), and that every formula proved using ACL2(p) is a theorem of the ACL2 logic (and in fact is provable using ACL2). However, we do not guarantee these properties. Since ACL2(p) is intended to be an aid in efficient evaluation and proof development, we focus less on ironclad soundness and more on providing an efficient and working implementation. Nevertheless, if you encounter a case where ACL2(p) computes an incorrect result, or produces a proof where ACL2 fails to do so (and this failure is not discussed in unsupported-waterfall-parallelism-features), please notify the implementors. The ACL2 source code provides experimental parallel execution and proof capabilities. For example, one of ACL2's strengths lies in its ability to simulate industrial models efficiently, and it can also decrease the time required for proofs about such models both by making use of parallel evaluation and by dispatching proof subgoals in parallel. While we aim to support Clozure Common Lisp (CCL), Steel Bank Common Lisp (SBCL), and Lispworks, SBCL and Lispworks both currently sometimes experience problems when evaluating the ACL2 proof process (the "waterfall") in parallel. Therefore, CCL is the recommend Lisp for anyone that wants to use parallelism and isn't working on fixing those problems.  File: acl2-doc-emacs.info, Node: COMPILING-ACL2P, Next: PARALLEL-PROGRAMMING, Prev: PARALLELISM, Up: PARALLELISM COMPILING-ACL2P compiling ACL2(p) This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. See *note PARALLELISM-TUTORIAL:: for an introduction to parallel programming in ACL2. You can build an experimental version of ACL2 that supports parallel execution in the following host Common Lisp implementations: * CCL (OpenMCL) * Lispworks 6.0 * SBCL with threads (feature :sb-thread) The command below will compile ACL2 to support parallel execution, including parallel execution during proofs. Any non-empty string may be used in place of t, and the value of LISP (shown here as ccl) is any Lisp executable on which one can build ACL2(p) (see *note PARALLELISM::). make ACL2_PAR=t LISP=ccl So for example, to make an executable image and also documentation (which will appear in subdirectories doc/EMACS and doc/HTML), using the Lisp executable ccl: make large DOC ACL2_PAR=t LISP=ccl acl2-sources/doc/EMACS/acl2-doc-emacs.info-80000664002132200015000000110752312222333541017652 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: PARALLEL-PROGRAMMING, Next: PARALLEL-PROOF, Prev: COMPILING-ACL2P, Up: PARALLELISM PARALLEL-PROGRAMMING parallel programming in ACL2(p) Here we document support for parallel programming in ACL2(p), an experimental extension of ACL2; also see *note PARALLELISM::. * Menu: * DEFLOCK:: define a wrapper macro that provides mutual exclusion in ACL2(p) * EARLY-TERMINATION:: early termination for pand and por. * ERROR-TRIPLES-AND-PARALLELISM:: how to avoid error triples in ACL2(p) * GRANULARITY:: limit the amount of parallelism * PAND:: parallel, Boolean version of and * PARALLEL-EXECUTION:: for ACL2(p): configure parallel execution * PARALLELISM-AT-THE-TOP-LEVEL:: parallel execution in the ACL2 top-level loop * PARALLELISM-PERFORMANCE:: performance issues for parallel execution * PARALLELISM-TUTORIAL:: a tutorial on how to use the parallelism library. * PARGS:: parallel evaluation of arguments in a function call * PLET:: parallel version of let * POR:: parallel, Boolean version of or * SPEC-MV-LET:: modification of mv-let supporting speculative and parallel execution * WITH-OUTPUT-LOCK:: provides a mutual-exclusion mechanism for performing output in parallel Related topics other than immediate subtopics: * SET-PARALLEL-EXECUTION:: for ACL2(p): enabling parallel execution for four parallelism primitives * SET-TOTAL-PARALLELISM-WORK-LIMIT:: for ACL2(p): set thread limit for parallelism primitives One of ACL2's strengths lies in its ability to execute industrial models efficiently. The ACL2 source code provides an experimental parallel execution capability that can increase the speed of explicit evaluation, including simulator runs using such models, and it can also decrease the time required for proofs that make heavy use of the evaluation of ground terms. The parallelism primitives are plet, pargs, pand, por, and spec-mv-let. Pand and por terminate early when an argument is found to evaluate to nil or non-nil, respectively, thus potentially improving on the efficiency of lazy evaluation. Spec-mv-let is a modification of mv-let that supports speculative and parallel execution. Of the above five parallelism primitives, all but spec-mv-let allow for limiting parallel execution (spawning of so-called "threads") depending on resource availability. Specifically, the primitives allow specification of a size condition to control the granularity under which threads are allowed to spawn. You can use such granularity declarations in recursively-defined functions to implement data-dependent parallelism in their execution. We recommend that in order to learn to use the parallelism primitives, you begin by reading examples: see *note PARALLELISM-TUTORIAL::. That section will direct you to further documentation topics. In addition to providing parallel programming primitives, ACL2(p) also provides the ability to execute the main ACL2 proof process in parallel. See *note SET-WATERFALL-PARALLELISM:: for further details.  File: acl2-doc-emacs.info, Node: DEFLOCK, Next: EARLY-TERMINATION, Prev: PARALLEL-PROGRAMMING, Up: PARALLEL-PROGRAMMING DEFLOCK define a wrapper macro that provides mutual exclusion in ACL2(p) This documentation topic relates to the experimental extension of ACL2 supporting parallel evaluation and proof; see *note PARALLELISM::. Example Form: (deflock *my-lock*) General Form: (deflock *symbol*) where *symbol* is a symbol whose first and last characters are both the character #\*. A call of this macro generates a definition of another macro, named with-, where is the given symbol with the leading and trailing * characters removed. This newly defined macro will guarantee mutually exclusive execution when called in the body of the raw Lisp definition of a function, as is typically the case for guard-verified functions, for :program mode functions, and for calls of macro top-level. (See *note GUARD-EVALUATION-TABLE:: for details of how raw Lisp code might not be invoked when guard-checking (see *note SET-GUARD-CHECKING::) has value :none or :all.) To see how mutual exclusion is guaranteed, consider the raw Lisp code generated for the macro, with-, that is introduced by a call of deflock. This code uses a lock (with the given *symbol* as its name), which guarantees that for any two forms that are each in the scope of a call of with-, the forms do not execute concurrently. Note that a call of deflock expands into the application of progn to two events, as illustrated below. ACL2 !>:trans1 (deflock *my-cw-lock*) (PROGN (TABLE LOCK-TABLE '*MY-CW-LOCK* T) (DEFMACRO WITH-MY-CW-LOCK (&REST ARGS) (LIST* 'WITH-LOCK '*MY-CW-LOCK* ARGS))) ACL2 !> Thus, deflock forms are legal embedded event forms (see *note EMBEDDED-EVENT-FORM::) for books as well as encapsulate and progn events. The following log shows a lock in action. Recall that locks work as expected in guard-verified and :program mode functions; they do not, however, work in :logic mode functions that have not been guard-verified, as illustrated below. ACL2 !>(deflock *my-cw-lock*) [[.. output omitted ..]] WITH-MY-CW-LOCK ACL2 !>(defun foo (n) (declare (xargs :guard (natp n) :verify-guards nil)) (plet ((x1 (with-my-cw-lock (cw "~x0" (make-list n)))) (x2 (with-my-cw-lock (cw "~x0" (make-list n))))) (and (null x1) (null x2)))) [[.. output omitted ..]] FOO ACL2 !>(foo 20) (NIL NIL NIL NIL( NIL NIL NIL NIL NIL NILNIL NIL NILNIL NIL NILNIL NIL NILNIL NIL NIL NILNIL NIL NIL NIL NILNIL NIL NILNIL ) NIL NIL NIL NIL NIL NIL NIL NIL) T ACL2 !>(verify-guards foo) [[.. output omitted ..]] FOO ACL2 !>(foo 20) (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) T ACL2 !>  File: acl2-doc-emacs.info, Node: EARLY-TERMINATION, Next: ERROR-TRIPLES-AND-PARALLELISM, Prev: DEFLOCK, Up: PARALLEL-PROGRAMMING EARLY-TERMINATION early termination for pand and por. This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. The evaluation of (and expr1 expr2) returns nil if expr1 evaluates to nil, avoiding the evaluation of expr2. More generally, the evaluation of (and expr1 expr2 ... exprk) terminates with a return value of nil as soon as any expri evaluates to nil -- no exprj is evaluated in this case for j > i. This so-called "lazy evaluation" of and terms can thus save some computation; roughly speaking, the smaller the i, the more computation is saved. If the above call of and is replaced by its parallel version, pand, then there can be even more opportunity for skipping work. The arguments to pand can be evaluated in parallel, in which case the first such evaluation that returns with a value of nil, if any, causes the remaining such evaluations to abort. Consider the following functions that compute whether a tree is valid (see *note GRANULARITY:: for a discussion of the granularity form). (defun valid-tip (x) (declare (xargs :guard t)) (or (eq x 'A) (eq x 'T) (eq x 'C) (eq x 'G))) (defun pvalid-tree (x depth) (declare (xargs :guard (natp depth))) (if (atom x) (valid-tip x) (pand (declare (granularity (< depth 10))) (pvalid-tree (car x) (1+ depth)) (pvalid-tree (cdr x) (1+ depth))))) We would like to stop execution as soon as any tip is found to be invalid. So, when computing the conjunction of terms by using pand, once one of those terms evaluates to nil, the computations for the other terms are aborted and the pand call returns nil. By using pand, we can in principle attain a speedup factor greater than the number of available cores. The concept of early termination also applies to por, except that early termination occurs when an argument evaluates to non-nil.  File: acl2-doc-emacs.info, Node: ERROR-TRIPLES-AND-PARALLELISM, Next: GRANULARITY, Prev: EARLY-TERMINATION, Up: PARALLEL-PROGRAMMING ERROR-TRIPLES-AND-PARALLELISM how to avoid error triples in ACL2(p) This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. ACL2 supports the use of error triples in many features; e.g., computed-hints. (For background on error triples, see *note PROGRAMMING-WITH-STATE::.) However, ACL2(p) does not support the use of error triples in some of these features (e.g., computed-hints) while waterfall-parallelism is enabled. You may see an error message like the following when running ACL2(p) with waterfall-parallelism enabled: ACL2 Error in ( THM ...): Since we are translating a form in ACL2(p) intended to be executed with waterfall parallelism enabled, the form (MY-STATE-MODIFYING-COMPUTED-HINT ID STATE) was expected to represent an ordinary value, not an error triple (mv erp val state), as would be acceptable in a serial execution of ACL2. Therefore, the form returning a tuple of the form (* * STATE) is an error. See :DOC unsupported- waterfall-parallelism-features and :DOC error-triples-and-parallelism for further explanation. In this particular example, the cause of the error was trying to use a computed hint that returned state, which is not allowed when executing the waterfall in parallel (see *note UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES:: for other related information). Often, the only reason users need to return state is so they can perform some output during the proof process. In this case, we suggest using one of the state-free output functions, like cw or observation-cw. If the user is concerned about the interleaving of their output with other output, these calls can be surrounded with the macro with-output-lock. Another frequent reason users return state is so they can cause a soft error and halt the proof process. In this case, we suggest instead calling er with the hard or hard? severity. By using these mechanisms, the user avoids modifying state, a requirement for much of the code written in ACL2(p). You may encounter other similar error messages when using computed-hints, custom-keyword-hints, or override-hints. Chances are that you are somehow returning an error triple when an ordinary value is needed. If this turns out not to be the case, please let the ACL2 implementors know.  File: acl2-doc-emacs.info, Node: GRANULARITY, Next: PAND, Prev: ERROR-TRIPLES-AND-PARALLELISM, Up: PARALLEL-PROGRAMMING GRANULARITY limit the amount of parallelism This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Some function calls are on arguments whose evaluation time is long enough to warrant parallel execution, while others are not. A granularity form can be used to make appropriate restrictions on the use of parallelism. For example, consider the Fibonacci function. Experiments have suggested that execution time can be reduced if whenever the argument is less than 27, a serial version of the Fibonacci function is called. One way to utilize this information is to write two definitions of the Fibonacci function, one serial and one parallel. (defun fib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (binary-+ (fib (- x 1)) (fib (- x 2)))))) (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) ((< x 27) (binary-+ (fib (- x 1)) (fib (- x 2)))) (t (pargs (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) We realize quickly that writing both of these function definitions is cumbersome and redundant. This problem can be avoided by using a granularity declaration with a parallelism primitive. This form ensures that a call is parallelized only if resources are available and the granularity form evaluates to a non-nil value at the time of the call. Below is a definition of the Fibonacci function using a granularity form. (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (pargs (declare (granularity (>= x 27))) (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) A granularity form can reference an extra formal parameter that describes the call depth of the function the user is parallelizing. Consider for example the following parallel mergesort function, based on Davis's Ordered Sets library. It splits the data into symmetric chunks for computation, so we increment the depth argument during the recursive call on both the car and cdr. (include-book "finite-set-theory/osets/sets" :dir :system) (defun sets::pmergesort-exec (x depth) (declare (xargs :mode :program)) (cond ((endp x) nil) ((endp (cdr x)) (sets::insert (car x) nil)) (t (mv-let (part1 part2) (sets::split-list x nil nil) (pargs (declare (granularity (< depth 2))) (sets::union (sets::pmergesort-exec part1 (1+ depth)) (sets::pmergesort-exec part2 (1+ depth)))))))) A less intrusive method (i.e., not requiring an extra formal parameter like the depth argument just above), which however can be less efficient, involves analyzing the data itself for structural properties. For example: (defun some-depth-exceeds (x n) (declare (xargs :guard (natp n))) (if (atom x) nil (if (zp n) t (or (some-depth-exceeds (car x) (1- n)) (some-depth-exceeds (cdr x) (1- n)))))) (defun valid-tip (x) (declare (xargs :guard t)) (or (eq x 'A) (eq x 'T) (eq x 'C) (eq x 'G))) (defun pvalid-tree (x) (declare (xargs :guard t)) (if (atom x) (valid-tip x) (pand (declare (granularity (some-depth-exceeds x 3))) (pvalid-tree (car x)) (pvalid-tree (cdr x))))) If you experiment with calls of pvalid-tree, you are likely to find that the "speedup" it provides over a corresponding serial version is, in fact, a slowdown! The problem is likely that some-depth-exceeds is an expensive function to run repeatedly. Instead of the approach above, it is often handy to add an extra formal parameter in order to allow for more efficient granularity forms, as we have done above in the definition of SETS::pmergesort-exec.  File: acl2-doc-emacs.info, Node: PAND, Next: PARALLEL-EXECUTION, Prev: GRANULARITY, Up: PARALLEL-PROGRAMMING PAND parallel, Boolean version of and This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Example Forms: (pand (subsetp-equal x y) (subsetp-equal y x)) (pand (declare (granularity (and (> (length x) 500) (> (length y) 500)))) (subsetp-equal x y) (subsetp-equal y x)) General Form: (pand (declare (granularity expr)) ; optional granularity declaration arg1 ... argN) where N >= 0 and each argi and expr are arbitrary terms. Pand evaluates its arguments in parallel. It returns a Boolean result: nil if any argument evaluates to nil, else t. Note that pand always returns a Boolean result, even though and can return a non-nil value other than t, namely the value of its last argument. (A moment's reflection will make it clear that in order for por to parallelize efficiently, it needs to return a Boolean value; so pand returns a Boolean value for consistency with por.) Another difference between pand and and is that for a call of pand, even if an argument evaluates to nil, a subsequent argument may be evaluated. Consider the following example (where cw prints a string; see *note CW::). (defun bar () (pand (equal (make-list 100000) nil) ; evaluates to nil (cw "hello world~%"))) When (bar) is evaluated, the above arguments of pand can execute in parallel, causing "hello world" to be printed to the terminal. If we had used and rather than pand, then since (equal (make-list 100000) nil) evaluates to nil, the above call of cw would be avoided and no such printing would take place. Even with pand, such printing _might_ not take place, depending on resources, timing of thread creation, and whether or not parallel execution is enabled (see *note SET-PARALLEL-EXECUTION::). Note that unlike the case for and, the definition of pand does not provide (consp x) as a guard to (car x) in the call of pand below: (defun h (x) (declare (xargs :guard t)) (pand (consp x) (equal (car x) 'foo))) As a result, guard verification will fail for the above definition. If pand were replaced by and, then guard verification would succeed. See *note PARALLELISM-TUTORIAL:: for another example. Also see *note PARALLELISM-AT-THE-TOP-LEVEL:: for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop. Finally see *note EARLY-TERMINATION:: to read how pand can offer more efficiency than and by avoiding evaluation of some of its arguments.  File: acl2-doc-emacs.info, Node: PARALLEL-EXECUTION, Next: PARALLELISM-AT-THE-TOP-LEVEL, Prev: PAND, Up: PARALLEL-PROGRAMMING PARALLEL-EXECUTION for ACL2(p): configure parallel execution See *note SET-PARALLEL-EXECUTION:: for how to configure parallel execution for calls of plet, pargs, pand, por (but not spec-mv-let).  File: acl2-doc-emacs.info, Node: PARALLELISM-AT-THE-TOP-LEVEL, Next: PARALLELISM-PERFORMANCE, Prev: PARALLEL-EXECUTION, Up: PARALLEL-PROGRAMMING PARALLELISM-AT-THE-TOP-LEVEL parallel execution in the ACL2 top-level loop This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Calls of parallelism primitives made explicitly in the ACL2 top-level loop, as opposed to inside function bodies, will never cause parallel execution. Such calls will either execute with serial execution or will cause an error; see *note SET-PARALLEL-EXECUTION::. For a way around this restriction, see *note TOP-LEVEL::. Consider for example the following call of pargs in the ACL2 top-level loop. Instead of executing pargs, ACL2 macroexpands away this call, leaving us with serial execution of the arguments to the cons call, or else causes an error (see *note SET-PARALLEL-EXECUTION::). If there is no error, then (pargs (cons (expensive-fn-1 4) (expensive-fn-2 5))) expands into: (cons (expensive-fn-1 4) (expensive-fn-2 5)) One trivial way to enable parallel execution of a form is to surround it with a call to macro top-level. Consider the following example. (top-level (pargs (cons (expensive-fn-1 4) (expensive-fn-2 5)))) Then in an executable image that supports parallel execution -- see *note COMPILING-ACL2P:: for instructions on how to build such an executable -- (expensive-fn-1 4) and (expensive-fn-2 5) can evaluate in parallel. A second way to enable parallel execution of a form is to place it inside a function body. For example, consider the following definition. (defun foo (x y) (pargs (cons (expensive-fn-1 x) (expensive-fn-2 y)))) Then in an executable image that supports parallel execution, submission of the form (foo 4 5) can cause parallel execution of (expensive-fn-1 4) and (expensive-fn-2 5). Note that guards need not be verified in order to obtain parallel execution. The only restrictions on parallel execution are to use an executable supporting it, to avoid calling parallelism primitives directly in the top-level loop, to have sufficient resources (especially, threads) available, and to avoid explicitly disabling parallel execution (see *note SET-PARALLEL-EXECUTION::).  File: acl2-doc-emacs.info, Node: PARALLELISM-PERFORMANCE, Next: PARALLELISM-TUTORIAL, Prev: PARALLELISM-AT-THE-TOP-LEVEL, Up: PARALLEL-PROGRAMMING PARALLELISM-PERFORMANCE performance issues for parallel execution This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. See *note GRANULARITY:: for an important construct that limits the spawning of parallel computations, which can be important when a computation is too short-lived to warrant a separate thread. There are times in which parallelism provides no speedup because of garbage collection in the underlying Lisp implementation. The following example illustrates this phenomenon. If you change the granularity declaration so that the depth bound is 3, 4, or larger instead of 2, you may still find no speedup. In all cases you may find that parallelism results in a significantly greater time spent in garbage collection. (include-book "finite-set-theory/osets/sets" :dir :system) (defun sets::pmergesort-exec (x depth) (declare (xargs :mode :program)) (cond ((endp x) nil) ((endp (cdr x)) (sets::insert (car x) nil)) (t (mv-let (part1 part2) (sets::split-list x nil nil) (pargs (declare (granularity (< depth 2))) (sets::union (sets::pmergesort-exec part1 (1+ depth)) (sets::pmergesort-exec part2 (1+ depth)))))))) (defconst *x* (reverse (fromto 1 400000))) (time$ (length (sets::pmergesort-exec *x* 0))) (set-parallel-execution nil) (time$ (length (sets::pmergesort-exec *x* 0)))  File: acl2-doc-emacs.info, Node: PARALLELISM-TUTORIAL, Next: PARGS, Prev: PARALLELISM-PERFORMANCE, Up: PARALLEL-PROGRAMMING PARALLELISM-TUTORIAL a tutorial on how to use the parallelism library. This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. In this topic we introduce the ACL2 parallelism primitives using the example of a doubly-recursive Fibonacci function, whose basic definition is as follows. See *note PARALLELISM:: for a very high-level summary of the parallelism capability described here, and see *note COMPILING-ACL2P:: for how to build an executable image that supports parallel execution. Here, we assume that such an executable is being used. Serial Fibonacci (defun fib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (+ (fib (- x 1)) (fib (- x 2)))))) Introducing Pargs A simple way to introduce parallelism into this function definition is to wrap the addition expression with a call of pargs, and the arguments to the addition will be computed in parallel whenever resources are available. As a result, we end up with a very similar and thus intuitive function definition. Note that we replaced + by binary-+, since pargs expects a function call, not a macro call. (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (pargs (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) Introducing the Granularity Problem After you submit the above two versions of the Fibonacci function, test them with the following forms. (time$ (fib 10)) (time$ (pfib 10)) Now increase the argument by increments of 5 to 10 until you find your curiosity satisfied or your patience wearing thin. You can interrupt evaluation if necessary and return to the ACL2 loop. You will immediately notice that you have not increased execution speed, at least not by much, by introducing parallelism. First, consider the computation of (pfib 4). Assuming resources are available, (pfib 4) will create a thread for computing (pfib 3) and another thread for (pfib 2). It is easy to imagine that setting up each thread takes much longer than the entire computation of (fib 4). Second, we must realize that if we have two threads available for computing (fib 10), then the evaluation of (fib 8) will probably complete before the evaluation of (fib 9). Once (fib 8) finishes, parallelism resources will become available for the next recursive call made on behalf of (fib 9). If for example that call is (fib 3), we will waste a lot of cycles just handing work to the thread that does this relatively small computation. We need a way to ensure that parallelism resources are only used on problems of a "large" size. Ensuring that only "large" problems are spawned is called the "granularity problem." In summary: We want to tell ACL2 that it can evaluate the arguments of pargs in parallel only when the parameter of pfib is greater than some threshold. Our tests using CCL have suggested that 27 is a reasonable threshold. Explicit Programming for the Granularity Problem One way to avoid the granularity problem is to duplicate code as follows. (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (if (> x 27) ; the granularity test (pargs (binary-+ (pfib (- x 1)) (pfib (- x 2)))) (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) Duplicating code is fundamentally a bad design principle, because it can double the work for future maintenance. A "granularity form" is an expression (declare (granularity )) that can allow threads to be spawned (without duplicating code) whenever the evaluation of results in a non-nil value. It may be placed inside any call of a parallelism primitive, in a position documentated separately for each primitive. Here is a definition of pfib using this feature for a call of the parallelism primitive pargs. (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (pargs (declare (granularity (> x 27))) (binary-+ (pfib (- x 1)) (pfib (- x 2))))))) Test each version as follows (or substitute your own natural number for 33). (time$ (fib 33)) (time$ (pfib 33)) Another Granularity Issue Related to Thread Limitations Our implementation of parallelism primitives has the property that once a thread is assigned a computation, that assignment stays in effect until the computation is complete. In particular, if a thread encounters a parallelism primitive that spawns child threads, the parent thread stays assigned, waiting until the child computations complete before it can continue its own computation. In the meantime, the parent thread reduces the number of additional threads that Lisp can provide by 1, rather than being reassigned to do other work. How can this lack of reassignment affect the user? Consider, for example, the application of a recursive function to a long list. Imagine that this function is written so that the body contains a recursive call, for example as (pargs (process (car x)) (recur (cdr x))). Each such pargs call that spawns child work must wait for its children, one of which is the work of evaluating (recur (cdr x)), to complete. There is an ACL2 limit on how much pending work can be in the system, limiting the number of pargs calls that can result in parallel execution. For example, if the ACL2 limit is k and each call of pargs actually spawns threads for evaluating its arguments, then after k cdrs there will be no further parallel execution. Possible solutions may include reworking of algorithms (for example to be tree-based rather than list-based) or using appropriate granularity forms. We hope that future implementations will allow thread "re-deployment" in order to mitigate this problem. This problem applies to plet, pargs, pand, por, and spec-mv-let. Introducing Plet We can use a let binding to compute the recursive calls of fib and then add the bound variables together, as follows. (defun fib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (let ((a (fib (- x 1))) (b (fib (- x 2)))) (+ a b))))) By using the parallelism primitive plet, we can introduce parallelism much as we did using pargs, with an optional granularity form, as follows. (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((or (zp x) (<= x 0)) 0) ((= x 1) 1) (t (plet (declare (granularity (> x 27))) ((a (pfib (- x 1))) (b (pfib (- x 2)))) (+ a b))))) Notice that this time, we were able to use + rather than being forced to use binary-+. Unlike pargs, which expects a function call (not a macro call), plet allows macros at the top level of its body. Introducing Pand (By Way of a Tree Validation Example) Consider "genetic trees" that contains leaves of DNA elements, in the sense that each tip is one of the symbols A, G, C, or T. First we define the function valid-tip which recognizes whether a tip contains one of these symbols. (defun valid-tip (tip) (declare (xargs :guard t)) (or (eq tip 'A) (eq tip 'G) (eq tip 'C) (eq tip 'T))) Now we define a function that traverses the tree, checking that every tip is valid. (defun valid-tree-serial (tree) (declare (xargs :guard t)) (if (atom tree) (valid-tip tree) (and (valid-tree-serial (car tree)) (valid-tree-serial (cdr tree))))) We also define a parallel version. (defun valid-tree-parallel (tree) (declare (xargs :guard t)) (if (atom tree) (valid-tip tree) (pand (valid-tree-parallel (car tree)) (valid-tree-parallel (cdr tree))))) Before we can time the functions, we need to create test trees. We have found that test trees need to be approximately 1 gigabyte before we clearly see speedup, and we make them asymmetric to demonstrate the ability of our implementation to adapt to asymmetric data. We can create the trees with the execution of the following forms. (defun make-valid-binary-tree (x) (declare (xargs :mode :program)) (if (< x 0) (cons (cons 'C 'G) (cons 'A 'T)) (cons (make-valid-binary-tree (- x 2)) ; 2 to make asymmetric (make-valid-binary-tree (- x 1))))) (defconst *valid-tree* (make-valid-binary-tree 30)) ; takes awhile (defconst *invalid-tree* (cons *valid-tree* nil)) ; nil is invalid tip We can time our functions with the forms: (time$ (valid-tree-serial *valid-tree*)) (time$ (valid-tree-parallel *valid-tree*)) Unfortunately, the serial version runs faster than the parallelism version; however, there is more to this story. Demonstrating Early Termination with an Invalid Tree Now observe this magic: (time$ (valid-tree-serial *invalid-tree*)) (time$ (valid-tree-parallel *invalid-tree*)) The serial version took awhile, while the parallel version finished almost immediately. The test for validation was split into testing the car and the cdr of the *invalid-tree* root, and since the cdr was equal to nil, its test returned immediately. This immediate return quickly interrupted the computation associated with the car, and returned the result. Granularity with Pand We can also define a parallel version with a granularity form: (defun valid-tree-parallel (tree depth) (declare (xargs :guard (natp depth))) (if (atom tree) (valid-tip tree) (pand (declare (granularity (< depth 5))) (valid-tree-parallel (car tree) (1+ depth)) (valid-tree-parallel (cdr tree) (1+ depth))))) We can test this form by executing our previous forms. You will probably find some speedup on a machine with several cores available, but more speedup can likely be obtained with an expensive test on the leaves in place of valid-tip. (time$ (valid-tree-serial *valid-tree*)) (time$ (valid-tree-parallel *valid-tree* 0)) Introducing Por Por can be used in the same way as pand, but with early termination occurring when an argument evaluates to a non-nil value, in which case the value returned is t. Finally, por also supports the use of a granularity form.  File: acl2-doc-emacs.info, Node: PARGS, Next: PLET, Prev: PARALLELISM-TUTORIAL, Up: PARALLEL-PROGRAMMING PARGS parallel evaluation of arguments in a function call This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Example Forms: (pargs (binary-+ (fibonacci (- x 1)) (fibonacci (- x 2)))) (pargs (declare (granularity (> x 35))) (binary-+ (fibonacci (- x 1)) (fibonacci (- x 2)))) General Form: (pargs (declare (granularity expr)) ; optional granularity declaration (f arg1 ... argN)) where N >= 0 and each argi and expr are arbitrary terms. Logically, pargs is just the identity macro, in the sense that the above forms can logically be replaced by (f arg1 ... argN). However, this pargs form may parallelize the evaluation of arguments arg1 through argN before applying function f to those results. If the above granularity declaration is present, then its expression (expr above) is first evaluated, and if the result is nil, then such parallelism is avoided. Even if parallelism is not thus avoided, parallelism may be limited by available resources. Since macros can change expressions in unexpected ways, it is illegal to call pargs on a form that is a macro call. To parallelize computation of arguments to a macro, see *note PLET::. See *note PARALLELISM-AT-THE-TOP-LEVEL:: for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop.  File: acl2-doc-emacs.info, Node: PLET, Next: POR, Prev: PARGS, Up: PARALLEL-PROGRAMMING PLET parallel version of let This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Example Forms: (plet ((a (fibonacci (- x 1))) (b (fibonacci (- x 2)))) (+ a b)) (plet (declare (granularity (> x 35))) ((a (fibonacci (- x 1))) (b (fibonacci (- x 2)))) (+ a b)) General Form: (plet (declare (granularity expr)) ; optional granularity declaration ((var1 val1) ... (varN valN)) (declare ...) ... (declare ...) ; optional declarations body) The syntax of plet is identical to the syntax of let, except that plet permits an optional granularity declaration in the first argument position; see *note GRANULARITY::. In the logic a call of plet macroexpands to the corresponding call of let, where the granularity declaration (if any) is dropped. Plet cause the evaluation of each vali above to be done in parallel before processing the body. If the above granularity declaration is present, then its expression (expr above) is first evaluated, and if the result is nil, then such parallelism is avoided. Even if parallelism is not thus avoided, parallelism may be limited by available resources. See *note PARALLELISM-AT-THE-TOP-LEVEL:: for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop.  File: acl2-doc-emacs.info, Node: POR, Next: SPEC-MV-LET, Prev: PLET, Up: PARALLEL-PROGRAMMING POR parallel, Boolean version of or This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Example Forms: (por (subsetp-equal x y) (subsetp-equal y x)) (por (declare (granularity (and (> (length x) 500) (> (length y) 500)))) (subsetp-equal x y) (subsetp-equal y x)) General Form: (por (declare (granularity expr)) ; optional granularity declaration arg1 ... argN) where N >= 0 and each argi and expr are arbitrary terms. Por evaluates its arguments in parallel. It returns a Boolean result: t if any argument evaluates to non-nil, else nil. Note that while or returns the first non-nil value from evaluating its arguments left-to-right (if any such value is not nil) por always returns a Boolean result, in support of efficiency (see *note EARLY-TERMINATION::) in light of the nondeterministic order in which argument values are returned. Another difference between por and or is that for a call of por, even if the an argument's value is not nil, a subsequent argument may be evaluated. See *note PAND:: for a discussion of the analogous property of pand. In particular, guards generated from calls of por may not assume for an argument that the preceding arguments evaluated to nil. See *note PARALLELISM-TUTORIAL:: for another example. Also see *note PARALLELISM-AT-THE-TOP-LEVEL:: for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop. Finally see *note EARLY-TERMINATION:: to read how por can offer more efficiency than or by avoiding evaluation of some of its arguments.  File: acl2-doc-emacs.info, Node: SPEC-MV-LET, Next: WITH-OUTPUT-LOCK, Prev: POR, Up: PARALLEL-PROGRAMMING SPEC-MV-LET modification of mv-let supporting speculative and parallel execution This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Example Form: (defun pfib-with-step-count (x) (declare (xargs :mode :program)) (if (or (zp x) (< x 33)) (fib-with-step-count x) (spec-mv-let (a cnt1) (pfib-with-step-count (- x 1)) (mv-let (b cnt2) (pfib-with-step-count (- x 2)) (if t (mv (+ a b) (+ 1 cnt1 cnt2)) (mv "speculative result is always needed" -1)))))) General Form: (spec-mv-let (v1 ... vn) ; bind distinct variables ; evaluate speculatively; return n values (mv-let ; or, use mv?-let if k=1 below (w1 ... wk) ; bind distinct variables ; evaluate eagerly (if ; use results from if true ; may mention v1 ... vn ))) ; does not mention v1 ... vn Our design of spec-mv-let is guided by its use in ACL2 source code to parallelize part of ACL2's proof process, in the experimental parallel extension of ACL2. The user can think of spec-mv-let as a speculative version of mv-let. (In ordinary ACL2, the semantics agree with this description but without speculative or parallel execution.) Evaluation of the above general form proceeds as suggested by the comments. First, is executed speculatively. Control then passes immediately to the mv-let call, without waiting for the result of evaluating . The variables (w1 ... wk) are bound to the result of evaluating , and then is evaluated. If the value of is true, then the values of (v1 ... vn) are needed, and blocks until they are available. If the value of is false, then the values of (v1 ... vn) are not needed, and the evaluation of may be aborted. The calls to mv-let and to if displayed above in the General Form are an essential part of the design of spec-mv-let, and are thus required. The following definition of fib-with-step-count completes the example above: (defun fib-with-step-count (x) (declare (xargs :mode :program)) (cond ((<= x 0) (mv 0 1)) ((= x 1) (mv 1 1)) (t (mv-let (a cnt1) (fib-with-step-count (- x 1)) (mv-let (b cnt2) (fib-with-step-count (- x 2)) (mv (+ a b) (+ 1 cnt1 cnt2)))))))  File: acl2-doc-emacs.info, Node: WITH-OUTPUT-LOCK, Prev: SPEC-MV-LET, Up: PARALLEL-PROGRAMMING WITH-OUTPUT-LOCK provides a mutual-exclusion mechanism for performing output in parallel This documentation topic relates to an experimental extension of ACL2, ACL2(p), created initially by David L. Rager. See *note COMPILING-ACL2P:: for how to build an executable image that supports parallel execution. Also see community books directory books/parallel/ for examples. One may wish to perform output while executing code in parallel. If threads are allowed to print concurrently, the output will be interleaved and often unreadable. To avoid this, the user can surround forms that perform output with the with-output-lock macro. Take the following definition of pfib as an example. (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((mbe :logic (or (zp x) (<= x 0)) :exec (<= x 0)) 0) ((= x 1) 1) (t (plet (declare (granularity t)) ((a (prog2$ (cw "Computing pfib ~x0~%" (- x 1)) (pfib (- x 1)))) (b (prog2$ (cw "Computing pfib ~x0~%" (- x 2)) (pfib (- x 2))))) (+ a b))))) With parallel-execution enabled, a call of (pfib 5)results in non-deterministically interleaved output, for example as follows. ACL2 !>(pfib 5) CComputing pfib 4 omputing pfib 3 ComCpuotmipnugt ipnfgi bp fib3 2 Computing pCfiobm put2i ng pfib 1 Computing pfib Co1mp uting pfib 0 CCoommppuuttiinngg ppffiibb 12 ComCpuotmipnugt ipnfgi bp fib1 0 CoCmopmuptuitnign gp fpifbi b 1 0 5 ACL2 !> If the user instead surrounds the calls to cw with the macro with-output-lock, as in the following session, the output will no longer be interleaved. ACL2 !> (defun pfib (x) (declare (xargs :guard (natp x))) (cond ((mbe :logic (or (zp x) (<= x 0)) :exec (<= x 0)) 0) ((= x 1) 1) (t (plet (declare (granularity t)) ((a (prog2$ (with-output-lock (cw "Computing pfib ~x0~%" (- x 1))) (pfib (- x 1)))) (b (prog2$ (with-output-lock (cw "Computing pfib ~x0~%" (- x 2))) (pfib (- x 2))))) (+ a b))))) ACL2 !>(pfib 5) Computing pfib 4 Computing pfib 3 Computing pfib 3 Computing pfib 2 Computing pfib 2 Computing pfib 1 Computing pfib 2 Computing pfib 1 Computing pfib 1 Computing pfib 0 Computing pfib 1 Computing pfib 0 Computing pfib 1 Computing pfib 0 5 ACL2 !>  File: acl2-doc-emacs.info, Node: PARALLEL-PROOF, Next: UNSUPPORTED-PARALLELISM-FEATURES, Prev: PARALLEL-PROGRAMMING, Up: PARALLELISM PARALLEL-PROOF parallel proof in ACL2(p) Here we document support for parallel proof in ACL2(p), an experimental extension of ACL2; also see *note PARALLELISM::, and for parallel programming in particular, see *note PARALLEL-PROGRAMMING::. * Menu: * ACL2P-KEY-CHECKPOINTS:: key checkpoints in ACL2(p) * DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT:: for ACL2(p): returns the default value for global total-parallelism-work-limit * PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION:: consequences of how parallelized proofs of subgoals are pushed for induction * UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES:: proof features not supported with waterfall-parallelism enabled * WATERFALL-PARALLELISM:: for ACL2(p): configuring the parallel execution of the waterfall * WATERFALL-PRINTING:: for ACL2(p): configuring the printing within the parallelized waterfall Related topics other than immediate subtopics: * SET-TOTAL-PARALLELISM-WORK-LIMIT:: for ACL2(p): set thread limit for parallelism primitives * SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR:: for ACL2(p): control the action taken when the thread limit is exceeded * SET-WATERFALL-PARALLELISM:: for ACL2(p): configuring the parallel execution of the waterfall * SET-WATERFALL-PARALLELISM-HACKS-ENABLED:: for ACL2(p): enable waterfall-parallelism hacks * SET-WATERFALL-PARALLELISM-HACKS-ENABLED!:: for ACL2(p): enabling waterfall parallelism hacks * SET-WATERFALL-PRINTING:: for ACL2(p): configuring the printing that occurs within the parallelized waterfall  File: acl2-doc-emacs.info, Node: ACL2P-KEY-CHECKPOINTS, Next: DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT, Prev: PARALLEL-PROOF, Up: PARALLEL-PROOF ACL2P-KEY-CHECKPOINTS key checkpoints in ACL2(p) This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. For printing output, the parallel version of the waterfall follows the precedent of gag-mode. The idea behind gag mode is to print only the subgoals most relevant to debugging a failed proof attempt. These subgoals are called 'key checkpoints' (see *note SET-GAG-MODE:: for the definition of "key" and "checkpoint"), and we restrict the default output mode for the parallel version of the waterfall to printing checkpoints similar to these key checkpoints. * Menu: Related topics other than immediate subtopics: * SET-WATERFALL-PRINTING:: for ACL2(p): configuring the printing that occurs within the parallelized waterfall As of this writing, we are aware of exactly one discrepancy between gag mode's key checkpoints and the parallel version of the waterfall's checkpoints. This discrepancy occurs when using "by" hints (see *note HINTS::). As an example, take the following form, which attempts to prove a non-theorem: (thm (equal (append x y z) (append z (append y x))) :hints (("Subgoal *1/2'''" :by nil))) With waterfall parallelism enabled, Subgoal *1/2" will be printed as a key checkpoint. This is different from using gag-mode while running the serial version of the waterfall, which skips printing the subgoal as a checkpoint. For those familiar with the ACL2 waterfall, we note that that the parallel version of the waterfall prints key checkpoints that are unproved in the following sense: a subgoal is a key checkpoint if it leads, in the current call of the waterfall, to a goal that is pushed for induction.  File: acl2-doc-emacs.info, Node: DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT, Next: PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION, Prev: ACL2P-KEY-CHECKPOINTS, Up: PARALLEL-PROOF DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT for ACL2(p): returns the default value for global total-parallelism-work-limit See *note SET-TOTAL-PARALLELISM-WORK-LIMIT::.  File: acl2-doc-emacs.info, Node: PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION, Next: UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES, Prev: DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT, Up: PARALLEL-PROOF PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION consequences of how parallelized proofs of subgoals are pushed for induction This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. The following discussion, concerning the naming of subgoals pushed for proof by induction and the timeliness of aborting when two or more goals are pushed for proof by induction, only applies when waterfall parallelism is enabled (see *note SET-WATERFALL-PARALLELISM::). When two sibling subgoals (e.g. 4.5 and 4.6) both push goals to be proved by induction (e.g., 4.6 pushes *1 and 4.5 pushes *2), a name is assigned to the second pushed subgoal (e.g., *2) as if the first push hasn't happened (e.g., *2 is mistakenly called *1). In such a case, we say what the name _could_ be. The following non-theorem illustrates how this works. (set-waterfall-parallelism :full) (thm (equal (append (car (cons x x)) y z) (append x x y))) There is another consequence of the way the parallelized waterfall pushes subgoals for proof by induction. Without waterfall parallelism enabled, ACL2 sometimes decides to abort instead of pushing a goal for later proof by induction, preferring instead to induct on the original conjecture. But with waterfall parallelism enabled, the prover no longer necessarily immediately aborts to prove the original conjecture. Suppose for example that sibling subgoals, Subgoal 4.6 and Subgoal 4.5, each push a subgoal for induction. If the waterfall is performing the proof of each of these subgoals in parallel, the proof will no longer abort immediately after the second push occurs, that is at Subgoal 4.5. As a result, the prover will continue through Subgoal 4.4, Subgoal 4.3, and beyond. It is not until the results of combining the proof results of Subgoal 4.6 with the results from the remaining sibling subgoals (4.5, 4.4, and so on), that the proof attempt will abort and revert to prove the original conjecture by induction. This example illustrates behavior that is rather like the case that :otf-flg is t, in the sense that the abort does not happen immediately, but also rather like the case that :otf-flg is nil, in the sense that the abort does occur before getting to Subgoal 3.  File: acl2-doc-emacs.info, Node: UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES, Next: WATERFALL-PARALLELISM, Prev: PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION, Up: PARALLEL-PROOF UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES proof features not supported with waterfall-parallelism enabled For a general introduction to ACL2(p), an experimental extension of ACL2 that supports parallel execution and proof, see *note PARALLELISM::. Please note that although this extension is usable and, we hope, robust in its behavior, there are still known issues to address beyond those listed explicitly below. While we expect ACL2(p) to perform correctly, it may never have the same level of attention to correctness as is given to ACL2; see *note PARALLELISM::, specifically the "IMPORTANT NOTE" there. Below we list proof features of ACL2 that are not yet supported when parallel execution is enabled for the primary ACL2 proof process, generally known as "the waterfall", typically by calling set-waterfall-parallelism. Please note that this topic is limited to the case that such waterfall parallelism is enabled. We believe that all ACL2 proof procedures are supported when waterfall parallelism is disabled, even in executables that support parallelism (see *note COMPILING-ACL2P::). Without a trust tag (see *note DEFTTAG::): We support clause-processors, computed-hints, and custom-keyword-hints that do not modify state, but we do not permit override-hints, regardless of whether they modify state. With a trust tag, the user can use clause-processors that modify state and can also use override-hints (see *note SET-WATERFALL-PARALLELISM-HACKS-ENABLED:: for a convenient mechanism for adding a trust tag). See *note ERROR-TRIPLES-AND-PARALLELISM:: for a discussion of how to avoid modifying state in such situations. Regardless of whether a trust tag is active: We do not support checkers of custom-keyword-hints to be anything but the default checker. GNU Make versions 3.81 and 3.82 formerly caused a lot of problems (version 3.80 somewhat less so), at least on Linux, when certifying books with ACL2 built on a host Lisp of CCL using `make'. CCL was updated around March 23, 2011 to fix this problem, so if you get segfaults (for example) with CCL, try updating your CCL installation. Book certification should generally work but may present some issues, including the following. o The standard `make'-based process for book certification will not use waterfall-parallelism, which is disabled by default (even when compiling-acl2p by using the ACL2_PAR flag). See *note BOOKS-CERTIFICATION:: and see *note BOOKS-CERTIFICATION-CLASSIC::, which explain that acl2-customization files are ignored during that process unless specified explicitly on the command line or in the environment. o A book certified with ACL2(p) might subsequently cause an error when included with ACL2. As of this writing, however, we have only seen this for a book in which deftheory-static is used. o In general, ACL2(p) is primarily intended to support more rapid interactive development. While we are unaware of an unsoundness likely to affect an ACL2(p) user, we suggest using ACL2 for final book certification, rather than ACL2(p), to lower the risk of unsound book certification. Proof output can contain repeated printing of the same subgoal name. Gag-mode isn't officially supported, although it has proved helpful to use ACL2(p) in conjunction with (set-gag-mode t) (because this setting suppresses the output that occurs outside the waterfall). This being said, ACL2(p) also prints key checkpoints (for example see *note INTRODUCTION-TO-KEY-CHECKPOINTS::), but with a notion of "key checkpoint" that does not take into account whether the goal is later proved by induction. See *note ACL2P-KEY-CHECKPOINTS:: for further explanation of these key checkpoints. Note that pso is also not supported. The :brr utility is not supported. The accumulated-persistence utility is not supported. Tracking for forward-chaining-reports is not supported (see *note SET-FC-CRITERIA::). Time limits (see *note WITH-PROVER-TIME-LIMIT::) aren't supported. The timing information printed at the end of a proof attempt, which is intended to represent cpu time (not wall-clock time), may be somewhat inaccurate when waterfall-parallelism is non-nil. Consider using time$ to obtain timing information. The use of wormholes is not recommended, as there may be race conditions. Output specific to :OR hints is disabled. Proof trees are likely not to work as originally designed. The use of set-inhibit-output-lst may not fully inhibit proof output. Reporting of splitter rules is currently unsupported when waterfall-parallelism is on. Interrupting a proof attempt is not yet properly supported. At a minimum, interrupts are trickier with waterfall parallelism enabled. For one, the user typically needs to issue the interrupt twice before the proof attempt is actually interrupted. Additionally, on rare occasions the theorem is registered as proved, even though the prover did not finish the proof. If this occurs, issue a :u (see *note UBT::) and you will likely be at a stable state. Also with regards to interrupting a proof attempt, sometimes the user may need to issue a :q and lp to reset properly the parallelism implementation to a stable state. The primary symptom that the user is experiencing this issue is that threads will continue to compute in the background, even though there should be no proof attempt in progress. The user can observe this symptom by examining the CPU utilization of their ACL2 process, for example on Linux/Unix with the shell process top. Lisp usage greater than a few percent is indicative of this problem. Because of how ACL2 arrays are designed, the user may find that, in practice, ACL2 arrays work (but perhaps with some slow-array-warning messages). However, we are aware of race conditions that can cause problems. Instead of dynamically monitoring rewrites, dmr instead dynamically outputs information helpful for debugging the performance of proof parallelism. The instructions concerning how to see this debugging information are the same as the instructions for enabling dmr mode. If you are working with LispWorks 6.0 or 6.0.1, then you may see messages about misaligned conses. The state of the system may be corrupted after such a message has been printed. This LispWorks bug is fixed in LispWorks 6.1. The waterfall parallelism mode :resource-and-timing-based is not fully supported when the host Lisp is other than CCL. It may work, but we have not attempted to address a potential race condition. Proof output for splitter rules (see *note SPLITTER::) is currently unsupported when waterfall-parallelism is enabled. (Comment for ACL2(h) users; see *note HONS-AND-MEMOIZATION::.) Memoization may not work as intended when executing in parallel (including the waterfall). In an effort to be helpful to the user, the functions automatically memoized by ACL2(h) are unmemoized when setting waterfall parallelism to anything but nil. Those exact functions are again memoized once waterfall parallelism is disabled. Additionally, any functions memoized within the ACL2 loop (by a call of memoize) are also unmemoized when enabling waterfall parallelism and once again memoized when disabling waterfall parallelism. This is implemented by returning the memoization state to what it was before enabling waterfall parallelism. As such, the user should be aware that any changes made to the memoization state while waterfall parallelism is enabled will be lost once waterfall parallelism is disabled.  File: acl2-doc-emacs.info, Node: WATERFALL-PARALLELISM, Next: WATERFALL-PRINTING, Prev: UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES, Up: PARALLEL-PROOF WATERFALL-PARALLELISM for ACL2(p): configuring the parallel execution of the waterfall See *note SET-WATERFALL-PARALLELISM::.  File: acl2-doc-emacs.info, Node: WATERFALL-PRINTING, Prev: WATERFALL-PARALLELISM, Up: PARALLEL-PROOF WATERFALL-PRINTING for ACL2(p): configuring the printing within the parallelized waterfall See *note SET-WATERFALL-PRINTING::.  File: acl2-doc-emacs.info, Node: UNSUPPORTED-PARALLELISM-FEATURES, Next: WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION, Prev: PARALLEL-PROOF, Up: PARALLELISM UNSUPPORTED-PARALLELISM-FEATURES ACL2 features not supported in ACL2(p) This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. See *note PARALLELISM-TUTORIAL:: for an introduction to parallel programming in ACL2. For proof features of ACL2 that are not yet supported when parallel execution is enabled for the primary ACL2 proof process, generally known as "the waterfall", see *note UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES::. Please note that this topic discusses ACL2 features that are disabled when using ACL2(p) (see *note COMPILING-ACL2P::). These features are disabled regardless of whether waterfall parallelism is enabled. Calls of observation-cw simply convert to calls of cw, so suppressing observations (see *note SET-INHIBIT-OUTPUT-LST::) will not suppress these messages. Memoization is not supported when executing in parallel. See *note UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES:: for memoization details related to waterfall parallelism. Since, as of April 2012, garbage collection is inherently sequential, ACL2(p) minimizes the use of garbage collection by setting a high garbage collection threshold. As a result, ACL2(p) is not expected to perform well on machines with less memory than this threshold (1 gigabyte, as of April 2012). In CCL, the underlying parallel execution engine is tuned for the number of CPU cores (or hardware threads) actually available in the machine. SBCL and LispWorks are tuned for a machine with 16 CPU cores. CCL is considered to be the "flagship Lisp" for parallel execution in ACL2. The SBCL and LispWorks implementations are thought to be generally stable. However, due to their relatively less common use, the SBCL and LispWorks implementations are likely less robust than the CCL implementation. The time-tracker utility is a no-op for ACL2(p).  File: acl2-doc-emacs.info, Node: WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION, Prev: UNSUPPORTED-PARALLELISM-FEATURES, Up: PARALLELISM WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION for ACL2(p): using waterfall parallelism during book certification This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. There are books whose certification can be sped up significantly by using waterfall parallelism. (See *note PARALLELISM::, including the caveat in its "IMPORTANT NOTE".) One such example in the ACL2 community books is models/jvm/m5/apprentice.lisp, which is typically excluded from regressions because of how long it takes to certify. In order to use waterfall parallelism during certification of a book .lisp using `make' (see *note BOOKS-CERTIFICATION:: and see *note BOOKS-CERTIFICATION-CLASSIC::), we recommend creating a file .acl2 that includes the following forms. #+acl2-par (set-waterfall-parallelism t) (certify-book ? t) ; other arguments may be preferable Note that there are books that will not certify when waterfall-parallelism is enabled. See file acl2-customization-files/README for more information, including how to certify essentially all books using waterfall parallelism.  File: acl2-doc-emacs.info, Node: PROGRAMMING, Next: PROOF-CHECKER, Prev: PARALLELISM, Up: Top PROGRAMMING programming in ACL2 This documentation topic is a parent topic under which we include documentation topics for built-in functions, macros, and special forms (see *note ACL2-BUILT-INS::) as well as topics for notions important to programming with ACL2. If you don't find what you're looking for, see the Index or see individual topics that may be more directly appropriate; for example, see *note EVENTS:: for top-level event constructorsr like defun. * Menu: * ACL2-BUILT-INS:: built-in ACL2 functions * ACL2-USER:: a package the ACL2 user may prefer * ARRAYS:: an introduction to ACL2 arrays * COMPILATION:: compiling ACL2 functions * DECLARE:: declarations * EQUALITY-VARIANTS:: versions of a function using different equality tests * IGNORABLE:: See *note DECLARE::. * IGNORE:: See *note DECLARE::. * IRRELEVANT-FORMALS:: formals that are used but only insignificantly * OPTIMIZE:: See *note DECLARE::. * REDEFINING-PROGRAMS:: an explanation of why we restrict redefinitions * SINGLE-THREADED-OBJECTS:: See *note STOBJ::. * STATE:: the von Neumannesque ACL2 state object * TIME-TRACKER:: display time spent during specified evaluation * TYPE:: See *note DECLARE::. * ZERO-TEST-IDIOMS:: how to test for 0 Related topics other than immediate subtopics: * ACL2-CUSTOMIZATION:: file of initial commands for ACL2 to run at startup * CERTIFY-BOOK:: how to produce a certificate for a book * COMP:: compile some ACL2 functions * COMP-GCL:: compile some ACL2 functions leaving .c and .h files * DEFCONST:: define a constant * DEFPKG:: define a new symbol package * DEFUN:: define a function symbol * MUTUAL-RECURSION:: define some mutually recursive functions * SET-BOGUS-MUTUAL-RECURSION-OK:: allow unnecessary ``mutual recursion'' * SET-COMPILE-FNS:: have each function compiled as you go along. * SET-IGNORE-OK:: allow unused formals and locals without an ignore or ignorable declaration * SET-IRRELEVANT-FORMALS-OK:: allow irrelevant formals in definitions * SET-STATE-OK:: allow the use of STATE as a formal parameter * TIME$:: time an evaluation * TRACE:: tracing functions in ACL2  File: acl2-doc-emacs.info, Node: ACL2-BUILT-INS, Next: ACL2-USER, Prev: PROGRAMMING, Up: PROGRAMMING ACL2-BUILT-INS built-in ACL2 functions This documentation topic is a parent topic under which we include documentation for built-in functions, macros, and special forms that are typically used in programming. For others, including those typically used as top-level commands or those that create events (defun, defthm, and so on), documentation may be found as a subtopic of some other parent topic. We do not document some of the more obscure functions provided by ACL2 that do not correspond to functions of Common Lisp. * Menu: * *:: multiplication macro * +:: addition macro * -:: macro for subtraction and negation * /:: macro for division and reciprocal * /=:: test inequality of two numbers * 1+:: increment by 1 * 1-:: decrement by 1 * <:: less-than * <=:: less-than-or-equal test * =:: test equality of two numbers * >:: greater-than test * >=:: greater-than-or-equal test * atsign:: (@) get the value of a global variable in state * ABS:: the absolute value of a real number * ACL2-COUNT:: a commonly used measure for justifying recursion * ACL2-NUMBER-LISTP:: recognizer for a true list of numbers * ACL2-NUMBERP:: recognizer for numbers * ACONS:: constructor for association lists * ADD-TO-SET:: add a symbol to a list * ADD-TO-SET-EQ:: See *note ADD-TO-SET::. * ADD-TO-SET-EQL:: See *note ADD-TO-SET::. * ADD-TO-SET-EQUAL:: See *note ADD-TO-SET::. * ALISTP:: recognizer for association lists * ALLOCATE-FIXNUM-RANGE:: set aside fixnums in GCL * ALPHA-CHAR-P:: recognizer for alphabetic characters * ALPHORDER:: total order on atoms * AND:: conjunction * APPEND:: concatenate zero or more lists * ASH:: arithmetic shift operation * ASSERT$:: cause a hard error if the given test is false * ASSIGN:: assign to a global variable in state * ASSOC:: look up key in association list * ASSOC-EQ:: See *note ASSOC::. * ASSOC-EQUAL:: See *note ASSOC::. * ASSOC-KEYWORD:: look up key in a keyword-value-listp * ASSOC-STRING-EQUAL:: look up key, a string, in association list * ATOM:: recognizer for atoms * ATOM-LISTP:: recognizer for a true list of atoms * BINARY-*:: multiplication function * BINARY-+:: addition function * BINARY-APPEND:: concatenate two lists * BOOLE$:: perform a bit-wise logical operation on 2 two's complement integers * BOOLEANP:: recognizer for booleans * BREAK$:: cause an immediate Lisp break * BREAKS:: Common Lisp breaks * BUTLAST:: all but a final segment of a list * CAAAAR:: car of the caaar * CAAADR:: car of the caadr * CAAAR:: car of the caar * CAADAR:: car of the cadar * CAADDR:: car of the caddr * CAADR:: car of the cadr * CAAR:: car of the car * CADAAR:: car of the cdaar * CADADR:: car of the cdadr * CADAR:: car of the cdar * CADDAR:: car of the cddar * CADDDR:: car of the cdddr * CADDR:: car of the cddr * CADR:: car of the cdr * CANONICAL-PATHNAME:: the true absolute filename, with soft links resolved * CAR:: returns the first element of a non-empty list, else nil * CASE:: conditional based on if-then-else using eql * CASE-MATCH:: pattern matching or destructuring * CDAAAR:: cdr of the caaar * CDAADR:: cdr of the caadr * CDAAR:: cdr of the caar * CDADAR:: cdr of the cadar * CDADDR:: cdr of the caddr * CDADR:: cdr of the cadr * CDAR:: cdr of the car * CDDAAR:: cdr of the cdaar * CDDADR:: cdr of the cdadr * CDDAR:: cdr of the cdar * CDDDAR:: cdr of the cddar * CDDDDR:: cdr of the cdddr * CDDDR:: cdr of the cddr * CDDR:: cdr of the cdr * CDR:: returns the second element of a cons pair, else nil * CEILING:: division returning an integer by truncating toward positive infinity * CHAR:: the nth element (zero-based) of a string * CHAR-CODE:: the numeric code for a given character * CHAR-DOWNCASE:: turn upper-case characters into lower-case characters * CHAR-EQUAL:: character equality without regard to case * CHAR-UPCASE:: turn lower-case characters into upper-case characters * CHAR<:: less-than test for characters * CHAR<=:: less-than-or-equal test for characters * CHAR>:: greater-than test for characters * CHAR>=:: greater-than-or-equal test for characters * CHARACTER-ALISTP:: recognizer for association lists with characters as keys * CHARACTER-LISTP:: recognizer for a true list of characters * CHARACTERP:: recognizer for characters * CHARACTERS:: characters in ACL2 * CHECK-SUM:: assigning ``often unique'' integers to files and objects * CLOSE-INPUT-CHANNEL:: See *note IO::. * CLOSE-OUTPUT-CHANNEL:: See *note IO::. * CODE-CHAR:: the character corresponding to a given numeric code * COERCE:: coerce a character list to a string and a string to a list * COMPLEX:: create an ACL2 number * COMPLEX-RATIONALP:: recognizes complex rational numbers * COMPLEX/COMPLEX-RATIONALP:: recognizer for complex numbers * CONCATENATE:: concatenate lists or strings together * COND:: conditional based on if-then-else * CONJUGATE:: complex number conjugate * CONS:: pair and list constructor * CONSP:: recognizer for cons pairs * COUNT:: count the number of occurrences of an item in a string or true-list * CPU-CORE-COUNT:: the number of cpu cores * CW:: print to the comment window * CW!:: print to the comment window * DELETE-ASSOC:: remove the first pair from an association list for a given key * DELETE-ASSOC-EQ:: See *note DELETE-ASSOC::. * DELETE-ASSOC-EQUAL:: See *note DELETE-ASSOC::. * DENOMINATOR:: divisor of a ratio in lowest terms * DIGIT-CHAR-P:: the number, if any, corresponding to a given character * DIGIT-TO-CHAR:: map a digit to a character * E0-ORD-<:: the old ordering function for ACL2 ordinals * E0-ORDINALP:: the old recognizer for ACL2 ordinals * EC-CALL:: execute a call in the ACL2 logic instead of raw Lisp * EIGHTH:: eighth member of the list * ENDP:: recognizer for empty lists * EQ:: equality of symbols * EQL:: test equality (of two numbers, symbols, or characters) * EQLABLE-ALISTP:: recognizer for a true list of pairs whose cars are suitable for eql * EQLABLE-LISTP:: recognizer for a true list of objects each suitable for eql * EQLABLEP:: the guard for the function eql * EQUAL:: true equality * ER:: print an error message and ``cause an error'' * ER-PROGN:: perform a sequence of state-changing ``error triples'' * ERROR1:: print an error message and cause a ``soft error'' * EVENP:: test whether an integer is even * EXPLODE-NONNEGATIVE-INTEGER:: the list of characters in the radix-r form of a number * EXPT:: exponential function * F-GET-GLOBAL:: get the value of a global variable in state * F-PUT-GLOBAL:: assign to a global variable in state * FIFTH:: fifth member of the list * FIRST:: first member of the list * FIX:: coerce to a number * FIX-TRUE-LIST:: coerce to a true list * FLET:: local binding of function symbols * FLOOR:: division returning an integer by truncating toward negative infinity * FMS:: :(str alist co-channel state evisc) => state * FMS!:: :(str alist co-channel state evisc) => state * FMS!-TO-STRING:: See *note PRINTING-TO-STRINGS::. * FMS-TO-STRING:: See *note PRINTING-TO-STRINGS::. * FMT:: formatted printing * FMT!:: :(str alist co-channel state evisc) => state * FMT!-TO-STRING:: See *note PRINTING-TO-STRINGS::. * FMT-TO-COMMENT-WINDOW:: print to the comment window * FMT-TO-STRING:: See *note PRINTING-TO-STRINGS::. * FMT1:: :(str alist col co-channel state evisc) => (mv col state) * FMT1!:: :(str alist col channel state evisc) => (mv col state) * FMT1!-TO-STRING:: See *note PRINTING-TO-STRINGS::. * FMT1-TO-STRING:: See *note PRINTING-TO-STRINGS::. * FOURTH:: fourth member of the list * GET-OUTPUT-STREAM-STRING$:: See *note IO::. * GETENV$:: read an environment variable * GETPROP:: access fast property lists * GOOD-ATOM-LISTP:: recognizer for a true list of ``good'' atoms * HARD-ERROR:: print an error message and stop execution * IDENTITY:: the identity function * IF:: if-then-else function * IFF:: logical ``if and only if'' * IFIX:: coerce to an integer * ILLEGAL:: print an error message and stop execution * IMAGPART:: imaginary part of a complex number * IMPLIES:: logical implication * IMPROPER-CONSP:: recognizer for improper (non-null-terminated) non-empty lists * INT=:: test equality of two integers * INTEGER-LENGTH:: number of bits in two's complement integer representation * INTEGER-LISTP:: recognizer for a true list of integers * INTEGERP:: recognizer for whole numbers * INTERN:: create a new symbol in a given package * INTERN$:: create a new symbol in a given package * INTERN-IN-PACKAGE-OF-SYMBOL:: create a symbol with a given name * INTERSECTION$:: elements of one list that are not elements of another * INTERSECTION-EQ:: See *note INTERSECTION$::. * INTERSECTION-EQUAL:: See *note INTERSECTION$::. * INTERSECTP:: test whether two lists intersect * INTERSECTP-EQ:: See *note INTERSECTP::. * INTERSECTP-EQUAL:: See *note INTERSECTP::. * KEYWORD-VALUE-LISTP:: recognizer for true lists whose even-position elements are keywords * KEYWORDP:: recognizer for keywords * KWOTE:: quote an arbitrary object * KWOTE-LST:: quote an arbitrary true list of objects * LAST:: the last cons (not element) of a list * LEN:: length of a list * LENGTH:: length of a string or proper list * LET:: binding of lexically scoped (local) variables * LET*:: binding of lexically scoped (local) variables * LEXORDER:: total order on ACL2 objects * LIST:: build a list * LIST*:: build a list * LISTP:: recognizer for (not necessarily proper) lists * LOGAND:: bitwise logical `and' of zero or more integers * LOGANDC1:: bitwise logical `and' of two ints, complementing the first * LOGANDC2:: bitwise logical `and' of two ints, complementing the second * LOGBITP:: the ith bit of an integer * LOGCOUNT:: number of ``on'' bits in a two's complement number * LOGEQV:: bitwise logical equivalence of zero or more integers * LOGIOR:: bitwise logical inclusive or of zero or more integers * LOGNAND:: bitwise logical `nand' of two integers * LOGNOR:: bitwise logical `nor' of two integers * LOGNOT:: bitwise not of a two's complement number * LOGORC1:: bitwise logical inclusive or of two ints, complementing the first * LOGORC2:: bitwise logical inclusive or of two ints, complementing the second * LOGTEST:: test if two integers share a `1' bit * LOGXOR:: bitwise logical exclusive or of zero or more integers * LOWER-CASE-P:: recognizer for lower case characters * MAKE-CHARACTER-LIST:: coerce to a list of characters * MAKE-LIST:: make a list of a given size * MAKE-ORD:: a constructor for ordinals. * MAX:: the larger of two numbers * MBE:: attach code for execution * MBE1:: attach code for execution * MBT:: introduce a test not to be evaluated * MEMBER:: membership predicate * MEMBER-EQ:: See *note MEMBER::. * MEMBER-EQUAL:: See *note MEMBER::. * MIN:: the smaller of two numbers * MINUSP:: test whether a number is negative * MOD:: remainder using floor * MOD-EXPT:: exponential function * MSG:: construct a ``message'' suitable for the ~@ directive of fmt * MUST-BE-EQUAL:: attach code for execution * MV:: returning a multiple value * MV-LET:: calling multi-valued ACL2 functions * MV-LIST:: converting multiple-valued result to a single-valued list * MV-NTH:: the mv-nth element (zero-based) of a list * MV?:: return one or more values * MV?-LET:: calling possibly multi-valued ACL2 functions * NAT-LISTP:: recognizer for a true list of natural numbers * NATP:: a recognizer for the natural numbers * NFIX:: coerce to a natural number * NINTH:: ninth member of the list * NO-DUPLICATESP:: check for duplicates in a list * NO-DUPLICATESP-EQ:: See *note NO-DUPLICATESP::. * NO-DUPLICATESP-EQUAL:: See *note NO-DUPLICATESP::. * NON-EXEC:: mark code as non-executable * NONNEGATIVE-INTEGER-QUOTIENT:: natural number division function * NOT:: logical negation * NTH:: the nth element (zero-based) of a list * NTHCDR:: final segment of a list * NULL:: recognizer for the empty list * NUMERATOR:: dividend of a ratio in lowest terms * O-FINP:: recognizes if an ordinal is finite * O-FIRST-COEFF:: returns the first coefficient of an ordinal * O-FIRST-EXPT:: the first exponent of an ordinal * O-INFP:: recognizes if an ordinal is infinite * O-P:: a recognizer for the ordinals up to epsilon-0 * O-RST:: returns the rest of an infinite ordinal * O<:: the well-founded less-than relation on ordinals up to epsilon-0 * O<=:: the less-than-or-equal relation for the ordinals * O>:: the greater-than relation for the ordinals * O>=:: the greater-than-or-equal relation for the ordinals * OBSERVATION:: print an observation * OBSERVATION-CW:: See *note OBSERVATION::. * ODDP:: test whether an integer is odd * OPEN-INPUT-CHANNEL:: See *note IO::. * OPEN-INPUT-CHANNEL-P:: See *note IO::. * OPEN-OUTPUT-CHANNEL:: See *note IO::. * OPEN-OUTPUT-CHANNEL-P:: See *note IO::. * OR:: disjunction * ORACLE-APPLY:: call a function argument on the given list of arguments * ORACLE-APPLY-RAW:: call a function argument on the given list of arguments, no restrictions * ORACLE-FUNCALL:: call a function argument on the remaining arguments * PAIRLIS:: See *note PAIRLIS$:: * PAIRLIS$:: zipper together two lists * PEEK-CHAR$:: See *note IO::. * PKG-IMPORTS:: list of symbols imported into a given package * PKG-WITNESS:: return a specific symbol in the indicated package * PLUSP:: test whether a number is positive * POSITION:: position of an item in a string or a list * POSITION-EQ:: See *note POSITION::. * POSITION-EQUAL:: See *note POSITION::. * POSP:: a recognizer for the positive integers * PPROGN:: evaluate a sequence of forms that return state * PRINC$:: print an atom * PRINT-OBJECT$:: See *note IO::. * PROG2$:: execute two forms and return the value of the second one * PROGN$:: execute a sequence of forms and return the value of the last one * PROOFS-CO:: the proofs character output channel * PROPER-CONSP:: recognizer for proper (null-terminated) non-empty lists * PSEUDO-TERMP:: a predicate for recognizing term-like s-expressions * PUT-ASSOC:: modify an association list by associating a value with a key * PUT-ASSOC-EQ:: See *note PUT-ASSOC::. * PUT-ASSOC-EQL:: See *note PUT-ASSOC::. * PUT-ASSOC-EQUAL:: See *note PUT-ASSOC::. * PUTPROP:: update fast property lists * QUOTE:: create a constant * R-EQLABLE-ALISTP:: recognizer for a true list of pairs whose cdrs are suitable for eql * R-SYMBOL-ALISTP:: recognizer for association lists with symbols as values * RANDOM$:: obtain a random value * RASSOC:: look up value in association list * RASSOC-EQ:: See *note RASSOC::. * RASSOC-EQUAL:: See *note RASSOC::. * RATIONAL-LISTP:: recognizer for a true list of rational numbers * RATIONALP:: recognizer for rational numbers (ratios and integers) * READ-BYTE$:: See *note IO::. * READ-CHAR$:: See *note IO::. * READ-OBJECT:: See *note IO::. * READ-RUN-TIME:: read elapsed runtime * REAL/RATIONALP:: recognizer for rational numbers (including real number in ACL2(r)) * REALFIX:: coerce to a real number * REALPART:: real part of a complex number * REM:: remainder using truncate * REMOVE:: remove all occurrences * REMOVE-DUPLICATES:: remove duplicates from a string or a list * REMOVE-DUPLICATES-EQ:: See *note REMOVE-DUPLICATES::. * REMOVE-DUPLICATES-EQUAL:: See *note REMOVE-DUPLICATES::. * REMOVE-EQ:: See *note REMOVE::. * REMOVE-EQUAL:: See *note REMOVE::. * REMOVE1:: remove first occurrences, testing using eql * REMOVE1-EQ:: See *note REMOVE1::. * REMOVE1-EQUAL:: See *note REMOVE1::. * REST:: rest (cdr) of the list * RETURN-LAST:: return the last argument, perhaps with side effects * REVAPPEND:: concatentate the reverse of one list to another * REVERSE:: reverse a list or string * RFIX:: coerce to a rational number * ROUND:: division returning an integer by rounding off * SEARCH:: search for a string or list in another string or list * SECOND:: second member of the list * SET-DIFFERENCE$:: elements of one list that are not elements of another * SET-DIFFERENCE-EQ:: See *note SET-DIFFERENCE$::. * SET-DIFFERENCE-EQUAL:: See *note SET-DIFFERENCE$::. * SETENV$:: set an environment variable * SEVENTH:: seventh member of the list * SIGNED-BYTE-P:: recognizer for signed integers that fit in a specified bit width * SIGNUM:: indicator for positive, negative, or zero * SIXTH:: sixth member of the list * STANDARD-CHAR-LISTP:: recognizer for a true list of standard characters * STANDARD-CHAR-P:: recognizer for standard characters * STANDARD-CO:: the character output channel to which ld prints * STANDARD-OI:: the standard object input ``channel'' * STANDARD-STRING-ALISTP:: recognizer for association lists with standard strings as keys * STATE-GLOBAL-LET*:: bind state global variables * STRING:: coerce to a string * STRING-APPEND:: concatenate two strings * STRING-DOWNCASE:: in a given string, turn upper-case characters into lower-case * STRING-EQUAL:: string equality without regard to case * STRING-LISTP:: recognizer for a true list of strings * STRING-UPCASE:: in a given string, turn lower-case characters into upper-case * STRING<:: less-than test for strings * STRING<=:: less-than-or-equal test for strings * STRING>:: greater-than test for strings * STRING>=:: less-than-or-equal test for strings * STRINGP:: recognizer for strings * STRIP-CARS:: collect up all first components of pairs in a list * STRIP-CDRS:: collect up all second components of pairs in a list * SUBLIS:: substitute an alist into a tree * SUBSEQ:: subsequence of a string or list * SUBSETP:: test if every member of one list is a member of the other * SUBSETP-EQ:: See *note SUBSETP::. * SUBSETP-EQUAL:: See *note SUBSETP::. * SUBST:: a single substitution into a tree * SUBSTITUTE:: substitute into a string or a list, using eql as test * SYMBOL-<:: less-than test for symbols * SYMBOL-ALISTP:: recognizer for association lists with symbols as keys * SYMBOL-LISTP:: recognizer for a true list of symbols * SYMBOL-NAME:: the name of a symbol (a string) * SYMBOL-PACKAGE-NAME:: the name of the package of a symbol (a string) * SYMBOLP:: recognizer for symbols * SYS-CALL:: make a system call to the host operating system * SYS-CALL+:: make a system call to the host OS, returning status and output * SYS-CALL-STATUS:: exit status from the preceding system call * TAKE:: initial segment of a list * TENTH:: tenth member of the list * TERM-ORDER:: the ordering relation on terms used by ACL2 * THE:: run-time type check * THIRD:: third member of the list * TIME$:: time an evaluation * TRUE-LIST-LISTP:: recognizer for true (proper) lists of true lists * TRUE-LISTP:: recognizer for proper (null-terminated) lists * TRUNCATE:: division returning an integer by truncating toward 0 * UNARY--:: arithmetic negation function * UNARY-/:: reciprocal function * UNION$:: elements of one list that are not elements of another * UNION-EQ:: See *note UNION$::. * UNION-EQUAL:: See *note UNION$::. * UNSIGNED-BYTE-P:: recognizer for natural numbers that fit in a specified bit width * UNTRANSLATE:: See *note USER-DEFINED-FUNCTIONS-TABLE::. * UPDATE-NTH:: modify a list by putting the given value at the given position * UPPER-CASE-P:: recognizer for upper case characters * WITH-LIVE-STATE:: allow a reference to state in raw Lisp * WRITE-BYTE$:: See *note IO::. * XOR:: logical ``exclusive or'' * ZEROP:: test an acl2-number against 0 * ZIP:: testing an ``integer'' against 0 * ZP:: testing a ``natural'' against 0 * ZPF:: testing a nonnegative fixnum against 0 See any documentation for Common Lisp for more details on many of these functions.  File: acl2-doc-emacs.info, Node: *, Next: +, Prev: ACL2-BUILT-INS, Up: ACL2-BUILT-INS * multiplication macro * is really a macro that expands to calls of the function binary-*. So for example (* x y 4 z) represents the same term as (binary-* x (binary-* y (binary-* 4 z))). See *note BINARY-*::. * is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: +, Next: -, Prev: *, Up: ACL2-BUILT-INS + addition macro + is really a macro that expands to calls of the function binary-+. So for example (+ x y 4 z) represents the same term as (binary-+ x (binary-+ y (binary-+ 4 z))). See *note BINARY-+::.  File: acl2-doc-emacs.info, Node: -, Next: /, Prev: +, Up: ACL2-BUILT-INS - macro for subtraction and negation See *note BINARY-+:: for addition and see *note UNARY--:: for negation. Note that - represents subtraction as follows: (- x y) represents the same term as (+ x (- y)) which is really (binary-+ x (unary-- y)). Also note that - represents arithmetic negation as follows: (- x) expands to (unary-- x).  File: acl2-doc-emacs.info, Node: /, Next: /=, Prev: -, Up: ACL2-BUILT-INS / macro for division and reciprocal See *note BINARY-*:: for multiplication and see *note UNARY-/:: for reciprocal. Note that / represents division as follows: (/ x y) represents the same term as (* x (/ y)) which is really (binary-* x (unary-/ y)). Also note that / represents reciprocal as follows: (/ x) expands to (unary-/ x). / is a Common Lisp macro. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: /=, Next: 1+, Prev: /, Up: ACL2-BUILT-INS /= test inequality of two numbers (/= x y) is logically equivalent to (not (equal x y)). Unlike equal, /= has a guard requiring both of its arguments to be numbers. Generally, /= is executed more efficiently than a combination of not and equal. For a discussion of the various ways to test against 0, See *note ZERO-TEST-IDIOMS::. /= is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: 1+, Next: 1-, Prev: /=, Up: ACL2-BUILT-INS 1+ increment by 1 (1+ x) is the same as (+ 1 x). See *note +::. 1+ is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: 1-, Next: <, Prev: 1+, Up: ACL2-BUILT-INS 1- decrement by 1 (1- x) is the same as (- x 1). See *note -::. 1- is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: <, Next: <=, Prev: 1-, Up: ACL2-BUILT-INS < less-than Completion Axiom (completion-of-<): (equal (< x y) (if (and (rationalp x) (rationalp y)) (< x y) (let ((x1 (if (acl2-numberp x) x 0)) (y1 (if (acl2-numberp y) y 0))) (or (< (realpart x1) (realpart y1)) (and (equal (realpart x1) (realpart y1)) (< (imagpart x1) (imagpart y1))))))) Guard for (< x y): (and (rationalp x) (rationalp y)) Notice that like all arithmetic functions, < treats non-numeric inputs as 0. This function has the usual meaning on the rational numbers, but is extended to the complex rational numbers using the lexicographic order: first the real parts are compared, and if they are equal, then the imaginary parts are compared.  File: acl2-doc-emacs.info, Node: <=, Next: =, Prev: <, Up: ACL2-BUILT-INS <= less-than-or-equal test <= is a macro, and (<= x y) expands to the same thing as (not (< y x)). See *note <::. <= is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: =, Next: >, Prev: <=, Up: ACL2-BUILT-INS = test equality of two numbers (= x y) is logically equivalent to (equal x y). Unlike equal, = has a guard requiring both of its arguments to be numbers. Generally, = is executed more efficiently than equal. For a discussion of the various ways to test against 0, See *note ZERO-TEST-IDIOMS::. = is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: >, Next: >=, Prev: =, Up: ACL2-BUILT-INS > greater-than test > is a macro, and (> x y) expands to the same thing as (< y x). See *note <::. > is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: >=, Next: atsign, Prev: >, Up: ACL2-BUILT-INS >= greater-than-or-equal test >= is a macro, and (>= x y) expands to the same thing as (not (< x y)). See *note <::. >= is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: atsign, Next: ABS, Prev: >=, Up: ACL2-BUILT-INS @ get the value of a global variable in state Examples: (+ (@ y) 1) (assign a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B)) General Form: (@ symbol) where symbol is any symbol to which you have assigned a global value. This macro expands into (f-get-global 'symbol state), which retrieves the stored value of the symbol. The macro f-get-global is closely related to @: (@ var) macroexpands to (f-get-global 'var state). The macro assign makes it convenient to set the value of a symbol. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.  File: acl2-doc-emacs.info, Node: ABS, Next: ACL2-COUNT, Prev: atsign, Up: ACL2-BUILT-INS ABS the absolute value of a real number (Abs x) is -x if x is negative and is x otherwise. The guard for abs requires its argument to be a rational (real, in ACL2(r)) number. Abs is a Common Lisp function. See any Common Lisp documentation for more information. From "Common Lisp the Language" page 205, we must not allow complex x as an argument to abs in ACL2, because if we did we would have to return a number that might be a floating point number and hence not an ACL2 object. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ACL2-COUNT, Next: ACL2-NUMBER-LISTP, Prev: ABS, Up: ACL2-BUILT-INS ACL2-COUNT a commonly used measure for justifying recursion (Acl2-count x) returns a nonnegative integer that indicates the "size" of its argument x. All characters and symbols have acl2-count 0. The acl2-count of a string is the number of characters in it, i.e., its length. The acl2-count of a cons is one greater than the sum of the acl2-counts of the car and cdr. The acl2-count of an integer is its absolute value. The acl2-count of a rational is the sum of the acl2-counts of the numerator and denominator. The acl2-count of a complex rational is one greater than the sum of the acl2-counts of the real and imaginary parts.  File: acl2-doc-emacs.info, Node: ACL2-NUMBER-LISTP, Next: ACL2-NUMBERP, Prev: ACL2-COUNT, Up: ACL2-BUILT-INS ACL2-NUMBER-LISTP recognizer for a true list of numbers The predicate acl2-number-listp tests whether its argument is a true list of numbers. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ACL2-NUMBERP, Next: ACONS, Prev: ACL2-NUMBER-LISTP, Up: ACL2-BUILT-INS ACL2-NUMBERP recognizer for numbers (acl2-numberp x) is true if and only if x is a number, i.e., a rational or complex rational number.  File: acl2-doc-emacs.info, Node: ACONS, Next: ADD-TO-SET, Prev: ACL2-NUMBERP, Up: ACL2-BUILT-INS ACONS constructor for association lists (Acons key datum alist) equals the result of consing the pair (cons key datum) to the front of the association list alist. (Acons key datum alist) has a guard of (alistp alist). Acons is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ADD-TO-SET, Next: ADD-TO-SET-EQ, Prev: ACONS, Up: ACL2-BUILT-INS ADD-TO-SET add a symbol to a list General Forms: (add-to-set x lst) (add-to-set x lst :test 'eql) ; same as above (eql as equality test) (add-to-set x lst :test 'eq) ; same, but eq is equality test (add-to-set x lst :test 'equal) ; same, but equal is equality test For a symbol x and an object lst, (add-to-set-eq x lst) is the result of consing x on to the front of lst, unless x is already a member of lst, in which case the result is lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst. The guard for a call of add-to-set depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between add-to-set and its variants: (add-to-set-eq x lst) is equivalent to (add-to-set x lst :test 'eq); (add-to-set-equal x lst) is equivalent to (add-to-set x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function add-to-set-equal.  File: acl2-doc-emacs.info, Node: ADD-TO-SET-EQ, Next: ADD-TO-SET-EQL, Prev: ADD-TO-SET, Up: ACL2-BUILT-INS ADD-TO-SET-EQ See *note ADD-TO-SET::.  File: acl2-doc-emacs.info, Node: ADD-TO-SET-EQL, Next: ADD-TO-SET-EQUAL, Prev: ADD-TO-SET-EQ, Up: ACL2-BUILT-INS ADD-TO-SET-EQL See *note ADD-TO-SET::.  File: acl2-doc-emacs.info, Node: ADD-TO-SET-EQUAL, Next: ALISTP, Prev: ADD-TO-SET-EQL, Up: ACL2-BUILT-INS ADD-TO-SET-EQUAL See *note ADD-TO-SET::.  File: acl2-doc-emacs.info, Node: ALISTP, Next: ALLOCATE-FIXNUM-RANGE, Prev: ADD-TO-SET-EQUAL, Up: ACL2-BUILT-INS ALISTP recognizer for association lists (alistp x) is true if and only if x is a list of cons pairs. (alistp x) has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ALLOCATE-FIXNUM-RANGE, Next: ALPHA-CHAR-P, Prev: ALISTP, Up: ACL2-BUILT-INS ALLOCATE-FIXNUM-RANGE set aside fixnums in GCL (Allocate-fixnum-range fixnum-lo fixnum-hi) causes Gnu Common Lisp (GCL) to create a persistent table for the integers between fixnum-lo and fixnum-hi (both bounds inclusive). This table is referenced first when any integer is boxed and the existing box in the table is used if the integer is in bounds. This can speed up GCL considerably by avoiding wasteful fixnum boxing. Here, fixnum-lo and fixnum-hi should be fixnums. On 32-bit machines it would be good for them to be of type (signed-byte 30), with fixnum-lo <= fixnum-hi. When this function is executed in a Lisp implementation other than GCL, it has no side effect. This function always returns nil.  File: acl2-doc-emacs.info, Node: ALPHA-CHAR-P, Next: ALPHORDER, Prev: ALLOCATE-FIXNUM-RANGE, Up: ACL2-BUILT-INS ALPHA-CHAR-P recognizer for alphabetic characters (Alpha-char-p x) is true if and only if x is a alphabetic character, i.e., one of the characters #\a, #\b, ..., #\z, #\A, #\B, ..., #\Z. The guard for alpha-char-p requires its argument to be a character. Alpha-char-p is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ALPHORDER, Next: AND, Prev: ALPHA-CHAR-P, Up: ACL2-BUILT-INS ALPHORDER total order on atoms Alphorder is a non-strict total order, a "less than or equal," on atoms. By "non-strict total order" we mean a function that always returns t or nil and satisfies the following properties. o Antisymmetry: XrY & YrX -> X=Y o Transitivity: XrY & YrZ -> XrZ o Trichotomy: XrY v YrX Also see *note LEXORDER::, which extends alphorder to all objects. (Alphorder x y) has a guard of (and (atom x) (atom y)). Within a single type: rationals are compared arithmetically, complex rationals are compared lexicographically, characters are compared via their char-codes, and strings and symbols are compared with alphabetic ordering. Across types, rationals come before complexes, complexes come before characters, characters before strings, and strings before symbols. We also allow for "bad atoms," i.e., atoms that are not legal Lisp objects but make sense in the ACL2 logic; these come at the end, after symbols. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: AND, Next: APPEND, Prev: ALPHORDER, Up: ACL2-BUILT-INS AND conjunction And is the macro for conjunctions. And takes any number of arguments. And returns nil if one of the arguments is nil, but otherwise returns the last argument. If there are no arguments, and returns t. And is a Common Lisp macro. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: APPEND, Next: ASH, Prev: AND, Up: ACL2-BUILT-INS APPEND concatenate zero or more lists Append, which takes zero or more arguments, expects all the arguments except perhaps the last to be true (null-terminated) lists. It returns the result of concatenating all the elements of all the given lists into a single list. Actually, in ACL2 append is a macro that expands into calls of the binary function binary-append if there are at least two arguments; if there is just one argument then the expansion is that argument; and finally, (append) expands to nil. Append is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: ASH, Next: ASSERT$, Prev: APPEND, Up: ACL2-BUILT-INS ASH arithmetic shift operation (ash i c) is the result of taking the two's complement representation of the integer i and shifting it by c bits: shifting left and padding with c 0 bits if c is positive, shifting right and dropping (abs c) bits if c is negative, and simply returning i if c is 0. The guard for ash requires that its arguments are integers. Ash is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ASSERT$, Next: ASSIGN, Prev: ASH, Up: ACL2-BUILT-INS ASSERT$ cause a hard error if the given test is false General Form: (assert$ test form) where test returns a single value and form is arbitrary. Semantically, this call of assert$ is equivalent to form. However, it causes a hard error if the value of test is nil. That hard error invokes the function illegal, which has a guard that is equal to nil; so if you use assert$ in code for which you verify guards, then a proof obligation will be that the occurrence of test is never nil.  File: acl2-doc-emacs.info, Node: ASSIGN, Next: ASSOC, Prev: ASSERT$, Up: ACL2-BUILT-INS ASSIGN assign to a global variable in state Examples: (assign x (expt 2 10)) (assign a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B)) General Form: (assign symbol term) where symbol is any symbol (with certain enforced exclusions to avoid overwriting ACL2 system "globals") and term is any ACL2 term that could be evaluated at the top-level. Assign evaluates the term, stores the result as the value of the given symbol in the global-table of state, and returns the result. (Note: the actual implementation of the storage of this value is much more efficient than this discussion of the logic might suggest.) Assign is a macro that effectively expands to the more complicated but understandable: (pprogn (f-put-global 'symbol term state) (mv nil (f-get-global 'symbol state) state)). The macro f-put-global is closely related to assign: (assign var val) macroexpands to (f-put-global 'var val state). The macro @ gives convenient access to the value of such globals. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.  File: acl2-doc-emacs.info, Node: ASSOC, Next: ASSOC-EQ, Prev: ASSIGN, Up: ACL2-BUILT-INS ASSOC look up key in association list General Forms: (assoc x alist) (assoc x alist :test 'eql) ; same as above (eql as equality test) (assoc x alist :test 'eq) ; same, but eq is equality test (assoc x alist :test 'equal) ; same, but equal is equality test (Assoc x alist) is the first member of alist whose car is x, or nil if no such member exists. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with the cars of successive elements of alist. The guard for a call of assoc depends on the test. In all cases, the second argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-alistp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between assoc and its variants: (assoc-eq x alist) is equivalent to (assoc x alist :test 'eq); (assoc-equal x alist) is equivalent to (assoc x alist :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function assoc-equal. Assoc is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: ASSOC-EQ, Next: ASSOC-EQUAL, Prev: ASSOC, Up: ACL2-BUILT-INS ASSOC-EQ See *note ASSOC::.  File: acl2-doc-emacs.info, Node: ASSOC-EQUAL, Next: ASSOC-KEYWORD, Prev: ASSOC-EQ, Up: ACL2-BUILT-INS ASSOC-EQUAL See *note ASSOC::.  File: acl2-doc-emacs.info, Node: ASSOC-KEYWORD, Next: ASSOC-STRING-EQUAL, Prev: ASSOC-EQUAL, Up: ACL2-BUILT-INS ASSOC-KEYWORD look up key in a keyword-value-listp If l is a list of even length of the form (k1 a1 k2 a2 ... kn an), where each ki is a keyword, then (assoc-keyword key l) is the first tail of l starting with key if key is some ki, and is nil otherwise. The guard for (assoc-keyword key l) is (keyword-value-listp l). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ASSOC-STRING-EQUAL, Next: ATOM, Prev: ASSOC-KEYWORD, Up: ACL2-BUILT-INS ASSOC-STRING-EQUAL look up key, a string, in association list (Assoc-string-equal x alist) is similar to assoc-equal. However, for string x and alist alist, the comparison of x with successive keys in alist is done using string-equal rather than equal. The guard for assoc-string-equal requires that x is a string and alist is an alist. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ATOM, Next: ATOM-LISTP, Prev: ASSOC-STRING-EQUAL, Up: ACL2-BUILT-INS ATOM recognizer for atoms (atom x) is true if and only if x is an atom, i.e., not a cons pair. Atom has a guard of t, and is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ATOM-LISTP, Next: BINARY-*, Prev: ATOM, Up: ACL2-BUILT-INS ATOM-LISTP recognizer for a true list of atoms The predicate atom-listp tests whether its argument is a true-listp of atoms, i.e., of non-conses. Also see *note GOOD-ATOM-LISTP::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: BINARY-*, Next: BINARY-+, Prev: ATOM-LISTP, Up: ACL2-BUILT-INS BINARY-* multiplication function Completion Axiom (completion-of-*): (equal (binary-* x y) (if (acl2-numberp x) (if (acl2-numberp y) (binary-* x y) 0) 0)) Guard for (binary-* x y): (and (acl2-numberp x) (acl2-numberp y)) Notice that like all arithmetic functions, binary-* treats non-numeric inputs as 0. Calls of the macro * expand to calls of binary-*; see *note *::.  File: acl2-doc-emacs.info, Node: BINARY-+, Next: BINARY-APPEND, Prev: BINARY-*, Up: ACL2-BUILT-INS BINARY-+ addition function Completion Axiom (completion-of-+): (equal (binary-+ x y) (if (acl2-numberp x) (if (acl2-numberp y) (binary-+ x y) x) (if (acl2-numberp y) y 0))) Guard for (binary-+ x y): (and (acl2-numberp x) (acl2-numberp y)) Notice that like all arithmetic functions, binary-+ treats non-numeric inputs as 0. Calls of the macro + expand to calls of binary-+; see *note +::.  File: acl2-doc-emacs.info, Node: BINARY-APPEND, Next: BOOLE$, Prev: BINARY-+, Up: ACL2-BUILT-INS BINARY-APPEND concatenate two lists This binary function implements append, which is a macro in ACL2. See *note APPEND:: The guard for binary-append requires the first argument to be a true-listp. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: BOOLE$, Next: BOOLEANP, Prev: BINARY-APPEND, Up: ACL2-BUILT-INS BOOLE$ perform a bit-wise logical operation on 2 two's complement integers When integers x and y are viewed in their two's complement representation, (boole$ op x y) returns the result of applying the bit-wise logical operation specified by op. The following table is adapted from documentation for analogous Common Lisp function boole in the Common Lisp HyperSpec (`http://www.lisp.org/HyperSpec/Body/fun_boole.html#boole'). Note that the values of op for boole$ are ACL2 constants, rather than corresponding values of op for the Common Lisp function boole. op result ----------- --------- *boole-1* x *boole-2* y *boole-andc1* and complement of x with y *boole-andc2* and x with complement of y *boole-and* and *boole-c1* complement of x *boole-c2* complement of y *boole-clr* the constant 0 (all zero bits) *boole-eqv* equivalence (exclusive nor) *boole-ior* inclusive or *boole-nand* not-and *boole-nor* not-or *boole-orc1* or complement of x with y *boole-orc2* or x with complement of y *boole-set* the constant -1 (all one bits) *boole-xor* exclusive or The guard of boole$ specifies that op is the value of one of the constants above and that x and y are integers. See any Common Lisp documentation for analogous information about Common Lisp function boole. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: BOOLEANP, Next: BREAK$, Prev: BOOLE$, Up: ACL2-BUILT-INS BOOLEANP recognizer for booleans (Booleanp x) is t if x is t or nil, and is nil otherwise. See *note GENERALIZED-BOOLEANS:: for a discussion of a potential soundness problem for ACL2 related to the question: Which Common Lisp functions are known to return Boolean values? To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: BREAK$, Next: BREAKS, Prev: BOOLEANP, Up: ACL2-BUILT-INS BREAK$ cause an immediate Lisp break ACL2 users are generally advised to avoid breaking into raw Lisp. Advanced users may, on occasion, see the need to do so. Evaluating (break$) will have that effect. (Exception: break$ is disabled after evaluation of (set-debugger-enable :never); see *note SET-DEBUGGER-ENABLE::.) Break$ returns nil.  File: acl2-doc-emacs.info, Node: BREAKS, Next: BUTLAST, Prev: BREAK$, Up: ACL2-BUILT-INS BREAKS Common Lisp breaks Example: Broken at PROVE. Type :H for Help. >>:Q ACL2 !> You may interrupt the system by typing various control character sequences. The precise sequences are determined by the host Lisp and operating system environment. For example, in GCL and Allegro Common Lisp, a console interrupt is caused by typing "ctrl-c". If, however, the GCL or Allegro is running in an Emacs shell buffer, one must type "ctrl-c ctrl-c". If a break occurs, for example because of a bug in ACL2 or a user interrupt, the break will run a Common Lisp read-eval-print loop, not an ACL2 read-eval-print loop. This may not be obvious if the prompts in the two loops are similar. Because you are typing to a Common Lisp evaluator, you must be careful. It is possible to damage your ACL2 state in irreparable ways by executing non-ACL2 Common Lisp. It is even possible to disrupt and render inaccurate the interrupted evaluation of a simple ACL2 expression. For ACL2 built on most host Common Lisps, you will see the string [RAW LISP] in the prompt at a break, to emphasize that one is inside a break and hence should quit from the break. For some host Common Lisps, the top-level prompt also contains the string [RAW LISP]. See *note PROMPT:: for how to control printing of that string. The most reliable way to return to the ACL2 top level is by executing the following command: (abort!). Appropriate cleanup will then be done, which should leave you in an appropriate state. However, you may be able to quit from the break in the normal Lisp manner (as with :q in GCL or CCL, :reset in Allegro CL, and q in CMU CL). If this attempt to quit is successful, it will return you to the innermost ACL2 read-eval-print loop, with appropriate cleanup performed first. Note that if you are within a brr environment when the break occurs, quitting from the break will only return you to that environment, not to the top of ACL2's read-eval-print loop.  File: acl2-doc-emacs.info, Node: BUTLAST, Next: CAAAAR, Prev: BREAKS, Up: ACL2-BUILT-INS BUTLAST all but a final segment of a list (Butlast l n) is the list obtained by removing the last n elements from the true list l. The following is a theorem (though it takes some effort, including lemmas, to get ACL2 to prove it). (implies (and (integerp n) (<= 0 n) (true-listp l)) (equal (length (butlast l n)) (if (< n (length l)) (- (length l) n) 0))) For related functions, see *note TAKE:: and see *note NTHCDR::. The guard for (butlast l n) requires that n is a nonnegative integer and lst is a true list. Butlast is a Common Lisp function. See any Common Lisp documentation for more information. Note: In Common Lisp the second argument of butlast is optional, but in ACL2 it is required. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CAAAAR, Next: CAAADR, Prev: BUTLAST, Up: ACL2-BUILT-INS CAAAAR car of the caaar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CAAADR, Next: CAAAR, Prev: CAAAAR, Up: ACL2-BUILT-INS CAAADR car of the caadr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CAAAR, Next: CAADAR, Prev: CAAADR, Up: ACL2-BUILT-INS CAAAR car of the caar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CAADAR, Next: CAADDR, Prev: CAAAR, Up: ACL2-BUILT-INS CAADAR car of the cadar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CAADDR, Next: CAADR, Prev: CAADAR, Up: ACL2-BUILT-INS CAADDR car of the caddr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CAADR, Next: CAAR, Prev: CAADDR, Up: ACL2-BUILT-INS CAADR car of the cadr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CAAR, Next: CADAAR, Prev: CAADR, Up: ACL2-BUILT-INS CAAR car of the car See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADAAR, Next: CADADR, Prev: CAAR, Up: ACL2-BUILT-INS CADAAR car of the cdaar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADADR, Next: CADAR, Prev: CADAAR, Up: ACL2-BUILT-INS CADADR car of the cdadr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADAR, Next: CADDAR, Prev: CADADR, Up: ACL2-BUILT-INS CADAR car of the cdar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADDAR, Next: CADDDR, Prev: CADAR, Up: ACL2-BUILT-INS CADDAR car of the cddar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADDDR, Next: CADDR, Prev: CADDAR, Up: ACL2-BUILT-INS CADDDR car of the cdddr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADDR, Next: CADR, Prev: CADDDR, Up: ACL2-BUILT-INS CADDR car of the cddr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CADR, Next: CANONICAL-PATHNAME, Prev: CADDR, Up: ACL2-BUILT-INS CADR car of the cdr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CANONICAL-PATHNAME, Next: CAR, Prev: CADR, Up: ACL2-BUILT-INS CANONICAL-PATHNAME the true absolute filename, with soft links resolved For the name fname of a file, the form (Canonical-pathname fname nil state) evaluates to a Unix-style absolute filename representing the same file as fname, but generally without any use of soft links in the name. (Below, we explain the qualifier "generally".) If however the file indicated by fname does not exist, (canonical-pathname fname nil state) is nil. Thus, canonical-pathname can be used as one would use the raw Lisp function probe-file. The specification of (Canonical-pathname fname dir-p state) when dir-p is not nil is simlar, except that if the specified file exists but is not a directory, then the result is nil. The function canonical-pathname has a guard of t, though the second argument must be the ACL2 state. This function is introduced with the following properties. (defthm canonical-pathname-is-idempotent (equal (canonical-pathname (canonical-pathname x dir-p state) dir-p state) (canonical-pathname x dir-p state))) (defthm canonical-pathname-type (or (equal (canonical-pathname x dir-p state) nil) (stringp (canonical-pathname x dir-p state))) :rule-classes :type-prescription) We use the qualifier "generally", above, because there is no guarantee that the filename will be canonical without soft links, though we expect this to be true in practice. ACL2 attempts to compute the desired result and then checks that the input and result have the same Common Lisp "truename". This check is expected to succeed, but if it fails then the input string is returned unchanged, and to be conservative, the value returned is nil in this case if dir-p is true.  File: acl2-doc-emacs.info, Node: CAR, Next: CASE, Prev: CANONICAL-PATHNAME, Up: ACL2-BUILT-INS CAR returns the first element of a non-empty list, else nil Completion Axiom (completion-of-car): (equal (car x) (cond ((consp x) (car x)) (t nil))) Guard: (or (consp x) (equal x nil)) Notice that in the ACL2 logic, car returns nil for every atom.  File: acl2-doc-emacs.info, Node: CASE, Next: CASE-MATCH, Prev: CAR, Up: ACL2-BUILT-INS CASE conditional based on if-then-else using eql Example Form: (case typ ((:character foo) (open file-name :direction :output)) (bar (open-for-bar file-name)) (otherwise (my-error "Illegal."))) is the same as (cond ((member typ '(:character foo)) (open file-name :direction :output)) ((eql typ 'bar) (open-for-bar file-name)) (t (my-error "Illegal."))) which in turn is the same as (if (member typ '(:character foo)) (open file-name :direction :output) (if (eql typ 'bar) (open-for-bar file-name) (my-error "Illegal."))) Notice the quotations that appear in the example above: '(:character foo) and 'bar. General Forms: (case expr (x1 val-1) ... (xk val-k) (otherwise val-k+1)) (case expr (x1 val-1) ... (xk val-k) (t val-k+1)) (case expr (x1 val-1) ... (xk val-k)) where each xi is either eqlablep or a true list of eqlablep objects. The final otherwise or t case is optional. Case is defined in Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: CASE-MATCH, Next: CDAAAR, Prev: CASE, Up: ACL2-BUILT-INS CASE-MATCH pattern matching or destructuring General Form: (case-match x (pat1 dcl1 body1) ... (patk dclk bodyk)) where x is a variable symbol, the pati are structural patterns as described below, the dcli are optional declare forms and the bodyi are terms. Return the value(s) of the bodyi corresponding to the first pati matching x, or nil if none matches. Pattern Language: With the few special exceptions described below, matching requires that the cons structure of x be isomorphic to that of the pattern, down to the atoms in the pattern. Non-symbol atoms in the pattern match only themselves. Symbols in the pattern denote variables which match anything and which are bound by a successful match to the corresponding substructure of x. Variables that occur more than once must match the same (EQUAL) structure in every occurrence. Exceptions: & Matches anything and is not bound. Repeated occurrences of & in a pattern may match different structures. nil, t, *sym* These symbols cannot be bound and match only their global values. !sym where sym is a symbol that is already bound in the context of the case-match, matches only the current binding of sym. 'obj Matches only itself. Some examples are shown below. Below we show some sample patterns and examples of things they match and do not match. pattern matches non-matches (x y y) (ABC 3 3) (ABC 3 4) ; 3 is not 4 (fn x . rst) (P (A I) B C) (ABC) ; NIL is not (x . rst) (J (A I)) ; rst matches nil ('fn (g x) 3) (FN (H 4) 3) (GN (G X) 3) ; 'fn matches only itself (& t & !x) ((A) T (B) (C)) ; provided x is '(C) Consider the two binary trees that contain three leaves. They might be described as (x . (y . z)) and ((x . y) . z), where x, y, and z are atomic. Suppose we wished to recognize those trees. The following case-match would do: (case-match tree ((x . (y . z)) (and (atom x) (atom y) (atom z))) (((x . y) . z) (and (atom x) (atom y) (atom z)))) Suppose we wished to recognize such trees where all three tips are identical. Suppose further we wish to return the tip if the tree is one of those recognized ones and to return the number 7 otherwise. (case-match tree ((x . (x . x)) (if (atom x) x 7)) (((x . x) . x) (if (atom x) x 7)) (& 7)) Note that case-match returns nil if no pati matches. Thus if we must return 7 in that case, we have to add as the final pattern the &, which always matches anything.  File: acl2-doc-emacs.info, Node: CDAAAR, Next: CDAADR, Prev: CASE-MATCH, Up: ACL2-BUILT-INS CDAAAR cdr of the caaar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDAADR, Next: CDAAR, Prev: CDAAAR, Up: ACL2-BUILT-INS CDAADR cdr of the caadr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDAAR, Next: CDADAR, Prev: CDAADR, Up: ACL2-BUILT-INS CDAAR cdr of the caar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDADAR, Next: CDADDR, Prev: CDAAR, Up: ACL2-BUILT-INS CDADAR cdr of the cadar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDADDR, Next: CDADR, Prev: CDADAR, Up: ACL2-BUILT-INS CDADDR cdr of the caddr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDADR, Next: CDAR, Prev: CDADDR, Up: ACL2-BUILT-INS CDADR cdr of the cadr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDAR, Next: CDDAAR, Prev: CDADR, Up: ACL2-BUILT-INS CDAR cdr of the car See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDAAR, Next: CDDADR, Prev: CDAR, Up: ACL2-BUILT-INS CDDAAR cdr of the cdaar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDADR, Next: CDDAR, Prev: CDDAAR, Up: ACL2-BUILT-INS CDDADR cdr of the cdadr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDAR, Next: CDDDAR, Prev: CDDADR, Up: ACL2-BUILT-INS CDDAR cdr of the cdar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDDAR, Next: CDDDDR, Prev: CDDAR, Up: ACL2-BUILT-INS CDDDAR cdr of the cddar See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDDDR, Next: CDDDR, Prev: CDDDAR, Up: ACL2-BUILT-INS CDDDDR cdr of the cdddr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDDR, Next: CDDR, Prev: CDDDDR, Up: ACL2-BUILT-INS CDDDR cdr of the cddr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDDR, Next: CDR, Prev: CDDDR, Up: ACL2-BUILT-INS CDDR cdr of the cdr See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: CDR, Next: CEILING, Prev: CDDR, Up: ACL2-BUILT-INS CDR returns the second element of a cons pair, else nil Completion Axiom (completion-of-cdr): (equal (cdr x) (cond ((consp x) (cdr x)) (t nil))) Guard: (or (consp x) (equal x nil)) Notice that in the ACL2 logic, cdr returns nil for every atom.  File: acl2-doc-emacs.info, Node: CEILING, Next: CHAR, Prev: CDR, Up: ACL2-BUILT-INS CEILING division returning an integer by truncating toward positive infinity Example Forms: ACL2 !>(ceiling 14 3) 5 ACL2 !>(ceiling -14 3) -4 ACL2 !>(ceiling 14 -3) -4 ACL2 !>(ceiling -14 -3) 5 ACL2 !>(ceiling -15 -3) 5 (Ceiling i j) is the result of taking the quotient of i and j and returning the smallest integer that is at least as great as that quotient. For example, the quotient of -14 by 3 is -4 2/3, and the smallest integer at least that great is -4. The guard for (ceiling i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero. Ceiling is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 ceiling function returns only a single value, To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR, Next: CHAR-CODE, Prev: CEILING, Up: ACL2-BUILT-INS CHAR the nth element (zero-based) of a string (Char s n) is the nth element of s, zero-based. If n is greater than or equal to the length of s, then char returns nil. (Char s n) has a guard that n is a non-negative integer and s is a stringp. Char is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR-CODE, Next: CHAR-DOWNCASE, Prev: CHAR, Up: ACL2-BUILT-INS CHAR-CODE the numeric code for a given character Completion Axiom (completion-of-char-code): (equal (char-code x) (if (characterp x) (char-code x) 0)) Guard for (char-code x): (characterp x) This function maps all non-characters to 0.  File: acl2-doc-emacs.info, Node: CHAR-DOWNCASE, Next: CHAR-EQUAL, Prev: CHAR-CODE, Up: ACL2-BUILT-INS CHAR-DOWNCASE turn upper-case characters into lower-case characters (Char-downcase x) is equal to #\a when x is #\A, #\b when x is #\B, ..., and #\z when x is #\Z, and is x for any other character. The guard for char-downcase requires its argument to be a standard character (see *note STANDARD-CHAR-P::). Char-downcase is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR-EQUAL, Next: CHAR-UPCASE, Prev: CHAR-DOWNCASE, Up: ACL2-BUILT-INS CHAR-EQUAL character equality without regard to case For characters x and y, (char-equal x y) is true if and only if x and y are the same except perhaps for their case. The guard on char-equal requires that its arguments are both standard characters (see *note STANDARD-CHAR-P::). Char-equal is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR-UPCASE, Next: CHAR<, Prev: CHAR-EQUAL, Up: ACL2-BUILT-INS CHAR-UPCASE turn lower-case characters into upper-case characters (Char-upcase x) is equal to #\A when x is #\a, #\B when x is #\b, ..., and #\Z when x is #\z, and is x for any other character. The guard for char-upcase requires its argument to be a standard character (see *note STANDARD-CHAR-P::). Char-upcase is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR<, Next: CHAR<=, Prev: CHAR-UPCASE, Up: ACL2-BUILT-INS CHAR< less-than test for characters (char< x y) is true if and only if the character code of x is less than that of y. See *note CHAR-CODE::. The guard for char< specifies that its arguments are characters. Char< is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR<=, Next: CHAR>, Prev: CHAR<, Up: ACL2-BUILT-INS CHAR<= less-than-or-equal test for characters (char<= x y) is true if and only if the character code of x is less than or equal to that of y. See *note CHAR-CODE::. The guard for char<= specifies that its arguments are characters. Char<= is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR>, Next: CHAR>=, Prev: CHAR<=, Up: ACL2-BUILT-INS CHAR> greater-than test for characters (char> x y) is true if and only if the character code of x is greater than that of y. See *note CHAR-CODE::. The guard for char> specifies that its arguments are characters. Char> is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHAR>=, Next: CHARACTER-ALISTP, Prev: CHAR>, Up: ACL2-BUILT-INS CHAR>= greater-than-or-equal test for characters (char>= x y) is true if and only if the character code of x is greater than or equal to that of y. See *note CHAR-CODE::. The guard for char>= specifies that its arguments are characters. Char>= is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHARACTER-ALISTP, Next: CHARACTER-LISTP, Prev: CHAR>=, Up: ACL2-BUILT-INS CHARACTER-ALISTP recognizer for association lists with characters as keys (Character-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where key is a characterp.  File: acl2-doc-emacs.info, Node: CHARACTER-LISTP, Next: CHARACTERP, Prev: CHARACTER-ALISTP, Up: ACL2-BUILT-INS CHARACTER-LISTP recognizer for a true list of characters The predicate character-listp tests whether its argument is a true list of characters. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CHARACTERP, Next: CHARACTERS, Prev: CHARACTER-LISTP, Up: ACL2-BUILT-INS CHARACTERP recognizer for characters (characterp x) is true if and only if x is a character.  File: acl2-doc-emacs.info, Node: CHARACTERS, Next: CHECK-SUM, Prev: CHARACTERP, Up: ACL2-BUILT-INS CHARACTERS characters in ACL2 ACL2 accepts 256 distinct characters, which are the characters obtained by applying the function code-char to each integer from 0 to 255. Among these, Common Lisp designates certain ones as _standard characters_, namely those of the form (code-char n) where n is from 33 to 126, together with #\Newline and #\Space. The actual standard characters may be viewed by evaluating the defconst *standard-chars*. To be more precise, Common Lisp does not specify the precise relationship between code-char and the standard characters. However, we check that the underlying Common Lisp implementation uses a particular relationship that extends the usual ASCII coding of characters. We also check that Space, Tab, Newline, Page, and Rubout correspond to characters with respective char-codes 32, 9, 10, 12, and 127. Code-char has an inverse, char-code. Thus, when char-code is applied to an ACL2 character, c, it returns a number n between 0 and 255 inclusive such that (code-char n) = c. The preceding paragraph implies that there is only one ACL2 character with a given character code. CLTL allows for "attributes" for characters, which could allow distinct characters with the same code, but ACL2 does not allow this. _The Character Reader_ ACL2 supports the `#\' notation for characters provided by Common Lisp, with some restrictions. First of all, for every character c, the notation #\c may be used to denote the character object c. That is, the user may type in this notation and ACL2 will read it as denoting the character object c. In this case, the character immediately following c must be one of the following "terminating characters": a Tab, a Newline, a Page character, a space, or one of the characters: " ' ( ) ; ` , Other than the notation above, ACL2 accepts alternate notation for five characters. #\Space #\Tab #\Newline #\Page #\Rubout Again, in each of these cases the next character must be from among the set of "terminating characters" described in the single-character case. Our implementation is consistent with IS0-8859, even though we don't provide #\ syntax for entering characters other than that described above. Finally, we note that it is our intention that any object printed by ACL2's top-level-loop may be read back into ACL2. Please notify the implementors if you find a counterexample to this claim.  File: acl2-doc-emacs.info, Node: CHECK-SUM, Next: CLOSE-INPUT-CHANNEL, Prev: CHARACTERS, Up: ACL2-BUILT-INS CHECK-SUM assigning "often unique" integers to files and objects A "check sum" is an integer in some fixed range computed from the printed representation of an object, e.g., the sum, modulo 2**32, of the ascii codes of all the characters in the printed representation. Ideally, you would like the check sum of an object to be uniquely associated with that object, like a fingerprint. It could then be used as a convenient way to recognize the object in the future: you could remember the check sum (which is relatively small) and when an object is presented to you and alleged to be the special one you could compute its check sum and see if indeed it was. Alas, there are many more objects than check sums (after all, each check sum is an object, and then there's t). So you try to design a check sum algorithm that maps similar looking objects far apart, in the hopes that corruptions and counterfeits -- which appear to be similar to the object -- have different check sums. Nevertheless, the best you can do is a many-to-one map. If an object with a different check sum is presented, you can be positive it is not the special object. But if an object with the same check sum is presented, you have no grounds for positive identification. The basic check sum algorithm in ACL2 is called check-sum-obj, which computes the check sum of an ACL2 object. Roughly speaking, we scan the print representation of the object and, for each character encountered, we multiply the ascii code of the character times its position in the stream (modulo a certain prime) and then add (modulo a certain prime) that into the running sum. This is inaccurate in many senses (for example, we don't always use the ascii code and we see numbers as though they were printed in base 127) but indicates the basic idea. ACL2 uses check sums to increase security in the books mechanism; see *note CERTIFICATE::.  File: acl2-doc-emacs.info, Node: CLOSE-INPUT-CHANNEL, Next: CLOSE-OUTPUT-CHANNEL, Prev: CHECK-SUM, Up: ACL2-BUILT-INS CLOSE-INPUT-CHANNEL See *note IO::.  File: acl2-doc-emacs.info, Node: CLOSE-OUTPUT-CHANNEL, Next: CODE-CHAR, Prev: CLOSE-INPUT-CHANNEL, Up: ACL2-BUILT-INS CLOSE-OUTPUT-CHANNEL See *note IO::.  File: acl2-doc-emacs.info, Node: CODE-CHAR, Next: COERCE, Prev: CLOSE-OUTPUT-CHANNEL, Up: ACL2-BUILT-INS CODE-CHAR the character corresponding to a given numeric code Completion Axiom (completion-of-code-char): (equal (code-char x) (if (and (integerp x) (>= x 0) (< x 256)) (code-char x) (code-char 0))) Guard for (code-char x): (and (integerp x) (>= x 0) (< x 256)) ACL2 supports 8-bit characters. Inputs not between 0 and 255 are treated as 0.  File: acl2-doc-emacs.info, Node: COERCE, Next: COMPLEX, Prev: CODE-CHAR, Up: ACL2-BUILT-INS COERCE coerce a character list to a string and a string to a list Completion Axiom (completion-of-coerce): (equal (coerce x y) (cond ((equal y 'list) (if (stringp x) (coerce x 'list) nil)) (t (coerce (make-character-list x) 'string)))) Guard for (coerce x y): (if (equal y 'list) (stringp x) (if (equal y 'string) (character-listp x) nil)) Also see community book books/misc/fast-coerce.lisp, contributed by Jared Davis, for a version of coerce that may be faster for Common Lisp implementations other than CCL 1.3 or later, if the second argument is 'list (for coercing a string to a list).  File: acl2-doc-emacs.info, Node: COMPLEX, Next: COMPLEX-RATIONALP, Prev: COERCE, Up: ACL2-BUILT-INS COMPLEX create an ACL2 number Examples: (complex x 3) ; x + 3i, where i is the principal square root of -1 (complex x y) ; x + yi (complex x 0) ; same as x, for rational numbers x The function complex takes two rational number arguments and returns an ACL2 number. This number will be of type (complex rational) [as defined in the Common Lisp language], except that if the second argument is zero, then complex returns its first argument. The function complex-rationalp is a recognizer for complex rational numbers, i.e. for ACL2 numbers that are not rational numbers. The reader macro #C (which is the same as #c) provides a convenient way for typing in complex numbers. For explicit rational numbers x and y, #C(x y) is read to the same value as (complex x y). The functions realpart and imagpart return the real and imaginary parts (respectively) of a complex (possibly rational) number. So for example, (realpart #C(3 4)) = 3, (imagpart #C(3 4)) = 4, (realpart 3/4) = 3/4, and (imagpart 3/4) = 0. The following built-in axiom may be useful for reasoning about complex numbers. (defaxiom complex-definition (implies (and (real/rationalp x) (real/rationalp y)) (equal (complex x y) (+ x (* #c(0 1) y)))) :rule-classes nil) A completion axiom that shows what complex returns on arguments violating its guard (which says that both arguments are rational numbers) is the following, named completion-of-complex. (equal (complex x y) (complex (if (rationalp x) x 0) (if (rationalp y) y 0)))  File: acl2-doc-emacs.info, Node: COMPLEX-RATIONALP, Next: COMPLEX/COMPLEX-RATIONALP, Prev: COMPLEX, Up: ACL2-BUILT-INS COMPLEX-RATIONALP recognizes complex rational numbers Examples: (complex-rationalp 3) ; nil, as 3 is rational, not complex rational (complex-rationalp #c(3 0)) ; nil, since #c(3 0) is the same as 3 (complex-rationalp t) ; nil (complex-rationalp #c(3 1)) ; t, as #c(3 1) is the complex number 3 + i See *note COMPLEX:: for more about complex rationals in ACL2.  File: acl2-doc-emacs.info, Node: COMPLEX/COMPLEX-RATIONALP, Next: CONCATENATE, Prev: COMPLEX-RATIONALP, Up: ACL2-BUILT-INS COMPLEX/COMPLEX-RATIONALP recognizer for complex numbers For most ACL2 users, this is a macro abbreviating complex-rationalp; see *note COMPLEX-RATIONALP::. In ACL2(r) (see *note REAL::), a complex number x may have irrational real and imaginary parts. This macro abbreviates the predicate complexp in ACL2(r), which holds for such x. Most ACL2 users can ignore this macro and use complex-rationalp instead. Some community books use complex/complex-rationalp so that they are suitable for ACL2(r) as well.  File: acl2-doc-emacs.info, Node: CONCATENATE, Next: COND, Prev: COMPLEX/COMPLEX-RATIONALP, Up: ACL2-BUILT-INS CONCATENATE concatenate lists or strings together Examples: (concatenate 'string "ab" "cd" "ef") ; equals "abcdef" (concatenate 'string "ab") ; equals "ab" (concatenate 'list '(a b) '(c d) '(e f)) ; equals '(a b c d e f) (concatenate 'list) ; equals nil General Form: (concatenate result-type x1 x2 ... xn) where n >= 0 and either: result-type is 'string and each xi is a string; or result-type is 'list and each xi is a true list. Concatenate simply concatenates its arguments to form the result string or list. Also see *note APPEND:: and see *note STRING-APPEND::. (The latter immediately generates a call to concatenate when applied to strings.) Note: We do *not* try to comply with the Lisp language's insistence that concatenate copies its arguments. Not only are we in an applicative setting, where this issue shouldn't matter for the logic, but also we do not actually modify the underlying lisp implementation of concatenate; we merely provide a definition for it. Concatenate is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: COND, Next: CONJUGATE, Prev: CONCATENATE, Up: ACL2-BUILT-INS COND conditional based on if-then-else Cond is the construct for IF, THEN, ELSE IF, ... The test is against nil. The argument list for cond is a list of "clauses", each of which is a list. In ACL2, clauses must have length 1 or 2. Cond is a Common Lisp macro. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: CONJUGATE, Next: CONS, Prev: COND, Up: ACL2-BUILT-INS CONJUGATE complex number conjugate Conjugate takes an ACL2 number as an argument, and returns its complex conjugate (i.e., the result of negating its imaginary part.). Conjugate is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: CONS, Next: CONSP, Prev: CONJUGATE, Up: ACL2-BUILT-INS CONS pair and list constructor (cons x y) is a pair whose first component is x and second component is y. If y is a list, then (cons x y) is a list that has an addtional element x on the front.  File: acl2-doc-emacs.info, Node: CONSP, Next: COUNT, Prev: CONS, Up: ACL2-BUILT-INS CONSP recognizer for cons pairs (consp x) is true if and only if x is a cons pair.  File: acl2-doc-emacs.info, Node: COUNT, Next: CPU-CORE-COUNT, Prev: CONSP, Up: ACL2-BUILT-INS COUNT count the number of occurrences of an item in a string or true-list Example Forms: (count #\D "DabcDefcDe") ; = 3 (count #\D "DabcDefcDe" :start 1) ; = 2 (count #\D "DabcDefcDe" :start 1 :end 5) ; = 1 (count #\D "DabcDefcDe" :start 1 :end 4) ; = 0 (count #\z "DabcDefcDe") ; = 0 (count '(a b) '(17 (a b) 23 (a b) (c d))) ; = 2 General Form: (count item sequence &key start end) (Count item sequence) returns the number of times item occurs in sequence. The guard for calls of count (which is actually a macro in ACL2) specifies that sequence is a string or a true-list, and that start, which defaults to 0, and end, which defaults to the length of sequence, are valid indices into sequence. See any Common Lisp documentation for more information about count, which is a Common Lisp utility. At this time ACL2 does not support keyword arguments for count other than :start and :end; we may add support for the :from-end keyword upon request.  File: acl2-doc-emacs.info, Node: CPU-CORE-COUNT, Next: CW, Prev: COUNT, Up: ACL2-BUILT-INS CPU-CORE-COUNT the number of cpu cores This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see *note PARALLELISM::. Unless the ACL2 executable supports parallel execution (see *note PARALLELISM::), this function returns (mv 1 state). Otherwise: (Cpu-core-count state) returns (mv core-count state), where core-count is the number of cpu cores if ACL2 can get that information from the underlying Common Lisp implementation. Otherwise an error occurs, unless global 'cpu-core-count is assigned to a positive integer value (see *note ASSIGN::), in which case that value is returned as the core-count. Example: (cpu-core-count state) ==> (mv 4 state) .  File: acl2-doc-emacs.info, Node: CW, Next: CW!, Prev: CPU-CORE-COUNT, Up: ACL2-BUILT-INS CW print to the comment window Example: (cw "The goal is ~p0 and the alist is ~x1.~%" (untranslate term t nil) unify-subst) Logically, this expression is equivalent to nil. However, it has the effect of first printing to the so-called "comment window" the fmt string as indicated. Thus, cw is like fmt (see *note FMT::) except in three important ways. First, it is a macro whose calls expand to calls of a :logic mode function. Second, it neither takes nor returns the ACL2 state; logically cw simply returns nil, although it prints to a _comment window_ that just happens to share the terminal screen with the standard character output *standard-co*. Third, its fmt args are positional references, so that for example (cw "Answers: ~p0 and ~p1" ans1 ans2) prints in the same manner as: (fmt "Answers: ~p0 and ~p1" (list (cons #\0 ans1) (cons #\1 ans2)) *standard-co* state nil) Typically, calls of cw are embedded in prog2$ forms, e.g., (prog2$ (cw ...) (mv a b c)) which has the side-effect of printing to the comment window and logically returning (mv a b c). General Form: (cw fmt-string arg1 arg2 ... argn) where n is between 0 and 9 (inclusive). The macro uses fmt-to-comment-window, passing it the column 0 and evisc-tuple nil, after assembling the appropriate alist binding the fmt vars #\0 through #\9; see *note FMT::. If you want (a) more than 10 vars, (b) vars other than the digit chars, (c) a different column, or (d) a different evisc-tuple, then call fmt-to-comment-window instead. Also see *note CW!::, which is useful if you want to be able to read the printed forms back in. Finally, we discuss another way to create formatted output that also avoids the need to pass in the ACL2 state. The idea is to use wormholes; see *note WORMHOLE::. Below is a function you can write, along with some calls, providing an illustration of this approach. (defun my-fmt-to-comment-window (str alist) (wormhole 'my-fmt-to-comment-window '(lambda (whs) whs) (list str alist) '(pprogn (fms (car (@ wormhole-input)) (cadr (@ wormhole-input)) *standard-co* state nil) (value :q)) :ld-verbose nil :ld-error-action :return ; harmless return on error :ld-prompt nil)) ; A non-erroneous call: (my-fmt-to-comment-window "Here is ~x0 for your inspection~%" (list (cons #\0 'foo))) ; An error inside the fmt string (unbound fmt var); note that even ; with the error, the wormhole is exited. (my-fmt-to-comment-window "Here is ~x1 for your inspection~%" (list (cons #\0 'foo))) ; A guard violation in the binding; note that even with the error, ; the wormhole is exited. (my-fmt-to-comment-window "Here is ~x0 for your inspection~%" (list (cons #\0 (car 'foo))))  File: acl2-doc-emacs.info, Node: CW!, Next: DELETE-ASSOC, Prev: CW, Up: ACL2-BUILT-INS CW! print to the comment window This is the same as cw, except that cw inserts backslash (\) characters when forced to print past the right margin, in order to make the output a bit clearer in that case. Use cw! instead if you want to be able to read the forms back in.  File: acl2-doc-emacs.info, Node: DELETE-ASSOC, Next: DELETE-ASSOC-EQ, Prev: CW!, Up: ACL2-BUILT-INS DELETE-ASSOC remove the first pair from an association list for a given key General Forms: (delete-assoc key alist) (delete-assoc key alist :test 'eql) ; same as above (eql as equality test) (delete-assoc key alist :test 'eq) ; same, but eq is equality test (delete-assoc key alist :test 'equal) ; same, but equal is equality test (Delete-assoc key alist) returns an alist that is the same as the list alist, except that the first pair in alist with a car of key is deleted, if there is one; otherwise alist is returned. Note that the order of the elements of alist is unchanged (though one may be deleted). The guard for a call of delete-assoc depends on the test. In all cases, the second argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-alistp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between delete-assoc and its variants: (delete-assoc-eq key alist) is equivalent to (delete-assoc key alist :test 'eq); (delete-assoc-equal key alist) is equivalent to (delete-assoc key alist :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function delete-assoc-equal.  File: acl2-doc-emacs.info, Node: DELETE-ASSOC-EQ, Next: DELETE-ASSOC-EQUAL, Prev: DELETE-ASSOC, Up: ACL2-BUILT-INS DELETE-ASSOC-EQ See *note DELETE-ASSOC::.  File: acl2-doc-emacs.info, Node: DELETE-ASSOC-EQUAL, Next: DENOMINATOR, Prev: DELETE-ASSOC-EQ, Up: ACL2-BUILT-INS DELETE-ASSOC-EQUAL See *note DELETE-ASSOC::.  File: acl2-doc-emacs.info, Node: DENOMINATOR, Next: DIGIT-CHAR-P, Prev: DELETE-ASSOC-EQUAL, Up: ACL2-BUILT-INS DENOMINATOR divisor of a ratio in lowest terms Completion Axiom (completion-of-denominator): (equal (denominator x) (if (rationalp x) (denominator x) 1)) Guard for (denominator x): (rationalp x)  File: acl2-doc-emacs.info, Node: DIGIT-CHAR-P, Next: DIGIT-TO-CHAR, Prev: DENOMINATOR, Up: ACL2-BUILT-INS DIGIT-CHAR-P the number, if any, corresponding to a given character (digit-char-p ch) is the integer corresponding to the character ch in base 10. For example, (digit-char-p #\3) is equal to the integer 3. More generally, an optional second argument specifies the radix (default 10, as indicated above). The guard for digit-char-p (more precisely, for the function our-digit-char-p that calls of this macro expand to) requires its second argument to be an integer between 2 and 36, inclusive, and its first argument to be a character. Digit-char-p is a Common Lisp function, though it is implemented in the ACL2 logic as an ACL2 macro. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: DIGIT-TO-CHAR, Next: E0-ORD-<, Prev: DIGIT-CHAR-P, Up: ACL2-BUILT-INS DIGIT-TO-CHAR map a digit to a character Example: ACL2 !>(digit-to-char 8) #\8 For an integer n from 0 to 15, (digit-to-char n) is the character corresponding to n in hex notation, using uppercase letters for digits exceeding 9. If n is in the appropriate range, that result is of course also the binary, octal, and decimal digit. The guard for digit-to-char requires its argument to be an integer between 0 and 15, inclusive. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: E0-ORD-<, Next: E0-ORDINALP, Prev: DIGIT-TO-CHAR, Up: ACL2-BUILT-INS E0-ORD-< the old ordering function for ACL2 ordinals See *note O<:: for the current new ordering function for ACL2 ordinals. The functions e0-ordinalp and e0-ord-< were replaced in ACL2 Version_2.8 by o-p and o<, respectively. However, books created before that version used the earlier functions for termination proofs; the old functions might be of use in these cases. To use the old functions in termination proofs, include the community book books/ordinals/e0-ordinal and execute the event (set-well-founded-relation e0-ord-<) (see *note SET-WELL-FOUNDED-RELATION::). For a more thorough discussion of these functions, see the documentation at the end of community book books/ordinals/e0-ordinal.lisp.  File: acl2-doc-emacs.info, Node: E0-ORDINALP, Next: EC-CALL, Prev: E0-ORD-<, Up: ACL2-BUILT-INS E0-ORDINALP the old recognizer for ACL2 ordinals See *note O-P:: for the current recognizer for ACL2 ordinals. The functions e0-ordinalp and e0-ord-< were replaced in ACL2 Version_2.8 by o-p and o<, respectively. However, books created before that version used the earlier functions for termination proofs; the old functions might be of use in these cases. To use the old functions in termination proofs, include the community book books/ordinals/e0-ordinal and execute the event (set-well-founded-relation e0-ord-<) (see *note SET-WELL-FOUNDED-RELATION::). For a more thorough discussion of these functions, see the documentation at the end of community book books/ordinals/e0-ordinal.lisp.  File: acl2-doc-emacs.info, Node: EC-CALL, Next: EIGHTH, Prev: E0-ORDINALP, Up: ACL2-BUILT-INS EC-CALL execute a call in the ACL2 logic instead of raw Lisp The name "ec-call" represents "executable-counterpart call." This utility is intended for users who are familiar with guards. See *note GUARD:: for a general discussion of guards. Logically, ec-call behaves like the identity macro; during proofs, (ec-call TERM) is typically replaced quickly by TERM during a proof attempt. However, ec-call causes function calls to be evaluated in the ACL2 logic rather than raw Lisp, as explained below. General Form: (ec-call (fn term1 ... termk)) where fn is a known function symbol other than those in the list that is the value of the constant *ec-call-bad-ops*. In particular, fn is not a macro. Semantically, (ec-call (fn term1 ... termk)) equals (fn term1 ... termk). However, this use of ec-call has two effects. (1) Guard verification generates no proof obligations from the guard of fn for this call. Indeed, guards need not have been verified for fn. (2) During evaluation, after the arguments of fn are evaluated as usual, the executable counterpart of fn is called, rather than fn as defined in raw Lisp. That is, the call of fn is made on its evaluated arguments as though this call is being made in the ACL2 top-level loop, rather than in raw Lisp. In particular, the guard of fn is checked, at least by default (see *note SET-GUARD-CHECKING::). Note that in the term (ec-call (fn term1 ... termk)), only the indicated call of fn is made in the logic; each termi is evaluated in the normal manner. If you want an entire term evaluated in the logic, wrap ec-call around each function call in the term (other than calls of if and ec-call). *Technical Remark* (probably best ignored). During evaluation of a call of defconst or defpkg in raw Lisp, a form (ec-call (fn term1 ... termk)) is treated as (fn term1 ... termk), that is, without calling the executable counterpart of fn. This situation occurs when loading a compiled file (or expansion file) on behalf of an include-book event. The reason is technical: executable counterparts are defined below a book's events in the book's compiled file. End of Technical Remark. Here is a small example. We define foo recursively but with guard verification inhibited on the recursive call, which is to be evaluated in the ACL2 logic. ACL2 !>(defun foo (x y) (declare (xargs :guard (consp y))) (if (consp x) (cons (car x) (ec-call (foo (cdr x) (cdr y)))) (car y))) The admission of FOO is trivial, using the relation O< (which is known to be well-founded on the domain recognized by O-P) and the measure (ACL2-COUNT X). We could deduce no constraints on the type of FOO. Computing the guard conjecture for FOO.... The guard conjecture for FOO is trivial to prove. FOO is compliant with Common Lisp. Summary Form: ( DEFUN FOO ...) Rules: NIL Warnings: None Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !>(foo '(2 3 4 5) '(6 7)) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X Y), which is (CONSP Y), is violated by the arguments in the call (FOO '(4 5) NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> The error above arises because eventually, foo recurs down to a value of parameter y that violates the guard. This is clear from tracing (see *note TRACE$:: and see *note TRACE::). Each call of the executable counterpart of foo (the so-called "*1*" function for foo) checks the guard and then invokes the raw Lisp version of foo. The raw Lisp version calls the executable counterpart on the recursive call. When the guard check fails we get a violation. ACL2 !>(trace$ foo) ((FOO)) ACL2 !>(foo '(2 3 4 5) '(6 7)) 1> (ACL2_*1*_ACL2::FOO (2 3 4 5) (6 7)) 2> (FOO (2 3 4 5) (6 7)) 3> (ACL2_*1*_ACL2::FOO (3 4 5) (7)) 4> (FOO (3 4 5) (7)) 5> (ACL2_*1*_ACL2::FOO (4 5) NIL) ACL2 Error in TOP-LEVEL: The guard for the function call (FOO X Y), which is (CONSP Y), is violated by the arguments in the call (FOO '(4 5) NIL). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. See :DOC set-guard-checking for information about suppressing this check with (set-guard-checking :none), as recommended for new users. ACL2 !> If we turn off guard errors then we can see the trace as above, but where we avoid calling the raw Lisp function when the guard fails to hold. ACL2 !>:set-guard-checking nil Masking guard violations but still checking guards except for self- recursive calls. To avoid guard checking entirely, :SET-GUARD-CHECKING :NONE. See :DOC set-guard-checking. ACL2 >(foo '(2 3 4 5) '(6 7)) 1> (ACL2_*1*_ACL2::FOO (2 3 4 5) (6 7)) 2> (FOO (2 3 4 5) (6 7)) 3> (ACL2_*1*_ACL2::FOO (3 4 5) (7)) 4> (FOO (3 4 5) (7)) 5> (ACL2_*1*_ACL2::FOO (4 5) NIL) 6> (ACL2_*1*_ACL2::FOO (5) NIL) 7> (ACL2_*1*_ACL2::FOO NIL NIL) <7 (ACL2_*1*_ACL2::FOO NIL) <6 (ACL2_*1*_ACL2::FOO (5)) <5 (ACL2_*1*_ACL2::FOO (4 5)) <4 (FOO (3 4 5)) <3 (ACL2_*1*_ACL2::FOO (3 4 5)) <2 (FOO (2 3 4 5)) <1 (ACL2_*1*_ACL2::FOO (2 3 4 5)) (2 3 4 5) ACL2 >  File: acl2-doc-emacs.info, Node: EIGHTH, Next: ENDP, Prev: EC-CALL, Up: ACL2-BUILT-INS EIGHTH eighth member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: ENDP, Next: EQ, Prev: EIGHTH, Up: ACL2-BUILT-INS ENDP recognizer for empty lists In the ACL2 logic, (endp x) is the same as (atom x). See *note ATOM::. Unlike atom, the guard for endp requires that x is a cons pair or is nil. Thus, endp is typically used as a termination test for functions that recur on a true-listp argument. See *note GUARD:: for general information about guards. Endp is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: EQ, Next: EQL, Prev: ENDP, Up: ACL2-BUILT-INS EQ equality of symbols Eq is the function for determining whether two objects are identical (i.e., have the exact same store address in the current von Neumann implementation of Common Lisp). It is the same as equal in the ACL2 logic. Eq is a Common Lisp function. In order to ensure conformance with Common Lisp, the ACL2 guard on eq requires at least one of the arguments to eq to be a symbol. Common Lisp guarantees that if x is a symbol, then x is eq to y if and only if x is equal to y. Thus, the ACL2 user should think of eq as nothing besides a fast means for checking equal when one argument is known to be a symbol. In particular, it is possible that an eq test will not even require the cost of a function call but will be as fast as a single machine instruction.  File: acl2-doc-emacs.info, Node: EQL, Next: EQLABLE-ALISTP, Prev: EQ, Up: ACL2-BUILT-INS EQL test equality (of two numbers, symbols, or characters) (eql x y) is logically equivalent to (equal x y). Unlike equal, eql has a guard requiring at least one of its arguments to be a number, a symbol, or a character. Generally, eql is executed more efficiently than equal. For a discussion of the various ways to test against 0, See *note ZERO-TEST-IDIOMS::. Eql is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: EQLABLE-ALISTP, Next: EQLABLE-LISTP, Prev: EQL, Up: ACL2-BUILT-INS EQLABLE-ALISTP recognizer for a true list of pairs whose cars are suitable for eql The predicate eqlable-alistp tests whether its argument is a true-listp of consp objects whose cars all satisfy eqlablep. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: EQLABLE-LISTP, Next: EQLABLEP, Prev: EQLABLE-ALISTP, Up: ACL2-BUILT-INS EQLABLE-LISTP recognizer for a true list of objects each suitable for eql The predicate eqlable-listp tests whether its argument is a true-listp of objects satisfying eqlablep. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: EQLABLEP, Next: EQUAL, Prev: EQLABLE-LISTP, Up: ACL2-BUILT-INS EQLABLEP the guard for the function eql The predicate eqlablep tests whether its argument is suitable for eql, at least one of whose arguments must satisfy this predicate in Common Lisp. (Eqlablep x) is true if and only if its argument is a number, a symbol, or a character. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: EQUAL, Next: ER, Prev: EQLABLEP, Up: ACL2-BUILT-INS EQUAL true equality (equal x y) is equal to t or nil, according to whether or not x and y are the same value. For a discussion of the various idioms for testing against 0, See *note ZERO-TEST-IDIOMS::.  File: acl2-doc-emacs.info, Node: ER, Next: ER-PROGN, Prev: EQUAL, Up: ACL2-BUILT-INS ER print an error message and "cause an error" See *note FMT:: for a general discussion of formatted printing in ACL2. All calls of er print formatted strings, just as is done by fmt. Example Forms: (er hard 'top-level "Illegal inputs, ~x0 and ~x1." a b) (er hard? 'top-level "Illegal inputs, ~x0 and ~x1." a b) (er soft 'top-level "Illegal inputs, ~x0 and ~x1." a b) The examples above all print an error message to standard output saying that a and b are illegal inputs. However, the first two abort evaluation after printing an error message (while logically returning nil, though in ordinary evaluation the return value is never seen); while the third returns (mv t nil state) after printing an error message. The result in the third case can be interpreted as an "error" when programming with the ACL2 state, something most ACL2 users will probably not want to do unless they are building systems of some sort; see *note PROGRAMMING-WITH-STATE::. If state is not available in the current context then you will probably want to use (er hard ...) or (er hard? ...) to cause an error; for example, if you are returning two values, you may write (mv (er hard ...) nil). The difference between the hard and hard? forms is one of guards. Use hard if you want the call to generate a (clearly impossible) guard proof obligation of (essentially) NIL. But use hard? if you want to be able to call this function in guard-verified code, since the call generates a (trivially satisfied) guard proof obligation of T. Er is a macro, and the above three examples expand to calls of ACL2 functions, as shown below. See *note ILLEGAL::, see *note HARD-ERROR::, and see *note ERROR1::. The first two have guards of (essentially) NIL and T, respectively, while error1 is in :program mode. General forms: (er hard ctx fmt-string arg1 arg2 ... argk) ==> {macroexpands, in essence, to:} (ILLEGAL CTX FMT-STRING (LIST (CONS #\0 ARG1) (CONS #\1 ARG2) ... (CONS #\k ARGk))) (er hard? ctx fmt-string arg1 arg2 ... argk) ==> {macroexpands, in essence, to:} (HARD-ERROR CTX FMT-STRING (LIST (CONS #\0 ARG1) (CONS #\1 ARG2) ... (CONS #\k ARGk))) (er soft ctx fmt-string arg1 arg2 ... argk) ==> {macroexpands, in essence, to:} (ERROR1 CTX FMT-STRING (LIST (CONS #\0 ARG1) (CONS #\1 ARG2) ... (CONS #\k ARGk)))  File: acl2-doc-emacs.info, Node: ER-PROGN, Next: ERROR1, Prev: ER, Up: ACL2-BUILT-INS ER-PROGN perform a sequence of state-changing "error triples" Example: (er-progn (check-good-foo-p (f-get-global 'my-foo state) state) (value (* (f-get-global 'my-foo state) (f-get-global 'bar state)))) This sequencing primitive is only useful when programming with state, something that very few users will probably want to do. See *note STATE::. Er-progn is used much the way that progn is used in Common Lisp, except that it expects each form within it to evaluate to an "error triple" of the form (mv erp val state); see *note ERROR-TRIPLES::. The first such form, if any, that evaluates to such a triple where erp is not nil yields the error triple returned by the er-progn. If there is no such form, then the last form returns the value of the er-progn form. General Form: (er-progn ... ) where each is an expression that evaluates to an error triple (see *note PROGRAMMING-WITH-STATE::). The above form is essentially equivalent to the following ("essentially" because in fact, care is taken to avoid variable capture). (mv-let (erp val state) (cond (erp (mv erp val state)) (t (mv-let (erp val state) (cond (erp (mv erp val state)) (t ... (mv-let (erp val state) (cond (erp (mv erp val state)) (t )))))))))  File: acl2-doc-emacs.info, Node: ERROR1, Next: EVENP, Prev: ER-PROGN, Up: ACL2-BUILT-INS ERROR1 print an error message and cause a "soft error" (Error1 ctx str alist) returns (mv t nil state). An error message is first printed using the the "context" ctx, as well as the string str and alist alist that are of the same kind as expected by fmt. See *note FMT::. Error1 can be interpreted as causing an "error" when programming with the ACL2 state, something most ACL2 users will probably not want to do; see *note LD-ERROR-TRIPLES:: and see *note ER-PROGN::. In order to cause errors with :logic mode functions, see *note HARD-ERROR:: and see *note ILLEGAL::. Better yet, see *note ER:: for a macro that provides a unified way of signaling errors. As mentioned above, error1 always returns (mv t nil state). But if a call (error1 ctx str alist) is encountered during evaluation, then the string str is first printed using the association list alist (as in fmt). Here is a trivial, contrived example. ACL2 !>(error1 'my-context "Printing 4: ~n0" (list (cons #\0 4)) state) ACL2 Error in MY-CONTEXT: Printing 4: four ACL2 !>  File: acl2-doc-emacs.info, Node: EVENP, Next: EXPLODE-NONNEGATIVE-INTEGER, Prev: ERROR1, Up: ACL2-BUILT-INS EVENP test whether an integer is even (evenp x) is true if and only if the integer x is even. Actually, in the ACL2 logic (evenp x) is defined to be true when x/2 is an integer. The guard for evenp requires its argument to be an integer. Evenp is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: EXPLODE-NONNEGATIVE-INTEGER, Next: EXPT, Prev: EVENP, Up: ACL2-BUILT-INS EXPLODE-NONNEGATIVE-INTEGER the list of characters in the radix-r form of a number Examples: ACL2 !>(explode-nonnegative-integer 925 10 nil) (#9 #2 #5) ACL2 !>(explode-nonnegative-integer 325 16 nil) (#3 #9 #D) For a non-negative integer n, (explode-nonnegative-integer n r nil) is the list of characters in the radix-r (base-r) representation of n. The guard for explode-nonnegative-integer requires the first argument to be a nonnegative integer and second argument to be a valid radix for ACL2 (2, 8, 10, or 16). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: EXPT, Next: F-GET-GLOBAL, Prev: EXPLODE-NONNEGATIVE-INTEGER, Up: ACL2-BUILT-INS EXPT exponential function (Expt r i) is the result of raising the number r to the integer power i. The guard for (expt r i) is that r is a number and i is an integer, and furthermore, if r is 0 then i is nonnegative. When the type requirements of the guard aren't met, (expt r i) first coerces r to a number and i to an integer. Expt is a Common Lisp function. See any Common Lisp documentation for more information. Note that r can be a complex number; this is consistent with Common lisp. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: F-GET-GLOBAL, Next: F-PUT-GLOBAL, Prev: EXPT, Up: ACL2-BUILT-INS F-GET-GLOBAL get the value of a global variable in state Examples: (+ (f-get-global 'y state) 1) (f-put-global 'a (aset1 'ascii-map-array (f-get-global 'a state) 66 'Upper-case-B) state) General Form: (f-get-global 'symbol state) where symbol is any symbol to which you have assigned a global value. The macro @ is closely related to f-get-global: (@ var) macroexpands to (f-get-global 'var state). The macro f-get-global makes it convenient to set the value of a symbol. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.  File: acl2-doc-emacs.info, Node: F-PUT-GLOBAL, Next: FIFTH, Prev: F-GET-GLOBAL, Up: ACL2-BUILT-INS F-PUT-GLOBAL assign to a global variable in state Examples: (f-put-global 'x (expt 2 10) state) (f-put-global 'a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B) state) General Form: (f-put-global (quote symbol) term state) where symbol is any symbol (with certain enforced exclusions to avoid overwriting ACL2 system "globals") and term is any ACL2 term that could be evaluated at the top-level. F-put-global evaluates the term, stores the result as the value of the given symbol in the global-table of state, and returns the new state. (Note: the actual implementation of the storage of this value is much more efficient than this discussion of the logic might suggest.) The macro assign is closely related to f-put-global: (assign var val) macroexpands to (f-put-global 'var val state). The macros @ and f-get-global give convenient access to the value of such globals. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.  File: acl2-doc-emacs.info, Node: FIFTH, Next: FIRST, Prev: F-PUT-GLOBAL, Up: ACL2-BUILT-INS FIFTH fifth member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: FIRST, Next: FIX, Prev: FIFTH, Up: ACL2-BUILT-INS FIRST first member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: FIX, Next: FIX-TRUE-LIST, Prev: FIRST, Up: ACL2-BUILT-INS FIX coerce to a number Fix simply returns any numeric argument unchanged, returning 0 on a non-numeric argument. Also see *note NFIX::, see *note IFIX::, and see *note RFIX:: for analogous functions that coerce to a natural number, an integer, and a rational number, respectively. Fix has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: FIX-TRUE-LIST, Next: FLET, Prev: FIX, Up: ACL2-BUILT-INS FIX-TRUE-LIST coerce to a true list Fix-true-list is the identity function on true-listp objects. It converts every list to a true list by dropping the final cdr, and it converts every atom to nil. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: FLET, Next: FLOOR, Prev: FIX-TRUE-LIST, Up: ACL2-BUILT-INS FLET local binding of function symbols Example Form: ; The following evaluates to (mv 7 10): (flet ((f (x) (+ x 3)) (g (x) (declare (type integer x)) (* x 2))) (mv (f 4) (g 5))) General Forms: (flet (def1 ... defk) body) (flet (def1 ... defk) declare-form1 .. declare-formk body) where body is a term, and each defi is a definition as in defun but with the leading defun symbol omitted. See *note DEFUN::. If any declare-formi are supplied, then each must be of the form (declare decl1 ... decln), where each decli is of the form (inline g1 ... gm) or (notinline g1 ... gm), and each gi is defined by some defi. The only effect of the declarations is to provide advice to the host Lisp compiler. The declarations are otherwise ignored by ACL2, so we mainly ignore them in the discussion below. The innermost flet-binding of a function symbol, f, above a call of f, is the one that provides the definition of f for that call. Note that flet does not provide recursion. Consider the following example. ; Give a global definition of f: (defun f (x) (+ x 3)) ; Evaluate an expression using a local binding of f: (flet ((f (x) (cons x (f (1+ x))))) (f 4)) In the above term (cons x (f (1+ x))), f refers to the global definition of f above the flet expression. However, (f 4) refers to the flet-binding of f, (f (x) (cons x (f x))). The result of the flet expression is thus obtained by evaluating (f 4) where (f 4) is (cons 4 (f (1+ 4))), where the latter call of f refers to the global definition; thus we have (cons 4 (f 5)), which evaluates to (4 . 8). Although flet behaves in ACL2 essentially as it does in Common Lisp, ACL2 imposes the following restrictions and qualifications. o Every declare form for a local definition (def1 through defk, above) must be an ignore, ignorable, or type expression. o Each defi must bind a different function symbol. o Each defi must bind a symbol that is a legal name for an ACL2 function symbol. In particular, the symbol may not be in the keyword package or the main Lisp package. Moreover, the symbol may not be a built-in ACL2 function or macro. o Every variable occurring in the body of a defi must be a formal parameter of that defi. (This restriction is not enforced in Common Lisp. If the restriction is inconvenient for you, the ACL2 implementors may be able to remove it, with some effort, if you ask.) o If the flet-binding defi is in the body of a function f, then the stobj inputs for defi are implicitly those of its inputs that are declared stobj inputs of f. Flet bindings are evaluated in parallel. Consider the following example. (defun f (x) x) (flet ((f (x) (cons x x)) (g (x) (f x))) (g 3)) The binding of g refers to the global value of f, not the flet-binding of f. Thus, the flet expression evaluates to 3. Compare the flet expression above to the following one, which instead evaluates to (3 . 3). (defun f (x) x) (flet ((f (x) (cons x x))) (flet ((g (x) (f x))) (g 3))) Under the hood, ACL2 translates flet bindings to lambda expressions (see *note TERM::), throwing away the inline and notinline declarations (if any). The following example illustrates this point. ACL2 !>:trans (flet ((f (x) (cons x x)) (g (x y) (+ x y))) (declare (inline f)) (f (g 3 4))) ((LAMBDA (X) (CONS X X)) ((LAMBDA (X Y) (BINARY-+ X Y)) '3 '4)) => * ACL2 !> Flet is part of Common Lisp. See any Common Lisp documentation for more information. We conclude by pointing out an important aspect of flet shared by ACL2 and Common Lisp: The binding is lexical, not dynamic. That is, the flet binding of a function symbol only applies to calls of that function symbol in the body of the flet, not other calls made in the course of evaluation. Consider the following example. Suppose we define: (defun f (x) x) (defun g (x) x) (defun h (x) (flet ((f (x) (cons x x))) (g x))) Then evaluation of (h 3) results in 3, not in the cons pair (3 . 3), because the flet binding of f only applies to calls of f that appear in the body of that flet. In this case, only g is called in the body of that flet.  File: acl2-doc-emacs.info, Node: FLOOR, Next: FMS, Prev: FLET, Up: ACL2-BUILT-INS FLOOR division returning an integer by truncating toward negative infinity Example Forms: ACL2 !>(floor 14 3) 4 ACL2 !>(floor -14 3) -5 ACL2 !>(floor 14 -3) -5 ACL2 !>(floor -14 -3) 4 ACL2 !>(floor -15 -3) 5 (Floor i j) returns the result of taking the quotient of i and j and returning the greatest integer not exceeding that quotient. For example, the quotient of -14 by 3 is -4 2/3, and the largest integer not exceeding that rational number is -5. The guard for (floor i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero. Floor is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 floor function returns only a single value, To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: FMS, Next: FMS!, Prev: FLOOR, Up: ACL2-BUILT-INS FMS :(str alist co-channel state evisc) => state See *note FMT:: for further explanation, including documentation of the tilde-directives.  File: acl2-doc-emacs.info, Node: FMS!, Next: FMS!-TO-STRING, Prev: FMS, Up: ACL2-BUILT-INS FMS! :(str alist co-channel state evisc) => state This function is nearly identical to fms; see *note FMS::. The only difference is that fms may insert backslash (\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use fms! instead if you want to be able to read the forms back in.  File: acl2-doc-emacs.info, Node: FMS!-TO-STRING, Next: FMS-TO-STRING, Prev: FMS!, Up: ACL2-BUILT-INS FMS!-TO-STRING See *note PRINTING-TO-STRINGS::.  File: acl2-doc-emacs.info, Node: FMS-TO-STRING, Next: FMT, Prev: FMS!-TO-STRING, Up: ACL2-BUILT-INS FMS-TO-STRING See *note PRINTING-TO-STRINGS::.  File: acl2-doc-emacs.info, Node: FMT, Next: FMT!, Prev: FMS-TO-STRING, Up: ACL2-BUILT-INS FMT formatted printing ACL2 provides the functions fmt, fmt1, and fms as substitutes for Common Lisp's format function. Also see *note FMT!::, see *note FMT1!::, and see *note FMS!:: for versions of these functions that write forms to files in a manner that allows them to be read, by avoiding using backslash (\) to break long lines. There are also analogues of these functions that return a string without taking state as an argument; see *note PRINTING-TO-STRINGS::. All three print a given string under an alist pairing character objects with values, interpreting certain "tilde-directives" in the string. Channel must be a character output channel (e.g., *standard-co*). General Forms: result (fms string alist channel state evisc-tuple) ; state (fmt string alist channel state evisc-tuple) ; (mv col state) (fmt1 string alist column channel state evisc-tuple) ; (mv col state) Fms and fmt print an initial newline to put channel in column 0; Fmt1 requires the current column as input. Columns are numbered from 0. The current column is the column into which the next character will be printed. (Thus, the current column number is also the number of characters printed since the last newline.) The col returned by fmt and fmt1 is the current column at the conclusion of the formatting. Evisc-tuple must be either nil (meaning no abbreviations are used when objects are printed) or an "evisceration tuple"; see *note EVISC-TUPLE::. We list the tilde-directives below. The notation is explained after the chart. ~xx pretty print vx (maybe after printing a newline) ~yx pretty print vx starting in current column; end with newline ~Xxy like ~xx but use vy as the evisceration tuple ~Yxy like ~yx but use vy as the evisceration tuple ~@x if vx is a string, "str", recursively format "str" if vx is ("str" . a), recursively format "str" under a+ ~#x~[...~/...~/ ... ~/...~] cases on vx ^ ^ ... ^ if 0<=vx<=k, choose vxth alternative 0 1 ... k if vx is a list of length 1, case 0; else 1 ~*x iterator: vx must be of the form ("str0" "str1" "str2" "str3" lst . a); if lst is initially empty, format "str0" under a+; otherwise, bind #\* successively to the elements of lst and then recursively format "stri" under a+, where i=1 if there is one element left to process, i=2 if there are two left, and i=3 otherwise. ~&x print elements of vx with ~x, separated by commas and a final ``and'' ~vx print elements of vx with ~x, separated by commas and a final ``or'' ~nx if vx is a small positive integer, print it as a word, e.g., seven; if vx is a singleton containing a small positive integer, print the corresponding ordinal as a word, e.g., seventh ~Nx like ~nx but the word is capitalized, e.g., Seven or Seventh ~tx tab out to column vx; newline first if at or past column vx ~cx vx is (n . w), print integer n right justified in field of width w ~fx print object vx flat over as many lines as necessary ~Fx same as ~f, except that subsequent lines are indented to start one character to the right of the first character printed ~sx if vx is a symbol, print vx, breaking on hyphens (unless the symbol would normally be printed with surrounding vertical bar characters (|), in which case print as with ~fx); if vx is a string, print the characters in it, breaking on hyphens; else vx is a number, to be printed using the current print-base and print-radix ~ tilde space: print a space ~_x print vx spaces ~ tilde newline: skip following whitespace ~% output a newline ~| output a newline unless already on left margin ~~ print a tilde ~- if close to rightmargin, output a hyphen and newline; else skip this char If x is a character, then vx is the value of #\x under the current alist. Consider for example the discussion above for ~y, "~yx pretty print vx", applied to the following expression: (fmt "HELLO ~y7" (list (cons #\7 'world)) *standard-co* state nil). Then in this example: #\x is 7; and vx is the value of character #\7 under the given alist, which is the symbol, WORLD. Thus, ACL2 will print HELLO WORLD. When we say "format str under a+" we mean: process the given string under an alist obtained by appending a to the current alist. The following example illustrates how this works. ACL2 !>(fms "~@0" (list (cons #\0 (cons "~x0 ~@1" (list (cons #\0 'abc)))) (cons #\1 "-- and now: ~x0 again~%")) *standard-co* state nil) ABC -- and now: ABC again ACL2 !> Note: ~p, ~q, ~P, and ~Q are also currently supported, but are deprecated. These are respectively the same as ~x, ~y, ~X, and ~Y, except that their arguments are expected to be terms, preferably untranslated (user-level) terms, that could be printed using infix notation in certain environments. Infix printing is not currently supported but may be if there is sufficient need for it. ACL2's formatting functions print to the indicated channel, keeping track of which column they are in. Fmt1 can be used if the caller knows which column the channel is in (i.e., how many characters have been printed since the last newline). Otherwise, fmt or fms must be used, both of which output a newline so as to establish the column position at 0. Unlike Common Lisp's format routine, fmt and its relatives break the output into lines so that, by default, an attempt is made to avoid printing past column 77 (the value of constant *fmt-hard-right-margin-default*). See *note SET-FMT-HARD-RIGHT-MARGIN:: for a discussion of how linebreaks are inserted and how to change the relevant default settings. The formatting functions scan the string from left to right, printing each successive character unless it is a tilde (~). Upon encountering tildes the formatters take action determined by the character or characters immediately following the tilde. The typical tilde-directive is a group of three successive characters from the string being printed. For example, ~x0 is a 3 character tilde-directive. The first character in a tilde-directive is always the tilde character itself. The next character is called the "command" character. The character after that is usually taken as the name of a "format variable" that is bound in the alist under which the string is being printed. Format variables are, by necessity, characters. The objects actually printed by a tilde-directive are the objects obtained by looking up the command's format variables in the alist. Typical format variable names are 0, 1, 2, ..., 9, a, b, c, etc., and if a tilde-directive uses the format variable 0, as in ~x0, then the character #\0 must be bound in the alist. Some tilde commands take no arguments and others take more than one, so some directives are of length two and others are longer. It should be noted that this use of characters in the string to denote arguments is another break from Common Lisp's format routine. In Common Lisp, the directives refer implicitly to the "next item to be printed." But in ACL2 the directives name each item explicitly with our format variables. The following text contains examples that can be evaluated. To make this process easier, we use a macro which is defined as part of ACL2 just for this documentation. The macro is named fmx and it takes up to eleven arguments, the first of which is a format string, str, and the others of which are taken as the values of format variables. The variables used are #\0 through #\9. The macro constructs an appropriate alist, a, and then evaluates (fmt str a *standard-co* state nil). Thus, (fmx "Here is v0, ~x0, and here is v1, ~x1." (cons 'value 0) (cons 'value 1)) is just an abbreviation for (fmt "Here is v0, ~x0, and here is v1, ~x1." (list (cons #\0 (cons 'value 0)) (cons #\1 (cons 'value 1))) *standard-co* state nil) which returns (mv 53 state) after printing the line Here is v0, (VALUE . 0), and here is v1, (VALUE . 1). We now devote special attention to three of the tilde-directives whose use is non-obvious. _The Case Statement_ ~#x is essentially a "case statement" in the language of fmt. The proper form of the statement is ~#x~[case-0~/case-1~/ ... ~/case-k~], where each of the case-i is a format string. In the most common use, the variable x has an integer value, vx, between 0 and k, inclusive. The effect of formatting the directive is to format case-vx. For example (fmx "Go ~#0~[North~/East~/South~/West~].~%" 1) will print "Go East." followed by a newline and will return (mv 0 state), while if you change the 1 above to 3 (the maximum legal value), it will print "Go West." In order to make it easier to print such phrases as "there are seven cases" requiring agreement between subject and verb based on the number of elements of a list, the case statement allows its variable to take a list as its value and selects case-0 if the list has length 1 and case-1 otherwise. (let ((cases '(a b c))) (fmx "There ~#0~[is ~n1 case~/are ~n1 cases~]." cases (length cases))) will print "There are three cases." but if you change the '(a b c) above simply to '(a) it will print "There is one case." and if you change it to nil it will print "There are zero cases." _Indirection_ Roughly speaking, ~@ will act as though the value of its argument is a format string and splice it into the current string at the current position. It is often used when the phrase to be printed must be computed. For example, (let ((ev 'DEFUN)) (fmx "~x0 is an event~@1." 'foo (if (member-eq ev '(defun defstub encapsulate)) " that may introduce a function symbol" ""))) will print "foo is an event that may introduce a function symbol," but if the value of ev is changed from 'defun to 'defthm, it prints "foo is an event." The ~@ directive "splices" in the computed phrase (which might be empty). Of course, this particular example could be done with the case statement ~#1~[~/ that may introduce a function symbol~] where the value of #\1 is appropriately computed to be 0 or 1. If the argument to ~@ is a pair, it is taken to be a format string consed onto an alist, i.e., ("str" . a), and the alist, a, is used to extend the current one before "str" is recursively processed. This feature of fmt can be used to pass around "phrases" that contain computed contextual information in a. The most typical use is as "error messages." For example, suppose you are writing a function which does not have access to state and so cannot print an error message. It may nevertheless be necessary for it to signal an error to its caller, say by returning two results, the first of which is interpreted as an error message if non-nil. Our convention is to use a ~@ pair to represent such messages. For example, the error value might be produced by the code: (cons "Error: The instruction ~x0 is illegal when the stack is ~x1.~%" (list (cons #\0 (current-instruction st)) (cons #\1 (i-stack st)))) If the current-instruction and i-stack (whatever they are) are '(popi 3) and '(a b) when the cons above is evaluated, then it produces '("Error: The instruction ~x0 is illegal when the stack is ~x1.~%" (#\0 POPI 3) (#\1 A B)) and if this pair is made the value of the fmt variable 0, then ~@0 will print Error: The instruction (POPI 3) is illegal when the stack is (A B). For example, evaluate (let ((pair '("Error: The instruction ~x0 is illegal when the stack is ~x1.~%" (#\0 POPI 3) (#\1 A B)))) (fmx "~@0" pair)). Thus, even though the function that produced the "error" could not print it, it could specify exactly what error message and data are to be printed. This example raises another issue. Sometimes it is desirable to break lines in your format strings so as to make your source code more attractive. That is the purpose of the tilde-newline directive. The following code produces exactly the same output as described above. (let ((pair '("Error: The instruction ~x0 ~ is illegal when the stack is ~ ~x1.~%" (#\0 POPI 3) (#\1 A B)))) (fmx "~@0" pair)). Finally, observe that when ~@0 extends the current alist, alist, with the one, a, in its argument, the bindings from a are added to the front of alist, overriding the current values of any shared variables. This ensures that the variable values seen by the recursively processed string, "str", are those from a, but if "str" uses variables not bound in a, their values are as specified in the original alist. Intuitively, variables bound in a are local to the processing of ("str" . a) but "str" may use "global variables." The example above illustrates this because when the ~@0 is processed, #\0 is bound to the error message pair. But when the ~x0 in the error string is processed, #\0 is bound to the illegal instruction. _Iteration_ The ~* directive is used to process each element of a list. For example, (let ((lst '(a b c d e f g h))) ; a true-list whose elements we exhibit (fmx "~*0" `("Whoa!" ; what to print if there's nothing to print "~x*!" ; how to print the last element "~x* and " ; how to print the 2nd to last element "~x*, " ; how to print all other elements ,lst))) ; the list of elements to print will print "A, B, C, D, E, F, G and H!". Try this example with other true list values of lst, such as '(a b), '(a), and nil. The tilde-directives ~&0 and ~v0, which take a true list argument and display its elements separated by commas and a final "and" or "or," are implemented in terms of the more general ~*. The ~* directive allows the 5-tuple to specify in its final cdr an alist with which to extend the current one before processing the individual elements. We often use ~* to print a series of phrases, separated by suitable punctuation, whitespace and noise words. In such use, the ~* handles the separation of the phrases and each phrase is generally printed by ~@. Here is a complex example. In the let*, below, we bind phrases to a list of ~@ pairs and then we create a ~* 5-tuple to print out the conjunction of the phrases with a parenthetical "finally!" if the series is longer than 3. (let* ((phrases (list (list "simplifying with the replacement rules ~&0" (cons #\0 '(rewrite-rule1 rewrite-rule2 rewrite-rule3))) (list "destructor elimination using ~x0" (cons #\0 'elim-rule)) (list "generalizing the terms ~&0" (cons #\0 '((rev x) (app u v)))) (list "inducting on ~x0" (cons #\0 'I)))) (5-tuple (list "magic" ; no phrases "~@*" ; last phrase "~@*, and~#f~[~/ (finally!)~] " ; second to last phrase "~@*, " ; other phrases phrases ; the phrases themselves (cons #\f (if (>(length phrases) 3) 1 0))))) ;print ``finally''? (fmx "We did it by ~*0." 5-tuple)) This let* prints We did it by simplifying with the replacement rules REWRITE-RULE1, REWRITE-RULE2 and REWRITE-RULE3, destructor elimination using ELIM- RULE, generalizing the terms (REV X) and (APP U V), and (finally!) inducting on I. You might wish to try evaluating the let* after removing elements of phrases. Most of the output produced by ACL2 is produced via fmt statements. Thus, inspection of the source code will yield many examples. A complicated example is the code that explains the simplifier's work. See :pc simplify-clause-msg1. An ad hoc example is provided by the function fmt-doc-example, which takes two arguments: an arbitrary true list and state. To see how fmt-doc-example works, :pe fmt-doc-example. (fmt-doc-example '(a b c d e f g h i j k l m n o p) state) will produce the output Here is a true list: (A B C D E F G H I J K L M N O P). It has 16 elements, the third of which is C. We could print each element in square brackets: ([A], [B], [C], [D], [E], [F], [G], [H], [I], [J], [K], [L], [M], [N], [almost there: O], [the end: P]). And if we wished to itemize them into column 15 we could do it like this 0123456789012345 0 (zeroth) A 1 (first) B 2 (second) C 3 (third) D 4 (fourth) E 5 (fifth) F 6 (sixth) G 7 (seventh) H 8 (eighth) I 9 (ninth) J 10 (tenth) K 11 (eleventh) L 12 (twelfth) M 13 (thirteenth) N 14 (14th) O 15 (15th) P End of example. and return (mv 15 state). Finally, we should remind the reader that fmt and its subfunctions, most importantly fmt0, are written entirely in ACL2. We make this comment for two reasons. First, it illustrates the fact that quite low level code can be efficiently written in the language. Second, it means that as a last resort for documentation purposes you can read the source code without changing languages.  File: acl2-doc-emacs.info, Node: FMT!, Next: FMT!-TO-STRING, Prev: FMT, Up: ACL2-BUILT-INS FMT! :(str alist co-channel state evisc) => state This function is nearly identical to fmt; see *note FMT::. The only difference is that fmt may insert backslash (\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use fmt! instead if you want to be able to read the forms back in.  File: acl2-doc-emacs.info, Node: FMT!-TO-STRING, Next: FMT-TO-COMMENT-WINDOW, Prev: FMT!, Up: ACL2-BUILT-INS FMT!-TO-STRING See *note PRINTING-TO-STRINGS::.  File: acl2-doc-emacs.info, Node: FMT-TO-COMMENT-WINDOW, Next: FMT-TO-STRING, Prev: FMT!-TO-STRING, Up: ACL2-BUILT-INS FMT-TO-COMMENT-WINDOW print to the comment window See *note CW:: for an introduction to the comment window and the usual way to print it. Function fmt-to-comment-window is identical to fmt1 (see *note FMT::), except that the channel is *standard-co* and the ACL2 state is neither an input nor an output. An analogous function, fmt-to-comment-window!, prints with fmt! instead of fmt, in order to avoid insertion of backslash (\) characters for margins; also see *note CW!::. Note that even if you change the value of ld special standard-co (see *note STANDARD-CO::), fmt-to-comment-window will print to *standard-co*, which is the original value of standard-co. General Form: (fmt-to-comment-window fmt-string alist col evisc-tuple) where these arguments are as desribed for fmt1; see *note FMT::.  File: acl2-doc-emacs.info, Node: FMT-TO-STRING, Next: FMT1, Prev: FMT-TO-COMMENT-WINDOW, Up: ACL2-BUILT-INS FMT-TO-STRING See *note PRINTING-TO-STRINGS::.  File: acl2-doc-emacs.info, Node: FMT1, Next: FMT1!, Prev: FMT-TO-STRING, Up: ACL2-BUILT-INS FMT1 :(str alist col co-channel state evisc) => (mv col state) See *note FMT:: for further explanation, including documentation of the tilde-directives.  File: acl2-doc-emacs.info, Node: FMT1!, Next: FMT1!-TO-STRING, Prev: FMT1, Up: ACL2-BUILT-INS FMT1! :(str alist col channel state evisc) => (mv col state) This function is nearly identical to fmt1; see *note FMT1::. The only difference is that fmt1 may insert backslash (\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use fmt1! instead if you want to be able to read the forms back in.  File: acl2-doc-emacs.info, Node: FMT1!-TO-STRING, Next: FMT1-TO-STRING, Prev: FMT1!, Up: ACL2-BUILT-INS FMT1!-TO-STRING See *note PRINTING-TO-STRINGS::.  File: acl2-doc-emacs.info, Node: FMT1-TO-STRING, Next: FOURTH, Prev: FMT1!-TO-STRING, Up: ACL2-BUILT-INS FMT1-TO-STRING See *note PRINTING-TO-STRINGS::.  File: acl2-doc-emacs.info, Node: FOURTH, Next: GET-OUTPUT-STREAM-STRING$, Prev: FMT1-TO-STRING, Up: ACL2-BUILT-INS FOURTH fourth member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: GET-OUTPUT-STREAM-STRING$, Next: GETENV$, Prev: FOURTH, Up: ACL2-BUILT-INS GET-OUTPUT-STREAM-STRING$ See *note IO::.  File: acl2-doc-emacs.info, Node: GETENV$, Next: GETPROP, Prev: GET-OUTPUT-STREAM-STRING$, Up: ACL2-BUILT-INS GETENV$ read an environment variable (Getenv$ str state), where str is a string, reads the value of environment variable str, returning a value of nil if none is found or if the read fails. The logical story is that getenv$ reads its value from the oracle field of the ACL2 state. The return value is thus a triple of the form (mv erp val state), where erp will always be nil in practice, and logically, val is the top of the acl2-oracle field of the state and the returned state has the updated (popped) acl2-oracle. Example: (getenv$ "PWD" state) ==> (mv nil "/u/joe/work" state) Also see *note SETENV$::.  File: acl2-doc-emacs.info, Node: GETPROP, Next: GOOD-ATOM-LISTP, Prev: GETENV$, Up: ACL2-BUILT-INS GETPROP access fast property lists General form: (getprop symb key default world-name world-alist) See community book books/misc/getprop.lisp for an example that illustrates the use of ACL2 utilities getprop and putprop to take advantage of under-the-hood Lisp (hashed) property lists. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: GOOD-ATOM-LISTP, Next: HARD-ERROR, Prev: GETPROP, Up: ACL2-BUILT-INS GOOD-ATOM-LISTP recognizer for a true list of "good" atoms The predicate good-atom-listp tests whether its argument is a true-listp of "good" atoms, i.e., where each element is a number, a symbol, a character, or a string. Also see *note ATOM-LISTP::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: HARD-ERROR, Next: IDENTITY, Prev: GOOD-ATOM-LISTP, Up: ACL2-BUILT-INS HARD-ERROR print an error message and stop execution (Hard-error ctx str alist) causes evaluation to halt with a short message using the "context" ctx. An error message is first printed using the string str and alist alist that are of the same kind as expected by fmt. See *note FMT::. Also see *note ER:: for a macro that provides a unified way of signaling errors. Hard-error has a guard of t. Also see *note ILLEGAL:: for a similar capability which however has a guard of nil that supports static checking using guard verification, rather than using dynamic (run-time) checking. This distinction is illustrated elsewhere: see *note PROG2$:: for examples. Semantically, hard-error ignores its arguments and always returns nil. But if a call (hard-error ctx str alist) is encountered during evaluation, then the string str is printed using the association list alist (as in fmt), after which evaluation halts immediately. Here is a trivial, contrived example. ACL2 !>(cons 3 (hard-error 'my-context "Printing 4: ~n0" (list (cons #\0 4)))) HARD ACL2 ERROR in MY-CONTEXT: Printing 4: four ACL2 Error in TOP-LEVEL: Evaluation aborted. ACL2 !>  File: acl2-doc-emacs.info, Node: IDENTITY, Next: IF, Prev: HARD-ERROR, Up: ACL2-BUILT-INS IDENTITY the identity function (Identity x) equals x; what else can we say? Identity is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: IF, Next: IFF, Prev: IDENTITY, Up: ACL2-BUILT-INS IF if-then-else function (if x y z) is equal to y if x is any value other than nil, and is equal to z if x is nil. Only one of y, z is evaluated when (if x y z) is evaluated. If has a guard of t. If is part of Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: IFF, Next: IFIX, Prev: IF, Up: ACL2-BUILT-INS IFF logical "if and only if" Iff is the ACL2 biconditional, "if and only if". (iff P Q) means that either P and Q are both false (i.e., nil) or both true (i.e., not nil). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: IFIX, Next: ILLEGAL, Prev: IFF, Up: ACL2-BUILT-INS IFIX coerce to an integer Ifix simply returns any integer argument unchanged, returning 0 on a non-integer argument. Also see *note NFIX::, see *note RFIX::, see *note REALFIX:: and see *note FIX:: for analogous functions that coerce to a natural number, a rational number, a real, and a number, respectively. Ifix has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ILLEGAL, Next: IMAGPART, Prev: IFIX, Up: ACL2-BUILT-INS ILLEGAL print an error message and stop execution (Illegal ctx str alist) causes evaluation to halt with a short message using the "context" ctx. An error message is first printed using the string str and alist alist that are of the same kind as expected by fmt. See *note FMT::, and see *note PROG2$:: for an example of how to use a related function, hard-error (see *note HARD-ERROR::). Also see *note ER:: for a macro that provides a unified way of signaling errors. The difference between illegal and hard-error is that the former has a guard of nil while the latter has a guard of t. Thus, you may want to use illegal rather than hard-error when you intend to do guard verification at some point, and you expect the guard to guarantee that the illegal call is never executed. See *note PROG2$:: for an example.  File: acl2-doc-emacs.info, Node: IMAGPART, Next: IMPLIES, Prev: ILLEGAL, Up: ACL2-BUILT-INS IMAGPART imaginary part of a complex number Completion Axiom (completion-of-imagpart): (equal (imagpart x) (if (acl2-numberp x) (imagpart x) 0)) Guard for (imagpart x): (acl2-numberp x)  File: acl2-doc-emacs.info, Node: IMPLIES, Next: IMPROPER-CONSP, Prev: IMAGPART, Up: ACL2-BUILT-INS IMPLIES logical implication Implies is the ACL2 implication function. (implies P Q) means that either P is false (i.e., nil) or Q is true (i.e., not nil). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: IMPROPER-CONSP, Next: INT=, Prev: IMPLIES, Up: ACL2-BUILT-INS IMPROPER-CONSP recognizer for improper (non-null-terminated) non-empty lists Improper-consp is the function that checks whether its argument is a non-empty list that ends in other than nil. See *note PROPER-CONSP:: and also see *note TRUE-LISTP::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: INT=, Next: INTEGER-LENGTH, Prev: IMPROPER-CONSP, Up: ACL2-BUILT-INS INT= test equality of two integers (int= x y) is logically equivalent to (equal x y). Unlike equal, int= requires its arguments to be numbers (or else causes a guard violation; see *note GUARD::). Generally, int= is executed more efficiently than equal or = on integers.  File: acl2-doc-emacs.info, Node: INTEGER-LENGTH, Next: INTEGER-LISTP, Prev: INT=, Up: ACL2-BUILT-INS INTEGER-LENGTH number of bits in two's complement integer representation For non-negative integers, (integer-length i) is the minimum number of bits needed to represent the integer. Any integer can be represented as a signed two's complement field with a minimum of (+ (integer-length i) 1) bits. The guard for integer-length requires its argument to be an integer. Integer-length is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: INTEGER-LISTP, Next: INTEGERP, Prev: INTEGER-LENGTH, Up: ACL2-BUILT-INS INTEGER-LISTP recognizer for a true list of integers The predicate integer-listp tests whether its argument is a true list of integers. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: INTEGERP, Next: INTERN, Prev: INTEGER-LISTP, Up: ACL2-BUILT-INS INTEGERP recognizer for whole numbers (integerp x) is true if and only if x is an integer.  File: acl2-doc-emacs.info, Node: INTERN, Next: INTERN$, Prev: INTEGERP, Up: ACL2-BUILT-INS INTERN create a new symbol in a given package (intern symbol-name symbol-package-name) returns a symbol with the given symbol-name and the given symbol-package-name. We restrict Common Lisp's intern so that the second argument is either the symbol *main-lisp-package-name*, the value of that constant, or is one of "ACL2", "ACL2-INPUT-CHANNEL", "ACL2-OUTPUT-CHANNEL", or "KEYWORD". To avoid that restriction, see *note INTERN$::. In ACL2 intern is actually implemented as a macro that expands to a call of a similar function whose second argument is a symbol. Invoke :pe intern to see the definition, or see *note INTERN-IN-PACKAGE-OF-SYMBOL::. To see why is intern so restricted consider (intern "X" "P"). In particular, is it a symbol and if so, what is its symbol-package-name? One is tempted to say "yes, it is a symbol in the package "P"." But if package "P" has not yet been defined, that would be premature because the imports to the package are unknown. For example, if "P" were introduced with (defpkg "P" '(LISP::X)) then in Common Lisp (symbol-package-name (intern "X" "P")) returns "LISP". The obvious restriction on intern is that its second argument be the name of a package known to ACL2. We cannot express such a restriction (except, for example, by limiting it to those packages known at some fixed time, as we do). Instead, we provide intern-in-package-of-symbol which requires a "witness symbol" for the package instead of the package. The witness symbol is any symbol (expressible in ACL2) and uniquely specifies a package necessarily known to ACL2.  File: acl2-doc-emacs.info, Node: INTERN$, Next: INTERN-IN-PACKAGE-OF-SYMBOL, Prev: INTERN, Up: ACL2-BUILT-INS INTERN$ create a new symbol in a given package Intern$ is a macro that behaves the same as the macro intern, except for weakening the restriction to a fixed set of package names so that any package name other than "" is legal. See *note INTERN::. Note that if you evaluate a call (intern$ x y) for which there is no package with name y that is known to ACL2, you will get an error. (Intern$ x y) expands to: (intern-in-package-of-symbol x (pkg-witness y)) See *note INTERN-IN-PACKAGE-OF-SYMBOL:: and see *note PKG-WITNESS::.  File: acl2-doc-emacs.info, Node: INTERN-IN-PACKAGE-OF-SYMBOL, Next: INTERSECTION$, Prev: INTERN$, Up: ACL2-BUILT-INS INTERN-IN-PACKAGE-OF-SYMBOL create a symbol with a given name Completion Axiom (completion-of-intern-in-package-of-symbol): (equal (intern-in-package-of-symbol x y) (if (and (stringp x) (symbolp y)) (intern-in-package-of-symbol x y) nil)) Guard for (intern-in-package-of-symbol x y): (and (stringp x) (symbolp y)) Intuitively, (intern-in-package-of-symbol x y) creates a symbol with symbol-name x interned in the package containing y. More precisely, suppose x is a string, y is a symbol with symbol-package-name pkg and that the defpkg event creating pkg had the list of symbols imports as the value of its second argument. Then (intern-in-package-of-symbol x y) returns a symbol, ans, the symbol-name of ans is x, and the symbol-package-name of ans is pkg, unless x is the symbol-name of some member of imports with symbol-package-name ipkg, in which case the symbol-package-name of ans is ipkg. Because defpkg requires that there be no duplications among the symbol-names of the imports, intern-in-package-of-symbol is uniquely defined. For example, suppose "MY-PKG" was created by (defpkg "MY-PKG" '(ACL2::ABC LISP::CAR)). Let w be 'my-pkg::witness. Observe that (symbolp w) is t ; w is a symbol (symbol-name w) is "WITNESS" ; w's name is "WITNESS" (symbol-package-name w) is "MY-PKG" ; w is in the package "MY-PKG" The construction of w illustrates one way to obtain a symbol in a given package: write it down as a constant using the double-colon notation. But another way to obtain a symbol in a given package is to create it with intern-in-package-of-symbol. (intern-in-package-of-symbol "XYZ" w) is MY-PKG::XYZ (intern-in-package-of-symbol "ABC" w) is ACL2::ABC (intern-in-package-of-symbol "CAR" w) is LISP::CAR (intern-in-package-of-symbol "car" w) is MY-PKG::|car|  File: acl2-doc-emacs.info, Node: INTERSECTION$, Next: INTERSECTION-EQ, Prev: INTERN-IN-PACKAGE-OF-SYMBOL, Up: ACL2-BUILT-INS INTERSECTION$ elements of one list that are not elements of another General Forms: (intersection$ l1 l2 ... lk) (intersection$ l1 l2 ... lk :test 'eql) ; same as above (intersection$ l1 l2 ... lk :test 'eq) ; same, but eq is equality test (intersection$ l1 l2 ... lk :test 'equal) ; same, but equal is equality test (Intersection$ x y) equals a list that contains the members of x that are also members of y. More precisely, the resulting list is the result of deleting from x those members that that are not members of y. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists. Intersection$ need not take exactly two arguments, though it must take at least one argument: (intersection$ x) is x, (intersection$ x y z ... :test test) is (intersection$ x (intersection$ y z ... :test test) :test test), and if :TEST is not supplied, then (intersection$ x y z ...) is (intersection$ x (intersection$ y z ...)). For the discussion below we restrict ourselves, then, to the cases (intersection$ x y) and (intersection$ x y :test test). The guard for a call of intersection$ (in the two cases just above) depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between intersection$ and its variants: (intersection-eq x lst) is equivalent to (intersection$ x lst :test 'eq); (intersection-equal x lst) is equivalent to (intersection$ x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function intersection-equal. Note that intersection-eq can take any positive number of arguments, in analogy to intersection$; indeed, (intersection-eq ...) expands to (intersection$ ... :test 'eq). However, intersection-equal is a function, not a macro, and takes exactly two arguments. Intersection$ is similar to the Common Lisp primitive intersection. However, Common Lisp does not specify the order of elements in the result of a call of intersection.  File: acl2-doc-emacs.info, Node: INTERSECTION-EQ, Next: INTERSECTION-EQUAL, Prev: INTERSECTION$, Up: ACL2-BUILT-INS INTERSECTION-EQ See *note INTERSECTION$::.  File: acl2-doc-emacs.info, Node: INTERSECTION-EQUAL, Next: INTERSECTP, Prev: INTERSECTION-EQ, Up: ACL2-BUILT-INS INTERSECTION-EQUAL See *note INTERSECTION$::.  File: acl2-doc-emacs.info, Node: INTERSECTP, Next: INTERSECTP-EQ, Prev: INTERSECTION-EQUAL, Up: ACL2-BUILT-INS INTERSECTP test whether two lists intersect General Forms: (set-difference$ l1 l2) (set-difference$ l1 l2 :test 'eql) ; same as above (eql as equality test) (set-difference$ l1 l2 :test 'eq) ; same, but eq is equality test (set-difference$ l1 l2 :test 'equal) ; same, but equal is equality test (Intersectp l1 l2) returns t if l1 and l2 have a member in common, else it returns nil. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists. The guard for a call of intersectp depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between intersectp and its variants: (intersectp-eq x lst) is equivalent to (intersectp x lst :test 'eq); (intersectp-equal x lst) is equivalent to (intersectp x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function intersectp-equal.  File: acl2-doc-emacs.info, Node: INTERSECTP-EQ, Next: INTERSECTP-EQUAL, Prev: INTERSECTP, Up: ACL2-BUILT-INS INTERSECTP-EQ See *note INTERSECTP::.  File: acl2-doc-emacs.info, Node: INTERSECTP-EQUAL, Next: KEYWORD-VALUE-LISTP, Prev: INTERSECTP-EQ, Up: ACL2-BUILT-INS INTERSECTP-EQUAL See *note INTERSECTP::.  File: acl2-doc-emacs.info, Node: KEYWORD-VALUE-LISTP, Next: KEYWORDP, Prev: INTERSECTP-EQUAL, Up: ACL2-BUILT-INS KEYWORD-VALUE-LISTP recognizer for true lists whose even-position elements are keywords (keyword-value-listp l) is true if and only if l is a list of even length of the form (k1 a1 k2 a2 ... kn an), where each ki is a keyword. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: KEYWORDP, Next: KWOTE, Prev: KEYWORD-VALUE-LISTP, Up: ACL2-BUILT-INS KEYWORDP recognizer for keywords (Keywordp x) is true if and only if x is a keyword, i.e., a symbol in the "KEYWORD" package. Such symbols are typically printed using a colon (:) followed by the symbol-name of the symbol. Keywordp has a guard of t. Keywordp is a Common Lisp function. See any Common Lisp documentation for more information. The following log may be illuminating. ACL2 !>(intern "ABC" "KEYWORD") :ABC ACL2 !>(symbol-name ':ABC) "ABC" ACL2 !>(symbol-package-name ':ABC) "KEYWORD" ACL2 !> To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: KWOTE, Next: KWOTE-LST, Prev: KEYWORDP, Up: ACL2-BUILT-INS KWOTE quote an arbitrary object For any object x, (kwote x) returns the two-element list whose elements are the symbol quote and the given x, respectively. The guard of (kwote x) is t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: KWOTE-LST, Next: LAST, Prev: KWOTE, Up: ACL2-BUILT-INS KWOTE-LST quote an arbitrary true list of objects The function kwote-lst applies the function kwote to each element of a given list. The guard of (kwote-lst lst) is (true-listp lst). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: LAST, Next: LEN, Prev: KWOTE-LST, Up: ACL2-BUILT-INS LAST the last cons (not element) of a list (Last l) is the last cons of a list. Here are examples. ACL2 !>(last '(a b . c)) (B . C) ACL2 !>(last '(a b c)) (C) (Last l) has a guard of (listp l); thus, l need not be a true-listp. Last is a Common Lisp function. See any Common Lisp documentation for more information. Unlike Common Lisp, we do not allow an optional second argument for last. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: LEN, Next: LENGTH, Prev: LAST, Up: ACL2-BUILT-INS LEN length of a list Len returns the length of a list. A Common Lisp function that is appropriate for both strings and proper lists is length; see *note LENGTH::. The guard for len is t. (Low-level implementation note. ACL2 provides a highly-optimized implementation of len, which is tail-recursive and fixnum-aware, that differs from its simple ACL2 definition.) To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: LENGTH, Next: LET, Prev: LEN, Up: ACL2-BUILT-INS LENGTH length of a string or proper list Length is the function for determining the length of a sequence. In ACL2, the argument is required to be either a true-listp or a string. Length is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: LET, Next: LET*, Prev: LENGTH, Up: ACL2-BUILT-INS LET binding of lexically scoped (local) variables Example LET Form: (let ((x (* x x)) (y (* 2 x))) (list x y)) If the form above is executed in an environment in which x has the value -2, then the result is '(4 -4). Let expressions bind variables so that their "local" values, the values they have when the "body" of the let is evaluated, are possibly different than their "global" values, the values they have in the context in which the let expression appears. In the let expression above, the local variables bound by the let are x and y. They are locally bound to the values delivered by the two forms (* x x) and (* 2 x), respectively, that appear in the "bindings" of the let. The body of the let is (list x y). Suppose that the let expression above occurs in a context in which x has the value -2. (The global value of y is irrelevant to this example.) For example, one might imagine that the let form above occurs as the body of some function, fn, with the formal parameter x and we are evaluating (fn -2). To evaluate the let above in a context in which x is -2, we first evaluate the two forms specifying the local values of the variables. Thus, (* x x) is evaluated and produces 4 (because x is -2) and (* 2 x) is evaluated and produces -4 (because x is -2). Then x and y are bound to these values and the body of the let is evaluated. Thus, when the body, (list x y) is evaluated, x is 4 and y is -4. Thus, the body produces '(4 -4). Note that the binding of y, which is written after the binding of x and which mentions x, nevertheless uses the global value of x, not the new local value. That is, the local variables of the let are bound "in parallel" rather than "sequentially." In contrast, if the Example LET* Form: (let* ((x (* x x)) (y (* 2 x))) (list x y)) is evaluated when the global value of x is -2, then the result is '(4 8), because the local value of y is computed after x has been bound to 4. Let* binds its local variables "sequentially." General LET Forms: (let ((var1 term1) ... (varn termn)) body) and (let ((var1 term1) ... (varn termn)) (declare ...) ... (declare ...) body) where the vari are distinct variables, the termi are terms involving only variables bound in the environment containing the let, and body is a term involving only the vari plus the variables bound in the environment containing the let. Each vari must be used in body or else declared ignored. A let form is evaluated by first evaluating each of the termi, obtaining for each a vali. Then, each vari is bound to the corresponding vali and body is evaluated. Actually, let forms are just abbreviations for certain uses of lambda notation. In particular (let ((var1 term1) ... (varn termn)) (declare ...) body) is equivalent to ((lambda (var1 ... varn) (declare ...) body) term1 ... termn). Let* forms are used when it is desired to bind the vari sequentially, i.e., when the local values of preceding varj are to be used in the computation of the local value for vari. General LET* Forms: (let* ((var1 term1) ... (varn termn)) body) and (let* ((var1 term1) ... (varn termn)) (declare (ignore x1 ... xm)) body) where the vari are variables (not necessarily distinct), the termi are terms involving only variables bound in the environment containing the let* and those varj such that j a2 > ... > an > 0 such that alpha > a1 and alpha = w^(a1)x1 + w^(a2)x2 + ... + w^(an)xn + p. We call a1 the "first exponent", x1 the "first coefficient", and the remainder (w^(a2)x2 + ... + w^(an)xn + p) the "rest" of alpha. (Make-ord fe fco rst) corresponds to the ordinal (w^fe)fco + rst. Thus the first infinite ordinal, w (omega), is constructed by (make-ord 1 1 0) and, for example, the ordinal (w^2)5 + w2 + 7 is constructed by: (make-ord 2 5 (make-ord 1 2 7)) . The reason make-ord is used rather than cons is that it allows us to reason more abstractly about the ordinals, without having to worry about the underlying representation. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MAX, Next: MBE, Prev: MAKE-ORD, Up: ACL2-BUILT-INS MAX the larger of two numbers (Max x y) is the larger of the numbers x and y. The guard for max requires its arguments to be rational (real, in ACL2(r)) numbers. Max is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MBE, Next: MBE1, Prev: MAX, Up: ACL2-BUILT-INS MBE attach code for execution The macro mbe ("must be equal") can be used in function definitions in order to cause evaluation to use alternate code to that provided for the logic. An example is given below. However, the use of mbe can lead to non-terminating computations. See *note DEFEXEC::, perhaps after reading the present documentation, for a way to prove termination. In the ACL2 logic, (mbe :exec exec-code :logic logic-code) equals logic-code; the value of exec-code is ignored. However, in raw Lisp it is the other way around: this form macroexpands simply to exec-code. ACL2's guard verification mechanism ensures that the raw Lisp code is only evaluated when appropriate, since the guard proof obligations generated for (the macroexpansion of) this call of mbe include not only the guard proof obligations from exec-code, but also, under suitable contextual assumptions, the term (equal exec-code logic-code). See *note VERIFY-GUARDS:: (in particular, for discussion of the contextual assumptions from the :guard and IF-tests) and, for general discussion of guards, see *note GUARD::. Normally, during evaluation of an mbe call, only the :logic code is evaluated unless the call is in the body of a guard-verified function, in which case only the :exec code is evaluated. This implies that equality of :exec and :logic code is never checked at runtime. (Rather, such equality is proved when verifying guards.) We started with "normally" above because there is an exception: during a "safe mode", which is used in macroexpansion and evaluation of defconst forms, the :logic and :exec code are both evaluated and their equality is checked. Note that the :exec and the :logic code in an mbe call must have the same return type. For example, one cannot return (mv * *) while the other returns just a single value. Also see *note MBT::, which stands for "must be true." You may find it more natural to use mbt for certain applications, as described in its documentation. * Menu: Related topics other than immediate subtopics: * DEFEXEC:: attach a terminating executable function to a definition Here is an example of the use of mbe. Suppose that you want to define factorial in the usual recursive manner, as follows. (defun fact (n) (if (zp n) 1 (* n (fact (1- n))))) But perhaps you want to be able to execute calls of fact on large arguments that cause stack overflows, perhaps during proofs. (This isn't a particularly realistic example, but it should serve.) So, instead you can define this tail-recursive version of factorial: (defun fact1 (n acc) (declare (xargs :guard (and (integerp n) (>= n 0) (integerp acc)))) (if (zp n) acc (fact1 (1- n) (* n acc)))) We are now ready to define fact using mbe. Our intention is that logically, fact is as shown in the first definition above, but that fact should be executed by calling fact1. Notice that we defer guard verification, since we are not ready to prove the correspondence between fact1 and fact. (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)) :verify-guards nil)) (mbe :exec (fact1 n 1) :logic (if (zp n) 1 (* n (fact (1- n)))))) Next, we prove the necessary correspondence lemmas. Notice the inclusion of a community book to help with the arithmetic reasoning. (include-book "books/arithmetic/top-with-meta") (defthm fact1-fact (implies (integerp acc) (equal (fact1 n acc) (* acc (fact n))))) We may now do guard verification for fact, which will allow the execution of the raw Lisp fact function, where the above mbe call expands simply to (fact1 n 1). (verify-guards fact) Now that guards have been verified, a trace of function calls illustrates that the evaluation of calls of fact is passed to evaluation of calls of fact1. The outermost call below is of the logical function stored for the definition of fact; all the others are of actual raw Common Lisp functions. ACL2 !>(trace$ fact fact1) NIL ACL2 !>(fact 3) 1> (ACL2_*1*_ACL2::FACT 3) 2> (FACT 3) 3> (FACT1 3 1) 4> (FACT1 2 3) 5> (FACT1 1 6) 6> (FACT1 0 6) <6 (FACT1 6) <5 (FACT1 6) <4 (FACT1 6) <3 (FACT1 6) <2 (FACT 6) <1 (ACL2_*1*_ACL2::FACT 6) 6 ACL2 !> You may occasionally get warnings when you compile functions defined using mbe. (For commands that invoke the compiler, see *note COMPILATION::.) These can be inhibited by using an ignorable declare form. Here is a simple but illustrative example. Note that the declarations can optionally be separated into two declare forms. (defun foo (x y) (declare (ignorable x) (xargs :guard (equal x y))) (mbe :logic x :exec y)) Finally, we observe that when the body of a function contains a term of the form (mbe :exec exec-code :logic logic-code), the user is very unlikely to see any logical difference than if this were replaced by logic-code. ACL2 takes various steps to ensure this. For example, the proof obligations generated for admitting a function treat the above mbe term simply as logic-code. Function expansion, :use hints, :definition rules, generation of constraints for functional instantiation, and creation of rules of class :rewrite and :forward-chaining also treat mbe calls as their :logic code.  File: acl2-doc-emacs.info, Node: MBE1, Next: MBT, Prev: MBE, Up: ACL2-BUILT-INS MBE1 attach code for execution The form (mbe1 exec logic) is equivalent to the forms (mbe :logic logic :exec exec) and (must-be-equal logic exec). See *note MBE::.  File: acl2-doc-emacs.info, Node: MBT, Next: MEMBER, Prev: MBE1, Up: ACL2-BUILT-INS MBT introduce a test not to be evaluated The macro mbt ("must be true") can be used in order to add code in order to admit function definitions in :logic mode, without paying a cost in execution efficiency. Examples below illustrate its intended use. Semantically, (mbt x) equals x. However, in raw Lisp (mbt x) ignores x entirely, and macroexpands to t. ACL2's guard verification mechanism ensures that the raw Lisp code is only evaluated when appropriate, since a guard proof obligation (equal x t) is generated. See *note VERIFY-GUARDS:: and, for general discussion of guards, see *note GUARD::. Also see *note MBE::, which stands for "must be equal." Although mbt is more natural in many cases, mbe has more general applicability. In fact, (mbt x) is essentially defined to be (mbe :logic x :exec t). We can illustrate the use of mbt on the following generic example, where , , , and are intended to be terms involving only the variable x. (defun foo (x) (declare (xargs :guard )) (if (foo ) )) In order to admit this function, ACL2 needs to discharge the proof obligation that is smaller than x, namely: (implies (o< (acl2-count ) (acl2-count x))) But suppose we need to know that is true in order to prove this. Since is only the guard, it is not part of the logical definition of foo. A solution is to add the guard to the definition of foo, as follows. (defun foo (x) (declare (xargs :guard )) (if (mbt ) (if (foo ) ) nil)) If we do this using rather than (mbt ), then evaluation of every recursive call of foo will cause the evaluation of (the appropriate instance of) . But since (mbt ) expands to t in raw Lisp, then once we verify the guards of foo, the evaluations of will be avoided (except at the top level, when we check the guard before allowing evaluation to take place in Common Lisp). Other times, the guard isn't the issue, but rather, the problem is that a recursive call has an argument that itself is a recursive call. For example, suppose that is of the form (foo ). There is no way we can hope to discharge the termination proof obligation shown above. A standard solution is to add some version of this test: (mbt (o< (acl2-count ) (acl2-count x))) Here is a specific example based on one sent by Vernon Austel. (defun recurX2 (n) (declare (xargs :guard (and (integerp n) (<= 0 n)) :verify-guards nil)) (cond ((zp n) 0) (t (let ((call (recurX2 (1- n)))) (if (mbt (< (acl2-count call) n)) (recurX2 call) 1 ;; this branch is never actually taken ))))) (defthm recurX2-0 (equal (recurX2 n) 0)) (verify-guards recurX2) If you (trace$ acl2-count), you will see that evaluation of (recurX2 2) causes several calls of acl2-count before the verify-guards. But this evaluation does not call acl2-count after the verify-guards, because the ACL2 evaluation mechanism uses raw Lisp to do the evaluation, and the form (mbt (< (acl2-count call) n)) macroexpands to t in Common Lisp. You may occasionally get warnings when you compile functions defined using mbt. (For commands that invoke the compiler, see *note COMPILATION::.) These can be inhibited by using an ignorable declare form. Here is a simple but illustrative example. Note that the declarations can optionally be separated into two declare forms. (defun foo (x y) (declare (ignorable x) (xargs :guard (equal x t))) (and (mbt x) y))  File: acl2-doc-emacs.info, Node: MEMBER, Next: MEMBER-EQ, Prev: MBT, Up: ACL2-BUILT-INS MEMBER membership predicate General Forms: (member x lst) (member x lst :test 'eql) ; same as above (eql as equality test) (member x lst :test 'eq) ; same, but eq is equality test (member x lst :test 'equal) ; same, but equal is equality test (Member x lst) equals the longest tail of the list lst that begins with x, or else nil if no such tail exists. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst. The guard for a call of member depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between member and its variants: (member-eq x lst) is equivalent to (member x lst :test 'eq); (member-equal x lst) is equivalent to (member x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function member-equal. Member is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: MEMBER-EQ, Next: MEMBER-EQUAL, Prev: MEMBER, Up: ACL2-BUILT-INS MEMBER-EQ See *note MEMBER::.  File: acl2-doc-emacs.info, Node: MEMBER-EQUAL, Next: MIN, Prev: MEMBER-EQ, Up: ACL2-BUILT-INS MEMBER-EQUAL See *note MEMBER::.  File: acl2-doc-emacs.info, Node: MIN, Next: MINUSP, Prev: MEMBER-EQUAL, Up: ACL2-BUILT-INS MIN the smaller of two numbers (Min x y) is the smaller of the numbers x and y. The guard for min requires its arguments to be rational (real, in ACL2(r)) numbers. Min is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MINUSP, Next: MOD, Prev: MIN, Up: ACL2-BUILT-INS MINUSP test whether a number is negative (Minusp x) is true if and only if x < 0. The guard of minusp requires its argument to be a rational (real, in ACL2(r)) number. Minusp is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MOD, Next: MOD-EXPT, Prev: MINUSP, Up: ACL2-BUILT-INS MOD remainder using floor ACL2 !>(mod 14 3) 2 ACL2 !>(mod -14 3) 1 ACL2 !>(mod 14 -3) -1 ACL2 !>(mod -14 -3) -2 ACL2 !>(mod -15 -3) 0 ACL2 !> (Mod i j) is that number k that (* j (floor i j)) added to k equals i. The guard for (mod i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero. Mod is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MOD-EXPT, Next: MSG, Prev: MOD, Up: ACL2-BUILT-INS MOD-EXPT exponential function (mod-expt r i m) is the result of raising the number r to the integer power i and then taking the residue mod m. That is, (mod-expt r i m) is equal to (mod (expt r i) m). The guard for (mod-expt r i m) is that r is a rational number and i is an integer; if r is 0 then i is nonnegative; and m is a non-zero rational number. In some implementations (GCL Version 2.7.0 as of this writing), this function is highly optimized when r and i are natural numbers, not both zero, and m is a positive integer. For other Lisp implementations, consider using function mod-expt-fast, defined in the community book arithmetic-3/floor-mod/mod-expt-fast.lisp, which should still provide significantly improved performance over this function. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MSG, Next: MUST-BE-EQUAL, Prev: MOD-EXPT, Up: ACL2-BUILT-INS MSG construct a "message" suitable for the ~@ directive of fmt See *note FMT:: for background on formatted printing with ACL2. We document msg precisely below, but first, we give an informal introduction and illustrate with an example. Suppose you are writing a program that is to do some printing. Typically, you will either pass the ACL2 state around (see *note PROGRAMMING-WITH-STATE::) and use formatted printing functions that take the state as an argument (see *note FMT::)), or else you might avoid using state by calling the utility, cw, to do you printing. Alternatively, you might print error messages upon encountering illegal situations; see *note ER::. But there are times where instead of printing immediately, you may prefer to pass messages around, for example to accumulate them before printing a final message. In such cases, it may be desirable to construct "message" objects to pass around. For example, consider the following pair of little programs. The first either performs a computation or prints an error, and the second calls the first on two inputs. (defun invert1 (x) (if (consp x) (cons (cdr x) (car x)) (prog2$ (cw "ERROR: ~x0 expected a cons, but was given ~x1.~|" 'invert1 x) nil))) (defun invert2 (x1 x2) (list (invert1 x1) (invert1 x2))) For example: ACL2 !>(invert1 '(3 . 4)) (4 . 3) ACL2 !>(invert1 'a) ERROR: INVERT1 expected a cons, but was given A. NIL ACL2 !>(invert2 '(3 . 4) '(5 . 6)) ((4 . 3) (6 . 5)) ACL2 !>(invert2 'a 'b) ERROR: INVERT1 expected a cons, but was given A. ERROR: INVERT1 expected a cons, but was given B. (NIL NIL) ACL2 !> Notice that when there are errors, there is no attempt to explain that these are due to a call of invert2. That could be fixed, of course, by arranging for invert2 to print its own error; but for complicated programs it can be awkward to coordinate printing from many sources. So let's try a different approach. This time, the first function returns two results. The first result is an "error indicator" -- either a message object or nil -- while the second is the computed value (only of interest when the first result is nil). Then the higher-level function can print a single error message that includes the error message(s) from the lower-level function, as shown below. (defun invert1a (x) (if (consp x) (mv nil (cons (cdr x) (car x))) (mv (msg "ERROR: ~x0 expected a cons, but was given ~x1.~|" 'invert1a x) nil))) (defun invert2a (x1 x2) (mv-let (erp1 y1) (invert1a x1) (mv-let (erp2 y2) (invert1a x2) (if erp1 (if erp2 (cw "~x0 failed with two errors:~| ~@1 ~@2" 'invert2a erp1 erp2) (cw "~x0 failed with one error:~| ~@1" 'invert2a erp1)) (if erp2 (cw "~x0 failed with one error:~| ~@1" 'invert2a erp2) (list y1 y2)))))) For example: ACL2 !>(invert2a '(3 . 4) '(5 . 6)) ((4 . 3) (6 . 5)) ACL2 !>(invert2a '(3 . 4) 'b) INVERT2A failed with one error: ERROR: INVERT1A expected a cons, but was given B. NIL ACL2 !>(invert2a 'a 'b) INVERT2A failed with two errors: ERROR: INVERT1A expected a cons, but was given A. ERROR: INVERT1A expected a cons, but was given B. NIL ACL2 !> If you study the example above, you might well understand msg. But we conclude with precise documentation. General Form: (msg str arg1 ... argk) where str is a string and k is at most 9. This macro returns a pair suitable for giving to the fmt directive ~@. Thus, suppose that #\c is bound to the value of (msg str arg1 ... argk), where c is a character and k is at most 9. Then the fmt directive ~@c will print out the string, str, in the context of the alist in which the successive fmt variables #\0, #\1, ..., #\k are bound to the successive elements of (arg1 ... argk).  File: acl2-doc-emacs.info, Node: MUST-BE-EQUAL, Next: MV, Prev: MSG, Up: ACL2-BUILT-INS MUST-BE-EQUAL attach code for execution The form (must-be-equal logic exec) evaluates to logic in the ACL2 logic but evaluates to exec in raw Lisp. The point is to be able to write one definition to reason about logically but another for evaluation. Please see *note MBE:: and see *note MBT:: for appropriate macros to use, rather than calling must-be-equal directly, since it is easy to commute the arguments of must-be-equal by accident. In essence, the guard for (must-be-equal x y) is (equal x y). However, note that must-be-equal is a macro: (must-be-equal logic exec) expands to (mbe1 exec logic), which expands to a call of return-last.  File: acl2-doc-emacs.info, Node: MV, Next: MV-LET, Prev: MUST-BE-EQUAL, Up: ACL2-BUILT-INS MV returning a multiple value Mv is the mechanism provided by ACL2 for returning two or more values. Logically, (mv x1 x2 ... xn) is the same as (list x1 x2 ... xn), a list of the indicated values. However, ACL2 avoids the cost of building this list structure, with the cost that mv may only be used in a certain style in definitions: if a function ever returns using mv (either directly, or by calling another function that returns a multiple value), then this function must always return the same number of values. For more explanation of the multiple value mechanism, see *note MV-LET::. Also see *note MV-LIST:: for a way to convert a multiple value into an ordinary list. ACL2 does not support the Common Lisp construct values, whose logical meaning seems difficult to characterize. Mv is the ACL2 analogue of that construct.  File: acl2-doc-emacs.info, Node: MV-LET, Next: MV-LIST, Prev: MV, Up: ACL2-BUILT-INS MV-LET calling multi-valued ACL2 functions Example Form: (mv-let (x y z) ; local variables (mv 1 2 3) ; multi-valued expression (declare (ignore y)) ; optional declarations (cons x z)) ; body The form above binds the three "local variables," x, y, and z, to the three results returned by the multi-valued expression and then evaluates the body. The result is '(1 . 3). The second local, y, is declared ignored. The multi-valued expression can be any ACL2 expression that returns k results, where k is the number of local variables listed. Often however it is simply the application of a k-valued function. Mv-let is the standard way to invoke a multi-valued function when the caller must manipulate the vector of results returned. General Form: (mv-let (var1 ... vark) term body) or (mv-let (var1 ... vark) term (declare ...) ... (declare ...) body) where the vari are distinct variables, term is a term that returns k results and mentions only variables bound in the environment containing the mv-let expression, and body is a term mentioning only the vari and variables bound in the environment containing the mv-let. Each vari must occur in body unless it is declared ignored or ignorable in one of the optional declare forms, unless this requirement is turned off; see *note SET-IGNORE-OK::. The value of the mv-let term is the result of evaluating body in an environment in which the vari are bound, in order, to the k results obtained by evaluating term in the environment containing the mv-let. Here is an extended example that illustrates both the definition of a multi-valued function and the use of mv-let to call it. Consider a simple binary tree whose interior nodes are conses and whose leaves are non-conses. Suppose we often need to know the number, n, of interior nodes of such a tree; the list, syms, of symbols that occur as leaves; and the list, ints, of integers that occur as leaves. (Observe that there may be leaves that are neither symbols nor integers.) Using a multi-valued function we can collect all three results in one pass. Here is the first of two definitions of the desired function. This definition is "primitive recursive" in that it has only one argument and that argument is reduced in size on every recursion. (defun count-and-collect (x) ; We return three results, (mv n syms ints) as described above. (cond ((atom x) ; X is a leaf. Thus, there are 0 interior nodes, and depending on ; whether x is a symbol, an integer, or something else, we return ; the list containing x in as the appropriate result. (cond ((symbolp x) (mv 0 (list x) nil)) ((integerp x)(mv 0 nil (list x))) (t (mv 0 nil nil)))) (t ; X is an interior node. First we process the car, binding n1, syms1, and ; ints1 to the answers. (mv-let (n1 syms1 ints1) (count-and-collect (car x)) ; Next we process the cdr, binding n2, syms2, and ints2. (mv-let (n2 syms2 ints2) (count-and-collect (car x)) ; Finally, we compute the answer for x from those obtained for its car ; and cdr, remembering to increment the node count by one for x itself. (mv (1+ (+ n1 n2)) (append syms1 syms2) (append ints1 ints2))))))) This use of a multiple value to "do several things at once" is very common in ACL2. However, the function above is inefficient because it appends syms1 to syms2 and ints1 to ints2, copying the list structures of syms1 and ints1 in the process. By adding "accumulators" to the function, we can make the code more efficient. (defun count-and-collect1 (x n syms ints) (cond ((atom x) (cond ((symbolp x) (mv n (cons x syms) ints)) ((integerp x) (mv n syms (cons x ints))) (t (mv n syms ints)))) (t (mv-let (n2 syms2 ints2) (count-and-collect1 (cdr x) (1+ n) syms ints) (count-and-collect1 (car x) n2 syms2 ints2))))) We claim that (count-and-collect x) returns the same triple of results as (count-and-collect1 x 0 nil nil). The reader is urged to study this claim until convinced that it is true and that the latter method of computing the results is more efficient. One might try proving the theorem (defthm count-and-collect-theorem (equal (count-and-collect1 x 0 nil nil) (count-and-collect x))). Hint: the inductive proof requires attacking a more general theorem. ACL2 does not support the Common Lisp construct multiple-value-bind, whose logical meaning seems difficult to characterize. Mv-let is the ACL2 analogue of that construct. Also see *note MV:: and see *note MV-LIST::.  File: acl2-doc-emacs.info, Node: MV-LIST, Next: MV-NTH, Prev: MV-LET, Up: ACL2-BUILT-INS MV-LIST converting multiple-valued result to a single-valued list Example Forms: ; Returns the list (3 4): (mv-list 2 (mv 3 4)) ; Returns a list containing the three values returned by var-fn-count: (mv-list 3 (var-fn-count '(cons (binary-+ x y) z) nil)) General form: (mv-list n term) Logically, (mv-list n term) is just term; that is, in the logic mv-list simply returns its second argument. However, the evaluation of a call of mv-list on explicit values always results in a single value, which is a (null-terminated) list. For evaluation, the term n above (the first argument to an mv-list call) must "essentially" (see below) be an integer not less than 2, where that integer is the number of values returned by the evaluation of term (the second argument to that mv-list call). We say "essentially" above because it suffices that the translation of n to a term (see *note TRANS::) be of the form (quote k), where k is an integer greater than 1. So for example, if term above returns three values, then n can be the expression 3, or (quote 3), or even (mac 3) if mac is a macro defined by (defmacro mac (x) x). But n cannot be (+ 1 2), because even though that expression evaluates to 3, nevertheless it translates to (binary-+ '1 '2), not to (quote 3). Mv-list is the ACL2 analogue of the Common Lisp construct multiple-value-list. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: MV-NTH, Next: MV?, Prev: MV-LIST, Up: ACL2-BUILT-INS MV-NTH the mv-nth element (zero-based) of a list (Mv-nth n l) is the nth element of l, zero-based. If n is greater than or equal to the length of l, then mv-nth returns nil. (Mv-nth n l) has a guard that n is a non-negative integer. Mv-nth is equivalent to the Common Lisp function nth (although without the guard condition that the list is a true-listp), but is used by ACL2 to access the nth value returned by a multiply valued expression. For example, the following are logically equivalent: (mv-let (erp val state) (read-object ch state) (value (list erp val))) and (let ((erp (mv-nth 0 (read-object ch state))) (val (mv-nth 1 (read-object ch state))) (state (mv-nth 2 (read-object ch state)))) (value (list erp val))) To see the ACL2 definition of mv-nth, see *note PF::. If EXPR is an expression that is multiply valued, then the form (mv-nth n EXPR) is illegal both in definitions and in forms submitted directly to the ACL2 loop. Indeed, EXPR cannot be passed as an argument to any function (mv-nth or otherwise) in such an evaluation context. The reason is that ACL2 code compiled for execution does not actually create a list for multiple value return; for example, the read-object call above logically returns a list of length 3, but when evaluated, it instead stores its three returned values without constructing a list. In such cases you can use mv-nth to access the corresponding list by using mv-list, writing (mv-nth n (mv-list k EXPR)) for suitable k, where mv-list converts a multiple value result into the corresponding list; see *note MV-LIST::.  File: acl2-doc-emacs.info, Node: MV?, Next: MV?-LET, Prev: MV-NTH, Up: ACL2-BUILT-INS MV? return one or more values Mv? is designed to work with mv?-let, generalizing how mv works with mv-let by allowing the binding of a single variable. For example, the following form is legal. (mv?-let (y) (mv? (f x)) (declare (type integer y)) (g x y z)) The expression above is equivalent to the following expression, because (mv? form) expands to form for any expression, form. (let ((y (f x))) (declare (type integer y)) (g x y z)) Logically, (mv? x) is the same as x, while (mv? v1 v2 ...) is the same as (list v1 v2 ...). Also see *note MV:: and see *note MV?-LET::.  File: acl2-doc-emacs.info, Node: MV?-LET, Next: NAT-LISTP, Prev: MV?, Up: ACL2-BUILT-INS MV?-LET calling possibly multi-valued ACL2 functions Mv?-let is a macro that extends the macro mv-let by allowing a single variable to be bound. Thus, the syntax is the same as that of mv-let except that mv?-let is permitted to bind a single variable to a form that produces a single value. The macros mv?-let and mv? are provided to facilitate the writing of macros that traffic in expressions that could return one or more (multiple) values. For example, the form (mv?-let (y) (f x) (declare (type integer y)) (g x y z)) is equivalent to the following form. (let ((y (f x))) (declare (type integer y)) (g x y z)) Calls of mv?-let and of mv-let are equivalent when the first argument contains at least two variables; see *note MV-LET:: for documentation. In the case of binding a single variable, the general form is (mv?-let (var) term (declare ...) ... (declare ...) body) and this is equivalent to the following form (see *note LET::). (let ((var term)) (declare ...) ... (declare ...) body) Also see *note MV?::.  File: acl2-doc-emacs.info, Node: NAT-LISTP, Next: NATP, Prev: MV?-LET, Up: ACL2-BUILT-INS NAT-LISTP recognizer for a true list of natural numbers The predicate nat-listp tests whether its argument is a true list of natural numbers. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NATP, Next: NFIX, Prev: NAT-LISTP, Up: ACL2-BUILT-INS NATP a recognizer for the natural numbers The natural numbers is the set of all non-negative integers, {0,1,2,3,...}. Natp returns t if and only its argument is a natural number, and nil otherwise. We recommend the community book books/arithmetic/natp-posp.lisp as a book for reasoning about posp and natp. This book is included by community books books/arithmetic/top and books/arithmetic/top-with-meta. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NFIX, Next: NINTH, Prev: NATP, Up: ACL2-BUILT-INS NFIX coerce to a natural number Nfix simply returns any natural number argument unchanged, returning 0 on an argument that is not a natural number. Also see *note IFIX::, see *note RFIX::, see *note REALFIX::, and see *note FIX:: for analogous functions that coerce to an integer, a rational number, a real, and a number, respectively. Nfix has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NINTH, Next: NO-DUPLICATESP, Prev: NFIX, Up: ACL2-BUILT-INS NINTH ninth member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: NO-DUPLICATESP, Next: NO-DUPLICATESP-EQ, Prev: NINTH, Up: ACL2-BUILT-INS NO-DUPLICATESP check for duplicates in a list General Forms: (no-duplicatesp x) (no-duplicatesp x :test 'eql) ; same as above (eql as equality test) (no-duplicatesp x :test 'eq) ; same, but eq is equality test (no-duplicatesp x :test 'equal) ; same, but equal is equality test (no-duplicatesp lst) is true if and only if no member of lst occurs twice in lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing elements of lst. The guard for a call of no-duplicatesp depends on the test. In all cases, the argument must satisfy true-listp. If the test is eql, then the argument must satisfy eqlable-listp. If the test is eq, then the argument must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between no-duplicatesp and its variants: (no-duplicatesp-eq x lst) is equivalent to (no-duplicatesp x lst :test 'eq); (no-duplicatesp-equal x lst) is equivalent to (no-duplicatesp x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function no-duplicatesp-equal.  File: acl2-doc-emacs.info, Node: NO-DUPLICATESP-EQ, Next: NO-DUPLICATESP-EQUAL, Prev: NO-DUPLICATESP, Up: ACL2-BUILT-INS NO-DUPLICATESP-EQ See *note NO-DUPLICATESP::.  File: acl2-doc-emacs.info, Node: NO-DUPLICATESP-EQUAL, Next: NON-EXEC, Prev: NO-DUPLICATESP-EQ, Up: ACL2-BUILT-INS NO-DUPLICATESP-EQUAL See *note NO-DUPLICATESP::.  File: acl2-doc-emacs.info, Node: NON-EXEC, Next: NONNEGATIVE-INTEGER-QUOTIENT, Prev: NO-DUPLICATESP-EQUAL, Up: ACL2-BUILT-INS NON-EXEC mark code as non-executable Non-exec is a macro such that logically, (non-exec x) is equal to x. However, the argument to a call of non-exec need not obey the usual syntactic restrictions for executable code, and indeed, evaluation of a call of non-exec will result in an error. Moreover, for any form occurring in the body of a function (see *note DEFUN::) that is a call of non-exec, no guard proof obligations are generated for that form. The following example, although rather contrived, illustrates the use of non-exec. One can imagine a less contrived example that efficiently computes return values for a small number of fixed inputs and, for other inputs, returns something logically "consistent" with those return values. (defun double (x) (case x (1 2) (2 4) (3 6) (otherwise (non-exec (* 2 x))))) We can prove that double is compliant with Common Lisp (see *note GUARD::) and that it always computes (* 2 x). (verify-guards double) (thm (equal (double x) (* 2 x))) We can evaluate double on the specified arguments. But a call of non-exec results in an error message that reports the form that was supplied to non-exec. ACL2 !>(double 3) 6 ACL2 !>(double 10) ACL2 Error in TOP-LEVEL: ACL2 has been instructed to cause an error because of an attempt to evaluate the following form (see :DOC non- exec): (* 2 X). To debug see :DOC print-gv, see :DOC trace, and see :DOC wet. ACL2 !> During proofs, the error is silent; it is "caught" by the proof mechanism and generally results in the introduction of a call of hide during a proof. Also see *note DEFUN-NX:: for a utility that makes every call of a function non-executable, rather than a specified form. The following examples contrast non-exec with defun-nx, in particular illustratating the role of non-exec in avoiding guard proof obligations. ; Guard verification fails: (defun-nx f1 (x) (declare (xargs :guard t)) (car x)) ; Guard verification succeeds after changing the guard above: (defun-nx f1 (x) (declare (xargs :guard (consp x))) (car x)) ; Guard verification succeeds: (defun f2 (x) (declare (xargs :guard t)) (non-exec (car x))) ; Evaluating (g1) prints "Hello" before signaling an error. (defun g1 () (f1 (cw "Hello"))) ; Evaluating (g2) does not print before signaling an error. (defun g2 () (non-exec (cw "Hello"))) ; Evaluating (h1) gives a guard violation for taking reciprocal of 0. (defun h1 () (f1 (/ 1 0))) ; Evaluating (h2) does not take a reciprocal, hence there is no guard ; violation for that; we just get the error expected from using non-exec. (defun h2 () (non-exec (/ 0)))  File: acl2-doc-emacs.info, Node: NONNEGATIVE-INTEGER-QUOTIENT, Next: NOT, Prev: NON-EXEC, Up: ACL2-BUILT-INS NONNEGATIVE-INTEGER-QUOTIENT natural number division function Example Forms: (nonnegative-integer-quotient 14 3) ; equals 4 (nonnegative-integer-quotient 15 3) ; equals 5 (nonnegative-integer-quotient i j) returns the integer quotient of the integers i and (non-zero) j, i.e., the largest k such that (* j k) is less than or equal to i. Also see *note FLOOR::, see *note CEILING:: and see *note TRUNCATE::, which are derived from this function and apply to rational numbers. The guard of (nonnegative-integer-quotient i j) requires that i is a nonnegative integer and j is a positive integer. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NOT, Next: NTH, Prev: NONNEGATIVE-INTEGER-QUOTIENT, Up: ACL2-BUILT-INS NOT logical negation Not is the ACL2 negation function. The negation of nil is t and the negation of anything else is nil. Not is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NTH, Next: NTHCDR, Prev: NOT, Up: ACL2-BUILT-INS NTH the nth element (zero-based) of a list (Nth n l) is the nth element of l, zero-based. If n is greater than or equal to the length of l, then nth returns nil. (Nth n l) has a guard that n is a non-negative integer and l is a true-listp. Nth is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NTHCDR, Next: NULL, Prev: NTH, Up: ACL2-BUILT-INS NTHCDR final segment of a list (Nthcdr n l) removes the first n elements from the list l. The following is a theorem. (implies (and (integerp n) (<= 0 n) (true-listp l)) (equal (length (nthcdr n l)) (if (<= n (length l)) (- (length l) n) 0))) For related functions, see *note TAKE:: and see *note BUTLAST::. The guard of (nthcdr n l) requires that n is a nonnegative integer and l is a true list. Nthcdr is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NULL, Next: NUMERATOR, Prev: NTHCDR, Up: ACL2-BUILT-INS NULL recognizer for the empty list Null is the function that checks whether its argument is nil. For recursive definitions it is often preferable to test for the end of a list using endp instead of null; see *note ENDP::. Null is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: NUMERATOR, Next: O-FINP, Prev: NULL, Up: ACL2-BUILT-INS NUMERATOR dividend of a ratio in lowest terms Completion Axiom (completion-of-numerator): (equal (numerator x) (if (rationalp x) (numerator x) 0)) Guard for (numerator x): (rationalp x)  File: acl2-doc-emacs.info, Node: O-FINP, Next: O-FIRST-COEFF, Prev: NUMERATOR, Up: ACL2-BUILT-INS O-FINP recognizes if an ordinal is finite We introduce the function o-finp which returns t for any ordinal that is finite, else nil. This function is equivalent to the function atom, and is introduced so that we can disable its definition when dealing with ordinals (also see *note MAKE-ORD::). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: O-FIRST-COEFF, Next: O-FIRST-EXPT, Prev: O-FINP, Up: ACL2-BUILT-INS O-FIRST-COEFF returns the first coefficient of an ordinal An ACL2 ordinal is either a natural number or, for an infinite ordinal, a list whose elements are exponent-coefficient pairs (see *note O-P::). In the latter case, this function returns the cdr of the first pair in the list. In the case of a natural number, this function returns the ordinal itself (since a natural number, n, can be thought of as (w^0)n). For the corresponding exponent, see *note O-FIRST-EXPT::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: O-FIRST-EXPT, Next: O-INFP, Prev: O-FIRST-COEFF, Up: ACL2-BUILT-INS O-FIRST-EXPT the first exponent of an ordinal An ACL2 ordinal is either a natural number or, for an infinite ordinal, a list whose elements are exponent-coefficient pairs (see *note O-P::). In the latter case, this function returns the car of the first pair in the list. In the case of a natural number, the value returned is 0 (since a natural number, n, can be thought of as (w^0)n). For the corresponding coefficient, see *note O-FIRST-COEFF::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: O-INFP, Next: O-P, Prev: O-FIRST-EXPT, Up: ACL2-BUILT-INS O-INFP recognizes if an ordinal is infinite O-infp is a macro. (O-infp x) opens up to (not (o-finp x)) (see *note O-FINP::).  File: acl2-doc-emacs.info, Node: O-P, Next: O-RST, Prev: O-INFP, Up: ACL2-BUILT-INS O-P a recognizer for the ordinals up to epsilon-0 Using the nonnegative integers and lists we can represent the ordinals up to epsilon-0. The ordinal representation used in ACL2 has changed as of Version_2.8 from that of Nqthm-1992, courtesy of Pete Manolios and Daron Vroon; additional discussion may be found in "Ordinal Arithmetic in ACL2", proceedings of ACL2 Workshop 2003, `http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/'. Previously, ACL2's notion of ordinal was very similar to the development given in "New Version of the Consistency Proof for Elementary Number Theory" in The Collected Papers of Gerhard Gentzen, ed. M.E. Szabo, North-Holland Publishing Company, Amsterdam, 1969, pp 132-213. The following essay is intended to provide intuition about ordinals. The truth, of course, lies simply in the ACL2 definitions of o-p and o<. Very intuitively, think of each non-zero natural number as by being denoted by a series of the appropriate number of strokes, i.e., 0 0 1 | 2 || 3 ||| 4 |||| ... ... Then "omega," here written as w, is the ordinal that might be written as w |||||..., i.e., an infinite number of strokes. Addition here is just concatenation. Observe that adding one to the front of w in the picture above produces w again, which gives rise to a standard definition of w: w is the least ordinal such that adding another stroke at the beginning does not change the ordinal. We denote by w+w or w*2 the "doubly infinite" sequence that we might write as follows. w*2 |||||... |||||... One way to think of w*2 is that it is obtained by replacing each stroke in 2 (||) by w. Thus, one can imagine w*3, w*4, etc., which leads ultimately to the idea of "w*w," the ordinal obtained by replacing each stroke in w by w. This is also written as "omega squared" or w^2, or: 2 w |||||... |||||... |||||... |||||... |||||... ... We can analogously construct w^3 by replacing each stroke in w by w^2 (which, it turns out, is the same as replacing each stroke in w^2 by w). That is, we can construct w^3 as w copies of w^2, 3 2 2 2 2 w w ... w ... w ... w ... ... Then we can construct w^4 as w copies of w^3, w^5 as w copies of w^4, etc., ultimately suggesting w^w. We can then stack omegas, i.e., (w^w)^w etc. Consider the "limit" of all of those stacks, which we might display as follows. . . . w w w w w That is epsilon-0. Below we begin listing some ordinals up to epsilon-0; the reader can fill in the gaps at his or her leisure. We show in the left column the conventional notation, using w as "omega," and in the right column the ACL2 object representing the corresponding ordinal. ordinal ACL2 representation 0 0 1 1 2 2 3 3 ... ... w '((1 . 1) . 0) w+1 '((1 . 1) . 1) w+2 '((1 . 1) . 2) ... ... w*2 '((1 . 2) . 0) (w*2)+1 '((1 . 2) . 1) ... ... w*3 '((1 . 3) . 0) (w*3)+1 '((1 . 3) . 1) ... ... 2 w '((2 . 1) . 0) ... ... 2 w +w*4+3 '((2 . 1) (1 . 4) . 3) ... ... 3 w '((3 . 1) . 0) ... ... w w '((((1 . 1) . 0) . 1) . 0) ... ... w 99 w +w +w4+3 '((((1 . 1) . 0) . 1) (99 . 1) (1 . 4) . 3) ... ... 2 w w '((((2 . 1) . 0) . 1) . 0) ... ... w w w '((((((1 . 1) . 0) . 1) . 0) . 1) . 0) ... ... Observe that the sequence of o-ps starts with the natural numbers (which are recognized by natp). This is convenient because it means that if a term, such as a measure expression for justifying a recursive function (see *note O<::) must produce an o-p, it suffices for it to produce a natural number. The ordinals listed above are listed in ascending order. This is the ordering tested by o<. The "epsilon-0 ordinals" of ACL2 are recognized by the recursively defined function o-p. The base case of the recursion tells us that natural numbers are epsilon-0 ordinals. Otherwise, an epsilon-0 ordinal is a list of cons pairs whose final cdr is a natural number, ((a1 . x1) (a2 . x2) ... (an . xn) . p). This corresponds to the ordinal (w^a1)x1 + (w^a2)x2 + ... + (w^an)xn + p. Each ai is an ordinal in the ACL2 representation that is not equal to 0. The sequence of the ai's is strictly decreasing (as defined by o<). Each xi is a positive integer (as recognized by posp). Note that infinite ordinals should generally be created using the ordinal constructor, make-ord, rather than cons. The functions o-first-expt, o-first-coeff, and o-rst are ordinals destructors. Finally, the function o-finp and the macro o-infp tell whether an ordinal is finite or infinite, respectively. The function o< compares two epsilon-0 ordinals, x and y. If both are integers, (o< x y) is just x, Prev: O<, Up: ACL2-BUILT-INS O<= the less-than-or-equal relation for the ordinals o<= is a macro and (o<= x y) expands to (not (o< y x)). See *note O<::.  File: acl2-doc-emacs.info, Node: O>, Next: O>=, Prev: O<=, Up: ACL2-BUILT-INS O> the greater-than relation for the ordinals O> is a macro and (o> x y) expands to (o< y x). See *note O<::.  File: acl2-doc-emacs.info, Node: O>=, Next: OBSERVATION, Prev: O>, Up: ACL2-BUILT-INS O>= the greater-than-or-equal relation for the ordinals O>= is a macro and (o>= x y) expands to (not (o< x y)). See *note O<::.  File: acl2-doc-emacs.info, Node: OBSERVATION, Next: OBSERVATION-CW, Prev: O>=, Up: ACL2-BUILT-INS OBSERVATION print an observation Here is a typical application of observation. ACL2 !>(let ((ctx 'top-level) (name 'foo)) (observation ctx "Skipping processing of name ~x0." name)) ACL2 Observation in TOP-LEVEL: Skipping processing of name FOO. ACL2 !> Observation prints an initial "ACL2 Observation...: ", and then prints the indicated message using formatted printing (see *note FMT::). Notice in the example above that evaluation of a call of observation returns state. Indeed, observation is actually a macro whose expansion takes and returns the ACL2 state. A similar utility, observation-cw, is available that does not take or return state; rather, it returns nil as the suffix "cw" suggests that a "comment window" is the target of this printing, rather than the state. For example: ACL2 !>(let ((ctx 'top-level) (name 'foo)) (observation-cw ctx "Skipping processing of name ~x0." name)) ACL2 Observation in TOP-LEVEL: Skipping processing of name FOO. NIL ACL2 !> Observation-cw takes exactly the same arguments as observation, but observation-cw does its printing in a so-called "wormhole"; see *note WORMHOLE::. General Forms: (observation ctx fmt-string fmt-arg1 fmt-arg2 ... fmt-argk) (observation-cw ctx fmt-string fmt-arg1 fmt-arg2 ... fmt-argk) where ctx generally evaluates to a symbol (but see below), and fmt-string together with the fmt-argi are suitable for passing to fmt. Output begins and ends with a newline. Recall from the example above that the output from a call of observation (or observation-cw) begins with "ACL2 Observation" and additional characters ending in ": ", for example " in TOP-LEVEL: ", followed by formatted output produced from fmt-string with the given fmt-argi. The characters printed immediately following the string "ACL2 Observation" depend on the value of ctx. If ctx is nil, nothing is printed. If ctx is a non-nil symbol, it is printed using fmt directive ~x. If ctx is a cons pair whose car is a symbol, formatted printing is applied to the string "(~x0 ~x1 ...)", where #\0 and #\1 are bound respectively to that car and cdr. Otherwise, ctx is printed using fmt directive ~@. We next discuss situations in which printing is inhibited for observation and observation-cw. No printing is done when observation is among the inhibited output types; see *note SET-INHIBIT-OUTPUT-LST::. Moreover, no printing is done by observation during include-book. If you want to avoid printing from observation-cw during include-book, then you need to manage that yourself.  File: acl2-doc-emacs.info, Node: OBSERVATION-CW, Next: ODDP, Prev: OBSERVATION, Up: ACL2-BUILT-INS OBSERVATION-CW See *note OBSERVATION::.  File: acl2-doc-emacs.info, Node: ODDP, Next: OPEN-INPUT-CHANNEL, Prev: OBSERVATION-CW, Up: ACL2-BUILT-INS ODDP test whether an integer is odd (oddp x) is true if and only if x is odd, i.e., not even in the sense of evenp. The guard for oddp requires its argument to be an integer. Oddp is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: OPEN-INPUT-CHANNEL, Next: OPEN-INPUT-CHANNEL-P, Prev: ODDP, Up: ACL2-BUILT-INS OPEN-INPUT-CHANNEL See *note IO::.  File: acl2-doc-emacs.info, Node: OPEN-INPUT-CHANNEL-P, Next: OPEN-OUTPUT-CHANNEL, Prev: OPEN-INPUT-CHANNEL, Up: ACL2-BUILT-INS OPEN-INPUT-CHANNEL-P See *note IO::.  File: acl2-doc-emacs.info, Node: OPEN-OUTPUT-CHANNEL, Next: OPEN-OUTPUT-CHANNEL-P, Prev: OPEN-INPUT-CHANNEL-P, Up: ACL2-BUILT-INS OPEN-OUTPUT-CHANNEL See *note IO::.  File: acl2-doc-emacs.info, Node: OPEN-OUTPUT-CHANNEL-P, Next: OR, Prev: OPEN-OUTPUT-CHANNEL, Up: ACL2-BUILT-INS OPEN-OUTPUT-CHANNEL-P See *note IO::.  File: acl2-doc-emacs.info, Node: OR, Next: ORACLE-APPLY, Prev: OPEN-OUTPUT-CHANNEL-P, Up: ACL2-BUILT-INS OR disjunction Or is the macro for disjunctions. Or takes any number of arguments and returns the first that is non-nil, or nil if there is no non-nil element. In the ACL2 logic, the macroexpansion of (or x y) is an IF term that appears to cause x to be evaluated twice: ACL2 !>:trans (or x y) (IF X X Y) => * ACL2 !> If x were replaced by an expression whose evaluation takes a long time, then such an expansion would be ineffecient. However, don't be fooled: you can expect Common Lisp implementations to avoid this problem, say by generating a new variable, for example: ACL2 !>:q ; Exit the ACL2 loop and go into raw Common Lisp Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ACL2>(macroexpand '(or x y)) (LET ((#:G5374 X)) (IF #:G5374 #:G5374 Y)) T ACL2> Or is a Common Lisp macro. See any Common Lisp documentation for more information. acl2-sources/doc/EMACS/acl2-doc-emacs.info-90000664002132200015000000111020512222333541017642 0ustar kaufmannacl2This is acl2-doc-emacs.info, produced by makeinfo version 4.13 from acl2-doc-emacs.texinfo. This is documentation for ACL2 Version 6.3 Copyright (C) 2013 University of Texas at Austin This program is free software; you can redistribute it and/or modify it under the terms of Version 2 of the GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. Written by: Matt Kaufmann and J Strother Moore Department of Computer Sciences University of Texas at Austin Austin, TX 78712-1188 U.S.A. INFO-DIR-SECTION Math START-INFO-DIR-ENTRY * acl2: (acl2-doc-emacs). Applicative Common Lisp END-INFO-DIR-ENTRY  File: acl2-doc-emacs.info, Node: ORACLE-APPLY, Next: ORACLE-APPLY-RAW, Prev: OR, Up: ACL2-BUILT-INS ORACLE-APPLY call a function argument on the given list of arguments Oracle-apply evaluates its first argument to produce an ACL2 function symbol, FN, and then applies FN to the value of the second argument, which should be a true list whose length is the number of inputs for FN. The return value is of the form (mv call-result state). Examples: (oracle-apply 'cons '(3 4) state) = (mv '(3 . 4) ) (oracle-apply (car '(floor foo)) (list (+ 6 7) 5) state) = (mv 2 ) Also see *note ORACLE-FUNCALL:: for a related utility. Note that calls of oracle-funcall and oracle-apply return two values: the result of the function application, and a modified state. Oracle-apply is defined in :logic mode, and in fact is guard-verified. However, you will not be able to prove much about this function, because it is defined in the logic using the acl2-oracle field of the ACL2 state. The behavior described above -- i.e., making a function call -- takes place when the third argument is the ACL2 state, so during proofs (when that can never happen), a term (oracle-apply 'fn '...) will not simplify using a call of fn. The guard for (oracle-apply fn args state) is the term (oracle-apply-guard fn args state), which says the following: fn and args must satisfy symbolp and true-listp, respectively; fn must be a known function symbol other than return-last that is not untouchable (see *note PUSH-UNTOUCHABLE::) and has no stobj arguments (not even state); and the length of args must equal the arity of fn (see *note SIGNATURE::). The requirement that fn be a known function symbol may be a bit onerous for guard verification, but this is easily overcome by using ec-call, for example as follows. (defun f (x state) (declare (xargs :stobjs state)) (ec-call (oracle-apply 'car (list x) state))) This use of ec-call will, however, cause the guard of oracle-apply to be checked at runtime. If the guard for oracle-apply fails to hold but there is no guard violation because guard-checking is suppressed (see *note SET-GUARD-CHECKING::), then the value returned is computed using its logical definition -- which, as mentioned above, uses the ACL2 oracle -- and hence the value computed is unpredictable (indeed, the function argument will not actually be called). The value returned by oracle-apply is always a single value obtained by calling the executable counterpart of its function argument, as we now explain. Consider a form (oracle-apply fn args state) that evaluates to (mv VAL state'), where fn evaluates to the function symbol F. If F returns multiple values, then VAL is the first value computed by the call of F on the value of args. More precisely, oracle-apply actually invokes the executable counterpart of F; thus, if args is the expression (list x1 ... xk), then VAL is the same as (first) value returned by evaluating (ec-call (F x1 x2 ... xk)). See *note EC-CALL::. (Remark. If you identify a need for a version of oracle-apply to return multiple values, we can perhaps provide such a utility; feel free to contact the ACL2 implementors to request it.) A subtlety is that the evaluation takes place in so-called "safe mode", which avoids raw Lisp errors due to calls of :program mode functions. The use of safe mode is unlikely to be noticed if the value of the first argument of oracle-apply is a :logic mode function symbol. However, for :program mode functions with side effects due to special raw Lisp code, as may be the case for built-in functions or for custom functions defined with active trust tags (see *note DEFTTAG::), use of the following function may be preferable: See *note ORACLE-APPLY-RAW:: for a much less restrictive version of oracle-apply, which avoids safe mode and (for example) can apply a function that has a definition in the host Lisp but not in the ACL2 world.  File: acl2-doc-emacs.info, Node: ORACLE-APPLY-RAW, Next: ORACLE-FUNCALL, Prev: ORACLE-APPLY, Up: ACL2-BUILT-INS ORACLE-APPLY-RAW call a function argument on the given list of arguments, no restrictions See *note ORACLE-APPLY::, as we assume familiarity with that function. Oracle-apply-raw is a variant of oracle-apply that is untouchable, and hence requires a trust tag to remove the untouchability (see *note DEFTTAG:: and see *note REMOVE-UNTOUCHABLE::). Unlike oracle-apply, oracle-apply-raw simply calls the raw Lisp function funcall to compute the result, without restriction: the specified :guard is t, the function itself is applied (not its executable counterpart), there is no restriction for untouchable functions or return-last, and safe mode is not used. Thus, in general, oracle-apply-raw can be dangerous to use: any manner of error can occur! As is the case for oracle-apply, the function symbol oracle-apply-raw is defined in :logic mode and is guard-verified. Oracle-apply-raw is logically defined to be oracle-apply; more precisely: (oracle-apply-raw fn args state) = {logical definition} (ec-call (oracle-apply fn args state))  File: acl2-doc-emacs.info, Node: ORACLE-FUNCALL, Next: PAIRLIS, Prev: ORACLE-APPLY-RAW, Up: ACL2-BUILT-INS ORACLE-FUNCALL call a function argument on the remaining arguments Oracle-funcall evaluates its first argument to produce an ACL2 function symbol, and then applies that function symbol to the values of the rest of the arguments. The return value is of the form (mv call-result state). Examples: (oracle-funcall 'cons 3 4) ==> (mv '(3 . 4) ) (oracle-funcall (car '(floor foo bar)) (+ 6 7) 5) ==> (mv 2 ) Oracle-funcall is a macro; each of its calls macroexpands to a call of the related utility oracle-apply that takes the ACL2 state as an argument, as follows: (oracle-funcall fn x1 x2 .. xk) macroexpands to (oracle-apply fn (list x1 x2 .. xk) state) Note that calls of oracle-funcall and oracle-apply return two values: the result of the function application, and a modified state. See *note ORACLE-APPLY:: for details, including information about guards.  File: acl2-doc-emacs.info, Node: PAIRLIS, Next: PAIRLIS$, Prev: ORACLE-FUNCALL, Up: ACL2-BUILT-INS PAIRLIS See *note PAIRLIS$:: The Common Lisp language allows its pairlis function to construct an alist in any order! So we have to define our own version: See *note PAIRLIS$::.  File: acl2-doc-emacs.info, Node: PAIRLIS$, Next: PEEK-CHAR$, Prev: PAIRLIS, Up: ACL2-BUILT-INS PAIRLIS$ zipper together two lists The Common Lisp language allows its pairlis function to construct an alist in any order! So we have to define our own version, pairlis$. It returns the list of pairs obtained by consing together successive respective members of the given lists until the first list runs out. (Hence in particular, if the second argument is nil then each element of the first argument is paired with nil.) The guard for pairlis$ requires that its arguments are true lists. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: PEEK-CHAR$, Next: PKG-IMPORTS, Prev: PAIRLIS$, Up: ACL2-BUILT-INS PEEK-CHAR$ See *note IO::.  File: acl2-doc-emacs.info, Node: PKG-IMPORTS, Next: PKG-WITNESS, Prev: PEEK-CHAR$, Up: ACL2-BUILT-INS PKG-IMPORTS list of symbols imported into a given package Completion Axiom (completion-of-pkg-imports): (equal (pkg-imports x) (if (stringp x) (pkg-imports x) nil)) Guard for (pkg-imports x): (stringp x) (Pkg-imports pkg) returns a duplicate-free list of all symbols imported into pkg, which should be the name of a package known to ACL2. For example, suppose "MY-PKG" was created by (defpkg "MY-PKG" '(ACL2::ABC LISP::CAR)). Then (pkg-imports "MY-PKG") equals the list (ACL2::ABC LISP::CAR). If pkg is not a string, then (pkg-imports pkg) is nil. If pkg is a string but not the name of a package known to ACL2, then the value of the form (pkg-imports pkg) is unspecified, and it evaluation will fail to yield a value. By "the symbols imported into pkg" we mean the symbols imported into pkg by the defpkg event that introduced pkg. There are no imports for built-in packages except for the "ACL2" package, which imports the symbols in the list value of the constant *common-lisp-symbols-from-main-lisp-package*. In particular, this is the case for the "COMMON-LISP" package. Users familiar with Common Lisp may find this surprising, since in actual Common Lisp implementations it is often the case that many symbols in that package are imported from other packages. However, ACL2 treats all symbols in the constant *common-lisp-symbols-from-main-lisp-package* as having a symbol-package-name of "COMMON-LISP", as though they were not imported. ACL2 admits a symbol imported into in the "COMMON-LISP" package only if it belongs to that list: any attempt to read any other symbol imported into the "COMMON-LISP" package, or to produce such a symbol with intern$ or intern-in-package-of-symbol, will cause an error. The following axioms formalize properties of pkg-imports discussed above (use :pe to view them). symbol-package-name-intern-in-package-of-symbol intern-in-package-of-symbol-is-identity symbol-listp-pkg-imports no-duplicatesp-pkg-imports completion-of-pkg-imports  File: acl2-doc-emacs.info, Node: PKG-WITNESS, Next: PLUSP, Prev: PKG-IMPORTS, Up: ACL2-BUILT-INS PKG-WITNESS return a specific symbol in the indicated package For any string pkg that names a package currently known to ACL2, (pkg-witness pkg) is a symbol in that package whose symbol-name is the value of constant *pkg-witness-name*. Logically, this is the case even if the package is not currently known to ACL2. However, if pkg-witness is called on a string that is not the name of a package known to ACL2, a hard Lisp error will result. (Pkg-witness pkg) has a guard of (and (stringp pkg) (not (equal pkg ""))). If pkg is not a string, then (pkg-witness pkg) is equal to (pkg-witness "ACL2")  File: acl2-doc-emacs.info, Node: PLUSP, Next: POSITION, Prev: PKG-WITNESS, Up: ACL2-BUILT-INS PLUSP test whether a number is positive (Plusp x) is true if and only if x > 0. The guard of plusp requires its argument to be a rational (real, in ACL2(r)) number. Plusp is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: POSITION, Next: POSITION-EQ, Prev: PLUSP, Up: ACL2-BUILT-INS POSITION position of an item in a string or a list General Forms: (position x seq) (position x seq :test 'eql) ; same as above (eql as equality test) (position x seq :test 'eq) ; same, but eq is equality test (position x seq :test 'equal) ; same, but equal is equality test (Position x seq) is the least index (zero-based) of the element x in the string or list seq, if x is an element of seq. Otherwise (position x seq) is nil. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with items of seq. The guard for a call of position depends on the test. In all cases, the second argument must satisfy stringp or true-listp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between position and its variants: (position-eq x seq) is equivalent to (position x seq :test 'eq); (position-equal x seq) is equivalent to (position x seq :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function position-equal. Position is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: POSITION-EQ, Next: POSITION-EQUAL, Prev: POSITION, Up: ACL2-BUILT-INS POSITION-EQ See *note POSITION::.  File: acl2-doc-emacs.info, Node: POSITION-EQUAL, Next: POSP, Prev: POSITION-EQ, Up: ACL2-BUILT-INS POSITION-EQUAL See *note POSITION::.  File: acl2-doc-emacs.info, Node: POSP, Next: PPROGN, Prev: POSITION-EQUAL, Up: ACL2-BUILT-INS POSP a recognizer for the positive integers (posp x) is logically equivalent to (not (zp x)) (see *note ZP::) and also to (and (natp x) (not (equal x 0))). We recommend the community book books/ordinals/natp-posp for reasoning about posp and natp. This book is included by community books books/arithmetic/top and books/arithmetic/top-with-meta. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: PPROGN, Next: PRINC$, Prev: POSP, Up: ACL2-BUILT-INS PPROGN evaluate a sequence of forms that return state Example Form: (pprogn (cond ((or (equal (car l) #\) (equal (car l) slash-char)) (princ$ #\ channel state)) (t state)) (princ$ (car l) channel state) (mv (cdr l) state)) The convention for pprogn usage is to give it a non-empty sequence of forms, each of which (except possibly for the last) returns state (see *note STATE::) as its only value. The state returned by each but the last is passed on to the next. The value or values of the last form are returned as the value of the pprogn. If you are using single-threaded objects you may wish to define an analogue of this function for your own stobj. General Form: (PPROGN form1 form2 ... formk result-form) This general form is equivalent, via macro expansion, to: (LET ((STATE form1)) (LET ((STATE form2)) ... (LET ((STATE formk)) result-form)))  File: acl2-doc-emacs.info, Node: PRINC$, Next: PRINT-OBJECT$, Prev: PPROGN, Up: ACL2-BUILT-INS PRINC$ print an atom Use princ$ to do basic printing of atoms (i.e., other than cons pairs). In particular, princ$ prints a string without the surrounding double-quotes and without escaping double-quote characters within the string. Note that princ$ is sensitive to the print-base, print-radix, and print-case; see *note SET-PRINT-BASE::, see *note SET-PRINT-RADIX::, and see *note SET-PRINT-CASE::. Princ$ returns state. Examples: ACL2 !>(princ$ "Howdy ho" (standard-co state) state) Howdy ho ACL2 !>(pprogn (princ$ "Howdy ho" (standard-co state) state) (newline (standard-co state) state)) Howdy ho ACL2 !>(princ$ "ab\"cd" *standard-co* state) ab"cd ACL2 !> ACL2 !>(princ$ 17 *standard-co* state) 17 ACL2 !>(set-print-base 16 state) ACL2 !>(princ$ 17 *standard-co* state) 11 ACL2 !>(set-print-radix t state) ACL2 !>(princ$ 17 *standard-co* state) #x11 ACL2 !>(princ$ 'xyz *standard-co* state) XYZ ACL2 !>(set-print-case :downcase state) ACL2 !>(princ$ 'xyz *standard-co* state) xyz ACL2 !> The guard for (princ$ x channel state) is essentially as follows; see *note IO:: for an explanation of guards of certain built-in functions that take state, such as princ$. (and (or (acl2-numberp x) (characterp x) (stringp x) (symbolp x)) (state-p1 state-state) (symbolp channel) (open-output-channel-p1 channel :character state-state)) See *note FMT:: for more sophisticated printing routines, and see *note IO:: for general information about input and output.  File: acl2-doc-emacs.info, Node: PRINT-OBJECT$, Next: PROG2$, Prev: PRINC$, Up: ACL2-BUILT-INS PRINT-OBJECT$ See *note IO::.  File: acl2-doc-emacs.info, Node: PROG2$, Next: PROGN$, Prev: PRINT-OBJECT$, Up: ACL2-BUILT-INS PROG2$ execute two forms and return the value of the second one See *note HARD-ERROR::, see *note ILLEGAL::, and see *note CW:: for examples of functions to call in the first argument of prog2$. Also see *note PROGN$:: for an extension of prog2$ that handles than two arguments. Semantically, (Prog2$ x y) equals y; the value of x is ignored. However, x is first evaluated for side effect. Since the ACL2 programming language is applicative, there can be no logical impact of evaluating x. However, x may involve a call of a function such as hard-error or illegal, which can cause so-called "hard errors", or a call of cw to perform output. Here is a simple, contrived example using hard-error. The intention is to check at run-time that the input is appropriate before calling function bar. (defun foo-a (x) (declare (xargs :guard (consp x))) (prog2$ (or (good-car-p (car x)) (hard-error 'foo-a "Bad value for x: ~p0" (list (cons #\0 x)))) (bar x))) The following similar function uses illegal instead of hard-error. Since illegal has a guard of nil, guard verification would guarantee that the call of illegal below will never be made (at least when guard checking is on; see *note SET-GUARD-CHECKING::). (defun foo-b (x) (declare (xargs :guard (and (consp x) (good-car-p (car x))))) (prog2$ (or (good-car-p (car x)) (illegal 'foo-b "Bad value for x: ~p0" (list (cons #\0 x)))) (bar x))) We conclude with a simple example using cw from the ACL2 sources. (defun print-terms (terms iff-flg wrld) ; Print untranslations of the given terms with respect to iff-flg, following ; each with a newline. ; We use cw instead of the fmt functions because we want to be able to use this ; function in print-type-alist-segments (used in brkpt1), which does not return ; state. (if (endp terms) terms (prog2$ (cw "~q0" (untranslate (car terms) iff-flg wrld)) (print-terms (cdr terms) iff-flg wrld))))  File: acl2-doc-emacs.info, Node: PROGN$, Next: PROOFS-CO, Prev: PROG2$, Up: ACL2-BUILT-INS PROGN$ execute a sequence of forms and return the value of the last one This macro expands to a corresponding nest of calls of prog2$; see *note PROG2$::. The examples below show how this works: the first case below is typical, but we conclude with two special cases. ACL2 !>:trans1 (progn$ (f1 x) (f2 x) (f3 x)) (PROG2$ (F1 X) (PROG2$ (F2 X) (F3 X))) ACL2 !>:trans1 (progn$ (f1 x) (f2 x)) (PROG2$ (F1 X) (F2 X)) ACL2 !>:trans1 (progn$ (f1 x)) (F1 X) ACL2 !>:trans1 (progn$) NIL ACL2 !>  File: acl2-doc-emacs.info, Node: PROOFS-CO, Next: PROPER-CONSP, Prev: PROGN$, Up: ACL2-BUILT-INS PROOFS-CO the proofs character output channel Proofs-co is an ld special (see *note LD::). The accessor is (proofs-co state) and the updater is (set-proofs-co val state). Proofs-co must be an open character output channel. It is to this channel that defun, defthm, and the other event commands print their commentary. "Proofs-co" stands for "proofs character output." The initial value of proofs-co is the same as the value of *standard-co* (see *note *STANDARD-CO*::).  File: acl2-doc-emacs.info, Node: PROPER-CONSP, Next: PSEUDO-TERMP, Prev: PROOFS-CO, Up: ACL2-BUILT-INS PROPER-CONSP recognizer for proper (null-terminated) non-empty lists Proper-consp is the function that checks whether its argument is a non-empty list that ends in nil. Also see *note TRUE-LISTP::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: PSEUDO-TERMP, Next: PUT-ASSOC, Prev: PROPER-CONSP, Up: ACL2-BUILT-INS PSEUDO-TERMP a predicate for recognizing term-like s-expressions Example Forms: (pseudo-termp '(car (cons x 'nil))) ; has value t (pseudo-termp '(car x y z)) ; also has value t! (pseudo-termp '(delta (h x))) ; has value t (pseudo-termp '(delta (h x) . 7)) ; has value nil (not a true-listp) (pseudo-termp '((lambda (x) (car x)) b)) ; has value t (pseudo-termp '(if x y 123)) ; has value nil (123 is not quoted) (pseudo-termp '(if x y '123)) ; has value t If x is the quotation of a term, then (pseudo-termp x) is t. However, if x is not the quotation of a term it is not necessarily the case that (pseudo-termp x) is nil. See *note TERM:: for a discussion of the various meanings of the word "term" in ACL2. In its most strict sense, a term is either a legal variable symbol, a quoted constant, or the application of an n-ary function symbol or closed lambda-expression to n terms. By "legal variable symbol" we exclude constant symbols, such as t, nil, and *ts-rational*. By "quoted constants" we include 't (aka (quote t)), 'nil, '31, etc., and exclude constant names such as t, nil and *ts-rational*, unquoted constants such as 31 or 1/2, and ill-formed quote expressions such as (quote 3 4). By "closed lambda expression" we exclude expressions, such as (lambda (x) (cons x y)), containing free variables in their bodies. Terms typed by the user are translated into strict terms for internal use in ACL2. The predicate termp checks this strict sense of "term" with respect to a given ACL2 logical world; See *note WORLD::. Many ACL2 functions, such as the rewriter, require certain of their arguments to satisfy termp. However, as of this writing, termp is in :program mode and thus cannot be used effectively in conjectures to be proved. Furthermore, if regarded simply from the perspective of an effective guard for a term-processing function, termp checks many irrelevant things. (Does it really matter that the variable symbols encountered never start and end with an asterisk?) For these reasons, we have introduced the notion of a "pseudo-term" and embodied it in the predicate pseudo-termp, which is easier to check, does not require the logical world as input, has :logic mode, and is often perfectly suitable as a guard on term-processing functions. A pseudo-termp is either a symbol, a true list of length 2 beginning with the word quote, the application of an n-ary pseudo-lambda expression to a true list of n pseudo-terms, or the application of a symbol to a true list of n pseudo-termps. By an "n-ary pseudo-lambda expression" we mean an expression of the form (lambda (v1 ... vn) pterm), where the vi are symbols (but not necessarily distinct legal variable symbols) and pterm is a pseudo-termp. Metafunctions may use pseudo-termp as a guard.  File: acl2-doc-emacs.info, Node: PUT-ASSOC, Next: PUT-ASSOC-EQ, Prev: PSEUDO-TERMP, Up: ACL2-BUILT-INS PUT-ASSOC modify an association list by associating a value with a key General Forms: (put-assoc name val alist) (put-assoc name val alist :test 'eql) ; same as above (eql as equality test) (put-assoc name val alist :test 'eq) ; same, but eq is equality test (put-assoc name val alist :test 'equal) ; same, but equal is equality test (Put-assoc name val alist) returns an alist that is the same as the list alist, except that the first pair in alist with a car of name is replaced by (cons name val), if there is one. If there is no such pair, then (cons name val) is added at the end. Note that the order of the keys occurring in alist is unchanged (though a new key may be added). The guard for a call of put-assoc depends on the test. In all cases, the last argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the last argument must satisfy eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the last argument must satisfy symbol-alistp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between put-assoc and its variants: (put-assoc-eq name val alist) is equivalent to (put-assoc name val alist :test 'eq); (put-assoc-equal name val alist) is equivalent to (put-assoc name val alist :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function put-assoc-equal.  File: acl2-doc-emacs.info, Node: PUT-ASSOC-EQ, Next: PUT-ASSOC-EQL, Prev: PUT-ASSOC, Up: ACL2-BUILT-INS PUT-ASSOC-EQ See *note PUT-ASSOC::.  File: acl2-doc-emacs.info, Node: PUT-ASSOC-EQL, Next: PUT-ASSOC-EQUAL, Prev: PUT-ASSOC-EQ, Up: ACL2-BUILT-INS PUT-ASSOC-EQL See *note PUT-ASSOC::.  File: acl2-doc-emacs.info, Node: PUT-ASSOC-EQUAL, Next: PUTPROP, Prev: PUT-ASSOC-EQL, Up: ACL2-BUILT-INS PUT-ASSOC-EQUAL See *note PUT-ASSOC::.  File: acl2-doc-emacs.info, Node: PUTPROP, Next: QUOTE, Prev: PUT-ASSOC-EQUAL, Up: ACL2-BUILT-INS PUTPROP update fast property lists General form: (putprop symbol key value world-alist) See community book books/misc/getprop.lisp for an example that illustrates the use of ACL2 utilities getprop and putprop to take advantage of under-the-hood Lisp (hashed) property lists. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: QUOTE, Next: R-EQLABLE-ALISTP, Prev: PUTPROP, Up: ACL2-BUILT-INS QUOTE create a constant The form (quote x) evaluates to x. See any Common Lisp documentation.  File: acl2-doc-emacs.info, Node: R-EQLABLE-ALISTP, Next: R-SYMBOL-ALISTP, Prev: QUOTE, Up: ACL2-BUILT-INS R-EQLABLE-ALISTP recognizer for a true list of pairs whose cdrs are suitable for eql The predicate r-eqlable-alistp tests whether its argument is a true-listp of consp objects whose cdrs all satisfy eqlablep. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: R-SYMBOL-ALISTP, Next: RANDOM$, Prev: R-EQLABLE-ALISTP, Up: ACL2-BUILT-INS R-SYMBOL-ALISTP recognizer for association lists with symbols as values (R-symbol-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where val is a symbolp. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: RANDOM$, Next: RASSOC, Prev: R-SYMBOL-ALISTP, Up: ACL2-BUILT-INS RANDOM$ obtain a random value Example: (random$ 10 state) ==> (mv 7 ) (Random$ limit state), where limit is a positive integer, returns a random non-negative integer together with a new state. Logically, it simply returns the first element of a list that is a field of the ACL2 state, called the acl2-oracle, together with the new state resulting from removing that element from that list. (Except, if that element is not in range as specified above, then 0 is returned.) However, random$ actually invokes a Common Lisp function to choose the integer returned. Quoting from the Common Lisp HyperSpec(TM), `http://www.lispworks.com/documentation/HyperSpec/Front': "An approximately uniform choice distribution is used... each of the possible results occurs with (approximate) probability 1/limit." Consider enabling rules natp-random$ and random$-linear if you want to reason about random$.  File: acl2-doc-emacs.info, Node: RASSOC, Next: RASSOC-EQ, Prev: RANDOM$, Up: ACL2-BUILT-INS RASSOC look up value in association list General Forms: (rassoc x alist) (rassoc x alist :test 'eql) ; same as above (eql as equality test) (rassoc x alist :test 'eq) ; same, but eq is equality test (rassoc x alist :test 'equal) ; same, but equal is equality test (Rassoc x alist) is the first member of alist whose cdr is x, or nil if no such member exists. (rassoc x alist) is thus similar to (assoc x alist), the difference being that it looks for the first pair in the given alist whose cdr, rather than car, is eql to x. See *note ASSOC::. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with the cdrs of successive elements of lst. The guard for a call of rassoc depends on the test. In all cases, the second argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy r-eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy r-symbol-alistp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between rassoc and its variants: (rassoc-eq x lst) is equivalent to (rassoc x lst :test 'eq); (rassoc-equal x lst) is equivalent to (rassoc x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function rassoc-equal. Rassoc is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: RASSOC-EQ, Next: RASSOC-EQUAL, Prev: RASSOC, Up: ACL2-BUILT-INS RASSOC-EQ See *note RASSOC::.  File: acl2-doc-emacs.info, Node: RASSOC-EQUAL, Next: RATIONAL-LISTP, Prev: RASSOC-EQ, Up: ACL2-BUILT-INS RASSOC-EQUAL See *note RASSOC::.  File: acl2-doc-emacs.info, Node: RATIONAL-LISTP, Next: RATIONALP, Prev: RASSOC-EQUAL, Up: ACL2-BUILT-INS RATIONAL-LISTP recognizer for a true list of rational numbers The predicate rational-listp tests whether its argument is a true list of rational numbers. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: RATIONALP, Next: READ-BYTE$, Prev: RATIONAL-LISTP, Up: ACL2-BUILT-INS RATIONALP recognizer for rational numbers (ratios and integers) (rationalp x) is true if and only if x is an rational number.  File: acl2-doc-emacs.info, Node: READ-BYTE$, Next: READ-CHAR$, Prev: RATIONALP, Up: ACL2-BUILT-INS READ-BYTE$ See *note IO::.  File: acl2-doc-emacs.info, Node: READ-CHAR$, Next: READ-OBJECT, Prev: READ-BYTE$, Up: ACL2-BUILT-INS READ-CHAR$ See *note IO::.  File: acl2-doc-emacs.info, Node: READ-OBJECT, Next: READ-RUN-TIME, Prev: READ-CHAR$, Up: ACL2-BUILT-INS READ-OBJECT See *note IO::.  File: acl2-doc-emacs.info, Node: READ-RUN-TIME, Next: REAL/RATIONALP, Prev: READ-OBJECT, Up: ACL2-BUILT-INS READ-RUN-TIME read elapsed runtime By default, (read-run-time state) returns (mv runtime state), where runtime is the elapsed runtime in seconds since the start of the current ACL2 session and state is the resulting ACL2 state. But read-run-time can be made to return elapsed realtime (wall clock time) instead; see *note GET-INTERNAL-TIME::. The logical definition probably won't concern many users, but for completeness, we say a word about it here. That definition uses the function read-acl2-oracle, which modifies state by popping the value to return from its acl2-oracle field.  File: acl2-doc-emacs.info, Node: REAL/RATIONALP, Next: REALFIX, Prev: READ-RUN-TIME, Up: ACL2-BUILT-INS REAL/RATIONALP recognizer for rational numbers (including real number in ACL2(r)) For most ACL2 users, this is a macro abbreviating rationalp. In ACL2(r) (see *note REAL::), this macro abbreviates the predicate realp, which holds for real numbers as well (including rationals). Most ACL2 users can ignore this macro and use rationalp instead, but many community books use real/rationalp so that these books will be suitable for ACL2(r) as well.  File: acl2-doc-emacs.info, Node: REALFIX, Next: REALPART, Prev: REAL/RATIONALP, Up: ACL2-BUILT-INS REALFIX coerce to a real number Realfix simply returns any real number argument unchanged, returning 0 on a non-real argument. Also see *note NFIX::, see *note IFIX::, see *note RFIX::, and see *note FIX:: for analogous functions that coerce to a natural number, an integer, a rational, and a number, respectively. Realfix has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: REALPART, Next: REM, Prev: REALFIX, Up: ACL2-BUILT-INS REALPART real part of a complex number Completion Axiom (completion-of-realpart): (equal (realpart x) (if (acl2-numberp x) (realpart x) 0)) Guard for (realpart x): (acl2-numberp x)  File: acl2-doc-emacs.info, Node: REM, Next: REMOVE, Prev: REALPART, Up: ACL2-BUILT-INS REM remainder using truncate ACL2 !>(rem 14 3) 2 ACL2 !>(rem -14 3) -2 ACL2 !>(rem 14 -3) 2 ACL2 !>(rem -14 -3) -2 ACL2 !>(rem -15 -3) 0 ACL2 !> (Rem i j) is that number k for which (* j (truncate i j)) added to k equals i. The guard for (rem i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero. Rem is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: REMOVE, Next: REMOVE-DUPLICATES, Prev: REM, Up: ACL2-BUILT-INS REMOVE remove all occurrences General Forms: (remove x lst) (remove x lst :test 'eql) ; same as above (eql as equality test) (remove x lst :test 'eq) ; same, but eq is equality test (remove x lst :test 'equal) ; same, but equal is equality test (Remove x lst) is equal to lst if x is not a member of lst, else is the result of removing all occurrences of x from lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst. Also see *note REMOVE1::. The guard for a call of remove depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between remove and its variants: (remove-eq x lst) is equivalent to (remove x lst :test 'eq); (remove-equal x lst) is equivalent to (remove x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function remove-equal. Remove is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: REMOVE-DUPLICATES, Next: REMOVE-DUPLICATES-EQ, Prev: REMOVE, Up: ACL2-BUILT-INS REMOVE-DUPLICATES remove duplicates from a string or a list General Forms: (remove-duplicates x) (remove-duplicates x :test 'eql) ; same as above (eql as equality test) (remove-duplicates x :test 'eq) ; same, but eq is equality test (remove-duplicates x :test 'equal) ; same, but equal is equality test (Remove-duplicates x) returns the result of deleting duplicate elements from the beginning of the list or string x. For example, (remove-duplicates '(1 2 3 2 4)) is equal to '(1 3 2 4). The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst. The guard for a call of remove-duplicates depends on the test. In all cases, the argument must satisfy stringp or true-listp. If the test is eql, then the argument must satisfy either stringp or eqlable-listp. If the test is eq, then the argument must satisfy symbol-listp. The relation between remove-duplicates and its variants is related to the usual pattern for equality variants; see *note EQUALITY-VARIANTS::. However, the possibility of a string argument changes the usual pattern a bit. As one might expect: (remove-duplicates-eq lst) is equivalent to (remove-duplicates lst :test 'eq). However, remove-duplicates-equal is defined without consideration of strings, for backward compatibility with versions of ACL2 through Version_4.2. The macro remove-duplicates-logic has been introduced to model the behavior of remove-duplicates even on strings; use :pe if you wish to see its definition. So we can say the following. (remove-duplicates-logic lst) is equivalent to (remove-duplicates lst :test 'equal); and (remove-duplicates-logic lst) is equal to (remove-duplicates-equal lst) when lst is not a string. In particular, when the argument is not a string, reasoning about any of these primitives reduces to reasoning about the function remove-duplicates-equal. Remove-duplicates is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: REMOVE-DUPLICATES-EQ, Next: REMOVE-DUPLICATES-EQUAL, Prev: REMOVE-DUPLICATES, Up: ACL2-BUILT-INS REMOVE-DUPLICATES-EQ See *note REMOVE-DUPLICATES::.  File: acl2-doc-emacs.info, Node: REMOVE-DUPLICATES-EQUAL, Next: REMOVE-EQ, Prev: REMOVE-DUPLICATES-EQ, Up: ACL2-BUILT-INS REMOVE-DUPLICATES-EQUAL See *note REMOVE-DUPLICATES::.  File: acl2-doc-emacs.info, Node: REMOVE-EQ, Next: REMOVE-EQUAL, Prev: REMOVE-DUPLICATES-EQUAL, Up: ACL2-BUILT-INS REMOVE-EQ See *note REMOVE::.  File: acl2-doc-emacs.info, Node: REMOVE-EQUAL, Next: REMOVE1, Prev: REMOVE-EQ, Up: ACL2-BUILT-INS REMOVE-EQUAL See *note REMOVE::.  File: acl2-doc-emacs.info, Node: REMOVE1, Next: REMOVE1-EQ, Prev: REMOVE-EQUAL, Up: ACL2-BUILT-INS REMOVE1 remove first occurrences, testing using eql General Forms: (remove1 x lst) (remove1 x lst :test 'eql) ; same as above (eql as equality test) (remove1 x lst :test 'eq) ; same, but eq is equality test (remove1 x lst :test 'equal) ; same, but equal is equality test (Remove1 x lst) is equal to lst if x is not a member of lst, else is the result of removing the first occurrences of x from lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst. Also see *note REMOVE::. The guard for a call of remove1 depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see *note EQLABLEP::) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between remove1 and its variants: (remove1-eq x lst) is equivalent to (remove1 x lst :test 'eq); (remove1-equal x lst) is equivalent to (remove1 x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function remove1-equal.  File: acl2-doc-emacs.info, Node: REMOVE1-EQ, Next: REMOVE1-EQUAL, Prev: REMOVE1, Up: ACL2-BUILT-INS REMOVE1-EQ See *note REMOVE1::.  File: acl2-doc-emacs.info, Node: REMOVE1-EQUAL, Next: REST, Prev: REMOVE1-EQ, Up: ACL2-BUILT-INS REMOVE1-EQUAL See *note REMOVE1::.  File: acl2-doc-emacs.info, Node: REST, Next: RETURN-LAST, Prev: REMOVE1-EQUAL, Up: ACL2-BUILT-INS REST rest (cdr) of the list In the logic, rest is just a macro for cdr. Rest is a Common Lisp function. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: RETURN-LAST, Next: REVAPPEND, Prev: REST, Up: ACL2-BUILT-INS RETURN-LAST return the last argument, perhaps with side effects Return-last is an ACL2 function that returns its last argument. It is used to implement common utilities such as prog2$ and time$. For most users, this may already be more than one needs to know about return-last; for example, ACL2 tends to avoid printing calls of return-last in its output, printing calls of prog2$ or time$ (or other such utilities) instead. If you encounter a call of return-last during a proof, then you may find it most useful to consider return-last simply as a function defined by the following equation. (equal (return-last x y z) z) It may also be useful to know that unlike other ACL2 functions, return-last can take a multiple value as its last argument, in which case this multiple value is returned. The following contrived definition illustrates this point. ACL2 !>(defun foo (fn x y z) (return-last fn x (mv y z))) Since FOO is non-recursive, its admission is trivial. We observe that the type of FOO is described by the theorem (AND (CONSP (FOO FN X Y Z)) (TRUE-LISTP (FOO FN X Y Z))). We used primitive type reasoning. (FOO * * * *) => (MV * *). Summary Form: ( DEFUN FOO ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)) Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) FOO ACL2 !>(foo 'bar 3 4 5) (4 5) ACL2 !>(mv-let (a b) (foo 'bar 3 4 5) (cons b a)) (5 . 4) ACL2 !> Most readers would be well served to avoid reading the rest of this documentation of return-last. For reference, however, below we document it in some detail. We include some discussion of its evaluation, in particular its behavior in raw Lisp, because we expect that most who read further are working with raw Lisp code (and trust tags). Return-last is an ACL2 function that can arise from macroexpansion of certain utilities that return their last argument, which may be a multiple value. Consider for example the simplest of these, prog2$: ACL2 !>:trans1 (prog2$ (cw "Some CW printing...~%") (+ 3 4)) (RETURN-LAST 'PROGN (CW "Some CW printing...~%") (+ 3 4)) ACL2 !> Notice that a call of prog2$ macroexpands to a call of return-last in which the first argument is (quote progn). Although return-last is a function in the ACL2 world, it is implemented "under the hood" as a macro in raw Lisp, as the following log (continuing the example above) illustrates. ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'PROGN (CW "Some CW printing...~%") (+ 3 4))) (PROGN (LET ((*AOKP* T)) (CW "SOME CW PRINTING...~%")) (+ 3 4)) T ? [RAW LISP] Thus, the original prog2$ call generates a corresponding call of progn in raw Lisp, which in turn causes evaluation of each argument and returns whatever is returned by evaluation of the last (second) argument. (Remark for those who use defattach. The binding of *aokp* to t is always included for the second argument as shown except when the first argument is of the form (QUOTE M) where M is a macro, or (less important) when the first argument is a symbol or a cons whose car is QUOTE. This binding allows ACL2 to use attachments in the second argument of return-last (hence, in the first argument of prog2$), even in contexts such as proofs in which attachments are normally not allowed. Those who use the experimental HONS version of ACL2 (see *note HONS-AND-MEMOIZATION::) will see an additional binding in the above single-step macroexpansion, which allows the storing of memoized results even when that would otherwise be prevented because of the use of attachments.) In general, a form (return-last (quote F) X Y) macroexpands to (F X Y), where F is defined in raw Lisp to return its last argument. The case that F is progn is a bit misleading, because it is so simple. More commonly, macroexpansion produces a call of a macro defined in raw Lisp that may produce side effects. Consider for example the ACL2 utility with-guard-checking, which is intended to change the guard-checking mode to the indicated value (see *note WITH-GUARD-CHECKING::). ACL2 !>(with-guard-checking :none (car 3)) ; no guard violation NIL ACL2 !>:trans1 (with-guard-checking :none (car 3)) (WITH-GUARD-CHECKING1 (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) ACL2 !>:trans1 (WITH-GUARD-CHECKING1 (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) (RETURN-LAST 'WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3))) (WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)) T ? [RAW LISP] (pprint (macroexpand-1 '(WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3)))) (LET ((ACL2_GLOBAL_ACL2::GUARD-CHECKING-ON (CHK-WITH-GUARD-CHECKING-ARG :NONE))) (DECLARE (SPECIAL ACL2_GLOBAL_ACL2::GUARD-CHECKING-ON)) (CAR 3)) ? [RAW LISP] The above raw Lisp code binds the state global variable guard-checking-on to :none, as chk-with-guard-checking-arg is just the identity function except for causing a hard error for an illegal input. The intended use of return-last is that the second argument is evaluated first in a normal manner, and then the third argument is evaluated in an environment that may depend on the value of the second argument. (For example, the macro with-prover-time-limit macroexpands to a call of return-last with a first argument of 'WITH-PROVER-TIME-LIMIT1-RAW, a second argument that evaluates to a numeric time limit, and a third argument that is evaluated in an environment where the theorem prover is restricted to avoid running longer than that time limit.) Although this intended usage model is not strictly enforced, it is useful to keep in mind in the following description of how calls of return-last are handled by the ACL2 evaluator. When a form is submitted in the top-level loop, it is handled by ACL2's custom evaluator. That evaluator is specified to respect the semantics of the expression supplied to it: briefly put, if an expression E evaluates to a value V, then the equality (equal E (quote V)) should be a theorem. Notice that this specification does not discuss the side-effects that may occur when evaluating a call of return-last, so we discuss that now. Suppose that the ACL2 evaluator encounters the call (return-last 'fn expr1 expr2). First it evaluates expr1. If this evaluation succeeds without error, then it constructs an expression of the form (fn *x* ev-form), where *x* is a Lisp variable bound to the result of evaluating expr1 and ev-form is a call of the evaluator for expr2. (Those who want implementation details are invited to look at function ev-rec-return-last in ACL2 source file translate.lisp.) There are exceptions if fn is progn, ec-call1-raw, with-guard-checking1-raw, or mbe1-raw, but the main idea is the same: do a reasonable job emulating the behavior of a raw-Lisp call of return-last. The following log shows how a time$ call can generate a call of the evaluator for the last argument of return-last (arguent expr2, above). We use :trans1 to show single-step macroexpansions, which indicate how a call of time$ expands to a call of return-last. The implementation actually binds the Lisp variable *RETURN-LAST-ARG3* to expr2 before calling the ACL2 evaluator, ev-rec. ACL2 !>:trans1 (time$ (+ 3 4)) (TIME$1 (LIST 0 NIL NIL NIL NIL) (+ 3 4)) ACL2 !>:trans1 (TIME$1 (LIST 0 NIL NIL NIL NIL) (+ 3 4)) (RETURN-LAST 'TIME$1-RAW (LIST 0 NIL NIL NIL NIL) (+ 3 4)) ACL2 !>(time$ (+ 3 4)) ; (EV-REC *RETURN-LAST-ARG3* ...) took ; 0.00 seconds realtime, 0.00 seconds runtime ; (1,120 bytes allocated). 7 ACL2 !> We now show how things can go wrong in other than the "intended use" case described above. In the example below, the macro mac-raw is operating directly on the syntactic representation of its first argument, which it obtains of course as the second argument of a return-last call. Again this "intended use" of return-last requires that argument to be evaluated and then only its result is relevant; its syntax is not supposed to matter. We emphasize that only top-level evaluation depends on this "intended use"; once evaluation is passed to Lisp, the issue disappears. We illustrate below how to use the top-level utility to avoid this issue; see *note TOP-LEVEL::. The example uses the utility defmacro-last to "install" special handling of the raw-Lisp macro mac-raw by return-last; later below we discuss defmacro-last. ACL2 !>(defttag t) TTAG NOTE: Adding ttag :T from the top level loop. T ACL2 !>(progn! (set-raw-mode t) (defmacro mac-raw (x y) `(progn (print (quote ,(cadr x))) (terpri) ; newline ,y))) Summary Form: ( PROGN! (SET-RAW-MODE T) ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) NIL ACL2 !>(defmacro-last mac) [[ ... output omitted ... ]] RETURN-LAST-TABLE ACL2 !>(return-last 'mac-raw '3 nil) *********************************************** ************ ABORTING from raw Lisp *********** Error: Fault during read of memory address #x120000300006 *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !>(top-level (return-last 'mac-raw '3 nil)) 3 NIL ACL2 !> We next describe how to extend the behavior of return-last. This requires an active trust tag (see *note DEFTTAG::), and is accomplished by extending a table provided by ACL2, see *note RETURN-LAST-TABLE::. Rather than using table events directly for this purpose, it is probably more convenient to use a macro, defmacro-last. We describe the community book books/misc/profiling.lisp in order to illustrate how this works. The events in that book include the following, which are described below. (defttag :profiling) (progn! (set-raw-mode t) (load (concatenate 'string (cbd) "profiling-raw.lsp"))) (defmacro-last with-profiling) The first event introduces a trust tag. The second loads a file into raw Lisp that defines a macro, with-profiling-raw, which can do profiling for the form to be evaluated. The third introduces an ACL2 macro with-profiling, whose calls expand into calls of the form (return-last (quote with-profiling-raw) & &). The third event also extends return-last-table so that these calls will expand in raw Lisp to calls of with-profiling-raw. The example above illustrates the following methodology for introducing a macro that returns its last argument but produces useful side-effects with raw Lisp code. (1) Introduce a trust tag (see *note DEFTTAG::). (2) Using progn!, load into raw Lisp a file defining a macro, RAW-NAME, that takes two arguments, returning its last (second) argument but using the first argument to produce desired side effects during evaluation of that last argument. (3) Evaluate the form (defmacro-last NAME :raw RAW-NAME). This will introduce NAME as an ACL2 macro that expands to a corresponding call of RAW-NAME (see (2) above) in raw Lisp. The specification of keyword value of :raw as RAW-NAME may be omitted if RAW-NAME is the result of modifying the symbol NAME by suffixing the string "-RAW" to the symbol-name of NAME. WARNING: Not every use of return-last can be soundly evaluated outside a function body. The reason is that ACL2's evaluator, ev-rec, recurs through terms that are presented in the top-level loop, and handles return-last calls in a special manner: basically, the call of ev-rec on the form (return-last 'mac-raw x y) leads to evaluation of a macro call of the form (mac-raw *return-last-arg2* (ev-rec ...)), where *return-last-arg2* is a global variable bound to the result of evaluating x with ev-rec. Consider the following example. (defttag t) (set-raw-mode-on state) (defmacro mac-raw (str y) ; print message is an atom `(let ((result (consp ,y)) (str ,str)) (or result (prog2$ (fmx ,str ',y) nil)))) (set-raw-mode-off state) (defmacro-last mac) ; Horrible error: (mac "Not a cons: ~x0~%" 17) ; Works, but probably many would consider it awkward to use top-level: (top-level (mac "Not a cons: ~x0~%" 17)) In such cases we suggest supplying keyword :top-level-ok nil to the call of defmacro-last, for example: (defmacro-last mac :top-level-ok nil) Then any attempt to call mac at the top level, as opposed to inside a function body, will cause a clean error before evaluation begins. It is useful to explore what is done by defmacro-last. ACL2 !>:trans1 (defmacro-last with-profiling) (PROGN (DEFMACRO WITH-PROFILING (X Y) (LIST 'RETURN-LAST (LIST 'QUOTE 'WITH-PROFILING-RAW) X Y)) (TABLE RETURN-LAST-TABLE 'WITH-PROFILING-RAW 'WITH-PROFILING)) ACL2 !>:trans1 (with-profiling '(assoc-eq fgetprop rewrite) (mini-proveall)) (RETURN-LAST 'WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL)) ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL))) (WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL)) T ? [RAW LISP] To understand the macro with-profiling-raw you could look at the community book loaded above: books/misc/profiling-raw.lsp. We mentioned above that ACL2 tends to print calls of prog2$ or time$ (or other such utilities) instead of calls of return-last. Here we elaborate that point. ACL2's `untranslate' utility treats (return-last (quote F) X Y) as (G X Y) if F corresponds to the symbol G in return-last-table. However, it is generally rare to encounter such a term during a proof, since calls of return-last are generally expanded away early during a proof. Calls of return-last that occur in code -- forms submitted in the top-level ACL2 loop, and definition bodies other than those marked as non-executable (see *note DEFUN-NX::) -- have the following restriction: if the first argument is of the form (quote F), then F must be an entry in return-last-table. There are however four exceptions: the following symbols are considered to be keys of return-last-table even if they are no longer associated with non-nil values, say because of a table event with keyword :clear. * progn, associated with prog2$ * mbe1-raw, associated with mbe1, a version of mbe * ec-call1-raw, associated with ec-call1 (a variant of ec-call) * with-guard-checking1-raw, associated with with-guard-checking1 (a variant of with-guard-checking) Note that because of its special status, it is illegal to trace return-last. We conclude by warning that as a user, you take responsibility for not compromising the soundness or error handling of ACL2 when you define a macro in raw Lisp and especially when you install it as a key of return-last-table, either directly or (more likely) using defmacro-last. In particular, be sure that you are defining a macro of two arguments that always returns the value of its last argument, returning the complete multiple value if that last argument evaluates to a multiple value. The following is correct, and illustrates care taken to return multiple values. :q (defmacro my-time1-raw (val form) (declare (ignore val)) `(let ((start-time (get-internal-run-time)) (result (multiple-value-list ,form)) (end-time (get-internal-run-time))) (format t "Total time: ~s~%" (float (/ (- end-time start-time) internal-time-units-per-second))) (values-list result))) (lp) (defttag t) (defmacro-last my-time1) (defmacro my-time (form) `(my-time1 nil ,form)) Then for example: ACL2 !>(my-time (equal (make-list 1000000) (make-list 1000000))) Total time: 0.12 T ACL2 !> But if instead we provide the following more naive implementation, of the above raw Lisp macro, the above evaluation can produce an error, for example if the host Lisp is CCL. (defmacro my-time1-raw (val form) (declare (ignore val)) `(let ((start-time (get-internal-run-time)) (result ,form) (end-time (get-internal-run-time))) (format t "Total time: ~s~%" (float (/ (- end-time start-time) internal-time-units-per-second))) result)) ; WRONG -- need multiple values returned! Here is a second, similar example. This time we'll start with the error; can you spot it? (defttag t) (progn! (set-raw-mode t) (defmacro foo-raw (x y) `(prog1 ,y (cw "Message showing argument 1: ~x0~%" ,x)))) (defmacro-last foo) We then can wind up with a hard Lisp error: ACL2 !>(foo 3 (mv 4 5)) Message showing argument 1: 3 *********************************************** ************ ABORTING from raw Lisp *********** Error: value NIL is not of the expected type REAL. *********************************************** If you didn't cause an explicit interrupt (Control-C), then the root cause may be call of a :program mode function that has the wrong guard specified, or even no guard specified (i.e., an implicit guard of t). See :DOC guards. To enable breaks into the debugger (also see :DOC acl2-customization): (SET-DEBUGGER-ENABLE T) ACL2 !> Here is a corrected version of the above macro. The point here is that prog1 returns a single value, while our-multiple-value-prog1 returns all the values that are returned by its first argument. (progn! (set-raw-mode t) (defmacro foo-raw (x y) `(our-multiple-value-prog1 ;; better ,y (cw "Message showing argument 1: ~x0~%" ,x)))) To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: REVAPPEND, Next: REVERSE, Prev: RETURN-LAST, Up: ACL2-BUILT-INS REVAPPEND concatentate the reverse of one list to another (Revappend x y) concatenates the reverse of the list x to y, which is also typically a list. The following theorem characterizes this English description. (equal (revappend x y) (append (reverse x) y)) Hint: This lemma follows immediately from the definition of reverse and the following lemma. (defthm revappend-append (equal (append (revappend x y) z) (revappend x (append y z)))) The guard for (revappend x y) requires that x is a true list. Revappend is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: REVERSE, Next: RFIX, Prev: REVAPPEND, Up: ACL2-BUILT-INS REVERSE reverse a list or string (Reverse x) is the result of reversing the order of the elements of the list or string x. The guard for reverse requires that its argument is a true list or a string. Reverse is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: RFIX, Next: ROUND, Prev: REVERSE, Up: ACL2-BUILT-INS RFIX coerce to a rational number Rfix simply returns any rational number argument unchanged, returning 0 on a non-rational argument. Also see *note NFIX::, see *note IFIX::, see *note REALFIX::, and see *note FIX:: for analogous functions that coerce to a natural number, an integer, a real, and a number, respectively. Rfix has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ROUND, Next: SEARCH, Prev: RFIX, Up: ACL2-BUILT-INS ROUND division returning an integer by rounding off Example Forms: ACL2 !>(round 14 3) 5 ACL2 !>(round -14 3) -5 ACL2 !>(round 14 -3) -5 ACL2 !>(round -14 -3) 5 ACL2 !>(round 13 3) 4 ACL2 !>(round -13 3) -4 ACL2 !>(round 13 -3) -4 ACL2 !>(round -13 -3) 4 ACL2 !>(round -15 -3) 5 ACL2 !>(round 15 -2) -8 (Round i j) is the result of taking the quotient of i and j and rounding off to the nearest integer. When the quotient is exactly halfway between consecutive integers, it rounds to the even one. The guard for (round i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero. Round is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 round function returns only a single value, To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SEARCH, Next: SECOND, Prev: ROUND, Up: ACL2-BUILT-INS SEARCH search for a string or list in another string or list Example Forms: (search "cd" "Cdabcdefcde") ; = 4, index of first match (search "cd" "Cdabcdefcde" :test 'equal) ; same as above (search "cd" "Cdabcdefcde" :from-end t) ; = 8, index of last match (search "cd" "Cdabcdefcde" :start1 1) ; = 1 (search "cd" "Cdabcdefcde" :start2 5) ; = 8 (search "cd" "Cdabcdefcde" :test 'char-equal) ; = 0 (case-insensitive) (search "ac" "Cdabcdefcde") ; = nil (search '(a b) '(9 8 a b 7 6)) ; = 2 General Form: (search seq1 seq2 &key from-end test start1 start2 end1 end2) Search indicates whether one string or list occurs as a (contiguous) subsequence of another string or list, respectively. It returns nil if no such match is found; otherwise it returns the (zero-based) index of the first match by default, but a non-nil value of keyword argument :from-end causes it to return the last match. The :test is equal by default. The other legal value for :test is char-equal, which can be given only for two strings, in which case the match is case-insensitive. Finally, values of :start1 and :end1 for the first sequence, and of :start2 and :end2 for the second sequence, bound the portion of the respective sequence used for deciding on a match, though the index returned is always an index into the second sequence as a whole. The guard for calls of search is given by a function, search-fn-guard, which has the following requirements. o The two arguments much both satisfy true-listp or else must both be strings, which must consist of standard characters (see *note STANDARD-CHAR-P::) if the :test is char-equal. o The :test must evaluate to one of the symbols equal or char-equal, where the latter is only allowed if the (first) two arguments are strings. o The values of :start1, :start2, :end1, and :end2 must all be natural numbers, where if omitted they default to 0, 0, the length len1 of the first argument, and the length len2 of the second argument, respectively. o If start1 is the value of :start1, defaulting as described just above, and similarly for the other start and end keywords and for lengths len1 and len2 as described just above, then: start1 <= end1 <= len1 and start2 <= end2 <= len2. Search is a Common Lisp function (actually, a macro in ACL2). See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: SECOND, Next: SET-DIFFERENCE$, Prev: SEARCH, Up: ACL2-BUILT-INS SECOND second member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: SET-DIFFERENCE$, Next: SET-DIFFERENCE-EQ, Prev: SECOND, Up: ACL2-BUILT-INS SET-DIFFERENCE$ elements of one list that are not elements of another General Forms: (set-difference$ l1 l2) (set-difference$ l1 l2 :test 'eql) ; same as above (eql as equality test) (set-difference$ l1 l2 :test 'eq) ; same, but eq is equality test (set-difference$ l1 l2 :test 'equal) ; same, but equal is equality test (Set-difference$ l1 l2) equals a list that contains the members of l1 that are not members of l2. More precisely, the resulting list is the same as one gets by deleting the members of l2 from l1, leaving the remaining elements in the same order as in l1. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists. The guard for a call of set-difference$ depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between set-difference$ and its variants: (set-difference-eq l1 l2) is equivalent to (set-difference$ l1 l2 :test 'eq); (set-difference-equal l1 l2) is equivalent to (set-difference$ l1 l2 :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function set-difference-equal. Set-difference$ is similar to the Common Lisp primitive set-difference. However, Common Lisp does not specify the order of elements in the result of a call of set-difference.  File: acl2-doc-emacs.info, Node: SET-DIFFERENCE-EQ, Next: SET-DIFFERENCE-EQUAL, Prev: SET-DIFFERENCE$, Up: ACL2-BUILT-INS SET-DIFFERENCE-EQ See *note SET-DIFFERENCE$::.  File: acl2-doc-emacs.info, Node: SET-DIFFERENCE-EQUAL, Next: SETENV$, Prev: SET-DIFFERENCE-EQ, Up: ACL2-BUILT-INS SET-DIFFERENCE-EQUAL See *note SET-DIFFERENCE$::.  File: acl2-doc-emacs.info, Node: SETENV$, Next: SEVENTH, Prev: SET-DIFFERENCE-EQUAL, Up: ACL2-BUILT-INS SETENV$ set an environment variable (Setenv$ str val), where str and val are strings, sets the environment variable str to have value val, for subsequent read by getenv$ (see *note GETENV$::), and returns nil. Or, if this operation is not implemented for the host Common Lisp, an error will occur. Example: (setenv$ "FOO" "BAR") It may be surprising that setenv$ returns nil; indeed, it neither takes nor returns the ACL2 state. The reason is that getenv$ takes responsibility for trafficking in state; it is defined in the logic using the function read-acl2-oracle, which (again, in the logic) does modify state, by popping an entry from its acl2-oracle field. getenv$.  File: acl2-doc-emacs.info, Node: SEVENTH, Next: SIGNED-BYTE-P, Prev: SETENV$, Up: ACL2-BUILT-INS SEVENTH seventh member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: SIGNED-BYTE-P, Next: SIGNUM, Prev: SEVENTH, Up: ACL2-BUILT-INS SIGNED-BYTE-P recognizer for signed integers that fit in a specified bit width (Signed-byte-p bits x) is T when bits is a positive integer and x is a signed integer whose 2's complement representation fits in a bit-width of bits, i.e., -2^(bits-1) <= x < 2^(bits-1). Note that a type-spec of (signed-byte i) for a variable x in a function's declare form translates to a guard condition of (signed-byte-p i x). The guard for signed-byte-p is T. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SIGNUM, Next: SIXTH, Prev: SIGNED-BYTE-P, Up: ACL2-BUILT-INS SIGNUM indicator for positive, negative, or zero (Signum x) is 0 if x is 0, -1 if x is negative, and is 1 otherwise. The guard for signum requires its argument to be rational (real, in ACL2(r)) number. Signum is a Common Lisp function. See any Common Lisp documentation for more information. From "Common Lisp the Language" page 206, we see a definition of signum in terms of abs. As explained elsewhere (see *note ABS::), the guard for abs requires its argument to be a rational (real, in ACL2(r)) number; hence, we make the same restriction for signum. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SIXTH, Next: STANDARD-CHAR-LISTP, Prev: SIGNUM, Up: ACL2-BUILT-INS SIXTH sixth member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: STANDARD-CHAR-LISTP, Next: STANDARD-CHAR-P, Prev: SIXTH, Up: ACL2-BUILT-INS STANDARD-CHAR-LISTP recognizer for a true list of standard characters (standard-char-listp x) is true if and only if x is a null-terminated list all of whose members are standard characters. See *note STANDARD-CHAR-P::. Standard-char-listp has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STANDARD-CHAR-P, Next: STANDARD-CO, Prev: STANDARD-CHAR-LISTP, Up: ACL2-BUILT-INS STANDARD-CHAR-P recognizer for standard characters (Standard-char-p x) is true if and only if x is a "standard" character, i.e., a member of the list *standard-chars*. This list includes #\Newline and #\Space characters, as well as the usual punctuation and alphanumeric characters. Standard-char-p has a guard requiring its argument to be a character. Standard-char-p is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STANDARD-CO, Next: STANDARD-OI, Prev: STANDARD-CHAR-P, Up: ACL2-BUILT-INS STANDARD-CO the character output channel to which ld prints Standard-co is an ld special (see *note LD::). The accessor is (standard-co state) and the updater is (set-standard-co val state). Standard-co must be an open character output channel. It is to this channel that ld prints the prompt, the form to be evaluated, and the results. The event commands such as defun, defthm, etc., which print extensive commentary do not print to standard-co but rather to a different channel, proofs-co, so that you may redirect this commentary while still interacting via standard-co. See *note PROOFS-CO::. "Standard-co" stands for "standard character output." The initial value of standard-co is the same as the value of *standard-co* (see *note *STANDARD-CO*::).  File: acl2-doc-emacs.info, Node: STANDARD-OI, Next: STANDARD-STRING-ALISTP, Prev: STANDARD-CO, Up: ACL2-BUILT-INS STANDARD-OI the standard object input "channel" Standard-oi is an ld special (see *note LD::). The accessor is (standard-oi state) and the updater is (set-standard-oi val state). Standard-oi must be an open object input channel, a true list of objects, or a list of objects whose last cdr is an open object input channel. It is from this source that ld takes the input forms to process. When ld is called, if the value specified for standard-oi is a string or a list of objects whose last cdr is a string, then ld treats the string as a file name and opens an object input channel from that file, where the connected book directory (see *note CBD::) is used to resolve relative pathnames. The channel opened by ld is closed by ld upon termination. "Standard-oi" stands for "standard object input." The read-eval-print loop in ld reads the objects in standard-oi and treats them as forms to be evaluated. The initial value of standard-oi is the same as the value of *standard-oi* (see *note *STANDARD-OI*::).  File: acl2-doc-emacs.info, Node: STANDARD-STRING-ALISTP, Next: STATE-GLOBAL-LET*, Prev: STANDARD-OI, Up: ACL2-BUILT-INS STANDARD-STRING-ALISTP recognizer for association lists with standard strings as keys (Standard-string-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where key is a string all of whose characters are standard (see *note STANDARD-CHAR-P::). Standard-string-alistp has a guard of t. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STATE-GLOBAL-LET*, Next: STRING, Prev: STANDARD-STRING-ALISTP, Up: ACL2-BUILT-INS STATE-GLOBAL-LET* bind state global variables See *note PROGRAMMING-WITH-STATE:: for requisite background on programming with the ACL2 state. Example Forms: (state-global-let* ((inhibit-output-lst *valid-output-names*)) (thm (equal x x))) (state-global-let* ((fmt-hard-right-margin 1000 set-fmt-hard-right-margin) (fmt-soft-right-margin 1000 set-fmt-soft-right-margin)) (mini-proveall)) General Form: (state-global-let* ((var1 form1) ; or (var1 form1 set-var1) ... (vark formk) ; or (vark formk set-vark) ) body) where: each vari is a variable; each formi is an expression whose value is a single ordinary object (i.e. not multiple values, and not state or any other stobj); set-vari, if supplied, is a function with signature ((set-vari * state) => state); and body is an expression that evaluates to an error triple (see *note ERROR-TRIPLES::). Each formi is evaluated in order, starting with form1, and with each such binding the state global variable vari is bound to the value of formi, sequentially in the style of let*. More precisely, then meaning of this form is to set (in order) the global values of the indicated state global variables vari to the values of formi using f-put-global, execute body, restore the vari to their previous values (but see the discussion of setters below), and return the triple produced by body (with its state as modified by the restoration). The restoration is guaranteed even in the face of aborts. The "bound" variables may initially be unbound in state and restoration means to make them unbound again. Still referring to the General Form above, let old-vali be the value of state global variable vari at the time vari is about to be assigned the value of formi. If set-vari is not supplied, then as suggested above, the following form is evaluated at the conclusion of the evaluation of the state-global-let* form, whether or not an error has occurred: (f-put-global 'vari 'old-vali state). However, if set-vari is supplied, then instead the form evaluated will be (set-vari 'old-vali state). This capability is particularly useful if vari is untouchable (see *note PUSH-UNTOUCHABLE::), since the above call of f-put-global is illegal. Note that the scope of the bindings of a state-global-let* form is the body of that form. This may seem obvious, but to drive the point home, let's consider the following example (see *note SET-PRINT-BASE:: and see *note SET-PRINT-RADIX::). ACL2 !>(state-global-let* ((print-base 16 set-print-base) (print-radix t set-print-radix)) (mv nil 10 state)) 10 ACL2 !> Why wasn't the result printed as #xA? The reason is that the result was printed after evaluation of the entire form had completed. If you want to see #xA, do the printing in the scope of the bindings, for example as follows. ACL2 !>(state-global-let* ((print-base 16 set-print-base) (print-radix t set-print-radix)) (pprogn (fms "~x0~%" (list (cons #0 10)) *standard-co* state nil) (mv nil 10 state))) #xA 10 ACL2 !>  File: acl2-doc-emacs.info, Node: STRING, Next: STRING-APPEND, Prev: STATE-GLOBAL-LET*, Up: ACL2-BUILT-INS STRING coerce to a string (String x) coerces x to a string. If x is already a string, then it is returned unchanged; if x is a symbol, then its symbol-name is returned; and if x is a character, the corresponding one-character string is returned. The guard for string requires its argument to be a string, a symbol, or a character. String is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING-APPEND, Next: STRING-DOWNCASE, Prev: STRING, Up: ACL2-BUILT-INS STRING-APPEND concatenate two strings String-append takes two arguments, which are both strings (if the guard is to be met), and returns a string obtained by concatenating together the characters in the first string followed by those in the second. Also see *note CONCATENATE::, noting that the macro call (concatenate 'string str1 str2). expands to the call (string-append str1 str2). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING-DOWNCASE, Next: STRING-EQUAL, Prev: STRING-APPEND, Up: ACL2-BUILT-INS STRING-DOWNCASE in a given string, turn upper-case characters into lower-case For a string x, (string-downcase x) is the result of applying char-downcase to each character in x. The guard for string-downcase requires its argument to be a string containing only standard characters. String-downcase is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING-EQUAL, Next: STRING-LISTP, Prev: STRING-DOWNCASE, Up: ACL2-BUILT-INS STRING-EQUAL string equality without regard to case For strings str1 and str2, (string-equal str1 str2) is true if and only str1 and str2 are the same except perhaps for the cases of their characters. The guard on string-equal requires that its arguments are strings consisting of standard characters (see *note STANDARD-CHAR-LISTP::). String-equal is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING-LISTP, Next: STRING-UPCASE, Prev: STRING-EQUAL, Up: ACL2-BUILT-INS STRING-LISTP recognizer for a true list of strings The predicate string-listp tests whether its argument is a true-listp of strings. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING-UPCASE, Next: STRING<, Prev: STRING-LISTP, Up: ACL2-BUILT-INS STRING-UPCASE in a given string, turn lower-case characters into upper-case For a string x, (string-upcase x) is the result of applying char-upcase to each character in x. The guard for string-upcase requires its argument to be a string containing only standard characters. String-upcase is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING<, Next: STRING<=, Prev: STRING-UPCASE, Up: ACL2-BUILT-INS STRING< less-than test for strings (String< str1 str2) is non-nil if and only if the string str1 precedes the string str2 lexicographically, where character inequalities are tested using char<. When non-nil, (string< str1 str2) is the first position (zero-based) at which the strings differ. Here are some examples. ACL2 !>(string< "abcd" "abu") 2 ACL2 !>(string< "abcd" "Abu") NIL ACL2 !>(string< "abc" "abcde") 3 ACL2 !>(string< "abcde" "abc") NIL The guard for string< specifies that its arguments are strings. String< is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING<=, Next: STRING>, Prev: STRING<, Up: ACL2-BUILT-INS STRING<= less-than-or-equal test for strings (String<= str1 str2) is non-nil if and only if the string str1 precedes the string str2 lexicographically or the strings are equal. When non-nil, (string<= str1 str2) is the first position (zero-based) at which the strings differ, if they differ, and otherwise is their common length. See *note STRING<::. The guard for string<= specifies that its arguments are strings. String<= is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING>, Next: STRING>=, Prev: STRING<=, Up: ACL2-BUILT-INS STRING> greater-than test for strings (String> str1 str2) is non-nil if and only if str2 precedes str1 lexicographically. When non-nil, (string> str1 str2) is the first position (zero-based) at which the strings differ. See *note STRING<::. String> is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRING>=, Next: STRINGP, Prev: STRING>, Up: ACL2-BUILT-INS STRING>= less-than-or-equal test for strings (String>= str1 str2) is non-nil if and only if the string str2 precedes the string str1 lexicographically or the strings are equal. When non-nil, (string>= str1 str2) is the first position (zero-based) at which the strings differ, if they differ, and otherwise is their common length. See *note STRING>::. The guard for string>= specifies that its arguments are strings. String>= is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRINGP, Next: STRIP-CARS, Prev: STRING>=, Up: ACL2-BUILT-INS STRINGP recognizer for strings (stringp x) is true if and only if x is a string.  File: acl2-doc-emacs.info, Node: STRIP-CARS, Next: STRIP-CDRS, Prev: STRINGP, Up: ACL2-BUILT-INS STRIP-CARS collect up all first components of pairs in a list (strip-cars x) is the list obtained by walking through the list x and collecting up all first components (cars). This function is implemented in a tail-recursive way, despite its logical definition. (strip-cars x) has a guard of (alistp x). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: STRIP-CDRS, Next: SUBLIS, Prev: STRIP-CARS, Up: ACL2-BUILT-INS STRIP-CDRS collect up all second components of pairs in a list (strip-cdrs x) has a guard of (alistp x), and returns the list obtained by walking through the list x and collecting up all second components (cdrs). This function is implemented in a tail-recursive way, despite its logical definition. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SUBLIS, Next: SUBSEQ, Prev: STRIP-CDRS, Up: ACL2-BUILT-INS SUBLIS substitute an alist into a tree (Sublis alist tree) is obtained by replacing every leaf of tree with the result of looking that leaf up in the association list alist. However, a leaf is left unchanged if it is not found as a key in alist. Leaves are looked up using the function assoc. The guard for (sublis alist tree) requires (eqlable-alistp alist). This guard ensures that the guard for assoc will be met for each lookup generated by sublis. Sublis is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SUBSEQ, Next: SUBSETP, Prev: SUBLIS, Up: ACL2-BUILT-INS SUBSEQ subsequence of a string or list For any natural numbers start and end, where start <= end <= (length seq), (subseq seq start end) is the subsequence of seq from index start up to, but not including, index end. End may be nil, which which case it is treated as though it is (length seq), i.e., we obtain the subsequence of seq from index start all the way to the end. The guard for (subseq seq start end) is that seq is a true list or a string, start and end are integers (except, end may be nil, in which case it is treated as (length seq) for the rest of this discussion), and 0 <= start <= end <= (length seq). Subseq is a Common Lisp function. See any Common Lisp documentation for more information. Note: In Common Lisp the third argument of subseq is optional, but in ACL2 it is required, though it may be nil as explained above. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SUBSETP, Next: SUBSETP-EQ, Prev: SUBSEQ, Up: ACL2-BUILT-INS SUBSETP test if every member of one list is a member of the other General Forms: (subsetp x y) (subsetp x y :test 'eql) ; same as above (eql as equality test) (subsetp x y :test 'eq) ; same, but eq is equality test (subsetp x y :test 'equal) ; same, but equal is equality test (Subsetp x y) is true if and only if every member of the list x is a member of the list y. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists. The guard for a call of subsetp depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between subsetp and its variants: (subsetp-eq x lst) is equivalent to (subsetp x lst :test 'eq); (subsetp-equal x lst) is equivalent to (subsetp x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function subsetp-equal. Subsetp is defined by Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: SUBSETP-EQ, Next: SUBSETP-EQUAL, Prev: SUBSETP, Up: ACL2-BUILT-INS SUBSETP-EQ See *note SUBSETP::.  File: acl2-doc-emacs.info, Node: SUBSETP-EQUAL, Next: SUBST, Prev: SUBSETP-EQ, Up: ACL2-BUILT-INS SUBSETP-EQUAL See *note SUBSETP::.  File: acl2-doc-emacs.info, Node: SUBST, Next: SUBSTITUTE, Prev: SUBSETP-EQUAL, Up: ACL2-BUILT-INS SUBST a single substitution into a tree (Subst new old tree) is obtained by substituting new for every occurence of old in the given tree. Equality to old is determined using the function eql. The guard for (subst new old tree) requires (eqlablep old), which ensures that the guard for eql will be met for each comparison generated by subst. Subst is defined in Common Lisp. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SUBSTITUTE, Next: SYMBOL-<, Prev: SUBST, Up: ACL2-BUILT-INS SUBSTITUTE substitute into a string or a list, using eql as test (Substitute new old seq) is the result of replacing each occurrence of old in seq, which is a list or a string, with new. The guard for substitute requires that either seq is a string and new is a character, or else: seq is a true-listp such that either all of its members are eqlablep or old is eqlablep. Substitute is a Common Lisp function. See any Common Lisp documentation for more information. Since ACL2 functions cannot take keyword arguments (though macros can), the test used in substitute is eql. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SYMBOL-<, Next: SYMBOL-ALISTP, Prev: SUBSTITUTE, Up: ACL2-BUILT-INS SYMBOL-< less-than test for symbols (symbol-< x y) is non-nil if and only if either the symbol-name of the symbol x lexicographially precedes the symbol-name of the symbol y (in the sense of string<) or else the symbol-names are equal and the symbol-package-name of x lexicographically precedes that of y (in the same sense). So for example, (symbol-< 'abcd 'abce) and (symbol-< 'acl2::abcd 'foo::abce) are true. The guard for symbol specifies that its arguments are symbols. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SYMBOL-ALISTP, Next: SYMBOL-LISTP, Prev: SYMBOL-<, Up: ACL2-BUILT-INS SYMBOL-ALISTP recognizer for association lists with symbols as keys (Symbol-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where key is a symbolp. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SYMBOL-LISTP, Next: SYMBOL-NAME, Prev: SYMBOL-ALISTP, Up: ACL2-BUILT-INS SYMBOL-LISTP recognizer for a true list of symbols The predicate symbol-listp tests whether its argument is a true list of symbols. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: SYMBOL-NAME, Next: SYMBOL-PACKAGE-NAME, Prev: SYMBOL-LISTP, Up: ACL2-BUILT-INS SYMBOL-NAME the name of a symbol (a string) Completion Axiom (completion-of-symbol-name): (equal (symbol-name x) (if (symbolp x) (symbol-name x) "")) Guard for (symbol-name x): (symbolp x)  File: acl2-doc-emacs.info, Node: SYMBOL-PACKAGE-NAME, Next: SYMBOLP, Prev: SYMBOL-NAME, Up: ACL2-BUILT-INS SYMBOL-PACKAGE-NAME the name of the package of a symbol (a string) WARNING: While symbol-package-name behaves properly on all ACL2 objects, it may give surprising results when called in raw Lisp. For more details see *note PKG-IMPORTS::, in particular the discussion there of the "COMMON-LISP" package. Completion Axiom (completion-of-symbol-package-name): (equal (symbol-package-name x) (if (symbolp x) (symbol-package-name x) "")) Guard for (symbol-package-name x): (symbolp x) Note: Symbol-package-name may diverge from the name of the symbol's package in raw Lisp, in the case that this package is the main Lisp package. For example, in GCL (symbol-package-name 'car) evaluates to "COMMON-LISP" even though the actual package name for the symbol, car, is "LISP".  File: acl2-doc-emacs.info, Node: SYMBOLP, Next: SYS-CALL, Prev: SYMBOL-PACKAGE-NAME, Up: ACL2-BUILT-INS SYMBOLP recognizer for symbols (symbolp x) is true if and only if x is a symbol.  File: acl2-doc-emacs.info, Node: SYS-CALL, Next: SYS-CALL+, Prev: SYMBOLP, Up: ACL2-BUILT-INS SYS-CALL make a system call to the host operating system Example Forms: (sys-call "cp" '("foo.lisp" "foo-copied.lisp")) (prog2$ (sys-call "cp" '("foo.lisp" "foo-copied.lisp")) (sys-call-status state)) The first argument of sys-call is a command for the host operating system, and the second argument is a list of strings that are the arguments for that command. In GCL and perhaps some other lisps, you can put the arguments with the command; but this is not the case, for example, in Allegro CL running on Linux. For a related utility, see *note SYS-CALL+::. The use of prog2$ above is optional, but illustrates a typical sort of use when one wishes to get the return status. See *note SYS-CALL-STATUS::. General Form: (sys-call cmd args) This function logically returns nil. However, it makes the indicated call to the host operating system, as described above, using a function supplied "under the hood" by the underlying Lisp system. On occasions where one wishes to obtain the numeric status returned by the host operating system (or more precisely, by the Lisp function under the hood that passes the system call to the host operating system), one may do so; see *note SYS-CALL-STATUS::. The status value is the value returned by that Lisp function, which may well be the same numeric value returned by the host operating system for the underlying system call. Note that sys-call does not touch the ACL2 state; however, sys-call-status updates the acl2-oracle field of the state. Be careful if you use sys-call! It can be used for example to overwrite files, or worse! We view a use of sys-call as a call to the operating system that is made outside ACL2. The following example from Bob Boyer shows how to use sys-call to execute, in effect, arbitrary Lisp forms. ACL2 provides a "trust tag" mechanism that requires execution of a defttag form before you can use sys-call; see *note DEFTTAG::. (Note: The setting of the raw Lisp variable *features* below is just to illustrate that any such mischief is possible. Normally *features* is a list with more than a few elements.) % cat foo print *0x85d2064=0x838E920 detach q % acl2 ... boilerplate deleted ACL2 !>(sys-call "gdb -p $PPID -w < foo >& /dev/null " nil) NIL ACL2 !>:q Exiting the ACL2 read-eval-print loop. To re-enter, execute (LP). ACL2>*features* (:AKCL-SET-MV) ACL2> Finally, we make a comment about output redirection, which also applies to other related features that one may expect of a shell. Sys-call does not directly support output redirection. If you want to run a program, P, and redirect its output, one option is to create a wrapper script, W to call instead. Thus W might be a shell script containing the line: P $* >& foo.out For a different, more direct solution, see *note SYS-CALL+::.  File: acl2-doc-emacs.info, Node: SYS-CALL+, Next: SYS-CALL-STATUS, Prev: SYS-CALL, Up: ACL2-BUILT-INS SYS-CALL+ make a system call to the host OS, returning status and output Example Form: ; The following returns (mv nil s state), where s is the standard output ; from the command: ls -l ./ (sys-call+ "ls" '("-l" "./") state) General Form: (sys-call+ cmd args state) where cmd is a command to the host operating system and args is a list of strings. Also see *note SYS-CALL::; but there are two differences between sys-call and sys-call+. First, the latter takes an extra argument, state. Second, while sys-call returns nil, sys-call+ returns three values: a so-called error triple (see *note ERROR-TRIPLES::), (mv erp val state). While execution returns values as described just below, further below we explain the logical return values. In the following, please keep in mind that the exact behavior depends on the platform; the description is only a guide. For example, on some platforms erp might always be nil, even if in the error case, and val might or might not include error output. (For details, look at the ACL2 source code for function system-call+, whose output is converted by replacing an erp of nil by 0.) Erp is either nil or a non-zero integer. Normally, nil indicates that the command ran without error, and otherwise erp is the exit status. Val is a string, typically the output generated by the call of cmd. State is an ACL2 state. While the description above pertains to the values returned by executing sys-call+, the logical values are as follows. For a discussion of the acl2-oracle field of an ACL2 state, see *note STATE::. Erp is the first element of the acl2-oracle of the input state if that element is a nonzero integer, and otherwise is nil. Val is the second element of the acl2-oracle of the input state if it is a string, else the empty string, "". State is the result of popping the acl2-oracle field twice from the input state. Note that unlike sys-call, a call of sys-call+ has no effect on subsequent calls of sys-call-status. As is the case for sys-call, a trust tag is required to call sys-call+. For discussion of this and more, see *note SYS-CALL::.  File: acl2-doc-emacs.info, Node: SYS-CALL-STATUS, Next: TAKE, Prev: SYS-CALL+, Up: ACL2-BUILT-INS SYS-CALL-STATUS exit status from the preceding system call This function returns two values, (mv status state). The first is the status resulting from the most recent invocation of function sys-call; see *note SYS-CALL::. The second is the ACL2 state object, which is also the input to this function. The function sys-call makes a system call to the host operating system using a function supplied "under the hood" by the underlying Lisp system. The status value is the value returned by that Lisp function, which may well be the numeric value returned by the host operating system for the underlying system call. For more information, see *note SYS-CALL::.  File: acl2-doc-emacs.info, Node: TAKE, Next: TENTH, Prev: SYS-CALL-STATUS, Up: ACL2-BUILT-INS TAKE initial segment of a list For any natural number n not exceeding the length of l, (take n l) collects the first n elements of the list l. The following is a theorem (though it takes some effort, including lemmas, to get ACL2 to prove it): (equal (length (take n l)) (nfix n)) If n is an integer greater than the length of l, then take pads the list with the appropriate number of nil elements. Thus, the following is also a theorem. (implies (and (integerp n) (true-listp l) (<= (length l) n)) (equal (take n l) (append l (make-list (- n (length l)))))) For related functions, see *note NTHCDR:: and see *note BUTLAST::. The guard for (take n l) is that n is a nonnegative integer and l is a true list. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: TENTH, Next: TERM-ORDER, Prev: TAKE, Up: ACL2-BUILT-INS TENTH tenth member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: TERM-ORDER, Next: THE, Prev: TENTH, Up: ACL2-BUILT-INS TERM-ORDER the ordering relation on terms used by ACL2 ACL2 must occasionally choose which of two terms is syntactically smaller. The need for such a choice arises, for example, when using equality hypotheses in conjectures (the smaller term is substituted for the larger elsewhere in the formula), in stopping loops in permutative rewrite rules (see *note LOOP-STOPPER::), and in choosing the order in which to try to cancel the addends in linear arithmetic inequalities. When this notion of syntactic size is needed, ACL2 uses "term order." Popularly speaking, term order is just a lexicographic ordering on terms. But the situation is actually more complicated. We define term order only with respect to terms in translated form. See *note TRANS::. Constants are viewed as built up by _pseudo-function_ applications, as described at the end of this documentation. Term1 comes before term2 in the term order iff (a) the number of variable occurrences in term1 is less than that in term2, or (b) the numbers of variable occurrences in the two terms are equal but the number of function applications in term1 is less than that in term2, or (c) the numbers of variable occurrences in the two terms are equal, the numbers of functions applications in the two terms are equal, but pseudo-function application count for term1 is less than that for term2, or (d) the numbers of variable occurrences in the two terms are equal, the numbers of functions applications in the two terms are equal, the pseudo-function application counts for the two terms are equal, and term1 comes before term2 in a lexicographic ordering, lexorder, based their structure as Lisp objects: see *note LEXORDER::. The function term-order, when applied to the translations of two ACL2 terms, returns t iff the first is "less than or equal" to the second in the term order. By "number of variable occurrences" we do not mean "number of distinct variables" but "number of times a variable symbol is mentioned." (Cons x x) has two variable occurrences, not one. Thus, perhaps counterintuitively, a large term that contains only one variable occurrence, e.g., (standard-char-p (car (reverse x))) comes before (cons x x) in the term order. Since constants contain no variable occurrences and non-constant expressions must contain at least one variable occurrence, constants come before non-constants in the term order, no matter how large the constants. For example, the list constant '(monday tuesday wednesday thursday friday) comes before x in the term order. Because term order is involved in the control of permutative rewrite rules and used to shift smaller terms to the left, a set of permutative rules designed to allow the permutation of any two tips in a tree representing the nested application of some function will always move the constants into the left-most tips. Thus, (+ x 3 (car (reverse klst)) (dx i j)) , which in translated form is (binary-+ x (binary-+ '3 (binary-+ (dx i j) (car (reverse klst))))), will be permuted under the built-in commutativity rules to (binary-+ '3 (binary-+ x (binary-+ (car (reverse klst)) (dx i j)))) or (+ 3 x (car (reverse klst)) (dx i j)). Two terms with the same numbers of variable occurrences and function applications and the same pseudo-function application count are ordered by lexicographic means, based on their structures. See *note LEXORDER::. Thus, if two terms (member ...) and (reverse ...) contain the same numbers of variable occurrences and function applications, and no quoted constants, then the member term is first in the term order because member comes before reverse in the term order (which is here reduced to alphabetic ordering). It remains to discuss the notion of _pseudo-function application count_. Clearly, two constants are ordered using cases (c) and (d) of term order, since they each contain 0 variable occurrences and no function calls. This raises the question "How many function applications are in a constant?" Because we regard the number of function applications as a more fundamental measure of the size of a constant than lexicographic considerations, we decided that for the purposes of term order, constants would be seen as being built by primitive constructor functions. These constructor functions are not actually defined in ACL2 but merely imagined for the purposes of term order. We here use suggestive names for these imagined functions, ignoring entirely the prior use of these names within ACL2. The imagined applications of these functions are what we refer to as _pseudo-function_ applications. The constant function z constructs 0. Positive integers are constructed from (z) by the successor function, s. Thus 2 is (s (s (z))) and contains three function applications. 100 contains one hundred and one applications. Negative integers are constructed from their positive counterparts by -. Thus, -2 is (- (s (s (z)))) and has four applications. Ratios are constructed by the dyadic function /. Thus, -1/2 is (/ (- (s (z))) (s (s (z)))) and contains seven applications. Complex rationals are similarly constructed from rationals. All character objects are considered primitive and are constructed by constant functions of the same name. Thus #\a and #\b both contain one application. Strings are built from the empty string, (o) by the "string-cons" function written cs. Thus "AB" is (cs (#\a) (cs (#\b) (o))) and contains five applications. Symbols are obtained from strings by "packing" the symbol-name with the unary function p. Thus 'ab is (p (cs (#\a) (cs (#\b) (o)))) and has six applications. Note that packages are here ignored and thus 'acl2::ab and 'my-package::ab each contain just six applications. Finally, conses are built with cons, as usual. So '(1 . 2) is (cons '1 '2) and contains six applications, since '1 contains two and '2 contains three. This, for better or worse, answers the question "How many function applications are in a constant?" Finally, when we refer to the "pseudo-function application count", we mean the number of pseudo-function applications as described above, except that we bound this number by the constant (fn-count-evg-max-val). (This bound is important for efficiency, so that constants that are very large cons structures do not cause significant slowdown as ACL2 attempts to walk through them while computing their pseudo-function application count.)  File: acl2-doc-emacs.info, Node: THE, Next: THIRD, Prev: TERM-ORDER, Up: ACL2-BUILT-INS THE run-time type check (The typ val) checks that val satisfies the type specification typ (see *note TYPE-SPEC::). An error is caused if the check fails, and otherwise, val is the value of this expression. Here are some examples. (the integer 3) ; returns 3 (the (integer 0 6) 3) ; returns 3 (the (integer 0 6) 7) ; causes an error (see below for exception) See *note TYPE-SPEC:: for a discussion of the legal type specifications. There is an exception to the rule that failure of the type-check causes an error: there is no error when guard-checking has been turned off with :set-guard-checking :NONE or (with-guard-checking :NONE ...). See *note SET-GUARD-CHECKING:: and see *note WITH-GUARD-CHECKING::. The following remark is for those who verify guards for their functions (see *note GUARD:: and see *note VERIFY-GUARDS::). We remark that a call of (the TYPE EXPR) in the body of a function definition generates a guard proof obligation that the type, TYPE, holds for the value of the expression, EXPR. Consider the following example. (defun f (x) (declare (xargs :guard (p1 x))) (if (p2 x) (the integer x) 17)) The guard proof obligation generated for the THE expression above is as follows. (implies (and (p1 x) (p2 x)) (let ((var x)) (integerp var))) THE is defined in Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: THIRD, Next: TIME$, Prev: THE, Up: ACL2-BUILT-INS THIRD third member of the list See any Common Lisp documentation for details.  File: acl2-doc-emacs.info, Node: TIME$, Next: TRUE-LIST-LISTP, Prev: THIRD, Up: ACL2-BUILT-INS TIME$ time an evaluation Semantically, (time$ x ...) equals x. However, its evaluation may write timing output to the trace output (which is usually the terminal), as explained further below. Examples: ; Basic examples: (time$ (foo 3 4)) (time$ (mini-proveall)) (defun bar (x) (time$ (f x))) ; Custom examples, which use a custom timing message rather than a built-in ; message from Lisp: ; Report only if real time is at least 1/2 second (two equivalent forms). (time$ (foo) :mintime 1/2) (time$ (foo) :real-mintime 1/2) ; Report only if allocation is at least 1000 bytes (and if the Lisp supports ; :minalloc). (time$ (foo) :minalloc 1000) ; Report only if real time is at least 1/2 second and (if the Lisp supports ; :minalloc) allocation is at least 931 bytes. (time$ (foo) :real-mintime 1/2 :minalloc 931) ; Print "Hello Moon, Goodbye World" instead of any timing data. (time$ (foo) :msg "Hello ~s0, ~s1 World." :args (list "Moon" "Goodbye")) ; Print default custom timing message (same as omitting :mintime 0): (time$ (foo) :mintime 0) ; Print supplied custom timing message. (let ((bar ...)) (time$ (foo) :msg "The execution of ~xf took ~st seconds of real ~ time and ~sc seconds of run time (cpu time), and ~ allocated ~sa bytes. In an unrelated note, bar ~ currently has the value ~x0.~%" :args (list bar))) General Forms: (time$ form) (time$ form ; arguments below are optional :real-mintime :run-mintime :minalloc :msg :args ) ; Note: :real-mintime can be replaced by :mintime where form is processed as usual except that the host Common Lisp times its evaluation. The simplest form is (time$ x), which will call the time utility in the underlying Lisp, and will print a small default message. If you want to see a message printed by the host Lisp, use (time$ x :mintime nil) instead, which may provide detailed, implementation-specific data such as the amounts of time spent in user and system mode, the gc time, the number of page faults encountered, and so on. Of you can create a custom message, configured using the :msg and :args parameters. Time$ can also be made to report timing information only conditionally: the :real-mintime (or equivalently, :mintime), :run-mintime, and :minalloc arguments can be used to avoid reporting timing information for computations that take a small amount of time (perhaps as might be expected in ordinary cases), but to draw the user's attention to computations that take longer or allocate more memory than expected. We next document the keyword arguments in some detail. Keyword arguments :real-mintime (or :mintime) and :run-mintime can be used to specify a minimum time threshold for time reporting. That is, no timing information will be printed if the execution of form takes less than the specified number of seconds of real (total) time or run (cpu) time, respectively. Note that rational numbers like 1/2 may be used to specify a fractional amount of seconds. It is an error to specify both :real-mintime and its synonym, :mintime. Keyword argument :minalloc is not supported on all Lisps. When it is not supported, it is ignored. But on supported Lisps, :minalloc can be used to specify a minimum memory allocation threshold. If form results in fewer than this many bytes being allocated, then no timing information will be reported. Keyword argument :msg, when provided, should be a string accepted by the fmt family of functions (see *note FMT::), and it may refer to the elements of :args by their positions, just as for cw (see *note CW::). The following directives allow you to report timing information using the :msg string. The examples at the top of this documentation topic illustrate the use of these directives. ~xf -- the form that was executed ~sa -- the amount of memory allocated, in bytes (in supported Lisps) ~st -- the real time taken, in seconds ~sc -- the run time (cpu time) taken, in seconds We turn now to an example that illustrates how time$ can be called in function bodies. Consider the following definition of the Fibonacci function, followed by the definition of a function that times k calls of this function. (defun fib (n) (if (zp n) 1 (if (= n 1) 1 (+ (fib (- n 1)) (fib (- n 2)))))) (defun time-fib (k) (if (zp k) nil (prog2$ (time$ (fib k) :mintime 1/2 :msg "(fib ~x0) took ~st seconds, ~sa bytes allocated.~%" :args (list k)) (time-fib (1- k))))) The following log shows a sample execution of the function defined just above. ACL2 !>(time-fib 36) (fib 36) took 3.19 seconds, 1280 bytes allocated. (fib 35) took 1.97 seconds, 1280 bytes allocated. (fib 34) took 1.21 seconds, 1280 bytes allocated. (fib 33) took 0.75 seconds, 1280 bytes allocated. NIL ACL2 !> Notes: (1) Common Lisp specifies that the time utility prints to "trace output", and time$ follows this convention. Thus, if you have opened a trace file (see *note OPEN-TRACE-FILE::), then you can expect to find the time$ output there. (2) Unless the :msg argument is supplied, an explicit call of time$ in the top-level loop will show that the form being timed is a call of the ACL2 evaluator function ev-rec. This is normal; the curious are invited, at their own risk, to see *note RETURN-LAST:: for an explanation.  File: acl2-doc-emacs.info, Node: TRUE-LIST-LISTP, Next: TRUE-LISTP, Prev: TIME$, Up: ACL2-BUILT-INS TRUE-LIST-LISTP recognizer for true (proper) lists of true lists True-list-listp is the function that checks whether its argument is a list that ends in, or equals, nil, and furthermore, all of its elements have that property. Also see *note TRUE-LISTP::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: TRUE-LISTP, Next: TRUNCATE, Prev: TRUE-LIST-LISTP, Up: ACL2-BUILT-INS TRUE-LISTP recognizer for proper (null-terminated) lists True-listp is the function that checks whether its argument is a list that ends in, or equals, nil. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: TRUNCATE, Next: UNARY--, Prev: TRUE-LISTP, Up: ACL2-BUILT-INS TRUNCATE division returning an integer by truncating toward 0 Example Forms: ACL2 !>(truncate 14 3) 4 ACL2 !>(truncate -14 3) -4 ACL2 !>(truncate 14 -3) -4 ACL2 !>(truncate -14 -3) 4 ACL2 !>(truncate -15 -3) 5 ACL2 !>(truncate 10/4 3/4) 3 (Truncate i j) is the result of taking the quotient of i and j and dropping the fraction. For example, the quotient of -14 by 3 is -4 2/3, so dropping the fraction 2/3, we obtain a result for (truncate -14 3) of -4. The guard for (truncate i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero. Truncate is a Common Lisp function. However, note that unlike Common Lisp, the ACL2 truncate function returns only a single value, Also see *note NONNEGATIVE-INTEGER-QUOTIENT::, which is appropriate for integers and may simplify reasoning, unless a suitable arithmetic library is loaded, but be less efficient for evaluation on concrete objects. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: UNARY--, Next: UNARY-/, Prev: TRUNCATE, Up: ACL2-BUILT-INS UNARY- arithmetic negation function Completion Axiom (completion-of-unary-minus): (equal (unary-- x) (if (acl2-numberp x) (unary-- x) 0)) Guard for (unary- x): (acl2-numberp x) Notice that like all arithmetic functions, unary- treats non-numeric inputs as 0. Calls of the macro - on one argument expand to calls of unary-; see *note -::.  File: acl2-doc-emacs.info, Node: UNARY-/, Next: UNION$, Prev: UNARY--, Up: ACL2-BUILT-INS UNARY-/ reciprocal function Completion Axiom (completion-of-unary-/): (equal (unary-/ x) (if (and (acl2-numberp x) (not (equal x 0))) (unary-/ x) 0)) Guard for (unary-/ x): (and (acl2-numberp x) (not (equal x 0))) Notice that like all arithmetic functions, unary-/ treats non-numeric inputs as 0. Calls of the macro / on one argument expand to calls of unary-/; see *note /::.  File: acl2-doc-emacs.info, Node: UNION$, Next: UNION-EQ, Prev: UNARY-/, Up: ACL2-BUILT-INS UNION$ elements of one list that are not elements of another General Forms: (union$ l1 l2 ... lk) (union$ l1 l2 ... lk :test 'eql) ; same as above (union$ l1 l2 ... lk :test 'eq) ; same, but eq is equality test (union$ l1 l2 ... lk :test 'equal) ; same, but equal is equality test (Union$ x y) equals a list that contains both the members of x and the members of y. More precisely, the resulting list is the same as one would get by first deleting the members of y from x, and then concatenating the result to the front of y. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists. Union$ need not take exactly two arguments: (union$) is nil, (union$ x) is x, (union$ x y z ... :test test) is (union$ x (union$ y z ... :test test) :test test), and if :TEST is not supplied, then (union$ x y z ...) is (union$ x (union$ y z ...)). For the discussion below we restrict ourselves, then, to the cases (union$ x y) and (union$ x y :test test). The guard for a call of union$ (in the two cases just above) depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp. See *note EQUALITY-VARIANTS:: for a discussion of the relation between union$ and its variants: (union-eq x lst) is equivalent to (union$ x lst :test 'eq); (union-equal x lst) is equivalent to (union$ x lst :test 'equal). In particular, reasoning about any of these primitives reduces to reasoning about the function union-equal. Note that union-eq can take any number of arguments, in analogy to union$; indeed, (union-eq ...) expands to (union$ ... :test 'eq). However, union-equal is a function, not a macro, and takes exactly two arguments. Union$ is similar to the Common Lisp primitive union. However, Common Lisp does not specify the order of elements in the result of a call of union.  File: acl2-doc-emacs.info, Node: UNION-EQ, Next: UNION-EQUAL, Prev: UNION$, Up: ACL2-BUILT-INS UNION-EQ See *note UNION$::.  File: acl2-doc-emacs.info, Node: UNION-EQUAL, Next: UNSIGNED-BYTE-P, Prev: UNION-EQ, Up: ACL2-BUILT-INS UNION-EQUAL See *note UNION$::.  File: acl2-doc-emacs.info, Node: UNSIGNED-BYTE-P, Next: UNTRANSLATE, Prev: UNION-EQUAL, Up: ACL2-BUILT-INS UNSIGNED-BYTE-P recognizer for natural numbers that fit in a specified bit width (Unsigned-byte-p bits x) is T when bits is a positive integer and x is a nonnegative integer that fits into a bit-width of bits, i.e., 0 <= x < 2^bits. Note that a type-spec of (unsigned-byte i) for a variable x in a function's declare form translates to a guard condition of (unsigned-byte-p i x). The guard for unsigned-byte-p is T. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: UNTRANSLATE, Next: UPDATE-NTH, Prev: UNSIGNED-BYTE-P, Up: ACL2-BUILT-INS UNTRANSLATE See *note USER-DEFINED-FUNCTIONS-TABLE::.  File: acl2-doc-emacs.info, Node: UPDATE-NTH, Next: UPPER-CASE-P, Prev: UNTRANSLATE, Up: ACL2-BUILT-INS UPDATE-NTH modify a list by putting the given value at the given position (Update-nth key val l) returns a list that is the same as the list l, except that the value at the 0-based position key (a natural number) is val. If key is an integer at least as large as the length of l, then l will be padded with the appropriate number of nil elements, as illustrated by the following example. ACL2 !>(update-nth 8 'z '(a b c d e)) (A B C D E NIL NIL NIL Z) We have the following theorem. (implies (and (true-listp l) (integerp key) (<= 0 key)) (equal (length (update-nth key val l)) (if (< key (length l)) (length l) (+ 1 key)))) The guard of update-nth requires that its first (position) argument is a natural number and its last (list) argument is a true list. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: UPPER-CASE-P, Next: WITH-LIVE-STATE, Prev: UPDATE-NTH, Up: ACL2-BUILT-INS UPPER-CASE-P recognizer for upper case characters (Upper-case-p x) is true if and only if x is an upper case character, i.e., a member of the list #\A, #\B, ..., #\Z. The guard for upper-case-p requires its argument to be a standard character (see *note STANDARD-CHAR-P::). Upper-case-p is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: WITH-LIVE-STATE, Next: WRITE-BYTE$, Prev: UPPER-CASE-P, Up: ACL2-BUILT-INS WITH-LIVE-STATE allow a reference to state in raw Lisp The macro with-live-state is an advanced feature that very few users will need (basically, only system hackers). Indeed, it is untouchable; see *note REMOVE-UNTOUCHABLE:: for how to enable calling with-live-state in the ACL2 loop. Example Form: (with-live-state (assign y 3)) General form: (with-live-state form) where form is an arbitrary form with a free reference to the variable state. Logically, (with-live-state FORM) macroexpands to FORM. However, in raw Lisp it expands to: (let ((state *the-live-state*)) FORM) If a form that mentions the variable state might be executed in raw Lisp -- that is, either outside the ACL2 loop or in raw mode (see *note SET-RAW-MODE::) -- then the surrounding the form with with-live-state as shown above can avoid potential warnings or (much less likely) errors. Note however that if state is lexically bound to a state other than the usual "live" state, surprising behavior may occur when evaluating a call of with-live-state in raw Lisp or raw mode (either directly by evaluation or at compile time), because with-live-state will override that lexical binding of state by a lexical binding of state to the usual "live" state.  File: acl2-doc-emacs.info, Node: WRITE-BYTE$, Next: XOR, Prev: WITH-LIVE-STATE, Up: ACL2-BUILT-INS WRITE-BYTE$ See *note IO::.  File: acl2-doc-emacs.info, Node: XOR, Next: ZEROP, Prev: WRITE-BYTE$, Up: ACL2-BUILT-INS XOR logical "exclusive or" Xor is the ACL2 exclusive-or function. (xor P Q) means that either P or Q, but not both, is false (i.e., nil). To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ZEROP, Next: ZIP, Prev: XOR, Up: ACL2-BUILT-INS ZEROP test an acl2-number against 0 (zerop x) is t if x is 0 and is nil otherwise. Thus, it is logically equivalent to (equal x 0). (Zerop x) has a guard requiring x to be numeric and can be expected to execute more efficiently than (equal x 0) in properly guarded compiled code. In recursions down the natural numbers, (zp x) is preferred over (zerop x) because the former coerces x to a natural and allows the termination proof. In recursions through the integers, (zip x) is preferred. See *note ZERO-TEST-IDIOMS::. Zerop is a Common Lisp function. See any Common Lisp documentation for more information. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ZIP, Next: ZP, Prev: ZEROP, Up: ACL2-BUILT-INS ZIP testing an "integer" against 0 (Zip i) is logically equivalent to (equal (ifix i) 0) and is the preferred termination test for recursion through the integers. (Zip i) returns t if i is 0 or not an integer; it returns nil otherwise. Thus, i (zip i) 3 nil 0 t -2 nil 5/2 t #c(1 3) t 'abc t (Zip i) has a guard requiring i to be an integer. For a discussion of the various idioms for testing against 0, see *note ZERO-TEST-IDIOMS::. Zip is typically used as the termination test in recursions through the integers. It has the advantage of "coercing" its argument to an integer and hence allows the definition to be admitted without an explicit type check in the body. Guard verification allows zip to be compiled as a direct =-comparision with 0.  File: acl2-doc-emacs.info, Node: ZP, Next: ZPF, Prev: ZIP, Up: ACL2-BUILT-INS ZP testing a "natural" against 0 (Zp n) is logically equivalent to (equal (nfix n) 0) and is the preferred termination test for recursion down the natural numbers. (Zp n) returns t if n is 0 or not a natural number; it returns nil otherwise. Thus, in the ACL2 logic (ignoring the issue of guards): n (zp n) 3 nil 0 t -1 t 5/2 t #c(1 3) t 'abc t (Zp n) has a guard requiring n to be a natural number. For a discussion of the various idioms for testing against 0, see *note ZERO-TEST-IDIOMS::. Zp is typically used as the termination test in recursions down the natural numbers. It has the advantage of "coercing" its argument to a natural and hence allows the definition to be admitted without an explicit type check in the body. Guard verification allows zp to be compiled as a direct =-comparision with 0.  File: acl2-doc-emacs.info, Node: ZPF, Prev: ZP, Up: ACL2-BUILT-INS ZPF testing a nonnegative fixnum against 0 Zpf is exactly the same as zp, except that zpf is intended for, and faster for, fixnum arguments. Its guard is specified with a type declaration, (type (unsigned-byte 29) x). (See *note DECLARE:: and see *note TYPE-SPEC::.) Also see *note ZP::. To see the ACL2 definition of this function, see *note PF::.  File: acl2-doc-emacs.info, Node: ACL2-USER, Next: ARRAYS, Prev: ACL2-BUILT-INS, Up: PROGRAMMING ACL2-USER a package the ACL2 user may prefer This package imports the standard Common Lisp symbols that ACL2 supports and also a few symbols from package "ACL2" that are commonly used when interacting with ACL2. You may prefer to select this as your current package so as to avoid colliding with ACL2 system names. This package imports the symbols listed in *common-lisp-symbols-from-main-lisp-package*, which contains hundreds of CLTL function and macro names including those supported by ACL2 such as cons, car, and cdr. It also imports the symbols in *acl2-exports*, which contains a few symbols that are frequently used while interacting with the ACL2 system, such as implies, defthm, and rewrite. It imports nothing else. Thus, names such as alistp, member-equal, and type-set, which are defined in the "ACL2" package are not present here. If you find yourself frequently colliding with names that are defined in "ACL2" you might consider selecting "ACL2-USER" as your current package (see *note IN-PACKAGE::). If you select "ACL2-USER" as the current package, you may then simply type member-equal to refer to acl2-user::member-equal, which you may define as you see fit. Of course, should you desire to refer to the "ACL2" version of member-equal, you will have to use the "ACL2::" prefix, e.g., acl2::member-equal. If, while using "ACL2-USER" as the current package, you find that there are symbols from "ACL2" that you wish we had imported into it (because they are frequently used in interaction), please bring those symbols to our attention. For example, should union-theories and universal-theory be imported? Except for stabilizing on the "frequently used" names from "ACL2", we intend never to define a symbol whose symbol-package-name is "ACL2-USER".  File: acl2-doc-emacs.info, Node: ARRAYS, Next: COMPILATION, Prev: ACL2-USER, Up: PROGRAMMING ARRAYS an introduction to ACL2 arrays Below we begin a detailed presentation of ACL2 arrays. ACL2's single-threaded objects (see *note STOBJ::) provide a similar functionality that is generally more efficient when there are updates (writes), but is also more restrictive. * Menu: * AREF1:: access the elements of a 1-dimensional array * AREF2:: access the elements of a 2-dimensional array * ARRAY1P:: recognize a 1-dimensional array * ARRAY2P:: recognize a 2-dimensional array * ARRAYS-EXAMPLE:: an example illustrating ACL2 arrays * ASET1:: set the elements of a 1-dimensional array * ASET2:: set the elements of a 2-dimensional array * COMPRESS1:: remove irrelevant pairs from a 1-dimensional array * COMPRESS2:: remove irrelevant pairs from a 2-dimensional array * DEFAULT:: return the :default from the header of a 1- or 2-dimensional array * DIMENSIONS:: return the :dimensions from the header of a 1- or 2-dimensional array * FLUSH-COMPRESS:: flush the under-the-hood array for the given name * HEADER:: return the header of a 1- or 2-dimensional array * MAXIMUM-LENGTH:: return the :maximum-length from the header of an array * SLOW-ARRAY-WARNING:: a warning or error issued when arrays are used inefficiently See *note ARRAYS-EXAMPLE:: for a brief introduction illustrating the use of ACL2 arrays. ACL2 provides relatively efficient 1- and 2-dimensional arrays. Arrays are awkward to provide efficiently in an applicative language because the programmer rightly expects to be able to "modify" an array object with the effect of changing the behavior of the element accessing function on that object. This, of course, does not make any sense in an applicative setting. The element accessing function is, after all, a function, and its behavior on a given object is immutable. To "modify" an array object in an applicative setting we must actually produce a new array object. Arranging for this to be done efficiently is a challenge to the implementors of the language. In addition, the programmer accustomed to the von Neumann view of arrays must learn how to use immutable applicative arrays efficiently. In this note we explain 1-dimensional arrays. In particular, we explain briefly how to create, access, and "modify" them, how they are implemented, and how to program with them. 2-dimensional arrays are dealt with by analogy. _The Logical Description of ACL2 Arrays_ An ACL2 1-dimensional array is an object that associates arbitrary objects with certain integers, called "indices." Every array has a dimension, dim, which is a positive integer. The indices of an array are the consecutive integers from 0 through dim-1. To obtain the object associated with the index i in an array a, one uses (aref1 name a i). Name is a symbol that is irrelevant to the semantics of aref1 but affects the speed with which it computes. We will talk more about array "names" later. To produce a new array object that is like a but which associates val with index i, one uses (aset1 name a i val). An ACL2 1-dimensional array is actually an alist. There is no special ACL2 function for creating arrays; they are generally built with the standard list processing functions list and cons. However, there is a special ACL2 function, called compress1, for speeding up access to the elements of such an alist. We discuss compress1 later. One element of the alist must be the "header" of the array. The header of a 1-dimensional array with dimension dim is of the form: (:HEADER :DIMENSIONS (dim) :MAXIMUM-LENGTH max :DEFAULT obj ; optional :NAME name ; optional :ORDER order ; optional values are < (the default), >, or :none/nil ). Obj may be any object and is called the "default value" of the array. Max must be an integer greater than dim. Name must be a symbol. The :default and :name entries are optional; if :default is omitted, the default value is nil. The function header, when given a name and a 1- or 2-dimensional array, returns the header of the array. The functions dimensions, maximum-length, and default are similar and return the corresponding fields of the header of the array. The role of the :dimensions field is obvious: it specifies the legal indices into the array. The roles played by the :maximum-length and :default fields are described below. Aside from the header, the other elements of the alist must each be of the form (i . val), where i is an integer and 0 <= i < dim, and val is an arbitrary object. The :order field of the header is ignored for 2-dimensional arrays. For 1-dimensional arrays, it specifies the order of keys (i, above) when the array is compressed as with compress1, as described below. An :order of :none or nil specifies no reordering of the alist by compress1, and an order of > specifies reordering by compress1 so that keys are in descending order. Otherwise, the alist is reordered by compress1 so that keys are in ascending order. (Aref1 name a i) is guarded so that name must be a symbol, a must be an array and i must be an index into a. The value of (aref1 name a i) is either (cdr (assoc i a)) or else is the default value of a, depending on whether there is a pair in a whose car is i. Note that name is irrelevant to the value of an aref1 expression. You might :pe aref1 to see how simple the definition is. (Aset1 name a i val) is guarded analogously to the aref1 expression. The value of the aset1 expression is essentially (cons (cons i val) a). Again, name is irrelevant. Note (aset1 name a i val) is an array, a', with the property that (aref1 name a' i) is val and, except for index i, all other indices into a' produce the same value as in a. Note also that if a is viewed as an alist (which it is) the pair "binding" i to its old value is in a' but "covered up" by the new pair. Thus, the length of an array grows by one when aset1 is done. Because aset1 covers old values with new ones, an array produced by a sequence of aset1 calls may have many irrelevant pairs in it. The function compress1 can remove these irrelevant pairs. Thus, (compress1 name a) returns an array that is equivalent (vis-a-vis aref1) to a but which may be shorter. For technical reasons, the alist returned by compress1 may also list the pairs in a different order than listed in a. To prevent arrays from growing excessively long due to repeated aset1 operations, aset1 actually calls compress1 on the new alist whenever the length of the new alist exceeds the :maximum-length entry, max, in the header of the array. See the definition of aset1 (for example by using :pe). This is primarily just a mechanism for freeing up cons space consumed while doing aset1 operations. Note however that this compress1 call is replaced by a hard error if the header specifies an :order of :none or nil. This completes the logical description of 1-dimensional arrays. 2-dimensional arrays are analogous. The :dimensions entry of the header of a 2-dimensional array should be (dim1 dim2). A pair of indices, i and j, is legal iff 0 <= i < dim1 and 0 <= j < dim2. The :maximum-length must be greater than dim1*dim2. Aref2, aset2, and compress2 are like their counterparts but take an additional index argument. Finally, the pairs in a 2-dimensional array are of the form ((i . j) . val). _The Implementation of ACL2 Arrays_ Very informally speaking, the function compress1 "creates" an ACL2 array that provides fast access, while the function aref1 "maintains" fast access. We now describe this informal idea more carefully. Aref1 is essentially assoc. If aref1 were implemented naively the time taken to access an array element would be linear in the dimension of the array and the number of "assignments" to it (the number of aset1 calls done to create the array from the initial alist). This is intolerable; arrays are "supposed" to provide constant-time access and change. The apparently irrelevant names associated with ACL2 arrays allow us to provide constant-time access and change when arrays are used in "conventional" ways. The implementation of arrays makes it clear what we mean by "conventional." Recall that array names are symbols. Behind the scenes, ACL2 associates two objects with each ACL2 array name. The first object is called the "semantic value" of the name and is an alist. The second object is called the "raw lisp array" and is a Common Lisp array. When (compress1 name alist) builds a new alist, a', it sets the semantic value of name to that new alist. Furthermore, it creates a Common Lisp array and writes into it all of the index/value pairs of a', initializing unassigned indices with the default value. This array becomes the raw lisp array of name. Compress1 then returns a', the semantic value, as its result, as required by the definition of compress1. When (aref1 name a i) is invoked, aref1 first determines whether the semantic value of name is a (i.e., is eq to the alist a). If so, aref1 can determine the ith element of a by invoking Common Lisp's aref function on the raw lisp array associated with name. Note that no linear search of the alist a is required; the operation is done in constant time and involves retrieval of two global variables, an eq test and jump, and a raw lisp array access. In fact, an ACL2 array access of this sort is about 5 times slower than a C array access. On the other hand, if name has no semantic value or if it is different from a, then aref1 determines the answer by linear search of a as suggested by the assoc-like definition of aref1. Thus, aref1 always returns the axiomatically specified result. It returns in constant time if the array being accessed is the current semantic value of the name used. The ramifications of this are discussed after we deal with aset1. When (aset1 name a i val) is invoked, aset1 does two conses to create the new array. Call that array a'. It will be returned as the answer. (In this discussion we ignore the case in which aset1 does a compress1.) However, before returning, aset1 determines if name's semantic value is a. If so, it makes the new semantic value of name be a' and it smashes the raw lisp array of name with val at index i, before returning a' as the result. Thus, after doing an aset1 and obtaining a new semantic value a', all aref1s on that new array will be fast. Any aref1s on the old semantic value, a, will be slow. To understand the performance implications of this design, consider the chronological sequence in which ACL2 (Common Lisp) evaluates expressions: basically inner-most first, left-to-right, call-by-value. An array use, such as (aref1 name a i), is "fast" (constant-time) if the alist supplied, a, is the value returned by the most recently executed compress1 or aset1 on the name supplied. In the functional expression of "conventional" array processing, all uses of an array are fast. The :name field of the header of an array is completely irrelevant. Our convention is to store in that field the symbol we mean to use as the name of the raw lisp array. But no ACL2 function inspects :name and its primary value is that it allows the user, by inspecting the semantic value of the array -- the alist -- to recall the name of the raw array that probably holds that value. We say "probably" since there is no enforcement that the alist was compressed under the name in the header or that all asets used that name. Such enforcement would be inefficient. _Some Programming Examples_ In the following examples we will use ACL2 "global variables" to hold several arrays. See *note @: atsign, and see *note ASSIGN::. Let the state global variable a be the 1-dimensional compressed array of dimension 5 constructed below. ACL2 !>(assign a (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero)))) Then (aref1 'demo (@ a) 0) is zero and (aref1 'demo (@ a) 1) is uninitialized. Now execute ACL2 !>(assign b (aset1 'demo (@ a) 1 'one)) Then (aref1 'demo (@ b) 0) is zero and (aref1 'demo (@ b) 1) is one. All of the aref1s done so far have been "fast." Note that we now have two array objects, one in the global variable a and one in the global variable b. B was obtained by assigning to a. That assignment does not affect the alist a because this is an applicative language. Thus, (aref1 'demo (@ a) 1) must *still* be uninitialized. And if you execute that expression in ACL2 you will see that indeed it is. However, a rather ugly comment is printed, namely that this array access is "slow." The reason it is slow is that the raw lisp array associated with the name demo is the array we are calling b. To access the elements of a, aref1 must now do a linear search. Any reference to a as an array is now "unconventional;" in a conventional language like Ada or Common Lisp it would simply be impossible to refer to the value of the array before the assignment that produced our b. Now let us define a function that counts how many times a given object, x, occurs in an array. For simplicity, we will pass in the name and highest index of the array: ACL2 !>(defun cnt (name a i x) (declare (xargs :guard (and (array1p name a) (integerp i) (>= i -1) (< i (car (dimensions name a)))) :mode :logic :measure (nfix (+ 1 i)))) (cond ((zp (1+ i)) 0) ; return 0 if i is at most -1 ((equal x (aref1 name a i)) (1+ (cnt name a (1- i) x))) (t (cnt name a (1- i) x)))) To determine how many times zero appears in (@ b) we can execute: ACL2 !>(cnt 'demo (@ b) 4 'zero) The answer is 1. How many times does uninitialized appear in (@ b)? ACL2 !>(cnt 'demo (@ b) 4 'uninitialized) The answer is 3, because positions 2, 3 and 4 of the array contain that default value. Now imagine that we want to assign 'two to index 2 and then count how many times the 2nd element of the array occurs in the array. This specification is actually ambiguous. In assigning to b we produce a new array, which we might call c. Do we mean to count the occurrences in c of the 2nd element of b or the 2nd element of c? That is, do we count the occurrences of uninitialized or the occurrences of two? If we mean the former the correct answer is 2 (positions 3 and 4 are uninitialized in c); if we mean the latter, the correct answer is 1 (there is only one occurrence of two in c). Below are ACL2 renderings of the two meanings, which we call [former] and [latter]. (Warning: Our description of these examples, and of an example [fast former] that follows, assumes that only one of these three examples is actually executed; for example, they are not executed in sequence. See "A Word of Warning" below for more about this issue.) (cnt 'demo (aset1 'demo (@ b) 2 'two) 4 (aref1 'demo (@ b) 2)) ; [former] (let ((c (aset1 'demo (@ b) 2 'two))) ; [latter] (cnt 'demo c 4 (aref1 'demo c 2))) Note that in [former] we create c in the second argument of the call to cnt (although we do not give it a name) and then refer to b in the fourth argument. This is unconventional because the second reference to b in [former] is no longer the semantic value of demo. While ACL2 computes the correct answer, namely 2, the execution of the aref1 expression in [former] is done slowly. A conventional rendering with the same meaning is (let ((x (aref1 'demo (@ b) 2))) ; [fast former] (cnt 'demo (aset1 'demo (@ b) 2 'two) 4 x)) which fetches the 2nd element of b before creating c by assignment. It is important to understand that [former] and [fast former] mean exactly the same thing: both count the number of occurrences of uninitialized in c. Both are legal ACL2 and both compute the same answer, 2. Indeed, we can symbolically transform [fast former] into [former] merely by substituting the binding of x for x in the body of the let. But [fast former] can be evaluated faster than [former] because all of the references to demo use the then-current semantic value of demo, which is b in the first line and c throughout the execution of the cnt in the second line. [Fast former] is the preferred form, both because of its execution speed and its clarity. If you were writing in a conventional language you would have to write something like [fast former] because there is no way to refer to the 2nd element of the old value of b after smashing b unless it had been saved first. We turn now to [latter]. It is both clear and efficient. It creates c by assignment to b and then it fetches the 2nd element of c, two, and proceeds to count the number of occurrences in c. The answer is 1. [Latter] is a good example of typical ACL2 array manipulation: after the assignment to b that creates c, c is used throughout. It takes a while to get used to this because most of us have grown accustomed to the peculiar semantics of arrays in conventional languages. For example, in raw lisp we might have written something like the following, treating b as a "global variable": (cnt 'demo (aset 'demo b 2 'two) 4 (aref 'demo b 2)) which sort of resembles [former] but actually has the semantics of [latter] because the b from which aref fetches the 2nd element is not the same b used in the aset! The array b is destroyed by the aset and b henceforth refers to the array produced by the aset, as written more clearly in [latter]. A Word of Warning: Users must exercise care when experimenting with [former], [latter] and [fast former]. Suppose you have just created b with the assignment shown above, ACL2 !>(assign b (aset1 'demo (@ a) 1 'one)) If you then evaluate [former] in ACL2 it will complain that the aref1 is slow and compute the answer, as discussed. Then suppose you evaluate [latter] in ACL2. From our discussion you might expect it to execute fast -- i.e., issue no complaint. But in fact you will find that it complains repeatedly. The problem is that the evaluation of [former] changed the semantic value of demo so that it is no longer b. To try the experiment correctly you must make b be the semantic value of demo again before the next example is evaluated. One way to do that is to execute ACL2 !>(assign b (compress1 'demo (@ b))) before each expression. Because of issues like this it is often hard to experiment with ACL2 arrays at the top-level. We find it easier to write functions that use arrays correctly and efficiently than to so use them interactively. This last assignment also illustrates a very common use of compress1. While it was introduced as a means of removing irrelevant pairs from an array built up by repeated assignments, it is actually most useful as a way of insuring fast access to the elements of an array. Many array processing tasks can be divided into two parts. During the first part the array is built. During the second part the array is used extensively but not modified. If your programming task can be so divided, it might be appropriate to construct the array entirely with list processing, thereby saving the cost of maintaining the semantic value of the name while few references are being made. Once the alist has stabilized, it might be worthwhile to treat it as an array by calling compress1, thereby gaining constant time access to it. ACL2's theorem prover uses this technique in connection with its implementation of the notion of whether a rune is disabled or not. Associated with every rune is a unique integer index, called its "nume." When each rule is stored, the corresponding nume is stored as a component of the rule. Theories are lists of runes and membership in the "current theory" indicates that the corresponding rule is enabled. But these lists are very long and membership is a linear-time operation. So just before a proof begins we map the list of runes in the current theory into an alist that pairs the corresponding numes with t. Then we compress this alist into an array. Thus, given a rule we can obtain its nume (because it is a component) and then determine in constant time whether it is enabled. The array is never modified during the proof, i.e., aset1 is never used in this example. From the logical perspective this code looks quite odd: we have replaced a linear-time membership test with an apparently linear-time assoc after going to the trouble of mapping from a list of runes to an alist of numes. But because the alist of numes is an array, the "apparently linear-time assoc" is more apparent than real; the operation is constant-time.  File: acl2-doc-emacs.info, Node: AREF1, Next: AREF2, Prev: ARRAYS, Up: ARRAYS AREF1 access the elements of a 1-dimensional array Example Form: (aref1 'delta1 a (+ i k)) General Form: (aref1 name alist index) where name is a symbol, alist is a 1-dimensional array and index is a legal index into alist. This function returns the value associated with index in alist, or else the default value of the array. See *note ARRAYS:: for details. This function executes in virtually constant time if alist is in fact the "semantic value" associated with name (see *note ARRAYS::). When it is not, aref1 must do a linear search through alist. In that case the correct answer is returned but a *slow array* comment is printed to the comment window. See *note SLOW-ARRAY-WARNING::.  File: acl2-doc-emacs.info, Node: AREF2, Next: ARRAY1P, Prev: AREF1, Up: ARRAYS AREF2 access the elements of a 2-dimensional array Example Form: (aref2 'delta1 a i j) General Form: (aref2 name alist i j) where name is a symbol, alist is a 2-dimensional array and i and j are legal indices into alist. This function returns the value associated with (i . j) in alist, or else the default value of the array. See *note ARRAYS:: for details. This function executes in virtually constant time if alist is in fact the "semantic value" associated with name (see *note ARRAYS::). When it is not, aref2 must do a linear search through alist. In that case the correct answer is returned but a *slow array* comment is printed to the comment window. See *note SLOW-ARRAY-WARNING::.  File: acl2-doc-emacs.info, Node: ARRAY1P, Next: ARRAY2P, Prev: AREF2, Up: ARRAYS ARRAY1P recognize a 1-dimensional array Example Form: (array1p 'delta1 a) General Form: (array1p name alist) where name and alist are arbitrary objects. This function returns t if alist is a 1-dimensional ACL2 array. Otherwise it returns nil. The function operates in constant time if alist is the semantic value of name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: ARRAY2P, Next: ARRAYS-EXAMPLE, Prev: ARRAY1P, Up: ARRAYS ARRAY2P recognize a 2-dimensional array Example Form: (array2p 'delta1 a) General Form: (array2p name alist) where name and alist are arbitrary objects. This function returns t if alist is a 2-dimensional ACL2 array. Otherwise it returns nil. The function operates in constant time if alist is the semantic value of name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: ARRAYS-EXAMPLE, Next: ASET1, Prev: ARRAY2P, Up: ARRAYS ARRAYS-EXAMPLE an example illustrating ACL2 arrays The example below illustrates the use of ACL2 arrays. It is not, of course, a substitute for the detailed explanations provided elsewhere (see *note ARRAYS::, including subtopics). ACL2 !>(defun defarray (name size initial-element) (compress1 name (cons (list :HEADER :DIMENSIONS (list size) :MAXIMUM-LENGTH (1+ size) :DEFAULT initial-element :NAME name) nil))) Since DEFARRAY is non-recursive, its admission is trivial. We observe that the type of DEFARRAY is described by the theorem (AND (CONSP (DEFARRAY NAME SIZE INITIAL-ELEMENT)) (TRUE-LISTP (DEFARRAY NAME SIZE INITIAL-ELEMENT))). We used the :type-prescription rule COMPRESS1. Summary Form: ( DEFUN DEFARRAY ...) Rules: ((:TYPE-PRESCRIPTION COMPRESS1)) Warnings: None Time: 0.02 seconds (prove: 0.00, print: 0.02, other: 0.00) DEFARRAY ACL2 !>(assign my-ar (defarray 'a1 5 17)) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1)) ACL2 !>(aref1 'a1 (@ my-ar) 3) 17 ACL2 !>(aref1 'a1 (@ my-ar) 8) ACL2 Error in TOP-LEVEL: The guard for the function symbol AREF1, which is (AND (ARRAY1P NAME L) (INTEGERP N) (>= N 0) (< N (CAR (DIMENSIONS NAME L)))), is violated by the arguments in the call (AREF1 'A1 '(#) 8). ACL2 !>(assign my-ar (aset1 'a1 (@ my-ar) 3 'xxx)) ((3 . XXX) (:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1)) ACL2 !>(aref1 'a1 (@ my-ar) 3) XXX ACL2 !>(aset1 'a1 (@ my-ar) 3 'yyy) ; BAD: (@ my-ar) now points to ; an old copy of the array! ((3 . YYY) (3 . XXX) (:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1)) ACL2 !>(aref1 'a1 (@ my-ar) 3) ; Because of "BAD" above, the array ; access is done using assoc rather ; than Lisp aref, hence is slower; ; but the answer is still correct, ; reflecting the value in (@ my-ar), ; which was not changed above. ********************************************************** Slow Array Access! A call of AREF1 on an array named A1 is being executed slowly. See :DOC slow-array-warning ********************************************************** XXX ACL2 !>  File: acl2-doc-emacs.info, Node: ASET1, Next: ASET2, Prev: ARRAYS-EXAMPLE, Up: ARRAYS ASET1 set the elements of a 1-dimensional array Example Form: (aset1 'delta1 a (+ i k) 27) General Form: (aset1 name alist index val) where name is a symbol, alist is a 1-dimensional array named name, index is a legal index into alist, and val is an arbitrary object. See *note ARRAYS:: for details. Roughly speaking this function "modifies" alist so that the value associated with index is val. More precisely, it returns a new array, alist', of the same name and dimension as alist that, under aref1, is everywhere equal to alist except at index where the result is val. That is, (aref1 name alist' i) is (aref1 name alist i) for all legal indices i except index, where (aref1 name alist' i) is val. In order to "modify" alist, aset1 conses a new pair onto the front. If the length of the resulting alist exceeds the :maximum-length entry in the array header, aset1 compresses the array as with compress1. It is generally expected that the "semantic value" of name will be alist (see *note ARRAYS::). This function operates in virtually constant time whether this condition is true or not (unless the compress1 operation is required). But the value returned by this function cannot be used efficiently by subsequent aset1 operations unless alist is the semantic value of name when aset1 is executed. Thus, if the condition is not true, aset1 prints a *slow array* warning to the comment window. See *note SLOW-ARRAY-WARNING::.  File: acl2-doc-emacs.info, Node: ASET2, Next: COMPRESS1, Prev: ASET1, Up: ARRAYS ASET2 set the elements of a 2-dimensional array Example Form: (aset2 'delta1 a i j 27) General Form: (aset2 name alist i j val) where name is a symbol, alist is a 2-dimensional array named name, i and j are legal indices into alist, and val is an arbitrary object. See *note ARRAYS:: for details. Roughly speaking this function "modifies" alist so that the value associated with (i . j) is val. More precisely, it returns a new array, alist', of the same name and dimension as alist that, under aref2, is everywhere equal to alist except at (i . j) where the result is val. That is, (aref2 name alist' x y) is (aref2 name alist x y) for all legal indices x y except i and j where (aref2 name alist' i j) is val. In order to "modify" alist, aset2 conses a new pair onto the front. If the length of the resulting alist exceeds the :maximum-length entry in the array header, aset2 compresses the array as with compress2. It is generally expected that the "semantic value" of name will be alist (see *note ARRAYS::). This function operates in virtually constant time whether this condition is true or not (unless the compress2 operation is required). But the value returned by this function cannot be used efficiently by subsequent aset2 operations unless alist is the semantic value of name when aset2 is executed. Thus, if the condition is not true, aset2 prints a *slow array* warning to the comment window. See *note SLOW-ARRAY-WARNING::.  File: acl2-doc-emacs.info, Node: COMPRESS1, Next: COMPRESS2, Prev: ASET2, Up: ARRAYS COMPRESS1 remove irrelevant pairs from a 1-dimensional array Example Form: (compress1 'delta1 a) General Form: (compress1 name alist) where name is a symbol and alist is a 1-dimensional array, generally named name. See *note ARRAYS:: for details. Logically speaking, this function removes irrelevant pairs from alist, possibly shortening it. The function returns a new array, alist', with the same header (including name and dimension) as alist, that, under aref1, is everywhere equal to alist. That is, (aref1 name alist' i) is (aref1 name alist i), for all legal indices i. Alist' may be shorter than alist and the non-irrelevant pairs may occur in a different order than in alist. Practically speaking, this function plays an important role in the efficient implementation of aref1. In addition to creating the new array, alist', compress1 makes that array the "semantic value" of name and allocates a raw lisp array to name. For each legal index, i, that raw lisp array contains (aref1 name alist' i) in slot i. Thus, subsequent aref1 operations can be executed in virtually constant time provided they are given name and the alist' returned by the most recently executed compress1 or aset1 on name. See *note ARRAYS::. In general, compress1 returns an alist whose cdr is an association list whose keys are nonnegative integers in ascending order. However, if the header specifies an :order of > then the keys will occur in descending order, and if the :order is :none or nil then the keys will not be sorted, i.e., compress1 is logically the identity function (though it still attaches an array under the hood). Note however that a compress1 call is replaced by a hard error if the header specifies an :order of :none or nil and the array's length exceeds the maximum-length field of its header.  File: acl2-doc-emacs.info, Node: COMPRESS2, Next: DEFAULT, Prev: COMPRESS1, Up: ARRAYS COMPRESS2 remove irrelevant pairs from a 2-dimensional array Example Form: (compress2 'delta1 a) General Form: (compress2 name alist) where name is a symbol and alist is a 2-dimensional array, generally named name. See *note ARRAYS:: for details. Logically speaking, this function removes irrelevant pairs from alist, possibly shortening it. The function returns a new array, alist', with the same header (including name and dimension) as alist, that, under aref2, is everywhere equal to alist. That is, (aref2 name alist' i j) is (aref2 name alist i j), for all legal indices i and j. Alist' may be shorter than alist and the non-irrelevant pairs may occur in a different order in alist' than in alist. Practically speaking, this function plays an important role in the efficient implementation of aref2. In addition to creating the new array, alist', compress2 makes that array the "semantic value" of name and allocates a raw lisp array to name. For all legal indices, i and j, that raw lisp array contains (aref2 name alist' i j) in slot i,j. Thus, subsequent aref2 operations can be executed in virtually constant time provided they are given name and the alist' returned by the most recently executed compress2 or aset2 on name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: DEFAULT, Next: DIMENSIONS, Prev: COMPRESS2, Up: ARRAYS DEFAULT return the :default from the header of a 1- or 2-dimensional array Example Form: (default 'delta1 a) General Form: (default name alist) where name is an arbitrary object and alist is a 1- or 2-dimensional array. This function returns the contents of the :default field of the header of alist. When aref1 or aref2 is used to obtain a value for an index (or index pair) not bound in alist, the default value is returned instead. Thus, the array alist may be thought of as having been initialized with the default value. default operates in virtually constant time if alist is the semantic value of name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: DIMENSIONS, Next: FLUSH-COMPRESS, Prev: DEFAULT, Up: ARRAYS DIMENSIONS return the :dimensions from the header of a 1- or 2-dimensional array Example Form: (dimensions 'delta1 a) General Form: (dimensions name alist) where name is arbitrary and alist is a 1- or 2-dimensional array. This function returns the dimensions list of the array alist. That list will either be of the form (dim1) or (dim1 dim2), depending on whether alist is a 1- or 2-dimensional array. Dim1 and dim2 will be integers and each exceed by 1 the maximum legal corresponding index. Thus, if dimensions returns, say, '(100) for an array a named 'delta1, then (aref1 'delta1 a 99) is legal but (aref1 'delta1 a 100) violates the guards on aref1. Dimensions operates in virtually constant time if alist is the semantic value of name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: FLUSH-COMPRESS, Next: HEADER, Prev: DIMENSIONS, Up: ARRAYS FLUSH-COMPRESS flush the under-the-hood array for the given name Example Form: (flush-compress 'my-array) General Form: (flush-compress name) where name is a symbol. Recall that (compress1 nm alist) associates an under-the-hood raw Lisp one-dimensional array of name nm with the given association list, alist, while (compress2 nm alist) is the analogous function for two-dimensional arrays; see *note COMPRESS1:: and see *note COMPRESS2::. The only purpose of flush-compress, which always returns nil, is to remove the association of any under-the-hood array with the given name, thus eliminating slow array accesses (see *note SLOW-ARRAY-WARNING::). It is not necessary if the return values of compress1 and compress2 are always used as the "current" copy of the named array, and thus flush-compress should rarely, if ever, be needed in user applications. Nevertheless, we provide the following contrived example to show how flush-compress can be used to good effect. Comments have been added to this log to provide explanation. ACL2 !>(assign a (compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero) (1 . one)))) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ACL2 !>(aref1 'demo (@ a) 0) ZERO ; As expected, the above evaluation did not cause a slow array warning. Now ; we associate a different under-the-hood array with the name 'demo. ACL2 !>(compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero))) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO)) ; The following array access produces a slow array warning because (@ a) is ; no longer associated under-the-hood with the array name 'demo. ACL2 !>(aref1 'demo (@ a) 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ; Now we associate under-the-hood, with array name 'demo, an alist equal to ; (@ a). ACL2 !>(compress1 'demo '((:header :dimensions (5) :maximum-length 15 :default uninitialized :name demo) (0 . zero) (1 . one))) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ; The following array access is still slow, because the under-the-hood array ; is merely associated with a copy of (@ a), not with the actual object ; (@ a). ACL2 !>(aref1 'demo (@ a) 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ; So we might try to fix the problem by recompressing. But this doesn't ; work. It would work, by the way, if we re-assign a: ; (assign a (compress1 'demo (@ a))). That is why we usually will not need ; flush-compress. ACL2 !>(compress1 'demo (@ a)) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ACL2 !>(aref1 'demo (@ a) 0) ********************************************************** Slow Array Access! A call of AREF1 on an array named DEMO is being executed slowly. See :DOC slow-array-warning ********************************************************** ZERO ; Finally, we eliminate the warning by calling flush-compress before we call ; compress1. The call of flush-compress removes any under-the-hood ; association of an array with the name 'demo. Then the subsequent call of ; compress1 associates the object (@ a) with that name. (Technical point: ; compress1 always associates the indicated name with the value that it ; returns. in this case, what compress1 returns is (@ a), because (@ a) is ; already, logically speaking, a compressed array1p (starts with a :header ; and the natural number keys are ordered). ACL2 !>(flush-compress 'demo) NIL ACL2 !>(compress1 'demo (@ a)) ((:HEADER :DIMENSIONS (5) :MAXIMUM-LENGTH 15 :DEFAULT UNINITIALIZED :NAME DEMO) (0 . ZERO) (1 . ONE)) ACL2 !>(aref1 'demo (@ a) 0) ZERO ACL2 !>  File: acl2-doc-emacs.info, Node: HEADER, Next: MAXIMUM-LENGTH, Prev: FLUSH-COMPRESS, Up: ARRAYS HEADER return the header of a 1- or 2-dimensional array Example Form: (header 'delta1 a) General Form: (header name alist) where name is arbitrary and alist is a 1- or 2-dimensional array. This function returns the header of the array alist. The function operates in virtually constant time if alist is the semantic value of name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: MAXIMUM-LENGTH, Next: SLOW-ARRAY-WARNING, Prev: HEADER, Up: ARRAYS MAXIMUM-LENGTH return the :maximum-length from the header of an array Example Form: (maximum-length 'delta1 a) General Form: (maximum-length name alist) where name is an arbitrary object and alist is a 1- or 2-dimensional array. This function returns the contents of the :maximum-length field of the header of alist. Whenever an aset1 or aset2 would cause the length of the alist to exceed its maximum length, a compress1 or compress2 is done automatically to remove irrelevant pairs from the array. Maximum-length operates in virtually constant time if alist is the semantic value of name. See *note ARRAYS::.  File: acl2-doc-emacs.info, Node: SLOW-ARRAY-WARNING, Prev: MAXIMUM-LENGTH, Up: ARRAYS SLOW-ARRAY-WARNING a warning or error issued when arrays are used inefficiently If you use ACL2 arrays you may sometimes see a *slow array* warning. We explain below what that warning means and some likely "mistakes" it may signify. First, we note that you can control whether or not you get a warning and, if so, whether or not a break (error from which you can continue; see *note BREAK$::) ensues: (assign slow-array-action :warning) ; warn on slow array access (default) (assign slow-array-action :break) ; warn as above, and then call break$ (assign slow-array-action nil) ; do not warn or break on slow array access If you are using ACL2 arrays, then you probably care about performance, in which case it is probably best to avoid the nil setting. Below we assume the default behavior: a warning, but no break. The discussion in the documentation for arrays defines what we mean by the semantic value of a name. As noted there, behind the scenes ACL2 maintains the invariant that with some names there is associated a pair consisting of an ACL2 array alist, called the semantic value of the name, and an equivalent raw lisp array. Access to ACL2 array elements, as in (aref1 name alist i), is executed in constant time when the array alist is the semantic value of the name, because we can just use the corresponding raw lisp array to obtain the answer. Aset1 and compress1 modify the raw lisp array appropriately to maintain the invariant. If aref1 is called on a name and alist, and the alist is not the then-current semantic value of the name, the correct result is computed but it requires linear time because the alist must be searched. When this happens, aref1 prints a *slow array* warning message to the comment window. Aset1 behaves similarly because the array it returns will cause the *slow array* warning every time it is used. From the purely logical perspective there is nothing "wrong" about such use of arrays and it may be spurious to print a warning message. But because arrays are generally used to achieve efficiency, the *slow array* warning often means the user's intentions are not being realized. Sometimes merely performance expectations are not met; but the message may mean that the functional behavior of the program is different than intended. Here are some "mistakes" that might cause this behavior. In the following we suppose the message was printed by aset1 about an array named name. Suppose the alist supplied aset1 is alist. (1) Compress1 was never called on name and alist. That is, perhaps you created an alist that is an array1p and then proceeded to access it with aref1 but never gave ACL2 the chance to create a raw lisp array for it. After creating an alist that is intended for use as an array, you must do (compress1 name alist) and pass the resulting alist' as the array. (2) Name is misspelled. Perhaps the array was compressed under the name 'delta-1 but accessed under 'delta1? (3) An aset1 was done to modify alist, producing a new array, alist', but you subsequently used alist as an array. Inspect all (aset1 name ...) occurrences and make sure that the alist modified is never used subsequently (either in that function or any other). It is good practice to adopt the following syntactic style. Suppose the alist you are manipulating is the value of the local variable alist. Suppose at some point in a function definition you wish to modify alist with aset1. Then write (let ((alist (aset1 name alist i val))) ...) and make sure that the subsequent function body is entirely within the scope of the let. Any uses of alist subsequently will refer to the new alist and it is impossible to refer to the old alist. Note that if you write (foo (let ((alist (aset1 name alist i val))) ...) ; arg 1 (bar alist)) ; arg 2 you have broken the rules, because in arg 1 you have modified alist but in arg 2 you refer to the old value. An appropriate rewriting is to lift the let out: (let ((alist (aset1 name alist alist i val))) (foo ... ; arg 1 (bar alist))) ; arg 2 Of course, this may not mean the same thing. (4) A function which takes alist as an argument and modifies it with aset1 fails to return the modified version. This is really the same as (3) above, but focuses on function interfaces. If a function takes an array alist as an argument and the function uses aset1 (or a subfunction uses aset1, etc.), then the function probably "ought" to return the result produced by aset1. The reasoning is as follows. If the array is passed into the function, then the caller is holding the array. After the function modifies it, the caller's version of the array is obsolete. If the caller is going to make further use of the array, it must obtain the latest version, i.e., that produced by the function.  File: acl2-doc-emacs.info, Node: COMPILATION, Next: DECLARE, Prev: ARRAYS, Up: PROGRAMMING COMPILATION compiling ACL2 functions ACL2 has several mechanisms to speed up the evaluation of function calls by _compiling_ functions: see *note COMP::, see *note SET-COMPILE-FNS::, and see *note CERTIFY-BOOK::. The intention is that compilation never changes the value returned by a function call, though it could cause the call to succeed rather than fail, for example by avoiding a stack overflow. The state global variable 'compiler-enabled is set automatically when the system is built, and may depend on the underlying Lisp implementation. (In order to disable the compiler at build time, which will defeat the speed-up but usually be pretty harmless when the host Lisp is CCL or SBCL, see the discussion of ACL2_COMPILER_DISABLED in distributed file GNUmakefile.) The value of 'compiler-enabled, as returned by (@ compiler-enabled), can be t, :books, or nil. If the value is nil, then include-book and certify-book coerce their arguments :load-compile-file and compile-flg arguments (respectively) to nil. Otherwise, the value is :books or t and there is no such coercion; but if the value is not t, then comp and set-compile-fns are no-ops, which is probably desirable for Lisps such as CCL and SBCL that compile on-the-fly even when the compiler is not explicitly invoked. However, you may have reason to want to change the above (default) behavior. To enable compilation by default for certify-book and include-book but not for comp or set-compile-fns: (set-compiler-enabled :books state) To enable compilation not only as above but also for comp and set-compile-fns: (set-compiler-enabled t state) To suppress compilation and loading of compiled files by include-book (for example, if you get a raw Lisp error such as "Wrong FASL version"): (set-compiler-enabled nil state) See *note BOOK-COMPILED-FILE:: for more discussion about compilation and books.  File: acl2-doc-emacs.info, Node: DECLARE, Next: EQUALITY-VARIANTS, Prev: COMPILATION, Up: PROGRAMMING DECLARE declarations Examples: (declare (ignore x y z)) (declare (ignorable x y z) (type integer i j k) (type (satisfies integerp) m1 m2)) (declare (xargs :guard (and (integerp i) (<= 0 i)) :guard-hints (("Goal" :use (:instance lemma3 (x (+ i j))))))) * Menu: * TYPE-SPEC:: type specifiers in declarations General Form: (declare d1 ... dn) where, in ACL2, each di is of one of the following forms: (ignore v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment. These variables must not occur free in the scope of the declaration. (ignorable v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment. These variables need not occur free in the scope of the declaration. This declaration can be useful for inhibiting compiler warnings. (type type-spec v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment and type-spec is a type specifier (as described in the documentation for type-spec). (xargs :key1 val1 ... :keyn valn) -- where the legal values of the keyi's and their respective vali's are described in the documentation for xargs. Xargs declarations are only allowed at the top level of definitions (defun and defmacro, as shown below). (optimize ...) -- for example, (optimize (safety 3)). This is allowed only at the top level of defun forms. See any Common Lisp documentation for more information. Declarations in ACL2 may occur only where dcl occurs below: (DEFUN name args doc-string dcl ... dcl body) (DEFMACRO name args doc-string dcl ... dcl body) (LET ((v1 t1) ...) dcl ... dcl body) (MV-LET (v1 ...) term dcl ... dcl body) (FLET ((name args dcl ... dcl body) ...)) Of course, if a form macroexpands into one of these (as let* expands into nested lets and our er-let* expands into nested mv-lets) then declarations are permitted as handled by the macros involved. Declare is defined in Common Lisp. See any Common Lisp documentation for more information.  File: acl2-doc-emacs.info, Node: TYPE-SPEC, Prev: DECLARE, Up: DECLARE TYPE-SPEC type specifiers in declarations Examples: The symbol INTEGER in (declare (type INTEGER i j k)) is a type-spec. Other type-specs supported by ACL2 include RATIONAL, COMPLEX, (INTEGER 0 127), (RATIONAL 1 *), CHARACTER, and ATOM. The type-specs and their meanings (when applied to the variable x as in (declare (type type-spec x)) are given below. type-spec meaning (AND type1 ... typek) (AND (p1 X) ... (pk X)) where (pj x) is the meaning for type-spec typej ATOM (ATOM X) BIT (OR (EQUAL X 1) (EQUAL X 0)) CHARACTER (CHARACTERP X) COMPLEX (AND (COMPLEX-RATIONALP X) (RATIONALP (REALPART X)) (RATIONALP (IMAGPART X))) (COMPLEX RATIONAL) same as COMPLEX, above (COMPLEX type) (AND (COMPLEX-RATIONALP X) (p (REALPART X)) (p (IMAGPART X))) where (p x) is the meaning for type-spec type CONS (CONSP X) INTEGER (INTEGERP X) (INTEGER i j) (AND (INTEGERP X) ; See notes below (<= i X) (<= X j)) (MEMBER x1 ... xn) (MEMBER X '(x1 ... xn)) (MOD i) same as (INTEGER 0 i-1) NIL NIL (NOT type) (NOT (p X)) where (p x) is the meaning for type-spec type NULL (EQ X NIL) (OR type1 ... typek) (OR (p1 X) ... (pk X)) where (pj x) is the meaning for type-spec typej RATIO (AND (RATIONALP X) (NOT (INTEGERP X))) RATIONAL (RATIONALP X) (RATIONAL i j) (AND (RATIONALP X) ; See notes below (<= i X) (<= X j)) REAL (RATIONALP X) ; (REALP X) in ACL2(r) (REAL i j) (AND (RATIONALP X) ; See notes below (<= i X) (<= X j)) (SATISFIES pred) (pred X) ; Lisp requires a unary function, not a macro SIGNED-BYTE (INTEGERP X) (SIGNED-BYTE i) same as (INTEGER k m) where k=-2^(i-1), m=2^(i-1)-1 STANDARD-CHAR (STANDARD-CHARP X) STRING (STRINGP X) (STRING max) (AND (STRINGP X) (EQUAL (LENGTH X) max)) SYMBOL (SYMBOLP X) T T UNSIGNED-BYTE same as (INTEGER 0 *) (UNSIGNED-BYTE i) same as (INTEGER 0 (2^i)-1) _Notes:_ In general, (integer i j) means (AND (INTEGERP X) (<= i X) (<= X j)). But if i is the symbol *, the first inequality is omitted. If j is the symbol *, the second inequality is omitted. If instead of being an integer, the second element of the type specification is a list containing an integer, (i), then the first inequality is made strict. An analogous remark holds for the (j) case. The RATIONAL and REAL type specifiers are similarly generalized.  File: acl2-doc-emacs.info, Node: EQUALITY-VARIANTS, Next: IGNORABLE, Prev: DECLARE, Up: PROGRAMMING EQUALITY-VARIANTS versions of a function using different equality tests The ACL2 environment includes not only a logic but also a programming language, which is based on Common Lisp. Execution efficiency may be increased by using fast equality tests: eq for symbols and eql for numbers, symbols, and characters (see *note EQLABLEP::). Several list-processing functions built into ACL2 thus have three variants, depending on whether the equality function used is eq, eql, or equal; a list is provided below. ACL2 has taken measures to ensure that one can reason about a single logical function even when one uses these different variants. Consider for example the case of list membership. Common Lisp provides a utility for this purposes, member, which can take a :TEST keyword argument, default eql. So for example, one might write (member a x :TEST 'eq) if either a is a symbol or x is a list of symbols, so that the fastest equality test (eq) may be used when comparing a to successive elements of the list, x. One might elsewhere write (member b (foo y)), which is equivalent to (member b (foo y) :TEST 'eql), for example if b is a number. If one wants to reason about both (member a x :TEST 'eq) and (member b y), it might be helpful for both calls of member to be the same logically, even though Common Lisp will execute them differently (using eq or eql, respectively). ACL2 arranges that in fact, both references to member generate calls of member-equal in the theorem prover. In fact, since member can take the optional :TEST keyword argument, then in ACl2 it must be defined as a macro, not a function (see *note DEFUN::). ACL2 arranges that a call of member generates a corresponding call of the function member-equal, regardless of the value of TEST, in a manner that produces member-equal in prover output. More generally, you can expect ACL2 to treat your use of member as though you had written member-equal, for example in the way it stores rewrite rules and other kinds of rules as well (see *note RULE-CLASSES::). We say little here about how this is all arranged by ACL2, other than to mention that mbe is utilized (so, you might see mention in proof logs) of the function return-last that implements mbe. Such details, which probably can be ignored by most users, may be found elsewhere; see *note EQUALITY-VARIANTS-DETAILS::. As a convenience to the user, the macro member-eq is provided that expands to a corresponding call of member with :TEST 'eq, as follows. ACL2 !>:trans1 (member-eq (foo x) (bar y)) (MEMBER (FOO X) (BAR Y) :TEST 'EQ) ACL2 !> For efficiency we recommend using the -equal equality variant, for example member-equal or ... :TEST 'equal, in certain contexts: defmacro, defpkg, defconst, and value-triple forms. However, the implementation of equality variants has been designed so that when defining a function, one may choose freely in a definition an equality variant of primitive F, to get efficient execution but where subsequent reasoning is about F-equal. For details about the above recommendation and for a discussion of the implementation, see *note EQUALITY-VARIANTS-DETAILS::. The following alphabetical list includes all primitives that have equality variants. Each macro F listed below takes an optional :TEST keyword argument of 'eq, 'eql, or 'equal, where 'eql is the default. For each such F, a function F-equal is defined such that for logical purposes (in particular theorem proving), each call of F expands to a corresponding call of F-equal. For convenience, a macro F-eq is also defined, so that a call of F-eq expands to a corresponding call of F with :TEST 'eq. add-to-set assoc delete-assoc intersection$ ; (see Note below) intersectp member no-duplicatesp position-ac position put-assoc rassoc remove-duplicates remove1 remove set-difference$ ; (see Note below) subsetp union$ ; (see Note below) Note: Three of the macros above have names ending with the character, `$': intersection$, set-difference$, and union$. In each case there is a corresponding Common Lisp primitive without the trailing `$': intersection, set-difference, and union. However, Common Lisp does not specify the order of elements in the list returned by those primitives, so ACL2 has its own. Nevertheless, the only use of the trailing `$' is to distinguish the primitives; associated functions and macros, for example union-eq and intersection-equal, do not include the `$' character in their names. * Menu: * EQUALITY-VARIANTS-DETAILS:: details about equality-variants  File: acl2-doc-emacs.info, Node: EQUALITY-VARIANTS-DETAILS, Prev: EQUALITY-VARIANTS, Up: EQUALITY-VARIANTS EQUALITY-VARIANTS-DETAILS details about equality-variants Here we present details about equality variants, none of which is likely to be important to the majority of ACL2 users. Please see *note EQUALITY-VARIANTS:: for relevant background. We begin by presenting events that implement the equality variants for member, as these illustrate the events introduced for all macros having equality variants. The definition of member, just below, calls the macro let-mbe, which in turn is just an abbreviation for a combination of let and mbe. (defmacro let-mbe (bindings &key logic exec) `(let ,bindings (mbe :logic ,logic :exec ,exec))) This use of let arranges that each argument of a call of member is evaluated only once. (defmacro member (x l &key (test ''eql)) (declare (xargs :guard (or (equal test ''eq) (equal test ''eql) (equal test ''equal)))) (cond ((equal test ''eq) `(let-mbe ((x ,x) (l ,l)) :logic (member-equal x l) :exec (member-eq-exec x l))) ((equal test ''eql) `(let-mbe ((x ,x) (l ,l)) :logic (member-equal x l) :exec (member-eql-exec x l))) (t ; (equal test 'equal) `(member-equal ,x ,l)))) Inspection of the definition above shows that every call of member expands to one that is logically equivalent to the corresponding call of member-equal, which is defined as follows. (defun member-equal (x lst) (declare (xargs :guard (true-listp lst))) (cond ((endp lst) nil) ((equal x (car lst)) lst) (t (member-equal x (cdr lst))))) The following two definitions model equality variants of member for tests eq and eql, respectively. (defun member-eq-exec (x lst) (declare (xargs :guard (if (symbolp x) (true-listp lst) (symbol-listp lst)))) (cond ((endp lst) nil) ((eq x (car lst)) lst) (t (member-eq-exec x (cdr lst))))) (defun member-eql-exec (x lst) (declare (xargs :guard (if (eqlablep x) (true-listp lst) (eqlable-listp lst)))) (cond ((endp lst) nil) ((eql x (car lst)) lst) (t (member-eql-exec x (cdr lst))))) At this point the user can write (member x y) or (member-equal x y) to call equality variants of member with test eql or equal, respectively. We thus provide the following macro for the eq variant. (defmacro member-eq (x lst) `(member ,x ,lst :test 'eq)) Guard proof obligations generated by calls of member will include those based on its use of mbe, and are supported by the following two lemmas. (defthm member-eq-exec-is-member-equal (equal (member-eq-exec x l) (member-equal x l))) (defthm member-eql-exec-is-member-equal (equal (member-eql-exec x l) (member-equal x l))) Finally, the following two events arrange that in certain contexts such as theories (including the use of in-theory in events and hints), member-eq and member are treated as references to member-equal. (add-macro-alias member-eq member-equal) (add-macro-alias member member-equal) We conclude this topic by exploring the following recommendation made in the documentation for equality-variants. For efficiency we recommend using the -equal equality variant, for example member-equal or ... :TEST 'equal, in certain contexts: defmacro, defpkg, defconst, and value-triple forms. ACL2 reliies on the underlying Common Lisp for evaluation. It also processes events in the ACL2 logic. In order to guarantee consistency of its logical and Common Lisp evaluations, ACL2 uses a "safe mode" to avoid ill-guarded calls. In particular, consider the use of mbe in execution of a call of an equality variant of a primitive, F, other than its F-equal variant. The mbe call discussed above requires a connection to be established between the :logic and :exec forms. For example, if F is called with :TEST 'eql (either explicitly or as the default), then ACL2 will call both F-eql-exec and F-equal, and check that the two results are equal. The following partial log illustrates the point above. We define a macro that calls member, and when a call of this macro is expanded during processing of a subsequent definition, we see that two membership functions are called on the same arguments. ACL2 !>(defmacro mac (lst) (list 'quote (and (true-listp lst) (member 'c lst :test 'eq)))) Summary Form: ( DEFMACRO MAC ...) Rules: NIL Time: 0.01 seconds (prove: 0.00, print: 0.00, other: 0.01) MAC ACL2 !>(trace$ member-equal member-eq-exec) ((MEMBER-EQUAL) (MEMBER-EQ-EXEC)) ACL2 !>(defun f () (mac (a b c d))) 1> (ACL2_*1*_ACL2::MEMBER-EQ-EXEC C (A B C D)) 2> (MEMBER-EQ-EXEC C (A B C D)) <2 (MEMBER-EQ-EXEC (C D)) <1 (ACL2_*1*_ACL2::MEMBER-EQ-EXEC (C D)) 1> (ACL2_*1*_ACL2::MEMBER-EQUAL C (A B C D)) 2> (MEMBER-EQUAL C (A B C D)) <2 (MEMBER-EQUAL (C D)) <1 (ACL2_*1*_ACL2::MEMBER-EQUAL (C D)) Since F is non-recursive, its admission is trivial. If performance is an issue then we can avoid such a problem, for example as follows. In a fresh session, let us define a suitable wrapper for calling member with :TEST 'eq. This time, the trace in our partial log shows that we have avoided calling two membership functions. ACL2 !>(defun mem-eq (x lst) (declare (xargs :guard (if (symbolp x) (true-listp lst) (symbol-listp lst)))) (member x lst :test 'eq)) [[ ... output omitted here ... ]] MEM-EQ ACL2 !>(defmacro mac (lst) (list 'quote (and (true-listp lst) (mem-eq 'c lst)))) Summary Form: ( DEFMACRO MAC ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) MAC ACL2 !>(trace$ member-equal member-eq-exec mem-eq) ((MEMBER-EQUAL) (MEMBER-EQ-EXEC) (MEM-EQ)) ACL2 !>(defun f () (mac (a b c d))) 1> (ACL2_*1*_ACL2::MEM-EQ C (A B C D)) 2> (MEM-EQ C (A B C D)) <2 (MEM-EQ (C D)) <1 (ACL2_*1*_ACL2::MEM-EQ (C D)) Since F is non-recursive, its admission is trivial.  File: acl2-doc-emacs.info, Node: IGNORABLE, Next: IGNORE, Prev: EQUALITY-VARIANTS, Up: PROGRAMMING IGNORABLE See *note DECLARE::.  File: acl2-doc-emacs.info, Node: IGNORE, Next: IRRELEVANT-FORMALS, Prev: IGNORABLE, Up: PROGRAMMING IGNORE See *note DECLARE::.  File: acl2-doc-emacs.info, Node: IRRELEVANT-FORMALS, Next: OPTIMIZE, Prev: IGNORE, Up: PROGRAMMING IRRELEVANT-FORMALS formals that are used but only insignificantly Let fn be a function of n arguments. Let x be the ith formal of fn. We say x is "irrelevant in fn" if x does not occur in either the guard or the measure for fn, and the value of (fn a1...ai...an) is independent of ai. The easiest way to define a function with an irrelevant formal is simply not to use the formal in the body of the function. Such formals are said to be "ignored" by Common Lisp and a special declaration is provided to allow ignored formals. ACL2 makes a distinction between ignored and irrelevant formals. Note however that if a variable is declared ignored or ignorable, then it will not be reported as irrelevant. An example of an irrelevant formal is x in the definition of fact below. (defun fact (i x) (declare (xargs :guard (and (integerp i) (<= 0 i)))) (if (zerop i) 1 (* i (fact (1- i) (cons i x))))). Observe that x is only used in recursive calls of fact; it never "gets out" into the result. ACL2 can detect some irrelevant formals by a closure analysis on how the formals are used. For example, if the ith formal is only used in the ith argument position of recursive calls, then it is irrelevant. This is how x is used above. It is possible for a formal to appear only in recursive calls but still be relevant. For example, x is *not* irrelevant below, even though it only appears in the recursive call. (defun fn (i x) (if (zerop i) 0 (fn x (1- i)))) The key observation above is that while x only appears in a recursive call, it appears in an argument position, namely i's, that is relevant. (The function above can be admitted with a :measure of (+ (nfix i) (nfix x)).) Establishing that a formal is irrelevant, in the sense defined above, can be an arbitrarily hard problem because it requires theorem proving. For example, is x irrelevant below? (defun test (i j k x) (if (p i j k) 0 x)) Note that the value of (test i j k x) is independent of x -- thus making x irrelevant -- precisely if (p i j k) is a theorem. ACL2's syntactic analysis of a definition does not guarantee to notice all irrelevant formals. We regard the presence of irrelevant formals as an indication that something is wrong with the definition. We cause an error on such definitions and suggest that you recode the definition so as to eliminate the irrelevant formals. If you must have an irrelevant formal, one way to "trick" ACL2 into accepting the definition, without slowing down the execution of your function, is to use the formal in an irrelevant way in the guard. For example, to admit fact, above, with its irrelevant x one might use (defun fact (i x) (declare (xargs :guard (and (integerp i) (<= 0 i) (equal x x)))) (if (zerop i) 0 (* i (fact (1- i) (cons i x))))) For those who really want to turn off this feature, we have provided a way to use the acl2-defaults-table for this purpose; see *note SET-IRRELEVANT-FORMALS-OK::.  File: acl2-doc-emacs.info, Node: OPTIMIZE, Next: REDEFINING-PROGRAMS, Prev: IRRELEVANT-FORMALS, Up: PROGRAMMING OPTIMIZE See *note DECLARE::.  File: acl2-doc-emacs.info, Node: REDEFINING-PROGRAMS, Next: SINGLE-THREADED-OBJECTS, Prev: OPTIMIZE, Up: PROGRAMMING REDEFINING-PROGRAMS an explanation of why we restrict redefinitions ACL2 does not in general allow the redefinition of functions because logical inconsistency can result: previously stored theorems can be rendered invalid if the axioms defining the functions involved are changed. However, to permit prototyping of both :program and :logic mode systems, ACL2 permits redefinition if the user has accepted logical responsibility for the consequences by setting ld-redefinition-action to an appropriate non-nil value. The refusal of ACL2 to support the unrestricted redefinition of :program mode functions may appear somewhat capricious. After all, what are the logical consequences of changing a definition if no axioms are involved? Three important points should be made before we discuss redefinition further. The first is that ACL2 does support redefinition (of both :program and :logic functions) when ld-redefinition-action is non-nil. The second is that a "redefinition" that does not change the mode, formals, guards, type declarations, stobjs, or body of a function is considered redundant and is permitted even when ld-redefinition-action is nil. We recognize and permit redundant definitions because it is not uncommon for two distinct books to share identical function definitions. When determining whether the body of a function is changed by a proposed redefinition, we actually compare the untranslated versions of the two bodies. See *note TERM::. For example, redundancy is not recognized if the old body is (list a b) and the new body is (cons a (cons b nil)). We use the untranslated bodies because of the difficulty of translating the new body in the presence of the old syntactic information, given the possibility that the redefinition might attempt to change the signature of the function, i.e., the number of formals, the number of results, or the position of single-threaded objects in either. The third important point is that a "redefinition" that preserves the formals, guard, types, stobjs, and body but changes the mode from :program to :logic is permitted even when ld-redefinition-action is nil. That is what verify-termination does. This note addresses the temptation to allow redefinition of :program functions in situations other than the three described above. Therefore, suppose ld-redefinition-action is nil and consider the cases. Case 1. Suppose the new definition attempts to change the formals or more generally the signature of the function. Accepting such a redefinition would render ill-formed other :program functions which call the redefined function. Subsequent attempts to evaluate those callers could arbitrarily damage the Common Lisp image. Thus, redefinition of :program functions under these circumstances requires the user's active approval, as would be sought with ld-redefinition-action '(:query . :overwrite). Case 2. Suppose the new definition attempts to change the body (even though it preserves the signature). At one time we believed this was acceptable and ACL2 supported the quiet redefinition of :program mode functions in this circumstance. However, because such functions can be used in macros and redundancy checking is based on untranslated bodies, this turns out to be unsound! (Aside: Perhaps this is not an issue if the function takes state or a user-defined stobj argument; but we do not further consider this distinction.) Such redefinition is therefore now prohibited. We illustrate such an unsoundness below. Let foo-thm1.lisp be a book with the following contents. (in-package "ACL2") (defun p1 (x) (declare (xargs :mode :program)) (list 'if x 't 'nil)) (defmacro p (x) (p1 x)) (defun foo (x) (p x)) (defthm foo-thm1 (iff (foo x) x) :rule-classes nil) Note that the macro form (p x) translates to (if x t nil). The :program function p1 is used to generate this translation. The function foo is defined so that (foo x) is (p x) and a theorem about foo is proved, namely, that (foo x) is true iff x is true. Now let foo-thm2.lisp be a book with the following contents. (in-package "ACL2") (defun p1 (x) (declare (xargs :mode :program)) (list 'if x 'nil 't)) (defmacro p (x) (p1 x)) (defun foo (x) (p x)) (defthm foo-thm2 (iff (foo x) (not x)) :rule-classes nil) In this book, the :program function p1 is defined so that (p x) means just the negation of what it meant in the first book, namely, (if x nil t). The function foo is defined identically -- more precisely, the untranslated body of foo is identical in the two books, but because of the difference between the two versions of the :program function p1 the axioms defining the two foos are different. In the second book we prove the theorem that (foo x) is true iff x is nil. Now consider what would happen if the signature-preserving redefinition of :program functions were permitted and these two books were included. When the second book is included the redefinition of p1 would be permitted since the signature is preserved and p1 is just a :program. But then when the redefinition of foo is processed it would be considered redundant and thus be permitted. The result would be a logic in which it was possible to prove that (foo x) is equivalent to both x and (not x). In particular, the following sequence leads to a proof of nil: (include-book "foo-thm1") (include-book "foo-thm2") (thm nil :hints (("Goal" :use (foo-thm1 foo-thm2)))) It might be possible to loosen the restrictions on the redefinition of :program functions by allowing signature-preserving redefinition of :program functions not involved in macro definitions. Alternatively, we could implement definition redundancy checking based on the translated bodies of functions (though that is quite problematic). Barring those two changes, we believe it is necessary simply to impose the same restrictions on the redefinition of :program mode functions as we do on :logic mode functions.  File: acl2-doc-emacs.info, Node: SINGLE-THREADED-OBJECTS, Next: STATE, Prev: REDEFINING-PROGRAMS, Up: PROGRAMMING SINGLE-THREADED-OBJECTS See *note STOBJ::.  File: acl2-doc-emacs.info, Node: STATE, Next: TIME-TRACKER, Prev: SINGLE-THREADED-OBJECTS, Up: PROGRAMMING STATE the von Neumannesque ACL2 state object Note: If you are interested in programming with state, see *note PROGRAMMING-WITH-STATE:: after reading the information below. * Menu: * ERROR-TRIPLES:: a common ACL2 programming idiom * PROGRAMMING-WITH-STATE:: programming using the von Neumannesque ACL2 state object The ACL2 state object is used extensively in programming the ACL2 system, and has been used in other ACL2 programs as well. However, most users, especially those interested in specification and verification (as opposed to programming per se), need not be aware of the role of the state object in ACL2, and will not write functions that use it explicitly. We say more about this point at the end of this documentation topic. The ACL2 state object is an example of a single-threaded object or stobj. ACL2 allows the user to define new single-threaded objects. Generally, ACL2 may need to access the ACL2 state but should not (cannot) change it except via a certain set of approved functions such as defun and defthm. If you need a state-like object to which you have complete rights, you may want a stobj. Key to the idea of our state is the notion of single-threadedness. For an explanation, see *note STOBJ::. The upshot of it is that state is a variable symbol with severe restrictions on its use, so that it can be passed into only certain functions in certain slots, and must be returned by those functions that "modify" it. Henceforth, we do not discuss single-threaded objects in general (which the user can introduce with defstobj and defabsstobj) but one in particular, namely ACL2's state object. The global table is perhaps the most visible portion of the state object. Using the interface functions @ and assign, a user may bind global variables to the results of function evaluations (much as an Nqthm user exploits the Nqthm utility r-loop). See *note @: atsign, and see *note ASSIGN::. ACL2 supports several facilities of a truly von Neumannesque state machine character, including file io and global variables. Logically speaking, the state is a true list of the 14 components described below. There is a "current" state object at the top-level of the ACL2 command loop. This object is understood to be the value of what would otherwise be the free variable state appearing in top-level input. When any command returns a state object as one of its values, that object becomes the new current state. But ACL2 provides von Neumann style speed for state operations by maintaining only one physical (as opposed to logical) state object. Operations on the state are in fact destructive. This implementation does not violate the applicative semantics because we enforce certain draconian syntactic rules regarding the use of state objects. For example, one cannot "hold on" to an old state, access the components of a state arbitrarily, or "modify" a state object without passing it on to subsequent state-sensitive functions. Every routine that uses the state facilities (e.g. does io, or calls a routine that does io), must be passed a "state object." And a routine must return a state object if the routine modifies the state in any way. Rigid syntactic rules governing the use of state objects are enforced by the function translate, through which all ACL2 user input first passes. State objects can only be "held" in the formal parameter state, never in any other formal parameter and never in any structure (excepting a multiple-value return list field which is always a state object). State objects can only be accessed with the primitives we specifically permit. Thus, for example, one cannot ask, in code to be executed, for the length of state or the car of state. In the statement and proof of theorems, there are no syntactic rules prohibiting arbitrary treatment of state objects. Logically speaking, a state object is a true list whose members are as follows: Open-input-channels, an alist with keys that are symbols in package "ACL2-INPUT-CHANNEL". The value (cdr) of each pair has the form ((:header type file-name open-time) . elements), where type is one of :character, :byte, or :object and elements is a list of things of the corresponding type, i.e. characters, integers of type (mod 255), or lisp objects in our theory. File-name is a string. Open-time is an integer. See *note IO::. Open-output-channels, an alist with keys that are symbols in package "ACL2-OUTPUT-CHANNEL". The value of a pair has the form ((:header type file-name open-time) . current-contents). See *note IO::. Global-table, an alist associating symbols (to be used as "global variables") with values. See *note @: atsign, and see *note ASSIGN::. T-stack, a list of arbitrary objects accessed and changed by the functions aref-t-stack and aset-t-stack. 32-bit-integer-stack, a list of arbitrary 32-bit-integers accessed and changed by the functions aref-32-bit-integer-stack and aset-32-bit-integer-stack. Big-clock-entry, an integer, that is used logically to bound the amount of effort spent to evaluate a quoted form. Idates, a list of dates and times, used to implement the function print-current-idate, which prints the date and time. Acl2-oracle, a list of objects, used for example to implement the functions that let ACL2 report how much time was used, but inaccessible to the user. Also see *note WITH-PROVER-TIME-LIMIT::. File-clock, an integer that is increased on every file opening and closing, and on each call of sys-call, and is used to maintain the consistency of the io primitives. Readable-files, an alist whose keys have the form (string type time), where string is a file name and time is an integer. The value associated with such a key is a list of characters, bytes, or objects, according to type. The time field is used in the following way: when it comes time to open a file for input, we will only look for a file of the specified name and type whose time field is that of file-clock. This permits us to have a "probe-file" aspect to open-file: one can ask for a file, find it does not exist, but come back later and find that it does now exist. Written-files, an alist whose keys have the form (string type time1 time2), where string is a file name, type is one of :character, :byte or :object, and time1 and time2 are integers. Time1 and time2 correspond to the file-clock time at which the channel for the file was opened and closed. This field is write-only; the only operation that affects this field is close-output-channel, which conses a new entry on the front. Read-files, a list of the form (string type time1 time2), where string is a file name and time1 and time2 were the times at which the file was opened for reading and closed. This field is write only. Writeable-files, an alist whose keys have the form (string type time). To open a file for output, we require that the name, type, and time be on this list. List-all-package-names-lst, a list of true-listps. Roughly speaking, the car of this list is the list of all package names known to this Common Lisp right now and the cdr of this list is the value of this state variable after you look at its car. The function, list-all-package-names, which takes the state as an argument, returns the car and cdrs the list (returning a new state too). This essentially gives ACL2 access to what is provided by CLTL's list-all-packages. Defpkg uses this feature to ensure that the about-to-be-created package is new in this lisp. Thus, for example, in akcl it is impossible to create the package "COMPILER" with defpkg because it is on the list, while in Lucid that package name is not initially on the list. User-stobj-alist, an alist which associates user-defined single-threaded objects (see *note STOBJ::) with their values. We recommend avoiding the use of the state object when writing ACL2 code intended to be used as a formal model of some system, for several reasons. First, the state object is complicated and contains many components that are oriented toward implementation and are likely to be irrelevant to the model in question. Second, there is currently not much support for reasoning about ACL2 functions that manipulate the state object, beyond their logical definitions. Third, the documentation about state is not as complete as one might wish. User-defined single-threaded objects offer the speed of state while giving the user complete access to all the fields. See *note STOBJ::. Again, if you are interested in programming with state see *note PROGRAMMING-WITH-STATE::.  File: acl2-doc-emacs.info, Node: ERROR-TRIPLES, Next: PROGRAMMING-WITH-STATE, Prev: STATE, Up: STATE ERROR-TRIPLES a common ACL2 programming idiom When evaluation returns three values, where the first two are ordinary (non-stobj) objects and the third is the ACL2 state, the result may be called an "error triple". If an error triple is (mv erp val state), we think of erp as an error flag and val as the returned value. By default, if the result of evaluating a top-level form is an error triple (mv erp val state), then that result is not printed if erp is non-nil or if val is the keyword :INVISIBLE, and otherwise val is printed with a preceding space. For example: ACL2 !>(+ 3 4) ; ordinary value 7 ACL2 !>(mv nil (+ 3 4) state) ; error triple, error component of nil 7 ACL2 !>(mv t (+ 3 4) state) ; error triple, non-nil error component ACL2 !>(mv nil :invisible state) ; special case for :INVISIBLE ACL2 !> See *note PROGRAMMING-WITH-STATE:: for a discussion of error triples and how to program with them. Also see *note LD-ERROR-TRIPLES:: and see *note LD:: for a discussion of the value :COMMAND-CONVENTIONS for keyword :LD-POST-EVAL-PRINT.  File: acl2-doc-emacs.info, Node: PROGRAMMING-WITH-STATE, Prev: ERROR-TRIPLES, Up: STATE PROGRAMMING-WITH-STATE programming using the von Neumannesque ACL2 state object This documentation section introduces some common techniques for programming using the ACL2 state object. A prerequisite is thus a basic understanding of that object; see *note STATE::. We hope this section is useful, and we invite suggestions for improvements and additions. A supplement to this section is the ACL2 source code, which uses most (and probably all) of the techniques discussed here. That code is thus a source of many examples, which can serve as "templates" to guide one's own programming with state. Recall that "ACL2" stands for "A Computational Logic for Applicative Common Lisp". In particular, the language is applicative: there are no global variables or side effects. For many purposes this does not feel restrictive; for example, an ACL2 user who is programming in raw Lisp may well be more comfortable coding a factorial function applicatively, using recursion, rather than using iteration with repeated assignment to the same variable. However, there are situations that call for reading or modifying the system state, such as performing input and output, signalling errors, saving information from one computation for use in a later one, or reading and updating system-level or environmental data. This section provides an introductory guide for writing functions that traffic in state. We emphasize that this guide is intended as an introduction; more complete documentation may often be found by following links to documentation of individual utilities, and again, more examples may be found by searching the ACL2 source code for uses of the functions and macros mentioned below. The rest of this section is organized as follows. ENABLING PROGRAMMING WITH STATE STATE GLOBALS AND THE ACL2 LOGICAL WORLD A REMARK ON GUARDS ERRORS AND ERROR TRIPLES SEQUENTIAL PROGRAMMING BINDING VARIABLES USING ERROR TRIPLES BINDING STATE GLOBAL VARIABLES INPUT AND OUTPUT TIMINGS ENVIRONMENT AND SYSTEM REMARKS ON EVENTS AND LD ADVANCED TOPICS ENABLING PROGRAMMING WITH STATE In order to submit a definition that takes state as a formal parameter, you must either declare state as a :stobj (see *note XARGS::) or first evaluate the following form at the top level: (set-state-ok t). Consider for example the following trivial definition. (defun foo (state) (mv 3 state)) If you submit the above definition in a fresh ACL2 session, you will get this error message. ACL2 Error in ( DEFUN FOO ...): The variable symbol STATE should not be used as a formal parameter of a defined function unless you are aware of its unusual status and the restrictions enforced on its use. See :DOC set-state-ok. If first you evaluate (set-state-ok t), you can admit the above definition. Alternatively, you can declare state as a :stobj, as follows. (defun foo (state) (declare (xargs :stobjs state)) (mv 3 state)) A difference in the two approaches is that for the latter, a guard proof obligation is generated by default. See the section below entitled "A remark on guards". STATE GLOBALS AND THE ACL2 LOGICAL WORLD Recall (see *note STATE::) that one of the fields of the ACL2 state object is the global-table, which logically is an alist associating symbols, known as "state globals" or "state global variables", with values. But no such alist actually exists in the implementation. Instead, ACL2 provides utilities for reading state globals -- see *note @: atsign. and see *note F-GET-GLOBAL:: -- and utilities for writing them -- see *note ASSIGN:: and see *note F-PUT-GLOBAL::. The following log shows how they work; further explanation follows below. ACL2 !>(assign my-var (+ 3 4)) 7 ACL2 !>(@ my-var) 7 ACL2 !>(f-put-global 'my-var (+ 1 5) state) ACL2 !>(f-get-global 'my-var state) 6 ACL2 !> Note that the first result is indented by one space. This is ACL2's way to indicate that the assign expression returned an "error triple" and that no error was signalled. We discuss error triples in more detail below; also see *note ERROR-TRIPLES::. As illustrated above, the output signatures of the utilities for assigning to state globals differ from each other as follows: f-put-global returns state, but assign returns an error triple (mv nil val state) where val is the value assigned to the state global. The output signatures of the utilities for reading, @ and f-get-global, are identical. In fact, the form (f-get-global 'my-var state) is the single-step macroexpansion of the form (@ my-var), as can be confirmed using trans1. ACL2 !>:trans1 (@ my-var) (F-GET-GLOBAL 'MY-VAR STATE) ACL2 !> State globals are useful for conveying persistent state information. Consider for example the utility set-inhibit-output-lst. The form (set-inhibit-output-lst '(prove proof-tree)) is approximately equivalent to (assign inhibit-output-lst '(prove proof-tree)). We say "approximately" because set-inhibit-output-lst additionally does some error checking to insure that all the tokens in the new list are legal. When deciding whether to print output, the ACL2 system reads the value of state global variable inhibit-output-lst. A particularly useful state global is current-acl2-world, whose value is the ACL2 logical world. Because the ACL2 world is commonly accessed in applications that use the ACL2 state, ACL2 provides a function that returns the world: (w state) = (f-get-global 'current-acl2-world state). While it is common to read the world, only functions set-w and set-w! are available to write the world, but these are untouchable and these should generally be avoided except by system implementors (pl[remove-untouchable]). A REMARK ON GUARDS For a function definition (see *note DEFUN::), if state is specified as a stobj as with the form (declare (xargs :stobjs state)), then the guard for that function is considered to include the condition (state-p state). By default, guard verification will then be performed. We can illustrate this point by modifying the example above as follows, to read the value of state global gag-mode. (defun foo (state) (declare (xargs :stobjs state)) (f-get-global 'gag-mode state)) If you try this in a fresh ACL2 session, the proof will fail with the following key checkpoint, which says that the state global gag-mode is bound in the global-table of the state. (IMPLIES (STATE-P1 STATE) (ASSOC-EQUAL 'GAG-MODE (NTH 2 STATE))) How can we deal with this proof failure? One way is simply to ignore the issue by defining the function in :program mode, as follows. (defun foo (state) (declare (xargs :stobjs state :mode :program)) (f-get-global 'gag-mode state)) Perhaps a better way is to strengthen the guard to assert that the indicated state global is bound, as follows. (defun foo (state) (declare (xargs :guard (boundp-global 'gag-mode state) :stobjs state)) (f-get-global 'gag-mode state)) Also see *note GUARD-MISCELLANY:: for a discussion of how guards are generated from xargs fields of declare forms, specifically, for keywords :guard and :stobjs. ERRORS AND ERROR TRIPLES When evaluation returns three values, where the first two are ordinary objects and the third is the ACL2 state, the result may be called an "error triple". (Whether it is treated as an error triple depends on the programmer.) Error triples are often denoted (mv erp val state), and common ACL2 programming idioms treat erp as a flag indicating whether an error is being signalled and val as the "value" computed. Also see *note ERROR-TRIPLES::. Even ACL2 users who are not programmers encounter error triples, because these are the values returned by evaluation of ACL2 events. Consider the following log, where the only user input is the defun form following the prompt. ACL2 !>(defun foo (x) x) Since FOO is non-recursive, its admission is trivial. We observe that the type of FOO is described by the theorem (EQUAL (FOO X) X). Summary Form: ( DEFUN FOO ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) FOO ACL2 !> All output above results from explicit calls of output functions, except for the next-to-last line, which contains FOO. Notice the single-space indentation preceding FOO. That space indicates that in fact, the value returned by evaluation of the defun form is the error triple whose error flag is nil and whose computed value is FOO. By default, ACL2 prints any error triple (mv nil val state) by inserting a space before printing val. You can change the default by setting state global ld-post-eval-print to t; notice how the same result is printed below. ACL2 !>:u 0:x(EXIT-BOOT-STRAP-MODE) ACL2 !>(set-ld-post-eval-print t state) (NIL T ) ACL2 !>(defun foo (x) x) Since FOO is non-recursive, its admission is trivial. We observe that the type of FOO is described by the theorem (EQUAL (FOO X) X). Summary Form: ( DEFUN FOO ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) (NIL FOO ) ACL2 !> The way error triples are printed by ld is controlled not only by state global ld-post-eval-print, but also by state global ld-error-triples. These are examples of "ld specials"; see *note LD::, see *note LD-POST-EVAL-PRINT::, and see *note LD-ERROR-TRIPLES::. It is so common to produce an error triple whose first (error flag) component is nil that ACL2 provides a handy macro, value, for this purpose. Thus, (value ) is equivalent to (mv nil state). Also see *note VALUE-TRIPLE:: for a similar construct that is a legal event form. We turn now to the topic of errors. The macro ER "causes" an error, but there are really two quite different kinds of errors: "soft" and "hard" errors. We use the term "soft error" to refer to a form that returns an error triple (mv erp val state) for which erp is non-nil. Soft errors do not interrupt the normal flow of evaluation: the error triple is returned to the caller which interprets the erp flag and val as directed by the programmer. Macros discussed below make it convenient to think about soft errors as short-circuiting the computation. Hard errors, on the other hand, do actually rip control away from the current evaluation and return it to the top-level loop. Logically speaking, expressions that cause hard errors return nil in the error case, but the nil is never seen in actual evaluation because control does not return to the caller. Note that the function abort!, which you can invoke by typing :a!, always returns to the top level. Note that ACL2 can prove that (abort!) returns nil but that this cannot be confirmed by computation. ACL2 !>(thm (equal (abort!) nil)) Q.E.D. Summary Form: ( THM ...) Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL) (:TYPE-PRESCRIPTION ABORT!)) Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !>(equal (abort!) nil) Abort to ACL2 top-level ... ACL2 !> (What actually happens with a hard error, including non-default cases, is a bit subtle; most readers will probably want to skip this paragraph. The read-eval-print loop implemented by ld is implemented by a call of the ACL2 evaluator function, trans-eval, on each input form. If a hard error occurs during evaluation of an input form, its trans-eval call will return with a soft error. Ld, in turn handles that soft error appropriately; see *note LD-ERROR-ACTION::.) The most common way to signal errors is the macro er, which prints a formatted error message and returns a soft or hard error as specified by the call. Note however that soft errors are signalled using :program mode functions. Since the output signatures of soft and hard errors are different -- hard errors "return" a single value while soft errors return a triple -- mixing them in an expression requires embedding the hard error form in (an irrelevant) triple, as illustrated below. All branches of the expression must produce an error triple if any branch does. ACL2 !>(defun chk-find-or-abort (e x state) (declare (xargs :mode :program)) (if (endp x) (value ; Note use of VALUE! (er hard 'chk-find-or-abort "Did not find ~x0!" e)) (if (not (integerp (car x))) (er soft 'chk-find-or-abort "Non-integer, ~x0, in list!" (car x)) (if (eql (car x) e) (value x) (chk-find-or-abort e (cdr x) state))))) Summary Form: ( DEFUN CHK-FIND-OR-ABORT ...) Rules: NIL Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) CHK-FIND-OR-ABORT ACL2 !>(chk-find-or-abort 3 '(1 2 3 4 5) state) (3 4 5) ACL2 !>(chk-find-or-abort 3 '(1 A 3 4 5) state) ACL2 Error in CHK-FIND-OR-ABORT: Non-integer, A, in list! ACL2 !>(chk-find-or-abort 3 '(1 2 4 5) state) HARD ACL2 ERROR in CHK-FIND-OR-ABORT: Did not find 3! ... ACL2 !> See *note ER:: for further discussion of errors. For some other individual topics related to errors see *note ASSERT$::, see *note BREAK-ON-ERROR::, see *note ERROR1::, see *note HARD-ERROR::, see *note ILLEGAL::, and see *note LD-ERROR-TRIPLES::. In the next section we discuss soft errors further, in the context of programming. SEQUENTIAL PROGRAMMING This section describes handy ways to modify state in steps, using macros that implement a sequence of let or mv-let bindings. For example, suppose you want to assign the values 1 and 2 to two state globals one-var and two-var, respectively. Because of ACL2's syntactic restrictions on state, it is not legal simply to write (f-put-global 'two-var 2 (f-put-global 'one-var 1 state)). However, let comes to the rescue as follows. (let ((state (f-put-global 'one-var 1 state))) (let ((state (f-put-global 'two-var 2 state))) state)) It is so common to bind state successively in such a manner that ACL2 provides a macro, pprogn, for this purpose. Thus, an equivalent solution to the problem above is (pprogn (f-put-global 'one-var 1 state) (f-put-global 'two-var 2 state) state) or, more simply, as follows. (pprogn (f-put-global 'one-var 1 state) (f-put-global 'two-var 2 state)) See *note PPROGN::. Note that the last form is allowed to return multiple values; the only requirement on the last form is that its value include state. It is also common to update the state using a sequence of forms such that each returns an error triple, where the intention is for evaluation to short-circuit immediately if a soft error is encountered. Suppose and are expressions that return error triples, where the state components of the error triples might be updated, and one wishes to evaluate and then , returning the (multiple) values returned by unless the error triple returned by is a soft error, in which case that error triple is returned. One can of course do so as follows. (mv-let (erp val state) (cond (erp (mv erp val state)) (t ))) But ACL2 provides a handy macro, er-progn, for this purpose. The following code is equivalent to the code just above. (er-progn ) See *note ER-PROGN:: for more details. Note that unlike pprogn, the return signature for the last expression must be the same as that of the others: an error triple. Let's consider how to use pprogn and er-progn together. In the following example f1 and f2 both return state, while each of g1 and g2 returns an error triple. The following code modifies state by executing these in the order f1, g1, f2, and finally g2, returning (mv nil val state) where val is the value component of the error triple returned by g2 -- except we return a soft error if g1 or g2 returns a soft error. (pprogn (f1 x state) (er-progn (g1 x state) (pprogn (f2 x state) (g2 x state)))) Finally, consider the events progn and progn!. These have similar behavior to that of er-progn. However, progn and progn! may only be used in event contexts, for example at the top level or immediately underneath a call of encapsulate or progn, while er-progn has no such restriction. So when writing code, use er-progn rather than progn or progn!. In particular, the body of a defun must not have any calls of progn (or of progn! either), and the same restriction holds for any code to be executed, such as the body of a make-event form. BINDING VARIABLES USING ERROR TRIPLES In this section we discuss the macro er-let*, which is a variant of the special form, let*, that is useful when programming with state. The macro er-let* is useful when binding variables to the value components of error triples. It is actually quite similar to er-progn, described above, except that er-let* binds variables. First consider the following example. (er-let* ((x1 (f1 state)) (x2 (f2 x1 state))) (value (cons x1 x2))) The code just above is essentially equivalent to writing the following. (mv-let (erp x1 state) (f1 state) (cond (erp (mv erp x1 state)) (t (mv-let (erp x2 state) (f2 x1 state) (cond (erp (mv erp x2 state)) (t (value (cons x1 x2)))))))) As suggested by the example above, er-let* has the same syntax as let*, except that declarations are not supported. (But note that ignore declarations are not needed; all variables that are bound are also used, at least in the error case. Consider replacing (cons x1 x2) by nil in the example displayed immediately above, and note that x1 and x2 are still used.) However, unlike let*, er-let* requires that for each binding (var expr), the expression expr must evaluate to an error triple and, moreover, it requires that the second argument (the "body") of er-let* must evaluate to an error triple. If one of the variable expressions (e.g., the f1 and f2 calls above) signals an error, its error triple is returned as the value of the er-let*. Of course, soft errors can be "caught" by using mv-let instead of er-let* and simply ignoring the error flag or, more generally, by returning a non-erroneous error triple even if the error flag was on. BINDING STATE GLOBAL VARIABLES In this section we introduce a utility, state-global-let*, that is an analogue of let* for state global variables. Consider the following example. (state-global-let* ((inhibit-output-lst (add-to-set-eq 'summary (@ inhibit-output-lst)))) (thm (equal x x))) This form binds state global variable inhibit-output-lst to the result of adding the symbol, summary, to the current value of that state global. Thus (see *note SET-INHIBIT-OUTPUT-LST::), the usual summary is not printed when evaluating this call of thm. See *note STATE-GLOBAL-LET*:: for more complete documentation. INPUT AND OUTPUT In ACL2, most input and output involves the ACL2 state. See *note IO::. TIMINGS For how to obtain the time elapsed since the start of the ACL2 session, see *note READ-RUN-TIME::. For a utility for saving times into the ACL2 state and for printing those saved times, see the community book misc/save-time.lisp. To time an evaluation (though this really isn't about state), see *note TIME$::. ENVIRONMENT AND SYSTEM Next, we mention briefly some ways in which ACL2 interacts with its environment using the ACL2 state. For how to read and write environment variables, see *note GETENV$:: and see *note SETENV$::. For how to run a command in the host operating system, see *note SYS-CALL::. REMARKS ON EVENTS AND LD In general, undefined or surprising behavior may occur when using ACL2 events or calling ld in your programs. In some cases ACL2 enforces restrictions against these uses. We strongly discourage using ld in programs, as it has been designed to be called only at the top level of a read-eval-print loop. There is also a restriction on contexts in which make-event may be called: it may only be called in a context where an event is expected, such as the top level, in a book, or as an argument of encapsulate or progn. The reason is that ACL2 does very subtle and careful tracking of make-event expansions; and it is only able to do this in event contexts, where it is able to carry out such tracking accurately. ADVANCED TOPICS ACL2 provides the function trans-eval to evaluate an arbitrary form (after translating it to a term, i.e., into internal form). For more information, we refer to reader to comments in the definition of trans-eval in the ACL2 source code. There are also many examples of its use in the ACL2 sources. For a function that provides the true absolute filename, with soft links resolved, see *note CANONICAL-PATHNAME::. For a function that returns a check-sum on the characters in a channel, see *note CHECK-SUM::. To obtain a random number, see *note RANDOM$::. If you are programming in raw-mode (see *note SET-RAW-MODE::) or in raw Lisp, use the variable *the-live-state* in place of the variable state. We invite suggestions for additional advanced topics.  File: acl2-doc-emacs.info, Node: TIME-TRACKER, Next: TYPE, Prev: STATE, Up: PROGRAMMING TIME-TRACKER display time spent during specified evaluation The time-tracker macro is a utility for displaying time spent during specified evaluation. In general, the user provides this specification. However, ACL2 itself uses this utility for tracking uses of its tau-system reasoning utility (see *note TIME-TRACKER-TAU::). We discuss that use as an example before discussing the general form for calls of time-tracker. Note that by default, the time being tracked is runtime (cpu time); to switch to realtime (elapsed time), see *note GET-INTERNAL-TIME::. Remark for ACL2(p) users (see *note PARALLELISM::): time-tracker is merely a no-op in ACL2(p). During the development of the tau-system, we were concerned about the possibility that it would slow down proofs without any indication of how one might avoid the problem. We wanted a utility that would alert the user in such situations. However, the tau-system code does not return state, so we could not track time spent in the state. We developed the time-tracker utility to track time and print messages, and we did it in a general way so that others can use it in their own code. Here is an example of such a message that could be printed during a proof. TIME-TRACKER-NOTE [:TAU]: Elapsed runtime in tau is 2.58 secs; see :DOC time-tracker-tau. And here is an example of such a message that could be printed at the end of the proof. TIME-TRACKER-NOTE [:TAU]: For the proof above, the total time spent in the tau system was 20.29 seconds. See :DOC time-tracker-tau. The time-tracker utility tracks computation time spent on behalf of a user-specified "tag". In the case of the tau-system, we chose the tag, :tau. The first argument of time-tracker is the tag, which in our running example is always :tau. Note that although all arguments of time-tracker are evaluated, the first argument is typically a keyword and the second is always a keyword, and such arguments evaluate to themselves. An ACL2 function invoked at the start of a proof includes approximately the following code. (progn$ (time-tracker :tau :end) (time-tracker :tau :init :times '(1 2 3 4 5) :interval 5 :msg "Elapsed runtime in tau is ~st secs; see :DOC ~ time-tracker-tau.~|~%") ...) The first time-tracker call (above) ends any existing time-tracking for tag :tau. One might have expected it be put into code managing the proof summary, but we decided not to rely on that code being executed, say, in case of an interrupt. When a given tag is not already being time-tracked, then :end is a no-op (rather than an error). The second time-tracker call (above) initiates time-tracking for the tag, :tau. Moreover, it specifies the effect of the :print? keyword. Consider the following abbreviated definition from the ACL2 source code. (defun tau-clausep-lst-rec (clauses ens wrld ans ttree state calist) (cond ((endp clauses) (mv (revappend ans nil) ttree calist)) (t (mv-let (flg1 ttree1 calist) (tau-clausep (car clauses) ens wrld state calist) (prog2$ (time-tracker :tau :print?) (tau-clausep-lst-rec (cdr clauses) ...)))))) Notice that (time-tracker :tau :print?) is executed immediately after tau-clausep is called. The idea is to check whether the total time elapsed inside the tau-system justifies printing a message to the user. The specification of :times '(1 2 3 4 5) in the :init form above says that a message should be printed after 1 second, after 2 seconds, ..., and after 5 seconds. Thereafter, the specification of :interval 5 in the :init form above says that each time we print, at least 5 additional seconds should have been tracked before (time-tracker :tau :print?) prints again. Finally, the :msg keyword above specifies just what should be printed. If it is omitted, then a reasonable default message is printed (as discussed below), but in this case we wanted to print a custom message. The :msg string above is what is printed using formatted printing (see *note FMT::), where the character #\t is bound to a string giving a decimal representation with two decimal points of the time tracked so far for tag :tau. (As our general description below points out, :msg can also be a "message" list rather than a string.) But when is time actually tracked for :tau? Consider the following definition from the ACL2 source code. (defun tau-clausep-lst (clauses ens wrld ans ttree state calist) (prog2$ (time-tracker :tau :start) (mv-let (clauses ttree calist) (tau-clausep-lst-rec clauses ens wrld ans ttree state calist) (prog2$ (time-tracker :tau :stop) (mv clauses ttree calist))))) The two calls of time-tracker above first start, and then stop, time-tracking for the tag, :tau. Thus, time is tracked during evaluation of the call of tau-clausep-lst-rec, which is the function (discussed above) that does the tau-system's work. Finally, as noted earlier above, ACL2 may print a time-tracking message for tag :tau at the end of a proof. The ACL2 function print-summary contains essentially the following code. (time-tracker :tau :print? :min-time 1 :msg "For the proof above, the total runtime ~ spent in the tau system was ~st seconds. ~ See :DOC time-tracker-tau.~|~%") The use of :min-time says that we are to ignore the :times and :interval established by the :init call described above, and instead, print a message if and only if at least 1 second (since 1 is the value of keyword :min-time) has been tracked for tag :tau. Formatted printing (see *note FMT::) is used for the value of :msg, where the character #\t is bound to a decimal string representation of the time in seconds, as described above. The example above covers all legal values for the second argument of time-tracker and discusses all the additional legal keyword arguments. We conclude with a precise discussion of all arguments. Note that all arguments are evaluated; thus when we refer to an argument, we are discussing the value of that argument. All times discussed are runtimes, i.e., cpu times, unless that default is changed; see *note GET-INTERNAL-TIME::. General forms: (time-tracker t) ; enable time-tracking (default) (time-tracker nil) ; disable time-tracking (time-tracker tag ; a symbol other than t or nil option ; :init, :end, :start, :stop, or :print? ;; keyword arguments: :times ; non-nil if and only if option is :init :interval ; may only be non-nil with :init option :min-time ; may only be non-nil with :print? option :msg ; may only be non-nil with :init and :print? options Time-tracking is enabled by default. If the first argument is t or nil, then no other arguments are permitted and time-tracking is enabled or disabled, respectively. When time-tracking is disabled, nothing below takes place. Thus we assume time-tracking is enabled for the remainder of this discussion. We also assume below that the first argument is neither t nor nil. We introduce some basic notions about time-tracking. A given tag, such as :tau in the example above, might or might not currently be "tracked": :init causes the specified tag to be tracked, while :end causes the specified tag not to be tracked. If the tag is being tracked, the tag might or might not be "active": :start causes the tag to be in an active state, whie :stop causes the tag not to be active. Note that only tracked tags can be in an active or inactive state. For a tag that is being tracked, the "accumulated time" is the total time spent in an active state since the time that the tag most recently started being tracked, and the "checkpoint list" is a non-empty list of rational numbers specifying when printing may take place, as described below. We now consider each legal value for the second argument, or "option", for a call of time-tracker on a given tag. :Init specifies that the tag is to be tracked. It also establishes defaults for the operation of :print?, as described below, using the :times, :interval, and :msg keywords. Of these three, only :times is required, and its value must be a non-empty list of rational numbers specifying the initial checkpoint list for the tag. It is an error to specify :init if the tag is already being tracked. (So if you don't care whether or not the tag is already being tracked and you want to initiate tracking for that tag, use :end first.) :End specifies that if the tag is being tracked, then it should nstop being tracked. If the tag is not being tracked, then :end has no effect. :Start specifies that the tag is to be active. It is an error to specify :start if the tag is not being tracked or is already active. :Stop specifies that the tag is to stop being active. It is an error to specify :stop if the tag is not being tracked or is not active. :Print? specifies that if the tag is being tracked (not necessarily active), then a message should be printed if a suitable condition is met. The nature of that message and that condition depend on the keyword options supplied with :print? as well as those supplied with the :init option that most recently initiated tracking. :Print? has no effect if the tag is not being tracked, except that if certain keyword values are checked if supplied with :print?: :min-time must be a rational number or nil, and :msg must be either a string, a true-list whose car is a string, or nil. The remainder of this documentation describes the :print? option in detail under the assumption that the tag is being tracked: first, giving the conditions under which it causes a message to be printed, and second, explaining what is printed. When :print? is supplied a non-nil value of :min-time, that value must be a rational number, in which case a message is printed if the accumulated time for the tag is at least that value. Otherwise a message is printed if the accumulated time is greater than or equal to the car of the checkpoint list for the tag. In that case, the tracking state for the tag is updated in the following two ways. First, the checkpoint list is scanned from the front and as long as the accumulated time is greater than or equal to the car of the remaining checkpoint list, that car is popped off the checkpoint list. Second, if the checkpoint list has been completely emptied and a non-nil :interval was supplied when tracking was most recently initiated with the :init option, then the checkpoint list is set to contain a single element, namely the sum of the accumulated time with that value of :interval. Finally, suppose the above criteria are met, so that :print? results in printing of a message. We describe below the message, msg, that is printed. Here is how it is printed (see *note FMT::), where seconds-as-decimal-string is a string denoting the number of seconds of accumulated time for the tag, with two digits after the decimal point. (fms "TIME-TRACKER-NOTE [~x0]: ~@1~|" (list (cons #0 tag) (cons #1 msg) (cons #t seconds-as-decimal-string)) (proofs-co state) state nil) The value of msg is the value of the :msg keyword supplied with :print?, if non-nil; else, the value of :msg supplied when most recently initialization with the :init option, if non-nil; and otherwise, the string "~st s" (the final "s" abbreviating the word "seconds"). It is convenient to supply :msg as a call (msg str arg-0 arg-1 ... arg-k), where str is a string and each arg-i is the value to be associated with #\i upon formatted printing (as with fmt) of the string str.  File: acl2-doc-emacs.info, Node: TYPE, Next: ZERO-TEST-IDIOMS, Prev: TIME-TRACKER, Up: PROGRAMMING TYPE See *note DECLARE::.  File: acl2-doc-emacs.info, Node: ZERO-TEST-IDIOMS, Prev: TYPE, Up: PROGRAMMING ZERO-TEST-IDIOMS how to test for 0 Below are six commonly used idioms for testing whether x is 0. Zip and zp are the preferred termination tests for recursions down the integers and naturals, respectively. idiom logical guard primary meaning compiled code* (equal x 0) (equal x 0) t (equal x 0) (eql x 0) (equal x 0) t (eql x 0) (zerop x) (equal x 0) x is a number (= x 0) (= x 0) (equal x 0) x is a number (= x 0) (zip x) (equal (ifix x) 0) x is an integer (= x 0) (zp x) (equal (nfix x) 0) x is a natural (int= x 0) (zpf x) (equal (nfix x) 0) x is a fixnum >= 0 (eql (the-fixnum x) 0) *See *note GUARDS-AND-EVALUATION::, especially the subsection titled "Guards and evaluation V: efficiency issues". Primary code is relevant only if guards are verified. The "compiled code" shown is only suggestive. The first four idioms all have the same logical meaning and differ only with respect to their executability and efficiency. In the absence of compiler optimizing, (= x 0) is probably the most efficient, (equal x 0) is probably the least efficient, and (eql x 0) is in between. However, an optimizing compiler could always choose to compile (equal x 0) as (eql x 0) and, in situations where x is known at compile-time to be numeric, (eql x 0) as (= x 0). So efficiency considerations must, of course, be made in the context of the host compiler. Note also that (zerop x) and (= x 0) are indistinguishable. They have the same meaning and the same guard, and can reasonably be expected to generate equally efficient code. Note that (zip x) and (zp x) do not have the same logical meanings as the others or each other. They are not simple tests for equality to 0. They each coerce x into a restricted domain, zip to the integers and zp to the natural numbers, choosing 0 for x when x is outside the domain. Thus, 1/2, #c(1 3), and 'abc, for example, are all "recognized" as zero by both zip and zp. But zip reports that -1 is different from 0 while zp reports that -1 "is" 0. More precisely, (zip -1) is nil while (zp -1) is t. Note that the last five idioms all have guards that restrict their Common Lisp executability. If these last five are used in situations in which guards are to be verified, then proof obligations are incurred as the price of using them. If guard verification is not involved in your project, then the first five can be thought of as synonymous. Zip and zp are not provided by Common Lisp but are ACL2-specific functions. Why does ACL2 provide these functions? The answer has to do with the admission of recursively defined functions and efficiency. Zp is provided as the zero-test in situations where the controlling formal parameter is understood to be a natural number. Zip is analogously provided for the integer case. We illustrate below. Here is an admissible definition of factorial (defun fact (n) (if (zp n) 1 (* n (fact (1- n))))) Observe the classic recursion scheme: a test against 0 and recursion by 1-. Note however that the test against 0 is expressed with the zp idiom. Note also the absence of a guard making explicit our intention that n is a natural number. This definition of factorial is readily admitted because when (zp n) is false (i.e., nil) then n is a natural number other than 0 and so (1- n) is less than n. The base case, where (zp n) is true, handles all the "unexpected" inputs, such as arise with (fact -1) and (fact 'abc). When calls of fact are evaluated, (zp n) checks (integerp n) and (> n 0). Guard verification is unsuccessful for this definition of fact because zp requires its argument to be a natural number and there is no guard on fact, above. Thus the primary raw lisp for fact is inaccessible and only the :logic definition (which does runtime "type" checking) is used in computation. In summary, this definition of factorial is easily admitted and easily manipulated by the prover but is not executed as efficiently as it could be. Runtime efficiency can be improved by adding a guard to the definition. (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (zp n) 1 (* n (fact (1- n))))) This guarded definition has the same termination conditions as before - termination is not sensitive to the guard. But the guards can be verified. This makes the primary raw lisp definition accessible during execution. In that definition, the (zp n) above is compiled as (= n 0), because n will always be a natural number when the primary code is executed. Thus, by adding a guard and verifying it, the elegant and easily used definition of factorial is also efficiently executed on natural numbers. Now let us consider an alternative definition of factorial in which (= n 0) is used in place of (zp n). (defun fact (n) (if (= n 0) 1 (* n (fact (1- n))))) This definition does not terminate. For example (fact -1) gives rise to a call of (fact -2), etc. Hence, this alternative is inadmissible. A plausible response is the addition of a guard restricting n to the naturals: (defun fact (n) (declare (xargs :guard (and (integerp n) (>= n 0)))) (if (= n 0) 1 (* n (fact (1- n))))) But because the termination argument is not sensitive to the guard, it is still impossible to admit this definition. To influence the termination argument one must change the conditions tested. Adding a runtime test that n is a natural number would suffice and allow both admission and guard verification. But such a test would slow down the execution of the compiled function. The use of (zp n) as the test avoids this dilemma. Zp provides the logical equivalent of a runtime test that n is a natural number but the execution efficiency of a direct = comparison with 0, at the expense of a guard conjecture to prove. In addition, if guard verification and most-efficient execution are not needed, then the use of (zp n) allows the admission of the function without a guard or other extraneous verbiage. While general rules are made to be broken, it is probably a good idea to get into the habit of using (zp n) as your terminating "0 test" idiom when recursing down the natural numbers. It provides the logical power of testing that n is a non-0 natural number and allows efficient execution. We now turn to the analogous function, zip. Zip is the preferred 0-test idiom when recursing through the integers toward 0. Zip considers any non-integer to be 0 and otherwise just recognizes 0. A typical use of zip is in the definition of integer-length, shown below. (ACL2 can actually accept this definition, but only after appropriate lemmas have been proved.) (defun integer-length (i) (declare (xargs :guard (integerp i))) (if (zip i) 0 (if (= i -1) 0 (+ 1 (integer-length (floor i 2)))))) Observe that the function recurses by (floor i 2). Hence, calling the function on 25 causes calls on 12, 6, 3, 1, and 0, while calling it on -25 generates calls on -13, -7, -4, -2, and -1. By making (zip i) the first test, we terminate the recursion immediately on non-integers. The guard, if present, can be verified and allows the primary raw lisp definition to check (= i 0) as the first terminating condition (because the primary code is executed only on integers).  File: acl2-doc-emacs.info, Node: PROOF-CHECKER, Next: PROOF-TREE, Prev: PROGRAMMING, Up: Top PROOF-CHECKER support for low-level interaction Call this up with (verify ...). * Menu: * DEFINE-PC-HELP:: define a macro command whose purpose is to print something * DEFINE-PC-MACRO:: define a proof-checker macro command * DEFINE-PC-META:: define a proof-checker meta command * INSTRUCTIONS:: instructions to the proof checker * MACRO-COMMAND:: compound command for the proof-checker * PROOF-CHECKER-COMMANDS:: list of commands for the proof-checker * RETRIEVE:: re-enter a (specified) proof-checker state * TOGGLE-PC-MACRO:: change an ordinary macro command to an atomic macro, or vice-versa * UNSAVE:: remove a proof-checker state * VERIFY:: enter the interactive proof checker This is an interactive system for checking ACL2 theorems, or at least exploring their proofs. One enters it using the VERIFY command (see *note VERIFY::), and then invokes commands at the resulting prompt to operate on a stack of goals, starting with the single goal that was supplied to VERIFY. The final command (or "instruction") can be an exit command, which can print out a defthm event if the goal stack is empty; see *note PROOF-CHECKER-COMMANDS::, in particular the exit command. That resulting defthm event includes an :instructions parameter, which directs replay of the proof-checker commands (for example during certification of a book containing that event; see *note BOOKS::). If you exit the proof-checker interactive loop, you may re-enter that session at the same point using the command (verify), i.e., with no arguments. The commands save and retrieve may be invoked to manage more than one session. The proof-checker can be invoked on a specific subgoal, and the resulting :instructions can be given as a hint to the theorem prover for that subgoal. See *note INSTRUCTIONS::. A tutorial is available on the world-wide web: `http://www.cs.utexas.edu/users/kaufmann/tutorial/rev3.html'. The tutorial illustrates more than just the proof-checker. The portion relevant to the proof-checker may be accessed directly: `http://www.cs.utexas.edu/users/kaufmann/tutorial/rev3.html#slide29' See *note SET-EVISC-TUPLE:: for how to arrange that output is printed in abbreviated form. In general, the proof-checker uses the :TERM evisc-tuple described in that documentation. Individual proof-checker commands are documented in subsection proof-checker-commands. When inside the interactive loop (i.e., after executing verify), you may use the help command to get a list of legal instructions and (help instr) to get help for the instruction instr.  File: acl2-doc-emacs.info, Node: DEFINE-PC-HELP, Next: DEFINE-PC-MACRO, Prev: PROOF-CHECKER, Up: PROOF-CHECKER DEFINE-PC-HELP define a macro command whose purpose is to print something Example: (define-pc-help pp () (if (goals t) (io? proof-checker nil state (state-stack) (fms0 "~|~y0~|" (list (cons #0 (fetch-term (conc t) (current-addr t)))))) (print-all-goals-proved-message state))) General Form: (define-pc-help name args &rest body) This defines a macro command named name, as explained further below. The body should (after removing optional declarations) be a form that returns state as its single value. Typically, it will just print something. What (define-pc-help name args &rest body) really does is to create a call of define-pc-macro that defines name to take arguments args, to have the declarations indicated by all but the last form in body, and to have a body that (via pprogn) first executes the form in the last element of body and then returns a call to the command skip (which will return (mv nil t state)).  File: acl2-doc-emacs.info, Node: DEFINE-PC-MACRO, Next: DEFINE-PC-META, Prev: DEFINE-PC-HELP, Up: PROOF-CHECKER DEFINE-PC-MACRO define a proof-checker macro command Example: (define-pc-macro ib (&optional term) (value (if term `(then (induct ,term) bash) `(then induct bash)))) The example above captures a common paradigm: one attempts to prove the current goal by inducting and then simplifying the resulting goals. (see *note PROOF-CHECKER-COMMANDS:: for documentation of the command then, which is itself a pc-macro command, and commands induct and bash.) Rather than issuing (then induct bash), or worse yet issuing induct and then issuing bash for each resulting goals, the above definition of ib would let you issue ib and get the same effect. General Form: (define-pc-macro cmd args doc-string dcl ... dcl body) where cmd is the name of the pc-macro than you want to define, args is its list of formal parameters. Args may include lambda-list keywords &optional and &rest; see *note MACRO-ARGS::, but note that here, args may not include &key or &whole. The value of body should be an error triple (see *note ERROR-TRIPLES::), of the form (mv erp xxx state) for some erp and xxx. If erp is nil, then xxx is handed off to the proof-checker's instruction interpreter. Otherwise, evaluation typically halts. We may write more on the full story later if there is interest in reading it.  File: acl2-doc-emacs.info, Node: DEFINE-PC-META, Next: INSTRUCTIONS, Prev: DEFINE-PC-MACRO, Up: PROOF-CHECKER DEFINE-PC-META define a proof-checker meta command Built-in proof-checker meta commands include undo and restore, and others (lisp, exit, and sequence); see *note PROOF-CHECKER-COMMANDS::. The advanced proof-checker user can define these as well. See ACL2 source file proof-checker-b.lisp for examples, and contact the ACL2 implementors if those examples do not provide sufficient documentation.  File: acl2-doc-emacs.info, Node: INSTRUCTIONS, Next: MACRO-COMMAND, Prev: DEFINE-PC-META, Up: PROOF-CHECKER INSTRUCTIONS instructions to the proof checker See *note PROOF-CHECKER:: for an introduction to the interactive "proof-checker" goal manager, which supports much more direct control of the proof process than is available by direct calls to the prover (as are normally made using defthm or thm). In brief, typical use is to evaluate the form (verify SOME-GOAL), where SOME-GOAL is a formula (i.e., term) that you would like to prove. Various commands (instructions) are available at the resulting prompt; see *note PROOF-CHECKER-COMMANDS::. When the proof is completed, suitable invocation of the exit command will print out a form containing an :instructions field that provides the instructions that you gave interactively, so that this form can be evaluated non-interactively. Thus, also see *note DEFTHM:: for the role of :instructions in place of :hints. As illustrated by the following example, the value associated with :instructions is a list of proof-checker commands. Example: (defthm associativity-of-append (equal (append (append x y) z) (append x (append y z))) :instructions (:induct (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2) :top (:dv 2) :x :top :s :bash)) When you are inside the interactive loop (i.e., after executing verify), you may invoke help to get a list of legal instructions and (help instr) to get help for the instruction instr. Below, we describe a capability for supplying :instructions as :hints. The most basic utilities for directing the discharge of a proof obligation are :hints and (less commonly) :instructions. Individual instructions may call the prover with :hints; in that sense, prover hints may occur inside instructions. We now describe how, on the other hand, instructions may occur inside hints. ACL2 supports :instructions as a hints keyword. The following example forms the basis for our running example. This example does not actually need hints, but imagine that the inductive step -- which is "Subgoal *1/2" -- was difficult. You could submit that goal to verify, do an interactive proof, submit (exit t) to obtain the list of :instructions, and then paste in those instructions. When you submit the resulting event, you might see the following. Below we'll explain the hint processing. ACL2 !>(thm (equal (append (append x y) z) (append x (append y z))) :hints (("Subgoal *1/2" :instructions (:promote (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2) :top (:dv 2) :x :top :s)))) Name the formula above *1. Perhaps we can prove *1 by induction. Three induction schemes are suggested by this conjecture. Subsumption reduces that number to two. However, one of these is flawed and so we are left with one viable candidate. We will induct according to a scheme suggested by (BINARY-APPEND X Y). This suggestion was produced using the :induction rule BINARY-APPEND. If we let (:P X Y Z) denote *1 above then the induction scheme we'll use is (AND (IMPLIES (AND (NOT (ENDP X)) (:P (CDR X) Y Z)) (:P X Y Z)) (IMPLIES (ENDP X) (:P X Y Z))). This induction is justified by the same argument used to admit BINARY-APPEND. When applied to the goal at hand the above induction scheme produces two nontautological subgoals. [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC replaces this goal by T. Subgoal *1/1 (IMPLIES (ENDP X) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). By the simple :definition ENDP we reduce the conjecture to Subgoal *1/1' (IMPLIES (NOT (CONSP X)) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But simplification reduces this to T, using the :definition BINARY-APPEND and primitive type reasoning. That completes the proof of *1. Q.E.D. Summary Form: ( THM ...) Rules: ((:DEFINITION BINARY-APPEND) (:DEFINITION ENDP) (:DEFINITION NOT) (:FAKE-RUNE-FOR-TYPE-SET NIL) (:INDUCTION BINARY-APPEND)) Time: 0.02 seconds (prove: 0.01, print: 0.01, other: 0.00) Proof succeeded. ACL2 !> To understand how the :instructions supplied above were processed, observe proof-checker instruction interpreter may be viewed as a "clause-processor": a function that takes an input goal and returns a list of goals (which can be the empty list). Such a function has the property that if all goals in that returned list are theorems, then so is the input goal. This view of the proof-checker instruction interpreter as a clause-processor leads to the following crucial observation. *IMPORTANT!*. Each call of the proof-checker instruction interpreter is treated as a standalone clause-processor that is insensitive to the surrounding prover environment. In particular: o The proof-checker's theory is not sensitive to :in-theory hints already processed in the surrounding proof. Indeed, the current theory for which proof-checker commands are processed is just the current theory of the ACL2 logical world, i.e., the value of (current-theory :here). Moreover, references to (current-theory :here) in a proof-checker in-theory command, even implicit references such as provided by enable and disable expressions, are also references to the current theory of the ACL2 logical world. o The runes used during an :instructions hint are not tracked beyond that hint, hence may not show up in the summary of the overall proof. Again, think of the :instructions hint as a clause-processor call, which has some effect not tracked by the surrounding proof other than for the child goals that it returns. We continue now with our discussion of the proof-checker instruction interpreter as a clause-processor. In the example above, the input goal ("Subgoal *1/2") was processed by the proof-checker instruction interpreter. The result was the empty goal stack, therefore proving the goal, as reported in the output, which we repeat here. [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). But the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC replaces this goal by T. *Remark.* This brief remark can probably be ignored, but we include it for completeness. The :CLAUSE-PROCESSOR message above may be surprising, since the hint attached to "Subgoal *1/2" is an :instructions hint, not a :clause-processor hint. But :instructions is actually a custom keyword hint (see *note CUSTOM-KEYWORD-HINTS::), and may be thought of as a macro that expands to a :clause-processor hint, one that specifies proof-checker-cl-proc as the clause-processor function. The keen observer may notice that the clause-processor is referred to as "trusted" in the above output. Normally one needs a trust tag (see *note DEFTTAG::) to install a trusted clause-processor, but that is not the case for the built-in clause-processor, proof-checker-cl-proc. Finally, we note that :instructions hints are "spliced" into the hints as follows: the appropriate :clause-processor hint replaces the :instructions hint, and the other hints remain intact. It may seems surprising that one can thus, for example, use :instructions and :in-theory together; but although the :in-theory hint will have no effect on execution of the :instructions (see first bullet above), the :in-theory hint will apply in the usual manner to any child goals (see *note HINTS-AND-THE-WATERFALL::). End of Remark. Now consider the case that the supplied instructions do not prove the goal. That is, suppose that the execution of those instructions results in a non-empty goal stack. In that case, the resulting goals become children of the input goals. The following edited log provides an illustration using a modification of the above example, this time with a single instruction that splits into two cases. ACL2 !>(thm (equal (append (append x y) z) (append x (append y z))) :hints (("Subgoal *1/2" :instructions ((:casesplit (equal x y)))))) [[ ... output omitted ... ]] Subgoal *1/2 (IMPLIES (AND (NOT (ENDP X)) (EQUAL (APPEND (APPEND (CDR X) Y) Z) (APPEND (CDR X) Y Z))) (EQUAL (APPEND (APPEND X Y) Z) (APPEND X Y Z))). We now apply the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC to produce two new subgoals. Subgoal *1/2.2 [[ ... output omitted ... ]] Subgoal *1/2.1 [[ ... output omitted ... ]] We have seen that an :instructions hint may produce zero or more subgoals. There may be times where you wish to insist that it produce zero subgoals, i.e., that it prove the desired goal. The proof-checker `finish' command works nicely for this purpose. For example, the following form is successfully admitted, but if you delete some of the commands (for example, the :s command at the end), you will see an informative error message. (thm (equal (append (append x y) z) (append x (append y z))) :hints (("Subgoal *1/2" :instructions ((finish :promote (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2) :top (:dv 2) :x :top :s))))) If an :instructions hint of the form ((finish ...)) fails to prove the goal, the clause-processor is deemed to have caused an error. Indeed, any "failure" of a supplied proof-checker instruction will be deemed to cause an error. In this case, you should see an error message such as the following: Saving proof-checker error state; see :DOC instructions. To retrieve: (RETRIEVE :ERROR1) In this case, you can evaluate the indicated retrieve command in the ACL2 read-eval-print loop, to get to the point of failure. You may have noticed that there is no output from the proof-checker in the examples above. This default behavior prevents confusion that could arise from use of proof-checker commands that call the theorem prover such as prove, bash, split, and induct. These commands produce output for what amounts to a fresh proof attempt, which could confuse attempts to understand the surrounding proof log. You can override the default behavior by providing a command of the form (comment inhibit-output-lst VAL) where VAL is either the keyword :SAME (indicating that no change should be made to which output is inhibited) or else is a legal value for inhibited output; see *note SET-INHIBIT-OUTPUT-LST::. The following two variants of the immediately preceding THM form will each produce output from the proof-checker commands, assuming in the first variant that output hasn't already been inhibited. (thm (equal (append (append x y) z) (append x (append y z))) :hints (("Subgoal *1/2" :instructions ((comment inhibit-output-lst :same) (:casesplit (equal x y)))))) (thm (equal (append (append x y) z) (append x (append y z))) :hints (("Subgoal *1/2" :instructions ((comment inhibit-output-lst (proof-tree)) (:casesplit (equal x y)))))) Note that such a comment instruction must be provided explicitly (i.e., not by way of a proof-checker macro-command) as the first instruction, in order to have the effect on inhibited output that is described above. The following contrived example gives a sense of how one might want to use :instructions within :hints. If you submit the following theorem (thm (implies (true-listp x) (equal (reverse (reverse x)) x))) then you will see the following checkpoint printed with the summary. Subgoal *1/3'' (IMPLIES (AND (CONSP X) (EQUAL (REVAPPEND (REVAPPEND (CDR X) NIL) NIL) (CDR X)) (TRUE-LISTP (CDR X))) (EQUAL (REVAPPEND (REVAPPEND (CDR X) (LIST (CAR X))) NIL) X)) This suggests proving the following theorem. Here we state it using defthmd, so that it is immediately disabled. Normally disabling would be unnecessary, but for our contrived example it is useful to imagine disabling it, say because we are following a methodology that tends to keep rewrite rules disabled. (defthmd revappend-revappend (equal (revappend (revappend x y) z) (revappend y (append x z)))) We might then enter the proof-checker to prove the original theorem interactively, as follows. ACL2 !>(verify (implies (true-listp x) (equal (reverse (reverse x)) x))) ->: bash ***** Now entering the theorem prover ***** Goal' ([ A key checkpoint: Goal' (IMPLIES (TRUE-LISTP X) (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL) X)) Goal' is subsumed by a goal yet to be proved. ]) Q.E.D. Creating one new goal: (MAIN . 1). The proof of the current goal, MAIN, has been completed. However, the following subgoals remain to be proved: (MAIN . 1). Now proving (MAIN . 1). ->: th ; show current goal ("th" for "theorem") *** Top-level hypotheses: 1. (TRUE-LISTP X) The current subterm is: (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL) X) ->: p ; show current subterm only (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL) X) ->: 1 ; dive to first argument ->: p (REVAPPEND (REVAPPEND X NIL) NIL) ->: sr ; show-rewrites 1. REVAPPEND-REVAPPEND (disabled) New term: (REVAPPEND NIL (APPEND X NIL)) Hypotheses: Equiv: EQUAL 2. REVAPPEND New term: (AND (CONSP (REVAPPEND X NIL)) (REVAPPEND (CDR (REVAPPEND X NIL)) (LIST (CAR (REVAPPEND X NIL))))) Hypotheses: Equiv: EQUAL ->: (r 1) ; rewrite with rule #1 above Rewriting with REVAPPEND-REVAPPEND. ->: p (REVAPPEND NIL (APPEND X NIL)) ->: top ; move to the top of the conclusion, making it the current subterm ->: p (EQUAL (REVAPPEND NIL (APPEND X NIL)) X) ->: prove ; finish the proof ***** Now entering the theorem prover ***** Q.E.D. *!*!*!*!*!*!* All goals have been proved! *!*!*!*!*!*!* You may wish to exit. ->: (exit t) ; the argument, t, causes :instructions to be printed (DEFTHM T (IMPLIES (TRUE-LISTP X) (EQUAL (REVERSE (REVERSE X)) X)) :INSTRUCTIONS (:BASH (:DV 1) (:REWRITE REVAPPEND-REVAPPEND) :TOP :PROVE)) NIL ACL2 !>(thm (IMPLIES (TRUE-LISTP X) (EQUAL (REVERSE (REVERSE X)) X)) :hints (("Goal" :INSTRUCTIONS ; copy what was printed above: (:BASH (:DV 1) (:REWRITE REVAPPEND-REVAPPEND) :TOP :PROVE)))) Goal' Q.E.D. Q.E.D. Q.E.D. Summary Form: ( THM ...) Rules: NIL Hint-events: ((:CLAUSE-PROCESSOR PROOF-CHECKER-CL-PROC)) Time: 0.00 seconds (prove: 0.00, print: 0.00, other: 0.00) Proof succeeded. ACL2 !> Finally we present an even more contrived example, based on the one above. This example illustrates that there is actually no limit imposed on the nesting of :instructions within :hints within :instructions, and so on. Notice the indication of nesting levels: "1>" to "<1" for output from nesting level 1, and "2>" to "<2" for output from nesting level 2. (thm (implies (true-listp x) (equal (reverse (reverse x)) x)) :hints (("Goal" :instructions ((comment inhibit-output-lst :same) (:prove :hints (("Goal" :in-theory (disable append)) ("Subgoal *1/3''" :instructions ((comment inhibit-output-lst :same) :bash (:dv 1) (:rewrite revappend-revappend))))))))) Here is an edited version of the resulting log. [Note: A hint was supplied for our processing of the goal above. Thanks!] [[1> Executing proof-checker instructions]] ->: (COMMENT INHIBIT-OUTPUT-LST :SAME) ->: (:PROVE :HINTS (("Goal" :IN-THEORY (DISABLE APPEND)) ("Subgoal *1/3''" :INSTRUCTIONS ((COMMENT INHIBIT-OUTPUT-LST :SAME) :BASH (:DV 1) (:REWRITE REVAPPEND-REVAPPEND))))) ***** Now entering the theorem prover ***** [[ ... output omitted ... ]] [Note: A hint was supplied for our processing of the goal below. Thanks!] Subgoal *1/3'' (IMPLIES (AND (CONSP X) (EQUAL (REVAPPEND (REVAPPEND (CDR X) NIL) NIL) (CDR X)) (TRUE-LISTP (CDR X))) (EQUAL (REVAPPEND (REVAPPEND (CDR X) (LIST (CAR X))) NIL) X)). [[2> Executing proof-checker instructions]] ->: (COMMENT INHIBIT-OUTPUT-LST :SAME) ->: :BASH ***** Now entering the theorem prover ***** [Note: A hint was supplied for our processing of the goal above. Thanks!] But we have been asked to pretend that this goal is subsumed by the yet-to-be-proved |PROOF-CHECKER Goal|. Q.E.D. Creating one new goal: (MAIN . 1). The proof of the current goal, MAIN, has been completed. However, the following subgoals remain to be proved: (MAIN . 1). Now proving (MAIN . 1). ->: (:DV 1) ->: (:REWRITE REVAPPEND-REVAPPEND) Rewriting with REVAPPEND-REVAPPEND. [[<2 Completed proof-checker instructions]] We now apply the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC to produce one new subgoal. Subgoal *1/3''' [[ ... output omitted ... ]] [[<1 Completed proof-checker instructions]] The nesting levels are independent of whether or not output is enabled; for example, if the first (comment ...) form below is omitted, then we will see only the output bracketed by "2>" to "<2". Note also that these levels are part of the error states saved for access by retrieve (as indicated above); for example, a failure at level 1 would be associated with symbol :ERROR1 as indicated above, while a failure at level 2 would be associated with symbol :ERROR2.  File: acl2-doc-emacs.info, Node: MACRO-COMMAND, Next: PROOF-CHECKER-COMMANDS, Prev: INSTRUCTIONS, Up: PROOF-CHECKER MACRO-COMMAND compound command for the proof-checker The proof-checker (see *note PROOF-CHECKER::) allows the user to supply interactive commands. Compound commands, called macro commands, may be defined; these expand into zero or more other commands. Some of these are "atomic" macro commands; these are viewed as a single command step when completed successfully. More documentation will be written on the proof-checker. For now, we simply point out that there are lots of examples of the use of define-pc-macro and define-pc-atomic-macro in the ACL2 source file "proof-checker-b.lisp". The former is used to create macro commands, which can be submitted to the interactive loop (see *note VERIFY::) and will "expand" into zero or more commands. The latter is similar, except that the undoing mechanism (see *note ACL2-PC||UNDO::) understands atomic macro commands to represent single interactive commands. Also see *note ACL2-PC||COMM:: and see *note ACL2-PC||COMMANDS:: for a discussion of the display of interactive commands. Also see *note TOGGLE-PC-MACRO:: for how to change a macro command to an atomic macro command, and vice versa.  File: acl2-doc-emacs.info, Node: PROOF-CHECKER-COMMANDS, Next: RETRIEVE, Prev: MACRO-COMMAND, Up: PROOF-CHECKER PROOF-CHECKER-COMMANDS list of commands for the proof-checker This documentation section contains documentation for individual commands that can be given inside the interactive proof-checker loop that is entered using verify. * Menu: * ACL2-PC||=:: (atomic macro) attempt an equality (or equivalence) substitution * ACL2-PC||ACL2-WRAP:: (macro) same as (lisp x) * ACL2-PC||ADD-ABBREVIATION:: (primitive) add an abbreviation * ACL2-PC||AL:: (macro) same as apply-linear * ACL2-PC||APPLY-LINEAR:: (primitive) apply a linear rule * ACL2-PC||BASH:: (atomic macro) call the ACL2 theorem prover's simplifier * ACL2-PC||BDD:: (atomic macro) prove the current goal using bdds * ACL2-PC||BK:: (atomic macro) move backward one argument in the enclosing term * ACL2-PC||BOOKMARK:: (macro) insert matching ``bookends'' comments * ACL2-PC||CASESPLIT:: (primitive) split into two cases * ACL2-PC||CG:: (macro) change to another goal. * ACL2-PC||CHANGE-GOAL:: (primitive) change to another goal. * ACL2-PC||CL-PROC:: (macro) same as clause-processor * ACL2-PC||CLAIM:: (atomic macro) add a new hypothesis * ACL2-PC||CLAUSE-PROCESSOR:: (atomic macro) use a clause-processor * ACL2-PC||COMM:: (macro) display instructions from the current interactive session * ACL2-PC||COMMANDS:: (macro) display instructions from the current interactive session * ACL2-PC||COMMENT:: (primitive) insert a comment * ACL2-PC||CONTRADICT:: (macro) same as contrapose * ACL2-PC||CONTRAPOSE:: (primitive) switch a hypothesis with the conclusion, negating both * ACL2-PC||DEMOTE:: (primitive) move top-level hypotheses to the conclusion * ACL2-PC||DIVE:: (primitive) move to the indicated subterm * ACL2-PC||DO-ALL:: (macro) run the given instructions * ACL2-PC||DO-ALL-NO-PROMPT:: (macro) run the given instructions, halting once there is a ``failure'' * ACL2-PC||DO-STRICT:: (macro) run the given instructions, halting once there is a ``failure'' * ACL2-PC||DROP:: (primitive) drop top-level hypotheses * ACL2-PC||DV:: (atomic macro) move to the indicated subterm * ACL2-PC||ELIM:: (atomic macro) call the ACL2 theorem prover's elimination process * ACL2-PC||EQUIV:: (primitive) attempt an equality (or congruence-based) substitution * ACL2-PC||EX:: (macro) exit after possibly saving the state * ACL2-PC||EXIT:: (meta) exit the interactive proof-checker * ACL2-PC||EXPAND:: (primitive) expand the current function call without simplification * ACL2-PC||FAIL:: (macro) cause a failure * ACL2-PC||FINISH:: (macro) require completion of instructions; save error if inside :hints * ACL2-PC||FORWARDCHAIN:: (atomic macro) forward chain from an implication in the hyps * ACL2-PC||FREE:: (atomic macro) create a ``free variable'' * ACL2-PC||GENEQV:: (macro) show the generated equivalence relation maintained at the current subterm * ACL2-PC||GENERALIZE:: (primitive) perform a generalization * ACL2-PC||GOALS:: (macro) list the names of goals on the stack * ACL2-PC||HELP:: (macro) proof-checker help facility * ACL2-PC||HELP!:: (macro) proof-checker help facility * ACL2-PC||HELP-LONG:: (macro) same as help! * ACL2-PC||HYPS:: (macro) print the hypotheses * ACL2-PC||ILLEGAL:: (macro) illegal instruction * ACL2-PC||IN-THEORY:: (primitive) set the current proof-checker theory * ACL2-PC||INDUCT:: (atomic macro) generate subgoals using induction * ACL2-PC||LEMMAS-USED:: (macro) print the runes (definitions, lemmas, ...) used * ACL2-PC||LISP:: (meta) evaluate the given form in Lisp * ACL2-PC||MORE:: (macro) proof-checker help facility * ACL2-PC||MORE!:: (macro) proof-checker help facility * ACL2-PC||NEGATE:: (macro) run the given instructions, and ``succeed'' if and only if they ``fail'' * ACL2-PC||NIL:: (macro) used for interpreting control-d * ACL2-PC||NOISE:: (meta) run instructions with output * ACL2-PC||NX:: (atomic macro) move forward one argument in the enclosing term * ACL2-PC||ORELSE:: (macro) run the first instruction; if (and only if) it ``fails'', run the second * ACL2-PC||P:: (macro) prettyprint the current term * ACL2-PC||P-TOP:: (macro) prettyprint the conclusion, highlighting the current term * ACL2-PC||PL:: (macro) print the rules for a given name * ACL2-PC||PP:: (macro) prettyprint the current term * ACL2-PC||PR:: (macro) print the rules for a given name * ACL2-PC||PRINT:: (macro) print the result of evaluating the given form * ACL2-PC||PRINT-ALL-CONCS:: (macro) print all the conclusions of (as yet unproved) goals * ACL2-PC||PRINT-ALL-GOALS:: (macro) print all the (as yet unproved) goals * ACL2-PC||PRINT-MAIN:: (macro) print the original goal * ACL2-PC||PRO:: (atomic macro) repeatedly apply promote * ACL2-PC||PROMOTE:: (primitive) move antecedents of conclusion's implies term to top-level hypotheses * ACL2-PC||PROTECT:: (macro) run the given instructions, reverting to existing state upon failure * ACL2-PC||PROVE:: (primitive) call the ACL2 theorem prover to prove the current goal * ACL2-PC||PSO:: (macro) print the most recent proof attempt from inside the proof-checker * ACL2-PC||PSO!:: (macro) print the most recent proof attempt from inside the proof-checker * ACL2-PC||PSOG:: (macro) print the most recent proof attempt from inside the proof-checker * ACL2-PC||PUT:: (macro) substitute for a ``free variable'' * ACL2-PC||QUIET:: (meta) run instructions without output * ACL2-PC||R:: (macro) same as rewrite * ACL2-PC||REDUCE:: (atomic macro) call the ACL2 theorem prover's simplifier * ACL2-PC||REDUCE-BY-INDUCTION:: (macro) call the ACL2 prover without induction, after going into induction * ACL2-PC||REMOVE-ABBREVIATIONS:: (primitive) remove one or more abbreviations * ACL2-PC||REPEAT:: (macro) repeat the given instruction until it ``fails'' * ACL2-PC||REPEAT-REC:: (macro) auxiliary to repeat * ACL2-PC||REPLAY:: (macro) replay one or more instructions * ACL2-PC||RESTORE:: (meta) remove the effect of an UNDO command * ACL2-PC||RETAIN:: (atomic macro) drop all *but* the indicated top-level hypotheses * ACL2-PC||RETRIEVE:: (macro) re-enter the proof-checker * ACL2-PC||REWRITE:: (primitive) apply a rewrite rule * ACL2-PC||RUN-INSTR-ON-GOAL:: (macro) auxiliary to THEN * ACL2-PC||RUN-INSTR-ON-NEW-GOALS:: (macro) auxiliary to then * ACL2-PC||RUNES:: (macro) print the runes (definitions, lemmas, ...) used * ACL2-PC||S:: (primitive) simplify the current subterm * ACL2-PC||S-PROP:: (atomic macro) simplify propositionally * ACL2-PC||SAVE:: (macro) save the proof-checker state (state-stack) * ACL2-PC||SEQUENCE:: (meta) run the given list of instructions according to a multitude of options * ACL2-PC||SHOW-ABBREVIATIONS:: (macro) display the current abbreviations * ACL2-PC||SHOW-LINEARS:: (macro) display the applicable linear rules * ACL2-PC||SHOW-REWRITES:: (macro) display the applicable rewrite rules * ACL2-PC||SHOW-TYPE-PRESCRIPTIONS:: (macro) display the applicable type-prescription rules * ACL2-PC||SKIP:: (macro) ``succeed'' without doing anything * ACL2-PC||SL:: (atomic macro) simplify with lemmas * ACL2-PC||SLS:: (macro) same as SHOW-LINEARS * ACL2-PC||SPLIT:: (atomic macro) split the current goal into cases * ACL2-PC||SR:: (macro) same as SHOW-REWRITES * ACL2-PC||ST:: (macro) same as SHOW-TYPE-PRESCRIPTIONS * ACL2-PC||SUCCEED:: (macro) run the given instructions, and ``succeed'' * ACL2-PC||TH:: (macro) print the top-level hypotheses and the current subterm * ACL2-PC||THEN:: (macro) apply one instruction to current goal and another to new subgoals * ACL2-PC||TOP:: (atomic macro) move to the top of the goal * ACL2-PC||TYPE-ALIST:: (macro) display the type-alist from the current context * ACL2-PC||UNDO:: (meta) undo some instructions * ACL2-PC||UNSAVE:: (macro) remove a proof-checker state * ACL2-PC||UP:: (primitive) move to the parent (or some ancestor) of the current subterm * ACL2-PC||USE:: (atomic macro) use a lemma instance * ACL2-PC||WRAP:: (atomic macro) execute the indicated instructions and combine all the new goals * ACL2-PC||WRAP-INDUCT:: (atomic macro) same as induct, but create a single goal * ACL2-PC||WRAP1:: (primitive) combine goals into a single goal * ACL2-PC||X:: (atomic macro) expand and (maybe) simplify function call at the current subterm * ACL2-PC||X-DUMB:: (atomic macro) expand function call at the current subterm, without simplifying  File: acl2-doc-emacs.info, Node: ACL2-PC||=, Next: ACL2-PC||ACL2-WRAP, Prev: PROOF-CHECKER-COMMANDS, Up: PROOF-CHECKER-COMMANDS ACL2-PC||= (atomic macro) attempt an equality (or equivalence) substitution Examples: = -- replace the current subterm by a term equated to it in one of the hypotheses (if such a term exists) (= x) -- replace the current subterm by x, assuming that the prover can show that they are equal (= (+ x y) z) -- replace the term (+ x y) by the term z inside the current subterm, assuming that the prover can prove (equal (+ x y) z) from the current top-level hypotheses or that this term or (equal z (+ x y)) is among the current top-level hypotheses or the current governors (= & z) -- exactly the same as above, if (+ x y) is the current subterm (= (+ x y) z :hints :none) -- same as (= (+ x y) z), except that a new subgoal is created with the current goal's hypotheses and governors as its top-level hypotheses and (equal (+ x y) z) as its conclusion (= (+ x y) z 0) -- exactly the same as immediately above (= (p x) (p y) :equiv iff :otf-flg t :hints (("Subgoal 2" :BY FOO) ("Subgoal 1" :use bar))) -- same as (= (+ x y) z), except that the prover uses the indicated values for otf-flg and hints, and only propositional (iff) equivalence is used (however, it must be that only propositional equivalence matters at the current subterm) General Form: (= &optional x y &rest keyword-args) If terms x and y are supplied, then replace x by y inside the current subterm if they are "known" to be "equal". Here "known" means the following: the prover is called as in the prove command (using keyword-args) to prove (equal x y), except that a keyword argument :equiv is allowed, in which case (equiv x y) is proved instead, where equiv is that argument. (See below for how governors are handled.) Actually, keyword-args is either a single non-keyword or is a list of the form ((kw-1 x-1) ... (kw-n x-n)), where each kw-i is one of the keywords :equiv, :otf-flg, :hints. Here :equiv defaults to equal if the argument is not supplied or is nil, and otherwise should be the name of an ACL2 equivalence relation. :Otf-flg and :hints give directives to the prover, as explained above and in the documentation for the prove command; however, no prover call is made if :hints is a non-nil atom or if keyword-args is a single non-keyword (more on this below). _Remarks on defaults_ (1) If there is only one argument, say a, then x defaults to the current subterm, in the sense that x is taken to be the current subterm and y is taken to be a. (2) If there are at least two arguments, then x may be the symbol &, which then represents the current subterm. Thus, (= a) is equivalent to (= & a). (Obscure point: actually, & can be in any package, except the keyword package.) (3) If there are no arguments, then we look for a top-level hypothesis or a governor of the form (equal c u) or (equal u c), where c is the current subterm. In that case we replace the current subterm by u. As with the prove command, we allow goals to be given "bye"s in the proof, which may be generated by a :hints keyword argument in keyword-args. These result in the creation of new subgoals. A proof is attempted unless the :hints argument is a non-nil atom other than :none, or unless there is one element of keyword-args and it is not a keyword. In that case, if there are any hypotheses in the current goal, then what is attempted is a proof of the implication whose antecedent is the conjunction of the current hypotheses and governors and whose conclusion is the appropriate equal term. *Remarks:* (1) It is allowed to use abbreviations in the hints. (2) The keyword :none has the special role as a value of :hints that is shown clearly in an example above. (3) If there are governors, then the new subgoal has as additional hypotheses the current governors.  File: acl2-doc-emacs.info, Node: ACL2-PC||ACL2-WRAP, Next: ACL2-PC||ADD-ABBREVIATION, Prev: ACL2-PC||=, Up: PROOF-CHECKER-COMMANDS ACL2-PC||ACL2-WRAP (macro) same as (lisp x) Example: (acl2-wrap (pe :here)) General Form: (acl2-wrap form) Same as (lisp form). This is provided for interface tools that want to be able to execute the same form in raw Lisp, in the proof-checker, or in the ACL2 top-level loop (lp).  File: acl2-doc-emacs.info, Node: ACL2-PC||ADD-ABBREVIATION, Next: ACL2-PC||AL, Prev: ACL2-PC||ACL2-WRAP, Up: PROOF-CHECKER-COMMANDS ACL2-PC||ADD-ABBREVIATION (primitive) add an abbreviation Example: (add-abbreviation v (* x y)) causes future occurrences of (* x y) to be printed as (? v), until (unless) a corresponding invocation of remove-abbreviations occurs. In this case we say that v "abbreviates" (* x y). General Form: (add-abbreviation var &optional raw-term) Let var be an abbreviation for raw-term, if raw-term is supplied, else for the current subterm. Note that var must be a variable that does not already abbreviate some term. A way to think of abbreviations is as follows. Imagine that whenever an abbreviation is added, say v abbreviates expr, an entry associating v to expr is made in an association list, which we will call "*abbreviations-alist*". Then simply imagine that ? is a function defined by something like: (defun ? (v) (let ((pair (assoc v *abbreviations-alist*))) (if pair (cdr pair) (error ...)))) Of course the implementation isn't exactly like that, since the "constant" *abbreviations-alist* actually changes each time an add-abbreviation instruction is successfully invoked. Nevertheless, if one imagines an appropriate redefinition of the "constant" *abbreviations-alist* each time an add-abbreviation is invoked, then one will have a clear model of the meaning of such an instruction. The effect of abbreviations on output is that before printing a term, each subterm that is abbreviated by a variable v is first replaced by (? v). The effect of abbreviations on input is that every built-in proof-checker command accepts abbreviations wherever a term is expected as an argument, i.e., accepts the syntax (? v) whenever v abbreviates a term. For example, the second argument of add-abbreviation may itself use abbreviations that have been defined by previous add-abbreviation instructions. See also remove-abbreviations and show-abbreviations.  File: acl2-doc-emacs.info, Node: ACL2-PC||AL, Next: ACL2-PC||APPLY-LINEAR, Prev: ACL2-PC||ADD-ABBREVIATION, Up: PROOF-CHECKER-COMMANDS ACL2-PC||AL (macro) same as apply-linear Example: (al 3) See the documentation for apply-linear, as al and apply-linear are identical. acl2-sources/doc/HTML/0002775002132200015000000000000012222334002014054 5ustar kaufmannacl2acl2-sources/doc/HTML/LICENSE0000664002132200015000000000340012222333514015063 0ustar kaufmannacl2[Note: The license below is based on the template found Dec. 16, 2012 at: http://opensource.org/licenses/BSD-3-Clause. It applies to all files distributed from http://www.cs.utexas.edu/users/moore/acl2/current/ except as otherwise noted.] Copyright (c) 2012, Regents of the University of Texas All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: o Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. o Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. o Neither the name of the University of Texas, Austin nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. acl2-sources/doc/HTML/+.html0000664002132200015000000000122512222333523015101 0ustar kaufmannacl2 +.html -- ACL2 Version 6.3

    +

    addition macro
    Major Section:  ACL2-BUILT-INS
    

    + is really a macro that expands to calls of the function binary-+. So for example

    (+ x y 4 z)
    
    represents the same term as
    (binary-+ x (binary-+ y (binary-+ 4 z))).
    
    See binary-+.




    acl2-sources/doc/HTML/1+.html0000664002132200015000000000105412222333523015162 0ustar kaufmannacl2 1+.html -- ACL2 Version 6.3

    1+

    increment by 1
    Major Section:  ACL2-BUILT-INS
    

    (1+ x) is the same as (+ 1 x). See +.

    1+ is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/1-.html0000664002132200015000000000106312222333523015164 0ustar kaufmannacl2 1-.html -- ACL2 Version 6.3

    1-

    decrement by 1
    Major Section:  ACL2-BUILT-INS
    

    (1- x) is the same as (- x 1). See -.

    1- is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/=.html0000664002132200015000000000165212222333523015127 0ustar kaufmannacl2 =.html -- ACL2 Version 6.3

    =

    test equality of two numbers
    Major Section:  ACL2-BUILT-INS
    

    (= x y) is logically equivalent to (equal x y).

    Unlike equal, = has a guard requiring both of its arguments to be numbers. Generally, = is executed more efficiently than equal.

    For a discussion of the various ways to test against 0, See zero-test-idioms.

    = is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/ABORT_bang_.html0000664002132200015000000000110112222333520016732 0ustar kaufmannacl2 ABORT_bang_.html -- ACL2 Version 6.3

    ABORT!

    to return to the top-level of ACL2's command loop
    Major Section:  MISCELLANEOUS
    

    This is an alias for a!; see a!. For a related feature that only pops up one level, see p!.




    acl2-sources/doc/HTML/ABOUT-ACL2.html0000664002132200015000000000242212222333515016301 0ustar kaufmannacl2 ABOUT-ACL2.html -- ACL2 Version 6.3

    ABOUT-ACL2

    about ACL2
    Major Section:  ACL2 Documentation
    

    This is ACL2 Version 6.3, copyright (C) 2013, Regents of the University of Texas, authored by Matt Kaufmann and J Strother Moore.

    For past versions, see http://www.cs.utexas.edu/users/moore/acl2/current/other-releases.html.

    For statistics on ACL2 code size, see file doc/acl2-code-size.txt.

    See documentation for how to access the user's manual.

    See the home page at http://www.cs.utexas.edu/users/moore/acl2/ for additional information including tutorials, applications, mailing lists, related publications, libraries, ACL2 workshops and seminars, installation instructions, and acknowledgements.

    See copyright for license and copyright information.




    acl2-sources/doc/HTML/ABS.html0000664002132200015000000000207412222333523015357 0ustar kaufmannacl2 ABS.html -- ACL2 Version 6.3

    ABS

    the absolute value of a real number
    Major Section:  ACL2-BUILT-INS
    

    (Abs x) is -x if x is negative and is x otherwise.

    The guard for abs requires its argument to be a rational (real, in ACL2(r)) number.

    Abs is a Common Lisp function. See any Common Lisp documentation for more information.

    From ``Common Lisp the Language'' page 205, we must not allow complex x as an argument to abs in ACL2, because if we did we would have to return a number that might be a floating point number and hence not an ACL2 object.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ACCUMULATED-PERSISTENCE-SUBTLETIES.html0000664002132200015000000001342212222333522021742 0ustar kaufmannacl2 ACCUMULATED-PERSISTENCE-SUBTLETIES.html -- ACL2 Version 6.3

    ACCUMULATED-PERSISTENCE-SUBTLETIES

    some subtle aspects of the counting done by accumulated-persistence
    Major Section:  ACCUMULATED-PERSISTENCE
    

    In this topic we cover the overcounting of ``useful'' and of recursive rune application attempts, and we describe how ``useless'' rune application attempts can actually be critical for a proof's success.

    Overcounting of ``useful'' and of recursive rune application attempts. Not every rune application may be necessary for a proof's success. Consider for example:

    (thm (equal (car (cons a (cdr (cons b x))))
                a))
    
    Then show-accumulated-persistence will tell us that :rewrite rules car-cons and cdr-cons each had one useful application. However, the rule cdr-cons is used to simplify (cdr (cons b x)) to x, and this simplification is unecessary for the proof. Indeed, the proof succeeds even when preceded by the event: (in-theory (disable cdr-cons)). We thus see that a rune application labeled as ``useful'' may be simplifying a term that is not relevant to the proof.

    As of this writing, we consider every :forward-chaining rule application to be ``useful'', for simplicity of the implementation. Moreover, our counting of these rules is such that a single rule may be counted more than once.

    Next we show how recursive rule applications are overcounted. Consider the following example.

    (defun mem (a x)
      (if (atom x)
          nil
        (or (equal a (car x)) (mem a (cdr x)))))
    
    Now suppose we consider the sequence of theorems (mem a (list a)), (mem a (list 1 a)), (mem a (list 1 2 a)), (mem a (list 1 2 3 a)), and so on. We will see that the :frames reported for each increases quadratically, even though the :tries increases linearly; so in this case the :tries statistics are more appropriate. Each time the definition of mem is applied, a new stack frame is pushed (see accumulated-persistence), and all subsequent applications of that definition are accumulated into the :frames count for that stack frame. The final :frames count will be the sum of the counts for those individual frames, which form a linear sequence whose sum is therefore quadratic in the number of applications of the definition of mem.

    How ``useless'' attempts can be critical for a proof's success. The command (accumulated-persistence :useless)] will list rules that did not contribute directly to the proof (see accumulated-persistence, in particular the discussion of ``useless'' there). However, a ``useless'' rule can on rare occasions be critical to the success of a proof. In the following example, we have a ``bad'' rule that can take the proof in the wrong direction, but a ``useless'' rule does a rewrite that prevents the succesful relieving of a hypothesis of the ``bad'' rule. In summary:

    ; Assume p0.  We want to prove p1.
    
    ; Key rule:
    p0 -> p1 = t
    
    ; Bad rule that could ruin the proof:
    p3 -> p1 = p2
    
    ; But unfortunately, we know p3:
    p0 -> p3
    
    ; Important ``useless'' rule, preventing ``bad rule'' above from firing:
    p3 = p4
    
    The following event captures the rules described above.
    (encapsulate
     ((p0 (x) t)
      (p1 (x) t)
      (p2 (x) t)
      (p3 (x) t)
      (p4 (x) t))
     (local (defun p0 (x) x))
     (local (defun p1 (x) x))
     (local (defun p2 (x) x))
     (local (defun p3 (x) x))
     (local (defun p4 (x) x))
    
    ; Key rule:
     (defthm p0-implies-p1
       (implies (p0 x)
                (p1 x)))
    
    ; Bad rule that could ruin the proof:
     (defthm p3-implies-p1-is-p2
       (implies (p3 x)
                (equal (p1 x) (p2 x))))
    
    ; But unfortunately, we know p3:
     (defthm p0-implies-p3
       (implies (p0 x)
                (p3 x)))
    
    ; Important ``useless'' rule, preventing p3-implies-p1-is-p2 from firing:
     (defthm p3-is-p4
       (equal (p3 x) (p4 x))))
    
    Now we can see that p3-is-p4 is labeled as ``useless'', by evaluating these commands.
    (accumulated-persistence t)
    (thm (implies (p0 x) (p1 x)))
    (show-accumulated-persistence)
    
    If instead we first evaluate (in-theory (disable p3-is-p4)) before the thm above, then the proof fails, even though p3-is-p4 was labeled as ``useless''!

    Nevertheless, in general it is probably safe to disable rules reported as ``useless'' by (show-accumulated-persistence :useless), and doing so may speed up a proof considerably.

    Remark. The example above suggests a surprising fact: on rare occasions, a proof may fail when you give an :in-theory hint consisting of exactly the runes reported in a proof that succeeds. For, imagine a rule R that is needed in part of the proof but is ``bad'' in a second part, and that some other, ``useless'' rule prevents the application of R in that second part. The example above suggests that disabling this ``useless'' rule can allow the second application of R, thus preventing the proof.




    acl2-sources/doc/HTML/ACCUMULATED-PERSISTENCE.html0000664002132200015000000005236012222333522020265 0ustar kaufmannacl2 ACCUMULATED-PERSISTENCE.html -- ACL2 Version 6.3

    ACCUMULATED-PERSISTENCE

    to get statistics on which runes are being tried
    Major Section:  OTHER
    

    Useful Forms:
    (accumulated-persistence t)              ; Activate statistics gathering.
    (accumulated-persistence :all)           ; As above, ``enhanced'' (see below)
    
    (show-accumulated-persistence :frames)   ; Display statistics ordered by
    (show-accumulated-persistence :tries)    ; frames built, times tried,
    (show-accumulated-persistence :ratio)    ; or their ratio.
    
    (accumulated-persistence nil)            ; Deactivate.
    
    Advanced forms:
    (show-accumulated-persistence :frames-s) ; The `s', `f', and `a' suffixes
    (show-accumulated-persistence :frames-f) ; stand for `success' (`useful'),
    (show-accumulated-persistence :frames-a) ; `failure' (`useless'), and `all',
    (show-accumulated-persistence :tries-s)  ; respectively.  The only effect of
    (show-accumulated-persistence :tries-f)  ; the `s' and `f' versions is to
    (show-accumulated-persistence :tries-a)  ; sort first by useful or useless
                                             ; applications, respectively (see
                                             ; below).  The `a' versions avoid
                                             ; showing the useful/useless
                                             ; breakdown.
    
    (show-accumulated-persistence :runes)    ; Just show runes alphabetically.
    
    

    Some Related Topics

    Note: set-accumulated-persistence is equivalent to accumulated-persistence.

    See the end of this item for a discussion of ``enhanced statistics gathering,'' which can be useful for more fine-grained proof debugging.

    Generally speaking, the more ACL2 knows, the slower it runs. That is because the search space grows with the number of alternative rules. Often, the system tries to apply rules that you have forgotten were even there, if you knew about them in the first place! ``Accumulated-persistence'' is a statistic (originally developed for Nqthm) that helps you identify the rules that are causing ACL2's search space to explode.

    For other proof debugging utilities, see break-rewrite and see dmr.

    Accumulated persistence tracking can be turned on or off. It is generally off. When on, proofs may take perhaps 50% more time than otherwise! But some useful numbers are collected. When it is turned on, by

    ACL2 !>(accumulated-persistence t)
    
    an accumulation site is initialized and henceforth data about which rules are being tried is accumulated into that site. That accumulated data can be displayed with show-accumulated-persistence, as described in detail below. When accumulated persistence is turned off, with (accumulated-persistence nil), the accumulation site is wiped out and the data in it is lost.

    The ``accumulated persistence'' of a rune is the number of runes the system has attempted to apply (since accumulated persistence was last activated) while the given rune was being tried.

    Consider a :rewrite rule named rune. For simplicity, let us imagine that rune is tried only once in the period during which accumulated persistence is being monitored. Recall that to apply a rewrite rule we must match the left-hand side of the conclusion to some term we are trying to rewrite, establish the hypotheses of rune by rewriting, and, if successful, then rewrite the right-hand side of the conclusion. We say rune is ``being tried'' from the time we have matched its left-hand side to the time we have either abandoned the attempt or finished rewriting its right-hand side. (By ``match'' we mean to include any loop-stopper requirement; see loop-stopper.) During that period of time other rules might be tried, e.g., to establish the hypotheses. The rules tried while rune is being tried are ``billed'' to rune in the sense that they are being considered here only because of the demands of rune. Thus, if no other rules are tried during that period, the accumulated persistence of rune is 1 -- we ``bill'' rune once for its own application attempt. If, on the other hand, we tried 10 rules on behalf of that application of rune, then rune's accumulated persistence would be 11.

    One way to envision accumulated persistence is to imagine that every time a rune is tried it is pushed onto a stack. The rules tried on behalf of a given application of a rune are thus pushed and popped on the stack above that rune. A lot of work might be done on its behalf -- the stack above the rune grows and shrinks repeatedly as the search continues for a way to use the rune. All the while, the rune itself ``persists'' in the stack, until we finish with the attempt to apply it, at which time we pop it off. The accumulated persistence of a rune application is thus the number of stack frames built while that rune was on the stack.

    Note that accumulated persistence is tallied whether or not the attempt to apply a rune is successful. Each of the rules tried on its behalf might have failed and the attempt to apply the rune might have also failed. The ACL2 proof script would make no mention of the rune or the rules tried on its behalf because they did not contribute to the proof. But time was spent pursuing the possible application of the rune and accumulated persistence is a measure of that time.

    A high accumulated persistence might come about in two extreme ways. One is that the rule causes a great deal of work every time it is tried. The other is that the rule is ``cheap'' but is tried very often. We therefore keep track of the number of times each rule is tried as well as its persistence. The ratio between the two is the average amount of work done on behalf of the rule each time it is tried.

    When the accumulated persistence totals are displayed by the function show-accumulated-persistence we sort them so that the most expensive runes are shown first. We can sort according to one of three basic keys:

    :frames - the number of frames built on behalf of the rune
    :tries  - the number of times the rune was tried
    :ratio  - frames built per try
    
    The key simply determines the order in which the information is presented. If no argument is supplied to show-accumulated-persistence, :frames is used.

    The display breaks each total into ``useful'' and ``useless'' subtotals. A ``useful'' rule try is one that is viewed as contributing to the progress of the proof, and the rest are ``useless'' rule applications. For example, if a :rewrite rule is tried but its hypotheses are not successfully relieved, then that rule application and all work done on behalf of those hypotheses is ``useless'' work. In general, an attempt to apply a rune is viewed as ``useful'' unless the attempt fails or the attempt is on the stack (as described above) for a rune application that ultimately fails. A large number of ``useless'' :frames or :tries along with correspondingly small ``useful'' counts may suggest runes to consider disabling (see disable and see in-theory). Thus, here is a more complete list of the arguments that may be supplied to show-accumulated-persistence. Suffixes ``s'', ``f'', and ``a'' are intended to suggest ``success'' (``useful''), ``failure'' (``useless''), and ``all''.

    :frames     - sort by the number of frames built on behalf of the rune
       :frames-s -   as above, but sort by useful applications
       :frames-f -   as above, but sort by useless applications
       :frames-a -   as above, but inhibit display of ``useful'' and
                     ``useless'' subtotals
    :tries      - sort by the number of times the rune was tried
       :tries-s  -   as above, but sort by useful applications
       :tries-f  -   as above, but sort by useless applications
       :tries-a  -   as above, but inhibit display of ``useful'' and
                     ``useless'' subtotals
    :ratio      - sort by frames built per try
    :useless    - show only the runes tried whose tries were all ``useless''
    

    For a given line of the report, every frame credited to a ``useful'' (respectively, ``useless'') rule application is considered ``useful'' (respectively, ``useless''). We illustrate with the following example.

    (progn
      (defstub hyp (x) t)
      (defstub concl (x) t)
      (defstub bad (x) t)
      (defstub good (x) t)
      (defaxiom good-ax
        (implies (good x) (hyp x)))
      (defaxiom bad-ax
        (implies (bad x) (hyp x)))
      (defaxiom hyp-implies-concl
        (implies (hyp x) (concl x)))
      )
    (accumulated-persistence t)
    (thm (implies (good x) (concl x)))
    (show-accumulated-persistence)
    
    To prove the thm form, ACL2 attempts to rewrite (concl x) to true by applying rule hyp-implies-concl. It then attempts to establish (hyp x) first by trying rule bad-ax, which fails, and second by trying rule good-ax, which succeeds. As expected, the report labels as ``useless'' the failure of the attempt to establish the hypothesis, (bad x).
       --------------------------------
             1        1 (    1.00) (:REWRITE BAD-AX)
             0        0    [useful]
             1        1    [useless]
       --------------------------------
    
    Now consider the top-level application of rule hyp-implies-concl. Even though the above report shows the application of bad-ax as ``useless'', note that this rule was applied on behalf of the successful (``useful'') application of hyp-implies-concl, and hence is incorporated into the ``useful'' line for hyp-implies-concl, as follows.
       --------------------------------
             3        1 (    3.00) (:REWRITE HYP-IMPLIES-CONCL)
             3        1    [useful]
             0        0    [useless]
       --------------------------------
    
    In summary: categorization of :frames as ``useful'' or ``useless'' is based on whether they support ``useful'' or ``useless'' :tries.

    Note that a rune with high accumulated persistence may not actually be the ``culprit.'' For example, suppose rune1 is reported to have a :ratio of 101, meaning that on the average a hundred and one frames were built each time rune1 was tried. Suppose rune2 has a :ratio of 100. It could be that the attempt to apply rune1 resulted in the attempted application of rune2 and no other rune. Thus, in some sense, rune1 is ``cheap'' and rune2 is the ``culprit'' even though it costs less than rune1.

    If a proof is aborted, then in general, show-accumulated-persistence will only display totals for runes whose attempted application is complete: that is, if the rewriter was in the process of relieving hypotheses for a rule, then information for that rule will not be included in the tally. We say ``in general'' because, as indicated near the top of the output from show-accumulated-persistence when such incomplete information is omitted, you can get this information by using argument :frames-a or :tries-a.

    There are other subtleties in how rune applications are tallied, documented elsewhere: see accumulated-persistence-subtleties.

    We conclude with a discussion of ``enhanced'' statistics gathering, which is enabled by supplying accumulated-persistence the argument :ALL:

    (accumulated-persistence :all)
    
    At some additional performance expense (but probably well under a factor of 2 altogether), ACL2 then gathers additional statistics for individual hypotheses of rules as well as their conclusions. To understand how this works, suppose rn is a rune. Then we prepend the keyword :CONC to rn to form what we call its ``conclusion xrune'', and for its I-th hypothesis we prepend :HYP I to rn to form its I-th ``hypothesis xrune.'' Here, ``xrune'' is pronounced ``ex rune'', and is mnemonic for ``extended rune.'' For example, if (REWRITE FOO) is a rune then (:CONC REWRITE FOO) is its conclusion xrune, and (:HYP 2 REWRITE FOO) is a hypothesis xrune corresponding to the second hypothesis of the corresponding rewrite rule.

    With (accumulated-persistence :all), we instruct ACL2 to track not only runes but also xrunes. Then, (show-accumulated-persistence) will display information for all xrunes in a format that we consider to be ``raw'', in the sense that data for xrunes are displayed just as for runes. But a ``merged'' format is also available. Here is a summary of display commands, followed below by further discussion.

      (show-accumulated-persistence :frames t) ; t is optional, i.e., the default
         ; Display enhanced statistics sorted by frames, in a ``raw'' format.
      (show-accumulated-persistence :frames :merge)
         ; Display enhanced statistics sorted by frames, in a ``merged'' format.
      (show-accumulated-persistence :frames nil)
         ; Display regular statistics sorted by frames, without the enhancements.
    
      ; More generally, the descriptions just above apply for any legal first
      ; argument:
    
      (show-accumulated-persistence KEY t)
      (show-accumulated-persistence KEY :merge)
      (show-accumulated-persistence KEY nil)
    
      ; Note also these alternate forms, equivalent to the first of the two forms
      ; just above, i.e., the form with second argument of t:
      (show-accumulated-persistence KEY :raw)
      (show-accumulated-persistence KEY)
    

    There is a significant difference between how runes are tracked and how ACL2 tracks hypothesis and conclusion xrunes: unlike regular runes, these xrunes do not contribute to the accumulated :frames counts. Rather, they serve as accumulation sites without contributing their :tries to any accumulation. Consider for example the snippet below, taken from a report created with the :merge option (to be discussed further below), i.e., by evaluating the form (show-accumulated-persistence :frames :merge).

       :frames   :tries    :ratio  rune
       --------------------------------
           462      211 (    2.18) (:REWRITE PERM-MEM)
            13        6    [useful]
           449      205    [useless]
          .............................
           251       47 (    5.34) (:HYP 2 :REWRITE PERM-MEM)
             6        6    [useful]
           245       41    [useless]
          .............................
             0      211 (    0.00) (:HYP 1 :REWRITE PERM-MEM)
             0        6    [useful]
             0      205    [useless]
          .............................
             0        7 (    0.00) (:CONC :REWRITE PERM-MEM)
             0        6    [useful]
             0        1    [useless]
       --------------------------------
    
    Notice that while :tries are recorded for the xrune (:HYP 1 :REWRITE PERM-MEM), no :frames are recorded. This is because no stack frames were built for runes while this xrune was on the stack -- only for the xrune itself, which as we explained above is not accumulated into the total :frames counts. As it turns out, this lack of stack frames is explained by the fact that the rewrite rule PERM-MEM has a free variable in the first hypothesis.
    ACL2 !>:pe perm-mem
             18  (DEFTHM PERM-MEM
                         (IMPLIES (AND (PERM X Y) (MEM A X))
                                  (MEM A Y))
                         :RULE-CLASSES ((:REWRITE :MATCH-FREE :ONCE)))
    ACL2 !>
    
    The second hypothesis, however, does cause additional rewriting in order to rewrite it to true, resulting in 251 stack frames for runes. We see that the conclusion does not lead to creation of any rune stack frames, which might seem to suggest that only 251 stack frames for runes were created on behalf of this rule application -- yet, we see that 462 frames were actually created. The difference is the 211 frames created for the rewrite rule itself. Even if the total had been a bit more than 462, one need not be surprised, as there could be some work recorded during application of the rewrite rule, such as type-prescription reasoning, that is not done during rewriting of a hypothesis or the conclusion.

    Now suppose we have executed (accumulated-persistence :all) and attempted some proofs, and now we are ready to see statistics. The form (show-accumulated-persistence) displays statistics exactly as described above, treating these extra xrunes just as though they are runes; similarly for the form (show-accumulated-persistence KEY), for any legal KEY. A second optional argument may however be supplied to show-accumulated-persistence. The default for that second argument is t, and a second argument of :raw is treated the same as t; thus, these arguments provide the behavior just described, where data for xrunes are displayed just as for runes. You may restrict output to runes, ignoring hypothesis and conclusion xrunes, by giving a second argument of nil. (This gives the same behavior as if we had started with the command (accumulated-persistence t) instead of the command (accumulated-persistence :all).) Finally, you may give a second argument of :merge, in which case output will be sorted and displayed as though only runes were tracked (not the extra xrunes), but each data item for a non-rune xrune will be merged so that it is displayed in suitable order just below its corresponding rune, as in the PERM-MEM example displayed above.

    We close by mentioning two aspects of enhanced statistics display for :CONC xrunes that have potential to be confusing. First consider the following example.

         :frames   :tries    :ratio  rune
       --------------------------------
            14        4 (    3.50) (:REWRITE DEFAULT-+-2)
             0        0    [useful]
            14        4    [useless]
          .............................
            10        4 (    2.50) (:HYP 1 :REWRITE DEFAULT-+-2)
             0        0    [useful]
            10        4    [useless]
       --------------------------------
    
    It may be surprising that no data is displayed for the corresponding :CONC xrune. The explanation, however, is simple: the hypothesis never rewrote to true, so the conclusion was never rewritten. This is consistent with the marking as ``useless'' of all :frames and :tries for the rune and the hypothesis xrune. Note by the way, once again, that the hypothesis xrune does not contribute to any :frames count.

    Another reason not to see data displayed for a :CONC xrune is that if a rule has no hypotheses, then no such data is collected. This decision was made because in the case of no hypotheses, we expect it to be very rare that information for the :CONC xrune will add any useful insight.

    On a final note: (show-accumulated-persistence :runes) may be used simply to see a list of all runes (or xrunes) displayed alphabetically.

    Users are encouraged to think about other meters we could install in ACL2 to help diagnose performance problems.




    acl2-sources/doc/HTML/ACKNOWLEDGMENTS.html0000664002132200015000000001763312222333520017203 0ustar kaufmannacl2 ACKNOWLEDGMENTS.html -- ACL2 Version 6.3

    ACKNOWLEDGMENTS

    some contributors to the well-being of ACL2
    Major Section:  MISCELLANEOUS
    

    The development of ACL2 was initially made possible by funding from the U. S. Department of Defense, including ARPA and ONR. We thank all the organizations that have contributed support, including the following (in alphabetical order).

    o AMD, for providing significant time over several years for Matt Kaufmann to carry out ACL2 research, support, and development
    o Computational Logic, Inc. and its president, Don Good, where the first eight years of ACL2 development occurred
    o Centaur Technology
    o DARPA
    o Digital Equipment Corporation
    o EDS, which provided some time for Matt Kaufmann's ACL2 work 1998-1999
    o ForrestHunt and, more generally, Warren A. Hunt, Jr. (see below)
    o IBM
    o NSF
    o ONR
    o Rockwell Collins
    o SRC
    o Sun Microsystems
    o University of Texas at Austin (in particular support to J Moore through the Admiral B. R. Inman Chair of Computing Theory)

    We are especially grateful to Warren A. Hunt, Jr. for his unrivaled efforts in securing support for the entire ACL2 research group at both Computational Logic, Inc., and the University of Texas at Austin. Without his efforts, we would have spent less time working on the system and fewer students would have been funded to apply it.

    ACL2 was started in August, 1989 by Boyer and Moore working together. They co-authored the first versions of axioms.lisp and basis.lisp, with Boyer taking the lead in the formalization of ``state'' and the most primitive io functions. Boyer also had a significant hand in the development of the early versions of the files interface-raw.lisp and translate.lisp. For several years, Moore alone was responsible for developing the ACL2 system code, though he consulted often with both Boyer and Kaufmann. In August, 1993, Kaufmann became jointly responsible with Moore for developing the system. Boyer has continued to provide valuable consulting on an informal basis.

    Bishop Brock was the heaviest early user of ACL2, and provided many suggestions for improvements. In particular, the :cases and :restrict hints were his idea; he developed an early version of congruence-based reasoning for Nqthm; and he helped in the development of some early books about arithmetic. In a demonstration of his courage and faith in us, he pushed for Computational Logic, Inc., to agree to the Motorola CAP contract -- which required formalizing a commercial DSP in the untested ACL2 -- and moved to Scottsdale, AZ, to do the work with the Motorola design team. His demonstration of ACL2's utility was an inspiration, even to those of us designing ACL2.

    John Cowles also helped in the development of some early books about arithmetic, and also provided valuable feedback and bug reports.

    Other early users of ACL2 at Computational Logic, Inc. helped influence its development. In particular, Warren Hunt helped with the port to Macintosh Common Lisp, and Art Flatau and Mike Smith provided useful general feedback.

    Mike Smith helped develop the Emacs portion of the implementation of proof trees.

    Bill Schelter made some enhancements to akcl (now gcl) that helped to enhance ACL2 performance in that Common Lisp implementation, and more generally, responded helpfully to our bug reports. Camm Maguire has since provided wonderful gcl support, and has created a Debian package for ACL2 built on GCL. We are also grateful to developers of other Common Lisp implementations.

    Kent Pitman helped in our interaction with the ANSI Common Lisp standardization committee, X3J13.

    John Cowles helped with the port to Windows (98) by answering questions and running tests.

    Ruben Gamboa created a modification of ACL2 to allow reasoning about the real numbers using non-standard analysis. His work has been incorporated into the ACL2 distribution; see real.

    Rob Sumners has made numerous useful suggestions. In particular, he has designed and implemented improvements for stobjs and been key in our development of locally-bound stobjs; see note-2-6.

    Robert Krug has designed and implemented many changes in the vicinity of the linear arithmetic package and its connection to type-set and rewrite. He was also instrumental in the development of extended-metafunctions.

    Pete Manolios has made numerous useful suggestions. In particular, Pete helped us to organize the first workshop and was a wonderful equal partner with the two of us (Kaufmann and Moore) in producing the books that arose from that workshop. Pete and his student, Daron Vroon, provided the current implementation of ordinals.

    Jared Davis and Sol Swords have our gratitude for starting the acl2-books repository, http://acl2-books.googlecode.com/.

    We thank David L. Rager for contributing an initial version of the support for parallelism in an experimental extension of ACL2.

    Bob Boyer and Warren A. Hunt, Jr. developed a canonical representation for ACL2 data objects and a function memoization mechanism to facilitate reuse of previously computed results. We thank them for their extensive efforts for the corresponding experimental (as of 2008 and 2009) extension of ACL2; see hons-and-memoization.

    We also thank the contributors to the ACL2 workshops for some suggested improvements and for the extensive collection of publicly distributed benchmark problems. And we thank participants at the ACL2 seminar at the University of Texas for useful feedback. More generally, we thank the ACL2 community for feedback, contributed books (see community-books), and their interest in the ACL2 project.

    Regarding the documentation:

    Bill Young wrote significant portions of the original acl2-tutorial section of the ACL2 documentation, including what is now called alternative-introduction. This was an especially important task in the early years when there was no guide for how to use ACL2 and we are very grateful. He, Bishop Brock, Rich Cohen, and Noah Friedman read over considerable amounts of the documentation, and made many useful comments. Others, particularly Bill Bevier and John Cowles, have also made useful comments on the documentation.

    Art Flatau helped develop the ACL2 markup language and translators from that language to Texinfo and HTML. Michael ``Bogo'' Bogomolny created a search engine, beginning with Version 2.6, and for that purpose modified the HTML translator to create one file per topic (a good idea in any case).

    Laura Lawless provided many hours of help in marking up appropriate parts of the documentation in typewriter font.

    Noah Friedman developed an Emacs tool that helped us insert ``invisible links'' into the documentation, which improve the usability of that documentation under HTML readers such as Mosaic.

    Richard Stallman contributed a texinfo patch, to be found in the file doc/texinfo.tex.




    acl2-sources/doc/HTML/ACL2-AS-STANDALONE-PROGRAM.html0000664002132200015000000000652712222333515020477 0ustar kaufmannacl2 ACL2-AS-STANDALONE-PROGRAM.html -- ACL2 Version 6.3

    ACL2-AS-STANDALONE-PROGRAM

    Calling ACL2 from another program
    Major Section:  ACL2-TUTORIAL
    

    ACL2 is intended for interactive use. It is generally unrealistic to expect it to prove theorems fully automatically; see the-method, and see introduction-to-the-theorem-prover for a more detailed tutorial.

    Nevertheless, here we describe an approach for how to call the ACL2 theorem prover noninteractively. These steps can of course be modified according to your needs. Here, we illustrate how to call ACL2 from another Lisp program (or an arbitrary program) to attempt to prove an arithmetic theorem.

    === STEP 1: ===

    Build a suitable ACL2 image by starting ACL2 and then executing the following forms. In particular, these define a macro, try-thm, that causes ACL2 to exit with with an exit status indicating success or failure of a proof attempt.

    (include-book "arithmetic-5/top" :dir :system)
    (defmacro try-thm (&rest args)
      `(mv-let (erp val state)
               (with-prover-time-limit 3 (thm ,@args))
               (declare (ignore val))
               (prog2$ (if erp (exit 1) (exit 0)) state))))
    (reset-prehistory) ; optional
    :q
    (save-exec "arith-acl2" "Included arithmetic-4/top")
    

    If you prefer, above you can replace 3 by some other number of seconds as a time limit for the prover. Also, you can replace

    (with-prover-time-limit 3 (thm ,@args))
    
    by
    (with-output :off :all (with-prover-time-limit 3 (thm ,@args)))
    
    if you want to turn off output. It may be best to leave the output on, instead eliminating it in the calling program (see Step 3 below).

    === STEP 2: ===

    Try a little test. In that same directory try this:

    echo '(try-thm (equal x x))' | ./arith-acl2
    echo $?
    

    The exit status should be 0, indicating success. Now try this:

    echo '(try-thm (not (equal x x)))' | ./arith-acl2
    echo $?
    

    The exit status should be 1, indicating failure.

    === STEP 3: ===

    Create a shell script that automates Step 2, for example:

    #!/bin/sh
    (echo "(try-thm $1)" | ./arith-acl2) >& /dev/null
    exit $?
    

    === STEP 4: ===

    Try your script from a Lisp program, if you like. Here is how you can do it in SBCL, for example. (Different Lisps have different ways to do this, as summarized in function system-call in ACL2 source file acl2-init.lisp.)

    (defun provable? (x)
      (let ((status
             (process-exit-code
              (sb-ext:run-program "./try-thm.sh" (list (format nil "~s" x))
                                  :output t :search t))))
        (eql status 0)))
    

    Then here is a log:

      * (provable? '(equal x y))
    
      NIL
      * (provable? '(equal x x))
    
      T
      *
    

    Certainly refinements are possible -- for example the above doesn't distinguish between unprovable and ill-formed input. But it's a start.




    acl2-sources/doc/HTML/ACL2-BUILT-INS.html0000664002132200015000000012600012222333523016733 0ustar kaufmannacl2 ACL2-BUILT-INS.html -- ACL2 Version 6.3

    ACL2-BUILT-INS

    built-in ACL2 functions
    Major Section:  PROGRAMMING
    

    This documentation topic is a parent topic under which we include documentation for built-in functions, macros, and special forms that are typically used in programming. For others, including those typically used as top-level commands or those that create events (defun, defthm, and so on), documentation may be found as a subtopic of some other parent topic. We do not document some of the more obscure functions provided by ACL2 that do not correspond to functions of Common Lisp.

    Some Related Topics

    See any documentation for Common Lisp for more details on many of these functions.




    acl2-sources/doc/HTML/ACL2-COUNT.html0000664002132200015000000000243512222333523016322 0ustar kaufmannacl2 ACL2-COUNT.html -- ACL2 Version 6.3

    ACL2-COUNT

    a commonly used measure for justifying recursion
    Major Section:  ACL2-BUILT-INS
    

    (Acl2-count x) returns a nonnegative integer that indicates the ``size'' of its argument x.

    All characters and symbols have acl2-count 0. The acl2-count of a string is the number of characters in it, i.e., its length. The acl2-count of a cons is one greater than the sum of the acl2-counts of the car and cdr. The acl2-count of an integer is its absolute value. The acl2-count of a rational is the sum of the acl2-counts of the numerator and denominator. The acl2-count of a complex rational is one greater than the sum of the acl2-counts of the real and imaginary parts.




    acl2-sources/doc/HTML/ACL2-CUSTOMIZATION.html0000664002132200015000000001243212222333530017476 0ustar kaufmannacl2 ACL2-CUSTOMIZATION.html -- ACL2 Version 6.3

    ACL2-CUSTOMIZATION

    file of initial commands for ACL2 to run at startup
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    ACL2 provides a mechanism to load automatically a so-called ``ACL2 customization file,'' via ld, the first time lp is called in an ACL2 session. ACL2 looks for this file as follows.

    o If the host Lisp reads a non-empty value for the system's environment variable ACL2_CUSTOMIZATION, then that string value is used for the customization file name. In this case, if the file does not exist or if the string is "NONE" then there is no customization file. Notes. (1) If the customization file name is a relative pathname (see pathname), then the pathname is considered relative to the connected book directory (see cbd). (2) If this variable is not already defined, then its value is set to NONE when the ACL2 makefile system is invoked (specifically, using community books file books/Makefile-generic), e.g., for a regression.

    o Otherwise (empty environment variable value), file "acl2-customization.lsp" or "acl2-customization.lisp" on the connected book directory (see cbd), generally the current directory, is the customization file (in that order) if either exists.

    o Otherwise file "acl2-customization.lsp" or "acl2-customization.lisp" on your home directory is the customization file (in that order), if either exists (except, this case is skipped on Windows operating systems.

    Except for the fact that this ld command is not typed explicitly by you, it is a standard ld command, with one exception: any settings of ld specials are remembered once this call of ld has completed. For example, suppose that you start your customization file with (set-ld-skip-proofsp t state), so that proofs are skipped as it is loaded with ld. Then the ld special ld-skip-proofsp will remain t after the ld has completed, causing proofs to be skipped in your ACL2 session, unless your customization file sets this variable back to nil, say with (set-ld-skip-proofsp nil state).

    If the customization file exists, it is loaded with ld using the usual default values for the ld specials (see ld). Thus, if an error is encountered, no subsequent forms in the file will be evaluated.

    To create a customization file it is recommended that you first give it a name other than "acl2-customization.lsp" or "acl2-customization.lisp" so that ACL2 does not try to include it prematurely when you next enter lp. Then, while in the uncustomized lp, explicitly invoke ld on your evolving (but renamed) customization file until all forms are successfully evaluated. The same procedure is recommended if for some reason ACL2 cannot successfully evaluate all forms in your customization file: temporarily rename your customization file so that ACL2 does not try to ld it automatically and then debug the new file by explicit calls to ld.

    WARNING! If you certify a book after the (automatic) loading of a customization file, the forms in that file will be part of the portcullis of the books you certify! That is, the forms in your customization file at certification time will be loaded whenever anybody uses the books you are certifying. Since customization files generally contain idiosyncratic commands, you may not want yours to be part of the books you create for others. Thus, if you have a customization file then you may want to invoke :ubt 1 before certifying any books; alternatively, see certify-book! for automatic invocation of ubt.

    On the other hand, if you wish to prevent undoing commands from the customization file, see reset-prehistory.

    Finally, we note that except on Windows-based systems, if there is a file acl2-init.lsp in your home directory, then it will be loaded into raw Lisp when ACL2 is invoked.




    acl2-sources/doc/HTML/ACL2-DEFAULTS-TABLE.html0000664002132200015000000004325412222333522017471 0ustar kaufmannacl2 ACL2-DEFAULTS-TABLE.html -- ACL2 Version 6.3

    ACL2-DEFAULTS-TABLE

    a table specifying certain defaults, e.g., the default defun-mode
    Major Section:  OTHER
    

    Example Forms:
    (table acl2-defaults-table :defun-mode) ; current default defun-mode
    (table acl2-defaults-table :defun-mode :program)
               ; set default defun-mode to :program
    

    See table for a discussion of tables in general. The legal keys for this table are shown below. They may be accessed and changed via the general mechanisms provided by tables. However, there are often more convenient ways to access and/or change the defaults. (See also the note below.)

    :defun-mode
    
    the default defun-mode, which must be :program or :logic. See defun-mode for a general discussion of defun-modes. The :defun-mode key may be conveniently set by keyword commands naming the new defun-mode, :program and :logic. See program and see logic.
    :enforce-redundancy
    
    if t, cause ACL2 to insist that most events are redundant (see redundant-events); if :warn, cause a warning instead of an error for such non-redundant events; else, nil. See set-enforce-redundancy.
    :ignore-doc-string-error
    
    if t, cause ACL2 to ignore ill-formed documentation strings rather than causing an error; if :warn, cause a warning instead of an error in such cases; else, nil (the default). See set-ignore-doc-string-error.
    :verify-guards-eagerness
    
    an integer between 0 and 2 indicating how eager the system is to verify the guards of a defun event. See set-verify-guards-eagerness.
    :compile-fns
    
    When this key's value is t, functions are compiled when they are defun'd; otherwise, the value is nil. (Except, this key's value is ignored when explicit compilation is suppressed; see compilation.) To set the flag, see set-compile-fns.
    :measure-function
    
    the default measure function used by defun when no :measure is supplied in xargs. The default measure function must be a function symbol of one argument. Let mfn be the default measure function and suppose no :measure is supplied with some recursive function definition. Then defun finds the first formal, var, that is tested along every branch and changed in each recursive call. The system then ``guesses'' that (mfn var) is the :measure for that defun.
    :well-founded-relation
    
    the default well-founded relation used by defun when no :well-founded-relation is supplied in xargs. The default well-founded relation must be a function symbol, rel, of two arguments about which a :well-founded-relation rule has been proved. See well-founded-relation.
    :bogus-defun-hints-ok
    
    When this key's value is t, ACL2 allows :hints for nonrecursive function definitions. Otherwise, the value is the nil (the default) or :warn (which makes the check but merely warns when the check fails). See set-bogus-defun-hints-ok.
    :bogus-mutual-recursion-ok
    
    When this key's value is t, ACL2 skips the check that every function in a mutual-recursion (or defuns) ``clique'' calls at least one other function in that ``clique.'' Otherwise, the value is nil (the default) or :warn (which makes the check but merely warns when the check fails). See set-bogus-mutual-recursion-ok.
    :irrelevant-formals-ok
    
    When this key's value is t, the check for irrelevant formals is bypassed; otherwise, the value is the keyword nil (the default) or :warn (which makes the check but merely warns when the check fails). See irrelevant-formals and see set-irrelevant-formals-ok.
    :ignore-ok
    
    When this key's value is t, the check for ignored variables is bypassed; otherwise, the value is the keyword nil (the default) or :warn (which makes the check but merely warns when the check fails). See set-ignore-ok.
    :bdd-constructors
    
    This key's value is a list of function symbols used to define the notion of ``BDD normal form.'' See bdd-algorithm and see hints.
    :ttag
    
    This key's value, when non-nil, allows certain operations that extend the trusted code base beyond what is provided by ACL2. See defttag. See defttag.
    :state-ok
    
    This key's value is either t or nil and indicates whether the user is aware of the syntactic restrictions on the variable symbol STATE. See set-state-ok.
    :backchain-limit
    
    This key's value is a list of two ``numbers.'' Either ``number'' may optionally be nil, which is treated like positive infinity. The numbers control backchaining through hypotheses during type-set reasoning and rewriting. See backchain-limit.
    :default-backchain-limit
    
    This key's value is a list of two ``numbers.'' Either ``number'' may optionally be nil, which is treated like positive infinity. The numbers are used respectively to set the backchain limit of a rule if one has not been specified. See backchain-limit.
    :step-limit
    
    This key's value is either nil or a natural number not exceeding the value of *default-step-limit*. If the value is nil or the value of *default-step-limit*, there is no limit on the number of ``steps'' that ACL2 counts during a proof: currently, the number of top-level rewriting calls. Otherwise, the value is the maximum number of such calls allowed during evaluation of any event. See set-prover-step-limit.
    :rewrite-stack-limit
    
    This key's value is a nonnegative integer less than (expt 2 28). It is used to limit the depth of calls of ACL2 rewriter functions. See rewrite-stack-limit.
    :let*-abstractionp
    
    This key affects how the system displays subgoals. The value is either t or nil. When t, let* expressions are introduced before printing to eliminate common subexpressions. The actual goal being worked on is unchanged.
    :nu-rewriter-mode
    
    This key's value is nil, t, or :literals. When the value is non-nil, the rewriter gives special treatment to expressions and functions defined in terms of nth and update-nth. See set-nu-rewriter-mode.
    :case-split-limitations
    
    This key's value is a list of two ``numbers.'' Either ``number'' may optionally be nil, which is treated like positive infinity. The numbers control how the system handles case splits in the simplifier. See set-case-split-limitations.
    :include-book-dir-alist
    
    This key's value is used by include-book's :DIR argument to associate a directory with a keyword. An exception is the keyword :SYSTEM for the books/ directory; see include-book, in particular the section on ``Books Directory.''
    :match-free-default
    
    This key's value is either :all, :once, or nil. See set-match-free-default.
    :match-free-override
    
    This key's value is a list of runes. See add-match-free-override.
    :match-free-override-nume
    
    This key's value is an integer used in the implementation of add-match-free-override, so that only existing runes are affected by that event.
    :non-linearp
    
    This key's value is either t or nil and indicates whether the user wishes ACL2 to extend the linear arithmetic decision procedure to include non-linear reasoning. See non-linear-arithmetic.
    :tau-auto-modep
    
    This key's value is either t or nil and indicates whether the user wishes ACL2 to look for opportunities to create :tau-system rules from all suitable defuns and from all suitable defthms (with non-nil :rule-classes). See set-tau-auto-mode.
    :ruler-extenders
    
    This key's value may be a list of symbols, indicating those function symbols that are not to block the collection of rulers; see defun. Otherwise the value is :all to indicate all function symbols, i.e., so that no function symbol blocks the collection of rulers. If a list is specified (rather than :all), then it may contain the keyword :lambdas, which has the special role of specifying all lambda applications. No other keyword is permitted in the list. See ruler-extenders.
    :memoize-ideal-okp
    
    This key is only legal in an experimental hons version (see hons-and-memoization). Its value must be either t, nil, or :warn. If the value is nil or not present, then it is illegal by default to memoize a :logic mode function that has not been guard-verified (see verify-guards), sometimes called an ``ideal-mode'' function. This illegality is the default because such calls of such functions in the ACL2 loop are generally evaluated in the logic (using so-called ``executable counterpart'' definitions), rather than directly by executing calls of the corresponding (memoized) raw Lisp function. However, such a raw Lisp call can be made when the function is called by a :program mode function, so we allow you to override the default behavior by associating the value t or :warn with the key :memoize-ideal-okp, where with :warn you get a suitable warning. Note that you can also allow memoization of ideal-mode functions by supplying argument :ideal-okp to your memoization event (see memoize), in which case the value of :memoize-ideal-okp in the acl2-defaults-table is irrelevant.

    Note: Unlike all other tables, acl2-defaults-table can affect the soundness of the system. The table mechanism therefore enforces on it a restriction not imposed on other tables: when table is used to update the acl2-defaults-table, the key and value must be variable-free forms. Thus, while

    (table acl2-defaults-table :defun-mode :program),
    
    (table acl2-defaults-table :defun-mode ':program), and
    
    (table acl2-defaults-table :defun-mode (compute-mode *my-data*))
    
    are all examples of legal events (assuming compute-mode is a function of one non-state argument that produces a defun-mode as its single value),
    (table acl2-defaults-table :defun-mode (compute-mode (w state)))
    
    is not legal because the value form is state-sensitive.

    Consider for example the following three events which one might make into the text of a book.

    (in-package "ACL2")
    
    (table acl2-defaults-table
      :defun-mode
      (if (ld-skip-proofsp state) :logic :program))
    
    (defun crash-and-burn (x) (car x))
    
    The second event is illegal because its value form is state-sensitive. If it were not illegal, then it would set the :defun-mode to :program when the book was being certified but would set the defun-mode to :logic when the book was being loaded by include-book. That is because during certification, ld-skip-proofsp is nil (proof obligations are generated and proved), but during book inclusion ld-skip-proofsp is non-nil (those obligations are assumed to have been satisfied.) Thus, the above book, when loaded, would create a function in :logic mode that does not actually meet the conditions for such status.

    For similar reasons, table events affecting acl2-defaults-table are illegal within the scope of local forms. That is, the text

    (in-package "ACL2")
    
    (local (table acl2-defaults-table :defun-mode :program))
    
    (defun crash-and-burn (x) (car x))
    
    is illegal because acl2-defaults-table is changed locally. If this text were acceptable as a book, then when the book was certified, crash-and-burn would be processed in :program mode, but when the certified book was included later, crash-and-burn would have :logic mode because the local event would be skipped.

    The text

    (in-package "ACL2")
    
    (program) ;which is (table acl2-defaults-table :defun-mode :program)
    
    (defun crash-and-burn (x) (car x))
    
    is acceptable and defines crash-and-burn in :program mode, both during certification and subsequent inclusion.

    We conclude with an important observation about the relation between acl2-defaults-table and include-book, certify-book, and encapsulate. Including or certifying a book never has an effect on the acl2-defaults-table, nor does executing an encapsulate event; we always restore the value of this table as a final act. (Also see include-book, see encapsulate, and see certify-book.) That is, no matter how a book fiddles with the acl2-defaults-table, its value immediately after including that book is the same as immediately before including that book. If you want to set the acl2-defaults-table in a way that persists, you need to do so using commands that are not inside books. It may be useful to set your favorite defaults in your acl2-customization file; see acl2-customization.




    acl2-sources/doc/HTML/ACL2-HELP.html0000664002132200015000000000130412222333522016153 0ustar kaufmannacl2 ACL2-HELP.html -- ACL2 Version 6.3

    ACL2-HELP

    the acl2-help mailing list
    Major Section:  OTHER
    

    You can email questions about ACL2 usage to the acl2-help mailing list: acl2-help@utlists.utexas.edu. If you have more general questions about ACL2, for example, about projects completed using ACL2, you may prefer the acl2 mailing list, acl2@utlists.utexas.edu, which tends to have wider distribution.




    acl2-sources/doc/HTML/ACL2-NUMBER-LISTP.html0000664002132200015000000000113412222333523017306 0ustar kaufmannacl2 ACL2-NUMBER-LISTP.html -- ACL2 Version 6.3

    ACL2-NUMBER-LISTP

    recognizer for a true list of numbers
    Major Section:  ACL2-BUILT-INS
    

    The predicate acl2-number-listp tests whether its argument is a true list of numbers.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ACL2-NUMBERP.html0000664002132200015000000000101512222333523016533 0ustar kaufmannacl2 ACL2-NUMBERP.html -- ACL2 Version 6.3

    ACL2-NUMBERP

    recognizer for numbers
    Major Section:  ACL2-BUILT-INS
    

    (acl2-numberp x) is true if and only if x is a number, i.e., a rational or complex rational number.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_=.html0000664002132200015000000001170512222333525020715 0ustar kaufmannacl2 ACL2-PC_colon__colon_=.html -- ACL2 Version 6.3

    ACL2-PC::=

    (atomic macro) attempt an equality (or equivalence) substitution
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    =     -- replace the current subterm by a term equated to it in
             one of the hypotheses (if such a term exists)
    (= x) -- replace the current subterm by x, assuming that the prover
             can show that they are equal
    (= (+ x y) z)
          -- replace the term (+ x y) by the term z inside the current
             subterm, assuming that the prover can prove
             (equal (+ x y) z) from the current top-level hypotheses
             or that this term or (equal z (+ x y)) is among the
             current top-level hypotheses or the current governors
    (= & z)
          -- exactly the same as above, if (+ x y) is the current
             subterm
    (= (+ x y) z :hints :none)
          -- same as (= (+ x y) z), except that a new subgoal is
             created with the current goal's hypotheses and governors
             as its top-level hypotheses and (equal (+ x y) z) as its
             conclusion
    (= (+ x y) z 0)
          -- exactly the same as immediately above
    (= (p x)
       (p y)
       :equiv iff
       :otf-flg t
       :hints (("Subgoal 2" :BY FOO) ("Subgoal 1" :use bar)))
          -- same as (= (+ x y) z), except that the prover uses
             the indicated values for otf-flg and hints, and only
             propositional (iff) equivalence is used (however, it
             must be that only propositional equivalence matters at
             the current subterm)
    
    General Form:
    (= &optional x y &rest keyword-args)
    
    If terms x and y are supplied, then replace x by y inside the current subterm if they are ``known'' to be ``equal''. Here ``known'' means the following: the prover is called as in the prove command (using keyword-args) to prove (equal x y), except that a keyword argument :equiv is allowed, in which case (equiv x y) is proved instead, where equiv is that argument. (See below for how governors are handled.)

    Actually, keyword-args is either a single non-keyword or is a list of the form ((kw-1 x-1) ... (kw-n x-n)), where each kw-i is one of the keywords :equiv, :otf-flg, :hints. Here :equiv defaults to equal if the argument is not supplied or is nil, and otherwise should be the name of an ACL2 equivalence relation. :Otf-flg and :hints give directives to the prover, as explained above and in the documentation for the prove command; however, no prover call is made if :hints is a non-nil atom or if keyword-args is a single non-keyword (more on this below).

    Remarks on defaults

    (1) If there is only one argument, say a, then x defaults to the current subterm, in the sense that x is taken to be the current subterm and y is taken to be a.

    (2) If there are at least two arguments, then x may be the symbol &, which then represents the current subterm. Thus, (= a) is equivalent to (= & a). (Obscure point: actually, & can be in any package, except the keyword package.)

    (3) If there are no arguments, then we look for a top-level hypothesis or a governor of the form (equal c u) or (equal u c), where c is the current subterm. In that case we replace the current subterm by u.

    As with the prove command, we allow goals to be given ``bye''s in the proof, which may be generated by a :hints keyword argument in keyword-args. These result in the creation of new subgoals.

    A proof is attempted unless the :hints argument is a non-nil atom other than :none, or unless there is one element of keyword-args and it is not a keyword. In that case, if there are any hypotheses in the current goal, then what is attempted is a proof of the implication whose antecedent is the conjunction of the current hypotheses and governors and whose conclusion is the appropriate equal term.

    Remarks: (1) It is allowed to use abbreviations in the hints. (2) The keyword :none has the special role as a value of :hints that is shown clearly in an example above. (3) If there are governors, then the new subgoal has as additional hypotheses the current governors.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_ACL2-WRAP.html0000664002132200015000000000134612222333525021771 0ustar kaufmannacl2 ACL2-PC_colon__colon_ACL2-WRAP.html -- ACL2 Version 6.3

    ACL2-PC::ACL2-WRAP

    (macro) same as (lisp x)
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (acl2-wrap (pe :here))
    
    General Form:
    (acl2-wrap form)
    
    Same as (lisp form). This is provided for interface tools that want to be able to execute the same form in raw Lisp, in the proof-checker, or in the ACL2 top-level loop (lp).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_ADD-ABBREVIATION.html0000664002132200015000000000523612222333525022776 0ustar kaufmannacl2 ACL2-PC_colon__colon_ADD-ABBREVIATION.html -- ACL2 Version 6.3

    ACL2-PC::ADD-ABBREVIATION

    (primitive) add an abbreviation
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example: (add-abbreviation v (* x y)) causes future occurrences of (* x y) to be printed as (? v), until (unless) a corresponding invocation of remove-abbreviations occurs. In this case we say that v ``abbreviates'' (* x y).

    General Form:
    (add-abbreviation var &optional raw-term)
    
    Let var be an abbreviation for raw-term, if raw-term is supplied, else for the current subterm. Note that var must be a variable that does not already abbreviate some term.

    A way to think of abbreviations is as follows. Imagine that whenever an abbreviation is added, say v abbreviates expr, an entry associating v to expr is made in an association list, which we will call ``*abbreviations-alist*''. Then simply imagine that ? is a function defined by something like:

    (defun ? (v)
      (let ((pair (assoc v *abbreviations-alist*)))
        (if pair (cdr pair)
          (error ...))))
    
    Of course the implementation isn't exactly like that, since the ``constant'' *abbreviations-alist* actually changes each time an add-abbreviation instruction is successfully invoked. Nevertheless, if one imagines an appropriate redefinition of the ``constant'' *abbreviations-alist* each time an add-abbreviation is invoked, then one will have a clear model of the meaning of such an instruction.

    The effect of abbreviations on output is that before printing a term, each subterm that is abbreviated by a variable v is first replaced by (? v).

    The effect of abbreviations on input is that every built-in proof-checker command accepts abbreviations wherever a term is expected as an argument, i.e., accepts the syntax (? v) whenever v abbreviates a term. For example, the second argument of add-abbreviation may itself use abbreviations that have been defined by previous add-abbreviation instructions.

    See also remove-abbreviations and show-abbreviations.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_AL.html0000664002132200015000000000111312222333525021025 0ustar kaufmannacl2 ACL2-PC_colon__colon_AL.html -- ACL2 Version 6.3

    ACL2-PC::AL

    (macro) same as apply-linear
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (al 3)
    
    
    See the documentation for apply-linear, as al and apply-linear are identical.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_APPLY-LINEAR.html0000664002132200015000000001357712222333525022407 0ustar kaufmannacl2 ACL2-PC_colon__colon_APPLY-LINEAR.html -- ACL2 Version 6.3

    ACL2-PC::APPLY-LINEAR

    (primitive) apply a linear rule
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (apply-linear foo)
       -- apply the linear rule `foo'
    (apply-linear (:linear foo))
       -- same as above
    (apply-linear 2)
       -- apply the second linear rule, as displayed by show-linears
    rewrite
       -- apply the first rewrite rule, as displayed by show-rewrites
    (apply-linear foo ((y 7)))
       -- apply the linear rule foo with the substitution
          that associates 7 to the ``free variable'' y
    (apply-linear foo ((x 2) (y 3)) t)
       -- apply the linear rule foo by substituting 2 and 3 for free
          variables x and y, respectively, and also binding all other
          free variables possible by using the current context
          (hypotheses and governors)
    
    General Form:
    (apply-linear &optional rule-id substitution instantiate-free)
    
    Add a new top-level hypothesis by applying a linear rule to the current subterm. The new hypothesis will be created according to the information provided by the show-linears (sls) command.

    A short name for this command is al.

    We assume familiarity with the proof-checker's rewrite (r) command. In brief, the apply-linear command is an analogue of the rewrite command, but for linear rules in place of rewrite rules. There is a significant difference: for the apply-linear command, instead of rewriting the current subterm as is done by the rewrite command, the conclusion of the applicable linear rule, suitably instantiated, is added as a new (and last) top-level hypothesis of the goal. There is another significant difference: the automatic application of linear rules in the theorem prover is somewhat more complex than the automatic application of rewrite rules, so the apply-linear command may not correspond as closely to the prover's automatic use of a linear rule as the rewrite command corresponds to the prover's automatic use of a rewrite rule.

    Below, we refer freely to the documentation for the proof-checker's rewrite command.

    The rule-id is treated just as it is by the rewrite command. If rule-id is a positive integer n, then the nth rule as displayed by show-linears is the one that is applied. If rule-id is nil or is not supplied, then it is treated as the number 1. Otherwise, rule-id should be either a symbol or else a :linear rune. If a symbol is supplied, then any linear rule of that name may be used.

    Consider the following example. Suppose that the current subterm is (< (g (h y)) y) and that foo is the name of the following linear rule.

    (implies (true-listp x)
             (< (g x) 15))
    

    Then the instruction (apply-linear foo) applies foo by adding a new hypothesis (< (g (h y)) 15). In addition, a new goal with conclusion (true-listp y) is created unless the current context (top-level hypotheses and governors) implies (true-listp y) using only ``trivial reasoning'', just as for the rewrite command.

    If the rule-id argument is a number or is not supplied, then the system will store an instruction of the form (apply-linear name ...), where name is the name of a linear rule; this is in order to make it easier to replay instructions when there have been changes to the history. Except: instead of the name (whether the name is supplied or calculated), the system stores the rune if there is any chance of ambiguity. (Formally, ``ambiguity'' here means that the rune being applied is of the form (:rewrite name . index), where index is not nil.)

    Speaking in general, then, an apply-linear instruction works as follows.

    First, a linear rule is selected according to the arguments of the instruction. The selection is made as explained under ``General Form'' above.

    Next, a trigger term of the rule (see linear) is matched with the current subterm, i.e., a substitution unify-subst is found such that if one instantiates that trigger term of the rule with unify-subst, then one obtains the current subterm. If this match fails, then the instruction fails.

    Next, an attempt is made to relieve (discharge) the hypotheses, possibly handling free variables (see free-variables), exactly as is done with hypotheses when applying the proof-checker command, rewrite (r).

    Finally, the instruction is applied exactly as the rewrite instruction is applied, except instead of replacing the current subterm, the rule's instantiated conclusion is added to the end of the list of top-level hypotheses of the goal.

    Note that as for the rewrite command, the substitution argument should be a list whose elements have the form (variable term), where term may contain abbreviations.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_BASH.html0000664002132200015000000000274112222333525021256 0ustar kaufmannacl2 ACL2-PC_colon__colon_BASH.html -- ACL2 Version 6.3

    ACL2-PC::BASH

    (atomic macro) call the ACL2 theorem prover's simplifier
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    bash -- attempt to prove the current goal by simplification alone
    (bash ("Subgoal 2" :by foo) ("Subgoal 1" :use bar))
         -- attempt to prove the current goal by simplification alone,
            with the indicated hints
    
    General Form:
    (bash &rest hints)
    
    Call the theorem prover's simplifier, creating a subgoal for each resulting goal.

    Notice that unlike prove, the arguments to bash are spread out, and are all hints.

    Bash is similar to reduce in that neither of these allows induction. But bash only allows simplification, while reduce allows processes eliminate-destructors, fertilize, generalize, and eliminate-irrelevance.

    Remark: All forcing rounds will be skipped (unless there are more than 15 subgoals generated in the first forcing round, an injustice that should be rectified, but might remain unless there is pressure to fix it).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_BDD.html0000664002132200015000000000206712222333525021133 0ustar kaufmannacl2 ACL2-PC_colon__colon_BDD.html -- ACL2 Version 6.3

    ACL2-PC::BDD

    (atomic macro) prove the current goal using bdds
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    bdd
    (bdd :vars nil :bdd-constructors (cons) :prove t :literal :all)
    

    The general form is as shown in the latter example above, but with any keyword-value pairs omitted and with values as described for the :bdd hint; see hints.

    This command simply calls the theorem prover with the indicated bdd hint for the top-level goal. Note that if :prove is t (the default), then the proof will succeed entirely using bdds or else it will fail immediately. See bdd.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_BK.html0000664002132200015000000000217412222333525021035 0ustar kaufmannacl2 ACL2-PC_colon__colon_BK.html -- ACL2 Version 6.3

    ACL2-PC::BK

    (atomic macro) move backward one argument in the enclosing term
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    bk
    
    For example, if the conclusion is (= x (* (- y) z)) and the current subterm is (* (- y) z), then after executing bk, the current subterm will be x.

    Move to the previous argument of the enclosing term.

    This is the same as up followed by (dive n-1), where n is the position of the current subterm in its parent term in the conclusion. Thus in particular, the nx command fails if one is already at the top of the conclusion.

    See also up, dive, top, and bk.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_BOOKMARK.html0000664002132200015000000000175212222333525021747 0ustar kaufmannacl2 ACL2-PC_colon__colon_BOOKMARK.html -- ACL2 Version 6.3

    ACL2-PC::BOOKMARK

    (macro) insert matching ``bookends'' comments
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (bookmark final-goal)
    
    General Form:
    (bookmark name &rest instruction-list)
    
    Run the instructions in instruction-list (as though this were a call of do-all; see the documentation for do-all), but first insert a begin bookend with the given name and then, when the instructions have been completed, insert an end bookend with that same name. See the documentation of comm for an explanation of bookends and how they can affect the display of instructions.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CASESPLIT.html0000664002132200015000000000574712222333525022101 0ustar kaufmannacl2 ACL2-PC_colon__colon_CASESPLIT.html -- ACL2 Version 6.3

    ACL2-PC::CASESPLIT

    (primitive) split into two cases
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (casesplit (< x y)) -- assuming that we are at the top of the
                           conclusion, add (< x y) as a new top-level
                           hypothesis in the current goal, and create a
                           subgoal identical to the current goal except
                           that it has (not (< x y)) as a new top-level
                           hypothesis
    
    General Form:
    (casesplit expr &optional use-hyps-flag do-not-flatten-flag)
    
    When the current subterm is the entire conclusion, this instruction adds expr as a new top-level hypothesis, and create a subgoal identical to the existing current goal except that it has the negation of expr as a new top-level hypothesis. See also claim. The optional arguments control the use of governors and the ``flattening'' of new hypotheses, as we now explain.

    The argument use-hyps-flag is only of interest when there are governors. (To read about governors, see the documentation for the command hyps). In that case, if use-hyps-flag is not supplied or is nil, then the description above is correct; but otherwise, it is not expr but rather it is (implies govs expr) that is added as a new top-level hypothesis (and whose negation is added as a top-level hypothesis for the new goal), where govs is the conjunction of the governors.

    If do-not-flatten-flag is supplied and not nil, then that is all there is to this command. Otherwise (thus this is the default), when the claimed term (first argument) is a conjunction (and) of terms and the claim instruction succeeds, then each (nested) conjunct of the claimed term is added as a separate new top-level hypothesis. Consider the following example, assuming there are no governors.

    (casesplit (and (and (< x y) (integerp a)) (equal r s)) t)
    
    Three new top-level hypotheses are added to the current goal, namely (< x y), (integerp a), and (equal r s). In that case, only one hypothesis is added to create the new goal, namely the negation of (and (< x y) (integerp a) (equal r s)). If the negation of this term had been claimed, then it would be the other way around: the current goal would get a single new hypothesis while the new goal would be created by adding three hypotheses.

    Remark: It is allowed to use abbreviations in the hints.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CG.html0000664002132200015000000000134612222333525021032 0ustar kaufmannacl2 ACL2-PC_colon__colon_CG.html -- ACL2 Version 6.3

    ACL2-PC::CG

    (macro) change to another goal.
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (cg (main . 1)) -- change to the goal (main . 1)
    cg              -- change to the next-to-top goal
    
    General Form:
    (CG &OPTIONAL goal-name)
    
    Same as (change-goal goal-name t), i.e. change to the indicated and move the current goal to the end of the goal stack.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CHANGE-GOAL.html0000664002132200015000000000215112222333525022201 0ustar kaufmannacl2 ACL2-PC_colon__colon_CHANGE-GOAL.html -- ACL2 Version 6.3

    ACL2-PC::CHANGE-GOAL

    (primitive) change to another goal.
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (change-goal (main . 1)) -- change to the goal (main . 1)
    change-goal              -- change to the next-to-top goal
    
    General Form:
    (change-goal &optional goal-name end-flg)
    
    Change to the goal with the name goal-name, i.e. make it the current goal. However, if goal-name is nil or is not supplied, then it defaults to the next-to-top goal, i.e., the second goal in the stack of goals. If end-flg is supplied and not nil, then move the current goal to the end of the goal stack; else merely swap it with the next-to-top goal. Also see documentation for cg.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CL-PROC.html0000664002132200015000000000120312222333525021570 0ustar kaufmannacl2 ACL2-PC_colon__colon_CL-PROC.html -- ACL2 Version 6.3

    ACL2-PC::CL-PROC

    (macro) same as clause-processor
    Major Section:  PROOF-CHECKER-COMMANDS
    

    See the documentation for proof-checker command clause-processor, which is identical to cl-proc.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CLAIM.html0000664002132200015000000000473312222333525021371 0ustar kaufmannacl2 ACL2-PC_colon__colon_CLAIM.html -- ACL2 Version 6.3

    ACL2-PC::CLAIM

    (atomic macro) add a new hypothesis
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (claim (< x y))   -- attempt to prove (< x y) from the current
                         top-level hypotheses and if successful, then
                         add (< x y) as a new top-level hypothesis in
                         the current goal
    (claim (< x y)
           :otf-flg t
           :hints (("Goal" :induct t)))
                      -- as above, but call the prover using the
                         indicated values for the otf-flg and hints
    (claim (< x y) 0) -- as above, except instead of attempting to
                         prove (< x y), create a new subgoal with the
                         same top-level hypotheses as the current goal
                         that has (< x y) as its conclusion
    (claim (< x y) :hints :none)
                      -- same as immediately above
    
    General Form:
    (claim expr &rest rest-args)
    
    This command creates a new subgoal with the same top-level hypotheses as the current goal but with a conclusion of expr. If rest-args is a non-empty list headed by a non-keyword, then there will be no proof attempted for the new subgoal. With that possible exception, rest-args should consist of keyword arguments. The keyword argument :do-not-flatten controls the ``flattening'' of new hypotheses, just as with the casesplit command (as described in its documentation). The remaining rest-args are used with a call the prove command on the new subgoal, except that if :hints is a non-nil atom, then the prover is not called -- rather, this is the same as the situation described above, where rest-args is a non-empty list headed by a non-keyword.

    Remarks: (1) Unlike the casesplit command, the claim command is completely insensitive to governors. (2) It is allowed to use abbreviations in the hints. (3) The keyword :none has the special role as a value of :hints that is shown clearly in an example above.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CLAUSE-PROCESSOR.html0000664002132200015000000000222512222333525023067 0ustar kaufmannacl2 ACL2-PC_colon__colon_CLAUSE-PROCESSOR.html -- ACL2 Version 6.3

    ACL2-PC::CLAUSE-PROCESSOR

    (atomic macro) use a clause-processor
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (cl-proc :function
             note-fact-clause-processor
            :hint
            '(equal a a))
    -- Invoke the indicated clause processor function with the indicated hint
    argument (see the beginning of community book
    books/clause-processors/basic-examples.lisp.
    
    General Form:
    (cl-proc &rest cl-proc-args)
    
    Invoke a clause-processor as indicated by cl-proc-args, which is a list of arguments that can serve as the value of a :clause-processor hint; see hints.

    This command calls the prove command, and hence should only be used at the top of the conclusion.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_COMM.html0000664002132200015000000000530612222333525021274 0ustar kaufmannacl2 ACL2-PC_colon__colon_COMM.html -- ACL2 Version 6.3

    ACL2-PC::COMM

    (macro) display instructions from the current interactive session
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    comm
    (comm 10)
    
    General Form:
    (comm &optional n)
    
    Prints out instructions in reverse order. This is actually the same as (commands n t) -- or, (commands nil t) if n is not supplied. As explained in the documentation for commands, the final argument of t causes suppression of instructions occurring between so-called ``matching bookends,'' which we now explain.

    A ``begin bookend'' is an instruction of the form

    (COMMENT :BEGIN x . y).
    
    Similarly, an ``end bookend'' is an instruction of the form
    (COMMENT :END x' . y').
    
    The ``name'' of the first bookend is x and the ``name'' of the second bookend is x'. When such a pair of instructions occurs in the current state-stack, we call them ``matching bookends'' provided that they have the same name (i.e. x equals x') and if no other begin or end bookend with name x occurs between them. The idea now is that comm hides matching bookends together with the instructions they enclose. Here is a more precise explanation of this ``hiding''; probably there is no value in reading on!

    A comm instruction hides bookends in the following manner. (So does a comment instruction when its second optional argument is supplied and non-nil.) First, if the first argument n is supplied and not nil, then we consider only the last n instructions from the state-stack; otherwise, we consider them all. Now the resulting list of instructions is replaced by the result of applying the following process to each pair of matching bookends: the pair is removed, together with everything in between the begin and end bookend of the pair, and all this is replaced by the ``instruction''

    ("***HIDING***" :COMMENT :BEGIN name ...)
    
    where (comment begin name ...) is the begin bookend of the pair. Finally, after applying this process to each pair of matching bookends, each begin bookend of the form (comment begin name ...) that remains is replaced by
    ("***UNFINISHED***" :COMMENT :BEGIN name ...) .
    




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_COMMANDS.html0000664002132200015000000000244412222333525021742 0ustar kaufmannacl2 ACL2-PC_colon__colon_COMMANDS.html -- ACL2 Version 6.3

    ACL2-PC::COMMANDS

    (macro) display instructions from the current interactive session
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    commands
    (commands 10 t)
    
    General Forms:
    
    commands or (commands nil)
    Print out all the instructions (in the current state-stack) in
    reverse order, i.e. from the most recent instruction to the starting
    instruction.
    
    (commands n) [n a positive integer]
    Print out the most recent n instructions (in the current
    state-stack), in reverse order.
    
    (commands x abbreviate-flag)
    Same as above, but if abbreviate-flag is non-NIL, then do not
    display commands between ``matching bookends''.  See documentation
    for comm for an explanation of matching bookends.
    
    Remark: If there are more than n instructions in the state-stack, then (commands n) is the same as commands (and also, (commands n abb) is the same as (commands nil abb)).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_COMMENT.html0000664002132200015000000000137012222333525021640 0ustar kaufmannacl2 ACL2-PC_colon__colon_COMMENT.html -- ACL2 Version 6.3

    ACL2-PC::COMMENT

    (primitive) insert a comment
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (comment now begin difficult final goal)
    
    General Form:
    (comment &rest x)
    
    This instruction makes no change in the state except to insert the comment instruction.

    Some comments can be used to improve the display of commands; see documentation for comm.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CONTRADICT.html0000664002132200015000000000101012222333525022157 0ustar kaufmannacl2 ACL2-PC_colon__colon_CONTRADICT.html -- ACL2 Version 6.3

    ACL2-PC::CONTRADICT

    (macro) same as contrapose
    Major Section:  PROOF-CHECKER-COMMANDS
    

    see documentation for contrapose




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_CONTRAPOSE.html0000664002132200015000000000233112222333525022211 0ustar kaufmannacl2 ACL2-PC_colon__colon_CONTRAPOSE.html -- ACL2 Version 6.3

    ACL2-PC::CONTRAPOSE

    (primitive) switch a hypothesis with the conclusion, negating both
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (contrapose 3)
    
    General Form:
    (contrapose &optional n)
    
    The (optional) argument n should be a positive integer that does not exceed the number of hypotheses. Negate the current conclusion and make it the nth hypothesis, while negating the current nth hypothesis and making it the current conclusion. If no argument is supplied then the effect is the same as for (contrapose 1).

    Remark: By ``negate'' we mean an operation that replaces nil by t, x by nil for any other explicit value x, (not x) by x, and any other x by (not x).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DEMOTE.html0000664002132200015000000000310012222333525021504 0ustar kaufmannacl2 ACL2-PC_colon__colon_DEMOTE.html -- ACL2 Version 6.3

    ACL2-PC::DEMOTE

    (primitive) move top-level hypotheses to the conclusion
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    demote        -- demote all top-level hypotheses
    (demote 3 5)  -- demote hypotheses 3 and 5
    
    For example, if the top-level hypotheses are x and y and the conclusion is z, then after execution of demote, the conclusion will be (implies (and x y) z) and there will be no (top-level) hypotheses.

    General Form:
    (demote &rest hyps-indices)
    
    Eliminate the indicated (top-level) hypotheses, but replace the conclusion conc with (implies hyps conc) where hyps is the conjunction of the hypotheses that were eliminated. If no arguments are supplied, then all hypotheses are demoted, i.e. demote is the same as (demote 1 2 ... n) where n is the number of top-level hypotheses.

    Remark: You must be at the top of the conclusion in order to use this command. Otherwise, first invoke top. Also, demote fails if there are no top-level hypotheses or if indices are supplied that are out of range.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DIVE.html0000664002132200015000000000363412222333525021272 0ustar kaufmannacl2 ACL2-PC_colon__colon_DIVE.html -- ACL2 Version 6.3

    ACL2-PC::DIVE

    (primitive) move to the indicated subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (DIVE 1)    -- assign the new current subterm to be the first
                   argument of the existing current subterm
    (DIVE 1 2)  -- assign the new current subterm to be the result of
                   first taking the 1st argument of the existing
                   current subterm, and then the 2nd argument of that
    
    For example, if the current subterm is
    (* (+ a b) c),
    
    then after (dive 1) it is
    (+ a b).
    
    If after that, then (dive 2) is invoked, the new current subterm will be
    b.
    
    Instead of (dive 1) followed by (dive 2), the same current subterm could be obtained by instead submitting the single instruction (dive 1 2).

    General Form:
    (dive &rest naturals-list)
    
    If naturals-list is a non-empty list (n_1 ... n_k) of natural numbers, let the new current subterm be the result of selecting the n_1-st argument of the current subterm, and then the n_2-th subterm of that, ..., finally the n_k-th subterm.

    Remark: Dive is related to the command pp, in that the diving is done according to raw (translated, internal form) syntax. Use the command dv if you want to dive according to the syntax displayed by the command p. Note that (dv n) can be abbreviated by simply n.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DO-ALL-NO-PROMPT.html0000664002132200015000000000161512222333525023001 0ustar kaufmannacl2 ACL2-PC_colon__colon_DO-ALL-NO-PROMPT.html -- ACL2 Version 6.3

    ACL2-PC::DO-ALL-NO-PROMPT

    (macro) run the given instructions, halting once there is a ``failure''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (do-all-no-prompt induct p prove)
    
    General Form:
    (do-all-no-prompt &rest instruction-list)
    
    Do-all-no-prompt is the same as do-all, except that the prompt and instruction are not printed each time, regardless of the value of pc-print-prompt-and-instr-flg. Also, restoring is disabled. See the documentation for do-all.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DO-ALL.html0000664002132200015000000000245412222333525021452 0ustar kaufmannacl2 ACL2-PC_colon__colon_DO-ALL.html -- ACL2 Version 6.3

    ACL2-PC::DO-ALL

    (macro) run the given instructions
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (do-all induct p prove)
    
    General Form:
    (do-all &rest instruction-list)
    
    Run the indicated instructions until there is a hard ``failure''. The instruction ``succeeds'' if and only if each instruction in instruction-list does. (See the documentation for sequence for an explanation of ``success'' and ``failure.'') As each instruction is executed, the system will print the usual prompt followed by that instruction, unless the global state variable pc-print-prompt-and-instr-flg is nil.

    Remark: If do-all ``fails'', then the failure is hard if and only if the last instruction it runs has a hard ``failure''.

    Obscure point: For the record, (do-all ins_1 ins_2 ... ins_k) is the same as (sequence (ins_1 ins_2 ... ins_k)).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DO-STRICT.html0000664002132200015000000000156012222333525022047 0ustar kaufmannacl2 ACL2-PC_colon__colon_DO-STRICT.html -- ACL2 Version 6.3

    ACL2-PC::DO-STRICT

    (macro) run the given instructions, halting once there is a ``failure''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (do-strict induct p prove)
    
    General Form:
    (do-strict &rest instruction-list)
    
    Run the indicated instructions until there is a (hard or soft) ``failure''. In fact do-strict is identical in effect to do-all, except that do-all only halts once there is a hard ``failure''. See the documentation for do-all.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DROP.html0000664002132200015000000000176312222333525021310 0ustar kaufmannacl2 ACL2-PC_colon__colon_DROP.html -- ACL2 Version 6.3

    ACL2-PC::DROP

    (primitive) drop top-level hypotheses
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (drop 2 3) -- drop the second and third hypotheses
    drop       -- drop all top-level hypotheses
    
    General Forms:
    (drop n1 n2 ...) -- Drop the hypotheses with the indicated indices.
    
    drop             -- Drop all the top-level hypotheses.
    
    Remark: If there are no top-level hypotheses, then the instruction drop will fail. If any of the indices is out of range, i.e. is not an integer between one and the number of top-level hypotheses (inclusive), then (drop n1 n2 ...) will fail.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_DV.html0000664002132200015000000000537312222333525021056 0ustar kaufmannacl2 ACL2-PC_colon__colon_DV.html -- ACL2 Version 6.3

    ACL2-PC::DV

    (atomic macro) move to the indicated subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (dv 1)    -- assign the new current subterm to be the first argument
                 of the existing current subterm
    (dv 1 2)  -- assign the new current subterm to be the result of
                 first taking the 1st argument of the existing
                 current subterm, and then the 2nd argument of that
    
    For example, if the current subterm is
    (* (+ a b) c),
    
    then after (dv 1) it is
    (+ a b).
    
    If after that, then (dv 2) is invoked, the new current subterm will be
    b.
    
    Instead of (dv 1) followed by (dv 2), the same current subterm could be obtained by instead submitting the single instruction (dv 1 2).

    General Form:
    (dv &rest naturals-list)
    
    If naturals-list is a non-empty list (n_1 ... n_k) of natural numbers, let the new current subterm be the result of selecting the n_1-st argument of the current subterm, and then the n_2-th subterm of that, ..., finally the n_k-th subterm.

    Remark: (dv n) may be abbreviated by simply n, so we could have typed 1 instead of (dv 1) in the first example above.

    Remark: See also dive, which is related to the command pp, in that the diving is done according to raw (translated, internal form) syntax. Use the command dv if you want to dive according to the syntax displayed by the command p. Thus, the command ``up'' is the inverse of dive, not of dv. The following example illustrates this point.

    ACL2 !>(verify (equal (* a b c) x))
    ->: p ; print user-level term
    (EQUAL (* A B C) X)
    ->: pp ; print internal-form (translated) term
    (EQUAL (BINARY-* A (BINARY-* B C)) X)
    ->: exit
    Exiting....
     NIL
    ACL2 !>(verify (equal (* a b c) x))
    ->: p
    (EQUAL (* A B C) X)
    ->: 1 ; same as (dv 1)
    ->: p ; print user-level term
    (* A B C)
    ->: pp ; print internal-form (translated) term
    (BINARY-* A (BINARY-* B C))
    ->: 3 ; dive to third argument of (* A B C)
    ->: p
    C
    ->: up ; go up one level in (BINARY-* A (BINARY-* B C))
    ->: p
    (* B C)
    ->: pp
    (BINARY-* B C)
    ->:
    




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_ELIM.html0000664002132200015000000000143312222333525021264 0ustar kaufmannacl2 ACL2-PC_colon__colon_ELIM.html -- ACL2 Version 6.3

    ACL2-PC::ELIM

    (atomic macro) call the ACL2 theorem prover's elimination process
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    elim
    

    Upon running the elim command, the system will create a subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof, where only elimination is used; not even simplification is used!




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_EQUIV.html0000664002132200015000000000533512222333525021434 0ustar kaufmannacl2 ACL2-PC_colon__colon_EQUIV.html -- ACL2 Version 6.3

    ACL2-PC::EQUIV

    (primitive) attempt an equality (or congruence-based) substitution
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (equiv (* x y) 3) -- replace (* x y) by 3 everywhere inside the
                         current subterm, if their equality is among the
                         top-level hypotheses or the governors
    (equiv x t iff)   -- replace x by t everywhere inside the current
                         subterm, where only propositional equivalence
                         needs to be maintained at each occurrence of x
    
    General form:
    (equiv old new &optional relation)
    
    Substitute new for old everywhere inside the current subterm, provided that either (relation old new) or (relation new old) is among the top-level hypotheses or the governors (possibly by way of backchaining and/or refinement; see below). If relation is nil or is not supplied, then it defaults to equal. See also the command =, which is much more flexible. Note that this command fails if no substitution is actually made.

    Remark: No substitution takes place inside explicit values. So for example, the instruction (equiv 3 x) will cause 3 to be replaced by x if the current subterm is, say, (* 3 y), but not if the current subterm is (* 4 y) even though 4 = (1+ 3).

    The following remarks are quite technical and mostly describe a certain weak form of ``backchaining'' that has been implemented for equiv in order to support the = command. In fact neither the term (relation old new) nor the term (relation new old) needs to be explicitly among the current ``assumptions'', i.e., the top-level hypothesis or the governors. Rather, there need only be such an assumption that ``tells us'' (r old new) or (r new old), for some equivalence relation r that refines relation. Here, ``tells us'' means that either one of the indicated terms is among those assumptions, or else there is an assumption that is an implication whose conclusion is one of the indicated terms and whose hypotheses (gathered up by appropriately flattening the first argument of the implies term) are all among the current assumptions.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_EX.html0000664002132200015000000000127012222333525021051 0ustar kaufmannacl2 ACL2-PC_colon__colon_EX.html -- ACL2 Version 6.3

    ACL2-PC::EX

    (macro) exit after possibly saving the state
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    ex
    

    Same as exit, except that first the instruction save is executed.

    If save queries the user and is answered negatively, then the exit is aborted.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_EXIT.html0000664002132200015000000000731212222333525021311 0ustar kaufmannacl2 ACL2-PC_colon__colon_EXIT.html -- ACL2 Version 6.3

    ACL2-PC::EXIT

    (meta) exit the interactive proof-checker
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    exit                        -- exit the interactive proof-checker
    (exit t)                    -- exit after printing a bogus defthm event
    (exit append-associativity) -- exit and create a defthm
                                   event named append-associativity
    
    General Forms:
    
    exit --  Exit without storing an event.
    
    (exit t) -- Exit after printing a bogus defthm event, showing :INSTRUCTIONS.
    
    (exit event-name &optional rule-classes do-it-flg) --
    Exit, and perhaps store an event
    
    The command exit returns you to the ACL2 loop. At a later time, (verify) may be executed to get back into the same proof-checker state, as long as there hasn't been an intervening use of the proof-checker (otherwise see save).

    When given one or more arguments as shown above, exit still returns you to the ACL2 loop, but first, if the interactive proof is complete, then it attempts create a defthm event with the specified event-name and rule-classes (which defaults to (:rewrite) if not supplied). The event will be printed to the terminal, and then normally the user will be queried whether an event should really be created. However, if the final optional argument do-it-flg is supplied and not nil, then an event will be made without a query.

    For example, the form

    (exit top-pop-elim (:elim :rewrite) t)
    
    causes a defthm event named top-pop-elim to be created with rule-classes (:elim :rewrite), without a query to the user (because of the argument t).

    Remark: it is permitted for event-name to be nil. In that case, the name of the event will be the name supplied during the original call of verify. (See the documentation for verify and commands.) Also in that case, if rule-classes is not supplied then it defaults to the rule-classes supplied in the original call of verify.

    Comments on ``success'' and ``failure''. An exit instruction will always ``fail'', so for example, if it appears as an argument of a do-strict instruction then none of the later (instruction) arguments will be executed. Moreover, the ``failure'' will be ``hard'' if an event is successfully created or if the instruction is simply exit; otherwise it will be ``soft''. See the documentation for sequence for an explanation of hard and soft ``failures''. An obscure but potentially important fact is that if the ``failure'' is hard, then the error signal is a special signal that the top-level interactive loop can interpret as a request to exit. Thus for example, a sequencing command that turns an error triple (mv erp val state) into (mv t val state) would never cause an exit from the interactive loop.

    If the proof is not complete, then (exit event-name ...) will not cause an exit from the interactive loop. However, in that case it will print out the original user-supplied goal (the one that was supplied with the call to verify) and the current list of instructions.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_EXPAND.html0000664002132200015000000000236412222333525021521 0ustar kaufmannacl2 ACL2-PC_colon__colon_EXPAND.html -- ACL2 Version 6.3

    ACL2-PC::EXPAND

    (primitive) expand the current function call without simplification
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    expand -- expand and do not simplify.
    
    For example, if the current subterm is (append a b), then after (expand t) the current subterm will be the term:
    (if (true-listp x)
        (if x
            (cons (car x) (append (cdr x) y))
          y)
      (apply 'binary-append (list x y)))
    
    regardless of the top-level hypotheses and the governors.

    General Form:
    (expand &optional do-not-expand-lambda-flg)
    
    Expand the function call at the current subterm, and do not simplify. The options have the following meanings:
    do-not-expand-lambda-flg:   default is nil; otherwise, the result
                                should be a lambda expression
    
    
    See also x, which allows simplification.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_FAIL.html0000664002132200015000000000171112222333525021250 0ustar kaufmannacl2 ACL2-PC_colon__colon_FAIL.html -- ACL2 Version 6.3

    ACL2-PC::FAIL

    (macro) cause a failure
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    fail
    (fail t)
    
    General Form:
    (fail &optional hard)
    
    This is probably only of interest to writers of macro commands. The only function of fail is to fail to ``succeed''.

    The full story is that fail and (fail nil) simply return (mv nil nil state), while (fail hard) returns (mv hard nil state) if hard is not nil. See also do-strict, do-all, and sequence.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_FINISH.html0000664002132200015000000000206112222333525021514 0ustar kaufmannacl2 ACL2-PC_colon__colon_FINISH.html -- ACL2 Version 6.3

    ACL2-PC::FINISH

    (macro) require completion of instructions; save error if inside :hints
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (finish induct prove bash)
    
    General Form:
    (finish &rest instructions)
    
    Run the indicated instructions, stopping at the first failure. If there is any failure, or if any new goals are created and remain at the end of the indicated instructions, then consider the call of finish to be a failure. See proof-checker-commands and visit the documentation for sequence for a discussion of the notion of ``failure'' for proof-checker commands.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_FORWARDCHAIN.html0000664002132200015000000000270512222333525022410 0ustar kaufmannacl2 ACL2-PC_colon__colon_FORWARDCHAIN.html -- ACL2 Version 6.3

    ACL2-PC::FORWARDCHAIN

    (atomic macro) forward chain from an implication in the hyps
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (forwardchain 2) ; Second hypothesis should be of the form
                     ; (IMPLIES hyp concl), and the result is to replace
                     ; that hypothesis with concl.
    
    
    General Forms:
    (forwardchain hypothesis-number)
    (forwardchain hypothesis-number hints)
    (forwardchain hypothesis-number hints quiet-flg)
    

    This command replaces the hypothesis corresponding to given index, which should be of the form (IMPLIES hyp concl), with its consequent concl. In fact, the given hypothesis is dropped, and the replacement hypothesis will appear as the final hypothesis after this command is executed.

    The prover must be able to prove the indicated hypothesis from the other hypotheses, or else the command will fail. The :hints argument is used in this prover call, and should have the usual syntax of hints to the prover.

    Output is suppressed if quiet-flg is supplied and not nil.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_FREE.html0000664002132200015000000000124112222333525021254 0ustar kaufmannacl2 ACL2-PC_colon__colon_FREE.html -- ACL2 Version 6.3

    ACL2-PC::FREE

    (atomic macro) create a ``free variable''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (free x)
    
    General Form:
    (free var)
    
    Mark var as a ``free variable''. Free variables are only of interest for the put command; see its documentation for an explanation.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_GENEQV.html0000664002132200015000000000363512222333525021531 0ustar kaufmannacl2 ACL2-PC_colon__colon_GENEQV.html -- ACL2 Version 6.3

    ACL2-PC::GENEQV

    (macro) show the generated equivalence relation maintained at the current subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    General Forms:
    geneqv     ; show list of equivalence relations being maintained
    (geneqv t) ; as above, but pair each relation with a justifying rune
    
    
    This is an advanced command, whose effect is to print the so-called ``generated equivalence relation'' (or ``geneqv'') that is maintained at the current subterm of the conclusion. That structure is a list of equivalence relations, representing the transitive closure E of the union of those relations, such that it suffices to maintain E at the current subterm: if that subterm, u, is replaced in the goal's conclusion, G, by another term equivalent to u with respect to E, then the resulting conclusion is Boolean equivalent to G. Also see defcong.

    The command `geneqv' prints the above list of equivalence relations, or more precisely, the list of function symbols for those relations. If however geneqv is given a non-nil argument, then a list is printed whose elements are each of the form (s r), where s is the symbol for an equivalence relation and r is a :congruence rune justifying the inclusion of s in the list of equivalence relations being maintained at the current subterm.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_GENERALIZE.html0000664002132200015000000000431712222333525022167 0ustar kaufmannacl2 ACL2-PC_colon__colon_GENERALIZE.html -- ACL2 Version 6.3

    ACL2-PC::GENERALIZE

    (primitive) perform a generalization
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (generalize
     ((and (true-listp x) (true-listp y)) 0)
     ((append x y) w))
    
    General Form:
    (generalize &rest substitution)
    
    Generalize using the indicated substitution, which should be a non-empty list. Each element of that list should be a two-element list of the form (term variable), where term may use abbreviations. The effect of the instruction is to replace each such term in the current goal by the corresponding variable. This replacement is carried out by a parallel substitution, outside-in in each hypothesis and in the conclusion. More generally, actually, the ``variable'' (second) component of each pair may be nil or a number, which causes the system to generate a new name of the form _ or _n, with n a natural number; more on this below. However, when a variable is supplied, it must not occur in any goal of the current proof-checker state.

    When the ``variable'' above is nil, the system will treat it as the variable |_| if that variable does not occur in any goal of the current proof-checker state. Otherwise it treats it as |_0|, or |_1|, or |_2|, and so on, until one of these is not among the variables of the current proof-checker state. If the ``variable'' is a non-negative integer n, then the system treats it as |_n| unless that variable already occurs among the current goals, in which case it increments n just as above until it obtains a new variable.

    Remark: The same variable may not occur as the variable component of two different arguments (though nil may occur arbitrarily many times, as may a positive integer).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_GOALS.html0000664002132200015000000000137012222333525021403 0ustar kaufmannacl2 ACL2-PC_colon__colon_GOALS.html -- ACL2 Version 6.3

    ACL2-PC::GOALS

    (macro) list the names of goals on the stack
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    goals
    

    Goals lists the names of all goals that remain to be proved. They are listed in the order in which they appear on the stack of remaining goals, which is relevant for example to the effect of a change-goal instruction.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_HELP-LONG.html0000664002132200015000000000121612222333525022022 0ustar kaufmannacl2 ACL2-PC_colon__colon_HELP-LONG.html -- ACL2 Version 6.3

    ACL2-PC::HELP-LONG

    (macro) same as help!
    Major Section:  PROOF-CHECKER-COMMANDS
    

    See the documentation for help!.

    Help-long has been included in addition to help! for historical reasons. (Such a command is included in Pc-Nqthm).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_HELP.html0000664002132200015000000000564612222333525021300 0ustar kaufmannacl2 ACL2-PC_colon__colon_HELP.html -- ACL2 Version 6.3

    ACL2-PC::HELP

    (macro) proof-checker help facility
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    
    (help all)     -- list all proof-checker commands
    (help rewrite) -- partial documentation on the rewrite command; the
                      rest is available using more or more!
    
    (help! rewrite) -- full documentation on the rewrite command
    
    help, help!     -- this documentation (in part, or in totality,
                       respectively)
    
    General Forms:
    (help &optional command)
    (help! &optional command)
    more
    more!
    
    The proof checker supports the same kind of documentation as does ACL2 proper. The main difference is that you need to type
    (help command)
    
    in a list rather than :doc command. So, to get all the documentation on command, type (help! command) inside the interactive loop, but to get only a one-line description of the command together with some examples, type (help command). In the latter case, you can get the rest of the help by typing more!; or type more if you don't necessarily want all the rest of the help at once. (Then keep typing more if you want to keep getting more of the help for that command.)

    An exception is (help all), which prints the documentation topic proof-checker-commands, to show you all possible proof-checker commands. So for example, when you see ACL2-PC::USE in that list, you can then submit (help use) or (help! use) to get documentation for the proof-checker use command.

    But summarizing for other than the case of all: as with ACL2, you can type either of the following:

    more, more!
    -- to obtain more (or, all the rest of) the documentation last
       requested by help (or, outside the proof-checker's loop, :doc)
    
    It has been arranged that the use of (help command) will tell you just about everything you could want to know about command, almost always by way of examples. For more details about a command, use help!, more, or more!.

    We use the word ``command'' to refer to the name itself, e.g. rewrite. We use the word ``instruction'' to refer to an input to the interactive system, e.g. (rewrite foo) or (help split). Of course, we allow commands with no arguments as instructions in many cases, e.g. rewrite. In such cases, command is treated identically to (command).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_HELP_bang_.html0000664002132200015000000000126012222333525022412 0ustar kaufmannacl2 ACL2-PC_colon__colon_HELP_bang_.html -- ACL2 Version 6.3

    ACL2-PC::HELP!

    (macro) proof-checker help facility
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Same as help, except that the entire help message is printed without any need to invoke more! or more.

    Invoke help for documentation about the proof-checker help facility.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_HYPS.html0000664002132200015000000000534312222333525021325 0ustar kaufmannacl2 ACL2-PC_colon__colon_HYPS.html -- ACL2 Version 6.3

    ACL2-PC::HYPS

    (macro) print the hypotheses
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    hyps               -- print all (top-level) hypotheses
    (hyps (1 3) (2 4)) -- print hypotheses 1 and 3 and governors 2 and 4
    (hyps (1 3) t)     -- print hypotheses 1 and 3 and all governors
    
    General Form:
    (hyps &optional hyps-indices govs-indices)
    
    Print the indicated top-level hypotheses and governors. (The notion of ``governors'' is defined below.) Here, hyps-indices and govs-indices should be lists of indices of hypotheses and governors (respectively), except that the atom t may be used to indicate that one wants all hypotheses or governors (respectively).

    The list of ``governors'' is defined as follows. Actually, we define here the notion of the governors for a pair of the form <term, address>]; we're interested in the special case where the term is the conclusion and the address is the current address. If the address is nil, then there are no governors, i.e., the list of governors is nil. If the term is of the form (if x y z) and the address is of the form (2 . rest) or (3 . rest), then the list of governors is the result of consing x or its negation (respectively) onto the list of governors for the pair <y, rest> or the pair <z, rest> (respectively). If the term is of the form (implies x y) and the address is of the form (2 . rest), then the list of governors is the result of consing x onto the list of governors for the pair <y, rest>. Otherwise, the list of governors for the pair <term, (n . rest)> is exactly the list of governors for the pair <argn, rest> where argn is the nth argument of term.

    If all goals have been proved, a message saying so will be printed. (as there will be no current hypotheses or governors!).

    The hyps command never causes an error. It ``succeeds'' (in fact its value is t) if the arguments (when supplied) are appropriate, i.e. either t or lists of indices of hypotheses or governors, respectively. Otherwise it ``fails'' (its value is nil).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_ILLEGAL.html0000664002132200015000000000151312222333525021606 0ustar kaufmannacl2 ACL2-PC_colon__colon_ILLEGAL.html -- ACL2 Version 6.3

    ACL2-PC::ILLEGAL

    (macro) illegal instruction
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (illegal -3)
    
    General Form:
    (illegal instruction)
    
    Probably not of interest to most users; always ``fails'' since it expands to the fail command.

    The illegal command is used mainly in the implementation. For example, the instruction 0 is ``read'' as (illegal 0), since dive expects positive integers.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_IN-THEORY.html0000664002132200015000000000607212222333525022060 0ustar kaufmannacl2 ACL2-PC_colon__colon_IN-THEORY.html -- ACL2 Version 6.3

    ACL2-PC::IN-THEORY

    (primitive) set the current proof-checker theory
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (in-theory
       (union-theories (theory 'minimal-theory) '(true-listp binary-append)))
    
    General Form:
    (in-theory &optional atom-or-theory-expression)
    
    If the argument is not supplied, then this command sets the current proof-checker theory (see below for explanation) to agree with the current ACL2 theory. Otherwise, the argument should be a theory expression, and in that case the proof-checker theory is set to the value of that theory expression.

    The current proof-checker theory is used in all calls to the ACL2 theorem prover and rewriter from inside the proof-checker. Thus, the most recent in-theory instruction in the current state-stack has an effect in the proof-checker totally analogous to the effect caused by an in-theory hint or event in ACL2. All in-theory instructions before the last are ignored, because they refer to the current theory in the ACL2 state, not to the existing proof-checker theory. For example:

       ACL2 !>:trans1 (enable bar)
        (UNION-THEORIES (CURRENT-THEORY :HERE)
                        '(BAR))
       ACL2 !>:trans1 (CURRENT-THEORY :HERE)
        (CURRENT-THEORY-FN :HERE WORLD)
       ACL2 !>
    
    Thus (in-theory (enable bar)) modifies the current theory of the current ACL2 world. So for example, suppose that foo is disabled outside the proof checker and you execute the following instructions, in this order.
       (in-theory (enable foo))
       (in-theory (enable bar))
    
    Then after the second of these, bar will be enabled in the proof-checker, but foo will be disabled. The reason is that (in-theory (enable bar)) instructs the proof-checker to modify the current theory (from outside the proof-checker, not from inside the proof-checker) by enabling bar.

    Note that in-theory instructions in the proof-checker have no effect outside the proof-checker's interactive loop.

    If the most recent in-theory instruction in the current state of the proof-checker has no arguments, or if there is no in-theory instruction in the current state of the proof-checker, then the proof-checker will use the current ACL2 theory. This is true even if the user has interrupted the interactive loop by exiting and changing the global ACL2 theory. However, if the most recent in-theory instruction in the current state of the proof-checker had an argument, then global changes to the current theory will have no effect on the proof-checker state.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_INDUCT.html0000664002132200015000000000256212222333525021530 0ustar kaufmannacl2 ACL2-PC_colon__colon_INDUCT.html -- ACL2 Version 6.3

    ACL2-PC::INDUCT

    (atomic macro) generate subgoals using induction
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    induct, (induct t)
       -- induct according to a heuristically-chosen scheme, creating
          a new subgoal for each base and induction step
    (induct (append (reverse x) y))
       -- as above, but choose an induction scheme based on the term
          (append (reverse x) y) rather than on the current goal
    
    General Form:
    (induct &optional term)
    
    Induct as in the corresponding :induct hint given to the theorem prover, creating new subgoals for the base and induction steps. If term is t or is not supplied, then use the current goal to determine the induction scheme; otherwise, use that term.

    Remark: As usual, abbreviations are allowed in the term.

    Remark: Induct actually calls the prove command with all processes turned off. Thus, you must be at top of the goal for an induct instruction.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_LEMMAS-USED.html0000664002132200015000000000103212222333525022245 0ustar kaufmannacl2 ACL2-PC_colon__colon_LEMMAS-USED.html -- ACL2 Version 6.3

    ACL2-PC::LEMMAS-USED

    (macro) print the runes (definitions, lemmas, ...) used
    Major Section:  PROOF-CHECKER-COMMANDS
    

    This is just an alias for runes.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_LISP.html0000664002132200015000000000446612222333525021316 0ustar kaufmannacl2 ACL2-PC_colon__colon_LISP.html -- ACL2 Version 6.3

    ACL2-PC::LISP

    (meta) evaluate the given form in Lisp
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (lisp (assign xxx 3))
    
    General Form:
    (lisp form)
    
    Evaluate form. The lisp command is mainly of interest for side effects. See also print, skip, and fail.

    The rest of the documentation for lisp is of interest only to those who use it in macro commands. If the Lisp evaluation (by trans-eval) of form returns an error triple (see error-triples) of the form (mv erp ((NIL NIL STATE) . (erp-1 val-1 &)) state), then the lisp command returns the appropriate error triple

    (mv (or erp erp-1)
        val-1
        state) .
    
    Otherwise, the trans-eval of form must return an error triple of the form (mv erp (cons stobjs-out val) &), and the lisp command returns the appropriate error triple
    (mv erp
        val
        state).
    

    Note that the output signature of the form has been lost. The user must know the signature in order to use the output of the lisp command. Trans-eval, which is undocumented except by comments in the ACL2 source code, has replaced, in val, any occurrence of the current state or the current values of stobjs by simple symbols such as REPLACED-STATE. The actual values of these objects may be recovered, in principle, from the state returned and the user-stobj-alist within that state. However, in practice, the stobjs cannot be recovered because the user is denied access to user-stobj-alist. The moral is: do not try to write macro commands that manipulate stobjs. Should the returned val contain REPLACED-STATE the value may simply be ignored and state used, since that is what REPLACED-STATE denotes.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_MORE.html0000664002132200015000000000116112222333525021276 0ustar kaufmannacl2 ACL2-PC_colon__colon_MORE.html -- ACL2 Version 6.3

    ACL2-PC::MORE

    (macro) proof-checker help facility
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Continues documentation of last proof-checker command visited with help.

    Invoke help for documentation about the proof-checker help facility.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_MORE_bang_.html0000664002132200015000000000126012222333525022424 0ustar kaufmannacl2 ACL2-PC_colon__colon_MORE_bang_.html -- ACL2 Version 6.3

    ACL2-PC::MORE!

    (macro) proof-checker help facility
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Continues documentation of last proof-checker command visited with help, until all documentation on that command is printed out.

    Invoke help for documentation about the proof-checker help facility.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_NEGATE.html0000664002132200015000000000146412222333525021505 0ustar kaufmannacl2 ACL2-PC_colon__colon_NEGATE.html -- ACL2 Version 6.3

    ACL2-PC::NEGATE

    (macro) run the given instructions, and ``succeed'' if and only if they ``fail''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example: (negate prove)

    General form:
    (negate &rest instruction-list)
    
    Run the indicated instructions exactly in the sense of do-all, and ``succeed'' if and only if they ``fail''.

    Remark: Negate instructions will never produce hard ``failures''.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_NIL.html0000664002132200015000000000142112222333525021155 0ustar kaufmannacl2 ACL2-PC_colon__colon_NIL.html -- ACL2 Version 6.3

    ACL2-PC::NIL

    (macro) used for interpreting control-d
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General form:
    nil
    
    (or, control-d).

    The whole point of this command is that in some Lisps (including akcl), if you type control-d then it seems, on occasion, to get interpreted as nil. Without this command, one seems to get into an infinite loop.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_NOISE.html0000664002132200015000000000205212222333525021411 0ustar kaufmannacl2 ACL2-PC_colon__colon_NOISE.html -- ACL2 Version 6.3

    ACL2-PC::NOISE

    (meta) run instructions with output
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (noise induct prove)
    
    General Form:
    (noise &rest instruction-list)
    
    Run the instruction-list through the top-level loop with output.

    In fact, having output is the default. Noise is useful inside a surrounding call of quiet, when one temporarily wants output. For example, if one wants to see output for a prove command immediately following an induct command but before an s command, one may want to submit an instruction like (quiet induct (noise prove) s). See also quiet.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_NX.html0000664002132200015000000000210012222333525021053 0ustar kaufmannacl2 ACL2-PC_colon__colon_NX.html -- ACL2 Version 6.3

    ACL2-PC::NX

    (atomic macro) move forward one argument in the enclosing term
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    nx
    
    For example, if the conclusion is (= x (* (- y) z)) and the current subterm is x, then after executing nx, the current subterm will be (* (- y) z).

    This is the same as up followed by (dive n+1), where n is the position of the current subterm in its parent term in the conclusion. Thus in particular, the nx command fails if one is already at the top of the conclusion.

    See also up, dive, top, and bk.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_ORELSE.html0000664002132200015000000000162612222333525021533 0ustar kaufmannacl2 ACL2-PC_colon__colon_ORELSE.html -- ACL2 Version 6.3

    ACL2-PC::ORELSE

    (macro) run the first instruction; if (and only if) it ``fails'', run the second
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (orelse top (print "Couldn't move to the top"))
    
    General form:
    (orelse instr1 instr2)
    
    Run the first instruction. Then if it ``fails'', run the second instruction also; otherwise, stop after the first.

    This instruction ``succeeds'' if and only if either instr1 ``succeeds'', or else instr2 ``succeeds''. If it ``fails'', then the failure is soft.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_P-TOP.html0000664002132200015000000000247212222333525021401 0ustar kaufmannacl2 ACL2-PC_colon__colon_P-TOP.html -- ACL2 Version 6.3

    ACL2-PC::P-TOP

    (macro) prettyprint the conclusion, highlighting the current term
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    p-top
    
    For example, if the conclusion is (equal (and x (p y)) (foo z)) and the current subterm is (p y), then p-top will print (equal (and x (*** (p y) ***)) (foo z)).

    Prettyprint the the conclusion, highlighting the current term. The usual user syntax is used, as with the command p (as opposed to pp). This is illustrated in the example above, where one would *not* see (equal (if x (*** (p y) ***) 'nil) (foo z)).

    Remark (obscure): In some situations, a term of the form (if x t y) occurring inside the current subterm will not print as (or x y), when x isn't a call of a boolean primitive. There's nothing incorrect about this, however.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_P.html0000664002132200015000000000203112222333525020730 0ustar kaufmannacl2 ACL2-PC_colon__colon_P.html -- ACL2 Version 6.3

    ACL2-PC::P

    (macro) prettyprint the current term
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    p
    

    Prettyprint the current term. The usual user syntax is used, so that for example one would see (and x y) rather than (if x y 'nil). (See also pp.) Also, abbreviations are inserted where appropriate; see add-abbreviation.

    The ``current term'' is the entire conclusion unless dive commands have been given, in which case it may be a subterm of the conclusion.

    If all goals have been proved, a message saying so will be printed (as there will be no current term!).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PL.html0000664002132200015000000000172112222333525021051 0ustar kaufmannacl2 ACL2-PC_colon__colon_PL.html -- ACL2 Version 6.3

    ACL2-PC::PL

    (macro) print the rules for a given name
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    pl
    (pl foo)
    
    General Form:
    (pl &optional x)
    
    This command simply invokes the corresponding command of the top-level ACL2 loop; see pl. If no argument is given, or if the argument is nil, then the current subterm should be a call of a function symbol, and the argument is taken to be that symbol.

    If you want information about applying rewrite rules to the current subterm, consider the show-rewrites (or equivalently, sr) command.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PP.html0000664002132200015000000000137712222333525021064 0ustar kaufmannacl2 ACL2-PC_colon__colon_PP.html -- ACL2 Version 6.3

    ACL2-PC::PP

    (macro) prettyprint the current term
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    pp
    

    This is the same as p (see its documentation), except that raw syntax (internal form) is used. So for example, one would see (if x y 'nil) rather than (and x y). Abbreviations are however still inserted, as with p.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PR.html0000664002132200015000000000172112222333525021057 0ustar kaufmannacl2 ACL2-PC_colon__colon_PR.html -- ACL2 Version 6.3

    ACL2-PC::PR

    (macro) print the rules for a given name
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    pr
    (pr foo)
    
    General Form:
    (pr &optional x)
    
    This command simply invokes the corresponding command of the top-level ACL2 loop; see pr. If no argument is given, or if the argument is nil, then the current subterm should be a call of a function symbol, and the argument is taken to be that symbol.

    If you want information about applying rewrite rules to the current subterm, consider the show-rewrites (or equivalently, sr) command.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PRINT-ALL-CONCS.html0000664002132200015000000000127012222333525022702 0ustar kaufmannacl2 ACL2-PC_colon__colon_PRINT-ALL-CONCS.html -- ACL2 Version 6.3

    ACL2-PC::PRINT-ALL-CONCS

    (macro) print all the conclusions of (as yet unproved) goals
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form: print-all-concs

    Prints all the conclusions of goals that remain to be proved, in a pleasant format. See also the proof-checker command print-all-goals.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PRINT-ALL-GOALS.html0000664002132200015000000000123212222333525022700 0ustar kaufmannacl2 ACL2-PC_colon__colon_PRINT-ALL-GOALS.html -- ACL2 Version 6.3

    ACL2-PC::PRINT-ALL-GOALS

    (macro) print all the (as yet unproved) goals
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form: print-all-goals

    Prints all the goals that remain to be proved, in a pleasant format. See also the proof-checker command print-all-concs.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PRINT-MAIN.html0000664002132200015000000000105312222333525022152 0ustar kaufmannacl2 ACL2-PC_colon__colon_PRINT-MAIN.html -- ACL2 Version 6.3

    ACL2-PC::PRINT-MAIN

    (macro) print the original goal
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    print-main
    

    Print the goal as originally entered.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PRINT.html0000664002132200015000000000241212222333525021430 0ustar kaufmannacl2 ACL2-PC_colon__colon_PRINT.html -- ACL2 Version 6.3

    ACL2-PC::PRINT

    (macro) print the result of evaluating the given form
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (print (append '(a b) '(c d)))
    Print the list (a b c d) to the terminal
    
    General Forms:
    (print form)
    (print form t)
    
    Prettyprints the result of evaluating form. The evaluation of form should return a single value that is not state or a single-threaded object (see stobj). The optional second argument causes printing to be done without elision (so-called ``evisceration''; see evisc-tuple).

    If the form you want to evaluate does not satisfy the criterion above, you should create an appropriate call of the lisp command instead. Notice that this command always returns (mv nil nil state) where the second result will always be REPLACED-STATE.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PRO.html0000664002132200015000000000133612222333525021200 0ustar kaufmannacl2 ACL2-PC_colon__colon_PRO.html -- ACL2 Version 6.3

    ACL2-PC::PRO

    (atomic macro) repeatedly apply promote
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    pro
    

    Apply the promote command until there is no change. This command ``succeeds'' exactly when at least one call of promote ``succeeds''. In that case, only a single new proof-checker state will be created.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PROMOTE.html0000664002132200015000000000275312222333525021671 0ustar kaufmannacl2 ACL2-PC_colon__colon_PROMOTE.html -- ACL2 Version 6.3

    ACL2-PC::PROMOTE

    (primitive) move antecedents of conclusion's implies term to top-level hypotheses
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    promote
    (promote t)
    
    For example, if the conclusion is (implies (and x y) z), then after execution of promote, the conclusion will be z and the terms x and y will be new top-level hypotheses.

    General Form:
    (promote &optional do-not-flatten-flag)
    
    Replace conclusion of (implies hyps exp) or (if hyps exp t) with simply exp, adding hyps to the list of top-level hypotheses. Moreover, if hyps is viewed as a conjunction then each conjunct will be added as a separate top-level hypothesis. An exception is that if do-not-flatten-flag is supplied and not nil, then only one top-level hypothesis will be added, namely hyps.

    Remark: You must be at the top of the conclusion in order to use this command. Otherwise, first invoke top.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PROTECT.html0000664002132200015000000000172112222333525021656 0ustar kaufmannacl2 ACL2-PC_colon__colon_PROTECT.html -- ACL2 Version 6.3

    ACL2-PC::PROTECT

    (macro) run the given instructions, reverting to existing state upon failure
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (protect induct p prove)
    
    General Form:
    (protect &rest instruction-list)
    
    Protect is the same as do-strict, except that as soon as an instruction ``fails'', the state-stack reverts to what it was before the protect instruction began, and restore is given the same meaning that it had before the protect instruction began. See the documentation for do-strict.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PROVE.html0000664002132200015000000000326612222333525021437 0ustar kaufmannacl2 ACL2-PC_colon__colon_PROVE.html -- ACL2 Version 6.3

    ACL2-PC::PROVE

    (primitive) call the ACL2 theorem prover to prove the current goal
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    prove -- attempt to prove the current goal
    (prove :otf-flg t
           :hints (("Subgoal 2" :by foo) ("Subgoal 1" :use bar)))
          -- attempt to prove the current goal, with the indicated hints
             and with OTF-FLG set
    
    General Form:
    (prove &rest rest-args)
    
    Attempt to prove the current goal, where rest-args is as in the keyword arguments to defthm except that only :hints and :otf-flg are allowed. The command succeeds exactly when the corresponding defthm would succeed, except that it is all right for some goals to be given ``bye''s. Each goal given a ``bye'' will be turned into a new subgoal. (See hints for an explanation of :by hints.)

    Remark: Use (= t) instead if you are not at the top of the conclusion. Also note that if there are any hypotheses in the current goal, then what is actually attempted is a proof of (implies hyps conc), where hyps is the conjunction of the top-level hypotheses and conc is the goal's conclusion.

    Remark: It is allowed to use abbreviations in the hints.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PSO.html0000664002132200015000000000240212222333525021174 0ustar kaufmannacl2 ACL2-PC_colon__colon_PSO.html -- ACL2 Version 6.3

    ACL2-PC::PSO

    (macro) print the most recent proof attempt from inside the proof-checker
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    pso
    

    Print the most recent proof attempt from inside the proof-checker assuming you are in gag-mode or have saved output (see set-saved-output). This includes all calls to the prover, including for example proof-checker commands induct, split, and bash, in addition to prove. So for example, you can follow (quiet prove) with pso to see the proof, including proof-tree output, if it failed.

    See also documentation for related proof-checker commands psog and pso!.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PSOG.html0000664002132200015000000000243412222333525021310 0ustar kaufmannacl2 ACL2-PC_colon__colon_PSOG.html -- ACL2 Version 6.3

    ACL2-PC::PSOG

    (macro) print the most recent proof attempt from inside the proof-checker
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    psog
    

    Print the most recent proof attempt from inside the proof-checker, including goal names, assuming you are in gag-mode or have saved output (see set-saved-output). This includes all calls to the prover, including for example proof-checker commands induct, split, and bash, in addition to prove. So for example, you can follow (quiet prove) with psog to see the proof, including proof-tree output, if it failed.

    See also documentation for related proof-checker commands pso and pso!.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PSO_bang_.html0000664002132200015000000000250612222333525022327 0ustar kaufmannacl2 ACL2-PC_colon__colon_PSO_bang_.html -- ACL2 Version 6.3

    ACL2-PC::PSO!

    (macro) print the most recent proof attempt from inside the proof-checker
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    pso!
    

    Print the most recent proof attempt from inside the proof-checker, including proof-tree output, assuming you are in gag-mode or have saved output (see set-saved-output). This includes all calls to the prover, including for example proof-checker commands induct, split, and bash, in addition to prove. So for example, you can follow (quiet prove) with pso! to see the proof, including proof-tree output, if it failed.

    See also documentation for related proof-checker commands pso and psog.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_PUT.html0000664002132200015000000000370212222333525021207 0ustar kaufmannacl2 ACL2-PC_colon__colon_PUT.html -- ACL2 Version 6.3

    ACL2-PC::PUT

    (macro) substitute for a ``free variable''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (put x 17)
    
    General Form:
    (put var expr)
    
    Substitute expr for the ``free variable'' var, as explained below.

    A ``free variable'' is, for our purposes, a variable var such that the instruction (free var) has been executed earlier in the state-stack. What (free var) really does is to let var be an abbreviation for the term (hide var) (see documentation for add-abbreviation). What (put var expr) really does is to unwind the state-stack, replacing that free instruction with the instruction (add-abbreviation var expr), so that future references to (? var) become reference to expr rather than to (hide var), and then to replay all the other instructions that were unwound. Because hide was used, the expectation is that in most cases, the instructions will replay successfully and put will ``succeed''. However, if any replayed instruction ``fails'', then the entire replay will abort and ``fail'', and the state-stack will revert to its value before the put instruction was executed.

    If (put var expr) ``succeeds'', then (remove-abbreviation var) will be executed at the end.

    Remark: The restore command will revert the state-stack to its value present before the put instruction was executed.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_QUIET.html0000664002132200015000000000123112222333525021421 0ustar kaufmannacl2 ACL2-PC_colon__colon_QUIET.html -- ACL2 Version 6.3

    ACL2-PC::QUIET

    (meta) run instructions without output
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (quiet induct prove)
    
    General Form:
    (quiet &rest instruction-list)
    
    Run the instruction-list through the top-level loop with no output.

    See also noise.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_R.html0000664002132200015000000000107012222333525020734 0ustar kaufmannacl2 ACL2-PC_colon__colon_R.html -- ACL2 Version 6.3

    ACL2-PC::R

    (macro) same as rewrite
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (r 3)
    
    
    See the documentation for rewrite, as r and rewrite are identical.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REDUCE-BY-INDUCTION.html0000664002132200015000000000271712222333525023355 0ustar kaufmannacl2 ACL2-PC_colon__colon_REDUCE-BY-INDUCTION.html -- ACL2 Version 6.3

    ACL2-PC::REDUCE-BY-INDUCTION

    (macro) call the ACL2 prover without induction, after going into induction
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    reduce-by-induction
      -- attempt to prove the current goal after going into induction,
         with no further inductions
    
    (reduce-by-induction ("Subgoal 2" :by foo) ("Subgoal 1" :use bar))
      -- attempt to prove the current goal after going into induction,
         with no further inductions, using the indicated hints
    
    General Form:
    (reduce-by-induction &rest hints)
    
    A subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof, except that the proof begins with a top-level induction.

    Notice that unlike prove, the arguments to reduce-by-induction are spread out, and are all hints. See also prove, reduce, and bash.

    Remark: Induction and the various processes will be used to the extent that they are ordered explicitly in the :induct and :do-not hints.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REDUCE.html0000664002132200015000000000276312222333525021514 0ustar kaufmannacl2 ACL2-PC_colon__colon_REDUCE.html -- ACL2 Version 6.3

    ACL2-PC::REDUCE

    (atomic macro) call the ACL2 theorem prover's simplifier
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    reduce -- attempt to prove the current goal without using induction
    (reduce ("Subgoal 2" :by foo) ("Subgoal 1" :use bar))
           -- attempt to prove the current goal without using
              induction, with the indicated hints
    
    General Form:
    (reduce &rest hints)
    
    Attempt to prove the current goal without using induction, using the indicated hints (if any). A subgoal will be created for each goal that would have been pushed for proof by induction in an ordinary proof.

    Notice that unlike prove, the arguments to reduce are spread out, and are all hints.

    Reduce is similar to bash in that neither of these allows induction. But bash only allows simplification, while reduce allows processes eliminate-destructors, fertilize, generalize, and eliminate-irrelevance.

    Remark: Induction will be used to the extent that it is ordered explicitly in the hints.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REMOVE-ABBREVIATIONS.html0000664002132200015000000000260012222333525023516 0ustar kaufmannacl2 ACL2-PC_colon__colon_REMOVE-ABBREVIATIONS.html -- ACL2 Version 6.3

    ACL2-PC::REMOVE-ABBREVIATIONS

    (primitive) remove one or more abbreviations
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    remove-abbreviations -- remove all abbreviations
    (remove-abbreviations v w)
                         -- assuming that V and W currently abbreviate
                            terms, then they are ``removed'' in the
                            sense that they are no longer considered to
                            abbreviate those terms
    
    General Forms:
    (remove-abbreviations &rest vars)
    
    If vars is not empty (i.e., not nil), remove the variables in vars from the current list of abbreviations, in the sense that each variable in vars will no longer abbreviate a term.

    Remark: The instruction fails if at least one of the arguments fails to be a variable that abbreviates a term.

    See also the documentation for add-abbreviation, which contains a discussion of abbreviations in general, and show-abbreviations.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REPEAT-REC.html0000664002132200015000000000100612222333525022121 0ustar kaufmannacl2 ACL2-PC_colon__colon_REPEAT-REC.html -- ACL2 Version 6.3

    ACL2-PC::REPEAT-REC

    (macro) auxiliary to repeat
    Major Section:  PROOF-CHECKER-COMMANDS
    

    See documentation for repeat.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REPEAT.html0000664002132200015000000000146712222333525021525 0ustar kaufmannacl2 ACL2-PC_colon__colon_REPEAT.html -- ACL2 Version 6.3

    ACL2-PC::REPEAT

    (macro) repeat the given instruction until it ``fails''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (repeat promote)
    
    General Form:
    (repeat instruction)
    
    The given instruction is run repeatedly until it ``fails''.

    Remark: There is nothing here in general to prevent the instruction from being run after all goals have been proved, though this is indeed the case for primitive instructions.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REPLAY.html0000664002132200015000000000300212222333525021524 0ustar kaufmannacl2 ACL2-PC_colon__colon_REPLAY.html -- ACL2 Version 6.3

    ACL2-PC::REPLAY

    (macro) replay one or more instructions
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    REPLAY     -- replay all instructions in the current session
                  (i.e., state-stack)
    (REPLAY 5) -- replay the most recent 5 instructions
    (REPLAY 5
            (COMMENT deleted dive command here))
               -- replace the 5th most recent instruction with the
                  indicated comment instruction, and then replay it
                  followed by the remaining 4 instructions
    
    General Form:
    (REPLAY &OPTIONAL n replacement-instruction)
    
    Replay the last n instructions if n is a positive integer; else n should be nil or not supplied, and replay all instructions. However, if replacement-instruction is supplied and not nil, then before the replay, replace the nth instruction (from the most recent, as shown by commands) with replacement-instruction.

    If this command ``fails'', then the restore command will revert the state-stack to its value present before the replay instruction was executed.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_RESTORE.html0000664002132200015000000000231112222333525021655 0ustar kaufmannacl2 ACL2-PC_colon__colon_RESTORE.html -- ACL2 Version 6.3

    ACL2-PC::RESTORE

    (meta) remove the effect of an UNDO command
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    restore
    

    Restore removes the effect of an undo command. This always works as expected if restore is invoked immediately after undo, without intervening instructions. However, other commands may also interact with restore, notably ``sequencing'' commands such as do-all, do-strict, protect, and more generally, sequence.

    Remark: Another way to control the saving of proof-checker state is with the save command; see the documentation for save.

    The restore command always ``succeeds''; it returns (mv nil t state).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_RETAIN.html0000664002132200015000000000156312222333525021524 0ustar kaufmannacl2 ACL2-PC_colon__colon_RETAIN.html -- ACL2 Version 6.3

    ACL2-PC::RETAIN

    (atomic macro) drop all but the indicated top-level hypotheses
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (RETAIN 2 3) -- keep the second and third hypotheses, and drop
                    the rest
    
    General Form:
    (retain &rest args)
    
    Drop all top-level hypotheses except those with the indicated indices.

    There must be at least one argument, and all must be in range (i.e. integers between one and the number of top-level hypotheses, inclusive).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_RETRIEVE.html0000664002132200015000000000227612222333525021771 0ustar kaufmannacl2 ACL2-PC_colon__colon_RETRIEVE.html -- ACL2 Version 6.3

    ACL2-PC::RETRIEVE

    (macro) re-enter the proof-checker
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (retrieve associativity-of-permutationp)
    retrieve
    
    General Form:
    (retrieve &optional name)
    
    Must be used from outside the interactive proof-checker loop. If name is supplied and not nil, this causes re-entry to the interactive proof-checker loop in the state at which save was last executed for the indicated name. (See documentation for save.) If name is nil or is not supplied, then the user is queried regarding which proof-checker state to re-enter. The query is omitted, however, if there only one proof-checker state is present that was saved with save, in which case that is the one that is used. See also unsave.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_REWRITE.html0000664002132200015000000001507112222333525021662 0ustar kaufmannacl2 ACL2-PC_colon__colon_REWRITE.html -- ACL2 Version 6.3

    ACL2-PC::REWRITE

    (primitive) apply a rewrite rule
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (rewrite reverse-reverse)
       -- apply the rewrite rule `reverse-reverse'
    (rewrite (:rewrite reverse-reverse))
       -- same as above
    (rewrite 2)
       -- apply the second rewrite rule, as displayed by show-rewrites
    rewrite
       -- apply the first rewrite rule, as displayed by show-rewrites
    (rewrite transitivity-of-< ((y 7)))
       -- apply the rewrite rule transitivity-of-< with the substitution
          that associates 7 to the ``free variable'' y
    (rewrite foo ((x 2) (y 3)) t)
       -- apply the rewrite rule foo by substituting 2 and 3 for free
          variables x and y, respectively, and also binding all other
          free variables possible by using the current context
          (hypotheses and governors)
    
    General Form:
    (rewrite &optional rule-id substitution instantiate-free)
    
    Replace the current subterm with a new term by applying a rewrite or definition rule. The replacement will be done according to the information provided by the show-rewrites (sr) command.

    See also the linear command, for an analogous command to use with linear rules.

    If rule-id is a positive integer n, then the nth rule as displayed by show-rewrites is the one that is applied. If rule-id is nil or is not supplied, then it is treated as the number 1. Otherwise, rule-id should be either a symbol or else a :rewrite or :definition rune. If a symbol is supplied, then any (:rewrite or :definition) rule of that name may be used. We say more about this, and describe the other optional arguments, below.

    Consider first the following example. Suppose that the current subterm is (reverse (reverse y)) and that there is a rewrite rule called reverse-reverse of the form

    (implies (true-listp x)
             (equal (reverse (reverse x)) x)) .
    
    Then the instruction (rewrite reverse-reverse) causes the current subterm to be replaced by y and creates a new goal with conclusion (true-listp y). An exception is that if the top-level hypotheses imply (true-listp y) using only ``trivial reasoning'' (more on this below), then no new goal is created.

    If the rule-id argument is a number or is not supplied, then the system will store an instruction of the form (rewrite name ...), where name is the name of a rewrite rule; this is in order to make it easier to replay instructions when there have been changes to the history. Except: instead of the name (whether the name is supplied or calculated), the system stores the rune if there is any chance of ambiguity. (Formally, ``ambiguity'' here means that the rune being applied is of the form (:rewrite name . index), where index is not nil.)

    Speaking in general, then, a rewrite instruction works as follows:

    First, a rewrite or definition rule is selected according to the arguments of the rewrite instruction. The selection is made as explained under ``General Form'' above.

    Next, the left-hand side of the rule is matched with the current subterm, i.e., a substitution unify-subst is found such that if one instantiates the left-hand side of the rule with unify-subst, then one obtains the current subterm. If this match fails, then the instruction fails.

    Next, an attempt is made to relieve (discharge) the hypotheses, much as the theorem prover relieves hypotheses except that there is no call to the rewriter. First, the substitution unify-subst is extended with the substitution argument, which may bind free variables (see free-variables). Each hypothesis of the rule is then considered in turn, from first to last. For each hypothesis, first the current substitution is applied, and then the system checks whether the hypothesis is ``clearly'' true in the current context. If there are variables in the hypotheses of the rule that are not bound by the current substitution, then a weak attempt is made to extend that substitution so that the hypothesis is present in the current context (see the documentation for the proof-checker hyps command under proof-checker-commands), much as would be done by the theorem prover's rewriter.

    If in the process above there are free variables (see free-variables), but the proof-checker can see how to bind them to relieve all hypotheses, then it will do so in both the show-rewrites (sr) and rewrite commands. But normally, if even one hypothesis remains unrelieved, then no automatic extension of the substitution is made. Except, if instantiate-free is not nil, then that extension to the substitution is kept. (Technical note: in the case of an unrelieved hypothesis and a non-nil value of instantiate-free, if a bind-free hypothesis produces a list of binding alists, then the last of those alists is the one that is used to extend the substitution.)

    Finally, the instruction is applied as follows. The current subterm is replaced by applying the final substitution described above to the right-hand side of the selected rule. And, one new subgoal is created for each unrelieved hypothesis of the rule, whose top-level hypotheses are the governors and top-level hypotheses of the current goal and whose conclusion and current subterm are the instance, by that same final substitution, of that unrelieved hypothesis.

    Remark: The substitution argument should be a list whose elements have the form (variable term), where term may contain abbreviations.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_RUN-INSTR-ON-GOAL.html0000664002132200015000000000100312222333525023122 0ustar kaufmannacl2 ACL2-PC_colon__colon_RUN-INSTR-ON-GOAL.html -- ACL2 Version 6.3

    ACL2-PC::RUN-INSTR-ON-GOAL

    (macro) auxiliary to THEN
    Major Section:  PROOF-CHECKER-COMMANDS
    

    See documentation for then.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_RUN-INSTR-ON-NEW-GOALS.html0000664002132200015000000000103212222333525023676 0ustar kaufmannacl2 ACL2-PC_colon__colon_RUN-INSTR-ON-NEW-GOALS.html -- ACL2 Version 6.3

    ACL2-PC::RUN-INSTR-ON-NEW-GOALS

    (macro) auxiliary to then
    Major Section:  PROOF-CHECKER-COMMANDS
    

    See documentation for then.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_RUNES.html0000664002132200015000000000216712222333525021437 0ustar kaufmannacl2 ACL2-PC_colon__colon_RUNES.html -- ACL2 Version 6.3

    ACL2-PC::RUNES

    (macro) print the runes (definitions, lemmas, ...) used
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples and general forms:
    (runes t)   ; print all runes used during this interactive proof
    (runes nil) ; print all runes used by the most recent command
    (runes)     ; same as (runes nil)
    runes       ; same as (runes nil)
    

    This command does not change the proof-checker state. Rather, it simply reports runes (see rune) that have participated in the interactive proof.

    Note that (runes nil) will show the runes used by the most recent primitive or macro command (as displayed by :comm).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_S-PROP.html0000664002132200015000000000167212222333525021523 0ustar kaufmannacl2 ACL2-PC_colon__colon_S-PROP.html -- ACL2 Version 6.3

    ACL2-PC::S-PROP

    (atomic macro) simplify propositionally
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    s-prop
    
    General Form:
    (s-prop &rest names)
    
    Simplify, using the default settings for s (which include if-normalization and rewriting without real backchaining), but with respect to a theory in which only basic functions and rules (the ones in (theory 'minimal-theory)), together with the names (or parenthesized names) in the &rest argument names, are enabled.

    See also the documentation for s.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_S.html0000664002132200015000000000602312222333525020740 0ustar kaufmannacl2 ACL2-PC_colon__colon_S.html -- ACL2 Version 6.3

    ACL2-PC::S

    (primitive) simplify the current subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    S  -- simplify the current subterm
    (S :backchain-limit 2 :normalize t :expand (append x z))
       -- simplify the current subterm, but during the rewriting
          process first ``normalize'' it by pushing IFs to the
          top-level, and also force the term (append x z) to be
          expanded during the rewriting process
    
    General Form:
    (s &key rewrite normalize backchain-limit repeat in-theory hands-off
            expand)
    
    Simplify the current subterm according to the keyword parameters supplied. First if-normalization is applied (unless the normalize argument is nil), i.e., each subterm of the form (f ... (if test x y) ...) is replaced by the term (if test (f ... x ...) (f ... y ...)) except, of course, when f is if and the indicated if subterm is in the second or third argument position. Then rewriting is applied (unless the rewrite argument is nil). Finally this pair of actions is repeated -- until the rewriting step causes no change in the term. A description of each parameter follows.
    :rewrite -- default t
    
    When non-nil, instructs the system to use ACL2's rewriter (or, something close to it) during simplification.
    :normalize -- default t
    
    When non-nil, instructs the system to use if-normalization (as described above) during simplification.
    :backchain-limit -- default 0
    
    Sets the number of recursive calls to the rewriter that are allowed for backchaining. Even with the default of 0, some reasoning is allowed (technically speaking, type-set reasoning is allowed) in the relieving of hypotheses. The value should be nil or a non-negative integer, and limits backchaining only for rewriting, not for type-set reasoning.
    :repeat -- default 0
    
    Sets the number of times the current term is to be rewritten. If this value is t, then the default is used (as specified by the constant *default-s-repeat-limit*).
    :in-theory, :hands-off, :expand
    
    These have their usual meaning; see hints.

    Remark: if conditional rewrite rules are used that cause case splits because of the use of force, then appropriate new subgoals will be created, i.e., with the same current subterm (and address) but with each new (forced) hypothesis being negated and then used to create a corresponding new subgoal. In that case, the current goal will have all such new hypotheses added to the list of top-level hypotheses.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SAVE.html0000664002132200015000000000243112222333525021273 0ustar kaufmannacl2 ACL2-PC_colon__colon_SAVE.html -- ACL2 Version 6.3

    ACL2-PC::SAVE

    (macro) save the proof-checker state (state-stack)
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (save lemma3-attempt)
    
    General Form:
    (save &optional name do-it-flg)
    
    Saves the current proof-checker state by ``associating'' it with the given name. Submit (retrieve name) to Lisp to get back to this proof-checker state. If verify was originally supplied with an event name, then the argument can be omitted in favor of that name as the default.

    Remark that if a save has already been done with the indicated name (or the default event name), then the user will be queried regarding whether to go ahead with the save -- except, if do-it-flg is supplied and not nil, then there will be no query and the save will be effected.

    See also the documentation for retrieve and unsave.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SEQUENCE.html0000664002132200015000000001161312222333525021747 0ustar kaufmannacl2 ACL2-PC_colon__colon_SEQUENCE.html -- ACL2 Version 6.3

    ACL2-PC::SEQUENCE

    (meta) run the given list of instructions according to a multitude of options
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (sequence (induct p prove) t)
    
    See also the definitions of commands do-all, do-strict, protect, and succeed.

    General Form:
    (sequence
     instruction-list
     &optional
     strict-flg protect-flg success-expr no-prompt-flg no-restore-flg)
    
    Each instruction in the list instruction-list is run, and the instruction ``succeeds'' if every instruction in instruction-list ``succeeds''. However, it might ``succeed'' even if some instructions in the list ``fail''; more generally, the various arguments control a number of aspects of the running of the instructions. All this is explained in the paragraphs below. First we embark on a general discussion of the instruction interpreter, including the notions of ``succeed'' and ``fail''.

    Remark: The arguments are not evaluated, except (in a sense) for success-expr, as described below.

    Each primitive and meta instruction can be thought of as returning an error triple, say (erp val state); see error-triples. An instruction (primitive or meta) ``succeeds'' if erp is nil and val is not nil; otherwise it ``fails''. (When we use the words ``succeed'' or ``fail'' in this technical sense, we'll always include them in double quotes.) If an instruction ``fails,'' we say that that the failure is ``soft'' if erp is nil; otherwise the failure is ``hard''. The sequence command gives the user control over how to treat ``success'' and ``failure'' when sequencing instructions, though we have created a number of handy macro commands for this purpose, notably do-all, do-strict and protect.

    Here is precisely what happens when a sequence instruction is run. The instruction interpreter is run on the instructions supplied in the argument instruction-list (in order). The interpreter halts the first time there is a hard ``failure.'' except that if strict-flg is supplied and not nil, then the interpreter halts the first time there is any ``failure.'' The error triple (erp val state) returned by the sequence instruction is the triple returned by the last instruction executed (or, the triple (nil t state) if instruction-list is nil), except for the following provision. If success-expr is supplied and not nil, then it is evaluated with the state global variables pc-erp and pc-val (in the "ACL2" package) bound to the corresponding components of the error triple returned (as described above). At least two values should be returned, and the first two of these will be substituted for erp and val in the triple finally returned by sequence. For example, if success-expr is (mv erp val), then no change will be made to the error triple, and if instead it is (mv nil t), then the sequence instruction will ``succeed''.

    That concludes the description of the error triple returned by a sequence instruction, but it remains to explain the effects of the arguments protect-flg and no-prompt-flg.

    If protect-flg is supplied and not nil and if also the instruction ``fails'' (i.e., the error component of the triple is not nil or the value component is nil), then the state is reverted so that the proof-checker's state (including the behavior of restore) is set back to what it was before the sequence instruction was executed. Otherwise, unless no-restore-flg is set, the state is changed so that the restore command will now undo the effect of this sequence instruction (even if there were nested calls to sequence).

    Finally, as each instruction in instruction-list is executed, the prompt and that instruction will be printed, unless the global state variable pc-print-prompt-and-instr-flg is unbound or nil and the parameter no-prompt-flg is supplied and not nil.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SHOW-ABBREVIATIONS.html0000664002132200015000000000302112222333525023277 0ustar kaufmannacl2 ACL2-PC_colon__colon_SHOW-ABBREVIATIONS.html -- ACL2 Version 6.3

    ACL2-PC::SHOW-ABBREVIATIONS

    (macro) display the current abbreviations
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (show-abbreviations v w)
       -- assuming that v and w currently abbreviate terms,
          then this instruction displays them together with
          the terms they abbreviate
    show-abbreviations
       -- display all abbreviations
    
    See also add-abbreviation and remove-abbreviations. In particular, the documentation for add-abbreviation contains a general discussion of abbreviations.

    General Form:
    (show-abbreviations &rest vars)
    
    Display each argument in vars together with the term it abbreviates (if any). If there are no arguments, i.e. the instruction is simply show-abbreviations, then display all abbreviations together with the terms they abbreviate.

    If the term abbreviated by a variable, say v, contains a proper subterm that is also abbreviate by (another) variable, then both the unabbreviated term and the abbreviated term (but not using (? v) to abbreviate the term) are displayed with together with v.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SHOW-LINEARS.html0000664002132200015000000000207412222333525022413 0ustar kaufmannacl2 ACL2-PC_colon__colon_SHOW-LINEARS.html -- ACL2 Version 6.3

    ACL2-PC::SHOW-LINEARS

    (macro) display the applicable linear rules
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    show-linears
    
    General Form:
    (show-linears &optional rule-id enabled-only-flg)
    
    This command displays linear rules with a trigger term that matches the current subterm, and shows how they can be applied. This command is analogous to the show-rewrites proof-checker command; see its documentation for details. Also see the documentation for proof-checker command apply-linear for how to apply linear rules.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SHOW-REWRITES.html0000664002132200015000000000410112222333526022554 0ustar kaufmannacl2 ACL2-PC_colon__colon_SHOW-REWRITES.html -- ACL2 Version 6.3

    ACL2-PC::SHOW-REWRITES

    (macro) display the applicable rewrite rules
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    show-rewrites
    
    General Form:
    (show-rewrites &optional rule-id enabled-only-flg)
    
    This command displays rewrite rules whose left-hand side matches the current subterm, and shows how that command can be applied. For each rule displayed, hypotheses are shown that would need to be proved after the rule is applied. Note that hypotheses are omitted from the display when the system can trivially verify that they hold; to see all hypotheses for each rule in a display that is independent of the arguments of the current subterm, use the pl or pr command.

    Here are details on the arguments and the output. If rule-id is supplied and is a name (non-nil symbol) or a :rewrite or :definition rune, then only the corresponding rewrite rule(s) will be displayed, while if rule-id is a positive integer n, then only the nth rule that would be in the list is displayed. In each case, the display will point out when a rule is currently disabled (in the interactive environment), except that if enabled-only-flg is supplied and not nil, then disabled rules will not be displayed at all. Finally, among the free variables of any rule (see free-variables), those that would remain free if the rule were applied will be displayed. Also see rewrite.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SHOW-TYPE-PRESCRIPTIONS.html0000664002132200015000000000233612222333526024143 0ustar kaufmannacl2 ACL2-PC_colon__colon_SHOW-TYPE-PRESCRIPTIONS.html -- ACL2 Version 6.3

    ACL2-PC::SHOW-TYPE-PRESCRIPTIONS

    (macro) display the applicable type-prescription rules
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    show-type-prescriptions
    
    General Form:
    (show-type-prescriptions &optional rule-id)
    
    Display type-prescription rules that apply to the current subterm. If rule-id is supplied and is a name (non-nil symbol) or a :rewrite or :definition rune, then only the corresponding rewrite rule(s) will be displayed. In each case, the display will point out when a rule is currently disabled (in the interactive environment). Also see type-prescription.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SKIP.html0000664002132200015000000000113012222333526021277 0ustar kaufmannacl2 ACL2-PC_colon__colon_SKIP.html -- ACL2 Version 6.3

    ACL2-PC::SKIP

    (macro) ``succeed'' without doing anything
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    skip
    

    Make no change in the state-stack, but ``succeed''. Same as (sequence nil).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SL.html0000664002132200015000000000204312222333526021053 0ustar kaufmannacl2 ACL2-PC_colon__colon_SL.html -- ACL2 Version 6.3

    ACL2-PC::SL

    (atomic macro) simplify with lemmas
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    sl
    (sl 3)
    
    General Form:
    (sl &optional backchain-limit)
    
    Simplify, but with all function definitions disabled (see function-theory in the top-level ACL2 loop), except for a few basic functions (the ones in (theory 'minimal-theory)). The backchain-limit has a default of 0, but if is supplied and not nil, then it should be a nonnegative integer; see the documentation for s.

    WARNING: This command completely ignores in-theory commands that are executed inside the proof-checker.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SLS.html0000664002132200015000000000155512222333526021205 0ustar kaufmannacl2 ACL2-PC_colon__colon_SLS.html -- ACL2 Version 6.3

    ACL2-PC::SLS

    (macro) same as SHOW-LINEARS
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    srs
    
    General Form:
    (srs &optional rule-id enabled-only-flg)
    
    See the documentation for show-linears, as sls and show-linears are identical. NOTE: In analogy to the sr abbreviation for show-rewrites, one might expect this command to be sl; but that name was taken (``simplify with lemmas'') before sls was implemented.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SPLIT.html0000664002132200015000000000352312222333526021434 0ustar kaufmannacl2 ACL2-PC_colon__colon_SPLIT.html -- ACL2 Version 6.3

    ACL2-PC::SPLIT

    (atomic macro) split the current goal into cases
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    split
    
    For example, if the current goal has one hypothesis (or x y) and a conclusion of (and a b), then split will create four new goals:
    one with hypothesis X and conclusion A
    one with hypothesis X and conclusion B
    one with hypothesis Y and conclusion A
    one with hypothesis Y and conclusion B.
    
    General Form:
    SPLIT
    
    Replace the current goal by subgoals whose conjunction is equivalent (primarily by propositional reasoning) to the original goal, where each such goal cannot be similarly split.

    Remark: The new goals will all have their hypotheses promoted; in particular, no conclusion will have a top function symbol of implies. Also note that split will fail if there is exactly one new goal created and it is the same as the existing current goal.

    The way split really works is to call the ACL2 theorem prover with only simplification (and preprocessing) turned on, and with only a few built-in functions (especially, propositional ones) enabled, namely, the ones in the list (theory 'minimal-theory). However, because the prover is called, type-set reasoning can be used to eliminate some cases. For example, if (true-listp x) is in the hypotheses, then probably (true-listp (cdr x)) will be reduced to t.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SR.html0000664002132200015000000000120412222333526021057 0ustar kaufmannacl2 ACL2-PC_colon__colon_SR.html -- ACL2 Version 6.3

    ACL2-PC::SR

    (macro) same as SHOW-REWRITES
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    sr
    
    General Form:
    (sr &optional rule-id enabled-only-flg)
    
    See the documentation for show-rewrites, as sr and show-rewrites are identical.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_ST.html0000664002132200015000000000122112222333526021060 0ustar kaufmannacl2 ACL2-PC_colon__colon_ST.html -- ACL2 Version 6.3

    ACL2-PC::ST

    (macro) same as SHOW-TYPE-PRESCRIPTIONS
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    sr
    
    General Form:
    (st &optional rule-id)
    
    See the documentation for show-type-prescriptions, as st and show-type-prescriptions are identical.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_SUCCEED.html0000664002132200015000000000136512222333526021616 0ustar kaufmannacl2 ACL2-PC_colon__colon_SUCCEED.html -- ACL2 Version 6.3

    ACL2-PC::SUCCEED

    (macro) run the given instructions, and ``succeed''
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (succeed induct p prove)
    
    General Form:
    (succeed &rest instruction-list)
    
    Run the indicated instructions until there is a hard ``failure'', and ``succeed''. (See the documentation for sequence for an explanation of ``success'' and ``failure''.)




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_TH.html0000664002132200015000000000236112222333526021053 0ustar kaufmannacl2 ACL2-PC_colon__colon_TH.html -- ACL2 Version 6.3

    ACL2-PC::TH

    (macro) print the top-level hypotheses and the current subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    th               -- print all (top-level) hypotheses and the current
                        subterm
    (th (1 3) (2 4)) -- print hypotheses 1 and 3 and governors 2 and 4,
                        and the current subterm
    (th (1 3) t)     -- print hypotheses 1 and 3 and all governors, and
                        the current subterm
    
    General Form:
    (th &optional hyps-indices govs-indices)
    
    Print hypotheses and the current subterm. The printing of hypotheses (and perhaps governors) are controlled as in the hyps command; see its documentation.

    Historical note: The name th is adapted from the Gypsy Verification Environment, where th abbreviates the command theorem, which says to print information on the current goal.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_THEN.html0000664002132200015000000000177012222333526021301 0ustar kaufmannacl2 ACL2-PC_colon__colon_THEN.html -- ACL2 Version 6.3

    ACL2-PC::THEN

    (macro) apply one instruction to current goal and another to new subgoals
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (then induct prove)
    
    General Form:
    (then first-instruction &optional completion must-succeed-flg)
    
    Run first-instruction, and then run completion (another instruction) on each subgoal created by first-instruction. If must-succeed-flg is supplied and not nil, then halt at the first ``failure'' and remove the effects of the invocation of completion that ``failed''.

    The default for completion is reduce.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_TOP.html0000664002132200015000000000211512222333526021177 0ustar kaufmannacl2 ACL2-PC_colon__colon_TOP.html -- ACL2 Version 6.3

    ACL2-PC::TOP

    (atomic macro) move to the top of the goal
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example and General Form:
    top
    
    For example, if the conclusion is (= x (* (- y) z)) and the current subterm is y, then after executing top, the current subterm will be the same as the conclusion, i.e., (= x (* (- y) z)).

    Top is the same as (up n), where n is the number of times one needs to execute up in order to get to the top of the conclusion. The top command fails if one is already at the top of the conclusion.

    See also up, dive, nx, and bk.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_TYPE-ALIST.html0000664002132200015000000000653412222333526022201 0ustar kaufmannacl2 ACL2-PC_colon__colon_TYPE-ALIST.html -- ACL2 Version 6.3

    ACL2-PC::TYPE-ALIST

    (macro) display the type-alist from the current context
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (type-alist t t)     ; display type-alist based on conclusion and governors
    (type-alist t t t)   ; as above, but also display forward-chaining report
    type-alist           ; same as (type-alist nil t) -- governors only
    (type-alist nil)     ; same as (type-alist nil t) -- governors only
    (type-alist t)       ; same as (type-alist t nil) -- conclusion only
    (type-alist nil nil) ; display type-alist without considering
                         ; conclusion or governors
    
    General Form:
    (type-alist &optional concl-flg govs-flg fc-report-flg)
    
    where if govs-flg is omitted then it defaults to (not concl-flg), and concl-flg and fc-report-flg default to nil.

    Display the current assumptions as a type-alist. Note that this display includes the result of forward chaining. When fc-report-flg is supplied a non-nil value, the display also includes a forward-chaining report; otherwise,the presence or absence of such a report is controlled by the usual global settings (see forward-chaining-reports).

    There are two basic reasons contemplated for using this command.

    1. The theorem prover has failed (either outside the proof-checker or using a proof-checker command such as bash or reduce and you want to debug by getting an idea of what the prover knows about the context.

    a. You really are interested in the context for the current term. Include hypotheses and governors (i.e., accounting for tests of surrounding if-expressions that must be true or false) but not the current conclusion (which the theorem prover's heuristics would generally ignore for contextual information). Command:
    (type-alist nil t) ; equivalently, type-alist or (type-alist nil)

    b. You are not thinking in particular about the current term; you just want to get an idea of the context that the prover would build at the top-level, for forward-chaining. Incorporate the conclusion but not the governors. Command:
    (type-alist t nil) ; equivalently, (type-alist t)

    2. You intend to use one of the proof-checker-commands that does simplification, such as s or x, and you want to see the context. Then include the surrounding if-term governors but not the goal's conclusion. Command:
    (type-alist nil t) ; equivalently, type-alist or (type-alist nil)

    See type-set (also see type-prescription) for information about ACL2's type system, which can assist in understanding the output of the type-alist command.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_UNDO.html0000664002132200015000000000232312222333526021303 0ustar kaufmannacl2 ACL2-PC_colon__colon_UNDO.html -- ACL2 Version 6.3

    ACL2-PC::UNDO

    (meta) undo some instructions
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    (undo 7)
    undo
    
    General Forms:
    
    (undo n) -- Undo the last n instructions.  The argument n should be
                a positive integer.
    
    undo     -- Same as (undo 1).
    
    Remark: To remove the effect of an undo command, use restore. See the documentation for details.

    Remark: If the argument n is greater than the total number of interactive instructions in the current session, then (undo n) will simply take you back to the start of the session.

    The undo meta command always ``succeeds''; it returns (mv nil t state) unless its optional argument is supplied and of the wrong type (i.e. not a positive integer) or there are no instructions to undo.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_UNSAVE.html0000664002132200015000000000211712222333526021540 0ustar kaufmannacl2 ACL2-PC_colon__colon_UNSAVE.html -- ACL2 Version 6.3

    ACL2-PC::UNSAVE

    (macro) remove a proof-checker state
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (unsave assoc-of-append)
    
    General Form:
    (unsave &optional name)
    
    Eliminates the association of a proof-checker state with name, if name is supplied and not nil. The name may be nil or not supplied, in which case it defaults to the event name supplied with the original call to verify (if there is one -- otherwise, the instruction ``fails'' and there is no change). The ACL2 function unsave may also be executed outside the interactive loop, with the same syntax.

    See also documentation for save and retrieve.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_UP.html0000664002132200015000000000226712222333526021071 0ustar kaufmannacl2 ACL2-PC_colon__colon_UP.html -- ACL2 Version 6.3

    ACL2-PC::UP

    (primitive) move to the parent (or some ancestor) of the current subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:  if the conclusion is (= x (* (- y) z)) and the
               current subterm is y, then we have:
    up or (up 1) -- the current subterm becomes (- y)
    (up 2)       -- the current subterm becomes (* (- y) z)
    (up 3)       -- the current subterm becomes the entire conclusion
    (up 4)       -- no change; can't go up that many levels
    
    General Form:
    (up &optional n)
    
    Move up n levels in the conclusion from the current subterm, where n is a positive integer. If n is not supplied or is nil, then move up 1 level, i.e., treat the instruction as (up 1).

    See also dive, top, nx, and bk.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_USE.html0000664002132200015000000000222112222333526021167 0ustar kaufmannacl2 ACL2-PC_colon__colon_USE.html -- ACL2 Version 6.3

    ACL2-PC::USE

    (atomic macro) use a lemma instance
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (USE true-listp-append
         (:instance assoc-of-append (x a) (y b) (z c)))
    -- Add two top-level hypotheses, one the lemma called
       true-listp-append, and the other an instance of the lemma called
       assoc-of-append by the substitution in which x is assigned a, y
       is assigned b, and z is assigned c.
    
    General Form:
    (use &rest args)
    
    Add the given lemma instances to the list of top-level hypotheses. See hints for the syntax of :use hints in defthm, which is essentially the same as the syntax here (see the example above).

    This command calls the prove command, and hence should only be used at the top of the conclusion.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_WRAP-INDUCT.html0000664002132200015000000000233112222333526022272 0ustar kaufmannacl2 ACL2-PC_colon__colon_WRAP-INDUCT.html -- ACL2 Version 6.3

    ACL2-PC::WRAP-INDUCT

    (atomic macro) same as induct, but create a single goal
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    wrap-induct
    (wrap-induct t)
    (wrap-induct (append (reverse x) y))
    
    General Form:
    (wrap-induct &optional term)
    
    The wrap-induct command is identical to the proof-checker induct command, except that only a single goal is created: the conjunction of the base and induction steps.

    Note: The output will generally indicate that more than goal has been created, e.g.:

    Creating two new goals:  (MAIN . 1) and (MAIN . 2).
    
    However, wrap-induct always creates a unique goal (when it succeeds). A subsequent message clarifies this, for example:
    NOTE: Created ONLY one new goal, which is the current goal:
      (MAIN . 1)
    




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_WRAP.html0000664002132200015000000000234212222333526021310 0ustar kaufmannacl2 ACL2-PC_colon__colon_WRAP.html -- ACL2 Version 6.3

    ACL2-PC::WRAP

    (atomic macro) execute the indicated instructions and combine all the new goals
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Example:
    (wrap induct) ; induct, then replace first new goal by the conjunction of all
                  ; the new goals, and drop all new goals after the first
    
    General Form:
    (wrap &rest instrs)
    
    First the instructions in instrs are executed, as in do-all. If this ``fails'' then no additional action is taken. Otherwise, the current goal after execution of instrs is conjoined with all ``new'' goals, in the sense that their names are not among the names of goals at the time instrs was begun. This conjunction becomes the new current goal and those ``new'' goals are dropped.

    See the code for the proof-checker command wrap-induct for an example of the use of wrap.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_WRAP1.html0000664002132200015000000000344212222333526021373 0ustar kaufmannacl2 ACL2-PC_colon__colon_WRAP1.html -- ACL2 Version 6.3

    ACL2-PC::WRAP1

    (primitive) combine goals into a single goal
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    ; Keep (main . 1) and (main . 2) if they exist, as well as the current goal;
    ; and for each other goal, conjoin it into the current goal and delete it:
    (wrap1 ((main . 1) (main . 2)))
    
    ; As explained below, conjoin all subsequent siblings of the current goal
    ; into the current goal, and then delete them:
    (wrap1)
    
    General Form:
    (wrap1 &optional kept-goal-names)
    
    If kept-goal-names is not nil, the current goal is replaced by conjoining it with all goals other than the current goal and those indicated by kept-goal-names, and those other goals are deleted. If kept-goal-names is omitted, then the the current goal must be of the form (name . n), and the goals to conjoin into the current goal (and delete) are those with names of the form (name . k) for k >= n.

    NOTE: Wrap1 always ``succeeds'', even if there are no other goals to conjoin into the current goal (a message is printed in that case), and it always leaves you with no hypotheses at the top of the current goal's conclusion (as though top and demote had been executed, if necessary).

    Also see proof-checker documentation for wrap (see proof-checker-commands).




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_X-DUMB.html0000664002132200015000000000135112222333526021472 0ustar kaufmannacl2 ACL2-PC_colon__colon_X-DUMB.html -- ACL2 Version 6.3

    ACL2-PC::X-DUMB

    (atomic macro) expand function call at the current subterm, without simplifying
    Major Section:  PROOF-CHECKER-COMMANDS
    

    General Form:
    x-dumb:  expand without simplification.
    

    Same as (expand t new-goals-flg keep-all-guards-flg). See documentation for expand.

    See also x, which allows simplification.




    acl2-sources/doc/HTML/ACL2-PC_colon__colon_X.html0000664002132200015000000000477612222333526020763 0ustar kaufmannacl2 ACL2-PC_colon__colon_X.html -- ACL2 Version 6.3

    ACL2-PC::X

    (atomic macro) expand and (maybe) simplify function call at the current subterm
    Major Section:  PROOF-CHECKER-COMMANDS
    

    Examples:
    x --  expand and simplify.
    
    For example, if the current subterm is (append a b), then after x the current subterm will probably be (cons (car a) (append (cdr a) b)) if (consp a) and (true-listp a) are among the top-level hypotheses and governors. If there are no top-level hypotheses and governors, then after x the current subterm will probably be:
    (if (true-listp x)
        (if x
            (cons (car x) (append (cdr x) y))
          y)
      (apply 'binary-append (list x y))).
    
    General Form:
    (X &key
       rewrite normalize backchain-limit in-theory hands-off expand)
    
    Expand the function call at the current subterm, and simplify using the same conventions as with the s command (see documentation for s).

    Unlike s, it is permitted to set both :rewrite and :normalize to nil, which will result in no simplification; see x-dumb.

    Remark (obscure): On rare occasions the current address may be affected by the use of x. For example, suppose we have the definition

    (defun g (x) (if (consp x) x 3))
    
    and then we enter the proof-checker with
    (verify (if (integerp x) (equal (g x) 3) t)) .
    
    Then after invoking the instruction (dive 2 1), so that the current subterm is (g x), followed by the instruction x, we would expect the conclusion to be (if (integerp x) (equal 3 3) t). However, the system actually replaces (equal 3 3) with t (because we use the ACL2 term-forming primitives), and hence the conclusion is actually (if (integerp x) t t). Therefore, the current address is put at (2) rather than (2 1). In such cases, a warning ``NOTE'' will be printed to the terminal.

    The other primitive commands to which the above ``truncation'' note applies are equiv, rewrite, and s.




    acl2-sources/doc/HTML/ACL2-SEDAN.html0000664002132200015000000000273012222333515016263 0ustar kaufmannacl2 ACL2-SEDAN.html -- ACL2 Version 6.3

    ACL2-SEDAN

    ACL2 Sedan interface
    Major Section:  ACL2-TUTORIAL
    

    Many successful ACL2 users run in an shell under Emacs; see emacs. However, those not familiar with Emacs may prefer to start with an Eclipse-based interface initiallly developed by Peter Dillinger and Pete Manolios called the ``ACL2 Sedan'', or ``ACL2s''. As of this writing, the home page for ACL2s is http://acl2s.ccs.neu.edu/acl2s/doc/.

    ACL2 sessions in the ACL2 Sedan can utilize non-standard extensions and enhancements, especially geared toward new users, termination reasoning, and attaching rich user interfaces. These extensions are generally available as certifiable ACL2 books, and can be downloaded from http://acl2s.ccs.neu.edu/acl2s/src/acl2-extensions. (Some code originating from this project has been migrated to the ACL2 community books, but only after it was quite stable.) Thanks to Peter Dillinger, Pete Manolios, Daron Vroon, and Harsh Raju Chamarthi for their work on the ACL2 Sedan and for making their books available to ACL2 users.




    acl2-sources/doc/HTML/ACL2-TUTORIAL.html0000664002132200015000000001151212222333515016672 0ustar kaufmannacl2 ACL2-TUTORIAL.html -- ACL2 Version 6.3

    ACL2-TUTORIAL

    tutorial introduction to ACL2
    Major Section:  ACL2 Documentation
    

    To learn about ACL2, read at least the following two links.

    * Industrial Applications of ACL2 (10 minutes) to help you understand what sophisticated users can do;

    * A Flying Tour (10 minutes) to get an overview of the system and what skills the user must have.

    If you want to learn how to use ACL2, we recommend that you read a selection of the materials referenced below, depending on your learning style, and do suggested exercises.

    * ``A Walking Tour'' (1 hour) provides an overview of the theorem prover.

    * The external site http://tryacl2.org provides interactive lessons to get you started using ACL2.

    * ``Introduction to the Theorem Prover'' (10-40 hours) provides instruction on how to interact with the system. Unlike the three documents above, this document expects you to think! It cites the necessary background pages on programming in ACL2 and on the logic and then instructs you in The Method, which is how expert users use ACL2. It concludes with some challenge problems for the ACL2 beginner (including solutions) and an FAQ. Most users will spend several hours a day for several days working through this material.

    * The book ''Computer-Aided Reasoning: An Approach'' (see http://www.cs.utexas.edu/users/moore/publications/acl2-books/car/index.html is worth a careful read, as you work exercises and learn ``The Method.''

    * ``Annotated ACL2 Scripts and Demos'' contains relatively elementary proof scripts that have been annotated to help train the newcomer.

    * Many files (``books'') in the ACL2 community books (see community-books) are extensively annotated. See the link to ``Lemma Libraries and Utilities'' on the ACL2 home page (http://www.cs.utexas.edu/users/moore/acl2).

    * An ``Alternative Introduction'' document, while largely subsumed by the topic ``Introduction to the Theorem Prover'' mentioned above, still might be useful because it covers much of the tutorial material in a different way.

    At this point you are probably ready to use ACL2 on your own small projects. A common mistake for beginners is to browse the documentation and then try to do something that is too big! Think of a very small project and then simplify it!

    Note that ACL2 has a very supportive user network. See the link to ``Mailing Lists'' on the ACL2 home page (http://www.cs.utexas.edu/users/moore/acl2).

    The topics listed below are a hodge podge, developed over time. Although some of these are not mentioned above, you might find some to be useful as well.

    Some Related Topics




    acl2-sources/doc/HTML/ACL2-USER.html0000664002132200015000000000560512222333525016214 0ustar kaufmannacl2 ACL2-USER.html -- ACL2 Version 6.3

    ACL2-USER

    a package the ACL2 user may prefer
    Major Section:  PROGRAMMING
    

    This package imports the standard Common Lisp symbols that ACL2 supports and also a few symbols from package "ACL2" that are commonly used when interacting with ACL2. You may prefer to select this as your current package so as to avoid colliding with ACL2 system names.

    This package imports the symbols listed in *common-lisp-symbols-from-main-lisp-package*, which contains hundreds of CLTL function and macro names including those supported by ACL2 such as cons, car, and cdr. It also imports the symbols in *acl2-exports*, which contains a few symbols that are frequently used while interacting with the ACL2 system, such as implies, defthm, and rewrite. It imports nothing else.

    Thus, names such as alistp, member-equal, and type-set, which are defined in the "ACL2" package are not present here. If you find yourself frequently colliding with names that are defined in "ACL2" you might consider selecting "ACL2-USER" as your current package (see in-package). If you select "ACL2-USER" as the current package, you may then simply type member-equal to refer to acl2-user::member-equal, which you may define as you see fit. Of course, should you desire to refer to the "ACL2" version of member-equal, you will have to use the "ACL2::" prefix, e.g., acl2::member-equal.

    If, while using "ACL2-USER" as the current package, you find that there are symbols from "ACL2" that you wish we had imported into it (because they are frequently used in interaction), please bring those symbols to our attention. For example, should union-theories and universal-theory be imported? Except for stabilizing on the ``frequently used'' names from "ACL2", we intend never to define a symbol whose symbol-package-name is "ACL2-USER".




    acl2-sources/doc/HTML/ACL2P-KEY-CHECKPOINTS.html0000664002132200015000000000451112222333522017746 0ustar kaufmannacl2 ACL2P-KEY-CHECKPOINTS.html -- ACL2 Version 6.3

    ACL2P-KEY-CHECKPOINTS

    key checkpoints in ACL2(p)
    Major Section:  PARALLEL-PROOF
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    For printing output, the parallel version of the waterfall follows the precedent of gag-mode. The idea behind gag mode is to print only the subgoals most relevant to debugging a failed proof attempt. These subgoals are called 'key checkpoints' (see set-gag-mode for the definition of ``key'' and ``checkpoint''), and we restrict the default output mode for the parallel version of the waterfall to printing checkpoints similar to these key checkpoints.

    Some Related Topics

    • SET-WATERFALL-PRINTING -- for ACL2(p): configuring the printing that occurs within the parallelized waterfall

    As of this writing, we are aware of exactly one discrepancy between gag mode's key checkpoints and the parallel version of the waterfall's checkpoints. This discrepancy occurs when using ``by'' hints (see hints). As an example, take the following form, which attempts to prove a non-theorem:

    (thm (equal (append x y z) (append z (append y x)))
         :hints (("Subgoal *1/2'''" :by nil)))
    
    

    With waterfall parallelism enabled, Subgoal *1/2'' will be printed as a key checkpoint. This is different from using gag-mode while running the serial version of the waterfall, which skips printing the subgoal as a checkpoint.

    For those familiar with the ACL2 waterfall, we note that that the parallel version of the waterfall prints key checkpoints that are unproved in the following sense: a subgoal is a key checkpoint if it leads, in the current call of the waterfall, to a goal that is pushed for induction.




    acl2-sources/doc/HTML/ACL2S.html0000664002132200015000000000062512222333520015553 0ustar kaufmannacl2 ACL2S.html -- ACL2 Version 6.3

    ACL2S

    See acl2-sedan.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/ACL2_Characters.html0000664002132200015000000000242612222333526017576 0ustar kaufmannacl2 ACL2_Characters.html -- ACL2 Version 6.3

    ACL2 Characters

    ACL2 accepts 256 distinct characters, which are the characters obtained by applying the function code-char to each integer from 0 to 255. Among these, Common Lisp designates certain ones as *standard-characters*, namely those of the form (code-char n) where n is from 33 to 126, together with #\Newline and #\Space. The actual standard characters may be viewed by evaluating the constant expression *standard-chars*.

    The standard character constants are written by writing a hash mark followed by a backslash (#\) followed by the character.

    The function characterp recognizes characters. For more details, See characters .




    acl2-sources/doc/HTML/ACL2_Conses_or_Ordered_Pairs.html0000664002132200015000000000422512222333526022252 0ustar kaufmannacl2 ACL2_Conses_or_Ordered_Pairs.html -- ACL2 Version 6.3

    ACL2 Conses or Ordered Pairs

    The function cons creates an ordered pair. Car and cdr return the first and second components, respectively, of an ordered pair. The function consp recognizes ordered pairs.

    Ordered pairs are used to represent lists and trees. See any Common Lisp documentation for a discussion of how list constants are written and for the many list processing functions available. Also, see programming where we list all the ACL2 primitive functions.

    Here are some examples of list constants to suggest their syntax.

    '(a . b)                ; a pair whose car is 'a and cdr is 'b
    '(a . nil)              ; a pair whose car is 'a and cdr is nil
    '(a)                    ; another way to write the same thing
    '(a b)                  ; a pair whose car is 'a and cdr is '(b)
    '(a b c)                ; a pair whose car is 'a and cdr is '(b c)
                            ;  i.e., a list of three symbols, a, b, and c.
    '((a . 1) (b . 2))      ; a list of two pairs
    

    It is useful to distinguish ``proper'' conses from ``improper'' ones, the former being those cons trees whose right-most branch terminates with nil. A ``true list'' (see true-listp ) is either nil or a proper cons. (A b c . 7) is an improper cons and hence not a true list.




    acl2-sources/doc/HTML/ACL2_Strings.html0000664002132200015000000000253712222333526017153 0ustar kaufmannacl2 ACL2_Strings.html -- ACL2 Version 6.3

    ACL2 Strings

    Strings of ACL2 characters are written as sequences of characters delimited by ``double quotation marks'' ("). To put a double quotation mark in a string (or, any other character such as backslash or newline that seems to cause problems), escape it by preceding it with a backslash (\).

    The function stringp recognizes strings and char will fetch the nth character of a string. There are many other primitives for handling strings, such as string< for comparing two strings lexicographically. We suggest you See programming where we list all of the primitive ACL2 functions. Alternatively, see any Common Lisp language documentation.




    acl2-sources/doc/HTML/ACL2_Symbols.html0000664002132200015000000000655312222333526017154 0ustar kaufmannacl2 ACL2_Symbols.html -- ACL2 Version 6.3

    ACL2 Symbols

    Common Lisp's symbols are a data type representing words. They are frequently regarded as atomic objects in the sense that they are not frequently broken down into their constituents. Often the only important properties of symbols is that they are not numbers, characters, strings, or lists and that two symbols are not equal if they look different (!). Examples of symbols include PLUS and SMITH::ABC. All function and variable names in ACL2 are symbols. When symbols are used as constants they must be quoted, as in 'PLUS.

    The symbol T is commonly used as the Boolean ``true.'' The symbol NIL is commonly used both as the Boolean ``false'' and as the ``empty list.'' Despite sometimes being called the ``empty list'' NIL is a symbol not an ``empty cons.'' Unlike other symbols, T and NIL may be used as constants without quoting them.

    Usually, symbols are written as sequences of alphanumeric characters other than those denoting numbers. Thus, A12, +1A and 1+ are symbols but +12 is a number. Roughly speaking, when symbols are read lower case characters are converted to upper case, so we frequently do not distinguish ABC from Abc or abc. Click here for information about case conversion when symbols are read. However, any character can be used in a symbol, but some characters must be ``escaped'' to allow the Lisp reader to parse the sequence as a symbol. For example, |Abc| is a symbol whose first character is capitalized and whose remaining characters are in lower case. |An odd duck| is a symbol containing two #\Space characters. See any Common Lisp documentation for the syntactic rules for symbols.

    Technically, a symbol is a special kind of pair consisting of a package name (which is a string) and a symbol name (which is also a string). (See symbol-package-name and see symbol-name .) The symbol SMITH::ABC is said to be in package "SMITH" and to have the symbol name "ABC". The symbol ABC in package "SMITH" is generally not equal to the symbol ABC in package "JONES". However, it is possible to ``import'' symbols from one package into another one, but in ACL2 this can only be done when the package is created. (See defpkg .) If the current-package is "SMITH" then SMITH::ABC may be more briefly written as just ABC. Intern ``creates'' a symbol of a given name in a given package.




    acl2-sources/doc/HTML/ACL2_System_Architecture.html0000664002132200015000000000273312222333526021506 0ustar kaufmannacl2 ACL2_System_Architecture.html -- ACL2 Version 6.3

    ACL2 System Architecture

    The user interacts with the theorem prover by giving it definitions, theorems and advice. Most often the advice is about how to store each proved theorem as a rule. Sometimes the advice is about how to prove a specific theorem.

    The database consists of all the rules ACL2 ``knows.'' It is possible to include in the database all of the rules in some certified file of other events. Such certified files are called books .

    Interesting proofs are usually built on top of many books, some of which are written especially for that problem domain and others of which are about oft-used domains, like arithmetic or list processing. ACL2's distribution includes many books written by users. See the ``books'' link under the Lemma Libraries and Utilities link of the ACL2 home page.




    acl2-sources/doc/HTML/ACL2_as_an_Interactive_Theorem_Prover.html0000664002132200015000000000122312222333526024147 0ustar kaufmannacl2 ACL2_as_an_Interactive_Theorem_Prover.html -- ACL2 Version 6.3

    ACL2 as an Interactive Theorem Prover

    The ACL2 theorem prover finds proofs in the ACL2 logic. It can be automatic. But most often the user must help it.

    The user usually guides ACL2 by suggesting that it first prove key lemmas. Lemmas are just theorems used in the proofs of other theorems.




    acl2-sources/doc/HTML/ACL2_as_an_Interactive_Theorem_Prover__lparen_cont_rparen_.html0000664002132200015000000000325012222333526030402 0ustar kaufmannacl2 ACL2_as_an_Interactive_Theorem_Prover__lparen_cont_rparen_.html -- ACL2 Version 6.3

    ACL2 as an Interactive Theorem Prover (cont)

    When ACL2 proves a lemma, it is converted into one or more rules and stored in a database. The theorem prover is rule-driven. By proving lemmas you can configure ACL2 to behave in certain ways when it is trying to prove formulas in a certain problem domain. The expert user can make ACL2 do amazingly ``smart'' looking things.

    But it would be wrong to think that ACL2 knows the mathematical content of a formula just because it has proved it. What ACL2 knows -- all ACL2 knows -- is what is encoded in its rules. There are many types of rules (see rule-classes ).

    Many formulas can be effectively coded as rules. But by the same token, it is possible to encode a formula as a rule that is so ineffective it cannot even prove itself!

    The way a formula is stored as a rule is entirely up to the user. That is, you determine how ACL2 should use each formula that it proves.

    The most common kind of rule is the rewrite rule. It is so common that if you don't tell ACL2 how to store a formula, it stores it as a rewrite rule.




    acl2-sources/doc/HTML/ACL2_is_an_Untyped_Language.html0000664002132200015000000000272012222333526022120 0ustar kaufmannacl2 ACL2_is_an_Untyped_Language.html -- ACL2 Version 6.3

    ACL2 is an Untyped Language

    The example

    ACL2 !>(app '(a b c) 27)
    (A B C . 27)
    
    illustrates the fact that ACL2's logic is untyped (click here for a brief discussion of the typed versus untyped nature of the logic).

    The definition of app makes no restriction of the arguments to lists. The definition says that if the first argument satisfies endp then return the second argument. In this example, when app has recursed three times down the cdr of its first argument, '(a b c), it reaches the final nil, which satisfies endp, and so 27 is returned. It is naturally consed into the emerging list as the function returns from successive recursive calls (since cons does not require its arguments to be lists, either). The result is an ``improper'' list, (a b c . 27).

    You can think of (app x y) as building a binary tree by replacing the right-most tip of the tree x with the tree y.




    acl2-sources/doc/HTML/ACONS.html0000664002132200015000000000151712222333523015616 0ustar kaufmannacl2 ACONS.html -- ACL2 Version 6.3

    ACONS

    constructor for association lists
    Major Section:  ACL2-BUILT-INS
    

    (Acons key datum alist) equals the result of consing the pair (cons key datum) to the front of the association list alist.

    (Acons key datum alist) has a guard of (alistp alist). Acons is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ACTIVE-RUNEP.html0000664002132200015000000000210012222333531016601 0ustar kaufmannacl2 ACTIVE-RUNEP.html -- ACL2 Version 6.3

    ACTIVE-RUNEP

    check that a rune exists and is enabled
    Major Section:  THEORIES
    

    Example:
    (active-runep '(:rewrite left-to-right))
    
    General Form:
    (active-runep rune)
    
    where rune has the shape of a rune. This macro expands to an expression using the variables ens and state, and returns non-nil when the given rune exists and is enabled (according to the given ``enabled structure,'' ens, and the current logical world of the given state). See theory-invariant for how this macro can be of use.




    acl2-sources/doc/HTML/ADD-BINOP.html0000664002132200015000000000115612222333530016205 0ustar kaufmannacl2 ADD-BINOP.html -- ACL2 Version 6.3

    ADD-BINOP

    associate a function name with a macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    The form (add-binop macro macro-fn) is an abbreviation for the form (add-macro-fn macro macro-fn t). See add-macro-fn.




    acl2-sources/doc/HTML/ADD-CUSTOM-KEYWORD-HINT.html0000664002132200015000000001060312222333516020233 0ustar kaufmannacl2 ADD-CUSTOM-KEYWORD-HINT.html -- ACL2 Version 6.3

    ADD-CUSTOM-KEYWORD-HINT

    add a new custom keyword hint
    Major Section:  EVENTS
    

    Examples:
    (add-custom-keyword-hint :my-hint (my-hint-fn val ...))
    
    (add-custom-keyword-hint :my-hint
                             (my-hint-fn val ...)
                             :checker (my-hint-checker-fn val ...))
    

    General Form:
    (add-custom-keyword-hint :key term1 :checker term2)
    
    where :key is a keywordp not among the primitive keyword hints listed in *hint-keywords*, the :checker argument is optional, and term1 and (if supplied) term2 are terms with certain free-variable and signature restrictions described below. Henceforth, :key is treated as a custom keyword hint, e.g., the user can employ :key in hints to defthm, such as:
    (defthm name ...
      :hints (("Subgoal *1/1'" ... :key val ...))).
    

    Custom keyword hints are complicated. To use them you must understand state, multiple values (e.g., mv and mv-let), ACL2's notion of error triples (see programming-with-state), how to generate ``soft'' errors with er, how to use fmt-strings to control output, how to use computed hints (see computed-hints) and some aspects of ACL2's internal event processing. Furthermore, it is possible to implement a custom keyword hint that can make an event non-reproducible! So we recommend that these hints be developed by ACL2 experts. Basically the custom keyword feature allows the implementors and other experts to extend the hint facility without modifying the ACL2 sources.

    Term1 is called the ``generator'' term and term2 is called the ``checker'' term of the custom keyword hint :key. Together they specify the semantics of the new custom keyword hint :key. Roughly speaking, when a custom keyword hint is supplied by the user, as in

    (defthm name ...
      :hints (("Subgoal *1/1'" ... :my-hint val ...))).
    
    the checker term is evaluated on val to check that val is of the expected shape. Provided val passes the check, the generator term is used to compute a standard hint. Like computed hints, the generator of a custom keyword hint is allowed to inspect the actual clause on which it is being fired. Indeed, it is allowed to inspect the entire list of hints (standard and custom) supplied for that clause. Thus, in the most general case, a custom keyword hint is just a very special kind of computed hint.

    The generator, term1, must have no free variables other than:

    (val keyword-alist
     id clause world stable-under-simplificationp
     hist pspv ctx state).
    
    Moreover, either term1 must evaluate to a single non-stobj value, or else it must be single-threaded in state and have the standard error-triple output signature, (mv * * state).

    The restrictions on the checker, term2, are that it be single-threaded in state, have the standard error-triple output signature, (mv * * state), and have no free variables other than:

    (val world ctx state).
    

    For examples, see the community books directory books/hints/, in particular basic-tests.lisp.

    To delete a previously added custom keyword hint, see remove-custom-keyword-hint.

    The community book hints/merge-hint.lisp can be useful in writing custom keyword hints. See the examples near the of the file.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.




    acl2-sources/doc/HTML/ADD-DEFAULT-HINTS.html0000664002132200015000000000577012222333530017313 0ustar kaufmannacl2 ADD-DEFAULT-HINTS.html -- ACL2 Version 6.3

    ADD-DEFAULT-HINTS

    add to the default hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    
    Examples:
    (add-default-hints '((computed-hint-1 clause)
                         (computed-hint-2 clause
                                          stable-under-simplificationp)))
    (add-default-hints '((computed-hint-3 id clause world))
                       :at-end t)
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It is local to the book or encapsulate form in which it occurs (see add-default-hints! for a corresponding non-local event).

    General Forms:
    (add-default-hints lst)
    (add-default-hints lst :at-end flg)
    
    where lst is a list. Generally speaking, the elements of lst should be suitable for use as computed-hints.

    This event is completely analogous to set-default-hints, the difference being that add-default-hints appends the indicated hints to the front of the list of default hints, so that they are tried first -- or, if flg is supplied and evaluates to other than nil, at the end of the list, so that they are tried last -- rather than replacing the default hints with the indicated hints. Each new hint is thus considered after each existing hints when both are applied to the same goal. Also See set-default-hints, see remove-default-hints, and see default-hints.

    Finally, note that the effects of set-default-hints, add-default-hints, and remove-default-hints are local to the book in which they appear. Thus, users who include a book with such forms will not have their default hints affected by such forms. In order to export the effect of setting the default hints, use set-default-hints!, add-default-hints!, or remove-default-hints!.

    For a related feature, which however is only for advanced system builders, see override-hints.




    acl2-sources/doc/HTML/ADD-DEFAULT-HINTS_bang_.html0000664002132200015000000000201312222333530020424 0ustar kaufmannacl2 ADD-DEFAULT-HINTS_bang_.html -- ACL2 Version 6.3

    ADD-DEFAULT-HINTS!

    add to the default hints non-locally
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please see add-default-hints, which is the same as add-default-hints! except that the latter is not local to the encapsulate or the book in which it occurs. Probably add-default-hints is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing encapsulate or book.




    acl2-sources/doc/HTML/ADD-DIVE-INTO-MACRO.html0000664002132200015000000000151712222333530017474 0ustar kaufmannacl2 ADD-DIVE-INTO-MACRO.html -- ACL2 Version 6.3

    ADD-DIVE-INTO-MACRO

    associate proof-checker diving function with macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (add-dive-into-macro cat expand-address-cat)
    
    This feature is used so that the proof-checker's DV command and numeric diving commands (e.g., 3) will dive properly into subterms. Please see dive-into-macros-table.




    acl2-sources/doc/HTML/ADD-INCLUDE-BOOK-DIR.html0000664002132200015000000000653612222333530017574 0ustar kaufmannacl2 ADD-INCLUDE-BOOK-DIR.html -- ACL2 Version 6.3

    ADD-INCLUDE-BOOK-DIR

    link keyword for :dir argument of ld and include-book
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Form:
    (add-include-book-dir :smith "/u/smith/")
     ; For (include-book "foo" :dir :smith), prepend "/u/smith/" to "foo".
    
    General Form:
    (add-include-book-dir kwd dir)
    
    where kwd is a keywordp and dir is the pathname of a directory. (If the final '/' is missing, ACL2 will add it for you.) The effect of this event is to modify the meaning of the :dir keyword argument of include-book as indicated by the examples above, and similarly for ld, namely by associating the indicated directory with the indicated keyword for purposes of the :dir argument. By the ``indicated directory'' we mean, in the case that the pathname is a relative pathname, the directory relative to the current connected book directory; see cbd. See delete-include-book-dir for how to undo this effect.

    A keyword that is already associated with a directory string by an existing invocation of add-include-book-dir cannot be associated with a different directory string. If that is your intention, first apply delete-include-book-dir to that keyword; see delete-include-book-dir. If however the new directory string is identical with the old, then the call of add-include-book-dir will be redundant (see redundant-events).

    The keyword :system can never be redefined. It will always point to the absolute pathname of the system books directory, which by default is immediately under the directory where the ACL2 executable was originally built (see include-book, in particular the discussion there of ``books directory'').

    This macro generates (in essence) a call (table acl2-defaults-table :include-book-dir-alist ...) and hence is local to any books and encapsulate events in which it occurs. See acl2-defaults-table. Even if you invoke add-include-book-dir before certifying a book, so that this event is among the book's portcullis commands rather than in the book itself, nevertheless that add-include-book-dir event will not be visible after the book is included. (Note: The above behavior is generally preserved in raw-mode (see set-raw-mode),though by means other than a table.)




    acl2-sources/doc/HTML/ADD-INVISIBLE-FNS.html0000664002132200015000000000322712222333530017307 0ustar kaufmannacl2 ADD-INVISIBLE-FNS.html -- ACL2 Version 6.3

    ADD-INVISIBLE-FNS

    make some unary functions invisible to the loop-stopper algorithm
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (add-invisible-fns binary-+ unary-- foo)
    (add-invisible-fns + unary-- foo)
    
    Each of the events above makes unary functions unary-- and foo ``invisible'' for the purposes of applying permutative :rewrite rules to binary-+ trees. Thus, arg and (unary-- arg) will be given the same weight and will be permuted so as to be adjacent.

    General Form:
    (add-invisible-fns top-fn unary-fn1 ... unary-fnk)
    
    where top-fn is a function symbol and the unary-fni are unary function symbols, or more generally, these are all macro aliases for function symbols (see macro-aliases-table).

    For more information see invisible-fns-table. Also see set-invisible-fns-table, which explains how to set the entire table in a single event, and see remove-invisible-fns.




    acl2-sources/doc/HTML/ADD-LD-KEYWORD-ALIAS.html0000664002132200015000000000074312222333530017607 0ustar kaufmannacl2 ADD-LD-KEYWORD-ALIAS.html -- ACL2 Version 6.3

    ADD-LD-KEYWORD-ALIAS

    See ld-keyword-aliases.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/ADD-LD-KEYWORD-ALIAS_bang_.html0000664002132200015000000000075212222333530020735 0ustar kaufmannacl2 ADD-LD-KEYWORD-ALIAS_bang_.html -- ACL2 Version 6.3

    ADD-LD-KEYWORD-ALIAS!

    See ld-keyword-aliases.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/ADD-MACRO-ALIAS.html0000664002132200015000000000255512222333530017032 0ustar kaufmannacl2 ADD-MACRO-ALIAS.html -- ACL2 Version 6.3

    ADD-MACRO-ALIAS

    associate a function name with a macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (add-macro-alias append binary-append)
    
    This example associates the function symbol binary-append with the macro name append. As a result, the name append may be used as a runic designator (see theories) by the various theory functions. See macro-aliases-table for more details. Also see add-macro-fn for an extension of this utility that also affects printing.

    General Form:
    (add-macro-alias macro-name function-name)
    
    This is a convenient way to add an entry to macro-aliases-table. See macro-aliases-table and also see remove-macro-alias.




    acl2-sources/doc/HTML/ADD-MACRO-FN.html0000664002132200015000000000526212222333530016502 0ustar kaufmannacl2 ADD-MACRO-FN.html -- ACL2 Version 6.3

    ADD-MACRO-FN

    associate a function name with a macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (add-macro-fn append binary-append)
    (add-macro-fn append binary-append t)
    
    These examples each associate the function symbol binary-append with the macro name append. As a result, theory functions will understand that append refers to binary-append -- see add-macro-alias -- and moreover, proof output will be printed using append rather than binary-append. In the first case, (append x (append y z)) is printed rather than (append x y z). In the second case, right-associated arguments are printed flat: (append x y z). Such right-association is considered only for binary function symbols; otherwise the optional third argument is ignored.

    General Forms:
    (add-macro-fn macro-name function-name)
    (add-macro-fn macro-name function-name nil) ; same as abov
    (add-macro-fn macro-name function-name t)
    

    This is a convenient way to add an entry to macro-aliases-table and at the same time extend the untrans-table. As suggested by the example above, calls of a function in this table will be printed as corresponding calls of macros, with right-associated arguments printed flat in the case of a binary function symbol if the optional third argument is t. In that case, for a binary function symbol fn associated with macro name mac, then a call (fn arg1 (fn arg2 (... (fn argk arg)))) will be displayed to the user as though the ``term'' were (mac arg1 arg2 ... argk arg). For a call (f a1 ... ak) of a function symbol that is not binary, or the optional argument is not supplied as t, then the effect is simply to replace f by the corresponding macro symbol. See add-macro-alias, which is invoked on the first two arguments. Also see remove-macro-alias, see untrans-table, and see remove-macro-fn.




    acl2-sources/doc/HTML/ADD-MATCH-FREE-OVERRIDE.html0000664002132200015000000001302312222333530020122 0ustar kaufmannacl2 ADD-MATCH-FREE-OVERRIDE.html -- ACL2 Version 6.3

    ADD-MATCH-FREE-OVERRIDE

    set :match-free value to :once or :all in existing rules
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Forms:
    (add-match-free-override :once t)
        ; Try only the first binding of free variables when relieving hypotheses
        ; of any rule of class :rewrite, :linear, or :forward-chaining.
    (add-match-free-override :all (:rewrite foo) (:rewrite bar))
        ; For rewrite rules foo and bar, try all bindings of free variables when
        ; relieving hypotheses.
    (add-match-free-override :clear)
        ; Restore :match-free to what was originally stored for each rule (either
        ; :all or :once).
    
    As described elsewhere (see free-variables), a rewrite, linear, or forward-chaining rule may have free variables in its hypotheses, and ACL2 can be directed either to try all bindings (``:all'') or just the first (``:once'') when relieving a hypothesis, as a basis for relieving subsequent hypotheses. This direction is generally provided by specifying either :match-free :once or :match-free :all in the :rule-classes of the rule, or by using the most recent set-match-free-default event. Also see rule-classes.

    However, if a proof is going slowly, you may want to modify the behavior of some such rules so that they use only the first match for free variables in a hypothesis when relieving subsequent hypotheses, rather than backtracking and trying additional matches as necessary. (But note: add-match-free-override is not relevant for type-prescription rules.) The event (add-match-free-override :once t) has that effect. Or at the other extreme, perhaps you want to specify all rules as :all rules except for a some specific exceptions. Then you can execute (add-match-free-override :all t) followed by, say, (add-match-free-override :once (:rewrite foo) (:linear bar)).

    General Forms:
    (add-match-free-override :clear)
    (add-match-free-override flg t)
    (add-match-free-override flg rune1 rune2 ... runek)
    
    where flg is :once or :all and the runei are runes. If :clear is specified then all rules will have the :all/:once behavior from when they were first stored. The second general form causes all rewrite linear, and forward-chaining rules to have the behavior specified by flg (:all or :once). Finally, the last of these, where runes are specified, is additive in the sense that only the indicated rules are affected; all others keep the behavior they had just before this event was executed (possible because of earlier add-match-free-override events).

    At the conclusion of this event, ACL2 prints out the list of all :linear, :rewrite, and :forward-chaining runes whose rules contain free variables in hypotheses that are to be bound :once, except that if there are no overrides (value :clear was used), then :clear is printed.

    This event only affects rules that exist at the time it is executed. Future rules are not affected by the override.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It uses the acl2-defaults-table, and hence its effect is local to the book or encapsulate form in which it occurs.

    Remarks

    Lists of the :rewrite, :linear, and :forward-chaining runes whose behavior was originally :once or :all are returned by the following forms, respectively.

    (free-var-runes :once (w state))
    (free-var-runes :all  (w state))
    
    The form
    (match-free-override (w state))
    
    evaluates to a pair, whose car is a number used by ACL2 to determine whether a rune is sufficiently old to be affected by the override, and whose cdr is the list of runes whose behavior is specified as :once by add-match-free-override; except, if no runes have been overridden, then the keyword :clear is returned.




    acl2-sources/doc/HTML/ADD-NTH-ALIAS.html0000664002132200015000000000212112222333530016607 0ustar kaufmannacl2 ADD-NTH-ALIAS.html -- ACL2 Version 6.3

    ADD-NTH-ALIAS

    associate one symbol with another for printing of nth/update-nth terms
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (add-nth-alias st0 st)
    
    This example associates the symbol st0 with the symbol st for purposes of printing certain terms of the form (nth n st0) and (update-nth n val st0).

    General Form:
    (add-nth-alias alias-name name)
    
    This is a convenient way to add an entry to nth-aliases-table. See nth-aliases-table and also see remove-nth-alias.




    acl2-sources/doc/HTML/ADD-OVERRIDE-HINTS.html0000664002132200015000000000327412222333530017443 0ustar kaufmannacl2 ADD-OVERRIDE-HINTS.html -- ACL2 Version 6.3

    ADD-OVERRIDE-HINTS

    add to the override-hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    See override-hints for a discussion of override-hints. Here we describe how to extend the list of override-hints. Note that the effects of add-override-hints events are local to the books or encapsulate events in which they reside; see add-override-hints! to avoid that restriction. Also see set-override-hints to set a new list of override-hints to it, ignoring the present list rather than adding to it.

    General Forms:
    (add-override-hints form)
    (add-override-hints form :at-end t)
    (add-override-hints form :at-end nil) ; default for :at-end
    
    where form evaluates to a list of computed hint forms. The effect of this event is to extend the current list of override-hints by appending the result of that evaluation. The default is to append the evaluation result to the front of the current list of override-hints, but if :at-end t is specified, then the evaluation result is appended to the end of the current list.




    acl2-sources/doc/HTML/ADD-OVERRIDE-HINTS_bang_.html0000664002132200015000000000172212222333530020565 0ustar kaufmannacl2 ADD-OVERRIDE-HINTS_bang_.html -- ACL2 Version 6.3

    ADD-OVERRIDE-HINTS!

    add non-locally to the override-hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Add-override-hints! is the same as add-override-hints, except that the former is not local to books or encapsulate events in which it occurs. See add-override-hints; also see set-override-hints.




    acl2-sources/doc/HTML/ADD-RAW-ARITY.html0000664002132200015000000000347612222333531016665 0ustar kaufmannacl2 ADD-RAW-ARITY.html -- ACL2 Version 6.3

    ADD-RAW-ARITY

    add arity information for raw mode
    Major Section:  SET-RAW-MODE
    

    Technical note: This macro is a no-op, and is not necessary, when ACL2 is built with #-acl2-mv-as-values.

    Users of raw mode (see set-raw-mode) can use arbitrary raw Lisp functions that are not known inside the usual ACL2 loop. In such cases, ACL2 may not know how to display a multiple value returned by ACL2's mv macro. The following example should make this clear.

    ACL2 P>(defun foo (x y) (mv y x))
    FOO
    ACL2 P>(foo 3 4)
    
    Note: Unable to compute number of values returned by this evaluation
    because function FOO is not known in the ACL2 logical world.  Presumably
    it was defined in raw Lisp or in raw mode.  Returning the first (perhaps
    only) value for calls of FOO.
    4
    ACL2 P>(add-raw-arity foo 2)
     RAW-ARITY-ALIST
    ACL2 P>(foo 3 4)
    (4 3)
    ACL2 P>
    
    The first argument of add-raw-arity should be a symbol, representing the name of a function, macro, or special form, and the second argument should either be a non-negative integer (denoting the number of values returned by ACL2) or else the symbol :LAST, meaning that the number of values returned by the call is the number of values returned by the last argument.

    The current arity assignments can be seen by evaluating (@ raw-arity-alist). See remove-raw-arity for how to undo a call of add-raw-arity.




    acl2-sources/doc/HTML/ADD-TO-SET-EQ.html0000664002132200015000000000064712222333523016622 0ustar kaufmannacl2 ADD-TO-SET-EQ.html -- ACL2 Version 6.3

    ADD-TO-SET-EQ

    See add-to-set.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/ADD-TO-SET-EQL.html0000664002132200015000000000065112222333523016731 0ustar kaufmannacl2 ADD-TO-SET-EQL.html -- ACL2 Version 6.3

    ADD-TO-SET-EQL

    See add-to-set.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/ADD-TO-SET-EQUAL.html0000664002132200015000000000065512222333523017163 0ustar kaufmannacl2 ADD-TO-SET-EQUAL.html -- ACL2 Version 6.3

    ADD-TO-SET-EQUAL

    See add-to-set.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/ADD-TO-SET.html0000664002132200015000000000457612222333523016324 0ustar kaufmannacl2 ADD-TO-SET.html -- ACL2 Version 6.3

    ADD-TO-SET

    add a symbol to a list
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (add-to-set x lst)
    (add-to-set x lst :test 'eql)   ; same as above (eql as equality test)
    (add-to-set x lst :test 'eq)    ; same, but eq is equality test
    (add-to-set x lst :test 'equal) ; same, but equal is equality test
    

    For a symbol x and an object lst, (add-to-set-eq x lst) is the result of consing x on to the front of lst, unless x is already a member of lst, in which case the result is lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst.

    The guard for a call of add-to-set depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between add-to-set and its variants:

    (add-to-set-eq x lst) is equivalent to (add-to-set x lst :test 'eq);

    (add-to-set-equal x lst) is equivalent to (add-to-set x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function add-to-set-equal.




    acl2-sources/doc/HTML/ADVANCED-FEATURES.html0000664002132200015000000004735712222333515017351 0ustar kaufmannacl2 ADVANCED-FEATURES.html -- ACL2 Version 6.3

    ADVANCED-FEATURES

    some advanced features of ACL2
    Major Section:  ACL2-TUTORIAL
    

    Maybe you've been using ACL2 for awhile, and you wonder if there are lesser-known features that you might find useful. Then this topic is for you. We present below a ``laundry list'' of some such features, with brief descriptions and links to documentation topics.

    Although the list below is long, it is not intended to be complete, and indeed some topics have been deliberately excluded. Some have fallen out of use, perhaps for good reason, such as NU-REWRITER and OBDD. Others are already likely to be discovered when needed, such as GETENV$ and perhaps DOUBLE-REWRITE. Some topics are referenced by documentation for others in the list, such as MBT, which is referenced by MBE. Some utilities such as PSTACK and VERBOSE-PSTACK seem too low-level to be worthy of inclusion below.

    For an extensive introduction to using the prover, which may include some aspects new to you, see INTRODUCTION-TO-THE-THEOREM-PROVER. A shorter topic contains highlights for efficient prover usage: see TIPS. Also see ACL2-SEDAN for an extension of ACL2 (written by others), ACL2s, that includes an Eclipse-based interface, more powerful and automatic termination reasoning, and other features.

    We now move on to the list.

    ========================================
    Top-level commands and utilities:
    ========================================
    

    o See A! and see P! to abort or pop.

    o See ACL2-CUSTOMIZATION for initial commands to run at startup.

    o See KEYWORD-COMMANDS for how keyword commands are processed.

    o See LD for many ways to control the top-level loop.

    o See COMPILATION for a discussion of set-compiler-enabled and other compiler-related utilities.

    o For useful reader macros `#!', `#.', and `#u', see SHARP-BANG-READER, see SHARP-DOT-READER, and see SHARP-U-READER.

    o To save and use an ACL2 executable, see ACL2-AS-STANDALONE-PROGRAM and see SAVE-EXEC.

    o For utilities related to timing, see TIME$, see WITH-PROVER-TIME-LIMIT, see WITH-PROVER-STEP-LIMIT, and see SET-PROVER-STEP-LIMIT.

    o To query and manage the database, see HISTORY (which discusses many useful utilities, such as :PBT and :PL), and see DEAD-EVENTS.

    o See ADD-INCLUDE-BOOK-DIR for linking keyword for :dir argument of LD and INCLUDE-BOOK.

    o See REBUILD for a fast way to load a file without waiting for proofs.

    o For parallel certification, see BOOKS-CERTIFICATION for use of the -j option of `make'; also see PROVISIONAL-CERTIFICATION.

    ========================================
    Some relatively less common events:
    ========================================
    

    o See RESET-PREHISTORY to reset the prehistory.

    o See ASSERT-EVENT to assert that a given form returns a non-nil value.

    o See DEFATTACH to execute constrained functions using corresponding attached functions.

    o See DEFUN-SK to define a function whose body has an outermost quantifier.

    o See DEFCHOOSE to define a Skolem (witnessing) function.

    o For efficiency consider using defconst-fast; see DEFCONST.

    o See SET-VERIFY-GUARDS-EAGERNESS to specify when guard verification is tried by default.

    ========================================
    Output and its control (see IO for additional information):
    ========================================
    

    o See WITH-OUTPUT to suppress or turn on specified output for an event.

    o See EVISC-TABLE for support for abbreviated output.

    o See NTH-ALIASES-TABLE for a table used to associate names for NTH/UPDATE-NTH printing.

    o See OUTPUT-TO-FILE to redirect output to a file.

    o See PRINT-CONTROL to control ACL2 printing.

    o See SET-EVISC-TUPLE to control suppression of details when printing.

    o See SET-INHIBIT-OUTPUT-LST to control output by type.

    o See SET-IPRINT to allow abbreviated output to be read back in.

    o See SET-PRINT-BASE to control the radix in which numbers are printed.

    o See SET-PRINT-RADIX to control whether the radix of a number is printed.

    o See SET-PRINT-CASE to control whether symbols are printed in upper case or in lower case.

    ========================================
    On proving termination for definitions:
    ========================================
    

    o See ORDINALS for a discussion of ordinals in ACL2.

    o See RULER-EXTENDERS for a control on ACL2's termination and induction analyses.

    o See SET-WELL-FOUNDED-RELATION to set the default well-founded relation for termination analysis.

    o See ACL2-SEDAN for a related tool that provides extra automation for termination proofs.

    ========================================
    Proof debugging and output control:
    ========================================
    

    o See ACCUMULATED-PERSISTENCE to get statistics on which runes are being tried.

    o See ADD-MACRO-FN and see ADD-MACRO-ALIAS to associate a function name with a macro name.

    o See BREAK-REWRITE for how to monitor rewrite rules.

    o See DMR for dynamic monitoring of rewriting and other prover activity.

    o See FORWARD-CHAINING-REPORTS to see reports about the forward chaining process.

    o See GUARD-DEBUG to generate markers to indicate sources of guard proof obligations.

    o See PROOF-CHECKER for support for low-level interaction.

    o See REDO-FLAT for redo on failure of a PROGN, ENCAPSULATE, or CERTIFY-BOOK.

    o See SET-GAG-MODE and see PSO to abbreviate or restore proof output.

    o See SET-INHIBIT-OUTPUT-LST, see SET-INHIBIT-WARNINGS, and see SET-INHIBITED-SUMMARY-TYPES to inhibit various types of output.

    o See SET-RAW-PROOF-FORMAT to make proof output display lists of runes.

    o See SKIP-PROOFS to skip proofs for a given form.

    ========================================
    Program debugging:
    ========================================
    

    o See BREAK$ to cause an immediate Lisp break.

    o See BREAK-ON-ERROR to break when encountering a hard or soft error caused by ACL2.

    o See DISASSEMBLE$ to disassemble a function.

    o See PRINT-GV to print a form whose evaluation caused a guard violation.

    o See PROFILE to turn on profiling for one function.

    o See TRACE$ and see OPEN-TRACE-FILE to trace function evaluations, possibly sending trace output to a file.

    o See WET to evaluate a form and print a subsequent error trace.

    ========================================
    Programming and evaluation idioms, support, utilities (also see PROGRAMMING
    for more utilities, e.g., RANDOM$)
    ========================================
    

    o See ARRAYS and See DEFSTOBJ for introductions to ACL2 arrays and single-threaded objects (stobjs), respectively, each of which provides efficient destructive operations in an applicative setting. Also see WITH-LOCAL-STOBJ for a way to create local stobjs.

    o See ASSERT$ to cause a hard error if the given test is false.

    o See CANONICAL-PATHNAME to obtain the true absolute filename, with soft links resolved.

    o See CASE-MATCH for a utility providing pattern matching and destructuring.

    o See DEFPUN to define a tail-recursive function symbol.

    o See EC-CALL to execute a call in the ACL2 logic instead of raw Lisp.

    o See ER to print an error message and ``cause an error''.

    o See FLET to provide local binding of function symbols.

    o See GC$ to invoke the garbage collector.

    o See MBE to attach code for execution.

    o See MV-LIST to convert a multiple-valued result to a single-valued list.

    o See MV? to return one or more values.

    o For non-executable code, see DEFUN-NX and see NON-EXEC.

    o See PROG2$ and see PROGN$ to execute two or more forms and return the value of the last one.

    o See PROGRAMMING-WITH-STATE for how to program using the von Neumannesque ACL2 state object.

    o See TOP-LEVEL to evaluate a top-level form as a function body.

    o See WITH-GUARD-CHECKING to suppress or enable guard-checking for a form.

    o For ways to fake access to the state see WORMHOLE, see WITH-LOCAL-STATE, see CW, see CW!, see PRINTING-TO-STRINGS, see OBSERVATION-CW, and (dangerous!) see WITH-LIVE-STATE.

    ========================================
    Connecting with the underlying host Lisp, and doing other evil:
    ========================================
    

    o See DEFTTAG to introduce a trust tag (ttag).

    o See DEFMACRO-LAST to define a macro that returns its last argument, but with side effects.

    o See PROGN! to evaluate forms that are not necessarily events.

    o See RETURN-LAST to return the last argument, perhaps with side effects.

    o See SET-RAW-MODE to enter or exit ``raw mode,'' a raw Lisp environment.

    o See SYS-CALL to make a system call to the host operating system.

    ========================================
    Macros and related utilities:
    ========================================
    

    o See DEFABBREV for a convenient form of macro definition for simple expansions.

    o See MACRO-ARGS for the formals list of a macro definition (see DEFMACRO).

    o See MAKE-EVENT for a sort of extension of DEFMACRO that allows access to the state, by evaluating (expanding) a given form and then evaluate the result of that expansion.

    o See TRANS, see TRANS!, and see TRANS1 to print the macroexpansion of a form.

    ========================================
    Experimental extensions:
    ========================================
    

    o See HONS-AND-MEMOIZATION for ACL2(h); in particular, see MEMOIZE for efficient function memoization and see PROFILE for profiling.

    o See REAL for ACL2(r), which supports the real numbers.

    o See PARALLELISM for ACL2(p), which supports parallel evaluation and proof.

    ========================================
    Database control and query:
    ========================================
    

    o See DISABLEDP to determine whether a given name or rune is disabled.

    o For redefinition support see REDEF, see REDEF!, see REDEF+, see REDEF-, and see REDEFINED-NAMES.

    o See TABLE for user-managed tables.

    o See VERIFY-GUARDS-FORMULA to view a guard proof obligation without doing the proof.

    ========================================
    Prover control
    ========================================
    

    o For congruence-based reasoning see DEFCONG, see CONGRUENCE, see EQUIVALENCE, see DEFEQUIV, and see DEFREFINEMENT.

    o For meta rules and clause processors see META, see DEFEVALUATOR, see CLAUSE-PROCESSOR, see DEFINE-TRUSTED-CLAUSE-PROCESSOR (for connecting with external tools, such as SAT solvers), and See EXTENDED-METAFUNCTIONS (for state and context-sensitive metafunctions).

    o For theory control, see THEORIES for detailed information, but in particular see DEFTHEORY, see THEORY-FUNCTIONS, see IN-ARITHMETIC-THEORY (and see NON-LINEAR-ARITHMETIC), and see THEORY-INVARIANT.

    o See HINTS for a complete list of prover hints, including some of the more obscure ones such as :restrict, :clause-processor, :nonlinearp, :backchain-limit-rw, :reorder, and :backtrack. Also see HINTS-AND-THE-WATERFALL for an explanation of how hints interact with the ACL2 proof process. For other topics related to hints, see OVERRIDE-HINTS, see ADD-CUSTOM-KEYWORD-HINT, see DEFAULT-HINTS, and see COMPUTED-HINTS (also see USING-COMPUTED-HINTS and for other topics USING-COMPUTED-HINTS-xxx see MISCELLANEOUS.

    o See BIND-FREE to bind free-variables of a rewrite or linear rule.

    o See CASE-SPLIT for a utility like FORCE that immediately splits the top-level goal on the indicated hypothesis.

    o See CASE-SPLIT-LIMITATIONS for a way to the number of cases produced at once

    o See DEFAULT-BACKCHAIN-LIMIT to specify the backchain limit for a rule.

    o See FORCE for an identity function used to force a hypothesis.

    o See OTF-FLG for a way to push more than one initial subgoal for induction.

    o See RULE-CLASSES to add various kinds of rules to the database, including more unusual sorts such as :built-in-clause rules and :induction rules.

    o See SET-BACKCHAIN-LIMIT to set the backchain-limit used by the type-set and rewriting mechanisms.

    o See SET-BODY to set an alternate definition body for :expand hints.

    o See SET-REWRITE-STACK-LIMIT to set the rewrite stack depth used by the rewriter.

    o See SYNTAXP to attach a heuristic filter on a :rewrite, :meta, or :linear rule.




    acl2-sources/doc/HTML/ALISTP.html0000664002132200015000000000125112222333523015742 0ustar kaufmannacl2 ALISTP.html -- ACL2 Version 6.3

    ALISTP

    recognizer for association lists
    Major Section:  ACL2-BUILT-INS
    

    (alistp x) is true if and only if x is a list of cons pairs.

    (alistp x) has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ALLOCATE-FIXNUM-RANGE.html0000664002132200015000000000224412222333523017773 0ustar kaufmannacl2 ALLOCATE-FIXNUM-RANGE.html -- ACL2 Version 6.3

    ALLOCATE-FIXNUM-RANGE

    set aside fixnums in GCL
    Major Section:  ACL2-BUILT-INS
    

    (Allocate-fixnum-range fixnum-lo fixnum-hi) causes Gnu Common Lisp (GCL) to create a persistent table for the integers between fixnum-lo and fixnum-hi (both bounds inclusive). This table is referenced first when any integer is boxed and the existing box in the table is used if the integer is in bounds. This can speed up GCL considerably by avoiding wasteful fixnum boxing. Here, fixnum-lo and fixnum-hi should be fixnums. On 32-bit machines it would be good for them to be of type (signed-byte 30), with fixnum-lo <= fixnum-hi.

    When this function is executed in a Lisp implementation other than GCL, it has no side effect. This function always returns nil.




    acl2-sources/doc/HTML/ALPHA-CHAR-P.html0000664002132200015000000000173212222333523016507 0ustar kaufmannacl2 ALPHA-CHAR-P.html -- ACL2 Version 6.3

    ALPHA-CHAR-P

    recognizer for alphabetic characters
    Major Section:  ACL2-BUILT-INS
    

    (Alpha-char-p x) is true if and only if x is a alphabetic character, i.e., one of the characters #\a, #\b, ..., #\z, #\A, #\B, ..., #\Z.

    The guard for alpha-char-p requires its argument to be a character.

    Alpha-char-p is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ALPHORDER.html0000664002132200015000000000311212222333523016264 0ustar kaufmannacl2 ALPHORDER.html -- ACL2 Version 6.3

    ALPHORDER

    total order on atoms
    Major Section:  ACL2-BUILT-INS
    

    Alphorder is a non-strict total order, a ``less than or equal,'' on atoms. By ``non-strict total order'' we mean a function that always returns t or nil and satisfies the following properties.

    o Antisymmetry: XrY & YrX -> X=Y

    o Transitivity: XrY & YrZ -> XrZ

    o Trichotomy: XrY v YrX

    Also see lexorder, which extends alphorder to all objects.

    (Alphorder x y) has a guard of (and (atom x) (atom y)).

    Within a single type: rationals are compared arithmetically, complex rationals are compared lexicographically, characters are compared via their char-codes, and strings and symbols are compared with alphabetic ordering. Across types, rationals come before complexes, complexes come before characters, characters before strings, and strings before symbols. We also allow for ``bad atoms,'' i.e., atoms that are not legal Lisp objects but make sense in the ACL2 logic; these come at the end, after symbols.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ALTERNATIVE-INTRODUCTION.html0000664002132200015000000007751012222333515020457 0ustar kaufmannacl2 ALTERNATIVE-INTRODUCTION.html -- ACL2 Version 6.3

    ALTERNATIVE-INTRODUCTION

    introduction to ACL2
    Major Section:  ACL2-TUTORIAL
    

    This section contains introductory material on ACL2 including what ACL2 is, how to get started using the system, how to read the output, and other introductory topics. It was written almost entirely by Bill Young of Computational Logic, Inc.

    You might also find CLI Technical Report 101 helpful, especially if you are familiar with Nqthm. If you would like more familiarity with Nqthm, we suggest CLI Technical Report 100.

    OVERVIEW

    ACL2 is an automated reasoning system developed (for the first 9 years) at Computational Logic, Inc. and (from January, 1997) at the University of Texas at Austin. It is the successor to the Nqthm (or Boyer-Moore) logic and proof system and its Pc-Nqthm interactive enhancement. The acronym ACL2 actually stands for ``A Computational Logic for Applicative Common Lisp''. This title suggests several distinct but related aspects of ACL2.

    We assume that readers of the ACL2 documentation have at least a very slight familiarity with some Lisp-like language. We will address the issue of prerequisites further, in ``ABOUT THIS TUTORIAL'' below.

    As a logic, ACL2 is a formal system with rigorously defined syntax and semantics. In mathematical parlance, the ACL2 logic is a first-order logic of total recursive functions providing mathematical induction on the ordinals up to epsilon-0 and two extension principles: one for recursive definition and one for constrained introduction of new function symbols, here called encapsulation. The syntax of ACL2 is that of Common Lisp; ACL2 specifications are ``also'' Common Lisp programs in a way that we will make clear later. In less formal language, the ACL2 logic is an integrated collection of rules for defining (or axiomatizing) recursive functions, stating properties of those functions, and rigorously establishing those properties. Each of these activities is mechanically supported.

    As a specification language, ACL2 supports modeling of systems of various kinds. An ACL2 function can equally be used to express purely formal relationships among mathematical entities, to describe algorithms, or to capture the intended behavior of digital systems. For digital systems, an ACL2 specification is a mathematical model that is intended to formalize relevant aspects of system behavior. Just as physics allows us to model the behavior of continuous physical systems, ACL2 allows us to model digital systems, including many with physical realizations such as computer hardware. As early as the 1930's Church, Kleene, Turing and others established that recursive functions provide an expressive formalism for modeling digital computation. Digital computation should be understood in a broad sense, covering a wide variety of activities including almost any systematic or algorithmic activity, or activity that can be reasonably approximated in that way. This ranges from the behavior of a digital circuit to the behavior of a programming language compiler to the behavior of a controller for a physical system (as long as the system can be adequately modeled discretely). All of these have been modeled using ACL2 or its predecessor Nqthm.

    ACL2 is a computational logic in at least three distinct senses. First, the theory of recursive functions is often considered the mathematics of computation. Church conjectured that any ``effective computation'' can be modeled as a recursive function. Thus, ACL2 provides an expressive language for modeling digital systems. Second, many ACL2 specifications are executable. In fact, recursive functions written in ACL2 are Common Lisp functions that can be submitted to any compliant Common Lisp compiler and executed (in an environment where suitable ACL2-specific macros and functions are defined). Third, ACL2 is computational in the sense that calculation is heavily integrated into the reasoning process. Thus, an expression with explicit constant values but no free variables can be simplified by calculation rather than by complex logical manipulations.

    ACL2 is a powerful, automated theorem prover or proof checker. This means that a competent user can utilize the ACL2 system to discover proofs of theorems stated in the ACL2 logic or to check previously discovered proofs. The basic deductive steps in an ACL2-checked proof are often quite large, due to the sophisticated combination of decision procedures, conditional rewriting, mathematical and structural induction, propositional simplification, and complex heuristics to orchestrate the interactions of these capabilities. Unlike some automated proof systems, ACL2 does not produce a formal proof. However, we believe that if ACL2 certifies the ``theoremhood'' of a given conjecture, then such a formal proof exists and, therefore, the theorem is valid. The ultimate result of an ACL2 proof session is a collection of ``events,'' possibly grouped into ``books,'' that can be replayed in ACL2. Therefore, a proof can be independently validated by any ACL2 user.

    ACL2 may be used in purely automated mode in the shallow sense that conjectures are submitted to the prover and the user does not interact with the proof attempt (except possibly to stop it) until the proof succeeds or fails. However, any non-trivial proof attempt is actually interactive, since successful proof ``events'' influence the subsequent behavior of the prover. For example, proving a lemma may introduce a rule that subsequently is used automatically by the prover. Thus, any realistic proof attempt, even in ``automatic'' mode, is really an interactive dialogue with the prover to craft a sequence of events building an appropriate theory and proof rules leading up to the proof of the desired result. Also, ACL2 supports annotating a theorem with ``hints'' designed to guide the proof attempt. By supplying appropriate hints, the user can suggest proof strategies that the prover would not discover automatically. There is a ``proof-tree'' facility (see proof-tree) that allows the user to monitor the progress and structure of a proof attempt in real-time. Exploring failed proof attempts is actually where heavy-duty ACL2 users spend most of their time.

    ACL2 can also be used in a more explicitly interactive mode. The ``proof-checker'' subsystem of ACL2 allows exploration of a proof on a fairly low level including expanding calls of selected function symbols, invoking specific rewrite rules, and selectively navigating around the proof. This facility can be used to gain sufficient insight into the proof to construct an automatic version, or to generate a detailed interactive-style proof that can be replayed in batch mode.

    Because ACL2 is all of these things -- computational logic, specification language, programming system, and theorem prover -- it is more than the sum of its parts. The careful integration of these diverse aspects has produced a versatile automated reasoning system suitable for building highly reliable digital systems. In the remainder of this tutorial, we will illustrate some simple uses of this automated reasoning system.

    ABOUT THIS TUTORIAL

    ACL2 is a complex system with a vast array of features, bells and whistles. However, it is possible to perform productive work with the system using only a small portion of the available functionality. The goals of this tutorial are to:

    familiarize the new user with the most basic features of and modes of interaction with ACL2;

    familiarize her with the form of output of the system; and

    work through a graduated series of examples.

    The more knowledge the user brings to this system, the easier it will be to become proficient. On one extreme: the ideal user of ACL2 is an expert Common Lisp programmer, has deep understanding of automated reasoning, and is intimately familiar with the earlier Nqthm system. Such ideal users are unlikely to need this tutorial. However, without some background knowledge, the beginning user is likely to become extremely confused and frustrated by this system. We suggest that a new user of ACL2 should:

    (a) have a little familiarity with Lisp, including basic Lisp programming and prefix notation (a Lisp reference manual such as Guy Steele's ``Common Lisp: The Language'' is also helpful);

    (b) be convinced of the utility of formal modeling; and

    (c) be willing to gain familiarity with basic automated theorem proving topics such as rewriting and algebraic simplification.

    We will not assume any deep familiarity with Nqthm (the so-called ``Boyer-Moore Theorem Prover''), though the book ``A Computational Logic Handbook'' by Boyer and Moore (Academic Press, 1988) is an extremely useful reference for many of the topics required to become a competent ACL2 user. We'll refer to it as ACLH below.

    As we said in the introduction, ACL2 has various facets. For example, it can be used as a Common Lisp programming system to construct application programs. In fact, the ACL2 system itself is a large Common Lisp program constructed almost entirely within ACL2. Another use of ACL2 is as a specification and modeling tool. That is the aspect we will concentrate on in the remainder of this tutorial.

    GETTING STARTED

    This section is an abridged version of what's available elsewhere; feel free to see startup for more details.

    How you start ACL2 will be system dependent, but you'll probably type something like ``acl2'' at your operating system prompt. Consult your system administrator for details.

    When you start up ACL2, you'll probably find yourself inside the ACL2 command loop, as indicated by the following prompt.

    
      ACL2 !>
    
    
    If not, you should type (LP). See lp, which has a lot more information about the ACL2 command loop.

    There are two ``modes'' for using ACL2, :logic and :program. When you begin ACL2, you will ordinarily be in the :logic mode. This means that any new function defined is not only executable but also is axiomatically defined in the ACL2 logic. (See defun-mode and see default-defun-mode.) Roughly speaking, :program mode is available for using ACL2 as a programming language without some of the logical burdens necessary for formal reasoning. In this tutorial we will assume that we always remain in :logic mode and that our purpose is to write formal models of digital systems and to reason about them.

    Now, within the ACL2 command loop you can carry out various kinds of activities, including the folllowing. (We'll see examples later of many of these.)

    define new functions (see defun);

    execute functions on concrete data;

    pose and attempt to prove conjectures about previously defined functions (see defthm);

    query the ACL2 ``world'' or database (e.g., see pe); and

    numerous other things.

    In addition, there is extensive on-line documentation, of which this tutorial introduction is a part.

    INTERACTING WITH ACL2

    The standard means of interacting with ACL2 is to submit a sequence of forms for processing by the ACL2 system. These forms are checked for syntactic and semantic acceptability and appropriately processed by the system. These forms can be typed directly at the ACL2 prompt. However, most successful ACL2 users prefer to do their work using the Emacs text editor, maintaining an Emacs ``working'' buffer in which forms are edited. Those forms are then copied to the ACL2 interaction buffer, which is often the "*shell*" buffer.

    In some cases, processing succeeds and makes some change to the ACL2 ``logical world,'' which affects the processing of subsequent forms. How can this processing fail? For example, a proposed theorem will be rejected unless all function symbols mentioned have been previously defined. Also the ability of ACL2 to discover the proof of a theorem may depend on the user previously having proved other theorems. Thus, the order in which forms are submitted to ACL2 is quite important. Maintaining forms in an appropriate order in your working buffer will be helpful for re-playing the proof later.

    One of the most common events in constructing a model is introducing new functions. New functions are usually introduced using the defun form; we'll encounter some exceptions later. Proposed function definitions are checked to make sure that they are syntactically and semantically acceptable (e.g., that all mentioned functions have been previously defined) and, for recursive functions, that their recursive calls terminate. A recursive function definition is guaranteed to terminate if there is some some ``measure'' of the arguments and a ``well-founded'' ordering such that the arguments to the function get smaller in each recursive call. See well-founded-relation.

    For example, suppose that we need a function that will append two lists together. (We already have one in the ACL2 append function; but suppose perversely that we decide to define our own.) Suppose we submit the following definition (you should do so as well and study the system output):

    
      (defun my-app (x y)
        (if (atom x)
            y
          (cons (car x) (my-app x y))))
    
    
    The system responds with the following message:
    
      ACL2 Error in ( DEFUN MY-APP ...):  No :MEASURE was supplied with
      the definition of MY-APP.  Our heuristics for guessing one have not
      made any suggestions.  No argument of the function is tested along
      every branch and occurs as a proper subterm at the same argument
      position in every recursive call.  You must specify a :MEASURE.  See
      :DOC defun.
    
    
    This means that the system could not find an expression involving the formal parameters x and y that decreases under some well-founded order in every recursive call (there is only one such call). It should be clear that there is no such measure in this case because the only recursive call doesn't change the arguments at all. The definition is obviously flawed; if it were accepted and executed it would loop forever. Notice that a definition that is rejected is not stored in the system database; there is no need to take any action to have it ``thrown away.'' Let's try again with the correct definition. The interaction now looks like (we're also putting in the ACL2 prompt; you don't type that):
    
      ACL2 !>(defun my-app (x y)
               (if (atom x)
                   y
                 (cons (car x) (my-app (cdr x) y))))
    
      The admission of MY-APP is trivial, using the relation O<
      (which is known to be well-founded on the domain recognized by
      O-P) and the measure (ACL2-COUNT X).  We observe that the
      type of MY-APP is described by the theorem
      (OR (CONSP (MY-APP X Y)) (EQUAL (MY-APP X Y) Y)).
      We used primitive type reasoning.
    
      Summary
      Form:  ( DEFUN MY-APP ...)
      Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
      Warnings:  None
      Time:  0.07 seconds (prove: 0.00, print: 0.00, other: 0.07)
      MY-APP
    
    
    Notice that this time the function definition was accepted. We didn't have to supply a measure explicitly; the system inferred one from the form of the definition. On complex functions it may be necessary to supply a measure explicitly. (See xargs.)

    The system output provides several pieces of information.

    The revised definition is acceptable. The system realized that there is a particular measure (namely, (acl2-count x)) and a well-founded relation (o<) under which the arguments of my-app get smaller in recursion. Actually, the theorem prover proved several theorems to admit my-app. The main one was that when (atom x) is false the acl2-count of (cdr x) is less than (in the o< sense) the acl2-count of x. Acl2-count is the most commonly used measure of the ``size`` of an ACL2 object. o< is the ordering relation on ordinals less than epsilon-0. On the natural numbers it is just ordinary ``<''.

    The observation printed about ``the type of MY-APP'' means that calls of the function my-app will always return a value that is either a cons pair or is equal to the second parameter.

    The summary provides information about which previously introduced definitions and lemmas were used in this proof, about some notable things to watch out for (the Warnings), and about how long this event took to process.

    Usually, it's not important to read this information. However, it is a good habit to scan it briefly to see if the type information is surprising to you or if there are Warnings. We'll see an example of them later.

    After a function is accepted, it is stored in the database and available for use in other function definitions or lemmas. To see the definition of any function use the :pe command (see pe). For example,

    
      ACL2 !>:pe my-app
       L       73:x(DEFUN MY-APP (X Y)
                          (IF (ATOM X)
                              Y (CONS (CAR X) (MY-APP (CDR X) Y))))
    
    
    This displays the definition along with some other relevant information. In this case, we know that this definition was processed in :logic mode (the ``L'') and was the 73rd command processed in the current session.

    We can also try out our newly defined function on some sample data. To do that, just submit a form to be evaluated to ACL2. For example,

    
      ACL2 !>(my-app '(0 1 2) '(3 4 5))
      (0 1 2 3 4 5)
      ACL2 !>(my-app nil nil)
      NIL
      ACL2 !>
    
    

    Now suppose we want to prove something about the function just introduced. We conjecture, for example, that the length of the append of two lists is the sum of their lengths. We can formulate this conjecture in the form of the following ACL2 defthm form.

    
      (defthm my-app-length
        (equal (len (my-app x y))
               (+ (len x) (len y))))
    
    
    First of all, how did we know about the functions len and +, etc.? The answer to that is somewhat unsatisfying -- we know them from our past experience in using Common Lisp and ACL2. It's hard to know that a function such as len exists without first knowing some Common Lisp. If we'd guessed that the appropriate function was called length (say, from our knowledge of Lisp) and tried :pe length, we would have seen that length is defined in terms of len, and we could have explored from there. Luckily, you can write a lot of ACL2 functions without knowing too many of the primitive functions.

    Secondly, why don't we need some ``type'' hypotheses? Does it make sense to append things that are not lists? Well, yes. ACL2 and Lisp are both quite weakly typed. For example, inspection of the definition of my-app shows that if x is not a cons pair, then (my-app x y) always returns y, no matter what y is.

    Thirdly, would it matter if we rewrote the lemma with the equality reversed, as follows?

    
      (defthm my-app-length2
        (equal (+ (len x) (len y))
               (len (my-app x y)))).
    
    
    The two are logically equivalent, but...yes, it would make a big difference. Recall our remark that a lemma is not only a ``fact'' to be proved; it also is used by the system to prove other later lemmas. The current lemma would be stored as a rewrite rule. (See rule-classes.) For a rewrite rule, a conclusion of the form (EQUAL LHS RHS) means to replace instances of the LHS by the appropriate instance of the RHS. Presumably, it's better to rewrite (len (my-app x y)) to (+ (len x) (len y)) than the other way around. The reason is that the system ``knows'' more about + than it does about the new function symbol my-app.

    So let's see if we can prove this lemma. Submitting our preferred defthm to ACL2 (do it!), we get the following interaction:

              --------------------------------------------------
    ACL2 !>(defthm my-app-length
      (equal (len (my-app x y))
             (+ (len x) (len y))))
    
    Name the formula above *1.
    
    Perhaps we can prove *1 by induction.  Three induction schemes are
    suggested by this conjecture.  These merge into two derived
    induction schemes.  However, one of these is flawed and so we are
    left with one viable candidate.
    
    We will induct according to a scheme suggested by (LEN X), but
    modified to accommodate (MY-APP X Y).  If we let (:P X Y) denote *1
    above then the induction scheme we'll use is
    (AND (IMPLIES (NOT (CONSP X)) (:P X Y))
         (IMPLIES (AND (CONSP X) (:P (CDR X) Y))
                  (:P X Y))).
    This induction is justified by the same argument used to admit LEN,
    namely, the measure (ACL2-COUNT X) is decreasing according to the
    relation O< (which is known to be well-founded on the domain
    recognized by O-P).  When applied to the goal at hand the
    above induction scheme produces the following two nontautological
    subgoals.
    
    Subgoal *1/2
    (IMPLIES (NOT (CONSP X))
             (EQUAL (LEN (MY-APP X Y))
                    (+ (LEN X) (LEN Y)))).
    
    But simplification reduces this to T, using the :definitions of FIX,
    LEN and MY-APP, the :type-prescription rule LEN, the :rewrite rule
    UNICITY-OF-0 and primitive type reasoning.
    
    Subgoal *1/1
    (IMPLIES (AND (CONSP X)
                  (EQUAL (LEN (MY-APP (CDR X) Y))
                         (+ (LEN (CDR X)) (LEN Y))))
             (EQUAL (LEN (MY-APP X Y))
                    (+ (LEN X) (LEN Y)))).
    
    This simplifies, using the :definitions of LEN and MY-APP, primitive
    type reasoning and the :rewrite rules COMMUTATIVITY-OF-+ and
    CDR-CONS, to
    
    Subgoal *1/1'
    (IMPLIES (AND (CONSP X)
                  (EQUAL (LEN (MY-APP (CDR X) Y))
                         (+ (LEN Y) (LEN (CDR X)))))
             (EQUAL (+ 1 (LEN (MY-APP (CDR X) Y)))
                    (+ (LEN Y) 1 (LEN (CDR X))))).
    
    But simplification reduces this to T, using linear arithmetic,
    primitive type reasoning and the :type-prescription rule LEN.
    
    That completes the proof of *1.
    
    Q.E.D.
    
    Summary
    Form:  ( DEFTHM MY-APP-LENGTH ...)
    Rules: ((:REWRITE UNICITY-OF-0)
            (:DEFINITION FIX)
            (:REWRITE COMMUTATIVITY-OF-+)
            (:DEFINITION LEN)
            (:REWRITE CDR-CONS)
            (:DEFINITION MY-APP)
            (:TYPE-PRESCRIPTION LEN)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:FAKE-RUNE-FOR-LINEAR NIL))
    Warnings:  None
    Time:  0.30 seconds (prove: 0.13, print: 0.05, other: 0.12)
     MY-APP-LENGTH
              --------------------------------------------------
    

    Wow, it worked! In brief, the system first tried to rewrite and simplify as much as possible. Nothing changed; we know that because it said ``Name the formula above *1.'' Whenever the system decides to name a formula in this way, we know that it has run out of techniques to use other than proof by induction.

    The induction performed by ACL2 is structural or ``Noetherian'' induction. You don't need to know much about that except that it is induction based on the structure of some object. The heuristics infer the structure of the object from the way the object is recursively decomposed by the functions used in the conjecture. The heuristics of ACL2 are reasonably good at selecting an induction scheme in simple cases. It is possible to override the heuristic choice by providing an :induction hint (see hints). In the case of the theorem above, the system inducts on the structure of x as suggested by the decomposition of x in both (my-app x y) and (len x). In the base case, we assume that x is not a consp. In the inductive case, we assume that it is a consp and assume that the conjecture holds for (cdr x).

    There is a close connection between the analysis that goes on when a function like my-app is accepted and when we try to prove something inductively about it. That connection is spelled out well in Boyer and Moore's book ``A Computational Logic,'' if you'd like to look it up. But it's pretty intuitive. We accepted my-app because the ``size'' of the first argument x decreases in the recursive call. That tells us that when we need to prove something inductively about my-app, it's a good idea to try an induction on the size of the first argument. Of course, when you have a theorem involving several functions, it may be necessary to concoct a more complicated induction schema, taking several of them into account. That's what's meant by ``merging'' the induction schemas.

    The proof involves two cases: the base case, and the inductive case. You'll notice that the subgoal numbers go down rather than up, so you always know how many subgoals are left to process. The base case (Subgoal *1/2) is handled by opening up the function definitions, simplifying, doing a little rewriting, and performing some reasoning based on the types of the arguments. You'll often encounter references to system defined lemmas (like unicity-of-0). You can always look at those with :pe; but, in general, assume that there's a lot of simplification power under the hood that's not too important to understand fully.

    The inductive case (Subgoal *1/1) is also dispatched pretty easily. Here we assume the conjecture true for the cdr of the list and try to prove it for the entire list. Notice that the prover does some simplification and then prints out an updated version of the goal (Subgoal *1/1'). Examining these gives you a pretty good idea of what's going on in the proof.

    Sometimes one goal is split into a number of subgoals, as happened with the induction above. Sometimes after some initial processing the prover decides it needs to prove a subgoal by induction; this subgoal is given a name and pushed onto a stack of goals. Some steps, like generalization (see ACLH), are not necessarily validity preserving; that is, the system may adopt a false subgoal while trying to prove a true one. (Note that this is ok in the sense that it is not ``unsound.'' The system will fail in its attempt to establish the false subgoal and the main proof attempt will fail.) As you gain facility with using the prover, you'll get pretty good at recognizing what to look for when reading a proof script. The prover's proof-tree utility helps with monitoring an ongoing proof and jumping to designated locations in the proof (see proof-tree). See tips for a number of useful pointers on using the theorem prover effectively.

    When the prover has successfully proved all subgoals, the proof is finished. As with a defun, a summary of the proof is printed. This was an extremely simple proof, needing no additional guidance. More realistic examples typically require the user to look carefully at the failed proof log to find ways to influence the prover to do better on its next attempt. This means either: proving some rules that will then be available to the prover, changing the global state in ways that will affect the proof, or providing some hints locally that will influence the prover's behavior. Proving this lemma (my-app-length) is an example of the first. Since this is a rewrite rule, whenever in a later proof an instance of the form (LEN (MY-APP X Y)) is encountered, it will be rewritten to the corresponding instance of (+ (LEN X) (LEN Y)). Disabling the rule by executing the command

    
      (in-theory (disable my-app-length)),
    
    
    is an example of a global change to the behavior of the prover since this rewrite will not be performed subsequently (unless the rule is again enabled). Finally, we can add a (local) disable ``hint'' to a defthm, meaning to disable the lemma only in the proof of one or more subgoals. For example:
    
      (defthm my-app-length-commutativity
        (equal (len (my-app x y))
               (len (my-app y x)))
        :hints (("Goal" :in-theory (disable my-app-length))))
    
    
    In this case, the hint supplied is a bad idea since the proof is much harder with the hint than without it. Try it both ways.

    By the way, to undo the previous event use :u (see u). To undo back to some earlier event use :ubt (see ubt). To view the current event use :pe :here. To list several events use :pbt (see pbt).

    Notice the form of the hint in the previous example (see hints). It specifies a goal to which the hint applies. "Goal" refers to the top-level goal of the theorem. Subgoals are given unique names as they are generated. It may be useful to suggest that a function symbol be disabled only for Subgoal 1.3.9, say, and a different function enabled only on Subgoal 5.2.8. Overuse of such hints often suggests a poor global proof strategy.

    We now recommend that you visit documentation on additional examples. See annotated-acl2-scripts.




    acl2-sources/doc/HTML/AND.html0000664002132200015000000000137212222333523015354 0ustar kaufmannacl2 AND.html -- ACL2 Version 6.3

    AND

    conjunction
    Major Section:  ACL2-BUILT-INS
    

    And is the macro for conjunctions. And takes any number of arguments. And returns nil if one of the arguments is nil, but otherwise returns the last argument. If there are no arguments, and returns t.

    And is a Common Lisp macro. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/ANNOTATED-ACL2-SCRIPTS.html0000664002132200015000000001120612222333515020071 0ustar kaufmannacl2 ANNOTATED-ACL2-SCRIPTS.html -- ACL2 Version 6.3

    ANNOTATED-ACL2-SCRIPTS

    examples of ACL2 scripts
    Major Section:  ACL2-TUTORIAL
    

    Beginning users may find these annotated scripts useful. We suggest that you read these in the following order:

    Tutorial1-Towers-of-Hanoi
    Tutorial2-Eights-Problem
    Tutorial3-Phonebook-Example
    Tutorial4-Defun-Sk-Example
    Tutorial5-Miscellaneous-Examples
    

    The page http://www.cs.utexas.edu/users/moore/publications/tutorial/rev3.html contains a script that illustrates how it feels to use The Method to prove an unusual list reverse function correct. The screen shots of ACL2's proof output are outdated -- in the version shown, ACL2 does not print Key Checkpoints, but the concept of key checkpoint is clear in the discussion and the behavior of the user.

    See http://www.cs.utexas.edu/users/moore/acl2/contrib/POLISHING-PROOFS-TUTORIAL.html for a tutorial on becoming successful at approaching a formalization and proof problem in ACL2. That tutorial, written by Shilpi Goel and Sandip Ray, has two parts: it illustrates how to guide the theorem prover to a successful proof, and it shows how to clean up the proof in order to facilitate maintenance and extension of the resulting book (see books).

    At http://www.cs.utexas.edu/users/moore/publications/tutorial/kaufmann-TPHOLs08/index.html is the demo given by Matt Kaufmann at TPHOLs08, including all the scripts. There is a gzipped tar file containing the entire contents of the demos.

    At http://www.cs.utexas.edu/users/moore/publications/tutorial/sort-equivalence is a collection of scripts illustrating both high-level strategy and lower-level tactics dealing with the functional equivalence of various list sorting algorithms. Start with the README on that directory. There is also a gzipped tar file containing all the scripts, at http://www.cs.utexas.edu/users/moore/publications/tutorial/sort-equivalence.tgz.

    When you feel you have read enough examples, you might want to try the following very simple example on your own. (See solution-to-simple-example for a solution, after you work on this example.) First define the notion of the ``fringe'' of a tree, where we identify trees simply as cons structures, with atoms at the leaves. For example:

    
      ACL2 !>(fringe '((a . b) c . d))
      (A B C D)
    
    
    Next, define the notion of a ``leaf'' of a tree, i.e., a predicate leaf-p that is true of an atom if and only if that atom appears at the tip of the tree. Define this notion without referencing the function fringe. Finally, prove the following theorem, whose proof may well be automatic (i.e., not require any lemmas).
    
      (defthm leaf-p-iff-member-fringe
        (iff (leaf-p atm x)
             (member-equal atm (fringe x))))
    
    

    Some Related Topics




    acl2-sources/doc/HTML/APPEND.html0000664002132200015000000000211112222333523015711 0ustar kaufmannacl2 APPEND.html -- ACL2 Version 6.3

    APPEND

    concatenate zero or more lists
    Major Section:  ACL2-BUILT-INS
    

    Append, which takes zero or more arguments, expects all the arguments except perhaps the last to be true (null-terminated) lists. It returns the result of concatenating all the elements of all the given lists into a single list. Actually, in ACL2 append is a macro that expands into calls of the binary function binary-append if there are at least two arguments; if there is just one argument then the expansion is that argument; and finally, (append) expands to nil.

    Append is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/APROPOS.html0000664002132200015000000000065712222333520016077 0ustar kaufmannacl2 APROPOS.html -- ACL2 Version 6.3

    APROPOS

    See finding-documentation.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/ARCHITECTURE-OF-THE-PROVER.html0000664002132200015000000001056312222333515020572 0ustar kaufmannacl2 ARCHITECTURE-OF-THE-PROVER.html -- ACL2 Version 6.3

    ARCHITECTURE-OF-THE-PROVER

    a simple overview of how the prover works
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Six built-in proof techniques are used by ACL2 to decompose the goal formula into subgoals.

    simplification -- decision procedures and rewriting with previously proved rules, but actually including a host of other techniques under your control. Simplification is the only proof technique that can reduce a formula to 0 subgoals (i.e., prove it) rather than just transform it to other formulas. The predominant activity in most proofs is simplification. There are many ways you can affect what the simplifier does to your formulas. Good users spend most of their time thinking about how to control the simplifier.

    destructor elimination -- getting rid of ``destructor terms'' like (CAR X) and (CDR X) by replacing a variable, e.g., X, by a ``constructor'' term, e.g., (CONS A B). But you can tell ACL2 about new destructor/constructor combinations.

    cross-fertilization -- using an equivalence hypothesis by substituting one side for the other into the conclusion and then throwing the hypothesis away. This is a heuristic that helps use an inductive hypothesis and prepare for another induction.

    generalization -- replacing a term by a new variable and restricting the new variable to have some of the properties of the term. You can control the restrictions imposed on the new variable. This is a heuristic that prepares the goal for another induction.

    elimination of irrelevance -- throwing away unnecessary hypotheses. This is a heuristic that prepares the goal for another induction.

    induction -- selecting an induction scheme to prove a formula. Inductions are ``suggested'' by the recursive functions appearing in the formula. But you can control what inductions are suggested by terms.

    But you can add additional techniques, called clause processors.

    The various techniques are tried in turn, with simplification first and induction last. Each technique reports one of three outcomes: it found nothing to change (i.e., the technique doesn't apply to that subgoal), it decided to abort the proof attempt (typically because there is reason to believe the proof is failing), or it decomposed the goal into k subgoals.

    The last outcome has a special case: if k is 0 then the technique proved the goal. Whenever k is non-0, the process starts over again with simplification on each of the k subgoals. However, it saves up all the subgoals for which induction is the only proof technique left to try. That way you see how it performs on every base case and induction step of one induction before it launches into another induction.

    It runs until you or one of the proof techniques aborts the proof attempt or until all subgoals have been proved.

    Note that if simplification produces a subgoal, that subgoal is re-simplified. This process continues until the subgoal cannot be simplified further. Only then is the next proof technique is tried. Such suboals are said to be stable under simplification.

    While this is happening, the prover prints an English narrative describing the process. Basically, after each goal is printed, the system prints an English paragraph that names the next applicable proof technique, gives a brief description of what that technique does to the subgoal, and says how many new subgoals are produced. Then each subgoal is dealt with in turn.

    If the proof is successful, you could read this log as a proof of the conjecture. But output from successful proofs is generally never read because it is not important to The Method described in introduction-to-the-theorem-prover.

    The output of an unsuccessful proof attempt concludes with some key checkpoints which usually bear looking at.




    acl2-sources/doc/HTML/AREF1.html0000664002132200015000000000235612222333525015555 0ustar kaufmannacl2 AREF1.html -- ACL2 Version 6.3

    AREF1

    access the elements of a 1-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (aref1 'delta1 a (+ i k))
    
    General Form:
    (aref1 name alist index)
    
    where name is a symbol, alist is a 1-dimensional array and index is a legal index into alist. This function returns the value associated with index in alist, or else the default value of the array. See arrays for details.

    This function executes in virtually constant time if alist is in fact the ``semantic value'' associated with name (see arrays). When it is not, aref1 must do a linear search through alist. In that case the correct answer is returned but a slow array comment is printed to the comment window. See slow-array-warning.




    acl2-sources/doc/HTML/AREF2.html0000664002132200015000000000237212222333525015554 0ustar kaufmannacl2 AREF2.html -- ACL2 Version 6.3

    AREF2

    access the elements of a 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (aref2 'delta1 a i j)
    
    General Form:
    (aref2 name alist i j)
    
    where name is a symbol, alist is a 2-dimensional array and i and j are legal indices into alist. This function returns the value associated with (i . j) in alist, or else the default value of the array. See arrays for details.

    This function executes in virtually constant time if alist is in fact the ``semantic value'' associated with name (see arrays). When it is not, aref2 must do a linear search through alist. In that case the correct answer is returned but a slow array comment is printed to the comment window. See slow-array-warning.




    acl2-sources/doc/HTML/ARGS.html0000664002132200015000000000176412222333516015515 0ustar kaufmannacl2 ARGS.html -- ACL2 Version 6.3

    ARGS

    args, guard, type, constraint, etc., of a function symbol
    Major Section:  DOCUMENTATION
    

    Example:
    :args assoc-eq
    

    Args takes one argument, a symbol which must be the name of a function or macro, and prints out the formal parameters, the guard expression, the output signature, the deduced type, the constraint (if any), and whether documentation about the symbol is available via :doc.




    acl2-sources/doc/HTML/ARRAY1P.html0000664002132200015000000000144412222333525016033 0ustar kaufmannacl2 ARRAY1P.html -- ACL2 Version 6.3

    ARRAY1P

    recognize a 1-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (array1p 'delta1 a)
    
    General Form:
    (array1p name alist)
    
    where name and alist are arbitrary objects. This function returns t if alist is a 1-dimensional ACL2 array. Otherwise it returns nil. The function operates in constant time if alist is the semantic value of name. See arrays.




    acl2-sources/doc/HTML/ARRAY2P.html0000664002132200015000000000144412222333525016034 0ustar kaufmannacl2 ARRAY2P.html -- ACL2 Version 6.3

    ARRAY2P

    recognize a 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (array2p 'delta1 a)
    
    General Form:
    (array2p name alist)
    
    where name and alist are arbitrary objects. This function returns t if alist is a 2-dimensional ACL2 array. Otherwise it returns nil. The function operates in constant time if alist is the semantic value of name. See arrays.




    acl2-sources/doc/HTML/ARRAYS-EXAMPLE.html0000664002132200015000000000554012222333525017047 0ustar kaufmannacl2 ARRAYS-EXAMPLE.html -- ACL2 Version 6.3

    ARRAYS-EXAMPLE

    an example illustrating ACL2 arrays
    Major Section:  ARRAYS
    

    The example below illustrates the use of ACL2 arrays. It is not, of course, a substitute for the detailed explanations provided elsewhere (see arrays, including subtopics).

    ACL2 !>(defun defarray (name size initial-element)
             (compress1 name
                        (cons (list :HEADER
                                    :DIMENSIONS (list size)
                                    :MAXIMUM-LENGTH (1+ size)
                                    :DEFAULT initial-element
                                    :NAME name)
                              nil)))
    
    Since DEFARRAY is non-recursive, its admission is trivial.  We observe
    that the type of DEFARRAY is described by the theorem
    (AND (CONSP (DEFARRAY NAME SIZE INITIAL-ELEMENT))
         (TRUE-LISTP (DEFARRAY NAME SIZE INITIAL-ELEMENT))).
    We used the :type-prescription rule COMPRESS1.
    
    Summary
    Form:  ( DEFUN DEFARRAY ...)
    Rules: ((:TYPE-PRESCRIPTION COMPRESS1))
    Warnings:  None
    Time:  0.02 seconds (prove: 0.00, print: 0.02, other: 0.00)
     DEFARRAY
    ACL2 !>(assign my-ar (defarray 'a1 5 17))
     ((:HEADER :DIMENSIONS (5)
               :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1))
    ACL2 !>(aref1 'a1 (@ my-ar) 3)
    17
    ACL2 !>(aref1 'a1 (@ my-ar) 8)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol AREF1,
    which is
    (AND (ARRAY1P NAME L) (INTEGERP N) (>= N 0) (< N (CAR (DIMENSIONS NAME L)))),
    is violated by the arguments in the call (AREF1 'A1 '(#) 8).
    
    ACL2 !>(assign my-ar (aset1 'a1 (@ my-ar) 3 'xxx))
     ((3 . XXX)
      (:HEADER :DIMENSIONS (5)
               :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1))
    ACL2 !>(aref1 'a1 (@ my-ar) 3)
    XXX
    ACL2 !>(aset1 'a1 (@ my-ar) 3 'yyy) ; BAD: (@ my-ar) now points to
                                        ;      an old copy of the array!
    ((3 . YYY)
     (3 . XXX)
     (:HEADER :DIMENSIONS (5)
              :MAXIMUM-LENGTH 6 :DEFAULT 17 :NAME A1))
    ACL2 !>(aref1 'a1 (@ my-ar) 3) ; Because of "BAD" above, the array
                                   ; access is done using assoc rather
                                   ; than Lisp aref, hence is slower;
                                   ; but the answer is still correct,
                                   ; reflecting the value in (@ my-ar),
                                   ; which was not changed above.
    
    
    **********************************************************
    Slow Array Access!  A call of AREF1 on an array named
    A1 is being executed slowly.  See :DOC slow-array-warning
    **********************************************************
    
    XXX
    ACL2 !>
    




    acl2-sources/doc/HTML/ARRAYS.html0000664002132200015000000007134612222333525015765 0ustar kaufmannacl2 ARRAYS.html -- ACL2 Version 6.3

    ARRAYS

    an introduction to ACL2 arrays
    Major Section:  PROGRAMMING
    

    Below we begin a detailed presentation of ACL2 arrays. ACL2's single-threaded objects (see stobj) provide a similar functionality that is generally more efficient when there are updates (writes), but is also more restrictive.

    Some Related Topics

    • AREF1 -- access the elements of a 1-dimensional array

    • AREF2 -- access the elements of a 2-dimensional array

    • ARRAY1P -- recognize a 1-dimensional array

    • ARRAY2P -- recognize a 2-dimensional array

    • ARRAYS-EXAMPLE -- an example illustrating ACL2 arrays

    • ASET1 -- set the elements of a 1-dimensional array

    • ASET2 -- set the elements of a 2-dimensional array

    • COMPRESS1 -- remove irrelevant pairs from a 1-dimensional array

    • COMPRESS2 -- remove irrelevant pairs from a 2-dimensional array

    • DEFAULT -- return the :default from the header of a 1- or 2-dimensional array

    • DIMENSIONS -- return the :dimensions from the header of a 1- or 2-dimensional array

    • FLUSH-COMPRESS -- flush the under-the-hood array for the given name

    • HEADER -- return the header of a 1- or 2-dimensional array

    • MAXIMUM-LENGTH -- return the :maximum-length from the header of an array

    • SLOW-ARRAY-WARNING -- a warning or error issued when arrays are used inefficiently

    See arrays-example for a brief introduction illustrating the use of ACL2 arrays.

    ACL2 provides relatively efficient 1- and 2-dimensional arrays. Arrays are awkward to provide efficiently in an applicative language because the programmer rightly expects to be able to ``modify'' an array object with the effect of changing the behavior of the element accessing function on that object. This, of course, does not make any sense in an applicative setting. The element accessing function is, after all, a function, and its behavior on a given object is immutable. To ``modify'' an array object in an applicative setting we must actually produce a new array object. Arranging for this to be done efficiently is a challenge to the implementors of the language. In addition, the programmer accustomed to the von Neumann view of arrays must learn how to use immutable applicative arrays efficiently.

    In this note we explain 1-dimensional arrays. In particular, we explain briefly how to create, access, and ``modify'' them, how they are implemented, and how to program with them. 2-dimensional arrays are dealt with by analogy.

    The Logical Description of ACL2 Arrays

    An ACL2 1-dimensional array is an object that associates arbitrary objects with certain integers, called ``indices.'' Every array has a dimension, dim, which is a positive integer. The indices of an array are the consecutive integers from 0 through dim-1. To obtain the object associated with the index i in an array a, one uses (aref1 name a i). Name is a symbol that is irrelevant to the semantics of aref1 but affects the speed with which it computes. We will talk more about array ``names'' later. To produce a new array object that is like a but which associates val with index i, one uses (aset1 name a i val).

    An ACL2 1-dimensional array is actually an alist. There is no special ACL2 function for creating arrays; they are generally built with the standard list processing functions list and cons. However, there is a special ACL2 function, called compress1, for speeding up access to the elements of such an alist. We discuss compress1 later.

    One element of the alist must be the ``header'' of the array. The header of a 1-dimensional array with dimension dim is of the form:

    (:HEADER :DIMENSIONS (dim)
             :MAXIMUM-LENGTH max
             :DEFAULT obj ; optional
             :NAME name   ; optional
             :ORDER order ; optional values are < (the default), >, or :none/nil
             ).
    
    Obj may be any object and is called the ``default value'' of the array. Max must be an integer greater than dim. Name must be a symbol. The :default and :name entries are optional; if :default is omitted, the default value is nil. The function header, when given a name and a 1- or 2-dimensional array, returns the header of the array. The functions dimensions, maximum-length, and default are similar and return the corresponding fields of the header of the array. The role of the :dimensions field is obvious: it specifies the legal indices into the array. The roles played by the :maximum-length and :default fields are described below.

    Aside from the header, the other elements of the alist must each be of the form (i . val), where i is an integer and 0 <= i < dim, and val is an arbitrary object.

    The :order field of the header is ignored for 2-dimensional arrays. For 1-dimensional arrays, it specifies the order of keys (i, above) when the array is compressed as with compress1, as described below. An :order of :none or nil specifies no reordering of the alist by compress1, and an order of > specifies reordering by compress1 so that keys are in descending order. Otherwise, the alist is reordered by compress1 so that keys are in ascending order.

    (Aref1 name a i) is guarded so that name must be a symbol, a must be an array and i must be an index into a. The value of (aref1 name a i) is either (cdr (assoc i a)) or else is the default value of a, depending on whether there is a pair in a whose car is i. Note that name is irrelevant to the value of an aref1 expression. You might :pe aref1 to see how simple the definition is.

    (Aset1 name a i val) is guarded analogously to the aref1 expression. The value of the aset1 expression is essentially (cons (cons i val) a). Again, name is irrelevant. Note (aset1 name a i val) is an array, a', with the property that (aref1 name a' i) is val and, except for index i, all other indices into a' produce the same value as in a. Note also that if a is viewed as an alist (which it is) the pair ``binding'' i to its old value is in a' but ``covered up'' by the new pair. Thus, the length of an array grows by one when aset1 is done.

    Because aset1 covers old values with new ones, an array produced by a sequence of aset1 calls may have many irrelevant pairs in it. The function compress1 can remove these irrelevant pairs. Thus, (compress1 name a) returns an array that is equivalent (vis-a-vis aref1) to a but which may be shorter. For technical reasons, the alist returned by compress1 may also list the pairs in a different order than listed in a.

    To prevent arrays from growing excessively long due to repeated aset1 operations, aset1 actually calls compress1 on the new alist whenever the length of the new alist exceeds the :maximum-length entry, max, in the header of the array. See the definition of aset1 (for example by using :pe). This is primarily just a mechanism for freeing up cons space consumed while doing aset1 operations. Note however that this compress1 call is replaced by a hard error if the header specifies an :order of :none or nil.

    This completes the logical description of 1-dimensional arrays. 2-dimensional arrays are analogous. The :dimensions entry of the header of a 2-dimensional array should be (dim1 dim2). A pair of indices, i and j, is legal iff 0 <= i < dim1 and 0 <= j < dim2. The :maximum-length must be greater than dim1*dim2. Aref2, aset2, and compress2 are like their counterparts but take an additional index argument. Finally, the pairs in a 2-dimensional array are of the form ((i . j) . val).

    The Implementation of ACL2 Arrays

    Very informally speaking, the function compress1 ``creates'' an ACL2 array that provides fast access, while the function aref1 ``maintains'' fast access. We now describe this informal idea more carefully.

    Aref1 is essentially assoc. If aref1 were implemented naively the time taken to access an array element would be linear in the dimension of the array and the number of ``assignments'' to it (the number of aset1 calls done to create the array from the initial alist). This is intolerable; arrays are ``supposed'' to provide constant-time access and change.

    The apparently irrelevant names associated with ACL2 arrays allow us to provide constant-time access and change when arrays are used in ``conventional'' ways. The implementation of arrays makes it clear what we mean by ``conventional.''

    Recall that array names are symbols. Behind the scenes, ACL2 associates two objects with each ACL2 array name. The first object is called the ``semantic value'' of the name and is an alist. The second object is called the ``raw lisp array'' and is a Common Lisp array.

    When (compress1 name alist) builds a new alist, a', it sets the semantic value of name to that new alist. Furthermore, it creates a Common Lisp array and writes into it all of the index/value pairs of a', initializing unassigned indices with the default value. This array becomes the raw lisp array of name. Compress1 then returns a', the semantic value, as its result, as required by the definition of compress1.

    When (aref1 name a i) is invoked, aref1 first determines whether the semantic value of name is a (i.e., is eq to the alist a). If so, aref1 can determine the ith element of a by invoking Common Lisp's aref function on the raw lisp array associated with name. Note that no linear search of the alist a is required; the operation is done in constant time and involves retrieval of two global variables, an eq test and jump, and a raw lisp array access. In fact, an ACL2 array access of this sort is about 5 times slower than a C array access. On the other hand, if name has no semantic value or if it is different from a, then aref1 determines the answer by linear search of a as suggested by the assoc-like definition of aref1. Thus, aref1 always returns the axiomatically specified result. It returns in constant time if the array being accessed is the current semantic value of the name used. The ramifications of this are discussed after we deal with aset1.

    When (aset1 name a i val) is invoked, aset1 does two conses to create the new array. Call that array a'. It will be returned as the answer. (In this discussion we ignore the case in which aset1 does a compress1.) However, before returning, aset1 determines if name's semantic value is a. If so, it makes the new semantic value of name be a' and it smashes the raw lisp array of name with val at index i, before returning a' as the result. Thus, after doing an aset1 and obtaining a new semantic value a', all aref1s on that new array will be fast. Any aref1s on the old semantic value, a, will be slow.

    To understand the performance implications of this design, consider the chronological sequence in which ACL2 (Common Lisp) evaluates expressions: basically inner-most first, left-to-right, call-by-value. An array use, such as (aref1 name a i), is ``fast'' (constant-time) if the alist supplied, a, is the value returned by the most recently executed compress1 or aset1 on the name supplied. In the functional expression of ``conventional'' array processing, all uses of an array are fast.

    The :name field of the header of an array is completely irrelevant. Our convention is to store in that field the symbol we mean to use as the name of the raw lisp array. But no ACL2 function inspects :name and its primary value is that it allows the user, by inspecting the semantic value of the array -- the alist -- to recall the name of the raw array that probably holds that value. We say ``probably'' since there is no enforcement that the alist was compressed under the name in the header or that all asets used that name. Such enforcement would be inefficient.

    Some Programming Examples

    In the following examples we will use ACL2 ``global variables'' to hold several arrays. See @, and see assign.

    Let the state global variable a be the 1-dimensional compressed array of dimension 5 constructed below.

    ACL2 !>(assign a (compress1 'demo
                                '((:header :dimensions (5)
                                           :maximum-length 15
                                           :default uninitialized
                                           :name demo)
                                  (0 . zero))))
    
    Then (aref1 'demo (@ a) 0) is zero and (aref1 'demo (@ a) 1) is uninitialized.

    Now execute

    ACL2 !>(assign b (aset1 'demo (@ a) 1 'one))
    
    Then (aref1 'demo (@ b) 0) is zero and (aref1 'demo (@ b) 1) is one.

    All of the aref1s done so far have been ``fast.''

    Note that we now have two array objects, one in the global variable a and one in the global variable b. B was obtained by assigning to a. That assignment does not affect the alist a because this is an applicative language. Thus, (aref1 'demo (@ a) 1) must still be uninitialized. And if you execute that expression in ACL2 you will see that indeed it is. However, a rather ugly comment is printed, namely that this array access is ``slow.'' The reason it is slow is that the raw lisp array associated with the name demo is the array we are calling b. To access the elements of a, aref1 must now do a linear search. Any reference to a as an array is now ``unconventional;'' in a conventional language like Ada or Common Lisp it would simply be impossible to refer to the value of the array before the assignment that produced our b.

    Now let us define a function that counts how many times a given object, x, occurs in an array. For simplicity, we will pass in the name and highest index of the array:

    ACL2 !>(defun cnt (name a i x)
             (declare (xargs :guard
                             (and (array1p name a)
                                  (integerp i)
                                  (>= i -1)
                                  (< i (car (dimensions name a))))
                             :mode :logic
                             :measure (nfix (+ 1 i))))
             (cond ((zp (1+ i)) 0) ; return 0 if i is at most -1
                   ((equal x (aref1 name a i))
                    (1+ (cnt name a (1- i) x)))
                   (t (cnt name a (1- i) x))))
    
    To determine how many times zero appears in (@ b) we can execute:
    ACL2 !>(cnt 'demo (@ b) 4 'zero)
    
    The answer is 1. How many times does uninitialized appear in (@ b)?
    ACL2 !>(cnt 'demo (@ b) 4 'uninitialized)
    
    The answer is 3, because positions 2, 3 and 4 of the array contain that default value.

    Now imagine that we want to assign 'two to index 2 and then count how many times the 2nd element of the array occurs in the array. This specification is actually ambiguous. In assigning to b we produce a new array, which we might call c. Do we mean to count the occurrences in c of the 2nd element of b or the 2nd element of c? That is, do we count the occurrences of uninitialized or the occurrences of two? If we mean the former the correct answer is 2 (positions 3 and 4 are uninitialized in c); if we mean the latter, the correct answer is 1 (there is only one occurrence of two in c).

    Below are ACL2 renderings of the two meanings, which we call [former] and [latter]. (Warning: Our description of these examples, and of an example [fast former] that follows, assumes that only one of these three examples is actually executed; for example, they are not executed in sequence. See ``A Word of Warning'' below for more about this issue.)

    (cnt 'demo (aset1 'demo (@ b) 2 'two) 4 (aref1 'demo (@ b) 2))  ; [former]
    
    (let ((c (aset1 'demo (@ b) 2 'two)))                           ; [latter]
      (cnt 'demo c 4 (aref1 'demo c 2)))
    
    Note that in [former] we create c in the second argument of the call to cnt (although we do not give it a name) and then refer to b in the fourth argument. This is unconventional because the second reference to b in [former] is no longer the semantic value of demo. While ACL2 computes the correct answer, namely 2, the execution of the aref1 expression in [former] is done slowly.

    A conventional rendering with the same meaning is

    (let ((x (aref1 'demo (@ b) 2)))                           ; [fast former]
      (cnt 'demo (aset1 'demo (@ b) 2 'two) 4 x))
    
    which fetches the 2nd element of b before creating c by assignment. It is important to understand that [former] and [fast former] mean exactly the same thing: both count the number of occurrences of uninitialized in c. Both are legal ACL2 and both compute the same answer, 2. Indeed, we can symbolically transform [fast former] into [former] merely by substituting the binding of x for x in the body of the let. But [fast former] can be evaluated faster than [former] because all of the references to demo use the then-current semantic value of demo, which is b in the first line and c throughout the execution of the cnt in the second line. [Fast former] is the preferred form, both because of its execution speed and its clarity. If you were writing in a conventional language you would have to write something like [fast former] because there is no way to refer to the 2nd element of the old value of b after smashing b unless it had been saved first.

    We turn now to [latter]. It is both clear and efficient. It creates c by assignment to b and then it fetches the 2nd element of c, two, and proceeds to count the number of occurrences in c. The answer is 1. [Latter] is a good example of typical ACL2 array manipulation: after the assignment to b that creates c, c is used throughout.

    It takes a while to get used to this because most of us have grown accustomed to the peculiar semantics of arrays in conventional languages. For example, in raw lisp we might have written something like the following, treating b as a ``global variable'':

    (cnt 'demo (aset 'demo b 2 'two) 4 (aref 'demo b 2))
    
    which sort of resembles [former] but actually has the semantics of [latter] because the b from which aref fetches the 2nd element is not the same b used in the aset! The array b is destroyed by the aset and b henceforth refers to the array produced by the aset, as written more clearly in [latter].

    A Word of Warning: Users must exercise care when experimenting with [former], [latter] and [fast former]. Suppose you have just created b with the assignment shown above,

    ACL2 !>(assign b (aset1 'demo (@ a) 1 'one))
    
    If you then evaluate [former] in ACL2 it will complain that the aref1 is slow and compute the answer, as discussed. Then suppose you evaluate [latter] in ACL2. From our discussion you might expect it to execute fast -- i.e., issue no complaint. But in fact you will find that it complains repeatedly. The problem is that the evaluation of [former] changed the semantic value of demo so that it is no longer b. To try the experiment correctly you must make b be the semantic value of demo again before the next example is evaluated. One way to do that is to execute
    ACL2 !>(assign b (compress1 'demo (@ b)))
    
    before each expression. Because of issues like this it is often hard to experiment with ACL2 arrays at the top-level. We find it easier to write functions that use arrays correctly and efficiently than to so use them interactively.

    This last assignment also illustrates a very common use of compress1. While it was introduced as a means of removing irrelevant pairs from an array built up by repeated assignments, it is actually most useful as a way of insuring fast access to the elements of an array.

    Many array processing tasks can be divided into two parts. During the first part the array is built. During the second part the array is used extensively but not modified. If your programming task can be so divided, it might be appropriate to construct the array entirely with list processing, thereby saving the cost of maintaining the semantic value of the name while few references are being made. Once the alist has stabilized, it might be worthwhile to treat it as an array by calling compress1, thereby gaining constant time access to it.

    ACL2's theorem prover uses this technique in connection with its implementation of the notion of whether a rune is disabled or not. Associated with every rune is a unique integer index, called its ``nume.'' When each rule is stored, the corresponding nume is stored as a component of the rule. Theories are lists of runes and membership in the ``current theory'' indicates that the corresponding rule is enabled. But these lists are very long and membership is a linear-time operation. So just before a proof begins we map the list of runes in the current theory into an alist that pairs the corresponding numes with t. Then we compress this alist into an array. Thus, given a rule we can obtain its nume (because it is a component) and then determine in constant time whether it is enabled. The array is never modified during the proof, i.e., aset1 is never used in this example. From the logical perspective this code looks quite odd: we have replaced a linear-time membership test with an apparently linear-time assoc after going to the trouble of mapping from a list of runes to an alist of numes. But because the alist of numes is an array, the ``apparently linear-time assoc'' is more apparent than real; the operation is constant-time.




    acl2-sources/doc/HTML/ASET1.html0000664002132200015000000000472412222333525015575 0ustar kaufmannacl2 ASET1.html -- ACL2 Version 6.3

    ASET1

    set the elements of a 1-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (aset1 'delta1 a (+ i k) 27)
    
    General Form:
    (aset1 name alist index val)
    
    where name is a symbol, alist is a 1-dimensional array named name, index is a legal index into alist, and val is an arbitrary object. See arrays for details. Roughly speaking this function ``modifies'' alist so that the value associated with index is val. More precisely, it returns a new array, alist', of the same name and dimension as alist that, under aref1, is everywhere equal to alist except at index where the result is val. That is, (aref1 name alist' i) is (aref1 name alist i) for all legal indices i except index, where (aref1 name alist' i) is val.

    In order to ``modify'' alist, aset1 conses a new pair onto the front. If the length of the resulting alist exceeds the :maximum-length entry in the array header, aset1 compresses the array as with compress1.

    It is generally expected that the ``semantic value'' of name will be alist (see arrays). This function operates in virtually constant time whether this condition is true or not (unless the compress1 operation is required). But the value returned by this function cannot be used efficiently by subsequent aset1 operations unless alist is the semantic value of name when aset1 is executed. Thus, if the condition is not true, aset1 prints a slow array warning to the comment window. See slow-array-warning.




    acl2-sources/doc/HTML/ASET2.html0000664002132200015000000000502412222333525015570 0ustar kaufmannacl2 ASET2.html -- ACL2 Version 6.3

    ASET2

    set the elements of a 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (aset2 'delta1 a i j 27)
    
    General Form:
    (aset2 name alist i j val)
    
    where name is a symbol, alist is a 2-dimensional array named name, i and j are legal indices into alist, and val is an arbitrary object. See arrays for details. Roughly speaking this function ``modifies'' alist so that the value associated with (i . j) is val. More precisely, it returns a new array, alist', of the same name and dimension as alist that, under aref2, is everywhere equal to alist except at (i . j) where the result is val. That is, (aref2 name alist' x y) is (aref2 name alist x y) for all legal indices x y except i and j where (aref2 name alist' i j) is val.

    In order to ``modify'' alist, aset2 conses a new pair onto the front. If the length of the resulting alist exceeds the :maximum-length entry in the array header, aset2 compresses the array as with compress2.

    It is generally expected that the ``semantic value'' of name will be alist (see arrays). This function operates in virtually constant time whether this condition is true or not (unless the compress2 operation is required). But the value returned by this function cannot be used efficiently by subsequent aset2 operations unless alist is the semantic value of name when aset2 is executed. Thus, if the condition is not true, aset2 prints a slow array warning to the comment window. See slow-array-warning.




    acl2-sources/doc/HTML/ASH.html0000664002132200015000000000206712222333523015367 0ustar kaufmannacl2 ASH.html -- ACL2 Version 6.3

    ASH

    arithmetic shift operation
    Major Section:  ACL2-BUILT-INS
    

    (ash i c) is the result of taking the two's complement representation of the integer i and shifting it by c bits: shifting left and padding with c 0 bits if c is positive, shifting right and dropping (abs c) bits if c is negative, and simply returning i if c is 0.

    The guard for ash requires that its arguments are integers.

    Ash is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ASSERT$.html0000664002132200015000000000203512222333523016014 0ustar kaufmannacl2 ASSERT$.html -- ACL2 Version 6.3

    ASSERT$

    cause a hard error if the given test is false
    Major Section:  ACL2-BUILT-INS
    

    General Form:
    (assert$ test form)
    
    where test returns a single value and form is arbitrary. Semantically, this call of assert$ is equivalent to form. However, it causes a hard error if the value of test is nil. That hard error invokes the function illegal, which has a guard that is equal to nil; so if you use assert$ in code for which you verify guards, then a proof obligation will be that the occurrence of test is never nil.




    acl2-sources/doc/HTML/ASSERT-EVENT.html0000664002132200015000000000505212222333516016633 0ustar kaufmannacl2 ASSERT-EVENT.html -- ACL2 Version 6.3

    ASSERT-EVENT

    assert that a given form returns a non-nil value
    Major Section:  EVENTS
    

    Examples:
    (assert-event (equal (+ 3 4) 7))
    (assert-event (equal (+ 3 4) 7) :msg (msg "Error: ~x0" 'equal-check))
    (assert-event (equal (+ 3 4) 7) :on-skip-proofs t)
    
    General Forms:
    (assert-event form)
    (assert-event form :on-skip-proofs t)
    

    Assert-event takes a ground form, i.e., one with no free variables; stobjs are allowed but only a single non-stobj value can be returned. The form is then evaluated and if the result is nil, then a so-called hard error (see er) results. This evaluation is however not done if proofs are being skipped, as during include-book (also see skip-proofs and see ld-skip-proofsp), unless :on-skip-proofs t is supplied.

    Normally, if an assert-event call fails then a generic failure message is printed, showing the offending form. However, if keyword argument :msg is supplied, then the failure message is printed as with fmt argument ~@0; see fmt. In particular, :msg is typically a string or a call (msg str arg-0 arg-1 ... arg-k), where str is a string and each arg-i is the value to be associated with #\i upon formatted printing (as with fmt) of the string str.

    This form may be put into a book to be certified (see books), because assert-event is a macro whose calls expand to calls of value-triple (see embedded-event-form). When certifying a book, guard-checking is off, as though (set-guard-checking nil) has been evaluated; see set-guard-checking. That, together with a ``safe mode,'' guarantees that assert-event forms are evaluated in the logic without guard violations while certifying a book.




    acl2-sources/doc/HTML/ASSIGN.html0000664002132200015000000000361212222333523015735 0ustar kaufmannacl2 ASSIGN.html -- ACL2 Version 6.3

    ASSIGN

    assign to a global variable in state
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (assign x (expt 2 10))
    (assign a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B))
    
    General Form:
    (assign symbol term)
    
    where symbol is any symbol (with certain enforced exclusions to avoid overwriting ACL2 system ``globals'') and term is any ACL2 term that could be evaluated at the top-level. Assign evaluates the term, stores the result as the value of the given symbol in the global-table of state, and returns the result. (Note: the actual implementation of the storage of this value is much more efficient than this discussion of the logic might suggest.) Assign is a macro that effectively expands to the more complicated but understandable:
    (pprogn (f-put-global 'symbol term state)
            (mv nil (f-get-global 'symbol state) state)).
    

    The macro f-put-global is closely related to assign: (assign var val) macroexpands to (f-put-global 'var val state).

    The macro @ gives convenient access to the value of such globals. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.




    acl2-sources/doc/HTML/ASSOC-EQ.html0000664002132200015000000000062312222333523016123 0ustar kaufmannacl2 ASSOC-EQ.html -- ACL2 Version 6.3

    ASSOC-EQ

    See assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/ASSOC-EQUAL.html0000664002132200015000000000063112222333523016464 0ustar kaufmannacl2 ASSOC-EQUAL.html -- ACL2 Version 6.3

    ASSOC-EQUAL

    See assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/ASSOC-KEYWORD.html0000664002132200015000000000171212222333523016742 0ustar kaufmannacl2 ASSOC-KEYWORD.html -- ACL2 Version 6.3

    ASSOC-KEYWORD

    look up key in a keyword-value-listp
    Major Section:  ACL2-BUILT-INS
    

    If l is a list of even length of the form (k1 a1 k2 a2 ... kn an), where each ki is a keyword, then (assoc-keyword key l) is the first tail of l starting with key if key is some ki, and is nil otherwise.

    The guard for (assoc-keyword key l) is (keyword-value-listp l).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ASSOC-STRING-EQUAL.html0000664002132200015000000000202212222333523017464 0ustar kaufmannacl2 ASSOC-STRING-EQUAL.html -- ACL2 Version 6.3

    ASSOC-STRING-EQUAL

    look up key, a string, in association list
    Major Section:  ACL2-BUILT-INS
    

    (Assoc-string-equal x alist) is similar to assoc-equal. However, for string x and alist alist, the comparison of x with successive keys in alist is done using string-equal rather than equal.

    The guard for assoc-string-equal requires that x is a string and alist is an alist.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ASSOC.html0000664002132200015000000000450312222333523015621 0ustar kaufmannacl2 ASSOC.html -- ACL2 Version 6.3

    ASSOC

    look up key in association list
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (assoc x alist)
    (assoc x alist :test 'eql)   ; same as above (eql as equality test)
    (assoc x alist :test 'eq)    ; same, but eq is equality test
    (assoc x alist :test 'equal) ; same, but equal is equality test
    

    (Assoc x alist) is the first member of alist whose car is x, or nil if no such member exists. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with the cars of successive elements of alist.

    The guard for a call of assoc depends on the test. In all cases, the second argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-alistp.

    See equality-variants for a discussion of the relation between assoc and its variants:

    (assoc-eq x alist) is equivalent to (assoc x alist :test 'eq);

    (assoc-equal x alist) is equivalent to (assoc x alist :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function assoc-equal.

    Assoc is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/ATOM-LISTP.html0000664002132200015000000000136012222333523016400 0ustar kaufmannacl2 ATOM-LISTP.html -- ACL2 Version 6.3

    ATOM-LISTP

    recognizer for a true list of atoms
    Major Section:  ACL2-BUILT-INS
    

    The predicate atom-listp tests whether its argument is a true-listp of atoms, i.e., of non-conses.

    Also see good-atom-listp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ATOM.html0000664002132200015000000000136312222333523015512 0ustar kaufmannacl2 ATOM.html -- ACL2 Version 6.3

    ATOM

    recognizer for atoms
    Major Section:  ACL2-BUILT-INS
    

    (atom x) is true if and only if x is an atom, i.e., not a cons pair.

    Atom has a guard of t, and is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/A_Flying_Tour_of_ACL2.html0000664002132200015000000000455312222333526020707 0ustar kaufmannacl2 A_Flying_Tour_of_ACL2.html -- ACL2 Version 6.3

    A Flying Tour of ACL2

    On this tour you will learn a little about what ACL2 is for rather than how ACL2 works. At the top and bottom bottom of the ``page'' there are ``flying tour'' icons. Click on either icon to go to the next page of the tour.

    The tour visits the following topics sequentially. But on your first reading, don't navigate through the tour by clicking on these links; they are shown as live links only so that later you can determine what you've visited. Instead, just use the flying tour icons.

    The Flight Plan
    * This Documentation
    * What is ACL2?
    * Mathematical Logic
    * Mechanical Theorem Proving
    * Mathematical Models in General
    * Mathematical Models of Computing Machines
         Formalizing Models
         Running Models
         Symbolic Execution of Models
         Proving Theorems about Models
    * Requirements of ACL2
         The User's Skills
         Training
         Host System
    

    On your first reading, don't explore other links you see in the tour. Some of them lead to the Walking Tour, which you can take coherently when you finish this tour. Others lead into the extensive hyptertext documentation and you are liable to get lost there unless you're trying to answer a specific question. We intend the tour to take about 10 minutes of your time.




    acl2-sources/doc/HTML/A_Sketch_of_How_the_Rewriter_Works.html0000664002132200015000000000272112222333526023646 0ustar kaufmannacl2 A_Sketch_of_How_the_Rewriter_Works.html -- ACL2 Version 6.3

    A Sketch of How the Rewriter Works

    Below we show the first target term, extracted from the current conjecture. Below it we show the associativity rule.

    The variables of the rewrite rule are instantiated so that the left-hand side of the rule matches the target:

         variable          term from target
           a                     x1
           b                     x2
           c                     (app x3 x4)
    

    Then the target is replaced by the instantiated right-hand side of the rule.

    Sometimes rules have hypotheses. To make a long story short, if the rule has hypotheses, then after matching the left-hand side, the rewriter instantiates the hypotheses and rewrites them recursively. This is called backchaining. If they all rewrite to true, then the target is replaced as above.

    We discuss the rewriter in more detail in the extended introduction to how to use the theorem prover, see introduction-to-the-theorem-prover, which we will recommend you work through after you have finished the two tours.




    acl2-sources/doc/HTML/A_Tiny_Warning_Sign.html0000664002132200015000000000152412222333526020604 0ustar kaufmannacl2 A_Tiny_Warning_Sign.html -- ACL2 Version 6.3

    A Tiny Warning Sign

    This warning sign, which usually appears as ``'', indicates that the link it marks takes you into ACL2's online documentation.

    The documentation is a vast graph of documented topics intended to help the user of ACL2 rather than the potential user. If you are exploring ACL2's home page to learn about the system, perhaps you should go back rather than follow the link marked with this sign. But you are welcome to explore the online documentation as well. Good luck.




    acl2-sources/doc/HTML/A_Trivial_Proof.html0000664002132200015000000000052012222333526017766 0ustar kaufmannacl2 A_Trivial_Proof.html -- ACL2 Version 6.3

    A Trivial Proof




    acl2-sources/doc/HTML/A_Typical_State.html0000664002132200015000000000226712222333526017766 0ustar kaufmannacl2 A_Typical_State.html -- ACL2 Version 6.3

    A Typical State

    Observe that the states in typical models talk about

    booleans    integers   vectors     records   caches
    bits        symbols    arrays      stacks    files
    characters  strings    sequences   tables    directories
    

    These objects are discrete rather than continuous; furthermore they are built incrementally or inductively by repeatedly using primitive operations to put together smaller pieces.

    The functions we need to manipulate these objects do things like concatenate, reverse, sort, search, count, etc.




    acl2-sources/doc/HTML/A_Walking_Tour_of_ACL2.html0000664002132200015000000000247212222333526021051 0ustar kaufmannacl2 A_Walking_Tour_of_ACL2.html -- ACL2 Version 6.3

    A Walking Tour of ACL2

    On this tour you will learn a little more about the ACL2 logic, the theorem prover, and the user interface.

    This time we will stick with really simple things, such as the associativity of list concatenation.

    We assume you have taken the Flying Tour but that you did not necessarily follow all the ``off-tour'' links because we encouraged you not to. With the Walking Tour we encourage you to visit off-tour links -- provided they are not marked with the tiny warning sign (). But they are ``branches'' in the tour that lead to ``dead ends.'' When you reach a dead end, remember to use your browser's Back Button to return to the Walking Tour to continue.

    When you get to the end of the tour we'll give you a chance to repeat quickly both the Flying and the Walking Tours to visit any off-tour links still of interest.




    acl2-sources/doc/HTML/A_bang_.html0000664002132200015000000000234612222333520016257 0ustar kaufmannacl2 A_bang_.html -- ACL2 Version 6.3

    A!

    to return to the top-level of ACL2's command loop
    Major Section:  MISCELLANEOUS
    

    When (a!) is evaluated inside of ACL2's command loop, the current computation is aborted and control returns to the top of the command loop, exactly as though the user had interrupted and aborted the current computation. (Note: Versions of ACL2 up to Version_3.4 provided `#.' for this purpose, but no longer; see sharp-dot-reader.)

    If you are at an ACL2 prompt (as opposed to a raw Lisp break), then you may type :a! in place of (a!); see keyword-commands.

    For a related feature that only pops up one level, see p!.

    Logically speaking, (a!) = nil. But imagine that it is defined in such a way that it causes a stack overflow or other resource exhaustion when called.




    acl2-sources/doc/HTML/About_Models.html0000664002132200015000000000206312222333526017330 0ustar kaufmannacl2 About_Models.html -- ACL2 Version 6.3

    About Models

    ACL2 is used to construct mathematical models of computer hardware and software (i.e., ``digital systems'').

    A mathematical model is a set of mathematical formulas used to predict the behavior of some artifact.

    The use of mathematical models allows faster and cheaper delivery of better systems.

    Models need not be complete or perfectly accurate to be useful to the trained engineer.

    Click here for more discussion of these assertions in an engineering context.




    acl2-sources/doc/HTML/About_Types.html0000664002132200015000000000550612222333526017216 0ustar kaufmannacl2 About_Types.html -- ACL2 Version 6.3

    About Types

    The universe of ACL2 objects includes objects of many different types. For example, t is a ``symbol'' and 3 is an ``integer.'' Roughly speaking the objects of ACL2 can be partitioned into the following types:

    Numbers                   3, -22/7, #c(3 5/2)
    Characters                #\A, #\a, #\Space
    Strings                   "This is a string."
    Symbols                   'abc, 'smith::abc
    Conses (or Ordered Pairs) '((a . 1) (b . 2))
    

    When proving theorems it is important to know the types of object returned by a term. ACL2 uses a complicated heuristic algorithm, called type-set , to determine what types of objects a term may produce. The user can more or less program the type-set algorithm by proving type-prescription rules.

    ACL2 is an ``untyped'' logic in the sense that the syntax is not typed: It is legal to apply a function symbol of n arguments to any n terms, regardless of the types of the argument terms. Thus, it is permitted to write such odd expressions as (+ t 3) which sums the symbol t and the integer 3. Common Lisp does not prohibit such expressions. We like untyped languages because they are simple to describe, though proving theorems about them can be awkward because, unless one is careful in the way one defines or states things, unusual cases (like (+ t 3)) can arise.

    To make theorem proving easier in ACL2, the axioms actually define a value for such terms. The value of (+ t 3) is 3; under the ACL2 axioms, non-numeric arguments to + are treated as though they were 0.

    You might immediately wonder about our claim that ACL2 is Common Lisp, since (+ t 3) is ``an error'' (and will sometimes even ``signal an error'') in Common Lisp. It is to handle this problem that ACL2 has guards. We will discuss guards later in the Walking Tour. However, many new users simply ignore the issue of guards entirely and that is what we recommend for now.

    You should now return to the Walking Tour.




    acl2-sources/doc/HTML/About_the_ACL2_Home_Page.html0000664002132200015000000000304612222333526021334 0ustar kaufmannacl2 About_the_ACL2_Home_Page.html -- ACL2 Version 6.3

    About the ACL2 Home Page

    The ACL2 Home Page is integrated into the ACL2 online documentation. Over 4 megabytes of hypertext is available here.

    The vast majority of the text is user-level documentation. For example, to find out about rewrite rules you could click on the link. (If you do that, remember to use your browser's Back Button to come back here.)

    The tiny warning signs mark links that lead out of the introductory-level material and into the user documentation. We advise against following such links upon your first reading of the documentation.

    At the end of the tours you will have a chance to revisit them quickly to explore alternative paths more fully.

    Finally, every page contains two icons at the bottom. The ACL2 icon leads you back to the ACL2 Home Page. The Index icon allows you to browse an alphabetical listing of all the topics in ACL2's online documentation. But both icons take you off the main route of the tour.




    acl2-sources/doc/HTML/About_the_Admission_of_Recursive_Definitions.html0000664002132200015000000000412012222333526025735 0ustar kaufmannacl2 About_the_Admission_of_Recursive_Definitions.html -- ACL2 Version 6.3

    About the Admission of Recursive Definitions

    You can't just add any formula as an axiom or definition and expect the logic to stay sound! For example, if we were permitted to define (APP X Y) so that it was equal to (NOT (APP X Y)) then we could prove anything. The purported ``definition'' of APP must have several properties to be admitted to the logic as a new axiom.

    The key property a recursive definition must have is that the recursion terminate. This, along with some syntactic criteria, ensures us that there exists a function satisfying the definition.

    Termination must be proved before the definition is admitted. This is done in general by finding a measure of the arguments of the function and a well-founded relation such that the arguments ``get smaller'' every time a recursive branch is taken.

    For app the measure is the ``size'' of the first argument, x, as determined by the primitive function acl2-count . The well-founded relation used in this example is o-p , which is the standard ordering on the ordinals less than ``epsilon naught.'' These particular choices for app were made ``automatically'' by ACL2. But they are in fact determined by various ``default'' settings. The user of ACL2 can change the defaults or specify a ``hint'' to the defun command to specify the measure and relation.

    You should now return to the Walking Tour.




    acl2-sources/doc/HTML/About_the_Prompt.html0000664002132200015000000001157412222333526020235 0ustar kaufmannacl2 About_the_Prompt.html -- ACL2 Version 6.3

    About the Prompt

    The string ``ACL2 !>'' is the ACL2 prompt.

    The prompt tells the user that an ACL2 command is expected. In addition, the prompt tells us a little about the current state of the ACL2 command interpreter. We explain the prompt briefly below. But first we talk about the command interpreter.

    An ACL2 command is generally a Lisp expression to be evaluated. There are some unusual commands (such as :q for quitting ACL2) which cause other behavior. But most commands are read, evaluated, and then have their results printed. Thus, we call the command interpreter a ``read-eval-print loop.'' The ACL2 command interpreter is named LD (after Lisp's ``load'').

    When a command is read, all the symbols in it are converted to uppercase. Thus, typing (defun app ...) is the same as typing (DEFUN APP ...) or (defun App ...). There are ways to force lowercase case characters into symbols but we won't discuss them here. A consequence of Common Lisp's default uppercasing is that you'll see a general lack of concern over the case used when symbols are displayed in this documentation.

    In addition, symbols ``belong'' to ``packages'' which give the user a way to control namespaces. The prompt tells us which package is the default one, namely "ACL2". That means when we call car, for example, we are invoking the standard definition of that symbol. If the packager were "JONES" then car would refer to the definition of that symbol in that package (which may or may not be different depending on what symbols were imported into that package.

    A command like (defun app (x y) ...) causes ACL2 to evaluate the defun function on app, (x y) and .... When that command is evaluated it prints some information to the terminal explaining the processing of the proposed definition. It returns the symbol APP as its value, which is printed by the command interpreter. (Actually, defun is not a function but a macro which expands to a form that involves state , a necessary precondition to printing output to the terminal and to ``changing'' the set of axioms. But we do not discuss this further here.)

    The defun command is an example of a special kind of command called an ``event.'' Events are those commands that change the ``logical world'' by adding such things as axioms or theorems to ACL2's database. See world . But not every command is an event command.

    A command like (app '(1 2 3) '(4 5 6 7)) is an example of a non-event. It is processed the same general way: the function app is applied to the indicated arguments and the result is printed. The function app does not print anything and does not change the ``world.''

    A third kind of command is one that display information about the current logical world or that ``roll back'' to previous versions of the world. Such commands are called ``history'' commands.

    What does the ACL2 prompt tell us about the read-eval-print loop? The prompt ``ACL2 !>'' tells us that the command will be read with current-package set to "ACL2", that guard checking (see set-guard-checking ) is on (``!''), and that we are at the top-level (there is only one ``>''). For more about the prompt, see default-print-prompt .

    You should now return to the Walking Tour.




    acl2-sources/doc/HTML/An_Example_Common_Lisp_Function_Definition.html0000664002132200015000000000331212222333526025276 0ustar kaufmannacl2 An_Example_Common_Lisp_Function_Definition.html -- ACL2 Version 6.3

    An Example Common Lisp Function Definition

    Consider the binary trees x and y below.

    In Lisp, x is written as the list '(A B) or, equivalently, as '(A B . NIL). Similarly, y may be written '(C D E). Suppose we wish to replace the right-most tip of x by the entire tree y. This is denoted (app x y), where app stands for ``append''.

    We can define app with:

    (defun app (x y)                           ; Concatenate x and y.
      (declare (type (satisfies true-listp) x)); We expect x to end in NIL.
      (cond ((endp x) y)                       ; If x is empty, return y.
            (t (cons (car x)                   ; Else, copy first node
                     (app (cdr x) y)))))       ;  and recur into next.
    

    If you defined this function in some Common Lisp, then to run app on the x and y above you could then type

    (app '(A B) '(C D E))
    
    and Common Lisp will print the result (A B C D E).




    acl2-sources/doc/HTML/An_Example_of_ACL2_in_Use.html0000664002132200015000000000470412222333526021517 0ustar kaufmannacl2 An_Example_of_ACL2_in_Use.html -- ACL2 Version 6.3

    An Example of ACL2 in Use

    To introduce you to ACL2 we will consider the app function discussed in the Common Lisp page, except we will omit for the moment the declare form, which in ACL2 is called a guard.

    Guards are arbitrary ACL2 terms that express the ``intended domain'' of functions. In that sense, guards are akin to type signatures. However, Common Lisp and ACL2 are untyped programming languages: while the language supports several different data types and the types of objects can be determined by predicates at runtime, any type of object may be passed to any function. Thus, guards are ``extra-logical.'' Recognizing both the practical and intellectual value of knowing that your functions are applied to the kinds of objects you intend, ACL2 imposes guards on Common Lisp and provides a means of proving that functions are used as intended. But the story is necessarily complicated and we do not recommend it to the new user. Get used to the fact that any ACL2 function may be applied to any objects and program accordingly. Read about guards later.

    Here is the definition again

    (defun app (x y)
      (cond ((endp x) y)
            (t (cons (car x) 
                     (app (cdr x) y)))))
    

    The next few stops along the Walking Tour will show you

      * how to use the ACL2 documentation,
      * what happens when the above definition is submitted to ACL2,
      * what happens when you evaluate calls of app,
      * what one simple theorem about app looks like,
      * how ACL2 proves the theorem, and
      * how that theorem can be used in another proof.
    
    Along the way we will talk about the definitional principle, types, the ACL2 read-eval-print loop, and how the theorem prover works.

    When we complete this part of the tour we will return briefly to the notion of guards and revisit several of the topics above in that context.




    acl2-sources/doc/HTML/Analyzing_Common_Lisp_Models.html0000664002132200015000000000306612222333526022515 0ustar kaufmannacl2 Analyzing_Common_Lisp_Models.html -- ACL2 Version 6.3

    Analyzing Common Lisp Models

    To analyze a model you must be able to reason about the operations and relations involved. Perhaps, for example, some aspect of the model depends upon the fact that the concatenation operation is associative.

    In any Common Lisp you can confirm that

    (app '(A B) (app '(C D) '(E F)))
    
    and
    (app (app '(A B) '(C D)) '(E F)))
    
    both evaluate to the same thing, (A B C D E F).

    But what distinguishes ACL2 (the logic) from applicative Common Lisp (the language) is that in ACL2 you can prove that the concatenation function app is associative when its arguments are true-lists, whereas in Common Lisp all you can do is test that proposition.

    That is, in ACL2 it makes sense to say that the following formula is a ``theorem.''

    Theorem Associativity of App
    (implies (and (true-listp a)
                  (true-listp b))
             (equal (app (app a b) c)
                    (app a (app b c))))
    

    Theorems about the properties of models are proved by symbolically manipulating the operations and relations involved. If the concatenation of sequences is involved in your model, then you may well need the theorem above in order to that your model has some particular property.




    acl2-sources/doc/HTML/BACKCHAIN-LIMIT-RW.html0000664002132200015000000000072512222333520017415 0ustar kaufmannacl2 BACKCHAIN-LIMIT-RW.html -- ACL2 Version 6.3

    BACKCHAIN-LIMIT-RW

    hints keyword :BACKCHAIN-LIMIT-RW
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/BACKCHAIN-LIMIT.html0000664002132200015000000001601612222333520017067 0ustar kaufmannacl2 BACKCHAIN-LIMIT.html -- ACL2 Version 6.3

    BACKCHAIN-LIMIT

    limiting the effort expended on relieving hypotheses
    Major Section:  MISCELLANEOUS
    

    Before ACL2 can apply a rule with hypotheses, it must establish that the hypotheses are true. (We ignore the relaxing of this requirement afforded by case-splits and forced hypotheses.) ACL2 typically establishes each hypothesis by backchaining -- instantiating the hypothesis and then rewriting it recursively. Here we describe how ACL2 allows the user to limit backchaining. At the end, below, we describe the function backchain-limit.

    Each hypothesis of a rewrite, meta, linear, or type-prescription rule is assigned a backchain-limit when the rule is stored. By default, this limit is nil, denoting infinity (no limit). However, the value used for the default may be set to a non-negative integer (or to nil) by the user; see set-default-backchain-limit. The default is overridden when a :backchain-limit-lst is supplied explicitly with the rule; see rule-classes. The number of recursive applications of backchaining starting with the hypothesis of a rule is limited to the backchain-limit associated with that hypothesis.

    Moreover, the user may set global backchain-limits that limit the total backchaining depth. See set-backchain-limit. One limit is for the use of rewrite, meta, and linear rules, while the other limit is for so-called ``type-set reasoning'', which uses rules of class type-prescription rules. The two limits operate independently. Below, we discuss the first kind of backchain limits, i.e., for other than type-prescription rules, except as otherwise indicated; but the mechanism for those rules is similar.

    Below we lay out the precise sense in which a global backchain-limit interacts with the backchain-limits of individual rules in order to limit backchaining. But first we note that when further backchaining is disallowed, ACL2 can still prove a hypothesis in a given context by using that contextual information. In fact, type-set reasoning may be used (except that a weaker version of it is used in the second case above, i.e., where we are already doing type-set reasoning). Thus, the relieving of hypotheses may be limited to the use of contextual information (without backchaining, i.e., without recursively rewriting hypotheses) by executing :set-backchain-limit 0.

    Recall that there are two sorts of backchain limits: those applied to hypotheses of individual rules, as assigned by their :rule-classes or else taken from the default (see set-default-backchain-limit); and the global limit, initially nil (no limit) but settable with :set-backchain-limit. Here is how these two types of limits interact to limit backchaining, i.e., recursive rewriting of hypotheses. ACL2 maintains a current backchain limit, which is the limit on the depth of recursive calls to the rewriter, as well as a current backchain depth, which is initially 0 and is incremented each time ACL2 backchains (and is decremented when a backchain completes). When ACL2 begins to rewrite a literal (crudely, one of the ``top-level'' terms of the goal currently being worked on), it sets the current backchain-limit to the global value, which is initially nil but can be set using :set-backchain-limit. When ACL2 is preparing to relieve a hypothesis by backchaining (hence, after it has already tried type-set reasoning), it first makes sure that the current backchain limit is greater than the current backchain depth. If not, then it refuses to relieve that hypothesis. Otherwise, it increments the current backchain depth and calculates a new current backchain-limit by taking the minimum of two values: the existing current backchain-limit, and the sum of the current backchain depth and the backchain-limit associated with the hypothesis. Thus, ACL2 only modifies the current backchain-limit if it is necessary to decrease that limit in order to respect the backchain limit associated with the hypothesis.

    We illustrate with the following examples.

    ; We stub out some functions so that we can reason about them.
    
    (defstub p0 (x) t)
    (defstub p1 (x) t)
    (defstub p2 (x) t)
    (defstub p3 (x) t)
    
    ; Initially, the default-backchain-limit is nil, or infinite.
    
    (defaxiom p2-implies-p1-limitless
      (implies (p2 x)
               (p1 x)))
    
    ; The following rule will have a backchain-limit of 0.
    
    (defaxiom p1-implies-p0-limit-0
      (implies (p1 x)
               (p0 x))
      :rule-classes ((:rewrite :backchain-limit-lst 0)))
    
    ; We have (p2 x) ==> (p1 x) ==> (p0 x).  We wish to establish that
    ; (p2 x) ==> (p0 x).  Normally, this would be no problem, but here
    ; we fail because ACL2 cannot establish (p0 x) by type-set reasoning
    ; alone.
    
    (thm
      (implies (p2 x)
               (p0 x)))
    
    ; We set the default-backchain-limit (for rewriting) to 1.
    
    :set-default-backchain-limit 1
    
    ; The following is more powerful than p1-implies-p0-limit-0
    ; because it can use rewrite rules to establish (p1 x).
    
    (defaxiom p1-implies-p0-limit-1
      (implies (p1 x)
               (p0 x)))
    
    ; This theorem will succeed:
    
    (thm
      (implies (p2 x)
               (p0 x)))
    
    ; We return the default-backchain-limit to its initial value.
    
    :set-default-backchain-limit nil
    
    ; Here is our last axiom.
    
    (defaxiom p3-implies-p2-limitless
      (implies (p3 x)
               (p2 x)))
    
    ; We now have (p3 x) ==> (p2 x) ==> (p1 x) ==> (p0 x).  However the
    ; rule p1-implies-p0-limit-1 has a backchain-limit of 1; hence we
    ; are not allowed to backchain far enough back to use
    ; p3-implies-p2-limitless.  We therefore lose.
    
    (defthm will-fail
      (implies (p3 x)
               (p0 x)))
    

    Finally, we remark that to see the current global backchain-limits, issue the following commands.

    (backchain-limit wrld :ts) ; backchain limit for type-set reasoning
    (backchain-limit wrld :rewrite) ; backchain limit for rewriting
    




    acl2-sources/doc/HTML/BACKTRACK.html0000664002132200015000000000067212222333520016236 0ustar kaufmannacl2 BACKTRACK.html -- ACL2 Version 6.3

    BACKTRACK

    hints keyword :BACKTRACK
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/BDD-ALGORITHM.html0000664002132200015000000007150412222333516016675 0ustar kaufmannacl2 BDD-ALGORITHM.html -- ACL2 Version 6.3

    BDD-ALGORITHM

    summary of the BDD algorithm in ACL2
    Major Section:  BDD
    

    The BDD algorithm in ACL2 uses a combination of manipulation of IF terms and unconditional rewriting. In this discussion we begin with some relevant mathematical theory. This is followed by a description of how ACL2 does BDDs, including concluding discussions of soundness, completeness, and efficiency.

    We recommend that you read the other documentation about BDDs in ACL2 before reading the rather technical material that follows. See BDD.

    Here is an outline of our presentation. Readers who want a user perspective, without undue mathematical theory, may wish to skip to Part (B), referring to Part (A) only on occasion if necessary.

    (A) Mathematical Considerations

    (A1) BDD term order

    (A2) BDD-constructors and BDD terms, and their connection with aborting the BDD algorithm

    (A3) Canonical BDD terms

    (A4) A theorem stating the equivalence of provable and syntactic equality for canonical BDD terms

    (B) Algorithmic Considerations

    (B1) BDD rules (rules used by the rewriting portion of the ACL2 BDD algorithm)

    (B2) Terms ``known to be Boolean''

    (B3) An ``IF-lifting'' operation used by the algorithm, as well as an iterative version of that operation

    (B4) The ACL2 BDD algorithm

    (B5) Soundness and Completeness of the ACL2 BDD algorithm

    (B6) Efficiency considerations

    (A) Mathematical Considerations

    (A1) BDD term order

    Our BDD algorithm creates a total ``BDD term order'' on ACL2 terms, on the fly. We use this order in our discussions below of IF-lifting and of canonical BDD terms, and in the algorithm's use of commutativity. The particular order is unimportant, except that we guarantee (for purposes of commutative functions) that constants are smaller in this order than non-constants.

    (A2) BDD-constructors (assumed to be '(cons)) and BDD terms

    We take as given a list of function symbols that we call the ``BDD-constructors.'' By default, the only BDD-constructor is cons, although it is legal to specify any list of function symbols as the BDD-constructors, either by using the acl2-defaults-table (see acl2-defaults-table) or by supplying a :BDD-CONSTRUCTORS hint (see hints). Warning: this capability is largely untested and may produce undesirable results. Henceforth, except when explicitly stated to the contrary, we assume that BDD-constructors is '(cons).

    Roughly speaking, a BDD term is the sort of term produced by our BDD algorithm, namely a tree with all cons nodes lying above all non-CONS nodes. More formally, a term is said to be a BDD term if it contains no subterm of either of the following forms, where f is not CONS.

    (f ... (CONS ...) ...)
    
    (f ... 'x ...)  ; where (consp x) = t
    

    We will see that whenever the BDD algorithm attempts to create a term that is not a BDD term, it aborts instead. Thus, whenever the algorithm completes without aborting, it creates a BDD term.

    (A3) Canonical BDD terms

    We can strengthen the notion of ``BDD term'' to a notion of ``canonical BDD term'' by imposing the following additional requirements, for every subterm of the form (IF x y z):

    (a) x is a variable, and it precedes (in the BDD term order) every variable occurring in y or z;

    (b) y and z are syntactically distinct; and,

    (c) it is not the case that y is t and z is nil.

    We claim that it follows easily from our description of the BDD algorithm that every term it creates is a canonical BDD term, assuming that the variables occurring in all such terms are treated by the algorithm as being Boolean (see (B2) below) and that the terms contain no function symbols other than IF and CONS. Thus, under those assumptions the following theorem shows that the BDD algorithm never creates distinct terms that are provably equal, a property that is useful for completeness and efficiency (as we explain in (B5) and (B6) below).

    (A4) Provably equal canonical BDD terms are identical

    We believe that the following theorem and proof are routine extensions of a standard result and proof to terms that allow calls of CONS.

    Theorem. Suppose that t1 and t2 are canonical BDD terms that contain no function symbols other than IF and CONS. Also suppose that (EQUAL t1 t2) is a theorem. Then t1 and t2 are syntactically identical.

    Proof of theorem: By induction on the total number of symbols occurring in these two terms. First suppose that at least one term is a variable; without loss of generality let it be t1. We must prove that t2 is syntactically the same as t1. Now it is clearly consistent that (EQUAL t1 t2) is false if t2 is a call of CONS (to see this, simply let t1 be an value that is not a CONSP). Similarly, t2 cannot be a constant or a variable other than t1. The remaining possibility to rule out is that t2 is of the form (IF t3 t4 t5), since by assumption its function symbol must be IF or CONS and we have already handled the latter case. Since t2 is canonical, we know that t3 is a variable. Since (EQUAL t1 t2) is provable, i.e.,

    (EQUAL t1 (if t3 t4 t5))
    
    is provable, it follows that we may substitute either t or nil for t3 into this equality to obtain two new provable equalities. First, suppose that t1 and t3 are distinct variables. Then these substitutions show that t1 is provably equal to both t4 and t5 (since t3 does not occur in t4 or t5 by property (a) above, as t2 is canonical), and hence t4 and t5 are provably equal to each other, which implies by the inductive hypothesis that they are the same term -- and this contradicts the assumption that t2 is canonical (property (b)). Therefore t1 and t3 are the same variable, i.e., the equality displayed above is actually (EQUAL t1 (if t1 t4 t5)). Substituting t and then nil for t1 into this provable equality lets us prove (EQUAL t t4) and (EQUAL nil t5), which by the inductive hypothesis implies that t4 is (syntactically) the term t and t5 is nil. That is, t2 is (IF t1 t nil), which contradicts the assumption that t2 is canonical (property (c)).

    Next, suppose that at least one term is a call of IF. Our first observation is that the other term is also a call of IF. For if the other is a call of CONS, then they cannot be provably equal, because the former has no function symbols other than IF and hence is Boolean when all its variables are assigned Boolean values. Also, if the other is a constant, then both branches of the IF term are provably equal to that constant and hence these branches are syntactically identical by the inductive hypothesis, contradicting property (b). Hence, we may assume for this case that both terms are calls of IF; let us write them as follows.

    t0:  (IF t1 t2 t3)
    u0:  (IF u1 u2 u3)
    
    Note that t1 and u1 are variables, by property (a) of canonical BDD terms. First we claim that t1 does not strictly precede u1 in the BDD term order. For suppose t1 does strictly precede u1. Then property (a) of canonical BDD terms guarantees that t1 does not occur in u0. Hence, an argument much like one used above shows that u0 is provably equal to both t2 (substituting t for t1) and t3 (substituting nil for t1), and hence t2 and t3 are provably equal. That implies that they are identical terms, by the inductive hypothesis, which then contradicts property (b) for t0. Similarly, u1 does not strictly precede t1 in the BDD term order. Therefore, t1 and u1 are the same variable. By substituting t for this variable we see that t2 and u2 are provably equal, and hence they are equal by the inductive hypothesis. Similarly, by substituting nil for t1 (and u1) we see that t3 and u3 are provably, hence syntactically, equal.

    We have covered all cases in which at least one term is a variable or at least one term is a call of IF. If both terms are constants, then provable and syntactic equality are clearly equivalent. Finally, then, we may assume that one term is a call of CONS and the other is a constant or a call of CONS. The constant case is similar to the CONS case if the constant is a CONSP, so we omit it; while if the constant is not a CONSP then it is not provably equal to a call of CONS; in fact it is provably not equal!

    So, we are left with a final case, in which canonical BDD terms (CONS t1 t2) and (CONS u1 u2) are provably equal, and we want to show that t1 and u1 are syntactically equal as are t2 and u2. These conclusions are easy consequences of the inductive hypothesis, since the ACL2 axiom CONS-EQUAL (which you can inspect using :PE) shows that equality of the given terms is equivalent to the conjunction of (EQUAL t1 t2) and (EQUAL u1 u2). Q.E.D.

    (B) Algorithmic Considerations

    (B1) BDD rules

    A rule of class :rewrite (see rule-classes) is said to be a ``BDD rewrite rule'' if and only if it satisfies the following criteria. (1) The rule is enabled. (2) Its equivalence relation is equal. (3) It has no hypotheses. (4) Its :loop-stopper field is nil, i.e., it is not a permutative rule. (5) All variables occurring in the rule occur in its left-hand side (i.e., there are no ``free variables''; see rewrite). A rule of class :definition (see rule-classes) is said to be a ``BDD definition rule'' if it satisfies all the criteria above (except (4), which does not apply), and moreover the top function symbol of the left-hand side was not recursively (or mutually recursively) defined. Technical point: Note that this additional criterion is independent of whether or not the indicated function symbol actually occurs in the right-hand side of the rule.

    Both BDD rewrite rules and BDD definition rules are said to be ``BDD rules.''

    (B2) Terms ''known to be Boolean''

    We apply the BDD algorithm in the context of a top-level goal to prove, namely, the goal at which the :BDD hint is attached. As we run the BDD algorithm, we allow ourselves to say that a set of terms is ``known to be Boolean'' if we can verify that the goal is provable from the assumption that at least one of the terms is not Boolean. Equivalently, we allow ourselves to say that a set of terms is ``known to be Boolean'' if we can verify that the original goal is provably equivalent to the assertion that if all terms in the set are Boolean, then the goal holds. The notion ``known to be Boolean'' is conservative in the sense that there are generally sets of terms for which the above equivalent criteria hold and yet the sets of terms are not noted as as being ``known to be Boolean.'' However, ACL2 uses a number of tricks, including type-set reasoning and analysis of the structure of the top-level goal, to attempt to establish that a sufficiently inclusive set of terms is known to be Boolean.

    From a practical standpoint, the algorithm determines a set of terms known to be Boolean; we allow ourselves to say that each term in this set is ``known to be Boolean.'' The algorithm assumes that these terms are indeed Boolean, and can make use of that assumption. For example, if t1 is known to be Boolean then the algorithm simplifies (IF t1 t nil) to t1; see (iv) in the discussion immediately below.

    (B3) IF-lifting and the IF-lifting-for-IF loop

    Suppose that one has a term of the form (f ... (IF test x y) ...), where f is a function symbol other than CONS. Then we say that ``IF-lifting'' test ``from'' this term produces the following term, which is provably equal to the given term.

    (if test
        (f ... x ...)  ; resulting true branch
        (f ... y ...)) ; resulting false branch
    
    Here, we replace each argument of f of the form (IF test .. ..), for the same test, in the same way. In this case we say that ``IF-lifting applies to'' the given term, ``yielding the test'' test and with the ``resulting two branches'' displayed above. Whenever we apply IF-lifting, we do so for the available test that is least in the BDD term order (see (A1) above).

    We consider arguments v of f that are ``known to be Boolean'' (see above) to be replaced by (IF v t nil) for the purposes of IF-lifting, i.e., before IF-lifting is applied.

    There is one special case, however, for IF-lifting. Suppose that the given term is of the form (IF v y z) where v is a variable and is the test to be lifted out (i.e., it is least in the BDD term order among the potential tests). Moroever, suppose that neither y nor z is of the form (IF v W1 W2) for that same v. Then IF-lifting does not apply to the given term.

    We may now describe the IF-lifting-for-IF loop, which applies to terms of the form (IF test tbr fbr) where the algorithm has already produced test, tbr, and fbr. First, if test is nil then we return fbr, while if test is a non-nil constant or a call of CONS then we return tbr. Otherwise, we see if IF-lifting applies. If IF-lifting does not apply, then we return (IF test tbr fbr). Otherwise, we apply IF-lifting to obtain a term of the form (IF x y z), by lifting out the appropriate test. Now we recursively apply the IF-lifting-for-IF loop to the term (IF x y z), unless any of the following special cases apply.

    (i) If y and z are the same term, then return y.

    (ii) Otherwise, if x and z are the same term, then replace z by nil before recursively applying IF-lifting-for-IF.

    (iii) Otherwise, if x and y are the same term and y is known to be Boolean, then replace y by t before recursively applying IF-lifting-for-IF.

    (iv) If z is nil and either x and y are the same term or x is ``known to be Boolean'' and y is t, then return x.

    NOTE: When a variable x is known to be Boolean, it is easy to see that the form (IF x t nil) is always reduced to x by this algorithm.

    (B4) The ACL2 BDD algorithm

    We are now ready to present the BDD algorithm for ACL2. It is given an ACL2 term, x, as well as an association list va that maps variables to terms, including all variables occurring in x. We maintain the invariant that whenever a variable is mapped by va to a term, that term has already been constructed by the algorithm, except: initially va maps every variable occurring in the top-level term to itself. The algorithm proceeds as follows. We implicitly ordain that whenever the BDD algorithm attempts to create a term that is not a BDD term (as defined above in (A2)), it aborts instead. Thus, whenever the algorithm completes without aborting, it creates a BDD term.

    If x is a variable, return the result of looking it up in va.

    If x is a constant, return x.

    If x is of the form (IF test tbr fbr), then first run the algorithm on test with the given va to obtain test'. If test' is nil, then return the result fbr' of running the algorithm on fbr with the given va. If test' is a constant other than nil, or is a call of CONS, then return the result tbr' of running the algorithm on tbr with the given va. If tbr is identical to fbr, return tbr. Otherwise, return the result of applying the IF-lifting-for-IF loop (described above) to the term (IF test' tbr' fbr').

    If x is of the form (IF* test tbr fbr), then compute the result exactly as though IF were used rather than IF*, except that if test' is not a constant or a call of CONS (see paragraph above), then abort the BDD computation. Informally, the tests of IF* terms are expected to ``resolve.'' NOTE: This description shows how IF* can be used to implement conditional rewriting in the BDD algorithm.

    If x is a LAMBDA expression ((LAMBDA vars body) . args) (which often corresponds to a LET term; see let), then first form an alist va' by binding each v in vars to the result of running the algorithm on the corresponding member of args, with the current alist va. Then, return the result of the algorithm on body in the alist va'.

    Otherwise, x is of the form (f x1 x2 ... xn), where f is a function symbol other than IF or IF*. In that case, let xi' be the result of running the algorithm on xi, for i from 1 to n, using the given alist va. First there are a few special cases. If f is EQUAL then we return t if x1' is syntactically identical to x2' (where this test is very fast; see (B6) below); we return x1' if it is known to be Boolean and x2' is t; and similarly, we return x2' if it is known to be Boolean and x1' is t. Next, if each xi' is a constant and the :executable-counterpart of f is enabled, then the result is obtained by computation. Next, if f is BOOLEANP and x1' is known to be Boolean, t is returned. Otherwise, we proceed as follows, first possibly swapping the arguments if they are out of (the BDD term) order and if f is known to be commutative (see below). If a BDD rewrite rule (as defined above) matches the term (f x1'... xn'), then the most recently stored such rule is applied. If there is no such match and f is a BDD-constructor, then we return (f x1'... xn'). Otherwise, if a BDD definition rule matches this term, then the most recently stored such rule (which will usually be the original definition for most users) is applied. If none of the above applies and neither does IF-lifting, then we return (f x1'... xn'). Otherwise we apply IF-lifting to (f x1'... xn') to obtain a term (IF test tbr fbr); but we aren't done yet. Rather, we run the BDD algorithm (using the same alist) on tbr and fbr to obtain terms tbr' and fbr', and we return (IF test tbr' fbr') unless tbr' is syntactically identical to fbr', in which case we return tbr'.

    When is it the case that, as said above, ``f is known to be commutative''? This happens when an enabled rewrite rule is of the form (EQUAL (f X Y) (f Y X)). Regarding swapping the arguments in that case: recall that we may assume very little about the BDD term order, essentially only that we swap the two arguments when the second is a constant and the first is not, for example, in (+ x 1). Other than that situation, one cannot expect to predict accurately when the arguments of commutative operators will be swapped.

    (B5) Soundness and Completeness of the ACL2 BDD algorithm

    Roughly speaking, ``soundness'' means that the BDD algorithm should give correct answers, and ``completeness'' means that it should be powerful enough to prove all true facts. Let us make the soundness claim a little more precise, and then we'll address completeness under suitable hypotheses.

    Claim (Soundness). If the ACL2 BDD algorithm runs to completion on an input term t0, then it produces a result that is provably equal to t0.

    We leave the proof of this claim to the reader. The basic idea is simply to check that each step of the algorithm preserves the meaning of the term under the bindings in the given alist.

    Let us start our discussion of completeness by recalling the theorem proved above in (A4).

    Theorem. Suppose that t1 and t2 are canonical BDD terms that contain no function symbols other than IF and CONS. Also suppose that (EQUAL t1 t2) is a theorem. Then t1 and t2 are syntactically identical.

    Below we show how this theorem implies the following completeness property of the ACL2 BDD algorithm. We continue to assume that CONS is the only BDD-constructor.

    Claim (Completeness). Suppose that t1 and t2 are provably equal terms, under the assumption that all their variables are known to be Boolean. Assume further that under this same assumption, top-level runs of the ACL2 BDD algorithm on these terms return terms that contain only the function symbols IF and CONS. Then the algorithm returns the same term for both t1 and t2, and the algorithm reduces (EQUAL t1 t2) to t.

    Why is this claim true? First, notice that the second part of the conclusion follows immediately from the first, by definition of the algorithm. Next, notice that the terms u1 and u2 obtained by running the algorithm on t1 and t2, respectively, are provably equal to t1 and t2, respectively, by the Soundness Claim. It follows that u1 and u2 are provably equal to each other. Since these terms contain no function symbols other than IF or CONS, by hypothesis, the Claim now follows from the Theorem above together with the following lemma.

    Lemma. Suppose that the result of running the ACL2 BDD algorithm on a top-level term t0 is a term u0 that contains only the function symbols IF and CONS, where all variables of t0 are known to be Boolean. Then u0 is a canonical BDD term.

    Proof: left to the reader. Simply follow the definition of the algorithm, with a separate argument for the IF-lifting-for-IF loop.

    Finally, let us remark on the assumptions of the Completeness Claim above. The assumption that all variables are known to be Boolean is often true; in fact, the system uses the forward-chaining rule boolean-listp-forward (you can see it using :pe) to try to establish this assumption, if your theorem has a form such as the following.

    (let ((x (list x0 x1 ...))
          (y (list y0 y1 ...)))
      (implies (and (boolean-listp x)
                    (boolean-listp y))
               ...))
    
    Moreover, the :BDD hint can be used to force the prover to abort if it cannot check that the indicated variables are known to be Boolean; see hints.

    Finally, consider the effect in practice of the assumption that the terms resulting from application of the algorithm contain calls of IF and CONS only. Typical use of BDDs in ACL2 takes place in a theory (see theories) in which all relevant non-recursive function symbols are enabled and all recursive function symbols possess enabled BDD rewrite rules that tell them how open up. For example, such a rule may say how to expand on a given function call's argument that has the form (CONS a x), while another may say how to expand when that argument is nil). (See for example the rules append-cons and append-nil in the documentation for IF*.) We leave it to future work to formulate a theorem that guarantees that the BDD algorithm produces terms containing calls only of IF and CONS assuming a suitably ``complete'' collection of rewrite rules.

    (B6) Efficiency considerations

    Following Bryant's algorithm, we use a graph representation of terms created by the BDD algorithm's computation. This representation enjoys some important properties.

    (Time efficiency) The test for syntactic equality of BDD terms is very fast.

    (Space efficiency) Equal BDD data structures are stored identically in memory.

    Implementation note. The representation actually uses a sort of hash table for BDD terms that is implemented as an ACL2 1-dimensional array. See arrays. In addition, we use a second such hash table to avoid recomputing the result of applying a function symbol to the result of running the algorithm on its arguments. We believe that these uses of hash tables are standard. They are also discussed in Moore's paper on BDDs; see bdd for the reference.




    acl2-sources/doc/HTML/BDD-INTRODUCTION.html0000664002132200015000000002034412222333516017264 0ustar kaufmannacl2 BDD-INTRODUCTION.html -- ACL2 Version 6.3

    BDD-INTRODUCTION

    examples illustrating the use of BDDs in ACL2
    Major Section:  BDD
    

    See bdd for a brief introduction to BDDs in ACL2 and for pointers to other documentation on BDDs in ACL2. Here, we illustrate the use of BDDs in ACL2 by way of some examples. For a further example, see if*.

    Let us begin with a really simple example. (We will explain the :bdd hint (:vars nil) below.)

    
    ACL2 !>(thm (equal (if a b c) (if (not a) c b))
                :hints (("Goal" :bdd (:vars nil)))) ; Prove with BDDs
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    But simplification with BDDs (7 nodes) reduces this to T, using the
    :definitions EQUAL and NOT.
    
    Q.E.D.
    
    Summary
    Form:  ( THM ...)
    Rules: ((:DEFINITION EQUAL) (:DEFINITION NOT))
    Warnings:  None
    Time:  0.18 seconds (prove: 0.05, print: 0.02, other: 0.12)
    
    Proof succeeded.
    ACL2 !>
    
    The :bdd hint (:vars nil) indicates that BDDs are to be used on the indicated goal, and that any so-called ``variable ordering'' may be used: ACL2 may use a convenient order that is far from optimal. It is beyond the scope of the present documentation to address the issue of how the user may choose good variable orderings. Someday our implementation of BDDs may be improved to include heuristically-chosen variable orderings rather than rather random ones.

    Here is a more interesting example.

    (defun v-not (x)
    ; Complement every element of a list of Booleans.
      (if (consp x)
          (cons (not (car x)) (v-not (cdr x)))
        nil))
    
    ; Now we prove a rewrite rule that explains how to open up v-not on
    ; a consp.
    (defthm v-not-cons
      (equal (v-not (cons x y))
             (cons (not x) (v-not y))))
    
    ; Finally, we prove for 7-bit lists that v-not is self-inverting.
    (thm
     (let ((x (list x0 x1 x2 x3 x4 x5 x6)))
       (implies (boolean-listp x)
                (equal (v-not (v-not x)) x)))
     :hints (("Goal" :bdd
                     ;; Note that this time we specify a variable order.
                     (:vars (x0 x1 x2 x3 x4 x5 x6)))))
    
    It turns out that the variable order doesn't seem to matter in this example; using several orders we found that 30 nodes were created, and the proof time was about 1/10 of a second on a (somewhat enhanced) Sparc 2. The same proof took about a minute and a half without any :bdd hint! This observation is a bit misleading perhaps, since the theorem for arbitrary x,
    (thm
     (implies (boolean-listp x)
              (equal (v-not (v-not x)) x)))
    
    only takes about 1.5 times as long as the :bdd proof for 7 bits, above! Nevertheless, BDDs can be very useful in reducing proof time, especially when there is no regular structure to facilitate proof by induction, or when the induction scheme is so complicated to construct that significant user effort is required to get the proof by induction to go through.

    Finally, consider the preceding example, with a :bdd hint of (say) (:vars nil), but with the rewrite rule v-not-cons above disabled. In that case, the proof fails, as we see below. That is because the BDD algorithm in ACL2 uses hypothesis-free :rewrite rules, :executable-counterparts, and nonrecursive definitions, but it does not use recursive definitions.

    Notice that when we issue the (show-bdd) command, the system's response clearly shows that we need a rewrite rule for simplifying terms of the form (v-not (cons ...)).

    ACL2 !>(thm
            (let ((x (list x0 x1 x2 x3 x4 x5 x6)))
              (implies (boolean-listp x)
                       (equal (v-not (v-not x)) x)))
            :hints (("Goal" :bdd (:vars nil)
                     :in-theory (disable v-not-cons))))
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    
    ACL2 Error in ( THM ...):  Attempted to create V-NOT node during BDD
    processing with an argument that is a call of a bdd-constructor,
    which would produce a non-BDD term (as defined in :DOC
    bdd-algorithm).  See :DOC show-bdd.
    
    
    Summary
    Form:  ( THM ...)
    Rules: NIL
    Warnings:  None
    Time:  0.58 seconds (prove: 0.13, print: 0.00, other: 0.45)
    
    ******** FAILED ********  See :DOC failure  ******** FAILED ********
    ACL2 !>(show-bdd)
    
    BDD computation on Goal yielded 17 nodes.
    ==============================
    
    BDD computation was aborted on Goal, and hence there is no
    falsifying assignment that can be constructed.  Here is a backtrace
    of calls, starting with the top-level call and ending with the one
    that led to the abort.  See :DOC show-bdd.
    
    (LET ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
         (IMPLIES (BOOLEAN-LISTP X)
                  (EQUAL (V-NOT (V-NOT X)) X)))
      alist: ((X6 X6) (X5 X5) (X4 X4) (X3 X3) (X2 X2) (X1 X1) (X0 X0))
    
    (EQUAL (V-NOT (V-NOT X)) X)
      alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
    
    (V-NOT (V-NOT X))
      alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
    
    (V-NOT X)
      alist: ((X (LIST X0 X1 X2 X3 X4 X5 ...)))
    ACL2 !>
    
    The term that has caused the BDD algorithm to abort is thus (V-NOT X), where X has the value (LIST X0 X1 X2 X3 X4 X5 ...), i.e., (CONS X0 (LIST X1 X2 X3 X4 X5 ...)). Thus, we see the utility of introducing a rewrite rule to simplify terms of the form (V-NOT (CONS ...)). The moral of this story is that if you get an error of the sort shown above, you may find it useful to execute the command (show-bdd) and use the result as advice that suggests the left hand side of a rewrite rule.

    Here is another sort of failed proof. In this version we have omitted the hypothesis that the input is a bit vector. Below we use show-bdd to see what went wrong, and use the resulting information to construct a counterexample. This failed proof corresponds to a slightly modified input theorem, in which x is bound to the 4-element list (list x0 x1 x2 x3).

    ACL2 !>(thm
            (let ((x (list x0 x1 x2 x3)))
              (equal (v-not (v-not x)) x))
            :hints (("Goal" :bdd
                     ;; This time we do not specify a variable order.
                     (:vars nil))))
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    
    ACL2 Error in ( THM ...):  The :BDD hint for the current goal has
    successfully simplified this goal, but has failed to prove it.
    Consider using (SHOW-BDD) to suggest a counterexample; see :DOC
    show-bdd.
    
    
    Summary
    Form:  ( THM ...)
    Rules: NIL
    Warnings:  None
    Time:  0.18 seconds (prove: 0.07, print: 0.00, other: 0.12)
    
    ******** FAILED ********  See :DOC failure  ******** FAILED ********
    ACL2 !>(show-bdd)
    
    BDD computation on Goal yielded 73 nodes.
    ==============================
    
    Falsifying constraints:
    ((X0 "Some non-nil value")
     (X1 "Some non-nil value")
     (X2 "Some non-nil value")
     (X3 "Some non-nil value")
     ((EQUAL 'T X0) T)
     ((EQUAL 'T X1) T)
     ((EQUAL 'T X2) T)
     ((EQUAL 'T X3) NIL))
    
    ==============================
    
    Term obtained from BDD computation on Goal:
    
    (IF X0
        (IF X1
            (IF X2 (IF X3 (IF # # #) (IF X3 # #))
                (IF X2 'NIL (IF X3 # #)))
            (IF X1 'NIL
                (IF X2 (IF X3 # #) (IF X2 # #))))
        (IF X0 'NIL
            (IF X1 (IF X2 (IF X3 # #) (IF X2 # #))
                (IF X1 'NIL (IF X2 # #)))))
    
    ACL2 Query (:SHOW-BDD):  Print the term in full?  (N, Y, W or ?):
    n ; I've seen enough.  The assignment shown above suggests
      ; (though not conclusively) that if we bind x3 to a non-nil
      ; value other than T, and bind x0, x1, and x2 to t, then we
      ; this may give us a counterexample.
    ACL2 !>(let ((x0 t) (x1 t) (x2 t) (x3 7))
             (let ((x (list x0 x1 x2 x3)))
               ;; Let's use LIST instead of EQUAL to see how the two
               ;; lists differ.
               (list (v-not (v-not x)) x)))
    ((T T T T) (T T T 7))
    ACL2 !>
    
    
    See if* for another example.




    acl2-sources/doc/HTML/BDD.html0000664002132200015000000000446512222333516015353 0ustar kaufmannacl2 BDD.html -- ACL2 Version 6.3

    BDD

    ordered binary decision diagrams with rewriting
    Major Section:  ACL2 Documentation
    

    Ordered binary decision diagrams (OBDDs, often simply called BDDs) are a technique, originally published by Randy Bryant, for the efficient simplification of Boolean expressions. In ACL2 we combine this technique with rewriting to handle arbitrary ACL2 terms that can represent not only Boolean values, but non-Boolean values as well. In particular, we provide a setting for deciding equality of bit vectors (lists of Boolean values).

    Some Related Topics

    • BDD-ALGORITHM -- summary of the BDD algorithm in ACL2

    • BDD-INTRODUCTION -- examples illustrating the use of BDDs in ACL2

    • IF* -- for conditional rewriting with BDDs

    • SHOW-BDD -- inspect failed BDD proof attempts

    An introduction to BDDs for the automated reasoning community may be found in ``Introduction to the OBDD Algorithm for the ATP Community'' by J Moore, Journal of Automated Reasoning (1994), pp. 33-45. (This paper also appears as Technical Report #84 from Computational Logic, Inc.)

    Further information about BDDs in ACL2 can be found in the subtopics of this documentation section. In particular, see bdd-introduction for a good starting place that provides a number of examples.

    See hints for a description of :bdd hints. For quick reference, here is an example; but only the :vars part of the hint is required, as explained in the documentation for hints. The values shown are the defaults.

    (:vars nil :bdd-constructors (cons) :prove t :literal :all)
    
    We suggest that you next visit the documentation topic BDD-INTRODUCTION.




    acl2-sources/doc/HTML/BIBLIOGRAPHY.html0000664002132200015000000000111612222333520016616 0ustar kaufmannacl2 BIBLIOGRAPHY.html -- ACL2 Version 6.3

    BIBLIOGRAPHY

    reports about ACL2
    Major Section:  MISCELLANEOUS
    

    For a list of notes and reports about ACL2, see http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html.




    acl2-sources/doc/HTML/BINARY-+.html0000664002132200015000000000174612222333523016073 0ustar kaufmannacl2 BINARY-+.html -- ACL2 Version 6.3

    BINARY-+

    addition function
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-+):

    (equal (binary-+ x y)
           (if (acl2-numberp x)
               (if (acl2-numberp y)
                   (binary-+ x y)
                 x)
             (if (acl2-numberp y)
                 y
               0)))
    

    Guard for (binary-+ x y):

    (and (acl2-numberp x) (acl2-numberp y))
    
    Notice that like all arithmetic functions, binary-+ treats non-numeric inputs as 0.

    Calls of the macro + expand to calls of binary-+; see +.




    acl2-sources/doc/HTML/BINARY-APPEND.html0000664002132200015000000000145212222333523016702 0ustar kaufmannacl2 BINARY-APPEND.html -- ACL2 Version 6.3

    BINARY-APPEND

    concatenate two lists
    Major Section:  ACL2-BUILT-INS
    

    This binary function implements append, which is a macro in ACL2. See append

    The guard for binary-append requires the first argument to be a true-listp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/BINARY-_star_.html0000664002132200015000000000171312222333523017202 0ustar kaufmannacl2 BINARY-_star_.html -- ACL2 Version 6.3

    BINARY-*

    multiplication function
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-*):

    (equal (binary-* x y)
           (if (acl2-numberp x)
               (if (acl2-numberp y)
                   (binary-* x y)
                 0)
             0))
    

    Guard for (binary-* x y):

    (and (acl2-numberp x) (acl2-numberp y))
    
    Notice that like all arithmetic functions, binary-* treats non-numeric inputs as 0.

    Calls of the macro * expand to calls of binary-*; see *.




    acl2-sources/doc/HTML/BIND-FREE-EXAMPLES.html0000664002132200015000000001615512222333520017423 0ustar kaufmannacl2 BIND-FREE-EXAMPLES.html -- ACL2 Version 6.3

    BIND-FREE-EXAMPLES

    examples pertaining to bind-free hypotheses
    Major Section:  BIND-FREE
    

    See bind-free for a basic discussion of the use of bind-free to control rewriting.

    Note that the examples below all illustrate the common case in which a bind-free hypothesis generates a binding alist. See bind-free, in particular the final section, for a discussion of the case that instead a list of binding alists is generated.

    We give examples of the use of bind-free hypotheses from the perspective of a user interested in reasoning about arithmetic, but it should be clear that bind-free can be used for many other purposes also.

    EXAMPLE 1: Cancel a common factor.

    (defun bind-divisor (a b)
    
    ; If a and b are polynomials with a common factor c, we return a
    ; binding for x.  We could imagine writing get-factor to compute the
    ; gcd, or simply to return a single non-invertible factor.
    
      (let ((c (get-factor a b)))
        (and c (list (cons 'x c)))))
    
    (defthm cancel-factor
      ;; We use case-split here to ensure that, once we have selected
      ;; a binding for x, the rest of the hypotheses will be relieved.
      (implies (and (acl2-numberp a)
                    (acl2-numberp b)
                    (bind-free (bind-divisor a b) (x))
                    (case-split (not (equal x 0)))
                    (case-split (acl2-numberp x)))
               (iff (equal a b)
                    (equal (/ a x) (/ b x)))))
    

    EXAMPLE 2: Pull integer summand out of floor. Note: This example has an extended bind-free hypothesis, which uses the term (find-int-in-sum sum mfc state).

    (defun fl (x)
      ;; This function is defined, and used, in the IHS books.
      (floor x 1))
    
    (defun int-binding (term mfc state)
      ;; The call to mfc-ts returns the encoded type of term. ;
      ;; Thus, we are asking if term is known by type reasoning to ;
      ;; be an integer. ;
      (declare (xargs :stobjs (state) :mode :program))
      (if (ts-subsetp (mfc-ts term mfc state)
                      *ts-integer*)
          (list (cons 'int term))
        nil))
    
    (defun find-int-in-sum (sum mfc state)
      (declare (xargs :stobjs (state) :mode :program))
      (if (and (nvariablep sum)
               (not (fquotep sum))
               (eq (ffn-symb sum) 'binary-+))
          (or (int-binding (fargn sum 1) mfc state)
              (find-int-in-sum (fargn sum 2) mfc state))
        (int-binding sum mfc state)))
    
    ; Some additional work is required to prove the following.  So for
    ; purposes of illustration, we wrap skip-proofs around the defthm.
    
    (skip-proofs
     (defthm cancel-fl-int
      ;; The use of case-split is probably not needed, since we should
      ;; know that int is an integer by the way we selected it.  But this
      ;; is safer.
       (implies (and (acl2-numberp sum)
                     (bind-free (find-int-in-sum sum mfc state) (int))
                     (case-split (integerp int)))
                (equal (fl sum)
                       (+ int (fl (- sum int)))))
       :rule-classes ((:rewrite :match-free :all)))
    )
    
    ; Arithmetic libraries will have this sort of lemma.
    (defthm hack (equal (+ (- x) x y) (fix y)))
    
    (in-theory (disable fl))
    
    (thm (implies (and (integerp x) (acl2-numberp y))
                  (equal (fl (+ x y)) (+ x (fl y)))))
    
    

    EXAMPLE 3: Simplify terms such as (equal (+ a (* a b)) 0)

    (defun factors (product)
      ;; We return a list of all the factors of product.  We do not
      ;; require that product actually be a product.
      (if (eq (fn-symb product) 'BINARY-*)
          (cons (fargn product 1)
                (factors (fargn product 2)))
        (list product)))
    
    (defun make-product (factors)
      ;; Factors is assumed to be a list of ACL2 terms.  We return an
      ;; ACL2 term which is the product of all the ellements of the
      ;; list factors.
      (cond ((atom factors)
             ''1)
            ((null (cdr factors))
             (car factors))
            ((null (cddr factors))
             (list 'BINARY-* (car factors) (cadr factors)))
            (t
             (list 'BINARY-* (car factors) (make-product (cdr factors))))))
    
    (defun quotient (common-factors sum)
      ;; Common-factors is a list of ACL2 terms.   Sum is an ACL2 term each
      ;; of whose addends have common-factors as factors.  We return
      ;; (/ sum (make-product common-factors)).
      (if (eq (fn-symb sum) 'BINARY-+)
          (let ((first (make-product (set-difference-equal (factors (fargn sum 1))
                                                           common-factors))))
            (list 'BINARY-+ first (quotient common-factors (fargn sum 2))))
        (make-product (set-difference-equal (factors sum)
                                            common-factors))))
    
    (defun intersection-equal (x y)
      (cond ((endp x)
             nil)
            ((member-equal (car x) y)
             (cons (car x) (intersection-equal (cdr x) y)))
            (t
             (intersection-equal (cdr x) y))))
    
    (defun common-factors (factors sum)
      ;; Factors is a list of the factors common to all of the addends
      ;; examined so far.  On entry, factors is a list of the factors in
      ;; the first addend of the original sum, and sum is the rest of the
      ;; addends.  We sweep through sum, trying to find a set of factors
      ;; common to all the addends of sum.
      (declare (xargs :measure (acl2-count sum)))
      (cond ((null factors)
             nil)
            ((eq (fn-symb sum) 'BINARY-+)
             (common-factors (intersection-equal factors (factors (fargn sum 1)))
                             (fargn sum 2)))
            (t
             (intersection-equal factors (factors sum)))))
    
    (defun simplify-terms-such-as-a+ab-rel-0-fn (sum)
      ;; If we can find a set of factors common to all the addends of sum,
      ;; we return an alist binding common to the product of these common
      ;; factors and binding quotient to (/ sum common).
      (if (eq (fn-symb sum) 'BINARY-+)
          (let ((common-factors (common-factors (factors (fargn sum 1))
                                                (fargn sum 2))))
            (if common-factors
                (let ((common (make-product common-factors))
                      (quotient (quotient common-factors sum)))
                  (list (cons 'common common)
                        (cons 'quotient quotient)))
              nil))
        nil))
    
    (defthm simplify-terms-such-as-a+ab-=-0
      (implies (and (bind-free
                     (simplify-terms-such-as-a+ab-rel-0-fn sum)
                     (common quotient))
                    (case-split (acl2-numberp common))
                    (case-split (acl2-numberp quotient))
                    (case-split (equal sum
                                       (* common quotient))))
               (equal (equal sum 0)
                      (or (equal common 0)
                          (equal quotient 0)))))
    
    (thm (equal (equal (+ u (* u v)) 0)
          (or (equal u 0) (equal v -1))))
    




    acl2-sources/doc/HTML/BIND-FREE.html0000664002132200015000000003462712222333520016213 0ustar kaufmannacl2 BIND-FREE.html -- ACL2 Version 6.3

    BIND-FREE

    to bind free variables of a rewrite, definition, or linear rule
    Major Section:  MISCELLANEOUS
    

    Examples:
    (IMPLIES (AND (RATIONALP LHS)
                  (RATIONALP RHS)
                  (BIND-FREE (FIND-MATCH-IN-PLUS-NESTS LHS RHS) (X)))
             (EQUAL (EQUAL LHS RHS)
                    (EQUAL (+ (- X) LHS) (+ (- X) RHS))))
    
    (IMPLIES (AND (BIND-FREE
                    (FIND-RATIONAL-MATCH-IN-TIMES-NESTS LHS RHS MFC STATE)
                    (X))
                  (RATIONALP X)
                  (CASE-SPLIT (NOT (EQUAL X 0))))
             (EQUAL (< LHS RHS)
                    (IF (< 0 X)
                        (< (* (/ X) LHS) (* (/ X) RHS))
                       (< (* (/ X) RHS) (* (/ X) LHS)))))
    

    Some Related Topics

    General Forms:

    (BIND-FREE term var-list)
    (BIND-FREE term t)
    (BIND-FREE term)
    
    A rule which uses a bind-free hypothesis has similarities to both a rule which uses a syntaxp hypothesis and to a :meta rule. Bind-free is like syntaxp, in that it logically always returns t but may affect the application of a :rewrite, :definition, or :linear rule when it is called at the top-level of a hypothesis. It is like a :meta rule, in that it allows the user to perform transformations of terms under progammatic control.

    Note that a bind-free hypothesis does not, in general, deal with the meaning or semantics or values of the terms, but rather with their syntactic forms. Before attempting to write a rule which uses bind-free, the user should be familiar with syntaxp and the internal form that ACL2 uses for terms. This internal form is similar to what the user sees, but there are subtle and important differences. Trans can be used to view this internal form.

    Just as for a syntaxp hypothesis, there are two basic types of bind-free hypotheses. The simpler type of bind-free hypothesis may be used as the nth hypothesis in a :rewrite, :definition, or :linear rule whose :corollary is (implies (and hyp1 ... hypn ... hypk) (equiv lhs rhs)) provided term is a term, term contains at least one variable, and every variable occuring freely in term occurs freely in lhs or in some hypi, i<n. In addition, term must not use any stobjs. Later below we will describe the second type, an extended bind-free hypothesis, which may use state. Whether simple or extended, a bind-free hypothesis may return an alist that binds free variables, as explained below, or it may return a list of such alists. We focus on the first of these cases: return of a single binding alist. We conclude our discussion with a section that covers the other case: return of a list of alists.

    We begin our description of bind-free by examining the first example above in some detail.

    We wish to write a rule which will cancel ``like'' addends from both sides of an equality. Clearly, one could write a series of rules such as

    (DEFTHM THE-HARD-WAY-2-1
       (EQUAL (EQUAL (+ A X B)
                     (+ X C))
              (EQUAL (+ A B)
                     (FIX C))))
    

    with one rule for each combination of positions the matching addends might be found in (if one knew before-hand the maximum number of addends that would appear in a sum). But there is a better way. (In what follows, we assume the presence of an appropriate set of rules for simplifying sums.)

    Consider the following definitions and theorem:

    (DEFUN INTERSECTION-EQUAL (X Y)
      (COND ((ENDP X)
             NIL)
            ((MEMBER-EQUAL (CAR X) Y)
             (CONS (CAR X) (INTERSECTION-EQUAL (CDR X) Y)))
            (T
             (INTERSECTION-EQUAL (CDR X) Y))))
    
    (DEFUN PLUS-LEAVES (TERM)
      (IF (EQ (FN-SYMB TERM) 'BINARY-+)
          (CONS (FARGN TERM 1)
                (PLUS-LEAVES (FARGN TERM 2)))
        (LIST TERM)))
    
    (DEFUN FIND-MATCH-IN-PLUS-NESTS (LHS RHS)
      (IF (AND (EQ (FN-SYMB LHS) 'BINARY-+)
               (EQ (FN-SYMB RHS) 'BINARY-+))
          (LET ((COMMON-ADDENDS (INTERSECTION-EQUAL (PLUS-LEAVES LHS)
                                                    (PLUS-LEAVES RHS))))
            (IF COMMON-ADDENDS
                (LIST (CONS 'X (CAR COMMON-ADDENDS)))
              NIL))
        NIL))
    
    (DEFTHM CANCEL-MATCHING-ADDENDS-EQUAL
      (IMPLIES (AND (RATIONALP LHS)
                    (RATIONALP RHS)
                    (BIND-FREE (FIND-MATCH-IN-PLUS-NESTS LHS RHS) (X)))
               (EQUAL (EQUAL LHS RHS)
                      (EQUAL (+ (- X) LHS) (+ (- X) RHS)))))
    

    How is this rule applied to the following term?

    (equal (+ 3 (expt a n) (foo a c))
           (+ (bar b) (expt a n)))
    
    As mentioned above, the internal form of an ACL2 term is not always what one sees printed out by ACL2. In this case, by using :trans one can see that the term is stored internally as
    (equal (binary-+ '3
                     (binary-+ (expt a n) (foo a c)))
           (binary-+ (bar b) (expt a n))).
    

    When ACL2 attempts to apply cancel-matching-addends-equal to the term under discussion, it first forms a substitution that instantiates the left-hand side of the conclusion so that it is identical to the target term. This substitution is kept track of by the substitution alist:

    ((LHS . (binary-+ '3
                       (binary-+ (expt a n) (foo a c))))
     (RHS . (binary-+ (bar b) (expt a n)))).
    
    ACL2 then attempts to relieve the hypotheses in the order they were given. Ordinarily this means that we instantiate each hypothesis with our substitution and then attempt to rewrite the resulting instance to true. Thus, in order to relieve the first hypothesis, we rewrite:
    (RATIONALP (binary-+ '3
                          (binary-+ (expt a n) (foo a c)))).
    
    Let us assume that the first two hypotheses rewrite to t. How do we relieve the bind-free hypothesis? Just as for a syntaxp hypothesis, ACL2 evaluates (find-match-in-plus-nests lhs rhs) in an environment where lhs and rhs are instantiated as determined by the substitution. In this case we evaluate
    (FIND-MATCH-IN-PLUS-NESTS '(binary-+ '3
                                          (binary-+ (expt a n) (foo a c)))
                              '(binary-+ (bar b) (expt a n))).
    
    Observe that, just as in the case of a syntaxp hypothesis, we substitute the quotation of the variables bindings into the term to be evaluated. See syntaxp for the reasons for this. The result of this evaluation, ((X . (EXPT A N))), is then used to extend the substitution alist:
    ((X . (EXPT A N))
     (LHS . (binary-+ '3
                       (binary-+ (expt a n) (foo a c))))
     (RHS . (binary-+ (bar b) (expt a n)))),
    
    and this extended substitution determines cancel-matching-addends-equal's result:
    (EQUAL (+ (- X) LHS) (+ (- X) RHS))
    ==>
    (EQUAL (+ (- (EXPT A N)) 3 (EXPT A N) (FOO A C))
           (+ (- (EXPT A N)) (BAR B) (EXPT A N))).
    
    Question: What is the internal form of this result?
    Hint: Use :trans.

    When this rule fires, it adds the negation of a common term to both sides of the equality by selecting a binding for the otherwise-free variable x, under programmatic control. Note that other mechanisms such as the binding of free-variables may also extend the substitution alist.

    Just as for a syntaxp test, a bind-free form signals failure by returning nil. However, while a syntaxp test signals success by returning true, a bind-free form signals success by returning an alist which is used to extend the current substitution alist. Because of this use of the alist, there are several restrictions on it -- in particular the alist must only bind variables, these variables must not be already bound by the substitution alist, and the variables must be bound to ACL2 terms. If term returns an alist and the alist meets these restrictions, we append the alist to the substitution alist and use the result as the new current substitution alist. This new current substitution alist is then used when we attempt to relieve the next hypothesis or, if there are no more, instantiate the right hand side of the rule.

    There is also a second, optional, var-list argument to a bind-free hypothesis. If provided, it must be either t or a list of variables. If it is not provided, it defaults to t. If it is a list of variables, this second argument is used to place a further restriction on the possible values of the alist to be returned by term: any variables bound in the alist must be present in the list of variables. We strongly recommend the use of this list of variables, as it allows some consistency checks to be performed at the time of the rule's admittance which are not possible otherwise.

    An extended bind-free hypothesis is similar to the simple type described above, but it uses two additional variables, mfc and state, which must not be bound by the left hand side or an earlier hypothesis of the rule. They must be the last two variables mentioned by term: first mfc, then state. These two variables give access to the functions mfc-xxx; see extended-metafunctions. As described there, mfc is bound to the so-called metafunction-context and state to ACL2's state. See bind-free-examples for examples of the use of these extended bind-free hypotheses.

    SECTION: Returning a list of alists.

    As promised above, we conclude with a discussion of the case that evaluation of the bind-free term produces a list of alists, x, rather than a single alist. In this case each member b of x is considered in turn, starting with the first and proceeding through the list. Each such b is handled exactly as discussed above, as though it were the result of evaluating the bind-free term. Thus, each b extends the current variable binding alist, and all remaining hypotheses are then relieved, as though b had been the value obtained by evaluating the bind-free term. As soon as one such b leads to successful relieving of all remaining hypotheses, the process of relieving hypotheses concludes, so no further members of x are considered.

    We illustrate with a simple pedagogical example. First introduce functions p1 and p2 such that a rewrite rule specifies that p2 implies p1, but with a free variable.

    
    (defstub p1 (x) t)
    (defstub p2 (x y) t)
    
    (defaxiom p2-implies-p1
      (implies (p2 x y)
               (p1 x)))
    
    
    If we add the following axiom, then (p1 x) follows logically for all x.
    
    (defaxiom p2-instance
      (p2 v (cons v 4)))
    
    
    Unfortunately, evaluation of (thm (p1 a)) fails, because ACL2 fails to bind the free variable y in order to apply the rule p2-instance.

    Let's define a function that produces a list of alists, each binding the variable y. Of course, we know that only the middle one below is necessary in this simple example. In more complex examples, one might use heuristics to construct such a list of alists.

    
    (defun my-alists (x)
      (list (list (cons 'y (fcons-term* 'cons x ''3)))
            (list (cons 'y (fcons-term* 'cons x ''4)))
            (list (cons 'y (fcons-term* 'cons x ''5)))))
    
    
    The following rewrite rule uses bind-free to return a list of candidate alists binding y.
    
    (defthm p2-implies-p1-better
      (implies (and (bind-free (my-alists x)
                               (y)) ; the second argument, (y), is optional
                    (p2 x y))
               (p1 x)))
    
    
    Now the proof succeeds for (thm (p1 a)). Why? When ACL2 applies the rewrite rule p2-implies-p1-better, it evaluates my-alists, as we can see from the following trace, to bind y in three different alists.
    
    ACL2 !>(thm (p1 a))
    1> (ACL2_*1*_ACL2::MY-ALISTS A)
    <1 (ACL2_*1*_ACL2::MY-ALISTS (((Y CONS A '3))
                                  ((Y CONS A '4))
                                  ((Y CONS A '5))))
    
    Q.E.D.
    
    
    The first alist, binding y to (cons a '3), fails to allow the hypothesis (p2 x y) to be proved. But the next binding of y, to (cons a '4), succeeds: then the current binding alist is ((x . a) (y . (cons a '4))), for which the hypothesis (p2 x y) rewrites to true using the rewrite rule p2-instance.




    acl2-sources/doc/HTML/BOOK-COMPILED-FILE.html0000664002132200015000000001546112222333516017421 0ustar kaufmannacl2 BOOK-COMPILED-FILE.html -- ACL2 Version 6.3

    BOOK-COMPILED-FILE

    creating and loading of compiled and expansion files for books
    Major Section:  BOOKS
    

    An effect of compilation is to speed up the execution of the functions defined in a book. Compilation can also remove tail recursion, thus avoiding stack overflows. The presence of compiled code for the functions in the book should not otherwise affect the performance of ACL2. See guard for a discussion; also See compilation.

    By default, the certify-book command compiles the book that it certifies. see certify-book for how to control this behavior.

    By default, the include-book command loads the compiled file for the book. The details of how this loading works are subtle, and do not need to be understood by most users. The ACL2 source code contains an ``Essay on Hash Table Support for Compilation'' that explains such details for those interested. All that users should generally need to know about this is that the compiled file is always the result of compiling a so-called ``expansion file'', which contains certain additional code besides the book itself. The relevance to users of the expansion file is that it can be loaded if the compiled file is missing (except when :load-compiled-file t is specified by the include-book form), and its existence is required in order for include-book to create a book's compiled file, as described below.

    Most users can skip the remainder of this documentation topic, which addresses the uncommon activity of using include-book to compile books.

    Include-book can be made to compile a book by supplying its keyword argument :load-compiled-file the value :comp. However, a compiled file can only be produced if there is already an expansion file that is at least as recent as the book's certificate. Such a file, whose name happens to be the result of concatenating the string "@expansion.lsp" to the book name (without the ".lisp" suffix), is created by certify-book when state global variable 'save-expansion-file has a non-nil value. That will be the case if ACL2 started up when environment variable ACL2_SAVE_EXPANSION was t (or any value that is not the empty string and whose string-upcase is not "NIL"), until the time (if any) that 'save-expansion-file is assigned a different value by the user. In most respects, the :comp setting is treated exactly the same as :warn; but after all events in the book are processed, the expansion file is compiled if a compiled file was not loaded, after which the resulting compiled file is loaded.

    One can thus, for example, compile books for several different host Lisps -- useful when installing ACL2 executables at the same site that are built on different host Lisps. A convenient way to do this in an environment that provides Gnu `make' is to certify the community books using the shell command ``make regression'' in the acl2-sources/ directory, after setting environment variable ACL2_SAVE_EXPANSION to t, and then moving to the books directory and executing the appropriate `make' commands to compile the books (targets fasl, o, and so on, according to the compiled file extension for the host Lisp).

    We conclude by saying more about the :load-compiled-file argument of include-book. We assume that state global 'compiler-enabled has a non-nil value; otherwise :load-compiled-file is always treated as nil.

    We do not consider raw mode below (see set-raw-mode), which presents a special case: ACL2 will attempt to load the book itself whenever it would otherwise load the expansion or compiled file, but cannot (either because the :load-compiled-file argument is nil, or for each of the expansion and compiled files, either it does not exist or it is out of date with respect to the .cert file).

    The :load-compiled-file argument is not recursive: calls of include-book that are inside the book supplied to include-book use their own :load-compiled-file arguments. However, those subsidiary include-book calls can nevertheless be sensitive to the :load-compiled-file arguments of enclosing include-book calls, as follows. If :load-compiled-file has value t, then every subsidiary include-book is required to load a compiled file. Moreover, if a book's compiled file or expansion file is loaded in raw Lisp, then an attempt will be made to load the compiled file or expansion file for any include-book form encountered during that load. If that attempt fails, then that load immediately aborts, as does its parent load, and so on up the chain. If, when going up the chain, an include-book is aborted for which keyword argument :load-compiled-file has value t, then an error occurs.

    When loading a book's compiled file or expansion file, FILE, it is possible to encounter an include-book form for a book that has no suitable compiled file or expansion file. In that case, the load of FILE is aborted at that point. Similarly, the load of FILE is aborted in the case that this include-book form has a suitable compiled file or expansion file whose load is itself aborted. Thus, whenever any include-book aborts, so do all of its parent include-books, up the chain. Such an abort causes an error when the include-book form specifies a :load-compiled-file value of t.




    acl2-sources/doc/HTML/BOOK-CONTENTS.html0000664002132200015000000001055512222333516016744 0ustar kaufmannacl2 BOOK-CONTENTS.html -- ACL2 Version 6.3

    BOOK-CONTENTS

    restrictions on the forms inside books
    Major Section:  BOOKS
    

    Example Book:
    
    ; This book defines my app function and the theorem that it is
    ; associative.  One irrelevant help lemma is proved first but
    ; it is local and so not seen by include-book.  I depend on the
    ; inferior book "weird-list-primitives" from which I get
    ; definitions of hd and tl.
    
    (in-package "MY-PKG")
    
    (include-book "weird-list-primitives")
    
    (defun app (x y) (if (consp x) (cons (hd x) (app (tl x) y)) y))
    
    (local
     (defthm help-lemma
       (implies (true-listp x) (equal (app x nil) x))))
    
    (defthm app-is-associative
      (equal (app (app a b) c) (app a (app b c))))
    
    
    The first form in a book must be (in-package "pkg") where "pkg" is some package name known to ACL2 whenever the book is certified. The rest of the forms in a book are embedded event forms, i.e., defuns, defthms, etc., some of which may be marked local. See embedded-event-form. The usual Common Lisp commenting conventions are provided. Note that since a book consists of embedded event forms, we can talk about the ``local'' and ``non-local'' events of a book.

    Because in-package is not an embedded event form, the only in-package in a book is the initial one. Because defpkg is not an embedded event form, a book can never contain a defpkg form. Because include-book is an embedded event form, books may contain references to other books. This makes books structured objects.

    When the forms in a book are read from the file, they are read with current-package set to the package named in the in-package form at the top of the file. The effect of this is that all symbols are interned in that package, except those whose packages are given explicitly with the ``::'' notation. For example, if a book begins with (in-package "ACL2-X") and then contains the form

      (defun fn (x)
        (acl2::list 'car x))
    
    then defun, fn, x, and car are all interned in the "ACL2-X" package. I.e., it is as though the following form were read instead:
      (acl2-x::defun acl2-x::fn (acl2-x::x)
          (acl2::list 'acl2-x::car acl2-x::x)).
    
    Of course, acl2-x::defun would be the same symbol as acl2::defun if the "ACL2-X" package imported acl2::defun.

    If each book has its own unique package name and all the names defined within the book are in that package, then name clashes between books are completely avoided. This permits the construction of useful logical worlds by the successive inclusion of many books. Although it is often too much trouble to manage several packages, their judicious use is a way to minimize name clashes. Often, a better way is to use local; see local.

    How does include-book know the definitions of the packages used in a book, since defpkgs cannot be among the forms? More generally, how do we know that the forms in a book will be admissible in the host logical world of an include-book? See certificate for answers to these questions.




    acl2-sources/doc/HTML/BOOK-EXAMPLE.html0000664002132200015000000002561712222333516016607 0ustar kaufmannacl2 BOOK-EXAMPLE.html -- ACL2 Version 6.3

    BOOK-EXAMPLE

    how to create, certify, and use a simple book
    Major Section:  BOOKS
    

    Suppose you have developed a sequence of admissible events which you want to turn into a book. We call this ``publishing'' the book. This note explains how to do that.

    A key idea of books is that they are ``incremental'' in the sense that when you include a book in a host logical world, the world is incrementally extended by the results established in that book. This is allowed only if every name defined by the incoming book is either new or is already identically defined. See redundant-events. This is exactly the same problem faced by a programmer who wishes to provide a utility to other people: how can he make sure he doesn't create name conflicts? The solution, in Common Lisp, is also the same: use packages. While books and packages have a very tenuous formal connection (every book must start with an in-package), the creation of a book is intimately concerned with the package issue. Having motivated what would otherwise appear as an unnecessary fascination with packages below, we now proceed with a description of how to publish a book.

    Just to be concrete, let's suppose you have already gotten ACL2 to accept the following sequence of commands, starting in the ACL2 initial state.

       (defpkg "ACL2-MY-BOOK"
               (union-eq *common-lisp-symbols-from-main-lisp-package*
                         *acl2-exports*))
       (in-package "ACL2-MY-BOOK")
       (defun app (x y)
         (if (consp x) (cons (car x) (app (cdr x) y)) y))
       (defun rev (x)
         (if (consp x) (app (rev (cdr x)) (list (car x))) nil))
       (defthm rev-app-hack
         (equal (rev (app a (list x))) (cons x (rev a))))
       (defthm rev-rev
         (implies (acl2::true-listp x) (equal (rev (rev x)) x)))
    
    Observe that the first form above defines a package (which imports the symbols defined in CLTL such as if and cons and the symbols used to command ACL2 such as defun and defthm). The second form selects that package as the current one. All subsequent forms are read into that package. The remaining forms are just event forms: defuns and defthms in this case.

    Typically you would have created a file with Emacs containing these forms and you will have submitted each of them interactively to ACL2 to confirm that they are all admissible. That interactive verification should start in ACL2's initial world -- although you might, of course, start your sequence of events with some include-books to build a more elaborate world.

    The first step towards publishing a book containing the results above is to create a file that starts with the in-package and then contains the rest of the forms. Let's call that file "my-book.lisp". The name is unimportant, except it must end with ".lisp". If there are events that you do not wish to be available to the user of the book -- e.g., lemmas you proved on your way toward proving the main ones -- you may so mark them by enclosing them in local forms. See local. Let us suppose you wish to hide rev-app-hack above. You may also add standard Lisp comments to the file. The final content of "my-book.lisp" might be:

     ; This book contains my app and rev functions and the theorem
     ; that rev is its own inverse.
    
       (in-package "ACL2-MY-BOOK")
       (defun app (x y)
         (if (consp x) (cons (car x) (app (cdr x) y)) y))
       (defun rev (x)
         (if (consp x) (app (rev (cdr x)) (list (car x))) nil))
    
     ; The following hack is not exported.
       (local (defthm rev-app-hack
         (equal (rev (app a (list x))) (cons x (rev a)))))
    
       (defthm rev-rev
         (implies (acl2::true-listp x) (equal (rev (rev x)) x)))
    
    The file shown above is the book. By the time this note is done you will have seen how to certify that the book is correct, how to compile it, and how to use it in other host worlds. Observe that the defpkg is not in the book. It cannot be: Common Lisp compilers disagree on how to treat new package definitions appearing in files to be compiled.

    Since a book is just a source file typed by the user, ACL2 provides a mechanism for checking that the events are all admissible and then marking the file as checked. This is called certification. To certify "my-book.lisp" you should first get into ACL2 with an initial world. Then, define the package needed by the book, by typing the following defpkg to the ACL2 prompt:

    ACL2 !>(defpkg "ACL2-MY-BOOK"
                   (union-eq *common-lisp-symbols-from-main-lisp-package*
                             *acl2-exports*))
    
    Then execute the command:
    ACL2 !>(certify-book "my-book" 1 t) ; the `t' is in fact the default
    
    Observe that you do not type the ".lisp" part of the file name. For purposes of books, the book's name is "my-book" and by the time all is said and done, there will be several extensions in addition to the ".lisp" extension associated with it.

    The 1 tells certify-book that you acknowledge that there is one command in this ``certification world'' (namely the defpkg). To use the book, any prospective host world must be extended by the addition of whatever commands occurred before certification. It would be a pity to certify a book in a world containing junk because that junk will become the ``portcullis'' guarding entrance to the book. The t above tells certify-book that you wish to compile "my-book.lisp" also (but see compilation for an exception). Certify-book makes many checks but by far the most important and time-consuming one is that it ``proves'' every event in the file.

    When certify-book is done it will have created two new files. The first will be called "my-book.cert" and contains the ``certificate'' attesting to the admissibility of the events in "my-book.lisp". The certificate contains the defpkg and any other forms necessary to construct the certification world. It also contains various check sums used to help you keep track of which version of "my-book.lisp" was certified.

    The second file that may be created by certify-book is the compiled version of "my-book.lisp" and will have a name that is assigned by the host compiler (e.g., "my-book.o" in GCL, "my-book.fasl" in SBCL). Certify-book will also load this object file. When certify-book is done, you may throw away the logical world it created, for example by executing the command :u.

    To use the book later in any ACL2 session, just execute the event (include-book "my-book"). This will do the necessary defpkg, load the non-local events in "my-book.lisp" and then may load the compiled code for the non-local functions defined in that file. Checks are made to ensure that the certificate file exists and describes the version of "my-book.lisp" that is read. The compiled code is loaded if and only if it exists and has a later write date than the source file (but see compilation for an exception).

    Since include-book is itself an event, you may put such forms into other books. Thus it is possible for the inclusion of a single book to lead to the inclusion of many others. The check sum information maintained in certificates helps deal with the version control problem of the referenced books. I.e., if this version of "my-book" is used during the certification of "your-book", then the certificate for "your-book" includes the check sum of this version of "my-book". If a later (include-book "your-book") finds a version of "my-book" with a different check sum, an error is signalled. But check sums are not perfect and the insecurity of the host file system prevents ACL2 from guaranteeing the logical soundness of an include-book event, even for a book that appears to have a valid certificate (they can be forged, after all). (See certificate for further discussion.)

    This concludes the example of how to create, certify and use a book. If you wish, you could now review the documentation for book-related topics (see books) and browse through them. They'll probably make sense in this context. Alternatively, you could continue the ``guided tour'' through the rest of the documentation of books. See book-name, following the pointer given at the conclusion.




    acl2-sources/doc/HTML/BOOK-MAKEFILES.html0000664002132200015000000000065112222333516017003 0ustar kaufmannacl2 BOOK-MAKEFILES.html -- ACL2 Version 6.3

    BOOK-MAKEFILES

    See books-certification.
    Major Section:  BOOKS
    




    acl2-sources/doc/HTML/BOOK-NAME.html0000664002132200015000000001244512222333516016227 0ustar kaufmannacl2 BOOK-NAME.html -- ACL2 Version 6.3

    BOOK-NAME

    conventions associated with book names
    Major Section:  BOOKS
    

    Examples:
    "list-processing"
    "/usr/home/smith/my-arith"
    
    Book names are string constants that can be elaborated into file names. We elaborate book names by concatenating the ``connected book directory'' (see cbd) string on the left and some ``extension,'' such as ".lisp", on the right. However, the connected book directory is not added if the book name itself already represents an absolute file name. Furthermore, include-book and certify-book temporarily reset the connected book directory to be the directory of the book being processed. This allows include-book forms to use file names without explicit mention of the enclosing book's directory. This in turn allows books (together with those that they include, using include-book) to be moved between directories while maintaining their certification and utility.

    You may wish to read elsewhere for details of ACL2 file name conventions (see pathname), for a discussion of the filename that is the result of the elaboration described here (see full-book-name), and for details of the concept of the connected book directory (see cbd). For details of how include-book (see include-book) and certify-book (see certify-book) use these concepts, see below.

    Often a book name is simply the familiar name of the file. (See full-book-name for discussion of the notions of ``directory string,'' ``familiar name,'' and ``extension''. These concepts are not on the guided tour through books and you should read them separately.) However, it is permitted for book names to include a directory or part of a directory name. Book names never include the extension, since ACL2 must routinely tack several different extensions onto the name during include-book. For example, include-book uses the ".lisp", ".cert" and possibly the ".o" or ".lbin" extensions of the book name.

    Book names are elaborated into full file names by include-book and certify-book. This elaboration is sensitive to the ``connected book directory.'' The connected book directory is an absolute filename string (see pathname) that is part of the ACL2 state. (You may wish to see cbd and to see set-cbd -- note that these are not on the guided tour). If a book name is an absolute filename string, ACL2 elaborates it simply by appending the desired extension to the right. If a book name is a relative filename string, ACL2 appends the connected book directory on the left and the desired extension on the right.

    Note that it is possible that the book name includes some partial specification of the directory. For example, if the connected book directory is "/usr/home/smith/" then the book name "project/task-1/arith" is a book name that will be elaborated to

    "/usr/home/smith/project/task-1/arith.lisp".
    

    Observe that while the events in this "arith" book are being processed the connected book directory will temporarily be set to

    "/usr/home/smith/project/task-1/".
    
    Thus, if the book requires other books, e.g.,
    (include-book "naturals")
    
    then it is not necessary to specify the directory on which they reside provided that directory is the same as the superior book.

    This inheritance of the connected book directory and its use to elaborate the names of inferior books makes it possible to move books and their inferiors to new directories, provided they maintain the same relative relationship. It is even possible to move with ease whole collections of books to different filesystems that use a different operating system than the one under which the original certification was performed.

    The ".cert" extension of a book, if it exists, is presumed to contain the most recent certificate for the book. See certificate (or, if you are on the guided tour, wait until the tour gets there).

    See book-contents to continue the guided tour.




    acl2-sources/doc/HTML/BOOKS-CERTIFICATION-CLASSIC.html0000664002132200015000000003211212222333516020665 0ustar kaufmannacl2 BOOKS-CERTIFICATION-CLASSIC.html -- ACL2 Version 6.3

    BOOKS-CERTIFICATION-CLASSIC

    classic ACL2 `make'-based certification of books
    Major Section:  BOOKS
    

    This documentation topic explains an approach to certifying directories of books, which we call ``classic ACL2 `make'-based certification''.

    Warning: The capability described in this section might be replaced at any time by a capability based on corresponding support for community books (see books-certification). If you think that would be a hardship, please contact the ACL2 implementors.

    This topic discusses a way to certify a directory of books other than the ACL2 community books. See books-certification for how to certify the set of ACL2 community books. There is also a section in that documentation topic, ``Restricting to Specific Directories and Books'', that provides an alternative to classic ACL2 `make'-based certification (as discussed in the present topic) for certifying specified sets of books.

    We assume here a familiarity with Unix/Linux `make'. We also assume that you are using GNU `make' rather than some other flavor of `make'. And finally, we assume, as is typically the case by following the standard installation instructions, that you install the ACL2 community books in the books/ subdirectory of your ACL2 distribution. We will refer below to that directory as BOOKS.

    In summary: to use `make' to certify books under a given directory, you may create a simple Makefile in that directory (as explained below) so that when you stand in that directory, you can submit the command, `make', to certify those books. If you have a multi-processor machine or the like, then you can use the `-j flag `make'-level parallelism by specifying the number of concurrent processes. For example:

    make -j 4
    
    For each book foo.lisp, a file foo.out in the same directory as foo.lisp will contain the output from the corresponding certification attempt. If you have previously executed such a command, then you might first want to delete certificate files and other generated files by executing the following command.
    make clean
    

    Note that when you run `make', then by default, the first error will cause the process to stop. You can use make -i to force `make' to ignore errors, thus continuing past them. Or, use make -k to keep going, but skipping certification for any book that includes another whose certification has failed.

    By default, your acl2-customization file (see acl2-customization) is ignored by such `make' commands. However, you can specify the use of an acl2-customization file by setting the value of environment variable ACL2_CUSTOMIZATION to the empty string, indicating a default such file, or to the desired absolute pathname. For example:

    make ACL2_CUSTOMIZATION=''
    make ACL2_CUSTOMIZATION='~/acl2-customization.lisp'
    

    We now discuss how to create makefiles to support `make' commands as discussed above.

    First we give five steps for creating a Makefile to support certification of a directory of books, without subdirectories. For examples of such Makefiles you can look in community book directories (which, however, might disappear in future versions of ACL2).

    1. Include the file Makefile-generic from the books/ subdirectory of your ACL2 sources directory, but first perhaps define the variable `ACL2'. Consider the following example.

    ACL2 ?= /Users/john_doe/acl2/acl2-sources/saved_acl2
    include /Users/john_doe/acl2/acl2-sources/books/Makefile-generic
    
    In this example, you can omit the first line, because the default ACL2 executable is file saved_acl2 in the directory immediately above the directory of the specified Makefile-generic file. Indeed, that is the common case. Note the use of ?= instead of = or :=, so that ACL2 can instead be defined by the environment or provided on the command line as part of the `make' command.

    2. (Optional; usually skipped.) Set the INHIBIT variable if you want to see more than the summary output. For example, if you want to see the same output as you would normally see at the terminal, put this line in your Makefile after the `include' lines.

    INHIBIT = (assign inhibit-output-lst (list (quote proof-tree)))
    
    For other values to use for INHIBIT, see set-inhibit-output-lst and see the original setting of INHIBIT in books/Makefile-generic.

    3. Specify the books to be certified. Normally, every file with extension .lisp will be a book that you want to certify, in which case you can skip this step. Otherwise, put a line in your Makefile after the ones above that specifies the books to be certified. The following example, from an old version of community books file books/finite-set-theory/osets/Makefile, should make this clear.

    BOOKS = computed-hints fast instance map membership outer primitives \
            quantify set-order sets sort
    
    But better yet, use the extension .lsp for any Lisp or ACL2 files that are not to be certified, so that the definition of BOOKS can be omitted.

    4. Create .acl2 files for books that are to be certified in other than the initial ACL2 world (see portcullis). For example, if you look in community books file books/arithmetic/equalities.acl2 you will see defpkg forms followed by a certify-book command, because it was determined that defpkg forms were necessary in the certification world in order to certify the equalities book. In general, for each <book-name>.lisp whose certification requires a non-initial certification world, you will need a corresponding <book-name>.acl2 file that ends with the appropriate certify-book command.

    You also have the option of creating a file cert.acl2 that has a special role. When file <book-name>.lisp is certified, if there is no file <book-name>.acl2 but there is a file cert.acl2, then cert.acl2 will be used as <book-name>.acl2 would have been used, as described in the preceding paragraph, except that the appropriate certify-book command will be generated automatically. Thus, no certify-book command should occur in cert.acl2.

    It is actually allowed to put raw lisp forms in a .acl2 file (presumably preceded by :q or (value :q) and followed by (lp)). But this is not recommended; we make no guarantees about certification performed any time after raw Lisp has been entered in the ACL2 session.

    5. Generally, the next step is to include the following line after the `include' of Makefile-generic (see the first step above).

    -include Makefile-deps
    
    This will cause `make' to create and then include a file Makefile-deps that contains ``dependency'' lines needed by `make'. If those dependencies are somehow flawed, it may be because you have include-book forms that are not truly including books, for example in multi-line comments (#|..|#). These will be ignored if preceded by a semicolon (;), or if you add a line break after ``include-book.'' But instead of adding the `-include' line above, you can create dependency lines yourself by running the command
    make dependencies
    
    and pasting the result into the end of your Makefile, and editing as you see fit.

    This concludes the basic instructions for creating a Makefile in a directory including books. Here are some other capabilities offered by community books file books/Makefile-subdirs. Not included below is a discussion of how to increase parallelism by avoiding the need to certify included books before certifying a given book; see provisional-certification.

    Subdirectory Support

    There is support for using `make' to certify books in subdirectories. Consider the following example.

    DIRS = pass1 bind-free floor-mod
    include ../Makefile-subdirs
    
    This indicates that we are to run `make' in subdirectories pass1/, bind-free/, and floor-mod/ of the current directory.

    You can combine this subdirectory support with the support already discussed for certifying books in the top-level directory. Here is an example, which as of this writing is in community books file books/arithmetic-3/Makefile contains the following lines.

    arith-top: top all
    all: top
    
    DIRS = pass1 bind-free floor-mod
    include ../Makefile-subdirs
    include ../Makefile-generic
    
    -include Makefile-deps
    
    The `top' target is defined in ../Makefile-subdirs to call `make' in each subdirectory specified in DIRS. We have set the default target in the example above to a new name, arith-top, that makes that top target before making the `all' target which, in turn, is the default target in any Makefile-generic, and is responsible for certifying books in the current directory as discussed in the five steps displayed above.

    Use Makefile-psubdirs instead of Makefile-subdirs if certification of a book in a subdirectory never depends on certification of a book in a different subdirectory, because then the -j option of `make' can allow subdirectories to be processed in parallel.

    Cleaning Up

    We note that there is a clean target. Thus,

    make clean
    
    will remove generated files including .cert, .out files, and compiled files.

    System Books

    An environment variable ACL2_SYSTEM_BOOKS is generally set automatically, so you can probably skip reading the following paragraph unless your attempt to certify books fails to locate those books properly.

    The environment variable ACL2_SYSTEM_BOOKS can be set to the top-level directory of the ACL2 community books. A Unix-style pathname, typically ending in books/ or books, is permissible. In most cases, your ACL2 executable is a small script in which you can set this environment variable just above the line on which the actual ACL2 image is invoked, for example:

    export ACL2_SYSTEM_BOOKS
    ACL2_SYSTEM_BOOKS=/home/acl2/v3-2/acl2-sources/books
    
    However, you can also set ACL2_SYSTEM_BOOKS as a `make' variable, by setting it in your Makefile before the first target definition, e.g.:
    ACL2_SYSTEM_BOOKS ?= /home/acl2/v3-2/acl2-sources/books
    

    Compilation Support

    The file books/Makefile-generic provides support for compiling books that are already certified (but see compilation for an exception). For example, suppose that you have certified books using GCL as the host Lisp, resulting in compiled files with the .o extension. Now suppose you would like to compile the books for Allegro Common Lisp, whose compiled files have the .fasl extension. The following command will work if you have included books/Makefile-generic in your Makefile.

    make fasl
    
    In general, the compiled file extension for a Lisp supported by ACL2 will be a target name for building compiled files for all your books (after certifying the books, if not already up-to-date on certification).

    If you run into problems, you can get help by joining the acl2-help email list (follow the link from the ACL2 home page) and sending a message to that list. Also consider trying another version of GNU `make'; for example, we have found that versions 3.81 and 3.82 sometimes cause errors on Linux where version 3.80 does not.




    acl2-sources/doc/HTML/BOOKS-CERTIFICATION.html0000664002132200015000000003205512222333516017614 0ustar kaufmannacl2 BOOKS-CERTIFICATION.html -- ACL2 Version 6.3

    BOOKS-CERTIFICATION

    certifying ACL2 community books
    Major Section:  BOOKS
    

    For background on the ACL2 community books, see community-books. Here we explain how to certify those books, or some of those books, with ACL2. We thank Bishop Brock, Jared Davis, and Sol Swords for their substantial contributions to this methodology. See books/Makefile, in the community books, for more about ``Credits and History'', and for additional technical details not covered in this topic (for example, how to build a local copy of the xdoc manual for those who may wish to do that).

    For more information about installing ACL2, see the installation instructions, either by following a link from the ACL2 home page or by going directly to the page http://www.cs.utexas.edu/users/moore/acl2/current/installation/installation.html. For information about so-called ``classic ACL2 `make'-based certification'', which provides support for certifying directories of books but may disappear in a future ACL2 release, see books-certification-classic.

    The Basics

    We make the following assumptions.

    o Gnu `make' is available on your system via the `make' command (rather than some other flavor of `make'). (Execute `make --version to verify this.)

    o You have built or obtained an ACL2 executable.

    o The ACL2 community books are installed in the books/ subdirectory of your ACL2 distribution, as is the case when you have followed the standard installation instructions.

    o All commands shown below are issued in the top-level (ACL2 sources) directory of your ACL2 distribution.

    By default the ACL2 executable is file saved_acl2 in your ACL2 sources directory, and you can issue the following command to the shell in order to do a ``regression run'' that certifies all of the community books using that executable.

    make regression
    
    Better yet, save a log file in case there are problems, for example as follows.
    (make regression) >& make-regression.log
    
    or perhaps better yet:
    (time nice make regression) >& make-regression.log
    
    For the sake of brevity, below we'll skip mentioning any of `time', `nice', or `>& make-regression.log'. But saving a log file, in particular, is useful in case you encounter problems to report.

    If you fetched the community books using svn, then you will have a directory books/workshops/ that is not necessary for certifying the other books. If you want to skip certification of the books under books/workshops/, use target `certify-books' instead of target `regression', for example as follows.

    (time nice make certify-books) >& make-certify-books.log
    

    Whether you use target `regression' or target `certify-books', then for each book foo.lisp whose certification is attempted, a file foo.cert.out in the same directory will contain the output from the book's certification attempt.

    A regression run may take a few hours, but if you have a multiprocessing computer, you can speed it up by certifying some books in parallel, by providing a value for `make' option -j. For example, if you have 8 hardware threads then you might want to issue the following command.

    make regression -j 8
    

    Specifying the ACL2 Executable

    If your ACL2 executable is not file saved_acl2 in the ACL2 sources directory, then you will need to specify that executable. You can do that by setting variable ACL2, either as an environment variable or, as displayed below, as a `make' variable. Either way, you will need to avoid relative pathnames. For example, the first two forms below are legal, but the third is not, assuming that my-acl2 is on your PATH in a Unix-like environment (e.g., linux or MacOS) and that my-saved_acl2 is just a pathname relative to your ACL2 sources directory, which is not on your path.

    make regression -j 8 ACL2=my-acl2
    make regression -j 8 ACL2=/u/smith/bin/acl2
    # The following only works if my-saved_acl2 is on your path (see above).
    make regression -j 8 ACL2=my-saved_acl2
    

    Cleaning

    You can delete files generated by book certification (including .cert files, .out files, compiled files, and more) by issuing the following command (again, in your ACL2 sources directory).

    make clean-books
    
    If you want to cause such deletion and then do a regression, simply replace the `regression' or `certify-books' target by `regression-fresh' or `certify-books-fresh', respectively, for example as follows. follows.
    make -j 4 regression-fresh
    make -j 4 certify-books-fresh
    

    If however you only want to clean up generated files residing under a given directory (or its subdirectories, and recursively), you can issue the following command while standing in that directory, where DIR is a pathname of your books directory.

    DIR/clean.pl
    
    For example, to clean up generated files under books/arithmetic, you could do the following.
    cd books/arithmetic
    ../clean.pl
    cd - # to return to the ACL2 sources directory, if you wish to do so
    

    Restricting to Specific Directories and Books

    You can specify which books you want certified by using any or all of the variables EXCLUDED_PREFIXES, ACL2_BOOK_CERTS, or ACL2_BOOK_DIRS. First, the set of desired .cert files is restricted to those that do not start with any string that is one of the words in the value of EXCLUDED_PREFIXES. Then ACL2_BOOK_CERTS and ACL2_BOOK_DIRS, if supplied, specify which books should be certified, as illustrated by the following example.

    make -j 8 regression-fresh \
     ACL2_BOOK_DIRS="symbolic paco" \
     ACL2_BOOK_CERTS=" \
      workshops/2006/cowles-gamboa-euclid/Euclid/ed6a.cert \
      workshops/2006/cowles-gamboa-euclid/Euclid/ed4bb.cert \
      "
    
    Then all book in directories symbolic and paco will be certified, as will the books workshops/2006/cowles-gamboa-euclid/Euclid/ed6a.lisp and workshops/2006/cowles-gamboa-euclid/Euclid/ed4bb.lisp. Note that all pathnames should be relative to your community books directory; in particular, they should not be absolute pathnames. Also notice the .cert extension used in files supplied for ACL2_BOOK_CERTS.

    Alternatively, you may wish to invoke books/cert.pl while standing in a directory under which you want to certify books. This will certify not only those books, but all supporting books -- even those not under the current directory -- that do not have up-to-date .cert files. The following is a simple command to invoke that will certify all books in the current directory, where if the books/ directory is not on your path, you will need to provide a suitable filename, e.g. ../../cert.pl or ~/acl2/books/cert.pl.

    cert.pl -j 4 *.lisp
    
    Here is a more complex command, which illustrates a way to certify books in subdirectories (as well as the current directory), the use of provisional certification (see provisional-certification), and `make'-level parallelism (in this case specifying four parallel processes).
    ACL2_PCERT=t cert.pl -j 4 `find . -name '*.lisp'`
    
    Note that with this approach, unlike classic ACL2 `make'-based certification (see books-certification-classic, out-of-date .cert files that are not under the current directory will also be built. For documentation of cert.pl invoke:
    cert.pl -h
    
    See the top of cert.pl for authorship and copyright information.

    Finally, we give a brief summary of how to use so-called ``classic ACL2 `make'-based certification'' for community books; see books-certification-classic for details. Note that support for this approach might be eliminated in a future ACL2 release. We welcome comments from the ACL2 community about whether or not that would be a good thing to do. See the discussion above about ACL2_BOOK_DIRS for the ``modern'' way to accomplish the same thing.

    Many community book directories have a Makefile. If you modify books only in such a directory, you can recertify by standing in that directory and issuing a `make' command. This command can optionally specify an ACL2 executable as well as parallelism, for example as follows, where the first line (make clean) is optional.

    make clean
    (time nice make -j 8 ACL2=my-acl2)
    

    ACL2 Customization Files

    By default, your acl2-customization file (see acl2-customization) is ignored by all flavors of ``make regression''. However, you can specify the use of an acl2-customization file by setting the value of environment variable ACL2_CUSTOMIZATION to the empty string, indicating a default such file, or to the desired absolute pathname. For example:

    make regression ACL2_CUSTOMIZATION=''
    make regression ACL2_CUSTOMIZATION='~/acl2-customization.lisp'
    

    Regressions for Experimental Extensions of ACL2

    The instructions are unchanged if you are using ACL2(h) (see hons-and-memoization). However, note that the default executable (when ACL2 is not specified) remains saved_acl2 in your ACL2 sources directory, not saved_acl2h as one might expect for ACL2(h)(p), respectively. So probably you'll want to supply ``ACL2=<your_acl2>'' explicitly with your `make' command.

    The comments above also pertain pertain to using ACL2(p) (see parallel); the default is saved_acl2 rather than saved_acl2p. However, we recommend that you use ACL2, not ACL2(p), for your regression. Then you can use ACL2(p) for your own proof developments. (The analogous comment applies to ACL2(hp), which combines capabilities of ACL2(h) and ACL2(p), i.e., we recommend that you use ACl2(h) for your regression in that case.) However, if you want to use ACL2(p) or ACL2(hp) for your regression, see waterfall-parallelism-for-book-certification.

    If you intend to certify books in the nonstd subdirectory of the community books, you will want to use ACL2(r) (see real). In that case, substitute target regression-nonstd for target regression. The default executable (when ACL2 is not specified) will be file saved_acl2r in your ACL2 sources directory, rather than saved_acl2.

    Provisional Certification

    To use provisional certification (see provisional-certification), supply ACL2_PCERT=t with your `make' command. Here is an example.

    time nice make regression -j 4 ACL2_BOOK_DIRS=deduction ACL2_PCERT=t
    

    Miscellany

    Other control of the certification process may be found by perusing community books file books/make_cert. In particular, the INHIBIT variable may be set to a call of set-inhibit-output-lst, for example as follows to obtain the output one would get by default in an (interactive) ACL2 session.

    time nice make regression -j 4 ACL2_BOOK_DIRS=arithmetic \
      INHIBIT='(set-inhibit-output-lst proof-tree)'
    

    Troubleshooting

    If you run into problems, you can get help by joining the acl2-help email list (follow the link from the ACL2 home page) and sending a message to that list. Also consider trying another version of GNU `make'; for example, we have found that versions 3.81 and 3.82 sometimes cause errors on Linux where version 3.80 does not. Note however that Version 3.80 does not print certain informational messages that are printed by later versions.




    acl2-sources/doc/HTML/BOOKS.html0000664002132200015000000001547012222333516015635 0ustar kaufmannacl2 BOOKS.html -- ACL2 Version 6.3

    BOOKS

    files of ACL2 event forms
    Major Section:  ACL2 Documentation
    

    This documentation topic is about ACL2 input files. However, there are two traditional (paper) books published about ACL2: a textbook and a case studies book. Further information on those two paper books is available by following links from the ACL2 home page, http://www.cs.utexas.edu/users/moore/acl2/.

    A ``book'' is a file of ACL2 events that have been certified as admissible. Using include-book you can construct a new logical world by assuming the events in any number of mutually compatible books. Relevant documented topics are listed below. Following this list is a ``guided tour'' through the topics.

    You can contribute books to the ACL2 community and obtain updates inbetween ACL2 releases by visiting the acl2-books project web page, http://acl2-books.googlecode.com/. Also see community-books.

    Some Related Topics

    Introduction.

    A ``book'' is a file of ACL2 forms. Books are prepared entirely by the user of the system, i.e., they are ``source'' files not ``object'' files. Some of the forms in a book are marked local and the others are considered ``non-local.''

    Include-book lets you load a book into any ACL2 world. If completed without error, the inclusion of a book extends the logic of the host world by the addition of just the non-local events in the book. You may extend the world by successively including a variety of books to obtain the desired collection of definitions and rules. Unless name conflicts occur (which are detected and signalled) inclusion of a book is consistency preserving provided the book itself is consistent as discussed later. However, include-book merely assumes the validity of the events in a book; if you include a book that contains an inconsistency (e.g., an inadmissible definition) then the resulting theory is inconsistent.

    It is possible to ``certify'' a book, with certify-book, guaranteeing that the error-free inclusion of the certified forms will produce a consistent extension of a consistent logic. Certification processes both the local and non-local forms, so you can mark as local those events you need for certification that you want to hide from users of the book (e.g., hacks, crocks, and kludges on the way to a good set of :rewrite rules). Certification can also ``compile'' a book, thereby speeding up the execution of the functions defined within it. The desire to compile books is largely responsible for the restrictions we put on the forms allowed in books.

    Extensive documentation is available on the various aspects of books. We recommend that you read it all before using books. It has been written so as to make sense when read in a certain linear sequence, called the ``guided tour'', though in general you may browse through it randomly. If you are on the guided tour, you should next read the documentation on book-example (see book-example).




    acl2-sources/doc/HTML/BOOLE$.html0000664002132200015000000000403312222333523015653 0ustar kaufmannacl2 BOOLE$.html -- ACL2 Version 6.3

    BOOLE$

    perform a bit-wise logical operation on 2 two's complement integers
    Major Section:  ACL2-BUILT-INS
    

    When integers x and y are viewed in their two's complement representation, (boole$ op x y) returns the result of applying the bit-wise logical operation specified by op. The following table is adapted from documentation for analogous Common Lisp function boole in the Common Lisp HyperSpec (http://www.lisp.org/HyperSpec/Body/fun_boole.html#boole). Note that the values of op for boole$ are ACL2 constants, rather than corresponding values of op for the Common Lisp function boole.

    op               result
    -----------      ---------
    *boole-1*        x
    *boole-2*        y
    *boole-andc1*    and complement of x with y
    *boole-andc2*    and x with complement of y
    *boole-and*      and
    *boole-c1*       complement of x
    *boole-c2*       complement of y
    *boole-clr*      the constant 0 (all zero bits)
    *boole-eqv*      equivalence (exclusive nor)
    *boole-ior*      inclusive or
    *boole-nand*     not-and
    *boole-nor*      not-or
    *boole-orc1*     or complement of x with y
    *boole-orc2*     or x with complement of y
    *boole-set*      the constant -1 (all one bits)
    *boole-xor*      exclusive or
    

    The guard of boole$ specifies that op is the value of one of the constants above and that x and y are integers.

    See any Common Lisp documentation for analogous information about Common Lisp function boole.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/BOOLEANP.html0000664002132200015000000000146612222333523016155 0ustar kaufmannacl2 BOOLEANP.html -- ACL2 Version 6.3

    BOOLEANP

    recognizer for booleans
    Major Section:  ACL2-BUILT-INS
    

    (Booleanp x) is t if x is t or nil, and is nil otherwise.

    See generalized-booleans for a discussion of a potential soundness problem for ACL2 related to the question: Which Common Lisp functions are known to return Boolean values?

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/BOUNDERS.html0000664002132200015000000004374612222333530016204 0ustar kaufmannacl2 BOUNDERS.html -- ACL2 Version 6.3

    BOUNDERS

    intervals, bounder functions, and bounder correctness
    Major Section:  TAU-SYSTEM
    

    Bounder Forms 1 and 2:
    (implies (and (tau-intervalp i1)
                  ...
                  (or (equal (tau-interval-dom i1) 'dom1-1)
                      ...)
                  ...
                  (in-tau-intervalp x1 i1)
                  ...)
             (and (tau-intervalp (bounder-fn i1 ...))
                  (in-tau-intervalp target
                                    (bounder-fn i1 ...))))
    
    where target is either (fn x1 ... y1 ...) or (mv-nth 'n (fn x1 ... y1 ...)), depending on whether we are in the Form 1 or Form 2 case, respectively. However, the shape above is meant just as a reminder. Details are given below.

    This topic first explains the basic shape of Bounder Form 1. Then it illustrates Bounder Form 2. Finally, it deals briefly with proving bounder correctness theorems. The community book tau-bounders/elementary-bounders contains bounders for various elementary functions including +, *, /, FLOOR, MOD, LOGAND, LOGNOT, LOGIOR, LOGORC1, LOGEQV, LOGXOR, and ASH. You might look at or include this book to see more example theorems, to see how proofs of such theorems are managed, and to experiment with their effects on proving theorems involving arithmetic over finite or half-finite intervals.

    A bounder correctness theorem establishes that bounder-fn is a ``bounder'' for the function fn. That means that when trying to compute a tau for a call of fn (or, in the case of Form 2, for the nth component of the multiple-value vector returned by a call of fn) the tau system can call bounder-fn on the intervals containing certain arguments of fn.

    Let us start with an example. Let fn be the addition function, + (actually, binary-+). Consider the target term (+ x y) and contemplate the question: if you know intervals containing x and y, say intx and inty respectively, what is an interval containing their sum? The answer is pretty easy to state in English: the domain of the answer interval is the less restrictive of the domains of intx and inty. The lower bound of the answer interval is the sum of the lower bounds of intx and inty, and the lower relation is the stronger of the lower relations of intx and inty. Analogous comments define the upper bound and relation of the answer interval. So for example, if x is an INTEGERP such that 0 <= x <= 10 and y is a RATIONALP such that 0 < y <= 20, then (+ x y) is a RATIONALP such that 0 < (+ x y) <= 30.

    Defining this precisely is more tedious than describing it in English because one must make precise the notions of ``less restrictive'' domains, ``weaker'' relations, and the possibility that either or both of the bounds could be ``infinite.'' But we can easily imagine defining the function bounder-for-+ that returns the answer interval described, given intx and inty.

    Then the following Bounder Form 1 formula establishes the correctness of bounder-for-+ and allows the tau system to use it to produce bounds in the tau computed for +-expressions:

    (implies (and (tau-intervalp intx)
                  (tau-intervalp inty)
                  (in-tau-intervalp x intx)
                  (in-tau-intervalp y inty))
             (and (tau-intervalp (bounder-for-+ intx inty))
                  (in-tau-intervalp (+ x y)
                                    (bounder-for-+ intx inty))))
    

    For example, suppose we have a formula with the following hypotheses

    (and (integerp a)
         (<= 0 a)
         (<= a 10)
         (rationalp b)
         (< 0 b)
         (<= b 20))
    
    and suppose the tau system encounters the term (+ a b). When the term is enountered, the tau for a would include an INTEGERP interval such that 0 <= a <= 10 and the tau for b would include a RATIONALP interval such that 0 < b <= 20. In its most primitive configuration, the tau system would only know that the tau for (+ a b) includes the recognizer RATIONALP (and all that it is known to imply). But after the bounder theorem above is proved and available as a :tau-system rule the tau system would infer that (+ a b) was in the RATIONALP interval such that 0 < (+ a b) <= 30.

    Thus, by defining bounder functions and proving them correct the user can give the tau system the ability to compute the bounds on function calls as a function of the known bounds on their actuals.

    It is sometimes useful to restrict the domains of the intervals to be considered. For example, in bounding *-expressions it is simplifying to restrict one's attention to intervals over the integers or rationals (and thus exclude the complex rationals so one need not think about the getting negative bounds by multiplying two ``positive'' complex rationals or how to ``round up'' from complex bounds to the rationals required by our intervals).

    If we were to define bounder-for-* so that it works correctly to bound *-expressions, but only for integer or rational arguments, its correctness theorem would be:

    (implies (and (tau-intervalp intx)                             ; (a)
                  (tau-intervalp inty)
                  (or (equal (tau-interval-dom intx) 'INTEGERP)    ; (b)
                      (equal (tau-interval-dom intx) 'RATIONALP))
                  (or (equal (tau-interval-dom inty) 'INTEGERP)
                      (equal (tau-interval-dom inty) 'RATIONALP))
                  (in-tau-intervalp x intx)                        ; (c)
                  (in-tau-intervalp y inty))
             (and (tau-intervalp (bounder-for-* intx inty))       ; (d)
                  (in-tau-intervalp (* x y)                        ; (e)
                                    (bounder-for-* intx inty))))
    

    In this case, bounder-for-* would be applied to the intervals for x and y only if those intervals were over the integers or the rationals.

    The above theorem for bounder-for-* begins to suggest the general form of a bounder theorem and we will use it to explain the general form.

    The hypotheses of a bounder theorem must be a conjunction and the conjuncts must be partitionable into three parts, (a), (b), and (c). The conclusion, must be a conjunction, must contain at least two conjuncts, (d) and (e), and is allowed to contain others that are simply ignored for purposes of bounders. (See the note below about why we allow but ignore additional conjuncts in the conclusion.)

    Part (a) introduces some distinct ``interval variables,'' here called ``ivars,'' that are known to denote intervals; for the example above, the ivars are intx and inty. Each hypothesis in part (a) is of the form (TAU-INTERVALP ivar).

    Part (b) allows us to restrict the domains of some of the intervals. Each hypothesis in part (b) must be a disjunction and each of the disjuncts must be of the form (EQUAL (TAU-INTERVAL-DOM ivar) 'dom), where ivar is one of the interval variables and dom is one of INTEGERP, RATIONALP, ACL2-NUMBERP, or NIL. It is not necessary to restrict every interval variable. Indeed, part (b) may be empty, as in the theorem for bounder-for-+ above.

    Part (c) consists of a set of (IN-TAU-INTERVALP avar ivar) hypotheses where each avar is a variable and no two hypotheses in part (c) use the same avar or ivar. We call the set of all such avar the ``actual variables'' or ``avars.'' The avars and ivars must be distinct. Part (c) sets up a correspondence between the avars and the ivars, each avar is in an interval denoted by one ivar.

    Part (d) introduces the name of the bounder function, here bounder-for-*, and the order of its ivar arguments. We see that bounder-for-* takes two arguments and they correspond, in order, to the intervals containing x and y. Part (d) also establishes that the bounder function always returns an interval under hypotheses (a), (b), and (c). Note that it is sometimes useful to return the ``universal interval'' (one that contains everything) if you don't want to compute a better interval for some case; see tau-intervalp or in-tau-intervalp.

    Part (e) introduces the name of the function being bounded, here *, and the order of its arguments. It establishes that the function being bounded really is bounded by the interval computed by the bounder function. In general, the function being bounded may take additional arguments. It is possible that the function being bounded takes some arguments that do not affect the bounds of its output.

    Thus, parts (c) and (e) together establish a mapping between the actuals of a call of the function being bounded and the intervals to be supplied to the bounder.

    The parts identified above may be presented in any order and the literals constituting those parts may be mingled. Thus, for example, here is another version of the theorem above that generates the same bounding information for the tau system. In this version, the hypotheses and conclusions are rearranged, bounder-for-* takes its arguments in the opposite order, and the theorem includes an additional conclusion.

    (implies (and (tau-intervalp intx)                             ; (a)
                  (or (equal (tau-interval-dom intx) 'INTEGERP)    ; (b)
                      (equal (tau-interval-dom intx) 'RATIONALP))
                  (in-tau-intervalp x intx)                        ; (c)
    
                  (tau-intervalp inty)                             ; (a)
                  (or (equal (tau-interval-dom inty) 'INTEGERP)    ; (b)
                      (equal (tau-interval-dom inty) 'RATIONALP))
                  (in-tau-intervalp y inty))
             (and (in-tau-intervalp (* x y)                        ; (e)
                                    (bounder-for-* inty intx))
                  (tau-intervalp (bounder-for-* inty intx))        ; (d)))
    
                  (or (equal (tau-interval-dom (bounder-for-* inty intx))
                             'INTEGERP)
                      (equal (tau-interval-dom (bounder-for-* inty intx))
                             'RATIONALP))
    

    Note on why bounder forms allow additional conjuncts in the conclusion: It is often the case that one creates bounders by composing other bounders. To prove compositional bounds correct one must often prove more than the mere correctness of the components. For example, one might need to prove that the domain of the new bounding interval is INTEGERP or otherwise restricted. We allow such ``unnecessary'' conclusions simply to save the user the burden of stating multiple theorems.

    An Illustration of Bounder Form 2: Suppose (quad i) is defined so that truncates the integer i to the largest multiple of 4 weakly below i and, additionally, returns the remainder. For example, (quad 26) returns (mv 24 2). Then here are bounders for each of its return values:

    (defun quad-bounds-0 (i)
      (cond ((and (tau-interval-lo i)
                  (<= 0 (tau-interval-lo i)))
             (make-tau-interval 'integerp nil 0 nil (tau-interval-hi i)))
            (t (make-tau-interval nil nil nil nil nil))))
    
    (defun quad-bounds-1 (i)
      (cond ((and (tau-interval-lo i)
                  (<= 0 (tau-interval-lo i)))
             (make-tau-interval 'integerp nil 0 nil 3))
            (t (make-tau-interval nil nil nil nil nil))))
    
    Note that the bounders assume i is an INTEGERP and return the universal interval when i is not a natural.

    As noted in the discussion below about how to prove bounder correctness theorems, proving these bounders correct will require an arithmetic book, e.g.,

    (include-book "arithmetic-5/top" :dir :system)
    

    Here then are two bounder correctness theorems of Form 2:

    (defthm quad-bounds-0-correct
      (implies (and (tau-intervalp i)
                    (equal (tau-interval-dom i) 'INTEGERP)
                    (in-tau-intervalp x i))
               (and (tau-intervalp (quad-bounds-0 i))
                    (in-tau-intervalp (mv-nth 0 (quad x))
                                      (quad-bounds-0 i))))
      :rule-classes :tau-system)
    
    (defthm quad-bounds-1-correct
      (implies (and (tau-intervalp i)
                    (equal (tau-interval-dom i) 'INTEGERP)
                    (in-tau-intervalp x i))
               (and (tau-intervalp (quad-bounds-1 i))
                    (in-tau-intervalp (mv-nth 1 (quad x)) (quad-bounds-1 i))))
      :rule-classes :tau-system)
    

    As noted above, if these bounders are to be used in constructing other bounders, we might include (in the first theorem) an additional concluding conjunct, such as

    (equal (tau-interval-dom (quad-bounds-0 i)) 'INTEGERP)
    
    so that we can keep quad-bounds-0 disabled to allow us to use quad-bounds-0-correct as a :rewrite or other rule and still relieve hypotheses about the domain of the interval it produces. These hypotheses would arise if some other verified bounder was called on the produced interval. In addition, as noted below, we might replace the :rule-classes above with
    :rule-classes
     ((:rewrite)
      (:forward-chaining :trigger-terms ((quad-bounds-0 i))))
    
    Since the theorem is being stored as some kind of rule and since it satisfies the Bounder Form 2 shape, it will additionally be stored as a :tau-system rule.

    Note on proving bounder theorems: Proving bounder theorems is just like proving any other arithmetic theorem and you will need whatever libraries are appropriate for the problem domain you are working in. Do not expect the tau system to be of much use in proving bounder theorems. A typical bounder theorem might require you to prove a subgoal like (< (fn x y) (g (tau-interval-hi int1) int2)). But tau deals with inequalities relating terms to constants, e.g., (< ... 16). A bounder theorem is a sort of ``metatheorem'' about how to construct bounded intervals from other bounded intervals. So when you undertake to define a bounder and prove it correct, go into the project with your eyes open!

    But bounder functions can be broadly divided into two classes, those defined in terms of arithmetic on the interval bounds and those defined in terms of other bounders. For example, given that

    (LOGXOR x y) = (LOGNOT (LOGEQV x y))
    
    an interval for bounding LOGXOR can be constructed by composing the constructions of intervals for LOGEQV and LOGNOT. So some bounder correctness proofs will involve direct manipulation of arithmetic inequalities and others might involve appeal to the correctness of other bounders, depending on how the new bounder is defined.

    Regardless of which style of bounder we are dealing with, we have found it useful to prove the basic theorems relating the tau interval accessors to MAKE-TAU-INTERVAL, e.g.,

    (equal (tau-interval-dom (make-tau-interval dom lo-rel lo hi-rel hi)) dom)
    
    and then disable those functions to avoid seeing excessive cars and cdrs.

    When dealing with bounders defined in the direct, arithmetic style, we tend to keep TAU-INTERVALP and IN-TAU-INTERVALP enabled so they unfold and expose the algebra.

    When dealing with bounders defined compositionally in terms of other verified bounders, we tend to keep TAU-INTERVALP and IN-TAU-INTERVALP disabled so we can rely on the previously proved bounder theorems as rewrite and forward chaining rules.

    Note that this last remark means that when you prove bounder correctness theorems you should include corollaries that are useful :rewrite and possibly :forward-chaining rules if you anticipate using that bounder in more complex ones. We tend to trigger the forward chaining with the bounder expression itself, rather than one of the hypotheses. For example in the rule above for bounder-for-* we would include (:forward-chaining :trigger-terms ((tau-bounder-expt2 int2))) and let the in-tau-intervalp hypotheses select the free variables x and y.




    acl2-sources/doc/HTML/BREAK$.html0000664002132200015000000000143212222333523015637 0ustar kaufmannacl2 BREAK$.html -- ACL2 Version 6.3

    BREAK$

    cause an immediate Lisp break
    Major Section:  ACL2-BUILT-INS
    

    ACL2 users are generally advised to avoid breaking into raw Lisp. Advanced users may, on occasion, see the need to do so. Evaluating (break$) will have that effect. (Exception: break$ is disabled after evaluation of (set-debugger-enable :never); see set-debugger-enable.) Break$ returns nil.




    acl2-sources/doc/HTML/BREAK-LEMMA.html0000664002132200015000000001126712222333516016435 0ustar kaufmannacl2 BREAK-LEMMA.html -- ACL2 Version 6.3

    BREAK-LEMMA

    a quick introduction to breaking rewrite rules in ACL2
    Major Section:  BREAK-REWRITE
    

    Example:
    :brr t                          ; if you haven't done that yet
    :monitor (:rewrite lemma12) t   ; to install a break point on the
                                    ; rule named (:rewrite lemma12)
    

    ACL2 does not support Nqthm's break-lemma but supports a very similar and more powerful break facility. Suppose some proof is failing; apparently some particular rule is not being used and you wish to learn why. Then you need the ACL2 break-rewrite facility. See break-rewrite and all of its associated :doc topics for details. The following basic steps are required.

    (1) To enable the ``break rewrite'' feature, you must first execute

    ACL2 !>:brr t
    
    at the top-level of ACL2. Equivalently, evaluate (brr t). Break-rewrite stays enabled until you disable it with (brr nil). When break-rewrite is enabled the ACL2 rewriter will run slower than normal but you will be able to monitor the attempts to apply specified rules.

    (2) Decide what runes (see rune) you wish to monitor. For example, you might want to know why (:rewrite lemma12 . 2) is not being used in the attempted proof. That, by the way, is the name of the second rewrite rule generated from the event named lemma12.

    The command

    ACL2 !>:monitor (:rewrite lemma12 . 2) t
    
    will install an ``unconditional'' break point on that rule. The ``t'' at the end of the command means it is unconditional, i.e., a break will occur every time the rule is tried. ACL2 supports conditional breaks also, in which case the t is replaced by an expression that evaluates to non-nil when you wish for a break to occur. See monitor. The above keyword command is, of course, equivalent to
    ACL2 !>(monitor '(:rewrite lemma12 . 2) t)
    
    which you may also type. You may install breaks on as many rules as you wish. You must use monitor on each rule. You may also change the break condition on a rule with monitor. Use unmonitor (see unmonitor) to remove a rule from the list of monitored rules.

    (3) Then try the proof again. When a monitored rule is tried by the rewriter you will enter an interactive break, called break-rewrite. See break-rewrite for a detailed description. Very simply, break-rewrite lets you inspect the context of the attempted application both before and after the attempt. When break-rewrite is entered it will print out the ``target'' term being rewritten. If you type :go break-rewrite will try the rule and then exit, telling you (a) whether the rule was applied, (b) if so, how the target was rewritten, and (c) if not, why the rule failed. There are many other commands. See brr-commands.

    (4) When you have finished using the break-rewrite feature you should disable it to speed up the rewriter. You can disable it with

    ACL2 !>:brr nil
    
    The list of monitored rules and their break conditions persists but is ignored. If you enable break-rewrite later, the list of monitored rules will be displayed and will be used again by rewrite.

    You should disable the break-rewrite feature whenever you are not intending to use it, even if the list of monitored rules is empty, because the rewriter is slowed down as long as break-rewrite is enabled.

    If you get a stack overflow, see cw-gstack.




    acl2-sources/doc/HTML/BREAK-ON-ERROR.html0000664002132200015000000000515312222333531016777 0ustar kaufmannacl2 BREAK-ON-ERROR.html -- ACL2 Version 6.3

    BREAK-ON-ERROR

    break when encountering a hard or soft error caused by ACL2
    Major Section:  TRACE
    

    General forms:
    (break-on-error t)    ; installs a trace causing a continuable error (break)
                          ;   when an error is invoked by ACL2.
    (break-on-error)      ; same as above
    (break-on-error :all) ; same as above, but even when inside the prover
    (break-on-error nil)  ; uninstall any above trace
    
    (Break-on-error) generates a suitable trace of error functions. Evaluate (trace$) after (break-on-error) if you want to see the specific trace forms (which you can modify and then submit directly to trace$, if you wish). This trace should cause entry to the Lisp debugger whenever ACL2 calls its error routines, except for certain errors when inside the theorem prover, and also at those times if option :all is supplied.

    NOTE: For technical reasons, you may see some error messages more than once.

    Finally, note that you are welcome to define your own version of break-on-error by modifying a copy of the source definition (search for ``(defmacro break-on-error'' in ACL2 source file other-events.lisp). Please feel free to send your version of break-on-error to the ACL2 implementors, for possible inclusion into ACL2.

    Break-on-error is implmented using ACL2 trace$. See trace! if you want an explanation of the ``TTAG NOTE'' that is printed.

    The argument, if supplied, is evaluated and must evaluate to t, nil, or :all.

    Also see set-debugger-enable for how to get raw-Lisp backtrace information when an error occurs as a result of break-on-error, or even of a raw Lisp error, by calling set-debugger-enable with argument :bt, :bt-break, or :break-bt. Note that for ACL2 errors (as opposed to raw Lisp errors), i.e. errors affected by break-on-error, all three of those keyword values are treated equivalently (and, all are ignored for non-ANSI GCL; see set-debugger-enable).




    acl2-sources/doc/HTML/BREAK-REWRITE.html0000664002132200015000000004231512222333516016721 0ustar kaufmannacl2 BREAK-REWRITE.html -- ACL2 Version 6.3

    BREAK-REWRITE

    the read-eval-print loop entered to monitor rewrite rules
    Major Section:  ACL2 Documentation
    

    ACL2 allows the user to monitor the application of rewrite rules. When monitored rules are about to be tried by the rewriter, an interactive break occurs and the user is allowed to watch and, in a limited sense, control the attempt to apply the rule. This interactive loop, which is technically just a call of the standard top-level ACL2 read-eval-print loop, ld, on a ``wormhole state'' (see wormhole), is called ``break-rewrite.'' While in break-rewrite, certain keyword commands are available for accessing information about the context in which the lemma is being tried. These keywords are called break-rewrite ``commands.''

    Also see dmr (Dynamically Monitor Rewrites) for a related utility, which allows you to watch progress of the rewriter in real time.

    To abort from inside break-rewrite at any time, execute

    For further information, see the related :doc topics listed below.

    Some Related Topics

    As explained in the documentation for monitor, it is possible to cause the ACL2 rewriter to monitor the attempted application of selected rules. When such a rule is about to be tried, the rewriter evaluates its break condition and if the result is non-nil, break-rewrite is entered.

    Break-rewrite permits the user to inspect the current state by evaluating break-rewrite commands. Type :help in break-rewrite to see what the break-rewrite commands are. However, break-rewrite is actually just a call of the general ACL2 read-eval-print loop, ld, on a certain state and the break-rewrite commands are simply aliases provided by ld-keyword-aliases table (see ld-keyword-aliases). See ld for details about this read-eval-print loop. Thus, with a few exceptions, anything you can do at the ACL2 top-level can be done within break-rewrite. For example, you can evaluate arbitrary expressions, use the keyword command hack, access documentation, print events, and even define functions and prove theorems. However, the ``certain state'' upon which ld was called is a ``wormhole state'' (see wormhole) because break-rewrite is not allowed to have any effect upon the behavior of rewrite. What this means, very roughly but understandably, is that break-rewrite operates on a copy of the state being used by rewrite and when break-rewrite exits the wormhole closes and the state ``produced'' by break-rewrite disappears. Thus, break-rewrite lets you query the state of the rewriter and even do experiments involving proofs, etc., but these experiments have no effect on the ongoing proof attempt. In particular:

    Note that the output from break-rewrite is sometimes abbreviated by default, such as for the term causing the break. This can be controlled by setting the :term evisc-tuple; see set-evisc-tuple. (Another option: use iprinting. See set-iprint.) But as noted above, if you use set-evisc-tuple from inside the break-rewrite wormhole, its effect will disappear when you exit the break. So you might want to issue a set-evisc-tuple command from the top level, outside break-rewrite.

    When you first enter break-rewrite a simple herald is printed such as:

    (3 Breaking (:rewrite lemma12) on (delta a (+ 1 j)):
    
    The integer after the open parenthesis indicates the depth of nested break-rewrite calls. In this discussion we use 3 consistently for this integer. Unless you abort or somehow enter unbalanced parentheses into the script, the entire session at a given depth will be enclosed in balanced parentheses, making it easy to skip over them in Emacs.

    You then will see the break-rewrite prompt:

    3 ACL2 !>
    
    The leading integer is, again, the depth. Because breaks often occur recursively it is convenient always to know the level with which you are interacting.

    You may type arbitrary commands as in the top-level ACL2 loop. For example, you might type:

    3 ACL2 !>:help
    
    or
    3 ACL2 !>:pe lemma12
    
    More likely, upon entering break-rewrite you will determine the context of the attempted application. Here are some useful commands:
    3 ACL2 >:target           ; the term being rewritten
    3 ACL2 >:unify-subst      ; the unifying substitution
    3 ACL2 >:path             ; the stack of goals pursued by the rewriter
                              ; starting at the top-level clause being simplified
                              ; and ending with the current application
    
    At this point in the interaction the system has not yet tried to apply the monitored rule. That is, it has not tried to establish the hypotheses, considered the heuristic cost of backchaining, rewritten the right-hand side of the conclusion, etc. When you are ready for it to try the rule you can type one of several different ``proceed'' commands. The basic proceed commands are :ok, :go, and :eval.
    :ok
    
    exits break-rewrite without further interaction. When break-rewrite exits it prints ``3)'', closing the parenthesis that opened the level 3 interaction.
    :go
    
    exits break-rewrite without further interaction, but prints out the result of the application attempt, i.e., whether the application succeeded, if so, what the :target term was rewritten to, and if not why the rule was not applicable.
    :eval
    
    causes break-rewrite to attempt to apply the rule but interaction at this level of break-rewrite resumes when the attempt is complete. When control returns to this level of break-rewrite a message indicating the result of the application attempt (just as in :go) is printed, followed by the prompt for additional user input.

    Generally speaking, :ok and :go are used when the break in question is routine or uninteresting and :eval is used when the break is one that the user anticipates is causing trouble. For example, if you are trying to determine why a lemma isn't being applied to a given term and the :target of the current break-rewrite is the term in question, you would usually :eval the rule and if break-rewrite reports that the rule failed then you are in a position to determine why, for example by carefully inspecting the :type-alist of governing assumptions or why some hypothesis of the rule could not be established.

    It is often the case that when you are in break-rewrite you wish to change the set of monitored runes. This can be done by using :monitor and :unmonitor as noted above. For example, you might want to monitor a certain rule, say hyp-reliever, just when it is being used while attempting to apply another rule, say main-lemma. Typically then you would monitor main-lemma at the ACL2 top-level, start the proof-attempt, and then in the break-rewrite in which main-lemma is about to be tried, you would install a monitor on hyp-reliever. If during the ensuing :eval hyp-reliever is broken you will know it is being used under the attempt to apply main-lemma.

    However, once hyp-reliever is being monitored it will be monitored even after main-lemma has been tried. That is, if you let the proof attempt proceed then you may see many other breaks on hyp-reliever, breaks that are not ``under'' the attempt to apply main-lemma. One way to prevent this is to :eval the application of main-lemma and then :unmonitor hyp-reliever before exiting. But this case arises so often that ACL2 supports several additional ``flavors'' of proceed commands.

    :Ok!, :go!, and :eval! are just like their counterparts (:ok, :go, and :eval, respectively), except that while processing the rule that is currently broken no runes are monitored. When consideration of the current rule is complete, the set of monitored runes is restored to its original setting.

    :Ok$, :go$, and :eval$ are similar but take an additional argument which must be a list of runes. An example usage of :eval$ is

    3 ACL2 !>:eval$ ((:rewrite hyp-reliever))
    
    These three commands temporarily install unconditional breaks on the runes listed, proceed with the consideration of the currently broken rule, and then restore the set of monitored rules to its original setting.

    Thus, there are nine ways to proceed from the initial entry into break-rewrite although we often speak as though there are two, :ok and :eval, and leave the others implicit. We group :go with :ok because in all their flavors they exit break-rewrite without further interaction (at the current level). All the flavors of :eval require further interaction after the rule has been tried.

    To abort a proof attempt and return to the top-level of ACL2 you may at any time type (a!) followed by a carriage return. If you are not in a raw Lisp break, you may type :a! instead. The utility p! is completely analogous to a! except that it pops up only one ld level. If you have just entered the break-rewrite loop, this will pop you out of that loop, back to the proof. See a! and see p!.

    We now address ourselves to the post-:eval interaction with break-rewrite. As noted, that interaction begins with break-rewrite's report on the results of applying the rule: whether it worked and either what it produced or why it failed. This information is also printed by certain keyword commands available after :eval, namely :wonp, :rewritten-rhs, and :failure-reason. In addition, by using brr@ (see brr@) you can obtain this information in the form of ACL2 data objects. This allows the development of more sophisticated ``break conditions''; see monitor for examples. In this connection we point out the macro form (ok-if term). See ok-if. This command exits break-rewrite if term evaluates to non-nil and otherwise does not exit. Thus it is possible to define macros that provide other kinds of exits from break-rewrite. The only way to exit break-rewrite after :eval is :ok (or, equivalently, the use of ok-if).

    ACL2 users who wish to know more about break-rewrite so that they can develop more convenient ways to monitor rules are encouraged to speak to J Moore.

    The rest of this documentation discusses a few implementation details of break-rewrite and may not be interesting to the typical user.

    There is no ACL2 function named break-rewrite. It is an illusion created by appropriate calls to two functions named brkpt1 and brkpt2. As previously noted, break-rewrite is ld operating on a wormhole state. One might therefore wonder how break-rewrite can apply a rule and then communicate the results back to the rewriter running in the external state. The answer is that it cannot. Nothing can be communicated through a wormhole. In fact, brkpt1 and brkpt2 are each calls of ld running on wormhole states. Brkpt1 implements the pre-:eval break-rewrite and brkpt2 implements the post-:eval break-rewrite. The rewriter actually calls brkpt1 before attempting to apply a rule and calls brkpt2 afterwards. In both cases, the rewriter passes into the wormhole the relevant information about the current context. Logically brkpt1 and brkpt2 are no-ops and rewrite ignores the nil they return. But while control is in them the execution of rewrite is suspended and cannot proceed until the break-rewrite interactions complete.

    This design causes a certain anomoly that might be troubling. Suppose that inside break-rewrite before :evaling a rule (i.e., in the brkpt1 wormhole state) you define some function, foo. Suppose then you :eval the rule and eventually control returns to break-rewrite (i.e., to brkpt2 on a wormhole state with the results of the application in it). You will discover that foo is no longer defined! That is because the wormhole state created during your pre-:eval interaction is lost when we exit the wormhole to resume the proof attempt. The post-:eval wormhole state is in fact identical to the initial pre-:eval state (except for the results of the application) because rewrite did not change the external state and both wormhole states are copies of it. A similar issue occurs with the use of trace utilities: all effects of calling trace$ and untrace$ are erased when you proceed from a break in the break-rewrite loop.

    There is a lot more to know about break-rewrite, most of which is fairly easy to learn from looking at the code, since it is all expressed in ACL2. Feel free to ask questions of J Moore.




    acl2-sources/doc/HTML/BREAKS.html0000664002132200015000000000506612222333523015725 0ustar kaufmannacl2 BREAKS.html -- ACL2 Version 6.3

    BREAKS

    Common Lisp breaks
    Major Section:  ACL2-BUILT-INS
    

    Example:
    Broken at PROVE.  Type :H for Help.
    >>:Q
    
    ACL2 !>
    

    You may interrupt the system by typing various control character sequences. The precise sequences are determined by the host Lisp and operating system environment. For example, in GCL and Allegro Common Lisp, a console interrupt is caused by typing ``ctrl-c''. If, however, the GCL or Allegro is running in an Emacs shell buffer, one must type ``ctrl-c ctrl-c''.

    If a break occurs, for example because of a bug in ACL2 or a user interrupt, the break will run a Common Lisp read-eval-print loop, not an ACL2 read-eval-print loop. This may not be obvious if the prompts in the two loops are similar. Because you are typing to a Common Lisp evaluator, you must be careful. It is possible to damage your ACL2 state in irreparable ways by executing non-ACL2 Common Lisp. It is even possible to disrupt and render inaccurate the interrupted evaluation of a simple ACL2 expression.

    For ACL2 built on most host Common Lisps, you will see the string [RAW LISP] in the prompt at a break, to emphasize that one is inside a break and hence should quit from the break. For some host Common Lisps, the top-level prompt also contains the string [RAW LISP]. See prompt for how to control printing of that string.

    The most reliable way to return to the ACL2 top level is by executing the following command: (abort!). Appropriate cleanup will then be done, which should leave you in an appropriate state.

    However, you may be able to quit from the break in the normal Lisp manner (as with :q in GCL or CCL, :reset in Allegro CL, and q in CMU CL). If this attempt to quit is successful, it will return you to the innermost ACL2 read-eval-print loop, with appropriate cleanup performed first. Note that if you are within a brr environment when the break occurs, quitting from the break will only return you to that environment, not to the top of ACL2's read-eval-print loop.




    acl2-sources/doc/HTML/BRR-COMMANDS.html0000664002132200015000000000511012222333516016572 0ustar kaufmannacl2 BRR-COMMANDS.html -- ACL2 Version 6.3

    BRR-COMMANDS

    Break-Rewrite Commands
    Major Section:  BREAK-REWRITE
    

    :a!             abort to ACL2 top-level
    :p!             pop one level (exits a top-level break-rewrite loop)
    :target         term being rewritten
    :unify-subst    substitution making :lhs equal :target
    :hyps           hypotheses of the rule
    :hyp i          ith hypothesis of the rule
    :lhs            left-hand side of rule's conclusion
    :rhs            right-hand side of rule's conclusion
    :type-alist     type assumptions governing :target
    :initial-ttree  ttree before :eval (see ttree)
    :ancestors      negations of backchaining hypotheses being pursued
    :wonp           indicates if application succeed (after :eval)
    :rewritten-rhs  rewritten :rhs (after :eval)
    :final-ttree    ttree after :eval (see ttree)
    :failure-reason reason rule failed (after :eval)
    :path           rewrite's path from top clause to :target
    :frame i        ith frame in :path
    :top            top-most frame in :path
    :ok             exit break
    :go             exit break, printing result
    :eval           try rule and re-enter break afterwards
    :ok!            :ok but no recursive breaks
    :go!            :go but no recursive breaks
    :eval!          :eval but no recursive breaks
    :ok$ runes      :ok with runes monitored during recursion
    :go$ runes      :go with runes monitored during recursion
    :eval$ runes    :eval with runes monitored during recursion
    :help           this message
    :standard-help  :help message from ACL2 top-level
    

    Break-rewrite is just a call of the standard ACL2 read-eval-print loop, ld, on a ``wormhole'' state. Thus, you may execute most commands you might normally execute at the top-level of ACL2. However, all state changes you cause from within break-rewrite are lost when you exit or :eval the rule. You cannot modify stobjs from within the break. See break-rewrite for more details and see ld for general information about the standard ACL2 read-eval-print loop.




    acl2-sources/doc/HTML/BRR.html0000664002132200015000000001136312222333516015402 0ustar kaufmannacl2 BRR.html -- ACL2 Version 6.3

    BRR

    to enable or disable the breaking of rewrite rules
    Major Section:  BREAK-REWRITE
    

    Example:
    :brr t       ; enable
    :brr nil     ; disable
    
    General Form:
    (brr flg)
    
    where flg evaluates to t or nil. This function modifies state so that the attempted application of certain rewrite rules are ``broken.'' ``Brr'' stands for ``break-rewrite'' and can be thought of as a mode with two settings. The normal mode is ``disabled.''

    When brr mode is ``enabled'' the ACL2 rewriter monitors the attempts to apply certain rules and advises the user of those attempts by entering an interactive wormhole break. From within this break the user can watch selected application attempts. See break-rewrite. The user can also interact with the system during brr breaks via brr-commands.

    The rules monitored are selected by using the monitor and unmonitor commands. It is possible to break a rune ``conditionally'' in the sense that an interactive break will occur only if a specified predicate is true of the environment at the time of the attempted application. See monitor and see unmonitor.

    Even if a non-empty set of rules has been selected, no breaks will occur unless brr mode is enabled. Thus, the first time in a session that you wish to monitor a rewrite rule, use :brr t to enable brr mode. Thereafter you may select runes to be monitored with monitor and unmonitor with the effect that whenever monitored rules are tried (and their break conditions are met) an interactive break will occur. Be advised that when brr mode is enabled the rewriter is somewhat slower than normal. Furthermore, that sluggishness persists even if no runes are monitored. You may regain normal performance -- regardless of what runes are monitored -- by disabling brr mode with :brr nil.

    Why isn't brr mode disabled automatically when no runes are monitored? More generally, why does ACL2 have brr mode at all? Why not just test whether there are monitored runes? If you care about the answers, see why-brr.

    BRR Mode and Console Interrupts: If the system is operating in brr mode and you break into raw Lisp (as by causing a console interrupt or happening upon a signalled Lisp error; see breaks), you can return to the ACL2 top-level, outside any brr environment, by executing (abort!). Otherwise, the normal way to quit from such a break (for example :q in GCL, :reset in Allegro CL, and q in CMU CL) will return to the innermost ACL2 read-eval-print loop, which may or may not be the top-level of your ACL2 session! In particular, if the break happens to occur while ACL2 is within the brr environment (in which it is preparing to read brr-commands), the abort will merely return to that brr environment. Upon exiting that environment, normal theorem proving is continued (and the brr environment may be entered again in response to subsequent monitored rule applications). Before returning to the brr environment, ACL2 ``cleans up'' from the interrupted brr processing. However, it is not possible (given the current implementation) to clean up perfectly. This may have two side-effects. First, the system may occasionally print the self-explanatory ``Cryptic BRR Message 1'' (or 2), informing you that the system has attempted to recover from an aborted brr environment. Second, it is possible that subsequent brr behavior in that proof will be erroneous because the cleanup was done incorrectly. The moral is that you should not trust what you learn from brr if you have interrupted and aborted brr processing during the proof. These issues do not affect the behavior or soundness of the theorem prover.




    acl2-sources/doc/HTML/BRR_at_.html0000664002132200015000000001371512222333516016230 0ustar kaufmannacl2 BRR_at_.html -- ACL2 Version 6.3

    BRR@

    to access context sensitive information within break-rewrite
    Major Section:  BREAK-REWRITE
    

    Example:
    (brr@ :target)      ; the term being rewritten
    (brr@ :unify-subst) ; the unifying substitution
    
    General Form:
    (brr@ :symbol)
    
    where :symbol is one of the following keywords. Those marked with * probably require an implementor's knowledge of the system to use effectively. They are supported but not well documented. More is said on this topic following the table.
    :symbol             (brr@ :symbol)
    -------             ---------------------
    
    :target             the term to be rewritten.  This term is an
                        instantiation of the left-hand side of the
                        conclusion of the rewrite-rule being broken.
                        This term is in translated form!  Thus, if
                        you are expecting (equal x nil) -- and your
                        expectation is almost right -- you will see
                        (equal x 'nil); similarly, instead of (cadr a)
                        you will see (car (cdr a)).  In translated
                        forms, all constants are quoted (even nil, t,
                        strings and numbers) and all macros are
                        expanded.
    
    :unify-subst        the substitution that, when applied to :target,
                        produces the left-hand side of the rule being
                        broken.  This substitution is an alist pairing
                        variable symbols to translated (!) terms.
    
    :wonp               t or nil indicating whether the rune was
                        successfully applied.  (brr@ :wonp) returns
                        nil if evaluated before :EVALing the rule.
    
    :rewritten-rhs      the result of successfully applying the rule
                        or else nil if (brr@ :wonp) is nil.  The result
                        of successfully applying the rule is always a
                        translated (!) term and is never nil.
    
    :failure-reason     some non-nil lisp object indicating why the rule
                        was not applied or else nil.  Before the rule is
                        :EVALed, (brr@ :failure-reason) is nil.  After
                        :EVALing the rule, (brr@ :failure-reason) is nil
                        if (brr@ :wonp) is t.  Rather than document the
                        various non-nil objects returned as the failure
                        reason, we encourage you simply to evaluate
                        (brr@ :failure-reason) in the contexts of interest.
                        Alternatively, study the ACL2 function tilde-@-
                        failure-reason-phrase.
    
    :lemma           *  the rewrite rule being broken.  For example,
                        (access rewrite-rule (brr@ :lemma) :lhs) will
                        return the left-hand side of the conclusion
                        of the rule.
    
    :type-alist      *  a display of the type-alist governing :target.
                        Elements on the displayed list are of the form
                        (term type), where term is a term and type
                        describes information about term assumed to hold
                        in the current context.  The type-alist may be
                        used to determine the current assumptions, e.g.,
                        whether A is a CONSP.
    
    :ancestors       *  a stack of frames indicating the backchain history
                        of the current context.  The theorem prover is in
                        the process of trying to establish each hypothesis
                        in this stack.  Thus, the negation of each hypothesis
                        can be assumed false.  Each frame also records the
                        rules on behalf of which this backchaining is being
                        done and the weight (function symbol count) of the
                        hypothesis.  All three items are involved in the
                        heuristic for preventing infinite backchaining.
                        Exception:  Some frames are ``binding hypotheses''
                        (equal var term) or (equiv var (double-rewrite term))
                        that bind variable var to the result of rewriting
                        term.
    
    :gstack          *  the current goal stack.  The gstack is maintained
                        by rewrite and is the data structure printed as the
                        current ``path.''  Thus, any information derivable
                        from the :path brr command is derivable from gstack.
                        For example, from gstack one might determine that
                        the current term is the second hypothesis of a
                        certain rewrite rule.
    
    In general brr@-expressions are used in break conditions, the expressions that determine whether interactive breaks occur when monitored runes are applied. See monitor. For example, you might want to break only those attempts in which one particular term is being rewritten or only those attempts in which the binding for the variable a is known to be a consp. Such conditions can be expressed using ACL2 system functions and the information provided by brr@. Unfortunately, digging some of this information out of the internal data structures may be awkward or may, at least, require intimate knowledge of the system functions. But since conditional expressions may employ arbitrary functions and macros, we anticipate that a set of convenient primitives will gradually evolve within the ACL2 community. It is to encourage this evolution that brr@ provides access to the *'d data.




    acl2-sources/doc/HTML/BUILT-IN-CLAUSE.html0000664002132200015000000001420712222333530017106 0ustar kaufmannacl2 BUILT-IN-CLAUSE.html -- ACL2 Version 6.3

    BUILT-IN-CLAUSE

    to build a clause into the simplifier
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example:
    (defthm acl2-count-abl
      (and (implies (and (true-listp x)
                         (not (equal x nil)))
                    (< (acl2-count (abl x))
                       (acl2-count x)))
           (implies (and (true-listp x)
                         (not (equal nil x)))
                    (< (acl2-count (abl x))
                       (acl2-count x))))
      :rule-classes :built-in-clause)
    

    A :built-in-clause rule can be built from any formula other than propositional tautologies. Roughly speaking, the system uses the list of built-in clauses as the first method of proof when attacking a new goal. Any goal that is subsumed by a built in clause is proved ``silently.''

    ACL2 maintains a set of ``built-in'' clauses that are used to short-circuit certain theorem proving tasks. We discuss this at length below. When a theorem is given the rule class :built-in-clause ACL2 flattens the implies and and structure of the :corollary formula so as to obtain a set of formulas whose conjunction is equivalent to the given corollary. It then converts each of these to clausal form and adds each clause to the set of built-in clauses.

    The example above (regardless of the definition of abl) will build in two clauses,

    {(not (true-listp x))
     (equal x nil)
     (< (acl2-count (abl x)) (acl2-count x))}
    
    and
    {(not (true-listp x))
     (equal nil x)
     (< (acl2-count (abl x)) (acl2-count x))}.
    
    We now give more background.

    Recall that a clause is a set of terms, implicitly representing the disjunction of the terms. Clause c1 is ``subsumed'' by clause c2 if some instance of c2 is a subset c1.

    For example, let c1 be

    {(not (consp l))
     (equal a (car l))
     (< (acl2-count (cdr l)) (acl2-count l))}.
    
    Then c1 is subsumed by c2, shown below,
    {(not (consp x))
     ; second term omitted here
     (< (acl2-count (cdr x)) (acl2-count x))}
    
    because we can instantiate x in c2 with l to obtain a subset of c1.

    Observe that c1 is the clausal form of

    (implies (and (consp l)
                  (not (equal a (car l))))
             (< (acl2-count (cdr l)) (acl2-count l))),
    
    c2 is the clausal form of
    (implies (consp l)
             (< (acl2-count (cdr l)) (acl2-count l)))
    
    and the subsumption property just means that c1 follows trivially from c2 by instantiation.

    The set of built-in clauses is just a set of known theorems in clausal form. Any formula that is subsumed by a built-in clause is thus a theorem. If the set of built-in theorems is reasonably small, this little theorem prover is fast. ACL2 uses the ``built-in clause check'' in four places: (1) at the top of the iteration in the prover -- thus if a built-in clause is generated as a subgoal it will be recognized when that goal is considered, (2) within the simplifier so that no built-in clause is ever generated by simplification, (3) as a filter on the clauses generated to prove the termination of recursively defun'd functions and (4) as a filter on the clauses generated to verify the guards of a function.

    The latter two uses are the ones that most often motivate an extension to the set of built-in clauses. Frequently a given formalization problem requires the definition of many functions which require virtually identical termination and/or guard proofs. These proofs can be short-circuited by extending the set of built-in clauses to contain the most general forms of the clauses generated by the definitional schemes in use.

    The attentive user might have noticed that there are some recursive schemes, e.g., recursion by cdr after testing consp, that ACL2 just seems to ``know'' are ok, while for others it generates measure clauses to prove. Actually, it always generates measure clauses but then filters out any that pass the built-in clause check. When ACL2 is initialized, the clause justifying cdr recursion after a consp test is added to the set of built-in clauses. (That clause is c2 above.)

    Note that only a subsumption check is made; no rewriting or simplification is done. Thus, if we want the system to ``know'' that cdr recursion is ok after a negative atom test (which, by the definition of atom, is the same as a consp test), we have to build in a second clause. The subsumption algorithm does not ``know'' about commutative functions. Thus, for predictability, we have built in commuted versions of each clause involving commutative functions. For example, we build in both

    {(not (integerp x))
     (< 0 x)
     (= x 0)
     (< (acl2-count (+ -1 x)) (acl2-count x))}
    
    and the commuted version
    {(not (integerp x))
     (< 0 x)
     (= 0 x)
     (< (acl2-count (+ -1 x)) (acl2-count x))}
    
    so that the user need not worry whether to write (= x 0) or (= 0 x) in definitions.

    :built-in-clause rules added by the user can be enabled and disabled.




    acl2-sources/doc/HTML/BUTLAST.html0000664002132200015000000000261512222333523016071 0ustar kaufmannacl2 BUTLAST.html -- ACL2 Version 6.3

    BUTLAST

    all but a final segment of a list
    Major Section:  ACL2-BUILT-INS
    

    (Butlast l n) is the list obtained by removing the last n elements from the true list l. The following is a theorem (though it takes some effort, including lemmas, to get ACL2 to prove it).

    (implies (and (integerp n)
                  (<= 0 n)
                  (true-listp l))
             (equal (length (butlast l n))
                    (if (< n (length l))
                        (- (length l) n)
                      0)))
    
    For related functions, see take and see nthcdr.

    The guard for (butlast l n) requires that n is a nonnegative integer and lst is a true list.

    Butlast is a Common Lisp function. See any Common Lisp documentation for more information. Note: In Common Lisp the second argument of butlast is optional, but in ACL2 it is required.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/BY.html0000664002132200015000000000064512222333520015263 0ustar kaufmannacl2 BY.html -- ACL2 Version 6.3

    BY

    hints keyword :BY
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/CAAAAR.html0000664002132200015000000000076612222333523015670 0ustar kaufmannacl2 CAAAAR.html -- ACL2 Version 6.3

    CAAAAR

    car of the caaar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CAAADR.html0000664002132200015000000000076612222333523015673 0ustar kaufmannacl2 CAAADR.html -- ACL2 Version 6.3

    CAAADR

    car of the caadr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CAAAR.html0000664002132200015000000000076212222333523015563 0ustar kaufmannacl2 CAAAR.html -- ACL2 Version 6.3

    CAAAR

    car of the caar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CAADAR.html0000664002132200015000000000076612222333523015673 0ustar kaufmannacl2 CAADAR.html -- ACL2 Version 6.3

    CAADAR

    car of the cadar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CAADDR.html0000664002132200015000000000076612222333523015676 0ustar kaufmannacl2 CAADDR.html -- ACL2 Version 6.3

    CAADDR

    car of the caddr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CAADR.html0000664002132200015000000000076212222333523015566 0ustar kaufmannacl2 CAADR.html -- ACL2 Version 6.3

    CAADR

    car of the cadr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CAAR.html0000664002132200015000000000075612222333523015465 0ustar kaufmannacl2 CAAR.html -- ACL2 Version 6.3

    CAAR

    car of the car
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADAAR.html0000664002132200015000000000076612222333523015673 0ustar kaufmannacl2 CADAAR.html -- ACL2 Version 6.3

    CADAAR

    car of the cdaar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADADR.html0000664002132200015000000000076612222333523015676 0ustar kaufmannacl2 CADADR.html -- ACL2 Version 6.3

    CADADR

    car of the cdadr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADAR.html0000664002132200015000000000076212222333523015566 0ustar kaufmannacl2 CADAR.html -- ACL2 Version 6.3

    CADAR

    car of the cdar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADDAR.html0000664002132200015000000000076612222333523015676 0ustar kaufmannacl2 CADDAR.html -- ACL2 Version 6.3

    CADDAR

    car of the cddar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADDDR.html0000664002132200015000000000076612222333523015701 0ustar kaufmannacl2 CADDDR.html -- ACL2 Version 6.3

    CADDDR

    car of the cdddr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADDR.html0000664002132200015000000000076212222333523015571 0ustar kaufmannacl2 CADDR.html -- ACL2 Version 6.3

    CADDR

    car of the cddr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CADR.html0000664002132200015000000000075612222333523015470 0ustar kaufmannacl2 CADR.html -- ACL2 Version 6.3

    CADR

    car of the cdr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CALLING-LD-IN-BAD-CONTEXTS.html0000664002132200015000000000452712222333522020461 0ustar kaufmannacl2 CALLING-LD-IN-BAD-CONTEXTS.html -- ACL2 Version 6.3

    CALLING-LD-IN-BAD-CONTEXTS

    errors caused by calling ld in inappropriate contexts
    Major Section:  LD
    

    The macro ld was designed to be called directly in the top-level ACL2 loop, although there may be a few occasions for calling it from functions. ACL2 cannot cope with invocations of ld during the process of loading a compiled file for a book, so this is an error.

    To see how that can happen, consider the following book, where file const.lsp contains the single form (defconst *foo* '(a b)).

      (in-package "ACL2")
      (defttag t)
      (progn! (ld "const.lsp"))
    
    An attempt to certify this book will cause an error, but that particular error can be avoided, as discussed below. If the book is certified, however, with production of a corresponding compiled file (which is the default behavior for certify-book), then any subsequent call of include-book that loads this compiled file will cause an error. Again, this error is necessary because of how ACL2 is designed; specifically, this ld call would interfere with tracking of constant definitions when loading the compiled file for the book.

    Because including such a book (with a compiled file) causes an error, then as a courtesy to the user, ACL2 arranges that the certification will fail (thus avoiding a surprise later when trying to include the book). The error in that case will look as follows.

      ACL2 Error in LD:  It is illegal to call LD in this context.  See DOC
      calling-ld-in-bad-contexts.
    
    If you really think it is OK to avoid this error, you can get around it by setting state global variable ld-okp to t: (assign ld-okp t). You can then certify the book in the example above, but you will still not be able to include it with a compiled file.




    acl2-sources/doc/HTML/CANONICAL-PATHNAME.html0000664002132200015000000000444312222333523017436 0ustar kaufmannacl2 CANONICAL-PATHNAME.html -- ACL2 Version 6.3

    CANONICAL-PATHNAME

    the true absolute filename, with soft links resolved
    Major Section:  ACL2-BUILT-INS
    

    For the name fname of a file, the form (Canonical-pathname fname nil state) evaluates to a Unix-style absolute filename representing the same file as fname, but generally without any use of soft links in the name. (Below, we explain the qualifier ``generally''.) If however the file indicated by fname does not exist, (canonical-pathname fname nil state) is nil. Thus, canonical-pathname can be used as one would use the raw Lisp function probe-file.

    The specification of (Canonical-pathname fname dir-p state) when dir-p is not nil is simlar, except that if the specified file exists but is not a directory, then the result is nil.

    The function canonical-pathname has a guard of t, though the second argument must be the ACL2 state. This function is introduced with the following properties.

    (defthm canonical-pathname-is-idempotent
      (equal (canonical-pathname (canonical-pathname x dir-p state) dir-p state)
             (canonical-pathname x dir-p state)))
    (defthm canonical-pathname-type
      (or (equal (canonical-pathname x dir-p state) nil)
          (stringp (canonical-pathname x dir-p state)))
      :rule-classes :type-prescription)
    

    We use the qualifier ``generally'', above, because there is no guarantee that the filename will be canonical without soft links, though we expect this to be true in practice. ACL2 attempts to compute the desired result and then checks that the input and result have the same Common Lisp ``truename''. This check is expected to succeed, but if it fails then the input string is returned unchanged, and to be conservative, the value returned is nil in this case if dir-p is true.




    acl2-sources/doc/HTML/CAR.html0000664002132200015000000000137412222333523015361 0ustar kaufmannacl2 CAR.html -- ACL2 Version 6.3

    CAR

    returns the first element of a non-empty list, else nil
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-car):

    (equal (car x)
           (cond
            ((consp x)
             (car x))
            (t nil)))
    

    Guard:

    (or (consp x) (equal x nil))
    
    Notice that in the ACL2 logic, car returns nil for every atom.




    acl2-sources/doc/HTML/CASE-MATCH.html0000664002132200015000000000674012222333523016323 0ustar kaufmannacl2 CASE-MATCH.html -- ACL2 Version 6.3

    CASE-MATCH

    pattern matching or destructuring
    Major Section:  ACL2-BUILT-INS
    

    General Form:
    (case-match x
      (pat1 dcl1 body1)
      ...
      (patk dclk bodyk))
    
    where x is a variable symbol, the pati are structural patterns as described below, the dcli are optional declare forms and the bodyi are terms. Return the value(s) of the bodyi corresponding to the first pati matching x, or nil if none matches.

    Pattern Language:
    With the few special exceptions described below, matching requires that the cons structure of x be isomorphic to that of the pattern, down to the atoms in the pattern. Non-symbol atoms in the pattern match only themselves. Symbols in the pattern denote variables which match anything and which are bound by a successful match to the corresponding substructure of x. Variables that occur more than once must match the same (EQUAL) structure in every occurrence.

    Exceptions:
    &               Matches anything and is not bound.  Repeated
                      occurrences of & in a pattern may match different
                      structures.
    nil, t, *sym*   These symbols cannot be bound and match only their
                      global values.
    !sym            where sym is a symbol that is already bound in the
                      context of the case-match, matches only the
                      current binding of sym.
    'obj            Matches only itself.
    
    Some examples are shown below.

    Below we show some sample patterns and examples of things they match and do not match.

    pattern       matches         non-matches
    (x y y)       (ABC 3 3)       (ABC 3 4)    ; 3 is not 4
    (fn x . rst)  (P (A I) B C)   (ABC)        ; NIL is not (x . rst)
                  (J (A I))                    ; rst matches nil
    ('fn (g x) 3) (FN (H 4) 3)    (GN (G X) 3) ; 'fn matches only itself
    (& t & !x)    ((A) T (B) (C))              ; provided x is '(C)
    
    Consider the two binary trees that contain three leaves. They might be described as (x . (y . z)) and ((x . y) . z), where x, y, and z are atomic. Suppose we wished to recognize those trees. The following case-match would do:
    (case-match tree
      ((x . (y . z))
       (and (atom x) (atom y) (atom z)))
      (((x . y) . z)
       (and (atom x) (atom y) (atom z))))
    
    Suppose we wished to recognize such trees where all three tips are identical. Suppose further we wish to return the tip if the tree is one of those recognized ones and to return the number 7 otherwise.
    (case-match tree
      ((x . (x . x))
       (if (atom x) x 7))
      (((x . x) . x)
       (if (atom x) x 7))
      (& 7))
    
    Note that case-match returns nil if no pati matches. Thus if we must return 7 in that case, we have to add as the final pattern the &, which always matches anything.




    acl2-sources/doc/HTML/CASE-SPLIT-LIMITATIONS.html0000664002132200015000000000205712222333520020146 0ustar kaufmannacl2 CASE-SPLIT-LIMITATIONS.html -- ACL2 Version 6.3

    CASE-SPLIT-LIMITATIONS

    a list of two ``numbers'' limiting the number of cases produced at once
    Major Section:  MISCELLANEOUS
    

    Examples:
    ACL2 !>(case-split-limitations (w state))
    (500 100)
    
    With the setting above, clausify will not try subsumption/replacement if more than 500 clauses are involved. Furthermore, the simplifier, as it sweeps over a clause, will inhibit further case splits when it has accumulated 100 subgoals. This inhibition is implemented by continuing to rewrite subsequent literals but not splitting out their cases. This can produce literals containing IFs.

    See set-case-split-limitations for a more general discussion.




    acl2-sources/doc/HTML/CASE-SPLIT.html0000664002132200015000000000634512222333520016360 0ustar kaufmannacl2 CASE-SPLIT.html -- ACL2 Version 6.3

    CASE-SPLIT

    like force but immediately splits the top-level goal on the hypothesis
    Major Section:  MISCELLANEOUS
    

    Case-split is an variant of force, which has similar special treatment in hypotheses of rules for the same rule-classes as for force (see force). This treatment takes place for rule classes :rewrite, :linear, :type-prescription, :definition, :meta (actually in that case, the result of evaluating the hypothesis metafunction call), and :forward-chaining.

    When a hypothesis of a conditional rule (of one of the classes listed above) has the form (case-split hyp) it is logically equivalent to hyp. However it affects the application of the rule generated as follows: if ACL2 attempts to apply the rule but cannot establish that the required instance of hyp holds in the current context, it considers the hypothesis true anyhow, but (assuming all hypotheses are seen to be true and the rule is applied) creates a subgoal in which that instance of hyp is assumed false. (There are exceptions, noted below.)

    For example, given the rule

    (defthm p1->p2
      (implies (case-split (p1 x))
               (p2 x)))
    
    then an attempt to prove
    (implies (p3 x) (p2 (car x)))
    
    can give rise to a single subgoal:
    (IMPLIES (AND (NOT (P1 (CAR X))) (P3 X))
             (P2 (CAR X))).
    
    Unlike force, case-split does not delay the ``false case'' to a forcing round but tackles it more or less immediately.

    The special ``split'' treatment of case-split can be disabled by disabling forcing: see force for a discussion of disabling forcing, and also see disable-forcing. Finally, we should mention that the rewriter is never willing to split when there is an if term present in the goal being simplified. Since and terms and or terms are merely abbreviations for if terms, they also prevent splitting. Note that if terms are ultimately eliminated using the ordinary flow of the proof (but see set-case-split-limitations), so case-split will ultimately function as intended.

    When in the proof checker, case-split behaves like force.




    acl2-sources/doc/HTML/CASE.html0000664002132200015000000000320512222333523015462 0ustar kaufmannacl2 CASE.html -- ACL2 Version 6.3

    CASE

    conditional based on if-then-else using eql
    Major Section:  ACL2-BUILT-INS
    

    Example Form:
    (case typ
      ((:character foo)
       (open file-name :direction :output))
      (bar (open-for-bar file-name))
      (otherwise
       (my-error "Illegal.")))
    
    is the same as
    (cond ((member typ '(:character foo))
           (open file-name :direction :output))
          ((eql typ 'bar)
           (open-for-bar file-name))
          (t (my-error "Illegal.")))
    
    which in turn is the same as
    (if (member typ '(:character foo))
        (open file-name :direction :output)
        (if (eql typ 'bar)
            (open-for-bar file-name)
            (my-error "Illegal.")))
    
    
    Notice the quotations that appear in the example above: '(:character foo) and 'bar.

    General Forms:
    (case expr
      (x1 val-1)
      ...
      (xk val-k)
      (otherwise val-k+1))
    
    (case expr
      (x1 val-1)
      ...
      (xk val-k)
      (t val-k+1))
    
    (case expr
      (x1 val-1)
      ...
      (xk val-k))
    
    where each xi is either eqlablep or a true list of eqlablep objects. The final otherwise or t case is optional.

    Case is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/CASES.html0000664002132200015000000000065612222333520015611 0ustar kaufmannacl2 CASES.html -- ACL2 Version 6.3

    CASES

    hints keyword :CASES
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/CBD.html0000664002132200015000000001461212222333516015345 0ustar kaufmannacl2 CBD.html -- ACL2 Version 6.3

    CBD

    connected book directory string
    Major Section:  BOOKS
    

    Example:
    ACL2 !>:cbd
    "/usr/home/smith/"
    
    The connected book directory is a nonempty string that specifies a directory as an absolute pathname. (See pathname for a discussion of file naming conventions.) When include-book is given a relative book name it elaborates it into a full book name, essentially by appending the connected book directory string to the left and ".lisp" to the right. (For details, see book-name and also see full-book-name.) Furthermore, include-book temporarily sets the connected book directory to the directory string of the resulting full book name so that references to inferior books in the same directory may omit the directory. See set-cbd for how to set the connected book directory string.

    General Form:
    (cbd)
    
    This is a macro that expands into a term involving the single free variable state. It returns the connected book directory string.

    The connected book directory (henceforth called the ``cbd'') is used by include-book to elaborate the supplied book name into a full book name (see full-book-name). For example, if the cbd is "/usr/home/smith/" then the elaboration of the book-name "project/task-1/arith" (to the ".lisp" extension) is "/usr/home/smith/project/task-1/arith.lisp". That full-book-name is what include-book opens to read the source text for the book.

    The cbd may be changed using set-cbd (see set-cbd). Furthermore, during the processing of the events in a book, include-book sets the cbd to be the directory string of the full-book-name of the book. Thus, if the cbd is "/usr/home/smith/" then during the processing of events by

    (include-book "project/task-1/arith")
    
    the cbd will be set to "/usr/home/smith/project/task-1/". Note that if "arith" recursively includes a subbook, say "naturals", that resides on the same directory, the include-book event for it may omit the specification of that directory. For example, "arith" might contain the event
      (include-book "naturals").
    
    In general, suppose we have a superior book and several inferior books which are included by events in the superior book. Any inferior book residing on the same directory as the superior book may be referenced in the superior without specification of the directory.

    We call this a ``relative'' as opposed to ``absolute'' naming. The use of relative naming is preferred because it permits books (and their accompanying inferiors) to be moved between directories while maintaining their certificates and utility. Certified books that reference inferiors by absolute file names are unusable (and rendered uncertified) if the inferiors are moved to new directories.

    Technical Note and a Challenge to Users:

    After elaborating the book name to a full book name, include-book opens a channel to the file to process the events in it. In some host Common Lisps, the actual file opened depends upon a notion of ``connected directory'' similar to our connected book directory. Our intention in always elaborating book names into absolute filename strings (see pathname for terminology) is to circumvent the sensitivity to the connected directory. But we may have insufficient control over this since the ultimate file naming conventions are determined by the host operating system rather than Common Lisp (though, we do check that the operating system ``appears'' to be one that we ``know'' about). Here is a question, which we'll pose assuming that we have an operating system that calls itself ``Unix.'' Suppose we have a file name, filename, that begins with a slash, e.g., "/usr/home/smith/...". Consider two successive invocations of CLTL's

    (open filename :direction :input)
    
    separated only by a change to the operating system's notion of connected directory. Must these two invocations produce streams to the same file? A candidate string might be something like "/usr/home/smith/*/usr/local/src/foo.lisp" which includes some operating system-specific special character to mean ``here insert the connected directory'' or, more generally, ``here make the name dependent on some non-ACL2 aspect of the host's state.'' If such ``tricky'' name strings beginning with a slash exist, then we have failed to isolate ACL2 adequately from the operating system's file naming conventions. Once upon a time, ACL2 did not insist that the cbd begin with a slash and that allowed the string "foo.lisp" to be tricky because if one were connected to "/usr/home/smith/" then with the empty cbd "foo.lisp" is a full book name that names the same file as "/usr/home/smith/foo.lisp". If the actual file one reads is determined by the operating system's state then it is possible for ACL2 to have two distinct ``full book names'' for the same file, the ``real'' name and the ``tricky'' name. This can cause ACL2 to include the same book twice, not recognizing the second one as redundant.




    acl2-sources/doc/HTML/CDAAAR.html0000664002132200015000000000076612222333523015673 0ustar kaufmannacl2 CDAAAR.html -- ACL2 Version 6.3

    CDAAAR

    cdr of the caaar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDAADR.html0000664002132200015000000000076612222333523015676 0ustar kaufmannacl2 CDAADR.html -- ACL2 Version 6.3

    CDAADR

    cdr of the caadr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDAAR.html0000664002132200015000000000076212222333523015566 0ustar kaufmannacl2 CDAAR.html -- ACL2 Version 6.3

    CDAAR

    cdr of the caar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDADAR.html0000664002132200015000000000076612222333523015676 0ustar kaufmannacl2 CDADAR.html -- ACL2 Version 6.3

    CDADAR

    cdr of the cadar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDADDR.html0000664002132200015000000000076612222333523015701 0ustar kaufmannacl2 CDADDR.html -- ACL2 Version 6.3

    CDADDR

    cdr of the caddr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDADR.html0000664002132200015000000000076212222333523015571 0ustar kaufmannacl2 CDADR.html -- ACL2 Version 6.3

    CDADR

    cdr of the cadr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDAR.html0000664002132200015000000000075612222333523015470 0ustar kaufmannacl2 CDAR.html -- ACL2 Version 6.3

    CDAR

    cdr of the car
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDAAR.html0000664002132200015000000000076612222333523015676 0ustar kaufmannacl2 CDDAAR.html -- ACL2 Version 6.3

    CDDAAR

    cdr of the cdaar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDADR.html0000664002132200015000000000076612222333523015701 0ustar kaufmannacl2 CDDADR.html -- ACL2 Version 6.3

    CDDADR

    cdr of the cdadr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDAR.html0000664002132200015000000000076212222333523015571 0ustar kaufmannacl2 CDDAR.html -- ACL2 Version 6.3

    CDDAR

    cdr of the cdar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDDAR.html0000664002132200015000000000076612222333523015701 0ustar kaufmannacl2 CDDDAR.html -- ACL2 Version 6.3

    CDDDAR

    cdr of the cddar
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDDDR.html0000664002132200015000000000076612222333523015704 0ustar kaufmannacl2 CDDDDR.html -- ACL2 Version 6.3

    CDDDDR

    cdr of the cdddr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDDR.html0000664002132200015000000000076212222333523015574 0ustar kaufmannacl2 CDDDR.html -- ACL2 Version 6.3

    CDDDR

    cdr of the cddr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDDR.html0000664002132200015000000000075612222333523015473 0ustar kaufmannacl2 CDDR.html -- ACL2 Version 6.3

    CDDR

    cdr of the cdr
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/CDR.html0000664002132200015000000000143512222333523015362 0ustar kaufmannacl2 CDR.html -- ACL2 Version 6.3

    CDR

    returns the second element of a cons pair, else nil
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-cdr):

    (equal (cdr x)
           (cond
            ((consp x)
             (cdr x))
            (t nil)))
    

    Guard:

    (or (consp x) (equal x nil))
    
    Notice that in the ACL2 logic, cdr returns nil for every atom.




    acl2-sources/doc/HTML/CEILING.html0000664002132200015000000000265312222333523016027 0ustar kaufmannacl2 CEILING.html -- ACL2 Version 6.3

    CEILING

    division returning an integer by truncating toward positive infinity
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    ACL2 !>(ceiling 14 3)
    5
    ACL2 !>(ceiling -14 3)
    -4
    ACL2 !>(ceiling 14 -3)
    -4
    ACL2 !>(ceiling -14 -3)
    5
    ACL2 !>(ceiling -15 -3)
    5
    
    (Ceiling i j) is the result of taking the quotient of i and j and returning the smallest integer that is at least as great as that quotient. For example, the quotient of -14 by 3 is -4 2/3, and the smallest integer at least that great is -4.

    The guard for (ceiling i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero.

    Ceiling is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 ceiling function returns only a single value,

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CERTIFICATE.html0000664002132200015000000001321712222333516016477 0ustar kaufmannacl2 CERTIFICATE.html -- ACL2 Version 6.3

    CERTIFICATE

    how a book is known to be admissible and where its defpkgs reside
    Major Section:  BOOKS
    

    A book, say "arith", is said to have a ``certificate'' if there is a file named "arith.cert". Certificates are created by the function certify-book and inspected by include-book. Check sums are used to help ensure that certificates are legitimate and that the corresponding book has not been modified since certification. But because the file system is insecure and check sums are not perfect it is possible for the inclusion of a book to cause inconsistency even though the book carries an impeccable certificate.

    The certificate includes the version number of the certifying ACL2. A book is considered uncertified if it is included in an ACL2 with a different version number.

    The presence of a ``valid'' certificate file for a book attests to two things: all of the events of the book are admissible in a certain extension of the initial ACL2 logic, and the non-local events of the book are independent of the local ones (see local-incompatibility). In addition, the certificate contains the commands used to construct the world in which certification occurred. Among those commands, of course, are the defpkgs defining the packages used in the book. When a book is included into a host world, that world is first extended by the commands listed in the certificate for the book. Unless that causes an error due to name conflicts, the extension ensures that all the packages used by the book are identically defined in the host world.

    Security:

    Because the host file system is insecure, there is no way ACL2 can guarantee that the contents of a book remain the same as when its certificate was written. That is, between the time a book is certified and the time it is used, it may be modified. Furthermore, certificates can be counterfeited. Check sums (see check-sum) are used to help detect such problems. But check sums provide imperfect security: two different files can have the same check sum.

    Therefore, from the strictly logical point of view, one must consider even the inclusion of certified books as placing a burden on the user:

    The non-erroneous inclusion of a certified book is consistency preserving provided (a) the objects read by include-book from the certificate were the objects written there by a certify-book and (b) the forms read by include-book from the book itself are the forms read by the corresponding certify-book.

    We say that a given execution of include-book is ``certified'' if a certificate file for the book is present and well-formed and the check sum information contained within it supports the conclusion that the events read by the include-book are the ones checked by certify-book. When an uncertified include-book occurs, warnings are printed or errors are caused. But even if no warning is printed, you must accept burdens (a) and (b) if you use books. These burdens are easier to live with if you protect your books so that other users cannot write to them, you abstain from running concurrent ACL2 jobs, and you abstain from counterfeiting certificates. But even on a single user uniprocessor, you can shoot yourself in the foot by using the ACL2 io primitives to fabricate an inconsistent book and the corresponding certificate.

    Note that part (a) of the burden described above implies, in particular, that there are no guarantees when a certificate is copied. When books are renamed (as by copying them), it is recommended that their certificates be removed and the books be recertified. The expectation is that recertification will go through without a hitch if relative pathnames are used. See pathname, which is not on the guided tour.

    Certificates essentially contain two parts, a portcullis and a keep. There is a third part, an expansion-alist, in order to record expansions if make-event has been used, but the user need not be concerned with that level of detail.

    See portcullis to continue the guided tour through books.




    acl2-sources/doc/HTML/CERTIFY-BOOK.html0000664002132200015000000003746412222333516016624 0ustar kaufmannacl2 CERTIFY-BOOK.html -- ACL2 Version 6.3

    CERTIFY-BOOK

    how to produce a certificate for a book
    Major Section:  BOOKS
    

    Examples:
    (certify-book "my-arith")          ; certify in a world with 0 commands
    (certify-book "my-arith" 3)        ; ... in a world with 3 commands
    (certify-book "my-arith" ?)        ; ... in a world without checking the
                                         ;     number of commands
    (certify-book "my-arith" 0 nil)    ; ... without compilation
    (certify-book "my-arith" 0 t)      ; ... with compilation (default)
    (certify-book "my-arith" 0 t :ttags (foo))
                                         ; ... allowing trust tag (ttag) foo
    (certify-book "my-arith" 0 t :ttags :all)
                                         ; ... allowing all trust tags (ttags)
    (certify-book "my-arith" t)        ; ... from world of old certificate
    (certify-book "my-arith" 0 nil :acl2x t)
                                         ; ... writing or reading a .acl2x file
    
    General Form:
    (certify-book book-name
                  k                           ; [default 0]
                  compile-flg                 ; [default t]
                  :defaxioms-okp t/nil        ; [default nil]
                  :skip-proofs-okp t/nil      ; [default nil]
                  :ttags ttags                ; [default nil]
                  :acl2x t/nil                ; [default nil]
                  :ttagsx ttags               ; [default nil]
                  :write-port t/nil           ; [default t]
                  :pcert pcert                ; [default nil]
                  )
    
    where book-name is a book name (see book-name), k is used to indicate your approval of the ``certification world,'' and compile-flg can control whether the book is to be compiled. The defaults for compile-flg, skip-proofs-okp, acl2x, write-port, and pcert can be affected by environment variables. All of these arguments are described in detail below, except for :pcert. (We assume below that the value of :pcert is nil (and environment variable ACL2_PCERT_ARG is unset or the empty string). For a discussion of this argument, see provisional-certification.)

    Certification occurs in some logical world, called the ``certification world.'' That world must contain the defpkgs needed to read and execute the forms in the book. The commands necessary to recreate that world from the ACL2 initial world are called the ``portcullis commands,'' and will be copied into the certificate created for the book. Those commands will be re-executed whenever the book is included, to ensure that the appropriate packages (and all other names used in the certification world) are correctly defined. The certified book will be more often usable if the certification world is kept to a minimal extension of the ACL2 initial world (for example, to prevent name clashes with functions defined in other books). Thus, before you call certify-book for the first time on a book, you may wish to get into the initial ACL2 world (e.g., with :ubt 1 or just starting a new version of ACL2), defpkg the desired packages, and then invoke certify-book.

    The k argument to certify-book must be either a nonnegative integer or else one of the symbols t or ? in any package. If k is an integer, then it must be the number of commands that have been executed after the initial ACL2 world to create the world in which certify-book was called. One way to obtain this number is by doing :pbt :start to see all the commands back to the first one.

    If k is t (or any symbol whose symbol-name is "T"), it means that certify-book should use the same world used in the last certification of this book. K may have such a value only if you call certify-book in the initial ACL2 world and there is a certificate on file for the book being certified. (Of course, the certificate is probably invalid.) In this case, certify-book reads the old certificate to obtain the portcullis commands and executes them to recreate the certification world.

    Finally, k may be ? (or any symbol whose symbol-name is "?"), in which case there is no check made on the certification world. That is, if k is such a value then no action related to the preceding two paragraphs is performed, which can be a nice convenience but at the cost of eliminating a potentially valuable check that the certification world may be as expected.

    We next describe the meaning of compile-flg and how it defaults. If explicit compilation has been suppressed by (set-compiler-enabled nil state), then compile-flg is coerced to nil; see compilation. Otherwise compile-flg may be given the value of t (or :all, which is equivalent to t except during provisional certification; see provisional-certification), indicating that the book is to be compiled, or else nil. (Note that compilation initially creates a compiled file with a temporary file name, and then moves that temporary file to the final compiled file name obtained by adding a suitable extension to the book name. Thus, a compiled file will appear atomically in its intended location.) Finally, suppose that compile-flg is not supplied (or is :default). If environment variable ACL2_COMPILE_FLG is defined and not the empty string, then its value should be T, NIL, or ALL after converting to upper case, in which case compile-flg is considered to have value t, nil, or :all (respectively). Otherwise compile-flg defaults to t. Note that the value :all is equivalent to t except for during the Convert procedure of provisional certification; see provisional-certification.

    Two keyword arguments, :defaxioms-okp and :skip-proofs-okp, determine how the system handles the inclusion of defaxiom events and skip-proofs events, respectively, in the book. The value t allows such events, but prints a warning message. The value nil causes an error if such an event is found. Nil is the default unless keyword argument :acl2x t is provided and state global 'write-acl2x is a cons (see set-write-acl2x), in which case the default is t.

    The keyword argument :ttags may normally be omitted. A few constructs, used for example if you are building your own system based on ACL2, may require it. See defttag for an explanation of this argument.

    When book B is certified with value t (the default) for keyword argument :write-port, a file B.port is written by certification process. This file contains all of the portcullis commands for B, i.e., all user commands present in the ACL2 logical world at the time certify-book is called. if B.lisp later becomes uncertified, say because events from that file or an included book have been edited, then (include-book "B") will consult B.port to evaluate forms in that file before evaluating the events in B.lisp. On the other hand, B.port is ignored when including B if B is certified.

    If you use guards, please note certify-book is executed as though (set-guard-checking nil) has been evaluated; see set-guard-checking. If you want guards checked, consider using ld instead, or in addition; see ld.

    For a general discussion of books, see books. Certify-book is akin to what we have historically called a ``proveall'': all the forms in the book are ``proved'' to guarantee their admissibility. More precisely, certify-book (1) reads the forms in the book, confirming that the appropriate packages are defined in the certification world; (2) does the full admissibility checks on each form (proving termination of recursive functions, proving theorems, etc.), checking as it goes that each form is an embedded event form (see embedded-event-form); (3) rolls the world back to the initial certification world and does an include-book of the book to check for local incompatibilities (see local-incompatibility); (4) writes a certificate recording not only that the book was certified but also recording the commands necessary to recreate the certification world (so the appropriate packages can be defined when the book is included in other worlds) and the check sums of all the books involved (see certificate); (5) compiles the book if so directed (and then loads the object file in that case). The result of executing a certify-book command is the creation of a single new event, which is actually an include-book event. If you don't want its included events in your present world, simply execute :ubt :here afterwards.

    A utility is provided to assist in debugging failures of certify-book; see redo-flat.)

    Certify-book requires that the default defun-mode (see default-defun-mode) be :logic when certification is attempted. If the mode is not :logic, an error is signalled.

    An error will occur if certify-book has to deal with any uncertified book other than the one on which it was called. For example, if the book being certified includes another book, that subbook must already have been certified.

    If you have a certified book that has remained unchanged for some time you might well not remember the appropriate defpkgs for it, though they are stored in the certificate file and (by default) also in the .port file. If you begin to change the book, don't throw away its certificate file just because it has become invalid! It is an important historical document until the book is re-certified. More important, don't throw away the .port file, as it will provide the portcullis commands when including the book as an uncertified book; see include-book.

    When certify-book is directed to produce a compiled file, it calls the Common Lisp function compile-file on the original source file. This creates a compiled file with an extension known to ACL2, e.g., if the book is named "my-book" then the source file is "my-book.lisp" and the compiled file under GCL will be "my-book.o" while under SBCL it will be "my-book.fasl". The compiled file is then loaded. When include-book is used later on "my-book" it will automatically load the compiled file, provided the compiled file has a later write date than the source file. The only effect of such compilation and loading is that the functions defined in the book execute faster. See guard for a discussion of the issues, and if you want more details about books and compilation, see book-compiled-file.

    When certify-book is directed not to produce a compiled file, it will delete any existing compiled file for the book, so as not to mislead include-book into loading the now outdated compiled file. Otherwise, certify-book will create a temporary ``expansion file'' to compile, obtained by appending the string "@expansion.lsp" to the end of the book name. Remark: Users may ignore that file, which is automatically deleted unless state global variable 'save-expansion-file has been set, presumably by a system developer, to a non-nil value; see book-compiled-file for more information about hit issue, including the role of environment variable ACL2_SAVE_EXPANSION.

    After execution of a certify-book form, the value of acl2-defaults-table is restored to what it was immediately before that certify-book form was executed. See acl2-defaults-table.

    Those who use the relatively advanced features of trust tags (see defttag) and make-event may wish to know how to create a certificate file that avoids dependence on trust tags that are used only during make-event expansion. For this, including documentation of the :acl2x and :ttagsx keyword arguments for certify-book, see set-write-acl2x.

    This completes the tour through the documentation of books.




    acl2-sources/doc/HTML/CERTIFY-BOOK_bang_.html0000664002132200015000000000301712222333522017732 0ustar kaufmannacl2 CERTIFY-BOOK_bang_.html -- ACL2 Version 6.3

    CERTIFY-BOOK!

    a variant of certify-book
    Major Section:  OTHER
    

    Examples:
    (certify-book! "my-arith" 3)     ;Certify in a world with 3
                                       ; commands, starting in a world
                                       ; with at least 3 commands.
    (certify-book! "my-arith")       ;Certify in the initial world.
    
    General Form:
    (certify-book! book-name k compile-flg)
    
    where book-name is a book name (see book-name), k is a nonnegative integer used to indicate the ``certification world,'' and compile-flg indicates whether you wish to compile the (functions in the) book.

    This command is identical to certify-book, except that the second argument k may not be t in certify-book! and if k exceeds the current command number, then an appropriate ubt! will be executed first. See certify-book and see ubt!.




    acl2-sources/doc/HTML/CERTIFYING-BOOKS.html0000664002132200015000000000065512222333516017275 0ustar kaufmannacl2 CERTIFYING-BOOKS.html -- ACL2 Version 6.3

    CERTIFYING-BOOKS

    See books-certification.
    Major Section:  BOOKS
    




    acl2-sources/doc/HTML/CHAR-CODE.html0000664002132200015000000000131312222333523016172 0ustar kaufmannacl2 CHAR-CODE.html -- ACL2 Version 6.3

    CHAR-CODE

    the numeric code for a given character
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-char-code):

    (equal (char-code x)
           (if (characterp x)
               (char-code x)
             0))
    

    Guard for (char-code x):

    (characterp x)
    
    This function maps all non-characters to 0.




    acl2-sources/doc/HTML/CHAR-DOWNCASE.html0000664002132200015000000000215712222333523016672 0ustar kaufmannacl2 CHAR-DOWNCASE.html -- ACL2 Version 6.3

    CHAR-DOWNCASE

    turn upper-case characters into lower-case characters
    Major Section:  ACL2-BUILT-INS
    

    (Char-downcase x) is equal to #\a when x is #\A, #\b when x is #\B, ..., and #\z when x is #\Z, and is x for any other character.

    The guard for char-downcase requires its argument to be a standard character (see standard-char-p).

    Char-downcase is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHAR-EQUAL.html0000664002132200015000000000200212222333523016323 0ustar kaufmannacl2 CHAR-EQUAL.html -- ACL2 Version 6.3

    CHAR-EQUAL

    character equality without regard to case
    Major Section:  ACL2-BUILT-INS
    

    For characters x and y, (char-equal x y) is true if and only if x and y are the same except perhaps for their case.

    The guard on char-equal requires that its arguments are both standard characters (see standard-char-p).

    Char-equal is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHAR-UPCASE.html0000664002132200015000000000214512222333523016444 0ustar kaufmannacl2 CHAR-UPCASE.html -- ACL2 Version 6.3

    CHAR-UPCASE

    turn lower-case characters into upper-case characters
    Major Section:  ACL2-BUILT-INS
    

    (Char-upcase x) is equal to #\A when x is #\a, #\B when x is #\b, ..., and #\Z when x is #\z, and is x for any other character.

    The guard for char-upcase requires its argument to be a standard character (see standard-char-p).

    Char-upcase is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHAR.html0000664002132200015000000000175512222333523015474 0ustar kaufmannacl2 CHAR.html -- ACL2 Version 6.3

    CHAR

    the nth element (zero-based) of a string
    Major Section:  ACL2-BUILT-INS
    

    (Char s n) is the nth element of s, zero-based. If n is greater than or equal to the length of s, then char returns nil.

    (Char s n) has a guard that n is a non-negative integer and s is a stringp.

    Char is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHARACTER-ALISTP.html0000664002132200015000000000121712222333523017236 0ustar kaufmannacl2 CHARACTER-ALISTP.html -- ACL2 Version 6.3

    CHARACTER-ALISTP

    recognizer for association lists with characters as keys
    Major Section:  ACL2-BUILT-INS
    

    (Character-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where key is a characterp.




    acl2-sources/doc/HTML/CHARACTER-ENCODING.html0000664002132200015000000000367012222333520017432 0ustar kaufmannacl2 CHARACTER-ENCODING.html -- ACL2 Version 6.3

    CHARACTER-ENCODING

    how bytes are parsed into characters
    Major Section:  IO
    

    When the Common Lisp reader comes across bytes in a file or at the terminal, they are parsed into characters. The simplest case is when each byte that is read is a standard character (see standard-char-p). It is actually quite common that each byte that is read corresponds to a single character. The parsing of bytes into characters is based on a character encoding, that is, a mapping that associates one or more bytes with each legal character.

    In order to help guarantee the portability of files (including books), ACL2 installs a common character encoding for reading files, often known as iso-8859-1 or latin-1. For some host Lisps this character encoding is also used for reading from the terminal; but, sadly, this may not hold for all host Lisps, and may not even be possible for some of them.

    The use of the above encoding could in principle cause problems if one's editor produces files using an encoding other than iso-8859-1, at least if one uses non-standard characters. In particular, the default Emacs buffer encoding may be utf-8. If your file has non-standard characters, then in Emacs you can evaluate the form

    (setq save-buffer-coding-system 'iso-8859-1)
    
    before saving the buffer into a file. This will happen automatically for users who load distributed file emacs/emacs-acl2.el into their Emacs sessions.

    For an example of character encodings in action, see the community book books/misc/character-encoding-test.lisp.




    acl2-sources/doc/HTML/CHARACTER-LISTP.html0000664002132200015000000000117212222333523017135 0ustar kaufmannacl2 CHARACTER-LISTP.html -- ACL2 Version 6.3

    CHARACTER-LISTP

    recognizer for a true list of characters
    Major Section:  ACL2-BUILT-INS
    

    The predicate character-listp tests whether its argument is a true list of characters.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHARACTERP.html0000664002132200015000000000077612222333523016375 0ustar kaufmannacl2 CHARACTERP.html -- ACL2 Version 6.3

    CHARACTERP

    recognizer for characters
    Major Section:  ACL2-BUILT-INS
    

    (characterp x) is true if and only if x is a character.




    acl2-sources/doc/HTML/CHARACTERS.html0000664002132200015000000000657112222333523016377 0ustar kaufmannacl2 CHARACTERS.html -- ACL2 Version 6.3

    CHARACTERS

    characters in ACL2
    Major Section:  ACL2-BUILT-INS
    

    ACL2 accepts 256 distinct characters, which are the characters obtained by applying the function code-char to each integer from 0 to 255. Among these, Common Lisp designates certain ones as standard characters, namely those of the form (code-char n) where n is from 33 to 126, together with #\Newline and #\Space. The actual standard characters may be viewed by evaluating the defconst *standard-chars*.

    To be more precise, Common Lisp does not specify the precise relationship between code-char and the standard characters. However, we check that the underlying Common Lisp implementation uses a particular relationship that extends the usual ASCII coding of characters. We also check that Space, Tab, Newline, Page, and Rubout correspond to characters with respective char-codes 32, 9, 10, 12, and 127.

    Code-char has an inverse, char-code. Thus, when char-code is applied to an ACL2 character, c, it returns a number n between 0 and 255 inclusive such that (code-char n) = c.

    The preceding paragraph implies that there is only one ACL2 character with a given character code. CLTL allows for ``attributes'' for characters, which could allow distinct characters with the same code, but ACL2 does not allow this.

    The Character Reader

    ACL2 supports the `#\' notation for characters provided by Common Lisp, with some restrictions. First of all, for every character c, the notation

    #\c
    
    may be used to denote the character object c. That is, the user may type in this notation and ACL2 will read it as denoting the character object c. In this case, the character immediately following c must be one of the following ``terminating characters'': a Tab, a Newline, a Page character, a space, or one of the characters:
    "  '  (  )  ;  `  ,
    
    Other than the notation above, ACL2 accepts alternate notation for five characters.
    #\Space
    #\Tab
    #\Newline
    #\Page
    #\Rubout
    

    Again, in each of these cases the next character must be from among the set of ``terminating characters'' described in the single-character case. Our implementation is consistent with IS0-8859, even though we don't provide #\ syntax for entering characters other than that described above.

    Finally, we note that it is our intention that any object printed by ACL2's top-level-loop may be read back into ACL2. Please notify the implementors if you find a counterexample to this claim.




    acl2-sources/doc/HTML/CHAR_gt_.html0000664002132200015000000000164412222333523016322 0ustar kaufmannacl2 CHAR_gt_.html -- ACL2 Version 6.3

    CHAR>

    greater-than test for characters
    Major Section:  ACL2-BUILT-INS
    

    (char> x y) is true if and only if the character code of x is greater than that of y. See char-code.

    The guard for char> specifies that its arguments are characters.

    Char> is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHAR_gt_=.html0000664002132200015000000000167612222333523016424 0ustar kaufmannacl2 CHAR_gt_=.html -- ACL2 Version 6.3

    CHAR>=

    greater-than-or-equal test for characters
    Major Section:  ACL2-BUILT-INS
    

    (char>= x y) is true if and only if the character code of x is greater than or equal to that of y. See char-code.

    The guard for char>= specifies that its arguments are characters.

    Char>= is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHAR_lt_.html0000664002132200015000000000163612222333523016330 0ustar kaufmannacl2 CHAR_lt_.html -- ACL2 Version 6.3

    CHAR<

    less-than test for characters
    Major Section:  ACL2-BUILT-INS
    

    (char< x y) is true if and only if the character code of x is less than that of y. See char-code.

    The guard for char< specifies that its arguments are characters.

    Char< is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHAR_lt_=.html0000664002132200015000000000167012222333523016423 0ustar kaufmannacl2 CHAR_lt_=.html -- ACL2 Version 6.3

    CHAR<=

    less-than-or-equal test for characters
    Major Section:  ACL2-BUILT-INS
    

    (char<= x y) is true if and only if the character code of x is less than or equal to that of y. See char-code.

    The guard for char<= specifies that its arguments are characters.

    Char<= is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CHECK-SUM.html0000664002132200015000000000452112222333523016230 0ustar kaufmannacl2 CHECK-SUM.html -- ACL2 Version 6.3

    CHECK-SUM

    assigning ``often unique'' integers to files and objects
    Major Section:  ACL2-BUILT-INS
    

    A ``check sum'' is an integer in some fixed range computed from the printed representation of an object, e.g., the sum, modulo 2**32, of the ascii codes of all the characters in the printed representation.

    Ideally, you would like the check sum of an object to be uniquely associated with that object, like a fingerprint. It could then be used as a convenient way to recognize the object in the future: you could remember the check sum (which is relatively small) and when an object is presented to you and alleged to be the special one you could compute its check sum and see if indeed it was. Alas, there are many more objects than check sums (after all, each check sum is an object, and then there's t). So you try to design a check sum algorithm that maps similar looking objects far apart, in the hopes that corruptions and counterfeits -- which appear to be similar to the object -- have different check sums. Nevertheless, the best you can do is a many-to-one map. If an object with a different check sum is presented, you can be positive it is not the special object. But if an object with the same check sum is presented, you have no grounds for positive identification.

    The basic check sum algorithm in ACL2 is called check-sum-obj, which computes the check sum of an ACL2 object. Roughly speaking, we scan the print representation of the object and, for each character encountered, we multiply the ascii code of the character times its position in the stream (modulo a certain prime) and then add (modulo a certain prime) that into the running sum. This is inaccurate in many senses (for example, we don't always use the ascii code and we see numbers as though they were printed in base 127) but indicates the basic idea.

    ACL2 uses check sums to increase security in the books mechanism; see certificate.




    acl2-sources/doc/HTML/CHECKPOINT-FORCED-GOALS.html0000664002132200015000000000210012222333526020175 0ustar kaufmannacl2 CHECKPOINT-FORCED-GOALS.html -- ACL2 Version 6.3

    CHECKPOINT-FORCED-GOALS

    Cause forcing goals to be checkpointed in proof trees
    Major Section:  PROOF-TREE
    

    Example forms:
    (checkpoint-forced-goals t)
    (checkpoint-forced-goals nil)
    
    Also see proof-tree.

    By default, goals are not marked as checkpoints by a proof tree display (as described elsewhere; see proof-tree) merely because they force some hypotheses, thus possibly contributing to a forcing round. However, some users may want such behavior, which will occur once the command (checkpoint-forced-goals t) has been executed. To return to the default behavior, use the command (checkpoint-forced-goals nil).




    acl2-sources/doc/HTML/CLAUSE-IDENTIFIER.html0000664002132200015000000000454412222333520017347 0ustar kaufmannacl2 CLAUSE-IDENTIFIER.html -- ACL2 Version 6.3

    CLAUSE-IDENTIFIER

    the internal form of a goal-spec
    Major Section:  MISCELLANEOUS
    

    To each goal-spec, str, there corresponds a clause-identifier produced by (parse-clause-id str). For example,

    (parse-clause-id "[2]Subgoal *4.5.6/7.8.9'''")
    
    returns ((2 4 5 6) (7 8 9) . 3).

    The function string-for-tilde-@-clause-id-phrase inverts parse-clause-id in the sense that given a clause identifier it returns the corresponding goal-spec.

    As noted in the documentation for goal-spec, each clause printed in the theorem prover's proof attempt is identified by a name. When these names are represented as strings they are called ``goal specs.'' Such strings are used to specify where in the proof attempt a given hint is to be applied. The function parse-clause-id converts goal-specs into clause identifiers, which are cons-trees containing natural numbers.

    Examples of goal-specs and their corresponding clause identifiers are shown below.

                 parse-clause-id
                       -->
    
    "Goal"                       ((0) NIL . 0)
    "Subgoal 3.2.1'"             ((0) (3 2 1) . 1)
    "[2]Subgoal *4.5.6/7.8.9'''" ((2 4 5 6) (7 8 9) . 3)
    
                       <--
          string-for-tilde-@-clause-id-phrase
    

    The caar of a clause id specifies the forcing round, the cdar specifies the goal being proved by induction, the cadr specifies the particular subgoal, and the cddr is the number of primes in that subgoal.

    Internally, the system maintains clause ids, not goal-specs. The system prints clause ids in the form shown by goal-specs. When a goal-spec is used in a hint, it is parsed (before the proof attempt begins) into a clause id. During the proof attempt, the system watches for the clause id and uses the corresponding hint when the id arises. (Because of the expense of creating and garbage collecting a lot of strings, this design is more efficient than the alternative.)




    acl2-sources/doc/HTML/CLAUSE-PROCESSOR.html0000664002132200015000000003670312222333530017307 0ustar kaufmannacl2 CLAUSE-PROCESSOR.html -- ACL2 Version 6.3

    CLAUSE-PROCESSOR

    make or apply a :clause-processor rule (goal-level simplifier)
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example (which we'll return to, below):
    (defthm correctness-of-note-fact-clause-processor
      (implies (and (pseudo-term-listp cl)
                    (alistp a)
                    (evl0 (conjoin-clauses
                           (note-fact-clause-processor cl term))
                          a))
               (evl0 (disjoin cl) a))
      :rule-classes :clause-processor)
    

    Also see define-trusted-clause-processor for documentation of an analogous utility that does not require the clause-processor to be proved correct. But please read the present documentation before reading about that utility. Both utilities designate functions ``clause-processors''. Such functions must be executable -- hence not constrained by virtue of being introduced in the signature of an encapsulate -- and must respect stobj and output arity restrictions. For example, something like (car (mv ...)) is illegal; also see signature.

    We begin this documentation with an introduction, focusing on an example, and then conclude with details. You might find it most useful simply to look at the examples in community books directory books/clause-processors/; see file Readme.lsp in that directory.

    A :clause-processor rule installs a simplifier at the level of goals, where a goal is represented as a clause: a list of terms that is implicitly viewed as a disjunction (the application of OR). For example, if ACL2 prints a goal in the form (implies (and p q) r), then the clause might be the one-element list containing the internal representation of this term -- (implies (if p q 'nil) r) -- but more likely, the corresponding clause is ((not p) (not q) r). Note that the members of a clause are translated terms; see term. For example, they do not contains calls of the macro AND, and constants are quoted.

    Note that clause-processor simplifiers are similar to metafunctions, and similar efficiency considerations apply. See meta, in particular the discussion on how to ``make a metafunction maximally efficient.''

    Unlike rules of class :meta, rules of class :clause-processor must be applied by explicit :clause-processor hints; they are not applied automatically (unless by way of computed hints; see computed-hints). But :clause-processor rules can be useful in situations for which it is more convenient to code a simplifier that manipulates the entire goal clause rather than individual subterms of terms in the clause.

    We begin with a simple illustrative example: a clause-processor that assumes an alleged fact (named term in the example) and creates a separate goal to prove that fact. We can extend the hypotheses of the current goal (named cl in the example) with a term by adding the negation of that term to the clause (disjunctive) representation of that goal. So the following returns a list of two clauses: the result of adding term as a hypothesis to the input clause, as just described, and a second clause consisting only of that term. This list of two clauses can be viewed as the conjunction of the first clause and the second clause (where again, each clause is viewed as a disjunction).

    
    (defun note-fact-clause-processor (cl term)
      (declare (xargs :guard t)) ; optional, for better efficiency
      (list (cons (list 'not term)
                  cl)
            (list term)))
    
    
    As with :meta rules, we need to introduce a suitable evaluator; see defevaluator if you want details. Since we expect to reason about the function NOT, because of its role in note-fact-clause-processor as defined above, we include NOT in the set of functions known to this evaluator. We also include IF, as is often a good idea.
    
    (defevaluator evl0 evl0-list
      ((not x) (if x y z)))
    
    
    ACL2 can now prove the following theorem automatically. (This is the example displayed at the outset of this documentation topic.) Of course, :clause-processor rules about clause-processor functions less trivial than note-fact-clause-processor may require lemmas to be proved first! The function disjoin takes a clause and returns its disjunction (the result of applying OR to its members), and conjoin-clauses applies disjoin to every element of a given list of clauses and then conjoins (applies AND) to the corresponding list of resulting terms.
    
    (defthm correctness-of-note-fact-clause-processor
      (implies (and (pseudo-term-listp cl)
                    (alistp a)
                    (evl0 (conjoin-clauses
                           (note-fact-clause-processor cl term))
                          a))
               (evl0 (disjoin cl) a))
      :rule-classes :clause-processor)
    
    
    Now let us submit a silly but illustrative example theorem to ACL2, to show how a corresponding :clause-processor hint is applied. The hint says to apply the clause-processor function, note-fact-clause-processor, to the current goal clause and a ``user hint'' as the second argument of that function, in this case (equal a a). Thus, a specific variable, clause, is always bound to the current goal clause for the evaluation of the :clause-processor hint, to produce a list of clauses. Since two subgoals are created below, we know that this list contained two clauses. Indeed, these are the clauses returned when note-fact-clause-processor is applied to two arguments: the current clause, which is the one-element list ((equal (car (cons x y)) x)), and the user hint, (equal a a).
    
    ACL2 !>(thm (equal (car (cons x y))
                       x)
                :hints
                (("Goal"
                  :clause-processor
                  (note-fact-clause-processor clause '(equal a a)))))
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    We now apply the verified :CLAUSE-PROCESSOR function NOTE-FACT-CLAUSE-
    PROCESSOR to produce two new subgoals.
    
    Subgoal 2
    (IMPLIES (EQUAL A A)
             (EQUAL (CAR (CONS X Y)) X)).
    
    But we reduce the conjecture to T, by the :executable-counterpart of
    IF and the simple :rewrite rule CAR-CONS.
    
    Subgoal 1
    (EQUAL A A).
    
    But we reduce the conjecture to T, by primitive type reasoning.
    
    Q.E.D.
    
    Summary
    Form:  ( THM ...)
    Rules: ((:EXECUTABLE-COUNTERPART IF)
            (:EXECUTABLE-COUNTERPART NOT)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:REWRITE CAR-CONS))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Proof succeeded.
    ACL2 !>
    

    That concludes our introduction to clause-processor rules and hints. We turn now to detailed documentation.

    The signature of a clause-processor function, CL-PROC, must have one of the following forms. Here, each st_i is a stobj (possibly state) while the other parameters and results are not stobjs (see stobj). Note that there need not be input stobjs in [3] -- i.e., k can be 0 -- and even if there are, there need not be output stobjs.

    [1]  ((CL-PROC cl) => cl-list)
    
    [2]  ((CL-PROC cl hint) => cl-list)
    
    [3]  ((CL-PROC cl hint st_1 ... st_k) => (mv erp cl-list st_i1 ... st_in))
    
    In [3], we think of the first component of the result as an error flag. Indeed, a proof will instantly abort if that error flag is not nil.

    We next discuss the legal forms of :clause-processor rules, followed below by a discussion of :clause-processor hints. In the discussion below, we use lower-case names to represent specific symbols, for example implies, and we use upper-case names to represent more arbitrary pieces of syntax (which we will describe), for example, CL.

    If a :rule-classes specification includes :clause-processor, then the corresponding term must have the following form. (Additional ``meta-extract'' hypotheses, not shown or discussed below, may be included as desired in order to use facts from the logical world to help prove the rule; see meta-extract for explanation of this advanced feature.)

    (implies (and (pseudo-term-listp CL)
                  (alistp A)
                  (EVL (conjoin-clauses <CL-LIST>)
                        B))
             (EVL (disjoin CL) A))
    
    Here EVL is a known evaluator; CL and A are distinct non-stobj variables; and <CL-LIST> is an expression representing the clauses returned by the clause-processor function CL-PROC, whose form depends on the signature of that function, as follows. Typically B is A, but it can be any term (useful when generalization is occurring; see the example ``Test generalizing alist'' in community book books/clause-processors/basic-examples.lisp). For cases [1] and [2] above, <CL-LIST> is of the form (CL-PROC CL) or (CL-PROC CL HINT), respectively, where in the latter case HINT is a non-stobj variable distinct from the variables CL and A. For case [3], <CL-LIST> is of the form
    (clauses-result (CL-PROC CL HINT st_1 ... st_k))
    
    where the st_i are the specific stobj names mentioned in [3]. Logically, clauses-result returns the cadr if the car is NIL, and otherwise (for the error case) returns a list containing the empty (false) clause. So in the non-error case, clauses-result picks out the second result, denoted cl-list in [3] above, and in the error case the implication above trivially holds.

    In the above theorem, we are asked to prove (EVL (disjoin CL) A) assuming that the conjunction of all clauses produced by the clause processor evaluates to a non-nil value under some alist B. In fact, we can choose B so as to allow us to assume evaluations of the generated clauses over many different alists. This technique is discussed in the community book books/clause-processors/multi-env-trick.lisp, which introduces some macros that may be helpful in accomplishing proofs of this type.

    The clause-processor function, CL, must have a guard that ACL2 can trivially prove from the hypotheses that the first argument of CL is known to be a pseudo-term-listp and any stobj arguments are assumed to satisfy their stobj predicates.

    Next we specify the legal forms for :clause-processor hints. These depend on the signature as described in [1] through [3] above. Below, as above, CL-PROC is the clause-processor function, and references to ``clause'' refer to that exact variable (not, for example, to cl). In each of the three cases, the forms shown for that case are equivalent; in particular, the :function syntax is simply a convenience for the final form in each case.

    Signature [1], ((cl-proc cl) => cl-list):

    :clause-processor CL-PROC
    :clause-processor (:function CL-PROC)
    :clause-processor (CL-PROC clause)
    
    or any term macroexpanding to (CL-PROC clause).

    Signature [2], ((cl-proc cl hint) => cl-list):

    :clause-processor (:function CL-PROC :hint HINT)
    :clause-processor (CL-PROC clause HINT)
    
    or any term macroexpanding to (CL-PROC clause HINT), where HINT is any term with at most CLAUSE free.

    Signature [3], ((CL-PROC cl hint ...) => (mv erp cl-list ...))

    :clause-processor (:function CL-PROC :hint HINT)
    :clause-processor (CL-PROC clause HINT st_1 ... st_k)
    
    or any term macroexpanding to (CL-PROC clause HINT st_1 ... st_k), where HINT is any term with at most CLAUSE free.

    A :clause-processor hint causes the proof to abort if the result returned by evaluating the suitable CL-PROC call, as above, is not a list of clauses, i.e., a list of (translated) term lists. The proof also aborts if in case [3] the first (erp) value returned is not nil, in which case erp is used for printing an error message as follows: if it is a string, then that string is printed; but if it is a non-empty true list whose first element is a string, then it is printed as though by (fmt ~@0 (list (cons #\0 erp)) ...) (see fmt). Otherwise, a non-nil erp value causes a generic error message to be printed.

    If there is no error as above, but the CL-PROC call returns clause list whose single element is equal to the input clause, then the hint is ignored since we are left with the goal with which we started. In that case, the other prover processes are then applied as usual.

    You can see all current :clause-processor rules by issuing the following command: (print-clause-processor-rules).

    The following paper discusses ACL2 clause-processors at a high level suitable for a non-ACL2 audience:

    M. Kaufmann, J S. Moore, S. Ray, and E. Reeber, ``Integrating External Deduction Tools with ACL2.'' Journal of Applied Logic (Special Issue: Empirically Successful Computerized Reasoning), Volume 7, Issue 1, March 2009, pp. 3--25. Also published online (DOI 10.1016/j.jal.2007.07.002). Preliminary version in: Proceedings of the 6th International Workshop on the Implementation of Logics (IWIL 2006) (C. Benzmueller, B. Fischer, and G. Sutcliffe, editors), CEUR Workshop Proceedings Vol. 212, Phnom Penh, Cambodia, pp. 7-26, November 2006, http://ceur-ws.org/Vol-212/.




    acl2-sources/doc/HTML/CLEAR-HASH-TABLES.html0000664002132200015000000000161712222333517017276 0ustar kaufmannacl2 CLEAR-HASH-TABLES.html -- ACL2 Version 6.3

    CLEAR-HASH-TABLES

    deprecated feature
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Deprecated. Calls clear-memoize-tables and then hons-clear or hons-wash, whichever makes sense for the underlying Common Lisp.




    acl2-sources/doc/HTML/CLEAR-MEMOIZE-STATISTICS.html0000664002132200015000000000170212222333517020373 0ustar kaufmannacl2 CLEAR-MEMOIZE-STATISTICS.html -- ACL2 Version 6.3

    CLEAR-MEMOIZE-STATISTICS

    clears all profiling info displayed by (memoize-summary)
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, this function just returns nil. It clears all profiling info displayed by (memoize-summary)




    acl2-sources/doc/HTML/CLEAR-MEMOIZE-TABLE.html0000664002132200015000000000143612222333517017534 0ustar kaufmannacl2 CLEAR-MEMOIZE-TABLE.html -- ACL2 Version 6.3

    CLEAR-MEMOIZE-TABLE

    forget values remembered for the given function
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    This function returns its argument, fn, unchanged. The values memoized for fn are forgotten.




    acl2-sources/doc/HTML/CLEAR-MEMOIZE-TABLES.html0000664002132200015000000000162512222333517017657 0ustar kaufmannacl2 CLEAR-MEMOIZE-TABLES.html -- ACL2 Version 6.3

    CLEAR-MEMOIZE-TABLES

    forget values remembered for all the memoized functions
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Clear-memoize-tables is a logical no-op. All memoized values are forgotten. It returns nil, invoking clear-memoize-table for each memoized function.




    acl2-sources/doc/HTML/CLOSE-INPUT-CHANNEL.html0000664002132200015000000000064312222333523017562 0ustar kaufmannacl2 CLOSE-INPUT-CHANNEL.html -- ACL2 Version 6.3

    CLOSE-INPUT-CHANNEL

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/CLOSE-OUTPUT-CHANNEL.html0000664002132200015000000000064512222333523017725 0ustar kaufmannacl2 CLOSE-OUTPUT-CHANNEL.html -- ACL2 Version 6.3

    CLOSE-OUTPUT-CHANNEL

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/CLOSE-TRACE-FILE.html0000664002132200015000000000155012222333531017165 0ustar kaufmannacl2 CLOSE-TRACE-FILE.html -- ACL2 Version 6.3

    CLOSE-TRACE-FILE

    stop redirecting trace output to a file
    Major Section:  TRACE
    

    General Form:
    (close-trace-file) ; trace output is no longer redirected to a file
    
    

    Output from trace$ normally goes to the screen, or more precisely, standard-co. It can be redirected to a file; see open-trace-file. Use close-trace-file to redirect trace output to standard-co.




    acl2-sources/doc/HTML/CODE-CHAR.html0000664002132200015000000000164412222333523016201 0ustar kaufmannacl2 CODE-CHAR.html -- ACL2 Version 6.3

    CODE-CHAR

    the character corresponding to a given numeric code
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-code-char):

    (equal (code-char x)
           (if (and (integerp x)
                    (>= x 0)
                    (< x 256))
               (code-char x)
             (code-char 0)))
    

    Guard for (code-char x):

    (and (integerp x)
         (>= x 0)
         (< x 256))
    
    ACL2 supports 8-bit characters. Inputs not between 0 and 255 are treated as 0.




    acl2-sources/doc/HTML/COERCE.html0000664002132200015000000000220512222333523015706 0ustar kaufmannacl2 COERCE.html -- ACL2 Version 6.3

    COERCE

    coerce a character list to a string and a string to a list
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-coerce):

    (equal (coerce x y)
           (cond
            ((equal y 'list)
             (if (stringp x)
                 (coerce x 'list)
               nil))
            (t
             (coerce (make-character-list x) 'string))))
    

    Guard for (coerce x y):

    (if (equal y 'list)
        (stringp x)
      (if (equal y 'string)
          (character-listp x)
        nil))
    

    Also see community book books/misc/fast-coerce.lisp, contributed by Jared Davis, for a version of coerce that may be faster for Common Lisp implementations other than CCL 1.3 or later, if the second argument is 'list (for coercing a string to a list).




    acl2-sources/doc/HTML/COMMAND-DESCRIPTOR.html0000664002132200015000000001216212222333520017500 0ustar kaufmannacl2 COMMAND-DESCRIPTOR.html -- ACL2 Version 6.3

    COMMAND-DESCRIPTOR

    an object describing a particular command typed by the user
    Major Section:  MISCELLANEOUS
    

    Examples:
    
    :max      ; the command most recently typed by the user
    :x        ; synonymous with :max
    (:x -1)   ; the command before the most recent one
    (:x -2)   ; the command before that
    :x-2      ; synonymous with (:x -2)
    5         ; the fifth command typed by the user
    1         ; the first command typed by the user
    0         ; the last command of the system initialization
    -1        ; the next-to-last initialization command
    :min      ; the first command of the initialization
    :start    ; the last command of the initial ACL2 logical world
    fn        ; the command that introduced the logical name fn
    (:search (defmacro foo-bar))
              ; the first command encountered in a search from :max to
              ; 0 that either contains defmacro and foo-bar in the
              ; command form or contains defmacro and foo-bar in some
              ; event within its block.
    

    The recorded history of your interactions with the top-level ACL2 command loop is marked by the commands you typed that changed the logical world. Each such command generated one or more events, since the only way for you to change the logical world is to execute an event function. See command and see events. We divide history into ``command blocks,'' grouping together each world changing command and its events. A ``command descriptor'' is an object that can be used to describe a particular command in the history of the ongoing session.

    Each command is assigned a unique integer called its ``command number'' which indicates the command's position in the chronological ordering of all of the commands ever executed in this session (including those executed to initialize the system). We assign the number 1 to the first command you type to ACL2. We assign 2 to the second and so on. The non-positive integers are assigned to ``prehistoric'' commands, i.e., the commands used to initialize the ACL2 system: 0 is the last command of the initialization, -1 is the one before that, etc.

    The legal command descriptors are described below. We use n to denote any integer, sym to denote any logical name (see logical-name), and cd to denote, recursively, any command descriptor.

     command                   command
    descriptor                described
    
    :max   -- the most recently executed command (i.e., the one with
              the largest command number)
    :x     -- synonymous with :max
    :x-k   -- synonymous with (:x -k), if k is an integer and k>0
    :min   -- the earliest command (i.e., the one with the smallest
              command number and hence the first command of the system
              initialization)
    :start -- the last command when ACL2 starts up
    n      -- command number n  (If n is not in the
              range :min<=n<=:max, n is replaced by the nearest of :min
              and :max.)
    sym    -- the command that introduced the logical name sym
    (cd n) -- the command whose number is n plus the command number of
              the command described by cd
    (:search pat cd1 cd2)
              In this command descriptor, pat must be either an atom or
              a true list of atoms and cd1 and cd2 must be command
              descriptors.  We search the interval from cd1 through cd2
              for the first command that matches pat.  Note that if cd1
              occurs chronologically after cd2, the search is
              ``backwards'' through history while if cd1 occurs
              chronologically before cd2, the search is ``forwards''.  A
              backwards search will find the most recent match; a
              forward search will find the chronologically earliest
              match.  A command matches pat if either the command form
              itself or one of the events in the block contains pat (or
              all of the atoms in pat if pat is a list).
    (:search pat)
              the command found by (:search pat :max 0), i.e., the most
              recent command matching pat that was part of the user's
              session, not part of the system initialization.
    




    acl2-sources/doc/HTML/COMMAND-LINE.html0000664002132200015000000000153312222333522016553 0ustar kaufmannacl2 COMMAND-LINE.html -- ACL2 Version 6.3

    COMMAND-LINE

    handling of command-line arguments when ACL2 is invoked
    Major Section:  OTHER
    

    You may provide command-line arguments when invoking ACL2, which are passed to the host Lisp. For more information on this topic, along with a discussion of how to save an ACL2 executable that avoids passing command-line arguments to the host Lisp, see save-exec.

    Some Related Topics

    • SAVE-EXEC -- save an executable image and a wrapper script




    acl2-sources/doc/HTML/COMMAND.html0000664002132200015000000000303712222333520016025 0ustar kaufmannacl2 COMMAND.html -- ACL2 Version 6.3

    COMMAND

    forms you type at the top-level, but...
    Major Section:  MISCELLANEOUS
    

    ...the word ``command'' usually refers to a top-level form whose evaluation produces a new logical world.

    Typical commands are:
    (defun foo (x) (cons x x))
    (defthm consp-foo (consp (foo x)))
    (defrec pair (hd . tl) nil)
    
    The first two forms are examples of commands that are in fact primitive events. See events. Defrec, on the other hand, is a macro that expands into a progn of several primitive events. In general, a world extending command generates one or more events.

    Both events and commands leave landmarks on the world that enable us to determine how the given world was created from the previous one. Most of your interactions will occur at the command level, i.e., you type commands, you print previous commands, and you undo back through commands. Commands are denoted by command descriptors. See command-descriptor.




    acl2-sources/doc/HTML/COMMON-LISP.html0000664002132200015000000000573412222333520016512 0ustar kaufmannacl2 COMMON-LISP.html -- ACL2 Version 6.3

    COMMON-LISP

    relation to Common Lisp, including deviations from the spec
    Major Section:  MISCELLANEOUS
    

    ACL2 is a logic, a theorem prover, and a programming language based on Common Lisp. A connection with Common Lisp is established with guards (see guard).

    However, here we document potential deviations from Common Lisp semantics even in the presence of verified guards. Our view is that these deviations are extremely unlikely to manifest; indeed, as of this writing we are unaware of any cases in which these issues arise in practice. However, we feel obligated to acknowledge their possibility, which could result in surprises during evaluation or even proof.

    Some Related Topics

    The Common Lisp spec allows certain predicates to return what it calls ``generalized Booleans,'' which are really arbitrary values that are to be viewed as either nil or non-nil. However, in ACL2 these functions are assumed to return nil or t. For details,see generalized-booleans.

    The execution of forms with :program mode functions can result in calls of functions on arguments that do not satisfy their guards. In practice, this simply causes hard Lisp errors. But in principle one could imagine a damaged Lisp image that operates incorrectly. See defun-mode-caveat.

    The Common Lisp spec, specifically Section 3.2.2.3 of the Common Lisp HyperSpec, allows for undefined results when a function is ``multiply defined'' in a compiled file. ACL2 allows redundant defuns in a book, and in general books are compiled by certify-book (but see certify-book and see compilation for how to control such compilation). Moreover, ACL2 provides a redefinition capability (see ld-redefinition-action and see redef), and the above section also allows for undefined results when a function is defined in a compiled file and then redefined, presumably (for example) because of inlining.




    acl2-sources/doc/HTML/COMMUNITY-BOOKS.html0000664002132200015000000000214312222333516017210 0ustar kaufmannacl2 COMMUNITY-BOOKS.html -- ACL2 Version 6.3

    COMMUNITY-BOOKS

    books contributed by the ACL2 community
    Major Section:  BOOKS
    

    For background on ACL2 books, which can contain useful definitions and theorems, see books.

    The ACL2 ``community books'' is a collection of books developed since the early 1990s by members of the ACL2 community. The installation instructions suggest installing these books in the books/ subdirectory of your local ACL2 installation. You can contribute books to the ACL2 community and obtain updates inbetween ACL2 releases by visiting the acl2-books project web page, http://acl2-books.googlecode.com/.

    To certify the community books, see regression.




    acl2-sources/doc/HTML/COMP-GCL.html0000664002132200015000000000204012222333516016106 0ustar kaufmannacl2 COMP-GCL.html -- ACL2 Version 6.3

    COMP-GCL

    compile some ACL2 functions leaving .c and .h files
    Major Section:  COMP
    

    Comp-gcl is for use by experts who want to examine the results of GCL compilation, and it may only be used with ACL2 implementations built on top of GCL. It takes exactly the same arguments as comp, and has the same basic functionality (see comp), but has two additional effects. First, files "TMP.lisp" and "TMP1.lisp" are always created, even when a single function is specified. Second, comp-gcl always leaves files "TMP.c", "TMP.h", "TMP1.c", and "TMP1.h" when compilation is complete.




    acl2-sources/doc/HTML/COMP.html0000664002132200015000000001466412222333516015522 0ustar kaufmannacl2 COMP.html -- ACL2 Version 6.3

    COMP

    compile some ACL2 functions
    Major Section:  EVENTS
    

    NOTE: Comp is a no-op if explicit compilation is suppressed; see compilation. The documentation here assumes that this is not the case.

    Examples:
    :comp t          ; compile all uncompiled ACL2 functions
    (comp t)         ; same as above, but can be put into a book
    (comp :exec)     ; compile all uncompiled logic (``*1*'') definitions
    :comp foo        ; compile the defined function foo
    :comp (:raw foo) ; compile the raw Lisp version of the defined function foo
                       but not the corresponding logic definition
    :comp (foo bar)  ; compile the defined functions foo and bar
    :comp (foo (:raw bar))  ; compile the defined functions foo and bar, but for
                            ; bar do not compile the corresponding logic definition
    
    General Form:
    :comp specifier
    where specifier is one of the following:
    
      t                     compile all user-defined ACL2 functions that are
                              currently uncompiled (redefined built-in functions
                              are not recompiled)
      :exec                 same as t, except that only logic versions are
                              compiled (see below), not raw Lisp definitions
      :raw                  same as t, except that only raw Lisp definitions are
                              compiled, not logic version (see below)
      (name-1 ... name-k)   a non-empty list of names of functions defined by
                              DEFUN in ACL2, except that each name-i can be of
                              the form (:raw sym) or (:exec sym), where sym is
                            the name of such a function
      name                  same as (name)
    

    When you define a function in ACL2, you are really causing two definitions to be made ``under the hood'' in Common Lisp: the definition is submitted explicitly to raw Lisp, but so is a corresponding ``logic definition''. If guards have not been verified, then only the logic definition will be evaluated; see guards-and-evaluation, in particular the section titled ``Guards and evaluation V: efficiency issues''.

    Thus, if you are not verifying guards and you want the benefit of Lisp compilation for speed and space efficiency, then you may want to place the form (comp :exec) in your books.

    Generally it is not necessary to place the form (comp t), or the form (comp :raw), in a book, because certify-book compiles the raw Lisp definitions anyhow, by default. But you may wish to put (comp t) or (comp fn1 fn2 ... fnk) in a book when such a form precedes expensive calls of functions, for example for proofs involving calls of functions on large constants, or to support computationally expensive macroexpansion.

    As suggested by the examples above, if a function specifier is of the form (:raw fn), then fn will be compiled in raw Common Lisp but its corresponding logic definition will not be compiled; and for (:exec fn), it's the other way around.

    The use of :comp may create various files whose names start with ``TMP*'', but it then deletes them. If you want to save these files, evaluate (assign keep-tmp-files t).

    Some Related Topics

    • COMP-GCL -- compile some ACL2 functions leaving .c and .h files

    Also see set-compile-fns for a way to compile each function as it is defined. But note that set-compile-fns is ignored during include-book.

    Note that if functions are traced (see trace$), then comp will first untrace the functions that are to be compiled, then will do the compile(s), and finally will re-trace the functions that it untraced (using their original trace specs). In particular, if you have traced a function and then you compile it using :comp, the resulting traced function will be compiled as well unless you specified :compile nil in your trace spec; and after you untrace the function it will definitely run compiled.

    We conclude with a technical remark only for those who use trust tags to write raw Lisp code. :Comp generally creates files to compile unless it is given a single function to compile. Those files contain the ACL2 definitions of all functions to compile, omitting those in the lists obtained by evaluating the forms (@ logic-fns-with-raw-code) and (@ program-fns-with-raw-code). :Comp skips compilation for functions that are already compiled, as is typically the case when you redefine functions in raw Lisp using the utility include-raw defined in community book books/tools/include-raw.lisp. But if you define interpreted (as opposed to compiled) functions with raw Lisp code, say by using trust tags (see defttag) and progn!, then you are advised to add all such symbols to one of the lists stored in the two state globals above: to logic-fns-with-raw-code if the function symbol is in :logic mode, else to program-fns-with-raw-code. Then, instead of the corresponding ACL2 definition (without raw Lisp code) being written to a file, the function symbol will be passed directly to the Lisp compile function. Note that the above two state globals are both untouchable, so you may need to deal with that before modifying them, for example as follows (also see remove-untouchable).

    (defttag t)
    (state-global-let*
     ((temp-touchable-vars t set-temp-touchable-vars))
     (progn! (f-put-global 'logic-fns-with-raw-code
                           (cons 'my-fn (@ logic-fns-with-raw-code))
                           state)))
    




    acl2-sources/doc/HTML/COMPILATION.html0000664002132200015000000000606712222333525016540 0ustar kaufmannacl2 COMPILATION.html -- ACL2 Version 6.3

    COMPILATION

    compiling ACL2 functions
    Major Section:  PROGRAMMING
    

    ACL2 has several mechanisms to speed up the evaluation of function calls by compiling functions: see comp, see set-compile-fns, and see certify-book. The intention is that compilation never changes the value returned by a function call, though it could cause the call to succeed rather than fail, for example by avoiding a stack overflow.

    The state global variable 'compiler-enabled is set automatically when the system is built, and may depend on the underlying Lisp implementation. (In order to disable the compiler at build time, which will defeat the speed-up but usually be pretty harmless when the host Lisp is CCL or SBCL, see the discussion of ACL2_COMPILER_DISABLED in distributed file GNUmakefile.) The value of 'compiler-enabled, as returned by (@ compiler-enabled), can be t, :books, or nil. If the value is nil, then include-book and certify-book coerce their arguments :load-compile-file and compile-flg arguments (respectively) to nil. Otherwise, the value is :books or t and there is no such coercion; but if the value is not t, then comp and set-compile-fns are no-ops, which is probably desirable for Lisps such as CCL and SBCL that compile on-the-fly even when the compiler is not explicitly invoked.

    However, you may have reason to want to change the above (default) behavior. To enable compilation by default for certify-book and include-book but not for comp or set-compile-fns:

    (set-compiler-enabled :books state)
    
    To enable compilation not only as above but also for comp and set-compile-fns:
    (set-compiler-enabled t state)
    
    To suppress compilation and loading of compiled files by include-book (for example, if you get a raw Lisp error such as ``Wrong FASL version''):
    (set-compiler-enabled nil state)
    

    See book-compiled-file for more discussion about compilation and books.




    acl2-sources/doc/HTML/COMPILING-ACL2P.html0000664002132200015000000000313312222333522017066 0ustar kaufmannacl2 COMPILING-ACL2P.html -- ACL2 Version 6.3

    COMPILING-ACL2P

    compiling ACL2(p)
    Major Section:  PARALLELISM
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism. See parallelism-tutorial for an introduction to parallel programming in ACL2.

    You can build an experimental version of ACL2 that supports parallel execution in the following host Common Lisp implementations:

    * CCL (OpenMCL)

    * Lispworks 6.0

    * SBCL with threads (feature :sb-thread)

    The command below will compile ACL2 to support parallel execution, including parallel execution during proofs. Any non-empty string may be used in place of t, and the value of LISP (shown here as ccl) is any Lisp executable on which one can build ACL2(p) (see parallelism).

    make ACL2_PAR=t LISP=ccl
    

    So for example, to make an executable image and also documentation (which will appear in subdirectories doc/EMACS and doc/HTML), using the Lisp executable ccl:

    make large DOC ACL2_PAR=t LISP=ccl
    




    acl2-sources/doc/HTML/COMPLEX-RATIONALP.html0000664002132200015000000000140512222333523017405 0ustar kaufmannacl2 COMPLEX-RATIONALP.html -- ACL2 Version 6.3

    COMPLEX-RATIONALP

    recognizes complex rational numbers
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (complex-rationalp 3)       ; nil, as 3 is rational, not complex rational
    (complex-rationalp #c(3 0)) ; nil, since #c(3 0) is the same as 3
    (complex-rationalp t)       ; nil
    (complex-rationalp #c(3 1)) ; t, as #c(3 1) is the complex number 3 + i
    

    See complex for more about complex rationals in ACL2.




    acl2-sources/doc/HTML/COMPLEX.html0000664002132200015000000000444012222333523016060 0ustar kaufmannacl2 COMPLEX.html -- ACL2 Version 6.3

    COMPLEX

    create an ACL2 number
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (complex x 3) ; x + 3i, where i is the principal square root of -1
    (complex x y) ; x + yi
    (complex x 0) ; same as x, for rational numbers x
    
    
    The function complex takes two rational number arguments and returns an ACL2 number. This number will be of type (complex rational) [as defined in the Common Lisp language], except that if the second argument is zero, then complex returns its first argument. The function complex-rationalp is a recognizer for complex rational numbers, i.e. for ACL2 numbers that are not rational numbers.

    The reader macro #C (which is the same as #c) provides a convenient way for typing in complex numbers. For explicit rational numbers x and y, #C(x y) is read to the same value as (complex x y).

    The functions realpart and imagpart return the real and imaginary parts (respectively) of a complex (possibly rational) number. So for example, (realpart #C(3 4)) = 3, (imagpart #C(3 4)) = 4, (realpart 3/4) = 3/4, and (imagpart 3/4) = 0.

    The following built-in axiom may be useful for reasoning about complex numbers.

    (defaxiom complex-definition
      (implies (and (real/rationalp x)
                    (real/rationalp y))
               (equal (complex x y)
                      (+ x (* #c(0 1) y))))
      :rule-classes nil)
    

    A completion axiom that shows what complex returns on arguments violating its guard (which says that both arguments are rational numbers) is the following, named completion-of-complex.

    (equal (complex x y)
           (complex (if (rationalp x) x 0)
                    (if (rationalp y) y 0)))
    




    acl2-sources/doc/HTML/COMPLEX_slash_COMPLEX-RATIONALP.html0000664002132200015000000000203412222333523021765 0ustar kaufmannacl2 COMPLEX_slash_COMPLEX-RATIONALP.html -- ACL2 Version 6.3

    COMPLEX/COMPLEX-RATIONALP

    recognizer for complex numbers
    Major Section:  ACL2-BUILT-INS
    

    For most ACL2 users, this is a macro abbreviating complex-rationalp; see complex-rationalp. In ACL2(r) (see real), a complex number x may have irrational real and imaginary parts. This macro abbreviates the predicate complexp in ACL2(r), which holds for such x. Most ACL2 users can ignore this macro and use complex-rationalp instead. Some community books use complex/complex-rationalp so that they are suitable for ACL2(r) as well.




    acl2-sources/doc/HTML/COMPOUND-RECOGNIZER.html0000664002132200015000000002570412222333530017646 0ustar kaufmannacl2 COMPOUND-RECOGNIZER.html -- ACL2 Version 6.3

    COMPOUND-RECOGNIZER

    make a rule used by the typing mechanism
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Examples:
    (defthm alistp-implies-true-listp-compound-recognizer
      (implies (alistp x)                 ; When (alistp x) is assumed true, add
               (true-listp x))            ; the additional hypothesis that x is
      :rule-classes :compound-recognizer) ; of primitive type true-listp.
    
    (defthm natp-compound-recognizer      ; See discussion below.
      (equal (natp x)
             (and (integerp x)
                  (<= 0 x)))
      :rule-classes :compound-recognizer)
    

    Before presenting the General Forms, we start with a motivating example: the second defthm form above, which provides a nice example of a :compound-recognizer rule that is built into ACL2. To see how this rule might be useful, consider the following (admittedly very simple) events.

    (defun triple (x)
      (* 3 x))
    
    (defthm triple-preserves-integerp
      (implies (integerp x)
               (integerp (triple x))))
    
    (in-theory (disable triple natp))
    
    If the above :compound-recognizer rule is disabled, then the following trivial theorem fails as shown; we explain below.
    (thm (implies (natp x)
                  (integerp (triple x)))
      :hints (("Goal" :in-theory (disable natp-compound-recognizer))))
    

    The problem is that when ACL2 tries to rewrite the term (integerp (triple x)) using the :rewrite rule triple-preserves-integerp, it needs to rewrite the hypothesis (integerp x) to t, but instead what is known is (natp x). If we remove the hint, then the proof succeeds because the above :compound-recognizer rule tells ACL2 that when assuming (natp x) to be true, it should actually assume both (integerp x) and (<= 0 x) to be true.

    General Forms:
    (implies (fn x) concl)               ; (1)
    (implies (not (fn x)) concl)         ; (2)
    (and (implies (fn x) concl1)         ; (3)
         (implies (not (fn x)) concl2))
    (if (fn x) concl1 concl2)            ; (4)
    (iff (fn x) concl)                   ; (5)
    (equal (fn x) concl)                 ; (6)
    
    where fn is a Boolean valued function of one argument, x is a variable symbol, and the system can deduce some restriction on the primitive type of x from the assumption that concl holds. The last restriction is vague but one way to understand it is to weaken it a little to ``and concl is a non-tautological conjunction or disjunction of the primitive type recognizers listed below.''

    The primitive ACL2 types and a suitable primitive recognizing expression for each are listed below.

      type                suitable primitive recognizer
    
      zero                (equal x 0)
      negative integers   (and (integerp x) (< x 0))
      positive integers   (and (integerp x) (> x 0))
      negative ratio      (and (rationalp x)
                               (not (integerp x))
                               (< x 0))
      positive ratio      (and (rationalp x)
                               (not (integerp x))
                               (> x 0))
      complex rational    (complex-rationalp x)
      nil                 (equal x nil)
      t                   (equal x t)
      other symbols       (and (symbolp x)
                               (not (equal x nil))
                               (not (equal x t)))
      proper conses       (and (consp x)
                               (true-listp x))
      improper conses     (and (consp x)
                               (not (true-listp x)))
      strings             (stringp x)
      characters          (characterp x)
    

    Thus, a suitable concl to recognize the naturals would be (or (equal x 0) (and (integerp x) (> x 0))) but it turns out that we also permit (and (integerp x) (>= x 0)). Similarly, the true-lists could be specified by

    (or (equal x nil) (and (consp x) (true-listp x)))
    
    but we in fact allow (true-listp x). When time permits we will document more fully what is allowed or implement a macro that permits direct specification of the desired type in terms of the primitives.

    There are essentially four forms of :compound-recognizer rules, as the forms labeled (3) and (4) above are equivalent, as are those labeled (5) and (6). We explain how such rules are used by considering the individual forms.

    Consider form (1), (implies (fn x) concl). The effect of such a rule is that when the rewriter assumes (fn x) true, as it would while diving through (if (fn x) xxx ...) to rewrite xxx, it restricts the type of x as specified by concl. For example, if concl is the term (integerp x), then when rewriting xxx, x will be assumed to be an integer. However, when assuming (fn x) false, as necessary in (if (fn x) ... xxx), the rule permits no additional assumptions about the type of x. For example, if fn is primep, i.e., the predicate that recognizes prime numbers, then (implies (primep x) (and (integerp x) (>= x 0))) is a compound recognizer rule of the first form. When (primep x) is assumed true, the rewriter gains the additional information that x is a natural number. When (primep x) is assumed false, no additional information is gained -- since x may be a non-prime natural or may not even be a natural.

    Form (2) is the symmetric case, when assuming (fn x) false permits type restrictions on x but assuming (fn x) true permits no such restrictions. For example, if we defined exprp to be the recognizer for well-formed expressions for some language in which all symbols, numbers, character objects and strings were well-formed -- e.g., the well-formedness rules only put restrictions on expressions represented by consps -- then the theorem (implies (not (exprp x)) (consp x)) is a rule of the second form. Assuming (exprp x) true tells us nothing about the type of x; assuming it false tells us x is a consp.

    Forms (3) and (4), which are really equivalent, address themselves to the case where one type may be deduced from (fn x) and a generally unrelated type may be deduced from its negation. If we modified the expression recognizer above so that character objects are illegal, then rules of the forms (3) and (4) are

    (and (implies (exprp x) (not (characterp x)))
         (implies (not (exprp x)) (or (consp x) (characterp x)))).
    (if (exprp x)
        (not (characterp x))
      (or (consp x) (characterp x)))
    

    Finally, rules of forms (5) and (6) address the case where fn recognizes all and only the objects whose type is described. In these cases, fn is really just a new name for some ``compound recognizers.'' The classic example is (booleanp x), which is just a handy combination of two primitive types:

    (iff (booleanp x) (or (equal x t) (equal x nil))).
    

    Often it is best to disable fn after proving that it is a compound recognizer, since otherwise the term (fn x) will be expanded and thus disappear.

    Every time you prove a new compound recognizer rule about fn it overrides all previously proved compound recognizer rules about fn. Thus, if you want to establish the type implied by (fn x) and you want to establish the type implied by (not (fn x)), you must prove a compound recognizer rule of the third, fourth, fifth, or sixth forms. Proving a rule of the first form followed by one of the second only leaves the second fact in the database.

    Compound recognizer rules can be disabled with the effect that older rules about fn, if any, are exposed.

    If you prove more than one compound recognizer rule for a function, you may see a warning message to the effect that the new rule is not as ``restrictive'' as the old. That is, the new rules do not give the rewriter strictly more type information than it already had. The new rule is stored anyway, overriding the old, if enabled. You may be playing subtle games with enabling or rewriting. But two other interpretations are more likely, we think. One is that you have forgotten about an earlier rule and should merely print it out to make sure it says what you intend, and then discard your new rule. The other is that you meant to give the system more information and the system has simply been unable to extract the intended type information from the term you placed in the conclusion of the new rule. Given our lack of specificity in saying how type information is extracted from rules, you can hardly blame yourself for this problem. Sorry. If you suspect you've been burned this way, you should rephrase the new rule in terms of the primitive recognizing expressions above and see if the warning is still given. It would also be helpful to let us see your example so we can consider it as we redesign this stuff.

    Compound recognizer rules are similar to :forward-chaining rules in that the system deduces new information from the act of assuming something true or false. If a compound recognizer rule were stored as a forward chaining rule it would have essentially the same effect as described, when it has any effect at all. The important point is that :forward-chaining rules, because of their more general and expensive form, are used ``at the top level'' of the simplification process: we forward chain from assumptions in the goal being proved. But compound recognizer rules are built in at the bottom-most level of the simplifier, where type reasoning is done.

    All that said, compound recognizer rules are a rather fancy, specialized mechanism. It may be more appropriate to create :forward-chaining rules instead of :compound-recognizer rules.




    acl2-sources/doc/HTML/COMPRESS1.html0000664002132200015000000000575412222333525016300 0ustar kaufmannacl2 COMPRESS1.html -- ACL2 Version 6.3

    COMPRESS1

    remove irrelevant pairs from a 1-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (compress1 'delta1 a)
    
    General Form:
    (compress1 name alist)
    
    where name is a symbol and alist is a 1-dimensional array, generally named name. See arrays for details. Logically speaking, this function removes irrelevant pairs from alist, possibly shortening it. The function returns a new array, alist', with the same header (including name and dimension) as alist, that, under aref1, is everywhere equal to alist. That is, (aref1 name alist' i) is (aref1 name alist i), for all legal indices i. Alist' may be shorter than alist and the non-irrelevant pairs may occur in a different order than in alist.

    Practically speaking, this function plays an important role in the efficient implementation of aref1. In addition to creating the new array, alist', compress1 makes that array the ``semantic value'' of name and allocates a raw lisp array to name. For each legal index, i, that raw lisp array contains (aref1 name alist' i) in slot i. Thus, subsequent aref1 operations can be executed in virtually constant time provided they are given name and the alist' returned by the most recently executed compress1 or aset1 on name. See arrays.

    In general, compress1 returns an alist whose cdr is an association list whose keys are nonnegative integers in ascending order. However, if the header specifies an :order of > then the keys will occur in descending order, and if the :order is :none or nil then the keys will not be sorted, i.e., compress1 is logically the identity function (though it still attaches an array under the hood). Note however that a compress1 call is replaced by a hard error if the header specifies an :order of :none or nil and the array's length exceeds the maximum-length field of its header.




    acl2-sources/doc/HTML/COMPRESS2.html0000664002132200015000000000424412222333525016272 0ustar kaufmannacl2 COMPRESS2.html -- ACL2 Version 6.3

    COMPRESS2

    remove irrelevant pairs from a 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (compress2 'delta1 a)
    
    General Form:
    (compress2 name alist)
    
    where name is a symbol and alist is a 2-dimensional array, generally named name. See arrays for details. Logically speaking, this function removes irrelevant pairs from alist, possibly shortening it. The function returns a new array, alist', with the same header (including name and dimension) as alist, that, under aref2, is everywhere equal to alist. That is, (aref2 name alist' i j) is (aref2 name alist i j), for all legal indices i and j. Alist' may be shorter than alist and the non-irrelevant pairs may occur in a different order in alist' than in alist.

    Practically speaking, this function plays an important role in the efficient implementation of aref2. In addition to creating the new array, alist', compress2 makes that array the ``semantic value'' of name and allocates a raw lisp array to name. For all legal indices, i and j, that raw lisp array contains (aref2 name alist' i j) in slot i,j. Thus, subsequent aref2 operations can be executed in virtually constant time provided they are given name and the alist' returned by the most recently executed compress2 or aset2 on name. See arrays.




    acl2-sources/doc/HTML/COMPUTED-HINTS.html0000664002132200015000000002331212222333520017050 0ustar kaufmannacl2 COMPUTED-HINTS.html -- ACL2 Version 6.3

    COMPUTED-HINTS

    computing advice to the theorem proving process
    Major Section:  MISCELLANEOUS
    

    General Form of :hints:
    (hint1 hint2 ... hintk)
    
    Each element, hinti, must be either a common hint or a computed hint.

    A common hint is of the form

    (goal-spec :key1 val1 ... :keyn valn)
    
    where goal-spec is as specified in goal-spec and each :keyi and vali is as specified in hints. Among the ``common hints'' we include both the primitive hints and user-defined custom keyword hints (see custom-keyword-hints).

    A computed hint may be a function symbol, fn, of three, four or seven arguments. Otherwise, a computed hint is a term with the following properties:

    (a) the only free variables allowed in the term are ID, CLAUSE, WORLD, STABLE-UNDER-SIMPLIFICATIONP, HIST, PSPV, CTX, and STATE;

    (b) the output signature of the term is either (MV * * STATE), which is to be treated as an error triple (see below), or is *, denoting a single non-stobj value; and

    (c) in the former case of (b) above, the term is single-threaded in STATE.

    If a computed hint is a function symbol fn, whose arity n is therefore three, four, or seven, then it is treated as the term resulting from applying that fn to the first n variables shown in (a) above. Notice that it must then return a single non-stobj value, not an error triple, since state is not one of the first seven variables shown in (a).

    Note: Error triples are an ACL2 idiom for implementing ``errors''; see error-triples. If a computation returns (mv erp val state) in a context in which ACL2 is respecting the error triple convention (see ld-error-triples and see ld-error-action), then an error is deemed to have occurred if erp is non-nil. The computation is expected to have printed an appropriate error message to state and further computation is aborted. On the other hand, if a computation returns an error triple in which erp is nil, then ``value'' of the computation is taken to be the second component, val, of the triple (along with the possibly modified state), and computation continues. For more information about programming with error triples, see programming-with-state.

    The function symbol cases are treated as abbreviations of the term (fn ID CLAUSE WORLD), (fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP), or (fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX) as appropriate for the arity of fn. (Note that this tells you which argument of fn is which.) Moreover, in these cases the value returned must be a single ordinary (non-stobj) value, not an error triple. In the discussion below we assume all computed hints are of the term form. Indeed, we almost assume all computed hints are of the 3 and 4 argument forms. We only comment briefly on the 7 argument form in using-computed-hints-8.

    The semantics of a computed hint term is as follows. On every subgoal, the term is evaluated in an environment in which the variables mentioned in (a) above are bound to context-sensitive values explained below. Either the computed hint signals an error, in which the proof attempt aborts, or else it returns a value, val and a new state, state. Any changes to those parts of state that affect logical soundness are undone; more specifically, the values of symbols (sometimes called ``state global variables'') in the list *protected-system-state-globals* in the global table of the state (see state) are restored when changed during evaluation. The value, val, of a non-erroneous computed hint calculation is either nil, which means the computed hint did not apply to the subgoal in question, or it is an alternating list of :keyi vali pairs as specified in hints. With one exception, those new hints are applied to the given subgoal as though the user had typed them explicitly.

    The exception is that the first keyword in the returned val is allowed to be :COMPUTED-HINT-REPLACEMENT. Its value should be nil, t, or a list of terms. If this keyword is not present, the default value of nil is provided. We explain :COMPUTED-HINT-REPLACEMENT below.

    The evaluation of a hint term is done with guard checking turned off (see set-guard-checking); e.g., the form (car 23) in a computed hint returns nil as per the axioms.

    When a non-nil value is returned, the keyword/value pairs (other than the optional :COMPUTED-HINT-REPLACEMENT) are used as the hint for the subgoal in question. Thus, your job as the programmer of computed hints is either to cause an error, typically by invoking er, or to return a non-erroneous value triple whose value is the list of keys and values you would have typed had you supplied a common hint for the subgoal. (In particular, any theory expressions in it are evaluated with respect to the global current-theory, not whatever theory is active at the subgoal in question.) If the generated list of keywords and values is illegal, an error will be signaled and the proof attempt will be aborted.

    The purpose of the :COMPUTED-HINT-REPLACEMENT keyword and its value, chr, is to change the list of hints. If chr is nil, then the hint which was applied is removed from the list of hints that is passed down to the children of the subgoal in question. This is the default. If chr is t, then the hint is left in the list of hints. This means that the same hint may act on the children of the subgoal. Otherwise, chr must be a list of terms, each of which is treated as a computed hint. The hint which was applied is deleted from the list of hints and the hints in chr are added to the list of hints passed to the children of the subgoal. The ability to compute new hints and pass them down allows strange and wonderful behavior.

    For these purposes, the goals produced by induction and the top-level goals of forcing rounds are not considered children; all original hints are available to them.

    Only the first hint applicable to a goal, as specified in the user-supplied list of :hints followed by the default-hints-table, will be applied to that goal. (For an advanced exception, see override-hints.)

    It remains only to describe the bindings of the free variables.

    Suppose the theorem prover is working on some clause, clause, named by some goal-spec, e.g., "Subgoal *1/2'''" in some logical world, world. Corresponding to the printed goal-spec is an internal data structure called a ``clause identifier'' id. See clause-identifier.

    In the case of a common hint, the hint applies if the goal-spec of the hint is the same as the goal-spec of the clause in question.

    In the case of a computed hint, the variable ID is bound to the clause id, the variable CLAUSE is bound to the (translated form of the) clause, and the variable WORLD is bound to the current ACL2 world. The variable STABLE-UNDER-SIMPLIFICATIONP is bound to t or nil. It is bound to t only if the clause is known to be stable under simplification. That is, the simplifier has been applied to the clause and did not change it. Such a clause is sometimes known as a ``simplification checkpoint.'' It is frequently useful to inject hints (e.g., to enable a rule or provide a :use hint) only when the goal in question has stabilized. If a hint is provided, the processing of the clause starts over with simplification.

    As for CTX and STATE, they are provided so that you can pass them to the er macro to print error messages. We recommend not writing computed hints that otherwise change STATE!

    The remaining variables, HIST and PSPV are not documented yet. Only users familiar with the internals of ACL2 are likely to need them or understand their values.

    For some instruction about how to use computed hints, see using-computed-hints.




    acl2-sources/doc/HTML/CONCATENATE.html0000664002132200015000000000335512222333523016501 0ustar kaufmannacl2 CONCATENATE.html -- ACL2 Version 6.3

    CONCATENATE

    concatenate lists or strings together
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (concatenate 'string "ab" "cd" "ef")     ; equals "abcdef"
    (concatenate 'string "ab")               ; equals "ab"
    (concatenate 'list '(a b) '(c d) '(e f)) ; equals '(a b c d e f)
    (concatenate 'list)                      ; equals nil
    
    General Form:
    (concatenate result-type x1 x2 ... xn)
    
    where n >= 0 and either: result-type is 'string and each xi is a string; or result-type is 'list and each xi is a true list. Concatenate simply concatenates its arguments to form the result string or list. Also see append and see string-append. (The latter immediately generates a call to concatenate when applied to strings.)

    Note: We do *not* try to comply with the Lisp language's insistence that concatenate copies its arguments. Not only are we in an applicative setting, where this issue shouldn't matter for the logic, but also we do not actually modify the underlying lisp implementation of concatenate; we merely provide a definition for it.

    Concatenate is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/COND.html0000664002132200015000000000133012222333523015467 0ustar kaufmannacl2 COND.html -- ACL2 Version 6.3

    COND

    conditional based on if-then-else
    Major Section:  ACL2-BUILT-INS
    

    Cond is the construct for IF, THEN, ELSE IF, ... The test is against nil. The argument list for cond is a list of ``clauses'', each of which is a list. In ACL2, clauses must have length 1 or 2.

    Cond is a Common Lisp macro. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/CONGRUENCE.html0000664002132200015000000002020212222333530016371 0ustar kaufmannacl2 CONGRUENCE.html -- ACL2 Version 6.3

    CONGRUENCE

    the relations to maintain while simplifying arguments
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes and how they are used to build rules from formulas. An example :corollary formula from which a :congruence rule might be built is:

    Example:
    (defthm set-equal-implies-iff-memb-2
      (implies (set-equal x y)
               (iff (memb e x) (memb e y)))
      :rule-classes :congruence)
    

    Also see defcong and see equivalence.

    General Form:
    (implies (equiv1 xk xk-equiv)
             (equiv2 (fn x1... xk       ...xn)
                     (fn x1... xk-equiv ...xn)))
    
    where equiv1 and equiv2 are known equivalence relations, fn is an n-ary function symbol and the xi and xk-equiv are all distinct variables. The effect of such a rule is to record that the equiv2-equivalence of fn-expressions can be maintained if, while rewriting the kth argument position, equiv1-equivalence is maintained. See equivalence for a general discussion of the issues. We say that equiv2, above, is the ``outside equivalence'' in the rule and equiv1 is the ``inside equivalence for the kth argument''

    The macro form (defcong equiv1 equiv2 (fn x1 ... x1) k) is an abbreviation for a defthm of rule-class :congruence that attempts to establish that equiv2 is maintained by maintaining equiv1 in fn's kth argument. The defcong macro automatically generates the general formula shown above. See defcong.

    The memb example above tells us that (memb e x) is propositionally equivalent to (memb e y), provided x and y are set-equal. The outside equivalence is iff and the inside equivalence for the second argument is set-equal. If we see a memb expression in a propositional context, e.g., as a literal of a clause or test of an if (but not, for example, as an argument to cons), we can rewrite its second argument maintaining set-equality. For example, a rule stating the commutativity of append (modulo set-equality) could be applied in this context. Since equality is a refinement of all equivalence relations, all equality rules are always available. See refinement.

    All known :congruence rules about a given outside equivalence and fn can be used independently. That is, consider two :congruence rules with the same outside equivalence, equiv, and about the same function fn. Suppose one says that equiv1 is the inside equivalence for the first argument and the other says equiv2 is the inside equivalence for the second argument. Then (fn a b) is equiv (fn a' b') provided a is equiv1 to a' and b is equiv2 to b'. This is an easy consequence of the transitivity of equiv. It permits you to think independently about the inside equivalences.

    Furthermore, it is possible that more than one inside equivalence for a given argument slot will maintain a given outside equivalence. For example, (length a) is equal to (length a') if a and a' are related either by list-equal or by string-equal. You may prove two (or more) :congruence rules for the same slot of a function. The result is that the system uses a new, ``generated'' equivalence relation for that slot with the result that rules of both (or all) kinds are available while rewriting.

    :Congruence rules can be disabled. For example, if you have two different inside equivalences for a given argument position and you find that the :rewrite rules for one are unexpectedly preventing the application of the desired rule, you can disable the rule that introduced the unwanted inside equivalence.

    Remark on Replacing IFF by EQUAL. You may encounter a warning suggesting that a congruence rule ``can be strengthened by replacing the second equivalence relation, IFF, by EQUAL.'' Suppose for example that this warning occurs when you submit the following rule:

    (defcong equiv1 iff (fn x y) 2)
    
    which is shorthand for the following:
    (defthm equiv1-implies-iff-fn-2
           (implies (equiv1 y y-equiv)
                    (iff (fn x y) (fn x y-equiv)))
           :rule-classes (:congruence))
    
    The warning is telling you that ACL2 was able to deduce that fn always returns a Boolean, and hence a trivial but useful consequence is obtained by replacing iff by equal --
    (defcong equiv1 equal (fn x y) 2)
    
    -- which is shorthand for the following:
    (defthm equiv1-implies-equal-fn-2
           (implies (equiv1 y y-equiv)
                    (equal (fn x y) (fn x y-equiv)))
           :rule-classes (:congruence))
    
    If you have difficulty proving the latter directly, you can derive it from the former by giving a suitable hint, minimally as follows.
    (defcong equiv1 equal (fn x y) 2
      :hints (("Goal"
               :use equiv1-implies-iff-fn-2
               :in-theory
               (union-theories '((:type-prescription fn))
                               (theory 'minimal-theory)))))
    
    By heeding this warning, you may avoid unnecessary double-rewrite warnings later. We now explain why, but see double-rewrite for relevant background material.

    For example, suppose you have proved the ``iff'' version of the congruence rule above, and later you submit the following rewrite rule.

    (defthm equal-list-perm
      (implies (equiv1 x y)
               (fn x y)))
    
    Since fn is known to return a Boolean, ACL2 performs an optimization that stores this rule as though it were the following.
    (defthm equal-list-perm
      (implies (equiv1 x y)
               (equal (fn x y) t)))
    
    Thus, if ACL2's rewriter sees a term (fn a b) in a context where the equivalence relation iff is not being maintained, then it cannot use rule equiv1-implies-iff-fn-2, so it rewrites argument a without the benefit of knowing that it suffices to maintain equiv1; and then it caches the result. When ACL2 subsequently attempts to relieve the hypothesis (equiv1 x y), it will rewrite x simply by returning the rewritten value of a from the result cache. This is unfortunate if a could have been rewritten more completely under maintainance of the equivalence relation equiv1 -- which is legal in the hypothesis since a is an argument of equiv1, which is an equivalence relation. The user who observes the warning from rule equiv1-implies-iff-fn-2, and replaces it with equiv1-implies-equal-fn-2, will avoid this unfortunate case.




    acl2-sources/doc/HTML/CONJUGATE.html0000664002132200015000000000133012222333523016263 0ustar kaufmannacl2 CONJUGATE.html -- ACL2 Version 6.3

    CONJUGATE

    complex number conjugate
    Major Section:  ACL2-BUILT-INS
    

    Conjugate takes an ACL2 number as an argument, and returns its complex conjugate (i.e., the result of negating its imaginary part.).

    Conjugate is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/CONS-SUBTREES.html0000664002132200015000000000135312222333517016750 0ustar kaufmannacl2 CONS-SUBTREES.html -- ACL2 Version 6.3

    CONS-SUBTREES

    (cons-subtrees x nil) builds a fast alist that associates each subtree of X with T, without duplication.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.




    acl2-sources/doc/HTML/CONS.html0000664002132200015000000000116412222333523015513 0ustar kaufmannacl2 CONS.html -- ACL2 Version 6.3

    CONS

    pair and list constructor
    Major Section:  ACL2-BUILT-INS
    

    (cons x y) is a pair whose first component is x and second component is y. If y is a list, then (cons x y) is a list that has an addtional element x on the front.




    acl2-sources/doc/HTML/CONSERVATIVITY-OF-DEFCHOOSE.html0000664002132200015000000004745512222333516020760 0ustar kaufmannacl2 CONSERVATIVITY-OF-DEFCHOOSE.html -- ACL2 Version 6.3

    CONSERVATIVITY-OF-DEFCHOOSE

    proof of conservativity of defchoose
    Major Section:  DEFCHOOSE
    

    This documentation topic provides underlying theory. It is of theoretical interest only; it has no relationship to the effective use of ACL2.

    The argument below for the conservativity of defchoose replaces the terse and somewhat misleading reference to a forcing argument in Appendix B of the paper by ACL2 authors Kaufmann and Moore, ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203).

    Our basic idea is to to take a (countable) first-order structure for ACL2, M, together with a function symbol, f, introduced by defchoose, and find a way to expand M with an interpretation of f (without changing the universe of M) so that e0-induction continues to hold in the expansion. A remark at the end of this documentation topic shows why care is necessary. A concept called ``forcing'', originally introduced by Paul Cohen for set theory, has long since been adapted by logicians (in a simplified form) to model theory. This simplified model-theoretic forcing provides the means for making our careful expansion.

    The forcing argument presented below is intended to be completely self-contained for those familiar with basic first-order logic and ACL2. No background in forcing (model-theoretic or otherwise) is expected, though we do expect a rudimentary background in first-order logic and familiarity with the following.

    Preliminaries. We write s[p<-p0] to denote the result of extending or modifying the assignment s by binding p to p0. Now let A be a subset of the universe U of a first-order structure M. A is said to be ``first-order definable with parameters'' in M if for some formula phi, variable x, and assignment s binding the free variables of phi except perhaps for x, A = {a \in U: M |= phi[s[x<-a]]. Note that we are writing ``\in'' to denote set membership. Finally, we indicate the end of a proof (or of a theorem statement, when the proof is omitted) with the symbol ``-|''.

    We gratefully acknowledge very helpful feedback from John Cowles, who found several errors in a draft of this note and suggested the exercises. We also thank Ruben Gamboa for helpful feedback, and we thank Jim Schmerl for an observation that led us directly to this proof in the first place.

    We are given a consistent first-order theory T, extending the ACL2 ground-zero theory, that satisfies the e0-induction scheme. We wish to show that the extension of T by the following arbitrary defchoose event is conservative, where g is a new function symbol.

         (defchoose g <bound-vars> <free-vars> <body>)
    
    Note that by ``the extension of T'' here we mean the extension of T by not only the new defchoose axiom displayed just below, but also the addition of e0-induction axioms for formulas in the language with the new defchoose function symbol, g.
         <body> -> (LET <free-vars> = g(<bound-vars>) in <body>)
    
    By definition of conservativity, since proofs are finite, it clearly suffices to consider an arbitrary finite subset of T. Then by the completeness, soundness, and downward Lowenheim-Skolem theorems of first-order logic, it suffices to show that an arbitrary countable model of T can be expanded (i.e., by interpreting the new symbol g without changing the universe of the model) to a model of the corresponding defchoose axiom above, in which all e0-induction axioms hold in the language of that model.

    Below, we will carry out a so-called forcing construction, which allows us to expand any countable model M of T to a model M[G] that satisfies e0-induction and also satisfies the above axiom generated from the above defchoose event. The ideas in this argument are standard in model theory; no novelty is claimed here.

    Fix a countable model M of a theory T that satisfies e0-induction and extends the ACL2 ground-zero theory. Also fix the above defchoose axiom, where g is not in the language of T.

    We start by defining a partial order P as follows. Let Nb and Nf be the lengths of <bound-vars> and <free-vars>, respectively. P consists of all fn in M such that the following formula is true in M. Roughly speaking, it says that fn is a finite function witnessing the above requirement for g.

           alistp(fn) &
           no-duplicatesp-equal(strip-cars(fn)) &
           (forall <bound-vars>, <free-vars> .
              (member-equal(cons(<bound-vars>,<free-vars>), fn) ->
               (length(<bound-vars>) = Nb &
                length(<free-vars>)  = Nf &
                ((exists <free-vars> . <body>) -> <body>))))
    
    P is ordered by subset, i.e., we say that p2 extends p1 if p1 is a subset (not necessarily proper) of p2 (more precisely, M |= subsetp-equal(p1,p2)).

    Remark. The original argument in Appendix B of the aforementioned paper can essentially be salvaged, as we now show. The key observation is that the particular choice of P is nearly irrelevant for the argument that follows below. In particular, we can instead define P to consist of finite one-one functions with domain contained in the set of natural numbers. More precisely, consider the following definitions.

         (defun function-p (fn)
           (declare (xargs :guard t))
           (and (alistp fn)
                (no-duplicatesp-equal (strip-cars fn))))
    
         (defun nat-listp (l)
           (declare (xargs :guard t))
           (cond ((atom l)
                  (eq l nil))
                 (t (and (natp (car l))
                         (nat-listp (cdr l))))))
    
         (defun nat-function-p (x)
           (and (function-p x)
                (nat-listp (strip-cars x))))
    
    and define inverse as follows.
         (defun inverse (fn)
           (declare (xargs :guard (alistp fn)))
           (if (endp fn)
               nil
             (cons (cons (cdar fn) (caar fn))
                   (inverse (cdr fn)))))
    
    Then P may instead be defined to consist of those fn for which nat-function-p(fn) & function-p(inverse(fn)). With this alternate definition of P, the argument below then goes through virtually unchanged, and we get an expansion M[G] of M in which there is a definable enumeration of the universe. The conservativity of defchoose then follows easily because the function being introduced can be defined explicitly using that enumeration (namely, always pick the least witness in the sense of the enumeration).

    End of Remark.

    Next we present the relevant forcing concepts from model theory.

    A dense subset of P is a subset D of P such that for every p \in P, there is d \in D such that d extends p. A subset G of P is generic with respect to a collection Ds of dense subsets of P, also written ``G is Ds-generic,'' if G is closed under subset (if p2 \in G and p2 extends p1 then p1 \in G), G is pairwise compatible (the union-equal of any two elements of G is in G), and every set in Ds has non-empty intersection with G.

    For p \in P, we say that a subset D of P is dense beyond p if for all p1 extending p there exists p2 extending p1 such that p2 \in D. This notion makes sense even for D not a subset of P if we treat elements of D not in P as nil.

    Proposition 1. For any partial order P and countable collection Ds of dense subsets of P, there is a Ds-generic subset of P.

    Proof. Let Ds = {D0,D1,D2,...}. Define a sequence <p_0,p_1,...> such that for all i, p_i \in Di and p_(i+1) extends p_i. Let G = {p \in P: for some i, pi extends p}. Then G is Ds-generic. -|

    Note that P is first-order definable (with parameters) in M. Let Df be the set of dense subsets of P that are first-order definable (with parameters) in M. A standard argument shows there are only countably many first-order definitions with parameters in a countable model M -- for example, we can Goedel number all terms and then all formulas -- hence, Df is countable.

    By Proposition 1, let G be Df-generic. Notice that for any list x of length Nb in M, the set of elements f of P for which x is in the domain of f is dense and first-order definable. We may thus define a function g0 as follows: g0(x_1,...,x_Nb) = y if there is some element of G containing the pair ((x_1 ... x_Nb) . y). It is easy to see that g0 is a total function on M. Let L be the language of T and let L[g] be the union of L with a set containing a single new function symbol, g. Let M[G] be the expansion of M to L[g] obtained by interpreting g to be g0 (see also Proposition 5 below).

    So now we have fixed M, P, Df, G, and g0, where G is Df-generic.

    Proposition 2. Let Df be the set of dense subsets of P that are first-order definable (with parameters) in M. Suppose that p \in G and D \in Df. Then for some q \in G extending p, q \in D.

    Proof. Let D0 be the set of p' \in D that either extend p or have no extension in D that extends p. We leave it as a straightforward exercise to show that D0 is dense, and D0 is clearly first-order definable (with parameters) in M. So by genericity of G, we may pick q \in D0 such that q \in G. Thus q \in D. By definition of generic, some extension q1 of both p and q belongs to G. Pick q2 \in D extending q1; thus q has an extension in D that extends p (namely, q2), so by definition of D0, q extends p. -|

    Definition of forcing. Let phi(x1,...,xk) be a first-order formula in L[g] and let p \in P. We define a formula of L, denoted ``p ||- phi'' (``p forces phi''), by recursion on phi (in the metatheory) as follows. (Here, we view ``or'' and ``forall'' as abbreviations.)

    If phi is atomic, then let phi'(A) be the result of replacing, inside-out, each subterm of the form g(x_1,...,x_Nb) with the term (cdr (assoc-equal (list x_1 ... x_Nb) A)), where A is neither p nor a variable occurring in phi. Then p ||- phi is defined as follows: ``The set {A \in P: A extends p and phi'(A)} is dense beyond p''. That is, p ||- phi is the following formula:

        (forall p1 \in P extending p)
         (exists p2 \in P extending p1) phi'(p2).
    
    p ||- ~phi is: (forall p' \in P extending p) ~(p' ||- phi)

    p ||- phi_1 & phi_2 is: (p ||- phi_1) & (p ||- phi_2)

    p ||- (exists x) phi is: (exists x) (p ||- phi)

    We will need the following definition later.

    Definition. p ||-w phi (p weakly forces phi) is an abbreviation for p ||- ~~phi.

    The following exercises were suggested by John Cowles as a means for gaining familiarity with the definition of forcing.

    Exercise 1. Consider the formula (phi_1 OR phi_2) as an abbreviation for ~(~phi_1 & ~phi_2), Show that p ||- (phi_1 OR phi_2) is equivalent to the following.

         (forall p' \in P extending p) (exists p'' \in P extending p')
          ((p'' ||- phi_1) OR (p'' ||- phi_2))
    

    Exercise 2. Consider the formula (forall x)phi as an abbreviation for ~(exists x)~phi, Show that p ||- (forall x)phi is equivalent to the following.

         (forall x)
          (forall p1 \in P extending p)
           (exists p2 \in P extending p1) (p2 ||- phi).
    

    Exercise 3. Prove that p ||-w phi is equivalent to the following.

         (forall p' \in P extending p)
          (exists p'' \in P extending p') (p'' ||- phi).
    

    Exercise 4. Let phi be a formula of L[g]. Prove: M |= (p ||- phi)[s[p<-p0]] implies M |= (p ||-w phi)[s[p<-p0]].

    Exercise 5. Let phi be a formula of L[g]. Prove: M |= (p ||- ~phi)[s[p<-p0]] iff M |= (p ||-w ~phi)[s[p<-p0]].

    [End of exercises.]

    The definition of forcing stipulates how to view ``p ||- phi(x1,...,xk)'' as a new formula theta(p,x1,...,xk). That is, ``||-'' transforms formulas, so for any first-order formula phi, ``p ||- phi'' is just another first-order formula. That observation shows that a formula such as ((p ||- phi) OR (p ||- ~phi)) is really just another first-order formula. The following proposition thus follows easily.

    Proposition 3. For any formula phi of L[g], {p0: M |= ((p ||- phi) OR (p ||- ~phi))[s[p<-p0]]]} is a dense subset of P, which (since it is first-order definable with parameters in M) intersects G. -|

    The following proposition is easily proved by a structural induction on phi, and is left to the reader.

    Proposition 4. Let phi be a formula of L[g]. Suppose p0 in P, p1 in P,
    M |= (p ||- phi)[s[p<-p0]] and p1 extends p0. Then
    M |= (p ||- phi)[s[p<-p1]]. -|

    We will also need the following.

    Proposition 5. The following is dense for any finite set S of Nb-tuples: {p \in P: for some <x_1 ... x_Nb> \in S, (list x_1 ... x_Nb) \in strip-cars(p)}. Thus, the function g0 is a total function. -|

    The next lemma tells us that the sentences true in M[G] are those that are forced by an element of G.

    Truth Lemma. Let phi be a formula in L[g], let s be an assignment to the free variables of phi, and let p be a variable not in the domain of s. Then M[G] |= phi[s] iff for some p0 \in G, M |= (p ||- phi)[s[p<-p0]].

    Proof. The proof is by induction on the structure of phi. First suppose phi is atomic. Let D* be the set of elements p0 \in P such that every assoc-equal evaluation from the definition of forcing phi returns a pair when A is bound to p0. (Intuitively, this means that p0 is a sufficiently large approximation from any G containing p0 to make sense of phi in M[G].) We make the following claim.

    (*)   For all p0 \in G such that p0 \in D*,
          M[G] |= phi[s] iff M |= (p ||- phi)[s[p<-p0]].
    

    To prove the claim, fix p0 in both G and D*, and recall the function g0 constructed from G in the definition of M[G]. Suppose that t_1, ..., t_Nb are terms and g(t_1, ..., t_Nb) is a subterm of phi. Then s assigns a value in M to each of the t_i. Let a_i be the value assigned by s to t_i. Then g0(a_1, ..., a_Nb) = (cdr (assoc-equal (list a_1 ... a_Nb) p0)), as the assoc-equal is a pair (since p0 \in D*) and has the indicated value (because p0 \in G). It follows by the definition of formula phi' in the definition of forcing:

         M[G] |= phi[s]  iff  M |= phi'(p)[s[p<-p0]]
    
    Moreover, because p0 \in D* it is clear that this holds if p0 is replaced by an arbitrary extension of p0. Then (*) easily follows.

    By Proposition 5, D* is dense, so there is some p0 in the intersection of D* and G. The forward direction of the conclusion then follows by (*). The reverse direction is clear from (*) by application of Proposition 2 to D* and Proposition 4.

    Next, suppose M[G] |= ~phi[x]. Then it is not the case that M[G] |= phi, so by the inductive hypothesis, there is no p0 \in G for which M |= (p ||- phi)[s[p<-p0]]. By Proposition 3, there is p0 \in G for which M |= (p ||- ~phi)[s[p<-p0]]. For the other direction, suppose it is not the case that M[G] |= ~phi[s]. So M[G] |= phi[s], and by the inductive hypothesis, there is p0 \in G for which M |= (p ||- phi)[s[p<-p0]]. It follows that there is no p1 \in G for which M |= (p ||- ~phi)[s[p<-p1]], since from such p1 we can find a common extension p2 of p0 and p1 (since G is generic), and since p2 extends p0 then by Proposition 4, M |= (p ||- phi)[s[p<-p2]], contradicting (by definition of forcing) M |= (p ||- ~phi)[s[p<-p1]] since p2 extends p1.

    The case (phi_1 & phi_2) follows easily from the inductive hypothesis. For the forward direction, apply Proposition 4 and the observation that by genericity, if p0 \in G and p1 \in G then p0 and p1 they have a common extension in G.

    Finally, the case (exists x) phi follows trivially from the inductive hypothesis. -|

    Truth Lemma Corollary. The Truth Lemma holds with ||-w replacing ||-.

    Proof. This is clear by applying the Truth Lemma to ~~phi. -|

    Here is our main theorem. Recall that all first-order theories in our ACL2 context satisfy the e0-induction scheme.

    Theorem. M[G] satisfies e0-induction.

    Proof. We consider an arbitrary instance of e0-induction in L[g], stated using a strict well-founded relation <| and a formula phi. We write phi(y) to indicate that y may be among the free variables of phi, and phi(y<-x) to denote the result of substituting x for y in phi.

      theta(y):   (forall y) [((forall x <| y) phi(y<-x)) -> phi(y)]
               -> (forall y) phi(y)
    
    Our goal is to prove that theta holds in M[G].

    Below, we abuse notation by leaving assignments implicit and by writing ``p ||- phi(y0)'' to signify that the formula (p ||- phi(y)) is true in M under the extension of the explicit assignment that binds y to y0. We believe that the intended meaning will be clear.

    Consider the following set D.

      D = {p \in P: either p ||-w phi(y0) for all y0,
                    or else
                    for some y0, p ||- ~phi(y0) and
                                 for all y1 <| y0 p ||-w phi(y1)}.
    
    The set D is clearly first-order definable (with parameters) in M. We claim that D is a dense subset of P. For suppose p0 \in P; we find p1 \in D extending p0, as follows. If p0 ||-w phi(y0) for all y0, then we may take p1 to be p0. Otherwise, by definition of ||-w and ||-, there is some y0 such that for some extension p0' of p0, p0' ||- ~phi(y0). Pick a <|-minimal such y0, and correspondingly pick p1 so that p1 extends p0 and p1 ||- ~phi(y0). In order to show that p1 \in D, it remains to show that for all y1 <| y0, p1 ||-w phi(y1), i.e., there is no q extending p1 such that q ||- ~phi(y1). This is indeed the case since otherwise q and y1 would contradict the <|-minimality of y0.

    Applying the genericity of G and just-proved density of D, pick p0 \in G such that p0 \in D. If p0 ||-w phi(y0) for all y0, then by the Truth Lemma Corollary, M[G] |= phi(y0) for all y0, and thus M[G] |= theta. Otherwise, since p0 \in D we may choose y0 such that p0 ||- ~phi(y0) and for all y1 <| y0, p0 ||-w phi(y1). By the Truth Lemma and its corollary, since p0 \in G we have:

    (1)   M[G] |= ~phi(y0).
    (2)   For all y1 <| y0, M[G] |= phi(y1).
    
    It follows that the antecedent of theta is false in M[G], as witnessed by y = y0; thus M[G] |= theta. -|

    Remark. We close by returning, as promised above, to the question of why so much care is necessary in constructing an expansion of M. We assume familiarity here with the notion of a ``non-standard'' natural number of M, i.e., one that is greater than the interpretation of any term that has the form (+ 1 1 1 ... 1). Here is a very simple example that illustrates the need for some care. Consider the following event, which introduces a function foo with the following property: for all x, if natp(x) then natp(foo(x)).

         (defchoose foo (y) (x)
           (implies (natp x) (natp y)))
    
    Certainly we can build a model of the above property from a model M of the ground-zero theory, by interpreting foo so that for all x for which M satisfies natp(x), foo(x) is also a natp in M. But suppose we start with a non-standard model M of the ground-zero theory, and we happen to define foo(x) to be 1 for all non-standard natural numbers x and 0 for all other x. The resulting expansion of M will not satisfy the e0-induction scheme or even the ordinary natural number induction scheme: foo(0)=0 holds in that expansion as does the implication foo(n)=0 => foo(n+1)=0 for every natural number n of M, standard or not; and yet foo(k)=0 fails for every non-standard natural number k of M.




    acl2-sources/doc/HTML/CONSP.html0000664002132200015000000000100112222333523015621 0ustar kaufmannacl2 CONSP.html -- ACL2 Version 6.3

    CONSP

    recognizer for cons pairs
    Major Section:  ACL2-BUILT-INS
    

    (consp x) is true if and only if x is a cons pair.




    acl2-sources/doc/HTML/CONSTRAINT.html0000664002132200015000000005450212222333520016436 0ustar kaufmannacl2 CONSTRAINT.html -- ACL2 Version 6.3

    CONSTRAINT

    restrictions on certain functions introduced in encapsulate events
    Major Section:  MISCELLANEOUS
    

    Suppose that a given theorem, thm, is to be functionally instantiated using a given functional substitution, alist. (See lemma-instance, or for an example, see functional-instantiation-example.) What is the set of proof obligations generated? It is the set obtained by applying alist to all terms, tm, such that (a) tm mentions some function symbol in the domain of alist, and (b) either (i) tm arises from the ``constraint'' on a function symbol ancestral in thm or in some defaxiom or (ii) tm is the body of a defaxiom. Here, a function symbol is ``ancestral'' in thm if either it occurs in thm, or it occurs in the definition of some function symbol that occurs in thm, and so on.

    The remainder of this note explains what we mean by ``constraint'' in the words above.

    In a certain sense, function symbols are introduced in essentially two ways. The most common way is to use defun (or when there is mutual recursion, mutual-recursion or defuns). There is also a mechanism for introducing ``witness functions''; see defchoose. The documentation for these events describes the axioms they introduce, which we will call here their ``definitional axioms.'' These definitional axioms are generally the constraints on the function symbols that these axioms introduce.

    However, when a function symbol is introduced in the scope of an encapsulate event, its constraints may differ from the definitional axioms introduced for it. For example, suppose that a function's definition is local to the encapsulate; that is, suppose the function is introduced in the signature of the encapsulate. Then its constraints include, at the least, those non-local theorems and definitions in the encapsulate that mention the function symbol.

    Actually, it will follow from the discussion below that if the signature is empty for an encapsulate, then the constraint on each of its new function symbols is exactly the definitional axiom introduced for it. Intuitively, we view such encapsulates just as we view include-book events. But the general case, where the signature is not empty, is more complicated.

    In the discussion that follows we describe in detail exactly which constraints are associated with which function symbols that are introduced in the scope of an encapsulate event. In order to simplify the exposition we make two cuts at it. In the first cut we present an over-simplified explanation that nevertheless captures the main ideas. In the second cut we complete our explanation by explaining how we view certain events as being ``lifted'' out of the encapsulate, resulting in a possibly smaller encapsulate, which becomes the target of the algorithm described in the first cut.

    At the end of this note we present an example showing why a more naive approach is unsound.

    Finally, before we start our ``first cut,'' we note that any information you want ``exported'' outside an encapsulate event must be there as an explicit definition or theorem. For example, even if a function foo has output type (mv t t) in its signature, the system will not know (true-listp (foo x)) merely on account of this information. Thus, if you are using functions like foo (constrained mv functions), then you may find it useful to prove (inside the encapsulate, to be exported) a :type-prescription rule for the constrained function, for example, the :type-prescription rule (true-listp (foo x)).

    First cut at constraint-assigning algorithm. Quite simply, the formulas introduced in the scope of an encapsulate are conjoined, and each function symbol introduced by the encapsulate is assigned that conjunction as its constraint.

    Clearly this is a rather severe algorithm. Let us consider two possible optimizations in an informal manner before presenting our second cut.

    Consider the (rather artificial) event below. The function before1 does not refer at all, even indirectly, to the locally-introduced function sig-fn, so it is unfortunate to saddle it with constraints about sig-fn.

    (encapsulate
     (((sig-fn *) => *))
    
     (defun before1 (x)
       (if (consp x)
           (before1 (cdr x))
         x))
    
     (local (defun sig-fn (x) (cons x x)))
    
     (defthm sig-fn-prop
       (consp (sig-fn x)))
     )
    
    We would like to imagine moving the definition of before1 to just in front of this encapsulate, as follows.
    (defun before1 (x)
      (if (consp x)
          (before1 (cdr x))
        x))
    
    (encapsulate
     (((sig-fn *) => *))
    
     (local (defun sig-fn (x) (cons x x)))
    
     (defthm sig-fn-prop
       (consp (sig-fn x)))
     )
    
    Thus, we will only assign the constraint (consp (sig-fn x)), from the theorem sig-fn-prop, to the function sig-fn, not to the function before1.

    More generally, suppose an event in an encapsulate event does not mention any function symbol in the signature of the encapsulate, nor any function symbol that mentions any such function symbol, and so on. (We might say that no function symbol from the signature is an ``ancestor'' of any function symbol occurring in the event.) Then we imagine moving the event, so that it appears in front of the encapsulate. We don't actually move it, but we pretend we do when it comes time to assign constraints. Thus, such definitions only introduce definitional axioms as the constraints on the function symbols being defined. In the example above, the event sig-fn-prop introduces no constraints on function before1.

    Once this first optimization is performed, we have in mind a set of ``constrained functions.'' These are the functions introduced in the encapsulate that would remain after moving some of them in front, as indicated above. Consider the collection of all formulas introduced by the encapsulate, except the definitional axioms, that mention these constrained functions. So for example, in the event below, no such formula mentions the function symbol after1.

    (encapsulate
     (((sig-fn *) => *))
    
     (local (defun sig-fn (x) (cons x x)))
    
     (defthm sig-fn-prop
       (consp (sig-fn x)))
    
     (defun after1 (x)
       (sig-fn x))
     )
    
    We can see that there is really no harm in imagining that we move the definition of after1 out of the encapsulate, to just after the encapsulate.

    Many subtle aspects of this rearrangement process have been omitted. For example, suppose the function fn uses sig-fn, the latter being a function in the signature of the encapsulation. Suppose a formula about fn is proved in the encapsulation. Then from the discussion above fn is among the constrained functions of the encapsulate: it cannot be moved before the encapsulate and it cannot be moved after the encapsulation. But why is fn constrained? The reason is that the theorem proved about fn may impose or express constraints on sig-fn. That is, the theorem proved about fn may depend upon properties of the witness used for sig-fn. Here is a simple example:

    (encapsulate
     (((sig-fn *) => *))
    
     (local (defun sig-fn (x) (declare (ignore x)) 0))
    
     (defun fn (lst)
       (if (endp lst)
           t
           (and (integerp (sig-fn (car lst)))
                (fn (cdr lst)))))
    
     (defthm fn-always-true
       (fn lst)))
    
    In this example, there are no defthm events that mention sig-fn explicitly. One might therefore conclude that it is completely unconstrained. But the witness we chose for it always returns an integer. The function fn uses sig-fn and we prove that fn always returns true. Of course, the proof of this theorem depends upon the properties of the witness for sig-fn, even though those properties were not explicitly ``called out'' in theorems proved about sig-fn. It would be unsound to move fn-always-true after the encapsulate. It would also be unsound to constrain sig-fn to satisfy just fn-always-true without including in the constraint the relation between sig-fn and fn. Hence both sig-fn and fn are constrained by this encapsulation and the constraint imposed on each is the same and states the relation between the two as characterized by the equation defining fn as well as the property that fn always returns true. Suppose, later, one proved a theorem about sig-fn and wished to functionally instantiate it. Then one must also functionally instantiate fn, even if it is not involved in the theorem, because it is only through fn that sig-fn inherits its constrained properties.

    This is a pathological example that illustrate a trap into which one may easily fall: rather than identify the key properties of the constrained function the user has foreshadowed its intended application and constrained those notions. Clearly, the user wishing to introduce the sig-fn above would be well-advised to use the following instead:

    (encapsulate
     (((sig-fn *) => *))
     (local (defun sig-fn (x) (declare (ignore x)) 0))
     (defthm integerp-sig-fn
       (integerp (sig-fn x))))
    
    (defun fn (lst)
      (if (endp lst)
          t
        (and (integerp (sig-fn (car lst)))
             (fn (cdr lst)))))
    
    (defthm fn-always-true
       (fn lst)))
    
    Note that sig-fn is constrained merely to be an integer. It is the only constrained function. Now fn is introduced after the encapsulation, as a simple function that uses sig-fn. We prove that fn always returns true, but this fact does not constrain sig-fn. Future uses of sig-fn do not have to consider fn at all.

    Sometimes it is necessary to introduce a function such as fn within the encapsulate merely to state the key properties of the undefined function sig-fn. But that is unusual and the user should understand that both functions are being constrained.

    Another subtle aspect of encapsulation that has been brushed over so far has to do with exactly how functions defined within the encapsulation use the signature functions. For example, above we say ``Consider the collection of all formulas introduced by the encapsulate, except the definitional axioms, that mention these constrained functions.'' We seem to suggest that a definitional axiom which mentions a constrained function can be moved out of the encapsulation and considered part of the ``post-encapsulation'' extension of the logical world, if the defined function is not used in any non-definitional formula proved in the encapsulation. For example, in the encapsulation above that constrained sig-fn and introduced fn within the encapsulation, fn was constrained because we proved the formula fn-always-true within the encapsulation. Had we not proved fn-always-true within the encapsulation, fn could have been moved after the encapsulation. But this suggests an unsound rule because whether such a function can be moved after the encapsulate depend on whether its admission used properties of the witnesses! In particular, we say a function is ``subversive'' if any of its governing tests or the actuals in any recursive call involve a function in which the signature functions are ancestral. See subversive-recursions.

    (Aside: The definition of fn in the first enapsulation above that defines fn, i.e., the encapsulation with fn-always-true inside, is subversive because the call of the macro AND expands to a call of IF that governs a recursive call of fn, in this case:

    (defun fn (lst)
      (if (endp lst)
          t
          (if (integerp (sig-fn (car lst)))
              (fn (cdr lst))
            nil))).
    
    If we switch the order of conjuncts in fn, then the definition of fn is no longer subversive, but it still ``infects'' the constraint generated for the encapsulation, hence for sig-fn, because fn-always-true blocks the definition of fn from being moved back (to after the encapsulation). If we both switch the order of conjuncts and drop fn-always-true from the encapsulation, then the definition of fn is in essence moved back to after the encapsulation, and the constraint for sig-fn no longer includes the definition of fn. End of aside.)

    Another aspect we have not discussed is what happens to nested encapsulations when each introduces constrained functions. We say an encapsulate event is ``trivial'' if it introduces no constrained functions, i.e., if its signatures is nil. Trivial encapsulations are just a way to wrap up a collection of events into a single event.

    From the foregoing discussion we see we are interested in exactly how we can ``rearrange'' the events in a non-trivial encapsulation -- moving some ``before'' the encapsulation and others ``after'' the encapsulation. We are also interested in which functions introduced by the encapsulation are ``constrained'' and what the ``constraints'' on each are. We may summarize the observations above as follows, after which we conclude with a more elaborate example.

    Second cut at constraint-assigning algorithm. First, we focus only on non-trivial encapsulations that neither contain nor are contained in non-trivial encapsulations. (Nested non-trivial encapsulations are not rearranged at all: do not put anything in such a nest unless you mean for it to become part of the constraints generated.) Second, in what follows we only consider the non-local events of such an encapsulate, assuming that they satisfy the restriction of using no locally defined function symbols other than the signature functions. Given such an encapsulate event, move, to just in front of it and in the same order, all definitions and theorems for which none of the signature functions is ancestral. Now collect up all formulas (theorems) introduced in the encapsulate other than definitional axioms. Add to this set any of those definitional equations that is either subversive or defines a function used in a formula in the set. The conjunction of the resulting set of formulas is called the ``constraint'' and the set of all the signature functions of the encapsulate together with all function symbols defined in the encapsulate and mentioned in the constraint is called the ``constrained functions.'' Assign the constraint to each of the constrained functions. Move, to just after the encapsulate, the definitions of all function symbols defined in the encapsulate that have been omitted from the constraint.

    Implementation note. In the implementation we do not actually move events, but we create constraints that pretend that we did.

    Here is an example illustrating our constraint-assigning algorithm. It builds on the preceding examples.

    (encapsulate
     (((sig-fn *) => *))
    
     (defun before1 (x)
       (if (consp x)
           (before1 (cdr x))
         x))
    
     (local (defun sig-fn (x) (cons x x)))
    
     (defthm sig-fn-prop
       (consp (sig-fn x)))
    
     (defun during (x)
       (if (consp x)
           x
         (cons (car (sig-fn x))
               17)))
    
     (defun before2 (x)
       (before1 x))
    
     (defthm before2-prop
       (atom (before2 x)))
    
     (defthm during-prop
       (implies (and (atom x)
                     (before2 x))
                (equal (car (during x))
                       (car (sig-fn x)))))
    
     (defun after1 (x)
       (sig-fn x))
    
     (defchoose after2 (x) (u)
       (and (< u x) (during x)))
     )
    

    Only the functions sig-fn and during receive extra constraints. The functions before1 and before2 are viewed as moving in front of the encapsulate, as is the theorem before2-prop. The functions after1 and after2 are viewed as being moved past the encapsulate. The implementation reports the following.

    In addition to SIG-FN, we export AFTER2, AFTER1, BEFORE2, DURING and
    BEFORE1.
    
    The following constraint is associated with both of the functions DURING and
    SIG-FN:
    
    (AND (EQUAL (DURING X)
                (IF (CONSP X)
                    X (CONS (CAR (SIG-FN X)) 17)))
         (CONSP (SIG-FN X))
         (IMPLIES (AND (ATOM X) (BEFORE2 X))
                  (EQUAL (CAR (DURING X))
                         (CAR (SIG-FN X)))))
    
    Notice that the formula (consp (during x)) is not a conjunct of the constraint. During the first pass of the encapsulate, this formula is stored as a :type-prescription rule deduced during the definition of the function during. However, the rule is not exported because of a rather subtle soundness issue. (If you are interested in details, see the comments in source function putprop-type-prescription-lst.)

    We conclude by asking (and to a certain extent, answering) the following question: Isn't there an approach to assigning constraints that avoids over-constraining more simply than our ``second cut'' above? Perhaps it seems that given an encapsulate, we should simply assign to each locally defined function the theorems exported about that function. If we adopted that simple approach the events below would be admissible.

    (encapsulate
     (((foo *) => *))
     (local (defun foo (x) x))
     (defun bar (x)
       (foo x))
     (defthm bar-prop
       (equal (bar x) x)
       :rule-classes nil))
    
    (defthm foo-id
      (equal (foo x) x)
      :hints (("Goal" :use bar-prop)))
    
    ; The following event is not admissible in ACL2.
    
    (defthm ouch!
      nil
      :rule-classes nil
      :hints
      (("Goal" :use
        ((:functional-instance foo-id
                               (foo (lambda (x) (cons x x))))))))
    
    Under the simple approach we have in mind, bar is constrained to satisfy both its definition and bar-prop because bar mentions a function declared in the signature list of the encapsulation. In fact, bar is so-constrained in the ACL2 semantics of encapsulation and the first two events above (the encapsulate and the consequence that foo must be the identity function) are actually admissible. But under the simple approach to assigning constraints, foo is unconstrained because no theorem about it is exported. Under that approach, ouch! is provable because foo can be instantiated in foo-id to a function other than the identity function.

    It's tempting to think we can fix this by including definitions, not just theorems, in constraints. But consider the following slightly more elaborate example. The problem is that we need to include as a constraint on foo not only the definition of bar, which mentions foo explicitly, but also abc, which has foo as an ancestor.

    (encapsulate
     (((foo *) => *))
     (local (defun foo (x) x))
     (local (defthm foo-prop
              (equal (foo x) x)))
     (defun bar (x)
       (foo x))
     (defun abc (x)
       (bar x))
     (defthm abc-prop
       (equal (abc x) x)
       :rule-classes nil))
    
    (defthm foo-id
      (equal (foo x) x)
      :hints (("Goal" :use abc-prop)))
    
    ; The following event is not admissible in ACL2.
    
    (defthm ouch!
      nil
      :rule-classes nil
      :hints
      (("Goal" :use
        ((:functional-instance foo-id
                               (foo (lambda (x) (cons x x)))
                               (bar (lambda (x) (cons x x))))))))
    




    acl2-sources/doc/HTML/COPYRIGHT.html0000664002132200015000000000245612222333520016323 0ustar kaufmannacl2 COPYRIGHT.html -- ACL2 Version 6.3

    COPYRIGHT

    ACL2 copyright, license, sponsorship
    Major Section:  MISCELLANEOUS
    

    ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp Copyright (C) 2013, Regents of the University of Texas

    This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright (C) 1997 Computational Logic, Inc. See the documentation topic NOTE-2-0.

    This program is free software; you can redistribute it and/or modify it under the terms of the LICENSE file distributed with ACL2.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE for more details.

    Written by: Matt Kaufmann and J Strother Moore email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu Department of Computer Science University of Texas at Austin Austin, TX 78701 U.S.A.

    Please also see acknowledgments.




    acl2-sources/doc/HTML/COROLLARY.html0000664002132200015000000000126212222333520016313 0ustar kaufmannacl2 COROLLARY.html -- ACL2 Version 6.3

    COROLLARY

    the corollary formula of a rune
    Major Section:  MISCELLANEOUS
    

    This is a low-level system function at the present time. See pr and see pr! instead. Also see rule-classes for the use of the symbol :corollary in specifying a rule class.




    acl2-sources/doc/HTML/COUNT.html0000664002132200015000000000304412222333523015640 0ustar kaufmannacl2 COUNT.html -- ACL2 Version 6.3

    COUNT

    count the number of occurrences of an item in a string or true-list
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    (count #\D "DabcDefcDe")                 ; = 3
    (count #\D "DabcDefcDe" :start 1)        ; = 2
    (count #\D "DabcDefcDe" :start 1 :end 5) ; = 1
    (count #\D "DabcDefcDe" :start 1 :end 4) ; = 0
    (count #\z "DabcDefcDe")                 ; = 0
    (count '(a b) '(17 (a b) 23 (a b) (c d)))   ; = 2
    
    General Form:
    (count item sequence &key start end)
    

    (Count item sequence) returns the number of times item occurs in sequence. The guard for calls of count (which is actually a macro in ACL2) specifies that sequence is a string or a true-list, and that start, which defaults to 0, and end, which defaults to the length of sequence, are valid indices into sequence.

    See any Common Lisp documentation for more information about count, which is a Common Lisp utility. At this time ACL2 does not support keyword arguments for count other than :start and :end; we may add support for the :from-end keyword upon request.




    acl2-sources/doc/HTML/CPU-CORE-COUNT.html0000664002132200015000000000240112222333523017007 0ustar kaufmannacl2 CPU-CORE-COUNT.html -- ACL2 Version 6.3

    CPU-CORE-COUNT

    the number of cpu cores
    Major Section:  ACL2-BUILT-INS
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Unless the ACL2 executable supports parallel execution (see parallelism), this function returns (mv 1 state). Otherwise:

    (Cpu-core-count state) returns (mv core-count state), where core-count is the number of cpu cores if ACL2 can get that information from the underlying Common Lisp implementation. Otherwise an error occurs, unless global 'cpu-core-count is assigned to a positive integer value (see assign), in which case that value is returned as the core-count.

    Example:
    (cpu-core-count state) ==> (mv 4 state)
    
    .




    acl2-sources/doc/HTML/CURRENT-PACKAGE.html0000664002132200015000000000637112222333520017126 0ustar kaufmannacl2 CURRENT-PACKAGE.html -- ACL2 Version 6.3

    CURRENT-PACKAGE

    the package used for reading and printing
    Major Section:  MISCELLANEOUS
    

    Current-package is an ld special (see ld). The accessor is (current-package state) and the updater is (set-current-package val state), or more conventionally, (in-package val). The value of current-package is actually the string that names the package. (Common Lisp's ``package'' objects do not exist in ACL2.) The current package must be known to ACL2, i.e., it must be one of the initial packages or a package defined with defpkg by the user.

    When printing symbols, the package prefix is displayed if it is not the current-package and may be optionally displayed otherwise. Thus, if current-package is "ACL2" then the symbol 'ACL2::SYMB may be printed as SYMB or ACL2::SYMB, while 'MY-PKG::SYMB must be printed as MY-PKG::SYMB. But if current-package is "MY-PKG" then the former symbol must be printed as ACL2::SYMB while the latter may be printed as SYMB.

    In Common Lisp, current-package also affects how objects are read from character streams. Roughly speaking, read and print are inverses if the current-package is fixed, so reading from a stream produced by printing an object must produce an equal object.

    In ACL2, the situation is more complicated because we never read objects from character streams, we only read them from object ``streams'' (channels). Logically speaking, the objects in such a channel are fixed regardless of the setting of current-package. However, our host file systems do not support the idea of Lisp object files and instead only support character files. So when you open an object input channel to a given (character file) we must somehow convert it to a list of ACL2 objects. This is done by a deus ex machina (``a person or thing that appears or is introduced suddenly and unexpectedly and provides a contrived solution to an apparently insoluble difficulty,'' Webster's Ninth New Collegiate Dictionary). Roughly speaking, the deus ex machina determines what sequence of calls to read-object will occur in the future and what the current-package will be during each of those calls, and then produces a channel containing the sequence of objects produced by an analogous sequence of Common Lisp reads with *current-package* bound appropriately for each.

    A simple rule suffices to make sane file io possible: before you read an object from an object channel to a file created by printing to a character channel, make sure the current-package at read-time is the same as it was at print-time.




    acl2-sources/doc/HTML/CURRENT-THEORY.html0000664002132200015000000000502612222333531017103 0ustar kaufmannacl2 CURRENT-THEORY.html -- ACL2 Version 6.3

    CURRENT-THEORY

    currently enabled rules as of logical name
    Major Section:  THEORIES
    

    Examples:
    (current-theory :here)
    (current-theory 'lemma3)
    
    See logical-name.

    General Form:
    (current-theory logical-name)
    
    Returns the current theory as it existed immediately after the introduction of logical-name provided it is evaluated in an environment in which the variable symbol WORLD is bound to the current ACL2 logical world, (w state). Thus,
    ACL2 !>(current-theory :here)
    
    will cause an (unbound variable) error while
    ACL2 !>(let ((world (w state))) (current-theory :here))
    
    will return the current theory in world.

    See theories and see logical-name for a discussion of theories in general and why the commonly used ``theory functions'' such as current-theory are really macros that expand into terms involving the variable world.

    The theory returned by current-theory is in fact the theory selected by the in-theory event most recently preceding logical name, extended by the rules introduced up through logical-name.

    You may experience a fencepost problem in deciding which logical name to use. Deflabel can always be used to mark unambiguously for future reference a particular point in the development of your theory. The order of events in the vicinity of an encapsulate is confusing. See encapsulate.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/CUSTOM-KEYWORD-HINTS.html0000664002132200015000000000156512222333520017772 0ustar kaufmannacl2 CUSTOM-KEYWORD-HINTS.html -- ACL2 Version 6.3

    CUSTOM-KEYWORD-HINTS

    user-defined hints
    Major Section:  MISCELLANEOUS
    

    See add-custom-keyword-hint for a discussion of how advanced users can define their own hint keywords. For examples, see the community books directory books/hints/, in particular basic-tests.lisp.

    Some Related Topics




    acl2-sources/doc/HTML/CW-GSTACK.html0000664002132200015000000000517412222333522016240 0ustar kaufmannacl2 CW-GSTACK.html -- ACL2 Version 6.3

    CW-GSTACK

    debug a rewriting loop or stack overflow
    Major Section:  OTHER
    

    Example Forms:
    (cw-gstack)
    (cw-gstack :frames 10)       ; show only the top 10 frames
    (cw-gstack :frames '(1 10))  ; same as above:  show only frames 1 through 10
    (cw-gstack :frames '(10 20)) ; show only frames 10 through 20
    (cw-gstack :evisc-tuple (evisc-tuple 3 4 nil nil))
                                 ; print with print-level 3 and print-length 4
    (cw-gstack :evisc-tuple nil) ; print using default ``evisceration'',
                                 ;   essentially the same as just above
    (cw-gstack :evisc-tuple '(nil 3 4 (hide)))
                                 ; same as just above
    
    General Form:
    (cw-gstack :frames frames :evisc-tuple evisc-tuple)
    
    where :frames and :evisc-tuple are optional, but if they are supplied, their values are evaluated. The value of frames should be either a natural number or a list of two natural numbers, the first less than the second; and the value of evisc-tuple should be an evisc-tuple (see evisc-tuple). If :evisc-tuple is omitted, then substructures deeper than 3 are replaced by ``#'' and those longer than 4 are replaced by ``...'', and terms of the form (hide ...) are printed as <hidden>. Also see set-iprint for an alternative to printing ``#'' and ``...''.

    Stack overflows may occur, perhaps caused by looping rewrite rules. In some Lisps, stack overflows may manifest themselves as segmentation faults, causing the entire ACL2 image to crash. Finding looping rewrite rules can be tricky, especially if you are using books supplied by other people. (However, see set-rewrite-stack-limit for a way to avoid stack overflows caused by rewriter loops.)

    Normally, a stack overflow will cause the printing of an error message that suggests how to proceed. Just follow those instructions, and you will generally be able to see what is causing the loop.

    Suggestion: Once you have found the loop and fixed it, you should execute the ACL2 command :brr nil, so that you don't slow down subsequent proof attempts.




    acl2-sources/doc/HTML/CW.html0000664002132200015000000000773212222333523015271 0ustar kaufmannacl2 CW.html -- ACL2 Version 6.3

    CW

    print to the comment window
    Major Section:  ACL2-BUILT-INS
    

    Example:

    (cw "The goal is ~p0 and the alist is ~x1.~%"
        (untranslate term t nil)
        unify-subst)
    
    Logically, this expression is equivalent to nil. However, it has the effect of first printing to the so-called ``comment window'' the fmt string as indicated. Thus, cw is like fmt (see fmt) except in three important ways. First, it is a macro whose calls expand to calls of a :logic mode function. Second, it neither takes nor returns the ACL2 state; logically cw simply returns nil, although it prints to a comment window that just happens to share the terminal screen with the standard character output *standard-co*. Third, its fmt args are positional references, so that for example
    (cw "Answers: ~p0 and ~p1" ans1 ans2)
    
    prints in the same manner as:
    (fmt "Answers: ~p0 and ~p1"
         (list (cons #\0 ans1) (cons #\1 ans2))
         *standard-co* state nil)
    
    Typically, calls of cw are embedded in prog2$ forms, e.g.,
    (prog2$ (cw ...)
            (mv a b c))
    
    which has the side-effect of printing to the comment window and logically returning (mv a b c).

    General Form:
    (cw fmt-string arg1 arg2 ... argn)
    
    where n is between 0 and 9 (inclusive). The macro uses fmt-to-comment-window, passing it the column 0 and evisc-tuple nil, after assembling the appropriate alist binding the fmt vars #\0 through #\9; see fmt. If you want
    (a) more than 10 vars,
    (b) vars other than the digit chars,
    (c) a different column, or
    (d) a different evisc-tuple,
    
    then call fmt-to-comment-window instead.

    Also see cw!, which is useful if you want to be able to read the printed forms back in.

    Finally, we discuss another way to create formatted output that also avoids the need to pass in the ACL2 state. The idea is to use wormholes; see wormhole. Below is a function you can write, along with some calls, providing an illustration of this approach.

    (defun my-fmt-to-comment-window (str alist)
      (wormhole 'my-fmt-to-comment-window
                '(lambda (whs) whs)
                (list str alist)
                '(pprogn
                  (fms (car (@ wormhole-input))
                       (cadr (@ wormhole-input))
                       *standard-co*
                       state
                       nil)
                  (value :q))
                :ld-verbose nil
                :ld-error-action :return ; harmless return on error
                :ld-prompt nil))
    
    ; A non-erroneous call:
    (my-fmt-to-comment-window "Here is ~x0 for your inspection~%"
                              (list (cons #\0 'foo)))
    
    ; An error inside the fmt string (unbound fmt var); note that even
    ; with the error, the wormhole is exited.
    (my-fmt-to-comment-window "Here is ~x1 for your inspection~%"
                              (list (cons #\0 'foo)))
    
    ; A guard violation in the binding; note that even with the error,
    ; the wormhole is exited.
    (my-fmt-to-comment-window "Here is ~x0 for your inspection~%"
                              (list (cons #\0 (car 'foo))))
    




    acl2-sources/doc/HTML/CW_bang_.html0000664002132200015000000000131112222333523016402 0ustar kaufmannacl2 CW_bang_.html -- ACL2 Version 6.3

    CW!

    print to the comment window
    Major Section:  ACL2-BUILT-INS
    

    This is the same as cw, except that cw inserts backslash (\) characters when forced to print past the right margin, in order to make the output a bit clearer in that case. Use cw! instead if you want to be able to read the forms back in.




    acl2-sources/doc/HTML/Common_Lisp.html0000664002132200015000000000505212222333526017173 0ustar kaufmannacl2 Common_Lisp.html -- ACL2 Version 6.3

    Common Lisp

    The logic of ACL2 is based on Common Lisp.

    Common Lisp is the standard list processing programming language. It is documented in: Guy L. Steele, Common Lisp The Language, Digital Press, 12 Crosby Drive, Bedford, MA 01730, 1990. See also http://www.cs.cmu.edu/Web/Groups/AI/html/cltl/cltl2.html.

    ACL2 formalizes only a subset of Common Lisp. It includes such familiar Lisp functions as cons, car and cdr for creating and manipulating list structures, various arithmetic primitives such as +, *, expt and <=, and intern and symbol-name for creating and manipulating symbols. Control primitives include cond, case and if, as well as function call, including recursion. New functions are defined with defun and macros with defmacro. See programming for a list of the Common Lisp primitives supported by ACL2.

    ACL2 supports five of Common Lisp's datatypes:

    * the precisely represented, unbounded numbers (integers, rationals, and the complex numbers with rational components, called the ``complex rationals'' here),

    * the characters with ASCII codes between 0 and 255

    * strings of such characters

    * symbols (including packages)

    * conses

    ACL2 is a very small subset of full Common Lisp. ACL2 does not include the Common Lisp Object System (CLOS), higher order functions, circular structures, and other aspects of Common Lisp that are non-applicative. Roughly speaking, a language is applicative if it follows the rules of function application. For example, f(x) must be equal to f(x), which means, among other things, that the value of f must not be affected by ``global variables'' and the object x must not change over time.




    acl2-sources/doc/HTML/Common_Lisp_as_a_Modeling_Language.html0000664002132200015000000000334412222333526023601 0ustar kaufmannacl2 Common_Lisp_as_a_Modeling_Language.html -- ACL2 Version 6.3

    Common Lisp as a Modeling Language

    In ACL2 we have adopted Common Lisp as the basis of our modeling language. If you have already read our brief note on Common Lisp and recall the example of app, please proceed. Otherwise click here for an exceedingly brief introduction to Common Lisp and then come back here.

    In Common Lisp it is very easy to write systems of formulas that manipulate discrete, inductively constructed data objects. In building a model you might need to formalize the notion of sequences and define such operations as concatenation, length, whether one is a permutation of the other, etc. It is easy to do this in Common Lisp. Furthermore, if you have a Common Lisp ``theory of sequences'' you can run the operations and relations you define. That is, you can execute the functions on concrete data to see what results your formulas produce.

    If you define the function app as shown above and then type

    (app '(A B) '(C D E))
    
    in any Common Lisp, the answer will be computed and will be (A B C D E).

    The executable nature of Common Lisp and thus of ACL2 is very handy when producing models.

    But executability is not enough for a modeling language because the purpose of models is to permit analysis.

    Click here to continue.




    acl2-sources/doc/HTML/Conversion.html0000664002132200015000000000206212222333526017077 0ustar kaufmannacl2 Conversion.html -- ACL2 Version 6.3

    Conversion to Uppercase

    When symbols are read by Common Lisp they are converted to upper case. Note carefully that this remark applies to the characters in symbols. The characters in strings are not converted upper case.

    To type a symbol containing lower case characters you can enclose the symbol in vertical bars, as in |AbC| or you can put a ``backslash'' before each lower case character you wish to preserve, as in A\bC. |AbC| and A\bC are two different ways of writing the same symbol (just like 2/4 and 1/2 are two different ways of writing the same rational and 123 and 0123 are two different ways to write the same natural number). The symbol has three characters in its name, the middle one of which is a lower case b.




    acl2-sources/doc/HTML/Corroborating_Models.html0000664002132200015000000000435712222333526021100 0ustar kaufmannacl2 Corroborating_Models.html -- ACL2 Version 6.3

    Corroborating Models

    After producing a model, it must be corroborated against reality. The Falling Body Model has been corroborated by a vast number of experiments in which the time and distance were measured and compared according to the formula. In general all models must be corroborated by experiment.

    The Falling Body Model can be derived from deeper models, namely Newton's laws of motion and the assertion that, over the limited distances concerned, graviation exerts a constant acceleration on the object. When the model in question can be derived from other models, it is the other models that are being corroborated by our experiments.

    Because nature is not formal, we cannot prove that our models of it are correct. All we can do is test our models against nature's behavior.

    Such testing often exposes restrictions on the applicability of our models. For example, the Falling Body Model is inaccurate if air resistance is significant. Thus, we learn not to use that model to predict how long it takes a feather to fall from a 200 foot tower in the earth's atmosphere.

    In addition, attempts at corroboration might reveal that the model is actually incorrect. Careful measurements might expose the fact that the gravitational force increases as the body falls closer to earth. Very careful measurements might reveal relativistic effects. Technically, the familiar Falling Body Model is just wrong, even under excessive restrictions such as ``in a perfect vacuum'' and ``over small distances.'' But it is an incredibly useful model nonetheless.

    There are several morals here.

    Models need not be complete to be useful.

    Models need not be perfectly accurate to be useful.

    The user of a model must understand its limitations.




    acl2-sources/doc/HTML/DEAD-EVENTS.html0000664002132200015000000001125312222333522016447 0ustar kaufmannacl2 DEAD-EVENTS.html -- ACL2 Version 6.3

    DEAD-EVENTS

    using proof supporters to identify dead code and unused theorems
    Major Section:  OTHER
    

    Below, when we talk about ``an event A'', we mean an event whose name is A.

    When event A is used in a proof performed to admit event B that you submit to ACL2, we say that A is a ``proof-supporter'' of B. ACL2 stores an association list such that for every event B with at least one proof-supporter, B is associated with a list of all of its proof-supporters, sorted by symbol-<. The following form evaluates to that alist, which is called the ``proof-supporters-alist''.

    (global-val 'proof-supporters-alist (w state))
    
    By ``used in a proof'' above, we mean: applied as a rule or supplied explicitly via hints of type :use, :by, or :clause-processor. That is, the events ``used in a proof'' for admitting an event E are those listed in the summary printed at the conclusion of admitting E.

    Note that if proofs are skipped when admitting event E, say because the last admission of E was done by include-book (or certify-book, which ends with an include-book), then there will be no entry in that alist for E. (An exception is made however for encapsulate events, where proof-supporters are remembered from the first pass; see below.) So if you want the proof-supporters-alist to include supporters for events in a book, use ld rather than include-book or certify-book to process the events in that book. If however you are interested in the proof-supporters FROM a book that support a later event, then it is fine to include that book.

    The case for encapsulate is slightly tricky. Consider an example of the following form.

    A ; event preceding the encapsulate
    (encapsulate
     ()
     B
     (local C) ; uses A and B in a proof
     D ; uses C in a proof
     )
    
    At the conclusion of this encapsulate event, the proof-supporters-alist associates D with A and B, but not C (which has disappeared, since it is local).

    Note that this sort of ``transitive closure'' operation is only performed when necessary due to the disappearance of local events. For example, if we replace (local C) above by just C, then D is associated in the proof-supporters-alist only with C, not with A or B. If you want the transitive closure of the relation computed by the proof-supporters-alist, you have to compute it yourself. (This was a deliberate design decision, in order to avoid slowing down event processing.) However, there is help available on how to do such a computation:

    A community book, books/misc/dead-events.lisp, does such a transitive closure, and moreover uses that information to find ``dead events'' relative to a list of ``desired'' events. For example, suppose you use LD to process the events, with proofs, in a book intended to prove theorems MAIN-1 and MAIN-2. (Remember, certify-book will not save such information.) Suppose furthermore that the book begins with some include-book forms followed by (deflabel book-start). You could evaluate this form:

    (dead-events '(main-1 main-2) :start 'book-start)
    
    The result is a list of events that you probably can delete from the book without causing any proofs to fail. See the dead-events.lisp book for further documentation.

    You might also find the code in the above book to be helpful for writing your own utilities based on the proof-supporters-alist.




    acl2-sources/doc/HTML/DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS.html0000664002132200015000000002043012222333515023747 0ustar kaufmannacl2 DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS.html -- ACL2 Version 6.3

    DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS

    how to get rid of key combinations of function symbols
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Suppose REV reverses a list, MEMBER checks that its first argument is an element of its second, and SQUARES-PLUS-3P is some complicated predicate. Suppose you're proving some Main Theorem that involves those concepts and the theorem prover presents you with the following hideous formula as a key checkpoint. What action should you take?

    Hint: Don't read the formula ``for sense,'' i.e., don't try to understand what this formula is saying! Just look at every subterm involving a nest of two function symbols and ask if you know something about those two symbols that allows you to simplify that one subterm.

    (IMPLIES (AND (CONSP X)
                  (MEMBER (+ 3 (* I I)) (REV X))
                  (LIST-OF-INTEGERS X)
                  (INTEGERP I)
                  (<= 0 I)
                  (INTEGERP K)
                  (<= 0 K)
                  (< I K)
                  (SQUARES-PLUS-3P K X)
                  (NOT (EQUAL (CAR X) (+ 3 (* I I))))
                  (NOT (MEMBER (+ 3 (* I I)) X)))
             (SQUARES-PLUS-3P K (REV X)))?
    

    The experienced ACL2 user will stop reading at the second hypothesis!

                  (MEMBER (+ 3 (* I I)) (REV X))
    

    The combination of MEMBER and REV can be simplified. The question ``is e a member of (REV x)'' can be answered by asking ``is e a member of x''. The two questions are equivalent. This insight comes from your intuition about the semantics of REV -- it just reorders the elements but doesn't add or delete any. The second question is simpler since it doesn't mention REV, so this is a good transformation to make. And the theorem that they are equivalent is simpler than the key checkpoint above because it involves fewer functions and smaller expressions.

    You might formalize this insight as

    (equal (member e (rev x))
           (member e x))
    
    But this conjecture is not a theorem, because (member e x) returns the cdr of x that begins with e, not just a Boolean (t or nil) indicating whether e is an element of x. The location of the first e in (rev x) is generally different than the location in x. So when we say the two questions are ``equivalent'' we don't mean they are equal. We mean that they're propositionally equivalent: both nil or both non-nil. This sense of equivalence is called ``if and only if'' and is checked by the function iff.

    So our intuitive insight can be phrased as this theorem:

    (iff (member e (rev x))
         (member e x))
    

    Suggesting that this formulation of the insight is ``obvious'' begs many questions. Mathematically, we could have avoided iff and just written two implications:

    (and (implies (member e x) (member e (rev x)))
         (implies (member e (rev x)) (member e x))).
    
    or
    (and (implies (member e x) (member e (rev x)))
         (implies (not (member e x))  (not (member e (rev x))))).
    
    Or we could have used iff but ``oriented'' it the other way:
    (iff (member e x)
         (member e (rev x)))
    
    We choose to write
    (iff (member e (rev x))
         (member e x))
    
    because of our knowledge of how ACL2 turns formulas into rules!

    We deal with this at greater length later. But just to drive the point home, if we issue the command:

    (defthm member-rev
      (iff (member e (rev x))
           (member e x)))
    
    ACL2 will build in a rule that causes every propositional occurrence of (MEMBER e (REV x)) to be replaced by (MEMBER e x). (By ``propositional occurrence'' we mean an occurrence in which the value is tested, as by IF or the propositional connectives. Remember, one might use member to determine the location of an element too.)

    Note carefully: if you do not tell ACL2 how to make a rule from a theorem, it makes a rewrite rule. Rewrite rules always replace instances of the left-hand side by the corresponding instances of the right-hand side. That is, when interpreted as a rewrite rule, (iff alpha beta) makes ACL2 replace alpha by beta.

    Probably the biggest mistake new users make is forgetting that every theorem they prove creates a very specific rule. You must remember that you are programming ACL2 with these rules. Being careless in your statement of theorems is tantamount to being careless in your programming. What you get is a mess.

    Had we proved the same equivalence, but with the iff commuted, we would be giving ACL2 bad advice. We would be telling it ``replace instances of (MEMBER e x) by the corresponding instances of (MEMBER e (REV x))''! If ACL2 had that rule and ever tried to simplify any member expression, e.g., (MEMBER A B), it would get into an infinite loop, e.g., producing the following sequence of transformations:

    (MEMBER A B)
    (MEMBER A (REV B))
    (MEMBER A (REV (REV B)))
    ...
    
    until it eventually exhausted some resource.

    Recall that we entertained the idea of phrasing our insight about member and rev with implications rather than iff. Generally speaking, implications produce weaker rules -- rules that apply less often. We discuss that later.

    Now suppose we've proved member-rev, oriented so as to rewrite (member e (rev x)) to (member e x), and built it in as a rewrite rule. Then suppose we repeated the attempt to prove our Main Theorem. This time, when the prover is processing the hideous Key Checkpoint printed above, our new lemma, member-rev, will hit it. It will transform the formula to:

    (IMPLIES (AND (CONSP X)
                  (MEMBER (+ 3 (* I I)) X)   ; <-- the hyp has simplified
                  (LIST-OF-INTEGERS X)
                  (INTEGERP I)
                  (<= 0 I)
                  (INTEGERP K)
                  (<= 0 K)
                  (< I K)
                  (SQUARES-PLUS-3P K X)
                  (NOT (EQUAL (CAR X) (+ 3 (* I I))))
                  (NOT (MEMBER (+ 3 (* I I)) X)))
             (SQUARES-PLUS-3P K (REV X)))?
    

    and then that will collapse to T, since the IMPLIES has contradictory hypotheses (note the last hypothesis above).

    By proving member-rev we proved the hideous checkpoint. We never had to look at the rest of the formula or think about why it is a theorem. Furthermore, attacking the main theorem again, from scratch, with member-rev in the database, may eliminate other checkpoints that came up the last time we tried to prove our main goal. So we recommend addressing one checkpoint at a time.

    This example illustrates that purely local thinking -- looking for simplifiable combinations of function symbols -- can sometimes lead to proofs and should always be your first reaction to a key checkpoint: what local fact do you know that would clean up the formula? Don't think about deep questions like ``why is this true?'' until you can't see any way to make it simpler.

    It is important to train yourself to see combinations of function symbols and to create strong rules for eliminating them. We will give you opportunities to practice this later in the tutorial.

    If you have been reading the tutorial introduction to the theorem prover, use your browser's Back Button now to return to INTRODUCTION-TO-KEY-CHECKPOINTS.




    acl2-sources/doc/HTML/DEALING-WITH-TAU-PROBLEMS.html0000664002132200015000000002013512222333520020431 0ustar kaufmannacl2 DEALING-WITH-TAU-PROBLEMS.html -- ACL2 Version 6.3

    DEALING-WITH-TAU-PROBLEMS

    some advice on dealing with problems caused by the tau system
    Major Section:  INTRODUCTION-TO-THE-TAU-SYSTEM
    

    For background on the tau system, see introduction-to-the-tau-system. The two most common problems caused by the tau system have to do with the system's interaction with ``legacy'' proof scripts. Such scripts may suffer because they were not designed to exploit tau reasoning and which may configure the tau database in quite incomplete and arbitrary ways. The two most common problems we have seen are (a) significant slow downs in a few proofs and (b) failed proof attempts due to hints being misapplied because the tau system caused subgoals to be renumbered.

    We discuss the rather limited means of dealing with these problems here. In future-work-related-to-the-tau-system we list some major inadequacies of the tau system.

    If the tau system contributes to a proof, the rune (:executable-counterpart tau-system) will be listed among the Rules in the Summary. However, merely by being attempted the tau system can slow down proofs in which it makes no contribution.

    The most brutal and fool-proof way to isolate a proof from the tau system is to disable the entire system. This can be done globally by

    (in-theory (disable (tau-system)))  ; (:executable-counterpart tau-system)
    
    or locally with a subgoal specific hint:
    ...
    :hints (("...subgoal id..." :in-theory (disable (tau-system))))
    
    Conducting a proof with and without the participation of the tau system can help you determine whether tau reasoning is helping or hurting.

    Dealing with Slowdowns

    The time-tracker utility was added to allow users to investigate whether excessive amounts of time are being spent in a given function. It was then used to annotate the code for the tau system as described in time-tracker-tau. The result is that if ``excessive'' time is spent in tau reasoning, messages to that effect will be printed to the proof log. The question is: aside from disabling the tau system how can the proof be sped up?

    There are two common causes of slowdown in the tau system. The first stems from the system's use of :executable-counterparts to determine whether a constant has a given tau. Recall that a tau is a conjunction of monadic predicates. To determine whether some constant satisfies the tau, the predicates are executed. If you have a hard-to-compute predicate this can be very slow. The most typical such predicates in ACL2 applications are those that check invariants, e.g., that recognize ``good states'' or ``well-formed data.'' These are often written inefficiently because they are intended only for used in theorems and, before the tau system was added, they may have never been applied to constants. The most common constants tau predicates are applied to are 0, T, and NIL, although different models may stress other constants. To understand why NIL for example is frequently tested, if the test of an IF-expression is computed to have tau s then the next question we ask is ``does nil satisfy s?''

    You may determine whether the tau system is spending time executing tau predicates by observing the rewriter -- see dmr -- or by interrupting the system and getting a backtrace (see set-debugger-enable).

    If excessive time is being spent in a tau predicate, a draconian solution is to disable the :executable-counterpart of that predicate, for example in either of these equivalent ways. The tau system does not execute disabled :executable-counterparts.

    (in-theory (disable (:executable-counterpart foo)))
    (in-theory (disable (foo)))
    
    In either case above, you may prefer to provide local :in-theory :hints rather than :in-theory events.

    Disabling the executable counterpart of expensive tau predicates will weaken the tau system, probably only negligibly, because it can no longer run the predicates to determine whether they admits given constants.

    A more sophisticated solution is to make the tau system record values of the :logic-mode function in question, so that the system will look up the necessary values rather than running the function every time the question arises. It will look up recorded values whether the executable counterpart of the tau predicate is enabled or disabled. Here is an example of a lemma that can provide such a solution. See the discussion of the Eval form of :tau-system rules.

    (defthm lemma
      (and (foo 0)
           (foo 17)
           (foo t)
           (not (foo '(a b c))))
      :rule-classes :tau-system)
    

    It might be difficult to determine which constants are being repeatedly tested, although tracing (trace$) suspected tau predicates will show what they are being called on.

    At the moment there are no better user-level tools to discover this. However, some users may wish to consider the following hack: In the ACL2 source file tau.lisp, immediately after the definition of the system function ev-fncall-w-tau-recog, there is a comment which contains some raw Lisp code that can be used to investigate whether tau's use of evaluation on constants is causing a problem and to determine which constants are involved.

    The second main cause of slowdowns by the tau system is that the system contains ``too many'' conjunctive rules (see the Conjunctive form in tau-system). Unfortunately, we have no tools for either identifying the problem or addressing it! That said, let us tell you what we do know!

    Conjunctive rules are used to ``complete'' each tau as it is built. Referring to the weekdayp example in tau-system, if a tau is constructed that recognizes weekdays but not MON, TUE, THU, or FRI, it is completed by adding that the tau recognizes (only) WED. This means that when we construct a tau we scan all known conjunctive rules and see whether all but one of the literals of any conjunctive rule are present. This can be expensive. To mitigate this expense, the tau system caches the computation on a per proof basis (the cache is cleared after every proof).

    To learn what conjunctive rules there are in your system, evaluate

    (assoc 'tau-conjunctive-rules (tau-database (w state)))
    
    Perhaps by sending the implementors that list, we can think of ways to index the conjunctive rules to save time.

    Dealing with Misapplied Hints

    The second common problem caused by the tau system in legacy proof scripts is that it can cause subgoals to be renumbered and thus cause hints to be missed. The only ways to address this problem is either to disable the tau system (locally or globally by disabling (:executable-counterpart tau-system)) or change the legacy hints to use the new subgoal names.




    acl2-sources/doc/HTML/DECLARE-STOBJS.html0000664002132200015000000000522112222333530017006 0ustar kaufmannacl2 DECLARE-STOBJS.html -- ACL2 Version 6.3

    DECLARE-STOBJS

    declaring a formal parameter name to be a single-threaded object
    Major Section:  STOBJ
    

    When a defun uses one of its formals as a single-threaded object (stobj), the defun must include a declaration that the formal is to be so used. An exception is the formal ``state,'' which if not declared as explained below, may still be used provided an appropriate global ``declaration'' is issued: see set-state-ok.

    If the formal in question is counters then an appropriate declaration is

    (declare (xargs :stobjs counters))
    
    or, more generally,
    (declare (xargs :stobjs (... counters ...)))
    
    where all the single-threaded formals are listed.

    For such a declaration to be legal it must be the case that all the names have previously been defined as single-threaded objects with defstobj.

    When an argument is declared to be single-threaded the guard of the function is augmented by conjoining to it the condition that the argument satisfy the recognizer for the single-threaded object. Furthermore, the syntactic checks done to enforce the legal use of single-threaded objects are also sufficient to allow these guard conjuncts to be automatically proved.

    The obvious question arises: Why does ACL2 insist that you declare stobj names before using them in defuns if you can only declare names that have already been defined with defstobj? What would go wrong if a formal were treated as a single-threaded object if and only if it had already been so defined?

    Suppose that one user, say Jones, creates a book in which counters is defined as a single-threaded object. Suppose another user, Smith, creates a book in which counters is used as an ordinary formal parameter. Finally, suppose a third user, Brown, wishes to use both books. If Brown includes Jones' book first and then Smith's, then Smith's function treats counters as single-threaded. But if Brown includes Smith's book first, the argument is treated as ordinary.

    ACL2 insists on the declaration to ensure that the definition is processed the same way no matter what the context.




    acl2-sources/doc/HTML/DECLARE.html0000664002132200015000000000553412222333525016017 0ustar kaufmannacl2 DECLARE.html -- ACL2 Version 6.3

    DECLARE

    declarations
    Major Section:  PROGRAMMING
    

    Examples:
    (declare (ignore x y z))
    (declare (ignorable x y z)
             (type integer i j k)
             (type (satisfies integerp) m1 m2))
    (declare (xargs :guard (and (integerp i)
                                (<= 0 i))
                    :guard-hints (("Goal" :use (:instance lemma3
                                                  (x (+ i j)))))))
    
    
    

    Some Related Topics

    • TYPE-SPEC -- type specifiers in declarations

    General Form: (declare d1 ... dn) where, in ACL2, each di is of one of the following forms: (ignore v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment. These variables must not occur free in the scope of the declaration. (ignorable v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment. These variables need not occur free in the scope of the declaration. This declaration can be useful for inhibiting compiler warnings. (type type-spec v1 ... vn) -- where each vi is a variable introduced in the immediately superior lexical environment and type-spec is a type specifier (as described in the documentation for type-spec). (xargs :key1 val1 ... :keyn valn) -- where the legal values of the keyi's and their respective vali's are described in the documentation for xargs. Xargs declarations are only allowed at the top level of definitions (defun and defmacro, as shown below). (optimize ...) -- for example, (optimize (safety 3)). This is allowed only at the top level of defun forms. See any Common Lisp documentation for more information.
    Declarations in ACL2 may occur only where dcl occurs below:
      (DEFUN name args doc-string dcl ... dcl body)
      (DEFMACRO name args doc-string dcl ... dcl body)
      (LET ((v1 t1) ...) dcl ... dcl body)
      (MV-LET (v1 ...) term dcl ... dcl body)
      (FLET ((name args dcl ... dcl body)
             ...))
    
    Of course, if a form macroexpands into one of these (as let* expands into nested lets and our er-let* expands into nested mv-lets) then declarations are permitted as handled by the macros involved.

    Declare is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/DEFABBREV.html0000664002132200015000000001035312222333516016233 0ustar kaufmannacl2 DEFABBREV.html -- ACL2 Version 6.3

    DEFABBREV

    a convenient form of macro definition for simple expansions
    Major Section:  EVENTS
    

    Examples:
    (defabbrev snoc (x y) (append y (list x)))
    (defabbrev sq (x) (declare (type (signed-byte 8) x)) (* x x))
    
    General Form:
    (defabbrev name (v1 ... vn) doc-string decl1 ... declk body)
    
    where name is a new function symbol, the vi are distinct variable symbols, and body is a term. The decli, if supplied, should be legal declare forms; see declare. Doc-string is an optional documentation string; see doc-string.

    Roughly speaking, the defabbrev event is akin to defining f so that (f v1 ... vn) = body. But rather than do this by adding a new axiom, defabbrev defines f to be a macro so that (f a1 ... an) expands to body, with the ``formals,'' vi, replaced by the ``actuals,'' ai.

    For example, if snoc is defined as shown in the first example above, then (snoc (+ i j) temp) is just an abbreviation for

    (append temp (list (+ i j))).
    

    In order to generate efficiently executable Lisp code, the macro that defabbrev introduces uses a let to bind the ``formals'' to the ``actuals.'' Consider the second example above. Logically speaking, (sq (ack i j)) is an abbreviation for (* (ack i j) (ack i j)). But in fact the macro for sq introduced by defabbrev actually arranges for (sq (ack i j)) to expand to:

    (let ((x (ack i j)))
      (* x x))
    
    which executes more efficiently than (* (ack i j) (ack i j)).

    In the theorem prover, the let above expands to

    ((lambda (x) (* x x)) (ack i j))
    
    and thence to (* (ack i j) (ack i j)).

    It is important to note that the term in body should not contain a call of name -- i.e., defabbrev should not be used in place of defun when the function is recursive. ACL2 will not complain when the defabbrev form is processed, but instead ACL2 will more than likely go into an infinite loop during macroexpansion of any form that has a call of name.

    It is also important to note that the parameters of any call of a macro defined by defabbrev will, as is the case for the parameters of a function call, be evaluated before the body is evaluated, since this is the evaluation order of let. This may lead to some errors or unexpected inefficiencies during evaluation if the body contains any conditionally evaluted forms like cond, case, or if. Consider the following example.

    (defabbrev foo (x y)
      (if (test x) (bar y) nil))
    
    Notice a typical one-step expansion of a call of foo (see trans1):
    ACL2 !>:trans1 (foo expr1 expr2)
     (LET ((X EXPR1) (Y EXPR2))
          (IF (TEST X) (BAR Y) NIL))
    ACL2 !>
    
    Now imagine that expr2 is a complicated expression whose evaluation is intended only when the predicate test holds of expr1. The expansion above suggests that expr2 will always be evaluated by the call (foo expr1 expr2), which may be inefficient (since perhaps we only need that value when test is true of expr1). The evaluation of expr2 may even cause an error, for example in :program mode if the expression expr2 has been constructed in a manner that could cause a guard violation unless test holds of expr1.




    acl2-sources/doc/HTML/DEFABSSTOBJ-MISSING-EVENTS.html0000664002132200015000000000362412222333516020615 0ustar kaufmannacl2 DEFABSSTOBJ-MISSING-EVENTS.html -- ACL2 Version 6.3

    DEFABSSTOBJ-MISSING-EVENTS

    obtain the events needed to admit a defabsstobj event
    Major Section:  EVENTS
    

    We assume familiarity with defabsstobj. Defabsstobj-missing-events is a macro is for advanced users (who, for example, understand the role of the translate and untranslate functions), who want programmatic access to the defthm events required to admit a specific defabsstobj event.

    This macro has the same syntax as defabsstobj -- to use it, just replace a call of defabsstobj by a call of defabsstobj-missing-events on the same arguments. The result is an error triple (mv erp val state). If erp is nil, then val is the list of all objects (name formula . old-formula), where a defthm event named name remains to be admitted whose translated formula is formula, and where old-formula is nil unless the indicated event already exists (hence with a different formula), in which case old-formula is the existing translated formula.

    To build a defthm event from the above value, val, we suggest evaluating a form like (untranslate formula t (w state)).




    acl2-sources/doc/HTML/DEFABSSTOBJ.html0000664002132200015000000010564012222333516016505 0ustar kaufmannacl2 DEFABSSTOBJ.html -- ACL2 Version 6.3

    DEFABSSTOBJ

    define a new abstract single-threaded object
    Major Section:  EVENTS
    

    We assume familiarity with single-threaded objects; see stobj and see defstobj. The event defabsstobj defines a so-called ``abstract stobj'', a notion we introduce briefly now and then explain in more depth below.

    The evaluation of a defstobj event produces logical definitions for several functions: a recognizer, which characterizes the stobj in terms of lists; a creator, which produces an initial suitable list structure; and field accessors and updators, defined in terms of nth and update-nth. Defabsstobj provides a way to define alternate definitions for ``stobj primitives'' for a corresponding single-threaded object. These stobj primitives include a recognizer, a creator, and other ``exported'' functions. In essence, defabsstobj establishes interface functions, or ``exports'', on a new stobj that is a copy of an indicated ``concrete'' stobj that already exists.

    We begin below with an introduction to abstract stobjs. We then explain the defabsstobj event by way of an example. We conclude by giving summary documentation for the defabsstobj event.

    For another introduction to abstract stobjs, see the paper ``Abstract Stobjs and Their Application to ISA Modeling'' by Shilpi Goel, Warren A. Hunt, Jr., and Matt Kaufmann, in the proceedings of ACL2 Workshop 2013, http://www.cs.uwyo.edu/~ruben/acl2-13.

    INTRODUCTION

    We start with a brief review of stobjs and some potential problems with them, followed by an introduction to abstract stobjs and how they can avoid these problems. Prior experience with stobjs will probably help the reader to absorb the ideas below.

    Recall that single-threaded objects, or stobjs, provide a way for ACL2 users to stay within the ACL2 logic, where every data object is an atom or a cons of data objects, while obtaining the benefits of fast evaluation through destructive updates. Consider for example this very simple event.

    (defstobj st fld)
    
    This event introduces a recognizer, stp, and a creator, create-st, for a data structure consisting of a single field accessed and updated by functions fld and update-fld, respectively. Each of these four primitive functions has both a logical definition, which is used when the prover reasons about the function, and an executable definition, which is used in raw Lisp. In the logic, stp recognizes objects that have the requisite fields. In raw Lisp, there is a ``live stobj'', which is an array object whose fields correspond to those specified by the defstobj event, implemented as Lisp arrays.

    Here are the logical definition and the executable definition, respectively, that are introduced for the field accessor, fld, introduced above. Notice that since a stobj is represented in raw Lisp using an array, the raw Lisp accessor uses a raw Lisp array accessor, svref. (You can see all the logical and executable definitions by evaluating the form (trace$ defstobj-axiomatic-defs defstobj-raw-defs) before evaluating the defstobj form.)

    ; logical definition
    (defun fld (st)
      (declare (xargs :guard (stp st)
                      :verify-guards t))
      (nth 0 st))
    
    ; executable (raw Lisp) definition
    (defun fld (st)
      (svref st 0))
    

    Sophisticated programming with stobjs can provide efficient implementations of algorithms, but may require the preservation of a complex invariant. One can, of course, define a function to implement such an invariant after introducing the stobj, as follows.

    ; Introduce a stobj.
    (defstobj st fld1 ... fldk)
    
    ; Define an invariant on that stobj.
    (defun good-stp (st)
      (declare (xargs :stobjs st))
      ...)
    
    ; Define some basic functions that update the stobj and preserve the
    ; invariant.
    (defun update-st (... st ...)
      (declare (xargs :stobjs st
                      :guard (and (good-stp st) ...)))
      ...)
    ...
    
    ; Prove that the invariant is indeed preserved by those basic functions.
    (defthm good-stp-update-st
      (implies (and (good-stp st)
                    ...)
               (good-stp (update-st ... st ...))))
    ...
    
    ; Implement algorithms built on the basic functions.
    (defun foo (... st ...)
      (declare (xargs :stobjs st
                      :guard (and (good-stp st) ...)))
      ... (update-st ... st ...) ...)
    
    ; Prove invariance theorems about these algorithms.
    (defthm good-stp-foo
      (implies (and (good-stp st)
                    ...)
               (good-stp (foo ... st ...))))
    ...
    
    ; Prove other properties of these algorithms.
    (defthm foo-is-correct
      (implies (and (good-stp st)
                    ...)
               (some-property (foo ... st ...))))
    ...
    

    But there are at least two potential difficulties in using stobjs as described above.

    1. When foo is executed on concrete data in the ACL2 loop, the guard check may be expensive because (good-stp st) is expensive.

    2. Reasoning about foo (using rules like foo-is-correct above) involves proving hypotheses of invariance theorems, which may be complicated for the user to manage or slow for the theorem prover.

    The defabsstobj event offers an opportunity to address these issues. It introduces a new stobj, which we call an ``abstract stobj'', which is associated with a corresponding ``concrete stobj'' introduced by an earlier defstobj event. The defabsstobj event specifies a logical (:LOGIC) and an executable (:EXEC) definition for each primitive operation, or ``stobj primitive'', involving that stobj. As is the case for defstobj, the logical definition is what ACL2 reasons about, and is appropriate to apply to an ACL2 object satisfying the logical definition of the recognizer function for the stobj. The executable definition is applied in raw Lisp to a live stobj, which is an array object associated with the given stobj name.

    We can picture a sequence of updates to corresponding abstract and concrete stobjs as follows. Initially in this picture, st$a0 and st$c0 are a corresponding abstract and concrete stobj (respectively). Then an update, u1, is applied with :LOGIC and :EXEC functions u$a1 and u$c1, respectively. The resulting abstract and concrete stobj, st$a1 and st$c1, correspond as before. Then a second update, u2, is applied with :LOGIC and :EXEC functions u$a2 and u$c2, respectively -- again preserving the correspondence. And so on.

    Abstract               u$a1       u$a2       u$a3
    (:logic)         st$a0  --> st$a1  --> st$a2  -->   ...
    
                       ^          ^          ^               ^
    Correspondence     |          |          |          ...  |
                       v          v          v               v
    
                           u$c1       u$c2       u$c3
    Concrete         st$c0  --> st$c1  --> st$c2  -->   ...
    (:exec)
    

    We conclude this introduction with some remarks about implementation. Consider an abstract stobj st with corresponding concrete stobj st$c. The live stobjs for st and st$c have the same structure, but are distinct arrays. Indeed, the raw Lisp creator function for st$c is called to create a new initial live stobj for st. As we will see below, reads and writes in raw Lisp to the live stobj for st are ultimately performed using the primitive accessors and updaters defined for st$c. One might think of the live stobjs for st and st$c as being congruent stobjs (see defstobj), except that the stobjs themselves are not congruent: the stobj primitives introduced for st may be applied to st but not arbitrary field updaters of st$c, for example. As one might expect, the :EXEC function for an exported function is applied to the live stobj for st in raw Lisp.

    EXAMPLE

    We present examples, with detailed comments intended to explain abstract stobjs, in two community books: books/misc/defabsstobj-example-1.lisp and books/misc/defabsstobj-example-2.lisp. In this section we outline the first of these. We suggest that after you finish this documentation topic, you read through those two books.

    Here is the first of two closely related defabsstobj events from the book defabsstobj-example-1.lisp, but in expanded form. We will show the abbreviated form later, which omits most of data in the form that is immediately below. Thus most of the information shown here is default information. We believe that the comments below explain most or all of what you need to know in order to start using defabsstobj, and that you will learn the remainder when you see error messages. For example, we do not say in the comments below that every :LOGIC and :EXEC function must be guard-verified, but that is indeed a requirement.

    (defabsstobj st ; The new abstract stobj is named st.
    
    ; The concrete stobj corresponding to st is st$c:
    
      :concrete st$c
    
    ; The recognizer for the new abstract stobj is stp, which is defined to be
    ; st$ap in the logic, and is executed on the live stobj in raw Lisp using
    ; st$cp.
    
      :recognizer (stp :logic st$ap :exec st$cp)
    
    ; The initial stobj is defined as create-st (a function of no arguments),
    ; which is defined logically as create-st$a, though create-st$c is invoked to
    ; create the initial live stobj for st.  The :correspondence and :preserved
    ; keywords refer to proof obligations, discussed below.
    
      :creator (create-st :logic create-st$a :exec create-st$c
                          :correspondence create-st{correspondence}
                          :preserved create-st{preserved})
    
    ; Proof obligations are generated that involve a correspondence between the
    ; new abstract stobj and corresponding concrete stobj.  The function
    ; st$corr, which need not be executable (see :DOC defun-nx), takes two
    ; arguments, a concrete stobj and an abstract stobj.  This function symbol is
    ; used in the statements of the proof obligations.
    
      :corr-fn st$corr
    
    ; In this example we have four exports.  In each case a new function is
    ; introduced that has the same signature as its :EXEC function, except that
    ; st$c is replaced by st.  The :LOGIC and :EXEC functions are as specified,
    ; and the other keywords refer to proof obligations that we discuss below.
    
      :exports ((lookup :logic lookup$a
                        :exec mem$ci
                        :correspondence lookup{correspondence}
                        :guard-thm lookup{guard-thm})
                (update :logic update$a
                        :exec update-mem$ci
                        :correspondence update{correspondence}
                        :preserved update{preserved}
                        :guard-thm update{guard-thm})
                (misc :logic misc$a
                      :exec misc$c
                      :correspondence misc{correspondence})
                (update-misc :logic update-misc$a
                             :exec update-misc$c
                             :correspondence update-misc{correspondence}
                             :preserved update-misc{preserved}))
      :doc nil)
    

    Note that all stobj primitives (recognizer, creator, and exported functions) are defined in the ACL2 loop in terms of their :LOGIC functions and in raw Lisp in terms of their :EXEC functions. In the ACL2 loop, a defun form defines a function, while in raw Lisp, a defmacro form defines a macro (for efficiency). We first illustrate how that works for the recognizer. (You can see all the logical and executable definitions by evaluating the form (trace$ defabsstobj-axiomatic-defs defabsstobj-raw-defs) before evaluating the defstobj form.)

    ; In the ACL2 loop:
    (defun stp (st)
      (declare (xargs :guard 't))
      (st$ap st))
    
    ; In raw Lisp:
    (defmacro stp (&rest args) (cons 'st$cp args))
    

    The definitions are made similarly for exported functions, with guards derived from their :LOGIC functions as follows. Consider the exported function update in our example. Its :LOGIC function, update$a, has formals (k val st$a) and the following guard.

    (and (and (integerp k) (<= 0 k) (<= k 49))
         (and (integerp val) (<= 0 val))
         (st$ap st$a)
         (mem$c-entryp val))
    
    The formals of update are obtained by starting with the formals of its :EXEC function, update-mem$ci -- which are (i v st$c) -- and replacing the concrete stobj name st$c by the new stobj name st. The formals of update are thus (i v st). The guard for update is obtained in two steps. The first step is to substitute the formals of update for the formals of update$a in the guard for update$a, to obtain the following.
    (and (and (integerp i) (<= 0 i) (<= i 49))
         (and (integerp v) (<= 0 v))
         (st$ap st)
         (mem$c-entryp v))
    
    The second step is to replace, for each new stobj primitive p, the :LOGIC function for p by p itself. The only :LOGIC function occurring in the formula just above is st$ap, which is the :LOGIC funcction for stp. The guard for update is thus as follows.
    (and (and (integerp i) (<= 0 i) (<= i 49))
         (and (integerp v) (<= 0 v))
         (stp st)
         (mem$c-entryp v))
    

    We turn now to the proof obligations, as promised above. There are three types: :CORRESPONDENCE, :PRESERVED, and :GUARD-THM. All required lemmas may be printed simply by defining the necessary :LOGIC and :EXEC functions and then submitting the defabsstobj event. (To advanced users: also see defabsstobj-missing-events for a utility that returns the required formulas in translated form.) Although the defabsstobj event will fail if the required lemmas have not been proved, first it will print the defthm forms that must be admitted in order to complete submission of the defabsstobj event.

    The detailed theory explaining the need for these lemmas may be found in a comment in ACL2 source file other-events.lisp, in a comment entitled ``Essay on the Correctness of Abstract Stobjs''. Here, we give an informal sense of the importance of these lemmas as we present examples of them. Fundamental is the notion of evaluation in the logic versus evaluation using live stobjs, where one imagines tracking the current value of each abstract stobj during each of these two evaluations.

    We start with the :CORRESPONDENCE lemmas. These guarantee that evaluation in the logic agrees with evaluation using live stobjs, in the sense that the only difference is between a logical stobj and a live stobj, where the two correspond in the sense of the function specified by :CORR-FN. We start with the :CREATOR function where the statement is quite simple, stating that the :CORR-FN holds initially.

    (defthm create-st{correspondence}
      (st$corr (create-st$c) (create-st$a)))
    
    For the exported functions, there are essentially two cases. If an exported function returns other than the new abstract stobj, then the theorem asserts the equality of the results of applying the :LOGIC and :EXEC functions for the exported function. Hypotheses include the :CORR-FN correspondence followed by the guard for the :LOGIC function, which is stated in terms of the formal parameters of the :EXEC function except using the abstract stobj (here, st) in place of the concrete stobj (here, st$c). The conclusion uses the :EXEC formals, modified in the call of the :LOGIC function (here, lookup$a) to use the abstract stobj, as in the hypotheses.
    (defthm lookup{correspondence}
      (implies (and (st$corr st$c st)
                    (integerp i) (<= 0 i) (<= i 49)
                    (st$ap st))
               (equal (mem$ci i st$c)
                      (lookup$a i st)))
      :rule-classes nil)
    
    By contrast, if the exported function returns the new abstract stobj, then the conclusion uses the correspondence function insted of EQUAL, as in the following.
    (defthm update{correspondence}
      (implies (and (st$corr st$c st)
                    (integerp i) (<= 0 i) (<= i 49)
                    (integerp v) (<= 0 v)
                    (st$ap st)
                    (mem$c-entryp v))
               (st$corr (update-mem$ci i v st$c)
                        (update$a i v st)))
      :rule-classes nil)
    
    For exported functions that return multiple values, such conclusions are conjoined together over the returned values.

    The :PRESERVED lemmas guarantee that updates to the abstract stobj preserve its recognizer. The fact that every exported function has this property provides justification for an optimization performed by ACL2 during generation of proof obligations for guard verification, by assuming that the recognizer always holds. The :PRESERVED lemma for the :CREATOR shows that the recognizer holds initially.

    (defthm create-st{preserved}
      (st$ap (create-st$a)))
    
    Here is a typical such lemma, for the exported function update. Note that there is no such lemma for lookup, since lookup does not return st.
    (defthm update{preserved}
      (implies (and (integerp i) (<= 0 i) (<= i 49)
                    (integerp v) (<= 0 v)
                    (st$ap st)
                    (mem$c-entryp v))
               (st$ap (update$a i v st))))
    

    Finally, we consider the :GUARD-THM lemmas. These serve to guarantee that the guard holds for each call of an :EXEC function. During guard verification, logical definitions are used; in particular, since each exported function is defined in the logic as the corresponding call of its :LOGIC function, guard verification shows that each call of the :LOGIC function for an exported function satisfies that function's guard. But why is this true for raw Lisp evaluation using live stobjs, where the :EXEC function is called for an exported function? The :GUARD-THM lemmas provide the answer, as they state that if the :LOGIC function's guard holds, then the :EXEC function's guard holds. Here is an example. Note that the hypotheses come from the correspondence of the concrete and abstract function as guaranteed by the :CORR function, together with the guard of the :LOGIC function; and the conclusion comes from the guard of the :EXEC function.

    (defthm lookup{guard-thm}
      (implies (and (st$corr st$c c)
                    (integerp i)
                    (<= 0 i)
                    (<= i 49)
                    (st$ap st))
               (and (integerp i)
                    (<= 0 i)
                    (< i (mem$c-length st$c))))
      :rule-classes nil)
    

    We conclude this EXAMPLE section by showing a short form for the defabsstobj form displayed above.

    (defabsstobj st
      :exports ((lookup :exec mem$ci)
                (update :exec update-mem$ci)
                misc update-misc))
    

    SUMMARY DOCUMENTATION

    The General Form is as shown below, where the order of keywords is unimportant. Duplicate keywords are discouraged; while permitted, only the first (leftmost) occurrence of a given keyword is used. Only the :exports keyword is required.

    (defabsstobj st
      :concrete concrete
      :recognizer recognizer
      :creator creator
      :corr-fn corr-fn
      :congruent-to congruent-to
      :protect-default protect-default
      :exports (e1 ... ek)
      :doc doc)
    
    The keyword argument :EXPORTS must be supplied, and missing or nil keyword arguments have defaults as indicated below. All arguments must satisfy the conditions below.

    Before we describe the arguments, we define a notion of a ``function spec'' and its ``completion''. A function spec is either a symbol or else a list of the form

    (fn :kwd1 val1 ... :kwdn valn),
    
    that is, a symbol followed by a keyword-value-listp. We view the case of a symbol, s, as the function spec (s), with no keywords. There must be no duplicate keywords. In each case that we expect a function spec, the context provides a set of valid keywords for that function spec; it is an error to provide any other keyword in the function spec. Each function spec is interpreted as its ``completion'', obtained by extending the function spec with a default value for each valid keyword as indicated below. With that interpretation, the ``exported function'' of a function spec is its car, and that function symbol and each keyword value must be a guard-verified function symbol; and moreover, the :EXEC function must not include the new abstract stobj name, st, among its formals.

    We are ready to describe the arguments of defabsstobj.

    St is a symbol, which names the new abstract stobj.

    Concrete is the name of an existing stobj that is not an abstract stobj, i.e., was introduced with defstobj (not defabsstobj).

    Recognizer is a function spec (for the recognizer function). The valid keywords are :LOGIC and :EXEC. The default for recognizer is obtained by adding the suffix "P" to name. The default value for :LOGIC is formed by adding the suffix "$AP" to recognizer; for :EXEC, by adding the suffix "$CP". The :EXEC function must be the recognizer for the specified :CONCRETE stobj.

    Creator is a function spec (for the creator function). The valid keywords are :LOGIC and :EXEC. The default for creator is obtained by adding the prefix "CREATE-" to name. The default value for :LOGIC is formed by adding the suffix "$A" to creator; for :EXEC, by adding the suffix "$C". The :CREATOR function must be the creator for the specified :CONCRETE stobj, as ACL2 checks that the :CREATOR function takes no arguments and returns the :CONCRETE stobj.

    Corr-fn is a known function symbol that takes two arguments (for the correspondence theorems). The default for corr-fn is obtained by adding the suffix "$CORR" to name.

    Congruent-to should either be nil (the default) or the name of an abstract stobj previously introduced (by defabsstobj). In the latter case, the current and previous abstract stobj should have the same concrete stobj (not merely congruent concrete stobjs), and their :EXPORTS fields should have the same length and also correspond, as follows: the ith export of each should have the same :LOGIC and :EXEC symbols. See defstobj for more about congruent stobjs. Note that if two names are congruent, then they are either both ordinary stobjs or both abstract stobjs.

    Protect-default should either be nil (the default) or t. It provides the value of keyword :PROTECT for each member of exports that does not explicitly specify :PROTECT. See the discussion of exports below.

    An important aspect of the congruent-to parameter is that if it is not nil, then the checks for lemmas -- {CORRESPONDENCE}, {GUARD-THM}, and {PRESERVED} -- are omitted. Thus, the values of keyword :CORR-FN, and the values of keywords :CORRESPONDENCE, :GUARD-THM, and :PRESERVED in each export (as we discuss next), are irrelevant; they are not inferred and they need not be supplied.

    The value of :EXPORTS is a non-empty true list. Each ei is a function spec (for an exported function). The valid keywords are :LOGIC, :EXEC, :CORRESPONDENCE, and :GUARD-THM, :PROTECT, and also :PRESERVED if and only if the specified :EXEC function returns the :CONCRETE stobj. The default values for all of these keywords except :PROTECT are obtained by respectively adding the suffix "$A" "$C", "{CORRESPONDENCE}", "{GUARD-THM}", or "{PRESERVED}". For :PROTECT, the default is nil unless the defabsstobj event specifies :PROTECT-DEFAULT t.

    Doc, if non-nil, is a documentation string (see doc-string).

    Not shown is the keyword, :MISSING; the effect of :missing t is to turn the call of defabsstobj into a corresponding call of defabsstobj-missing-events.

    Note that a defabsstobj event will fail if the required lemmas -- that is, those for valid keywords :CORRESPONDENCE, :GUARD-THM, and :PRESERVED -- have not been proved, unless proofs are being skipped. The exemption when skipping proofs allows the supporting lemmas to be local to books and encapsulate events. If the ld special ld-skip-proofsp is t, then the missing events are printed with a warning before the defabsstobj event is admitted; but if ld-skip-proofsp is the symbol INCLUDE-BOOK, then that warning is omitted. (Also see skip-proofs and see ld-skip-proofsp.) If however proofs are not being skipped, then the defabsstobj event will fail after printing the missing events. Advanced users may wish to see defabsstobj-missing-events for a utility that returns a data structure containing the missing lemmas.

    Let st be an abstract stobj with corresponding concrete stobj st$c. let f be an exported function for st and let f$a and f$c be the corresponding :LOGIC and :EXEC functions, respectively. The formals of f are obtained by taking the formals of f$c and replacing st$c by st. The guard for f is derived as follows from the guard of f$a. First, the formals of f$a are replaced by the formals of f in the guard of f$a, to obtain a term we denote here as guard-pre. Now for each exported function symbol g of st with corresponding :LOGIC function g$a, form a functional substitution by consing g$a with g. Finally, apply that functional substitution to guard-pre; the result is the guard of f. That guard must satisfy the usual conditions of a guard: thus, it must return a single non-stobj value and satisfy normal syntactic restrictions, including single-threadedness in its handling of stobjs.

    Remark. Because of how guards are created for exported functions, and in particular because :LOGIC functions are replaced as discussed above, a good discipline is to define :LOGIC functions that are not intended for general use, but are intended only for use as :LOGIC functions of corresponding stobj primitives. For example, suppose that you use length as the :LOGIC function for some stobj primitive, f (as opposed to using your own function, say, foo-length or foo$a). Then every call of length will be replaced by f when creating the guard of a stobj primitive from the guard of its :LOGIC function. This might not be what you intended if you were using length in that guard simply to compute the length of an ordinary list.

    There are a few additional restrictions, as follows.

    All exported function names must be new (unless redefinition is on; see ld-redefinition-action), and there must be no duplicates among them.

    The :CONCRETE stobj name must be a formal parameter of the :EXEC fn of every function spec, except for the :CREATOR function spec. Also the input signatures of the :LOGIC and :EXEC function for a function spec must agree, except perhaps at the position of that :CONCRETE formal.

    For function specs other than the :CREATOR function spec, the output signatures of the :LOGIC and :EXEC functions must have the same length and must agree, except perhaps at position p_out of the :CONCRETE stobj in the :EXEC function's output. If p_in is the position of the :CONCRETE stobj in the :EXEC function's formals, then the :LOGIC function's output at position p_out should match the :LOGIC function's formal at position p_in.

    The :PROTECT keyword is something that you should ignore unless you get an error message about it, pertaining to modifying the concrete stobj non-atomically. In that case, you can eliminate the error by providing :PROTECT t in the function spec, or by providing defabsstobj keyword argument :PROTECT-DEFAULT t at the top level. The above explanation is probably all you need to know about :PROTECT, but just below is a more complete explanation for those who desire it. Further information is also available if you need it; see set-absstobj-debug, and see the example uses of these keywords in community book books/misc/defabsstobj-example-2.lisp.

    For those who are interested, here is a more detailed discussion of :PROTECT and :PROTECT-DEFAULT, as promised above. It applies to any function spec for an export (hence not to the :CREATOR function spec). If the :EXEC function is a stobj primitive, then clearly the following property holds: any execution of a call of that function can only update the concrete stobj at most once -- i.e., modification of the concrete stobj is atomic. ACL2 can deduce this property not only for stobj primitives but for many other functions as well. However, if ACL2 cannot deduce this property, then it will cause an error saying that the :EXEC function ``appears capable of modifying the concrete stobj, <stobj_name>, non-atomically.'' That message also explains how to eliminate this error: provide :PROTECT t for the function spec. Alternatively, all function specs without an explicit :PROTECT keyword can be implicitly supplied :PROTECT t by supplying the value t for the :PROTECT-DEFAULT keyword parameter of the defabsstobj event. However, beware that when :PROTECT is t, the generated raw Lisp code runs slightly less efficiently -- though perhaps with negligible efficiency loss if the :EXEC function is not trivial. Community books books/misc/defabsstobj-example-3.lisp and books/misc/defabsstobj-example-4.lisp provide related information.

    We conclude with some remarks.

    Unlike defstobj, there is no :renaming argument. Instead, the scheme described above provides a flexible way to assign names.

    Those who use the experimental extension ACL2(h), which includes function memoization (see memoize), may be aware that the memo table for a function is flushed whenever it is the case that one of its stobj inputs is updated. In fact, such flushing happens even when a stobj that is congruent to one of its stobj inputs is updated. For that purpose, an abstract stobj is considered to be congruent to its corresponding concrete stobj.




    acl2-sources/doc/HTML/DEFATTACH.html0000664002132200015000000006346512222333516016252 0ustar kaufmannacl2 DEFATTACH.html -- ACL2 Version 6.3

    DEFATTACH

    execute constrained functions using corresponding attached functions
    Major Section:  EVENTS
    

    This documentation topic is organized into the following sections:

    Introductory example.
    Syntax and semantics of defattach.
    Three primary uses of defattach.
    Miscellaneous remarks, with discussion of possible user errors.

    Please see encapsulate if you intend to use defattach but are not already familiar with the use of encapsulate to introduce constrained functions.

    See community book books/misc/defattach-example.lisp for a small example. it illustrates how defattach may be used to build something like ``higher-order'' programs, in which constrained functions may be refined to different executable functions. More uses of defattach may be found in the ACL2 source code, specifically, file boot-strap-pass-2.lisp.

    The argument :skip-checks t enables easy experimentation with defattach, by permitting use of :program mode functions and the skipping of semantic checks. Also permitted is :skip-checks nil (the default) and :skip-checks :cycles, which turns off only the update of the extended ancestor relation (see below) and hence the check for cycles in this relation; see below. We do not make any logical claims when the value of :skip-checks is non-nil; indeed, a trust tag is required in this case (see defttag). Remark for those who use the experimental HONS extension (see hons-and-memoization): the interaction of memoization and attachments is not tracked for attachments introduced with a non-nil value of :skip-checks. For more discussion of :skip-checks t, see defproxy; we do not discuss :skip-checks further, here.

    Introductory example.

    We begin with a short log illustrating the use of defattach. Notice that after evaluating the event (defattach f g), a call of the constrained function f is evaluated by instead calling g on the arguments.

    ACL2 !>(encapsulate
            ((f (x) t :guard (true-listp x)))
            (local (defun f (x) x))
            (defthm f-property
              (implies (consp x) (consp (f x)))))
    [... output omitted ...]
     T
    ACL2 !>(defun g (x)
             (declare (xargs :guard (or (consp x) (null x))))
             (cons 17 (car x)))
    [... output omitted ...]
     G
    ACL2 !>(f '(3 4)) ; undefined function error
    
    
    ACL2 Error in TOP-LEVEL:  ACL2 cannot ev the call of undefined function
    F on argument list:
    
    ((3 4))
    
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.
    
    ACL2 !>(defattach f g)
    [... output omitted ...]
     :ATTACHMENTS-RECORDED
    ACL2 !>(f '(3 4)) ; f is evaluated using g
    (17 . 3)
    ACL2 !>(trace$ f g)
     ((F) (G))
    ACL2 !>(f '(3 4)) ; f is evaluated using g
    1> (ACL2_*1*_ACL2::F (3 4))
      2> (ACL2_*1*_ACL2::G (3 4))
        3> (G (3 4))
        <3 (G (17 . 3))
      <2 (ACL2_*1*_ACL2::G (17 . 3))
    <1 (ACL2_*1*_ACL2::F (17 . 3))
    (17 . 3)
    ACL2 !>(defattach f nil) ; unattach f (remove its attachment)
    [... output omitted ...]
     :ATTACHMENTS-RECORDED
    ACL2 !>(f '(3 4)) ; undefined function error once again
    1> (ACL2_*1*_ACL2::F (3 4))
    
    
    ACL2 Error in TOP-LEVEL:  ACL2 cannot ev the call of undefined function
    F on argument list:
    
    ((3 4))
    
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.
    
    ACL2 !>
    

    Syntax and semantics of defattach.

    The log above shows that the event (defattach f g) allows g to be used for evaluating calls of f. From a logical perspective, the evaluation takes place in the addition to the current session of an ``attachment equation'' axiom (universally quantified over all x) for each defattach event:

    (equal (f x) (g x)) ;;; attachment equation axiom for (defattach f g)
    

    Below we explain defattach in some detail. But it is important to keep in mind that evaluation with the attachment equations takes place in an extension of the logical theory of the session. ACL2 guarantees that this so-called ``evaluation theory'' remains consistent, assuming the absence of defaxiom events from the user. This guarantee is a consequence of a more general guarantee: an ACL2 logical world exists in which (loosely speaking) the attachment equation for (defattach f g), as (defun f (...) (g ...)), takes the place of the original defining event for f, for each defattach event. This more general guarantee holds even if there are defaxiom events, though as explained below, no function symbol that syntactically supports a defaxiom formula is allowed to get an attachment. A deeper discussion of the logical issues is available (but not intended to be read by most users) in a long comment in the ACL2 source code labeled ``Essay on Defattach.''

    Example Forms:
    (defattach f g)   ; call g in place of calling constrained function f
    (defattach (f g)) ; same as just above
    (defattach (f g :hints (("Goal" :in-theory (enable foo)))))
                      ; equivalent to first form above, except with hints for the
                      ; proof that the guard of f implies the guard of g
    (defattach (f g :hints (("Goal" :in-theory (enable foo)))
                    :otf-flg t))
                      ; as above, except with an :otf-flg of t for the proof that
                      ; the guard of f implies the guard of g
    (defattach (f g)
               :hints (("Goal" :use my-thm)))
                      ; equivalent to first form above, except with hints for the
                      ; proof that the constraints on f hold for g
    (defattach (f g)
               :hints (("Goal" :use my-thm))
               :otf-flg t)
                      ; as above, except with an :otf-flg of t for the proof that
                      ; the constraints on f hold for g
    (defattach (f g)
               (h j)) ; Attach g to f and attach j to h
    (defattach (f g :attach nil)
               (h j)) ; Same as just above, including the same proof obligations,
                      ; except for one difference: because of :attach nil, calls
                      ; of f will not be evaluated, i.e., there will be no
                      ; executable attachment of g to f
    (defattach (f nil)
               (h j)) ; Attach j to h and unattach f
    (defattach (f g :hints (("Goal" :in-theory (enable foo))))
               (h j :hints (("Goal" :in-theory (enable bar))))
               :hints (("Goal" :use my-thm)))
                      ; Attach g to f and attach j to h, with hints:
                      ; - For proving that the guard of f implies the guard of g,
                      ;   enable foo;
                      ; - For proving that the guard of h implies the guard of j,
                      ;   enable bar; and
                      ; - For proving that the constraints on f and h hold for
                      ;   g and j (respectively), use theorem my-thm.
    
    
    (defattach f nil)   ; remove the attachment of f, if any (e.g., g above)
    (defattach (f nil)) ; same as just above
    
    General Forms:
    (defattach f g)   ; single attach or, if g is nil, unattach
    (defattach (f1 g1 :kwd val ...)
               ...
               (fk gk :kwd' val' ...)
               :kwd'' val'' ...)
    
    where each indicated keyword-value pair is optional and each keyword is one of :ATTACH, :HINTS, :OTF-FLG, or :INSTRUCTIONS. The value of each :ATTACH keyword is either t or nil, with default t except that the value of :ATTACH at the ``top level,'' after each entry (fi gi ...), is the default for each :ATTACH keyword supplied in such an entry. We discuss the :ATTACH keyword later in this documentation topic. The associated values for the other keywords have the usual meanings for the proof obligations described below: the guard proof obligation for keywords within each (fi gi ...) entry, and the constraint proof obligation for keywords at the top level. No keyword may occur twice in the same context, i.e., within the same (fi gi ...) entry or at the top level; and :INSTRUCTIONS may not occur in the same context with :HINTS or :OTF-FLG.

    The first General Form above is simply an abbreviation for the form (defattach (f g)), which is an instance of the second General Form above. For the second General Form we say that gi is ``attached to'' fi (by the defattach event) if gi is not nil, and otherwise we say that fi is ``unattached'' (by the defattach event). It is also convenient to refer to <fi,gi> as an ``attachment pair'' (of the event) if gi is not nil. We may refer to the set of fi as the ``attachment nest'' of each fi.

    We start with a brief introduction to the first General Form in the case that g is not nil. This form arranges that during evaluation, with exceptions noted below, every call of the constrained function symbol f will in essence be replaced by a call of the function symbol g on the same arguments. We may then refer to g as the ``attachment of'' f, or say that ``g is attached to f.'' Notable exceptions, where we do not use attachments during evaluation, are for macroexpansion, evaluation of defconst and defpkg terms, evaluation during table events, some stobj operations including all updates, and especially evaluation of ground terms (terms without free variables) during proofs. However, even for these cases we allow the use of attachments in the first argument of prog2$ and, more generally, the next-to-last (i.e., second) argument of return-last when its first argument is not of the form 'm for some macro, m.

    To see why attachments are disallowed during evaluation of ground terms during proofs (except for the prog2$ and return-last cases mentioned above), consider the following example.

    (defstub f (x) t)
    (defun g (x) (+ 3 x))
    (defattach f g)
    
    If the form (f 2) is submitted at the ACL2 prompt, the result will be 5 because the attachment g of f is called on the argument, 2. However, during a proof the term (f 2) will not be simplified to 5, since that would be unsound, as there are no axioms about f that would justify such a simplification.

    For the case that g is nil in the first General Form above, the result is the removal of the existing attachment to f, if any. After this removal, calls of f will once again cause errors saying that ``ACL2 cannot ev the call of undefined function f ...''. In this case not only is the previous attachment to f removed; moreover, for every function symbol f' in the attachment nest of f in the defattach event that introduced the existing attachment to f, then f' is unattached. (An example near the end of this documentation topic shows why this unattachment needs to be done.) Such removal takes place before the current defattach is processed, but is restored if the new event fails to be admitted.

    We focus henceforth on the second General Form. There must be at least one attachment, i.e., i must be at least 1. All keywords are optional; their role is described below. The fi must be distinct constrained function symbols, that is, function symbols all introduced in signatures of encapsulate events (or macros such as defstub that generate encapsulate events). Each non-nil gi is a :logic-mode function symbol that has had its guards verified, with the same signature as fi (though formal parameters for fi and gi may have different names). (Note: The macro defattach!, defined in community book books/misc/defattach-bang, avoids this restriction.) This event generates proof obligations and an ordering check, both described below. The effect of this event is first to remove any existing attachments for all the function symbols fi, as described above for the first General Form, and then to attach each gi to fi.

    Proof obligations must be checked before making attachments. For this discussion we assume that each gi is non-nil (otherwise first remove all attachment pairs <fi,gi> for which gi is nil). Let s be the functional substitution mapping each fi to gi. For any term u, we write u\s for the result of applying s to u; that is, u\s is the ``functional instance'' obtained by replacing each fi by gi in u. Let G_fi and G_gi be the guards of fi and gi, respectively. Let G_fi' be the result of replacing each formal of fi by the corresponding formal of gi in G_fi. ACL2 first proves, for each i (in order), the formula (implies G_fi' G_gi)\s. If this sequence of proofs succeeds, then the remaining formula to prove is the functional instance C\s of the conjunction C of the constraints on the symbols fi; see constraint. This last proof obligation is thus similar to the one generated by functional instantiation (see constraint). As with functional instantiation, ACL2 stores the fact that such proofs have been done so that they are avoided in future events (see lemma-instance). Thus, you will likely avoid some proofs with the sequence

    (defattach f g)
    (defattach f nil)
    (defattach f g)
    (defattach f nil)
    ...
    
    rather than the sequence:
    (defattach f g)
    :u
    (defattach f g)
    :u
    ...
    

    It remains to describe an ordering check. We begin with the following motivating example.

    (defstub f (x) t) ; constrained function with no constraints
    (defun g (x) (declare (xargs :guard t)) (not (f x)))
    (defattach f g) ; ILLEGAL!
    
    Were the above defattach event to succeed, the evaluation theory (discussed above) would be inconsistent: (f x) equals (g x) by the new attachment equation, which in turn equals (not (f x)) by definition of g. The evaluation would therefore be meaningless. Also, from a practical perspective, there would be an infinite loop resulting from any call of f.

    We consider a function symbol g to be an ``extended immediate ancestor of'' a function symbol f if either of the following two criteria is met: (a) g occurs in the formula that introduces f (i.e., definition body or constraint) and g is introduced by an event different from (earlier than) the event introducing f; or (b) g is attached to f. For a proposed defattach event, we check that this relation has no cycles, where for condition (b) we include all attachment pairs that would result, including those remaining from earlier defattach events.

    Of course, a special case is that no function symbol may be attached to itself. Similarly, no function symbol may be attached to any of its ``siblings'' -- function symbols introduced by the same event -- as siblings are considered equivalent for purposes of the acyclicity check.

    Three primary uses of defattach.

    We anticipate three uses of defattach:

    (1) Constrained function execution

    (2) Sound modification of the ACL2 system

    (3) Program refinement

    We discuss these in turn.

    (1) The example at the beginning of this documentation illustrates constrained function execution.

    (2) ACL2 is written essentially in itself. Thus, there is an opportunity to attaching to system functions. For example, encapsulated function too-many-ifs-post-rewrite, in the ACL2 source code, receives an attachment of too-many-ifs-post-rewrite-builtin, which implements a heuristic used in the rewriter. To find all such examples, search the source code for the string `-builtin'.

    Over time, we expect to continue replacing ACL2 source code in a similar manner. We invite the ACL2 community to assist in this ``open architecture'' enterprise; feel free to email the ACL2 implementors if you are interested in such activity.

    (3) Recall that for an attachment pair <f,g>, a proof obligation is (speaking informally) that g satisfies the constraint on f. Yet more informally speaking, g is ``more defined'' than f; we can think of g as ``refining'' f. With these informal notions as motivation, we can view defattach as providing refinement though the following formal observation: the evaluation theory extends the theory of the ACL2 session, specifically by the addition of all attachment equations. For the logic-inclined, it may be useful to think model-theoretically: The class of models of the evaluation theory is non-empty but is a subset of the class of models of the current session theory.

    Miscellaneous remarks, with discussion of possible user errors.

    We conclude with remarks on some details.

    A defattach event is never redundant (see redundant-events); in that sense it is analogous to in-theory.

    As mentioned above, the use of attachments is disabled for evaluation of ground terms during proofs. However, attachments can be used on code during the proof process, essentially when the ``program refinement'' is on theorem prover code rather than on functions we are reasoning about. The attachment to too-many-ifs-post-rewrite described above provides one example of such attachments. Meta functions and clause-processor functions can also have attachments, with the restriction that no common ancestor with the evaluator can have an attachment; see evaluator-restrictions.

    For an attachment pair <f,g>, evaluation of f never consults the guard of f. Rather, control passes to g, whose guard is checked if necessary. The proof obligation related to guards, as described above, guarantees that any legal call of f is also a legal call of g. Thus for guard-verified code that results in calls of f in raw Lisp, it is sound to replace these calls with corresponding calls of g.

    Defattach events are illegal inside any encapsulate event with a non-empty signature unless they are local to the encapsulate.

    We next discuss a restriction based on a notion of a function symbol syntactically supporting an event. Function symbol f is ancestral in event E if either f occurs in E, or (recursively) f occurs in an event E' that introduces some function symbol g that is ancestral in E. We require that no function symbol ancestral in the formula of a defaxiom event may have an attachment. Theoretical reasons are discussed in comments in the ACL2 source code, but here we give a little example showing the need for some such restriction: without it, we show how to prove nil!

    (defn g1 () 1)
    (defn g2 () 2)
    (defstub f1 () t)
    (defstub f2 () t)
    (defund p (x)
      (declare (ignore x))
      t)
    (defevaluator evl evl-list
      ((p x)))
    (defaxiom f1-is-f2
      (equal (f1) (f2)))
    (defun meta-fn (x)
      (cond ((equal (f1) (f2))
             x)
            (t *nil*)))
    (defthm bad-meta-rule
      (equal (evl x a)
             (evl (meta-fn x) a))
      :rule-classes ((:meta :trigger-fns (p))))
    (defattach f1 g1)
    (defattach f2 g2)
    (defthm contradiction
      nil
      :hints (("Goal" :use ((:instance (:theorem (not (p x)))
                                       (x t)))))
      :rule-classes nil)
    

    To see all attachments: (all-attachments (w state)). (Note that attachments introduced with a non-nil value of :skip-checks will be omitted from this list.)

    Next we discuss the :ATTACH keyword. There is rarely if ever a reason to specify :ATTACH T, but the following (admittedly contrived) example shows why it may be necessary to specify :ATTACH NIL. First we introduce three new function symbols.

      (defstub f (x) t)
    
      (defun g (x)
        (f x))
    
      (encapsulate ((h (x) t))
        (local (defun h (x) (g x)))
        (defthm h-prop
          (equal (h x) (g x))))
    
    Now suppose we want to attach the function acl2-numberp to both f and h.
      (defattach (f acl2-numberp) (h acl2-numberp))
    
    Such an attempt fails, because the following constraint is generated but is not a theorem: (EQUAL (ACL2-NUMBERP X) (G X)). Clearly we also need to attach to g as well.
      (defattach (f acl2-numberp) (h acl2-numberp) (g acl2-numberp))
    
    But this fails for a different reason, as explained by the error message:
      ACL2 Error in ( DEFATTACH (F ACL2-NUMBERP) ...):  It is illegal to
      attach to function symbol G, because it was introduced with DEFUN.
      See :DOC defattach.
    
    That is: logically, we need to attach acl2-numberp to g, but we cannot actually attach to g because it was introduced with defun, not with encapsulate. So we specify :ATTACH NIL for the attachment to g, saying that no actual attachment should be made to the code for g, even though for logical purposes we should consider that g has been given the indicated attachment.
      (defattach (f acl2-numberp) (h acl2-numberp) (g acl2-numberp :attach nil))
    
    Finally, we can check that f, g, and h execute as expected.
        ACL2 !>(assert-event (and (f 3)
                           (not (f t))
                           (g 3)
                           (not (g t))
                           (h 3)
                           (not (h t))))
         :PASSED
        ACL2 !>
    

    We conclude with an example promised above, showing why it is necessary in general to unattach all function symbols in an existing attachment nest when unattaching any one of those function symbols. Consider the following example.

    (defstub f1 () t)
    (encapsulate ((f2 () t))
      (local (defun f2 () (f1)))
      (defthm f2=f1 (equal (f2) (f1))))
    (encapsulate ((f3 () t))
      (local (defun f3 () (f1)))
      (defthm f3=f1 (equal (f3) (f1))))
    (defun four () (declare (xargs :guard t)) 4)
    (defun five () (declare (xargs :guard t)) 5)
    (defattach (f1 four) (f2 four))
    (defattach (f1 five) (f3 five))
    
    The second defattach replaces erases the existing attachment pair <f1,four> before installing the new attachment pairs <f1,five> and <f3,five>. After the second defattach, both (f1) and (f3) evaluate to 5. Now suppose that the attachment pair <f2,four> were not erased. Then we would have (f1) evaluating to 5 and (f2) evaluating to 4, contradicting the constraint f2=f1. The evaluation theory would thus be inconsistent, and at a more concrete level, the user might well be surprised by evaluation results if the code were written with the assumption specified in the constraint f2=f1.




    acl2-sources/doc/HTML/DEFAULT-BACKCHAIN-LIMIT.html0000664002132200015000000000141512222333520020146 0ustar kaufmannacl2 DEFAULT-BACKCHAIN-LIMIT.html -- ACL2 Version 6.3

    DEFAULT-BACKCHAIN-LIMIT

    specifying the backchain limit for a rule
    Major Section:  MISCELLANEOUS
    

    See backchain-limit.

    The initial value is (nil nil). To inspect the current value (as explained elsewhere; see backchain-limit):

    (default-backchain-limit wrld :ts) ; for type-set reasoning
    (default-backchain-limit wrld :rewrite) ; for rewriting
    




    acl2-sources/doc/HTML/DEFAULT-DEFUN-MODE.html0000664002132200015000000000715212222333520017416 0ustar kaufmannacl2 DEFAULT-DEFUN-MODE.html -- ACL2 Version 6.3

    DEFAULT-DEFUN-MODE

    the default defun-mode of defun'd functions
    Major Section:  MISCELLANEOUS
    

    When a defun is processed and no :mode xarg is supplied, the function default-defun-mode is used. To find the default defun-mode of the current ACL2 world, type (default-defun-mode (w state)). See defun-mode for a discussion of defun-modes. To change the default defun-mode of the ACL2 world, type one of the keywords :program or :logic.

    The default ACL2 prompt displays the current default defun-mode by showing the character p for :program mode, and omitting it for :logic mode; see default-print-prompt. The default defun-mode may be changed using the keyword commands :program and :logic, which are equivalent to the commands (program) and (logic). Each of these names is documented separately: see program and see logic. The default defun-mode is stored in the table acl2-defaults-table and hence may also be changed by a table command. See table and also see acl2-defaults-table. Both mode-changing commands are events.

    While events that change the default defun-mode are permitted within an encapsulate or the text of a book, their effects are local in scope to the duration of the encapsulation or inclusion. For example, if the default defun-mode is :logic and a book is included that contains the event (program), then subsequent events within the book are processed with the default defun-mode :program; but when the include-book event completes, the default defun-mode will still be :logic. Commands that change the default defun-mode are not permitted inside local forms.




    acl2-sources/doc/HTML/DEFAULT-HINTS-TABLE.html0000664002132200015000000000243712222333530017547 0ustar kaufmannacl2 DEFAULT-HINTS-TABLE.html -- ACL2 Version 6.3

    DEFAULT-HINTS-TABLE

    a table used to provide hints for proofs
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please see set-default-hints, see add-default-hints, and see remove-default-hints for how to use this table. For completeness, we mention here that under the hood, these events all update the default-hints-table by updating its key, t, for example as follows.

    (table default-hints-table t
           '((computed-hint-1 clause)
             (computed-hint-2 clause
                              stable-under-simplificationp)))
    

    The use of default hints is explained elsewhere; see set-default-hints.

    Advanced users only: see override-hints for an advanced variant of default hints.




    acl2-sources/doc/HTML/DEFAULT-HINTS.html0000664002132200015000000000237412222333520016721 0ustar kaufmannacl2 DEFAULT-HINTS.html -- ACL2 Version 6.3

    DEFAULT-HINTS

    a list of hints added to every proof attempt
    Major Section:  MISCELLANEOUS
    

    Examples:
    ACL2 !>(default-hints (w state))
    ((computed-hint-1 clause)
     (computed-hint-2 clause stable-under-simplificationp))
    
    The value returned by this function is added to the right of the :hints argument of every defthm and thm command, and to hints provided to defuns as well (:hints, :guard-hints, and (for ACL2(r)) :std-hints).

    See set-default-hints for a more general discussion. Advanced users only: see override-hints for an advanced variant of default hints that are not superseded by :hints arguments.




    acl2-sources/doc/HTML/DEFAULT-PRINT-PROMPT.html0000664002132200015000000000433412222333520017745 0ustar kaufmannacl2 DEFAULT-PRINT-PROMPT.html -- ACL2 Version 6.3

    DEFAULT-PRINT-PROMPT

    the default prompt printed by ld
    Major Section:  MISCELLANEOUS
    

    Example prompt:
    ACL2 p!s>
    
    The prompt printed by ACL2 displays the current package, followed by a space, followed by zero or more of the three characters as specified below, followed by the character > printed one or more times, reflecting the number of recursive calls of ld. The three characters in the middle are as follows:
    p     ; when (default-defun-mode (w state)) is :program
    !     ; when guard checking is on
    s     ; when (ld-skip-proofsp state) is t
    
    See default-defun-mode, see set-guard-checking, and see ld-skip-proofsp.

    Also see ld-prompt to see how to install your own prompt.

    Here are some examples with ld-skip-proofsp nil.

    ACL2 !>    ; logic mode with guard checking on
    ACL2 >     ; logic mode with guard checking off
    ACL2 p!>   ; program mode with guard checking on
    ACL2 p>    ; program mode with guard checking off
    
    Here are some examples with default-defun-mode of :logic.
    ACL2 >     ; guard checking off, ld-skip-proofsp nil
    ACL2 s>    ; guard checking off, ld-skip-proofsp t
    ACL2 !>    ; guard checking on, ld-skip-proofsp nil
    ACL2 !s>   ; guard checking on, ld-skip-proofsp t
    
    Finally, here is the prompt in raw mode (see set-raw-mode), regardless of the settings above:
    ACL2 P>
    




    acl2-sources/doc/HTML/DEFAULT-RULER-EXTENDERS.html0000664002132200015000000000262312222333520020261 0ustar kaufmannacl2 DEFAULT-RULER-EXTENDERS.html -- ACL2 Version 6.3

    DEFAULT-RULER-EXTENDERS

    the default ruler-extenders for defun'd functions
    Major Section:  MISCELLANEOUS
    

    When a defun is processed and no :ruler-extenders xarg is supplied, the function default-ruler-extenders is used to obtain the current ruler-extenders; see ruler-extenders. To find the default ruler-extenders of the current ACL2 world, type (default-ruler-extenders (w state)).

    While events that change the default ruler-extenders are permitted within an encapsulate or the text of a book, their effects are local in scope to the duration of the encapsulation or inclusion. See default-defun-mode for an analogous discussion for defun-modes.




    acl2-sources/doc/HTML/DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT.html0000664002132200015000000000113612222333522022113 0ustar kaufmannacl2 DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT.html -- ACL2 Version 6.3

    DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT

    for ACL2(p): returns the default value for global total-parallelism-work-limit
    Major Section:  PARALLEL-PROOF
    

    See set-total-parallelism-work-limit.




    acl2-sources/doc/HTML/DEFAULT-VERIFY-GUARDS-EAGERNESS.html0000664002132200015000000000101312222333530021323 0ustar kaufmannacl2 DEFAULT-VERIFY-GUARDS-EAGERNESS.html -- ACL2 Version 6.3

    DEFAULT-VERIFY-GUARDS-EAGERNESS

    See set-verify-guards-eagerness.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/DEFAULT.html0000664002132200015000000000235312222333525016040 0ustar kaufmannacl2 DEFAULT.html -- ACL2 Version 6.3

    DEFAULT

    return the :default from the header of a 1- or 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (default 'delta1 a)
    
    General Form:
    (default name alist)
    
    where name is an arbitrary object and alist is a 1- or 2-dimensional array. This function returns the contents of the :default field of the header of alist. When aref1 or aref2 is used to obtain a value for an index (or index pair) not bound in alist, the default value is returned instead. Thus, the array alist may be thought of as having been initialized with the default value. default operates in virtually constant time if alist is the semantic value of name. See arrays.




    acl2-sources/doc/HTML/DEFAXIOM.html0000664002132200015000000000353412222333516016152 0ustar kaufmannacl2 DEFAXIOM.html -- ACL2 Version 6.3

    DEFAXIOM

    add an axiom
    Major Section:  EVENTS
    

    WARNING: We strongly recommend that you not add axioms. If at all possible you should use defun or mutual-recursion to define new concepts recursively or use encapsulate to constrain them constructively. If your goal is to defer a proof by using a top-down style, consider using skip-proofs; see the discussion on ``Top-Down Proof'' in Section B.1.2 of ``Computer-Aided Reasoning: An Approach.'' Adding new axioms frequently renders the logic inconsistent.

    Example:
    (defaxiom sbar (equal t nil)
              :rule-classes nil
              :doc ":Doc-Section ...")
    
    General Form:
    (defaxiom name term
             :rule-classes rule-classes
             :doc          doc-string)
    
    where name is a new symbolic name (see name), term is a term intended to be a new axiom, and rule-classes and doc-string are as described in the corresponding documentation topics . The two keyword arguments are optional. If :rule-classes is not supplied, the list (:rewrite) is used; if you wish the axiom to generate no rules, specify :rule-classes nil.




    acl2-sources/doc/HTML/DEFCHOOSE.html0000664002132200015000000001557612222333516016266 0ustar kaufmannacl2 DEFCHOOSE.html -- ACL2 Version 6.3

    DEFCHOOSE

    define a Skolem (witnessing) function
    Major Section:  EVENTS
    

    Examples:
    (defchoose choose-x-for-p1-and-p2 (x) (y z)
      (and (p1 x y z)
           (p2 x y z)))
    
    (defchoose choose-x-for-p1-and-p2 x (y z) ; equivalent to the above
      (and (p1 x y z)
           (p2 x y z)))
    
    ; The following is as above, but strengthens the axiom added to pick a sort
    ; of canonical witness, as described below.
    (defchoose choose-x-for-p1-and-p2 x (y z)
      (and (p1 x y z)
           (p2 x y z))
      :strengthen t)
    
    (defchoose choose-x-and-y-for-p1-and-p2 (x y) (z)
      (and (p1 x y z)
           (p2 x y z)))
    
    
    

    Some Related Topics

    General Form: (defchoose fn (bound-var1 ... bound-varn) (free-var1 ... free-vark) body :doc doc-string :strengthen b),
    where fn is the symbol you wish to define and is a new symbolic name (see name), (bound-var1 ... bound-varn) is a list of distinct `bound' variables (see below), (free-var1 ... free-vark) is the list of formal parameters of fn and is disjoint from the bound variables, and body is a term. The use of lambda-list keywords (such as &optional) is not allowed. The documentation string argument, :doc doc-string, is optional; for a description of the form of doc-string see doc-string. The :strengthen keyword argument is optional; if supplied, it must be t or nil.

    The system treats fn very much as though it were declared in the signature of an encapsulate event, with a single axiom exported as described below. If you supply a :use hint (see hints), :use fn, it will refer to that axiom. No rule (of class :rewrite or otherwise; see rule-classes) is created for fn.

    Defchoose is only executed in defun-mode :logic; see defun-mode. Also see defun-sk.

    In the most common case, where there is only one bound variable, it is permissible to omit the enclosing parentheses on that variable. The effect is the same whether or not those parentheses are omitted. We describe this case first, where there is only one bound variable, and then address the other case. Both cases are discussed assuming :strengthen is nil, which is the default. We deal with the case :strengthen t at the end.

    The effect of the form

    (defchoose fn bound-var (free-var1 ... free-vark)
      body)
    
    is to introduce a new function symbol, fn, with formal parameters (free-var1 ... free-vark). Now consider the following axiom, which states that fn picks a value of bound-var so that the body will be true, if such a value exists:
    (1)   (implies body
                   (let ((bound-var (fn free-var1 ... free-vark)))
                     body))
    
    This axiom is ``clearly conservative'' under the conditions expressed above: the function fn simply picks out a ``witnessing'' value of bound-var if there is one. For a rigorous statement and proof of this conservativity claim, see conservativity-of-defchoose.

    Next consider the case that there is more than one bound variable, i.e., there is more than one bound-var in the following.

    (defchoose fn
               (bound-var1 ... bound-varn)
               (free-var1 ... free-vark)
               body)
    
    Then fn returns a multiple value with n components, and formula (1) above is expressed using mv-let as follows:
    (implies body
             (mv-let (bound-var1 ... bound-varn)
                     (fn free-var1 ... free-vark)
                     body))
    

    We now discuss the case that :strengthen t is supplied. For simplicity we return to our simplest case, with defchoose applied to function fn, a single free variable y, and a single bound variable bound-var. The idea is that if we pick the ``smallest'' witnessing bound-var for two different free variables y and y1, then either those two witnesses are the same, or else one is less than the other, in which case the smaller one is a witness for its free variable but not for the other. (See comments in source function defchoose-constraint-extra for more details.) Below, body1 is the result of replacing y by y1 in body.

    (2)   (or (equal (fn y) (fn y1))
              (let ((bound-var (fn y)))
                (and body
                     (not body1)))
              (let ((bound-var (fn y1)))
                (and body1
                     (not body))))
    
    An important application of this additional axiom is to be able to define a ``fixing'' function that picks a canonical representative of each equivalence class, for a given equivalence relation. The following events illustrate this point.
    (encapsulate
     ((equiv (x y) t))
     (local (defun equiv (x y) (equal x y)))
     (defequiv equiv))
    
    (defchoose efix (x) (y)
      (equiv x y)
      :strengthen t)
    
    (defthm equiv-implies-equal-efix-1
      (implies (equiv y y1)
               (equal (efix y) (efix y1)))
      :hints (("Goal" :use efix))
      :rule-classes (:congruence))
    
    (defthm efix-fixes
      (equiv (efix x) x)
      :hints (("Goal" :use ((:instance efix (y x))))))
    

    If there is more than one bound variable, then (2) is modified in complete analogy to (1) to use mv-let in place of let.

    Comment for logicians: As we point out in the documentation for defun-sk, defchoose is ``appropriate,'' by which we mean that it is conservative, even in the presence of epsilon-0 induction. For a proof, See conservativity-of-defchoose.




    acl2-sources/doc/HTML/DEFCONG.html0000664002132200015000000000441012222333516016015 0ustar kaufmannacl2 DEFCONG.html -- ACL2 Version 6.3

    DEFCONG

    prove congruence rule
    Major Section:  EVENTS
    

    Defcong is used to prove that one equivalence relation preserves
    another in a given argument position of a given function.
    Example:
    (defcong set-equal iff (memb x y) 2)
    
    is an abbreviation for
    (defthm set-equal-implies-iff-memb-2
      (implies (set-equal y y-equiv)
               (iff (memb x y) (memb x y-equiv)))
      :rule-classes (:congruence))
    
    See congruence and also see equivalence.

    General Form:
    (defcong equiv1 equiv2 term k
      :rule-classes rule-classes
      :instructions instructions
      :hints hints
      :otf-flg otf-flg
      :event-name event-name
      :doc doc)
    
    where equiv1 and equiv2 are known equivalence relations, term is a call of a function fn on the correct number of distinct variable arguments (fn x1 ... xn), k is a positive integer less than or equal to the arity of fn, and other arguments are as specified in the documentation for defthm. The defcong macro expands into a call of defthm. The name of the defthm event is equiv1-implies-equiv2-fn-k unless an :event-name keyword argument is supplied for the name. The term of the theorem is
    (implies (equiv1 xk yk)
             (equiv2 (fn x1... xk ...xn)
                     (fn x1... yk ...xn))).
    
    The rule-class :congruence is added to the rule-classes specified, if it is not already there. All other arguments to the generated defthm form are as specified by the keyword arguments above.




    acl2-sources/doc/HTML/DEFCONST.html0000664002132200015000000000621012222333516016155 0ustar kaufmannacl2 DEFCONST.html -- ACL2 Version 6.3

    DEFCONST

    define a constant
    Major Section:  EVENTS
    

    Examples:
    (defconst *digits* '(0 1 2 3 4 5 6 7 8 9))
    (defconst *n-digits* (the unsigned-byte (length *digits*)))
    
    General Form:
    (defconst name term doc-string)
    
    where name is a symbol beginning and ending with the character *, term is a variable-free term that is evaluated to determine the value of the constant, and doc-string is an optional documentation string (see doc-string).

    When a constant symbol is used as a term, ACL2 replaces it by its value; see term.

    Note that defconst uses a ``safe mode'' to evaluate its form, in order to avoids soundness issues but with an efficiency penalty (perhaps increasing the evaluation time by several hundred percent). If efficiency is a concern, or if for some reason you need the form to be evaluated without safe mode (e.g., you are an advanced system hacker using trust tags to traffic in raw Lisp code), consider using the macro defconst-fast instead, defined in community book books/make-event/defconst-fast.lisp, for example:

    (defconst-fast *x* (expensive-fn ...))
    
    A more general utility may be found in community book books/tools/defconsts.lisp. Also see using-tables-efficiently for an analogous issue with table events.

    It may be of interest to note that defconst is implemented at the lisp level using defparameter, as opposed to defconstant. (Implementation note: this is important for proper support of undoing and redefinition.)

    We close with a technical remark, perhaps of interest only to users of ACL2(h), the experimental extension of ACL2 that supports hash cons, function memoization, and hash-table-based ``fast alists''; see hons-and-memoization. For an event of the form (defconst *C* (quote OBJ)), i.e., (defconst *C* 'OBJ), then the value associated with *C* is OBJ; that is, the value of *C* is eq to the actual object OBJ occurring in the defconst form. So for example, if make-event is used to generate such a defconst event, as it is in the two books mentioned above, and OBJ is a fast alist (using ACL2(h)), then the value of *C* is a fast alist. This guarantee disappears if the term in the defconst form is not a quoted object, i.e., if it is not of the form (quote OBJ).




    acl2-sources/doc/HTML/DEFDOC.html0000664002132200015000000001053012222333516015674 0ustar kaufmannacl2 DEFDOC.html -- ACL2 Version 6.3

    DEFDOC

    add a documentation topic
    Major Section:  EVENTS
    

    Examples:
    (defdoc interp-section
       ":Doc-Section ...")
    
    General Form:
    (defdoc name doc-string)
    
    where name is a symbol or string to be documented and doc-string is a documentation string (see doc-string). This event adds the documentation string for symbol name to the :doc database. It may also be used to change the documentation for name if name already has documentation. The difference between this event and deflabel is that, unlike deflabel (but like table), it does not mark the current history with the name. But like deflabel, defdoc events are never considered redundant (see redundant-events).

    See deflabel for a means of attaching a documentation string to a name that marks the current history with that name. We now elaborate further on how defdoc may be useful in place of deflabel.

    It is usually sufficient to use deflabel when you might be tempted to use defdoc. However, unlike deflabel, defdoc does not mark the current history with name. Thus, defdoc is useful for introducing the documentation for a defun or deftheory event, for example, several events before the function or theory is actually defined.

    For example, suppose you want to define a theory (using deftheory). You need to prove the lemmas in that theory before executing the deftheory event. However, it is quite natural to define a :Doc-Section (see doc-string) whose name is the name of the theory to be defined, and put the documentation for that theory's lemmas into that :Doc-Section. Defdoc is ideal for this purpose, since it can be used to introduce the :Doc-Section, followed by the lemmas referring to that :Doc-Section, and finally concluded with a deftheory event of the same name. If deflabel were used instead of defdoc, for example, then the deftheory event would be disallowed because the name is already in use by the deflabel event.

    We also imagine that some users will want to use defdoc to insert the documentation for a function under development. This defdoc event would be followed by definitions of all the subroutines of that function, followed in turn by the function definition itself.

    Any time defdoc is used to attach documentation to an already-documented name, the name must not be attached to a new :Doc-Section. We make this requirement as a way of avoiding loops in the documentation tree. When documentation is redefined, a warning will be printed to the terminal.




    acl2-sources/doc/HTML/DEFEQUIV.html0000664002132200015000000000351012222333516016160 0ustar kaufmannacl2 DEFEQUIV.html -- ACL2 Version 6.3

    DEFEQUIV

    prove that a function is an equivalence relation
    Major Section:  EVENTS
    

    Example:
    (defequiv set-equal)
    
    is an abbreviation for
    (defthm set-equal-is-an-equivalence
      (and (booleanp (set-equal x y))
           (set-equal x x)
           (implies (set-equal x y) (set-equal y x))
           (implies (and (set-equal x y)
                         (set-equal y z))
                    (set-equal x z)))
      :rule-classes (:equivalence))
    
    See equivalence.

    General Form:
    (defequiv fn
      :rule-classes rule-classes
      :instructions instructions
      :hints hints
      :otf-flg otf-flg
      :event-name event-name
      :doc doc)
    
    where fn is a function symbol of arity 2, event-name, if supplied, is a symbol, and all other arguments are as specified in the documentation for defthm. The defequiv macro expands into a call of defthm. The name of the defthm is fn-is-an-equivalence, unless event-name is supplied, in which case event-name is the name used. The term generated for the defthm event states that fn is Boolean, reflexive, symmetric, and transitive. The rule-class :equivalence is added to the rule-classes specified, if it is not already there. All other arguments to the generated defthm form are as specified by the other keyword arguments above.




    acl2-sources/doc/HTML/DEFEVALUATOR.html0000664002132200015000000001206412222333516016635 0ustar kaufmannacl2 DEFEVALUATOR.html -- ACL2 Version 6.3

    DEFEVALUATOR

    introduce an evaluator function
    Major Section:  EVENTS
    

    Example:
    (defevaluator evl evl-list
      ((length x) (member-equal x y)))
    
    See meta.

    General Form:
    (defevaluator ev ev-list
       ((g1 x1 ... xn_1)
        ...
        (gk x1 ... xn_k))
    
    where ev and ev-list are new function symbols and g1, ..., gk are old function symbols with the indicated number of formals, i.e., each gi has n_i formals.

    This function provides a convenient way to constrain ev and ev-list to be mutually-recursive evaluator functions for the symbols g1, ..., gk. Roughly speaking, an evaluator function for a fixed, finite set of function symbols is a restriction of the universal evaluator to terms composed of variables, constants, lambda expressions, and applications of the given functions. However, evaluator functions are constrained rather than defined, so that the proof that a given metafunction is correct vis-a-vis a particular evaluator function can be lifted (by functional instantiation) to a proof that it is correct for any larger evaluator function. See meta for a discussion of metafunctions.

    Defevaluator executes an encapsulate after generating the appropriate defun and defthm events. Perhaps the easiest way to understand what defevaluator does is to execute the keyword command

    :trans1 (defevaluator evl evl-list ((length x) (member-equal x y)))
    
    and inspect the output. This trick is also useful in the rare case that the event fails because a hint is needed. In that case, the output of :trans1 can be edited by adding hints, then submitted directly.

    Formally, ev is said to be an ``evaluator function for g1, ..., gk, with mutually-recursive counterpart ev-list'' iff ev and ev-list are constrained functions satisfying just the constraints discussed below.

    Ev and ev-list must satisfy constraints (0)-(4) and (k):

    (0) How to ev an arbitrary function application:
        (implies (and (consp x)
                      (syntaxp (not (equal a ''nil)))
                      (not (equal (car x) 'quote)))
                 (equal (ev x a)
                        (ev (cons (car x)
                                  (kwote-lst (ev-list (cdr x) a)))
                            nil)))
    
    (1) How to ev a variable symbol:
        (implies (symbolp x)
                 (equal (ev x a) (and x (cdr (assoc-equal x a)))))
    
    (2) How to ev a constant:
        (implies (and (consp x)
                      (equal (car x) 'quote))
                 (equal (ev x a) (cadr x)))
    
    (3) How to ev a lambda application:
        (implies (and (consp x)
                      (consp (car x)))
                 (equal (ev x a)
                        (ev (caddar x)
                            (pairlis$ (cadar x)
                                      (ev-list (cdr x) a)))))
    
    (4) How to ev an argument list:
        (implies (consp x-lst)
                 (equal (ev-list x-lst a)
                        (cons (ev (car x-lst) a)
                              (ev-list (cdr x-lst) a))))
        (implies (not (consp x-lst))
                 (equal (ev-list x-lst a)
                        nil))
    
    (k) For each i from 1 to k, how to ev an application of gi,
        where gi is a function symbol of n arguments:
        (implies (and (consp x)
                      (equal (car x) 'gi))
                 (equal (ev x a)
                        (gi (ev x1 a)
                            ...
                            (ev xn a)))),
        where xi is the (cad...dr x) expression equivalent to (nth i x).
    
    Defevaluator defines suitable witnesses for ev and ev-list, proves the theorems about them, and constrains ev and ev-list appropriately. We expect defevaluator to work without assistance from you, though the proofs do take some time and generate a lot of output. The proofs are done in the context of a fixed theory, namely the value of the constant *defevaluator-form-base-theory*.

    (Aside: (3) above may seem surprising, since the bindings of a are not included in the environment that is used to evaluate the lambda body, (caddar x). However, ACL2 lambda expressions are all closed: in (lambda (v1 ... vn) body), the only free variables in body are among the vi. See term.)




    acl2-sources/doc/HTML/DEFEXEC.html0000664002132200015000000002406612222333516016024 0ustar kaufmannacl2 DEFEXEC.html -- ACL2 Version 6.3

    DEFEXEC

    attach a terminating executable function to a definition
    Major Section:  EVENTS
    

    Suppose you define a function (fn x) with a guard of (good-input-p x), and you know that when the guard holds, the measure decreases on each recursive call. Unfortunately, the definitional principle (see defun) ignores the guard. For example, if the definition has the form

    (defun fn (x)
      (declare (xargs :guard (good-input-p x)))
      (if (not-done-yet x)
          (... (fn (destr x)) ...)
        ...))
    
    then in order to admit this definition, ACL2 must prove the appropriate formula asserting that (destr x) is ``smaller than'' x under the assumption (not-done-yet x) but without the assumption (good-input-p x), even if (not-done-yet x) is true. In essence, it may be necessary to submit instead the following definition.
    (defun fn (x)
      (declare (xargs :guard (good-input-p x)))
      (if (good-input-p x)
          (if (not-done-yet x)
              (... (fn (destr x)) ...)
            ...)
        nil)
    
    But it is unfortunate that when calls of fn are evaluated, for example when fn is applied to an explicit constant during a proof, then a call of good-input-p must now be evaluated on each recursive call.

    Fortunately, defexec provides a way to keep the execution efficient. For the example above we could use the following form.

    (defexec fn (x)
      (declare (xargs :guard (good-input-p x)))
      (mbe :logic (if (good-input-p x)
                      (if (not-done-yet x)
                          (... (fn (destr x)) ...)
                        ...)
                    nil)
           :exec  (if (not-done-yet x)
                      (... (fn (destr x)) ...)
                    ...)))
    
    Here ``mbe'' stands for ``must be equal'' and, roughly speaking, its call above is logically equal to the :logic form but is evaluated using the :exec form when the guard holds. See mbe. The effect is thus to define fn as shown in the defun form above, but to cause execution of fn using the :exec body. The use of defexec instead of defun in the example above causes a termination proof to be performed, in order to guarantee that evaluation always theoretically terminates, even when using the :exec form for evaluation.
    Example:
    
    ; Some of the keyword arguments in the declarations below are irrelevant or
    ; unnecessary, but they serve to illustrate their use.
    
    (defexec f (x)
      (declare (xargs :measure (+ 15 (acl2-count x))
                      :ruler-extenders :basic
                      :hints (("Goal" :in-theory (disable nth)))
                      :guard-hints (("Goal" :in-theory (disable last)))
                      :guard (and (integerp x) (<= 0 x) (< x 25)))
               (exec-xargs
                      :test (and (integerp x) (<= 0 x))
                      :default-value 'undef ; defaults to nil
                      :measure (nfix x)
                      :ruler-extenders :basic
                      :well-founded-relation o<))
      (mbe :logic (if (zp x)
                      1
                    (* x (f (- x 1))))
           :exec  (if (= x 0)
                      1
                    (* x (f (- x 1))))))
    
    The above example macroexpands to the following.
    (ENCAPSULATE ()
     (LOCAL
      (ENCAPSULATE ()
       (SET-IGNORE-OK T)
       (SET-IRRELEVANT-FORMALS-OK T)
       (LOCAL (DEFUN F (X)
                (DECLARE
                 (XARGS :VERIFY-GUARDS NIL
                        :HINTS (("Goal" :IN-THEORY (DISABLE NTH)))
                        :MEASURE (NFIX X)
                        :RULER-EXTENDERS :BASIC
                        :WELL-FOUNDED-RELATION O<))
                (IF (AND (INTEGERP X) (<= 0 X))
                    (IF (= X 0) 1 (* X (F (- X 1))))
                    'UNDEF)))
       (LOCAL (DEFTHM F-GUARD-IMPLIES-TEST
                (IMPLIES (AND (INTEGERP X) (<= 0 X) (< X 25))
                         (AND (INTEGERP X) (<= 0 X)))
                :RULE-CLASSES NIL))))
     (DEFUN F (X)
       (DECLARE (XARGS :MEASURE (+ 15 (ACL2-COUNT X))
                       :RULER-EXTENDERS :BASIC
                       :HINTS (("Goal" :IN-THEORY (DISABLE NTH)))
                       :GUARD-HINTS (("Goal" :IN-THEORY (DISABLE LAST)))
                       :GUARD (AND (INTEGERP X) (<= 0 X) (< X 25))))
       (MBE :LOGIC
            (IF (ZP X) 1 (* X (F (- X 1))))
            :EXEC
            (IF (= X 0) 1 (* X (F (- X 1)))))))
    
    Notice that in the example above, the :hints in the local definition of F are inherited from the :hints in the xargs of the defexec form. We discuss such inheritance below.

    CAVEAT: Termination is not considered for calls of mbe under the top-level call. Moreover, the :exec part of an mbe call under the :logic part of any superior mbe call is completely ignored.

    General Form:
    (defexec fn (var1 ... varn) doc-string dcl ... dcl
      (mbe :LOGIC logic-body
           :EXEC  exec-body))
    
    where the syntax is identical to the syntax of defun where the body is a call of mbe, with the exceptions described below. Thus, fn is the symbol you wish to define and is a new symbolic name and (var1 ... varn) is its list of formal parameters (see name). The first exception is that at least one dcl (i.e., declare form) must specify a :guard, guard. The second exception is that one of the dcls is allowed to contain an element of the form (exec-xargs ...). The exec-xargs form, if present, must specify a non-empty keyword-value-listp each of whose keys is one of :test, :default-value, or one of the standard xargs keys of :measure, :ruler-extenders, :well-founded-relation, :hints, or :stobjs. Any of these five standard xargs keys that is present in an xargs of some dcl but is not specified in the (possibly nonexistent) exec-xargs form is considered to be specified in the exec-xargs form, as illustrated in the example above for :hints. (So for example, if you want :hints in the final, non-local definition but not in the local definition, then specify the :hints in the xargs but specify :hints nil in the exec-xargs.) If :test is specified and not nil, let test be its value; otherwise let test default to guard. If :default-value is specified, let default-value be its value; else default-value is nil. Default-value should have the same signature as exec-body; otherwise the defexec form will fail to be admitted.

    The above General Form's macroexpansion is of the form (PROGN encap final-def), where encap and final-def are as follows. Final-def is simply the result of removing the exec-xargs declaration (if any) from its declare form, and is the result of evaluating the given defexec form, since encap is of the following form.

    ; encap
    (ENCAPSULATE ()
      (set-ignore-ok t)             ; harmless for proving termination
      (set-irrelevant-formals-ok t) ; harmless for proving termination
      (local local-def)
      (local local-thm))
    
    The purpose of encap is to ensure the the executable version of name terminates on all arguments. Thus, local-def and local-thm are as follows, where the xargs of the declare form are the result of adding :VERIFY-GUARDS NIL to the result of removing the :test and (optional) :default-value from the exec-xargs.
    ; local-def
    (DEFUN fn formals
      (DECLARE (XARGS :VERIFY-GUARDS NIL ...))
      (IF test
          exec-body
        default-value))
    
    ; local-thm
    (DEFTHM fn-EXEC-GUARD-HOLDS
      (IMPLIES guard test)
      :RULE-CLASSES NIL)
    
    We claim that if the above local-def and local-thm are admitted, then all evaluations of calls of fn terminate. The concern is that the use of mbe in final-def allows for the use of exec-body for a call of fn, as well as for subsequent recursive calls, when guard holds and assuming that the guards have been verified for final-def. However, by local-thm we can conclude in this case that test holds, in which case the call of fn may be viewed as a call of the version of fn defined in local-def. Moreover, since guards have been verified for final-def, then guards hold for subsequent evaluation of exec-body, and in particular for recursive calls of fn, which can thus continue to be viewed as calls using local=def.




    acl2-sources/doc/HTML/DEFINE-PC-HELP.html0000664002132200015000000000304212222333525016730 0ustar kaufmannacl2 DEFINE-PC-HELP.html -- ACL2 Version 6.3

    DEFINE-PC-HELP

    define a macro command whose purpose is to print something
    Major Section:  PROOF-CHECKER
    

    Example:
    (define-pc-help pp ()
      (if (goals t)
          (io? proof-checker nil state
               (state-stack)
               (fms0 "~|~y0~|"
                     (list (cons #0
                                 (fetch-term (conc t)
                                             (current-addr t))))))
        (print-all-goals-proved-message state)))
    
    General Form:
    (define-pc-help name args &rest body)
    
    This defines a macro command named name, as explained further below. The body should (after removing optional declarations) be a form that returns state as its single value. Typically, it will just print something.

    What (define-pc-help name args &rest body) really does is to create a call of define-pc-macro that defines name to take arguments args, to have the declarations indicated by all but the last form in body, and to have a body that (via pprogn) first executes the form in the last element of body and then returns a call to the command skip (which will return (mv nil t state)).




    acl2-sources/doc/HTML/DEFINE-PC-MACRO.html0000664002132200015000000000406612222333525017050 0ustar kaufmannacl2 DEFINE-PC-MACRO.html -- ACL2 Version 6.3

    DEFINE-PC-MACRO

    define a proof-checker macro command
    Major Section:  PROOF-CHECKER
    

    Example:
    (define-pc-macro ib (&optional term)
      (value
       (if term
           `(then (induct ,term) bash)
         `(then induct bash))))
    
    The example above captures a common paradigm: one attempts to prove the current goal by inducting and then simplifying the resulting goals. (see proof-checker-commands for documentation of the command then, which is itself a pc-macro command, and commands induct and bash.) Rather than issuing (then induct bash), or worse yet issuing induct and then issuing bash for each resulting goals, the above definition of ib would let you issue ib and get the same effect.

    General Form:
    (define-pc-macro cmd args doc-string dcl ... dcl body)
    
    where cmd is the name of the pc-macro than you want to define, args is its list of formal parameters. Args may include lambda-list keywords &optional and &rest; see macro-args, but note that here, args may not include &key or &whole.

    The value of body should be an error triple (see error-triples), of the form (mv erp xxx state) for some erp and xxx. If erp is nil, then xxx is handed off to the proof-checker's instruction interpreter. Otherwise, evaluation typically halts. We may write more on the full story later if there is interest in reading it.




    acl2-sources/doc/HTML/DEFINE-PC-META.html0000664002132200015000000000155012222333525016730 0ustar kaufmannacl2 DEFINE-PC-META.html -- ACL2 Version 6.3

    DEFINE-PC-META

    define a proof-checker meta command
    Major Section:  PROOF-CHECKER
    

    Built-in proof-checker meta commands include undo and restore, and others (lisp, exit, and sequence); see proof-checker-commands. The advanced proof-checker user can define these as well. See ACL2 source file proof-checker-b.lisp for examples, and contact the ACL2 implementors if those examples do not provide sufficient documentation.




    acl2-sources/doc/HTML/DEFINE-TRUSTED-CLAUSE-PROCESSOR.html0000664002132200015000000003121212222333516021361 0ustar kaufmannacl2 DEFINE-TRUSTED-CLAUSE-PROCESSOR.html -- ACL2 Version 6.3

    DEFINE-TRUSTED-CLAUSE-PROCESSOR

    define a trusted (unverified) goal-level simplifier
    Major Section:  EVENTS
    

    This documentation assumes familiarity with :clause-processor rules; see clause-processor. Briefly put, a clause-processor is a user-defined function that takes as input the ACL2 representation of a goal -- a clause -- and returns a list of goals (i.e., a list of clauses). A :clause-processor rule is a way to inform ACL2 that a clause-processor has been proved correct and now may be specified in :clause-processor hints.

    Here we describe a utility, define-trusted-clause-processor, that provides another way to inform ACL2 that a function is to be considered a clause-processor that can be specified in a :clause-processor hint. You can find examples of correct and incorrect use of this utility in community book books/clause-processors/basic-examples.

    Consider the simple example already presented for :clause-processor rules (again, see clause-processor), for a simple clause-processor named note-fact-clause-processor. Instead of introducing an evaluator and proving a correctness theorem with :rule-classes :clause-processor, we can simply inform ACL2 that we trust the function note-fact-clause-processor to serve as a clause-processor.

    (define-trusted-clause-processor
      note-fact-clause-processor
      nil
      :ttag my-ttag)
    
    A non-nil :ttag argument generates a defttag event in order to acknowledge the dependence of the ACL2 session on the (unproved) correctness of this clause-processor. That argument can be omitted if there is currently an active trust tag. See defttag. Because we are trusting this clause-processor, rather than having proved it correct, we refer to it as a ``trusted'' clause-processor to contrast with a proved-correct, or ``verified'', clause-processor.

    Now that the event displayed above has established note-fact-clause-processor as a (trusted) clause-processor, we can use it in a :clause-processor hint, for example as follows. Notice that the output is identical to that for the corresponding example presented for the verified case (see clause-processor), except that the word ``verified'' has been replaced by the word ``trusted''.

    ACL2 !>(thm (equal (car (cons x y))
                       x)
                :hints
                (("Goal"
                  :clause-processor
                  (note-fact-clause-processor clause '(equal a a)))))
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    We now apply the trusted :CLAUSE-PROCESSOR function NOTE-FACT-CLAUSE-
    PROCESSOR to produce two new subgoals.
    
    Subgoal 2
    (IMPLIES (EQUAL A A)
             (EQUAL (CAR (CONS X Y)) X)).
    
    But we reduce the conjecture to T, by the :executable-counterpart of
    IF and the simple :rewrite rule CAR-CONS.
    
    Subgoal 1
    (EQUAL A A).
    
    But we reduce the conjecture to T, by primitive type reasoning.
    
    Q.E.D.
    
    Summary
    Form:  ( THM ...)
    Rules: ((:EXECUTABLE-COUNTERPART IF)
            (:EXECUTABLE-COUNTERPART NOT)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:REWRITE CAR-CONS))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Proof succeeded.
    ACL2 !>
    
    Indeed, if one runs this example first and subsequently verifies the clause-processor, one will see the word ``trusted'' change to ``verified''.

    The general form is as follows.

    (define-trusted-clause-processor
      cl-proc           ;;; clause-processor function
      supporters        ;;; see below
      &key
      label             ;;; optional, but required if doc is non-nil
      doc               ;;; optional
      ttag              ;;; discussed above
      partial-theory    ;;; optional encapsulate event
      )
    
    If a :label LAB is supplied, then a subsidiary deflabel event will be generated with name LAB, which will enable you to to undo this define-trusted-clause-processor event using: :ubt LAB. If you supply a :label then you may supply a :doc argument to use with that generated deflabel event. We discussed the :ttag argument above. The entire form is considered redundant (skipped) if it is identical to one already executed in the current ACL2 world; but if it is not redundant, then cl-proc must not already have been similarly designated as a trusted clause-processor.

    Note that cl-proc may be defined either in :program-mode or :logic-mode.

    The supporters argument should be a true list of function symbols in the current ACL2 world. It is important that this list include user-defined functions whose definitions support the correctness of the clause-processor function. Otherwise, local definitions of those missing supporters can render the use of this clause-processor unsound, as discussed in the paper referenced at the end of the clause-processor documentation topic. Moreover, ACL2 assumes for dependent clause-processors (discussed below) that every function symbol constrained by the ``promised encapsulate'' of that event is either among those supporters or ancestral in one of them (i.e. a supporter of a supporter, a supporter of one of those, etc.).

    Dependent clause-processors and promised encapsulates: The :partial-theory argument

    Suppose you want to introduce a clause-processor to reason about a complex hardware simulator that is implemented outside ACL2. Sawada and Reeber had just such a problem, as reported in their FMCAD 2006 paper. Indeed, they used sys-call to implement a :program-mode function in ACL2 that can invoke that simulator. In principle one could code the simulator directly in ACL2; but it would be a tremendous amount of work that has no practical purpose, given the interface to the external simulator. So: In what sense can we have a clause-processor that proves properties about a simulator when that simulator is not fully axiomatized in ACL2? Our answer, in a nutshell, is this: The above :partial-theory argument provides a way to write merely some of the constraints on the external tool (or even no constraints at all), with the understanding that such constraints are present implicitly in a stronger ``promised'' encapsulate, for example by exporting the full definition.

    If a trusted clause-processor is introduced with a :partial-theory argument, we call it a ``dependent'' clause-processor, because its correctness is dependent on the constraints implicitly introduced by the :partial-theory encapsulate form. The implicit constraints should logically imply the constraints actually introduced by the explicit encapsulate, but they should also be sufficient to justify every possible invocation of the clause-processor in a :clause-processor hint. The user of a define-trusted-clause-processor form is making a guarantee -- or, is relying on a guarantee provided by the writer of that form -- that in principle, there exists a so-called ``promised encapsulate'': an encapsulate form with the same signature as the :partial-theory encapsulate form associated with the trusted clause-processor, but whose constraints introduced are the aforementioned implicit constraints.

    There are several additional requirements on a :partial-theory argument. First, it must be an encapsulate event with non-empty signature. Moreover, the functions introduced by that event must be exactly those specified in the signature, and no more. And further still, the define-trusted-clause-processor form cannot be executed inside any encapsulate form with non-empty signature; we can think of this situation as attempting to associate more than one encapsulate with the functions introduced in the inner encapsulate.

    The :partial-theory event will (in essence) be executed as part of the evaluation of the define-trusted-clause-processor form. Again, a critical obligation rests on the user who provides a :partial-theory: there must exist (in principle at least) a corresponding promised encapsulate form with the same signature that could logically be admitted, whenever the above define-trusted-clause-processor form is evaluated successfully, that justifies the designation of cl-proc as a clause-processor. See also the paper mentioned above for more about promised encapsulates. A key consequence is that the constraints are unknown for the functions introduced in (the signature of) a :partial-theory encapsulate form. Thus, functional instantiation (see functional-instantiation-example) is disabled for function in the signature of a :partial-theory form.

    A remark on the underlying implementation

    You can see all of the current trusted clause-processors by issuing the command (table trusted-clause-processor-table). Those that are dependent clause-processors will be associated in the resulting association list with a pair whose car is the list of supporters and whose cdr is t, i.e., with (supporters . t); the others will be associated just with (supporters).

    Thus, define-trusted-clause-processor is actually a macro that generates (among other things) a table event for a table named trusted-clause-processor-table; see table. You are invited to use :trans1 to see expansions of calls of this macro.

    A technique for using raw Lisp to define a trusted clause-processor

    The following code is intended to give an idea for how one might define the ``guts'' of a trusted clause-processor in raw Lisp. The idea is to stub out functions, such as acl2-my-prove below, that you want to define in raw Lisp; and then, load a raw Lisp file to overwrite any such function with the real code. But then we make any such overwritten function untouchable. (This last step is important because otherwise, one can prove nil using a :functional-instance :use hint, by exploiting the fact that this function has executable code for which there is no corresponding definitional axiom.)

    (defstub acl2-my-prove (term hint) t)
    
    (program)
    
    (defttag :my-cl-proc)
    
    (progn
    
    ; We wrap everything here in a single progn, so that the entire form is
    ; atomic.  That's important because we want the use of push-untouchable to
    ; prevent anything besides my-clause-processor from calling acl2-my-prove.
    
      (progn!
    
       (set-raw-mode-on state)
    
       (load "my-hint-raw.lsp") ; defines my-prove in raw Lisp
    
       (defun acl2-my-prove (term hint)
         (my-prove term hint)))
    
      (defun my-clause-processor (cl hint)
        (declare (xargs :guard (pseudo-term-listp cl)
                        :mode :program))
        (if (acl2-my-prove (disjoin cl) hint)
            (disjoin-clause-segments-to-clause
             (pairlis$ (hint-to-termlist hint) nil)
             cl)
          (prog2$ (cw "~|~%NOTE: Unable to prove goal with ~
                      my-clause-processor and indicated hint.~|")
                  (list cl))))
    
      (push-untouchable acl2-my-prove t)
      )
    




    acl2-sources/doc/HTML/DEFINITION.html0000664002132200015000000002647012222333530016406 0ustar kaufmannacl2 DEFINITION.html -- ACL2 Version 6.3

    DEFINITION

    make a rule that acts like a function definition
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes and how they are used to build rules from formulas. An example :corollary formula from which a :definition rule might be built is:

    Examples:
    (defthm open-len-twice
      (implies (true-listp x)
               (equal (len x)
                      (if (null x)
                          0
                        (if (null (cdr x))
                            1
                          (+ 2 (len (cddr x)))))))
      :rule-classes :definition)
    
    ; Same as above, with :controller-alist made explicit:
    (defthm open-len-twice
      (implies (true-listp x)
               (equal (len x)
                      (if (null x)
                          0
                        (if (null (cdr x))
                            1
                          (+ 2 (len (cddr x)))))))
      :rule-classes ((:definition :controller-alist ((len t)))))
    
    General Form:
    (implies hyp (equiv (fn a1 ... an) body))
    
    where equiv is an equivalence relation and fn is a function symbol other than if, hide, force or case-split. Such rules allow ``alternative'' definitions of fn to be proved as theorems but used as definitions. These rules are not true ``definitions'' in the sense that they (a) cannot introduce new function symbols and (b) do not have to be terminating recursion schemes. They are just conditional rewrite rules that are controlled the same way we control recursive definitions. We call these ``definition rules'' or ``generalized definitions''.

    Consider the general form above. Generalized definitions are stored among the :rewrite rules for the function ``defined,'' fn above, but the procedure for applying them is a little different. During rewriting, instances of (fn a1 ... an) are replaced by corresponding instances of body provided the hyps can be established as for a :rewrite rule and the result of rewriting body satisfies the criteria for function expansion. There are two primary criteria, either of which permits expansion. The first is that the ``recursive'' calls of fn in the rewritten body have arguments that already occur in the goal conjecture. The second is that the ``controlling'' arguments to fn are simpler in the rewritten body.

    The notions of ``recursive call'' and ``controllers'' are complicated by the provisions for mutually recursive definitions. Consider a ``clique'' of mutually recursive definitions. Then a ``recursive call'' is a call to any function defined in the clique and an argument is a ``controller'' if it is involved in the measure that decreases in all recursive calls. These notions are precisely defined by the definitional principle and do not necessarily make sense in the context of generalized definitional equations as implemented here.

    But because the heuristics governing the use of generalized definitions require these notions, it is generally up to the user to specify which calls in body are to be considered recursive and what the controlling arguments are. This information is specified in the :clique and :controller-alist fields of the :definition rule class.

    The :clique field is the list of function symbols to be considered recursive calls of fn. In the case of a non-recursive definition, the :clique field is empty; in a singly recursive definition, it should consist of the singleton list containing fn; otherwise it should be a list of all of the functions in the mutually recursive clique with this definition of fn.

    If the :clique field is not provided it defaults to nil if fn does not occur as a function symbol in body and it defaults to the singleton list containing fn otherwise. Thus, :clique must be supplied by the user only when the generalized definition rule is to be treated as one of several in a mutually recursive clique.

    The :controller-alist is an alist that maps each function symbol in the :clique to a mask specifying which arguments are considered controllers. The mask for a given member of the clique, fn, must be a list of t's and nil's of length equal to the arity of fn. A t should be in each argument position that is considered a ``controller'' of the recursion. For a function admitted under the principle of definition, an argument controls the recursion if it is one of the arguments measured in the termination argument for the function. But in generalized definition rules, the user is free to designate any subset of the arguments as controllers. Failure to choose wisely may result in the ``infinite expansion'' of definitional rules but cannot render ACL2 unsound since the rule being misused is a theorem.

    If the :controller-alist is omitted it can sometimes be defaulted automatically by the system. If the :clique is nil, the :controller-alist defaults to nil. If the :clique is a singleton containing fn, the :controller-alist defaults to the controller alist computed by (defun fn args body). (The user can obtain some control over this analysis by setting the default ruler-extenders; see ruler-extenders.) If the :clique contains more than one function, the user must supply the :controller-alist specifying the controllers for each function in the clique. This is necessary since the system cannot determine and thus cannot analyze the other definitional equations to be included in the clique.

    For example, suppose fn1 and fn2 have been defined one way and it is desired to make ``alternative'' mutually recursive definitions available to the rewriter. Then one would prove two theorems and store each as a :definition rule. These two theorems would exhibit equations ``defining'' fn1 and fn2 in terms of each other. No provision is here made for exhibiting these two equations as a system of equations. One is proved and then the other. It just so happens that the user intends them to be treated as mutually recursive definitions. To achieve this end, both :definition rules should specify the :clique (fn1 fn2) and should specify a suitable :controller-alist. If, for example, the new definition of fn1 is controlled by its first argument and the new definition of fn2 is controlled by its second and third (and they each take three arguments) then a suitable :controller-alist would be ((fn1 t nil nil) (fn2 nil t t)). The order of the pairs in the alist is unimportant, but there must be a pair for each function in the clique.

    Inappropriate heuristic advice via :clique and :controller-alist can cause ``infinite expansion'' of generalized definitions, but cannot render ACL2 unsound.

    Note that the actual definition of fn1 has the runic name (:definition fn1). The runic name of the alternative definition is (:definition lemma), where lemma is the name given to the event that created the generalized :definition rule. This allows theories to switch between various ``definitions'' of the functions.

    By default, a :definition rule establishes the so-called ``body'' of a function. The body is used by :expand hints, and it is also used heuristically by the theorem prover's preprocessing (the initial simplification using ``simple'' rules that is controlled by the preprocess symbol in :do-not hints), induction analysis, and the determination for when to warn about non-recursive functions in rules. The body is also used by some heuristics involving whether a function is recursively defined, and by the expand, x, and x-dumb commands of the proof-checker.

    See rule-classes for a discussion of the optional field :install-body of :definition rules, which controls whether a :definition rule is used as described in the paragraph above. Note that even if :install-body nil is supplied, the rewriter will still rewrite with the :definition rule; in that case, ACL2 just won't install a new body for the top function symbol of the left-hand side of the rule, which for example affects the application of :expand hints as described in the preceding paragraph. Also see set-body and see show-bodies for how to change the body of a function symbol.

    Note only that if you prove a definition rule for function foo, say, foo-new-def, you will need to refer to that definition as foo-new-def or as (:DEFINITION foo-new-def). That is because a :definition rule does not change the meaning of the symbol foo for :use hints, nor does it change the meaning of the symbol foo in theory expressions; see theories, in particular the discussion there of runic designators. Similarly :pe foo and :pf foo will still show the original definition of foo.

    The definitional principle, defun, actually adds :definition rules. Thus the handling of generalized definitions is exactly the same as for ``real'' definitions because no distinction is made in the implementation. Suppose (fn x y) is defun'd to be body. Note that defun (or defuns or mutual-recursion) can compute the clique for fn from the syntactic presentation and it can compute the controllers from the termination analysis. Provided the definition is admissible, defun adds the :definition rule (equal (fn x y) body).




    acl2-sources/doc/HTML/DEFLABEL.html0000664002132200015000000000331312222333516016107 0ustar kaufmannacl2 DEFLABEL.html -- ACL2 Version 6.3

    DEFLABEL

    build a landmark and/or add a documentation topic
    Major Section:  EVENTS
    

    Examples:
    (deflabel interp-section
       :doc
       ":Doc-Section ...")
    
    General Form:
    (deflabel name :doc doc-string)
    
    where name is a new symbolic name (see name) and doc-string is an optional documentation string (see doc-string). This event adds the documentation string for symbol name to the :doc database. By virtue of the fact that deflabel is an event, it also marks the current history with the name. Thus, even undocumented labels are convenient as landmarks in a proof development. For example, you may wish to undo back through some label or compute a theory expression (see theories) in terms of some labels. Deflabel events are never considered redundant. See redundant-events.

    See defdoc for a means of attaching a documentation string to a name without marking the current history with that name.




    acl2-sources/doc/HTML/DEFLOCK.html0000664002132200015000000000772512222333522016030 0ustar kaufmannacl2 DEFLOCK.html -- ACL2 Version 6.3

    DEFLOCK

    define a wrapper macro that provides mutual exclusion in ACL2(p)
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel evaluation and proof; see parallelism.

    Example Form:
    (deflock *my-lock*)
    
    General Form:
    (deflock *symbol*)
    
    where *symbol* is a symbol whose first and last characters are both the character #\*.

    A call of this macro generates a definition of another macro, named with-<modified-lock-symbol>, where <modified-lock-symbol> is the given symbol with the leading and trailing * characters removed. This newly defined macro will guarantee mutually exclusive execution when called in the body of the raw Lisp definition of a function, as is typically the case for guard-verified functions, for :program mode functions, and for calls of macro top-level. (See guard-evaluation-table for details of how raw Lisp code might not be invoked when guard-checking (see set-guard-checking) has value :none or :all.)

    To see how mutual exclusion is guaranteed, consider the raw Lisp code generated for the macro, with-<modified-lock-symbol>, that is introduced by a call of deflock. This code uses a lock (with the given *symbol* as its name), which guarantees that for any two forms that are each in the scope of a call of with-<modified-lock-symbol>, the forms do not execute concurrently.

    Note that a call of deflock expands into the application of progn to two events, as illustrated below.

    ACL2 !>:trans1 (deflock *my-cw-lock*)
     (PROGN (TABLE LOCK-TABLE '*MY-CW-LOCK* T)
            (DEFMACRO WITH-MY-CW-LOCK (&REST ARGS)
                      (LIST* 'WITH-LOCK '*MY-CW-LOCK* ARGS)))
    ACL2 !>
    
    Thus, deflock forms are legal embedded event forms (see embedded-event-form) for books as well as encapsulate and progn events.

    The following log shows a lock in action. Recall that locks work as expected in guard-verified and :program mode functions; they do not, however, work in :logic mode functions that have not been guard-verified, as illustrated below.

    ACL2 !>(deflock *my-cw-lock*)
    [[.. output omitted ..]]
     WITH-MY-CW-LOCK
    ACL2 !>(defun foo (n)
             (declare (xargs :guard (natp n) :verify-guards nil))
             (plet ((x1 (with-my-cw-lock (cw "~x0" (make-list n))))
                    (x2 (with-my-cw-lock (cw "~x0" (make-list n)))))
                   (and (null x1) (null x2))))
    [[.. output omitted ..]]
     FOO
    ACL2 !>(foo 20)
    (NIL NIL NIL NIL( NIL NIL NIL NIL NIL NILNIL  NIL NILNIL  NIL NILNIL
         NIL NILNIL NIL  NIL NILNIL  NIL NIL
    NIL      NILNIL  NIL NILNIL )
    NIL NIL NIL NIL NIL NIL NIL NIL)
    T
    ACL2 !>(verify-guards foo)
    [[.. output omitted ..]]
     FOO
    ACL2 !>(foo 20)
    (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL
         NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
    (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL
         NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL)
    T
    ACL2 !>
    




    acl2-sources/doc/HTML/DEFMACRO-LAST.html0000664002132200015000000000107612222333516016676 0ustar kaufmannacl2 DEFMACRO-LAST.html -- ACL2 Version 6.3

    DEFMACRO-LAST

    define a macro that returns its last argument, but with side effects
    Major Section:  EVENTS
    

    This is an advanced feature that requires a trust tag. For explanation, including an example, see return-last.




    acl2-sources/doc/HTML/DEFMACRO.html0000664002132200015000000001053412222333516016134 0ustar kaufmannacl2 DEFMACRO.html -- ACL2 Version 6.3

    DEFMACRO

    define a macro
    Major Section:  EVENTS
    

    Example Defmacros:
    (defmacro xor (x y)
      (list 'if x (list 'not y) y))
    
    (defmacro git (sym key)
      (list 'getprop sym key nil
            '(quote current-acl2-world)
            '(w state)))
    
    (defmacro one-of (x &rest rst)
      (declare (xargs :guard (symbol-listp rst)))
      (cond ((null rst) nil)
            (t (list 'or
                     (list 'eq x (list 'quote (car rst)))
                     (list* 'one-of x (cdr rst))))))
    
    Example Expansions:
    term                    macroexpansion
    
    (xor a b)              (if a (not b) b)
    (xor a (foo b))        (if a (not (foo b)) (foo b))
    
    (git 'car 'lemmas)     (getprop 'car 'lemmas nil
                                    'current-acl2-world
                                    (w state))
    
    (one-of x a b c)       (or (eq x 'a)
                               (or (eq x 'b)
                                   (or (eq x 'c) nil)))
    
    (one-of x 1 2 3)       ill-formed (guard violation)
    
    General Form:
    (defmacro name macro-args doc-string dcl ... dcl body)
    
    where name is a new symbolic name (see name), macro-args specifies the formal parameters of the macro, and body is a term. The formal parameters can be specified in a much more general way than is allowed by ACL2 defun events; see macro-args for a description of keyword (&key) and optional (&optional) parameters as well as other so-called ``lambda-list keywords'', &rest and &whole. Doc-string is an optional documentation string; see doc-string. Each dcl is an optional declaration (see declare) except that the only xargs keyword permitted by defmacro is :guard.

    For compute-intensive applications see the community book misc/defmac.lisp, which can speed up macroexpansion by introducing an auxiliary defun. For more information, evaluate the form (include-book "misc/defmac" :dir :system) and then evaluate :doc defmac.

    Macroexpansion occurs when a form is read in, i.e., before the evaluation or proof of that form is undertaken. To experiment with macroexpansion, see trans. When a form whose car is name arises as the form is read in, the arguments are bound as described in CLTL pp. 60 and 145, the guard is checked, and then the body is evaluated. The result is used in place of the original form.

    In ACL2, macros do not have access to the ACL2 state state. (If state or any user-defined stobj (see stobj) is a macro argument, it is treated as an ordinary variable, bound at macro-expansion time to a piece of syntax.) This is in part a reflection of CLTL, p. 143, ``More generally, an implementation of Common Lisp has great latitude in deciding exactly when to expand macro calls with a program. ... Macros should be written in such a way as to depend as little as possible on the execution environment to produce a correct expansion.'' In ACL2, the product of macroexpansion is independent of the current environment and is determined entirely by the macro body and the functions and constants it references. It is possible, however, to define macros that produce expansions that refer to state or other single-threaded objects (see stobj) or variables not among the macro's arguments. See the git example above. For a related utility that does have access to the ACL2 state, see make-event.




    acl2-sources/doc/HTML/DEFN.html0000664002132200015000000000101312222333516015460 0ustar kaufmannacl2 DEFN.html -- ACL2 Version 6.3

    DEFN

    definition with guard t
    Major Section:  EVENTS
    

    Defn is defun with guard t.




    acl2-sources/doc/HTML/DEFND.html0000664002132200015000000000106412222333516015572 0ustar kaufmannacl2 DEFND.html -- ACL2 Version 6.3

    DEFND

    disabled definition with guard t
    Major Section:  EVENTS
    

    Defnd is defund with guard t.




    acl2-sources/doc/HTML/DEFPKG.html0000664002132200015000000001537312222333516015722 0ustar kaufmannacl2 DEFPKG.html -- ACL2 Version 6.3

    DEFPKG

    define a new symbol package
    Major Section:  EVENTS
    

    Example:
    (defpkg "MY-PKG"
            (union-eq *acl2-exports*
                      *common-lisp-symbols-from-main-lisp-package*))
    
    
    

    Some Related Topics

    General Form: (defpkg "name" term doc-string)

    where "name" is a non-empty string consisting of standard characters (see standard-char-p), none of which is lower case, that names the package to be created; term is a variable-free expression that evaluates to a list of symbols, where no two distinct symbols in the list may have the same symbol-name, to be imported into the newly created package; and doc-string is an optional documentation string; see doc-string. The name of the new package must be ``new'': the host lisp must not contain any package of that name. There are two exceptions to this newness rule, discussed at the end of this documentation.

    (There is actually an additional argument, book-path, that is used for error reporting but has no logical content. Users should generally ignore this argument, as well as the rest of this sentence: a book-path will be specified for defpkg events added by ACL2 to the portcullis of a book's certificate; see hidden-death-package.)

    Defpkg forms can be entered at the top-level of the ACL2 command loop. They should not occur in books (see certify-book).

    After a successful defpkg it is possible to ``intern'' a string into the package using intern-in-package-of-symbol. The result is a symbol that is in the indicated package, provided the imports allow it. For example, suppose 'my-pkg::abc is a symbol whose symbol-package-name is "MY-PKG". Suppose further that the imports specified in the defpkg for "MY-PKG" do not include a symbol whose symbol-name is "XYZ". Then

    (intern-in-package-of-symbol "XYZ" 'my-pkg::abc)
    
    returns a symbol whose symbol-name is "XYZ" and whose symbol-package-name is "MY-PKG". On the other hand, if the imports to the defpkg does include a symbol with the name "XYZ", say in the package "LISP", then
    (intern-in-package-of-symbol "XYZ" 'my-pkg::abc)
    
    returns that symbol (which is uniquely determined by the restriction on the imports list above). See intern-in-package-of-symbol.

    Upon admission of a defpkg event, the function pkg-imports is extended to compute a list of all symbols imported into the given package, without duplicates.

    Defpkg is the only means by which an ACL2 user can create a new package or specify what it imports. That is, ACL2 does not support the Common Lisp functions make-package or import. Currently, ACL2 does not support exporting at all.

    The Common Lisp function intern is weakly supported by ACL2; see intern. A more general form of that function is also provided: see intern$.

    We now explain the two exceptions to the newness rule for package names. The careful experimenter will note that if a package is created with a defpkg that is subsequently undone, the host lisp system will contain the created package even after the undo. Because ACL2 hangs onto worlds after they have been undone, e.g., to implement :oops but, more importantly, to implement error recovery, we cannot actually destroy a package upon undoing it. Thus, the first exception to the newness rule is that name is allowed to be the name of an existing package if that package was created by an undone defpkg and the newly proposed set of imports is identical to the old one. See package-reincarnation-import-restrictions. This exception does not violate the spirit of the newness rule, since one is disinclined to believe in the existence of undone packages. The second exception is that name is allowed to be the name of an existing package if the package was created by a defpkg with identical set of imports. That is, it is permissible to execute ``redundant'' defpkg commands. The redundancy test is based on the values of the two import forms (comparing them after sorting and removing duplicates), not on the forms themselves.

    Finally, we explain why we require the package name to contain standard characters, none of which is lower case. We have seen at least one implementation that handled lower-case package names incorrectly. Since we see no need for lower-case characters in package names, which can lead to confusion anyhow (note for example that foo::bar is a symbol whose symbol-package-name is "FOO", not "foo"), we simply disallow them. Since the notion of ``lower case'' is only well-specified in Common Lisp for standard characters, we restrict to these.

    NOTE: Also see managing-acl2-packages for contributed documentation on managing ACL2 packages.




    acl2-sources/doc/HTML/DEFPROXY.html0000664002132200015000000003024412222333516016214 0ustar kaufmannacl2 DEFPROXY.html -- ACL2 Version 6.3

    DEFPROXY

    define a non-executable :program-mode function for attachment
    Major Section:  EVENTS
    

    This event is provided for those who want to experiment with defattach using :program mode functions, and without proof obligations or constraints on cycles in the extended ancestors graph; see defattach. If you merely want to define a stub or a non-executable function, see defstub or see defun-nx, respectively.

    See community book books/misc/defproxy-test.lisp for an extended (but simple) example.

    Example Forms:
    (defproxy subr1 (* *) => *)
    (defproxy add-hash (* * hashtable) => (mv * hashtable))
    
    General Form:
    (defproxy name args-sig => output-sig)
    
    where name is a new function symbol and (name . args-sig) => output-sig) is a signature; see signature.

    The macro defproxy provides a convenient way to introduce a ``proxy'': a :program mode function that can be given attachments for execution (see defattach), assuming that there is an active trust tag (see defttag). Thus, a defproxy calls expands to a defun form with the following xargs declare form: :non-executable :program. Note that verify-termination is not permitted for such a function. However, it is permitted to put the proxy function into :logic mode by use of an encapsulate event; indeed, this is the way to ``upgrade'' an attachment so that the normal checks are performed and no trust tag is necessary.

    In order to take advantage of a defproxy form, one provides a subsequent defattach form to attach an executable function to the defproxy-introduced function. When :skip-checks t is provided in a defattach form, the usual checks for defattach events are skipped, including proof obligations and the check that the extended ancestor relation has no cycles (see defattach). There must be an active trust tag (see defttag) in order to use :skip-checks t. In that case the use of :skip-checks t is permitted; but note that its use is in fact required if a :program mode function is involved, and even if a :logic mode function is involved that has not been guard-verified.

    The following log shows a simple use of defproxy.

    ACL2 !>(defproxy foo-stub (*) => *)
    
    Summary
    Form:  ( DEFUN FOO-STUB ...)
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     FOO-STUB
    ACL2 !>(foo-stub '(3 4 5))
    
    
    ACL2 Error in TOP-LEVEL:  ACL2 cannot ev the call of undefined function
    FOO-STUB on argument list:
    
    ((3 4 5))
    
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.
    
    ACL2 !>(defun foo-impl (x)
             (declare (xargs :mode :program
                             :guard (or (consp x) (eq x nil))))
             (car x))
    
    Summary
    Form:  ( DEFUN FOO-IMPL ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO-IMPL
    ACL2 !>(defttag t)
    
    TTAG NOTE: Adding ttag :T from the top level loop.
     T
    ACL2 !>(defattach (foo-stub foo-impl) :skip-checks t)
    
    Summary
    Form:  ( DEFATTACH (FOO-STUB FOO-IMPL) ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     :ATTACHMENTS-RECORDED
    ACL2 !>(foo-stub '(3 4 5))
    3
    ACL2 !>
    

    One can replace this attachment with one that uses :logic mode functions and does not skip checks. The idea is to reintroduce the proxy function using an encapsulate form, which does not require redefinition (see ld-redefinition-action) to be enabled, and either to put the attachment into :logic mode with the guard verified, as we do in the example below, or else to attach to a different guard-verified :logic mode function.

    ACL2 !>(defattach (foo-stub nil) :skip-checks t) ; remove attachment
    
    Summary
    Form:  ( DEFATTACH (FOO-STUB NIL) ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     :ATTACHMENTS-RECORDED
    ACL2 !>(encapsulate
            ((foo-stub (x) t :guard (true-listp x)))
            (local (defun foo-stub (x) (cdr x)))
            (defthm foo-stub-reduces-acl2-count
              (implies (consp x)
                       (< (acl2-count (foo-stub x))
                          (acl2-count x)))))
    
    [[ ... output omitted here ... ]]
    
    The following constraint is associated with the function FOO-STUB:
    
    (IMPLIES (CONSP X) (< (ACL2-COUNT (FOO-STUB X)) (ACL2-COUNT X)))
    
    Summary
    Form:  ( ENCAPSULATE ((FOO-STUB ...) ...) ...)
    Rules: NIL
    Warnings:  Non-rec
    Time:  0.02 seconds (prove: 0.01, print: 0.00, other: 0.01)
     T
    ACL2 !>(verify-termination foo-impl)
    
    Since FOO-IMPL is non-recursive, its admission is trivial.  We could
    deduce no constraints on the type of FOO-IMPL.
    
    Computing the guard conjecture for FOO-IMPL....
    
    The guard conjecture for FOO-IMPL is trivial to prove.  FOO-IMPL is
    compliant with Common Lisp.
    
    Summary
    Form:  ( DEFUN FOO-IMPL ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Summary
    Form:  ( MAKE-EVENT (VERIFY-TERMINATION-FN ...))
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     FOO-IMPL
    ACL2 !>(defttag nil) ; optional
     NIL
    ACL2 !>(defattach (foo-stub foo-impl))
    
    The guard proof obligation is
    
    (IMPLIES (TRUE-LISTP X)
             (OR (CONSP X) (EQ X NIL))).
    
    But we reduce the conjecture to T, by primitive type reasoning.
    
    Q.E.D.
    
    This concludes the guard proof.
    
    
    We now prove that the attachment satisfies the required constraint.
    The goal to prove is
    
    (IMPLIES (CONSP X)
             (< (ACL2-COUNT (FOO-IMPL X))
                (ACL2-COUNT X))).
    
    [[ ... output omitted here ... ]]
    
    Q.E.D.
    
    Summary
    Form:  ( DEFATTACH (FOO-STUB FOO-IMPL))
    Rules: ((:DEFINITION ACL2-COUNT)
            (:DEFINITION FOO-IMPL)
            (:ELIM CAR-CDR-ELIM)
            (:FAKE-RUNE-FOR-LINEAR NIL)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:REWRITE CAR-CONS)
            (:REWRITE CDR-CONS)
            (:TYPE-PRESCRIPTION ACL2-COUNT))
    Time:  0.02 seconds (prove: 0.01, print: 0.01, other: 0.00)
     :ATTACHMENTS-RECORDED
    ACL2 !>
    

    We close with some remarks on the checking of guards in the case that defattach has been called with keyword argument :skip-checks t. We illustrate with examples, where we assume an attachment pair (f . g) created by an event (defattach ... (f g) ... :skip-checks t ...). A good model for the treatment of :skip-checks t is dependent on whether f was introduced with defproxy or with encapsulate: for defproxy, the normal guard-related checks are treated as skipped, while for encapsulate, they are assumed to hold.

    First suppose that f was introduced using defproxy, and consider the following example.

    (defproxy f (*) => *)
    (defun g (x) (car x)) ; not guard-verified; implicit guard of t is too weak
    (defttag t) ; trust tag needed for :skip-checks t
    (defattach (f g) :skip-checks t)
    
    If we try to evaluate the form (f 3) in ACL2, then the top-level so-called ``executable counterpart'' (i.e., the logically-defined funcction, also known as the ``*1*'' function) of f is invoked. It calls the executable counterpart of g, which calls the executable counterpart of car, which in turn checks the guard of car and causes a guard violation error (unless we first turn off guard-checking; see set-guard-checking).
    ACL2 !>(trace$ f g)
     ((F) (G))
    ACL2 !>(f 3)
    1> (ACL2_*1*_ACL2::F 3)
      2> (ACL2_*1*_ACL2::G 3)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (CAR X),
    which is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments
    in the call (CAR 3).  To debug see :DOC print-gv, see :DOC trace, and
    see :DOC wet.  See :DOC set-guard-checking for information about suppressing
    this check with (set-guard-checking :none), as recommended for new
    users.
    
    ACL2 !>
    

    Little changes if we modify the example above by strengtheing the guard of g.

    (defproxy f (*) => *)
    (defun g (x)
      (declare (xargs :guard (consp x)))
      (car x))
    (defttag t) ; trust tag needed for :skip-checks t
    (defattach (f g) :skip-checks t)
    
    The result of evaluating (f 3) is as before, except that this time the guard violation occurs at the time that g is called.
    ACL2 !>(trace$ f g)
     ((F) (G))
    ACL2 !>(f 3)
    1> (ACL2_*1*_ACL2::F 3)
      2> (ACL2_*1*_ACL2::G 3)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (G X), which
    is (CONSP X), is violated by the arguments in the call (G 3).  To debug
    see :DOC print-gv, see :DOC trace, and see :DOC wet.  See :DOC set-
    guard-checking for information about suppressing this check with (set-
    guard-checking :none), as recommended for new users.
    
    ACL2 !>
    

    Now consider a slight variation of the example just above, in which f is introduced using encapsulate instead of using defproxy.

    (encapsulate ( ((f *) => *) )
                 (local (defun f (x) x)))
    (defun g (x)
      (declare (xargs :guard (consp x)))
      (car x))
    (defttag t) ; trust tag needed for :skip-checks t
    (defattach (f g) :skip-checks t)
    
    Since f was introduced by encapsulate instead of by defproxy, ACL2 assumes that the usual guard properties hold. In particular, it assumes that (informally speaking) the guard of f implies the guard of g; see defattach for details. So in this case, ACL2 proceeds under that assumption even though it's actually false, and the result is a raw Lisp error.

    ACL2 !>(trace$ f g)
     ((F) (G))
    ACL2 !>(f 3)
    1> (ACL2_*1*_ACL2::F 3)
      2> (G 3)
    
    ***********************************************
    ************ ABORTING from raw Lisp ***********
    Error:  Attempt to take the car of 3 which is not listp.
    ***********************************************
    
    If you didn't cause an explicit interrupt (Control-C),
    then the root cause may be call of a :program mode
    function that has the wrong guard specified, or even no
    guard specified (i.e., an implicit guard of t).
    See :DOC guards.
    
    To enable breaks into the debugger (also see :DOC acl2-customization):
    (SET-DEBUGGER-ENABLE T)
    ACL2 !>
    

    If you replace g by its definition in the first example of this series, i.e. with a guard (implicitly) of t, you will see the same error, this time because the defattach event assumed that g was guard-verified.




    acl2-sources/doc/HTML/DEFPUN.html0000664002132200015000000000342512222333516015736 0ustar kaufmannacl2 DEFPUN.html -- ACL2 Version 6.3

    DEFPUN

    define a tail-recursive function symbol
    Major Section:  EVENTS
    

    Defpun is a macro developed by Pete Manolios and J Moore that allows tail-recursive definitions. It is defined in community book books/misc/defpun.lisp, so to use it, execute the following event.

    (include-book "misc/defpun" :dir :system)
    
    Details of defpun are provided by Manolios and Moore in the ``Partial Functions in ACL2'' published with the ACL2 2000 workshop; see http://www.cs.utexas.edu/users/moore/acl2/workshop-2000/. Also see http://www.cs.utexas.edu/users/moore/publications/defpun/index.html.

    A variant, defp, has been developed by Matt Kaufmann to allow more general forms of tail recursion. If defpun doesn't work for you, try defp by first executing the following event.

    (include-book "misc/defp" :dir :system)
    

    Sandip Ray has contributed a variant of defpun, defpun-exec, that supports executability. See community book books/defexec/defpun-exec/defpun-exec.lisp:

    (include-book "defexec/defpun-exec/defpun-exec" :dir :system)
    
    He has also contributed community book books/misc/misc2/defpun-exec-domain-example.lisp, for functions that are uniquely defined in a particular domain.




    acl2-sources/doc/HTML/DEFREFINEMENT.html0000664002132200015000000000316112222333516016725 0ustar kaufmannacl2 DEFREFINEMENT.html -- ACL2 Version 6.3

    DEFREFINEMENT

    prove that equiv1 refines equiv2
    Major Section:  EVENTS
    

    Example:
    (defrefinement equiv1 equiv2)
    
    is an abbreviation for
    (defthm equiv1-refines-equiv2
      (implies (equiv1 x y) (equiv2 x y))
      :rule-classes (:refinement))
    
    See refinement.

    General Form:
    (defrefinement equiv1 equiv2
      :rule-classes rule-classes
      :instructions instructions
      :hints hints
      :otf-flg otf-flg
      :event-name event-name
      :doc doc)
    
    where equiv1 and equiv2 are known equivalence relations, event-name, if supplied, is a symbol and all other arguments are as specified in the documentation for defthm. The defrefinement macro expands into a call of defthm. The name supplied is equiv1-refines-equiv2, unless event-name is supplied, in which case it is used as the name. The term supplied states that equiv1 refines equiv2. The rule-class :refinement is added to the rule-classes specified, if it is not already there. All other arguments to the generated defthm form are as specified by the other keyword arguments above.




    acl2-sources/doc/HTML/DEFSTOBJ.html0000664002132200015000000005526112222333516016162 0ustar kaufmannacl2 DEFSTOBJ.html -- ACL2 Version 6.3

    DEFSTOBJ

    define a new single-threaded object
    Major Section:  EVENTS
    

    Note: Novices are advised to avoid defstobj, perhaps instead using community books books/cutil/defaggregate.lisp or books/data-structures/structures.lisp. At the least, consider using (set-verify-guards-eagerness 0) to avoid guard verification. On the other hand, after you learn to use defstobj, see defabsstobj for another way to introduce single-threaded objects.

    Example:
    (defconst *mem-size* 10) ; for use of *mem-size* just below
    (defstobj st
              (reg :type (array (unsigned-byte 31) (8))
                   :initially 0)
              (p-c :type (unsigned-byte 31)
                   :initially 555)
              halt                  ; = (halt :type t :initially nil)
              (mem :type (array (unsigned-byte 31) (*mem-size*))
                   :initially 0 :resizable t))
    
    General Form:
    (defstobj name
              (field1 :type type1 :initially val1 :resizable b1)
              ...
              (fieldk :type typek :initially valk :resizable bk)
              :renaming alist
              :doc doc-string
              :inline flg
              :congruent-to old-stobj-name)
    
    where name is a new symbol, each fieldi is a symbol, each typei is either a type-indicator (a type-spec or stobj name) or of the form (ARRAY type-indicator max), each vali is an object satisfying typei, and each bi is t or nil. Each pair :initially vali and :resizable bi may be omitted; more on this below. The :renaming alist argument is optional and allows the user to override the default function names introduced by this event. The doc-string is also optional. The :inline flg Boolean argument is also optional and declares to ACL2 that the generated access and update functions for the stobj should be implemented as macros under the hood (which has the effect of inlining the function calls). The optional :congruent-to old-stobj-name argument specifies an existing stobj with exactly the same structure, and is discussed below. We describe further restrictions on the fieldi, typei, vali, and on alist below. We recommend that you read about single-threaded objects (stobjs) in ACL2 before proceeding; see stobj.

    The effect of this event is to introduce a new single-threaded object (i.e., a ``stobj''), named name, and the associated recognizers, creator, accessors, updaters, constants, and, for fields of ARRAY type, length and resize functions.

    The Single-Threaded Object Introduced

    The defstobj event effectively introduces a new global variable, named name, which has as its initial logical value a list of k elements, where k is the number of ``field descriptors'' provided. The elements are listed in the same order in which the field descriptors appear. If the :type of a field is (ARRAY type-indicator (max)) then max is a non-negative integer or a symbol introduced by defconst) whose value is a non-negative integer, and the corresponding element of the stobj is initially of length specified by max.

    Whether the value :type is of the form (ARRAY type-indicator (max)) or, otherwise, just type-indicator, then type-indicator is typically a type-spec; see type-spec. However, type-indicator can also be the name of a stobj that was previously introduced (by defstobj or defabsstobj). We ignore this ``nested stobj'' case below; see nested-stobjs for a discussion of stobjs within stobjs.

    The keyword value :initially val specifies the initial value of a field, except for the case of a :type (ARRAY type-indicator (max)), in which case val is the initial value of the corresponding array.

    Note that the actual representation of the stobj in the underlying Lisp may be quite different; see stobj-example-2. For the moment we focus entirely on the logical aspects of the object.

    In addition, the defstobj event introduces functions for recognizing and creating the stobj and for recognizing, accessing, and updating its fields. For fields of ARRAY type, length and resize functions are also introduced. Constants are introduced that correspond to the accessor functions.

    Restrictions on the Field Descriptions in Defstobj

    Each field descriptor is of the form:

    (fieldi :TYPE typei :INITIALLY vali)
    
    Note that the type and initial value are given in ``keyword argument'' format and may be given in either order. The typei and vali ``arguments'' are not evaluated. If omitted, the type defaults to t (unrestricted) and the initial value defaults to nil.

    Each typei must be either a type-spec or else a list of the form (ARRAY type-spec (max)). (Again, we are ignoring the case of nested stobjs, discussed elsewhere; see nested-stobjs.) The latter forms are said to be ``array types.'' Examples of legal typei are:

    (INTEGER 0 31)
    (SIGNED-BYTE 31)
    (ARRAY (SIGNED-BYTE 31) (16))
    (ARRAY (SIGNED-BYTE 31) (*c*)) ; where *c* has a non-negative integer value
    

    The typei describes the objects which are expected to occupy the given field. Those objects in fieldi should satisfy typei. We are more precise below about what we mean by ``expected.'' We first present the restrictions on typei and vali.

    Non-Array Types

    When typei is a type-spec it restricts the contents, x, of fieldi according to the ``meaning'' formula given in the table for type-spec. For example, the first typei above restricts the field to be an integer between 0 and 31, inclusive. The second restricts the field to be an integer between -2^30 and (2^30)-1, inclusive.

    The initial value, vali, of a field description may be any ACL2 object but must satisfy typei. Note that vali is not a form to be evaluated but an object. A form that evaluates to vali could be written 'vali, but defstobj does not expect you to write the quote mark. For example, the field description

    (days-off :initially (saturday sunday))
    
    describes a field named days-off whose initial value is the list consisting of the two symbols SATURDAY and SUNDAY. In particular, the initial value is NOT obtained by applying the function saturday to the variable sunday! Had we written
    (days-off :initially '(saturday sunday))
    
    it would be equivalent to writing
    (days-off :initially (quote (saturday sunday)))
    
    which would initialize the field to a list of length two, whose first element is the symbol quote and whose second element is a list containing the symbols saturday and sunday.

    Array Types

    When typei is of the form (ARRAY type-spec (max)), the field is supposed to be a list of items, initially of length specified by max, each of which satisfies the indicated type-spec. Max must be a non-negative integer or a defined constant evaluating to a non-negative integer. Thus, each of

    (ARRAY (SIGNED-BYTE 31) (16))
    (ARRAY (SIGNED-BYTE 31) (*c*)) ; given previous event (defconst *c* 16)
    
    restricts the field to be a list of integers, initially of length 16, where each integer in the list is a (SIGNED-BYTE 31). We sometimes call such a list an ``array'' (because it is represented as an array in the underlying Common Lisp). The elements of an array field are indexed by position, starting at 0. Thus, the maximum legal index of an array field one less than is specified by max. Note that the value of max must be less than the Common Lisp constant array-dimension-limit, and also (though this presumably follows) less than the Common Lisp constant array-total-size-limit.

    Note also that the ARRAY type requires that the max be enclosed in parentheses. This makes ACL2's notation consistent with the Common Lisp convention of describing the (multi-)dimensionality of arrays. But ACL2 currently supports only single dimensional arrays in stobjs.

    For array fields, the initial value vali must be an object satisfying the type-spec of the ARRAY description. The initial value of the field is a list of max repetitions of vali.

    Array fields can be ``resized,'' that is, their lengths can be changed, if :resizable t is supplied as shown in the example and General Form above. The new length must satisfy the same restriction as does max, as described above. Each array field in a defstobj event gives rise to a length function, which gives the length of the field, and a resize function, which modifies the length of the field if :resizable t was supplied with the field when the defstobj was introduced and otherwise causes an error. If :resizable t was supplied and the resize function specifies a new length k, then: if k is less than the existing array length, the array is shortened simply by dropping elements with index at least k; otherwise, the array is extended to length k by mapping the new indices to the initial value (supplied by :initially, else default nil).

    Array resizing is relatively slow, so we recommend using it somewhat sparingly.

    The Default Function Names

    To recap, in

    (defstobj name
              (field1 :type type1 :initially val1)
              ...
              (fieldk :type typek :initially valk)
              :renaming alist
              :doc doc-string
              :inline inline-flag)
    
    name must be a new symbol, each fieldi must be a symbol, each typei must be a type-spec or (ARRAY type-spec (max)), and each vali must be an object satisfying typei.

    Roughly speaking, for each fieldi, a defstobj introduces a recognizer function, an accessor function, and an updater function. The accessor function, for example, takes the stobj and returns the indicated component; the updater takes a new component value and the stobj and return a new stobj with the component replaced by the new value. But that summary is inaccurate for array fields.

    The accessor function for an array field does not take the stobj and return the indicated component array, which is a list of length specified by max. Instead, it takes an additional index argument and returns the indicated element of the array component. Similarly, the updater function for an array field takes an index, a new value, and the stobj, and returns a new stobj with the indicated element replaced by the new value.

    These functions -- the recognizer, accessor, and updater, and also length and resize functions in the case of array fields -- have ``default names.'' The default names depend on the field name, fieldi, and on whether the field is an array field or not. For clarity, suppose fieldi is named c. The default names are shown below in calls, which also indicate the arities of the functions. In the expressions, we use x as the object to be recognized by field recognizers, i as an array index, v as the ``new value'' to be installed by an updater, and name as the single-threaded object.

                     non-array field        array field
    recognizer         (cP x)                (cP x)
    accessor           (c name)              (cI i name)
    updater            (UPDATE-c v name)     (UPDATE-cI i v name)
    length                                   (c-LENGTH name)
    resize                                   (RESIZE-c k name)
    

    Finally, a recognizer and a creator for the entire single-threaded object are introduced. The creator returns the initial stobj, but may only be used in limited contexts; see with-local-stobj. If the single-threaded object is named name, then the default names and arities are as shown below.

    top recognizer     (nameP x)
    creator            (CREATE-name)
    

    For example, the event

    (DEFSTOBJ $S
      (X :TYPE INTEGER :INITIALLY 0)
      (A :TYPE (ARRAY (INTEGER 0 9) (3)) :INITIALLY 9))
    
    introduces a stobj named $S. The stobj has two fields, X and A. The A field is an array. The X field contains an integer and is initially 0. The A field contains a list of integers, each between 0 and 9, inclusively. Initially, each of the three elements of the A field is 9.

    This event introduces the following sequence of definitions:

    (DEFUN XP (X) ...)               ; recognizer for X field
    (DEFUN AP (X) ...)               ; recognizer of A field
    (DEFUN $SP ($S) ...)             ; top-level recognizer for stobj $S
    (DEFUN CREATE-$S () ...)         ; creator for stobj $S
    (DEFUN X ($S) ...)               ; accessor for X field
    (DEFUN UPDATE-X (V $S) ...)      ; updater for X field
    (DEFUN A-LENGTH ($S) ...)        ; length of A field
    (DEFUN RESIZE-A (K $S) ...)      ; resizer for A field
    (DEFUN AI (I $S) ...)            ; accessor for A field at index I
    (DEFUN UPDATE-AI (I V $S) ...)   ; updater for A field at index I
    

    Avoiding the Default Function Names

    If you do not like the default names listed above you may use the optional :renaming alist to substitute names of your own choosing. Each element of alist should be of the form (fn1 fn2), where fn1 is a default name and fn2 is your choice for that name.

    For example

    (DEFSTOBJ $S
      (X :TYPE INTEGER :INITIALLY 0)
      (A :TYPE (ARRAY (INTEGER 0 9) (3)) :INITIALLY 9)
      :renaming ((X XACCESSOR) (CREATE-$S MAKE$S)))
    
    introduces the following definitions
    (DEFUN XP (X) ...)               ; recognizer for X field
    (DEFUN AP (X) ...)               ; recognizer of A field
    (DEFUN $SP ($S) ...)             ; top-level recognizer for stobj $S
    (DEFUN MAKE$S () ...)            ; creator for stobj $S
    (DEFUN XACCESSOR ($S) ...)       ; accessor for X field
    (DEFUN UPDATE-X (V $S) ...)      ; updater for X field
    (DEFUN A-LENGTH ($S) ...)        ; length of A field
    (DEFUN RESIZE-A (K $S) ...)      ; resizer for A field
    (DEFUN AI (I $S) ...)            ; accessor for A field at index I
    (DEFUN UPDATE-AI (I V $S) ...)   ; updater for A field at index I
    
    Note that even though the renaming alist substitutes ``XACCESSOR'' for ``X'' the updater for the X field is still called ``UPDATE-X.'' That is because the renaming is applied to the default function names, not to the field descriptors in the event.

    Use of the :renaming alist may be necessary to avoid name clashes between the default names and and pre-existing function symbols.

    Constants

    Defstobj events also introduce constant definitions (see defconst). One constant is introduced for each accessor function by prefixing and suffixing a `*' character on the function name. The value of that constant is the position of the field being accessed. For example, if the accessor functions are a, b, and c, in that order, then the following constant definitions are introduced.

    (defconst *a* 0)
    (defconst *b* 1)
    (defconst *c* 2)
    
    These constants are used for certain calls of nth and update-nth that are displayed to the user in proof output. For example, for stobj st with accessor functions a, b, and c, in that order, the term (nth '2 st) would be printed during a proof as (nth *c* st). Also see term, in particular the discussion there of untranslated terms, and see nth-aliases-table.

    Inspecting the Effects of a Defstobj

    Because the stobj functions are introduced as ``sub-events'' of the defstobj the history commands :pe and :pc will not print the definitions of these functions but will print the superior defstobj event. To see the definitions of these functions use the history command :pcb!.

    To see an s-expression containing the definitions what constitute the raw Lisp implementation of the event, evaluate the form

    (nth 4 (global-val 'cltl-command (w state)))
    
    immediately after the defstobj event has been processed.

    A defstobj is considered redundant only if the name, field descriptors, renaming alist, and inline flag are identical to a previously executed defstobj. Note that a redundant defstobj does not reset the stobj fields to their initial values.

    Inlining and Performance

    The :inline keyword argument controls whether or not accessor, updater, and length functions are inlined (as macros under the hood, in raw Lisp). If :inline t is provided then these are inlined; otherwise they are not. The advantage of inlining is potentially better performance; there have been contrived examples, doing essentially nothing except accessing and updating array fields, where inlining reduced the time by a factor of 10 or more; and inlining has sped up realistic examples by a factor of at least 2. Inlining may get within a factor of 2 of C execution times for such contrived examples, and within a few percent of C execution times on realistic examples.

    A drawback to inlining is that redefinition may not work as expected, much as redefinition may not work as expected for macros: defined functions that call a macro, or inlined stobj function, will not see a subsequent redefinition of the macro or inlined function. Another drawback to inlining is that because inlined functions are implemented as macros in raw Lisp, tracing (see trace$) will not show their calls. These drawbacks are avoided by default, but the user who is not concerned about them is advised to specify :inline t.

    Specifying Congruent Stobjs

    Two stobjs are may be considered to be ``congruent'' if they have the same structure, that is, their defstobj events are identical when ignoring field names. In particular, every stobj is congruent to itself. In order to tell ACL2 that a new stobj st2 is indeed to be considered as congruent to an existing stobj st1, the defstobj event introducing st2 is given the keyword argument :congruent-to st1. Congruence is an equivalence relation: when you specify a new stobj to be congruent to an old one, you are also specifying that the new stobj is congruent to all other stobjs that are congruent to the old one. Thus, continuing the example above, if you specify that st3 is :congruent-to st2, then st1, st2, and st3 will all be congruent to each other.

    When two stobjs are congruent, ACL2 allows you to substitute one for another in a function call. Any number of stobjs may be replaced with congruent stobjs in the call, provided no two get replaced with the same stobj. The return values are correspondingly modified: if stobj st1 is replaced by st2 at an argument position, and if st1 is returned in the output signature of the function, then st2 is returned in place of st1.

    The following example illustrates congruent stobjs. For more examples of how to take advantage of congruent stobjs, and also of how to misuse them, see community book books/misc/congruent-stobjs-test.lisp.

    (defstobj st1 fld1)
    (defstobj st2 fld2 :congruent-to st1)
    (defstobj st3 fld3 :congruent-to st2) ; equivalently, :congruent-to st1
    (defun f (st1 st2 st3)
      (declare (xargs :stobjs (st1 st2 st3)))
      (list (fld2 st1) (fld3 st2) (fld1 st3)))
    (update-fld1 1 st1)
    (update-fld1 2 st2) ; notice use of update-fld1 on st2
    (update-fld1 3 st3) ; notice use of update-fld1 on st3
    (assert-event (equal (f st3 st2 st1) '(3 2 1)))
    
    The following example shows an error that occurs when stobj arguments are repeated, i.e., at least two stobj arguments (in this case, three) get replaced by the same stobj.
    ACL2 !>(f st1 st1 st1)
    
    
    ACL2 Error in TOP-LEVEL:  The form ST1 is being used, as an argument
    to a call of F, where the single-threaded object ST2 was expected,
    even though these are congruent stobjs.  See :DOC defstobj, in particular
    the discussion of congruent stobjs.  Note:  this error occurred in
    the context (F ST1 ST1 ST1).
    
    ACL2 !>
    




    acl2-sources/doc/HTML/DEFSTUB.html0000664002132200015000000000476712222333516016063 0ustar kaufmannacl2 DEFSTUB.html -- ACL2 Version 6.3

    DEFSTUB

    stub-out a function symbol
    Major Section:  EVENTS
    

    Examples:
    ACL2 !>(defstub subr1 (* * state) => (mv * state))
    ACL2 !>(defstub add-hash (* * hashtable) => hashtable)
    
    General Forms:
    (defstub name args-sig => output-sig)
    (defstub name args-sig => output-sig :doc doc-string)
    

    Name is a new function symbol and (name . args-sig) => output-sig) is a signature. If the optional doc-string is supplied it should be a documentation string. See also the ``Old Style'' heading below.

    Defstub macro expands into an encapsulate event (see encapsulate). Thus, no axioms are available about name but it may be used wherever a function of the given signature is permitted. Exception: if output-sig is of the form (mv ...), then a :type-prescription rule is introduced stating that name returns a value satisfying true-listp.

    Old Style:

    Old Style General Form:
    (defstub name formals output)
    (defstub name formals output :doc doc-string)
    
    where name is a new function symbol, formals is its list of formal parameters, and output is either a symbol (indicating that the function returns one result) or a term of the form (mv s1 ... sn), where each si is a symbol (indicating that the function returns n results). Whether and where the symbol state occurs in formals and output indicates how the function handles state. It should be the case that (name formals output) is in fact a signature (see signature).

    Note that with the old style notation it is impossible to stub-out a function that uses any single-threaded object other than state. The old style is preserved for compatibility with earlier versions of ACL2.




    acl2-sources/doc/HTML/DEFTHEORY-STATIC.html0000664002132200015000000001037412222333516017274 0ustar kaufmannacl2 DEFTHEORY-STATIC.html -- ACL2 Version 6.3

    DEFTHEORY-STATIC

    define a `static' theory (to enable or disable a set of rules)
    Major Section:  EVENTS
    

    This macro provides a variant of deftheory, such that the resulting theory is the same at include-book time as it was at certify-book time.

    We assume that the reader is familiar with theories; see deftheory. We begin here by illustrating how deftheory-static differs from deftheory. Suppose for example that the following events are the first two events in a book, where that book is certified in the initial ACL2 world (see ground-zero).

    (deftheory my-theory
      (current-theory :here))
    (deftheory-static my-static-theory
      (current-theory :here))
    
    Now suppose we include that book after executing the following event.
    (in-theory (disable car-cons))
    
    Suppose that later we execute (in-theory (theory 'my-theory)). Then the rule car-cons will be disabled, because it was disabled at the time the expression (current-theory :here) was evaluated when processing the deftheory of my-theory while including the book. However, if we execute (in-theory (theory 'my-static-theory)), then the rule car-cons will be enabled, because the value of the theory my-static-theory was saved at the time the book was certified.

    General Form:
    (deftheory-static name term :doc doc-string)
    
    The arguments are handled the same as for deftheory. Thus, name is a new symbolic name (see name), term is a term that when evaluated will produce a theory (see theories), and doc-string is an optional documentation string (see doc-string). Except for the variable world, term must contain no free variables. Term is evaluated with world bound to the current world (see world) and the resulting theory is then converted to a runic theory (see theories) and associated with name. Henceforth, this runic theory is returned as the value of the theory expression (theory name).

    As for deftheory, the value returned is the length of the resulting theory.

    We conclude with an optional discussion about the implementation of deftheory-static, for those familiar with make-event. The following macroexpansion of the deftheory-static form above shows how this works (see trans1).

    ACL2 !>:trans1 (deftheory-static my-static-theory
                     (current-theory :here))
     (MAKE-EVENT (LET ((WORLD (W STATE)))
                      (LIST 'DEFTHEORY
                            'MY-STATIC-THEORY
                            (LIST 'QUOTE (CURRENT-THEORY :HERE)))))
    ACL2 !>
    

    The idea is that upon evaluation of this make-event form, the first step is to evaluate the indicated LET expression to obtain a form (deftheory my-theory '(...)), where ``(...)'' is a list of all runes in current theory. If this form is in a book being certified, then the resulting deftheory form is stored in the book's certificate, and is used when the book is included later.




    acl2-sources/doc/HTML/DEFTHEORY.html0000664002132200015000000000733412222333516016311 0ustar kaufmannacl2 DEFTHEORY.html -- ACL2 Version 6.3

    DEFTHEORY

    define a theory (to enable or disable a set of rules)
    Major Section:  EVENTS
    

    Example:
    (deftheory interp-theory
               (set-difference-theories
                 (universal-theory :here)
                 (universal-theory 'interp-section)))
    
    General Form:
    (deftheory name term :doc doc-string)
    
    where name is a new symbolic name (see name), term is a term that when evaluated will produce a theory (see theories), and doc-string is an optional documentation string (see doc-string). Except for the variable world, term must contain no free variables. Term is evaluated with world bound to the current world (see world) and the resulting theory is then converted to a runic theory (see theories) and associated with name. Henceforth, this runic theory is returned as the value of the theory expression (theory name).

    The value returned is the length of the resulting theory. For example, in the following, the theory associated with 'FOO has 54 runes:

    ACL2 !>(deftheory foo (union-theories '(binary-append)
                                          (theory 'minimal-theory)))
    
    Summary
    Form:  ( DEFTHEORY FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     54
    ACL2 !>
    

    Note that the theory being defined depends on the context. For example, consider the following (contrived) example book.

      (in-package "ACL2")
      (defund foo (x) (consp x)) ; defund disables foo
      (local (in-theory (enable foo)))
      (deftheory my-theory (current-theory :here))
      (in-theory (disable foo))
      (defthm foo-property
        (implies (consp x)
                 (foo x))
        :hints (("Goal" :in-theory (enable my-theory))))
    
    At the time foo-property is proved admissible during book certification (see certify-book), the local in-theory event has previously been evaluated, so the definition of foo is enabled. Thus, the :in-theory hint on foo-property will enable foo, and the theorem proves. HOWEVER, when the book is later included (see include-book), the local event is skipped, so the definition of foo is disabled at the time the theory my-theory is defined. Hence, unlike the case for the admissibility pass of the book's certification, that theory does not include the definition of foo when the book is included.

    There is, however, a way to ensure that a theory defined in a book is the same at include-book time as it was during the admissibility pass of the book's certification; see deftheory-static.




    acl2-sources/doc/HTML/DEFTHM.html0000664002132200015000000000623312222333517015725 0ustar kaufmannacl2 DEFTHM.html -- ACL2 Version 6.3

    DEFTHM

    prove and name a theorem
    Major Section:  EVENTS
    

    Examples:
    (defthm assoc-of-app
            (equal (app (app a b) c)
                   (app a (app b c))))
    
    The following nonsensical example illustrates all the optional arguments but is illegal because not all combinations are permitted. See hints for a complete list of hints.
    (defthm main
            (implies (hyps x y z) (concl x y z))
           :rule-classes (:REWRITE :GENERALIZE)
           :instructions (induct prove promote (dive 1) x
                                 (dive 2) = top (drop 2) prove)
           :hints (("Goal"
                    :do-not '(generalize fertilize)
                    :in-theory (set-difference-theories
                                 (current-theory :here)
                                 '(assoc))
                    :induct (and (nth n a) (nth n b))
                    :use ((:instance assoc-of-append
                                     (x a) (y b) (z c))
                          (:functional-instance
                            (:instance p-f (x a) (y b))
                            (p consp)
                            (f assoc)))))
           :otf-flg t
           :doc "#0[one-liner/example/details]")
    
    General Form:
    (defthm name term
            :rule-classes rule-classes
            :instructions instructions
            :hints        hints
            :otf-flg      otf-flg
            :doc          doc-string)
    
    where name is a new symbolic name (see name), term is a term alleged to be a theorem, and rule-classes, instructions, hints, otf-flg and doc-string are as described in their respective documentation. The five keyword arguments above are all optional, however you may not supply both :instructions and :hints, since one drives the proof checker and the other drives the theorem prover. If :rule-classes is not specified, the list (:rewrite) is used; if you wish the theorem to generate no rules, specify :rule-classes nil.

    When ACL2 processes a defthm event, it first tries to prove the term using the indicated hints (see hints) or instructions (see proof-checker). If it is successful, it stores the rules described by the rule-classes (see rule-classes), proving the necessary corollaries.




    acl2-sources/doc/HTML/DEFTHMD.html0000664002132200015000000000257512222333517016036 0ustar kaufmannacl2 DEFTHMD.html -- ACL2 Version 6.3

    DEFTHMD

    prove and name a theorem and then disable it
    Major Section:  EVENTS
    

    Use defthmd instead of defthm when you want to disable a theorem immediately after proving it. This macro has been provided for users who prefer working in a mode where theorems are only enabled when explicitly directed by :in-theory. Specifically, the form

    (defthmd NAME TERM ...)
    
    expands to:
    (progn
      (defthmd NAME TERM ...)
      (with-output
       :off summary
       (in-theory (disable NAME)))
      (value-triple '(:defthmd NAME))).
    

    Note that defthmd commands are never redundant (see redundant-events). Even if the defthm event is redundant, then the in-theory event will still be executed.

    The summary for the in-theory event is suppressed. See defthm for documentation of defthm.




    acl2-sources/doc/HTML/DEFTTAG.html0000664002132200015000000003651412222333517016041 0ustar kaufmannacl2 DEFTTAG.html -- ACL2 Version 6.3

    DEFTTAG

    introduce a trust tag (ttag)
    Major Section:  EVENTS
    

    Introduction. This event is intended for advanced users who, in essence, want to build extensions of ACL2. The typical intended use is to create books that extend the functionality of ACL2 in ways not allowed without a so-called ``active trust tag''. A trust tag thus represents a contract: The writer of such a book is guaranteeing that the book extends ACL2 in a ``correct'' way as defined by the writer of the book. The writer of the book will often have a small section of the book in the scope of an active trust tag that can be inspected by potential users of that book:

    <initial part of book, which does not use trust tags>
    (defttag :some-ttag) ; install :some-ttag as an active trust tag
    <various code that requires an active trust tag>
    (defttag nil)        ; remove active trust tag
    <initial part of book, which does not use trust tags>
    

    Why might trust tags be needed? The evaluation of certain functions can introduce bugs and even unsoundness, but can be useful in restricted ways that avoid such issues. For example, sys-call can be used in an unsafe way, for example to overwrite files, or worse; see sys-call for a frightening example from Bob Boyer. The following example shows that the function sys-call is restricted by default, but can be called after installing an active trust tag.

    ACL2 !>(sys-call "pwd" nil)
    
    
    ACL2 Error in TOP-LEVEL:  The SYS-CALL function cannot be called unless
    a trust tag is in effect.  See :DOC defttag.
    
    ACL2 !>(defttag t) ; Install :T as an active trust tag.
    
    TTAG NOTE: Adding ttag :T from the top level loop.
     T
    ACL2 !>(sys-call "pwd" nil) ; print the current directory and return NIL
    /u/kaufmann
    NIL
    ACL2 !>(defttag nil) ; Remove the active trust tag (using value NIL).
     NIL
    ACL2 !>(sys-call "pwd" nil) ; Now we get the error again:
    
    
    ACL2 Error in TOP-LEVEL:  The SYS-CALL function cannot be called unless
    a trust tag is in effect.  See :DOC defttag.
    
    ACL2 !>
    

    Of course, using sys-call with the Linux command pwd is not likely to cause any soundness problems! So suppose we want to create a function that prints the working directory. We might put the following events into a book that is to be certified.

    (in-package "ACL2")
    (defttag :pwd-ttag)
    (defun print-working-dir ()
      (declare (xargs :mode :program))
      (sys-call "pwd" nil))
    (defttag nil) ; optional (books end with this implicitly)
    
    We can certify this book with a specification that :pwd-ttag is a legal trust tag:
    (certify-book "pwd" 0 t :ttags (:pwd-ttag))
    
    One can now use this book by executing include-book with keyword parameter :ttags (:pwd-ttag) and then calling function print-working-dir:
    (include-book "pwd" :ttags (:pwd-ttag))
    (print-working-dir) ; working directory is printed to terminal
    

    Detailed documentation.

    General Forms:
    (defttag tag-name)
    (defttag tag-name :doc doc-string)
    
    where tag-name is a symbol. The :doc doc-string argument is optional; if supplied, then it must be a valid documentation string (see doc-string), and the defttag call will generate a corresponding defdoc event for tag-name. (For the rest of this discussion we ignore the :doc argument.)

    Note however that (other than the :doc argument), if tag-name is not nil then it is converted to a ``corresponding keyword'': a symbol in the "KEYWORD" package with the same symbol-name as tag-name. Thus, for example, (defttag foo) is equivalent to (defttag :foo). Moreover, a non-nil symbol with a symbol-name of "NIL" is illegal for trust tags; thus, for example, (defttag :nil) is illegal.

    This event introduces or removes a so-called active trust tag (or ``ttag'', pronounced ``tee tag''). An active ttag is a keyword symbol that is associated with potentially unsafe evaluation. For example, calls of sys-call are illegal unless there is an active trust tag. An active trust tag can be installed using a defttag event. If one introduces an active ttag and then writes definitions that calls sys-call, presumably in a defensibly ``safe'' way, then responsibility for those calls is attributed to that ttag. This attribution (or blame!) is at the level of books; a book's certificate contains a list of ttags that are active in that book, or in a book that is included (possibly locally), or in a book included in a book that is included (either inclusion being potentially local), and so on. We explain all this in more detail below.

    (Defttag :tag-name) is essentially equivalent to

    (table acl2-defaults-table :ttag :tag-name)
    
    and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. We say more about the scope of defttag forms below.

    Note: This is an event! It does not print the usual event summary but nevertheless executes the above table event and hence changes the ACL2 logical world, and is so recorded. Although no event summary is printed, it is important to note that the ``TTAG NOTE'', discussed below, is always printed for a non-nil :tag-name (unless deferred; see set-deferred-ttag-notes).

    Active ttags. Suppose tag-name is a non-nil symbol. Then (defttag :tag-name) sets :tag-name to be the (unique) ``active ttag.'' There must be an active ttag in order for there to be any mention of certain function and macro symbols, including sys-call; evaluate the form (strip-cars *ttag-fns-and-macros*) to see the full list of such symbols. On the other hand, (defttag nil) removes the active ttag, if any; there is then no active ttag. The scope of a defttag form in a book being certified or included is limited to subsequent forms in the same book before the next defttag (if any) in that book. Similarly, if a defttag form is evaluated in the top-level loop, then its effect is limited to subsequent forms in the top-level loop before the next defttag in the top-level loop (if any). Moreoever, certify-book is illegal when a ttag is active; of course, in such a circumstance one can execute (defttag nil) in order to allow book certification.

    Ttag notes and the ``certifier.'' When a defttag is executed with an argument other than nil, output is printed, starting on a fresh line with: TTAG NOTE. For example:

    ACL2 !>(defttag :foo)
    
    TTAG NOTE: Adding ttag :FOO from the top level loop.
     :FOO
    ACL2 !>
    
    If the defttag occurs in an included book, the message looks like this.
    TTAG NOTE (for included book): Adding ttag :FOO from file /u/smith/acl2/my-book.lisp.
    
    The ``TTAG NOTE'' message is always printed on a single line. The intention is that one can search the standard output for all such notes in order to find all defttag events. In a sense, defttag events can allow you to define your own system on top of ACL2 (for example, see progn!). So in order for someone else (who we might call the ``certifier'') to be confident that your collection of books is meaningful, that certifier should certify all the user-supplied books from scratch and check either that no :ttags were supplied to certify-book, or else look for every TTAG NOTE in the standard output in order to locate all defttag events with non-nil tag name. In this way, the certifier can in principle decide whether to be satisfied that those defttag events did not allow inappropriate forms in the user-supplied books.

    In order to eliminate much of the output from TTAG NOTEs, see set-deferred-ttag-notes. Note however that the resulting security is somewhat less; therefore, a TTAG NOTE is printed when invoking set-deferred-ttag-notes to defer printing of ttag notes.

    Allowed ttags when certifying and including books. A defttag form may not be evaluated unless its argument is a so-called ``allowed'' ttag. All ttags are allowed in the interactive top-level loop. However, during certify-book and include-book, the set of allowed ttags is restricted according to the :ttags keyword argument. If this argument is omitted then no ttag is allowed, so a defttag call will fail during book certification or inclusion in this case. This restriction applies even to defttag forms already evaluated in the so-called certification world at the time certify-book is called. But note that (defttag nil) is always legal.

    A :ttags argument of certify-book and include-book can have value :all, indicating that every ttag is allowed, i.e., no restriction is being placed on the arguments, just as in the interactive top-level loop. In the case of include-book, an omitted :ttags argument or an argument of :default is treated as :all, except that warnings will occur when the book's certificate includes ttags; but for certify-book, an omitted ttags argument is treated as nil. Otherwise, if the :ttags argument is supplied but not :all, then its value is a true list of ttag specifications, each having one of the following forms, where sym is a non-nil symbol which is treated as the corresponding keyword.

    (1) :sym

    (2) (:sym)

    (3) (:sym x1 x2 ... xk), where k > 0 and each xi is a string, except that one xi may be nil.

    In Case (1), (defttag :sym) is allowed to occur in at most one book or else in the top-level loop (i.e., the certification world for a book under certification or a book being included). Case (2) allows (defttag :sym) to occur in an unlimited number of books. For case (3) the xi specify where (defttag :sym) may occur, as follows. The case that xi is nil refers to the top-level loop, while all other xi are filenames, where the ".lisp" extension is optional and relative pathnames are considered to be relative to the connected book directory (see cbd). Note that the restrictions on (defttag :sym) apply equally to any equivalent for based on the notion of ``corresponding keyword'' discussed above, e.g., (defttag acl2::sym).

    An error message, as shown below, illustrates how ACL2 enforcess the notion of allowed ttags. Suppose that you call certify-book with argument :ttags (:foo), where you have already executed (defttag :foo) in the certification world (i.e., before calling certify-book). Then ACL2 immediately associates the ttag :foo with nil, where again, nil refers to the top-level loop. If ACL2 then encounters (defttag foo) inside that book, you will get the following error (using the full book name for the book, as shown):

    ACL2 Error in ( TABLE ACL2-DEFAULTS-TABLE ...):  The ttag :FOO associated
    with file /u/smith/work/my-book.lisp is not among the set of ttags permitted
    in the current context, specified as follows:
      ((:FOO NIL)).
    See :DOC defttag.
    
    In general the structure displayed by the error message, which is ((:FOO NIL)) in this case, represents the currently allowed ttags with elements as discussed in (1) through (3) above. In this case, that list's unique element is (:FOO NIL), meaning that ttag :FOO is only allowed at the top level (as represented by NIL).

    Associating ttags with books and with the top-level loop. When a book is certified, each form (defttag tag) that is encountered for non-nil tag in that book or an included book is recorded in the generated certificate, which associates the keyword corresponding to tag with the full-book-name of the book containing that deftag. If such a defttag form is encountered outside a book, hence in the portcullis of the book being certified or one of its included books, then that keyword is associated with nil in the generated certificate. Note that the notion of ``included book'' here applies to the recursive notion of a book either included directly in the book being certified or else included in such a book, where we account even for locally included books.

    For examples of ways to take advantage of ttags, see community book books/hacking/hacker.lisp and see ttags-seen, see progn!, see remove-untouchable, see set-raw-mode, and see sys-call.




    acl2-sources/doc/HTML/DEFUN-INLINE.html0000664002132200015000000001401012222333517016563 0ustar kaufmannacl2 DEFUN-INLINE.html -- ACL2 Version 6.3

    DEFUN-INLINE

    define a potentially inlined function symbol and associated macro
    Major Section:  EVENTS
    

    You may be able to improve performance by replacing an event (defun f ...) with a corresponding event (defun-inline f ...), in order to encourage the host Lisp compiler to inline calls of f.

    Example Form:
    (defun-inline lng (x)
      (declare (xargs :guard (true-listp x)))
      (if (endp x) 0 (1+ (lng (cdr x)))))
    
    General Form:
    (defun-inline fn (var1 ... varn) doc-string dcl ... dcl body)
    
    satisfying the same requirements as in the General Form for defun. The effect is to define a macro fn and a function fn$inline (i.e., a symbol in the same package as fn but whose symbol-name has the suffix "$INLINE", such that each call of fn expands to a call of the function symbol fn$inline on the same arguments. If doc-string is supplied, then it is associated with fn, not with fn$inline. Moreover, table events are generated that allow the use of fn in theory expressions to represent fn$inline and that cause any untranslated (user-level) call of fn$inline to be printed as the corresponding call of fn.

    A form (defun-inline f ...) actually defines a function named f$inline and a corresponding macro named f whose calls expand to calls of f$inline, while providing the illusion that there is just the ``function'' f. For example, the Example Form above macroexpands in one step to the following form.

    (progn (defmacro lng (non-stobj-var0)
             (list 'lng$inline non-stobj-var0))
           (add-macro-fn lng lng$inline)
           (defun lng$inline (x)
             (declare (xargs :guard (true-listp x)))
             (if (endp x) 0 (1+ (lng (cdr x))))))
    
    Note that the above call of add-macro-fn generates the aforementioned two table events (see add-macro-fn), which provide the illusion that we are just defining a function lng, as you can see in the following log: lng appears rather than lng$inline.
    ACL2 !>(set-gag-mode nil)
    <state>
    ACL2 !>(thm (equal (lng (append x y))
                       (+ (lng x) (lng y)))
                :hints (("Goal" :in-theory (enable lng))))
    
    [.. output omitted ..]
    
    Subgoal *1/2
    (IMPLIES (AND (NOT (ENDP X))
                  (EQUAL (LNG (APPEND (CDR X) Y))
                         (+ (LNG (CDR X)) (LNG Y))))
             (EQUAL (LNG (APPEND X Y))
                    (+ (LNG X) (LNG Y)))).
    
    [.. output omitted ..]
    

    Under the hood, ACL2 arranges that every function symbol with suffix "$INLINE" is presented to the compiler as one whose calls we would prefer to inline. Technically: the Common Lisp form (declaim (inline f$inline)) is generated for a function symbol f$inline before that symbol's definition is submitted. However, the Common Lisp spec explicitly avoids requiring that the compiler respect this declaim form. Fortunately, Common Lisp implementations often do respect it.

    Also see defund-inline, see defun-notinline, and see defund-notinline.

    Remarks.

    (1) None of these macros (including defun-inline) is supported for use inside a mutual-recursion.

    (2) Every function symbol defined in ACL2 whose symbol-name has the suffix "$INLINE" is proclaimed to be inline; similarly for "$NOTINLINE" and notinline.

    (3) No special treatment for inlining (or notinlining) is given for function symbols locally defined by flet, with two exceptions: when explicitly declared inline or notinline by the flet form, and for symbols discussed in (1) and (2) above that, at some point in the current ACL2 session, were defined as function symbols in ACL2 (even if not currently defined because of undoing or being local).

    (4) The function symbol actually being defined by (defun-inline foo ...) is foo$inline. As mentioned above, one can be oblivious to this fact when writing theory expressions or perusing prover output. However, for other purposes (for example, verify-guards and defabsstobj :exports) you will need to supply the name of the function symbol rather than the name of the macro; e.g., for the above form (defun-inline foo ...), you may subsequently issue the event (verify-guards foo$inline) but not (verify-guards foo).

    (5) Obscure Remark. Suppose that you certify a book with compilation (the default) in one host Lisp, saving the expansion file. Suppose that you then compile in another host Lisp by using include-book with argument :load-compiled-file :comp. Then in subsequent sessions, including that book with the second host Lisp will not result in any inline or notinline behavior for functions defined in the book. This may be fixed in a future release if someone complains.




    acl2-sources/doc/HTML/DEFUN-MODE-CAVEAT.html0000664002132200015000000001335712222333520017301 0ustar kaufmannacl2 DEFUN-MODE-CAVEAT.html -- ACL2 Version 6.3

    DEFUN-MODE-CAVEAT

    potential soundness issue for functions with defun-mode :program
    Major Section:  COMMON-LISP
    

    Technically speaking, in the current implementation, the execution of functions having defun-mode :program may damage the ACL2 system in a way that renders it unsound. In practice, we have never seen this happen; so, the explanations below can be viewed as extremely paranoid. Nevertheless, here we document this concern, even if it should be taken with more than a grain of salt.

    See defun-mode for a discussion of defun-modes. That discussion describes an imagined implementation that is slightly different from this one. This note explains that the current implementation is open to unsoundness.

    For discussion of a different soundness issue that is also related to function execution, see generalized-booleans.

    The execution of a function having defun-mode :program may violate Common Lisp guards on the subroutines used. (This may be true even for calls of a function on arguments that satisfy its guard, because ACL2 has not verified that its guard is sufficient to protect its subroutines.) When a guard is violated at runtime all bets are off. That is, no guarantees are made either about the answer being ``right'' or about the continued rationality of the ACL2 system itself.

    For example, suppose you make the following defun:

    
    (defun crash (i)
      (declare (xargs :mode :program :guard (integerp i)))
      (car i))
    

    Note that the declared guard does not in fact adequately protect the subroutines in the body of crash; indeed, satisfying the guard to crash will guarantee that the car expression is in violation of its guard. Because this function is admitted in :program-mode, no checks are made concerning the suitability of the guard. Furthermore, in the current ACL2 implementation, crash is executed directly in Common Lisp. Thus if you call crash on an argument satisfying its guard you will cause an erroneous computation to take place.

    ACL2 !>(crash 7)
    
    Error: Caught fatal error [memory may be damaged]
    ...
    
    There is no telling how much damage is done by this errant computation. In some lisps your ACL2 job may actually crash back to the operating system. In other lisps you may be able to recover from the ``hard error'' and resume ACL2 in a damaged but apparently functional image.

    THUS, HAVING A FUNCTION WITH DEFUN-MODE :PROGRAM IN YOUR SYSTEM ABSOLVES US, THE ACL2 IMPLEMENTORS, FROM RESPONSIBILITY FOR THE SOUNDNESS OF OUR SYSTEM.

    Furthermore

    ACL2 DOES NOT YET PROVIDE ANY MEANS OF REGAINING ASSURANCES OF SOUNDNESS AFTER THE INTRODUCTION OF A FUNCTION IN :PROGRAM MODE, EVEN IF IT IS ULTIMATELY CONVERTED TO :LOGIC MODE (since its execution could have damaged the system in a way that makes it possible to verify its termination and guards unsoundly).

    Finally,

    THE VAST MAJORITY OF ACL2 SYSTEM CODE IS IN :PROGRAM MODE AND SO ALL BETS ARE OFF FROM BEFORE YOU START!

    This hopeless state of current affairs will change, we think. We think we have defined our functions ``correctly'' in the sense that they can be converted, without ``essential'' modification, to :logic mode. We think it very unlikely that a mis-guarded function in :program mode (whether ours or yours) will cause unsoundness without some sort of hard lisp error accompanying it. We think that ultimately we can make it possible to execute your functions (interpretively) without risk to the system, even when some have :program mode. In that imagined implementation, code using functions having :program mode would run more slowly, but safely. These functions could be introduced into the logic ex post facto, whereupon the code's execution would speed up because Common Lisp would be allowed to execute it directly. We therefore ask that you simply pretend that this is that imagined implementation, introduce functions in :program mode, use them as convenient and perhaps ultimately introduce some of them in :logic mode and prove their properties. If you use the system this way we can develop (or dismiss) this style of formal system development. BUT BE ON THE LOOKOUT FOR SCREWUPS DUE TO DAMAGE CAUSED BY THE EXECUTION OF YOUR FUNCTIONS HAVING :PROGRAM MODE!




    acl2-sources/doc/HTML/DEFUN-MODE.html0000664002132200015000000002051612222333520016333 0ustar kaufmannacl2 DEFUN-MODE.html -- ACL2 Version 6.3

    DEFUN-MODE

    determines whether a function definition is a logical act
    Major Section:  MISCELLANEOUS
    

    Two ``defun-modes'' are supported, :program and :logic. Roughly speaking, :program mode allows you to prototype a function for execution without any proof burdens, while :logic mode allows you to add a new definitional axiom to the logic. The system comes up in :logic mode. Execution of functions whose defun-mode is :program may render ACL2 unsound! See defun-mode-caveat.

    Note that calls of local and of many events are skipped in :program mode; see program.

    When you define a function in the ACL2 logic, that function can be run on concrete data. But it is also possible to reason deductively about the function because each definition extends the underlying logic with a definitional axiom. To ensure that the logic is sound after the addition of this axiom, certain restrictions have to be met, namely that the recursion terminates. This can be quite challenging.

    Because ACL2 is a programming language, you often may wish simply to program in ACL2. For example, you may wish to define your system and test it, without any logical burden. Or, you may wish to define ``utility'' functions -- functions that are executed to help manage the task of building your system but functions whose logical properties are of no immediate concern. Such functions might be used to generate test data or help interpret the results of tests. They might create files or explore the ACL2 database. The termination arguments for such functions are an unnecessary burden provided no axioms about the functions are ever used in deductions.

    Thus, ACL2 introduces the idea of the ``defun-mode'' of a function. The :mode keyword of defun's declare xarg allows you to specify the defun-mode of a given definition. If no :mode keyword is supplied, the default defun-mode is used; see default-defun-mode.

    There are two defun-modes, each of which is written as a keyword:

    :program -- logically undefined but executable outside deductive contexts.

    :logic -- axiomatically defined as per the ACL2 definitional principle.

    It is possible to change the defun-mode of a function from :program to :logic. We discuss this below.

    We think of functions having :program mode as ``dangerous'' functions, while functions having :logic mode are ``safe.'' The only requirement enforced on :program mode functions is the syntactic one: each definition must be well-formed ACL2. Naively speaking, if a :program mode function fails to terminate then no harm is done because no axiom is added (so inconsistency is avoided) and some invocations of the function may simply never return. This simplistic justification of :program mode execution is faulty because it ignores the damage that might be caused by ``mis-guarded'' functions. See defun-mode-caveat.

    We therefore implicitly describe an imagined implementation of defun-modes that is safe and, we think, effective. But please see defun-mode-caveat.

    The default defun-mode is :logic. This means that when you defun a function the system will try to prove termination. If you wish to introduce a function of a different defun-mode use the :mode xargs keyword. Below we show fact introduced as a function in :program mode.

    (defun fact (n)
      (declare (xargs :mode :program))
      (if (or (not (integerp n)) (= n 0))
          1
        (* n (fact (1- n)))))
    
    No axiom is added to the logic as a result of this definition. By introducing fact in :program mode we avoid the burden of a termination proof, while still having the option of executing the function. For example, you can type
    ACL2 !>(fact 3)
    
    and get the answer 6. If you type (fact -1) you will get a hard lisp error due to ``infinite recursion.''

    However, the ACL2 theorem prover knows no axioms about fact. In particular, if the term (fact 3) arises in a proof, the theorem prover is unable to deduce that it is 6. From the perspective of the theorem prover it is as though fact were an undefined function symbol of arity 1. Thus, modulo certain important issues (see defun-mode-caveat), the introduction of this function in :program mode does not imperil the soundness of the system -- despite the fact that the termination argument for fact was omitted -- because nothing of interest can be proved about fact. Indeed, we do not allow fact to be used in logical contexts such as conjectures submitted for proof.

    It is possible to convert a function from :program mode to :logic mode at the cost of proving that it is admissible. This can be done by invoking

    (verify-termination fact)
    
    which is equivalent to submitting the defun of fact, again, but in :logic mode.
    (defun fact (n)
      (declare (xargs :mode :logic))
      (if (or (not (integerp n)) (= n 0))
          1
        (* n (fact (1- n)))))
    
    This particular event will fail because the termination argument requires that n be nonnegative. A repaired defun, for example with = replaced by <=, will succeed, and an axiom about fact will henceforth be available.

    Technically, verify-termination submits a redefinition of the :program mode function. This is permitted, even when ld-redefinition-action is nil, because the new definition is identical to the old (except for its :mode and, possibly, other non-logical properties).

    See guard for a discussion of how to restrict the execution of functions. Guards may be ``verified'' for functions in :logic mode; see verify-guards.




    acl2-sources/doc/HTML/DEFUN-NOTINLINE.html0000664002132200015000000000243712222333517017156 0ustar kaufmannacl2 DEFUN-NOTINLINE.html -- ACL2 Version 6.3

    DEFUN-NOTINLINE

    define a not-to-be-inlined function symbol and associated macro
    Major Section:  EVENTS
    

    See defun-inline for an analogous utility that supports inlining. The present utility is probably far less useful; it tells the compiler not to inline calls of the function being defined. Also see defund-notinline for a variant of this event that disables the newly-defined function symbol.

    Under the hood, (defun-inline f ...) and (defun-notinline f ...) cause evaluation of Common Lisp forms (declaim (inline f$inline)) and (declaim (notinline f$notinline)), respectively. According to the Common Lisp spec, the compiler need not respect the first of these (for inline), but it must respect the second of these (for notinline). Fortunately, Common Lisp implementations often do respect the first of these as well.




    acl2-sources/doc/HTML/DEFUN-NX.html0000664002132200015000000000753512222333517016150 0ustar kaufmannacl2 DEFUN-NX.html -- ACL2 Version 6.3

    DEFUN-NX

    define a non-executable function symbol
    Major Section:  EVENTS
    

    Example:
    
    (set-state-ok t)
    (defun-nx foo (x state)
      (mv-let (a b c)
              (cons x state)
              (list a b c b a)))
    ; Note ``ill-formed'' call of foo just below.
    (defun bar (state y)
      (foo state y))
    

    The macro defun-nx introduces definitions using the defun macro, always in :logic mode, such that the calls of the resulting function cannot be evaluated. Such a definition is admitted without enforcing syntactic restrictions for executability, in particular for single-threadedness (see stobj) and multiple-values passing (see mv and see mv-let). After such a definition is admitted, the usual syntactic rules for state and user-defined stobjs are relaxed for calls of the function it defines. Also see non-exec for a way to designate subterms of function bodies, or subterms of code to be executed at the top level, as non-executable.

    The syntax of defun-nx is identical to that of defun. A form

    (defun-nx name (x1 ... xk) ... body)
    
    expands to the following form.
    (defun name (x1 ... xk)
      (declare (xargs :non-executable t :mode :logic))
      ...
      (prog2$ (throw-nonexec-error 'name (list x1 ... xk))
              body))
    
    Note that because of the insertion of the above call of throw-nonexec-error, no formal is ignored when using defun-nx.

    During proofs, the error is silent; it is ``caught'' by the proof mechanism and generally results in the introduction of a call of hide during a proof. If an error message is produced by evaluating a call of the function on a list of arguments that includes state or user-defined stobjs, these arguments will be shown as symbols such as |<state>| in the error message. In the case of a user-defined stobj bound by with-local-stobj or stobj-let, the symbol printed will include the suffix {instance}, for example, |<st>{instance}|.

    It is harmless to include :non-executable t in your own xargs declare form; defun-nx will still lay down its own such declaration, but ACL2 can tolerate the duplication.

    Note that defund-nx is also available. It has an effect identical to that of defun-nx except that as with defund, it leaves the function disabled.

    If you use guards (see guard), please be aware that even though syntactic restrictions are relaxed for defun-nx, guard verification proceeds exactly as for defun. If you want ACL2 to skip a form for purposes of generating guard proof obligations, use the macro non-exec, which generates a call of throw-nonexec-error that differs somewhat from the one displayed above. See non-exec.

    See defun for documentation of defun.




    acl2-sources/doc/HTML/DEFUN-SK-EXAMPLE.html0000664002132200015000000000741612222333517017227 0ustar kaufmannacl2 DEFUN-SK-EXAMPLE.html -- ACL2 Version 6.3

    DEFUN-SK-EXAMPLE

    a simple example using defun-sk
    Major Section:  DEFUN-SK
    

    For a more through, systematic beginner's introduction to quantification in ACL2, see quantifier-tutorial.

    The following example illustrates how to do proofs about functions defined with defun-sk. The events below can be put into a certifiable book (see books). The example is contrived and rather silly, in that it shows how to prove that a quantified notion implies itself, where the antecedent and conclusion are defined with different defun-sk events. But it illustrates the formulas that are generated by defun-sk, and how to use them. Thanks to Julien Schmaltz for presenting this example as a challenge.

    (in-package "ACL2")
    
    (encapsulate
     (((p *) => *)
      ((expr *) => *))
    
     (local (defun p (x) x))
     (local (defun expr (x) x)))
    
    (defun-sk forall-expr1 (x)
      (forall (y) (implies (p x) (expr y))))
    
    (defun-sk forall-expr2 (x)
      (forall (y) (implies (p x) (expr y)))))
    
    ; We want to prove the theorem my-theorem below.  What axioms are there that
    ; can help us?  If you submit the command
    
    ; :pcb! forall-expr1
    
    ; then you will see the following two key events.  (They are completely
    ; analogous of course for FORALL-EXPR2.)
    
    ;   (DEFUN FORALL-EXPR1 (X)
    ;     (LET ((Y (FORALL-EXPR1-WITNESS X)))
    ;          (IMPLIES (P X) (EXPR Y))))
    ;
    ;   (DEFTHM FORALL-EXPR1-NECC
    ;     (IMPLIES (NOT (IMPLIES (P X) (EXPR Y)))
    ;              (NOT (FORALL-EXPR1 X)))
    ;     :HINTS
    ;     (("Goal" :USE FORALL-EXPR1-WITNESS)))
    
    ; We see that the latter has value when FORALL-EXPR1 occurs negated in a
    ; conclusion, or (therefore) positively in a hypothesis.  A good rule to
    ; remember is that the former has value in the opposite circumstance: negated
    ; in a hypothesis or positively in a conclusion.
    
    ; In our theorem, FORALL-EXPR2 occurs positively in the conclusion, so its
    ; definition should be of use.  We therefore leave its definition enabled,
    ; and disable the definition of FORALL-EXPR1.
    
    ;   (thm
    ;     (implies (and (p x) (forall-expr1 x))
    ;              (forall-expr2 x))
    ;     :hints (("Goal" :in-theory (disable forall-expr1))))
    ;
    ;   ; which yields this unproved subgoal:
    ;
    ;   (IMPLIES (AND (P X) (FORALL-EXPR1 X))
    ;            (EXPR (FORALL-EXPR2-WITNESS X)))
    
    ; Now we can see how to use FORALL-EXPR1-NECC to complete the proof, by
    ; binding y to (FORALL-EXPR2-WITNESS X).
    
    ; We use defthmd below so that the following doesn't interfere with the
    ; second proof, in my-theorem-again that follows.
    (defthmd my-theorem
      (implies (and (p x) (forall-expr1 x))
               (forall-expr2 x))
      :hints (("Goal"
               :use ((:instance forall-expr1-necc
                                (x x)
                                (y (forall-expr2-witness x)))))))
    
    ; The following illustrates a more advanced technique to consider in such
    ; cases.  If we disable forall-expr1, then we can similarly succeed by having
    ; FORALL-EXPR1-NECC applied as a :rewrite rule, with an appropriate hint in how
    ; to instantiate its free variable.  See :doc hints.
    
    (defthm my-theorem-again
      (implies (and (P x) (forall-expr1 x))
               (forall-expr2 x))
      :hints (("Goal"
               :in-theory (disable forall-expr1)
               :restrict ((forall-expr1-necc
                           ((y (forall-expr2-witness x))))))))
    




    acl2-sources/doc/HTML/DEFUN-SK.html0000664002132200015000000003726212222333517016140 0ustar kaufmannacl2 DEFUN-SK.html -- ACL2 Version 6.3

    DEFUN-SK

    define a function whose body has an outermost quantifier
    Major Section:  EVENTS
    

    Examples:
    (defun-sk exists-x-p0-and-q0 (y z)
      (exists x
              (and (p0 x y z)
                   (q0 x y z))))
    
    (defun-sk exists-x-p0-and-q0 (y z) ; equivalent to the above
      (exists (x)
              (and (p0 x y z)
                   (q0 x y z))))
    
    (defun-sk forall-x-y-p0-and-q0 (z)
      (forall (x y)
              (and (p0 x y z)
                   (q0 x y z)))
      :strengthen t)
    
    
    

    Some Related Topics

    General Form: (defun-sk fn (var1 ... varn) body &key rewrite doc quant-ok skolem-name thm-name witness-dcls strengthen)
    where fn is the symbol you wish to define and is a new symbolic name (see name), (var1 ... varn) is its list of formal parameters (see name), and body is its body, which must be quantified as described below. The &key argument doc is an optional documentation string to be associated with fn; for a description of its form, see doc-string. In the case that n is 1, the list (var1) may be replaced by simply var1. The other arguments are explained below.

    For a simple example, see defun-sk-example. For a more elaborate example, see Tutorial4-Defun-Sk-Example. See quantifier-tutorial for a careful beginner's introduction that takes you through typical kinds of quantifier-based reasoning in ACL2. Also see quantifiers for an example illustrating how the use of recursion, rather than explicit quantification with defun-sk, may be preferable.

    Below we describe the defun-sk event precisely. First, let us consider the examples above. The first example, again, is:

    (defun-sk exists-x-p0-and-q0 (y z)
      (exists x
              (and (p0 x y z)
                   (q0 x y z))))
    
    It is intended to represent the predicate with formal parameters y and z that holds when for some x, (and (p0 x y z) (q0 x y z)) holds. In fact defun-sk is a macro that adds the following two events, as shown just below. The first event guarantees that if this new predicate holds of y and z, then the term shown, (exists-x-p0-and-q0-witness y z), is an example of the x that is therefore supposed to exist. (Intuitively, we are axiomatizing exists-x-p0-and-q0-witness to pick a witness if there is one. We comment below on the use of defun-nx; for now, consider defun-nx to be defun.) Conversely, the second event below guarantees that if there is any x for which the term in question holds, then the new predicate does indeed hold of y and z.
    (defun-nx exists-x-p0-and-q0 (y z)
      (let ((x (exists-x-p0-and-q0-witness y z)))
        (and (p0 x y z) (q0 x y z))))
    (defthm exists-x-p0-and-q0-suff
      (implies (and (p0 x y z) (q0 x y z))
               (exists-x-p0-and-q0 y z)))
    
    Now let us look at the third example from the introduction above:
    (defun-sk forall-x-y-p0-and-q0 (z)
      (forall (x y)
              (and (p0 x y z)
                   (q0 x y z))))
    
    The intention is to introduce a new predicate (forall-x-y-p0-and-q0 z) which states that the indicated conjunction holds of all x and all y together with the given z. This time, the axioms introduced are as shown below. The first event guarantees that if the application of function forall-x-y-p0-and-q0-witness to z picks out values x and y for which the given term (and (p0 x y z) (q0 x y z)) holds, then the new predicate forall-x-y-p0-and-q0 holds of z. Conversely, the (contrapositive of) the second axiom guarantees that if the new predicate holds of z, then the given term holds for all choices of x and y (and that same z).
    (defun-nx forall-x-y-p0-and-q0 (z)
      (mv-let (x y)
              (forall-x-y-p0-and-q0-witness z)
              (and (p0 x y z) (q0 x y z))))
    (defthm forall-x-y-p0-and-q0-necc
      (implies (not (and (p0 x y z) (q0 x y z)))
               (not (forall-x-y-p0-and-q0 z))))
    
    The examples above suggest the critical property of defun-sk: it indeed does introduce the quantified notions that it claims to introduce.

    Notice that the defthm event just above, forall-x-y-p0-and-q0-necc, may not be of optimal form as a rewrite rule. Users sometimes find that when the quantifier is forall, it is useful to state this rule in a form where the new quantified predicate is a hypothesis instead. In this case that form would be as follows:

    (defthm forall-x-y-p0-and-q0-necc
      (implies (forall-x-y-p0-and-q0 z)
               (and (p0 x y z) (q0 x y z))))
    
    ACL2 will turn this into one :rewrite rule for each conjunct, (p0 x y z) and (q0 x y z), with hypothesis (forall-x-y-p0-and-q0 z) in each case. In order to get this effect, use :rewrite :direct, in this case as follows.
    (defun-sk forall-x-y-p0-and-q0 (z)
      (forall (x y)
              (and (p0 x y z)
                   (q0 x y z)))
      :rewrite :direct)
    

    We now turn to a detailed description of defun-sk, starting with a discussion of its arguments as shown in the "General Form" above.

    The third argument, body, must be of the form

    (Q bound-vars term)
    
    where: Q is the symbol forall or exists (in the "ACL2" package), bound-vars is a variable or true list of variables disjoint from (var1 ... varn) and not including state, and term is a term. The case that bound-vars is a single variable v is treated exactly the same as the case that bound-vars is (v).

    The result of this event is to introduce a ``Skolem function,'' whose name is the keyword argument skolem-name if that is supplied, and otherwise is the result of modifying fn by suffixing "-WITNESS" to its name. The following definition and one of the following two theorems (as indicated) are introduced for skolem-name and fn in the case that bound-vars (see above) is a single variable v. The name of the defthm event may be supplied as the value of the keyword argument :thm-name; if it is not supplied, then it is the result of modifying fn by suffixing "-SUFF" to its name in the case that the quantifier is exists, and "-NECC" in the case that the quantifier is forall.

    (defun-nx fn (var1 ... varn)
      (let ((v (skolem-name var1 ... varn)))
        term))
    
    (defthm fn-suff ;in case the quantifier is EXISTS
      (implies term
               (fn var1 ... varn)))
    
    (defthm fn-necc ;in case the quantifier is FORALL
      (implies (not term)
               (not (fn var1 ... varn))))
    

    In the forall case, however, the keyword pair :rewrite :direct may be supplied after the body of the defun-sk form, in which case the contrapositive of the above form is used instead:

    (defthm fn-necc ;in case the quantifier is FORALL
      (implies (fn var1 ... varn)
               term))
    
    This is often a better choice for the "-NECC" rule, provided ACL2 can parse term as a :rewrite rule. A second possible value of the :rewrite argument of defun-sk is :default, which gives the same behavior as when :rewrite is omitted. Otherwise, the value of :rewrite should be the term to use as the body of the fn-necc theorem shown above; ACL2 will attempt to do the requisite proof in this case. If that term is weaker than the default, the properties introduced by defun-sk may of course be weaker than they would be otherwise. Finally, note that the :rewrite keyword argument for defun-sk only makes sense if the quantifier is forall; it is thus illegal if the quantifier is exists. Enough said about :rewrite!

    In the case that bound-vars is a list of at least two variables, say (bv1 ... bvk), the definition above (with no keywords) is the following instead, but the theorem remains unchanged.

    (defun-nx fn (var1 ... varn)
      (mv-let (bv1 ... bvk)
              (skolem-name var1 ... varn)
              term))
    

    In order to emphasize that the last element of the list, body, is a term, defun-sk checks that the symbols forall and exists do not appear anywhere in it. However, on rare occasions one might deliberately choose to violate this convention, presumably because forall or exists is being used as a variable or because a macro call will be eliminating ``calls of'' forall and exists. In these cases, the keyword argument quant-ok may be supplied a non-nil value. Then defun-sk will permit forall and exists in the body, but it will still cause an error if there is a real attempt to use these symbols as quantifiers.

    The use of defun-nx above, rather than defun, disables certain checks that are required for evaluation, in particular the single-threaded use of stobjs. However, there is a price: calls of these defined functions cannot be evaluated; see defun-nx. Normally that is not a problem, since these notions involve quantifiers. But you are welcome to replace this declare form with your own, as follows: if you supply a list of declare forms to keyword argument :witness-dcls, these will become the declare forms in the generated defun. Note that if your value of witness-dcls does not contain the form (declare (xargs :non-executable t)), then the appropriate wrapper for non-executable functions will not be added automatically, i.e., defun will be used in place of defun-nx. Note also that if guard verification is attempted, then it will likely fail with an error message complaining that ``guard verification may depend on local properties.'' In that case, you may wish to delay guard verification, as in the following example.

    (encapsulate
     ()
     (defun-sk foo (x)
       (exists n (and (integerp n)
                      (< n x)))
       :witness-dcls ((declare (xargs :guard (integerp x)
                                      :verify-guards nil))))
     (verify-guards foo))
    

    Defun-sk is a macro implemented using defchoose. Hence, it should only be executed in defun-mode :logic; see defun-mode and see defchoose. Advanced feature: If argument :strengthen t is passed to defun-sk, then :strengthen t will generate the extra constraint that that is generated for the corresponding defchoose event; see defchoose. You can use the command :pcb! to see the event generated by a call of the defun-sk macro.

    If you find that the rewrite rules introduced with a particular use of defun-sk are not ideal, even when using the :rewrite keyword discussed above (in the forall case), then at least two reasonable courses of action are available for you. Perhaps the best option is to prove the rewrite rules you want. If you see a pattern for creating rewrite rules from your defun-sk events, you might want to write a macro that executes a defun-sk followed by one or more defthm events. Another option is to write your own variant of the defun-sk macro, say, my-defun-sk, for example by modifying a copy of the definition of defun-sk from the ACL2 sources.

    If you want to represent nested quantifiers, you can use more than one defun-sk event. For example, in order to represent

    (forall x (exists y (p x y z)))
    
    you can use defun-sk twice, for example as follows.
    (defun-sk exists-y-p (x z)
      (exists y (p x y z)))
    
    (defun-sk forall-x-exists-y-p (z)
      (forall x (exists-y-p x z)))
    

    Some distracting and unimportant warnings are inhibited during defun-sk.

    Note for ACL2(r) users (see real): In ACL2(r), the keyword :CLASSICALP is also supported. Its legal values are t (the default) and nil, and it determines whether or not (respectively) ACL2(r) will consider fn to be a classical function. It must be the case that the value is t (perhaps implicitly, by default) if and only if body is classical.

    Note that this way of implementing quantifiers is not a new idea. Hilbert was certainly aware of it 60 years ago! Also see conservativity-of-defchoose for a technical argument that justifies the logical conservativity of the defchoose event in the sense of the paper by Kaufmann and Moore entitled ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203).




    acl2-sources/doc/HTML/DEFUN.html0000664002132200015000000003362012222333517015617 0ustar kaufmannacl2 DEFUN.html -- ACL2 Version 6.3

    DEFUN

    define a function symbol
    Major Section:  EVENTS
    

    Examples:
    (defun app (x y)
      (if (consp x)
          (cons (car x) (app (cdr x) y))
          y))
    
    (defun fact (n)
      (declare (xargs :guard (and (integerp n)
                                  (>= n 0))))
      (if (zp n)
          1
          (* n (fact (1- n)))))
    
    General Form:
    (defun fn (var1 ... varn) doc-string dcl ... dcl body),
    
    where fn is the symbol you wish to define and is a new symbolic name (see name), (var1 ... varn) is its list of formal parameters (see name), and body is its body. The definitional axiom is logically admissible provided certain restrictions are met. These are sketched below.

    Note that ACL2 does not support the use of lambda-list keywords (such as &optional) in the formals list of functions. We do support some such keywords in macros and often you can achieve the desired syntax by defining a macro in addition to the general version of your function. See defmacro. The documentation string, doc-string, is optional; for a description of its form, see doc-string.

    The declarations (see declare), dcl, are also optional. If more than one dcl form appears, they are effectively grouped together as one. Perhaps the most commonly used ACL2 specific declaration is of the form (declare (xargs :guard g :measure m)). This declaration in the defun of some function fn has the effect of making the ``guard'' for fn be the term g and the ``measure'' be the term m. The notion of ``measure'' is crucial to ACL2's definitional principle. The notion of ``guard'' is not, and is discussed elsewhere; see verify-guards and see set-verify-guards-eagerness. Note that the :measure is ignored in :program mode; see defun-mode.

    We now briefly discuss the ACL2 definitional principle, using the following definition form which is offered as a more or less generic example.

    (defun fn (x y)
      (declare (xargs :guard (g x y)
                      :measure (m x y)))
      (if (test x y)
          (stop x y)
        (step (fn (d x) y))))
    
    Note that in our generic example, fn has just two arguments, x and y, the guard and measure terms involve both of them, and the body is a simple case split on (test x y) leading to a ``non-recursive'' branch, (stop x y), and a ``recursive'' branch. In the recursive branch, fn is called after ``decrementing'' x to (d x) and some step function is applied to the result. Of course, this generic example is quite specific in form but is intended to illustrate the more general case.

    Provided this definition is admissible under the logic, as outlined below, it adds the following axiom to the logic.

    Defining Axiom:
    (fn x y)
      =
    (if (test x y)
        (stop x y)
      (step (fn (d x) y)))
    
    Note that the guard of fn has no bearing on this logical axiom.

    This defining axiom is actually implemented in the ACL2 system by a :definition rule, namely

    (equal (fn x y)
           (if (test a b)
               (stop a b)
             (step (fn (d a) b)))).
    
    See definition for a discussion of how definition rules are applied. Roughly speaking, the rule causes certain instances of (fn x y) to be replaced by the corresponding instances of the body above. This is called ``opening up'' (fn x y). The instances of (fn x y) opened are chosen primarily by heuristics which determine that the recursive calls of fn in the opened body (after simplification) are more desirable than the unopened call of fn.

    This discussion has assumed that the definition of fn was admissible. Exactly what does that mean? First, fn must be a previously unaxiomatized function symbol (however, see ld-redefinition-action). Second, the formal parameters must be distinct variable names. Third, the guard, measure, and body should all be terms and should mention no free variables except the formal parameters. Thus, for example, body may not contain references to ``global'' or ``special'' variables; ACL2 constants or additional formals should be used instead.

    The final conditions on admissibility concern the termination of the recursion. Roughly put, all applications of fn must terminate. In particular, there must exist a binary relation, rel, and some unary predicate mp such that rel is well-founded on objects satisfying mp, the measure term m must always produce something satisfying mp, and the measure term must decrease according to rel in each recursive call, under the hypothesis that all the tests governing the call are satisfied. By the meaning of well-foundedness, we know there are no infinitely descending chains of successively rel-smaller mp-objects. Thus, the recursion must terminate.

    The only primitive well-founded relation in ACL2 is o< (see o<), which is known to be well-founded on the o-ps (see o-p). For the proof of well-foundedness, see proof-of-well-foundedness. However it is possible to add new well-founded relations. For details, see well-founded-relation. We discuss later how to specify which well-founded relation is selected by defun and in the present discussion we assume, without loss of generality, that it is o< on the o-ps.

    For example, for our generic definition of fn above, with measure term (m x y), two theorems must be proved. The first establishes that m produces an ordinal:

    (o-p (m x y)).
    
    The second shows that m decreases in the (only) recursive call of fn:
    (implies (not (test x y))
             (o< (m (d x) y) (m x y))).
    
    Observe that in the latter formula we must show that the ``m-size'' of (d x) and y is ``smaller than'' the m-size of x and y, provided the test, (test x y), in the body fails, thus leading to the recursive call (fn (d x) y).

    See o< for a discussion of this notion of ``smaller than.'' It should be noted that the most commonly used ordinals are the natural numbers and that on natural numbers, o< is just the familiar ``less than'' relation (<). Thus, it is very common to use a measure m that returns a nonnegative integer, for then (o-p (m x y)) becomes a simple conjecture about the type of m and the second formula above becomes a conjecture about the less-than relationship of nonnegative integer arithmetic.

    The most commonly used measure function is acl2-count, which computes a nonnegative integer size for all ACL2 objects. See acl2-count.

    Probably the most common recursive scheme in Lisp programming is when some formal is supposed to be a list and in the recursive call it is replaced by its cdr. For example, (test x y) might be simply (atom x) and (d x) might be (cdr x). In that case, (acl2-count x) is a suitable measure because the acl2-count of a cons is strictly larger than the acl2-counts of its car and cdr. Thus, ``recursion by car'' and ``recursion by cdr'' are trivially admitted if acl2-count is used as the measure and the definition protects every recursive call by a test insuring that the decremented argument is a consp. Similarly, ``recursion by 1-'' in which a positive integer formal is decremented by one in recursion, is also trivially admissible. See built-in-clause to extend the class of trivially admissible recursive schemes.

    We now turn to the question of which well-founded relation defun uses. It should first be observed that defun must actually select both a relation (e.g., o<) and a domain predicate (e.g., o-p) on which that relation is known to be well-founded. But, as noted elsewhere (see well-founded-relation), every known well-founded relation has a unique domain predicate associated with it and so it suffices to identify simply the relation here.

    The xargs field of a declare permits the explicit specification of any known well-founded relation with the keyword :well-founded-relation. An example is given below. If the xargs for a defun specifies a well-founded relation, that relation and its associated domain predicate are used in generating the termination conditions for the definition.

    If no :well-founded-relation is specified, defun uses the :well-founded-relation specified in the acl2-defaults-table. See set-well-founded-relation to see how to set the default well-founded relation (and, implicitly, its domain predicate). The initial default well-founded relation is o< (with domain predicate o-p).

    This completes the brief sketch of the ACL2 definitional principle. Optionally, see ruler-extenders for a more detailed discussion of the termination analysis and resulting proof obligations for admissibility, as well as a discussion of the relation to how ACL2 stores induction schemes.

    On very rare occasions ACL2 will seem to "hang" when processing a definition, especially if there are many subexpressions of the body whose function symbol is if (or which macroexpand to such an expression). In those cases you may wish to supply the following to xargs: :normalize nil. This is an advanced feature that turns off ACL2's usual propagation upward of if tests.

    The following example illustrates all of the available declarations and most hint keywords, but is completely nonsensical. For documentation, see xargs and see hints.

    (defun example (x y z a b c i j)
      (declare (ignore a b c)
               (type integer i j)
               (xargs :guard (symbolp x)
                      :measure (- i j)
                      :ruler-extenders :basic
                      :well-founded-relation my-wfr
                      :hints (("Goal"
                               :do-not-induct t
                               :do-not '(generalize fertilize)
                               :expand ((assoc x a) (member y z))
                               :restrict ((<-trans ((x x) (y (foo x)))))
                               :hands-off (length binary-append)
                               :in-theory (set-difference-theories
                                            (current-theory :here)
                                            '(assoc))
                               :induct (and (nth n a) (nth n b))
                               :use ((:instance assoc-of-append
                                                (x a) (y b) (z c))
                                     (:functional-instance
                                       (:instance p-f (x a) (y b))
                                       (p consp)
                                       (f assoc)))))
                      :guard-hints (("Subgoal *1/3'"
                                     :use ((:instance assoc-of-append
                                                      (x a) (y b) (z c)))))
                      :mode :logic
                      :normalize nil
                      :verify-guards nil
                      :non-executable t
                      :otf-flg t))
      (example-body x y z i j))
    




    acl2-sources/doc/HTML/DEFUND-INLINE.html0000664002132200015000000000125712222333517016700 0ustar kaufmannacl2 DEFUND-INLINE.html -- ACL2 Version 6.3

    DEFUND-INLINE

    define a potentially disabled, inlined function symbol and associated macro
    Major Section:  EVENTS
    

    Defund-inline is a variant of defun-inline, the difference being that defund-inline disables the newly-defined function symbol. See defun-inline.




    acl2-sources/doc/HTML/DEFUND-NOTINLINE.html0000664002132200015000000000130512222333517017253 0ustar kaufmannacl2 DEFUND-NOTINLINE.html -- ACL2 Version 6.3

    DEFUND-NOTINLINE

    define a disabled, not-to-be-inlined function symbol and associated macro
    Major Section:  EVENTS
    

    Defund-notinline is a variant of defun-notinline, the difference being that defund-notinline disables the newly-defined function symbol. See defun-notinline.




    acl2-sources/doc/HTML/DEFUND.html0000664002132200015000000000553312222333517015725 0ustar kaufmannacl2 DEFUND.html -- ACL2 Version 6.3

    DEFUND

    define a function symbol and then disable it
    Major Section:  EVENTS
    

    Use defund instead of defun when you want to disable a function immediately after its definition in :logic mode. This macro has been provided for users who prefer working in a mode where functions are only enabled when explicitly directed by :in-theory. Specifically, the form

    (defund NAME FORMALS ...)
    
    expands to:
    (progn
      (defun NAME FORMALS ...)
      (with-output
       :off summary
       (in-theory (disable NAME)))
      (value-triple '(:defund NAME))).
    
    Only the :definition rule (and, for recursively defined functions, the :induction rule) for the function are disabled. In particular, defund does not disable either the :type-prescription or the :executable-counterpart rule. Also, the summary for the in-theory event is suppressed.

    If the function is defined in :program mode, either because the default-defun-mode is :program or because :mode :program has been specified in an xargs form of a declare form, then no in-theory event is executed. (More precisely, in-theory events are ignored when the default-defun-mode is :program, and if :mode :program is specified then defund does not generate an in-theory event.)

    Note that defund commands are never redundant (see redundant-events) when the default-defun-mode is :logic, because the in-theory event will always be executed.

    See defun for documentation of defun.




    acl2-sources/doc/HTML/DEFUNS.html0000664002132200015000000000300412222333520015725 0ustar kaufmannacl2 DEFUNS.html -- ACL2 Version 6.3

    DEFUNS

    an alternative to mutual-recursion
    Major Section:  MISCELLANEOUS
    

    Example:
    (DEFUNS
     (evenlp (x)
       (if (consp x) (oddlp (cdr x)) t))
     (oddlp (x)
       (if (consp x) (evenlp (cdr x)) nil)))
    
    General Form:
    (DEFUNS defuns-tuple1 ... defuns-tuplen)
    
    is equivalent to
    (MUTUAL-RECURSION
      (DEFUN . defuns-tuple1)
      ...
      (DEFUN . defuns-tuplen))
    
    In fact, defuns is the more primitive of the two and mutual-recursion is just a macro that expands to a call of defun after stripping off the defun at the car of each argument to mutual-recursion. We provide and use mutual-recursion rather than defuns because by leaving the defuns in place, mutual-recursion forms can be processed by the Emacs tags program. See mutual-recursion.




    acl2-sources/doc/HTML/DELETE-ASSOC-EQ.html0000664002132200015000000000065712222333523017072 0ustar kaufmannacl2 DELETE-ASSOC-EQ.html -- ACL2 Version 6.3

    DELETE-ASSOC-EQ

    See delete-assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/DELETE-ASSOC-EQUAL.html0000664002132200015000000000066512222333523017433 0ustar kaufmannacl2 DELETE-ASSOC-EQUAL.html -- ACL2 Version 6.3

    DELETE-ASSOC-EQUAL

    See delete-assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/DELETE-ASSOC.html0000664002132200015000000000444412222333523016565 0ustar kaufmannacl2 DELETE-ASSOC.html -- ACL2 Version 6.3

    DELETE-ASSOC

    remove the first pair from an association list for a given key
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (delete-assoc key alist)
    (delete-assoc key alist :test 'eql)   ; same as above (eql as equality test)
    (delete-assoc key alist :test 'eq)    ; same, but eq is equality test
    (delete-assoc key alist :test 'equal) ; same, but equal is equality test
    

    (Delete-assoc key alist) returns an alist that is the same as the list alist, except that the first pair in alist with a car of key is deleted, if there is one; otherwise alist is returned. Note that the order of the elements of alist is unchanged (though one may be deleted).

    The guard for a call of delete-assoc depends on the test. In all cases, the second argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-alistp.

    See equality-variants for a discussion of the relation between delete-assoc and its variants:

    (delete-assoc-eq key alist) is equivalent to (delete-assoc key alist :test 'eq);

    (delete-assoc-equal key alist) is equivalent to (delete-assoc key alist :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function delete-assoc-equal.




    acl2-sources/doc/HTML/DELETE-INCLUDE-BOOK-DIR.html0000664002132200015000000000370712222333530020143 0ustar kaufmannacl2 DELETE-INCLUDE-BOOK-DIR.html -- ACL2 Version 6.3

    DELETE-INCLUDE-BOOK-DIR

    unlink keyword for :dir argument of ld and include-book
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Forms:
    (delete-include-book-dir :smith)
     ; Remove association of directory with :smith for include-book.
    
    General Form:
    (delete-include-book-dir kwd)
    
    where kwd is a keywordp. The effect of this event is to modify the meaning of the :dir keyword argument of include-book and ld as indicated by the examples above, namely by removing association of any directory with the indicated keyword for purposes of the include-book (and ld) :dir argument. Normally one would instead use add-include-book-dir to associate a new directory with that keyword; see add-include-book-dir.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    This macro is local to any books and encapsulate events in which it occurs; see add-include-book-dir for a discussion of this aspect of both macros.




    acl2-sources/doc/HTML/DENOMINATOR.html0000664002132200015000000000123112222333523016523 0ustar kaufmannacl2 DENOMINATOR.html -- ACL2 Version 6.3

    DENOMINATOR

    divisor of a ratio in lowest terms
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-denominator):

    (equal (denominator x)
           (if (rationalp x)
               (denominator x)
             1))
    

    Guard for (denominator x):

    (rationalp x)
    




    acl2-sources/doc/HTML/DIGIT-CHAR-P.html0000664002132200015000000000226512222333523016524 0ustar kaufmannacl2 DIGIT-CHAR-P.html -- ACL2 Version 6.3

    DIGIT-CHAR-P

    the number, if any, corresponding to a given character
    Major Section:  ACL2-BUILT-INS
    

    (digit-char-p ch) is the integer corresponding to the character ch in base 10. For example, (digit-char-p #\3) is equal to the integer 3. More generally, an optional second argument specifies the radix (default 10, as indicated above).

    The guard for digit-char-p (more precisely, for the function our-digit-char-p that calls of this macro expand to) requires its second argument to be an integer between 2 and 36, inclusive, and its first argument to be a character.

    Digit-char-p is a Common Lisp function, though it is implemented in the ACL2 logic as an ACL2 macro. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/DIGIT-TO-CHAR.html0000664002132200015000000000172412222333523016646 0ustar kaufmannacl2 DIGIT-TO-CHAR.html -- ACL2 Version 6.3

    DIGIT-TO-CHAR

    map a digit to a character
    Major Section:  ACL2-BUILT-INS
    

    Example:
    ACL2 !>(digit-to-char 8)
    #\8
    
    For an integer n from 0 to 15, (digit-to-char n) is the character corresponding to n in hex notation, using uppercase letters for digits exceeding 9. If n is in the appropriate range, that result is of course also the binary, octal, and decimal digit.

    The guard for digit-to-char requires its argument to be an integer between 0 and 15, inclusive.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/DIMENSIONS.html0000664002132200015000000000266312222333525016430 0ustar kaufmannacl2 DIMENSIONS.html -- ACL2 Version 6.3

    DIMENSIONS

    return the :dimensions from the header of a 1- or 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (dimensions 'delta1 a)
    
    General Form:
    (dimensions name alist)
    
    where name is arbitrary and alist is a 1- or 2-dimensional array. This function returns the dimensions list of the array alist. That list will either be of the form (dim1) or (dim1 dim2), depending on whether alist is a 1- or 2-dimensional array. Dim1 and dim2 will be integers and each exceed by 1 the maximum legal corresponding index. Thus, if dimensions returns, say, '(100) for an array a named 'delta1, then (aref1 'delta1 a 99) is legal but (aref1 'delta1 a 100) violates the guards on aref1. Dimensions operates in virtually constant time if alist is the semantic value of name. See arrays.




    acl2-sources/doc/HTML/DISABLE-FORCING.html0000664002132200015000000000223712222333520017100 0ustar kaufmannacl2 DISABLE-FORCING.html -- ACL2 Version 6.3

    DISABLE-FORCING

    to disallow forced case-splits
    Major Section:  MISCELLANEOUS
    

    General Form:
    ACL2 !>:disable-forcing   ; disallow forced case splits
    
    See force and see case-split for a discussion of forced case splits, which are inhibited by this command.

    Disable-forcing is actually a macro that disables the executable counterpart of the function symbol force; see force. When you want to use hints to turn off forced case splits, use a form such as one of the following (these are equivalent).

    :in-theory (disable (:executable-counterpart force))
    :in-theory (disable (force))
    




    acl2-sources/doc/HTML/DISABLE-IMMEDIATE-FORCE-MODEP.html0000664002132200015000000000256612222333520021012 0ustar kaufmannacl2 DISABLE-IMMEDIATE-FORCE-MODEP.html -- ACL2 Version 6.3

    DISABLE-IMMEDIATE-FORCE-MODEP

    forced hypotheses are not attacked immediately
    Major Section:  MISCELLANEOUS
    

    General Form:
    ACL2 !>:disable-immediate-force-modep
    
    This event causes ACL2 to delay forced hypotheses to the next forcing round, rather than attacking them immediately. See immediate-force-modep. Or for more basic information, first see force for a discussion of forced case splits.

    Disable-immediate-force-modep is a macro that disables the executable counterpart of the function symbol immediate-force-modep. When you want to disable this mode in hints, use a form such as one of the following (these are equivalent).

    :in-theory (disable (:executable-counterpart immediate-force-modep))
    :in-theory (disable (immediate-force-modep))
    




    acl2-sources/doc/HTML/DISABLE.html0000664002132200015000000000270512222333531016015 0ustar kaufmannacl2 DISABLE.html -- ACL2 Version 6.3

    DISABLE

    deletes names from current theory
    Major Section:  THEORIES
    

    Example:
    (disable fact (fact) associativity-of-app)
    
    General Form:
    (disable name1 name2 ... namek)
    
    where each namei is a runic designator; see theories. The result is the theory that contains all the names in the current theory except those listed. Note that this is merely a function that returns a theory. The result is generally a very long list of runes and you will probably regret printing it.

    The standard way to ``disable'' a fixed set of names, is as follows; see hints and see in-theory.

    :in-theory (disable name1 name2 ... namek)    ; in a hint
    (in-theory (disable name1 name2 ... namek))   ; as an event
    (local ; often desirable, to avoid exporting from the current context
     (in-theory (disable name1 name2 ... namek)))
    
    Note that all the names are implicitly quoted. If you wish to disable a computed list of names, lst, use the theory expression (set-difference-theories (current-theory :here) lst).




    acl2-sources/doc/HTML/DISABLEDP.html0000664002132200015000000000324512222333520016237 0ustar kaufmannacl2 DISABLEDP.html -- ACL2 Version 6.3

    DISABLEDP

    determine whether a given name or rune is disabled
    Major Section:  MISCELLANEOUS
    

    Examples:
    
    :disabledp foo   ; returns a list of all disabled runes whose base
                     ; symbol is foo (see rune)
    (disabledp 'foo) ; same as above (i.e., :disabledp foo)
    :disabledp (:rewrite bar . 1) ; returns t if the indicated rune is
                                  ; disabled, else nil
    (disabledp (:rewrite bar . 1)); same as immediately above
    

    Also see pr, which gives much more information about the rules associated with a given event.

    Disabledp takes one argument, an event name (see events) other than nil or a rune. In the former case it returns the list of disabled runes associated with that name, in the sense that the rune's ``base symbol'' is that name (see rune) or, if the event named is a defmacro event, then the list of disabled runes associated with the function corresponding to that macro name, if any (see macro-aliases-table). In the latter case, where the argument is a rune, disabledp returns t if the rune is disabled, and nil otherwise.




    acl2-sources/doc/HTML/DISASSEMBLE$.html0000664002132200015000000000515012222333522016546 0ustar kaufmannacl2 DISASSEMBLE$.html -- ACL2 Version 6.3

    DISASSEMBLE$

    disassemble a function
    Major Section:  OTHER
    

    The macro disassemble$ provides a convenient interface to the underlying disassemble utility of the host Common Lisp implementation, which prints assembly code for a given function symbol at the terminal. It works by including the community book books/misc/disassemble.lisp, which defines the supporting function disassemble$-fn, and then by calling that function. Note that the arguments to disassemble$ are evaluated. Also note that disassemble$ is intended as a top-level utility for the ACL2 loop, not to be called in code; for such a purpose, include the above book and call disassemble$-fn directly.

    Example Forms:
    
    (disassemble$ 'foo)
    (disassemble$ 'foo :recompile t)
    
    General Forms:
    (disassemble$ form)
    (disassemble$ form :recompile flg)
    
    where form evaluates to a function symbol and flg evaluates to any value. If flg is nil, then the existing definition of that function symbol is disassembled. But if flg is supplied and has a value other than nil or :default, and if that function symbol is defined in the ACL2 loop (not merely in raw Lisp; for example, see set-raw-mode), then the disassembly will be based on a recompilation of that ACL2 definition. Normally this recompilation is not necessary, but for some host Lisps, it may be useful; in particular, for CCL the above book arranges that source code information is saved, so that the output is annotated with such information. When recompilation takes place, the previous definition is restored after disassembly is complete. Finally, if flg is omitted or has the value :default -- i.e., in the default case -- then recompilation may take place or not, depending on the host Lisp. The values of (@ host-lisp) for which recompilation takes place by default may be found by looking at the above book, or by including it and evaluating the constant *host-lisps-that-recompile-by-default*. As of this writing, CCL is the only such Lisp (because that is the one for which we can obtain source annotation in the output by recompiling).




    acl2-sources/doc/HTML/DIVE-INTO-MACROS-TABLE.html0000664002132200015000000000466612222333530020066 0ustar kaufmannacl2 DIVE-INTO-MACROS-TABLE.html -- ACL2 Version 6.3

    DIVE-INTO-MACROS-TABLE

    right-associated function information for the proof-checker
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    ACL2 !>(dive-into-macros-table (w state))
    ((CAT . EXPAND-ADDRESS-CAT)
     (LXOR . EXPAND-ADDRESS-LXOR)
    
    This table associates macro names with functions used by the proof-checker's DV and numeric diving commands (e.g., 3) in order to dive properly into subterms. See proof-checker, in particular the documentation for DV.

    This table can be extended easily. See add-dive-into-macro and also see remove-dive-into-macro.

    The symbol associated with a macro should be a function symbol taking four arguments, in this order:

    car-addr ; the first number in the list given to the proof-checker's
               DV command
    raw-term ; the untranslated term into which we will dive
    term     ; the translated term into which we will dive
    wrld     ; the current ACL2 logical world
    
    The function will normally return a list of positive integers, representing the (one-based) address for diving into term that corresponds to the single-address dive into raw-term by car-address. However, it can return (cons str alist), where str is a string suitable for fmt and args is the corresponding alist for fmt.

    Referring to the example above, expand-address-cat would be such a function, which will be called on raw-term values that are calls of cat. See the community book books/misc/rtl-untranslate.lisp for the definition of such a function.

    See table for a general discussion of tables.




    acl2-sources/doc/HTML/DMR.html0000664002132200015000000002220712222333522015373 0ustar kaufmannacl2 DMR.html -- ACL2 Version 6.3

    DMR

    dynamically monitor rewrites and other prover activity
    Major Section:  OTHER
    

    In addition to utilities that allow you to set breakpoints or print rewriting information to the screen -- see break-rewrite -- ACL2 provides a utility for watching the activity of the rewriter and some other proof processes, in real time. This utility is called ``dmr'', which is an acronym for ``dynamically monitor rewrites''. The utility comes in two parts: an ACL2 component that frequently updates a file (the ``dmr file'') containing the relevant information, and an Emacs component that frequently updates the an Emacs buffer (the ``dmr buffer'') with the contents of that file. Other editors could, in principle, be programmed to display that file; anyone developing such a capability is invited to contribute it to the ACL2 community.

    The dmr utility can be extremely helpful for expensive proofs, especially when ACL2 is not providing any output to the terminal. The break-rewrite and accumulated-persistence utilities may be a bit easier to use, so you might want to try those first. But the dmr utility can be a very helpful debugging aide, as it can visually give you a sense of where ACL2 is spending its time.

    The Emacs portion of this utility is already loaded if you load the distributed Emacs file emacs/emacs-acl2.el. Otherwise, invoke the following Emacs command, say by typing Control-X Control-E after the right parenthesis, where DIR is the directory of your ACL2 distribution.

    (load "<DIR>/emacs/monitor.el") ; absolute pathnames might work best
    
    You only need to do that once. Then each time you want to observe the rewriter in action, invoke the following to see it displayed in a buffer, which we call the ``dmr buffer'':
    Control-t 1
    
    But first you will need to enable monitoring at the ACL2 level:
    (dmr-start)
    
    Monitoring has some cost. So if you have started it, then at some point you may want to turn it off when not using it. Any time the dmr buffer (generally called "acl2-dmr-<user_name>") is not visible, Emacs monitoring is turned off. You can also turn off Emacs monitoring explicitly, with:
    Control-t 2
    
    At the ACL2 level you can disable monitoring as follows:
    (dmr-stop)
    

    Interpreting the dmr buffer display.

    We explain the dmr buffer display by way of the following example. It is a snapshot of a dmr buffer taken from one of the community books, books/workshops/2004/legato/support/proof-by-generalization-mult.lisp.

     0. (DEFTHM . WP-ZCOEF-G-MULTIPLIES)
     1. SIMPLIFY-CLAUSE
       2. Rewriting (to simplify) the atom of literal 18; argument(s) 1
       4. Rewriting (to simplify) the expansion; argument(s) 3|2
       7. Applying (:DEFINITION WP-ZCOEF-G)
    *  8. Rewriting (to simplify) the rewritten body; argument(s) 2|1|2|2
    *  13. Applying (:REWRITE MOD-ZERO . 2)
    *    14. Rewriting (to establish) the atom of hypothesis 4
    *    15. Applying (:META META-INTEGERP-CORRECT)
    
    Each line indicates an ACL2 activity that leads to the activity shown on the line just below it. Moreover, lines are sometimes collapsed to make the display more compact. Consider for example the first few lines. Above, we are proving a theorem named WP-ZCOEF-G-MULTIPLIES. Lines 1 and 2 show the clause simplification process invokes the rewriter on the 18th literal. (Recall that a clause is a disjunction of literals; for example the clause {(NOT A), (NOT B), C} would be displayed as (IMPLIES (AND A B) C).) This 18th literal mentioned on line 2 is a function call (f arg1 ...), and ``argument(s) 1'' indicates that the rewriter, which works inside-out, is considering the first argument (``arg1''). Thus the display could instead have shown the following.
       2. Rewriting (to simplify) the atom of literal 18
       3. Rewriting (to simplify) the first argument
       4. Rewriting (to simplify) the expansion; argument(s) 3|2
    
    But it saved space to combine lines 2 and 3. Line 4 suggests that the above arg1 is a function call that has been opened up because of an :expand hint or, perhaps, an expansion directed explicitly by the prover (as can happen during induction). The annotation ``argument(s) 3|2'' then indicates that the rewriter is diving into the third argument of the expansion, and then into the second argument of that. Let us call the result term7 (since it is the one to be considered on line 7).

    Now consider the next two lines:

       7. Applying (:DEFINITION WP-ZCOEF-G)
    *  8. Rewriting (to simplify) the rewritten body; argument(s) 2|1|2|2
    
    Line 7 is saying that term7 (defined above) is modified by applying the definition of WP-ZCOEF-G to it. Line 8 then says that the body of this definition has been rewritten (with its formals bound to the actuals from term7) and the rewriter is diving into the subterms of that rewritten body, as indicated. Notice also that line 8 is the first line marked with an asterisk (``*'') in the margin. This line is the first that is different from what was shown the previous time the display was updated (about 1/10 second earlier, by default). When a line is marked with an asterisk, so are all the lines below it; so the lines without an asterisk are those that have been stable since the last display. In this example we may see line 7 marked without an asterisk for a while, which suggests that the rule (:DEFINITION WP-ZCOEF-G) is expensive. (Also see accumulated-persistence.) In general, a line that persists for awhile without a leading asterisk can suggest why the proof is taking a long time.

    Finally, note the indentation of line 14 relative to line 13. Extra indentation occurs when an attempt is being made to relieve a hypothesis (i.e., rewrite it to t). In particular, rewrites that will be incorporated directly into a (top-level) literal are all indented just two spaces, starting with the first rewrite directly under a process such as SIMPLIFY-CLAUSE (shown line 1 above). If the indentation is at least the value of raw Lisp variable *dmr-indent-max* (by default, 20), then indenttion is restricted to that column, but ACL2 prints {n} where n is the column that would have been used for indentation if there were no maximum.

    You can move the cursor around in the dmr buffer even while it is being updated. But emacs will attempt to keep the cursor no later than the first asterisk (``*'') in the margin. Thus, you can move the cursor around in the stable part of the display, and emacs will keep the cursor in that stable part.

    WARNING: Things could go terribly wrong if the same user runs two different ACL2 sessions with dmr active, because the same file will be written by two different ACL2 processes.

    WARNING: For dmr to work, emacs and ACL2 need to be run on the same machine because the file used to communicate between ACL2 and emacs is under /tmp. Except, you can probably hack around that restriction by changing *dmr-file-name* in ACL2 (in raw Lisp) and correspondingly in Emacs file monitor.el.

    More generally, advanced users are welcome to search for the string

    User-settable dmr variables
    
    in ACL2 files interface-raw.lisp and emacs/monitor.el in order to customize their dmr environments.

    In order to update the dmr file with the latest stack information, interrupt ACL2 and then evaluate: (dmr-flush). In order to support resumption of the interrupted proof (assuming your host Common Lisp supports resumption), evaluation of (dmr-start) will automatically enable the debugger if it is not already enabled and not fully disabled with value :never (see set-debugger-enable). If such automatic enabling takes place, then (dmr-stop) will restore the old setting of the debugger unless, after (dmr-start) enables the debugger but before (dmr-stop) is called, you call set-debugger-enable (or more precisely: function set-debugger-enable-fn is called).

    Note for users of the experimental extension ACL2(p) (see parallelism): when waterfall-parallelism has been set to a non-nil value (see set-waterfall-parallelism), statistics about parallel execution are printed instead of the usual information.




    acl2-sources/doc/HTML/DO-NOT-INDUCT.html0000664002132200015000000000070612222333520016673 0ustar kaufmannacl2 DO-NOT-INDUCT.html -- ACL2 Version 6.3

    DO-NOT-INDUCT

    hints keyword :DO-NOT-INDUCT
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/DO-NOT.html0000664002132200015000000000066112222333520015707 0ustar kaufmannacl2 DO-NOT.html -- ACL2 Version 6.3

    DO-NOT

    hints keyword :DO-NOT
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/DOC-STRING.html0000664002132200015000000004004412222333516016364 0ustar kaufmannacl2 DOC-STRING.html -- ACL2 Version 6.3

    DOC-STRING

    formatted documentation strings
    Major Section:  DOCUMENTATION
    

    Examples:
    ":Doc-Section name
    one-liner~/notes~/details"
    
    ":Doc-Section name
    one-liner~/
    notes~/
    details~/
    :cite old-name1
    :cited-by old-name2"
    
    Use (get-doc-string 'name state) to see other examples.

    Documentation strings not beginning with ``:Doc-Section'' (case is irrelevant) are ignored. See markup for how to supply formatting information (such as fonts and displayed text) in documentation strings.

    ACL2 attaches special importance to documentation strings beginning with the header ``:Doc-Section'' (or any variant thereof obtained by changing case). Any documentation string that does not begin with such a header is considered unformatted and is ignored. For the rest of this discussion, we use the phrase ``documentation string'' as though it read ``formatted documentation string.''

    Documentation strings are always processed in the context of some symbol, name, being defined. (Indeed, if an event defines no symbol, e.g., verify-guards or in-theory, then it is not permitted to have a formatted documentation string.) The string will be associated with name in the ``documentation database.'' The database is divided into ``sections'' and each section is named by a symbol. Among the sections are events, documentation, history, other, and miscellaneous. A complete list of the sections may be obtained by typing :docs * at the terminal. You can create new sections. The main purpose of sections is simply to partition the large set of names into smaller subsets whose contents can be enumerated separately. The idea is that the user may remember (or recognize) the relevant section name and then read its contents to find interesting items.

    Within a section are ``documentation tuples'' which associate with each documented name its documentation string and a list of related documented names, called the ``related names'' of the name. When :doc prints the documentation for name, it always lists the related names.

    When a formatted documentation string is submitted with the defining event of some name, the section name and an initial set of related names are parsed from the string. In addition, the formatted string contains various ``levels'' of detail that are printed out at different times. Finally, it is possible for a string to cause the newly documented name to be added to the related names of any previously documented name. Thus, as new names are introduced they can be grouped with old ones.

    The general form of an ACL2 formatted documentation string is

    ":DOC-SECTION <section-name>
      <one-liner>~/
      <notes>~/
      <details>~/
      :CITE <n1>
      ...
      :CITE <nn>
      :CITED-BY <m1>
      ...
      :CITED-BY <mm>"
    
    Before we explain this, let it be noted that (get-doc-string name state) will return the documentation string associated with name in the documentation database. You may want to call get-doc-string on 'pe and 'union-theories just to see some concrete documentation strings. This documentation string, which is rather long, is under 'doc-string.

    A formatted documentation string has five parts: the header and section-name (terminating in the first #\Newline), the <one-liner>, <notes>, and <details> (each terminating in a tilde-slash (``~/'') pair), and a citation part. These five parts are parsed into six components. <section-name> is read as the name of a symbol, section-name. <one-liner>, <notes>, and <details> are arbitrary sequences of characters (ignoring initial white space and not including the tilde-slash pairs which terminate them). The <ni> are read as symbols and assembled into a list called the ``cite'' symbols. The <mi> are read as symbols and assembled into a list called the ``cited-by'' symbols. See the warning below regarding the hackish nature of our symbol reader.

    Section-name must either be a previously documented symbol or else be name, the symbol being documented. To open a new section of the database, named section-name, you should define the logical name section-name (as by deflabel or any other event; also see defdoc) and attach to it a documentation string for section section-name. You might wish to print out the documentation string we use for some of our section names, e.g., (get-doc-string 'events state). By forcing section names to be documented symbols, we permit sections themselves to have one line descriptions and discussions, presented by the standard documentation facilities like the facilities :doc and :more-doc that may be used at the terminal.

    Each of the ni's and mi's must be previously documented symbols.

    Both <one-liner> and <details> must be non-empty, i.e., must contain some non-whitespace characters. <notes> may be empty. The :cites and :cited-bys pairs may be intermingled and may be separated by either newlines or spaces. The citation part may be empty. When the citation part is empty, the tilde-slash pair terminating the <details> part may be omitted. Thus, the simplest form of a formatted documentation string is:

    ":Doc-Section <section-name>
     <one-liner>~/~/
     <details>"
    
    Since white space at the front of <one-liner>, <notes> and <details> is ignored, we often precede those parts by #\Newlines to make the strings easier to read in our source files. We also typically indent all of the text in the string by starting each line with a few spaces. (The Emacs commands for formatting Lisp get confused if you have arbitrary characters on the left margin.) We assume that every line in <one-liner>, <notes>, and <details> starts with at least as many spaces as <one-liner> does, i.e., we assume they are all indented the same amount (or more). Let d be the number of spaces separating <one-liner> from the #\Newline preceding it. When the various parts are printed, we ``de-indent'' by stripping out the first d spaces following each #\Newline.

    However, we find that when documentation is printed flush against the left margin it is difficult to distinguish the documentation text from previous output. We therefore prefix each line we print by a special pad of characters. By default, this pad is ``| '' so that documentation text has a vertical bar running down the left margin. But the pad is just the value of the global variable doc-prefix and you may assign it any string you wish.

    To add such a string to the database under the symbol name we make a new entry in the section-name section of the database. The entry associates name with the string and uses the string's cites list as the initial value of the related names field. In addition, we add name to the related names field of each of the names listed in the string's cited-by list. We also add name to the related names field of its section-name. Observe that the cites list in a string is only the initial value of the related names of the names. Future documentation strings may add to it via :cited-by or :Doc-Section. Indeed, this is generally the case. We discuss this further below.

    When a brief description of name is required (as by :docs **), name and <one-liner> are printed. <one-liner> is usually printed starting in column 15 (however see print-doc-start-column). Despite its name, <one-liner> need not be one line. It usually is one line, however.

    When you type :doc name at the terminal, the first response will be to print name and <one-liner>. Then :doc prints <notes>, if any. Then, if name is the name of a section, it prints the <one-liner>s for each of its related names. For example, try :doc events. If name is not a section name but does have some related names, they are merely listed but not explained. Try :doc theory-functions. :more-doc name prints <details>.

    Our style is to let each new concept add itself to the related names of old concepts. To do otherwise increases the chances that documentation gets outdated because one often forgets to update supposedly complete lists of the relevant topics when new topics are invented. For example, :doc theory-functions lists each available theory function. But get-doc-string of 'theory-functions just shows a few examples and has an empty cites list. From where do we get the names of the theory functions listed by :doc? The answer is that each theory function has its own documentation string and those strings each specify :cited-by theory-functions. See for example get-doc-string of 'union-theories. So by the time the entire system is assembled, the related names of 'theory-functions contains all the (documented) theory functions. This makes it easy to add new theory functions without changing the general discussion in 'theory-functions.

    When an event or command form is printed, as by :pe or :pc, that contains a formatted documentation string, we do not print the actual documentation string (since they are usually large and distracting). Instead we print the string:

      "Documentation available via :doc"
    
    inviting you to use :doc and :more-doc (or get-doc-string) if you wish to see the documentation at the terminal.

    Warning on Reading Symbols from Strings: When we read a symbol, such as the section-symbol, from a documentation string, we use a quick and dirty imitation of the much more powerful CLTL read program. In particular, we scan past any whitespace, collect all the characters we see until we get to more whitespace or the end of the string, convert the characters to upper case, make a string out of them, and intern that string. Thus, if you typed ":Doc-Section 123 ..." we would read the 123 as the symbol |123|. Observe that special characters, such as parentheses and escape characters, are not afforded their usual reverence by our hack. Furthermore, the question arises: in which package do we intern the symbol? The answer is, usually, the package containing the name being defined. I.e., if you are documenting my-pkg::name and you attach a documentation string that begins ":Doc-Section: Machines ..." then the section-symbol will be my-pkg::machines. We recognize two special cases. If the first character read is a colon, we use the keyword package. If the first five characters read are acl2:: then we intern in the "ACL2" package. Our own section names, e.g., events, are in the "ACL2" package.

    In a related area, when you ask for the documentation of a name, e.g., when you type :doc name at the terminal, that name is read with the full ACL2 reader, not the hack just described. That name is read into the current package. Thus, if you are operating in-package "MY-PKG" and type :doc events, what is read is my-pkg::events. The database may not contain an entry for this symbol. Before reporting that no documentation exists, we try acl2::events.

    One last note: defpkg permits a formatted documentation string, which is associated in the database with the name of the package. But the name of the package is a string, not a symbol. It is permitted to access the documentation of a string (i.e., package name). But there are no facilities for getting such a stringp name into the related names of another name nor of making such stringp names be section names. That is because we always read symbols from strings and never read strings from strings. I.e., if you did write "Doc-Section \"MY-PKG\" ..." it would read in as a weird symbol.




    acl2-sources/doc/HTML/DOC.html0000664002132200015000000000753512222333516015370 0ustar kaufmannacl2 DOC.html -- ACL2 Version 6.3

    DOC

    brief documentation (type :doc name)
    Major Section:  DOCUMENTATION
    

    NOTE: The :doc command only makes sense at the terminal. Most users will probably access the ACL2 documentation in other ways; see documentation. In particular, consider using the xdoc manual at the following location on the web, for topics documented in ACL2 community books as well as the ACL2 system (though the latter are rearranged):

    http://fv.centtech.com/acl2/latest/doc/

    Examples:
    ACL2 !>:doc DEFTHM          ; print documentation of DEFTHM
    ACL2 !>:doc logical-name    ; print documentation of LOGICAL-NAME
    ACL2 !>:doc "MY-PKG"        ; print documentation of "MY-PKG"
    
    Related Topics:
    :more                      ; continues last :doc or :more-doc text
    :more-doc name             ; prints more documentation for name
    :docs **                   ; lists all documented symbols
    :docs "compil"             ; documented symbols apropos "compil"
    :DOC documentation         ; describes how documentation works
    
    General Form:
    ACL2>:doc logical-name
    
    where logical-name is a logical name (see logical-name) for which you hope there is documentation. Chances are there is no documentation at the moment, but we are working on adding documentation strings for all the user level ACL2 functions.

    For a general discussion of our treatment of documentation strings, see documentation.

    This is the first cut at online documentation. Users can be particularly helpful by sending mail on the inadequacies of the system. Address it just to Moore and put Documentation in the subject line. There are several things that trouble me about what I've done here.

    First, many concepts aren't documented. Ultimately, I'd like to . document (a) every CLTL primitive (e.g., case and coerce) and (b) every ACL2 extension (e.g., aref1 and getprop). But so far I have focussed on documenting (c) the ACL2 system primitives (e.g., defthm and what hints look like). My priorities are (c), then (b), and then (a), following the philosophy that the most unstable features should get online documentation in these early releases. Having gotten the basic documentation in place, I'll document new things as they are added, and in response to your pleas I'll try to add documentation to old things that are widely regarded as important.

    Second, I worry that the existing documentation is unhelpful because it provides too much or too little detail, or it provides the detail too far away from where it is needed. Please be on the lookout for this. Did you get what you needed when you appealed to :doc or :more-doc? If not, what was it you needed? Would more cross-references help? Did you get lost in maze of cross-references?




    acl2-sources/doc/HTML/DOCS.html0000664002132200015000000000464312222333516015510 0ustar kaufmannacl2 DOCS.html -- ACL2 Version 6.3

    DOCS

    available documentation topics (by section)
    Major Section:  DOCUMENTATION
    

    NOTE: The :docs command only makes sense at the terminal.

    When the :docs command is given a stringp argument it searches the text produced by :doc and :more-doc and lists all the documented topics whose text contains the given string. For purposes of this string matching we ignore distinctions of case and the amount and kind (but not presence) of white space. We also treat hyphen as whitespace.

    However, the following examples show how :docs can be used on other than string patterns.

    Examples:
    ACL2 !>:docs *           ; lists documentation sections
    ACL2 !>:docs **          ; lists all documented topics within all sections
    ACL2 !>:docs events      ; lists all topics in section EVENTS
    ACL2 !>:docs "compil"    ; lists topics ``apropos''
    

    The database of formatted documentation strings is structured into sections. Within a section are topics. Each topic has a one-liner, some notes, and some detailed discussions. The :docs command provides a view of the entire database.

    :docs takes one argument, as described below:

    arg               effect
    
    *                 list all section headings in the database
    **                list all section headings and all topics within
                      each section
    name              list all topics in the section named name (where
                      name is some symbol other than * and **).  This
                      is always the same as :doc name.
    pattern           list all topics whose :doc or :more-doc text
                      mentions the string pattern.  For purposes of this
                      string matching we ignore distinctions of case and
                      the amount and kind (but not presence) of white
                      space.  We also treat hyphen as whitespace.
    




    acl2-sources/doc/HTML/DOCUMENTATION.html0000664002132200015000000002770612222333516016776 0ustar kaufmannacl2 DOCUMENTATION.html -- ACL2 Version 6.3

    DOCUMENTATION

    functions that display documentation
    Major Section:  ACL2 Documentation
    

    This section explains the ACL2 online documentation system. Thus, most of it assumes that you are typing at the terminal, inside an ACL2 session. If you are reading this description in another setting (for example, in a web browser, in Emacs info, or on paper), simply ignore the parts of this description that involve typing at the terminal.

    ACL2 users are welcome to contribute additional documentation. See the web page http://www.cs.utexas.edu/users/moore/acl2/contrib/.

    For an introduction to the ACL2 online documentation system, type :more below. Whenever the documentation system concludes with ``(type :more for more, :more! for the rest)'' you may type :more to see the next block of documentation.

    Topics related to documentation are documented individually:

    Some Related Topics

    To view the documentation in a web browser, open a browser to file doc/HTML/acl2-doc.html under your ACL2 source directory, or just go to the ACL2 home page at http://www.cs.utexas.edu/users/moore/acl2/.

    Alternatively, follow a link on the ACL2 home page to a manual, known as the xdoc manual, which incorporates (but rearranges) the ACL2 documentation as well as documentation from many ACL2 community books. You can build a local copy of that manual; see for example the section ``BUILDING THE XDOC MANUAL'' in the community books Makefile for instructions.

    To use Emacs Info (inside Emacs), first load distributed file emacs/emacs-acl2.el (perhaps inside your .emacs file) and then execute meta-x acl2-info. In order to see true links to external web pages, you may find the following addition to your .emacs file to be helpful.

    ; For emacs-version 22 or (presumably) later, you can probably set
    ; arrange that in Emacs Info, URLs become links, in the sense that
    ; if you hit <RETURN> while standing on a URL, then you will be
    ; taken to that location in a web browser.  If this does not happen
    ; automatically, then evaluating the `setq' form below might work
    ; if you have firefox.  If that does not work, then you can probably
    ; figure out what to do as follows.  First type
    ;   control-h v browse-url-browser-function
    ; and then from the resulting help page,
    ; hit <return> on the link ``customize'' in:
    ; ``You can customize this variable''
    ; and then follow instructions.
    (setq browse-url-browser-function (quote browse-url-firefox))
    

    There is a print version of the documentation, though we recommend using one of the other methods (web, Emacs Info, or online) to browse it. If you really want the print version, you can find it here: http://www.cs.utexas.edu/users/moore/publications/acl2-book.ps.gz.

    Below we focus on how to access the online documentation, but some of the discussion is relevant to other formats.

    The ACL2 online documentation feature allows you to see extensive documentation on many ACL2 functions and ideas. You may use the documentation facilities to document your own ACL2 functions and theorems.

    If there is some name you wish to know more about, then type

    ACL2 !>:doc name
    
    in the top-level loop. If the name is documented, a brief blurb will be printed. If the name is not documented, but is ``similar'' to some documented names, they will be listed. Otherwise, nil is returned.

    Every name that is documented contains a one-line description, a few notes, and some details. :Doc will print the one-liner and the notes. When :doc has finished it stops with the message ``(type :more for more, :more! for the rest)'' to remind you that details are available. If you then type

    ACL2 !>:more
    
    a block of the continued text will be printed, again concluding with ``(type :more for more, :more! for the rest)'' if the text continues further, or concluding with ``*-'' if the text has been exhausted. By continuing to type :more until exhausting the text you can read successive blocks. Alternatively, you can type :more! to get all the remaining blocks.

    If you want to get the details and don't want to see the elementary stuff typed by :doc name, type:

    ACL2 !>:MORE-DOC name
    
    We have documented not just function names but names of certain important ideas too. For example, see rewrite and see meta to learn about :rewrite rules and :meta rules, respectively. See hints to learn about the structure of the :hints argument to the prover. The deflabel event (see deflabel) is a way to introduce a logical name for no reason other than to attach documentation to it; also see defdoc.

    How do you know what names are documented? There is a documentation database which is querried with the :docs command.

    The documentation database is divided into sections. The sections are listed by

    ACL2 !>:docs *
    
    Each section has a name, sect, and by typing
    ACL2 !>:docs sect
    
    or equivalently
    ACL2 !>:doc sect
    
    you will get an enumeration of the topics within that section. Those topics can be further explored by using :doc (and :more) on them. In fact the section name itself is just a documented name. :more generally gives an informal overview of the general subject of the section.
    ACL2 !>:docs **
    
    will list all documented topics, by section. This fills several pages but might be a good place to start.

    If you want documentation on some topic, but none of our names or brief descriptions seem to deal with that topic, you can invoke a command to search the text in the database for a given string. This is like the GNU Emacs ``apropos'' command.

    ACL2 !>:docs "functional inst"
    
    will list every documented topic whose :doc or :more-doc text includes the substring "functional inst", where case and the exact number of spaces are irrelevant.

    If you want documentation on an ACL2 function or macro and the documentation database does not contain any entries for it, there are still several alternatives.

    ACL2 !>:args fn
    
    will print the arguments and some other relevant information about the named function or macro. This information is all gleaned from the definition (not from the documentation database) and hence this is a definitive way to determine if fn is defined as a function or macro.

    You might also want to type:

    ACL2 !>:pc fn
    
    which will print the command which introduced fn. You should see command-descriptor for details on the kinds of input you can give the :pc command.

    The entire ACL2 documentation database is user extensible. That is, if you document your function definitions or theorems, then that documentation is made available via the database and its query commands.

    The implementation of our online documentation system makes use of Common Lisp's ``documentation strings.'' While Common Lisp permits a documentation string to be attached to any defined concept, Common Lisp assigns no interpretation to these strings. ACL2 attaches special significance to documentation strings that begin with the characters ``:Doc-Section''. When such a documentation string is seen, it is stored in the database and may be displayed via :doc, :more, :docs, etc. Such documentation strings must follow rigid syntactic rules to permit their processing by our commands. These are spelled out elsewhere; see doc-string.

    A description of the structure of the documentation database may also be found; see doc-string.

    Finally: To build the HTML documentation, proceed with the following sequence of steps.

    1. In the doc/ subdirectory of the ACL2 distribution, start ACL2 and then evaluate (certify-book "write-acl2-html").

    2. Exit ACL2 and start it up again (or, evaluate :u).

    3. Include the documented books within your ACL2 loop using include-book.

    4. Evaluate (include-book "../doc/write-acl2-html" :dir :system).

    5. Call macro write-html-file, following the instructions at the end of distributed file doc/write-acl2-html.lisp.




    acl2-sources/doc/HTML/DOC_bang_.html0000664002132200015000000000145712222333516016513 0ustar kaufmannacl2 DOC_bang_.html -- ACL2 Version 6.3

    DOC!

    all the documentation for a name (type :doc! name)
    Major Section:  DOCUMENTATION
    

    NOTE: The :doc! command only makes sense at the terminal.

    Examples:
    ACL2 !>:doc! defthm
    ACL2 !>:doc! certificate
    

    This command is like :doc name followed by :more!. It prints all the documentation of name.




    acl2-sources/doc/HTML/DOUBLE-REWRITE.html0000664002132200015000000002764412222333520017052 0ustar kaufmannacl2 DOUBLE-REWRITE.html -- ACL2 Version 6.3

    DOUBLE-REWRITE

    cause a term to be rewritten twice
    Major Section:  MISCELLANEOUS
    

    Logically, double-rewrite is the identity function: (double-rewrite x) is equal to x. However, the ACL2 rewriter treats calls of double-rewrite in the following special manner. When it encounters a term (double-rewrite u), it first rewrites u in the current context, and then the rewriter rewrites the result.

    Such double-rewriting is rarely necessary, but it can be useful when rewriting under non-trivial equivalence relations (see equivalence). The following example will illustrate the issue.

    ; Define an equivalence relation.
    (defun my-equiv (x y)
      (equal x y))
    (defequiv my-equiv)
    
    ; Define a unary function whose argument is preserved by my-equiv.
    (defun foo (x)
      (declare (ignore x))
      t)
    (defcong my-equiv equal (foo x) 1)
    
    ; Define some other unary functions.
    (defun g (x) x)
    (defun h1 (x) x)
    (defun h2 (x) x)
    
    ; Prove some lemmas and then disable the functions above.
    (defthm lemma-1
      (my-equiv (h1 x) (h2 x)))
    (defthm lemma-2
      (foo (h2 x)))
    (defthm lemma-3
      (implies (foo x)
               (equal (g x) x)))
    (in-theory (union-theories (theory 'minimal-theory)
                               '(lemma-1 lemma-2 lemma-3
                                 my-equiv-implies-equal-foo-1)))
    
    ; Attempt to prove a simple theorem that follows ``obviously'' from the
    ; events above.
    (thm (equal (g (h1 a)) (h1 a)))
    
    We might expect the proof of this final thm to succeed by the following reasoning. It is immediate from lemma-3 provided we can establish (foo (h1 a)). By the defcong event above, we know that (foo (h1 a)) equals (foo (h2 a)) provided (my-equiv (h1 a) (h2 a)); but this is immediate from lemma-1. And finally, (foo (h2 a)) is true by lemma-2.

    Unfortunately, the proof fails. But fortunately, ACL2 gives the following useful warning when lemma-3 is submitted:

    ACL2 Warning [Double-rewrite] in ( DEFTHM LEMMA-3 ...):  In the :REWRITE
    rule generated from LEMMA-3, equivalence relation MY-EQUIV is maintained
    at one problematic occurrence of variable X in hypothesis (FOO X),
    but not at any binding occurrence of X.  Consider replacing that occurrence
    of X in this hypothesis with (DOUBLE-REWRITE X).  See :doc double-
    rewrite for more information on this issue.
    
    We can follow the warning's advice by changing lemma-3 to the following.
    (defthm lemma-3
      (implies (foo (double-rewrite x))
               (equal (g x) x)))
    
    With this change, the proof succeeds for the final thm above.

    In practice, it should suffice for users to follow the advice given in the ``Double-rewrite'' warnings, by adding calls of double-rewrite around certain variable occurrences. But this can cause inefficiency in large proof efforts. For that reason, and for completeness, it seems prudent to explain more carefully what is going on; and that is what we do for the remainder of this documentation topic. Optionally, also see the paper ``Double Rewriting for Equivalential Reasoning in ACL2'' by Matt Kaufmann and J Strother Moore, in the proceedings of the 2006 ACL2 Workshop (paper is published in ACM Digital Library, http://portal.acm.org/toc.cfm?id=1217975).

    Suggesting congruence rules.

    Sometimes the best way to respond to a ``Double-rewrite'' warning may be to prove a congruence rule. Consider for example this rule.

    (defthm insert-sort-is-id
      (perm (insert-sort x) x))
    
    Assuming that perm has been identified as an equivalence relation (see defequiv), we will get the following warning.
    ACL2 Warning [Double-rewrite] in ( DEFTHM INSERT-SORT-IS-ID ...):
    In a :REWRITE rule generated from INSERT-SORT-IS-ID, equivalence relation
    PERM is maintained at one problematic occurrence of variable X in the
    right-hand side, but not at any binding occurrence of X.  Consider
    replacing that occurrence of X in the right-hand side with
    (DOUBLE-REWRITE X).  See :doc double-rewrite for more information on
    this issue.
    
    The problem is that the second occurrence of x (the right-hand side of the rule insert-sort-is-id) is in a context where perm is to be maintained, yet in this example, the argument x of insert-sort on the left-hand side of that rule is in a context where perm will not be maintained. This can lead one to consider the possibility that perm could be maintained in that left-hand side occurrence of x, and if so, to prove the following congruence rule.
    (defcong perm perm (insert-sort x) 1)
    
    This will eliminate the above warning for insert-sort-is-id. More important, this defcong event would probably be useful, since it would allow rewrite rules with equivalence relation perm to operate on the first argument of any call of insert-sort whose context calls for maintaining perm.

    Details on double-rewrite.

    The reader who wants these details may first wish to see equivalence for relevant review.

    The ACL2 rewriter takes a number of contextual arguments, including the generated equivalence relation being maintained (see congruence) and an association list that maps variables to terms. We call the latter alist the unify-subst because it is produced by unifying (actually matching) a pattern against a current term; let us explain this point by returning to the example above. Consider what happens when the rewriter is given the top-level goal of the thm above.

    (equal (g (h1 a)) (h1 a))
    
    This rewrite is performed with the empty alist (unify-subst), and is begun by rewriting the first argument (in that same empty unify-subst):
    (g (h1 a))
    
    Note that the only equivalence relation being maintained at this point is equal. Now, the rewriter notices that the left-hand side of lemma-3, which is (g x), matches (g (h1 a)). The rewriter thus creates a unify-subst binding x to (h1 a): ((x . (h1 a))). It now attempts to rewrite the hypothesis of lemma-3 to t under this unify-subst.

    Consider what happens now if the hypothesis of lemma-3 is (foo x). To rewrite this hypothesis under a unify-subst of ((x . (h1 a))), it will first rewrite x under this unify-subst. The key observation here is that this rewrite takes place simply by returning the value of x in the unify-subst, namely (h1 a). No further rewriting is done! The efficiency of the ACL2 rewriter depends on such caching of previous rewriting results.

    But suppose that, instead, the hypothesis of lemma-3 is (foo (double-rewrite x)). As before, the rewriter dives to the first argument of this call of foo. But this time the rewriter sees the call (double-rewrite x), which it handles as follows. First, x is rewritten as before, yielding (h1 a). But now, because of the call of double-rewrite, the rewriter takes (h1 a) and rewrites it under the empty unify-subst. What's more, because of the defcong event above, this rewrite takes place in a context where it suffices to maintain the equivalence relation my-equiv. This allows for the application of lemma-1, hence (h1 a) is rewritten (under unify-subst = nil) to (h2 a). Popping back up, the rewriter will now rewrite the call of foo to t using lemma-2.

    The example above explains how the rewriter treats calls of double-rewrite, but it may leave the unfortunate impression that the user needs to consider each :rewrite or :linear rule carefully, just in case a call of double-rewrite may be appropriate. Fortunately, ACL2 provides a ``[Double-rewrite]'' warning to inform the user of just this sort of situation. If you don't see this warning when you submit a (:rewrite or :linear) rule, then the issue described here shouldn't come up for that rule. Such warnings may appear for hypotheses or right-hand side of a :rewrite rule, and for hypotheses or full conclusion (as opposed to just the trigger term) of a :linear rule.

    If you do see a ``[Double-rewrite]'' warning, then should you add the indicated call(s) of double-rewrite? At the time of writing this documentation, the answer is not clear. Early experiments with double rewriting suggested that it may be too expensive to call double-rewrite in every instance where a warning indicates that there could be an advantage to doing so. And at the time of this writing, the ACL2 regression suite has about 1900 such warnings (but note that books were developed before double-rewrite or the ``[Double-rewrite]'' warning were implemented), which suggests that one can often do fine just ignoring such warnings. However, it seems advisable to go ahead and add the calls of double-rewrite indicated by the warnings unless you run across efficiency problems caused by doing so. Of course, if you decide to ignore all such warnings you can execute the event:
    (set-inhibit-warnings "Double-rewrite").

    Finally, we note that it is generally not necessary to call double-rewrite in order to get its effect in the following case, where the discussion above might have led one to consider a call of double-rewrite: a hypothesis is a variable, or more generally, we are considering a variable occurrence that is a branch of the top-level IF structure of a hypothesis. The automatic handling of this case, by a form of double rewriting, was instituted in ACL2 Version_2.9 and remains in place with the introduction of double-rewrite. Here is a simple illustrative example. Notice that foo-holds applies to prove the final thm below, even without a call of double-rewrite in the hypothesis of foo-holds, and that there is no ``[Double-rewrite]'' warning when submitting foo-holds.

    (encapsulate
     (((foo *) => *)
      ((bar *) => *))
    
     (local (defun foo (x) (declare (ignore x)) t))
     (local (defun bar (x) (declare (ignore x)) t))
    
     (defthm foo-holds
       (implies x
                (equal (foo x) t)))
     (defthm bar-holds-propositionally
       (iff (bar x) t)))
    
    (thm (foo (bar y)))
    




    acl2-sources/doc/HTML/DYNAMICALLY-MONITOR-REWRITES.html0000664002132200015000000000064512222333522021110 0ustar kaufmannacl2 DYNAMICALLY-MONITOR-REWRITES.html -- ACL2 Version 6.3

    DYNAMICALLY-MONITOR-REWRITES

    See dmr.
    Major Section:  OTHER
    




    acl2-sources/doc/HTML/E0-ORD-_lt_.html0000664002132200015000000000243512222333523016554 0ustar kaufmannacl2 E0-ORD-_lt_.html -- ACL2 Version 6.3

    E0-ORD-<

    the old ordering function for ACL2 ordinals
    Major Section:  ACL2-BUILT-INS
    

    See o< for the current new ordering function for ACL2 ordinals.

    The functions e0-ordinalp and e0-ord-< were replaced in ACL2 Version_2.8 by o-p and o<, respectively. However, books created before that version used the earlier functions for termination proofs; the old functions might be of use in these cases. To use the old functions in termination proofs, include the community book books/ordinals/e0-ordinal and execute the event (set-well-founded-relation e0-ord-<) (see set-well-founded-relation). For a more thorough discussion of these functions, see the documentation at the end of community book books/ordinals/e0-ordinal.lisp.




    acl2-sources/doc/HTML/E0-ORDINALP.html0000664002132200015000000000240712222333523016424 0ustar kaufmannacl2 E0-ORDINALP.html -- ACL2 Version 6.3

    E0-ORDINALP

    the old recognizer for ACL2 ordinals
    Major Section:  ACL2-BUILT-INS
    

    See o-p for the current recognizer for ACL2 ordinals.

    The functions e0-ordinalp and e0-ord-< were replaced in ACL2 Version_2.8 by o-p and o<, respectively. However, books created before that version used the earlier functions for termination proofs; the old functions might be of use in these cases. To use the old functions in termination proofs, include the community book books/ordinals/e0-ordinal and execute the event (set-well-founded-relation e0-ord-<) (see set-well-founded-relation). For a more thorough discussion of these functions, see the documentation at the end of community book books/ordinals/e0-ordinal.lisp.




    acl2-sources/doc/HTML/EARLY-TERMINATION.html0000664002132200015000000000570612222333522017421 0ustar kaufmannacl2 EARLY-TERMINATION.html -- ACL2 Version 6.3

    EARLY-TERMINATION

    early termination for pand and por.
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    The evaluation of (and expr1 expr2) returns nil if expr1 evaluates to nil, avoiding the evaluation of expr2. More generally, the evaluation of (and expr1 expr2 ... exprk) terminates with a return value of nil as soon as any expri evaluates to nil -- no exprj is evaluated in this case for j > i. This so-called ``lazy evaluation'' of and terms can thus save some computation; roughly speaking, the smaller the i, the more computation is saved.

    If the above call of and is replaced by its parallel version, pand, then there can be even more opportunity for skipping work. The arguments to pand can be evaluated in parallel, in which case the first such evaluation that returns with a value of nil, if any, causes the remaining such evaluations to abort.

    Consider the following functions that compute whether a tree is valid (see granularity for a discussion of the granularity form).

    (defun valid-tip (x)
      (declare (xargs :guard t))
      (or (eq x 'A)
          (eq x 'T)
          (eq x 'C)
          (eq x 'G)))
    
    (defun pvalid-tree (x depth)
      (declare (xargs :guard (natp depth)))
      (if (atom x)
          (valid-tip x)
        (pand (declare (granularity (< depth 10)))
              (pvalid-tree (car x) (1+ depth))
              (pvalid-tree (cdr x) (1+ depth)))))
    

    We would like to stop execution as soon as any tip is found to be invalid. So, when computing the conjunction of terms by using pand, once one of those terms evaluates to nil, the computations for the other terms are aborted and the pand call returns nil. By using pand, we can in principle attain a speedup factor greater than the number of available cores.

    The concept of early termination also applies to por, except that early termination occurs when an argument evaluates to non-nil.




    acl2-sources/doc/HTML/EC-CALL.html0000664002132200015000000001464012222333523015754 0ustar kaufmannacl2 EC-CALL.html -- ACL2 Version 6.3

    EC-CALL

    execute a call in the ACL2 logic instead of raw Lisp
    Major Section:  ACL2-BUILT-INS
    

    The name ``ec-call'' represents ``executable-counterpart call.'' This utility is intended for users who are familiar with guards. See guard for a general discussion of guards.

    Logically, ec-call behaves like the identity macro; during proofs, (ec-call TERM) is typically replaced quickly by TERM during a proof attempt. However, ec-call causes function calls to be evaluated in the ACL2 logic rather than raw Lisp, as explained below.

    General Form:
    (ec-call (fn term1 ... termk))
    
    where fn is a known function symbol other than those in the list that is the value of the constant *ec-call-bad-ops*. In particular, fn is not a macro. Semantically, (ec-call (fn term1 ... termk)) equals (fn term1 ... termk). However, this use of ec-call has two effects.

    (1) Guard verification generates no proof obligations from the guard of fn for this call. Indeed, guards need not have been verified for fn.

    (2) During evaluation, after the arguments of fn are evaluated as usual, the executable counterpart of fn is called, rather than fn as defined in raw Lisp. That is, the call of fn is made on its evaluated arguments as though this call is being made in the ACL2 top-level loop, rather than in raw Lisp. In particular, the guard of fn is checked, at least by default (see set-guard-checking).

    Note that in the term (ec-call (fn term1 ... termk)), only the indicated call of fn is made in the logic; each termi is evaluated in the normal manner. If you want an entire term evaluated in the logic, wrap ec-call around each function call in the term (other than calls of if and ec-call).

    Technical Remark (probably best ignored). During evaluation of a call of defconst or defpkg in raw Lisp, a form (ec-call (fn term1 ... termk)) is treated as (fn term1 ... termk), that is, without calling the executable counterpart of fn. This situation occurs when loading a compiled file (or expansion file) on behalf of an include-book event. The reason is technical: executable counterparts are defined below a book's events in the book's compiled file. End of Technical Remark.

    Here is a small example. We define foo recursively but with guard verification inhibited on the recursive call, which is to be evaluated in the ACL2 logic.

    ACL2 !>(defun foo (x y)
            (declare (xargs :guard (consp y)))
            (if (consp x)
                (cons (car x) (ec-call (foo (cdr x) (cdr y))))
              (car y)))
    
    The admission of FOO is trivial, using the relation O< (which is known
    to be well-founded on the domain recognized by O-P) and the measure
    (ACL2-COUNT X).  We could deduce no constraints on the type of FOO.
    
    Computing the guard conjecture for FOO....
    
    The guard conjecture for FOO is trivial to prove.  FOO is compliant
    with Common Lisp.
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO
    ACL2 !>(foo '(2 3 4 5) '(6 7))
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (FOO X Y),
    which is (CONSP Y), is violated by the arguments in the call
    (FOO '(4 5) NIL).  To debug see :DOC print-gv, see :DOC trace, and
    see :DOC wet.  See :DOC set-guard-checking for information about suppressing
    this check with (set-guard-checking :none), as recommended for new
    users.
    
    ACL2 !>
    
    The error above arises because eventually, foo recurs down to a value of parameter y that violates the guard. This is clear from tracing (see trace$ and see trace). Each call of the executable counterpart of foo (the so-called ``*1*'' function for foo) checks the guard and then invokes the raw Lisp version of foo. The raw Lisp version calls the executable counterpart on the recursive call. When the guard check fails we get a violation.
    ACL2 !>(trace$ foo)
     ((FOO))
    ACL2 !>(foo '(2 3 4 5) '(6 7))
    1> (ACL2_*1*_ACL2::FOO (2 3 4 5) (6 7))
      2> (FOO (2 3 4 5) (6 7))
        3> (ACL2_*1*_ACL2::FOO (3 4 5) (7))
          4> (FOO (3 4 5) (7))
            5> (ACL2_*1*_ACL2::FOO (4 5) NIL)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (FOO X Y),
    which is (CONSP Y), is violated by the arguments in the call
    (FOO '(4 5) NIL).  To debug see :DOC print-gv, see :DOC trace, and
    see :DOC wet.  See :DOC set-guard-checking for information about suppressing
    this check with (set-guard-checking :none), as recommended for new
    users.
    
    ACL2 !>
    
    If we turn off guard errors then we can see the trace as above, but where we avoid calling the raw Lisp function when the guard fails to hold.
    ACL2 !>:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(foo '(2 3 4 5) '(6 7))
    1> (ACL2_*1*_ACL2::FOO (2 3 4 5) (6 7))
      2> (FOO (2 3 4 5) (6 7))
        3> (ACL2_*1*_ACL2::FOO (3 4 5) (7))
          4> (FOO (3 4 5) (7))
            5> (ACL2_*1*_ACL2::FOO (4 5) NIL)
              6> (ACL2_*1*_ACL2::FOO (5) NIL)
                7> (ACL2_*1*_ACL2::FOO NIL NIL)
                <7 (ACL2_*1*_ACL2::FOO NIL)
              <6 (ACL2_*1*_ACL2::FOO (5))
            <5 (ACL2_*1*_ACL2::FOO (4 5))
          <4 (FOO (3 4 5))
        <3 (ACL2_*1*_ACL2::FOO (3 4 5))
      <2 (FOO (2 3 4 5))
    <1 (ACL2_*1*_ACL2::FOO (2 3 4 5))
    (2 3 4 5)
    ACL2 >
    




    acl2-sources/doc/HTML/EIGHTH.html0000664002132200015000000000066512222333523015726 0ustar kaufmannacl2 EIGHTH.html -- ACL2 Version 6.3

    EIGHTH

    eighth member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/ELIM.html0000664002132200015000000001745212222333530015504 0ustar kaufmannacl2 ELIM.html -- ACL2 Version 6.3

    ELIM

    make a destructor elimination rule
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    The following example of an :elim rule is an important one, and is built into ACL2.

    (defaxiom car-cdr-elim
      (implies (consp x)
               (equal (cons (car x) (cdr x)) x))
      :rule-classes :elim)
    

    The class of :elim rules is fundamentally quite different from the more common class of :rewrite rules. Briefly put, a :rewrite rule replaces instances of its left-hand side with corresponding instances of its right-hand side. But an :elim rule, on the other hand, has the effect of generalizing so-called ``destructor'' function applications to variables. In essence, applicability of a :rewrite rule is based on matching its left-hand side, while applicability of an :elim rule is based on the presence of at least one destructor term.

    For example, a conjecture about (car x) and (cdr x) can be replaced by a conjecture about new variables x1 and x2, as shown in the following example. (Run the command :mini-proveall and search for CAR-CDR-ELIM to see the full proof containing this excerpt.)

    Subgoal *1/1'
    (IMPLIES (AND (CONSP X)
                  (TRUE-LISTP (REV (CDR X))))
             (TRUE-LISTP (APP (REV (CDR X)) (LIST (CAR X))))).
    
    The destructor terms (CAR X) and (CDR X) can be eliminated by using
    CAR-CDR-ELIM to replace X by (CONS X1 X2), (CAR X) by X1 and (CDR X)
    by X2.  This produces the following goal.
    
    Subgoal *1/1''
    (IMPLIES (AND (CONSP (CONS X1 X2))
                  (TRUE-LISTP (REV X2)))
             (TRUE-LISTP (APP (REV X2) (LIST X1)))).
    
    This simplifies, using primitive type reasoning, to
    
    Subgoal *1/1'''
    (IMPLIES (TRUE-LISTP (REV X2))
             (TRUE-LISTP (APP (REV X2) (LIST X1)))).
    
    The resulting conjecture is often simpler and hence more amenable to proof.

    The application of an :elim rule thus replaces a variable by a term that contains applications of so-called ``destructor'' functions to that variable. The example above is typical: the variable x is replaced by the term (cons (car x) (cdr x)), which applies a so-called ``constructor'' function, cons, to applications (car x) and (cdr x) of destructor functions car and cdr to that same variable, x. But that is only part of the story. ACL2 then generalizes the destructor applications (car x) and (cdr x) to new variables x1 and x2, respectively, and ultimately the result is a simpler conjecture.

    More generally, the application of an :elim rule replaces a variable by a term containing applications of destructors; there need not be a clear-cut notion of ``constructor.'' But the situation described above is typical, and we will focus on it, giving full details when we introduce the ``General Form'' below.

    Notice that the situation can be complicated a bit by a rule's hypotheses. For example, the replacement specified by the rule car-cdr-elim (shown near the beginning of this discussion) is only valid if the variable being replaced is a cons structure. Thus, when ACL2 applies car-cdr-elim to replace a variable v, it will split into two cases: one case in which (consp v) is true, in which v is replaced by (cons (car v) (cdr v)) and then (car v) and (cdr v) are generalized to new variables; and one case in which (consp v) is false. In practice, (consp v) is often provable, perhaps even literally present as a hypotheses; then of course there is no need to introduce the second case. That is why there is no such second case in the example above.

    You might find :elim rules to be useful whenever you have in mind a data type that can be built up from its fields with a ``constructor'' function and whose fields can be accessed by corresponding ``destructor'' functions. So for example, if you have a ``house'' data structure that represents a house in terms of its address, price, and color, you might have a rule like the following.

    Example:
    (implies (house-p x)
             (equal (make-house (address x)
                                (price x)
                                (color x))
                    x))
    
    The application of such a rule is entirely analogous to the application of the rule car-cdr-elim discussed above. We discuss such rules and their application more carefully below.

    General Form:
    (implies hyp (equiv lhs x))
    
    where equiv is a known equivalence relation (see defequiv); x is a variable symbol; and lhs contains one or more terms (called ``destructor terms'') of the form (fn v1 ... vn), where fn is a function symbol and the vi are distinct variable symbols, v1, ..., vn include all the variable symbols in the formula, no fn occurs in lhs in more than one destructor term, and all occurrences of x in lhs are inside destructor terms.

    To use an :elim rule, the theorem prover waits until a conjecture has been maximally simplified. It then searches for an instance of some destructor term (fn v1 ... vn) in the conjecture, where the instance for x is some variable symbol, vi, and every occurrence of vi outside the destructor terms is in an equiv-hittable position. If such an instance is found, then the theorem prover instantiates the :elim formula as indicated by the destructor term matched; splits the conjecture into two goals, according to whether the instantiated hypothesis, hyp, holds; and in the case that it does hold, generalizes all the instantiated destructor terms in the conjecture to new variables and then replaces vi in the conjecture by the generalized instantiated lhs. An occurrence of vi is ``equiv-hittable'' if sufficient congruence rules (see defcong) have been proved to establish that the propositional value of the clause is not altered by replacing that occurrence of vi by some equiv-equivalent term.

    If an :elim rule is not applied when you think it should have been, and the rule uses an equivalence relation, equiv, other than equal, it is most likely that there is an occurrence of the variable that is not equiv-hittable. Easy occurrences to overlook are those in the governing hypotheses. If you see an unjustified occurrence of the variable, you must prove the appropriate congruence rule to allow the :elim to fire.

    Further examples of how ACL2 :elim rules are used may be found in the corresponding discussion of ``Elimation of Destructors'' for Nqthm, in Section 10.4 of A Computational Logic Handbook.




    acl2-sources/doc/HTML/EMACS.html0000664002132200015000000000145512222333515015605 0ustar kaufmannacl2 EMACS.html -- ACL2 Version 6.3

    EMACS

    emacs support for ACL2
    Major Section:  ACL2-TUTORIAL
    

    Many successful ACL2 users run in an shell under the Emacs editor. If you do so, then you may wish to load the distributed file emacs/emacs-acl2.el. The file begins with considerable comments describing what it offers. It is intended to work both with GNU Emacs and XEmacs.

    If you are not comfortable with Emacs, you may prefer to use an Eclipse-based interface; see acl2-sedan.




    acl2-sources/doc/HTML/EMBEDDED-EVENT-FORM.html0000664002132200015000000002150312222333520017516 0ustar kaufmannacl2 EMBEDDED-EVENT-FORM.html -- ACL2 Version 6.3

    EMBEDDED-EVENT-FORM

    forms that may be embedded in other events
    Major Section:  MISCELLANEOUS
    

    Examples:
    (defun hd (x) (if (consp x) (car x) 0))
    (local (defthm lemma23 ...))
    (progn (defun fn1 ...)
           (local (defun fn2 ...))
           ...)
    
    General Form:
    An embedded event form is a term, x, such that:

    o x is a call of an event function other than DEFPKG (see events for a listing of the event functions);

    o x is of the form (LOCAL x1) where x1 is an embedded event form;

    o x is of the form (SKIP-PROOFS x1) where x1 is an embedded event form;

    o x is of the form (MAKE-EVENT &), where & is any term whose expansion is an embedded event (see make-event);

    o x is of the form (WITH-OUTPUT ... x1), (WITH-PROVER-STEP-LIMIT ... x1 ...), or (WITH-PROVER-TIME-LIMIT ... x1), where x1 is an embedded event form;

    o x is a call of ENCAPSULATE, PROGN, PROGN!, or INCLUDE-BOOK;

    o x macroexpands to one of the forms above; or

    o [intended only for the implementation] x is (RECORD-EXPANSION x1 x2), where x1 and x2 are embedded event forms.

    An exception: an embedded event form may not set the acl2-defaults-table when in the context of local. Thus for example, the form

    (local (table acl2-defaults-table :defun-mode :program))
    
    is not an embedded event form, nor is the form (local (program)), since the latter sets the acl2-defaults-table implicitly. An example at the end of the discussion below illustrates why there is this restriction.

    Only embedded event forms are allowed in a book after its initial in-package form. See books. However, you may find that make-event allows you to get the effect you want for a form that is not an embedded event form. For example, you can put the following into a book, which assigns the value 17 to state global variable x:

    (make-event (er-progn (assign x 17)
                          (value '(value-triple nil)))
                :check-expansion t)
    

    When an embedded event is executed while ld-skip-proofsp is 'include-book, those parts of it inside local forms are ignored. Thus,

       (progn (defun f1 () 1)
              (local (defun f2 () 2))
              (defun f3 () 3))
    
    will define f1, f2, and f3 when ld-skip-proofsp is nil or t, but will define only f1 and f3 when ld-skip-proofsp is 'include-book.

    Discussion:

    Encapsulate, progn, and include-book place restrictions on the kinds of forms that may be processed. These restrictions ensure that the non-local events are indeed admissible provided that the sequence of local and non-local events is admissible when proofs are done, i.e., when ld-skip-proofs is nil. But progn! places no such restrictions, hence is potentially dangerous and should be avoided unless you understand the ramifications; so it is illegal unless there is an active trust tag (see defttag).

    Local permits the hiding of an event or group of events in the sense that local events are processed when we are trying to establish the admissibility of a sequence of events embedded in encapsulate forms or in books, but are ignored when we are constructing the world produced by assuming that sequence. Thus, for example, a particularly ugly and inefficient :rewrite rule might be made local to an encapsulate that ``exports'' a desirable theorem whose proof requires the ugly lemma.

    To see why we can't allow just anything as an embedded event, consider allowing the form

    (if (ld-skip-proofsp state)
        (defun foo () 2)
        (defun foo () 1))
    
    followed by
    (defthm foo-is-1 (equal (foo) 1)).
    
    When we process the events with ld-skip-proofsp is nil, the second defun is executed and the defthm succeeds. But when we process the events with ld-skip-proofsp 'include-book, the second defun is executed, so that foo no longer has the same definition it did when we proved foo-is-1. Thus, an invalid formula is assumed when we process the defthm while skipping proofs. Thus, the first form above is not a legal embedded event form.

    If you encounter a situation where these restrictions seem to prevent you from doing what you want to do, then you may find make-event to be helpful. See make-event.

    Defpkg is not allowed because it affects how things are read after it is executed. But all the forms embedded in an event are read before any are executed. That is,

    (encapsulate nil
                 (defpkg "MY-PKG" nil)
                 (defun foo () 'my-pkg::bar))
    
    makes no sense since my-pkg::bar must have been read before the defpkg for "MY-PKG" was executed.

    Finally, let us elaborate on the restriction mentioned earlier related to the acl2-defaults-table. Consider the following form.

    (encapsulate
     ()
     (local (program))
     (defun foo (x)
       (if (equal 0 x)
           0
         (1+ (foo (- x))))))
    
    See local-incompatibility for a discussion of how encapsulate processes event forms. Briefly, on the first pass through the events the definition of foo will be accepted in defun mode :program, and hence accepted. But on the second pass the form (local (program)) is skipped because it is marked as local, and hence foo is accepted in defun mode :logic. Yet, no proof has been performed in order to admit foo, and in fact, it is not hard to prove a contradiction from this definition!




    acl2-sources/doc/HTML/ENABLE-FORCING.html0000664002132200015000000000237212222333520016763 0ustar kaufmannacl2 ENABLE-FORCING.html -- ACL2 Version 6.3

    ENABLE-FORCING

    to allow forced splits
    Major Section:  MISCELLANEOUS
    

    General Form:
    ACL2 !>:enable-forcing    ; allowed forced case splits
    
    See force and see case-split for a discussion of forced case splits, which are turned back on by this command. (See disable-forcing for how to turn them off.)

    Enable-forcing is actually a macro that enables the executable counterpart of the function symbol force; see force. When you want to use hints to turn on forced case splits, use a form such as one of the following (these are equivalent).

    :in-theory (enable (:executable-counterpart force))
    :in-theory (enable (force))
    




    acl2-sources/doc/HTML/ENABLE-IMMEDIATE-FORCE-MODEP.html0000664002132200015000000000254612222333520020673 0ustar kaufmannacl2 ENABLE-IMMEDIATE-FORCE-MODEP.html -- ACL2 Version 6.3

    ENABLE-IMMEDIATE-FORCE-MODEP

    forced hypotheses are attacked immediately
    Major Section:  MISCELLANEOUS
    

    General Form:
    ACL2 !>:enable-immediate-force-modep
    
    This event causes ACL2 to attack forced hypotheses immediately instead of delaying them to the next forcing round. See immediate-force-modep. Or for more basic information, first see force for a discussion of forced case splits.

    Enable-immediate-force-modep is a macro that enables the executable counterpart of the function symbol immediate-force-modep. When you want to enable this mode in hints, use a form such as one of the following (these are equivalent).

    :in-theory (enable (:executable-counterpart immediate-force-modep))
    :in-theory (enable (immediate-force-modep))
    




    acl2-sources/doc/HTML/ENABLE.html0000664002132200015000000000265012222333531015677 0ustar kaufmannacl2 ENABLE.html -- ACL2 Version 6.3

    ENABLE

    adds names to current theory
    Major Section:  THEORIES
    

    Example:
    (enable fact (fact) associativity-of-app)
    
    General Form:
    (enable name1 name2 ... namek)
    
    where each namei is a runic designator; see theories. The result is the theory that contains all the names in the current theory plus those listed. Note that this is merely a function that returns a theory. The result is generally a very long list of runes and you will probably regret printing it.

    The standard way to ``enable'' a fixed set of names, is as follows; see hints and see in-theory.

    :in-theory (enable name1 name2 ... namek)  ; in a hint
    (in-theory (enable name1 name2 ... namek)) ; as an event
    (local ; often desirable, to avoid exporting from the current context
     (in-theory (enable name1 name2 ... namek)))
    
    Note that all the names are implicitly quoted. If you wish to enable a computed list of names, lst, use the theory expression (union-theories (current-theory :here) lst).




    acl2-sources/doc/HTML/ENCAPSULATE.html0000664002132200015000000003667112222333517016533 0ustar kaufmannacl2 ENCAPSULATE.html -- ACL2 Version 6.3

    ENCAPSULATE

    hide some events and/or constrain some functions
    Major Section:  EVENTS
    

    Encapsulate provides a way to execute a sequence of events and then hide some of the resulting effects. There are two kinds of encapsulations: ``trivial'' and ``non-trivial''. We discuss these briefly before providing detailed documentation.

    A trivial encapsulation is an event of the following form.

    (encapsulate
     () ; nil here indicates "trivial"
     <event-1>
     ...
     <event-k>)
    
    We use the term ``sub-events'' to refer to <event-1> through <event-k>. Each sub-event <event-i> may be ``local'', that is, of the form (local <event-i'>); the other sub-events are called ``non-local''. When this encapsulate form is submitted to ACL2, it is processed in two passes. On the first pass, each sub-event is processed in sequence; admission of the encapsulate fails if any <event-i> fails to be admitted. Then a second pass is made after rolling back the logical world to what it was just before executing the encapsulate form. In the second pass, only the non-local forms <event-i> are evaluated, again in order, and proofs are skipped.

    For example, the following trivial encapsulation exports a single event, member-equal-reverse. The lemma member-revappend is used (as a rewrite rule) to prove member-equal-reverse on the first pass, but since member-revappend is local, it is ignored on the second (final) pass.

    (encapsulate
     ()
    
     (local
      (defthm member-revappend
        (iff (member-equal a (revappend x y))
             (or (member-equal a x)
                 (member-equal a y)))
        :hints (("Goal" :induct (revappend x y)))))
    
     (defthm member-equal-reverse
       (iff (member-equal a (reverse x))
            (member-equal a x))))
    
    Of course, one might prefer to prove these events at the top level, rather than within an encapsulation; but the point here is to illustrate that you can have local events that do not become part of the logical world. (Such a capability is also provided at the level of books; in particular, see include-book.)

    On the other hand, non-trivial encapsulations provide a way to introduce axioms about new function symbols, without introducing inconsistency and without introducing complete definitions. The following example illustrates how that works.

    (encapsulate
    
    ; The following list has a single signature, introducing a function foo of
    ; one argument that returns one value.  (The list is non-empty, so we call
    ; this a "non-trivial" encapsulation.)
     ( ((foo *) => *) )
    
    ; Introduce a ``witness'' (example) for foo, marked as local so that
    ; it is not exported:
     (local (defun foo (x) x))
    
    ; Introduce a non-local property to be exported:
     (defthm foo-preserves-consp
       (implies (consp x)
                (consp (foo x))))
    )
    
    The form above introduces a new function symbol, foo, with the indicated property and no definition. In fact, the output from ACL2 concludes as follows.
    
    The following constraint is associated with the function FOO:
    
    (IMPLIES (CONSP X) (CONSP (FOO X)))
    
    To understand this example, we consider how non-trivial encapsulations are processed. The same two passes are made as for trivial encapsulations, and the (local) definition of foo is ignored on the second pass, and hence does not appear in the resulting ACL2 logical world. But before the second pass, each signature is stored in the world. Thus, when the theorem foo-preserves-consp is encountered in the second pass, foo is a known function symbol with the indicated signature.

    We turn now to more complete documentation. But discussion of redundancy for encapsulate events may be found elsewhere; see redundant-encapsulate.

    Other Examples:
    (encapsulate (((an-element *) => *))
    
    ; The list of signatures above could also be written
    ;            ((an-element (lst) t))
    
      (local (defun an-element (lst)
               (if (consp lst) (car lst) nil)))
      (local (defthm member-equal-car
                (implies (and lst (true-listp lst))
                         (member-equal (car lst) lst))))
      (defthm thm1
         (implies (null lst) (null (an-element lst))))
      (defthm thm2
         (implies (and (true-listp lst)
                       (not (null lst)))
                  (member-equal (an-element lst) lst))))
    
    (encapsulate
     () ; empty signature: no constrained functions indicated
    
     (local (defthm hack
              (implies (and (syntaxp (quotep x))
                            (syntaxp (quotep y)))
                       (equal (+ x y z)
                              (+ (+ x y) z)))))
    
     (defthm nthcdr-add1-conditional
       (implies (not (zp (1+ n)))
                (equal (nthcdr (1+ n) x)
                       (nthcdr n (cdr x))))))
    
    
    

    Some Related Topics

    General Form: (encapsulate (signature ... signature) ev1 ... evn)
    where each signature is a well-formed signature, each signature describes a different function symbol, and each evi is an embedded event form (See embedded-event-form). Also see signature, in particular for a discussion of how a signature can assign a guard to a function symbol. There must be at least one evi. The evi inside local special forms are called ``local'' events below. Events that are not local are sometimes said to be ``exported'' by the encapsulation. We make the further restriction that no defaxiom event may be introduced in the scope of an encapsulate (not even by encapsulate or include-book events that are among the evi). Furthermore, no non-local include-book event is permitted in the scope of any encapsulate with a non-empty list of signatures.

    To be well-formed, an encapsulate event must have the properties that each event in the body (including the local ones) can be successfully executed in sequence and that in the resulting theory, each function mentioned among the signatures was introduced via a local event and has the signature listed. (A utility is provided to assist in debugging failures of such execution; see redo-flat.) In addition, the body may contain no ``local incompatibilities'' which, roughly stated, means that the events that are not local must not syntactically require symbols defined by local events, except for the functions listed in the signatures. See local-incompatibility. Finally, no non-local recursive definition in the body may involve in its suggested induction scheme any function symbol listed among the signatures. See subversive-recursions.

    Observe that if the signatures list is empty, the resulting ``trivial'' encapsulate may still be useful for deriving theorems to be exported whose proofs require lemmas you prefer to hide (i.e., made local). Whether trivial or not (i.e., whether the signature is empty or not), encapsulate exports the results of evaluating its non-local events, but its local events are ignored for the resulting logical world.

    The result of a non-trivial encapsulate event is an extension of the logic in which, roughly speaking, the functions listed in the signatures are constrained to have the signatures listed and to satisfy the non-local theorems proved about them. In fact, other functions introduced in the encapsulate event may be considered to have ``constraints'' as well. (See constraint for details, which are only relevant to functional instantiation.) Since the constraints were all theorems in the ``ephemeral'' or ``local'' theory, we are assured that the extension produced by encapsulate is sound. In essence, the local definitions of the constrained functions are just ``witness functions'' that establish the consistency of the constraints. Because those definitions are local, they are not present in the theory produced by encapsulation. After a non-trivial encapsulate event is admitted, theorems about the constrained function symbols may then be proved -- theorems whose proofs necessarily employ only the constraints. Thus, those theorems may be later functionally instantiated, as with the :functional-instance lemma instance (see lemma-instance), to derive analogous theorems about different functions, provided the constraints (see constraint) can be proved about the new functions.

    The default-defun-mode for the first event in an encapsulation is the default defun-mode ``outside'' the encapsulation. But since events changing the defun-mode are permitted within the body of an encapsulate, the default defun-mode may be changed. However, defun-mode changes occurring within the body of the encapsulate are not exported. In particular, the acl2-defaults-table after an encapsulate is always the same as it was before the encapsulate, even though the encapsulate body might contain defun-mode changing events, :program and :logic. See defun-mode. More generally, after execution of an encapsulate event, the value of acl2-defaults-table is restored to what it was immediately before that event was executed. See acl2-defaults-table.

    We make some remarks on guards and evaluation. Calls of functions introduced in the signatures list cannot be evaluated in the ACL2 read-eval-print loop. See defattach for a way to overcome this limitation. Moreover, any :guard supplied in the signature is automatically associated in the world with its corresponding function symbol, with no requirement other than that the guard is a legal term all of whose function symbols are in :logic mode with their guards verified. In particular, there need not be any relationship between a guard in a signature and the guard in a local witness function. Finally, note that for functions introduced non-locally inside a non-trivial encapsulate event, guard verification is illegal unless ACL2 determines that the proof obligations hold outside the encapsulate event as well.

    (encapsulate
     ((f (x) t))
     (local (defun f (x) (declare (xargs :guard t)) (consp x)))
     ;; ERROR!
     (defun g (x)
       (declare (xargs :guard (f x)))
       (car x)))
    

    The order of the events in the vicinity of an encapsulate is confusing. We discuss it in some detail here because when logical names are being used with theory functions to compute sets of rules, it is sometimes important to know the order in which events were executed. (See logical-name and see theory-functions.) What, for example, is the set of function names extant in the middle of an encapsulation?

    If the most recent event is previous and then you execute an encapsulate constraining an-element with two non-local events in its body, thm1 and thm2, then the order of the events after the encapsulation is (reading chronologically forward): previous, thm1, thm2, an-element (the encapsulate itself). Actually, between previous and thm1 certain extensions were made to the world by the superior encapsulate, to permit an-element to be used as a function symbol in thm1.

    Remark for ACL2(r) (see real). For ACL2(r), encapsulate can be used to introduce classical and non-classical functions, as determined by the signatures; see signature. Those marked as classical (respectively non-classical) must have classical (respectively, non-classical) local witness functions. A related requirement applies to functional instantiation; see lemma-instance.




    acl2-sources/doc/HTML/ENDP.html0000664002132200015000000000220612222333523015475 0ustar kaufmannacl2 ENDP.html -- ACL2 Version 6.3

    ENDP

    recognizer for empty lists
    Major Section:  ACL2-BUILT-INS
    

    In the ACL2 logic, (endp x) is the same as (atom x). See atom.

    Unlike atom, the guard for endp requires that x is a cons pair or is nil. Thus, endp is typically used as a termination test for functions that recur on a true-listp argument. See guard for general information about guards.

    Endp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ENTER-BOOT-STRAP-MODE.html0000664002132200015000000000375212222333520020002 0ustar kaufmannacl2 ENTER-BOOT-STRAP-MODE.html -- ACL2 Version 6.3

    ENTER-BOOT-STRAP-MODE

    The first millisecond of the Big Bang
    Major Section:  MISCELLANEOUS
    

    ACL2 functions, e.g., if, that show enter-boot-strap-mode as their defining command are in fact primitives. It is impossible for the system to display defining axioms about these symbols.

    Enter-boot-strap-mode is a Common Lisp function but not an ACL2 function. It magically creates from nil an ACL2 property list world that lets us start the boot-strapping process. That is, once enter-boot-strap-mode has created its world, it is possible to process the defconsts, defuns, and defaxioms, necessary to bring up the rest of the system. Before that world is created, the attempt by ACL2 even to translate a defun form, say, would produce an error because defun is undefined.

    Several ACL2 functions show enter-boot-strap-mode as their defining command. Among them are if, cons, car, and cdr. These functions are characterized by axioms rather than definitional equations -- axioms that in most cases are built into our code and hence do not have any explicit representation among the rules and formulas in the system.




    acl2-sources/doc/HTML/EQ.html0000664002132200015000000000262212222333523015256 0ustar kaufmannacl2 EQ.html -- ACL2 Version 6.3

    EQ

    equality of symbols
    Major Section:  ACL2-BUILT-INS
    

    Eq is the function for determining whether two objects are identical (i.e., have the exact same store address in the current von Neumann implementation of Common Lisp). It is the same as equal in the ACL2 logic.

    Eq is a Common Lisp function. In order to ensure conformance with Common Lisp, the ACL2 guard on eq requires at least one of the arguments to eq to be a symbol. Common Lisp guarantees that if x is a symbol, then x is eq to y if and only if x is equal to y. Thus, the ACL2 user should think of eq as nothing besides a fast means for checking equal when one argument is known to be a symbol. In particular, it is possible that an eq test will not even require the cost of a function call but will be as fast as a single machine instruction.




    acl2-sources/doc/HTML/EQL.html0000664002132200015000000000202112222333523015363 0ustar kaufmannacl2 EQL.html -- ACL2 Version 6.3

    EQL

    test equality (of two numbers, symbols, or characters)
    Major Section:  ACL2-BUILT-INS
    

    (eql x y) is logically equivalent to (equal x y).

    Unlike equal, eql has a guard requiring at least one of its arguments to be a number, a symbol, or a character. Generally, eql is executed more efficiently than equal.

    For a discussion of the various ways to test against 0, See zero-test-idioms.

    Eql is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/EQLABLE-ALISTP.html0000664002132200015000000000157612222333523017017 0ustar kaufmannacl2 EQLABLE-ALISTP.html -- ACL2 Version 6.3

    EQLABLE-ALISTP

    recognizer for a true list of pairs whose cars are suitable for eql
    Major Section:  ACL2-BUILT-INS
    

    The predicate eqlable-alistp tests whether its argument is a true-listp of consp objects whose cars all satisfy eqlablep.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/EQLABLE-LISTP.html0000664002132200015000000000136312222333523016710 0ustar kaufmannacl2 EQLABLE-LISTP.html -- ACL2 Version 6.3

    EQLABLE-LISTP

    recognizer for a true list of objects each suitable for eql
    Major Section:  ACL2-BUILT-INS
    

    The predicate eqlable-listp tests whether its argument is a true-listp of objects satisfying eqlablep.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/EQLABLEP.html0000664002132200015000000000150712222333523016137 0ustar kaufmannacl2 EQLABLEP.html -- ACL2 Version 6.3

    EQLABLEP

    the guard for the function eql
    Major Section:  ACL2-BUILT-INS
    

    The predicate eqlablep tests whether its argument is suitable for eql, at least one of whose arguments must satisfy this predicate in Common Lisp. (Eqlablep x) is true if and only if its argument is a number, a symbol, or a character.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/EQUAL.html0000664002132200015000000000121212222333523015612 0ustar kaufmannacl2 EQUAL.html -- ACL2 Version 6.3

    EQUAL

    true equality
    Major Section:  ACL2-BUILT-INS
    

    (equal x y) is equal to t or nil, according to whether or not x and y are the same value.

    For a discussion of the various idioms for testing against 0, See zero-test-idioms.




    acl2-sources/doc/HTML/EQUALITY-VARIANTS-DETAILS.html0000664002132200015000000002011012222333525020510 0ustar kaufmannacl2 EQUALITY-VARIANTS-DETAILS.html -- ACL2 Version 6.3

    EQUALITY-VARIANTS-DETAILS

    details about equality-variants
    Major Section:  EQUALITY-VARIANTS
    

    Here we present details about equality variants, none of which is likely to be important to the majority of ACL2 users. Please see equality-variants for relevant background.

    We begin by presenting events that implement the equality variants for member, as these illustrate the events introduced for all macros having equality variants. The definition of member, just below, calls the macro let-mbe, which in turn is just an abbreviation for a combination of let and mbe.

    (defmacro let-mbe (bindings &key logic exec)
      `(let ,bindings
         (mbe :logic ,logic
              :exec ,exec)))
    
    This use of let arranges that each argument of a call of member is evaluated only once.
    (defmacro member (x l &key (test ''eql))
      (declare (xargs :guard (or (equal test ''eq)
                                 (equal test ''eql)
                                 (equal test ''equal))))
      (cond
       ((equal test ''eq)
        `(let-mbe ((x ,x) (l ,l))
                  :logic (member-equal x l)
                  :exec  (member-eq-exec x l)))
       ((equal test ''eql)
        `(let-mbe ((x ,x) (l ,l))
                  :logic (member-equal x l)
                  :exec  (member-eql-exec x l)))
       (t ; (equal test 'equal)
        `(member-equal ,x ,l))))
    
    Inspection of the definition above shows that every call of member expands to one that is logically equivalent to the corresponding call of member-equal, which is defined as follows.
    (defun member-equal (x lst)
      (declare (xargs :guard (true-listp lst)))
      (cond ((endp lst) nil)
            ((equal x (car lst)) lst)
            (t (member-equal x (cdr lst)))))
    
    The following two definitions model equality variants of member for tests eq and eql, respectively.
    (defun member-eq-exec (x lst)
      (declare (xargs :guard (if (symbolp x)
                                 (true-listp lst)
                               (symbol-listp lst))))
      (cond ((endp lst) nil)
            ((eq x (car lst)) lst)
            (t (member-eq-exec x (cdr lst)))))
    
    (defun member-eql-exec (x lst)
      (declare (xargs :guard (if (eqlablep x)
                                 (true-listp lst)
                               (eqlable-listp lst))))
      (cond ((endp lst) nil)
            ((eql x (car lst)) lst)
            (t (member-eql-exec x (cdr lst)))))
    
    At this point the user can write (member x y) or (member-equal x y) to call equality variants of member with test eql or equal, respectively. We thus provide the following macro for the eq variant.
    (defmacro member-eq (x lst)
      `(member ,x ,lst :test 'eq))
    
    Guard proof obligations generated by calls of member will include those based on its use of mbe, and are supported by the following two lemmas.
    (defthm member-eq-exec-is-member-equal
      (equal (member-eq-exec x l)
             (member-equal x l)))
    
    (defthm member-eql-exec-is-member-equal
      (equal (member-eql-exec x l)
             (member-equal x l)))
    
    Finally, the following two events arrange that in certain contexts such as theories (including the use of in-theory in events and hints), member-eq and member are treated as references to member-equal.
    (add-macro-alias member-eq member-equal)
    (add-macro-alias member member-equal)
    

    We conclude this topic by exploring the following recommendation made in the documentation for equality-variants.

    For efficiency we recommend using the -equal equality variant, for example member-equal or ... :TEST 'equal, in certain contexts: defmacro, defpkg, defconst, and value-triple forms.

    ACL2 reliies on the underlying Common Lisp for evaluation. It also processes events in the ACL2 logic. In order to guarantee consistency of its logical and Common Lisp evaluations, ACL2 uses a ``safe mode'' to avoid ill-guarded calls. In particular, consider the use of mbe in execution of a call of an equality variant of a primitive, F, other than its F-equal variant. The mbe call discussed above requires a connection to be established between the :logic and :exec forms. For example, if F is called with :TEST 'eql (either explicitly or as the default), then ACL2 will call both F-eql-exec and F-equal, and check that the two results are equal.

    The following partial log illustrates the point above. We define a macro that calls member, and when a call of this macro is expanded during processing of a subsequent definition, we see that two membership functions are called on the same arguments.

    ACL2 !>(defmacro mac (lst)
             (list 'quote (and (true-listp lst)
                               (member 'c lst :test 'eq))))
    
    Summary
    Form:  ( DEFMACRO MAC ...)
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     MAC
    ACL2 !>(trace$ member-equal member-eq-exec)
     ((MEMBER-EQUAL) (MEMBER-EQ-EXEC))
    ACL2 !>(defun f () (mac (a b c d)))
    1> (ACL2_*1*_ACL2::MEMBER-EQ-EXEC C (A B C D))
      2> (MEMBER-EQ-EXEC C (A B C D))
      <2 (MEMBER-EQ-EXEC (C D))
    <1 (ACL2_*1*_ACL2::MEMBER-EQ-EXEC (C D))
    1> (ACL2_*1*_ACL2::MEMBER-EQUAL C (A B C D))
      2> (MEMBER-EQUAL C (A B C D))
      <2 (MEMBER-EQUAL (C D))
    <1 (ACL2_*1*_ACL2::MEMBER-EQUAL (C D))
    
    Since F is non-recursive, its admission is trivial.
    

    If performance is an issue then we can avoid such a problem, for example as follows. In a fresh session, let us define a suitable wrapper for calling member with :TEST 'eq. This time, the trace in our partial log shows that we have avoided calling two membership functions.

    ACL2 !>(defun mem-eq (x lst)
             (declare (xargs :guard (if (symbolp x)
                                        (true-listp lst)
                                      (symbol-listp lst))))
             (member x lst :test 'eq))
    [[ ... output omitted here ... ]]
     MEM-EQ
    ACL2 !>(defmacro mac (lst)
             (list 'quote (and (true-listp lst)
                               (mem-eq 'c lst))))
    
    Summary
    Form:  ( DEFMACRO MAC ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     MAC
    ACL2 !>(trace$ member-equal member-eq-exec mem-eq)
     ((MEMBER-EQUAL)
      (MEMBER-EQ-EXEC)
      (MEM-EQ))
    ACL2 !>(defun f () (mac (a b c d)))
    1> (ACL2_*1*_ACL2::MEM-EQ C (A B C D))
      2> (MEM-EQ C (A B C D))
      <2 (MEM-EQ (C D))
    <1 (ACL2_*1*_ACL2::MEM-EQ (C D))
    
    Since F is non-recursive, its admission is trivial.
    




    acl2-sources/doc/HTML/EQUALITY-VARIANTS.html0000664002132200015000000001702512222333525017440 0ustar kaufmannacl2 EQUALITY-VARIANTS.html -- ACL2 Version 6.3

    EQUALITY-VARIANTS

    versions of a function using different equality tests
    Major Section:  PROGRAMMING
    

    The ACL2 environment includes not only a logic but also a programming language, which is based on Common Lisp. Execution efficiency may be increased by using fast equality tests: eq for symbols and eql for numbers, symbols, and characters (see eqlablep). Several list-processing functions built into ACL2 thus have three variants, depending on whether the equality function used is eq, eql, or equal; a list is provided below. ACL2 has taken measures to ensure that one can reason about a single logical function even when one uses these different variants.

    Consider for example the case of list membership. Common Lisp provides a utility for this purposes, member, which can take a :TEST keyword argument, default eql. So for example, one might write

    (member a x :TEST 'eq)
    
    if either a is a symbol or x is a list of symbols, so that the fastest equality test (eq) may be used when comparing a to successive elements of the list, x. One might elsewhere write (member b (foo y)), which is equivalent to (member b (foo y) :TEST 'eql), for example if b is a number. If one wants to reason about both (member a x :TEST 'eq) and (member b y), it might be helpful for both calls of member to be the same logically, even though Common Lisp will execute them differently (using eq or eql, respectively). ACL2 arranges that in fact, both references to member generate calls of member-equal in the theorem prover.

    In fact, since member can take the optional :TEST keyword argument, then in ACl2 it must be defined as a macro, not a function (see defun). ACL2 arranges that a call of member generates a corresponding call of the function member-equal, regardless of the value of TEST, in a manner that produces member-equal in prover output. More generally, you can expect ACL2 to treat your use of member as though you had written member-equal, for example in the way it stores rewrite rules and other kinds of rules as well (see rule-classes). We say little here about how this is all arranged by ACL2, other than to mention that mbe is utilized (so, you might see mention in proof logs) of the function return-last that implements mbe. Such details, which probably can be ignored by most users, may be found elsewhere; see equality-variants-details.

    As a convenience to the user, the macro member-eq is provided that expands to a corresponding call of member with :TEST 'eq, as follows.

    ACL2 !>:trans1 (member-eq (foo x) (bar y))
     (MEMBER (FOO X) (BAR Y) :TEST 'EQ)
    ACL2 !>
    

    For efficiency we recommend using the -equal equality variant, for example member-equal or ... :TEST 'equal, in certain contexts: defmacro, defpkg, defconst, and value-triple forms. However, the implementation of equality variants has been designed so that when defining a function, one may choose freely in a definition an equality variant of primitive F, to get efficient execution but where subsequent reasoning is about F-equal. For details about the above recommendation and for a discussion of the implementation, see equality-variants-details.

    The following alphabetical list includes all primitives that have equality variants. Each macro F listed below takes an optional :TEST keyword argument of 'eq, 'eql, or 'equal, where 'eql is the default. For each such F, a function F-equal is defined such that for logical purposes (in particular theorem proving), each call of F expands to a corresponding call of F-equal. For convenience, a macro F-eq is also defined, so that a call of F-eq expands to a corresponding call of F with :TEST 'eq.

    add-to-set
    assoc
    delete-assoc
    intersection$ ; (see Note below)
    intersectp
    member
    no-duplicatesp
    position-ac
    position
    put-assoc
    rassoc
    remove-duplicates
    remove1
    remove
    set-difference$ ; (see Note below)
    subsetp
    union$ ; (see Note below)
    

    Note: Three of the macros above have names ending with the character, `$': intersection$, set-difference$, and union$. In each case there is a corresponding Common Lisp primitive without the trailing `$': intersection, set-difference, and union. However, Common Lisp does not specify the order of elements in the list returned by those primitives, so ACL2 has its own. Nevertheless, the only use of the trailing `$' is to distinguish the primitives; associated functions and macros, for example union-eq and intersection-equal, do not include the `$' character in their names.

    Some Related Topics




    acl2-sources/doc/HTML/EQUIVALENCE.html0000664002132200015000000002412212222333530016507 0ustar kaufmannacl2 EQUIVALENCE.html -- ACL2 Version 6.3

    EQUIVALENCE

    mark a relation as an equivalence relation
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example:
    (defthm r-equal-is-an-equivalence ; assumes that r-equal has been defined
      (and (booleanp (r-equal x y))
           (r-equal x x)
           (implies (r-equal x y) (r-equal y x))
           (implies (and (r-equal x y)
                         (r-equal y z))
                    (r-equal x z)))
      :rule-classes :equivalence)
    

    Also see defequiv.

    General Form:
    (and (booleanp (equiv x y))
         (equiv x x)
         (implies (equiv x y) (equiv y x))
         (implies (and (equiv x y)
                       (equiv y z))
                  (equiv x z)))
    
    except that the order of the conjuncts and terms and the choice of variable symbols is unimportant. The effect of such a rule is to identify equiv as an equivalence relation. Note that only Boolean 2-place function symbols can be treated as equivalence relations. See congruence and see refinement for closely related concepts.

    The macro form (defequiv equiv) is an abbreviation for a defthm of rule-class :equivalence that establishes that equiv is an equivalence relation. It generates the formula shown above. See defequiv.

    When equiv is marked as an equivalence relation, its reflexivity, symmetry, and transitivity are built into the system in a deeper way than via :rewrite rules. More importantly, after equiv has been shown to be an equivalence relation, lemmas about equiv, e.g.,

    (implies hyps (equiv lhs rhs)),
    
    when stored as :rewrite rules, cause the system to rewrite certain occurrences of (instances of) lhs to (instances of) rhs. Roughly speaking, an occurrence of lhs in the kth argument of some fn-expression, (fn ... lhs' ...), can be rewritten to produce (fn ... rhs' ...), provided the system ``knows'' that the value of fn is unaffected by equiv-substitution in the kth argument. Such knowledge is communicated to the system via ``congruence lemmas.''

    For example, suppose that r-equal is known to be an equivalence relation. The :congruence lemma

    (implies (r-equal s1 s2)
             (equal (fn s1 n) (fn s2 n)))
    
    informs the rewriter that, while rewriting the first argument of fn-expressions, it is permitted to use r-equal rewrite-rules. See congruence for details about :congruence lemmas. Interestingly, congruence lemmas are automatically created when an equivalence relation is stored, saying that either of the equivalence relation's arguments may be replaced by an equivalent argument. That is, if the equivalence relation is fn, we store congruence rules that state the following fact:
    (implies (and (fn x1 y1)
                  (fn x2 y2))
             (iff (fn x1 x2) (fn y1 y2)))
    
    Another aspect of equivalence relations is that of ``refinement.'' We say equiv1 ``refines'' equiv2 iff (equiv1 x y) implies (equiv2 x y). :refinement rules permit you to establish such connections between your equivalence relations. The value of refinements is that if the system is trying to rewrite something while maintaining equiv2 it is permitted to use as a :rewrite rule any refinement of equiv2. Thus, if equiv1 is a refinement of equiv2 and there are equiv1 rewrite-rules available, they can be brought to bear while maintaining equiv2. See refinement.

    The system initially has knowledge of two equivalence relations, equality, denoted by the symbol equal, and propositional equivalence, denoted by iff. Equal is known to be a refinement of all equivalence relations and to preserve equality across all arguments of all functions.

    Typically there are five steps involved in introducing and using a new equivalence relation, equiv.

    (1) Define equiv,

    (2) prove the :equivalence lemma about equiv,

    (3) prove the :congruence lemmas that show where equiv can be used to maintain known relations,

    (4) prove the :refinement lemmas that relate equiv to known relations other than equal, and

    (5) develop the theory of conditional :rewrite rules that drive equiv rewriting.

    More will be written about this as we develop the techniques. For now, here is an example that shows how to make use of equivalence relations in rewriting.

    Among the theorems proved below is

    (defthm insert-sort-is-id
      (perm (insert-sort x) x))
    
    Here perm is defined as usual with delete and is proved to be an equivalence relation and to be a congruence relation for cons and member.

    Then we prove the lemma

    (defthm insert-is-cons
      (perm (insert a x) (cons a x)))
    
    which you must think of as you would (insert a x) = (cons a x).

    Now prove (perm (insert-sort x) x). The base case is trivial. The induction step is

       (consp x)
     & (perm (insert-sort (cdr x)) (cdr x))
    
    -> (perm (insert-sort x) x).
    
    Opening insert-sort makes the conclusion be
       (perm (insert (car x) (insert-sort (cdr x))) x).
    
    Then apply the induction hypothesis (rewriting (insert-sort (cdr x)) to (cdr x)), to make the conclusion be
    (perm (insert (car x) (cdr x)) x)
    
    Then apply insert-is-cons to get (perm (cons (car x) (cdr x)) x). But we know that (cons (car x) (cdr x)) is x, so we get (perm x x) which is trivial, since perm is an equivalence relation.

    Here are the events.

    (encapsulate (((lt * *) => *))
      (local (defun lt (x y) (declare (ignore x y)) nil))
      (defthm lt-non-symmetric (implies (lt x y) (not (lt y x)))))
    
    (defun insert (x lst)
      (cond ((atom lst) (list x))
            ((lt x (car lst)) (cons x lst))
            (t (cons (car lst) (insert x (cdr lst))))))
    
    (defun insert-sort (lst)
      (cond ((atom lst) nil)
            (t (insert (car lst) (insert-sort (cdr lst))))))
    
    (defun del (x lst)
      (cond ((atom lst) nil)
            ((equal x (car lst)) (cdr lst))
            (t (cons (car lst) (del x (cdr lst))))))
    
    (defun mem (x lst)
      (cond ((atom lst) nil)
            ((equal x (car lst)) t)
            (t (mem x (cdr lst)))))
    
    (defun perm (lst1 lst2)
      (cond ((atom lst1) (atom lst2))
            ((mem (car lst1) lst2)
             (perm (cdr lst1) (del (car lst1) lst2)))
            (t nil)))
    
    (defthm perm-reflexive
      (perm x x))
    
    (defthm perm-cons
      (implies (mem a x)
               (equal (perm x (cons a y))
                      (perm (del a x) y)))
      :hints (("Goal" :induct (perm x y))))
    
    (defthm perm-symmetric
      (implies (perm x y) (perm y x)))
    
    (defthm mem-del
      (implies (mem a (del b x)) (mem a x)))
    
    (defthm perm-mem
      (implies (and (perm x y)
                    (mem a x))
               (mem a y)))
    
    (defthm mem-del2
      (implies (and (mem a x)
                    (not (equal a b)))
               (mem a (del b x))))
    
    (defthm comm-del
      (equal (del a (del b x)) (del b (del a x))))
    
    (defthm perm-del
      (implies (perm x y)
               (perm (del a x) (del a y))))
    
    (defthm perm-transitive
      (implies (and (perm x y) (perm y z)) (perm x z)))
    
    (defequiv perm)
    
    (in-theory (disable perm
                        perm-reflexive
                        perm-symmetric
                        perm-transitive))
    
    (defcong perm perm (cons x y) 2)
    
    (defcong perm iff (mem x y) 2)
    
    (defthm atom-perm
      (implies (not (consp x)) (perm x nil))
      :rule-classes :forward-chaining
      :hints (("Goal" :in-theory (enable perm))))
    
    (defthm insert-is-cons
      (perm (insert a x) (cons a x)))
    
    (defthm insert-sort-is-id
      (perm (insert-sort x) x))
    
    (defun app (x y) (if (consp x) (cons (car x) (app (cdr x) y)) y))
    
    (defun rev (x)
      (if (consp x) (app (rev (cdr x)) (list (car x))) nil))
    
    (defcong perm perm (app x y) 2)
    
    (defthm app-cons
      (perm (app a (cons b c)) (cons b (app a c))))
    
    (defthm app-commutes
      (perm (app a b) (app b a)))
    
    (defcong perm perm (app x y) 1
      :hints (("Goal" :induct (app y x))))
    
    (defthm rev-is-id (perm (rev x) x))
    
    (defun == (x y)
      (if (consp x)
          (if (consp y)
              (and (equal (car x) (car y))
                   (== (cdr x) (cdr y)))
              nil)
          (not (consp y))))
    
    (defthm ==-reflexive (== x x))
    
    (defthm ==-symmetric (implies (== x y) (== y x)))
    
    (defequiv ==)
    
    (in-theory (disable ==-symmetric ==-reflexive))
    
    (defcong == == (cons x y) 2)
    
    (defcong == iff (consp x) 1)
    
    (defcong == == (app x y) 2)
    
    (defcong == == (app x y) 1)
    
    (defthm rev-rev (== (rev (rev x)) x))
    




    acl2-sources/doc/HTML/EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES.html0000664002132200015000000000670012222333515023171 0ustar kaufmannacl2 EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES.html -- ACL2 Version 6.3

    EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES

    logically equivalent formulas can generate radically different rules
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Consider the rewrite rules that would be generated from the three commands below. In all three cases, the fact being stated relates the nth element of the reverse of x to the nth element of x. In fact, the three formulas are simple rearrangements of each other and are all equivalent. The theorem prover treats all three formulas equivalently when proving them. But the rules generated from them are very different.

    (defthm nth-rev-1
      (implies (and (natp n)
                    (< n (len x)))
               (equal (nth n (rev x))
                      (nth (- (len x) (+ 1 n)) x))))
    
    (defthm nth-rev-2
      (implies (and (natp n)
                    (< n (len x)))
               (equal (nth (- (len x) (+ 1 n)) x)
                      (nth n (rev x)))))
    
    (defthm nth-rev-3
      (implies (and (natp n)
                    (not (equal (nth n (rev x))
                                (nth (- (len x) (+ 1 n)) x))))
               (not (< n (len x)))))
    

    Here are the three rewrite rules:

    nth-rev-1:
    Replace instances of (NTH n (REV x))
    by (NTH (- (LEN x) (+ 1 n)) x),
    if you can establish that n is a natural number less than the length of x.

    nth-rev-2:
    Replace instances of (NTH (- (LEN x) (+ 1 n)) x)
    by (NTH n (REV x)),
    if you can establish that n is a natural number less than the length of x.

    nth-rev-3:
    Replace instances of (< n (LEN x))
    by NIL
    if you can establish that n is a natural number and that (NTH n (REV x)) is different from (NTH (- (LEN x) (+ 1 n)) x).

    As the driver of ACL2, you have to decide which rule you want when you give the command to prove it.

    If you tell the theorem prover to use both nth-rev-1 and nth-rev-2, ACL2 will enter an infinite loop when it sees any term matching either NTH expression.

    Most users would choose form nth-rev-1 of the rule. It eliminates rev from the problem -- at the expense of introducing some arithmetic. But arithmetic is so fundamental it is rarely possible to avoid it and it is likely to be in the problem already since you're indexing into (rev x). The nth-rev-2 form of the rule is ``bad'' because it introduces rev into a problem where it might not have appeared. The nth-rev-3 version is ``bad'' because it makes the theorem prover shift its attention from a simple arithmetic inequality to a complicated property of nth and rev, which might not be in the problem.

    Use your browser's Back Button now to return to introduction-to-rewrite-rules-part-1.




    acl2-sources/doc/HTML/ER-PROGN.html0000664002132200015000000000436412222333523016147 0ustar kaufmannacl2 ER-PROGN.html -- ACL2 Version 6.3

    ER-PROGN

    perform a sequence of state-changing ``error triples''
    Major Section:  ACL2-BUILT-INS
    

    Example:
    (er-progn (check-good-foo-p (f-get-global 'my-foo state) state)
              (value (* (f-get-global 'my-foo state)
                        (f-get-global 'bar state))))
    

    This sequencing primitive is only useful when programming with state, something that very few users will probably want to do. See state.

    Er-progn is used much the way that progn is used in Common Lisp, except that it expects each form within it to evaluate to an ``error triple'' of the form (mv erp val state); see error-triples. The first such form, if any, that evaluates to such a triple where erp is not nil yields the error triple returned by the er-progn. If there is no such form, then the last form returns the value of the er-progn form.

    General Form:
    (er-progn <expr1> ... <exprk>)
    
    where each <expri> is an expression that evaluates to an error triple (see programming-with-state). The above form is essentially equivalent to the following (``essentially'' because in fact, care is taken to avoid variable capture).
    (mv-let (erp val state)
            <expr1>
            (cond (erp (mv erp val state))
                  (t (mv-let (erp val state)
                             <expr2>
                             (cond (erp (mv erp val state))
                                   (t ...
                                          (mv-let (erp val state)
                                                  <expr{k-1}>
                                                  (cond (erp (mv erp val state))
                                                        (t <exprk>)))))))))
    




    acl2-sources/doc/HTML/ER.html0000664002132200015000000000626512222333523015266 0ustar kaufmannacl2 ER.html -- ACL2 Version 6.3

    ER

    print an error message and ``cause an error''
    Major Section:  ACL2-BUILT-INS
    

    See fmt for a general discussion of formatted printing in ACL2. All calls of er print formatted strings, just as is done by fmt.

    Example Forms:
    (er hard  'top-level "Illegal inputs, ~x0 and ~x1." a b)
    (er hard? 'top-level "Illegal inputs, ~x0 and ~x1." a b)
    (er soft  'top-level "Illegal inputs, ~x0 and ~x1." a b)
    
    The examples above all print an error message to standard output saying that a and b are illegal inputs. However, the first two abort evaluation after printing an error message (while logically returning nil, though in ordinary evaluation the return value is never seen); while the third returns (mv t nil state) after printing an error message. The result in the third case can be interpreted as an ``error'' when programming with the ACL2 state, something most ACL2 users will probably not want to do unless they are building systems of some sort; see programming-with-state. If state is not available in the current context then you will probably want to use (er hard ...) or (er hard? ...) to cause an error; for example, if you are returning two values, you may write (mv (er hard ...) nil).

    The difference between the hard and hard? forms is one of guards. Use hard if you want the call to generate a (clearly impossible) guard proof obligation of (essentially) NIL. But use hard? if you want to be able to call this function in guard-verified code, since the call generates a (trivially satisfied) guard proof obligation of T.

    Er is a macro, and the above three examples expand to calls of ACL2 functions, as shown below. See illegal, see hard-error, and see error1. The first two have guards of (essentially) NIL and T, respectively, while error1 is in :program mode.

    General forms:
    (er hard  ctx fmt-string arg1 arg2 ... argk)
      ==> {macroexpands, in essence, to:}
    (ILLEGAL    CTX FMT-STRING
                (LIST (CONS #\0 ARG1) (CONS #\1 ARG2) ... (CONS #\k ARGk)))
    
    (er hard? ctx fmt-string arg1 arg2 ... argk)
      ==> {macroexpands, in essence, to:}
    (HARD-ERROR CTX FMT-STRING
                (LIST (CONS #\0 ARG1) (CONS #\1 ARG2) ... (CONS #\k ARGk)))
    
    (er soft  ctx fmt-string arg1 arg2 ... argk)
      ==> {macroexpands, in essence, to:}
    (ERROR1     CTX FMT-STRING
                (LIST (CONS #\0 ARG1) (CONS #\1 ARG2) ... (CONS #\k ARGk)))
    




    acl2-sources/doc/HTML/ERROR-TRIPLES-AND-PARALLELISM.html0000664002132200015000000000655512222333522021135 0ustar kaufmannacl2 ERROR-TRIPLES-AND-PARALLELISM.html -- ACL2 Version 6.3

    ERROR-TRIPLES-AND-PARALLELISM

    how to avoid error triples in ACL2(p)
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    ACL2 supports the use of error triples in many features; e.g., computed-hints. (For background on error triples, see programming-with-state.) However, ACL2(p) does not support the use of error triples in some of these features (e.g., computed-hints) while waterfall-parallelism is enabled.

    You may see an error message like the following when running ACL2(p) with waterfall-parallelism enabled:

    ACL2 Error in ( THM ...):  Since we are translating a form in ACL2(p)
    intended to be executed with waterfall parallelism enabled, the form
    (MY-STATE-MODIFYING-COMPUTED-HINT ID STATE) was expected to represent
    an ordinary value, not an error triple (mv erp val state), as would
    be acceptable in a serial execution of ACL2.  Therefore, the form returning
    a tuple of the form (* * STATE) is an error.  See :DOC unsupported-
    waterfall-parallelism-features and :DOC error-triples-and-parallelism
    for further explanation.
    

    In this particular example, the cause of the error was trying to use a computed hint that returned state, which is not allowed when executing the waterfall in parallel (see unsupported-waterfall-parallelism-features for other related information).

    Often, the only reason users need to return state is so they can perform some output during the proof process. In this case, we suggest using one of the state-free output functions, like cw or observation-cw. If the user is concerned about the interleaving of their output with other output, these calls can be surrounded with the macro with-output-lock.

    Another frequent reason users return state is so they can cause a soft error and halt the proof process. In this case, we suggest instead calling er with the hard or hard? severity. By using these mechanisms, the user avoids modifying state, a requirement for much of the code written in ACL2(p).

    You may encounter other similar error messages when using computed-hints, custom-keyword-hints, or override-hints. Chances are that you are somehow returning an error triple when an ordinary value is needed. If this turns out not to be the case, please let the ACL2 implementors know.




    acl2-sources/doc/HTML/ERROR-TRIPLES.html0000664002132200015000000000325212222333525016764 0ustar kaufmannacl2 ERROR-TRIPLES.html -- ACL2 Version 6.3

    ERROR-TRIPLES

    a common ACL2 programming idiom
    Major Section:  STATE
    

    When evaluation returns three values, where the first two are ordinary (non-stobj) objects and the third is the ACL2 state, the result may be called an ``error triple''. If an error triple is (mv erp val state), we think of erp as an error flag and val as the returned value. By default, if the result of evaluating a top-level form is an error triple (mv erp val state), then that result is not printed if erp is non-nil or if val is the keyword :INVISIBLE, and otherwise val is printed with a preceding space. For example:

    ACL2 !>(+ 3 4) ; ordinary value
    7
    ACL2 !>(mv nil (+ 3 4) state) ; error triple, error component of nil
     7
    ACL2 !>(mv t (+ 3 4) state) ; error triple, non-nil error component
    ACL2 !>(mv nil :invisible state) ; special case for :INVISIBLE
    ACL2 !>
    

    See programming-with-state for a discussion of error triples and how to program with them. Also see ld-error-triples and see ld for a discussion of the value :COMMAND-CONVENTIONS for keyword :LD-POST-EVAL-PRINT.




    acl2-sources/doc/HTML/ERROR1.html0000664002132200015000000000355312222333523015727 0ustar kaufmannacl2 ERROR1.html -- ACL2 Version 6.3

    ERROR1

    print an error message and cause a ``soft error''
    Major Section:  ACL2-BUILT-INS
    

    (Error1 ctx str alist) returns (mv t nil state). An error message is first printed using the the ``context'' ctx, as well as the string str and alist alist that are of the same kind as expected by fmt. See fmt.

    Error1 can be interpreted as causing an ``error'' when programming with the ACL2 state, something most ACL2 users will probably not want to do; see ld-error-triples and see er-progn. In order to cause errors with :logic mode functions, see hard-error and see illegal. Better yet, see er for a macro that provides a unified way of signaling errors.

    As mentioned above, error1 always returns (mv t nil state). But if a call (error1 ctx str alist) is encountered during evaluation, then the string str is first printed using the association list alist (as in fmt). Here is a trivial, contrived example.

    ACL2 !>(error1 'my-context
                   "Printing 4: ~n0"
                   (list (cons #\0 4))
                   state)
    
    
    ACL2 Error in MY-CONTEXT:  Printing 4: four
    
    ACL2 !>
    




    acl2-sources/doc/HTML/ESCAPE-TO-COMMON-LISP.html0000664002132200015000000000150312222333520017756 0ustar kaufmannacl2 ESCAPE-TO-COMMON-LISP.html -- ACL2 Version 6.3

    ESCAPE-TO-COMMON-LISP

    escaping to Common Lisp
    Major Section:  MISCELLANEOUS
    

    Example:
    ACL2 !>:Q
    

    There is no Common Lisp escape feature in the lp. This is part of the price of purity. To execute a form in Common Lisp as opposed to ACL2, exit lp with :q, submit the desired forms to the Common Lisp read-eval-print loop, and reenter ACL2 with (lp).




    acl2-sources/doc/HTML/EVALUATOR-RESTRICTIONS.html0000664002132200015000000003027212222333530020241 0ustar kaufmannacl2 EVALUATOR-RESTRICTIONS.html -- ACL2 Version 6.3

    EVALUATOR-RESTRICTIONS

    some restrictions on the use of evaluators in meta-level rules
    Major Section:  META
    

    Note: This topic, which explains some subtleties for evaluators, can probably be skipped by most readers.

    Rules of class :meta and of class :clause-processor are stated using so-called ``evaluator'' functions. Here we explain some restrictions related to evaluators. Below we refer primarily to :meta rules, but the discussion applies equally to :clause-processor rules.

    In a nutshell, we require that a rule's evaluator does not support other functions in the rule, and we require that the evaluator not be introduced under a non-trivial encapsulate. We also require that no function has an attachment (see defattach) that is both ancestral in the evaluator and also ancestral in the meta or clause-processor functions. We explain these restrictions in detail below.

    An argument given elsewhere (see meta, in particular ``Aside for the logic-minded'') explains that the correctness argument for applying metatheoretic simplifiers requires that one be able to ``grow'' an evaluator (see defevaluator) to handle all functions in the current ACL2 world. Then we may, in essence, functionally instantiate the original evaluator to the new (``grown'') evaluator, provided that the new evaluator satisfies all of the axioms of the original. We therefore require that the evaluator function does not support the formula of any defaxiom event. This notion of ``support'' (sometimes denoted ``is an ancestor of'') is defined recursively as follows: a function symbol supports a formula if either it occurs in that formula, or else it supports the definition or constraint for some function symbol that occurs in that formula. Moreover, we require that neither the evaluator function nor its list version support the definition or constraint for any other function symbol occurring in the proposed :meta theorem.

    We also require that the evaluator does not support the formula of a :meta rule's metafunction (nor, if there is one, hypothesis metafunction) or of a :clause-processor rule's clause-processor function. This requirement, along with with the analogous requirement for defaxiom events stated above, are necessary in order to carry out the functional instantiation argument alluded to above, as follows (where the reader may find it useful to have some familiarity with the paper ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pages 161-203). By the usual conservativity argument, we know that the rule follows logically from the axiomatic events for its supporters. This remains true if we functionally instantiate the evaluator with one corresponding to all the functions symbols of the current session, since none of the definitions of supporters of defaxioms or metafunctions are hit by that functional substitution.

    Notice though that the argument above depends on knowing that the rule is not itself an axiom about the evaluator! Therefore, we also restrict evaluators so that they are not defined in the scope of a superior encapsulate event with non-empty signature, in order to avoid an even more subtle problem. The aforementioned correctness argument depends on knowing that the rule is provable from the axioms on the evaluator and metafunction (and hypothesis metafunction, if any). The additional restriction avoids unsoundness! The following events, if allowed, produce a proof that (f x) equals t even though, as shown below, that does not follow logically from the axioms introduced.

    ; Introduce our metafunction.
    (defun my-cancel (term)
      (case-match term
        (('f ('g))
         *t*)
        (& term)))
    
    ; Introduce our evaluator and prove our meta rule, but in the same
    ; encapsulate!
    (encapsulate
     ((f (x) t))
    
     (local (defun f (x) (declare (ignore x)) t))
    
     (defevaluator evl evl-list
       ((f x)))
    
     (defthm correctness-of-my-cancel
       (equal (evl x a)
              (evl (my-cancel x) a))
       :rule-classes ((:meta :trigger-fns (f)))))
    
    ; Prove that (f x) = t.
    (encapsulate
     ()
    
     (local (defstub c () t))
    
     (local (encapsulate
             ()
             (local (defun g () (c)))
             (local (in-theory (disable g (g))))
             (local (defthm f-g
                      (equal (f (g)) t)
                      :rule-classes nil))
             (defthm f-c
               (equal (f (c)) t)
               :hints (("Goal" :use f-g
                        :in-theory (e/d (g) (correctness-of-my-cancel))))
               :rule-classes nil)))
    
     (defthm f-t
       (equal (f x) t)
       :hints (("Goal" :by (:functional-instance
                            f-c
                            (c (lambda () x)))))
       :rule-classes nil))
    
    To see that the term (equal (f x) t) does not follow logically from the axiomatic events above, consider following the above definition of my-cancel with the following events instead.
    ; (defun my-cancel (term) ...) as before, then:
    
    (defun f (x)
      (not x))
    
    (defun g ()
      nil)
    
    (defevaluator evl evl-list
       ((f x) (g)))
    
    These events imply the axiomatic events above, because we still have the definition of my-cancel, we have a stronger defevaluator event, and we can now prove correctness-of-my-cancel exactly as it is stated above. So, the rule f-t is a logical consequence of the chronology of the current session. However, in the current session we can also prove the following rule, which contradicts f-t.
    (defthm f-not-t
      (equal (f t) nil)
      :rule-classes nil)
    
    It follows that the current session logically yields a contradiction!

    Erik Reeber has taken the above example and modified it to prove nil in ACL2 Version_3.1, as follows.

    
    (in-package "ACL2")
    
    (defun my-cancel (term)
       (case-match term
         (('f ('g))
          *t*)
         (('f2 ('g2))
          *t*)
         (& term)))
    
    (defun f2 (x)
       (not x))
    
    (defun g2 ()
       nil)
    
    (encapsulate
      ((f (x) t))
    
      (local (defun f (x) (declare (ignore x)) t))
    
      (defevaluator evl evl-list
        ((f x)
         (f2 x)
         (g2)))
    
      (defthm correctness-of-my-cancel
        (equal (evl x a)
               (evl (my-cancel x) a))
        :rule-classes ((:meta :trigger-fns (f)))))
    
    (encapsulate
      ()
    
      (local (defstub c () t))
    
      (local (encapsulate
              ()
              (local (defun g () (c)))
              (local (in-theory (disable g (g))))
              (local (defthm f-g
                       (equal (f (g)) t)
                       :rule-classes nil))
              (defthm f-c
                (equal (f (c)) t)
                :hints (("Goal" :use f-g
                         :in-theory (e/d (g) (correctness-of-my-cancel))))
                :rule-classes nil)))
    
      (defthm f-t
        (equal (f x) t)
        :hints (("Goal" :by (:functional-instance
                             f-c
                             (c (lambda () x)))))
        :rule-classes nil))
    
    (defun g ()
       nil)
    
    ; Below is the expansion of the following defevaluator, changed slightly as
    ; indicated by comments.
    ; (defevaluator evl2 evl2-list ((f x) (f2 x) (g) (g2)))
    
    (ENCAPSULATE
      (((EVL2 * *) => *)
       ((EVL2-LIST * *) => *))
      (SET-INHIBIT-WARNINGS "theory")
      (LOCAL (IN-THEORY *DEFEVALUATOR-FORM-BASE-THEORY*))
      (LOCAL
       (MUTUAL-RECURSION (DEFUN EVL2 (X A)
                           (DECLARE (XARGS :VERIFY-GUARDS NIL
                                           :MEASURE (ACL2-COUNT X)
                                           :WELL-FOUNDED-RELATION O<
                                           :MODE :LOGIC))
                           (COND ((SYMBOLP X) (CDR (ASSOC-EQ X A)))
                                 ((ATOM X) NIL)
                                 ((EQ (CAR X) 'QUOTE) (CAR (CDR X)))
                                 ((CONSP (CAR X))
                                  (EVL2 (CAR (CDR (CDR (CAR X))))
                                        (PAIRLIS$ (CAR (CDR (CAR X)))
                                                  (EVL2-LIST (CDR X) A))))
                                 ((EQUAL (CAR X) 'F) ; changed f to f2 just below
                                  (F2 (EVL2 (CAR (CDR X)) A)))
                                 ((EQUAL (CAR X) 'F2)
                                  (F2 (EVL2 (CAR (CDR X)) A)))
                                 ((EQUAL (CAR X) 'G) (G))
                                 ((EQUAL (CAR X) 'G2) (G2))
                                 (T NIL)))
                         (DEFUN EVL2-LIST (X-LST A)
                           (DECLARE (XARGS :MEASURE (ACL2-COUNT X-LST)
                                           :WELL-FOUNDED-RELATION O<))
                           (COND ((ENDP X-LST) NIL)
                                 (T (CONS (EVL2 (CAR X-LST) A)
                                          (EVL2-LIST (CDR X-LST) A)))))))
    
      (DEFTHM EVL2-CONSTRAINT-1
        (IMPLIES (SYMBOLP X)
                 (EQUAL (EVL2 X A)
                        (CDR (ASSOC-EQ X A)))))
      (DEFTHM EVL2-CONSTRAINT-2
        (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'QUOTE))
                 (EQUAL (EVL2 X A) (CADR X))))
      (DEFTHM EVL2-CONSTRAINT-3
        (IMPLIES (AND (CONSP X) (CONSP (CAR X)))
                 (EQUAL (EVL2 X A)
                        (EVL2 (CADDAR X)
                              (PAIRLIS$ (CADAR X)
                                        (EVL2-LIST (CDR X) A))))))
      (DEFTHM EVL2-CONSTRAINT-4
        (IMPLIES (NOT (CONSP X-LST))
                 (EQUAL (EVL2-LIST X-LST A) NIL)))
      (DEFTHM EVL2-CONSTRAINT-5
        (IMPLIES (CONSP X-LST)
                 (EQUAL (EVL2-LIST X-LST A)
                        (CONS (EVL2 (CAR X-LST) A)
                              (EVL2-LIST (CDR X-LST) A)))))
      (DEFTHM EVL2-CONSTRAINT-6
        (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F))
                 (EQUAL (EVL2 X A) ; changed f to f2 just below
                        (F2 (EVL2 (CADR X) A)))))
      (DEFTHM EVL2-CONSTRAINT-7
        (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'F2))
                 (EQUAL (EVL2 X A)
                        (F2 (EVL2 (CADR X) A)))))
      (DEFTHM EVL2-CONSTRAINT-8
        (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G))
                 (EQUAL (EVL2 X A) (G))))
      (DEFTHM EVL2-CONSTRAINT-9
        (IMPLIES (AND (CONSP X) (EQUAL (CAR X) 'G2))
                 (EQUAL (EVL2 X A) (G2)))))
    
    (defthm f2-t
       (equal (f2 x) t)
       :hints (("Goal" :by (:functional-instance
                            f-t
                            (f f2)
                            (evl evl2)
                            (evl-list evl2-list)))))
    
    (defthm bug-implies-nil
       nil
       :hints (("Goal" :use ((:instance f2-t (x t)))))
       :rule-classes nil)
    

    Finally, we also require that no function has an attachment (see defattach) that is both ancestral in the evaluator and also ancestral in the meta or clause-processor functions. (If you don't use defattach then you can ignore this condition.) Without this restriction, the following events prove nil.

    (in-package "ACL2")
    (defstub f () t)
    (defevaluator evl evl-list
      ((f)))
    (defun my-meta-fn (x)
      (if (equal x '(f))
          (list 'quote (f))
        x))
    (defthm my-meta-fn-correct
      (equal (evl x a)
             (evl (my-meta-fn x) a))
      :rule-classes ((:meta :trigger-fns (f))))
    (defun constant-nil ()
      (declare (xargs :guard t))
      nil)
    (defattach f constant-nil)
    (defthm f-is-nil
    ; proved using my-meta-fn-correct
      (equal (f) nil)
      :rule-classes nil)
    (defthm contradiction
      nil
      :hints (("Goal" :use ((:functional-instance
                             f-is-nil
                             (f (lambda () t))))))
      :rule-classes nil)
    
    To see why this restriction is sufficient, see a comment in the ACL2 source code entitled ``; Essay on Correctness of Meta Reasoning.''




    acl2-sources/doc/HTML/EVENP.html0000664002132200015000000000155112222333523015626 0ustar kaufmannacl2 EVENP.html -- ACL2 Version 6.3

    EVENP

    test whether an integer is even
    Major Section:  ACL2-BUILT-INS
    

    (evenp x) is true if and only if the integer x is even. Actually, in the ACL2 logic (evenp x) is defined to be true when x/2 is an integer.

    The guard for evenp requires its argument to be an integer.

    Evenp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/EVENTS.html0000664002132200015000000002354412222333516015765 0ustar kaufmannacl2 EVENTS.html -- ACL2 Version 6.3

    EVENTS

    functions that extend the logic
    Major Section:  ACL2 Documentation
    

    Some Related Topics

    Any extension of the syntax of ACL2 (i.e., the definition of a new constant or macro), the axioms (i.e., the definition of a function), or the rule database (i.e., the proof of a theorem), constitutes a logical ``event.'' Events change the ACL2 logical world (see world). Indeed, the only way to change the ACL2 world is via the successful evaluation of an event function. Every time the world is changed by an event, a landmark is left on the world and it is thus possible to identify the world ``as of'' the evaluation of a given event. An event may introduce new logical names. Some events introduce no new names (e.g., verify-guards), some introduce exactly one (e.g., defmacro and defthm), and some may introduce many (e.g., encapsulate ).

    ACL2 typically completes processing of an event by printing a summary. Unless proofs are skipped (see ld-skip-proofsp) or summary output is inhibited (see set-inhibit-output-lst), information about the proof attempt (if any) is printed that includes a list of rules used, a summary of warnings, and the number of ``prover steps'' (if any; see with-prover-step-limit). A breakdown of the time used is also printed, which by default is runtime (cpu time), but can be changed to realtime (wall clock time); see get-internal-time.

    See embedded-event-form for a discussion of events permitted in books.




    acl2-sources/doc/HTML/EVISC-TABLE.html0000664002132200015000000000764012222333517016457 0ustar kaufmannacl2 EVISC-TABLE.html -- ACL2 Version 6.3

    EVISC-TABLE

    support for abbreviated output
    Major Section:  EVENTS
    

    The evisc-table is an ACL2 table (see table) whose purpose is to modify the print representations of specified non-nil objects. When a key (some object) is associated with a string value, then that string is printed instead of that key (as an abbreviation). For example, the following log shows how to abbreviate the key (a b c) with the token <my-abc-list>.

    ACL2 !>(table evisc-table '(a b c) "<my-abc-list>")
    
    Summary
    Form:  ( TABLE EVISC-TABLE ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     EVISC-TABLE
    ACL2 !>'(a b c)
    <my-abc-list>
    ACL2 !>'(4 5 a b c)
    (4 5 . <my-abc-list>)
    ACL2 !>
    

    Every value in this table must be either a string or nil, where nil eliminates any association of the key with an abbreviation. Continuing with the log above:

    ACL2 !>(table evisc-table '(a b c) nil)
    
    Summary
    Form:  ( TABLE EVISC-TABLE ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     EVISC-TABLE
    ACL2 !>'(a b c)
    (A B C)
    ACL2 !>'(4 5 a b c)
    (4 5 A B C)
    ACL2 !>
    

    It can be particularly helpful to use this table to abbreviate a constant introduced by defconst by prefixing the constant name with "#,", as we now describe. Consider first the following example.

    (defconst *abc* '(1 2 3 4 5 6 7 8))
    (table evisc-table *abc*
      (concatenate 'string "#," (symbol-name '*abc*)))
    
    Then the constant *abc* is printed as follows -- very helpful if its associated structure is significantly larger than the 8-element list of numbers shown above!
    ACL2 !>*abc*
    #,*ABC*
    ACL2 !>
    
    What's more, the ACL2 reader will replace #,*C*, where *C* is defined by defconst, by its value, regardless of evisc-table; see sharp-dot-reader. Continuing with the example above, we have:
    ACL2 !>(cdr (quote #,*ABC*))
    (2 3 4 5 6 7 8)
    ACL2 !>
    
    Of course, more care needs to be taken if packages are involved (see defpkg), as we now illustrate.
    ACL2 !>(defpkg "FOO" nil)
    
    Summary
    Form:  ( DEFPKG "FOO" ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     "FOO"
    ACL2 !>(defconst foo::*a* '(1 2 3))
    
    Summary
    Form:  ( DEFCONST FOO::*A* ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO::*A*
    ACL2 !>(table evisc-table foo::*a* "#,foo::*a*")
    
    Summary
    Form:  ( TABLE EVISC-TABLE ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     EVISC-TABLE
    ACL2 !>foo::*a*
    #,foo::*a*
    ACL2 !>'#,foo::*a*
    #,foo::*a*
    ACL2 !>(cdr '#,foo::*a*)
    (2 3)
    ACL2 !>
    
    We conclude by an example showing some extra care that may be important to consider taking. We start with:
    (defconst |*BaR*| '(3 4))
    
    Then the following works just fine; but try it without the extra code for the may-need-slashes case and you'll see that the sharp-dot printing is missing. First:
    (table evisc-table
           |*BaR*|
           (let ((x (symbol-name '|*BaR*|)))
             (if (may-need-slashes x)
                 (concatenate 'string "#.|" x "|")
               (concatenate 'string "#." x))))
    
    Then:
    ACL2 !>|*BaR*|
    #,|*BaR*|
    ACL2 !>
    




    acl2-sources/doc/HTML/EVISC-TUPLE.html0000664002132200015000000000540012222333520016503 0ustar kaufmannacl2 EVISC-TUPLE.html -- ACL2 Version 6.3

    EVISC-TUPLE

    control suppression of details when printing
    Major Section:  IO
    

    ACL2 output is generally printed in full. However, ACL2 can be directed to abbreviate, or ``eviscerate'', objects before printing them. To ``eviscerate'' an object we replace certain substructures within it by strings that are printed in their stead. Such replacement is made relative to a so-called ``evisc-tuple'', which has four components: (evisc-tuple print-level print-length alist hiding-cars) is the same as the value of (list alist print-level print-length hiding-cars), and the components are used as follows (with priority order as discussed below). The alist component is used to replace any substructure occurring as a key by the corresponding string. The print-level and print-length are analogous to Common Lisp variables *print-level* and *print-length*, respectively, and cause replacement of substructures deeper than print-level by `#' and those longer than print-length by `...'. Finally, any consp x that starts with one of the symbols in hiding-cars is printed as <hidden>.

    The following example illustrates the use of an evisc-tuple that limits the print-level to 3 -- only three descents into list structures are permitted before replacing a subexpression by `#' -- and limits the print-length to 4 -- only the first four elements of any list structure will be printed before replacing its tail by `...'.

    ACL2 !>(fms "~x0~%"
                (list (cons #\0 '((a b ((c d)) e f g) u v w x y)))
                *standard-co*
                state
                (evisc-tuple 3 4 nil nil))
    
    ((A B (#) E ...) U V W ...)
    <state>
    ACL2 !>
    
    Notice that it is impossible to read the printed value back into ACL2, since there is no way for the ACL2 reader to interpret `#' or `...'. To solve this problem, see set-iprint.

    In the above example we pass an evisc-tuple explicitly to a printing function, in this case, fms (see fmt). But ACL2 also does its own printing, for example during a proof attempt. There are global evisc-tuples that control ACL2's printing; see set-evisc-tuple and see without-evisc.




    acl2-sources/doc/HTML/EVISCERATE-HIDE-TERMS.html0000664002132200015000000000176712222333520020010 0ustar kaufmannacl2 EVISCERATE-HIDE-TERMS.html -- ACL2 Version 6.3

    EVISCERATE-HIDE-TERMS

    to print (hide ...) as <hidden>
    Major Section:  IO
    

    Example:
    (assign eviscerate-hide-terms t)
    (assign eviscerate-hide-terms nil)
    

    Eviscerate-hide-terms is a state global variable whose value is either t or nil. The variable affects how terms are displayed by default (but not if you have set the term-evisc-tuple to other than its default; see set-evisc-tuple). If t, terms of the form (hide ...) are printed as <hidden>. Otherwise, they are printed normally.




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-BINARY-TREES.html0000664002132200015000000000272212222333515022204 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-BINARY-TREES.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-BINARY-TREES

    induction on binary trees
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Classical Induction on Binary Trees: To prove (p x), for all x, by classical induction on binary tree structures, prove each of the following:

    Base Case:
    (implies (atom x) (p x))
    

    Induction Step:
    (implies (and (not (atom x))
                  (p (car x))
                  (p (cdr x)))
             (p x))
    

    An argument analogous to that given in example-induction-scheme-on-lists should convince you that (p x) holds for every object.

    A function that suggests this induction is:

    (defun flatten (x)
      (if (atom x)
          (list x)
          (app (flatten (car x))
               (flatten (cdr x))))).
    




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2.html0000664002132200015000000000427312222333515021561 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2

    induction downwards 2 steps at a time
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Classical Induction on Natural Numbers Preserving Parity: Here is another way to decompose natural numbers. To prove (p n), for all n, prove each of the following:

    Base Case 1:
    (implies (zp n) (p n))
    

    Base Case 2:
    (implies (equal n 1) (p n))
    

    Induction Step:
    (implies (and (not (zp n))
                  (not (equal n 1))
                  (p (- n 2)))
             (p n))
    
    Base Case 1 establishes that p holds for 0 (and all objects other than positive naturals).

    Base Case 2 establishes that p holds for 1.

    The Induction Step establishes that if n is a natural number greater than 1, and if p holds for n-2, then p holds for n.

    Note that we have thus proved that (p n) holds, for all n. For example, (p -7), (p 'abc), and (p 0) are all established by Base Case 1. (p 1) is established by Base Case 2. (p 2) is established from (p 0) and the Induction Step. Think about it! (p 3) is established form (p 1) and the Induction Step, etc.

    A function that suggests this induction is:

    (defun parity (n)
      (if (zp n)
          'even
          (if (equal n 1)
              'odd
              (parity (- n 2))))).
    




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION.html0000664002132200015000000000430112222333515022324 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION

    induction on natural numbers
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Classical Induction on Natural Numbers: Induction is familiar in the arithmetic setting. To prove (p n), for all n, by classical induction on the construction of the natural numbers, prove each of the following:

    Base Case:
    (implies (zp n) (p n))
    

    Induction Step:
    (implies (and (not (zp n))
                  (p (- n 1)))
             (p n))
    
    The Base Case establishes that p holds for 0. In fact, because of the definition of zp , it establishes that (p n) holds when n is 0 and it holds when n is not a natural number.

    The Induction Step establishes that if n is a natural number other than 0, and if p holds for n-1, then p holds for n. The hypothesis (p (- n 1)) above is called the induction hypothesis.

    A function that suggests this induction is

    (defun nat-recursion (n)
      (if (zp n)
          n
          (nat-recursion (- n 1))))
    
    Similarly, the term (fact n) suggests this induction if fact is defined:
     (defun fact (k)
      (if (zp k)
          1
          (* k (fact (- k 1))))).
    
    even though the formal parameter of this definition of fact is k, not n.




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-ON-LISTS.html0000664002132200015000000000352612222333515021553 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-ON-LISTS.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-ON-LISTS

    induction on lists
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Classical Induction on Lists: To prove (p x), for all x, by classical induction on the linear list structure, prove each of the following:

    Base Case:
    (implies (endp x) (p x))
    

    Induction Step:
    (implies (and (not (endp x))
                  (p (cdr x)))
             (p x))
    

    An argument analogous to that given for natural numbers, example-induction-scheme-nat-recursion, establishes (p x) for every x. For example, (p -7), (p 'abc), and (p nil) are all established by the Base Case. (p '(Friday)) follows from (p nil), given the Induction Step. That sentence bears thinking about! Think about it! Similarly, (p '(Yellow)) holds for the same reason. (p '(Thursday Friday)) follows from (p '(Friday)) and the Induction Step, etc.

    A function that suggests this induction is

    (defun app (x y)
      (if (endp x)
          y
          (cons (car x)
                (app (cdr x) y)))).
    




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES.html0000664002132200015000000000260112222333515023255 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES

    induction on several variables
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Induction on Several Variables To (p n x) for all n and all x, prove each of the following:

    Base Case 1:
    (implies (endp x)
             (p n x))
    

    Base Case 2:
    (implies (and (not (endp x))
                  (zp n))
             (p n x))
    

    Induction Step:
    (implies (and (not (endp x))
                  (not (zp n))
                  (p (- n 1) (cdr x)))
             (p n x))
    

    A function that suggests this induction is

    (defun nth (n x)
      (if (endp x)
          nil
          (if (zp n)
              (car x)
              (nth (- n 1) (cdr x))))).
    




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-UPWARDS.html0000664002132200015000000000362412222333515021467 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-UPWARDS.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-UPWARDS

    induction upwards
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Induction Upwards: To (p i max) for all i and all max, prove each of the following:

    Base Case:
    (implies (not (and (natp i)
                       (natp max)
                       (< i max)))
             (p i max))
    

    Induction Step:
    (implies (and  (natp i)
                   (natp max)
                   (< i max)
                   (p (+ i 1) max))
             (p i max))
    
    Note that the induction hypothesis is about an i that is bigger than the i in in the conclusion. In induction, as in recursion, the sense of one thing being ``smaller'' than another is determined by an arbitrary measure of all the variables, not the magnitude or extent of some particular variable.

    A function that suggests this induction is shown below. ACL2 has to be told the measure, namely the difference between max and i (coerced to a natural number to insure that the measure is an ordinal).

    (defun count-up (i max)
      (declare (xargs :measure (nfix (- max i))))
      (if (and (natp i)
               (natp max)
               (< i max))
          (cons i (count-up (+ 1 i) max))
          nil)).
    




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS.html0000664002132200015000000000640212222333515023012 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS

    induction scheme with accumulators
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    To prove (p x a) for all x and all a, prove each of the following:

    Base Case:
    (implies (endp x)
             (p x a))
    

    Induction Step:
    (implies (and (not (endp x))
                  (p (cdr x) (cons (car x) a)))
             (p x a))
    
    Note that in the induction hypothesis we assume p for a smaller x but a larger a. In fact, we could include as many induction hypotheses as we want and use any terms we want in the a position as long as the x position is occupied by a smaller term.

    A function that suggests this particular induction is shown below.

    (defun rev1 (x a)
      (if (endp x)
          a
          (rev1 (cdr x) (cons (car x) a)))).
    

    A function that suggests a similar induction in which three induction hypotheses are provided, one in which the a position is occupied by (cons (car x) a), another in which the a position is occupied by some arbitrary term b, and a third in which the a position is occupied by a, is suggested by the term (rev1-modified x a b) where

    (defun rev1-modified (x a b)
      (if (endp x)
          (list x a b)
          (list (rev1-modified (cdr x) (cons (car x) a) b)
                (rev1-modified (cdr x) b b)
                (rev1-modified (cdr x) a b))))
    
    Remember that the value of this term or function is irrelevant to the induction suggested. Because ACL2's definitional principle insists that all the formal parameters play a role in the computation (at least syntactically), it is common practice when defining functions for their induction schemes to return the list of all the formals (to insure all variables are involved) and to combine recursive calls on a given branch with list (to avoid introducing additional case analysis as would happen if and or or or other propositional functions are used).

    If you tried to prove (p x a) and suggested the induct hint (rev1-modified x a (fact k)), as by

    (thm (p x a)
         :hints (("Goal" :induct (rev1-modified x a (fact k)))))
    
    the inductive argument would be:
    Base Case:
    (implies (endp x)
             (p x a))
    

     Inductive Step:
     (implies (and (not (endp x))
                   (p (cdr x) (cons (car x) a))
                   (p (cdr x) (fact k))
                   (p (cdr x) a))
              (p x a))
     




    acl2-sources/doc/HTML/EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS.html0000664002132200015000000000420112222333515024564 0ustar kaufmannacl2 EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS

    induction scheme with more than one induction step
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    See logic-knowledge-taken-for-granted-inductive-proof for an explanation of what we mean by the induction suggested by a recursive function or a term.

    Several Induction Steps: To (p x i a) for all x, i, and a, prove each of the following:

    Base Case 1:
    (implies (zp i)
             (p x i a))
    

    Induction Step 1:
    (implies (and (not (zp i))
                  (equal (parity i) 'even)
                  (p (* x x)
                     (floor i 2)
                     a))
             (p x i a))
    

    Induction Step 2:
    (implies (and (not (zp i))
                  (not (equal (parity i) 'even))
                  (p x
                     (- i 1)
                     (* x a)))
             (p x i a))
    

    A function that suggests this induction is the binary exponentiation function for natural numbers.

    (defun bexpt (x i a)
      (cond ((zp i) a)
            ((equal (parity i) 'even)
             (bexpt (* x x)
                    (floor i 2)
                    a))
            (t (bexpt x
                      (- i 1)
                      (* x a)
                      )))).
    
    In order to admit this function it is necessary to know that (floor i 2) is smaller than i in the case above. This can be proved if the community book "arithmetic-5/top" has been included from the ACL2 system directory, i.e.,
    (include-book "arithmetic-5/top" :dir :system)
    
    should be executed before defining bexpt.




    acl2-sources/doc/HTML/EXAMPLE-INDUCTIONS.html0000664002132200015000000000702712222333515017526 0ustar kaufmannacl2 EXAMPLE-INDUCTIONS.html -- ACL2 Version 6.3

    EXAMPLE-INDUCTIONS

    some examples of induction schemes in ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Here are some pages illustrating various induction schemes suggested by recursive functions.

    Classical Induction on Natural Numbers: see example-induction-scheme-nat-recursion.

    Induction Preserving Even/Odd Parity or
    Induction Downwards by 2 or
    Induction with Multiple Base Cases: see example-induction-scheme-down-by-2 for an induction in which the induction hypothesis decreases the induction variable by an amount other than 1. This illustrates that the induction hypothesis can be about whatever term or terms are needed to explain how the formula recurs. The example also illustrates an induction with more than one Base Case.

    Classical Induction on Lists: see example-induction-scheme-on-lists for an induction over linear lists, in which we inductively assume the conjecture for (cdr x) and prove it for x. It doesn't matter whether the list is nil-terminated or not; the Base Case addresses all the possibilities.

    Classical Induction on Binary (Cons) Trees: see example-induction-scheme-binary-trees for an induction over the simplest form of binary tree. Here the Induction Step provides two hypotheses, one about the left subtree and one about the right subtree.

    Induction on Several Variables: see example-induction-scheme-on-several-variables for an induction in which several variables participate in the case analysis and induction hypotheses.

    Induction Upwards: see example-induction-scheme-upwards for an induction scheme in which the induction hypothesis is about something ``bigger than'' the induction conclusion. This illustrates that the sense in which the hypothesis is about something ``smaller'' than the conclusion is determined by a measure of all the variables, not the magnitude or extent of some single variable.

    Induction with Auxiliary Variables or
    Induction with Accumulators: see example-induction-scheme-with-accumulators for an induction scheme in which one variable ``gets smaller'' but another is completely arbitrary. Such schemes are common when dealing with tail-recursive functions that accumulate partial results in auxiliary variables. This example also shows how to provide several arbitrary terms in a non-inductive variable of a scheme.

    Induction with Multiple Induction Steps: see example-induction-scheme-with-multiple-induction-steps for an induction in which we make different inductive hypotheses depending on which case we're in. This example also illustrates the handling of auxiliary variables or accumulators.




    acl2-sources/doc/HTML/EXECUTABLE-COUNTERPART-THEORY.html0000664002132200015000000000406212222333531021205 0ustar kaufmannacl2 EXECUTABLE-COUNTERPART-THEORY.html -- ACL2 Version 6.3

    EXECUTABLE-COUNTERPART-THEORY

    executable counterpart rules as of logical name
    Major Section:  THEORIES
    

    Examples:
    (executable-counterpart-theory :here)
    (executable-counterpart-theory 'lemma3)
    
    See logical-name.

    General Form:
    (executable-counterpart-theory logical-name)
    
    Returns the theory containing all the :executable-counterpart runes, whether enabled or not, that existed immediately after logical-name was introduced. See the documentation for theories, logical-name, executable-counterpart and function-theory.

    You may experience a fencepost problem in deciding which logical name to use. Deflabel can always be used to mark unambiguously for future reference a particular point in the development of your theory. The order of events in the vicinity of an encapsulate is confusing. See encapsulate.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/EXECUTABLE-COUNTERPART.html0000664002132200015000000001135412222333520020175 0ustar kaufmannacl2 EXECUTABLE-COUNTERPART.html -- ACL2 Version 6.3

    EXECUTABLE-COUNTERPART

    a rule for computing the value of a function
    Major Section:  MISCELLANEOUS
    

    Examples:
    (:executable-counterpart length)
    
    which may be abbreviated in theories as
    (length)
    

    Every defun introduces at least two rules used by the theorem prover. Suppose fn is the name of a defun'd function. Then (:definition fn) is the rune (see rune) naming the rule that allows the simplifier to replace calls of fn by its instantiated body. (:executable-counterpart fn) is the rune for the rule for how to evaluate the function on known constants.

    When typing theories it is convenient to know that (fn) is a runic designator that denotes (:executable-counterpart fn). See theories.

    If (:executable-counterpart fn) is enabled, then when applications of fn to known constants are seen by the simplifier they are computed out by executing the Common Lisp code for fn (with the appropriate handling of guards). Suppose fact is defined as the factorial function. If the executable counterpart rune of fact, (:executable-counterpart fact), is enabled when the simplifier encounters (fact 12), then that term will be ``immediately'' expanded to 479001600. Note that even if subroutines of fn have disabled executable counterparts, fn will call their Lisp code nonetheless: once an executable counterpart function is applied, no subsidiary enable checks are made.

    Such one-step expansions are sometimes counterproductive because they prevent the anticipated application of certain lemmas about the subroutines of the expanded function. Such computed expansions can be prevented by disabling the executable counterpart rune of the relevant function. For example, if (:executable-counterpart fact) is disabled, (fact 12) will not be expanded by computation. In this situation, (fact 12) may be rewritten to (* 12 (fact 11)), using the rule named (:definition fact), provided the system's heuristics permit the introduction of the term (fact 11). Note that lemmas about multiplication may then be applicable (while such lemmas would be inapplicable to 479001600). In many proofs it is desirable to disable the executable counterpart runes of certain functions to prevent their expansion by computation. See executable-counterpart-theory.

    Finally: What do we do about functions that are ``constrained'' rather than defined, such as the following? (See encapsulate.)

    (encapsulate (((foo *) => *))
                 (local (defun foo (x) x)))
    
    Does foo have an executable counterpart? Yes: since the vast majority of functions have sensible executable counterparts, it was decided that all functions, even such ``constrained'' ones, have executable counterparts. We essentially ``trap'' when such calls are inappropriate. Thus, consider for example:
    (defun bar (x)
      (if (rationalp x)
          (+ x 1)
        (foo x)))
    
    If the term (bar '3) is encountered by the ACL2 rewriter during a proof, and if the :executable-counterpart of bar is enabled, then it will be invoked to reduce this term to '4. However, if the term (bar 'a) is encountered during a proof, then since 'a is not a rationalp and since the :executable-counterpart of foo is only a ``trap,'' then this call of the :executable-counterpart of bar will result in a ``trap.'' In that case, the rewriter will return the term (hide (bar 'a)) so that it never has to go through this process again. See hide.




    acl2-sources/doc/HTML/EXISTS.html0000664002132200015000000000151112222333517015767 0ustar kaufmannacl2 EXISTS.html -- ACL2 Version 6.3

    EXISTS

    existential quantifier
    Major Section:  DEFUN-SK
    

    The symbol exists (in the ACL2 package) represents existential quantification in the context of a defun-sk form. See defun-sk and see forall.

    See quantifiers for an example illustrating how the use of recursion, rather than explicit quantification with defun-sk, may be preferable.




    acl2-sources/doc/HTML/EXIT-BOOT-STRAP-MODE.html0000664002132200015000000000236512222333520017675 0ustar kaufmannacl2 EXIT-BOOT-STRAP-MODE.html -- ACL2 Version 6.3

    EXIT-BOOT-STRAP-MODE

    the end of pre-history
    Major Section:  MISCELLANEOUS
    

    Exit-boot-strap-mode is the last step in creating the ACL2 world in which the user lives. It has command number 0. Commands before it are part of the system initialization and extend all the way back to :min. Commands after it are those of the user.

    Exit-boot-strap-mode is a Common Lisp function but not an ACL2 function. It is called when every defconst, defun, etc., in our source code has been processed under ACL2 and the world is all but complete. exit-boot-strap-mode has only one job: to signal the completion of the boot-strapping.




    acl2-sources/doc/HTML/EXIT.html0000664002132200015000000000065312222333522015523 0ustar kaufmannacl2 EXIT.html -- ACL2 Version 6.3

    EXIT

    quit entirely out of Lisp
    Major Section:  OTHER
    

    Same as good-bye.




    acl2-sources/doc/HTML/EXPAND.html0000664002132200015000000000066112222333520015726 0ustar kaufmannacl2 EXPAND.html -- ACL2 Version 6.3

    EXPAND

    hints keyword :EXPAND
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/EXPLODE-NONNEGATIVE-INTEGER.html0000664002132200015000000000220612222333523020715 0ustar kaufmannacl2 EXPLODE-NONNEGATIVE-INTEGER.html -- ACL2 Version 6.3

    EXPLODE-NONNEGATIVE-INTEGER

    the list of characters in the radix-r form of a number
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    ACL2 !>(explode-nonnegative-integer 925 10 nil)
    (#9 #2 #5)
    ACL2 !>(explode-nonnegative-integer 325 16 nil)
    (#3 #9 #D)
    
    For a non-negative integer n, (explode-nonnegative-integer n r nil) is the list of characters in the radix-r (base-r) representation of n.

    The guard for explode-nonnegative-integer requires the first argument to be a nonnegative integer and second argument to be a valid radix for ACL2 (2, 8, 10, or 16).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/EXPT.html0000664002132200015000000000221712222333523015531 0ustar kaufmannacl2 EXPT.html -- ACL2 Version 6.3

    EXPT

    exponential function
    Major Section:  ACL2-BUILT-INS
    

    (Expt r i) is the result of raising the number r to the integer power i.

    The guard for (expt r i) is that r is a number and i is an integer, and furthermore, if r is 0 then i is nonnegative. When the type requirements of the guard aren't met, (expt r i) first coerces r to a number and i to an integer.

    Expt is a Common Lisp function. See any Common Lisp documentation for more information. Note that r can be a complex number; this is consistent with Common lisp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/EXTENDED-METAFUNCTIONS.html0000664002132200015000000004321612222333520020167 0ustar kaufmannacl2 EXTENDED-METAFUNCTIONS.html -- ACL2 Version 6.3

    EXTENDED-METAFUNCTIONS

    state and context sensitive metafunctions
    Major Section:  MISCELLANEOUS
    

    General Form of an Extended :Meta theorem:
    (implies (and (pseudo-termp x)              ; this hyp is optional
                  (alistp a)                    ; this hyp is optional
                  (ev (hyp-fn x mfc state) a)   ; this hyp is optional
                  ; meta-extract hyps may also be included (see below)
                  )
             (equiv (ev x a)
                    (ev (fn x mfc state) a)))
    
    where the restrictions are as described in the documentation for meta where state is literally the symbol STATE, and x, a, mfc, and state are distinct variable symbols. A :meta theorem of the above form installs fn as a metatheoretic simplifier with hypothesis function hyp-fn, exactly as for vanilla metafunctions. The only difference is that when the metafunctions are applied, some contextual information is passed in via the mfc argument and the ACL2 state is made available.

    See meta for a discussion of vanilla flavored metafunctions. This documentation assumes you are familiar with the simpler situation, in particular, how to define a vanilla flavored metafunction, fn, and its associated hypothesis metafunction, hyp-fn, and how to state and prove metatheorems installing such functions. Defining extended metafunctions requires that you also be familiar with many ACL2 implementation details. This documentation is sketchy on these details; see the ACL2 source code or email the acl2-help list if you need more help.

    Additional hypotheses are supported, called ``meta-extract hypotheses'', that allow metafunctions to depend on the validity of certain terms extracted from the context or the logical world. These hypotheses provide an even more advanced form of metatheorem so we explain them elsewhere; see meta-extract.

    The metafunction context, mfc, is a list containing many different data structures used by various internal ACL2 functions. We do not document the form of mfc. Your extended metafunction should just take mfc as its second formal and pass it into the functions mentioned below. The ACL2 state is well-documented (see state). Below we present expressions below that can be useful in defining extended metafunctions. Some of these expressions involve keyword arguments, :forcep and :ttree, which are optional and in most cases are fine to omit, and which we explain after we present the useful expressions.

    (mfc-clause mfc): returns the current goal, in clausal form. A clause is a list of ACL2 terms, implicitly denoting the disjunction of the listed terms. The clause returned by mfc-clause is the clausal form of the translation (see trans) of the goal or subgoal on which the rewriter is working. When a metafunction calls mfc-clause, the term being rewritten by the metafunction either occurs somewhere in this clause or, perhaps more commonly, is being rewritten on behalf of some lemma to which the rewriter has backchained while trying to rewrite a term in the clause.

    (mfc-ancestors mfc): returns an alist whose keys are the negations of the backchaining hypotheses being pursued. In particular, (null (mfc-ancestors mfc)) will be true exactly when rewriting is on part of the current goal. Exception: An element of this alist whose key is of the form (:binding-hyp hyp unify-subst) indicates that hyp has been encountered as a hypothesis of the form (equal var term) or (equiv var (double-rewrite-term)), in each case binding variable var to the result of rewriting term under unify-subst.

    (mfc-rdepth mfc): returns the remaining stack depth for calls of the rewriter (by default, *default-rewrite-stack-limit* at the top level; see rewrite-stack-limit). When this is 0, no further calls of the rewriter can be made without error.

    (mfc-type-alist mfc): returns the type-alist governing the occurrence of the term, x, being rewritten by the metafunction. A type-alist is an association list, each element of which is of the form (term ts . ttree). Such an element means that the term term has the type-set ts. The ttree component is probably irrelevant here. All the terms in the type-alist are in translated form (see trans). The ts are numbers denoting finite Boolean combinations of ACL2's primitive types (see type-set). The type-alist includes not only information gleaned from the conditions governing the term being rewritten but also that gleaned from forward chaining from the (negations of the) other literals in the clause returned by mfc-clause.

    (mfc-unify-subst mfc): returns nil except when evaluating a syntaxp or bind-free hypothesis, in which case, returns the unifying substitution present at the start of that evaluation.

    (mfc-world mfc): returns the ACL2 logical world.

    (mfc-ts term mfc state :forcep forcep :ttreep ttreep): returns the type-set of term in the current context; see type-set.

    (mfc-rw term obj equiv-info mfc state): returns the result of rewriting term in the current context, mfc, with objective obj and the equivalence relation described by equiv-info. Obj should be t, nil, or ?, and describes your objective: try to show that term is true, false, or anything. Equiv-info is either nil, t, a function symbol fn naming a known equivalence relation, or a list of congruence rules. Nil means return a term that is equal to term. T means return a term that is propositionally equivalent (i.e., in the iff sense) to term, while fn means return a term fn-equivalent to term. The final case, which is intended only for advanced users, allows the specification of generated equivalence relations, as supplied to the geneqv argument of rewrite. Generally, if you wish to establish that term is true in the current context, use the idiom

    (equal (mfc-rw term t t mfc state) *t*)
    
    The constant *t* is set to the internal form of the constant term t, i.e., 't.

    (mfc-rw+ term alist obj equiv-info mfc state): if alist is nil then this is equivalent to (mfc-rw term obj equiv-info mfc state). However, the former takes an argument, alist, that binds variables to terms, and returns the result of rewriting term under that alist, where this rewriting is as described for mfc-rw above. The function mfc-rw+ can be more efficient than mfc-rw, because the terms in the binding alist have generally already been rewritten, and it can be inefficient to rewrite them again. For example, consider a rewrite rule of the following form.

    (implies (and ...
                  (syntaxp (... (mfc-rw `(bar ,x) ...) ...))
                  ...)
             (equal (... x ...) ...))
    
    Here, x is bound in the conclusion to the result of rewriting some term, say, tm. Then the above call of mfc-rw will rewrite tm, which is probably unnecessary. So a preferable form of the rule above may be as follows, so that tm is not further rewritten by mfc-rw+.
    (implies (and ...
                  (syntaxp (... (mfc-rw+ '(bar v) `((v . ,x)) ...) ...))
                  ...)
             (equal (... x ...) ...))
    
    However, you may find that the additional rewriting done by mfc-rw is useful in some cases.

    (mfc-ap term mfc state): returns t or nil according to whether linear arithmetic can determine that term is false. To the cognoscenti: returns the contradiction flag produced by linearizing term and adding it to the linear-pot-lst.

    (mfc-relieve-hyp hyp alist rune target bkptr mfc state): returns t or nil according to whether the indicated hypothesis term, hyp, can be relieved (proved) under the giving variable bindings, alist. Note that this function returns nil if hyp has free variables (see free-variables). Here, hyp is the hypothesis of the indicated rune at (one-based) position bkptr, and target is an instantiated term to which rune is being applied. Note that no indication is returned for whether any assumptions have been generated by force or case-split. (If you need such a feature, feel free to request it of the ACL2 implementors.)

    We explain the :forcep and :ttreep keyword arguments provided in some expressions, as promised above. Their defaults are :same and nil, respectively. A value of :same for :forcep means that forcing will be allowed if and only if it is allowed in the current rewriting environment; see force. A value of t or nil for :forcep overrides this default and allows or disallows forcing, respectively. By default these functions return a single value, val, but if :ttreep is t then they return (mv val ttree), where ttree is the tag-tree (see ttree) returned by the indicated operation, with an input tag-tree of nil).

    During the execution of a metafunction by the theorem prover, the expressions above compute the results specified. Typically, you should imagine that there are no axioms about the mfc- function symbols: treat them as uninterpreted function symbols. There is an advanced feature, meta-extract hypotheses, that can avoid this logical weakness in some cases when proving :meta rules; see meta-extract. But we assume for the rest of the present documentation topic that you do not use meta-extract hypotheses. Thus, in the proof of the correctness of a metafunction, no information is available about the results of these functions: you should use these functions for heuristic purposes only.

    For example, your metafunction may use these functions to decide whether to perform a given transformation, but the transformation must be sound regardless of the value that your metafunction returns. We illustrate this below. It is sometimes possible to use the hypothesis metafunction, hyp-fn, to generate a sufficient hypothesis to justify the transformation. The generated hypothesis might have already been ``proved'' internally by your use of mfc-ts or mfc-rw, but the system will have to prove it ``officially'' by relieving it. We illustrate this below also.

    We conclude with a script that defines, verifies, and uses several extended metafunctions. This script is based on one provided by Robert Krug, who was instrumental in the development of this style of metafunction and whose help we gratefully acknowledge.

    
    ; Here is an example.  I will define (foo i j) simply to be (+ i j).
    ; But I will keep its definition disabled so the theorem prover
    ; doesn't know that.  Then I will define an extended metafunction
    ; that reduces (foo i (- i)) to 0 provided i has integer type and the
    ; expression (< 10 i) occurs as a hypothesis in the clause.
    
    ; Note that (foo i (- i)) is 0 in any case.
    
    (defun foo (i j) (+ i j))
    
    (defevaluator eva eva-lst ((foo i j)
                               (unary-- i)
    
    ; I won't need these two cases until the last example below, but I
    ; include them now.
    
                               (if x y z)
                               (integerp x)))
    
    (set-state-ok t)
    
    (defun metafn (x mfc state)
      (cond
       ((and (consp x)
             (equal (car x) 'foo)
             (equal (caddr x) (list 'unary-- (cadr x))))
    
    ; So x is of the form (foo i (- i)).  Now I want to check some other
    ; conditions.
    
        (cond ((and (ts-subsetp (mfc-ts (cadr x) mfc state)
                                *ts-integer*)
                    (member-equal `(NOT (< '10 ,(cadr x)))
                                  (mfc-clause mfc)))
               (quote (quote 0)))
              (t x)))
       (t x)))
    
    (defthm metafn-correct
      (equal (eva x a) (eva (metafn x mfc state) a))
      :rule-classes ((:meta :trigger-fns (foo))))
    
    (in-theory (disable foo))
    
    ; The following will fail because the metafunction won't fire.
    ; We don't know enough about i.
    
    (thm (equal (foo i (- i)) 0))
    
    ; Same here.
    
    (thm (implies (and (integerp i) (< 0 i)) (equal (foo i (- i)) 0)))
    
    ; But this will work.
    
    (thm (implies (and (integerp i) (< 10 i))
                  (equal (foo i (- i)) 0)))
    
    ; This won't, because the metafunction looks for (< 10 i) literally,
    ; not just something that implies it.
    
    (thm (implies (and (integerp i) (< 11 i))
                  (equal (foo i (- i)) 0)))
    
    ; Now I will undo the defun of metafn and repeat the exercise, but
    ; this time check the weaker condition that (< 10 i) is provable
    ; (by rewriting it) rather than explicitly present.
    
    (ubt 'metafn)
    
    (defun metafn (x mfc state)
      (cond
       ((and (consp x)
             (equal (car x) 'foo)
             (equal (caddr x) (list 'unary-- (cadr x))))
        (cond ((and (ts-subsetp (mfc-ts (cadr x) mfc state)
                                *ts-integer*)
                    (equal (mfc-rw `(< '10 ,(cadr x)) t t mfc state)
                           *t*))
    
    ; The mfc-rw above rewrites (< 10 i) with objective t and iffp t.  The
    ; objective means the theorem prover will try to establish it.  The
    ; iffp means the theorem prover can rewrite maintaining propositional
    ; equivalence instead of strict equality.
    
               (quote (quote 0)))
              (t x)))
       (t x)))
    
    (defthm metafn-correct
      (equal (eva x a) (eva (metafn x mfc state) a))
      :rule-classes ((:meta :trigger-fns (foo))))
    
    (in-theory (disable foo))
    
    ; Now it will prove both:
    
    (thm (implies (and (integerp i) (< 10 i))
                  (equal (foo i (- i)) 0)))
    
    (thm (implies (and (integerp i) (< 11 i))
                  (equal (foo i (- i)) 0)))
    
    ; Now I undo the defun of metafn and change the problem entirely.
    ; This time I will rewrite (integerp (foo i j)) to t.  Note that
    ; this is true if i and j are integers.  I can check this
    ; internally, but have to generate a hyp-fn to make it official.
    
    (ubt 'metafn)
    
    (defun metafn (x mfc state)
      (cond
       ((and (consp x)
             (equal (car x) 'integerp)
             (consp (cadr x))
             (equal (car (cadr x)) 'foo))
    
    ; So x is (integerp (foo i j)).  Now check that i and j are
    ; ``probably'' integers.
    
        (cond ((and (ts-subsetp (mfc-ts (cadr (cadr x)) mfc state)
                                *ts-integer*)
                    (ts-subsetp (mfc-ts (caddr (cadr x)) mfc state)
                                *ts-integer*))
               *t*)
              (t x)))
       (t x)))
    
    ; To justify this transformation, I generate the appropriate hyps.
    
    (defun hyp-fn (x mfc state)
    
      (declare (ignore mfc state))
    
    ; The hyp-fn is run only if the metafn produces an answer different
    ; from its input.  Thus, we know at execution time that x is of the
    ; form (integerp (foo i j)) and we know that metafn rewrote
    ; (integerp i) and (integerp j) both to t.  So we just produce their
    ; conjunction.  Note that we must produce a translated term; we
    ; cannot use the macro AND and must quote constants!  Sometimes you
    ; must do tests in the hyp-fn to figure out which case the metafn
    ; produced, but not in this example.
    
               `(if (integerp ,(cadr (cadr x)))
                    (integerp ,(caddr (cadr x)))
                    'nil))
    
    (defthm metafn-correct
      (implies (eva (hyp-fn x mfc state) a)
               (equal (eva x a) (eva (metafn x mfc state) a)))
      :rule-classes ((:meta :trigger-fns (integerp))))
    
    (in-theory (disable foo))
    
    ; This will not be proved.
    
    (thm (implies (and (rationalp x) (integerp i)) (integerp (foo i j))))
    
    ; But this will be.
    
    (thm (implies (and (rationalp x)
                       (integerp i)
                       (integerp j))
                  (integerp (foo i j))))
    
    




    acl2-sources/doc/HTML/EXTERNAL-FORMAT.html0000664002132200015000000000064312222333520017157 0ustar kaufmannacl2 EXTERNAL-FORMAT.html -- ACL2 Version 6.3

    EXTERNAL-FORMAT

    See character-encoding.
    Major Section:  IO
    




    acl2-sources/doc/HTML/EXTRA-INFO.html0000664002132200015000000000111712222333521016361 0ustar kaufmannacl2 EXTRA-INFO.html -- ACL2 Version 6.3

    EXTRA-INFO

    generate markers to indicate sources of guard proof obligations
    Major Section:  GUARD
    

    See guard-debug for a discussion of this function, which is useful for debugging failures during guard verification.




    acl2-sources/doc/HTML/E_slash_D.html0000664002132200015000000000330112222333531016564 0ustar kaufmannacl2 E_slash_D.html -- ACL2 Version 6.3

    E/D

    enable/disable rules
    Major Section:  THEORIES
    

    The macro e/d creates theory expressions for use in in-theory hints and events. It provides a convenient way to enable and disable simultaneously, without having to write arcane theory expressions.

    Examples:
    (e/d (lemma1 lemma2))          ; equivalent to (enable lemma1 lemma2)
    (e/d () (lemma))               ; equivalent to (disable lemma)
    (e/d (lemma1) (lemma2 lemma3)) ; Enable lemma1 then disable lemma2, lemma3.
    (e/d () (lemma1) (lemma2))     ; Disable lemma1 then enable lemma2.
    
    General Form:
    (e/d enables-0 disables-0 ... enables-n disables-n)
    
    where each enables-i and disables-i is a list of runic designators; see theories, see enable, and see disable.

    The e/d macro takes any number of lists suitable for the enable and disable macros, and creates a theory that is equal to (current-theory :here) after executing the following commands.

    (in-theory (enable . enables-0)) (in-theory (disable . disables-0)) [etc.] (in-theory (enable . enables-n)) (in-theory (disable . disables-n))




    acl2-sources/doc/HTML/Evaluating_App_on_Sample_Input.html0000664002132200015000000000240412222333526023025 0ustar kaufmannacl2 Evaluating_App_on_Sample_Input.html -- ACL2 Version 6.3

    Evaluating App on Sample Input

    ACL2 !>(app nil '(x y z))
    (X Y Z)
    
    ACL2 !>(app '(1 2 3) '(4 5 6 7))
    (1 2 3 4 5 6 7)
    
    ACL2 !>(app '(a b c d e f g) '(x y z))   ; click here for an explanation
    (A B C D E F G X Y Z)
    
    ACL2 !>(app (app '(1 2) '(3 4)) '(5 6))
    (1 2 3 4 5 6)
    
    ACL2 !>(app '(1 2) (app '(3 4) '(5 6)))
    (1 2 3 4 5 6)
    
    ACL2!>(let ((a '(1 2))
                (b '(3 4))
                (c '(5 6)))
            (equal (app (app a b) c)
                   (app a (app b c))))
    T
    

    As we can see from these examples, ACL2 functions can be executed more or less as Common Lisp.

    The last three examples suggest an interesting property of app.




    acl2-sources/doc/HTML/F-GET-GLOBAL.html0000664002132200015000000000266312222333523016516 0ustar kaufmannacl2 F-GET-GLOBAL.html -- ACL2 Version 6.3

    F-GET-GLOBAL

    get the value of a global variable in state
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (+ (f-get-global 'y state) 1)
    (f-put-global 'a
                  (aset1 'ascii-map-array
                         (f-get-global 'a state)
                         66
                         'Upper-case-B)
                  state)
    
    General Form:
    (f-get-global 'symbol state)
    
    where symbol is any symbol to which you have assigned a global value.

    The macro @ is closely related to f-get-global: (@ var) macroexpands to (f-get-global 'var state).

    The macro f-get-global makes it convenient to set the value of a symbol. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.




    acl2-sources/doc/HTML/F-PUT-GLOBAL.html0000664002132200015000000000350712222333523016545 0ustar kaufmannacl2 F-PUT-GLOBAL.html -- ACL2 Version 6.3

    F-PUT-GLOBAL

    assign to a global variable in state
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (f-put-global 'x (expt 2 10) state)
    (f-put-global 'a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B) state)
    
    General Form:
    (f-put-global (quote symbol) term state)
    
    where symbol is any symbol (with certain enforced exclusions to avoid overwriting ACL2 system ``globals'') and term is any ACL2 term that could be evaluated at the top-level. F-put-global evaluates the term, stores the result as the value of the given symbol in the global-table of state, and returns the new state. (Note: the actual implementation of the storage of this value is much more efficient than this discussion of the logic might suggest.)

    The macro assign is closely related to f-put-global: (assign var val) macroexpands to (f-put-global 'var val state).

    The macros @ and f-get-global give convenient access to the value of such globals. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.




    acl2-sources/doc/HTML/FAILED-FORCING.html0000664002132200015000000001765712222333520016775 0ustar kaufmannacl2 FAILED-FORCING.html -- ACL2 Version 6.3

    FAILED-FORCING

    how to deal with a proof failure in a forcing round
    Major Section:  MISCELLANEOUS
    

    See forcing-round for a background discussion of the notion of forcing rounds. When a proof fails during a forcing round it means that the ``gist'' of the proof succeeded but some ``technical detail'' failed. The first question you must ask yourself is whether the forced goals are indeed theorems. We discuss the possibilities below.

    If you believe the forced goals are theorems, you should follow the usual methodology for ``fixing'' failed ACL2 proofs, e.g., the identification of key lemmas and their timely and proper use as rules. See failure, see gag-mode, and see proof-tree.

    The rules designed for the goals of forcing rounds are often just what is needed to prove the forced hypothesis at the time it is forced. Thus, you may find that when the system has been ``taught'' how to prove the goals of the forcing round no forcing round is needed. This is intended as a feature to help structure the discovery of the necessary rules.

    If a hint must be provided to prove a goal in a forcing round, the appropriate ``goal specifier'' (the string used to identify the goal to which the hint is to be applied) is just the text printed on the line above the formula, e.g., "[1]Subgoal *1/3''". See goal-spec.

    If you solve a forcing problem by giving explicit hints for the goals of forcing rounds, you might consider whether you could avoid forcing the assumption in the first place by giving those hints in the appropriate places of the main proof. This is one reason that we print out the origins of each forced assumption. An argument against this style, however, is that an assumption might be forced in hundreds of places in the main goal and proved only once in the forcing round, so that by delaying the proof you actually save time.

    We now turn to the possibility that some goal in the forcing round is not a theorem.

    There are two possibilities to consider. The first is that the original theorem has insufficient hypotheses to ensure that all the forced hypotheses are in fact always true. The ``fix'' in this case is to amend the original conjecture so that it has adequate hypotheses.

    A more difficult situation can arise and that is when the conjecture has sufficient hypotheses but they are not present in the forcing round goal. This can be caused by what we call ``premature'' forcing.

    Because ACL2 rewrites from the inside out, it is possible that it will force hypotheses while the context is insufficient to establish them. Consider trying to prove (p x (foo x)). We first rewrite the formula in an empty context, i.e., assuming nothing. Thus, we rewrite (foo x) in an empty context. If rewriting (foo x) forces anything, that forced assumption will have to be proved in an empty context. This will likely be impossible.

    On the other hand, suppose we did not attack (foo x) until after we had expanded p. We might find that the value of its second argument, (foo x), is relevant only in some cases and in those cases we might be able to establish the hypotheses forced by (foo x). Our premature forcing is thus seen to be a consequence of our ``over eager'' rewriting.

    Here, just for concreteness, is an example you can try. In this example, (foo x) rewrites to x but has a forced hypothesis of (rationalp x). P does a case split on that very hypothesis and uses its second argument only when x is known to be rational. Thus, the hypothesis for the (foo x) rewrite is satisfied. On the false branch of its case split, p simplies to (p1 x) which can be proved under the assumption that x is not rational.

    (defun p1 (x) (not (rationalp x)))
    (defun p (x y)(if (rationalp x) (equal x y) (p1 x)))
    (defun foo (x) x)
    (defthm foo-rewrite (implies (force (rationalp x)) (equal (foo x) x)))
    (in-theory (disable foo))
    
    The attempt then to do (thm (p x (foo x))) forces the unprovable goal (rationalp x).

    Since all ``formulas'' are presented to the theorem prover as single terms with no hypotheses (e.g., since implies is a function), this problem would occur routinely were it not for the fact that the theorem prover expands certain ``simple'' definitions immediately without doing anything that can cause a hypothesis to be forced. See simple. This does not solve the problem, since it is possible to hide the propositional structure arbitrarily deeply. For example, one could define p, above, recursively so that the test that x is rational and the subsequent first ``real'' use of y occurred arbitrarily deeply.

    Therefore, the problem remains: what do you do if an impossible goal is forced and yet you know that the original conjecture was adequately protected by hypotheses?

    One alternative is to disable forcing entirely. See disable-forcing. Another is to disable the rule that caused the force.

    A third alternative is to prove that the negation of the main goal implies the forced hypothesis. For example,

    (defthm not-p-implies-rationalp
      (implies (not (p x (foo x))) (rationalp x))
      :rule-classes nil)
    
    Observe that we make no rules from this formula. Instead, we merely :use it in the subgoal where we must establish (rationalp x).
    (thm (p x (foo x))
         :hints (("Goal" :use not-p-implies-rationalp)))
    
    When we said, above, that (p x (foo x)) is first rewritten in an empty context we were misrepresenting the situation slightly. When we rewrite a literal we know what literal we are rewriting and we implicitly assume it false. This assumption is ``dangerous'' in that it can lead us to simplify our goal to nil and give up -- we have even seen people make the mistake of assuming the negation of what they wished to prove and then via a very complicated series of transformations convince themselves that the formula is false. Because of this ``tail biting'' we make very weak use of the negation of our goal. But the use we make of it is sufficient to establish the forced hypothesis above.

    A fourth alternative is to weaken your desired theorem so as to make explicit the required hypotheses, e.g., to prove

    (defthm rationalp-implies-main
      (implies (rationalp x) (p x (foo x)))
      :rule-classes nil)
    
    This of course is unsatisfying because it is not what you originally intended. But all is not lost. You can now prove your main theorem from this one, letting the implies here provide the necessary case split.
    (thm (p x (foo x))
         :hints (("Goal" :use rationalp-implies-main)))
    




    acl2-sources/doc/HTML/FAILURE.html0000664002132200015000000000644612222333520016045 0ustar kaufmannacl2 FAILURE.html -- ACL2 Version 6.3

    FAILURE

    how to deal with a proof failure
    Major Section:  MISCELLANEOUS
    

    When ACL2 gives up it does not mean that the submitted conjecture is invalid, even if the last formula ACL2 printed in its proof attempt is manifestly false. Since ACL2 sometimes generalizes the goal being proved, it is possible it adopted an invalid subgoal as a legitimate (but doomed) strategy for proving a valid goal. Nevertheless, conjectures submitted to ACL2 are often invalid and the proof attempt often leads the careful reader to the realization that a hypothesis has been omitted or that some special case has been forgotten. It is good practice to ask yourself, when you see a proof attempt fail, whether the conjecture submitted is actually a theorem.

    If you think the conjecture is a theorem, then you must figure out from ACL2's output what you know that ACL2 doesn't about the functions in the conjecture and how to impart that knowledge to ACL2 in the form of rules. The ``key checkpoint'' information printed at the end of the summary provides a fine place to start. See the-method for a general discussion of how to prove theorems with ACL2, and see introduction-to-the-theorem-prover for a more detailed tutorial. Also see set-gag-mode for discussion of key checkpoints and an abbreviated output mode that focuses attention on them. You may find it most useful to start by focusing on key checkpoints that are not under a proof by induction, if any, both because these are more likely to suggest useful lemmas and because they are more likely to be theorems; for example, generalization may have occurred before a proof by induction has begun. If you need more information than is provided by the key checkpoints -- although this should rarely be necessary -- then you can look at the full proof, perhaps with the aid of certain utilities: see proof-tree, see set-gag-mode, and see set-saved-output.

    For information on a tool to help debug failures of encapsulate and progn events, as well as certify-book failures, see redo-flat.

    Again, see the-method for a general discussion of how to prove theorems with ACL2, and see introduction-to-the-theorem-prover for a more detailed tutorial. See also the book ``Computer-Aided Reasoning: An Approach'' (Kaufmann, Manolios, Moore), as well as the discussion of how to read Nqthm proofs and how to use Nqthm rules in ``A Computational Logic Handbook'' by Boyer and Moore (Academic Press, 1988).

    If the failure occurred during a forcing round, see failed-forcing.




    acl2-sources/doc/HTML/FAST-ALIST-FREE-ON-EXIT.html0000664002132200015000000000327312222333517020166 0ustar kaufmannacl2 FAST-ALIST-FREE-ON-EXIT.html -- ACL2 Version 6.3

    FAST-ALIST-FREE-ON-EXIT

    Free a fast alist after the completion of some form.
    Major Section:  HONS-AND-MEMOIZATION
    

    Logically, (fast-alist-free-on-exit alist form) is the identity and returns form. Also see fast-alist-free.

    Under the hood, this essentially expands to:

     (prog1 form
            (fast-alist-free alist))
    

    In other words, alist is not freed immediately, but instead remains a fast alist until the form completes. This may be useful when you are writing code that uses a fast alist but has many return points.

    See also the macro fast-alists-free-on-exit defined in the community book "books/centaur/misc/hons-extra.lisp", which can be used to free several alists.

    The community book "books/centaur/misc/hons-extra.lisp" extends the b* macro (defined in the community book "books/tools/bstar.lisp") with the free-on-exit pattern binder. That is, after executing (include-book "centaur/misc/hons-extra.lisp" :dir :system), the form

     (b* (...
          ((free-on-exit a b c))
          ...)
       ...)
    

    causes a, b, and c to be freed when the b* completes, but they remain fast alists until then.




    acl2-sources/doc/HTML/FAST-ALIST-FREE.html0000664002132200015000000000307312222333517017103 0ustar kaufmannacl2 FAST-ALIST-FREE.html -- ACL2 Version 6.3

    FAST-ALIST-FREE

    (fast-alist-free alist) throws away the hash table associated with a fast alist.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, this function is the identity; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, fast-alist-free removes the hash table associated with this alist, if one exists. This effectively converts the alist into an ordinary alist.

    Also see fast-alist-free-on-exit.

    Because there is no automatic mechanism for freeing the hash tables used in fast alists, to avoid memory leaks you should manually free any alists that will no longer be used. You may find fast-alist-summary useful in tracking down alists that were not properly freed.

    It is safe to call fast-alist-free on any argument, including fast alists that have already been freed and objects which are not alists at all.




    acl2-sources/doc/HTML/FAST-ALIST-LEN.html0000664002132200015000000000222612222333517016777 0ustar kaufmannacl2 FAST-ALIST-LEN.html -- ACL2 Version 6.3

    FAST-ALIST-LEN

    (fast-alist-len alist) counts the number of unique keys in a fast alist.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically this function counts how many elements would remain in the alist were we to shrink it with hons-shrink-alist.

    Good discipline requires that the alist is a fast alist. Under the hood, when the alist is a fast alist we can simply call the underlying Common Lisp function hash-table-count on the associated hash table, which is very fast and doesn't require us to actually shrink the alist.




    acl2-sources/doc/HTML/FAST-ALIST-SUMMARY.html0000664002132200015000000000215012222333517017512 0ustar kaufmannacl2 FAST-ALIST-SUMMARY.html -- ACL2 Version 6.3

    FAST-ALIST-SUMMARY

    (fast-alist-summary) prints some basic statistics about any current fast alists.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, fast-alist-summary just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, this function gathers and prints some basic statistics about the current fast alists. It may be useful for identifying fast alists that should have been freed with fast-alist-free.




    acl2-sources/doc/HTML/FAST-ALISTS.html0000664002132200015000000000654112222333520016504 0ustar kaufmannacl2 FAST-ALISTS.html -- ACL2 Version 6.3

    FAST-ALISTS

    alists with hidden hash tables for faster execution
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    The implementation of fast alists is, in many ways, similar to that of ACL2 arrays. Logically, hons-acons is just like acons, and hons-get is similar to assoc-equal. But under the hood, hash tables are associated with these alists, and, when a certain discipline is followed, these functions execute with hash table speeds.

    What is this discipline? Each hons-acons operation "steals" the hash table associated with the alist that is being extended. Because of this, one must be very conscious of which object is the most recent extension of an alist and use that extension exclusively.

    The only penalty for a failure to keep track of the most recent extension is a loss of execution speed, not of correctness, and perhaps the annoyance of some slow-alist-warnings.

    Maintaining discipline may require careful passing of a fast alist up and down through function calls, as with any single threaded object in an applicative setting, but there is no syntactic enforcement that insists you only use the most recent extension of an alist as there is for single threaded objects (stobjs). Also, even with perfectly proper code, discipline can sometimes be lost due to user interrupts and aborts.

    Performance Notes

    See hons-acons for how the final cdr of a fast alist can be used as a size hint or as a name for reports printed by calling fast-alist-summary.

    The keys of fast alists are always normed. Why? In Common Lisp, equal-based hashing is relatively slow, so to allow the use of eql-based hash tables, hons-acons and hons-get always hons-copy the keys involved.

    Since alist keys are frequently atoms, this norming activity may often be so fast that you do not need to think about it. But if you are going to use large structures as the keys for your fast alist, this overhead can be significant, and you may want to ensure that your keys are normed ahead of time.

    There is no automatic mechanism for reclaiming the hash tables that are associated with alists. Because of this, to avoid memory leaks, you should call fast-alist-free to remove the hash table associated with alists that will no longer be used.




    acl2-sources/doc/HTML/FC-REPORT.html0000664002132200015000000000131012222333517016246 0ustar kaufmannacl2 FC-REPORT.html -- ACL2 Version 6.3

    FC-REPORT

    to report on the forward chaining activity in the most recent proof
    Major Section:  FORWARD-CHAINING-REPORTS
    

    Example: (fc-report 15)
    
    General Form: (fc-report k)
    
    where k is the number of some forward chaining report printed in the most recent event.

    See forward-chaining-reports for details.




    acl2-sources/doc/HTML/FIFTH.html0000664002132200015000000000066212222333523015613 0ustar kaufmannacl2 FIFTH.html -- ACL2 Version 6.3

    FIFTH

    fifth member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/FILE-READING-EXAMPLE.html0000664002132200015000000000561012222333515017631 0ustar kaufmannacl2 FILE-READING-EXAMPLE.html -- ACL2 Version 6.3

    FILE-READING-EXAMPLE

    example of reading files in ACL2
    Major Section:  TUTORIAL5-MISCELLANEOUS-EXAMPLES
    

    This example illustrates the use of ACL2's IO primitives to read the forms in a file. See io.

    This example provides a solution to the following problem. Let's say that you have a file that contains s-expressions. Suppose that you want to build a list by starting with nil, and updating it ``appropriately'' upon encountering each successive s-expression in the file. That is, suppose that you have written a function update-list such that (update-list obj current-list) returns the list obtained by ``updating'' current-list with the next object, obj, encountered in the file. The top-level function for processing such a file, returning the final list, could be defined as follows. Notice that because it opens a channel to the given file, this function modifies state and hence must return state. Thus it actually returns two values: the final list and the new state.

    
      (defun process-file (filename state)
        (mv-let
         (channel state)
         (open-input-channel filename :object state)
         (mv-let (result state)
                 (process-file1 nil channel state) ;see below
                 (let ((state (close-input-channel channel state)))
                   (mv result state)))))
    
    
    The function process-file1 referred to above takes the currently constructed list (initially, nil), together with a channel to the file being read and the state, and returns the final updated list. Notice that this function is tail recursive. This is important because many Lisp compilers will remove tail recursion, thus avoiding the potential for stack overflows when the file contains a large number of forms.
    
      (defun process-file1 (current-list channel state)
        (mv-let (eofp obj state)
                (read-object channel state)
                (cond
                 (eofp (mv current-list state))
                 (t (process-file1 (update-list obj current-list)
                                   channel state)))))
    
    

    As an exercise, you might want to add guards to the functions above and verify the guards (see verify-guards). See args or make a call of the form (guard 'your-function nil (w state)) to see the guard of an existing function.




    acl2-sources/doc/HTML/FINALIZE-EVENT-USER.html0000664002132200015000000001322612222333530017605 0ustar kaufmannacl2 FINALIZE-EVENT-USER.html -- ACL2 Version 6.3

    FINALIZE-EVENT-USER

    user-supplied code to complete events, e.g., with extra summary output
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This utility is intended for system hackers, not standard ACL2 users.

    ACL2 prints summaries at the conclusions of processing events (unless summaries are inhibited; see set-inhibit-output-lst and also see set-inhibited-summary-types). You may arrange for processing to take place just after the summary, by defining a function with argument list (ctx body state) that returns one value, namely state. We describe ctx and body at the end below, but you may simply prefer to ignore these arguments.) Your function should normally be a guard-verified :logic mode function with no guard other than that provided by the input requirement on state, that is, (state-p state); but later below we discuss how to avoid this requirement. You then attach (see defattach) your function to the function finalize-event-user. The following example illustrates how this all works.

    (defun finalize-event-user-test (ctx body state)
      (declare (xargs :stobjs state)
               (ignore ctx body))
      (cond ((and (boundp-global 'abbrev-evisc-tuple state)
                  (open-output-channel-p *standard-co*
                                         :character
                                         state))
             (pprogn
              (if (eq (f-get-global 'abbrev-evisc-tuple state) :DEFAULT)
                  (princ$ "Abbrev-evisc-tuple has its default value.~%"
                          *standard-co*
                          state)
                (princ$ "Abbrev-evisc-tuple has been modified.~%"
                        *standard-co*
                        state))))
            (t state)))
    
    (defattach finalize-event-user finalize-event-user-test)
    

    After admission of the two events above, an event summary will conclude with extra printout, for example:

    Note: Abbrev-evisc-tuple has its default value.
    

    If the attachment function (above, finalize-event-user-test) does not meet all the requirements stated above, then you can use the :skip-checks argument of defattach to get around the requirement, as illustrated by the following example.

    (defun finalize-event-user-test2 (state)
      (declare (xargs :stobjs state
                      :mode :program)
               (ignore ctx body))
      (observation
       'my-test
       "~|Value of term-evisc-tuple: ~x0~|"
       (f-get-global 'term-evisc-tuple state)))
    
    (defttag t) ; needed for :skip-checks t
    
    (defattach (finalize-event-user finalize-event-user-test2)
               :skip-checks t)
    
    So for example:
    ACL2 !>(set-term-evisc-tuple (evisc-tuple 2 7 nil nil) state)
     (:TERM)
    ACL2 !>(defconst *foo6* '(a b c))
    
    Summary
    Form:  ( DEFCONST *FOO6* ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    ACL2 Observation in MY-TEST:
    Value of term-evisc-tuple: (NIL 2 7 NIL)
     *FOO6*
    ACL2 !>
    

    Note that (as of this writing) the macro observation expands to a call of a :program-mode function. Thus, the trick shown above involving :skip-checks allows the use of :program-mode functions; for example, you can print with fmt.

    See community book books/misc/defattach-bang.lisp for a variant of defattach that uses ec-call to avoid issues of guard verification.

    Also see initialize-event-user, which discusses the handling of state globals by that utility as well as by finalize-event-user.

    Finally, as promised above, we briefly describe the arguments ctx and body. These are the arguments passed to the call of macro with-ctx-summarized under which finalize-event-user (or initialize-event-user) was called. Thus, they are unevaluated expressions. For example, system function defthm-fn1 has a body of the following form.

    (with-ctx-summarized
     (if (output-in-infixp state) event-form (cons 'defthm name))
     (let ((wrld (w state))
           (ens (ens state))
           .....
    
    Thus, when initialize-event-user and finalize-event-user are called on behalf of defthm, ctx is the s-expression
    (if (output-in-infixp state) event-form (cons 'defthm name))
    
    while body is the following s-expression (with most code elided).
     (let ((wrld (w state))
           (ens (ens state))
           .....
    

    You might find it helpful to use trace$ to get a sense of ctx and body, for example, (trace$ finalize-event-user).




    acl2-sources/doc/HTML/FIND-RULES-OF-RUNE.html0000664002132200015000000000371712222333520017435 0ustar kaufmannacl2 FIND-RULES-OF-RUNE.html -- ACL2 Version 6.3

    FIND-RULES-OF-RUNE

    find the rules named rune
    Major Section:  MISCELLANEOUS
    

    General Form:
    (find-rules-of-rune rune wrld)
    

    This function finds all the rules in wrld with :rune rune. It returns a list of rules in their internal form (generally as described by the corresponding defrec). Decyphering these rules is difficult since one cannot always look at a rule object and decide what kind of record it is without exploiting many system invariants (e.g., that :rewrite runes only name rewrite-rules).

    At the moment this function returns nil if the rune in question is a :refinement rune, because there is no object representing :refinement rules. (:refinement rules cause changes in the 'coarsenings properties.) In addition, if the rune is an :equivalence rune, then congruence rules with that rune will be returned -- because :equivalence lemmas generate some congruence rules -- but the fact that a certain function is now known to be an equivalence relation is not represented by any rule object and so no such rule is returned. (The fact that the function is an equivalence relation is encoded entirely in its presence as a 'coarsening of equal.)




    acl2-sources/doc/HTML/FINDING-DOCUMENTATION.html0000664002132200015000000000337112222333520020035 0ustar kaufmannacl2 FINDING-DOCUMENTATION.html -- ACL2 Version 6.3

    FINDING-DOCUMENTATION

    searching the documentation
    Major Section:  MISCELLANEOUS
    

    The :doc and :doc! commands will display, at the terminal, documentation topics defined in ACL2 or in books that have already been included. The :docs command allows you to search the documentation at the terminal. See docs.

    But how can you find documentation for books that are not included in the current ACL2 session?

    The xdoc manual is a combined manual for the ACL2 sources and the community books. It is built using documentation not only from the ACL2 sources (which is rearranged) but also from the community books (whether included in the current session or not), using an ``xdoc processor'' created by Jared Davis. The ACL2 home page (http://www.cs.utexas.edu/users/moore/acl2/) has a link to this manual, which as of this writing may be found directly by visiting the following web page: http://fv.centtech.com/acl2/latest/doc/. You can build a local copy of this manual from the ACL2 Community Books, following instructions in their Makefile.




    acl2-sources/doc/HTML/FIRST.html0000664002132200015000000000066212222333523015642 0ustar kaufmannacl2 FIRST.html -- ACL2 Version 6.3

    FIRST

    first member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/FIX-TRUE-LIST.html0000664002132200015000000000140412222333523016722 0ustar kaufmannacl2 FIX-TRUE-LIST.html -- ACL2 Version 6.3

    FIX-TRUE-LIST

    coerce to a true list
    Major Section:  ACL2-BUILT-INS
    

    Fix-true-list is the identity function on true-listp objects. It converts every list to a true list by dropping the final cdr, and it converts every atom to nil.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/FIX.html0000664002132200015000000000153712222333523015403 0ustar kaufmannacl2 FIX.html -- ACL2 Version 6.3

    FIX

    coerce to a number
    Major Section:  ACL2-BUILT-INS
    

    Fix simply returns any numeric argument unchanged, returning 0 on a non-numeric argument. Also see nfix, see ifix, and see rfix for analogous functions that coerce to a natural number, an integer, and a rational number, respectively.

    Fix has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/FLET.html0000664002132200015000000001330212222333523015500 0ustar kaufmannacl2 FLET.html -- ACL2 Version 6.3

    FLET

    local binding of function symbols
    Major Section:  ACL2-BUILT-INS
    

    Example Form:
    ; The following evaluates to (mv 7 10):
    (flet ((f (x)
              (+ x 3))
           (g (x)
              (declare (type integer x))
              (* x 2)))
      (mv (f 4) (g 5)))
    
    General Forms:
    (flet (def1 ... defk) body)
    (flet (def1 ... defk) declare-form1 .. declare-formk body)
    
    where body is a term, and each defi is a definition as in defun but with the leading defun symbol omitted. See defun. If any declare-formi are supplied, then each must be of the form (declare decl1 ... decln), where each decli is of the form (inline g1 ... gm) or (notinline g1 ... gm), and each gi is defined by some defi.

    The only effect of the declarations is to provide advice to the host Lisp compiler. The declarations are otherwise ignored by ACL2, so we mainly ignore them in the discussion below.

    The innermost flet-binding of a function symbol, f, above a call of f, is the one that provides the definition of f for that call. Note that flet does not provide recursion. Consider the following example.

    ; Give a global definition of f:
    (defun f (x) (+ x 3))
    ; Evaluate an expression using a local binding of f:
    (flet ((f (x) (cons x (f (1+ x)))))
      (f 4))
    
    In the above term (cons x (f (1+ x))), f refers to the global definition of f above the flet expression. However, (f 4) refers to the flet-binding of f, (f (x) (cons x (f x))). The result of the flet expression is thus obtained by evaluating (f 4) where (f 4) is (cons 4 (f (1+ 4))), where the latter call of f refers to the global definition; thus we have (cons 4 (f 5)), which evaluates to (4 . 8).

    Although flet behaves in ACL2 essentially as it does in Common Lisp, ACL2 imposes the following restrictions and qualifications.

    o Every declare form for a local definition (def1 through defk, above) must be an ignore, ignorable, or type expression.

    o Each defi must bind a different function symbol.

    o Each defi must bind a symbol that is a legal name for an ACL2 function symbol. In particular, the symbol may not be in the keyword package or the main Lisp package. Moreover, the symbol may not be a built-in ACL2 function or macro.

    o Every variable occurring in the body of a defi must be a formal parameter of that defi. (This restriction is not enforced in Common Lisp. If the restriction is inconvenient for you, the ACL2 implementors may be able to remove it, with some effort, if you ask.)

    o If the flet-binding defi is in the body of a function f, then the stobj inputs for defi are implicitly those of its inputs that are declared stobj inputs of f.

    Flet bindings are evaluated in parallel. Consider the following example.

    (defun f (x) x)
    (flet ((f (x) (cons x x))
           (g (x) (f x)))
      (g 3))
    
    The binding of g refers to the global value of f, not the flet-binding of f. Thus, the flet expression evaluates to 3. Compare the flet expression above to the following one, which instead evaluates to (3 . 3).
    (defun f (x) x)
    (flet ((f (x) (cons x x)))
      (flet ((g (x) (f x)))
        (g 3)))
    

    Under the hood, ACL2 translates flet bindings to lambda expressions (see term), throwing away the inline and notinline declarations (if any). The following example illustrates this point.

    ACL2 !>:trans (flet ((f (x) (cons x x))
                         (g (x y) (+ x y)))
                    (declare (inline f))
                    (f (g 3 4)))
    
    ((LAMBDA (X) (CONS X X))
     ((LAMBDA (X Y) (BINARY-+ X Y)) '3 '4))
    
    => *
    
    ACL2 !>
    

    Flet is part of Common Lisp. See any Common Lisp documentation for more information. We conclude by pointing out an important aspect of flet shared by ACL2 and Common Lisp: The binding is lexical, not dynamic. That is, the flet binding of a function symbol only applies to calls of that function symbol in the body of the flet, not other calls made in the course of evaluation. Consider the following example. Suppose we define:

    (defun f (x) x)
    (defun g (x) x)
    (defun h (x)
      (flet ((f (x) (cons x x)))
        (g x)))
    
    Then evaluation of (h 3) results in 3, not in the cons pair (3 . 3), because the flet binding of f only applies to calls of f that appear in the body of that flet. In this case, only g is called in the body of that flet.




    acl2-sources/doc/HTML/FLOOR.html0000664002132200015000000000263012222333523015631 0ustar kaufmannacl2 FLOOR.html -- ACL2 Version 6.3

    FLOOR

    division returning an integer by truncating toward negative infinity
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    ACL2 !>(floor 14 3)
    4
    ACL2 !>(floor -14 3)
    -5
    ACL2 !>(floor 14 -3)
    -5
    ACL2 !>(floor -14 -3)
    4
    ACL2 !>(floor -15 -3)
    5
    
    (Floor i j) returns the result of taking the quotient of i and j and returning the greatest integer not exceeding that quotient. For example, the quotient of -14 by 3 is -4 2/3, and the largest integer not exceeding that rational number is -5.

    The guard for (floor i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero.

    Floor is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 floor function returns only a single value,

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/FLUSH-COMPRESS.html0000664002132200015000000001256612222333525017075 0ustar kaufmannacl2 FLUSH-COMPRESS.html -- ACL2 Version 6.3

    FLUSH-COMPRESS

    flush the under-the-hood array for the given name
    Major Section:  ARRAYS
    

    Example Form:
    (flush-compress 'my-array)
    
    General Form:
    (flush-compress name)
    
    where name is a symbol.

    Recall that (compress1 nm alist) associates an under-the-hood raw Lisp one-dimensional array of name nm with the given association list, alist, while (compress2 nm alist) is the analogous function for two-dimensional arrays; see compress1 and see compress2. The only purpose of flush-compress, which always returns nil, is to remove the association of any under-the-hood array with the given name, thus eliminating slow array accesses (see slow-array-warning). It is not necessary if the return values of compress1 and compress2 are always used as the ``current'' copy of the named array, and thus flush-compress should rarely, if ever, be needed in user applications.

    Nevertheless, we provide the following contrived example to show how flush-compress can be used to good effect. Comments have been added to this log to provide explanation.

    ACL2 !>(assign a (compress1 'demo
                                '((:header :dimensions (5)
                                           :maximum-length 15
                                           :default uninitialized
                                           :name demo)
                                  (0 . zero)
                                  (1 . one))))
     ((:HEADER :DIMENSIONS (5)
               :MAXIMUM-LENGTH
               15 :DEFAULT UNINITIALIZED :NAME DEMO)
      (0 . ZERO)
      (1 . ONE))
    ACL2 !>(aref1 'demo (@ a) 0)
    ZERO
    ; As expected, the above evaluation did not cause a slow array warning.  Now
    ; we associate a different under-the-hood array with the name 'demo.
    ACL2 !>(compress1 'demo
                      '((:header :dimensions (5)
                                 :maximum-length 15
                                 :default uninitialized
                                 :name demo)
                        (0 . zero)))
    ((:HEADER :DIMENSIONS (5)
              :MAXIMUM-LENGTH
              15 :DEFAULT UNINITIALIZED :NAME DEMO)
     (0 . ZERO))
    ; The following array access produces a slow array warning because (@ a) is
    ; no longer associated under-the-hood with the array name 'demo.
    ACL2 !>(aref1 'demo (@ a) 0)
    
    
    **********************************************************
    Slow Array Access!  A call of AREF1 on an array named
    DEMO is being executed slowly.  See :DOC slow-array-warning
    **********************************************************
    
    ZERO
    ; Now we associate under-the-hood, with array name 'demo, an alist equal to
    ; (@ a).
    ACL2 !>(compress1 'demo
                      '((:header :dimensions (5)
                                 :maximum-length 15
                                 :default uninitialized
                                 :name demo)
                        (0 . zero)
                        (1 . one)))
    ((:HEADER :DIMENSIONS (5)
              :MAXIMUM-LENGTH
              15 :DEFAULT UNINITIALIZED :NAME DEMO)
     (0 . ZERO)
     (1 . ONE))
    ; The following array access is still slow, because the under-the-hood array
    ; is merely associated with a copy of (@ a), not with the actual object
    ; (@ a).
    ACL2 !>(aref1 'demo (@ a) 0)
    
    
    **********************************************************
    Slow Array Access!  A call of AREF1 on an array named
    DEMO is being executed slowly.  See :DOC slow-array-warning
    **********************************************************
    
    ZERO
    ; So we might try to fix the problem by recompressing. But this doesn't
    ; work.  It would work, by the way, if we re-assign a:
    ; (assign a (compress1 'demo (@ a))).  That is why we usually will not need
    ; flush-compress.
    ACL2 !>(compress1 'demo (@ a))
    ((:HEADER :DIMENSIONS (5)
              :MAXIMUM-LENGTH
              15 :DEFAULT UNINITIALIZED :NAME DEMO)
     (0 . ZERO)
     (1 . ONE))
    ACL2 !>(aref1 'demo (@ a) 0)
    
    
    **********************************************************
    Slow Array Access!  A call of AREF1 on an array named
    DEMO is being executed slowly.  See :DOC slow-array-warning
    **********************************************************
    
    ZERO
    ; Finally, we eliminate the warning by calling flush-compress before we call
    ; compress1.  The call of flush-compress removes any under-the-hood
    ; association of an array with the name 'demo.  Then the subsequent call of
    ; compress1 associates the object (@ a) with that name.  (Technical point:
    ; compress1 always associates the indicated name with the value that it
    ; returns.  in this case, what compress1 returns is (@ a), because (@ a) is
    ; already, logically speaking, a compressed array1p (starts with a :header
    ; and the natural number keys are ordered).
    ACL2 !>(flush-compress 'demo)
    NIL
    ACL2 !>(compress1 'demo (@ a))
    ((:HEADER :DIMENSIONS (5)
              :MAXIMUM-LENGTH
              15 :DEFAULT UNINITIALIZED :NAME DEMO)
     (0 . ZERO)
     (1 . ONE))
    ACL2 !>(aref1 'demo (@ a) 0)
    ZERO
    ACL2 !>
    




    acl2-sources/doc/HTML/FLUSH-HONS-GET-HASH-TABLE-LINK.html0000664002132200015000000000136712222333520021117 0ustar kaufmannacl2 FLUSH-HONS-GET-HASH-TABLE-LINK.html -- ACL2 Version 6.3

    FLUSH-HONS-GET-HASH-TABLE-LINK

    deprecated feature
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Deprecated. Alias for fast-alist-free.




    acl2-sources/doc/HTML/FMS-TO-STRING.html0000664002132200015000000000067112222333523016724 0ustar kaufmannacl2 FMS-TO-STRING.html -- ACL2 Version 6.3

    FMS-TO-STRING

    See printing-to-strings.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/FMS.html0000664002132200015000000000101412222333523015370 0ustar kaufmannacl2 FMS.html -- ACL2 Version 6.3

    FMS

    :(str alist co-channel state evisc) => state
    Major Section:  ACL2-BUILT-INS
    

    See fmt for further explanation, including documentation of the tilde-directives.




    acl2-sources/doc/HTML/FMS_bang_-TO-STRING.html0000664002132200015000000000070012222333523020043 0ustar kaufmannacl2 FMS_bang_-TO-STRING.html -- ACL2 Version 6.3

    FMS!-TO-STRING

    See printing-to-strings.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/FMS_bang_.html0000664002132200015000000000140712222333523016524 0ustar kaufmannacl2 FMS_bang_.html -- ACL2 Version 6.3

    FMS!

    :(str alist co-channel state evisc) => state
    Major Section:  ACL2-BUILT-INS
    

    This function is nearly identical to fms; see fms. The only difference is that fms may insert backslash (\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use fms! instead if you want to be able to read the forms back in.




    acl2-sources/doc/HTML/FMT-TO-COMMENT-WINDOW.html0000664002132200015000000000323012222333523020020 0ustar kaufmannacl2 FMT-TO-COMMENT-WINDOW.html -- ACL2 Version 6.3

    FMT-TO-COMMENT-WINDOW

    print to the comment window
    Major Section:  ACL2-BUILT-INS
    

    See cw for an introduction to the comment window and the usual way to print it.

    Function fmt-to-comment-window is identical to fmt1 (see fmt), except that the channel is *standard-co* and the ACL2 state is neither an input nor an output. An analogous function, fmt-to-comment-window!, prints with fmt! instead of fmt, in order to avoid insertion of backslash (\) characters for margins; also see cw!. Note that even if you change the value of ld special standard-co (see standard-co), fmt-to-comment-window will print to *standard-co*, which is the original value of standard-co.

    General Form:
    (fmt-to-comment-window fmt-string alist col evisc-tuple)
    
    where these arguments are as desribed for fmt1; see fmt.




    acl2-sources/doc/HTML/FMT-TO-STRING.html0000664002132200015000000000067112222333523016725 0ustar kaufmannacl2 FMT-TO-STRING.html -- ACL2 Version 6.3

    FMT-TO-STRING

    See printing-to-strings.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/FMT.html0000664002132200015000000005156112222333523015405 0ustar kaufmannacl2 FMT.html -- ACL2 Version 6.3

    FMT

    formatted printing
    Major Section:  ACL2-BUILT-INS
    

    ACL2 provides the functions fmt, fmt1, and fms as substitutes for Common Lisp's format function. Also see fmt!, see fmt1!, and see fms! for versions of these functions that write forms to files in a manner that allows them to be read, by avoiding using backslash (\) to break long lines. There are also analogues of these functions that return a string without taking state as an argument; see printing-to-strings.

    All three print a given string under an alist pairing character objects with values, interpreting certain ``tilde-directives'' in the string. Channel must be a character output channel (e.g., *standard-co*).

    General Forms:                                            result
    (fms string alist channel state evisc-tuple)         ; state
    (fmt string alist channel state evisc-tuple)         ; (mv col state)
    (fmt1 string alist column channel state evisc-tuple) ; (mv col state)
    
    Fms and fmt print an initial newline to put channel in column 0; Fmt1 requires the current column as input. Columns are numbered from 0. The current column is the column into which the next character will be printed. (Thus, the current column number is also the number of characters printed since the last newline.) The col returned by fmt and fmt1 is the current column at the conclusion of the formatting. Evisc-tuple must be either nil (meaning no abbreviations are used when objects are printed) or an ``evisceration tuple''; see evisc-tuple.

    We list the tilde-directives below. The notation is explained after the chart.

    ~xx  pretty print vx (maybe after printing a newline)
    ~yx  pretty print vx starting in current column; end with newline
    ~Xxy like ~xx but use vy as the evisceration tuple
    ~Yxy like ~yx but use vy as the evisceration tuple
    ~@x  if vx is a string, "str",  recursively format "str"
         if vx is ("str" . a), recursively format "str" under a+
    ~#x~[...~/...~/ ... ~/...~] cases on vx
         ^    ^     ...   ^  if 0<=vx<=k, choose vxth alternative
         0    1     ...   k  if vx is a list of length 1, case 0; else 1
    ~*x  iterator: vx must be of the form
         ("str0" "str1" "str2" "str3" lst . a);
         if lst is initially empty, format "str0" under a+; otherwise,
         bind #\* successively to the elements of lst and then
         recursively format "stri" under a+, where i=1 if there is one
         element left to process, i=2 if there are two left, and i=3
         otherwise.
    ~&x  print elements of vx with ~x, separated by commas and a
         final ``and''
    ~vx  print elements of vx with ~x, separated by commas and a
         final ``or''
    ~nx  if vx is a small positive integer, print it as a word, e.g.,
         seven;
         if vx is a singleton containing a small positive integer, print
           the corresponding ordinal as a word, e.g., seventh
    ~Nx  like ~nx but the word is capitalized, e.g., Seven or Seventh
    ~tx  tab out to column vx; newline first if at or past column vx
    ~cx  vx is (n . w), print integer n right justified in field of
         width w
    ~fx  print object vx flat over as many lines as necessary
    ~Fx  same as ~f, except that subsequent lines are indented to
         start one character to the right of the first character printed
    ~sx  if vx is a symbol, print vx, breaking on hyphens (unless the
         symbol would normally be printed with surrounding vertical bar
         characters (|), in which case print as with ~fx); if vx is a
         string, print the characters in it, breaking on hyphens; else
         vx is a number, to be printed using the current print-base and
         print-radix
    ~    tilde space: print a space
    ~_x  print vx spaces
    ~
         tilde newline: skip following whitespace
    ~%   output a newline
    ~|   output a newline unless already on left margin
    ~~   print a tilde
    ~-   if close to rightmargin, output a hyphen and newline; else
         skip this char
    
    If x is a character, then vx is the value of #\x under the current alist. Consider for example the discussion above for ~y, ``~yx pretty print vx'', applied to the following expression: (fmt "HELLO ~y7" (list (cons #\7 'world)) *standard-co* state nil). Then in this example: #\x is 7; and vx is the value of character #\7 under the given alist, which is the symbol, WORLD. Thus, ACL2 will print HELLO WORLD. When we say ``format str under a+'' we mean: process the given string under an alist obtained by appending a to the current alist. The following example illustrates how this works.
    ACL2 !>(fms "~@0"
                (list (cons #\0 (cons "~x0 ~@1" (list (cons #\0  'abc))))
                      (cons #\1 "-- and now: ~x0 again~%"))
                *standard-co* state nil)
    
    ABC -- and now: ABC again
    <state>
    ACL2 !>
    

    Note: ~p, ~q, ~P, and ~Q are also currently supported, but are deprecated. These are respectively the same as ~x, ~y, ~X, and ~Y, except that their arguments are expected to be terms, preferably untranslated (user-level) terms, that could be printed using infix notation in certain environments. Infix printing is not currently supported but may be if there is sufficient need for it.

    ACL2's formatting functions print to the indicated channel, keeping track of which column they are in. Fmt1 can be used if the caller knows which column the channel is in (i.e., how many characters have been printed since the last newline). Otherwise, fmt or fms must be used, both of which output a newline so as to establish the column position at 0. Unlike Common Lisp's format routine, fmt and its relatives break the output into lines so that, by default, an attempt is made to avoid printing past column 77 (the value of constant *fmt-hard-right-margin-default*). See set-fmt-hard-right-margin for a discussion of how linebreaks are inserted and how to change the relevant default settings.

    The formatting functions scan the string from left to right, printing each successive character unless it is a tilde (~). Upon encountering tildes the formatters take action determined by the character or characters immediately following the tilde. The typical tilde-directive is a group of three successive characters from the string being printed. For example, ~x0 is a 3 character tilde-directive. The first character in a tilde-directive is always the tilde character itself. The next character is called the ``command'' character. The character after that is usually taken as the name of a ``format variable'' that is bound in the alist under which the string is being printed. Format variables are, by necessity, characters. The objects actually printed by a tilde-directive are the objects obtained by looking up the command's format variables in the alist. Typical format variable names are 0, 1, 2, ..., 9, a, b, c, etc., and if a tilde-directive uses the format variable 0, as in ~x0, then the character #\0 must be bound in the alist. Some tilde commands take no arguments and others take more than one, so some directives are of length two and others are longer.

    It should be noted that this use of characters in the string to denote arguments is another break from Common Lisp's format routine. In Common Lisp, the directives refer implicitly to the ``next item to be printed.'' But in ACL2 the directives name each item explicitly with our format variables.

    The following text contains examples that can be evaluated. To make this process easier, we use a macro which is defined as part of ACL2 just for this documentation. The macro is named fmx and it takes up to eleven arguments, the first of which is a format string, str, and the others of which are taken as the values of format variables. The variables used are #\0 through #\9. The macro constructs an appropriate alist, a, and then evaluates (fmt str a *standard-co* state nil).

    Thus,

    (fmx "Here is v0, ~x0, and here is v1, ~x1."
         (cons 'value 0)
         (cons 'value 1))
    
    is just an abbreviation for
    (fmt "Here is v0, ~x0, and here is v1, ~x1."
         (list (cons #\0 (cons 'value 0))
               (cons #\1 (cons 'value 1)))
         *standard-co*
         state
         nil)
    
    which returns (mv 53 state) after printing the line
       Here is v0, (VALUE . 0), and here is v1, (VALUE . 1).
    
    We now devote special attention to three of the tilde-directives whose use is non-obvious.

    The Case Statement

    ~#x is essentially a ``case statement'' in the language of fmt. The proper form of the statement is

    ~#x~[case-0~/case-1~/ ... ~/case-k~],
    
    where each of the case-i is a format string. In the most common use, the variable x has an integer value, vx, between 0 and k, inclusive. The effect of formatting the directive is to format case-vx.

    For example

    (fmx "Go ~#0~[North~/East~/South~/West~].~%" 1)
    
    will print ``Go East.'' followed by a newline and will return

    (mv 0 state), while if you change the 1 above to 3 (the maximum legal value), it will print ``Go West.''

    In order to make it easier to print such phrases as ``there are seven cases'' requiring agreement between subject and verb based on the number of elements of a list, the case statement allows its variable to take a list as its value and selects case-0 if the list has length 1 and case-1 otherwise.

    (let ((cases '(a b c)))
      (fmx "There ~#0~[is ~n1 case~/are ~n1 cases~]."
           cases
           (length cases)))
    
    will print ``There are three cases.'' but if you change the

    '(a b c) above simply to '(a) it will print ``There is one case.'' and if you change it to nil it will print ``There are zero cases.''

    Indirection

    Roughly speaking, ~@ will act as though the value of its argument is a format string and splice it into the current string at the current position. It is often used when the phrase to be printed must be computed. For example,

    (let ((ev 'DEFUN))
     (fmx "~x0 is an event~@1."
          'foo
          (if (member-eq ev '(defun defstub encapsulate))
              " that may introduce a function symbol"
              "")))
    
    will print ``foo is an event that may introduce a function symbol,'' but if the value of ev is changed from 'defun to 'defthm, it prints ``foo is an event.'' The ~@ directive ``splices'' in the computed phrase (which might be empty). Of course, this particular example could be done with the case statement
    ~#1~[~/ that may introduce a function symbol~]
    
    where the value of #\1 is appropriately computed to be 0 or 1.

    If the argument to ~@ is a pair, it is taken to be a format string consed onto an alist, i.e., ("str" . a), and the alist, a, is used to extend the current one before "str" is recursively processed. This feature of fmt can be used to pass around ``phrases'' that contain computed contextual information in a. The most typical use is as ``error messages.'' For example, suppose you are writing a function which does not have access to state and so cannot print an error message. It may nevertheless be necessary for it to signal an error to its caller, say by returning two results, the first of which is interpreted as an error message if non-nil. Our convention is to use a ~@ pair to represent such messages. For example, the error value might be produced by the code:

    (cons
      "Error:  The instruction ~x0 is illegal when the stack is ~x1.~%"
      (list (cons #\0 (current-instruction st))
            (cons #\1 (i-stack st))))
    
    If the current-instruction and i-stack (whatever they are) are '(popi 3) and '(a b) when the cons above is evaluated, then it produces
    '("Error:  The instruction ~x0 is illegal when the stack is ~x1.~%"
      (#\0 POPI 3)
      (#\1 A B))
    
    and if this pair is made the value of the fmt variable 0, then ~@0 will print
       Error:  The instruction (POPI 3) is illegal when the stack is (A B).
    
    For example, evaluate
    (let
     ((pair
      '("Error:  The instruction ~x0 is illegal when the stack is ~x1.~%"
        (#\0 POPI 3)
        (#\1 A B))))
     (fmx "~@0" pair)).
    
    Thus, even though the function that produced the ``error'' could not print it, it could specify exactly what error message and data are to be printed.

    This example raises another issue. Sometimes it is desirable to break lines in your format strings so as to make your source code more attractive. That is the purpose of the tilde-newline directive. The following code produces exactly the same output as described above.

    (let ((pair '("Error:  The instruction ~x0 ~
                  is illegal when the stack is ~
                  ~x1.~%"
                  (#\0 POPI 3)
                  (#\1 A B))))
     (fmx "~@0" pair)).
    
    Finally, observe that when ~@0 extends the current alist, alist, with the one, a, in its argument, the bindings from a are added to the front of alist, overriding the current values of any shared variables. This ensures that the variable values seen by the recursively processed string, "str", are those from a, but if "str" uses variables not bound in a, their values are as specified in the original alist. Intuitively, variables bound in a are local to the processing of ("str" . a) but "str" may use ``global variables.'' The example above illustrates this because when the ~@0 is processed, #\0 is bound to the error message pair. But when the ~x0 in the error string is processed, #\0 is bound to the illegal instruction.

    Iteration

    The ~* directive is used to process each element of a list. For example,

    (let ((lst '(a b c d e f g h))) ; a true-list whose elements we exhibit
     (fmx "~*0"
          `("Whoa!"          ; what to print if there's nothing to print
            "~x*!"           ; how to print the last element
            "~x* and "       ; how to print the 2nd to last element
            "~x*, "          ; how to print all other elements
            ,lst)))          ; the list of elements to print
    
    will print ``A, B, C, D, E, F, G and H!''. Try this example with other true list values of lst, such as '(a b), '(a), and nil. The tilde-directives ~&0 and ~v0, which take a true list argument and display its elements separated by commas and a final ``and'' or ``or,'' are implemented in terms of the more general ~*.

    The ~* directive allows the 5-tuple to specify in its final cdr an alist with which to extend the current one before processing the individual elements.

    We often use ~* to print a series of phrases, separated by suitable punctuation, whitespace and noise words. In such use, the ~* handles the separation of the phrases and each phrase is generally printed by ~@.

    Here is a complex example. In the let*, below, we bind phrases to a list of ~@ pairs and then we create a ~* 5-tuple to print out the conjunction of the phrases with a parenthetical ``finally!'' if the series is longer than 3.

    (let* ((phrases
            (list (list "simplifying with the replacement rules ~&0"
                        (cons #\0 '(rewrite-rule1
                                    rewrite-rule2
                                    rewrite-rule3)))
                  (list "destructor elimination using ~x0"
                        (cons #\0 'elim-rule))
                  (list "generalizing the terms ~&0"
                        (cons #\0 '((rev x) (app u v))))
                  (list "inducting on ~x0"
                        (cons #\0 'I))))
           (5-tuple
            (list
             "magic"                            ; no phrases
             "~@*"                              ; last phrase
             "~@*, and~#f~[~/ (finally!)~] "    ; second to last phrase
             "~@*, "                            ; other phrases
             phrases                            ; the phrases themselves
             (cons #\f
                   (if (>(length phrases) 3) 1 0))))) ;print ``finally''?
      (fmx "We did it by ~*0." 5-tuple))
    
    This let* prints
       We did it by simplifying with the replacement rules REWRITE-RULE1,
       REWRITE-RULE2 and REWRITE-RULE3, destructor elimination using ELIM-
       RULE, generalizing the terms (REV X) and (APP U V), and (finally!)
       inducting on I.
    
    You might wish to try evaluating the let* after removing elements of phrases.

    Most of the output produced by ACL2 is produced via fmt statements. Thus, inspection of the source code will yield many examples. A complicated example is the code that explains the simplifier's work. See :pc simplify-clause-msg1. An ad hoc example is provided by the function fmt-doc-example, which takes two arguments: an arbitrary true list and state. To see how fmt-doc-example works, :pe fmt-doc-example.

    (fmt-doc-example '(a b c d e f g h i j k l m n o p) state)
    
    will produce the output
       Here is a true list:  (A B C D E F G H I J K L M N O P).  It has 16
       elements, the third of which is C.
    
       We could print each element in square brackets:
       ([A], [B], [C], [D], [E], [F], [G], [H], [I], [J], [K], [L], [M], [N],
       [almost there: O], [the end: P]).  And if we wished to itemize them
       into column 15 we could do it like this
       0123456789012345
           0 (zeroth) A
           1 (first)  B
           2 (second) C
           3 (third)  D
           4 (fourth) E
           5 (fifth)  F
           6 (sixth)  G
           7 (seventh)
                      H
           8 (eighth) I
           9 (ninth)  J
          10 (tenth)  K
          11 (eleventh)
                      L
          12 (twelfth)
                      M
          13 (thirteenth)
                      N
          14 (14th)   O
          15 (15th)   P
       End of example.
    
    and return (mv 15 state).

    Finally, we should remind the reader that fmt and its subfunctions, most importantly fmt0, are written entirely in ACL2. We make this comment for two reasons. First, it illustrates the fact that quite low level code can be efficiently written in the language. Second, it means that as a last resort for documentation purposes you can read the source code without changing languages.




    acl2-sources/doc/HTML/FMT1-TO-STRING.html0000664002132200015000000000067312222333523017010 0ustar kaufmannacl2 FMT1-TO-STRING.html -- ACL2 Version 6.3

    FMT1-TO-STRING

    See printing-to-strings.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/FMT1.html0000664002132200015000000000103312222333523015453 0ustar kaufmannacl2 FMT1.html -- ACL2 Version 6.3

    FMT1

    :(str alist col co-channel state evisc) => (mv col state)
    Major Section:  ACL2-BUILT-INS
    

    See fmt for further explanation, including documentation of the tilde-directives.




    acl2-sources/doc/HTML/FMT1_bang_-TO-STRING.html0000664002132200015000000000070212222333523020127 0ustar kaufmannacl2 FMT1_bang_-TO-STRING.html -- ACL2 Version 6.3

    FMT1!-TO-STRING

    See printing-to-strings.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/FMT1_bang_.html0000664002132200015000000000143012222333523016602 0ustar kaufmannacl2 FMT1_bang_.html -- ACL2 Version 6.3

    FMT1!

    :(str alist col channel state evisc) => (mv col state)
    Major Section:  ACL2-BUILT-INS
    

    This function is nearly identical to fmt1; see fmt1. The only difference is that fmt1 may insert backslash (\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use fmt1! instead if you want to be able to read the forms back in.




    acl2-sources/doc/HTML/FMT_bang_-TO-STRING.html0000664002132200015000000000070012222333523020044 0ustar kaufmannacl2 FMT_bang_-TO-STRING.html -- ACL2 Version 6.3

    FMT!-TO-STRING

    See printing-to-strings.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/FMT_bang_.html0000664002132200015000000000140712222333523016525 0ustar kaufmannacl2 FMT_bang_.html -- ACL2 Version 6.3

    FMT!

    :(str alist co-channel state evisc) => state
    Major Section:  ACL2-BUILT-INS
    

    This function is nearly identical to fmt; see fmt. The only difference is that fmt may insert backslash (\) characters when forced to print past the right margin in order to make the output a bit clearer in that case. Use fmt! instead if you want to be able to read the forms back in.




    acl2-sources/doc/HTML/FNCALL-TERM.html0000664002132200015000000000064512222333520016455 0ustar kaufmannacl2 FNCALL-TERM.html -- ACL2 Version 6.3

    FNCALL-TERM

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/FORALL.html0000664002132200015000000000150512222333517015732 0ustar kaufmannacl2 FORALL.html -- ACL2 Version 6.3

    FORALL

    universal quantifier
    Major Section:  DEFUN-SK
    

    The symbol forall (in the ACL2 package) represents universal quantification in the context of a defun-sk form. See defun-sk and see exists.

    See quantifiers for an example illustrating how the use of recursion, rather than explicit quantification with defun-sk, may be preferable.




    acl2-sources/doc/HTML/FORCE.html0000664002132200015000000002133712222333521015611 0ustar kaufmannacl2 FORCE.html -- ACL2 Version 6.3

    FORCE

    identity function used to force a hypothesis
    Major Section:  MISCELLANEOUS
    

    Force is the identity function: (force x) is equal to x. However, for rules of many classes (see rule-classes), a hypothesis of the form (force term) is given special treatment, as described below. This treatment takes place for rule classes :rewrite, :linear, :type-prescription, :definition, :meta (actually in that case, the result of evaluating the hypothesis metafunction call), and :forward-chaining.

    When a hypothesis of a conditional rule (of one of the classes listed above) has the form (force hyp), it is logically equivalent to hyp but has a pragmatic effect. In particular, when the rule is considered, the needed instance of the hypothesis, hyp', may be assumed if the usual process fails to prove it or its negation. In that situation, if the rule is eventually applied, then a special case is generated, requiring the system to prove that hyp' is true in the current context. The proofs of all such ``forced assumptions'' are, by default, delayed until the successful completion of the main goal. See forcing-round and see immediate-force-modep.

    Forcing is generally used on hypotheses that are always expected to be true, as is commonly the case for guards of functions. All the power of the theorem prover is brought to bear on a forced hypothesis and no backtracking is possible. Forced goals can be attacked immediately (see immediate-force-modep) or in a subsequent forcing round (see forcing-round). Also see case-split for a related utility. If the :executable-counterpart of the function force is disabled, then no hypothesis is forced. For more on enabling and disabling forcing, see enable-forcing and see disable-forcing.

    It sometimes happens that a conditional rule is not applied because some hypothesis, hyp, could not be relieved, even though the required instance of hyp, hyp', can be shown true in the context. This happens when insufficient resources are brought to bear on hyp' at the time we try to relieve it. A sometimes desirable alternative behavior is for the system to assume hyp', apply the rule, and to generate explicitly a special case to show that hyp' is true in the context. This is called ``forcing'' hyp. It can be arranged by restating the rule so that the offending hypothesis, hyp, is embedded in a call of force, as in (force hyp). By using the :corollary field of the rule-classes entry, a hypothesis can be forced without changing the statement of the theorem from which the rule is derived.

    Technically, force is just a function of one argument that returns that argument. It is generally enabled and hence evaporates during simplification. But its presence among the hypotheses of a conditional rule causes case splitting to occur if the hypothesis cannot be conventionally relieved.

    Since a forced hypothesis must be provable whenever the rule is otherwise applicable, forcing should be used only on hypotheses that are expected always to be true.

    A particularly common situation in which some hypotheses should be forced is in ``most general'' type-prescription lemmas. If a single lemma describes the ``expected'' type of a function, for all ``expected'' arguments, then it is probably a good idea to force the hypotheses of the lemma. Thus, every time a term involving the function arises, the term will be given the expected type and its arguments will be required to be of the expected type. In applying this advice it might be wise to avoid forcing those hypotheses that are in fact just type predicates on the arguments, since the routine that applies type-prescription lemmas has fairly thorough knowledge of the types of all terms.

    Force can have the additional benefit of causing the ACL2 typing mechanism to interact with the ACL2 rewriter to establish the hypotheses of type-prescription rules. To understand this remark, think of the ACL2 type reasoning system as a rather primitive rule-based theorem prover for questions about Common Lisp types, e.g., ``does this expression produce a consp?'' ``does this expression produce some kind of ACL2 number, e.g., an integerp, a rationalp, or a complex-rationalp?'' etc. It is driven by type-prescription rules. To relieve the hypotheses of such rules, the type system recursively invokes itself. This can be done for any hypothesis, whether it is ``type-like'' or not, since any proposition, p, can be phrased as the type-like question ``does p produce an object of type nil?'' However, as you might expect, the type system is not very good at establishing hypotheses that are not type-like, unless they happen to be assumed explicitly in the context in which the question is posed, e.g., ``If p produces a consp then does p produce nil?'' If type reasoning alone is insufficient to prove some instance of a hypothesis, then the instance will not be proved by the type system and a type-prescription rule with that hypothesis will be inapplicable in that case. But by embedding such hypotheses in force expressions you can effectively cause the type system to ``punt'' them to the rest of the theorem prover. Of course, as already noted, this should only be done on hypotheses that are ``always true.'' In particular, if rewriting is required to establish some hypothesis of a type-prescription rule, then the rule will be found inapplicable because the hypothesis will not be established by type reasoning alone.

    The ACL2 rewriter uses the type reasoning system as a subsystem. It is therefore possible that the type system will force a hypothesis that the rewriter could establish. Before a forced hypothesis is reported out of the rewriter, we try to establish it by rewriting.

    This makes the following surprising behavior possible: A type-prescription rule fails to apply because some true hypothesis is not being relieved. The user changes the rule so as to force the hypothesis. The system then applies the rule but reports no forcing. How can this happen? The type system ``punted'' the forced hypothesis to the rewriter, which established it.

    Finally, we should mention that the rewriter is never willing to force when there is an if term present in the goal being simplified. Since and terms and or terms are merely abbreviations for if terms, they also prevent forcing. Note that if terms are ultimately eliminated using the ordinary flow of the proof (but see set-case-split-limitations), allowing force ultimately to function as intended. Moreover, forcing can be disabled, as described above; also see disable-forcing.




    acl2-sources/doc/HTML/FORCED.html0000664002132200015000000000061512222333521015711 0ustar kaufmannacl2 FORCED.html -- ACL2 Version 6.3

    FORCED

    See force.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/FORCING-ROUND.html0000664002132200015000000001527112222333521016727 0ustar kaufmannacl2 FORCING-ROUND.html -- ACL2 Version 6.3

    FORCING-ROUND

    a section of a proof dealing with forced assumptions
    Major Section:  MISCELLANEOUS
    

    If ACL2 ``forces'' some hypothesis of some rule to be true, it is obliged later to prove the hypothesis. See force. ACL2 delays the consideration of forced hypotheses until the main goal has been proved. It then undertakes a new round of proofs in which the main goal is essentially the conjunction of all hypotheses forced in the preceding proof. Call this round of proofs the ``Forcing Round.'' Additional hypotheses may be forced by the proofs in the Forcing Round. The attempt to prove these hypotheses is delayed until the Forcing Round has been successfully completed. Then a new Forcing Round is undertaken to prove the recently forced hypotheses and this continues until no hypotheses are forced. Thus, there is a succession of Forcing Rounds.

    The Forcing Rounds are enumerated starting from 1. The Goals and Subgoals of a Forcing Round are printed with the round's number displayed in square brackets. Thus, "[1]Subgoal 1.3" means that the goal in question is Subgoal 1.3 of the 1st forcing round. To supply a hint for use in the proof of that subgoal, you should use the goal specifier "[1]Subgoal 1.3". See goal-spec.

    When a round is successfully completed -- and for these purposes you may think of the proof of the main goal as being the 0th forcing round -- the system collects all of the assumptions forced by the just-completed round. Here, an assumption should be thought of as an implication, (implies context hyp), where context describes the context in which hyp was assumed true. Before undertaking the proofs of these assumptions, we try to ``clean them up'' in an effort to reduce the amount of work required. This is often possible because the forced assumptions are generated by the same rule being applied repeatedly in a given context.

    By delaying and collecting the forced assumptions until the completion of the ``main goal'' we gain two advantages. First, the user gets confirmation that the ``gist'' of the proof is complete and that all that remains are ``technical details.'' Second, by delaying the proofs of the forced assumptions ACL2 can undertake the proof of each assumption only once, no matter how many times it was forced in the main goal.

    In order to indicate which proof steps of the previous round were responsible for which forced assumptions, we print a sentence explaining the origins of each newly forced goal. For example,

    [1]Subgoal 1, below, will focus on
    (GOOD-INPUTP (XTRANS I)),
    which was forced in
     Subgoal 14, above,
      by applying (:REWRITE PRED-CRUNCHER) to
      (PRED (XTRANS I) I),
     and
     Subgoal 28, above,
      by applying (:REWRITE PRED-CRUNCHER) to
      (PRED (XTRANS I) I).
    

    In this entry, ``[1]Subgoal 1'' is the name of a goal which will be proved in the next forcing round. On the next line we display the forced hypothesis, call it x, which is (good-inputp (xtrans i)) in this example. This term will be the conclusion of the new subgoal. Since the new subgoal will be printed in its entirety when its proof is undertaken, we do not here exhibit the context in which x was forced. The sentence then lists (possibly a succession of) a goal name from the just-completed round and some step in the proof of that goal that forced x. In the example above we see that Subgoals 14 and 28 of the just-completed proof forced (good-inputp (xtrans i)) by applying (:rewrite pred-cruncher) to the term (pred (xtrans i) i).

    If one were to inspect the theorem prover's description of the proof steps applied to Subgoals 14 and 28 one would find the word ``forced'' (or sometimes ``forcibly'') occurring in the commentary. Whenever you see that word in the output, you know you will get a subsequent forcing round to deal with the hypotheses forced. Similarly, if at the beginning of a forcing round a rune is blamed for causing a force in some subgoal, inspection of the commentary for that subgoal will reveal the word ``forced'' after the rule name blamed.

    Most forced hypotheses come from within the prover's simplifier. When the simplifier encounters a hypothesis of the form (force hyp) it first attempts to establish it by rewriting hyp to, say, hyp'. If the truth or falsity of hyp' is known, forcing is not required. Otherwise, the simplifier actually forces hyp'. That is, the x mentioned above is hyp', not hyp, when the forced subgoal was generated by the simplifier.

    Once the system has printed out the origins of the newly forced goals, it proceeds to the next forcing round, where those goals are individually displayed and attacked.

    At the beginning of a forcing round, the enabled structure defaults to the global enabled structure. For example, suppose some rune, rune, is globally enabled. Suppose in some event you disable the rune at "Goal" and successfully prove the goal but force "[1]Goal". Then during the proof of "[1]Goal", rune is enabled ``again.'' The right way to think about this is that the rune is ``still'' enabled. That is, it is enabled globally and each forcing round resumes with the global enabled structure.




    acl2-sources/doc/HTML/FORWARD-CHAINING-REPORTS.html0000664002132200015000000003560412222333517020400 0ustar kaufmannacl2 FORWARD-CHAINING-REPORTS.html -- ACL2 Version 6.3

    FORWARD-CHAINING-REPORTS

    to see reports about the forward chaining process
    Major Section:  ACL2 Documentation
    

    Debugging forward-chaining rules can be hard because their effects are not directly visible on the goal. In this documentation we tell you how to get reports on the forward chaining activity occurring in your proof attempts. This documentation is written in several parts. The first part is an introduction for the first-time user of forward chaining reports. The next two parts describe how to read reports. The last part describes how to monitor forward chaining activity only for selected runes, etc. We recommend the new user of these reports read everything!

    Some Related Topics

    A Quick Introduction to Forward Chaining Reports

    Caution: The reporting mechanism maintains some state and if you have already used forward chaining reporting in a session the directions below may not work as advertised! To return to the default forward chaining reporting state, execute this form at the top level:

    (reset-fc-reporting)
    

    You can get a report about all forward chaining activity in subsequent proofs by doing:

    (set-fc-criteria t)
    
    Options will be discussed later that allow you to monitor the activity caused by particular :forward-chaining rules or terms.

    Then do a proof that is expected to cause some forward chaining. In the proof output you will see lines like this:

    (Forward Chaining on behalf of PREPROCESS-CLAUSE:  (FC-Report 1))
    
    This is the only difference you should see in the proof output.

    After the proof attempt has terminated, you can execute:

    (fc-report k)
    
    for any k printed during the immediately preceding proof attempt. That will print a much longer report describing the activity that occurred during the kth use of forward chaining in that proof attempt.

    If you want to see these reports in real time (embedded in the proof output), do this before invoking the prover:

    (set-fc-report-on-the-fly t)
    

    Collecting the data used to generate these reports slows down the prover. If you no longer wish to see such reports, do

    (set-fc-criteria nil)
    

    How To Read FC Reports

    The report printed by (fc-report k) is of the form:

     Forward Chaining Report k:
     Caller: token
     Clause: (lit1 lit2 ... litn)
     Number of Rounds: m
     Contradictionp: bool
     Activations: (act1 act2 ...)
     

    This report means that the kth use of forward chaining in the most recent proof attempt was done on behalf of token (see below). The initial context (set of assumptions) consisted of the negations of the literals listed in the clause shown and the initial candidate trigger terms are all those appearing in that clause. This invocation of forward chaining proceeded to do m rounds of successive extensions of the initial context and ultimately either reached a contradiction (bool = T) or returned an extended context (bool = NIL). Note that reaching a contradiction from the negations of all the literals in a clause is ``good'' because it means the clause is true. The report concludes with the final status of all the forward chaining rules fired during the process. We explain how to read one of these activation reports in the next section.

    Forward chaining is done on behalf of many proof techniques in the system. Each is associated with a token. The main proof technique that uses forward chaining is SIMPLIFY-CLAUSE. This is the call of forward chaining that sets up the context used by the rewriter to relieve hypotheses during backchaining. Another common caller of forward chaining is PREPROCESS-CLAUSE, the first process in the ACL2 waterfall (see hints-and-the-waterfall). Forward chaining often proves ``near propositional'' goals (those depending just on boolean implications between basic predicates). Other tokens you may see include INDUCT, which uses forward chaining to set up a context for applying :induction rules, and the definitional principle (and related utilities such as verify-termination and verify-guards) which uses forward chaining during the construction of both measure conjectures and guard conjectures. When used this way, the token is defun-or-guard-verification.

    How to Read Activation Reports

    The forward chaining report concludes with a list of activation reports.

    Activations: (act1 act2 ...)
    
    Each acti is of the form:
    (rune
     (:TRIGGER inst-trig)
     ((:UNIFY-SUBST subst)
      (:DISPOSITION outcome-part1 outcome-part2 inst-term))
     ...)
    
    where the ... indicates that the rest of the report consists of more of those tuples listing a :UNIFY-SUBST and :DISPOSITION. We call each tuple a disposition of the activation and each disposition describes a substitution subst identifying the final instantiation of the rule and how the activation fared. Suppose there are n dispositions. (If the rule in question contains no free variables, n will be 1.)

    This activation report means that during the forward chaining process in question, the :forward-chaining rune rune was fired due to the presence in the evolving context of the trigger term inst-trig. (Note that inst-trig is an instantiation of the trigger term of the named rule. That is, the variable symbols you see in inst-trig are those of the clause printed in the forward chaining report.) The activation of rune by inst-trig proceeded to split n ways as different choices were made for the free-variables occuring among the hypotheses. Each of those n choices gave rise to a different substitution subst, and each succeeded or failed as described by the corresponding :DISPOSITION.

    The :DISPOSITION of an activation is described in three parts, outcome-part1, outcome-part2, and inst-term. Outcome-part1 is either SUCCESS or BLOCKED, meaning that the instance given by subst either succeeded in the sense that all of its instantiated hypotheses were found in the context, or failed because some instantiated hypothesis was not found.

    If outcome-part1 is SUCCESS then inst-term is the instantiated conclusion produced by the rule. Outcome-part2 is APPROVED if the instantiated conclusion was acceptable to our heuristics designed to prevent looping and not already known in the evolving context. Outcome-part2 is REJECTED if the instantiated conclusion was not approved by our heuristics. Outcome-part2 is REDUNDANT if the instantiated conclusion was approved by the heuristics but already known true in the current evolving context. If APPROVED, the truth of the instantiated conclusion is added to the evolving context. Otherwise, it is not.

    If outcome-part1 is BLOCKED then outcome-part2 is one of three possible things: FALSE, in which case inst-term is an instantiated hypothesis of the rule that is assumed false in the current context, UNRELIEVED-HYP, in which case inst-term is an instantiated hypothesis whose truthvalue is not determined by the context, or UNRELIEVED-HYP-FREE, in which case inst-term is an oddly instantiated hypothesis whose truthvalue is not determined by the context and which also contains free variables. In the last case, the ``odd'' instantiation was by the substitution subst but extended so that free variables in the hypothesis are renamed to start with the prefix UNBOUND-FREE- to draw your attention to them.

    Note: All of the terms printed in the report are instantiated with the relevant unifying substitution (possibly extended to bind free variables).

    Specifying the Tracking Criteria

    During a proof attempt, the forward chaining module stores information about the activations satisfying certain criteria. The criteria is a list of triples. Each triple consists of a forward chaining rune, an instantiated trigger term, and an instantiated conclusion to watch for. However, any or all of the components of such a triple may be t and that is given special significance.

    An activation satisfies a criteria if it satisfies at least one of the triples. An activation satisfies a triple if it satisfies all three of the components. Every activation satisfies the component t. An activation satisfies a rune if the activation describes a firing of the named rule. An activation satisfies an instantiated trigger term if the activation was created by that trigger being present in the context. An activation satisfies an instantiated conclusion if the activation could produce the instantiated conclusion (with the right choice of any free variables).

    Thus, the criteria is interpreted as a disjunction of conjunctions, making it possible to track a specific set of runes, triggers, and conclusions.

    For example, here is a triple that might appear in the criteria:

    ((:FORWARD-CHAINING ALISTP-FORWARD-TO-TRUE-LISTP)
     t
     t).
    
    This triple would cause every activation of the given rule to be tracked. However, the triple
    ((:FORWARD-CHAINING ALISTP-FORWARD-TO-TRUE-LISTP)
     (ALISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S))))
     t)
    
    would only track activations of that rule fired by the specific term shown as the second element of the triple. Futhermore
    (t
     (ALISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S))))
     t)
    
    would track any forward chaining rule triggered by that term, and
    (t
     t
     (TRUE-LISTP (MAKE-BINDINGS VARS (TOP-N (OP1 INST) (STACK S)))))
    
    would track any rule fired by any trigger that might lead to the specific term given as the third component above.

    Note: The condition on how an activation satisfies an instantiated conclusion is a little subtle. Consider the activation of the forward chaining rule

    (implies (and (symbol-listp x)
                  (equal (len x) (len y)))
             (true-listp (make-bindings x y)))
    
    triggered by (SYMBOL-LISTP VARS) arising in the current context. This activation could produce the specific conclusion shown in the last triple above, if it just happened that (TOP-N (OP1 INST) (STACK S)) were chosen as the binding of the free variable y. Thus, the activation of this rule triggered by (SYMBOL-LISTP VARS) satisfies the last triple above.

    Observe that the triple

    (t t t)
    
    is satisfied by every activation of any rule by any trigger term producing any conclusion.

    The function set-fc-criteria sets the criteria describing which activations are to be tracked. For example, if you execute:

    (set-fc-criteria ((:FORWARD-CHAINING LEMMA1)
                      t t)
                     ((:FORWARD-CHAINING LEMMA2)
                      (ALISTP (BASIC-MAPPER A B))
                      t)
                     (t t (TRUE-LISTP (DLT D)))),
    
    the system would track all activations of the forward-chaining rule LEMMA1, plus those activations of forward-chaining rule LEMMA2 triggered by the term given in the second triple, plus any activation of any rule that might derive (TRUE-LISTP (DLT D)).

    Because criteria generally mention variable symbols used in a specific conjecture, it is probably best to reconsider your criteria every time you want to track forward chaining.

    If the criteria is nil, then nothing is tracked. Setting the criteria to nil is the way you turn off tracking and reporting of forward chaining activity. You may do this either by (set-fc-criteria) or by (set-fc-criteria nil). (Technically the second form is an odd use of set-fc-criteria, which expects any supplied arguments to be triples; if the ``triple'' nil is the only one supplied, we take it to mean that the entire criteria should be nil.)

    To track every forward chaining activation you may set the criteria with either (set-fc-criteria (t t t)) or use the abbreviation (set-fc-criteria t).

    If, when you read a forward chaining report, you see no mention of an activation you have in mind, e.g., of a certain rune or deriving a certain conclusion, and you have set the criteria correctly, then the activation never happened. (This is akin to using :brr and :monitor to monitor the application of a rewrite rule and then seeing no interactive break.)

    For some relevant functions to help you manage criteria and when the full reports are printed see fc-report, show-fc-criteria, set-fc-criteria, reset-fc-reporting, and set-fc-report-on-the-fly.




    acl2-sources/doc/HTML/FORWARD-CHAINING.html0000664002132200015000000002241412222333530017232 0ustar kaufmannacl2 FORWARD-CHAINING.html -- ACL2 Version 6.3

    FORWARD-CHAINING

    make a rule to forward chain when a certain trigger arises
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Examples:
    
    (defthm p-and-r-forward           ; When (p a) appears in a formula to be
     (implies (and (p x) (r x))       ; simplified, try to establish (p a) and
              (q (f x)))              ; (r a) and, if successful, add (q (f a))
     :rule-classes :forward-chaining) ; to the known assumptions.
    
    (defthm p-and-r-forward           ; as above with most defaults filled in
      (implies (and (p x) (r x))
               (q (f x)))
      :rule-classes ((:forward-chaining :trigger-terms ((p x))
                                        :corollary (implies (and (p x) (r x))
                                                            (q (f x)))
                                        :match-free :all)))
    

    To specify the triggering terms provide a non-empty list of terms as the value of the :trigger-terms field of the rule class object.

    General Form:
    Any theorem, provided an acceptable triggering term exists.
    
    The structure of this documentation is as follows. First we give a brief overview of forward chaining and contrast it to backchaining (rewriting). Then we lay out the syntactic restrictions on :forward-chaining rules. Then we give more details about the process and point to a tool to assist you in debugging your :forward-chaining rules.

    Overview and When to Use Forward Chaining

    Forward chaining is performed as part of the simplification process: before the goal is rewritten a context is established. The context tells the theorem prover what may be assumed during rewriting, in particular, to establish hypotheses of rewrite rules. Forward chaining is used to extend the context before rewriting begins. For example, the :forward-chaining rule (implies (p x) (p1 x)) would add (p1 A) to the context, where A is some term, if (p A) is already in the context.

    Forward chaining and backchaining are duals. If a rewrite rule requires that (p1 A) be established and (p A) is known, it could be done either by making (implies (p x) (p1 x)) a :forward-chaining rule or a :rewrite rule. Which should you choose?

    As a rule of thumb, if a conclusion like (p1 A) is expected to be widely needed, it is better to derive it via forward chaining because then it is available ``for free'' during the rewriting after paying the one-time cost of forward chaining. Alternatively, if (p1 A) is a rather special hypothesis of key importance to only a few rewrite rules, it is best to derive it only when needed. Thus forward chaining is pro-active and backward chaining (rewriting) is reactive.

    Syntactic Restrictions

    Forward chaining rules are generated from the corollary term (see rule-classes) as follows. First, every let expression is expanded away (hence, so is every let* and lambda expression), as is every call of a so-called ``guard holder,'' mv-list or return-last (the latter resulting from macroexpansion of calls of prog2$, must-be-equal or mbe), ec-call, and a few others), or `the'. If the resulting term has the form (implies hyp concl), then concl is treated as a conjunction, with one forward chaining rule with hypothesis hyp created for each conjunct. In the other case, where the corollary term is not an implies, we process it as we process the conclusion in the first case.

    Note that unlike rewrite rules, a nested implication is not folded into a single implication. Consider for example the following term.

    (implies (p1 x)
             (implies (p2 x)
                      (p3 x)))
    
    Although this term is parsed for a rewrite rule as (implies (and (p1 x) (p2 x)) (p3 x)), that is not the case when this term is parsed for a forward-chaining rule, in which case (p1 x) is treated as the hypothesis and (implies (p2 x) (p3 x)) is treated as the conclusion.

    The :trigger-terms field of a :forward-chaining rule class object should be a non-empty list of terms, if provided, and should have certain properties described below. If the :trigger-terms field is not provided, it defaults to the singleton list containing the ``atom'' of the first hypothesis of the formula. (The atom of (not x) is x; the atom of any other term is the term itself.) If there are no hypotheses and no :trigger-terms were provided, an error is caused.

    A triggering term is acceptable if it is not a variable, a quoted constant, a lambda application, a let- (or let*-) expression, or a not-expression, and every variable symbol in the conclusion of the theorem either occurs in the hypotheses or occurs in the trigger.

    More Details about Forward Chaining

    :Forward-chaining rules are used by the simplifier before it begins to rewrite the literals of the goal. (Forward chaining is thus carried out from scratch for each goal.) If any term in the goal is an instance of a trigger of some forward chaining rule, we try to establish the hypotheses of that forward chaining theorem (from the negation of the goal). To relieve a hypothesis we only use type reasoning, evaluation of ground terms, and presence among our known assumptions. We do not use rewriting. So-called free variables in hypotheses are treated specially; see free-variables. If all hypotheses are relieved, and certain heuristics approve of the newly derived conclusion, we add the instantiated conclusion to our known assumptions. Since this might introduce new terms into the assumptions, forward chaining is repeated. Heuristic approval of each new addition is necessary to avoid infinite looping as would happen with the rule (implies (p x) (p (f x))), which might otherwise forward chain from (p A) to (p (f A)) to (p (f (f A))), etc.

    Caution. Forward chaining does not actually add terms to the goals displayed during proof attempts. Instead, it extends an associated context, called ``assumptions'' in the preceding paragraph, that ACL2 builds from the goal currently being proved. (For insiders: forward chaining extends the type-alist.) The context starts out with ``obvious'' consequences of the negation of the goal. For example, if the goal is

    (implies (and (p A) (q (f A)))
             (c A))
    
    then the context notes that (p A) and (q (f A)) are non-nil and (c A) is nil. Forward chaining is then used to expand the context. For example, if a forward chaining rule has (f x) as a trigger term and has body (implies (p x) (r (f x))), then the context is extended by binding (r (f A)) to non-nil, provided the heuristics approve of this extension. Note however that since (r (f A)) is put into the context, not the goal, you will not see it in the goal formula. Furthermore, the assumption added to the context is just the instantiation of the conclusion of the rule, with no simplification or rewriting applied. Thus, for example, if it contains an enabled non-recursive function symbol it is unlikely ever to match a (rewritten) term arising during subsequent simplification of the goal.

    However, forward-chaining does support the linear arithmetic reasoning package. For example, suppose that forward-chaining puts (< (f x) (g x)) into the context. Then this inequality also goes into the linear arithmetic database, together with suitable instances of linear lemmas whose trigger term is a call of g. See linear.

    Debugging :forward-chaining rules can be difficult since their effects are not directly visible on the goal being simplified. Tools are available to help you discover what forward chaining has occurred see forward-chaining-reports.




    acl2-sources/doc/HTML/FOURTH.html0000664002132200015000000000066512222333523015765 0ustar kaufmannacl2 FOURTH.html -- ACL2 Version 6.3

    FOURTH

    fourth member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING.html0000664002132200015000000001333312222333530022473 0ustar kaufmannacl2 FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING.html -- ACL2 Version 6.3

    FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING

    examples pertaining to free variables in forward-chaining rules
    Major Section:  FREE-VARIABLES-EXAMPLES
    

    The following examples illustrate ACL2's handling of free variables in forward-chaining rules, as well as user control over how such free variables are handled. See free-variables for a background discussion.

    ; First let us introduce a transitive operation, op, and prove a
    ; forward-chaining rule stating the transitivity of op.
    
    (encapsulate
     (((op * *) => *))
     (local (defun op (x y) (< x y)))
     (defthm transitivity-of-op
       (implies (and (op x y) (op y z)) (op x z))
       :rule-classes :forward-chaining))
    
    ; The following theorem is proved by forward chaining, using the above rule.
    
    (thm
     (implies (and (op u v) (op v w) (op v a))
              (op u w)))
    
    ; The proof of the theorem just above succeeds because the term (op u v)
    ; triggers the application of forward-chaining rule transitivity-of-op,
    ; binding x to u and y to v.  Free variable z of that rule is bound to both w
    ; and to a, resulting in the addition of both (op u w) and (op u a) to the
    ; context.  However, (op v a) happens to be at the front of the context, so
    ; if only one free-variable binding had been allowed, then z would have only
    ; been bound to a, not to w, as we now illustrate.
    
    (add-match-free-override :once (:forward-chaining transitivity-of-op))
    
    (thm ; FAILS!
     (implies (and (op u v) (op v w) (op v a))
              (op u w)))
    
    :ubt! 1
    
    ; Starting over, this time we prove transitivity-of-op as a :match-free :once
    ; forward-chaining rule.  Note that the presence of :match-free eliminates
    ; the free-variables warning that we got the first time.
    
    (encapsulate
     (((op * *) => *))
     (local (defun op (x y) (< x y)))
     (defthm transitivity-of-op
       (implies (and (op x y) (op y z)) (op x z))
       :rule-classes ((:forward-chaining :match-free :once))))
    
    (thm ; FAILS!
     (implies (and (op u v) (op v w) (op v a))
              (op u w)))
    
    ; Notice that if we swap the order of the last two hypotheses the theorem
    ; goes through, because this time (op v w) is first in the context.
    
    (thm ; SUCCEEDS!
     (implies (and (op u v) (op v a) (op v w))
              (op u w)))
    
    :u
    
    ; Now let's try setting the default to :once.
    
    (set-match-free-default :once)
    
    ; We still get a free-variables warning when we admit this forward-chaining rule.
    
    (encapsulate
     (((op * *) => *))
     (local (defun op (x y) (< x y)))
     (defthm transitivity-of-op
       (implies (and (op x y) (op y z)) (op x z))
       :rule-classes ((:forward-chaining))))
    
    ; This theorem fails--as it should.
    
    (thm ; FAILS!
     (implies (and (op u v) (op v w) (op v a))
              (op u w)))
    
    ; But if we convert this rule (or here, all possible rules) to :all rules,
    ; then the proof succeeds.
    
    (add-match-free-override :all t)
    
    (thm ; SUCCEEDS!
     (implies (and (op u v) (op v w) (op v a))
              (op u w)))
    
    ; Now let's test a relatively slow :all case (the next thm below).
    
    :ubt! 1
    
    (encapsulate
     (((op1 *) => *)
      ((op3 * * *) => *))
     (local (defun op1 (x) (declare (ignore x)) t))
     (local (defun op3 (x0 x1 x2)
              (declare (ignore x0 x1 x2))
              t))
     (defthm op1-op3-property
       (implies (and (op1 x0) (op1 x1) (op1 x2))
                (op3 x0 x1 x2))
       :rule-classes ((:forward-chaining :match-free :all))))
    
    ; The following succeeds, but takes a little time (about a second in one run).
    
    (thm (implies
          (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5)
               (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11)
               (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16)
               (op1 a17) (op1 a18) (op1 a19) (op1 a20))
          (op3 a5 a6 a0)))
    
    (add-match-free-override :once t)
    
    ; The same theorem now fails because of the add-match-free-override, but is
    ; more than an order of magnitude faster.
    
    (thm (implies
          (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5)
               (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11)
               (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16)
               (op1 a17) (op1 a18) (op1 a19) (op1 a20))
          (op3 a5 a6 a0)))
    
    ; A slight variant succeeds in a negligible amount of time (still with the
    ; :once override above).
    
    (thm (implies
          (and (op1 a0) (op1 a1) (op1 a2) (op1 a3) (op1 a4) (op1 a5)
               (op1 a6) (op1 a7) (op1 a8) (op1 a9) (op1 a10) (op1 a11)
               (op1 a12) (op1 a13) (op1 a14) (op1 a15) (op1 a16)
               (op1 a17) (op1 a18) (op1 a19) (op1 a20))
          (op3 a5 a20 a20)))
    
    ; Reality check: This shouldn't give a free-variables warning, and everything
    ; should work great since there are no free variables with this trigger term.
    
    :ubt! 1
    
    (encapsulate
     (((op1 *) => *)
      ((op7 * * * * * * *) => *))
     (local (defun op1 (x)
              (declare (ignore x))
              t))
     (local (defun op7 (x0 x1 x2 x3 x4 x5 x6)
              (declare (ignore x0 x1 x2 x3 x4 x5 x6))
              t))
     (defthm op1-op7-property
       (implies (and (op1 x0) (op1 x1) (op1 x2)
                     (op1 x3) (op1 x4) (op1 x5) (op1 x6))
                (op7 x0 x1 x2 x3 x4 x5 x6))
       :rule-classes ((:forward-chaining
                       :trigger-terms ((op7 x0 x1 x2 x3 x4 x5 x6))))))
    
    ; The following then succeeds, and very quickly.
    
    (thm (implies (and (op1 a0) (op1 a1) (op1 a2)
                       (op1 a3) (op1 a4) (op1 a5) (op1 a6))
                  (op7 a4 a6 a5 a6 a6 a6 a6)))
    
    




    acl2-sources/doc/HTML/FREE-VARIABLES-EXAMPLES-REWRITE.html0000664002132200015000000004447612222333530021346 0ustar kaufmannacl2 FREE-VARIABLES-EXAMPLES-REWRITE.html -- ACL2 Version 6.3

    FREE-VARIABLES-EXAMPLES-REWRITE

    examples pertaining to free variables in rewrite rules
    Major Section:  FREE-VARIABLES-EXAMPLES
    

    The following examples illustrate ACL2's handling of free variables in rewrite rules, as well as user control over how such free variables are handled. See free-variables for a background discussion.

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Example 1
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (defstub p2 (x y) t) ; introduce unconstrained function
    
    ; Get warning because of free variable.  This would be an error if you had
    ; first executed (set-match-free-error t) in order to force yourself to
    ; specify :match-free (illustrated later, below).
    (defaxiom p2-trans
      (implies (and (p2 x y)
                    (p2 y z))
               (p2 x z)))
    
    ; Succeeds.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    ; The following causes an error because p2-trans is not a rune.
    (add-match-free-override :once p2-trans)
    
    ; After the following, the rewrite rule p2-trans will only allow one
    ; attempt per hypothesis to bind free variables.
    (add-match-free-override :once (:rewrite p2-trans))
    
    ; Now this same theorem fails to be proved.  Here's why.  The
    ; context for proving (p2 a d) happens to include the hypotheses in
    ; reverse order.  So when the first hypothesis of p2-trans, namely
    ; (p2 x y), is relieved, where x is bound to a (as we are attempting
    ; to rewrite the current literal (p2 a d)), we find (p2 a b) in the
    ; context before (p2 a c) and hence y is bound to b.  The
    ; instantiated second hypothesis of p2-trans is thus (p2 b d), and
    ; the proof fails.  Before the add-match-free-override form above,
    ; the proof succeeded because the rewriter was allowed to backtrack
    ; and find the other binding for the first hypothesis of p2-trans,
    ; namely, y bound to c.  Then the instantiated second hypothesis of
    ; p2-trans is (p2 c d), which is known to be true in the current
    ; context.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    ; Return to original behavior for binding free variables.
    (add-match-free-override :all t)
    
    ; Succeeds once again.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    (u) ; undo (add-match-free-override :all t)
    
    ; This is an error, since no further arguments should appear after
    ; :clear.
    (add-match-free-override :clear t)
    
    ; Return all rules to original behavior for binding free variables,
    ; regardless of which previous add-match-free-override forms have
    ; been executed.
    (add-match-free-override :clear)
    
    ; This succeeds just as it did originally.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    (ubt! 'p2-trans) ; back to the start, except retain the defstub
    
    ; Require that :match-free be specified for :linear and :rewrite rules with
    ; free variables.
    (set-match-free-error t)
    
    ; Fails because :match-free is missing.
    (defaxiom p2-trans
      (implies (and (p2 x y)
                    (p2 y z))
               (p2 x z)))
    
    ; Fails because :match-free must be followed by :once or :all.
    (defaxiom p2-trans
      (implies (and (p2 x y)
                    (p2 y z))
               (p2 x z))
      :rule-classes ((:rewrite :match-free nil)))
    
    ; Succeeds, this time with no warning at all.
    (defaxiom p2-trans
      (implies (and (p2 x y)
                    (p2 y z))
               (p2 x z))
      :rule-classes ((:rewrite :match-free :once)))
    
    ; Fails because we only bind once (see earlier long comment).
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    ; Treat p2-trans as though `:match-free :all' had been specified.
    (add-match-free-override :all (:rewrite p2-trans))
    
    ; Succeeds since more than one binding is allowed for p2-trans.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    (u)
    (u)
    
    ; Specify that future :linear and :rewrite rules with free variables
    ; that do not have :match-free specified are treated as though
    ; `:match-free :once' were specified.
    (set-match-free-default :once)
    
    ; Succeeds without error since `:match-free' is specified, as described
    ; above.  But there is a warning, since :match-free is not specified for this
    ; :rewrite rule.
    (defaxiom p2-trans
      (implies (and (p2 x y)
                    (p2 y z))
               (p2 x z)))
    
    ; Fails since only single bindings are allowed for p2-trans.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    ; Treat p2-trans as though `:match-free :all' had been specified.
    (add-match-free-override :all t)
    
    ; Succeeds.
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    ; Test searching of ground units, i.e. rewrite rules without variables on the
    ; left side of the conclusion, for use in relieving hypotheses with free
    ; variables.  This is a very contrived example.
    
    (ubt! 1) ; back to the start
    
    (encapsulate
     (((p1 *) => *)
      ((p2 * *) => *)
      ((p3 *) => *)
      ((a) => *)
      ((b) => *))
     (local (defun p1 (x) x))
     (local (defun p2 (x y) (list x y)))
     (local (defun p3 (x) x))
     (local (defun a () 0))
     (local (defun b () 0)))
    
    ; Allow default of :match-free :all (form may be omitted).
    (set-match-free-error nil)
    
    (defaxiom ax1
      (implies (and (p2 x y)
                    (p1 y))
               (p3 x)))
    
    (defaxiom p2-a-b
      (p2 (a) (b)))
    
    (defaxiom p2-a-a
      (p2 (a) (a)))
    
    (defaxiom p1-b
      (p1 (b)))
    
    ; Succeeds; see long comment below on next attempt to prove this
    ; theorem.
    (thm (implies (p2 (a) y)
                  (p3 (a))))
    
    ; Now ax1 will only relieve hypothesis (p2 x y) for one binding of y:
    (add-match-free-override :once t)
    
    ; Fails when ax1 attempts to rewrite the conclusion to true, because
    ; the most recent ground unit for hypothesis (p2 x y) with x bound
    ; to (a) is rule p2-a-a, which binds y to (a).  If more than one ground
    ; unit could be used then we would backtrack and apply rule p2-a-b,
    ; which binds y to (b) and hence hypothesis (p1 y) of ax1 is
    ; relieved by rule p1-b.
    (thm (implies (p2 (a) y)
                  (p3 (a))))
    
    ; Return rules to original :match-free behavior.
    (add-match-free-override :clear)
    
    ; Succeeds once again.
    (thm (implies (p2 (a) y)
                  (p3 (a))))
    
    ; Just for kicks, change the behavior of a built-in rule irrelevant
    ; to the proof at hand.
    (add-match-free-override :once (:rewrite string<-l-trichotomy))
    
    ; Still succeeds.
    (thm (implies (p2 (a) y)
                  (p3 (a))))
    

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Example 2
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    

    The next example illustrates the use of the break-rewrite facility to get information about handling of free variables by the rewriter. Explanation is given after this (edited) transcript. Input begins on lines with a prompt (search for ``ACL2''); the rest is output.

    ACL2 !>(encapsulate
            ((p1 (u x) t)
             (bad (x) t)
             (p2 (x y z) t)
             (bar (x y) t)
             (foo (x y) t)
             (poo (x y) t)
             (prop (u) t))
    
            (local (defun p1 (u x) (declare (ignore u x)) nil))
            (local (defun bad (x) (declare (ignore x)) nil))
            (local (defun p2 (x y z) (declare (ignore x y z)) nil))
            (local (defun bar (x y) (declare (ignore x y)) nil))
            (local (defun foo (x y) (declare (ignore x y)) nil))
            (local (defun poo (x y) (declare (ignore x y)) nil))
            (local (defun prop (u) (declare (ignore u)) t))
    
            (defthm foo-poo
              (implies (syntaxp (equal y 'y3))
                       (equal (foo x y)
                              (poo x y))))
    
            (defthm lemma-1
              (implies (and (p1 u x)
                            (bad x)
                            (p2 x y z)
                            (bar x y)
                            (equal x x) ; admittedly silly!
                            (foo x y))
                       (prop u))
              :rule-classes ((:rewrite :match-free :all))))
    
    ; [[ output omitted ]]
    
    Summary
    Form:  ( ENCAPSULATE ((P1 ...) ...) ...)
    Rules: NIL
    Warnings:  Subsume and Non-rec
    Time:  0.08 seconds (prove: 0.00, print: 0.01, other: 0.06)
     T
    ACL2 !>:brr t
    The monitored runes are:
    NIL
     T
    ACL2 !>:monitor (:rewrite lemma-1) t
    (((:REWRITE LEMMA-1) 'T))
    ACL2 !>(thm (implies (and (p1 u0 x1)
                              (bad x1)
                              (bad x3)
                              (bar x3 y1)
                              (bar x3 y3)
                              (p1 u0 x2)
                              (p1 u0 x3)
                              (p2 x3 y1 z1)
                              (p2 x3 y3 z1))
                         (prop u0)))
    
    (1 Breaking (:REWRITE LEMMA-1) on (PROP U0):
    1 ACL2 >:eval
    
    1x (:REWRITE LEMMA-1) failed because :HYP 1 contains free variables.
    The following display summarizes the attempts to relieve hypotheses
    by binding free variables; see :DOC free-variables.
    
        [1] X : X1
    Failed because :HYP 3 contains free variables Y and Z, for which no
    suitable bindings were found.
        [1] X : X2
    Failed because :HYP 2 rewrote to (BAD X2).
        [1] X : X3
            [3] Z : Z1
                Y : Y1
    Failed because :HYP 6 rewrote to (FOO X3 Y1).
            [3] Z : Z1
                Y : Y3
    Failed because :HYP 6 rewrote to (POO X3 Y3).
    
    1 ACL2 >:unify-subst
         U : U0
    1 ACL2 >
    
    The :eval command above asks the rewriter to attempt to apply the rewrite rule lemma-1 to the term (prop u0), shown just above the line with :eval. As we can see at the end, the variable u in the conclusion of lemma-1 is being bound to the variable u0 in the conjecture. The first hypothesis of lemma-1 is (p1 u x), so the rewriter looks for some x for which (p1 u0 x) is known to be true. It finds x1, and then goes on to consider the second hypothesis, (bad x). Since the theorem we are proving has (bad x1) in the hypothesis and x is currently bound to x1, the rewriter is satisfied and moves on to the third hypothesis of lemma-1, (p2 x y z). However, x is bound to x1 and there are no instances of y and z for which (p2 x1 y z) is known in the current context. All of the above analysis is summarized in the first part of the output from :eval above:
        [1] X : X1
    Failed because :HYP 3 contains free variables Y and Z, for which no
    suitable bindings were found.
    
    Thus, the binding of x to x1 on behalf of the first hypothesis has failed.

    The rewriter now backs up to look for other values of x that satisfy the first hypothesis, and finds x2 because our current theorem has a hypothesis of (p1 u0 x2). But this time, the second hypothesis of lemma-1, (bad x), is not known to be true for x; that is, (bad x2) does not rewrite to t; in fact, it rewrites to itself. That explains the next part of the output from :eval above:

        [1] X : X2
    Failed because :HYP 2 rewrote to (BAD X2).
    

    The rewriter now backs up again to look for other values of x that satisfy the first hypothesis, and finds x3 because our current theorem has a hypothesis of (p1 u0 x3). This time, the second hypothesis of lemma-1 is not a problem, and moreover, the rewriter is able to bind y and z to y1 and z1, respectively, in order to satisfy the third hypothesis, (p2 x y z): that is, (p2 x2 y1 z1) is known in the current context. That explains more of the above output from :eval:

        [1] X : X3
            [3] Z : Z1
                Y : Y1
    
    Unfortunately, the sixth hypothesis, (foo x y), rewrites to itself under the above bindings:
    Failed because :HYP 6 rewrote to (FOO X3 Y1).
    
    So the rewriter looks for other bindings to satisfy the third hypothesis and finds these.
            [3] Z : Z1
                Y : Y3
    
    This time, the sixth hypothesis can be rewritten under the above bindings, from (foo x3 y3) to (poo x3 y3) by lemma foo-poo, but still not to t.
    Failed because :HYP 6 rewrote to (POO X3 Y3).
    
    There are no more free variable bindings to try, so this concludes the output from :eval.

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Example 3
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    

    The next pair of examples illustrates so-called ``binding hypotheses'' (see free-variables) and explores some of their subtleties. The first shows binding hypotheses in action on a simple example. The second shows how binding hypotheses interact with equivalence relations and explains the role of double-rewrite.

    Our first example sets up a theory with two user-supplied rewrite rules, one of which has a binding hypothesis. Below we explain how that binding hypothesis contributes to the proof.

    ; Define some unary functions.
    (defun f (x) (declare (ignore x)) t)
    (defun g (x) x)
    (defun h (x) x)
    (defun k (x) x)
    
    ; Prove some simple lemmas.  Note the binding hypothesis in g-rewrite.
    (defthm f-k-h
      (f (k (h x))))
    (defthm g-rewrite
           (implies (and (equal y (k x)) ; binding hypothesis
                         (f y))
                    (equal (g x) y)))
    
    ; Restrict to a theory that includes the above lemmas but avoids the above
    ; definitions.
    (in-theory (union-theories (theory 'minimal-theory)
                               '(f-k-h g-rewrite)))
    
    ; Prove a theorem.
    (thm (equal (g (h a)) (k (h a))))
    

    Let us look at how ACL2 uses the above binding hypothesis in the proof of the preceding thm form. The rewriter considers the term (g (h a)) and finds a match with the left-hand side of the rule g-rewrite, binding x to (h a). The first hypothesis binds y to the result of rewriting (k x) in the current context, where the variable x is bound to the term (h a); thus y is bound to (k (h a)). The second hypothesis, (f y), is then rewritten under this binding, and the result is t by application of the rewrite rule f-k-h. The rule g-rewrite is then applied under the already-mentioned binding of x to (h a). This rule application triggers a recursive rewrite of the right-hand side of g-rewrite, which is y, in a context where y is bound (as discussed above) to (k (h a)). The result of this rewrite is that same term, (k (h a)). The original call of equal then trivially rewrites to t.

    We move on now to our second example, which is similar but involves a user-defined equivalence relation. You may find it helpful to review :equivalence rules; see equivalence.

    Recall that when a hypothesis is a call of an equivalence relation other than equal, the second argument must be a call of double-rewrite in order for the hypothesis to be treated as a binding hypothesis. That is indeed the case below; an explanation follows.

    ; Define an equivalence relation.
    (defun my-equiv (x y) (equal x y))
    (defequiv my-equiv) ; introduces rule MY-EQUIV-IS-AN-EQUIVALENCE
    
    ; Define some unary functions
    (defun f (x) (declare (ignore x)) t)
    (defun g (x) x)
    (defun h1 (x) x)
    (defun h2 (x) x)
    
    ; Prove some simple lemmas.  Note the binding hypothesis in lemma-3.
    (defthm lemma-1
      (my-equiv (h1 x) (h2 x)))
    (defthm lemma-2
      (f (h2 x)))
    (defthm lemma-3
           (implies (and (my-equiv y (double-rewrite x)) ; binding hypothesis
                         (f y))
                    (equal (g x) y)))
    
    ; Restrict to a theory that includes the above lemmas but avoids the above
    ; definitions.
    (in-theory (union-theories (theory 'minimal-theory)
                               '(lemma-1 lemma-2 lemma-3
                                         my-equiv-is-an-equivalence)))
    
    ; Prove a theorem.
    (thm (equal (g (h1 a)) (h2 a)))
    

    The proof succeeds much as in the first example, but the following observation is key: when ACL2 binds y upon considering the first hypothesis of lemma-3, it rewrites the term (double-rewrite x) in a context where it need only preserve the equivalence relation my-equiv. At this point, x is bound by applying lemma-3 to the term (g (h1 a)); so, x is bound to (h1 a). The rule lemma-1 then applies to rewrite this occurrence of x to (h2 a), but only because it suffices to preserve my-equiv. Thus y is ultimately bound to (h2 a), and the proof succeeds as one would expect.

    If we tweak the above example slightly by disabling the user's equivalence rune, then the proof of the thm form fails because the above rewrite of (double-rewrite x) is done in a context where it no longer suffices to preserve my-equiv as we dive into the second argument of my-equiv in the first hypothesis of lemma-3; so, lemma-1 does not apply this time.

    (in-theory (union-theories (theory 'minimal-theory)
                               '(lemma-1 lemma-2 lemma-3)))
    
    ; Proof fails in this case!
    (thm (equal (g (h1 a)) (h2 a)))
    




    acl2-sources/doc/HTML/FREE-VARIABLES-EXAMPLES.html0000664002132200015000000000317712222333530020220 0ustar kaufmannacl2 FREE-VARIABLES-EXAMPLES.html -- ACL2 Version 6.3

    FREE-VARIABLES-EXAMPLES

    examples pertaining to free variables in rules
    Major Section:  FREE-VARIABLES
    

    The examples in the two sub-topics of this topic illustrate the handling of free variables in rules of class :rewrite (see free-variables-examples-rewrite) and of class :forward-chaining (see free-variables-examples-forward-chaining), respectively. These implicitly illustrate free-variables handling in rules of class :linear as well. Also see free-variables and see rule-classes.

    Some Related Topics




    acl2-sources/doc/HTML/FREE-VARIABLES-TYPE-PRESCRIPTION.html0000664002132200015000000000717012222333530021477 0ustar kaufmannacl2 FREE-VARIABLES-TYPE-PRESCRIPTION.html -- ACL2 Version 6.3

    FREE-VARIABLES-TYPE-PRESCRIPTION

    matching for free variable in type-prescription rules
    Major Section:  FREE-VARIABLES
    

    We assume familiarity with the issue of dealing with free variables in hypotheses; see free-variables.

    By default, starting with Version 4.3, ACL2 attempts all possible matches for free variables. Consider the following example.

    (defstub f1 (x) t)
    (defstub f2 (x y) t)
    (defstub f3 (y) t)
    
    (defaxiom f1-prop
      (implies (and (f2 x y) ; <-- y is free in this hypothesis
                    (f3 y))
               (f1 x))       ; <-- (f1 x) is the type-term (type is `non-nil')
      :rule-classes :type-prescription)
    
    ; Succeeds:
    (thm (implies (and (f2 a b)
                       (f3 b))
                  (f1 a)))
    
    ; The following fails unless we try more than one match for free variables in
    ; hypotheses.
    (thm (implies (and (f2 a b)
                       (f2 a c)
                       (f2 a d)
                       (f3 b))
                  (f1 a)))
    

    There may be times when you want to match only the first free variable. In that case, you can write a function of two arguments, the type-prescription rune being applied and the current ACL2 world, that prohibits multiple matching for those times. Your function is then `attached' to the built-in constrained function, oncep-ts. The following examples are intended to explain how this works.

    First, let us disallow all mutliple matching of free variables (i.e., implement the behavior often referred to as ``:match-free :once''; see free-variables).

    (defun oncep-tp-always (rune wrld)
      (declare (ignore rune wrld)
               (xargs :mode :logic :guard t))
      t)
    
    (defattach oncep-tp oncep-tp-always)
    
    The second thm form above will now fail, because only one free-variable match is permitted for the first hypothesis of rule f1-prop above.

    Now suppose that instead, we want to disallow multiple matches for free variables in hypotheses of type-prescription rules except for the rule f1-prop above. With the following events, the second thm form above once again succeeds.

    (defun oncep-tp-always-except-f1-prop (rune wrld)
      (declare (ignore wrld)
               (xargs :mode :logic :guard (and (consp rune)
                                               (consp (cdr rune))
                                               (symbolp (cadr rune)))))
      (not (eq (base-symbol rune) 'f1-prop)))
    
    (defattach oncep-tp oncep-tp-always-except-f1-prop)
    

    In general, your defattach event will attach a function symbol to oncep-tp. The guard of that function symbol must be implied by the tuard of oncep-tp:

    ACL2 !>:args oncep-tp
    
    Function         ONCEP-TP
    Formals:         (RUNE WRLD)
    Signature:       (ONCEP-TP * *)
                     => *
    Guard:           (AND (PLIST-WORLDP WRLD)
                          (CONSP RUNE)
                          (CONSP (CDR RUNE))
                          (SYMBOLP (CADR RUNE)))
    Guards Verified: T
    Defun-Mode:      :logic
    Type:            built-in (or unrestricted)
    
     ONCEP-TP
    ACL2 !>
    




    acl2-sources/doc/HTML/FREE-VARIABLES.html0000664002132200015000000003214312222333530016777 0ustar kaufmannacl2 FREE-VARIABLES.html -- ACL2 Version 6.3

    FREE-VARIABLES

    free variables in rules
    Major Section:  RULE-CLASSES
    

    As described elsewhere (see rule-classes), ACL2 rules are treated as implications for which there are zero or more hypotheses hj to prove. In particular, rules of class :rewrite may look like this:

    (implies (and h1 ... hn)
             (fn lhs rhs))
    
    Variables of hi are said to occur free in the above :rewrite rule if they do not occur in lhs or in any hj with j<i. (To be precise, here we are only discussing those variables that are not in the scope of a let/let*/lambda that binds them.) We also refer to these as the free variables of the rule. ACL2 may issue a warning or error when there are free variables in a rule, as described below. (Variables of rhs may be considered free if they do not occur in lhs or in any hj. But we do not consider those in this discussion.)

    In general, the free variables of rules are those variables occurring in their hypotheses (not let/let*/lambda-bound) that are not bound when the rule is applied. For rules of class :linear and :forward-chaining, variables are bound by a trigger term. (See rule-classes for a discussion of the :trigger-terms field). For rules of class :type-prescription, variables are bound by the :typed-term field.

    Let us discuss the method for relieving hypotheses of rewrite rules with free variables. Similar considerations apply to linear and forward-chaining rules, and type-prescription rules.

    See free-variables-examples for more examples of how this all works, including illustration of how the user can exercise some control over it. In particular, see free-variables-examples-rewrite for an explanation of output from the break-rewrite facility in the presence of rewriting failures involving free variables, as well as an example exploring ``binding hypotheses'' as described below.

    Note that the :match-free mechanism discussed below does not apply to type-prescription rules. See free-variables-type-prescription for a discussion of how to control free-variable matchng for type-prescription rules.

    Some Related Topics

    We begin with an example. Does the proof of the thm below succeed?

    (defstub p2 (x y) t)
    
    (defaxiom p2-trans
      (implies (and (p2 x y)
                    (p2 y z))
               (equal (p2 x z) t))
      :rule-classes ((:rewrite :match-free :all)))
    
    (thm (implies (and (p2 a c)
                       (p2 a b)
                       (p2 c d))
                  (p2 a d)))
    
    Consider what happens when the proof of the thm is attempted. The ACL2 rewriter attempts to apply rule p2-trans to the conclusion, (p2 a d). So, it binds variables x and z from the left-hand side of the conclusion of p2-trans to terms a and d, respectively, and then attempts to relieve the hypotheses of p2-trans. The first hypothesis of p2-trans, (p2 x y), is considered first. Variable y is free in that hypothesis, i.e., it has not yet been bound. Since x is bound to a, the rewriter looks through the context for a binding of y such that (p2 a y) is true, and it so happens that it first finds the term (p2 a b), thus binding y to b. Now it goes on to the next hypothesis, (p2 y z). At this point y and z have already been bound to b and d; but (p2 b d) cannot be proved.

    So, in order for the proof of the thm to succeed, the rewriter needs to backtrack and look for another way to instantiate the first hypothesis of p2-trans. Because :match-free :all has been specified, backtracking does take place. This time y is bound to c, and the subsequent instantiated hypothesis becomes (p2 c d), which is true. The application of rule (p2-trans) succeeds and the theorem is proved.

    If instead :match-free :all had been replaced by :match-free :once in rule p2-trans, then backtracking would not occur, and the proof of the thm would fail.

    Next we describe in detail the steps used by the rewriter in dealing with free variables.

    ACL2 uses the following sequence of steps to relieve a hypothesis with free variables, except that steps (1) and (3) are skipped for :forward-chaining rules and step (3) is skipped for :type-prescription rules. First, if the hypothesis is of the form (force hyp0) or (case-split hyp0), then replace it with hyp0.

    (1) Suppose the hypothesis has the form (equiv var term) where var is free and no variable of term is free, and either equiv is equal or else equiv is a known equivalence relation and term is a call of double-rewrite. We call this a ``binding hypothesis.'' Then bind var to the result of rewriting term in the current context.

    (2) Look for a binding of the free variables of the hypothesis so that the corresponding instance of the hypothesis is known to be true in the current context.

    (3) Search all enabled, hypothesis-free rewrite rules of the form (equiv lhs rhs), where lhs has no variables (other than those bound by let, let*, or lambda), rhs is known to be true in the current context, and equiv is typically equal but can be any equivalence relation appropriate for the current context (see congruence); then attempt to bind the free variables so that the instantiated hypothesis is lhs.

    If all attempts fail and the original hypothesis is a call of force or case-split, where forcing is enabled (see force) then the hypothesis is relieved, but in the split-off goals, all free variables are bound to unusual names that call attention to this odd situation.

    When a rewrite or linear rule has free variables in the hypotheses, the user generally needs to specify whether to consider only the first instance found in steps (2) and (3) above, or instead to consider them all. Below we discuss how to specify these two options as ``:once'' or ``:all'' (the default), respectively.

    Is it better to specify :once or :all? We believe that :all is generally the better choice because of its greater power, provided the user does not introduce a large number of rules with free variables, which has been known to slow down the prover due to combinatorial explosion in the search (Steps (2) and (3) above).

    Either way, it is good practice to put the ``more substantial'' hypotheses first, so that the most likely bindings of free variables will be found first (in the case of :all) or found at all (in the case of :once). For example, a rewrite rule like

    (implies (and (p1 x y)
                  (p2 x y))
             (equal (bar x) (bar-prime x)))
    
    may never succeed if p1 is nonrecursive and enabled, since we may well not find calls of p1 in the current context. If however p2 is disabled or recursive, then the above rule may apply if the two hypotheses are switched. For in that case, we can hope for a match of (p2 x y) in the current context that therefore binds x and y; then the rewriter's full power may be brought to bear to prove (p1 x y) for that x and y.

    Moreover, the ordering of hypotheses can affect the efficiency of the rewriter. For example, the rule

    (implies (and (rationalp y)
                  (foo x y))
             (equal (bar x) (bar-prime x)))
    
    may well be sub-optimal. Presumably the intention is to rewrite (bar x) to (bar-prime x) in a context where (foo x y) is explicitly known to be true for some rational number y. But y will be bound first to the first term found in the current context that is known to represent a rational number. If the 100th such y that is found is the first one for which (foo x y) is known to be true, then wasted work will have been done on behalf of the first 99 such terms y -- unless :once has been specified, in which case the rule will simply fail after the first binding of y for which (rationalp y) is known to be true. Thus, a better form of the above rule is almost certainly the following.
    (implies (and (foo x y)
                  (rationalp y))
             (equal (bar x) (bar-prime x)))
    

    Specifying `once' or `all'.

    As noted above, the following discussion applies only to rewrite, linear, and forward-chaining rules. See free-variables-type-prescription for a discussion of analogous considerations for type-prescription rules.

    One method for specifying :once or :all for free-variable matching is to provide the :match-free field of the :rule-classes of the rule, for example, (:rewrite :match-free :all). See rule-classes. However, there are global events that can be used to specify :once or :all; see set-match-free-default and see add-match-free-override. Here are some examples.

    (set-match-free-default :once)    ; future rules without a :match-free field
                                      ; are stored as :match-free :once (but this
                                      ; behavior is local to a book)
    (add-match-free-override :once t) ; existing rules are treated as
                                      ; :match-free :once regardless of their
                                      ; original :match-free fields
    (add-match-free-override :once (:rewrite foo) (:rewrite bar . 2))
                                      ; the two indicated rules are treated as
                                      ; :match-free :once regardless of their
                                      ; original :match-free fields
    

    Some history. Before Version 2.7 the ACL2 rewriter performed Step (2) above first. More significantly, it always acted as though :once had been specified. That is, if Step (2) did not apply, then the rewriter took the first binding it found using either Steps (1) or (3), in that order, and proceeded to relieve the remaining hypotheses without trying any other bindings of the free variables of that hypothesis.




    acl2-sources/doc/HTML/FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS.html0000664002132200015000000011172312222333515022540 0ustar kaufmannacl2 FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS.html -- ACL2 Version 6.3

    FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS

    some questions newcomers frequently ask
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    This FAQ is for people who've read through all the other sections of the tutorial introduction to the theorem prover (see introduction-to-the-theorem-prover and all the links from it that are not marked with the little warning sign (``''). Do not expect to understand our answers if you haven't taken the time to read through the tutorial. In the answers below you will see more links into the hypertext reference manual. While such links were marked ``'' in the tutorial, they are not marked that way here. When you enter the reference manual be prepared to explore and assemble a mini-course on the topic of interest, not a quick fix.

    Q. How do I find something in the ACL2 documentation? A. Try going to the Search link on the ACL2 home page.

    Q. How does the theorem prover work? A. We really don't think you need to know much about the inner workings of the prover to become an effective user. That doesn't mean the system is self-explanatory! It means that stuff you need to learn is not how the theorem prover works but how to interact with it! That is what introduction-to-the-theorem-prover is about. However, if you want the most basic overview of the prover, see architecture-of-the-prover.

    Q. How do I define a new function? A. See defun.

    Q. How do I define a new predicate? A. See defun.

    Q. How do I define a new relation? A. See defun.

    Q. How do I define a function or predicate that takes a varying number of arguments? A. You can't. However, see defmacro to learn how to define a macro that takes a varying number of arguments and expands into an arbitrary term that you compute.

    Q. How do I define a macro that is sensitive to the state? A. You can't. However, advanced users should consider make-event.

    Q. How do I define mutually recursive functions? A. See mutual-recursion. However, you should realize that when two functions, say f and g, are mutually recursive, properties of f generally have to be stated simultaneously with properties of g, since inductive proofs about one function require inductive hypotheses about the other. Furthermore, ACL2 does not know how to do inductions for mutually recursive functions and must be told. See mutual-recursion-proof-example.

    Q. How do I declare the type signature of a function? A. You can't. ACL2 is a syntactically untyped language and all functions are defined on all objects in the ACL2 universe. We recommend that the new user get used to this and only then explore the use of ACL2 to express and enforce a type regime. In ACL2, the guard of a function is akin to the type signature of a function in typed languages. However, ACL2 guards may be arbitrary terms, not just type conditions, they only concern the inputs to the function (not its result), and do not affect the axiom defining the function -- all functions are defined on every combination of objects. You may, of course, prove theorems that establish that every function called in a definition or theorem is supplied with input satisfying its guards (which necessarily involves describe the outputs too). These formulas are called guard conjectures and the process of proving them is called guard verification. Since guards are arbitrary ACL2 formulas, the ``type regimes'' one tends to enforce in ACL2 can be much for flexible and expressive than those in most programming languages. However, that expressibility also means guard verification can be challenging (indeed undecidable). On the other hand, if one limits oneself to simple type-like guards, lemmas can be proved that make most guard verification fully automatic and one can configure ACL2 to do guard verification automatically at defun-time. One may also delay guard verification until ``the right'' lemmas have been proved. By doing guard verification one can make functions execute faster by allowing the code to avoid runtime checks. This is especially valuable to industrial users who have large models used both in verification and as simulation engines for designed artifacts. In addition, guard verification can give you the assurance that you are using your functions within their intended domains and hence is a form of functional specification and verification. However, all these advantages aside, it is remarkably easy to drift away from the simplest type regimes and write a guard that raises deep mathematical problems. New users should not try to confront these problems until they are comfortable with the theorem prover and with lemma development. Therefore, we strongly recommend that you forget about types and guards and get used to reasoning about total functions. When you do decide to learn about them, be prepared for a complex story involving specification, execution efficiency, and proof management. See guard.

    Q. How do I tell defun what measure to use? A. See xargs, specifically :measure.

    Q. I specified a measure that always returns a natural number but ACL2 is acting like it's not a natural number. A. There are two likely problems. The most likely one is that your measure isn't really always a natural! Suppose the formals of your defun are x and y and your measure is (m x y). Suppose the recursive calls of your function are protected by tests that insure that x and y are naturals. Then you might assume x and y are naturals in the measure. But ACL2 has to prove (o-p (m x y)), where o-p is the predicate that recognizes ordinals (and naturals are ordinals). Note that the theorem doesn't have any hypotheses! You might intuitively think that your measure has to be an ordinal just under the conditions that lead to recursive calls. That's not what ACL2 enforces. It has to be an ordinal, always. So you must change your specified measure. For example, consider wrapping nfix around it or around its uses of x and y to coerce those quantities to naturals. The second most likely explanation is that your measure returns a natural, always, but ACL2 doesn't know that and it takes induction to prove. This might happen if m involves some recursive functions. In this case, prove (natp (m x y)) before your defun. Perhaps you should consider making the natp lemma a :type-prescription lemma to make ACL2's typing algorithm aware of it.

    Q. How do I tell defun what well-founded relation to use? A. See xargs, specifically :well-founded-relation.

    Q. How do I show that a relation is well-founded? A. Prove a theorem establishing that there is an order preserving embedding into the ordinals and store it with :rule-classes :well-founded-relation.

    Q. What is an ordinal? What does it mean to be well-founded? A. Ordinals are an extension of the natural numbers used to insure that a process can't go on forever. Like naturals, they can be added, multiplied, and exponentiated. There is a sense of one ordinal being less than another. Unlike the naturals, each of which is finite, the ordinals include infinite objects. Now imagine ``standing'' on an ordinal and ``stepping'' to a smaller one. Like the naturals, this ``walk down the ordinals'' can't go on forever, even if you start on an infinite ordinal. That is because the ordinals are well-founded. See o-p for more information about ordinals in ACL2 and about well-foundedness. See ordinals for a deeper discussion and a discussion of books that can help configure ACL2 to reason about ordinals.

    Q. How can provide hints for the termination proofs in defun? A. See xargs, specifically :hints (for the termination proofs) and :guard-hints (for the guard verification proofs).

    Q. How do I define a constant (something like a global variable)? A. See defconst. But remember that as an applicative programming language, ACL2 does not have global variables! You can define a symbol to have a fixed value and use the symbol sort of like a global variable in function definitions: you may refer to the value of the symbol in your functions without passing the variable in as formal parameter. But you may not ever change the value of the symbol!

    Q. How do I save the value of a top-level computation for future use? A. See assign and see @.

    Q. How do I introduce new syntactic form or abbreviation? A. See defmacro.

    Q. How can create and modify an array? A. ACL2 is a functional language, so it is impossible to destructively modify an existing object; technically, all ``updates'' to objects must be implemented by ``copy-on-write'' semantics. That said, ACL2 provides support for arrays, provided you use them in a restricted way. They give you constant-time access and change under the use restrictions.

    Q. How do I read from or write to a file? How do I do IO? A. To manipulate files, your function must have state as an argument, so you should read about the restrictions that imposes. For input/output facilities, see io.

    Q. How do I define a structure that can be destructively modified? A. ACL2 is an applicative programming language. You can't modify objects arbitrarily! You basically have to ``copy on write,'' which means you construct new objects from old ones, making the changes you want in the new one. If the car of some object is 1 at one moment and 2 later, then the basic logical axiom (car x) = (car x) is violated! However, if the only reference to the old object, e.g., x, was to pass it to the code that copied and ``changed'' it, then ACL2 can re-use the old object to produce the new one and the axioms would not object. Such syntactic restrictions can make x a modifiable structure but they will impose a heavy burden on you as a programmer: if pass such an x to a function and the function modifies it, then you must pass x only to that function and you must return the modified value and use it henceforth. Such objects are said to be single threaded. See defstobj.

    Q. How do I write a universal quantifier? An existential quantifier? How can I say ``for all'' or ``there exists''? A You can't literally write quantifiers. But ACL2 has the power of full first order logic with quantification. See quantifiers.

    Q. How do I introduce an undefined or uninterpreted function symbol? Can I constrain it to have certain properties? A. See encapsulate.

    Q. How can I hide a lemma? I want to prove a lemma temporarily to use in another proof but I don't want the lemma around thereafter. A. One way to get a similar effect is to prove the lemma and then disable it with an (in-theory (disable ...)) event; see in-theory. Another way is to put the lemma and the theorem that needs it into an encapsulate and wrap a local around the lemma.

    Q. What is an event? A. An event is a command that adds information to the ACL2 database (the ``logical world''), like defun or defthm. See events.

    Q. How do I say that I do not want a rewrite rule generated from a theorem? A. The command

    (defthm name formula
            :rule-classes nil)
    
    will attempt to prove formula and, if successful, give formula the name name, which you may use in hints as a theorem, but it will build no rules from the formula.

    Q. How do I say that I want a formula to be stored as a rewrite rule? A. The command

    (defthm name formula)
    
    will attempt to prove formula and, if successful, it will give formula the name name and generate a rewrite rule from it, with the runic name (:rewrite name)]. It could happen that formula generates more than one rewrite rule, e.g., this happens if the conclusion is an AND. In this case, each conjunctive branch through the conclusion generates a rule named (:rewrite name . i), for i=1,2,... For more details, see rewrite.

    Q. How do I say that I want a formula to be stored as a rewrite rule and some other kinds of rules? A. The command

    (defthm name formula
            :rule-classes (:class1 ... classk))
    
    will attempt to prove formula and, if successful, it will give formula the name name and generate a rule of each :classi specified. Each :classi should either be a keyword, like :REWRITE or :GENERALIZE, naming a rule class (see rule-classes), or else should be a list that starts with a rule class and then lists the relevant modifiers. Be sure to include :REWRITE among the rule classes if you want the formula to generate a rewrite rule. It doesn't do that automatically if you explicitly specify :rule-classes!

    Q. How do I rearrange the shape of a formula before generating a rule from it? A. See rule-classes and read about the :corollary modifier.

    Q. What is a type-prescription? A. ACL2 has an algorithm for determining the type of object returned by a term, where a type is one of the Common Lisp primitive datatypes such as natural, integer, Boolean, cons, true-listp, etc. Rules provided by you can influence this algorithm. See type-prescription.

    Q. How do rewrite rules work? A. Re-read the tutorial sections: introduction-to-rewrite-rules-part-1 and introduction-to-rewrite-rules-part-2.

    Q. How do I see what's in the database? A. You can't look at the entire database with user-friendly tools. You can print the command that introduced a particular name, print the entire sequence of user commands, print the commands in the region between two commands, print all the rewrite rules that apply to a given term or function symbol, and many other options. See history. If you have loaded a book from another user, you might wish to read the source file. For example, the source file for (include-book "arithmetic-5/top" :dir :system) is the file named arithmetic-5/top.lisp on the acl2-sources/books/ directory where ever your ACL2 sources are installed. Often, books are well-documented by comments by their authors. Some books have Readme or README files on the same directory.

    Q. How do I undo a command? A. See history, especially see u (``undo'') and see ubt (``undo back through''). Q. What rewrite rules match a given term? A. See pl.

    Q. What were those questions to ask when looking at key checkpoints? A. See introduction-to-key-checkpoints.

    Q. How do I figure out why a rewrite rule won't fire? A. If you activate rewrite rule monitoring (see brr) and then install a monitor (see monitor) on the rule in question, ACL2 will enter an interactive break whenever the pattern of the rule is matched against a target in a proof. So after installing the monitor, re-try the proof and then interact with the rewriter via break commands (see brr-commands). Like all trace and break packages, you have to know what you're doing to use the break rewrite facility, so read the material in the reference manual. If no interactive break happens after you've installed the monitor on your rule and re-tried the proof, it means no suitable target ever arose. Don't forget to turn off monitoring when you're done as it slows down the system.

    Q. Why is a proof taking so long? A. Unexpectedly poor performance on simple problems is usually a sign of cyclic rewriting or combinatoric explosion. See dmr and see accumulated-persistence.

    Q. How do I tell ACL2 what induction to do for a particular formula? A. When issuing the defthm command for the formula, supply an :induct hint:

    (defthm name
            formula
            :hints (("Goal" :induct (f x1 ... xn))))
    
    where f is a function that recurs the way you want the induction to unfold and x1 ... xn are the relevant variables in formula. You usually have to define f appropriately for each formula that needs an induct hint, though sometimes you can re-use an earlier f or one of the functions in the formula itself. It doesn't matter what value (f x1 ... xn) returns. All that matters is how it recurs. The termination analysis for f justifies the induction done. See hints, especially the section on :induct hints; also see induction.

    Q. ACL2 doesn't know simple arithmetic that can simplify the term (+ 1 -1 x). A. You should load an arithmetic book whenever you're dealing with an arithmetic problem. The simplest arithmetic book is typically loaded with the event (include-book "arithmetic/top-with-meta" :dir :system). If you're using floor and mod or non-linear arithmetic like (* x y) you should use (include-book "arithmetic-5/top" :dir :system). See also the discussion of arithmetic books under the ``Lemma Libraries and Utilities'' link of the ACL2 home page, and see community-books.

    Q. ACL2 is not using an arithmetic lemma that I proved. A. Lemmas concluding with arithmetic inequalities, like

    (implies (member e x)
             (< (len (delete e x)) (len x)))
    
    are not good rewrite rules because they rarely match targets because of intervening arithmetic operators. For example, the above conclusion doesn't match (< (LEN (DELETE E X)) (+ 1 (LEN X))). You should store such lemmas as :linear rules by using the command:
    (defthm len-delete
      (implies (member e x)
               (< (len (delete e x)) (len x)))
      :rule-classes :linear)
    
    See linear.

    Q. What is a linear rule? A. See linear.

    Q. How do I make ACL2 treat a relation as an equality? A. We assume you mean to treat the relation as an equivalence, i.e., replace one term by another when they are equivalent under your relation. See equivalence, see congruence, and see refinement.

    Q. One of my rewrite rules has a hypothesis that doesn't rewrite to true. What do I do? A. Prove a rewrite rule that establishes that hypothesis under the relevant assumptions from the context of the intended target. Alternatively, undo the rule and restate it with a force around the problematic hypothesis, making ACL2 assume the hypothesis when the rule is applied and raising the truth of the hypothesis as an explicit subgoal of the proof. See also case-split. Of course, you should always state the strongest rewrite rules you can think of, eliminating hypotheses or shifting them to the right-hand side inside of IFs; see strong-rewrite-rules.

    Q. How do I make ACL2 ``guess'' the right instantiation of a free variable? A. You can provide a :restrict hint that names the problematic lemma and provides an instantiation of the free variable. See hints, specifically :restrict. You could alternatively give a hint that :uses the rule and provides the appropriate instantiation in full. See hints, specifically :use. Recall that ACL2 guesses free variable instantiations by matching the problematic hypothesis to the assumptions in the context of the target. If the appropriate assumption is present but ACL2 is finding another one, try undoing the problematic rule and proving it again, specifying the :match-free :all modifier of the :rewrite or :linear rule class. See rule-classes. Alternatively, undo and prove the problematic rule again and use a bind-free form to compute the appropriate instantiation.

    Q. How can I make ACL2 do a case split to prove a certain subgoal? A. See hints, specifically :cases.

    Q. How can I prevent ACL2 from using a rewrite rule? A. See hints, specifically :in-theory (disable ...). If the use of the rule is problematic in only one subgoal and the lemma is needed in other subgoals, disable the lemma only in the problematic subgoal by specifying the subgoal name (e.g., "Subgoal 1/3.2.1") as the goal specifier in the hint. If the rule isn't needed anywhere in the proof, you could use the specifier "Goal". If you don't think the rule will ever be needed for a while, you could globally disable it with the event (in-theory (disable ...)) (see in-theory) executed before the first problematic proof. If the rule has never been used or must always be disabled, undo it and prove it again with :rule-classes nil.

    Q. How can I prevent ACL2 from running a definition on constants? I tried disabling the function but that didn't work. A. If you have a function named f then disabling f will disable the definitional axiom about f. But ACL2 has another kind of rule about f, telling it how to evaluate f. The rune of this rule is (:executable-counterpart f). Try disabling that, as in the :hints ((... :in-theory (disable (:executable-counterpart f)) ...c[)).

    Q. How can I make ACL2 use a rule in a proof? A. See hints, specifically :use.

    Q. How can I make ACL2 expand a function call in a proof? A. You can give an :See expand hint.

    Q. ACL2 sometimes aborts the proof attempt before showing me all of the subgoals. How can I make it just keep going instead of aborting early? A. See otf-flg, which stands for Onward Thru the Fog FLaG.

    Q. How can I compute when I want a rule to fire? A. See syntaxp.

    Q. How can I add pragmatic advice to a lemma after it has been proved? For example, how can add a forced hypothesis, a backchain limit, or a syntaxp test? A. You can't. You can undo the lemma, restate it appropriately, and prove it again. This produces the cleanest database. Alternatively, you can prove the restated lemma under a new name -- a task that should be easy since the old version is in the database and will presumably make the proof trivial -- and then disable the old name.

    Q. How can I stop ACL2 from rewriting a term? A. If you need rewriting done but want to prevent some particular terms from being rewritten, see hints, specifically :hands-off. Alternatively, consider embedding the problematic term in a hide. Users sometime develop special theories (see theory) containing just the rules they want and then use hints to switch to those theories on the appropriate subgoals.

    Q. Can I compute which subgoals a hint refers to? A. Yes, see computed-hints. This topic is for advanced users but knowing that it is possible might come in handy someday.

    Q. I want the rewriter to always use one theory and then switch to another so that it doesn't use my most complicated before until doing the simple things. A. This is possible but is something for the advanced user. It can be done using a form of computed-hints. See using-computed-hints-7.

    Q. Is there a way to attach the same hint to every defthm? A. See default-hints.

    Q. How can I just tell ACL2 the proof steps? A. See verify and see proof-checker.

    Q. How can I write my own simplifier? A. See meta.

    Q. How can I add an axiom or just assume some lemma without proof? A. This is very dangerous but is a good strategy for exploring whether or not a certain set of lemmas (and their rules) is sufficient to prove your goal. See defaxiom and see skip-proofs.

    Q. How can redefine a user-defined function? A. This is tricky. What if you've already proved theorems about the old definition and then wish to change it? There are several options. See ld-redefinition-action (and note specifically the discussion of updater function for it, set-ld-redefinition-action); also see redef, see redef!, see redef+, and see redef-.

    Q. How do I change a function from :program mode to :logic mode? A. See verify-termination.

    Q. How do I change the guards on a function? A. You can't. Undo it and redefine it.

    Q. What is program mode? A. See program.

    Q. What does the ACL2 prompt mean? A. See introduction-to-a-few-system-considerations or, specifically, see prompt.

    Q. What is logic mode? A. See logic.

    Q. How do I get into or out of :program mode? :Logic mode? A. See program and see logic. You can enter these modes temporarily for a particular defun by using (declare (xargs :mode :program)) or (declare (xargs :mode :logic)) after the list of formal parameters to the definition.

    Q. How do I quit from ACL2? A. This varies depending on the interface you're using. See introduction-to-a-few-system-considerations.

    Q. How do I load a file of definitions and theorems created by someone else? A. See include-book.

    Q. How do I create my own book of definitions and theorems? A. See books.

    Q. Where are the books referenced by :dir :system on my machine? A. If your ACL2 is installed on the directory dir/acl2-sources and you follow the standard installation instructions, then the books are typically the files under the directory dir/acl2-sources/books/.

    Q. How can I find out what books are available? A. Go to the ACL2 home page, http://www.cs.utexas.edu/u/moore/acl2/ and look at the link labeled ``Lemma Libraries and Utilities.''

    Q. How do I produce my own book? A. See books.

    Q. What is a decision procedure? What decision procedures does ACL2 have? A. A decision procedure is an algorithm that given enough time and space can determine, for all the formulas in a certain syntactic class of formulas, whether the formula is a theorem or not. The most well-known decision procedure is for propositional calculus: by testing a formula under all the combinations Boolean assignments to the variables, one can decide if a propositional formula is a theorem. The syntactic class consists of all formulas you can write using just variables, constants, and compositions of the functions and, or, not, implies, iff, and if. There are, of course, an exponential number of different assignments so the algorithm can be slow. ACL2 contains a propositional decision procedure based on symbolic normalization that can be faster than trying all the combinations of truthvalues -- but is not guaranteed to be faster. ACL2 also contains an optional bdd procedure. ACL2 also contains a decision procedure for rational arithmetic involving variables, rational constants, addition, multiplication by rational constants, equality, and the various versions of arithmetic inequality (<, <=, >=, and >). It can be extended with :linear lemmas. ACL2 is complete for equality over uninterpreted (e.g., undefined and unconstrained) function symbols using an algorithm based on transitive closure of equivalence classes under functional substitutivity. Finally, you can make ACL2 use other decision procedures, even ones implemented outside of ACL2; see clause-processor.

    Q. ACL2 has the change of variable trick (destructor elimination) that it does to get rid of (CAR X) and (CDR X) by replacing x by (CONS A B). Is there a way to make ACL2 do that for other terms? A. Yes. See elim.

    Q. How can I prevent destructor elimination? A. See hints, specifically :do-not '(eliminate-destructors).

    Q. How can I prevent cross-fertilization? A. See hints, specifically :do-not '(fertilize).

    Q. How can I prevent generalization? A. See hints, specifically :do-not '(generalize).

    Q. How can I make ACL2 impose a restriction on a new variable introduced by destructor elimination or generalization? A. See generalize.

    Q. What rule classes are there? A. See rule-classes.

    Q. What is a theory? A. See theories.

    Q. How are hints inherited by the children of a subgoal? A. See hints-and-the-waterfall.

    Q. How do I use ACL2 under Emacs? A. See emacs.

    Q. How do I use ACL2 under Eclipse? A. See ACL2-Sedan.

    Q. How do I interrupt the prover? A. The keyboard sequence for interrupting a running process depends your operating system, host Common Lisp, and user interface (e.g., Emacs, Eclipse, etc.). But perhaps a few examples will help you discover what you need to know. If your host Common Lisp is GCL or Allegro and you are typing directly to the Common Lisp process, then you can interrupt a computation by typing ``ctrl-c'' (hold down the Control key and hit the ``c'' key once). But other Lisps may require some other control sequence. If you are typing to an Emacs process which is running the GCL or Allegro Common Lisp process in a shell buffer, you should type ctrl-c ctrl-c -- that is, you have to type the previously mentioned sequence twice in succession. If you are running the ACL2 Sedan, you can use the Interrupt Session button on the tool bar. The environment you enter when you interrupt depends on various factors and basically you should endeavor to get back to the top level ACL2 command loop, perhaps by typing some kind of Lisp depenent ``abort'' command like A or :q, or :abort!. You can usually determine what environment you're in by paying attention to the prompt.

    Q. What is the ACL2 loop? A. That is the name given to the interactive environment ACL2 provides, a ``read-eval-print loop'' in which the user submits successive commands by typing ACL2 expressions and keyword commands. See introduction-to-a-few-system-considerations.

    Q. What is raw lisp? A. That is our name for the host Common Lisp in which ACL2 is implemented. See introduction-to-a-few-system-considerations. There is an ACL2 mode named raw mode which is different from ``raw lisp.'' See set-raw-mode.

    Q. Can I get a tree-like view of a proof? A. See proof-tree for an Emacs utility that displays proof trees and allows you to navigate through a proof from the proof tree. The ACL2 Sedan also supports proof trees and you should see the ACL2s documention on that topic.

    Q. I used the earlier Boyer-Moore theorem prover, Nqthm. How is ACL2 different? A. See nqthm-to-acl2.

    If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/FULL-BOOK-NAME.html0000664002132200015000000000413712222333516016766 0ustar kaufmannacl2 FULL-BOOK-NAME.html -- ACL2 Version 6.3

    FULL-BOOK-NAME

    book naming conventions assumed by ACL2
    Major Section:  BOOKS
    

    For this discussion we assume that the resident operating system is Unix (trademark of AT&T), but analogous remarks apply to other operating systems supported by ACL2, in particular, the Macintosh operating system where `:' plays roughly the role of `/' in Unix; see pathname.

    ACL2 defines a ``full book name'' to be an ``absolute filename string,'' that may be divided into contiguous sections: a ``directory string'', a ``familiar name'' and an ``extension''. See pathname for the definitions of ``absolute,'' ``filename string,'' and other notions pertaining to naming files. Below we exhibit the three sections of one such string:

    "/usr/home/smith/project/arith.lisp"
    
    "/usr/home/smith/project/"           ; directory string
                            "arith"      ; familiar name
                                 ".lisp" ; extension
    
    
    The sections are marked by the rightmost slash and rightmost dot, as shown below.
    "/usr/home/smith/project/arith.lisp"
                            |     |
                            slash dot
                            |     |
    "/usr/home/smith/project/"           ; directory string
                            "arith"      ; familiar name
                                 ".lisp" ; extension
    
    The directory string includes (and terminates with) the rightmost slash. The extension includes (and starts with) the rightmost dot. The dot must be strictly to the right of the slash so that the familiar name is well-defined and nonempty.

    If you are using ACL2 on a system in which file names do not have this form, please contact the authors and we'll see what we can do about generalizing ACL2's conventions.




    acl2-sources/doc/HTML/FUNCTION-THEORY.html0000664002132200015000000000364712222333531017215 0ustar kaufmannacl2 FUNCTION-THEORY.html -- ACL2 Version 6.3

    FUNCTION-THEORY

    function symbol rules as of logical name
    Major Section:  THEORIES
    

    Examples:
    (function-theory :here)
    (function-theory 'lemma3)
    
    See logical-name.

    General Form:
    (function-theory logical-name)
    
    Returns the theory containing all the :definition runes, whether enabled or not, that existed immediately after logical-name was introduced. See the documentation for theories, logical-name and executable-counterpart-theory.

    You may experience a fencepost problem in deciding which logical name to use. Deflabel can always be used to mark unambiguously for future reference a particular point in the development of your theory. The order of events in the vicinity of an encapsulate is confusing. See encapsulate.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/FUNCTIONAL-INSTANTIATION-EXAMPLE.html0000664002132200015000000001216212222333515021527 0ustar kaufmannacl2 FUNCTIONAL-INSTANTIATION-EXAMPLE.html -- ACL2 Version 6.3

    FUNCTIONAL-INSTANTIATION-EXAMPLE

    a small proof demonstrating functional instantiation
    Major Section:  TUTORIAL5-MISCELLANEOUS-EXAMPLES
    

    The example below demonstrates the use of functional instantiation, that is, the use of a generic result in proving a result about specific functions. In this example we constrain a function to be associative and commutative, with an identity or ``root,'' on a given domain. Next, we define a corresponding function that applies the constrained associative-commutative function to successive elements of a list. We then prove that the latter function gives the same value when we first reverse the elements of the list. Finally, we use functional instantiation to derive the corresponding result for the function that multiplies successive elements of a list.

    The details of the proof (such as the in-theory event and particulars of the lemmas) are not the point here. Rather, the point is to demonstrate the interaction of encapsulate events and :functional-instance lemma-instances. Of course, if you are interested in details then you may find it helpful to run these events through ACL2.

    Also see constraint for more about :functional-instance and see lemma-instance for general information about the use of previously-proved lemmas.

    (in-package "ACL2")
    
    (encapsulate
     (((ac-fn * *) => *)
      ((ac-fn-domain *) => *)
      ((ac-fn-root) => *))
     (local (defun ac-fn (x y) (+ x y)))
     (local (defun ac-fn-root () 0))
     (local (defun ac-fn-domain (x) (acl2-numberp x)))
     (defthm ac-fn-comm
       (equal (ac-fn x y)
              (ac-fn y x)))
     (defthm ac-fn-assoc
       (equal (ac-fn (ac-fn x y) z)
              (ac-fn x (ac-fn y z))))
     (defthm ac-fn-id
       (implies (ac-fn-domain x)
                (equal (ac-fn (ac-fn-root) x)
                       x)))
     (defthm ac-fn-closed
       (and (ac-fn-domain (ac-fn x y))
            (ac-fn-domain (ac-fn-root)))))
    
    ;;;;;;;;;;;;;;;;;;;;;;;
    ; End of encapsulate. ;
    ;;;;;;;;;;;;;;;;;;;;;;;
    
    ; Define a ``fold'' function that iteratively applies ac-fn over a list.
    (defun ac-fn-list (x)
      (if (atom x)
          (ac-fn-root)
        (ac-fn (car x)
               (ac-fn-list (cdr x)))))
    
    ; Recognize lists all of whose elements are in the intended domain.
    (defun ac-fn-domain-list (x)
      (if (atom x)
          t
        (and (ac-fn-domain (car x))
             (ac-fn-domain-list (cdr x)))))
    
    ; Define a list reverse function.
    (defun rev (x)
      (if (atom x)
          nil
        (append (rev (cdr x))
                (list (car x)))))
    
    ; The following is needed for proving ac-fn-list-append, which is
    ; needed for proving ac-fn-list-rev.
    (defthm ac-fn-list-closed
       (ac-fn-domain (ac-fn-list x)))
    
    ; Needed for proving ac-fn-list-rev:
    (defthm ac-fn-list-append
      (implies (and (ac-fn-domain-list x)
                    (ac-fn-domain-list y))
               (equal (ac-fn-list (append x y))
                      (ac-fn (ac-fn-list x)
                             (ac-fn-list y)))))
    
    ; Needed for proving ac-fn-list-rev:
    (defthm ac-fn-domain-list-rev
      (equal (ac-fn-domain-list (rev x))
             (ac-fn-domain-list x)))
    
    ; The following is a good idea because without it, the proof attempt
    ; for ac-fn-list-rev (see just below) fails with the term (HIDE
    ; (AC-FN-LIST NIL)).  It is often a good idea to disable
    ; executable-counterparts of functions that call constrained
    ; functions.
    (in-theory (disable (:executable-counterpart ac-fn-list)))
    
    (defthm ac-fn-list-rev
      (implies (ac-fn-domain-list x)
               (equal (ac-fn-list (rev x))
                      (ac-fn-list x))))
    
    ; Our goal now is to apply functional instantiation to ac-fn-list-rev
    ; in order to prove the corresponding theorem, times-list-rev, based
    ; on * instead of ac-fn.
    
    (defun times-list (x)
      (if (atom x)
          1
        (* (car x)
           (times-list (cdr x)))))
    
    (defun acl2-number-listp (x)
      (if (atom x)
          t
        (and (acl2-numberp (car x))
             (acl2-number-listp (cdr x)))))
    
    ; The following relies on the following built-in rules for * (whose
    ; statements correspond directly to their names): commutativity-of-*,
    ; associativity-of-*, and unicity-of-1.
    
    (defthm times-list-rev
      (implies (acl2-number-listp x)
               (equal (times-list (rev x))
                      (times-list x)))
      :hints (("Goal"
               :use
               ((:functional-instance
                 ac-fn-list-rev
                 ;; Instantiate the generic functions:
                 (ac-fn *)
                 (ac-fn-root (lambda () 1))
                 (ac-fn-domain acl2-numberp)
                 ;; Instantiate the other relevant functions:
                 (ac-fn-list times-list)
                 (ac-fn-domain-list acl2-number-listp))))))
    




    acl2-sources/doc/HTML/FUNCTIONAL-INSTANTIATION-IN-ACL2R.html0000664002132200015000000000274212222333521021543 0ustar kaufmannacl2 FUNCTIONAL-INSTANTIATION-IN-ACL2R.html -- ACL2 Version 6.3

    FUNCTIONAL-INSTANTIATION-IN-ACL2R

    additional requirements for :functional-instance hints in ACL2(r)
    Major Section:  MISCELLANEOUS
    

    This documentation topic relates to ACL2(r), the modification of ACL2 that supports the real numbers (see real).

    See hints and see lemma-instance for a discussion of :use hints that employ the :functional-instance keyword. Here, we document additional requirements for such hints that applies to ACL2(r). We assume familiarity with lemma instances; see lemma-instance.

    (1) When functionally instantiating a non-classical formula, it is illegal to use pseudo-lambda expressions in a lemma instance.

    (2) A classical function symbol must be bound either to a classical function symbol or to a lambda (or, if allowed, pseudo-lambda) expression with a classical body. Similarly, a non-classical function symbol must be bound either to a non-classical function symbol or to a lambda (or, if allowed, pseudo-lambda) expression with a non-classical body.




    acl2-sources/doc/HTML/FURTHER-INFORMATION-ON-REWRITING.html0000664002132200015000000002011612222333515021534 0ustar kaufmannacl2 FURTHER-INFORMATION-ON-REWRITING.html -- ACL2 Version 6.3

    FURTHER-INFORMATION-ON-REWRITING

    a grab bag of advice and information on rewriting
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    In the following paragraphs we give some links to advanced topics, marked with ``''. If you are reading this topic as part of the tutorial on the theorem prover, do not follow these links upon your first reading. Just take note of the existence of the facilities and ideas mentioned.

    Arithmetic: If your goal theorem involves even trivial arithmetic, such as adding or subtracting 1, we recommend that you do

    (include-book "arithmetic/top-with-meta" :dir :system)
    
    which loads into ACL2 all the rules in one of the so-called ACL2 ``community books''. (Books are certified files of definitions, lemmas, etc., usually prepared by other ACL2 users and explicitly shared with the community. The ACL2 installation instructions suggest downloading the community books.) The book "top-with-meta" is the most elementary and most widely used arithmetic book. Other community books include "arithmetic-5/top" and various hardware and floating-point arithmetic books.

    Rules Concluding with Arithmetic Inequalities: If you are tempted to create a rewrite rule with an arithmetic inequality as its conclusion or left-hand side, think again. Inequalities such as

    (<= (len (delete e x)) (len x))
    
    make poor left-hand sides for rewrite rules. For example, the inequality above does not match the target
    (<= (LEN (DELETE E X)) (+ 1 (LEN X)))
    
    even though it is sufficient to prove the target (given some simple arithmetic). We recommend that if you have a theorem that establishes an arithmetic inequality, you make it a linear rule. See linear .

    Rearranging Formulas Before Making Rules: It is possible to rearrange the propositional structure of a proved formula before processing it as a rule. This allows you to state a theorem one way ``for publication'' and rearrange it to be stored as a more effective rule. See introduction-to-the-database (a tutorial topic you'll come to later) and its discussion of the concept of corollary. Also, see the discussion of corollary in rule-classes .

    Rewriting with New Equivalence Relations: You may introduce new equivalence relations, like ``set-equal'' or ``is-a-permutation'' and cause the rewriter to replace equivalents by equivalents in suitable contexts, where you use congruence rules to inform ACL2 of where these more relaxed notions of equivalence may be used; see equivalence and see congruence .

    Pragmatic Advice to Control Rules: You may attach various pragmas to a rule that allow you rather fine heuristic control over whether and how the rule is applied. For example, you may mark a hypothesis to be forced (see force ) meaning that the rule is to be applied even if that hypothesis is not relieved -- but if the proof is successful the system will turn its attention to all forced subgoals. You may similarly mark a hypothesis so as to cause a case split, allowing the relief of the hypothesis on one branch and spawning another branch explicitly denying the hypothesis; see case-split . You may add a bogus hypothesis that looks at the intended application of the rule and decides whether to apply the rule or not, performing an arbitrary computation on the syntactic context of the application; see syntaxp . By providing a :match-free modifier to the :rewrite rule declaration in your rule-classes, you may tell ACL2 to try all or only the first free variable value it guesses (see rule-classes ). You may provide a bogus hypothesis that computes from the syntactic environment the values to guess for the free variables in a rule; see bind-free . You may mark a term so that the rewriter does not dive into it; see hide .

    Programming Your Own Rewriter: If you cannot find a way to use rewrite rules to make the transformations you desire, you might investigate the use of metafunctions. A metafunction is just a little theorem prover of your own design. It takes as input a list structure representing a term and returns a list structure representing a term. If you can prove that the meaning of the input and output terms are equivalent, you can extend the ACL2 simplifier to call your metafunction. See meta .

    The Order in which Targets are Rewritten: The rewriter sweeps through terms ``inside-out'' otherwise known as ``left-most innermost first''. Thus, before trying to apply rewrite rules to (f a1 ... an), rules are applied to the ai. This has the good effect of normalizing the ai.

    This fact might help you understand why sometimes your rules ``don't seem to fire.'' For example, suppose you have a rule for rewriting (len (rev x)) to (len x) and suppose you wish to prove a theorem about (LEN (REV (CONS A B))). Suppose rev is defined in terms of append, as shown in programming-knowledge-taken-for-granted. Then you might see a checkpoint in which the (LEN (REV ...)) above has been simplified to (LEN (APPEND (REV B) (LIST A))) instead of to (LEN (CONS A B)). Why wasn't your rule about (len (rev x)) applied? The reason is that (REV (CONS A B)) rewrote to (APPEND (REV B) (LIST A)) before rules were applied to (LEN (REV ...)). You need a rule about (len (append x y)), as you will see from the checkpoint.

    The Order in which Rules are Tried: The rewriter tries the most recently proved rules first. For example, suppose f, g, and h are functions defined so that the following two theorems are provable and suppose you executed the following two events in the order shown:

    (defthm rule1 (equal (f (g x)) (h 1 x)))
    (defthm rule2 (equal (f (g x)) (h 2 X)))
    
    Then if rewrite rules are applied to (F (G A)), the result will be (H 2 A), because the latter rule, rule2, is applied first. It is generally best not to have conflicting rules or, at least, to understand how such conflicts are resolved. The system will warn you when you propose a rule that conflicts with an existing one.

    If you were reading this topic as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-rewrite-rules-part-2.




    acl2-sources/doc/HTML/FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM.html0000664002132200015000000001251512222333520022227 0ustar kaufmannacl2 FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM.html -- ACL2 Version 6.3

    FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM

    some tau system problems that we hope someone will address
    Major Section:  INTRODUCTION-TO-THE-TAU-SYSTEM
    

    The tau system is inexpressive compared to modern polymorphic type systems -- something that may be unavoidable in an untyped first-order language. However, we believe its effectiveness could be greatly increased by the development of some additional tools. We also believe that most of these tools could be provided by ACL2 users creating certified community books that exploit the basic tools already provided. It is likely the initial attempts to create such books will show the inadequacy of some of the current algorithms and data structures in the tau system and might point the way to improvements.

    As implementors of ACL2 our preference is to support the improvement of the core algorithms and data structures and provide customizable hooks allowing users to exploit them by developing effective and convenient interfaces. However, we want the further elaboration and optimization of the tau system to be based on user experience not just speculation.

    Some tools we think might help are listed below. We invite volunteers and further suggestions.

    A tau-centric signature notation for use in function definitions, exploited by a macro replacing defun so that input-output relationships phrased in tau terms are proved as :tau-system rules immediately after functions are introduced:

    We have, for example, experimented with a book defining a macro that allows the definition of the function ap (append) accompanied by a signature rule. Subsequent defsig events can add other signatures in the same notation.

    (defsig ap (true-listp * true-listp ==> true-listp) (x y)
       (if (consp x)
           (cons (car x) (ap (cdr x) y))
           y))
    
    (defsig ap (integer-listp * integer-listp ==> integer-listp))
    
    This experimental book provides succinct syntax for all tau signatures. For example, that book parses the signature
    (NATP (/= 3 5 7) (< 16) * TRUE-LISTP ==> BOOLEANP * INTEGER-LISTP * NATP)
    
    to be the signature of a function with two inputs and three outputs. The first input must be a natural number, different from 3, 5, and 7, and less than 16. The second input must be a true list. The first output is a boolean, the second a list of integers, and the third a natural.

    To express this signature for function fn as a formula requires significantly more typing by the user:

    (IMPLIES (AND (NATP X)
                  (NOT (EQUAL X 3))
                  (NOT (EQUAL X 5))
                  (NOT (EQUAL X 7))
                  (< X 16)
                  (TRUE-LISTP Y))
             (AND (BOOLEANP (MV-NTH 0 (FN X Y)))
                  (INTEGER-LISP (MV-NTH 1 (FN X Y)))
                  (NATP (MV-NTH 2 (FN X Y)))))
    

    We suspect that the provision of some succinct tau-centric notation (not necessarily the one above) for signatures at definition-time will mean users get more benefit from the tau system.

    Some tau inference mechanisms: This phrase suggests two different improvements. One is to implement a mechanism that adds or completes signatures for function definitions by exploiting knowledge of commonly used recursive schemas and the signatures of the subroutines in the body. For example, the definition of ap above rather obviously has the signature

    (integer-listp * integer-listp ==> integer-listp)
    
    and many others just based on the two recursive schemas that (a) collect certain elements from lists and (b) check that all elements have a certain property.

    The other ``tau inference'' improvement is to implement a mechanism for inferring relations between user-defined Booleans, perhaps by exploiting example generation, testing, and knowledge of recursive schemas. For example, it is fairly obvious that symbol-alistp implies alistp. Making the user state these relations invites omissions that render the tau system very unpredictable.

    A tau assistant: It would be useful to have a way to find out what tau rules are missing. Given a term that the user believes should ``obviously'' have some tau (``type'') what rules might be added to make the tau algorithm compute that expected tau? For example, if (g x) is known to satisfy P and (f x) is known to satisfy R when its argument satisfies S:

    g : T ==> P
    f : S ==> R
    
    then if the user asserts that (f (g x)) ``ought'' to have tau R, one plausible suggestion is the simple tau rule that (P x) implies (S x). Such assistance -- while still confronting an undecidable problem -- might be easier to implement within the tau framework than more generally in ACL2. (Many users have wanted such an assistant to suggest lemmas for the rewriter.)




    acl2-sources/doc/HTML/Flawed_Induction_Candidates_in_App_Example.html0000664002132200015000000000155712222333526025260 0ustar kaufmannacl2 Flawed_Induction_Candidates_in_App_Example.html -- ACL2 Version 6.3

    Flawed Induction Candidates in App Example

    Induction on a is unflawed: every occurrence of a in the conjecture

    (equal (app (app a b) c)
           (app a (app b c)))
    
    is in a position being recursively decomposed!

    Now look at the occurrences of b. The first (shown in bold below) is in a position that is held constant in the recursion of (app a b). It would be ``bad'' to induct on b here.

    (equal (app (app a b) c)
           (app a (app b c)))
    




    acl2-sources/doc/HTML/Free_Variables_in_Top-Level_Input.html0000664002132200015000000000424012222333526023357 0ustar kaufmannacl2 Free_Variables_in_Top-Level_Input.html -- ACL2 Version 6.3

    Free Variables in Top-Level Input

    ACL2 !>(equal (app (app a b) c)
                  (app a (app b c))))
    
    ACL2 Error in TOP-LEVEL:  Global variables, such as C, B, and A, are
    not allowed. See :DOC ASSIGN and :DOC @.
    
    

    ACL2 does not allow ``global variables'' in top-level input. There is no ``top-level binding environment'' to give meaning to these variables.

    Thus, expressions involving no variables can generally be evaluated,

    ACL2 !>(equal (app (app '(1 2) '(3 4)) '(5 6))
                  (app '(1 2) (app '(3 4) '(5 6))))
    (1 2 3 4 5 6)
    
    but expressions containing variables cannot.

    There is an exception to this rule. References to ``single-threaded objects'' may appear in top-level forms. See stobj . A single-threaded object is an ACL2 object, usually containing many fields, whose use is syntactically restricted so that it may be given as input only to certain functions and must be returned as output by certain functions. These restrictions allow single- threaded objects to be efficiently manipulated. For example, only a single copy of the object actually exists, even though from a logical perspective one might expect the object to be ``copied on write.''

    The most commonly used single-threaded object in ACL2 is the ACL2 system state, whose current value is always held in the variable state .

    ACL2 provides a way for you to use state to save values of computations at the top-level and refer to them later. See assign and @ .




    acl2-sources/doc/HTML/Functions_for_Manipulating_these_Objects.html0000664002132200015000000000262612222333526025147 0ustar kaufmannacl2 Functions_for_Manipulating_these_Objects.html -- ACL2 Version 6.3

    Functions for Manipulating these Objects

    Consider a typical ``stack'' of control frames.

    Suppose the model required that we express the idea of ``the most recent frame whose return program counter points into MAIN.''

    The natural expression of this notion involves

    function application -- ``fetch the return-pc of this frame''

    case analysis -- ``if the pc is MAIN, then ...''

    iteration or recursion -- ``pop this frame off and repeat.''

    The designers of ACL2 have taken the position that a programming language is the natural language in which to define such notions, provided the language has a mathematical foundation so that models can be analyzed and properties derived logically.

    Common Lisp is the language supported by ACL2. To be precise, a small applicative subset of Common Lisp is the language supported by ACL2.




    acl2-sources/doc/HTML/GAG-MODE.html0000664002132200015000000000112112222333521016060 0ustar kaufmannacl2 GAG-MODE.html -- ACL2 Version 6.3

    GAG-MODE

    verbosity of proof output
    Major Section:  MISCELLANEOUS
    

    Please see set-gag-mode for an explanation of gag-mode, which can take any of the following values:

    (gag-mode) ; generally evaluates to t, nil, or :goals
    




    acl2-sources/doc/HTML/GC$.html0000664002132200015000000000235212222333521015304 0ustar kaufmannacl2 GC$.html -- ACL2 Version 6.3

    GC$

    invoke the garbage collector
    Major Section:  MISCELLANEOUS
    

    This function is provided so that the user can call the garbage collector of the host Lisp from inside the ACL2 loop. Specifically, a call of gc$ is translated into a call of a function below on the same arguments.

    Allegro CL:            excl:gc
    CCL                    ccl::gc
    CLISP                  ext:gc
    CMU Common Lisp        system::gc
    GCL                    si::gbc
    LispWorks              hcl::gc-generation [default argument list:
                                               (7) for 64-bit OS, else (3)]
    SBCL                   sb-ext:gc
    
    The arguments, if any, are as documented in the underlying Common Lisp. It is up to the user to pass in the right arguments, although we may do some rudimentary checks.

    Also see gc-verbose.

    Evaluation of a call of this macro always returns nil.




    acl2-sources/doc/HTML/GC-VERBOSE.html0000664002132200015000000000226012222333521016341 0ustar kaufmannacl2 GC-VERBOSE.html -- ACL2 Version 6.3

    GC-VERBOSE

    control printing from the garbage collector
    Major Section:  MISCELLANEOUS
    

    General Form:
    (gc-verbose arg)
    

    Garbage collection (gc) is performed by every Lisp implementation; see gc$. However, different ACL2 builds might see more or fewer messages. This macro is intended to provide an interface for controlling the verbosity, which is off if the argument evaluates to nil and otherwise is on.

    The above functionality is only supported for the following host Common Lisp implementations: CCL, CMUCL, and GCL. Otherwise, the only effect of this macro is to print a notice that it is not supported. You are welcome to contact the ACL2 developers if you would like to help in adding such support for another host Common Lisp.

    Evaluation of a call of this macro always returns nil.




    acl2-sources/doc/HTML/GCL.html0000664002132200015000000001147112222333521015356 0ustar kaufmannacl2 GCL.html -- ACL2 Version 6.3

    GCL

    tips on building and using ACL2 based on Gnu Common Lisp
    Major Section:  MISCELLANEOUS
    

    See the installation instructions for basic information about building ACL2 on top of GCL, including information about where to fetch GCL. Here, we provide some tips that may be useful.

    1. You can place forms to evaluate at start-up into file init.lsp in the directory where you are starting ACL2 (GCL), or into file acl2-init.lsp in your home directory. For example, in order to evaluate both of the lisp forms mentioned in 2 below, you could put them both into init.lsp in the current directory or in ~/acl2-init.lsp (either way, without (lp) or :q):

    (setq si::*optimize-maximum-pages* nil)
    (si::allocate 'cons 75000 t)
    
    Note that if you want to put ACL2 patches in this file, you should precede them with (in-package "ACL2").

    2. Suppose you run out of space, for example with an error like this:

       Error: The storage for CONS is exhausted.
         Currently, 59470 pages are allocated.
         Use ALLOCATE to expand the space.
    
    The following suggestion from Camm Maguire will minimize the heap size, at the cost of more garbage collection time.
    :q   ; exit the ACL2 loop
    (setq si::*optimize-maximum-pages* nil)
    (lp) ; re-enter the ACL2 loop
    
    A second thing to try, suggested by several people, is to preallocate more pages before the run, e.g.:
    :q   ; exit the ACL2 loop
    (si::allocate 'cons 75000 t)
    (lp) ; re-enter the ACL2 loop
    
    Also see reset-kill-ring for a suggestion on how to free up space.

    3. Windows users have seen this error:

    cc1.exe: unrecognized option `-fno-zero-initialized-in-bss'
    
    Camm Maguire suggests that a solution may be to evaluate the following in GCL before building ACL2.
    (in-package 'compiler)
    (let* ((x `-fno-zero-initialized-in-bss')
           (i (search x *cc*)))
            (setq *cc* (concatenate 'string
                                    (subseq *cc* 0 i)
                                    (subseq *cc* (+ i (length x))))))
    

    4. It is possible to profile using ACL2 built on GCL. See file save-gprof.lsp in the ACL2 source directory.

    5. Some versions of GCL may have garbage-collector bugs that, on rare occasions, cause ACL2 (when built on GCL) to break. If you run into this, a solution may be to execute the following:

    :q
    (si::sgc-on nil)
    (lp)
    
    Alternatively, put (si::sgc-on nil) in your ~/acl2-init.lsp file.

    A full regression test and found that this decreased performance by about 10%. But even with that, GCL is probably one of the faster Common Lisp implementations for ACL2 on Linux. Performance figures may often be found by following the ``Recent changes'' link on the ACL2 home page.

    6. GCL operations on numbers can sometimes be sped up, perhaps by up to two orders of magnitude, by suitable declare forms (also see type-spec). The following example, developed with Warren Hunt and Serita Nelesen, illustrates the use of such declarations.

    ; File iplus.lisp:
    ; Operations on naturals together with positive infinity (represented as -1).
    
    ; After (ld "iplus.lisp"), escape to raw Lisp with :q and then eavluate
    ; (disassemble 'big-test).  You should see lots of arithmetic operations
    ; in C code, but no calls of C functions CMPmake_fixnum or number_plus.
    
    (in-package "ACL2")
    
    (defmacro i-max ()
      (expt 2 (1- 28)))
    
    (defmacro i+ (x y)
      `(the (signed-byte 28)
            (let ((x ,x)
                  (y ,y))
              (declare (type (signed-byte 28) x y))
              (cond ((or (< x 0)
                         (< y 0))
                     -1)
                    (t (let ((result
                              (the (signed-byte 29) (+ x y))))
                         (declare (type (signed-byte 29) result))
                         (cond ((>= result (i-max)) -1)
                               (t (the (signed-byte 28) result)))))))))
    
    (defmacro imin (x y)
      `(the (signed-byte 28)
            (let ((x ,x)
                  (y ,y))
              (declare (type (signed-byte 28) x y))
              (cond ((< x 0)
                     (cond ((< y 0) -1)
                           (t y)))
                    ((< y 0)
                     x)
                    (t
                     (the (signed-byte 28) (min x y)))))))
    
    (defun big-test (x y z)
      (declare (type (signed-byte 28) x y z))
      (imin (i+ x y)
            (i+ y (imin x z))))
    




    acl2-sources/doc/HTML/GCS.html0000664002132200015000000000063112222333517015366 0ustar kaufmannacl2 GCS.html -- ACL2 Version 6.3

    GCS

    See get-command-sequence.
    Major Section:  HISTORY
    




    acl2-sources/doc/HTML/GENERALIZE.html0000664002132200015000000000355712222333530016404 0ustar kaufmannacl2 GENERALIZE.html -- ACL2 Version 6.3

    GENERALIZE

    make a rule to restrict generalizations
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example:
    (defthm integer-listp-rev
      (implies (integer-listp x)
               (integer-listp (rev x)))
      :rule-classes :generalize)
    
    General Form:
    any theorem
    
    To use a :generalize rule, the system waits until it has decided to generalize some term, term, by replacing it with some new variable v. If any :generalize formula can be instantiated so that some non-variable subterm becomes term, then that instance of the formula is added as a hypothesis. Thus for the example above, if the term (rev x2) is generalized to the variable rv during a proof, then the following is added as a hypothesis when generalizing to a new goal.
    (implies (integer-listp x2)
             (integer-listp rv))
    

    At the moment, the best description of how ACL2 :generalize rules are used may be found in the discussion of ``Generalize Rules,'' page 248 of A Computational Logic Handbook, or ``Generalization,'' page 132 of ``Computer-Aided Reasoning: An Approach.'' Also see introduction-to-the-theorem-prover for detailed tutorial on using ACL2 to prove theorems, which includes some discussion of generalization.




    acl2-sources/doc/HTML/GENERALIZED-BOOLEANS.html0000664002132200015000000001072612222333520017703 0ustar kaufmannacl2 GENERALIZED-BOOLEANS.html -- ACL2 Version 6.3

    GENERALIZED-BOOLEANS

    potential soundness issues related to ACL2 predicates
    Major Section:  COMMON-LISP
    

    The discussion below outlines a potential source of unsoundness in ACL2. Although to our knowledge there is no existing Lisp implementation in which this problem can arise, nevertheless we feel compelled to explain this situation.

    Consider for example the question: What is the value of (equal 3 3)? According to the ACL2 axioms, it's t. And as far as we know, based on considerable testing, the value is t in every Common Lisp implementation. However, according the Common Lisp draft proposed ANSI standard, any non-nil value could result. Thus for example, (equal 3 3) is allowed by the standard to be 17.

    The Common Lisp standard specifies (or soon will) that a number of Common Lisp functions that one might expect to return Boolean values may, in fact, return arbitrary values. Examples of such functions are equal, rationalp, and <; a much more complete list is given below. Indeed, we dare to say that every Common Lisp function that one may believe returns only Booleans is, nevertheless, not specified by the standard to have that property, with the exceptions of the functions not and null. The standard refers to such arbitrary values as ``generalized Booleans,'' but in fact this terminology makes no restriction on those values. Rather, it merely specifies that they are to be viewed, in an informal sense, as being either nil or not nil.

    This situation is problematic for ACL2, which axiomatizes these functions to return Booleans. The problem is that because (for efficiency and simplicity) ACL2 makes very direct use of compiled Common Lisp functions to support the execution of its functions, there is in principle a potential for unsoundness due to these ``generalized Booleans.'' For example, ACL2's equal function is defined to be Common Lisp's equal. Hence if in Common Lisp the form (equal 3 3) were to evaluate to 17, then in ACL2 we could prove (using the :executable-counterpart of equal) (equal (equal 3 3) 17). However, ACL2 can also prove (equal (equal x x) t), and these two terms together are contradictory, since they imply (instantiating x in the second term by 3) that (equal 3 3) is both equal to 17 and to t.

    To make matters worse, the standard allows (equal 3 3) to evaluate to different non-nil values every time. That is: equal need not even be a function!

    Fortunately, no existing Lisp implementation takes advantage of the flexibility to have (most of) its predicates return generalized Booleans, as far as we know. We may implement appropriate safeguards in future releases of ACL2, in analogy to (indeed, probably extending) the existing safeguards by way of guards (see guard). For now, we'll sleep a bit better knowing that we have been up-front about this potential problem.

    The following list of functions contains all those that are defined in Common Lisp to return generalized Booleans but are assumed by ACL2 to return Booleans. In addition, the functions acl2-numberp and complex-rationalp are directly defined in terms of respective Common Lisp functions numberp and complexp.

    /=
    <
    =
    alpha-char-p
    atom
    char-equal
    char<
    char<=
    char>
    char>=
    characterp
    consp
    digit-char-p
    endp
    eq
    eql
    equal
    evenp
    integerp
    keywordp
    listp
    logbitp
    logtest
    lower-case-p
    minusp
    oddp
    plusp
    rationalp
    standard-char-p
    string-equal
    string<
    string<=
    string>
    string>=
    stringp
    subsetp
    symbolp
    upper-case-p
    zerop
    




    acl2-sources/doc/HTML/GENERALIZING-KEY-CHECKPOINTS.html0000664002132200015000000001025712222333515021031 0ustar kaufmannacl2 GENERALIZING-KEY-CHECKPOINTS.html -- ACL2 Version 6.3

    GENERALIZING-KEY-CHECKPOINTS

    getting rid of unnecessary specificity
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Suppose MEMBER determines whether its first argument is a member of its second, and SUBSETP determines whether every element of its first argument is a member of its second. Suppose that you're trying to prove some Main Theorem and are told the formula below is a key checkpoint. What should you do?

    
    Key Checkpoint:
    (IMPLIES (AND (CONSP A)
                  (INTEGERP (CAR A))
                  (MEMBER (CAR A) B)
                  (SUBSETP (CDR A) B)
                  (NOT (SUBSETP (CDR A) (APPEND B C))))
             (MEMBER (CAR A) C))
    

    The key observation is that the third hypothesis implies the negation of the fourth. Writing it in the contrapositive:

    (IMPLIES (AND ...
                  (SUBSETP (CDR A) B)
                  ...)
             (SUBSETP (CDR A) (APPEND B C)))
    
    In fact, that is more complicated than it needs to be. A ``correct'' response to the key checkpoint above is to prove
    (defthm subsetp-append
      (implies (subsetp a b)
               (subsetp a (append b c)))).
    

    A still better response is to prove this:

    (defthm subsetp-append-2
      (implies (or (subsetp a b)
                   (subsetp a c))
               (subsetp a (append b c)))).
    

    This version is better because it handles both of the possibilities regarding whether a is a subset of the arguments of the append.

    It would be nice if we could think of a ``strong'' version, one in which (SUBSETP a (APPEND b c)) is rewritten to some clearly simpler term, but nothing comes to mind.

    In any case, if you cannot see any obvious simplification of the individual terms in the Key Checkpoint, you should ask yourself whether there are connections beween the various propositional parts (as here, with the third and fourth hypotheses). It is a good heuristic to look for relations between parts with the same top-level function symbol (as here, with SUBSETP). It is also a good heuristic to throw out parts of the formula that seem disconnected (as here, with the terms involving (CAR A)).

    This discussion suggests several ``modes'' of looking at key checkpoints and the idea of trying the modes in sequence:

    (1) look for simplifiable combinations of function symbols within propositional components,

    (2) look for relations between propositional components, and

    (3) throw out irrelevant or weakly connected components.

    In all cases you are bringing to bear your intuitions about the semantics of the terms. That is, you're not just slinging symbols. You should be looking out for obvious truths about individual parts of the checkpoints... truths that are obvious to you but not to ACL2!

    Ultimately the three ``modes'' are the same and we do not really recommend adhering to the given sequence. You're just looking for simpler theorems that, in combination, imply the checkpoint. Keeping the ``modes'' in mind may help focus your attention so you consider all the plausible possibilities. After a little experience you'll find yourself looking for all these things simultaneously as part ``cleaning up'' the checkpoints.

    When your main goal theorems are harder than these, your main concern will be looking at a Key Checkpoint and asking yourself ``why is this true?'' But you don't want to do that until you've cleaned it up ``locally'' as much as possible and sometimes -- more often than you might think -- local considerations can prove many of your checkpoints.

    If you have been working your way through the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-key-checkpoints.




    acl2-sources/doc/HTML/GET-COMMAND-SEQUENCE.html0000664002132200015000000000240212222333517017651 0ustar kaufmannacl2 GET-COMMAND-SEQUENCE.html -- ACL2 Version 6.3

    GET-COMMAND-SEQUENCE

    return list of commands that are between two command descriptors
    Major Section:  HISTORY
    

    Examples:
    (get-command-sequence 4 12)
    :gcs 4 12 ; same as above
    (get-command-sequence 4 :x)
    :gcs 4 :x ; same as above
    

    See pcs for a utility that prints abbreviated information about the commands that are between two command descriptors. The utility get-command-sequence -- or simply gcs, so that you can just type :gcs at the prompt -- has the same syntax but instead of printing, it simply returns the corresponding list of commands. More precisely, it returns an error triple (mv erp val state) (see error-triples) such that if erp is not nil, then val is the desired list of commands.




    acl2-sources/doc/HTML/GET-INTERNAL-TIME.html0000664002132200015000000000525212222333521017336 0ustar kaufmannacl2 GET-INTERNAL-TIME.html -- ACL2 Version 6.3

    GET-INTERNAL-TIME

    runtime vs. realtime in ACL2 timings
    Major Section:  MISCELLANEOUS
    

    The ACL2 system provides utilities that deal with elapsed time. The most visible of these is in the time summaries printed when completing evaluation of events. For others, see with-prover-time-limit, see read-run-time, see time-tracker, see time-tracker-tau, and see pstack.

    By default, these utilities all use an underlying notion of run time provided by the host Common Lisp implementation: specifically, Common Lisp function get-internal-run-time. However, Common Lisp also provides function get-internal-run-time, which returns the real time (wall clock time). While the latter is specified to measure elapsed time, the former is left to the implementation, which might well only measure time spent in the Lisp process. Consider the following example, which is a bit arcane but basically sleeps for 2 seconds.

      (defttag t) ; to allow sys-call
      (make-event
       (prog2$ (sys-call "sleep" '("2"))
               (value '(value-triple nil))))
    
    A typical time summary might be as follows, drastically under-reporting the elapsed time.
      Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
    
    However, you can instruct ACL2 to switch to using elapsed time (run time), in summaries and elsewhere, by evaluating the following form.
      (assign get-internal-time-as-realtime t)
    
    To return to using runtime:
      (assign get-internal-time-as-realtime nil)
    
    While the above example is rather silly, the issue becomes significant in time summaries for proofs that call out to external tools (see sys-call and see clause-processor).

    Note that a function get-internal-time is defined in raw Lisp but is not available inside the ACL2 loop. However, the expression (read-run-time state) provides an interface to this function that is available inside the ACL2 loop; see read-run-time.

    We are open to changing the default to elapsed wall-clock time (realtime), and may do so in future ACL2 releases.




    acl2-sources/doc/HTML/GET-OUTPUT-STREAM-STRING$.html0000664002132200015000000000065712222333523020475 0ustar kaufmannacl2 GET-OUTPUT-STREAM-STRING$.html -- ACL2 Version 6.3

    GET-OUTPUT-STREAM-STRING$

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/GET-WORMHOLE-STATUS.html0000664002132200015000000000247412222333521017646 0ustar kaufmannacl2 GET-WORMHOLE-STATUS.html -- ACL2 Version 6.3

    GET-WORMHOLE-STATUS

    make a wormhole's status visible outside the wormhole
    Major Section:  MISCELLANEOUS
    

    General Form: (get-wormhole-status name state)

    Name should be the name of a wormhole (see wormhole). This function returns an error triple (see error-triples) of the form (mv nil s state), where s is the status of the named wormhole. The status is obtained by reading the oracle in the ACL2 state.

    This function makes the status of a wormhole visible outside the wormhole. But since this function takes state and modifies it, the function may only be used in contexts in which you may change state. Otherwise, the wormhole status may stay in the wormhole. See wormhole-eval and wormhole.




    acl2-sources/doc/HTML/GETENV$.html0000664002132200015000000000222512222333523016004 0ustar kaufmannacl2 GETENV$.html -- ACL2 Version 6.3

    GETENV$

    read an environment variable
    Major Section:  ACL2-BUILT-INS
    

    (Getenv$ str state), where str is a string, reads the value of environment variable str, returning a value of nil if none is found or if the read fails. The logical story is that getenv$ reads its value from the oracle field of the ACL2 state. The return value is thus a triple of the form (mv erp val state), where erp will always be nil in practice, and logically, val is the top of the acl2-oracle field of the state and the returned state has the updated (popped) acl2-oracle.

    Example:
    (getenv$ "PWD" state) ==> (mv nil "/u/joe/work" state)
    
    Also see setenv$.




    acl2-sources/doc/HTML/GETPROP.html0000664002132200015000000000145412222333523016073 0ustar kaufmannacl2 GETPROP.html -- ACL2 Version 6.3

    GETPROP

    access fast property lists
    Major Section:  ACL2-BUILT-INS
    

    General form:
    (getprop symb key default world-name world-alist)
    

    See community book books/misc/getprop.lisp for an example that illustrates the use of ACL2 utilities getprop and putprop to take advantage of under-the-hood Lisp (hashed) property lists.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/GOAL-SPEC.html0000664002132200015000000001124112222333521016216 0ustar kaufmannacl2 GOAL-SPEC.html -- ACL2 Version 6.3

    GOAL-SPEC

    to indicate where a hint is to be used
    Major Section:  MISCELLANEOUS
    

    Examples:
    "Goal"
    "goal"
    "Subgoal *1/3''"
    "subgoal *1/3''"
    "[2]Subgoal *1/3''"
    

    When hints are given to the theorem prover, a goal-spec is provided to specify the goal to which the hints are to be applied. The hints provided are carried along innocuously until the named goal arises. When it arises, the hints are ``activated'' for that goal and its descendents.

    A legal goal specification may be extracted from the theorem prover's output. Certain lines clearly label formulas, as in

    Subgoal *1/3.2'
    (IMPLIES ... ...)
    
    and these lines all give rise to goal specifications. In general, these lines all start either with ``Goal'' or ``Subgoal'' or else with those words preceded by a number in square brackets, as in
    [1]Subgoal *1/3.2'
    (IMPLIES ... ...).
    
    A goal specification may be obtained by deleting any surrounding whitespace from such a line and embedding the text in string quotation marks. Thus
    "[1]Subgoal *1/3.2'"
    
    is the goal specifier for the goal above.

    As noted, a hint is applied to a goal when the hint's goal specification matches the name ACL2 assigns to the goal. The matching algorithm is case-insensitive. Thus, alternative goal specifications for the goal above are "[1]subgoal *1/3.2'" and "[1]SUBGOAL *1/3.2'". The matching algorithm does not tolerate non-case discrepancies. Thus, "[1]Subgoal*1/3.2'" and " [1]Subgoal *1/3.2'" are unacceptable.

    Sometimes a formula is given two names, e.g.,

    Subgoal *1/14.2'
    (IMPLIES ...
             ...)
    Name the formula above *1.1.
    
    It is the first name (the one that starts with ``Goal'' or ``Subgoal'') and not the second which constitutes a legal goal-spec. Roughly speaking, when the system prints the line containing the goal specification, it activates any hints that are attached to that goal-spec. Consider the example above. Suppose Subgoal *1/14.2' could be proved by using a certain lemma instance. Then the appropriate entry in the hints would be:
    ("Subgoal *1/14.2'" :use ...)
    
    This might surprise you because the system appears to do nothing to *1/14.2' besides push it for a subsequent induction. But actually between the time the system printed the goal-spec line and the time it decides to push the goal, you can think of the system as trying everything it has. So a use hint activated when Subgoal *1/14.2' arises is just what you want.

    But what if you want to give an :induct hint? By the time induction is tried, the formula has been given the name *1.1. Well, this is one you just have to remember:

    ("Subgoal *1/14.2'" :induct ...).
    
    When the above hint is activated the :induct directive short-circuits the rest of the processing and sends immediately the formula into the pool of goals to prove by induction. The induct hint is attached to the formula in the pool and when the time comes to turn our attention to that goal, the induct advice is followed.

    We conclude by emphasizing a point made above, that a hint is applied to a goal when the hint's goal specification matches the name ACL2 assigns to the goal. If there is no such match, then the hint is ignored. Consider the following example.

    (thm (equal (append (append x y) z) (append x y z))
         :hints (("Subgoal *1/" :in-theory nil)))
    
    Normally, :in-theory hints are inherited by subgoals (see hints-and-the-waterfall), so you might expect that the empty theory is used in Subgoal *1/2 and Subgoal *1/1. But in fact, since there is no subgoal printed that is labeled Subgoal *1/, the above :in-theory hint is ignored. The above example is in contrast to the following, where the hint makes the proof fail, because there really is a Subgoal *1/ in the proof this time.
    (thm (implies (and (not (endp x)) (not (endp (cdr x))))
                  (equal (append (append x y) z) (append x y z)))
         :hints (("Subgoal *1/" :in-theory nil)))
    




    acl2-sources/doc/HTML/GOOD-ATOM-LISTP.html0000664002132200015000000000147412222333523017134 0ustar kaufmannacl2 GOOD-ATOM-LISTP.html -- ACL2 Version 6.3

    GOOD-ATOM-LISTP

    recognizer for a true list of ``good'' atoms
    Major Section:  ACL2-BUILT-INS
    

    The predicate good-atom-listp tests whether its argument is a true-listp of ``good'' atoms, i.e., where each element is a number, a symbol, a character, or a string.

    Also see atom-listp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/GOOD-BYE.html0000664002132200015000000000304112222333522016111 0ustar kaufmannacl2 GOOD-BYE.html -- ACL2 Version 6.3

    GOOD-BYE

    quit entirely out of Lisp
    Major Section:  OTHER
    

    Examples:
    ACL2 !>(good-bye)
    ; [ACL2 is exited]
    
    ACL2 !>(good-bye 3)
    ; [ACL2 is exited with Unix exit status 3]
    
    Note: Your entire session will disappear forever when you evaluate (good-bye).

    The command (good-bye) quits not only out of the ACL2 command loop, but in fact quits entirely out of the underlying Lisp. Thus, there is no going back! You will not be able to re-enter the command loop after typing (good-bye)! All your work will be lost!!!

    This command may not work in some underlying Common Lisp implementations. In such cases, there is no harm in trying; ACL2 will let you know how to proceed if it cannot exit.

    In some systems, typing control-d at the top-level ACL2 prompt (control-c control-d if inside emacs) will call this function.

    If you give good-bye an argument, it should be a natural number, and it indicates the Unix (Linux) exit status.

    If you merely want to exit the ACL2 command loop, use :q instead (see q).




    acl2-sources/doc/HTML/GRANULARITY.html0000664002132200015000000001122312222333522016546 0ustar kaufmannacl2 GRANULARITY.html -- ACL2 Version 6.3

    GRANULARITY

    limit the amount of parallelism
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Some function calls are on arguments whose evaluation time is long enough to warrant parallel execution, while others are not. A granularity form can be used to make appropriate restrictions on the use of parallelism.

    For example, consider the Fibonacci function. Experiments have suggested that execution time can be reduced if whenever the argument is less than 27, a serial version of the Fibonacci function is called. One way to utilize this information is to write two definitions of the Fibonacci function, one serial and one parallel.

    (defun fib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (binary-+ (fib (- x 1))
                         (fib (- x 2))))))
    
    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            ((< x 27) (binary-+ (fib (- x 1))
                                (fib (- x 2))))
            (t (pargs (binary-+ (pfib (- x 1))
                                (pfib (- x 2)))))))
    
    We realize quickly that writing both of these function definitions is cumbersome and redundant. This problem can be avoided by using a granularity declaration with a parallelism primitive. This form ensures that a call is parallelized only if resources are available and the granularity form evaluates to a non-nil value at the time of the call. Below is a definition of the Fibonacci function using a granularity form.
    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (pargs (declare (granularity (>= x 27)))
                      (binary-+ (pfib (- x 1))
                                (pfib (- x 2)))))))
    

    A granularity form can reference an extra formal parameter that describes the call depth of the function the user is parallelizing. Consider for example the following parallel mergesort function, based on Davis's Ordered Sets library. It splits the data into symmetric chunks for computation, so we increment the depth argument during the recursive call on both the car and cdr.

    (include-book "finite-set-theory/osets/sets" :dir :system)
    (defun sets::pmergesort-exec (x depth)
      (declare (xargs :mode :program))
      (cond ((endp x) nil)
            ((endp (cdr x)) (sets::insert (car x) nil))
            (t (mv-let (part1 part2)
                       (sets::split-list x nil nil)
                       (pargs
                        (declare (granularity (< depth 2)))
                        (sets::union (sets::pmergesort-exec part1
                                                            (1+ depth))
                                     (sets::pmergesort-exec part2
                                                            (1+ depth))))))))
    

    A less intrusive method (i.e., not requiring an extra formal parameter like the depth argument just above), which however can be less efficient, involves analyzing the data itself for structural properties. For example:

    (defun some-depth-exceeds (x n)
      (declare (xargs :guard (natp n)))
      (if (atom x)
          nil
        (if (zp n)
            t
          (or (some-depth-exceeds (car x) (1- n))
              (some-depth-exceeds (cdr x) (1- n))))))
    
    (defun valid-tip (x)
      (declare (xargs :guard t))
      (or (eq x 'A)
          (eq x 'T)
          (eq x 'C)
          (eq x 'G)))
    
    (defun pvalid-tree (x)
      (declare (xargs :guard t))
      (if (atom x)
          (valid-tip x)
        (pand (declare (granularity (some-depth-exceeds x 3)))
              (pvalid-tree (car x))
              (pvalid-tree (cdr x)))))
    
    If you experiment with calls of pvalid-tree, you are likely to find that the ``speedup'' it provides over a corresponding serial version is, in fact, a slowdown! The problem is likely that some-depth-exceeds is an expensive function to run repeatedly. Instead of the approach above, it is often handy to add an extra formal parameter in order to allow for more efficient granularity forms, as we have done above in the definition of SETS::pmergesort-exec.




    acl2-sources/doc/HTML/GROUND-ZERO.html0000664002132200015000000000170612222333531016525 0ustar kaufmannacl2 GROUND-ZERO.html -- ACL2 Version 6.3

    GROUND-ZERO

    enabled rules in the startup theory
    Major Section:  THEORIES
    

    ACL2 concludes its initialization (boot-strapping) procedure by defining the theory ground-zero; see theories. In fact, this theory is just the theory defined by (current-theory :here) at the conclusion of initialization; see current-theory.

    Note that by evaluating the event

    (in-theory (current-theory 'ground-zero))
    
    you can restore the current theory to its value at the time you started up ACL2.




    acl2-sources/doc/HTML/GUARD-DEBUG.html0000664002132200015000000001272012222333521016435 0ustar kaufmannacl2 GUARD-DEBUG.html -- ACL2 Version 6.3

    GUARD-DEBUG

    generate markers to indicate sources of guard proof obligations
    Major Section:  GUARD
    

    ACL2 guard verification (see guard) is often best avoided by beginning users of ACL2. When guard verification is employed, it can generate numerous goals, some of which may not be theorems if the definition being processed has bugs. It can be difficult to find such bugs. This documentation topic explains a capability provided by ACL2 to help find such bugs.

    We begin with the following example. Although it is contrived and a bit simplistic, it illustrates how the guard-debug utility works.

    (defun length-repeat (x)
      (length (append x x)))
    (verify-guards length-repeat :guard-debug t)
    

    The output produces two top-level key checkpoints, as follows.

    Subgoal 2
    (IMPLIES (EXTRA-INFO '(:GUARD (:BODY LENGTH-REPEAT))
                         '(APPEND X X))
             (TRUE-LISTP X))
    
    Subgoal 1
    (IMPLIES (AND (EXTRA-INFO '(:GUARD (:BODY LENGTH-REPEAT))
                              '(LENGTH (APPEND X X)))
                  (NOT (TRUE-LISTP (APPEND X X))))
             (STRINGP (APPEND X X)))
    
    The upper subgoal (numbered 2) says that the body of the definition of length-repeat contains a call (APPEND X X), which is the source of the goal. In this case, that makes sense: the guard for a call (append arg1 arg2) is (true-listp arg1), which in this case is (TRUE-LISTP X). The lower subgoal (numbered 1) says that the same definition contains the call (LENGTH (APPEND X X)), which generates the proof obligation that if (APPEND X X) does not satisfy true-listp, then it must satisfy stringp. That proof obligation comes directly from the guard for length.

    Of course, the example above is a bit obvious. But for large definitional bodies such information can be very helpful. Note that guard-debug can be specified not only in verify-guards events but also in xargs declare forms of defun events.

    We now describe the guard-debug utility in some detail.

    (Extra-info x y) always returns t by definition. However, if guard verification takes place with a non-nil setting of guard-debug (see below), then the goals generated for guard verification can include hypotheses that are calls of extra-info. Typically, such a hypothesis is of the following form:

    (extra-info '(:guard (:body F))
                '(G ARG1 ... ARGk))
    
    The above form indicates that the goal in which it occurs was generated to verify that the guard of the function F is satisfied by the arguments ARG1 through ARGk, where the term (G ARG1 ... ARGk) occurs in the body of the function F whose guard verification is in progress. If however the above call of G occurs in the guard of F instead of the body of F, then :body is replaced by :guard above:
    (extra-info '(:guard (:guard F))
                '(G ARG1 ... ARGk))
    
    If the same proof obligation (goal clause) arises from more than one occurrence of the same call, then a single goal will be generated, which has several extra-info hypotheses added to show the multiple sources of that proof obligation.

    All rules (see rune) associated with extra-info are disabled by default, so that hypotheses of the form described above are not simplified to t. These hypotheses are intended to ride along for free: you can generally expect the proof to have the same structure whether or not the :guard-debug option is supplied, though on rare occasions the proofs may diverge.

    It remains to explain the notion of a guard-debug setting of t, that is, to explain how to obtain the additional hypotheses described above. If guards are being verified during processing of a defun event (whether or not inside a call of mutual-recursion), then one specifies :guard-debug t in an xargs declaration of a declare form; see xargs. If however the guard verification is on behalf of a verify-guards call, whether for a definition or a theorem, then one specifies the keyword argument :guard-debug t.

    Also see print-gv for a utility for debugging guard violations, in contrast to the above guard-debug mechanism, which is for debugging failed proofs arising from guard verification.




    acl2-sources/doc/HTML/GUARD-EVALUATION-EXAMPLES-LOG.html0000664002132200015000000004744112222333521021121 0ustar kaufmannacl2 GUARD-EVALUATION-EXAMPLES-LOG.html -- ACL2 Version 6.3

    GUARD-EVALUATION-EXAMPLES-LOG

    log showing combinations of defun-modes and guard-checking
    Major Section:  GUARD
    

    See guard-evaluation-examples-script for a script that shows the interaction of defun-modes with the value set by set-guard-checking. Here, we present a log resulting from running this script.

    See set-guard-checking for discussion of the interaction between defun-modes and guard-checking that is illustrated by this script. Also see guard-evaluation-table for a succinct table, with associated discussion, that covers in detail the interactions illustrated here.

    ACL2 !>(defun fact (x)
             (declare (xargs :guard (integerp x)
                             :mode :program))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    Summary
    Form:  ( DEFUN FACT ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 !>(trace$ fact)
     ((FACT))
    ACL2 !>:set-guard-checking t
    
    Guard-checking-on already has value T.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the :program function symbol
    FACT, which is (INTEGERP X), is violated by the arguments in the call
    (FACT T).  See :DOC trace for a useful debugging utility.  See :DOC
    set-guard-checking for information about suppressing this check with
    (set-guard-checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the :program function symbol
    FACT, which is (INTEGERP X), is violated by the arguments in the call
    (FACT T).  See :DOC trace for a useful debugging utility.  See :DOC
    set-guard-checking for information about suppressing this check with
    (set-guard-checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:u
    
              0:x(EXIT-BOOT-STRAP-MODE)
    ACL2 >(defun fact (x)
             (declare (xargs :guard t
                             :mode :program))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    Summary
    Form:  ( DEFUN FACT ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 >(trace$ fact)
     ((FACT))
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:u
    
              0:x(EXIT-BOOT-STRAP-MODE)
    ACL2 >(defun fact (x)
             (declare (xargs :guard (integerp x)
                             :verify-guards nil
                             :mode :logic))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    For the admission of FACT we will use the relation O< (which is known
    to be well-founded on the domain recognized by O-P) and the measure
    (ACL2-COUNT X).  The non-trivial part of the measure conjecture is
    
    [[output omitted here]]
    
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 >(trace$ fact)
     ((FACT))
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(fact 2)
    [[Comment added to the log:
      Normally you would get a message about guard-checking being
      inhibited on recursive calls.  However, when a function is
      traced the guard-checking is done on recursive calls unless
      the guards have been verified (see :DOC verify-guards).
    ]]
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol FACT, which
    is (INTEGERP X), is violated by the arguments in the call (FACT T).
    See :DOC trace for a useful debugging utility.  See :DOC set-guard-
    checking for information about suppressing this check with (set-guard-
    checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol FACT, which
    is (INTEGERP X), is violated by the arguments in the call (FACT T).
    See :DOC trace for a useful debugging utility.  See :DOC set-guard-
    checking for information about suppressing this check with (set-guard-
    checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    [[Comment added to the log:
      In spite of the warning above, guards are checked here on
      self-recursive calls, because the function is traced.
    ]]
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >(verify-guards fact)
    
    Computing the guard conjecture for FACT....
    
    The guard conjecture for FACT is trivial to prove, given the :compound-
    recognizer rule POSP-COMPOUND-RECOGNIZER, primitive type reasoning
    and the :type-prescription rule FACT.  FACT is compliant with Common
    Lisp.
    
    Summary
    Form:  ( VERIFY-GUARDS FACT)
    Rules: ((:COMPOUND-RECOGNIZER POSP-COMPOUND-RECOGNIZER)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:TYPE-PRESCRIPTION FACT))
    Warnings:  None
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     FACT
    ACL2 >(trace$ fact)
     ((FACT))
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol FACT, which
    is (INTEGERP X), is violated by the arguments in the call (FACT T).
    See :DOC trace for a useful debugging utility.  See :DOC set-guard-
    checking for information about suppressing this check with (set-guard-
    checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol FACT, which
    is (INTEGERP X), is violated by the arguments in the call (FACT T).
    See :DOC trace for a useful debugging utility.  See :DOC set-guard-
    checking for information about suppressing this check with (set-guard-
    checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:u
     L        1:x(DEFUN FACT (X) ...)
    ACL2 >:u
              0:x(EXIT-BOOT-STRAP-MODE)
    ACL2 >(defun fact (x)
             (declare (xargs :guard (integerp x)
                             :mode :logic))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    For the admission of FACT we will use the relation O< (which is known
    to be well-founded on the domain recognized by O-P) and the measure
    (ACL2-COUNT X).  The non-trivial part of the measure conjecture is
    
    [[output omitted here]]
    
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 >(trace$ fact)
     ((FACT))
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol FACT, which
    is (INTEGERP X), is violated by the arguments in the call (FACT T).
    See :DOC trace for a useful debugging utility.  See :DOC set-guard-
    checking for information about suppressing this check with (set-guard-
    checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol FACT, which
    is (INTEGERP X), is violated by the arguments in the call (FACT T).
    See :DOC trace for a useful debugging utility.  See :DOC set-guard-
    checking for information about suppressing this check with (set-guard-
    checking :none), as recommended for new users.
    
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:u
              0:x(EXIT-BOOT-STRAP-MODE)
    ACL2 >(defun fact (x)
             (declare (xargs :guard t
                             :verify-guards nil
                             :mode :logic))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    For the admission of FACT we will use the relation O< (which is known
    to be well-founded on the domain recognized by O-P) and the measure
    (ACL2-COUNT X).  The non-trivial part of the measure conjecture is
    
    [[output omitted here]]
    
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 >(trace$ fact)
     ((FACT))
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (ACL2_*1*_ACL2::FACT 1)
        3> (ACL2_*1*_ACL2::FACT 0)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >(verify-guards fact)
    
    Computing the guard conjecture for FACT....
    
    The guard conjecture for FACT is trivial to prove, given the :compound-
    recognizer rule POSP-COMPOUND-RECOGNIZER and the :type-prescription
    rule FACT.  FACT is compliant with Common Lisp.
    
    Summary
    Form:  ( VERIFY-GUARDS FACT)
    Rules: ((:COMPOUND-RECOGNIZER POSP-COMPOUND-RECOGNIZER)
            (:TYPE-PRESCRIPTION FACT))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    
    [[Note added to log: No need to trace fact again after verify-guards.]]
    
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 !>:set-guard-checking :all
    
    Leaving guard checking on, but changing value to :ALL.
    
    ACL2 !>(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 !>(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >:set-guard-checking nil
    
    Masking guard violations but still checking guards except for self-
    recursive calls.  To avoid guard checking entirely, :SET-GUARD-CHECKING
    :NONE.  See :DOC set-guard-checking.
    
    ACL2 >(fact 2)
    1> (ACL2_*1*_ACL2::FACT 2)
      2> (FACT 2)
        3> (FACT 1)
          4> (FACT 0)
          <4 (FACT 1)
        <3 (FACT 1)
      <2 (FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 2)
    2
    ACL2 >(fact t)
    1> (ACL2_*1*_ACL2::FACT T)
      2> (FACT T)
      <2 (FACT 1)
    <1 (ACL2_*1*_ACL2::FACT 1)
    1
    ACL2 >
    




    acl2-sources/doc/HTML/GUARD-EVALUATION-EXAMPLES-SCRIPT.html0000664002132200015000000001371012222333521021474 0ustar kaufmannacl2 GUARD-EVALUATION-EXAMPLES-SCRIPT.html -- ACL2 Version 6.3

    GUARD-EVALUATION-EXAMPLES-SCRIPT

    a script to show combinations of defun-modes and guard-checking
    Major Section:  GUARD
    

    Below is a script that illustrates the combination of defun-modes -- :program mode, :logic mode without guards verified, and :logic mode with guards verified -- with values from set-guard-checking -- t (the default), :all, :none, and nil. (It does not illustrate the value :nowarn, which is the same as t except for inhibiting a warning.) The script also illustrates cases where the guard is not, or is, t.

    See guard-evaluation-examples-log for result of running this script. Before presenting the script below, we give some instructions in case you want to run it yourself.

    See set-guard-checking for discussion of the interaction between defun-modes and guard-checking that is illustrated by this script. Also see guard-evaluation-table for a succinct table, with associated discussion, that covers in detail the interactions illustrated here.

    The script mentions the running of ``Tracing Code''. The code is the following sequence of commands.

    (trace$ fact)
    :set-guard-checking t
    (fact 2)
    (fact t)
    :set-guard-checking :all
    (fact 2)
    (fact t)
    :set-guard-checking :none
    (fact 2)
    (fact t)
    :set-guard-checking nil
    (fact 2)
    (fact t)
    

    If you want to run the script yourself, you may find it handy to use the following Emacs keyboard macro for running the tracing code in 2-window mode, with the cursor in the window with the script and ACL2 running in the other window.

    (fset 'step-guard-script
       [?C-a ?C-  ?C-e ?M-w ?C-a ?C-n
        ?C-x ?o ?M-> ?C-y return ?C-x ?o])
    
    ; Put it on a key (if you have defined the indicated keymap by using
    ; emacs/emacs-acl2.el):
    (define-key ctl-t-keymap "r" 'step-guard-script)
    

    The script follows.

    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Program mode
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (defun fact (x)
             (declare (xargs :guard (integerp x)
                             :mode :program))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    ; Run the Tracing Code here.  It shows execution in raw Lisp in the t and nil
    ; cases of :set-guard-checking, but not in the :all or :none cases.  We get a
    ; guard violation for argument t in the case :set-guard-checking t.
    
    :u
    
    (defun fact (x)
             (declare (xargs :guard t
                             :mode :program))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    ; Run the Tracing Code here.  It should give the same results as above,
    ; except that we no longer get a guard violation in the case
    ; :set-guard-checking t.
    
    :u
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Logic mode, guard other than t
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (defun fact (x)
             (declare (xargs :guard (integerp x)
                             :verify-guards nil
                             :mode :logic))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    ; Run the Tracing Code here.  It should give guard violations for (fact t)
    ; with guard-checking set to t or :all.  It should never run in raw Lisp,
    ; because we have not verified guards.  In the t case, we can get a warning
    ; about avoiding the guard check on recursive calls, but only if we do not
    ; trace the function, fact.
    
    (verify-guards fact)
    
    ; Run the Tracing Code here.  The results should be as described just above,
    ; except that now we go into raw Lisp for (fact 2) with guard-checking other
    ; than :none.
    
    :u
    :u
    
    ; The following definition is the same as above, except that guards are
    ; verified.
    
    (defun fact (x)
             (declare (xargs :guard (integerp x)
                             :mode :logic))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    ; Run the Tracing Code here.  We should get the same traces as in the
    ; immediately preceding case, since guards had been verified in both cases.
    
    :u
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Logic mode, guard t
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    (defun fact (x)
             (declare (xargs :guard t
                             :verify-guards nil
                             :mode :logic))
             (if (posp x)
                 (* x (fact (1- x)))
               1))
    
    ; Run the Tracing Code here.  We should never go in to raw Lisp, because
    ; guards have not been verified.  We will see the same traces for (fact 2) as
    ; with the (integerp x) guard above with :verify-guards nil specified, except
    ; that even without tracing, there is no warning for :set-guard-checking t
    ; about recursive calls.  And, there are no guard violations for (fact t), of
    ; course, since posp (necessarily, if we are to verify guards) has a guard of
    ; t.
    
    (verify-guards fact)
    
    ; Run the Tracing Code here.  You shouldn't see any surprises.  Note however
    ; that if we back up to the start (using :u :u) and then define fact as just
    ; above but without :verify-guards nil, then the :none setting will allow us
    ; to go into raw Lisp: although :none generally avoids execution of raw Lisp
    ; counterparts, it allows this when the guard is T and guards have been
    ; verified.
    
    




    acl2-sources/doc/HTML/GUARD-EVALUATION-TABLE.html0000664002132200015000000001701412222333521020104 0ustar kaufmannacl2 GUARD-EVALUATION-TABLE.html -- ACL2 Version 6.3

    GUARD-EVALUATION-TABLE

    a table that shows combinations of defun-modes and guard-checking
    Major Section:  GUARD
    

    See set-guard-checking for an introduction to the topic discussed here. Also see guard for a general discussion of guards, and see guard-evaluation-examples-script for a script that illustrates combinations presented below.

    Note: The default setting for guard-checking (that is, the initial value for (@ guard-checking-on)) is T.

    The table below illustrates the interaction of the defun-mode with the value supplied to set-guard-checking. The first row considers functions defined in :program mode; the other two consider functions defined in :logic mode. The columns correspond to four values of state global 'guard-checking-on, as supplied to set-guard-checking. (A fifth value, :nowarn, is similar to t but suppresses warnings encountered with t (as explained in those warning messages), and is not considered here.) During proofs, 'guard-checking-on is set to nil regardless of how this variable has been set in the top-level loop.

    Below this table, we make some comments about its entries, ordered by row and then by column. For example, when we refer to ``b2'' we are discussing the execution of a :logic mode function whose guards have not been verified, after having executed :set-guard-checking :all.

       guard-checking-on:  (1)t      (2):all   (3):none   (4)nil
    
     (a) :program             a1        a2        a3        a4
     (b) guards not verified  b1        b2        b3        b4
     (c) guards verified      c1        c2        c3        c4
    

    a1. Check the guard upon entry, then use the raw Lisp code if the guard checks (else cause an error). This is a common setting when one wants a little guard checking but also wants the efficiency of raw Lisp. But note that you can get raw Lisp errors. For example, if you make the definition (defun foo (x) (car x)) in :program mode and execute :set-guard-checking t, and then execute (foo 3), you will likely get an error from the call (car 3) made in raw Lisp.

    a2. For built-in (predefined) functions, see a1 instead. Otherwise:
    Check the guard, without exception. Thus, we never run the raw Lisp code in this case. This can be useful when testing :program mode functions, but you may want to run :comp t or at least :comp :exec in this case, so that the execution is done using compiled code.

    a3. For built-in (predefined) functions, see a4 instead. Otherwise:
    Do not check the guard. For :program mode functions, we never run the raw Lisp code in this case; so if you care about efficiency, see the comment in a2 above about :comp. This combination is useful if you are using ACL2 as a programming language and do not want to prove theorems about your functions or suffer guard violations. In this case, you can forget about any connection between ACL2 and Common Lisp.

    a4. Run the raw Lisp code without checking guards at all. Thus, for :program mode functions, the nil setting is often preferable to the :none setting because you get the efficiency of raw Lisp execution. However, with nil you can therefore get hard Lisp errors as in a1 above.

    b1. Guards are checked at the top-level, though not at self-recursive calls. We never run the raw Lisp code in this case; guards would need to be verified first.

    b2. Unlike the t setting, guards are checked even on self-recursive calls. But like the t setting, we do not run the raw Lisp code. Use this setting if you want guards checked on each recursive call in spite of the cost of doing so.

    b3, b4. Execution avoids the raw Lisp code and never checks guards. The nil and :none settings behave the same in this case (i.e., for :logic mode functions whose guards have not been verified), except that recursive calls are never inlined for :none and tracing (see trace) will show recursive calls for :none but not for nil.

    c1, c2. Guards are checked. If the checks pass, evaluation takes place using the raw Lisp code. If the checks fail, we get a guard violation. Either way, we do not execute ``in the logic''; we only execute using the raw Lisp code. Note that t and :all behave the same in this case, (i.e. for :logic mode functions whose guards have been verified).

    c3, c4. For the :none and nil settings, :logic mode functions whose guards have been verified will never cause guard violations. However, with nil and for built-in functions in :logic mode, guards are still checked: if the check succeeds, then evaluation is done using the raw Lisp code, and if not, it is done by the ``logic'' code, including self-recursive calls (though unlike the t case, we will not see a warning about this). But with :none for user-defined functions, no guard checking is done, and the only time the raw Lisp code will be executed is when the guard is t and guards are verified at the time the executable counterpart of the function is defined (i.e., when the function is admitted unless it is later defined again and compiled using :comp). Thus, if you use :none and you want a function (foo x) with guard (g x) to execute using raw Lisp code, you can write a ``wrapper''function with a guard of t:

    (defun foo-wrap (x)
      (declare (xargs :guard t))
      (if (g x)
          (foo x)
        'do-not-case))
    
    If you want the speed of executing raw Lisp code and you have non-trivial guards on functions that you want to call at the top-level, use nil rather than :none.




    acl2-sources/doc/HTML/GUARD-EXAMPLE.html0000664002132200015000000002323412222333515016707 0ustar kaufmannacl2 GUARD-EXAMPLE.html -- ACL2 Version 6.3

    GUARD-EXAMPLE

    a brief transcript illustrating guards in ACL2
    Major Section:  TUTORIAL5-MISCELLANEOUS-EXAMPLES
    

    This note addresses the question: what is the use of guards in ACL2? Although we recommend that beginners try to avoid guards for a while, we hope that the summary here is reasonably self-contained and will provide a reasonable introduction to guards in ACL2. For a more systematic discussion, see guard. For a summary of that topic, see guard-quick-reference.

    Before we get into the issue of guards, let us note that there are two important ``modes'':

    defun-mode -- ``Does this defun add an axiom (`:logic mode') or not (`:program mode')?'' (See defun-mode.) Only :logic mode functions can have their ``guards verified'' via mechanized proof; see verify-guards.

    set-guard-checking -- ``Should runtime guard violations signal an error (:all, and usually with t or :nowarn) or go undetected (nil, :none)? Equivalently, are expressions evaluated in Common Lisp or in the logic?'' (See set-guard-checking.)

    Prompt examples

    Here some examples of the relation between the ACL2 prompt and the ``modes'' discussed above. Also see default-print-prompt. The first examples all have ld-skip-proofsp nil; that is, proofs are not skipped.

    
      ACL2 !>    ; logic mode with guard checking on
      ACL2 >     ; logic mode with guard checking off
      ACL2 p!>   ; program mode with guard checking on
      ACL2 p>    ; program mode with guard checking off
    
    
    Here are some examples with default-defun-mode of :logic.
    
      ACL2 >     ; guard checking off, ld-skip-proofsp nil
      ACL2 s>    ; guard checking off, ld-skip-proofsp t
      ACL2 !>    ; guard checking on, ld-skip-proofsp nil
      ACL2 !s>   ; guard checking on, ld-skip-proofsp t
    
    

    Sample session

    ACL2 !>(+ 'abc 3)
    
    ACL2 Error in TOP-LEVEL: The guard for the function symbol
    BINARY-+, which is (AND (ACL2-NUMBERP X) (ACL2-NUMBERP Y)),
    is violated by the arguments in the call (+ 'ABC 3).
    
    ACL2 !>:set-guard-checking nil
    ;;;; verbose output omitted here
    ACL2 >(+ 'abc 3)
    3
    ACL2 >(< 'abc 3)
    T
    ACL2 >(< 3 'abc)
    NIL
    ACL2 >(< -3 'abc)
    T
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(defun sum-list (x)
            (declare (xargs :guard (integer-listp x)
                            :verify-guards nil))
            (cond ((endp x) 0)
                  (t (+ (car x) (sum-list (cdr x))))))
    
    The admission of SUM-LIST is trivial, using the relation
    O< (which is known to be well-founded on the domain
    recognized by O-P) and the measure (ACL2-COUNT X).
    We observe that the type of SUM-LIST is described by the
    theorem (ACL2-NUMBERP (SUM-LIST X)).  We used primitive type
    reasoning.
    
    Summary
    Form:  ( DEFUN SUM-LIST ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.03 seconds
       (prove: 0.00, print: 0.00, proof tree: 0.00, other: 0.03)
     SUM-LIST
    ACL2 !>(sum-list '(1 2 3))
    
    ACL2 Warning [Guards] in TOP-LEVEL:  Guard-checking will be inhibited
    on recursive calls of the executable counterpart (i.e., in the ACL2
    logic) of SUM-LIST.  To check guards on all recursive calls:
      (set-guard-checking :all)
    To leave behavior unchanged except for inhibiting this message:
      (set-guard-checking :nowarn)
    
    6
    ACL2 !>(sum-list '(1 2 abc 3))
    
    ACL2 Error in TOP-LEVEL: The guard for the function symbol
    BINARY-+, which is (AND (ACL2-NUMBERP X) (ACL2-NUMBERP Y)),
    is violated by the arguments in the call (+ 'ABC 3).
    
    ACL2 !>:set-guard-checking nil
    ;;;; verbose output omitted here
    ACL2 >(sum-list '(1 2 abc 3))
    6
    ACL2 >(defthm sum-list-append
           (equal (sum-list (append a b))
                  (+ (sum-list a) (sum-list b))))
    
    << Starting proof tree logging >>
    
    Name the formula above *1.
    
    Perhaps we can prove *1 by induction.  Three induction
    schemes are suggested by this conjecture.  Subsumption
    reduces that number to two.  However, one of these is flawed
    and so we are left with one viable candidate.
    
    ...
    
    That completes the proof of *1.
    
    Q.E.D.
    

    Guard verification vs. defun

    
          Declare Form                        Guards Verified?
    
      (declare (xargs :mode :program ...))          no
      (declare (xargs :guard g))                    yes
      (declare (xargs :guard g :verify-guards nil)) no
      (declare (xargs ...<no :guard>...))           no
    
    ACL2 >:pe sum-list
     l        8  (DEFUN SUM-LIST (X)
                  (DECLARE (XARGS :GUARD (INTEGER-LISTP X)
                                  :VERIFY-GUARDS NIL))
                  (COND ((ENDP X) 0)
                        (T (+ (CAR X) (SUM-LIST (CDR X))))))
    ACL2 >(verify-guards sum-list)
    The non-trivial part of the guard conjecture for SUM-LIST,
    given the :type-prescription rule SUM-LIST, is
    
    Goal
    (AND (IMPLIES (AND (INTEGER-LISTP X) (NOT (CONSP X)))
                  (EQUAL X NIL))
         (IMPLIES (AND (INTEGER-LISTP X) (NOT (ENDP X)))
                  (INTEGER-LISTP (CDR X)))
         (IMPLIES (AND (INTEGER-LISTP X) (NOT (ENDP X)))
                  (ACL2-NUMBERP (CAR X)))).
    
    ...
    
    ACL2 >:pe sum-list
     lv       8  (DEFUN SUM-LIST (X)
                  (DECLARE (XARGS :GUARD (INTEGER-LISTP X)
                                  :VERIFY-GUARDS NIL))
    ACL2 >:set-guard-checking t
    
    Turning guard checking on, value T.
    
    ACL2 !>(sum-list '(1 2 abc 3))
    
    ACL2 Error in TOP-LEVEL: The guard for the function symbol
    SUM-LIST, which is (INTEGER-LISTP X), is violated by the
    arguments in the call (SUM-LIST '(1 2 ABC ...)).  See :DOC trace for a useful
    debugging utility.  See :DOC set-guard-checking for information about
    suppressing this check with (set-guard-checking :none), as recommended for
    new users.
    
    ACL2 !>:set-guard-checking nil
    ;;;; verbose output omitted here
    ACL2 >(sum-list '(1 2 abc 3))
    6
    ACL2 >:comp sum-list
    Compiling gazonk0.lsp.
    End of Pass 1.
    End of Pass 2.
    Finished compiling gazonk0.lsp.
    Loading gazonk0.o
    start address -T 1bbf0b4 Finished loading gazonk0.o
    Compiling gazonk0.lsp.
    End of Pass 1.
    End of Pass 2.
    Finished compiling gazonk0.lsp.
    Loading gazonk0.o
    start address -T 1bc4408 Finished loading gazonk0.o
     SUM-LIST
    ACL2 >:q
    
    Exiting the ACL2 read-eval-print loop.
    ACL2>(trace sum-list)
    (SUM-LIST)
    
    ACL2>(lp)
    
    ACL2 Version 1.8.  Level 1.  Cbd "/slocal/src/acl2/v1-9/".
    Type :help for help.
    ACL2 >(sum-list '(1 2 abc 3))
    6
    ACL2 >(sum-list '(1 2 3))
      1> (SUM-LIST (1 2 3))>
        2> (SUM-LIST (2 3))>
          3> (SUM-LIST (3))>
            4> (SUM-LIST NIL)>
            <4 (SUM-LIST 0)>
          <3 (SUM-LIST 3)>
        <2 (SUM-LIST 5)>
      <1 (SUM-LIST 6)>
    6
    ACL2 >:pe sum-list-append
              9  (DEFTHM SUM-LIST-APPEND
                         (EQUAL (SUM-LIST (APPEND A B))
                                (+ (SUM-LIST A) (SUM-LIST B))))
    ACL2 >(verify-guards sum-list-append)
    
    The non-trivial part of the guard conjecture for
    SUM-LIST-APPEND, given the :type-prescription rule SUM-LIST,
    is
    
    Goal
    (AND (TRUE-LISTP A)
         (INTEGER-LISTP (APPEND A B))
         (INTEGER-LISTP A)
         (INTEGER-LISTP B)).
    
    ...
    
    ****** FAILED ******* See :DOC failure ****** FAILED ******
    ACL2 >(defthm common-lisp-sum-list-append
             (if (and (integer-listp a)
                      (integer-listp b))
                 (equal (sum-list (append a b))
                        (+ (sum-list a) (sum-list b)))
                 t)
             :rule-classes nil)
    
    << Starting proof tree logging >>
    
    By the simple :rewrite rule SUM-LIST-APPEND we reduce the
    conjecture to
    
    Goal'
    (IMPLIES (AND (INTEGER-LISTP A)
                  (INTEGER-LISTP B))
             (EQUAL (+ (SUM-LIST A) (SUM-LIST B))
                    (+ (SUM-LIST A) (SUM-LIST B)))).
    
    But we reduce the conjecture to T, by primitive type
    reasoning.
    
    Q.E.D.
    ;;;; summary omitted here
    ACL2 >(verify-guards common-lisp-sum-list-append)
    
    The non-trivial part of the guard conjecture for
    COMMON-LISP-SUM-LIST-APPEND, given the :type-prescription
    rule SUM-LIST, is
    
    Goal
    (AND (IMPLIES (AND (INTEGER-LISTP A)
                       (INTEGER-LISTP B))
                  (TRUE-LISTP A))
         (IMPLIES (AND (INTEGER-LISTP A)
                       (INTEGER-LISTP B))
                  (INTEGER-LISTP (APPEND A B)))).
    
    ...
    
    Q.E.D.
    
    That completes the proof of the guard theorem for
    COMMON-LISP-SUM-LIST-APPEND.  COMMON-LISP-SUM-LIST-APPEND
    is compliant with Common Lisp.
    ;;;; Summary omitted here.
    ACL2 >(defthm foo (consp (mv x y)))
    
    ...
    
    Q.E.D.
    
    
    ACL2 >(verify-guards foo)
    
    ACL2 Error in (VERIFY-GUARDS FOO): The number of values we
    need to return is 1 but the number of values returned by the
    call (MV X Y) is 2.
    
    > (CONSP (MV X Y))
    
    
    ACL2 Error in (VERIFY-GUARDS FOO): The guards for FOO cannot
    be verified because the theorem has the wrong syntactic
    form.  See :DOC verify-guards.
    




    acl2-sources/doc/HTML/GUARD-HINTS.html0000664002132200015000000000070012222333521016467 0ustar kaufmannacl2 GUARD-HINTS.html -- ACL2 Version 6.3

    GUARD-HINTS

    xargs keyword :GUARD-HINTS
    Major Section:  MISCELLANEOUS
    

    See xargs.




    acl2-sources/doc/HTML/GUARD-INTRODUCTION.html0000664002132200015000000000613212222333521017530 0ustar kaufmannacl2 GUARD-INTRODUCTION.html -- ACL2 Version 6.3

    GUARD-INTRODUCTION

    introduction to guards in ACL2
    Major Section:  GUARD
    

    Most users can probably profit by avoiding dealing with guards most of the time. If they seem to get in the way, they can be ``turned off'' using the command :set-guard-checking nil; for more about this, see set-guard-checking. For more about guards in general, see guard.

    The guard on a function symbol is a formula about the formals of the function. To see the guard on a function, use the keyword command :args. See args. To specify the guard on a function at defun-time, use the :guard xarg (See xargs) or type declarations (see declare). The latter may be preferable if you want the host Lisp compiler to use this information.

    Guards can be seen as having either of two roles: (a) they are a specification device allowing you to characterize the kinds of inputs a function ``should'' have, or (b) they are an efficiency device allowing logically defined functions to be executed directly in Common Lisp. Briefly: If the guards of a function definition are ``verified'' (see verify-guards), then the evaluation of a call of that function on arguments satisfying its guard will have the following property:

    All subsequent function calls during that evaluation will be on arguments satisfying the guard of the called function.

    The consequence of this fact for (a) is that your specification function is well-formed, in the sense that the values returned by this function on appropriate arguments only depend on the restrictions of the called functions to their intended domains. The consequence of this fact for (b) is that in the ACL2 system, when a function whose guards have been verified is called on arguments that satisfy its guard, then the raw lisp function defined by this function's defun event is used to evaluate the call. Note however that even when the user-supplied defun is not used, ACL2 uses a corresponding ``executable counterpart'' that generally performs, we expect, nearly as well as the raw lisp function. See comp to see how compilation can speed up both kinds of execution.

    Let us turn next to the issue of the relationship between guards and evaluation. See guards-and-evaluation.




    acl2-sources/doc/HTML/GUARD-MISCELLANY.html0000664002132200015000000001310612222333521017246 0ustar kaufmannacl2 GUARD-MISCELLANY.html -- ACL2 Version 6.3

    GUARD-MISCELLANY

    miscellaneous remarks about guards
    Major Section:  GUARD
    

    The discussion of guards concludes here with a few miscellaneous remarks. (Presumably you found this documentation by following a link; see guards-for-specification.) For further information related to guards other than what you find under ``guard,'' see any of the following documentation topics: guard-example, set-verify-guards-eagerness, set-guard-checking, verify-guards, and (for a discussion of keyword :SPLIT-TYPES) xargs.

    Defun can be made to try to verify the guards on a function. This is controlled by the ``defun-mode'' of the defun; see defun-mode. The defun-mode is either as specified with the :mode xarg of the defun or else defaults to the default defun-mode. See default-defun-mode. If the defun-mode of the defun is :logic and either a guard is specified explicitly or :verify-guards t is specified in the xargs, then we attempt to verify the guards of the function. Otherwise we do not. (But see set-verify-guards-eagerness for how to modify this behavior.)

    It is sometimes impossible for the system to verify the guards of a recursive function at definition time. For example, the guard conjectures might require the invention and proof of some inductively derived property of the function (as often happens when the value of a recursive call is fed to a guarded subroutine). So sometimes it is necessary to define the function using :verify-guards nil then to state and prove key theorems about the function, and only then have the system attempt guard verification. Post-defun guard verification is achieved via the event verify-guards. See verify-guards.

    It should be emphasized that guard verification affects only two things: how fast ACL2 can evaluate the function and whether the function is executed correctly by raw Common Lisp, without guard violations. Since ACL2 does not use the raw Common Lisp definition of a function to evaluate its calls unless that function's guards have been verified, the latter effect is felt only if you run functions in raw Common Lisp rather than via ACL2's command loop.

    Guard verification does not otherwise affect the theorem prover or the semantics of a definition. If you are not planning on running your function on ``big'' inputs and you don't care if your function runs correctly in raw Common Lisp (e.g., you have formalized some abstract mathematical property and just happened to use ACL2 as your language), there is no need to suffer through guard verification. Often users start by not doing guard verification and address that problem later. Sometimes you are driven to it, even in mathematical projects, because you find that you want to run your functions particularly fast or in raw Common Lisp.

    If certify-book is used to compile a file, and the file contains functions with unverified guard conjectures, then you will be warned that the compiled file cannot be loaded into raw Common Lisp with the expectation that the functions will run correctly. This is just the same point we have been making: ACL2 and Common Lisp agree only on the restricted domains specified by our guards. When guards are violated, Common Lisp can do anything. When you call a compiled function on arguments violating its guards, the chances are only increased that Common Lisp will go berserk, because compiled functions generally check fewer things at runtime and tend to be more fragile than interpreted ones.

    Finally, we note that ACL2 collects up guards from declare forms in order of appearance. So for example, the declare form

    (declare (xargs :guard (foo x))
             (type string x)
    
    will generate the guard (and (foo x) (stringp x)), while the form
    (declare (type string x)
             (xargs :guard (foo x))
    
    will generate the guard (and (stringp x) (foo x)). The only exception to this rule is the case that :guard and :stobjs are specified in the same xargs form, in which case the :stobjs form will be treated as through it comes before the :guard form.




    acl2-sources/doc/HTML/GUARD-OBLIGATION.html0000664002132200015000000000734412222333522017245 0ustar kaufmannacl2 GUARD-OBLIGATION.html -- ACL2 Version 6.3

    GUARD-OBLIGATION

    the guard proof obligation
    Major Section:  OTHER
    

    See verify-guards, and see guard for a discussion of guards. Also see verify-guards-formula for a utility provided for viewing the guard proof obligation, without proof. Guard-obligation is a lower level function for use in system programs, not typically appropriate for most ACL2 users. If you simply want to see the guard proof obligations, see verify-guards-formula.

    Example Form:
    (guard-obligation 'foo nil 'top-level state)
    (guard-obligation '(if (consp x) (foo (car x)) t) nil 'my-function state)
    
    General Forms:
    (guard-obligation name guard-debug ctx state)
    (guard-obligation term guard-debug ctx state)
    
    where the first argument is either the name of a function or theorem or is a non-variable term that may be in untranslated form; guard-debug is typically nil but may be t (see guard-debug); ctx is a context (typically, a symbol used in error and warning messages); and state references the ACL2 state.

    If you want to obtain the formula but you don't care about the so-called ``tag tree'':

    (mv-let (erp val state)
            (guard-obligation x guard-debug 'top-level state)
            (if erp
               ( .. code for handling error case, e.g., name is undefined .. )
              (let ((cl-set (cadr val))) ; to be proved for guard verification
                ( .. code using cl-set, which is a list of clauses,
                     implicitly conjoined, each of which is viewed as
                     a disjunction .. ))))
    

    The form (guard-obligation x guard-debug ctx state) evaluates to a triple (mv erp val state), where erp is nil unless there is an error, and state is the ACL2 state. Suppose erp is nil. Then val is the keyword :redundant if the corresponding verify-guards event would be redundant; see redundant-events. Otherwise, val is a tuple (list* names cl-set ttree), where: names is (cons :term xt) if x is not a variable, where xt is the translated form of x; and otherwise is a list containing x along with, if x is defined in a mutual-recursion, any other functions defined in the same mutual-recursion nest; cl-set is a list of lists of terms, viewed as a conjunction of clauses (each viewed (as a disjunction); and ttree is an assumption-free tag-tree that justifies cl-set. (The notion of ``tag-tree'' may probably be ignored except for system developers.)

    Guard-obligation is typically used for function names or non-variable terms, but as for verify-guards, it may also be applied to theorem names.

    See the source code for verify-guards-formula for an example of how to use guard-obligation.




    acl2-sources/doc/HTML/GUARD-QUICK-REFERENCE.html0000664002132200015000000001075212222333521017762 0ustar kaufmannacl2 GUARD-QUICK-REFERENCE.html -- ACL2 Version 6.3

    GUARD-QUICK-REFERENCE

    brief summary of guard checking and guard verification
    Major Section:  GUARD
    

    For a careful introduction to guards, see guard.

    I. GUARD CHECKING DURING EXECUTION

    Effect

    Guards on definitions are checked at execution time (except for guards on subsidiary calls of recursive or mutually recursive functions).

    When does it happen

    By default, guards are checked for all forms submitted at the top level.

    To disable
    :set-guard-checking nil ; skip raw Lisp if there is a guard violation :set-guard-checking :none ; skip guard checking entirely

    To (re-)enable
    :set-guard-checking t

    See set-guard-checking for more options.

    II. GUARD VERIFICATION

    Effect

    A proof is attempted of the obligations arising from the guards of subsidiary functions in a defun, defthm, or defaxiom event. In the case of a defun, the guard itself is also verified (under an implicit guard of t).

    When does it happen

    Only names of defined functions, defthms, and defaxioms are subject to guard verification. Guard verification may occur when functions are defined (using defun), but it requires an explicit call of verify-guards in order to verify guards for defthms and defaxioms. Constrained functions (see encapsulate) may not have their guards verified.

    (verify-guards foo ...)
    causes guard verification for the defun, defthm, or defaxiom named by foo, if it has not already been successfully done. The default defun-mode (see default-defun-mode) must be :logic, or else this event is ignored.

    (defun foo ...)
    causes guard verification of foo if and only if the following two conditions are both met. First, foo is processed in :logic mode (either by setting mode :logic globally, or by including :mode :logic in the xargs declaration). Second, at least one of the following sub-conditions is met. Also see xargs, and see set-verify-guards-eagerness for how to change this behavior.

    1. The xargs declaration specifies a :guard.

    2. There is at least one type declaration (see declare).

    3. The xargs declaration specifies :stobjs.

    4. The xargs declaration specifies :verify-guards t.

    (verify-termination foo ...)
    causes guard verification of foo if foo is a function currently defined in :program mode and the criteria for guard verification of a defun form are met, as discussed above. The default defun-mode (see default-defun-mode) must be :logic, or else this event is ignored.




    acl2-sources/doc/HTML/GUARD.html0000664002132200015000000000750412222333521015615 0ustar kaufmannacl2 GUARD.html -- ACL2 Version 6.3

    GUARD

    restricting the domain of a function
    Major Section:  MISCELLANEOUS
    

    The ACL2 system provides a mechanism for restricting a function to a particular domain. Such restrictions are called ``guards.'' A definition's guard has no effect on the logical axiom added when that definition is accepted by ACL2, and novices are often best served by avoiding guards. However, guards can be useful as a specification device or for increasing execution efficiency. To make such a restriction, use the :guard keyword (see xargs) or, especially if you want the host Lisp compiler to use this information, use type declarations (see declare; also see xargs for a discussion of the split-types keyword). The general issue of guards and guard verification is discussed in the topics listed below.

    Some Related Topics

    To begin further discussion of guards, see guard-introduction. That section directs the reader to further sections for more details. To see just a summary of some commands related to guards, see guard-quick-reference. For a discussion of the use of proof to verify the absence of guard violations, see verify-guards.




    acl2-sources/doc/HTML/GUARDS-AND-EVALUATION.html0000664002132200015000000004605612222333521020012 0ustar kaufmannacl2 GUARDS-AND-EVALUATION.html -- ACL2 Version 6.3

    GUARDS-AND-EVALUATION

    the relationship between guards and evaluation
    Major Section:  GUARD
    

    The guard has no effect on the logical axiom added by the definition of a function. It does, however, have consequences for how calls of that function are evaluated in ACL2. We begin by explaining those consequences, when ACL2 is in its default ``mode,'' i.e., as originally brought up. In subsequent discussion we'll consider other ways that guards can interact with evaluation.

    For more about guards in general, see guard. For in-depth discussion of the interaction between the defun-mode and guard checking, see set-guard-checking, see guard-evaluation-table, see guard-evaluation-examples-script, and see guard-evaluation-examples-log. Also see generalized-booleans for discussion about a subtle issue in the evaluation of certain Common Lisp functions.

    Guards and evaluation I: the default

    Consider the following very simple definition.

    (defun foo (x) (cons 1 (cdr x)))
    
    First consider how raw Common Lisp behaves when evaluating calls of this function. To evaluate (foo x) for some expression x, first x is evaluated to some value v, and then (cons 1 (cdr x)) is evaluated with x bound to v. For example, if v is (cons 'a 3), then Common Lisp computes (cons 1 3). But if (for example) v is a number, e.g., 7, then there is no way to predict what Common Lisp might do. Some implementations would cause ``sensible'' errors, others might return nonsense, still others might crash the host machine. The results tend toward the catastrophic if the call of foo in question is in compiled code.

    Now by default, ACL2 evaluates calls of foo exactly as Common Lisp does, except that it uses guards to check the ``legality'' of each function call. So for example, since (cdr x) has a guard of (or (consp x) (equal x nil)), the call (foo 7) would cause a ``guard violation,'' as illustrated below.

    ACL2 !>(foo 7)
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol CDR, which
    is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
    call (CDR 7).
    
    ACL2 !>
    
    Thus, the relation between evaluation in ACL2 and evaluation in Common Lisp is that the two produce the very same results, provided there is no guard violation.

    Guards and evaluation II: :set-guard-checking.

    The ACL2 logic is a logic of total functions. That is, every application of a function defined has a completely specified result. See the documentation for each individual primitive for the specification of what it returns when its guard is violated; for example, see cdr.

    The presence of guards thus introduces a choice in the sense of evaluation. When you type a form for evaluation do you mean for guards to be checked or not? Put another way, do you mean for the form to be evaluated in Common Lisp (if possible) or in the ACL2 logic? Note: If Common Lisp delivers an answer, it will be the same as in the logic, but it might be erroneous to execute the form in Common Lisp. For example, the ACL2 logic definition of cdr implies that the cdr of an atom is nil; see cdr. So: should (cdr 7) cause a guard violation error or return nil?

    The top-level ACL2 loop has a variable which controls which sense of execution is provided. By default, ``guard checking'' is on, by which we mean that guards are checked at runtime, in the sense already described. To allow execution to proceed in the logic when there is a guard violation, do :set-guard-checking nil; or evaluate :set-guard-checking :none to skip guard checking entirely. To turn ``guard checking'' back on, execute the top-level form :set-guard-checking t. The status of guard checking reflected in the prompt; guard-checking is ``on'' when the prompt contains an exclamation mark (also see default-print-prompt). For example,

    ACL2 !>
    
    means guard checking is on and
    ACL2 >
    
    means guard checking is off. The exclamation mark can be thought of as ``barring'' certain computations. The absence of the mark suggests the absence of error messages or unbarred access to the logical axioms. Thus, for example
    ACL2 !>(car 'abc)
    
    will signal an error, while
    ACL2 >(car 'abc)
    
    will return nil. To return to our previous example: with guard checking off, (foo 7) evaluates to (cons 1 nil). Also see set-guard-checking.

    Guards and evaluation III: guard verification

    Consider the defininition of foo given above, but modified so that a reasonable guard of (consp x) is specified, as shown below.

    (defun foo (x)
      (declare (xargs :guard (consp x)))
      (cons 1 (cdr x)))
    
    We say ``reasonable guard'' above because if x is such that (consp x) holds, then the call of cdr in the evaluation of (foo x) will not cause a guard violation. Thus, it ``should'' be legal to evaluate (foo x), for any such x, simply by evaluating this form in raw Common Lisp.

    The verify-guards event has been provided for this purpose. Details may be found elsewhere; see verify-guards. Briefly, for any defined function fn, the event (verify-guards fn) attempts to check the condition discussed above, that whenever fn is called on arguments that satisfy its guard, the evaluation of this call will proceed without any guard violation. (Moreover, the guard itself should be evaluable without any guard violation.) If this check is successful, then future calls of this sort will be evaluated in raw Common Lisp.

    Returning to our example above, the (verify-guards foo) will succeed because the guard (consp x) of foo implies the guard generated from the call (cdr x) in the body of the definition, namely, (or (consp x) (equal x nil)) (see cdr). Then the evaluation of (foo (cons 'a 3)) will take place in raw Common Lisp, because (cons 'a 3) satisfies the guard of foo.

    This ability to dive into raw Common Lisp hinges on the proof that the guards you attach to your own functions are sufficient to ensure that the guards encountered in the body are satisfied. This is called ``guard verification.'' Once a function has had its guards verified, then ACL2 can evaluate the function somewhat faster (but see ``Guards and evaluation V: efficiency issues'' below). Perhaps more importantly, ACL2 can also guarantee that the function will be evaluated correctly by any implementation of Common Lisp (provided the guard of the function is satisfied on the input). That is, if you have verified the guards of a system of functions and you have determined that they work as you wish in your host ACL2 (perhaps by proving it, perhaps by testing), then they will work identically in any Common Lisp.

    There is a subtlety to our treatment of evaluation of calls of functions whose guards have been verified. If the function's guard is not satisfied by such a call, then no further attempt is made to evaluate any call of that function in raw lisp during the course of evaluation of that call. This is obvious if guard checking is on, because an error is signalled the first time its guard is violated; but in fact it is also true when guard checking is off. See guard-example for an example.

    Guards and evaluation IV: functions having :program mode

    Strictly speaking, functions in :program mode (see defun-mode) do not have definitions in the ACL2 logic. So what does it mean to evaluate calls of such functions in ACL2? In general we treat :program functions much as we treat :logic functions whose guards have been verified, except that when no error occurs then the corresponding raw Lisp function is always called. (We say ``in general'' because there are exceptions, discussed in the ``Aside'' just below.) Note that when the guard of a function in :logic mode is violated, there is still a value that the ACL2 logic proves is equal to the given call. But the same cannot be said for a function in :program mode. Nevertheless, for the sake of convenience we go ahead and evaluate the corresponding raw Lisp function except in the situation where the guard is violated and guard-checking is on, aside from the following:

    Aside. There are exceptions to the use of raw Lisp, discussed just above, to evaluate calls of :program mode functions. The primary one is that after :set-guard-checking :none, evaluation of user-defined :program mode function calls is done in the ACL2 logic, not in raw Lisp. The more obscure exception is that during expansion of macros and make-event forms, and during evaluation of defconst forms, ACL2 enters a ``safe mode'' in which this escape to raw Lisp is prevented. The following example illustrates how the user can experiment directly with safe mode, though it is preferred to use :set-guard-checking :none if you are happy to skip all guard checking and evaluate forms in the logic.

    ACL2 !>(defun foo (x)
             (declare (xargs :mode :program :guard t))
             (car x))
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO
    ACL2 !>(foo 3)
    Error: Attempt to take the car of 3 which is not listp.
      [condition type: SIMPLE-ERROR]
    
    Restart actions (select using :continue):
     0: Return to Top Level (an "abort" restart).
     1: Abort entirely from this process.
    [1] ACL2(2): :pop
    ACL2 !>(assign safe-mode t)
     T
    ACL2 !>(foo 3)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol CAR, which
    is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
    call (CAR 3).  See :DOC trace for a useful debugging utility.  See
    :DOC set-guard-checking for information about suppressing this check
    with (set-guard-checking :none), as recommended for new users.
    
    ACL2 !>(assign safe-mode nil)
     NIL
    ACL2 !>(foo 3)
    Error: Attempt to take the car of 3 which is not listp.
      [condition type: SIMPLE-ERROR]
    
    Restart actions (select using :continue):
     0: Return to Top Level (an "abort" restart).
     1: Abort entirely from this process.
    [1] ACL2(2):
    
    The other exception occurs after set-guard-checking can be called with a value of :all; see set-guard-checking. End of aside.

    Thus, as with :logic functions: when a guard has been satisfied on a call of a function with :program mode, no subsidiary guard checking will be done.

    Notice that by treating functions in :program mode like functions whose guards have been verified, we are using raw lisp to compute their values when their guards are met. We do not check guards any further once raw lisp is invoked. This can lead to hard lisp errors if the guards are not appropriate, as illustrated below.

    ACL2 >:program
    ACL2 p>(defun foo (x)
            (declare (xargs :guard t))
            (cons 1 (cdr x)))
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.02 seconds (prove: 0.00, print: 0.00, proof tree: 0.00, other: 0.02)
     FOO
    ACL2 p>(foo 3)
    
    Error: 3 is not of type LIST.
    Fast links are on: do (use-fast-links nil) for debugging
    Error signalled by CDR.
    Broken at COND.  Type :H for Help.
    ACL2>>
    
    See defun-mode-caveat.

    However, here is a way to get ACL2 to do run-time guard checking for user-defined :program mode functions. With this method, ACL2 will evaluate calls of user-defined :program mode functions in a manner that follows their ACL2 definitions. Simply execute the following in the ACL2 loop to put ACL2 into a ``safe mode.''

    (f-put-global 'safe-mode t state)
    
    Let us revisit the example above, using safe mode. Notice that the guard of cdr is now being checked, because the executable counterpart of foo is being called even though the guard is t.
    ACL2 !>(f-put-global 'safe-mode t state)
    <state>
    ACL2 !>:program
    ACL2 p!>(defun foo (x)
              (declare (xargs :guard t))
              (cons 1 (cdr x)))
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO
    ACL2 p!>(foo 3)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol CDR, which
    is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
    call (CDR 3).  See :DOC trace for a useful debugging utility.  See
    :DOC set-guard-checking for information about suppressing this check
    with (set-guard-checking :none), as recommended for new users.
    
    ACL2 p!>
    
    If we go back into ``unsafe'' mode, then we once again see a raw Lisp error, as we now illustrate.
    ACL2 p!>(f-put-global 'safe-mode nil state)
    <state>
    ACL2 p!>(foo 3)
    
    Error: 3 is not of type LIST.
    Fast links are on: do (si::use-fast-links nil) for debugging
    Error signalled by CDR.
    Broken at COND.  Type :H for Help.
    ACL2>>
    

    Guards and evaluation V: efficiency issues

    We have seen that by verifying the guards for a :logic function, we arrange that raw lisp is used for evaluation of calls of such functions when the arguments satisfy its guard.

    This has several apparent advantages over the checking of guards as we go. First, the savings is magnified as your system of functions gets deeper: the guard is checked upon the top-level entry to your system and then raw Common Lisp does all the computing. Second, if the raw Common Lisp is compiled (see compilation), enormous speed-ups are possible. Third, if your Common Lisp or its compiler does such optimizations as tail-recursion removal, raw Common Lisp may be able to compute your functions on input much ``bigger'' than ACL2 can.

    The first of these advantages is quite important if you have complicated guards. However, the other two advantages are probably not very important, as we now explain.

    When a function is defined in :logic mode, its defun is executed in raw Common Lisp. (We might call this the ``primary'' raw lisp definition of the function.) However, a corresponding ``logic definition'' is also executed. The ``logic definition'' is a defun in raw lisp that checks guards at runtime and escapes to the primary raw lisp definition if the guard holds of the arguments and the function has already had its guards verified. Otherwise the logic definition executes the body of the function by calling the logic definitions of each subroutine. Now it is true that compilation generally speeds up execution enormously. However, the :comp command (see comp) compiles both of the raw lisp definitions associated with a :logic function. Also, we have attempted to arrange that for every tail recursion removal done on the actual defun, a corresponding tail recursion removal is done on the ``logic definition.''

    We believe that in most cases, the logic definition executes almost as fast as the primary raw lisp definition, at least if the evaluation of the guards is fast. So, the main advantage of guard verification is probably that it lets you know that the function may be executed safely in raw lisp, returning the value predicted by the ACL2 logic, whenever its arguments satisfy its guard. We envision the development of systems of applicative lisp functions that have been developed and reasoned about using ACL2 but which are intended for evaluation in raw Common Lisp (perhaps with only a small ``core'' of ACL2 loaded), so this advantage of guard verification is important.

    Nevertheless, guard verification might be important for optimal efficiency when the functions make use of type declarations. For example, at this writing, the GCL implementation of Common Lisp can often take great advantage of declare forms that assign small integer types to formal parameters. Note that while type declarations contributed to the guard by default, they can be proved from the guard instead; see xargs for a discussion of the :SPLIT-TYPES keyword.

    To continue the discussion of guards, see guards-for-specification to read about the use of guards as a specification device.




    acl2-sources/doc/HTML/GUARDS-FOR-SPECIFICATION.html0000664002132200015000000000516612222333521020344 0ustar kaufmannacl2 GUARDS-FOR-SPECIFICATION.html -- ACL2 Version 6.3

    GUARDS-FOR-SPECIFICATION

    guards as a specification device
    Major Section:  GUARD
    

    A use of guard verification that has nothing to do with efficiency is as a way to gain confidence in specifications. This use has the feel of ``types'' in many traditional programming languages, though guards allow much greater expressiveness than most systems of types (and unfortunately, as a result they are not syntactically checkable).

    For more discussion of guards in general, see guard.

    Suppose you have written a collection of function definitions that are intended to specify the behavior of some system. Perhaps certain functions are only intended to be called on certain sorts of inputs, so you attach guards to those functions in order to ``enforce'' that requirement. And then, you verify the guards for all those functions.

    Then what have you gained, other than somewhat increased efficiency of execution (as explained above), which quite possibly isn't your main concern? You have gained the confidence that when evaluating any call of a (specification) function whose arguments satisfy that function's guard, all subsequent function calls during the course of evaluation will have this same property, that the arguments satisfy the guard of the calling function. In logical terms, we can say that the equality of the original call with the returned value is provable from weakened versions of the definitions, where each definitional axiom is replaced by an implication whose antecedent is the requirement that the arguments satisfy the guard and whose consequent is the original axiom. For example,

    (defun foo (x)
      (declare (xargs :guard (consp x)))
      (cons 1 (cdr x)))
    
    originally generates the axiom
    (equal (foo x)
           (cons 1 (cdr x)))
    
    but in fact, when evaluation involves no guard violation then the following weaker axiom suffices in the justification of the evaluation.
    (implies (consp x)
             (equal (foo x)
                    (cons 1 (cdr x))))
    
    If you are following links to read this documentation as a hypertext style document, then please see guard-miscellany. This concludes our discussion of guards with miscellaneous remarks, and also contains pointers to related topics.




    acl2-sources/doc/HTML/Guards.html0000664002132200015000000000402712222333526016202 0ustar kaufmannacl2 Guards.html -- ACL2 Version 6.3

    Guards

    Common Lisp functions are partial; they are not defined for all possible inputs. But ACL2 functions are total. Roughly speaking, the logical function of a given name in ACL2 is a completion of the Common Lisp function of the same name obtained by adding some arbitrary but ``natural'' values on arguments outside the ``intended domain'' of the Common Lisp function.

    ACL2 requires that every ACL2 function symbol have a ``guard,'' which may be thought of as a predicate on the formals of the function describing the intended domain. The guard on the primitive function car , for example, is (or (consp x) (equal x nil)), which requires the argument to be either an ordered pair or nil. We will discuss later how to specify a guard for a defined function; when one is not specified, the guard is t which is just to say all arguments are allowed.

    But guards are entirely extra-logical: they are not involved in the axioms defining functions. If you put a guard on a defined function, the defining axiom added to the logic defines the function on all arguments, not just on the guarded domain.

    So what is the purpose of guards?

    The key to the utility of guards is that we provide a mechanism, called ``guard verification,'' for checking that all the guards in a formula are true. See verify-guards. This mechanism will attempt to prove that all the guards encountered in the evaluation of a guarded function are true every time they are encountered.

    For a thorough discussion of guards, see the paper [km97] in the ACL2 bibliography.




    acl2-sources/doc/HTML/Guessing_the_Type_of_a_Newly_Admitted_Function.html0000664002132200015000000000233612222333526026225 0ustar kaufmannacl2 Guessing_the_Type_of_a_Newly_Admitted_Function.html -- ACL2 Version 6.3

    Guessing the Type of a Newly Admitted Function

    When a function is admitted to the logic, ACL2 tries to ``guess'' what type of object it returns. This guess is codified as a term that expresses a property of the value of the function. For app the term is

    (OR (CONSP (APP X Y))
        (EQUAL (APP X Y) Y))
    
    which says that app returns either a cons or its second argument. This formula is added to ACL2's rule base as a type-prescription rule. Later we will discuss how rules are used by the ACL2 theorem prover. The point here is just that when you add a definition, the database of rules is updated, not just by the addition of the definitional axiom, but by several new rules.

    You should now return to the Walking Tour.




    acl2-sources/doc/HTML/Guiding_the_ACL2_Theorem_Prover.html0000664002132200015000000000241712222333526022765 0ustar kaufmannacl2 Guiding_the_ACL2_Theorem_Prover.html -- ACL2 Version 6.3

    Guiding the ACL2 Theorem Prover

    Now that you have seen the theorem prover in action you might be curious as to how you guide it.

    Look at the picture above. It is meant to suggest that Q is an important lemma needed for the proof of P. Note that to lead the prover to the proof of P the user first proves Q. In a way, the formulation and proof of Q is a hint to the prover about how to prove P.

    The user usually doesn't think of Q or recognize the need to prove it separately until he or she sees the theorem prover fail to prove P without it ``knowing'' Q.

    The way the user typically discovers the need for Q is to look at failed proofs.




    acl2-sources/doc/HTML/HANDS-OFF.html0000664002132200015000000000067212222333521016217 0ustar kaufmannacl2 HANDS-OFF.html -- ACL2 Version 6.3

    HANDS-OFF

    hints keyword :HANDS-OFF
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/HARD-ERROR.html0000664002132200015000000000361612222333523016362 0ustar kaufmannacl2 HARD-ERROR.html -- ACL2 Version 6.3

    HARD-ERROR

    print an error message and stop execution
    Major Section:  ACL2-BUILT-INS
    

    (Hard-error ctx str alist) causes evaluation to halt with a short message using the ``context'' ctx. An error message is first printed using the string str and alist alist that are of the same kind as expected by fmt. See fmt. Also see er for a macro that provides a unified way of signaling errors.

    Hard-error has a guard of t. Also see illegal for a similar capability which however has a guard of nil that supports static checking using guard verification, rather than using dynamic (run-time) checking. This distinction is illustrated elsewhere: see prog2$ for examples.

    Semantically, hard-error ignores its arguments and always returns nil. But if a call (hard-error ctx str alist) is encountered during evaluation, then the string str is printed using the association list alist (as in fmt), after which evaluation halts immediately. Here is a trivial, contrived example.

    ACL2 !>(cons 3 (hard-error 'my-context
                                "Printing 4: ~n0"
                                (list (cons #\0 4))))
    
    
    HARD ACL2 ERROR in MY-CONTEXT:  Printing 4: four
    
    
    
    ACL2 Error in TOP-LEVEL:  Evaluation aborted.
    
    ACL2 !>
    




    acl2-sources/doc/HTML/HEADER.html0000664002132200015000000000142012222333525015676 0ustar kaufmannacl2 HEADER.html -- ACL2 Version 6.3

    HEADER

    return the header of a 1- or 2-dimensional array
    Major Section:  ARRAYS
    

    Example Form:
    (header 'delta1 a)
    
    General Form:
    (header name alist)
    
    where name is arbitrary and alist is a 1- or 2-dimensional array. This function returns the header of the array alist. The function operates in virtually constant time if alist is the semantic value of name. See arrays.




    acl2-sources/doc/HTML/HELP.html0000664002132200015000000000105512222333516015502 0ustar kaufmannacl2 HELP.html -- ACL2 Version 6.3

    HELP

    brief survey of ACL2 features
    Major Section:  DOCUMENTATION
    

    Example:
    ACL2 !>:help
    

    See lp for general information about the top-level command environment for ACL2.




    acl2-sources/doc/HTML/HIDDEN-DEATH-PACKAGE.html0000664002132200015000000000660012222333516017562 0ustar kaufmannacl2 HIDDEN-DEATH-PACKAGE.html -- ACL2 Version 6.3

    HIDDEN-DEATH-PACKAGE

    handling defpkg events that are local
    Major Section:  DEFPKG
    

    This documentation topic explains a little bit about certain errors users may see when attempting to evaluate a defpkg event. In brief, if you see an error that refers you to this topic, you are probably trying to admit a defpkg event, and you should change the name of the package to be introduced by that event.

    Recall that defpkg events introduce axioms, for example as follows.

    ACL2 !>(defpkg "PKG0" '(a b))
    
    Summary
    Form:  ( DEFPKG "PKG0" ...)
    Rules: NIL
    Warnings:  None
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     "PKG0"
    ACL2 !>:pr! "PKG0"
    
    Rune:       (:REWRITE PKG0-PACKAGE)
    Status:     Enabled
    Lhs:        (SYMBOL-PACKAGE-NAME (INTERN-IN-PACKAGE-OF-SYMBOL X Y))
    Rhs:        "PKG0"
    Hyps:       (AND (STRINGP X)
                     (NOT (MEMBER-SYMBOL-NAME X '(A B)))
                     (SYMBOLP Y)
                     (EQUAL (SYMBOL-PACKAGE-NAME Y) "PKG0"))
    Equiv:      EQUAL
    Backchain-limit-lst:    NIL
    Subclass:   BACKCHAIN
    Loop-stopper: NIL
    ACL2 !>
    
    Now, a defpkg event may be executed underneath an encapsulate or include-book form that is marked local. In that case, traces of the added axiom will disappear after the surrounding encapsulate or include-book form is admitted. This can cause inconsistencies. (You can take our word for it, or you can look at the example shown in the ``Essay on Hidden Packages'' in source file axioms.lisp.)

    In order to prevent unsoundness, then, ACL2 maintains the following invariant. Let us say that a defpkg event is ``hidden'' if it is in support of the current logical world but is not present in that world as an event, because it is local as indicated above. We maintain the invariant that all defpkg events, even if ``hidden'', are tracked under-the-hood in the current logical world. Sometimes this property causes defpkg events to be written to the portcullis of a book's certificate (see books). At any rate, if you then try to define the package in a manner inconsistent with the earlier such definition, that is, with a different imports list, you will see an error because of the above-mentioned tracking.

    (By the way, this topic's name comes from Holly Bell, who heard "hidden death package" instead of "hidden defpkg". The description seemed to fit. Thanks, Holly!)




    acl2-sources/doc/HTML/HIDDEN-DEFPKG.html0000664002132200015000000000072112222333516016642 0ustar kaufmannacl2 HIDDEN-DEFPKG.html -- ACL2 Version 6.3

    HIDDEN-DEFPKG

    handling defpkg events that are local
    Major Section:  DEFPKG
    

    See hidden-death-package




    acl2-sources/doc/HTML/HIDE.html0000664002132200015000000001227212222333521015462 0ustar kaufmannacl2 HIDE.html -- ACL2 Version 6.3

    HIDE

    hide a term from the rewriter
    Major Section:  MISCELLANEOUS
    

    Hide is actually the identity function: (hide x) = x for all x. However, terms of the form (hide x) are ignored by the ACL2 rewriter, except when explicit :expand hints are given for such terms (see hints) or when rewrite rules explicitly about hide are available. An :expand hint that removes all calls of hide is:

    :expand ((:free (x) (hide x)))
    
    The above hint can be particularly useful when ACL2's equality heuristics apply hide to an equality after substituting it into the rest of the goal, if that goal (or a subgoal of it) fails to be proved.

    Hide terms are also ignored by the induction heuristics.

    Sometimes the ACL2 simplifier inserts hide terms into a proof attempt out of the blue, as it were. Why and what can you do about it? Suppose you have a constrained function, say constrained-fn, and you define another function, say another-fn, in terms of it, as in:

    (defun another-fn (x y z)
      (if (big-hairy-test x y z)
          (constrained-fn x y z)
          t))
    
    Suppose the term (another-fn 'a 'b 'c) arises in a proof. Since the arguments are all constants, ACL2 will try to reduce such a term to a constant by executing the definition of another-fn. However, after a possibly extensive computation (because of big-hairy-test) the execution fails because of the unevaluable call of constrained-fn. To avoid subsequent attempts to evaluate the term, ACL2 embeds it in a hide expression, i.e., rewrites it to (hide (another-fn 'a 'b 'c)).

    You might think this rarely occurs since all the arguments of another-fn must be constants. You would be right except for one special case: if another-fn takes no arguments, i.e., is a constant function, then every call of it fits this case. Thus, if you define a function of no arguments in terms of a constrained function, you will often see (another-fn) rewrite to (hide (another-fn)).

    We do not hide the term if the executable counterpart of the function is disabled -- because we do not try to evaluate it in the first place. Thus, to prevent the insertion of a hide term into the proof attempt, you can globally disable the executable counterpart of the offending defined function, e.g.,

    (in-theory (disable (:executable-counterpart another-fn))).
    

    It is conceivable that you cannot afford to do this: perhaps some calls of the offending function must be computed while others cannot be. One way to handle this situation is to leave the executable counterpart enabled, so that hide terms are introduced on the calls that cannot be computed, but prove explicit :rewrite rules for each of those hide terms. For example, suppose that in the proof of some theorem, thm, it is necessary to leave the executable counterpart of another-fn enabled but that the call (another-fn 1 2 3) arises in the proof and cannot be computed. Thus the proof attempt will introduce the term (hide (another-fn 1 2 3)). Suppose that you can show that (another-fn 1 2 3) is (contrained-fn 1 2 3) and that such a step is necessary to the proof. Unfortunately, proving the rewrite rule

    (defthm thm-helper
      (equal (another-fn 1 2 3) (constrained-fn 1 2 3)))
    
    would not help the proof of thm because the target term is hidden inside the hide. However,
    (defthm thm-helper
      (equal (hide (another-fn 1 2 3)) (constrained-fn 1 2 3)))
    
    would be applied in the proof of thm and is the rule you should prove.

    Now to prove thm-helper you need to use the two ``tricks'' which have already been discussed. First, to eliminate the hide term in the proof of thm-helper you should include the hint :expand (hide (another-fn 1 2 3)). Second, to prevent the hide term from being reintroduced when the system tries and fails to evaluate (another-fn 1 2 3) you should include the hint :in-theory (disable (:executable-counterpart another-fn)). Thus, thm-helper will actually be:

    (defthm thm-helper
      (equal (hide (another-fn 1 2 3)) (constrained-fn 1 2 3))
      :hints
      (("Goal" :expand (hide (another-fn 1 2 3))
               :in-theory (disable (:executable-counterpart another-fn)))))
    

    See eviscerate-hide-terms for how to affect the printing of hide terms.




    acl2-sources/doc/HTML/HINTS-AND-THE-WATERFALL.html0000664002132200015000000003167312222333521020201 0ustar kaufmannacl2 HINTS-AND-THE-WATERFALL.html -- ACL2 Version 6.3

    HINTS-AND-THE-WATERFALL

    how hints fit into the ACL2 proof waterfall
    Major Section:  MISCELLANEOUS
    

    Below we describe the flow of an ACL2 proof attempt, with special attention to how hints are applied during a proof. For most ACL2 users, only one point is important to take away from this documentation topic: you may specify hints during a proof (see hints; perhaps also see computed-hints and see default-hints), and they can be expected to behave intuitively. See the-method for a summary of how to interact with the ACL2 prover; see introduction-to-the-theorem-prover for a more detailed tutorial; and see hints for an introduction to ACL2 hints, including detailed documentation for specific hint types.

    The remainder of this topic serves as a reference in case one needs a deeper understanding of the workings of ACL2's handling of hints. Also, for examples of the sophisticated use of hints, primarily for experts, see community book books/hints/basic-tests.lisp.

    First, we describe the ACL2 ``waterfall'', which handles each goal either by replacing it with a list (possibly empty) of child goals, or else by putting the goal into a ``pool'' for later proof by induction. Then, we describe how hints are handled by the waterfall.

    The Waterfall.

    Each goal considered by the ACL2 prover passes through a series of proof processes, called the ``waterfall processes'', as stored in the constant *preprocess-clause-ledge*. The top process applies top-level hints, including :use hints; the next is a lightweight ``preprocess'' simplifier for ``simple'' rules (see simple); the next is the main ACL2 simplifier; and finally ACL2 attempts (in this order) destructor elimination, fertilization (heuristic use of equalities), generalization, and elimination of irrelevance. Each process may ``hit'', creating zero or more child goals that are each then handled at the top of the waterfall; or it may ``miss'', in which case the next process in the above sequence is considered. If all processes miss, then a ``push'' process defers the goal until it is later considered for proof by induction. When all goals have been thus handled, the goal most recently pushed for proof by induction is considered, and the process repeats.

    We next describe the two additional ways in which control can be returned to the top of the waterfall.

    When the simplification process is attempted unsuccessfully for a goal, the goal is deemed to have ``settled down''. In this case, and if no ancestor of the goal has settled down, then the ``settled-down'' process is deemed to have ``hit'' on the goal, the effect being that the goal makes a new pass through all the waterfall processes. (Other processes can then notice that settling down has occurred and modify their heuristics accordingly.) For example, if "Goal" simplifies to "Subgoal 2" (among others), and "Subgoal 2" simplifies to "Subgoal 2.3" (among others), which in turn is not further simplified, then the ``settled-down'' process hits on "Subgoal 2.3" but not on any of its children, their children, and so on.

    When simplification has missed (and thus the goal has settled down), the next proof process is normally destructor elimination. However, if a computed hint is suitable (in a sense described below; also see computed-hints, especially the discussion of stable-under-simplificationp), then that hint is selected as control is returned to the top of the waterfall. A subtlety is that in this case, if the most recent hit had been from settling down, then the prover ``changes its mind'' and considers that the goal has not yet settled down after all as it continues through the waterfall.

    Each time a goal is considered at the top of the waterfall, then before passing through the proof processes as described above, ACL2 searches for a relevant hint to select unless it has already been provided a hint in the ``stable-under-simplificationp'' case mentioned above. We turn now to a more thorough discussion of how hints are selected and applied.

    The handling of hints.

    In the discussion below we will ignore forcing rounds, as each forcing round is simply treated as a new proof attempt that uses the list of hints provided at the start of the proof.

    When the theorem prover is called by thm or events such as defthm, defun, and verify-guards, it gathers up the hints that have been supplied, often provided as a :hints argument, but for example using a :guard-hints argument for guard verification proofs. (ACL2(r) users (see real) may also employ :std-hints.) It then appends these to the front of the list of default hints (see default-hints). The resulting list becomes the initial value of the list of ``pending hints'', one of two critical lists maintained by the theorem prover to manage hints. The other critical list is a list of ``hint settings''; the two lists are maintained as follows.

    When a goal is first considered, a hint is selected from the list of pending hints if any is found to apply, as described below. If a hint is selected, then it takes effect and is removed from the pending hints. Except: if the selected hint is a computed hint with value t specified for :computed-hint-replacement, then it is not removed; and if that value is a list of hints, then that list is appended to the front of the list of pending hints after the selected hint is removed (also see computed-hints). The selected hint is also used to update the hint settings, as described below.

    The list of hint settings associates hint keywords with values. It is passed from the current goal to its children (and hence the children's children, and so on), though modified by hints selected from pending hints, as described below. This list is maintained so that when a goal is pushed for proof by induction, the hint settings are applied at the start of the proof by induction. Note that the list of hint settings is not re-applied to descendents of a goal in the current waterfall; a hint is applied only when it is selected (and also perhaps later as just described, through the stored hint settings at the start of a proof by induction). For example, if the hint selected for "Subgoal 3" includes :in-theory (enable foo), then the hint settings are correspondingly updated when processing "Subgoal 3", and they persist at subgoals such as "Subgoal 3.2" and "Subgoal 3.2.1" (unless overriden by hints on those goals); but the theory specifying foo is not re-installed at every such subgoal.

    When a hint is selected, the list of hint settings is updated so that for each keyword :kwd and associated value val from the hint, :kwd is associated with val in the hint settings, discarding any previous association of :kwd with a value in the hint settings. Except, certain ``top-level'' hints are never saved in the hint settings: :use, :cases, :by, :bdd, :or, and :clause-processor.

    For example, suppose that we specify the following hints, with no default hints.

    (("Goal" :expand ((bar x y)))
     ("Subgoal 3" :in-theory (enable foo)))
    
    These hints then become the initial list of pending hints. When the proof attempt begins, the prover encounters the top-level goal ("Goal") and pulls the "Goal" hint from the pending hints, so that the list of hint settings contains a value only for keyword :expand. This hint setting will remain for all children of the top-level goal as well, and their children, and so on, and will be inherited by induction -- in other words, it will remain throughout the entire proof. Now consider what happens when the proof reaches "Subgoal 3". At this point there is only one pending hint, which is in fact attached to that subgoal. Therefore, this hint is pulled from the pending hints (leaving that list empty), and the hint settings are extended by associating the :in-theory keyword with the theory represented by (enable foo). That theory is immediately installed until the prover finishes addressing "Subgoal 3", its children, their children, and so on; and until that completion is reached, the :in-theory keyword remains associated with the (enable foo) in the hint settings, although of course there is no re-installation of the theory at any ensuing child goal. When finally "Subgoal 3" and its descendents have been completed and the prover is about to consider "Subgoal 2", the :in-theory association is removed from the hint settings and the global theory is re-installed. However, the list of pending hints remains empty.

    It remains to describe how a hint is selected for a goal. When a goal is first considered (hence at the top of the waterfall), the list of pending hints is scanned, in order, until one of the hints is suitable for the goal. An explicit hint (goal-name :kwd1 val1 ... :kwdn valn) is suitable if goal-name is the name of the current goal and there is at least one keyword. A computed hint is suitable if it evaluates to a non-nil value. As indicated earlier in this documentation topic, an exception occurs when a computed hint is selected after simplification fails (the ``stable-under-simplificationp'' case): in that case, the goal returns to the top of the waterfall with that hint as the selected hint, and no additional search for a hint to select is made at that time.

    The following slightly tricky example illustrates handling of hints.

    ACL2 !>(set-default-hints '(("Goal" :do-not '(preprocess))))
     (("Goal" :DO-NOT '(PREPROCESS)))
    ACL2 !>(thm (equal (append (append x y) z) (append x y z))
                :hints (("Goal" :in-theory (disable car-cons))))
    
    ACL2 Warning [Hints] in ( THM ...):  The goal-spec "Goal" is explicitly
    associated with more than one hint.  All but the first of these hints
    may be ignored.  If you intended to give all of these hints, combine
    them into a single hint of the form ("Goal" :kwd1 val1 :kwd2 val2 ...).
    See :DOC hints-and-the-waterfall.
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    Name the formula above *1.
    

    The warning above is printed because "Goal" is associated with two pending hints: one given by the set-default-hints call and one supplied by the :hints keyword of the thm form. The :in-theory hint is selected because user-supplied hints are ahead of default hints in the list of pending hints; we then get the first ``Note'' above. The goal progresses through the waterfall without any proof process applying to the goal; in particular, it cannot be further simplified. After the simplification process, a ``settled-down'' process applies, as discussed above, immediately causing another trip through the waterfall. Since the :in-theory hint was earlier removed from the list of pending hints when it was applied, the default (:do-not) hint is now the only pending hint. That hint is applied, resulting in the second ``Note'' above.

    Again, more examples may be found in the community book books/hints/basic-tests.lisp. A particularly tricky but informative example in that book is the one related to nonlinearp-default-hint.

    Also see override-hints for an advanced feature that allows modification of the hint selected for a goal.




    acl2-sources/doc/HTML/HINTS.html0000664002132200015000000012140412222333521015634 0ustar kaufmannacl2 HINTS.html -- ACL2 Version 6.3

    HINTS

    advice to the theorem proving process
    Major Section:  MISCELLANEOUS
    

    Examples:
    The following :hints value is nonsensical.  Nevertheless, it
    illustrates all of the available hint keywords except the
    ``custom keywords'' (see custom-keyword-hints) definable
    by the user.
    
    :hints (("Goal"
             :do-not-induct t
             :do-not '(generalize fertilize)
             :expand ((assoc x a)
                      :lambdas
                      (:free (y) (:with member (member y z))))
             :restrict ((<-trans ((x x) (y (foo x)))))
             :hands-off (length binary-append)
             :in-theory (set-difference-theories
                          (current-theory :here)
                          '(assoc))
             :induct (and (nth n a) (nth n b))
             :use ((:instance assoc-of-append
                              (x a) (y b) (z c))
                   (:functional-instance
                     (:instance p-f (x a) (y b))
                     (p consp)
                     (f assoc)))
             :bdd (:vars (c a0 b0 a1 b1) :prove nil :bdd-constructors (cons))
             :clause-processor (:function cl-proc :hint (my-hint clause))
             :instructions (:x :prove)
             :cases ((true-listp a) (consp a))
             :by (:instance rev-rev (x (cdr z)))
             :nonlinearp t
             :backchain-limit-rw 3
             :reorder (4 7 2)
             :case-split-limitations (20 10)
             :no-op t
             :no-thanks t
             :error ("Bad value ~x0." 123)
             :or (hint-kwd-alist-1 ... hint-kwd-alist-k)
             :rw-cache-state nil
             :backtrack (my-computed-hint clause processor clause-list)))
    
    A very common hint is the :use hint, which in general takes as its value a list of ``lemma instances'' (see lemma-instance) but which allows a single lemma name as a special case:
    :hints (("[1]Subgoal *1/1.2'" :use lemma23))
    

    ACL2 also provides ``custom keyword'' hints (see custom-keyword-hints) and even more general ``computed hints'' for the advanced user (see computed-hints).

    Only the first hint applicable to a goal, as specified in the user-supplied list of :hints followed by the default hints (see default-hints-table), will be applied to that goal. For an advanced exception, see override-hints. For a detailed discussion of how hints fit into the ACL2 waterfall, see hints-and-the-waterfall. For examples of the sophisticated use of hints, primarily for experts, see community book books/hints/basic-tests.lisp.

    Background: Hints are allowed in all events that use the theorem prover. During defun events there are two different uses of the theorem prover: one to prove termination and another to verify the guards. To pass a hint to the theorem prover during termination proofs, use the :hints keyword in the defun's xargs declaration. To pass a hint to the theorem prover during the guard verification portion of admitting a defun, use the :guard-hints keyword in the defun's xargs declaration. The verify-guards event and the defthm event also use the theorem prover. To pass hints to them, use the :hints keyword argument to the event.

    General Form of Common :hints:
      ((goal-spec :key1 val1 ... :keyn valn)
       ...
       (goal-spec :key1 val1 ... :keyn valn))
    
    where goal-spec is as described elsewhere (see goal-spec) and the keys and their respective values are shown below with their interpretations. (We also provide ``computed hints'' but discuss them separately; see computed-hints.)

    :DO-NOT-INDUCT
    Value is t, :otf-flg-override, :otf, name or nil, indicating whether induction is permitted under the specified goal. If value is t or :otf-flg-override, then the attempt to apply induction to the indicated goal or any subgoal under the indicated goal will immediately cause the theorem prover to report failure, except that if :otf-flg t is specified (see otf-flg) and value is t, then the proof will continue until the time at which the goal pushed for induction is finally considered. The latter behavior is also what occurs if value is :otf. Thus, any non-nil value requires the indicated goal to be proved entirely by simplification, destructor elimination, and the other ``waterfall'' processes. Induction to prove the indicated goal (or any subgoal) is not permitted. See however the :induct hint below. If value is a symbol other than t, :otf-flg-override, :otf or nil, the theorem prover will give a ``bye'' to any subgoal that would otherwise be attacked with induction. This will cause the theorem prover to fail eventually but will collect the necessary subgoals. If value is nil, this hint means induction is permitted. Since that is the default, there is no reason to use the value nil. Note that a :do-not-induct hint is ignored for any goal on which an :induct hint is supplied. For an advanced example of the use of value :otf with override-hints, see community book books/hints/basic-tests.lisp.

    :DO-NOT
    Value is a term having at most the single free variable world, which when evaluated (with world bound to the current ACL2 logical world) produces a list of symbols that is a subset of the list

    (preprocess ;propositional logic, simple rules
     simplify   ;as above plus rewriting, linear arithmetic
     eliminate-destructors
     fertilize  ;use of equalities
     generalize
     eliminate-irrelevance).
    
    The hint indicates that the ``processes'' named should not be used at or below the goal in question. Thus, to prevent generalization and fertilization, say, include the hint
    :do-not '(generalize fertilize)
    
    If value is a single symbol, as in
    :do-not generalize,
    
    it is taken to be '(value).

    :EXPAND
    Value is a true list of terms, each of which is of one of the forms (let ((v1 t1)...) b) or (fn t1 ... tn), where fn is a defined function symbol with formals v1, ..., vn, and body b. Such a term is said to be ``expandable:'' it can be replaced by the result of substituting the ti's for the vi's in b. The terms listed in the :expand hint are expanded when they are encountered by the simplifier while working on the specified goal or any of its subgoals. We permit value to be a single such term instead of a singleton list. Remarks: (1) Allowed are ``terms'' of the form (:free (var1 var2 ... varn) pattern) where the indicated variables are distinct and pattern is a term. Such ``terms'' indicate that we consider the indicated variables to be instantiatable, in the following sense: whenever the simplifier encounters a term that can be obtained from pattern by instantiating the variables (var1 var2 ... varn), then it expands that term. (2) Also allowed are ``terms'' of the form (:with name term), where name is a function symbol, a macro name that denotes a function symbol (see macro-aliases-table), or a rune. The corresponding rule of class :rewrite, which is often a definition rule but need not be, is then used in place of the current body for the function symbol of term; see show-bodies and see set-body. If the rule is of the form (implies hyp (equiv lhs rhs)), then after matching lhs to the current term in a context that is maintaining equivalence relation equiv, ACL2 will replace the current term with (if hyp rhs (hide term)), or just rhs if the rule is just (equal lhs rhs). (3) A combination of both :free and :with, as described above, is legal. (4) The term :LAMBDAS is treated specially. It denotes the list of all lambda applications (i.e., let expressions) encountered during the proof. Conceptually, this use of :LAMBDAS tells ACL2 to treat lambda applications as a notation for substitutions, rather than as function calls whose opening is subject to the ACL2 rewriter's heuristics (specifically, not allowing lambda applications to open when they introduce ``too many'' if terms).

    :HANDS-OFF
    Value is a true list of function symbols or lambda expressions, indicating that under the specified goal applications of these functions are not to be rewritten. Note however that subterms will still be rewritten; see hide if that is not what is intended. (The community book books/clause-processors/autohide.lisp from Jared Davis may also be helpful in that case.) Value may also be a single function symbol or lambda expression instead of a list.

    :IN-THEORY
    Value is a ``theory expression,'' i.e., a term having at most the single free variable world which when evaluated (with world bound to the current ACL2 logical world (see world)) will produce a theory to use as the current theory for the goal specified. See theories.

    Note that an :IN-THEORY hint will always be evaluated relative to the current ACL2 logical world, not relative to the theory of a previous goal. Consider the following example.

    (defthm prop
      (p (f (g x)))
      :hints (("Goal"      :in-theory (disable f))
              ("Subgoal 3" :in-theory (enable  g))))
    
    Consider in particular the theory in effect at Subgoal 3. This call of the enable macro enables g relative to the current-theory of the current logical world, not relative to the theory produced by the hint at Goal. Thus, the disable of f on behalf of the hint at Goal will be lost at Subgoal 3, and f will be enabled at Subgoal 3 if was enabled globally when prop was submitted.

    :INDUCT
    Value is either t or a term containing at least one recursively defined function symbol; if t, this hint indicates that the system should proceed to apply its induction heuristic to the specified goal produced (without trying simplification, etc.); if value is a term other than t, then not only should the system apply induction immediately, but it should analyze value rather than the goal to generate its induction scheme. Merging and the other induction heuristics are applied. Thus, if value contains several mergeable inductions, the ``best'' will be created and chosen. E.g., the :induct hint

     (and (nth i a) (nth j a))
    
    suggests simultaneous induction on i, j, and a.

    If both an :induct and a :do-not-induct hint are supplied for a given goal then the indicated induction is applied to the goal and the :do-not-induct hint is inherited by all subgoals generated.

    :USE
    Value is a lemma-instance or a true list of lemma-instances, indicating that the propositions denoted by the instances be added as hypotheses to the specified goal. See lemma-instance. Note that :use makes the given instances available as ordinary hypotheses of the formula to be proved. The :instance form of a lemma-instance permits you to instantiate the free variables of previously proved theorems any way you wish; but it is up to you to provide the appropriate instantiations because once the instances are added as hypotheses their variables are no longer instantiable. These new hypotheses participate fully in all subsequent rewriting, etc. If the goal in question is in fact an instance of a previously proved theorem, you may wish to use :by below. Note that theories may be helpful when employing :use hints; see minimal-theory.

    Note that if the value is the name of a function symbol introduced by defun, then the ``normalized'' body of that definition is used, for which ACL2 has propagated IF tests upward. This behavior differs from that provided by a :by hint, where the original body of the definition is used.

    :BDD
    This hint indicates that ordered binary decision diagrams (BDDs) with rewriting are to be used to prove or simplify the goal. See bdd for an introduction to the ACL2 BDD algorithm.

    Value is a list of even length, such that every other element, starting with the first, is one of the keywords :vars, :bdd-constructors, :prove, or :literal. Each keyword that is supplied should be followed by a value of the appropriate form, as shown below; for others, a default is used. Although :vars must always be supplied, we expect that most users will be content with the defaults used for the other values.

    :vars -- A list of ACL2 variables, which are to be treated as Boolean variables. The prover must be able to check, using trivial reasoning (see type-set), that each of these variables is Boolean in the context of the current goal. Note that the prover will use very simple heuristics to order any variables that do not occur in :vars (so that they are ``greater than'' the variables that do occur in :vars), and these heuristics are often far from optimal. In addition, any variables not listed may fail to be assumed Boolean by the prover, which is likely to seriously impede the effectiveness of ACL2's BDD algorithm. Thus, users are encouraged not to rely on the default order, but to supply a list of variables instead. Finally, it is allowed to use a value of t for vars. This means the same as a nil value, except that the BDD algorithm is directed to fail unless it can guarantee that all variables in the input term are known to be Boolean (in a sense discussed elsewhere; see bdd-algorithm).

    :literal -- An indication of which part of the current goal should receive BDD processing. Possible values are:

      :all     treat entire goal as a single literal (the default)
      :conc    process the conclusion
      n        process the hypothesis with index n (1, 2, ...)
    

    :bdd-constructors -- When supplied, this value should be a list of function symbols in the current ACL2 world; it is (cons) by default, unless :bdd-constructors has a value in the acl2-defaults-table by default, in which case that value is the default. We expect that most users will be content with the default. See bdd-algorithm for information about how this value is used.

    :prove -- When supplied, this value should be t or nil; it is t by default. When the goal is not proved and this value is t, the entire proof will abort. Use the value nil if you are happy to the proof to go on with the simplified term.

    :CLAUSE-PROCESSOR

    Value specifies the application of a user-defined simplifier to the current goal. See clause-processor, which provides necessary background and hint syntax. Also see define-trusted-clause-processor for a discussion of ``trusted clause-processors'': goal-level simplifiers that may be external to ACL2 and do not need to be proved correct in ACL2.

    You can see all current :clause-processor rules by issuing the command (print-clause-processor-rules), and you can see the names of all trusted clause-processors by issuing the command (table trusted-clause-processor-table).

    :INSTRUCTIONS

    Value is a list of proof-checker instructions; see instructions. Unlike other hint keywords described here, this one is actually a custom keyword hint (see custom-keyword-hints) that generates a suitable :clause-processor hint.

    :CASES
    Value is a non-empty list of terms. For each term in the list, a new goal is created from the current goal by assuming that term; and also, in essence, one additional new goal is created by assuming all the terms in the list false. We say ``in essence'' because if the disjunction of the terms supplied is a tautology, then that final goal will be a tautology and hence will in fact never actually be created.

    :BY
    Value is a lemma-instance, nil, or a new event name. If the value is a lemma-instance (see lemma-instance), then it indicates that the goal (when viewed as a clause) is either equal to the proposition denoted by the instance, or is subsumed by that proposition when both are viewed as clauses. To view a formula as a clause, union together the negations of the hypotheses and add the conclusion. For example,

    (IMPLIES (AND (h1 t1) (h2 t2)) (c t1))
    
    may be viewed as the clause
    {~(h1 t1) ~(h2 t2) (c t1)}.
    
    Clause c1 is ``subsumed'' by clause c2 iff some instance of c2 is a subset of c1. For example, the clause above is subsumed by {~(h1 x) (c x)}, which when viewed as a formula is (implies (h1 x) (c x)).

    Note that if the value is the name of a function symbol introduced by defun, then the original form of the body of that definition is used. This behavior differs from that provided by a :use hint, where the so-called ``normalized'' body, for which ACL2 has propagated IF tests upward.

    If the value is nil or a new name, the prover does not even attempt to prove the goal to which this hint is attached. Instead the goal is given a ``bye'', i.e., it is skipped and the proof attempt continues as though the goal had been proved. If the prover terminates without error then it reports that the proof would have succeeded had the indicated goals been proved and it prints an appropriate defthm form to define each of the :by names. The ``name'' nil means ``make up a name.'' Here is an example (admittedly contrived for illustration purposes).

    ACL2 !>(thm (equal (append (append x y) z)
                       (append x y z))
                :hints (("Subgoal *1/2'" :by nil)))
    
    Name the formula above *1.
    
    [[... output omitted here ...]]
    
    [Note:  A hint was supplied for our processing of the goal below.
    Thanks!]
    
    Subgoal *1/2'
    (IMPLIES (AND (CONSP X)
                  (EQUAL (APPEND (APPEND (CDR X) Y) Z)
                         (APPEND (CDR X) Y Z)))
             (EQUAL (APPEND (APPEND X Y) Z)
                    (APPEND X Y Z))).
    
    But we have been asked to pretend that this goal is subsumed by the
    yet-to-be-proved |THM Subgoal *1/2'|.
    
    Subgoal *1/1
    [[... proof goes on; further output omitted here ...]]
    

    The system does not attempt to check the uniqueness of the :by names (supplied or made up), since by the time those goals are proved the namespace will be cluttered still further. Therefore, the final list of ``appropriate'' defthm forms may be impossible to admit without some renaming by the user. If you must invent new names, remember to substitute the new ones for the old ones in the :by hints themselves.

    :RESTRICT
    Warning: This is a sophisticated hint, suggested by Bishop Brock, that is intended for advanced users. In particular, :restrict hints are ignored by the preprocessor, so you might find it useful to give the hint :do-not '(preprocess) when using any :restrict hints, at least if the rules in question are abbreviations (see simple).

    Value is an association list. Its members are of the form (x subst1 subst2 ...), where: x is either (1) a rune whose car is :rewrite or :definition or (2) an event name corresponding to one or more such runes; and (subst1 subst2 ...) is a non-empty list of substitutions, i.e., of association lists pairing variables with terms. First consider the case that x is a :rewrite or :definition rune. Recall that without this hint, the rule named x is used by matching its left-hand side (call it lhs) against the term currently being considered by the rewriter, that is, by attempting to find a substitution s such that the instantiation of lhs using s is equal to that term. If however the :restrict hint contains (x subst1 subst2 ...), then this behavior will be modified by restricting s so that it must extend subst1; and if there is no such s, then s is restricted so that it must extend subst2; and so on, until the list of substitutions is exhausted. If no such s is found, then the rewrite or definition rule named x is not applied to that term. Finally, if x is an event name corresponding to one or more :rewrite or :definition runes (that is, x is the ``base symbol'' of such runes; see rune), say runes r1, ... rn, then the meaning is the same except that (x subst1 subst2 ...) is replaced by (ri subst1 subst2 ...) for each i. Once this replacement is complete, the hint may not contain two members whose car is the same rune.

    Note that the substitutions in :restrict hints refer to the variables actually appearing in the goals, not to the variables appearing in the rule being restricted.

    Here is an example, supplied by Bishop Brock. Suppose that the database includes the following rewrite rule, which is probably kept disabled. (We ignore the question of how to prove this rule.)

    cancel-<-*$free:
    (implies (and (rationalp x)
                  (rationalp y)
                  (rationalp z))
             (equal (< y z)
                    (if (< x 0)
                        (> (* x y) (* x z))
                      (if (> x 0)
                          (< (* x y) (* x z))
                        (hide (< y z))))))
    
    Then ACL2 can prove the following theorem (unless other rules get in the way), essentially by multiplying both sides by x.
    (thm
      (implies (and (rationalp x)
                    (< 1 x))
               (< (/ x) 1))
      :hints
      (("Goal"
        :in-theory (enable cancel-<-*$free)
        :restrict ((cancel-<-*$free ((x x) (y (/ x)) (z 1)))))))
    
    The :restrict hint above says that the variables x, y, and z in the rewrite rule cancel-<-*$free above should be instantiated respectively by x, (/ x), and 1. Thus (< y z) becomes (< (/ x) 1), and this inequality is replaced by the corresponding instance of the right-hand-side of cancel-<-*$free. Since the current conjecture assumes (< 1 x), that instance of the right-hand side simplifies to
    (< (* x (/ x)) (* x 1))
    
    which in turn simplifies to (< 1 x), a hypothesis in the present theorem.

    :NONLINEARP
    Value is t or nil, indicating whether non-linear-arithmetic is active. The default value is nil. See non-linear-arithmetic.

    :BACKCHAIN-LIMIT-RW
    Value is a natural number or nil, indicating the level of backchaining for rewrite, meta, and linear rules. This overrides, for the current goal and (as with :in-theory hints) descendent goals, the default backchain-limit (see set-backchain-limit).

    :REORDER
    Value is a list of positive integers without duplicates, corresponding to the numbering of subgoals generated for the goal-spec "G", say "G.k" down to "G.1". Those subgoals are reordered so that if value is (n1 n2 ... nk), then the goal now numbered "G.k" will be the goal originally numbered "G.n1"; the goal now numbered "G.k-1" will be the goal formerly numbered "G.n2"; and so on, down the list of ni, after which the goals not yet printed are printed in their original order. Note that reordering for subgoals of a goal to be proved by induction, such as *1, is not supported.

    :CASE-SPLIT-LIMITATIONS
    Value is the same as for set-case-split-limitations. The simplifier will behave as though the value had instead been supplied to set-case-split-limitations; see set-case-split-limitations. This behavior will persist through subgoals unless overridden by another :CASE-SPLIT-LIMITATIONS hint.

    :NO-OP
    Value is any object and is irrelevant. This hint does nothing. But empty hints, such as ("Goal"), are illegal and there are occasions, especially when writing custom keyword hints (see custom-keyword-hints) and computed hints (see computed-hints) where it is convenient to be able to generate a non-empty no-op hint. The standard idiom is ("Goal" :NO-OP T) but the T is completely ignored. Unlike other hint keywords, multiple occurrences of the keyword :NO-OP are tolerated.

    :NO-THANKS
    Value is any object. This hint does nothing, except that if value is non-nil then the usual ``[Note: A hint was supplied... Thanks!]'' is not printed.

    :ERROR
    Value is typically a ``fmt message'' to be printed by the fmt tilde-directive ~@ but may be any object. The effect of this hint is to cause an error when the hint is translated. There is no reason to include an :ERROR hint in any user-typein, since it will only cause an error when the form is evaluated. :ERROR hints are useful in the definition of functions that generate custom keyword hints (custom-keyword-hints) and computed hints (computed-hints). For example, if you wish to define a custom keyword hint :my-hint val and you wish the hint to signal an error if there is something inappropriate about val in the context of the hint, use the following code to generate the hint

    (list :ERROR (cons "Your specified value, ~x0, is inappropriate"
                       (list (cons #0 val))))
    
    which is equivalent to
    (list :ERROR (msg "Your specified value, ~x0, is inappropriate"
                      val))
    
    which, if val has the value 123, would evaluate to the hint
    (:ERROR ("Your specified value, ~x0, is inappropriate" (#0 . 123))).
    
    Note that any time an :ERROR keyword is produced during hint processing, including iterations of the expansions of custom keyword hints or of override-hints, an error will occur.

    :OR
    Value is a list (kwd-val-listp-1 ... kwd-val-listp-k), where each kwd-val-listp-i is a list satisfying keyword-value-listp, i.e., an alternating list of keywords and values. This hint causes an attempt to prove the specified goal using hints kwd-val-listp-i in sequence (first kwd-val-listp-1, then kwd-val-listp-2, and so on), until the first of these succeeds. If none succeeds, then the prover proceeds after heuristically choosing the ``best'' result, taking into account the goals pushed in each case for proof by induction.

    The following (contrived but illustrative example illustrates how :or hints work.

      ACL2 !>(thm (f x)
                  :hints
                  (("Goal"
                    :expand ((nth x 3))
                    :or ((:in-theory (disable car-cons))
                         (:use cdr-cons :in-theory (enable append)))
                    :do-not '(generalize))))
    
      [Note:  A hint was supplied for our processing of the goal above.
      Thanks!]
    
      The :OR hint for Goal gives rise to two disjunctive branches.  Proving
      any one of these branches would suffice to prove Goal.  We explore
      them in turn, describing their derivations as we go.
    
      ---
      Subgoal D2
      ( same formula as Goal ).
    
      The first disjunctive branch (of 2) for Goal can be created by applying
      the hint:
      ("Subgoal D2" :EXPAND ((NTH X 3))
                    :IN-THEORY (DISABLE CAR-CONS)
                    :DO-NOT '(GENERALIZE)).
    
    
      [Note:  A hint was supplied for our processing of the goal above.
      Thanks!]
    
      Normally we would attempt to prove this formula by induction.  However,
      we prefer in this instance to focus on the original input conjecture
      rather than this simplified special case.  We therefore abandon our
      previous work on this conjecture and reassign the name *1 to the original
      conjecture.  (See :DOC otf-flg.)  [Note:  Thanks again for the hint.]
    
      ---
      Subgoal D1
      ( same formula as Goal ).
    
      The second disjunctive branch (of 2) for Goal can be created by applying
      the hint:
      ("Subgoal D1" :EXPAND ((NTH X 3))
                    :USE CDR-CONS
                    :IN-THEORY (ENABLE APPEND)
                    :DO-NOT '(GENERALIZE)).
    
    
      [Note:  A hint was supplied for our processing of the goal above.
      Thanks!]
    
      ACL2 Warning [Use] in ( THM ...):  It is unusual to :USE an enabled
      :REWRITE or :DEFINITION rule, so you may want to consider disabling
      (:REWRITE CDR-CONS).
    
    
      We augment the goal with the hypothesis provided by the :USE hint.
      The hypothesis can be obtained from CDR-CONS.  We are left with the
      following subgoal.
    
      Subgoal D1'
      (IMPLIES (EQUAL (CDR (CONS X Y)) Y)
               (F X)).
    
      By the simple :rewrite rule CDR-CONS we reduce the conjecture to
    
      Subgoal D1''
      (F X).
    
    ... and so on. This example illustrates how ACL2 processes :or hints in general. For each i from 1 to k, a so-called ``disjunctive'' subgoal is created by splicing kwd-val-listp-i into the other hint values (if any) supplied for the given goal, in order. A corresponding subgoal is created for each i, numbered in the usual manner (hence, counting down) except that the ``D'' is prefixed to each resulting goal.

    :RW-CACHE-STATE
    Value is an element of the list constant *legal-rw-cache-states*: :atom (the default), nil, t, or :disabled. This hint applies to the indicated goal and all its descendents, to set the so-called ``rw-cache-state'' to the indicated value; see set-rw-cache-state.

    :BACKTRACK
    This is an advanced hint. You can probably accomplish its effect by the use of ordinary computed hints; see computed-hints. But if you are an expert, read on. (See hints-and-the-waterfall for some relevant background.)

    Value is a computed hint, which is an expression that evaluates either to nil -- indicating that the :backtrack hint is to have no effect -- or to a non-empty alternating list of :keyi :vali pairs, as expected for a hint. However, unlike ordinary computed hints, :backtrack hints are evaluated after a goal has been processed to yield zero or more subgoals, not before. Moreover, variables PROCESSOR and CLAUSE-LIST are allowed, but variable STABLE-UNDER-SIMPLIFICATIONP is not. We explain in more detail below, but first consider the following simple example. First we define a standard list reversal function:

    (defun rev (x)
      (if (consp x)
          (append (rev (cdr x)) (cons (car x) nil))
        nil))
    
    Now we prove:
    (thm (true-listp (rev x)))
    
    The successful proof includes the following output.
    Subgoal *1/1'
    (IMPLIES
      (AND (CONSP X)
           (TRUE-LISTP (REV (CDR X))))
      (TRUE-LISTP (APPEND (REV (CDR X)) (LIST (CAR X))))).
    
    The destructor terms (CAR X) and (CDR X) can be
    eliminated by using CAR-CDR-ELIM to replace X
    by (CONS X1 X2), (CAR X) by X1 and (CDR X) by
    X2.  This produces the following goal.
    
    Subgoal *1/1''
    (IMPLIES (AND (CONSP (CONS X1 X2))
                  (TRUE-LISTP (REV X2)))
             (TRUE-LISTP (APPEND (REV X2) (LIST X1)))).
    
    But suppose that we attach a :backtrack hint to the goal above at which destructor elimination was applied:
    (thm (true-listp (rev x))
         :hints (("Subgoal *1/1'"
                  :backtrack
                  (quote (:do-not '(eliminate-destructors))))))
    
    Then when ACL2 applies destructor elimination as displayed above, this time the :backtrack hint applies, evaluating to (:do-not '(eliminate-destructors)). Since this list is not nil, the prover decides not to keep the new subgoal, and instead supplies this :do-not hint before attacking the goal again. In this example, ACL2 happens to use a technique later in its ``waterfall'' arsenal than destructor elimination, namely, generalization:
    Subgoal *1/1'
    (IMPLIES
      (AND (CONSP X)
           (TRUE-LISTP (REV (CDR X))))
      (TRUE-LISTP (APPEND (REV (CDR X)) (LIST (CAR X))))).
    
    [Note:  A hint was supplied for our processing
    of the goal above, because of a :backtrack hint
    that is preventing destructor elimination.
    Thanks!]
    
    We generalize this conjecture, replacing
    (REV (CDR X)) by RV.  This produces
    
    Subgoal *1/1''
    (IMPLIES (AND (CONSP X) (TRUE-LISTP RV))
             (TRUE-LISTP (APPEND RV (LIST (CAR X))))).
    

    We now provide a careful explanation of how :backtrack hints work, but we suggest that you keep the example above in mind. If ``:backtrack form'' is part of the hint that has been selected for a goal, then form is evaluated when one of ACL2's clause processors successfully applies to the current goal to produce a list of subgoals. This evaluation takes place in an environment just like that for any computed hint (see computed-hints), with the following exceptions. First, the variable STABLE-UNDER-SIMPLIFICATIONP is not allowed to occur free in form, but instead the following new variables are allowed to occur free and are bound for this evaluation as follows: PROCESSOR is bound to the processor in the list *preprocess-clause-ledge* that has applied to the goal, and CLAUSE-LIST is bound to the list of clauses (each a list of literals that is implicitly disjoined) returned by that clause processor. Second, the variables HIST and PSPV are bound to the history and pspv returned by the clause processor, not the ones that were passed to the clause processor. If this evaluation returns an error, then the proof aborts, as for any computed hint whose evaluation returns an error. If this evaluation returns nil, then the :backtrack hint has no effect, and the goal is replaced by the list of goals (the value of CLAUSE-LIST described above), as usual. Otherwise, the clause processor is deemed to have failed, and the goal clause is tried again starting at the top of the waterfall after selecting the hint returned by the above evaluation. That hint will normally be an alternating list of hint keywords and their values, but if it is a custom keyword hint (see custom-keyword-hints), then it will be handled in the usual manner but with the first three variables above bound to the symbol :OMITTED. Of course, if the new hint includes a value for :BACKTRACK then this process can loop; care should be taken to keep that from happening.

    A final note about :BACKTRACK hints: since these are a form of computed hints, override-hints (if any) are applied to their evaluation result just as with any computed hint. That is, the backtrack hint is successively modified with each override-hint, to produce a final hint that is actually used (or, ignored if that final hint is nil). See override-hints.




    acl2-sources/doc/HTML/HISTORY.html0000664002132200015000000001252112222333517016114 0ustar kaufmannacl2 HISTORY.html -- ACL2 Version 6.3

    HISTORY

    functions that display or change history
    Major Section:  ACL2 Documentation
    

    Some Related Topics

    ACL2 keeps track of the commands that you have executed that have extended the logic or the rule database, as by the definition of macros, functions, etc. Using the facilities in this section you can review the sequence of commands executed so far. For example, you can ask to see the most recently executed command, or the command 10 before that, or the command that introduced a given function symbol. You can also undo back through some previous command, restoring the logical world to what it was before the given command.

    The annotations printed in the margin in response to some of these commands (such as `P', `L', and `V') are explained in the documentation for :pc.

    Several technical terms are used in the documentation of the history commands. You must understand these terms to use the commands. These terms are documented via :doc entries of their own. See command, see events, see command-descriptor, and see logical-name.




    acl2-sources/doc/HTML/HONS-ACONS.html0000664002132200015000000000676612222333520016373 0ustar kaufmannacl2 HONS-ACONS.html -- ACL2 Version 6.3

    HONS-ACONS

    (hons-acons key val alist) is the main way to create or extend fast-alists.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, hons-acons is like acons except that its guard does not require alistp; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, two things are done differently. First, the key is copied with hons-copy; this lets us use EQL-based hash tables instead of EQUAL-based hash tables for better performance. Second, assuming there is a valid hash table associated with this alist, we destructively update the hash table by binding key to val. The hash table will no longer be associated with alist, but will instead be associated with the new alist returned by hons-acons.

    Hash Table Creation

    A new hash table is created by hons-acons whenever alist is an atom. Unlike ordinary acons, we do not require that alist be nil, and in fact you may wish to use a non-nil value for one of two reasons.

    1. As a size hint

    By default, the new hash table will be given a quite modest default capacity of 60 elements. As more elements are added, the table may need to be resized to accommodate them, and this resizing has some runtime cost.

    When a natural number is used as a fast alist's name, we interpret it as a size hint. For example, (hons-acons 'foo 'bar 1000) instructs us to use 1000 as the initial size for this hash table and binds 'foo to 'bar. The resulting table should not need to be resized until more than 1000 elements are added. We ignore size hints that request fewer than 60 elements.

    Because of hash collisions, hash tables typically need to have a larger size than the actual number of elements they contain. The hash tables for fast alists are told to grow when they reach 70% full. So, an easy rule of thumb might be: multiply the expected number of elements you need by 1.5 to keep your hash tables about 2/3 full.

    2. As an alist name

    We also frequently use a symbol for alist, and think of this symbol as the name of the new alist. We have found that naming alists can be valuable for two reasons:

    First, the name can be helpful in identifying fast alists that should have been freed, see fast-alist-summary.

    Second, names can sometimes be used to avoid a subtle and insidious table-stealing phenomenon that occurs when using fast-alists that are themselves normed; see hons-acons!.

    At the moment, there is no way to simultaneously name a fast alist and also give it a size hint. We may eventually allow strings to include embedded name and size components, but for now we have not implemented this capability.




    acl2-sources/doc/HTML/HONS-ACONS_bang_.html0000664002132200015000000000671312222333520017511 0ustar kaufmannacl2 HONS-ACONS_bang_.html -- ACL2 Version 6.3

    HONS-ACONS!

    (hons-acons! key val alist) is an alternative to hons-acons that produces normed, fast alists.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, hons-acons! is like acons except that its guard does not require alistp; we leave it enabled and would think it odd to ever prove a theorem about it.

    Ordinarily, fast-alists are constructed with hons-acons instead of hons-acons!. In such alists, the keys are honsed, but the conses that make up the "spine" of the alist itself are ordinary conses. In other words, it is basically correct to say:

     (hons-acons key val alist) == (cons (cons (hons-copy key) val) alist)
    

    In contrast, when hons-acons! is used, the conses making up the alist itself are also normed. That is,

     (hons-acons! key val alist) == (hons (hons key val) alist)
    

    Generally, you should not use hons-acons! unless you really know what you're doing.

    Drawback 1. hons-acons! requires you to hons-copy all of the values that are being stored in the fast alist. If you are storing large values, this may be expensive.

    Drawback 2. It can be more difficult to maintain the proper discipline when using hons-acons!. For instance, consider the following:

      (let ((al1 (hons-acons 1 'one (hons-acons 2 'two nil)))
            (al2 (hons-acons 1 'one (hons-acons 2 'two nil))))
         ...)
    

    Here, both al1 and al2 are valid fast alists and they can be extended independently without any trouble. But if these alists had instead been constructed with hons-acons!, then since both al1 and al2 are equal, normed conses, they will literally be eq and hence will refer to precisely the same hash table. In other words, hons-acons! makes it relatively easy to inadvertently steal the hash table associated with some other fast alist. This problem can be alleviated somewhat by uniquely naming alists; see the discussion in hons-acons for details.

    Despite these drawbacks, hons-acons! is the only way besides hons-shrink-alist! to generate a fast alist that is normed. It is not adequate to hons-copy a fast alist that was generated by ordinary hons-acons calls, because this would produce an EQUAL-but-not-EQ object, and this new object would not be associated with the fast alist's hash table.




    acl2-sources/doc/HTML/HONS-AND-MEMOIZATION.html0000664002132200015000000006050612222333517017721 0ustar kaufmannacl2 HONS-AND-MEMOIZATION.html -- ACL2 Version 6.3

    HONS-AND-MEMOIZATION

    hash cons, function memoization, and applicative hash tables
    Major Section:  ACL2 Documentation
    

    Bob Boyer and Warren Hunt, and later Jared Davis and Sol Swords, have developed a canonical representation for ACL2 data objects and a function memoization mechanism to facilitate reuse of previously computed results. This facility includes procedures to read and print ACL2 expressions in such a way that repetition of some ACL2 objects is eliminated, thereby permitting a kind of on-the-fly file compression. The implementation does not alter the semantics of ACL2 except to add a handful of definitions.

    We give the name ``ACL2(h)'' to the resulting experimental extension of the ACL2 system, which includes hash cons, function memoization, and fast association lists (applicative hash tables). It is optimized for Clozure Common Lisp (CCL), but other ANSI-compliant host Lisp implementations may also work, provided there are sufficient machine resources.

    If you want to use ACL2(h), you might find it helpful to consult the document centaur/README.html in the ACL2 community books.

    An easy way is to build ACL2(h) is to include the following with a `make' command:

    ACL2_HONS=h
    
    So for example, to make an executable image and also documentation (which will appear in subdirectories doc/EMACS and doc/HTML):
    make large DOC ACL2_HONS=h
    

    Some Related Topics

    • CLEAR-HASH-TABLES -- deprecated feature

    • CLEAR-MEMOIZE-STATISTICS -- clears all profiling info displayed by (memoize-summary)

    • CLEAR-MEMOIZE-TABLE -- forget values remembered for the given function

    • CLEAR-MEMOIZE-TABLES -- forget values remembered for all the memoized functions

    • CONS-SUBTREES -- (cons-subtrees x nil) builds a fast alist that associates each subtree
      of X with T, without duplication.

    • FAST-ALIST-FREE -- (fast-alist-free alist) throws away the hash table associated with a fast
      alist.

    • FAST-ALIST-FREE-ON-EXIT -- Free a fast alist after the completion of some form.

    • FAST-ALIST-LEN -- (fast-alist-len alist) counts the number of unique keys in a fast alist.

    • FAST-ALIST-SUMMARY -- (fast-alist-summary) prints some basic statistics about any current fast
      alists.

    • FAST-ALISTS -- alists with hidden hash tables for faster execution

    • FLUSH-HONS-GET-HASH-TABLE-LINK -- deprecated feature

    • HONS -- (hons x y) returns a normed object equal to (cons x y).

    • HONS-ACONS -- (hons-acons key val alist) is the main way to create or extend
      fast-alists.

    • HONS-ACONS! -- (hons-acons! key val alist) is an alternative to hons-acons that
      produces normed, fast alists.

    • HONS-ASSOC-EQUAL -- (hons-assoc-equal key alist) is not fast; it serves as the logical
      definition for hons-get.

    • HONS-CLEAR -- (hons-clear gc) is a drastic garbage collection mechanism that clears out
      the underlying Hons Space.

    • HONS-COPY -- (hons-copy x) returns a normed object that is equal to X.

    • HONS-COPY-PERSISTENT -- (hons-copy-persistent x) returns a normed object that is equal to X
      and which will be re-normed after any calls to hons-clear.

    • HONS-EQUAL -- (hons-equal x y) is a recursive equality check that optimizes when parts of
      its arguments are normed.

    • HONS-EQUAL-LITE -- (hons-equal-lite x y) is a non-recursive equality check that optimizes if
      its arguments are normed.

    • HONS-GET -- (hons-get key alist) is the efficient lookup operation for
      fast-alists.

    • HONS-NOTE -- notes about HONS, especially pertaining to expensive resizing operations

    • HONS-RESIZE -- (hons-resize ...) can be used to manually adjust the sizes of the hash
      tables that govern which ACL2 Objects are considered normed.

    • HONS-SHRINK-ALIST -- (hons-shrink-alist alist ans) can be used to eliminate "shadowed pairs"
      from an alist or to copy fast-alists.

    • HONS-SHRINK-ALIST! -- (hons-shrink-alist! alist ans) is an alternative to hons-shrink-alist
      that produces a normed result.

    • HONS-SUMMARY -- (hons-summary) prints basic information about the sizes of the tables in
      the current Hons Space.

    • HONS-WASH -- (hons-wash) is like gc$ but can also garbage collect normed
      objects (CCL Only).

    • MAKE-FAST-ALIST -- (make-fast-alist alist) creates a fast-alist from the input alist,
      returning alist itself or, in some cases, a new object equal to it.

    • MEMOIZE -- turn on memoization for a specified function

    • MEMOIZE-SUMMARY -- display all collected profiling and memoization table info

    • MEMSUM -- display all collected profiling and memoization info

    • NEVER-MEMOIZE -- Mark a function as unsafe to memoize.

    • NORMED -- Normed objects are ACL2 Objects that are "canonical" or "unique" in a
      certain sense.

    • NUMBER-SUBTREES -- (number-subtrees x) returns the number of distinct subtrees of X, in the
      sense of equal

    • RESTORE-MEMOIZATION-SETTINGS -- restore the saved memoization settings

    • SAVE-AND-CLEAR-MEMOIZATION-SETTINGS -- save and remove the current memoization settings

    • SLOW-ALIST-WARNING -- warnings issued when fast-alists are used inefficiently

    • UNMEMOIZE -- turn off memoization for the specified function

    • WITH-FAST-ALIST -- (with-fast-alist name form) causes name to be a fast alist for the
      execution of form.

    • WITH-STOLEN-ALIST -- (with-stolen-alist name form) ensures that name is a fast alist at the
      start of the execution of form. At the end of execution, it ensures that
      name is a fast alist if and only if it was originally. That is, if
      name was not a fast alist originally, its hash table link is freed, and if
      it was a fast alist originally but its table was modified during the execution
      of form, that table is restored. Note that any extended table created from
      the original fast alist during form must be manually freed.

    Much of the documentation for the remainder of this topic is taken from the paper ``Function Memoization and Unique Object Representation for ACL2 Functions'' by Robert S. Boyer and Warren A. Hunt, Jr., which has appeared in the Sixth International Workshop on the ACL2 Theorem Prover and Its Applications, ACM Digital Library, 2006.

    In the implementation of the ACL2 logic, ACL2 data objects are represented by Common Lisp objects of the same type, and the ACL2 pairing operation is internally implemented by the Common Lisp cons function. In Common Lisp, cons is guaranteed to provide a new pair, distinct from any previously created pair. We have defined a new ACL2 function HONS that is logically identical to the ACL2 cons function, but whose implementation usually reuses an existing pair if its components are identical to the components of an existing pair. A record of ACL2 HONS objects is kept, and when an ACL2 function calls hons ACL2 searches for an existing identical pair before allocating a new pair; this operation been called ``hash consing''.

    It appears that hash consing was first conceived by A. P. Ershov in 1957, to speed up the recognition of common subexpressions. Ershov showed how to collapse trees to minimal DAGs by traversing trees bottom up, and he used hashing to eliminate the re-evaluation of common subexpressions. Later, Eiichi Goto implemented a Lisp system with a built-in hash consing operation: his h-CONS cells were rewrite protected and free of duplicate copies, and Goto used this hash consing operation to facilitate the implementation of a symbolic algebra system he developed.

    Memoizing functions also has a long history. In 1967, Donald Michie proposed using memoized functions to improve the performance of machine learning. Rote learning was improved by a learning function not forgetting what it had previously learned; this information was stored as memoized function values.

    The use of hash consing has appeared many times. For instance, Henry Baker used hash consing to improve the performance of the well-known Boyer rewriting benchmark. Baker used both hash consing and function memoization to improve the speed of the Takeuchi function, exactly in the spirit of our implementation, but without the automated, system-wide integration we report here.

    The ACL2 implementation permits memoization of user-defined functions. During execution a user may enable or disable function memoization on an individual function basis, may clear memoization tables, and may even keep a stack of memoization tables. This facility takes advantage of our implementation where we keep one copy of each distinct ACL2 data object. Due to the functional nature of ACL2, it is sufficient to have at most one copy of any data structure; thus, a user may arrange to keep data canonicalized. This implementation extends to the entire ACL2 system the benefits enjoyed by BDDs: canonicalization, memoization, and fast equality check.

    We have defined various algorithms using these features, and we have observed, in some cases, substantial performance increases. For instance, we have implemented unordered set intersection and union operations that operate in time roughly linear in the size of the sets. Without using arrays, we defined a canonical representation for Boolean functions using ACL2 objects. We have investigated the performance of rewriting and tree consensus algorithms to good effect, and we believe function memoization offers interesting opportunities to simplify algorithm definition while simultaneously providing performance improvements.

    We recommend that users focus at first on the logical definitions of hons and other primitives rather than their underlying Common Lisp implementations. Integrated with this memoization system is a performance monitoring system, which can provide real-time feedback on the operation and usefulness of hons and function memoization. For a more detailed description of these tools, please see the ACL2 2006 workshop paper mentioned above.

    The Fibonacci function is a small example that demonstrates the utility of function memoization. The following definition exhibits a runtime that is exponential in its input argument.

    (defun fib (x)
      (declare (xargs :guard (natp x)))
      (mbe
       :logic
       (cond ((zp x) 0)
             ((= x 1) 1)
             (t (+ (fib (- x 1)) (fib (- x 2)))))
       :exec
       (if (< x 2)
           x
         (+ (fib (- x 1)) (fib (- x 2))))))
    

    Below we show how the ACL2 time$ utility can measure the time to execute a call to the fib function (with some editing to avoid noise from the underlying Common Lisp implementation). Memoize is actually an ACL2 macro that expands to a call of the ACL2 function used to identify a function for memoization; see memoize. This function also accepts a well-formed term that must be true in order for the system to memoize a call of the memoized function; to ensure that an instance of the term is safe for evaluation in Common Lisp, a check is performed that if the guard of the memoized function is satisfied, then this instance will execute without error.

    ACL2 !>(time$ (fib 40))
    ; (EV-REC *RETURN-LAST-ARG3* ...) took
    ; 0.99 seconds realtime, 0.98 seconds runtime
    ; (1,296 bytes allocated).
    102334155
    ACL2 !>(memoize 'fib)
    
    Summary
    Form:  ( TABLE MEMOIZE-TABLE ...)
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
    
    Summary
    Form:  ( PROGN (TABLE MEMOIZE-TABLE ...) ...)
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     FIB
    ACL2 !>(time$ (fib 40))
    ; (EV-REC *RETURN-LAST-ARG3* ...) took
    ; 0.00 seconds realtime, 0.00 seconds runtime
    ; (2,864 bytes allocated).
    102334155
    ACL2 !>(time$ (fib 100))
    ; (EV-REC *RETURN-LAST-ARG3* ...) took
    ; 0.00 seconds realtime, 0.00 seconds runtime
    ; (7,024 bytes allocated).
    354224848179261915075
    ACL2 !>(unmemoize 'fib)
    

    We see that once the function fib is identified as a function for which function calls should be memoized, the execution times are substantially reduced. Finally, we can prevent fib from being further memoized; in fact, unmemoize erases the previously memoized values.

    The implementation of hash consing, memoization, and applicative hash tables involves several facets: canonical representation of ACL2 data, function memoization, and the use of Lisp hash tables to improve the performance of association list operations. We discuss each of these in turn, and we mention some subtle interrelationships. Although it is not necessary to understand the discussion in this section, it may permit some users to better use this implementation. This discussion may be confusing for some ACL2 users as it makes references to Lisp implementation features.

    The ACL2 system is actually implemented as a Lisp program that is layered on top of a Common Lisp system implementation. ACL2 data constants are implemented with their corresponding counterparts in Common Lisp; that is, ACL2 cons pairs, strings, characters, numbers, and symbols are implemented with their specific Common Lisp counterparts. This choice permits a number of ACL2 primitive functions to be implemented with their corresponding Common Lisp functions, but there are indeed differences. ACL2 is a logic, and as such, it does not specify anything to do with physical storage or execution performance. When ACL2 is used, the knowledgeable user can write functions to facilitate the reuse of some previously computed values.

    Recall the three mechanisms under discussion: hash consing, function memoization, and fast association list operations. The function memoization mechanism takes advantage of the canonical representation of data objects provided by the hons operation as does the fast association list operation mechanism. Each time hons is invoked, its arguments are themselves converted, if necessary, to uniquely represented objects.

    The ACL2 universe is recursively closed under the cons pairing operation and the atoms. Hash consing (hons) is logically identical to cons, but a set of tables is used to record each hons pair. When a hons pair is requested, the implementation checks, in the current set of tables, whether the requested pair already exists. If not, a new pair is created and a record of that pair is made; otherwise, a reference to the previously created pair is returned. Thus, any data object created with hons has a unique representation, as does every subcomponent. We also arrange for strings to have a unique representation -- only one copy of each different string is kept -- and when any previously unseen string is an argument to hons, we add the string to a unique table of strings. A system-wide benefit of having a canonical representation for data is that equality comparisons between any two data objects can be done in constant time.

    The definition of hons in no way changes the operation of cons -- hons creates a cons pair. When asked to create a hons, the implementation checks to see if there is a cons with the same car and cdr as the hons being requested. Thus, the only difference between the results of a hons call and a corresponding cons call is a notation in some invisible (to the ACL2 logic) tables where we record which cons elements are also hons elements. Since a hons is nothing but a cons, the operation of car and cdr is unchanged. In fact, the implementation is designed so that at any time it is safe to clear the table identifying which cons elements are also considered hons elements.

    User-defined functions with defined and verified guards can be memoized. When a function is memoized, a user-supplied condition restricts the domain when memoization is attempted. When the condition is satisfied, memoization is attempted (assuming that memoization for the function is presently enabled); otherwise, the function is just evaluated. Each memoized function has a hash table that is used to keep track of a unique list of function arguments paired with their values. If appropriate, for each function the corresponding table is checked to see if a previous call with exactly the same arguments already exists in the table: if so, then the associated value is returned; if not, then the function is evaluated and a new key-value pair is added to the table of memoized values so that some future call will benefit from the memoization. With ACL2 user functions memoization can be dynamically enabled and disabled. There is an ACL2 function that clears a specific memoization table. And finally, just as with the hons table, a stack of these function memoization tables is maintained; that is, it is possible to memoize a function, use it a bit, set the memoized values aside, start a new table, use it, and then return to the original table.

    We next discuss the fast lookup operation for association lists. When a pair is added to an association list using the functions hons-acons or hons-acons!, the system also records the key-value pair in an associated hash table. As long as a user only used these two functions when placing key-value pairs on an association list, the key-value pairs in the corresponding hash table will be synchronized with the key-value pairs in the association list. Later, if hons-get is used to look up a key, then instead of performing a linear search of the association list we consult the associated hash table. If a user does not strictly follow this discipline, then a linear search may be required. In some sense, these association lists are much like ACL2 arrays, but without the burden of explicitly naming the arrays. The ACL2 array compress1 function is analogous to the functions hons-shrink-alist and hons-shrink-alist!. There are user-level ACL2 functions that allow the associated hash tables to be cleared and removed.

    Finally, we would advise anyone who is using CCL in a research environment to stay plugged into the ``trunk'' or ``bleeding edge'' of CCL development. This is very easy to do by typing a few commands to a shell, for example standing above the target directory as follows, provided one has svn working.

    
     For linux:
    
       rm -rf ccl
       svn co http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx8664/ccl
    
     For an x86 Macintosh running the Darwin OS:
    
       svn co http://svn.clozure.com/publicsvn/openmcl/trunk/darwinx8664/ccl
    
     To keep up to date, you may find it sufficient to do:
    
       cd ccl
       svn update
    
     Whether obtaining a fresh CCL or just updating, finally issue these
     commands.
    
       ./lx86cl64
       (rebuild-ccl :full t)
       (quit)
       ./lx86cl64
       (rebuild-ccl :full t)
       (quit)
    
    

    REFERENCES

    Baker, Henry G., The Boyer Benchmark at Warp Speed. ACM Lisp Pointers V,3 (Jul-Sep 1992), pages 13-14.

    Baker, Henry G., A Tachy 'TAK'. ACM Lisp Pointers Volume 3, July-September, 1992, pages 22-23.

    Robert S. Boyer and Warren A. Hunt, Jr., Function Memoization and Unique Object Representation for ACL2 Functions, in the Sixth International Workshop on the ACL2 Theorem Prover and Its Applications, ACM Digital Library, 2006.

    A. P. Ershov. On Programming of Arithmetic Operations. In the Communications of the ACM, Volume 118, Number 3, August, 1958, pages 427-430.

    Eiichi Goto, Monocopy and Associative Algorithms in Extended Lisp, TR-74-03, University of Toyko, 1974.

    Richard P. Gabriel. Performance and Evaluation of Lisp Systems. MIT Press, 1985.

    Donald Michie. Memo functions: a Language Feature with Rote Learning Properties. Technical Report MIP-R-29, Department of Artificial Intelligence, University of Edinburgh, Scotland, 1967.

    Donald Michie. Memo Functions and Machine Learning. In Nature, Volumne 218, 1968, pages 19-22.




    acl2-sources/doc/HTML/HONS-ASSOC-EQUAL.html0000664002132200015000000000250112222333520017224 0ustar kaufmannacl2 HONS-ASSOC-EQUAL.html -- ACL2 Version 6.3

    HONS-ASSOC-EQUAL

    (hons-assoc-equal key alist) is not fast; it serves as the logical definition for hons-get.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    The definition of hons-assoc-equal is similar to that of assoc-equal, except that (1) it does not require alistp as a guard, and (2) it "skips over" any non-conses when its alist argument is malformed.

    We typically leave hons-get enabled and reason about hons-assoc-equal instead. One benefit of this approach is that it avoids certain "false" discipline warnings that might arise from execution during theorem proving.




    acl2-sources/doc/HTML/HONS-CLEAR.html0000664002132200015000000000357312222333520016347 0ustar kaufmannacl2 HONS-CLEAR.html -- ACL2 Version 6.3

    HONS-CLEAR

    (hons-clear gc) is a drastic garbage collection mechanism that clears out the underlying Hons Space.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, hons-clear just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, hons-clear brutally (1) clears all the tables that govern which conses are normed, then (2) optionally garbage collects, per the gc argument, then finally (3) re-norms the keys of fast-alists and persistently normed conses; see hons-copy-persistent.

    Note. The hash tables making up the Hons Space retain their sizes after being cleared. If you want to shrink them, see hons-resize.

    Note. CCL users might prefer hons-wash, which is relatively efficient and allows for the garbage collection of normed conses without impacting their normed status.

    Note. It is not recommended to interrupt this function. Doing so may cause persistently normed conses and fast alist keys to become un-normed, which might lead to less efficient re-norming and/or violations of the fast-alist discipline.




    acl2-sources/doc/HTML/HONS-COPY-PERSISTENT.html0000664002132200015000000000262012222333520017761 0ustar kaufmannacl2 HONS-COPY-PERSISTENT.html -- ACL2 Version 6.3

    HONS-COPY-PERSISTENT

    (hons-copy-persistent x) returns a normed object that is equal to X and which will be re-normed after any calls to hons-clear.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically hons-copy-persistent is the identity; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, hons-copy-persistent is virtually identical to hons-copy.

    The only difference is that when hons-clear is used, any persistently normed conses are automatically re-normed, and this re-norming can be carried out more efficiently than, say, an ordinary hons-copy.




    acl2-sources/doc/HTML/HONS-COPY.html0000664002132200015000000000373212222333520016270 0ustar kaufmannacl2 HONS-COPY.html -- ACL2 Version 6.3

    HONS-COPY

    (hons-copy x) returns a normed object that is equal to X.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    In the logic, hons-copy is just the identity function; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, hons-copy does whatever is necessary to produce a normed version of X.

    What might this involve?

    If X is a symbol, character, or number, then it is already normed and nothing is done.

    If X is a string, we check if any normed version of X already exists. If so, we return the already-normed version; otherwise, we install X as the normed version for all strings that are equal to X.

    If X is a cons, we must determine if there is a normed version of X, or recursively construct and install one. Norming large cons trees can become expensive, but there are a couple of cheap cases. In particular, if X is already normed, or if large subtrees of X are already normed, then not much needs to be done. The slowest case is norming some large ACL2 cons structure that has no subtrees which are already normed.

    Note that running hons-copy on an object that was created with cons is often slower than just using hons directly when constructing the object.




    acl2-sources/doc/HTML/HONS-EQUAL-LITE.html0000664002132200015000000000314012222333520017111 0ustar kaufmannacl2 HONS-EQUAL-LITE.html -- ACL2 Version 6.3

    HONS-EQUAL-LITE

    (hons-equal-lite x y) is a non-recursive equality check that optimizes if its arguments are normed.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    In the logic, hons-equal-lite is just equal; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, hons-equal-lite checks whether its arguments are normed, and if so it effectively becomes a eql check. Otherwise, it immediately calls equal to determine if its arguments are equal.

    The idea here is to strike a useful balance between equal and hons-equal. If both arguments happen to be normed, we get to use a very fast equality comparison. Otherwise, we just get out of the way and let equal do its work, without the extra overhead of recursively checking whether the subtrees of x and y are normed.




    acl2-sources/doc/HTML/HONS-EQUAL.html0000664002132200015000000000315512222333520016364 0ustar kaufmannacl2 HONS-EQUAL.html -- ACL2 Version 6.3

    HONS-EQUAL

    (hons-equal x y) is a recursive equality check that optimizes when parts of its arguments are normed.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    In the logic, hons-equal is just equal; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, when hons-equal encounters two arguments that are both normed, it becomes a mere eql check, and hence avoids the overhead of recursively checking large cons structures for equality.

    Note. If hons-equal is given arguments that do not contain many normed objects, it can actually be much slower than equal! This is because it checks to see whether its arguments are normed at each recursive step, and so you are repeatedly paying the price of such checks. Also see hons-equal-lite, which only checks at the top level whether its arguments are normed.




    acl2-sources/doc/HTML/HONS-GET.html0000664002132200015000000000255212222333520016134 0ustar kaufmannacl2 HONS-GET.html -- ACL2 Version 6.3

    HONS-GET

    (hons-get key alist) is the efficient lookup operation for fast-alists.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, hons-get is just an alias for hons-assoc-equal; we typically leave it enabled and prefer to reason about hons-assoc-equal instead. One benefit of this approach is that it helps to avoids "false" discipline warnings that might arise from execution during theorem proving.

    Under the hood, when alist is a fast alist that is associated with a valid hash table, hons-get first norms key using hons-copy, then becomes a gethash operation on the hidden hash table.




    acl2-sources/doc/HTML/HONS-NOTE.html0000664002132200015000000000745412222333520016270 0ustar kaufmannacl2 HONS-NOTE.html -- ACL2 Version 6.3

    HONS-NOTE

    notes about HONS, especially pertaining to expensive resizing operations
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Certain ``Hons-Notes'' are printed to the terminal to report implementation-level information about the management of HONS structures. Some of these may be low-level and of interest mainly to system developers. But below we discuss what users can learn from a particular hons-note, ``ADDR-LIMIT reached''. In short: If you are seeing a lot of such hons-notes, then you may be using a lot of memory. (Maybe you can reduce that memory; for certain BDD operations, for example (as defined in community books books/centaur/ubdds/), variable ordering can make a big difference.)

    By way of background:

    The ADDR-HT is the main hash table that lets us look up normed conses, i.e., honses. It is an ordinary Common Lisp hash table, so when it starts to get too full the Lisp will grow it. It can get really big. (It has been seen to take more than 10 GB of memory just for the hash table's array, not to mention the actual conses!)

    Hons-Notes may be printed when the ADDR-HT is getting really full. This growth can be expensive and can lead to memory problems. Think about what it takes to resize a hash table:

    (1) allocate a new, bigger array
    (2) rehash elements, copying them into the new array
    (3) free the old array

    Until you reach step (3) and a garbage collection takes place, you have to have enough memory to have both the old and new arrays allocated. If the old array was 10 GB and we want to allocate a new one that's 15 GB, this can get pretty bad. Also, rehashing the elements is expensive time-wise when there are lots of elements.

    Since resizing a hash table is expensive, we want to avoid it. There's a hons-resize command for this. You may want your books to start with something like the following.

      (value-triple (set-max-mem (* 30 (expt 2 30))))      ; 30 GB heap
      (value-triple (hons-resize :addr-ht #u_100_000_000)) ; 100M honses
    

    Basically, if you roughly know how many honses your book will need, you can just get them up front and then never to resize.

    The Hons-Notes about ``ADDR-LIMIT reached'' are basically there to warn you that the ADDR-HT is being resized. This can help you realize that your hons-resize command had too small of an ADDR-HT size, or might suggest that your book should have a hons-resize command. There are also commands like (hons-summary) and, defined in community book books/centaur/misc/memory-mgmt-logic.lisp, (hons-analyze-memory nil). These can show you how many honses you currently have, how much space they are taking, and that sort of thing. (A nice trick is to call hons-summary at the end of a book, to get an idea of how many honses you should ask for in your hons-resize command).




    acl2-sources/doc/HTML/HONS-RESIZE.html0000664002132200015000000000657512222333520016527 0ustar kaufmannacl2 HONS-RESIZE.html -- ACL2 Version 6.3

    HONS-RESIZE

    (hons-resize ...) can be used to manually adjust the sizes of the hash tables that govern which ACL2 Objects are considered normed.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    General form:

     (hons-resize [:str-ht size]
                  [:nil-ht size]
                  [:cdr-ht size]
                  [:cdr-ht-eql size]
                  [:addr-ht size]
                  [:other-ht size]
                  [:sbits size]
                  [:fal-ht size]
                  [:persist-ht size])
    

    Example:

     (hons-resize :str-ht 100000
                  :addr-ht (expt 2 27))
    

    Logically, hons-resize just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, hons-resize can be used to change the sizes of certain hash tables in the Hons Space.

    All arguments are optional. The size given to each argument should either be nil (the default) or a natural number. A natural number indicates the newly desired size for the hash table, whereas nil means "don't resize this table." Any non-natural argument is regarded as nil.

    You may wish to call this function for two reasons.

    1. Improving Performance by Resizing Up

    Since the hash tables in the Hons Space automatically grow as new objects are normed, hons-resize is unnecessary. But automatic growth can be slow because it is incremental: a particular hash table might be grown dozens of times over the course of your application. Using hons-resize to pick a more appropriate initial size may help to reduce this overhead.

    2. Reducing Memory Usage by Resizing Down

    Resizing can also be used to shrink the hash tables. This might possibly be useful immediately after a hons-clear to free up memory taken by the hash tables themselves. (Of course, the tables will grow again as you create new normed objects.)

    Advice for using hons-resize.

    Note that hash tables that are already close to the desired size, or which have too many elements to fit into the desired size, will not actually be resized. This makes resizing relatively "safe."

    Note that the hons-summary function can be used to see how large and how full your hash tables are. This may be useful in deciding what sizes you want to give to hons-resize.

    Note that hons-resize operates by (1) allocating new hash tables, then (2) copying everything from the old hash table into the new table. Because of this, for best performance you should ideally call it when the hash tables involved are minimally populated, i.e., at the start of your application, or soon after a hons-clear.




    acl2-sources/doc/HTML/HONS-SHRINK-ALIST.html0000664002132200015000000000643112222333520017365 0ustar kaufmannacl2 HONS-SHRINK-ALIST.html -- ACL2 Version 6.3

    HONS-SHRINK-ALIST

    (hons-shrink-alist alist ans) can be used to eliminate "shadowed pairs" from an alist or to copy fast-alists.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization. It assumes familiarity with fast alists; see fast-alists.

    Logically, (hons-shrink-alist alist ans) is defined as follows:

     (cond ((atom alist)
            ans)
           ((atom (car alist))
            (hons-shrink-alist (cdr alist) ans))
           ((hons-assoc-equal (car (car alist)) ans)
            (hons-shrink-alist (cdr alist) ans))
           (t
            (hons-shrink-alist (cdr alist) (cons (car alist) ans))))
    

    The alist argument need not be a fast alist.

    Typically ans is set to nil or some other atom. In this case, shrinking produces a new, fast alist which is like alist except that (1) any "malformed," atomic entries have been removed, (2) all "shadowed pairs" have been removed, and (3) incidentally, the surviving elements have been reversed. This can be useful as a way to clean up any unnecessary bindings in alist, or as a way to obtain a "deep copy" of a fast alist that can extended independently from the original while maintaining discipline.

    Note that hons-shrink-alist is potentially expensive, for the following two reasons.

    o The alist is copied, dropping any shadowed pairs. This process will require a hash table lookup for each entry in the alist; and it will require creating a new alist, which uses additional memory.

    o Unless ans is a fast alist that is stolen (see below), a new hash table is created, which uses additional memory. This hash table is populated in time that is linear in the number of unique keys in the alist.

    When ans is not an atom, good discipline requires that it is a fast alist. In this case, hons-shrink-alist steals the hash table for ans and extends it with all of the bindings in alist that are not in ans. From the perspective of hons-assoc-equal, you can think of the resulting alist as being basically similar to (append ans alist), but in a different order.

    Note that when ans is not a fast alist (e.g., ans is an atom) then such stealing does not take place.

    A common idiom is to replace an alist by the result of shrinking it, in which case it is best to free the input alist, for example as follows.

      (let ((alist (fast-alist-free-on-exit alist
                                            (hons-shrink-alist alist nil))))
        ...)
    
    See fast-alist-free-on-exit and see fast-alist-free.




    acl2-sources/doc/HTML/HONS-SHRINK-ALIST_bang_.html0000664002132200015000000000261512222333520020513 0ustar kaufmannacl2 HONS-SHRINK-ALIST_bang_.html -- ACL2 Version 6.3

    HONS-SHRINK-ALIST!

    (hons-shrink-alist! alist ans) is an alternative to hons-shrink-alist that produces a normed result.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically this function is just hons-shrink-alist; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, this is the same as hons-shrink-alist except that it uses something like hons-acons! instead of hons-acons. You generally should not use this function unless you really know what you're doing and understand the drawbacks discussed in hons-acons!.




    acl2-sources/doc/HTML/HONS-SUMMARY.html0000664002132200015000000000254312222333520016652 0ustar kaufmannacl2 HONS-SUMMARY.html -- ACL2 Version 6.3

    HONS-SUMMARY

    (hons-summary) prints basic information about the sizes of the tables in the current Hons Space.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, hons-summary just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, this function gathers and prints some basic information about the sizes of the tables in the Hons Space.

    This information may be useful for deciding if you want to garbage collect using functions such as hons-clear or hons-wash. It may also be useful for determining good initial sizes for the Hons Space hash tables for your particular computation; see hons-resize.




    acl2-sources/doc/HTML/HONS-WASH.html0000664002132200015000000000364212222333520016260 0ustar kaufmannacl2 HONS-WASH.html -- ACL2 Version 6.3

    HONS-WASH

    (hons-wash) is like gc$ but can also garbage collect normed objects (CCL Only).
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, hons-wash just returns nil; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, in CCL, hons-wash runs a garbage collection after taking special measures to allow normed conses to be collected. In Lisps other than CCL, hons-wash does nothing. This is because the efficient implementation of hons-wash is specific to the "static honsing" scheme which requires CCL-specific features.

    Why is this function needed? Ordinarily, it is not possible to garbage collect any normed conses. This is because the Hons Space includes pointers to any normed conses, and hence Lisp's garbage collector thinks these objects are live. To correct for this, hons-wash (1) clears out these pointers, (2) runs a garbage collection, then (3) re-norms any previously-normed conses which have survived the garbage collection.

    Note. It is not recommended to interrupt this function. Doing so may cause persistently normed conses and fast alist keys to become un-normed, which might lead to less efficient re-norming and/or violations of the fast-alist discipline.




    acl2-sources/doc/HTML/HONS.html0000664002132200015000000000322712222333520015517 0ustar kaufmannacl2 HONS.html -- ACL2 Version 6.3

    HONS

    (hons x y) returns a normed object equal to (cons x y).
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    In the logic, hons is just cons; we leave it enabled and would think it odd to ever prove a theorem about it.

    Under the hood, hons does whatever is necessary to ensure that its result is normed.

    What might this involve?

    Since the car and cdr of any normed cons must be normed, we need to hons-copy x and y. This requires little work if x and y are already normed, but can be expensive if x or y contain large, un-normed cons structures.

    After that, we need to check whether any normed cons equal to (x . y) already exists. If so, we return it; otherwise, we need to construct a new cons for (x . y) and install it as the normed version of (x . y).

    Generally speaking, these extra operations make hons much slower than cons, even when given normed arguments.




    acl2-sources/doc/HTML/Hey_Wait_bang___Is_ACL2_Typed_or_Untyped_lparen_Q_rparen_.html0000664002132200015000000000233612222333526030054 0ustar kaufmannacl2 Hey_Wait_bang___Is_ACL2_Typed_or_Untyped_lparen_Q_rparen_.html -- ACL2 Version 6.3

    Hey Wait! Is ACL2 Typed or Untyped?

    The example

    ACL2 !>(app 7 27)
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol ENDP, which
    is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
    call (ENDP 7).
    
    illustrates the fact that while ACL2 is an untyped language the ACL2 evaluator can be configured so as to check ``types'' at runtime. We should not say ``types'' here but ``guards.'' Click here for a discussion of guards.

    The guard on endp requires its argument to be a true list. Since 7 is not a true list, and since ACL2 is checking guards in this example, an error is signaled by ACL2. How do you know ACL2 is checking guards? Because the prompt tells us (click here) with its ``!''.




    acl2-sources/doc/HTML/How_Long_Does_It_Take_to_Become_an_Effective_User_lparen_Q_rparen_.html0000664002132200015000000000223212222333526031776 0ustar kaufmannacl2 How_Long_Does_It_Take_to_Become_an_Effective_User_lparen_Q_rparen_.html -- ACL2 Version 6.3

    How Long Does It Take to Become an Effective User?

    We expect that a talented undergraduate majoring in computer science (or perhaps mathematics) will probably take several weeks to become an effective ACL2 user. The time will depend, of course, on that student's familiarity with logic (or formal methods) and Lisp programming, as well as willingness to read and study the ACL2 User's Manual.

    Of course, it is critical to do some projects in order to gain proficiency. (Hence access to an ACL2 implementation is also a requirement, for example by downloading and installing following links from the ACL2 home page.) But it is critical to start with ``toy'' projects before tackling a ``grand challenge.''




    acl2-sources/doc/HTML/How_To_Find_Out_about_ACL2_Functions.html0000664002132200015000000000346612222333526023734 0ustar kaufmannacl2 How_To_Find_Out_about_ACL2_Functions.html -- ACL2 Version 6.3

    How To Find Out about ACL2 Functions

    Most ACL2 primitives are documented. Here is the definition of app again, with the documented topics highlighted. All of the links below lead into the ACL2 reference manual. So follow these links if you wish, but use your Back Button to return here!

    (defun app (x y)
      (cond ((endp x) y)
            (t (cons (car x)
                     (app (cdr x) y)))))
    

    By following the link on endp we see that it is a Common Lisp function and is defined to be the same as atom , which recognizes non-conses. But endp has a guard. Since we are ignorning guards for now, we'll ignore the guard issue on endp.

    So this definition reads ``to app x and y: if x is an atom, return y; otherwise, app (cdr x) and y and then cons (car x) onto that.''




    acl2-sources/doc/HTML/How_To_Find_Out_about_ACL2_Functions__lparen_cont_rparen_.html0000664002132200015000000000326312222333526030160 0ustar kaufmannacl2 How_To_Find_Out_about_ACL2_Functions__lparen_cont_rparen_.html -- ACL2 Version 6.3

    How To Find Out about ACL2 Functions (cont)

    You can always use the Index icon below to find the documentation of functions. Try it. Click on the Index icon below. Then use the Find command of your browser to find ``endp'' in that document and follow the link. But remember to come back here.

    The ACL2 documentation is also available via Emacs' TexInfo, allowing you to explore the hyperlinked documentation in the comfort of a text editor that can also interact with ACL2.

    In addition, runtime images of ACL2 have the hyperlinked text as a large ACL2 data structure that can be explored with ACL2's :doc command. If you have ACL2 running, try the command :doc endp.

    Another way to find out about ACL2 functions, if you have an ACL2 image available, is to use the command :args which prints the formals, type, and guard of a function symbol.

    Of course, the ACL2 documentation can also be printed out as a very long book but we do not recommend that! See the ACL2 Home Page to download the Postscript.

    Now let's continue with app.




    acl2-sources/doc/HTML/I-AM-HERE.html0000664002132200015000000000254012222333521016152 0ustar kaufmannacl2 I-AM-HERE.html -- ACL2 Version 6.3

    I-AM-HERE

    a convenient marker for use with rebuild
    Major Section:  MISCELLANEOUS
    

    Example Input File for Rebuild:
    (defun fn1 (x y) ...)
    (defthm lemma1 ...)
    (defthm lemma2 ...)
    (i-am-here)
    The following lemma won't go through.  I started
    typing the hint but realized I need to prove a
    lemma first.  See the failed proof attempt in foo.bar.
    I'm going to quit for the night now and resume tomorrow
    from home.
    
    (defthm lemma3 ...
      :hints (("Goal" :use (:instance ???
    ...
    

    By putting an (i-am-here) form at the ``frontier'' of an evolving file of commands, you can use rebuild to load the file up to the (i-am-here). I-am-here simply returns an error triple (see error-triples) that indicates an error, and any form that ``causes an error'' will do the same job. Note that the text of the file after the (i-am-here) need not be machine readable.




    acl2-sources/doc/HTML/I-CLOSE.html0000664002132200015000000000110612222333526016003 0ustar kaufmannacl2 I-CLOSE.html -- ACL2 Version 6.3

    I-CLOSE

    ACL2(r) test for whether two numbers are infinitesimally close
    Major Section:  REAL
    

    (I-close x y) is true if and only if x-y is an infinitesimal number. This predicate is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/I-LARGE.html0000664002132200015000000000112512222333526015771 0ustar kaufmannacl2 I-LARGE.html -- ACL2 Version 6.3

    I-LARGE

    ACL2(r) recognizer for infinitely large numbers
    Major Section:  REAL
    

    (I-large x) is true if and only if x is non-zero and 1/x is an infinitesimal number. This predicate is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/I-LIMITED.html0000664002132200015000000000107712222333526016234 0ustar kaufmannacl2 I-LIMITED.html -- ACL2 Version 6.3

    I-LIMITED

    ACL2(r) recognizer for limited numbers
    Major Section:  REAL
    

    (I-limited x) is true if and only if x is a number that is not infinitely large. This predicate is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/I-SMALL.html0000664002132200015000000000107612222333526016014 0ustar kaufmannacl2 I-SMALL.html -- ACL2 Version 6.3

    I-SMALL

    ACL2(r) recognizer for infinitesimal numbers
    Major Section:  REAL
    

    (I-small x) is true if and only if x is an infinitesimal number (possibly 0). This predicate is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/IDENTITY.html0000664002132200015000000000106612222333523016203 0ustar kaufmannacl2 IDENTITY.html -- ACL2 Version 6.3

    IDENTITY

    the identity function
    Major Section:  ACL2-BUILT-INS
    

    (Identity x) equals x; what else can we say?

    Identity is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/IF-INTRO.html0000664002132200015000000000062712222333521016141 0ustar kaufmannacl2 IF-INTRO.html -- ACL2 Version 6.3

    IF-INTRO

    See splitter.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/IF.html0000664002132200015000000000150612222333523015247 0ustar kaufmannacl2 IF.html -- ACL2 Version 6.3

    IF

    if-then-else function
    Major Section:  ACL2-BUILT-INS
    

    (if x y z) is equal to y if x is any value other than nil, and is equal to z if x is nil.

    Only one of y, z is evaluated when (if x y z) is evaluated.

    If has a guard of t.

    If is part of Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/IFF.html0000664002132200015000000000126112222333523015353 0ustar kaufmannacl2 IFF.html -- ACL2 Version 6.3

    IFF

    logical ``if and only if''
    Major Section:  ACL2-BUILT-INS
    

    Iff is the ACL2 biconditional, ``if and only if''. (iff P Q) means that either P and Q are both false (i.e., nil) or both true (i.e., not nil).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/IFIX.html0000664002132200015000000000162012222333523015505 0ustar kaufmannacl2 IFIX.html -- ACL2 Version 6.3

    IFIX

    coerce to an integer
    Major Section:  ACL2-BUILT-INS
    

    Ifix simply returns any integer argument unchanged, returning 0 on a non-integer argument. Also see nfix, see rfix, see realfix and see fix for analogous functions that coerce to a natural number, a rational number, a real, and a number, respectively.

    Ifix has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/IF_star_.html0000664002132200015000000002036112222333516016441 0ustar kaufmannacl2 IF_star_.html -- ACL2 Version 6.3

    IF*

    for conditional rewriting with BDDs
    Major Section:  BDD
    

    The function IF* is defined to be IF, but it is used in a special way by ACL2's BDD package.

    As explained elsewhere (see bdd-algorithm), ACL2's BDD algorithm gives special treatment to terms of the form (IF* TEST TBR FBR). In such cases, the algorithm simplifies TEST first, and the result of that simplification must be a constant (normally t or nil, but any non-nil explicit value is treated like t here). Otherwise, the algorithm aborts.

    Thus, IF* may be used to implement a sort of conditional rewriting for ACL2's BDD package, even though this package only nominally supports unconditional rewriting. The following contrived example should make this point clear.

    Suppose that we want to prove that (nthcdr (length x) (append x y)) is equal to y, but that we would be happy to prove this only for lists having length 4. We can state such a theorem as follows.

    (let ((x (list x0 x1 x2 x3)))
      (equal (nthcdr (length x) (append x y))
             y))
    
    If we want to prove this formula with a :BDD hint, then we need to have appropriate rewrite rules around. First, note that LENGTH is defined as follows (try :PE LENGTH):
    (length x)
     =
    (if (stringp x)
        (len (coerce x 'list))
        (len x))
    
    Since BDD-based rewriting is merely very simple unconditional rewriting (see bdd-algorithm), we expect to have to prove a rule reducing STRINGP of a CONS:
    (defthm stringp-cons
      (equal (stringp (cons x y))
             nil))
    
    Now we need a rule to compute the LEN of X, because the definition of LEN is recursive and hence not used by the BDD package.
    (defthm len-cons
      (equal (len (cons a x))
             (1+ (len x))))
    
    We imagine this rule simplifying (LEN (LIST X0 X1 X2 X3)) in terms of (LEN (LIST X1 X2 X3)), and so on, and then finally (LEN nil) should be computed by execution (see bdd-algorithm).

    We also need to imagine simplifying (APPEND X Y), where still X is bound to (LIST X0 X1 X2 X3). The following two rules suffice for this purpose (but are needed, since APPEND, actually BINARY-APPEND, is recursive).

    (defthm append-cons
      (equal (append (cons a x) y)
             (cons a (append x y))))
    
    (defthm append-nil
      (equal (append nil x)
             x))
    
    Finally, we imagine needing to simplify calls of NTHCDR, where the first argument is a number (initially, the length of (LIST X0 X1 X2 X3), which is 4). The second lemma below is the traditional way to accomplish that goal (when not using BDDs), by proving a conditional rewrite rule. (The first lemma is only proved in order to assist in the proof of the second lemma.)
    (defthm fold-constants-in-+
      (implies (and (syntaxp (quotep x))
                    (syntaxp (quotep y)))
               (equal (+ x y z)
                      (+ (+ x y) z))))
    
    (defthm nthcdr-add1-conditional
      (implies (not (zp (1+ n)))
               (equal (nthcdr (1+ n) x)
                      (nthcdr n (cdr x)))))
    
    The problem with this rule is that its hypothesis makes it a conditional rewrite rule, and conditional rewrite rules are not used by the BDD package. (See bdd-algorithm for a discussion of ``BDD rules.'') (Note that the hypothesis cannot simply be removed; the resulting formula would be false for n = -1 and x = '(a), for example.) We can solve this problem by using IF*, as follows; comments follow.
    (defthm nthcdr-add1
      (equal (nthcdr (+ 1 n) x)
             (if* (zp (1+ n))
                  x
                  (nthcdr n (cdr x)))))
    
    How is nthcdr-add1 applied by the BDD package? Suppose that the BDD computation encounters a term of the form (NTHCDR (+ 1 N) X). Then the BDD package will apply the rewrite rule nthcdr-add1. The first thing it will do when attempting to simplify the right hand side of that rule is to attempt to simplify the term (ZP (1+ N)). If N is an explicit number (which is the case in the scenario we envision), this test will reduce (assuming the executable counterparts of ZP and BINARY-+ are enabled) to t or to nil. In fact, the lemmas above (not including the lemma nthcdr-add1-conditional) suffice to prove our goal:
    (thm (let ((x (list x0 x1 x2 x3)))
           (equal (nthcdr (length x) (append x y))
                  y))
         :hints (("Goal" :bdd (:vars nil))))
    

    If we execute the following form that disables the definition and executable counterpart of the function ZP

    (in-theory (disable zp (zp)))
    
    before attempting the proof of the theorem above, we can see more clearly the point of using IF*. In this case, the prover makes the following report.
    ACL2 Error in ( THM ...):  Unable to resolve test of IF* for term
    
    (IF* (ZP (+ 1 N)) X (NTHCDR N (CDR X)))
    
    under the bindings
    
    ((X (CONS X0 (CONS X1 (CONS X2 #)))) (N '3))
    
    -- use SHOW-BDD to see a backtrace.
    
    If we follow the advice above, we can see rather clearly what happened. See show-bdd.
    ACL2 !>(show-bdd)
    
    BDD computation on Goal yielded 21 nodes.
    ==============================
    
    BDD computation was aborted on Goal, and hence there is no
    falsifying assignment that can be constructed.  Here is a backtrace
    of calls, starting with the top-level call and ending with the one
    that led to the abort.  See :DOC show-bdd.
    
    (LET ((X (LIST X0 X1 X2 X3)))
         (EQUAL (NTHCDR (LENGTH X) (APPEND X Y)) Y))
      alist: ((Y Y) (X3 X3) (X2 X2) (X1 X1) (X0 X0))
    
    (NTHCDR (LENGTH X) (APPEND X Y))
      alist: ((X (LIST X0 X1 X2 X3)) (Y Y))
    
    (IF* (ZP (+ 1 N)) X (NTHCDR N (CDR X)))
      alist: ((X (LIST* X0 X1 X2 X3 Y)) (N 3))
    ACL2 !>
    
    Each of these term-alist pairs led to the next, and the test of the last one, namely (ZP (+ 1 N)) where N is bound to 3, was not simplified to t or to nil.

    What would have happened if we had used IF in place of IF* in the rule nthcdr-add1? In that case, if ZP and its executable counterpart were disabled then we would be put into an infinite loop! For, each time a term of the form (NTHCDR k V) is encountered by the BDD package (where k is an explicit number), it will be rewritten in terms of (NTHCDR k-1 (CDR V)). We would prefer that if for some reason the term (ZP (+ 1 N)) cannot be decided to be t or to be nil, then the BDD computation should simply abort.

    Even if there were no infinite loop, this kind of use of IF* is useful in order to provide feedback of the form shown above whenever the test of an IF term fails to simplify to t or to nil.




    acl2-sources/doc/HTML/IGNORABLE.html0000664002132200015000000000062312222333525016254 0ustar kaufmannacl2 IGNORABLE.html -- ACL2 Version 6.3

    IGNORABLE

    See declare.
    Major Section:  PROGRAMMING
    




    acl2-sources/doc/HTML/IGNORE.html0000664002132200015000000000061512222333525015736 0ustar kaufmannacl2 IGNORE.html -- ACL2 Version 6.3

    IGNORE

    See declare.
    Major Section:  PROGRAMMING
    




    acl2-sources/doc/HTML/IGNORED-ATTACHMENT.html0000664002132200015000000001153412222333521017466 0ustar kaufmannacl2 IGNORED-ATTACHMENT.html -- ACL2 Version 6.3

    IGNORED-ATTACHMENT

    why attachments are sometimes not used
    Major Section:  MISCELLANEOUS
    

    Attachments provide a way to execute constrained functions. But in some cases, ACL2 will not permit such execution. We discuss this issue briefly here. For more information about attachments, see defattach.

    We illustrate this issue with the following example, discussed below.

      (encapsulate
       ()
       (defstub foo () t)
       (defn foo-impl-1 () t)
       (defattach foo foo-impl-1)
       (defn foo-impl-2 () nil)
       (local (defattach foo foo-impl-2))
       (defmacro mac () (foo)) ; nil in the first pass, t in the second pass
       (defun bar () (mac))    ; nil in the first pass, t in the second pass
       (defthm bar-is-nil
         (equal (bar) nil))
       )
    

    Here, a non-executable function foo is introduced with no constraints, and is provided two contradictory implementations, foo-impl-1 and foo-impl-2. A function, bar, is defined using a macro, mac, whose expansion depends on which of foo-impl-1 or foo-impl-2 is attached to foo. If ACL2 were to allow this, then as indicated by the comments above, (bar) would be defined to be nil on the first pass of the encapsulate form, where foo is attached to foo-impl-2; but (bar) would be defined to be t on the second pass, where foo is attached to foo-impl-1 because the second defattach call is local. Thus, after execution of the encapsulate form, (bar) would be provably equal to t even though there would be a theorem, bar-is-nil -- proved during the first pass of the encapsulate -- saying that (bar) is nil!

    Fortunately, ACL2 does not permit this to happen. The example above produces the following output.

      ACL2 !>>(DEFUN BAR NIL (MAC))
    
    
      ACL2 Error in ( DEFUN BAR ...):  In the attempt to macroexpand the
      form (MAC), evaluation of the macro body caused the following error:
    
      ACL2 cannot ev the call of undefined function FOO on argument list:
    
      NIL
    
      Note that because of logical considerations, attachments (including
      FOO-IMPL-2) must not be called in this context.
    

    We see, then, the importance of disallowing evaluation using attachments during macroexpansion. ACL2 is careful to avoid attachments in situations, like this one, where using attachments could be unsound.

    We conclude with an example illustrating how make-event can be used to work around the refusal of ACL2 to use attachments during macroexpansion. The idea is that make-event expansions are stored, and this avoids the issue of local attachments. In particular, for the example below, the second defattach affects the body of f2 even though that defattach is local, because the expansion of the corresponding make-event is saved during the first pass of certify-book, when full admissibility checks are done. Then even after including the book, the definition of f2 will be based on the second (local) defattach form below.

    (in-package "ACL2")
    
    (defun body-1 (name formals body)
      (declare (ignore name))
      `(if (consp ,(car formals))
           ,body
         nil))
    
    (defun body-2 (name formals body)
      (declare (ignore name))
      `(if (acl2-numberp ,(car formals))
           ,body
         t))
    
    (defmacro defun+ (name formals body)
      `(make-event
        (if (foo) ; attachable stub
            '(defun ,name ,formals ,(body-1 name formals body))
          '(defun ,name ,formals ,(body-2 name formals body)))))
    
    ;;; (defun+ f1 (x y) (cons x y)) ; fails because foo has no attachment
    
    (defstub foo () t)
    (defn foo-true () t)
    (defn foo-false () nil)
    
    (defattach foo foo-true)
    
    (defun+ f1 (x y) (cons x y))
    
    (local (defattach foo foo-false))
    
    (defun+ f2 (x y) (cons x y))
    
    (assert-event (equal (f1 3 t) nil))
    
    (assert-event (equal (f2 3 t) (cons 3 t)))
    




    acl2-sources/doc/HTML/ILLEGAL.html0000664002132200015000000000307212222333523016022 0ustar kaufmannacl2 ILLEGAL.html -- ACL2 Version 6.3

    ILLEGAL

    print an error message and stop execution
    Major Section:  ACL2-BUILT-INS
    

    (Illegal ctx str alist) causes evaluation to halt with a short message using the ``context'' ctx. An error message is first printed using the string str and alist alist that are of the same kind as expected by fmt. See fmt, and see prog2$ for an example of how to use a related function, hard-error (see hard-error). Also see er for a macro that provides a unified way of signaling errors.

    The difference between illegal and hard-error is that the former has a guard of nil while the latter has a guard of t. Thus, you may want to use illegal rather than hard-error when you intend to do guard verification at some point, and you expect the guard to guarantee that the illegal call is never executed. See prog2$ for an example.




    acl2-sources/doc/HTML/IMAGPART.html0000664002132200015000000000121512222333523016152 0ustar kaufmannacl2 IMAGPART.html -- ACL2 Version 6.3

    IMAGPART

    imaginary part of a complex number
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-imagpart):

    (equal (imagpart x)
           (if (acl2-numberp x)
               (imagpart x)
             0))
    

    Guard for (imagpart x):

    (acl2-numberp x)
    




    acl2-sources/doc/HTML/IMMED-FORCED.html0000664002132200015000000000063712222333521016546 0ustar kaufmannacl2 IMMED-FORCED.html -- ACL2 Version 6.3

    IMMED-FORCED

    See splitter.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/IMMEDIATE-FORCE-MODEP.html0000664002132200015000000000376412222333521017753 0ustar kaufmannacl2 IMMEDIATE-FORCE-MODEP.html -- ACL2 Version 6.3

    IMMEDIATE-FORCE-MODEP

    when executable counterpart is enabled, forced hypotheses are attacked immediately
    Major Section:  MISCELLANEOUS
    

    Also see disable-immediate-force-modep and see enable-immediate-force-modep.

    This function symbol is defined simply to provide a rune which can be enabled and disabled. Enabling

    (:executable-counterpart immediate-force-modep)
    
    causes ACL2 to attack forced hypotheses immediately instead of delaying them to the next forcing round.
    Example Hints
    :in-theory (disable (:executable-counterpart immediate-force-modep))
               ; delay forced hyps to forcing round
    :in-theory (enable (:executable-counterpart immediate-force-modep))
               ; split on forced hyps immediately
    
    
    See force for background information. When a forced hypothesis cannot be established a record is made of that fact and the proof continues. When the proof succeeds a ``forcing round'' is undertaken in which the system attempts to prove each of the forced hypotheses explicitly. However, if the rune (:executable-counterpart immediate-force-modep) is enabled at the time the hypothesis is forced, then ACL2 does not delay the attempt to prove that hypothesis but undertakes the attempt more or less immediately.




    acl2-sources/doc/HTML/IMPLIES.html0000664002132200015000000000124112222333523016047 0ustar kaufmannacl2 IMPLIES.html -- ACL2 Version 6.3

    IMPLIES

    logical implication
    Major Section:  ACL2-BUILT-INS
    

    Implies is the ACL2 implication function. (implies P Q) means that either P is false (i.e., nil) or Q is true (i.e., not nil).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/IMPROPER-CONSP.html0000664002132200015000000000137612222333523017073 0ustar kaufmannacl2 IMPROPER-CONSP.html -- ACL2 Version 6.3

    IMPROPER-CONSP

    recognizer for improper (non-null-terminated) non-empty lists
    Major Section:  ACL2-BUILT-INS
    

    Improper-consp is the function that checks whether its argument is a non-empty list that ends in other than nil. See proper-consp and also see true-listp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/IN-ARITHMETIC-THEORY.html0000664002132200015000000000571512222333517017727 0ustar kaufmannacl2 IN-ARITHMETIC-THEORY.html -- ACL2 Version 6.3

    IN-ARITHMETIC-THEORY

    designate theory for some rewriting done for non-linear arithmetic
    Major Section:  EVENTS
    

    We assume familiarity with theories; in particular, see in-theory for the normal way to set the current theory. Here, we discuss an analogous event that pertains only to non-linear arithmetic (see non-linear-arithmetic).

    Example:
    (in-arithmetic-theory '(lemma1 lemma2))
    
    General Form:
    (in-arithmetic-theory term :doc doc-string)
    
    where term is a term that when evaluated will produce a theory (see theories), and doc-string is an optional documentation string not beginning with ``:Doc-Section ...''. Except for the variable world, term must contain no free variables. Term is evaluated with the variable world bound to the current world to obtain a theory and the corresponding runic theory (see theories) is then used by non-linear arithmetic (see non-linear-arithmetic).

    Warning: If term involves macros such as ENABLE and DISABLE you will probably not get what you expect! Those macros are defined relative to the CURRENT-THEORY. But in this context you might wish they were defined in terms of the ``CURRENT-ARITHMETIC-THEORY'' which is not actually a defined function. We do not anticipate that users will repeatedly modify the arithmetic theory. We expect term most often to be a constant list of runes and so have not provided ``arithmetic theory manipulation functions'' analogous to CURRENT-THEORY and ENABLE.

    Because no unique name is associated with an in-arithmetic-theory event, there is no way we can store the documentation string doc-string in our il[documentation] database. Hence, we actually prohibit doc-string from having the form of an ACL2 documentation string; see doc-string.

    See non-linear-arithmetic.




    acl2-sources/doc/HTML/IN-PACKAGE.html0000664002132200015000000000201312222333522016301 0ustar kaufmannacl2 IN-PACKAGE.html -- ACL2 Version 6.3

    IN-PACKAGE

    select current package
    Major Section:  OTHER
    

    Example:
    (in-package "MY-PKG")
    
    General Form:
    (in-package str)
    
    where str is a string that names an existing ACL2 package, i.e., one of the initial packages such as "KEYWORD" or "ACL2" or a package introduced with defpkg. For a complete list of the known packages created with defpkg, evaluate
    (strip-cars (known-package-alist state)).
    
    See defpkg. An ACL2 book (see books) must contain a single in-package form, which must be the first form in that book.




    acl2-sources/doc/HTML/IN-TAU-INTERVALP.html0000664002132200015000000001032312222333530017243 0ustar kaufmannacl2 IN-TAU-INTERVALP.html -- ACL2 Version 6.3

    IN-TAU-INTERVALP

    Boolean membership in a tau interval
    Major Section:  TAU-SYSTEM
    

    General Form:
    (in-tau-intervalp e x)
    

    Here, x should be an interval (see tau-intervalp). This function returns t or nil indicating whether e, which is generally but not necessarily a number, is an element of interval x. By that is meant that e satisfies the domain predicate of the interval and lies between the two bounds.

    Suppose x is an interval with the components dom, lo-rel, lo, hi-rel and hi. Suppose (<? rel u v) means (< u v) when rel is true and (<= u v) otherwise, with appropriate treatment of infinities.

    Then for e to be in interval x, it must be the case that e satisfies the domain predicate dom (where where dom=nil means there is no restriction on the domain) and (<? lo-rel lo e) and (<? hi-rel e hi). [Note: ``Appropriate treatment of infinities'' is slightly awkward if both infinities are represented by the same object, nil. However, this is handled by coercing e to a number after checking that it is in the domain. By this trick, ``<?'' is presented with at most one ``infinity'' and it is always negative when in the first argument and positive when in the second.]

    Note that every element in an INTEGERP interval is contained in the analogous RATIONALP interval (i.e., the interval obtained by just replacing the domain INTEGERP by RATIONALP). That is because every integer is a rational. Similarly, every rational is an ACL2 number.

    Note that an interval in which the relations are weak and the bounds are equal rationals is the ``unit'' or ``identity'' interval containing exactly that rational.

    Note that an interval in which the relations are strong and the bounds are equal rationals is empty: it contains no objects.

    Note that the interval (make-tau-interval nil nil nil nil nil) is the ``universal interval:'' it contains all ACL2 objects. It contains all numbers because they statisfy the non-existent domain restriction and lie between minus infinity and plus infinity. It contains all non-numbers because the interval contains 0 and ACL2's inequalities coerce non-numbers to 0. The universal interval is useful if you are defining a bounder (see bounders) for a function and do not wish to address a certain case: return the universal interval.

    Recall that make-tau-interval constructs intervals. Using make-tau-interval we give several self-explanatory examples of in-tau-intervalp:

    (in-tau-intervalp 3 (make-tau-interval 'INTEGERP nil 0 nil 10))           = t
    (in-tau-intervalp 3 (make-tau-interval 'RATIONALP nil 0 nil 10))          = t
    (in-tau-intervalp 3 (make-tau-interval NIL nil 0 nil 10))                 = t
    
    (in-tau-intervalp -3 (make-tau-interval 'INTEGERP nil 0 nil 10))          = nil
    (in-tau-intervalp 30 (make-tau-interval 'INTEGERP nil 0 nil 10))          = nil
    
    (in-tau-intervalp 3/5 (make-tau-interval 'INTEGERP nil 0 nil 10))         = nil
    (in-tau-intervalp 3/5 (make-tau-interval 'RATIONALP nil 0 nil 10))        = t
    
    (in-tau-intervalp #c(3 5) (make-tau-interval 'RATIONALP nil 0 nil 10))    = nil
    (in-tau-intervalp #c(3 5) (make-tau-interval 'ACL2-NUMBERP nil 0 nil 10)) = t
    
    (in-tau-intervalp 'ABC (make-tau-interval NIL nil 0 nil 10))              = t
    




    acl2-sources/doc/HTML/IN-THEORY.html0000664002132200015000000000651712222333517016301 0ustar kaufmannacl2 IN-THEORY.html -- ACL2 Version 6.3

    IN-THEORY

    designate ``current'' theory (enabling its rules)
    Major Section:  EVENTS
    

    Example:
    (in-theory (set-difference-theories
                 (universal-theory :here)
                 '(flatten (:executable-counterpart flatten))))
    
    General Form:
    (in-theory term :doc doc-string)
    
    where term is a term that when evaluated will produce a theory (see theories), and doc-string is an optional documentation string not beginning with ``:Doc-Section ...''. Because no unique name is associated with an in-theory event, there is no way we can store the documentation string doc-string in our documentation database. Hence, we actually prohibit doc-string from having the form of an ACL2 documentation string; see doc-string.

    Except for the variable world, term must contain no free variables. Term is evaluated with the variable world bound to the current world to obtain a theory and the corresponding runic theory (see theories) is then made the current theory. Thus, immediately after the in-theory, a rule is enabled iff its rule name is a member of the runic interpretation (see theories) of some member of the value of term. See theory-functions for a list of the commonly used theory manipulation functions.

    Note that it is often useful to surround in-theory events with local, that is, to use (local (in-theory ...)). This use of local in encapsulate events and books will prevent the effect of this theory change from being exported outside the context of that encapsulate or book.

    Also see hints for a discussion of the :in-theory hint, including some explanation of the important point that an :in-theory hint will always be evaluated relative to the current ACL2 logical world, not relative to the theory of a previous goal.

    In-theory returns an error triple (see error-triples). When the return is without error, the value is of the form (mv nil (:NUMBER-OF-ENABLED-RUNES k) state) where k is the length of the new current theory. This value of k is what is printed when an in-theory event evaluates without error at the top level.




    acl2-sources/doc/HTML/INCLUDE-BOOK.html0000664002132200015000000003043012222333517016565 0ustar kaufmannacl2 INCLUDE-BOOK.html -- ACL2 Version 6.3

    INCLUDE-BOOK

    load the events in a file
    Major Section:  EVENTS
    

    Examples:
    (include-book "my-arith")
    (include-book "/home/smith/my-arith")
    (include-book "/../../my-arith")
    
    General Form:
    (include-book file :load-compiled-file action
                       :uncertified-okp t/nil      ; [default t]
                       :defaxioms-okp t/nil        ; [default t]
                       :skip-proofs-okp t/nil      ; [default t]
                       :ttags ttags                ; [default nil]
                       :dir directory
                       :doc doc-string)
    
    where file is a book name. See books for general information, see book-name for information about book names, and see pathname for information about file names. Action is one of t, nil, :default, :warn, or :comp; these values are explained below, and the default is :default. The three -okp keyword arguments, which default to t, determine whether errors or warnings are generated under certain conditions explained below; when the argument is t, warnings are generated. The dir argument, if supplied, is a keyword that represents an absolute pathname for a directory (see pathname), to be used instead of the current book directory (see cbd) for resolving the given file argument to an absolute pathname. In particular, by default :dir :system resolves file using the books/ directory of your ACL2 installation, unless your ACL2 executable was built somewhere other than where it currently resides; please see the ``Books Directory'' below. To define other keywords that can be used for dir, see add-include-book-dir. Doc-string is an optional documentation string; see doc-string. If the book has no certificate, if its certificate is invalid or if the certificate was produced by a different version of ACL2, a warning is printed and the book is included anyway; see certificate. This can lead to serious errors, perhaps mitigated by the presence of a .port file from an earlier certification; see uncertified-books. If the portcullis of the certificate (see portcullis) cannot be raised in the host logical world, an error is caused and no change occurs to the logic. Otherwise, the non-local events in file are assumed. Then the keep of the certificate is checked to ensure that the correct files were read; see keep. A warning is printed if uncertified books were included. Even if no warning is printed, include-book places a burden on you; see certificate.

    If you use guards, please note include-book is executed as though (set-guard-checking nil) has been evaluated; see set-guard-checking. If you want guards checked, please see ld and/or see rebuild.

    The value of :load-compiled-file controls whether a compiled file for the given file is loaded by include-book. Note that this keyword applies only to the given file, not to any included sub-books. In order to skip loading all compiled files, for the given file as well as all included sub-books -- for example, to avoid Lisp errors such as ``Wrong FASL version'' -- use (set-compiler-enabled nil state); see compilation. Otherwise, if keyword argument :load-compiled-file is missing or its value is the keyword :default, then it is treated as :warn. If its value is nil, no attempt is made to load the compiled file for the book provided. In order to load the compiled file, it must be more recent than the book's certificate (except in raw mode, where it must be more recent than the book itself; see set-raw-mode). For non-nil values of :load-compiled-file that do not result in a loaded compiled file, ACL2 proceeds as follows. Note that a load of a compiled file or expansion file aborts partway through whenever an include-book form is encountered for which no suitable compiled or expansion file can be loaded. For much more detail, see book-compiled-file.

    t: Cause an error if the compiled file is not loaded. This same requirement is imposed on every include-book form evaluated during the course of evaluation of the present include-book form, except that for those subsidiary include-books that do not themselves specify :load-compiled-file t, it suffices to load the expansion file (see book-compiled-file).

    :warn: An attempt is made to load the compiled file, and a warning is printed if that load fails to run to completion.

    :comp: A compiled file is loaded as with value t, except that if a suitable ``expansion file'' exists but the compiled file does not, then the compiled file is first created. See book-compiled-file.

    The three -okp arguments, :uncertified-okp, defaxioms-okp, and skip-proofs-okp, determine the system's behavior when the book or any subbook is found to be uncertified, when the book or any subbook is found to contain defaxiom events, and when the book or any subbook is found to contain skip-proofs events, respectively. All three default to t, which means it is ``ok'' for the condition to arise. In this case, a warning is printed but the processing to load the book is allowed to proceed. When one of these arguments is nil and the corresponding condition arises, an error is signaled and processing is aborted. Exception: :uncertified-okp is ignored if the include-book is being performed on behalf of a certify-book.

    The keyword argument :ttags may normally be omitted. A few constructs, used for example if you are building your own system based on ACL2, may require it. See defttag for an explanation of this argument.

    Include-book is similar in spirit to encapsulate in that it is a single event that ``contains'' other events, in this case the events listed in the file named. Include-book processes the non-local event forms in the file, assuming that each is admissible. Local events in the file are ignored. You may use include-book to load several books, creating the logical world that contains the definitions and theorems of all of them.

    If any non-local event of the book attempts to define a name that has already been defined -- and the book's definition is not syntactically identical to the existing definition -- the attempt to include the book fails, an error message is printed, and no change to the logical world occurs. See redundant-events for the details.

    When a book is included, the default defun-mode (see default-defun-mode) for the first event is always :logic. That is, the default defun-mode ``outside'' the book -- in the environment in which include-book was called -- is irrelevant to the book. Events that change the defun-mode are permitted within a book (provided they are not in local forms). However, such changes within a book are not exported, i.e., at the conclusion of an include-book, the ``outside'' default defun-mode is always the same as it was before the include-book.

    Unlike every other event in ACL2, include-book puts a burden on you. Used improperly, include-book can be unsound in the sense that it can create an inconsistent extension of a consistent logical world. A certification mechanism is available to help you carry this burden -- but it must be understood up front that even certification is no guarantee against inconsistency here. The fundamental problem is one of file system security. See certificate for a discussion of the security issues.

    At the beginning of execution of an include-book form, even before executing portcullis commands, the value of acl2-defaults-table is restored to the value it had at startup. After execution of an include-book form, the value of acl2-defaults-table is restored to what it was immediately before that include-book form was executed. See acl2-defaults-table.

    Books Directory. We refer to the ``books directory'' of an executable image as the full pathname string of the directory associated with include-book keyword option :dir :system for that image. By default, it is the books/ subdirectory of the directory where the sources reside and the executable image is thus built (except for ACL2(r) -- see real --, where it is books/nonstd/). If those books reside elsewhere, the environment variable ACL2_SYSTEM_BOOKS can be set to the books/ directory under which they reside (a Unix-style pathname, typically ending in books/ or books, is permissible). In most cases, your ACL2 executable is a small script in which you can set this environment variable just above the line on which the actual ACL2 image is invoked, for example:

    export ACL2_SYSTEM_BOOKS
    ACL2_SYSTEM_BOOKS=/home/acl2/4-0/acl2-sources/books
    
    If you follow suggestions in the installation instructions, these books will be the ACL2 community books; see community-books.

    This concludes the guided tour through books. See set-compile-fns for a subtle point about the interaction between include-book and on-the-fly compilation. See certify-book for a discussion of how to certify a book.

    :cited-by Programming




    acl2-sources/doc/HTML/INCOMPATIBLE.html0000664002132200015000000000165212222333531016620 0ustar kaufmannacl2 INCOMPATIBLE.html -- ACL2 Version 6.3

    INCOMPATIBLE

    declaring that two rules should not both be enabled
    Major Section:  THEORIES
    

    Example:
    (theory-invariant (incompatible (:rewrite left-to-right)
                                    (:rewrite right-to-left)))
    
    General Form:
    (incompatible rune1 rune2)
    
    where rune1 and rune2 are two specific runes. The arguments are not evaluated. Invariant is just a macro that expands into a term that checks that not both runes are enabled. See theory-invariant.




    acl2-sources/doc/HTML/INDUCT.html0000664002132200015000000000066112222333521015736 0ustar kaufmannacl2 INDUCT.html -- ACL2 Version 6.3

    INDUCT

    hints keyword :INDUCT
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/INDUCTION.html0000664002132200015000000002342012222333530016302 0ustar kaufmannacl2 INDUCTION.html -- ACL2 Version 6.3

    INDUCTION

    make a rule that suggests a certain induction
    Major Section:  RULE-CLASSES
    

    Example:
    (defthm recursion-by-sub2-induction-rule
      t
      :rule-classes ((:induction :pattern (* 1/2 i)
                                 :condition (and (integerp i) (>= i 0))
                                 :scheme (recursion-by-sub2 i))))
    

    In ACL2, as in Nqthm, the functions in a conjecture ``suggest'' the inductions considered by the system. Because every recursive function must be admitted with a justification in terms of a measure that decreases in a well-founded way on a given set of ``controlling'' arguments, every recursive function suggests a dual induction scheme that ``unwinds'' the function from a given application.

    For example, since append (actually binary-append, but we'll ignore the distinction here) decomposes its first argument by successive cdrs as long as it is a non-nil true list, the induction scheme suggested by (append x y) has a base case supposing x to be either not a true list or to be nil and then has an induction step in which the induction hypothesis is obtained by replacing x by (cdr x). This substitution decreases the same measure used to justify the definition of append. Observe that an induction scheme is suggested by a recursive function application only if the controlling actuals are distinct variables, a condition that is sufficient to ensure that the ``substitution'' used to create the induction hypothesis is indeed a substitution and that it drives down a certain measure. In particular, (append (foo x) y) does not suggest an induction unwinding append because the induction scheme suggested by (append x y) requires that we substitute (cdr x) for x and we cannot do that if x is not a variable symbol.

    Once ACL2 has collected together all the suggested induction schemes it massages them in various ways, combining some to simultaneously unwind certain cliques of functions and vetoing others because they ``flaw'' others. We do not further discuss the induction heuristics here; the interested reader should see Chapter XIV of A Computational Logic (Boyer and Moore, Academic Press, 1979) which represents a fairly complete description of the induction heuristics of ACL2.

    However, unlike Nqthm, ACL2 provides a means by which the user can elaborate the rules under which function applications suggest induction schemes. Such rules are called :induction rules. The definitional principle automatically creates an :induction rule, named (:induction fn), for each admitted recursive function, fn. It is this rule that links applications of fn to the induction scheme it suggests. Disabling (:induction fn) will prevent fn from suggesting the induction scheme derived from its recursive definition. It is possible for the user to create additional :induction rules by using the :induction rule class in defthm.

    Technically we are ``overloading'' defthm by using it in the creation of :induction rules because no theorem need be proved to set up the heuristic link represented by an :induction rule. However, since defthm is generally used to create rules and rule-class objects are generally used to specify the exact form of each rule, we maintain that convention and introduce the notion of an :induction rule. An :induction rule can be created from any lemma whatsoever.

    General Form of an :induction Lemma or Corollary:
    T
    
    General Form of an :induction rule-class:
    (:induction :pattern pat-term
                :condition cond-term
                :scheme scheme-term)
    
    where pat-term, cond-term, and scheme-term are all terms, pat-term is the application of a function symbol, fn, scheme-term is the application of a function symbol, rec-fn, that suggests an induction, and, finally, every free variable of cond-term and scheme-term is a free variable of pat-term. We actually check that rec-fn is either recursively defined -- so that it suggests the induction that is intrinsic to its recursion -- or else that another :induction rule has been proved linking a call of rec-fn as the :pattern to some scheme.

    The induction rule created is used as follows. When an instance of the :pattern term occurs in a conjecture to be proved by induction and the corresponding instance of the :condition term is known to be non-nil (by type reasoning alone), the corresponding instance of the :scheme term is created and the rule ``suggests'' the induction, if any, suggested by that term. (Analysis of that term may further involve induction rules, though the applied rule is removed from consideration during that further analysis, in order to avoid looping.) If rec-fn is recursive, then the suggestion is the one that unwinds that recursion.

    Consider, for example, the example given above,

    (:induction :pattern (* 1/2 i)
                :condition (and (integerp i) (>= i 0))
                :scheme (recursion-by-sub2 i)).
    
    In this example, we imagine that recursion-by-sub2 is the function:
    (defun recursion-by-sub2 (i)
      (if (and (integerp i)
               (< 1 i))
          (recursion-by-sub2 (- i 2))
          t))
    
    Observe that this function recursively decomposes its integer argument by subtracting 2 from it repeatedly and stops when the argument is 1 or less. The value of the function is irrelevant; it is its induction scheme that concerns us. The induction scheme suggested by (recursion-by-sub2 i) is
    (and (implies (not (and (integerp i) (< 1 i)))   ; base case
                  (:p i))
         (implies (and (and (integerp i) (< 1 i))    ; induction step
                       (:p (- i 2)))
                  (:p i)))
    
    We can think of the base case as covering two situations. The first is when i is not an integer. The second is when the integer i is 0 or 1. In the base case we must prove (:p i) without further help. The induction step deals with those integer i greater than 1, and inductively assumes the conjecture for i-2 while proving it for i. Let us call this scheme ``induction on i by twos.''

    Suppose the above :induction rule has been added. Then an occurrence of, say, (* 1/2 k) in a conjecture to be proved by induction would suggest, via this rule, an induction on k by twos, provided k was known to be a nonnegative integer. This is because the induction rule's :pattern is matched in the conjecture, its :condition is satisfied, and the :scheme suggested by the rule is that derived from (recursion-by-sub2 k), which is induction on k by twos. Similarly, the term (* 1/2 (length l)) would suggest no induction via this rule, even though the rule ``fires'' because it creates the :scheme (recursion-by-sub2 (length l)) which suggests no inductions unwinding recursion-by-sub2 (since the controlling argument of recursion-by-sub2 in this :scheme is not a variable symbol).

    Continuing this example one step further illustrates the utility of :induction rules. We could define the function recursion-by-cddr that suggests the induction scheme decomposing its consp argument two cdrs at a time. We could then add the :induction rule linking (* 1/2 (length x)) to (recursion-by-cddr x) and arrange for (* 1/2 (length l)) to suggest induction on l by cddr.

    Observe that :induction rules require no proofs to be done. Such a rule is merely a heuristic link between the :pattern term, which may occur in conjectures to be proved by induction, and the :scheme term, from which an induction scheme may be derived. Hence, when an :induction rule-class is specified in a defthm event, the theorem proved is irrelevant. The easiest theorem to prove is, of course, t. Thus, we suggest that when an :induction rule is to be created, the following form be used:

    (defthm name T
      :rule-classes ((:induction :pattern pat-term
                                 :condition cond-term
                                 :scheme scheme-term)))
    
    The name of the rule created is (:induction name). When that rune is disabled the heuristic link between pat-term and scheme-term is broken.




    acl2-sources/doc/HTML/INITIALIZE-EVENT-USER.html0000664002132200015000000000576612222333531020060 0ustar kaufmannacl2 INITIALIZE-EVENT-USER.html -- ACL2 Version 6.3

    INITIALIZE-EVENT-USER

    user-supplied code to initiate events
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This utility is intended for system hackers, not standard ACL2 users.

    See finalize-event-user to see how to supply code to be run at the end of events. We assume familiarity with finalize-event-user; here we focus on how to supply code for the beginning as well as the end of events.

    As with finalize-event-user, you attach your own function of argument list (ctx qbody state) to initialize-event-user. (See finalize-event-user for discussion of ctx and body.) The attachment should return state and have a trivial guard, requiring (implicitly) only that state satisfies state-p unless you use trust tags to avoid that requirement. For example:

    (defattach initialize-event-user initialize-event-user-test)
    

    Why would you want to do this? Presumably you are building a system on top of ACL2 and you want to track your own data. For example, suppose you want to save the time in some state global variable, my-time. You could do the following.

    (defun my-init (ctx body state)
      (declare (xargs :stobjs state
                      :guard-hints
                      (("Goal" :in-theory (enable read-run-time))))
               (ignore ctx body))
      (mv-let (seconds state)
              (read-run-time state)
              (f-put-global 'start-time seconds state)))
    
    (defun my-final (ctx body state)
      (declare (xargs :stobjs state
                      :guard-hints
                      (("Goal" :in-theory (enable read-run-time))))
               (ignore ctx body))
      (mv-let (seconds state)
              (read-run-time state)
              (prog2$ (if (boundp-global 'start-time state)
                          (cw "Time: ~x0 seconds~%"
                              (- seconds (fix (@ start-time))))
                        (cw "BIG SURPRISE!~%"))
                      (f-put-global 'end-time seconds state))))
    
    (defattach initialize-event-user my-init)
    (defattach finalize-event-user my-final)
    
    Here is an abbreviated log, showing the time being printed at the end.
    ACL2 !>(thm (equal (append (append x y) x y x y x y x y)
                       (append x y x y x y x y x y)))
    
    *1 (the initial Goal, a key checkpoint) is pushed for proof by induction.
    ....
    ACL2 Error in ( THM ...):  See :DOC failure.
    
    ******** FAILED ********
    Time: 869/100 seconds
    ACL2 !>
    




    acl2-sources/doc/HTML/INSTRUCTIONS.html0000664002132200015000000005157512222333525016732 0ustar kaufmannacl2 INSTRUCTIONS.html -- ACL2 Version 6.3

    INSTRUCTIONS

    instructions to the proof checker
    Major Section:  PROOF-CHECKER
    

    See proof-checker for an introduction to the interactive ``proof-checker'' goal manager, which supports much more direct control of the proof process than is available by direct calls to the prover (as are normally made using defthm or thm). In brief, typical use is to evaluate the form (verify SOME-GOAL), where SOME-GOAL is a formula (i.e., term) that you would like to prove. Various commands (instructions) are available at the resulting prompt; see proof-checker-commands. When the proof is completed, suitable invocation of the exit command will print out a form containing an :instructions field that provides the instructions that you gave interactively, so that this form can be evaluated non-interactively.

    Thus, also see defthm for the role of :instructions in place of :hints. As illustrated by the following example, the value associated with :instructions is a list of proof-checker commands.

    Example:
    (defthm associativity-of-append
            (equal (append (append x y) z)
                   (append x (append y z)))
            :instructions
            (:induct (:dv 1) (:dv 1) :x :up :x (:dv 2) := (:drop 2)
             :top (:dv 2) :x :top :s :bash))
    

    When you are inside the interactive loop (i.e., after executing verify), you may invoke help to get a list of legal instructions and (help instr) to get help for the instruction instr.

    Below, we describe a capability for supplying :instructions as :hints.

    The most basic utilities for directing the discharge of a proof obligation are :hints and (less commonly) :instructions. Individual instructions may call the prover with :hints; in that sense, prover hints may occur inside instructions. We now describe how, on the other hand, instructions may occur inside hints.

    ACL2 supports :instructions as a hints keyword. The following example forms the basis for our running example. This example does not actually need hints, but imagine that the inductive step -- which is "Subgoal *1/2" -- was difficult. You could submit that goal to verify, do an interactive proof, submit (exit t) to obtain the list of :instructions, and then paste in those instructions. When you submit the resulting event, you might see the following. Below we'll explain the hint processing.

    ACL2 !>(thm (equal (append (append x y) z)
                       (append x (append y z)))
                :hints (("Subgoal *1/2"
                         :instructions
                         (:promote (:dv 1) (:dv 1) :x :up :x (:dv 2) :=
                          (:drop 2) :top (:dv 2) :x :top :s))))
    
    Name the formula above *1.
    
    Perhaps we can prove *1 by induction.  Three induction schemes are
    suggested by this conjecture.  Subsumption reduces that number to two.
    However, one of these is flawed and so we are left with one viable
    candidate.
    
    We will induct according to a scheme suggested by (BINARY-APPEND X Y).
    This suggestion was produced using the :induction rule BINARY-APPEND.
    If we let (:P X Y Z) denote *1 above then the induction scheme we'll
    use is
    (AND (IMPLIES (AND (NOT (ENDP X)) (:P (CDR X) Y Z))
                  (:P X Y Z))
         (IMPLIES (ENDP X) (:P X Y Z))).
    This induction is justified by the same argument used to admit BINARY-APPEND.
    When applied to the goal at hand the above induction scheme produces
    two nontautological subgoals.
    
    [Note:  A hint was supplied for our processing of the goal below.
    Thanks!]
    
    Subgoal *1/2
    (IMPLIES (AND (NOT (ENDP X))
                  (EQUAL (APPEND (APPEND (CDR X) Y) Z)
                         (APPEND (CDR X) Y Z)))
             (EQUAL (APPEND (APPEND X Y) Z)
                    (APPEND X Y Z))).
    
    But the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC replaces
    this goal by T.
    
    Subgoal *1/1
    (IMPLIES (ENDP X)
             (EQUAL (APPEND (APPEND X Y) Z)
                    (APPEND X Y Z))).
    
    By the simple :definition ENDP we reduce the conjecture to
    
    Subgoal *1/1'
    (IMPLIES (NOT (CONSP X))
             (EQUAL (APPEND (APPEND X Y) Z)
                    (APPEND X Y Z))).
    
    But simplification reduces this to T, using the :definition BINARY-APPEND
    and primitive type reasoning.
    
    That completes the proof of *1.
    
    Q.E.D.
    
    Summary
    Form:  ( THM ...)
    Rules: ((:DEFINITION BINARY-APPEND)
            (:DEFINITION ENDP)
            (:DEFINITION NOT)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:INDUCTION BINARY-APPEND))
    Time:  0.02 seconds (prove: 0.01, print: 0.01, other: 0.00)
    
    Proof succeeded.
    ACL2 !>
    

    To understand how the :instructions supplied above were processed, observe proof-checker instruction interpreter may be viewed as a ``clause-processor'': a function that takes an input goal and returns a list of goals (which can be the empty list). Such a function has the property that if all goals in that returned list are theorems, then so is the input goal. This view of the proof-checker instruction interpreter as a clause-processor leads to the following crucial observation.

    IMPORTANT!. Each call of the proof-checker instruction interpreter is treated as a standalone clause-processor that is insensitive to the surrounding prover environment. In particular:

    o The proof-checker's theory is not sensitive to :in-theory hints already processed in the surrounding proof. Indeed, the current theory for which proof-checker commands are processed is just the current theory of the ACL2 logical world, i.e., the value of (current-theory :here). Moreover, references to (current-theory :here) in a proof-checker in-theory command, even implicit references such as provided by enable and disable expressions, are also references to the current theory of the ACL2 logical world.

    o The runes used during an :instructions hint are not tracked beyond that hint, hence may not show up in the summary of the overall proof. Again, think of the :instructions hint as a clause-processor call, which has some effect not tracked by the surrounding proof other than for the child goals that it returns.

    We continue now with our discussion of the proof-checker instruction interpreter as a clause-processor.

    In the example above, the input goal ("Subgoal *1/2") was processed by the proof-checker instruction interpreter. The result was the empty goal stack, therefore proving the goal, as reported in the output, which we repeat here.

    [Note:  A hint was supplied for our processing of the goal below.
    Thanks!]
    
    Subgoal *1/2
    (IMPLIES (AND (NOT (ENDP X))
                  (EQUAL (APPEND (APPEND (CDR X) Y) Z)
                         (APPEND (CDR X) Y Z)))
             (EQUAL (APPEND (APPEND X Y) Z)
                    (APPEND X Y Z))).
    
    But the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC replaces
    this goal by T.
    

    Remark. This brief remark can probably be ignored, but we include it for completeness. The :CLAUSE-PROCESSOR message above may be surprising, since the hint attached to "Subgoal *1/2" is an :instructions hint, not a :clause-processor hint. But :instructions is actually a custom keyword hint (see custom-keyword-hints), and may be thought of as a macro that expands to a :clause-processor hint, one that specifies proof-checker-cl-proc as the clause-processor function. The keen observer may notice that the clause-processor is referred to as ``trusted'' in the above output. Normally one needs a trust tag (see defttag) to install a trusted clause-processor, but that is not the case for the built-in clause-processor, proof-checker-cl-proc. Finally, we note that :instructions hints are ``spliced'' into the hints as follows: the appropriate :clause-processor hint replaces the :instructions hint, and the other hints remain intact. It may seems surprising that one can thus, for example, use :instructions and :in-theory together; but although the :in-theory hint will have no effect on execution of the :instructions (see first bullet above), the :in-theory hint will apply in the usual manner to any child goals (see hints-and-the-waterfall). End of Remark.

    Now consider the case that the supplied instructions do not prove the goal. That is, suppose that the execution of those instructions results in a non-empty goal stack. In that case, the resulting goals become children of the input goals. The following edited log provides an illustration using a modification of the above example, this time with a single instruction that splits into two cases.

    ACL2 !>(thm (equal (append (append x y) z)
                       (append x (append y z)))
                :hints (("Subgoal *1/2"
                         :instructions
                         ((:casesplit (equal x y))))))
    
    [[ ... output omitted ... ]]
    
    Subgoal *1/2
    (IMPLIES (AND (NOT (ENDP X))
                  (EQUAL (APPEND (APPEND (CDR X) Y) Z)
                         (APPEND (CDR X) Y Z)))
             (EQUAL (APPEND (APPEND X Y) Z)
                    (APPEND X Y Z))).
    
    We now apply the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC
    to produce two new subgoals.
    
    Subgoal *1/2.2
    
    [[ ... output omitted ... ]]
    
    Subgoal *1/2.1
    
    [[ ... output omitted ... ]]
    

    We have seen that an :instructions hint may produce zero or more subgoals. There may be times where you wish to insist that it produce zero subgoals, i.e., that it prove the desired goal. The proof-checker `finish' command works nicely for this purpose. For example, the following form is successfully admitted, but if you delete some of the commands (for example, the :s command at the end), you will see an informative error message.

    (thm (equal (append (append x y) z)
                (append x (append y z)))
         :hints (("Subgoal *1/2"
                  :instructions
                  ((finish :promote (:dv 1) (:dv 1) :x :up :x (:dv 2) :=
                           (:drop 2) :top (:dv 2) :x :top :s)))))
    

    If an :instructions hint of the form ((finish ...)) fails to prove the goal, the clause-processor is deemed to have caused an error. Indeed, any ``failure'' of a supplied proof-checker instruction will be deemed to cause an error. In this case, you should see an error message such as the following:

    
    Saving proof-checker error state; see :DOC instructions.  To retrieve:
    
    (RETRIEVE :ERROR1)
    
    
    In this case, you can evaluate the indicated retrieve command in the ACL2 read-eval-print loop, to get to the point of failure.

    You may have noticed that there is no output from the proof-checker in the examples above. This default behavior prevents confusion that could arise from use of proof-checker commands that call the theorem prover such as prove, bash, split, and induct. These commands produce output for what amounts to a fresh proof attempt, which could confuse attempts to understand the surrounding proof log. You can override the default behavior by providing a command of the form

    (comment inhibit-output-lst VAL)
    
    where VAL is either the keyword :SAME (indicating that no change should be made to which output is inhibited) or else is a legal value for inhibited output; see set-inhibit-output-lst. The following two variants of the immediately preceding THM form will each produce output from the proof-checker commands, assuming in the first variant that output hasn't already been inhibited.
    (thm (equal (append (append x y) z)
                       (append x (append y z)))
                :hints (("Subgoal *1/2"
                         :instructions
                         ((comment inhibit-output-lst :same)
                          (:casesplit (equal x y))))))
    
    (thm (equal (append (append x y) z)
                       (append x (append y z)))
                :hints (("Subgoal *1/2"
                         :instructions
                         ((comment inhibit-output-lst (proof-tree))
                          (:casesplit (equal x y))))))
    
    Note that such a comment instruction must be provided explicitly (i.e., not by way of a proof-checker macro-command) as the first instruction, in order to have the effect on inhibited output that is described above.

    The following contrived example gives a sense of how one might want to use :instructions within :hints. If you submit the following theorem

    (thm (implies (true-listp x)
                  (equal (reverse (reverse x)) x)))
    
    then you will see the following checkpoint printed with the summary.
    Subgoal *1/3''
    (IMPLIES (AND (CONSP X)
                  (EQUAL (REVAPPEND (REVAPPEND (CDR X) NIL) NIL)
                         (CDR X))
                  (TRUE-LISTP (CDR X)))
             (EQUAL (REVAPPEND (REVAPPEND (CDR X) (LIST (CAR X)))
                               NIL)
                    X))
    
    This suggests proving the following theorem. Here we state it using defthmd, so that it is immediately disabled. Normally disabling would be unnecessary, but for our contrived example it is useful to imagine disabling it, say because we are following a methodology that tends to keep rewrite rules disabled.
    (defthmd revappend-revappend
      (equal (revappend (revappend x y) z)
             (revappend y (append x z))))
    
    We might then enter the proof-checker to prove the original theorem interactively, as follows.
    ACL2 !>(verify (implies (true-listp x)
                            (equal (reverse (reverse x)) x)))
    ->: bash
    ***** Now entering the theorem prover *****
    Goal'
    
    ([ A key checkpoint:
    
    Goal'
    (IMPLIES (TRUE-LISTP X)
             (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL)
                    X))
    
    Goal' is subsumed by a goal yet to be proved.
    
    ])
    
    Q.E.D.
    
    
    Creating one new goal:  (MAIN . 1).
    
    The proof of the current goal, MAIN, has been completed.  However,
    the following subgoals remain to be proved:
      (MAIN . 1).
    Now proving (MAIN . 1).
    ->: th ; show current goal ("th" for "theorem")
    *** Top-level hypotheses:
    1. (TRUE-LISTP X)
    
    The current subterm is:
    (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL)
           X)
    ->: p ; show current subterm only
    (EQUAL (REVAPPEND (REVAPPEND X NIL) NIL)
           X)
    ->: 1 ; dive to first argument
    ->: p
    (REVAPPEND (REVAPPEND X NIL) NIL)
    ->: sr ; show-rewrites
    
    1. REVAPPEND-REVAPPEND (disabled)
      New term: (REVAPPEND NIL (APPEND X NIL))
      Hypotheses: <none>
      Equiv: EQUAL
    
    2. REVAPPEND
      New term: (AND (CONSP (REVAPPEND X NIL))
                     (REVAPPEND (CDR (REVAPPEND X NIL))
                                (LIST (CAR (REVAPPEND X NIL)))))
      Hypotheses: <none>
      Equiv: EQUAL
    ->: (r 1) ; rewrite with rule #1 above
    Rewriting with REVAPPEND-REVAPPEND.
    ->: p
    (REVAPPEND NIL (APPEND X NIL))
    ->: top ; move to the top of the conclusion, making it the current subterm
    ->: p
    (EQUAL (REVAPPEND NIL (APPEND X NIL)) X)
    ->: prove ; finish the proof
    ***** Now entering the theorem prover *****
    
    Q.E.D.
    
    *!*!*!*!*!*!* All goals have been proved! *!*!*!*!*!*!*
    You may wish to exit.
    ->: (exit t) ; the argument, t, causes :instructions to be printed
    (DEFTHM T
            (IMPLIES (TRUE-LISTP X)
                     (EQUAL (REVERSE (REVERSE X)) X))
            :INSTRUCTIONS (:BASH (:DV 1)
                                 (:REWRITE REVAPPEND-REVAPPEND)
                                 :TOP :PROVE))
     NIL
    ACL2 !>(thm
            (IMPLIES (TRUE-LISTP X)
                     (EQUAL (REVERSE (REVERSE X)) X))
            :hints (("Goal"
                     :INSTRUCTIONS ; copy what was printed above:
                     (:BASH (:DV 1)
                            (:REWRITE REVAPPEND-REVAPPEND)
                            :TOP :PROVE))))
    Goal'
    
    Q.E.D.
    
    Q.E.D.
    
    Q.E.D.
    
    Summary
    Form:  ( THM ...)
    Rules: NIL
    Hint-events: ((:CLAUSE-PROCESSOR PROOF-CHECKER-CL-PROC))
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Proof succeeded.
    ACL2 !>
    

    Finally we present an even more contrived example, based on the one above. This example illustrates that there is actually no limit imposed on the nesting of :instructions within :hints within :instructions, and so on. Notice the indication of nesting levels: ``1>'' to ``<1'' for output from nesting level 1, and ``2>'' to ``<2'' for output from nesting level 2.

    (thm (implies (true-listp x)
                  (equal (reverse (reverse x)) x))
         :hints (("Goal"
                  :instructions
                  ((comment inhibit-output-lst :same)
                   (:prove
                    :hints (("Goal" :in-theory (disable append))
                            ("Subgoal *1/3''"
                             :instructions
                             ((comment inhibit-output-lst :same)
                              :bash
                              (:dv 1)
                              (:rewrite revappend-revappend)))))))))
    
    Here is an edited version of the resulting log.
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    [[1> Executing proof-checker instructions]]
    
    ->: (COMMENT INHIBIT-OUTPUT-LST :SAME)
    ->: (:PROVE
             :HINTS
             (("Goal" :IN-THEORY (DISABLE APPEND))
              ("Subgoal *1/3''" :INSTRUCTIONS ((COMMENT INHIBIT-OUTPUT-LST :SAME)
                                               :BASH (:DV 1)
                                               (:REWRITE REVAPPEND-REVAPPEND)))))
    ***** Now entering the theorem prover *****
    
    [[ ... output omitted ... ]]
    
    [Note:  A hint was supplied for our processing of the goal below.
    Thanks!]
    
    Subgoal *1/3''
    (IMPLIES (AND (CONSP X)
                  (EQUAL (REVAPPEND (REVAPPEND (CDR X) NIL) NIL)
                         (CDR X))
                  (TRUE-LISTP (CDR X)))
             (EQUAL (REVAPPEND (REVAPPEND (CDR X) (LIST (CAR X)))
                               NIL)
                    X)).
    
    [[2> Executing proof-checker instructions]]
    
    ->: (COMMENT INHIBIT-OUTPUT-LST :SAME)
    ->: :BASH
    ***** Now entering the theorem prover *****
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    But we have been asked to pretend that this goal is subsumed by the
    yet-to-be-proved |PROOF-CHECKER Goal|.
    
    Q.E.D.
    
    
    Creating one new goal:  (MAIN . 1).
    
    The proof of the current goal, MAIN, has been completed.  However,
    the following subgoals remain to be proved:
      (MAIN . 1).
    Now proving (MAIN . 1).
    ->: (:DV 1)
    ->: (:REWRITE REVAPPEND-REVAPPEND)
    Rewriting with REVAPPEND-REVAPPEND.
    
    [[<2 Completed proof-checker instructions]]
    
    We now apply the trusted :CLAUSE-PROCESSOR function PROOF-CHECKER-CL-PROC
    to produce one new subgoal.
    
    Subgoal *1/3'''
    
    [[ ... output omitted ... ]]
    
    [[<1 Completed proof-checker instructions]]
    
    The nesting levels are independent of whether or not output is enabled; for example, if the first (comment ...) form below is omitted, then we will see only the output bracketed by ``2>'' to ``<2''. Note also that these levels are part of the error states saved for access by retrieve (as indicated above); for example, a failure at level 1 would be associated with symbol :ERROR1 as indicated above, while a failure at level 2 would be associated with symbol :ERROR2.




    acl2-sources/doc/HTML/INT=.html0000664002132200015000000000147612222333523015506 0ustar kaufmannacl2 INT=.html -- ACL2 Version 6.3

    INT=

    test equality of two integers
    Major Section:  ACL2-BUILT-INS
    

    (int= x y) is logically equivalent to (equal x y).

    Unlike equal, int= requires its arguments to be numbers (or else causes a guard violation; see guard). Generally, int= is executed more efficiently than equal or = on integers.




    acl2-sources/doc/HTML/INTEGER-LENGTH.html0000664002132200015000000000173512222333523017031 0ustar kaufmannacl2 INTEGER-LENGTH.html -- ACL2 Version 6.3

    INTEGER-LENGTH

    number of bits in two's complement integer representation
    Major Section:  ACL2-BUILT-INS
    

    For non-negative integers, (integer-length i) is the minimum number of bits needed to represent the integer. Any integer can be represented as a signed two's complement field with a minimum of (+ (integer-length i) 1) bits.

    The guard for integer-length requires its argument to be an integer. Integer-length is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/INTEGER-LISTP.html0000664002132200015000000000112212222333523016731 0ustar kaufmannacl2 INTEGER-LISTP.html -- ACL2 Version 6.3

    INTEGER-LISTP

    recognizer for a true list of integers
    Major Section:  ACL2-BUILT-INS
    

    The predicate integer-listp tests whether its argument is a true list of integers.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/INTEGERP.html0000664002132200015000000000073412222333523016170 0ustar kaufmannacl2 INTEGERP.html -- ACL2 Version 6.3

    INTEGERP

    recognizer for whole numbers
    Major Section:  ACL2-BUILT-INS
    

    (integerp x) is true if and only if x is an integer.




    acl2-sources/doc/HTML/INTERESTING-APPLICATIONS.html0000664002132200015000000001720112222333515020430 0ustar kaufmannacl2 INTERESTING-APPLICATIONS.html -- ACL2 Version 6.3

    INTERESTING-APPLICATIONS

    some industrial examples of ACL2 use
    Major Section:  ACL2-TUTORIAL
    

    ACL2 is an interactive system in which you can model digital artifacts and guide the system to mathematical proofs about the behavior of those models. It has been used at such places as AMD, Centaur, IBM, and Rockwell Collins to verify interesting properties of commercial designs. It has been used to verify properties of models of microprocessors, microcode, the Sun Java Virtual Machine, operating system kernels, other verifiers, and interesting algorithms.

    Here we list just a few of the industrially-relevant results obtained with ACL2. Reading the list may help you decide you want to learn how to use ACL2. If you do decide you want to learn more, we recommend that you take The Tours after you leave this page.

    ACL2 was used at Motorola Government Systems to certify several microcode programs for the Motorola CAP digital signal processor, including a comparator sort program that is particularly subtle. In the same project, ACL2 was used to model the CAP at both the pipelined architectural level and the instruction set level. The architectural model was bit- and cycle-accurate: it could be used to predict every bit of memory on every cycle. The models were proved equivalent under certain hypotheses, the most important being a predicate that analyzed the microcode for certain pipeline hazards. This predicate defined what the hazards were, syntactically, and the equivalence of the two models established the correctness of this syntactic characterization of hazards. Because ACL2 is a functional programming language, the ACL2 models and the hazard predicate could be executed. ACL2 executed microcode interpretr several times faster than the hardware simulator could execute it -- with assurance that the answers were equivalent. In addition, the ACL2 hazard predicate was executed on over fifty microcode programs written by Motorola engineers and extracted from the ROM mechanically. Hazards were found in some of these. (See, for example, Bishop Brock and Warren. A. Hunt, Jr. ``Formal analysis of the motorola CAP DSP.'' In Industrial-Strength Formal Methods. Springer-Verlag, 1999.)

    ACL2 was used at Advanced Micro Devices (AMD) to verify the compliance of the AMD Athon's (TM) elementary floating point operations with their IEEE 754 specifications. This followed ground-breaking work in 1995 when ACL2 was used to prove the correctness of the microcode for floating-point division on the AMD K5. The AMD Athlon work proved addition, subtraction, multiplication, division, and square root compliant with the IEEE standard. Bugs were found in RTL designs. These bugs had survived undetected in hundreds of millions of tests but were uncovered by ACL2 proof attempts. The RTL in the fabricated Athlon FPU has been mechanically verified by ACL2. Similar ACL2 proofs have been carried out for every major AMD FPU design fabricated since the Athlon. (See for example, David Russinoff. ``A mechanically checked proof of correctness of the AMD5K86 floating-point square root microcode''. Formal Methods in System Design Special Issue on Arithmetic Circuits, 1997.)

    ACL2 was used at IBM to verify the floating point divide and square root on the IBM Power 4. (See Jun Sawada. ``Formal verification of divide and square root algorithms using series calculation''. In Proceedings of the ACL2 Workshop 2002, Grenoble, April 2002.)

    ACL2 was used to verify floating-point addition/subtraction instructions for the media unit from Centaur Technology's 64-bit, X86-compatible microprocessor. This unit implements over one hundred instructions, with the most complex being floating-point addition/subtraction. The media unit can add/subtract four pairs of floating-point numbers every clock cycle with an industry-leading two-cycle latency. The media unit was modeled by translating its Verilog design into an HDL deeply embedded in the ACL2 logic. The proofs used a combination of AIG- and BDD-based symbolic simulation, case splitting, and theorem proving. (See Warren A. Hunt, Jr. and Sol Swords. ``Centaur Technology Media Unit Verification''. In CAV '09: Proceedings of the 21st International Conference on Computer Aided Verification, pages 353--367, Berlin, Heidelberg, 2009. Springer-Verlag.)

    Rockwell Collins used ACL2 to prove information flow properties about its Advanced Architecture MicroProcessor 7 Government Version (AAMP7G), a Multiple Independent Levels of Security (MILS) device for use in cryptographic applications. The AAMP7G provides MILS capability via a verified secure hardware-based separation kernel. The AAMP7G's design was proved to achieve MILS using ACL2, in accordance with the standards set by EAL-7 of the Common Criteria and Rockwell Collins has received National Security Agency (NSA) certification for the device based on this work. (See David S. Hardin, Eric W. Smith, and William. D. Young. ``A robust machine code proof framework for highly secure applications''. In Proceedings of the sixth international workshop on the ACL2 theorem prover and its applications, pages 11--20, New York, NY, USA, 2006. ACM.)

    Key properties of the Sun Java Virtual Machine and its bytecode verifier were verified in ACL2. Among the properties proved were that certain invariants are maintained by class loading and that the bytecode verifier insures that execution is safe. In addition, various JVM bytecode programs have been verified using this model of the JVM. (See Hanbing Liu. Formal Specification and Verification of a JVM and its Bytecode Verifier. PhD thesis, University of Texas at Austin, 2006.)

    The Boyer-Moore fast string searching algorithm was verified with ACL2, including a model of the JVM bytecode for the search algorithm itself (but not the preprocessing). (See J S. Moore and Matt Martinez. ``A mechanically checked proof of the correctness of the Boyer-Moore fast string searching algorithm.'' In Engineering Methods and Tools for Software Safety and Security pages 267--284. IOS Press, 2009.)

    ACL2 was used to verify the fidelity between an ACL2-like theorem prover and a simple (``trusted by inspection'') proof checker, thereby establishing (up to the soundness of ACL2) the soundness of the ACL2-like theorem prover. This project was only part of a much larger project in which the resulting ACL2 proof script was then hand-massaged into a script suitable for the ACL2-like theorem prover to process, generating a formal proof of its fidelity that has been checked by the trusted proof checker. (See Jared Davis. Milawa: A Self-Verifying Theorem Prover. Ph.D. Thesis, University of Texas at Austin, December, 2009.)

    These are but a few of the interesting projects carried out with ACL2. Many of the authors mentioned above have versions of the papers on their web pages. In addition, see the link to ``Books and Papers about ACL2 and Its Applications'' on the ACL2 home page (http://www.cs.utexas.edu/users/moore/acl2). Also see the presentations in each of the workshops listed in the link to ``ACL2 Workshops'' on the ACL2 home page.




    acl2-sources/doc/HTML/INTERN$.html0000664002132200015000000000207312222333523016014 0ustar kaufmannacl2 INTERN$.html -- ACL2 Version 6.3

    INTERN$

    create a new symbol in a given package
    Major Section:  ACL2-BUILT-INS
    

    Intern$ is a macro that behaves the same as the macro intern, except for weakening the restriction to a fixed set of package names so that any package name other than "" is legal. See intern. Note that if you evaluate a call (intern$ x y) for which there is no package with name y that is known to ACL2, you will get an error.

    (Intern$ x y) expands to:

    (intern-in-package-of-symbol x (pkg-witness y))
    
    See intern-in-package-of-symbol and see pkg-witness.




    acl2-sources/doc/HTML/INTERN-IN-PACKAGE-OF-SYMBOL.html0000664002132200015000000000574512222333523020623 0ustar kaufmannacl2 INTERN-IN-PACKAGE-OF-SYMBOL.html -- ACL2 Version 6.3

    INTERN-IN-PACKAGE-OF-SYMBOL

    create a symbol with a given name
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-intern-in-package-of-symbol):

    (equal (intern-in-package-of-symbol x y)
           (if (and (stringp x)
                    (symbolp y))
               (intern-in-package-of-symbol x y)
             nil))
    

    Guard for (intern-in-package-of-symbol x y):

    (and (stringp x) (symbolp y))
    

    Intuitively, (intern-in-package-of-symbol x y) creates a symbol with symbol-name x interned in the package containing y. More precisely, suppose x is a string, y is a symbol with symbol-package-name pkg and that the defpkg event creating pkg had the list of symbols imports as the value of its second argument. Then (intern-in-package-of-symbol x y) returns a symbol, ans, the symbol-name of ans is x, and the symbol-package-name of ans is pkg, unless x is the symbol-name of some member of imports with symbol-package-name ipkg, in which case the symbol-package-name of ans is ipkg. Because defpkg requires that there be no duplications among the symbol-names of the imports, intern-in-package-of-symbol is uniquely defined.

    For example, suppose "MY-PKG" was created by

    (defpkg "MY-PKG" '(ACL2::ABC LISP::CAR)).
    
    Let w be 'my-pkg::witness. Observe that
    (symbolp w) is t                     ; w is a symbol
    (symbol-name w) is "WITNESS"         ; w's name is "WITNESS"
    (symbol-package-name w) is "MY-PKG"  ; w is in the package "MY-PKG"
    
    The construction of w illustrates one way to obtain a symbol in a given package: write it down as a constant using the double-colon notation.

    But another way to obtain a symbol in a given package is to create it with intern-in-package-of-symbol.

    (intern-in-package-of-symbol "XYZ" w) is MY-PKG::XYZ
    
    (intern-in-package-of-symbol "ABC" w) is ACL2::ABC
    
    (intern-in-package-of-symbol "CAR" w) is LISP::CAR
    
    (intern-in-package-of-symbol "car" w) is MY-PKG::|car|
    




    acl2-sources/doc/HTML/INTERN.html0000664002132200015000000000452412222333523015753 0ustar kaufmannacl2 INTERN.html -- ACL2 Version 6.3

    INTERN

    create a new symbol in a given package
    Major Section:  ACL2-BUILT-INS
    

    (intern symbol-name symbol-package-name) returns a symbol with the given symbol-name and the given symbol-package-name. We restrict Common Lisp's intern so that the second argument is either the symbol *main-lisp-package-name*, the value of that constant, or is one of "ACL2", "ACL2-INPUT-CHANNEL", "ACL2-OUTPUT-CHANNEL", or "KEYWORD". To avoid that restriction, see intern$.

    In ACL2 intern is actually implemented as a macro that expands to a call of a similar function whose second argument is a symbol. Invoke :pe intern to see the definition, or see intern-in-package-of-symbol.

    To see why is intern so restricted consider (intern "X" "P"). In particular, is it a symbol and if so, what is its symbol-package-name? One is tempted to say ``yes, it is a symbol in the package "P".'' But if package "P" has not yet been defined, that would be premature because the imports to the package are unknown. For example, if "P" were introduced with

    (defpkg "P" '(LISP::X))
    
    then in Common Lisp (symbol-package-name (intern "X" "P")) returns "LISP".

    The obvious restriction on intern is that its second argument be the name of a package known to ACL2. We cannot express such a restriction (except, for example, by limiting it to those packages known at some fixed time, as we do). Instead, we provide intern-in-package-of-symbol which requires a ``witness symbol'' for the package instead of the package. The witness symbol is any symbol (expressible in ACL2) and uniquely specifies a package necessarily known to ACL2.




    acl2-sources/doc/HTML/INTERSECTION$.html0000664002132200015000000000650112222333523016723 0ustar kaufmannacl2 INTERSECTION$.html -- ACL2 Version 6.3

    INTERSECTION$

    elements of one list that are not elements of another
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (intersection$ l1 l2 ... lk)
    (intersection$ l1 l2 ... lk :test 'eql) ; same as above
    (intersection$ l1 l2 ... lk :test 'eq)    ; same, but eq is equality test
    (intersection$ l1 l2 ... lk :test 'equal) ; same, but equal is equality test
    

    (Intersection$ x y) equals a list that contains the members of x that are also members of y. More precisely, the resulting list is the result of deleting from x those members that that are not members of y. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists.

    Intersection$ need not take exactly two arguments, though it must take at least one argument: (intersection$ x) is x, (intersection$ x y z ... :test test) is (intersection$ x (intersection$ y z ... :test test) :test test), and if :TEST is not supplied, then (intersection$ x y z ...) is (intersection$ x (intersection$ y z ...)). For the discussion below we restrict ourselves, then, to the cases (intersection$ x y) and (intersection$ x y :test test).

    The guard for a call of intersection$ (in the two cases just above) depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between intersection$ and its variants:

    (intersection-eq x lst) is equivalent to (intersection$ x lst :test 'eq);

    (intersection-equal x lst) is equivalent to (intersection$ x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function intersection-equal.

    Note that intersection-eq can take any positive number of arguments, in analogy to intersection$; indeed, (intersection-eq ...) expands to (intersection$ ... :test 'eq). However, intersection-equal is a function, not a macro, and takes exactly two arguments.

    Intersection$ is similar to the Common Lisp primitive intersection. However, Common Lisp does not specify the order of elements in the result of a call of intersection.




    acl2-sources/doc/HTML/INTERSECTION-EQ.html0000664002132200015000000000066112222333523017163 0ustar kaufmannacl2 INTERSECTION-EQ.html -- ACL2 Version 6.3

    INTERSECTION-EQ

    See intersection$.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/INTERSECTION-EQUAL.html0000664002132200015000000000066712222333523017533 0ustar kaufmannacl2 INTERSECTION-EQUAL.html -- ACL2 Version 6.3

    INTERSECTION-EQUAL

    See intersection$.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/INTERSECTION-THEORIES.html0000664002132200015000000000245212222333531020077 0ustar kaufmannacl2 INTERSECTION-THEORIES.html -- ACL2 Version 6.3

    INTERSECTION-THEORIES

    intersect two theories
    Major Section:  THEORIES
    

    Example:
    (intersection-theories (current-theory :here)
                           (theory 'arith-patch))
    
    General Form:
    (intersection-theories th1 th2)
    
    where th1 and th2 are theories (see theories). To each of the arguments there corresponds a runic theory. This function returns the intersection of those two runic theories, represented as a list and ordered chronologically.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/INTERSECTP-EQ.html0000664002132200015000000000064712222333523016741 0ustar kaufmannacl2 INTERSECTP-EQ.html -- ACL2 Version 6.3

    INTERSECTP-EQ

    See intersectp.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/INTERSECTP-EQUAL.html0000664002132200015000000000065512222333524017303 0ustar kaufmannacl2 INTERSECTP-EQUAL.html -- ACL2 Version 6.3

    INTERSECTP-EQUAL

    See intersectp.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/INTERSECTP.html0000664002132200015000000000403712222333523016433 0ustar kaufmannacl2 INTERSECTP.html -- ACL2 Version 6.3

    INTERSECTP

    test whether two lists intersect
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (set-difference$ l1 l2)
    (set-difference$ l1 l2 :test 'eql)   ; same as above (eql as equality test)
    (set-difference$ l1 l2 :test 'eq)    ; same, but eq is equality test
    (set-difference$ l1 l2 :test 'equal) ; same, but equal is equality test
    

    (Intersectp l1 l2) returns t if l1 and l2 have a member in common, else it returns nil. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists.

    The guard for a call of intersectp depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between intersectp and its variants:

    (intersectp-eq x lst) is equivalent to (intersectp x lst :test 'eq);

    (intersectp-equal x lst) is equivalent to (intersectp x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function intersectp-equal.




    acl2-sources/doc/HTML/INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS.html0000664002132200015000000002411312222333515023153 0ustar kaufmannacl2 INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS.html -- ACL2 Version 6.3

    INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS

    the mechanics of interaction with the theorem prover
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    ACL2 is implemented in Common Lisp. There are many different Common Lisps and they differ in details relating to interacting with the system. We sometimes refer to the host Common Lisp as ``raw Lisp.'' The new user is advised not to operate in raw Lisp as it is possible to, say, redefine ACL2 system faclities like defthm.

    Most people use Emacs (see Emacs ) or the ACL2 Sedan (Eclipse) interface (see ACL2-Sedan ). They provide protection against certain common mistakes, e.g., trying to edit a block of input text after the operating system has buffered it up and sent it to the Lisp reader which is parsing it as you type. More on this below. In addition, the Sedan provides helpful syntax checking and a disciplined approach to the stack of lemmas generated by The Method. But the variety of interfaces to the variety of Lisps mean that there is great variation in what one must type to interact with ACL2.

    The best example is perhaps trying to interrupt a running proof. If your host Common Lisp is GCL or Allegro and you are typing directly to the Common Lisp process, then you can interrupt a computation by typing ``ctrl-c'' (hold down the Control key and hit the ``c'' key once). But other Lisps may require some other control sequence. If you are typing to an Emacs process which is running the GCL or Allegro Common Lisp process in a shell buffer, you should type ctrl-c ctrl-c. If you are running the ACL2 Sedan, you can use the Interrupt Session button on the tool bar. The environment you enter when you interrupt depends on various factors and basically you should endeavor to get back to the top level ACL2 command loop, perhaps by typing some kind of Lisp depenent ``abort'' command like A or :q, or :abort!. You can usually determine what environment you're in by paying attention to the prompt, which we discuss below.

    The ACL2 ``interactive loop'' is called LP () and is generally invoked automatically from your Common Lisp when you start up the ACL2 process. LP is just a special case of an ACL2 function called LD , which the user can call from within the ACL2 interactive loop to enter the loop recursively. New users don't have to know this except that it helps explain why some commands have the string ``-ld-'' in their names!

    ACL2 presents itself as a ``read-eval-print'' loop: you're repeatedly prompted for some type-in, which is read, evaluated, and may cause some printing. The prompt tells you something about ACL2's state. In the standard environment, the prompt is

    ACL2 !>
    
    The ``ACL2'' tells you that the symbols you use in your command are those defined in the standard ACL2 namespace (or, in the jargon of Lisp, the ``current package,'' see current-package ). You could create a new namespace (see defpkg ) and set the current package to it (see in-package ). The next part of the prompt above (``!''), the exclamation mark) tells you that before ACL2 evaluates your type-in it will check to see whether guards () are respected, i.e., whether the functions used in your command are being supplied arguments in their ``expected domains.'' If evaluation is allowed by the guards, it proceeds exactly according to the ACL2 axioms; if evaluation is not allowed, an error is signaled. ACL2 event commands check their arguments thoroughly at run-time, regardless of Lisp's notion of ``expected domains.''

    If the exclamation mark is missing from the prompt,

    ACL2 >
    
    then evaluation occurs strictly according to the ACL2 axioms, without regard for any declared guards.

    You can switch between these two prompts by typing

    ACL2 !>:set-guard-checking nil
    
    to turn guard checking off and
    ACL2 >:set-guard-checking t
    
    to turn it on. Try typing (car 7) to each prompt.

    If there is a ``p'' in the prompt,

    ACL2 p!>
    
    with or without the exclamation mark:
    ACL2 p>
    
    it means you are in :program () mode rather than :logic () mode. In :program mode, defun just defines Common Lisp programs that you can evaluation but it adds no axioms and you cannot use such defined functions in theorems or invoke defthm. :Program mode is often used to prototype a model.

    Most commands are just typical parenthesized Lisp expressions, like

    ACL2 !>(defthm rev-rev
             (implies (true-listp x)
                      (equal (rev (rev x)) x)))
    
    but some are typed as keywords followed by a certain number of arguments.

    For example, to undo the last event you may type

    ACL2 !>:u
    
    or to undo back through the introduction of rev you may type
    ACL2 !>:ubt rev
    
    The first is equivalent to evaluating (u) and the second is equivalent to evaluating (ubt 'rev). See keyword-commands . So if you see a sentence like ``to turn on the break rewrite facility, execute :brr t,'' we mean type
    ACL2 !>:brr t
    
    or equivalently
    ACL2 !>(brr t)
    

    If you see a prompt that doesn't look like those above you are probably not typing commands to the standard ACL2 read-eval-print loop! If you've somehow called LD recursively, the prompt ``gets deeper,'' e.g.,

    ACL2 !>>
    
    and you can pop out one level with :q (for ``quit'') or pop to the outermost ACL2 loop with :abort! . If you are in the outermost call of the ACL2 interactive loop and you type :q, you pop out into raw lisp. The prompt there is generally different from the ACL2 prompt but that is outside our our control and varies from Lisp to Lisp. We have arranged for many (but not all) Lisps to use a raw lisp prompt involving the string "[RAW LISP]". To get back into the ACL2 interactive loop from raw lisp, evaluate (LP).

    If you see a prompt that looks like an ACL2 prompt but has a number in front of it, e.g.,

    1 ACL2 >
    
    then you're talking to the break rewrite facility (and you are 1 level deep in the example above). Presumably at earlier time in this session you enabled that facility, with :brr t, installed a monitor on some rule, invoked the prover, entered the break, and forgot. Everything you have done (e.g., lemmas you might have proved with defthm) inside that break will be lost when you exit the break.

    Since the break rewrite facility is ``ours'' we can tell you how to exit it! To exit our breaks and return to the top-level ACL2 loop, execute :abort!.

    If you discover you've been working in a brr break, exit, turn off the break facility wih :brr nil, and redo whatever defuns and defthms you did while in that break.

    Users of the Emacs interface may occasionally type commands directly in the *shell* buffer running ACL2. This can be ``dangerous'' for two reasons. One is that if you type an event, like a defun or defthm, directly to the shell, it will not be recorded in your ``script'' buffer and you may forget it in your final script. The other is that if you attempt to edit a multi-line command on any but the most recent line, e.g., to correct the spelling of defthm below after you've typed the ``(implies (true-listp x)'' you will confuse the Lisp parser because it has already read ``(defth rev-rev''.

    ACL2 !>(defth rev-rev
             (implies (true-listp x)
    
    This usually provokes the raw Lisp to enter a low level error break from which you must abort, possibly reenter the ACL2 loop, and re-type the corrected command from scratch.

    Another common mistake when using interfaces other than the ACL2 Sedan is to type an ill-formed ACL2 expression containing dots or commas, which also often provokes a break into the raw Lisp's error handler.

    The fundamental lesson is that you should pay attention to the prompt and learn what the different prompts mean -- or use the ACL2 Sedan.

    If you have been working your way through the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/INTRODUCTION-TO-HINTS.html0000664002132200015000000001442312222333515020100 0ustar kaufmannacl2 INTRODUCTION-TO-HINTS.html -- ACL2 Version 6.3

    INTRODUCTION-TO-HINTS

    how to provide hints to the theorem prover
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    We assume you've read introduction-to-rewrite-rules-part-1, introduction-to-key-checkpoints, and introduction-to-the-database.

    You may give the theorem prover a hint that is specific to a particular subgoal generated during a proof attempt. Of course, you learn the name of the subgoal by inspecting the key checkpoints or other proof output. You are not expected to anticipate the need for hints at specific subgoals; instead, you usually deduce that a hint is required because the subgoals is not proved but you see that existing rules and context make it provable.

    The most common hint is to enable and/or disable a particular rule on some particular subgoal.

    (defthm name
            formula
            :hints (("Subgoal *1/3.2''" :in-theory (disable nth-nthcdr))))
    
    The hint above tells the rewriter that just before it begins to work on Subgoal *1/3.2'' it should switch to a local theory in which all of the rules generated from the event nth-nthcdr are disabled. That local theory remains the one in use for all descendent subgoals generated from the one named, until and unless one of those descendent subgoals has an :in-theory hint associated with it. There are many kinds of hints besides :in-theory and in general, after each subgoal name, you can give various forms of advice and list various adjustments you wish to make to the context in which the prover is operating when it begins addressing the subgoal named.

    The top-level goal is always named Goal. Thus

    (defthm name
            formula
            :hints (("Goal" ...hints1...)
                    ("Subgoal *1/3.2''" ...hints2...)))
    
    has the effect of using hints1 for the top-level goal and all of its children throughout the entire proof, except for Subgoal *1/3.2'' and its children, where hints2 is used instead.

    There are a few hints which ``take effect'' exactly on the subgoal to which they are attached and are not inherited by their descendents.

    Here is an incomplete list of some of the more common hints; we note the ones that do not pass on their effects to their descendents. We recommend that you not follow the advanced links (marked ``'') below until you have read the entire tutorial.

    See in-theory for how to enable and/or disable rules. The new theory is computed by a ``theory expression'' (see theories ) such as (disable nth-nthcdr) and typically makes adjustments such as additions or deletions to the global current theory. All the relevant new theories are computed before the proof begins. Thus, in

    (defthm name
            formula
            :hints (("Goal" :in-theory (disable rule1))
                    ("Subgoal *1/3.2''" (disable rule2))))
    
    the theory mentioned for Goal is the global current theory minus rule1, while the theory mentioned for its descendent, Subgoal *1/3.2'', is the global current theory minus rule2. In particular, if both rule1 and rule2 are enabled in the global current theory, then rule1 is enabled during the processing of Subgoal *1/3.2'' because it was not removed explicitly there.

    See use for how to force the theorem prover to take note of particular instances of particular theorems; in particular, the instances are created and added as hypotheses to the subgoal in question. The hint is not inherited by the descendents of the subgoal (since the formula being proved has been modified by the hint). If the rule you are using (with a :use hint) is an enabled rewrite rule, it might interfere with the added hypothesis -- by rewriting itself to T -- and thus often you will both :use ... and :in-theory (disable ...).

    See expand for how to tell the theorem prover to expand one or more function calls whenever encountered.

    See cases for how to force the theorem prover to do a case split to prove the subgoal under each of an exhaustive list of cases given in the hint. This hint takes action specifically at the named subgoal and is not passed down to its children.

    See induct for how to tell the theorem prover to go immediately into induction for the subgoal in question, and to use the induction scheme suggested by the hint rather than the one suggested by the terms in the subgoal itself. This hint is not inherited by its descendents.

    See hints for a complete list of all hints, and see hints-and-the-waterfall for a more thorough description of how the effects of hints at a subgoal are inherited by the descendents.

    If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/INTRODUCTION-TO-KEY-CHECKPOINTS.html0000664002132200015000000002515212222333515021414 0ustar kaufmannacl2 INTRODUCTION-TO-KEY-CHECKPOINTS.html -- ACL2 Version 6.3

    INTRODUCTION-TO-KEY-CHECKPOINTS

    What questions to ask at key checkpoints
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    We assume you've read about rewrite rules; see introduction-to-rewrite-rules-part-1.

    When a proof attempt fails, ACL2 prints some key checkpoints. These are formulas that we think you should look at. There are two kinds printed: key checkpoints before an induction, and key checkpoints under a top-level induction. (Key checkpoints under deeper inductions and checkpoints that aren't considered ``key'' may exist in the proof attempt, but ACL2 doesn't print them at the end of failed proofs because you shouldn't be distracted by them.)

    Below is a list of questions to ask yourself about the key checkpoints. Initially, we recommend just picking one key checkpoint before an induction (perhaps the simplest looking one) and asking these questions. These questions may lead you to look at other key checkpoints. As you gain more experience you'll elaborate and generalize this advice.

    (1) Do you believe this formula is a theorem? If you don't think it is, it's pointless to try to prove it! You should reconsider your top-level formula in light of the special case suggested by this key checkpoint.

    (2) Can it be simplified? Is there some combination of function symbols in it that could be eliminated or simplified by exploiting some simpler fact? By a ``simpler fact'' we mean a theorem about a few of the symbols in this formula. For an example of this see dealing-with-key-combinations-of-function-symbols. Don't think about the deep question ``how can I prove the checkpoint?'' until you've got it into its simplest form.

    (3) Is the simpler fact already in the database? If there is some simpler fact that would help clean up the checkpoint but you believe the simpler fact is already in the database, you can use :pl , :pc , :pbt , and other history commands to inspect the database; (see history ). But if you find the allegedly relevant simpler fact in the database, you must ask: why wasn't it used? There are four principal reasons:

    (3a) it is disabled -- so enable it; you'll learn how when you read the coming sections on introduction-to-the-database and introduction-to-hints.

    (3b) its left-hand side doesn't match the target -- so improve the rule by generalizing its left-hand side or prove a new rule for this situation; if you decide to remove the old rule from the database, see undo commands in history .

    (3c) it is an IFF rule but the target doesn't occur propositionally -- so see if you you can strengthen the rule to an EQUAL rule or weaken the context of the target by changing the conjecture to use the target propositionally; if you decide to remove the old rule from the database, see undo commands in history .

    (3d) the hypotheses of the rule cannot be relieved for this occurrence of the target; this can be caused by the rule's hypotheses being too strong (requiring more than they should), or by the hypotheses of the current conjecture being too weak (you forgot some key hypothesis), or by ACL2 not having the rules it needs to prove that the conjecture's hypotheses really do imply the rule's. Tools are available (:see brr ) help you figure out why the rule failed, so use them and improve the rule, or the current conjecture, or the database as appropriate.

    (4) If the simpler fact is not already known, prove it. This means you must create a new defthm event with appropriate rule-classes to store your new theorem so that it will be used. See dealing-with-key-combinations-of-function-symbols. Then you must start using The Method recursively to prove your new lemma.

    (5) Otherwise, is this formula something you'd prove by induction? If you can't simplify it, it may be because you ``need this fact to prove this fact,'' in which case, induction is the right thing to do. But first, remember that in order for a formulas to be provable by induction, it must be very general. Why must it be general? Because in an inductive proof, the main thing you have to work with is the induction hypothesis, which is an instance of the theorem you're proving. If the theorem is not general enough, you won't be able to assume an instance that will help you. ACL2 may try induction even on formulas that are not general enough. Don't assume that the formula is ripe for induction just because ACL2 found an induction to do! Before you ``approve'' a formula for induction, ask whether it is perhaps a special case of some more general theorem. See generalizing-key-checkpoints now and then come back here. If you found a generalization, you should probably be proving that formula instead of this one. So formulate the appropriate defthm and use The Method recursively to prove it.

    (6) If the formula is right for induction, did ACL2 do an induction for it? You can answer that without looking at the proof. Just see if there are any key checkpoints after induction. If not, why didn't ACL2 induct? Perhaps you told ACL2 not to induct! Perhaps no term in the conjecture suggests an appropriate induction? You could remedy this by extending ACL2's induction analysis by adding to the database. Or you could just tell ACL2 what induction to do for this formula. You'll learn about both later (when you read coming sections of the tutorial).

    (7) If ACL2 did do an induction, was it the right one? You can find the induction scheme used by reading the first induction message in the output log after you submitted the conjecture to ACL2. But most often you will realize the ``wrong'' induction was done just by looking at the post-induction key checkpoints, keeping in mind that each is supposed to be a natural special case of the theorem you're proving. Is the case analysis inappropriate? Are induction hypotheses missing? If so, you should look at the induction scheme. If you determine the wrong induction was done, extend ACL2's induction analysis or tell it which induction to do, which you'll learn about in the coming sections of the tutorial. For more advice about looking at post-induction key checkpoints, see post-induction-key-checkpoints now and then come back here.

    (8) If the post-induction key checkpoints seems plausible, then repeat the questions above for each one of them, perhaps starting with the simplest.

    In any case, after successfully taking whatever action you've decided on, e.g., proving some new lemma and adding it as a rule:

    Start over trying to prove your main conjecture. This is important! Do not just scroll back to the key checkpoints generated the last time you tried to prove it. Instead, re-generate them in the context of your new, improved database and hints.

    You will be following this general outline almost all of the time that you're interacting with ACL2. You will not often be asking ``Why is ACL2 making me think about this subgoal? What did ACL2 do to get here? How does ACL2 work?''

    Two other ideas are helpful to keep in mind.

    Is a key checkpoint unexpectedly complicated? Pay special attention to the case where the complication seems to be the introduction of low-level details you thought you'd dealt with or by the introduction of symbols you didn't expect to see in this proof. These can be signs that you ought to disable some rules in the database (e.g., a definition of a complicated function about which you've proved all the necessary lemmas or some lemma that transforms the problem as was appropriate for some other proof).

    Does the theorem prover just hang up, printing nothing? If this happens, you must interrupt it. How you interrupt the prover is dependent on which Common Lisp and which interface you're using. But most Common Lisps treat control-c as a console interrupt. If you're in Emacs running ACL2 as a shell process, you must type control-c control-c. If you're in ACL2s, hit the Interrupt Session button. Interrupting ACL2 can leave you in an interactive loop similar in appearance but different from ACL2's top-level! So pay careful attention to the prompt and see breaks .

    Once you've regained control from the ``runaway'' theorem prover, there are several tools you can use to find out what it is doing in real-time. Generally speaking, when the theorem prover goes silent for a very long time it is either in some kind of rewrite loop caused by rules that cause it to flip back and forth between various supposedly normal forms, or else it has split the problem into a huge number of cases and suffering a combinatoric explosion. See DMR and, perhaps, see accumulated-persistence .

    If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/INTRODUCTION-TO-REWRITE-RULES-PART-1.html0000664002132200015000000002304412222333515022125 0ustar kaufmannacl2 INTRODUCTION-TO-REWRITE-RULES-PART-1.html -- ACL2 Version 6.3

    INTRODUCTION-TO-REWRITE-RULES-PART-1

    introduction to ACL2's notion of rewrite rules
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Rewrite rules make ACL2 replace one term by another. This is done by the rewriter, which is part of ACL2's simplifier. The rewriter sweeps through the goal formula trying all the rewrite rules it knows. Here's an example. Just pretend that you have made a rewrite rule from the formula below.

    (implies (and (natp i)
                  (< i (len a)))
             (equal (put i v (append a b))
                    (append (put i v a) b)))
    
    Then every time the rewriter sees a target term that matches
    (put i v (append a b))
    
    it considers the rule, with the variables i, v, a, and b of the rule bound to whatever terms matched them in the target, say the terms i, v, a, and b. To consider the rule, the rewriter first tries to establish (``relieve'') the hypotheses. In particular, it rewrites:
    (natp i)           ; hyp 1
    
    and
    (< i (len a)). ; hyp 2
    
    If both hyptheses rewrite to true, then the rule fires and replaces the target by:
    (append (put i v a) b).
    
    In short, rewrite rules direct ACL2 to rearrange the terms in the goal formulas.

    We are more precise later, but for now we turn to the question of how do you make a rewrite rule from a formula? The answer is, you prove the formula with the defthm command. Recall that

    (defthm name
            formula
            ...)
    
    commands ACL2 to try to prove formula and, if successful, build it into the database as a rule according to your specification in the rule-classes argument of the ... part of the command.

    To make it easy for you to generate rewrite rules, defthm has a simple heuristic: if you don't tell it what kind of rule to generate from formula, it generates a rewrite rule! Thus, if this command

    (defthm name
            formula)
    
    is successful, ACL2 will have a new rewrite rule in the database, even though you did not explicitly tell it to add a rule.

    A common mistake for the new users is to forget that the above command adds a rewrite rule. This often results in a tangle of rules that lead nowhere or to infinite rewriting that you will have to interrupt. It is also good to remember that the command only adds a rule. It does not magically make ACL2 aware of all the mathematical consequences of the formula: it just makes a rewrite rule.

    When you prove a theorem with defthm you are programming ACL2. Being careless in your statement of lemmas is tantamount to being careless in your programming.

    ACL2 can generate rewrite rules from formulas that look like this:

    (IMPLIES (AND hyp1 ... hypk)
             (eqv lhs rhs))
    
    where eqv is either EQUAL or IFF, and lhs is not a variable symbol, not a constant, and not a call of the function IF, and not a call of an abbreviation (``macro'') that expands to any of these. So illegal lhs include X, 0, (IF X Y Y), and (OR p q). The last is illegal because OR is just an abbreviation for a certain IF-expression.

    Technical Note: This tutorial introduction to the theorem prover takes liberties with the truth! We are trying to give you a useful predictive model of the system without burdening you with all the details, which are discussed in the reference manual. For example, using directives in the rule-classes you can rearrange the proved formula into the form you want your rule to take, and you can make ACL2 take advantage of equivalence relations eqv other than just EQUAL and IFF. But we'll ignore these fine points for now.

    We call the hyp terms the hypotheses of the rule. We call lhs the left-hand side of the rule, and we call rhs the right-hand side of the rule. If the conclusion of the rule is an EQUAL term we call it an equality rule. Otherwise, it is a propositional equivalence rule. If there are no hypotheses, k=0, we say the rule is an unconditional rewrite rule; otherwise it is conditional.

    ACL2 allows several special cases of the shapes above. See special-cases-for-rewrite-rules, but come back here and continue.

    A rewrite rule makes ACL2 seek out occurrences of terms that match the left-hand side of the rule and replace those occurrences using the right-hand side, provided all the hypotheses rewrite to true in the context of the application of the rule.

    That is, the left-hand side is treated as a pattern that triggers the rule. The hypotheses are conditions that have to be proved in order for the rule to fire. The right-hand side is the replacement and is put into the formula where the pattern occurred.

    Now for some clarifications. ACL2 only considers enabled rules. And ACL2 will use a propositional rule to replace a target only if the target occurs in a propositional place in the formula. Generally that means it occurs in the argument of a propositional connective, like AND, OR, NOT, IMPLIES, and IFF, or in the test of an IF. When we say that the left-hand side of the rule must match the target we mean that we can instantiate the variables in the rule to make the left-hand side identical to the target. To relieve or establish the hypotheses of the rule, ACL2 just applies other rewrite rules to try to prove the instantiated hypotheses from the assumptions governing the occurrence of the target. When ACL2 replaces the target, it replaces it with the instantiated right-hand side of the rule and then applies rewrite rules to that.

    If a hypothesis has variables that do not occur in the left-hand side of the rule, then the pattern matching process won't find values for those variables. We call those free variables. They must be instantiated before ACL2 can relieve that hypothesis. To instantiate them, ACL2 has to guess values that would make the hypothesis true in this context, i.e., true given the assumptions of the goal theorem. So if you're trying to prove

    (IMPLIES (AND (TRUE-LISTP A)
                  (MEMBER (CAR P) A)
                  (MEMBER (CDR P) A))
             ...)
    
    and the target you're rewriting is in the ``...'' part of the formula, the rewriter knows that (TRUE-LISTP A) (MEMBER (CAR P) A) and (MEMBER (CDR P) A) are true. So if a rewrite rule is considered and the rule has (member e x) as a hypothesis, where e is a free variable but x was bound to A in the pattern matching, then it will guess that e must be (CAR P) or (CDR P), even though there are many other possibilities that would make (MEMBER e A) true. Of course, whatever guess it makes must also satisfy all the other hypotheses that mention e in the rule. It simply isn't very imaginative at guessing!

    The most predictable rewrite rules have no free variables. You can add pragmatic advice to help ACL2 with free variables, telling it to try all the possibilities it finds, to try just the first, or even to compute a ``creative'' guess.

    It is possible to make the rewriting process loop forever, e.g., by rewriting alpha to beta with one set of rules and rewriting beta to alpha with another. Even a single rule can make the process loop; we'll show you an example of that later in the tutorial. ACL2 can handle commutativity rules without looping. It uses (equal (+ x y) (+ y x)) to replace (+ B A) by (+ A B), but not vice versa. (It is sensitive to alphabetic ordering when dealing with permutative rules.)

    Logically equivalent formulas can generate radically different rewrite rules! Rearranging the propositional structure of the formula or swapping the left and right sides of an equality -- while having no effect on the mathematical meaning of a formula -- can have a drastic impact on the pragmatic meaning as a rule. To see an illustration of this, see equivalent-formulas-different-rewrite-rules.

    Developing an effective set of rewrite rules is key to success at using ACL2. We'll look more at this later in the tutorial.

    If you are working your way through the tutorial for the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover. If you are reading just about how to make effective rewrite rules, go on to introduction-to-rewrite-rules-part-2.




    acl2-sources/doc/HTML/INTRODUCTION-TO-REWRITE-RULES-PART-2.html0000664002132200015000000003546512222333515022140 0ustar kaufmannacl2 INTRODUCTION-TO-REWRITE-RULES-PART-2.html -- ACL2 Version 6.3

    INTRODUCTION-TO-REWRITE-RULES-PART-2

    how to arrange rewrite rules
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    You should design your rewrite rules to canonicalize the terms in your problem, that is, your rules should drive terms into some normal form so that different but equivalent terms are rewritten into the preferred shape, making equivalent terms identical. You are very familiar with this idea from algebra, where you learned to normalize polynomials. Thus, when you see (2x + 6)(3x - 9) you automaticaly normalize it, by ``multiplying out and collecting like terms,'' to get (6x^2 - 54). This normalization strategy allows you to recognize equivalent terms presented differently, such as 6(x^2 - 9).

    The ACL2 user is responsible for making up the rules. (Standard ``books'' -- files of ACL2 definitions and theorems -- can often provide rules for some sets of functions, e.g., arithmetic.) This is a heavy burden on you but it means you are in charge of your own normal forms. For example, if you use the function nthcdr, which returns the nth cdr of a list, you might see both (cdr (nthcdr i x)) and (nthcdr i (cdr x)). These two expressions are equivalent but not identical. You will want to decide which you want to see and prove the rewrite rule that converts the other to that preferred form.

    Most good users develop an implicit ordering on terms and rewrite ``heavy'' terms to ``lighter'' ones. This insures that there are no loops in their rewrite rules. But this ordering changes according to the user and the problem.

    Generally, the lightest terms are primitives such as IF, EQUAL, arithmetic, etc. Functions defined without explicit recursion tend to be ignored because they are just expanded away (but see below). Recursively defined functions tend to be heavier than any other recursive function used in their definitions, so, for example, if rev is defined in terms of append, rev is heavier than append. But the size and subtlety of recursively defined functions also affects their place in the ordering.

    But rewrite rules are surprisingly subtle. Recall that a rewrite rule can be made from a formula of this form:

    (IMPLIES (AND hyp1 ... hypk)
             (eqv lhs rhs))
    
    where eqv is either EQUAL or IFF, and lhs is a call of a function other than IF. In such a rule, lhs is the pattern responsible for triggering the rule, the hypi are conditions which must be satisfied by the context of the target being rewritten, and rhs is the replacement. The replacement only happens if the rule is enabled, the pattern matches, the conditions are satisfied, and (in the case of an IFF rule) the target occurs propositionally. There are other heuristic restrictions that we won't discuss here.

    So how should you phrase a theorem in order to make it an effective rule?

    General Principles:

    * Strengthen the Formula: The fewer hypotheses a formula has the better the rewrite rule formed from it. The more general the left-hand side the better the rule. The fewer free variables in the hypothesis, the better. The idea is to form a rule that fires predictably. Later in this tutorial you'll get some practice formulating strong rules.

    * Choosing the Conclusion: If a lemma is an implication, you have to choose what the conclusion will be. (You'll also have to ``orient'' that conclusion by choosing a left-hand side and a right-hand side, but we discuss that below). You can swap the conclusion and any hypothesis by negating both, producing a different conclusion. There are generally two (possibly conflicting) heuristics for deciding which part of the formula should be the conclusion:

    Choosing the Conclusion Heuristic 1: Can you make the conclusion be an EQUAL or IFF expression that has a ``heavy term'' on one side? That will make a rule that replaces the heavy term with a lighter one. We discuss this situation more below.

    Choosing the Conclusion Heuristic 2: Can you make the conclusion be a non-propositional term that contains all the variables mentioned in the hypotheses? By ``non-propositional'' we mean a term that is not just the propositional combination (e.g., with AND or OR) of other terms but instead some call of some ``heavy'' function? If your conclusion contains all the variables mentioned in the hypotheses, matching it will instantiate all the variables in the hypotheses. That way ACL2 will not have to guess instantiations of unbound variables when it tries to relieve the hypotheses. It is not very good at guessing.

    * Orienting the Conclusion: If the conclusion is an EQUAL or an IFF, you have to decide which is the left-hand side and which is the right. If the conclusion is (NOT lhs), then the left-hand side is lhs and the right-hand side is NIL. If the conclusion is not an EQUAL, an IFF, or a NOT then the conclusion itself will be the left-hand side and the right-hand side will be T. If your lemma was created by looking a Key Checkpoints while using The Method, the left-hand side should match some term in that checkpoint.

    Remember, the left-hand side is the ``trigger'' that will make the rule fire. It is the pattern that ACL2 will be looking for.

    * Pay attention to the free variables: Look at the variables that occur in the pattern (the left-hand side) and compare them to the variables that occur in the hypotheses. Does some hypothesis contain a variable, say v, that is not in the pattern? We call v a free variable because it will not be assigned a value (``bound'') by the process of pattern matching. ACL2 will have to guess a value for v. If some hypothesis contains v as a free variable, ask whether more than one hypothesis contains v? ACL2 uses the first hypothesis containing a free v to guide its guess for v. To ``guess'' a value for v, ACL2 uses that hypothesis as a pattern and tries to match it against the assumptions in the checkpoint formula being proved. This means that key hypothesis must be in normal form, to match the rewritten assumptions of the goal. It also means that you should reorder the hypotheses to put the most unusual hypothesis containing a free v first in the list of conjuncts. For example, if v is free in two hypotheses, (natp v) and (member (nthcdr v a) b), then we recommend putting the member term first. There are likely to be many terms in the goal satisfying the natp hypothesis -- or none if natp has expanded to an integer inequality -- while there are likely to be few terms satisfying the member hypothesis, especially if a and b are bound by the left-hand side of the rule.

    Here are some (possibly conflicting) heuristics for choosing the left-hand side:

    Choose a Left-Hand Side that Occurs in a Key Checkpoint: If you use the Method you will tend to do this anyway, because you'll see terms in the Key Checkpoints that you want to get rid of. But many moderately experienced users ``look ahead'' to how the proof will go and formulate a few anticipatory rules with the idea of guiding ACL2 down the preferred path to the proof. When you do that, you risk choosing left-hand sides that won't actually arise in the problem. So when you formulate anticipatory rules, pay special attention to the functions and terms you put in the left-hand sides. The next few paragraphs deal with specific cases.

    Avoid Non-Recursive Functions in the Left-Hand Side: If the left-hand side contains a call of a defined function whose definition is not recursive, then it will almost never match any target in the formula being rewritten unless the function is disabled. Suppose for example you have defined SQ so that (SQ x) is (* x x). Suppose you considered choosing a left-hand side like (+ (SQ x) (SQ y)). Suppose you hoped it would hit the target (+ (SQ A) (SQ B)) in some formula. But when ACL2 simplifies the formula, it will first rewrite that target to

    (+ (* A A) (* B B))
    
    by expanding the definition of SQ, since it could do so without introducing any recursive calls. But now the target won't match your rule. By choosing a left-hand side that occurs in a Key Checkpoint (and is not one of a handful of abbreviations ACL2 uses in its output like AND, NOT), you'll avoid this problem since SQ will have already been expanded before the Key Checkpoint is printed.

    Disable Non-Recursive Functions: If you insist on a left-hand side that contains calls of non-recursive functions, remember to disable those non-recursive functions after you've proved all the rules you want about them. By disabling SQ you can prevent ACL2 from expanding the definition as it did above. Sometimes you will define a function non-recursively to formalize some concept that is common in your application and you will want to create a sort of algebra of rules about the function. By all means do so, so you can conduct your formal reasoning in terms of the concepts you're informally manipulating. But after proving the required laws, disable the non-recursive concept so that ACL2 just uses your laws and not the messy definition.

    Choose a Left-Hand Side Already in Simplest Form: This is a generalization of the advice above. If any rule will rewrite your left-hand side, it will prevent your rule from matching any target. For example, if you write a left-hand side like (foo (car (cons x y))) then it would never match any target! The reason is that even if (FOO (CAR (CONS A B))) did occur in some goal formula, before ACL2 would try your rule about foo it will use the obvious rule about CAR and CONS to transform your imagined target to (FOO A). Thus, your rule would not match. So you have to keep in mind all your other rules when you choose a left-hand side (and when you choose the hypotheses to guide free variable selection). If you always choose a pattern that matches a term in a Key Checkpoint, you avoid this problem.

    Make Sure the Left-Hand Side is ``Heavier'' than the Right: Sometimes this is obvious, as when you choose (REV (REV x)) for the left-hand side and x for the right. But what do you about (REV (APPEND x y)) versus (APPEND (REV y) (REV x))? Most of the time we choose to drive the heaviest function (in this case REV) down toward the variables, lifting the lighter function (APPEND) up so that we can reason about the lighter function's interaction with the surrounding ``matrix'' of the formula. So we'd rewrite (REV (APPEND x y)) to (APPEND (REV y) (REV x)), not vice versa.

    Alternative Ways to Talk About the Same Thing: If your problem and specification use two different ways to talk about the same thing, choose one form and rewrite the other into that form. For example, the ACL2 built-in nth returns the nth element of a list, and the built-in function nthcdr returns the nth cdr of a list. They are defined independently. But (nth n x) is the same thing as (car (nthcdr n x)). Since nth can be expressed in terms of nthcdr but not vice versa, it is clear we should prove (equal (nth n x) (car (nthcdr n x))) as a rewrite rule if both nth and nthcdr are involved in the problem.

    Don't Let Computational Efficiency Dictate the Terms: If you have two functions that are equivalent (perhaps one was defined to be computationally more efficient), prove their equivalence as a rewrite rule that eliminates the more complicated function. An extreme example would be a model that uses a sophisticated data structure (like a balanced binary tree, red-black tree, ordered array, or hash table) to implement something simple like an association of keys to values. By proving the equivalence as stated you can eliminate the messy function early and do the bulk of your reasoning in terms of its simple specification.

    The best ACL2 users become very good at keeping all these things in mind when designing their rewrite rules. Practice makes perfect. Don't be afraid during your learning of ACL2 to undo the rules you first invented and try to make better ones.

    Finally, be patient! There will be times when you think to yourself ``Why should I spend my time thinking of rules that guide ACL2? I know the proof!'' There are two reasons. First, you may ``know'' the proof but you may well be wrong and part-way through this whole exercise you may realize that you're missing a major hypothesis or special case that breaks your whole conception of the problem. The proof is in the details. Second, most of the time the library of rules you develop in this process will be used over and over again on variants of the main problem in the months and years ahead. This is sometimes called the proof maintenance problem. Theorems don't suffer bit rot! But the artifacts you're modeling change and you will need to prove new versions of old theorems. A good general purpose library makes this much easier.

    We now recommend that you practice inventing strong rules; see strong-rewrite-rules.

    For advice on handling specific kinds of formulas and definitions, see specific-kinds-of-formulas-as-rewrite-rules.

    For more information about the rewriter works and how rules are created, see further-information-on-rewriting.

    If you are working your way through the tutorial introduction to the theorem prover, use your browser's Back Button to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/INTRODUCTION-TO-THE-DATABASE.html0000664002132200015000000003406312222333515020777 0ustar kaufmannacl2 INTRODUCTION-TO-THE-DATABASE.html -- ACL2 Version 6.3

    INTRODUCTION-TO-THE-DATABASE

    how to update the database
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    We assume you've read introduction-to-rewrite-rules-part-1 and introduction-to-key-checkpoints.

    The theorem prover's heuristics are influenced by the database of rules and the enabled/disabled status of the rules. You can think of the database as a global hint, potentially affecting all parts of a proof attempt.

    However, in addition to the ``global hint,'' it is possible to give local hints that affect the theorem prover's behavior on specific subgoals. We discuss the database here and discuss local hints later in the tutorial.

    The theorem prover's ``database'' is called the ACL2 world. You change the world by issuing commands called events. The most common events are defun for defining new functions (and predicates) and defthm for proving new theorems. Both add rules to the database. Here are some commonly used events.

    We recommend that upon the first reading of this tutorial you do not follow the links shown below! The links take you into the hypertext reference manual, where it is easy to get lost unless you're looking for detail about one specific feature.

    See defun to define a new function or predicate symbol. Definitional axioms are just a kind of rewrite rule, but defun may also add rules affecting the determination of the type of a term and rules affecting the induction analysis. When you issue a defun command you will always supply the name of the function or predicate, the list of formal parameters, v1,...vn, and the body:

    (defun name (v1 ... vn)
       body)
    
    If the event is accepted, a definitional axiom is added to the world, (name v1...vn)=body, stored as a special kind of unconditional rewrite rule. However, the defun event may require theorems to be proved. Specifically, measure theorems must be proved to establish that recursively defined functions always terminate, by proving that an ordinal measure of the formal parameters decreases in a well-founded way in every recursive call. In addition, if guards are being used to declare the expected domain of the newly defined function, guard theorems might be proved to establish that all functions stay within their expected domains. In any case, you may provide additional information to the defun event, coded as part of the declaration that Common Lisp allows:
    (defun name (v1 ... vn)
       (declare (xargs ...))
       body)
    
    The xargs (``extra arguments to defun'') entry may specify, among other things, the measure to use in the termination proof, hints for use by the prover during the termination proof, the guard of the new function, and hints for use by the prover during the guard verification step.

    See defthm to prove a theorem and to add it as a rule of one or more specified rule-classes. When you issue a defthm command you always specify a name for the theorem you're trying to prove and a formula stating the theorem. You may optionally supply some local hints as we describe later in the tutorial. You may also optionally supply some rule classes indicating how you want your formula stored as a rule, after it is proved. We discuss the defthm rule classes below.

    See in-theory to enable or disable rules. Rules have names derived from the names you give to functions and theorems, e.g., (:REWRITE LEFT-IDENTITY-OF-FOO . 2) for the second rewrite rule you created from the theorem named LEFT-IDENTITY-OF-FOO. Rule names are called runes. A theory is just a set (list) of runes. The current theory is the list of enabled runes and the in-theory event can add runes to or delete runes from the current theory.

    See include-book to change the world by loading a certified file of other events. The most common use of include-book is to load ``community books'' -- books written by other ACL2 users who have released them for distribution to the community. The most common books loaded are probably the arithmetic books:

    ; * for the most elementary arithmetic, needed for any problem
    ;   that involves even simple addition and multiplication like
    ;   (+ x (* 2 y) -3):
    
        (include-book "arithmetic/top-with-meta" :dir :system)
    
    ; * for more complicated arithmetic involving non-linear terms like
    ;   (* x y), (expt x (+ i j)), and floor and mod
    
        (include-book "arithmetic-5/top" :dir :system)
    
    But for a complete list of system books, see books .

    See certify-book to certify a file of events for reuse later.

    See defconst to define a new constant, allowing you to write a symbol, e.g., *weekdays* in place of some object, e.g., '(MON TUE WED THU FRI) in formulas.

    See defmacro to define a new syntactic abbreviation. The macro facility in Lisp is quite powerful, allowing you to compute the form to which some type-in expands. For example, the primitive macro COND is defined so that (COND ((P X) 1)((Q X) 2)(T 3)) expands to (IF (P X) 1 (IF (Q X) 2 3)).

    See defstobj to introduce a single-threaded object that your functions may modify ``destructively'' provided they follow strict syntactic rules.

    See events for a complete list of the ACL2 events. There are events to allow mutually recursive definitions, to introduce some new function symbols constrained to satisfy given axioms, to allow the temporary introduction of a ``local'' event to help prove some goal theorem and then disappear, to provide the power of first-order quantification and a choice operator, and many other features.

    There are also commands that allow you to inspect the world, e.g., to print the command that introduced a given name, to show all the commands back to a certain one, undo the last command or more generally roll-back to an earlier command. See history .

    The Defthm Rule-Classes

    We've already discussed the key role that rules play in controlling the behavior of the system. New rules are introduced primiarily with the defthm event, though defun and other events may introduce rules.

    To prove formula and generate, say a :rewrite rule and a :generalize rule from it, you would write

    (defthm name
            formula
            :rule-classes (:rewrite :generalize))
    
    If you wanted to rearrange the shape of the formula before generating the :rewrite rule you could provide a :corollary modifier to the :rewrite rule class:
    (defthm name
            formula
            :rule-classes ((:rewrite :corollary ...)
                           :generalize)).
    

    There are many classes of rules, affecting different parts of the system. Each class is denoted by a keyword, e.g., :REWRITE, :LINEAR, etc. You are responsible for specifying the class(es) of rules to be generated from a given formula and several different rules (possibly of different classes) may be derived from a single formula. Each class admits optional modifiers that allow you finer control over each rule. Each class admits the :corollary modifier with which you can rearrange the formula before a rule of that class is generated. This allows you to state a theorem in its most elegant form for publication purposes but store it as a rule with the most appropriate hypotheses and conclusion. Other modifiers tend to be specific to certain rule classes, but for example, :rewrite rule modifiers include an optional limit on the depth of backchaining and options for handling free variables.

    We give some links below to other classes of rules. However, we recommend that you not follow these links upon your first reading of this tutorial!

    See REWRITE for a description of how to create a rewrite rule.

    See LINEAR for a description of how to store theorems concluding with arithmetic inequalities. The trouble with storing

    (<= (len (delete e x)) (len x))
    
    as a rewrite rule is that it only matches instances of that inequality and thus fails to match
    (<= (LEN (DELETE E X)) (+ 1 (LEN X)))
    
    ACL2 contains an extensible linear arithmetic decision procedure and by storing inequalities as :linear rules you can make that decision procedure aware of the basic inequalities between non-primitive numerically valued terms.

    See EQUIVALENCE , see CONGRUENCE , and see REFINEMENT to learn how to introduce a new equivalence relation to the rewriter. For example, suppose you define set-equal so that it returns t precisely if its two arguments are lists containing the same elements, regardless of order or number of occurrences. Note that under this sense of ``equivalence'', (rev x) is the identity function and append is commutative, for example.

    (set-equal (rev x) x)
    
    (set-equal (append x y) (append y x))
    
    You can make ACL2 use these two theorems as :rewrite rules to replace instances of (REV x) and (APPEND x y) by set-equal terms, even though the results are not actually EQUAL. This is possible provided the target occurs in a context admitting set-equal as a congruence relation. For example, the :congruence rule:
    (implies (set-equal a b)
             (iff (member e a)
                  (member e b)))
    
    gives the rewriter permission to use the above set-equal rules as rewrite rules in the second argument of any member expression being used in a propositional way.

    See ELIM for a description of how to make the system adopt a ``change of variable'' tactic that can trade in destructor functions for constructor functions. In analogy with how ACL2 eliminates (CAR X) and (CDR X) by replacing X with (CONS A B), you can make it eliminate other destructors. For example, the community book "arithmetic-5/top" provides an elim rule that eliminates (floor x y) and (mod x y) by replacing x by (+ r (* y q)), so that the floor expression becomes q and the mod expression becomes r. When introducing your own elim rules you will probably also need to introduce generalize rules (see below) so that the new variables are appropriately constrained.

    See GENERALIZE for a description of how you can make ACL2 restrict the new variables it introduces when generalizing. ACL2 will sometimes replace a term by a new variable and with generalize rules you can insure that the new variable symbol has certain properties of the term it replaces.

    See INDUCTION for a description of how to tailor the inductions suggested by a term. Most of the time when ACL2 chooses the ``wrong'' induction, the easiest fix is with a local :induct hint (see below). But if the same problem arises repeatedly in several theorems, you might want to ``educate'' ACL2's induction heuristic.

    For a complete list of rule-classes, See rule-classes .

    If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/INTRODUCTION-TO-THE-TAU-SYSTEM.html0000664002132200015000000003433712222333520021306 0ustar kaufmannacl2 INTRODUCTION-TO-THE-TAU-SYSTEM.html -- ACL2 Version 6.3

    INTRODUCTION-TO-THE-TAU-SYSTEM

    a decision procedure for runtime types
    Major Section:  ACL2 Documentation
    

    This doc topic is the main source of information about the tau system and discusses the general idea behind the procedure and how to exploit it.

    A ``Type-Checker'' for an Untyped Language

    Because ACL2 is an untyped language it is impossible to type check it. All functions are total. An n-ary function may be applied to any combination of n ACL2 objects. The syntax of ACL2 stipulates that (fn a1...an) is a well-formed term if fn is a function symbol of n arguments and the ai are well-formed terms. No mention is made of the ``types'' of terms. That is what is meant by saying ACL2 is an untyped language.

    Nevertheless, the system provides a variety of monadic Boolean function symbols, like natp, integerp, alistp, etc., that recognize different ``types'' of objects at runtime. Users typically define many more such recognizers for domain-specific ``types.'' Because of the prevalence of such ``types,'' ACL2 must frequently reason about the inclusion of one ``type'' in another. It must also reason about the consequences of functions being defined so as to produce objects of certain ``types'' when given arguments of certain other ``types.''

    Because the word ``type'' in computer science tends to imply syntactic or semantic restrictions on functions, we avoid using that word henceforth. Instead, we just reason about monadic Boolean predicates. You may wish to think of ``tau'' as synonymous with ``type'' but without any suggestion of syntactic or semantic restrictions.

    Some Related Topics

    Design Philosophy

    The following basic principles were kept in mind when developing tau checker and may help you exploit it.

    (1) The tau system is supposed to be a lightweight, fast, and helpful decision procedure for an elementary subset of the logic focused on monadic predicates and function signatures.

    (2) Most subgoals produced by the theorem prover are not in any decidable subset of the logic! Thus, decision procedures fail to prove the vast majority of the formulas they see and will be net time-sinks if tried too often no matter how fast they are.

    Tau reasoning is used by the prover as part of preprocess-clause, one of the first proof techniques the system tries. The tau system filters out ``obvious'' subgoals. The tau system is only tried when subgoals first enter the waterfall and when they are stable under simplification.

    (3) The tau system is ``benign'' in the sense that the only way it contributes to a proof is to eliminate (prove!) subgoals. It does not rewrite, simplify, or change formulas. Tau reasoning is not used by the rewriter. The tau system either eliminates a subgoal by proving it or leaves it unchanged.

    (4) It is impossible to infer automatically the relations between arbitrary recursively defined predicates and functions. Thus, the tau system's knowledge of tau relationships and function signatures is gleaned from theorems stated by the user and proved by the system.

    (5) Users wishing to build effective ``type-checkers'' for their models must learn how rules affect the tau system's behavior. There are two main forms of tau rules: those that reveal inclusion/exclusion relations between named tau predicates, e.g., that 16-bit naturals are also 32-bit naturals,

    (implies (n16p x) (n32p x)),
    
    and signatures for all relevant functions, e.g., writing a 32-bit natural to a legal slot in a register file produces a register file:
    (implies (and (natp n)
                  (< n 16)
                  (n32p val)
                  (register-filep regs))
             (register-filep (update-nth n val regs))).
    
    For a complete description of acceptable forms see :tau-system.

    (6) The tau system is ``greedy'' in its efforts to augment its database. Its database is potentially augmented when rules of any :rule-class (see :rule-classes) are proved. For example, if you make a :rewrite or :type-prescription rule which expresses a relationship between one tau and another (e.g., that (P x) implies (Q x)), ACL2 will build it into the tau database. The rule-class :tau-system can be used to add a rule to the tau database without adding any other kind of rule.

    (7) Greediness is forced into the design by benignity: the tau system may ``know'' some fact that the rewriter does not, and because tau reasoning is not used in rewriting, that missing fact must be conveyed to the rewriter through some other class of rule, e.g., a :rewrite or :type-prescription or :forward-chaining rule. By making the tau system greedy, we allow the user to program the rewriter and the tau system simultaneously while keeping them separate. However, this means you must keep in mind the effects of a rule on both the rewriter and the tau system and use :tau-system rules explicitly when you want to ``talk'' just to the tau system.

    (8) Tau rules are built into the database with as much preprocessing as possible (e.g., the system transitively closes inclusion/exclusion relationships at rule-storage time) so the checker can be fast.

    (9) For speed, tau does not track dependencies and is not sensitive to the enabled/disabled status (see enable and disable) of rules. Once a fact has been built into the tau database, the only way to prevent that fact from being used is by disabling the entire tau system, by disabling (:executable-counterpart tau-system). If any tau reasoning is used in a proof, the rune (:executable-counterpart tau-system) is reported in the summary. For a complete list of all the runes in the tau database, evaluate (global-val 'tau-runes (w state)). Any of these associated theorems could have been used.

    These design criteria are not always achieved! For example, the tau system's ``greediness'' can be turned off (see set-tau-auto-mode), the tau database can be regenerated from scratch to ignore disabled rules (see regenerate-tau-database), and disabling the executable-counterpart of a tau predicate symbol will prevent the tau system from trying to run the predicate on constants. The tau system's benignity can be frustrating since it might ``know'' something the rewriter does not. More problematically, the tau system is not always ``fast'' and not always ``benign!'' The typical way tau reasoning can slow a proof down is by evaulating expensive tau predicates on constants. The typical way tau reasoning can hurt a previously successful proof is by proving some subgoals (!) and thus causing the remaining subgoals to have different clause-identifiers, thus making explicit hints no longer applicable. We deal with such problems in dealing-with-tau-problems.

    Technical Details

    The tau system consists of both a database and an algorithm for using the database. The database contains theorems that match certain schemas allowing them to be stored in the tau database. Roughly speaking the schemas encode ``inclusion'' and ``exclusion'' relations, e.g., that natp implies integerp and that integerp implies not consp, and they encode ``signatures'' of functions, e.g., theorems that relate the output of a function to the input, provided only tau predicates are involved.

    By ``tau predicates'' we mean the application of a monadic Boolean-valued function symbol, the equality of something to a quoted constant, an arithmetic ordering relation between something and a rational constant, or the logical negation of such a term. Here are some examples of tau predicates:

    (natp i)
    (not (consp x))
    (equal y 'MONDAY)
    (not (eql 23 k))
    (< 8 max)
    (<= max 24)
    
    Synonyms for equal include =, eq, and eql. Note that negated equalites are also allowed. The arithmetic ordering relations that may be used are <, <=, >=, and >. One of the arguments to every arithmetic ordering relation must be an integer or rational constant for the term to be treated as a tau predicate.

    A ``tau'' is a data object representing a set of signed (positive or negative) tau predicates whose meaning is the conjunction of the literals in the set.

    When we say that a term ``has'' a given tau we mean the term satisfies all of the recognizers in that tau.

    The tau algorithm is a decision procedure for the logical theory described (only) by the rules in the database. The algorithm takes a term and a list of assumptions mapping subterms (typically variable symbols) to tau, and returns the tau of the given term.

    When the system is called upon to decide whether a term satisfies a given monadic predicate, it computes the tau of the term and asks whether the predicate is in that set. More generally, to determine if a term satisfies a tau, s, we compute a tau, r, for the term and ask whether s is a subset of r. To determine whether a constant, c, satisfies tau s we apply each of the literals in s to c. Evaluation might, of course, be time-consuming for complex user-defined predicates.

    The tau database contains rules derived from definitions and theorems stated by the user. See :tau-system for a description of the acceptable forms of tau rules.

    To shut off the greedy augmentation of the tau database, see set-tau-auto-mode. This may be of use to users who wish to tightly control the rules in the tau database. To add a rule to the tau database without adding any other kind of rule, use the rule class :tau-system.

    There are some slight complexities in the design related to how we handle events with both :tau-system corollaries and corollaries of other :rule-classes, see set-tau-auto-mode.

    To prevent tau reasoning from being used, disable the :executable-counterpart of tau-system, i.e., execute

    (in-theory (disable (:executable-counterpart tau-system)))
    
    or, equivalently,
    (in-theory (disable (tau-system)))
    
    To prevent tau from being used in the proof of a particular subgoal, locally disable the :executable-counterpart of tau-system with a local :in-theory hint (see hints).

    The event command tau-status is a macro that can be used to toggle both whether tau reasoning is globally enabled and whether the tau database is augmented greedily. For example, the event

    (tau-status :system nil :auto-mode nil)
    
    prevents the tau system from being used in proofs and prevents the augmentation of the tau database by rules other than those explicitly labeled :tau-system.

    To see what the tau system ``knows'' about a given function symbol see tau-data. To see the entire tau database, see tau-database. To regenerate the tau database using only the runes listed in the current enabled theory, see regenerate-tau-database.




    acl2-sources/doc/HTML/INTRODUCTION-TO-THE-THEOREM-PROVER.html0000664002132200015000000007215312222333515021753 0ustar kaufmannacl2 INTRODUCTION-TO-THE-THEOREM-PROVER.html -- ACL2 Version 6.3

    INTRODUCTION-TO-THE-THEOREM-PROVER

    how the theorem prover works -- level 0
    Major Section:  ACL2-TUTORIAL
    

    Software is complex, and ACL2 is a piece of software that is used to analyze software -- adding another layer of complexity. Furthermore, instead of being limited to static analysis for certain fixed properties, ACL2 allows you -- indeed, forces you -- to formalize the problem and the questions. It ``knows'' nothing inherent about your problem before you start to interact with it. But it can be used to help answer the most complicated questions you can ask about software.

    All this is to say that it is not the kind of tool that you just install and then start to use effectively. So OK, you've installed it or confirmed that you can invoke it. Good for you. Now you have to learn how to use it! Your success ultimately comes down to your understanding of your problem domain and your appropriate exploitation of ACL2's strengths and avoidance of its weaknesses. So put aside the idea of sitting down and interacting with it. Instead, learn about it.

    We assume you know some of the industrial applications of ACL2. Realizing that such things can be done may sustain you during the long learning curve! We also assume you have taken both the Flying Tour and the Walking Tour. The tours give you a good overview of the whole system where this tutorial focuses on how to use the prover itself.

    If you haven't visited these links, please do so now.

    This tutorial will take you several hours -- maybe several days -- to work through. Do not breeze through it as you might a blog. Think your way through it. Remember what you read. Do not take short cuts. If you start to use ACL2 before you really know how, it will only frustrate you.

    We recommend that you read this tutorial with an HTML browser so that you can see which links you've followed and which you have not. To give you a sense of what is in store, here is a map of this document. But don't navigate through it from here! Read it linearly, following the links when the text directs you to.

     INTRODUCTION-TO-THE-THEOREM-PROVER
         INTRODUCTION-TO-REWRITE-RULES-PART-1
             SPECIAL-CASES-FOR-REWRITE-RULES
             EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES
         INTRODUCTION-TO-KEY-CHECKPOINTS
             DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS
             GENERALIZING-KEY-CHECKPOINTS
             POST-INDUCTION-KEY-CHECKPOINTS
         INTRODUCTION-TO-REWRITE-RULES-PART-2
             STRONG-REWRITE-RULES
                 PRACTICE-FORMULATING-STRONG-RULES
                     PRACTICE-FORMULATING-STRONG-RULES-1
                     PRACTICE-FORMULATING-STRONG-RULES-2
                     PRACTICE-FORMULATING-STRONG-RULES-3
                     PRACTICE-FORMULATING-STRONG-RULES-4
                     PRACTICE-FORMULATING-STRONG-RULES-5
                     PRACTICE-FORMULATING-STRONG-RULES-6
             SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES
             FURTHER-INFORMATION-ON-REWRITING
         INTRODUCTION-TO-THE-DATABASE
         INTRODUCTION-TO-HINTS
         INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS
         INTRODUCTORY-CHALLENGES
             INTRODUCTORY-CHALLENGE-PROBLEM-1
             INTRODUCTORY-CHALLENGE-PROBLEM-2
             INTRODUCTORY-CHALLENGE-PROBLEM-3
             (there are others but at least do a few)
         FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS
    

    If any of the links above are marked as ``visited'' by your browser, use your browser's tools menu to mark all links as unvisited. As you can see, we really think you'll get the most out of this document if you take it seriously. As you read, you will see some links to ``advanced'' topics. These are marked with a tiny warning sign, ``''. They lead out of this linear tutorial and into ACL2's hypertext reference manual. We recommend that you not visit any of these advanced links until you have read the entire tutorial at least once.

    After you finish this tutorial material, we recommend that you look at the ACL2 Demos, at the ``Demos'' link of the ACL2 home page, http://www.cs.utexas.edu/users/moore/acl2.

    Most users of ACL2 have bought the book

    Computer-Aided Reasoning: An Approach, Kaufmann, Manolios, and Moore, Kluwer Academic Publishers, June, 2000

    which is available in paperback from Lulu for approximately $20 (as of 2010). See http://www.lulu.com/content/1746161. That book contains hundreds of exercises in programming, proof, and using The Method described here to prove theorems. Solutions to the exercises are online. For more information about the book and a companion one (also available on Lulu) about industrial applications of ACL2, see

    http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html#Books

    Using ACL2 is akin to having a partner in the theorem proving enterprise. It will do some of the work and you will do some of the work. It can't really be any other way because theorem proving is undecidable. You bring a quirkly, error-prone, creative insight to the problem, and ACL2 brings accuracy, logic, and perserverance.

    Here we describe a ``model'' of how the system works and introduce some of the ideas and terminology you'll use repeatedly when interacting with it.

    This article is about the theorem prover itself, not the programming language and not the logic. We assume you know enough about the ACL2 programming language that you can define simple functions, run them, and read and write ACL2 constants and terms. For some examples of what we'll take for granted about ACL2 programming, see programming-knowledge-taken-for-granted.

    We also assume you know enough about logic to understand, for example, the words we use to talk about formulas and proofs. To see some examples of what we'll take for granted about your knowledge of logic terminology, see logic-knowledge-taken-for-granted.

    When you give the theorem prover a goal formula to prove, it tries to prove it by breaking it down into subgoals, each of which must be proved in order to prove the original goal. This breaking apart of the goal is done by various proof techniques built into ACL2. Different proof techniques break the formula apart in different ways. For example, the simplifier rips apart the propositional structure to isolate the various cases and applies rewrite rules to simplify the subterms of the formula, while the induction engine will attempt to break the goal into some base cases and some induction steps.

    The theorem prover's behavior is affected by a database of rules derived from axioms, definitions, and previously proved theorems. The database also records the enabled status of each rule; only enabled rules are seen by the prover and you can set the status of a rule. There are many other user-settable switches and parameters affecting the behavior of the prover; you'll learn about some of them later.

    You guide the theorem prover most of the time simply by identifying lemmas for it to prove. (A lemma is just a theorem that you think is useful in the proofs of other theorems.)

    Why does this guide the theorem prover? Because every time you get the system to prove a theorem, it turns the theorem into a rule (unless you tell it not to) and stores the rule in the database. That changes how the prover behaves subsequently. But you determine the kind of rule ACL2 stores.

    To learn to ``drive'' the theorem prover you have to learn how various rules affect the system's behavior and how it turns proved formulas into rules. But before we discuss this, we discuss a more mathematical activity: how do you figure out the lemmas ACL2 will need in order for it to prove interesting theorems? ACL2 can often help you in this activity, if you use it in the right way.

    Here is the way we recommend you use ACL2.

    The Method.

    (1) you present ACL2 with the goal conjecture to prove

    (2) typically, it fails to prove it (or you abort its attempt), but it prints some Key Checkpoints

    (3) you look at the Key Checkpoints and decide that you know a fact that will help; this tutorial will present some helpful questions to keep in mind

    (4) you formalize your knowledge as a formula, along with directions for how ACL2 should turn the formula into a rule; this tutorial will tell you about the most commonly used rule, the rewrite rule

    (5) you recursively apply The Method to get ACL2 to prove your formula and to store it as the kind of rule you specified

    (6) go to step (1)

    Caveat: This approach simply won't work on some theorems! Basically, this is a ``troubleshooting'' approach, where you're letting ACL2 determine the basic strategy and you're just helping with the subgoals. But for some theorems, ACL2's approach will be misguided and no amount of troubleshooting will salvage its strategy. You'll have a sense that this is happening when it happens because the formulas ACL2 presents to you will raise issues that feel irrelevant to you. The basic truth is that if you think a formula is always true there are usually strong intuitive reasons behind your belief. If you were asked to defend your belief, you'd start to explain your reasons and with training you can turn that into a proof. So when ACL2's formulas present you with things you haven't thought of either (a) you'll have an ``Ah ha!'' moment when you realize you hadn't considered that case or (b) you'll realize that ACL2's approach is different from your intuitive ``proof.''

    But, surprisingly often, the troubleshooting approach to finding proofs works quite well, especially as you rein in your expectations and develop a sense of what ACL2 can handle on its own. Of course, if you can decompose the proof into a couple of main lemmas before you start, so much the better: write down your sequence of lemmas, thinking about the rules you want them to generate, and get ACL2 to prove each of them before giving it the main theorem. This proof planning approach will gradually become an integral part of your use of The Method.

    The only mechanized help we can offer with The Method, aside from the theorem prover itself, are tools to help you manage the stack of subgoals it generates when, in step (5) you recursively apply The Method to a lemma. There are both Emacs and Eclipse tools available.

    To use The Method you have to read the Key Checkpoints printed at the very end of failed proof attempts, just after the line that reads:

    The key checkpoint goals, below, may help you to debug this failure.
    
    Most users do not read the output from successful proofs and do not read the output during a proof -- they just let it stream by as a sort of gestalt meter on the theorem prover's progress or lack thereof. For example, you'll be able to tell it is in a loop and needs to be interrupted.

    You will respond to most Key Checkpoints by formulating new lemmas for the system to prove and store as rules designed by you to alter ACL2's behavior so that it proves the Key Checkpoints. You will give each lemma a name and write some formula to express the mathematical idea. You'll command ACL2 to prove it by typing:

    (defthm name
            formula
            ...)
    
    In the ``...'' you may provide two other kinds of information: hints for how to prove formula and directives, called rule-classes, for how to convert formula into a rule after it has proved formula. Note that you are in charge of determining what kind of rule ACL2 generates! There are over a dozen different types of rules with many opportunities to specialize each type. But the most common kind of rule you'll want to generate is a rewrite rule.

    We recommend that you read the following topics in the following order, without skipping anything but links into the reference manual, which are marked by the little warning sign, ``''.

    (1) See introduction-to-rewrite-rules-part-1 to read about the use and design of rewrite rules.

    (2) See introduction-to-key-checkpoints to see how to use The Method to help you design rules.

    (3) See introduction-to-rewrite-rules-part-2 for general guidance on how to turn formulas into effective rules.

    (4) See introduction-to-the-database to see how to issue commands that build different kinds of rules and that affect the enabled status of existing rules.

    (5) See introduction-to-hints to see how to give the prover hints for how to prove specific theorems or even subgoals of specific proof attempts.

    (6) See introduction-to-a-few-system-considerations for a few words about system aspects of interacting with ACL2.

    (7) See introductory-challenges for a graduated sequence of good challenge problems for the new user to tackle. Do not skip this section! It is here that you really learn how to use ACL2 -- by using it.

    (8) See frequently-asked-questions-by-newcomers for a list of questions that new users frequently ask, answered mainly by providing links into the reference manual. We recommend that you skim through these questions and remember that you can find the answers here later. We are very interested in receiving suggestions for how to improve this FAQ and this tutorial. See the ACL2 home page, specifically the link ``Mailing Lists''.

    Please read all of the material cited above (skipping only the reference-manual links (``'')) before you try to use ACL2 on problems of your own.

    By this point you should have read, at least, the following topics from this tutorial introduction to the theorem prover:

     INTRODUCTION-TO-THE-THEOREM-PROVER
         INTRODUCTION-TO-REWRITE-RULES-PART-1
             SPECIAL-CASES-FOR-REWRITE-RULES
             EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES
         INTRODUCTION-TO-KEY-CHECKPOINTS
             DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS
             GENERALIZING-KEY-CHECKPOINTS
             POST-INDUCTION-KEY-CHECKPOINTS
         INTRODUCTION-TO-REWRITE-RULES-PART-2
             STRONG-REWRITE-RULES
                 PRACTICE-FORMULATING-STRONG-RULES
                     PRACTICE-FORMULATING-STRONG-RULES-1
                     PRACTICE-FORMULATING-STRONG-RULES-2
                     PRACTICE-FORMULATING-STRONG-RULES-3
                     PRACTICE-FORMULATING-STRONG-RULES-4
                     PRACTICE-FORMULATING-STRONG-RULES-5
                     PRACTICE-FORMULATING-STRONG-RULES-6
             SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES
             FURTHER-INFORMATION-ON-REWRITING
         INTRODUCTION-TO-THE-DATABASE
         INTRODUCTION-TO-HINTS
         INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS
         INTRODUCTORY-CHALLENGES
             INTRODUCTORY-CHALLENGE-PROBLEM-1
             INTRODUCTORY-CHALLENGE-PROBLEM-2
             INTRODUCTORY-CHALLENGE-PROBLEM-3
             (there are others but at least do a few)
         FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS
    

    We also recommend that you look at the ACL2 Demos, at the ``demos'' link of the ACL2 home page http://www.cs.utexas.edu/users/moore/acl2.

    Most users of ACL2 have bought the book

    Computer-Aided Reasoning: An Approach, Kaufmann, Manolios, and Moore, Kluwer Academic Publishers, June, 2000

    which is available in paperback from Lulu for approximately $20 (as of 2010). See http://www.lulu.com/content/1746161. That book contains hundreds of exercises in programming, proof, and using The Method to prove theorems. Solutions to the exercises are online. For more information about the book and a companion one (also available on Lulu) about industrial applications of ACL2, see

    http://www.cs.utexas.edu/users/moore/publications/acl2-papers.html#Books

    Thank you for spending the time to get acquainted with the basics of the ACL2 theorem prover. Don't hesitate to send further questions to the ACL2 Help address on the ``Mailing Lists'' link of the ACL2 home page.

    End of Tutorial Introduction to the Theorem Prover

    Below is a list of all of the topics cited on this page.

    Some Related Topics




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER.html0000664002132200015000000000333712222333515022434 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER

    answer to challenge problem 1 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    This answer is in the form of an ACL2 script sufficient to lead ACL2 to a proof.

    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x)) (list (car x)))))
    
    ; Trying triple-rev at this point produces a key checkpoint containing
    ; (REV (APPEND (REV (CDR X)) (LIST (CAR X)))), which suggests:
    
    (defthm rev-append
      (equal (rev (append a b))
             (append (rev b) (rev a))))
    
    ; And now triple-rev succeeds.
    
    (defthm triple-rev
      (equal (rev (rev (rev x))) (rev x)))
    
    ; An alternative, and more elegant, solution is to prove the rev-rev
    ; instead of rev-append:
    
    ; (defthm rev-rev
    ;   (implies (true-listp x)
    ;            (equal (rev (rev x)) x)))
    
    ; Rev-rev is also discoverable by The Method because it is
    ; suggested by the statement of triple-rev itself:  rev-rev
    ; simplifies a simpler composition of the functions in triple-rev.
    
    ; Both solutions produce lemmas likely to be of use in future proofs
    ; about rev.
    
    

    Use your browser's Back Button now to return to introductory-challenge-problem-1.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-1.html0000664002132200015000000000226612222333515021437 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-1.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-1

    challenge problem 1 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing

    :ubt! 1
    
    which will undo everything since the first user event.

    Then define this function:

    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x)) (list (car x)))))
    
    Then use The Method to prove:
    (defthm triple-rev
      (equal (rev (rev (rev x))) (rev x)))
    

    When you've solved this problem, compare your answer to ours; see introductory-challenge-problem-1-answer.

    Then, use your browser's Back Button to return to introductory-challenges.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER.html0000664002132200015000000000327612222333515022437 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER

    answer to challenge problem 2 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    This answer is in the form of a script sufficient to lead ACL2 to a proof.

    ; Trying subsetp-reflexive at this point produces the key checkpoint:
    
    ; (IMPLIES (AND (CONSP X)
    ;               (SUBSETP (CDR X) (CDR X)))
    ;          (SUBSETP (CDR X) X))
    
    ; which suggests the generalization:
    
    (defthm subsetp-cdr
      (implies (subsetp a (cdr b))
               (subsetp a b)))
    
    ; And now subsetp-reflexive succeeds.
    
    (defthm subsetp-reflexive
      (subsetp x x))
    
    ; A weaker version of the lemma, namely the one in which we
    ; add the hypothesis that b is a cons, is also sufficient.
    
    ;    (defthm subsetp-cdr-weak
    ;      (implies (and (consp b)
    ;                    (subsetp a (cdr b)))
    ;               (subsetp a b)))
    
    ; But the (consp b) hypothesis is not really necessary in
    ; ACL2's type-free logic because (cdr b) is nil if b is
    ; not a cons.  For the reasons explained in the tutorial, we
    ; prefer the strong version.
    
    

    Use your browser's Back Button now to return to introductory-challenge-problem-2.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-2.html0000664002132200015000000000175712222333515021444 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-2.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-2

    challenge problem 2 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1.

    Use The Method to prove

    (defthm subsetp-reflexive
      (subsetp x x))
    

    When you've solved this problem, compare your answer to ours; see introductory-challenge-problem-2-answer.

    Then, use your browser's Back Button to return to introductory-challenges.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER.html0000664002132200015000000001040612222333515022431 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER

    answer to challenge problem 3 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    This answer is in the form of a script sufficient to lead ACL2 to a proof.

    ; Trying dupsp-rev at this point produces the key checkpoint:
    
    ; (IMPLIES (AND (CONSP X)
    ;               (NOT (MEMBER (CAR X) (CDR X)))
    ;               (EQUAL (DUPSP (REV (CDR X)))
    ;                      (DUPSP (CDR X))))
    ;          (EQUAL (DUPSP (APPEND (REV (CDR X)) (LIST (CAR X))))
    ;                 (DUPSP (CDR X))))
    
    ; which suggests the lemma
    
    ; (defthm dupsp-append
    ;   (implies (not (member e x))
    ;            (equal (dupsp (append x (list e)))
    ;                   (dupsp x))))
    
    ; However, attempting to prove that, produces a key checkpoint
    ; containing (MEMBER (CAR X) (APPEND (CDR X) (LIST E))).
    ; So we prove the lemma:
    
    (defthm member-append
      (iff (member e (append a b))
           (or (member e a)
               (member e b))))
    
    ; Note that we had to use iff instead of equal since member is not a
    ; Boolean function.
    
    ; Having proved this lemma, we return to dupsp-append and succeed:
    
    (defthm dupsp-append
      (implies (not (member e x))
               (equal (dupsp (append x (list e)))
                      (dupsp x))))
    
    ; So now we return to dups-rev, expecting success.  But it fails
    ; with the same key checkpoint:
    
    ; (IMPLIES (AND (CONSP X)
    ;               (NOT (MEMBER (CAR X) (CDR X)))
    ;               (EQUAL (DUPSP (REV (CDR X)))
    ;                      (DUPSP (CDR X))))
    ;          (EQUAL (DUPSP (APPEND (REV (CDR X)) (LIST (CAR X))))
    ;                 (DUPSP (CDR X))))
    
    ; Why wasn't our dupsp-append lemma applied?  We have two choices here:
    ; (1) Think.  (2) Use tools.
    
    ; Think:  When an enabled rewrite rule doesn't fire even though the left-hand
    ; side matches the target, the hypothesis couldn't be relieved.  The dups-append
    ; rule has the hypothesis (not (member e x)) and after the match with the left-hand side,
    ; e is (CAR X) and x is (REV (CDR X)).  So the system couldn't rewrite
    ; (NOT (MEMBER (CAR X) (REV (CDR X)))) to true, even though it knows that
    ; (NOT (MEMBER (CAR X) (CDR X))) from the second hypothesis of the checkpoint.
    ; Obviously, we need to prove member-rev below.
    
    ; Use tools:  We could enable the ``break rewrite'' facility, with
    
    ; ACL2 !>:brr t
    
    ; and then install an unconditional monitor on the rewrite rule
    ; dupsp-append, whose rune is (:REWRITE DUPSP-APPEND), with:
    
    ; :monitor (:rewrite dupsp-append) t
    
    ; Then we could re-try our main theorem, dupsp-rev.  At the resulting
    ; interactive break we type :eval to evaluate the attempt to relieve the
    ; hypotheses of the rule.
    
    ; (1 Breaking (:REWRITE DUPSP-APPEND) on
    ; (DUPSP (BINARY-APPEND (REV #) (CONS # #))):
    ; 1 ACL2 >:eval
    
    ; 1x (:REWRITE DUPSP-APPEND) failed because :HYP 1 rewrote to
    ; (NOT (MEMBER (CAR X) (REV #))).
    
    ; Note that the report above shows that hypothesis 1 of the rule
    ; did not rewrite to T but instead rewrote to an expression
    ; involving (member ... (rev ...)).  Thus, we're led to the
    ; same conclusion that Thinking produced.  To get out of the
    ; interactive break we type:
    
    ; 1 ACL2 >:a!
    ; Abort to ACL2 top-level
    
    ; and then turn off the break rewrite tool since we won't need it
    ; again right now, with:
    
    ; ACL2 !>:brr nil
    
    ; In either case, by thinking or using tools, we decide to prove:
    
    (defthm member-rev
      (iff (member e (rev x))
           (member e x)))
    
    ; which succeeds.  Now when we try to prove dups-rev, it succeeds.
    
    (defthm dupsp-rev
      (equal (dupsp (rev x))
             (dupsp x)))
    
    

    Use your browser's Back Button now to return to introductory-challenge-problem-3.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-3.html0000664002132200015000000000245512222333515021441 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-3.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-3

    challenge problem 3 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1.

    Define the following functions and use The Method to prove the theorem at the bottom:

    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x)) (list (car x)))))
    
    (defun dupsp (x)  ; does x contain duplicate elements?
      (if (endp x)
          nil
          (if (member (car x) (cdr x))
              t
              (dupsp (cdr x)))))
    
    (defthm dupsp-rev
      (equal (dupsp (rev x)) (dupsp x)))
    

    When you've solved this problem, compare your answer to ours; see introductory-challenge-problem-3-answer.

    Then, use your browser's Back Button to return to introductory-challenges.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER.html0000664002132200015000000005165712222333515022447 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER

    answer to challenge problem 4 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    This answer is in the form of a script sufficient to lead ACL2 to a proof, with a brief prologue.

    We wish to collect one copy of each element in x. We'll actually define the method two ways, primitive recursively and tail-recursively, the latter method being analogous to the program:

    a = nil;
    while (x not empty) {
      a = if (member (car x) a) then a else (cons (car x) a);
      x = (cdr x);
      }
    return a;
    

    We'll prove the two ``equivalent'' and we'll prove that they return a subset of x that contains no duplications.

    This page is organized into four sections. (A) We will start by proving that the primitive recursive version correct: it returns a subset of its argument that is duplication free. This will be straightforward. (B) Then we'll define the while-loop version and we will prove it ``equivalent'' to the primitive recursive version. This will be challenging primarily because the two methods collect their answers in different orders; even stating the relationship between the two is interesting. Proving it will involve a few lemmas. But once we prove their ``equivalence'' the correctness of the while-loop version will be straightforward from the correctness of the primitive recursive version. (C) We will disable the rules we prove about the while-loop version and prove it correct directly, without exploiting the primitive recursive version. This requires leading the theorem prover more carefully because reasoning about tail-recursive functions that accumulate results is sometimes delicate. (D) Lessons learned -- a narrative that summarizes what we learn from these examples.

    We follow The Method, which, recall, involves us in recursive attempts to prove lemmas. We use a notation to indicate our sequence of proof attempts. Here is an example (although in actual use we print things across multiple lines). The number in bracket indicates our ``stack depth''. The ``key term'' is some term from a Key Checkpoint in the failed proof which is responsible for our subsequent action. Sometimes instead of a Key Term we just give an English explanation of what we're thinking.

    [0] (defthm main ...)     Failed!    Key Term: ...
    [1] (defthm lemma-1 ...)  Succeeded!
    [0] (defthm main ...)     Failed!    Key Term: ...
    [1] (defthm lemma-2 ...)  Failed!    Key Term: ...
    [2] (defthm lemma-2a ...) Succeeded!
    [2] (defthm lemma-2b ...) Succeeded!
    [1] (defthm lemma-2 ...)  Succeeded!
    [0] (defthm main ...)     Succeeded!
    

    The rest of this page is just a re-playable script.

    ; -----------------------------------------------------------------
    ; Section A:  The Primitive Recursive Version and Its Correctness
    
    ; The property of having duplications is defined as:
    
    (defun dupsp (x)
      (if (endp x)
          nil
          (if (member (car x) (cdr x))
              t
              (dupsp (cdr x)))))
    
    ; The primitive recursive method of collecting one copy of each element is:
    
    (defun collect-once (x)
      (if (endp x)
          nil
          (if (member (car x) (cdr x))
              (collect-once (cdr x))
              (cons (car x) (collect-once (cdr x))))))
    
    ; [0]
    (defthm main-theorem-1-about-collect-once
      (subsetp (collect-once x) x))
    ; Succeeded!
    
    ; [0]
    ; (defthm main-theorem-2-about-collect-once
    ;   (not (dupsp (collect-once x))))
    ; Failed!
    ; Key Term:  (MEMBER (CAR X) (COLLECT-ONCE (CDR X)))
    
    ; [1]
    (defthm member-collect-once
      (iff (member e (collect-once a))
           (member e a)))
    ; Succeeded!
    
    ; [0]
    (defthm main-theorem-2-about-collect-once
      (not (dupsp (collect-once x))))
    ; Succeeded!
    
    ; That was really easy!
    
    ;-----------------------------------------------------------------
    ; Section B:  The While-Loop Version and Its Correctness --
    ;  presented in two parts:  its equivalence to the primitive recursive
    ;  version and then its correctness proved via that equivalence
    
    ; The tail-recursive, or while-loop version, is defined as follows.  The
    ; function below is the loop itself and it ought to be called with a = nil to
    ; implement the initialization of a in the pseudo-code above.
    
    (defun while-loop-version (x a)
      (if (endp x)
          a
          (while-loop-version (cdr x)
                              (if (member (car x) a)
                                  a
                                  (cons (car x) a)))))
    
    ; We wish to prove that the two are equivalent.  But they are actually
    ; very different.  For example,
    
    ; (collect-once '(2 4 1 3 1 2 3 4))           = (1 2 3 4)
    ; (while-loop-version '(2 4 1 3 1 2 3 4) nil) = (3 1 4 2)
    
    ; Things get a little more complicated if a is non-nil:
    ; (while-loop-version '(2 4 1 3 1 2 3 4) '(2 2 4 4)) = (3 1 2 2 4 4)
    
    ; Several observations help explain what is happening.  (1) Collect-once
    ; collects the last occurrence of each element, in the order of their last
    ; occurrences.  So, for example, since the last occurrence of 2 preceeds the
    ; last occurrence of 3 in '(2 4 1 3 1 2 3 4)), then the collected 2 preceeds
    ; the collected 3 in the answer.  But while-loop-version collects the first
    ; occurrence of each element, in the reverse order of that occurrence.  So it
    ; adds 2 to its accumulator first and adds 3 last, making 3 preceed 2 in the
    ; answer.
    
    ; (2) The while-loop-version does not collect anything already in a and indeed
    ; just adds stuff to the front of a, returning everything initially in a plus
    ; one occurrence of everything in x not in a.
    
    ; To state the relationship that holds between these two we have to define two
    ; other functions.
    
    ; This is our familiar list reverse function...
    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x))
                  (list (car x)))))
    
    ; And this function ``removes'' from x all the elements in y, i.e., copies x
    ; while dropping the elements of y.
    
    (defun list-minus (x y)
      (if (endp x)
          nil
          (if (member (car x) y)
              (list-minus (cdr x) y)
              (cons (car x) (list-minus (cdr x) y)))))
    
    ; The specific equivalence we're really interested in is
    ; (equal (while-loop-version x nil)
    ;        (collect-once (rev x)))
    
    ; But we will not be able to prove that by induction because it has the
    ; constant nil where we need a variable, a, in order to admit an appropriate
    ; inductive instance.  So we will attack the most general problem.  What is
    ; (while-loop-version x a) equal to, in terms of collect-once?
    
    ; The most general relationship between the two collection functions is:
    
    ; (equal (while-loop-version x a)
    ;        (append (collect-once (list-minus (rev x) a)) a))
    
    ; This formula bears thinking about!  If you're like us, you won't believe it
    ; until it is proved!
    
    ; [0]
    ; (defthm general-equivalence
    ;   (equal (while-loop-version x a)
    ;          (append (collect-once (list-minus (rev x) a)) a)))
    ; Failed!
    ; Key term in checkpoint:
    ; (LIST-MINUS (APPEND (REV (CDR X)) (LIST (CAR X))) A)
    
    ; [1]
    (defthm list-minus-append
      (equal (list-minus (append a b) c)
             (append (list-minus a c)
                     (list-minus b c))))
    ; Succeeded!
    
    ; [0]
    ; (defthm general-equivalence
    ;   (equal (while-loop-version x a)
    ;          (append (collect-once (list-minus (rev x) a)) a)))
    ; Failed!
    ; Key term in checkpoint:
    ; (COLLECT-ONCE (APPEND (LIST-MINUS (REV (CDR X)) A) (LIST (CAR X))))
    
    ; [1]
    ; (defthm collect-once-append
    ;   (equal (collect-once (append a b))
    ;          (append (list-minus (collect-once a) b)
    ;                  (collect-once b))))
    ; Failed!
    ; Key term:
    ; (MEMBER (CAR A) (APPEND (CDR A) B))
    
    ; [2]
    (defthm member-append
      (iff (member e (append a b))
           (or (member e a)
               (member e b))))
    ; Succeeded!
    
    ; [1]
    (defthm collect-once-append
      (equal (collect-once (append a b))
             (append (list-minus (collect-once a)
                                 b)
                     (collect-once b))))
    ; Succeeded!
    
    ; [0]
    ; (defthm general-equivalence
    ;   (equal (while-loop-version x a)
    ;          (append (collect-once (list-minus (rev x) a)) a)))
    ; Failed!
    ; Key term:
    ; (APPEND (APPEND (LIST-MINUS (COLLECT-ONCE (LIST-MINUS (REV (CDR X)) A))
    
    ; [1]
    (defthm assoc-append
      (equal (append (append a b) c)
             (append a (append b c))))
    ; Succeeded!
    
    ; [0]
    ; (defthm general-equivalence
    ;   (equal (while-loop-version x a)
    ;          (append (collect-once (list-minus (rev x) a)) a)))
    ; Failed!
    ; Key term:
    ; (LIST-MINUS (COLLECT-ONCE (LIST-MINUS (REV (CDR X)) A)) ...)
    
    ; This key term makes us think of the lemma to move the LIST-MINUS inside the
    ; COLLECT-ONCE.  But when that's done, we will have two LIST-MINUS terms
    ; nestled together and we will want to combine them into one.  Call these two
    ; lemmas (a) and (b).
    
    ; [1] (a)
    ; (defthm list-minus-collect-once
    ;   (equal (list-minus (collect-once x) a)
    ;          (collect-once (list-minus x a))))
    ; Failed!
    ; Key term:
    ; (MEMBER (CAR X) (LIST-MINUS (CDR X) A))
    
    ; [2] (A pretty fact)
    (defthm member-list-minus
      (iff (member e (list-minus x a))
           (and (member e x)
                (not (member e a)))))
    ; Succeeded!
    
    ; [1] (a)
    (defthm list-minus-collect-once
      (equal (list-minus (collect-once x) a)
             (collect-once (list-minus x a))))
    ; Succeeded!
    
    ; [1] (b)
    (defthm list-minus-list-minus
      (equal (list-minus (list-minus x a) b)
             (list-minus x (append b a))))
    ; Succeeded!
    
    ; [0]
    (defthm general-equivalence
      (equal (while-loop-version x a)
             (append (collect-once (list-minus (rev x) a)) a)))
    ; Succeeded!
    
    ; That completes the proof of the ``equivalence'' of the two methods.
    
    ; Now we prove (1) that the result of while-loop-version is a subset, and (2)
    ; that it contains no duplications.  We prove the two conjuncts separately.
    
    ; [0]
    (defthm main-theorem-1-about-while-loop
      (subsetp (while-loop-version x nil) x))
    ; Succeeded!
    
    ; But the theorem prover works harder to do the proof above than one might have
    ; expected because it doesn't turn into an instance of
    ; main-theorem-1-about-collect-once because of the presence of the rev term.
    ; However, we're content that ACL2 managed to do the proof on its own.
    
    ; [0]
    (defthm main-theorem-2-about-while-loop
      (not (dupsp (while-loop-version x nil))))
    
    ; So we see that the proof of correctness of while-loop-version isn't hard,
    ; after we establish the relationship with the primitive recursive version.
    ; But finding and proving the relationship is fairly challenging.
    
    ; -----------------------------------------------------------------
    ; Section C:  A Direct Proof of the Correctness of the While-Loop Version
    
    ; Some would consider the proof in Section B ``indirect'' because we first showed
    ; how while-loop-version could be expressed as a collect-once and then proved
    ; our main theorems about while-loop-version, which means those main proofs
    ; were conducted in terms of collect-once, not while-loop-version.
    
    ; It is interesting to compare this proof with the ``direct'' one in which
    ; we don't use collect-once at all and reason only about while-loop-version.
    
    ; So to do that comparison, let's disable all the lemmas we've proved about
    ; while-loop-version and try to prove the two main theorems above about
    ; while-loop-version.
    
    (in-theory (disable general-equivalence
                        main-theorem-1-about-while-loop
                        main-theorem-2-about-while-loop))
    
    
    ; [0]
    ; (defthm main-theorem-1-about-while-loop-redux
    ;   (subsetp (while-loop-version x nil) x))
    ; Failed!  [Well, the truth is below...]
    
    ; We don't even submit this event above because we recognize that it is not
    ; general enough to permit proof by induction.  We need to deal with the nil in
    ; the second argument of while-loop-version.  Experience with induction tells
    ; us this should be a variable, so we can assume an appropriate inductive
    ; instance.  Therefore, we adopt this subgoal immediately:
    
    ; [1]
    ; (defthm main-lemma-1-about-while-loop-version
    ;   (subsetp (while-loop-version x a) (append x a)))
    ; Failed!
    ; Key Term:  Does the wrong induction.
    
    ; [1]
    ; (defthm main-lemma-1-about-while-loop-version
    ;   (subsetp (while-loop-version x a) (append x a))
    ;   :hints (("Goal" :induct (while-loop-version x a))))
    ; Failed!  Two key terms are suggested
    ; Key term: (IMPLIES (AND ... (SUBSETP (WHILE-LOOP-VERSION (CDR X) A) (APPEND (CDR X) A)))
    ;                    (SUBSETP (WHILE-LOOP-VERSION (CDR X) A) (CONS ... (APPEND (CDR X) A))))
    ; Key term: (SUBSETP A A)
    ; So we'll prove both before trying again.
    ; [2]
    (defthm subsetp-cons
      (implies (subsetp a b)
               (subsetp a (cons e b))))
    ; Succeeded!
    
    ; [2]
    (defthm subsetp-reflexive
      (subsetp a a))
    ; Succeeded!
    
    ; [1]
    ; (defthm main-lemma-1-about-while-loop-version
    ;   (subsetp (while-loop-version x a) (append x a))
    ;   :hints (("Goal" :induct (while-loop-version x a))))
    ; Failed!
    ; Key Term:
    ; (IMPLIES (AND ...
    ;               (SUBSETP (WHILE-LOOP-VERSION (CDR X) (CONS (CAR X) A))
    ;                        (APPEND (CDR X) (CONS (CAR X) A))))
    ;          (SUBSETP (WHILE-LOOP-VERSION (CDR X) (CONS (CAR X) A))
    ;                   (CONS (CAR X) (APPEND (CDR X) A))))
    
    ; We'd be done if we could rewrite the
    ; (APPEND (CDR X) (CONS (CAR X) A))
    ; to
    ; (CONS (CAR X) (APPEND (CDR X) A))
    ; These two terms are not equal!  But they are ``set-equal'' and this kind of
    ; rewriting is possible using user-defined equivalences and congruence rules.
    ; But the new user should not dive into congruences yet.  So we will do this
    ; with ordinary lemmas:
    
    ; The plan then is to prove
    ; (iff (subsetp a (append b (cons e c)))
    ;      (subsetp a (cons e (append b c))))
    
    ; Consider the first half of this bi-implication:
    ; (implies (subsetp a (append b (cons e c)))            ; hyp1
    ;          (subsetp a (cons e (append b c))))           ; concl
    ; Notice that if we knew
    ; (subsetp (append b (cons e c)) (cons e (append b c))) ; hyp2
    ; then we could use hyp1 and hyp2 together with the transitivity of
    ; subsetp to get concl.
    
    ; The proof in the other direction is comparable but requires the
    ; (subsetp (cons e (append b c)) (append b (cons e c)))
    
    ; Thus, our plan is prove
    ; (a) transitivity of subsetp
    ; (b) (subsetp (append b (cons e c)) (cons e (append b c)))
    ; (c) (subsetp (cons e (append b c)) (append b (cons e c)))
    
    ; in order to prove
    ; (d) (iff (subsetp a (append b (cons e c)))
    ;         (subsetp a (cons e (append b c))))
    
    ; [2] (a)
    (defthm trans-subsetp
      (implies (and (subsetp a b)
                    (subsetp b c))
               (subsetp a c)))
    ; Succeeded!
    
    ; [2] (b)
    (defthm append-cons-v-cons-append-1
      (subsetp (append b (cons e c))
               (cons e (append b c))))
    ; Succeeded!
    
    ; [2] (c)
    (defthm append-cons-v-cons-append-2
      (subsetp (cons e (append b c))
               (append b (cons e c))))
    ; Succeeded!
    
    ; [2] (d)
    (defthm subsetp-append-cons-cons-append
      (iff (subsetp a (append b (cons e c)))
           (subsetp a (cons e (append b c)))))
    ; Succeeded!
    
    ; [1]
    (defthm main-lemma-1-about-while-loop-version
      (subsetp (while-loop-version x a) (append x a))
      :hints (("Goal" :induct (while-loop-version x a))))
    ; Succeeded!
    
    ; [0]
    ; (defthm main-theorem-1-about-while-loop-version
    ;   (subsetp (while-loop-version x nil) x))
    ; Failed!  [But the truth is below...]
    
    ; But we don't submit this because we don't expect it to be proved
    ; from the main lemma just proved:  they don't match!  But
    ; note that if we instantiated the main lemma, replacing a by nil,
    ; we get:
    
    ; (subsetp (while-loop-version x nil) (append x nil))
    
    ; and we could simplify the (append x nil) to x in this context, with
    ; another congruence rule -- if we were using them.  So let's prove
    ; first that we can simplify (append x nil) inside a subsetp:
    
    ; [1]
    (defthm subsetp-append-nil
      (iff (subsetp x (append y nil))
           (subsetp x y)))
    ; Succeeded!
    
    ; and then just tell ACL2 how to use the lemma to get the main theorem.  Note
    ; that we give a hint to instantiate main-lemma-1... but we also disable
    ; main-lemma-1... because otherwise it will rewrite itself away!  Once the
    ; instance of main-lemma-1... is sitting around as a hypothesis,
    ; subsetp-append-nil will rewrite the (append x nil) to x for us and finish the
    ; proof.
    
    ; [0]
    (defthm main-theorem-1-about-while-loop-version
      (subsetp (while-loop-version x nil) x)
      :hints (("Goal"
               :use (:instance main-lemma-1-about-while-loop-version
                               (x x)
                               (a nil))
               :in-theory (disable main-lemma-1-about-while-loop-version))))
    ; Succeeded!
    
    ; Recall that the main-theorem-1... just proved is just half of what we want.
    ; We also want:
    
    ; [0]
    ; (defthm main-theorem-2-about-while-loop-version
    ;   (not (dupsp (while-loop-version x nil))))
    ; Failed!  [But the truth is below...]
    
    ; But, again, we don't submit that because the nil makes it not general enough for
    ; induction.  Instead we go immediately to:
    
    ; [1]
    (defthm main-lemma-2-about-while-loop-version
      (implies (not (dupsp a))
               (not (dupsp (while-loop-version x a)))))
    ; Succeeded!
    
    ; This time we know our main-lemma-2... will match (there's no (append x nil)
    ; in there to mess things up) and so we can complete the proof with:
    
    ; [0]
    (defthm main-theorem-2-about-while-loop-version
      (not (dupsp (while-loop-version x nil))))
    ; Succeeded!
    
    ;-----------------------------------------------------------------
    ; Section D:  Lessons Learned
    
    ; The most obvious lesson is that it is easier to reason about the primitive
    ; recursive collect-once than about the while-loop-version.  Thus, if your only
    ; need is for a function that collects one occurrence of each element of a list
    ; and you don't care about the order in which you collect them and you don't
    ; need it to be very sparing of stack space when it executes, then use the
    ; primitive recursive definition and don't even think about while loops!
    
    ; So why might you be driven to while-loop-version?  One possibility is that
    ; the list you wish to process is very long and the primitive recursive version
    ; would produce a stack overflow.  In ACL2, that would mean the list would have
    ; to be several thousand long.  Is your application really so demanding?
    
    ; Another possibility is that you are modeling in Lisp a while loop expressed
    ; in some other programming language.  In that case, the fidelity of your model to
    ; the artifact being modeled is important and you should use while-loop-version.
    
    ; Another possibility is that for some reason order matters and you really are
    ; interested in collecting the first occurrence rather than the last.  Of
    ; course this is most likely to be relevant in more interesting applications
    ; where the occurrences are somehow distinguishable.
    
    ; If you are forced to deal with the while-loop-version the question is do you
    ; do an indirect proof as in Section B or a direct proof as in Section C?
    ; The indirect proof involved 10 theorems and the direct proof involved 11.
    ; That is not a significant difference.
    
    ; But our sense is that the indirect proof is easier to find, once you figure
    ; out the basic shape of the relation between while-loop-version collect-once.
    ; In particular, we had to give the theorem prover two hints in the direct
    ; proof (versus no hints in the indirect proof).  One of our hints was about
    ; what induction to do and the other was about how to use a previously proved
    ; instance of a lemma involving an accumulator.  Furthermore, we had to think
    ; carefully about the use of the transitivity of subsetp and we had to hack our
    ; way around rewriting (append a (cons e b)) to (cons e (append a b)) in a
    ; subsetp-expression.
    
    ; Some of these ``set'' problems could have been handled a lot more elegantly by
    ; defining set-equal as an equivalence relation and proving the congruence
    ; rules to allow the rewriting of set-equal terms to set-equal terms inside
    ; certain expressions like subsetp and member.  However, that involves a lot of
    ; overhead in the form of congruence rules showing that set-equality is
    ; maintained by replacement of set-equals by set-equals in various argument
    ; positions of the various functions.  See :doc congruence.  In general, we
    ; find congruence-based reasoning extremely neat and powerful when the
    ; appropriate infrastructure has been built up.  But because the infrastructure
    ; is ``heavy'' we tend not to invest in it for small projects.
    
    ; In summary, different users might take home different lessons about whether a
    ; direct or indirect proof is better here.  This is in part due to the
    ; complexity of the functional relationship between collect-once and
    ; while-loop-version, which additionall involved append, list-minus, and rev.
    ; Had the relationship been simpler, the indirect proof would have been
    ; preferred.
    
    ; An undeniable lesson, however, is that it is helpful to know both styles of
    ; proof and to be able to explore both as needed in your applications.
    

    Use your browser's Back Button now to return to introductory-challenge-problem-4.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGE-PROBLEM-4.html0000664002132200015000000000547212222333515021444 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGE-PROBLEM-4.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGE-PROBLEM-4

    challenge problem 4 for the new user of ACL2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Start in a fresh ACL2, either by restarting your ACL2 image from scratch or executing :ubt! 1.

    This problem is much more open ended than the preceding ones. The challenge is to define a function that collects exactly one copy of each element of a list and to prove that it returns a subset of the list with no duplications.

    Hint: We recommend that you read this hint to align your function names with our solution, to make comparisons easier. Our answer is shown in see introductory-challenge-problem-4-answer. In that page you'll see a definition of a function collect-once and the proofs of two theorems:

    (defthm main-theorem-1-about-collect-once
      (subsetp (collect-once x) x))
    
    (defthm main-theorem-2-about-collect-once
      (not (dupsp (collect-once x))))
    
    The function dupsp is as defined in see introductory-challenge-problem-3. This is quite easy.

    Then, we define a tail-recursive version of the method based on the pseudo-code:

     a = nil;
     while (x not empty) {
      a = if (member (car x) a) then a else (cons (car x) a);
      x = (cdr x);
      }
     return a;
    
    We formalize this with the function while-loop-version, where (while-loop-version x nil) is the ``semantics'' of the code above. I.e., the function while-loop-version captures the while loop in the pseudo-code above and returns the final value of a, and it should be invoked with the initial value of a being nil.

    We prove (while-loop-version x nil) returns a subset of x that contains no duplications. Furthermore, we do it two ways: first ``indirectly'' by relating while-loop-version to collect-once, and second (``directly'') without using collect-once. Both of these proofs are much harder than the collect-once approach, involving about a dozen lemmas each.

    Compare your solutions to ours at see introductory-challenge-problem-4-answer.

    Then, use your browser's Back Button to return to introductory-challenges.




    acl2-sources/doc/HTML/INTRODUCTORY-CHALLENGES.html0000664002132200015000000000452712222333515020330 0ustar kaufmannacl2 INTRODUCTORY-CHALLENGES.html -- ACL2 Version 6.3

    INTRODUCTORY-CHALLENGES

    challenge problems for the new ACL2 user
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Do each of the problems. In each case, start with a fresh ACL2 (or undo all effects of previous events with :ubt! 1). This may require that you ``re-discover'' the same lemma more than once in different problems, but recognizing the need for something you used in some previous project is part of the training.

    We recommend that you follow The Method and consult the documentation as needed -- but that you not look at our answers until you're well and truly baffled!

    See introductory-challenge-problem-1 (Answer: introductory-challenge-problem-1-answer)

    See introductory-challenge-problem-2 (Answer: introductory-challenge-problem-2-answer)

    See introductory-challenge-problem-3 (Answer: introductory-challenge-problem-3-answer)

    See introductory-challenge-problem-4 (Answer: introductory-challenge-problem-4-answer)

    In addition to these explicit challenge problems designed for beginners, the ACL2 documentation has many example solutions to problems (not always phrased in the question/answer format here). If you are looking for other examples, you should consider

    annotated-acl2-scripts (Answer: the answers are given in the examples)

    When you've done the problems and compared your solutions to ours, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/INVISIBLE-FNS-TABLE.html0000664002132200015000000000565512222333531017556 0ustar kaufmannacl2 INVISIBLE-FNS-TABLE.html -- ACL2 Version 6.3

    INVISIBLE-FNS-TABLE

    functions that are invisible to the loop-stopper algorithm
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    ACL2 !>(invisible-fns-table (w state))
    ((binary-+ unary--)
     (binary-* unary-/)
     (unary-- unary--)
     (unary-/ unary-/))
    
    Among other things, the setting above has the effect of making unary-- ``invisible'' for the purposes of applying permutative :rewrite rules to binary-+ trees. Also see add-invisible-fns and see remove-invisible-fns, which manage macro aliases (see macro-aliases-table), as well as see set-invisible-fns-table.

    See table for a general discussion of tables.

    The ``invisible functions table'' is an alist with elements of the following form, where fn is a function symbol and the ufni are unary function symbols in the current ACL2 world, and k is at least 1.

    (fn ufn1 ufn2 ... ufnk)
    

    This table thus associates with certain function symbols, e.g., fn above, a set of unary functions, e.g., the ufni above. The ufni associated with fn in the invisible functions table are said to be ``invisible with respect to fn.'' If fn is not the car of any pair in the alist, then no function is invisible for it. Thus for example, setting the invisible functions alist to nil completely eliminates the consideration of invisibility.

    The notion of invisibility is involved in the use of the :loop-stopper field of :rewrite rules to prevent the indefinite application of permutative rewrite rules. Roughly speaking, if rewrite rules are being used to permute arg and (ufni arg) inside of a nest of fn calls, and ufni is invisible with respect to fn, then arg and (ufni arg) are considered to have the same ``weight'' and will be permuted so as to end up as adjacent tips in the fn nest. See loop-stopper.




    acl2-sources/doc/HTML/IO.html0000664002132200015000000003757212222333520015271 0ustar kaufmannacl2 IO.html -- ACL2 Version 6.3

    IO

    input/output facilities in ACL2
    Major Section:  ACL2 Documentation
    

    Example:
    (mv-let
      (channel state)
      (open-input-channel "foo.lisp" :object state)
      (mv-let (eofp obj state)
              (read-object channel state)
              (.
                .
                 (let ((state (close-input-channel channel state)))
                       (mv final-ans state))..)))
    
    Also see file-reading-example.

    For advanced ways to control printing, see print-control.

    For a discussion of formatted printing, see fmt.

    To control ACL2 abbreviation (``evisceration'') of objects before printing them, see set-evisc-tuple, see without-evisc, and see set-iprint.

    To redirect output to a file, see output-to-file.

    Some Related Topics

    ACL2 supports input and output facilities equivalent to a subset of those found in Common Lisp. ACL2 does not support random access to files or bidirectional streams. In Common Lisp, input and output are to or from objects of type stream. In ACL2, input and output are to or from objects called ``channels,'' which are actually symbols. Although a channel is a symbol, one may think of it intuitively as corresponding to a Common Lisp stream. Channels are in one of two ACL2 packages, "ACL2-INPUT-CHANNEL" and "ACL2-OUTPUT-CHANNEL". When one ``opens'' a file one gets back a channel whose symbol-name is the file name passed to ``open,'' postfixed with -n, where n is a counter that is incremented every time an open or close occurs.

    There are three channels which are open from the beginning and which cannot be closed:

      acl2-input-channel::standard-character-input-0
      acl2-input-channel::standard-object-input-0
      acl2-input-channel::standard-character-output-0
    
    All three of these are really Common Lisp's *standard-input* or *standard-output*, appropriately.

    For convenience, three global variables are bound to these rather tedious channel names:

      *standard-ci*
      *standard-oi*
      *standard-co*
    
    Common Lisp permits one to open a stream for several different kinds of io, e.g. character or byte. ACL2 permits an additional type called ``object''. In ACL2 an ``io-type'' is a keyword, either :character, :byte, or :object. When one opens a file, one specifies a type, which determines the kind of io operations that can be done on the channel returned. The types :character and :byte are familiar. Type :object is an abstraction not found in Common Lisp. An :object file is a file of Lisp objects. One uses read-object to read from :object files and print-object$ (or print-object$-ser) to print to :object files. (The reading and printing are really done with the Common Lisp read and print functions. For those familiar with read, we note that the recursive-p argument is nil.) The function read-object-suppress is logically the same as read-object except that read-object-suppress throws away the second returned value, i.e. the value that would normally be read, simply returning (mv eof state); under the hood, read-object-suppress avoids errors, for example those caused by encountering symbols in packages unknown to ACL2.

    File-names are strings. ACL2 does not support the Common Lisp type pathname. However, for the file-name argument of the output-related functions listed below, ACL2 supports a special value, :STRING. For this value, the channel connects (by way of a Common Lisp output string stream) to a string rather than to a file: as characters are written to the channel they can be retrieved by using get-output-stream-string$.

    Here are the names, formals and output descriptions of the ACL2 io functions.

    Input Functions:
      (open-input-channel (file-name io-type state) (mv channel state))
      (open-input-channel-p (channel io-type state) boolean)
      (close-input-channel (channel state) state)
      (read-char$ (channel state) (mv char/nil state)) ; nil for EOF
      (peek-char$ (channel state) boolean)
      (read-byte$ (channel state) (mv byte/nil state)) ; nil for EOF
      (read-object (channel state) (mv eof-read-flg obj-read state))
      (read-object-suppress (channel state) (mv eof-read-flg state))
    
    Output Functions:
      (open-output-channel  (file-name io-type state) (mv channel state))
      (open-output-channel! (file-name io-type state) (mv channel state))
      (open-output-channel-p (channel io-type state) boolean)
      (close-output-channel (channel state) state)
      (princ$ (obj channel state) state)
      (write-byte$ (byte channel state) state)
      (print-object$ (obj channel state) state)
      (print-object$-ser (obj serialize-character channel state) state)
      (fms  (string alist channel state evisc-tuple) state)
      (fms! (string alist channel state evisc-tuple) state)
      (fmt  (string alist channel state evisc-tuple) (mv col state))
      (fmt! (string alist channel state evisc-tuple) (mv col state))
      (fmt1 (string alist col channel state evisc-tuple) (mv col state))
      (fmt1! (string alist col channel state evisc-tuple) (mv col state))
      (cw (string arg0 arg1 ... argn) nil)
      (get-output-stream-string$ (channel state
                                  &optional (close-p 't)
                                            (ctx ''get-output-stream-string$))
                                 (mv erp string state))
    
    The ``formatting'' functions are particularly useful; see fmt and see cw. In particular, cw prints to a ``comment window'' and does not involve the ACL2 state, so many may find it easier to use than fmt and its variants. The functions fms!, fmt!, and fmt1! are the same as their respective functions without the ``!,'' except that the ``!'' functions are guaranteed to print forms that can be read back in (at a slight readability cost).

    When one enters ACL2 with (lp), input and output are taken from *standard-oi* to *standard-co*. Because these are synonyms for *standard-input* and *standard-output*, one can drive ACL2 io off of arbitrary Common Lisp streams, bound to *standard-input* and *standard-output* before entry to ACL2.

    The macro get-output-stream-string$ returns the string accumulated into the given channel. By default, a call of this macro closes the supplied output channel. However, a third argument is optional (default t), and if it evaluates to nil then the channel remains open. The fourth argument is an optional context, which generally evaluates to a symbol, for error reporting. The following example illustrates.

    ACL2 !>
    (mv-let
       (channel state)
       (open-output-channel :string :object state)
       (pprogn (print-object$-ser 17 nil channel state)
               (print-object$-ser '(a b (c d)) nil channel state)
               (er-let*
                 ((str1 (get-output-stream-string$
                         channel state
                         nil))) ; keep the channel open
                 (pprogn (print-object$-ser 23 nil channel state)
                         (print-object$-ser '((e f)) nil channel state)
                         (er-let* ; close the channel
                           ((str2 (get-output-stream-string$ channel state)))
                           (value (cons str1 str2)))))))
     ("
    17
    (A B (C D))" . "
    23
    ((E F))")
    ACL2 !>
    
    Also see printing-to-strings for a discussion of formatted printing functions such as fmt-to-string that do not take a channel or state argument and return a string.

    By default, symbols are printed in upper case when vertical bars are not required, as specified by Common Lisp. See set-print-case for how to get ACL2 to print symbols in lower case.

    By default, numbers are printed in radix 10 (base 10). See set-print-base for how to get ACL2 to print numbers in radix 2, 8, or 16.

    To see the guard of an IO function, or indeed any function, see args or call the function guard; but some built-in functions (including some IO functions) will print the result using the variable STATE-STATE. While that is logically correct, if you want to execute the guard then you should replace that variable by STATE and also replace each built-in function symbol of the form xxx-p1 by corresponding function symbol xxx-p. Consider the following example.

    ACL2 !>:args princ$
    
    Function         PRINC$
    Formals:         (X CHANNEL STATE-STATE)
    Signature:       (PRINC$ * * STATE)
                     => STATE
    Guard:           (AND (OR (ACL2-NUMBERP X)
                              (CHARACTERP X)
                              (STRINGP X)
                              (SYMBOLP X))
                          (STATE-P1 STATE-STATE)
                          (SYMBOLP CHANNEL)
                          (OPEN-OUTPUT-CHANNEL-P1 CHANNEL
                                                  :CHARACTER STATE-STATE))
    Guards Verified: T
    Defun-Mode:      :logic
    Type:            (CONSP (PRINC$ X CHANNEL STATE-STATE))
    Documentation available via :DOC
     PRINC$
    ACL2 !>(untranslate (guard 'princ$ nil (w state)) t (w state))
    (AND (OR (ACL2-NUMBERP X)
             (CHARACTERP X)
             (STRINGP X)
             (SYMBOLP X))
         (STATE-P1 STATE-STATE)
         (SYMBOLP CHANNEL)
         (OPEN-OUTPUT-CHANNEL-P1 CHANNEL
                                 :CHARACTER STATE-STATE))
    ACL2 !>
    
    If you want to execute the guard for princ$, then according to the suggestion above, you should consider the guard for (princ$ x channel state) to be as follows.
    (AND (OR (ACL2-NUMBERP X)
             (CHARACTERP X)
             (STRINGP X)
             (SYMBOLP X))
         (STATE-P STATE)
         (SYMBOLP CHANNEL)
         (OPEN-OUTPUT-CHANNEL-P CHANNEL :CHARACTER STATE))
    
    For example, we can check the guard for a given value and channel as follows.
    ACL2 !>(let ((x 3) (channel *standard-co*))
             (AND (OR (ACL2-NUMBERP X)
                      (CHARACTERP X)
                      (STRINGP X)
                      (SYMBOLP X))
                  (STATE-P STATE)
                  (SYMBOLP CHANNEL)
                  (OPEN-OUTPUT-CHANNEL-P CHANNEL :CHARACTER STATE)))
    T
    ACL2 !>
    

    Comment for advanced users: Function open-output-channel! is identical as a function to open-output-channel, except that the former may be called even during make-event expansion and clause-processor hints, but requires that there is an active trust tag (see defttag).

    Finally, we note that the community book books/misc/file-io.lisp contains useful file io functions whose definitions illustrate some of the features described above.




    acl2-sources/doc/HTML/IPRINT.html0000664002132200015000000000060112222333520015746 0ustar kaufmannacl2 IPRINT.html -- ACL2 Version 6.3

    IPRINT

    See set-iprint.
    Major Section:  IO
    




    acl2-sources/doc/HTML/IPRINTING.html0000664002132200015000000000060712222333520016312 0ustar kaufmannacl2 IPRINTING.html -- ACL2 Version 6.3

    IPRINTING

    See set-iprint.
    Major Section:  IO
    




    acl2-sources/doc/HTML/IRRELEVANT-FORMALS.html0000664002132200015000000000763012222333525017533 0ustar kaufmannacl2 IRRELEVANT-FORMALS.html -- ACL2 Version 6.3

    IRRELEVANT-FORMALS

    formals that are used but only insignificantly
    Major Section:  PROGRAMMING
    

    Let fn be a function of n arguments. Let x be the ith formal of fn. We say x is ``irrelevant in fn'' if x does not occur in either the guard or the measure for fn, and the value of (fn a1...ai...an) is independent of ai.

    The easiest way to define a function with an irrelevant formal is simply not to use the formal in the body of the function. Such formals are said to be ``ignored'' by Common Lisp and a special declaration is provided to allow ignored formals. ACL2 makes a distinction between ignored and irrelevant formals. Note however that if a variable is declared ignored or ignorable, then it will not be reported as irrelevant.

    An example of an irrelevant formal is x in the definition of fact below.

    (defun fact (i x)
      (declare (xargs :guard (and (integerp i) (<= 0 i))))
      (if (zerop i) 1 (* i (fact (1- i) (cons i x))))).
    
    Observe that x is only used in recursive calls of fact; it never ``gets out'' into the result. ACL2 can detect some irrelevant formals by a closure analysis on how the formals are used. For example, if the ith formal is only used in the ith argument position of recursive calls, then it is irrelevant. This is how x is used above.

    It is possible for a formal to appear only in recursive calls but still be relevant. For example, x is not irrelevant below, even though it only appears in the recursive call.

    (defun fn (i x) (if (zerop i) 0 (fn x (1- i))))
    
    The key observation above is that while x only appears in a recursive call, it appears in an argument position, namely i's, that is relevant. (The function above can be admitted with a :measure of (+ (nfix i) (nfix x)).)

    Establishing that a formal is irrelevant, in the sense defined above, can be an arbitrarily hard problem because it requires theorem proving. For example, is x irrelevant below?

    (defun test (i j k x) (if (p i j k) 0 x))
    
    Note that the value of (test i j k x) is independent of x -- thus making x irrelevant -- precisely if (p i j k) is a theorem. ACL2's syntactic analysis of a definition does not guarantee to notice all irrelevant formals.

    We regard the presence of irrelevant formals as an indication that something is wrong with the definition. We cause an error on such definitions and suggest that you recode the definition so as to eliminate the irrelevant formals. If you must have an irrelevant formal, one way to ``trick'' ACL2 into accepting the definition, without slowing down the execution of your function, is to use the formal in an irrelevant way in the guard. For example, to admit fact, above, with its irrelevant x one might use

    (defun fact (i x)
      (declare (xargs :guard (and (integerp i) (<= 0 i) (equal x x))))
      (if (zerop i) 0 (* i (fact (1- i) (cons i x)))))
    
    For those who really want to turn off this feature, we have provided a way to use the acl2-defaults-table for this purpose; see set-irrelevant-formals-ok.




    acl2-sources/doc/HTML/KEEP.html0000664002132200015000000000402212222333516015473 0ustar kaufmannacl2 KEEP.html -- ACL2 Version 6.3

    KEEP

    how we know if include-book read the correct files
    Major Section:  BOOKS
    

    The certificate (see certificate for general information) of a certified file is divided into two parts, a portcullis and a keep. These names come from castle lore. The keep is the strongest and usually tallest tower of a castle from which the entire courtyard can be surveyed by the defenders. The keep of a book is a list of file names and check sums used after the book has been included, to determine if the files read were (up to check sum) those certified.

    Once the portcullis is open, include-book can enter the book and read the event forms therein. The non-local event forms are in fact executed, extending the host theory. That may read in other books. When that has been finished, the keep of the certificate is inspected. The keep is a list of the book names which are included (hereditarily through all subbooks) in the certified book (including the certified book itself) together with the check sums of the objects in those books at the time of certification. We compare the check sums of the books just included to the check sums of the books stored in the keep. If differences are found then we know that the book or one of its subbooks has been changed since certification.

    See include-book to continue the guided tour through books.




    acl2-sources/doc/HTML/KEYWORD-COMMANDS.html0000664002132200015000000000563312222333521017277 0ustar kaufmannacl2 KEYWORD-COMMANDS.html -- ACL2 Version 6.3

    KEYWORD-COMMANDS

    how keyword commands are processed
    Major Section:  MISCELLANEOUS
    

    Examples:
    user type-in                 form evaluated
    :pc 5                        (ACL2::PC '5)
    :pcs app rev                 (ACL2::PCS 'app 'rev)
    :length (1 2 3)              (ACL2::LENGTH '(1 2 3))
    :quit                        (ACL2::QUIT) ; Note: avoid optional argument
    

    When a keyword, :key, is read as a command, ACL2 determines whether the symbol with the same name in the "ACL2" package, acl2::key, is a function or simple macro of n arguments. If so, ACL2 reads n more objects, obj1, ..., objn, and then acts as though it had read the following form (for a given key):

    (ACL2::key 'obj1 ... 'objn)
    
    Thus, by using the keyword command hack you avoid typing the parentheses, the "ACL2" package name, and the quotation marks.

    See ld-keyword-aliases for how to customize this behavior.

    Note the generality of this hack. Any function or macro in the "ACL2" package can be so invoked, not just ``commands.'' Indeed, there is no such thing as a distinguished class of commands. Users may take advantage of the keyword command hack by defining functions and macros in the "ACL2" package.

    The one caveat is that when the keyword hack is used to invoke a macro, only the required arguments for that macro are read before calling that macro: none of the &optional, &rest, &body, or &key arguments are read for that call. The macro is thus called with only its required arguments. The following log illustrates this caveat.

    ACL2 !>:set-iprint t
    
    ACL2 Query (:SET-IPRINT):  Action  (T, NIL, RESET, RESET-ENABLE, SAME,
    Q or ?):
    ACL2 Observation in SET-IPRINT:  Iprinting has been enabled.
    ACL2 !>
    
    What happened? First, the command :set-iprint was read. Since the macro set-iprint has no required arguments, the ACL2 evaluator was then called on the form (set-iprint), that is, calling the macro on no arguments. Set-iprint is defined to query the ACL2 user when its first argument is omitted. The log shows that query, which is set up to read the next form from the input stream. That form was available immediately: the form t that had been supplied by the user. So the query returned immediately and the set-iprint call was completed.




    acl2-sources/doc/HTML/KEYWORD-VALUE-LISTP.html0000664002132200015000000000133212222333524017636 0ustar kaufmannacl2 KEYWORD-VALUE-LISTP.html -- ACL2 Version 6.3

    KEYWORD-VALUE-LISTP

    recognizer for true lists whose even-position elements are keywords
    Major Section:  ACL2-BUILT-INS
    

    (keyword-value-listp l) is true if and only if l is a list of even length of the form (k1 a1 k2 a2 ... kn an), where each ki is a keyword.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/KEYWORD.html0000664002132200015000000000062512222333521016074 0ustar kaufmannacl2 KEYWORD.html -- ACL2 Version 6.3

    KEYWORD

    See keywordp.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/KEYWORDP.html0000664002132200015000000000213012222333524016210 0ustar kaufmannacl2 KEYWORDP.html -- ACL2 Version 6.3

    KEYWORDP

    recognizer for keywords
    Major Section:  ACL2-BUILT-INS
    

    (Keywordp x) is true if and only if x is a keyword, i.e., a symbol in the "KEYWORD" package. Such symbols are typically printed using a colon (:) followed by the symbol-name of the symbol.

    Keywordp has a guard of t.

    Keywordp is a Common Lisp function. See any Common Lisp documentation for more information. The following log may be illuminating.

    ACL2 !>(intern "ABC" "KEYWORD")
    :ABC
    ACL2 !>(symbol-name ':ABC)
    "ABC"
    ACL2 !>(symbol-package-name ':ABC)
    "KEYWORD"
    ACL2 !>
    

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/KWOTE-LST.html0000664002132200015000000000124512222333524016303 0ustar kaufmannacl2 KWOTE-LST.html -- ACL2 Version 6.3

    KWOTE-LST

    quote an arbitrary true list of objects
    Major Section:  ACL2-BUILT-INS
    

    The function kwote-lst applies the function kwote to each element of a given list. The guard of (kwote-lst lst) is (true-listp lst).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/KWOTE.html0000664002132200015000000000127412222333524015645 0ustar kaufmannacl2 KWOTE.html -- ACL2 Version 6.3

    KWOTE

    quote an arbitrary object
    Major Section:  ACL2-BUILT-INS
    

    For any object x, (kwote x) returns the two-element list whose elements are the symbol quote and the given x, respectively. The guard of (kwote x) is t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LAMBDA.html0000664002132200015000000000061312222333521015665 0ustar kaufmannacl2 LAMBDA.html -- ACL2 Version 6.3

    LAMBDA

    See term.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/LAST-PROVER-STEPS.html0000664002132200015000000000622112222333521017420 0ustar kaufmannacl2 LAST-PROVER-STEPS.html -- ACL2 Version 6.3

    LAST-PROVER-STEPS

    the number of prover steps most recently taken
    Major Section:  MISCELLANEOUS
    

    For discussions of prover step limits, See set-prover-step-limit and see with-prover-step-limit. The value of the form (last-prover-steps state) indicates the number of prover steps taken, in the sense described below, for the most recent context in which an event summary would normally be printed. Note that the value of (last-prover-steps state) is updated for all events, and for all other forms such as calls of thm or certify-book, that would print a summary -- regardless of whether or not such output is inhibited (see set-inhibit-output-lst and see set-inhibited-summary-types). In particular, the value is updated (typically to nil) for table events, even when no summary is printed; for example, the value is updated to nil for table events such as (logic), (program), and even calls of set-prover-step-limit.

    The value of (last-prover-steps state) is determined as follows, based on the most recent summary context (as described above):

    nil, if no prover steps were taken; else,

    the (positive) number of steps taken, if the number of steps did not exceed the starting limit; else,

    the negative of the starting limit.

    We conclude with a remark for advanced users who wish to invoke last-prover-steps in the development of utilities that track prover steps. Suppose that you want to write a utility that takes some action based on the number of prover steps performed by the first defun event that is generated, among others, for example the number of prover steps taken to admit f1 in the following example.

    (progn (defun f1 ...)
           (defun f2 ...))
    
    A solution is to record the steps taken by the first defun before executing subsequent events, as follows (see make-event).
    (progn (defun f1 ...)
           (make-event (pprogn (f-put-global 'my-step-count
                                             (last-prover-steps state)
                                             state)
                               (value '(value-triple nil))))
           (defun f2 ...))
    




    acl2-sources/doc/HTML/LAST.html0000664002132200015000000000206412222333524015515 0ustar kaufmannacl2 LAST.html -- ACL2 Version 6.3

    LAST

    the last cons (not element) of a list
    Major Section:  ACL2-BUILT-INS
    

    (Last l) is the last cons of a list. Here are examples.

    ACL2 !>(last '(a b . c))
    (B . C)
    ACL2 !>(last '(a b c))
    (C)
    

    (Last l) has a guard of (listp l); thus, l need not be a true-listp.

    Last is a Common Lisp function. See any Common Lisp documentation for more information. Unlike Common Lisp, we do not allow an optional second argument for last.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LD-ERROR-ACTION.html0000664002132200015000000002004012222333521017102 0ustar kaufmannacl2 LD-ERROR-ACTION.html -- ACL2 Version 6.3

    LD-ERROR-ACTION

    determines ld's response to an error
    Major Section:  MISCELLANEOUS
    

    Ld-error-action is an ld special (see ld). The accessor is (ld-error-action state) and the updater is (set-ld-error-action val state). Ld-error-action must be :continue, :return, :return!, or :error. The initial value of ld-error-action is :continue, which means that the top-level ACL2 command loop will not exit when an error is caused by user-typein. But the default value for ld-error-action on calls of ld is :return!.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-error-action is one of them. Suppose that ld-error-triples is t and a form evaluates to an error triple (mv erp val state); see error-triples. If the ``error component'', erp, is non-nil, then an error is said to have occurred. If an error occurs, ld's action depends on ld-error-action. If it is :continue, ld just continues processing the forms in standard-oi. If it is :return or :return!, ld stops and returns as though it had emptied the channel. If it is :error, ld stops and returns, signalling an error to its caller by returning an error triple with non-nil error component, and reverting the logical world to its value just before that call of ld.

    To see this effect of :ERROR for ld-error-action, consider the following example.

    (ld '((defun f (x) x) (defun bad (x)) (defun g (x) x)))
    
    When the defun of bad fails (because its body is missing), evaluation of the ld call stops; thus, the defun of g is not evaluated. The definition of f will be removed from the logical world before the call of ld returns.

    However, by default each user call of ld is made with a ld-error-action of :RETURN! (not :ERROR). In the common case that all nested calls of ld inside the ACL2 loop are made this way, an error will not roll back the logical world. However, it will still halt evaluation of forms for the current call of ld and any parent calls of ld (other than the call made on behalf of lp that entered the ACL2 loop in the first place), as though there were no more forms to evaluate.

    We have already discussed the behavior of ld when an error occurs. But there is another case when ld can stop processing more forms: when ld-error-action is not :CONTINUE, ld-error-triples is t, and evaluation of a form returns an error triple (mv nil val state), where nil is the error component and whose ``value component'', val is a cons pair whose car is the symbol :STOP-LD. Let val be the pair (:STOP-LD . x). Then the call of ld returns the error triple (mv nil (:STOP-LD n . x) state), where n is the value of state global variable 'ld-level at the time of termination. The following example illustrates how this works.

    (ld '((defun f1 (x) x)
          (ld '((defun f2 (x) x)
                (mv nil '(:STOP-LD my-error more-info) state)
                (defun g2 (x) x)))
          (defun g1 (x) x)))
    
    The outer call of ld returns the value
    (:STOP-LD 2 3 MY-ERROR MORE-INFO)
    
    and leaves us in a world the includes definitions for f1 and f2, but no definition for g1 or g2 since neither of their two defun forms was evaluated. The value of state global 'ld-level is incremented from 1 to 2 when the outer ld is entered and then again to 3 when the inner ld is entered. When the inner ld escounters the error triple (mv nil (:STOP-LD my-error more-info) state), it sees :STOP-LD in the car of the value component and pushes the current value of 'ld-level, 3, onto the cdr of that value, to return the value triple (mv nil (:STOP-LD my-error 3 more-info) state). The outer of ld then sees this value and returns (mv nil (:STOP-LD my-error 2 3 more-info) state), since its current value of 'ld-level is 2 after the inner ld exits.

    That concludes our discussion of how these special :STOP-LD values are handled; but how are they created? While they can be created directly by evaluation results as suggested in the example above, that is not the standard way. Rather, ld returns an error triple (mv nil (:STOP-LD n) state), where n is the value of variable ld-level at the time of termination, when the following conditions hold: an error occurs, ld-error-action is RETURN! (which is the default), and ld-error-triples is t (the default). The following example, which is a bit similar to the preceding one, illustrates both creation and handling of the special :STOP-LD values.

    (ld '((defun f1 (x) x)
          (ld '((defun f2 (x) x)
                (ld '((defun f3 (x) x)
                      (defun bad (x)) ; ERROR -- missing the body
                      (defun g3 (x) x)))
                (defun g2 (x) x)))
          (defun g1 (x) x)))
    
    The result is that f1, f2, and f3 are defined, but none of g1, g2, or g3 is defined. Let's see why. The innermost call of ld has a default :ld-error-action of :RETURN! (as do the other calls). So when the definition of bad fails, then the innermost ld returns (mv nil (:STOP-LD 4) state). The middle ld sees this value, and since its :ld-error-action is not :CONTINUE (because it has the default value of :RETURN!), it returns before considering the definition of g2, with value (mv nil (:STOP-LD 3 4) state). The topmost call of ld similarly sees the :STOP-LD; stops evaluation of forms, without defining g1; and returns (mv nil (:STOP-LD 2 3 4) state).




    acl2-sources/doc/HTML/LD-ERROR-TRIPLES.html0000664002132200015000000000352612222333521017261 0ustar kaufmannacl2 LD-ERROR-TRIPLES.html -- ACL2 Version 6.3

    LD-ERROR-TRIPLES

    determines whether a form caused an error during ld
    Major Section:  MISCELLANEOUS
    

    Ld-error-triples is an ld special (see ld). The accessor is (ld-error-triples state) and the updater is (set-ld-error-triples val state). Ld-error-triples must be either t or nil. The initial value of ld-error-triples is t.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-error-triples is one of them. If this variable has the value t then when a form evaluates to an error triple (mv erp val state) (see error-triples) such that erp is non-nil, then an error is deemed to have occurred. When an error occurs in evaluating a form, ld rolls back the ACL2 world to the configuration it had at the conclusion of the last error-free form. Then ld takes the action determined by ld-error-action.




    acl2-sources/doc/HTML/LD-EVISC-TUPLE.html0000664002132200015000000000347012222333521017006 0ustar kaufmannacl2 LD-EVISC-TUPLE.html -- ACL2 Version 6.3

    LD-EVISC-TUPLE

    determines whether ld suppresses details when printing
    Major Section:  MISCELLANEOUS
    

    Ld-evisc-tuple is an ld special (see ld). The accessor is (ld-evisc-tuple state) and an updater is (set-ld-evisc-tuple val state), although the use of set-evisc-tuple is preferred for updating. Ld-evisc-tuple must be either nil, which is its initial value, or a legal evisc-tuple: see set-evisc-tuple.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-evisc-tuple is one of them. Ld may print the forms it is evaluating and/or the results of evaluation. Depending on the value of ld-evisc-tuple ld may ``eviscerate'' objects before printing them. See set-evisc-tuple for a discussion of evisceration and of how other evisc-tuples affect the printing of error messages and warnings, as well as other output not from ld.




    acl2-sources/doc/HTML/LD-KEYWORD-ALIASES.html0000664002132200015000000001154312222333531017452 0ustar kaufmannacl2 LD-KEYWORD-ALIASES.html -- ACL2 Version 6.3

    LD-KEYWORD-ALIASES

    abbreviation of some keyword commands
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-ld-keyword-aliases '((:q 0 q-fn)
                              (:e 0 exit-acl2-macro))
                            state)
    (ld-keyword-aliases state) ; current value of the ld-keyword-aliases table
    
    Ld-keyword-aliases is the name of a ACL2 table (see table) and also the name of a function of state that returns the value of this table. That value must be an alist, each element of which is of the form (:keyword n fn), where :keyword is a keyword, n is a nonnegative integer, and fn is a function symbol of arity n, a macro symbol, or a lambda expression of arity n. When keyword is typed as an ld command, n more forms are read, x1, ..., xn, and the form (fn 'x1 ... 'xn) is then evaluated. The initial value of the ld-keyword-aliases table is nil.

    ACL2 provides functions to modify the ld-keyword-aliases table, as follows.

    (Set-ld-keyword-aliases val state): sets the table to val, which must be a legal alist as described above. This is an event that may go into a book (see events), but its effect will be local to that book.

    Set-ld-keyword-aliases! is the same as set-ld-keyword-aliases, except that its effect is not local. Indeed, the form (set-ld-keyword-aliases val state) is equivalent to the form (local (set-ld-keyword-aliases! val state).

    (Add-ld-keyword-alias key val state): modifies the table by binding the keyword key to val, which must be a legal value as described above. This is an event that may go into a book (see events), but its effect will be local to that book.

    Add-ld-keyword-alias! is the same as add-ld-keyword-alias, except that its effect is not local. Indeed, the form (add-ld-keyword-alias key val state) is equivalent to the form (local (add-ld-keyword-alias! key val state).

    Consider the first example above:

    (set-ld-keyword-aliases '((:q 0 q-fn)
                              (:e 0 exit-acl2-macro))
                            state)
    
    With this event, :q is redefined to have the effect of executing (q-fn), so for example if you have defined q-fn with
    (defmacro q-fn ()
      '(er soft 'q "You un-bound :q and now we have a soft error."))
    
    then :q will cause an error, and if you have defined
    (defmacro exit-acl2-macro () '(exit-ld state))
    
    then :e will cause the effect (it so happens) that :q normally has. If you prefer :e to :q for exiting the ACL2 loop, you might even want to put such definitions of q-fn and exit-acl2-macro together with the set-ld-keyword-aliases form above into your "acl2-customization.lsp" file; see acl2-customization.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-keyword-aliases is one of them. Ld-keyword-aliases affects how keyword commands are parsed. Generally speaking, ld's command interpreter reads ``:fn x1 ... xn'' as ``(fn 'x1 ... 'xn)'' when :fn is a keyword and fn is the name of an n-ary function; see keyword-commands. But this parse is overridden, as described above, for the keywords bound in the ld-keyword-aliases table.




    acl2-sources/doc/HTML/LD-MISSING-INPUT-OK.html0000664002132200015000000000266112222333521017564 0ustar kaufmannacl2 LD-MISSING-INPUT-OK.html -- ACL2 Version 6.3

    LD-MISSING-INPUT-OK

    determines which forms ld evaluates
    Major Section:  MISCELLANEOUS
    

    ld-missing-input-ok is an ld special (see ld). The accessor is (ld-missing-input-ok state) and the updater is (set-ld-missing-input-ok val state). ld-missing-input-ok must be either nil, t, or :warn. The initial value of ld-missing-input-ok is nil.

    The general-purpose ACL2 read-eval-print loop, ld, is controlled by various flags that control its behavior, and ld-missing-input-ok is one of them. In brief, the first argument of ld can indicate a file from which to read input. If the file does not exist, it is an error by default, but ld becomes essentially a no-op if t or :warn is supplied for :ld-missing-input-ok, where :warn prints a warning. Also see ld.




    acl2-sources/doc/HTML/LD-POST-EVAL-PRINT.html0000664002132200015000000001162412222333521017452 0ustar kaufmannacl2 LD-POST-EVAL-PRINT.html -- ACL2 Version 6.3

    LD-POST-EVAL-PRINT

    determines whether and how ld prints the result of evaluation
    Major Section:  MISCELLANEOUS
    

    Ld-post-eval-print is an ld special (see ld). The accessor is (ld-post-eval-print state) and the updater is (set-ld-post-eval-print val state). Ld-post-eval-print must be either t, nil, or :command-conventions. The initial value of ld-post-eval-print is :command-conventions.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-post-eval-print is one of them. If this global variable is t, ld prints the result. In the case of a form that produces a multiple value, ld prints the list containing all the values (which, logically speaking, is what the form returned). If ld-post-eval-print is nil, ld does not print the values. This is most useful when ld is used to load a previously processed file.

    Finally, if ld-post-eval-print is :command-conventions and also ld-error-triples is t, then ld prints the result but treats ``error triples'' specially. An ``error triple'' (see error-triples) is a result, (mv erp val state), that consists of two ordinary values and state. Many ACL2 functions use such triples to signal errors. The convention is that if erp (the first value) is nil, then the function is returning val (the second value) as its conventional single result and possibly side-effecting state (as with some output). If erp is t, then an error has been caused, val is irrelevant and the error message has been printed in the returned state. Example ACL2 functions that follow this convention include defun and in-package. If such ``error producing'' functions are evaluated while ld-post-eval-print is set to t, then you would see them producing lists of length 3. This is disconcerting to users accustomed to Common Lisp (where these functions produce single results but sometimes cause errors or side-effect state). For more information about error triples, see programming-with-state.

    When ld-post-eval-print is :command-conventions and a form produces an error triple (mv erp val state) as its value, ld prints nothing if erp is non-nil and otherwise ld prints just val. Because it is a misrepresentation to suggest that just one result was returned, ld prints the value of the global variable 'triple-print-prefix before printing val. 'triple-print-prefix is initially " ", which means that when non-erroneous error triples are being abbreviated to val, val appears one space off the left margin instead of on the margin.

    In addition, when ld-post-eval-print is :command-conventions and the value component of an error triple is the keyword :invisible then ld prints nothing. This is the way certain commands (e.g., :pc) appear to return no value.

    By printing nothing when an error has been signalled, ld makes it appear that the error (whose message has already appeared in state) has ``thrown'' the computation back to load without returning a value. By printing just val otherwise, we suppress the fact that state has possibly been changed.




    acl2-sources/doc/HTML/LD-PRE-EVAL-FILTER.html0000664002132200015000000000420512222333521017401 0ustar kaufmannacl2 LD-PRE-EVAL-FILTER.html -- ACL2 Version 6.3

    LD-PRE-EVAL-FILTER

    determines which forms ld evaluates
    Major Section:  MISCELLANEOUS
    

    Ld-pre-eval-filter is an ld special (see ld). The accessor is (ld-pre-eval-filter state) and the updater is (set-ld-pre-eval-filter val state). Ld-pre-eval-filter must be either :all, :query, or a new name that could be defined (e.g., by defun or defconst). The initial value of ld-pre-eval-filter is :all.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-pre-eval-filter is one of them. If the filter is :all, then every form read is evaluated. If the filter is :query, then after a form is read it is printed to standard-co and the user is asked if the form is to be evaluated or skipped. If the filter is a new name, then all forms are evaluated until that name is introduced, at which time ld terminates normally.

    The :all filter is, of course, the normal one. :Query is useful if you want to replay selected the commands in some file. The new name filter is used if you wish to replay all the commands in a file up through the introduction of the given one.




    acl2-sources/doc/HTML/LD-PRE-EVAL-PRINT.html0000664002132200015000000000545012222333521017313 0ustar kaufmannacl2 LD-PRE-EVAL-PRINT.html -- ACL2 Version 6.3

    LD-PRE-EVAL-PRINT

    determines whether ld prints the forms to be eval'd
    Major Section:  MISCELLANEOUS
    

    Ld-pre-eval-print is an ld special (see ld). The accessor is (ld-pre-eval-print state) and the updater is (set-ld-pre-eval-print val state). Ld-pre-eval-print must be either t, nil, or :never. The initial value of ld-pre-eval-print is nil.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-pre-eval-print is one of them. If this global variable is t, then before evaluating the form just read from standard-oi, ld prints the form to standard-co. If the variable is nil, no such printing occurs. The t option is useful if you are reading from a file of commands and wish to assemble a complete script of the session in standard-co.

    The value :never of ld-pre-eval-print is rarely used. During the evaluation of encapsulate and of certify-book forms, subsidiary events are normally printed, even if ld-pre-eval-print is nil. Thus for example, when the user submits an encapsulate form, all subsidiary events are generally printed even in the default situation where ld-pre-eval-print is nil. But occasionally one may want to suppress such printing. In that case, ld-pre-eval-print should be set to :never. As described elsewhere (see set-inhibit-output-lst), another way to suppress such printing is to execute (set-inhibit-output-lst lst) where lst evaluates to a list including 'prove and 'event.




    acl2-sources/doc/HTML/LD-PROMPT.html0000664002132200015000000000631612222333521016271 0ustar kaufmannacl2 LD-PROMPT.html -- ACL2 Version 6.3

    LD-PROMPT

    determines the prompt printed by ld
    Major Section:  MISCELLANEOUS
    

    Ld-prompt is an ld special (see ld). The accessor is (ld-prompt state) and the updater is (set-ld-prompt val state). Ld-prompt must be either nil, t, or a function symbol that, when given an open output character channel and state, prints the desired prompt to the channel and returns two values: the number of characters printed and the state. The initial value of ld-prompt is t.

    The general-purpose ACL2 read-eval-print loop, ld, reads forms from standard-oi, evaluates them and prints the result to standard-co. However, there are various flags that control ld's behavior and ld-prompt is one of them. Ld-prompt determines whether ld prints a prompt before reading the next form from standard-oi. If ld-prompt is nil, ld prints no prompt. If ld-prompt is t, the default prompt printer is used, which displays information that includes the current package, default defun-mode, guard checking status (on or off), and ld-skip-proofsp; see default-print-prompt.

    If ld-prompt is neither nil nor t, then it should be a function name, fn, such that (fn channel state) will print the desired prompt to channel in state and return (mv col state), where col is the number of characters output (on the last line output). You may define your own prompt printing function.

    If you supply an inappropriate prompt function, i.e., one that causes an error or does not return the correct number and type of results, the following odd prompt will be printed instead:

    Bad Prompt
    See :DOC ld-prompt>
    
    which will lead you to this message. You should either call ld appropriately next time or assign an appropriate value to ld-prompt.




    acl2-sources/doc/HTML/LD-QUERY-CONTROL-ALIST.html0000664002132200015000000000757712222333521020157 0ustar kaufmannacl2 LD-QUERY-CONTROL-ALIST.html -- ACL2 Version 6.3

    LD-QUERY-CONTROL-ALIST

    how to default answers to queries
    Major Section:  MISCELLANEOUS
    

    Ld-query-control-alist is an ld special (see ld). The accessor is (ld-query-control-alist state) and the updater is (set-ld-query-control-alist val state). Roughly speaking, ld-query-control-alist is either nil (meaning all queries should be interactive), t (meaning all should default to the first accepted response), or an alist that pairs query ids to keyword responses. The alist may end in either t or nil, indicating the default value for all ids not listed explicitly. Formally, the ld-query-control-alist must satisfy ld-query-control-alistp. The initial ld-query-control-alist is nil, which means all queries are handled interactively.

    When an ACL2 query is raised, a unique identifying symbol is printed in parentheses after the word ``Query''. This symbol, called the ``query id,'' can be used in conjunction with ld-query-control-alist to prevent the query from being handled interactively. By ``handled interactively'' we mean that the query is printed to *standard-co* and a response is read from *standard-oi*. The alist can be used to obtain a ``default value'' for each query id. If this value is nil, then the query is handled interactively. In all other cases, the system handles the query without interaction (although text may be printed to standard-co to make it appear that an interaction has occurred; see below). If the default value is t, the system acts as though the user responded to the query by typing the first response listed among the acceptable responses. If the default value is neither nil nor t, then it must be a keyword and one of the acceptable responses. In that case, the system acts as though the user responded with the given keyword.

    Next, we discuss how the ld-query-control-alist assigns a default value to each query id. It assigns each id the first value paired with the id in the alist, or, if no such pair appears in the alist, it assigns the final cdr of the alist as the value. Thus, nil assigns all ids nil. T assigns all ids t. '((:filter . nil) (:sysdef . :n) . t) assigns nil to the :filter query, :n to the :sysdef query, and t to all others.

    It remains only to discuss how the system prints text when the default value is not nil, i.e., when the query is handled without interaction. In fact, it is allowed to pair a query id with a singleton list containing a keyword, rather than a keyword, and this indicates that no printing is to be done. Thus for the example above, :sysdef queries would be handled noninteractively, with printing done to simulate the interaction. But if we change the example so that :sysdef is paired with (:n), i.e., if ld-query-control-alist is '((:filter . nil) (:sysdef :n) . t), then no such printing would take place for sysdef queries. Instead, the default value of :n would be assigned ``quietly''.




    acl2-sources/doc/HTML/LD-REDEFINITION-ACTION.html0000664002132200015000000002601212222333521020075 0ustar kaufmannacl2 LD-REDEFINITION-ACTION.html -- ACL2 Version 6.3

    LD-REDEFINITION-ACTION

    to allow redefinition without undoing
    Major Section:  MISCELLANEOUS
    

    Ld-redefinition-action is an ld special (see ld). The accessor is (ld-redefinition-action state) and the updater is (set-ld-redefinition-action val state).

    WARNING! If ld-redefinition-action is non-nil then ACL2 is liable to be made unsafe or unsound either by ill-considered definitions, or because redefining a macro or inlined function called in the body of a function, g, may not cause the new definition to be called by g. The keyword command :redef will set ld-redefinition-action to a convenient setting allowing unsound redefinition. See below.

    When ld-redefinition-action is nil, redefinition is prohibited. In that case, an error message is printed upon any attempt to introduce a name that is already in use. There is one exception to this rule. It is permitted to redefine a function symbol in :program mode to be a function symbol in :logic mode provided the formals and body remain the same. This is the standard way a function ``comes into'' logical existence.

    Throughout the rest of this discussion we exclude from our meaning of ``redefinition'' the case in which a function in :program mode is identically redefined in :logic mode. At one time, ACL2 freely permitted the signature-preserving redefinition of :program mode functions but it no longer does. See redefining-programs.

    When ld-redefinition-action is non-nil, you are allowed to redefine a name that is already in use. The system may be rendered unsound by such an act. It is important to understand how dangerous redefinition is. Suppose fn is a function symbol that is called from within some other function, say g. Suppose fn is redefined so that its arity changes. Then the definition of g is rendered syntactically ill-formed by the redefinition. This can be devastating since the entire ACL2 system assumes that terms in its database are well-formed. For example, if ACL2 executes g by running the corresponding function in raw Common Lisp the redefinition of fn may cause raw lisp to break in irreparable ways. As Lisp programmers we live with this all the time by following the simple rule: after changing the syntax of a function don't run any function that calls it via its old syntax. This rule also works in the context of the evaluation of ACL2 functions, but it is harder to follow in the context of ACL2 deductions, since it is hard to know whether the database contains a path leading the theorem prover from facts about one function to facts about another. Finally, of course, even if the database is still syntactically well-formed there is no assurance that all the rules stored in it are valid. For example, theorems proved about g survive the redefinition of fn but may have crucially depended on the properties of the old fn. In summary, we repeat the warning: all bets are off if you set ld-redefinition-action to non-nil.

    ACL2 provides some enforcement of the concern above, by disabling certify-book if any world-changing events exist in the certification world that were executed with a non-nil value of 'ld-redefinition-action. (This value is checked at the end of each top-level command, but the value does not change during evaluation of embedded event forms; see embedded-event-form.)

    If at any point in a session you wish to see the list of all names that have been redefined, see redefined-names.

    That said, we'll give you enough rope to hang yourself. When ld-redefinition-action is non-nil, it must be a pair, (a . b). The value of a determines how the system interacts with you when a redefinition is submitted. The value of b allows you to specify how the property list of the redefined name is to be ``renewed'' before the redefinition.

    There are several dimensions to the space of possibilities controlled by part a: Do you want to be queried each time you redefine a name, so you can confirm your intention? (We sometimes make typing mistakes or simply forget we have used that name already.) Do you want to see a warning stating that the name has been redefined? Do you want ACL2 system functions given special protection from possible redefinition? Below are the choices for part a:

    :query -- every attempt to redefine a name will produce a query. The query will allow you to abort the redefinition or proceed. It will will also allow you to specify the part b for this redefinition. :Query is the recommended setting for users who wish to dabble in redefinition.

    :warn -- any user-defined function may be redefined but a post-redefinition warning is printed. The attempt to redefine a system name produces a query. If you are prototyping and testing a big system in ACL2 this is probably the desired setting for part a.

    :doit -- any user-defined function may be redefined silently (without query or warning) but when an attempt is made to redefine a system function, a query is made. This setting is recommended when you start making massive changes to your prototyped system (and tire of even the warning messages issued by :warn).

    In support of our own ACL2 systems programming there are two other settings. We suggest ordinary users not use them.

    :warn! -- every attempt to redefine a name produces a warning but no query. Since ACL2 system functions can be redefined this way, this setting should be used by the only-slightly-less-than supremely confident ACL2 system hacker.

    :doit! -- this setting allows any name to be redefined silently (without query or warnings). ACL2 system functions are fair game. This setting is reserved for the supremely confident ACL2 system hacker. (Actually, this setting is used when we are loading massively modified versions of the ACL2 source files.)

    Part b of ld-redefinition-action tells the system how to ``renew'' the property list of the name being redefined. There are two choices:

    :erase -- erase all properties stored under the name, or

    :overwrite -- preserve existing properties and let the redefining overwrite them.

    It should be stressed that neither of these b settings is guaranteed to result in an entirely satisfactory state of affairs after the redefinition. Roughly speaking, :erase returns the property list of the name to the state it was in when the name was first introduced. Lemmas, type information, etc., stored under that name are lost. Is that what you wanted? Sometimes it is, as when the old definition is ``completely wrong.'' But other times the old definition was ``almost right'' in the sense that some of the work done with it is still (intended to be) valid. In that case, :overwrite might be the correct b setting. For example if fn was a function and is being re-defun'd with the same signature, then the properties stored by the new defun should overwrite those stored by the old defun but the properties stored by defthms will be preserved.

    In addition, neither setting will cause ACL2 to erase properties stored under other symbols! Thus, if FOO names a rewrite rule which rewrites a term beginning with the function symbol BAR and you then redefine FOO to rewrite a term beginning with the function symbol BAZ, then the old version of FOO is still available (because the rule itself was added to the rewrite rules for BAR, whose property list was not cleared by redefining FOO).

    The b setting is only used as the default action when no query is made. If you choose a setting for part a that produces a query then you will have the opportunity, for each redefinition, to specify whether the property list is to be erased or overwritten.

    The keyword command :redef sets ld-redefinition-action to the pair (:query . :overwrite). Since the resulting query will give you the chance to specify :erase instead of :overwrite, this setting is quite convenient. But when you are engaged in heavy-duty prototyping, you may wish to use a setting such as :warn or even :doit. For that you will have to invoke a form such as:

    (set-ld-redefinition-action '(:doit . :overwrite) state) .
    

    Encapsulate causes somewhat odd interaction with the user if redefinition occurs within the encapsulation because the encapsulated event list is processed several times. For example, if the redefinition action causes a query and a non-local definition is actually a redefinition, then the query will be posed twice, once during each pass. C'est la vie.

    Finally, it should be stressed again that redefinition is dangerous because not all of the rules about a name are stored on the property list of the name. Thus, redefinition can render ill-formed terms stored elsewhere in the database or can preserve now-invalid rules. See redundant-events, in particular the section ``Note About Unfortunate Redundancies,'' for more discussion of potential pitfalls of redefinition.




    acl2-sources/doc/HTML/LD-SKIP-PROOFSP.html0000664002132200015000000001746012222333521017146 0ustar kaufmannacl2 LD-SKIP-PROOFSP.html -- ACL2 Version 6.3

    LD-SKIP-PROOFSP

    how carefully ACL2 processes your commands
    Major Section:  MISCELLANEOUS
    

    Examples:
    ACL2 !>(set-ld-skip-proofsp t state)
     T
    ACL2 !s>(set-ld-skip-proofsp nil state)
     NIL
    ACL2 !>(set-ld-skip-proofsp 'include-book state)
     INCLUDE-BOOK
    ACL2 !s>
    

    A global variable in the ACL2 state, called 'ld-skip-proofsp, determines the thoroughness with which ACL2 processes your commands. This variable may take on one of three values: t, nil or 'include-book. When ld-skip-proofsp is non-nil, the system assumes that which ought to be proved and is thus unsound. The form (set-ld-skip-proofsp flg state) is the general-purpose way of setting ld-skip-proofsp. This global variable is an ``ld special,'' which is to say, you may call ld in such a way as to ``bind'' this variable for the dynamic extent of the ld.

    When ld-skip-proofsp is non-nil, the default prompt displays the character s. Thus, the prompt

    ACL2 !s>
    
    means that the default defun-mode is :logic (otherwise the character p, for :program, would also be printed; see default-print-prompt) but ``proofs are being skipped.''

    Observe that there are two legal non-nil values, t and 'include-book. When ld-skip-proofsp is t, ACL2 skips all proof obligations but otherwise performs all other required analysis of input events. When ld-skip-proofsp is 'include-book, ACL2 skips not only proof obligations but all analysis except that required to compute the effect of successfully executed events. To explain the distinction, let us consider one particular event, say a defun. Very roughly speaking, a defun event normally involves a check of the syntactic well-formedness of the submitted definition, the generation and proof of the termination conditions, and the computation and storage of various rules such as a :definition rule and some :type-prescription rules. By ``normally'' above we mean when ld-skip-proofsp is nil. How does a defun behave when ld-skip-proofsp is non-nil?

    If ld-skip-proofsp is t, then defun performs the syntactic well-formedness checks and computes and stores the various rules, but it does not actually carry out the termination proofs. If ld-skip-proofsp is 'include-book, defun does not do the syntactic well-formedness check nor does it carry out the termination proof. Instead, it merely computes and stores the rules under the assumption that the checks and proofs would all succeed. Observe that a setting of 'include-book is ``stronger'' than a setting of t in the sense that 'include-book causes defun to assume even more about the admissibility of the event than t does.

    As one might infer from the choice of name, the include-book event sets ld-skip-proofsp to 'include-book when processing the events in a book being loaded. Thus, include-book does the miminal work necessary to carry out the effects of every event in the book. The syntactic checks and proof obligations were, presumably, successfully carried out when the book was certified.

    A non-nil value for ld-skip-proofsp also affects the system's output messages. Event summaries (the paragraphs that begin ``Summary'' and display the event forms, rules used, etc.) are not printed when ld-skip-proofsp is non-nil. Warnings and observations are printed when ld-skip-proofsp is t but are not printed when it is 'include-book.

    Intuitively, ld-skip-proofsp t means skip just the proofs and otherwise do all the work normally required for an event; while ld-skip-proofsp 'include-book is ``stronger'' and means do as little as possible to process events. In accordance with this intuition, local events are processed when ld-skip-proofsp is t but are skipped when ld-skip-proofsp is 'include-book.

    The ACL2 system itself uses only two settings, nil and 'include-book, the latter being used only when executing the events inside of a book being included. The ld-skip-proofsp setting of t is provided as a convenience to the user. For example, suppose one has a file of events. By loading it with ld with ld-skip-proofsp set to t, the events can all be checked for syntactic correctness and assumed without proof. This is a convenient way to recover a state lost by a system crash or to experiment with a modification of an events file.

    The foregoing discussion is actually based on a lie. ld-skip-proofsp is allowed two other values, 'initialize-acl2 and 'include-book-with-locals. The first causes behavior similar to t but skips local events and avoids some error checks that would otherwise prevent ACL2 from properly booting. The second is identical to 'include-book but also executes local events. These additional values are not intended for use by the user, but no barriers to their use have been erected.

    We close by reminding the user that ACL2 is potentially unsound if ld-skip-proofsp is ever set by the user. We provide access to it simply to allow experimentation and rapid reconstruction of lost or modified logical worlds.




    acl2-sources/doc/HTML/LD-VERBOSE.html0000664002132200015000000000450712222333521016355 0ustar kaufmannacl2 LD-VERBOSE.html -- ACL2 Version 6.3

    LD-VERBOSE

    determines whether ld prints ``ACL2 Loading ...''
    Major Section:  MISCELLANEOUS
    

    Ld-verbose is an ld special (see ld). The accessor is (ld-verbose state) and the updater is (set-ld-verbose val state). Ld-verbose must be t, nil or a string or consp suitable for fmt printing via the ~@ command. The initial value of ld-verbose is a fmt message that prints the ACL2 version number, ld level and connected book directory.

    Before processing the forms in standard-oi, ld may print a header. The printing of this header is controlled by ld-verbose. If ld-verbose is nil, no header is printed. If it is t, ld prints the message

       ACL2 loading <file>
    
    where <file> is the input channel supplied to ld. A similar message is printed when ld completes. If ld-verbose is neither t nor nil then it is presumably a header and is printed with the ~@ fmt directive before ld begins to read and process forms. In this case the ~@ fmt directive is interpreted in an environment in which #\v is the ACL2 version string, #\l is the level of the current recursion in ld and/or wormhole, and #\c is the connected book directory (cbd).




    acl2-sources/doc/HTML/LD.html0000664002132200015000000004626612222333522015263 0ustar kaufmannacl2 LD.html -- ACL2 Version 6.3

    LD

    the ACL2 read-eval-print loop, file loader, and command processor
    Major Section:  OTHER
    

    Examples:
    (LD "foo.lisp")              ; read and evaluate each form in file
                                 ; "foo.lisp", in order
    (LD "foo.lisp" :ld-pre-eval-print t)
                                 ; as above, but print each form to standard
                                 ; character output just before it is evaluated
    
    General Form:
    (LD standard-oi                  ; open obj in channel, stringp file name
                                     ; to open and close, or list of forms
                                     ; Optional keyword arguments:
        :dir                ...      ; use this add-include-book-dir directory
        :standard-co        ...      ; open char out or file to open and close
        :proofs-co          ...      ; open char out or file to open and close
        :current-package    ...      ; known package name
        :ld-skip-proofsp    ...      ; nil, 'include-book, or t
                                     ;   (see ld-skip-proofsp)
        :ld-redefinition-action ...  ; nil or '(:a . :b)
        :ld-prompt          ...      ; nil, t, or some prompt printer fn
        :ld-missing-input-ok ...     ; nil, t, :warn, or warning message
        :ld-pre-eval-filter ...      ; :all, :query, or some new name
        :ld-pre-eval-print  ...      ; nil, t, or :never
        :ld-post-eval-print ...      ; nil, t, or :command-conventions
        :ld-evisc-tuple     ...      ; nil or '(alist nil nil level length)
        :ld-error-triples   ...      ; nil or t
        :ld-error-action    ...      ; :return!, :return, :continue or :error
        :ld-query-control-alist ...  ; alist supplying default responses
        :ld-verbose         ...)     ; nil or t
    
    
    

    Some Related Topics

    Ld is the top-level ACL2 read-eval-print loop. (When you call lp, a little initialization is done in raw Common Lisp and then ld is called.) Ld is also a general-purpose ACL2 file loader and a command interpreter. Ld is actually a macro that expands to a function call involving state. Ld returns an ``error triple'' (mv erp val state) as explained below. (For much more on error triples, see programming-with-state.)

    See rebuild for a variant of ld that skips proofs. See output-to-file for examples showing how to redirect output to a file.

    The arguments to ld, except for :dir, all happen to be global variables in state (see state and see programming-with-state). For example, 'current-package and 'ld-verbose are global variables, which may be accessed via (@ current-package) and (@ ld-verbose). When ld is called, it ``binds'' these variables. By ``binds'' we actually mean the variables are globally set but restored to their old values on exit. Because ld provides the illusion of state global variables being bound, they are called ``ld specials'' (after the Lisp convention of calling a variable ``special'' if it is referenced freely after having been bound).

    Note that all arguments but the first are passed via keyword. Any variable not explicitly given a value in a call retains its pre-call value, with the exception of :ld-error-action, which defaults to :return! if not explicitly specified.

    Just as an example to drive the point home: If current-package is "ACL2" and you typed

    (ld *standard-oi* :current-package "MY-PKG")
    
    you would find yourself in (an inner) read-eval-print loop in which the current-package was "MY-PKG". You could operate there as long as you wished, changing the current package at will. But when you typed :q you would return to the outer read-eval-print loop where the current package would still be "ACL2".

    Roughly speaking, ld repeatedly reads a form from standard-oi, evaluates it, and prints its result to standard-co. It does this until the form is :q or evaluates to an error triple whose value component is :q, or until the input channel or list is emptied. However, ld has many bells and whistles controlled by the ld specials. Each such special is documented individually. For example, see the documentation for standard-oi, current-package, ld-pre-eval-print, etc.

    A more precise description of ld is as follows. In the description below we use the ld specials as variables, e.g., we say ``a form is read from standard-oi.'' By this usage we refer to the current value of the named state global variable, e.g., we mean ``a form is read from the current value of 'standard-oi.'' This technicality has an important implication: If while interacting with ld you change the value of one of the ld specials, e.g., 'standard-oi, you will change the behavior of ld, e.g., subsequent input will be taken from the new value.

    Three ld specials are treated as channels: standard-oi is treated as an object input channel and is the source of forms evaluated by ld; standard-co and proofs-co are treated as character output channels and various flavors of output are printed to them. However, the supplied values of these specials need not actually be channels; several special cases are recognized.

    If the supplied value of one of these is in fact an open channel of the appropriate type, that channel is used and is not closed by ld. If the supplied value of one of these specials is a string, the string is treated as a file name in (essentially) Unix syntax (see pathname) and a channel of the appropriate type is opened to/from that file. Any channel opened by ld during the binding of the ld specials is automatically closed by ld upon termination. If standard-co and proofs-co are equal strings, only one channel to that file is opened and is used for both.

    As a special convenience, when standard-oi is a string and the :dir argument provided and not nil, we look up :dir in the table of directories maintained by add-include-book-dir, and prepend this directory to standard-oi to create the filename. (In this case, however, we require that standard-oi is a relative pathname, not an absolute pathname.) For example, one can write (ld "arithmetic/top-with-meta.lisp" :dir :system) to ld that particular community books library. (Of course, you should almost always load books like arithmetic/top-with-meta using include-book instead of ld.) If :dir is not specified, then a relative pathname is resolved using the connected book directory; see cbd.

    Several other alternatives are allowed for standard-oi. If standard-oi is a true list then it is taken as the list of forms to be processed. If standard-oi is a list ending in an open channel, then ld processes the forms in the list and then reads and processes the forms from the channel. Analogously, if standard-oi is a list ending a string, an object input channel from the named file is opened and ld processes the forms in the list followed by the forms in the file. That channel is closed upon termination of ld.

    In the cases that a string is to be converted to an object input channel -- that is, when standard-oi is a string or is a list ending in a string -- an error occurs by default if the conversion fails, presumably because the file named by the string does not exist. However, if keyword argument :ld-missing-input-ok is t, then ld immediately returns without error in this case, but without reading or executing any forms, as though standard-oi is nil and keyword arguments :ld-verbose and ld-prompt both have value nil. The other legal values for :ld-missing-input-ok are nil, which gives the default behavior, and :warn, which behaves the same as t except that a warning is printed, which contains the same information as would be printed for the default error described above.

    The remaining ld specials are handled more simply and generally have to be bound to one of a finite number of tokens described in the :doc entries for each ld special. Should any ld special be supplied an inappropriate value, an error message is printed.

    Next, if ld-verbose is t, ld prints the message ``ACL2 loading name'' where name names the file or channel from which forms are being read. At the conclusion of ld, it will print ``Finished loading name'' if ld-verbose is t.

    Finally, ld repeatedly executes the ACL2 read-eval-print step, which may be described as follows. A prompt is printed to standard-co if ld-prompt is non-nil. The format of the prompt is determined by ld-prompt. If it is t, the default ACL2 prompt is used. If it is any other non-nil value then it is treated as an ACL2 function that will print the desired prompt. See ld-prompt. In the exceptional case where ld's input is coming from the terminal (*standard-oi*) but its output is going to a different sink (i.e., standard-co is not *standard-co*), we also print the prompt to the terminal.

    Ld then reads a form from standard-oi. If the object read is a keyword, ld constructs a ``keyword command form'' by possibly reading several more objects. See keyword-commands. This construction process is sensitive to the value of ld-keyword-aliases. See ld-keyword-aliases. Otherwise, the object read is treated as the command form.

    Ld next decides whether to evaluate or skip this form, depending on ld-pre-eval-filter. Initially, the filter must be either :all, :query, or a new name. If it is :all, it means all forms are evaluated. If it is :query, it means each form that is read is displayed and the user is queried. Otherwise, the filter is a name and each form that is read is evaluated as long as the name remains new, but if the name is ever introduced then no more forms are read and ld terminates. See ld-pre-eval-filter.

    If the form is to be evaluated, then ld first prints the form to standard-co, if ld-pre-eval-print is t. With this feature, ld can process an input file or form list and construct a script of the session that appears as though each form was typed in. See ld-pre-eval-print.

    Ld then evaluates the form, with state bound to the current state. The result is some list of (multiple) values. If a state is among the values, then ld uses that state as the subsequent current state.

    Depending on ld-error-triples, ld may interpret the result as an ``error.'' See ld-error-triples. We first discuss ld's behavior if no error signal is detected (either because none was sent or because ld is ignoring them because ld-error-triples is nil).

    In the case of a non-erroneous result, ld does two things: First, if the logical world in the now current state is different than the world before execution of the form, ld adds to the world a ``command landmark'' containing the form evaluated. See command-descriptor. Second, ld prints the result to standard-co, but only if ld-post-eval-print is not nil. The result is printed as a list of (multiple) values unless ld-post-eval-print is :command-conventions, ld-error-triples is t, and the result is an ``error triple'', i.e., of the form (mv * * state) (see error-triples). In that case, only the non-erroneous ``value'' component of the result is printed. See ld-post-eval-print.

    Whenever ld prints anything (whether the input form, a query, or some results) it ``eviscerates'' it if ld-evisc-tuple is non-nil. Essentially, evisceration is a generalization of Common Lisp's use of *print-level* and *print-length* to hide large substructures. See evisc-tuple and also see set-iprint.

    We now return to the case of a form whose evaluation signals an error. In this case, ld first restores the ACL2 logical world to what it was just before the erroneous form was evaluated. Thus, a form that partially changes the world (i.e., begins to store properties) and then signals an error, has no effect on the world. You may see this happen on commands that execute several events (e.g., an encapsulate or a progn of several defuns): even though the output makes it appear that the initial events were executed, if an error is signalled by a later event the entire block of events is discarded.

    After rolling back, ld takes an action determined by ld-error-action. If the action is :continue, ld merely iterates the read-eval-print step. Note that nothing suggestive of the value of the ``erroneous'' form is printed. If the action is :return, ld terminates normally; similarly if the action is :return!, but a special value is returned that can cause superior ld commands to terminate; see ld-error-action for details. If the action is :error, ld terminates signalling an error to its caller. If its caller is in fact another instance of ld and that instance is watching out for error signals, the entire world created by the inner ld will be discarded by the outer ld if the inner ld terminates with an error.

    Ld returns an error triple, (mv erp val state). Erp is t or nil indicating whether an error is being signalled. If no error is signalled, val is the ``reason'' ld terminated and is one of :exit (meaning :q was read), :eof (meaning the input source was exhausted), :error (meaning an error occurred but has been supressed), :filter (meaning the ld-pre-eval-filter terminated ld), or a cons pair whose first component is the symbol :STOP-LD, which typically indicates that an error occurred while the value of variable 'ld-error-action was :RETURN!. See ld-error-action for details of this last case.




    acl2-sources/doc/HTML/LEMMA-INSTANCE.html0000664002132200015000000001666312222333521017016 0ustar kaufmannacl2 LEMMA-INSTANCE.html -- ACL2 Version 6.3

    LEMMA-INSTANCE

    an object denoting an instance of a theorem
    Major Section:  MISCELLANEOUS
    

    Lemma instances are the objects one provides via :use and :by hints (see hints) to bring to the theorem prover's attention some previously proved or easily provable fact. A typical use of the :use hint is given below. The value specified is a list of five lemma instances.

    :use (reverse-reverse
          (:type-prescription app)
          (:instance assoc-of-app
                     (x a) (y b) (z c))
          (:functional-instance p-f
                                (p consp) (f flatten))
          (:instance (:theorem (equal x x))
                     (x (flatten a))))
    
    Observe that an event name can be a lemma instance. The :use hint allows a single lemma instance to be provided in lieu of a list, as in:
    :use reverse-reverse
    
    or
    :use (:instance assoc-of-app (x a) (y b) (z c))
    

    A lemma instance denotes a formula which is either known to be a theorem or which must be proved to be a theorem before it can be used. To use a lemma instance in a particular subgoal, the theorem prover adds the formula as a hypothesis to the subgoal before the normal theorem proving heuristics are applied.

    A lemma instance, or lmi, is of one of the following five forms:

    (1) name, where name names a previously proved theorem, axiom, or definition and denotes the formula (theorem) of that name.

    (2) rune, where rune is a rune (see rune) denoting the :corollary justifying the rule named by the rune.

    (3) (:theorem term), where term is any term alleged to be a theorem. Such a lemma instance denotes the formula term. But before using such a lemma instance the system will undertake to prove term.

    (4) (:instance lmi (v1 t1) ... (vn tn)), where lmi is recursively a lemma instance, the vi's are distinct variables and the ti's are terms. Such a lemma instance denotes the formula obtained by instantiating the formula denoted by lmi, replacing each vi by ti. Normally ACL2 enforces the requirement that every variable vi must be bound in the formula denoted by lmi. However, the keyword :extra-bindings-ok may be inserted immediately after the lemma instance in order to remove that requirement: (:instance lmi :extra-bindings-ok (v1 t1) ... (vn tn)).

    (5) (:functional-instance lmi (f1 g1) ... (fn gn)), where lmi is recursively a lemma instance and each fi is an ``instantiable'' function symbol of arity ni and gi is a function symbol, a macro alias for a function symbol gi' (see macro-aliases-table) in which case we treat gi as gi', or a pseudo-lambda expression of arity ni. An instantiable function symbol is any defined or constrained function symbol except the primitives not, member, implies, and o<, and a few others, as listed by the constant *non-instantiable-primitives*. These are built-in in such a way that we cannot recover the constraints on them. (Special case: a function introduced in the :partial-theory of a dependent clause-processor is not instantiable; see define-trusted-clause-processor.) A pseudo-lambda expression is an expression of the form (lambda (v1 ... vn) body) where the vi are distinct variable symbols and body is any term. No a priori relation is imposed between the vi and the variables of body, i.e., body may ignore some vi's and may contain ``free'' variables. However, we do not permit v to occur freely in body if the functional substitution is to be applied to any formula (lmi or the constraints to be satisfied) in a way that inserts v into the scope of a binding of v by let or mv-let (or, lambda). If you happen to violate this restriction, an informative error message will be printed. That message will list for you the potentially illegal choices for v in the context in which the functional substitution is offered. A :functional-instance lemma instance denotes the formula obtained by functionally instantiating the formula denoted by lmi, replacing fi by gi. However, before such a lemma instance can be used, the system will generate proof obligations arising from the replacement of the fi's by the gi's in constraints that ``support'' the lemma to be functionally instantiated; see constraint. One might expect that if the same instantiated constraint were generated on behalf of several events, then each of those instances would have to be proved. However, for the sake of efficiency, ACL2 stores the fact that such an instantiated constraint has been proved and avoids it in future events.

    Note that ACL2(r) (see real) imposes additional requirements for functional instantiation. See functional-instantiation-in-acl2r.

    Obscure case for definitions. If the lemma instance refers to a :definition rune, then it refers to the corollary formula of that rune, which can be a simplified (``normalized'') form of the original formula. However, if the hint is a :by hint and the lemma instance is based on a name (i.e., a symbol), rather than a rune, then the formula is the original formula of the event, as shown by :pe, rather than the normalized version, as shown by :pf. This is as one would expect: If you supply the name of an event, you expect it to refer to the original event. For :use hints we use the simplified (normalized) form instead, which is reasonable since one would expect simplification during the proof that re-traces the normalization done at the time the rule was created.

    See functional-instantiation-example for an example of the use of :functional-instance (so-called ``functional instantiation).''




    acl2-sources/doc/HTML/LEN.html0000664002132200015000000000157112222333524015372 0ustar kaufmannacl2 LEN.html -- ACL2 Version 6.3

    LEN

    length of a list
    Major Section:  ACL2-BUILT-INS
    

    Len returns the length of a list.

    A Common Lisp function that is appropriate for both strings and proper lists is length; see length. The guard for len is t.

    (Low-level implementation note. ACL2 provides a highly-optimized implementation of len, which is tail-recursive and fixnum-aware, that differs from its simple ACL2 definition.)

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LENGTH.html0000664002132200015000000000141012222333524015725 0ustar kaufmannacl2 LENGTH.html -- ACL2 Version 6.3

    LENGTH

    length of a string or proper list
    Major Section:  ACL2-BUILT-INS
    

    Length is the function for determining the length of a sequence. In ACL2, the argument is required to be either a true-listp or a string.

    Length is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LET.html0000664002132200015000000001565012222333524015403 0ustar kaufmannacl2 LET.html -- ACL2 Version 6.3

    LET

    binding of lexically scoped (local) variables
    Major Section:  ACL2-BUILT-INS
    

    Example LET Form:
    (let ((x (* x x))
          (y (* 2 x)))
     (list x y))
    
    If the form above is executed in an environment in which x has the value -2, then the result is '(4 -4).

    Let expressions bind variables so that their ``local'' values, the values they have when the ``body'' of the let is evaluated, are possibly different than their ``global'' values, the values they have in the context in which the let expression appears. In the let expression above, the local variables bound by the let are x and y. They are locally bound to the values delivered by the two forms (* x x) and (* 2 x), respectively, that appear in the ``bindings'' of the let. The body of the let is (list x y).

    Suppose that the let expression above occurs in a context in which x has the value -2. (The global value of y is irrelevant to this example.) For example, one might imagine that the let form above occurs as the body of some function, fn, with the formal parameter x and we are evaluating (fn -2).

    To evaluate the let above in a context in which x is -2, we first evaluate the two forms specifying the local values of the variables. Thus, (* x x) is evaluated and produces 4 (because x is -2) and (* 2 x) is evaluated and produces -4 (because x is -2). Then x and y are bound to these values and the body of the let is evaluated. Thus, when the body, (list x y) is evaluated, x is 4 and y is -4. Thus, the body produces '(4 -4).

    Note that the binding of y, which is written after the binding of x and which mentions x, nevertheless uses the global value of x, not the new local value. That is, the local variables of the let are bound ``in parallel'' rather than ``sequentially.'' In contrast, if the

    Example LET* Form:
    (let* ((x (* x x))
           (y (* 2 x)))
     (list x y))
    
    is evaluated when the global value of x is -2, then the result is '(4 8), because the local value of y is computed after x has been bound to 4. Let* binds its local variables ``sequentially.''
    General LET Forms:
    (let ((var1 term1) ... (varn termn)) body)
    and
    (let ((var1 term1) ... (varn termn))
     (declare ...) ... (declare ...)
     body)
    
    where the vari are distinct variables, the termi are terms involving only variables bound in the environment containing the let, and body is a term involving only the vari plus the variables bound in the environment containing the let. Each vari must be used in body or else declared ignored.

    A let form is evaluated by first evaluating each of the termi, obtaining for each a vali. Then, each vari is bound to the corresponding vali and body is evaluated.

    Actually, let forms are just abbreviations for certain uses of lambda notation. In particular

    (let ((var1 term1) ... (varn termn)) (declare ...) body)
    
    is equivalent to
    ((lambda (var1 ... varn)
       (declare ...)
       body)
     term1 ... termn).
    
    Let* forms are used when it is desired to bind the vari sequentially, i.e., when the local values of preceding varj are to be used in the computation of the local value for vari.
    General LET* Forms:
    (let* ((var1 term1) ... (varn termn)) body)
    and
    (let* ((var1 term1) ... (varn termn))
     (declare (ignore x1 ... xm))
     body)
    
    where the vari are variables (not necessarily distinct), the termi are terms involving only variables bound in the environment containing the let* and those varj such that j<i, and body is a term involving only the vari plus the variables bound in the environment containing the let*. Each vari must be used either in some subsequent termj or in body, except that in the second form above we make an exception when vari is among the xk, in which case vari must not be thus used. Note that let* does not permit the inclusion of any declare forms other than one as shown above. In the second general form above, every xk must be among the vari, and furthermore, xk may not equal vari and varj for distinct i, j.

    The first let* above is equivalent to

    (let ((var1 term1))
     ...
     (let ((varn termn)) body)...)
    
    Thus, the termi are evaluated successively and after each evaluation the corresponding vali is bound to the value of termi. The second let* is similarly expanded, except that each for each vari that is among the (x1 ... xm), the form (declare (ignore vari)) is inserted immediately after (vari termi).

    Each (vari termi) pair in a let or let* form is called a ``binding'' of vari and the vari are called the ``local variables'' of the let or let*. The common use of let and let* is to save the values of certain expressions (the termi) so that they may be referenced several times in the body without suggesting their recomputation.

    Let is part of Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LET_star_.html0000664002132200015000000000437512222333524016575 0ustar kaufmannacl2 LET_star_.html -- ACL2 Version 6.3

    LET*

    binding of lexically scoped (local) variables
    Major Section:  ACL2-BUILT-INS
    

    Example LET* Forms:
    (let* ((x (* x x))
           (y (* 2 x)))
     (list x y))
    
    (let* ((x (* x x))
           (y (* 2 x))
           (x (* x y))
           (a (* x x)))
     (declare (ignore a))
     (list x y))
    
    If the forms above are executed in an environment in which x has the value -2, then the respective results are '(4 8) and '(32 8). See let for a discussion of both let and let*, or read on for a briefer discussion.

    The difference between let and let* is that the former binds its local variables in parallel while the latter binds them sequentially. Thus, in let*, the term evaluated to produce the local value of one of the locally bound variables is permitted to reference any locally bound variable occurring earlier in the binding list and the value so obtained is the newly computed local value of that variable. See let.

    In ACL2 the only declare forms allowed for a let* form are ignore, ignorable, and type. See declare. Moreover, no variable declared ignored or ignorable may be bound more than once. A variable with a type declaration may be bound more than once, in which case the type declaration is treated by ACL2 as applying to each binding occurrence of that variable. It seems unclear from the Common Lisp spec whether the underlying Lisp implementation is expected to apply such a declaration to more than one binding occurrence, however, so performance in such cases may depend on the underlying Lisp.

    Let* is a Common Lisp macro. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LEXORDER.html0000664002132200015000000000246412222333524016202 0ustar kaufmannacl2 LEXORDER.html -- ACL2 Version 6.3

    LEXORDER

    total order on ACL2 objects
    Major Section:  ACL2-BUILT-INS
    

    Lexorder is a non-strict total order, a ``less than or equal,'' on ACL2 objects. Also see alphorder, the restriction of lexorder to atoms; the notion of ``non-strict total order'' is defined there.

    Lexorder has a guard of t.

    For lexorder, an atom and a cons are ordered so that the atom comes first, and two conses are ordered so that the one with the recursively smaller car comes first, with the cdrs being compared only if the cars are equal. Lexorder compares two atoms by using alphorder.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LINEAR-ARITHMETIC.html0000664002132200015000000001032012222333521017342 0ustar kaufmannacl2 LINEAR-ARITHMETIC.html -- ACL2 Version 6.3

    LINEAR-ARITHMETIC

    A description of the linear arithmetic decision procedure
    Major Section:  MISCELLANEOUS
    

    We describe the procedure very roughly here. Fundamental to the procedure is the notion of a linear polynomial inequality. A ``linear polynomial'' is a sum of terms, each of which is the product of a rational constant and an ``unknown.'' The ``unknown'' is permitted to be 1 simply to allow a term in the sum to be constant. Thus, an example linear polynomial is 3*x + 7*a + 2; here x and a are the (interesting) unknowns. However, the unknowns need not be variable symbols. For example, (length x) might be used as an unknown in a linear polynomial. Thus, another linear polynomial is 3*(length x) + 7*a. A ``linear polynomial inequality'' is an inequality (either < or <=) relation between two linear polynomials. Note that an equality may be considered as a pair of inequalities; e.q., 3*x + 7*a + 2 = 0 is the same as the conjunction of 3*x + 7*a + 2 <= 0 and 0 <= 3*x + 7*a + 2.

    Certain linear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third inequality with fewer unknowns. If this deduced inequality is manifestly false, a contradiction has been deduced from the assumed inequalities.

    For example, suppose we have two assumptions

    p1:       3*x + 7*a <  4
    p2:               3 <  2*x
    
    and we wish to prove that, given p1 and p2, a < 0. As suggested above, we proceed by assuming the negation of our goal
    p3:               0 <= a.
    
    and looking for a contradiction.

    By cross-multiplying and adding the first two inequalities, (that is, multiplying p1 by 2, p2 by 3 and adding the respective sides), we deduce the intermediate result

    p4:  6*x + 14*a + 9 < 8 + 6*x
    
    which, after cancellation, is:
    p4:        14*a + 1 <  0.
    
    If we then cross-multiply and add p3 to p4, we get
    p5:               1 <= 0,
    
    a contradiction. Thus, we have proved that p1 and p2 imply the negation of p3.

    All of the unknowns of an inequality must be eliminated by cancellation in order to produce a constant inequality. We can choose to eliminate the unknowns in any order, but we eliminate them in term-order, largest unknowns first. (See term-order.) That is, two polys are cancelled against each other only when they have the same largest unknown. For instance, in the above example we see that x is the largest unknown in each of p1 and p2, and a in p3 and p4.

    Now suppose that this procedure does not produce a contradiction but instead yields a set of nontrivial inequalities. A contradiction might still be deduced if we could add to the set some additional inequalities allowing further cancellations. That is where :linear lemmas come in. When the set of inequalities has stabilized under cross-multiplication and addition and no contradiction is produced, we search the database of :linear rules for rules about the unknowns that are candidates for cancellation (i.e., are the largest unknowns in their respective inequalities). See linear for a description of how :linear rules are used.

    See also non-linear-arithmetic for a description of an extension to the linear-arithmetic procedure described here.




    acl2-sources/doc/HTML/LINEAR.html0000664002132200015000000002135012222333530015720 0ustar kaufmannacl2 LINEAR.html -- ACL2 Version 6.3

    LINEAR

    make some arithmetic inequality rules
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example:
    (defthm length-member-leq-length       If inequality reasoning begins to
      (implies (and (eqlablep e)           consider how (length (member a b))
                    (true-listp x))        compares to any other term, add to
               (<= (length (member e x))   the set of known inequalities the fact
                   (length x)))            that it is no larger than (length b),
      :rule-classes :linear)               provided (eqlablep a) and
                                           (true-listp b) rewrite to t.
    
    General Form:
    (and ...
         (implies (and ...hi...)
                  (implies (and ...hk...)
                           (and ...
                                (rel lhs rhs)
                                ...)))
         ...)
    

    Note: One :linear rule class object might create many linear arithmetic rules from the :corollary formula. To create the rules, we first flatten the and and implies structure of the formula, transforming it into a conjunction of formulas, each of the form

    (implies (and h1 ... hn) (rel lhs rhs))
    
    where no hypothesis is a conjunction and rel is one of the inequality relations <, <=, =, >, or >=. If necessary, the hypothesis of such a conjunct may be vacuous. We create a :linear rule for each such conjunct, if possible, and otherwise cause an error.

    Each rule has one or more ``trigger terms'' which may be specified by the user using the :trigger-terms field of the rule class or which may be defaulted to values chosen by the system. We discuss the determination of trigger terms after discussing how linear rules are used.

    :Linear rules are used by an arithmetic decision procedure during rewriting. See linear-arithmetic and see non-linear-arithmetic. Here we assume that the reader is familiar with the material described in linear-arithmetic.

    Recall that we eliminate the unknowns of an inequality in term-order, largest unknowns first. (See term-order.) In order to facilitate this strategy, we store the inequalities in ``linear pots''. For purposes of the present discussion, let us say that an inequality is ``about'' its largest unknown. Then, all of the inequalities about a particular unknown are stored in the same linear pot, and the pot is said to be ``labeled'' with that unknown. This storage layout groups all of the inequalities which are potential candidates for cancellation with each other into one place. It is also key to the efficient operation of :linear rules.

    If the arithmetic decision procedure has stabilized and not yielded a contradiction, we scan through the list of linear pots examining each label as we go. If the trigger term of some :linear rule can be instantiated to match the label, we so instantiate that rule and attempt to relieve the hypotheses with general-purpose rewriting. If we are successful, we add the rule's instantiated conclusion to our set of inequalities. This may let cancellation continue.

    Note: Problems may arise if you explicitly store a linear lemma under a trigger term that, when instantiated, is not the largest unknown in the instantiated concluding inequality. Suppose for example you store the linear rule (<= (fn i j) (/ i (* j j))) under the trigger term (fn i j). Then when the system ``needs'' an inequality about (fn a b), (i.e., because (fn a b) is the label of some linear pot, and hence the largest unknown in some inequality), it will appeal to the rule and deduce (<= (fn a b) (/ a (* b b))). However, the largest unknown in this inequality is (/ a (* b b)) and hence it will be stored in a linear pot labeled with (/ a (* b b)). The original, triggering inequality which is in a pot about (fn a b) will therefore not be cancelled against the new one. It is generally best to specify as a trigger term one of the ``maximal'' terms of the polynomial, as described below.

    We now describe how the trigger terms are determined. Most of the time, the trigger terms are not specified by the user and are instead selected by the system. However, the user may specify the terms by including an explicit :trigger-terms field in the rule class, e.g.,

    General Form of a Linear Rule Class:
    (:LINEAR :COROLLARY formula
             :TRIGGER-TERMS (term1 ... termk))
    
    Each termi must be a term and must not be a variable, quoted constant, lambda application, let-expression or if-expression. In addition, each termi must be such that if all the variables in the term are instantiated and then the hypotheses of the corollary formula are relieved (possibly instantiating additional free variables), then all the variables in the concluding inequality are instantiated. We generate a linear rule for each conjuctive branch through the corollary and store each rule under each of the specified triggers. Thus, if the corollary formula contains several conjuncts, the variable restrictions on the termi must hold for each conjunct.

    If :trigger-terms is omitted the system computes a set of trigger terms. Each conjunct of the corollary formula may be given a unique set of triggers depending on the variables that occur in the conjunct and the addends that occur in the concluding inequality. In particular, the trigger terms for a conjunct is the list of all ``maximal addends'' in the concluding inequality.

    The ``addends'' of (+ x y) and (- x y) are the union of the addends of x and y. The addends of (- x) and (* n x), where n is a rational constant, is just {x}. The addends of an inequality are the union of the addends of the left- and right-hand sides. The addends of any other term, x, is {x}.

    A term is maximal for a conjunct (implies hyps concl) of the corollary if (a) the term is a non-variable, non-quote, non-lambda application, non-let and non-if expression, (b) the term contains enough variables so that when they are instantiated and the hypotheses are relieved (which may bind some free variables; see free-variables) then all the variables in concl are instantiated, and (c) no other addend is always ``bigger'' than the term, in the technical sense described below.

    The technical notion referenced above depends on the notion of fn-count, the number of function symbols in a term, and pseudo-fn-count, which is essentially the number of function symbols implicit in a constant (see term-order, specifically the discussion of ``pseudo-function application count'' at the end). We say term1 is always bigger than term2 if all instances of term1 have a larger fn-count (actually lexicographic order of fn-count and pseudo-fn-count) than the corresponding instances of term2. This is equivalent to saying that the fn-count of term1 is larger than that of term2 (by ``fn-count'' here we mean the lexicographic order of fn-count and pseudo-fn-count) and the variable bag for term2 is a subbag of that for term1. For example, (/ a (* b b)) is always bigger than (fn a b) because the first has two function applications and {a b} is a subbag of a b b, but (/ a (* b b)) is not always bigger than (fn a x).




    acl2-sources/doc/HTML/LIST.html0000664002132200015000000000133712222333524015527 0ustar kaufmannacl2 LIST.html -- ACL2 Version 6.3

    LIST

    build a list
    Major Section:  ACL2-BUILT-INS
    

    List is the macro for building a list of objects. For example, (list 5 6 7) returns a list of length 3 whose elements are 5, 6, and 7 respectively. Also see list*.

    List is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LISTP.html0000664002132200015000000000152112222333524015642 0ustar kaufmannacl2 LISTP.html -- ACL2 Version 6.3

    LISTP

    recognizer for (not necessarily proper) lists
    Major Section:  ACL2-BUILT-INS
    

    (listp x) is true when x is either a cons pair or is nil.

    Listp has no guard, i.e., its guard is t.

    Listp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LIST_star_.html0000664002132200015000000000132012222333524016707 0ustar kaufmannacl2 LIST_star_.html -- ACL2 Version 6.3

    LIST*

    build a list
    Major Section:  ACL2-BUILT-INS
    

    List* is the Common Lisp macro for building a list of objects from given elements and a tail. For example, (list* 5 6 '(7 8 9)) equals the list '(5 6 7 8 9). Also see list.

    List* is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LOCAL-INCOMPATIBILITY.html0000664002132200015000000001616112222333521020042 0ustar kaufmannacl2 LOCAL-INCOMPATIBILITY.html -- ACL2 Version 6.3

    LOCAL-INCOMPATIBILITY

    when non-local events won't replay in isolation
    Major Section:  MISCELLANEOUS
    

    Sometimes a ``local incompatibility'' is reported while attempting to embed some events, as in an encapsulate or include-book. This is generally due to the use of a locally defined name in a non-local event or the failure to make a witnessing definition local.

    Local incompatibilities may be detected while trying to execute the strictly non-local events of an embedding. For example, encapsulate operates by first executing all the events (local and non-local) with ld-skip-proofsp nil, to confirm that they are all admissible. Then it attempts merely to assume the non-local ones to create the desired theory, by executing the events with ld-skip-proofsp set to 'include-book. Similarly, include-book assumes the non-local ones, with the understanding that a previously successful certify-book has performed the admissiblity check.

    How can a sequence of events admitted with ld-skip-proofsp nil fail when ld-skip-proofsp is 'include-book? The key observation is that in the latter case only the non-local events are processed. The local ones are skipped and so the non-local ones must not depend upon them.

    Two typical mistakes are suggested by the detection of a local incompatibility: (1) a locally defined function or macro was used in a non-local event (and, in the case of encapsulate, was not included among the signatures) and (2) the witnessing definition of a function that was included among the signatures of an encapsulate was not made local.

    An example of mistake (1) would be to include among your encapsulated events both (local (defun fn ...)) and (defthm lemma (implies (fn ...) ...)). Observe that fn is defined locally but a formula involving fn is defined non-locally. In this case, either the defthm should be made local or the defun should be made non-local.

    An example of mistake (2) would be to include (fn (x) t) among your signatures and then to write (defun fn (x) ...) in your events, instead of (local (defun fn ...)).

    One subtle aspect of encapsulate is that if you constrain any member of a mutually recursive clique you must define the entire clique locally and then you must constrain those members of it you want axiomatized non-locally.

    Errors due to local incompatibility should never occur in the assumption of a fully certified book. Certification ensures against it. Therefore, if include-book reports an incompatibility, we assert that earlier in the processing of the include-book a warning was printed advising you that some book was uncertified. If this is not the case -- if include-book reports an incompatibility and there has been no prior warning about lack of certification -- please report it to us.

    When a local incompatibility is detected, we roll-back to the world in which we started the encapsulate or include-book. That is, we discard the intermediate world created by trying to process the events skipping proofs. This is clean, but we realize it is very frustrating because the entire sequence of events must be processed from scratch. Assuming that the embedded events were, once upon a time, processed as top-level commands (after all, at some point you managed to create this sequence of commands so that the local and non-local ones together could survive a pass in which proofs were done), it stands to reason that we could define a predicate that would determine then, before you attempted to embed them, if local incompatibilities exist. We hope to do that, eventually.

    We conclude with a subtle example of local incompatibility. The problem is that in order for foo-type-prescription to be admitted using the specified :typed-term (foo x), the conclusion (my-natp (foo x)) depends on my-natp being a compound-recognizer. This is fine on the first pass of the encapsulate, during which lemma my-natp-cr is admitted. But my-natp-cr is skipped on the second pass because it is marked local, and this causes foo-type-prescription to fail on the second pass.

    (defun my-natp (x)
      (declare (xargs :guard t))
      (and (integerp x)
           (<= 0 x)))
    
    (defun foo (x)
      (nfix x))
    
    (encapsulate
     ()
     (local (defthm my-natp-cr
              (equal (my-natp x)
                     (and (integerp x)
                          (<= 0 x)))
              :rule-classes :compound-recognizer))
     (defthm foo-type-prescription
       (my-natp (foo x))
       :hints (("Goal" :in-theory (enable foo)))
       :rule-classes ((:type-prescription :typed-term (foo x)))))
    




    acl2-sources/doc/HTML/LOCAL.html0000664002132200015000000000536512222333517015615 0ustar kaufmannacl2 LOCAL.html -- ACL2 Version 6.3

    LOCAL

    hiding an event in an encapsulation or book
    Major Section:  EVENTS
    

    Examples:
    (local (defthm hack1
             (implies (and (acl2-numberp x)
                           (acl2-numberp y)
                           (equal (* x y) 1))
                      (equal y (/ x)))))
    
    (local (defun double-naturals-induction (a b)
             (cond ((and (integerp a) (integerp b) (< 0 a) (< 0 b))
                    (double-naturals-induction (1- a) (1- b)))
                   (t (list a b)))))
    
    General Form:
    (local ev)
    
    where ev is an event form. If the current default defun-mode (see default-defun-mode) is :logic and ld-skip-proofsp is nil or t, then (local ev) is equivalent to ev. But if the current default defun-mode is :program or if ld-skip-proofsp is 'include-book, then (local ev) is a no-op. Thus, if such forms are in the event list of an encapsulate event or in a book, they are processed when the encapsulation or book is checked for admissibility in :logic mode but are skipped when extending the host world. Such events are thus considered ``local'' to the verification of the encapsulation or book. The non-local events are the ones ``exported'' by the encapsulation or book. See encapsulate for a thorough discussion. Also see local-incompatibility for a discussion of a commonly encountered problem with such event hiding: you can't make an event local if its presence is required to make sense of a non-local one.

    Note that events that change the default defun-mode, and in fact any events that set the acl2-defaults-table, are disallowed inside the scope of local. See embedded-event-form.




    acl2-sources/doc/HTML/LOGAND.html0000664002132200015000000000171612222333524015721 0ustar kaufmannacl2 LOGAND.html -- ACL2 Version 6.3

    LOGAND

    bitwise logical `and' of zero or more integers
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logand returns their bitwise logical `and'. In ACL2 logand is a macro that expands into calls of the binary function binary-logand, except that (logand) expands to -1 and (logand x) expands to x.

    The guard for binary-logand requires its arguments to be integers. Logand is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LOGANDC1.html0000664002132200015000000000157712222333524016112 0ustar kaufmannacl2 LOGANDC1.html -- ACL2 Version 6.3

    LOGANDC1

    bitwise logical `and' of two ints, complementing the first
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logandc1 returns the bitwise logical `and' of the second with the bitwise logical `not' of the first.

    The guard for logandc1 requires its arguments to be integers. Logandc1 is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGANDC2.html0000664002132200015000000000160012222333524016076 0ustar kaufmannacl2 LOGANDC2.html -- ACL2 Version 6.3

    LOGANDC2

    bitwise logical `and' of two ints, complementing the second
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logandc2 returns the bitwise logical `and' of the first with the bitwise logical `not' of the second.

    The guard for logandc2 requires its arguments to be integers. Logandc2 is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGBITP.html0000664002132200015000000000175512222333524016060 0ustar kaufmannacl2 LOGBITP.html -- ACL2 Version 6.3

    LOGBITP

    the ith bit of an integer
    Major Section:  ACL2-BUILT-INS
    

    For a nonnegative integer i and an integer j, (logbitp i j) is a Boolean, which is t if and only if the value of the ith bit is 1 in the two's complement representation of j.

    (Logbitp i j) has a guard that i is a nonnegative integer and j is an integer.

    Logbitp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGCOUNT.html0000664002132200015000000000146112222333524016204 0ustar kaufmannacl2 LOGCOUNT.html -- ACL2 Version 6.3

    LOGCOUNT

    number of ``on'' bits in a two's complement number
    Major Section:  ACL2-BUILT-INS
    

    (Logcount x) is the number of ``on'' bits in the two's complement representation of x.

    (Logcount x) has a guard of (integerp x).

    Logcount is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGEQV.html0000664002132200015000000000173212222333524015750 0ustar kaufmannacl2 LOGEQV.html -- ACL2 Version 6.3

    LOGEQV

    bitwise logical equivalence of zero or more integers
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logeqv returns their bitwise logical equivalence. In ACL2 logeqv is a macro that expands into calls of the binary function binary-logeqv, except that (logeqv) expands to -1 and (logeqv x) expands to x.

    The guard for binary-logeqv requires its arguments to be integers. Logeqv is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE.html0000664002132200015000000000232312222333515022711 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE

    a brief explanation of base cases
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    According to the sentence, the conjecture being proved is ``reversing the reverse of a true-listp yields the original list.'' The formula corresponding to this conjecture is:

    (implies (true-listp z)
             (equal (rev (rev z)) z)).
    
    We're also told that this is an inductive proof. Evidently we're doing an induction on the structure of the list z. Then the Base Case is the formula:
    (implies (endp z)
             (implies (true-listp z)
                      (equal (rev (rev z)) z))).
    

    Now use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS.html0000664002132200015000000000440312222333515024075 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS

    substitution of equals for equals
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Anytime you have an equality hypothesis relating two terms, e.g.,

    (equal lhs rhs)
    
    it is legal to substitute one for the other anyplace else in the formula. Doing so does not change the truthvalue of the formula.

    You can use a negated equality this way provided it appears in the conclusion. For example, it is ok to transform

    (implies (true-listp x)
             (not (equal x 23)))
    
    to
    (implies (true-listp 23)
             (not (equal x 23)))
    
    by substitutions of equals for equals. That is because, by propositional calculus, we could rearrange the formulas into their contrapositive forms:
    (implies (equal x 23)
             (not (true-listp x)))
    
    and
    (implies (equal x 23)
             (not (true-listp 23)))
    
    and see the equality as a hypothesis and the substitution of 23 for x as sensible.

    Sometimes people speak loosely and say ``substitution of equals for equals'' when they really mean ``substitutions of equivalents for equivalents.'' Equality, as tested by EQUAL, is only one example of an equivalence relation. The next most common is propositional equivalence, as tested by IFF. You can use propositional equivalence hypotheses to substitute one side for the other provided the target term occurs in a propositional place, as discussed at the bottom of propositional calculus.

    Now use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION.html0000664002132200015000000000330712222333515023140 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION

    evaluation during proofs
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Any time you are proving a formula and see a subterm in the formula that contains no variables, you can just evaluate the subterm.

    This is familiar from algebra: It is not uncommon to rearrange a polynominal to collect all the constants and then add them up:

    (3x + 2 + 7y + 2)
    =
    (3x + 7y + (2 + 2))
    =
    (3x + 7y + 4).
    
    That last step is just evaluation.

    It happens often in ACL2 proofs because theorems involve constants and defined functions and when those constants ``drift into the maw'' of a function, the function call can be eliminated and replaced by a new constant. ACL2 does this automatically; you don't have to tell it. In fact, there are a few occasions where you might prefer it not evaluate and those are the ones you have to look out for! They'll be obvious when they happen because you'll see a mysterious constant crop up in the proof.

    Evaluation is legal because it is just the repeated use of unconditional rewriting to replace definitions by their instantiated bodies until no function calls remain.

    Now use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF.html0000664002132200015000000002105212222333515023703 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF

    a brief explanation of induction
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    We start by showing classical induction on the natural numbers in an ACL2 setting before turning to a more general treatment of induction.

    Classical Induction on Natural Numbers: Induction is familiar in the arithmetic setting. Let (p n) denote some formula involving the variable n (and perhaps some other variables which we don't exhibit). Then to prove (p n), for all n, by classical induction on the construction of the natural numbers, prove each of the following:

    Base Case:
    (implies (zp n) (p n))
    

    Induction Step:
    (implies (and (not (zp n))
                  (p (- n 1)))
             (p n))
    
    The Base Case establishes that p holds for 0. In fact, because of the definition of zp , it establishes that (p n) holds when n is 0 and it holds when n is not a natural number.

    The Induction Step establishes that if n is a natural number other than 0, and if p holds for n-1, then p holds for n. The hypothesis (p (- n 1)) above is called the induction hypothesis.

    Note that if the Base Case and Induction Step are valid, then we know (p n), for all n. You can convince yourself of this by picking any object and asking ``how do I know p holds for this object?'' For example, (p -7), (p 'abc), and (p 0) are all established by the Base Case. What about (p 1)? That follows from (p 0), given the Induction Step. Why? To prove (p 1) using the Induction Step, you have to establish (not (zp 1)), which is true, and (p (- 1 1)), which is (p 0), which is true by the Base Case. So (p 1) is true. Similar reasoning proves (p 2) from from (p 1), etc. Clearly, for every natural number other than 0 we could reason like this to show that p holds. Since the Base Case handled all the objects that are not natural numbers, and handled 0, we know (p n), for all n.

    There is a duality between recursion and induction that ACL2 exploits. The fact that the Base and Induction steps above are sufficient to prove p for all objects is related to the fact that the following recursion defines a total, terminating function:

    (defun nat-recursion (n)
      (if (zp n)
          n
          (nat-recursion (- n 1))))
    

    When this function is admitted we have to prove that if (zp n) does not hold, then (- n 1) is smaller, in some sense, than n. This sense of ``smaller'' is determined by some measure of the arguments. That measure must return an ordinal (ordinals ), but the most common measures return natural numbers, which are among the ordinals. Furthermore, that measure should insure that the terms in the recursive calls are smaller than the formals, i.e., the measure of (- n 1) must be smaller than the measure of n, when the recursive branches are taken. This sense of ``smaller'' must be well-founded: it must be impossible to have an infinitely descending chain of smaller things. This is true of the less-than relation on the ordinals (see o< ). Well-foundedness means that eventually any recursion must ``bottom out'' because things can't keep getting smaller forever.

    The recursion in nat-recursion suggests the induction shown above: the Base Case is defined by the if branch that does not lead to a recursive call. The Induction Step is defined by the other branch. The induction hypothesis is defined by what we recur on, i.e., (- n 1). The theorems proved when nat-recursion is introduced justify the classical induction scheme noted above.

    Every recursively defined ACL2 function suggests a legal induction and vice versa.

    Furthermore, every call of a recursively defined function on distinct variable symbols also suggests a legal induction: just take the induction suggested by the function's recursive definition after renaming the formal parameters to be the variables in the call.

    For example, it should be clear that (nat-recursion a) suggests a Base Case defined by (zp a), and induction step defined by (not (zp a)) and an induction hypothesis about (- a 1).

    Note that the term (fact n) suggests the same classical induction on natural numbers shown above, where fact is defined as follows (even though we've used the formal parameter k below).

    (defun fact (k)
      (if (zp k)
          1
          (* k (fact (- k 1)))))
    
    The induction suggested by a term like (fact n) is insensitive to the name of the formal parameter used in the defun.

    The induction suggested by a function or term is insensitive to the value returned by the function or term.

    It doesn't matter what the function returns in its ``base case'' (e.g., 1 in fact) or what the function ``does'' to its recursive call (e.g., multiply by k in the defun of fact).

    All that matters is (i) how the if structure breaks down the cases on k, (ii) which branches lead to recursion, and (iii) what arguments are passed to the recursive calls. Those things determine (i) the case analysis of the induction scheme, (ii) which cases are Base Cases and which are Induction Steps, and (iii) what the induction hypotheses are.

    For a selection of common inductions schemes in ACL2 (e.g., on the structure of natural numbers, lists, and trees and on several variables at once, multiple base cases, multiple induction hypotheses, multiple induction steps, etc.) check this link.

    Every legal ACL2 induction corresponds to an admissible recursive function and vice versa. Similarly, every legal ACL2 induction corresponds to a call of a recursively defined function on distinct variables.

    ACL2 chooses which induction to do by looking at the terms that occur in the conjecture. For many elementary theorems, ACL2 chooses the right induction by itself.

    You may occasionally need to tell it what induction to do. You do that by showing it a term that suggests the induction you want. We'll explain how you communicate this to ACL2 later. If you understand how recursive functions suggest inductions, then you know what you need to know to use ACL2.

    The main point of this discussion of induction is familiarize you with the basic terms: Base Case (of which there may be several), Induction Step (of which there may be several), Induction Hypothesis (of which there may be several in each Induction Step), measure and well-founded relation justifying an induction, and the induction suggested by a term or recursive function definition. Furthermore, every Induction Hypothesis is always an instance of the conjecture being proved: each induction hypothesis is obtained from the conjecture being proved by applying a substitution replacing variables by terms.

    If you are reviewing the material taken for granted about logic while working your way through the introduction to the theorem prover, please use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE.html0000664002132200015000000000325312222333515022675 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE

    a brief explanation of substitution instances
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Let p and q be terms or formulas (there is no difference in ACL2). Then we say p is an instance or substitution instance of q if and only if p can be obtained from q by uniformly replacing the variables of q by terms. Sometimes we call p the target and q the pattern because by choosing appropriate replacements we can make the pattern match many different targets.

    For example, the following target is an instance of the given pattern:

    target:      (APP (APP (REV A) (REV B)) (REV C))
    pattern:     (APP (APP   x       y    ) (REV z))
    

    The replacement or substitution used in this match of the pattern to the target is:

    variable in pattern          replacement term
    x                              (REV A)
    y                              (REV B)
    z                              C
    
    Such substitutions are usually written this way in ACL2:
    ((x  (REV A))
     (y  (REV B))
     (z  C)).
    

    Please use your browser's Back Button to return to the page that mentioned ``instance.''




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS.html0000664002132200015000000002314212222333515024743 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS

    a brief explanation of propositional calculus
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    It is impossible in this short introduction to teach you propositional calculus if you don't already know it!

    A typical use of propositional calculus is to observe that

    (implies (endp z)
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    is equivalent to:
     (implies (and (endp z)
                   (true-listp z))
              (equal (rev (rev z)) z))
    

    If this is surprising and you know propositional calculus, then the problem might be our notation. We're exploiting the tautology

    (p ---> (q ---> r)) <---> ((p & q) ---> r)
    
    where ---> and <---> are meant to be the traditional arrows denoting logical implication and logical equivalence.

    If you don't know propositional calculus, we'll say just a few things to help ease your journey.

    A propositional formula, in ACL2, is any formula written entirely in terms of variable symbols, T, NIL, and the propositional functions AND, OR, NOT, IMPLIES, and IFF. The ``tautology'' above in traditional notation is this propositional formula in ACL2:

    (IFF (IMPLIES P (IMPLIES Q R))
         (IMPLIES (AND P Q) R)).
    

    If you have a formula like

    (implies hyp
             concl)
    
    then we say that formula is an implication, that hyp is the hypothesis, and that concl is the conclusion. If the hypothesis is an and expression, as in
    (implies (and hyp1
                  hyp2
                  ...)
             concl)
    
    then we call hyp1 is the first hypothesis, hyp2 is the second hypothesis, etc.

    If a term is of the form

    (and term1 term2 ...)
    
    we say it is a conjunction and that term1 is the first conjunct, term2 is the second conjunct, etc. We treat an or-term analogously but call it a disjunction and its arguments are disjuncts.

    A tautology is any propositional formula that can be proved by testing it under all combinations of Boolean assignments to its variables. We give an example of such a truth-table proof below, but hasten to add that ACL2 does not generally use truth tables to recognize tautologies. It primarily uses IF-normalization and BDDs to recognize tautologies, which can be seen as a mix of symbolic manipulation and case analysis.

    Many tautologies have names, but ACL2 doesn't refer to them by name because it derives them from first principles. We list a few here because we sometimes use the names in our documentation; more importantly, you should look at these formulas and convince yourself that they're always true for all Boolean values of the variables:

    Double Negation:
    (iff (not (not p)) p)
    
    DeMorgan:
    (iff (not (and p q))
         (or (not p) (not q)))
    
    Distributivity:
    (iff (and p (or q r))
         (or (and p q)
             (and p r)))
    
    Promotion:
    (iff (implies p (implies q r))
         (implies (and p q) r))
    
    Implicative Disjunction:
    (iff (implies p q)
         (or (not p) q))
    
    Contrapositive:
    (iff (implies p q)
         (implies (not q) (not p)))
    
    Generalized Contrapositive:
    (iff (implies (and p r) q)
         (implies (and p (not q)) (not r)))
    
    
    There are, of course, many others, even with these same names! For example, there is a dual version of DeMorgan showing how not distributes over or, a dual version of Distributivity for or over and, etc.

    Dealing with propositional calculus will not generally be a problem for you because it is decidable and ACL2 has procedures that decide propositional formulas. However, propositional calculus can lead to exponential explosion and can thus explain why ACL2 has ``gone out to lunch.'' In addition, sometimes if you are curious as to why ACL2 is working on a certain subgoal the reason can be traced back to propositional calculus.

    The most common example of this is that to prove a formula of the form

    (implies (implies p1 q1)
             (implies p2 q2))
    
    propositional calculus will convert it to
    (and (implies (and p2 (not p1)) q2)
         (implies (and p2 q1) q2))
    
    Many users are surprised that the first conjunct above does not have q1 as a hypothesis. If you ever stare at an ACL2 goal and say to yourself ``A hypothesis is missing!'' the chances are that propositional calculus is ``to blame.'' In particular, if you are trying to prove that (implies p1 q1) implies something, you must deal with the case that (implies p1 q1) is true because p1 is false. Think about it.

    Now we illustrate the truth table method for deciding tautologies, even though that is not what ACL2 generally uses. Consider the formula called Promotion above:

    (IFF (IMPLIES P (IMPLIES Q R))
         (IMPLIES (AND P Q) R))
    

    The formula above is a tautology. It contains three variables, P, Q, and R, and so there are 8 combinations of Boolean assignments to them. If we let

    formula1:  (IMPLIES P (IMPLIES Q R))
    formula2:  (IMPLIES (AND P Q) R)
    
    then we wish to test the formula (IFF formula1 formula2):
    P   Q   R       formula1   formula2   (IFF formula1 formula2)
    ---------
    T   T   T            T         T       T
    T   T   NIL          NIL       NIL     T
    T   NIL T            T         T       T
    T   NIL NIL          T         T       T
    NIL T   T            T         T       T
    NIL T   NIL          T         T       T
    NIL NIL T            T         T       T
    NIL NIL NIL          T         T       T
    
    So we see that the formula always returns T and is thus a tautology.

    Recall that in the original example at the top of this page we were trying to prove the formula

    (implies (endp z)
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    This formula is an instance of
    (implies p (implies q r)).
    
    The substitution required by the match is
    sigma:
    ((p    (endp z))
     (q    (true-listp z))
     (r    (equal (rev (rev z)) z)))
    

    Since we know the tautology:

    (iff (implies p (implies q r))
         (implies (and p q) r)).
    
    is always true no matter what Boolean values p, q, and r have, then we know this instance of it (obtained by applying the substitution sigma above) is always true:
    (iff (implies (endp z)                            formula1'
                  (implies (true-listp z)
                           (equal (rev (rev z)) z)))
         (implies (and (endp z)                       formula2'
                       (true-listp z))
                  (equal (rev (rev z)) z))).
    
    Thus, if we're trying to prove formula1' it is permitted to try to to prove formula2' instead, because they return the same truthvalue.

    This sketch of propositional reasoning in ACL2 is a little suspect because we didn't address the possibility that the substitution might replace the propositional variables by non-propositional terms. But the tautology was verified only on Boolean values for those variables. This actually works out because in ACL2 all propositional testing is done against nil and any non-nil value, including t, is as good as another. However, the tautology allows us to replace one formula by the other only in contexts in which we just care about propositional truth, i.e., whether the formula is nil or not. When we prove a formula in ACL2 we are really establishing that it never returns nil, i.e., no matter what the values of the variables, the value of the formula is non-nil.

    A very simple example of this is with Double Negation.

    (iff (not (not p)) p)
    
    is a tautology. This means that if we were trying to prove
    (implies (not (not p)) ...)
    
    we could transform it to
    (implies p ...).
    
    But if we were trying to prove:
    (equal (not (not p)) p)
    
    we could not prove it by using Double Negation! The formula above claims that (not (not p)) and p have identical values. They do not! For example, (not (not 3)) is t, not 3. However, (not (not 3)) and t are propositionally equivalent (i.e., satisfy iff) because one is as good as the other in a test. That is what Double Negation says.

    As long as you only use propositional formulas in propositional places this aspect of ACL2 should not affect you.

    Now please use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER.html0000664002132200015000000001055312222333515022750 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER

    the inductive step of the rev-rev proof -- Answer to Question 1
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    The correct answer to Question 1 in logic-knowledge-taken-for-granted is Choice (iv).

    The Induction Step of the inductive proof of

    (implies (true-listp z)
             (equal (rev (rev z)) z))
    
    for an induction on the linear list z is:
    Induction Step:
    (implies (and (not (endp z))
                  (implies (true-listp (cdr z))
                           (equal (rev (rev (cdr z))) (cdr z))))
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    The second hypothesis above is the the induction hypothesis. The conclusion above is the formula we are trying to prove. Each induction hypothesis is always an instance of the formula being proved, i.e., it is obtained from the formula being proved by uniformly replacing the variables in the formula with terms. Notice how the induction hypothesis above is the same as the induction conclusion, except that all the zs have been replaced by (cdr z).

    If you thought the right answer was

    Induction Step -- Choice (i):
    (implies (not (endp z))
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    then perhaps you didn't understand that we're doing an inductive proof. Certainly if you prove the Base Case already discussed and you prove Choice (i) above, then you will have proved the goal conjecture, but you would have done it by simple case analysis: prove it when (endp z) and prove it when (not (endp z)). While logically valid, you probably can't prove Choice (i) directly because you have no induction hypothesis to work with.

    If you thought the right answer was:

    Induction Step -- Choice (ii):
    (implies (true-listp (cdr z))
             (equal (rev (rev (cdr z))) (cdr z)))
    
    then perhaps you misunderstand the difference between the Induction Step and the Induction Hypothesis. The Induction Step is the ``other half'' of the main proof, balancing the Base Case. The Induction Hypothesis is just a hypothesis you get to use during the Induction Step. The question Q1 asked what is the Induction Step.

    If you thought the right answer was:

    Induction Step -- Choice (iii):
    (implies (and (not (endp z))
                  (equal (rev (rev (cdr x))) (cdr x))) ; ``induction hyp''
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    then you are making the most common mistake newcomers make to induction. You are giving yourself an ``induction hypothesis'' that is not an instance of the conjecture you're proving. This alleged induction hypothesis says that (rev (rev (cdr x))) is (cdr x), whereas the correct induction hypothesis says those two terms are equal if (true-listp (cdr x)). This alleged induction hypothesis is a stronger claim than we're trying to prove. It turns out that by making this mistake you can ``prove'' conjectures that are not always true! Remember: the induction hypothesis is always an instance of the conjecture you're proving, not just some piece of it. Of course, ACL2 ``knows'' this and will never make this mistake. But we remind you of it because there may be times when you intuit a different hypothesis and don't understand why ACL2 doesn't use it.

    If this doesn't make sense, perhaps you should read about induction again.

    When you understand why Choice (iv) is the correct answer, use your browser's Back Button to return to logic-knowledge-taken-for-granted and go to question Q2.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER.html0000664002132200015000000001174112222333515022751 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER

    the inductive step of the rev-rev proof -- Answer to Question 2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    The correct answer to Question 2 in logic-knowledge-taken-for-granted is Subgoal (i) plus any one of the other other three. For your reference, the four choices were:

    Subgoal (i):
    (implies (and (not (endp z))
                  (true-listp z))
             (true-listp (cdr z)))
    
    Subgoal (ii):
    (implies (and (not (endp z))
                  (true-listp z)
                  (equal (rev (rev (cdr z))) (cdr z)))
             (equal (rev (rev z)) z))
    
    Subgoal (iii):
    (implies (and (not (endp z))
                  (equal (rev (rev (cdr z))) (cdr z)))
             (equal (rev (rev z)) z))
    
    Subgoal (iv):
    (implies (and (not (endp z))
                  (true-listp (cdr z))
                  (equal (rev (rev (cdr z))) (cdr z)))
             (equal (rev (rev z)) z))
    

    In particular, it is wrong to think the Induction Step of the proof of

    (implies (true-listp z)
             (equal (rev (rev z)) z))
    
    can be established by proving just Subgoal (ii), Subgoal (iii), Subgoal (iv), or combinations of those three. You must also prove Subgoal (i) or something like it!

    The Inductive Step for the conjecture above is

    Induction Step:
    (implies (and (not (endp z))
                  ; Induction Hypothesis:
                  (implies (true-listp (cdr z))
                           (equal (rev (rev (cdr z))) (cdr z))))
             ; Induction Conclusion:
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    Note that the Inductive Hypothesis is an implication:
    (implies (true-listp (cdr z))
             (equal (rev (rev (cdr z))) (cdr z)))
    
    This hypothesis can be true two different ways. The ``normal'' way -- the way everybody remembers -- is that (true-listp (cdr z)) is true and thus (equal (rev (rev (cdr z))) (cdr z)) is true. But the way many people forget is that (true-listp (cdr z)) is false. You must prove the Induction Step even in this ``forgetable'' case.

    In this case, the Induction Step simplifies to

    Induction Step:
    (implies (and (not (endp z))
                  (not (true-listp (cdr z))))
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    By Promotion (see the list of tautologies in our discussion of propositional calculus) this is
    Induction Step':
    (implies (and (not (endp z))
                  (not (true-listp (cdr z)))
                  (true-listp z))
             (equal (rev (rev z)) z))
    
    Using the Contrapositive and rearranging the order of the hypotheses (see propositional calculus again), this is
    Induction Step'':
    (implies (and (not (endp z))
                  (true-listp z)
                  (not (equal (rev (rev z)) z)))
             (true-listp (cdr z)))
    
    Notice that Subgoal (i) implies Induction Step'':
    Subgoal (i):
    (implies (and (not (endp z))
                  (truelistp z))
             (truelistp (cdr z)))
    

    Every inductive proof of an implication raises a case like this. If we denote the conjecture (implies p q) as p ---> q, then the Induction Step will look like this:

      ( c  &  (p'  --->  q'))
    --->
      (p ---> q)
    
    where c is the test that determines the inductive step, (e.g., (not (endp z))) and p' and q' are inductive instances of p and q. Promotion produces
      ( c  & p & (p'  --->  q'))
    --->
      q
    
    It is then very common to prove that p implies p',
    (i):
    (c & p) ---> p'
    
    and then prove that q' implies q,
    (ii):
    (c & p & q') ---> q
    
    These correspond exactly to our choices Subgoal (i) and Subgoal (ii).

    It is sometimes helpful to remember this diagram:

    (c  &  (p'  --->  q')
            ^         |
            |         |
            |         v
     -->   (p   --->  q )
    

    When you understand why Subgoals (i) and (ii) are sufficient, use your browser's Back Button to return to logic-knowledge-taken-for-granted and go to question Q3.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER.html0000664002132200015000000000315012222333515022745 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER

    the inductive step of the rev-rev proof -- Answer to Question 2
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    The correct answer to Question 3 in logic-knowledge-taken-for-granted is that you need to prove

    Subgoal to Relieve Hyp 1:
    (implies (and (q (f a))
                  (r a))
             (p (f a)))
    
    in order to use
    Theorem:
    (implies (p (f x))
             (equal (g (h x))
                    x))
    
    to rewrite the target (g (h a)) to a in
    Goal Conjecture:
    (implies (and (q (f a))
                  (r a))
             (s (g (h a))))
    

    If you don't see why, re-read the discussion of rewriting again. Forgetting about the need to relieve hypotheses is a common mistake in informal proofs. ACL2 won't forget to relieve them. But if you forget about the need to do it, you may be confused when ACL2 doesn't see the ``proof'' you see!

    Now use your browser's Back Button to return to the end of quiz in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY.html0000664002132200015000000000747712222333515024513 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY

    further information on expanding definitions via rewriting
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    We assume you've read about ``instances'' and picked up our basic terminology including the ideas of matching a pattern term to a target term, obtaining a substitution and how to instantiate a term with a substitution. We use these notions below without further citation.

    In addition, we assume you've read about ``rewriting'' where we introduced the idea of treating a theorem (axiom, definition, or lemma) as a conditional rewrite rule and replaced one term by an equivalent one provided we can relieve the hypotheses.

    Suppose you're trying to prove formula1 and you transform it to formula2 by rewriting. What happened?

    formula1:
    (implies (and (not (consp z))
                  (true-listp z))
             (equal (rev (rev z)) z))
    
    formula2:
    (implies (and (not (consp z))
                  (equal z nil))
             (equal (rev (rev z)) z))
    
    Evidently we replaced (true-listp z) by (equal z nil). But how did that happen? What really happened was the sequential application of several unconditional rewrites and the use of replacement of equals by equals.

    The definition of true-listp is:

    (defun true-listp (x)
      (if (consp x)
          (true-listp (cdr x))
          (equal x nil))).
    
    By rewriting once with the definition of true-listp, we transform formula1 to:
    formula1':
    (implies (and (not (consp z))
                  (if (consp z)
                      (true-listp (cdr z))
                      (equal z nil)))
             (equal (rev (rev z)) z)).
    
    Note how the call of true-listp has been replaced by the entire body of true-listp.

    Next, note that the first hypothesis above is that (consp z) is false. That is, (not (consp z)) is the same as (equal (consp z) nil). Thus, replacement of equals by equals means we can transform formula1' to

    formula1'':
    (implies (and (not (consp z))
                  (if nil
                      (true-listp (cdr z))
                      (equal z nil)))
             (equal (rev (rev z)) z)).
    
    (We will explore replacement of equals by equals later.)

    Furthermore, we know the basic axiom about if:

    Axiom if-nil:
    (if nil x y) = y.
    

    Rewriting with this particular axiom, using (if nil x y) as the pattern and y as the replacement, will transform formula1'' to

    formula2:
    (implies (and (not (consp z))
                  (equal z nil))
             (equal (rev (rev z)) z)).
    

    Often when we say we derived one formula from another by ``expansion'' and or by ``rewriting'' we take many rewrite steps, as here. We typically use hypotheses of the formula without noting ``replacement of equals by equals'' as when we replaced (consp z) by nil, and we typically omit to mention the use of basic axioms like if-nil above.

    Now use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING.html0000664002132200015000000002452512222333515023050 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING

    a brief explanation of rewriting from the logical perspective
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    First we give two examples of rewriting. Then we give a rather detailed description. We recommend you read the description, even if you understand the two examples, just so that you learn our terminology.

    Example 1: Suppose your goal conjecture is:

    Goal Conjecture:
    (implies (and (endp z)
                  (true-listp z))
             (equal (rev (rev z)) z))
    
    Then you can use the following theorem (actually the definitional axiom introduced by the defun of endp):
    Definitional Axiom: endp
    (equal (endp x)
           (not (consp x))).
    
    to rewrite the Goal Conjecture to
    Rewritten Goal Conjecture:
    (implies (and (not (consp z))
                  (true-listp z))
             (equal (rev (rev z)) z))
    
    Note that in this example, rewriting replaced the call of endp by its body after instantiating its body with the actuals from the call. This is sometimes just called expanding the definition of endp. (The notions of formal, body, call, and actuals are discussed in programming-knowledge-taken-for-granted.)

    Expanding a definition is an example of unconditional rewriting. All definitions in ACL2 are just bare equalities relating a call of the function on its formals to its body. Any time you use an equality theorem, whether a definitional equality or something more general like

    (equal (append (append x y) z)
           (append x (append y z)))
    
    to replace an instance of one side by the corresponding instance of the other in a goal conjecture, we call that unconditional rewriting with the equality.

    Example 2: Suppose your goal conjecture is:

    Goal Conjecture:
    (implies (and (subsetp a b)
                  (true-listp b)
                  (member e a))
             (< (len (rm e b)) (len b))).
    
    This conjecture may be read ``if a is a subset of the true-listp b and e is a member of a, then the result of removing e from b has a shorter length than b.''

    You can use the following theorem:

    Theorem:
    (implies (member u v)
             (equal (len (rm u v))
                    (- (len v) 1)))
    
    to rewrite the Goal Conjecture to
    Rewritten Goal Conjecture:
    (implies (and (subsetp a b)
                  (true-listp b)
                  (member e a))
             (< (- (len b) 1) (len b))).
    
    To do this you must know that the following subgoal is provable:
    Subgoal to Relieve Hyp 1:
    (implies (and (subsetp a b)
                  (true-listp b)
                  (member e a))
             (member e b)).
    

    This is an example of conditional rewriting. In order to use the Theorem we had to establish that its hypotheses are satisfied. That is called relieving the hypotheses and was done by proving the Subgoal to Relieve Hyp 1. Conditional rewriting is the most commonly used proof technique in ACL2.

    Unconditional rewriting is just a special case, where there are no hypotheses to relieve.

    Expanding a definition is just another special case, where there are no hypotheses to relieve and the pattern is easy to match because it is a call of a function on distinct variables.

    This page discusses rewriting from the logical perspective. It is important that you are familiar with the notions of a pattern term being an instance of a target term. We often say the pattern matches the target. These notions involve a corresponding substitution of terms for variables. All these notions are discussed in the link for ``instance'' above and we recommend you read it before continuing. Then use your browser's Back Button to come back here.

    You should also be aware of the terms introduced in our discussion of propositional calculus.

    Rewriting is a fundamental rule of inference in our system. The rule allows you to use a theorem, i.e., an axiom, lemma, or definition, to replace one term by another in the goal conjecture you're trying to prove.

    Suppose you have a theorem that is of the form (or can be put into the form):

    Theorem:
    (implies (and hyp1
                  ...
                  hypk)
             (equal pattern
                    replacement))
    

    From the logical perspective we don't care how the theorem was actually written when it was proved. It might have no hypotheses (in which case the hypi could just be t), or it could have been written in a different but equivalent propositional style, (or (not hyp1) ...), or the equality could have been written the other way around, (equal replacement pattern). Such syntactic details don't matter. Just take a theorem and use propositional calculus to rearrange it equivalently into this form for the purposes of this one rewrite step.

    Suppose pattern is an instance of some target term, target that occurs in your goal conjecture. Let the corresponding substitution be sigma. If sigma does not contain a binding for every variable that occurs in Theorem, then extend sigma to sigma' by adding one binding for each such variable. (This is necessary only if pattern does not contain every variable in Theorem.)

    Let replacement' be the result of instantiating replacement with sigma'. Let hypi' be the result of instantiating hypi with sigma'.

    Then the Rewrite Rule of Inference tells us it is permitted to replace that occurrence of target in the goal by replacement' -- if you can prove each hypi' in this context. We make this last condition clear in a moment.

    The justification for this is that Theorem is true for all values of the variables. Hence, it is true for the values specified by sigma'. If the hypi' are true, then the target is really equal to replacement'. But it is always permitted to replace something by something it's equal to.

    Rewriting thus involves several steps:

    (1) Finding a target and a theorem to use to rewrite it to some more desirable replacement.

    (2) Instantiating pattern in the (rearranged) theorem to match target.

    (3) Extending sigma to sigma' to bind all the variables in Theorem.

    (4) Establishing that the sigma' instances of each of the hypi hold. This is called relieving the hypotheses of the theorem and is discussed in greater detail below.

    (5) Replacing the occurrence of target in the goal conjecture by the sigma' instance of replacement, provided all the hypotheses are relieved.

    Step (4) above, relieving the hypotheses, requires first identifying the ``context'' of the target in the goal conjecture. To do this, use propositional calculus to rearrange the goal conjecture so the occurrence of target is in the conclusion and let context be the hypothesis.

    Rearranged Conjecture:
    (implies context
             (... target ...))
    

    To relieve the hypotheses you must then prove each of the following subgoals:

    Subgoal to Relieve Hyp i:
    (implies context hypi').
    

    It is important to note that this description of rewriting with Theorem describes the process from a strictly logical perspective. The syntax of the theorem and the goal don't matter. You're free to use propositional calculus to rearrange them to put them into the appropriate forms to fit the descriptions given. Clearly, if you have a candidate Theorem in the ``wrong'' form and but it can be rearranged with propositional calculus into the ``right'' form, then that rearranged theorem is also a Theorem and can be used as described. But in the actual implementation of ACL2, the syntactic form of a proved Theorem affects how it is used by rewriting. If a proved theorem takes the form of an implication concluding with an equality, ACL2 treats the left-hand side of the equality as pattern and the right-hand side as replacement, unless you tell it otherwise. We'll discuss this later.

    Furthermore, being from the logical perspective this discussion of rewriting does not address (a) how you extend simga to sigma' -- any extension will do provided it allows you to relieve the hypotheses. The ACL2 theorem prover uses certain heuristics which we'll discuss later, including methods by which you can guide the system in the selection.

    Crucially, it does not discuss whether it is a good idea to do a particular rewrite! For example, the definitional equality:

    (equal (len x)
           (if (endp x)
               0
               (+ 1 (len (cdr x)))))
    
    may be used repeatedly, endlessly, to replace (len a) by an ever growing sequence of terms:
    (len a)
    =
    (if (endp a)
        0
        (+ 1 (len (cdr a))))
    =
    (if (endp a)
        0
        (+ 1 (if (endp (cdr a))
                 0
                 (+ 1 (len (cdr (cdr a)))))))
    = ...
    

    The ACL2 implmentation of rewriting uses certain heuristics and the you can guide the system in its choices. We'll discuss this later.

    Now use your browser's Back Button to return to the example proof in logic-knowledge-taken-for-granted.




    acl2-sources/doc/HTML/LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED.html0000664002132200015000000002140612222333515021473 0ustar kaufmannacl2 LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED.html -- ACL2 Version 6.3

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED

    background knowledge in ACL2 logic for theorem prover tutorial
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    You might think that in order to use the theorem prover you have to know the mathematical logic supported by ACL2. But you need to know a lot less about it than you might think.

    Technically, a theorem is a formula that can be derived from axioms by using rules of inference. Thus, to do a proof you have to know (a) the syntax of formulas, (b) the axioms, and (c) the rules of inference. Traditionally, these things are spelled out in excruciating detail in treatments of mathematical logic -- and for good reason.

    The whole point of proving theorems is that it is a way to determine that a formula is ``always true'' (under some model of the axioms). By ``always true'' we actually mean what logicians mean when they say the formula is valid: true in the model, for all possible values of the variables. Here by ``model of the axioms'' we mean an understanding of the meaning of the various function symbols so that the axioms are true for all values of the variables. If the variables in your conjecture can take on an infinite number of values, proof is often the only way to determine that a conjecture is ``always true.'' So if proof is being used to determine that a questionable formula is always true the proof must be carried out flawlessly. Thus, the (a) syntax, (b) axioms, and (c) rules of inference must be described precisely and followed to the letter.

    But formal mathematical logic was invented to explain how people reason. To the extent that logic mimics human reasoning, proofs can be seen as just extremely carefully crafted arguments. Given that ACL2 is responsible for following the rules ``to the letter,'' your main job is ``explain'' the big leaps.

    To use the theorem prover you must understand (a) the syntax, because you must be able to write formulas flawlessly. But you don't have to know (b) the axioms and (c) the rules of inference at nearly the same level of precision, as long as you understand the basic structure and language of proofs.

    Below is part of a proof of a certain theorem. You ought to be able to understand the following. Since what we describe is a proof of one case of the formula, we hope that you're convinced that the formula holds for that case.

    Read this and follow the links to confirm that you understand what happens. Be sure to then use your browser's Back Button to return to this page and continue.

    An Annotated Proof of

    (implies (true-listp z)
             (equal (rev (rev z)) z))
    

    ``We will prove that reversing the reverse of a true-listp yields the original list. The formula stating this is above. We will prove it by induction on the list structure of z.

    The base case of the induction is:

    (implies (endp z)
             (implies (true-listp z)
                      (equal (rev (rev z)) z))).
    
    This formula is equivalent, by propositional calculus, to
    (implies (and (endp z)
                  (true-listp z))
             (equal (rev (rev z)) z))
    

    Rewriting with the definition of endp produces:

    (implies (and (not (consp z))
                  (true-listp z))
             (equal (rev (rev z)) z))
    

    Rewriting repeatedly starting with the definition of true-listp produces:

    (implies (and (not (consp z))
                  (equal z nil))
             (equal (rev (rev z)) z))
    
    Then using the second hypothesis, just substituting equals for equals, we get
    (implies (and (not (consp z))
                  (equal z nil))
             (equal (rev (rev nil)) nil))
    
    Since the conclusion involves no variables, we can evaluate it, getting
    (implies (and (not (consp z))
                  (equal z nil))
             T)
    
    But this is an instance of the tautology (implies p T). Thus, the base case is proved.''

    Now it is time for a little quiz. There are just three questions.

    Q1: The case above was the Base Case of an inductive proof of

    (implies (true-listp z)
             (equal (rev (rev z)) z))
    
    in which we did induction on the structure of the linear list z. What is the Induction Step? That is, what do you have to prove besides the Base Case to complete this inductive proof?

    Below are four commonly given answers; choose one. Then look here to find out if you're right.

    Induction Step -- Choice (i):
    (implies (not (endp z))
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    Induction Step -- Choice (ii):
    (implies (true-listp (cdr z))
             (equal (rev (rev (cdr z))) (cdr z)))
    
    Induction Step -- Choice (iii):
    (implies (and (not (endp z))
                  (equal (rev (rev (cdr x))) (cdr x)))
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    
    Induction Step -- Choice (iv):
    (implies (and (not (endp z))
                  (implies (true-listp (cdr z))
                           (equal (rev (rev (cdr z))) (cdr z))))
             (implies (true-listp z)
                      (equal (rev (rev z)) z)))
    

    Q2: To prove the Induction Step we must prove one or more of the goals below.

    Which combinations are sufficient to imply the Induction Step? Decide what is required and then look here to find out if you're right. To help you, the Induction Step is of the form:

    Induction Step:
    (implies (and c
                  (implies p' q'))
             (implies p q))
    
    and beside each candidate subgoal we show its structure in those terms.

    Subgoal (i):
    (implies (and (not (endp z))                        ; (implies (and c
                  (true-listp z))                       ;               p)
             (true-listp (cdr z)))                      ;          p')
    
    Subgoal (ii):
    (implies (and (not (endp z))                        ; (implies (and c
                  (true-listp z)                        ;               p
                  (equal (rev (rev (cdr z))) (cdr z)))  ;               q')
             (equal (rev (rev z)) z))                   ;          q)
    
    Subgoal (iii):
    (implies (and (not (endp z))                        ; (implies (and c
                  (equal (rev (rev (cdr z))) (cdr z)))  ;               q')
             (equal (rev (rev z)) z))                   ;          q)
    
    Subgoal (iv):
    (implies (and (not (endp z))                        ; (implies (and c
                  (true-listp (cdr z))                  ;               p'
                  (equal (rev (rev (cdr z))) (cdr z)))  ;               q')
             (equal (rev (rev z)) z))                   ;          q)
    

    Q3: Suppose you know the theorem

    Theorem:
    (implies (p (f x))
             (equal (g (h x))
                    x))
    
    and you wish to rewrite the target (g (h a)) to a in
    Goal Conjecture:
    (implies (and (q (f a))
                  (r a))
             (s (g (h a))))
    
    What must you prove to relieve the hypothesis of Theorem?

    After you've thought about it, look here for our answer.

    End of the Quiz

    If this page made sense, you're ready to read the introduction to the theorem prover.

    If you are reading this as part of the tutorial introduction to the theorem prover, use your browser's Back Button now to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/LOGIC.html0000664002132200015000000000404212222333531015603 0ustar kaufmannacl2 LOGIC.html -- ACL2 Version 6.3

    LOGIC

    to set the default defun-mode to :logic
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    ACL2 p!>:logic
    ACL2 !>
    
    Typing the keyword :logic sets the default defun-mode to :logic.

    Functions defined in :logic mode are logically defined. See defun-mode.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    See defun-mode for a discussion of the defun-modes available and what their effects on the logic are. See default-defun-mode for a discussion of how the default defun-mode is used. This event is equivalent to (table acl2-defaults-table :defun-mode :logic), and hence is local to any books and encapsulate events in which it occurs. See acl2-defaults-table.

    Recall that the top-level form :logic is equivalent to (logic); see keyword-commands. Thus, to change the default defun-mode to :logic in a book, use (logic), which is an embedded event form, rather than :logic, which is not a legal form for books. See embedded-event-form.




    acl2-sources/doc/HTML/LOGICAL-NAME.html0000664002132200015000000001113612222333521016537 0ustar kaufmannacl2 LOGICAL-NAME.html -- ACL2 Version 6.3

    LOGICAL-NAME

    a name created by a logical event
    Major Section:  MISCELLANEOUS
    

    Examples:
    assoc
    caddr
    +
    "ACL2-USER"
    "arith"
    "project/task-1/arith.lisp"
    :here
    

    A logical name is either a name introduced by some event, such as defun, defthm, or include-book, or else is the keyword :here, which refers to the most recent such event. See events. Every logical name is either a symbol or a string. For the syntactic rules on names, see name. The symbols name functions, macros, constants, axioms, theorems, labels, and theories. The strings name packages or books. We permit the keyword symbol :here to be used as a logical name denoting the most recently completed event.

    The logical name introduced by an include-book is the full book name string for the book (see full-book-name). Thus, under the appropriate setting for the current book directory (see cbd) the event (include-book "arith") may introduce the logical name

    "/usr/home/smith/project/task-1/arith.lisp" .
    
    Under a different cbd setting, it may introduce a different logical name, perhaps
    "/local/src/acl2/library/arith.lisp" .
    
    It is possible that identical include-book events forms in a session introduce two different logical names because of the current book directory.

    A logical name that is a string is either a package name or a book name. If it is not a package name, we support various conventions to interpret it as a book name. If it does not end with the string ".lisp" we extend it appropriately. Then, we search for any book name that has the given logical name as a terminal substring. Suppose (include-book "arith") is the only include-book so far and that "/usr/home/smith/project/task-1/arith.lisp" is the source file it processed. Then "arith", "arith.lisp" and "task-1/arith.lisp" are all logical names identifying that include-book event (unless they are package names). Now suppose a second (include-book "arith") is executed and processes "/local/src/acl2/library/arith.lisp". Then "arith" is no longer a logical name, because it is ambiguous. However, "task-1/arith" is a logical name for the first include-book and "library/arith" is a logical name for the second. Indeed, the first can be named by "1/arith" and the second by "y/arith".

    Logical names are used primarily in the theory manipulation functions, e.g., universal-theory and current-theory with which you may obtain some standard theories as of some point in the historical past. The reference points are the introductions of logical names, i.e., the past is determined by the events it contains. One might ask, ``Why not discuss the past with the much more flexible language of command descriptors?'' (See command-descriptor.) The reason is that inside of such events as encapsulate or macro commands that expand to progns of events, command descriptors provide too coarse a grain.

    When logical names are used as referents in theory expressions used in books, one must consider the possibility that the defining event within the book in question becomes redundant by the definition of the name prior to the assumption of the book. See redundant-events.




    acl2-sources/doc/HTML/LOGIOR.html0000664002132200015000000000173312222333524015747 0ustar kaufmannacl2 LOGIOR.html -- ACL2 Version 6.3

    LOGIOR

    bitwise logical inclusive or of zero or more integers
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logior returns their bitwise logical inclusive or. In ACL2 logior is a macro that expands into calls of the binary function binary-logior, except that (logior) expands to 0 and (logior x) expands to x.

    The guard for binary-logior requires its arguments to be integers. Logior is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LOGNAND.html0000664002132200015000000000145712222333524016041 0ustar kaufmannacl2 LOGNAND.html -- ACL2 Version 6.3

    LOGNAND

    bitwise logical `nand' of two integers
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, lognand returns their bitwise logical `nand'.

    The guard for lognand requires its arguments to be integers. Lognand is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGNOR.html0000664002132200015000000000150312222333524015747 0ustar kaufmannacl2 LOGNOR.html -- ACL2 Version 6.3

    LOGNOR

    bitwise logical `nor' of two integers
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, lognor returns the bitwise logical `nor' of the first with the second.

    The guard for lognor requires its arguments to be integers. Lognor is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGNOT.html0000664002132200015000000000172312222333524015755 0ustar kaufmannacl2 LOGNOT.html -- ACL2 Version 6.3

    LOGNOT

    bitwise not of a two's complement number
    Major Section:  ACL2-BUILT-INS
    

    (lognot i) is the two's complement bitwise `not' of the integer i.

    Lognot is actually defined by coercing its argument to an integer (see ifix), negating the result, and then subtracting 1.

    The guard for lognot requires its argument to be an integer.

    Lognot is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGORC1.html0000664002132200015000000000161012222333524016014 0ustar kaufmannacl2 LOGORC1.html -- ACL2 Version 6.3

    LOGORC1

    bitwise logical inclusive or of two ints, complementing the first
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logorc1 returns the bitwise logical inclusive or of the second with the bitwise logical `not' of the first.

    The guard for logorc1 requires its arguments to be integers. Logorc1 is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGORC2.html0000664002132200015000000000161112222333524016016 0ustar kaufmannacl2 LOGORC2.html -- ACL2 Version 6.3

    LOGORC2

    bitwise logical inclusive or of two ints, complementing the second
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logorc2 returns the bitwise logical inclusive or of the first with the bitwise logical `not' of the second.

    The guard for logorc2 requires its arguments to be integers. Logorc2 is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGTEST.html0000664002132200015000000000165712222333524016102 0ustar kaufmannacl2 LOGTEST.html -- ACL2 Version 6.3

    LOGTEST

    test if two integers share a `1' bit
    Major Section:  ACL2-BUILT-INS
    

    When integers x and y are viewed in their two's complement representation, (logtest x y) is true if and only if there is some position for which both x and y have a `1' bit in that position.

    The guard for logtest requires its arguments to be integers. Logtest is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LOGXOR.html0000664002132200015000000000173312222333524015766 0ustar kaufmannacl2 LOGXOR.html -- ACL2 Version 6.3

    LOGXOR

    bitwise logical exclusive or of zero or more integers
    Major Section:  ACL2-BUILT-INS
    

    When integers are viewed in their two's complement representation, logxor returns their bitwise logical exclusive or. In ACL2 logxor is a macro that expands into calls of the binary function binary-logxor, except that (logxor) expands to 0 and (logxor x) expands to x.

    The guard for binary-logxor requires its arguments to be integers. Logxor is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/LOOP-STOPPER.html0000664002132200015000000002475112222333521016661 0ustar kaufmannacl2 LOOP-STOPPER.html -- ACL2 Version 6.3

    LOOP-STOPPER

    limit application of permutative rewrite rules
    Major Section:  MISCELLANEOUS
    

    See rule-classes for a discussion of the syntax of the :loop-stopper field of :rewrite rule-classes. Here we describe how that field is used, and also how that field is created when the user does not explicitly supply it.

    For example, the built-in :rewrite rule commutativity-of-+,

    (implies (and (acl2-numberp x)
                  (acl2-numberp y))
             (equal (+ x y) (+ y x))),
    
    creates a rewrite rule with a loop-stopper of ((x y binary-+)). This means, very roughly, that the term corresponding to y must be ``smaller'' than the term corresponding to x in order for this rule to apply. However, the presence of binary-+ in the list means that certain functions that are ``invisible'' with respect to binary-+ (by default, unary-- is the only such function) are more or less ignored when making this ``smaller'' test. We are much more precise below.

    Our explanation of loop-stopping is in four parts. First we discuss ACL2's notion of ``term order.'' Next, we bring in the notion of ``invisibility'', and use it together with term order to define orderings on terms that are used in the loop-stopping algorithm. Third, we describe that algorithm. These topics all assume that we have in hand the :loop-stopper field of a given rewrite rule; the fourth and final topic describes how that field is calculated when it is not supplied by the user.

    ACL2 must sometimes decide which of two terms is syntactically simpler. It uses a total ordering on terms, called the ``term order.'' Under this ordering constants such as '(a b c) are simpler than terms containing variables such as x and (+ 1 x). Terms containing variables are ordered according to how many occurrences of variables there are. Thus x and (+ 1 x) are both simpler than (cons x x) and (+ x y). If variable counts do not decide the order, then the number of function applications are tried. Thus (cons x x) is simpler than (+ x (+ 1 y)) because the latter has one more function application. Finally, if the number of function applications do not decide the order, a lexicographic ordering on Lisp objects is used. See term-order for details.

    When the loop-stopping algorithm is controlling the use of permutative :rewrite rules it allows term1 to be moved leftward over term2 only if term1 is smaller, in a suitable sense. Note: The sense used in loop-stopping is not the above explained term order but a more complicated ordering described below. The use of a total ordering stops rules like commutativity from looping indefinitely because it allows (+ b a) to be permuted to (+ a b) but not vice versa, assuming a is smaller than b in the ordering. Given a set of permutative rules that allows arbitrary permutations of the tips of a tree of function calls, this will normalize the tree so that the smallest argument is leftmost and the arguments ascend in the order toward the right. Thus, for example, if the same argument appears twice in the tree, as x does in the binary-+ tree denoted by the term (+ a x b x), then when the allowed permutations are done, all occurrences of the duplicated argument in the tree will be adjacent, e.g., the tree above will be normalized to (+ a b x x).

    Suppose the loop-stopping algorithm used term order, as noted above, and consider the binary-+ tree denoted by (+ x y (- x)). The arguments here are in ascending term order already. Thus, no permutative rules are applied. But because we are inside a +-expression it is very convenient if x and (- x) could be given virtually the same position in the ordering so that y is not allowed to separate them. This would allow such rules as (+ i (- i) j) = j to be applied. In support of this, the ordering used in the control of permutative rules allows certain unary functions, e.g., the unary minus function above, to be ``invisible'' with respect to certain ``surrounding'' functions, e.g., + function above.

    Briefly, a unary function symbol fn1 is invisible with respect to a function symbol fn2 if fn2 belongs to the value of fn1 in invisible-fns-table; also see set-invisible-fns-table, which explains its format and how it can be set by the user. Roughly speaking, ``invisible'' function symbols are ignored for the purposes of the term-order test.

    Consider the example above, (+ x y (- x)). The translated version of this term is (binary-+ x (binary-+ y (unary-- x))). The initial invisible-fns-table makes unary-- invisible with repect to binary-+. The commutativity rule for binary-+ will attempt to swap y and (unary-- x) and the loop-stopping algorithm is called to approve or disapprove. If term order is used, the swap will be disapproved. But term order is not used. While the loop-stopping algorithm is permuting arguments inside a binary-+ expression, unary-- is invisible. Thus, insted of comparing y with (unary-- x), the loop-stopping algorithm compares y with x, approving the swap because x comes before y.

    Here is a more precise specification of the total order used for loop-stopping with respect to a list, fns, of functions that are to be considered invisible. Let x and y be distinct terms; we specify when ``x is smaller than y with respect to fns.'' If x is the application of a unary function symbol that belongs to fns, replace x by its argument. Repeat this process until the result is not the application of such a function; let us call the result x-guts. Similarly obtain y-guts from y. Now if x-guts is the same term as y-guts, then x is smaller than y in this order iff x is smaller than y in the standard term order. On the other hand, if x-guts is different than y-guts, then x is smaller than y in this order iff x-guts is smaller than y-guts in the standard term order.

    Now we may describe the loop-stopping algorithm. Consider a rewrite rule with conclusion (equiv lhs rhs) that applies to a term x in a given context; see rewrite. Suppose that this rewrite rule has a loop-stopper field (technically, the :heuristic-info field) of ((x1 y1 . fns-1) ... (xn yn . fns-n)). (Note that this field can be observed by using the command :pr with the name of the rule; see pr.) We describe when rewriting is permitted. The simplest case is when the loop-stopper list is nil (i.e., n is 0); in that case, rewriting is permitted. Otherwise, for each i from 1 to n let xi' be the actual term corresponding to the variable xi when lhs is matched against the term to be rewritten, and similarly correspond yi' with y. If xi' and yi' are the same term for all i, then rewriting is not permitted. Otherwise, let k be the least i such that xi' and yi' are distinct. Let fns be the list of all functions that are invisible with respect to every function in fns-k, if fns-k is non-empty; otherwise, let fns be nil. Then rewriting is permitted if and only if yi' is smaller than xi' with respect to fns, in the sense defined in the preceding paragraph.

    It remains only to describe how the loop-stopper field is calculated for a rewrite rule when this field is not supplied by the user. (On the other hand, to see how the user may specify the :loop-stopper, see rule-classes.) Suppose the conclusion of the rule is of the form (equiv lhs rhs). First of all, if rhs is not an instance of the left hand side by a substitution whose range is a list of distinct variables, then the loop-stopper field is nil. Otherwise, consider all pairs (u . v) from this substitution with the property that the first occurrence of v appears in front of the first occurrence of u in the print representation of rhs. For each such u and v, form a list fns of all functions fn in lhs with the property that u or v (or both) appears as a top-level argument of a subterm of lhs with function symbol fn. Then the loop-stopper for this rewrite rule is a list of all lists (u v . fns).




    acl2-sources/doc/HTML/LOWER-CASE-P.html0000664002132200015000000000170212222333524016546 0ustar kaufmannacl2 LOWER-CASE-P.html -- ACL2 Version 6.3

    LOWER-CASE-P

    recognizer for lower case characters
    Major Section:  ACL2-BUILT-INS
    

    (Lower-case-p x) is true if and only if x is a lower case character, i.e., a member of the list #\A, #\B, ..., #\Z.

    The guard for lower-case-p requires its argument to be a standard character (see standard-char-p).

    Lower-case-p is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/LP.html0000664002132200015000000001135112222333521015261 0ustar kaufmannacl2 LP.html -- ACL2 Version 6.3

    LP

    the Common Lisp entry to ACL2
    Major Section:  MISCELLANEOUS
    

    To enter the ACL2 command loop from Common Lisp, call the Common Lisp program lp (which stands for ``loop,'' as in ``read-eval-print loop'' or ``command loop.'') The ACL2 command loop is actually coded in ACL2 as the function ld (which stands for ``load''). The command loop is just what you get by loading from the standard object input channel, *standard-oi*. Calling ld directly from Common Lisp is possible but fragile because hard lisp errors or aborts throw you out of ld and back to the top-level of Common Lisp. Lp calls ld in such a way as to prevent this and is thus the standard way to get into the ACL2 command loop. Also see acl2-customization for information on the loading of an initialization file.

    All of the visible functionality of lp is in fact provided by ld, which is written in ACL2 itself. Therefore, you should see ld for detailed documentation of the ACL2 command loop. We sketch it below, for novice users.

    Every expression typed to the ACL2 top-level must be an ACL2 expression.

    Any ACL2 expression may be submitted for evaluation. Well-formedness is checked. Some well-formed expressions cannot be evaluated because they involve (at some level) undefined constrained functions (see encapsulate). In addition, ACL2 does not allow ``global variables'' in expressions to be evaluated. Thus, (car '(a b c)) is legal and evaluates to A, but (car x) is not, because there is no ``global context'' or binding environment that gives meaning to the variable symbol x.

    There is an exception to the global variable rule outlined above: single-threaded objects (see stobj) may be used as global variables in top-level expressions. The most commonly used such object is the ACL2 ``current state,'' which is the value of the variable symbol state. This variable may occur in the top-level form to be evaluated, but must be passed only to ACL2 functions ``expecting'' state as described in the documentation for state and for stobjs in general. If the form returns a new state object as one of its values, then that is considered the new ``current'' state for the evaluation of the subsequent form. See state.

    ACL2 provides some support for the functionality usually provided by global variables in a read-eval-print loop, namely the saving of the result of a computation for subsequent re-use in another expression. See assign and see @.

    If the form read is a single keyword, e.g., :pe or :ubt, then special procedures are followed. See keyword-commands.

    The command loop keeps track of the commands you have typed and allows you to review them, display them, and roll the logical state back to that created by any command. See history.

    ACL2 makes the convention that if a top-level form returns three values, the last of which is an ACL2 state, then the first is treated as a flag that means ``an error occurred,'' the second is the value to be printed if no error occurred, and the third is (of course) the new state. When ``an error occurs'' no value is printed. Thus, if you execute a top-level form that happens to return three such values, only the second will be printed (and that will only happen if the first is nil!). See ld for details.




    acl2-sources/doc/HTML/MACRO-ALIASES-TABLE.html0000664002132200015000000000675512222333531017530 0ustar kaufmannacl2 MACRO-ALIASES-TABLE.html -- ACL2 Version 6.3

    MACRO-ALIASES-TABLE

    a table used to associate function names with macro names
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (table macro-aliases-table 'append 'binary-append)
    
    This example associates the function symbol binary-append with the macro name append. As a result, the name append may be used as a runic designator (see theories) by the various theory functions. Thus, for example, it will be legal to write
    (in-theory (disable append))
    
    as an abbreviation for
    (in-theory (disable binary-append))
    
    which in turn really abbreviates
    (in-theory (set-difference-theories (current-theory :here)
                                        '(binary-append)))
    
    General Form:
    
    (table macro-aliases-table 'macro-name 'function-name)
    
    or very generally
    (table macro-aliases-table macro-name-form function-name-form)
    
    where macro-name-form and function-name-form evaluate, respectively, to a macro name and a symbol in the current ACL2 world. See table for a general discussion of tables and the table event used to manipulate tables.

    Note that function-name-form (above) does not need to evaluate to a function symbol, but only to a symbol. As a result, one can introduce the alias before defining a recursive function, as follows.

    (table macro-aliases-table 'mac 'fn)
    (defun fn (x)
      (if (consp x)
          (mac (cdr x))
        x))
    
    Although this is obviously contrived example, this flexibility can be useful to macro writers; see for example the definition of ACL2 system macro defun-inline.

    The table macro-aliases-table is an alist that associates macro symbols with function symbols, so that macro names may be used as runic designators (see theories). For a convenient way to add entries to this table, see add-macro-alias. To remove entries from the table with ease, see remove-macro-alias.

    This table is used by the theory functions; see theories. For example, in order that (disable append) be interpreted as (disable binary-append), it is necessary that the example form above has been executed. In fact, this table does indeed associate many of the macros provided by the ACL2 system, including append, with function symbols. Loosely speaking, it only does so when the macro is ``essentially the same thing as'' a corresponding function; for example, (append x y) and (binary-append x y) represent the same term, for any expressions x and y.




    acl2-sources/doc/HTML/MACRO-ARGS.html0000664002132200015000000000631012222333521016340 0ustar kaufmannacl2 MACRO-ARGS.html -- ACL2 Version 6.3

    MACRO-ARGS

    the formals list of a macro definition
    Major Section:  MISCELLANEOUS
    

    Examples:
    (x y z)
    (x y z &optional max (base '10 basep))
    (x y &rest rst)
    (x y &key max base)
    (&whole sexpr x y)
    

    The ``lambda-list'' of a macro definition may include simple formal parameter names as well as appropriate uses of the following lambda-list keywords from CLTL (pp. 60 and 145), respecting the order shown:

      &whole,
      &optional,
      &rest,
      &body,
      &key, and
      &allow-other-keys.
    
    ACL2 does not support &aux and &environment. In addition, we make the following restrictions:

    (1) initialization forms in &optional and &key specifiers must be quoted values;

    (2) &allow-other-keys may only be used once, as the last specifier; and

    (3) destructuring is not allowed.

    You are encouraged to experiment with the macro facility. One way to do so is to define a macro that does nothing but return the quotation of its arguments, e.g.,

    (defmacro demo (x y &optional opt &key key1 key2)
      (list 'quote (list x y opt key1 key2)))
    
    You may then execute the macro on some sample forms, e.g.,
      term                         value
    (demo 1 2)                (1 2 NIL NIL NIL)
    (demo 1 2 3)              (1 2 3 NIL NIL)
    (demo 1 2 :key1 3)        error:  non-even key/value arglist
                              (because :key1 is used as opt)
    (demo 1 2 3 :key2 5)      (1 2 3 NIL 5)
    
    In particular, Common Lisp specifies that if you use both &rest and &key, then both will be bound using the same list of arguments. The following example should serve to illustrate how this works.
    ACL2 !>(defmacro foo (&rest args &key k1 k2 k3)
             (list 'quote (list args k1 k2 k3)))
    
    Summary
    Form:  ( DEFMACRO FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO
    ACL2 !>(foo :k1 3 :k2 4 :k3 5)
    ((:K1 3 :K2 4 :K3 5) 3 4 5)
    ACL2 !>(foo :k1 3 :k2 4)
    ((:K1 3 :K2 4) 3 4 NIL)
    ACL2 !>(foo :k1 3 :bad-key 7)
    
    
    ACL2 Error in macro expansion:  Illegal key/value args (:BAD-KEY 7)
    in macro expansion of (FOO :K1 3 :BAD-KEY 7).  The argument list for
    FOO is
    (&REST ARGS &KEY K1 K2 K3).
    
    ACL2 !>
    
    If we don't want to get the error above, we can use &allow-other-keys, as follows.
    ACL2 !>(defmacro bar (&rest args &key k1 k2 k3
                                &allow-other-keys)
             (list 'quote (list args k1 k2 k3)))
    
    Summary
    Form:  ( DEFMACRO BAR ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     BAR
    ACL2 !>(bar :k1 3 :bad-key 7)
    ((:K1 3 :BAD-KEY 7) 3 NIL NIL)
    ACL2 !>
    

    Also see trans.




    acl2-sources/doc/HTML/MACRO-COMMAND.html0000664002132200015000000000342012222333525016665 0ustar kaufmannacl2 MACRO-COMMAND.html -- ACL2 Version 6.3

    MACRO-COMMAND

    compound command for the proof-checker
    Major Section:  PROOF-CHECKER
    

    The proof-checker (see proof-checker) allows the user to supply interactive commands. Compound commands, called macro commands, may be defined; these expand into zero or more other commands. Some of these are ``atomic'' macro commands; these are viewed as a single command step when completed successfully.

    More documentation will be written on the proof-checker. For now, we simply point out that there are lots of examples of the use of define-pc-macro and define-pc-atomic-macro in the ACL2 source file "proof-checker-b.lisp". The former is used to create macro commands, which can be submitted to the interactive loop (see verify) and will ``expand'' into zero or more commands. The latter is similar, except that the undoing mechanism (see acl2-pc::undo) understands atomic macro commands to represent single interactive commands. Also see acl2-pc::comm and see acl2-pc::commands for a discussion of the display of interactive commands.

    Also see toggle-pc-macro for how to change a macro command to an atomic macro command, and vice versa.




    acl2-sources/doc/HTML/MAKE-CHARACTER-LIST.html0000664002132200015000000000116512222333524017533 0ustar kaufmannacl2 MAKE-CHARACTER-LIST.html -- ACL2 Version 6.3

    MAKE-CHARACTER-LIST

    coerce to a list of characters
    Major Section:  ACL2-BUILT-INS
    

    Non-characters in the given list are coerced to the character with code 0.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MAKE-EVENT-DETAILS.html0000664002132200015000000003462312222333517017441 0ustar kaufmannacl2 MAKE-EVENT-DETAILS.html -- ACL2 Version 6.3

    MAKE-EVENT-DETAILS

    details on make-event expansion
    Major Section:  MAKE-EVENT
    

    The normal user of make-event can probably ignore this section, but we include it for completeness. We assume that the reader has read and understood the basic documentation for make-event (see make-event), but we begin below with a summary of expansion.

    Introduction

    Here is a summary of how we handle expansion involving make-event forms.

    (make-event form :check-expansion nil)

    This shows the :check-expansion default of nil, and is typical user input. We compute the expansion exp of form, which is the expansion of the original make-event expression and is evaluated in place of that expression.

    (make-event form :check-expansion t)

    The user presumably wants it checked that the expansion doesn't change in the future, in particular during include-book. If the expansion of form is exp, then we will evaluate exp to obtain the value as before, but this time we record that the expansion of the original make-event expression is (make-event form :check-expansion exp) rather than simply exp.

    (make-event form :check-expansion exp) ; exp a cons

    This is generated for the case that :check-expansion is t, as explained above. Evaluation is handled as described in that above case, except here we check that the expansion result is the given exp. (Actually, the user is also allowed supply such a form.) The original make-event expression does not undergo any expansion (intuitively, it expands to itself).

    Now let us take a look at how we expand progn forms (encapsulate is handled similarly).

    (progn ... (make-event form :check-expansion nil) ...)

    The expansion is obtained by replacing the make-event form as follows. Let exp be the expansion of form, Then replace the above make-event form, which we denote as F, by (record-expansion F exp). Here, record-expansion is a macro that returns its second argument.

    (progn ... (make-event form :check-expansion t) ...)

    The expansion is of the form (record-expansion F exp) as in the nil case above, except that this time exp is (make-event form :check-expansion exp'), where exp' is the expansion of form.

    (progn ... (make-event form :check-expansion exp) ...) ; exp a cons

    No expansion takes place unless expansion takes place for at least one of the other subforms of the progn, in which case each such form F is replaced by (record-expansion F exp) where exp is the expansion of F.

    Detailed semantics

    In our explanation of the semantics of make-event, we assume familiarity with the notion of ``embedded event form'' (see embedded-event-form).

    Let's say that the ``actual embedded event form'' corresponding to a given form is the underlying call of an ACL2 event: that is, LOCALs are dropped when ld-skip-proofsp is 'include-book, and macros are expanded away, thus leaving us with a progn, a make-event, or an event form (possibly encapsulate), any of which might have surrounding local, skip-proofs, or with-output calls.

    Thus, such an actual embedded event form can be viewed as having the form (rebuild-expansion wrappers base-form) where base-form is a progn, a make-event, or an event form (possibly encapsulate), and wrappers are (as in ACL2 source function destructure-expansion) the result of successively removing the event form from the result of macroexpansion, leaving a sequence of (local), (skip-proofs), and (with-output ...) forms. In this case we say that the form ``destructures into'' the indicated wrappers and base-form, and that it can be ``rebuilt from'' those wrappers and base-form.

    Elsewhere we define the notion of the ``expansion result'' from an evaluation (see make-event), and we mention that when expansion concludes, the ACL2 logical world and most of the state are restored to their pre-expansion values. Specifically, after evaluation of the argument of make-event (even if it is aborted), the ACL2 logical world is restored to its pre-evaluation value, as are all state global variables in the list *protected-system-state-globals*. Thus, assignments to user-defined state globals (see assign) do persist after expansion, since they are not in that list.

    We recursively define the combination of evaluation and expansion of an embedded event form, as follows. We also simultaneously define the notion of ``expansion takes place,'' which is assumed to propagate upward (in a sense that will be obvious), such that if no expansion takes place, then the expansion of the given form is considered to be itself. It is useful to keep in mind a goal that we will consider later: Every make-event subterm of an expansion result has a :check-expansion field that is a consp, where for this purpose make-event is viewed as a macro that returns its :check-expansion field. (Implementation note: The latest expansion of a make-event, progn, progn!, or encapsulate is stored in state global 'last-make-event-expansion, except that if no expansion has taken place for that form then 'last-make-event-expansion has value nil.)

    If the given form is not an embedded event form, then simply cause a soft error, (mv erp val state) where erp is not nil. Otherwise:

    If the evaluation of the given form does not take place (presumably because local events are being skipped), then no expansion takes place. Otherwise:

    Let x be the actual embedded event form corresponding to the given form, which destructures into wrappers W and base-form B. Then the original form is evaluated by evaluating x, and its expansion is as follows.

    If B is (make-event form :check-expansion val), then expansion takes place if and only if val is not a consp and no error occurs, as now described. Let R be the expansion result from protected evaluation of form, if there is no error. R must be an embedded event form, or it is an error. Then evaluate/expand R, where if val is not nil then state global 'ld-skip-proofsp is initialized to nil. (This initialization is important so that subsequent expansions are checked in a corresponding environment, i.e., where proofs are turned on in both the original and subsquent environments.) It is an error if this evaluation causes an error. Otherwise, the evaluation yields a value, which is the result of evaluation of the original make-event expression, as well as an expansion, E_R. Let E be rebuilt from W and E_R. The expansion of the original form is E if val is nil, and otherwise is the result of replacing the original form's :check-expansion field with E, with the added requirement that if val is not t (thus, a consp) then E must equal val or else we cause an error.

    If B is either (progn form1 form2 ...) or (encapsulate sigs form1 form2 ...), then after evaluating B, the expansion of the original form is the result of rebuilding from B, with wrappers W, after replacing each formi in B for which expansion takes place by (record-expansion formi formi'), where formi' is the expansion of formi. Note that these expansions are determined as the formi are evaluated in sequence (where in the case of encapsulate, this determination occurs only during the first pass). Except, if no expansion takes place for any formi, then the expansion of the original form is itself.

    Otherwise, the expansion of the original form is itself.

    Similarly to the progn and encapsulate cases above, book certification causes a book to be replaced by its so-called ``book expansion.'' There, each event ev for which expansion took place during the proof pass of certification -- say, producing ev' -- is replaced by (record-expansion ev ev').

    Implementation Note. The book expansion is actually implemented by way of the :expansion-alist field of its certificate, which associates 0-based positions of top-level forms in the book (not including the initial in-package form) with their expansions. Thus, the book's source file is not overwritten; rather, the certificate's expansion-alist is applied when the book is included or compiled. End of Implementation Note.

    It is straightforward by computational induction to see that for any expansion of an embedded event form, every make-event sub-event has a consp :check-expansion field. Here, by ``sub-event'' we mean to expand macros; and we also mean to traverse progn and encapsulate forms as well as :check-expansion fields of make-event forms. Thus, we will only see make-event forms with consp :check-expansion fields in the course of include-book forms, the second pass of encapsulate forms, and raw Lisp. This fact guarantees that an event form will always be treated as its original expansion.

    Notes on ttags

    See defttag for documentation of the notion of ``trust tag'' (``ttag''). We note here that even if an event (defttag tag-name) for non-nil tag-name is admitted only during the expansion phase of a make-event form, then such expansion will nevertheless still cause tag-name to be recorded in the logical world (assuming that the make-event form is admitted). So in order to certify such a book, a suitable :ttags argument must be supplied; see certify-book.

    ACL2 does provide a way to avoid the need for :ttags arguments in such cases. The idea is to certify a book twice, where the results of make-event expansion are saved from the first call of certify-book and provided to the second call. See set-write-acl2x.

    Finally, we discuss a very unusual case where certification does not involve trust tags but a subsequent include-book does involve trust tags: a make-event call specifying :check-expansion t, whose expansion generates a defttag event during include-book but not certify-book. Consider the following book.

    (in-package "ACL2")
    (make-event
     (er-progn
      (if (@ skip-notify-on-defttag) ; non-nil when including a certified book
          (pprogn
           (fms "Value of (@ skip-notify-on-defttag): ~x0~|"
                (list (cons #0 (@ skip-notify-on-defttag)))
                *standard-co* state nil)
           (encapsulate
            ()
            (defttag :foo)
            (value-triple "Imagine something bad here!")))
        (value nil))
      (value '(value-triple :some-value)))
     :check-expansion t)
    
    This book certifies successfully without the need for a :ttags argument for certify-book. Indeed, the above book's certificate does not specify :foo as a trust tag associated with the book, because no defttag event was executed during book certification. Unfortunately, if we try to include this book without specifying a value of :ttags that allows :foo, book inclusion will cause executing of the above defttag. If that inclusion happens in the context of certifying some superior book and the appropriate :ttags arguments have not been provided, that certification will fail.




    acl2-sources/doc/HTML/MAKE-EVENT.html0000664002132200015000000010656712222333517016365 0ustar kaufmannacl2 MAKE-EVENT.html -- ACL2 Version 6.3

    MAKE-EVENT

    evaluate (expand) a given form and then evaluate the result
    Major Section:  EVENTS
    

    Make-event is a utility for generating events. It provides a capability not offered by Lisp macros (see defmacro), as it allows access to the ACL2 state and logical world. In essence, the expression (make-event form) replaces itself with the result of evaluating form, say, ev, as though one had submitted ev instead of the make-event call. For example, (make-event (quote (defun f (x) x))) is equivalent to the event (defun f (x) x).

    We break this documentation into the following sections.

    Introduction
    Detailed Documentation
    Error Reporting
    Restriction to Event Contexts
    Examples Illustrating How to Access State
    Advanced Expansion Control

    We begin with an informal introduction, which focuses on examples and introduces the key notion of ``expansion phase''.

    Introduction

    Make-event is particularly useful for those who program using the ACL2 state; see programming-with-state. That is because the evaluation of form may read and even modify the ACL2 state.

    Suppose for example that we want to define a constant *world-length*, that is the length of the current ACL2 world. A make-event form can accomplish this task, as follows.

      ACL2 !>(length (w state))
      98883
      ACL2 !>(make-event
              (list 'defconst '*world-length* (length (w state))))
    
      Summary
      Form:  ( DEFCONST *WORLD-LENGTH* ...)
      Rules: NIL
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
      Summary
      Form:  ( MAKE-EVENT (LIST ...))
      Rules: NIL
      Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
       *WORLD-LENGTH*
      ACL2 !>*world-length*
      98883
      ACL2 !>(length (w state))
      98890
      ACL2 !>
    
    How did this work? First, evaluation of the form (list 'defconst '*world-length* (length (w state))) returned the event form (defconst *world-length* 98883). Then that event form was automatically submitted to ACL2. Of course, that changed the ACL2 logical world, which is why the final value of (length (w state)) is greater than its initial value.

    The example above illustrates how the evaluation of a make-event call takes place in two phases. The first phase evaluates the argument of the call, in this case (list 'defconst '*world-length* (length (w state))), to compute an event form, in this case (defconst *world-length* 98883). We call this evaluation the ``expansion'' phase. Then the resulting event form is evaluated, which in this case defines the constant *world-length*.

    Now suppose we would like to introduce such a defconst form any time we like. It is common practice to define macros to automate such tasks. Now we might be tempted simply to make the following definition.

    ; WRONG!
    (defmacro define-world-length-constant (name state)
      (list 'defconst name (length (w state))))
    
    But ACL2 rejects such a definition, because a macro cannot take the ACL2 state as a parameter; instead, the formal parameter to this macro named "STATE" merely represents an ordinary object. You can try to experiment with other such direct methods to define such a macro, but they won't work.

    Instead, however, you can use the approach illustrated by the make-event example above to define the desired macro, as follows.

    (defmacro define-world-length-constant (name)
      `(make-event (list 'defconst ',name (length (w state)))))
    
    Here are example uses of this macro.
      ACL2 !>(define-world-length-constant *foo*)
    
      Summary
      Form:  ( DEFCONST *FOO* ...)
      Rules: NIL
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
      Summary
      Form:  ( MAKE-EVENT (LIST ...))
      Rules: NIL
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
       *FOO*
      ACL2 !>*foo*
      98891
      ACL2 !>:pe *foo*
                2:x(DEFINE-WORLD-LENGTH-CONSTANT *FOO*)
                   
      >             (DEFCONST *FOO* 98891)
      ACL2 !>(length (w state))
      98897
      ACL2 !>(define-world-length-constant *bar*)
    
      Summary
      Form:  ( DEFCONST *BAR* ...)
      Rules: NIL
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
      Summary
      Form:  ( MAKE-EVENT (LIST ...))
      Rules: NIL
      Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
       *BAR*
      ACL2 !>*bar*
      98897
      ACL2 !>:pe *bar*
                3:x(DEFINE-WORLD-LENGTH-CONSTANT *BAR*)
                   
      >             (DEFCONST *BAR* 98897)
      ACL2 !>(length (w state))
      98903
      ACL2 !>
    

    Finally, we note that the expansion phase can be used for computation that has side effects, generally by modifying state. Here is a modification of the above example that does not change the world at all, but instead saves the length of the world in a state global.

    (make-event
     (pprogn (f-put-global 'my-world-length (length (w state)) state)
             (value '(value-triple nil))))
    
    Notice that this time, the value returned by the expansion phase is not an event form, but rather, is an error triple (see error-triples) whose value component is an event form, namely, the event form (value-triple nil). Evaluation of that event form does not change the ACL2 world (see value-triple). Thus, the sole purpose of the make-event call above is to change the state by associating the length of the current logical world with the state global named 'my-world-length. After evaluating this form, (@ my-world-length) provides the length of the ACL2 world, as illustrated by the following transcript.
      ACL2 !>:pbt 0
                0:x(EXIT-BOOT-STRAP-MODE)
      ACL2 !>(length (w state))
      98883
      ACL2 !>(make-event
              (pprogn (f-put-global 'my-world-length (length (w state)) state)
                      (value '(value-triple nil))))
    
      Summary
      Form:  ( MAKE-EVENT (PPROGN ...))
      Rules: NIL
      Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
       NIL
      ACL2 !>(length (w state))
      98883
      ACL2 !>:pbt 0
                0:x(EXIT-BOOT-STRAP-MODE)
      ACL2 !>
    

    When make-event is invoked by a book, it is expanded during book certification but not, by default, when the book is included. So for the example (define-world-length-constant *foo*) given above, if that form is in a book, then the value of *foo* will be the length of the world at the time this form was invoked during book certification, regardless of world length at include-book time. (The expansion is recorded in the book's certificate, and re-used.) To overcome this default, you can specified keyword value :CHECK-EXPANSION t. This will cause an error if the expansion is different, but it can be useful for side effects. For example, if you insert the following form in a book, then the length of the world will be printed when the form is encountered, whether during certify-book or during include-book.

    (make-event
     (pprogn (fms "Length of current world: ~x0~|"
                  (list (cons #\0 (length (w state))))
                  *standard-co* state nil)
             (value '(value-triple nil)))
     :check-expansion t)
    

    Some Related Topics

    Detailed Documentation

    Examples:
    
    ; Trivial example: evaluate (quote (defun foo (x) x)) to obtain
    ; (defun foo (x) x), which is then evaluated.
    (make-event (quote (defun foo (x) x)))
    
    ; Evaluate (generate-form state) to obtain (mv nil val state), and
    ; then evaluate val.  (Generate-form is not specified here, but
    ; imagine for example that it explores the state and then generates
    ; some desired definition or theorem.)
    (make-event (generate-form state))
    
    ; As above, but make sure that if this form is in a book, then when
    ; we include the book, the evaluation of (generate-form state)
    ; should return the same value as it did when the book was
    ; certified.
    (make-event (generate-form state)
                :CHECK-EXPANSION t)
    
    ; As above (where the :CHECK-EXPANSION value can be included or
    ; not), where if there is an error during expansion, then the error
    ; message will explain that expansion was on behalf of the indicated
    ; object, typically specified as the first argument.
    (make-event (generate-form state)
                :ON-BEHALF-OF (generate-form state))
    
    General Form:
    (make-event form :CHECK-EXPANSION chk :ON-BEHALF-OF obj :EXPANSION? form)
    
    where chk is nil (the default), t, or the intended ``expansion result'' from the evaluation of form (as explained below); and if supplied, obj is an arbitrary ACL2 object, used only in reporting errors in expansion, i.e., in the evaluation of form. The :EXPANSION? keyword is discussed in the final section, on Advanced Expansion Control.

    We strongly recommend that you browse some .lisp files in the community books directory books/make-event/. You may even find it helpful, in order to understand make-event, to do so before continuing to read this documentation. You may also find it useful to browse community book books/misc/eval.lisp, which contains definitions of macros must-succeed and must-fail that are useful for testing and are used in many books in the books/make-event/ directory, especially eval-tests.lisp. Another example, books/make-event/defrule.lisp, shows how to use macros whose calls expand to make-event forms, which in turn can generate events. For more examples, see file books/make-event/Readme.lsp. Other than the examples, the explanations here should suffice for most users. If you want explanations of subtler details, see make-event-details.

    Note that make-event may only be used at the ``top level'' or where an event is expected. See the section ``Restriction to Event Contexts'', below.

    Make-event is related to Lisp macroexpansion in the sense that its argument is evaluated to obtain an expansion result, which is evaluated again. Let us elaborate on each of these notions in turn: ``is evaluated,'' ``expansion result'', and ``evaluated again.'' The final section, on Advanced Expansion Control, will generalize these processes in a way that we ignore for now.

    ``is evaluated'' -- The argument can be any expression, which is evaluated as would be any expression submitted to ACL2's top level loop. Thus, state and user-defined stobjs may appear in the form supplied to make-event. Henceforth, we will refer to this evaluation as ``expansion.'' Expansion is actually done in a way that restores ACL2's built-in state global variables, including the logical world, to their pre-expansion values (with a few exceptions -- see make-event-details -- and where we note that changes to user-defined state global variables (see assign) are preserved). So, for example, events might be evaluated during expansion, but they will disappear from the logical world after expansion returns its result. Moreover, proofs are enabled by default at the start of expansion (see ld-skip-proofsp) if keyword :CHECK-EXPANSION is supplied and has a non-nil value.

    ``expansion result'' -- The above expansion may result in an ordinary (non-state, non-stobj) value, which we call the ``expansion result.'' Or, expansion may result in a multiple value of the form (mv erp val state), or, more generally, (mv erp val state stobj-1 ... stobj-k) where each stobj-i is a stobj; then the expansion result is val unless erp is not nil, in which case there is no expansion result, and the original make-event evaluates to a soft error. In either case (single or multiple value), either val is an embedded event form (see embedded-event-form), or else the original make-event evaluates to a soft error, printed as described under ``Error Reporting'' below.

    ``evaluated again'' -- the expansion result is evaluated in place of the original make-event.

    The expansion process can invoke subsidiary calls of make-event, and the expansion result can (perhaps after macroexpansion) be a call of make-event. It can be useful to track all these make-event calls. The state global variable make-event-debug may be set to a non-nil value, for example (assign make-event-debug t), in order to see a trace of the expansion process, where a level is displayed (as in ``3>'') to indicate the depth of subsidiary expansions.

    Expansion of a make-event call will yield an event that replaces the original make-event call. In particular, if you put a make-event form in a book, then in essence it is replaced by its expansion result, created during the proof pass of the certify-book process. We now elaborate on this idea of keeping the original expansion.

    A make-event call generates a ``make-event replacement'' that may be stored by the system. In the simplest case, this replacement is the expansion result. When a book is certified, these replacements are stored in a book's certificate (technically, in the :EXPANSION-ALIST field). Thus, although the book is not textually altered during certification, one may imagine a ``book expansion'' corresponding to the original book, in which events are substituted by replacements that were generated during the proof phase of certification. A subsequent include-book will then include the book expansion corresponding to the indicated book. When a book is compiled during certify-book, it is actually the corresponding book expansion, stored as a temporary file, that is compiled instead. That temporary file is deleted after compilation unless one first evaluates the form (assign keep-tmp-files t). Note however that all of the original forms must still be legal events; see embedded-event-form. So for example, if the first event in a book is (local (defmacro my-id (x) x)), and is followed by (my-id (make-event ...)), the final ``include-book'' pass of certify-book will fail because my-id is not defined when the my-id call is encountered.

    A make-event replacement might not be the expansion when either of the keyword arguments :CHECK-EXPANSION or :EXPANSION? is supplied. We deal with the latter in the final section, on Advanced Expansion Control. If :CHECK-EXPANSION t is supplied and the expansion is exp, then the replacement is obtained from the original make-event call, by substituting exp for t as the value of keyword :CHECK-EXPANSION. Such a make-event call -- during the second pass of an encapsulate or during event processing on behalf of include-book -- will do the expansion again and check that the expansion result is equal to the original expansion result, exp. In the unusual case that you know the expected expansion result, res, you can specify :CHECK-EXPANSION res in the first place, so that the check is also done during the initial evaluation of the make-event form. IMPORTANT BUT OBSCURE DETAIL: That expansion check is only done when processing events, not during a preliminary load of a book's compiled file. The following paragraph elaborates.

    (Here are details on the point made just above, for those who use the :CHECK-EXPANSION argument to perform side-effects on the state. When you include a book, ACL2 generally loads a compiled file before processing the events in the book; see book-compiled-file. While it is true that a non-nil :CHECK-EXPANSION argument causes include-book to perform expansion of the make-event form during event processing it does not perform expansion when the compiled file (or expansion file; again, see book-compiled-file) is loaded.)

    ACL2 performs the following space-saving optimization: when the expansion result is a local event, then the make-event replacement is (local (value-triple :ELIDED)).

    The notion of ``expansion'' and ``replacement'' extend to the case that a call of make-event is found in the course of macroexpansion. The following example illustrates this point.

    (encapsulate
     ()
     (defmacro my-mac ()
       '(make-event '(defun foo (x) x)))
     (my-mac))
    :pe :here
    
    The above call of pe shows that the form (my-mac) has a make-event expansion (and replacement) of (DEFUN FOO (X) X):
    (ENCAPSULATE NIL
                 (DEFMACRO MY-MAC
                           NIL
                           '(MAKE-EVENT '(DEFUN FOO (X) X)))
                 (RECORD-EXPANSION (MY-MAC)
                                   (DEFUN FOO (X) X)))
    

    Error Reporting

    Suppose that expansion produces a soft error as described above. That is, suppose that the argument of a make-event call evaluates to a multiple value (mv erp val state ...) where erp is not nil. If erp is a string, then that string is printed in the error message. If erp is a cons pair whose car is a string, then the error prints "~@0" with #\0 bound to that cons pair; see fmt. Any other non-nil value of erp causes a generic error message to be printed.

    Restriction to Event Contexts

    A make-event call must occur either at the top level, or during make-event expansion, or as an argument of an event constructor. We explain in more detail below. This restriction is imposed to enable ACL2 to track expansions produced by make-event.

    The following examples illustrate this restriction.

    ; Legal:
    (progn (with-output
            :on summary
            (make-event '(defun foo (x) x))))
    
    ; Illegal:
    (mv-let (erp val state)
            (make-event '(defun foo (x) x))
            (mv erp val state))
    

    More precisely: a make-event call that is not itself evaluated during make-event expansion is subject to the following requirement. After macroexpansion has taken place, such a make-event call must be in an ``event context'', defined recursively as follows. (All but the first two cases below correspond to similar cases for constructing events; see embedded-event-form.)

    o A form submitted at the top level, or more generally, supplied to a call of ld, is in an event context.

    o A form occurring at the top level of a book is in an event context.

    o If (LOCAL x1) is in an event context, then so is x1.

    o If (SKIP-PROOFS x1) is in an event context, then so is x1.

    o If (MAKE-EVENT x ...) is in an event context and its expansion x1 is an embedded event form, then x1 is in an event context.

    o If (WITH-OUTPUT ... x1), (WITH-PROVER-STEP-LIMIT ... x1 ...), or (WITH-PROVER-TIME-LIMIT ... x1) is in an event context, then so is x1.

    o For any call of PROGN or PROGN!, each of its arguments is in an event context.

    o For any call of ENCAPSULATE, each of its arguments except the first (the signature list) is in an event context.

    o If (RECORD-EXPANSION x1 x2) is in an event context, then x1 and x2 are in event contexts. Note: record-expansion is intended for use only by the implementation, which imposes the additional restriction that x1 and its subsidiary make-event calls (if any) must specify a :CHECK-EXPANSION argument that is a consp.

    Low-level remark, for system implementors. There is the one exception to the above restriction: a single state-global-let* form immediately under a progn! call. For example:

    (progn! (state-global-let* <bindings> (make-event ...)))
    
    However, the following form may be preferable (see progn!):
    (progn! :STATE-GLOBAL-BINDINGS <bindings> (make-event ...))
    
    Also see remove-untouchable for an interesting use of this exception.

    Examples Illustrating How to Access State

    You can modify the ACL2 state by doing your state-changing computation during the expansion phase, before expansion returns the event that is submitted. Here are some examples.

    First consider the following. Notice that expansion modifies state global my-global during make-event expansion, and then expansion returns a defun event to be evaluated.

    (make-event
      (er-progn (assign my-global (length (w state)))
                (value '(defun foo (x) (cons x x)))))
    
    Then we get:
      ACL2 !>(@ my-global)
      72271
      ACL2 !>:pe foo
       L        1:x(MAKE-EVENT (ER-PROGN # #))
                   
      >L            (DEFUN FOO (X) (CONS X X))
      ACL2 !>
    

    Here's a slightly fancier example, where the computation affects the defun. In a new session, execute:

    (make-event
      (er-progn (assign my-global (length (w state)))
                (value `(defun foo (x) (cons x ,(@ my-global))))))
    
    Then:
      ACL2 !>(@ my-global)
      72271
      ACL2 !>:pe foo
       L        1:x(MAKE-EVENT (ER-PROGN # #))
                   
      >L            (DEFUN FOO (X) (CONS X 72271))
      ACL2 !>
    
    Note that ACL2 table events may avoid the need to use state globals. For example, instead of the example above, consider this example in a new session.
    (make-event
      (let ((world-len (length (w state))))
        `(progn (table my-table :STORED-WORLD-LENGTH ,world-len)
                (defun foo (x) (cons x ,world-len)))))
    
    Then:
      ACL2 !>(table my-table)
       ((:STORED-WORLD-LENGTH . 72271))
      ACL2 !>:pe foo
                1:x(MAKE-EVENT (LET # #))
                   
      >L            (DEFUN FOO (X) (CONS X 72271))
      ACL2 !>
    

    By the way, most built-in state globals revert after expansion. But your own global (like my-global above) can be set during expansion, and the new value will persist.

    Advanced Expansion Control

    We conclude this documentation section by discussing three kinds of additional control over make-event expansion. These are all illustrated in community book books/make-event/make-event-keywords-or-exp.lisp. The discussion below is split into the following three parts.

    (1) The value produced by expansion may have the form (:DO-PROOFS exp), which specifies exp as the expansion result, to be evaluated without skipping proofs even when including a book.

    (2) The value produced by expansion may have the form (:OR exp-1 ... exp-k), which specifies that the first form exp-i to evaluate without error is the expansion result.

    (3) The keyword argument :EXPANSION? can serve to eliminate the storing of make-event replacements, as described above for the ``book expansion'' of a book.

    We now elaborate on each of these.

    (1) :DO-PROOFS ``call'' produced by expansion.

    We have discussed the expansion result produced by the expansion phase of evaluating a make-event call. However, if the expansion phase produces an expression of the form (:DO-PROOFS exp), then the expansion result is actually exp. The :DO-PROOFS wrapper indicates that even if proofs are currently being skipped (see ld-skip-proofsp), then evaluation of exp should take place with proofs not skipped. For example, proofs will be performed when evaluating the make-event expansion, namely the indicated defthm event, in the following example.

    (set-ld-skip-proofsp t state)
    (make-event '(:DO-PROOFS
                  (defthm app-assoc (equal
                                     (append (append x y) z)
                                     (append x y z)))))
    

    Note that such use of :DO-PROOFS causes proofs to be performed when evaluating the expansion while including an uncertified book. But when including a certified book, then unless :CHECK-EXPANSION is supplied a non-nil value, the make-event replacement will just be the expansion, which does not include the :DO-PROOFS wrapper and hence will be evaluated with proofs skipped.

    (2) :OR ``call'' produced by expansion.

    There may be times where you want to try different expansions. For example, the community book books/make-event/proof-by-arith.lisp attempts to admit a given event, which we'll denote EV, by trying events of the following form as BOOK varies over different community books.

    (encapsulate
     ()
     (local (include-book BOOK :DIR :SYSTEM))
     EV)
    
    A naive implementation of this macro would evaluate all such encapsulate events until one succeeds, and then return that successful event as the expansion. Then that event would need to be evaluated again! With some hacking one could avoid that re-evaluation by using skip-proofs, but that won't work if you are trying to create a certified book without skipped proofs. Instead, the implementation creates an expansion of the form (:OR ev-1 ev-2 ... ev-k), where the list (ev-1 ev-2 ... ev-k) enumerates the generated encapsulate events. In general, for this ``disjunctive case'' of a result from expansion, each ev-i is evaluated in sequence, and the first that succeeds without error is considered to be the expansion result -- and a repeat evaluation is avoided. If evaluation of each ev-i results in an error, then so does the make-event call.

    This special use of :OR in a value produced by expansion is only supported at the top level. That is, the result can be (:OR ev-1 ev-2 ... ev-k) but then each ev-i must be a legal expansion result, without such further use of :OR -- except, ev-i may be (:DO-PROOFS ev-i'), where ev-i' then would serve as the expansion rather than ev-i.

    (3) The :EXPANSION? keyword argument.

    If keyword argument :EXPANSION? has a nonnil value, then the :CHECK-EXPANSION keyword must be omitted or have value nil or t, hence not a cons pair.

    The idea of the :EXPANSION? keyword is to give you a way to avoid storing expansion results in a book's certificate. Roughly speaking, when the expansion result matches the value of :EXPANSION?, then no expansion result is stored for the event by book certification; then when the book is later included, the value of :EXPANSION? is used as the expansion, thus bypassing the expansion phase. One could say that the event is its own make-event replacement, but it is more accurate to say that there is no make-event replacement at all, since nothing is stored in the certificate for this event. Below, we elaborate on make-event replacements when :EXPANSION is used and also discuss other properties of this keyword.

    We modify the notion of ``expansion result'' for make-event forms to comprehend the use of the :EXPANSION? keyword. For that purpose, let's consider a call of make-event to be ``reducible'' if it has an :EXPANSION? keyword with non-nil value, exp, and its :CHECK-EXPANSION keyword is missing or has value nil, in which case the ``reduction'' of this make-event call is defined to be exp. The expansion result as originally defined is modified by the following ``recursive reduction'' process: recur through the original expansion, passing through calls of local, skip-proofs, with-output, with-prover-step-limit, and with-prover-time-limit, and replacing (recursively) any reducible call of make-event by its reduction. Furthermore, we refer to two forms as ``reduction equivalent'' if their recursive reductions are equal. Note that the recursive reduction process does not pass through progn or encapsulate, but that process is applied to the computation of expansions for their subsidiary make-event calls.

    To explain further the effect of :EXPANSION? exp, we split into the following two cases.

    o Case 1: Evaluation is not taking place when including a book or evaluating the second pass of an encapsulate event; more precisely, the value of (ld-skip-proofsp state) is not the symbol INCLUDE-BOOK. There are two subcases.

    - Case 1a: The expansion result is not reduction-equivalent to exp. Then the make-event call is processed as though the :EXPANSION? keyword had been omitted.

    - Case 2a: The expansion result is reduction-equivalent to exp. Then there is no make-event replacement for this call of make-event; no replacement will be put into the certificate file for a book containing this make-event call. When that book is subsequently included, the original form will be evaluated in the manner described in the next case.

    o Case 2: Evaluation is taking place when including a book or evaluating the second pass of an encapsulate event; more precisely, the value of (ld-skip-proofsp state) is the symbol INCLUDE-BOOK. Then the expansion is exp. The expansion phase is skipped unless :CHECK-EXPANSION is t.

    The :EXPANSION? keyword can be particularly useful in concert with the disjunctive (``:OR'') case (2) discussed above. Suppose that expansion produces a value as discussed in (2) above, (:OR exp-1 ... exp-k). If one of these expressions exp-i is more likely than the others to be the expansion, then you may wish to specify :EXPANSION? exp-i, as this will avoid storing a make-event replacement in that common case. This could be useful if the expressions are large, to avoid enlarging the certificate file for a book containing the make-event call.

    It is legal to specify both :EXPANSION? exp and :CHECK-EXPANSION t. When either (ld-skip-proofsp state) is the symbol INCLUDE-BOOK, or evaluation is taking place in raw Lisp, then this combination is treated the same as if :EXPANSION? is omitted and the value of :CHECK-EXPANSION is exp. Otherwise, this combination is treated the same as :CHECK-EXPANSION t, modified to accommodate the effect of :EXPANSION? as discussed above: if the expansion is indeed the value of :EXPANSION?, then no make-event replacement is generated.




    acl2-sources/doc/HTML/MAKE-FAST-ALIST.html0000664002132200015000000000272512222333520017074 0ustar kaufmannacl2 MAKE-FAST-ALIST.html -- ACL2 Version 6.3

    MAKE-FAST-ALIST

    (make-fast-alist alist) creates a fast-alist from the input alist, returning alist itself or, in some cases, a new object equal to it.
    Major Section:  HONS-AND-MEMOIZATION
    

    Note: it is often better to use with-fast-alist; see with-fast-alist.

    Logically, make-fast-alist is the identity function.

    Under the hood, we construct and return an object that is equal to alist and which is a fast alist. If alist is already a fast alist, almost no work is required: we simply return it unchanged.

    When alist is not fast, we must minimally construct a hash table for its bindings. It is often possible to bind this new hash table to alist itself. But in certain cases when the alist keys are not normed, a new alist must be constructed, also, and so we may return an equal but not eq alist. (In these cases, we still try to avoid at least some consing by reusing the "longest normed tail" of the alist.)




    acl2-sources/doc/HTML/MAKE-LIST.html0000664002132200015000000000165612222333524016246 0ustar kaufmannacl2 MAKE-LIST.html -- ACL2 Version 6.3

    MAKE-LIST

    make a list of a given size
    Major Section:  ACL2-BUILT-INS
    

    For a nonnegative integer size, (Make-list size) is a list of elements of length size, each of which is initialized to the :initial-element (which defaults to nil).

    Make-list is a macro in ACL2, defined in terms of a tail recursive function make-list-ac whose guard requires size to be a nonnegative integer. Make-list is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/MAKE-ORD.html0000664002132200015000000000336412222333524016115 0ustar kaufmannacl2 MAKE-ORD.html -- ACL2 Version 6.3

    MAKE-ORD

    a constructor for ordinals.
    Major Section:  ACL2-BUILT-INS
    

    Make-ord is the ordinal constructor. Its use is recommended instead of using cons to make ordinals. For a discussion of ordinals, see ordinals.

    For any ordinal, alpha < epsilon-0, there exist natural numbers p and n, positive integers x1, x2, ..., xn and ordinals a1 > a2 > ... > an > 0 such that alpha > a1 and alpha = w^(a1)x1 + w^(a2)x2 + ... + w^(an)xn + p. We call a1 the ``first exponent'', x1 the ``first coefficient'', and the remainder (w^(a2)x2 + ... + w^(an)xn + p) the ``rest'' of alpha.

    (Make-ord fe fco rst) corresponds to the ordinal (w^fe)fco + rst. Thus the first infinite ordinal, w (omega), is constructed by

    (make-ord 1 1 0)
    
    and, for example, the ordinal (w^2)5 + w2 + 7 is constructed by:
    (make-ord 2 5 (make-ord 1 2 7)) .
    

    The reason make-ord is used rather than cons is that it allows us to reason more abstractly about the ordinals, without having to worry about the underlying representation.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MAKE-TAU-INTERVAL.html0000664002132200015000000000664312222333530017344 0ustar kaufmannacl2 MAKE-TAU-INTERVAL.html -- ACL2 Version 6.3

    MAKE-TAU-INTERVAL

    make a tau interval
    Major Section:  TAU-SYSTEM
    

    General Form:
    (make-tau-interval doc lo-rel lo hi-rel hi)
    

    An interval is a structure of the form: (dom (lo-rel . lo) . (hi-rel . hi)). Every tau contains an interval used to represent the domain, the upper, and the lower bounds of the objects recognized by the tau.

    make-tau-interval constructs well-formed intervals only if its five arguments satisfy certain restrictions given below. When these restrictions are violated make-tau-interval can construct objects that are not intervals! make-tau-interval does not attempt to coerce or adjust its arguments to make well-formed intervals.

    For examples of intervals (and non-intervals!) constructed by make-tau-interval see tau-intervalp. For examples of what objects are contained in certain intervals, see in-tau-intervalp.

    The components of an interval are as follows:

    dom (``domain'') -- must be one of four symbols: INTEGERP, RATIONALP, ACL2-NUMBERP, or NIL denoting no restriction on the domain.

    The two ``relations,'' lo-rel and hi-rel are Booleans, where t denotes less-than inequality (<) and nil represents less-than-or-equal inequality (<=). Think of t meaning ``strong'' and nil meaning ``weak'' inequality.

    Lo and hi must be either nil or explicit rational numbers. If lo is nil it denotes negative infinity; if hi is nil it denotes positive infinity. Lo must be no greater than hi. Note: Even though ACL2-NUMBERP intervals may contain complex rationals, the lo and hi bounds must be rational. This is an arbitrary decision made by the implementors to simplify coding.

    Finally, if the dom is INTEGERP, then both relations should be weak and lo and hi must be integers when they are non-nil.

    For x to be ``in'' an interval it must be of the type described by the domain predicate dom, lo must be smaller than x in the strong or weak sense denoted by lo-rel, and x must be smaller than hi in the strong or weak sense denoted by hi-rel.

    The components of an interval may be accessed with the functions tau-interval-dom, tau-interval-lo-rel, tau-interval-lo, tau-interval-hi-rel, and tau-interval-hi.




    acl2-sources/doc/HTML/MAKE-WORMHOLE-STATUS.html0000664002132200015000000000153512222333521017741 0ustar kaufmannacl2 MAKE-WORMHOLE-STATUS.html -- ACL2 Version 6.3

    MAKE-WORMHOLE-STATUS

    creates a wormhole status object from given status, entry code, and data
    Major Section:  MISCELLANEOUS
    

    General Form:  (make-wormhole-status whs code data)
    
    See wormhole. Whs should be a well-formed wormhole status, code should be :ENTER or :SKIP, and data is arbitrary. This function returns a new status with the specified entry code and data, reusing whs if it is appropriate.




    acl2-sources/doc/HTML/MANAGING-ACL2-PACKAGES.html0000664002132200015000000000123412222333516017765 0ustar kaufmannacl2 MANAGING-ACL2-PACKAGES.html -- ACL2 Version 6.3

    MANAGING-ACL2-PACKAGES

    user-contributed documentation on packages
    Major Section:  DEFPKG
    

    Jared Davis has contributed documentation on managing ACL2 packages. See http://www.cs.utexas.edu/users/moore/acl2/contrib/managing-acl2-packages.html.




    acl2-sources/doc/HTML/MARKUP.html0000664002132200015000000003105212222333516015751 0ustar kaufmannacl2 MARKUP.html -- ACL2 Version 6.3

    MARKUP

    the markup language for ACL2 documentation strings
    Major Section:  DOCUMENTATION
    

    ACL2 documentation strings make special use of the tilde character (~). In particular, we describe here a ``markup language'' for which the tilde character plays a special role. The markup language is valuable if you want to write documentation that is to be displayed outside your ACL2 session. If you are not writing such documentation, and if also you do not use the character `~', then there is no need to read on.

    Three uses of the tilde character (~) in documentation strings are as follows. Below we explain the uses that constitute the ACL2 markup language.

    ~/
    Indicates the end of a documentation section; see doc-string.

    ~~
    Indicates the literal insertion of a tilde character (~).

    ~]
    This directive in a documentation string is effective only during the processing of part 2, the details (see doc-string), and controls how much is shown on each round of :more processing when printing to the terminal. If the system is not doing :more processing, then it acts as though the ~] is not present. Otherwise, the system put out a newline and halts documentation printing on the present topic, which can be resumed if the user types :more at the terminal.

    The other uses of the tilde character are of the following form.

      ~key[arg]
    
    Before launching into an explanation of how this works in detail, let us consider some small examples.

    Here is a word that is code:

      ~c[function-name].
    
    Here is a phrase with an ``emphasized'' word, ``not'':
      Do ~em[not] do that.
    
    Here is the same phrase, but where ``not'' receives stronger emphasis (presumably boldface in a printed version):
      Do ~st[not] do that.
    
    Here is a passage that is set off as a display, in a fixed-width font:
      ~bv[]
      This passage has been set off as ``verbatim''.
      The present line starts just after a line break.  Normally, printed
      text is formatted, but inside ~bv[]...~ev[], line breaks are taken
      literally.
      ~ev[]
    
    In general, the idea is to provide a ``markup language'' that can be reasonably interpreted not only at the terminal (via :doc), but also via translators into other languages. In fact, translators have been written into Texinfo and HTML.

    Let us turn to a more systematic consideration of how to mark text in documentation strings using expressions of the form ~key[arg], which we will call ``doc-string tilde directives.'' The idea is that key informs the documentation printer (which could be the terminal, a hardcopy printer, or some hypertext tool) about the ``style'' used to display arg. The intention is that each such printer should do the best it can. For example, we have seen above that ~em[arg] tells the printer to emphasize arg if possible, using an appropriate display to indicate emphasis (italics, or perhaps surrounding arg with some character like _, or ...). For another example, the directive for bold font, ~b[arg], says that printed text for arg should be in bold if possible, but if there is no bold font available (such as at the terminal), then the argument should be printed in some other reasonable manner (for example, as ordinary text). The key part is case-insensitive; for example, you can use ~BV[] or ~Bv[] or ~bV[] in place of ~bv[].

    Every form below may have any string as the argument (inside [..]), as long as it does not contain a newline (more on that below). However, when an argument does not make much sense to us, we show it below as the empty string, e.g., ``~bv[]'' rather than ``~bv[arg]''.

    ~-[]      Print the equivalent of a dash
    
    ~b[arg]   Print the argument in bold font, if available
    
    ~bid[arg]   ``Begin implementation dependent'' -- Ignores argument at
              terminal.
    
    ~bf[]     Begin formatted text (respecting spaces and line breaks),
              but in ordinary font (rather than, say, fixed-width font)
              if possible
    
    ~bq[]     Begin quotation (indented text, if possible)
    
    ~bv[]     Begin verbatim (print in fixed-width font, respecting
              spaces and line breaks)
    
    ~c[arg]   Print arg as ``code'', such as in a fixed-width font
    
    ~ef[]     End format; balances ~bf[]
    
    ~eid[arg]   ``End implementation dependent'' -- Ignores argument at
              terminal.
    
    ~em[arg]  Emphasize arg, perhaps using italics
    
    ~eq[]     End quotation; balances ~bq[]
    
    ~ev[]     End verbatim; balances ~bv[]
    
    ~i[arg]   Print arg in italics font
    
    ~id[arg]   ``Implementation dependent'' -- Ignores argument at
              terminal.
    
    ~il[arg]  Print argument as is, but make it a link to another doc
              topic (for true hypertext environments).  Note that the
              link argument must match the package name for the desired
              topic unless the package name is ``ACL2''.  That is, to
              link to documentation on ``foo::topic'', one needs to use
              the full name for ``foo::topic'', including its package
              name.
    
    ~ilc[arg] Same as ~il[arg], except that arg should be printed as
              with ~c[arg]
    
    ~l[arg]   Ordinary link to another doc topic; prints as ``See :DOC
              arg'' at the terminal (but also see ~pl below, which puts
              ``see'' in lower case)
    
    ~nl[]     Print a newline
    
    ~par[]    Paragraph mark, of no significance at the terminal
              (can be safely ignored; see also notes below)
    
    ~pl[arg]  Parenthetical link (borrowing from Texinfo):  same as
              ~l[arg], except that ``see'' is in lower case.  This is
              typically used at other than the beginning of a sentence.
    
    ~sc[arg]  Print arg in (small, if possible) capital letters
    
    ~st[arg]  Strongly emphasize arg, perhaps using a bold font
    
    ~t[arg]   Typewriter font; similar to ~c[arg], but leaves less
              doubt about the font that will be used.
    
    ~terminal[arg]  Terminal only; arg is to be ignored except when
              reading documentation at the terminal, using :DOC.
    
    ~url[arg]  Print arg as HTML hyperlink if possible (else print like ~c)
    

    Style notes and further details

    It is not a good idea to put doc-string tilde directives inside verbatim environments, ~bv[] ... ~ev[].

    Do not nest doc-string tilde directives; that is, do not write

      The ~c[~il[append] function ...
    
    but note that the ``equivalent'' expression
      The ~ilc[append] function ...
    
    is fine. The following phrase is also acceptable:
      ~bf[]This is
      ~em[formatted] text.
      ~ef[]
    
    because the nesting is only conceptual, not literal.

    We recommend that for displayed text, ~bv[] and ~ev[] should usually each be on lines by themselves. That way, printed text may be less encumbered with excessive blank lines. Here is an example.

      Here is some normal text.  Now start a display:
      ~bv[]
        2 + 2 = 4
      ~ev[]
      And here is the end of that paragraph.
    
      Here is the start of the next paragraph.
    
    The analogous consideration applies to ~bf[] and ~ef[] as well as ~bq[] and ~eq[].

    You may ``quote'' characters inside the arg part of ~key[arg], by preceding them with ~. This is, in fact, the only legal way to use a newline character or a right bracket (]) inside the argument to a doc-string tilde directive.

    Write your documentation strings without hyphens. Otherwise, you may find your text printed on paper (via TeX, for example) like this --

      Here is a hyphe- nated word.
    
    even if what you had in mind was:
      Here is a hyphe-
      nated word.
    
    When you want to use a dash (as opposed to a hyphen), consider using ~-[], which is intended to be interpreted as a ``dash.'' For example:
      This sentence ~-[] which is broken with dashes ~-[] is boring.
    
    would be written to the terminal (using :doc) by replacing ~-[] with two hyphen characters, but would presumably be printed on paper with a dash.

    Be careful to balance the ``begin'' and ``end'' pairs, such as ~bv[] and ~ev[]. Also, do not use two ``begin'' directives (~bf[], ~bq[], or ~bv[]) without an intervening ``end'' directive. It is permissible (and perhaps this is not surprising) to use the doc-string part separator ~/ while between such a begin-end pair.

    Because of a bug in texinfo (as of this writing), you may wish to avoid beginning a line with (any number of spaces followed by) the - character or ~-[].

    The ``paragraph'' directive, ~par[], is rarely if ever used. There is a low-level capability, not presently documented, that interprets two successive newlines as though they were ~par[]. This is useful for the HTML driver. For further details, see the authors of ACL2.

    Emacs code is available for manipulating documentation strings that contain doc-string tilde-directives (for example, for doing a reasonable job filling such documentation strings). See the authors if you are interested.

    We tend to use ~em[arg] for ``section headers,'' such as ``Style notes and further details'' above. We tend to use ~st[arg] for emphasis of words inside text. This division seems to work well for our Texinfo driver. Note that ~st[arg] causes arg to be printed in upper-case at the terminal (using :doc), while ~em[arg] causes arg to be printed at the terminal as though arg were not marked for emphasis.

    Our Texinfo and HTML drivers both take advantage of capabilities for indicating which characters need to be ``escaped,'' and how. Unless you intend to write your own driver, you probably do not need to know more about this issue; otherwise, contact the ACL2 authors. We should probably mention, however, that Texinfo makes the following requirement: when using ~l[arg], where arg contains one of the special characters @, {, or }, you must immediately follow this use with a period or comma. Also, the Emacs ``info'' documentation that we generate by using our Texinfo driver has the property that in node names, : has been replaced by | (because of quirks in info); so for example, the ``proof-checker'' simplification command, s, is documented under acl2-pc||s rather than under acl2-pc::s.

    We have tried to keep this markup language fairly simple; in particular, there is no way to refer to a link by other than the actual name. So for example, when we want to make :doc an invisible link in ``code'' font, we write the following form, which indicates that : should be in that font and then doc should both be in that font and be an invisible link.

      ~c[:]~ilc[doc]
    




    acl2-sources/doc/HTML/MAX.html0000664002132200015000000000144412222333524015400 0ustar kaufmannacl2 MAX.html -- ACL2 Version 6.3

    MAX

    the larger of two numbers
    Major Section:  ACL2-BUILT-INS
    

    (Max x y) is the larger of the numbers x and y.

    The guard for max requires its arguments to be rational (real, in ACL2(r)) numbers.

    Max is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MAXIMUM-LENGTH.html0000664002132200015000000000243012222333525017044 0ustar kaufmannacl2 MAXIMUM-LENGTH.html -- ACL2 Version 6.3

    MAXIMUM-LENGTH

    return the :maximum-length from the header of an array
    Major Section:  ARRAYS
    

    Example Form:
    (maximum-length 'delta1 a)
    
    General Form:
    (maximum-length name alist)
    
    where name is an arbitrary object and alist is a 1- or 2-dimensional array. This function returns the contents of the :maximum-length field of the header of alist. Whenever an aset1 or aset2 would cause the length of the alist to exceed its maximum length, a compress1 or compress2 is done automatically to remove irrelevant pairs from the array. Maximum-length operates in virtually constant time if alist is the semantic value of name. See arrays.




    acl2-sources/doc/HTML/MBE.html0000664002132200015000000001603112222333524015354 0ustar kaufmannacl2 MBE.html -- ACL2 Version 6.3

    MBE

    attach code for execution
    Major Section:  ACL2-BUILT-INS
    

    The macro mbe (``must be equal'') can be used in function definitions in order to cause evaluation to use alternate code to that provided for the logic. An example is given below. However, the use of mbe can lead to non-terminating computations. See defexec, perhaps after reading the present documentation, for a way to prove termination.

    In the ACL2 logic, (mbe :exec exec-code :logic logic-code) equals logic-code; the value of exec-code is ignored. However, in raw Lisp it is the other way around: this form macroexpands simply to exec-code. ACL2's guard verification mechanism ensures that the raw Lisp code is only evaluated when appropriate, since the guard proof obligations generated for (the macroexpansion of) this call of mbe include not only the guard proof obligations from exec-code, but also, under suitable contextual assumptions, the term (equal exec-code logic-code). See verify-guards (in particular, for discussion of the contextual assumptions from the :guard and IF-tests) and, for general discussion of guards, see guard.

    Normally, during evaluation of an mbe call, only the :logic code is evaluated unless the call is in the body of a guard-verified function, in which case only the :exec code is evaluated. This implies that equality of :exec and :logic code is never checked at runtime. (Rather, such equality is proved when verifying guards.) We started with ``normally'' above because there is an exception: during a ``safe mode'', which is used in macroexpansion and evaluation of defconst forms, the :logic and :exec code are both evaluated and their equality is checked.

    Note that the :exec and the :logic code in an mbe call must have the same return type. For example, one cannot return (mv * *) while the other returns just a single value.

    Also see mbt, which stands for ``must be true.'' You may find it more natural to use mbt for certain applications, as described in its documentation.

    Some Related Topics

    • DEFEXEC -- attach a terminating executable function to a definition

    Here is an example of the use of mbe. Suppose that you want to define factorial in the usual recursive manner, as follows.

    (defun fact (n)
      (if (zp n)
          1
        (* n (fact (1- n)))))
    
    But perhaps you want to be able to execute calls of fact on large arguments that cause stack overflows, perhaps during proofs. (This isn't a particularly realistic example, but it should serve.) So, instead you can define this tail-recursive version of factorial:
    (defun fact1 (n acc)
      (declare (xargs :guard (and (integerp n) (>= n 0) (integerp acc))))
      (if (zp n)
          acc
        (fact1 (1- n) (* n acc))))
    
    We are now ready to define fact using mbe. Our intention is that logically, fact is as shown in the first definition above, but that fact should be executed by calling fact1. Notice that we defer guard verification, since we are not ready to prove the correspondence between fact1 and fact.
    (defun fact (n)
      (declare (xargs :guard (and (integerp n) (>= n 0))
                      :verify-guards nil))
      (mbe :exec  (fact1 n 1)
           :logic (if (zp n)
                      1
                    (* n (fact (1- n))))))
    
    Next, we prove the necessary correspondence lemmas. Notice the inclusion of a community book to help with the arithmetic reasoning.
    (include-book "books/arithmetic/top-with-meta")
    
    (defthm fact1-fact
      (implies (integerp acc)
               (equal (fact1 n acc)
                      (* acc (fact n)))))
    
    We may now do guard verification for fact, which will allow the execution of the raw Lisp fact function, where the above mbe call expands simply to (fact1 n 1).
    (verify-guards fact)
    
    Now that guards have been verified, a trace of function calls illustrates that the evaluation of calls of fact is passed to evaluation of calls of fact1. The outermost call below is of the logical function stored for the definition of fact; all the others are of actual raw Common Lisp functions.
    ACL2 !>(trace$ fact fact1)
    NIL
    ACL2 !>(fact 3)
    1> (ACL2_*1*_ACL2::FACT 3)
      2> (FACT 3)
        3> (FACT1 3 1)
          4> (FACT1 2 3)
            5> (FACT1 1 6)
              6> (FACT1 0 6)
              <6 (FACT1 6)
            <5 (FACT1 6)
          <4 (FACT1 6)
        <3 (FACT1 6)
      <2 (FACT 6)
    <1 (ACL2_*1*_ACL2::FACT 6)
    6
    ACL2 !>
    

    You may occasionally get warnings when you compile functions defined using mbe. (For commands that invoke the compiler, see compilation.) These can be inhibited by using an ignorable declare form. Here is a simple but illustrative example. Note that the declarations can optionally be separated into two declare forms.

    (defun foo (x y)
      (declare (ignorable x)
               (xargs :guard (equal x y)))
      (mbe :logic x :exec y))
    

    Finally, we observe that when the body of a function contains a term of the form (mbe :exec exec-code :logic logic-code), the user is very unlikely to see any logical difference than if this were replaced by logic-code. ACL2 takes various steps to ensure this. For example, the proof obligations generated for admitting a function treat the above mbe term simply as logic-code. Function expansion, :use hints, :definition rules, generation of constraints for functional instantiation, and creation of rules of class :rewrite and :forward-chaining also treat mbe calls as their :logic code.




    acl2-sources/doc/HTML/MBE1.html0000664002132200015000000000107512222333524015437 0ustar kaufmannacl2 MBE1.html -- ACL2 Version 6.3

    MBE1

    attach code for execution
    Major Section:  ACL2-BUILT-INS
    

    The form (mbe1 exec logic) is equivalent to the forms (mbe :logic logic :exec exec) and (must-be-equal logic exec). See mbe.




    acl2-sources/doc/HTML/MBT.html0000664002132200015000000001217312222333524015376 0ustar kaufmannacl2 MBT.html -- ACL2 Version 6.3

    MBT

    introduce a test not to be evaluated
    Major Section:  ACL2-BUILT-INS
    

    The macro mbt (``must be true'') can be used in order to add code in order to admit function definitions in :logic mode, without paying a cost in execution efficiency. Examples below illustrate its intended use.

    Semantically, (mbt x) equals x. However, in raw Lisp (mbt x) ignores x entirely, and macroexpands to t. ACL2's guard verification mechanism ensures that the raw Lisp code is only evaluated when appropriate, since a guard proof obligation (equal x t) is generated. See verify-guards and, for general discussion of guards, see guard.

    Also see mbe, which stands for ``must be equal.'' Although mbt is more natural in many cases, mbe has more general applicability. In fact, (mbt x) is essentially defined to be (mbe :logic x :exec t).

    We can illustrate the use of mbt on the following generic example, where <g>, <test>, <rec-x>, and <base> are intended to be terms involving only the variable x.

    (defun foo (x)
      (declare (xargs :guard <g>))
      (if <test>
          (foo <rec-x>)
        <base>))
    
    In order to admit this function, ACL2 needs to discharge the proof obligation that <rec-x> is smaller than x, namely:
    (implies <test>
             (o< (acl2-count <rec-x>)
                  (acl2-count x)))
    
    But suppose we need to know that <g> is true in order to prove this. Since <g> is only the guard, it is not part of the logical definition of foo. A solution is to add the guard to the definition of foo, as follows.
    (defun foo (x)
      (declare (xargs :guard <g>))
      (if (mbt <g>)
          (if <test>
              (foo <rec-x>)
            <base>)
        nil))
    
    If we do this using <g> rather than (mbt <g>), then evaluation of every recursive call of foo will cause the evaluation of (the appropriate instance of) <g>. But since (mbt <g>) expands to t in raw Lisp, then once we verify the guards of foo, the evaluations of <g> will be avoided (except at the top level, when we check the guard before allowing evaluation to take place in Common Lisp).

    Other times, the guard isn't the issue, but rather, the problem is that a recursive call has an argument that itself is a recursive call. For example, suppose that <rec-x> is of the form (foo <expr>). There is no way we can hope to discharge the termination proof obligation shown above. A standard solution is to add some version of this test:

    (mbt (o< (acl2-count <rec-x>) (acl2-count x)))
    
    Here is a specific example based on one sent by Vernon Austel.
    (defun recurX2 (n)
      (declare (xargs :guard (and (integerp n) (<= 0 n))
                      :verify-guards nil))
      (cond ((zp n) 0)
            (t (let ((call (recurX2 (1- n))))
                 (if (mbt (< (acl2-count call) n))
                     (recurX2 call)
                   1 ;; this branch is never actually taken
                   )))))
    
    (defthm recurX2-0
     (equal (recurX2 n) 0))
    
    (verify-guards recurX2)
    
    If you (trace$ acl2-count), you will see that evaluation of (recurX2 2) causes several calls of acl2-count before the verify-guards. But this evaluation does not call acl2-count after the verify-guards, because the ACL2 evaluation mechanism uses raw Lisp to do the evaluation, and the form (mbt (< (acl2-count call) n)) macroexpands to t in Common Lisp.

    You may occasionally get warnings when you compile functions defined using mbt. (For commands that invoke the compiler, see compilation.) These can be inhibited by using an ignorable declare form. Here is a simple but illustrative example. Note that the declarations can optionally be separated into two declare forms.

    (defun foo (x y)
      (declare (ignorable x)
               (xargs :guard (equal x t)))
      (and (mbt x) y))
    




    acl2-sources/doc/HTML/MEASURE.html0000664002132200015000000000066412222333521016054 0ustar kaufmannacl2 MEASURE.html -- ACL2 Version 6.3

    MEASURE

    xargs keyword :MEASURE
    Major Section:  MISCELLANEOUS
    

    See xargs.




    acl2-sources/doc/HTML/MEMBER-EQ.html0000664002132200015000000000062712222333524016227 0ustar kaufmannacl2 MEMBER-EQ.html -- ACL2 Version 6.3

    MEMBER-EQ

    See member.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/MEMBER-EQUAL.html0000664002132200015000000000063512222333524016570 0ustar kaufmannacl2 MEMBER-EQUAL.html -- ACL2 Version 6.3

    MEMBER-EQUAL

    See member.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/MEMBER.html0000664002132200015000000000436512222333524015727 0ustar kaufmannacl2 MEMBER.html -- ACL2 Version 6.3

    MEMBER

    membership predicate
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (member x lst)
    (member x lst :test 'eql)   ; same as above (eql as equality test)
    (member x lst :test 'eq)    ; same, but eq is equality test
    (member x lst :test 'equal) ; same, but equal is equality test
    

    (Member x lst) equals the longest tail of the list lst that begins with x, or else nil if no such tail exists. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst.

    The guard for a call of member depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between member and its variants:

    (member-eq x lst) is equivalent to (member x lst :test 'eq);

    (member-equal x lst) is equivalent to (member x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function member-equal.

    Member is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/MEMOIZE-SUMMARY.html0000664002132200015000000000167312222333520017213 0ustar kaufmannacl2 MEMOIZE-SUMMARY.html -- ACL2 Version 6.3

    MEMOIZE-SUMMARY

    display all collected profiling and memoization table info
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, this function just returns nil, but it displays profiling and memoization table information. The profiling statistics may be cleared with (clear-memoize-statistics).




    acl2-sources/doc/HTML/MEMOIZE.html0000664002132200015000000003412712222333517016066 0ustar kaufmannacl2 MEMOIZE.html -- ACL2 Version 6.3

    MEMOIZE

    turn on memoization for a specified function
    Major Section:  EVENTS
    

    This documentation topic relates to an experimental extension of ACL2 under development by Bob Boyer and Warren Hunt. See hons-and-memoization for a general discussion of memoization and the related features of hash consing and applicative hash tables.

    Examples:
    (memoize 'foo)                      ; remember the values of calls
                                        ;   of foo
    (memoize 'foo :condition t)         ; same as above
    (memoize 'foo :condition '(test x)) ; memoize for args satisfying
                                        ;   the given condition
    (memoize 'foo :condition-fn 'test)  ; memoize for args satisfying
                                        ;   a call of the given function
    (memoize 'foo :recursive nil)       ; don't memoize recursive calls
    (memoize 'foo :aokp t)              ; attachments OK for stored results
    (memoize 'foo :ideal-okp t)         ; memoize even if foo is in :logic mode
                                        ;   but has not been guard-verified
    
    
    

    Some Related Topics

    General Form: (memoize fn ; memoizes fn and returns fn :condition condition ; optional (default t) :condition-fn condition-fn ; optional :hints hints ; optional, for verifying the ; guards of condition-fn :otf-flg otf-flg ; optional, for verifying the ; guards of condition-fn :recursive t/nil ; optional (default t) :commutative t/lemma-name ; optional (default nil) :forget t/nil ; optional (default nil) :memo-table-init-size size ; optional (default *mht-default-size*) :aokp t/nil ; optional (default nil) :ideal-okp t/:warn/nil ; optional (default nil) :verbose t/nil ; optional (default t) )
    where fn evaluates to a user-defined function symbol; condition is either t (the default), 't, nil, or 'nil, or else evaluates to an expression whose free variables are among the formal parameters of fn; and condition-fn is either nil (the default) or else evaluates to a legal function symbol. Further restrictions and options are discussed below. Note that all arguments are evaluated (but for the special handling of value t for :commutative, the argument must literally be t; see below).

    Generally fn must evaluate to a defined function symbol. However, this value can be the name of a macro that is associated with such a function symbol; see macro-aliases-table. That associated function symbol is the one called ``memoized'' in the discussion below, but we make no more mention of this subtlety.

    In the most common case, memoize takes a single argument, which evaluates to a function symbol. We call this function symbol the ``memoized function'' because ``memos'' are saved and re-used, in the following sense. When a call of the memoized function is evaluated, the result is ``memoized'' by associating the call's arguments with that result, in a suitable table. But first an attempt is made to avoid such evaluation, by doing a lookup in that table on the given arguments for the result, as stored for a previous call on those arguments. If such a result is found, then it is returned without further computation. This paragraph also applies if :condition is supplied but is t or 't.

    If keyword argument :condition-fn is supplied, but :condition is not, then the result of evaluating :condition-fn must be a defined function symbol whose formal parameter list and guard are the same as for the function being memoized. If fn is in :logic mode, then guards must have been verified for :condition-fn. Such a ``condition function'' will be run whenever the memoized function is called, on the same parameters, and the lookup or table store described above are only performed if the result from the condition function call is non-nil.

    Suppose however that :condition is supplied. If the value supplied is t or 't, then the lookup and table store described above are always done. If the value is nil or 'nil, then this lookup and table store are never done, although statistics may be gathered; see profile. Now consider other values for :condition. An attempt will be made to define a condition function whose guard and formal parameters list are the same as those of the memoized function, and whose body is the result, r, of evaluating the given condition. The name of that condition function is the result of evaluating :condition-fn if supplied, else is the result of concatenating the string "-MEMOIZE-CONDITION" to the end of the name of the memoized function. The condition function will be defined with guard verification turned off, but that definition will be followed immediately by a verify-guards event; and this is where the optional :hints and :otf-flg are attached. At evaluation time the condition function is used as described in the preceding paragraph; so in effect, the condition (r, above) is evaluated, with its variables bound to the corresponding actuals of the memoized function call, and the memoized function attempts a lookup or table store if and only if the result of that evaluation is non-nil.

    Note that fn can be either a :logic mode function or a :program mode function. However, only the corresponding raw Lisp function is actually memoized, so guard violations can defeat memoization, and :logic mode functions without their guards verified will only be memoized when called by :program mode functions. (See guards-and-evaluation for more information about guards and evaluation in ACL2.) If fn is a :logic mode function and :condition is supplied and not t or nil, then the condition must be a guard-verified function.

    Calls of this macro generate events of the form (table memoize-table fn ((:condition-fn fn) ...)). When successful, the returned value is of the form (mv nil function-symbol state).

    Suppose that a function is already memoized. Then it is illegal to memoize that function. Moreover, if the function was memoized with an associated condition (i.e., was memoized with keyword :condition or :condition-fn having value other than t or nil), then it is also illegal to convert the function from :program to :logic mode (see verify-termination). To turn off memoization, see unmemoize.

    Memoize is illegal for a function if its arguments include state or if it returns any stobjs. Also, memoize never allows attachments to be used (see defattach); if an attachment is used during evaluation, then the evaluation result will not be stored.

    We conclude with by documenting keyword parameters not discussed above.

    Keyword parameter :recursive is t by default, which means that recursive calls of fn will be memoized just as ``top-level'' calls of fn. When :recursive is instead set to nil, memoization is only done at the top level. Using :recursive nil is similar to writing a wrapper function that just calls fn, and memoizing the wrapper instead of fn.

    If :trace has a non-nil value, then memoize also traces in a traditional Lisp style. If :trace has value notinline or notinline, then a corresponding declaration is added at the beginning of the new definition of fn.

    A non-nil value for :commutative can be supplied if fn is a binary function in :logic mode. If the memoize event is successful, then subsequently: whenever each argument to fn is either a rational number or a hons, then when the evaluation of fn on those arguments is memoized, the evaluation of fn on the swap of those arguments is, in essence, also memoized. If :commutative is supplied and is not nil or t, then it should be the name of a previously-proved theorem whose formula states the commutativity of fn, i.e., is the formula (equal (fn x y) (fn y x)) for a pair {x,y} of distinct variables. If :commutative is t -- but not merely an expression that evaluates to t -- then an attempt to prove such a lemma will be made on-the-fly. The name of the lemma is the symbol in the same package as fn, obtained by adding the suffix "-COMMUTATIVE" to the symbol-name of fn. If the proof attempt fails, then you may want first to prove the lemma yourself with appropriate hints and perhaps supporting lemmas, and then supply the name of that lemma as the value of :commutative.

    If :commutative is supplied, and a non-commutative condition is provided by :condition or :condition-fn, then although the results will be correct, the extra memoization afforded by :commutative is unspecified.

    If :memo-table-init-size is supplied, then it should be a positive integer specifying the initial size of an associated hash table.

    Argument :aokp is relevant only when attachments are used; see defattach for background on attachments. When :aokp is nil, the default, computed values are not stored when an attachment was used, or even when an attachment may have been used because a function was called that had been memoized using :aokp t. Otherwise, computed values are always stored, but saved values are not used except when attachments are allowed. To summarize:

      aokp=nil (default): ``Pure'', i.e., values do not depend on attachments
      - Fetch: always legal
      - Store: only store resulting value when attachments were not used
    
      aokp=t: ``Impure'', i.e., values may depend on attachments
      - Fetch: only legal when attachments are allowed (e.g., not during proofs)
      - Store: always legal
    

    If :ideal-okp is supplied and not nil, then it is permitted to memoize an ``ideal-mode'' function: one in :logic mode whose guards have not been verified. In general, it is ill-advised to memoize an ideal-mode function, because its calls are typically evaluated ``in the logic'' without calling a memoized ``raw Lisp'' version of the function. However, if the function is called by a :program mode function, evaluation can transfer to raw Lisp before reaching the call of the memoized function, in which case memoization will take place. For such situations you can provide value :warn or t for keyword parameter :ideal-okp. Both of these values allow memoization of ideal-mode functions, but if :warn is supplied then a warning will take place. Note that you may set the key :memoize-ideal-okp of the acl2-defaults-table to value t or :warn to change the default, but if parameter :ideal-okp is supplied, the acl2-defaults-table value is ignored.

    If :verbose is supplied, it should either be nil, which will inhibit proof, event, and summary output (see with-output), or else t (the default), which does not inhibit output. If the output baffles you, try

    :trans1 (memoize ...)
    
    to see the single-step macroexpansion of your memoize call.

    The default for :forget is nil. If :forget is supplied, and not nil, then it must be t, which causes all memoization done for a top-level call of fn to be forgotten when that top-level call exits.




    acl2-sources/doc/HTML/MEMSUM.html0000664002132200015000000000144212222333520015750 0ustar kaufmannacl2 MEMSUM.html -- ACL2 Version 6.3

    MEMSUM

    display all collected profiling and memoization info
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    This macro is an abbreviation for memoize-summary. Logically, it just returns nil.




    acl2-sources/doc/HTML/META-EXTRACT-CONTEXTUAL-FACT.html0000664002132200015000000000070712222333521021006 0ustar kaufmannacl2 META-EXTRACT-CONTEXTUAL-FACT.html -- ACL2 Version 6.3

    META-EXTRACT-CONTEXTUAL-FACT

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/META-EXTRACT-FORMULA.html0000664002132200015000000000066712222333521017717 0ustar kaufmannacl2 META-EXTRACT-FORMULA.html -- ACL2 Version 6.3

    META-EXTRACT-FORMULA

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/META-EXTRACT-GLOBAL-FACT+.html0000664002132200015000000000070112222333521020325 0ustar kaufmannacl2 META-EXTRACT-GLOBAL-FACT+.html -- ACL2 Version 6.3

    META-EXTRACT-GLOBAL-FACT+

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/META-EXTRACT-GLOBAL-FACT.html0000664002132200015000000000067712222333521020266 0ustar kaufmannacl2 META-EXTRACT-GLOBAL-FACT.html -- ACL2 Version 6.3

    META-EXTRACT-GLOBAL-FACT

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/META-EXTRACT-RW+-TERM.html0000664002132200015000000000067112222333521017755 0ustar kaufmannacl2 META-EXTRACT-RW+-TERM.html -- ACL2 Version 6.3

    META-EXTRACT-RW+-TERM

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/META-EXTRACT.html0000664002132200015000000004247712222333521016621 0ustar kaufmannacl2 META-EXTRACT.html -- ACL2 Version 6.3

    META-EXTRACT

    meta reasoning using valid terms extracted from context or world
    Major Section:  MISCELLANEOUS
    

    For this advanced topic, we assume familiarity with metatheorems and metafunctions (see meta), as well as extended metafunctions (see extended-metafunctions). The capability described here -- so-called ``meta-extract hypotheses'' for a :meta or a :clause-processor rule -- provides an advanced form of meta-level reasoning that was initially designed largely by Sol Swords, who also provided a preliminary implementation.

    A meta rule or clause-processor rule may have so-called ``meta-extract'' hypotheses that take forms displayed below. Here evl is the evaluator, obj is an arbitrary term, mfc is the metafunction context (which is a variable other than the symbol STATE that represents the metafunction context; see extended-metafunctions), state is literally the symbol STATE, a is the second argument of evl in both arguments of the conclusion of the rule, and aa is an arbitrary term.

    (evl (meta-extract-contextual-fact obj mfc state) a)
    (evl (meta-extract-global-fact obj state) aa)) ; equivalent to the next form
    (evl (meta-extract-global-fact+ obj state state) aa)
    (evl (meta-extract-global-fact+ obj st state) aa)
    
    The first form is only legal for :meta rules for which the metafunction is an extended metafunction. The remaining forms are legal for both :meta rules and :clause-processor rules.

    Sol Swords has contributed a community book, clause-processors/meta-extract-user.lisp, that uses a Skolemization trick to allow one to use at most one meta-extract-global-fact+ hypothesis and at most one meta-extract-contextual-fact hypothesis.

    These additional hypotheses may be necessary in order to prove a proposed metatheorem or (for the second type of hypothesis above) clause-processor rule, in particular when the correctness of the metafunction depends on the correctness of utilities extracting formulas from the logical world or (for the first type) facts from the metafunction context (mfc). After the rule is proved, however, the meta-extract hypotheses have no effect on how the rule is applied during a proof. An argument for correctness of using meta-extract hypotheses is given in the ACL2 source code within a comment entitled ``Essay on Correctness of Meta Reasoning''. In the documentation below, we focus primarily on :meta rules, since the use of meta-extract-global-fact hypotheses in :clause-processor rules is entirely analogous. (At the end, though, we discuss the last of the four forms displayed above.) And for :meta rules we focus not on the application of rules but, rather, on how the use of meta-extract hypotheses allow you to prove correctness of metafunctions that use facts from the logical world or the metafunction context (mfc).

    Below we describe properties of meta-extract-contextual-fact and meta-extract-global-fact, but only after we illustrate their utility with an example. But even before we present that example, we first give a sense of how to think about these functions by showing a theorem that one can prove about the first of them. If this snippet doesn't help your intuition, then just skip over it and start with the example.

    (defevaluator evl evl-list
      ((binary-+ x y) (typespec-check x y)))
    
    (thm (implies
          (not (bad-atom (cdr (assoc-equal 'x alist))))
          (equal (evl (meta-extract-contextual-fact (list :typeset 'x)
                                                    mfc
                                                    state)
                      alist)
                 (not (equal 0 ; indicates non-empty intersection
                             (logand (type-set-quote ; type-set of a constant
                                      (cdr (assoc-equal 'x alist)))
                                     (mfc-ts-fn 'x mfc state nil)))))))
    

    The following example comes from the community book, books/clause-processors/meta-extract-simple-test.lisp (after it defines the evaluator), which presents very basic (and contrived) examples that nevertheless illustrate meta-extract hypotheses.

    (defthm plus-identity-2-meta
      (implies (and (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp)
                                                          state)
                                (list (cons 'u
                                            (nthmeta-ev (cadr (cadr term))
                                                        a))))
                    (nthmeta-ev (meta-extract-contextual-fact
                                 `(:typeset ,(caddr term)) mfc state)
                                a))
               (equal (nthmeta-ev term a)
                      (nthmeta-ev (plus-identity-2-metafn term mfc state) a)))
      :rule-classes ((:meta :trigger-fns (binary-+))))
    
    The two hypotheses illustratate the two basic kinds of meta-extract hypotheses: applications of the evaluator to a call of meta-extract-global-fact and to a call of meta-extract-contextual-fact. Here is the definition of the metafunction used in the above rule, slightly simplified here from what is found in the above book (but adequate for proving the two events that follow it in the above book).
    (defun plus-identity-2-metafn (term mfc state)
      (declare (xargs :stobjs state :verify-guards nil))
      (case-match term
        (('binary-+ ('bar &) y)
         (cond
          ((equal (meta-extract-formula 'bar-posp state)
                  '(POSP (BAR U)))
           (if (ts= (mfc-ts y mfc state :forcep nil)
                    *ts-character*)
               (cadr term)
             term))
          (t term)))
        (& term)))
    
    This metafunction returns its input term unchanged except in the case that the term is of the form (binary-+ (bar x) y) and the following two conditions are met, in which case it returns (bar x).
    (1)  (equal (meta-extract-formula 'bar-posp state)
                '(POSP (BAR U)))
    
    (2)  (ts= (mfc-ts y mfc state :forcep nil)
              *ts-character*)
    
    So suppose that term is (list 'binary-+ (list 'bar x) y). We show how the meta-extract hypotheses together with (1) and (2) imply that the conclusion of the above :meta rule holds. Here is that conclusion after a bit of simplification.
    (equal (nthmeta-ev (list 'binary-+ (list 'bar x) y) a)
           (nthmeta-ev (list 'bar x) a))
    
    This equality simplifies as follows using the evaluator properties of nthmeta-ev.
    (equal (binary-+ (bar (nthmeta-ev x a))
                     (nthmeta-ev y a))
           (bar (nthmeta-ev x a)))
    
    Since a positive number plus a character is that number, it clearly suffices to show:
    (A)  (posp (bar (nthmeta-ev x a)))
    
    (B)  (characterp (nthmeta-ev y a))
    
    It remains then to show that these follow from (1) and (2) together with the meta-extract hypotheses.

    First consider (A). We show that it is just a simplification of the first meta-extract hypothesis.

    (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp)
                                          state)
                (list (cons 'u
                            (nthmeta-ev (cadr (cadr term))
                                        a))))
    = {by our assumption that term is (list 'binary-+ (list 'bar x) y)}
    (nthmeta-ev (meta-extract-global-fact '(:formula bar-posp)
                                          state)
                (list (cons 'u
                            (nthmeta-ev x a))))
    = {by definition of meta-extract-global-fact, as discussed later}
    (nthmeta-ev (meta-extract-formula 'bar-posp state)
                (list (cons 'u
                            (nthmeta-ev x a))))
    = {by (1)}
    (nthmeta-ev '(posp (bar u))
                (list (cons 'u
                            (nthmeta-ev x a))))
    = {by evaluator properties of nthmeta-ev}
    (posp (bar (nthmeta-ev x a)))
    

    Now consider (B). We show that it is just a simplification of the second meta-extract hypothesis.

    (nthmeta-ev (meta-extract-contextual-fact
                 `(:typeset ,(caddr term)) mfc state)
                a)
    = {by our assumption that term is (list 'binary-+ (list 'bar x) y)}
    (nthmeta-ev (meta-extract-contextual-fact (list ':typeset y) mfc state)
                a)
    = {by definition of meta-extract-contextual-fact, as discussed later}
    (nthmeta-ev (list 'typespec-check
                      (list 'quote
                            (mfc-ts y mfc state :forcep nil))
                      y)
                a)
    = {by (2)}
    (nthmeta-ev (list 'typespec-check
                      (list 'quote *ts-character*)
                      y)
                a)
    = {by evaluator properties of nthmeta-ev}
    (typespec-check *ts-character* (nthmeta-ev y a))
    = {by definition of typespec-check}
    (characterp (nthmeta-ev y a))
    

    Note the use of :forcep nil above. All of the mfc-xx functions take a keyword argument :forcep. Calls of mfc-xx functions made on behalf of meta-extract-contextual-fact always use :forcep nil, so in order to reason about these calls in your own metafunctions, you will want to use :forcep nil. We have contemplated adding a utility like meta-extract-contextual-fact that allows forcing but returns a tag-tree (see ttree), and may do so if there is demand for it.

    Finally, we document what is provided logically by calls of meta-extract-global-fact and meta-extract-contextual-fact. Of course, you are invited to look at the definitions of these function in the ACL2 source code, or by using :pe. Note that both of these functions are non-executable (each of their bodies is inside a call of non-exec); their purpose is purely logical, not for execution. The functions return *t*, i.e., (quote t), in cases that they provide no information.

    First we consider the value of (meta-extract-global-fact obj state) for various values of obj. When we refer below to concepts like ``body'' and ``evaluation'', we refer to these with respect to the logical world of the input state.

    Case obj = (list :formula FN):
    The value reduces to the value of (meta-extract-formula FN state), which returns the ``formula'' of FN in the following sense. If FN is a function symbol with formals (X1 ... Xk), then the formula is the constraint on FN if FN is constrained or introduced by defchoose, and otherwise is (equal (FN X1 ... Xk) BODY), where BODY is the (unsimplified) body of the definition of FN. Otherwise, if FN is the name of a theorem, the formula is just what is stored for that theorem. Otherwise, the formula is *t*.

    Case obj = (list :lemma FN N):
    Assume N is a natural number; otherwise, treat N as 0. If FN is a function symbol with more than N associated lemmas -- ``associated'' in the sense of being either a :definition rule for FN or a :rewrite rule for FN whose left-hand side has a top function symbol of FN -- then the value is the Nth such lemma (with zero-based indexing). Otherwise the value is *t*.

    Case obj = (list :fncall FN ARGLIST):
    Assume that FN is a :logic-mode function symbol and that ARGLIST is a true list of values of the same length as list of formal parameters for FN (i.e., as the input arity of FN). Also assume that the application of FN to actual parameter list ARGLIST returns a result (mv nil x). Let (QARG1 ... QARGk) be the result of quoting each element of ARGLIST, i.e., replacing each y in ARGLIST by the two-element list (quote y). Then the value is the term (equal (FN QARG1 ... QARGk) (quote x)).

    For any other values of obj, the value is *t*.

    Finally, the value of (meta-extract-contextual-fact obj mfc state) is as follows for various values of obj. Note a difference from the semantics of meta-extract-global-fact: below, the relevant logical world is the one stored in the metafunction context, mfc, not in the input state.

    Case obj = (list :typeset TERM ...):
    The value is the value of (typespec-check ts TERM), where ts is the value of (mfc-ts TERM mfc state :forcep nil :ttreep nil), and where (typespec-check ts val) is defined to be true when val has type-set ts. (Exception: If val satisfies bad-atom then typespec-check is true when ts is negative.)

    Case obj = (list :rw+ TERM ALIST OBJ EQUIV ...):
    We assume below that EQUIV is a symbol that represents an equivalence relation, where nil represents equal, t represents iff, and otherwise EQUIV represents itself (an equivalence relation in the current logical world). For any other EQUIV the value is *t*. Now let rhs be the value of (mfc-rw+ TERM ALIST OBJ EQUIV mfc state :forcep nil :ttreep nil). Then the value is the term (list 'equv (sublis-var ALIST TERM) rhs), where equv is the equivalence relation represented by EQUIV, and sublis-var is defined to substitute a variable-binding alist into a term.

    Case obj = (list :rw TERM OBJ EQUIV ...):
    The value is the same as above but for an ALIST of nil, i.e., for the case that obj is (list :rw+ TERM nil OBJ EQUIV ...).

    Case obj = (list :ap TERM ...):
    The value is (list 'not TERM) if (mfc-ap TERM mfc state :forcep nil) is true, else is *t*.

    Case obj = (list :relieve-hyp HYP ALIST RUNE TARGET BKPTR ...):
    The value is (sublis-var alist hyp) -- see above for a discussion of sublis-var -- if the following is true.

    (mfc-relieve-hyp hyp alist rune target bkptr mfc state
                     :forcep nil :ttreep nil)
    
    Otherwise the value is *t*.

    If no case above applies, then the value is *t*.

    We conclude by considering the fourth of the four forms above (and implicitly, its special case represented by the third form above):

    (evl (meta-extract-global-fact+ obj st state) aa)
    
    The discussion above is for the function meta-extract-global-fact+, but assumes that the logical worlds of st and state are equal; otherwise the value returned is *t*. Of course, since a call of meta-extract-global-fact expands to a corresponding call of meta-extract-global-fact+ in which the last two arguments are both state, that condition holds automatically for that case. But the state mentioned in the meta-extract hypotheses of a meta rule or clause-processor rule is in essence an initial state. In the case of a clause-processor rule, the clause-processor function may modify that initial state (say, by printing or modifying some state globals) without changing its world, and then pass that modified state to fncall-term. While fncall-term may produce a different result for this modified state than for the initial state, both are valid: the state used for heuristic purposes, such as determining whether guard-checking may cause an error. A useful instance of the hypothesis displayed above will be one in which st is that modified state.




    acl2-sources/doc/HTML/META.html0000664002132200015000000005505312222333530015503 0ustar kaufmannacl2 META.html -- ACL2 Version 6.3

    META

    make a :meta rule (a hand-written simplifier)
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Meta rules extend the ACL2 simplifier with hand-written code to transform certain terms to equivalent ones. To add a meta rule, the :corollary formula must establish that the hand-written ``metafunction'' preserves the meaning of the transformed term.

    Examples:
    (defthm fn-correct-1                ; Modify the rewriter to use fn to
      (equal (evl x a)                  ; transform terms that are calls of
             (evl (fn x) a))            ; nth or of foo.
      :rule-classes ((:meta :trigger-fns (nth foo))))
    
    (defthm fn-correct-2                ; As above, but this illustrates
      (implies (and (pseudo-termp x)    ; that without loss of generality we
                    (alistp a))         ; may restrict x to be shaped like a
               (equal (evl x a)         ; term and a to be an alist.
                      (evl (fn x) a)))
      :rule-classes ((:meta :trigger-fns (nth foo))))
    
    (defthm fn-correct-3                ; As above (with or without the
      (implies (and (pseudo-termp x)    ; hypotheses on x and a), with the
                    (alistp a)          ; additional restriction that the
                    (evl (hyp-fn x) a)) ; meaning of (hyp-fn x) is true in
               (equal (evl x a)         ; the current context.  That is, the
                      (evl (fn x) a)))  ; applicability of the transformation
      :rule-classes                     ; may be dependent upon some computed
      ((:meta :trigger-fns (nth foo)))) ; hypotheses.
    

    While our intention is that the set of ACL2 documentation topics is self-contained, readers might find it useful to see the following paper for an introduction to meta reasoning in ACL2.

    W. A. Hunt, Jr., R. B. Krug, M. Kaufmann, J S. Moore and E. W. Smith, ``Meta Reasoning in ACL2.'' TPHOLs 2005, ed. J. Hurd and T. F. Melham, LNCS 3603, Springer-Verlag, Berlin, 2005, pp. 163-178.

    A non-nil list of function symbols must be supplied as the value of the :trigger-fns field in a :meta rule class object (except that a macro alias can stand in for a function symbol; see add-macro-alias).

    Some Related Topics

    General Forms:
    (implies (and (pseudo-termp x)        ; this hyp is optional
                  (alistp a)              ; this hyp is optional
                  (ev (hyp-fn x ...) a)   ; this hyp is optional
                  ; meta-extract hyps may also be included (see below)
                  )
             (equiv (ev x a)
                    (ev (fn x ...) a)))
    
    where equiv is a known equivalence relation, x and a are distinct variable names, and ev is an evaluator function (see below), and fn is a function symbol, as is hyp-fn when provided. The arguments to fn and hyp-fn should be identical. In the most common case, both take a single argument, x, which denotes the term to be simplified. If fn and/or hyp-fn are guarded, their guards should be trivially implied by pseudo-termp. We say the theorem above is a ``metatheorem'' or ``metalemma'' and fn is a ``metafunction'', and hyp-fn is a ``hypothesis metafunction''.

    If ``...'' is empty, i.e., the metafunctions take just one argument, we say they are ``vanilla flavored.'' If ``...'' is non-empty, we say the metafunctions are ``extended.'' Extended metafunctions can access state and context sensitive information to compute their results, within certain limits. We discuss vanilla metafunctions here and recommend a thorough understanding of them before proceeding (at which time see extended-metafunctions).

    Additional hypotheses are supported, called ``meta-extract hypotheses''. These allow metafunctions to depend on the validity of certain terms extracted from the context or the logical world. These hypotheses provide a relatively advanced form of metatheorem so we explain them elsewhere; see meta-extract.

    One might think that metafunctions and (if supplied) hypothesis metafunctions must be executable: that is, not constrained (i.e., introduced in the signature of encapsulate events), and not declared :non-executable. After all, there is no point in installing a simplifier that cannot be run! However, such a restriction is not enforced, because one could introduce a metafunction using encapsulate and then use defattach to attach it to an executable function; see defattach.

    We defer discussion of the case in which there is a hypothesis metafunction and for now address the case in which the other two hypotheses are present.

    In the discussion below, we refer to the argument, x, of fn and hyp-fn as a ``term.'' When these metafunctions are executed by the simplifier, they will be applied to (the quotations of) terms. But during the proof of the metatheorem itself, x may not be the quotation of a term. If the pseudo-termp hypothesis is omitted, x may be any object. Even with the pseudo-termp hypothesis, x may merely ``look like a term'' but use non-function symbols or function symbols of incorrect arity. In any case, the metatheorem is stronger than necessary to allow us to apply the metafunctions to terms, as we do in the discussion below. We return later to the question of proving the metatheorem.

    Suppose the general form of the metatheorem above is proved with the pseudo-termp and alistp hypotheses. Then when the simplifier encounters a term, (h t1 ... tn), that begins with a function symbol, h, listed in :trigger-fns, it applies the metafunction, fn, to the quotation of the term, i.e., it evaluates (fn '(h t1 ... tn)) to obtain some result, which can be written as 'val. If 'val is different from '(h t1 ... tn) and val is a term, then (h t1 ... tn) is replaced by val, which is then passed along for further rewriting. Because the metatheorem establishes the correctness of fn for all terms (even non-terms!), there is no restriction on which function symbols are listed in the :trigger-fns. Generally, of course, they should be the symbols that head up the terms simplified by the metafunction fn. See term-table for how one obtains some assistance towards guaranteeing that val is indeed a term.

    The ``evaluator'' function, ev, is a function that can evaluate a certain class of expressions, namely, all of those composed of variables, constants, and applications of a fixed, finite set of function symbols, g1, ..., gk. Generally speaking, the set of function symbols handled by ev is chosen to be exactly the function symbols recognized and manipulated by the metafunctions being introduced. For example, if fn manipulates expressions in which 'equal and 'binary-append occur as function symbols, then ev is generally specified to handle equal and binary-append. The actual requirements on ev become clear when the metatheorem is proved. The standard way to introduce an evaluator is to use the ACL2 macro defevaluator, though this is not strictly necessary. See defevaluator if you want details.

    [Aside for the logic-minded.] Why are we justified in using metafunctions this way? Suppose (fn 'term1) is 'term2. What justifies replacing term1 by term2? The first step is to assert that term1 is (ev 'term1 a), where a is an alist that maps 'var to var, for each variable var in term1. This step is incorrect, because 'term1 may contain function symbols other than the ones, g1, ..., gk, that ev knows how to handle. But we can grow ev to a ``larger'' evaluator, ev*, an evaluator for all of the symbols that occur in term1 or term2. We can prove that ev* satisfies the constraints on ev, provided no defaxiom events are adding constraints to ev (or callers of ev, and recursively); ACL2 checks this additional property. Hence, the metatheorem holds for ev* in place of ev, by functional instantiation. We can then carry out the proof of the equivalence of term1 and term2 as follows: Fix a to be an alist that maps the quotations of the variables of term1 and term2 to themselves. Then,

    term1 = (ev* 'term1 a)      ; (1) by construction of ev* and a
          = (ev* (fn 'term1) a) ; (2) by the metatheorem for ev*
          = (ev* 'term2 a)      ; (3) by evaluation of fn
          = term2               ; (4) by construction of ev* and a
    
    Note that in line (2) above, where we appeal to the (functional instantiation of the) metatheorem, we can relieve its (optional) pseudo-termp and alistp hypotheses by appealing to the facts that term1 is a term and a is an alist by construction. [End of Aside for the logic-minded.]

    There are subtleties related to the notion of ``growing'' ev to a ``larger'' evaluator, as mentioned in the paragraph just above. For corresponding restrictions on :meta rules, see evaluator-restrictions.

    Finally, we turn to the second case, in which there is a hypothesis metafunction. In that case, consider as before what happens when the simplifier encounters a term, (h t1 ... tn), where h is listed in :trigger-fns. This time, after it applies fn to '(h t1 ... tn) to obtain the quotation of some new term, 'val, it then applies the hypothesis metafunction, hyp-fn. That is, it evaluates (hyp-fn '(h t1 ... tn)) to obtain some result, which can be written as 'hyp-val. If hyp-val is not in fact a term, the metafunction is not used. Provided hyp-val is a term, the simplifier attempts to establish (by conventional backchaining) that this term is non-nil in the current context. If this attempt fails, then the meta rule is not applied. Otherwise, (h t1...tn) is replaced by val as in the previous case (where there was no hypothesis metafunction).

    Why is it justified to make this extension to the case of hypothesis metafunctions? First, note that the rule

    (implies (and (pseudo-termp x)
                  (alistp a)
                  (ev (hyp-fn x) a))
             (equal (ev x a)
                    (ev (fn x) a)))
    
    is logically equivalent to the rule
    (implies (and (pseudo-termp x)
                  (alistp a))
             (equal (ev x a)
                    (ev (new-fn x) a)))
    
    where (new-fn x) is defined to be (list 'if (hyp-fn x) (fn x) x). (If we're careful, we realize that this argument depends on making an extension of ev to an evaluator ev* that handles if and the functions manipulated by hyp-fn.) If we write 'term for the quotation of the present term, and if (hyp-fn 'term) and (fn 'term) are both terms, say hyp1 and term1, then by the previous argument we know it is sound to rewrite term to (if hyp1 term1 term). But since we have established in the current context that hyp1 is non-nil, we may simplify (if hyp1 term1 term) to term1, as desired.

    We now discuss the role of the pseudo-termp hypothesis. (Pseudo-termp x) checks that x has the shape of a term. Roughly speaking, it ensures that x is a symbol, a quoted constant, or a true list consisting of a lambda expression or symbol followed by some pseudo-terms. Among the properties of terms not checked by pseudo-termp are that variable symbols never begin with ampersand, lambda expressions are closed, and function symbols are applied to the correct number of arguments. See pseudo-termp.

    There are two possible roles for pseudo-termp in the development of a metatheorem: it may be used as the guard of the metafunction and/or hypothesis metafunction and it may be used as a hypothesis of the metatheorem. Generally speaking, the pseudo-termp hypothesis is included in a metatheorem only if it makes it easier to prove. The choice is yours. (An extreme example of this is when the metatheorem is invalid without the hypothesis!) We therefore address ourselves the question: should a metafunction have a pseudo-termp guard? A pseudo-termp guard for a metafunction, in connection with other considerations described below, improves the efficiency with which the metafunction is used by the simplifier.

    To make a metafunction maximally efficient you should (a) provide it with a pseudo-termp guard and exploit the guard when possible in coding the body of the function (see guards-and-evaluation, especially the section on efficiency issues), (b) verify the guards of the metafunction (see verify-guards), and (c) compile the metafunction (see comp). When these three steps have been taken the simplifier can evaluate (fn 'term1) by running the compiled ``primary code'' (see guards-and-evaluation) for fn directly in Common Lisp. (Note however that explicit compilation may be suppressed; see compilation.)

    Before discussing efficiency issues further, let us review for a moment the general case in which we wish to evaluate (fn 'obj) for some :logic function. We must first ask whether the guards of fn have been verified. If not, we must evaluate fn by executing its logic definition. This effectively checks the guards of every subroutine and so can be slow. If, on the other hand, the guards of fn have been verified, then we can run the primary code for fn, provided 'obj satisfies the guard of fn. So we must next evaluate the guard of fn on 'obj. If the guard is met, then we run the primary code for fn, otherwise we run the logic code.

    Now in the case of a metafunction for which the three steps above have been followed, we know the guard is (implied by) pseudo-termp and that it has been verified. Furthermore, we know without checking that the guard is met (because term1 is a term and hence 'term1 is a pseudo-termp). Hence, we can use the compiled primary code directly.

    We strongly recommend that you compile your metafunctions, as well as all their subroutines (unless explicit compilation is suppressed; see compilation). Guard verification is also recommended.

    Finally, we present a very simple example of the use of :meta rules, based on one provided by Robert Krug. This example illustrates a trick for avoiding undesired rewriting after applying a metafunction or any other form of rewriting. To elaborate: in general, the term t2 obtained by applying a metafunction to a term t1 is then handed immediately to the rewriter, which descends recursively through the arguments of function calls to rewrite t2 completely. But if t2 shares a lot of structure with t1, then it might not be worthwhile to rewrite t2 immediately. (A rewrite of t2 will occur anyhow the next time a goal is generated.) The trick involves avoiding this rewrite by wrapping t2 inside a call of hide, which in turn is inside a call of a user-defined ``unhiding'' function, unhide.

    (defun unhide (x)
      (declare (xargs :guard t))
      x)
    
    (defthm unhide-hide
      (equal (unhide (hide x))
             x)
      :hints (("Goal" :expand ((hide x)))))
    
    (in-theory (disable unhide))
    
    (defun my-plus (x y)
      (+ x y))
    
    (in-theory (disable my-plus))
    
    (defevaluator evl evl-list
      ((my-plus x y)
       (binary-+ x y)
       (unhide x)
       (hide x)))
    
    (defun meta-fn (term)
      (declare (xargs :guard (pseudo-termp term)))
      (if (and (consp term)
               (equal (length term) 3)
               (equal (car term) 'my-plus))
          `(UNHIDE (HIDE (BINARY-+ ,(cadr term) ,(caddr term))))
        term))
    
    (defthm my-meta-lemma
      (equal (evl term a)
             (evl (meta-fn term) a))
      :hints (("Goal" :in-theory (enable my-plus)))
      :rule-classes ((:meta :trigger-fns (my-plus))))
    
    

    Notice that in the following (silly) conjecture, ACL2 initially does only does the simplification directed by the metafunction; a second goal is generated before the commuativity of addition can be applied. If the above calls of UNHIDE and HIDE had been stripped off, then Goal' would have been the term printed in Goal'' below.

    ACL2 !>(thm
            (equal (my-plus b a)
                   ccc))
    
    This simplifies, using the :meta rule MY-META-LEMMA and the :rewrite
    rule UNHIDE-HIDE, to
    
    Goal'
    (EQUAL (+ B A) CCC).
    
    This simplifies, using the :rewrite rule COMMUTATIVITY-OF-+, to
    
    Goal''
    (EQUAL (+ A B) CCC).
    

    The discussion above probably suffices to make good use of this (UNHIDE (HIDE ...)) trick. However, we invite the reader who wishes to understand the trick in depth to evaluate the following form before submitting the thm form above.

    (trace$ (rewrite :entry (list (take 2 arglist))
                     :exit (list (car values)))
            (rewrite-with-lemma :entry (list (take 2 arglist))
                                :exit (take 2 values)))
    
    The following annotated subset of the trace output (which may appear a bit different depending on the underlying Common Lisp implementation) explains how the trick works.

        2> (REWRITE ((MY-PLUS B A) NIL))>
          3> (REWRITE-WITH-LEMMA
                  ((MY-PLUS B A)
                   (REWRITE-RULE (:META MY-META-LEMMA)
                                 1822
                                 NIL EQUAL META-FN NIL META NIL NIL)))>
    
    We apply the meta rule, then recursively rewrite the result, which is the
    (UNHIDE (HIDE ...)) term shown just below.
    
            4> (REWRITE ((UNHIDE (HIDE (BINARY-+ B A)))
                         ((A . A) (B . B))))>
              5> (REWRITE ((HIDE (BINARY-+ B A))
                           ((A . A) (B . B))))>
    
    The HIDE protects its argument from being touched by the rewriter.
    
              <5 (REWRITE (HIDE (BINARY-+ B A)))>
              5> (REWRITE-WITH-LEMMA
                      ((UNHIDE (HIDE (BINARY-+ B A)))
                       (REWRITE-RULE (:REWRITE UNHIDE-HIDE)
                                     1806 NIL EQUAL (UNHIDE (HIDE X))
                                     X ABBREVIATION NIL NIL)))>
    
    Now we apply UNHIDE-HIDE, then recursively rewrite its right-hand
    side in an environment where X is bound to (BINARY-+ B A).
    
                6> (REWRITE (X ((X BINARY-+ B A))))>
    
    Notice that at this point X is cached, so REWRITE just returns
    (BINARY-+ B A).
    
                <6 (REWRITE (BINARY-+ B A))>
              <5 (REWRITE-WITH-LEMMA T (BINARY-+ B A))>
            <4 (REWRITE (BINARY-+ B A))>
          <3 (REWRITE-WITH-LEMMA T (BINARY-+ B A))>
        <2 (REWRITE (BINARY-+ B A))>
    




    acl2-sources/doc/HTML/MIN.html0000664002132200015000000000144612222333524015400 0ustar kaufmannacl2 MIN.html -- ACL2 Version 6.3

    MIN

    the smaller of two numbers
    Major Section:  ACL2-BUILT-INS
    

    (Min x y) is the smaller of the numbers x and y.

    The guard for min requires its arguments to be rational (real, in ACL2(r)) numbers.

    Min is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MINIMAL-THEORY.html0000664002132200015000000000274612222333531017055 0ustar kaufmannacl2 MINIMAL-THEORY.html -- ACL2 Version 6.3

    MINIMAL-THEORY

    a minimal theory to enable
    Major Section:  THEORIES
    

    This theory (see theories) enables only a few built-in functions and executable counterparts. It can be useful when you want to formulate lemmas that rather immediately imply the theorem to be proved, by way of a :use hint (see hints), for example as follows.

    :use (lemma-1 lemma-2 lemma-3)
    :in-theory (union-theories '(f1 f2) (theory 'minimal-theory))
    
    In this example, we expect the current goal to follow from lemmas lemma-1, lemma-2, and lemma-3 together with rules f1 and f2 and some obvious facts about built-in functions (such as the definition of implies and the :executable-counterpart of car). The :in-theory hint above is intended to speed up the proof by turning off all inessential rules.




    acl2-sources/doc/HTML/MINUSP.html0000664002132200015000000000144612222333524015770 0ustar kaufmannacl2 MINUSP.html -- ACL2 Version 6.3

    MINUSP

    test whether a number is negative
    Major Section:  ACL2-BUILT-INS
    

    (Minusp x) is true if and only if x < 0.

    The guard of minusp requires its argument to be a rational (real, in ACL2(r)) number.

    Minusp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MISCELLANEOUS.html0000664002132200015000000005734112222333520016761 0ustar kaufmannacl2 MISCELLANEOUS.html -- ACL2 Version 6.3

    MISCELLANEOUS

    a miscellany of documented functions and concepts (often cited in more accessible documentation)
    Major Section:  ACL2 Documentation
    

    Some Related Topics

    Perhaps as the system matures this section will become more structured.




    acl2-sources/doc/HTML/MOD-EXPT.html0000664002132200015000000000266612222333524016157 0ustar kaufmannacl2 MOD-EXPT.html -- ACL2 Version 6.3

    MOD-EXPT

    exponential function
    Major Section:  ACL2-BUILT-INS
    

    (mod-expt r i m) is the result of raising the number r to the integer power i and then taking the residue mod m. That is, (mod-expt r i m) is equal to (mod (expt r i) m).

    The guard for (mod-expt r i m) is that r is a rational number and i is an integer; if r is 0 then i is nonnegative; and m is a non-zero rational number.

    In some implementations (GCL Version 2.7.0 as of this writing), this function is highly optimized when r and i are natural numbers, not both zero, and m is a positive integer. For other Lisp implementations, consider using function mod-expt-fast, defined in the community book arithmetic-3/floor-mod/mod-expt-fast.lisp, which should still provide significantly improved performance over this function.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MOD.html0000664002132200015000000000211012222333524015361 0ustar kaufmannacl2 MOD.html -- ACL2 Version 6.3

    MOD

    remainder using floor
    Major Section:  ACL2-BUILT-INS
    

    ACL2 !>(mod 14 3)
    2
    ACL2 !>(mod -14 3)
    1
    ACL2 !>(mod 14 -3)
    -1
    ACL2 !>(mod -14 -3)
    -2
    ACL2 !>(mod -15 -3)
    0
    ACL2 !>
    
    (Mod i j) is that number k that (* j (floor i j)) added to k equals i.

    The guard for (mod i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero.

    Mod is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MODE.html0000664002132200015000000000065312222333521015475 0ustar kaufmannacl2 MODE.html -- ACL2 Version 6.3

    MODE

    xargs keyword :MODE
    Major Section:  MISCELLANEOUS
    

    See xargs.




    acl2-sources/doc/HTML/MONITOR.html0000664002132200015000000002464012222333516016106 0ustar kaufmannacl2 MONITOR.html -- ACL2 Version 6.3

    MONITOR

    to monitor the attempted application of a rule name
    Major Section:  BREAK-REWRITE
    

    Example:
    (monitor '(:rewrite assoc-of-app) 't)
    :monitor (:rewrite assoc-of-app) t
    :monitor (:definition app) (equal (brr@ :target) '(app c d))
    
    General Form:
    (monitor rune term)
    
    where rune is a rune and term is a term, called the ``break condition.'' Rune must be either a :rewrite rune or a :definition rune.

    When a rune is monitored any attempt to apply it may result in an interactive break in an ACL2 ``wormhole state.'' There you will get a chance to see how the application proceeds. See break-rewrite for a description of the interactive loop entered. Whether an interactive break occurs depends on the value of the break condition expression associated with the monitored rune.

    NOTE: Some :rewrite rules are considered ``simple abbreviations''; see simple. These can be be monitored, but only at certain times during the proof. Monitoring is carried out by code inside the rewriter but abbreviation rules may be applied by a special purpose simplifier inside the so-called preprocess phase of a proof. If you desire to monitor an abbreviation rule, a warning will be printed suggesting that you may want to supply the hint :DO-NOT '(PREPROCESS); see hints. Without such a hint, an abbreviation rule can be applied during the preprocess phase of a proof, and no such application will cause an interactive break.

    To remove a rune from the list of monitored runes, use unmonitor. To see which runes are monitored and what their break conditions are, evaluate (monitored-runes).

    Monitor, unmonitor and monitored-runes are macros that expand into expressions involving state. While these macros appear to return the list of monitored runes this is an illusion. They all print monitored rune information to the comment window and then return error triples (see error-triples) instructing ld to print nothing. It is impossible to return the list of monitored runes because it exists only in the wormhole state with which you interact when a break occurs. This allows you to change the monitored runes and their conditions during the course of a proof attempt without changing the state in which the the proof is being constructed.

    Unconditional break points are obtained by using the break condition t. We now discuss conditional break points. The break condition, expr, must be a term that contains no free variables other than state and that returns a single non-state result. In fact, the result should be nil, t, or a true list of commands to be fed to the resulting interactive break. Whenever the system attempts to use the associated rule, expr is evaluated in the wormhole interaction state. A break occurs only if the result of evaluating expr is non-nil. If the result is a true list, that list is appended to the front of standard-oi and hence is taken as the initial user commands issued to the interactive break.

    In order to develop effective break conditions it must be possible to access context sensitive information, i.e., information about the context in which the monitored rune is being tried. The brr@ macro may be used in break conditions to access such information as the term being rewritten and the current governing assumptions. This information is not stored in the proof state but is transferred into the wormhole state when breaks occur. The macro form is (brr@ :sym) where :sym is one of several keyword symbols, including :target (the term being rewritten), :unify-subst (the substitution that instantiates the left-hand side of the conclusion of the rule so that it is the target term), and :type-alist (the governing assumptions). See brr@.

    For example,

    ACL2 !>:monitor (:rewrite assoc-of-app)
                    (equal (brr@ :target) '(app a (app b c)))
    
    will monitor (:rewrite assoc-of-app) but will cause an interactive break only when the target term, the term being rewritten, is (app a (app b c)).

    Because break conditions are evaluated in the interaction environment, the user developing a break condition for a given rune can test candidate break conditions before installing them. For example, suppose an unconditional break has been installed on a rune, that an interactive break has occurred and that the user has determined both that this particular application is uninteresting and that many more such applications will likely occur. An appropriate response would be to develop an expression that recognizes such applications and returns nil. Of course, the hard task is figuring out what makes the current application uninteresting. But once a candidate expression is developed, the user can evaluate it in the current context simply to confirm that it returns nil.

    Recall that when a break condition returns a non-nil true list that list is appended to the front of standard-oi. For example,

    ACL2 !>:monitor (:rewrite assoc-of-app) '(:go)
    
    will cause (:rewrite assoc-of-app) to be monitored and will make the break condition be '(:go). This break condition always evaluates the non-nil true list (:go). Thus, an interactive break will occur every time (:rewrite assoc-of-app) is tried. The break is fed the command :go. Now the command :go causes break-rewrite to (a) evaluate the attempt to apply the lemma, (b) print the result of that attempt, and (c) exit from the interactive break and let the proof attempt continue. Thus, in effect, the above :monitor merely ``traces'' the attempted applications of the rune but never causes an interactive break requiring input from the user.

    It is possible to use this feature to cause a conditional break where the effective break condition is tested after the lemma has been tried. For example:

    ACL2 !>:monitor (:rewrite lemma12)
                    '(:unify-subst
                      :eval$ nil
                      :ok-if (or (not (brr@ :wonp))
                                 (not (equal (brr@ :rewritten-rhs) '(foo a))))
                      :rewritten-rhs)
    
    causes the following behavior when (:rewrite lemma12) is tried. A break always occurs, but it is fed the commands above. The first, :unify-subst, causes break-rewrite to print out the unifying substitution. Then in response to :eval$ nil the lemma is tried but with all runes temporarily unmonitored. Thus no breaks will occur during the rewriting of the hypotheses of the lemma. When the attempt has been made, control returns to break-rewrite (which will print the results of the attempt, i.e., whether the lemma was applied, if so what the result is, if not why it failed). The next command, the :ok-if with its following expression, is a conditional exit command. It means exit break-rewrite if either the attempt was unsuccessful, (not (brr@ :wonp)), or if the result of the rewrite is any term other than (foo a). If this condition is met, the break is exited and the remaining break commands are irrelevant. If this condition is not met then the next command, :rewritten-rhs, prints the result of the application (which in this contrived example is known to be (foo a)). Finally, the list of supplied commands is exhausted but break-rewrite expects more input. Therefore, it begins prompting the user for input. The end result, then, of the above :monitor command is that the rune in question is elaborately traced and interactive breaks occur whenever it rewrites its target to (foo a).

    We recognize that the above break condition is fairly arcane. We suspect that with experience we will develop some useful idioms. For example, it is straightforward now to define macros that monitor runes in the ways suggested by the following names: trace-rune, break-if-target-is, and break-if-result-is. For example, the last could be defined as

    (defmacro break-if-result-is (rune term)
      `(monitor ',rune
                '(quote (:eval :ok-if
                               (not (equal (brr@ :rewritten-rhs) ',term))))))
    
    (Note however that the submitted term must be in translated form.)

    Since we don't have any experience with this kind of control on lemmas we thought it best to provide a general (if arcane) mechanism and hope that the ACL2 community will develop the special cases that we find most convenient.




    acl2-sources/doc/HTML/MONITORED-RUNES.html0000664002132200015000000000134612222333516017207 0ustar kaufmannacl2 MONITORED-RUNES.html -- ACL2 Version 6.3

    MONITORED-RUNES

    print the monitored runes and their break conditions
    Major Section:  BREAK-REWRITE
    

    Example and General Form:
    :monitored-runes
    

    This macro prints a list, each element of which is of the form (rune expr), showing each monitored rune and its current break condition.




    acl2-sources/doc/HTML/MORE-DOC.html0000664002132200015000000000303712222333516016121 0ustar kaufmannacl2 MORE-DOC.html -- ACL2 Version 6.3

    MORE-DOC

    a continuation of the :doc documentation
    Major Section:  DOCUMENTATION
    

    NOTE: The :more-doc command only makes sense at the terminal.

    Examples:
    ACL2 !>:more-doc DEFTHM
    ACL2 !>:more-doc logical-name
    
    Often it is assumed in the text provided by :more-doc name that you have read the text provided by :doc name.

    :More-doc just continues spewing out at you the documentation string provided with a definition. If the user has done his job, :doc will probably remind you of the basics and :more-doc, if read after :doc, will address obscure details that are nevertheless worth noting.

    When :more-doc types ``(type :more for more, :more! for the rest)'' you can get the next block of the continuation by typing :more or all of the remaining blocks by typing :more!. See more.




    acl2-sources/doc/HTML/MORE.html0000664002132200015000000000553312222333516015521 0ustar kaufmannacl2 MORE.html -- ACL2 Version 6.3

    MORE

    your response to :doc or :more's ``(type :more...)''
    Major Section:  DOCUMENTATION
    

    NOTE: The command :more only makes sense at the terminal.

    Example:
    ACL2 !>:more
    
    will continue printing whatever documentation was started by :doc or :more-doc.

    When you type :doc name, for some documented name, the system responds by typing the one-liner and the notes sections of the documentation for name. It then types ``(type :more for more, :more! for the rest)''. If you then type

    ACL2 !>:more
    
    the system will start to print the details section of name. The same thing could be achieved by typing :more-doc name, but that requires you to type name again.

    Similarly, if you have typed :more-doc name, the system will print the first ``block'' of the details section and then print ``(type :more for more, :more! for the rest)''. Typing :more at that point will cause the next block of the details section to be printed. Eventually :more will conclude by printing ``*-'' which is the indicator that the text has been exhausted.

    What is a ``block'' of text? :More looks for the end of a paragraph (two adjacent newlines) after printing n lines. If it doesn't find one before it has printed k lines, it just stops there. N and k here are the values of the two state global variables 'more-doc-min-lines and 'more-doc-max-lines. You may use @ and assign to inspect and set these variables, e.g., (@ more-doc-max-lines) will return the current maximum number of lines printed by :more and (assign more-doc-max-lines 19) will set it to 19. On terminals having only 24 lines, we find min and max settings of 12 and 19 the most pleasant.

    If you want :more to print all of the details instead of feeding them to you one block at a time, type :more! instead.




    acl2-sources/doc/HTML/MORE_bang_.html0000664002132200015000000000264012222333516016643 0ustar kaufmannacl2 MORE_bang_.html -- ACL2 Version 6.3

    MORE!

    another response to ``(type :more for more, :more! for the rest)''
    Major Section:  DOCUMENTATION
    

    NOTE: The command :more! only makes sense at the terminal.

    Example:
    ACL2 !>:more!
    
    will print all of the remaining documentation started by the last :doc or :more-doc.

    See more for some background. Typing :more! will print all remaining blocks of documentation.

    :More! is like :more except that it prints all the text at once. For example, if you type :doc name you will see some text followed by ``(type :more for more, :more! for the rest)''. If you then type simply :more! you will see all of the details, while if you type :more you will be fed the next block of details.




    acl2-sources/doc/HTML/MSG.html0000664002132200015000000001163412222333524015403 0ustar kaufmannacl2 MSG.html -- ACL2 Version 6.3

    MSG

    construct a ``message'' suitable for the ~@ directive of fmt
    Major Section:  ACL2-BUILT-INS
    

    See fmt for background on formatted printing with ACL2.

    We document msg precisely below, but first, we give an informal introduction and illustrate with an example. Suppose you are writing a program that is to do some printing. Typically, you will either pass the ACL2 state around (see programming-with-state) and use formatted printing functions that take the state as an argument (see fmt)), or else you might avoid using state by calling the utility, cw, to do you printing. Alternatively, you might print error messages upon encountering illegal situations; see er. But there are times where instead of printing immediately, you may prefer to pass messages around, for example to accumulate them before printing a final message. In such cases, it may be desirable to construct ``message'' objects to pass around.

    For example, consider the following pair of little programs. The first either performs a computation or prints an error, and the second calls the first on two inputs.

    
    (defun invert1 (x)
      (if (consp x)
          (cons (cdr x) (car x))
        (prog2$ (cw "ERROR: ~x0 expected a cons, but was given ~x1.~|"
                    'invert1 x)
                nil)))
    
    (defun invert2 (x1 x2)
      (list (invert1 x1) (invert1 x2)))
    
    
    For example:
    
      ACL2 !>(invert1 '(3 . 4))
      (4 . 3)
      ACL2 !>(invert1 'a)
      ERROR: INVERT1 expected a cons, but was given A.
      NIL
      ACL2 !>(invert2 '(3 . 4) '(5 . 6))
      ((4 . 3) (6 . 5))
      ACL2 !>(invert2 'a 'b)
      ERROR: INVERT1 expected a cons, but was given A.
      ERROR: INVERT1 expected a cons, but was given B.
      (NIL NIL)
      ACL2 !>
    
    
    Notice that when there are errors, there is no attempt to explain that these are due to a call of invert2. That could be fixed, of course, by arranging for invert2 to print its own error; but for complicated programs it can be awkward to coordinate printing from many sources. So let's try a different approach. This time, the first function returns two results. The first result is an ``error indicator'' -- either a message object or nil -- while the second is the computed value (only of interest when the first result is nil). Then the higher-level function can print a single error message that includes the error message(s) from the lower-level function, as shown below.
    
    (defun invert1a (x)
      (if (consp x)
          (mv nil
              (cons (cdr x) (car x)))
        (mv (msg "ERROR: ~x0 expected a cons, but was given ~x1.~|"
                 'invert1a x)
            nil)))
    
    (defun invert2a (x1 x2)
      (mv-let (erp1 y1)
              (invert1a x1)
              (mv-let (erp2 y2)
                      (invert1a x2)
                      (if erp1
                          (if erp2
                              (cw "~x0 failed with two errors:~|  ~@1  ~@2"
                                  'invert2a erp1 erp2)
                            (cw "~x0 failed with one error:~|  ~@1"
                                'invert2a erp1))
                        (if erp2
                            (cw "~x0 failed with one error:~|  ~@1"
                                'invert2a erp2)
                          (list y1 y2))))))
    
    For example:
      ACL2 !>(invert2a '(3 . 4) '(5 . 6))
      ((4 . 3) (6 . 5))
      ACL2 !>(invert2a '(3 . 4) 'b)
      INVERT2A failed with one error:
        ERROR: INVERT1A expected a cons, but was given B.
      NIL
      ACL2 !>(invert2a 'a 'b)
      INVERT2A failed with two errors:
        ERROR: INVERT1A expected a cons, but was given A.
        ERROR: INVERT1A expected a cons, but was given B.
      NIL
      ACL2 !>
    

    If you study the example above, you might well understand msg. But we conclude with precise documentation.

    General Form:
    (msg str arg1 ... argk)
    
    where str is a string and k is at most 9.

    This macro returns a pair suitable for giving to the fmt directive ~@. Thus, suppose that #\c is bound to the value of (msg str arg1 ... argk), where c is a character and k is at most 9. Then the fmt directive ~@c will print out the string, str, in the context of the alist in which the successive fmt variables #\0, #\1, ..., #\k are bound to the successive elements of (arg1 ... argk).




    acl2-sources/doc/HTML/MUST-BE-EQUAL.html0000664002132200015000000000230112222333524016665 0ustar kaufmannacl2 MUST-BE-EQUAL.html -- ACL2 Version 6.3

    MUST-BE-EQUAL

    attach code for execution
    Major Section:  ACL2-BUILT-INS
    

    The form (must-be-equal logic exec) evaluates to logic in the ACL2 logic but evaluates to exec in raw Lisp. The point is to be able to write one definition to reason about logically but another for evaluation. Please see mbe and see mbt for appropriate macros to use, rather than calling must-be-equal directly, since it is easy to commute the arguments of must-be-equal by accident.

    In essence, the guard for (must-be-equal x y) is (equal x y). However, note that must-be-equal is a macro: (must-be-equal logic exec) expands to (mbe1 exec logic), which expands to a call of return-last.




    acl2-sources/doc/HTML/MUTUAL-RECURSION-PROOF-EXAMPLE.html0000664002132200015000000001543312222333515021310 0ustar kaufmannacl2 MUTUAL-RECURSION-PROOF-EXAMPLE.html -- ACL2 Version 6.3

    MUTUAL-RECURSION-PROOF-EXAMPLE

    a small proof about mutually recursive functions
    Major Section:  TUTORIAL5-MISCELLANEOUS-EXAMPLES
    

    Sometimes one wants to reason about mutually recursive functions. Although this is possible in ACL2, it can be a bit awkward. This example is intended to give some ideas about how one can go about such proofs.

    For an introduction to mutual recursion in ACL2, see mutual-recursion.

    We begin by defining two mutually recursive functions: one that collects the variables from a term, the other that collects the variables from a list of terms. We actually imagine the term argument to be a pseudo-termp; see pseudo-termp.

    (mutual-recursion
    
    (defun free-vars1 (term ans)
      (cond ((atom term)
             (add-to-set-eq term ans))
            ((fquotep term) ans)
            (t (free-vars1-lst (cdr term) ans))))
    
    (defun free-vars1-lst (lst ans)
      (cond ((atom lst) ans)
            (t (free-vars1-lst (cdr lst)
                               (free-vars1 (car lst) ans)))))
    
    )
    
    Now suppose that we want to prove the following theorem.
    (defthm symbol-listp-free-vars1-try-1
      (implies (and (pseudo-termp x)
                    (symbol-listp ans))
               (symbol-listp (free-vars1 x ans))))
    
    Often ACL2 can generate a proof by induction based on the structure of definitions of function symbols occurring in the conjecture. In this case, ACL2 chooses to use an induction scheme suggested by (symbol-listp ans), and sadly, that doesn't work. If one were doing this proof with pencil and paper, one would be more likely to prove a combination of the conjecture above and an analogous conjecture about free-vars1-lst. Feel free to try a pencil and paper proof! Or you can read on, to see how one can get ACL2 to do such a proof after all.

    The trick is to define a function that suggests an appropriate induction. The induction suggested is based on the if-then-else structure of the function's definition, where inductive hypotheses are generated for recursive calls -- below we explain how that works for this function.

    (defun symbol-listp-free-vars1-induction (x ans)
      (if (atom x)
    ; then we just make sure x and ans aren't considered irrelevant:
          (list x ans)
        (list (symbol-listp-free-vars1-induction (car x) ans)
              (symbol-listp-free-vars1-induction (cdr x) ans)
              (symbol-listp-free-vars1-induction (cdr x)
                                                 (free-vars1 (car x) ans)))))
    
    The if-then-else structure of this function generates two cases. In one case, (atom x) is true, and the theorem to be proved should be proved under no additional hypotheses except for (atom x); in other words, (atom x) gives us the base case of the induction. In the other case, (not (atom x)) is assumed together with three instances of the theorem to be proved, one for each recursive call. So, one instance substitutes (car x) for x; one substitutes (cdr x) for x; and the third substitutes (cdr x) for x and (free-vars1 (car x) ans) for ans. If you think about how you would go about a hand proof of the theorem to follow, you'll likely come up with a similar scheme.

    We now prove the two theorems together as a conjunction, because the inductive hypotheses for one are sometimes needed in the proof of the other (even when you do this proof on paper!).

    (defthm symbol-listp-free-vars1
      (and (implies (and (pseudo-termp x)
                         (symbol-listp ans))
                    (symbol-listp (free-vars1 x ans)))
           (implies (and (pseudo-term-listp x)
                         (symbol-listp ans))
                    (symbol-listp (free-vars1-lst x ans))))
      :hints
      (("Goal" :induct (symbol-listp-free-vars1-induction x ans))))
    

    The above works, but we conclude by illustrating a more efficient approach, in which we restrict to appropriate inductive hypotheses for each case.

    (ubt 'symbol-listp-free-vars1-induction)
    
    (defun symbol-listp-free-vars1-induction (flg x ans)
    
    ; Flg is nil if we are ``thinking'' of a single term.
    
      (if (atom x) ; whether x is a single term or a list of terms
          (list x ans)
        (if flg ; i.e., if x is a list of terms
            (list (symbol-listp-free-vars1-induction nil (car x) ans)
                  (symbol-listp-free-vars1-induction t
                                                     (cdr x)
                                                     (free-vars1 (car x) ans)))
          (symbol-listp-free-vars1-induction t (cdr x) ans))))
    
    We now state the theorem as a conditional, so that it can be proved nicely using the induction scheme that we have just coded. The prover will not store an IF term as a rewrite rule, but that's OK (provided we tell it not to try), because we're going to derive the corollaries of interest later and make them into rewrite rules.
    (defthm symbol-listp-free-vars1-flg
      (if flg
          (implies (and (pseudo-term-listp x)
                        (symbol-listp ans))
                   (symbol-listp (free-vars1-lst x ans)))
        (implies (and (pseudo-termp x)
                      (symbol-listp ans))
                 (symbol-listp (free-vars1 x ans))))
      :hints
      (("Goal" :induct (symbol-listp-free-vars1-induction flg x ans)))
      :rule-classes nil)
    
    And finally, we may derive the theorems we are interested in as immediate corollaries.
    (defthm symbol-listp-free-vars1
      (implies (and (pseudo-termp x)
                    (symbol-listp ans))
               (symbol-listp (free-vars1 x ans)))
      :hints (("Goal" :by (:instance symbol-listp-free-vars1-flg
                                     (flg nil)))))
    
    (defthm symbol-listp-free-vars1-lst
      (implies (and (pseudo-term-listp x)
                    (symbol-listp ans))
               (symbol-listp (free-vars1-lst x ans)))
      :hints (("Goal" :by (:instance symbol-listp-free-vars1-flg
                                     (flg t)))))
      

    You may find community books (see community-books that help you to automate this kind of reasoning about mutually recursive functions. See for example the community book tools/flag.lisp.




    acl2-sources/doc/HTML/MUTUAL-RECURSION.html0000664002132200015000000001366112222333517017337 0ustar kaufmannacl2 MUTUAL-RECURSION.html -- ACL2 Version 6.3

    MUTUAL-RECURSION

    define some mutually recursive functions
    Major Section:  EVENTS
    

    Example:
    (mutual-recursion
     (defun evenlp (x)
       (if (consp x) (oddlp (cdr x)) t))
     (defun oddlp (x)
       (if (consp x) (evenlp (cdr x)) nil)))
    
    General Form:
    (mutual-recursion def1 ... defn)
    where each defi is a call of defun, defund, defun-nx,
    or defund-nx.
    
    When mutually recursive functions are introduced it is necessary to do the termination analysis on the entire clique of definitions. Each defun form specifies its own measure, either with the :measure keyword xarg (see xargs) or by default to acl2-count. When a function in the clique calls a function in the clique, the measure of the callee's actuals must be smaller than the measure of the caller's formals -- just as in the case of a simply recursive function. But with mutual recursion, the callee's actuals are measured as specified by the callee's defun while the caller's formals are measured as specified by the caller's defun. These two measures may be different but must be comparable in the sense that o< decreases through calls.

    If you want to specify :hints or :guard-hints (see xargs), you can put them in the xargs declaration of any of the defun forms, as the :hints from each form will be appended together, as will the guard-hints from each form.

    You may find it helpful to use a lexicographic order, the idea being to have a measure that returns a list of two arguments, where the first takes priority over the second. Here is an example.

    (include-book "ordinals/lexicographic-ordering" :dir :system)
    
    (encapsulate
     ()
     (set-well-founded-relation l<) ; will be treated as LOCAL
    
     (mutual-recursion
      (defun foo (x)
        (declare (xargs :measure (list (acl2-count x) 1)))
        (bar x))
      (defun bar (y)
        (declare (xargs :measure (list (acl2-count y) 0)))
        (if (zp y) y (foo (1- y))))))
    

    The guard analysis must also be done for all of the functions at the same time. If any one of the defuns specifies the :verify-guards xarg to be nil, then guard verification is omitted for all of the functions. Similarly, if any one of the defuns specifies the :non-executable xarg to be t, or if any of the definitions uses defun-nx or defund-nx, then every one of the definitions will be treated as though it specifies a :non-executable xarg of t.

    Technical Note: Each defi above must be a call of defun, defund, defun-nx, or defund-nx. In particular, it is not permitted for a defi to be an arbitrary form that macroexpands into a defun form. This is because mutual-recursion is itself a macro, and since macroexpansion occurs from the outside in, at the time (mutual-recursion def1 ... defk) is expanded the defi have not yet been macroexpanded.

    Suppose you have defined your own defun-like macro and wish to use it in a mutual-recursion expression. Well, you can't. (!) But you can define your own version of mutual-recursion that allows your defun-like form. Here is an example. Suppose you define

    (defmacro my-defun (&rest args) (my-defun-fn args))
    
    where my-defun-fn takes the arguments of the my-defun form and produces from them a defun form. As noted above, you are not allowed to write (mutual-recursion (my-defun ...) ...). But you can define the macro my-mutual-recursion so that
    (my-mutual-recursion (my-defun ...) ... (my-defun ...))
    
    expands into (mutual-recursion (defun ...) ... (defun ...)) by applying my-defun-fn to each of the arguments of my-mutual-recursion.
    (defun my-mutual-recursion-fn (lst)
      (declare (xargs :guard (alistp lst)))
    
    ; Each element of lst must be a consp (whose car, we assume, is always
    ; MY-DEFUN).  We apply my-defun-fn to the arguments of each element and
    ; collect the resulting list of DEFUNs.
    
      (cond ((atom lst) nil)
            (t (cons (my-defun-fn (cdr (car lst)))
                     (my-mutual-recursion-fn (cdr lst))))))
    
    (defmacro my-mutual-recursion (&rest lst)
    
    ; Each element of lst must be a consp (whose car, we assume, is always
    ; MY-DEFUN).  We obtain the DEFUN corresponding to each and list them
    ; all inside a MUTUAL-RECURSION form.
    
      (declare (xargs :guard (alistp lst)))
      (cons 'mutual-recursion (my-mutual-recursion-fn lst))).
    




    acl2-sources/doc/HTML/MV-LET.html0000664002132200015000000001370412222333524015721 0ustar kaufmannacl2 MV-LET.html -- ACL2 Version 6.3

    MV-LET

    calling multi-valued ACL2 functions
    Major Section:  ACL2-BUILT-INS
    

    Example Form:
    (mv-let (x y z)              ; local variables
            (mv 1 2 3)           ; multi-valued expression
            (declare (ignore y)) ; optional declarations
            (cons x z))          ; body
    
    The form above binds the three ``local variables,'' x, y, and z, to the three results returned by the multi-valued expression and then evaluates the body. The result is '(1 . 3). The second local, y, is declared ignored. The multi-valued expression can be any ACL2 expression that returns k results, where k is the number of local variables listed. Often however it is simply the application of a k-valued function. Mv-let is the standard way to invoke a multi-valued function when the caller must manipulate the vector of results returned.

    General Form:
    (mv-let (var1 ... vark)
            term
            body)
    or
    (mv-let (var1 ... vark)
            term
            (declare ...) ... (declare ...)
            body)
    
    where the vari are distinct variables, term is a term that returns k results and mentions only variables bound in the environment containing the mv-let expression, and body is a term mentioning only the vari and variables bound in the environment containing the mv-let. Each vari must occur in body unless it is declared ignored or ignorable in one of the optional declare forms, unless this requirement is turned off; see set-ignore-ok. The value of the mv-let term is the result of evaluating body in an environment in which the vari are bound, in order, to the k results obtained by evaluating term in the environment containing the mv-let.

    Here is an extended example that illustrates both the definition of a multi-valued function and the use of mv-let to call it. Consider a simple binary tree whose interior nodes are conses and whose leaves are non-conses. Suppose we often need to know the number, n, of interior nodes of such a tree; the list, syms, of symbols that occur as leaves; and the list, ints, of integers that occur as leaves. (Observe that there may be leaves that are neither symbols nor integers.) Using a multi-valued function we can collect all three results in one pass.

    Here is the first of two definitions of the desired function. This definition is ``primitive recursive'' in that it has only one argument and that argument is reduced in size on every recursion.

    (defun count-and-collect (x)
    
    ; We return three results, (mv n syms ints) as described above.
    
      (cond ((atom x)
    
    ; X is a leaf.  Thus, there are 0 interior nodes, and depending on
    ; whether x is a symbol, an integer, or something else, we return
    ; the list containing x in as the appropriate result.
    
             (cond ((symbolp x) (mv 0 (list x) nil))
                   ((integerp x)(mv 0 nil      (list x)))
                   (t           (mv 0 nil      nil))))
            (t
    
    ; X is an interior node.  First we process the car, binding n1, syms1, and
    ; ints1 to the answers.
    
               (mv-let (n1 syms1 ints1)
                       (count-and-collect (car x))
    
    ; Next we process the cdr, binding n2, syms2, and ints2.
    
                       (mv-let (n2 syms2 ints2)
                               (count-and-collect (car x))
    
    ; Finally, we compute the answer for x from those obtained for its car
    ; and cdr, remembering to increment the node count by one for x itself.
    
                               (mv (1+ (+ n1 n2))
                                   (append syms1 syms2)
                                   (append ints1 ints2)))))))
    
    This use of a multiple value to ``do several things at once'' is very common in ACL2. However, the function above is inefficient because it appends syms1 to syms2 and ints1 to ints2, copying the list structures of syms1 and ints1 in the process. By adding ``accumulators'' to the function, we can make the code more efficient.
    (defun count-and-collect1 (x n syms ints)
      (cond ((atom x)
             (cond ((symbolp x) (mv n (cons x syms) ints))
                   ((integerp x) (mv n syms (cons x ints)))
                   (t (mv n syms ints))))
            (t (mv-let (n2 syms2 ints2)
                       (count-and-collect1 (cdr x) (1+ n) syms ints)
                       (count-and-collect1 (car x) n2 syms2 ints2)))))
    
    We claim that (count-and-collect x) returns the same triple of results as (count-and-collect1 x 0 nil nil). The reader is urged to study this claim until convinced that it is true and that the latter method of computing the results is more efficient. One might try proving the theorem
    (defthm count-and-collect-theorem
      (equal (count-and-collect1 x 0 nil nil) (count-and-collect x))).
    
    Hint: the inductive proof requires attacking a more general theorem.

    ACL2 does not support the Common Lisp construct multiple-value-bind, whose logical meaning seems difficult to characterize. Mv-let is the ACL2 analogue of that construct. Also see mv and see mv-list.




    acl2-sources/doc/HTML/MV-LIST.html0000664002132200015000000000415112222333524016044 0ustar kaufmannacl2 MV-LIST.html -- ACL2 Version 6.3

    MV-LIST

    converting multiple-valued result to a single-valued list
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    ; Returns the list (3 4):
    (mv-list 2 (mv 3 4))
    
    ; Returns a list containing the three values returned by var-fn-count:
    (mv-list 3 (var-fn-count '(cons (binary-+ x y) z) nil))
    
    General form:
    (mv-list n term)
    

    Logically, (mv-list n term) is just term; that is, in the logic mv-list simply returns its second argument. However, the evaluation of a call of mv-list on explicit values always results in a single value, which is a (null-terminated) list. For evaluation, the term n above (the first argument to an mv-list call) must ``essentially'' (see below) be an integer not less than 2, where that integer is the number of values returned by the evaluation of term (the second argument to that mv-list call).

    We say ``essentially'' above because it suffices that the translation of n to a term (see trans) be of the form (quote k), where k is an integer greater than 1. So for example, if term above returns three values, then n can be the expression 3, or (quote 3), or even (mac 3) if mac is a macro defined by (defmacro mac (x) x). But n cannot be (+ 1 2), because even though that expression evaluates to 3, nevertheless it translates to (binary-+ '1 '2), not to (quote 3).

    Mv-list is the ACL2 analogue of the Common Lisp construct multiple-value-list.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/MV-NTH.html0000664002132200015000000000455712222333524015734 0ustar kaufmannacl2 MV-NTH.html -- ACL2 Version 6.3

    MV-NTH

    the mv-nth element (zero-based) of a list
    Major Section:  ACL2-BUILT-INS
    

    (Mv-nth n l) is the nth element of l, zero-based. If n is greater than or equal to the length of l, then mv-nth returns nil.

    (Mv-nth n l) has a guard that n is a non-negative integer.

    Mv-nth is equivalent to the Common Lisp function nth (although without the guard condition that the list is a true-listp), but is used by ACL2 to access the nth value returned by a multiply valued expression. For example, the following are logically equivalent:

    (mv-let (erp val state)
            (read-object ch state)
            (value (list erp val)))
    
    and
    (let ((erp (mv-nth 0 (read-object ch state)))
          (val (mv-nth 1 (read-object ch state)))
          (state (mv-nth 2 (read-object ch state))))
      (value (list erp val)))
    

    To see the ACL2 definition of mv-nth, see pf.

    If EXPR is an expression that is multiply valued, then the form (mv-nth n EXPR) is illegal both in definitions and in forms submitted directly to the ACL2 loop. Indeed, EXPR cannot be passed as an argument to any function (mv-nth or otherwise) in such an evaluation context. The reason is that ACL2 code compiled for execution does not actually create a list for multiple value return; for example, the read-object call above logically returns a list of length 3, but when evaluated, it instead stores its three returned values without constructing a list. In such cases you can use mv-nth to access the corresponding list by using mv-list, writing (mv-nth n (mv-list k EXPR)) for suitable k, where mv-list converts a multiple value result into the corresponding list; see mv-list.




    acl2-sources/doc/HTML/MV.html0000664002132200015000000000245012222333524015273 0ustar kaufmannacl2 MV.html -- ACL2 Version 6.3

    MV

    returning a multiple value
    Major Section:  ACL2-BUILT-INS
    

    Mv is the mechanism provided by ACL2 for returning two or more values. Logically, (mv x1 x2 ... xn) is the same as (list x1 x2 ... xn), a list of the indicated values. However, ACL2 avoids the cost of building this list structure, with the cost that mv may only be used in a certain style in definitions: if a function ever returns using mv (either directly, or by calling another function that returns a multiple value), then this function must always return the same number of values.

    For more explanation of the multiple value mechanism, see mv-let. Also see mv-list for a way to convert a multiple value into an ordinary list.

    ACL2 does not support the Common Lisp construct values, whose logical meaning seems difficult to characterize. Mv is the ACL2 analogue of that construct.




    acl2-sources/doc/HTML/MV_qm_-LET.html0000664002132200015000000000335612222333524016557 0ustar kaufmannacl2 MV_qm_-LET.html -- ACL2 Version 6.3

    MV?-LET

    calling possibly multi-valued ACL2 functions
    Major Section:  ACL2-BUILT-INS
    

    Mv?-let is a macro that extends the macro mv-let by allowing a single variable to be bound. Thus, the syntax is the same as that of mv-let except that mv?-let is permitted to bind a single variable to a form that produces a single value. The macros mv?-let and mv? are provided to facilitate the writing of macros that traffic in expressions that could return one or more (multiple) values.

    For example, the form

    (mv?-let (y)
             (f x)
             (declare (type integer y))
             (g x y z))
    
    is equivalent to the following form.
    (let ((y (f x)))
      (declare (type integer y))
      (g x y z))
    

    Calls of mv?-let and of mv-let are equivalent when the first argument contains at least two variables; see mv-let for documentation. In the case of binding a single variable, the general form is

    (mv?-let (var)
             term
             (declare ...) ... (declare ...)
             body)
    
    and this is equivalent to the following form (see let).
    (let ((var term))
      (declare ...) ... (declare ...)
      body)
    

    Also see mv?.




    acl2-sources/doc/HTML/MV_qm_.html0000664002132200015000000000231312222333524016125 0ustar kaufmannacl2 MV_qm_.html -- ACL2 Version 6.3

    MV?

    return one or more values
    Major Section:  ACL2-BUILT-INS
    

    Mv? is designed to work with mv?-let, generalizing how mv works with mv-let by allowing the binding of a single variable. For example, the following form is legal.

    (mv?-let (y)
             (mv? (f x))
             (declare (type integer y))
             (g x y z))
    
    The expression above is equivalent to the following expression, because (mv? form) expands to form for any expression, form.
    (let ((y (f x)))
      (declare (type integer y))
      (g x y z))
    

    Logically, (mv? x) is the same as x, while (mv? v1 v2 ...) is the same as (list v1 v2 ...). Also see mv and see mv?-let.




    acl2-sources/doc/HTML/Modeling_in_ACL2.html0000664002132200015000000000211012222333526017731 0ustar kaufmannacl2 Modeling_in_ACL2.html -- ACL2 Version 6.3

    Modeling in ACL2

    Below we define mc(s,n) to be the function that single-steps n times from a given starting state, s. In Common Lisp, ``mc(s,n)'' is written (mc s n).

    (defun mc (s n)                     ; To step s n times:
     (if (zp n)                         ; If n is 0
         s                              ;    then return s
         (mc (single-step s) (- n 1)))) ;    else step single-step(s)
                                                          n-1 times.
    

    This is an example of a formal model in ACL2.




    acl2-sources/doc/HTML/Models_in_Engineering.html0000664002132200015000000000144412222333526021200 0ustar kaufmannacl2 Models_in_Engineering.html -- ACL2 Version 6.3

    Models in Engineering

    Frequently, engineers use mathematical models. Use of such models frequently lead to

    better designs,

    faster completion of acceptable products, and

    reduced overall cost,

    because models allow the trained user to study the design before it is built and analyze its properties. Usually, testing and analyzing a model is cheaper and faster than fabricating and refabricating the product.




    acl2-sources/doc/HTML/Models_of_Computer_Hardware_and_Software.html0000664002132200015000000000241612222333526025053 0ustar kaufmannacl2 Models_of_Computer_Hardware_and_Software.html -- ACL2 Version 6.3

    Models of Computer Hardware and Software

    Computing machines, whether hardware or software or some combintation, are frequently modeled as ``state machines.''

    To so model a computing machine we must represent its states as objects in our mathematical framework.

    Transitions are functions or relations on state objects.

    In what language shall we define these objects, functions, and relations?

    The mathematical languages we were taught in high school

    algebra,

    geometry,

    trignometry, and

    calculus

    are often inappropriate for modeling digital systems. They primarily let us talk about numbers and continuous functions.

    To see what kind of expressive power we need, take a closer look at what a typical state contains.




    acl2-sources/doc/HTML/NAME.html0000664002132200015000000000506112222333521015467 0ustar kaufmannacl2 NAME.html -- ACL2 Version 6.3

    NAME

    syntactic rules on logical names
    Major Section:  MISCELLANEOUS
    

    Examples                 Counter-Examples
    
    PRIMEP               91         (not a symbolp)
    F-AC-23              :CHK-LIST  (in KEYWORD package)
    1AX                  *PACKAGE*  (in the Lisp Package)
    |Prime Number|       1E3        (not a symbolp)
    

    Many different ACL2 entities have names, e.g., functions, macros, variables, constants, packages, theorems, theories, etc. Package names are strings. All other names are symbols and may not be in the "KEYWORD" package. Moreover, names of functions, macros, constrained functions, and constants, other than those that are predefined, must not be in the ``main Lisp package'' (which is ("LISP" or "COMMON-LISP", according to whether we are following CLTL1 or CLTL2). An analogous restriction on variables is given below.

    T, nil, and all names above except those that begin with ampersand (&) are names of variables or constants. T, nil, and those names beginning and ending with star (*) are constants. All other such names are variables.

    Note that names that start with ampersand, such as &rest, may be lambda list keywords and are reserved for such use whether or not they are in the CLTL constant lambda-list-keywords. (See pg 82 of CLTL2.) That is, these may not be used as variables. Unlike constants, variables may be in the main Lisp package as long as they are in the original list of imports from that package to ACL2, the list *common-lisp-symbols-from-main-lisp-package*, and do not belong to a list of symbols that are potentially ``global.'' This latter list is the value of *common-lisp-specials-and-constants*.

    Our restrictions pertaining to the main Lisp package take into account that some symbols, e.g., lambda-list-keywords and boole-c2, are ``special.'' Permitting them to be bound could have harmful effects. In addition, the Common Lisp language does not allow certain manipulations with many symbols of that package. So, we stay away from them, except for allowing certain variables as indicated above.




    acl2-sources/doc/HTML/NAT-LISTP.html0000664002132200015000000000112512222333524016262 0ustar kaufmannacl2 NAT-LISTP.html -- ACL2 Version 6.3

    NAT-LISTP

    recognizer for a true list of natural numbers
    Major Section:  ACL2-BUILT-INS
    

    The predicate nat-listp tests whether its argument is a true list of natural numbers.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NATP.html0000664002132200015000000000171112222333524015512 0ustar kaufmannacl2 NATP.html -- ACL2 Version 6.3

    NATP

    a recognizer for the natural numbers
    Major Section:  ACL2-BUILT-INS
    

    The natural numbers is the set of all non-negative integers, {0,1,2,3,...}. Natp returns t if and only its argument is a natural number, and nil otherwise. We recommend the community book books/arithmetic/natp-posp.lisp as a book for reasoning about posp and natp. This book is included by community books books/arithmetic/top and books/arithmetic/top-with-meta.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NESTED-STOBJS.html0000664002132200015000000006450112222333530016737 0ustar kaufmannacl2 NESTED-STOBJS.html -- ACL2 Version 6.3

    NESTED-STOBJS

    using stobjs that contain stobjs
    Major Section:  STOBJ
    

    For this topic we assume that you already understand the basics of single-threaded objects in ACL2. See stobj, and in particular, see defstobj. The latter topic defers discussion of the ability to specify a stobj field that is itself a stobj or an array of stobjs. That discussion is the subject of the present documentation topic.

    Our presentation is in four sections. First we augment the documentation for defstobj by explaining how stobjs may be specified for fields in a new stobj definition. Then we explain an aliasing problem, which accounts for a prohibition against making direct calls to accessors and updaters involving stobj fields of stobjs. Next, we introduce an ACL2 primitive, stobj-let, which provides the only way to read and write stobj components of stobjs. The final section provides precise documentation for stobj-let.

    See also ACL2 community book demos/modeling/nested-stobj-toy-isa.lisp for a worked example, which applies nested stobj structures to defining interpreters. A variety of small additional examples may be found in ACL2 community book misc/nested-stobj-tests.lisp. For further discussion, you are welcome to read the ``Essay on Nested Stobjs'', a long comment in ACL2 source file other-events.lisp. However, this documentation topic is intended to be self-contained for those familiar with stobjs.

    SECTION: Extension of defstobj to permit stobjs within stobjs

    Recall that the :type keyword of a defstobj field descriptor can be a ``type-indicator'' that specifies the type of the field as a type-spec (see type-spec). For example, the following specifies an integer field and a field that is an array of bytes.

      (defstobj st
        (int-field :type integer :initially 0)
        (ar-field :type (array unsigned-byte (10)) :initially 0))
    
    But the :type of a stobj field descriptor may instead be based on a stobj. For example, the following sequence of three events is legal. The first field descriptor of top, named sub1-field, illustrates one new kind of value for :type: the name of a previously-introduced stobj, which here is sub1. The second field descriptor of top, named sub2-ar-field, illustrates the other new kind of value for :type: an array whose elements are specified by the name of a previously-introduced stobj, in this case, the stobj sub2.
      (defstobj sub1 fld1)
      (defstobj sub2 fld2)
      (defstobj top
        (sub1-field :type sub1)
        (sub2-ar-field :type (array sub2 (10))))
    
    The :initially keyword is illegal for fields whose :type is a stobj or an array of stobjs. Each such initial value is provided by a corresponding call of the stobj creator for that stobj. In particular, in the case of an array of stobjs, the stobj creator is called once for each element of the array, so that the array elements are distinct. For example, each element of sub2-ar-field in the example above is initially provided by a separate call of create-sub2. Each initial element is thus unique, and in particular is distinct from the initial global value of the stobj. Similarly, the initial global stobj for sub1 is distinct from the initial sub1-field field of the global stobj for top, as these result from separate calls of create-sub1.

    When a stobj is used in a field of another stobj, we may refer to the former field as a ``child stobj'' and the latter stobj as a ``parent stobj''. So in the example above, sub1-field is a child stobj of type sub1 for parent stobj top, and sub2-ar-field is an array of child stobjs of type sub2 for parent stobj top. A child stobj has the same structural shape as the global stobj of its type, but as explained above, these are distinct structures. We follow standard terminology by saying ``isomorphic'' to indicate the same structural shape. So for example, (the value of) sub1-field is isomorphic to sub1, though these are distinct structures.

    SECTION: An aliasing problem

    Before introducing stobj-let below, we provide motivatation for this ACL2 primitive.

    Consider the following events.

      (defstobj child fld)
      (defstobj parent
        (fld2 :type child))
    
    Now suppose we could evaluate the following code, to be run immediately after admitting the two defstobj events above.
      (let* ((child (fld2 parent))
             (child (update-fld 3 child)))
        (mv child parent))
    
    Now logically there is no change to parent: parent is passed through unchanged. We can indeed prove that fact!
      (thm (equal (mv-nth 1
                          (let* ((child (fld2 parent))
                                 (child (update-fld 3 child)))
                            (mv child parent)))
                  parent))
    
    But recall that stobjs are updated with destructive assignments. That is, we really are updating (fld2 parent) to be the new value of child, whether this is explained logically or not. Thus, evaluation of the above let* form does in fact change the actual global stobj, parent.

    (Aside: Here is an explanation involving raw Lisp, for those who might find this useful. We escape to raw Lisp and execute the following; note that *the-live-parent* is the Lisp variable representing the global value of parent.

    (let ((parent *the-live-parent*))
      (let* ((child (fld2 parent))
             (child (update-fld 4 child)))
        (mv child parent)))
    
    Then, in raw Lisp, (fld (fld2 *the-live-parent*)) evaluates to 4, illustrating the destructive update. End of Aside.)

    Such aliasing can permit a change to a child stobj to cause a logically-inexplicable change to the parent stobj. Similarly, unfettered accessing of stobj fields can result in logically inexplicable changes to the child stobj when the parent stobj is changed. Thus, ACL2 disallows direct calls of stobj accessors and updaters for fields whose :type is a stobj or an array of stobjs. Instead, ACL2 provides stobj-let for reading and writing such fields in a sound manner.

    SECTION: Accessing and updating stobj fields of stobjs using stobj-let

    ACL2 provides a primitive, stobj-let, to access and update stobj fields of stobjs, in a manner that avoids the aliasing problem discussed above. In this section we provide an informal introduction to stobj-let, using examples, to be followed in the next section by precise documentation.

    We begin by returning to a slight variant of the example above.

      (defstobj child fld)
      (defstobj parent
        (fld2 :type child)
        fld3)
    
    The following form returns the result of updating the fld2 field of parent, which is a stobj isomorphic to child, to have a value of 3. Below we explain the terms ``bindings'', ``producer variables'', ``producer'', and ``consumer'', as well as how to understand this form.
      (stobj-let
       ((child (fld2 parent)))  ; bindings
       (child)                  ; producer variable(s)
       (update-fld 3 child)     ; producer
       (update-fld3 'a parent)) ; consumer
    
    The four lines under ``stobj-let'' just above can be understood as follows.
    o Bindings:
        Bind child to (fld2 parent).
    o Producer variable(s) and producer:
        Then bind the variable, child, to
        the value of the producer, (update-fld 3 child).
    o Implicit update of parent:
        Update fld2 of parent with the producer variable, child.
    o Consumer:
        Finally, return (update-fld3 'a parent).
    
    Thus, the logical expansion of the stobj-let form above is the following expression, though this is approximate (see below).
      (let ((child (fld2 parent))) ; bindings
        (let ((child (update-fld 3 child))) ; bind producer vars to producer
          (let ((parent (update-fld2 child parent))) ; implicit update of parent
            (update-fld3 'a parent))))
    
    The bindings always bind distinct names to child stobjs of a unique parent stobj, where the child stobj corresponds to the :type associated with the indicated accessor in the defstobj form for the parent stobj. Thus in this case, for the unique binding, variable child is bound to the stobj of `type' child for accessor fld2 of the parent stobj, parent. We refer to child from the bindings as a ``stobj-let-bound variable''. Note also that the ``implicit extra step'' mentioned above is generated by macroexpansion of stobj-let; it logically updates the parent with new child values, just before calling the consumer. Implementation note: Destructive updating in raw Lisp lets us omit this implicit extra step.

    The form above is equivalent to the form displayed just below, which differs only in specifying an explicit stobj updater corresponding to the stobj accessor, fld2. Here we show the default updater name, whose name has "UPDATE-" prepended to the name of the accessor. But if the :RENAMING field of the defstobj event specified a different updater name corresponding to fld2, then that would need to be included where we have added update-fld2 below.

      (stobj-let
       ((child (fld2 parent) update-fld2)) ; bindings, including updater(s)
       (child)                  ; producer variables
       (update-fld 3 child)     ; producer
       (update-fld3 'a parent)) ; consumer
    

    You can experiment using :trans1 to see the single-step macroexpansion of a stobj-let form in the logic. For example, here is how that works for a stobj-let form that binds three fields and updates two of them. Notice that because more than one field is updated, an mv-let form is generated to bind the two fields to their values returned by the producer, rather than a let form as previously generated. First, let's introduce some events.

    (defstobj child1 child1-fld)
    (defstobj child2 child2-fld)
    (defstobj child3 child3-fld)
    (defstobj mom
      (fld1 :type child1)
      (fld2 :type child2)
      (fld3 :type child3))
    ; Silly stub:
    (defun update-last-op (op mom)
      (declare (xargs :stobjs mom))
      (declare (ignore op))
      mom)
    (defun new-mom (mom)
      (declare (xargs :stobjs mom))
      (stobj-let
       ((child1 (fld1 mom))
        (child2 (fld2 mom))
        (child3 (fld3 mom)))
       (child1 child3)
       (let* ((child1 (update-child1-fld 'one child1))
              (child3 (update-child3-fld 'three child3)))
         (mv child1 child3))
       (update-last-op 'my-compute mom)))
    
    Now let's look at the single-step macroexpansion of the above stobj-let form.
    ACL2 !>:trans1 (stobj-let
                    ((child1 (fld1 mom))
                     (child2 (fld2 mom))
                     (child3 (fld3 mom)))
                    (child1 child3)
                    (let* ((child1 (update-child1-fld 'one child1))
                           (child3 (update-child3-fld 'three child3)))
                      (mv child1 child3))
                    (update-last-op 'my-compute mom))
     (PROGN$
      (LET
       ((CHILD1 (FLD1 MOM))
        (CHILD2 (FLD2 MOM))
        (CHILD3 (FLD3 MOM)))
       (DECLARE (IGNORABLE CHILD1 CHILD2 CHILD3))
       (MV-LET
          (CHILD1 CHILD3)
          (CHECK-VARS-NOT-FREE (MOM)
                               (LET* ((CHILD1 (UPDATE-CHILD1-FLD 'ONE CHILD1))
                                      (CHILD3 (UPDATE-CHILD3-FLD 'THREE CHILD3)))
                                     (MV CHILD1 CHILD3)))
          (LET* ((MOM (UPDATE-FLD1 CHILD1 MOM))
                 (MOM (UPDATE-FLD3 CHILD3 MOM)))
                (CHECK-VARS-NOT-FREE (CHILD1 CHILD2 CHILD3)
                                     (UPDATE-LAST-OP 'MY-COMPUTE MOM))))))
    ACL2 !>
    

    If you try to evaluate a stobj-let form directly in the top-level loop, rather than from within a function body, you will get an error. The example above illustrates how stobj-let may be used in function bodies; here is another example, presented using an edited log.

      ACL2 !>(defstobj child fld)
    
      Summary
      Form:  ( DEFSTOBJ CHILD ...)
      Rules: NIL
      Time:  0.02 seconds (prove: 0.00, print: 0.00, other: 0.02)
       CHILD
      ACL2 !>(defstobj parent
               (fld2 :type child)
               fld3)
    
      Summary
      Form:  ( DEFSTOBJ PARENT ...)
      Rules: NIL
      Time:  0.02 seconds (prove: 0.00, print: 0.00, other: 0.02)
       PARENT
      ACL2 !>(defun f (parent)
               (declare (xargs :stobjs parent))
               (stobj-let
                ((child (fld2 parent)))   ; bindings
                (child)                   ; producer variables
                (update-fld 3 child)      ; producer
                (update-fld3 'a parent))) ; consumer
      [[output omitted]]
       F
      ACL2 !>(f parent)
      <parent>
      ACL2 !>(defun check-f (parent)
               ; returns the value of the field of the child stobj
               (declare (xargs :stobjs parent))
               (stobj-let
                ((child (fld2 parent))) ; bindings
                (val)                   ; producer variables
                (fld child)             ; producer
                val))                   ; consumer
      [[output omitted]]
       CHECK-F
      ACL2 !>(check-f parent)
      3
      ACL2 !>
    

    Notice that the second function defined above, check-f, uses a stobj-let form that returns an ordinary value: it reads a value from a child stobj, but does not write to the child stobj, as indicated by the lack of a child stobj among the producer variables. So for that stobj-let form, there is no implicit extra step.

    We labeled a stobj-let expansion above as ``approximate'' for two reasons, which we give here informally. (Now you know how to apply :trans1 to that stobj-let call to see the precise expansion.) First, stobj-let declares the stobj-let-bound variables to be ignorable for the top let bindings. Second, and more importantly, stobj-let imposes the following restrictions on the producer and consumer, to avoid the aliasing problem: it disallows references to the parent stobj in the producer and it also disallows references to any bound stobj (i.e., bound in the bindings) in the consumer.

    We conclude this section with examples based on a slight variation of the nested stobj example from the first section above. These events can also be found in ACL2 community book misc/nested-stobj-tests.lisp, immediately under the following comment:

    ; As promised in :doc stobj-let, we begin with an example from that :doc.
    
    Note that some lemmas were needed in order to complete the guard proof for the function update-top, which may be found in the above file; they are omitted below.

    First we introduce three stobjs.

      (defstobj kid1 fld1)
      (defstobj kid2 fld2)
      (defstobj mom
        (kid1-field :type kid1)
        (kid2-ar-field :type (array kid2 (5)))
        last-op)
    
    The next function takes a given index and a mom stobj, and swaps the value stored in the stobj in mom's kid2-ar-field array at that index with the value stored in the stobj in mom's kid1-field field.
      (defun mom-swap-fields (index mom)
        (declare (xargs :stobjs mom
                        :guard (and (natp index)
                                    (< index (kid2-ar-field-length mom)))))
        (stobj-let
         ((kid1 (kid1-field mom))
          (kid2 (kid2-ar-fieldi index mom)))
         (kid1 kid2)
         (let* ((val1 (fld1 kid1))
                (val2 (fld2 kid2))
                (kid1 (update-fld1 val2 kid1))
                (kid2 (update-fld2 val1 kid2)))
           (mv kid1 kid2))
         (update-last-op 'swap mom)))
    
    Function mom.kid1-fld1 stores a given value in the given mom's kid1-fld1 field.
      (defun mom.kid1-fld1 (val mom)
        (declare (xargs :stobjs mom))
        (stobj-let
         ((kid1 (kid1-field mom)))
         (kid1)
         (update-fld1 val kid1)
         (update-last-op val mom)))
    
    We next combine the two functions above, according to an op argument, as indicated by the following definition.
      (defun update-mom (op mom)
        (declare (xargs :stobjs mom))
        (cond ((and (consp op)
                    (eq (car op) 'swap)
                    (natp (cdr op))
                    (< (cdr op) (kid2-ar-field-length mom)))
               (mom-swap-fields (cdr op) mom))
              (t (mom.kid1-fld1 op mom))))
    
    The following checker function uses a stobj-let form like the ones above, a major difference being that the producer variable is not a stobj, since it does not modify the input stobj, mom.
      (defun check-update-mom (index val1 val2 last-op mom)
          (declare (xargs :stobjs mom
                          :mode :program
                          :guard
                          (or (null index)
                              (and (natp index)
                                   (< index (kid2-ar-field-length mom))))))
          (and (equal (last-op mom) last-op)
               (stobj-let
                ((kid1 (kid1-field mom))
                 (kid2 (kid2-ar-fieldi index mom)))
                (val) ; producer variables
                (and (equal val1 (fld1 kid1))
                     (equal val2 (fld2 kid2)))
                val)))
    
    Now let us run our update function to populate some fields within the mom stobj.
      (let* ((mom ; set mom to (3 (x0 x1 x2 x3 x4))
               (update-mom 3 mom))
              (mom ; set mom to (x1 (x0 3 x2 x3 x4))
               (update-mom '(swap . 1) mom))
              (mom ; set mom to (7 (x0 3 x2 x3 x4))
               (update-mom 7 mom))
              (mom ; set mom to (x0 (7 3 x2 x3 x4))
               (update-mom '(swap . 0) mom))
              (mom ; set mom to (5 (7 3 x2 x3 x4))
               (update-mom 5 mom))
              (mom ; set mom to (7 (5 3 x2 x3 x4))
               (update-mom '(swap . 0) mom)))
         mom)
    
    Are the above values of 7, 5, and 3 as expected, with a last operation being a swap? Yes!
      ACL2 !>(and (check-update-mom 0 7 5 'swap mom)
                  (check-update-mom 1 7 3 'swap mom))
      T
      ACL2 !>
    
    Notice that above, we never tried to access two different entries of the array. This can be done, but we need to bind two different stobjs to those fields. Fortunately, congruent stobjs make this possible; see defstobj, in particular the discussion of congruent stobjs. Since we want to bind two stobjs to values in the array that are isomorphic to the stobj kid2, we introduce a stobj congruent to kid2.
      (defstobj kid2a fld2a :congruent-to kid2)
    
    Then we can define our swapping function as follows. The guard proof obligation includes the requirement that the two indices be distinct, again to avoid an aliasing problem.
      (defun mom-swap-indices (i1 i2 mom)
        (declare (xargs :stobjs mom
                        :guard (and (natp i1)
                                    (< i1 (kid2-ar-field-length mom))
                                    (natp i2)
                                    (< i2 (kid2-ar-field-length mom))
                                    (not (equal i1 i2)))))
        (stobj-let
         ((kid2 (kid2-ar-fieldi i1 mom))
          (kid2a (kid2-ar-fieldi i2 mom)))
         (kid2 kid2a)
         (let* ((val2 (fld2 kid2))
                (val2a (fld2 kid2a))
                (kid2 (update-fld2 val2a kid2))
                (kid2a (update-fld2 val2 kid2a)))
           (mv kid2 kid2a))
         mom))
    
    The aforementioned community book, misc/nested-stobj-tests.lisp, contains a corresponding checker immediately following this definition.

    SECTION: Precise documentation for stobj-let

    General Form:
    (stobj-let
     BINDINGS
     PRODUCER-VARIABLES
     PRODUCER
     CONSUMER)
    
    where PRODUCER-VARIABLES is a non-empty true list of legal variable names without duplicates, PRODUCER and CONSUMER are expressions, and BINDINGS is a list subject to the following requirements.

    BINDINGS is a non-empty true list of tuples, each of which has the form (VAR ACCESSOR) or (VAR ACCESSOR UPDATER). There is a stobj name, ST, previously introduced by defstobj (not defabsstobj), such that each accessor is of the form (ACC ST) or (ACCi I ST), with the same stobj name (ST) for each binding. In the case (ACC ST), ACC is the accessor for a non-array field of ST. In the case (ACCi I ST), ACCi is the accessor for an array field of ST, and I is either a variable, a natural number, a list (quote N) where N is a natural number, or a symbol introduced by defconst. If UPDATER is supplied, then it is a symbol that is the name of the stobj updater for the field of ST accessed by ACCESSOR. If UPDATER is not supplied, then for the discussion below we consider it to be, implicitly, the symbol in the same package as the function symbol of ACCESSOR (i.e., ACC or ACCi), obtained by prepending the string "UPDATE-" to the symbol-name of that function symbol. Finally, ACCESSOR has a signature specifying a return value that is either ST or is stobj that is congruent to ST.

    If the conditions above are met, then the General Form expands to the one of the following expressions, depending on whether the list PRODUCER-VARIABLES has one member or more than one member, respectively. (But see below for extra code that may be inserted if there are stobj array accesses in BINDINGS.) Here we write STOBJ-LET-BOUND-VARS for the list of variables VAR discussed above, i.e., for (strip-cars BINDINGS). And, we write UPDATES for the result of mapping through PRODUCER-VARIABLES and, for each variable VAR that has a binding (VAR ACCESSOR UPDATER) in BINDINGS (where UPDATER may be implicit, as discussed above), collect into UPDATES the tuple (ST (UPDATER VAR ST)).

    For PRODUCER-VARIABLES = (PRODUCER-VAR):

      (let BINDINGS
        (declare (ignorable . STOBJ-LET-BOUND-VARIABLES))
        (let ((PRODUCER-VAR PRODUCER))
          (let* UPDATES
            CONSUMER)))
    

    Otherwise:

      (let BINDINGS
        (declare (ignorable . STOBJ-LET-BOUND-VARIABLES))
        (mv-let PRODUCER-VARS
                PRODUCER
                (let* UPDATES
                  CONSUMER)))
    
    Moreover, ACL2 places restrictions on the resulting expression: ST must not occur free in PRODUCER, and every variable in STOBJ-LET-BOUND-VARIABLES must not occur free in CONSUMER.

    Stobj-let forms can be evaluated using ordinary objects in theorem contexts, much as any form. They can also, of course, appear in function bodis. However, a stobj-let form cannot be evaluated directly in the top-level loop or other top-level contexts for execution (such as during make-event expansion).

    Finally, let FORM denote the form displayed above (either case). We explain how FORM is actually replaced by an expression of the form (PROGN$ ... FORM). This expression generates an extra guard proof obligation, which guarantees that no aliasing occurs from binding two stobj-let-bound variables to the same array access. So fix a stobj array accessor ACCi for which some stobj is bound to (ACCi I ST) in BINDINGS; we define an expression ACCi-CHECK as follows. Collect up all such index expressions I, where if I is of the form (quote N) then replace I by N. If the resulting list of index expressions for ACCi consists solely of distinct numbers, or if it is of length 1, then no extra check is generated for ACCi. Otherwise, let ACCi-CHECK be the form (chk-no-duplicatesp (list I1 ... Ik)), where I1, ..., Ik are the index expressions for ACCi. Note: chk-no-duplicatesp is a function that returns nil, but has a guard that its argument is an eqlable-listp that satisfies no-duplicatesp. Finally, FORM is replaced by (PROGN$ CHK1 ... CHKn FORM), where the CHKm range over all of the above ACCi-CHECK.




    acl2-sources/doc/HTML/NEVER-MEMOIZE.html0000664002132200015000000000217212222333520016730 0ustar kaufmannacl2 NEVER-MEMOIZE.html -- ACL2 Version 6.3

    NEVER-MEMOIZE

    Mark a function as unsafe to memoize.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Logically, this function just returns nil. But execution of (never-memoize fn) records that fn must never be memoized, so that any attempt to memoize fn will fail.

    Any function can be marked as unsafe to memoize; in fact, fn need not even be defined at the time it is marked.

    This is useful for prohibiting the memoization of functions that are known to involve destructive functions like nreverse.




    acl2-sources/doc/HTML/NFIX.html0000664002132200015000000000165212222333524015520 0ustar kaufmannacl2 NFIX.html -- ACL2 Version 6.3

    NFIX

    coerce to a natural number
    Major Section:  ACL2-BUILT-INS
    

    Nfix simply returns any natural number argument unchanged, returning 0 on an argument that is not a natural number. Also see ifix, see rfix, see realfix, and see fix for analogous functions that coerce to an integer, a rational number, a real, and a number, respectively.

    Nfix has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NIL-GOAL.html0000664002132200015000000000536712222333521016122 0ustar kaufmannacl2 NIL-GOAL.html -- ACL2 Version 6.3

    NIL-GOAL

    how to proceed when the prover generates a goal of nil
    Major Section:  MISCELLANEOUS
    

    At the end of a failed proof, one typically sees so-called ``key checkpoints'' (see set-gag-mode). These may be annotated as follows.

    [NOTE: A goal of NIL was generated.  See :DOC nil-goal.]
    
    This documentation topic gives some ideas about how to think about the situation described by that message: some goal has reduced to nil.

    Suppose then that you see the above NOTE. If you look back at the proof log, even with gag-mode enabled, you will see a message saying that a goal of NIL ``has been generated''. This may indicate that the original goal is not a theorem, since most of the prover's activity is to replace a goal by an equivalent conjunction of its child subgoals. However, if some ancestor of the nil goal has undergone a process other than simplification or destructor elimination -- fertilization (heuristic use of equalities), generalization, or elimination of irrelevance -- then it is quite possible that the prover got to the nil goal by replacing a goal by a stronger (and perhaps false) conjunction of child subgoals.

    At present, if you are using gag-mode (the default) then you will need to issue the command :pso (``Print Saved Output'') if you want to see whether the situation above has occurred. However, that might not be necessary. A good rule of thumb is that if the nil goal is under more level of induction (e.g., with a prefix ``*i.j'' such as ``Subgoal *1.1/2.2''), then there is some likelihood that the situation above did indeed occur, and you can spend your time and energy looking at the key checkpoints printed in the summary to see if they suggest useful rewrite rules to prove. On the other hand, if the nil goal is at the top level (e.g. with a name not starting with ``*'', such as ``Subgoal 3.2''), then the original conjecture is probably not a theorem. If you do not quickly see why that is the case, then you might find it useful to issue the command :pso to see which case reduced to nil, in order to get insight about how the theorem might be falsified.




    acl2-sources/doc/HTML/NINTH.html0000664002132200015000000000066212222333524015634 0ustar kaufmannacl2 NINTH.html -- ACL2 Version 6.3

    NINTH

    ninth member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/NO-DUPLICATESP-EQ.html0000664002132200015000000000066712222333524017353 0ustar kaufmannacl2 NO-DUPLICATESP-EQ.html -- ACL2 Version 6.3

    NO-DUPLICATESP-EQ

    See no-duplicatesp.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/NO-DUPLICATESP-EQUAL.html0000664002132200015000000000067512222333524017714 0ustar kaufmannacl2 NO-DUPLICATESP-EQUAL.html -- ACL2 Version 6.3

    NO-DUPLICATESP-EQUAL

    See no-duplicatesp.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/NO-DUPLICATESP.html0000664002132200015000000000373012222333524017042 0ustar kaufmannacl2 NO-DUPLICATESP.html -- ACL2 Version 6.3

    NO-DUPLICATESP

    check for duplicates in a list
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (no-duplicatesp x)
    (no-duplicatesp x :test 'eql)   ; same as above (eql as equality test)
    (no-duplicatesp x :test 'eq)    ; same, but eq is equality test
    (no-duplicatesp x :test 'equal) ; same, but equal is equality test
    

    (no-duplicatesp lst) is true if and only if no member of lst occurs twice in lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing elements of lst.

    The guard for a call of no-duplicatesp depends on the test. In all cases, the argument must satisfy true-listp. If the test is eql, then the argument must satisfy eqlable-listp. If the test is eq, then the argument must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between no-duplicatesp and its variants:

    (no-duplicatesp-eq x lst) is equivalent to (no-duplicatesp x lst :test 'eq);

    (no-duplicatesp-equal x lst) is equivalent to (no-duplicatesp x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function no-duplicatesp-equal.




    acl2-sources/doc/HTML/NO-THANKS.html0000664002132200015000000000067212222333521016254 0ustar kaufmannacl2 NO-THANKS.html -- ACL2 Version 6.3

    NO-THANKS

    hints keyword :NO-THANKS
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/NON-EXEC.html0000664002132200015000000000651312222333524016131 0ustar kaufmannacl2 NON-EXEC.html -- ACL2 Version 6.3

    NON-EXEC

    mark code as non-executable
    Major Section:  ACL2-BUILT-INS
    

    Non-exec is a macro such that logically, (non-exec x) is equal to x. However, the argument to a call of non-exec need not obey the usual syntactic restrictions for executable code, and indeed, evaluation of a call of non-exec will result in an error. Moreover, for any form occurring in the body of a function (see defun) that is a call of non-exec, no guard proof obligations are generated for that form.

    The following example, although rather contrived, illustrates the use of non-exec. One can imagine a less contrived example that efficiently computes return values for a small number of fixed inputs and, for other inputs, returns something logically ``consistent'' with those return values.

    (defun double (x)
      (case x
        (1 2)
        (2 4)
        (3 6)
        (otherwise (non-exec (* 2 x)))))
    
    We can prove that double is compliant with Common Lisp (see guard) and that it always computes (* 2 x).
    (verify-guards double)
    (thm (equal (double x) (* 2 x)))
    
    We can evaluate double on the specified arguments. But a call of non-exec results in an error message that reports the form that was supplied to non-exec.
    ACL2 !>(double 3)
    6
    ACL2 !>(double 10)
    
    
    ACL2 Error in TOP-LEVEL:  ACL2 has been instructed to cause an error
    because of an attempt to evaluate the following form (see :DOC non-
    exec):
    
      (* 2 X).
    
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.
    
    ACL2 !>
    

    During proofs, the error is silent; it is ``caught'' by the proof mechanism and generally results in the introduction of a call of hide during a proof.

    Also see defun-nx for a utility that makes every call of a function non-executable, rather than a specified form. The following examples contrast non-exec with defun-nx, in particular illustratating the role of non-exec in avoiding guard proof obligations.

    ; Guard verification fails:
    (defun-nx f1 (x)
      (declare (xargs :guard t))
      (car x))
    
    ; Guard verification succeeds after changing the guard above:
    (defun-nx f1 (x)
      (declare (xargs :guard (consp x)))
      (car x))
    
    ; Guard verification succeeds:
    (defun f2 (x)
      (declare (xargs :guard t))
      (non-exec (car x)))
    
    ; Evaluating (g1) prints "Hello" before signaling an error.
    (defun g1 ()
      (f1 (cw "Hello")))
    
    ; Evaluating (g2) does not print before signaling an error.
    (defun g2 ()
      (non-exec (cw "Hello")))
    
    ; Evaluating (h1) gives a guard violation for taking reciprocal of 0.
    (defun h1 ()
      (f1 (/ 1 0)))
    
    ; Evaluating (h2) does not take a reciprocal, hence there is no guard
    ; violation for that; we just get the error expected from using non-exec.
    (defun h2 ()
      (non-exec (/ 0)))
    




    acl2-sources/doc/HTML/NON-EXECUTABLE.html0000664002132200015000000000071112222333521017015 0ustar kaufmannacl2 NON-EXECUTABLE.html -- ACL2 Version 6.3

    NON-EXECUTABLE

    xargs keyword :NON-EXECUTABLE
    Major Section:  MISCELLANEOUS
    

    See xargs.




    acl2-sources/doc/HTML/NON-LINEAR-ARITHMETIC.html0000664002132200015000000002154712222333521020007 0ustar kaufmannacl2 NON-LINEAR-ARITHMETIC.html -- ACL2 Version 6.3

    NON-LINEAR-ARITHMETIC

    Non-linear Arithmetic
    Major Section:  MISCELLANEOUS
    

    This documentation topic is divided into two parts. We first discuss the practical aspect of how to use the non-linear arithmetic extension to ACL2, and then the theory behind it. We assume that the reader is familiar with the material in linear-arithmetic and that on :linear rules.

    We begin our discussion of how to use non-linear arithmetic with a simple example. Assume that we wish to prove:

    (thm
     (implies (and (rationalp x)
                   (rationalp y)
                   (rationalp z)
                   (< 0 y)
                   (< x (* y z)))
              (< (floor x y) z)))
    

    Note that (floor x y) <= (/ x y). Note also that if we divide both sides of x < (* y z) by y, since 0 < y, we obtain (/ x y) < z. By chaining these two inequalities together, we get the inequality we desired to prove.

    We now proceed with our example session:

    (skip-proofs
     (progn
    
    ; Since the truth of this theorem depends on the linear properties
    ; of floor, we will need the linear lemma:
    
       (defthm floor-bounds-1
           (implies (and (rationalp x)
                         (rationalp y))
                    (and (< (+ (/ x y) -1)
                            (floor x y))
                         (<= (floor x y)
                             (/ x y))))
           :rule-classes ((:linear :trigger-terms ((floor x y)))))
    
    ; We now disable floor, so that the linear lemma will be used.
    
       (in-theory (disable floor))
    
    ; We create five rewrite rules which we will use during non-linear
    ; arithmetic.  The necessity for these is due to one of the differences in
    ; ACL2's behaviour when non-linear arithmetic is turned on.  Although
    ; the conclusions of linear lemmas have always been rewritten before
    ; they are used, now, when non-linear arithmetic is turned on, the
    ; conclusions are rewritten under a different theory than under ``normal''
    ; rewriting.  This theory is also used in other, similar, circumstances
    ; described below.
    
       (defthm |arith (* -1 x)|
           (equal (* -1 x)
                  (- x)))
    
       (defthm |arith (* 1 x)|
           (equal (* 1 x)
                  (fix x)))
    
       (defthm |arith (* x (/ x) y)|
           (equal (* x (/ x) y)
                  (if (equal (fix x) 0)
                      0
                      (fix y))))
    
       (defthm |arith (* y x)|
           (equal (* y x)
                  (* x y)))
    
       (defthm |arith (fix x)|
           (implies (acl2-numberp x)
                    (equal (fix x)
                           x))))
     )  ; End skip-proofs.
    
    ; We disable the above rewrite rules from normal use.
    
    (in-theory (disable |arith (* -1 x)|
                        |arith (* 1 x)|
                        |arith (* x (/ x) y)|
                        |arith (* y x)|
                        |arith (fix x)|))
    
    ; We create an arithmetic-theory.  Note that we must give a quoted
    ; constant for the theory -- none of the normal theory-functions
    ; are applicable to in-arithmetic-theory.
    
    (in-arithmetic-theory '(|arith (* -1 x)|
                            |arith (* 1 x)|
                            |arith (* x (/ x) y)|
                            |arith (* y x)|
                            |arith (fix x)|))
    
    ; We turn non-linear arithmetic on.
    
    (set-non-linearp t)
    
    ; We can now go ahead and prove our theorem.
    
    (thm
     (implies (and (rationalp x)
                   (rationalp y)
                   (rationalp z)
                   (< 0 y)
                   (< x (* y z)))
              (< (floor x y) z)))
    

    The above example illustrates the two practical requirements for using non-linear arithmetic in ACL2. First, one must set up an arithmetic-theory. Usually, one would not set up an arithmetic-theory on one's own but would instead load a library book or books which do so. Second, one must turn the non-linear arithmetic extension on. This one must do explicitly -- no book can do this for you.

    For a brief discussion of why this is so, even though (set-non-linearp t) is an embeddable event, see acl2-defaults-table (in particular, the final paragraph). (Note that (set-non-linearp t) modifies the acl2-defaults-table.) Also see set-non-linearp, see embedded-event-form, and see events.

    You can also enable non-linear arithmetic with the hint :nonlinearp t. See hints. We, in fact, recommend the use of a hint which will enable nonlinear arithmetic only when the goal has stabilized under rewriting. Using default-hints can make this easier.

    (defun nonlinearp-default-hint (stable-under-simplificationp hist pspv)
      (cond (stable-under-simplificationp
             (if (not (access rewrite-constant
                              (access prove-spec-var pspv :rewrite-constant)
                              :nonlinearp))
                 '(:computed-hint-replacement t :nonlinearp t)
               nil))
            ((access rewrite-constant
                     (access prove-spec-var pspv :rewrite-constant)
                     :nonlinearp)
             (if (not (equal (caar hist) 'SETTLED-DOWN-CLAUSE))
                 '(:computed-hint-replacement t :nonlinearp nil)
               nil))
            (t nil)))
    
    (set-default-hints '((nonlinearp-default-hint stable-under-simplificationp
                                                  hist pspv)))
    

    This has proven to be a helpful strategy which allows faster proof times.

    We now proceed to briefly describe the theory behind the non-linear extension to ACL2. In linear-arithmetic it was stated that, ``[L]inear polynomial inequalities can be combined by cross-multiplication and addition to permit the deduction of a third inequality....'' That is, if

    0 < poly1,
    0 < poly2,
    
    and c and d are positive rational constants, then
    0 < c*poly1 + d*poly2.
    

    Similarly, given the above,

    0 < poly1*poly2.
    

    In the linear arithmetic case, we are taking advantage of the facts that multiplication by a positive rational constant does not change the sign of a polynomial and that the sum of two positive polynomials is itself positive. In the non-linear arithmetic case, we are using the fact that the product of two positive polynomials is itself positive.

    For example, suppose we have the three assumptions:

    p1:  3*x*y + 7*a < 4
    p2:            3 < 2*x  or p2': 0 < -3 + 2*x
    p3:            1 < y    or p3': 0 < -1 + y,
    
    and we wish to prove that a < 0. As described elsewhere (see linear-arithmetic), we proceed by assuming the negation of our goal:
    p4:            0 <= a,
    
    and looking for a contradiction.

    There are no cancellations which can be performed by linear arithmetic in the above situation. (Recall that two polynomials are cancelled against each other only when they have the same largest unknown.) However, p1 has a product as its largest unknown, and for each of the factors of that product there is a poly that has that factor as a largest unknown. When non-linear arithmetic is enabled, ACL2 will therefore multiply p1' and p2' obtaining

    p5:            0 < 3 + -2*x + -3*y + 2*x*y.
    
    The addition of this polynomial will allow cancelation to continue and, in this case, we will prove our goal. Thus, just as ACL2 adds two polynomials together when they have the same largest unknown of opposite signs in order to create a new ``smaller'' polynomial; so ACL2 multiplies polynomials together when the product of their largest unknowns is itself the largest unknown of another polynomial. As the use of :linear lemmas to further seed the arithmetic database may allow cancellation to proceed, so may the use of non-linear arithmetic.

    This multiplication of polynomials is the motivation for an arithmetic-theory distinct from than the normal one. Because this may be done so often, and because the individual factors have presumably already been rewritten, it is important that this be done in an efficient way. The use of a small, specialized, theory helps avoid the repeated application of rewrite rules to already stabilized terms.




    acl2-sources/doc/HTML/NONLINEARP.html0000664002132200015000000000067512222333521016422 0ustar kaufmannacl2 NONLINEARP.html -- ACL2 Version 6.3

    NONLINEARP

    hints keyword :NONLINEARP
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/NONNEGATIVE-INTEGER-QUOTIENT.html0000664002132200015000000000236212222333524021071 0ustar kaufmannacl2 NONNEGATIVE-INTEGER-QUOTIENT.html -- ACL2 Version 6.3

    NONNEGATIVE-INTEGER-QUOTIENT

    natural number division function
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    (nonnegative-integer-quotient 14 3) ; equals 4
    (nonnegative-integer-quotient 15 3) ; equals 5
    
    (nonnegative-integer-quotient i j) returns the integer quotient of the integers i and (non-zero) j, i.e., the largest k such that (* j k) is less than or equal to i. Also see floor, see ceiling and see truncate, which are derived from this function and apply to rational numbers.

    The guard of (nonnegative-integer-quotient i j) requires that i is a nonnegative integer and j is a positive integer.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NORMALIZE.html0000664002132200015000000000067212222333521016312 0ustar kaufmannacl2 NORMALIZE.html -- ACL2 Version 6.3

    NORMALIZE

    xargs keyword :NORMALIZE
    Major Section:  MISCELLANEOUS
    

    See xargs.




    acl2-sources/doc/HTML/NORMED.html0000664002132200015000000000670512222333520015740 0ustar kaufmannacl2 NORMED.html -- ACL2 Version 6.3

    NORMED

    Normed objects are ACL2 Objects that are "canonical" or "unique" in a certain sense.
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    In Common Lisp, we can tell whether an ACL2 object is normed or not, but there is no way for an ordinary ACL2 function to see whether an object is normed. Hence, whether or not an object is normed is an implementation-level concept.

    The fundamental property of normed objects is that if A and B are both normed, then (equal A B) holds if and only if (eql A B) holds. For strings and conses, (eql A B) holds only when (eq A B), so any normed conses or strings are equal precisely when they are eq. The potential benefits of having normed objects include: constant-time equality comparisons, reduced memory usage, fast association lists, and function memoization.

    Note that in our implementation, all symbols, characters, and numbers are automatically normed, and whenever a cons is normed its car and cdr must also be normed.

    The Meaning of Normed in Common Lisp.

    Recall that the ACL2 Objects are certain "good" Common Lisp symbols, characters, strings, and numbers, and the conses which can be recursively formed from these good atoms.

    Not all properties of these objects are visible in the ACL2 logic. An example of an invisible property is the pointer identity of an object. For example, suppose we write the following.

     (defconst *x* (cons 1 2))
     (defconst *y* (cons 1 2))
    

    In Common Lisp, cons is guaranteed to provide a new pair that is distinct from any previously created pair, so we know that *x* and *y* will be distinct objects and we will be able to tell them apart from one another using eq. But this difference is not visible in the ACL2 logic, and no ACL2 function can tell *x* apart from *y*.

    The normed-ness of an object is a similarly invisible property. In Common Lisp, invisible to ACL2, we maintain a data structure called a "Hons Space" that records which objects are normed. So, being normed is not an intrinsic property of an object, but instead is determined by whether the object is mentioned in this Hons Space.

    Notes about Garbage Collection.

    The Hons Space includes tables with pointers to every normed cons and string. One consequence of this is that normed objects cannot be reclaimed by Lisp's ordinary garbage collector, even after they have become unreachable from the perspective of an ACL2 program.

    For CCL users only, hons-wash is a special kind of garbage collection that allows normed conses to be reclaimed. For other Lisps, the only option is to occasionally, manually clear out these Hons Space tables with hons-clear.




    acl2-sources/doc/HTML/NOT.html0000664002132200015000000000130712222333524015411 0ustar kaufmannacl2 NOT.html -- ACL2 Version 6.3

    NOT

    logical negation
    Major Section:  ACL2-BUILT-INS
    

    Not is the ACL2 negation function. The negation of nil is t and the negation of anything else is nil.

    Not is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NOTE-2-0.html0000664002132200015000000000303512222333526016014 0ustar kaufmannacl2 NOTE-2-0.html -- ACL2 Version 6.3

    NOTE-2-0

    ACL2 Version 2.0 (July, 1997) Notes
    Major Section:  RELEASE-NOTES
    

    This is the first version of ACL2 released under the copyright of the University of Texas (UT). Future releases of ACL2 will be made from UT rather than Computational Logic, Inc. (CLI). Version 2.0 is just Version 1.9 as released by CLI, with a few bugs fixed.

    A bug causing an infinite loop was fixed in functional instantiation. The bug manifested itself when two conditions occurred simultaneously: First, the functional substitution replaces a function symbol, e.g., FOO, with a LAMBDA expression containing a free variable (a variable not among in the LAMBDA formals). And, second, in one of the constraints being instantiated there is a call of the function symbol FOO within the scope of another LAMBDA expression. Unless you used such a functional substitution, this bug fix will not affect you.

    Less important notes:

    The implementation of PRINC$ was changed so that it was no longer sensitive to the external setting of *print-base* and other Common Lisp special variables.

    Typographical errors were fixed in the documentation.




    acl2-sources/doc/HTML/NOTE-2-1.html0000664002132200015000000000134112222333526016013 0ustar kaufmannacl2 NOTE-2-1.html -- ACL2 Version 6.3

    NOTE-2-1

    ACL2 Version 2.1 (December, 1997) Notes
    Major Section:  RELEASE-NOTES
    

    The identity function case-split has been added. It is similar to force but causes an immediate split of the top-level goal on whether the indicated hypothesis is true.

    Less important notes:

    Minor bugs in the documentation were fixed.




    acl2-sources/doc/HTML/NOTE-2-2.html0000664002132200015000000000641612222333526016024 0ustar kaufmannacl2 NOTE-2-2.html -- ACL2 Version 6.3

    NOTE-2-2

    ACL2 Version 2.2 (August, 1998) Notes
    Major Section:  RELEASE-NOTES
    

    Important changes:

    A bug was fixed in the compile command, :comp. The compiled code produced by :comp in previous versions could be wildly incorrect because of a confusion between the printer and the reader regarding what was the current Lisp *package*. This bug could manifest itself only if you used the :comp command to compile previously uncompiled functions while the current package was different from "ACL2". What happened in that situation depended upon what symbols were imported into your current package. The most likely behavior is that the compiler would break or complain or the resulting compiled code would call functions that did not exist.

    There have been no other important changes to the code.

    However, this release contains some useful new books, notably those on the books subdirectories cli-misc and ihs. Both have README files. The ihs books provide support for integer hardware specifications. These books were crucial to Bishop Brock's successful modeling of the Motorola CAP. We thank Bishop for producing them and we thank all those who worked so hard to get these books released. We highly recommend the ihs books to those modeling ALUs and other arithmetic components of microprocessors or programming languages.

    In previous versions of ACL2, the arithmetic books, found on books/arithmetic/, included the addition of several unproved axioms stating properties of the rationals that we believed could be derived from our ``official'' axioms but which we had not mechanically proved. The axioms were found in the book rationals-with-axioms.lisp, which was then used in the uppermost arithmetic books top.lisp and top-with-meta.lisp. John Cowles has now provided us with ACL2 proofs of those ``axioms'' and so in this release you will find both rationals-with-axioms.lisp and rationals-with-axioms-proved.lisp. The former is provided for compatibility's sake. The latter is identical but contains defthms where the former contains defaxioms. The top-most books have been rebuilt using ``-axioms-proved'' book. Thanks John.

    Less important notes:

    Bishop Brock found a bug in translated-acl2-unwind-protectp4. Jun Sawada reported a bug in linear arithmetic that caused us not to prove certain trivial theorems concluding with (not (equal i j)). We have fixed both.

    We now prohibit definitions that call certain event commands such as DEFTHM and TABLE because our Common Lisp implementations of them differ from their ACL2 meanings (so that compiled books can be loaded correctly and efficiently).

    Minor bugs in the documentation were fixed.




    acl2-sources/doc/HTML/NOTE-2-3.html0000664002132200015000000000533412222333526016023 0ustar kaufmannacl2 NOTE-2-3.html -- ACL2 Version 6.3

    NOTE-2-3

    ACL2 Version 2.3 (October, 1998) Notes
    Major Section:  RELEASE-NOTES
    

    Important changes:

    Versions of ACL2 preceding this one contain a subtle soundness bug! We found a flaw in our detection of subversive-recursions. The bug allowed some subversive recursions to slip through undetected.

    We believe it would have been difficult to have exploited this flaw inadvertently. In particular, the following five conditions are necessary.

    (1) Introduce a constrained function, say f, via an encapsulate.

    (2) In the same encapsulation, define a clique of mutually recursive functions. This clique must be non-local and in :logic mode.

    (3) In that mutually recursive clique, use the constrained function f (perhaps indirectly) so that the termination argument for the clique depends on properties of the witness for f. Thus, f or some other function dependent upon f, must be used in an argument in a recursive call or in a term governing a recursive call. Furthermore, the use of f must be such that the termination proof cannot be done without exploiting properties of the witness for f. Other uses of the constrained functions in the clique are ok.

    (4) Fail to include the exploited properties of f among the constraints of the encapsulation.

    (5) Later, outside the encapsulation, explicitly use a functional instantiation in which f is replaced by a function not enjoying the crucial properties.

    See subversive-recursions for details.

    Less important notes:

    We have begun to write some introductory tutorial material for those who wish to learn to program in ACL2. Most of this material is HTML-based. See the Hyper-Card on the ACL2 home page.

    The documentation of verify-guards was improved to explain why one might wish to verify the ``guards'' of a defthm event. The missing documentation was noticed by John Cowles.

    A bug was fixed in cross fertilization. The bug caused the system to report that it had substituted one term for another when in fact no substitution occurred. The bug was noticed by Bill McCune.




    acl2-sources/doc/HTML/NOTE-2-4.html0000664002132200015000000000403612222333526016022 0ustar kaufmannacl2 NOTE-2-4.html -- ACL2 Version 6.3

    NOTE-2-4

    ACL2 Version 2.4 (August, 1999) Notes
    Major Section:  RELEASE-NOTES
    

    Important changes:

    We corrected a soundness bug in Version 2.3 related to the handling of immediate-force-modep. The bad behavior was noticed by Robert Krug. Thanks!

    We corrected a bug that permitted verify-guards to accept a function even though a subfunction had not yet had its guards verified. Thanks to John Cowles for noticing this.

    User defined single-threaded objects are now supported. See stobj.

    Less important notes:

    We corrected a bug that prevented the intended expansion of some recursive function calls.

    We changed the handling of the primitive function ILLEGAL, which is logically defined to be nil but which is programmed to signal an error, so that when it is evaluated as part of a proof, it does not signal an error. The old handling of the function prevented some guard proofs involving THE or LETs with internal declarations.

    We corrected a bug that permitted some LOCAL DEFAXIOM events to slip into certified books.

    We corrected a bug that prevented the correct undoing of certain DEFPKG forms.

    Changes were made to support CMU Lisp. Pete Manolios helped with these changes.

    Changes were made to make the make files more compatible with Allegro Common Lisp. Jun Sawada, who has been a great help with keeping ACL2 up and running at UT on various platforms, was especially helpful. Thanks Jun.




    acl2-sources/doc/HTML/NOTE-2-5.html0000664002132200015000000004625112222333526016030 0ustar kaufmannacl2 NOTE-2-5.html -- ACL2 Version 6.3

    NOTE-2-5

    ACL2 Version 2.5 (June, 2000) Notes
    Major Section:  RELEASE-NOTES
    

    Important Changes:

    Concurrent with the release of ACL2 Version 2.5 is the publication of two books about ACL2. See the ``Books and Papers about ACL2 and Its Applications'' on the ACL2 Home Page.

    The books subdirectory now contains many new certifiable books, including solutions to the exercises in the two published books and full scripts for the case studies. See books/README.html.

    Improved Unix Makefile support for book certification has also been written. See books/README.html.

    The list of symbols in *acl2-exports* has been considerably expanded. If you have packages built by importing *acl2-exports* you might want to look carefully at the new value of that constant. The new value includes all :logic mode functions as of Version 2.5, as well as all documented macros and all built-in theorem names.

    Include-book and certify-book were modified to have some additional keyword arguments. It is possible to certify a book containing defaxiom and/or skip-proofs events and get warning messages or errors signaled, according to the settings of these new flags. In addition, it is possible to specify in include-book whether the book must be certified (under penalty of error if not). The default values of these new arguments cause warnings to be printed rather than errors signaled.

    The above change involved altering the form of certificate files. When books certified under previous versions are included, more warnings will be generated because these books are considered possibly to contain defaxiom and/or skip-proofs events.

    We anticipate further changes to this aspect of books and consider the current mechanisms (for controlling whether warnings or errors are signaled) just a prototype. See also the discussion below of ``soundness related'' warnings. Your suggestions are welcome.

    A discrepancy between ACL2 and Common Lisp was fixed, having to do with declare ignore. In past versions of ACL2, a formal parameter of a defun was considered ignored if it was not used in the body, the guard or the measure of the defun. That meant that a variable used only in the guard could not be declared ignored in ACL2; but some Common Lisp compilers would complain because the variable was not used in the body. Now, ACL2 considers a variable ignored if it is not used in the body.

    ACL2 can now be built in releases 5.0 and later of Allegro Common Lisp. (Other releases of Allegro Common Lisp and of other lisps continue to be supported as well.) This includes Allegro Common Lisp running on Windows 98 platforms. John Cowles helped us do some testing and answered questions for us. Thanks John!

    We incorporated Ruben Gamboa's changes to allow the building of a variant, ACL2(r), of ACL2, in which the user can reason about the real numbers using non-standard analysis. See real. Note that ACL2(r) and ACL2 have different underlying theories, and books certified in one system may not be included in the other. For backward compatibility and to ensure a smooth transition, ACL2 is built by default, not ACL2(r). This is a compile-time switch; see the makefile for instructions. There should be no changes to ACL2 resulting from the capability of building ACL2(r) from the same sources. Also see acknowledgments for more on the history of ACL2(r).

    A large number of bugs (some affecting soundness) were fixed, and many small new features were added. See below.

    Less Important Changes:

    Some warnings are now considered ``soundness related,'' namely, those that advise you that an uncertified book has been included or that a book containing DEFAXIOM or SKIP-PROOFS events. (Technically, DEFAXIOMs do not imperil soundness in the proof- theoretic sense, though they may imperil the validity of theorems. But you sould know when a book has added an axiom to your logic!) In previous versions of ACL2, all warnings were inhibited if the token warning was included in the argument to set-inhibit-output-lst. Now, soundness related warnings are printed even if warnings have been inhibited. To inhibit all warnings, supply the token warning! to set-inhibit-output-lst.

    Several bugs in defstobj were fixed, relating to the possibility that some of the subfunctions introduced by the defstobj were already defined.

    :Puff no longer tries to expand defstobj events. Previously, the attempt would cause a hard error.

    A soundness bug was fixed. The bug might have been exercised if you had an alternative definition (implies hyps (equiv (fn ...) body)) in which equiv is an equivalence relation other than EQUAL. In this case, calls of fn might have been expanded to body in places that were not equiv-hittable.

    An obscure soundness bug was fixed. The bug was exercised only if you had a metafunction with a computed hypothesis (i.e., a ``meta hypothesis function''), the hypothesis contained a free variable, i.e., a variable not involved in the term being rewritten, and the free variable occurred in the output of the metafunction. The possibility of this bug was brought to our attention by Robert Krug.

    We fixed a bug in the handling of hide related to the question of whether a variable symbol occurs in a term. The old code did not find the variable and could cause the system to throw away a hypothesis about it on the grounds that it was never mentioned. Rob Sumners helped discover this problem.

    The handling of :elim rules was generalized, permitting arbitrary known equivalence relations instead of merely equal in the concluding equality.

    The printing of runes (rule names; see rune) used has been made "deterministic," both in proof output and in proof attempt summaries, by sorting the runes before printing.

    The handling of free variables has been improved for hypotheses such as (< 0 X), and more generally, any hypotheses involving a comparison with 0 (even for example (< X 1) where X is known to be an integer, which is handled as (<= X 0)). Thanks to Robert Krug for bringing relevant examples to our attention.

    A new value, :comp, has been implemented for the :load-compiled-file keyword of include-book. If this value is supplied, then a compiled file will always be loaded, even if that requires creating the compiled file first.

    The event include-book now generates a warning when a compiled file is expected but not found (see include-book). Formerly, it only did so when executed at the top level; it failed to generate the warning when executed on behalf of a surrounding include-book command.

    Certain redefinition warnings generated by Allegro Common Lisp have been eliminated.

    A new key has been implemented for the acl2-defaults-table, :bogus-mutual-recursion-ok, set with :set-bogus-mutual-recursion-ok. Thanks to David Russinoff for pointing out the utility of such a key.

    A bug was fixed in defun-sk that prevented its generated events from being accepted when guard verification is being performed. Thanks to Bill Young for bringing this problem to our attention. A second bug was brought to our attention by Pete Manolios, which was causing certain defun-sk events to be rejected. That problem has been fixed, and an "Infected" warning has also been eliminated.

    The command good-bye now works with Allegro Common Lisp.

    A low-level bug was fixed that could, for example, cause an error such as "Error: Expected 5 args but received 4 args" when interrupting a local event.

    A bug has been fixed in the proof-checker related to definition expansion. Thanks to Pete Manolios for bringing this to our attention with a simple example.

    A bug has been fixed related to the :bdd hint in the presence of equivalence relations. Thanks to Pete Manolios for bringing this to our attention with a simple example.

    The functions position and position-equal formerly required the second argument to be a true list. In accordance with Common Lisp, we now also allow the second argument to be a string. This could cause earlier proofs about these functions to fail unless true-listp is known to hold where necessary.

    Robert Krug wrote a patch, which has been incorporated, to prevent certain infinite loops that can arise in linear arithmetic. Thanks, Robert!

    The macro let* no longer requires the bound variables to be distinct.

    An obscure bug was fixed related to congruence rules. The bug would sometimes cause ACL2 to behave as though no rules (other than equality) were available for some argument positions. Thanks to Pete Manolios for bringing this bug to our attention.

    Documentation topics have been added for hard-error and prog2$, and the documentation for illegal has been improved. Thanks to Rob Sumners for a useful suggestion in the examples in documentation for prog2$ and a fix in documentation for sublis.

    The event form certify-book was made more secure, in that it can now catch attempts to write a book to disk during its certification. Thanks to Rob Sumners for pointing out the insecurity of the existing mechanism.

    A Y2K problem was fixed with our applicative handling of dates.

    Accessors and updaters for stobjs have been made more efficient when the underlying lisp is Allegro Common Lisp, by the use of appropriate simple array declarations.

    A raw Lisp break had been possible when a certified book that had no guard verification was included in a session after (set-verify-guards-eagerness 2). This has been fixed.

    The keyword command :comp can now be used to compile only raw Lisp functions, excluding executable counterparts, by supplying the argument :raw.

    Rewrite rule nth-of-character-listp was removed from source file axioms.lisp since it is essentially subsumed by characterp-nth.

    Printing has been sped up. In one example the improvement was over 50% in both Allegro and GCL.

    We now allow printing in a "downcase" mode, where symbols are printed in lower case. All printing functions except print-object$ now print characters in lower case for a symbol when the ACL2 state global variable print-case has value :downcase and vertical bars are not necessary for printing that symbol. See IO for a discussion of the macros acl2-print-case and set-acl2-print-case. The default printing remains unchanged, i.e., symbols are printed in upper case when vertical bars are not required.

    A low-level printing function (prin1$) was modified so that it is not sensitive to various Common Lisp globals related to printing. So for example, the function fmt is no longer sensitive to the value of Common Lisp global *print-case*. (The preceding paragraph explains how to control the case for printing in ACL2.)

    The definition of array1p was fixed so that the :maximum-length of an array must be strictly greater than the number specified in the :dimensions field; they may no longer be equal. This was always the intention; the documentation (see arrays) has remained unchanged. The corresponding change was also made to array2p. Allegro Common Lisp formerly caused an error when compress1 was called on an array where the numbers above were equal; now, we get a guard violation instead, which is appropriate.

    In the context of theories, a name now represents not just the corresponding :definition rune, as it has done in earlier versions of ACL2, but also the corresponding :induction rune. See theories for a discussion of runic designators. Most users will rarely, if ever, notice this change. One situation where this change will make a difference is after executing (in-theory (current-theory 'foo)) followed by (in-theory (enable bar)), where function bar is introduced after event foo, and bar is recursively defined. The latter in-theory form now enables the rune (:induction bar), which implies that the prover can use the induction scheme stored at definition time for bar. Formerly, the rune (:induction bar) was not enabled by (in-theory (enable bar)), and hence the induction scheme for bar was ignored even when explicit :induct hints were supplied.

    You may now supply xargs keyword pair :normalize nil in order to prevent certain definitions from ``hanging'' when there are many if-subexpressions. see defun.

    We now translate type declarations of real into guards, as we have already done for other types such as rational. For example, (declare (type real x)) generates the guard (rationalp x). See type-spec.

    The theorem prover now behaves reasonably under the combination of specifying a value of t both for :otf-flg and for a hint :do-not-induct. Previously, it aborted the first time it would have otherwise pushed a goal for induction, but now, it will continue and wait until all induction subgoals have been pushed before it aborts.

    We changed slightly the definition of round. However, we believe that the new definition is equivalent to the old.

    The definition of Common Lisp function substitute has been added.

    The following changes have been made in the use of file names within ACL2. We thank Warren Hunt and John Cowles for running some tests of these changes on Macintosh and Windows 98 platforms (respectively).

    (1) Names of directories and files now use a syntax like that used for Unix (trademark of AT&T), where directories are separated using the ``/'' character even when the operating system is not Unix or Linux. See pathname. ACL2 also continues to support its notion of structured pathnames from Version 2.4 and before, but might not do so in future releases and hence no longer documents such syntax.

    (2) The command :set-cbd may now take a relative pathname as an argument.

    (3) When the macro ld is given a file name as a value for standard-oi, then if that file name is a relative pathname it refers to the result of prepending the connected book directory (see pathname, see cbd, and see set-cbd) in order to obtain an absolute pathname. Simiarly for the ld specials standard-co and proofs-co.

    It is no longer necessary to issue :set-state-ok t if you include a stobj declaration for state, for example:

    (declare (xargs :stobjs state))
    
    See declare-stobjs.

    The proof-checker has been cleaned up a bit, including the documentation and the capability (once again) to define pc-macro commands (see define-pc-macro) and proof-checker meta commands (see define-pc-meta).

    Recall that events generate summaries that include a line beginning with ``Warnings:'', which is followed (on the same line) by zero or more brief strings that summarize the warnings generated by that event. Formerly, this warnings summary for an encapsulate or include-book event did not include the summary strings for warnings generated by subsidiary events. This has been fixed.

    Macro cw has been documented and now expands to a call of a ;logic mode function. See cw for a way to print to the screen without having to involve the ACL2 state. Thanks to Rob Sumners for suggesting that we document this useful utility.

    Functions duplicates, add-to-set-equal, intersection-eq, evens, and odds are now :logic mode functions.




    acl2-sources/doc/HTML/NOTE-2-5_lparen_R_rparen_.html0000664002132200015000000000116712222333526021355 0ustar kaufmannacl2 NOTE-2-5_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-2-5(R)

    ACL2 Version 2.5(r) (June, 2000) Notes
    Major Section:  RELEASE-NOTES
    

    Important changes to non-standard version:

    Please see note-2-5 for changes to Version 2.5 of ACL2. We hope to write more documentation for ACL2(r) in the future.




    acl2-sources/doc/HTML/NOTE-2-6-GUARDS.html0000664002132200015000000001014512222333526017005 0ustar kaufmannacl2 NOTE-2-6-GUARDS.html -- ACL2 Version 6.3

    NOTE-2-6-GUARDS

    ACL2 Version 2.6 Notes on Guard-related Changes
    Major Section:  NOTE-2-6
    

    When you declare that a function treats certain formals as :stobjs, the guard of the function is automatically extended to include the corresponding stobj-recognizer calls. For example, if a definition includes (declare (xargs :stobjs (ST))) then the guard of the function is changed by the addition of the conjunct (ST-P ST).

    One impact of this is that if you use the built-in ACL2 state as a formal parameter of a function, (STATE-P STATE) is added to the guard. This may introduce a guard where there was none in previous versions of the system. In older versions, therefore, no attempt would be made to verify-guards, while in the new version, we would attempt guard verification. You may wish to add (declare (xargs :verify-guards nil)) to such definitions.

    A related change affects users who do not use stobjs or state. In previous versions of the system -- as now -- a type declaration extended the guard you provided explicitly. Thus, if you wrote (declare (type integer n)) then (INTEGERP n) was added to your guard. This is still the case and :stobjs recognizers are similarly added. But in older versions of the system we ``added'' the conjuncts without checking whether they were already present in the guard you provided. This sometimes produced such guards as (and (integerp n) (integerp n)) where the first was produced by your type declaration and the second was your :guard. We now eliminate redundant conjuncts; this may rearrange the order of the conjuncts.

    The guard conjectures for functions using stobjs have been simplified somewhat by taking advantage of the syntactic restrictions checked for single-threaded objects.

    The following functions have been modified so that character and string arguments are restricted to standard characters. (See standard-char-p and see standard-char-listp.)

    upper-case-p lower-case-p char-upcase char-downcase string-downcase1 string-downcase string-upcase1 string-upcase char-equal string-equal1 string-equal

    Also, function standard-string-alistp replaces function string-alistp, with concomitant changes in the guard to assoc-string-equal, and in variable *acl2-exports*. Also, lemma standard-string-alistp-forward-to-alistp replaces lemma string-alistp-forward-to-alistp. There is a new lemma standard-char-p-nth, which has also been added to *acl2-exports*.

    The guard had been inadvertently omitted from the definition of the function substitute (and its subroutine substitute-ac). This omission has been corrected; also, the guard is slightly stronger than the documentation had claimed (and that has been corrected).




    acl2-sources/doc/HTML/NOTE-2-6-NEW-FUNCTIONALITY.html0000664002132200015000000002674012222333526020507 0ustar kaufmannacl2 NOTE-2-6-NEW-FUNCTIONALITY.html -- ACL2 Version 6.3

    NOTE-2-6-NEW-FUNCTIONALITY

    ACL2 Version 2.6 Notes on New Functionality
    Major Section:  NOTE-2-6
    

    A fundamental change is the provision of the ``nu-rewriter'' for simplifying expressions composed of NTH, UPDATE-NTH, and UPDATE-NTH-ARRAY applications and LET expressions and other calls of non-recursive functions or LAMBDA expressions involving those symbols. The nu-rewriter applies the obvious rewrite rule for (NTH i (UPDATE-NTH j v s)) and the analogous rule for UPDATE-NTH-ARRAY. See nu-rewriter The nu-rewriter can be enabled with set-nu-rewriter-mode.

    A new flag has been added to the xargs of defun permitting the declaration that the function is non-executable. The usage is (declare (xargs :non-executable t)) and the effect is that the function has no executable counterpart. On the positive side: the function is permitted to use single-threaded object names and functions arbitrarily, as in theorems rather than as in executable definitions. Such functions are not permitted to declare any names :stobjs but accessors, etc., may be used, just as in theorems.

    A new flag has been added to permit the system to abbreviate output by introducing LET* notation identifying common subterms. The formula being proved is not affected; this flag changes its displayed form only. See set-let*-abstractionp.

    A ``raw mode'' has been added, primarily for faster loading of applications. see set-raw-mode.

    Functions alphorder and lexorder have been put in :logic mode. Lexorder is now a total order ordering of the ACL2 universe, and theorems are included to that effect. Thanks to Pete Manolios for suggesting the idea and providing events to use, and to Rob Sumners for assistance with some modifications. See also the new book books/misc/total-order for an irreflexive total order.

    The ACL2 user can now make system calls to the host operating system. See sys-call and see sys-call-status. Thanks to Rob Sumners for working out this idea with Pete Manolios and Robert Krug, who we also thank, and for working out the implementation with us.

    It is no longer required to use absolute pathnames in include-book forms that have been executed before a certify-book. Any relative pathname strings in such contexts will be expanded into absolute pathnames before they are saved in the portcullis of the certificate of the book being certified.

    ACL2 can now be built on top of Allegro Common Lisp 6.0, and also on Windows platforms on top of Allegro Common Lisp and GCL. Thanks to Pete Manolios and Vinay K. Siddhavanahalli for their help with Windows.

    Rob Sumners has designed and provided an initial implementation for two improvements to defstobj (also see stobj). First, array fields can now be resized. Resize and length functions are provided for array fields, which can be used to resize stobj array fields dynamically. The recognizers for array fields have been simplified to accommodate this change, so that they only check that each element of the array field has the specified type. Second, performance has been improved for stobjs with a large number of fields, by changing their Common Lisp implementation to store the fields in a simple vector instead of a list.

    Now stobjs may be bound locally; see with-local-stobj. Thanks to Rob Sumners, who encouraged us to implement this capability, was an early user of it, and participated usefully in discussions on its design.

    New functions fms!, fmt!, and fmt1! are the same as their respective functions without the ``!,'' except that the ``!'' functions are guaranteed to print forms that can be read back in (at a slight readability cost).

    We added extended-metafunctions, metafunctions which allow state and context sensitive rewriting to some extent. We thank Robert Krug for pushing for and on this idea.

    The documentation has been improved. In particular, a new documentation topic provides a gentle introduction to ACL2 arrays -- see arrays-example -- and additional documentation has been provided for getting started with proof trees in emacs -- see proof-tree.

    New Makefile targets fasl and o have been added to the books/ directory of the distribution. For example, you might first certify books using an ACL2 built on top of GCL (which creates compiled files with suffix o). Then, when standing in the books/ directory, you might execute the command

    make fasl ACL2=my-allegro-acl2

    which will create compiled (.fasl) files for Allegro Common Lisp, assuming that my-allegro-acl2 starts up an ACL2 built on that Common Lisp.

    The macro let* now allows variables to be declared ignored. See let* and see let.

    The user may now control backchaining. This feature was designed and primarily implemented by Robert Krug (though the authors of ACL2 are resposible for any errors); thanks, Robert! See backchain-limit.

    It is now possible to ``slow down'' the rate at which case splits are generated by the simplifier. See set-case-split-limitations.

    Accesses to stobjs using nth or update-nth are now displayed using symbolic constants instead of numeric indices. For example, given the event

    (defstobj foo a b :renaming ((b c)))
    
    then the term (nth 0 foo) will be displayed (for example, during proofs) as (nth *a* foo) while (nth 1 foo) will be displayed as (nth *c* foo). The defstobj event now correspondingly introduces a defconst event for each field accessor function, introducing a constant whose name is obtained from the accessor's name by prefixing and suffixin a ``*,'' as in the example above: accessor a generates (defconst *a* 0) and accessor c generates (defconst *c* 1). See nth-aliases-table for how to extend this feature for alternate names of stobjs.

    Computed hints have been improved. It is now possible to detect within a computed hint whether the goal clause is stable under simplification; it is also possible for a computed hint to change the list of available hints. See computed-hints.

    It is now possible to provide ``default hints'' that are appended to the hints explicitly provided. See set-default-hints.

    Using computed hints (see computed-hints) and default hints (see set-default-hints) it is possible to implement a book that supports ``priority phased simplification.'' Using this book you can assign priorities to your rules and cause the theorem prover to simplify each goal maximally under all the rules of one priority before enabling rules of the next priority. See books/misc/priorities.lisp.

    The macro defabbrev has been improved to allow declare forms and documentation strings and to do more error-checking. Thanks to Rob Sumners for designing this enhancement and providing the first implementation. See defabbrev.

    Further changes were made to support CMU Lisp. Wolfhard Buss helped with these changes.

    A new table was added that is used when printing proof output, so that nests of right-associated calls of a binary function are replaced by corresponding macro calls, as has been the case for binary-+ and +, binary-append and append, and so on. See add-binop.

    Operators logand, logior, logxor, and logeqv are now macros (formerly, they were functions) that call corresponding binary functions (e.g., binary-logand) defined in source file "axioms.lisp". Thanks to Rob Sumners for this enhancement. Proof output will however continue to show calls of logand, logior, logxor, and logeqv.

    Function (allocate-fixnum-range fixnum-lo fixnum-hi) sets aside more "permanent" fixnums in GCL.

    ACL2 now runs under CLISP. Thanks to Wolfhard Buss and Sam Steingold for their assistance with the port.

    Michael ``Bogo'' Bogomolny has created a search engine, accessible from the ACL2 home page. For that purpose he modified the HTML translator to create one file per topic (a good idea in any case). Thanks, Bogo!

    An emacs file of potential (but optional) use for ACL2 users may be found in emacs/emacs-acl2.el. In particular, this file supports the use of proof trees (see proof-tree).

    Some books have been added or modified. In particular, Robert Krug has contributed books/arithmetic-2/, which provides an alternative to the existing collection of books about arithmetic, books/arithmetic/. For a discussion of the distributed books see the link to README.html in the installation instructions.




    acl2-sources/doc/HTML/NOTE-2-6-OTHER.html0000664002132200015000000001521612222333526016705 0ustar kaufmannacl2 NOTE-2-6-OTHER.html -- ACL2 Version 6.3

    NOTE-2-6-OTHER

    ACL2 Version 2.6 Notes on Other (Minor) Changes
    Major Section:  NOTE-2-6
    

    Warning strings are now case-insensitive. See set-inhibit-warnings.

    ACL2 causes a warning when an in-theory hint or event causes a 0-ary function's definition to be disabled but its :executable-counterpart to be enabled.

    A minor modification has been made to defstobj that can have a positive impact on performance in Allegro Common Lisp. (For Lisp hackers: the stobj name was formerly declared special, and that was disabling Allegro's tail-merging routing for compilation of some recursive functions using stobjs.) The downside is that stobj names can no longer be evaluated in raw Lisp. However, raw Lisp is not the right place to be evaluating ACL2 forms anyhow; see set-raw-mode. We thank Rob Sumners for bringing this issue to our attention.

    Before Version 2.6, there has been the following problem with defstub and encapsulate in the case that the current package is not the ACL2 package. If a signature was specified using the symbol =>, then that symbol had have been imported into the current package from the ACL2 package when the current package was defined. There are no longer any package restrictions on the use of =>. Thanks to John Cowles for bringing this problem to our attention.

    Bugs in defun-sk have been fixed. Defun-sk forms introducing functions of no arguments were failing to be admitted, for example: (defun-sk always-p1 () (forall (x) (p1 x))). Thanks to John Cowles for bringing this problem to our attention. Also, defun-sk failed on an example in the documentation (see tutorial4-defun-sk-example), as pointed out by Matyas Sustik; this bug has been fixed as well.

    The trace mechanism has been fixed to handle stobjs, and to avoid the printing of so-called enabled structures.

    The brr command :type-alist now produces more readable output.

    An include-book of an uncertified book no longer loads an associated compiled file.

    We added a few checks to make sure that the underlying lisp is suitable, for example checking that the reader is case-insensitive and reads in symbols with upper-case names where appropriate.

    We now warn when forcing (see force) or immediate force mode (see immediate-force-modep) change state between enabled and disabled. Also see enable-immediate-force-modep and see disable-immediate-force-modep for information about these new macros, which may be used to control immediate force mode.

    We have eliminated the use of a low-level raw Lisp constant, *most-recent-multiplicity*. Our test suite saw a speed-up of approximately 2% as a result for an ACL2 image built on GCL (but no significant speed-up for an ACL2 image built on Allegro Common Lisp). We thank Rob Sumners for suggesting this improvement.

    Fixnum declarations are now realized as (signed-byte 29) instead of (signed-byte 27). We check that the underlying Common Lisp recognizes objects of type (signed-byte 29) as fixnums, with the exception of CLISP, which is said to have an efficient bignum implementation.

    A new documentation topic functional-instantiation-example illustrates functional instantiation.

    A bug has been fixed in the monitoring of runes (see monitor). Thanks to Dave Greve for sending an example that clearly showed the problem.

    A warning is now issued when it is detected that a :type-prescription rule may not be as strong as it appears because it is not sufficient to prove itself by type reasoning.

    An error is caused for rules of class :meta when the function symbol IF is among the :trigger-fns. (IF was ignored anyhow; the point of this change is to avoid misleading the user.)

    A minor bug has been fixed in :pr, evident for example if this command was applied to IF.

    A minor hole in :set-bogus-mutual-recursion-ok did not permit the acceptance of mutual-recursion forms that include constant function definitions. This has been fixed. Thanks to Eric Smith for coming up with a simple example illustrating the problem.

    The temporary files "TMP.lisp" and "TMP1.lisp" written out by :comp are now written to the connected book directory (see cbd).

    Previously, the Allegro compiler was not eliminating tail recursion for executable counterparts of functions, because of the way one of its flags had been set. As a result, calls of functions whose guards had not been verified could run out of stack space when this was not necessary. This situation has been fixed.

    Executable counterparts could have slow array accesses. This has been fixed (specifically, constants are no longer replaced with their values in the definitions of executable counterparts).

    Various improvements have been made to the documentation. Thanks in particular to Eric Smith for pointing out a numbers of places where fixes were in order.

    File "mcl-acl2-startup.lisp" has been updated, thanks to feedback from Philippe Georgelin.

    Inefficiencies in GCL fixnum computations were remedied for macros +f and *f. Thanks to Rob Sumners for pointing out this issue.




    acl2-sources/doc/HTML/NOTE-2-6-PROOF-CHECKER.html0000664002132200015000000000264512222333526017755 0ustar kaufmannacl2 NOTE-2-6-PROOF-CHECKER.html -- ACL2 Version 6.3

    NOTE-2-6-PROOF-CHECKER

    ACL2 Version 2.6 Notes on Proof-checker Changes
    Major Section:  NOTE-2-6
    

    The proof-checker command =, when used with no arguments, now reports which hypothesis is being used.

    The output from proof-checker command type-alist has been improved.

    A slight change has been made to the proof-checker for commands promote, casesplit, equiv, and =, so that terms of the form (if x nil y) are recognized as conjunctions, (and (not x) y). Thanks to Pete Manolios for suggesting that we consider such a change.

    There is a new proof-checker command print-all-concs that prints all the conclusions of the unproved goals.

    A new proof-checker command, runes, has been added. It reports the runes that have participated in the interactive proof up to the current point.




    acl2-sources/doc/HTML/NOTE-2-6-PROOFS.html0000664002132200015000000001445112222333526017034 0ustar kaufmannacl2 NOTE-2-6-PROOFS.html -- ACL2 Version 6.3

    NOTE-2-6-PROOFS

    ACL2 Version 2.6 Notes on Changes in Proof Engine
    Major Section:  NOTE-2-6
    

    Certain optimizations are performed when converting terms to clausal form. For example, (< 0 1) is known to be t, (HARD-ERROR ctx str alist) is known to be nil, and (INTEGERP n) is known to imply (RATIONALP n).

    In earlier versions of ACL2, the conversion of a term to clausal form expanded LAMBDA applications. That may no longer occur. Some proofs may slow down (or fail) because your LAMBDA-expressions are not expanded away when you ``expected'' them to be.

    Robert Krug found a soundness bug in our linear arithmetic package. The bug was caused by the derivation of an equation from two inequalities without taking adequate precautions to ensure that both sides of the inequalities were numeric. Robert also kindly provided a fix which we adopted. Thanks Robert!

    We fixed a bug that could prevent the application of a metatheorem.

    A bug has been fixed that had caused bogus forcing rounds (see forcing-round). The bug could occur when the hypothesis of a rule was forced (see force) before the prover decided to start over and prove the original goal by induction. Thanks to Rob Sumners for drawing our attention to this problem.

    Some low-level fixes have been made that prevent certain infinite loops, based on reports by users. We thank Yunja Choi, Matt Wilding, and Pete Manolios for reporting such problems.

    An obscure potential soundness hole has been fixed by redoing the way evaluation takes place in the ACL2 loop and during theorem proving. We expect that users will see no difference based on this change. (Those interested in the details can see the long comment ``Essay on Evaluation in ACL2'' in source file interface-raw.lisp.)

    A small change was made in computation for a heuristic that controls backchaining. This will speed up proofs dramatically in a very few cases but should have a very small impact in general.

    The simplifier has been modified to avoid eliminating hypotheses of goals that can be established by contextual (specifically, type-set) reasoning alone. We believe that this change will generally strengthen ACL2's reasoning engine, although on rare occasions a lemma that formerly was provable may require user assistance. Thanks to Robert Krug for suggesting this change and providing its implementation.

    Case splits are now limited, by default. This may allow some proof attempts to provide output where previously the prover would appear to ``go out to lunch.'' For a more complete discussion, including instructions for how users can control case splitting, see set-case-split-limitations.

    A bug has been fixed in the handling of :type-prescription rules by the bdd package. Thanks to Rob Sumners for discovering this bug and supplying a helpful example.

    ACL2 may now use the built-in induction scheme for a function symbol even if that function symbol is disabled. Formerly, if a function symbol was disabled then its induction scheme was only considered if an explicit induction hint was supplied, other than :induct t.

    We eliminated the rule-class linear-alias. This rule class was seldom used and complicated the linear arithmetic decision procedure in ways that made it difficult to extend to handle some non-linear special cases. The only use of the rule-class that we know of was in our own nqthm books, which were an attempt to provide an embedding of the Nqthm logic and theorem prover into ACL2. But that facility was also practically never used, as far as we know. So both linear-alias rules and the nqthm books have been eliminated.

    In earlier versions of ACL2, when the IF-form of (AND p q) was assumed true -- as when rewriting the alpha expression in (IF (AND p q) alpha beta) -- the assumption mechanism did not deduce that p and q are true, only that their conjunction, in its IF-form, is true. This has long been known as a deficiency in both ACL2 and the earlier Nqthm but it was tedious to do better when one considered the full range of IF-forms one might encounter in the test of another IF. Rather than code all the cases, we just waited until clausification got rid of them. Robert Krug developed a pretty nice treatment of the general case and we added it in this version. This also involved a surprising number of changes elsewhere in the system because the improved handling of assumptions caused the theorem prover often to ``erase'' hypotheses provided by :use hints because it could simplify them to t. Thank you Robert!

    In response to a suggestion from Robert Krug, we added mfc-ap so that extended metafunctions can take advantage of linear arithmetic. See extended-metafunctions.

    There is less delay in printing goals. In previous versions, a goal was not printed until its subgoals were created (or the goal was proved). Now, the goal is printed essentially as soon as it is created.

    A small technical change has been made in the function term-order, to give priority on the function symbol count over the weighting of constants. So for example, while previously the term (f) preceded the constant 2, that is no longer the case. If this change is noticed at all, it will probably be noticed in how so-called permutative rewrite rules are applied; see loop-stopper. Thanks to Robert Krug for suggesting this improvement and providing part of the implemtation.




    acl2-sources/doc/HTML/NOTE-2-6-RULES.html0000664002132200015000000001135512222333526016716 0ustar kaufmannacl2 NOTE-2-6-RULES.html -- ACL2 Version 6.3

    NOTE-2-6-RULES

    ACL2 Version 2.6 Notes on Changes in Rules and Constants
    Major Section:  NOTE-2-6
    

    The following symbols have been added to the list constant *common-lisp-specials-and-constants*: REPLACE, FILL, CHARACTER, =, BREAK, and PRIN1. This was done in support of ports to Allegro 6.0 and Windows platforms (see note-2-6-new-functionality).

    The list of symbols in *acl2-exports* has been modified, for example to include show-accumulated-persistence and the legal arguments to set-inhibit-output-lst.

    Functions zp and zip are now handled slightly differently. They are are now disabled, but each comes with a :rewrite rule that allows their expansion on non-variable terms, and also with a :compound-recognizer rule that avoids the need for opening up these functions when applied to variables. The resulting behavior should be very similar to the behavior of previous versions, except that case splits will be avoided when these functions are applied to variables.

    Function standard-string-alistp replaces function string-alistp. For further discussion, see note-2-6-guards.

    Rules of class :rewrite whose conclusion is a term of the form (equal lhs rhs) have always been stored in the expected way: lhs rewrites to rhs. This way of storing :rewrite rules has been extended to allow =, eq, or eql in place of equal.

    Rewrite rule nth-update-nth, in source file axioms.lisp, has been strengthened.

    A new rewrite rule equal-constant-+ has been added to the book arithmetic/equalities. This should generally be a beneficial change, but existing proofs involving the arithmetic books could conceivably be affected.

    Function symbol-package-name and constant *main-lisp-package-name* have undergone small changes. This change should rarely be noticed by users and is discussed elsewhere; see note-2-6-system.

    We mention here that proofs involving stobjs may need to be modified because of changes in auxiliary functions generated by defstobj. (These changes were made in support of a new resizing capability, mentioned elsewhere in these release notes; see note-2-6-new-functionality.

    In the distributed book directory books/arithmetic/, the book rationals-with-axioms-proved.lisp has been renamed rationals.lisp.

    (ACL2(r) only) Rewrite rules realp-+, realp-*, realp-unary--, and realp-unary-/ have been added in analogy to existing rules rationalp-+, rationalp-*, rationalp-unary--, and rationalp-unary-/. Thanks to Jun Sawada for suggesting this change.

    The definition of aref1 has been modified slightly. Previously, if *my-a* were an array then (aref1 'some-name *my-a* :header) would evaluate to the cdr of the header of *my-a* rather than to its default. See arrays.

    Changes have been made in the ihs books, based on suggestions from Jun Sawada, that support its use with ACL2(r) (see real). The primary change is to replace calls of rationalp with calls of real/rationalp, which should have no effect on users of standard ACL2.




    acl2-sources/doc/HTML/NOTE-2-6-SYSTEM.html0000664002132200015000000000540212222333526017044 0ustar kaufmannacl2 NOTE-2-6-SYSTEM.html -- ACL2 Version 6.3

    NOTE-2-6-SYSTEM

    ACL2 Version 2.6 Notes on System-level Changes
    Major Section:  NOTE-2-6
    

    We modified the tracking of skip-proofs events and the use of state global ld-skip-proofsp in order to avoid some soundness issues. For example, skip-proofs events buried in locally-included books are now tracked. The ``Essay on Skip-proofs'' in source file axioms.lisp gives several examples of dicey behavior that is no longer supported.

    We fixed a problem with some of the makefiles, so that recursive invocations of `make' now use the version of `make' specified on the command line.

    Files were fixed to help non-Unix/Linux users with book certification. Thanks to John Cowles for finding some problems and suggesting fixes to books/certify-numbers.lisp, books/arithmetic/certify.lsp, and books/cowles/certify.lsp. We thank Scott Burson for noticing and fixing some other such problems. Moreover, a bdd test was being ignored entirely in Version 2.5; this problem has been fixed as well.

    A minor change in system function save-acl2-in-allegro will allow this function to continue to work in Allegro CL versions starting (someday) with 10.0. Thanks to Art Flatau for suggesting such a fix.

    The books/case-studies/ directory has been removed. These books are in support of the first (1998) ACL2 workshop, and are accessible via the ACL2 home page on the Web, http://www.cs.utexas.edu/users/moore/acl2/. Also, the books/cli-misc directory has been renamed books/misc, and the books/nqthm directory has been removed.

    The notion of ACL2 version has been slightly modified to catch unsoundness due to implementation dependencies. See version. Another change to eliminate such unsoundness is that built-in symbols now have a symbol-package-name of "COMMON-LISP"; formerly, this string was "LISP" for ACL2 images built on GCL. See symbol-package-name. At a low level, the (undocumented) constant *main-lisp-package-name* is now "COMMON-LISP"; before, it was "LISP" for GCL.




    acl2-sources/doc/HTML/NOTE-2-6.html0000664002132200015000000000420712222333526016024 0ustar kaufmannacl2 NOTE-2-6.html -- ACL2 Version 6.3

    NOTE-2-6

    ACL2 Version 2.6 (November, 2001) Notes
    Major Section:  RELEASE-NOTES
    

    Because of the large number of modifications, we have divided up the Version 2.6 notes into the following subtopics.

    o New functionality (see note-2-6-new-functionality):
    o Changes in proof engine (see note-2-6-proofs):
    o Changes in rules and definitions (see note-2-6-rules):
    o Guard-related changes (see note-2-6-guards):
    o Proof-checker changes (see note-2-6-proof-checker):
    o System-level changes (see note-2-6-system):
    o Other (minor) changes (see note-2-6-other):

    Some Related Topics




    acl2-sources/doc/HTML/NOTE-2-6_lparen_R_rparen_.html0000664002132200015000000000122512222333526021351 0ustar kaufmannacl2 NOTE-2-6_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-2-6(R)

    ACL2 Version 2.6(r) (November, 2001) Notes
    Major Section:  RELEASE-NOTES
    

    Important changes to non-standard version: None since Version 2.5.

    Please see note-2-6 for changes to Version 2.6 of ACL2. We hope to write more documentation for ACL2(r) in the future.




    acl2-sources/doc/HTML/NOTE-2-7-BUG-FIXES.html0000664002132200015000000004261612222333527017323 0ustar kaufmannacl2 NOTE-2-7-BUG-FIXES.html -- ACL2 Version 6.3

    NOTE-2-7-BUG-FIXES

    ACL2 Version 2.7 Notes on Bug Fixes
    Major Section:  NOTE-2-7
    

    Francisco J. Martin-Mateos emailed us a soundness bug (!) in our handling of functional instantiation (for example see functional-instantiation-example). We are grateful for that email, which clearly illustrated the problem. It is included just below the definition of push-clause in ACL2 source file prove.lisp, where we have fixed the bug. This bug was fixed in a re-release of Version 2.6 in February, 2002.

    Rob Sumners emailed us a soundness bug (!) in function commutative-p1, which is used by the ACL2 bdd package. We are grateful for his help; his email gave a proof of nil and also pointed to the problem function. This bug was fixed in a re-release of Version 2.6 in February, 2002.

    We discovered and fixed a soundness bug illustrated by the book below, which was certifiable in Version 2.6 and ends in a proof of nil. The event (verify-guards foo) should have been rejected, because foo calls a function whose guards have not been verified, namely, bar. However, ACL2 did not notice the call of function bar in the body of foo because it was looking in the simplified (normalized) body of foo rather than in the original body of foo. During processing of the book below, the logical definition of zp is used before (verify-guards foo), and (zp -3) reduces to t in the logic. After (verify-guards foo), ACL2 simplifies (foo -3) by going into raw Lisp, where (zp -3) is evaluated and reduces to nil.

      (in-package "ACL2")
      (defun bar (x)
        (zp x))
      (defthm zp-false-on-negatives
        (implies (< x 0)
                 (bar x))
        :rule-classes :type-prescription)
      (defun foo (x)
        (declare (xargs :guard (rationalp x)
                        :verify-guards nil))
        (if (< x 0)
            (if (bar x) 0 1) ; simplified body reduces this line to 0
          17))
      (defthm foo-of-minus-3-is-0
        (equal (foo -3) 0)
        :rule-classes nil)
      (verify-guards foo)
      (defthm foo-of-minus-3-is-1
        (equal (foo -3) 1)
        :rule-classes nil)
      (defthm bug
        nil
        :rule-classes nil
        :hints (("Goal" :use (foo-of-minus-3-is-0 foo-of-minus-3-is-1))))
    
    The above bug exploited the fact that zp has a different definition in raw Lisp than in the logic for arguments that violate its guard). The following example caused a hard error in raw Lisp, though not a soundness error.
      (in-package "ACL2")
      (defun bar (x)
        (cons (car x) (car x)))
      (defun foo (x)
        (declare (xargs :guard t
                        :verify-guards nil))
        (if (bar x) x nil))
      (verify-guards foo)
      (defthm bug
        (equal (foo 3) t)
        :rule-classes nil)
    
    We have made a minor change to the notion of the formula of a function symbol, related to the change above, which however is unlikely to be noticeable.

    In order to make it harder to hit problems like the guard problem above, we have slighly modified the raw Lisp definition of zp.

    A break-rewrite command, :ancestors, was broken, but has been fixed. Thanks to Eric Smith for bringing the problem to our attention, and to Robert Krug for supplying the final part of the fix.

    Some proof-checker commands caused errors when all goals have already been proved. This has been fixed. Thanks to Matt Wilding for reporting this bug.

    Fixed a bug in :comp. When compiling uncompiled functions with very large definitions, ACL2 was inserted a backslash (\) character into generated files.

    Fixed the :type-alist :brr command (see brr-commands), whose output was difficult to read when typed after an :eval..

    Fixed some clumsy handling of errors when including an uncertified book, for example, with the error message when including an uncertified book with a bad deftheory event. Thanks to Eric Smith for pointing out this problem.

    Two modifications to certify-book now cause it to reflect natural expectations with respect to soundness. First, it now has default values of nil instead of t for keyword arguments :skip-proofs-okp and :defaxioms-okp. Thanks to Robert Krug for suggesting this change and the ACL2 seminar at the University of Texas for discussing it. Second, when :skip-proofs-okp (respectively, :defaxioms-okp) is nil, either explicitly or by default, then skip-proofs commands (respectively, defaxiom events) are disallowed inside any included books, regardless of the keyword parameters passed to include-book. This had not been the case for previous versions of ACL2, regardless of the values of :skip-proofs-okp or :defaxioms-okp passed to include-book.

    Improved warnings and errors for certify-book and include-book to mention the portcullis as a possible source of skip-proofs and defaxioms.

    ACL2 formerly caused an error when hints in a :corollary were not well-formed. This situation could arise as follows when certifying a book. A lemma FOO is proved LOCALly to the book (or, is present in a sub-book that is included locally). The :corollary of a subsequent theorem, BAR, disables that rule in a hint. When BAR is proved, this is not a problem. But certify-book makes a second pass after processing the events in a book: it essentially does an include-book. During the include-book pass, FOO is not known (because it was local), and therefore ACL2 fails to process the disable of FOO in an in-theory hint. The fix is that during include-book, hints are ignored in corollaries just as they have been for the main theorem (or definition).

    It was possible for guard verification to succeed where it should have failed. We have fixed the bug (which was in source function (ironically named!) fcons-term-smart). Thanks to Robert Krug for sending us an example of bungled guard verification. It turns out that this bug was also present in Version_2.6.

    The proof-checker command = has been improved. Formerly, it could fail to apply when certain implies terms were in the context. Thanks to Pete Manolios for bringing this problem to our attention.

    The command add-binop failed to work. This has been fixed. Thanks to Rob Sumners for pointing out this problem. Also see note-2-7-other for a discussion of how this and another table are no longer part of the acl2-defaults-table.

    Book certification could cause a segmentation fault in cases where the certification world (see certify-book) has a very large number of events. This has been fixed.

    We now allow empty :use hints and empty hints, as requested by Eric Smith. Examples:

    ("Goal" :use ())
    ("Goal")
    

    A large mutual-recursion nest could cause a stack overflow when executing either :pr FN, :pr! FN, or :monitor (:definition FN) t, where FN is in that large mutual recursion nest. This has been fixed (implementation detail: function actual-props has been made tail-recursive). NOTE: If you just want the definition of FN, :pf FN can be much faster than :pr FN if FN is in a large mutual-recursion.

    Hard Lisp errors could occur when including uncertified books. This has been fixed; ACL2 now does syntax-checking formerly omitted when including uncertified books.

    Previously, the evaluation of defstobj and mutual-recursion forms could cause ``undefined'' warnings when the form was compiled. This has been fixed. Thanks to Eric Smith for bring a mutual-recursion example to our attention.

    A bug has been fixed in the syntactic check for valid :loop-stopper values. Formerly, valid :loop-stopper values were erroneously restricted to lists of length at most 2 (a minor problem, since these lists typically have length 1), and the function symbol(s) need not have been defined in the current ACL2 world. Thanks to Eric Smith for sending an example to demonstrate the latter problem.

    Functions definitions that are :non-executable (see xargs) had never been recognized as redundant, but this has been fixed. Thanks to Vernon Austel for pointing out this problem.

    Compilation using :comp now compiles user-defined :program mode functions. Formerly only :logic mode functions could be compiled using :comp.

    Handling of :by hints has been improved in essentially three ways. The primary change is that now, when the current goal exactly matches the supplied lemma instance, the subsumption test will always succeeds (see hints, in particular the discussion of :by). Second, certain proof failures involving :by hints were failing silently, with duplicate messages ``As indicated by the hint, this goal is subsumed by....'' This could happen when the original goal was among the goals generated by applying the hint. This problem has been fixed by no longer considering this proof step to be specious (see specious-simplification). Third and finally, when the lemma-instance refers to a definition, the original body of that definition is used rather than the simplfied (``normalized'') body.

    In addition to the obove, we now recognize more cases of specious simplification (see specious-simplification). Thanks to Eric Smith for bringing this issue to our attention.

    Fixed building of ACL2 under CLISP so that (1) the appropriate ACL2 startup message is printed out when ACL2 starts up, and (2) the lisp process supplied to make, e.g., LISP=/usr/bin/clisp, is the one written out to the saved ACL2 file. Thanks to Dave Greve and Noah Friedman for suggesting (2). Also, ACL2 now works with CLISP 2.30. We have accommodated a change in CLISP's handling of streams and its package-locking mechanism, as well as certain non-standard characters that formerly could cause CLISP 2.30 to break, even when those characters are in comments.

    Eliminated compiler warnings for CMU Lisp.

    Fixed an incorrect error supplied when book certification proceeded so quickly that the file write dates of the book (.lisp file) and the corresponding compiled file are equal. Now that error only occurs if the compiled file has a strictly earlier write date, which probably should never happen.

    Fixed an infinite loop when executing make clean-books (and hence `make' with targets that call clean-books, namely, certify-books-fresh, regression-fresh, and regression-nonstd-fresh), which could occur when any subdirectories of books/ are missing -- even workshops/, which is intended to be optional. Thanks to Pete Manolios for pointing out this bug.

    The include-book command now works properly even when filenames, or their directories or parent directories (etc.) are links. Thanks to Matt Wilding for pointing out this problem.

    The commands :puff :puff* have been fixed. Formerly, there was a bug when :puff or :puff* caused the execution of an include-book for an absolute pathname, P, that was other than the current connected book directory (see cbd). When including P, any subsidiary include-book with a relative pathname would be erroneously considered relative to the current cbd rather than relative to the directory of P. Thanks to Pete Manolios and Matt Wilding for pointing out this problem.

    It had been possible in a ``large'' ACL2 image to call verify-termination successfully on built-in function sys-call, with undesirable results. This hole has been plugged. Thanks to Rob Sumners for pointing out this problem. The new function gc$ must also stay in :program mode.

    ACL2 no longer warns when certifying a book based on local functions whose guards have not yet been verified. Thanks to Pete Manolios for pointing out this issue.

    An occasional ``slow array warning'' had been possible during proofs. The following sequence shows how to evoke that warning in previous versions.

    (in-theory (disable binary-append))
    (in-theory (enable binary-append))
    (in-theory (disable binary-append))
    (ubt 2)
    (thm (equal (car (cons x y)) x))
    
    (See note-2-7-other for a discussion of a change to compress1 in support of this fix; however, users should not need to read that discussion.)

    The raw Lisp code for defchoose had a small bug, which was only evidenced in CLISP implementations as far as we know. It has been fixed.

    When ld is applied to a stringp file name, it now temporarily sets the connected book directory (see cbd) to the directory of that file while evaluating forms in that file. To see the effect of this change, imagine a subdirectory "sub" of the current directory, and imagine executing (ld "sub/foo.lisp"), where file foo.lisp contains the form (include-book "bar"). Presumably the intention was to consider the file bar.lisp in the same directory, sub/, as foo.lisp. Ld now honors that intention, but in previous versions "bar.lisp" would have been a reference to a file in the current directory, not in sub/.

    For users of run-acl2 [perhaps there are none!]: A fix has been provided by a Debian user via Camm Maguire so that acl2-mode anyone using that?] will work in Xemacs, which apparently uses variable lisp-mode-shared-map rather than shared-lisp-mode-map.

    ACL2 has, for a long time (always?), had a mechanism for avoiding re-proving constraints generated by :functional-instance lemma-instances in :use and :by hints. But this mechanism had not applied to defined (as opposed to constrained) functions. This has been fixed. Thanks to Francisco J. Martin-Mateos (ChesKo) for pointing out this problem by sending a clear example.




    acl2-sources/doc/HTML/NOTE-2-7-GUARDS.html0000664002132200015000000000220012222333527017000 0ustar kaufmannacl2 NOTE-2-7-GUARDS.html -- ACL2 Version 6.3

    NOTE-2-7-GUARDS

    ACL2 Version 2.7 Notes on Guard-related Changes
    Major Section:  NOTE-2-7
    

    It was possible for guard verification to succeed where it should have failed. See the discussion under note-2-7-bug-fixes.

    There have been changes in the guards generated from type declarations for the following cases. Thanks to Dave Greve and Matt Wilding for suggesting such changes.

    (type (signed-byte n) val)
    (type (unsigned-byte n) val)
    (type (integer m n) val)
    
    The following examples illustrate the changes.
    (type (signed-byte 4) x)
    ==> [old] (AND (INTEGERP X) (<= -8 X) (<= X 7))
    ==> [new] (SIGNED-BYTE-P 4 X)
    
    (type (unsigned-byte 4) x)
    ==> [old] (AND (INTEGERP X) (<= 0 X) (<= X 15))
    ==> [new] (UNSIGNED-BYTE-P 4 X)
    




    acl2-sources/doc/HTML/NOTE-2-7-NEW-FUNCTIONALITY.html0000664002132200015000000002440512222333527020505 0ustar kaufmannacl2 NOTE-2-7-NEW-FUNCTIONALITY.html -- ACL2 Version 6.3

    NOTE-2-7-NEW-FUNCTIONALITY

    ACL2 Version 2.7 Notes on New Functionality
    Major Section:  NOTE-2-7
    

    ACL2 now has a more powerful technique for relieving a :rewrite or :linear rule's hypothesis that contains free variables. A new documentation section has been written describing the handling free variables in rules; see free-variables. In brief, the primary change is that when a free-variable match for the current hypothesis fails to allow subsequent hypotheses to be relieved, then additional matches may be attempted until they have all been tried. Also see rule-classes (discussion of :match-free). Also see set-match-free-error, see set-match-free-default, and see add-match-free-override for interfaces provided to the user for controlling the way ACL2 deals with free variables in hypotheses. We thank Rob Sumners for several helpful discussions about the designs of those interfaces, as well as Eric Smith and Robert Krug for helpful related discussions. Robert Krug also found a performance bug in a preliminary version, for which we are grateful.

    WARNING: Book certification attempts may take much longer now that, by default, ACL2 looks for more free variable matches (see paragraph just above). You can get the old behavior by inserting the form

    (set-match-free-default :once)
    
    just after the initial in-package form. However, rules from included books that have free variables can still slow down certification. This can be fixed by inserting
    (add-match-free-override :once t)
    
    before the first event in the file that generates a proof.

    Forward-chaining has been made more powerful in the presence of free variables (see free-variables), thanks to a contribution by Erik Reeber. Both before and now, when an attempt is made to relieve (prove) a hypothesis of a :forward-chaining rule in the case that at least one variable in that hypothesis is not yet bound, ACL2 looks in the current context for an instance of that hypothesis. If it finds one, then it binds the unbound variables and continues to the next hyopothesis. What is new is that ACL2 can now looks for multiple instances of that hypothesis. Consider the following example; an explanation is below.

    (encapsulate (((op * *) => *))
                 (local (defun op (x y) (< x y)))
                 (defthm transitivity-of-op
                   (implies (and (op x y) (op y z)) (op x z))
                   :rule-classes :forward-chaining))
    
    ; fails in Version_2.6; succeeds in in Version_2.7
    (thm (implies (and (op a b) (op b c) (op b e)) (op a c)))
    
    Before Version_2.7, the proof of the thm above fails. When the :forward-chaining rule transitivity-of-op binds x to a and y to b, it then looks for an instance of (op y z) in the current context, with y bound to b but z unbound. It happens to find (op b e) before (op b c), and it then adds (op a e) to the context. But starting with Version_2.7, it continues to look for additional instances and finds (op b c) in the context as well, chaining forward to (op a c) and thus proving the theorem.

    A new macro, bind-free, provides a simple way to get much or most of the power of metafunctions. Thanks to Eric Smith for coming up with the idea and to Robert Krug for providing an implementation (which we modified only very slightly) and documentation. See bind-free and see bind-free-examples.

    With the addition of bind-free (mentioned above), syntaxp has become a macro, although that change should be transparent to the user. More importantly, the argument of syntaxp may now refer to variables mfc and state, giving syntaxp some of the power of extended metafunctions; see syntaxp and see extended-metafunctions. Thanks to Robert Krug for implementing that extension. Also, the argument of syntaxp may now include calls of :program mode functions. See syntaxp and see syntaxp-examples (thanks to Robert Krug for updating the former and creating the latter documentation).

    The linear-arithmetic decision procedure (see linear-arithmetic) has now been extended so that ACL2 can reason about non-linear arithmetic as well (see non-linear-arithmetic for how to turn on this feature). We thank Robert Krug for the initial implementation of this, and Eric Smith for finding a couple of bugs in it.

    Some trace utilities have been made available in the ACL2 loop.

    o Function trace$ (and also untrace$) calls the corresponding underlying Lisp routine trace (and untrace), which however continues (as it has for some time) to be enhanced for GCL and Allegro CL.

    o Macro open-trace-file causes trace output to go to a specified file. Macro close-trace-file causes trace output to go to the screen (which is the default).

    o Macro with-error-trace (or, wet for short) causes a backtrace to be written out for many failures, including guard violations. See trace, see trace$, and see :DOC wet [** NOTE: eliminated after Version 3.3].

    A new theory, minimal-theory has been provided (see theories). It can be particularly useful for speeding up proofs involving :use hints.

    New events defund and defthmd behave exactly like defun and defthm, respectively, except that these new events disable the new name.

    The new macro with-output can be used to suppress output that would normally result from evaluation of a form.

    The form (pstack) can give the user an idea of what the prover has been up to during a proof, or after a user-aborted proof. Moreover, by evaluating (verbose-pstack t) (see verbose-pstack) one can get trace-like information about prover functions, including time summaries, printed to the screen during a proof. Thanks to Bill Legato and Robert Krug for initiating this work and to Robert for providing some initial implementation.

    The new command :comp-gcl is identical in functionality, except that it always leaves .c and .h files when compiling in GCL. Thanks to Rob Sumners and Vernon Austel for suggesting such a capability.

    The macro e/d provides a convenient way to enable some rules and disable others. It was formerly in a book supplied with the distribution, books/ihs/ihs-init.lisp, written by Bishop Brock (who we thank for providing this useful macro).

    New distributed books include those in books/ordinals/, books/rtl/rel3/, and books/misc/simplify-defuns.lisp (which is documented in books/misc/simplify-defuns.txt).

    The :expand hint now accepts a special value, :LAMBDAS, that tells the ACL2 rewriter to expand all lambda applications (let expressions). See hints.

    A new function zpf has been added as fast test against 0 for nonnegative fixnums.

    A new macro gc$ allows the user to call the garbage collector of the underlying Common Lisp. Thanks to Rob Sumners for suggesting this feature.

    It is now possible to monitor simple (abbreviation) rules. However, as a warning explains, they are still not considered monitored during preprocessing; see monitor. Thanks to Robert Krug for providing this improvement.

    The second argument of certify-book, if supplied, formerly had to be either t or a non-negative integer. Now it can be the symbol ?, in the ACL2 package, indicating that the usual check should be suppressed on the number of commands that have been executed to create the world in which certify-book was called.




    acl2-sources/doc/HTML/NOTE-2-7-OTHER.html0000664002132200015000000002431712222333527016711 0ustar kaufmannacl2 NOTE-2-7-OTHER.html -- ACL2 Version 6.3

    NOTE-2-7-OTHER

    ACL2 Version 2.7 Notes on Miscellaneous Changes
    Major Section:  NOTE-2-7
    

    Made several minor documentation improvements. We are grateful to Eric Smith for suggesting (most of) these.

    Improved (show-bdd) (see bdd) to give more useful feedback when there are ``leaf'' terms not known to be Boolean.

    Sped up processing of large mutual-recursion nests. In one large example the speedup was roughly two orders of magnitude.

    Modified event printing so that if both 'prove and 'event are inhibited, then events are no longer printed on behalf of certify-book, encapsulate, or defstobj. Thanks to Eric Smith for prompting consideration of such a change.

    The following technical change was made to support with-error-trace and wet (see note-2-7-new-functionality), but may be of interest to those who do low-level programming using the ACL2 logical world. The 'unnormalized-body property is now stored not only for functions defined in :logic mode, but also for functions defined by the user in :program mode. (:Program mode Functions built into ACL2 still have their 'unnormalized-body property omitted, in order to save space.)

    The handling of ``invisible'' functions for purposes of controlling rewriting (see loop-stopper) has been moved to a new table; see invisible-fns-table. Macros that access and modify this table are called ``...-invisible-fns-table'' in place of their former names, ``...-invisible-fns-alist.'' This feature was formerly implemented in the acl2-defaults-table, which prevented a book from exporting lists of invisible functions intended to work with the rewrite rules developed in the book. Thanks to Eric Smith and Rob Sumners for suggesting this change. See set-invisible-fns-table (formerly set-invisible-fns-alist), and also see add-invisible-fns and see remove-invisible-fns, which provides ways to incrementally add to and remove from this table, respectively. The handling of printing binary function call nests using macros (See add-binop) has also been moved out of the acl2-defaults-table as suggested by Eric and Rob, but this feature didn't work anyhow (see note-2-7-bug-fixes). Incidentally, the symbols binop-table, add-binop, and remove-binop have all been added to the list *acl2-exports* (see acl2-user), add-invisible-fns and remove-invisible-fns have been added to that list, and set-invisible-fns-alist has been replaced in that list by set-invisible-fns-table. Function invisible-fns-alistp is no longer defined and has been removed from *acl2-exports*.

    We now enforce the stated restriction on the pairings in macro-aliases-table (see macro-aliases-table), namely, that it associates names of macros with names of funcions (with respect to the current ACL2 logical world). We make a similar requirement on invisible-fns-table.

    The theory-invariant event has been modified so that the default action is an error rather than a warning. Thanks to Eric Smith for suggesting this change. Also, the value returned upon successful execution of a theory-invariant event is now the key.

    Proof output that reports destructor elimination no longer uses the word ``generalizing''. This small change may help in browsing proof output, since now ``generaliz'' takes you to true uses of generalization. Thanks to Matyas Sustik for suggesting such a change.

    The command :pl now prints an abbreviated controller-alist for ;definition rules. Formerly the output from :pl could be overwhelming when the supplied function was part of a large mutual-recursion nest.

    The defaults for keyword parameters of certify-book have changed. See note-2-7-bug-fixes, in particular, the discussion there of two modifications to certify-book.

    Technical changes have been made to compress1 and compress2 that should usually be invisible to users. The next paragraph describes them in detail, only for competeness (i.e., that description can be ignored by most users). But first, here is an example showing an effect on users. The slow array warning was not there previously. Notice that the warning only arises if the event form is changed. The solution is to be sure that redundant defconst forms are syntactically identical.

    ACL2 !>(defconst *a* (compress1 'demo
                                    '((:header :dimensions (5)
                                               :maximum-length 15
                                               :default uninitialized
                                               :name demo)
                                      (1 . one)
                                      (0 . zero))))
    
    Summary
    Form:  ( DEFCONST *A* ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     *A*
    ACL2 !>(aref1 'demo *a* 0)
    ZERO
    ACL2 !>(defconst *a* (compress1 'demo
                                    '((:header :dimensions (5)
                                               :maximum-length 15
                                               :default uninitialized
                                               :name demo)
                                      (1 . one)
                                      (0 . zero))))
    
    This event is redundant.  See :DOC redundant-events.
    
    Summary
    Form:  ( DEFCONST *A* ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     :REDUNDANT
    ACL2 !>(aref1 'demo *a* 0)
    ZERO
    ACL2 !>(defconst *a* (compress1 'demo
                                    '((:header :dimensions (5)
                                               :maximum-length 15
                                               :default uninitialized
                                               :name demo)
                                      (0 . zero)
                                      (1 . one))))
    
    This event is redundant.  See :DOC redundant-events.
    
    Summary
    Form:  ( DEFCONST *A* ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     :REDUNDANT
    ACL2 !>(aref1 'demo *a* 0)
    
    
    **********************************************************
    Slow Array Access!  A call of AREF1 on an array named
    DEMO is being executed slowly.  See :DOC slow-array-warning
    **********************************************************
    
    ZERO
    ACL2 !>
    

    As before, the von Neumann structure stored in the 'acl2-array property of the array name contains the array list object in its car. However, previously it was the case that compress1 and compress2 did not update that car when its new value would be equal to its old value. This was done largely in support of some type-set tables defined using defconst in type-set-b.lisp. The new versions of compress1 and compress2 are simpler in that no such exception is made in the case of equal lists, although instead the entire compression process is short-circuited when the input array list object is eq to the car of the 'acl2-array property. This change was made because the equality test was causing a ``slow array access'' warning to be printed in rare cases during proofs, as described elswhere (see note-2-7-bug-fixes).

    We no longer distribute documentation specific to Lucid Emacs. The Info documentation in directory doc/EMACS/ works well both for Gnu Emacs and XEmacs.

    A little-advertised macro, value, has long been allowed for top-level forms in books; see embedded-event-form. This has been replaced by a new macro, value-triple. The two have the same semantics at the top-level of books, where state is ``live''. However, value-triple should be used at the top-level of a book, while value should be used in function definitions (as before). This change eliminates a warning put out by the Allegro Common Lisp compiler for top-level value forms in books.




    acl2-sources/doc/HTML/NOTE-2-7-PROOF-CHECKER.html0000664002132200015000000000122612222333527017751 0ustar kaufmannacl2 NOTE-2-7-PROOF-CHECKER.html -- ACL2 Version 6.3

    NOTE-2-7-PROOF-CHECKER

    ACL2 Version 2.7 Notes on Proof-checker Changes
    Major Section:  NOTE-2-7
    

    Output from the proof-checker can now be inhibited by supplying the symbol proof-checker in the list given to set-inhibit-output-lst.




    acl2-sources/doc/HTML/NOTE-2-7-PROOFS.html0000664002132200015000000001454712222333527017044 0ustar kaufmannacl2 NOTE-2-7-PROOFS.html -- ACL2 Version 6.3

    NOTE-2-7-PROOFS

    ACL2 Version 2.7 Notes on Changes in Proof Engine
    Major Section:  NOTE-2-7
    

    An improvement in the linear arithmetic heuristics has been provided by Robert Krug. For information about this change, search for the comment in add-linear-lemma (file rewrite.lisp) that begins as follows.

    ; Previous to Version_2.7, we just went ahead and used the result of
    
    Thanks, Robert! Also thanks to Eric Smith for providing a motivating example.

    The non-linear-arithmetic addition (see non-linear-arithmetic) led to several small changes in the linear-arithmetic decision procedure (see linear-arithmetic). Two of these changes could affect existing proofs.

    First, when we are setting up the initial arithmetic database (which we call the ``pot-lst''), we have always scanned it to see if there were any pairs of inequalities from which we could derive a previously unknown equality. In some cases we added this equality to the clause and in others we used it to rewrite the clause, substituting one side of the equality for the other throughout the clause. Previously, the heuristics that we used to determine whether we performed the substitution differed from those used in several other places in the code. This has now been regularized, and similar heuristics are now used throughout the code.

    The second change to the linear-arithmetic decision procedure is that we now explicitly add inequalities derived from type reasoning to the pot-lst. Previously, we performed cancellations against these inequalities without adding them to the pot-lst. This change results in there being more inequalities in the pot-lst than before, and so more chances for there to be a pair of inequalities from which an equality can be derived. In effect, certain simple consequences of the current goal (see type-set) may now be added as hypotheses of the goal or used to peform equality substitutions.

    A slight improvement has been made to the way certain rewrite rules are stored. It was already the case that a rewrite rule rule whose conclusion C is not a call of a known equivalence relation (or eq, eql, or =) is stored as (iff C t), except that if ACL2 can determine (using its type-set mechanism) that C is Boolean, then the rule is stored as (equal C t). The iprovement is that if C and C' are Boolean, then a rule stated as (iff C C') is stored as (equal C C'). Thanks to Pete Manolios for providing an example that led us to consider this improvement.

    The heuristic use of equalities (fertilization) has been modified. Previously, ACL2 would sometimes substitute using an equality but keep the equality, and then undo the substitution by using the equality again. Now, when ACL2 keeps an equality after using it, it puts the equality inside a call of hide. Descendents of that goal that are unchanged by simplification will have this call of hide removed so that the equality can once again contribute to the proof. This change can cause some proofs to succeed that otherwise would fail. In the unlikely event that a proof fails that formerly succeeded, the following hint on "Goal" may fix the problem (see hints):

    :expand ((:free (x) (hide x)))
    

    We have refined the heuristics employed when an IF form is assumed true or false. Our previous attempt (see note-2-6-proofs for the original announcement) was not as general as we had believed. We have also improved some low-level code responsible for rewriting IF expressions. In earlier versions of ACL2, it was possible to have the truth or falsity of an IF expression explicitly recorded in the type-alist, and yet not use this information during rewriting. This problem has been corrected. Thanks to Robert Krug for noticing this problem and implementing the fix.

    We have sped up the rewriter in some cases where there are large collections of mutually-recursive functions (see mutual-recursion). (Implementation notes: technically, we have modified the way function being-openedp operates on the fnstack, and we have modified *current-acl2-world-key-ordering* as described in the essay above its definition.)

    Forward-chaining is now done in the preprocessing phase of proof attempts (see the discussion of :DO-NOT -- see hints). This is part of a technical change, made in support of translation of type declarations to guards (see note-2-7-guards). Previously, whenever ACL2 checked for built-in-clauses, it then looked for a contradiction using type-set reasoning if it did not find a suitable built-in clause. The change is to perform forward-chaining in such cases (i.e., when a built-in clause is not found).

    A couple of changes have been made in the generation of goals for forcing-rounds. Thanks to Eric Smith for bringing issues to our attention that led to these changes. For one, guards are no longer relevant in such goal generation. Formerly, the addition of a guard could make a proof fail that otherwise succeeded. Secondly, contextual information is now always kept when it involves a constrained constant, i.e., a zero-ary function introduced in the signature of an encapsulate.




    acl2-sources/doc/HTML/NOTE-2-7-RULES.html0000664002132200015000000000276512222333527016725 0ustar kaufmannacl2 NOTE-2-7-RULES.html -- ACL2 Version 6.3

    NOTE-2-7-RULES

    ACL2 Version 2.7 Notes on Changes in Rules and Constants
    Major Section:  NOTE-2-7
    

    The defcong macro has been slightly changed. The difference is that the variable generated with suffix -EQUIV will now be in the same package as the name of the variable from which it is generated, rather than always belonging to the ACL2 package. Thanks to Hanbing Liu for suggesting this change. (Note that a couple of books have been modified to accommodate this change, e.g., books/finite-set-theory/set-theory.)

    In Version_2.6, a change was made for rules of class :rewrite whose conclusion is a term of the form (EQV lhs rhs), where EQV is =, eq, or eql: the rule was stored as though EQV were equal. (See note-2-6-rules.) This change has been extended to rules of class :definition.




    acl2-sources/doc/HTML/NOTE-2-7-SYSTEM.html0000664002132200015000000000677412222333527017063 0ustar kaufmannacl2 NOTE-2-7-SYSTEM.html -- ACL2 Version 6.3

    NOTE-2-7-SYSTEM

    ACL2 Version 2.7 Notes on System-level Changes
    Major Section:  NOTE-2-7
    

    ACL2 now runs (once again) under LispWorks, specifically, LispWorks 4.2.0. However, we needed a patch, which presumably will be unnecessary after 4.2.7. From LispWorks support:

    Users with LispWorks4.2.7 should ask us at lisp-support@xanalys.com for the transform-if-node patch. It will be helpful if they quote (Lisp Support Call #11372) when doing so. Also, they must send a bug form generated from their LispWorks image: instructions at http://www.lispworks.com/support/bug-report.html.

    File books/Makefile-generic has been improved so that failed attempts to certify a book will cause the `make' to fail. Previously, an existing .cert file was left in place, and that sufficed for the `make' to be considered a success. Now, the old .cert file is first removed when recertification is found to be necessary.

    A change has been made to source file acl2.lisp to accommodate GCL 2.4.3. (ACL2 Version 2.6 does not work with some versions of GCL 2.4.3.)

    The error message has been improved when certain forms are typed to raw Lisp and the ACL2 loop has never been entered (with (LP)).

    The following symbols in the ACL2 package have been made untouchable, meaning that they are not available to the user: ev-fncall, ev, ev-lst, ev-acl2-unwind-protect, ev-fncall!, and user-stobj-alist-safe. The reason is that these functions can not be called safely except under certain restrictions. If you want to call the ACL2 evaluator, consider using the built-in system functions trans-eval or simple-translate-and-eval.

    CLISP Version_2.30 implements a notion of ``locking'' the "LISP" package that is incompatible with building ACL2. (CLISP Version_2.27 does not appear to have had this feature.) We have gotten around this problem by unlocking the "LISP" package in ACL2 images built on such CLISPs.

    Automatic proclaiming for GCL, which has (for a long time) been done for functions in compiled books, has been improved. Formerly, the only time a non-trivial output type (i.e., other than t) was inferred was when macroexpansion produced an explicit call of the. Now, if expressions can also generate non-t output types. Consider the following example.

    (defmacro the-fixnum (n)
      (list 'the '(signed-byte 29) n))
    (defmacro 1+f (x)
      (list 'the-fixnum
            (list '1+ (list 'the-fixnum x))))
    (defun foo (x)
      (declare (type (unsigned-byte 27) x))
      (if (zp x)
          0
        (1+f (foo (1-f x)))))
    
    Formerly, the proclaim forms for foo, before and after this improvement, are as shown below.
    (PROCLAIM '(FTYPE (FUNCTION ((UNSIGNED-BYTE 27)) T) FOO))                ;old
    (PROCLAIM '(FTYPE (FUNCTION ((UNSIGNED-BYTE 27)) (SIGNED-BYTE 29)) FOO)) ;new
    

    Compiler info messages sent to error stream were eliminated for CMUCL.




    acl2-sources/doc/HTML/NOTE-2-7.html0000664002132200015000000001407412222333526016030 0ustar kaufmannacl2 NOTE-2-7.html -- ACL2 Version 6.3

    NOTE-2-7

    ACL2 Version 2.7 (November, 2002) Notes
    Major Section:  RELEASE-NOTES
    

    The Version_2.7 notes are divided into the subtopics below. Here we give only a brief summary of a few of the changes that seem most likely to impact existing proofs. Not included in this brief summary, but included in the subtopics, are descriptions of improvements (including bug fixes and new functionality) that should not get in the way of existing proof efforts.

    In particular, please see note-2-7-new-functionality for discussion of a number of new features that you may find useful.

    Acknowledgements and elaboration, as well as other changes, can be found in the subtopics listed below.

    o Bug fixes (see note-2-7-bug-fixes):

    + Three soundness bugs were fixed. These bugs were probably rarely hit, so users may well not notice these changes.

    + Certify-book now requires :skip-proofs-ok t (respectively, :defaxioms-okp t) if there are skip-proofs (respectively, defaxiom) events in the book or any included sub-books.

    + When :by hints refer to a definition, they now use the original body of that definition rather than the simplfied (``normalized'') body.

    + When ld is applied to a stringp file name, it now temporarily sets the connected book directory (see cbd) to the directory of that file while evaluating forms in that file.

    o New functionality (see note-2-7-new-functionality):

    + ACL2 now works harder to apply :rewrite and :linear rules with free variables in the hypotheses. See note-2-7-new-functionality, in particular its first two paragraphs, for details. Forward-chaining also does more with free variables.

    o Changes in proof engine (see note-2-7-proofs):

    + Some prover heuristics have changed slightly. Among other consequences, this can cause subgoal hints to change. For example, suppose that the Version_2.6 proof of a particular theorem generated "Subgoal 2" and "Subgoal 1" while Version_2.7 only generates the second of these. Then a subgoal hint attached to "Subgoal 1" in Version_2.6 would have to be attached to "Goal'" in Version_2.7. (See goal-spec.) The full topic has details (see note-2-7-proofs).

    o Changes in rules and definitions (see note-2-7-rules):

    + The package name of a generated variable has changed for defcong.

    o Guard-related changes (see note-2-7-guards):

    + Guard verification formerly succeeded in a few cases where it should have failed.

    + Guards generated from type declarations now use functions signed-byte-p and unsigned-byte-p, now defined in source file axioms.lisp and formerly defined rather similarly under books/ihs/.

    o Proof-checker changes (see note-2-7-proof-checker):

    + See the above doc topic.

    o System-level changes (see note-2-7-system):

    + See the above doc topic.

    o Other changes (see note-2-7-other):

    + A new table, invisible-fns-table, takes the place of the handling of invisible functions in the acl2-defaults-table,

    + The theory-invariant event has been modified so that the default action is an error rather than a warning.

    + Proof output that reports destructor elimination no longer uses the word ``generalizing''.

    Again, please proceed to the subtopics for more thorough release notes.

    Some Related Topics




    acl2-sources/doc/HTML/NOTE-2-7_lparen_R_rparen_.html0000664002132200015000000000233312222333527021354 0ustar kaufmannacl2 NOTE-2-7_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-2-7(R)

    ACL2 Version 2.7(r) (November, 2002) Notes
    Major Section:  RELEASE-NOTES
    

    In source file axioms.lisp, in order for proofs to succeed, (make proofs), the definitions of acl2-count and explode-atom have been modified slightly, and lemma standard-numberp-one [modified after Version_3.4 to become standardp-one] has been given :rule-classes nil.

    All skip-proofs forms have been eliminated from the nonstd books, thanks to Ruben Gamboa.

    The directory books/sqrt/, which was intended for ACL2(r), has been moved to books/nonstd/sqrt/ and added as appropriate to books/nonstd/Makefile.

    Please see note-2-7 for changes to Version_2.7 of ACL2.




    acl2-sources/doc/HTML/NOTE-2-8-BUG-FIXES.html0000664002132200015000000005035312222333527017321 0ustar kaufmannacl2 NOTE-2-8-BUG-FIXES.html -- ACL2 Version 6.3

    NOTE-2-8-BUG-FIXES

    ACL2 Version 2.8 Notes on Bug Fixes
    Major Section:  NOTE-2-8
    

    We have fixed a soundness bug in the tautology checker's handling of expressions of the form (not (not x)). This bug has gone back at least as far as Version_2.4. All of the regression tests passed after the fix, without modification. So we hope that this bug has rarely bitten anyone. Thanks to Qiang Zhang for sending us a proof of nil that led us to this fix: (thm (equal (and p q) (not (or (not p) (not q))))). And thanks to Matyas Sustik for an observation that led to an improvement of our initial fix.

    The preceding version (2.7) introduced a soundness bug in handling of ACL2 arrays, in which functions compress1 and compress2 were returning the input alist rather than compressing it appropriately. Here is a proof of nil that no longer succeeds, based on a bug report from Warren Hunt, who we thank for bringing this problem to our atttention.

    (defthm bad
      (not (let* ((ar2 (aset1 'my-array ar1 3 10))
                  (ar3 (compress1 'my-array ar2))
                  (ar4 (reverse (reverse ar2)))
                  (ar5 (compress1 'my-array ar4)))
             (and (equal ar2 ar4)
                  (not (equal ar3 ar5)))))
      :rule-classes nil)
    (defthm contradiction
      nil
      :rule-classes nil
      :hints (("Goal" :use
               ((:instance bad
                           (ar1 (compress1 'my-array
                                           '((3 . 5)
                                             (:HEADER :DIMENSIONS (5)
                                                      :MAXIMUM-LENGTH 6
                                                      :DEFAULT 0
                                                      :NAME MY-ARRAY)))))))))
    
    On a related note, a new function flush-compress can be used for subtle control of under-the-hood raw Lisp support for fast array access, although we expect it to be very rare that users need this extra support.

    Previous versions have had two soundness bugs that can occur when using the proof-checker:

    o The first bug pertains to the expand command, and hence x and x-dumb commands (which call expand); see proof-checker-commands. The bug can occur when applying the above commands when the current term is a call of a constrained function symbol for which there is a :definition rule. Now, the expand command will succeed only when the function symbol of the current term is a defined function symbol, in which case the original definition is always used, in analogy to how the :expand hint works in the prover; see hints. Thanks to John Erickson for sending an example that led us to wonder if there might be a soundness problem.

    o The second bug pertains to the s command (and commands that call it, e.g., s-prop). The proof-checker forms a context out of the top-level hypotheses and the if-terms governing the current term. If there is a contradiction in the top-level hypotheses, the proof-checker can appropriately consider the goal to be proved, and it does so. But formerly, the criterion was weaker: the contradiction could involve the combination of the top-level hypotheses and if-term governors. Thanks to Rob Sumners for noticing this bug.

    A soundness bug could be provoked in some Lisps by applying defpkg to the empty string. This has been disallowed.

    We fixed a soundness bug related to packages caused by a failure to track axioms introduced locally on behalf of defpkg events. See hidden-death-package.

    We fixed a soundness bug caused by a failure to check that a :type-prescription rule can be processed when proofs are skipped or under a defequiv event. The former case can occur when processing an encapsulate or include-book event, where the rule could depend on a local :compound-recognizer rule preceding the proposed :type-prescription rule under the same encapsulate or include-book event. See local-incompatibility for such an example.

    We fixed a potential soundness bug relating to reclassifying a :program mode function to :logic mode (as done by verify-termination or the submission of an appropriate ``redundant'' definition) without adequate checking that stobj usage was identical. Allegedly redundant definitions must now preserve the stobjs declaration as well as the formals, body, guard and type declarations. We thank Vernon Austel for pointing out this problem.

    It was possible to get a raw Lisp error by introducing a locally defined function with guard verification inhibited and then subsequently introducing the same definition non-locally without that inhibition. The following example will clarify.

    (encapsulate nil
      (local
        (defun foo (x) (declare (xargs :guard t :verify-guards nil)) (car x)))
      (defun foo (x) (declare (xargs :guard t)) (car x)))
    ; The following causes a raw lisp error because ACL2 runs the Common Lisp
    ; definition of foo, because it thinks that foo's guard of t was verified.
    (thm (equal (foo 3) xxx))
    
    Thanks to Jared Davis for bringing this problem to our attention. We are particularly grateful to Jared because his example exploited this bug by applying it to a function defined using mbe (introduced in this same version, 2.8), in order to prove nil!

    The sort of error message shown below can legitimately occur when certifying a book in a certification world where there was an include-book command with a relative pathname (see pathname). However, it was occurring more often than necessary. This has been fixed.

    ACL2 Error in (CERTIFY-BOOK "foo" ...): The certification world has include-book commands for book "bar" that correspond to different full pathnames, namely "/u/dir1/bar" and "/u/dir2/bar". ACL2 cannot currently certify a book in such a world. To work around this problem, use an absolute pathname for at least one of these books (see :DOC pathname).

    Bugs were fixed in with-output, in particular related to the use of values :all. Also, documentation for with-output has been improved. Thanks to Vernon Austel for pointing out the bugs.

    Fixed a lisp error occurring when bash proof-checker command was given illegal syntax, e.g., (bash (("Goal" :in-theory (enable binary-append)))) instead of (bash ("Goal" :in-theory (enable binary-append))).

    We added an appropriate guard to find-rules-of-rune, which will avoid hard lisp errors when this function is called on non-rune arguments. Thanks to Eric Smith for pointing out this issue.

    It was possible for a redundant include-book form (see redundant-events) to leave a command in the ACL2 logical world and to cause (re-)loading of a compiled file. These behaviors have been fixed. In particular, if book1 has already been included in the current ACL2 world and (include-book "book1") occurs in book2, then the compiled file for book1 will not be loaded again when book2 is included. Thanks to Dave Greve for bringing our attention to these problems, and to Eric Smith for bringing up a special case earlier (where "//" occurred in the book name).

    The summary printed at the end of a proof had not listed :induction rules used in a proof. This has been corrected.

    The use of proof trees in emacs redefined `control-c control-c' in such a way that in telnet mode, the telnet session was interrupted and perhaps could not be continued. This has been fixed.

    Source function load-theory-into-enabled-structure contained a guard-violating call of compress1. Thanks to Vernon Austel for bringing this problem to our attention; even though this bug was benign (as he pointed out), we like keeping the source code free of guard violations.

    A number of proof-checker atomic macros caused a hard error when all goals have already been proved. This has been fixed. Thanks to John Erickson for sending an example of the issue.

    A bug has been fixed in add-match-free-override. Formerly, a table guard violation occurred when calling add-match-free-override more than once with first argument other than :clear.

    Defininitions of functions involving large constants could cause stack overflows. This has been fixed, at least in some of the most egregious cases (by making a source function fn-count-evg tail-recursive). Thanks to Jared Davis for bringing this problem to our attention.

    Evaluation of computed hints could cause stack overflows. This has been fixed. Thanks to Eric Smith for bringing this problem to our attention.

    Evaluation of :monitor on :definition runes is now fast even if the specified function is part of a very large mutual-recursion nest. Thanks to Eric Smith for sending an example showing that this wasn't always the case.

    Fixed a bug in books/bdd/cbf.lisp that was causing certification of distributed bdd books to fail when the connected book directory (see cbd) differs from the current working directory. Thanks to Scott Guthery for bringing this bug to our attention and supplying a helpful log.

    Duplicate rule names have been eliminated from warnings generated upon the use of enabled :rewrite or :definition rules. Thanks to Eric Smith for pointing out this problem.

    The trace utilities (see trace), as modified for GCL and Allegro Common Lisp, had failed to show more than the first return value for so-called ``*1*'' functions (essentially, executable-counterpart functions) when they were returning multiple values (via mv). This has been fixed. Thanks to Erik Reeber for pointing out this problem. Also, it is now possible to refer to arglist in trace$ forms when ACL2 is built on GCL, not just when ACL2 is built on Allegro Common Lisp.

    Uses of hide introduced during proofs by failed attempts to evaluate constrained functions (see hide) are now tracked, so that the rune (:DEFINITION HIDE) will show up in the summary.

    The following bug, introduced back in Version 2.7, has been fixed. The bug applied only to GCL and may well not have affected anyone. But the function proclamation computed by ACL2 for compilation usually had an output type of nil where it should have been t.

    The macro gc$ had a bug exhibited when it was supplied one or more arguments. This has been fixed.

    The macro defabbrev broke when supplied a string and no documentation, e.g., (defabbrev foo () ""). Thanks to Rob Sumners for noticing this problem and providing a fix, which we have incorporated.

    For ACL2 executables built on Allegro Common Lisp, a Lisp error occurred when trace$ was called on other than a defined function symbol. Now ACL2 prints a more useful error message.

    The proof-checker no longer accepts a (verify) command when some function symbol in the original goal no longer exists in the current ACL2 logical world. Thanks to John Erickson for bringing this issue to our attention.

    The function ld-redefinition-action may now be called by the user. Thanks to Vernon Austel for suggesting that we remove this symbol from the list of so-called untouchables.

    The handling of free variables in hypotheses (see free-variables) of rewrite and linear rules had a bug that prevented some proofs from going through. Here is a simple example, essentially provided by Diana Moisuc, who we thank for bringing this issue to our attention. The proof of the thm below had failed, but now will succeed. This particular bug prevented, for example, the :all behavior from occurring when the first hypothesis of the rule does not have free variables. NOTE: Now that this bug has been fixed, you may find some proofs running much more slowly than before. You can use accumulated-persistence to locate rules that are slowing down your proofs because of excessive attention to free variables, and then execute add-match-free-override for those rules (or, just change the rules themselves to specify :once in the :rule-classes).

    (defstub foo1 (* ) => *)
    (skip-proofs
     (defthm aux-foo1
       (implies (and (integerp a)
                     (integerp i)
                     (equal (foo1 0)  (list 0 i)))
                (equal (foo1 a) (list 0 (+ a i))))
       :rule-classes ((:rewrite :match-free :all))))
    (thm
     (implies (and (integerp i)
                   (integerp a)
                   (equal (foo1 0) (list 0 i)))
              (equal (foo1 a) (list 0 (+ a i)))))
    

    Formerly, creation of large arrays could cause an error in the underlying Common Lisp implementation without helpful messages for the user. Now, we check Common Lisp restrictions on arrays and print a helpful error message if they are violated, namely: each dimension must be less than the value of Common Lisp constant array-dimension-limit, and the product of the dimensions must be less than the value of Common Lisp constant array-total-size-limit. Thanks to Warren Hunt for bringing this issue to our attention. Note: this change also removes a former restriction of stobj array fields to size smaller than 2^28-1, provided the underlying Lisp can support larger arrays.

    The default-hints in the current logical world were ignored by verify-guards. This has been fixed. Thanks to Jared Davis for pointing out this bug and sending a helpful example.

    The brr mechanism has been cleaned up in order to avoid hard errors and infinite loops that can arrive when typing interrupts (control-c) or end-of-files (control-d) inside the brr loop. Thanks to Dave Greve, Olga Matlin, Eric Smith, and Serita Van Groningen for bringing this issue to our attention. As a byproduct, if you type control-d (or if inside emacs, control-c control-d), you may now quit entirely out of ACL2 and lisp (see good-bye) in some cases where you formerly would not have, for example when sitting at the ACL2 prompt (which formerly, in Allegro Common Lisp for example, would merely take you into raw Lisp rather than quitting everything).

    We have eliminated structural flaws in the HTML documentation pages that could make them unreadable in some browsers. Thanks to Bill Young for bringing this issue to our attention and to Joe Hendrix for diagnosing the problem.

    The proof-checker could run very slowly after many instructions in a given session. This has been fixed; thanks to Art Flatau for bringing this problem to our attention. (Implementation detail: We now keep tag-trees duplicate-free when we accumulate them into state. This change could have minor speed advantages for some top-level proofs too, not just in the proof-checker.)

    The printing of accesses to stobjs using nth or update-nth has been done using symbolic constants since ACL2 Version_2.6. However, there was a bug that prevented this feature from working for update-nth except at a top-level call. This has been fixed. Thanks to Julien Schmaltz for bringing this problem to our attention. For example, consider these events:

    (defstobj st field0 field1)
    (thm (equal (nth 1 (update-nth 0 17 st)) (car (cons xxx yyy)))
         :hints (("Goal" :in-theory (disable nth update-nth))))
    
    Before the fix, the proof attempt of the above silly thm printed the following.
    (NTH 1 (UPDATE-NTH *FIELD0* 17 ST))
    
    After the fix, we instead see the following.
    (NTH *FIELD1* (UPDATE-NTH *FIELD0* 17 ST))
    

    It is now possible to certify and subsequently include books that require guard-checking to be off. For example, the book can contain the form (defconst *silly* (car 3)) even though 3 fails to satisfy the guard of car. Formerly, it was necessary to execute :set-guard-checking nil before a certify-book or include-book in order for such a form to be handled without error. Thanks to Hanbing Liu for bringing this problem to our attention.

    Fixed a proof-checker bug that could cause probably cause strange error, ``Attempt to access the plist field''. Thanks to Bill Young for bringing this problem to our attention.

    Fixed a proof-checker bug that was failing to record applications of rewrite rules using the proof-checker's :rewrite command, causing the proof summary to omit mention of that rule (for example, when using the proof-checker's :exit command to generate an :instructions hint). Thanks to Bill Young for pointing out this bug.

    Modernized some of the proof-tree emacs and infix printing stuff, thanks to suggestions made by Camm Maguire.




    acl2-sources/doc/HTML/NOTE-2-8-GUARDS.html0000664002132200015000000000105512222333527017010 0ustar kaufmannacl2 NOTE-2-8-GUARDS.html -- ACL2 Version 6.3

    NOTE-2-8-GUARDS

    ACL2 Version 2.8 Notes on Guard-related Changes
    Major Section:  NOTE-2-8
    

    All the guard-related changes may be found elsewhere; in particular, see note-2-8-bug-fixes.




    acl2-sources/doc/HTML/NOTE-2-8-NEW-FUNCTIONALITY.html0000664002132200015000000002376712222333527020520 0ustar kaufmannacl2 NOTE-2-8-NEW-FUNCTIONALITY.html -- ACL2 Version 6.3

    NOTE-2-8-NEW-FUNCTIONALITY

    ACL2 Version 2.8 Notes on New Functionality
    Major Section:  NOTE-2-8
    

    WARNING: You may find that control-d (in emacs, control-c control-d) can throw you completely out of Lisp where it had not formerly done so.

    (CLISP and Allegro CL only) ACL2 now starts up inside the ACL2 loop -- that is, (LP) is executed automatically -- when built on CLISP or Allegro CL. This was already the case for GCL and CMUCL, and it still is not true for LispWorks. Thanks to Joe Corneli for bringing the CLISP command-line option "-i" to our attention, which led to this CLISP change and inspired reconsideration of how to do this for Allegro CL.

    Pete Manolios and Daron Vroon have changed the representation of ordinals in ACL2, defined algorithms for ordinal arithmetic, and created a library of theorems to reason about ordinal arithmetic. We thank them for these nice contributions. See note-2-8-ordinals for details, in particular, for how to preserve existing proofs that depend on the previous ordinal representation.

    Sometimes users create rules of class :rewrite that cause an infinite loop in the ACL2 rewriter. This has lead to Lisp stack overflows and even segmentation faults. Now, the depth of calls of functions in the ACL2 rewriter is limited, and under user control. See rewrite-stack-limit.

    Macros mbe (``must be equal'') and mbt (``must be true'') have been introduced, which allow the user to attach fast executable definitions to (presumably slower) :logic mode functions. Thanks to Vernon Austel for a key idea. Also provided is a macro defexec, which employs mbe but enforces the requirement that the executable definition also terminates. Thanks to Jose Luis Ruiz Reina for collaborating in the design and development of defexec, and for useful comments from a number of others as well in the development of mbe including Joe Hendrix and Rob Sumners.

    Definitions have been added for functions rassoc-eq and rassoc-equal, which are like rassoc but use different tests and have different guards. (Compare assoc-eq and assoc-equal, which are in similar relation to assoc.)

    The user can now control multiple matching for free variables in hypotheses for :forward-chaining rules, as has already been supported for :rewrite and :linear rules. For :forward-chaining rules, ``free variables'' are those in the hypotheses not bound by a given trigger term. As for :rewrite and :linear rules, free-variable matching may be limited to the first successful attempt by specifying :match-free :once with :forward-chaining in the :rule-classes, and add-match-free-override may be used to modify the behavior of an existing rule. Thanks to Erik Reeber for most of the implementation of these new capabilities, as well as significant assistance with a corresponding new documentation topic (see free-variables-examples-forward-chaining).

    It is no longer necessary to specify (set-match-free-error nil) in order to avoid errors when a rule with free variables in its hypotheses is missing the :match-free field. (This was already true during book certification, but now it is the case in interactive sessions as well.)

    The form (break-on-error) causes, at least for most Lisps, entry into the Lisp debugger whenever ACL2 causes an error. See break-on-error. Thanks to John Erickson for providing encouragement to provide this feature.

    A new table has been provided so that advanced users can override the built-in untranslate functionality. See user-defined-functions-table.

    The pstack mechanism (formerly denoted checkpoints) has been improved. The ``process [prover] stack,'' or pstack, is automatically printed when proofs abort. Evaluation of function calls on explicit arguments during proofs is now tracked. Actual parameters are shown with (pstack t) rather than formals. Thanks to Bill Legato for suggesting the first two of these improvements and, in general, encouraging changes that make ACL2 easier to use.

    The defstobj event is now allowed to take an :inline argument, which can speed up execution. Thanks to Rob Sumners for suggesting and implementing this new feature.

    Macro assert$ has been added in order to make it easy to write assertions in one's code. Semantically, (assert$ test form) is the same as form, but it causes a hard error (using illegal) if test evaluates to nil.

    Macro cw-gstack no longer takes arguments for the gstack or state. However, it now takes a keyword argument (which is optional), :evisc-tuple, that can be used to control how it prints terms. In particular, cw-gstack abbreviates large terms by default, but (cw-gstack :evisc-tuple nil) causes terms to be printed in full. Thanks to Robert Krug and Eric Smith for requesting this improvement.

    The advanced user now has more control over the evisceration of terms. See ld-evisc-tuple, in particular the new paragraph on ``The printing of error messages and warnings.''

    The include-book event now has an additional (optional) keyword, :dir. The value of :dir should be a keyword that is associated with an absolute directory pathname to be used in place of the current book directory (see cbd) for resolving the first argument of include-book to an absolute pathname. At start-up, the only such keyword is :system, so that for example (include-book "arithmetic/top" :dir :system) will include the book "arithmetic/top" under the "books/" directory of your ACL2 installation. But you can associate ``projects'' with keywords using add-include-book-dir, e.g., (add-include-book-dir :my-project "/u/smith/project0/"). See add-include-book-dir and also see delete-include-book-dir and see include-book. Note: You will probably not find :dir :system to be useful if the distributed books are not placed in the path of their original location, pointed to by :dir :system, which will often happen if the executable image is obtained from another site. Also see include-book, in particular its ``soundness warning''.

    The printing of results in raw mode (see set-raw-mode) may now be partially controlled by the user: see add-raw-arity. Also, newlines are printed when necessary before the value is printed.

    For those using Unix/Linux `make': A cert.acl2 file can contain forms to be evaluated before an appropriate certify-book command is invoked automatically (not included in cert.acl2).

    Jared Davis has contributed a new set of books for ordered finite set theory to the standard distribution, books/finite-set-theory/osets-0.81/. See the README file in that directory. Thanks, Jared.

    Robert Krug has contributed two related changes (thanks, Robert!) in support of stronger arithmetic reasoning. First, one can now enable and disable nonlinear arithmetic with a :nonlinearp hint, which will override the default provided by set-non-linearp (initially, nil). See hints. Second, computed-hints can now have access to the HISTORY, PSPV, and CTX variables of the waterfall, which (for example) allows the writing of a hint which will enable nonlinear arithmetic on precisely those goals that are stable-under-simplificationp. See computed-hints.

    Robert Krug has contributed a new set of arithmetic books to the standard distribution, books/arithmetic-3/. See the README file in that directory. Thanks, Robert.




    acl2-sources/doc/HTML/NOTE-2-8-ORDINALS.html0000664002132200015000000000073212222333527017237 0ustar kaufmannacl2 NOTE-2-8-ORDINALS.html -- ACL2 Version 6.3

    NOTE-2-8-ORDINALS

    ACL2 Version 2.8 Notes on Changes to the Ordinals
    Major Section:  NOTE-2-8
    

    Please see ordinals.




    acl2-sources/doc/HTML/NOTE-2-8-OTHER.html0000664002132200015000000000646212222333527016713 0ustar kaufmannacl2 NOTE-2-8-OTHER.html -- ACL2 Version 6.3

    NOTE-2-8-OTHER

    ACL2 Version 2.8 Notes on Miscellaneous Changes
    Major Section:  NOTE-2-8
    

    Execution of table events has been sped up in many cases by avoiding excessive consing.

    ACL2 now warns if :rewrite (or :definition) rules contain free variables on the right-hand side. Thanks to Dave Greve for raising this issue.

    Emacs file emacs/emacs-acl2.el has been updated to better comprehend the notion of the ``ACL2 shell'', which is the buffer to which ACL2 forms are written by commands defined in the above file. Thus, command control-t e has been modified always to write to the ACL2 shell (which is "*shell*" by default), and the following new commands have been defined.

    o control-t c
    Set the ACL2 shell to the current buffer. o control-t b
    Change to the ACL2 shell.

    The commands :pl and :pr may now be given a macro name that corresponds via the macro-aliases-table to a function name, so that for example :pl append is treated the same as :pl binary-append. A more interesting improvement, for :pl only, is that :pl may now take any term. When :pl is given a term other than a symbol, it will print all rewrite rules that match that term. Thanks to David Russinoff, Robert Krug, and Bill Legato for getting this going.

    A new function, pkg-witness, returns a symbol in the given package.

    The installation instructions have been updated, for example to give more guidance on obtaining Lisp implementations and to mention the acl2-help mailing list.

    Jared Davis has suggested some symbols to be added to *acl2-exports*, and we have done so. Thanks, Jared.

    o MFC (used in syntaxp and extended-metafunctions; thanks also to Robert Krug for this one) o ID, CLAUSE, WORLD, and STABLE-UNDER-SIMPLIFICATIONP (used in computed-hints) o SET-DEFAULT-HINTS

    The command :pe has been improved so that when the event is inside an included book, the path of included books (from the top-level book down to the one containing the event) is shown. Thanks to Eric Smith (perhaps among others) for pointing out the utility of this improvement.

    A new release of the rtl library has been included: books/rtl/rel4/. See the README file in that directory.




    acl2-sources/doc/HTML/NOTE-2-8-PROOF-CHECKER.html0000664002132200015000000000442712222333527017760 0ustar kaufmannacl2 NOTE-2-8-PROOF-CHECKER.html -- ACL2 Version 6.3

    NOTE-2-8-PROOF-CHECKER

    ACL2 Version 2.8 Notes on Proof-checker Changes
    Major Section:  NOTE-2-8
    

    Added new proof-checker commands wrap1, wrap, and wrap-induct. Wrap replaces multiple goals by their conjunction: (wrap instr1 instr2 ...) employs wrap1 so that the indicated instructions create only at most one new goal. Wrap-induct is a simple example of the use of wrap, so that induction creates only one goal (the conjunction of the base and induction steps). Wrap1 can be used immediately after a prover call (bash, prove, reduce, bdd, or induct) to collapse the new goals into one. See proof-checker-commands.

    The proof-checker command = failed to work as expected when a governing IF-test of the current term is T. This has been fixed (by fixing source function conjuncts-of). Thanks to Yoann Padioleau for bringing this problem to our attention.

    The type-alist command now takes optional arguments that control whether or not the governors and/or conclusion are used in computing the context that is printed (see proof-checker-commands, specifically subtopic type-alist). Thanks to Rob Sumners for suggesting this improvement.

    The macro toggle-pc-macro has always taken an optional second argument of atomic-macro or macro. However, this was not clearly documented, and those two symbols had to be in the ACL2 package. Both of these problems have been remedied. Thanks to John Erickson for bringing the lack of documentation of the second argument to our attention.




    acl2-sources/doc/HTML/NOTE-2-8-PROOFS.html0000664002132200015000000001216212222333527017034 0ustar kaufmannacl2 NOTE-2-8-PROOFS.html -- ACL2 Version 6.3

    NOTE-2-8-PROOFS

    ACL2 Version 2.8 Notes on Changes in Proof Engine
    Major Section:  NOTE-2-8
    

    ACL2 now prevents certain rewriting loops; see rewrite-stack-limit.

    During the computation of constraints for functional instantiation, (prog2$ term1 term2) and (the type term2) are now treated as term2.

    A change has been made in heuristics for controlling rewriting during proofs by induction. Formerly, during induction proofs, ACL2 suppressed rewriting of certain ``induction hypothesis'' terms, and forced expansion of certain ``induction conclusion'' terms, until rewriting had stabilized. This meddling with the rewriter is still turned off when rewriting has stabilized, but it is now turned off earlier once an ancestor has been through the rewriter and the current goal is free of ``induction conclusion'' terms. Thanks to Dave Greve and Matt Wilding for providing an example and associated analysis that led us to look for a heuristic modification.

    A change has been made in the heuristics for handling certain ``weak'' compound-recognizer rules when building contexts. Those who want to dig deeply into this change are welcome to look at the code following the call of most-recent-enabled-recog-tuple in the code for function assume-true-false in the ACL2 sources.

    The handling of free variables in a hypothesis of a rewrite rule (see free-variables) has been improved in the case that the hypothesis is of the form (equiv x y), where equiv is a known equivalence relation (see equivalence). Previously, if the rewriter was attempting to rewrite the hypothesis (equiv x y) of a rewrite rule, in a context where x' is an instance of x, then the rewriter could fail to notice a term (equiv x' y') true in the current context where y' is an instance of y, in the case that x' precedes y' in the term-order. This has been remedied. This improvement applies regardless of whether x, y, or (we believe) both are already fully instantiated in the present context. Thanks to Joe Hendrix for bringing up an example and to Vernon Austel for providing another, simple example.

    A very minor change has been made to the rewriter in the case that an equality appears on the left-hand side of a :rewrite rule. Formerly, when such an equality (equal x y) was commuted to (equal y x) in order for the rule to match the current term, then all equalities on the instantiated right-hand side of the rule were commuted, except for those occurring inside another equality. The instantiated right-hand side is no longer modified. It seems very unlikely that this change will cause proofs to fail, though we cannot completely rule out that possibility.

    We have modified how the ACL2 simplifier handles the application of a defined function symbol to constant arguments in certain cases, which we now describe. As before, ACL2 attempts to simplify such a function application by evaluation, provided the :executable-counterpart of the function is enabled. And as before, if that evaluation fails due to a subroutine call of a constrained function (introduced by encapsulate), ACL2 may wrap a call of hide around this function application. (See hide.) But now, ACL2 attempts to apply definitions and rewrite rules in the case that this evaluation fails, and only if the resulting term is unchanged does ACL2 wrap hide around this function application. Thanks to Matt Wilding for bringing up the idea of this modification.

    The generation of "Goal" for recursive (and mutually-recursive) definitions now uses the subsumption/replacement limitation (default 500). See case-split-limitations.

    Default hints now apply to hints given in definitions, not just theorems. See default-hints.

    Thanks to Robert Krug for implementing the following two improvements involving linear arithmetic reasoning: linear arithmetic now uses the conclusions of forward-chaining rules, and type-set now uses a small amount of linear reasoning when deciding inequalities.




    acl2-sources/doc/HTML/NOTE-2-8-RULES.html0000664002132200015000000000455012222333527016720 0ustar kaufmannacl2 NOTE-2-8-RULES.html -- ACL2 Version 6.3

    NOTE-2-8-RULES

    ACL2 Version 2.8 Notes on Changes in Rules, Definitions, and Constants
    Major Section:  NOTE-2-8
    

    The theory minimal-theory has been changed by adding the definition rune for mv-nth to the theory. A corresponding change has been made to the theory warning mechanism, which was failing to warn if the definition of mv-nth is disabled, even though calls of mv-nth can be expanded by special-purpose code in the rewriter. Thanks to Serita Van Groningen for pointing out this problem with the theory warning mechanism.

    The defevaluator event has been modified so that in the body of the evaluator function, to add a new case (ATOM X) (returning nil) has been inserted immediately after the case (EQ (CAR X) 'QUOTE). This is a no-op semantically but may speed up proofs. Thanks to Warren Hunt for suggesting this change.

    A new form of :compound-recognizer rule is now allowed:

    (if (fn x) concl1 concl2)
    
    This is equivalent to an existing form:
    (and (implies (fn x) concl1)
         (implies (not (fn x)) concl2))
    
    Thanks to Josh Purinton for bringing this to our attention.

    Rewrite rules realpart-+ and imagpart-+ have been added in order to simplify the realpart and imagpart (respectively) of a sum. They follow from a theorem add-def-complex that equates a sum with the complex number formed by adding real and imaginary parts. All three of these theorems may be found in source file axioms.lisp. Thanks to Eric Smith for raising a question leading to these additions, as well as to Joe Hendrix and Vernon Austel for helpful suggestions.




    acl2-sources/doc/HTML/NOTE-2-8-SYSTEM.html0000664002132200015000000000265712222333527017060 0ustar kaufmannacl2 NOTE-2-8-SYSTEM.html -- ACL2 Version 6.3

    NOTE-2-8-SYSTEM

    ACL2 Version 2.8 Notes on System-level Changes
    Major Section:  NOTE-2-8
    

    ACL2 now runs on OpenMCL, ``an opensourced Common Lisp implementation, derived from Digitool's Macintosh Common Lisp product.'' Thanks to Greg Wright and Robert Krug for doing most of the work for this port.

    When (LP) is first executed, the underlying raw Lisp package will change to "ACL2" (if that is not already the current package in raw Lisp). This is a minor change that will probably not be noticed, since up to now it has probably been the case that the ACL2 executable starts up with "ACL2" as the underlying raw Lisp package. But this change was made because we have been informed that ACL2 executables based on OpenMCL need not start up with "ACL2" as the underlying raw Lisp package.

    ACL2 now runs on MCL 5.0. Thanks to Pascal Costanza for updates to the instructions in file mcl-acl2-startup.lisp and for an update to the ACL2 sources (parameter *compiled-file-extension*).




    acl2-sources/doc/HTML/NOTE-2-8.html0000664002132200015000000002476112222333527016036 0ustar kaufmannacl2 NOTE-2-8.html -- ACL2 Version 6.3

    NOTE-2-8

    ACL2 Version 2.8 (March, 2004) Notes
    Major Section:  RELEASE-NOTES
    

    BRIEF SUMMARY.

    The Version_2.8 notes are divided into the indicated subtopics. Here we give only a brief summary of just a few of the major new features and changes that seem most likely to impact existing proofs. Not included in this brief summary, but included in the subtopics, are descriptions of many improvements (including bug fixes and new functionality) that should not get in the way of existing proof efforts. In the description below we also omit discussion of changes that will become clear by way of error messages if they affect you.

    In particular, please see note-2-8-new-functionality for discussion of a number of new features that you may find useful.

    Acknowledgements and elaboration, as well as other changes, can be found in the subtopics listed below.

    o Some of the bug fixes (see note-2-8-bug-fixes):

    + Some soundness bugs were fixed.

    + The handling of free variables in hypotheses (see free-variables) of rewrite and linear rules had a bug that prevented some proofs from going through. Now that this bug has been fixed, you may find some proofs running much more slowly than before. You can use accumulated-persistence and add-match-free-override to remedy this situation; see note-2-8-bug-fixes for details.

    + The default-hints in the current logical world are no longer ignored by verify-guards.

    + Forms violating guard-checking such as (defconst *silly* (car 3)) are now allowed in books.

    o Some of the new functionality (see note-2-8-new-functionality):

    + WARNING: You may find that control-d (in emacs, control-c control-d) can throw you completely out of Lisp where it had not formerly done so.

    + ACL2 now starts up inside the ACL2 loop -- that is, (LP) is executed automatically -- when built on CLISP or Allegro CL. This was already the case for GCL and CMUCL, and it still is not true for LispWorks.

    + See note-2-8-ordinals for a discussion of a significant change in ordinal represtation, and in particular, for how to preserve existing proofs that depend on the previous ordinal representation.

    + Macros mbe (``must be equal''), mbt (``must be true''), and defexec have been introduced, which allow the user to attach alternate executable definitions to functions.

    + The user can now control multiple matching for free variables in hypotheses for :forward-chaining rules, as has already been supported for :rewrite and :linear rules.

    + It is no longer necessary to specify (set-match-free-error nil) in order to avoid errors when a rule with free variables in its hypotheses is missing the :match-free field.

    + The form (break-on-error) causes, at least for most Lisps, entry into the Lisp debugger whenever ACL2 causes an error.

    + A new table has been provided so that advanced users can override the built-in untranslate functionality. See user-defined-functions-table.

    + The pstack (`process [prover] stack'') mechanism, formerly denoted checkpoints, has been improved. One of these improvements is to show actual parameters with (pstack t) rather than formals.

    + The defstobj event is now allowed to take an :inline argument, which can speed up execution.

    + Macro cw-gstack no longer takes arguments for the gstack or state. To print terms in full rather than abbreviated: (cw-gstack :evisc-tuple nil).

    + The include-book event now has an additional (optional) keyword, :dir. In particular, (include-book "foo/bar" :dir :system) will include the indicated book after prepending the path of the built-in books/ directory. You will probably not find :dir :system to be useful if you move the executable image or distributed books; see include-book, in particular its ``soundness warning''.

    + The printing of results in raw mode (see set-raw-mode) may now be partially controlled by the user: see add-raw-arity.

    + For those using Unix/Linux `make': A cert.acl2 file can contain forms to be evaluated before an appropriate certify-book command is invoked automatically (not included in cert.acl2).

    o Some of the changes in the proof engine (see note-2-8-proofs):

    + ACL2 now prevents certain rewriting loops; see rewrite-stack-limit.

    + Small changes have been made to heuristics for controlling rewriting during proofs by induction and in handling certain ``weak'' compound-recognizer rules.

    + The handling of free variables in a hypothesis of a rewrite rule (see free-variables) has been improved in the case that the hypothesis is of the form (equiv x y), where equiv is a known equivalence relation (see equivalence).

    + We have modified how the ACL2 simplifier handles the application of a defined function symbol to constant arguments, by avoiding the introduction of hide when evaluation fails if the term can be rewritten.

    + The generation of "Goal" for recursive (and mutually-recursive) definitions now uses the subsumption/replacement limitation (default 500). See case-split-limitations.

    + Default hints now apply to hints given in definitions, not just theorems. See default-hints.

    + Linear arithmetic now uses the conclusions of forward-chaining rules, and type-set now uses a small amount of linear reasoning when deciding inequalities.

    o Some of the changes in rules, definitions, and constants (see note-2-8-rules):

    + See the above doc topic.

    o Guard-related changes are described in see note-2-8-bug-fixes.

    o Some of the proof-checker changes (see note-2-8-proof-checker):

    + Added new proof-checker commands wrap1, wrap, and wrap-induct, to combine multiple conjuncts or goals.

    + The type-alist command now takes optional arguments that control whether or not the governors and/or conclusion are used in computing the context.

    o Some of the system-level changes (see note-2-8-system):

    + ACL2 now runs on OpenMCL and on MCL 5.0.

    o Some of the other changes (see note-2-8-other):

    + Emacs file emacs/emacs-acl2.el has been updated (see note-2-8-other for details).

    + When :pl is given a term other than a symbol, it will print all rewrite rules that match that term.

    + A new function, pkg-witness, returns a symbol in the given package.

    + The list constant *acl2-exports* has been extended.

    + A new release of the rtl library has been included: books/rtl/rel4/. See the README file in that directory.

    Again, please proceed to the subtopics for more thorough release notes.

    Some Related Topics




    acl2-sources/doc/HTML/NOTE-2-8_lparen_R_rparen_.html0000664002132200015000000000146012222333527021355 0ustar kaufmannacl2 NOTE-2-8_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-2-8(R)

    ACL2 Version 2.8(r) (March, 2003) Notes
    Major Section:  RELEASE-NOTES
    

    The Makefile has been modified by adding a new target, clean-links. This can be used in order to remove all soft links, which is useful if the directory is copied or moved to a new location or if there are file system changes that cause problems with link pathnames.

    Please also see note-2-8 for changes to Version_2.8 of ACL2.




    acl2-sources/doc/HTML/NOTE-2-9-1.html0000664002132200015000000002247312222333527016173 0ustar kaufmannacl2 NOTE-2-9-1.html -- ACL2 Version 6.3

    NOTE-2-9-1

    ACL2 Version 2.9.1 (December, 2004) Notes
    Major Section:  RELEASE-NOTES
    

    (GCL only) A bug in symbol-package-name has been fixed that could be exploited to prove nil, and hence is a soundness bug. Thanks to Dave Greve for sending us an example of a problem with defcong (see below) that led us to this discovery.

    ACL2 now warns when defcong specifies equal as the first equivalence relation, e.g., (defcong equal iff (member x y) 2). The warning says that the rule has no effect because equal already refines all other equivalence relations. Formerly, this caused an error unless :event-name was supplied (see defcong), and in fact the error was a nasty raw Lisp error on GCL platforms due to some mishandling of packages by ACL2 that has been fixed (see the paragraph about symbol-package-name above). Thanks to Dave Greve for sending a helpful example in his report of this problem.

    (GCL only) The build process was broken for GCL 2.6.0 (and perhaps some earlier versions), and has been fixed. Thanks to Jose Luis Ruiz-Reyna for bringing this problem to our attention.

    (GCL only) We have increased the hole size to at least 20% of max-pages, which may eliminate some garbage collection at the expense of larger virtual memory (not larger resident memory or larger image). Thanks to Camm Maguire for helpful explanations on this topic.

    We have clarified the guard warning message that is printed during evaluation of recursively-defined functions whose guards have not been verified, for example:

      ACL2 Warning [Guards] in TOP-LEVEL:  Guard-checking may be inhibited
      on some recursive calls of executable counterparts (i.e., in the ACL2
      logic), including perhaps EVENLP.  To check guards on all recursive
      calls:
        (set-guard-checking :all)
      To leave behavior unchanged except for inhibiting this message:
        (set-guard-checking :nowarn)
    
    And, ACL2 no longer prints that message when the guard was unspecified for the function or was specified as T. Thanks to Serita Nelesen for bringing the latter issue to our attention. Finally, ACL2 now prints such a warning at most once during the evaluation of any top-level form; thanks to Bill Young for pointing out this issue.

    The function verbose-pstack has been enhanced to allow specified prover functions not to be traced. See verbose-pstack.

    Added lp, wet, and set-non-linearp to *acl2-exports*, and hence to the "ACL2-USER" package.

    The distributed book books/arithmetic-3/bind-free/integerp.lisp has been modified in order to prevent potential looping; specifically, the definition of function reduce-integerp-+-fn-1. Thanks to Robert Krug for providing this change.

    A small improvement was made in the wet failure message when the error occurs during translation to internal form. Thanks to Jared Davis for pointing out the obscurity of some wet error messages.

    We have improved ACL2's evaluation mechanism for the function bad-atom<=, which now is specified to return nil if neither argument is a so-called ``bad atom'' (as recognized by function bad-atom). The following events had caused a hard error, for example. (We're sorry that bad-atom and bad-atom<= are not documented, but we also consider it unlikely that anyone needs such documentation; otherwise, please contact the implementors.)

    (defun foo (x y) (declare (xargs :guard t)) (bad-atom<= x y))
    (defun bar (x y) (declare (xargs :guard t)) (foo x y))
    (thm (equal (bar 3 4) 7))
    
    We have also changed the guard on alphorder to require both arguments to be atoms.

    For forms (local x) that are skipped during include-book, or during the second pass of certify-book or encapsulate, ACL2 had nevertheless checked that x is a legal event form. This is no longer the case.

    The proof-checker now does non-linear arithmetic when appropriate. It had formerly ignored set-non-linearp executed in the ACL2 command loop.

    Incremental releases are now supported. See version and {obsolete after Version 4.3} set-tainted-okp. Thanks to Hanbing Liu for discovering a flaw in our original design.

    The pattern-matching algorithm for :rewrite rules has been made slightly more restrictive, thanks to a suggestion and examples from Robert Krug. For example, previously one could get an infinite loop as follows.

    (defstub foo (x) t)
    (defaxiom foo-axiom
      (equal (foo (+ 1 x))
             (foo x)))
    (thm (foo 0)) ; or replace 0 by any integer!
    
    That is because the term (foo 0) was considered to match against the pattern (foo (+ 1 x)), with x bound to -1. While such matching is sound, it leads to an infinite loop since it allows foo-axiom to rewrite (foo 0) to (foo -1), and then (foo -1) to (foo -2), and so on. The fix is to insist that the new value, in this case -1, is no larger in size according to acl2-count than the old value, in this case 0. Since that test fails, the match is considered to fail and the loop no longer occurs. An analogous fix has been made for multiplication, where now we only match when the new term is still a non-zero integer. That change avoids a loop here.
    (defstub foo (x) t)
    (defaxiom foo-axiom
      (equal (foo (* 2 x))
             (foo x)))
    (thm (foo 0)) ; or try (thm (foo 4))
    

    Added macro find-lemmas in books/misc/find-lemmas.lisp (see brief documentation there) for finding all lemmas that mention all function symbols in a given list.

    :Restrict hints now work for :definition rules, though they continue to be ignored by the preprocessor and hence you may want to use :do-not '(preprocess) with any restrict hints. Thanks to John Matthews for pointing out the lack of support for :definition rules in :restrict hints.

    Some books have been updated. In particular, there is a new directory books/workshops/2004/ in workshops distribution, for the 2004 ACL2 workshop. There is also a new version of Jared Davis's ordered sets library, formerly in books/finite-set-theory/osets-0.81/ but now in books/finite-set-theory/osets/.

    Fixed a bug in the (under-the-hood) raw Lisp definition of defchoose, which had been causing a warning in CMU Common Lisp.

    [Technical improvements related to the use of ``make dependencies'' for certifying distributed books:]
    File books/Makefile-generic now does a better job with ``make dependencies,'' specifically with respect to handling *.acl2 files and handling include-book commands with :dir :system. Regarding the latter, suppose for example that book basic.lisp contains the line:

    (include-book "arithmetic/top-with-meta" :dir :system)
    
    Then make dependencies would generate the following line:
    basic.cert: $(ACL2_SRC_BOOKS)/arithmetic/top-with-meta.cert
    
    Thus, if :dir :system is used with include-book, the corresponding Makefile should define the variable ACL2_SRC_BOOKS. A standard Makefile header for a books directory could thus be as follows.
    # The following variable should represent the ACL2 source directory.  It is the
    # only variable in this Makefile that may need to be edited.
    ACL2_SRC = ../../../../../..
    
    ACL2_SRC_BOOKS = $(ACL2_SRC)/books
    include $(ACL2_SRC_BOOKS)/Makefile-generic
    ACL2 = $(ACL2_SRC)/saved_acl2
    
    Finally, the ``-s'' flag may now be omitted when running ``make dependencies.''




    acl2-sources/doc/HTML/NOTE-2-9-2.html0000664002132200015000000002560612222333527016175 0ustar kaufmannacl2 NOTE-2-9-2.html -- ACL2 Version 6.3

    NOTE-2-9-2

    ACL2 Version 2.9.2 (April, 2005) Notes
    Major Section:  RELEASE-NOTES
    

    Also see note-2-9-1 for other changes since the last non-incremental release (Version_2.9).

    There was a bug in non-linear arithmetic (see non-linear-arithmetic) that caused the following error:

    ACL2 !>(include-book "rtl/rel4/lib/top" :dir :system)
    ....
    ACL2 !>(set-non-linearp t)
     T
    ACL2 !>(thm
     (implies (and (bvecp a 77)
                   (bvecp b 50))
              (bvecp (fl (/ (* a b) (expt 2 23)))
                     104))
     :hints (("Goal" :in-theory (enable bvecp))))
    
    [Note:  A hint was supplied for our processing of the goal above.
    Thanks!]
    
    By the simple :definition BVECP, the :executable-counterparts of EXPT
    and UNARY-/ and the simple :rewrite rule ASSOCIATIVITY-OF-* we reduce
    the conjecture to
    
    Goal'
    (IMPLIES (AND (INTEGERP A)
                  (<= 0 A)
                  (< A 151115727451828646838272)
                  (INTEGERP B)
                  (<= 0 B)
                  (< B 1125899906842624))
             (BVECP (FL (* A B 1/8388608)) 104)).
    
    
    HARD ACL2 ERROR in VARIFY:  This should not have happened.  The supposed
    variable, '1/8388608, is instead a constant.
    
    ACL2 !>
    
    Thanks to Robert Krug for providing a fix for the above error.

    Guard-checking was being inhibited (since v2-9) for calls of built-in primitives on explicit values, e.g., (car 3). This has been fixed.

    Guard-related warnings could be printed during proofs (this bug was introduced in Version_2.9.1). These warnings have been eliminated.

    Compound-recognizer rules natp-compound-recognizer and posp-compound-recognizer are now built into ACL2 for predicates natp and posp, and hence have been deleted from book natp-posp.lisp (where they were called natp-cr and posp-cr, respectively).

    The function file-clock-p, which recognizes a component of the ACL2 state, is now defined using natp instead of integerp. Thanks to Jared Davis for suggesting this change. (Technical explanation about functions in ACL2 source file axioms.lisp: With a file-clock of -1, the call of make-input-channel in open-input-channel will create a channel that can't be closed; see the guard of close-input-channel.)

    (Allegro CL users only) Support is now provided for building an Allegro CL application, provided you have an Allegro CL dynamic runtime license. (Our belief is that with such a license, many users can use the same application, rather than each user needing a separate license.) See new GNUmakefile target allegro-app and file build-allegro-exe.cl for more information.

    The new home page now contains a link to a new page other-releases.html, which contains information about other ACL2 releases. (This is in one's local home page, but may not show up on the central ACL2 home page until the next non-incremental release.) Thanks to Warren Hunt for suggesting this addition.

    We thank Erik Reeber for suggesting a solution to output redirection using sys-call, which we have described at the end of its documentation.

    A new documentation topic fixes the flawed argument for conservativity of the defchoose event that appears in Appendix B of Kaufmann and Moore's paper, ``Structured Theory Development for a Mechanized Logic'' (Journal of Automated Reasoning 26, no. 2 (2001), pp. 161-203). See conservativity-of-defchoose. Thanks to John Cowles and Ruben Gamboa for helpful feedback on drafts of this note.

    The solution to exercise 6.15 in books/textbook/chap6/solutions.txt has been fixed. Thanks to Aaron Smith for pointing out the problem.

    A new documentation topic defun-sk-example gives a little more help in using defun-sk effectively. Thanks to Julien Schmaltz for presenting this example as a challenge.

    (GCL only) There is now a way to speed up GCL builds of ACL2, at the cost of perhaps a percent or so in performance of the resulting image. Using `make' one supplies the following.

    LISP='gcl -eval "(defparameter user::*fast-acl2-gcl-build* t)"
    

    Various makefiles have been improved in several ways.

    (1) Parallel book certification, using GNU make's -j option, can be used.

    (2) Book certifications now stops at the first failure if books/Makefile or books/Makefile-generic is used, and returns non-zero exit status. However, the various make targets in the ACL2 source directory (regression, certify-books, etc.) still continue past failures unless you provide ACL2_IGNORE=' ' on the `make' command line.

    (3) The build process has been modified (file GNUmakefile) so that it stops upon a failed compile or a failed initialization.

    (4) The automatic dependency generation (from ``make dependencies'' has been improved so that commands of the form (ld "my-book.lisp") in .acl2 files cause the appropriate depedencies to be generated.

    Thanks to comments from several users that led to the above Makefile improvements: Ray Richards, Doug Harper, and the Rockwell ACL2 users for (1) and (2) (and inspiring (4)), and David Rager for (2) and (3). In particular, Doug Harper sent a replacement for the .date mechanism, which was interfering with make -n; so, these files are no longer written.

    A mechanism has been added for saving output. In particular, you can now call ld on a file with output turned off, for efficiency, and yet when a proof fails you can then display the proof attempt for the failed (last) event. See set-saved-output. Another new command -- see set-print-clause-ids -- causes subgoal numbers to be printed during proof attempts when output is inhibited.

    Documentation has been added for using ACL2's makefile support to automate the certification of collections of books. See books-certification-classic.

    Fixed a bug in sys-call-status that was causing hard Lisp errors.

    Improved cw-gstack to allow a :frames argument to specify a range of one or more frames to be printed. see cw-gstack.

    Fixed a bug in proof-checker command forwardchain. Thanks to Ming-Hsiu Wang for bringing this bug to our attention.

    We have provided a mechanism for saving an executable image. See saving-and-restoring and see save-exec. We have eliminated obsolete functions note-lib and make-lib.

    Modified the ground-zero theory so that it contains all of the built-in rules (in ACL2 source file axioms.lisp). It had formerly failed to include rules from some definitions and theorems near the end of axioms.lisp.

    A new event, set-enforce-redundancy, allows the enforcement of defthm, defun, and most other events during book development. See set-enforce-redundancy.

    A bug has been fixed that had allowed deftheory events to cause a hard Lisp error when calling union-theories on ill-formed theories after, for example:

    :set-guard-checking nil
    (in-theory (union-theories '((:rewrite no-such-rule))
                               (current-theory 'ground-zero)))
    
    The handling of guard checking has been modified somewhat in a way that should only very rarely affect users. (An ``Essay on Guard Checking'' in the ACL2 source code explains this point to anyone interested in implementation details.)

    (GCL ONLY) Removed the -dir setting in the ACL2 wrapper script for GCL. This should generally have no effect for most users, but it eliminates a potential source of error down the road.

    Several interesting new definitions and lemmas have been added to the rtl library developed at AMD, and incorporated into books/rtl/rel4/lib/. Other book changes include a change to lemma truncate-rem-elim in books/ihs/quotient-remainder-lemmas.lisp, as suggested by Jared Davis.

    The macro real/rationalp may now be referred to in in-theory events and hints, thanks to a new add-macro-alias event. Thanks to Jared Davis for this suggestion.

    ACL2 terms of the form (if p 'nil 't) are now printed as (not p), where in some setting they had been printed as (and (not p) t). Thanks to Robert Krug for this improvement.

    (GCL ONLY) Added profiling support, based heavily on code supplied by Camm Maguire. See file save-gprof.lsp for instructions. Thanks to Camm, and also to David Hardin for inspiring this addition.

    Added support for preprocessing before printing (untranslating) a term. See user-defined-functions-table, in particular the discussion of untranslate-preprocess. Thanks to Jared Davis for inspiring this addition, and for providing a book that takes advantage of it (books/misc/untranslate-patterns.lisp).

    The documentation has been improved for explaining how runes are assigned; see rune. Thanks to Robert Krug for pointing out inaccuracies in the existing documentation.




    acl2-sources/doc/HTML/NOTE-2-9-3-PPR-CHANGE.html0000664002132200015000000002054612222333527017556 0ustar kaufmannacl2 NOTE-2-9-3-PPR-CHANGE.html -- ACL2 Version 6.3

    NOTE-2-9-3-PPR-CHANGE

    change in pretty-printing for ACL2 Version_2.9.3
    Major Section:  NOTE-2-9-3
    

    We have improved pretty-printing in ACL2 Version_2.9.3 to handle keywords a little differently. To see a discussion of the basics of this change, see note-2-9-3. In this note we describe it in considerable detail.

    Those who wish to understand the ACL2 pretty-printer's implementation can now find considerably more comments on it in the source code. In this note, we do not focus on the implementation. Rather, we motivate the change and show how the improved prettyprinter performs.

    Why do we want better keyword handling? Imagine a macro that builds a new state from an old state by changing the values in the affected fields, leaving everything else unchanged. One could write

    (modify th s :key1 val1 :key2 val2 :key3 val3)
    
    where the three keys identify fields in the state.

    To make it easier to read new concrete states, we may have a function that prints them ``relative'' to a given base state, expressing the new state as a modification of the given base state. So we may find ourselves prettyprinting modify forms like that above.

    The previous prettyprinter will sometimes print the form above as follows.

    (modify th s :key1
            val1
            :key2 val2 :key3 val3)
    
    This can be unpleasant to read, because of the way :key1 and val1 are separated. Here is an example of the old prettyprinter and the new one, both printing an expression from the ACL2 source code in a width of 40:
    Old:
    (ADD-TO-TAG-TREE
     'ASSUMPTION
     (MAKE
      ASSUMPTION :TYPE-ALIST TYPE-ALIST
      :TERM TERM :REWRITTENP REWRITTENP
      :IMMEDIATEP IMMEDIATEP :ASSUMNOTES
      (LIST
       (MAKE
            ASSUMNOTE :CL-ID
            NIL :RUNE RUNE :TARGET TARGET)))
     TTREE)
    
    New:
    (ADD-TO-TAG-TREE
         'ASSUMPTION
         (MAKE ASSUMPTION
               :TYPE-ALIST TYPE-ALIST
               :TERM TERM
               :REWRITTENP REWRITTENP
               :IMMEDIATEP IMMEDIATEP
               :ASSUMNOTES
               (LIST (MAKE ASSUMNOTE
                           :CL-ID NIL
                           :RUNE RUNE
                           :TARGET TARGET)))
         TTREE)
    
    Basically the change we made forces the prettyprinter to print each :key on a new line unless they all fit on a single line. So we would now get either
    (modify th s :key1 val1 :key2 :val2 :key3 val3)
    
    or
    (modify th s
            :key1 val1
            :key2 val2
            :key3 val3)
    
    Furthermore, we fixed it so that if val1 (say) is a big s-expression we may still print it on the same line as its key. The old prettyprinter enforced the rule that if you wanted to print (foo a b) and b gets broken up into several lines, then it has to start on a new line. Thus, we'd never print
    (foo a (bbb
            (mum x)))
    
    but would print instead
    (foo a
         (bbb
          (mum x)))
    
    Now, if a is a keyword, we can print the first way.

    So here are some nice examples of prettyprinted keyword forms. All of these are printed for a page of width 40.

    <--            40 chars               ->
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    
    (MODIFY TH S :KEY1 V1 :KEY2 V2)
    
    (MODIFY TH S :KEY1 V1 :KEY2 V2 :KEY3 V3)
    
    (MODIFY TH S1                               ; Because of the extra char
            :KEY1 V1                            ; in S1 the flat size exceeds
            :KEY2 V2                            ; 40 and we break it.
            :KEY3 V3)
    
    The old ppr would have printed this as:
    (MODIFY
         TH S1 :KEY1 V1 :KEY2 V2 :KEY3 V3)
    
    Returning to new examples:
    <--            40 chars               ->
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    
    (MODIFY TH S
            :KEY1 (IF (IF X Y Z) AAAA BBBB)
            :KEY2 VAL2
            :KEY3 VAL3)
    
    Now we extend AAAA and BBBB by one char each, so it would overflow the right margin if printed as above, and we get:
    <--            40 chars               ->
    xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    
    (MODIFY
         TH S
         :KEY1 (IF (IF X Y Z) AAAAX BBBBX)
         :KEY2 VAL2
         :KEY3 VAL3)
    
    If we make these names even longer we force the value off the line containing :key1:
    (MODIFY
         TH S
         :KEY1
         (IF (IF X Y Z) AAAAXXXXX BBBBXXXXX)
         :KEY2 VAL2
         :KEY3 VAL3)
    
    Here are some examples from the ACL2 source code, printed in 40 characters:
    (DEFTHM
     ALPHORDER-ANTI-SYMMETRIC
     (IMPLIES (AND (NOT (CONSP X))
                   (NOT (CONSP Y))
                   (ALPHORDER X Y)
                   (ALPHORDER Y X))
              (EQUAL X Y))
     :HINTS
     (("Goal"
       :IN-THEORY
       (UNION-THEORIES
        '(STRING< SYMBOL-<)
        (DISABLE
           CODE-CHAR-CHAR-CODE-IS-IDENTITY))
       :USE
       ((:INSTANCE SYMBOL-EQUALITY (S1 X)
                   (S2 Y))
        (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC)
        (:INSTANCE
             CODE-CHAR-CHAR-CODE-IS-IDENTITY
             (C Y))
        (:INSTANCE
             CODE-CHAR-CHAR-CODE-IS-IDENTITY
             (C X)))))
     :RULE-CLASSES
     ((:FORWARD-CHAINING
       :COROLLARY
       (IMPLIES
          (AND (ALPHORDER X Y)
               (NOT (CONSP X))
               (NOT (CONSP Y)))
          (IFF (ALPHORDER Y X) (EQUAL X Y)))
       :HINTS
       (("Goal"
         :IN-THEORY (DISABLE ALPHORDER))))))
    
    Here is that same one, printed in a width of 60.
    (DEFTHM
     ALPHORDER-ANTI-SYMMETRIC
     (IMPLIES (AND (NOT (CONSP X))
                   (NOT (CONSP Y))
                   (ALPHORDER X Y)
                   (ALPHORDER Y X))
              (EQUAL X Y))
     :HINTS
     (("Goal"
         :IN-THEORY
         (UNION-THEORIES
              '(STRING< SYMBOL-<)
              (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY))
         :USE ((:INSTANCE SYMBOL-EQUALITY (S1 X)
                          (S2 Y))
               (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC)
               (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y))
               (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY
                          (C X)))))
     :RULE-CLASSES
     ((:FORWARD-CHAINING
          :COROLLARY (IMPLIES (AND (ALPHORDER X Y)
                                   (NOT (CONSP X))
                                   (NOT (CONSP Y)))
                              (IFF (ALPHORDER Y X) (EQUAL X Y)))
          :HINTS (("Goal" :IN-THEORY (DISABLE ALPHORDER))))))
    
    Just for comparison, here is the above printed in 60 columns by the old prettyprinter.
    (DEFTHM
     ALPHORDER-ANTI-SYMMETRIC
     (IMPLIES (AND (NOT (CONSP X))
                   (NOT (CONSP Y))
                   (ALPHORDER X Y)
                   (ALPHORDER Y X))
              (EQUAL X Y))
     :HINTS
     (("Goal" :IN-THEORY
              (UNION-THEORIES
                   '(STRING< SYMBOL-<)
                   (DISABLE CODE-CHAR-CHAR-CODE-IS-IDENTITY))
              :USE
              ((:INSTANCE SYMBOL-EQUALITY (S1 X)
                          (S2 Y))
               (:INSTANCE BAD-ATOM<=-ANTISYMMETRIC)
               (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY (C Y))
               (:INSTANCE CODE-CHAR-CHAR-CODE-IS-IDENTITY
                          (C X)))))
     :RULE-CLASSES
     ((:FORWARD-CHAINING
           :COROLLARY
           (IMPLIES (AND (ALPHORDER X Y)
                         (NOT (CONSP X))
                         (NOT (CONSP Y)))
                    (IFF (ALPHORDER Y X) (EQUAL X Y)))
           :HINTS
           (("Goal" :IN-THEORY (DISABLE ALPHORDER))))))
    

    Of course, given that you cannot tell for sure whether the keywords you're seeing are part of a keyword/value parameter list or part of some constant containing random keywords, the prettyprinter can't solve the problem perfectly. We just tried to make it work nicely on well-formed keyword/value parameter lists.

    For example, here is a form printed by the each prettyprinter.

    Old:
    (MEMBER
     X
     '(:MONDAY
          :MON :TUESDAY :TUES :WEDNESDAY
          :WED :THURSDAY :THURS :FRIDAY
          :FRI :SATURDAY :SAT :SUNDAY :SUN))
    
    New:
    (MEMBER X
            '(:MONDAY :MON
                      :TUESDAY :TUES
                      :WEDNESDAY :WED
                      :THURSDAY :THURS
                      :FRIDAY :FRI
                      :SATURDAY :SAT
                      :SUNDAY :SUN))
    

    The new way is not how one would print it by hand! But then, neither is the old way.




    acl2-sources/doc/HTML/NOTE-2-9-3.html0000664002132200015000000002774312222333527016202 0ustar kaufmannacl2 NOTE-2-9-3.html -- ACL2 Version 6.3

    NOTE-2-9-3

    ACL2 Version 2.9.3 (August, 2005) Notes
    Major Section:  RELEASE-NOTES
    

    Also see note-2-9-1 and see note-2-9-2 for other changes since the last non-incremental release (Version_2.9).

    We fixed a soundness bug that exploited the ability to define :program mode functions that are improperly guarded, and then to use those functions in defconst forms. The fix is to evaluate defconst forms using the same ``safe-mode'' that is used in macroexpansion (see guards-and-evaluation). Here is a proof of nil that succeeded in Allegro Common Lisp (but not, for example, GCL). See also a long comment in source function defconst-fn for an example that does not require the use of :set-guard-checking.

    :set-guard-checking nil ; execute before certifying the book below
    
    (in-package "ACL2")
    
    (encapsulate
     ()
     (local (defun f1 ()
              (declare (xargs :mode :program))
              (char-upcase (code-char 224))))
     (local (defconst *b* (f1)))
     (defun f1 ()
       (char-upcase (code-char 224)))
     (defconst *b* (f1))
     (defthm bad
       (not (equal *b* (code-char 224)))
       :rule-classes nil))
    
    (defthm ouch
      nil
      :hints (("Goal" :use bad))
      :rule-classes nil)
    

    We fixed a soundness hole due to the fact that the "LISP" package does not exist in OpenMCL. We now explicitly disallow this package name as an argument to defpkg. Thanks to Bob Boyer and Warren Hunt for bringing an issue to our attention that led to this fix.

    ACL2 now requires all package names to consist of standard characters (see standard-char-p, none of which is lower case. The reason is that we have seen at least one lisp implementation that does not handle lower case package names correctly. Consider for example the following raw lisp log (some newlines omitted).

    >(make-package "foo")
    #<"foo" package>
    >(package-name (symbol-package 'FOO::A))
    "foo"
    >(package-name (symbol-package '|FOO|::A))
    "foo"
    >
    
    Distributed book books/textbook/chap10/compiler, as well as workshop books in directory books/workshops/2004/cowles-gamboa/support/, were modified to accommodate the above change.

    Added newline, add-to-set-eql, the-fixnum, and the-fixnum! to *acl2-exports*. Thanks to Jared Davis for bringing these to our attention.

    Added a line to acl2.lisp to support CMUCL running on Mac OSX, thanks to a suggestion from Fabricio Chalub Barbosa do Rosario.

    The executable scripts for saved ACL2 images now include $*, so that command-line arguments will be passed along.

    (For GCL profiling only) Fixed a colon (:) that should have been a semicolon (;) in file save-gprof.lsp. Thanks to David Hardin for pointing out this bug.

    The documentation for :elim rules has been expanded and improved, thanks to useful feedback from Hanbing Liu.

    Fixed a bug in the guard for function include-book-dir.

    For those who want to experiment with an alternate implementation of mv and mv-let, there is now support for under-the-hood implementation of these in terms of raw Lisp functions values and multiple-value-bind, respectively. The regression suite has seen about a 10% speed-up in Allegro CL and about an 8% slowdown in GCL for builds with this change. See the makefile (GNUmakefile) for examples of how to build ACL2 by including the feature, :acl2-mv-as-values. Source file init.lsp has been renamed to init.lisp in support of this change (technical detail: otherwise GCL loads the init file too soon, before its -eval argument is evaluated). Thanks to David Rager for inspiring this change, by pointing out the problematic use of globals by the existing mv implementation from the standpoint of supporting parallel evaluation. This capability is experimental: there is likely to be some remaining work to be done on it.

    A change related to the one just above is that we now limit the maximum number of arguments to any call of mv to 32. Thanks to Bob Boyer for raising a question that lead to this change.

    Eliminated some compiler warnings in OpenMCL.

    In the rtl library (books/rtl/rel4/), functions bits and setbits have had their guards improved (as they had been too restrictive, especially for setbits).

    A new function time$ permits timing of forms, by using (under the hood) the host Common Lisp's time utility.

    We fixed an infinite loop that could occur during destructor elimination (see elim). Thanks to Sol Swords to bringing this to our attention and sending a nice example, and to Doug Harper for sending a second example that we also found useful.

    The method of speeding up GCL-based builds (see note-2-9-2) has changed slightly from Version_2.9.2. Now, in the `make' command:

      LISP='gcl -eval "(defparameter user::*fast-acl2-gcl-build* t)"
    

    We improved the pretty-printer's handling of keywords. For example, before this change one might see the following printed by ACL2.

    (MODIFY TH S :KEY1 VAL1 :KEY2
            (IF (IF X Y Z) AAAAAAAAAA BBBBBBB))
    
    Now, the above might print as follows. Notice that we have avoided breaking after a keyword (see keywordp) that is preceded by other forms on the same line.
    (MODIFY TH S
            :KEY1 VAL1
            :KEY2 (IF (IF X Y Z) AAAAAAAAAA BBBBBBB))
    
    See note-2-9-3-ppr-change for a detailed discussion of this change.

    (GCL ONLY) Evaluation in a break is no longer inhibited by ACL2 when built on top of GCL, so GCL now matches other Common Lisps in this respect.

    For ACL2 built on most host Common Lisps, you will see the string [RAW LISP] in the prompt, at least at a break, to emphasize that one is inside a break and hence should probably quit from the break. See breaks.

    Jared Davis suggested improvements to lemmas len-update-nth (in source file axioms.lisp) and append-true-listp-type-prescription (in books/meta/term-defuns.lisp), which have been incorporated. The former required a change in books/workshops book 2004/ruiz-et-al/support/q-dag-unification.cert, which has been made.

    The proof-checker command rewrite allows further binding of free variables in hypotheses, with new optional argument instantiate-free. Proof-checker command show-rewrites (sr) gives corresponding additional information. Documentation for these commands has been improved; see proof-checker-commands. Thanks to John Matthews and Bill Young for suggestions and feedback leading to these improvements.

    Fixed downcase printing so that the package name of a symbol is also downcased. For example, after execution of (defpkg "FOO" nil) and (set-acl2-print-case :downcase), 'foo::ab will print back as the same, rather than as 'FOO::ab.

    It is now possible to control the output so that numbers are printed in binary, octal, or hex, though the default is still radix 10. See set-print-base. Note that in support of this change, built-in functions explode-nonnegative-integer and explode-atom now take an extra print-base argument. Different support for radix conversion may be found in a book newly contributed by Jun Sawada, books/misc/radix.lisp.

    Built-in axiom car-cdr-elim is now only an :elim rule. It was formerly both an :elim rule and a :rewrite rule. A new rule, cons-car-cdr, takes the place of the old :rewrite rule, but is instead a hypothesis-free rule that can cause a case split (see source file axioms.lisp). Thanks to Jared Davis for suggesting this change.

    Lemmas about alphorder (alphorder-reflexive, alphorder-transitive, alphorder-anti-symmetric, and alphorder-total) are now available. (They had been local in source file axioms.lisp.) Thanks to Serita Nelesen for bringing this issue to our attention.

    ACL2 has, for some time, printed a space in the event summary after the open parenthesis for a defthm event, in order to ease backward searching for the original form, for example (defthm bar ...):

    Form:  ( DEFTHM BAR ...)
    
    The intention was that this extra space should be printed for every event form; but it was missing in some cases, for example, for verify-guards. This has been fixed.

    In analogy to include-book, now ld takes the (optional) keyword argument :dir. Thanks to Jared Davis for providing an implementation of this feature and to Eric Smith and Jeff Marshall for requesting this feature.

    We fixed a bug in include-book that could cause an error when redefinition is on, for example:

    (set-ld-redefinition-action '(:warn! . :overwrite) state)
    (include-book "/u/acl2/books/arithmetic/top")
    

    The behavior of include-book now matches the documentation: handling of compiled files for uncertified books will follow the same rules as for certified books. In particular, if you create an object file in raw Lisp for some book, then including that book will load that object file. Thanks to Jared Davis for bringing this issue to our attention.

    New documentation explains the interaction of redefinition and redundancy. See redundant-events -- the ``Note About Unfortunate Redundancies'' is new. Thanks to Grant Passmore for providing examples that led us to write this additional documentation.

    Solutions to exercises in ``How To Prove Theorems Formally'' (http://www.cs.utexas.edu/users/moore/publications/how-to-prove-thms) are now available in distributed book books/misc/how-to-prove-thms.lisp. Also in that directory may be found a new book hanoi.lisp that contains a solution to the Towers of Hanoi problem.

    Some Related Topics




    acl2-sources/doc/HTML/NOTE-2-9-4.html0000664002132200015000000005101112222333527016164 0ustar kaufmannacl2 NOTE-2-9-4.html -- ACL2 Version 6.3

    NOTE-2-9-4

    ACL2 Version 2.9.4 (February, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    Also see note-2-9-1, see note-2-9-2, and see note-2-9-3 for other changes since the last non-incremental release (Version_2.9).

    A soundness bug has been fixed that was due to inadequate checking of :meta rules in the presence of local events. Specifically, a local defevaluator event is insufficient for supporting a :meta rule (an example is shown in source function chk-acceptable-rules). Thanks to Dave Greve and Jared Davis for bringing this bug to our attention, by sending a proof of nil that exploited this bug. The fix is to check legality of :meta rules even when skipping proofs during an include-book event or the second pass of an encapsulate event.

    Fixed problem with parallel make for workshop books by adding a dependency line to books/workshops/2003/Makefile.

    Default hints (see set-default-hints) no longer prevent the use of :INSTRUCTIONS (see proof-checker). Thanks to Jared Davis for pointing out this problem.

    New functions remove-eq and remove-equal have been defined, in analogy to remove. These two symbols have also been added to *acl2-exports*. Thanks to David Rager for pointing out that remove-equal was missing. Moreover, the definitions of delete1-eq and delete1-equal have been eliminated. Function remove1-eq, now in :logic mode in source file axioms.lisp, serves in place of delete1-eq, with corresponding new function definitions for remove1 and remove1-equal.

    The symbol assert$ has been added to *acl2-exports*. Thanks to Jared Davis for the suggestion.

    Added SBCL support. Thanks to Juho Snellman for significant assistance with the port. Thanks to Bob Boyer for suggesting the use of feature :acl2-mv-as-values with SBCL, which can allow thread-level parallelism in the underlying lisp; we have done so when feature :sb-thread is present.

    We have continued to incorporate suggestions for wording improvements in documentation and error messages. Thanks to all who send these suggestions, especially to Eric Smith, who has suggested the vast majority of them.

    Made a small improvement to errors and warnings caused on behalf of set-enforce-redundancy, to indicate when an event of the same name already exists.

    Fixed a bug in books/misc/rtl-untranslate.lisp that was causing a guard violation when adding a new entry for an existing key.

    Fixed a bug in translation to internal form that caused defun-sk and defchoose to have difficulties handling ignored variables in let forms. Thanks to Sandip Ray to bringing this issue to our attention with a helpful example.

    The form (push :acl2-mv-as-values *features*) has been added in source file acl2-init.lisp for SBCL and OpenMCL only, in order to support parallel execution (looking to the future...).

    Default-hints (see set-default-hints) were being ignored inside the proof-checker, but no longer. Thanks to John Erickson for bringing this problem to our attention and providing a simple example of it.

    Modified the TAGS "make" target (specifically, function make-tags) so that it is gracefully skipped if the etags program is not found. Thanks to David Rager for pointing out this issue.

    Sandip Ray has re-worked the supporting materials for his ACL2 Workshop 2003 talk (originally with John Matthews and Mark Tuttle), to run in a few minutes. The result is in workshops/2003/ray-matthews-tuttle/support/ and is included in the full ACL2 regression suite. Thanks, Sandip.

    Debian releases of ACL2 had created superfluous .cert.final files when certifying books. This has been fixed; thanks to Jared Davis for noticing this problem.

    Jared Davis has pointed out that ``If you add a :backchain-limit-lst 0 to a rewrite rule whose hypotheses are all forced, then ACL2 `really assumes them' without trying to relieve them right there through rewriting.'' Relevant documentation has been added for :backchain-limit-lst; see rule-classes.

    A new version of the rtl library has been included in books/rtl/rel5/. Thanks to David Russinoff for contributing hand proofs for the new lemmas, and to Matt Kaufmann for carrying out their mechanization.

    Fixed a bug in save-exec that was failing to set the initial cbd according to the current directory when starting up ACL2. Thanks to Camm Maguire for bringing our attention to this problem.

    Variables introduced during let* abstraction are now in the current package. Thanks to Jared Davis for suggesting such a change. See set-let*-abstractionp.

    It is now allowed for two definitions to be considered the same from the standpoint of redundancy (see redundant-events) when one specifies a :guard of t and the other has no explicit :guard (hence, the guard is implicitly t). Thanks to Jared Davis for bringing this issue to our attention.

    (For users of emacs/emacs-acl2.el) There have been a few enhancements to distributed file emacs/emacs-acl2. el (skip this paragraph if you don't use that file):

    o Control-t q continues to compare windows ignoring whitespace, but now, a prefix argument can be given to control case is also ignored (ignore case if positive, else use case).

    o Control-t Control-l has been defined to be similar to Control-t l, except that proofs are skipped and output is suppressed.

    o Control-t u has been defined to print, to the shell buffer, a :ubt! form for the command containing the cursor.

    o Control-t Control-f buries the current buffer.

    o Meta-x new-shell now puts the new shell buffer in shell-mode (thanks to David Rager for noticing this issue).

    Linear arithmetic has been modified so that we do not generate the equality (equal term1 term2) from the pair of inequalities (<= term1 term2) and (<= term2 term1) in the case that we would have to force both term1 and term2 to be acl2-numberps. Thanks to Dave Greve for providing a motivating example and to Robert Krug for providing a fix.

    The event delete-include-book-dir had not been allowed inside books and encapsulate forms. This was an oversight, and has been fixed.

    Sandip Ray has contributed a new library of books to support proofs of partial and total correctness of sequential programs based on assertional reasoning, in books/symbolic/. This work is based on the paper J. Matthews, J S. Moore, S. Ray, and D. Vroon, ``A Symbolic Simulation Approach to Assertional Program Verification,'' currently in draft form. In particular, the books include the macro defsimulate, which automatically transforms inductive assertion proofs of correctness of sequential programs to the corresponding interpreter proofs. See the README in that directory.

    We have changed the implementation of :dir :system for ld and include-book. This change will not affect you if you build an ACL2 executable in the normal manner, leaving in place the books/ subdirectory of the source directory; nor will it affect you if you download a GCL Debian binary distribution. The change is that if environment variable ACL2_SYSTEM_BOOKS is set, then it specifies the distributed books directory, i.e., the directory determined by :dir :system. You may find it convenient to set this variable in your ACL2 script file (typically, saved_acl2). If it is set when you build ACL2, the generated script for running ACL2 will begin by setting ACL2_SYSTEM_BOOKS to that value. Thanks to various people who have discussed this issue, in particular Jared Davis who sent an email suggesting consideration of the use of an environment variable, and to Eric Smith who helped construct this paragraph. (Note that this use of ACL2_SYSTEM_BOOKS replaces the use of ACL2_SRC_BOOKS described previously; see note-2-9-1.)

    ACL2 now automatically deletes files TMP*.lisp created during the build process and created by :comp. If you want these to be saved, evaluate (assign keep-tmp-files t) in the ACL2 loop or in raw Lisp. The clean target for the standard `make' process for certifying books (see books-certification-classic) will however delete all files TMP*.*.

    The TMP files discussed just above now generally include the current process ID in their names, e.g., TMP@16388@1.lisp instead of TMP1.lisp. Thanks to Bob Boyer for suggesting this measure, which will reduce the possibility that two different processes will attempt to access the same temporary file.

    Now, :pe will print the information formerly printed by :pe!, slightly enhanced to work for logical names that are strings, not just symbols. Thanks to Warren Hunt for leading us to this change by suggesting that :pe nth print the definition of nth.

    We eliminated spurious warnings that could occur in raw mode in OpenMCL or CMUCL when stobjs are present. We thank Juho Snellman for pointing out the relevant bug and appropriate fix.

    Mfc-rw now takes a third argument that can specify an arbitrary known equivalence relation; see extended-metafunctions. Thanks to Dave Greve for discussions suggesting this improvement.

    A small modification to a symbol-reading function allows documentation string processing on Windows systems that use CR/LF for line breaks. Thanks to William Cook for bringing this issue to our attention.

    The documentation has been improved on how to control the printing of ACL2 terms. See user-defined-functions-table. Thanks to Sandip Ray for asking a question that led to the example presented there.

    We fixed an inefficiency that could cause an ld command to seem to hang at its conclusion. Thanks to Sandip Ray for pointing out this problem.

    We checked that ACL2 runs under LispWorks 4.4.5, and have inhibited redefinition warnings.

    Two changes have been made on behalf of congruence-based reasoning. Thanks to Dave Greve for examples and discussions that have led to these changes, and to Eric Smith and Vernon Austel, who also sent relevant examples.

    o When a call of the new unary function double-rewrite is encountered by the rewriter, its argument will be rewritten twice. This solves certain problems encountered in congruence-based rewriting. Warnings for :rewrite and :linear rules will suggest when calls of double-rewrite on variables in hypotheses are likely to be a good idea. See double-rewrite.

    o Hypotheses of the form (equiv var (double-rewrite term)), where equiv is a known equivalence relation and var is a free variable (see free-variables), will bind var to the result of rewriting term twice. Previously, hypotheses of the form (equal var term) would bind a free variable var, but the call had to be of equal rather than of an arbitrary known equivalence relation.

    The following improvements were made in support of ACL2 on top of OpenMCL.

    o New versions of OpenMCL that do not have :mcl in Lisp variable *features* will now work with ACL2. Thanks to David Rager for bringing this issue to our attention.

    o Added support for OpenMCL 1.0 for 64-bit DarwinPPC/MacOS X, thanks to Robert Krug.

    o Fixed tracing in OpenMCL so that the level is reset to 1 even if there has been an abort.

    o Added support in OpenMCL for WET.

    o Incorporated suggestions from Gary Byers for printing the ``Welcome to OpenMCL'' prompt before initially entering the ACL2 loop and, and for setting useful environment variable CCL_DEFAULT_DIRECTORY in the ACL2 script.

    Fixed a long-standing bug in forward-chaining, where variable-free hypotheses were being evaluated even if the executable-counterparts of their function symbols had been disabled. Thanks to Eric Smith for bringing this bug to our attention by sending a simple example that exhibited the problem.

    Improved reporting by the break-rewrite utility upon failure to relieve hypotheses in the presence of free variables, so that information is shown about the attempting bindings. See free-variables-examples-rewrite. Thanks to Eric Smith for requesting this improvement. Also improved the break-rewrite loop so that terms, in particular from unifying substitutions, are printed without hiding subterms by default. The user can control such hiding (``evisceration''); see :DOC set-brr-term-evisc-tuple.

    A new directory books/defexec/ contains books that illustrate the use of mbe and defexec. Thanks to the contributors of those books (see the README file in that directory).

    The directories books/rtl/rel2 and books/rtl/rel3 are no longer distributed. They are still available by email request. (Subdirectory rel1/ supports some of the optional workshop/ books, so it is still distributed.)

    Added book books/misc/sticky-disable.lisp to manage theories that might otherwise be modified adversely by include-book. Thanks to Ray Richards for a query that led to our development of this tool.

    The commands (exit) and (quit) may now be used to quit ACL2 and Lisp completely; in fact they macroexpand to calls of the same function as does good-bye (which is now a macro). Thanks to Jared Davis for suggesting the new aliases. (OpenMCL-only comment:) These all work for OpenMCL even inside the ACL2 loop.

    The macro wet now hides structure by default on large expressions. However, a new optional argument controls this behavior, for example avoiding such hiding if that argument is nil. Thanks to Hanbing Liu for pointing out that wet was not helpful for very large terms.

    We have fixed a bug in the forward-chaining mechanism that, very rarely, could cause a proof to be aborted needlessly with an obscure error message. Thanks to Jared Davis for sending us an example that evoked this bug.

    Fixed a bug that was causing proof output on behalf of :functional-instance to be confusing, because it failed to mention that the number of constraints may be different from the number of subgoals generated. Thanks to Robert Krug for pointing out this confusing output. The fix also causes the reporting of rules used when silently simplifying the constraints to create the subgoals.

    Fixed a bug in handling of leading ./ in pathnames, as in: (include-book "./foo"). Thanks to Jared Davis for bringing this bug to our attention.

    Made a small fix for handling of free variables of :forward-chaining rules, which had erroneously acted as though a hypothesis (equal var term) can bind the variable var.

    A small change has been made for :type-prescription rules for hypotheses of the form (equal var term), where var is a free variable and no variable of term is free (see free-variables). As with :rewrite and :linear rules, we now bind var to term even if (equal u term) happens to be known in the current context for some term u. Also as with :rewrite and :linear rules, similar handling is given to hypotheses (equiv var (double-rewrite term)) where equiv is a known equivalence relation.

    We changed the handling of free variables in hypotheses of :rewrite rules being handled by the proof-checker's rewrite (r) command, in complete analogy to the change described just above for :type-prescription rules.

    The installation instructions have been updated for obtaining GCL on a Macintosh. Thanks to Robert Krug for supplying this information and to Camm Maguire for simplifying the process by eliminating the gettext dependency.

    The macro comp is now an event, so it may be placed in books.

    Previously, a save-exec call could fail because of file permission issues, yet ACL2 (and the underlying Lisp) would quit anyhow. This has been fixed. Thanks to Peter Dillinger for bringing this problem to our attention.

    Jared Davis, with assistance from David Rager, has updated his ordered sets library, books/finite-set-theory/osets/. See file CHANGES.html in that directory.

    A new function, reset-kill-ring, has been provided for the rare user who encounters memory limitations. See reset-kill-ring.




    acl2-sources/doc/HTML/NOTE-2-9-5.html0000664002132200015000000003652612222333527016203 0ustar kaufmannacl2 NOTE-2-9-5.html -- ACL2 Version 6.3

    NOTE-2-9-5

    Changes in Version 3.0 since Version 2.9.4
    Major Section:  RELEASE-NOTES
    

    Fixed a bug in cw-gstack that was causing a hard error when attempting to report on a forced assumption. Thanks to Jared Davis for pointing this out and sending an example that helped us to determine a fix.

    Added set-backchain-limit to the set of legal events that can be placed in encapsulate forms and books. Thanks to John Cowles for bringing this issue to our attention.

    Fixed a bug that broke wet. Thanks to David Rager for bringing this bug to our attention.

    Guard verification now evaluates ground subexpressions (those with no free variables) when computing the guard conjecture for the body of a function. Thanks to Jared Davis for useful conversations leading to this change. See verify-guards, in particular its ``Note on computation of guard conjectures and evaluation'' near the end of that topic, for more details.

    Added a warning when a theory-invariant is redefined. Thanks to Jared Davis for suggesting a warning in this case and providing an informative example. Also, theory-invariants are now maintained more completely, as they are checked at the end of every event except for events executed on behalf of an include-book or the second pass of an encapsulate.

    Fixed the handling of runic designators to match their specification (see theories), so that disabling the name of a defthm event disables all rules generated for that event.

    (For those who do numerous builds using feature :acl2-mv-as-values, currently only OpenMCL and multi-threaded SBCL by default:) You can speed up builds by adding the following parameter to `make', under conditions described in GNUmakefile: USE_ACL2_PROCLAIMS=:REUSE.

    Arranged that traced functions (see trace$) are automatically untraced when events are undone (for example see ubt), at least for most underlying Common Lisp implementations.

    The macro defun-sk now creates non-executable functions, which allows stobjs to be used where they had previously been prohibited. More generally, the user now has control over declare forms to be used by the underlying defun'd function; see defun-sk. Thanks to Sandip Ray for pointing out the need for such a modification.

    :Definition rules are now treated, at least by default, as truly first-class definitions. In particular, :expand hints use the latest :definition rule by default. You may specify :install-body nil to get the previous behavior of :definition rules; See definition, and you may choose a previously-installed :definition rule to provide the current body; see set-body. Also see rule-classes for details of the :install-body field, and see hints to see a new :with directive for controlling expansion. The :with directive for :expand hints can even direct the use of a :rewrite rule for expansion! Thanks to various people, including Sandip Ray and Rob Sumners, for discussions on the issue of the applicability of :definition rules for :expand hints.

    Constraints for functional instantiation now use the original definition rather than a simplified (``normalized'') version of it.

    Fixed a bug that caused the prompt to stay the same when guard-checking is off (see set-guard-checking) and raw-mode is changed (see set-raw-mode).

    Lemma names in directory books/ordinals have been changed by replacing /\ with & and replacing \/ with V. We made this change because backslash is an escape character and hence disappears unless it is itself escaped.

    Fixed proof-tree output so that failed non-proof events do not cause the proof-tree to be re-printed. Thus for example, if you have already advanced the checkpoint marker, it will not be reset by subequent failed non-proof events. Thanks to Pete Manolios and Peter Dillinger for bringing this bug to our attention.

    Fixed a bug that was preventing the printing of stobj fields as constants instead of numbers in certain cases. (Note that this bug only affected printing, not soundness.) Thanks to Eric Smith for bringing this problem to our attention and providing the following example (which now works fine).

    (defstobj st fld1 fld2)
    (in-theory (disable update-nth))
    (defund run (st)
      (declare (xargs :stobjs (st))) ;adding this didn't seem to help..
      st)
    
    ;works great; *fld1* prints as  *fld1*
    (thm (equal (update-nth *fld1* 'abc st)
                (car (cons x y))))
    
    ;*fld1* gets printed as 0, presumably because the call to run intervenes.
    (thm (equal (update-nth *fld1* 'abc (run st))
                (car (cons x y))))
    

    The macro progn now allows the use of macros defined within its bodies even when at the event level, as illustrated by the following example.

    (progn (defmacro my-defun (&rest args)
             `(defun ,@args))
           (my-defun g (x) x))
    
    Thanks to Anna Slobodova for bringing this issue to our attention. A related change is that all arguments of progn must now be embedded event forms (see embedded-event-form), so use er-progn instead if this is not the case.

    The change to progn mentioned above also fixes a bug in handling local events inside a progn that is inside an encapsulate or in a book. For example, the following form formerly caused an error.

    (encapsulate
     ()
     (defun foo (x) x)
     (progn (local (defun bar (x) x))
            (defun abc (x) x)))
    

    We fixed two bugs in :puff and :puff*. The first, brought to our attention by Eric Smith (who we thank), caused a cryptic error message when puffing a command with no subsidiary stored events; try, for example, (encapsulate () (value-triple 3)). The second was due to a failure to restore the acl2-defaults-table. Suppose for example that we have certified the book foo.lisp, which contains (program) followed by some definitions and/or theorems. Now suppose we start ACL2 and execute the following.

    (include-book "foo")
    (defthm test-thm
      (equal x x)
      :rule-classes nil)
    
    If we now execute :puff 1, ACL2 will roll back the world to before the include-book; then ``puff'' the include-book, which will leave us in :program mode; and finally skip re-execution of the defthm because such events are skipped in :program mode. The fix is to re-install the acl2-defaults-table immediately after the include-book to its pre-include-book value.

    A new event, make-event, provides something like macros that take state. For example, one can use it to put tests into certified books, do proof search, and generate new function names. Many examples appear in directory books/make-event/. See make-event. Thanks to Bob Boyer and Jared Davis for useful feedback and to Warren Hunt, David Rager, and Sandip Ray for helpful discussions leading to some of the examples in directory books/make-event/.

    In support of make-event, which is described in the preceding paragraph, certify-book has a new keyword argument, :save-expansion, that controls whether the result of expanding make-event forms is written out to a file. See certify-book; and for a discussion of book expansion files, see make-event.

    We fixed a soundness bug that did not correctly detect local events. For example, the following event was admitted.

    (encapsulate
     ()
     (local
      (encapsulate
       ()
       (local (progn (program))) ; or, (local (with-output :off summary (program)))
       (set-irrelevant-formals-ok t)
       (defun foo (x)
         (declare (xargs :measure (acl2-count x)))
         (1+ (foo x)))))
     (defthm inconsistent
       nil
       :hints (("Goal" :use foo))
       :rule-classes nil))
    

    A new value for guard checking, :none, is now allowed. If you execute :set-guard-checking :none, then no guard checking will take place (but raw Lisp code will not be executed in this case). As a result, you should never see a guard violation, even for calls of :program mode functions. We thank Pete Manolios, who has long wanted this feature, and also Peter Dillinger, for asking for it. New documentation explains the interaction between the defun-mode and the value supplied to :set-guard-checking. See guard-evaluation-table, see guard-evaluation-examples-script, and see guard-evaluation-examples-log.

    In the course of adding the guard-checking value :none described in the paragraph above, we eliminated an optimization that eliminated guard checking for some recursive calls of :logic mode mutually-recursive functions that have not had their guards verified. But we doubt that this change will be noticed by any users!)

    The ACL2 hyper-card has been enhanced, thanks to David Rager, with a listing of ``Useful EMACS Commands'' to match comments in emacs/emacs-acl2.el.

    Users contributed books following the Readme.lsp methodology: data-structures/memories and unicode (Jared Davis), proofstyles (Sandip Ray and J Moore).

    Made some improvements to books/Makefile-generic (a file discussed elsewhere; see books-certification-classic). In particular, improved handling of .acl2 files for dependencies target.

    (Only OpenMCL and, with feature :acl2-mv-as-values, GCL) Fixed a bug that was causing proclaiming to fail when definitions are submitted interactively.

    The default stack size has been increased for several lisps.

    (Very technical) A restriction has been weakened on the use of local stobjs under a call of an ACL2 evaluator (trans-eval or simple-translate-and-eval). Now, the error can only take place for stobj names that occur in the term being evaluated. Thanks to Erik Reeber for bringing this issue to our attention.

    The notion of ``ancestor'' has been changed slightly. This notion is used by extended metafunctions and break-rewrite (see extended-metafunctions and see brr@), and also with backchain limits (see backchain-limit and see set-backchain-limit). Basically, each time a hypothesis is encountered during application of a rewrite rule, that hypothesis is pushed (after instantiating and negating) onto the current list of ancestors before it is rewritten. However, hypotheses of the form (equal var term), where var is free (see free-variables), had not been included in the ancestors (similarly for (equiv var (double-rewrite term)) where equiv is a known equivalence relation). Now such ``binding hypotheses'' are included in a special way in ancestors data structures. In particular, (null (mfc-ancestors mfc)) will now be true if and only if the term being rewritten is part of the current goal as opposed to a hypothesis from a rule encountered during backchaining, even if that hypothesis is a binding hypothesis. Thanks to Dave Greve for bringing this issue to our attention.

    Termination and induction analysis now continue through both arguments of prog2$, not just the second. (Normally, the gathering up of if tests stops at function calls; but it continued through the second argument of prog2$, and now it will continue through both arguments.) Thanks to Sol Swords for discussion leading to this change.

    The ACL2 distribution is now kept on the http server rather than the ftp server (but the home page has not been moved). Thanks to Robert Krug for letting us know that some ACL2 users have found it inconvenient to fetch ACL2 using ftp.

    The file books/README.html has been renamed to books/Readme.html, since some browsers don't show the former in the directory listing.




    acl2-sources/doc/HTML/NOTE-2-9.html0000664002132200015000000006662112222333527016040 0ustar kaufmannacl2 NOTE-2-9.html -- ACL2 Version 6.3

    NOTE-2-9

    ACL2 Version 2.9 (October, 2004) Notes
    Major Section:  RELEASE-NOTES
    

    TABLE OF CONTENTS.
    ============================== BUG FIXES. NEW FUNCTIONALITY. CHANGES IN PROOF ENGINE. GUARD-RELATED CHANGES. PROOF-CHECKER CHANGES. SYSTEM-LEVEL CHANGES. BOOK CHANGES. MISCELLANEOUS CHANGES. ==============================

    BUG FIXES.

    We fixed a soundness bug due to a conflict between user-supplied package names and internal package names (obtained by prepending a Lisp constant, *1*-package-prefix*) and user-supplied package names. For example, the form (defpkg "ACL2_*1*_MYPKG" ()) is no longer legal. Thanks to Robert Krug for asking a question that led directly to the discovery of this bug.

    We fixed a soundness bug that allows :logic mode functions to call :program mode functions. The fix furthermore prevents functions with guards verified from calling functions with guards not verified. We had thought we already prevented all this, but there was a problem with the interaction of local definitions and redundancy checking (see redundant-events).

    We fixed a soundness bug that could occur when built-in functions were called during macroexpansion (a hole in so-called ``safe-mode'').

    Fixed a minor bug in system functions genvar1 and genvar, where eq had been used in place of eql. This bug was discovered during the plugging of a hole in safe-mode, mentioned just above.

    We fixed handling of the :inline keyword for defstobj, which previously could cause raw Lisp errors in OpenMCL and CMU Common Lisp. Thanks to John Matthews for bringing this issue to our attention.

    Calls of include-book could result in a state for which some function definitions were not compiled that should have been. The result could be performance degradation or even stack overflows. This situation could arise in the following two ways.

    o Inclusion of a book with an absolute pathname that differs from the absolute pathname at certification time, presumably because of the use of soft links. Thanks to Bob Boyer and Warren Hunt for bringing a stack overflow to our attention that led us to this fix.

    o Large mutual-recursion nests (more than 20 functions) are executed in a superior book.

    We fixed some performance bugs that can increase the speed of include-book calls by a factor of close to 2. Thanks to Eric Smith for asking if we could speed up include-book processing; we have done so in the past, but primarily focusing on large mutual-recursion nests (which have nothing in particular to do with the current improvements). Also, thanks to Rob Sumners for a very useful remark early in the process that kept us from drawing an incorrect conclusion at that point.

    We fixed :pl so that it can be run on a form that returns multiple values, which it could not do previouslly. Thanks to Eric Smith for pointing out this problem.

    Fixed a bug in the Allegro ACL2 trace utility (see trace$) that was causing ``10>'' to be printed as ``9>'', ``11>'' to be printed as ``10 >'', ``12>'' to be printed as ``11 >'', and so on.

    Fixed a proof-checker bug that was preventing the use of the DV command (or a numerical command) on let expressions. Thanks to Bill Young for pointing out this problem.

    Fixed a bug in a comment on how to set ACL2_BOOKS_DIR in the makefile. Thanks to Dave Greve for pointing out this problem.

    Fixed a potential soundness bug in the linear arithmetic routines. Thanks to Jared Davis for noticing this problem and to Robert Krug for implementing the fix. (Technical details: We had been assuming that polynomials were being normalized -- see the definition of good-polyp in linear-a.lisp -- but this assumption was false.)

    When the macro open-trace-file is opened twice in succession, it now automatically closes the first trace output channel before opening another.

    It is now possible to use `make' to build ACL2 on Windows systems that support `make'. Thanks to Pete Manolios and Mike Thomas for pointing out the problem, to Jared Davis and Mike for helping us to analyze the problem, and to Jared for testing the fix.

    Fixed a bug in the guard of with-output that was causing a needless guard violation.

    NEW FUNCTIONALITY.

    The new events add-default-hints and remove-default-hints allow one to append to or subtract from the current list of default hints. The event set-default-hints continues to set the list of default hints, discarding the previous value of the default-hints. Note that set-default-hints is still local to the encapsulate or book in which it occurs, and add-default-hints has the same property, although neither is implemented any longer using the acl2-defaults-table. New events add-default-hints!, remove-default-hints!, and set-default-hints! are the same as add-default-hints, remove-default-hints, and set-default-hints, respectively, except that the former three events are not local to their enclosing encapsulate or book. Thanks to Jared Davis for suggesting these enhancements.

    OpenMCL's tracing routines have been modified in a similar manner as those of Allegro CL. Thanks to Robert Krug for providing this enhancement.

    Guard-checking can now be caused to happen on recursive calls. See ``GUARD-RELATED CHANGES'' below for details.

    Advanced users can now inhibit compilation of so-called ``*1* functions'' with the :comp command; see comp. Thanks to Rob Sumners for suggesting this enhancement.

    Added new legal argument hard? for the er macro, which is now documented. See er. Thanks to Rob Sumners for a discussion leading to this change. Also, the three legal first arguments to er -- hard, hard?, and soft -- may now be in any package (thanks to Jared Davis for bringing this issue to our attention).

    We have removed the requirement that for a rule's hypothesis (bind-free term var-list), at least one variable must occur free in term. For example, the expression (bind-free (bind-divisor a b) (x)) was legal because a and b occur free in (bind-divisor a b); but (bind-free (foo (bar)) (x)) was not legal. The latter is no longer disallowed. (Technical note: this allows bind-free to be used to create explicit substitutions in metafunction hypotheses.)

    The following two enhancements have been implemented for rules of class :meta. Thanks to Eric Smith for requesting more control of reasoning with :meta rules, which led to these enhancements, and to him and Robert Krug for helpful discussions.

    o It is now possible to control backchaining in rules of class :meta by providing a :backchain-limit-lst argument, as was already allowed for rules of class :rewrite and :linear. See rule-classes. However, unlike those other two rule classes, the value for :backchain-limit-lst is prohibited from being a non-empty list; it must be either nil or a non-negative integer.

    o (For advanced users.) It is now legal for hypothesis metafunctions to generate, in essense, calls of syntaxp and bind-free, handled essentially as they are handled in hypotheses of :rewrite and :linear rules. We say ``essentially'' primarily because both syntaxp and bind-free are actually macros, but hypothesis metafunctions must generate translated terms (see term). The enterprising advanced user can call :trans to see examples of translated terms corresponding to calls of syntaxp and bind-free.

    A new command :trans! has been added, which is like :trans except that :trans! ignored issues of single-threadedness. See trans!. Thanks to Eric Smith for suggesting this addition.

    The :pf command now works when the argument is the name of a macro associated with a function by macro-aliases-table.

    CHANGES IN PROOF ENGINE.

    The simplifier has been changed slightly in order to avoid using forward-chaining facts derived from a literal (essentially, a top-level hypothesis or conclusion) that has been rewritten. As a practical matter, this may mean that the user should not expect forward-chaining to take place on a term that can be rewritten for any reason (generally function expansion or application of rewrite rules). Formerly, the restriction was less severe: forward-chaining facts from a hypothesis could be used as long as the hypothesis was not rewritten to t. Thanks to Art Flatau for providing an example that led us to make this change; see the comments in source function rewrite-clause for details.

    The rewriter has been modified to work slightly harder in relieving hypotheses. Thanks to Eric Smith for providing an example that inspired the following, which illustrates the issue. Suppose we introduce functions foo and bar with the (non-local) properties shown below.

     (encapsulate
      (((foo *) => *)
       ((bar *) => *))
    
      (local (defun foo (x) (declare (ignore x)) t))
      (local (defun bar (x) (declare (ignore x)) t))
    
      (defthm foo-holds
        (implies x
                 (equal (foo x) t)))
      (defthm bar-holds-propositionally
        (iff (bar x) t)))
    
    Consider what happens when ACL2's rewriter is used to prove the following theorem.
    (thm (foo (bar y)))
    
    With ACL2's inside-out rewriting, (bar y) is first considered, but rewrite rule bar-holds-propositionally does not apply because the context requires preserving equality, not mere Boolean (iff) equivalence. Then the rewriter moves its attention outward and sees the term (foo (bar y)). It attempts to apply the rule foo-holds, in a context created by binding its variable x to the term (bar y). It then attempts to relieve the hypothesis x of rule foo-holds in that context. Before this change, ACL2 basically assumed that since rewriting was inside out, then (bar y) had already been rewritten as much as possible, so the rewrite of x in the aforementioned context (binding x to (bar y)) simply returned (bar y), and the attempt to relieve the hypothesis of foo-holds failed. The change is essentially for ACL2's rewriter to make a second pass through the rewriter when the attempt fails to rewrite a variable to t, this time using the fact that we are in a Boolean context. (We mention that source function rewrite-solidify-plus implements this idea, for those who want to dig deeply into this issue.) In our example, that means that the rewriter considers (bar y) in a Boolean context, where it may apply the rule bar-holds-propositionally to relieve the hypothesis successfully.

    When (set-non-linearp t) has been executed, non-linear-arithmetic can now be applied in some cases for which it previously was not. Thanks to Robert Krug for supplying this modification and to Julien Schmaltz for providing a motivating example.

    We modified the rewriter to avoid certain infinite loops caused by an interaction of the opening of recursive functions with equality reasoning. (This change is documented in detail in the source code, in particular functions rewrite-fncall and fnstack-term-member.) Thanks to Fares Fraij for sending us an example that led us to make this change.

    The :executable-counterpart of function hide is now disabled when ACL2 starts up. This removes an anomoly, for example that

    (thm (not (equal 1 (* (hide 0) a))))
    
    succeeded while
    (thm (equal (foo (equal 1 (* (hide 0) a))) (foo nil)))
    
    failed. Now both fail.

    The theory *s-prop-theory* is no longer used by the proof-checker; it has been replaced by (theory 'minimal-theory. We have left the constant *s-prop-theory* defined in the source code in support of existing books, however. This change eliminates annoying theory warnings printed upon invocation of proof-checker commands s-prop, sl, and split.

    Terms are now kept in an internal form that avoids calls of primitive functions (built-ins without explicit definitions; see code for cons-term for details), in favor of the constants that result from evlaluating those calls. So for example, the internal form for (cons 1 2) is (quote (1 . 2)). This change was made at around the same time as changes in support of bind-free; see above. One consequence is that the splitting of goals into cases (technically, source function clausify and even more technically, source function call-stack) has been modified, which can cause subgoal numbers to change.

    GUARD-RELATED CHANGES.

    Guard-checking can now be caused to happen on recursive calls, where this was formerly not the case for :program mode functions and, perhaps more important, for :logic mode functions whose guards have not been verified. Moreover, a warning is printed when ACL2 does not rule out the exclusion of guard-checking on recursive calls. See set-guard-checking. Thanks to David Rager for bringing this issue to our attention, and to Rob Sumners and the Univ. of Texas ACL2 seminar in general for their feedback.

    Guard violations are reported with less of the offending term hidden. Thanks to Jared Davis for suggesting that we look at this issue.

    PROOF-CHECKER CHANGES.

    We fixed the proof-checker so that diving works as you might expect for a macro call (op a b c) representing (binary-op a (binary-op b c)). In the past, if the current term was of the form (append t1 t2 t3), then (DV 3) (and 3) would dive to t3 even though the corresponding function call is (binary-append t1 (binary-append t2 t3)). This is still the case, but now this behavior holds for any macro associated with a function in binop-table (see add-binop). Moreover, users can now write customized diving functions; see dive-into-macros-table, and also see books/misc/rtl-untranslate.lisp for example calls to add-dive-into-macro. Of course, the old behavior can still be obtained using the proof-checker's DIVE command; see proof-checker-commands.

    The runes command in the proof-checker now shows only the runes used by the most recent primitive or macro command (as shown by :comm), unless it is given a non-nil argument. Also, proof-checker command lemmas-used has been added as, in essence, an alias for runes.

    (The following two items are also mentioned above under ``BUG FIXES.'')

    Fixed a proof-checker bug that was preventing the use of the DV command (or a numerical command) on let expressions. Thanks to Bill Young for pointing out this problem.

    The theory *s-prop-theory* is no longer used by the proof-checker; it has been replaced by (theory 'minimal-theory. We have left the constant *s-prop-theory* defined in the source code in support of existing books, however. This change eliminates annoying theory warnings printed upon invocation of proof-checker commands s-prop, sl, and split.

    SYSTEM-LEVEL CHANGES.

    Fixed a problem with building ACL2 on CMUCL in some systems (source function save-acl2-in-cmulisp). Thanks to Bill Pase for bringing this to our attention.

    The installation instructions have been extended to include instructions for building on GCL in Mac OS X. Thanks to Jun Sawada and Camm Maguire.

    Initial pre-allocation of space has been updated for GCL to reflect more current GCL executables (we considered GCL 2.6.1-38). This can help avoid running out of memory for large ACL2 sessions.

    The main Makefile has been replaced by GNUmakefile, in order to enforce the use of GNU `make'. If you use another `make' program, you'll get an error message that may help you proceed.

    (GCL only) SGC is no longer turned on for GCL 2.6 sub-versions through 2.6.3 if si::*optimize-maximum-pages* is bound to T, due to an apparent issue with their interaction in those sub-versions. Also, we have eliminated preallocation for all versions after 2.6.1 because GCL doesn't need it (comments are in source function save-acl2-in-akcl). Thanks to Camm Maguire for excellent GCL help and guidance, and to Camm and Bob Boyer for useful discussions.

    We have removed support for so-called ``small'' images. Thus, :doc, :pe and :pc, verify-termination, and other commands are fully supported in ACL2 saved images. Because of this and other changes in the generation of the so-called ``*1*'' logical functions, related to guards (as described above in -GUARD-RELATED CHANGES'', and related to the discussion of safe-mode in ``BUG FIXES'' above), image sizes have increased substantially.

    We no longer time or run ``nice'' the certification of individual books. The file books/Makefile-generic had done these by default, and some individual distributed and workshop book directories had Makefiles that did so as well. Thanks to Mike Thomas, who pointed out the lack of nice on some Windows systems (and we decided on this simple solution). Overall targets in books/Makefile still time their runs by default, and the partiular time program is now controlled by a Makefile variable.

    Failures during make certify-books or make regression now show up in the log as ``**CERTIFICATION FAILED**'', regardless of the operating system (as long as it supports `make'). Formerly, one searched for ``**'' but this did not appear in openMCL runs.

    We have eliminated ``Undefined function'' warnings that could occur in OpenMCL.

    BOOK CHANGES.

    Reconciled the definitions of firstn in book/misc/csort.lisp, books/bdd/bdd-primitives.lisp, books/ordinals/ordinal-definitions.lisp, and books/data-structures/list-defuns.lisp. Thanks to Ray Richards for bringing this issue to our attention.

    Distributed book books/misc/defpun now can handle stobjs where it did not previously. Thanks to John Matthews for bringing this issue to our attention.

    The "make" variable COMPILE_FLG in file books/Makefile-generic formerly only had an effect if there was a cert.acl2 file present. That oversight has been remedied.

    File "books/arithmetic/certify.lsp" was missing a certify-book command for "natp-posp". Thanks to John Cowles for noticing this deficiency and supplying a fix. (This file is of use to those who want to certify the "books/arithmetic/" books without using "make".)

    A few small changes have been made to "books/rtl/rel4".

    Small changes were made to books misc/symbol-btree and misc/rtl-untranslate. In particular, the definition of symbol-btreep was strengthened.

    We made a minor fix to books/ordinals/e0-ordinal.lisp, adding (verify-guards ob+) and hence (verify-guards ocmp) as well. This was necessitated by the fix prohibiting functions with guards verified from calling functions with guards not verified (see also the related discussion under ``BUG FIXES'' above).

    MISCELLANEOUS CHANGES.

    Further sped up processing of large mutual-recursion nests (extending what was done for Version_2.7), perhaps by a factor of two in some cases.

    As promised in Version_2.5 (see note-2-5), structured pathnames are no longer supported. So for example, the argument to include-book must now be a string constant.

    Some documentation has been improved, for stobjs thanks to suggestions by John Matthews and much of the rest thanks to feedback from Eric Smith.

    The function current-package is now available to users (it has been taken off the list of so-called ``untouchables''). Thanks to Jared Davis for bringing this issue to our attention.

    The documentation for topic using-computed-hints-7 has been improved. Thanks to Doug Harper and Eric Smith for inspiring this improvement.

    We added several symbols to *acl2-exports*: cw, er, intern$, set-case-split-limitations, and set-difference-eq. Thanks to Jared Davis for suggesting most of these.

    Now, a table event that sets the value for a key, (table tbl key val :put), is redundant (see redundant-events) when it does not change the value associated with an existing key of the table. In particular, define-pc-macro is now fully redundant when it does not change an existing proof-checker macro-command definition. Thanks to Bill Young for bringing the latter issue to our attention.

    The definitions of unused system functions ev-w and ev-w-lst have been deleted.

    ACL2 now prints a warning if a defpkg event introduces a package name with lower-case letters, since there is opportunity for later confusion in that case. Thanks to Frederic Peschanski for bringing this problem to our attention and Sandip Ray for encouragement.

    ACL2 now works in Version 19 of CMU Common Lisp.

    The function sys-call has been modified so that for ACL2 built on Allegro Common Lisp in Unix or Linux, the existing environment is used. Thanks to Erik Reeber for bringing this issue to our attention.

    The function disabledp can now be given a macro name that has a corresponding function; see macro-aliases-table. Also, disabledp now has a guard of t but causes a hard error on an inappropriate argument.




    acl2-sources/doc/HTML/NOTE-2-9_lparen_R_rparen_.html0000664002132200015000000000123112222333527021352 0ustar kaufmannacl2 NOTE-2-9_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-2-9(R)

    ACL2 Version 2.9(r) (October, 2004) Notes
    Major Section:  RELEASE-NOTES
    

    No changes have been made for support of non-standard analysis, other than a minor modification or two in books/nonstd/ books.

    Please also see note-2-9 for changes to Version_2.9 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-0-1.html0000664002132200015000000003730212222333527016160 0ustar kaufmannacl2 NOTE-3-0-1.html -- ACL2 Version 6.3

    NOTE-3-0-1

    ACL2 Version 3.0.1 (August, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Fixed a soundness bug, introduced in the previous release, due to a failure to disallow table events that set the acl2-defaults-table in a local context. Here is a proof of nil that exploits the bug.

    (encapsulate ()
     (local (program))
     (defun foo ()
       (declare (xargs :measure 17))
       (+ 1 (foo))))
    (thm
     nil
     :hints (("Goal" :in-theory (disable foo (foo)) :use foo)))
    

    Fixed a bug in the alternatives to good-bye, which are the exit and quit commands. Thanks to Jared Davis and Peter Dillinger for pointing this out right away.

    The definition of len has been highly optimized in raw Lisp. Thanks to Bob Boyer and Warren Hunt for suggesting such an improvement and providing a lot of help in coming up with the current implementation.

    The clause subsumption algorithms have been improved, both to improve efficiency during warnings for :rewrite rules and to punt when the subsumption computation for induction appears to be blowing up. Thanks to Robert Krug for bringing this issue to our attention and supplying a useful example.

    A bug has been fixed that prevented time$ from working properly in OpenMCL and multi-threaded SBCL (actually, in any ACL2 image where feature :acl2-mv-as-values is present). Thanks to Sol Swords for bringing this problem to our attention.

    A type-spec of the form (satisfies pred) carries the requirement that pred be a unary function symbol in the current ACL2 world; otherwise, it is illegal. Thanks to Dave Greve for pointing out that Common Lisp has this requirement.

    Installed a fix provided by Gary Byers (for ACL2 source function install-new-raw-prompt), for OpenMCL, that fixes an issue exposed in some versions of OpenMCL when compiler optimization is off.

    Fixed a bug in contributed book misc/untranslate-patterns.lisp that was causing calls of add-untranslate-pattern to be rejected in books. Thanks to Ray Richards for pointing out this bug and to Jared Davis for assisting in the fix.

    Fixed a bug in defstobj when keywords :initially and :resizable are both supplied. In this case, the definition of the resizing function mistakenly failed to quote the :initially value, even though this value is not to be evaluated. One could even get an error in this case, as in the following example supplied by Erik Reeber, whom we thank for bringing this bug to our attention:

      (defstobj $test
        (test-x :type (array t (5)) :initially (0) :resizable t))
    

    A new feature, with-prover-time-limit, allows the setting of time limits during proofs. This is not a general-purpose time-limit utility, nor is it guaranteed to implement a strict bound; it only attempts to limit time approximately during proofs. Thanks to Pete Manolios and Daron Vroon, who made the most recent request for such a feature, and to Robert Krug for a helpful discussion.

    (GCL only) Fixed a bug in the procedure for building a profiling image. Thanks to Sol Swords for bringing this bug to our attention and to Eric Smith for bringing a subsequent problem to our attention.

    Handling of theories can now use significantly less time and space. A regression suite run took about 25% longer before this change than it did after making this change (and also the ones in the next two paragraphs). Thanks to Vernon Austel for bringing this issue to our attention and for supplying code, quite some time ago, that provided detailed, useful implementation suggestions. Also thanks to the folks at Rockwell Collins, Inc. for pushing the limits of the existing implementation, thus encouraging this improvement.

    Fixed a performance bug in obtaining executable counterpart symbols.

    We now avoid certain computations made on behalf of warnings, when such warnings are disabled.

    We have relaxed the checks made when including an uncertified book, to match the checks made when including a certified book. Thanks to Eric Smith for suggesting this change.

    Fixed a bug in :pso (see set-saved-output) that caused an error when printing the time summary.

    Made fixes to avoid potential hard Lisp errors caused by the use of :program mode functions. The fix was to use a ``safe mode,'' already in use to prevent such errors during macroexpansion; see guards-and-evaluation. However, such errors were possible during evaluation of macro guards, for example as follows:

    (defun foo (x)
      (declare (xargs :mode :program))
      (car x))
    (defmacro mac (x)
      (declare (xargs :guard (foo 3)))
      x)
    (defun g (x)
      (mac x))
    
    A similar issue existed for calls of defpkg, in-theory, table, make-event, and value-triple, but has been fixed for all but in-theory and make-event, where technical issues have caused us to defer this change.

    Fixed a bug in wet that caused problems in OpenMCL, and perhaps other Lisp implementations, when the argument to wet calls, or depends on, certain built-ins including prog2$, time$, mbe, and must-be-equal. Thanks to David Rager for bringing this problem to our attention.

    The file books/Makefile-generic has been improved so that when book certification fails with `make', the failure message contains the book filename.

    Documentation has been written to explain how to avoid an expensive immediate rewrite of the result of applying a :rewrite or :meta rule. See meta. Thanks to Robert Krug for supplying this trick, and to Eric Smith and Dave Greve for useful discussions.

    (OpenMCL only) OpenMCL-based ACL2 image names formerly had extension ".dppccl", which was correct only for some platforms (including 32-bit Darwin PPC). That has been fixed, thanks to a suggestion from Gary Byers.

    It is now legal to attach both a :use and a :cases hint at the same goal. Thanks to Eric Smith for (most recently) requesting this feature.

    It is now permissible to include the same symbol more than once in the imports list of a defpkg form (i.e., its second argument). Also, the evaluation of defpkg forms with long import lists now uses a reasonably efficient sorting routine to check for two different symbols with the same name (see also books/misc/sort-symbols.lisp). If you currently call a function like remove-duplicates-eql for your imports list, as had been suggested by a defpkg error message, then you may experience some speed-up by removing that call. Thanks to Eric Smith for helping to discover this issue through profiling.

    Made miscellaneous efficiency improvements not listed above (for example, following a suggestion of Eric Smith to avoid checking for so-called ``bad Lisp objects'' during include-book, which saved almost 3% in time on one large example).

    Modified the notion of ``untouchable'' to separate the notion of untouchable functions and macros from the notion of untouchable state global variables. See push-untouchable. Thanks to Bob Boyer for sending an example, (put-global 'ld-evisc-tuple t state), that suggested to us the need for more restrictive handling of untouchables. In particular, many ld specials (see ld) are now untouchable. You may be able to work around this restriction by calling ld; see for example the change to books/misc/expander.lisp. But please contact the ACL2 implementors if this sort of workaround does not appear to be sufficient for your purposes.

    Fixed a bug in function set-standard-oi (see standard-oi).

    Fixed a bug in the use of ld-evisc-tuple. The bad behavior was an improper use of the print-level and print-length components of the tuple (specifically, taking its caddr and cadddr instead of taking its cadr and caddr). Thanks to Bob Boyer for bringing this bug to our attention.

    A new argument to the compile-flg argument of certify-book, :all, causes creation of a file to be compiled in place of the given book, where that file contains not only a copy of the book (with make-event forms expanded) but also contains definitions of the so-called ``executable counterparts'' of the functions defined in the book. Then, functions defined in the book will be run compiled when including the book, even for functions whose guards have not been verified, or are in :program mode and running in so-called ``safe mode'' (for example, during expansion of macros). The default behavior, value t of compile-flg, is unchanged. Moreover, a new :comp! argument of include-book now compiles the executable counterparts when creating the book's compiled file, and unlike :comp, always deletes the old compiled file first so that one always gets a fresh compile.

    Now, certify-book gives a "Guards" warning only for :logic mode functions that are defined in the given book but have not had their guards verified. Previously, it also warned about such functions that were defined in the certification world or in sub-books.

    A new command, redo-flat, facilitates the debugging of failed encapsulate and progn forms by evaluating preliminary forms in order to leave one at the point of failure. See redo-flat. Thanks to Ray Richards and others for asking for such a utility, and to Sandip Ray for useful discussions.

    We have changed the automatic declaration of of function types (still done in GCL and OpenMCL only, for now). Our motivation was to avoid the assumption that Common Lisp functions return one value when ACL2 says that they do; thanks to Bob Boyer for bringing this issue to our attention with the example of defining (foo x y) to be (floor x y). ACL2 was saying that foo returns a single value, but because floor returns two values in raw Lisp, so does foo. Other changes to automatic declaration include comprehending defund, not just defun.

    A new function, mod-expt, computes (mod (expt base exp) m), and does so efficiently in some implementations (currently only in GCL 2.7.0, which is not yet released). Thanks to Warren Hunt for suggesting such an addition.

    New functions getenv$ and setenv$ have been made available for reading and writing environment variables. Thanks to Jun Sawada for requesting these utilities.

    The query utility :pl has been improved in several ways. As before, :meta rules are only printed if the argument is a symbol; but the information printed for them is now more appropriate. The following are changes for the case that the argument is not a symbol, but rather, a term. (1) Rules are displayed that have equivalence relations other than equal. (2) All matching :definition rules are displayed, where previously :definition rules were only shown if they were ``simple'' rules (sometimes known as ``abbreviations''); see simple. (3) The ``Equiv'' field is printed for terms, not just symbols. (4) The substitution is shown that, when applied to the left-hand side of the rule, will yield the specified term. Thanks to Eric Smith for suggesting these changes.

    The proof-checker command ;show-rewrites has been improved to match the changes described above for :pl. In particular, :definition rules that are not ``simple'' are now displayed by the proof-checker's show-rewrites (and sr) command, and the proof-checker's rewrite command has been correspondingly modified to accept these :definition rules.

    Fixed `make' targets copy-distribution, copy-workshops, and copy-nonstd so that they should also work for non-developers.

    Fixed a bug that was causing :pr to display syntaxp hypotheses oddly in some cases, in particular (syntaxp (let ...)). (The problem was in the ``untranslate'' display of the internal form of syntaxp calls.) Thanks to Robert Krug for bringing this problem to our attention. We also removed the restriction on bind-free that its argument could not be a variable, a constant, or (more interestingly) a lambda application (i.e., a let or mv-let expression).




    acl2-sources/doc/HTML/NOTE-3-0-1_lparen_R_rparen_.html0000664002132200015000000000120612222333527021502 0ustar kaufmannacl2 NOTE-3-0-1_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-0-1(R)

    ACL2 Version 3.0.1(r) (August, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    No significant changes have been made since Version 3.0 for support of non-standard analysis in particular.

    Please also see note-3-0-1 for changes to Version 3.0.1 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-0-2.html0000664002132200015000000004752512222333527016171 0ustar kaufmannacl2 NOTE-3-0-2.html -- ACL2 Version 6.3

    NOTE-3-0-2

    ACL2 Version 3.0.2 (December, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Fixed soundness bugs in the handling of primitive function pkg-witness, and improved its documentation. (The executable counterpart returned an incorrect default value, and the axiom symbol-package-name-pkg-witness-name needed pkg-name to be other than "" in order to avoid the default value of "ACL2".) As fallout, a new built-in :forward-chaining rule, symbol-package-name-of-symbol-is-not-empty-string, now asserts that the symbol-package-name of a symbol is never "". Thanks to Mike Gordon for bringing these soundness bugs to our attention by attempting to prove translations of ACL2 axioms in HOL4.

    Fixed a soundness bug in linear arithmetic, due to incomplete tracking of forced assumptions while deriving inequalities. Thanks to Robert Krug for providing a fix and a proof of nil before the fix.

    Fixed a soundness bug in the redundancy criterion for defun events, which has been modified; see redundant-events. This bug is illustrated below. Thanks to Peter Dillinger and Jared Davis for contributions to an email thread that led us to discover this bug. The solution is that for a definition to be redundant with an earlier definition, ACL2 no longer ignores :measure xargs except when skipping proofs (e.g., during include-book). However, a new ``measure'', (:? v1 ... vk), is supported, for specifying a measured subset of the set of formals, i.e., a set of formals that serves as the set of parameters for some valid measure.

    (encapsulate
     ()
    
     (local (defun foo (x y)
              (declare (xargs :measure (acl2-count y)))
              (if (and (consp x) (consp y))
                  (foo (cons x x) (cdr y))
                y)))
    
    ; So the following is redundant -- but it guesses a measure
    ; of (acl2-count x), which isn't right!
     (defun foo (x y)
       (if (and (consp x) (consp y))
           (foo (cons x x) (cdr y))
         y)))
    
    ; end of encapsulate
    
    ; Now we prove a non-theorem by exploiting the bug above,
    ; erroneously replacing formal y by a constant in the induction
    ; scheme hinted below.  (This should not be allowed, as y should be
    ; labeled as a measured formal.)
    
    (defthm bad
      (atom x)
      :rule-classes nil
      :hints (("Goal" :induct (foo x '(3)))))
    
    ; It's easy to get a contradiction by instantiating the
    ; non-theorem just above.
    (defthm contradiction
      nil
      :rule-classes nil
      :hints (("Goal" :use ((:instance bad (x '(7)))))))
    

    Fixed a bug in :pl and the proof-checker's show-rewrites (sr) command that was causing a Lisp break. For :pl, also improved the display of unifying substitutions, modified output to take binding hypotheses (equal var term) into account properly, and arranged for inclusion of meta rules that modify the given term. Thanks to Eric Smith for bringing these issues to our attention.

    Introduced new utilities for undoing commands, :ubu and :ubu!, which are analogous to :ubt and :ubt! (respectively) except that they only undo back up to, but not including, the indicated command.

    Fixed a performance bug, pointed out by Eric Smith, that was negating efforts made for the preceding release to avoid computation for disabled warnings.

    Added time$ and value-triple to *acl2-exports*. Thanks to Bob Boyer and Erik Reeber (respectively) for bringing these issues to our attention.

    Improved the automatic proclaiming of function types for GCL and OpenMCL, specifically to use an output format consistent with the Common Lisp spec. Thanks to Bob Boyer for bringing this issue to our attention.

    Added books/misc/transfinite.lisp, which deals with transfinite induction in ACL2. Thanks to Eric Smith for contributing this book.

    Added books/misc/process-book-readme.lisp to the distribution. Thanks to Sandip Ray for pointing out its omission.

    Added contributions books/concurrent-programs/bakery/ and books/concurrent-programs/german-protocol/. These contributions can be used as tutorials, especially by new ACL2 users, for learning how to model concurrent protocols in ACL2 and the steps involved in reasoning about their correctness. Thanks to Sandip Ray for these contributions. See the Readme.lsp files in these directories.

    Theory invariants may now involve the variable ENS instead of the variable THEORY. The practical effect of this change is that any expression of the form (MEMBER-EQUAL rune THEORY) occurring in a theory-invariant expression should be replaced by (ACTIVE-RUNEP rune). See theory-invariant. Thanks to Eric Smith and Dave Greve for pointing out an inefficiency in the handling of theory invariants that led to this change, which can speed up their handling by orders of magnitude on large examples, and to Eric for testing this change and pointing out problems with an early implementation of it.

    Theory invariants (see theory-invariant) are no longer checked on theories defined by deftheory events. After all, one can define a theory with deftheory that is not intended to be used as the current theory, but rather is intended to be combined with other theories (see theory-functions). Thanks to Eric Smith for bringing this issue to our attention.

    Theory-invariant errors had been reported with very little detail when warnings were inhibited. This problem has been fixed; thanks to Eric Smith for bringing it to our attention and providing an example. We have also improved the handling of redundancy for theory-invariant events.

    The macro defun-sk now has a new optional keyword, rewrite, that can be used to change the form of the :rewrite rule generated when the quantifier is forall. Thanks to Eric Smith and Sandip Ray for useful discussions on this topic. We have also slightly modified the hints for the defthm event underneath a defun-sk in order to make the proof more reliably efficient.

    A new event, reset-prehistory, allows setting of a barrier before which undoing is illegal. An argument to this macro allows the barrier to be made permanent; otherwise, it can be removed with :ubt-prehistory. Thanks to Peter Dillinger for useful conversations leading to the addition of reset-prehistory.

    A new query, (wormhole-p state), allows users to determine whether or not they are in a wormhole. Thanks to Peter Dillinger for providing this utility.

    Value-triple no longer evaluates its form during include-book, and in raw Lisp its calls trivially macroexpand to nil, without any consideration of its argument. This change avoids errors and warnings when stobj names occur in the argument.

    We fixed what could be considered a soundness hole that could occur by exploiting redefinition in a particular way. Thanks to Peter Dillinger for raising a question that led to discovery of this hole.

    A bug has been fixed in handling of illegal theory expressions. Thanks to Eric Smith, who reported this problem and provided the example (in-theory '((:definition natp) (:rewrite doesntexist))) to show how a hard error could occur.

    Improved error reporting by certify-book when the certification world contains inadmissible forms.

    Modified defchoose to add two new keyword arguments. There is now a :doc keyword argument; previously, an optional documentation string (see doc-string) was to be placed just before the body, without a keyword. There is also a :strengthen argument that strengthens the axiom added, which allows for the definition of ``fixing'' functions for equivalence relations that choose canonical representatives of equivalence classes. See defchoose. Thanks for Dave Greve for useful discussions that led us to this :strengthen enhancement.

    Added books/misc/bash.lisp, which provides utilities for simplifying a goal into a list of subgoals (as documented at the top of that file). Thanks to Dave Greve for requesting this utility and suggesting refinements to its functionality, which have been incorporated.

    (For Emacs users only) The command meta-x new-shell provided by file emacs/emacs-acl2.el now puts you in shell-mode, which for example supports directory tracking. Thanks to Jared Davis for suggesting this change.

    Fixed some mishandling of stobjs by make-event expansion.

    Introduced a new event, defttag, that introduces a ``trust tag'' (``ttag'') allowing for extensions of ACL2 and for the use of generally unsafe ACL2 constructs. Thanks to Peter Dillinger, Sandip Ray, and Erik Reeber for useful discussions on defttag and the following related items.

    A new event, remove-untouchable, can be used to give users access to system functions and data structures. We also fixed a bug in push-untouchable; and, it no longer is a no-op in :program mode. Thanks to Peter Dillinger for proposing remove-untouchable and suggesting that it and push-untouchable be functional in :program mode.

    Raw-mode (see set-raw-mode) no longer disables certify-book. However, set-raw-mode is now disallowed unless there is an active ttag (see defttag). If you want to execute (set-raw-mode t) and there is no active ttag, consider executing (set-raw-mode-on!) instead.

    Redefinition of system functions is disallowed unless there is an active ttag. However, redef! now introduces (defttag :redef!) in order to allow redefinition of system functions.

    A new event, progn!, is a legal embedded event form that can go in books and both encapsulate and progn forms (see embedded-event-form), and is similar to progn except that it allows arbitrary forms. Thus, a progn! form is potentially dangerous and can only be evaluated if there is an active ttag.

    See ttags-seen for information about how to find the ttags known in the current ACL2 world, and for related caveats.

    A new book created with Peter Dillinger, books/misc/hacker.lisp (added after Version_3.3: now books/hacking/hacker.lisp), uses progn! to define utiliities with-raw-mode and with-redef-allowed, which respectively allow raw Lisp evaluation and redefinition to take place within a certifiable book (!).

    Macro with-output is no longer allowed in function bodies because it does not have (and has never had) any effect in raw Lisp. See with-output for a workaround.

    Fixed a bug in redundancy of defstobj in raw Lisp, which caused an error when certifying a book with a redundant defstobj event whose stobj had already been modified. Here is an example:

    (defstobj st fld)
    (update-fld 3 st)
    (certify-book "foo" 1) ; where foo.lisp contains (defstobj st fld)
    

    New books illustrating make-event have been contributed in directory books/make-event/: dotimes.lisp (David Rager), stobj-test.lisp, and logical-tangent.lisp (Peter Dillinger).

    Modified print-object$ (see io) so that it no longer prints an extra space at the end.

    Replaced the ``draconian restriction to avoid capture'' that had prevented some :functional-instance hints from being legal. The corresponding check now only requires that no variable free in the functional substitution is captured by a let or mv-let (or lambda) binding. See lemma-instance.

    Added new extended metafunction, mfc-rw+, which is equivalent to mfc-rw except that it takes an alist argument, which may be useful for efficiency. See extended-metafunctions. Thanks to Robert Krug for suggesting this more efficient variant of mfc-rw.

    Added support for the ignorable declare form.

    We now cause an error on a call of open-input-channel (see io) with an argument string whose first character is the | character. Thanks to Bob Boyer for providing an example (several months ago) showing the danger of such calls, namely that the following command would log you out and kill all of your processes when running on top of GCL in Linux:
    (open-input-channel "|kill -9 -1" :object state)

    Restricted the use of make-event to contexts in which it can be tracked properly, under legal events (see embedded-event-form). Thanks to Peter Dillinger for bringing an example to our attention that led to this fix.

    Fixed a bug that was avoiding guard-checking for the functions compress1 and compress2. Thanks to David Rager for bringing this bug to our attention.

    Added an error message when a defun or mutual-recursion event fails, to clarify whether failure is for the measure conjecture or for the guard conjecture. Thanks to David Rager for requesting clarification for such failures.

    Fixed a bug in reporting of guard violations (hard Lisp error) when certain macros (for example, cond) are used in the guard. Thanks to Jared Davis for bringing this problem to our attention and providing assistance with the solution, in particular by providing a helpful example.

    Grant Passmore has contributed a resolution/paramodulation prover written in ACL2, in directory books/deduction/passmore/. Thanks, Grant.

    Improved the error message when illegal theories are encountered.

    Improved the suppression of output for inhibit-output arguments of routines in the book books/misc/expander.lisp. Thanks to Qiang Zhang for pointing out the possibility for improvement here.

    Added a new directory books/arithmetic-3/extra/ that extends books/arithmetic-3 with additional rules, contributed by Alex Spiridonov with guidance from Robert Krug. WARNING: This directory is under development. It may undergo large changes in future releases, so please consider it experimental and subject to change. Feedback is welcomed.

    As part of the work mentioned just above, Robert Krug and Alex Spiridonov contributed improvements to books/arithmetic-3/:

    o A new rule |(* (/ x) (/ (expt x n)))| in bind-free/collect.lisp, which is important for reducing collect-* expressions though it slowed down one proof (see comment above this rule in bind-free/collect.lisp).

    o Slight improvements of rules integerp-mod and rationalp-mod in floor-mod/floor-mod.lisp.

    o To avoid conflict with books/rtl/rel6/arithmetic/, renamed rule mod-minus to mod-neg in floor-mod/floor-mod.lisp, and renamed integerp-+-reduce-leading-constant to integerp-+-reduce-leading-rational-constant in bind-free/integerp.lisp.

    (GCL on Windows only) Made a low-level change to avoid multiplying stacks for GCL on Windows, since GCL 2.6.6 broke while doing this.

    Fixed bugs in linear arithmetic (rarely evidenced, it seems) involving using < to compare complex rational constants. Thanks to Robert Krug for helping with the fixes.

    Added a new event, assert-event, for checking that forms evaluate to non-nil values. Thanks to Peter Dillinger for suggesting and collaborating on this addition.




    acl2-sources/doc/HTML/NOTE-3-0.html0000664002132200015000000000275612222333527016027 0ustar kaufmannacl2 NOTE-3-0.html -- ACL2 Version 6.3

    NOTE-3-0

    ACL2 Version 3.0 (June, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    Please see note-2-9-5 for a description of changes since Version 2.9.4. These include the new make-event feature, a soundness bug fix, an improvement for :expand hints, evaluation in the logic by way of :set-guard-checking :none, and many other improvements.

    More generally, there have been several incremental releases since Version 2.9: see note-2-9-1, see note-2-9-2, see note-2-9-3, see note-2-9-4, and see note-2-9-5.

    A very few users have contributed books following the instructions on the web. We expect that when more contributions come in, we will give more attention to the question of how to organize the distributed and workshop books. For now, we have simply added the new contributions according to the old-style distribution methodology.




    acl2-sources/doc/HTML/NOTE-3-0_lparen_R_rparen_.html0000664002132200015000000000117012222333527021344 0ustar kaufmannacl2 NOTE-3-0_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-0(R)

    ACL2 Version 3.0(r) (June, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    No significant changes have been made since Version 2.9 for support of non-standard analysis in particular.

    Please also see note-3-0 for changes to Version 3.0 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-1.html0000664002132200015000000000136712222333527016025 0ustar kaufmannacl2 NOTE-3-1.html -- ACL2 Version 6.3

    NOTE-3-1

    ACL2 Version 3.1 (December, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Please see note-3-0-2 for a description of changes since Version 3.0.1, and also see note-3-0-1 for additional changes since Version 3.0.




    acl2-sources/doc/HTML/NOTE-3-1_lparen_R_rparen_.html0000664002132200015000000000120212222333527021341 0ustar kaufmannacl2 NOTE-3-1_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-1(R)

    ACL2 Version 3.1(r) (December, 2006) Notes
    Major Section:  RELEASE-NOTES
    

    No significant changes have been made since Version 3.0 for support of non-standard analysis in particular.

    Please also see note-3-1 for changes to Version 3.1 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-2-1.html0000664002132200015000000002567312222333527016172 0ustar kaufmannacl2 NOTE-3-2-1.html -- ACL2 Version 6.3

    NOTE-3-2-1

    ACL2 Version 3.2.1 (June, 2007) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    (OpenMCL and multi-threaded SBCL only) Fixed a soundness bug in the evaluation of mbe forms in the presence of Lisp feature :acl2-mv-as-values. Thanks to Sol Swords for reporting this problem and sending a simple proof of nil (which can be found in a comment in the ACL2 sources, in (deflabel note-3-2-1 ...)).

    Added a new utility, dmr (Dynamicaly Monitor Rewrites), for watching the activity of the rewriter and some other proof processes. See dmr. We thank Robert Krug for useful contributions.

    Fixed a bug in evaluation of calls of with-prover-time-limit.

    Fixed the writing of executable scripts when building ACL2, so that the build-time value of environment variable ACL2_SYSTEM_BOOKS is no longer written there. Thanks to Dave Greve for discussing this change.

    Fixed bugs in :pl (which are similarly present in the proof-checker's sr (show-rewrites) command. The first bug was evident from the following forms sent by Robert Krug, which caused an error.

    (include-book "arithmetic-3/floor-mod/floor-mod" :dir :system)
    :pl (mod (+ 1 x) n)
    
    The second bug was due to a failure to take note of which rules are disabled, and could be seen by executing the following (very slow!).
    (defstub modulus () t)
    (include-book "arithmetic-3/floor-mod/floor-mod" :dir :system)
    :pl (mod (+ x y) (modulus))
    

    Modified certify-book so that by default, all executable-counterpart functions (sometimes called ``*1* functions'') are compiled. This is the behavior that was already supported with a compile-flg argument of :all; the change is that argument t now has this behavior as well (and :all is supported only for legacy purposes). A new value for compile-flg, :raw, gives the behavior formerly produced by value t, namely where executable-counterpart functions are not compiled. The above changes are irrelevant if compilation is suppressed; see compilation. Finally, if environment variable ACL2_COMPILE_FLG is set, then after converting to upper-case this environment variable's value of "T", "NIL", or ":RAW" will determine the value of the optional compile-flg argument to be t, nil, or :raw, respectively, when this argument is not explicitly supplied.

    Modified include-book so that :comp argument now acts like :comp!, i.e., compiling a file that includes the file together with all executable counterpart (so-called ``*1*'') functions. A new argument, :comp-raw, has the behavior that :comp had formerly, i.e., compiling the actual book only.

    The function nonnegative-integer-quotient is now computed in raw Lisp by calling floor on its arguments. This change was suggested by Peter Dillinger, in order to avoid stack overflows such as reported by Daron Vroon. A new book, books/misc/misc2/misc.lisp, contains a proof of equivalence of nonnegative-integer-quotient and floor, and serves as a repository for other miscellaeous proofs, including those justifying ACL2 modifications such as this one.

    Enhanced accumulated-persistence to break down results by useful vs. useless rule applications. In particular, this provides information about which rules were ever applied successfully, as requested by Bill Young.

    Added coverage of :meta rules to the accumulated-persistence statistics.

    Fixed a bug that was causing a :clause-processor hint to fire on a subgoal of the goal to which it was attached, when the original application didn't change the clause. Thanks to Dave Greve for pointing out this bug and providing a useful example.

    Fixed a bug in handling of computed hints related to the stable-under-simplificationp parameter (see computed-hints). There were actually two bugs. A minor but confusing bug was that the same goal was printed twice upon application of such a hint. The major bug was that :use hints (as well as other ``top'' hints: :by, :cases, and :clause-processor) were not being applied properly. Thanks to Jared Davis for sending an example some time ago that showed the duplicate printing, and to Dave Greve for sending an example showing mis-application of :clause-processor hints. Note that you may find that existing computed hints using the stable-under-simplificationp parameter no longer have the same behavior; see a comment about computed hints in note-3-2-1, ACL2 source file ld.lisp, for an example of how you might want to fix such computed hints.

    David Russinoff has contributed an updated version of books/quadratic-reciprocity/ including minor modifications of the treatment of prime numbers and a proof that there exist infinitely many primes. Thanks to David for contributing this work, and to Jose Luis Ruiz-Reina for posing the challenge.

    Reduced the sizes of some certificate (.cert) files by relaxing the test that allows original defpkg events to be placed there, rather than evaluating the import list term into an explicit list of symbols.

    Improved execution efficiency slightly for function rcdp in file books/misc/records.lisp, by using mbe to introduce a tail-recursive body.

    The executable script generated by save-exec (and by the normal build process) now includes a time stamp as a comment. Thanks to Jared Davis for suggesting this change in order to support his use of omake. In the process, we also arranged that the startup banner for an executable created by save-exec shows all of the build (save) times, not just the one for the original image.

    Sped up most redundant defpkg events by avoiding evaluation and sorting of the imports list in the case of identical event forms. And, for defpkg events that are not redundant, sped up their processing in Allegro CL (and perhaps other Lisps, but apparently not GCL) by using our own import function.

    Modified add-include-book-dir so that it refuses to associate a keyword with a different directory string than one it is already bound to. See delete-include-book-dir for how to remove the existing binding first. Thanks to Robert Krug for pointing out that without this change, one can find it difficult to debug a failure due to rebinding a keyword with add-include-book-dir.

    Added a new value for the :do-not-induct hint (see hints), :otf-flg-override, which causes ACL2 to ignore the :otf-flg when considering whether to abort the proof because of a :do-not-induct hint. Thanks to Daron Vroon for suggesting such an addition.

    Modified the printing of messages for entering and exiting raw mode (see set-raw-mode), so that in particular they are inhibited during include-book or whenever observations are inhibited (see set-inhibit-output-lst). Thanks to Peter Dillinger for suggesting such a change.

    (For system hackers only.) The handling of events of the form (progn! (state-global-let* ...)) had a bug that was causing bindings to be evaluated twice. Moreover, the use of system function state-global-let* is suspect in raw Lisp. We have eliminated special treatment of state-global-let* by progn! in favor of a new keyword argument, state-global-bindings, that provides the intended functionality. See progn!. Moreover, special handling that allowed make-event forms under state-global-let* has been removed; the desired effect can be obtained using (progn! :state-global-bindings ...). Thanks to Peter Dillinger for pointing out the above bug and collaborating on these changes.

    Incorporated backward-compatible enhancements to books/misc/expander.lisp from Daron Vroon (documented near the top of that file).

    The specification of :backchain-limit-lst had required that only a single (:rewrite, :linear, or :meta) rule be generated. We have weakened this restriction to allow more than one rule provided that each rule has the same list of hypotheses. For example, the rule class (:rewrite :backchain-limit-lst 1) is now legal for the corollary formula (implies (f x) (and (g x) (h x))), where this was not formerly the case. Thanks to Dave Greve for bringing this issue to our attention.




    acl2-sources/doc/HTML/NOTE-3-2-1_lparen_R_rparen_.html0000664002132200015000000000102512222333527021503 0ustar kaufmannacl2 NOTE-3-2-1_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-2-1(R)

    ACL2 Version 3.2.1(r) (June, 2007) Notes
    Major Section:  RELEASE-NOTES
    

    Please also see note-3-2-1 for changes to Version 3.2.1 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-2.html0000664002132200015000000005761212222333527016032 0ustar kaufmannacl2 NOTE-3-2.html -- ACL2 Version 6.3

    NOTE-3-2

    ACL2 Version 3.2 (April, 2007) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Before this release, a raw Lisp error could put the ACL2 user into the debugger of the host Common Lisp. Now, such cases will generally put the user back at the top-level loop after an informative message. For details, see set-debugger-enable; also see break$.

    Fixed a soundness bug that was allowing unknown packages to sneak into a book and wreak havoc. Thanks to Peter Dillinger for sending an interesting example that led us to an exploration resulting in finding this bug. (A comment in the source code for note-3-2 shows such an example.) That example led us to fix a couple of other bugs related to packages. See hidden-death-package if you are generally interested in such issues, and for associated examples, see comments in note-3-2 in the ACL2 source code.

    Fixed subtle soundness bugs related to :meta rules by restricting evaluators (see defevaluator), as discussed in a new documentation topic: see evaluator-restrictions.

    Fixed a soundness bug that was allowing redefinition from :logic to :program mode. This prohibition had been in ACL2 for awhile but was accidentally removed in the preceding version.

    Fixed a soundness bug related to trace$. Thanks to Peter Dillinger for bringing it to our attention and for useful discussions, and providing a proof of nil, the essence of which is illustrated as follows:

    (value-triple (trace$ (bar :entry (defun foo () 17))))
    
    Thus, trace$ could be used to cause destructive raw Lisp behavior. Now, trace$ fails unless it is either given a list of symbols or else there is an active trust tag (see defttag); otherwise, consider using trace! instead.

    Closed a loophole that could be viewed as compromising soundness. It was possible to write files during book certification by exploiting make-event expansion, but that is no longer the case by default. A new function open-output-channel! is identical as a function to open-output-channel, except that the new function may be called even during make-event expansion and clause-processor hints, but requires that there is an active trust tag (see defttag). Thanks to Peter Dillinger for producing a convincing example (forging a certificate during book certification; see open-output-channel!) and to him, Sandip Ray, and Jared Davis for useful discussions on the topic.

    Added book books/defexec/reflexive/reflexive.lisp to illustrate reflexive functions.

    ACL2 now generate scripts that invoke the saved image with exec. (Previously this was only done for GCL and CLISP.) The benefit of this change can be to avoid the lingering of ACL2 processes after enclosing processes have exited. Thanks to Peter Dillinger for pointing out this issue.

    ACL2 has a better implementation of (good-bye) (hence of synonyms (quit) and (exit)). As a result, you should now be able to exit ACL2 and Lisp from within the ACL2 read-eval-print loop with any of the above; formerly, this was not supported for some Lisp implementations, and was slow in OpenMCL. Thanks to SBCL developer Harald Hanche-Olsen for useful advice.

    Fixed a bug in raw-mode (see set-raw-mode) that was causing hard errors when evaluating calls of er-progn, or of macros expanding to such calls.

    Fixed a few Makefile dependencies, necessary only for parallel `make'.

    A new book, misc/defpun-exec-domain-example.lisp, provides an example showing how partial functions which return a unique value for arguments in a specified domain can be efficiently executed with ACL2. Execution is achieved using the mbe construct. Thanks to Sandip Ray for providing this example.

    Existing function mod-expt computes (mod (expt base exp) mod) with great efficiency in GCL, but not in other Lisps. Now, the book arithmetic-3/floor-mod/mod-expt-fast.lisp defines a function mod-expt-fast that should provide significantly improved performance for such expressions in other Lisps as well, though still probably not as fast as when using mod-expt in GCL. Thanks to Warren Hunt, with contributions from Robert Krug, for providing this book,

    Modified macro break-on-error to print of an error message before entering a break, and to cause a hard error if the underlying Lisp cannot handle it (formerly, a raw Lisp break would occur). Thanks to Bob Boyer for bringing these issues to our attention.

    The book books/misc/defpun.lisp, as well as other books related to the defpun macro, has been modified to avoid namespace collisions by prefixing function symbol names with "DEFPUN-"; for example base has been replaced by defpun-base. Thanks to Dave Greve for providing a first version of this update to defpun.lisp.

    A theory, base, in books/arithmetic-3/bind-free/top.lisp, has been renamed arithmetic-3-bind-free-base, to avoid potential name conflicts.

    Fixed books/arithmetic-3/bind-free/banner.lisp to print (as before) a message about how to turn on non-linear arithmetic, by modifying the call of value-triple to use :on-skip-proofs t. Thanks to Robert Krug for bringing this issue to our attention.

    Modified books/Makefile-subdirs and books/Makefile-psubdirs so that they can be used with books/Makefile-generic. Thus, one can set things up so that `make' can be used to certify books both in the current directory and subdirectories, for example as follows.

      ACL2 = ../../saved_acl2
    
      arith-top: top all
      all: top
    
      DIRS = pass1 bind-free floor-mod
      include ../Makefile-subdirs
      include ../Makefile-generic
    
      top.cert: top.lisp
      top.cert: bind-free/top.cert
      top.cert: floor-mod/floor-mod.cert
      top.cert: floor-mod/mod-expt-fast.cert
    

    An experimental extension of ACL2 is under development by Bob Boyer and Warren Hunt to support function memoization, hash conses, and an applicative version of hash tables. The default build of ACL2 does not include this extension, other than simple logic definitions of functions in new source file hons.lisp. Future versions of ACL2 may fully incorporate this experimental extension.

    The defevaluator event macro has been modified primarily by adding a new constraint as follows, where evl is the evaluator. The idea is that for the evaluation of a function call, one may replace each argument by the quotation of its evaluation and then also replace the alist environment with nil.

    (DEFTHMD UNHIDE-evl-CONSTRAINT-0
      (IMPLIES (AND (CONSP X)
                    (SYNTAXP (NOT (EQUAL A ''NIL)))
                    (NOT (EQUAL (CAR X) 'QUOTE)))
               (EQUAL (evl X A)
                      (evl (CONS (CAR X)
                                 (KWOTE-LST (UNHIDE-evl-LIST (CDR X) A)))
                           NIL))))
    
    In order to support this change, there is another change: an evaluator maps nil to nil (note (AND X (CDR (ASSOC-EQ X A))) in place of (CDR (ASSOC-EQ X A)) below).
    (DEFTHM UNHIDE-evl-CONSTRAINT-1
      (IMPLIES (SYMBOLP X)
               (EQUAL (UNHIDE-evl X A)
                      (AND X (CDR (ASSOC-EQ X A))))))
    
    With the new defevaluator, Dave Greve has been able to do a proof about beta reduction that seemed impossible before (see books/misc/beta-reduce.lisp). Thanks to Dave for suggesting an initial version of this change.

    Explicit compilation is now avoided for OpenMCL, resulting in fewer files to manage (no more files resulting from compilation) and, according to some tests, slightly faster run times. See compilation. Thanks to Bob Boyer and Warren Hunt for suggesting this possibility.

    Now, the term-evisc-tuple (see ld-evisc-tuple) is overridden by state global user-term-evisc-tuple in all cases. Formerly, this was only the case when term-evisc-tuple was called with non-nil first argument.

    Symbols with the dot (.) character are generally no longer printed with vertical bars. For example, before this change:

      ACL2 !>'ab.c
      |AB.C|
      ACL2 !>
    
    After this change:
      ACL2 !>'ab.c
      AB.C
      ACL2 !>
    
    Thanks to Jared Davis for suggesting this improvement.

    Fixed bugs in guard verification for theorems. The following examples illustrate these bugs. If either theorem's body is executed in raw Lisp there is likely to be a hard Lisp error, even though verify-guards was supposed to ensure against that behavior.

    ; Example: Verify-guards failed to check that all functions in the theorem
    ; had already been guard-verified.
    (defun my-car (x) (car x))
    (defthm my-car-compute-example (equal (my-car 3) (my-car 3)))
    (verify-guards my-car-compute-example)
    
    ; Example: Verify guards of a theorem whose body uses state improperly.
    (defthm bad-state-handler
      (if (state-p state)
          (equal (car state) (car state))
        t)
      :rule-classes nil)
    (verify-guards bad-state-handler)
    

    See GCL for an example, developed with Warren Hunt and Serita Nelesen, that shows how to get fast fixnum (small integer) arithmetic operations in GCL.

    Fixnum declarations are now realized as (signed-byte 30) and (unsigned-byte 29) instead of what was generally (signed-byte 29) and (unsigned-byte 28). MCL users may thus find better performance if they switch to OpenMCL. Note that some definitions have changed correspondingly; for example, zpf now declares its argument to be of type (unsigned-byte 29) instead of (unsigned-byte 28). A few books may thus need to be adjusted; for example, changes were made to books in books/data-structures/memories/.

    ACL2's rewriter now avoids removing certain true hypotheses and false conclusions. When a hypothesis rewrites to true or a conclusion rewrites to false, ACL2 formerly removed that hypothesis or conclusion. Now, it only does such removal when the hypothesis or conclusion is either a call of equal or an equivalence relation (see equivalence), or else is sufficiently trivial (roughly, either redundant with another hypothesis or conclusion or else trivially true without considering the rest of the goal). A specific example may be found in source file simplify.lisp; search for ``; But we need to do even more work''. Thanks to Robert Krug for providing the idea for this improvement and its initial implementation. As is common with heuristic changes, you may find it necessary on occasion to rename some subgoals in your hints. And in this case, you might also find it necessary on rare occasions to add :do-not '(generalize) hints.

    A new function, mfc-relieve-hyp, allows (for example) for more powerful bind-free hypotheses, by providing an interface to the rewriter's routine for relieving hypotheses. See extended-metafunctions. Thanks to Robert Krug for providing the idea for this feature and its initial implementation.

    Two improvements have been made to non-linear arithmetic (see non-linear-arithmetic). One allows for deducing strict inequality (<) for the result of certain polynomial multiplications, where previously only non-strict inequality (<=) was deduced. A second allows the use of the product of two polynomials when at least one of them is known to be rational. We had previously restricted the use of the product to the case where both were known to be rational. Thanks to Robert Krug for these improvements.

    (OpenMCL and Allegro CL only) Fixed ACL2's redefinitions of raw Lisp trace and untrace in OpenMCL and Allegro CL so that when given no arguments, they return the list of traced functions. For trace, this is an ANSI spec requirement. Note that trace$ and untrace$ continue to return nil in the ACL2 loop.

    Fixed a bug that was allowing the symbol &whole to appear in other than the first argument position for a defmacro event, in violation of the Common Lisp spec (and leading to potentially surprising behavior). Thanks to Peter Dillinger for bringing this bug to our attention.

    It had been illegal to use make-event under some calls of ld. This has been fixed. Thanks to Jared Davis for bringing this issue to our attention with a simple example, in essence:

    (ld '((defmacro my-defun (&rest args) `(make-event '(defun ,@args)))
          (my-defun f (x) x)))
    

    ACL2 no longer prohibits certain make-event forms when including uncertified books. Thanks to Peter Dillinger for first bringing this issue to our attention.

    Hard errors arose when using break-rewrite stack display commands, in particular :path and :frame, from inside the proof-checker. This has been fixed.

    Fixed a bug that could cause functions that call system built-ins f-put-global, f-get-global, or f-boundp-global to cause a raw Lisp error even when proving theorems. Thanks to Peter Dillinger, for reporting such a failure for the form (thm (w '(1 2 3))).

    Renamed the formal parameters of function set-equal in distributed book books/arithmetic-3/bind-free/normalize.lisp so that more distributed books can be included together in the same session. In particular books books/data-structures/set-theory and books/arithmetic-3/extra/top-ext can now be included together. Thanks to Carl Eastlund for bringing this problem to our attention and to Robert Krug for suggesting the formals renaming as a fix.

    Metafunctions must now be executable. See meta.

    New utilities allow for user-defined simplifiers at the goal level, both verified and unverified (``trusted''), where the latter can even be defined by programs outside ACL2. See clause-processor, which points to a new directory books/clause-processors/ that contains examples of these new utilities, including for example a system (``SULFA'') contributed by Erik Reeber that implements a decision procedure (thanks, Erik). Also see proof-checker-commands for the new proof-checker command clause-processor (or for short, cl-proc).

    The rewriter has been tweaked to run faster in some cases involving very large terms. Thanks to Eric Smith and Jared Davis for providing a helpful example that helped us locate the source of this inefficiency.

    Added books/make-event/defspec.lisp. This book shows how one can mimic certain limited forms of higher-order statements in ACL2 by use of macros, make-event, and table events. Thanks to Sandip Ray for his contribution.

    A new release of the RTL library, books/rtl/rel7/, replaces the previous version, books/rtl/rel6/. Thanks to Hanbing Liu and David Russinoff for providing this new version.

    We thank David Russinoff for providing a proof of the law of quadratic reciprocity. See books/quadratic-reciprocity/Readme.lsp.

    Eliminated a slow array warning (see slow-array-warning) that could occur when exiting a wormhole after executing an in-theory event in that wormhole. Thanks to Dave Greve for bringing this problem to our attention.

    A new accessor, (mfc-rdepth mfc), provides a new field, the remaining rewrite stack depth, which has been added to metafunction context structures; see extended-metafunctions. Thanks to Eric Smith for suggesting this addition.

    The algorithms were modified for collecting up rule names and other information used in proofs, into so-called ``tag-trees''. Tag-trees are now free of duplicate objects, and this change can dramatically speed up some proofs that involve many different rules. Thanks to Eric Smith for doing some profiling that brought this issue to our attention, and for reporting that this change reduced proof time on an example by about 47% (from 3681.46 reported seconds down to 1954.69).

    All legal xargs keywords may now be used in verify-termination events. In particular, this is the case for :normalize.

    (SBCL and CMUCL only) Fixed a problem with stobj array resizing functions that was causing a hard error in ACL2 images built on SBCL or CMUCL.

    A new table, evisc-table, allows you to introduce print abbreviations, for example for large constants. Moreover, a new reader macro -- #, -- makes it convenient to reference constants even inside a quote. See evisc-table. Thanks to Bob Boyer and Warren Hunt for useful discussions leading to this feature.

    The macros in books/misc/expander.lisp now have a new keyword argument, :simplify-hyps-p. The default behavior is as before, but now case splitting from hypothesis simplification can be avoided. For details, evaluate (include-book "misc/expander" :dir :system) and then :doc! defthm? and :doc! symsym. Thanks to Daron Vroon for sending a question that prompted this additional functionality.

    ACL2 failed to apply :restrict hints to rules of class :definition, except for the simplest sorts (see simple). This has been fixed. Thanks to Jared Davis for pointing out this bug by sending a small example.

    Added a new :msg argument to assert-event; see assert-event. The implementation of value-triple has been modified to support this change.

    Fixed a bug in macro io? that now allows the commentp argument to be t. This provides a way other than cw to print without modifying state, for example as follows. (Warning: Certain errors may leave you in a wormhole, in which case use :a! to abort.)

    ACL2 !>(prog2$ (io? event t state
                        ()
                        (fms "Howdy~%" nil *standard-co* state nil))
                   (+ 3 4))
    
    Howdy
    7
    ACL2 !>:set-inhibit-output-lst (proof-tree event)
     (PROOF-TREE EVENT)
    ACL2 !>(prog2$ (io? event t state
                        ()
                        (fms "Howdy~%" nil *standard-co* state nil))
                   (+ 3 4))
    7
    ACL2 !>
    

    ACL2 now disallows calls of progn! inside function bodies, just as it already disallowed such calls of progn, since in both cases the Common Lisp meaning differs from the ACL2 meaning.

    Redefinition of system functions now always requires an active trust tag (see defttag). This restriction was intended before, but there was a hole that allowed a second redefinition without an active trust tag. Thanks to Peter Dillinger for pointing out this bug.

    Verify-termination has been disabled for a few more built-in functions that are in :program mode. (If you are curious about which ones they are, evaluate (f-get-global 'built-in-program-mode-fns state).) [Note added for Version_3.4: This state global has been changed to 'program-fns-with-raw-code.] Moreover, such functions now will execute only their raw Lisp code, so for example they cannot be called during macroexpansion. Thanks to Peter Dillinger and Sandip Ray for useful discussions on details of the implementation of this restriction.

    New untouchable state global variables, temp-touchable-vars and temp-touchable-fns, can control the enforcement of untouchability. See remove-untouchable. Thanks to Peter Dillinger for suggesting these features.

    The ``TTAG NOTE'' string was being printed by encapsulate events whenever an active trust tag was already in effect (see defttag), even if the encapsulate event contained no defttag event. This has been fixed. Thanks to Peter Dillinger for a query leading to this fix.

    Fixed a bug in progn! that could leave the user in raw-mode (see set-raw-mode). This could occur when certifying a book with a compile-flg value of t (see certify-book), when that book contained a progn! event setting raw-mode to t without setting raw-mode back to nil:

    (progn! (set-raw-mode t) ...)
    




    acl2-sources/doc/HTML/NOTE-3-2_lparen_R_rparen_.html0000664002132200015000000000247212222333527021354 0ustar kaufmannacl2 NOTE-3-2_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-2(R)

    ACL2 Version 3.2(r) (April, 2007) Notes
    Major Section:  RELEASE-NOTES
    

    Changed the default distributed books directory for ACL2(r) from books/ to books/nonstd/. See include-book, in particular the discussion of ``Distributed Books Directory''.

    Added directory books/arithmetic-3/ and its subdirectories to books/nonstd/. (But a chunk of theorems from arithmetic-3/extra/ext.lisp are ``commented out'' using #-:non-standard-analysis because they depend on books/rtl/rel7/, which is not yet in books/nonstd/; feel free to volunteer to remedy this!)

    Incorporated changes from Ruben Gamboa to some (linear and non-linear) arithmetic routines in the theorem prover, to comprehend the reals rather than only the rationals.

    Please also see note-3-2 for changes to Version 3.2 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-3.html0000664002132200015000000005545012222333527016031 0ustar kaufmannacl2 NOTE-3-3.html -- ACL2 Version 6.3

    NOTE-3-3

    ACL2 Version 3.3 (November, 2007) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 3.2.1 into new features, bug fixes, prover algorithm enhancements, and miscellaneous. Also see note-3-2-1 for other changes since Version 3.2.

    NEW FEATURES

    A new ``gag-mode'' provides less verbose, more helpful feedback from the theorem prover, in support of The Method (see the-method). See set-gag-mode. We recommend the use of gag-mode, which may become the default in future ACL2 releases, and we welcome suggestions for improvement. We thank Robert Krug and Sandip Ray for helpful feedback in the design of gag-mode. Note that when proofs fail, then even without gag-mode and even if proof output is inhibited, the summary will contain a useful listing of so-called ``key checkpoints'' (see set-gag-mode).

    Added support for a leading `~' in filenames. Thanks to Bob Boyer for suggesting this enhancement. Note that since `~/' depends on the user, it is disallowed in books to be certified (see certify-book), since otherwise an include-book form in a book, b, could have a different meaning at certification time than at the time include-book is later executed on book b.

    Made a change to allow (time$ FORM) and (with-prover-time-limit TIME FORM) when FORM includes make-event calls that change the ACL2 world. Thanks to Jared Davis for requesting such support for time$.

    Computed hints (see computed-hints) may now produce a so-called ``error triple'', i.e., a result of the form (mv erp val state), where a non-nil erp causes an error, and otherwise val is the value of the hint. It remains legal for a computed hint to return a single ordinary value; indeed, the symbol form of a computed hint must still be a function that returns an ordinary single value.

    New hints provide additional control of the theorem prover, as follows. See hints for more details, and see new distributed book directory books/hints/ for examples, in particular file basic-tests.lisp in that directory for simple examples.

    o The hint :OR (hints-1 ... hints-k) causes an attempt to prove the specified goal using each hints-i in turn, until the first of these succeeds. If none succeeds, then the prover proceeds after heuristically choosing the ``best'' result, taking into account the goals pushed in each case for proof by induction.

    o A custom hint is a keyword that the user associates with a corresponding hint-generating function by invoking add-custom-keyword-hint. Thus, a custom hint may be viewed as a convenient sort of computed hint.

    o A custom hint, :MERGE, is implemented in distributed book books/hints/merge.lisp. It is useful for combining hints.

    o A sophisticated yet useful custom hint is the :CONSIDER hint implemented in distributed book books/hints/consider-hint.lisp. With this hint, you can for example give the equivalent of a :USE hint without the need to supply an instantiation. Include that book in order to see documentation online with :doc consideration, and see the book books/hints/consider-hint-tests.lisp for examples.

    A new hint, :reorder, allows the specification of which subgoals are to be considered first. Thanks to Sandip Ray for putting forward this idea.

    Enhanced set-saved-output by supporting a second argument of :same, which avoids changing which output is inhibited.

    Added macros thm? and not-thm? to distributed book books/make-event/eval.lisp, so that it's easy to test within a certified book that a proof attempt succeeds or that it fails.

    Added printing function cw!, which is analogous to cw just as fmt! is to fmt, i.e., printing so that the result can be read back in. Thanks to Jared Davis for suggesting this enhancement (after doing his own implementation).

    The ACL2 customization file can now be specified using environment variable ACL2-CUSTOMIZATION [note: starting with Version_4.0, ACL2_CUSTOMIZATION]. See acl2-customization. Thanks to Peter Dillinger for requesting this feature.

    Added new emacs capabilities for proof trees (all documented in emacs):

    o New function start-proof-tree-noninteractive, for example
    (start-proof-tree-noninteractive "*shell*")

    o C-z o Switch to another frame

    o C-z b Switch to prooftree buffer

    o C-z B Switch to prooftree buffer in "prooftree-frame" frame

    Added Common Lisp function, search, as a macro in logic mode, with limited support for keyword arguments. Thanks to Warren Hunt for requesting this addition.

    Sandip Ray has contributed a book, books/make-event/defrefine.lisp, that provides a collection of macros to aid in reasoning about ACL2 functions via refinement.

    Wrote and incorporated new utility for listing all the theorems in an included book. See books/misc/book-thms.lisp. Thanks to Jared Davis for requesting this functionality.

    The new distributed book misc/defp.lisp generalizes the defpun macro to allow more general forms of tail recursion.

    (Low-level printing improvement) A new function, set-ppr-flat-right-margin, allows the right margin for certain kinds of ``flat'' printing to exceed column 40. Thanks to Jared Davis for pointing out that state global variables 'fmt-hard-right-margin and 'fmt-soft-right-margin are not alone sufficient to extend the right margin in all cases.

    The event add-include-book-dir can now take a relative pathname as an argument. Formerly, it required an absolute pathname.

    A new book, books/misc/defopener.lisp, provides a utility creating a theorem that equates a term with its simplification.

    ACL2 now provides limited support for the Common Lisp primitive FLET, which supports local function bindings. See flet. Thanks to Warren Hunt for requesting this feature.

    Added a definition of boole$, a close analogue of Common Lisp function boole. Thanks to Bob Boyer for providing an initial implementation.

    BUG FIXES

    Fixed defstobj to inhibit a potentially useless theory warning.

    Fixed a bug in the application of certify-book to relative pathnames for files in other than the current directory. Thanks to Amr Helmy for bringing this bug to our attention.

    Fixed a bug in :pl and :pr for displaying rules of class :meta. Thanks to Jared Davis for finding this bug and providing a fix.

    Formerly, set-default-backchain-limit was not a legal event form for encapsulate forms and books. This has been fixed. Thanks to Robert Krug and Sandip Ray for bringing this bug to our attention.

    Fixed the handling of hints in proof-checker commands for the prover, such as bash -- see proof-checker-commands -- so that the user can override the default settings of hints, in particular of :do-not and :do-not-induct hints attached to "Goal". This fix also applies to the distributed book misc/bash.lisp, where Robert Krug noticed that he got an error with :hints (("Goal" :do-not '(preprocess))); we thank Robert for pointing out this problem.

    Fixed a bug in handling of stobjs occurring in guards of functions whose guards have been verified. In such cases, a raw Lisp error was possible when proving theorems about non-''live'' stobjs. We thank Daron Vroon for sending us an example that highlighted this bug. The following (simpler) example causes such an error in previous versions of ACL2.

    (defstobj st fld)
    (defun foo (st)
      (declare (xargs :stobjs st :guard (fld st)))
      st)
    (thm (equal (foo '(3))
                '(3)))
    

    The dmr feature for dynamic monitoring of rewrites had a bug, where the file used for communicating with emacs was the same for all users, based on who built the ACL2 executable image. This has been fixed. Thanks to Robert Krug for bringing this bug to our attention.

    Fixed a bug in some warnings, in particular the warning for including an uncertified book, that was giving an incorrect warning summary string.

    Inclusion of uncertified books erroneously re-used make-event expansions that were stored in stale certificates. This is no longer the case. Thanks to Jared Davis for bringing this bug to our attention.

    Fixed a bug that was disallowing calls of with-output in events that were executing before calling certify-book.

    Modified the functionality of binop-table so other than binary function symbols are properly supported (hence with no action based on right-associated arguments). See add-binop.

    Fixed small proof-checker issues related to packages. Emacs commands ctrl-t d and ctrl-t ctrl-d now work properly with colon (`:') and certain other punctuation characters. The p-top command now prints ``***'' regardless of the current package.

    Fixed a bug that allowed certify-book to succeed without specifying value t for keyword argument :skip-proofs-okp, even with include-book events in the certification world depending on events executed under skip-proofs.

    Improved show-accumulated-persistence in the following two ways. Thanks to Robert Krug and Bill Young for requesting these improvements and for providing useful feedback.

    o It can provide more complete information when aborting a proof.

    o The :frames reported for a rule are categorized as ``useful'' and ``useless'' according to whether they support ``useful'' or ``useless'' :tries of that rule, respectively. See accumulated-persistence for further explanation.

    Modified make-event so that the reported time and warnings include those from the expansion phase. In analogy with encapsulate and progn, the rules reported still do not include those from subsidiary events (including the expansion phase). A related change to ld avoids resetting summary information (time, warnings) with each top-level form evaluation; events already handle this information themselves.

    Fixed set-inhibit-output-lst so that all warnings are inhibited when warning! but not warning is included in the list. Formerly, only soundness-related warnings were inhibited in this case. Thanks to Eric Smith for bringing this bug to our attention.

    Distributed directory doc/HTML/ now again includes installation instructions (which was missing in Version_3.2.1), in doc/HTML/installation/installation.html.

    Some fixes have been made for proof-tree support.

    o Proof-tree output is no longer inhibited automatically during certify-book, though it continues to be inhibited by default (i.e., ACL2 continues to start up as though set-inhibit-output-lst has been called with argument '(proof-tree)).

    o Fixed a bug in Xemacs support for proof-tree help keys C-z h and C-z ?.

    o Fixed a bug in proof-trees that was failing to deal with the case that a goal pushed for proof by induction is subsumed by such a goal to be proved later. Now, the proof-tree display regards such subsumed goals as proved, as is reported in the theorem prover's output.

    Fixed a bug that was disallowing value-triple forms inside encapsulate forms in a certification world (see portcullis).

    If the :load-compiled-file argument of a call of include-book is :comp, then an existing compiled file will be loaded, provided it is more recent than the corresponding book (i.e., .lisp file). A bug was causing the compiled file to be deleted and then reconstructed in the case of :comp, where this behavior was intended only for :comp!.

    Fixed a bug that was avoiding compilation of some executable counterparts (sometimes called ``*1* functions'') during certify-book, and also during include-book with :load-compiled-file value of :comp or :comp!). Thanks to Eric Smith for sending a small example to bring this bug to our attention.

    Incorporated a fix from Eric Smith for a typo (source function ancestors-check1) that could cause hard Lisp errors. Thanks, Eric!

    Fixed the following issues with packages and book certificates. See hidden-death-package if you are generally interested in such issues, and for associated examples, see comments on ``Fixed the following issues with packages'' in note-3-3 in the ACL2 source code.

    o Reduced the size of .cert files by eliminating some unnecessary defpkg events generated for the portcullis.

    o Fixed a bug that has caused errors when reading symbols from a portcullis that are in undefined packages defined in locally included books.

    o Fixed a bug that could lead to failure of include-book caused by a subtle interaction between set-ignore-ok and defpkg events generated for the portcullis of a certificate.

    PROVER ALGORITHM ENHANCEMENTS

    Non-linear arithmetic (see non-linear-arithmetic) has been improved to be more efficient or more powerful in some cases. Thanks to Robert Krug for contributing these improvements.

    Improved certain (so-called ``type-set'') reasoning about whether or not expressions denote integers. Thanks to Robert Krug for contributing code to implement this change, along with examples illustrating its power that are now distributed in the book books/misc/integer-type-set-test.lisp.

    Improved ACL2's heuristics for relieving hypotheses, primarily to use linear reasoning on conjuncts and disjuncts of the test of an if expression. For example, given a hypothesis of the form (if (or term1 term2) ...), ACL2 will now use linear reasoning to attempt to prove both term1 and term2, not merely for term2. Thanks to Robert Krug for supplying examples illustrating the desirability of such an improvement and for useful discussions about the fix.

    Made a slight heuristic change, so that when a hypothesis with let or mv-let subterms (i.e. lambda subterms) rewrites to t, then that hypothesis is necessarily eliminated. Thanks to Jared Davis for sending an example that led us to develop this change, and thanks to Robert Krug for a helpful discussion.

    MISCELLANEOUS

    Added documentation on how to use make-event to avoid duplicating expensive computations, thanks to Jared Davis. See using-tables-efficiently.

    Modified the error message for calls of undefined functions to show the arguments. Thanks to Bob Boyer for requesting this enhancement.

    Modified utilies :pr, :pr!, :pl, and :show-bodies to incorporate code contributed by Jared Davis. That code defines low-level source functions info-for-xxx that collect information about rules, which is thus available to advanced users.

    Dynamic monitoring of rewrites (see dmr) has been improved in the following ways, as suggested by Robert Krug.

    o Some stale entries from the rewrite stack are no longer printed, in particular above ADD-POLYNOMIAL-INEQUALITIES.

    o An additional rewrite stack entry is made when entering non-linear arithmetic (see non-linear-arithmetic).

    o An ADD-POLYNOMIAL-INEQUALITIES entry is printed with a counter, to show how often this process is called.

    Modified save-exec so that the newly-saved image will have the same raw Lisp package as the existing saved image. This is a very technical change that will likely not impact most users; for example, the package in the ACL2 read-eval-print loop (see lp) had already persisted from the original to newly-saved image. Thanks to Jared Davis for suggesting this change.

    Changed make-event expansion so that changes to set-saved-output, set-print-clause-ids, set-fmt-soft-right-margin, and set-fmt-hard-right-margin will persist after being evaluated during make-event expansion. (Specifically, *protected-system-state-globals* has been modified; see make-event-details.) Thanks to Jared Davis for bringing this issue to our attention.

    Output from the proof-checker is now always enabled when invoking verify, even if it is globally inhibited (see set-inhibit-output-lst).

    Improved the message printed when an :induct hint fails, to give more information in some cases. Thanks to Rex Page for suggesting where an improvement could be made and providing useful feedback on an initial improvement.

    Added a warning for congruence rules (see defcong) that specify iff as the second equivalence relation when equal can be used instead. Those who heed these warnings can eliminate certain subsequent double-rewrite warnings for rewrite rules with conclusions of the form (iff term1 term2), and hence implicitly for Boolean conclusions term1 that are interpreted as (iff term1 t). Thanks to Sarah Weissman for sending us an example that highlighted the need for such a warning.

    Modified macro :redef! (which is for system implementors) so that it eliminates untouchables.

    Several improvements have been made to the experimental hons/memoization version of ACL2. See hons-and-memoization.

    The distributed books directory, (@ distributed-books-dir), is now printed in the start-up message.




    acl2-sources/doc/HTML/NOTE-3-3_lparen_R_rparen_.html0000664002132200015000000000101512222333527021345 0ustar kaufmannacl2 NOTE-3-3_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-3(R)

    ACL2 Version 3.3(r) (November, 2007) Notes
    Major Section:  RELEASE-NOTES
    

    Please also see note-3-3 for changes to Version 3.3 of ACL2.




    acl2-sources/doc/HTML/NOTE-3-4.html0000664002132200015000000010426412222333527016030 0ustar kaufmannacl2 NOTE-3-4.html -- ACL2 Version 6.3

    NOTE-3-4

    ACL2 Version 3.4 (August, 2008) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 3.3 into changes to existing features, new features, bug fixes, new and updated books, and Emacs support. Each change is described just once, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    Fixed a long-standing potential infinite loop in the rewriter. Thanks to Sol Swords for sending a concise example illustrating the looping behavior. (Those interested in details are welcome to look at the comment about loop behavior in source function rewrite-equal.)

    Incorporated a slight strengthening of non-linear arithmetic contributed by Robert Krug (thanks, Robert). With non-linear arithmetic enabled, the problem was essentially that ACL2 made the following ``optimization'': given inequalities (< a u) and (< b v), for positive rational constants a and b terms u and v of which at least one is known to be rational, infer (< (* a b) (* u v)). Without this optimization, however, ACL2 now infers the stronger inequality obtained by direct multiplication of the two given inequalities. To see the effect of this change, submit the event (set-non-linearp t) followed by:

    (thm (implies (and (rationalp x) (< 3 x)
                       (rationalp y) (< 4 y))
                  (< 0 (+ 12 (* -4 x) (* -3 y) (* x y)))))
    

    The utility set-checkpoint-summary-limit has been modified in several ways: it now takes a single argument (no longer takes state as an argument); a natural number n abbreviates the pair (n . n); the argument is no longer evaluated, but it still optionally may be quoted; and a new value, t, suppresses all printing of the checkpoint summary. Thanks to Jared Davis for suggesting most of these improvements.

    There was formerly a restriction on mbe that the :exec argument may not contain a call of mbe. This restriction has been removed, thanks to a request from Jared Davis and Sol Swords. Thanks also to Sandip Ray, who pointed out that this restriction may have been in place in order that defexec can guarantee termination using the :exec code; its documentation has therefore been updated to clarify this situation.

    Rules of class :rewrite are now stored by performing certain logical simplifications on the left side of the conclusion: (prog2$ X Y) is replaced by Y, (mbe :logic X :exec Y) is replaced by X (more precisely, the analogous change is made to the generated call of must-be-equal); and (the TYPE X) is replaced by X (again, the change is actually made on the macroexpanded form). Thanks to Jared Davis and Sol Swords for requesting this change. An analogous change has also been made for rules of class :forward-chaining.

    The trace$ utility has been reimplemented to work independently of the underlying Lisp trace. It thus works the same for every host Lisp, except as provided by an interface to the underlying host Lisp trace (the :native option). Note that the host Lisp trace continues to be modified for GCL, Allegro CL, and CCL (OpenMCL); see trace. See trace$ for updated detailed documentation on tracing options, many of which are new, for example an :evisc-tuple option that can be set to :no-print if you want the function traced without the usual entry and exit printing. The previous trace$ had some issues, including the following, which have all been fixed. Thanks to Peter Dillinger for assistance in determining desired functionality of the new trace$ and for helping to test it.

    Recursive calls were not always shown in the trace for two reasons. (1) Compiler inlining could prevent recursive calls from being shown in the trace, in particular in CCL (OpenMCL). Thanks to Jared Davis and Warren Hunt for pointing out this issue and requesting a fix, and to Bob Boyer and Gary Byers for relevant helpful discussions. (2) ACL2's algorithm for producing executable counterparts prevented tracing of recursive calls even after (set-guard-checking :none). Thanks to Peter Dillinger for requesting a fix.

    It was possible to exploit a bug in the interaction of multiple values and trace to prove a contradiction. An example is in a comment in (deflabel note-3-4 ...) in the ACL2 source code.

    Certain large structures could cause expensive computations for printing even when a :cond condition was specified and evaluated to nil.

    Trace! now suppresses printing of the event summary, and returns the value that would be returned (if there is an active trust tag) by the corresponding call of trace$.

    Some bugs have been fixed in the underlying native trace installed by ACL2 for GCL, Allegro CL, and CCL (OpenMCL), including the following. In GCL it had been impossible to use the variable ARGLIST in a :cond expression. In Allegro CL and CCL, a trace$ bug mishandled tracing non-ACL2 functions when directives such as :entry and :exit were supplied. GCL trace now hides the world even when tracing non-ACL2 functions. Tracing in CCL no longer causes a Lisp error when untracing a traced function defined outside the ACL2 loop; for example (trace$ len1) followed by (untrace$ len1) no longer causes an error.

    The macro wet has been changed, for the better we think. see wet.

    The generation of goals for forcing-rounds has been changed to avoid dropping assumptions formerly deemed ``irrelevant''. (A simple example may be found in a comment in source function unencumber-assumption, source file prove.lisp.) Thanks to Jared Davis for sending us an example that led us to make this change.

    Modified the implementation of make-event so that in the certificate of a book, local events arising from make-event forms are elided. For example, if (make-event <form>) expands to (local <expanded-form>), then where the latter had been stored in the certificate, now instead (local (value-triple :ELIDED)) will be stored. Thanks to Eric Smith for requesting this improvement. He has reported that a preliminary version of this improvement shrunk a couple of his .cert files from perhaps 40MB each to about 140K each.

    Now, a table event that sets the entire table, (table tbl nil alist :clear), is redundant (see redundant-events) when the supplied alist is equal to the current value of the table. Thanks to Peter Dillinger for requesting this change.

    The event constructor progn! now returns the value that is returned by evaluation of its final form if no error occurs, except that it still returns nil if the that final evaluation leaves ACL2 in raw-mode.

    :Pso and :psog have been improved so that they show the key checkpoint summary at the end of a failed proof. (For a discussion of key checkpoints, see set-gag-mode.) As a result, a call of set-checkpoint-summary-limit now affects subsequent evaluation of :pso and :psog. In particular, you no longer need to reconstruct a proof (by calling thm or defthm) in order to see key checkpoints that were omitted due to the limit; just call set-checkpoint-summary-limit and then run :pso or :psog.

    The proof-checker behaves a little differently under gag-mode. Now, proof-checker commands that call the theorem prover to create new proof-checker goals, such as bash and induct (see proof-checker-commands), will show key checkpoints when in gag-mode. As before, proof-checker commands pso and pso! (and now, also psog) -- see pso, see psog, and see pso! -- can then show the unedited proof log. However, unlike proof attempts done in the ACL2 loop, such proof attempts will not show a summary of key checkpoints at the end, because from a prover perspective, all such goals were considered to be temporarily ``proved'' by giving them ``byes'', to be dispatched by later proof-checker commands.

    A little-known feature had been that a measure of 0 was treated as though no measure was given. This has been changed so that now, a measure of nil is treated as though no measure was given.

    Expanded *acl2-exports* to include every documented symbol whose name starts with "SET-". Thanks to Jared Davis for remarking that set-debugger-enable was omitted from *acl2-exports*, which led to this change.

    The trace mechanism has been improved so that the :native and :multiplicity options can be used together for Lisps that support the trace :exit keyword. These Lisps include GCL and Allegro CL, whose native trace utilities have been modified for ACL2. For SBCL and CCL (OpenMCL), which use the built-in Lisp mechanism for returning multiple values in ACL2 (see mv), the use of :multiplicity with :native remains unnecessary and will be ignored. In support of this change, the modification of native Allegro CL tracing for ACL2 was fixed to handle :exit forms correctly that involve mv.

    NEW FEATURES

    The command :redef! is just like :redef, but prints a warning rather than doing a query. The old version of :redef! was for system hackers and has been renamed to :redef+.

    Introduced a new utility for evaluating a function call using the so-called executable counterpart -- that is, executing the call in the logic rather than in raw Lisp. See ec-call. Thanks to Sol Swords for requesting this utility and participating in its high-level design.

    See print-gv for a new utility that assists with debugging guard violations. Thanks to Jared Davis for requesting more tool assistance for debugging guard violations.

    Improved the guard violation error message to show the positions of the formals, following to a suggestion of Peter Dillinger.

    Added new guard-debug capability to assist in debugging failed attempts at guard verification. See guard-debug. Thanks to Jared Davis for requesting a tool to assist in such debugging and to him, Robert Krug, and Sandip Ray for useful discussions.

    New utilities provide the formula to be proved by verify-guards. See verify-guards-formula and see guard-obligation, Thanks to Mark Reitblatt for making a request leading to these utilities. These utilities can be applied to a term, not just an event name; thanks to Peter Dillinger for correspondence that led to this extension.

    A new utility causes runes to be printed as lists in proof output from simplification, as is done already in proof summaries. See set-raw-proof-format. Thanks to Jared Davis for requesting this utility.

    An experimental capability allows for parallel evaluation. See parallelism. Thanks to David Rager for providing an initial implementation of this capability.

    Defined xor in analogy to iff. Thanks to Bob Boyer, Warren Hunt, and Sol Swords for providing this definition.

    Improved distributed file doc/write-acl2-html.lisp so that it can now be used to build HTML documentation files for documentation strings in user books. See the comment in the definition of macro acl2::write-html-file at the end of that file. Thanks to Dave Greve and John Powell for requesting this improvement.

    It is now possible to specify :hints for non-recursive function definitions (which can be useful when definitions are automatically generated). See set-bogus-defun-hints-ok. Thanks to Sol Swords for requesting such a capability.

    Keyword argument :dir is now supported for rebuild just as it has been for ld.

    We relaxed the criteria for functional substitutions, so that a function symbol can be bound to a macro symbol that corresponds to a function symbol in the sense of macro-aliases-table. So for example, a functional substitution can now contain the doublet (f +), where previously it would have been required instead to contain (f binary-+).

    We now allow arbitrary packages in raw mode (see set-raw-mode) -- thanks to Jared Davis for requesting this enhancement -- and more than that, we allow arbitrary Common Lisp in raw mode. Note however that for arbitrary packages, you need to be in raw mode when the input is read, not just when the input form is evaluated.

    Two new keywords are supported by the with-output macro. A :gag-mode keyword argument suppresses some prover output as is done by set-gag-mode. Thanks to Jared Davis for asking for a convenient way to set gag-mode inside a book, in particular perhaps for a single theorem; this keyword provides that capability. A :stack keyword allows sub-events of progn or encapsulate to ``pop'' the effect of a superior with-output call. Thanks to Peter Dillinger for requesting such a feature. See with-output.

    The command good-bye and its aliases exit and quit now all take an optional status argument, which provides the Unix exit status for the underlying process. Thanks to Florian Haftmann for sending a query to the ACL2 email list that led to this enhancement.

    Keyword commands now work for macros whose argument lists have lambda list keywords. For a macro with a lambda list keyword in its argument list, the corresponding keyword command reads only the minimum number of required arguments. See keyword-commands.

    It is now legal to declare variables ignorable in let* forms, as in (let* ((x (+ a b)) ...) (declare (ignorable x)) ...). Thanks to Jared Davis for requesting this enhancement.

    Added a warning when more than one hint is supplied explicitly for the same goal. It continues to be the case that only the first hint applicable to a given goal will be applied, as specified in the user-supplied list of :hints followed by the default-hints-table. Thanks to Mark Reitblatt for sending a question that led both to adding this clarification to the documentation and to adding this warning.

    You may now use set-non-linear, set-let*-abstraction, set-tainted-ok, and set-ld-skip-proofs in place of their versions ending in ``p''. Thanks to Jared Davis for suggesting consideration of such a change. All ``set-'' utilites now have a version without the final ``p'' (and most do not have a version with the final ``p'').

    Added a "Loop-Stopper" warning when a :rewrite rule is specified with a :loop-stopper field that contains illegal entries that will be ignored. Thanks to Jared Davis for recommending such a warning.

    Added a substantial documentation topic that provides a beginner's guide to the use of quantification with defun-sk in ACL2. Thanks to Sandip Ray for contributing this guide, to which we have made only very small modifications. See quantifier-tutorial.

    Defun-sk now allows the keyword option :strengthen t, which will generate the extra constraint that that is generated for the corresponding defchoose event; see defchoose. Thanks to Dave Greve for suggesting this feature.

    BUG FIXES

    Fixed a soundness bug related to the use of mbe inside encapsulate events. An example proof of nil (before the fix) is in a comment in (deflabel note-3-4 ...) in the ACL2 source code. We therefore no longer allow calls of mbe inside encapsulate events that have non-empty signatures.

    Fixed a bug related to the definition of a function supporting the macro value-triple. Although this bug was very unlikely to affect any user, it could be carefully exploited to make ACL2 unsound:

    (defthm fact
      (equal (caadr (caddr (value-triple-fn '(foo 3) nil nil)))
             'value) ; but it's state-global-let* in the logic
      :rule-classes nil)
    (defthm contradiction
      nil
      :hints (("Goal" :use fact :in-theory (disable (value-triple-fn))))
      :rule-classes nil)
    

    Non-LOCAL definitions of functions or macros are no longer considered redundant with built-ins when the built-ins have special raw Lisp code, because ACL2 was unsound without this restriction! A comment about redundant definitions in source function chk-acceptable-defuns shows how one could prove nil without this new restriction. Note that system utility :redef+ removes this restriction.

    Although ACL2 already prohibited the use of certain built-in :program mode functions for verify-termination and during macroexpansion, we have computed a much more complete list of functions that need such restrictions, the value of constant *primitive-program-fns-with-raw-code*.

    Modified what is printed when a proof fails, to indicate more clearly which event failed.

    Fixed a problem with dmr in CCL (OpenMCL) that was causing a raw Lisp break after an interrupt in some cases. Thanks to Gary Byers for a suggestion leading to this fix.

    Fixed bugs in proof-checker code for dealing with free variables in hypotheses.

    Upon an abort, the printing of pstack and gag-mode summary information for other than GCL was avoided when inside a call of ld. This has been fixed.

    (Windows only) Fixed bugs for ACL2 built on SBCL on Windows, including one that prevented include-book parameters :dir :system from working, and one that prevented certain compilation. Thanks to Peter Dillinger for bringing these to our attention and supplying a fix for the second. Thanks also to Andrew Gacek for bringing include-book issues to our attention. Also, fixed writing of file saved_acl2 at build time so that for Windows, Unix-style pathnames are used.

    Fixed a hard Lisp error that could occur with keywords as table names, e.g., (table :a :a nil :put). Thanks to Dave Greve for bringing this problem to our attention and providing this example.

    Fixed handling of :OR hints so that proof attempts under an :OR hint do not abort (reverting to induction on the original input conjecture) prematurely. Thanks to Robert Krug for pointing out this problem and pointing to a possible initial fix.

    (SBCL and CLISP only) It is now possible to read symbols in the "COMMON-LISP" package inside the ACL2 command loop (see lp). This could cause a raw Lisp error in previous versions of ACL2 whose host Common Lisp was SBCL or CLISP. Thanks to Peter Dillinger for bringing this issue to our attention.

    Fixed a bug that was preventing certain hints, such as :do-not hints, from being used after the application of an :or hint. Thanks to Robert Krug for bringing this bug to our attention.

    (Hons version only) Fixed a bug in the interaction of memoize (hons version only) with event processing, specifically in interaction with failures inside a call of progn or encapsulate. Thanks to Jared Davis for bringing this bug to our attention and sending an example. A simplified example may be found in a comment in source function table-cltl-cmd, source file history-management.lisp; search for ``Version_3.3'' there.

    Fixed cw-gstack so that its :evisc-tuple is applied to the top clause, instead of using (4 5 nil nil) in all cases. If no :evisc-tuple is supplied then (term-evisc-tuple t state) is used for the top clause, as it is already used for the rest of the stack.

    Fixed a bug in the interaction of proof-trees with :induct hint value :otf-flg-override. Thanks to Peter Dillinger for reporting this bug and sending an example that evokes it.

    Fixed bugs in :pr and find-rules-of-rune for the case of rule class :elim. Thanks to Robert Krug and Jared Davis for bringing these related bugs to our attention.

    Improved failure messages so that the key checkpoints are printed only once when there is a proof failure. Formerly, a proof failure would cause the key checkpoints to be printed for every encapsulate or certify-book superior to the proof attempt.

    Fixed a bug in generation of guards for calls of pkg-witness. Thanks to Mark Reitblatt for sending an example showing this bug. The bug can be in play when you see the message: ``HARD ACL2 ERROR in MAKE-LAMBDA-APPLICATION: Unexpected unbound vars ("")''. A distillation of Mark's example that causes this hard error is as follows.

    (defun foo (x)
      (declare (xargs :guard t))
      (let ((y x)) (pkg-witness y)))
    

    The cond macro now accepts test/value pairs of the form (T val) in other than the last position, such as the first such pair in (cond (t 1) (nil 2) (t 3)). Thanks to Jared Davis for sending this example and pointing out that ACL2 was sometimes printing goals that have such a form, and hence cannot be submitted back to ACL2. A few macros corresponding to cond in some books under books/rtl and books/bdd were similarly modified. (A second change will probably not be noticeable, because it doesn't affect the final result: singleton cond clauses now generate a call of or in a single step of macroexpansion, not of if. For example, (cond (a) (b x) (t y)) now expands to (OR A (IF B X Y)) instead of (IF A A (IF B X Y)). See the source code for cond-macro for a comment about this change.)

    Fixed a bug in the interaction of proof-checker command DV, including numeric (``diving'') commands, with the add-binop event. Specifically, if you executed (add-binop mac fn) with fn having arity other than 2, a proof-checker command such as 3 or (dv 3) at a call of mac could have the wrong effect. We also fixed a bug in diving with DV into certain AND and OR calls. Thanks for Mark Reitblatt for bringing these problems to our attention with helpful examples.

    Fixed a couple of bugs that were causing an error, ``HARD ACL2 ERROR in RENEW-NAME/OVERWRITE''. Thanks to Sol Swords for bringing the first of these bugs to our attention.

    Fixed a bug that could cause certify-book to fail in certain cases where there are local make-event forms.

    Fixed a bug in start-proof-tree that could cause Lisp to hang or produce an error. Thanks to Carl Eastlund for sending an example to bring this bug to our attention.

    Fixed a bug in the proof output, which was failing to report cases where the current goal simplifies to itself or to a set including itself (see specious-simplification).

    Fixed a bug in with-prover-time-limit that was causing a raw Lisp error for a bad first argument. Thanks to Peter Dillinger for pointing out this bug.

    The following was claimed in :doc note-3-3, but was not fixed until the present release:
    Distributed directory doc/HTML/ now again includes installation instructions, in doc/HTML/installation/installation.html.

    In certain Common Lisp implementations -- CCL (OpenMCL) and LispWorks, at least -- an interrupt could leave you in a break such that quitting the break would not show the usual summary of key checkpoints. This has been fixed.

    NEW AND UPDATED BOOKS

    Updated books/clause-processors/SULFA/ with a new version from Erik Reeber; thanks, Erik.

    Added new books directory tools/ from Sol Swords. See books/tools/Readme.lsp for a summary of what these books provide.

    The distributed book books/misc/file-io.lisp includes a new utility, write-list!, which is like write-list except that it calls open-output-channel! instead of open-output-channel. Thanks to Sandip Ray for requesting this utility and assisting with its implementation.

    Added record-update macro supplied by Sandip Ray to distributed book books/misc/records.lisp.

    Sandip Ray has contributed books that prove soundness and completeness of different proof strategies used in sequential program verification. Distributed directory books/proofstyles/ has three new directories comprising that contribution: soundness/, completeness/, and counterexamples/. The existing books/proofstyles/ directory has been moved to its subdirectory invclock/.

    Jared Davis has contributed a profiling utility for ACL2 built on CCL (OpenMCL). See books/misc/oprof.lisp. Thanks, Jared.

    ACL2 utilities getprop and putprop take advantage of under-the-hood Lisp (hashed) property lists. The new book books/misc/getprop.lisp contains an example showing how this works.

    Added the following new book directories: books/paco/, which includes a small ACL2-like prover; and books/models/jvm/m5, which contains the definition of one of the more elaborate JVM models, M5, along with other files including JVM program correctness proofs. See files Readme.lsp in these directories, and file README in the latter.

    Added books about sorting in books/sorting. See Readme.lsp in that directory for documentation.

    Added book books/misc/computed-hint-rewrite.lisp to provide an interface to the rewriter for use in computed hints. Thanks to Jared Davis for requesting this feature.

    Jared Davis has provided a pseudorandom number generator, in books/misc/random.lisp.

    Robert Krug has contributed a new library, books/arithmetic-4/, for reasoning about arithmetic. He characterizes it as being more powerful than its predecessor, books/arithmetic-3/, and without its predecessor's rewriting loops, but significantly slower than its predecessor on some theorems.

    Incorporated changes from Peter Dillinger to verify guards for functions in books/ordinals/lexicographic-ordering.lisp (and one in ordinal-definitions.lisp in that directory).

    A new directory, books/hacking/, contains a library for those who wish to use trust tags to modify or extend core ACL2 behavior. Thanks to Peter Dillinger for contributing this library. Obsolete version books/misc/hacker.lisp has been deleted. Workshop contribution books/workshops/2007/dillinger-et-al/code/ is still included with the workshops/ tar file, but should be considered deprecated.

    In books/make-event/assert.lisp, changed assert! and assert!-stobj to return (value-triple :success) upon success instead of (value-triple nil), following a suggestion from Jared Davis.

    EMACS SUPPORT

    Changed emacs/emacs-acl2.el so that the fill column default (for the right margin) is only set (still to 79) in lisp-mode.

    Modified Emacs support in file emacs/emacs-acl2.el so that names of events are highlighted just as defun has been highlighted when it is called. Search in the above file for font-lock-add-keywords for instructions on how to eliminate this change.

    The name of the temporary file used by some Emacs utilities defined in file emacs/emacs-acl2.el has been changed to have extension .lsp instead of .lisp; thus it is now temp-emacs-file.lsp. Also, `make' commands to `clean' books will delete such files (specifically, books/Makefile-generic has been changed to delete temp-emacs-file.lsp).




    acl2-sources/doc/HTML/NOTE-3-4_lparen_R_rparen_.html0000664002132200015000000000164512222333527021357 0ustar kaufmannacl2 NOTE-3-4_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-4(R)

    ACL2 Version 3.4(r) (August, 2008) Notes
    Major Section:  RELEASE-NOTES
    

    Please also see note-3-4 for changes to Version 3.4 of ACL2.

    Fixed makefiles, books/nonstd/Makefile and GNUmakefile. The old set-up seemed to work fine as long as all books certified, but it was really broken, for example only certifying some of the books in books/nonstd/nsa/, and then only when required by books in other directories. Also fixed the ``clean'' target to clean links rather than to make links.




    acl2-sources/doc/HTML/NOTE-3-5.html0000664002132200015000000012142512222333527016027 0ustar kaufmannacl2 NOTE-3-5.html -- ACL2 Version 6.3

    NOTE-3-5

    ACL2 Version 3.5 (May, 2009) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 3.4 into the following categories: changes to existing features, new features, heuristic improvements, bug fixes, new and updated books, Emacs support, and experimental hons version. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    Many improvements have been made to ACL2's ``evisceration'' mechanism for hiding substructures of objects before they are printed, and to related documentation:

    o A new documentation topic explains evisc-tuples. See evisc-tuple.

    o A new interface, set-evisc-tuple, has been provided for setting the four global evisc-tuples. See set-evisc-tuple.

    o A new mode, ``iprinting'', allows eviscerated output to be read back in. See set-iprint.

    o Function default-evisc-tuple has been deprecated and will probably be eliminated in future releases; use abbrev-evisc-tuple instead. Also eliminated is the brr-term-evisc-tuple (also the user-brr-term-evisc-tuple). The term-evisc-tuple controls printing formerly controlled by the brr-term-evisc-tuple or user-brr-term-evisc-tuple.

    o ACL2 output is done in a more consistent manner, respecting the intention of those four global evisc-tuples. In particular, more proof output is sensitive to the term-evisc-tuple. Again, see set-evisc-tuple.

    o A special value, :DEFAULT, may be provided to set-evisc-tuple in order to restore these evisc-tuples to their original settings.

    o (Details for heavy users of the evisc-tuple mechanism) (1) There are no longer state globals named user-term-evisc-tuple or user-default-evisc-tuple. (2) Because of the above-mentioned :DEFAULT, if you have referenced state globals directly, you should use accessors instead, for example (abbrev-evisc-tuple state) instead of (@ abbrev-evisc-tuple). (3) For uniformity, set-trace-evisc-tuple now takes a second argument, state.

    Improved break-on-error in several ways. First, it breaks earlier in a more appropriate place. Thanks to Dave Greve for highlighting this problem with the existing implementation. Also, break-on-error now breaks on hard errors, not only soft errors (see er, options hard and hard?). Thanks to Warren Hunt and Anna Slobodova for sending an example that showed a flaw in an initial improvement. Finally, new options cause printing of the call stack for some host Common Lisps. See break-on-error. Thanks to Bob Boyer for requesting this feature.

    Trace! may now be used in raw Lisp (though please note that all soundness claims are off any time you evaluate forms in raw Lisp!). Thanks to Bob Boyer for feedback that led to this enhancement.

    ACL2 now searches for file acl2-customization.lsp in addition to (and just before) its existing search for acl2-customization.lisp; See acl2-customization. Thanks to Jared Davis for suggesting this change, which supports the methodology that files with a .lisp extension are certifiable books (thus avoiding the need to set the BOOKS variable in makefiles; see books-certification-classic).

    Improved the error message for illegal declare forms of the form (type (satisfies ...)). Thanks to Dave Greve for sending an example highlighting the issue.

    If trace output is going to a file (because open-trace-file has been executed), then a note will be printed to that effect at the time that a call of trace$ or trace! is applied to one or more trace specs.

    The notion of redundancy (see redundant-events) has been made more restrictive for mutual-recursion events. Now, if either the old or new event is a mutual-recursion event, then redundancy requires that both are mutual-recursion events that define the same set of function symbols. Although we are not aware of any soundness bugs fixed by this modification, nevertheless we believe that it reduces the risk of soundness bugs in the future.

    The definition of trace* has been moved to a book, misc/trace1.lisp. A new version, used in ACL2s, is in book misc/trace-star.lisp. Trace utilities trace$ and trace! are still built into ACL2. [Note: File misc/trace1.lisp was deleted after Version 4.2.]

    Certain certificate files will now be much smaller, by printing in a way that takes advantage of structure sharing. Certifying the following example produces a .cert file of over 3M before this change, but less than 1K after the change.

    (defun nest (i)
      ;; Makes an exponentially-sized nest of conses i deep.
      (if (zp i)
          nil
        (let ((next (nest (1- i))))
          (cons next next))))
    
    (make-event
     `(defconst *big* ',(nest 20)))
    
    Thanks to Sol Swords for providing the above example and to him as well as to Bob Boyer, Jared Davis, and Warren Hunt for encouraging development of this improvement. We have also applied this improvement to the printing of function definitions to files on behalf of certify-book and comp.

    Names of symbols are now printed with vertical bars according to the Common Lisp spec. Formerly, if the first character of a symbol name could be the first character of the print representation of a number, then the symbol was printed using vertical bars (|..|) around its name. Now, a much more restrictive test for ``potential numbers'' is used, which can result in fewer such vertical bars. Base 16 is now carefully considered as well; see set-print-base. Thanks to Bob Boyer for requesting this improvement. Note that macros set-acl2-print-base and set-acl2-print-case have been replaced by functions; see set-print-base and see set-print-case.

    The ACL2 reader now supports `#.' syntax in place of the `#, syntax formerly supported. Thanks to Bob Boyer for requesting this change. See sharp-dot-reader. NOTE that because of this change, `#.' no longer causes an abort; instead please use (a!) or optionally, if in the ACL2 loop, :a!; see a!.

    Some small changes have been made related to gag-mode:

    o Gag-mode now suppresses some messages that were being printed upon encountering disjunctive splits from :OR hints. Thanks to Sol Swords for suggesting this improvement.

    o ACL2 had printed ``Q.E.D.'' with all output suppressed and gag-mode enabled. Now, ``Q.E.D.'' will be suppressed when PROVE and SUMMARY output are suppressed, even if gag-mode is enabled.

    o The use of set-gag-mode had drastic effects on the inhibited output (see set-inhibit-output-lst), basically inhibiting nearly all output (even most warnings) when turning on gag-mode and enabling all output except proof-tree output when turning off gag-mode. Now, set-gag-mode only inhibits or enables proof (PROVE) output, according to whether gag-mode is being turned on or off (respectively). The related utility set-saved-output has also been modified, basically to eliminate :all as a first argument and to allow t and :all as second arguments, for inhibiting prover output or virtually all output, respectively (see set-saved-output).

    A defstub event signature specifying output of the form (mv ...) now introduces a :type-prescription rule asserting that the new function returns a true-listp result. Thanks to Bob Boyer for sending the following example, which motivated this change.

    (defstub census (*) => (mv * *))
    (defn foo (x)
      (mv-let (a1 a2)
              (census x)
              (list a1 a2)))
    

    Improved the efficiency of string-append so that in raw Lisp, it calls concatenate. Thanks to Jared Davis for suggesting this change, including the use of mbe. A minor change was made to the definition of concatenate to support this change, and the lemma append-to-nil was added (see below).

    The checksum algorithm used for certificate files of books has been changed. Thanks to Jared Davis for contributing the new code. This change will likely not be noticed unless one is using the experimental hons version of ACL2, where it can greatly speed up book certification and inclusion because of function memoization (of source function fchecksum-obj).

    Fewer calls are made to the checksum algorithm on behalf of certify-book and a few other operations. Thanks to Jared Davis for providing data that helped lead us to these changes.

    Formatted printing directives ~p, ~q, ~P, and ~Q are deprecated, though still supported. See fmt. Instead, please use ~x, ~y, ~X, and ~Y (respectively). As a by-product, rule names in proof output are no longer hyphenated.

    A new keyword, :multiplicity, is available for tracing raw Lisp functions using the ACL2 trace utility. See trace$.

    Users may now control whether or not a slow array access results in a warning printed to the screen (which is the default, as before), and if so, whether or not the warning is followed by a break. See slow-array-warning.

    On linux-like systems (including Mac OS X and SunOS), :comp will now write its temporary files into the "/tmp" directory, which is the value of state global 'tmp-dir. You can change that directory with (assign tmp-dir "<your_temp_directory_path>").

    The messages printed for uncertified books have been enhanced. Thanks to Jared Davis for requesting such an improvement.

    A function definition that would be redundant if in :logic mode is now considered redundant even if it (the new definition) is in :program mode. That is, if a definition is ``downgraded'' from :logic to :program mode, the latter (:program mode) definition is considered redundant. Previously, such redundancy was disallowed, but we have relaxed that restriction because of a scenario brought to our attention by Jared Davis: include a book with the :logic mode definition, and then include a book with the :program mode definition followed by verify-termination. Thanks, Jared.

    The ACL2 reader no longer accepts characters other than those recognized by standard-char-p except for #\Tab, #\Page, and #\Rubout (though it still accepts strings containing such characters). As a result, no make-event expansion is allowed to contain any such unacceptable character or string. Thanks to Sol Swords for sending an example that led us to make this restriction. A simple example is the following book:

    (in-package "ACL2")
    (defconst *my-null* (code-char 0))
    (make-event `(defconst *new-null* ,*my-null*))
    
    For this book, a call of certify-book formerly broke during the compilation phase, but if there was no compilation, then a call of include-book broke. Now, the error occurs upon evaluation of the make-event form.

    ACL2 now collects up guards from declare forms more as a user might expect, without introducing an unexpected ordering of conjuncts. We thank Jared Davis for sending us the following illustrative example, explained below.

    (defun f (x n)
      (declare (xargs :guard (and (stringp x)
                                  (natp n)
                                  (= (length x) n)))
               (type string x)
               (ignore x n))
      t)
    
    Formerly, a guard was generated for this example by unioning the conjuncts from the :guard onto a list containing the term (string x) generated from the type declaration, resulting in an effective guard of:
    (and (natp n)
         (= (length x) n)
         (stringp x))
    
    The guard of this guard failed to be verified because (stringp x)) now comes after the call (length x). With the fix, contributions to the guards are collected up in the order in which they appear. So in the above example, the effective guard is the specified :guard; the contribution (stringp x) comes later, and is thus dropped since it is redundant. NOTE by the way that if :guard and :stobjs are specified in the same xargs form, then for purposes of collecting up the effective guard as described above, :stobjs will be treated as through it comes before the :guard.

    Modified close-output-channel to try to do a better job flushing buffers. Thanks to Bob Boyer for helpful correspondence.

    The notion of ``subversive recursion'' has been modified so that some functions are no longer marked as subversive. See subversive-recursions, in particular the discussion elaborating on the notion of ``involved in the termination argument'' at the end of that documentation topic.

    Formerly, :type-prescription rules for new definitions inside encapsulate forms were sometimes added as constraints. This is no longer the case. See also discussion of the ``soundness bug in the forming of constraints'', which is related.

    NEW FEATURES

    It is now possible to affect ACL2's termination analysis (and resulting induction analysis). Thanks to Peter Dillinger for requesting this feature. The default behavior is essentially unchanged. But for example, the following definition is accepted by ACL2 because of the use of the new :ruler-extenders features; See ruler-extenders.

      (defun f (x)
        (declare (xargs :ruler-extenders :all))
        (cons 3
              (if (consp x)
                  (f (cdr x))
                nil)))
    

    The following lemma was added in support of the improvement to string-append described above:

    (defthm append-to-nil
      (implies (true-listp x)
               (equal (append x nil)
                      x)))
    

    A mechanism has been provided for users to contribute documentation. See managing-acl2-packages for an example, which contains a link to an external web page on effective use of ACL2 packages, kindly provided by Jared Davis. ACL2 documentation strings may now link to external web pages using the new symbol, ~url; see markup. Of course, those links appear in the web version of the documentation, but you made need to take a bit of action in order for these to appear as links in the Emacs Info version; see documentation.

    Added intersectp (similar to intersectp-eq and intersectp-equal).

    The user now has more control over how ACL2 prints forms; See print-control. Thanks to Bob Boyer for useful discussions leading to this enhancement.

    Some Common Lisp implementations only allow the syntax pkg-name::expression when expression is a symbol. The ACL2 reader has been modified to support a package prefix for arbitrary expressions; see sharp-bang-reader. Thanks to Hanbing Liu for a query that led to this feature and to Pascal J. Bourguignon for suggesting an implmentation.

    Ill-formed documentation strings need not cause an error. See set-ignore-doc-string-error. Thanks to Bob Boyer for requesting this feature.

    Type declarations are now permitted in let* forms; see let*, see declare, and see type-spec.

    (For Lisp programmers) Macro with-live-state has been provided for programmers who refer to free variable STATE, for example with macros that generate uses of STATE, and want to avoid compiler warnings when evaluating in raw Lisp. For example, the following form can be submitted either inside or outside the ACL2 loop to get the desired effect (see doc-string): (with-live-state (f-put-global 'doc-prefix " " state)). For another example use of this macro, see the definition of trace$ (ACL2 source file other-events.lisp).

    (System hackers only) Added :redef- to undo the effect of :redef+. See redef-.

    Function random$ is a built-in random number generator. See random$. Thanks to Sol Swords for requesting this feature and providing an initial implementation.

    HEURISTIC IMPROVEMENTS

    Sped up guard generation for some functions with large if-then-else structures in their bodies. Thanks to Sol Swords for sending an illustrative example.

    Sped up guard generation in some cases by evaluating ground (variable-free) subexpressions. Thanks to Bob Boyer for sending a motivating example: (defn foo (x) (case x ((1 2) 1) ((3 4) 3) ... ((999 1000) 999))).

    Modified slightly a heuristic association of ``size'' with constants, which can result in significant speed-ups in proofs involving constants that are very large cons trees.

    Added a restriction in the linear arithmetic procedure for deleting polynomial inequalities from the linear database. Formerly, an inequality could be deleted if it was implied by another inequality. Now, such deletion requires that certain heuristic ``parent tree'' information is at least as restrictive for the weaker inequality as for the stronger. Thanks to Dave Greve for bringing a relevant example to our attention and working with us to figure out some surprising behavior, and to Robert Krug for making a key observation leading to the fix.

    (GCL especially) Improved compiled code slightly by communicating to raw Lisp the output type when a function body is of the form (the character ...). This tiny improvement will probably only be observed in GCL, if at all.

    Applied a correction suggested by Robert Krug to the variant of term-order used in parts of ACL2's arithmetic reasoning.

    BUG FIXES

    Fixed bugs in the handling of flet expressions, one of which had the capability of rendering ACL2 unsound. Thanks to Sol Swords for pointing out two issues and sending examples. One example illustrated how ACL2 was in essence throwing away outer flet bindings when processing an inner flet. We have exploited that example to prove a contradiction, as follows: this book was certifiable before this fix.

    (in-package "ACL2")
    
    (defun a (x)
      (list 'c x))
    
    ; Example from Sol Swords, which failed to be admitted (claiming that
    ; function A is undefined) without the above definition of A.
    (defun foo1 (x y)
       (flet ((a (x) (list 'a x)))
         (flet ((b (y) (list 'b y)))
           (b (a (list x y))))))
    
    (defthm not-true
      (equal (foo1 3 4)
             '(b (c (3 4))))
      :hints (("Goal"
               :in-theory
               (disable (:executable-counterpart foo1))))
      :rule-classes nil)
    
    (defthm contradiction
      nil
      :hints (("Goal" :use not-true))
      :rule-classes nil)
    
    Sol's second example, below, pointed to a second bug related to computing output signatures in the presence of nested flet expressions, which we have also fixed: this form failed before the fix.
    :trans (flet ((foo (a) (list (flet ((bar (b) b)) a)))) x)
    

    Fixed a subtle soundness bug in the forming of constraints from deduced type prescriptions. As a result, when ACL2 prints a warning message labeling encapsulated functions as ``subversive'', ACL2 will no longer deduce :type-prescription rules for those functions. Examples that exploit the bug in ACL2 Version_3.4 may be found in comments in ACL2 source function convert-type-set-to-term (file other-processes.lisp) and especially in function putprop-type-prescription-lst (file defuns.lisp). For more on the general issue of ``subversive recursions,'' see subversive-recursions.)

    Fixed a soundness bug in the handling of inequalities by the type-set mechanism, which was using the inequality database inside the body of a lambda.

    Fixed a long-standing soundness bug in compress1 and compress2, whose raw Lisp code gave the logically incorrect result in the case of a single entry other than the header, where that entry mapped an index to the default value. Also fixed soundness bugs in compress1, in the case of :order >, where the raw Lisp code could drop the header from the result or, when the input alist had entries in ascending order, fail to return an alist in descending order. For example, the following book certified successfully.

    (in-package "ACL2")
    (defthm true-formula-1
      (equal (compress1 'a '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5
                                      :DEFAULT 1 :NAME A :ORDER <)
                             (1 . 1)))
             '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5
                        :DEFAULT 1 :NAME A :ORDER <)))
      :hints (("Goal" :in-theory (disable (compress1))))
      :rule-classes nil)
    (defthm ouch-1
      nil
      :hints (("Goal" :use true-formula-1))
      :rule-classes nil)
    (defthm true-formula-2
      (equal (compress1 'a '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5
                                      :DEFAULT NIL :NAME A :ORDER >)
                             (1 . 1)
                             (2 . 2)))
             '((:HEADER :DIMENSIONS (4) :MAXIMUM-LENGTH 5
                        :DEFAULT NIL :NAME A :ORDER >)
               (2 . 2)
               (1 . 1)))
      :hints (("Goal" :in-theory (disable (compress1))))
      :rule-classes nil)
    (defthm ouch-2
      nil
      :hints (("Goal" :use true-formula-2))
      :rule-classes nil)
    (defthm true-formula-3
      (equal (compress1 'a '((:HEADER :DIMENSIONS (3) :MAXIMUM-LENGTH 4
                                      :NAME A :ORDER >)
                             (1 . B)
                             (0 . A)))
             '((:HEADER :DIMENSIONS (3) :MAXIMUM-LENGTH 4
                        :NAME A :ORDER >)
               (1 . B)
               (0 . A)))
      :hints (("Goal" :in-theory (disable (compress1))))
      :rule-classes nil)
    (defthm ouch-3
      nil
      :hints (("Goal" :use true-formula-3))
      :rule-classes nil)
    

    Fixed a soundness bug involving measured subsets and verify-termination, by changing verify-termination so that it uses make-event. See verify-termination, in particular the discussion about make-event near the end of that documentation topic. Peter Dillinger first raised the idea to us of making such a change; when we found this soundness bug, we were certainly happy to do so!

    Fixed a bug that could cause a hard Lisp error but not, apparently, unsoundness. The bug was in the lack of attention to the order of guard and type declarations when checking for redundancy. In the following example, the second definition was redundant during the first pass of the encapsulate form. The second definition, however, was stored on the second pass with a guard of (and (consp (car x)) (consp x)), which caused a hard Lisp error when evaluating (foo 3) due to a misguided attempt to evaluate (car 3) in raw Lisp. The fix is to restrict redundancy of definitions so that the guard and type declarations must be in the same order for the two definitions.

    (encapsulate
     ()
     (local (defun foo (x)
              (declare (xargs :guard (consp x)))
              (declare (xargs :guard (consp (car x))))
              x))
     (defun foo (x)
       (declare (xargs :guard (consp (car x))))
       (declare (xargs :guard (consp x)))
       x))
    ; Now we get a hard Lisp error from evaluation of the guard of foo:
    (foo 3)
    

    Fixed a bug in the guard violation report for function intern-in-package-of-symbol. Thanks to Dave Greve for bringing this bug to our attention.

    Made a change to allow certain hints, in particular certain :clause-processor hints, that had previously caused errors during termination proofs by viewing the function being defined as not yet existing. Thanks to Sol Swords for bringing this issue to our attention with a nice example.

    ACL2 now properly handles interrupts (via control-c) issued during printing of the checkpoint summary. Previously it was possible on some platforms to make ACL2 hang when interrupting both during a proof and during the ensuing printing of the checkpoint summary. Thanks to Jared Davis and Sol Swords for bringing this problem to our attention.

    Fixed a bug that was preventing, inside some book "b", the use of a :dir argument to include-book that refers to a directory defined using add-include-book-dir earlier in the book "b". (We found this ourselves, but we thank John Cowles for observing it independently and sending us a nice example.)

    (GCL and CCL only) Fixed a bug in certain under-the-hood type inferencing. Thanks to Sol Swords for sending an example using stobjs defined with the :inline keyword, along with a helpful backtrace in CCL, and to Gary Byers for his debugging help.

    Fixed a bug in print-gv, which was mishandling calls of functions with more than one argument.

    Fixed a bug in the handling of AND and OR terms by the proof-checker command DV, including numeric (``diving'') commands. Thanks for Mark Reitblatt for bringing this problems to our attention with a helpful example.

    Fixed printing of goal names resulting from the application of :OR hints so that they aren't ugly when working in other than the "ACL2" package. Thanks to Sol Swords for bringing this issue to our attention.

    Fixed proof-tree printing so that interrupts will not cause problems with hiding ordinary output because of incomplete proof-tree output. Thanks to Peter Dillinger for pointing out this issue.

    Fixed a hard error that could be caused by mishandling a forced hypothesis during forward-chaining. Thanks to Peter Dillinger for bringing this bug to our attention by sending a useful example.

    Fixed a bug that could cause simplifications to fail because of alleged ``specious simplification.'' This bug could appear when deriving an equality from the linear arithmetic database, and then attempting to add this equality to the current goal's hypotheses when it was already present. Thanks to Eric Smith for sending a helpful example (in July 2005!) that helped us debug this issue.

    Fixed a bug in processing of :type-set-inverter rules.

    Fixed a bug that was causing an error, at least for an underlying Lisp of CCL (OpenMCL), when ec-call was applied to a term returning multiple values. Thanks to Sol Swords for sending an example that brought this bug to our attention.

    Fixed handling of array orders to treat keyword value :order :none correctly from an array's header. Previously, there were two problems. One problem was that :order :none was treated like the default for :order, <, while :order nil was treated in the manner specified by :order :none (see arrays). Now, both :order :none and :order nil are treated as :order nil had been treated, i.e., so that there is no reordering of the alist by compress1. The other problem with this case of :order was that the :maximum-length field of the header was not being respected: the length could grow without bound. Now, as previously explained (but not previously implemented) -- see arrays -- a compress1 call made on behalf of aset1 causes a hard error if the header of the supplied array specifies an :order of :none or nil.

    An ignorable declare form had caused an error in some contexts when it should have been allowed. In particular, this problem could arise when using an ignorable declaration at the top level in a defabbrev form. It could also arise upon calling verify-termination when the corresponding defun form contained an ignorable declaration at the top level. These bugs have been fixed.

    Contrary to existing documentation (see make-event-details), the value of ``ld special variable'' ld-skip-proofsp was always set to nil during make-event expansion, not merely when the make-event form has a non-nil value for keyword parameter :check-expansion. This has been fixed. Thanks to Sol Swords for bringing this issue to our attention.

    We have disallowed the certification of a book when not at the top-level, either directly in the top-level loop or at the top level of ld. Before this restriction, the following would certify a book with a definition such as (defun foo (x) (h x)) that calls function h before defining it, if the certification was by way of the form such as:

    (er-progn (defun h (x) x) (certify-book "my-book"))
    
    But a subsequent include-book of "my-book" would then fail, because h is not defined at the top level.

    Printing with fmt directive ~c now works properly even when the print-base is other than 10. Thanks to Sol Swords for reporting this bug and providing a fix for it.

    (SBCL, CMUCL, and CCL only) Fixed a bug in sys-call-status in the case that the underlying Common Lisp is SBCL, CMUCL, or CCL (OpenMCL). Thanks to Jun Sawada for bringing this bug to our attention and providing a fix.

    Fixed a bug that was preventing local defstobj events in encapsulate events. Thanks to Jared Davis for bringing this bug to our attention.

    Fixed a bug evidenced by error message ``Unexpected form in certification world'', which could result from attempting to certify a book after evaluating an encapsulate form with a local defmacro. Thanks to Jared Davis for pointing out this bug and sending the example:

    (encapsulate
     ()
     (local (defmacro foo (x) `(table foo 'bar ,x)))
     (local (foo 3)))
    

    Formerly, evaluating a trace$ form inside a wormhole such as the break-rewrite loop could leave the user in a bad state after returning to the top level, in which that function could not be untraced. This has been fixed. Note however that when you proceed from a break in the break-rewrite loop, the tracing state will be the same as it was when you entered that break: all effects of calling trace$ and untrace$ are erased when you proceed from the break.

    A :guard of (and) is no longer ignored. Thanks to Sol Swords for bringing this bug to our attention.

    A bug has been fixed that could result in needlessly weak induction schemes in the case that a recursive call is made in the first argument of prog2$. This has been fixed by including prog2$ as a default ruler-extender in the new ruler-extenders feature (see above, and see ruler-extenders). For details on this bug see Example 11 in distributed book books/misc/misc2/ruler-extenders-tests.lisp.

    (For CCL/OpenMCL on Windows) ACL2 should now build on CCL (OpenMCL) on Windows. Thanks to David Rager for bringing this issue to our attention and helping with a fix that worked for CCL 1.2, and to the CCL team for improving handling of Windows-style filenames in CCL 1.3.

    NEW AND UPDATED BOOKS

    See http://code.google.com/p/acl2-books/wiki/BooksSince34 for a list of books in Version 3.5 of ACL2 but not Version 3.4.

    Run the shell command

    svn log -r 94:HEAD
    
    to see all changes to books/ since the release of Version 3.4.

    Here are just a few highlights.

    Thanks largely to Jared Davis, many Makefiles have been improved to do automatic dependency analysis. See books-certification-classic for how to get your own Makefiles to do this by adding a line: -include Makefile-deps.

    Libraries books/arithmetic-4/ and books/rtl/rel7/ have been eliminated from the default book certification (make regression), in favor of new libraries books/arithmetic-5/ and books/rtl/rel8/ contributed by Robert Krug and Hanbing Liu, respectively. They and Jun Sawada have arranged the compatibility of these libraries; i.e., it is possible to evaluate both of the following in the same session:

    (include-book "arithmetic-5/top" :dir :system)
    (include-book "rtl/rel8/lib/top" :dir :system)
    

    Library books/rtl/rel1/ is no longer certified by default (though it is still distributed in support of ACL2(r); see real).

    EMACS SUPPORT

    Slightly modified Control-t e (defined in emacs/emacs-acl2.el) to comprehend the notion of an ``ACL2 scope'', and added Control-t o to insert a superior encapsulate defining such a scope. See the Emacs documentation for Control-t e (generally obtained after typing Control-h k).

    Modified distributed file emacs/emacs-acl2.el so that if you put the following two forms in your ~/.emacs file above the form that loads emacs/emacs-acl2.el, then Emacs will not start up a shell. Thanks to Terry Parks for leading us to this modification.

    (defvar acl2-skip-shell nil)
    (setq acl2-skip-shell t)
    

    EXPERIMENTAL HONS VERSION

    Bob Boyer and others have contributed numerous changes for the experimental ``hons'' version of ACL2 (see hons-and-memoization).

    The ACL2 state can now be queried with (@ hons-enabled) so that a result of t says that one is in the experimental hons version, while nil says the opposite.




    acl2-sources/doc/HTML/NOTE-3-5_lparen_R_rparen_.html0000664002132200015000000000660612222333527021362 0ustar kaufmannacl2 NOTE-3-5_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-5(R)

    ACL2 Version 3.5(r) (May, 2009) Notes
    Major Section:  RELEASE-NOTES
    

    Please also see note-3-5 for changes in Version 3.5 of ACL2.

    This release incorporates improvements from Ruben Gamboa in support for non-standard analysis in ACL2(r), in the following ways:

    ACL2(r) now supports non-classical objects that are not also numeric, e.g., non-classical cons pairs. Consequently, the built-in standard-numberp has been replaced with standardp.

    If f is a classical function, the value (f x1 ... xn) is guaranteed to be standard when the xi are standard. ACL2(r) can now recognize this fact automatically, using defun-std. For example, the following can be used to assert that the square root of 2 is a standard value.

    (defthm-std sqrt-2-rational
      (standardp (acl2-sqrt 2)))
    
    More generally, the expression (f x1 ... xn) can contain free variables, but the result is guaranteed to be standard when the variables take on standard variables, as in the following:
    (defthm-std sqrt-x-rational
      (implies (standardp x)
               (standardp (acl2-sqrt x))))
    

    A potential soundness bug in encapsulate was fixed. Specifically, when a classical, constrained function is instantiated with a lambda expression containing free variables, it may produce non-standard values depending on the values of the free variables. This means that the functional instantiation cannot be used to justify a non-classical theorem. For example, consider the following sequence:

    (encapsulate
      ((f (x) t))
      (local (defun f (x) x)))
    (defthm-std f-x-standard
      (implies (standardp x)
               (standardp (f x))))
    (defthm plus-x-standard
      (implies (standardp x)
               (standardp (+ x y)))
      :hints (("Goal"
               :use ((:functional-instance f-x-standard
                                           (f (lambda (x) (+ x y))))))))
    (defthm plus-x-eps-not-standard
      (implies (standardp x)
               (not (standardp (+ x (/ (i-large-integer)))))))
    (defthm nil-iff-t
      nil
      :hints (("Goal"
              :use ((:instance plus-x-standard
                               (y (/ (i-large-integer))))
                    (:instance plus-x-eps-not-standard)))))
    

    ACL2(r) also supports the introduction of non-classical functions with defchoose. These behave just as normal functions introduced with defchoose, but they have a non-classical choice property.

    Finally, ACL2(r) now comes with a revamped library supporting non-standard analysis, still distributed separately as books/nonstd/. The new library uses defchoose to state more natural and useful versions of the IVT, MVT, etc. It also supports the introduction of inverse functions, e.g., logarithms. Finally, the library has much more extensive support for differentiation.




    acl2-sources/doc/HTML/NOTE-3-6-1.html0000664002132200015000000000474412222333527016172 0ustar kaufmannacl2 NOTE-3-6-1.html -- ACL2 Version 6.3

    NOTE-3-6-1

    ACL2 Version 3.6.1 (September, 2009) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    The essential changes to ACL2 since Version 3.6 are the two bug fixes described below. There was also some functionality-neutral code refactoring, as requested by Daron Vroon in support of the ACL2 Sedan (see acl2-sedan). Also see http://code.google.com/p/acl2-books/wiki/BooksSince36 for a list of books in Version 3.6.1 of ACL2 but not Version 3.6. For changes to existing books rather than additions, see the log entries at http://code.google.com/p/acl2-books/source/list starting with revision r329 up through revision 350.

    Fixed a soundness bug in the handling of ruler-extenders, specifically in the handling of LET-expressions. Thanks to Pete Manolios, who sent us a proof of nil, essentially as follows. In the termination proof for foo below, the binding of x to (cons t x) was not substituted into the recursive call of foo for purposes of the termination proof.

    (defun foo (x)
      (declare (xargs :ruler-extenders :all))
      (let ((x (cons t x)))
        (if (endp (cdr x))
            x
          (cons t (foo (cdr x))))))
    
    (defthm foo-bad
      nil
      :hints (("Goal"
               :use ((:instance foo (x '(3))))
               :in-theory (disable foo (foo))))
      :rule-classes nil)
    

    Fixed a typo in code supporting ruler-extenders (specifically, swapped arguments in a recursive call of ACL2 source function get-ruler-extenders2, which could cause problems for functions defined using mutual-recursion). Thanks to Daron Vroon for bringing this bug to our attention, pointing out the swapped arguments.




    acl2-sources/doc/HTML/NOTE-3-6.html0000664002132200015000000004347312222333527016036 0ustar kaufmannacl2 NOTE-3-6.html -- ACL2 Version 6.3

    NOTE-3-6

    ACL2 Version 3.6 (August, 2009) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 3.5 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental hons-and-memoization version. Each change is described in just one category, though of course many changes could be placed in more than one category.

    Note that (starting with ACL2 Version 3.5) LispWorks is no longer supported as a platform for ACL2, as the LispWorks compiler could not handle the ACL2 sources; see comments in the ACL2 sources about ``function size'' being ``too large''.

    CHANGES TO EXISTING FEATURES

    In the xargs declare form, the function symbol (or symbols, plural, in the case of mutual-recursion) being defined may now be used in the specified :measure and :guard terms. Note that, the definition(s) of the new symbol(s) are still not used in the termination proof. Thanks to Daron Vroon for discussions leading to this addition for the measure and to Dave Greve for requesting such an enhancement for the guard.

    Processing of the :guard-hints in an xargs declare form is now delayed until the start of guard verification. As a result, the function symbol(s) being defined may now be used as one might expect in such hints, for example in an :in-theory form. Thanks to Jared Davis for suggesting that we make such an improvement and providing an example.

    Made a low-level change to make-event in support of the ACL2s utility ``dynamic-make-event''. Thanks to Peter Dillinger for explaining the issue leading to this change.

    Modified the failure message printed when a measure conjecture fails to be proved, to indicate whether or not the hint :ruler-extenders :all would create a different measure conjecture. Thanks to Peter Dillinger for suggesting such a modification.

    A call of add-default-hints had added hints to the end of the current list of default hints. Now, it adds them to the beginning of that list, so that they are tried first. However, add-default-hints now takes a keyword argument, :at-end. If that argument is supplied and evaluates to other than nil, then the previous behavior occurs.

    When save-exec is used to save ACL2 images, the build dates are now printed on separate lines at startup and in the executable script. Thanks To Bob Boyer for requesting some newlines.

    Forward-chaining rules are now generated so that every let (also let* and lambda) expression is expanded away, as is every call of a so-called ``guard holder'' (must-be-equal, prog2$, ec-call, the). These were formerly only expanded away in the conclusion. Thanks to Konrad Slind for a helpful email leading to this change.

    Current-theory now causes a hard error when applied to a name not found in the current ACL2 logical world, rather than simply returning nil.

    When the underlying Common Lisp is GCL, ACL2 no longer uses the #n= reader macro when writing out certain files, including certificate files. In all other Lisps, it now uses the #n= ``print-circle'' feature not only for certificate files and ``expansion.lsp'' files written for example in support of make-event, but also for files written in support of :comp. This is all managed with new state global variable print-circle-files; see print-control. Thanks to Dave Greve for pointing out that GCL is limited by default to 1024 indices for #n=.

    NEW FEATURES

    A documentation topic explains in some detail how hints work with the ACL2 proof ``waterfall'': see hints-and-the-waterfall. This topic may be useful to those who write computed hints (see computed-hints) or other advanced hints.

    Added a new hint keyword, :no-thanks, that avoids printing the usual ``Thanks'' message for hints. Thanks to Peter Dillinger for requesting this feature.

    Added a new hint keyword, :backtrack, that checks the goals produced by processing a goal, and can cause the goal to be re-processed using a new hint. See hints. Thanks to Pete Manolios for a conversation that led to the idea of this hint.

    Added a new class of hints, override-hints, that is similar to default-hints, except that override-hints are always applied, even if the user has supplied a hint explicitly for the goal. See override-hints. Thanks to Pete Manolios and Harsh Raju Chamarthi for useful discussions on this topic, including its application to testing.

    When a goal ready to be pushed for proof by induction is given the new hint ``:do-not-induct :otf'', it is indeed pushed for proof by induction, rather than causing immediate failure as in the case of the hint ``:do-not-induct t''. Instead, the proof fails when the goal is later picked to be proved by induction. Thanks to Peter Dillinger for discussions leading to this feature.

    Related to computed hints only: Each history entry in the list stored in variable HIST (see computed-hints) now has a :CLAUSE field, which provide's access to a goal's parent, parent's parent, and so on (within the same induction and forcing round only).

    It is now possible to inhibit warnings produced by in-theory events and hints that occur when certain built-in definitions and executable-counterparts are disabled: just evaluate (assign verbose-theory-warning nil). Thanks to Jared Davis (and probably others in the past) for requesting such a mechanism.

    HEURISTIC IMPROVEMENTS

    A source function (linearize-lst) was replaced by tail-recursive code, which can avoid a stack overflow. Thanks to Dave Greve for sending a helpful example.

    The heuristics for limiting forward-chaining have been slightly relaxed, to allow derivations based on the occurrence of all arguments of the forward-chaining rule's conclusion in the goal (after stripping leading calls of NOT). Thanks to Dave Greve for contributing this improvement and providing a motivating example.

    We simplified induction schemes by eliminating each hypothesis of the form (not (equal term (quote const))) for which some other hypothesis in the same case equates term with some (presumably other) quoted constant. Thanks to Dave Greve for requesting an improvement of this sort.

    BUG FIXES

    Fixed a soundness bug related to redundancy of encapsulate events (see redundant-events) and ruler-extenders. A proof of nil in ACL2 Version 3.5 appears in a comment in (deflabel note-3-6 ...) in the ACL2 source code. The fix is to insist that in order for one encapsulate event to be redundant with another, they must be evaluated with the same default-ruler-extenders. Analogous to this issue of default-ruler-extenders for encapsulates is an issue of the default-verify-guards-eagerness, which has similarly been fixed.

    Fixed soundness bugs related to the handling of subversive-recursions for constraints. Proofs of nil in ACL2 Version 3.5 appear in a comment in (deflabel note-3-6 ...) in the ACL2 source code.

    Fixed a bug that could cause the following error during calls of certify-book in the presence of calls of skip-proofs in the book:

       ACL2 Warning [Skip-proofs] in
    
       HARD ACL2 ERROR in FMT0: Illegal Fmt Syntax.  The tilde-@ directive at
       position 0 of the string below is illegal because its variable evaluated
       to 0, which is neither a string nor a list.
    
       "~@0"
    
    Thanks to Dave Greve for reporting this bug and making available a very helpful test case.

    The :corollary of a rule (see rule-classes) failed to use the default-hints of the logical world. This has been fixed.

    (CCL only) We removed a call, for CCL 1.3 (and beyond) only, of foreign function sync in the closing of output channels. Thanks to Daron Vroon for reporting issues with such a call on a non-Intel platform.

    Fixed a bug in reporting failures when monitoring rewrite rules with free variables in the hypotheses, that could cause a hard Lisp error (from which ACL2 continues, however). Thanks to Eric Smith for sending a very helpful example with his bug report.

    Fixed the handling of :induct hints, which had thrown away hint information from parent goals. For example, the thm form below failed to prove even though the second hint is in some sense superfluous; induction occurs automatically at "Goal'" even without the hint on that. The failure was due to discarding the hint information on "Goal".

    (in-theory (disable append))
    (thm (equal (cdr (cons a (append (append x y) z))) (append x y z))
         :hints
         (("Goal" :in-theory (enable append))
          ("Goal'" :induct t) ; failed unless this line is commented out
          ))
    

    Fixed a bug in the args command that was failing to show the formals of primitives (built-in functions like consp that do not come with explicit definitions). Thanks to John Cowles for pointing out this bug. (At a lower level, the bug was that primitives failed to have 'stobjs-in or 'stobjs-out properties.)

    Fixed bugs in the utility supporting moving directories of certified books, sometimes used in Debian builds (as described in source function make-certificate-file). Thanks to Alan Dunn for pointing out such a bug, in paths associated with defpkg events in portcullis commands in certificates (which are used for error reporting). There were also bugs, now fixed, that prevented renaming some book paths. Please note that this mechanism is not guaranteed to be sound; in particular, it can probably misbehave when macros are used to generate portcullis events. However, it seems likely that such issues will be very rare.

    Eliminated warnings that could arise when tracing a function with trace$. Now, when trace$ is applied to a function without option :native, that function's declarations and documentation are discarded.

    Fixed a bug that could cause a failure when building an executable image using SBCL as the underlying Common Lisp. Thanks to Jun Sawada for reporting this failure. We made a similar fix for CMUCL.

    Fixed a bug in save-exec in the case that an absolute pathnanme is supplied. Thanks to Jared Davis for bringing this bug to our attention.

    Fixed a bug in the use of trace$ with the :native and :multiplicity options that caused hard errors for some underlying Lisps.

    Fixed a bug in the interaction of trace$ and :comp, which caused error as comp tried to re-trace functions that it temporarily untraced.

    NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE

    See http://code.google.com/p/acl2-books/wiki/BooksSince35 for a list of books in Version 3.6 of ACL2 but not Version 3.5. For changes to existing books rather than additions, see the log entries at http://code.google.com/p/acl2-books/source/list starting with revision r286 up through revision r329.

    It is no longer required to specify a value for environment (or `make') variable ACL2_SYSTEM_BOOKS when running `make' in the distributed book directory, even when that directory is not under the directory containing the ACL2 executable. Thanks to Peter Dillinger for providing this improvement, by modifying books/Makefile-generic and, as needed, distributed book Makefiles.

    Thanks to Peter Dillinger, some books in support of the ACL2 Sedan (ACL2s) are more easily available for ACL2 users who do not use ACL2s. See acl2-sedan.

    EMACS SUPPORT

    If the following form is evaluated before the load of emacs/emacs-acl2.el, then variables are now set to reflect the directory containing that file.

    (if (boundp '*acl2-sources-dir*)
        (makunbound '*acl2-sources-dir*))
    

    Fixed info-dir-entry line in generated info file (by patching doc/write-acl2-texinfo.lisp). Thanks to Alan Dunn for providing this patch.

    EXPERIMENTAL HONS VERSION

    Bob Boyer and others have contributed numerous changes for the experimental ``hons'' version of ACL2 (see hons-and-memoization). A number of these have been crafted to work specifically with CCL (Clozure Common Lisp, formerly OpenMCL), which is now required as the underlying Lisp for the ``hons'' version of ACL2.

    A heuristic (source function too-many-ifs has been made more scalable (for the non-HONS version as well, in fact), but with no functional change. Thanks to Jared Davis for noticing performance issues and suggesting fixes.

    Other changes including the following, quoting Bob Boyer:

    The CCL CASE macro now does better than a dumb linear search in some CASEes.

    SH and CSH are functions to talk to the underlying Gnu-Linux from CCL. Good for repeated calling when you simply cannot afford the copying cost of a FORK because you are using, say, a dozen gigabytes.

    Added CCL compiler-macros for IF and OR, to support some 'coverage' analysis, cf. IF-REPORT, extending the profiling.

    Introduced the type 'mfixnum' so that things like counting honses and counting calls to memoized or profiled functions can run fast in CCL 64 bits and yet still run at all under 32 bits.

    Moved all HONSes to CCL's newish static space, which permits the address of a cons to be used as a hash key, as in most Lisps. (CCL moves most conses and most everything when it does a compacting-gc.)

    Quite a few changes in the memoize-fn reporting.

    Added a timer facility, cf. call-with-timeout. Good for running under throttle some gross thoughts like 'Is it true that you can't fit 12 pigeons into 11 holes' on some propositional calculus systems/functions.

    Added rwx-size, pid-owner-cmdlines, rss, and proc-stat to help see what is really going on virtually in Gnu-Linux.

    Fixed at least one bug in compact-print-file and helped make its integration into ACL2's read-object$ a little more sound. Still worried some about *print-readably* vs. readtable-case. Does anyone else stay awake late at night worrying about readtable-case?

    Revised how the *watch-dog-process* interrupts the main process for the thousandth time, cf. watch. Haven't changed it in weeks, which means that (a) it is getting tolerable or (b) I've run out of gas.




    acl2-sources/doc/HTML/NOTE-3-6_lparen_R_rparen_.html0000664002132200015000000000101312222333527021346 0ustar kaufmannacl2 NOTE-3-6_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-3-6(R)

    ACL2 Version 3.6(r) (August, 2009) Notes
    Major Section:  RELEASE-NOTES
    

    Please also see note-3-6 for changes in Version 3.6 of ACL2.




    acl2-sources/doc/HTML/NOTE-4-0-WORMHOLE-CHANGES.html0000664002132200015000000000573612222333527020331 0ustar kaufmannacl2 NOTE-4-0-WORMHOLE-CHANGES.html -- ACL2 Version 6.3

    NOTE-4-0-WORMHOLE-CHANGES

    how to convert calls of wormhole for Version 4.0
    Major Section:  NOTE-4-0
    

    Here we describe how to convert an ``old-style'' call of wormhole -- that is, a call suitable for ACL2 versions preceding 4.0 -- in which the pseudo-flag was t. In order to convert such a call

    (wormhole t 'name input form ...)
    
    to a new-style call, the following steps must be carried out. Note that the wormhole name must always be quoted now.

    First, eliminate the first argument, t, and add a new second argument that is the quoted lambda expression

    '(lambda (whs) (set-wormhole-entry-code whs :ENTER))
    
    Setting the entry code to :ENTER is not necessary if you maintain the invariant (after initialization) that it is always :ENTER. In that case, the simpler quoted lambda will suffice:
    '(lambda (whs) whs)
    

    Second, change the form argument so that instead of talking about the state-global variable wormhole-output it talks about the state-global variable wormhole-status. Look for (@ wormhole-output), (assign wormhole-output ...), (f-get-global 'wormhole-output ...) and (f-put-global 'wormhole-output ...) in form and replace them with expressions involving wormhole-status.

    However, remember that the old data stored in wormhole-output is now in the wormhole-data component of the wormhole-status. Thus, for example, an old use of (@ wormhole-output) will typically be replaced by (wormhole-data (@ wormhole-status)) and an old use of (assign wormhole-output ...) will typically be replaced by

    (assign wormhole-status (set-wormhole-data (@ wormhole-status) ...))
    

    In summary, an old-style call like

    (wormhole t 'name
              input
              '(...1 (@ wormhole-output) ...2
                ...3 (assign wormhole-output ...4) ...5)
              ...6)
    
    can become
    (wormhole 'name
              '(lambda (whs) (set-wormhole-entry-code whs :ENTER))
              input
              '(...1 (wormhole-data (@ wormhole-status)) ...2
                ...3 (assign wormhole-status
                            (set-wormhole-data (@ wormhole-status)
                                               ...4) ...5)
              ...6)
    

    In any case, and especially if your wormhole call had a pseudo-flag other than t, we recommend that you see wormhole.




    acl2-sources/doc/HTML/NOTE-4-0.html0000664002132200015000000013752112222333527016027 0ustar kaufmannacl2 NOTE-4-0.html -- ACL2 Version 6.3

    NOTE-4-0

    ACL2 Version 4.0 (July, 2010) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 3.6.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category. Also see note-3-6-1 for other changes since Version 3.6.

    CHANGES TO EXISTING FEATURES

    There have been extensive changes to the handling of compiled files for books, which may generally be invisible to most users. The basic idea is that when compiled files are loaded on behalf of include-book, they are now loaded before events in the book are processed, not afterwards. This can speed up calls of include-book, especially if the underlying Lisp compiles all definitions on the fly (as is the case for CCL and SBCL). One functional change is that for keyword argument :load-compiled-file of include-book, the values :comp-raw, :try, and :comp! are no longer legal. (Note that the handling of :comp-raw was actually broken, so it seems that this value wasn't actually used by anyone; also, the handling of :comp! formerly could cause an error in some Lisp platforms, including SBCL.) Another change is that if include-book is called with :load-compiled-file t, then each sub-book must have a compiled file or a so-called ``expansion file''; see book-compiled-file. In the unlikely event that this presents a problem, the makefile provides a way to build with compilation disabled; see compilation. Users of raw mode (see set-raw-mode) will be happy to know that include-book now works if there an up-to-date compiled file for the book, since portcullis commands are now incorporated into that compiled file. The mechanism for saving expansion files has changed, and the :save-expansion argument of certify-book has been eliminated; see certify-book. More discussion of ACL2's new handling of book compilation is described in a new documentation topic; see book-compiled-file.

    It was possible to get a hard Lisp error when certifying a book with a redundant defconst form whose term is not identical to the existing defconst form for the same name. The following example illustrates the problem, which has been fixed (as part of the change in handling of compiled files for books, described above). Imagine that after the initial (in-package "ACL2") form, file foo.lisp has just the form (defconst *a* (append nil nil)). Then before the fix, we could have:

      ACL2 !>(defconst *a* nil)
      [[output omitted]]
      ACL2 !>(certify-book "foo" 1)
      [[initial output omitted]
      * Step 5:  Compile the functions defined in "/v/joe/foo.lisp".
      Compiling /v/joe/foo.lisp.
      End of Pass 1.
      End of Pass 2.
      OPTIMIZE levels: Safety=0 (No runtime error checking), Space=0, Speed=3
      Finished compiling /vjoe/foo.lisp.
      Loading /v/joe/foo.lisp
    
      Error: Illegal attempt to redeclare the constant *A*.
    

    The wormhole facility has been changed to repair a bug that allowed guard violations to go undetected. The major change has to do with the treatment of what used to be called the ``pseudo-flag'' argument which has been replaced by a quoted lambda expression. See note-4-0-wormhole-changes for help in converting calls of wormhole. Also see see wormhole and see wormhole-eval.

    The function assign-wormhole-output has been eliminated but its functionality can be provided by wormhole-eval.

    The ACL2 tutorial has been greatly expanded, for example to include a self-contained discussion of rewriting. See acl2-tutorial.

    Formerly, the mbe macro and must-be-equal function were disallowed in any definition within an encapsulate having a non-empty signature. Now, these are allowed provided the definition has been declared to be non-executable (see defun-nx). As a result, defevaluator events may now include must-be-equal among the function symbols known by the evaluator; this had not previously been allowed. Thanks to Sol Swords for discussions leading to this relaxation for defevaluator.

    Princ$ now prints strings more efficiently. Thanks to Jared Davis for suggesting the improvements to princ$.

    The use of xargs declaration :non-executable t no longer requires the absence of state or declared stobjs among the formal parameters of a function definition. As before, the use of :non-executable t turns off single-threadedness checking for the body, and also as before, attempts to execute the function will fail. Thanks to Sol Swords for requesting this relaxation (for automatic generation of so-called ``flag functions'' for definitions using mutual-recursion).

    The documentation has been improved for explaining to advanced users the details of the ACL2 hint mechanism; see hints-and-the-waterfall, and see the example about nonlinearp-default-hint in distributed book books/hints/basic-tests.lisp. Thanks to Robert Krug for useful discussions, in particular suggesting the above example as one to be explained with the documentation.

    The time$ macro has been enhanced to allow user control of the timing message that is printed, and of when it is printed. See time$. Thanks to Jared Davis for providing the essential design, helpful documentation (largely incorporated), and an initial implementation for raw Lisp.

    The :ttags argument to include-book had been required when including a certified book that uses trust tags. This is no longer the case: essentially, :ttags defaults to :all except that warnings will be printed. Thanks to Jared Davis for requesting such a relaxation, and to him and Sandip Ray for useful discussions.

    The definition of mv-let has been modified so that the single-step macroexpansion (see trans1) of its calls can be evaluated. Thanks to Pete Manolios for bringing this evaluation issue to our attention and ensuing discussions.

    All calls of so-called ``guard-holders'' -- prog2$, must-be-equal (from calls of see mbe), ec-call, and mv-list -- are now removed before storing hypotheses of rules of class :rewrite or :linear. Thanks to Sol Swords for requesting this enhancement and sending the following example in the case of rewrite rules.

    (defthm foo
      (prog2$ (cw "asdf")
              (and (equal (car (cons x y)) x)
                   (equal (cdr (cons x y)) y))))
    

    The handling of fmt directive ~s has been modified so that if the argument is a symbol that would normally be printed using vertical bars (|), then that symbol is printed as with ~f. Thanks to Jared Davis for providing the following example showing that treatment of ~s was a bit unexpected: (cw "~s0.~%" '|fo\|o|).

    Error messages have been improved for ill-formed terms (in ACL2's so-called ``translation'' routines). Thanks to Jared Davis for requesting such an enhancement.

    Modified defun-sk so that it executes in :logic mode. Previously, evaluation of a defun-sk event in :program mode caused a somewhat inscrutable error, but now, :program mode is treated the same as :logic mode for purposes of defun-sk.

    The ``system hacker'' commands (redef+) and (redef-) are now embedded event forms (see embedded-event-form), hence may be used in books as well as in progn and encapsulate events. Also, these two commands are now no-ops in raw Lisp.

    The function symbol worldp (in the "ACL2" package) has been renamed to plist-worldp.

    The function gc$-fn (resulting from macroexpansion of gc$) is now in :logic mode. Thanks to Jared Davis for requesting this change.

    The user now has control over whether compilation is used, for example whether or not certify-book compiles by default, using function set-compiler-enabled. See compilation.

    Modified the conversion of relative to absolute pathnames in portcullis commands for book certification. Now, more pathnames remain as relative pathnames.

    The "Ttags" warning that can be printed by include-book is now given even if set-inhibit-output-lst has specified `warning'. To suppress it, specify warning! instead, for example, (set-inhibit-output-lst '(acl2::warning! acl2::proof-tree)).

    On occasion, ACL2 prints the message ``Flushing current installed world'' as it cleans up when certain actions (installing a world) are interrupted. This operation has been sped up considerably. If your session includes many events, you can probably speed up any such operation further by invoking reset-prehistory. Thanks to Jared Davis for sending a query that led us to make this improvement.

    Calls of the form (ec-call (must-be-equal logic exec)) are no longer allowed, since we do not have confidence that they would be handled correctly.

    The underlying function for good-bye (and hence for exit and quit) is now in :logic mode. Thanks to Jared Davis for requesting this enhancement.

    We now require that every function symbol in the signature of an encapsulate event have a :logic mode definition at the end of the first pass, not merely a :program mode definition (which formerly was sufficient). You can still define such a function in :program mode, provided it is followed by a :logic mode definition (where of course both definitions are local, since we are discussing functions is introduced in the signature). Thanks to Carl Eastlund for bringing this issue to our attention. (Note: An analogous modification has been made for :bdd hints as well.)

    The following functions now have raw Lisp implementations that may run faster than their ACL2 definitions: assoc-eq, assoc-equal, member-eq, member-equal, subsetp-eq, subsetp-equal, remove-eq, remove-equal, position-eq, and position-equal. Thanks to Jared Davis for suggesting that we consider such an improvement.

    We now avoid infinite loops caused when tracing functions that implement trace$. Thanks to Rob Sumners and Eric Smith for useful discussions.

    The implementation of trace! has been modified slightly, to accommodate the fix for ``some holes in the handling of trust tags'' described later, below.

    This item applies unless the host Lisp is GCL. An interrupt (control-c) will now cause a proof to exit normally in most cases, by simulating a timeout, as though with-prover-time-limit had been called with a time-limit of 0. If the first interrupt doesn't terminate the proof, a second one should do so (because a different, more ``severe'' mechanism is used after the first attempt). As a result, redo-flat should work as one might expect even if a proof is interrupted. Thanks to Dave Greve for requesting this enhancement to redo-flat. Technical note: for reasons related to this change, time-limits are no longer checked in evaluator functions (ev-fncall, ev, ev-lst, ev-fncall-w, ev-w, and ev-w-lst).

    It is now legal for proof-checker macro-commands to appear in :instructions that are used in place of :hints. Thanks to Sandip Ray for (most recently) requesting this feature.

    The value of :command-conventions for ld special variable ld-post-eval-print is now treated as though it were t if the value ld special variable ld-error-triples is nil. The following example illustrates this change.

    ACL2 !>(ld-post-eval-print state) ; default
    :COMMAND-CONVENTIONS
    ACL2 !>(ld-error-triples state) ; default
    T
    ACL2 !>(set-ld-error-triples nil state)
    *** Then, before the change:
    ACL2 !>(mv t 3 state)
     3
    *** Instead, after the change:
    ACL2 !>(mv t 3 state)
    (T 3 <state>)
    

    The default behavior of ld has been changed. Formerly when an error occurred that halted a subsidiary call of ld, then the parent ld would continue. That is no longer the case. Consider the following example.

    (ld '((ld '((defun f (x) x)
                (defun bad (x)) ; ERROR -- missing the body
                ))
          (defun g (x) x)))
    
    Formerly, g would be defined in the resulting logical world. Now, the error halts not only the inner ld but also the outer ld. See ld, and for details of the new default value for :ld-error-action, :RETURN!, see see ld-error-action. Also see the paragraph below about a new utility, :p!. Thanks to Robert Krug and Sandip Ray for helpful discussions.

    Environment variable ACL2-CUSTOMIZATION has been replaced by ACL2_CUSTOMIZATION -- that is, the hyphen has been replaced by an underscore -- so that it can be set conveniently in the bash shell. See acl2-customization.

    The ``Warnings:'' summary is now omitted when there are no warnings, where formerly ``Warnings: None'' was printed. Thanks to Jared Davis for suggesting this change.

    We have modified the generation of constraints for encapsulate events in two primary ways, neither of them likely to affect many users. One change is that the virtual movement of definitions and theorems to in front of an encapsulate event, or of definitions to behind that event, is no longer inhibited in the case of nested encapsulates with non-empty signatures. The following example illustrates the other change, as discussed below.

    (encapsulate
     ((f (x) t))
     (local (defun f (x) x))
     (defun g (x) (cons x (f x)))
     (defun h (x) (g x))
     (defthm h-is-f (equal (car (h x)) x)))
    
    Previously, the constraint on f and h was essentially the conjunction of the definition of h and the theorem h-is-f. Now, the definition of g is conjoined as well; moreover, g receives the same constraint as do f and h, where previously g was only constrained by its definition. While we are not aware of a soundness bug caused by the previous approach, the new approach follows more precisely the intended notion of constraint.

    The use of trace$ (or trace!) option :multiplicity had been required when option :native was supplied. This is no longer the case. Also, a bug has been fixed that had prevented :multiplicity from working properly in GCL and Allegro CL.

    Several errors have been eliminated that formerly occurred when the constraints for a function symbol were unknown because it was constrained using a dependent clause-processor (see define-trusted-clause-processor. Now, it is assumed that the supporters argument in a define-trusted-clause-processor event is such that every ancestor of any function symbol constrained by the ``promised encapsulate'' of that event among, or ancestral in, those supporters. Thanks to Sol Swords, Sandip Ray, and Jared Davis for helpful discussions.

    The notion of constraint for functions introduced by defun has been modified slightly. No longer do we remove from the body of the definition calls of so-called ``guard-holders'': prog2$, must-be-equal, ec-call, and mv-list, and uses of the-error generated by the. Also, we now expand calls of the-error with the same aggressive heuristics applied to a number of other functions (technically, adding it to the list *expandable-boot-strap-non-rec-fns*).

    NEW FEATURES

    A new event, defattach, allows evaluation of calls of constrained (encapsulated) functions. In particular, users can now, in principle, soundly modify ACL2 source code; please feel free to contact the ACL2 implementors if you are interested in doing so. See defattach.

    Eric Smith has noticed that if you exit the break-rewrite loop using :a! during an ld of a file, then all changes to the logical world are discarded that were made during that call of ld. A new utility, :p!, pops just one level instead, and avoids discarding that work. (This change is related to an item above, ``The default behavior of ld has been changed.'') Thanks to Eric for pointing out this issue.

    New function mv-list is the identity function logically, but converts multiple values to lists. The first argument is the number of values, so an example form is as follows, where foo returns three values: (mv-list 3 (foo x y)). Thanks to Sol Swords for requesting this feature and for reporting a soundness bug in one of our preliminary implementations.

    A new state global variable, host-lisp, has as its value a keyword whose value depends on the underlying Common Lisp implementation. Use (@ host-lisp) to see its value.

    It is now possible to write documentation for HTML without error when there are links to nonexistent documentation topics. See the comments in macro acl2::write-html-file at the end of file doc/write-acl2-html.lisp. When there are such errors, they should be easier to understand than previously. Thanks to Alan Dunn for providing the initial modifications.

    It is now possible to inhibit specified parts of the Summary printed at the conclusion of an event. See set-inhibited-summary-types. Also see with-output, in particular the discussion of the new :summary keyword. Thanks to Sol Swords for requesting more control over the Summary.

    A new :hints keyword, :case-split-limitations, can override the default case-split-limitations settings (see set-case-split-limitations) in the simplifier. Thanks to Ian Johnson for requesting this addition and providing an initial implementation.

    It is now possible to defer and avoid some ttag-related output; see set-deferred-ttag-notes. Thanks to Jared Davis for requesting less verbosity from ttag-related output.

    A new command, :pl2, allows you to restrict the rewrite rules printed that apply to a given term. See pl2. Thanks to Robert Krug for requesting such a capability.

    ACL2 now provides a utility for canonicalizing filenames, so that soft links are resolved; see canonical-pathname. Moreover, ACL2 uses this utility in its own sources, which can eliminate some issues. In particular, include-book with argument :ttags :all no longer breaks when given a book name differing from the book name that was used at certification time; thanks to Sol Swords for reporting that problem. Also, certain errors have been eliminated involving the combination of packages in the certification world and trust tags; thanks to Jared Davis for sending an example of that problem.

    You can now suppress or enable guard-checking for an individual form; see with-guard-checking. Thanks to Sol Swords for requesting this feature.

    The walkabout utility has been documented (thanks to Rob Sumners for suggesting this documentation). This utility can make it easy to explore a large cons tree. New interactive commands (pp n) and (pp print-level print-length) have been added to restrict how much of the current object is displayed. See walkabout.

    Rules of class :type-prescription may now be provided a :backchain-limit-lst keyword. The default behavior is unchanged, but now type-set is sensitive not only to the new :backchain-limit-lst of a :type-prescription rule (if supplied) but to the default-backchain-limit of the current logical world. Setting of backchain-limits can now specify either the new (type-set) limit or the old limit (for rewriting); see set-default-backchain-limit and see set-backchain-limit. Moreover, the functions default-backchain-limit and backchain-limit now take a second argument of :ts or :rewrite to specify which backchain-limit is desired.

    HEURISTIC IMPROVEMENTS

    The so-called ``too-many-ifs'' heuristic has been modified. Such a heuristic has been employed in ACL2 (and previous Boyer-Moore provers) for many years, in order to limit the introduction of calls of IF by non-recursive functions. Most users need not be concerned with this change, but two proofs in the regression suite (out of thousands) needed trivial adjustment, so user proofs could need tweaking. In one application, this modification sped up proofs by 15%; but the change in runtime for the regression suite is negligible, so such speedups may vary. Thanks to Sol Swords for providing a test from ACL2 runs at Centaur Technology, which was useful in re-tuning this heuristic.

    Guard proof obligations could have size quadratic in the number of clauses in a case statement. This inefficiency has been removed with a change that eliminates a hypothesis of the form (not (eql term constant)) when there is already a stronger hypothesis, equating the same term with a different constant. Thanks to Sol Swords for bringing this problem to our attention and suggesting an alternate approach to solving it, which we may consider in the future if related efficiency problems persist.

    We adjusted the heuristics for determining induction schemes in the presence of ruler-extenders, when handling calls of a function symbol that is a ruler-extender, in either of two cases: either the function takes only one argument; or the function is prog2$ or ec-call, and the first argument contains no recursive call. These cases are treated more directly as though the ruler-extender call is replaced by the unique (in the case of prog2$ and ec-call, the second) argument.

    A new :type-prescription rule, true-listp-append, has been added:

    (implies (true-listp b)
             (true-listp (append a b)))
    
    If you are interested in the motivation for adding this rule, see comments in true-listp-append in ACL2 source file axioms.lisp.

    The use of :forward-chaining lemmas has been improved slightly. In previous versions, a conclusion derived by forward chaining was discarded if it was derivable by type-set reasoning, since it was ``already provable.'' But this heuristic prevented the conclusion from triggering further forward chaining. This has been fixed. Thanks to Dave Greve for pointing out this problem.

    The fundamental utility that turns an IF expression into a set of clauses has been optimized to better handle tests of the form (equal x 'constant) and their negations. This eliminates an exponential explosion in large case analyses. But it comes at the inconveience of sometimes reordering the clauses produced. The latter aspect of this change may require you to change some Subgoal numbers in proof hints. We apologize for the inconvenience.

    Certification can now run faster (specifically, the compilation phase) for books with very large structures generated by make-event, when there is significant sharing of substructure, because of a custom optimization of the Lisp reader. Thanks to Sol Swords for bringing this efficiency issue to our attention.

    Jared Davis reported inefficiency in certain make-event evaluation due to a potentially expensive ``bad lisp object'' check on the expansion produced by the make-event. This check has been eliminated except in the case that the expansion introduces packages (for example, by including a book during the expansion phase that introduces packages). Thanks to Jared for providing a helpful example.

    The application of rules of class :induction had the potential to loop (as commented in ACL2 source function apply-induction-rule). This has been fixed. Thanks to Daron Vroon and Pete Manolios for sending nice examples causing the loop.

    Heuristics have been tweaked so that false goals may be simplified to nil that had formerly been left unchanged by simplification, perhaps resulting in useless and distracting proofs by induction. Thanks to Pete Manolios for pointing out this issue by sending the following example: (thm (<= (+ 1 (acl2-count x)) 0)). (Technical explanation: When every literal in a clause simplifies to nil, even though we might not normally delete one or more such literals, we will replace the entire clause by the false clause.)

    Improved the efficiency of the built-in function, take. Thanks to Bob Boyer for suggesting this improvement.

    ACL2 can now use evaluation to relieve hypotheses when applying :type-prescription rules. Thanks to Peter Dillinger and Dave Greve for requesting this enhancement, and to Robert Krug for a relevant discussion long ago.

    Evaluation has been sped up during theorems for calls of mv-let, by avoiding repeated evaluation of the expression to which its variables are bound. Thanks to Sol Swords for requesting this improvement and sending an illustrative example.

    Modified a heuristic to avoid the opening up non-recursive function calls on calls of hide involving if-expressions. For example, the thm form below is now admitted

    (defun bar (x)
      (cons x x))
    (thm (equal (bar (hide (if a b c)))
         (cons (hide (if a b c)) (hide (if a b c)))))
    

    BUG FIXES

    Fixed a soundness bug in destructor elimination, which was preventing some cases from being generated. Thanks to Eric Smith for reporting this bug and sending a helpful example. (Technical detail: the fixes were in ACL2 source functions apply-instantiated-elim-rule and eliminate-destructors-clause1, and comments in the former contain Eric's example.)

    Fixed a bug that supported a proof of nil by exploiting the fact that portcullis commands were not included in check-sum computations in a book's certificate. For such a proof of nil, see the relevant comment in the ACL2 source file ld.lisp under (deflabel note-4-0 ...).

    Changed the implementation of add-include-book-dir. The previous implementation could allow relative pathnames to be stored in the portcullis commands of certificates of books, which perhaps could lead to unsoundness (though we did not try to exploit this to prove nil). Thanks to Jared Davis for reporting a bug in our first new implementation. An additional change to both add-include-book-dir and delete-include-book-dir is that these now work in raw-mode (see set-raw-mode). (Thanks to Dave Greve for suggesting a reduction in the warnings we produced related to raw-mode.) Note that it is no longer permitted to make a direct call of the form (table acl2-defaults-table :include-book-dir-alist ...); use add-include-book-dir instead.

    Fixed a soundness bug related to xargs keyword :non-executable. New macros, defun-nx and defund-nx, have been provided for declaring functions to be non-executable; see defun-nx. While we expect this bug to occur only rarely if at all in practice, the following example shows how it could be evoked.

      ;;;;;;;;;;;;;;;;;;;;
      ;;; Book sub.lisp
      ;;;;;;;;;;;;;;;;;;;;
      (in-package "ACL2")
      (defun f ()
        (declare (xargs :guard t
                        :non-executable t))
        (mv-let (a b c)
                (mv 3 4)
                (declare (ignore a b))
                c))
      (defun g ()
        (declare (xargs :guard t))
        (prog2$ (mv-let (x y z)
                        (mv 2 3 4)
                        (declare (ignore x y z))
                        nil)
                (f)))
      (defthm g-nil
        (equal (g) nil)
        :hints (("Goal" :in-theory (disable (f))))
        :rule-classes nil)
      ;;;;;;;;;;;;;;;;;;;;
      ;;; Book top.lisp
      ;;;;;;;;;;;;;;;;;;;;
      (in-package "ACL2")
      (include-book "sub")
      (defthm contradiction
        nil
        :hints (("Goal" :use g-nil))
        :rule-classes nil)
    

    The modification described above pertaining to defun-nx also prevents execution of non-executable functions that have been traced. The following example illustrates the problem; now, the following defun of g is illegal, and the problem disappears if defun-nx is used instead.

    (defun g (x) ; Use defun-nx to avoid an error after Version_3.6.1.
      (declare (xargs :guard t :non-executable t))
      x)
    (g 3) ; causes error, as expected
    (trace$ g)
    (g 3) ; returned 3 before the bug fix; after fix, causes error as expected
    

    A hard error was possible when attempting to include an uncertified book containing events of the form (make-event '(local ...)). This has been fixed. Thanks to Sol Swords for bringing this issue to our attention.

    Fixed a bug in the heuristic improvement described for Version_3.6 (see note-3-6) as ``We simplified induction schemes....'' The bug had prevented, in unusual cases such as the following (notice the impossible case), a proof by induction.

    (defun foo (a x)
      (and (consp x)
           (case a
             (0 (foo (car x) (cdr x)))
             (1 (foo (cdr x) (car x)))
             (0 (foo a (cons x x))))))
    (in-theory (disable (:type-prescription foo)))
    (thm (atom (foo a x)))
    

    Macro cw-gstack did not work with an :evisc-tuple argument. This has been fixed by changing cw-gstack so that it now evaluates its arguments. Thanks to Sol Swords for bringing this bug to our attention.

    Fixed a bug in :pso during the printing of failure messages for termination proofs.

    Fixed a bug in the handling of #. (see sharp-dot-reader). Thanks to Bob Boyer for bringing this bug to our attention.

    Replaced a hard Lisp error with a clean error, in certain cases that a :hints value is erroneously supplied as a non-nil atom. Example: (thm (equal x x) :hints 3).

    Fixed a bug in the interaction of function tracing with conversion of a function from :program to :logic mode. The following example illustrates what had been wrong.

    (defun f (x)
      (declare (xargs :mode :program))
      (car x))
    (f 3) ; raw Lisp hard error
    (trace$ f)
    (f 3) ; raw Lisp hard error (still)
    (defun f (x) (car x)) ; upgrade f to :logic mode
    (f 3) ; clean guard violation; f is no longer traced
    (trace$) ; uh oh - f is shown as traced
    (untrace$ f)
    (f 3) ; OUCH: hard Lisp error because old :program mode definition of
          ; the executable counterpart (sometimes called *1*f) was restored!
    

    Made a fix so that when building ACL2 with `make' option ACL2_SAFETY=3, there will no longer be any safety-0 compiled code generated. Thanks to Gary Byers for bringing this bug to our attention.

    Fixed a bug in the handling of override-hints that generate custom keyword hints (see custom-keyword-hints) involving the variable stable-under-simplificationp. Thanks to Ian Johnson for bringing this bug to our attention with explanation that included a helpful example, included as comment in the ACL2 source code for function apply-override-hint.

    The saved_acl2 script in CLISP could contain unexpected characters where simple newlines were expected. Dave Greve found this in a Cygwin environment on Windows. Thanks to Dave for reporting this bug and experimenting with a fix, and thanks to the CLISP folks for providing helpful information.

    Fixed a bug that could make :oops cause an error. Also, the oops command can no longer take you back before a reset-prehistory event.

    (GCL only) Fixed a bug that could occur when calling trace in raw Lisp in GCL.

    Proof summaries have been improved, so that they account for runes used in rewriting that takes place when generating goals to be proved in a forcing round. Thanks to Jared Davis for sending us an example illustrating this issue.

    Fixed a bug that (at least in CCL) could put extra backslashes (`\') in a pathname that ACL2 writes out to the executable script created by a build. Thanks to Gary Byers for explaining that the CCL behavior is legal (for our previous use of Common Lisp function merge-pathnames).

    We closed some holes in the handling of trust tags (also known as ``ttags''; see defttag) by include-book. The following example illustrates this rather subtle situation. Consider the following book.

    (in-package "ACL2")
    (make-event
     (er-progn
      (encapsulate
       ()
       (defttag :foo)
       (value-triple "Imagine something bad here!"))
      (value '(value-triple :some-value)))
     :check-expansion t)
    
    Formerly, the following commands succeeded.
    (certify-book "test3" 0 t :ttags :all)
    :u
    (include-book "test3" :ttags nil)
    
    But because of make-event keyword argument :check-expansion t, we know that the event (defttag :foo) is evaluated by the above include-book form, and hence the :ttags argument of include-book, above, should have specified :foo. The problem was that defttag forms evaluated during make-event expansion did not contribute to the trust tag information stored in the book's certificate. Note: Because of this change, one should avoid using make-event with :check-expansion t when the expansion would introduce a defttag event during include-book but not certify-book time. For an example illustrating this issue, see make-event-details, specifically the new version of the section labeled ``A note on ttags'' at the end of that documentation topic.

    Closed a small loophole that had the potential, in rare circumstances, to violate atomicity of under-the-hood updates for ACL2 arrays.

    The following example was formerly allowed, but resulted in a guard-verified function (here, g) whose guard proof obligation is not a theorem outside the encapsulate event. We now disallow guard verification for functions introduced non-locally inside an encapsulate event unless we determine that the proof obligations hold outside the encapsulate event as well.

    (encapsulate
     ((f (x) t))
     (local (defun f (x) (declare (xargs :guard t)) (consp x)))
     ;; ERROR!
     (defun g (x)
       (declare (xargs :guard (f x)))
       (car x)))
    

    The use of :comp on stobj functions had potentially caused a hard Lisp error; for example, this could occur when (defstobj foo fld) was followed by :comp foop. This has been fixed.

    Fixed a bug that could cause a raw Lisp error when the first argument of with-local-stobj is not a symbol.

    It had been possible to use the reserved keyword :computed-hints-replacement as the name of a custom keyword hint (see custom-keyword-hints). This has been fixed. Thanks to Dave Greve, who pointed out a confusing hint error message (which has also been fixed) that led us to this issue.

    Fixed a bug that could cause a hard Lisp error, instead of a graceful ACL2 error, if keyword :backchain-limit-lst in a rule class is given a cons that is not a true list, such as (1 . 1).

    Eliminated an error that could occur when redefining a function as a macro and then compiling, as in the example below.

    (defun foo (x) x)
    :redef!
    (defmacro foo (x) x)
    :comp t
    
    Thanks to Eric Smith for sending the above example in his bug report.

    Fixed a bug that could result in an assertion when a clause-processor causes an error.

    NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE

    See http://code.google.com/p/acl2-books/source/list for a record of books changed or added since the preceding release, with log entries.

    We note in particular the new system/ directory, which begins to specify ACL2 system code in anticipation of opening the architecture of ACL2 (see defattach for a relevant tool). Some system functions were changed slightly (but with the expectation of not generally affecting ACL2 behavior) in support of the development of this directory. Those interested in contributing to further such efforts are invited to contact the ACL2 implementors.

    New utilities have been provided for certifying most of the distributed books with more `make'-level parallelism. For example, we have obtained close to a 12x reduction in time by using `make -j 24 regression-fast' on a 24-processor machine. For more information see books/make-targets, or to include the books/workshops in the regression run, see books/regression-targets. Thanks to Sol Swords for providing these nice utilities.

    The top-level makefile, GNUmakefile, has been fixed so that the build processes (which are inherently sequential) will ignore the -j option of `make'. Note that regressions can still, however, be done in parallel, as the -j option will be passed automatically to the appropriate `make' command.

    EMACS SUPPORT

    EXPERIMENTAL VERSIONS

    The HONS version, supported primarily by Bob Boyer and Warren Hunt (see hons-and-memoization), has undergone numerous improvements. For example, keyword argument :FORGET is now supported when calling memoize from within the ACL2 loop, and system function worse-than is memoized with the :condition that both terms are function applications (clearing the memo-table after each prover invocation). Thanks to Jared Davis and Sol Swords for investigating the memoization of worse-than, and with suitable condition. Thanks also to Jared Davis for contributing structural modifications to the implementation of hons.

    David Rager contributed modifications to the parallel version (see parallelism), which include taking advantage of atomic increments available at least since Version 1.0.21 of SBCL and Version 1.3 of CCL.

    Some Related Topics




    acl2-sources/doc/HTML/NOTE-4-0_lparen_R_rparen_.html0000664002132200015000000000100412222333527021341 0ustar kaufmannacl2 NOTE-4-0_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-4-0(R)

    ACL2 Version 4.0(r) (July, 2010) Notes
    Major Section:  RELEASE-NOTES
    

    Please see note-4-0 for changes in Version 4.0 of ACL2.




    acl2-sources/doc/HTML/NOTE-4-1.html0000664002132200015000000001777612222333527016041 0ustar kaufmannacl2 NOTE-4-1.html -- ACL2 Version 6.3

    NOTE-4-1

    ACL2 Version 4.1 (September, 2010) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 4.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    The guard associated with calls of the macro, search, has been weakened so that now, given strings are no longer restricted to contain only standard characters unless the :test argument is char-equal.

    Modified the writing of ``hidden defpkg'' forms into certificate files (see hidden-defpkg), to support moving certificate files for distributed books, as is done by ACL2s (see acl2-sedan) and Debian releases of ACL2. Thanks to Camm Maguire for reporting a problem with Debian releases of ACL2 that led to this change.

    Expanded the constant *acl2-exports* by adding intersection-equal to the list. Thanks to Jared Davis for requesting this change.

    The :comp utility now compiles functions that have code conditionalized for raw Lisp only (presumably because a trust tag was active when they were defined). Previously, this was not the case when :comp was applied to more than a single function symbol.

    NEW FEATURES

    A new macro, top-level, allows evaluation directly in the top level loop for forms that normally need to be evaluated inside function bodies, such as with-local-stobj. See top-level. Thanks to Jared Davis for requesting such a utility.

    Added count, a Common Lisp function, to ACL2. In support of that addition, also added rewrite rule eqlablep-nth.

    HEURISTIC IMPROVEMENTS

    [None this time.]

    BUG FIXES

    We fixed a soundness bug that could occur when a function that returns multiple values that is called in its own guard. Thanks to Sol Swords for reporting this bug and sending a small self-contained example, which is included in a comment in the function chk-acceptable-defuns1 in ACL2 source file defuns.lisp.

    It was possible to cause an error when giving theory hints during redefinition of functions. This has been fixed. Thanks to Ian Johnson for sending an example that nicely illustrated this problem.

    Fixed system function io? for the case that formal parameter commentp is t and vars is non-empty. Thanks to David Rager for bringing to our attention the fact that io? was broken for such a combination of parameters.

    Not exactly a bug fix, but: defun-sk was breaking when a :guard is specified, so we have improved the documentation (see defun-sk) to explain how to provide verified guards for a function introduced by defun-sk. Thanks to Jared Davis for bringing this issue to our attention.

    Made a fix to the handling of interrupts, which in rare cases might have left one in a state where all subsequent proof attempts were labeled as ``Aborting due to an interrupt''.

    Fixed :pso and related utilities, so that when proof output is redirected to a file, all summary output goes to that file rather than to the terminal.

    (GCL on Windows only) Removed an inappropriate check, resulting in an error about ``pathname-device,'' that could prevent Windows GCL builds of ACL2. Thanks to Camm Maguire for reporting this problem and a helpful discussion.

    (Windows only) Modified the computation of canonical pathnames to avoid issues of case-insensitivity, in particular for the drive (e.g., "C:" vs. "c:"). Thanks to Harsh Raju Chamarthi for reporting this issue and helping with its debugging.

    (Windows only) The value of (@ distributed-books-dir) no longer will be missing the Windows drive prefix, for example, "C:". Thanks to Harsh Raju Chamarthi for reporting this issue and helping with its debugging.

    NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE

    See http://code.google.com/p/acl2-books/source/list for a record of books changed or added since the preceding release, with log entries.

    Modified books/Makefile-generic by adding a new BOOKS_SKIP_COMP variable, which is used in Makefiles in some subdirectories of books/, in order to avoid errors when compiling certified books for multiple Lisps.

    EMACS SUPPORT

    Distributed file emacs/emacs-acl2.el has been modified so that the forms control-t e and control-t control-e now pick up package markers (see sharp-bang-reader), in the following sense: if the top-level form is preceded by a line starting with #!, then that line is included in the inserted string. Thanks to Jared Davis for suggesting this enhancement and providing a preliminary implementation.

    EXPERIMENTAL VERSIONS

    For the HONS version there have been some changes to memoize:

    Memoize accepts a new keyword, :recursive, that is a synonym for the existing keyword :inline. Thanks to Sol Swords for requesting this addition. Moreover, it is now enforced that these keywords have Boolean values.

    Memoize may now be called on :program mode functions. Thanks to Sol Swords for requesting this enhancement.

    A bug has been fixed. Now, if memoize is called with a :condition-fn (with value other than nil or t), then the guard of the memoized function and the :condition-fn must be the same. Previously, one could exploit the lack of such a check to get a hard Lisp error, for example as follows.

         (defun f (x) (declare (xargs :guard t)) x)
         (defun cf (x) (declare (xargs :guard (consp x))) (car x))
         (memoize 'f :condition-fn 'cf)
         (f 3)
    

    Memoization is now illegal for built-in functions that use underlying raw Lisp in their implementations. To see why, consider the form (gc$), which is a macro call expanding to (gc$-fn nil). Previously, after evaluation of (memoize 'gc$-fn), a call of gc$ would no longer call the garbage collector, which had been invoked by raw Lisp code. Now, evaluation of (memoize 'gc$-fn) causes an error.




    acl2-sources/doc/HTML/NOTE-4-1_lparen_R_rparen_.html0000664002132200015000000000101112222333527021340 0ustar kaufmannacl2 NOTE-4-1_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-4-1(R)

    ACL2 Version 4.1(r) (September, 2010) Notes
    Major Section:  RELEASE-NOTES
    

    Please see note-4-1 for changes in Version 4.1 of ACL2.




    acl2-sources/doc/HTML/NOTE-4-2.html0000664002132200015000000007103412222333527016025 0ustar kaufmannacl2 NOTE-4-2.html -- ACL2 Version 6.3

    NOTE-4-2

    ACL2 Version 4.2 (January, 2011) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 4.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    The accumulated-persistence utility can now do finer-grained tracking, providing data for individual hypotheses and the conclusion of a rule. See accumulated-persistence. To try this out, evaluate the form (accumulated-persistence :all); then see accumulated-persistence for a discussion of display options using show-accumulated-persistence. Thanks to Dave Greve for suggesting this new capability and collaborating on its design and implementation.

    The defattach utility now permits the use of :program mode functions, though this requires the use of a trust tag (see defttag). See defattach and for discussion of the new capability, see defproxy, which explains how part of this change involves allowing :program mode functions to be declared non-executable.

    Redefinition (see ld-redefinition-action) is no longer permitted for functions that have attachments (see defattach). In such cases, the attachment must be removed first, e.g. with (defattach foo nil).

    Made small changes to mv-nth and defun-sk in order to permit guard verification of functions introduced with more than one quantified variable in a defun-sk form. The change to mv-nth is to weaken the guard by eliminating the requirement that the second argument satisfy true-listp, and replacing the call of endp in the definition body by a corresponding call of atom. The new definition of mv-nth is thus logically equivalent to the old definition, but with a weaker guard. Thanks to Sol Swords for sending the following example, for which the final verify-guards form had failed but now succeeds.

    (defstub foo (a b c) nil)
    (defun-sk forall-a-b-foo (c)
       (forall (a b) (foo a b c))
       :witness-dcls ((declare (Xargs :guard t
                                      :verify-guards nil))))
    (verify-guards forall-a-b-foo)
    

    The implementations of prog2$, time$, with-prover-time-limit, with-guard-checking, mbe (and must-be-equal), and ec-call have changed. See the discussion below of the new utility, return-last. A consequence is that trace$ is explicitly disallowed for these and related symbols, which formerly could cause hard Lisp errors, because they are now macros. Tracing of return-last is also disallowed. Another consequence is that time$ now prints a more abbreviated message by default, but a version of the old behavior can be obtained with :mintime nil.

    The following utilities no longer print an observation about raw-mode transitions: set-raw-mode-on, set-raw-mode-on!, set-raw-mode, and set-raw-mode-off. Thanks to Jared Davis for suggestion this change in the case of include-book (which proved awkward to restrict to that case).

    The system function translate-and-test now permits its LAMBDA form to refer to the variable WORLD, which is bound to the current ACL2 logical world.

    Modified abort handling to avoid talking about an interrupt when the error was caused by a Lisp error rather than an interrupt.

    The value of the constant *acl2-exports*, which is still a list, has been extended significantly, though only with the addition of symbols that one might reasonably have expected all along to belong to this list. A new distributed book, books/misc/check-acl2-exports.lisp, checks (at certification time) that no documented constant, macro, or function symbol in the "ACL2" package has been accidentally omitted from *acl2-exports*. Thanks to Dave Greve for helpful discussions related to this change.

    Improved the built-in `untranslate' functions to produce let* expressions when appropriate (more to help with tools that call untranslate and the like, than to help with proof output).

    The utility redo-flat now works for certify-book failures, just as it continues to work for failures of encapsulate and progn.

    The following only affects users who use trust tags to add to list values of either of the state global variables program-fns-with-raw-code or logic-fns-with-raw-code. For functions that belong to either of the above two lists, trace$ will supply a default value of :fncall to keyword :notinline, to avoid discarding raw-Lisp code for the function.

    The guard of the macro intern has been strengthened so that its second argument may no longer be either the symbol *main-lisp-package-name* or the string "COMMON-LISP". That change supports another change, namely that the following symbols in the "COMMON-LISP" package are no longer allowed into ACL2: symbols in that package that are not members of the list constant *common-lisp-symbols-from-main-lisp-package* yet are imported into the "COMMON-LISP" package from another package. See pkg-imports and see symbol-package-name. To see why we made that change, consider for example the following theorem, which ACL2 was able to prove when the host Lisp is GCL.

    (let ((x "ALLOCATE") (y 'car))
      (implies (and (stringp x)
                    (symbolp y)
                    (equal (symbol-package-name y)
                           "COMMON-LISP"))
               (equal (symbol-package-name (intern-in-package-of-symbol x y))
                      "SYSTEM")))
    
    Now suppose that one includes a book with this theorem (with :rule-classes nil), using an ACL2 built on top of a different host Lisp, say CCL, that does not import the symbol SYSTEM::ALLOCATE into the "COMMON-LISP" package. Then then one can prove nil by giving this theorem as a :use hint.

    The axioms introduced by defpkg have changed. See the discussion of pkg-imports under ``NEW FEATURES'' below.

    The error message for free variables (e.g., in definition bodies and guards) now supplies additional information when there are governing IF conditions. Thanks to Jared Davis for requesting this enhancement and collaborating in its design.

    The command :redef- now turns off redefinition.

    Improved proof output in the case of a :clause-processor hint that proves the goal, so that the clause-processor function name is printed.

    The proof-checker command `then' now stops at the first failure (if any).

    It is no longer permitted to submit definitions in :logic mode for merely part of an existing mutual-recursion event. Such an action left the user in an odd state and seemed a potential soundness hole.

    The function break$ is now in :logic mode. Thanks to Jared Davis for requesting this enhancement.

    The macro verify-termination now provides clearer output in the case that it is redundant. More important perhaps, as a courtesy it now causes an error when applied to a constrained function, since presumably such an application was unintended (as the constrained function could never have been in :program mode). Note that if one desires different behavior, one can create one's own version of verify-termination (but with a different name).

    Improved the guards for the following functions, often weakening them, to reflect more precisely the requirements for calling eq: alist-difference-eq, intersection-eq, intersection1-eq, intersectp-eq, not-in-domain-eq, set-difference-assoc-eq, set-equalp-eq, and union-eq. Thanks to Jared Davis for pointing out this issue for intersectp-eq.

    (CCL only) Made a change that can reduce the size of a compiled file produced by certify-book when the host Lisp is CCL, by discarding source information (for example, discarding local events).

    NEW FEATURES

    See the discussion above about new statistics that can be gathered by the accumulated-persistence utility.

    A new hint, :instructions, allows use of the proof-checker at the level of hints to the prover. Thanks to Pete Manolios for requesting this feature (in 2001!). See instructions.

    (For system hackers) There are new versions of system functions translate1 and translate, namely translate1-cmp and translate-cmp respectively, that do not take or return state. See the Essay on Context-message Pairs for relevant information. Thanks to David Rager for collaborating on this enhancement.

    A new utility, return-last, is now the unique ACL2 function that can pass back a multiple value result from one of its arguments. Thus, now the following are macros whose calls ultimately expand to calls of return-last: prog2$, time$, with-prover-time-limit, with-guard-checking, mbe (and must-be-equal), and ec-call. With an active trust tag, an advanced user can now write code that has side effects in raw Lisp; see return-last. Thanks to Jared Davis for requesting this feature.

    A new function, pkg-imports, specifies the list of symbols imported into a given package. The axioms for defpkg have been strengthened, taking advantage of this function. Now one can prove theorems using ACL2 that we believe could not previously be proved using ACL2, for example the following.

    (equal (symbol-package-name (intern-in-package-of-symbol str t))
           (symbol-package-name (intern-in-package-of-symbol str nil)))
    
    Thanks to Sol Swords for a helpful report, which included the example above. See pkg-imports and see defpkg.

    Added function no-duplicatesp-eq.

    Added a new hint keyword, :backchain-limit-rw, to control the level of backchaining for rewrite, meta, and linear rules. This overrides, for the current goal and (as with :in-theory hints) descendent goals, the default backchain-limit (see set-backchain-limit). Thanks to Jared Davis for requesting this feature.

    Support is now provided for creating and certifying books that do not depend on trust tags, in the case that the only use of trust tags is during make-event expansion. See set-write-acl2x. Thanks to Sol Swords for reporting a couple of bugs in a preliminary implementation.

    Function (file-write-date$ filename state) has been added, giving the write date of the given file.

    See forward-chaining-reports for how to get new reports on the forward chaining activity occurring in your proof attempts. Thanks to Dave Greve for inspiring the addition of this utility.

    It is now possible to use ACL2's printing utilities to return strings, by opening output channels to the keyword :STRING rather than to filenames. See io. Thanks to Jared Davis for a helpful conversation that led us to add this feature.

    HEURISTIC IMPROVEMENTS

    We have slightly improved the handling of :forward-chaining rules that contain free variables. Formerly, such rules might fire only once, when the first match for a free variable is discovered, and would not fire again even if subsequent forward chaining made available another match. This made it difficult to predict whether a rule with free variables would fire or not, depending as it did on the order in which newly derived conclusions were added. The new handling is a little slower but more predictable. Thanks to Dave Greve for sending a helpful example that led us to consider making such an improvement.

    We have slightly improved the so-called ``type-set'' heuristics to work a bit harder on terms of the form (rec term), where rec is a so-called ``compound-recognizer'' function, that is, a function with a corresponding enabled :compound-recognizer rule. Thanks to Jared Davis for sending a helpful example (found, in essence, in the modified function type-set-rec, source file type-set-b.lisp).

    We made three heuristic improvements in the way contexts (so-called ``type-alists'') are computed from goals (``clauses''). Although these changes did not noticeably affect timing results for the ACL2 regression suite, they can be very helpful for goals with many hypotheses. Thanks to Dave Greve for sending a useful example (one where we found a goal with 233 hypotheses!).

    The algorithm for substituting alists into terms was modified. This change is unlikely to affect many users, but in one example it resulted in a speed-up of about 21%. Thanks to Dave Greve for supplying that example.

    Sped up include-book a bit by memoizing checksums of symbols. (This change pertains to ``normal'' ACL2 only, not the hons version (see hons-and-memoization, where such memoization already occurred.) We found about a 23% speed-up on an example from Dave Greve.

    Made a small change to the algorithm used to prove hypotheses of :type-prescription rules (ACL2 source function type-set-relieve-hyps). One change avoids a linear walk through the context (the ``type-alist'' structure), while the other could avoid storing unnecessary forced assumptions (into the so-called ``tag-tree'').

    BUG FIXES

    Fixed a long-standing soundness bug caused by the interaction of forced hypotheses with destructor elimination. The fix was to avoid using forcing when building the context (so-called ``type-alist'') when the goal is considered for destructor elimination; those who are interested can see a discussion in source function eliminate-destructors-clause1, which includes a proof of nil that no longer succeeds. A similar fix was made for generalization, though we have not exploited the previous code to prove nil in that case.

    Fixed a bug that allowed book certification to ignore skip-proofs around encapsulate events. Thus, a book could contain an event of the form (skip-proofs (encapsulate ...)), and a call of certify-book on that book could succeed even without supplying keyword :skip-proofs-okp t. This bug was introduced in Version 3.5 (May, 2009).

    Fixed a bug that could occur when including a book that attempts to redefine a function as a macro, or vice-versa. (For details of the issue, see the comment in the definition of variable *hcomp-fn-macro-restore-ht* in source file other-events.lisp.)

    (Windows only) Fixed handling of the Windows drive so that an executable image saved on one machine can be used on another, even with a different drive. Thanks to Harsh Raju Chamarthi for reporting this issue and doing a lot of testing and collaboration to help us get this right.

    Made a change to avoid possible low-level errors, such as bus errors, when quitting ACL2 by calling good-bye or its synonyms. This was occurring in CCL, and we are grateful to Gary Byers for helping us find the source of those errors (which basically was that ACL2 was attempting to quit while already in the process of quitting).

    Fixed a bug in with-guard-checking, which was being ignored in function bodies.

    Fixed a bug in top-level, which was not reverting the logical world when an error resulted from evaluation of the given form. Thanks to Jared Davis for bringing this bug to our attention.

    Fixed a long-standing bug (back through Version 2.7) that was discarding changes to the connected book directory (see cbd) when exiting and then re-entering the top-level ACL2 loop (with lp).

    In some host Lisps, it has been possible to be in a situation where it is impossible to interrupt checkpoint printing during the summary. We had thought this solved when the host Lisp was CCL, but Sol Swords sent us an example (for which we are grateful) illustrating that this behavior could occur. This has been fixed.

    Fixed a bug in a proof obligation generated for :meta and :clause-processor rules, that the guard on the metafunction or clause-processor function, fn, holds under suitable assumptions. Those assumptions include not only that the first argument of fn satisfies pseudo-termp, but also that all stobj inputs satisfy the corresponding stobj recognizer predicates. We had erroneously considered stobj outputs of fn instead of stobj inputs. Thanks to Sol Swords for bringing this bug to our attention with a simple example, and correctly pointing us to the bug in our code.

    Fixed the following bugs in defattach. We hadn't always been applying the full functional substitution when generating guard proof obligations. We had been able to hit an assertion when reattaching to more than one function. Attachment was permitted in the case of an untouchable function (see remove-untouchable). Finally, the guard proof obligation could fail in the case that the two functions have different formal parameter lists, as in the following example.

    (encapsulate
     ((foo (x) x :guard (symbolp x)))
     (local (defun foo (x) x)))
    (defun bar (x2)
      (declare (xargs :guard (symbolp x2)))
      x2)
    (defattach foo bar)
    

    Fixed a raw Lisp error that could be caused by including a book using make-event to define a function symbol in a locally-introduced package. An example appears in a comment in ACL2 source function write-expansion-file.

    Made a change that can prevent an error near the end of book certification when the underlying Host Lisp is Allegro Common Lisp, in the case that environment variable ACL2_SYSTEM_BOOKS has been set to the name of a directory with a parent that is a soft link. Thanks to Dave Greve for supplying an example to led us to this fix, which involves avoiding Allegro CL's implementation of the Common Lisp function, truename.

    Fixed a bug that was failing to substitute fully using bindings of free variables in forced hypotheses. A related change is that instead of binding such a free variable to a new variable of the form ???-Y, the new variable is now of the form UNBOUND-FREE-Y.

    Fixed a bug that could inhibit the printing of certain theory warnings (and probably, in the other direction, cause inappropriate such printing).

    We eliminated excessive "Raw-mode" warnings about add-include-book-dir that could be generated by the use of raw-mode during include-book. Thanks to Dave Greve for bringing this issue to our attention.

    Fixed the printing of results from forms within an encapsulate, so that they are abbreviated according to the ld-evisc-tuple.

    It is now possible to evaluate stobj-related forms after evaluating :set-guard-checking :none or :set-guard-checking nil, even in cases where such evaluation formerly caused a guard violation due to a bug in ACL2. Here is an example of an error that no longer occurs.

    ACL2 !>(defstobj st fld)
    
    Summary
    Form:  ( DEFSTOBJ ST ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     ST
    ACL2 !>(set-guard-checking :none)
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(fld st)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (FLD ST),
    which is (STP ST), is violated by the arguments in the call (FLD ST).
    [... etc. ...]
    
    You can understand how things now work by imagining that when a function introduced by defstobj is called, guard-checking values of :none or nil are temporarily converted to t. Thanks to Pete Manolios, Ian Johnson, and Harsh Raju Chamarthi for requesting this improvement.

    Fixed a bug in which the wrong attachment could be made when the same function has an attachment in a book and another in the certification world of that book (possibly even built into ACL2), if the load of a compiled file is aborted because a sub-book's compiled file is missing. The bug has been present since the time that defattach was added (Version_4.0). An example may be found in a comment in the deflabel for note-4-2 (ACL2 source file ld.lisp).

    The :doc and related utilities now cause a clean error when provided other than a symbol. Thanks to Jared Davis for pointing out the raw Lisp error that had occurred in such cases.

    It had been the case that in raw-mode (see set-raw-mode), it was possible to confuse include-book when including a book in a directory different from the current directory. This has been fixed. Thanks to Hanbing Liu for bringing this problem to our attention with a small example.

    NEW AND UPDATED BOOKS AND RELATED INFRASTRUCTURE

    Many changes have been made to the distributed books, thanks to an active ACL2 community. You can contribute books and obtain updates between ACL2 releases by visiting the acl2-books project web page, http://acl2-books.googlecode.com/.

    There is new Makefile support for certifying just some of the distributed books. See books-certification-classic, in particular discussion of the variable ACL2_BOOK_DIRS. Thanks to Sandip Ray for requesting this enhancement.

    The documentation for make-event now points to a new book, books/make-event/defrule.lisp, that shows how make-event can be used to do macroexpansion before generating events. Thanks to Carl Eastlund for useful interaction on the acl2-help mailing list that led us to add this example.

    EMACS SUPPORT

    Incorporated a version of changes from Jared Davis to the control-t f emacs utility (distributed file emacs/emacs-acl2.el), so that one can fill a format string from anywhere within the string.

    EXPERIMENTAL VERSIONS

    We refrain from listing changes here to experimental versions, other than an enhancement to the HONS version that can reduce sizes of certificate files, by applying hons-copy to introduce structure sharing (ACL2 source function make-certificate-file1).




    acl2-sources/doc/HTML/NOTE-4-2_lparen_R_rparen_.html0000664002132200015000000000100712222333527021346 0ustar kaufmannacl2 NOTE-4-2_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-4-2(R)

    ACL2 Version 4.2(r) (January, 2011) Notes
    Major Section:  RELEASE-NOTES
    

    Please see note-4-2 for changes in Version 4.2 of ACL2.




    acl2-sources/doc/HTML/NOTE-4-3.html0000664002132200015000000011205312222333527016023 0ustar kaufmannacl2 NOTE-4-3.html -- ACL2 Version 6.3

    NOTE-4-3

    ACL2 Version 4.3 (July, 2011) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 4.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level and to distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    Significant changes have been made for list-processing primitives such as member and assoc; see equality-variants. In summary: instead of separate functions based on eq, eql, and equal, there is essentially just one function, which is based on equal; the eq and eql variants are logically just the equal variant. For example, member-eq and member are macros that generate corresponding calls of member-equal in the logic, although in raw Lisp they will execute using tests eq and eql, respectively. References to any of these in logical contexts such as theories are now references to the function based on equal; for example, the hint :in-theory (disable member) is completely equivalent to the hint :in-theory (disable member-equal). Distributed books have been modified as necessary to accommodate this change. While the need for such changes was relatively infrequent, changes were for example needed in contexts where terms are manipulated directly; for example, defevaluator needs to mention member-equal rather than member, just as it was already the case to mention, say, binary-append rather than append. Again, see equality-variants for more information about equality variants.

    A few improvements were made in support of the modified treatment of equality variants discussed above. The changes include the following.

    o We now allow the use of macro aliases (see macro-aliases-table in :trigger-fns of rules (see rule-classes).

    o We now remove so-called ``guard holders'' (including calls of return-last, hence of mbe) in :trigger-terms of rules.

    o We also remove guard holders in formulas of :congruence and type-prescription rules.

    o Macros union-eq and intersection-eq can now take any positive number of arguments, and union-eq can take zero arguments. (Thanks to Jared Davis for requesting this enhancement.) The same can be said for new macros union$ and intersection$, respectively.

    o A few changes were made to built-in theorems from source file axioms.lisp, in particular disabling :type-prescription rule consp-assoc-equal (formerly two enabled rules, consp-assoc-eq and consp-assoc) but adding this theorem as a :forward-chaining rule, and similarly for true-list-listp-forward-to-true-listp-assoc-equal (and eliminating rule true-list-listp-forward-to-true-listp-assoc-eq; and disabling rule true-listp-cadr-assoc-eq-for-open-channels-p. Also, theorem all-boundp-preserves-assoc has been renamed to all-boundp-preserves-assoc-equal and also strengthened.

    o Some guards were slightly improved (logically weaker or the same).

    Improved get-output-stream-string$ to allow for a context and to do genuine error printing instead of using cw. See io.

    Added the symbols flet and with-local-stobj to *acl2-exports*.

    A small change was made to the processing of more than one :guard declaration for the same function. In particular, a guard of t is essentially ignored.

    Attachments are now allowed during evaluation of the first argument of prog2$ even in contexts (such as proofs) during which the use of attachments is normally prohibited. More generally, the second of the three arguments of return-last has this property, except in the case of mbe (or related macros like mbe1), where the exec argument may provide the value. Thanks to Sol Swords for useful discussions leading us to implement this enhancement.

    The restriction has been removed that prohibited the use of mbe inside encapsulate events with a non-empty signature. This restriction was introduced in Version 3.4, but has not been necessary since Version 4.0, when we first started disallowing guard verification for functions introduced non-locally inside such encapsulate events.

    We weakened the checks involving common ancestors for evaluator and meta (and clause-processor) functions (see evaluator-restrictions) so that except in the mbe case, the next-to-last argument of return-last is not considered. Thanks to Sol Swords for bringing this issue to our attention.

    The macro append no longer requires at least two arguments. Thanks to Dave Greve for requesting this enhancement.

    (Mostly for system hackers) Now, break-on-error breaks at a more appropriate (earlier) time for certain system functions that do not return state, such as translate11. Thanks to David Rager for requesting this improvement.

    Show-accumulated-persistence may take a new argument, :runes, which simply causes an alphabetical list of runes to be printed out.

    Improved trace$ so that :entry, :exit, and :cond forms may reference state even if the function being traced does not include state as a formal.

    The system function acl2x-expansion-alist now takes a second argument, namely state. This change allows for more flexibility in the sorts of attachments that can be made to this function (see defattach). Thanks to Jared Davis and Sol Swords for requesting this enhancement and providing a preliminary implementation.

    An obscure proof-checker change, unlikely to affect users, replaces the state global variables erp, val, print-macroexpansion-flg, and print-prompt-and-instr-flg by pc-erp, pc-val, pc-print-macroexpansion-flg, and pc-print-prompt-and-instr-flg, respectively.

    State globals fmt-hard-right-margin and fmt-soft-right-margin are now untouchable (see set-fmt-hard-right-margin and see push-untouchable). If you bind these state globals with state-global-let*, then you will need to do so with appropriate setters to restore their values, for example as follows.

      (state-global-let*
       ((fmt-hard-right-margin 500 set-fmt-hard-right-margin)
        (fmt-soft-right-margin 480 set-fmt-soft-right-margin))
       ...)
    

    The error message has been improved for the case of evaluating an undefined function that has an attachment (see defattach). Thanks to Jared Davis for sending the following example, which illustrates the additional part of the message.

      ACL2 !>(defstub foo (x) t)
      [[... output omitted ...]]
      ACL2 !>(defattach foo identity)
      [[... output omitted ...]]
      ACL2 !>(defconst *x* (foo 3))
    
    
      ACL2 Error in ( DEFCONST *X* ...):  ACL2 cannot ev the call of undefined
      function FOO on argument list:
    
      (3)
    
      Note that because of logical considerations, attachments (including
      IDENTITY) must not be called in this context.
    
      [[... additional output omitted ...]]
    

    The directory string supplied to add-include-book-dir no longer must terminate with the `/' character, as had been required in some Lisp implementations. Thanks to Sol Swords for bringing this issue to our attention.

    We no longer print induction schemes with gag-mode; use :pso if you want to see them. Thanks to Dave Greve for this suggestion.

    It is now legal to supply a constant for a stobj array dimension. See defstobj. Thanks to Warren Hunt for requesting this enhancement.

    We cleaned up a few issues with defpkg.

    o It is no longer illegal to submit a defpkg form in raw-mode (see set-raw-mode). Thanks to Jun Sawada for reporting an example in which an include-book form submitted in raw-mode caused an error because of a `hidden' defpkg form (see hidden-defpkg). There will no longer be an error in such cases.

    o It had been the case that locally including a book could make it possible to use a package defined by that book. Consider for example the following book, foo.lisp.

    (in-package "ACL2")
    (local (include-book "arithmetic/top" :dir :system))
    
    After certifying this book, it had been possible to admit the following events in a new session.
    (include-book "foo")
    (defconst acl2-asg::*foo* 3)
    (defconst *c* 'acl2-asg::xyz)
    
    In Version_4.3, neither of these defconst events is admitted.

    o A hard Lisp error is now avoided that had been possible in rare cases when including books with hidden packages (see hidden-defpkg). An example may be found in a comment in the deflabel for note-4-3 (in ACL2 source file ld.lisp).

    The undocumented (but sometimes useful) functions packn1 and packn are now guard-verified :logic mode functions. Thanks to Sandip Ray for requesting this enhancement.

    It had been the case that when including a book, functions defined in the book's certification world (that is, in its portcullis commands) were typically not given compiled code. That has been fixed.

    The commands :pl and :pl2 have been improved, primarily by printing information for more rule classes. See pl and see pl2. See also the item below about the new proof-checker command, show-type-prescriptions.

    NEW FEATURES

    New macros mv?-let and mv? extend the funtionality of mv-let and mv (respectively) to the case of a single value.

    Macro with-local-state is available for system programmers who wish bind state locally, essentially using with-local-stobj. But this should only be done with extreme care, and it requires an active trust tag; see with-local-state.

    Formatted printing functions now have analogues that print to strings and do not take an output channel or state as arguments. See printing-to-strings.

    The system function ancestors-check is now available for verified modification by users, i.e., attachment using (defattach ancestors-check <your_function>). Thanks to Robert Krug for providing the necessary proof support, which we modified only in small ways.

    New macros, observation-cw and warning$-cw, provide formatted printing of observations and warnings (respectively) without state. Thanks to Harsh Raju Chamarthi and David Rager for requests leading to these utilities. Observation-cw is now used in some of the distributed books (thanks to Robert Krug for useful interaction for that).

    The proof-checker command type-alist (see proof-checker-commands) now takes an optional third argument that causes the production of forward-chaining reports (see forward-chaining-reports). Thanks to Dave Greve for requesting such an enhancement.

    The reports generated by forward-chaining, forward-chaining-reports, have been changed to indicate when a conclusion reached by forward chaining is REDUNDANT with respect to the type information already known. Thanks to Dave Greve for suggesting this enhancement.

    The utility with-prover-time-limit is now legal for events (see embedded-event-form). For example, the following is now legal.

    (encapsulate
     ()
     (with-prover-time-limit
      2
      (defthm append-assoc
        (equal (append (append x y) z)
               (append x (append y z))))))
    

    The new utility with-prover-step-limit is analogous to the utility with-prover-time-limit, but counts ``prover steps'' rather than checking for time elapsed. See with-prover-step-limit. Also see set-prover-step-limit to provide a default step-limit. Note that just as with-prover-time-limit may now be used to create events, as discussed just above, with-prover-step-limit may also be used to create events. Thanks to Carl Eastlund for requesting support for step-limits.

    The macro progn$ is analogous to prog2$, but allows an arbitrary number of arguments. For example:

    ACL2 !>:trans1 (progn$ (f1 x) (f2 x) (f3 x))
     (PROG2$ (F1 X) (PROG2$ (F2 X) (F3 X)))
    ACL2 !>
    
    Thanks to David Rager for contributing this macro.

    The macro defattach may now be supplied the argument :skip-checks :cycles. In this case, as with argument :skip-checks t, a trust tag is reuired (see defttag), and no logical claims are made. The effect is to avoid the usual check that the extended ancestor relation has no cycles (see defattach). Thanks to Dave Greve for requesting this feature.

    You can now limit the printing of subgoal names when using :set-gag-mode :goals. See set-print-clause-ids. Thanks to Karl Hoech for a suggestion leading to this enhancement.

    A new proof-checker command, show-type-prescriptions, or st for short, provides information about :type-prescription rules that match a given term. Thanks to Dave Greve for requesting this enhancement. See also the item above about related improvements to commands :pl and :pl2.

    HEURISTIC IMPROVEMENTS

    ACL2 now avoids some repeated attempts to rewrite hypotheses of rewrite rules. See set-rw-cache-state for a discussion of this behavior and how to avoid it. The default behavior has been observed to reduce by 11% the overall time required to complete a regression. Here are the directories that had the top three time decreases and top three time increases, shown in seconds.

      -368 coi/gacc (1064 down to 696: decrease of 35%)
      -220 workshops/1999/ste (664 down to 444: decrease of 33%)
      -148 unicode (331 down to 183: decrease of 45%)
      ....
        +7 workshops/2002/cowles-flat/support (229 up to 236: increase of 3%)
        +8 workshops/1999/ivy/ivy-v2/ivy-sources (508 up to 516: increase of 2%)
       +12 workshops/2009/hardin/deque-stobj (78 up to 91: increase of 17%)
    

    The so-called ``ancestors check,'' which is used to limit backchaining, has been strengthened so that two calls of equal are considered the same even if their arguments appear in the opposite order. Thanks to Robert Krug for providing an implementation and a useful discussion.

    The check for irrelevant-formals in processing of defuns has been made more efficient. Thanks to Eric Smith for reporting this issue in 2001 (!) and thanks to Warren Hunt for recently sending an example. For that example, we have seen the time for the irrelevant-formals check reduced from about 10 seconds to about 0.04 seconds.

    (GCL only) The macro mv has been modified so that certain fixnum boxing can be avoided.

    (Allegro CL only) We have set to nil four Allegro CL variables that otherwise enable storing of certain source information (for details, see the discussion of ``cross-referencing'' in ACL2 source file acl2-init.lisp). As a result of this change we have about a 6% speedup on the regression suite, but a 27% time reduction on an example that includes a lot of books.

    Exhaustive matching for the case of free-variables has been extended to type-prescription rules, in analogy to the default setting :match-free :all already in place for rewrite, linear, and forward-chaining rules. See free-variables-type-prescription. Thanks to Dave Greve for requesting this enhancement.

    BUG FIXES

    A soundness bug was fixed in some raw-Lisp code implementing the function, take. Thanks to Sol Swords for pointing out this bug with (essentially) the following proof of nil.

    (defthmd take-1-nil-logic
      (equal (take 1 nil) '(nil))
      :hints(("Goal" :in-theory (disable (take)))))
    (thm nil :hints (("Goal" :use take-1-nil-logic)))
    

    Calls of mbe in ``safe-mode'' situations -- i.e., during evaluation of defconst, value-triple, and defpkg forms, and during macroexpansion -- are now guard-checked. Thus, in these situations both the :logic and :exec forms will be evaluated, with an error if the results are not equal. Formerly, only the :logic form was evaluated, which was a soundness bug that could be exploited to prove nil. For a such a proof and a bit of further explanation, see the example at the top of the comments for (deflabel note-4-3 ..) in ACL2 source file ld.lisp.

    It had been possible to prove nil by proving the following theorem using ACL2 built on CCL and then proving its negation using ACL2 built on a different host Lisp.

    (defthm host-lisp-is-ccl
      (equal (cdr (assoc 'host-lisp *initial-global-table*))
             :ccl)
      :rule-classes nil)
    
    This hole has been plugged by moving the setting of 'host-lisp out of the constant *initial-global-table*.

    Fixed trace$ for arguments that are stobj accessors or updaters. It also gives an informative error in this case when the accessor or updater is a macro (because the introducing defstobj event specified :inline t).

    Avoided a potential error that could occur when no user home directory is located. Our previous solution for Windows simply avoided looking for ACL2 customization files (see acl2-customization) and acl2-init.lsp files in a user's home directory. With this change, we handle such files the same for Windows as for non-Windows systems: we always look for ACL2 customization files (see acl2-customization) and acl2-init.lsp files in a user's home directory, but only if such a directory exists. Thanks to Hanbing Liu for reporting this issue.

    (GCL only) Fixed a bug that prevented the use of get-output-stream-string$ when the host Lisp is GCL.

    Fixed with-live-state to work properly for executable counterparts (so-called ``*1*'' functions).

    Fixed a bug in the error message caused by violating the guard of a macro call.

    Fixed a bug in an error message that one could get when calling defattach with argument :skip-checks t to attach to a :program mode function symbol that was introduced with defun. (This is indeed an error, but the message was confusing.) Thanks to Robert Krug for bringing this bug to our attention.

    Fixed a bug in the loop-checking done on behalf of defattach, which could miss a loop. For an example, see the comment about loop-checking in the comments for (deflabel note-4-3 ..) in ACL2 source file ld.lisp.

    Terms of the form (hide <term>) without free variables could be simplified, contrary to the purpose of hide. This is no longer the case, Thanks to Dave Greve for reporting this issue.

    An infinite loop could occur when an error was encountered in a call of wormhole-eval, for example with the following form, and this has been fixed.

    (wormhole-eval 'demo
                   '(lambda ()
                      (er hard 'my-top "Got an error!"))
                   nil)
    

    Fixed a bug in detection of package redefinition. While we have no example demonstrating this as a soundness bug, we cannot rule it out.

    Fixed a bug in the message produced by an erroneous call of flet. Thanks to Jared Davis for reporting this bug and sending a helpful example.

    For a failed defaxiom or defthm event, we now avoid printing runes that are used only in processing proposed rules to be stored, but not in the proof itself. Thanks to Dave Greve for sending us an example that led us to make this fix.

    ACL2 did not reliably enforce the restriction against non-local include-book events inside encapsulate events, as illustrated by the following examples.

    ; not permitted (as expected)
    (encapsulate () (include-book "foo"))
    
    ; permitted (as expected)
    (encapsulate () (local (include-book "foo")))
    
    ; formerly permitted (surprisingly); now, not permitted
    (local (encapsulate () (include-book "foo")))
    
    Moreover, the corresponding error message has been fixed. Thanks to Jared Davis and Sandip Ray for relevant discussions.

    When include-book is given a first argument that is not a string, a more graceful error now occurs, where previously an ugly raw Lisp error had occurred. Thanks to Eric Smith for bringing this bug to our attention.

    Fixed a bug in an error message that was printed when an unexpected expression has occurred where a declare form is expected.

    (Since all functions are compiled when the host Lisp is CCL or SBCL, the following bug fix did not occur for those host Lisps.) After evaluation of (set-compile-fns t), all defined functions are expected to run with compiled code; but this was not the case for functions exported from an encapsulate event. This has been fixed.

    It had been the case that the :puff command was broken for include-book form whose book had been certified in a world with an add-include-book-dir event. This has been fixed.

    Evaluation of stobj updaters (see defstobj) may no longer use attachments (see defattach). This is a subtle point that will likely not affect many users. Thanks to Jared Davis for bringing this issue to our attention; a slight variant of his example appears in a comment in ACL2 source function oneify-cltl-code.

    It had been the case that even when a stobj creator function was declared to be untouchable (see push-untouchable), a with-local-stobj form based on that same stobj was permitted. Now, such forms are not admitted. Thanks to Jared Davis for a query leading to this fix.

    Fixed a buggy message upon guard violations, which was suggesting the use of (set-guard-checking :none) in some cases when guard-checking was already set to :none.

    It had been possible to get a hard Lisp error when computing with ec-call in books. The following is an example of such a book, whose certification no longer causes an error.

    (in-package "ACL2")
    (defun f (x) x)
    (defconst *c* (ec-call (f 3)))
    (defun g (x) (cons x x))
    

    The command :pl2, and also the proof-checker commands rewrite and show-rewrites (and hence their respective aliases r and sr), now take rule-id arguments that can be :definition runes. These commands dealt with definition rules already, e.g.

    :pl2 (append x y) binary-append
    
    but they did not allow explicit specification of :definition runes, e.g.:
    :pl2 (append x y) (:definition binary-append)
    

    The following example illustrates a bug in the processing of (admittedly obscure) hints of the form :do-not-induct name, where name is not t, :otf-flg-override, :otf, or nil. In this example, ACL2 had essentially ignored the hint and reverted to prove the original goal by induction, rather than to skip the goal temporarily as is expected for such hints. Thanks to David Rager for a helpful discussion.

    (thm (and (equal (append (append x y) z) (append x y z))
              (equal (append (append x2 y2) z2) (append x2 y2 z2)))
         :hints (("Subgoal 1" :do-not-induct some-name)))
    

    Fixed a slight bug in the definitions of built-in theories. For example, in a fresh ACL2 session the value of the following form is nil, but formerly included several :definition runes.

    (let ((world (w state)))
      (set-difference-theories (function-theory :here)
                               (function-theory 'ground-zero)))
    

    CHANGES AT THE SYSTEM LEVEL AND TO DISTRIBUTED BOOKS

    Many changes have been made to the distributed books, as recorded in svn logs under the `Source' and 'Updates' links at http://acl2-books.googlecode.com/. Here we list some of the more significant changes.

    o A large library has been graciously contributed by the formal verification group at Centaur Technology. See books/centaur/ and, in particular, file books/centaur/README, which explains how the library depends on the experimental HONS extension (see hons-and-memoization).

    o Among the new books is an illustration of defattach, books/misc/defattach-example.lisp, as well as a variant of defattach that avoids the need for guard verification, books/misc/defattach-bang.lisp.

    o Distributed book books/misc/trace1.lisp has been deleted. It had provided slightly more friendly trace output for new users, but distributed book books/misc/trace-star.lisp may be better suited for that purpose.

    ACL2 can once again be built on LispWorks (i.e., as the host Lisp), at least with LispWorks 6.0. Thanks to David Rager for useful conversations. Several changes have been made from previous LispWorks-based ACL2 executables:
    o ACL2 now starts up in its read-eval-print loop.
    o You can save an image with save-exec.
    o Multiprocessing is not enabled.
    o The stack size is managed using a LispWorks variable that causes the stack to grow as needed.
    o When ACL2 is built a script file is written, as is done for other host Lisps. Thus, (assuming that no PREFIX is specified), saved_acl2 is just a small text file that invokes a binary executable, which for Lispworks is saved_acl2.lw.

    The HTML documentation no longer has extra newlines in <pre> environments.

    Statistics on ACL2 code size may be found in distributed file doc/acl2-code-size.txt. This file and other information can be found in a new documentation topic, about-acl2.

    Fixed the build process to pay attention to environment variable ACL2_SYSTEM_BOOKS (which may be supplied as a command-line argument to `make'). An ACL2 executable can thus now be built even when there is no books/ subdirectory if a suitable replacement directory is supplied.

    Some warnings from the host Lisp are now suppressed that could formerly appear. For example, the warnings shown below occurs in Version 4.2 using Allegro CL, but not in Version 4.3.

    ACL2 !>(progn (set-ignore-ok t)
                  (set-irrelevant-formals-ok t)
                  (defun bar (x y)
                    x))
    [[.. output omitted ..]]
     BAR
    ACL2 !>:comp bar
    ; While compiling BAR:
    Warning: Variable Y is never used.
    ; While compiling (LABELS ACL2_*1*_ACL2::BAR ACL2_*1*_ACL2::BAR):
    Warning: Variable Y is never used.
     BAR
    ACL2 !>
    

    EMACS SUPPORT

    The distributed Emacs file emacs/emacs-acl2.el now indents calls of er@par and warning$@par the same way that calls of defun are indented.

    EXPERIMENTAL VERSIONS

    The parallel version (see parallelism) now supports parallel evaluation of the ``waterfall'' part of the ACL2 prover; see set-waterfall-parallelism. Thanks to David Rager for doing the primary design and implementation work.

    A new macro, spec-mv-let, supports speculative and parallel execution in the parallel version, courtesy of David Rager.

    Among the enhancements for the HONS version (see hons-and-memoization) are the following.

    Memoized functions may now be traced (see trace$). Thanks to Sol Swords for requesting this enhancement.

    Memoize-summary and clear-memoize-statistics are now :logic mode functions that return nil. Thanks to Sol Swords for this enhancement.

    Memoize is now explicitly illegal for constrained functions. (Already such memoization was ineffective.)

    A new keyword argument, :AOKP, controls whether or not to allow memoization to take advantage of attachments; see memoize and for relevant background, see defattach.

    Memoize is now illegal by default for :logic mode functions that have not had their guards verified. See memoize (keyword :ideal-okp) and see acl2-defaults-table (key :memoize-ideal-okp) for and explanation of this restriction and how to avoid it.

    History commands such as :pe and :pbt now display ``M'' or ``m'' to indicate memoized functions. See pc.




    acl2-sources/doc/HTML/NOTE-4-3_lparen_R_rparen_.html0000664002132200015000000000100412222333527021344 0ustar kaufmannacl2 NOTE-4-3_lparen_R_rparen_.html -- ACL2 Version 6.3

    NOTE-4-3(R)

    ACL2 Version 4.3(r) (July, 2011) Notes
    Major Section:  RELEASE-NOTES
    

    Please see note-4-3 for changes in Version 4.3 of ACL2.




    acl2-sources/doc/HTML/NOTE-5-0.html0000664002132200015000000015757012222333527016036 0ustar kaufmannacl2 NOTE-5-0.html -- ACL2 Version 6.3

    NOTE-5-0

    ACL2 Version 5.0 (August, 2012) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 4.3 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level and to distributed books, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    NOTE: ACL2 is now distributed under Version 2 of the GNU General Public License. [Added later: The license has changed since Version_5.0. See LICENSE.] Formerly, any later version had been acceptable. Moreover, books are no longer distributed from a University of Texas website, but rather, from Google Code at http://code.google.com/p/acl2-books/downloads/.

    CHANGES TO EXISTING FEATURES

    A fatal error now occurs if environment variable ACL2_CUSTOMIZATION has a value other than NONE or the empty string, but is not the name of an existing file. Thanks to Harsh Raju Chamarthi for requesting such a change.

    Functions read-acl2-oracle (and read-acl2-oracle@par), read-run-time, and main-timer are no longer untouchable (see remove-untouchable).

    We now avoid certain duplicate conjuncts in the constraint stored for encapsulate events. For example, the constraint stored for the following event formerly included (EQUAL (FOOP (CONS X Y)) (FOOP Y)) and (BOOLEANP (FOOP X)) twice each, but no more.

    (encapsulate
     ((foop (x) t))
     (local (defun foop (x) (declare (ignore x)) t))
     (defthm foop-constraints
       (and (booleanp (foop x))
            (equal (foop (cons x y)) (foop y)))
       :rule-classes
       ((:type-prescription :corollary (booleanp (foop x)))
        (:rewrite :corollary (equal (foop (cons x y)) (foop y))))))
    

    The :guard for a constrained function (see signature) may now mention function symbols introduced in the same encapsulate event that introduces that function. Thanks to Nathan Wetzler for a helpful discussion leading to this improvement.

    The test for redundancy (see redundant-events) of encapsulate events has been improved in cases involving redefinition (see ld-redefinition-action). Thanks to Jared Davis for providing the following example, which illustrates the problem.

      (redef!)
    
      (encapsulate ()
        (defun g (x)
          (+ 3 x)))
    
      (g 0) ; 3, as expected
    
      (encapsulate ()
        (defun g (x)
          (+ 4 x)))
    
      (g 0) ; 4, as expected
    
      ; Unfortunately, the following was flagged as redundant because it agreed
      ; with the first encapsulate above.  That has been fixed; now, it is
      ; recognized as not being redundant.
      (encapsulate ()
        (defun g (x)
          (+ 3 x)))
    

    The test for redundancy of defun and defconst events has been improved in the case that redefinition is active. In that case, redundancy now additionally requires that the ``translated'' body is unchanged, i.e., even after expanding macro calls and replacing constants (defined by defconst) with their values. Thanks to Sol Swords for requesting this enhancement, and to Jared Davis for pointing out a bug in a preliminary change. See redundant-events, in particular the ``Note About Unfortunate Redundancies''. Note that this additional requirement was already in force for redundancy of defmacro events.

    The macro defmacro-last and the table return-last-table have been modified so that when they give special treatment to a macro mac and its raw Lisp counterpart mac-raw, a call (return-last 'mac-raw ...) can be made illegal when encountered directly in the top level loop, as opposed to inside a function body. See return-last. Thanks to Harsh Raju Chamarthi for showing us an example that led us to make this improvement.

    We removed a barrier to admitting function definitions, as we explain using the following example.

    (defun foo (m state)
      (declare (xargs :stobjs state))
      (if (consp m)
          (let ((state (f-put-global 'last-m m state)))
            (foo (cdr m) state))
        state))
    
    Previously, ACL2 complained that it could not determine the outputs of the LET form, as is necessary in order to ensure that STATE is returned by it. ACL2 now works harder to solve this problem as well as the analogous problem for MV-LET and, more generally for mutual-recursion. (The main idea is to reverse the order of processing the IF branches if necessary.) We thank Sol Swords for contributing a version of the above example and requesting this improvement.

    It is no longer the case that break-on-error causes a Lisp break when encountering an error during translation of user input into internal (translated) form (see term). The reason is that an improvement to the translation process, specifically the one described in the preceding paragraph, allows certain backtracking from ``errors'', which are intended to be silent rather than causing breaks into raw Lisp. Thanks to Jared Davis for sending an example leading to this change.

    (CCL and SBCL only) When the host Lisp is CCL or SBCL, then since all functions are compiled, a certify-book command will no longer load the newly-compiled file (and similarly for include-book with argument :load-compiled-file :comp).

    Set-write-acl2x now returns an error triple and can take more values, some of which automatically allow including uncertified books when certify-book is called with argument :acl2x t.

    The environment variable COMPILE_FLG has been renamed ACL2_COMPILE_FLG; see certify-book.

    The macros defthmd and defund no longer return an error triple with value :SKIPPED when proofs are being skipped. Rather, the value returned is the same as would be returned on success when proofs are not skipped.

    For those who use set-write-acl2x: now, when certify-book is called without a :ttagsx argument supplied, then the value of :ttagsx defaults to the (explicit or default) value of the :ttags argument.

    The :pl and :pl2 commands can now accept terms that had previously been rejected. For example, the command :pl (member a (append x y)) had caused an error, but now it works as one might reasonably expect, treating member as member-equal (see equality-variants for relevant background). Thanks to Jared Davis for reporting this problem by sending the above example.

    We have eliminated some hypotheses in built-in rewrite rules characterp-nth and ordered-symbol-alistp-delete-assoc-eq.

    Added the symbols f-get-global, f-put-global, and state-global-let* to *acl2-exports*.

    Added to the guards of push-untouchable and remove-untouchable the requirement that the second argument must be a Boolean. Thanks to Jared Davis for sending an example that led to this change.

    The built-in function string-for-tilde-@-clause-id-phrase has been put into :logic mode and had its guards verified, as have some subsidiary functions. A few new rules have been added in support of this work; search for string-for-tilde-@-clause-id-phrase in ACL2 source file boot-strap-pass-2.lisp if interested. Thanks to David Rager for contributing an initial version of this improvement.

    All trust tags are now in the keyword package. The defttag event may still take a symbol in an arbitrary package, but the trust tag created will be in the keyword package (with the same symbol-name as the symbol provided). Similarly, non-nil symbols occurring in the :ttags argument of an include-book or certify-book command will be converted to corresponding keywords. See defttag.

    There have been several changes to gag-mode. It is now is initially set to :goals, suppressing most proof commentary other than key checkpoints; see set-gag-mode. (As before, see pso for how to recover the proof output.) Also, top-level induction schemes are once again printed when gag-mode is on, though these as well as printing of guard conjectures can be abbreviated (``eviscerated'') with a new evisc-tuple; see set-evisc-tuple, in particular the discussion there of :GAG-MODE. Finally, the commentary printed within gag-mode that is related to forcing-rounds is now less verbose. Thanks to Dave Greve and David Rager for discussions leading to the change in the printing of induction schemes under gag-mode; thanks to Warren Hunt for an email that led us to similar handling for printing of guard conjectures; and thanks to Robert Krug for a suggestion that led us to restore, in abbreviated form, important information about the sources of forcing round goals.

    An error now occurs if ld is called while loading a compiled book. See calling-ld-in-bad-contexts. Thanks to David Rager for reporting a low-level assertion failure that led us to make this change.

    The proof-checker interactive loop is more robust: most errors will leave you in that loop, rather than kicking you out of the proof-checker and thus back to the main ACL2 read-eval-print loop. Thanks to David Hardin for suggesting this improvement in the case of errors arising from extra right parentheses.

    The summary at the end of a proof now prints the following note when appropriate:

    [NOTE: A goal of NIL was generated.  See :DOC nil-goal.]
    
    See nil-goal.

    Improved dmr to show the function being called in the case of explicit evaluation: ``(EV-FNCALL function-being-called)''.

    It is now permitted to bind any number of stobjs to themselves in the bindings of a LET expression. But if any stobj is bound to other than itself in LET bindings, then there still must be only one binding in that LET expression. The analogous relaxation holds for LAMBDA expressions. Thanks to Sol Swords for requesting such a change, which was needed for some code generated by macro calls.

    The macro top-level now returns without error; See top-level. Formerly, this macro always returned an error triple (mv t .. state), which meant that normal calls of ld would stop after encountering a call of top-level. Thanks to Jared Davis for bringing this issue to our attention.

    It is no longer the case that when you specify xargs keyword :non-executable t in a defun form rather than using defun-nx, then the form of the body need match only the shape (prog2$ (throw-nonexec-error ... ...) ...). We now require that the body of the definition of a function symbol, fn, with formals (x1 ... xk), be of the form (prog2$ (throw-nonexec-error 'fn (list x1 ... xk)) ...). This fixes the following odd behavior, which could be considered a bug. Consider a book that contains the following two events.

    (defun foo (x)
      (declare (xargs :guard t :non-executable t :mode :logic))
      (prog2$ (throw-nonexec-error 'bar (list x))
              (cons 3 x)))
    (defn h (x)
      (foo x))
    
    After certifying this book and then including it in a new session, the behavior occurred that is displayed below; notice the mention of BAR. However, if the two forms were submitted directly in the loop, then the error message had mentioned FOO instead of BAR. This discrepancy has been eliminated, by rejecting the proposed definition of foo because the name in the first argument of throw-nonexec-error was 'bar where now it must be 'foo.
    ACL2 !>(h 3)
    
    
    ACL2 Error in TOP-LEVEL:  ACL2 cannot ev the call of undefined function
    BAR on argument list:
    
    (3)
    
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.
    
    ACL2 !>
    

    A tautology checker used in the ACL2 sources (function if-tautologyp) has been limited somewhat in the effort it makes to recognize a tautology. While we expect it to be rare for the effect of this change to be noticeable, we thank Sol Swords for sending us an example that motivated this change: a guard verification that took about 5 seconds in Version_4.3 now takes, on the same machine, about 0.07 seconds.

    The behavior of backquote (`) has been changed slightly to be compatible with its behavior in raw Lisp. The change is to allow the use of comma-atsign (,@) at the end of a list, as in the following example.

    (let ((x 3) (y 2) (z 7)) `(,x ,y ,@z))
    
    Formerly, evaluation of this form had caused a guard violation in the ACL2 loop unless guard-checking was off (i.e., set-guard-checking was invoked with nil or :none), in which case it returned (3 2). But we observed evaluation of this form to return (3 2 . 7) in every host Lisp on which ACL2 runs (Allegro CL, CCL, CLISP, CMUCL, GCL, LispWorks, and SBCL). Now, ACL2 behaves like these Lisps.

    A call of the theory macro had previously returned nil when applied to other than the name of name of a previously executed deftheory event. Now, a hard error occurs.

    The table binop-table has been replaced by the table untrans-table. However, add-binop and remove-binop continue to have the same effect as before. See add-macro-fn, which is a new feature discussed below.

    The function booleanp is now defined using eq instead of equal, which may increase its efficiency. Thanks to Jared Davis for this change.

    For pairs (key . val) in the macro-aliases-table, there had been a requirement that val is a known function symbol. Now, it only needs to be a symbol. (This change was made to support the new feature, defun-inline, described elsewhere in these release notes.)

    NEW FEATURES

    A new ``tau system'' provides a kind of ``type checker.'' See tau-system. Thanks to Dave Greve for supplying a motivating example (on which this system can provide significant speedup), and to Sol Swords for sending a very helpful bug report on a preliminary implementation.

    Users may now arrange for additional summary information to be printed at the end of events. [Note added at Version_6.1: Formerly we pointed here to print-summary-user, but now, see finalize-event-user; also see note-6-1]. Thanks to Harsh Raju Chamarthi for requesting this feature and participating in a design discussion.

    A new, advanced proof-checker command, geneqv, shows the generated equivalence relation at the current subterm. Thanks to Dave Greve for an inquiry leading to this enhancement.

    A new reader macro, #u, permits the use of underscore characters in a number. See sharp-u-reader. Thanks to Jared Davis for requesting this capability.

    New proof-checker commands pl and pr provide interfaces to the ACL2 commands :pl and :pr, respectively. These can be useful if you want to see trivially-proved hypotheses, as now clarified in the proof-checker documentation for its show-rewrites command. See proof-checker-commands. Thanks to Pete Manolios for suggesting such clarification and capability.

    It is now legal to call non-executable functions without the usual signature restrictions imposed on executable code. For example, the third event below was not admissible, but now it is.

    (defstobj foo fld)
    (defun-nx id (x)
      x)
    (defun f (foo)
      (declare (xargs :stobjs foo :verify-guards nil))
      (cons 3 (id foo)))
    
    Thanks to Jared Davis for requesting this enhancement, in particular for calling non-executable functions in the :logic part of an mbe call. Here is Jared's example, which is admissible now but formerly was not.
    (defstobj foo (fld))
    (defun-nx my-identity (x) x)
    (defun my-fld (foo)
      (declare (xargs :stobjs foo))
      (mbe :logic (my-identity foo)
           :exec (let ((val (fld foo)))
                   (update-fld val foo))))
    

    A new macro, non-exec, allows the use of non-executable code, for example inside ordinary function definitions. Thanks to Sol Swords for requesting this enhancement.

    A new ``provisional certification'' process is supported that can allow books to be certified before their included sub-books have been certified, thus allowing for potentially much greater `make'-level parallelism. See provisional-certification. Thanks to Jared Davis for requesting this feature and for helpful discussions, based in part on rudimentary provisional certification schemes that he developed first at Rockwell Collins and later for his `Milawa' project. Also, thanks to Jared and to Sol Swords for testing this feature and for providing a fix for a bug in a preliminary implementation, and thanks to Sol for providing performance feedback and a crucial suggestion that led to an improved implementation.

    Event summaries now show the names of events that were mentioned in hints of type :use, :by, or :clause-processor. See set-inhibited-summary-types. Thanks to Francisco J. Martin Mateos for requesting such an enhancement (actually thanks to the community, as his request is the most recent but this has come up from time to time before).

    ACL2 now stores a data structure representing the relation ``Event A is used in the proof of Event B.'' See dead-events, which explains this data structure and mentions one application: to identify dead code and unused theorems. Thanks to Shilpi Goel for requesting such a feature and for helpful feedback.

    A new documentation topic provides a guide to programming with state; see programming-with-state. Thanks to Sarah Weissman for suggesting that such a guide might be useful, and to David Rager for helpful feedback on a preliminary version. There also has been some corresponding reorganization of the documentation as well as creation of additional documentation (e.g., see state-global-let*). Now, most built-in functions and macros commonly used in programs (as opposed to events like defun, for example) are subtopics of a new topic -- see acl2-built-ins -- which is a subtopic of programming, a topic that in turn has considerably fewer direct subtopics than before.

    It is now possible to bind extra variables in a :USE hint, thus avoiding the error message: ``The formula you wish to instantiate, ..., mentions only the variable(s) ...''. See lemma-instance, in particular the discussion of keyword :extra-bindings-ok. Thanks to Sol Swords for requesting such an enhancement.

    The function read-object-suppress is like read-object except that it avoids errors and discards the value read. See io.

    A stobj may now be passed as an argument where another stobj is expected if the two are ``congruent''. See defstobj, in particular, its discussion of the new :congruent-to keyword of defstobj. Thanks to Sol Swords for requesting this enhancement and for useful discussions contributing to its design.

    A new top-level utility has been provided that shows the assembly language for a defined function symbol; see disassemble$. Thanks to Jared Davis for requesting such a utility and to Shilpi Goel for pointing out an inconvenience with the initial implementation. Note that it uses the distributed book books/misc/disassemble.lisp, which users are welcome to modify (see http://www.cs.utexas.edu/users/moore/acl2/).

    The macro set-accumulated-persistence is an alias for accumulated-persistence. Thanks to Robert Krug for suggesting this addition.

    A new documentation topic lists lesser-known and advanced ACL2 features, intended for those with prior ACL2 experience who wish to extend their knowledge of ACL2 capabilities. See advanced-features. Thanks to Warren Hunt and Anna Slobodova for requesting such information.

    A new macro, deftheory-static, provides a variant of deftheory such that the resulting theory is the same at include-book time as it was at certify-book time. Thanks to Robert Krug for helpful discussions on this new feature and for updating his books/arithmetic-5/ distributed books to use this feature.

    A new event, defabsstobj, provides a new way to introduce single-threaded objects (see stobj and see defstobj). These so-called ``abstract stobjs'' permit user-provided logical definitions for primitive operations on stobjs, for example using an alist-based representation instead of a list-based representation for array fields. Moreover, the proof obligations guarantee that the recognizer is preserved; hence the implementation avoids executing the recognizer, which may be an arbitrarily complex invariant that otherwise would be an expensive part of guard checks. Thanks to Warren Hunt for a request leading us to design and implement this new feature, and thanks to Rob Sumners for a request leading us to implement a related utility, defabsstobj-missing-events. See defabsstobj. Also thanks to Sol Swords for sending an example exhibiting a bug in the initial implementation, which has been fixed.

    A new command, :psof <filename>, is like :pso but directs proof replay output to the specified file. For large proofs, :psof may complete much more quickly than :pso. see psof. More generally, a new utility, wof (an acronym for ``With Output File''), directs standard output and proofs output to a file; see wof.

    The new macro defnd defines a function with :guard t and disables that function, in analogy to how defund defines with defun and then disables. Thanks to Shilpi Goel for requesting this feature.

    The :pl2 command now shows :linear rules; and a new proof-checker command, show-linears (equivalently, sls), is an analogue of the proof-checker show-rewrites (sr) command, but for linear rules. Thanks to Shilpi Goel for requesting this new proof-checker command. Finally, a corresponding new proof-checker command, apply-linear (al), is an analogue of the proof-checker rewrite (r) command, but for linear rules.

    The macros add-macro-fn and remove-macro-fn replace macros add-binop and remove-binop, respectively, though the latter continue to work. The new macros allow you to decide whether or not to display calls of binary macros as flat calls for right-associated arguments, e.g., (append x y z) rather than (append x (append y z)). See add-macro-fn.

    It is now possible to request that the host Lisp compiler inline calls of specified functions, or to direct that the host Lisp compiler not inline such calls. See defun-inline and see defun-notinline. We thank Jared Davis for several extensive, relevant conversations, and for finding a bug in a preliminary implementation. We also thank others who have engaged in discussions with us about inlining for ACL2; besides Jared Davis, we recall such conversations with Rob Sumners, Dave Greve, and Shilpi Goel.

    HEURISTIC IMPROVEMENTS

    Reading of ACL2 arrays (see aref1, see aref2) has been made more efficient (as tested with CCL as the host Lisp) in the case of consecutive repeated reads of the same named array. Thanks to Jared Davis and Sol Swords for contributing this improvement.

    Slightly modified the induction schemes stored, so that calls of so-called ``guard-holders'' (such as mbe and prog2$ -- indeed, any call of return-last -- and the) are expanded away. In particular, calls of equality variants such as member are treated as their corresponding function calls, e.g., member-equal; see equality-variants. Guard-holders are also now expanded away before storing constraints for encapsulate events, which can sometimes result in simpler constraints.

    Improved the performance of dmr (technical note: by modifying raw Lisp code for function dmr-flush, replacing finish-output by force-output).

    We now avoid certain rewriting loops. A long comment about this change, including an example of a loop that no longer occurs, may be found in source function expand-permission-result.

    Slightly strengthened type-set reasoning at the level of literals (i.e., top-level hypotheses and conclusions). See the comment in ACL2 source function rewrite-atm about the ``use of dwp = t'' for an example of a theorem provable only after this change.

    Strengthened the ability of type-set reasoning to make deductions about terms being integers or non-integer rationals. The following example illustrates the enhancement: before the change, no simplification was performed, but after the change, the conclusion simplifies to (foo t). Thanks to Robert Krug for conveying the problem to us and outlining a solution.

    (defstub foo (x) t)
    (thm ; should reduce conclusion to (foo t)
     (implies (and (rationalp x)
                   (rationalp y)
                   (integerp (+ x (* 1/3 y))))
              (foo (integerp (+ y (* 3 x))))))
    

    BUG FIXES

    Fixed a class of soundness bugs involving each of the following functions: getenv$, get-wormhole-status, cpu-core-count, wormhole-p, random$, file-write-date$, and serialize-read-fn, and (for the HONS version of ACL2) clear-memoize-table and clear-memoize-tables as well as (possible soundness bug) serialize-write-fn. For example, we were able to admit the following events, but that is no longer the case (neither for getenv$ as shown, nor analogously for other functions listed above).

    (defthm not-true
      (stringp (cadr (getenv$ "PWD" (build-state))))
      :rule-classes nil)
    
    (defthm contradiction
      nil
      :hints (("Goal"
               :in-theory (disable (getenv$))
               :use not-true))
      :rule-classes nil)
    

    Fixed a soundness bug involving with-live-state, which could cause an error in the use of add-include-book-dir or delete-include-book-dir in a book or its portcullis commands. See with-live-state, as the documentation for this macro has been updated; in particular it is now untouchable (see remove-untouchable) and is intended only for system hackers. Thanks to Jared Davis for reporting a bug in the use of add-include-book-dir after our first attempt at a fix.

    Fixed a soundness bug based on the use of skip-proofs together with the little-used argument k=t for certify-book. An example proof of nil appears in a comment in the ACL2 sources, in (deflabel note-5-0 ...).

    Fixed a soundness bug that allowed users to define new proof-checker primitive commands. Before this fix, a book proving nil could be certified, as shown in a comment now in the introduction of the table pc-command-table in source file proof-checker-a.lisp.

    (Technical change, primarily related to make-event:) Plugged a security hole that allowed books' certificates to be out-of-date with respect to make-event expansions, but not recognized as such. The change is to include the so-called expansion-alist in the certificate's checksum. An example appears in a comment in the ACL2 sources, in (deflabel note-5-0 ...).

    Fixed a bug in guard verification due to expanding calls of primitives when translating user-level terms to internal form, so called ``translated terms'' (see term). While we have not observed a soundness hole due to this bug, we have not ruled it out. Before the bug fix, the following event was admissible, as guard verification succeeded (but clearly should not have).

    (defun f ()
      (declare (xargs :guard t))
      (car (identity 3)))
    
    For those who want details about this bug, we analyze how ACL2 generates guard proof obligations for this example. During that process, it evaluates ground subexpressions. Thus, (identity '3) is first simplified to '3; so a term must be built from the application of car to '3. Guard-checking is always turned on when generating guard proof obligations, so now, ACL2 refuses to simplify (car '3) to 'nil. However, before this bug fix, when ACL2 was building a term by applying car to argument '3, it did so directly without checking guards; source code function cons-term is `smart' that way. After the fix, such term-building reduction is only performed when the primitive's guard is met.

    While calls of many event macros had been prohibited inside executable code, others should have been but were not. For example, the following was formerly allowed.

    (defun foo (state)
      (declare (xargs :mode :program :stobjs state))
      (add-custom-keyword-hint :my-hint (identity nil)))
    (foo state) ; Caused hard raw Lisp error!
    
    Thus, several event macros (including for example add-custom-keyword-hint) may no longer be called inside executable code.

    Fixed an assertion that could occur, for example, after reverting to prove the original goal by induction and generating a goal of NIL. Thanks to Jared Davis for sending us a helpful example to bring this bug to our attention.

    It was possible for defstobj to generate raw Lisp code with excessively restrictive type declarations. This has been fixed. Thanks to Warren Hunt for reporting this bug and sending an example that illustrates it. See stobj-example-2 for examples of such raw Lisp code; now, one finds (and fixnum (integer 0 *)) where formerly the type was restricted to (integer 0 268435455).

    Fixed a bug in that was ignoring the use of :computed-hint-replacement in certain cases involving a combination of computed hints and custom keyword hints. Thanks to Robert Krug for reporting this bug and sending a very helpful example.

    Fixed a bug in the output from defattach, which was failing to list previous events in the message about ``bypassing constraints that have been proved when processing the event(s)''.

    (GCL only) Fixed a bug in set-debugger-enable (which was only a bug in GCL, not an issue for other host Lisps).

    Fixed ACL2 trace output to indent properly for levels above 99 (up to 9999). Thanks to Warren Hunt for bringing this bug to our attention.

    Fixed a bug in the reporting of times in event summaries -- probably one that has been very long-standing! The times reported had often been too small in the case of compound events, notably include-book. Thanks to everyone who reported this problem (we have a record of emails from Eric Smith and Jared Davis on this issue).

    Fixed a bug in :expand hints, where the use of :lambdas could prevent other parts of such a hint. For example, the following invocation of thm failed before this fix was made.

    (defund foo (x) (cons x x))
    (thm (equal (car (foo x)) x)
    :hints (("Goal" :expand (:lambdas (foo x)))))
    

    Certain ``program-only'' function calls will now cause hard Lisp errors. (The rather obscure reason for this fix is to support logical modeling of the ACL2 evaluator. A relevant technical discussion may be found in source function oneify-cltl-code, at the binding of variable fail_program-only-safe.)

    There was an unnecessary restriction that FLET-bound functions must return all stobjs among their inputs. For example, the following definition was rejected because state was not among the outputs of h. This restriction has been removed.

    (defun foo (state)
      (declare (xargs :stobjs state))
      (flet ((h (state) (f-boundp-global 'x state)))
        (h state)))
    

    We fixed a bug, introduced in the preceding release (Version 4.3), in the check for irrelevant formals (see irrelevant-formals). That check had been too lenient in its handling of lambda (LET) expressions, for example allowing the following definition to be admitted in spite of its first formal parameter obviously being irrelevant.

    (defun foo (x clk)
      (if (zp clk)
          :diverge
        (let ((clk (1- clk)))
          (foo x clk))))
    

    Fixed a bug in the mini-proveall target in GNUmakefile. The fix includes a slight change to the :mini-proveall command (an extra event at the end). Thanks to Camm Maguire for reporting this bug.

    Fixed a bug that occurred when certify-book was called after using set-fmt-soft-right-margin or set-fmt-hard-right-margin to set a small right margin.

    Fixed set-inhibit-warnings so that it takes effect for a subsequent include-book event. Thanks to Jared Davis and David Rager for queries that led to this fix.

    Hard Lisp errors are now avoided for certain :rewrite rules: those whose equivalence relation is other than equal when the rule is originally processed, but is no longer a known equivalence relation when the rule is to be stored. Thanks to Jared Davis for sending a useful example, a minor variant of which is included in a comment in source function interpret-term-as-rewrite-rule (file defthm.lisp).

    Fixed a bug in the ACL2 evaluator (source function raw-ev-fncall), which was unlikely to be exhibited in practice.

    Fixed a hard Lisp error that could occur for ill-formed :meta rule-classes, e.g., (:meta :trigger-fns '(foo)).

    It is now an error to include a stobj name in the :renaming alist (see defstobj).

    Some bogus warnings about non-recursive function symbols have been eliminated for rules of class :type-prescription.

    (Allegro CL host Lisp only) Fixed an obsolete setting of compiler variable comp:declared-fixnums-remain-fixnums-switch, which may have been responsible for intermittent (and infrequent) checksum errors encountered while including books during certification of the regression suite.

    Fixed a proof-checker bug that could result in duplicate goal names in the case of forced hypotheses. An example showing this bug, before the fix, appears in a comment in the ACL2 sources, in (deflabel note-5-0 ...).

    We fixed a bug in a prover routine involved in type-set computations involving linear arithmetic. This bug has been around since at least as far back as Version_3.3 (released November, 2007). We are not aware of any resulting unsoundness, though it did have the potential to weaken the prover. For example, the following is proved now, but was not proved before the bug was fixed.

    (thm
     (implies (and (rationalp x)
                   (rationalp y)
                   (integerp (+ (* 1/3 y) x)))
              (integerp (+ y (* 3 x))))
     :hints (("Goal" :in-theory (disable commutativity-of-+))))
    

    Although all bets are off when using redefinition (see ld-redefinition-action), we wish to minimize negative effects of its use, especially raw Lisp errors. The examples below had caused raw Lisp errors, but no longer.

    (defstobj st fld :inline t)
    (redef!)
    (defstobj st new0 fld)
    (u)
    (fld st) ; previously an error, which is now fixed
    
    ; Fresh ACL2 session:
    (redef!)
    (defun foo (x) x)
    (defmacro foo (x) `(quote ,x))
    (u)
    
    ; Fresh ACL2 session:
    (redef!)
    (defmacro foo (x) (cons 'list x))
    (defun foo (x) x)
    

    Fixed a bug that could cause hard Lisp errors in an encapsulate event. Thanks to Sol Swords for sending an example that exhibited this bug. Here is a simpler such example; the bug was in how it was checked whether the guard for a guard-verified function (here, g) depends on some function introduced in the signature of the encapsulate (here, the function f).

    (encapsulate
     ((f (x) t))
     (local (defun f (x) (declare (xargs :guard t)) x))
     (defun g (x)
       (declare (xargs :guard (if (integerp x) (f x) t)))
       x))
    

    Fixed a bug in mfc-relieve-hyp that we believe could prohibit its use on the last hypothesis. Thanks to Sol Swords for reporting this bug and providing a fix.

    The syntax #! (see sharp-bang-reader) was broken after a skipped readtime conditional. For example, the following input line caused an error.

    #+skip #!acl2(quote 3)
    
    This bug has been fixed.

    Fixed a bug in the break-rewrite utility, which was evidenced by error messages that could occur when dealing with free variables. An example of such an error message is the following; we thank Robert Krug for sending us an example that produced this error and enabled us to produce a fix.

    HARD ACL2 ERROR in TILDE-@-FAILURE-REASON-PHRASE1:  Unrecognized failure
    reason, ((MEM-ARRAY . X86) (ADDR QUOTE 9)).
    

    We fixed an obscure bug that we believe could interfere with defproxy because of an incorrect (declaim (notinline <function>)) form.

    CHANGES AT THE SYSTEM LEVEL AND TO DISTRIBUTED BOOKS

    Improvements have been made related to the reading of characters. In particular, checks are now done for ASCII encoding and for the expected char-code values for Space, Tab, Newline, Page, and Rubout. Also, an error no longer occurs with certain uses of non-standard characters. For example, it had caused an error to certify a book after a single portcullis command of (make-event `(defconst *my-null* ,(code-char 0))); but this is no longer an issue. Thanks to Jared Davis for helpful correspondence that led us to make these improvements.

    The character encoding for reading from files has been fixed at iso-8859-1. See character-encoding. Thanks to Jared Davis for bringing this portability issue to our attention (as this change arose in order to deal with a change in the default character encoding for the host Lisp, CCL), and pointing us in the right direction for dealing with it. In many cases, the character encoding for reading from the terminal is also iso-8859-1; but this is not guaranteed. In particular, when the host Lisp is SBCL this may not be the case.

    Although the HTML documentation is distributed with ACL2, it had not been possible for users to build that documentation without omitting graphics, for example on the ACL2 home page. That has been fixed, as files graphics/*.gif are now distributed.

    Compiler warnings are suppressed more completely than they had been before. For example, the following had produced a compiler warning when the host Lisp is CCL, but no longer does so.

    (defun f () (car 3))
    (trace$ f)
    

    Removed support for ``tainted'' certificates. One reason is that there are rarely incremental releases. A stronger reason is that for the compatibility of a new release is with the previous non-incremental release, it's not particularly relevant whether or not the new release is incremental.

    The `make' variable BOOKS can now be defined above the line that includes Makefile-generic. (For relevant background, see books-certification-classic.)

    (SBCL only) ACL2 images built on SBCL now have an option, --dynamic-space-size 2000, that can avoid space problems that could previously have caused the session to die.

    The default value for variable LISP in file GNUmakefile is now ccl. Thus, if you use `make' in the standard way to build an ACL2 executable, the default host Lisp is ccl rather than gcl.

    EMACS SUPPORT

    EXPERIMENTAL VERSIONS

    For the version supporting the reals, ACL2(r) (see real), the supporting function floor1 has been defined in raw Lisp. This avoids an error such as in the following case.

    (defun f () (declare (xargs :guard t)) (floor1 8/3))
    (f) ; had caused raw Lisp error, before the fix
    

    Among the enhancements for the parallel version, ACL2(p) (see parallelism), are the following. We thank David Rager for his work in developing ACL2(p) and these improvements in particular.

    The macro set-parallel-evaluation has been renamed set-parallel-execution.

    Calls of the macro set-waterfall-printing are no longer events, so may not be placed at the top level of books. However, it is easy to create events that have these effects; see set-waterfall-printing. Note that now, :ubt and similar commands do not change the settings for either waterfall-parallelism or waterfall-printing.

    The implementation of deflock has been improved. Now, the macro it defines can provide a lock when invoked inside a guard-verified or :program mode function. Previously, this was only the case if the function definition was loaded from raw Lisp, typically via a compiled file.

    The underlying implementation for waterfall parallelism (see set-waterfall-parallelism) has been improved. As a result, even the largest proofs in the regression suite can be run efficiently in :resource-based waterfall parallelism mode. Among these improvements is one that can prevent machines from rebooting because operating system limits have been exceeded; thanks to Robert Krug for bringing this issue to our attention.

    There is also a new flag for configuring the way waterfall parallelism behaves once underlying system resource limits are reached. This flag is most relevant to :full waterfall parallelism. see set-total-parallelism-work-limit for more information.

    The dmr utility has the same behavior in ACL2(p) as it has in ACL2 unless waterfall-parallelism has been set to a non-nil value (see set-waterfall-parallelism), in which case statistics about parallel execution are printed instead of the usual information.

    The user can now build the regression suite using waterfall parallelism. See the distributed file acl2-customization-files/README for details, and see unsupported-waterfall-parallelism-features for a disclaimer related to building the regression suite using waterfall parallelism.

    When building ACL2 with both the hons and parallelism extensions (what is called ``ACL2(hp)''), the functions that are automatically memoized by the hons extension are now automatically unmemoized and memoized when the user toggles waterfall parallelism on and off, respectively.

    Calling set-waterfall-parallelism with a flag of t now results in the same settings as if it were called with a flag of :resource-based, which is now the recommended mode for waterfall parallelism. Thanks to Shilpi Goel for requesting this feature.

    The prover now aborts in a timely way in response to interrupts issued during a proof with waterfall parallelism enabled. (This had often not been the case.) Thanks to Shilpi Goel for requesting this improvement.

    Among the enhancements for the HONS extension (see hons-and-memoization) are the following.

    The compact-print code has been replaced by new serialization routines contributed by Jared Davis. This may improve performance when including books that contain make-events that expand to very large constants. You can also now save objects to disk without going into raw lisp; see serialize for details.

    Printing of certain messages has been sped up (by using Lisp function force-output in place of finish-output). Thanks to Jared Davis for contributing this improvement.

    Stobj array writes are perhaps twice as fast.

    It is now permitted to memoize functions that take user-defined stobjs as inputs, provided that no stobjs are returned. Even if stobjs are returned, memoization is permitted provided the condition is nil, as when profiling (see profile). Thanks to Sol Swords for an observation that led to this improvement and for useful conversations, including follow-up leading us to improve our initial implementation.

    Fixes have been made for memoizing with a non-nil value of :ideal-okp. Errors had occurred when memoizing with a :condition other than t for a :logic mode function that had not been guard-verified, even with a non-nil value of :ideal-okp; and after successfully memoizing such a function (without such :condition), it had not been possible to unmemoize it. Thanks to Sol Swords for reporting issues with the :ideal-okp argument of memoize.

    If a book defined a function that was subsequently memoized in that book, the function would no longer behaves as memoized upon completion of book certification (unless that certify-book command was undone and replaced by evaluation of a corresponding include-book command). This has been fixed. Thanks to David Rager for pointing out the problem by sending an example.

    We now support ACL2(h) built not only on 64-bit CCL but also on all supported host Ansi Common Lisps (i.e., all supported host Lisps except GCL). Thanks to Jared Davis for doing much of the work to make this improvement. Note that performance will likely be best for 64-bit CCL; for some Lisps, performance may be much worse, probably depending in part on the underlying implementation of hash tables.




    acl2-sources/doc/HTML/NOTE-6-0.html0000664002132200015000000005735712222333527016041 0ustar kaufmannacl2 NOTE-6-0.html -- ACL2 Version 6.3

    NOTE-6-0

    ACL2 Version 6.0 (December, 2012) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 5.0 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    NOTE. But we start with one major change that is outside the usual categories:

    LICENSE change

    The ACL2 license has been changed from GPL Version 2 to a 3-clause BSD license, found in the LICENSE file distributed with ACL2.

    CHANGES TO EXISTING FEATURES

    Function fmt-to-string and similar functions (see printing-to-strings) now use the default right margin settings; formerly the right margin had been set at 10,000. If you want the former behavior, you can use the :fmt-control-alist, as illustrated below.

    (fmt-to-string "~x0"
                   (list (cons #\0 (make-list 30)))
                   :fmt-control-alist
                   `((fmt-soft-right-margin . 10000)
                     (fmt-hard-right-margin . 10000)))
    

    The use of attachments (see defattach) has been made more efficient, avoiding some low-level checks (Common Lisp `boundp' checks). Thanks to Shilpi Goel for constructing an example that we used to help direct us to remove inefficiency. The following results for that example -- a Fibonacci program run on a machine interpreter in raw-mode (see set-raw-mode) -- give a sense of the potential speedup, though we note that a full ACL2(h) regression showed no significant speedup.

    
    ; Time before the change:
    ; 0.89 seconds realtime, 0.90 seconds runtime
    
    ; Time after the change:
    ; 0.75 seconds realtime, 0.75 seconds runtime
    
    ; Time when cheating to avoid the cost of attachments, by redefining a
    ; function to BE its attachment (so, this gives a lower bound on possible
    ; execution time):
    ; 0.72 seconds realtime, 0.72 seconds runtime
    

    Functions read-acl2-oracle and read-acl2-oracle@par are no longer untouchable (see remove-untouchable). We reported this change for Version_5.0 but it was not made; thanks to Jared Davis for bringing this to our attention. Function get-timer also is no longer untouchable.

    The function butlast now behaves more reasonably on arguments violating its guard. For example, (butlast '(1 2 3) -1) is now provably equal to (1 2 3) instead of to (1 2 3 nil). Thanks to Jared Davis for suggesting a change to the definition of butlast.

    The utilities mfc-ts and mfc-ap (see extended-metafunctions) formerly never used forcing (see force). Now, by default, forcing is allowed during execution of these functions if and only if it is permitted in the rewriting environment where they are called. Moreover, these and the mfc-xx utilities -- mfc-rw, mfc-rw+, and mfc-relieve-hyp -- are now macros that take (optional) keyword arguments :forcep and :ttreep. The :forcep argument is :same by default, providing the forcing behavior inherited from the environment (as described above); but it can be the symbol t or nil, indicating that forcing is to be enabled or disabled, respectively. The :ttree argument is nil by default, but when it is t, then a second value is returned, which is a tag-tree. See extended-metafunctions.

    Many improvements have been made to the tau-system (see tau-system), including support for arithmetic intervals bounded by constants. Thus, for example, (and (<= 0 x) (<= x 15)) is a tau predicate. The documentation has also been improved (see introduction-to-the-tau-system). Also see time-tracker-tau for discussion of how the new time-tracker utility can help discover ways to detect slowdown related to the tau-system.

    The defthm events printed by defabsstobj, namely those that remain to be proved, are now given with :rule-classes nil since there is probably no intention to use them as rules. Thanks to Robert Krug for suggesting that we consider this change.

    The formal parameters for a macro definition (see defmacro) may now include state and user-defined stobjs. (However, macro formals may not be declared as stobjs; see xargs.) Thanks to Jose Luis Ruiz-Reina for raising this issue and to Rob Sumners for helpful conversations -- both of these nearly 10 years ago!

    The utilities defun-inline, defun-notinline, defund-inline, and defund-notinline have been simplified, by taking advantage of the lifting of restrictions on formal parameters of macro definitions mentioned above (involving symbols that happen to be stobj names). Now, when any of the above four utilities is called with a given set of formal parameters, those formals will be used not only for the generated defun event but also for the generated defmacro event. (Previously, they had been renamed for the defmacro event in order to respect the stobj name restriction that no longer exists.) Thanks to Jared Davis for pointing out the value of making this change.

    The events add-invisible-fns and remove-invisible-fns now convert arguments as appropriate using the macro-aliases-table. For example, the event (add-invisible-fns append car) is now legal (though probably not a good idea), because add-invisible-fns is now sensitive to the fact that append maps to binary-append in the macro-aliases-table.

    When :pe is applied to a built-in function that does not have a defining event, such as symbolp, :pe now gives more useful output that points to the documentation instead of printing a call of ENTER-BOOT-STRAP-MODE. Thanks to Anthony Knape for bringing this issue to our attention.

    The macros memoize and unmemoize now cause a warning rather than an error in ACL2 (and work as before in ACL2(h)).

    Terms are now parsed into :type-prescription rules in a manner that removes let bindings both at the top level and in the conclusion (but still not in the hypotheses of the rule). See type-prescription. Thanks to Jared Davis for requesting such an enhancement.

    Printing of numbers is now appropriately sensitive to the print radix; see set-print-radix. Thanks to Shilpi Goel for requesting this enhancement.

    The system function explode-atom no longer includes the radix indicator. The new function explode-atom+ may be used for that purpose.

    NEW FEATURES

    Among the new features for system hackers are analogues of system function simple-translate-and-eval that do not return state. (Thanks to David Rager for requesting this feature and helpful conversations on its implementation.) This and other low-level changes are typically documented in comments in the corresponding release note event, which in this case is (deflabel note-6-0 ...).

    More built-in functions are now guard-verified (and in :logic mode). Furthermore, a mechanism exists for marking yet more built-in functions as guard-verified based on books contributed by users; see Part II of http://www.cs.utexas.edu/users/moore/acl2/open-architecture/. The current state of that enterprise may be viewed by evaluating the constant *system-verify-guards-alist*, which associates a community book name with a list of functions. When ACL2 is built in the normal way, each of those functions is marked as guard-verified when ACL2 is started up; but a special developer build can be used to check that the indicated book, together with its sub-books, proves that those functions are guard-verified.

    Metatheorems (see meta) may now have additional hypotheses, called ``meta-extract hypotheses'', that allow metafunctions to depend on the validity of certain terms extracted from the context or the logical world. See meta-extract. Thanks to Sol Swords for providing an initial implementation, together with very helpful discussions as well as a community book, books/clause-processors/meta-extract-user.lisp, that extends the power of meta-extract hypotheses.

    New utilities oracle-funcall, oracle-apply, and oracle-apply-raw call a function argument on specified arguments. Thanks to Jared Davis for requesting this utility.

    A new utility makes it convenient to track time spent inside specified function calls or, more generally, during specified evaluation. See time-tracker.

    New runic designators make it easy to refer to macro names when building theories. Thus, for example, the object (:i append) may be used in theory expressions to designate the rune (:induction binary-append). See theories. Thanks to Jared Davis for a useful discussion leading to this enhancement.

    Defabsstobj events now take an optional :congruent-to keyword argument, much like defstobj. Thanks to Sol Swords for requesting this feature and for suggesting a very nice optimization that avoids the need to prove additional lemmas.

    Flet may now include inline and notinline declarations. Thanks to Jared Davis for requesting this feature.

    The utility gc-verbose controls printing of messages by the garbage collector, for certain host Lisps. See gc-verbose. Thanks to Shilpi Goel for requesting this utility.

    Added definitions of functions nat-listp and acl2-number-listp. Thanks to Harsh Raju Chamarthi for requesting these additions. Many community books had varying definitions of these functions; these additions guarantee that all books must agree on how these two functions are defined. (Some community books have been changed in order that they remain certifiable, given these additions.) Note that a few built-in :forward-chaining rules were modified in order to accommodate these additions, and the definition of integer-listp was modified to call eq instead of equal, like the other such definitions.

    See get-command-sequence for a new utility that returns a list of commands between two given command descriptors.

    HEURISTIC IMPROVEMENTS

    We obtained a substantial speedup -- 13% observed for the regression suite, and 8% observed for the ACL2(h) regression suite -- by tweaking the break-rewrite implementation to eliminate virtually all of its overhead when it is not in use (the default, which holds until :brr t is evaluated). Thanks to David Rager for a conversation involving ACL2(p) performance statistics that suggested looking at changing break-rewrite to boost performance.

    The heuristics for automatically expanding recursive function calls have been changed during proofs by induction. Now, during induction, more terms that suggested the induction scheme are automatically expanded. Thanks to David Rager for providing an example and having discussions with us that spurred us to develop this heuristic improvement.

    BUG FIXES

    Fixed a soundness bug in defabsstobj based on guards that violated single-threadedness restrictions. Thanks to Sol Swords for bringing this bug to our attention and supplying a proof of nil, which we include as a comment in source file ld.lisp, in (deflabel note-6-0 ...). We also thank Sol for helpful discussions about guards of functions introduced by defabsstobj, which has led us to enhance the documentation; see defabsstobj.

    Fixed a soundness bug in defabsstobj based on interrupted updates of abstract stobjs. As part of the fix a new keyword, :PROTECT, has been introduced for defabsstobj exports, along with a new top-level defabsstobj keyword, :PROTECT-DEFAULT; see defabsstobj. We do some analysis that we expect will avoid the use of :PROTECT in many cases, which is fortunate since the use of :PROTECT t may cause a slight slowdown in (abstract) stobj updates. Thanks to Sol Swords for bringing this bug to our attention and supplying a proof of nil, which we include as a comment in source file other-events.lisp, in the definition of function set-absstobj-debug.

    Fixed a raw Lisp error that occurred when tracing a stobj resize function, thanks to an error report from Warren Hunt, Marijn Heule, and Nathan Wetzler.

    Fixed a raw Lisp error that occurred for certain ill-formed signatures, as in the following example.

    ACL2 !>(encapsulate
               (((f (*) => * :guard t)))
               (local (defun f (x) (consp x))))
    
    ***********************************************
    ************ ABORTING from raw Lisp ***********
    Error:  value (F (*) => * :GUARD T) is not of the expected type SYMBOL.
    ***********************************************
    

    The notion of ``error triple'' (see error-triples) had been implemented ambiguously, with the result that for a stobj, st, the result of evaluating the following two forms was the same: (mv nil st state) and (mv t st state). Of course, these are just examples; in general, a result of (mv erp val state) was sometimes treated as an error triple even when val is a stobj. Now, (mv erp val state) is an error triple only when erp and val are ordinary (non-stobj) values. Thanks to Warren Hunt and Marijn Heule for bringing this problem to our attention.

    The ``with-error-trace'' utility, wet, now works in the non-error case when given a form that returns multiple values. (Note however that STATE will be printed as REPLACED-STATE; and similarly, a user-defined stobj, say ST, will be printed as REPLACED-ST.)

    Some possible error messages for defabsstobj have been fixed that had been ill-formed. Thanks to Sol Swords for bringing this bug to our attention.

    Fixed a bug that sometimes caused the times displayed in the summary for certify-book to be smaller than the actual times.

    Fixed a bug in the guards to system functions fmt-char and fmt-var, which are no longer :logic-mode, guard-verified functions.

    (GCL only) Fixed a bug present in Gnu Common Lisp for #u (see sharp-u-reader).

    CHANGES AT THE SYSTEM LEVEL

    The state global variable 'distributed-books-dir has been renamed 'system-books-dir. On a related note, the documentation now refers to ``community books'' rather than ``distributed books'', and there is a corresponding new documentation topic; see community-books.

    Fixed a bug in the implementation of wet (which is actually in the community book books/misc/wet.lisp).

    A directory, interface/, is no longer part of the ACL2 distribution. Rather, it is a subdirectory of the ACL2 community books. Thus, if you fetch those books in the usual way (see the installation instructions on the ACL2 home page), you will find a directory books/interface/. Subdirectory emacs/ of that interface directory provides Emacs support for proof-trees as well an acl2-mode. This change has been reflected in ACL2 file emacs/emacs-acl2.el, so users will probably not be impacted if they load that file into Emacs.

    The community books file books/Makefile-generic now causes, by default, a backtrace to be printed when there is a raw Lisp error.

    Some changes have been made to how regressions are run, i.e., to how the community books are certified. (1) The standard regression now includes community books directory books/centaur. To skip these (for example, a Windows system has encountered difficulty with them even after installing Perl), include ACL2_CENTAUR=skip with your `make' command. (2) A new `make' (or environment) variable, ACL2_JOBS, specifies the number of parallel jobs to run, serving as a replacement for the -j argument of `make' that works for all community books, including those under directory centaur; see books-certification-classic. (3) It is no longer necessary to do an ACL2(h) regression in order to build a copy of the documentation generated by Jared Davis's xdoc utility at books/xdoc-impl/manual/preview.html; a vanilla ACL2 regression will build this manual. (4) It is no longer necessary to set the ACL2 environment variable for ACL2(h) regressions if you want to use the executable saved_acl2h in the ACL2 sources directory.

    The ACL2 home page now has a search utility for documentation and books. Thanks to Shilpi Goel and David Rager for feedback on a preliminary version of this utility.

    (only for SBCL with 64-bit ACL2(h)) The value of SBCL command line option --dynamic-space-size for ACL2(h) on 64-bit platforms has been increased from 2000 to 16000 (as explained in a comment in the ACL2 source definition of *sbcl-dynamic-space-size*).

    EMACS SUPPORT

    EXPERIMENTAL/ALTERNATE VERSIONS

    Among the enhancements for ACL2(r) (see real) are the following.

    Thanks to Ruben Gamboa for his helpful role in making the following improvements made with Ruben Gamboa in support for non-standard analysis in ACL2(r).

    Constrained functions can now be introduce as non-classical. See signature.

    Defun-sk now takes a new keyword argument, :CLASSICALP, that determines whether or not the named function is classical. See defun-sk.

    Incorporated a bug fix from Ruben Gamboa for ceiling. The default (for `bad' arguments) had been 1, but now we follow normal ACL2 practice by returning 0 in that case.

    Among the enhancements for the HONS extension (see hons-and-memoization) are the following.

    Macros with-fast-alist, with-stolen-alist, and fast-alist-free-on-exit are now defined in ACL2(h), rather than being defined in the community book "books/centaur/misc/hons-extra.lisp". Thanks to Jared Davis and Sol Swords for donating this code, and thanks to Jared for helpful discussions leading to this change.

    Among the enhancements for ACL2(p) (see parallelism) are the following. We thank David Rager for his work in developing ACL2(p) and for his helpful role in these improvements.

    A bug has been fixed that could leave one in a wormhole, awaiting input, after an error, such as an error in an :in-theory hint during a proof. Thanks to Shilpi Goel for bringing this bug to our attention.

    A key checkpoint for a given goal is now printed only once. Previously, if a key checkpoint led to more than one goal pushed for proof by induction, the key checkpoint would be printed once for each such goal during the proof, and also once for each such goal in the summary at the end.




    acl2-sources/doc/HTML/NOTE-6-1.html0000664002132200015000000003172212222333530016020 0ustar kaufmannacl2 NOTE-6-1.html -- ACL2 Version 6.3

    NOTE-6-1

    ACL2 Version 6.1 (February, 2013) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 6.0 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    More system functions are in :logic mode, guard-verified. Evaluate

    (strip-cars (cdr (assoc-equal "system/top" *system-verify-guards-alist*)))
    
    for the list of functions checked to be guard-verifiable in the community books. Thanks to those who have contributed to this effort, as shown in file headers in directory system/ of the community books.

    The macro defund now avoids an error when :mode :program has been specified in an xargs form of a declare form, for example: (defund f (x) (declare (xargs :mode :program)) x). It does this by avoiding the generation of in-theory events in such cases. Thanks to David Rager and Jared Davis for requesting such a change, and for ensuing helpful discussions.

    Added a field :UNIFY-SUBST to metafunction contexts (see EXTENDED-METAFUNCTIONS), accessed with function mfc-unify-subst. Thanks to Sol Swords for requesting this enhancement.

    The functions sys-call and sys-call-status are now guard-verified :logic-mode functions.

    It had been the case that if any supporter of a dependent clause processor (see define-trusted-clause-processor) is among the ancestors of a given formula, then it was illegal to apply functional instantiation (see lemma-instance) to that formula. Now, this is illegal only if some such supporter is in the domain of the functional substitution.

    The tau system (see tau-system, or if you are unfamiliar with the tau system, see introduction-to-the-tau-system) now allows the user to define and verify functions that compute bounds on arithmetic expressions. See bounders.

    The utility print-summary-user has been replaced by finalize-event-user, which is described below. If you previously attached a function to print-summary-user, say my-print-summary-user, then you can get the effect you had previously as follows.

    (defun my-finalize-event-user (state)
      (declare (xargs :mode :logic :stobjs state))
      (prog2$ (my-print-summary-user state)
              state))
    (defattach finalize-event-user my-finalize-event-user)
    

    It had been the case that when you LD a file, the connected book directory (see cbd) was set to the canonical pathname of that file's directory for the duration of the LD call. This could cause problems, however, if the file is actually a soft link: an include-book form in the book with a relative pathname for the book would be resolved with respect to the absolute pathname for that link, which is probably not what was intended. So soft links are no longer followed when computing the above connected book directory. The following example, which is how we discovered this problem, may clarify. We attempted to execute the form (ld "top.lisp") using ACL2(r) (see real) in community books directory nonstd/arithmetic/, where all of the .lisp files are soft links to files in arithmetic/. Thus, the form (include-book "equalities") attempted to include arithmetic/equalities instead of nonstd/arithmetic/equalities, which caused an error.

    We no longer document the use of value :START for with-prover-step-limit. This value has always been used by the ACL2 implementation and may have semantics that change with new ACL2 versions. If you have reason to use this value, please contact the ACL2 implementors.

    NEW FEATURES

    By default, the prover now gives information about case splits. See splitter. Thanks to many ACL2 users, most recently David Rager, for requesting such a capability. Also thanks to David Rager and Jared Davis for helpful discussions, and thanks to Robert Krug for feedback on the initial implementation and documentation that led us to make improvements.

    New utilities initialize-event-user and finalize-event-user allow the user to run state-modifying code at the start and end of events. Thanks to Harsh Raju Chamarthi for requesting these capabilities. Note that finalize-event-user replaces print-summary-user.

    HEURISTIC IMPROVEMENTS

    Several heuristic improvements have been made to the tau system, even if you do not explicitly use the new capability for computing bounds on arithmetic expressions, mentioned above. See tau-system, or if you are unfamiliar with the tau system, see introduction-to-the-tau-system.

    BUG FIXES

    A soundness bug has been fixed that exploited the use of expansion files (see book-compiled-file) together with defstobj. For an example illustrating this bug, see the comment about ``Expansion/Defstobj Bug'' in the form (deflabel note-6-1 ...) in ACL2 source file ld.lisp.

    We fixed a soundness bug involving system function canonical-pathname and (most likely) other functions in the former value of constant *unattachable-primitives*. Thanks to Jared Davis and Sol Swords for bringing this bug to our attention by way of an example. We include a very slight variant of that example in a comment within the form (deflabel note-6-1 ...) in ACL2 source file ld.lisp.

    There was a soundness bug that allowed attachments to prove nil in a consistent logical world involving defaxiom events. This has been fixed, by requiring that no function symbol ancestral in a defaxiom formula is allowed to get an attachment. See defattach, in particular discussion of ``a restriction based on a notion of a function symbol syntactically supporting an event'', which concludes with a proof of nil that is no longer possible.

    (ACL2(h) only) We fixed a soundness bug in the interaction of memoization with congruent stobjs, in cases where the :congruent-to field of defstobj was not the canonical representative in the congruence class. For an example illustrating this bug, see the comment about ``memoize/congruent stobj bug'' in the form (deflabel note-6-1 ...) in ACL2 source file ld.lisp.

    Functions defined by defstobj had failed to be compiled when certifying books, except in host Lisps that compile on-the-fly (CCL, SBCL). This has been fixed for all host Lisps. A related change, probably less significant, was made for defabsstobj. Thanks to Sol Swords for reporting bugs that turned out to be mistakes in a preliminary implementation of this change.

    Fixed an assertion error involving linear arithmetic. Thanks to Sol Swords for sending an example illustrating the bug (now appearing as a comment in ACL2 source function linearize1).

    Fixed a bug that was breaking the ACL2s build mechanism (see acl2-sedan) by causing certain needless evaluation of ``hidden defpkg'' forms in certificate files when executing a call of include-book. The bug could also affect rare error messages arising from ill-formed certificate files. Thanks to Harsh Raju Chamarthi for bringing this bug to our attention by sending us an example script of the sort that was breaking during an ACL2s build.

    Fixed handling of pathnames by some low-level code (system function our-truename) that could cause errors, for example for host-Lisp GCL on some platforms when environment variable HOME points to a non-existent directory. Thanks to Camm Maguire for bringing this issue to our attention and helping with the debugging.

    Fixed a coding bug in generation of stobj resizing functions for a stobj named OLD. The following example illustrates the bug.

    (defstobj old
      (fld :type (array (unsigned-byte 31) (8))
            :initially 0 :resizable t))
    (resize-fld 10 old)
    ; The following returned 8 but should have returned 10:
    (fld-length old)
    

    Fixed a bug in defabsstobj-missing-events (which macroexpanded incorrectly). Thanks to Sol Swords for bringing this bug to our attention.

    Fixed two bugs in the handling of step-limits. Thanks to Hanbing Liu for bringing the main such bug to our attention, which was that ACL2 could report a step-limit violation during certify-book (in fact, during any compound event such as a call of encapsulate or progn), even without direct user involvement in managing step-limits (see set-prover-step-limit and see with-prover-step-limit). The other bug was that a bad argument to set-prover-step-limit could result in a raw Lisp error, for example: (progn (set-prover-step-limit '(a b))).

    CHANGES AT THE SYSTEM LEVEL

    The books/ directory no longer needs to exist in order to build an ACL2 executable. Thanks to Robert Krug for pointing out that the installation instructions had suggested that this was already the case.

    Many changes have been made to the community books (see community-books). For example, some community books now include std/lists/rev.lisp, which contains the rule revappend-removal, which may cause some proofs involving revappend to fail where they formerly succeeded, or vice-versa. When a proof fails that formerly succeeded, it may be useful for you to look over the runes printed in the event summary.

    EMACS SUPPORT

    EXPERIMENTAL/ALTERNATE VERSIONS

    For ACL2(p), wormhole-eval is now locked by default; thanks to David Rager for suggesting this change. But there is a way to avoid the lock; see wormhole-eval. In particular, the lock is avoided in the implementations of accumulated-persistence and forward-chaining-reports, which are not supported in ACL2(p) (see unsupported-waterfall-parallelism-features).




    acl2-sources/doc/HTML/NOTE-6-2.html0000664002132200015000000004662412222333530016030 0ustar kaufmannacl2 NOTE-6-2.html -- ACL2 Version 6.3

    NOTE-6-2

    ACL2 Version 6.2 (June, 2013) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 6.1 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    The macro top-level has been changed, so that evaluation of a form (top-level x) results in an error when evaluation of x results in an error. Thanks to Jared Davis for observing that when evaluating a file using ld, an interrupt of a call of a top-level call in that file would not prevent evaluation of later forms in the file.

    The macro THE no longer causes an error when guard-checking is :NONE. For example, it had been the case that evaluation of (the integer t) always caused an error; but now, there is no error after executing command :set-guard-checking :NONE. Thanks to Jared Davis for asking for a way to avoid such errors.

    The error printed when attempting to ``reincarnate'' a package -- that is, to define a package that is not part of the ACL2 logical world but exists in raw Lisp because it was once part of the world -- is now much more instructive. In particular, it shows pathnames for the previous and proposed defpkg events, and it shows the symbols that are imported by one but not the other. Thanks to Jared Davis for requesting this improvement.

    Functions open-input-channel and open-output-channel no longer cause an error when failing to open a channel because of a permissions problem, but instead return (mv nil state). Thanks to Jared Davis for requesting this change. (Note: this change does not apply if the host Lisp is non-ANSI, i.e., if the host Lisp is non-ANSI GCL.)

    The advanced meta-extract mechanisms, provided for using facts from the world or metafunction context, have been enhanced in the following ways, in collaboration with Sol Swords. See meta-extract for more details.

    It is now permissible to use calls of meta-extract-global-fact in hypotheses of clause-processor rules, much as they are used in hypotheses of meta rules. See meta-extract. Thanks to Sol Swords for requesting this feature.

    The utility meta-extract-global-fact is now a macro, which expands to a corresponding call of the new function, meta-extract-global-fact+. This new function takes an alternate, extra state as an argument; it is not to be executed, and it operates on the alternate state, whose logical world is intended to be the same as that of the ``live'' (usual) state.

    A new sort of value for the obj argument is supported for meta-extract-global-fact (and meta-extract-global-fact+), which results in a term equating a function application to its result. See meta-extract, in particular the discussion of :fncall.

    It is now possible for trace$ to avoid printing prefixes of the form "n> " and "<n ", while also (optionally) avoiding indentation. See trace$, in particular the discussion of :fmt!. Thanks to Shilpi Goel for requesting this feature.

    It was possible for the guard-debug feature to generate duplicate calls of extra-info in hypotheses generated for guard verification. We have eliminated duplicates of this sort.

    When in-theory returns without error, it returns a value (:NUMBER-OF-ENABLED-RUNES k), where k is the length of the new current theory. (Formerly, k was returned.) This value is thus printed when an in-theory event is submitted at the top level. Thanks to Gisela Rossi for feedback that led us to make this change.

    A new keyword parameter for ld is :ld-missing-input-ok. Its default value is nil, which causes an error, as before, upon failure to open a specified file. Other legal values are t and :WARN; see ld-missing-input-ok and see ld.

    Extended *acl2-exports*, in particular adding UNSIGNED-BYTE-P and SIGNED-BYTE-P (thanks to a suggestion by Jared Davis)

    Even if the function f is defined to take one or more stobj arguments, the form (ec-call (f ...)) is now legal if all arguments of the call of f are non-stobj objects, in any context where only ordinary object return values are expected.

    When the second argument of certify-book is a symbol, that symbol formerly needed to be ? or t, in the "ACL2" package. Now, the symbol-package-name of the second argument is ignored: any symbol whose symbol-name is "?" or "T" is treated the same in that argument position as the symbol ? or t in the "ACL2" package, respectively. Thanks to Warren Hunt and Nathan Wetzler for suggesting consideration of a more relaxed criterion for that second argument.

    (For system hackers, not standard ACL2 users:) Utilities initialize-event-user and finalize-event-user now each take a list of three arguments, (ctx body state). Thanks to Harsh Raju Chamarthi for requesting this change.

    NEW FEATURES

    It is now permissible to specify a stobj field that is itself either a stobj or an array of stobjs. A new primitive, stobj-let, is provided in order to access or update such fields; see stobj-let. Thanks to Warren Hunt and Sol Swords for requesting the ability to specify nested stobjs.

    New accessor function (mfc-world mfc) returns the world component of a metafunction context. See extended-metafunctions.

    A new xargs keyword, :SPLIT-TYPES, can be used to ``split'' the type declarations from the guard in the following sense. By default, or when :SPLIT-TYPES has value nil, one gets the existing behavior: the terms corresponding to type declarations are conjoined into the guard. However, if :SPLIT-TYPES t is specified, then that is not the case; instead, guard verification will require that these terms are proved under the hypothesis that the guard holds. In this way, one can add type declarations to assist the host Lisp compiler without cluttering the function's guard. See xargs. Thanks to Jared Davis for requesting this feature.

    Advanced users may want to see quick-and-dirty-subsumption-replacement-step for a way to turn off a prover heuristic. Thanks to those who have mentioned to us potential issues with this heuristic, most recently Dave Greve.

    HEURISTIC IMPROVEMENTS

    We made changes to the ``ancestors check'' heuristic (source function ancestors-check-builtin), as follows.

    The heuristic could prevent a rewrite rule's hypothesis from being rewritten to true, even when that hypothesis is of the form (force <term>). Now, forcing will take place as expected; see force. Thanks to Robert Krug for bringing this issue to our attention and sending an example, which we include as a comment in the ACL2 source code (see (deflabel note-6-2 ...)).

    The heuristic is now delayed until after we check whether the hypothesis is already known, using type-set reasoning alone (in particular, not using rewriting), to be true or to be false. We believe that this is now the ``right'' order for those two operations. We saw a slight speed up in the regression tests (about a percent) with this change, but that might be in the noise.

    A technical change makes the heuristic slightly less aggressive in preventing backchaining. Roughly speaking, ordering checks based on function symbol counts could suffice to permit backchaining, where now variable counts also suffice. Thanks to Robert Krug for showing us an example where backchaining led to a term with no free variables that was nevertheless subject to the ancestors check, preventing it from being rewritten.

    (For those who use defattach to attach to ancestors-check) We have used defrec to introduce an `ancestor' data structure. A new function, strip-ancestor-literals, should be used to obtain the literals from a list of ancestors, although strip-cars will still work at this time.

    When we rewrite the current literal of the current clause we assume the falsity of the other literals and of the conclusions produced by forward chaining. We have changed the order in which those assumptions are made, which affects the type-alist used during rewriting. This has three effects: the new type-alist, which is sometimes stronger than the old one, may allow additional rules to fire, the choice of free vars may be different, and the order of the literals in forced subgoals may be different. Should ``legacy'' proofs fail under the new type-alist, we recommend looking for rules that are fired in the new proof that were not fired (on that same subgoal) in the old one. Thanks to Dave Greve for sending us an example that led us to make this change.

    BUG FIXES

    We fixed a soundness bug that could be exploited by calling system functions acl2-magic-mfc or acl2-magic-canonical-pathname. Thanks to Sol Swords for bringing this bug to our attention.

    We fixed a soundness bug in the handling of stobjs, in which strings were recognized as stobjs in raw Lisp. Thanks to Jared Davis for sending us a proof of nil that exploited this bug. We now have a much simpler example of this bug, as follows.

    (defstobj st fld)
    (defthm bad (stp "abc") :rule-classes nil)
    (defthm contradiction
      nil
      :hints (("Goal" :in-theory (disable (stp)) :use bad))
      :rule-classes nil)
    

    We fixed bugs in extended metafunctions (see extended-metafunctions). The macro mfc-ap no longer takes a :TTREEP keyword argument, because this argument could allow returning a tag tree that does not properly account for forcing. The remaining mfc-xx macros -- mfc-relieve-hyp, mfc-rw+, mfc-rw, and mfc-ts -- still take a :TTREEP keyword argument, but the corresponding functions when :TTREEP is t -- mfc-relieve-hyp-ttree, mfc-rw+-ttree, mfc-rw-ttree, and mfc-ts-ttree -- were introduced with incorrect output signatures. A complication is that mfc-relieve-hyp-ttree was improperly defined in raw Lisp in a way that actually matched the incorrect signature! All of these bugs have been fixed. Perhaps any of them could have made it possible to prove nil, though we have not tried to do so.

    (Windows only) On Windows, it had been possible for ACL2 not to consider two pathnames to name the same file when the only difference is the case of the drive, e.g., `C:' vs. `c:'. This has been fixed. Thanks to Sol Swords for reporting this issue.

    Fixed a bug in the storing of rules for the tau system; see tau-system. (The error message mentions PARTITION-SIGNATURE-HYPS-INTO-TAU-ALIST-AND-OTHERS.) Thanks to Sol Swords for reporting this bug and sending a simple example to illustrate it.

    It had been possible to admit the missing defthm events printed by defabsstobj, and yet get an error when subsequently submitting the same defabsstobj event, stating: ``Note discrepancy with existing formula''. The problem could occur when an expression of the form (or X Y) occurred in one of those missing events, because ACL2 created it from the term (if X 't Y) but then translated (or X Y) to (if X X Y), resulting in a mismatch. This has been fixed. Thanks to Jared Davis for reporting this bug using a simple example.

    A hard Lisp error was possible for certain illegal functional substitutions (see lemma-instance). Thanks to Sol Swords for reporting this bug.

    We fixed a bug in the case that an exported function of a defabsstobj event had a guard of t. Thanks to Jared Davis for sending a simple example when reporting this bug.

    We now avoid an infinite loop that could occur when attempting to close the standard character output channel (see standard-co). Instead, an error message explains how to accomplish what was probably intended. Thanks to Shilpi Goel for bringing this issue to our attention.

    (Windows only) Fixed a bug that was causing a hard error on Windows when ACL2 encountered filenames starting with the tilde character (~), for example, (ld "~/acl2-customization.lsp"). Thanks to Sol Swords for bringing this bug to our attention. Also thanks to Harsh Raju Chamarthi for a useful conversation that led to a better fix than our first one.

    CHANGES AT THE SYSTEM LEVEL

    ACL2 may now be built on recent versions of a new host Lisp, ANSI Gnu Common Lisp (GCL). Traditional (non-ANSI) GCL was the original host Lisp underlying ACL2, and we are grateful for GCL support that we received from the late Bill Schelter and, more recently and particularly for ANSI GCL, from Camm Maguire.

    The `make' process suggested for book certification has changed substantially, thanks in large part to contributions from Jared Davis and Sol Swords. We have seen the new process provide better performance on machines with many cores, and we expect maintenance advantages such as eliminating the need for Makefiles in individual book directories. The ``classic'' process, which was based on community books file books/Makefile-generic, is still supported (see books-certification-classic) but may disappear in a future release of ACL2. See books-certification. Most changes should be invisible to the user, other than improved `make'-level parallelism, with the exception of the following.

    o Variable ACL2_JOBS is no longer supported, nor is it necessary; simply use `make' option `-j' instead.

    o Regressions now use `make' option -k by default, which causes the regression to keep going after errors, rather than -i, which ignores errors. If you encounter problems because of this change, use ACL2_IGNORE=-i with your `make' command.

    o The `regression' target works for the experimental extension, ACL2(h) (see hons-and-memoization); target `regression-hons' no longer exists.

    Please let us know if you run into problems with the new infrastructure, as we consider the legacy infrastructure to be deprecated and we will probably eliminate much of it in the future. In particular, circular dependencies were formerly prohibited at the directory level, but that is no longer the case, and we expect such cycles to occur in the future.

    Although ACL2 users don't typically modify raw Lisp variables, we have arranged to reset Lisp variable *default-pathname-defaults* if necessary at startup so that it will not interfere with ACL2, in particular by messing up the initial connected book directory (see cbd). Thanks to Jared Davis, Sol Swords, and Raymond Toy for helping us to identify this issue.

    EMACS SUPPORT

    EXPERIMENTAL/ALTERNATE VERSIONS

    In ACL2(h), print-object$ no longer uses the serialize printer except in system applications as before (e.g., write out .cert files). Thanks to Dave Greve for bringing this issue to our attention.

    Jared Davis contributed changes related to the memoize utility of ACL2(h), including some low-level changes as well as the following.

    o Never-memoize specifies that a given function should never be memoized.

    o Removed memoize-let, which may never have ever been used.

    o Removed the :inline keyword option to memoize, which was just an alias for the :recursive option.

    For ACL2(p), some anomalous behavior may no longer occur because prover calls (more specifically, trips through the ACL2 ``waterfall'') will return only after all sub-computations (threads) have finished. Thanks to David Rager for contributing this improvement.

    ACL2(pr), which includes parallelism (as for ACL2(p)) and non-standard analysis support for the reals (as for ACL2(r)), now builds and can certify the community nonstd/ books. Thanks to David Rager for his contribution to this capability.




    acl2-sources/doc/HTML/NOTE-6-3.html0000664002132200015000000004272012222333530016022 0ustar kaufmannacl2 NOTE-6-3.html -- ACL2 Version 6.3

    NOTE-6-3

    ACL2 Version 6.3 (October, 2013) Notes
    Major Section:  RELEASE-NOTES
    

    NOTE! New users can ignore these release notes, because the documentation has been updated to reflect all changes that are recorded here.

    Below we roughly organize the changes since Version 6.2 into the following categories of changes: existing features, new features, heuristic improvements, bug fixes, changes at the system level, Emacs support, and experimental versions. Each change is described in just one category, though of course many changes could be placed in more than one category.

    CHANGES TO EXISTING FEATURES

    The evaluation of a term from a bind-free hypothesis had been expected to produce an alist binding free variables to terms. While that is still legal, it is also legal for that evaluation to produce a list of such alists: then each is considered, until one of them permits all remaining hypotheses to be relieved. See bind-free. Thanks to Sol Swords for requesting this enhancement.

    ACL2 continues to provide a way to specify keyword command abbreviations for the top-level loop; see ld-keyword-aliases. However, ld-keyword-aliases is now a table rather than a state global; it is thus no longer a so-called LD special. The functionality of set-ld-keyword-aliases has essentially been preserved, except that it is now an event (see events), hence it may appear in a book; it is local to a book (or encapsulate event); and the state argument is optional, and deprecated. A non-local version (set-ld-keyword-aliases!) has been added, along with corresponding utilities add-keyword-alias and add-keyword-alias! for adding a single keyword alias. See ld-keyword-aliases. Thanks to Jared Davis for correspondence that led us to make this change.

    The proof-checker command (exit t) now exits without a query (but still prints an event to show the :INSTRUCTIONS). Thanks to Warren Hunt for feedback leading us to make this change.

    We made the following minor changes to the behavior or dmr; see dmr. First, if dmr monitoring is enabled, then (dmr-start) will have no effect other than to print a corresponding observation, and if monitoring is disabled, then (dmr-stop) will have no effect other than to print a corresponding observation. Second, it had been the case that when (dmr-start) is invoked, the debugger was always automatically enabled with value t (see set-debugger-enable), and the debugger remained enabled when (dmr-stop) was invoked. Now, the debugger is only enabled by (dmr-start) if it is not already enabled and does not have setting :never. Moreover, if such automatic enabling takes place, then the old setting for the debugger is restored by (dmr-stop) unless set-debugger-enable has first been called after that automatic enabling. Finally, if the value of state global variable 'debugger-enable is :bt, then the new value will be :break-bt, not t.

    When a call of progn is executed in the ACL2 loop, its constituent events and their results are printed, just as was already done for calls of encapsulate. Thanks to Jared Davis for a conversation causing us to consider this change.

    (CCL only) When set-debugger-enable is invoked with an argument that prints a backtrace and CCL is the host Lisp, the backtrace will be limited to 10,000 stack frames. (We have seen more than 65,000 stack frames before this change.) This limit is the value of raw Lisp variable *ccl-print-call-history-count*, which may be assigned another positive integer value to serve as the maximum number of stack frames to be printed.

    Improvements have been made pertaining to the disabling (inhibiting) of individual types of warning. Now, inhibited warnings are implemented in a straightforward way using a separate table for this purpose, the inhibit-warnings-table, rather than using the acl2-defaults-table. See set-inhibit-warnings, and see set-inhibit-warnings! for a variant that is not local to an encapsulate or a book in which it occurs. Thanks to Sol Swords for sending examples showing how set-inhibit-warnings did not always behave as one might reasonably expect when books are involved.

    It had been the case that lp took a single argument, 'raw. This argument was not documented and also caused an error, so it has been eliminated.

    The functionality of make-event has been significantly expanded. First: if the expansion is of the form (:OR e1 e2 ...), then event forms e1, e2, and so on are evaluated, in order, until the evaluation of some ek completes without error. In that case, the expansion is treated simply as ek. With this capability, alternative expansions can be attempted and the successful one does not need to be evaluated again. See the new version of community book books/make-event/proof-by-arith.lisp for an example. Second, an expansion may be of the form (:DO-PROOFS e), in which case the event e is evaluated with proofs not skipped; see ld-skip-proofsp. Third, new keyword :EXPANSION? can be used to avoid storing expansions in certificate files. See make-event.

    When a defun event prints a failure message in the summary, that message now indicates when the failure is due to a failed proof of guard verification or a failed proof of the measure theorem. Thanks to Shilpi Goel for requesting this enhancement.

    NEW FEATURES

    ACL2 can now be instructed to time activities using real time (wall clock time) instead of run time (typically, cpu time). See get-internal-time. Thanks to Jared Davis for asking to be able to obtain real-time reports in event summaries.

    A new utility, sys-call+, is similar to existing utility sys-call in that it executes a command. Unlike sys-call, however, sys-call+ returns values that include output from the command (in addition to the exit status), rather than simply printing the command. See sys-call+.

    The new macro verify-guards+ extends the functionality of verify-guards by permitting macro-aliases (see macro-aliases-table). See verify-guards+. Thanks to Jared Davis for requesting this feature and suggesting the use of make-event in its implementation. We have also modified verify-guards to print a friendlier error message when its argument is a macro-alias.

    See last-prover-steps for a new utility that returns the number of prover steps most recently taken.

    HEURISTIC IMPROVEMENTS

    The processing of :use and :by hints has been changed in the following two rather subtle ways, thanks to suggestions from Sol Swords.

    o For :by hints, the simplest check was an equality check, rather than a more general subsumption check. That equality check was made after removing so-called ``guard holders'' (must-be-equal, prog2$, ec-call, the) from both the previous theorem and the purported theorem. Now, guard-holder removal has been strengthened, so that the results are also put into so-called quote-normal form, for example replacing (cons '3 '4) by '(3 . 4).

    o For a lemma-instance provided to a :use or :by hint that is a :functional-instance, if a :do-not hint (see hints) has specified that preprocess-clause is not to be used, then preprocessing will not be used on the constraints.

    We eliminated certain warnings about being ``weak'' for every :type-prescription rule whose conclusion designates that the function call can be equal to one of its arguments, e.g., (or (integerp (foo y)) (equal (foo y) y)). In many cases (such as the one above), such warnings about ``weak'' simply aren't correct.

    BUG FIXES

    Fixed a soundness bug that was permitting a stobj to be bound by a let or mv-let form, without being among the outputs of that form. Thanks to Jen Davis and Dave Greve for reporting this bug. Their report included an example which forms the basis for a proof of nil, included as a comment in the form (deflabel note-6-3 ...) in ACL2 source file ld.lisp.

    (GCL only) Fixed an obscure soundness bug due to an error in the GCL implementation of set-debugger-enable. For details, see the relevant comment in the ACL2 source code under (deflabel note-6-3 ...).

    Fixed a bug in the case of a field of a (concrete) stobj that is an abstract stobj (see nested-stobjs). Thanks to David Rager for bringing this bug to our attention.

    Splitter output for type if-intro (see splitter) could formerly occur even when at most one subgoal is generated. This has been fixed.

    Fixed a bug in wof, hence in psof (which uses wof), that was causing the printing of a bogus error message.

    A small logical bug has been fixed in the logical definition of sys-call-status. Formerly it always returned (mv nil state) whenever the oracle of the state is non-empty (see state).

    Fixed a bug that was causing an error upon evaluation of the form (set-prover-step-limit nil). Thanks to David Russinoff for reporting this error.

    The :measure (if supplied) is now ignored when checking redundancy with respect to a non-recursive definition that is not defined within a mutual-recursion. (See redundant-events and see xargs.) It had been possible to get a low-level ACL2 error in this situation. Thanks to Jared Davis for reporting this bug with a helpful example.

    Eliminated a potential error when using comp to compile an uncompiled function defined under progn!, which we observed in LispWorks.

    CHANGES AT THE SYSTEM LEVEL

    The ACL2 sources are now publicly available between ACL2 releases, using svn; see the new ``acl2-devel'' project hosted by Google code at http://acl2-devel.googlecode.com. Although such a copy of ACL2 is likely to work well with the latest svn (trunk) revision of the ACL2 community books (see community-books), please take seriously the warning message printed at startup: ``The authors of ACL2 consider svn distributions to be experimental; they may be incomplete, fragile, and unable to pass our own regression.'' That message also provides instructions for bug reports. If you decide to use svn versions of either the community books or ACL2, then you should use both, as they tend to be kept in sync. We fully expect ACL2 releases to continue from time to time, as usual. Thanks to Jared Davis for his efforts in setting up the new acl2-devel project and svn repository, and to him and David Rager for convincing us to distribute ACL2 sources via svn between releases.

    Thanks to a suggestion from Jared Davis, over 30 built-in functions are now declared to be inline in order to boost performance. (The list may be found by searching ACL2 source file axioms.lisp for ``(declaim (inline''.)

    Better support has been provided for command line arguments, especially those supplied directly by the user when calling ACL2. For one, problems with quoting have been solved using "$@" in place of $*. Also, the function save-exec now allows specification of arguments, both for the host Lisp as well as ``inert'' arguments that can be passed along to calls of programs (as with sys-call). A keyword argument, :return-from-lp, specifies a form to evaluate before quitting the read-eval-print loop at startup. See save-exec. Also see the source function user-args-string and its comments, source file acl2-init.lisp, for more information. Thanks to Jared Davis for suggesting the use of "$@", as well as modifications to save-exec and helpful conversations about that.

    A rather extensive overhaul has taken place for the function proclaiming mechanism. As before, this is only used when the host Lisp is GCL. However, building an executable is now faster for some Lisps, including GCL, by avoiding repeated recompilation and perhaps repeated initialization.

    (CCL only) We increased stack sizes when the host Lisp is CCL. The default for recent CCL versions is equivalent to specifying `-Z 2M' on the command line, but saved ACL2 scripts (including experimental versions ACL2(h), ACL2(p), ACL2(r), and combinations of them) to `-Z 64M', representing a 32-fold increase. Thanks to Jared Davis for pointing us to community books file books/centaur/ccl-config.lsp and to Sol Swords for helpful discussions.

    (SBCL only) Fixed save-exec for host Lisp SBCL to provide the same export of variable SBCL_HOME that was provided in the original saved_acl2 script.

    (GCL only) We made changes, following suggestions from Camm Maguire (whom we thank for these suggestions), to support ACL2 builds on recent versions of GCL (2.6.8 and 2.6.10; we recommend against using GCL 2.6.9, since issues there were fixed in 2.6.10). Specifically, we no longer set the hole size, and we allocate contiguous pages sufficient to run an ACL2 regression without failing due to memory limitations.

    EMACS SUPPORT

    Modified file emacs/emacs-acl2.el to eliminate some warnings that were appearing in a recent Emacs version, replacing (end-of-buffer) by (goto-char (point-max)) and next-line by forward-line. Thanks to Warren Hunt for bringing the warnings to our attention.

    EXPERIMENTAL/ALTERNATE VERSIONS

    (Allegro CL only) ACL2(h) now avoids blow-ups in hash table sizes that could be caused by hons-shrink-alist. Thanks to Jared Davis for helping to debug this problem, and to David Rager for contributing the community book books/parsers/earley/earley-parser.lisp, which highlighted this problem.

    (SBCL only) Fixed a bug that was causing a Lisp break after turning on waterfall-parallelism. Thanks to David Rager for confirming that our proposed fix is correct.




    acl2-sources/doc/HTML/NOTE1.html0000664002132200015000000000303612222333530015575 0ustar kaufmannacl2 NOTE1.html -- ACL2 Version 6.3

    NOTE1

    Acl2 Version 1.1 Notes
    Major Section:  RELEASE-NOTES
    

    The new features are extensively documented. The relevant topics are:

    Some Related Topics

    • BOOKS -- files of ACL2 event forms

    • GUARD -- restricting the domain of a function

    • MORE -- your response to :doc or :more's ``(type :more...)''

    • REDUNDANT-EVENTS -- allowing a name to be introduced ``twice''

    It is especially important to read all of of the documentation for books before trying to use books. However, the new :more keyword command is so handy for reading long documentation strings that we recommend you start with :doc more if reading at the terminal. Some documentation has been written for guards which you might find interesting.




    acl2-sources/doc/HTML/NOTE2.html0000664002132200015000000000456612222333530015607 0ustar kaufmannacl2 NOTE2.html -- ACL2 Version 6.3

    NOTE2

    Acl2 Version 1.2 Notes
    Major Section:  RELEASE-NOTES
    

    Hacker mode has been eliminated and programming mode has been added. Programming mode is unsound but does syntax checking and permits redefinitions of names. See :doc load-mode and :doc g-mode.

    The arguments to ld have changed. Ld is now much more sophisticated. See ld.

    For those occasions on which you wish to look at a large list structure that you are afraid to print, try (walkabout x state), where x is an Acl2 expression that evaluates to the structure in question. I am afraid there is no documentation yet, but it is similar in spirit to the Interlisp structure editor. You are standing on an object and commands move you around in it. E.g., 1 moves you to its first element, 2 to its second, etc.; 0 moves you up to its parent; nx and bk move you to its next sibling and previous sibling; pp prettyprints it; q exits returning nil; = exits returning the thing you're standing on; (= symb) assigns the thing you're standing on to the state global variable symb.

    Several new hints have been implemented, including :by and :do-not. The old :do-not-generalize has been scrapped in favor of such new hints as :do-not (generalize elim). :By lets you say ``this goal is subsumed by'' a given lemma instance. The :by hint also lets you say ``this goal can't be proved yet but skip it and see how the rest of the proof goes.'' See hints.




    acl2-sources/doc/HTML/NOTE3.html0000664002132200015000000001671612222333530015610 0ustar kaufmannacl2 NOTE3.html -- ACL2 Version 6.3

    NOTE3

    Acl2 Version 1.3 Notes
    Major Section:  RELEASE-NOTES
    

    Programming mode has been eliminated. Instead, all functions have a ``color'' which indicates what can be done with the function. For example, :red functions can be executed but have no axioms describing them. Thus, :red functions can be introduced after passing a simple syntactic check and they can be redefined without undoing. But nothing of consequence can be proved about them. At the other extreme are :gold functions which can be executed and which also have passed both the termination and the guard verification proofs. The color of a function can be specified with the new xargs keyword, :color, which, if omitted defaults to the global setting of ld-color. Ld-color replaces load-mode. Setting ld-color to :red causes behavior similar to the old :g-mode. Setting ld-color to :gold causes behavior similar to the old :v-mode. It is possible to prototype your system in :red and then convert :red functions to :blue individually by calling verify-termination on them. They can then be converted to :gold with verify-guards. This allows us to undertake to verify the termination and guards of system functions. See :doc color for an introduction to the use of colors.

    Type prescription rules have been added. Recall that in Nqthm, some rewrite rules were actually stored as ``type-prescriptions.'' Such rules allow the user to inform Nqthm's primitive type mechanism as to the kinds of shells returned by a function. Earlier versions of Acl2 did not have an analogous kind of rule because Acl2's type mechanism is complicated by guards. Version 1.3 supports type-prescription rules. See type-prescription.

    Three more new rule-classes implement congruence-based rewriting. It is possible to identify a binary relation as an equivalence relation (see equivalence), to show that one equivalence relation refines another (see refinement) and to show that a given equivalence relation is maintained when rewriting a given function call, e.g., (fn ...xk...), by maintaining another equivalence relation while rewriting the kth argument (see congruence). If r has been shown to be an equivalence relation and then (implies hyps (r (foo x) (bar x))) is proved as a :rewrite rule, then instances of (foo x) will be replaced by corresponding instances of (bar x) provided the instance occurs in a slot where the maintainence of r-equivalence is known to be sufficient and hyps can be established as usual.

    In Version 1.2, rule-classes were simple keywords, e.g., :rewrite or :elim. In Version 1.3, rule-classes have been elaborated to allow you to specify how the theorem ought to be used as a rule. That is, the new rule-classes allows you to separate the mathematical statement of the formula from its interpretation as a rule. See rule-classes.

    Rules used to be named by symbols, e.g., car and car-cons were the names of rules. Unfortunately, this was ambiguous because there are three rules associated with function symbols: the symbolic definition, the executable counterpart, and the type-prescription; many different rules might be associated with theorems, depending on the rule classes. In Version 1.3 rules are named by ``runes'' (which is just short hand for ``rule names''). Example runes are (:definition car), (:executable-counterpart car), and (:type-prescription car . 1). Every rule added by an event has a different name and you can enable and disable them independently. See rune and see theories.

    The identity function force, of one argument, has been added and given a special interpretation by the functions responsible for establishing hypotheses in backchaining: When the system fails to establish some hypothesis of the form (force term), it simply assumes it is true and goes on, delaying until later the establishment of term. In particular, pushes a new subgoal to prove term in the current context. When that subgoal is attacked, all of the resources of the theorem prover, not just rewriting, are brought to bear. Thus, for example, if you wish to prove the rule (implies (good-statep s) (equal (exec s n) s')) and it is your expectation that every time exec appears its first argument is a good-statep then you might write the rule as (implies (force (good-statep s)) (equal (exec s n) s')). This rule is essentially an unconditional rewrite of (exec s n) to s' that spawns the new goal (good-statep s). See force. Because you can now specify independently how a theorem is used as a rule, you need not write the force in the actual theorem proved. See rule-classes.

    Version 1.3 supports a facility similar to Nqthm's break-lemma. See break-rewrite. You can install ``monitors'' on runes that will cause interactive breaks under certain conditions.

    Acl2 also provides ``wormholes'' which allow you to write functions that cause interaction with the user but which do not require that you have access to state. See wormhole.

    The rewriter now automatically backchains to stronger recognizers. There is no user hook to this feature but it may simplify some proofs with which older versions of Acl2 had trouble. For example, if the rewriter is trying to prove (rationalp (foo a b c)) it is now smart enough to try lemmas that match with (integerp (foo a b c)).




    acl2-sources/doc/HTML/NOTE4.html0000664002132200015000000002325712222333530015607 0ustar kaufmannacl2 NOTE4.html -- ACL2 Version 6.3

    NOTE4

    Acl2 Version 1.4 Notes
    Major Section:  RELEASE-NOTES
    

    Once again ld only takes one required argument, as the bind-flg has been deleted.

    Three commands have been added in the spirit of :pe. :Pe! is similar to :pe but it prints all events with the given name, rather than just the most recent. The command :pf prints the corollary formula corresponding to a name or rune. The command :pl (print lemmas) prints rules whose top function symbol is the given name. See pe!, see pf, and see pl.

    Book naming conventions have been changed somewhat. The once-required .lisp extension is now prohibited! Directories are supported, including a notion of ``connected book directory''. See book-name. Also, the second argument of certify-book is now optional, defaulting to 0.

    Compilation is now supported inside the Acl2 loop. See comp and see set-compile-fns.

    The default color is now part of the Acl2 world; see :doc default-color. Ld-color is no longer an ld special. Instead, colors are events; see the documentation for red, pink, blue, and gold.

    A table exists for controlling whether Acl2 prints comments when it forces hypotheses of rules; see :doc force-table. Also, it is now possible to turn off the forcing of assumptions by disabling the definition of force; see force.

    The event defconstant is no longer supported, but a very similar event, defconst, has been provided in its place. See defconst.

    The event for defining congruence relations is now defcong (formerly, defcon).

    Patterns are now allowed in :expand hints. See the documentation for :expand inside the documentation for hints.

    We have improved the way we report rules used by the simplifier. All runes of the same type are reported together in the running commentary associated with each goal, so that for example, executable counterparts are listed separately from definitions, and rewrite rules are listed separately from linear rules. The preprocessor now mentions ``simple'' rules; see simple.

    The mechanism for printing warning messages for new rewrite rules, related to subsumption, now avoids worrying about nonrecursive function symbols when those symbols are disabled. These messages have also been eliminated for the case where the old rule is a :definition rule.

    Backquote has been modified so that it can usually provide predictable results when used on the left side of a rewrite rule.

    Time statistics are now printed even when an event fails.

    The Acl2 trace package has been modified so that it prints using the values of the Lisp globals *print-level* and *print-length* (respectively).

    Table has been modified so that the :clear option lets you replace the entire table with one that satisfies the val and key guards (if any); see table.

    We have relaxed the translation rules for :measure hints to defun, so that the the same rules apply to these terms that apply to terms in defthm events. In particular, in :measure hints mv is treated just like list, and state receives no special handling.

    The loop-stopper test has been relaxed. The old test required that every new argument be strictly less than the corresponding old argument in a certain term-order. The new test uses a lexicographic order on term lists instead. For example, consider the following rewrite rule.

      (equal
       (variable-update var1
                        val1 (variable-update var2 val2 vs))
       (variable-update var2
                        val2 (variable-update var1 val1 vs)))
    
    This rule is permutative. Now imagine that we want to apply this rule to the term
      (variable-update u y (variable-update u x vs)).
    
    Since the actual corresponding to both var1 and var2 is u, which is not strictly less than itself in the term-order, this rule would fail to be applied in this situation when using the old test. However, since the pair (u x) is lexicographically less than the pair (u y) with respect to our term-order, the rule is in fact applied using our new test.

    Messages about events now contain a space after certain left parentheses, in order to assist emacs users. For example, the event

      (defthm abc (equal (+ (len x) 0) (len x)))
    
    leads to a summary containing the line
      Form:  ( DEFTHM ABC ...)
    
    and hence, if you search backwards for ``(defthm abc'', you won't stop at this message.

    More tautology checking is done during a proof; in fact, no goal printed to the screen, except for the results of applying :use and :by hints or the top-level goals from an induction proof, are known to Acl2 to be tautologies.

    The ld-query-control-alist may now be used to suppress printing of queries; see ld-query-control-alist.

    Warning messages are printed with short summary strings, for example the string ``Use'' in the following message.

      Acl2 Warning [Use] in DEFTHM:  It is unusual to :USE an enabled
      :REWRITE or :DEFINITION rule, so you may want to consider
      disabling FOO.
    
    At the end of the event, just before the time is printed, all such summary strings are printed out.

    The keyword command :u has been introduced as an abbreviation for :ubt :max. Printing of query messages is suppressed by :u.

    The keyword :cheat is no longer supported by any event form.

    Some irrelevant formals are detected; see irrelevant-formals.

    A bug in the application of metafunctions was fixed: now if the output of a metafunction is equal to its input, the application of the metafunction is deemed unsuccessful and the next metafunction is tried.

    An example has been added to the documentation for equivalence to suggest how to make use of equivalence relations in rewriting.

    The following Common Lisp functions have been added to Acl2: alpha-char-p, upper-case-p, lower-case-p, char-upcase, char-downcase, string-downcase, string-upcase, and digit-charp-p.

    A documentation section called proof-checker has been added for the interactive facility, whose documentation has been slightly improved. See in particular the documentation for proof-checker, verify, and macro-command.

    A number of events that had been inadvertently disallowed in books are now permitted in books. These are: defcong, defcor, defequiv, defrefinement, defstub, and verify-termination.




    acl2-sources/doc/HTML/NOTE5.html0000664002132200015000000004377212222333530015614 0ustar kaufmannacl2 NOTE5.html -- ACL2 Version 6.3

    NOTE5

    Acl2 Version 1.5 Notes
    Major Section:  RELEASE-NOTES
    

    Acl2 now allows ``complex rationals,'' which are complex numbers whose real parts are rationals and whose imaginary parts are non-zero rationals. See complex.

    A new way of handling forced hypotheses has been implemented. Rather than cause a case split at the time the force occurs, we complete the main proof and then embark on one or more ``forcing rounds'' in which we try to prove the forced hypotheses. See forcing-round. To allow us to compare the new handling of force with the old, Version 1.5 implements both and uses a flag in state to determine which method should be used. Do (assign old-style-forcing t) if you want force to be handled as it was in Version 1.4. However, we expect to eliminate the old-style forcing eventually because we think the new style is more effective. To see the difference between the two approaches to forcing, try proving the associativity of append under both settings of old-style-forcing. To get the new behavior invoke:

    (thm (implies (and (true-listp a) (true-listp b))
                  (equal (append (append a b) c)
                         (append a (append b c)))))
    
    Then (assign old-style-forcing t) and invoke the thm command above again.

    A new :cases hints allows proof by cases. See hints.

    Include-book and encapsulate now restore the acl2-defaults-table when they complete. See include-book and see encapsulate.

    The guards on many Acl2 primitives defined in axioms.lisp have been weakened to permit them to be used in accordance with lisp custom and tradition.

    It is possible to attach heuristic filters to :rewrite rules to limit their applicability. See syntaxp.

    A tutorial has been added (but as of Version_3.6.1 it has become obsolete).

    Events now print the Summary paragraph listing runes used, time, etc., whether they succeed or fail. The format of the ``failure banner'' has been changed but still has multiple asterisks in it. Thm also prints a Summary, whether it succeeds or fails; but thm is not an event.

    A new event form skip-proofs has been added; see skip-proofs.

    A user-specific customization facility has been added in the form of a book that is automatically included, if it exists on the current directory. See acl2-customization.

    A facility for conditional metalemmas has been implemented; see meta.

    The acceptable values for ld-skip-proofsp have changed. In the old version (Version 1.4), a value of t meant that proofs and local events are to be skipped. In Version 1.5, a value of t means proofs (but not local events) are to be skipped. A value of 'include-book means proofs and local events are to be skipped. There are two other, more obscure, acceptable values. See ld-skip-proofsp.

    In order to turn off the forcing of assumptions, one should now disable the :executable-counterpart of force (rather than the :definition of force, as in the previous release); see force.

    The macros enable-forcing and disable-forcing make it convenient to enable or disable forcing. See enable-forcing and see disable-forcing.

    The new commands :pr and :pr! print the rules created by an event or command. See pr and see pr!.

    The new history commands :puff and :puff* will replace a compound command such as an encapsulate or include-book by the sequence of events in it. That is, they ``puff up'' or ``lift'' the subevents of a command to the command level, eliminating the formerly superior command and lengthening the history. This is useful if you want to ``partially undo'' an encapsulate or book or other compound command so you can experiment. See puff and see puff*.

    Theory expressions now are allowed to use the free variable world and prohibited from using the free variable state. See theories, although it is essentially the same as before except it mentions world instead of state. See world for a discussion of the Acl2 logical world. Allowing in-theory events to be state-sensitive violated an important invariant about how books behaved.

    Table keys and values now are allowed to use the free variable world and prohibited from using the free variable state. See the note above about theory expressions for some explanation.

    The macro for minus, -, used to expand (- x 3) to (+ x -3) and now expands it to (+ -3 x) instead. The old macro, if used in the left-hand sides of rewrite rules, produced inapplicable rules because the constant occurs in the second argument of the +, but potential target terms generally had the constant in the first argument position because of the effect of commutativity-of-+.

    A new class of rule, :linear-alias rules, allows one to implement the nqthm package and similar hacks in which a disabled function is to be known equivalent to an arithmetic function.

    A new class of rule, :built-in-clause rules, allows one to extend the set of clauses proved silently by defun during measure and guard processing. See built-in-clause.

    The new command pcb! is like pcb but sketches the command and then prints its subsidiary events in full. See pcb!.

    :Rewrite class rules may now specify the :loop-stopper field. See rule-classes and see loop-stopper.

    The rules for how loop-stoppers control permutative rewrite rules have been changed. One effect of this change is that now when the built-in commutativity rules for + are used, the terms a and (- a) are permuted into adjacency. For example, (+ a b (- a)) is now normalized by the commutativity rules to (+ a (- a) b); in Version 1.4, b was considered syntactically smaller than (- a) and so (+ a b (- a)) is considered to be in normal form. Now it is possible to arrange for unary functions be be considered ``invisible'' when they are used in certain contexts. By default, unary-- is considered invisible when its application appears in the argument list of binary-+. See loop-stopper and see :DOC set-invisible-fns-table.

    Extensive documentation has been provided on the topic of Acl2's ``term ordering.'' See term-order.

    Calls of ld now default ld-error-action to :return rather than to the current setting.

    The command descriptor :x has been introduced and is synonymous with :max, the most recently executed command. History commands such as :pbt print a :x beside the most recent command, simply to indicate that it is the most recent one.

    The command descriptor :x-23 is synonymous with (:x -23). More generally, every symbol in the keyword package whose first character is #\x and whose remaining characters parse as a negative integer is appropriately understood. This allows :pbt :x-10 where :pbt (:max -10) or :pbt (:here -10) were previously used. The old forms are still legal.

    The order of the arguments to defcong has been changed.

    The simplifier now reports the use of unspecified built-in type information about the primitives with the phrase ``primitive type reasoning.'' This phrase may sometimes occur in situations where ``propositional calculus'' was formerly credited with the proof.

    The function pairlis has been replaced in the code by a new function pairlis$, because Common Lisp does not adequately specify its pairlis function.

    Some new Common Lisp functions have been added, including logtest, logcount, integer-length, make-list, remove-duplicates, string, and concatenate. The source file /slocal/src/acl2/axioms.lisp is the ultimate reference regarding Common Lisp functions in Acl2.

    The functions defuns and theory-invariant have been documented. See defuns and see theory-invariant.

    A few symbols have been added to the list *acl2-exports*.

    A new key has been implemented for the acl2-defaults-table, :irrelevant-formals-ok. See set-irrelevant-formals-ok.

    The connected book directory, cbd, must be nonempty and begin and end with a slash. It is set (and displayed) automatically upon your first entry to lp. You may change the setting with set-cbd. See cbd.

    :oops will undo the last :ubt. See oops.

    Documentation has been written about the ordinals. See :DOC e0-ordinalp and see :DOC e0-ord-<. [Note added later: Starting with Version_2.8, instead see o-p and see o<.

    The color events -- (red), (pink), (blue), and (gold) -- may no longer be enclosed inside calls of local, for soundness reasons. In fact, neither may any event that sets the acl2-defaults-table. See embedded-event-form.

    See ld-keyword-aliases for an example of how to change the exit keyword from :q to something else.

    The attempt to install a monitor on :rewrite rules stored as simple abbreviations now causes an error because the application of abbreviations is not tracked.

    A new message is sometimes printed by the theorem prover, indicating that a given simplification is ``specious'' because the subgoals it produces include the input goal. In Version 1.4 this was detected but not reported, causing behavior some users found bizarre. See specious-simplification.

    :Definition rules are no longer always required to specify the :clique and :controller-alist fields; those fields can be defaulted to system-determined values in many common instances. See definition.

    A warning is printed if a macro form with keyword arguments is given duplicate keyword values. Execute (thm t :doc nil :doc "ignored") and read the warning printed.

    A new restriction has been placed on encapsulate. Non-local recursive definitions inside the encapsulate may not use, in their tests and recursive calls, the constrained functions introduced by the encapsulate. See subversive-recursions. (Note added in Version 2.3: Subversive recursions were first recognized by us here in Version 1.5, but our code for recognizing them was faulty and the bug was not fixed until Version 2.3.)

    The events defequiv, defcong, defrefinement, and defevaluator have been reimplemented so that they are just macros that expand into appropriate defthm or encapsulate events; they are no longer primitive events. See the documentation of each affected event.

    The defcor event, which was a shorthand for a defthm that established a corollary of a named, previously proved event, has been eliminated because its implementation relied on a technique we have decided to ban from our code. If you want the effect of a defcor in Version 1.5 you must submit the corresponding defthm with a :by hint naming the previously proved event.

    Error reporting has been improved for inappropriate in-theory hints and events, and for syntax errors in rule classes, and for non-existent filename arguments to ld.

    Technical Note: We now maintain the Third Invariant on type-alists, as described in the Essay on the Invariants on Type-alists, and Canonicality. This change will affect some proofs, for example, by causing a to rewrite more quickly to c when (equiv a b) and (equiv b c) are both known and c is the canonical representative of the three.




    acl2-sources/doc/HTML/NOTE6.html0000664002132200015000000001741412222333530015607 0ustar kaufmannacl2 NOTE6.html -- ACL2 Version 6.3

    NOTE6

    Acl2 Version 1.6 Notes
    Major Section:  RELEASE-NOTES
    

    A new key has been implemented for the acl2-defaults-table, :ignore-ok. See set-ignore-ok.

    It is now legal to have color events, such as (red), in the portcullis of a book. More generally, it is legal to set the acl2-defaults-table in the portcullis of a book. For example, if you execute :red and then certify a book, the event (red) will show up in the portcullis of that book, and hence the definitions in that book will all be red (except when overridden by appropriate declarations or events). When that book is included, then as always, its portcullis must first be ``raised,'' and that will cause the default color to become red before the events in the book are executed. As always, the value of acl2-defaults-table immediately after execution of an include-book, certify-book, or encapsulate form will be the same as it was immediately before execution (and hence, so will the default color). See portcullis and, for more about books, see books.

    A theory ground-zero has been defined to contain exactly those rules that are enabled when Acl2 starts up. See ground-zero.

    The function nth is now enabled, correcting an oversight from Version 1.5.

    Customization files no longer need to meet the syntactic restrictions put on books; rather, they can contain arbitrary Acl2 forms. See acl2-customization.

    Structured directory names and structured file names are supported; see especially the documentation for pathname, book-name, and cbd.

    Acl2 now works with some Common Lisp implementations other than akcl, including Lucid, Allegro, and MCL.

    A facility has been added for displaying proof trees, especially using emacs; see proof-tree.

    There is a considerable amount of new documentation, in particular for the printing functions fmt, fmt1, and fms, and for the notion of Acl2 term (see term).

    It is possible to introduce new well-founded relations, to specify which relation should be used by defun, and to set a default relation. See well-founded-relation.

    It is possible to make functions suggest new inductions. See induction.

    It is possible to change how Acl2 expresses type-set information; in particular, this affects what clauses are proved when forced assumptions are generated. See type-set-inverter.

    A new restriction has been added to defpkg, having to do with undoing. If you undo a defpkg and define the same package name again, the imports list must be identical to the previous imports or else an explanatory error will occur. See package-reincarnation-import-restrictions.

    Theory-invariant and set-irrelevant-formals-ok are now embedded event forms.

    The command :good-bye may now be used to quit entirely out of Lisp, thus losing your work forever. This command works in akcl but may not work in every Common Lisp.

    A theory ground-zero has been added that contains exactly the enabled rules in the startup theory. See ground-zero.

    Define-pc-macro and define-pc-atomic-macro now automatically define :red functions. (It used to be necessary, in general, to change color to :red before invoking these.)

    For a proof of the well-foundedness of e0-ord-< on the e0-ordinalps, see proof-of-well-foundedness. [Note added later: Starting with Version_2.8, o< and o-p replace e0-ord-< and e0-ordinalp, respectively.]

    Free variables are now handled properly for hypotheses of :type-prescription rules.

    When the system is loaded or saved, state is now bound to *the-live-state*.

    Certify-book has been modified so that when it compiles a file, it loads that object file.

    Defstub has been modified so that it works when the color is hot (:red or :pink).

    Several basic, but not particularly commonly used, events have been added or changed. The obscure axiom symbol-name-intern has been modified. The definition of firstn has been changed. Butlast is now defined. The definition of integer-length has been modified. The left-hand side of the rewrite rule rational-implies2 has been changed from (* (numerator x) (/ (denominator x))) to (* (/ (denominator x)) (numerator x)), in order to respect the fact that unary-/ is invisible with respect to binary-*. See loop-stopper.

    The `preprocess' process in the waterfall (see hints for a discussion of the :do-not hint) has been changed so that it works to avoid case-splitting. The `simplify' process refuses to force (see force) when there are if terms, including and and or terms, in the goal being simplified.

    The function apply is no longer introduced automatically by translation of user input to internal form when functions are called on inappropriate explicit values, e.g., (car 3).

    The choice of which variable to use as the measured variable in a recursive definition has been very slightly changed.




    acl2-sources/doc/HTML/NOTE7.html0000664002132200015000000002673012222333530015611 0ustar kaufmannacl2 NOTE7.html -- ACL2 Version 6.3

    NOTE7

    ACL2 Version 1.7 (released October 1994) Notes
    Major Section:  RELEASE-NOTES
    

    Include-book now takes (optionally) an additional keyword argument, indicating whether a compiled file is to be loaded. The default behavior is unchanged, except that a warning is printed when a compiled file is not loaded. See include-book.

    A markup language for documentation strings has been implemented, and many of the source files have been marked up using this language (thanks largely to the efforts of Laura Lawless). See markup. Moreover, there are translators that we have used to provide versions of the ACL2 documentation in info (for use in emacs), html (for Mosaic), and tex (for hardcopy) formats.

    A new event defdoc has been implemented. It is like deflabel, but allows redefinition of doc strings and has other advantages. See defdoc.

    We used to ignore corollaries when collecting up the axioms introduced about constrained functions. That bug has been fixed. We thank John Cowles for bringing this bug to our attention.

    The macro defstub now allows a :doc keyword argument, so that documentation may be attached to the name being introduced.

    A new command nqthm-to-acl2 has been added to help Nqthm users to make the transition to ACL2. See nqthm-to-acl2, which also includes a complete listing of the relevant tables.

    Many function names, especially of the form ``foo-lst'', have been changed in order to support the following convention, for any ``foo'':

    (foo-listp lst) represents the notion (for x in lst always foop x).
    
    A complete list of these changes may be found at the end of this note. All of them except symbolp-listp and list-of-symbolp-listp have the string ``-lst'' in their names. Note also that keyword-listp has been renamed keyword-value-listp.

    Accumulated persistence has been implemented. It is not connected to :brr or rule monitoring. See accumulated-persistence.

    :Trigger-terms has been added for :linear rule classes, so you can hang a linear rule under any addend you want. See linear, which has been improved and expanded.

    ACL2 now accepts 256 characters and includes the Common Lisp functions code-char and char-code. However, ACL2 controls the lisp reader so that #\c may only be used when c is a single standard character or one of Newline, Space, Page, Rubout, Tab. If you want to enter other characters use code-char, e.g., (coerce (list (code-char 7) (code-char 240) #a) 'string). See characters. Note: our current handling of characters makes the set of theorems different under Macintosh Common Lisp (MCL) than under other Common Lisps. We hope to rectify this situation before the final release of ACL2.

    A new table, macro-aliases-table, has been implemented, that associates macro names with function names. So for example, since append is associated with binary-append, the form (disable append) it is interpreted as though it were (disable binary-append). See macro-aliases-table, see add-macro-alias and see remove-macro-alias.

    The implementation of conditional metalemmas has been modified so that the metafunction is applied before the hypothesis metafunction is applied. See meta.

    The Common Lisp functions acons and endp have been defined in the ACL2 logic.

    We have added the symbol declare to the list *acl2-exports*, and hence to the package "ACL2-USER".

    A new hint, :restrict, has been implemented. See hints.

    It used to be that if :ubt were given a number that is greater than the largest current command number, it treated that number the same as :max. Now, an error is caused.

    The table :force-table has been eliminated.

    A command :disabledp (and macro disabledp) has been added; see disabledp.

    Compilation via :set-compile-fns is now suppressed during include-book. In fact, whenever the state global variable ld-skip-proofsp has value 'include-book.

    Here are some less important changes, additions, and so on.

    Unlike previous releases, we have not proved all the theorems in axioms.lisp; instead we have simply assumed them. We have deferred such proofs because we anticipate a fairly major changed in Version 1.8 in how we deal with guards.

    We used to (accidentally) prohibit the ``redefinition'' of a table as a function. That is no longer the case.

    The check for whether a corollary follows tautologically has been sped up, at the cost of making the check less ``smart'' in the following sense: no longer do we expand primitive functions such as implies before checking this propositional implication.

    The command ubt! has been modified so that it never causes or reports an error. See ubt!.

    ACL2 now works in Harlequin LispWorks.

    The user can now specify the :trigger-terms for :linear rules. See linear.

    The name of the system is now ``ACL2''; no longer is it ``Acl2''.

    The raw lisp counterpart of theory-invariant is now defined to be a no-op as is consistent with the idea that it is just a call of table.

    A bug was fixed that caused proof-checker instructions to be executed when ld-skip-proofsp was t.

    The function rassoc has been added, along with a corresponding function used in its guard, r-eqlable-alistp.

    The in-theory event and hint now print a warning not only when certain ``primitive'' :definition rules are disabled, but also when certain ``primitive'' :executable-counterpart rules are disabled.

    The modified version of trace provided by ACL2, for use in raw Lisp, has been modified so that the lisp special variable *trace-alist* is consulted. This alist associates, using eq, values with their print representations. For example, initially *trace-alist* is a one-element list containing the pair (cons state '|*the-live-state*|).

    The system now prints an observation when a form is skipped because the default color is :red or :pink. (Technically: when-cool has been modified.)

    Additional protection exists when you submit a form to raw Common Lisp that should only be submitted inside the ACL2 read-eval-print loop.

    Here is a complete list of the changes in function names described near the top of this note, roughly of the form

    foo-lst --> foo-listp
    
    meaning: the name ``foo-lst'' has been changed to ``foo-listp.''
    symbolp-listp    --> symbol-listp
    list-of-symbolp-listp  --> symbol-list-listp
                           {for consistency with change to symbol-listp}
    rational-lst     --> rational-listp
                         {which in fact was already defined as well}
    integer-lst      --> integer-listp
    character-lst    --> character-listp
    stringp-lst      --> string-listp
    32-bit-integer-lst   --> 32-bit-integer-listp
    typed-io-lst     --> typed-io-listp
    open-channel-lst --> open-channel-listp
    readable-files-lst   --> readable-files-listp
    written-file-lst --> written-file-listp
    read-file-lst    --> read-file-listp
    writeable-file-lst   --> writable-file-listp
                         {note change in spelling of ``writable''}
    writeable-file-lst1  --> writable-file-listp1
    pseudo-termp-lst     --> pseudo-term-listp
    hot-termp-lst --> hot-term-listp {by analogy with pseudo-term-listp}
    weak-termp-lst   --> weak-term-listp
    weak-termp-lst-lst   --> weak-termp-list-listp
    ts-builder-case-lstp -> ts-builder-case-listp
    quotep-lst       --> quote-listp
    termp-lst        --> term-listp
    instr-lst        --> instr-listp
    spliced-instr-lst    --> spliced-instr-listp
    rewrite-fncallp-lst  --> rewrite-fncallp-listp
    every-occurrence-equiv-hittablep1-lst -->
                every-occurrence-equiv-hittablep1-listp
    some-occurrence-equiv-hittablep1-lst  -->
                some-occurrence-equiv-hittablep1-listp
                {by analogy with the preceding, even though it's a
                 ``some'' instead of ``all'' predicate]
    almost-quotep1-lst   --> almost-quotep1-listp
    ffnnames-subsetp-lst --> ffnnames-subsetp-listp
    boolean-lstp     --> boolean-listp
    subst-expr1-lst-okp  --> subst-expr1-ok-listp
    




    acl2-sources/doc/HTML/NOTE8-UPDATE.html0000664002132200015000000000663512222333530016574 0ustar kaufmannacl2 NOTE8-UPDATE.html -- ACL2 Version 6.3

    NOTE8-UPDATE

    ACL2 Version 1.8 (Summer, 1995) Notes
    Major Section:  RELEASE-NOTES
    

    ACL2 can now use Ordered Binary Decision Diagram technology. See bdd. There is also a proof-checker bdd command.

    ACL2 is now more respectful of the intention of the function hide. In particular, it is more careful not to dive inside any call of hide during equality substitution and case splitting.

    The ld special (see ld) ld-pre-eval-print may now be used to turn off printing of input forms during processing of encapsulate and certify-book forms, by setting it to the value :never, i.e., (set-ld-pre-eval-print :never state). See ld-pre-eval-print.

    The TUTORIAL documentation section (now obsolete) has, with much help from Bill Young, been substantially improved to a bona fide introduction.

    The term pretty-printer has been modified to introduce (<= X Y) as an abbreviation for (not (< Y X)).

    Forward chaining and linear arithmetic now both benefit from the evaluation of ground subterms.

    A new macro set-inhibit-output-lst has been defined. This should be used when setting the state global inhibit-output-lst; see set-inhibit-output-lst and see proof-tree.

    The test for redundancy in definitions includes the guard and type declarations. See redundant-events.

    See generalized-booleans for a discussion of a potential soundness problem for ACL2 related to the question: Which Common Lisp functions are known to return Boolean values?

    Here we will put some less important changes, additions, and so on.

    A bug has been fixed so that now, execution of :comp t (see comp) correctly handles non-standard characters.

    A bug in digit-char-p has been fixed, so that the ``default'' is nil rather than 0.

    True-listp now tests the final cdr against nil using eq instead of equal, for improved efficiency. The logical meaning is, however, unchanged.

    Put-assoc-equal has been added to the logic (it used to have :defun-mode :program, and has been documented.




    acl2-sources/doc/HTML/NOTE8.html0000664002132200015000000005211412222333530015605 0ustar kaufmannacl2 NOTE8.html -- ACL2 Version 6.3

    NOTE8

    ACL2 Version 1.8 (May, 1995) Notes
    Major Section:  RELEASE-NOTES
    

    See note8-update for yet more recent changes.

    Guards have been eliminated from the ACL2 logic. A summary is contained in this brief note. Also see defun-mode and see set-guard-checking.

    Guards may be included in defuns as usual but are ignored from the perspective of admission to the logic: functions must terminate on all arguments.

    As in Nqthm, primitive functions, e.g., + and car, logically default unexpected arguments to convenient values. Thus, (+ 'abc 3) is 3 and (car 'abc) is nil. See programming, and see the documentation for the individual primitive functions.

    In contrast to earlier versions of ACL2, Version 1.8 logical functions are executed at Nqthm speeds even when guards have not been verified. In versions before 1.8, such functions were interpreted by ACL2.

    Colors have been eliminated. Two ``defun-modes'' are supported, :program and :logic. Roughly speaking, :program does what :red used to do, namely, allow you to prototype functions for execution without any proof burdens. :Logic mode does what :blue used to do, namely, allow you to add a new definitional axiom to the logic. A global default-defun-mode is comparable to the old default color. The system comes up in :logic mode. To change the global defun-mode, type :program or :logic at the top-level. To specify the defun-mode of a defun locally use

    (declare (xargs :mode mode)).
    

    The prompt has changed. The initial prompt, indicating :logic mode, is

    ACL2 !>
    
    If you change to :program mode the prompt becomes
    ACL2 p!>
    

    Guards can be seen as having either of two roles: (a) they are a specification device allowing you to characterize the kinds of inputs a function ``should'' have, or (b) they are an efficiency device allowing logically defined functions to be executed directly in Common Lisp. If a guard is specified, as with xargs :guard, then it is ``verified'' at defun-time (unless you also specify xargs :verify-guards nil). Guard verification means what it always has: the input guard is shown to imply the guards on all subroutines in the body. If the guards of a function are verified, then a call of the function on inputs satisfying the guard can be computed directly by Common Lisp. Thus, verifying the guards on your functions will allow them to execute more efficiently. But it does not affect their logical behavior and since you will automatically get Nqthm speeds on unverified logical definitions, most users will probably use guards either as a specification device or only use them when execution efficiency is extremely important.

    Given the presence of guards in the system, two issues are unavoidable. Are guards verified as part of the defun process? And are guards checked when terms are evaluated? We answer both of those questions below.

    Roughly speaking, in its initial state the system will try to verify the guards of a defun if a :guard is supplied in the xargs and will not try otherwise. However, guard verification in defun can be inhibited ``locally'' by supplying the xargs :verify-guards nil. ``Global'' inhibition can be obtained via the :set-verify-guards-eagerness. If you do not use the :guard xargs, you will not need to think about guard verification.

    We now turn to the evaluation of expressions. Even if your functions contain no guards, the primitive functions do and hence you have the choice: when you submit an expression for evaluation do you mean for guards to be checked at runtime or not? Put another way, do you mean for the expression to be evaluated in Common Lisp (if possible) or in the logic? Note: If Common Lisp delivers an answer, it will be the same as in the logic, but it might be erroneous to execute the form in Common Lisp. For example, should (car 'abc) cause a guard violation error or return nil?

    The top-level ACL2 loop has a variable which controls which sense of execution is provided. To turn ``guard checking on,'' by which we mean that guards are checked at runtime, execute the top-level form :set-guard-checking t. To turn it off, do :set-guard-checking nil. The status of this variable is reflected in the prompt.

    ACL2 !>
    
    means guard checking is on and
    ACL2 >
    
    means guard checking is off. The exclamation mark can be thought of as ``barring'' certain computations. The absence of the mark suggests the absence of error messages or unbarred access to the logical axioms. Thus, for example
    ACL2 !>(car 'abc)
    
    will signal an error, while
    ACL2 >(car 'abc)
    
    will return nil.

    Note that whether or not guards are checked at runtime is independent of whether you are operating in :program mode or :logic mode and whether theorems are being proved or not. (Although it must be added that functions defined in :program mode cannot help but check their guards because no logical definition exists.)

    Version 1.8 permits the verification of the guards of theorems, thus insuring that all instances of the theorem will evaluate without error in Common Lisp. To verify the guards of a theorem named name execute the event

    (verify-guards name).
    
    If a theorem's guards have been verified, the theorem is guaranteed to evaluate without error to non-nil in Common Lisp (provided resource errors do not arise).

    Caveat about verify-guards: implies is a function symbol, so in the term (implies p q), p cannot be assumed true when q is evaluated; they are both evaluated ``outside.'' Hence, you cannot generally verify the guards on a theorem if implies is used to state the hypotheses. Use if instead. In a future version of ACL2, implies will likely be a macro.

    See sum-list-example.lisp for a nice example of the use of Version 1.8. This is roughly the same as the documentation for guard-example.

    We have removed the capability to do ``old-style-forcing'' as existed before Version 1.5. See note5.

    NOTE: Some low level details have, of course, changed. One such change is that there are no longer two distinct type prescriptions stored when a function is admitted with its guards verified. So for example, the type prescription rune for binary-append is now

    (:type-prescription binary-append)
    
    while in Versions 1.7 and earlier, there were two such runes:
    (:type-prescription binary-append . 1)
    (:type-prescription binary-append . 2)
    

    Nqthm-style forcing on linear arithmetic assumptions is no longer executed when forcing is disabled.

    Functional instantiation now benefits from a trick also used in Nqthm: once a constraint generated by a :functional-instance lemma instance (see lemma-instance) has been proved on behalf of a successful event, it will not have to be re-proved on behalf of a later event.

    1+ and 1- are now macros in the logic, not functions. Hence, for example, it is ``safe'' to use them on left-hand sides of rewrite rules, without invoking the common warning about the presence of nonrecursive function symbols.

    A new documentation section file-reading-example illustrates how to process forms in a file.

    A new proof-checker command forwardchain has been added; see acl2-pc::forwardchain.

    It is now possible to use quantifiers. See defun-sk and see defchoose.

    There is a new event set-inhibit-warnings, which allows the user to turn off warnings of various types. see set-inhibit-warnings.

    An unsoundness relating encapsulate and :functional-instance hints has been remedied, with a few small effects visible at the user level. The main observable effect is that defaxiom and non-local include-book events are no longer allowed in the scope of any encapsulate event that has a non-empty signature.

    When certify-book is called, we now require that the default defun-mode (see default-defun-mode) be :logic. On a related note, the default defun-mode is irrelevant to include-book; the mode is always set to :logic initially, though it may be changed within the book and reverts to its original value at the conclusion of the include-book. A bug in include-book prevented it from acting this way even though the documentation said otherwise.

    The documentation has been substantially improved. A new section ``Programming'' contains documentation of many useful functions provided by ACL2; see programming. Also, the documentation has been ``marked up'' extensively. Thus in particular, users of Mosaic will find many links in the documentation.

    The symbols force, mv-nth, and acl2-count have been added to the list *acl2-exports*.

    We now permit most names from the main Lisp package to be used as names, except for names that define functions, macros, or constants. See name.

    We have changed the list of imports from the Common Lisp package to ACL2, i.e., the list *common-lisp-symbols-from-main-lisp-package*, to be exactly those external symbols of the Common Lisp package as specified by the draft Common Lisp standard. In order to accommodate this change, we have renamed some ACL2 functions as shown below, but these and other ramifications of this change should be transparent to most ACL2 users.

    warning      --> warning$
    print-object --> print-object$
    

    Proof trees are no longer enabled by default. To start them up, :start-proof-tree.

    We have added the capability of building smaller images. The easiest way to do this on a Unix (trademark of AT&T) system is: make small.

    Here we will put some less important changes, additions, and so on.

    We have added definitions for the Common Lisp function position (for the test eql), as well as corresponding versions position-equal and position-eq that use tests equal and eq, respectively. See position, see position-equal, and see position-eq.

    The defthm event rational-listp-implies-rationalp-car no longer exists.

    We fixed a bug in the hint mechanism that applied :by, :cases, and :use hints to the first induction goal when the prover reverted to proving the original goal by induction.

    We fixed a bug in the handling of (set-irrelevant-formals-ok :warn).

    In support of removing the old-style forcing capability, we deleted the initialization of state global old-style-forcing and deleted the definitions of recover-assumptions, recover-assumptions-from-goal, remove-assumptions1, remove-assumptions, and split-on-assumptions, and we renamed split-on-assumptions1 to split-on-assumptions.

    The special value 'none in the proof-checker commands claim and = has been replaced by :none.

    A bug in the handling of hints by subgoals has been fixed. For example, formerly a :do-not hint could be ``erased'' by a :use hint on a subgoal. Thanks go to Art Flatau for noticing the bug.

    The functions weak-termp and weak-term-listp have been deleted, and their calls have been replaced by corresponding calls of pseudo-termp and pseudo-term-listp. The notion of pseudo-termp has been slightly strenthened by requiring that terms of the form (quote ...) have length 2.

    Performance has been improved in various ways. At the prover level, backchaining through the recognizer alist has been eliminated in order to significantly speed up ACL2's rewriter. Among the other prover changes (of which there are several, all technical): we no longer clausify the input term when a proof is interrupted in favor of inducting on the input term. At the IO level, we have improved performance somewhat by suitable declarations and proclamations. These include technical modifications to the macros mv and mv-let, and introduction of a macro the-mv analogous to the macro the but for forms returning multiple values.

    The function spaces now takes an extra argument, the current column.

    A bug in the proof-checker equiv command was fixed.

    The function intersectp has been deleted, because it was essentially duplicated by the function intersectp-equal.

    We now proclaim functions in AKCL and GCL before compiling books. This should result in somewhat increased speed.

    The function repeat has been eliminated; use make-list instead.

    The proof-checker command expand has been fixed so that it eliminates let (lambda) expressions when one would expect it to.

    A new primitive function, mv-nth, has been introduced. Mv-nth is equivalent to nth and is used in place of nth in the translation of mv-let expressions. This allows the user to control the simplification of mv-let expressions without affecting how nth is treated. In that spirit, the rewriter has been modified so that certain mv-nth expressions, namely those produced in the translation of (mv-let (a b c)(mv x y z) p), are given special treatment.

    A minor bug in untranslate has been fixed, which for example will fix the printing of conjunctions.

    Translate now takes a logicp argument, which indicates whether it enforces the restriction that :program mode functions do not occur in the result.

    The modified version of trace provided by ACL2, for use in raw Lisp, has been modified so that the lisp special variable *trace-alist* has a slightly different functionality. This alist associates, using eq, symbols with the print representations of their values. For example, initially *trace-alist* is a one-element list containing the pair (cons 'state '|*the-live-state*|). Thus, one may cons the pair (cons '*foo* "It's a FOO!") on to *trace-alist*; then until *foo* is defined, this change will have no effect, but after for example

    (defconst *foo* 17)
    
    then trace will print 17 as "It's a FOO!".

    Trace also traces the corresponding logic function.

    Proof-tree display has been improved slightly in the case of successful proofs and certain event failures.

    The function positive-integer-log2 has been deleted.

    The macro skip-proofs now prints a warning message when it is encountered in the context of an encapsulate event or a book. See skip-proofs.

    Some functions related to the-fn and wormhole1 now have defun-mode :program, but this change is almost certain to be inconsequential to all users.




    acl2-sources/doc/HTML/NOTE9.html0000664002132200015000000000715112222333530015607 0ustar kaufmannacl2 NOTE9.html -- ACL2 Version 6.3

    NOTE9

    ACL2 Version 1.9 (Fall, 1996) Notes
    Major Section:  RELEASE-NOTES
    

    By default, when the system is started it is illegal to use the variable STATE as a formal parameter of a function definition. The aim is to prevent novice users from stumbling into the Byzantine syntactic restrictions on that variable symbol. Use

    :set-state-ok t
    
    or, equivalently,
    (set-state-ok t)
    
    to switch back to the old default mode. See set-state-ok

    Set-state-ok is an event that affects the ACL2 defaults table (see acl2-defaults-table). Recall that when books are included, the defaults table is restored to its pre-inclusion state. Thus, while a set-state-ok form will permit the book to define a state-using function, it will not permit the user of the book to make such a definition. We recommend putting (set-state-ok t) in any book that defines a state using function.

    Books certified under Version 1.8 must be recertified under Version 1.9. See :DOC version.

    The simplifier has been made to look out for built-in clauses, whereas in past versions such clauses were only noticed by the ``preprocessor'' at the top of the waterfall. THIS CHANGE MAY PREVENT OLD SCRIPTS FROM REPLAYING! The undesirable side-effect is caused by the fact that :HINTS require you to refer to clauses by their exact name (see goal-spec) and because the new simplifier proves more clauses than before, the goals produced have different names. Thus, if a script uses :HINTS that refer to clauses other than "Goal", e.g., "Subgoal 1.3" then the hint may be applied to a different subgoal than originally intended.

    The use of built-in-clauses has been made more efficient. If a set of clauses arise often in a piece of work, it might be advantageous to build them in even if that results in a large set (hundreds?) of built-in clauses. See built-in-clause

    Wormholes can now be used in :logic mode functions. See wormhole

    It is now possible to provide ``computed hints.'' For example, have you ever wished to say ``in all goals with a name like this, :use that'' or ``if this term is in the subgoal, then :use that''? Well, see computed-hints and the extraordinarily long example in see using-computed-hints.

    Hide terms may be rewritten with :rewrite rules about hide. See hide, where we also now explain why hide terms are sometimes introduced into your proof attempts.

    A bug that sometimes caused the ``non-lazy IF'' hard error message was fixed.

    A bug that sometimes caused a hard error in forward chaining was fixed.

    A bug in print-rules (:pr) was fixed.

    We report the use of :executable-counterparts in the evaluation of SYNTAXP forms.

    Some documentation errors were fixed.

    A bug in parent-tree tracking in add-literal-and-pt was fixed.

    A bug in ok$, go$ and eval$ was fixed.

    Clausify now optimizes (mv-nth 'k (list x0 ... xk ... xn)) to xk.




    acl2-sources/doc/HTML/NQTHM-TO-ACL2.html0000664002132200015000000001723012222333516016642 0ustar kaufmannacl2 NQTHM-TO-ACL2.html -- ACL2 Version 6.3

    NQTHM-TO-ACL2

    ACL2 analogues of Nqthm functions and commands
    Major Section:  DOCUMENTATION
    

    Example Forms:
    :nqthm-to-acl2 prove-lemma   ; Display ACL2 topic(s) and/or print
                                 ; information corresponding to Nqthm
                                 ; PROVE-LEMMA command.
    (nqthm-to-acl2 'prove-lemma) ; Same as above.
    
    General Form:
    (nqthm-to-acl2 name)
    
    where name is a notion documented for Nqthm: either a function in the Nqthm logic, or a command. If there is corresponding information available for ACL2, it will be printed in response to this command. This information is not intended to be completely precise, but rather, is intended to help those familiar with Nqthm to make the transition to ACL2.

    We close with two tables that contain all the information used by this nqthm-to-acl2 command. The first shows the correspondence between functions in the Nqthm logic and corresponding ACL2 functions (when possible); the second is similar, but for commands rather than functions.

    Nqthm functions  -->     ACL2
    ----------------------------------------
    ADD1          -->  1+
    ADD-TO-SET    -->  ADD-TO-SET-EQUAL and ADD-TO-SET-EQ
    AND           -->  AND
    APPEND        -->  APPEND and BINARY-APPEND
    APPLY-SUBR    -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    APPLY$        -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    ASSOC         -->  ASSOC-EQUAL, ASSOC and ASSOC-EQ
    BODY          -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    CAR           -->  CAR
    CDR           -->  CDR
    CONS          -->  CONS
    COUNT         -->  ACL2-COUNT
    DIFFERENCE    -->  -
    EQUAL         -->  EQUAL, EQ, EQL and =
    EVAL$         -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    FALSE         -->  Nqthm's F corresponds to the ACL2 symbol NIL.
    FALSEP        -->  NOT and NULL
    FORMALS       -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    GEQ           -->  >=
    GREATERP      -->  >
    IDENTITY      -->  IDENTITY
    IF            -->  IF
    IFF           -->  IFF
    IMPLIES       -->  IMPLIES
    LEQ           -->  <=
    LESSP         -->  <
    LISTP         -->  CONSP
    LITATOM       -->  SYMBOLP
    MAX           -->  MAX
    MEMBER        -->  MEMBER-EQUAL, MEMBER and MEMBER-EQ
    MINUS         -->  - and UNARY--
    NEGATIVEP     -->  MINUSP
    NEGATIVE-GUTS -->  ABS
    NLISTP        -->  ATOM
    NOT           -->  NOT
    NUMBERP       -->  ACL2-NUMBERP, INTEGERP and RATIONALP
    OR            -->  OR
    ORDINALP      -->  O-P
    ORD-LESSP     -->  O<
    PACK          -->  See intern and coerce.
    PAIRLIST      -->  PAIRLIS$
    PLUS          -->  + and BINARY-+
    QUOTIENT      -->  /
    REMAINDER     -->  REM and MOD
    STRIP-CARS    -->  STRIP-CARS
    SUB1          -->  1-
    TIMES         -->  * and BINARY-*
    TRUE          -->  The symbol T.
    UNION         -->  UNION-EQUAL and UNION-EQ
    UNPACK        -->  See symbol-name and coerce.
    V&C$          -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    V&C-APPLY$    -->  No correspondent, but see the documentation for
                       DEFEVALUATOR and META.
    ZERO          -->  The number 0.
    ZEROP         -->  ZP
    
    ========================================
    
    Nqthm commands   -->     ACL2
    ----------------------------------------
    ACCUMULATED-PERSISTENCE
                  -->  ACCUMULATED-PERSISTENCE
    ADD-AXIOM     -->  DEFAXIOM
    ADD-SHELL     -->  There is no shell principle in ACL2.
    AXIOM         -->  DEFAXIOM
    BACKQUOTE-SETTING
                  -->  Backquote is supported in ACL2, but not
                       currently documented.
    BOOT-STRAP    -->  GROUND-ZERO
    BREAK-LEMMA   -->  MONITOR
    BREAK-REWRITE -->  BREAK-REWRITE
    CH            -->  PBT
                       See also :DOC history.
    CHRONOLOGY    -->  PBT
                       See also :DOC history.
    COMMENT       -->  DEFLABEL
    COMPILE-UNCOMPILED-DEFNS
                  -->  COMP
    CONSTRAIN     -->  See :DOC encapsulate and :DOC local.
    DATA-BASE     -->  Perhaps the closest ACL2 analogue of DATA-BASE
                       is PROPS.  But see :DOC history for a collection
                       of commands for querying the ACL2 database
                       (``world'').  Note that the notions of
                       supporters and dependents are not supported in
                       ACL2.
    DCL           -->  DEFSTUB
    DEFN          -->  DEFUN and DEFMACRO
    DEFTHEORY     -->  DEFTHEORY
    DISABLE       -->  DISABLE
    DISABLE-THEORY
                  -->  See :DOC theories.  The Nqthm command
                       (DISABLE-THEORY FOO) corresponds roughly to the
                       ACL2 command
                       (in-theory (set-difference-theories
                                    (current-theory :here)
                                    (theory 'foo))).
    DO-EVENTS     -->  LD
    DO-FILE       -->  LD
    ELIM          -->  ELIM
    ENABLE        -->  ENABLE
    ENABLE-THEORY -->  See :DOC theories.  The Nqthm command
                       (ENABLE-THEORY FOO) corresponds roughly to the
                       ACL2 command
                       (in-theory (union-theories
                                    (theory 'foo)
                                    (current-theory :here))).
    EVENTS-SINCE  -->  PBT
    FUNCTIONALLY-INSTANTIATE
                  -->  ACL2 provides a form of the :USE hint that
                       corresponds roughly to the
                       FUNCTIONALLY-INSTANTIATE event of Nqthm. See
                       :DOC lemma-instance.
    GENERALIZE    -->  GENERALIZE
    HINTS         -->  HINTS
    LEMMA         -->  DEFTHM
    MAINTAIN-REWRITE-PATH
                  -->  BRR
    MAKE-LIB      -->  There is no direct analogue of Nqthm's notion of
                       ``library.''  See :DOC books for a description
                       of ACL2's mechanism for creating and saving
                       collections of events.
    META          -->  META
    NAMES         -->  NAME
    NOTE-LIB      -->  INCLUDE-BOOK
    PPE           -->  PE
    PROVE         -->  THM
    PROVEALL      -->  See :DOC ld and :DOC certify-book.  The latter
                       corresponds to Nqthm's PROVE-FILE,which may be
                       what you're interested in,really.
    PROVE-FILE    -->  CERTIFY-BOOK
    PROVE-FILE-OUT
                  -->  CERTIFY-BOOK
    PROVE-LEMMA   -->  DEFTHM
                       See also :DOC hints.
    R-LOOP        -->  The top-level ACL2 loop is an evaluation loop as
                       well, so no analogue of R-LOOP is necessary.
    REWRITE       -->  REWRITE
    RULE-CLASSES  -->  RULE-CLASSES
    SET-STATUS    -->  IN-THEORY
    SKIM-FILE     -->  LD-SKIP-PROOFSP
    TOGGLE        -->  IN-THEORY
    TOGGLE-DEFINED-FUNCTIONS
                  -->  EXECUTABLE-COUNTERPART-THEORY
    TRANSLATE     -->  TRANS and TRANS1
    UBT           -->  UBT and U
    UNBREAK-LEMMA -->  UNMONITOR
    UNDO-BACK-THROUGH
                  -->  UBT
    UNDO-NAME     -->  See :DOC ubt.  There is no way to undo names in
                       ACL2 without undoing back through such names.
                       However, see :DOC ld-skip-proofsp for
                       information about how to quickly recover the
                       state.
    




    acl2-sources/doc/HTML/NTH-ALIASES-TABLE.html0000664002132200015000000000326612222333531017312 0ustar kaufmannacl2 NTH-ALIASES-TABLE.html -- ACL2 Version 6.3

    NTH-ALIASES-TABLE

    a table used to associate names for nth/update-nth printing
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (table nth-aliases-table 'st0 'st)
    
    This example associates the symbol st0 with the symbol st. As a result, when the theorem prover prints terms of the form (nth n st0) or (update-nth n val st0), where st is a stobj whose nth accessor function is f-n, then it will print n as *f-n*.

    General Form:
    (table nth-aliases-table 'alias-name 'name)
    
    This event causes alias-name to be treated like name for purposes of the printing of terms that are calls of nth and update-nth. (Note however that name is not recursively looked up in this table.) Both must be symbols other than state. See term, in particular the discussion there of untranslated terms.

    For a convenient way to add entries to this table, see add-nth-alias. To remove entries from the table with ease, see remove-nth-alias.




    acl2-sources/doc/HTML/NTH.html0000664002132200015000000000172412222333524015405 0ustar kaufmannacl2 NTH.html -- ACL2 Version 6.3

    NTH

    the nth element (zero-based) of a list
    Major Section:  ACL2-BUILT-INS
    

    (Nth n l) is the nth element of l, zero-based. If n is greater than or equal to the length of l, then nth returns nil.

    (Nth n l) has a guard that n is a non-negative integer and l is a true-listp.

    Nth is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NTHCDR.html0000664002132200015000000000226012222333524015732 0ustar kaufmannacl2 NTHCDR.html -- ACL2 Version 6.3

    NTHCDR

    final segment of a list
    Major Section:  ACL2-BUILT-INS
    

    (Nthcdr n l) removes the first n elements from the list l.

    The following is a theorem.

    (implies (and (integerp n)
                  (<= 0 n)
                  (true-listp l))
             (equal (length (nthcdr n l))
                    (if (<= n (length l))
                        (- (length l) n)
                      0)))
    
    For related functions, see take and see butlast.

    The guard of (nthcdr n l) requires that n is a nonnegative integer and l is a true list.

    Nthcdr is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NU-REWRITER.html0000664002132200015000000001033012222333521016525 0ustar kaufmannacl2 NU-REWRITER.html -- ACL2 Version 6.3

    NU-REWRITER

    rewriting NTH/UPDATE-NTH expressions
    Major Section:  MISCELLANEOUS
    

    The rewriter contains special provisions for rewriting expressions composed of nth, update-nth, update-nth-array, together with let expressions and other applications of non-recursive functions or lambda expressions. For details see the paper ``Rewriting for Symbolic Execution of State Machine Models'' by J Strother Moore. Also see set-nu-rewriter-mode.

    The ``nu-rewriter'' is a recent addition to the main rewrite engine in ACL2. Consider the expression

     (let ((s (update-nth 1 (new-a x s) s)))
       (let ((s (update-nth 2 (new-b x s) s)))
         (let ((s (update-nth 3 (new-c x s) s)))
           s)))
    
    If the lets in this expression are expanded, a very large expression results because of the duplicate occurrences of s:
    (update-nth 3
                (new-c x
                       (update-nth 2
                                   (new-b x
                                          (update-nth 1
                                                      (new-a x s)
                                                      s))
                                   (update-nth 1
                                               (new-a x s)
                                               s)))
                (update-nth 2
                            (new-b x
                                   (update-nth 1
                                               (new-a x s)
                                               s))
                            (update-nth 1
                                        (new-a x s)
                                        s))).
    
    This expansion of the let expression can be very expensive in space and time. In particular, the (new-a x s) expression might be rewritten many times.

    Now imagine asking what 2nd component of the structure is. That is, consider

     (nth 2
          (let ((s (update-nth 1 (new-a x s) s)))
            (let ((s (update-nth 2 (new-b x s) s)))
              (let ((s (update-nth 3 (new-c x s) s)))
                s))))
    
    The normal ACL2 rewrite engine would answer this question by first rewriting the arguments to the nth expression; in particular, it would expand the nested let expression to the large nested update-nth expression and then, using rules such as
    (defthm nth-update-nth
      (equal (nth m (update-nth n val l))
             (if (equal (nfix m) (nfix n))
                 val (nth m l))))
    
    would reduce the expression to (new-b x (update-nth 1 (new-a x s) s)).

    The purpose of the nu-rewriter is to allow simplifications like this without first expanding the lets. The ``nu'' in the name is an acronym for ``nth/update-nth''. The nu-rewriter knows how to move an nth into a let without expanding the let and how to simplify it if it nestles up against an update-nth.

    There are four characteristics of this problem: the presence of nth, the presence of update-nth, the use of let to provide ``sequential'' updates, and the use of constant indices. Nth and update-nth need not occur explicitly; they may be used inside of definitions of ``wrapper'' functions.

    Because the nu-rewriter changes the order in which things are rewritten, its routine use can make ACL2 unable to reproduce old proofs. It is therefore switched off by default. If your application exhibits the characteristics above, you might wish to switch the nu-rewriter on using set-nu-rewriter-mode.

    More will eventually be written about the nu-rewriter. However, it is described in detail in the paper cited at the beginning of this documentation topic.




    acl2-sources/doc/HTML/NULL.html0000664002132200015000000000152312222333524015523 0ustar kaufmannacl2 NULL.html -- ACL2 Version 6.3

    NULL

    recognizer for the empty list
    Major Section:  ACL2-BUILT-INS
    

    Null is the function that checks whether its argument is nil. For recursive definitions it is often preferable to test for the end of a list using endp instead of null; see endp.

    Null is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/NUMBER-SUBTREES.html0000664002132200015000000000210212222333520017161 0ustar kaufmannacl2 NUMBER-SUBTREES.html -- ACL2 Version 6.3

    NUMBER-SUBTREES

    (number-subtrees x) returns the number of distinct subtrees of X, in the sense of equal
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    In the logic, number-subtrees is defined as the length of cons-subtrees.

    Under the hood, we first hons-copy X to obtain a normed version, then count the number of unique conses in X using an EQ hash table.




    acl2-sources/doc/HTML/NUMERATOR.html0000664002132200015000000000121612222333524016324 0ustar kaufmannacl2 NUMERATOR.html -- ACL2 Version 6.3

    NUMERATOR

    dividend of a ratio in lowest terms
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-numerator):

    (equal (numerator x)
           (if (rationalp x)
               (numerator x)
             0))
    

    Guard for (numerator x):

    (rationalp x)
    




    acl2-sources/doc/HTML/Name_the_Formula_Above.html0000664002132200015000000000070312222333526021273 0ustar kaufmannacl2 Name_the_Formula_Above.html -- ACL2 Version 6.3

    Name the Formula Above

    When the theorem prover explicitly assigns a name, like *1, to a formula, it has decided to prove the formula by induction.




    acl2-sources/doc/HTML/Nontautological_Subgoals.html0000664002132200015000000000115312222333526021753 0ustar kaufmannacl2 Nontautological_Subgoals.html -- ACL2 Version 6.3

    Prover output omits some details

    The theorem prover's proof output is intended to suggest an outline of the reasoning process employed by its proof engine, which is virtually always more than is necessary for the ACL2 user. In particular, the output often omits subgoals that are sufficiently trivial, including tautologies.




    acl2-sources/doc/HTML/Numbers_in_ACL2.html0000664002132200015000000000526012222333526017617 0ustar kaufmannacl2 Numbers_in_ACL2.html -- ACL2 Version 6.3

    Numbers in ACL2

    ACL2 numbers are precisely represented and unbounded. They can be partitioned into the following subtypes:

    Rationals
     Integers
      Positive integers                3
      Zero                             0
      Negative Integers                -3
     Non-Integral Rationals
      Positive Non-Integral Rationals  19/3
      Negative Non-Integral Rationals  -22/7
    Complex Rational Numbers           #c(3 5/2) ; = 3+(5/2)i
    

    Signed integer constants are usually written (as illustrated above) as sequences of decimal digits, possibly preceded by + or -. Decimal points are not allowed. Integers may be written in binary, as in #b1011 (= 23) and #b-111 (= -7). Octal may also be used, #o-777 = -511. Non-integral rationals are written as a signed decimal integer and an unsigned decimal integer, separated by a slash. Complex rationals are written as #c(rpart ipart) where rpart and ipart are rationals.

    Of course, 4/2 = 2/1 = 2 (i.e., not every rational written with a slash is a non-integer). Similarly, #c(4/2 0) = #c(2 0) = 2.

    The common arithmetic functions and relations are denoted by +, -, *, /, =, <, <=, > and >=. However there are many others, e.g., floor, ceiling, and lognot. We suggest you see programming where we list all of the primitive ACL2 functions. Alternatively, see any Common Lisp language documentation.

    The primitive predicates for recognizing numbers are illustrated below. The following ACL2 function will classify an object, x, according to its numeric subtype, or else return 'NaN (not a number). We show it this way just to illustrate programming in ACL2.

    (defun classify-number (x)
      (cond ((rationalp x)
             (cond ((integerp x)
                    (cond ((< 0 x) 'positive-integer)
                          ((= 0 x) 'zero)
                          (t 'negative-integer)))
                   ((< 0 x) 'positive-non-integral-rational)
                   (t 'negative-non-integral-rational)))
            ((complex-rationalp x) 'complex-rational)
            (t 'NaN)))
    




    acl2-sources/doc/HTML/O-FINP.html0000664002132200015000000000154412222333524015704 0ustar kaufmannacl2 O-FINP.html -- ACL2 Version 6.3

    O-FINP

    recognizes if an ordinal is finite
    Major Section:  ACL2-BUILT-INS
    

    We introduce the function o-finp which returns t for any ordinal that is finite, else nil. This function is equivalent to the function atom, and is introduced so that we can disable its definition when dealing with ordinals (also see make-ord).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/O-FIRST-COEFF.html0000664002132200015000000000174712222333524016664 0ustar kaufmannacl2 O-FIRST-COEFF.html -- ACL2 Version 6.3

    O-FIRST-COEFF

    returns the first coefficient of an ordinal
    Major Section:  ACL2-BUILT-INS
    

    An ACL2 ordinal is either a natural number or, for an infinite ordinal, a list whose elements are exponent-coefficient pairs (see o-p). In the latter case, this function returns the cdr of the first pair in the list. In the case of a natural number, this function returns the ordinal itself (since a natural number, n, can be thought of as (w^0)n).

    For the corresponding exponent, see o-first-expt.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/O-FIRST-EXPT.html0000664002132200015000000000173312222333524016615 0ustar kaufmannacl2 O-FIRST-EXPT.html -- ACL2 Version 6.3

    O-FIRST-EXPT

    the first exponent of an ordinal
    Major Section:  ACL2-BUILT-INS
    

    An ACL2 ordinal is either a natural number or, for an infinite ordinal, a list whose elements are exponent-coefficient pairs (see o-p). In the latter case, this function returns the car of the first pair in the list. In the case of a natural number, the value returned is 0 (since a natural number, n, can be thought of as (w^0)n).

    For the corresponding coefficient, see o-first-coeff.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/O-INFP.html0000664002132200015000000000103212222333524015674 0ustar kaufmannacl2 O-INFP.html -- ACL2 Version 6.3

    O-INFP

    recognizes if an ordinal is infinite
    Major Section:  ACL2-BUILT-INS
    

    O-infp is a macro. (O-infp x) opens up to (not (o-finp x)) (see o-finp).




    acl2-sources/doc/HTML/O-P.html0000664002132200015000000001763412222333524015356 0ustar kaufmannacl2 O-P.html -- ACL2 Version 6.3

    O-P

    a recognizer for the ordinals up to epsilon-0
    Major Section:  ACL2-BUILT-INS
    

    Using the nonnegative integers and lists we can represent the ordinals up to epsilon-0. The ordinal representation used in ACL2 has changed as of Version_2.8 from that of Nqthm-1992, courtesy of Pete Manolios and Daron Vroon; additional discussion may be found in ``Ordinal Arithmetic in ACL2'', proceedings of ACL2 Workshop 2003, http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/. Previously, ACL2's notion of ordinal was very similar to the development given in ``New Version of the Consistency Proof for Elementary Number Theory'' in The Collected Papers of Gerhard Gentzen, ed. M.E. Szabo, North-Holland Publishing Company, Amsterdam, 1969, pp 132-213.

    The following essay is intended to provide intuition about ordinals. The truth, of course, lies simply in the ACL2 definitions of o-p and o<.

    Very intuitively, think of each non-zero natural number as by being denoted by a series of the appropriate number of strokes, i.e.,

    0             0
    1             |
    2             ||
    3             |||
    4             ||||
    ...           ...
    
    Then ``omega,'' here written as w, is the ordinal that might be written as
    w             |||||...,
    
    i.e., an infinite number of strokes. Addition here is just concatenation. Observe that adding one to the front of w in the picture above produces w again, which gives rise to a standard definition of w: w is the least ordinal such that adding another stroke at the beginning does not change the ordinal.

    We denote by w+w or w*2 the ``doubly infinite'' sequence that we might write as follows.

    w*2           |||||... |||||...
    
    One way to think of w*2 is that it is obtained by replacing each stroke in 2 (||) by w. Thus, one can imagine w*3, w*4, etc., which leads ultimately to the idea of ``w*w,'' the ordinal obtained by replacing each stroke in w by w. This is also written as ``omega squared'' or w^2, or:
     2
    w             |||||... |||||... |||||... |||||... |||||... ...
    
    We can analogously construct w^3 by replacing each stroke in w by w^2 (which, it turns out, is the same as replacing each stroke in w^2 by w). That is, we can construct w^3 as w copies of w^2,
     3              2       2       2       2
    w              w  ...  w  ...  w  ...  w ... ...
    
    Then we can construct w^4 as w copies of w^3, w^5 as w copies of w^4, etc., ultimately suggesting w^w. We can then stack omegas, i.e., (w^w)^w etc. Consider the ``limit'' of all of those stacks, which we might display as follows.
           .
          .
         .
        w
       w
      w
     w
    w
    
    That is epsilon-0.

    Below we begin listing some ordinals up to epsilon-0; the reader can fill in the gaps at his or her leisure. We show in the left column the conventional notation, using w as ``omega,'' and in the right column the ACL2 object representing the corresponding ordinal.

      ordinal            ACL2 representation
    
      0                  0
      1                  1
      2                  2
      3                  3
      ...                ...
      w                 '((1 . 1) . 0)
      w+1               '((1 . 1) . 1)
      w+2               '((1 . 1) . 2)
      ...                ...
      w*2               '((1 . 2) . 0)
      (w*2)+1           '((1 . 2) . 1)
      ...                ...
      w*3               '((1 . 3) . 0)
      (w*3)+1           '((1 . 3) . 1)
      ...                ...
    
       2
      w                 '((2 . 1) . 0)
      ...                ...
    
       2
      w +w*4+3          '((2 . 1) (1 . 4) . 3)
      ...                ...
    
       3
      w                 '((3 . 1) . 0)
      ...                ...
    
    
       w
      w                 '((((1 . 1) . 0) . 1) . 0)
      ...                ...
    
       w  99
      w +w  +w4+3       '((((1 . 1) . 0) . 1) (99 . 1) (1 . 4) . 3)
      ...                ...
    
        2
       w
      w                 '((((2 . 1) . 0) . 1) . 0)
    
      ...                ...
    
        w
       w
      w                 '((((((1 . 1) . 0) . 1) . 0) . 1) . 0)
      ...               ...
    
    Observe that the sequence of o-ps starts with the natural numbers (which are recognized by natp). This is convenient because it means that if a term, such as a measure expression for justifying a recursive function (see o<) must produce an o-p, it suffices for it to produce a natural number.

    The ordinals listed above are listed in ascending order. This is the ordering tested by o<.

    The ``epsilon-0 ordinals'' of ACL2 are recognized by the recursively defined function o-p. The base case of the recursion tells us that natural numbers are epsilon-0 ordinals. Otherwise, an epsilon-0 ordinal is a list of cons pairs whose final cdr is a natural number, ((a1 . x1) (a2 . x2) ... (an . xn) . p). This corresponds to the ordinal (w^a1)x1 + (w^a2)x2 + ... + (w^an)xn + p. Each ai is an ordinal in the ACL2 representation that is not equal to 0. The sequence of the ai's is strictly decreasing (as defined by o<). Each xi is a positive integer (as recognized by posp).

    Note that infinite ordinals should generally be created using the ordinal constructor, make-ord, rather than cons. The functions o-first-expt, o-first-coeff, and o-rst are ordinals destructors. Finally, the function o-finp and the macro o-infp tell whether an ordinal is finite or infinite, respectively.

    The function o< compares two epsilon-0 ordinals, x and y. If both are integers, (o< x y) is just x<y. If one is an integer and the other is a cons, the integer is the smaller. Otherwise, o< recursively compares the o-first-expts of the ordinals to determine which is smaller. If they are the same, the o-first-coeffs of the ordinals are compared. If they are equal, the o-rsts of the ordinals are recursively compared.

    Fundamental to ACL2 is the fact that o< is well-founded on epsilon-0 ordinals. That is, there is no ``infinitely descending chain'' of such ordinals. See proof-of-well-foundedness.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/O-RST.html0000664002132200015000000000202112222333524015607 0ustar kaufmannacl2 O-RST.html -- ACL2 Version 6.3

    O-RST

    returns the rest of an infinite ordinal
    Major Section:  ACL2-BUILT-INS
    

    An ACL2 infinite ordinal is a list whose elements are exponent-coefficient pairs (see o-p and see o-infp). The first exponent and first coefficient of an ordinal can be obtained by using o-first-expt and o-first-coeff respectively. To obtain the rest of the ordinal (for recursive analysis), use the o-rst function. It returns the rest of the ordinal after the first exponent and coefficient are removed.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/OBDD.html0000664002132200015000000000072412222333521015460 0ustar kaufmannacl2 OBDD.html -- ACL2 Version 6.3

    OBDD

    ordered binary decision diagrams with rewriting
    Major Section:  MISCELLANEOUS
    

    See bdd for information on this topic.




    acl2-sources/doc/HTML/OBSERVATION-CW.html0000664002132200015000000000065312222333524017056 0ustar kaufmannacl2 OBSERVATION-CW.html -- ACL2 Version 6.3

    OBSERVATION-CW

    See observation.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/OBSERVATION.html0000664002132200015000000000772712222333524016560 0ustar kaufmannacl2 OBSERVATION.html -- ACL2 Version 6.3

    OBSERVATION

    print an observation
    Major Section:  ACL2-BUILT-INS
    

    Here is a typical application of observation.

    ACL2 !>(let ((ctx 'top-level)
                 (name 'foo))
             (observation ctx
                          "Skipping processing of name ~x0."
                          name))
    
    ACL2 Observation in TOP-LEVEL:  Skipping processing of name FOO.
    <state>
    ACL2 !>
    
    Observation prints an initial ``ACL2 Observation...: '', and then prints the indicated message using formatted printing (see fmt). Notice in the example above that evaluation of a call of observation returns state. Indeed, observation is actually a macro whose expansion takes and returns the ACL2 state. A similar utility, observation-cw, is available that does not take or return state; rather, it returns nil as the suffix ``cw'' suggests that a ``comment window'' is the target of this printing, rather than the state. For example:
    ACL2 !>(let ((ctx 'top-level)
                 (name 'foo))
             (observation-cw ctx
                             "Skipping processing of name ~x0."
                             name))
    
    ACL2 Observation in TOP-LEVEL:  Skipping processing of name FOO.
    NIL
    ACL2 !>
    
    Observation-cw takes exactly the same arguments as observation, but observation-cw does its printing in a so-called ``wormhole''; see wormhole.

    General Forms:
    (observation    ctx fmt-string fmt-arg1 fmt-arg2 ... fmt-argk)
    (observation-cw ctx fmt-string fmt-arg1 fmt-arg2 ... fmt-argk)
    
    where ctx generally evaluates to a symbol (but see below), and fmt-string together with the fmt-argi are suitable for passing to fmt. Output begins and ends with a newline.

    Recall from the example above that the output from a call of observation (or observation-cw) begins with ``ACL2 Observation'' and additional characters ending in ``: '', for example `` in TOP-LEVEL: '', followed by formatted output produced from fmt-string with the given fmt-argi. The characters printed immediately following the string ``ACL2 Observation'' depend on the value of ctx. If ctx is nil, nothing is printed. If ctx is a non-nil symbol, it is printed using fmt directive ~x. If ctx is a cons pair whose car is a symbol, formatted printing is applied to the string "(~x0 ~x1 ...)", where #\0 and #\1 are bound respectively to that car and cdr. Otherwise, ctx is printed using fmt directive ~@.

    We next discuss situations in which printing is inhibited for observation and observation-cw. No printing is done when observation is among the inhibited output types; see set-inhibit-output-lst. Moreover, no printing is done by observation during include-book. If you want to avoid printing from observation-cw during include-book, then you need to manage that yourself.




    acl2-sources/doc/HTML/ODDP.html0000664002132200015000000000146412222333524015503 0ustar kaufmannacl2 ODDP.html -- ACL2 Version 6.3

    ODDP

    test whether an integer is odd
    Major Section:  ACL2-BUILT-INS
    

    (oddp x) is true if and only if x is odd, i.e., not even in the sense of evenp.

    The guard for oddp requires its argument to be an integer.

    Oddp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/OK-IF.html0000664002132200015000000000500212222333516015553 0ustar kaufmannacl2 OK-IF.html -- ACL2 Version 6.3

    OK-IF

    conditional exit from break-rewrite
    Major Section:  BREAK-REWRITE
    

    Example Form:
    :ok-if (null (brr@ :wonp))
    
    General Form:
    :ok-if expr
    
    where expr is a term involving no free variables other than state and returning one non-state result which is treated as Boolean. This form is intended to be executed from within break-rewrite (see break-rewrite).

    Consider first the simple situation that the (ok-if term) is a command read by break-rewrite. Then, if the term is non-nil, break-rewrite exits and otherwise it does not.

    More generally, ok-if returns an ACL2 error triple (mv erp val state). (See ld or see programming-with-state for more on error triples.) If any form being evaluated as a command by break-rewrite returns the triple returned by (ok-if term) then the effect of that form is to exit break-rewrite if term is non-nil. Thus, one might define a function or macro that returns the value of ok-if expressions on all outputs and thus create a convenient new way to exit break-rewrite.

    The exit test, term, generally uses brr@ to access context sensitive information about the attempted rule application. See brr@. Ok-if is useful inside of command sequences produced by break conditions. See monitor. :ok-if is most useful after an :eval command has caused break-rewrite to try to apply the rule because in the resulting break environment expr can access such things as whether the rule succeeded, if so, what term it produced, and if not, why. There is no need to use :ok-if before :evaling the rule since the same effects could be achieved with the break condition on the rule itself. Perhaps we should replace this concept with :eval-and-break-if? Time will tell.




    acl2-sources/doc/HTML/OOPS.html0000664002132200015000000001424712222333517015542 0ustar kaufmannacl2 OOPS.html -- ACL2 Version 6.3

    OOPS

    undo a :u or :ubt
    Major Section:  HISTORY
    

    The keyword command :oops will undo the most recent :ubt (or :u, which we here consider just another :ubt). A second :oops will undo the next most recent :ubt, a third will undo the :ubt before that one, and a fourth :oops will return the logical world to its configuration before the first :oops.

    Consider the logical world (see world) that represents the current extension of the logic and ACL2's rules for dealing with it. The :ubt and :u commands ``roll back'' to some previous world (see ubt). Sometimes these commands are used to inadvertently undo useful work and user's wish they could ``undo the last undo.'' That is the function provided by :oops.

    :Oops is best described in terms of an implementation. Imagine a ring of four worlds and a marker (*) indicating the current ACL2 world:

                 *
               w0
             /    \
           w3      w1
             \    /
               w2
    
    This is called the ``kill ring'' and it is maintained as follows. When you execute an event the current world is extended and the kill ring is not otherwise affected. When you execute :ubt or :u, the current world marker is moved one step counterclockwise and that world in the ring is replaced by the result, say w0', of the :ubt or :u.
               w0
             /    \
          *w0'     w1
             \    /
               w2
    
    If you were to execute events at this point, w0' would be extended and no other changes would occur in the kill ring.

    When you execute :oops, the marker is moved one step clockwise. Thus the kill ring becomes

                 *
               w0
             /    \
           w0'     w1
             \    /
               w2
    
    and the current ACL2 world is w0 once again. That is, :oops ``undoes'' the :ubt that produced w0' from w0. Similarly, a second :oops will move the marker to w1, undoing the undo that produced w0 from w1. A third :oops makes w2 the current world. Note however that a fourth :oops restores us to the configuration previously displayed above in which w0' has the marker.

    In general, the kill ring contains the current world and the three most recent worlds in which a :ubt or :u were done.

    While :ubt may appear to discard the information in the events undone, we can see that the world in which the :ubt occurred is still available. No information has been lost about that world. But :ubt does discard information! :Ubt discards the information necessary to recover from the third most recent ubt! An :oops, on the other hand, discards no information, it just selects the next available world on the kill ring and doing enough :oopses will return you to your starting point.

    We can put this another way. You can freely type :oops and inspect the world that you thus obtain with :pe, :pc, and other history commands. You can repeat this as often as you wish without risking the permanent loss of any information. But you must be more careful typing :ubt or :u. While :oops makes :ubt seem ``safe'' because the most recent :ubt can always be undone, information is lost when you execute :ubt.

    We note that :ubt and :u may remove compiled definitions (but note that in some Lisps, including CCL (OpenMCL) and SBCL, functions are always compiled). When the original world is restored using :oops, restored functions will not generally be compiled (except for Lisps as above), though the user can remedy this situation; see comp.

    Finally, we note that our implementation of oops can use a significant amount of memory, because of the saving of old logical worlds. Most users are unlikely to experience a memory problem, but if you do, then you may want to disable oops by evaluting (reset-kill-ring 0 state); see reset-kill-ring.




    acl2-sources/doc/HTML/OPEN-INPUT-CHANNEL-P.html0000664002132200015000000000064512222333524017656 0ustar kaufmannacl2 OPEN-INPUT-CHANNEL-P.html -- ACL2 Version 6.3

    OPEN-INPUT-CHANNEL-P

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/OPEN-INPUT-CHANNEL.html0000664002132200015000000000064112222333524017455 0ustar kaufmannacl2 OPEN-INPUT-CHANNEL.html -- ACL2 Version 6.3

    OPEN-INPUT-CHANNEL

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/OPEN-OUTPUT-CHANNEL-P.html0000664002132200015000000000064712222333524020021 0ustar kaufmannacl2 OPEN-OUTPUT-CHANNEL-P.html -- ACL2 Version 6.3

    OPEN-OUTPUT-CHANNEL-P

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/OPEN-OUTPUT-CHANNEL.html0000664002132200015000000000064312222333524017620 0ustar kaufmannacl2 OPEN-OUTPUT-CHANNEL.html -- ACL2 Version 6.3

    OPEN-OUTPUT-CHANNEL

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/OPEN-OUTPUT-CHANNEL_bang_.html0000664002132200015000000001043012222333520020735 0ustar kaufmannacl2 OPEN-OUTPUT-CHANNEL_bang_.html -- ACL2 Version 6.3

    OPEN-OUTPUT-CHANNEL!

    when trust tags are needed to open output channels
    Major Section:  IO
    

    Use this function in place of open-output-channel if you want to open a channel for output at times this would otherwise be prohibited, for example during make-event expansion and clause-processor hints. If this functionality doesn't quite seem like what you need, take a look at the definition of open-output-channel! in axioms.lisp, specifically the binding of state global variable writes-okp. The following example, taken from community book books/hons-archive/hons-archive.lisp, illustrates the latter approach.

    (defmacro har-zip! (x filename &key sortp)
      "See :doc hons-archive"
      `(mv-let (erp val state)
               (progn!
                :state-global-bindings
                ((temp-touchable-vars t set-temp-touchable-vars))
                (state-global-let*
                 ((writes-okp t))
                 (let ((state (har-zip-fn ,x ,filename ,sortp state)))
                   (mv nil nil state))))
               (declare (ignore erp val))
               state))
    

    The book below illustrates the soundness loophole plugged in ACL2 Version_3.2 related to file writes during book certification.

    ; The following example is adapted (with only very slight changes)
    ; from one written by Peter Dillinger.  It illustrates the prohibition
    ; against writing files enforced by with-output-channel during book
    ; certification (more specifically, during make-event expansion).
    
    ; This book certifies in ACL2 Version_3.1 before the fix discussed in the
    ; paragraph about it being ``possible to write files during book
    ; certification'' in :DOC NOTE-3-2.  The fix was actually made to ACL2
    ; function open-output-channel.
    
    ; After the fix, in order for certification to succeed one needs to do
    ; two things.  First, in raw lisp:
    ;   (push :after-writes-okp-fix *features*)
    ; Second, certify with this command:
    ;   (certify-book "writes-okp" 0 nil :ttags (:writes-okp))
    
    (in-package "ACL2")
    
    (local
     (defun write-objects-to-channel (obj-lst chan state)
       (declare (xargs :mode :program
                       :stobjs state
                       :guard (true-listp obj-lst)))
       (if (consp obj-lst)
           (pprogn (print-object$ (car obj-lst) chan state)
                   (write-objects-to-channel (cdr obj-lst) chan state)
                   state)
         state)))
    
    #+after-writes-okp-fix
    (defttag :writes-okp)
    
    (local
     (defun write-objects-to-file (obj-lst filename state)
       (declare (xargs :mode :program
                       :stobjs state
                       :guard (and (stringp filename)
                                   (true-listp obj-lst))))
       (mv-let (chan state)
               #-after-writes-okp-fix
               (open-output-channel filename :object state)
               #+after-writes-okp-fix
               (open-output-channel! filename :object state)
               (if chan
                   (pprogn (write-objects-to-channel obj-lst chan state)
                           (close-output-channel chan state)
                           (value :done))
                 (er soft 'write-object-to-file
                     "Could not open for writing: ~x0"
                     filename)))))
    
    (local
     (defconst *nil.lisp*
       '((in-package "ACL2")
         (defthm bad nil :rule-classes nil))))
    
    (local
     (defconst *nil.cert*
       '((IN-PACKAGE "ACL2")
         "ACL2 Version 3.1"
         :BEGIN-PORTCULLIS-CMDS
         :END-PORTCULLIS-CMDS
         NIL
         (("/home/peterd/test/nil.lisp" "nil" "nil"
           ((:SKIPPED-PROOFSP) (:AXIOMSP) (:TTAGS)) . 134094174))
         62589544
         )))
    
    (local
     (make-event (er-progn
                  (write-objects-to-file *nil.lisp* "nil.lisp" state)
                  (write-objects-to-file *nil.cert* "nil.cert" state)
                  (value '(value-triple :invisible)))))
    
    (local (include-book
            "nil" :load-compiled-file nil))
    
    (defthm bad nil :rule-classes nil)
    




    acl2-sources/doc/HTML/OPEN-TRACE-FILE.html0000664002132200015000000000154212222333531017062 0ustar kaufmannacl2 OPEN-TRACE-FILE.html -- ACL2 Version 6.3

    OPEN-TRACE-FILE

    redirect trace output to a file
    Major Section:  TRACE
    

    Example:
    (open-trace-file "foo") ; trace output will go to file foo
    
    General Form:
    (open-trace-file filename) ; trace output will go to file filename
    

    Output from trace$ normally goes to the screen, i.e., standard-co. But it can be redirected to a file as shown above. See close-trace-file for how to send trace output back to the screen.




    acl2-sources/doc/HTML/OPTIMIZE.html0000664002132200015000000000062112222333525016210 0ustar kaufmannacl2 OPTIMIZE.html -- ACL2 Version 6.3

    OPTIMIZE

    See declare.
    Major Section:  PROGRAMMING
    




    acl2-sources/doc/HTML/OR.html0000664002132200015000000000260012222333524015266 0ustar kaufmannacl2 OR.html -- ACL2 Version 6.3

    OR

    disjunction
    Major Section:  ACL2-BUILT-INS
    

    Or is the macro for disjunctions. Or takes any number of arguments and returns the first that is non-nil, or nil if there is no non-nil element.

    In the ACL2 logic, the macroexpansion of (or x y) is an IF term that appears to cause x to be evaluated twice:

    ACL2 !>:trans (or x y)
    
    (IF X X Y)
    
    => *
    
    ACL2 !>
    
    If x were replaced by an expression whose evaluation takes a long time, then such an expansion would be ineffecient. However, don't be fooled: you can expect Common Lisp implementations to avoid this problem, say by generating a new variable, for example:
    ACL2 !>:q ; Exit the ACL2 loop and go into raw Common Lisp
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ACL2>(macroexpand '(or x y))
    
    (LET ((#:G5374 X)) (IF #:G5374 #:G5374 Y))
    T
    
    ACL2>
    

    Or is a Common Lisp macro. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/ORACLE-APPLY-RAW.html0000664002132200015000000000357112222333524017235 0ustar kaufmannacl2 ORACLE-APPLY-RAW.html -- ACL2 Version 6.3

    ORACLE-APPLY-RAW

    call a function argument on the given list of arguments, no restrictions
    Major Section:  ACL2-BUILT-INS
    

    See oracle-apply, as we assume familiarity with that function. Oracle-apply-raw is a variant of oracle-apply that is untouchable, and hence requires a trust tag to remove the untouchability (see defttag and see remove-untouchable). Unlike oracle-apply, oracle-apply-raw simply calls the raw Lisp function funcall to compute the result, without restriction: the specified :guard is t, the function itself is applied (not its executable counterpart), there is no restriction for untouchable functions or return-last, and safe mode is not used. Thus, in general, oracle-apply-raw can be dangerous to use: any manner of error can occur!

    As is the case for oracle-apply, the function symbol oracle-apply-raw is defined in :logic mode and is guard-verified. Oracle-apply-raw is logically defined to be oracle-apply; more precisely:

    (oracle-apply-raw fn args state)
    = {logical definition}
    (ec-call (oracle-apply fn args state))
    




    acl2-sources/doc/HTML/ORACLE-APPLY.html0000664002132200015000000001273012222333524016603 0ustar kaufmannacl2 ORACLE-APPLY.html -- ACL2 Version 6.3

    ORACLE-APPLY

    call a function argument on the given list of arguments
    Major Section:  ACL2-BUILT-INS
    

    Oracle-apply evaluates its first argument to produce an ACL2 function symbol, FN, and then applies FN to the value of the second argument, which should be a true list whose length is the number of inputs for FN. The return value is of the form (mv call-result state).

    Examples:
    (oracle-apply 'cons '(3 4) state) = (mv '(3 . 4) <state>)
    (oracle-apply (car '(floor foo)) (list (+ 6 7) 5) state) = (mv 2 <state>)
    

    Also see oracle-funcall for a related utility.

    Note that calls of oracle-funcall and oracle-apply return two values: the result of the function application, and a modified state.

    Oracle-apply is defined in :logic mode, and in fact is guard-verified. However, you will not be able to prove much about this function, because it is defined in the logic using the acl2-oracle field of the ACL2 state. The behavior described above -- i.e., making a function call -- takes place when the third argument is the ACL2 state, so during proofs (when that can never happen), a term (oracle-apply 'fn '...) will not simplify using a call of fn.

    The guard for (oracle-apply fn args state) is the term (oracle-apply-guard fn args state), which says the following: fn and args must satisfy symbolp and true-listp, respectively; fn must be a known function symbol other than return-last that is not untouchable (see push-untouchable) and has no stobj arguments (not even state); and the length of args must equal the arity of fn (see signature). The requirement that fn be a known function symbol may be a bit onerous for guard verification, but this is easily overcome by using ec-call, for example as follows.

    (defun f (x state)
      (declare (xargs :stobjs state))
      (ec-call (oracle-apply 'car (list x) state)))
    
    This use of ec-call will, however, cause the guard of oracle-apply to be checked at runtime.

    If the guard for oracle-apply fails to hold but there is no guard violation because guard-checking is suppressed (see set-guard-checking), then the value returned is computed using its logical definition -- which, as mentioned above, uses the ACL2 oracle -- and hence the value computed is unpredictable (indeed, the function argument will not actually be called).

    The value returned by oracle-apply is always a single value obtained by calling the executable counterpart of its function argument, as we now explain. Consider a form (oracle-apply fn args state) that evaluates to (mv VAL state'), where fn evaluates to the function symbol F. If F returns multiple values, then VAL is the first value computed by the call of F on the value of args. More precisely, oracle-apply actually invokes the executable counterpart of F; thus, if args is the expression (list x1 ... xk), then VAL is the same as (first) value returned by evaluating (ec-call (F x1 x2 ... xk)). See ec-call.

    (Remark. If you identify a need for a version of oracle-apply to return multiple values, we can perhaps provide such a utility; feel free to contact the ACL2 implementors to request it.)

    A subtlety is that the evaluation takes place in so-called ``safe mode'', which avoids raw Lisp errors due to calls of :program mode functions. The use of safe mode is unlikely to be noticed if the value of the first argument of oracle-apply is a :logic mode function symbol. However, for :program mode functions with side effects due to special raw Lisp code, as may be the case for built-in functions or for custom functions defined with active trust tags (see defttag), use of the following function may be preferable:

    See oracle-apply-raw for a much less restrictive version of oracle-apply, which avoids safe mode and (for example) can apply a function that has a definition in the host Lisp but not in the ACL2 world.




    acl2-sources/doc/HTML/ORACLE-FUNCALL.html0000664002132200015000000000277012222333524017005 0ustar kaufmannacl2 ORACLE-FUNCALL.html -- ACL2 Version 6.3

    ORACLE-FUNCALL

    call a function argument on the remaining arguments
    Major Section:  ACL2-BUILT-INS
    

    Oracle-funcall evaluates its first argument to produce an ACL2 function symbol, and then applies that function symbol to the values of the rest of the arguments. The return value is of the form (mv call-result state).

    Examples:
    (oracle-funcall 'cons 3 4) ==> (mv '(3 . 4) <state>)
    (oracle-funcall (car '(floor foo bar)) (+ 6 7) 5) ==> (mv 2 <state>)
    

    Oracle-funcall is a macro; each of its calls macroexpands to a call of the related utility oracle-apply that takes the ACL2 state as an argument, as follows:

    (oracle-funcall fn x1 x2 .. xk)
    
    macroexpands to
    (oracle-apply fn (list x1 x2 .. xk) state)
    

    Note that calls of oracle-funcall and oracle-apply return two values: the result of the function application, and a modified state.

    See oracle-apply for details, including information about guards.




    acl2-sources/doc/HTML/ORDINALS.html0000664002132200015000000002015012222333521016156 0ustar kaufmannacl2 ORDINALS.html -- ACL2 Version 6.3

    ORDINALS

    ordinals in ACL2
    Major Section:  MISCELLANEOUS
    

    Ordinals are used in ACL2 for proving termination in the admission of recursive function definitions. For a proof that the ACL2 ordinals are well-founded, see proof-of-well-foundedness.

    The representation of ordinals changed in ACL2 Version_2.8, and is due to Pete Manolios and Daron Vroon. They have also defined algorithms for ordinal arithmetic, created a library of theorems to reason about ordinal arithmetic, and written the rest of this documentation in order to explain this change. We thank them for their efforts. Although they have provided the implementation and even modified the community books as needed, we have looked over their work and are maintaining it (and this documentation); if there are any bugs, they should be considered ours (Matt Kaufmann and J Moore).

    A book is included for compatibility with the representation before Version_2.8. For books that contain events relying on the previous ordinal implementation, insert the following lines before the first such event:

    (include-book "ordinals/e0-ordinal" :dir :system)
    (set-well-founded-relation e0-ord-<)
    

    The new ordinal representation is based on a slightly different version of Cantor Normal Form than that used by the old ordinals. An advantage of the new representation is that it is exponentially more succinct than the old representation.

    While pre-Version_2.8 ACL2 versions provided built-in functions for checking if an object is an ordinal and for comparing two ordinals, they did not provide support for reasoning about and constructing ordinals. The community books directory books/ordinals provides such support. First, it provides efficient algorithms for ordinal arithmetic (including addition, subtraction, multiplication, and exponentiation). The algorithms and their complexity are described in the following paper.

    Manolios, Panagiotis & Vroon, Daron.
    Algorithms for ordinal arithmetic.
    Baader, Franz (ed),
    19th International Conference on Automated Deduction--CADE-19.
    Pages 243-257 of LNAI, vol. 2741.  Springer-Verlag.
    
    Second, the algorithms are mechanically verified and libraries of theorems which can be used to automate reasoning involving the ordinals are provided. For details, see the following paper.
    Manolios, Panagiotis & Vroon, Daron.
    Ordinal arithmetic in ACL2.
    Kaufmann, Matt, & Moore, J Strother (eds).
    Fourth International Workshop on the ACL2 Theorem
    Prover and Its Applications (ACL2-2003),
    July, 2003.
    See http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/.
    
    We now describe aspects of the above mentioned books in more detail.

    The new ordering function is o< and the new ordinal recognizer is o-p. See also natp, posp, o<=, o>, o>=, o-first-expt, o-first-coeff, o-rst, make-ord, o-finp, and o-infp.

    The old ordinals were based on the following formulation of Cantor Normal Form:

    For any ordinal, a < epsilon-0, there exist natural numbers p and n, and ordinals a1 >= a2 >= ... >= an > 0 such that a > a1 and a = w^(a1) + w^(a2) + ... + w^(an) + p.

    Thus, a predicate recognizing ACL2's old ordinals is given by the following definition.

    (defun e0-ordinalp (x)
      (if (consp x)
          (and (e0-ordinalp (car x))
               (not (equal (car x) 0))
               (e0-ordinalp (cdr x))
               (or (atom (cdr x))
                   (not (e0-ord-< (car x) (cadr x)))))
        (and (integerp x)
             (>= x 0))))
    
    The new representation is based on a corollary to the above theorem, which we get by the left distributive property of ordinal multiplication over ordinal addition. Thus, w^a + w^a = (w^a)2, w^a + w^a + w^a = (w^a)3 and so forth. The corollary is as follows:

    For any ordinal, a < epsilon-0, there exist natural numbers p and n, positive integers x1, x2, ..., xn and ordinals a1 > a2 > ... > an > 0 such that a > a1 and a = w^(a1)x1 + w^(a2)x2 + ... + w^(an)xn + p.

    Instead of representing an ordinal as a list of non-increasing ordinals, we represent it as a list of exponent-coefficient pairs, such that the exponents are strictly decreasing (see o-p). Note that this representation is exponentially more efficient than the old representation.

    The ordinal arithmetic functions: o+, o-, o*, and o^ are defined in the ordinals library (in the community books directory books/ordinals). To use them, include the book ordinals-without-arithmetic or ordinals, depending on whether you want the arithmetic books included or not (ordinals includes community book books/arithmetic/top-with-meta). To use the old ordinals, include the book e0-ordinal and run the command (set-well-founded-relation e0-ord-<)

    The community book books/arithmetic/natp-posp is a book for reasoning about posp and natp. We recommend using this book if you have to reason about posp and natp. It is included in community book books/arithmetic/top, which is included in community book books/arithmetic/top-with-meta, which is included in community book books/ordinals/ordinals.

    If you have a good reason to use the old definitions of the ordinals (e.g., because of legacy code and theorems), then we provide a convenient way to do this. The book ordinal-isomorphism proves that the new ordinals are order-isomorphic to the old ordinals and thus theorems proved in one context can be directly transferred to the other. For an example of how to do this, look at the book defmul in the community books directory books/workshops/2000/ruiz/multiset.

    The ordinals books have been used to prove non-trivial theorems. For a good example, see the books in the community books directory books/workshops/2003/sustik/support, where Matyas Sustik proves Dickson's lemma.

    Finally, many termination proofs can be carried out with weaker orderings than the ordinals up to epsilon-0. For example, many inductive theorem provers only know that the lexicographic ordering on natural numbers is well-founded. The book lexicographic-ordering contains a definition of such an ordering l< whose arguments are either a list of natural numbers, or a natural number. In the book we prove that l< is well-founded (that is, we prove a :well-founded-relation defthm and provide a macro llist to simplify the generation of measure functions. We also show how to use l< to prove that the famous Ackermann function terminates. Finally, since l< does something reasonable with natural numbers, it gets along with acl2-count, the default measure chosen by ACL2.




    acl2-sources/doc/HTML/OTF-FLG.html0000664002132200015000000000453712222333521016014 0ustar kaufmannacl2 OTF-FLG.html -- ACL2 Version 6.3

    OTF-FLG

    allow more than one initial subgoal to be pushed for induction
    Major Section:  MISCELLANEOUS
    

    The value of this flag is normally nil. If you want to prevent the theorem prover from abandoning its initial work upon pushing the second subgoal, set :otf-flg to t.

    Suppose you submit a conjecture to the theorem prover and the system splits it up into many subgoals. Any subgoal not proved by other methods is eventually set aside for an attempted induction proof. But upon setting aside the second such subgoal, the system chickens out and decides that rather than prove n>1 subgoals inductively, it will abandon its initial work and attempt induction on the originally submitted conjecture. The :otf-flg (Onward Thru the Fog) allows you to override this chickening out. When :otf-flg is t, the system will push all the initial subgoals and proceed to try to prove each, independently, by induction.

    Even when you don't expect induction to be used or to succeed, setting the :otf-flg is a good way to force the system to generate and display all the initial subgoals.

    For defthm and thm, :otf-flg is a keyword argument that is a peer to :rule-classes and :hints. It may be supplied as in the following examples; also see defthm.

    (thm (my-predicate x y) :rule-classes nil :otf-flg t)
    
    (defthm append-assoc
      (equal (append (append x y) z)
             (append x (append y z)))
      :hints (("Goal" :induct t))
      :otf-flg t)
    
    The :otf-flg may be supplied to defun via the xargs declare option. When you supply an :otf-flg hint to defun, the flag is effective for the termination proofs and the guard proofs, if any.




    acl2-sources/doc/HTML/OTHER.html0000664002132200015000000001521512222333522015633 0ustar kaufmannacl2 OTHER.html -- ACL2 Version 6.3

    OTHER

    other commonly used top-level functions
    Major Section:  ACL2 Documentation
    

    Some Related Topics

    This section contains an assortment of top-level functions that fit into none of the other categories and yet are suffiently useful as to merit ``advertisement'' in the :help command.




    acl2-sources/doc/HTML/OUTPUT-TO-FILE.html0000664002132200015000000000541212222333520017043 0ustar kaufmannacl2 OUTPUT-TO-FILE.html -- ACL2 Version 6.3

    OUTPUT-TO-FILE

    redirecting output to a file
    Major Section:  IO
    

    For a general discussion of ACL2 input/output and of the ACL2 read-eval-print loop, see io and see ld (respectively). Here we use an example to illustrate how to use some of the options provided by ld to redirect ACL2 output to a file, other than the printing of the prompt (which continues to go to the terminal).

    There are two ld specials that control output from the ld command: proofs-co for proof output and standard-co for other output. The following example shows how to use these to redirect output to a file "tmp.out". The following command opens a character output channel to to the file "tmp.out" and redirects proof output to that channel, i.e., to file "tmp.out".

    (mv-let (chan state)
            (open-output-channel "tmp.out" :character state)
            (set-proofs-co chan state))
    
    Next, we redirect standard output to that same channel.
    (set-standard-co (proofs-co state) state)
    
    Now we can load an input file, in this case file "tmp.lisp", and output will be redirected to file "tmp.out". (The use of :ld-pre-eval-print t is optional; see ld.)
    (ld "tmp.lisp" :ld-pre-eval-print t)
    
    Having completed our load operation, we restore both proof output and standard output to the terminal, as follows.
    (set-standard-co *standard-co* state)
    (close-output-channel (proofs-co state) state)
    (set-proofs-co *standard-co* state)
    

    The following variant of the above example shows how to redirect output as above except without changing the global settings of the two ld specials, proofs-co and standard-co. This approach uses a notion of ``global variables'' stored in the ACL2 state; see assign and see @.

    (mv-let (chan state)
            (open-output-channel "tmp.out" :character state)
            (assign tmp-channel chan))
    (ld "tmp.lisp" :ld-pre-eval-print t
                   :proofs-co (@ tmp-channel)
                   :standard-co (@ tmp-channel))
    (close-output-channel (@ tmp-channel) state)
    




    acl2-sources/doc/HTML/OVERRIDE-HINTS.html0000664002132200015000000002230712222333521017053 0ustar kaufmannacl2 OVERRIDE-HINTS.html -- ACL2 Version 6.3

    OVERRIDE-HINTS

    a list of hints given priority in every proof attempt
    Major Section:  MISCELLANEOUS
    

    This is an advanced feature, originally implemented to help system designers to create ``modes'' that control the way hints are supplied to the theorem prover. Please see default-hints for the much more usual way to install hints that may be applied by default.

    Some Related Topics

    Examples:
    ACL2 !>(override-hints (w state))
    ((computed-hint-1 clause keyword-alist processor)
     (computed-hint-2 clause keyword-alist stable-under-simplificationp))
    
    Override-hints returns a list of computed hints (see computed-hints) which, unlike other computed hints, may mention the variable KEYWORD-ALIST.

    Before reading further, please see hints-and-the-waterfall to review the basics of how hints are applied during a proof. In particular, we assume familiarity with the notion of selecting a hint to be applied to the current goal. If there are override-hints, that hint selection is tentative, because if it reduced to nil after the application of override-hints, then that hint will be skipped and the attempt will continue for selecting an applicable hint. (Craft your override-hints so that :no-op t is returned in such cases instead of nil, if you don't want the hint to be skipped.) But we must explain what is meant by ``the application of override-hints'', and we do that now.

    Suppose that there are override-hints when a hint is selected for the current goal. That selected hint is a keyword-alist, which is an alternating list of hint keywords and their values, whose source is either an explicit hint (goal-name :key1 val1 ... :keyn valn) where the :keyi are allowed to be custom hint keywords (which are expanded away; see custom-keyword-hints), or else is the non-nil keyword-alist produced by evaluating a computed hint. Then the override-hints are applied to that keyword-alist as follows, one at a time, in order of their occurrence in the list of override-hints (as determined by the use of set-override-hints and add-override-hints). The first override-hint is evaluated, in the usual manner of evaluating computed hints but with the variable KEYWORD-ALIST bound to the above keyword-alist. That evaluation produces a result that should also be a keyword-alist, or else an error occurs. Any custom keyword hints are then eliminated from that keyword-alist. The resulting keyword-alist must not contain the :ERROR hint keyword and must not start with the :COMPUTED-HINT-REPLACEMENT keyword; otherwise an error occurs. With KEYWORD-ALIST bound to this result, the second override-hint is similarly evaluated. This process continues, and the keyword-alist returned by the final override-hint is the one used when processing the goal at hand. Except: If that keyword-alist is nil, then the next hint among the pending hints is tentatively selected and the process repeats, applying each override hint to that new tentative selection. Of course we might obtain nil again, in which case we tentatively select the next pending hint; and so on.

    If finally no hint is selected for the current goal, then KEYWORD-ALIST is bound to nil and the override-hints are applied as described above. But note that this final step is skipped if hint selection is being performed because stable-under-simplificationp has just become true, rather than at the top of the waterfall. (Otherwise the override-hints could easily keep firing uselessly yet putting us back at the top of the waterfall, with no change to the given goal, resulting in an infinite loop.)

    As mentioned above, the :COMPUTED-HINT-REPLACEMENT keyword is illegal for the value of an override-hint. But a selected hint may be a computed hint that evaluates to a keyword-alist beginning with prefix :COMPUTED-HINT-REPLACEMENT val. What value does ACL2 return for such a computed hint in the presence of override-hints? First, this prefix is stripped off before passing the resulting keyword-alist to the override-hints as described above. If the result of applying override-hints to that keyword-alist is not nil, then the prefix is put back on the front of that resulting keyword-alist after doing internal processing of the hint, including expansion of any custom keyword hints. Otherwise, the application of override-hints to the computed hint is nil, so this hint is not selected after all.

    WARNING: Unlike ordinary computed hints, a value of nil for an override-hint is not ignored. That is: When an ordinary computed hint evaluates to nil, it is deemed not to apply, and the next available hint is consulted. But when an override-hint is evaluated, the result is always supplied for the next binding of the variable KEYWORD-ALIST, even if that result is nil. If you want an override-hint to be a no-op, return as the expression the variable KEYWORD-ALIST rather than an expression that evaluates to nil.

    This feature can be used in order to implement a form of additive hints. Suppose for example that you want a hint that turns off generalization. A simple but inadequate solution is:

    (add-default-hints '((quote (:do-not '(generalize)))))
    
    The problem is that if there is any explicit hint supplied for a given goal, then it will be the one selected, and the above will be ignored. But suppose that the explicit hint supplied is of the form ("Subgoal x.y" :do-not '(fertilize)). What we would really want in this case is to generate the hint for the indicated subgoal that binds :do-not to a list indicating that both fertilization _and_ generalization are disabled for that goal. A solution is to merge, for example as follows. (The use of prog2$ and cw is of course optional, included here to provide debug printing.)
    (add-override-hints
     '((let* ((tmp (assoc-keyword :do-not KEYWORD-ALIST))
              (new-keyword-alist
               (cond (tmp (list* :do-not
                                 `(cons 'generalize ,(cadr tmp))
                                 (remove-keyword :do-not KEYWORD-ALIST)))
                     (t (list* :do-not ''(generalize) KEYWORD-ALIST)))))
         (prog2$ (cw "New: ~x0~|" new-keyword-alist)
                 new-keyword-alist))))
    

    REMARKS

    (1) The utilities add-override-hints, add-override-hints!, set-override-hints, set-override-hints!, remove-override-hints, and remove-override-hints! are also available, in complete analogy to their default-hints versions.

    (2) The community book hints/basic-tests.lisp illustrates the use of override-hints and illuminates a number of corner cases; search in that file for ``Test override-hints.''

    (3) The community book hints/merge-hint.lisp provides support for merging hints that might be useful for writers of override-hint expressions (see the examples at the end of that file).

    (4) Override-hints are used in the processing of :BACKTRACK hints (see hints).




    acl2-sources/doc/HTML/O_gt_.html0000664002132200015000000000103012222333524015771 0ustar kaufmannacl2 O_gt_.html -- ACL2 Version 6.3

    O>

    the greater-than relation for the ordinals
    Major Section:  ACL2-BUILT-INS
    

    O> is a macro and (o> x y) expands to (o< y x). See o<.




    acl2-sources/doc/HTML/O_gt_=.html0000664002132200015000000000105312222333524016073 0ustar kaufmannacl2 O_gt_=.html -- ACL2 Version 6.3

    O>=

    the greater-than-or-equal relation for the ordinals
    Major Section:  ACL2-BUILT-INS
    

    O>= is a macro and (o>= x y) expands to (not (o< x y)). See o<.




    acl2-sources/doc/HTML/O_lt_.html0000664002132200015000000001010412222333524016000 0ustar kaufmannacl2 O_lt_.html -- ACL2 Version 6.3

    O<

    the well-founded less-than relation on ordinals up to epsilon-0
    Major Section:  ACL2-BUILT-INS
    

    If x and y are both o-ps (see o-p) then (o< x y) is true iff x is strictly less than y. o< is well-founded on the o-ps. When x and y are both nonnegative integers, o< is just the familiar ``less than'' relation (<).

    o< plays a key role in the formal underpinnings of the ACL2 logic. In order for a recursive definition to be admissible it must be proved to ``terminate.'' By terminate we mean that the arguments to the function ``get smaller'' as the function recurses and this sense of size comparison must be such that there is no ``infinitely descending'' sequence of ever smaller arguments. That is, the relation used to compare successive arguments must be well-founded on the domain being measured.

    The most basic way ACL2 provides to prove termination requires the user to supply (perhaps implicitly) a mapping of the argument tuples into the ordinals with some ``measure'' expression in such a way that the measures of the successive argument tuples produced by recursion decrease according to the relation o<. The validity of this method rests on the well-foundedness of o< on the o-ps.

    Without loss of generality, suppose the definition in question introduces the function f, with one formal parameter x (which might be a list of objects). Then we require that there exist a measure expression, (m x), that always produces an o-p. Furthermore, consider any recursive call, (f (d x)), in the body of the definition. Let hyps be the conjunction of terms, each of which is either the test of an if in the body or else the negation of such a test, describing the path through the body to the recursive call in question. Then it must be a theorem that

      (IMPLIES hyps (O< (m (d x)) (m x))).
    
    When we say o< is ``well-founded'' on the o-ps we mean that there is no infinite sequence of o-ps such that each is smaller than its predecessor in the sequence. Thus, the theorems that must be proved about f when it is introduced establish that it cannot recur forever because each time a recursive call is taken (m x) gets smaller. From this, and the syntactic restrictions on definitions, it can be shown (as on page 44 in ``A Computational Logic'', Boyer and Moore, Academic Press, 1979) that there exists a function satisfying the definition; intuitively, the value assigned to any given x by the alleged function is that computed by a sufficiently large machine. Hence, the logic is consistent if the axiom defining f is added.

    See o-p for a discussion of the ordinals and how to compare two ordinals.

    The definitional principle permits the use of relations other than o< but they must first be proved to be well-founded on some domain. See well-founded-relation. Roughly put, alternative relations are shown well-founded by providing an order-preserving mapping from their domain into the ordinals. See defun for details on how to specify which well-founded relation is to be used.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/O_lt_=.html0000664002132200015000000000105012222333524016075 0ustar kaufmannacl2 O_lt_=.html -- ACL2 Version 6.3

    O<=

    the less-than-or-equal relation for the ordinals
    Major Section:  ACL2-BUILT-INS
    

    o<= is a macro and (o<= x y) expands to (not (o< y x)). See o<.




    acl2-sources/doc/HTML/On_the_Naming_of_Subgoals.html0000664002132200015000000000121312222333526021777 0ustar kaufmannacl2 On_the_Naming_of_Subgoals.html -- ACL2 Version 6.3

    On the Naming of Subgoals

    Subgoal *1/2 is the induction step from the scheme, obtained by instantiating the scheme with our conjecture.

    We number the cases ``backward'', so this is case ``2'' of the proof of ``*1''. We number them backward so you can look at a subgoal number and get an estimate for how close you are to the end.




    acl2-sources/doc/HTML/Other_Requirements.html0000664002132200015000000000200212222333526020570 0ustar kaufmannacl2 Other_Requirements.html -- ACL2 Version 6.3

    Other Requirements

    ACL2 is distributed on the Web without fee.

    There is a license agreement based on the 3-clause BSD license. See the file LICENSE in the ACL2 distribution.

    ACL2 currently runs on Unix, Linux, Windows, and Macintosh OS X operating systems.

    It can be built in any of the following Common Lisps:

      * Allegro Common Lisp,
      * CCL (formerly OpenMCL)
      * CLISP,
      * CMU Common Lisp,
      * GCL (Gnu Common Lisp),
      * LispWorks, and
      * SBCL (Steel Bank Common Lisp)
    




    acl2-sources/doc/HTML/Overview_of_the_Expansion_of_ENDP_in_the_Base_Case.html0000664002132200015000000000113212222333526026632 0ustar kaufmannacl2 Overview_of_the_Expansion_of_ENDP_in_the_Base_Case.html -- ACL2 Version 6.3

    Overview of the Expansion of ENDP in the Base Case

    Subgoal *1/1 is the Base Case of our induction. It simplifies to Subgoal *1/1' by expanding the ENDP term in the hypothesis, just as we saw in the earlier proof of Subgoal *1/2.




    acl2-sources/doc/HTML/Overview_of_the_Expansion_of_ENDP_in_the_Induction_Step.html0000664002132200015000000000150712222333526027762 0ustar kaufmannacl2 Overview_of_the_Expansion_of_ENDP_in_the_Induction_Step.html -- ACL2 Version 6.3

    Overview of the Expansion of ENDP in the Induction Step

    In this message the system is saying that Subgoal *1/2 has been rewritten to the Subgoal *1/2', by expanding the definition of endp. This is an example of simplification, one of the main proof techniques used by the theorem prover.

    Click here if you would like to step through the simplification of Subgoal *1/2.




    acl2-sources/doc/HTML/Overview_of_the_Final_Simplification_in_the_Base_Case.html0000664002132200015000000000110212222333526027454 0ustar kaufmannacl2 Overview_of_the_Final_Simplification_in_the_Base_Case.html -- ACL2 Version 6.3

    Overview of the Final Simplification in the Base Case

    The But is our signal that the goal is proved.

    Click here to step through the proof. It is very simple.




    acl2-sources/doc/HTML/Overview_of_the_Proof_of_a_Trivial_Consequence.html0000664002132200015000000000362212222333526026262 0ustar kaufmannacl2 Overview_of_the_Proof_of_a_Trivial_Consequence.html -- ACL2 Version 6.3

    Overview of the Proof of a Trivial Consequence

    ACL2 !>(defthm trivial-consequence
             (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7)
                    (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7))))))
    
    ACL2 Warning [Subsume] in ( DEFTHM TRIVIAL-CONSEQUENCE ...):  The previously
    added rule ASSOCIATIVITY-OF-APP subsumes the newly proposed :REWRITE
    rule TRIVIAL-CONSEQUENCE, in the sense that the old rule rewrites a
    more general target.  Because the new rule will be tried first, it
    may nonetheless find application.
    
    By the simple :rewrite rule ASSOCIATIVITY-OF-APP we reduce the conjecture
    to
    
    Goal'
    (EQUAL (APP X1
                (APP X2
                     (APP X3 (APP X4 (APP X5 (APP X6 X7))))))
           (APP X1
                (APP X2
                     (APP X3 (APP X4 (APP X5 (APP X6 X7))))))).
    
    But we reduce the conjecture to T, by primitive type reasoning.
    
    Q.E.D.
    
    Summary
    Form:  ( DEFTHM TRIVIAL-CONSEQUENCE ...)
    Rules: ((:REWRITE ASSOCIATIVITY-OF-APP)
            (:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  Subsume
    Time:  0.20 seconds (prove: 0.02, print: 0.00, other: 0.18)
     TRIVIAL-CONSEQUENCE
    

    You might explore the links before moving on.




    acl2-sources/doc/HTML/Overview_of_the_Simplification_of_the_Base_Case_to_T.html0000664002132200015000000000210512222333526027332 0ustar kaufmannacl2 Overview_of_the_Simplification_of_the_Base_Case_to_T.html -- ACL2 Version 6.3

    Overview of the Simplification of the Base Case to T

    Subgoal *1/1
    (IMPLIES (ENDP A)
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    
    By the simple :definition ENDP we reduce the conjecture to
    
    Subgoal *1/1'
    (IMPLIES (NOT (CONSP A))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    
    But simplification reduces this to T, using the :definition APP and
    primitive type reasoning.
    
    




    acl2-sources/doc/HTML/Overview_of_the_Simplification_of_the_Induction_Conclusion.html0000664002132200015000000000137612222333526030701 0ustar kaufmannacl2 Overview_of_the_Simplification_of_the_Induction_Conclusion.html -- ACL2 Version 6.3

    Overview of the Simplification of the Induction Conclusion

    In this message the system is saying that Subgoal *1/2' has been rewritten to T using the rules noted. The word ``But'' at the beginning of the sentence is a signal that the goal has been proved.

    Click here to step through the proof of Subgoal *1/2'.




    acl2-sources/doc/HTML/Overview_of_the_Simplification_of_the_Induction_Step_to_T.html0000664002132200015000000000257112222333526030463 0ustar kaufmannacl2 Overview_of_the_Simplification_of_the_Induction_Step_to_T.html -- ACL2 Version 6.3

    Overview of the Simplification of the Induction Step to T

    Subgoal *1/2
    (IMPLIES (AND (NOT (ENDP A))
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    
    By the simple :definition ENDP we reduce the conjecture to
    
    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    
    But simplification reduces this to T, using the :definition APP, the
    :rewrite rules CDR-CONS and CAR-CONS and primitive type reasoning.
    




    acl2-sources/doc/HTML/PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS.html0000664002132200015000000000501612222333521022732 0ustar kaufmannacl2 PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS.html -- ACL2 Version 6.3

    PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS

    re-defining undone defpkgs
    Major Section:  MISCELLANEOUS
    

    Suppose (defpkg "pkg" imports) is the most recently executed successful definition of "pkg" in this ACL2 session and that it has since been undone, as by :ubt. Any future attempt in this session to define "pkg" as a package must specify an identical imports list.

    The restriction stems from the need to implement the reinstallation of saved logical worlds as in error recovery and the :oops command. Suppose that the new defpkg attempts to import some symbol, a::sym, not imported by the previous definition of "pkg". Because it was not imported in the original package, the symbol pkg::sym, different from a::sym, may well have been created and may well be used in some saved worlds. Those saved worlds are Common Lisp objects being held for you ``behind the scenes.'' In order to import a::sym into "pkg" now we would have to unintern pkg::sym, rendering those saved worlds ill-formed. It is because of saved worlds that we do not actually clear out a package when it is undone.

    At one point we thought it was sound to allow the new defpkg to import a subset of the old. But that is incorrect. Suppose the old definition of "pkg" imported a::sym but the new one does not. Suppose we allowed that and implemented it simply by setting the imports of "pkg" to the new subset. Then consider the conjecture (eq a::sym pkg::sym). This ought not be a theorem because we did not import a::sym into "pkg". But in fact in AKCL it is a theorem because pkg::sym is read as a::sym because of the old imports.




    acl2-sources/doc/HTML/PAIRLIS$.html0000664002132200015000000000207612222333524016124 0ustar kaufmannacl2 PAIRLIS$.html -- ACL2 Version 6.3

    PAIRLIS$

    zipper together two lists
    Major Section:  ACL2-BUILT-INS
    

    The Common Lisp language allows its pairlis function to construct an alist in any order! So we have to define our own version, pairlis$. It returns the list of pairs obtained by consing together successive respective members of the given lists until the first list runs out. (Hence in particular, if the second argument is nil then each element of the first argument is paired with nil.)

    The guard for pairlis$ requires that its arguments are true lists.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/PAIRLIS.html0000664002132200015000000000111612222333524016052 0ustar kaufmannacl2 PAIRLIS.html -- ACL2 Version 6.3

    PAIRLIS

    See pairlis$
    Major Section:  ACL2-BUILT-INS
    

    The Common Lisp language allows its pairlis function to construct an alist in any order! So we have to define our own version: See pairlis$.




    acl2-sources/doc/HTML/PAND.html0000664002132200015000000000753412222333522015501 0ustar kaufmannacl2 PAND.html -- ACL2 Version 6.3

    PAND

    parallel, Boolean version of and
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Example Forms:
    (pand (subsetp-equal x y)
          (subsetp-equal y x))
    
    (pand (declare
           (granularity
            (and (> (length x) 500)
                 (> (length y) 500))))
           (subsetp-equal x y)
           (subsetp-equal y x))
    

    General Form:
    (pand (declare (granularity expr)) ; optional granularity declaration
          arg1 ... argN)
    
    where N >= 0 and each argi and expr are arbitrary terms.

    Pand evaluates its arguments in parallel. It returns a Boolean result: nil if any argument evaluates to nil, else t. Note that pand always returns a Boolean result, even though and can return a non-nil value other than t, namely the value of its last argument. (A moment's reflection will make it clear that in order for por to parallelize efficiently, it needs to return a Boolean value; so pand returns a Boolean value for consistency with por.)

    Another difference between pand and and is that for a call of pand, even if an argument evaluates to nil, a subsequent argument may be evaluated. Consider the following example (where cw prints a string; see cw).

    (defun bar ()
      (pand (equal (make-list 100000) nil) ; evaluates to nil
            (cw "hello world~%")))
    
    When (bar) is evaluated, the above arguments of pand can execute in parallel, causing ``hello world'' to be printed to the terminal. If we had used and rather than pand, then since (equal (make-list 100000) nil) evaluates to nil, the above call of cw would be avoided and no such printing would take place. Even with pand, such printing might not take place, depending on resources, timing of thread creation, and whether or not parallel execution is enabled (see set-parallel-execution).

    Note that unlike the case for and, the definition of pand does not provide (consp x) as a guard to (car x) in the call of pand below:

    (defun h (x)
      (declare (xargs :guard t))
      (pand (consp x) (equal (car x) 'foo)))
    
    As a result, guard verification will fail for the above definition. If pand were replaced by and, then guard verification would succeed.

    See parallelism-tutorial for another example. Also see parallelism-at-the-top-level for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop. Finally see early-termination to read how pand can offer more efficiency than and by avoiding evaluation of some of its arguments.




    acl2-sources/doc/HTML/PARALLEL-EXECUTION.html0000664002132200015000000000144212222333522017504 0ustar kaufmannacl2 PARALLEL-EXECUTION.html -- ACL2 Version 6.3

    PARALLEL-EXECUTION

    for ACL2(p): configure parallel execution
    Major Section:  PARALLEL-PROGRAMMING
    

    See set-parallel-execution for how to configure parallel execution for calls of plet, pargs, pand, por (but not spec-mv-let).




    acl2-sources/doc/HTML/PARALLEL-PROGRAMMING.html0000664002132200015000000001150212222333522017721 0ustar kaufmannacl2 PARALLEL-PROGRAMMING.html -- ACL2 Version 6.3

    PARALLEL-PROGRAMMING

    parallel programming in ACL2(p)
    Major Section:  PARALLELISM
    

    Here we document support for parallel programming in ACL2(p), an experimental extension of ACL2; also see parallelism.

    Some Related Topics

    One of ACL2's strengths lies in its ability to execute industrial models efficiently. The ACL2 source code provides an experimental parallel execution capability that can increase the speed of explicit evaluation, including simulator runs using such models, and it can also decrease the time required for proofs that make heavy use of the evaluation of ground terms.

    The parallelism primitives are plet, pargs, pand, por, and spec-mv-let. Pand and por terminate early when an argument is found to evaluate to nil or non-nil, respectively, thus potentially improving on the efficiency of lazy evaluation. Spec-mv-let is a modification of mv-let that supports speculative and parallel execution.

    Of the above five parallelism primitives, all but spec-mv-let allow for limiting parallel execution (spawning of so-called ``threads'') depending on resource availability. Specifically, the primitives allow specification of a size condition to control the granularity under which threads are allowed to spawn. You can use such granularity declarations in recursively-defined functions to implement data-dependent parallelism in their execution.

    We recommend that in order to learn to use the parallelism primitives, you begin by reading examples: see parallelism-tutorial. That section will direct you to further documentation topics.

    In addition to providing parallel programming primitives, ACL2(p) also provides the ability to execute the main ACL2 proof process in parallel. See set-waterfall-parallelism for further details.




    acl2-sources/doc/HTML/PARALLEL-PROOF.html0000664002132200015000000000531612222333522017032 0ustar kaufmannacl2 PARALLEL-PROOF.html -- ACL2 Version 6.3

    PARALLEL-PROOF

    parallel proof in ACL2(p)
    Major Section:  PARALLELISM
    

    Here we document support for parallel proof in ACL2(p), an experimental extension of ACL2; also see parallelism, and for parallel programming in particular, see parallel-programming.

    Some Related Topics




    acl2-sources/doc/HTML/PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION.html0000664002132200015000000000555412222333522022763 0ustar kaufmannacl2 PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION.html -- ACL2 Version 6.3

    PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION

    consequences of how parallelized proofs of subgoals are pushed for induction
    Major Section:  PARALLEL-PROOF
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    The following discussion, concerning the naming of subgoals pushed for proof by induction and the timeliness of aborting when two or more goals are pushed for proof by induction, only applies when waterfall parallelism is enabled (see set-waterfall-parallelism).

    When two sibling subgoals (e.g. 4.5 and 4.6) both push goals to be proved by induction (e.g., 4.6 pushes *1 and 4.5 pushes *2), a name is assigned to the second pushed subgoal (e.g., *2) as if the first push hasn't happened (e.g., *2 is mistakenly called *1). In such a case, we say what the name _could_ be. The following non-theorem illustrates how this works.

    (set-waterfall-parallelism :full)
    (thm (equal (append (car (cons x x)) y z) (append x x y)))
    

    There is another consequence of the way the parallelized waterfall pushes subgoals for proof by induction. Without waterfall parallelism enabled, ACL2 sometimes decides to abort instead of pushing a goal for later proof by induction, preferring instead to induct on the original conjecture. But with waterfall parallelism enabled, the prover no longer necessarily immediately aborts to prove the original conjecture. Suppose for example that sibling subgoals, Subgoal 4.6 and Subgoal 4.5, each push a subgoal for induction. If the waterfall is performing the proof of each of these subgoals in parallel, the proof will no longer abort immediately after the second push occurs, that is at Subgoal 4.5. As a result, the prover will continue through Subgoal 4.4, Subgoal 4.3, and beyond. It is not until the results of combining the proof results of Subgoal 4.6 with the results from the remaining sibling subgoals (4.5, 4.4, and so on), that the proof attempt will abort and revert to prove the original conjecture by induction. This example illustrates behavior that is rather like the case that :otf-flg is t, in the sense that the abort does not happen immediately, but also rather like the case that :otf-flg is nil, in the sense that the abort does occur before getting to Subgoal 3.




    acl2-sources/doc/HTML/PARALLEL.html0000664002132200015000000000067312222333521016147 0ustar kaufmannacl2 PARALLEL.html -- ACL2 Version 6.3

    PARALLEL

    evaluating forms in parallel
    Major Section:  MISCELLANEOUS
    

    See parallelism.




    acl2-sources/doc/HTML/PARALLELISM-AT-THE-TOP-LEVEL.html0000664002132200015000000000575012222333522020747 0ustar kaufmannacl2 PARALLELISM-AT-THE-TOP-LEVEL.html -- ACL2 Version 6.3

    PARALLELISM-AT-THE-TOP-LEVEL

    parallel execution in the ACL2 top-level loop
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Calls of parallelism primitives made explicitly in the ACL2 top-level loop, as opposed to inside function bodies, will never cause parallel execution. Such calls will either execute with serial execution or will cause an error; see set-parallel-execution. For a way around this restriction, see top-level.

    Consider for example the following call of pargs in the ACL2 top-level loop. Instead of executing pargs, ACL2 macroexpands away this call, leaving us with serial execution of the arguments to the cons call, or else causes an error (see set-parallel-execution). If there is no error, then

    (pargs (cons (expensive-fn-1 4) (expensive-fn-2 5)))
    
    expands into:
    (cons (expensive-fn-1 4) (expensive-fn-2 5))
    

    One trivial way to enable parallel execution of a form is to surround it with a call to macro top-level. Consider the following example.

    (top-level (pargs (cons (expensive-fn-1 4) (expensive-fn-2 5))))
    
    Then in an executable image that supports parallel execution -- see compiling-acl2p for instructions on how to build such an executable -- (expensive-fn-1 4) and (expensive-fn-2 5) can evaluate in parallel.

    A second way to enable parallel execution of a form is to place it inside a function body. For example, consider the following definition.

    (defun foo (x y)
      (pargs (cons (expensive-fn-1 x) (expensive-fn-2 y))))
    
    Then in an executable image that supports parallel execution, submission of the form (foo 4 5) can cause parallel execution of (expensive-fn-1 4) and (expensive-fn-2 5).

    Note that guards need not be verified in order to obtain parallel execution. The only restrictions on parallel execution are to use an executable supporting it, to avoid calling parallelism primitives directly in the top-level loop, to have sufficient resources (especially, threads) available, and to avoid explicitly disabling parallel execution (see set-parallel-execution).




    acl2-sources/doc/HTML/PARALLELISM-BUILD.html0000664002132200015000000000076412222333521017356 0ustar kaufmannacl2 PARALLELISM-BUILD.html -- ACL2 Version 6.3

    PARALLELISM-BUILD

    building an ACL2 executable with parallel execution enabled
    Major Section:  MISCELLANEOUS
    

    See compiling-acl2p.




    acl2-sources/doc/HTML/PARALLELISM-PERFORMANCE.html0000664002132200015000000000420612222333522020254 0ustar kaufmannacl2 PARALLELISM-PERFORMANCE.html -- ACL2 Version 6.3

    PARALLELISM-PERFORMANCE

    performance issues for parallel execution
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    See granularity for an important construct that limits the spawning of parallel computations, which can be important when a computation is too short-lived to warrant a separate thread.

    There are times in which parallelism provides no speedup because of garbage collection in the underlying Lisp implementation. The following example illustrates this phenomenon. If you change the granularity declaration so that the depth bound is 3, 4, or larger instead of 2, you may still find no speedup. In all cases you may find that parallelism results in a significantly greater time spent in garbage collection.

    (include-book "finite-set-theory/osets/sets" :dir :system)
    (defun sets::pmergesort-exec (x depth)
        (declare (xargs :mode :program))
        (cond ((endp x) nil)
              ((endp (cdr x)) (sets::insert (car x) nil))
              (t (mv-let (part1 part2)
                         (sets::split-list x nil nil)
                         (pargs
                          (declare (granularity (< depth 2)))
                          (sets::union (sets::pmergesort-exec part1
                                                              (1+ depth))
                                       (sets::pmergesort-exec part2
                                                              (1+ depth))))))))
    (defconst *x* (reverse (fromto 1 400000)))
    (time$ (length (sets::pmergesort-exec *x* 0)))
    (set-parallel-execution nil)
    (time$ (length (sets::pmergesort-exec *x* 0)))
    




    acl2-sources/doc/HTML/PARALLELISM-TUTORIAL.html0000664002132200015000000003107112222333522017756 0ustar kaufmannacl2 PARALLELISM-TUTORIAL.html -- ACL2 Version 6.3

    PARALLELISM-TUTORIAL

    a tutorial on how to use the parallelism library.
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    In this topic we introduce the ACL2 parallelism primitives using the example of a doubly-recursive Fibonacci function, whose basic definition is as follows. See parallelism for a very high-level summary of the parallelism capability described here, and see compiling-acl2p for how to build an executable image that supports parallel execution. Here, we assume that such an executable is being used.

    Serial Fibonacci

    (defun fib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (+ (fib (- x 1)) (fib (- x 2))))))
    

    Introducing Pargs

    A simple way to introduce parallelism into this function definition is to wrap the addition expression with a call of pargs, and the arguments to the addition will be computed in parallel whenever resources are available. As a result, we end up with a very similar and thus intuitive function definition. Note that we replaced + by binary-+, since pargs expects a function call, not a macro call.

    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (pargs (binary-+ (pfib (- x 1))
                                (pfib (- x 2)))))))
    

    Introducing the Granularity Problem

    After you submit the above two versions of the Fibonacci function, test them with the following forms.

    (time$ (fib 10))
    (time$ (pfib 10))
    
    Now increase the argument by increments of 5 to 10 until you find your curiosity satisfied or your patience wearing thin. You can interrupt evaluation if necessary and return to the ACL2 loop. You will immediately notice that you have not increased execution speed, at least not by much, by introducing parallelism.

    First, consider the computation of (pfib 4). Assuming resources are available, (pfib 4) will create a thread for computing (pfib 3) and another thread for (pfib 2). It is easy to imagine that setting up each thread takes much longer than the entire computation of (fib 4).

    Second, we must realize that if we have two threads available for computing (fib 10), then the evaluation of (fib 8) will probably complete before the evaluation of (fib 9). Once (fib 8) finishes, parallelism resources will become available for the next recursive call made on behalf of (fib 9). If for example that call is (fib 3), we will waste a lot of cycles just handing work to the thread that does this relatively small computation. We need a way to ensure that parallelism resources are only used on problems of a "large" size. Ensuring that only "large" problems are spawned is called the ``granularity problem.''

    In summary: We want to tell ACL2 that it can evaluate the arguments of pargs in parallel only when the parameter of pfib is greater than some threshold. Our tests using CCL have suggested that 27 is a reasonable threshold.

    Explicit Programming for the Granularity Problem

    One way to avoid the granularity problem is to duplicate code as follows.

    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (if (> x 27) ; the granularity test
                   (pargs (binary-+ (pfib (- x 1))
                                    (pfib (- x 2))))
                 (binary-+ (pfib (- x 1))
                           (pfib (- x 2)))))))
    
    Duplicating code is fundamentally a bad design principle, because it can double the work for future maintenance. A ``granularity form'' is an expression
    (declare (granularity <form>))
    
    that can allow threads to be spawned (without duplicating code) whenever the evaluation of <form> results in a non-nil value. It may be placed inside any call of a parallelism primitive, in a position documentated separately for each primitive. Here is a definition of pfib using this feature for a call of the parallelism primitive pargs.
    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (pargs
                (declare (granularity (> x 27)))
                (binary-+ (pfib (- x 1))
                          (pfib (- x 2)))))))
    

    Test each version as follows (or substitute your own natural number for 33).

      (time$ (fib 33))
      (time$ (pfib 33))
    

    Another Granularity Issue Related to Thread Limitations

    Our implementation of parallelism primitives has the property that once a thread is assigned a computation, that assignment stays in effect until the computation is complete. In particular, if a thread encounters a parallelism primitive that spawns child threads, the parent thread stays assigned, waiting until the child computations complete before it can continue its own computation. In the meantime, the parent thread reduces the number of additional threads that Lisp can provide by 1, rather than being reassigned to do other work.

    How can this lack of reassignment affect the user? Consider, for example, the application of a recursive function to a long list. Imagine that this function is written so that the body contains a recursive call, for example as (pargs (process (car x)) (recur (cdr x))). Each such pargs call that spawns child work must wait for its children, one of which is the work of evaluating (recur (cdr x)), to complete. There is an ACL2 limit on how much pending work can be in the system, limiting the number of pargs calls that can result in parallel execution. For example, if the ACL2 limit is k and each call of pargs actually spawns threads for evaluating its arguments, then after k cdrs there will be no further parallel execution.

    Possible solutions may include reworking of algorithms (for example to be tree-based rather than list-based) or using appropriate granularity forms. We hope that future implementations will allow thread ``re-deployment'' in order to mitigate this problem. This problem applies to plet, pargs, pand, por, and spec-mv-let.

    Introducing Plet

    We can use a let binding to compute the recursive calls of fib and then add the bound variables together, as follows.

    (defun fib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (let ((a (fib (- x 1)))
                     (b (fib (- x 2))))
                 (+ a b)))))
    

    By using the parallelism primitive plet, we can introduce parallelism much as we did using pargs, with an optional granularity form, as follows.

    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((or (zp x) (<= x 0)) 0)
            ((= x 1) 1)
            (t (plet
                (declare (granularity (> x 27)))
                ((a (pfib (- x 1)))
                 (b (pfib (- x 2))))
                (+ a b)))))
    
    Notice that this time, we were able to use + rather than being forced to use binary-+. Unlike pargs, which expects a function call (not a macro call), plet allows macros at the top level of its body.

    Introducing Pand (By Way of a Tree Validation Example)

    Consider ``genetic trees'' that contains leaves of DNA elements, in the sense that each tip is one of the symbols A, G, C, or T. First we define the function valid-tip which recognizes whether a tip contains one of these symbols.

    (defun valid-tip (tip)
      (declare (xargs :guard t))
      (or (eq tip 'A)
          (eq tip 'G)
          (eq tip 'C)
          (eq tip 'T)))
    

    Now we define a function that traverses the tree, checking that every tip is valid.

    (defun valid-tree-serial (tree)
      (declare (xargs :guard t))
      (if (atom tree)
          (valid-tip tree)
        (and (valid-tree-serial (car tree))
             (valid-tree-serial (cdr tree)))))
    

    We also define a parallel version.

    (defun valid-tree-parallel (tree)
      (declare (xargs :guard t))
      (if (atom tree)
          (valid-tip tree)
        (pand (valid-tree-parallel (car tree))
              (valid-tree-parallel (cdr tree)))))
    

    Before we can time the functions, we need to create test trees. We have found that test trees need to be approximately 1 gigabyte before we clearly see speedup, and we make them asymmetric to demonstrate the ability of our implementation to adapt to asymmetric data. We can create the trees with the execution of the following forms.

    (defun make-valid-binary-tree (x)
      (declare (xargs :mode :program))
      (if (< x 0)
          (cons (cons 'C 'G) (cons 'A 'T))
        (cons (make-valid-binary-tree (- x 2)) ; 2 to make asymmetric
              (make-valid-binary-tree (- x 1)))))
    
    (defconst *valid-tree* (make-valid-binary-tree 30)) ; takes awhile
    (defconst *invalid-tree* (cons *valid-tree* nil)) ; nil is invalid tip
    

    We can time our functions with the forms:

    (time$ (valid-tree-serial *valid-tree*))
    (time$ (valid-tree-parallel *valid-tree*))
    
    Unfortunately, the serial version runs faster than the parallelism version; however, there is more to this story.

    Demonstrating Early Termination with an Invalid Tree

    Now observe this magic:

    (time$ (valid-tree-serial   *invalid-tree*))
    (time$ (valid-tree-parallel *invalid-tree*))
    
    The serial version took awhile, while the parallel version finished almost immediately. The test for validation was split into testing the car and the cdr of the *invalid-tree* root, and since the cdr was equal to nil, its test returned immediately. This immediate return quickly interrupted the computation associated with the car, and returned the result.

    Granularity with Pand

    We can also define a parallel version with a granularity form:

    (defun valid-tree-parallel (tree depth)
      (declare (xargs :guard (natp depth)))
      (if (atom tree)
          (valid-tip tree)
        (pand
         (declare (granularity (< depth 5)))
         (valid-tree-parallel (car tree) (1+ depth))
         (valid-tree-parallel (cdr tree) (1+ depth)))))
    

    We can test this form by executing our previous forms. You will probably find some speedup on a machine with several cores available, but more speedup can likely be obtained with an expensive test on the leaves in place of valid-tip.

    (time$ (valid-tree-serial   *valid-tree*))
    (time$ (valid-tree-parallel *valid-tree* 0))
    

    Introducing Por

    Por can be used in the same way as pand, but with early termination occurring when an argument evaluates to a non-nil value, in which case the value returned is t. Finally, por also supports the use of a granularity form.




    acl2-sources/doc/HTML/PARALLELISM.html0000664002132200015000000000600412222333522016513 0ustar kaufmannacl2 PARALLELISM.html -- ACL2 Version 6.3

    PARALLELISM

    experimental extension for parallel execution and proofs
    Major Section:  ACL2 Documentation
    

    This documentation topic relates to an experimental extension of ACL2, ACL2(p), created initially by David L. Rager. See compiling-acl2p for how to build an executable image that supports parallel execution. Also see community books directory books/parallel/ for examples. For a completely different sort of parallelism, at the system level, see provisional-certification.

    Some Related Topics

    IMPORTANT NOTE. We hope and expect that every evaluation result is correctly computed by ACL2(p), and that every formula proved using ACL2(p) is a theorem of the ACL2 logic (and in fact is provable using ACL2). However, we do not guarantee these properties. Since ACL2(p) is intended to be an aid in efficient evaluation and proof development, we focus less on ironclad soundness and more on providing an efficient and working implementation. Nevertheless, if you encounter a case where ACL2(p) computes an incorrect result, or produces a proof where ACL2 fails to do so (and this failure is not discussed in unsupported-waterfall-parallelism-features), please notify the implementors.

    The ACL2 source code provides experimental parallel execution and proof capabilities. For example, one of ACL2's strengths lies in its ability to simulate industrial models efficiently, and it can also decrease the time required for proofs about such models both by making use of parallel evaluation and by dispatching proof subgoals in parallel.

    While we aim to support Clozure Common Lisp (CCL), Steel Bank Common Lisp (SBCL), and Lispworks, SBCL and Lispworks both currently sometimes experience problems when evaluating the ACL2 proof process (the ``waterfall'') in parallel. Therefore, CCL is the recommend Lisp for anyone that wants to use parallelism and isn't working on fixing those problems.




    acl2-sources/doc/HTML/PARGS.html0000664002132200015000000000404312222333522015623 0ustar kaufmannacl2 PARGS.html -- ACL2 Version 6.3

    PARGS

    parallel evaluation of arguments in a function call
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Example Forms:
    (pargs (binary-+ (fibonacci (- x 1)) (fibonacci (- x 2))))
    
    (pargs (declare (granularity (> x 35)))
           (binary-+ (fibonacci (- x 1)) (fibonacci (- x 2))))
    
    General Form:
    (pargs (declare (granularity expr)) ; optional granularity declaration
           (f arg1 ... argN))
    
    where N >= 0 and each argi and expr are arbitrary terms. Logically, pargs is just the identity macro, in the sense that the above forms can logically be replaced by (f arg1 ... argN). However, this pargs form may parallelize the evaluation of arguments arg1 through argN before applying function f to those results. If the above granularity declaration is present, then its expression (expr above) is first evaluated, and if the result is nil, then such parallelism is avoided. Even if parallelism is not thus avoided, parallelism may be limited by available resources.

    Since macros can change expressions in unexpected ways, it is illegal to call pargs on a form that is a macro call. To parallelize computation of arguments to a macro, see plet.

    See parallelism-at-the-top-level for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop.




    acl2-sources/doc/HTML/PATHNAME.html0000664002132200015000000000536312222333516016155 0ustar kaufmannacl2 PATHNAME.html -- ACL2 Version 6.3

    PATHNAME

    introduction to filename conventions in ACL2
    Major Section:  BOOKS
    

    The notion of pathname objects from Common Lisp is not supported in ACL2, nor is the function pathname. However, ACL2 supports file operations, using conventions for naming files based on those of the Unix (trademark of AT&T) operating system, so that the character / is used to terminate directory names. Some file names are ``absolute'' (complete) descriptions of a file or directory; others are ``relative'' to the current working directory or to the connected book directory (see cbd). We emphasize that even for users of Windows-based systems or Macintosh computers, ACL2 file names are in the Unix style. We will call these ACL2 pathnames, often omitting the ``ACL2.''

    Pathnames starting with the directory separator (/) or the tilde character (~) are absolute pathnames. All other pathnames are relative pathnames. An exception is in the Microsoft Windows operating system, where it is illegal for the pathname to start with a tilde character but the drive may be included, e.g., "c:/home/smith/acl2/book-1.lisp". In fact, the drive must be included in the portcullis of a book; see portcullis. Note also that some host Common Lisps will not support pathnames starting with "~", for example ~smith, though ACL2 will generally support those starting with "~/" regardless of the host Common Lisp.

    Consider the following examples. The filename string

    "/home/smith/acl2/book-1.lisp"
    
    is an absolute pathname, with top-level directory "home", under that the directory "smith" and then the directory "acl2", and finally, within that directory the file "book-1.lisp". If the connected book directory is "/home/smith/" (see cbd), then the filename string above also corresponds to the relative filename string "acl2/book1.lisp".

    Finally, we note that (on non-Windows systems) the pathname "~" and pathnames starting with "~/" are illegal in books being certified. Otherwise, a subsidiary include-book form would have a different meaning at certification time than at a later time when the certified book is included by a different user.




    acl2-sources/doc/HTML/PBT.html0000664002132200015000000000263112222333517015401 0ustar kaufmannacl2 PBT.html -- ACL2 Version 6.3

    PBT

    print the commands back through a command descriptor
    Major Section:  HISTORY
    

    Examples:
    :pbt :max      ; print back through the most recent command
    :pbt :x        ; print back through the most recent command
    :pbt fn        ; print back through the introduction of fn
    :pbt 5         ; print back through the fifth command executed
    :pbt (:x -4)   ; print back through the most recent five commands
    
    See command-descriptor.

    Pbt takes one argument, a command descriptor, and prints the commands from :max (aka :x) through the one described. See command-descriptor for a description of what a command descriptor is. See pc for a description of the format used to display commands. Pbt will print the commands that ubt will undo.




    acl2-sources/doc/HTML/PC.html0000664002132200015000000002056512222333517015264 0ustar kaufmannacl2 PC.html -- ACL2 Version 6.3

    PC

    print the command described by a command descriptor
    Major Section:  HISTORY
    

    Examples:
    :pc 3    ; print the third command executed
    :pc :max ; print the most recent command
    :pc :x   ; print the most recent command
    :pc fn   ; print the command that introduced fn
    
    See command-descriptor.

    Pc takes one argument, a command descriptor, and prints the command identified by that descriptor. See command-descriptor. For example

    ACL2 !>:pc foo
     LVd     52 (DEFUN FOO (X) X)
    
    Pc always prints a space first, followed by three (possibly blank) characters (``LVd'' above) explained below (four, in the experimental HONS version, as discussed further below). Then pc prints the command number, a number uniquely identifying the command's position in the sequence of commands since the beginning of the user's session. Finally, the command itself is printed.

    While pc always prints a space first, some history commands, for example :pcs and :pe, use the first column of output to delimit a region of commands or to point to a particular event within a command.

    For example, :pcs 52 54 will print something like

    /LVd     52 (DEFUN FOO (X) X)
     LV      53 (DEFUN BAR (X) (CONS X X))
    \        54 (DEFTHM FOO-BAR (EQUAL (CAR (BAR X)) (FOO X)))
              : ...
            127 (DEFUN LATEST (X) X)
    
    Here, the two slash characters in the first column are intended to suggest a bracket delimiting commands 52 through 54. The last command printed by pcs is always the most recent command, i.e., the command at :here, and is separated from the rest of the display by an elipsis if some commands are omitted.

    Similarly, the :pe command will print a particular event within a command block and will indicate that event by printing a ``>'' in the first column. The symbol is intended to be an arrow pointing at the event in question.

    For example, :pe true-listp-app might print:

             1 (INCLUDE-BOOK "list-book")
                \
    >           (DEFTHM TRUE-LISTP-APP
                        (EQUAL (TRUE-LISTP (APP A B)) (TRUE-LISTP B)))
    
    using the arrow to indicate the event itself. The slash printed to connect the command, include-book, with the event, defthm, is intended to suggest a tree branch indicating that the event is inferior to (and part of) the command.

    The mysterious characters sometimes preceding a command have the following interpretations. The first two have to do with the function symbols introduced by the command and are blank if no symbols were introduced.

    At any time we can classify our function symbols into disjoint sets, which we will here name with characters. The ``P'' functions are those in :program mode. The ``L'' functions are those in :logic mode whose guards have not been verified. The ``V'' functions are those in :logic mode whose guards have been verified. You may also see the use of (lower-case) ``v'' to indicate functions introduced by encapsulate. Note that verify-termination and verify-guards cause function symbols to be reclassified. If a command introduces function symbols then the first mysterious character indicates the class of the symbols at the time of introduction and the second character indicates the current class of the symbols (if the current class is different from the introductory class).

    Thus, the display

     PLd     52 (DEFUN FOO (X) X)
    
    tells us that command 52 introduced a :program function but that some command after 52 changed its mode to :logic and that the guards of foo have not been verified. That is, foo's termination has been verified even though it was not verified as part of the command that introduced foo. Had a subsequent command verified the guards of foo, the display would contain a V where the L is.

    The display

     P d     52 (DEFUN FOO (X) X)
    
    indicates that foo was introduced in :program mode and still is in that mode.

    The third character indicates the enabled/disabled status of the runes introduced by the command. If the status character is blank then all the runes (if any) introduced are enabled. If the status character is ``D'' then some runes were introduced and they are all disabled. If the status character is ``d'' then at least one, but not all, of the runes introduced is disabled. Thus, in the display

     L d     52 (DEFUN FOO (X) X)
    
    we see that some rune introduced by command 52 is disabled. As noted in the documentation for rune, a defun command introduces many runes, e.g., the axiomatic definition rule, (:definition fn), the executable counterpart rule, (:executable-counterpart fn), and type-prescriptions, (:type-prescription fn). The display above does not say which of the runes based on foo is disabled, but it does tell us one of them is; see disabledp for how to obtain the disabled runes for a given function symbol.

    Finally, for the experimental HONS version only (see hons-and-memoization), a fourth character is printed, indicating whether functions are memoized. A symbol may be memoized if it is a function symbol that is not constrained (i.e., introduced by defchoose or in the signature of an encapsulate event). If the command introduces no symbol that may be memoized, then a space is printed. Otherwise, if every memoizable symbol is memoized, an ``M'' is printed. Otherwise, an ``m'' is printed.




    acl2-sources/doc/HTML/PCB.html0000664002132200015000000000460512222333517015363 0ustar kaufmannacl2 PCB.html -- ACL2 Version 6.3

    PCB

    print the command block described by a command descriptor
    Major Section:  HISTORY
    

    Examples:
    :pcb :max ; print the most recent command block
    :pcb :x   ; print the most recent command block
    :pcb fn   ; print the command block that introduced fn
    :pcb 5    ; print the fifth command block
    
    See command-descriptor.

    Pcb takes one argument, a command descriptor, and prints the command block of the command described. See command-descriptor for details of command descriptors. See pc for description of the format in which commands are displayed. The command block of a command consists of the command itself and all of the events it created. If the command created a single event and that event is in fact the command (i.e., if the command typed was just an event such as a defun or defthm rather than a macro that expanded to some event forms), then pcb just prints the command. Pcb sketches command and all of the events it created, rather than printing them fully. If you wish to see just the command, in its entirety, use pc. If you wish to see one of the events within the block, in its entirety, use pe. If you wish to see the command sketched and all of the events it created, in their entirety, use pcb!.




    acl2-sources/doc/HTML/PCB_bang_.html0000664002132200015000000000210512222333517016502 0ustar kaufmannacl2 PCB_bang_.html -- ACL2 Version 6.3

    PCB!

    print in full the command block described by a command descriptor
    Major Section:  HISTORY
    

    Examples:
    :pcb! :max ; print the most recent command block
    :pcb! :x   ; print the most recent command block
    :pcb! fn   ; print the command block that introduced fn
    :pcb! 5    ; print the fifth command block
    
    See command-descriptor.

    Pcb! takes one argument, a command descriptor, and prints the command block of the command described. Unlike pcb, pcb! prints the event forms in full; see pcb for details.




    acl2-sources/doc/HTML/PCS.html0000664002132200015000000000263412222333517015404 0ustar kaufmannacl2 PCS.html -- ACL2 Version 6.3

    PCS

    print the sequence of commands between two command descriptors
    Major Section:  HISTORY
    

    Examples:
    :pcs 1 5              ; print commands 1 through 5
    :pcs 5 1              ; same as above
    :pcs :x (:x -3)       ; print the 3 most recently executed commands
    :pcs fn assoc-of-fn   ; print the commands between the one that introduced
                          ; fn and the one that introduced assoc-of-fn
    

    Pcs takes two arguments, both of which are command descriptors, and prints the commands between them with pc. The order of the two descriptors is irrelevant. See command-descriptor for a description of command descriptors. See pc for a description of the format in which commands are displayed.

    For a related utility that simply returns a sequence of commands, see get-command-sequence.




    acl2-sources/doc/HTML/PE.html0000664002132200015000000000437012222333517015262 0ustar kaufmannacl2 PE.html -- ACL2 Version 6.3

    PE

    print the events named by a logical name
    Major Section:  HISTORY
    

    Example:
    :pe fn   ; sketches the command that introduced fn and
             ; prints in full the event within it that created fn.
    
    See logical-name.

    Pe takes one argument, a logical name, and prints in full the event corresponding to the name. Pe also sketches the command responsible for that event if the command is different from the event itself. See pc for a description of the format used to display a command. To remind you that the event is inferior to the command, i.e., you can only undo the entire command, not just the event, the event is indented slightly from the command and a slash (meant to suggest a tree branch) connects them.

    If the given logical name corresponds to more than one event, then :pe will print the above information for every such event. Here is an example of such behavior.

    ACL2 !>:pe nth
          -4270  (ENCAPSULATE NIL ...)
                 \
    >V            (VERIFY-TERMINATION NTH)
    
    Additional events for the logical name NTH:
     PV   -4949  (DEFUN NTH (N L)
                        "Documentation available via :doc"
                        (DECLARE (XARGS :GUARD (AND (INTEGERP N)
                                                    (>= N 0)
                                                    (TRUE-LISTP L))))
                        (IF (ENDP L)
                            NIL
                            (IF (ZP N)
                                (CAR L)
                                (NTH (- N 1) (CDR L)))))
    ACL2 !>
    
    If you prefer to see only the formula for the given name, for example if it is part of a large mutual-recursion, see pf.




    acl2-sources/doc/HTML/PEEK-CHAR$.html0000664002132200015000000000062112222333524016252 0ustar kaufmannacl2 PEEK-CHAR$.html -- ACL2 Version 6.3

    PEEK-CHAR$

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/PE_bang_.html0000664002132200015000000000100012222333517016373 0ustar kaufmannacl2 PE_bang_.html -- ACL2 Version 6.3

    PE!

    deprecated (see pe)
    Major Section:  HISTORY
    

    Please see pe. :Pe now has the functionality formerly provided by :pe!.




    acl2-sources/doc/HTML/PF.html0000664002132200015000000000165212222333517015263 0ustar kaufmannacl2 PF.html -- ACL2 Version 6.3

    PF

    print the formula corresponding to the given name
    Major Section:  HISTORY
    

    Examples:
    :pf (:definition fn) ; prints the definition of fn as an equality
    :pf fn               ; same as above
    
    :pf (:rewrite foo)   ; prints the statement of the rewrite rule foo
    :pf foo              ; same as above
    

    pf takes one argument, an event name or a rune, and prints the formula associated with name. If the argument is the name of a macro associated with a function name by macro-aliases-table, then the function name is used as the argument.




    acl2-sources/doc/HTML/PKG-IMPORTS.html0000664002132200015000000000571012222333524016527 0ustar kaufmannacl2 PKG-IMPORTS.html -- ACL2 Version 6.3

    PKG-IMPORTS

    list of symbols imported into a given package
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-pkg-imports):

    (equal (pkg-imports x)
           (if (stringp x)
               (pkg-imports x)
             nil))
    

    Guard for (pkg-imports x):

    (stringp x)
    

    (Pkg-imports pkg) returns a duplicate-free list of all symbols imported into pkg, which should be the name of a package known to ACL2. For example, suppose "MY-PKG" was created by

    (defpkg "MY-PKG" '(ACL2::ABC LISP::CAR)).
    
    Then (pkg-imports "MY-PKG") equals the list (ACL2::ABC LISP::CAR).

    If pkg is not a string, then (pkg-imports pkg) is nil. If pkg is a string but not the name of a package known to ACL2, then the value of the form (pkg-imports pkg) is unspecified, and it evaluation will fail to yield a value. By ``the symbols imported into pkg'' we mean the symbols imported into pkg by the defpkg event that introduced pkg. There are no imports for built-in packages except for the "ACL2" package, which imports the symbols in the list value of the constant *common-lisp-symbols-from-main-lisp-package*. In particular, this is the case for the "COMMON-LISP" package. Users familiar with Common Lisp may find this surprising, since in actual Common Lisp implementations it is often the case that many symbols in that package are imported from other packages. However, ACL2 treats all symbols in the constant *common-lisp-symbols-from-main-lisp-package* as having a symbol-package-name of "COMMON-LISP", as though they were not imported. ACL2 admits a symbol imported into in the "COMMON-LISP" package only if it belongs to that list: any attempt to read any other symbol imported into the "COMMON-LISP" package, or to produce such a symbol with intern$ or intern-in-package-of-symbol, will cause an error.

    The following axioms formalize properties of pkg-imports discussed above (use :pe to view them).

    symbol-package-name-intern-in-package-of-symbol
    intern-in-package-of-symbol-is-identity
    symbol-listp-pkg-imports
    no-duplicatesp-pkg-imports
    completion-of-pkg-imports
    




    acl2-sources/doc/HTML/PKG-WITNESS.html0000664002132200015000000000214212222333524016522 0ustar kaufmannacl2 PKG-WITNESS.html -- ACL2 Version 6.3

    PKG-WITNESS

    return a specific symbol in the indicated package
    Major Section:  ACL2-BUILT-INS
    

    For any string pkg that names a package currently known to ACL2, (pkg-witness pkg) is a symbol in that package whose symbol-name is the value of constant *pkg-witness-name*. Logically, this is the case even if the package is not currently known to ACL2. However, if pkg-witness is called on a string that is not the name of a package known to ACL2, a hard Lisp error will result.

    (Pkg-witness pkg) has a guard of (and (stringp pkg) (not (equal pkg ""))). If pkg is not a string, then (pkg-witness pkg) is equal to (pkg-witness "ACL2")




    acl2-sources/doc/HTML/PL.html0000664002132200015000000000630612222333517015272 0ustar kaufmannacl2 PL.html -- ACL2 Version 6.3

    PL

    print the rules for the given name or term
    Major Section:  HISTORY
    

    Examples:
    :pl foo     ; prints rules that rewrite some call of foo
    :pl (+ x y) ; prints rules that rewrite (+ x y)
    

    Also see pl2, which restricts output to rules that you specify for a given term.

    Pl takes one argument, which should be a symbol or a term.

    First suppose that the argument is a symbol. Then it should be either a function symbol or else a macro alias for a function symbol (see macro-aliases-table), which is treated as the corresponding function symbol. In this case :pl displays rules that apply to terms whose top function symbol is the one specified, specifically, rules of class :rewrite, :definition, :meta, :linear, :type-prescription, :forward-chaining, :elim, and :induction.

    Otherwise the argument should be a term (in user syntax, so that for example macros are permitted). In this case, :pl displays the :rewrite, :definition, and :meta rules that rewrite the specified term, followed by the applicable :type-prescription rules. Each rule is displayed with additional information, such as the hypotheses that remain after applying some simple techniques to discharge them that are likely to apply in any context. Note that for :meta rules, only those are displayed that meet two conditions: the application of the metafunction returns a term different from the input term, and if there is a hypothesis metafunction then it also returns a term. (A subtlety: In the case of extended metafunctions (see extended-metafunctions), a trivial metafunction context is used for the application of the metafunction.)

    Note that some rule classes are not handled by :pl. In particular, if you want to see all :clause-processor rules, issue the command :print-clause-processor-rules, and for trusted clause-processors, (table trusted-clause-processor-table); see clause-processor and see define-trusted-clause-processor.




    acl2-sources/doc/HTML/PL2.html0000664002132200015000000000370512222333517015354 0ustar kaufmannacl2 PL2.html -- ACL2 Version 6.3

    PL2

    print rule(s) for the given form
    Major Section:  HISTORY
    

    Examples:
    :pl2 (+ x y) nil ; prints rules that apply to (+ x y)
    :pl2 (+ x y) foo ; prints rules named foo that apply to (+ x y)
    :pl2 (+ x y) (:rewrite foo) ; if the rule with rune (:rewrite foo) applies
                                ;   to (+ x y), then print it
    :pl2 (+ x y) (:type-prescription foo)
                                ; as above, but for the indicated
                                ;   type-prescription rule
    

    Pl2 takes two arguments. The first is a term. The second is either nil or a ``rule-id'' that is either a symbol or a rune. The result is to print rules of class :rewrite, :definition, :meta, :linear, and :type-prescription that apply to the given term. Indeed, :pl2 prints exactly what is printed by applying :pl to the first argument -- see pl -- except that if the second argument is not nil then it is used to filter the rules printed, as follows.

    If the rule-id is a symbol, then only rules whose name is that symbol will be printed.

    If the rule-id is a rune, then at most one rule will be printed: the rule named by that rune (if the rule would be printed by :pl).




    acl2-sources/doc/HTML/PLET.html0000664002132200015000000000412112222333522015510 0ustar kaufmannacl2 PLET.html -- ACL2 Version 6.3

    PLET

    parallel version of let
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Example Forms:
    (plet ((a (fibonacci (- x 1)))
           (b (fibonacci (- x 2))))
          (+ a b))
    
    (plet (declare (granularity (> x 35)))
          ((a (fibonacci (- x 1)))
           (b (fibonacci (- x 2))))
          (+ a b))
    
    General Form:
    (plet (declare (granularity expr)) ; optional granularity declaration
          ((var1 val1)
           ...
           (varN valN))
          (declare ...) ... (declare ...) ; optional declarations
          body)
    
    The syntax of plet is identical to the syntax of let, except that plet permits an optional granularity declaration in the first argument position; see granularity. In the logic a call of plet macroexpands to the corresponding call of let, where the granularity declaration (if any) is dropped.

    Plet cause the evaluation of each vali above to be done in parallel before processing the body. If the above granularity declaration is present, then its expression (expr above) is first evaluated, and if the result is nil, then such parallelism is avoided. Even if parallelism is not thus avoided, parallelism may be limited by available resources.

    See parallelism-at-the-top-level for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop.




    acl2-sources/doc/HTML/PLUSP.html0000664002132200015000000000144112222333524015653 0ustar kaufmannacl2 PLUSP.html -- ACL2 Version 6.3

    PLUSP

    test whether a number is positive
    Major Section:  ACL2-BUILT-INS
    

    (Plusp x) is true if and only if x > 0.

    The guard of plusp requires its argument to be a rational (real, in ACL2(r)) number.

    Plusp is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/POR.html0000664002132200015000000000520012222333522015403 0ustar kaufmannacl2 POR.html -- ACL2 Version 6.3

    POR

    parallel, Boolean version of or
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Example Forms:
    (por (subsetp-equal x y)
         (subsetp-equal y x))
    
    (por (declare
          (granularity
           (and (> (length x) 500)
                (> (length y) 500))))
          (subsetp-equal x y)
          (subsetp-equal y x))
    

    General Form:
    (por (declare (granularity expr)) ; optional granularity declaration
         arg1 ... argN)
    
    where N >= 0 and each argi and expr are arbitrary terms.

    Por evaluates its arguments in parallel. It returns a Boolean result: t if any argument evaluates to non-nil, else nil. Note that while or returns the first non-nil value from evaluating its arguments left-to-right (if any such value is not nil) por always returns a Boolean result, in support of efficiency (see early-termination) in light of the nondeterministic order in which argument values are returned.

    Another difference between por and or is that for a call of por, even if the an argument's value is not nil, a subsequent argument may be evaluated. See pand for a discussion of the analogous property of pand. In particular, guards generated from calls of por may not assume for an argument that the preceding arguments evaluated to nil.

    See parallelism-tutorial for another example. Also see parallelism-at-the-top-level for restrictions on evaluating parallelism primitives from within the ACL2 top-level loop. Finally see early-termination to read how por can offer more efficiency than or by avoiding evaluation of some of its arguments.




    acl2-sources/doc/HTML/PORTCULLIS.html0000664002132200015000000001221612222333516016453 0ustar kaufmannacl2 PORTCULLIS.html -- ACL2 Version 6.3

    PORTCULLIS

    the gate guarding the entrance to a certified book
    Major Section:  BOOKS
    

    The certificate (see certificate for general information) of a certified file is divided into two parts, a portcullis and a keep. These names come from castle lore. The portcullis of a castle is an iron grate that slides up through the ceiling of the tunnel-like entrance. The portcullis of a book ensures that include-book does not start to read the book until the appropriate context has been created.

    Technically, the portcullis consists of the version number of the certifying ACL2, a list of commands used to create the ``certification world'' and an alist specifying the check sums of all the books included in that world. The portcullis is constructed automatically by certify-book from the world in which certify-book is called, but that world must have certain properties described below. After listing the properties we discuss the issues in a more leisurely manner.

    Each command in the portcullis must be either a defpkg form or an embedded event form (see embedded-event-form).

    Consider a book to be certified. The book is a file containing event forms. Suppose the file contains references to such symbols as my-pkg::fn and acl2-arith::cancel, but that the book itself does not create the packages. Then a hard Lisp error would be caused merely by the attempt to read the expressions in the book. The corresponding defpkgs cannot be written into the book itself because the book must be compilable and Common Lisp compilers differ on the rules concerning the inline definition of new packages. The only safe course is to make all defpkgs occur outside of compiled files.

    More generally, when a book is certified it is certified within some logical world. That ``certification world'' contains not only the necessary defpkgs but also, perhaps, function and constant definitions and maybe even references to other books. When certify-book creates the certificate for a file it recovers from the certification world the commands used to create that world from the initial ACL2 world. Those commands become part of the portcullis for the certified book. In addition, certify-book records in the portcullis the check sums (see check-sum) of all the books included in the certification world.

    Include-book presumes that it is impossible even to read the contents of a certified book unless the portcullis can be ``raised.'' To raise the portcullis we must be able to execute (possibly redundantly, but certainly without error), all of the commands in the portcullis and then verify that the books thus included were identical to those used to build the certification world (up to check sum). This raising of the portcullis must be done delicately since defpkgs are present: we cannot even read a command in the portcullis until we have successfully executed the previous ones, since packages are being defined.

    Clearly, a book is most useful if it is certified in the most elementary extension possible of the initial logic. If, for example, your certification world happens to contain a defpkg for "MY-PKG" and the function foo, then those definitions become part of the portcullis for the book. Every time the book is included, those names will be defined and will have to be either new or redundant (see redundant-events). But if those names were not necessary to the certification of the book, their presence would unnecessarily restrict the utility of the book.

    See keep to continue the guided tour of books.




    acl2-sources/doc/HTML/POSITION-EQ.html0000664002132200015000000000063712222333524016525 0ustar kaufmannacl2 POSITION-EQ.html -- ACL2 Version 6.3

    POSITION-EQ

    See position.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/POSITION-EQUAL.html0000664002132200015000000000064512222333524017066 0ustar kaufmannacl2 POSITION-EQUAL.html -- ACL2 Version 6.3

    POSITION-EQUAL

    See position.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/POSITION.html0000664002132200015000000000463712222333524016226 0ustar kaufmannacl2 POSITION.html -- ACL2 Version 6.3

    POSITION

    position of an item in a string or a list
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (position x seq)
    (position x seq :test 'eql)   ; same as above (eql as equality test)
    (position x seq :test 'eq)    ; same, but eq is equality test
    (position x seq :test 'equal) ; same, but equal is equality test
    

    (Position x seq) is the least index (zero-based) of the element x in the string or list seq, if x is an element of seq. Otherwise (position x seq) is nil. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with items of seq.

    The guard for a call of position depends on the test. In all cases, the second argument must satisfy stringp or true-listp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between position and its variants:

    (position-eq x seq) is equivalent to (position x seq :test 'eq);

    (position-equal x seq) is equivalent to (position x seq :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function position-equal.

    Position is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/POSP.html0000664002132200015000000000161412222333524015533 0ustar kaufmannacl2 POSP.html -- ACL2 Version 6.3

    POSP

    a recognizer for the positive integers
    Major Section:  ACL2-BUILT-INS
    

    (posp x) is logically equivalent to (not (zp x)) (see zp) and also to (and (natp x) (not (equal x 0))). We recommend the community book books/ordinals/natp-posp for reasoning about posp and natp. This book is included by community books books/arithmetic/top and books/arithmetic/top-with-meta.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/POST-INDUCTION-KEY-CHECKPOINTS.html0000664002132200015000000000525412222333515021273 0ustar kaufmannacl2 POST-INDUCTION-KEY-CHECKPOINTS.html -- ACL2 Version 6.3

    POST-INDUCTION-KEY-CHECKPOINTS

    reading post-induction key checkpoints
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Each post-induction key checkpoint is a theorem if and only if the original conjecture was a theorem. The reason is that each subgoal produced by induction concludes with the original formula and simplification preserves equivalence.

    So if you see a post-induction key checkpoint that is not a theorem, stop looking at the checkpoints! Your original conjecture is not a theorem! Fix it.

    If you're convinced all the post-induction conjectures are theorems, ask whether each has the hypotheses you'd need to prove it. If the case analysis feels inappropriate or induction hypotheses seem to be missing, then ACL2 might have done the wrong induction. Find the induction scheme it did by reading the first induction message printed after the conjecture was submitted. If it is wrong, then extend ACL2's induction analysis or tell ACL2 what induction to do, as explained shortly.

    But before you decide the induction hypothesis is missing, look closely for contradictions among the hypotheses of the checkpoint formula. For example, perhaps one of the hypotheses is (MEMBER e x) and another is (NOT (MEMBER e' x')) where e, x, e', and x' are possibly complicated expressions.

    Is it possible that e and e' are equal and x and x' are equal? If so, then the two hypotheses are contradictory and the checkpoint would be proved if you could find rules that would simplify those expressions to their common forms. So look for theorems about those subexpressions.

    Or maybe you can get e and e' to reduce to some common d but but find that x and x' are really different. Then ask whether

    (implies (member d x) (member d x'))
    
    If you could prove that, the key checkpoint would be proved. Of course, you may need other hypotheses from the checkpoint to state your theorems.

    If you have been working your way through the tutorial introduction to the theorem prover, use your browser's Back Button now to introduction-to-key-checkpoints.




    acl2-sources/doc/HTML/PPROGN.html0000664002132200015000000000267612222333524015770 0ustar kaufmannacl2 PPROGN.html -- ACL2 Version 6.3

    PPROGN

    evaluate a sequence of forms that return state
    Major Section:  ACL2-BUILT-INS
    

    Example Form:
    (pprogn
     (cond ((or (equal (car l) #\) (equal (car l) slash-char))
            (princ$ #\ channel state))
           (t state))
     (princ$ (car l) channel state)
     (mv (cdr l) state))
    
    The convention for pprogn usage is to give it a non-empty sequence of forms, each of which (except possibly for the last) returns state (see state) as its only value. The state returned by each but the last is passed on to the next. The value or values of the last form are returned as the value of the pprogn.

    If you are using single-threaded objects you may wish to define an analogue of this function for your own stobj.

    General Form:

    (PPROGN form1
            form2
            ...
            formk
            result-form)
    
    This general form is equivalent, via macro expansion, to:
    (LET ((STATE form1))
         (LET ((STATE form2))
              ...
              (LET ((STATE formk))
                   result-form)))
    




    acl2-sources/doc/HTML/PR.html0000664002132200015000000000371512222333517015301 0ustar kaufmannacl2 PR.html -- ACL2 Version 6.3

    PR

    print the rules stored by the event with the given name
    Major Section:  HISTORY
    

    Examples:
    
    :pr fn ; prints the rules from the definition of fn (including any
           ; :type-prescription rule and :definition rule)
    
    :pr assoc-append ; if assoc-append is a rewrite rule, prints that rule
    

    Also see pr!, which is similar but works at the command level instead of at the event level, and see pl, which displays all rewrite rules for calls of fn, not just those introduced at definition time.

    Pr takes one argument, a logical name, and prints the rules associated with it. In each case it prints the rune, the current enabled/disabled status, and other appropriate fields from the rule. It may be helpful to read the documentation for various kinds of rules in order to understand the information printed by this command. For example, the information printed for a linear rule might be:

      Rune:     (:LINEAR ABC)
      Enabled:  T
      Hyps:     ((CONSP X))
      Concl:    (< (ACL2-COUNT (CAR X)) (ACL2-COUNT X))
      Max-term: (ACL2-COUNT (CAR X))
      Backchain-limit-lst:    (3)
    
    The hyps and concl fields for this rule are fairly self-explanatory, but it is useful to see linear to learn about maximal terms (which, as one might guess, are stored under ``Max-term'').

    Currently, this function does not print congruence rules or equivalence rules.

    The expert user might also wish to use find-rules-of-rune. See find-rules-of-rune.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES-1.html0000664002132200015000000001210312222333515021764 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES-1.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES-1

    rules suggested by (TRUE-LISTP (APPEND (FOO A) (BAR B)))
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of strong rules (see strong-rewrite-rules).

    (TRUE-LISTP (APPEND (FOO A) (BAR B)))
    

    Obviously, you must think about the conditions under which (APPEND x y) returns a true-list. Recall that APPEND concatentates x and y, with y being the terminal sublist. Its definition is equivalent to

    (defun append (x y)
      (if (endp x)
          y
          (cons (car x)
                (append (cdr x) y))))
    
    Technical Note: Append is really a macro that permits you to write calls of append with more than two arguments.

    In a sense, append ``expects'' its arguments to be lists ending in nil, so-called true-listps. (Such expectations are formalized in ACL2 by the notion of the ``guard'' of the function, but we strongly recommend not investigating guards until you're good at using the system.)

    New users frequently start every new theorem by listing all their expectations on the arguments of functions in the problem. For example, if the new user wants to make some statement about when (append x y) is a true-listp, it is not uncommon for him or her first to write:

    (implies (and (true-listp x)
                  (true-listp y))
             ...)
    
    to get ``comfortable.'' Then, thinking about when (append x y) is a true-listp is easy: it always returns a true-listp. It's always a true-listp.'' This thinking produces the theorem:
    (defthm true-listp-append-really-weak
      (implies (and (true-listp x)
                    (true-listp y))
               (true-listp (append x y))))
    
    You'll note we gave it a name suggesting it is ``really weak.''

    One sense in which it is weak is that it has an unnecessary hypothesis. If y is a true-listp, then (append x y) is too, whether x is a true-listp or not. In ACL2, all functions are total. Logically speaking, it doesn't matter whether endp expects its argument to be a true-listp or not, it behaves. (Append x y) either returns y or a cons whose second argument is generated by append. Thus, if y is a true-listp, the answer is too. So here is an improved version of the rule:

    (defthm true-listp-append-weak
      (implies (true-listp y)
               (true-listp (append x y))))
    
    We still think of it as ``weak'' because it has a hypothesis that limits its applicability.

    The strong version of the rule is

    (defthm true-listp-append-strong
      (equal (true-listp (append x y))
             (true-listp y))).
    
    That is, append returns a true-listp precisely when its second argument is a true-listp. We recommend that the strong version be made a :rewrite rule.

    The weak version of the rule allows us to reduce (TRUE-LISTP (APPEND x y)) to true if we can show that (TRUE-LISTP y) is true. But suppose (TRUE-LISTP y) is actually false. Then (TRUE-LISTP (APPEND x y)) would not simplify under the weak version of the rule. But under the strong version it would simplify to NIL.

    Technical Note: The weak version of the rule is a useful :type-prescription rule. The type mechanism cannot currently exploit the strong version of the rule.

    The strategy of ``getting comfortable'' by adding a bunch of hypotheses before you know you need them is not conducive to creating strong rules. We tend to state the main relationship that we intuit about some function and then add the hypotheses we need to make it true. In this case, there were no necessary hypotheses. But if there are, we first identify them and then we ask ``what can I say about the function if these hypotheses aren't true?'' and try to strengthen the statement still further.

    Use your browser's Back Button now to return to practice-formulating-strong-rules.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES-2.html0000664002132200015000000000544712222333515022002 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES-2.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES-2

    rules suggested by (TRUE-LISTP (REV (FOO A)))
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of strong rules (see strong-rewrite-rules).

    (TRUE-LISTP (REV (FOO A)))
    

    The definition of rev in this problem is

    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x)) (list (car x)))))
    

    Since the definition terminates with an endp test and otherwise cdrs the argument, the author of rev was clearly expecting x to be a true-listp. (Indeed, the ``guard'' for rev must require include (true-listp x) since that is endp's guard.) So you're naturally justified in limiting your thoughts about (rev x) to x that are true-lists. This gives rise to the theorem:

    (defthm true-listp-rev-weak
      (implies (true-listp x)
               (true-listp (rev x))))
    
    This is the kind of thinking illustrated in the earlier append example (see practice-formulating-strong-rules-1) and, to paraphrase Z in Men in Black, it exemplifies ``everything we've come to expect from years of training with typed languages.''

    But logically speaking, the definition of rev does not require x to be a true-listp. It can be any object at all: ACL2 functions are total. Rev either returns nil or the result of appending a singleton list onto the right end of its recursive result. That append always returns a true-listp since the singleton list is a true list. (See practice-formulating-strong-rules-1.)

    So this is a theorem and a very useful :rewrite rule:

    (defthm true-listp-rev-strong
      (true-listp (rev x))).
    

    Use your browser's Back Button now to return to practice-formulating-strong-rules.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES-3.html0000664002132200015000000001300512222333515021770 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES-3.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES-3

    rules suggested by (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of strong rules (see strong-rewrite-rules).

    (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))
    

    Since (append x y) contains all the members of x and all the members of y, e is a member of (append x y) precisely when e is a member of x or of y. So a strong statement of this is:

    (defthm member-append-strong-false
      (equal (member e (append x y))
             (or (member e x)
                 (member e y))))
    

    However, this is not a theorem because member is not Boolean. (Member e x), for example, returns the first tail of x that starts with e, or else nil. To see an example of this formula that evaluates to nil, let

    e = 3
    x = '(1 2 3)
    y = '(4 5 6).
    
    Then the left-hand side, (member e (append x y)) evaluates to (3 4 5 6) while the right-hand side evaluates to (3).

    However, the two sides are propositionally equivalent (both either nil or non-nil together). So this is a useful :rewrite rule:

    (defthm member-append-strong
      (iff (member e (append x y))
           (or (member e x)
               (member e y)))).
    
    It tells the system that whenever it encounters an instance of (MEMBER e (APPEND x y)) in a propositional occurrence (where only its truthvalue is relevant), it should be replaced by this disjunction of (MEMBER e x) and (MEMBER e y).

    The following two formulas are true but provide much weaker rules and we would not add them:

    (implies (member e x) (member e (append x y)))
    
    (implies (member e y) (member e (append x y)))
    
    because they each cause the system to backchain upon seeing (MEMBER e (APPEND x y)) expressions and will not apply unless one of the two side-conditions can be established.

    There is a rewrite rule that is even stronger than member-append-strong. It is suggested by the counterexample, above, for the EQUAL version of the rule.

    (defthm member-append-really-strong
      (equal (member e (append x y))
             (if (member e x)
                 (append (member e x) y)
                 (member e y))))
    
    While member-append-strong only rewrites member-append expressions occurring propositionally, the -really-strong version rewrites every occurrence.

    However, this rule will be more useful than member-append-strong only if you have occurrences of member in non-propositional places. For example, suppose you encountered a term like:

    (CONS (MEMBER e (APPEND x y)) z).
    
    Then the -strong rule does not apply but the -really-strong rule does.

    Furthermore, the -really-strong rule, by itself, is not quite as good as the -strong rule in propositional settings! For example, if you have proved the -really-strong rule, you'll notice that the system still has to use induction to prove

    (IMPLIES (MEMBER E A)
             (MEMBER E (APPEND B A))).
    
    The -really-strong rule would rewrite it to
    (IMPLIES (MEMBER E A)
             (IF (MEMBER E A)
                 (APPEND (MEMBER E A) B)
                 (MEMBER E B)))
    
    which would further simplify to
    (IMPLIES (MEMBER E A)
             (APPEND (MEMBER E A) B))
    
    What lemma does this suggest? The answer is the rather odd:
    (implies x (append x y))
    
    which rewrites propositional occurrences of (APPEND x y) to T if x is non-nil. This is an inductive fact about append.

    A problem with the -really-strong rule is that it transforms even propositional occurrences of member into mixed propositional and non-propositional occurrences.

    (defthm member-append-really-strong
      (equal (member e (append x y))      ; <-- even if this is a propositional occurrence
             (if (member e x)
                 (append (member e x) y)  ; <-- the member in here is not!
                 (member e y))))
    
    So if you are using the -really-strong lemma in a situation in which all your member expressions are used propositionally, you'll suddenly find yourself confronted with non-propositional uses of member.

    Our advice is not to use the -really-strong version unless your application is inherently using member in a non-propositional way.

    Use your browser's Back Button now to return to practice-formulating-strong-rules.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES-4.html0000664002132200015000000000337412222333515022001 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES-4.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES-4

    rules suggested by (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of strong rules (see strong-rewrite-rules).

    (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))
    

    When is (append x y) a subset of z? When everything in x is in z and everything in y is in z. We would make it a rewrite rule:

    (defthm subsetp-append-1-strong
      (equal (subsetp (append x y) z)
             (and (subsetp x z)
                  (subsetp y z))))
    

    We put the ``-1-'' in the name because there is a comparable theorem for when the append is in the second argument of the subsetp; see practice-formulating-strong-rules-5.

    This strong rule is better than the conditional rule;

    (defthm subsetp-append-1-weak
      (implies (and (subsetp x z)
                    (subsetp y z))
               (subsetp (append x y) z)))
    
    for all the usual reasons.

    Use your browser's Back Button now to return to practice-formulating-strong-rules.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES-5.html0000664002132200015000000000563112222333515022000 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES-5.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES-5

    rules suggested by (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of strong rules (see strong-rewrite-rules).

    (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))
    

    When is x a subset of (append y z)? Clearly it is if x is a subset of y or x is a subset of z. We could write that:

    (defthm subsetp-append-2-weak
      (implies (or (subsetp x y)
                   (subsetp x z))
               (subsetp x (append y z))))
    
    The rule generated from this is: ``if you ever encounter (an instance of) (SUBSETP x (APPEND y z)), backchain to the or above and try to establish it. If you can establish it, replace the target by T.''

    This does not fully characterize the situation though. For example, '(1 2 3 4) is a subset of (append '(1 3) '(2 4)) without being a subset of either argument of the append.

    However, no obvious equivalence comes to mind -- indeed, to express any of the ideas floating around here requires defining and introducing more functions, which is not recommended unless those functions are already in the problem.

    For example, if you defined the concept of ``set-minus'' so that (set-minus x y) consists of those elements of x not in y, then you could prove:

    (defthm subset-append-2-strong-but-messy
      (equal (subsetp x (append y z))
             (and (subsetp (set-minus x z) y)
                  (subsetp (set-minus x y) z))))
    
    But this rewrite rule would ``trade'' append away and introduce set-minus. That might be a good strategy if set-minus were already in the problem. But if it were not, it might not be. We wouldn't recommend this rule unless it were helpful in normalizing the expressions in the problem.

    We recommend sticking with the weak version of the rule,

    (defthm subsetp-append-2-weak
      (implies (or (subsetp x y)
                   (subsetp x z))
               (subsetp x (append y z)))).
    
    This illustrates the fact that sometimes there is no strong version of a rule!

    Use your browser's Back Button now to return to practice-formulating-strong-rules.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES-6.html0000664002132200015000000001120712222333515021775 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES-6.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES-6

    rules suggested by (MEMBER (FOO A) (NATS-BELOW (BAR B)))
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    What rules come to mind when looking at the following subterm of a Key Checkpoint? Think of strong rules (see strong-rewrite-rules).

    (MEMBER (FOO A) (NATS-BELOW (BAR B)))
    
    The definition of NATS-BELOW is
    (defun nats-below (j)
      (if (zp j)
          '(0)
          (cons j (nats-below (- j 1)))))
    
    Thus, (nats-below 7) is (7 6 5 4 3 2 1 0). So when is k a member of (nats-below j)?

    The weakest version is

    (defthm member-nats-below-weak
      (implies (and (natp k)
                    (natp j)
                    (<= k j))
               (member k (nats-below j))))
    
    But clearly we could improve this to:
    (defthm member-nats-below-weak-better
      (implies (and (natp k)
                    (natp j))
               (iff (member k (nats-below j))
                    (<= k j))))
    
    or even
    (defthm member-nats-below-weak-better
      (implies (natp j)
               (iff (member k (nats-below j))
                    (and (natp k)
                         (<= k j)))))
    
    Clearly though, we'd like to get rid of the (natp j) hypothesis and the neatest plausible version is:
    (defthm member-nats-below-weak-neatest
      (iff (member k (nats-below j))
           (and (natp j)
                (natp k)
                (<= k j))))
    
    
    But it is not a theorem! For example, if j is -1 and k is 0, then the left-hand side above returns t, because (nats-below j) is (0), but the right-hand side is nil.

    But this suggests a strategy for dealing with necessary hypotheses, like (natp j). We can move them into an IF on the right-hand side! Something like this might be a useful rewrite rule:

    (iff (member k (nats-below j))
         (if (natp j)
             (and (natp k)
                  (<= k j))
             ...)).
    
    We know, from member-nats-below-weak-better, that if (natp j) is true, the member is equivalent to (and (natp k) (<= k j)). So now consider what we know if (natp j) is false. If we can think of some term it's equivalent to nd that term is simpler than the member expression, we have a strong rule.

    But by inspection of the definition of nats-below, we see that when (natp j) is false, (nats-below j) is the list (0) because (zp j) is t. That is, nats-below treats all non-natural arguments like they were 0. Thus, when (natp j) is false, (member k (nats-below j)) is (member k '(0)), which is (equal k 0).

    So the strong version is

    (defthm member-nats-below-strong
       (iff (member k (nats-below j))
            (if (natp j)
                (and (natp k)
                     (<= k j))
                (equal k 0))))
    
    This is a great :rewrite rule. It gets rid of the member and nats-below and introduces arithmetic.

    This example illustrates the idea of putting an if on the right-hand-side of the equivalence. Many users tend to limit themselves to propositional forms inside iff or to simple expressions inside of equal. But it is quite natural to use if to express what the answer is: if j is a natural, then k is in (nats-below j) precisely if k is a natural less than or equal to j; if j is not a natural, then k is in (nats-below j) precisely if k is 0.

    Use if to lay out the cases you must consider, if you can think of a simpler, equivalent expression for every possible case.

    Use your browser's Back Button now to return to practice-formulating-strong-rules.




    acl2-sources/doc/HTML/PRACTICE-FORMULATING-STRONG-RULES.html0000664002132200015000000000505112222333515021632 0ustar kaufmannacl2 PRACTICE-FORMULATING-STRONG-RULES.html -- ACL2 Version 6.3

    PRACTICE-FORMULATING-STRONG-RULES

    a few simple checkpoints suggesting strong rules
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Consider these definitions:

    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x)) (list (car x)))))
    
    (defun nats-below (j)
      (if (zp j)
          '(0)
          (cons j (nats-below (- j 1)))))
    
    We assume you are familiar with such ACL2 built-ins as append, member, subsetp and true-listp. When we use throw-away names like FOO, BAR, and MUM below we mean to suggest some arbitrary function you shouldn't think about! We're just trying to train your eye to ignore irrelevant things.

    Below are some terms that should suggest rewrite rules to you. Imagine that each of these terms occurs in some Key Checkpoint. What rules come to mind? Try to think of the strongest rules you can.

    Term 1:
    (TRUE-LISTP (APPEND (FOO A) (BAR B)))

    Answers: See practice-formulating-strong-rules-1

    Term 2:
    (TRUE-LISTP (REV (FOO A)))

    Answers: See practice-formulating-strong-rules-2

    Term 3:
    (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))

    Answers: See practice-formulating-strong-rules-3

    Term 4:
    (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))

    Answers: See practice-formulating-strong-rules-4

    Term 5:
    (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))

    Answers: See practice-formulating-strong-rules-5

    Term 6:
    (MEMBER (FOO A) (NATS-BELOW (BAR B)))

    Answers: See practice-formulating-strong-rules-6

    We recommend doing all of these little exercises. When you're finished, use your browser's Back Button to return to strong-rewrite-rules.




    acl2-sources/doc/HTML/PRINC$.html0000664002132200015000000000453012222333524015671 0ustar kaufmannacl2 PRINC$.html -- ACL2 Version 6.3

    PRINC$

    print an atom
    Major Section:  ACL2-BUILT-INS
    

    Use princ$ to do basic printing of atoms (i.e., other than cons pairs). In particular, princ$ prints a string without the surrounding double-quotes and without escaping double-quote characters within the string. Note that princ$ is sensitive to the print-base, print-radix, and print-case; see set-print-base, see set-print-radix, and see set-print-case. Princ$ returns state.

    Examples:
    ACL2 !>(princ$ "Howdy ho" (standard-co state) state)
    Howdy ho<state>
    ACL2 !>(pprogn (princ$ "Howdy ho" (standard-co state) state)
                   (newline (standard-co state) state))
    Howdy ho
    <state>
    ACL2 !>(princ$ "ab\"cd" *standard-co* state)
    ab"cd<state>
    ACL2 !>
    ACL2 !>(princ$ 17 *standard-co* state)
    17<state>
    ACL2 !>(set-print-base 16 state)
    <state>
    ACL2 !>(princ$ 17 *standard-co* state)
    11<state>
    ACL2 !>(set-print-radix t state)
    <state>
    ACL2 !>(princ$ 17 *standard-co* state)
    #x11<state>
    ACL2 !>(princ$ 'xyz *standard-co* state)
    XYZ<state>
    ACL2 !>(set-print-case :downcase state)
    <state>
    ACL2 !>(princ$ 'xyz *standard-co* state)
    xyz<state>
    ACL2 !>
    

    The guard for (princ$ x channel state) is essentially as follows; see io for an explanation of guards of certain built-in functions that take state, such as princ$.

    (and (or (acl2-numberp x)
             (characterp x)
             (stringp x)
             (symbolp x))
         (state-p1 state-state)
         (symbolp channel)
         (open-output-channel-p1 channel :character state-state))
    

    See fmt for more sophisticated printing routines, and see IO for general information about input and output.




    acl2-sources/doc/HTML/PRINT-CONTROL.html0000664002132200015000000001520712222333520016763 0ustar kaufmannacl2 PRINT-CONTROL.html -- ACL2 Version 6.3

    PRINT-CONTROL

    advanced controls of ACL2 printing
    Major Section:  IO
    

    See IO for a summary of printing in ACL2. Here we document some advanced ways to control what is printed by ACL2's primary printing routines.

    See set-print-base, see set-print-radix, and see set-print-case for discussions of the most common ways to control what is printed. Indeed, these are the only ways to control the behavior of princ$ and prin1$.

    The rest of this topic is for advanced users of ACL2. We refer to Common Lisp behavior, as described in any good Common Lisp documentation.

    Print-control variables. Set-print-base, set-print-radix, and set-print-case assign to corresponding so-called ``state global variables'' 'print-base, 'print-radix, and 'print-case, which can be accessed using the expressions (@ print-base), (@ print-radix), and (@ print-case), respectively; see assign. Here is a table showing all print-control variables, their setters, and their defaults.

    print-base          set-print-base          10
    print-case          set-print-case          :upcase
    print-circle        set-print-circle        nil
      [but see remark on print-circle-files, below]
    print-escape        set-print-escape        t
    print-length        set-print-length        nil
    print-level         set-print-level         nil
    print-lines         set-print-lines         nil
    print-pretty        set-print-pretty        nil
    print-radix         set-print-radix         nil
    print-readably      set-print-readably      nil
    print-right-margin  set-print-right-margin  nil
    

    Each ACL2 print-control variable print-xxx can correspond in function to Common Lisp variable *PRINT-XXX*. Specifically, the evaluation of forms (set-print-base t), (set-print-radix t), and (set-print-case t) affects ACL2 printing functions in much the same way that setting to t Common Lisp variables *PRINT-BASE*, *PRINT-RADIX*, and *PRINT-CASE*, respectively, affects Common Lisp printing. The same is true for print-escape, except that this does not affect princ$ or prin1$, which correspond to Common Lisp functions princ and prin1: princ treats *PRINT-ESCAPE* as nil while prin1 treats *PRINT-ESCAPE* as t. Moreover, all print-control variables not mentioned in this paragraph are set to their defaults in princ$ and prin1$, as indicated by ACL2 constant *print-control-defaults*, except that print-readably is set to nil in princ$.

    Fmt and its related functions are sensitive to state globals 'print-base, 'print-radix, 'print-case, 'print-escape, and 'print-readably, in analogy with Common Lisp functions that don't fix *PRINT-ESCAPE* or *PRINT-READABLY*. But the fmt functions do not respect settings of other print-control variables; for example, they act as though 'print-circle is nil. Since ACL2 output is produced using the same underlying print routines as the fmt functions, it also is insensitive to all print-control variables except for the five above. To control the print-level and print-length used for producing ACL2 output, see set-evisc-tuple.

    Print-object$ is sensitive to all of the print-control variables.

    Remark on print-circle-files: ACL2 typically binds 'print-circle to t before writing certificate files, or auxiliary files that are compiled when make-event forms are present in a book, or files in support of :comp commands. This binding allows for structure sharing that can keep these files from growing large. However, this behavior is defeated in GCL (Gnu Common Lisp), because of the small number of indices n available by default (1024) for the #n= reader macro. For the files described above, what actually happens is that 'print-circle is bound to the value of 'print-circle-files, which by default is t unless the underlying Lisp is GCL, in which case it is set to nil. See assign for how to set state globals such as 'print-circle-files. For example, if you build GCL with a larger number of #n= indices available, you may wish to restore the print-circle behavior for certificate files by following these instructions from Camm Maguire:

    This can trivially be revised to any larger constant by editing the following line of read.d and recompiling:

    #ifndef SHARP_EQ_CONTEXT_SIZE
    #define SHARP_EQ_CONTEXT_SIZE 500
    #endif

    End of Remark.

    Evaluate (reset-print-control) to restore all print-control variables to their original settings, as stored in constant *print-control-defaults*.

    (Remark for those using ACL2 built on Gnu Common Lisp (GCL) versions that are non-ANSI, which as of October 2008 is all GCL versions recommended for ACL2: Note that Common Lisp variables *PRINT-LINES*, *PRINT-MISER-WIDTH*, *PRINT-READABLY*, *PRINT-PPRINT-DISPATCH*, and *PRINT-RIGHT-MARGIN* do not have any effect for such GCL versions.)




    acl2-sources/doc/HTML/PRINT-DOC-START-COLUMN.html0000664002132200015000000000264112222333521020135 0ustar kaufmannacl2 PRINT-DOC-START-COLUMN.html -- ACL2 Version 6.3

    PRINT-DOC-START-COLUMN

    printing the one-liner
    Major Section:  MISCELLANEOUS
    

    Examples:
    (assign print-doc-start-column nil)
    (assign print-doc-start-column 17)
    

    This state global variable controls the column in which the ``one-liner'' of a formatted documentation string is printed. Generally, when :doc is used to print a documentation string, the name of the documented concept is printed and then :doc tabs over to print-doc-start-column and prints the one-liner. If the name extends past the desired column, :doc outputs a carriage return and then tabs over to the column. If print-doc-start-column is nil, :doc just starts the one-liner two spaces from the end of the name, on the same line. The initial value of print-doc-start-column is 15.




    acl2-sources/doc/HTML/PRINT-GV.html0000664002132200015000000001267412222333522016166 0ustar kaufmannacl2 PRINT-GV.html -- ACL2 Version 6.3

    PRINT-GV

    print a form whose evaluation caused a guard violation
    Major Section:  OTHER
    

    By default, ACL2 checks input constraints on functions, known as guards. When guards are violated, an informative message is printed; but sometimes it is useful to investigate why the guard check fails. The utility print-gv is provided for that purpose. (Alternatively, you may prefer to avoid guards entirely with (set-guard-checking :none); see set-guard-checking.)

    Example Forms:
    (print-gv)
    (print-gv :evisc-tuple (evisc-tuple 4 4 nil nil))
    (print-gv :evisc-tuple (evisc-tuple 4 ; print-level
                                        5 ; print-length
                                        (world-evisceration-alist state nil)
                                        nil ; hiding-cars
                                        ))
    

    General Form:
    (print-gv :evisc-tuple x)
    
    where the :evisc-tuple argument is optional and defaults to one that hides only the ACL2 logical world, by printing <world> instead of a very large structure. See evisc-tuple for a discussion of evisc-tuples.

    To see how one might use print-gv, consider the following definition.

    (defun foo (x)
      (declare (xargs :guard (and (my-first-predicate x)
                                  (my-second-predicate x))))
      (cdr x))
    
    Now suppose we evaluate a call of foo and obtain a guard violation.
    ACL2 !>(foo 3)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (FOO X),
    which is (AND (MY-FIRST-PREDICATE X) (MY-SECOND-PREDICATE X)), is violated
    by the arguments in the call (FOO 3).  To debug: See :DOC print-gv,
    see :DOC trace, and see :DOC wet.  See :DOC set-guard-checking for
    information about suppressing this check with (set-guard-checking :none),
    as recommended for new users.
    
    ACL2 !>
    
    We can obtain (in essence) the guard form that was false, as follows.
    ACL2 !>(print-gv)
    
    (LET ((X '3))
         (DECLARE (IGNORABLE X))
         (AND (MY-FIRST-PREDICATE X)
              (MY-SECOND-PREDICATE X)))
    
    ACL2 !>
    
    But which conjunct is to blame? With a bit of editing we can try each conjunct in turn.
    ACL2 !>(LET ((X '3))
                (DECLARE (IGNORABLE X))
                (MY-FIRST-PREDICATE X))
    T
    ACL2 !>(LET ((X '3))
                (DECLARE (IGNORABLE X))
                (MY-SECOND-PREDICATE X))
    NIL
    ACL2 !>
    
    Aha! Now we can investigate why the second predicate fails for 3.

    The following hack will give you access in raw Lisp to the form printed by (print-gv). After a guard violation, just submit this form to raw Lisp:

    (print-gv1 (wormhole-data (cdr (assoc 'ev-fncall-guard-er-wormhole
                                          *wormhole-status-alist*)))
               state)
    

    If you use local stobjs (see with-local-stobj) or stobj fields of stobjs, you may need to edit the output of print-gv in order to evaluate it. Consider the following example.

    (defstobj st fld)
    
    (defun g (x st)
      (declare (xargs :guard (consp x) :stobjs st)
               (ignore x))
      (fld st))
    
    (defun test ()
      (with-local-stobj
       st
       (mv-let (result st)
               (mv (g 3 st) st)
               result)))
    
    (test)
    
    Then :print-gv yields the result shown below.
    (FLET ((G{GUARD} (X ST)
                     (DECLARE (IGNORABLE X ST))
                     (AND (STP ST) (CONSP X))))
          (G{GUARD} '3 |<some-stobj>|))
    
    In this example you could replace ``|<some-stobj>|'' by ``st'' to obtain a result of nil. But similar cases may require the use of a local stobj that is no longer available, in which case you may need to be creative in order to take advantage of :print-gv. Here is such an example.
    (defstobj st2 fld2)
    
    (defun g2 (st2)
      (declare (xargs :guard (null (fld2 st2)) :stobjs st2))
      (mv 0 st2))
    
    (defun test2 ()
      (with-local-stobj
       st2
       (mv-let (result st2)
               (let ((st2 (update-fld2 17 st2)))
                 (g2 st2))
               result)))
    
    (test2)
    
    In this case, :print-gv yields the following.
    (FLET ((G2{GUARD} (ST2)
                      (DECLARE (IGNORABLE ST2))
                      (AND (ST2P ST2) (NULL (FLD2 ST2)))))
          (G2{GUARD} |<some-stobj>|))
    
    But if you replace ``|<some-stobj>|'' by ``st'', the guard holds; it is only the local stobj, which is no longer available, that produced a guard violation (because its field had been updated to a cons).
    ACL2 !>(FLET ((G2{GUARD} (ST2)
                             (DECLARE (IGNORABLE ST2))
                             (AND (ST2P ST2) (NULL (FLD2 ST2)))))
                 (G2{GUARD} st2))
    T
    ACL2 !>
    

    Finally, we note that while print-gv is a utility for debugging guard violations, in contrast, see guard-debug for a utility to assist in debugging failed proofs arising from guard verification.




    acl2-sources/doc/HTML/PRINT-OBJECT$.html0000664002132200015000000000062712222333524016661 0ustar kaufmannacl2 PRINT-OBJECT$.html -- ACL2 Version 6.3

    PRINT-OBJECT$

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/PRINT-SUMMARY-USER.html0000664002132200015000000000074112222333531017553 0ustar kaufmannacl2 PRINT-SUMMARY-USER.html -- ACL2 Version 6.3

    PRINT-SUMMARY-USER

    See finalize-event-user.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/PRINTING-TO-STRINGS.html0000664002132200015000000000675512222333520017662 0ustar kaufmannacl2 PRINTING-TO-STRINGS.html -- ACL2 Version 6.3

    PRINTING-TO-STRINGS

    printing to strings instead of files or standard output
    Major Section:  IO
    

    Each of ACL2's formatted printing functions, FM*, has an analoguous macro FM*-TO-STRING indicated below. These functions do not include a channel or state as an argument, and FM*-TO-STRING returns the string that FM* would print to the state, in place of state; see fmt. The legal keyword arguments are described below.

    General Forms:                                 result
    (fms-to-string string alist &key ...)          ; string
    (fmt-to-string string alist &key ...)          ; (mv col string)
    (fmt1-to-string string alist column &key ...)  ; (mv col string)
    (fms!-to-string string alist &key ...)         ; string
    (fmt!-to-string string alist &key ...)         ; (mv col string)
    (fmt1!-to-string string alist column &key ...) ; (mv col string)
    

    The legal keyword arguments are as follows. They are all optional with a default of nil.

    Evisc-tuple is evaluated, and corresponds exactly to the evisc-tuple argument of the corresponding FM* function; see fmt.

    Fmt-control-alist should typically evaluate to an alist that maps print-control variables to values; see print-control. Any alist mapping variables to values is legal, however. By default the print controls are set according to the value of constant *fmt-control-defaults*; fmt-control-alist overrides these defaults. For example, *fmt-control-defaults* sets the right margin just as it is set in the initial ACL2 state, by binding fmt-soft-right-margin and fmt-hard-right-margin to their respective defaults of *fmt-soft-right-margin-default* and *fmt-hard-right-margin-default*. The following example shows how you can override those defaults, in this case arranging to print flat by setting the right margin to a large number.

    (fmt-to-string "~x0"
                   (list (cons #\0 (make-list 30)))
                   :fmt-control-alist
                   `((fmt-soft-right-margin . 10000)
                     (fmt-hard-right-margin . 10000)))
    

    Iprint is typically nil, but t is also a legal value. See set-iprint for the effect of value t, which however is local to this call of a FM*-TO-STRING function; the behavior if iprinting afterwards is not affected by this call. In particular, you will not be able to look at the values of iprint tokens printed by this call, so the value t is probably of limited utility at best.

    Also see io for a discussion of the utility get-output-stream-string$, which allows for accumulating the results of more than one printing call into a single string but requires the use of state.




    acl2-sources/doc/HTML/PROFILE.html0000664002132200015000000000540412222333517016055 0ustar kaufmannacl2 PROFILE.html -- ACL2 Version 6.3

    PROFILE

    turn on profiling for one function
    Major Section:  EVENTS
    

    NOTE: An alternative to this utility, which has a lot less functionality but doesn't depend on the experimental extension of ACL2 mentioned just below, may be found in community book books/misc/profiling.lisp.

    This documentation topic relates to an experimental extension of ACL2 under development by Bob Boyer and Warren Hunt. See hons-and-memoization for a general discussion of memoization, on which this profile utility is built, and the related features of hash consing and applicative hash tables.

    Profile can be useful in Common Lisp debugging and performance analysis, including examining the behavior of ACL2 functions.

    Example:
    (profile 'fn)       ; keep count of the calls of fn
    (profile 'fn        ; as above, with with some memoize options
             :trace t
             :forget t)
    (memsum) ; report statistics on calls of memoized functions (e.g., fn)
    (clear-memoize-statistics) ; clear memoization (and profiling) statistics
    

    Evaluation of (profile 'fn) redefines fn so that a count will be kept of the calls of FN. The information recorded may be displayed, for example, by invoking (memoize-summary) or (equivalently) (memsum). If you wish to gather fresh statistics for the evaluation of a top-level form, see clear-memoize-statistics.

    Profile is just a macro that calls memoize to do its work. Profile gives the two keyword parameters :condition and :recursive of memoize the value nil. Other keyword parameters for memoize, which must not include :condition, :condition-fn, or :recursive, are passed through. To eliminate profiling, use unmemoize; for example, to eliminate profiling for function fn, evaluate (unmemoize 'fn).

    You may find raw Lisp functions profile-all and profile-acl2 to be useful. Please contact the ACL2 developers if you want versions of these functions to be executable from inside the ACL2 read-eval-print loop.




    acl2-sources/doc/HTML/PROG2$.html0000664002132200015000000000567612222333524015663 0ustar kaufmannacl2 PROG2$.html -- ACL2 Version 6.3

    PROG2$

    execute two forms and return the value of the second one
    Major Section:  ACL2-BUILT-INS
    

    See hard-error, see illegal, and see cw for examples of functions to call in the first argument of prog2$. Also see progn$ for an extension of prog2$ that handles than two arguments.

    Semantically, (Prog2$ x y) equals y; the value of x is ignored. However, x is first evaluated for side effect. Since the ACL2 programming language is applicative, there can be no logical impact of evaluating x. However, x may involve a call of a function such as hard-error or illegal, which can cause so-called ``hard errors'', or a call of cw to perform output.

    Here is a simple, contrived example using hard-error. The intention is to check at run-time that the input is appropriate before calling function bar.

    (defun foo-a (x)
      (declare (xargs :guard (consp x)))
      (prog2$
       (or (good-car-p (car x))
           (hard-error 'foo-a
                       "Bad value for x: ~p0"
                       (list (cons #\0 x))))
       (bar x)))
    
    The following similar function uses illegal instead of hard-error. Since illegal has a guard of nil, guard verification would guarantee that the call of illegal below will never be made (at least when guard checking is on; see set-guard-checking).
    (defun foo-b (x)
      (declare (xargs :guard (and (consp x) (good-car-p (car x)))))
      (prog2$
       (or (good-car-p (car x))
           (illegal 'foo-b
                    "Bad value for x: ~p0"
                    (list (cons #\0 x))))
       (bar x)))
    

    We conclude with a simple example using cw from the ACL2 sources.

    (defun print-terms (terms iff-flg wrld)
    
    ; Print untranslations of the given terms with respect to iff-flg, following
    ; each with a newline.
    
    ; We use cw instead of the fmt functions because we want to be able to use this
    ; function in print-type-alist-segments (used in brkpt1), which does not return
    ; state.
    
      (if (endp terms)
          terms
        (prog2$
         (cw "~q0" (untranslate (car terms) iff-flg wrld))
         (print-terms (cdr terms) iff-flg wrld))))
    




    acl2-sources/doc/HTML/PROGN$.html0000664002132200015000000000161612222333524015705 0ustar kaufmannacl2 PROGN$.html -- ACL2 Version 6.3

    PROGN$

    execute a sequence of forms and return the value of the last one
    Major Section:  ACL2-BUILT-INS
    

    This macro expands to a corresponding nest of calls of prog2$; see prog2$. The examples below show how this works: the first case below is typical, but we conclude with two special cases.

    ACL2 !>:trans1 (progn$ (f1 x) (f2 x) (f3 x))
     (PROG2$ (F1 X) (PROG2$ (F2 X) (F3 X)))
    ACL2 !>:trans1 (progn$ (f1 x) (f2 x))
     (PROG2$ (F1 X) (F2 X))
    ACL2 !>:trans1 (progn$ (f1 x))
     (F1 X)
    ACL2 !>:trans1 (progn$)
     NIL
    ACL2 !>
    




    acl2-sources/doc/HTML/PROGN.html0000664002132200015000000000431612222333517015643 0ustar kaufmannacl2 PROGN.html -- ACL2 Version 6.3

    PROGN

    evaluate some events
    Major Section:  EVENTS
    

    Example Form:
    (progn (defun foo (x) x)
           (defmacro my-defun (&rest args)
             (cons 'defun args))
           (my-defun bar (x) (foo x)))
    
    General form:
    (progn event1 event2 ... eventk)
    
    where k >= 0 and each eventi is a legal embedded event form (see embedded-event-form). These events are evaluated in sequence. A utility is provided to assist in debugging failures of such execution; see redo-flat.

    NOTE: If the eventi above are not all legal embedded event forms (see embedded-event-form), consider using er-progn or (with great care!) progn! instead.

    For a related event form that does allow introduction of constraints and local events, see encapsulate.

    ACL2 does not allow the use of progn in definitions. Instead, the macro er-progn can be used for sequencing state-oriented operations; see er-progn and see state. If you are using single-threaded objects (see stobj) you may wish to define a version of er-progn that cascades the object through successive changes. ACL2's pprogn is the state analogue of such a macro.

    If your goal is simply to execute a sequence of top-level forms, for example a sequence of definitions, consider using ld instead; see ld.




    acl2-sources/doc/HTML/PROGN_bang_.html0000664002132200015000000001556012222333517016774 0ustar kaufmannacl2 PROGN_bang_.html -- ACL2 Version 6.3

    PROGN!

    evaluate some forms, not necessarily events
    Major Section:  EVENTS
    

    WARNING! This event is intended for advanced users who, in essence, want to build extensions of ACL2. See see defttag, in particular, the ``WARNING'' there, and see the warning about stobjs at the end of this documentation topic.

    Progn! can be used like progn, even in books. But unlike progn, progn! does not require its constituent forms to be events (see embedded-event-form), except that the first form cannot be a symbol unless it is :state-global-bindings (advanced feature, described below). However, see make-event for a ``Restriction to the Top Level'' that still applies under a call of progn!.

    Because progn! allows non-events, it differs from progn in another important respect: progn! is illegal unless there is an active ttag; see defttag.

    See community book books/hacking/hacker.lisp for two macros, with-raw-mode and with-redef-allowed, each defined in terms of progn!, that allow arbitrary forms in contexts that would normally require legal embedded event forms.

    Given a form (progn! form1 form2 ... formk), ACL2 will evaluate each formi in turn (for i from 1 to k). If a form returns more than one value (see mv) where the first value returned is not nil, then no later form will be evaluated and the result returned by the progn! call will be (mv erp val state) for some non-nil value erp, signifying an error (see ld-error-triples). Otherwise the evaluation is considered to have succeeded, and will continue with later forms. The value returned by a call of progn! with no such error is of the form (mv nil v state), where v depends on the last form as follows. If the last form evaluates to a single value, then v is that value, except if the value is a stobj, say ST, then v is the symbol REPLACED-ST. Otherwise the last form evaluates to some (mv nil x ...), and v is x unless after the final form's evaluation we are in raw-mode (see set-raw-mode), in which case the progn! call returns nil (so that ACL2 can at least print the result -- imagine Lisp returning a pathname object from a load call, for example).

    The normal undoing mechanism does not generally apply to forms within a progn! that are not legal ACL2 events (see embedded-event-form). In particular, note that a non-local call of progn! in an encapsulate event will generally be evaluated twice: once on each pass. This fact is worth keeping in mind if you are using progn! to change the state of the system; ask yourself if it is acceptable to apply that state-changing operation more than once.

    Please note that progn! may differ from progn in the following sense: definitions within a call of progn! might not be compiled. For example, consider the following book.

    (in-package "ACL2")
    (defttag :test)
    (progn  (defun f1 (x) x))
    (progn! (defun f2 (x) x))
    
    If the underlying Lisp is GCL 2.6.7, then after including this certified book (where the default certification took place, creating a compiled file), then f1 is a compiled function but f2 is not. For other Lisps supported by ACL2, both f1 and f2 are compiled, though we are not sure that every function under every call of progn! would similarly be compiled.

    We now describe, for system hackers only, a sophisticated extension of progn! not mentioned above: support for keyword argument :state-global-bindings. If the first argument of progn! is this keyword, then the second argument is treated as a list of bindings as expected by ACl2 system function state-global-let*. Thus, in the ACL2 loop,

    (progn! :state-global-bindings bindings form1 form2 ... formk)
    
    is treated as follows:
    (progn! (state-global-let* bindings (progn! form1 form2 ... formk)))
    
    However, in raw Lisp the former is just:
    (progn form1 form2 ... formk)
    
    Thus, one should use the :state-global-bindings argument with care, since the behavior in the ACL2 loop can differ from that in Common Lisp. The intention is that one bind only state global variables that are relevant to evaluation of the forms within the ACL2 loop and are harmlessly ignored for evaluation of those forms in raw Lisp. Here is a typical sort of example, as state global ld-redefinition-action is not relevant to the evaluation of defun in raw Lisp.
    (progn! (remove-untouchable 'ld-redefinition-action nil)
            (progn! :state-global-bindings
                    ((ld-redefinition-action '(:doit . :overwrite)))
                    (defun foo (x)
                      (cons x x)))
            (push-untouchable 'ld-redefinition-action nil))
    

    Finally, we point out a pitfall of progn! related to stobjs. The following book can cause a hard Lisp error, depending on the host Common Lisp, when certified with a non-nil value for compile-flg (see certify-book).

    (in-package "ACL2")
    (defstobj st fld)
    (defttag :my-ttag)
    (progn! (update-fld 3 st))
    
    The problem is that the stobj variable st is not known to raw Lisp. The compilation problem disappears if the last form above is replaced with the following two forms.
    (include-book "hacking/hacker" :dir :system)
    (with-raw-mode (update-fld 3 *the-live-st*))
    




    acl2-sources/doc/HTML/PROGRAM.html0000664002132200015000000000464512222333531016066 0ustar kaufmannacl2 PROGRAM.html -- ACL2 Version 6.3

    PROGRAM

    to set the default defun-mode to :program
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    ACL2 !>:program
    ACL2 p!>
    
    Typing the keyword :program sets the default defun-mode to :program.

    Functions defined in :program mode are logically undefined but can be executed on constants outside of deductive contexts. See defun-mode.

    Calls of the following macros are ignored (skipped) when in :program mode.

    local
    verify-guards
    verify-termination
    defaxiom
    defthm
    deftheory
    in-theory
    in-arithmetic-theory
    regenerate-tau-database
    theory-invariant
    defchoose
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    See defun-mode for a discussion of the defun-modes available and what their effects on the logic are. See default-defun-mode for a discussion of how the default defun-mode is used. This event is equivalent to (table acl2-defaults-table :defun-mode :program), and hence is local to any books and encapsulate events in which it occurs. See acl2-defaults-table.

    Recall that the top-level form :program is equivalent to (program); see keyword-commands. Thus, to change the default defun-mode to :program in a book, use (program), which is an embedded event form, rather than :program, which is not a legal form for books. See embedded-event-form.




    acl2-sources/doc/HTML/PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED.html0000664002132200015000000004244512222333515022426 0ustar kaufmannacl2 PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED.html -- ACL2 Version 6.3

    PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED

    background knowledge in ACL2 programming for theorem prover tutorial
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    This brief review of the programming language is presented as a sequence of questions and answers meant to test your knowledge of the ACL2 programming language. If you want a gentle introduction to the programming language, see http://www.cs.utexas.edu/users/moore/publications/gentle-intro-to-acl2-programming.html.

    Before we get started with the programming drill, let us remind you that all we're interested in here is the language, not the ``program development environment.'' It's impossible to program in ACL2 or any other language without a decent environment, one that at the very least includes a way to prepare and edit files of programs. The two most popular program development environments among ACL2 users are Emacs and the Eclipse-based ACL2-Sedan . The Sedan provides the most support for the new user, including real-time syntax checking and a facility for testing among many other features. But in this drill we're not interested in the program development environment, we're interested in your understanding of the ACL2 language.

    Q: What do you think this command does?

    (defun rev (x)
      (if (endp x)
          nil
          (append (rev (cdr x)) (list (car x)))))
    
    A: It defines a function named rev that takes one argument, treats it like a list, and reverses the order of the elements in that list. To figure this out from the definition, you have to know that append concatenates two lists. Logically speaking, the defun of rev adds the axiom:
    (rev x)
    =
    (if (endp x)
        nil
        (append (rev (cdr x)) (list (car x)))),
    
    implicitly quantified for all x.

    Q: Given the defun of rev above, what are the formal parameters? What is the body of the definition? Write down a call of append that occurs in the body of rev. What are the actuals of that call? A: The formals of rev is the list of variables after the first rev in the defun, namely (x). We say x is the first (and only) formal here. The body of rev is the entire if-expression. The only call of append in the body is

    (append (rev (cdr x)) (list (car x)))
    
    and the actuals of that call are, respectively, (rev (cdr x)) and (list (car x)).

    Q: What do you get if you evaluate (rev '(a b c d))? A: (D C B A).

    Q: How did rev change the case of the elements, e.g., lowercase a was in the input list but uppercase A was in the output? A: This is a trick question. Rev doesn't change the case of the elements. ACL2 is case-insensitive when dealing with symbols. The symbol a is read in as the symbol A. Thus, when writing function names, for example, we can write rev, Rev, REV, or even ReV and always be referring to the function REV. By default, ACL2 prints symbols in uppercase.

    Q: What does (rev '((a b c) "Abc" "a" b #\c)) return? A: (#\c B "a" "Abc" (A B C)). If you thought the answer was any of these, then you need to think or read more carefully:

    (#\C B "A" "ABC" (A B C))
    
    (#\C B "A" "ABC" (C B A))
    
    The first wrong answer above is wrong because Lisp is ``case insensitive'' only for symbols, not for character objects like #\c (the lowercase character c) or for strings. Furthermore, "A" is a string, not a symbol; it is different from A. The second wrong answer above is wrong because rev does not go into the individual elements of the list, it just reverses the order of the elements. So it doesn't change the element (A B C) to (C B A).

    Q: In the question about what (rev '(a b c d)) returns, we put a quote mark before the (a b c d) but not before the answer, (D C B A). Why? A: The phrase ``x evaluates to y'' treats x as a term to be evaluated and y as an object. (Rev '(a b c d)) is a term to be evaluated and denotes a call of the function rev on the value of the argument term '(a b c d). The value of that argument term is the object (a b c d). The value of the call of rev is the object (d c b a). If you have an object, obj, and you wish to create a term whose value is obj, then you put a quote mark in front of it, 'obj.

    Q: Can rev be applied to something other than a list? A: Yes, every ACL2 function can be applied to any object. ACL2 is an untyped programming language: every variable ranges over the entire universe of objects. In normal usage, rev is applied to lists but there is nothing about the syntax of the language that prevents it being applied to non-lists.

    Q: So what does (rev 23) evaluate to? A: Nil.

    Q: Why? A: Because (endp 23) is t, because endp is defined:

    (defun endp (x) (not (consp x)))
    
    Thus, if rev is applied to anything that is not a cons, it returns nil.

    Q: So what does (rev '(a b c . d)) evaluate to? A: (c b a). To explain why requires demonstrating that you know what (a b c . d) means. It is the object computed by evaluating:

    (cons 'a
          (cons 'b
                (cons 'c
                      'd))).
    
    That is, it is a list whose ``terminal marker'' is the atom D. Rev treats that list exactly as it treats the nil-terminated list of the same elements, (a b c), because (endp 'D) = (endp nil) = t.

    Q: What does (rev 1 2 3) evaluate to? A: That's a trick question. Rev takes one argument, not three. So (rev 1 2 3) is an ill-formed term.

    Q: What does (rev '(a b c . d . nil)) evaluate to? A: That is a trick question. There is no such object. In Lisp's ``dot notation'' every dot must be followed by a well-formed object and then a close parenthesis. Usually that ``well-formed object'' is an atom. If it is not an atom, i.e., if it is a cons, then the entire expression could have been written without that dot. For example, (a b c . (d e)) is an object, but it could be written (a b c d e).

    Q: Do (rev (rev x)) and x always evaluate to the same object? A: No. (Rev (rev 23)) evaluates to nil, not 23.

    Q: Do (rev (rev x)) and x always evaluate to the same object when x is a cons? A: No. (rev (rev '(a b c . d))) evaluates to (a b c), not (a b c . d).

    Q: When are (rev (rev x)) and x equal? A: When the terminal marker of x is nil.

    Q: Can you define a Lisp function that recognizes nil-terminated lists? A: Yes, but it is not necessary for the user to define that concept because Common Lisp provides such a function which is logically defined as follows:

    (defun true-listp (x)
      (if (consp x)
          (true-listp (cdr x))
          (equal x nil))).
    
    This can be paraphrased: (true-listp x) means that if x is a cons, its cdr is a true-listp and if x is not a cons, it must be nil. Thus, (true-listp '(a b c)) is t and (true-listp '(a b c . d)) is nil.

    Q: Can you write a Lisp formula that says ``If z is a nil-terminated list then reversing the result of reversing z is z''?

    A: Yes:

    (implies (true-listp z)
             (equal (rev (rev z)) z)).
    

    Q: Is this all there is to ACL2 programming? A: No! ACL2 provides many other features. For a full list of all the primitive functions in ACL2 see programming . Some highlights for the beginner are mentioned below, but all of the links below ought to be tagged with the sign.

    * list: build a nil-terminated list from the values of n terms, e.g., (list x (+ 1 x) (+ 2 x)) returns (3 4 5) if x is 3.

    * list*: build a non-nil terminated list of n objects from the values of n+1 terms, e.g., (list* x (+ 1 x) (+ 2 x) (* -1 x)) returns the list (3 4 5 . -3) if x is 3.

    * and, or, not, implies, iff: The propositional connectives. And and or are allowed to take a varying number of arguments, e.g., (and p q r) is just an abbreviation for (and p (and q r)). In Lisp, and returns nil if any of its arguments evaluates to nil; otherwise it returns the value of the last argument! Thus, (and t t 3) returns 3! If you object to the idea that and is not Boolean, don't give it non-Boolean arguments! Similarly, or returns the value of the first argument that evaluates to non-nil, or nil if they all evaluate to nil. Both and and or can be thought of as ``lazy'' in that they don't always have to evaluate all their arguments. This is really accomplished by treating and and or as abbrevations for if nests.

    * +, *, -, /, floor, mod, <, <=, >=, >: the Lisp elementary arithmetic operators. Both + and * allow varying numbers of arguments. All the arithmetic operators default non-numeric arguments to 0. If you don't like the idea that (+ 1 2 t) is 3, don't ask + to add t to something!

    * natp, integerp, rationalp, characterp, stringp, symbolp, consp: the recognizers for the primitive data types. The first three recognize subsets of the ACL2 numeric universe. The naturals are a subset of the integers, the integers are a subset of the rationals, and the rationals are a subset of the objects recognized by acl2-numberp, which also includes the complex-rationalps. The other recognizers listed above recognize characters, strings, symbols, and conses.

    * cond: a convenient way to write a cascading nest of ifs, e.g.,

    (cond ((not (natp x)) 'non-natural)
          ((equal x 0) 'zero)
          ((evenp x) 'positive-even)
          (t 'positive-odd))
    
    abbreviates
    (if (not (natp x))
        'non-natural
        (if (equal x 0)
            'zero
            (if (evenp x)
                'positive-even
                'positive-odd))).
    

    * case: a convenient way to case split on the identity of an object.

    (case key
      (non-natural -1)
      (zero 0)
      ((positive-even positive-odd) 'positive-natural)
      (otherwise 'unknown))
    
    abbreviates
    (cond ((eql key 'non-natural) -1)
          ((eql key 'zero) 0)
          ((member key '(positive-even positive-odd))
           'positive-natural)
          (t 'unknown)).
    

    * user defined macros: using defmacro you can introduce your own abbreviations. We recommend you not do this until you're good at list processing since macros are functions that build objects representing terms.

    * mutual-recursion: allows you to define mutually-recursive functions.

    * mv and mv-let: allow functions to return ``multiple-values''. In Lisp, such functions return vectors of values, the vectors are represented as lists of values, but the implementations are generally more efficient. For example, (mv x y z) returns a ``vector'' consisting of the values of x, y, and z.

    (mv-let (a b c)
            (foo x)
            (bar a b c x))
    
    evaluates (foo x), treats the result as a vector of three values, binds the variables a, b, and c to those three values, and evaluates and returns (bar a b c x).

    ACL2 also provides many other features, such as single-threaded objects which may be ``destructively modified'' (see stobj , including a very special single-threaded object that records the state of the ACL2 system), file input and output (see io ), applicative arrays (see arrays ) and property lists (see getprop ) and other facilities necessary for it to be a practical programming language. However, we strongly recommend that as a new user you stay away from these features until you are good at proving theorems about elementary list processing!

    If this little drill made sense to you, you know enough of the programming language to get started. Use your browser's Back Button now to return to introduction-to-the-theorem-prover.

    If you are uncomfortable with ACL2 programming, we recommend that you study http://www.cs.utexas.edu/users/moore/publications/gentle-intro-to-acl2-programming.html and http://www.cs.utexas.edu/users/moore/publications/acl2-programming-exercises1.html.

    However, we strongly recommend that you first invest in learning either the Emacs or Eclipse-based ACL2-Sedan program development environments, since it is foolish to try to learn how to program in a stand-alone read-eval-print loop!

    While getting started, many users find the Hyper-Card a handy index into the documentation for the ACL2 language:

    http://www.cs.utexas.edu/users/moore/publications/hyper-card.html

    Once you are comfortable with the ACL2 programming language, use your browser's Back Button to return to introduction-to-the-theorem-prover.




    acl2-sources/doc/HTML/PROGRAMMING-WITH-STATE.html0000664002132200015000000006451012222333525020170 0ustar kaufmannacl2 PROGRAMMING-WITH-STATE.html -- ACL2 Version 6.3

    PROGRAMMING-WITH-STATE

    programming using the von Neumannesque ACL2 state object
    Major Section:  STATE
    

    This documentation section introduces some common techniques for programming using the ACL2 state object. A prerequisite is thus a basic understanding of that object; see state. We hope this section is useful, and we invite suggestions for improvements and additions.

    A supplement to this section is the ACL2 source code, which uses most (and probably all) of the techniques discussed here. That code is thus a source of many examples, which can serve as ``templates'' to guide one's own programming with state.

    Recall that ``ACL2'' stands for ``A Computational Logic for Applicative Common Lisp''. In particular, the language is applicative: there are no global variables or side effects. For many purposes this does not feel restrictive; for example, an ACL2 user who is programming in raw Lisp may well be more comfortable coding a factorial function applicatively, using recursion, rather than using iteration with repeated assignment to the same variable.

    However, there are situations that call for reading or modifying the system state, such as performing input and output, signalling errors, saving information from one computation for use in a later one, or reading and updating system-level or environmental data. This section provides an introductory guide for writing functions that traffic in state. We emphasize that this guide is intended as an introduction; more complete documentation may often be found by following links to documentation of individual utilities, and again, more examples may be found by searching the ACL2 source code for uses of the functions and macros mentioned below. The rest of this section is organized as follows.

    ENABLING PROGRAMMING WITH STATE
    STATE GLOBALS AND THE ACL2 LOGICAL WORLD
    A REMARK ON GUARDS
    ERRORS AND ERROR TRIPLES
    SEQUENTIAL PROGRAMMING
    BINDING VARIABLES USING ERROR TRIPLES
    BINDING STATE GLOBAL VARIABLES
    INPUT AND OUTPUT
    TIMINGS
    ENVIRONMENT AND SYSTEM
    REMARKS ON EVENTS AND LD
    ADVANCED TOPICS
    

    ENABLING PROGRAMMING WITH STATE

    In order to submit a definition that takes state as a formal parameter, you must either declare state as a :stobj (see xargs) or first evaluate the following form at the top level: (set-state-ok t).

    Consider for example the following trivial definition.

    (defun foo (state)
      (mv 3 state))
    
    If you submit the above definition in a fresh ACL2 session, you will get this error message.
      ACL2 Error in ( DEFUN FOO ...):  The variable symbol STATE should not
      be used as a formal parameter of a defined function unless you are
      aware of its unusual status and the restrictions enforced on its use.
      See :DOC set-state-ok.
    
    If first you evaluate (set-state-ok t), you can admit the above definition. Alternatively, you can declare state as a :stobj, as follows.
    (defun foo (state)
      (declare (xargs :stobjs state))
      (mv 3 state))
    
    A difference in the two approaches is that for the latter, a guard proof obligation is generated by default. See the section below entitled ``A remark on guards''.

    STATE GLOBALS AND THE ACL2 LOGICAL WORLD

    Recall (see state) that one of the fields of the ACL2 state object is the global-table, which logically is an alist associating symbols, known as ``state globals'' or ``state global variables'', with values. But no such alist actually exists in the implementation. Instead, ACL2 provides utilities for reading state globals -- see @ and see f-get-global -- and utilities for writing them -- see assign and see f-put-global. The following log shows how they work; further explanation follows below.

    ACL2 !>(assign my-var (+ 3 4))
     7
    ACL2 !>(@ my-var)
    7
    ACL2 !>(f-put-global 'my-var (+ 1 5) state)
    <state>
    ACL2 !>(f-get-global 'my-var state)
    6
    ACL2 !>
    
    Note that the first result is indented by one space. This is ACL2's way to indicate that the assign expression returned an ``error triple'' and that no error was signalled. We discuss error triples in more detail below; also see error-triples.

    As illustrated above, the output signatures of the utilities for assigning to state globals differ from each other as follows: f-put-global returns state, but assign returns an error triple (mv nil val state) where val is the value assigned to the state global. The output signatures of the utilities for reading, @ and f-get-global, are identical. In fact, the form (f-get-global 'my-var state) is the single-step macroexpansion of the form (@ my-var), as can be confirmed using trans1.

    ACL2 !>:trans1 (@ my-var)
     (F-GET-GLOBAL 'MY-VAR STATE)
    ACL2 !>
    

    State globals are useful for conveying persistent state information. Consider for example the utility set-inhibit-output-lst. The form (set-inhibit-output-lst '(prove proof-tree)) is approximately equivalent to (assign inhibit-output-lst '(prove proof-tree)). We say ``approximately'' because set-inhibit-output-lst additionally does some error checking to insure that all the tokens in the new list are legal. When deciding whether to print output, the ACL2 system reads the value of state global variable inhibit-output-lst.

    A particularly useful state global is current-acl2-world, whose value is the ACL2 logical world. Because the ACL2 world is commonly accessed in applications that use the ACL2 state, ACL2 provides a function that returns the world: (w state) = (f-get-global 'current-acl2-world state). While it is common to read the world, only functions set-w and set-w! are available to write the world, but these are untouchable and these should generally be avoided except by system implementors (pl[remove-untouchable]).

    A REMARK ON GUARDS

    For a function definition (see defun), if state is specified as a stobj as with the form (declare (xargs :stobjs state)), then the guard for that function is considered to include the condition (state-p state). By default, guard verification will then be performed.

    We can illustrate this point by modifying the example above as follows, to read the value of state global gag-mode.

    (defun foo (state)
      (declare (xargs :stobjs state))
      (f-get-global 'gag-mode state))
    
    If you try this in a fresh ACL2 session, the proof will fail with the following key checkpoint, which says that the state global gag-mode is bound in the global-table of the state.
      (IMPLIES (STATE-P1 STATE)
               (ASSOC-EQUAL 'GAG-MODE (NTH 2 STATE)))
    

    How can we deal with this proof failure? One way is simply to ignore the issue by defining the function in :program mode, as follows.

    (defun foo (state)
      (declare (xargs :stobjs state
                      :mode :program))
      (f-get-global 'gag-mode state))
    
    Perhaps a better way is to strengthen the guard to assert that the indicated state global is bound, as follows.
    (defun foo (state)
      (declare (xargs :guard (boundp-global 'gag-mode state)
                      :stobjs state))
      (f-get-global 'gag-mode state))
    
    Also see guard-miscellany for a discussion of how guards are generated from xargs fields of declare forms, specifically, for keywords :guard and :stobjs.

    ERRORS AND ERROR TRIPLES

    When evaluation returns three values, where the first two are ordinary objects and the third is the ACL2 state, the result may be called an ``error triple''. (Whether it is treated as an error triple depends on the programmer.) Error triples are often denoted (mv erp val state), and common ACL2 programming idioms treat erp as a flag indicating whether an error is being signalled and val as the ``value'' computed. Also see error-triples.

    Even ACL2 users who are not programmers encounter error triples, because these are the values returned by evaluation of ACL2 events. Consider the following log, where the only user input is the defun form following the prompt.

    ACL2 !>(defun foo (x) x)
    
    Since FOO is non-recursive, its admission is trivial.  We observe that
    the type of FOO is described by the theorem (EQUAL (FOO X) X).
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FOO
    ACL2 !>
    
    All output above results from explicit calls of output functions, except for the next-to-last line, which contains FOO. Notice the single-space indentation preceding FOO. That space indicates that in fact, the value returned by evaluation of the defun form is the error triple whose error flag is nil and whose computed value is FOO. By default, ACL2 prints any error triple (mv nil val state) by inserting a space before printing val. You can change the default by setting state global ld-post-eval-print to t; notice how the same result is printed below.
    ACL2 !>:u
              0:x(EXIT-BOOT-STRAP-MODE)
    ACL2 !>(set-ld-post-eval-print t state)
    (NIL T <state>)
    ACL2 !>(defun foo (x) x)
    
    Since FOO is non-recursive, its admission is trivial.  We observe that
    the type of FOO is described by the theorem (EQUAL (FOO X) X).
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    (NIL FOO <state>)
    ACL2 !>
    

    The way error triples are printed by ld is controlled not only by state global ld-post-eval-print, but also by state global ld-error-triples. These are examples of ``ld specials''; see ld, see ld-post-eval-print, and see ld-error-triples.

    It is so common to produce an error triple whose first (error flag) component is nil that ACL2 provides a handy macro, value, for this purpose. Thus, (value <expression>) is equivalent to (mv nil <expression> state). Also see value-triple for a similar construct that is a legal event form.

    We turn now to the topic of errors. The macro ER ``causes'' an error, but there are really two quite different kinds of errors: ``soft'' and ``hard'' errors. We use the term ``soft error'' to refer to a form that returns an error triple (mv erp val state) for which erp is non-nil. Soft errors do not interrupt the normal flow of evaluation: the error triple is returned to the caller which interprets the erp flag and val as directed by the programmer. Macros discussed below make it convenient to think about soft errors as short-circuiting the computation. Hard errors, on the other hand, do actually rip control away from the current evaluation and return it to the top-level loop. Logically speaking, expressions that cause hard errors return nil in the error case, but the nil is never seen in actual evaluation because control does not return to the caller.

    Note that the function abort!, which you can invoke by typing :a!, always returns to the top level. Note that ACL2 can prove that (abort!) returns nil but that this cannot be confirmed by computation.

    ACL2 !>(thm (equal (abort!) nil))
    
    Q.E.D.
    
    Summary
    Form:  ( THM ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:TYPE-PRESCRIPTION ABORT!))
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Proof succeeded.
    ACL2 !>(equal (abort!) nil)
    Abort to ACL2 top-level
    ...
    ACL2 !>
    

    (What actually happens with a hard error, including non-default cases, is a bit subtle; most readers will probably want to skip this paragraph. The read-eval-print loop implemented by ld is implemented by a call of the ACL2 evaluator function, trans-eval, on each input form. If a hard error occurs during evaluation of an input form, its trans-eval call will return with a soft error. Ld, in turn handles that soft error appropriately; see ld-error-action.)

    The most common way to signal errors is the macro er, which prints a formatted error message and returns a soft or hard error as specified by the call. Note however that soft errors are signalled using :program mode functions.

    Since the output signatures of soft and hard errors are different -- hard errors ``return'' a single value while soft errors return a triple -- mixing them in an expression requires embedding the hard error form in (an irrelevant) triple, as illustrated below. All branches of the expression must produce an error triple if any branch does.

    ACL2 !>(defun chk-find-or-abort (e x state)
             (declare (xargs :mode :program))
             (if (endp x)
                 (value                          ; Note use of VALUE!
                   (er hard 'chk-find-or-abort
                       "Did not find ~x0!"
                        e))
                 (if (not (integerp (car x)))
                     (er soft 'chk-find-or-abort
                         "Non-integer, ~x0, in list!"
                         (car x))
                     (if (eql (car x) e)
                         (value x)
                         (chk-find-or-abort e (cdr x) state)))))
    
    Summary
    Form:  ( DEFUN CHK-FIND-OR-ABORT ...)
    Rules: NIL
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     CHK-FIND-OR-ABORT
    ACL2 !>(chk-find-or-abort 3 '(1 2 3 4 5) state)
     (3 4 5)
    ACL2 !>(chk-find-or-abort 3 '(1 A 3 4 5) state)
    
    
    ACL2 Error in CHK-FIND-OR-ABORT:  Non-integer, A, in list!
    
    ACL2 !>(chk-find-or-abort 3 '(1 2 4 5) state)
    
    HARD ACL2 ERROR in CHK-FIND-OR-ABORT:  Did not find 3!
    ...
    ACL2 !>
    

    See er for further discussion of errors. For some other individual topics related to errors see assert$, see break-on-error, see error1, see hard-error, see illegal, and see ld-error-triples.

    In the next section we discuss soft errors further, in the context of programming.

    SEQUENTIAL PROGRAMMING

    This section describes handy ways to modify state in steps, using macros that implement a sequence of let or mv-let bindings. For example, suppose you want to assign the values 1 and 2 to two state globals one-var and two-var, respectively. Because of ACL2's syntactic restrictions on state, it is not legal simply to write (f-put-global 'two-var 2 (f-put-global 'one-var 1 state)). However, let comes to the rescue as follows.

    (let ((state (f-put-global 'one-var 1 state)))
      (let ((state (f-put-global 'two-var 2 state)))
        state))
    
    It is so common to bind state successively in such a manner that ACL2 provides a macro, pprogn, for this purpose. Thus, an equivalent solution to the problem above is
    (pprogn (f-put-global 'one-var 1 state)
            (f-put-global 'two-var 2 state)
            state)
    
    or, more simply, as follows.
    (pprogn (f-put-global 'one-var 1 state)
            (f-put-global 'two-var 2 state))
    
    See pprogn. Note that the last form is allowed to return multiple values; the only requirement on the last form is that its value include state.

    It is also common to update the state using a sequence of forms such that each returns an error triple, where the intention is for evaluation to short-circuit immediately if a soft error is encountered. Suppose <expr1> and <expr2> are expressions that return error triples, where the state components of the error triples might be updated, and one wishes to evaluate <expr1> and then <expr2>, returning the (multiple) values returned by <expr2> unless the error triple returned by <expr1> is a soft error, in which case that error triple is returned. One can of course do so as follows.

    (mv-let (erp val state)
            <expr1>
            (cond (erp (mv erp val state))
                  (t <expr2>)))
    
    But ACL2 provides a handy macro, er-progn, for this purpose. The following code is equivalent to the code just above.
    (er-progn <expr1> <expr2>)
    
    See er-progn for more details. Note that unlike pprogn, the return signature for the last expression must be the same as that of the others: an error triple.

    Let's consider how to use pprogn and er-progn together. In the following example f1 and f2 both return state, while each of g1 and g2 returns an error triple. The following code modifies state by executing these in the order f1, g1, f2, and finally g2, returning (mv nil val state) where val is the value component of the error triple returned by g2 -- except we return a soft error if g1 or g2 returns a soft error.

    (pprogn (f1 x state)
            (er-progn (g1 x state)
                      (pprogn (f2 x state)
                              (g2 x state))))
    

    Finally, consider the events progn and progn!. These have similar behavior to that of er-progn. However, progn and progn! may only be used in event contexts, for example at the top level or immediately underneath a call of encapsulate or progn, while er-progn has no such restriction. So when writing code, use er-progn rather than progn or progn!. In particular, the body of a defun must not have any calls of progn (or of progn! either), and the same restriction holds for any code to be executed, such as the body of a make-event form.

    BINDING VARIABLES USING ERROR TRIPLES

    In this section we discuss the macro er-let*, which is a variant of the special form, let*, that is useful when programming with state.

    The macro er-let* is useful when binding variables to the value components of error triples. It is actually quite similar to er-progn, described above, except that er-let* binds variables. First consider the following example.

    (er-let* ((x1 (f1 state))
              (x2 (f2 x1 state)))
      (value (cons x1 x2)))
    
    The code just above is essentially equivalent to writing the following.
    (mv-let (erp x1 state)
            (f1 state)
            (cond (erp (mv erp x1 state))
                  (t (mv-let (erp x2 state)
                             (f2 x1 state)
                             (cond (erp (mv erp x2 state))
                                   (t (value (cons x1 x2))))))))
    

    As suggested by the example above, er-let* has the same syntax as let*, except that declarations are not supported. (But note that ignore declarations are not needed; all variables that are bound are also used, at least in the error case. Consider replacing (cons x1 x2) by nil in the example displayed immediately above, and note that x1 and x2 are still used.) However, unlike let*, er-let* requires that for each binding (var expr), the expression expr must evaluate to an error triple and, moreover, it requires that the second argument (the ``body'') of er-let* must evaluate to an error triple. If one of the variable expressions (e.g., the f1 and f2 calls above) signals an error, its error triple is returned as the value of the er-let*.

    Of course, soft errors can be ``caught'' by using mv-let instead of er-let* and simply ignoring the error flag or, more generally, by returning a non-erroneous error triple even if the error flag was on.

    BINDING STATE GLOBAL VARIABLES

    In this section we introduce a utility, state-global-let*, that is an analogue of let* for state global variables. Consider the following example.

    (state-global-let*
     ((inhibit-output-lst (add-to-set-eq 'summary (@ inhibit-output-lst))))
     (thm (equal x x)))
    
    This form binds state global variable inhibit-output-lst to the result of adding the symbol, summary, to the current value of that state global. Thus (see set-inhibit-output-lst), the usual summary is not printed when evaluating this call of thm.

    See state-global-let* for more complete documentation.

    INPUT AND OUTPUT

    In ACL2, most input and output involves the ACL2 state. See io.

    TIMINGS

    For how to obtain the time elapsed since the start of the ACL2 session, see read-run-time.

    For a utility for saving times into the ACL2 state and for printing those saved times, see the community book misc/save-time.lisp.

    To time an evaluation (though this really isn't about state), see time$.

    ENVIRONMENT AND SYSTEM

    Next, we mention briefly some ways in which ACL2 interacts with its environment using the ACL2 state.

    For how to read and write environment variables, see getenv$ and see setenv$.

    For how to run a command in the host operating system, see sys-call.

    REMARKS ON EVENTS AND LD

    In general, undefined or surprising behavior may occur when using ACL2 events or calling ld in your programs. In some cases ACL2 enforces restrictions against these uses. We strongly discourage using ld in programs, as it has been designed to be called only at the top level of a read-eval-print loop.

    There is also a restriction on contexts in which make-event may be called: it may only be called in a context where an event is expected, such as the top level, in a book, or as an argument of encapsulate or progn. The reason is that ACL2 does very subtle and careful tracking of make-event expansions; and it is only able to do this in event contexts, where it is able to carry out such tracking accurately.

    ADVANCED TOPICS

    ACL2 provides the function trans-eval to evaluate an arbitrary form (after translating it to a term, i.e., into internal form). For more information, we refer to reader to comments in the definition of trans-eval in the ACL2 source code. There are also many examples of its use in the ACL2 sources.

    For a function that provides the true absolute filename, with soft links resolved, see canonical-pathname.

    For a function that returns a check-sum on the characters in a channel, see check-sum.

    To obtain a random number, see random$.

    If you are programming in raw-mode (see set-raw-mode) or in raw Lisp, use the variable *the-live-state* in place of the variable state.

    We invite suggestions for additional advanced topics.




    acl2-sources/doc/HTML/PROGRAMMING.html0000664002132200015000000001040212222333523016526 0ustar kaufmannacl2 PROGRAMMING.html -- ACL2 Version 6.3

    PROGRAMMING

    programming in ACL2
    Major Section:  ACL2 Documentation
    

    This documentation topic is a parent topic under which we include documentation topics for built-in functions, macros, and special forms (see acl2-built-ins) as well as topics for notions important to programming with ACL2. If you don't find what you're looking for, see the Index or see individual topics that may be more directly appropriate; for example, see events for top-level event constructorsr like defun.

    Some Related Topics




    acl2-sources/doc/HTML/PROMPT.html0000664002132200015000000000245512222333521015774 0ustar kaufmannacl2 PROMPT.html -- ACL2 Version 6.3

    PROMPT

    the prompt printed by ld
    Major Section:  MISCELLANEOUS
    

    The prompt printed by ACL2 conveys information about various ``modes.'' See default-print-prompt and see ld-prompt for details.

    The prompt during raw Lisp breaks is, with most Common Lisp implementations, adjusted by ACL2 to include the string "[RAW LISP"], in order to reminder users not to submit ACL2 forms there; see breaks. For Lisps that seem to use the same code for printing prompts at the top-level as in breaks, the top-level prompt is similarly adjusted. For Lisps with the above prompt adjustment, The following forms may be executed in raw Lisp (i.e., after typing :q).

    (install-new-raw-prompt) ; install prompt with [RAW LISP] as described above
    (install-old-raw-prompt) ; revert to original prompt from host Common Lisp
    




    acl2-sources/doc/HTML/PROOF-CHECKER-COMMANDS.html0000664002132200015000000004512012222333525020101 0ustar kaufmannacl2 PROOF-CHECKER-COMMANDS.html -- ACL2 Version 6.3

    PROOF-CHECKER-COMMANDS

    list of commands for the proof-checker
    Major Section:  PROOF-CHECKER
    

    This documentation section contains documentation for individual commands that can be given inside the interactive proof-checker loop that is entered using verify.

    Some Related Topics




    acl2-sources/doc/HTML/PROOF-CHECKER.html0000664002132200015000000001027512222333525016705 0ustar kaufmannacl2 PROOF-CHECKER.html -- ACL2 Version 6.3

    PROOF-CHECKER

    support for low-level interaction
    Major Section:  ACL2 Documentation
    

    Call this up with (verify ...).

    Some Related Topics

    This is an interactive system for checking ACL2 theorems, or at least exploring their proofs. One enters it using the VERIFY command (see verify), and then invokes commands at the resulting prompt to operate on a stack of goals, starting with the single goal that was supplied to VERIFY. The final command (or ``instruction'') can be an exit command, which can print out a defthm event if the goal stack is empty; see proof-checker-commands, in particular the exit command. That resulting defthm event includes an :instructions parameter, which directs replay of the proof-checker commands (for example during certification of a book containing that event; see books).

    If you exit the proof-checker interactive loop, you may re-enter that session at the same point using the command (verify), i.e., with no arguments. The commands save and retrieve may be invoked to manage more than one session.

    The proof-checker can be invoked on a specific subgoal, and the resulting :instructions can be given as a hint to the theorem prover for that subgoal. See instructions.

    A tutorial is available on the world-wide web:
    http://www.cs.utexas.edu/users/kaufmann/tutorial/rev3.html.
    The tutorial illustrates more than just the proof-checker. The portion relevant to the proof-checker may be accessed directly:
    http://www.cs.utexas.edu/users/kaufmann/tutorial/rev3.html#slide29

    See set-evisc-tuple for how to arrange that output is printed in abbreviated form. In general, the proof-checker uses the :TERM evisc-tuple described in that documentation.

    Individual proof-checker commands are documented in subsection proof-checker-commands. When inside the interactive loop (i.e., after executing verify), you may use the help command to get a list of legal instructions and (help instr) to get help for the instruction instr.




    acl2-sources/doc/HTML/PROOF-OF-WELL-FOUNDEDNESS.html0000664002132200015000000002052412222333521020453 0ustar kaufmannacl2 PROOF-OF-WELL-FOUNDEDNESS.html -- ACL2 Version 6.3

    PROOF-OF-WELL-FOUNDEDNESS

    a proof that o< is well-founded on o-ps
    Major Section:  MISCELLANEOUS
    

    The soundness of ACL2 rests in part on the well-foundedness of o< on o-ps. This can be taken as obvious if one is willing to grant that those concepts are simply encodings of the standard mathematical notions of the ordinals below epsilon-0 and its natural ordering relation. But it is possible to prove that o< is well-founded on o-ps without having to assert any connection to the ordinals and that is what we do here. The community book books/ordinals/proof-of-well-foundedness carries out the proof outlined below in ACL2, using only that the natural numbers are well-founded.

    Before outlining the above mentioned proof, we note that in the analogous documentation page of ACL2 Version_2.7, there is a proof of the well-foundedness of e0-ord-< on e0-ordinalps, the less-than relation and recognizer for the old ordinals (that is, for the ordinals appearing in ACL2 up through that version). Manolios and Vroon have given a proof in ACL2 Version_2.7 that the current ordinals (based on o< and o-p) are order-isomorphic to the old ordinals (based on e0-ord-< and e0-ordinalp). Their proof establishes that switching from the old ordinals to the current ordinals preserves the soundness of ACL2. For details see their paper:

    Manolios, Panagiotis & Vroon, Daron.
    Ordinal arithmetic in ACL2.
    Kaufmann, Matt, & Moore, J Strother (eds).
    Fourth International Workshop on the ACL2 Theorem
    Prover and Its Applications (ACL2-2003),
    July, 2003.
    See http://www.cs.utexas.edu/users/moore/acl2/workshop-2003/.
    

    We now give an outline of the above mentioned proof of well-foundedness. We first observe three facts about o< on ordinals that have been proved by ACL2 using only structural induction on lists. These theorems can be proved by hand.

    (defthm transitivity-of-o<
      (implies (and (o< x y)
                    (o< y z))
               (o< x z))
      :rule-classes nil)
    
    (defthm non-circularity-of-o<
      (implies (o< x y)
               (not (o< y x)))
      :rule-classes nil)
    
    (defthm trichotomy-of-o<
      (implies (and (o-p x)
                    (o-p y))
               (or (equal x y)
                   (o< x y)
                   (o< y x)))
      :rule-classes nil)
    
    These three properties establish that o< orders the o-ps. To put such a statement in the most standard mathematical nomenclature, we can define the macro:
    (defmacro o<= (x y)
      `(not (o< ,y ,x)))
    
    and then establish that o<= is a relation that is a simple, complete (i.e., total) order on ordinals by the following three lemmas, which have been proved:
    (defthm antisymmetry-of-o<=
      (implies (and (o-p x)
                    (o-p y)
                    (o<= x y)
                    (o<= y x))
               (equal x y))
      :rule-classes nil
      :hints (("Goal" :use non-circularity-of-o<)))
    
    (defthm transitivity-of-o<=
      (implies (and (o-p x)
                    (o-p y)
                    (o<= x y)
                    (o<= y z))
               (o<= x z))
      :rule-classes nil
      :hints (("Goal" :use transitivity-of-o<)))
    
    (defthm trichotomy-of-o<=
      (implies (and (o-p x)
                    (o-p y))
               (or (o<= x y)
                   (o<= y x)))
      :rule-classes nil
      :hints (("Goal" :use trichotomy-of-o<)))
    
    Crucially important to the proof of the well-foundedness of o< on o-ps is the concept of ordinal-depth, abbreviated od:
    (defun od (l)
      (if (o-finp l)
          0
        (1+ (od (o-first-expt l)))))
    
    If the od of an o-p x is smaller than that of an o-p y, then x is o< y:
    (defun od-1 (x y)
      (if (o-finp x)
          (list x y)
        (od-1 (o-first-expt x) (o-first-expt y))))
    
    (defthm od-implies-ordlessp
      (implies (and (o-p x)
                    (< (od x) (od y)))
               (o< x y))
      :hints (("Goal"
               :induct (od-1 x y))))
    
    Remark. A consequence of this lemma is the fact that if s = s(1), s(2), ... is an infinite, o< descending sequence of o-ps, then od(s(1)), od(s(2)), ... is a ``weakly'' descending sequence of non-negative integers: od(s(i)) is greater than or equal to od(s(i+1)).

    Lemma Main. For each non-negative integer n, o< well-orders the set of o-ps with od less than or equal to n .

     Base Case.  n = 0.  The o-ps with 0 od are the non-negative
     integers.  On the non-negative integers, o< is the same as <.
    
     Induction Step.  n > 0.  We assume that o< well-orders the
     o-ps with od less than n.
    
       If o< does not well-order the o-ps with od less than or equal to n,
       consider, D, the set of infinite, o< descending sequences of o-ps of od
       less than or equal to n.  The first element of a sequence in D has od n.
       Therefore, the o-first-expt of the first element of a sequence in D has od
       n-1.  Since o<, by IH, well-orders the o-ps with od less than n, the set
       of o-first-expts of first elements of the sequences in D has a minimal
       element, which we denote by B and which has od of n-1.
    
       Let k be the minimum integer such that for some infinite, o< descending
       sequence s of o-ps with od less than or equal to n, the first element of s
       has an o-first-expt of B and an o-first-coeff of k.  Notice that k is
       positive.
    
       Having fixed B and k, let s = s(1), s(2), ... be an infinite, o<
       descending sequence of o-ps with od less than or equal to n such that s(1)
       has a o-first-expt of B and an o-first-coeff of k.
    
       We show that each s(i) has a o-first-expt of B and an o-first-coeff of
       k. For suppose that s(j) is the first member of s either with o-first-expt
       B and o-first-coeff m (m neq k) or with o-first-expt B' and o-first-coeff
       B' (B' neq B). If (o-first-expt s(j)) = B', then B' has od n-1 (otherwise,
       by IH, s would not be infinite) and B' is o< B, contradicting the
       minimality of B. If 0 < m < k, then the fact that the sequence beginning
       at s(j) is infinitely descending contradicts the minimality of k. If m >
       k, then s(j) is greater than its predecessor; but this contradicts the
       fact that s is descending.
    
       Thus, by the definition of o<, for s to be a decreasing sequence of o-ps,
       (o-rst s(1)), (o-rst s(2)), ... must be a decreasing sequence. We end by
       showing this cannot be the case. Let t = t(1), t(2), ... be an infinite
       sequence of o-ps such that t(i) = (o-rst s(i)). Then t is infinitely
       descending. Furthermore, t(1) begins with an o-p B' that is o< B. Since t
       is in D, t(1) has od n, therefore, B' has od n-1. But this contradicts the
       minimality of B. Q.E.D.
    
    Theorem. o< well-orders the o-ps. Proof. Every infinite, o< descending sequence of o-ps has the property that each member has od less than or equal to the od, n, of the first member of the sequence. This contradicts Lemma Main. Q.E.D.




    acl2-sources/doc/HTML/PROOF-SUPPORTERS-ALIST.html0000664002132200015000000000067112222333521020234 0ustar kaufmannacl2 PROOF-SUPPORTERS-ALIST.html -- ACL2 Version 6.3

    PROOF-SUPPORTERS-ALIST

    See dead-events.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/PROOF-TREE-DETAILS.html0000664002132200015000000000542712222333526017467 0ustar kaufmannacl2 PROOF-TREE-DETAILS.html -- ACL2 Version 6.3

    PROOF-TREE-DETAILS

    proof tree details not covered elsewhere
    Major Section:  PROOF-TREE
    

    See proof-tree for an introduction to proof trees, and for a list of related topics. Here we present some details not covered elsewhere.

    1. When proof tree display is enabled (because the command :stop-proof-tree has not been executed, or has been superseded by a later :start-proof-tree command), then time summaries will include the time for proof tree display. This time includes the time spent computing with proof trees, such as the pruning process described briefly above. Even when proof trees are not displayed, such as when their display is turned off in the middle of a proof, this time will be printed if it is not 0.

    2. When a goal is given a :bye in a proof (see hints), it is treated for the purpose of proof tree display just as though it had been proved.

    3. Several state global variables affect proof tree display. (@ proof-tree-indent) is initially the string "| ": it is the string that is laid down the appropriate number of times to effect indentation. (@ proof-tree-buffer-width) is initially the value of (fmt-soft-right-margin state), and is used to prevent printing of the annotation ``(forced ...)'' in any greater column than this value. However, (assign proof-tree-buffer-width nil) to avoid any such suppression. Finally, (@ checkpoint-processors) is a list of processors from the constant list *preprocess-clause-ledge*, together with :induct. You may remove elements of (@ checkpoint-processors) to limit which processes are considered checkpoints, but note that this can affect what is printed by gag-mode (see set-gag-mode).

    4. When :otf-flg is not set to t in a proof, and the prover then decides to revert to the original goal and prove it by induction, the proof tree display will reflect this fact as shown here:

    c  0 |  |  Subgoal 2 PUSH (reverting)
    

    5. The usual failure message is printed as part of the prooftree display when a proof has failed.




    acl2-sources/doc/HTML/PROOF-TREE-EXAMPLES.html0000664002132200015000000002437312222333526017621 0ustar kaufmannacl2 PROOF-TREE-EXAMPLES.html -- ACL2 Version 6.3

    PROOF-TREE-EXAMPLES

    proof tree example
    Major Section:  PROOF-TREE
    

    See proof-tree for an introduction to proof trees, and for a list of related topics. Here we present a detailed example followed by a shorter example that illustrates proof by induction.

    Consider the guard proof for the definition of a function cancel_equal_plus; the body of this definition is of no importance here. The first proof tree display is:

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
         |  <18 subgoals>
    
    This is to be read as follows.

    At this stage of the proof we have encountered the top-level goal, named "Goal", which generated 18 subgoals using the ``preprocess'' process. We have not yet begun to work on those subgoals.

    The corresponding message from the ordinary prover output is:

    By case analysis we reduce the conjecture to the following 18 conjectures.

    Note that the field just before the name of the goal ("Goal"), which here contains the number 18, indicates the number of cases (children) created by the goal using the indicated process. This number will remain unchanged as long as this goal is displayed.

    The next proof tree display is:

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       1 |  Subgoal 18 simp
         |  |  <1 subgoal>
         |  <17 more subgoals>
    
    which indicates that at this point, the prover has used the simplification (``simp'') process on Subgoal 18 to create one subgoal (``<1 subgoal>''). The vertical bar (``|'') below ``Subgoal 18'', accompanied by the line below it, signifies that there are 17 siblings of Subgoal 18 that remain to be processed.

    The next proof tree displayed is:

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       1 |  Subgoal 18 simp
    c  2 |  |  Subgoal 18' ELIM
         |  |  |  <2 subgoals>
         |  <17 more subgoals>
    
    Let us focus on the fourth line of this display:
    c  2 |  |  Subgoal 18' ELIM
    

    The ``c'' field marks this goal as a ``checkpoint'', i.e., a goal worthy of careful scrutiny. In fact, any goal that creates children by a process other than ``preprocess'' or ``simp'' is marked as a checkpoint. In this case, the destructor-elimination (``ELIM'') process has been used to create subgoals of this goal. The indentation shows that this goal, Subgoal 18', is a child of Subgoal 18. The number ``2'' indicates that 2 subgoals have been created (by ELIM). Note that this information is consistent with the line just below it, which says ``<2 subgoals>''.

    Finally, the last line of this proof tree display,

         |  <17 more subgoals>
    
    is connected by vertical bars (``|'') up to the string "Subgoal 18", which suggests that there are 17 immediate subgoals of Goal remaining to process after Subgoal 18. Note that this line is indented one level from the second line, which is the line for the goal named "Goal". The display is intended to suggest that the subgoals of Goal that remain to be proved consist of Subgoal 18 together with 17 more subgoals.

    The next proof tree display differs from the previous one only in that now, Subgoal 18' has only one more subgoal to be processed.

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       1 |  Subgoal 18 simp
    c  2 |  |  Subgoal 18' ELIM
         |  |  |  <1 more subgoal>
         |  <17 more subgoals>
    
    Note that the word ``more'' in ``<1 more subgoal>'' tells us that there was originally more than one subgoal of Subgoal 18. In fact that information already follows from the line above, which (as previously explained) says that Subgoal 18' originally created 2 subgoals.

    The next proof tree display occurs when the prover completes the proof of that ``1 more subgoal'' referred to above.

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
         |  <17 more subgoals>
    
    Then, Subgoal 17 is processed and creates one subgoal, by simplification:
    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       1 |  Subgoal 17 simp
         |  |  <1 subgoal>
         |  <16 more subgoals>
    
    ... and so on.

    Later in the proof one might find the following successive proof tree displays.

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
         |  <9 more subgoals>
    
    ( DEFUN CANCEL_EQUAL_PLUS ...)
    
      18 Goal preprocess
       0 |  Subgoal 9 simp (FORCED)
         |  <8 more subgoals>
    
    These displays tell us that Subgoal 9 simplified to t (note that the ``0'' shows clearly that no subgoals were created), but that some rule's hypotheses were forced. Although this goal is not checkpointed (i.e., no ``c'' appears on the left margin), one can cause such goals to be checkpointed; see checkpoint-forced-goals.

    In fact, the proof tree displayed at the end of the ``main proof''(the 0-th forcing round) is as follows.

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       0 |  Subgoal 9 simp (FORCED)
       0 |  Subgoal 8 simp (FORCED)
       0 |  Subgoal 7 simp (FORCED)
       0 |  Subgoal 6 simp (FORCED)
       0 |  Subgoal 4 simp (FORCED)
       0 |  Subgoal 3 simp (FORCED)
    
    This is followed by the following proof tree display at the start of the forcing round.
      18 Goal preprocess
       0 |  Subgoal 9 simp (FORCED [1]Subgoal 4)
       0 |  Subgoal 8 simp (FORCED [1]Subgoal 6)
       0 |  Subgoal 7 simp (FORCED [1]Subgoal 1)
       0 |  Subgoal 6 simp (FORCED [1]Subgoal 3)
       0 |  Subgoal 4 simp (FORCED [1]Subgoal 5)
       0 |  Subgoal 3 simp (FORCED [1]Subgoal 2)
    ++++++++++++++++++++++++++++++
       6 [1]Goal FORCING-ROUND
       2 |  [1]Subgoal 6 preprocess
         |  |  <2 subgoals>
         |  <5 more subgoals>
    
    This display shows which goals to ``blame'' for the existence of each goal in the forcing round. For example, Subgoal 9 is to blame for the creation of [1]Subgoal 4.

    Actually, there is no real goal named "[1]Goal". However, the line

       6 [1]Goal FORCING-ROUND
    
    appears in the proof tree display to suggest a ``parent'' of the six top-level goals in that forcing round. As usual, the numeric field before the goal name contains the original number of children of that (virtual, in this case) goal -- in this case, 6.

    In our example proof, Subgoal 6 eventually gets proved, without doing any further forcing. At that point, the proof tree display looks as follows.

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       0 |  Subgoal 9 simp (FORCED [1]Subgoal 4)
       0 |  Subgoal 7 simp (FORCED [1]Subgoal 1)
       0 |  Subgoal 6 simp (FORCED [1]Subgoal 3)
       0 |  Subgoal 4 simp (FORCED [1]Subgoal 5)
       0 |  Subgoal 3 simp (FORCED [1]Subgoal 2)
    ++++++++++++++++++++++++++++++
       6 [1]Goal FORCING-ROUND
         |  <5 more subgoals>
    
    Notice that the line for Subgoal 8,
       0 |  Subgoal 8 simp (FORCED [1]Subgoal 6)
    
    no longer appears. That is because the goal [1]Subgoal 6 has been proved, along with all its children; and hence, the proof of Subgoal 8 no longer depends on any further reasoning.

    The final two proof tree displays in our example are as follows.

    ( DEFUN CANCEL_EQUAL_PLUS ...)
      18 Goal preprocess
       0 |  Subgoal 7 simp (FORCED [1]Subgoal 1)
    ++++++++++++++++++++++++++++++
       6 [1]Goal FORCING-ROUND
       2 |  [1]Subgoal 1 preprocess
       1 |  |  [1]Subgoal 1.1 preprocess
       1 |  |  |  [1]Subgoal 1.1' simp
    c  3 |  |  |  |  [1]Subgoal 1.1'' ELIM
         |  |  |  |  |  <1 more subgoal>
    
    ( DEFUN CANCEL_EQUAL_PLUS ...)
    <<PROOF TREE IS EMPTY>>
    
    The explanation for the empty proof tree is simple: once [1]Subgoal 1.1.1 was proved, nothing further remained to be proved. In fact, the much sought-after ``Q.E.D.'' appeared shortly after the final proof tree was displayed.

    Let us conclude with a final, brief example that illustrates proof by induction. Partway through the proof one might come across the following proof tree display.

    ( DEFTHM PLUS-TREE-DEL ...)
       1 Goal preprocess
       2 |  Goal' simp
    c  0 |  |  Subgoal 2 PUSH *1
         |  |  <1 more subgoal>
    
    This display says that in the attempt to prove a theorem called plus-tree-del, preprocessing created the only child Goal' from Goal, and Goal' simplified to two subgoals. Subgoal 2 is immediately pushed for proof by induction, under the name ``*1''. In fact if Subgoal 1 simplifies to t, then we see the following successive proof tree displays after the one shown above.
    ( DEFTHM PLUS-TREE-DEL ...)
       1 Goal preprocess
       2 |  Goal' simp
    c  0 |  |  Subgoal 2 PUSH *1
    
    ( DEFTHM PLUS-TREE-DEL ...)
       1 Goal preprocess
       2 |  Goal' simp
    c  0 |  |  Subgoal 2 PUSH *1
    ++++++++++++++++++++++++++++++
    c  6 *1 INDUCT
         |  <5 more subgoals>
    
    The separator ``+++++...'' says that we are beginning another trip through the waterfall. In fact this trip is for a proof by induction (as opposed to a forcing round), as indicated by the word ``INDUCT''. Apparently *1.6 was proved immediately, because it was not even displayed; a goal is only displayed when there is some work left to do either on it or on some goal that it brought (perhaps indirectly) into existence.

    Once a proof by induction is completed, the ``PUSH'' line that refers to that proof is eliminated (``pruned''). So for example, when the present proof by induction is completed, the line

    c  0 |  |  Subgoal 2 PUSH *1
    
    is eliminated, which in fact causes the lines above it to be eliminated (since they no longer refer to unproved children). Hence, at that point one might expect to see:
    ( DEFTHM PLUS-TREE-DEL ...)
    <<PROOF TREE IS EMPTY>>
    
    However, if the proof by induction of *1 necessitates further proofs by induction or a forcing round, then this ``pruning'' will not yet be done.




    acl2-sources/doc/HTML/PROOF-TREE.html0000664002132200015000000001207512222333526016401 0ustar kaufmannacl2 PROOF-TREE.html -- ACL2 Version 6.3

    PROOF-TREE

    proof tree displays
    Major Section:  ACL2 Documentation
    

    A view of ACL2 proofs may be obtained by way of ``proof tree displays,'' which appear in proof output (see proofs-co) when proof-tree output is enabled (see below) When ACL2 starts a proof and proof-tree output is enabled, the proof output begins with the following string.

    << Starting proof tree logging >>
    
    Then for each goal encountered during the proof, a corresponding proof tree display (as described below) is printed into the proof output: first the characters in the constant string *proof-tree-start-delimiter* are printed, then the proof tree display, and finally the characters in the constant string *proof-tree-end-delimiter*.

    External tools may present proof tree displays in a separate window. In particular, a tool distributed with the ACL2 community books customizes the emacs environment to provide window-based proof tree displays together with commands for traversing the proof transcript; see the discussion of ``ACL2 proof-tree support'' in file emacs/emacs-acl2.el distributed with ACL2.

    The command :start-proof-tree enables proof-tree output, while :stop-proof-tree disables proof-tree output; see start-proof-tree and see stop-proof-tree.

    Some Related Topics

    Here is an example of a proof tree display, with comments. Lines marked with ``c'' are considered ``checkpoints,'' i.e., goals whose scrutiny may be of particular value.

    ( DEFTHM PLUS-TREE-DEL ...)    ;currently proving PLUS-TREE-DEL
       1 Goal preprocess   ;"Goal" creates 1 subgoal by preprocessing
       2 |  Goal' simp     ;"Goal'" creates 2 subgoals by simplification
    c  0 |  |  Subgoal 2 PUSH *1   ;"Subgoal 2" pushes "*1" for INDUCT
    ++++++++++++++++++++++++++++++ ;first pass thru waterfall completed
    c  6 *1 INDUCT                 ;Proof by induction of "*1" has
         |  <5 more subgoals>      ; created 6 top-level subgoals.  At
                                   ; this point, one of those 6 has been
                                   ; proved, and 5 remain to be proved.
                                   ; We are currently working on the
                                   ; first of those 5 remaining goals.
    
    See proof-tree-examples for many examples that contain proof tree displays. But first, we summarize the kinds of lines that may appear in a proof tree display. The simplest form of a proof tree display is a header showing the current event, followed by list of lines, each having one of the following forms.
        n <goal> <process> ...
    
    Says that the indicated goal created n subgoals using the indicated process. Here ``...'' refers to possible additional information.
    c   n <goal> <process> ...
    
    As above, but calls attention to the fact that this goal is a ``checkpoint'' in the sense that it may be of particular interest. Some displays may overwrite ``c'' with ``>'' to indicate the current checkpoint being shown in the proof transcript.
         |  <goal> ...
         |  |  <k subgoals>
    
    Indicates that the goal just above this line, which is pointed to by the rightmost vertical bar (``|''), has k subgoals, none of which have yet been processed.
         |  <goal> ...
         |  |  <k more subgoals>
    
    As above, except that some subgoals have already been processed.
    ++++++++++++++++++++++++++++++
    
    Separates successive passes through the ``waterfall''. Thus, this ``fencepost'' mark indicates the start of a new proof by induction or of a new forcing round.

    See proof-tree-examples for detailed examples. See checkpoint-forced-goals to learn how to mark goals as checkpoints that force the creation of goals in forcing rounds. Finally, see proof-tree-details for some points not covered elsewhere.




    acl2-sources/doc/HTML/PROOFS-CO.html0000664002132200015000000000217012222333524016257 0ustar kaufmannacl2 PROOFS-CO.html -- ACL2 Version 6.3

    PROOFS-CO

    the proofs character output channel
    Major Section:  ACL2-BUILT-INS
    

    Proofs-co is an ld special (see ld). The accessor is (proofs-co state) and the updater is (set-proofs-co val state). Proofs-co must be an open character output channel. It is to this channel that defun, defthm, and the other event commands print their commentary.

    ``Proofs-co'' stands for ``proofs character output.'' The initial value of proofs-co is the same as the value of *standard-co* (see *standard-co*).




    acl2-sources/doc/HTML/PROPER-CONSP.html0000664002132200015000000000126312222333524016641 0ustar kaufmannacl2 PROPER-CONSP.html -- ACL2 Version 6.3

    PROPER-CONSP

    recognizer for proper (null-terminated) non-empty lists
    Major Section:  ACL2-BUILT-INS
    

    Proper-consp is the function that checks whether its argument is a non-empty list that ends in nil. Also see true-listp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/PROPS.html0000664002132200015000000000107312222333522015652 0ustar kaufmannacl2 PROPS.html -- ACL2 Version 6.3

    PROPS

    print the ACL2 properties on a symbol
    Major Section:  OTHER
    

    Example:
    :props assoc-eq
    

    Props takes one argument, a symbol, and prints all of the properties that are on that symbol in the ACL2 world.




    acl2-sources/doc/HTML/PROVISIONAL-CERTIFICATION.html0000664002132200015000000004377612222333516020560 0ustar kaufmannacl2 PROVISIONAL-CERTIFICATION.html -- ACL2 Version 6.3

    PROVISIONAL-CERTIFICATION

    certify a book in stages for improved parallelism
    Major Section:  BOOKS
    

    Provisional certification is a process that can increase parallelism at the system level, typically using `make', when certifying a collection of books. We got this idea from Jared Davis, who developed rudimentary provisional certification schemes first at Rockwell Collins and later for his `Milawa' project. Our design has also benefited from conversations with Sol Swords.

    To invoke provisional certification, see books-certification. For example, you could issue the following command.

    ACL2_PCERT=t cert.pl -j 4 `find . -name '*.lisp'`
    

    Alternatively, see books-certification-classic for a discussion of classic ACL2 `make'-based certification (which may disappear in a future ACL2 release); here we extend those instructions to show how to use provisional certification. (Also, you may wish to look at community books file books/system/pcert/Makefile for an example.) We begin by describing a few ways to do that. A simple way is to add the line `ACL2_PCERT=t' to a `make' command that you use for book certification, for example as follows.

    make -j 4 ACL2_PCERT=t
    
    The following works too, in a bash shell.
    (export ACL2_PCERT=t ; time make -j 4)
    
    Alternatively, add the line
    ACL2_PCERT ?= t
    
    to the Makefile residing in the directory, placed above the line that specifies the `include' of file Makefile-generic. A successful `make' will result in creating the desired certificate (.cert) files.

    Warning: If you put the line ``ACL2_PCERT ?= t'' below the include of Makefile-generic, it might have no effect. For example, try editing community books file books/system/pcert/Makefile by moving the line ``ACL2_PCERT ?= t'' to the bottom of the file, and watch ``make top.cert'' fail to invoke provisional certification.

    The description above may be sufficient for you to use provisional certification. We provide additional documentation below for the reader who wants to understand more about this process, for example when not using `make'. Below we assume prior familiarity with books, in particular certify-book and include-book. The remainder of this documentation topic is divided into sections: Summary, Correctness Claim and Issues, Combining Pcertify and Convert into Pcertify+, and Further Information.

    Summary

    Recall that certification of a book, bk, produces a certificate file bk.cert. The provisional certification process produces this file as well, but as the last of the following three steps. All of these steps are carried out by calls of certify-book using its :pcert keyword argument. We typically call these steps ``procedures'', to distinguish them from the steps of an individual call of certify-book.

    o The ``Pcertify'' procedure (sometimes called the ``Create'' procedure) is invoked by calling certify-book with keyword argument :pcert :create. It produces a file bk.pcert0, sometimes called the ``.pcert0'' file (pronounced ``dot pee cert zero''). Proofs are skipped during this procedure, which can be viewed as an elaborate syntax check, augmented by compilation if specified (as it is by default).

    o The ``Convert'' procedure is invoked by calling certify-book with keyword argument :pcert :convert. It creates file bk.pcert1 from bk.pcert0, by doing proofs for all events in bk.lisp. Note that the third argument (the `compile-flg' argument) is ignored by such a call of certify-book unless its value is :all (either explicitly or by way of environment variable ACL2_COMPILE_FLG). A subtlety is that if there is a compiled file at least as recent as the corresponding .pcert0 file, then that compiled file's write date will be updated to the current time at the end of this procedure. The usual local-incompatibility check at the end of certify-book is omitted for the Convert procedure, since it was performed towards the end of the Create procedure.

    o The ``Complete'' procedure is invoked by calling certify-book with keyword argument :pcert :complete. It checks that every included book (including every one that is locally included) has a .cert file that is at least as recent as the corresponding book. The effect is to move bk.pcert1 to bk.cert. Note that all arguments of certify-book other than the :pcert argument are ignored for this procedure, other than for some trivial argument checking.

    You can combine the Pcertify and Convert procedures into a single procedure, Pcertify+, which may be useful for books that contain expensive include-book events but do few proofs. We defer discussion of that feature to the section below, ``Combining Pcertify and Convert into Pcertify+''.

    The main idea of provisional certification is to break sequential dependencies caused by include-book, that is, so that a book's proofs are carried out even when it includes books (sometimes called ``sub-books'') that have not themselves been fully certified. For example, suppose that a proof development consists of books A.lisp, B.lisp, and C.lisp, where file A.lisp contains the form (include-book "B") and file B.lisp contains the form (include-book "C"). Normally one would first certify C, then B, and finally, A. However, the provisional certification process can speed up the process on a multi-core machine, as follows: the Pcertify (pronounced ``pee certify'') procedure respects this order but (one hopes) is fast since proofs are skipped; the Convert procedure essentially completes the certification in parallel by doing proofs and creating .pcert1 files based on .pcert0 files; and the Complete procedure respects book order when quickly renaming .pcert1 files to .cert files. In our example, the steps would be as follows, but note that we need not finish all Pcertify steps before starting some Convert steps, nor need we finish all Convert steps before starting some Complete steps, as explained further below.

    o Pcertify books "C", "B",and then "A", sequentially, skipping proofs but doing compilation.

    o Do proofs in parallel for the books using the Convert procedure, where each book relies on the existence of its own .pcert0 file as well as a .cert, .pcert0, or .pcert1 file for each of its included sub-books. Write out a .pcert1 file for the book when the proof succeeds.

    o Rename the .pcert1 file to a corresponding .cert file when a .cert file exists and is up-to-date for each included book.

    The Convert step can begin for bk.lisp any time after bk.pcert0 is built. The Complete step can begin for this book any time after bk.pcert1 and every sub-bk.cert are built, for sub-bk a sub-book of bk.

    The new procedures -- Pcertify, Convert, and Complete -- are invoked by supplying a value for the keyword argument :pcert of certify-book, namely :create, :convert, or :complete, respectively. Typically, and by default, the compile-flg argument of certify-book is t for the Pcertify procedure, so that compilation can take full advantage of parallelism. This argument is treated as nil for the other procedures except when its value is :all in the Convert procedure, as mentioned above.

    For those who use make-event, we note that expansion is done in the Pcertify procedure; the later steps use the expansion resulting from that procedure. The reason is that although a call of make-event is similar to a macro call, a difference is that the expansion of a make-event form can depend on the state. Therefore, we insist on doing such an expansion only once, so that all books involved agree on the expansion. We say more about make-event below.

    Correctness Claim and Issues

    The Basic Claim for certification is the same whether or not the provisional certification process is employed: all books should be certified from scratch, with no files written to the directories of the books except by ACL2. Moreover, no trust tags should be used (see defttag), or else it is the responsibility of the user to investigate every occurrence of ``TTAG NOTE'' that is printed to standard output.

    But common practice is to certify a set of books in stages: certify a few books, fix some books, re-certify changed books, certify some more books, and so on. In practice, we expect this process to be sound even though it does not meet the preconditions for the Basic Claim above. In particular, we expect that the use of checksums in certificates will make it exceedingly unlikely that a book is still treated as certified after any events in the book or any sub-book, or any portcullis commands of the book or any sub-book, have been modified.

    Provisional certification makes it a bit easier for a determined user to subvert correctness. For example, the Complete procedure only checks write dates to ensure that each sub-book's .cert file is no older than the corresponding .lisp file, but it does not look inside .cert files of sub-books; in particular it does not look at their checksum information. Of course, the automatic dependency analysis provided by classic ACL2 `make'-based certification avoids accidental problems of this sort. And, checksum information will indeed be applied at include-book time, at least for sub-books included non-locally.

    In short: while we believe that the provisional certification process can be trusted, we suggest that for maximum trust, it is best for all books in a project to be certified from scratch without the provisional certification process.

    Combining Pcertify and Convert into Pcertify+

    You can combine the Pcertify and Convert procedure into a single procedure, Pcertify+, which may be useful for books that contain expensive include-book events but do few proofs. If you are using `make' to do provisional certification as described above, just set `make' variable ACL2_BOOKS_PCERT_ARG_T to the list of books for which you want the Pcertify+ procedure performed instead of separate Pcertify and Convert procedures. Either of two common methods may be used to set this variable, as illustrated below for the case that books sub.lisp and mid.lisp are the ones on which you want Pcertify+ performed. One method is to add the following to your directory's Makefile, above the include of Makefile-generic.

    ACL2_BOOKS_PCERT_ARG_T = sub mid
    
    Alternatively, you can specify the desired books on the command line, for example as follows.
    make -j 4 ACL2_BOOKS_PCERT_ARG_T='sub mid'
    
    Note that the books are given without their .lisp extensions.

    At the ACL2 level, the Pcertify+ procedure is performed when the value t is supplied to the :pcert keyword argument of certify-book. Thus, :pcert t can be thought of as a combination of :pcert :create and :pcert :convert. However, what ACL2 actually does is to perform the Pcertify step without skipping proofs, and at the end of the certify-book run, it writes out both the .pcert0 and .pcert1 file, with essentially the same contents. (We say ``essentially'' because the implementation writes :PCERT-INFO :PROVED to the end of the .pcert0 file, but not to the .pcert1 file.)

    Further Information

    Some errors during provisional certification cannot be readily solved. For example, if there are circular directory dependencies (for example, some book in directory D1 includes some book in directory D2 and vice-versa), then classic ACL2 `make'-based certification will quite possibly fail. For another example, perhaps your directory's Makefile is awkward to convert to one with suitable dependencies. When no fix is at hand, it might be best simply to avoid provisional certification. If you are using classic ACL2 `make'-based certification, you can simply add the following line to your directory's Makefile, or use ``ACL2_PCERT= '' on the `make' command line, to avoid provisional certification.

    override ACL2_PCERT =
    

    We invite anyone who has troubleshooting tips to contact the ACL2 developers with suggestions for adding such tips to this section.

    Our next remark is relevant only to users of make-event, and concerns the interaction of that utility with state global ld-skip-proofsp. Normally, the global value of ld-skip-proofsp is unchanged during make-event expansion, except that it is bound to nil when the make-event form has a non-nil :check-expansion argument. But during the Pcertify procedure (not the Pcertify+ procedure), ld-skip-proofsp is always bound to nil at the start of make-event expansion. To see why, consider for example the community book books/make-event/proof-by-arith.lisp. This book introduces a macro, proof-by-arith, that expands to a call of make-event. This make-event form expands by trying to prove a given theorem using a succession of included arithmetic books, until the proof succeeds. Now proofs are skipped during the Pcertify procedure, and if proofs were also skipped during make-event expansion within that procedure, the first arithmetic book's include-book form would always be saved because the theorem's proof ``succeeded'' (as it was skipped!). Of course, the theorem's proof could then easily fail during the Convert step. If you really want to inhibit proofs during make-event expansion in the Pcertify step, consider using a form such as the following: (state-global-let* ((ld-skip-proofsp nil)) ...).

    Finally, we describe what it means for there to be a valid certificate file for including a certified book. Normally, this is a file with extension .cert. However, if that .cert file does not exist, then ACL2 looks for a .pcert0 file instead; and if that also does not exist, it looks for a .pcert1 file. (To see why does the .pcert0 file take priority over the .pcert1 file, note that the Convert procedure copies a .pcert0 file to a .pcert1 file, so both might exist -- but the .pcert1 file might be incomplete if copying is in progress.) Once the candidate certificate file is thus selected, it must be valid in order for the book to be considered certified (see certificate). For the certificate file as chosen above, then in order for a compiled file to be loaded, it must be at least as recent as that certificate file.

    Again, as discussed above, a .pcert0 or .pcert1 file may serve as a valid certificate file when the .cert file is missing. But when that happens, a warning may be printed that a ``provisionally certified'' book has been included. No such warning occurs if environment variable ACL2_PCERT has a non-empty value, or if that warning is explicitly inhibited (see set-inhibit-warnings and see set-inhibit-output-lst).




    acl2-sources/doc/HTML/PR_bang_.html0000664002132200015000000000233312222333517016422 0ustar kaufmannacl2 PR_bang_.html -- ACL2 Version 6.3

    PR!

    print rules stored by the command with a given command descriptor
    Major Section:  HISTORY
    

    Examples:
    
    :pr! fn ; prints the rules from the definition of fn (including any
            ; :type-prescription rule and :definition rule), as well as all other
            ; rules created by the command that created by fn (which could be
            ; many rules if, for example, fn was defined by an include-book
            ; command).
    
    :pr! :max ; prints all the rules stored by the most recent command
    

    Also see pr, which is similar but works at the event level instead of at the command level.

    Pr takes one argument, a command descriptor, and prints the rules created by the corresponding event. In each case it prints the rune, the current enabled/disabled status, and other appropriate fields from the rule. See pr for further details.




    acl2-sources/doc/HTML/PSEUDO-TERMP.html0000664002132200015000000000770312222333524016643 0ustar kaufmannacl2 PSEUDO-TERMP.html -- ACL2 Version 6.3

    PSEUDO-TERMP

    a predicate for recognizing term-like s-expressions
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    (pseudo-termp '(car (cons x 'nil)))      ; has value t
    (pseudo-termp '(car x y z))              ; also has value t!
    (pseudo-termp '(delta (h x)))            ; has value t
    (pseudo-termp '(delta (h x) . 7))        ; has value nil (not a true-listp)
    (pseudo-termp '((lambda (x) (car x)) b)) ; has value t
    (pseudo-termp '(if x y 123))             ; has value nil (123 is not quoted)
    (pseudo-termp '(if x y '123))            ; has value t
    
    If x is the quotation of a term, then (pseudo-termp x) is t. However, if x is not the quotation of a term it is not necessarily the case that (pseudo-termp x) is nil.

    See term for a discussion of the various meanings of the word ``term'' in ACL2. In its most strict sense, a term is either a legal variable symbol, a quoted constant, or the application of an n-ary function symbol or closed lambda-expression to n terms. By ``legal variable symbol'' we exclude constant symbols, such as t, nil, and *ts-rational*. By ``quoted constants'' we include 't (aka (quote t)), 'nil, '31, etc., and exclude constant names such as t, nil and *ts-rational*, unquoted constants such as 31 or 1/2, and ill-formed quote expressions such as (quote 3 4). By ``closed lambda expression'' we exclude expressions, such as (lambda (x) (cons x y)), containing free variables in their bodies. Terms typed by the user are translated into strict terms for internal use in ACL2.

    The predicate termp checks this strict sense of ``term'' with respect to a given ACL2 logical world; See world. Many ACL2 functions, such as the rewriter, require certain of their arguments to satisfy termp. However, as of this writing, termp is in :program mode and thus cannot be used effectively in conjectures to be proved. Furthermore, if regarded simply from the perspective of an effective guard for a term-processing function, termp checks many irrelevant things. (Does it really matter that the variable symbols encountered never start and end with an asterisk?) For these reasons, we have introduced the notion of a ``pseudo-term'' and embodied it in the predicate pseudo-termp, which is easier to check, does not require the logical world as input, has :logic mode, and is often perfectly suitable as a guard on term-processing functions.

    A pseudo-termp is either a symbol, a true list of length 2 beginning with the word quote, the application of an n-ary pseudo-lambda expression to a true list of n pseudo-terms, or the application of a symbol to a true list of n pseudo-termps. By an ``n-ary pseudo-lambda expression'' we mean an expression of the form (lambda (v1 ... vn) pterm), where the vi are symbols (but not necessarily distinct legal variable symbols) and pterm is a pseudo-termp.

    Metafunctions may use pseudo-termp as a guard.




    acl2-sources/doc/HTML/PSO.html0000664002132200015000000000153512222333522015413 0ustar kaufmannacl2 PSO.html -- ACL2 Version 6.3

    PSO

    show the most recently saved output
    Major Section:  OTHER
    

    Evaluate :pso in order to print output that was generated in an environment where output was being saved; see set-saved-output for details. However, proof-tree output will be suppressed; use :pso! if you want that output to be printed as well.

    Also see psog, for printing saved output in gag-mode.




    acl2-sources/doc/HTML/PSOF.html0000664002132200015000000000267312222333522015525 0ustar kaufmannacl2 PSOF.html -- ACL2 Version 6.3

    PSOF

    show the most recently saved output
    Major Section:  OTHER
    

    For a similar utility, see pso. Like :pso, the :psof command prints output that was generated in an environment where output was being saved, typically gag-mode; also see set-saved-output. But unlike :pso, :psof takes a filename argument and saves output to that file, instead of to the terminal. For large proofs, :psof may complete more quickly than :pso. Note that as with :pso, proof-tree output will be suppressed.

    The first line of output from :psof directs the Emacs editor to use auto-revert mode. You can change the frequency of auto-reverting the buffer connected to a file by evaluating a suitable command in Emacs. For example, the command (setq auto-revert-interval .1) arranges for auto-revert mode to update as needed every 1/10 of a second.




    acl2-sources/doc/HTML/PSOG.html0000664002132200015000000000136412222333522015522 0ustar kaufmannacl2 PSOG.html -- ACL2 Version 6.3

    PSOG

    show the most recently saved output in gag-mode
    Major Section:  OTHER
    

    Evaluate :psog in order to print output in gag-mode that was generated in an environment where output was being saved; see set-saved-output for details.

    Also see pso and see pso! for printing the full output.




    acl2-sources/doc/HTML/PSO_bang_.html0000664002132200015000000000161212222333522016535 0ustar kaufmannacl2 PSO_bang_.html -- ACL2 Version 6.3

    PSO!

    show the most recently saved output, including proof-tree output
    Major Section:  OTHER
    

    Evaluate :pso! in order to print output that was generated in an environment where output was being saved; see set-saved-output for details. Note that proof-tree will be included; use :pso if you want that output to be suppressed.

    Also see psog, for printing saved output in gag-mode.




    acl2-sources/doc/HTML/PSTACK.html0000664002132200015000000000623212222333522015736 0ustar kaufmannacl2 PSTACK.html -- ACL2 Version 6.3

    PSTACK

    seeing what the prover is up to
    Major Section:  OTHER
    

    General Forms:
    (pstack)      ; inspect break
    (pstack t)    ; inspect break, printing all calls in abbreviated form
    (pstack :all) ; as above, but only abbreviating the ACL2 world
    

    When the form (pstack) is executed during a break from a proof, or at the end of a proof that the user has aborted, a ``process stack'' (or ``prover stack'') will be printed that gives some idea of what the theorem prover has been doing. Moreover, by evaluating (verbose-pstack t) before starting a proof (see verbose-pstack) one can get trace-like information about prover functions, including time summaries, printed to the screen during a proof. This feature is currently quite raw and may be refined considerably as time goes on, based on user suggestions. For example, the usual control of printing given by set-inhibit-output-lst is irrelevant for printing the pstack.

    The use of (pstack t) or (pstack :all) should only be used by those who are comfortable looking at functions in the ACL2 source code. Otherwise, simply use (pstack).

    Some Related Topics

    • VERBOSE-PSTACK -- seeing what the prover is up to (for advanced users)

    Entries in the pstack include the following (listed here alphabetically, except for the first).

    preprocess-clause, simplify-clause, etc. (in general,xxx-clause): top-level processes in the prover ``waterfall''

    clausify: splitting a goal into subgoals

    ev-fncall: evaluating a function on explicit arguments

    ev-fncall-meta: evaluating a metafunction

    forward-chain: building a context for the current goal using forward-chaining rules

    induct: finding an induction scheme

    pop-clause: getting the next goal to prove by induction

    process-assumptions: creating forcing rounds

    remove-built-in-clauses: removing built-in clauses (see built-in-clause)

    process-equational-polys: deducing interesting equations

    remove-trivial-equivalences: removing trivial equalities (and equivalences) from the current goal

    rewrite-atm: rewriting a top-level term in the current goal

    setup-simplify-clause-pot-lst: building the linear arithmetic database for the current goal

    strip-branches, subsumption-replacement-loop: subroutines of clausify

    waterfall: top-level proof control




    acl2-sources/doc/HTML/PUFF.html0000664002132200015000000002546712222333517015530 0ustar kaufmannacl2 PUFF.html -- ACL2 Version 6.3

    PUFF

    replace a compound command by its immediate subevents
    Major Section:  HISTORY
    

    Example Forms:
    ACL2 !>:puff :max
    ACL2 !>:puff :x
    ACL2 !>:puff 15
    ACL2 !>:puff "book"
    
    General Form:
    :puff cd
    
    where cd is a command descriptor (see command-descriptor) for a ``puffable'' command (see below). Puff replaces the command at cd by the immediate subevents of the command, executed as commands. Puff then prints, using pcs, the puffed region.

    We consider puff to be a sort of hack; it is generally robust and sound, but that is not guaranteed. If any existing ACL2 event resulted from puff, ACL2 considers proofs to have been skipped, and thus certify-book is disallowed until such events have been undone (see ubt).

    A ``puffable'' command is an encapsulate command, an include-book command, or any command other than those consisting of a single primitive event. For example, since defun is a primitive event, a defun command is not puffable. But a macro form that expands into several defun events is puffable. The only primitive events that are puffable are calls of encapsulate or include-book. A puffable command contains (interesting) subevents, namely, the events in the body of the encapsulate, in the file of the book included, or in the command block.

    (Obscure exceptions. (1) An encapsulate command generated by the macro define-trusted-clause-processor is not puffable. (2) If a use of make-event results in local events in the scope of an encapsulate or include-book event within a command, then an attempt to puff that command will result in an error, leaving the world unchanged, if any such local event was necessary to support other events in the command. (3) An attempt to puff an include-book command may fail for a book that has been modified, as describe late in this documentation topic.)

    The puff command ``lifts'' the immediate subevents of the indicated command so that they become commands themselves. The command puff* recursively puffs the newly introduced commands. See puff*, which also gives an example illustrating both puff and puff*. Puff undoes the command at cd and replaces it by its immediate subevents. Thus, in general the length of the history grows when a puff command is executed. If puff causes an error (see below), the logical world remains unchanged from its initial configuration.

    The intended use of puff is to allow the user access to the events ``hidden'' inside compound commands. For example, while trying to prove some theorem, p, about a constrained function, fn, one might find that the encapsulate, cd, that introduced fn failed to include an important constraint, q. Without puff, the only way to proceed is to undo back through cd, create a suitable encapsulate that proves and exports q as well as the old constraints, re-execute the new encapsulate, re-execute the events since cd, and then try p again. Unfortunately, it may be hard to prove q and additional events may have to be inserted into the encapsulate to prove it. It may also be hard to formulate the ``right'' q, i.e., one that is provable in the encapsulate and provides the appropriate facts for use in the proof of p.

    Using puff, the user can erase the encapsulate at cd, replacing it by the events in its body. Now the formerly constrained function, fn, is defined as its witness. The user can experiment with formulations and proofs of q suitable for p. Of course, to get into the ultimately desired state -- where fn is constrained rather than defined and q is exported by an encapsulate at cd -- the user must ultimately undo back to cd and carry out the more tedious program described above. But by using puff it is easier to experiment.

    Similar applications of puff allow the user of a book to expose the innards of the book as though they had all be typed as commands. The user might then ``partially undo'' the book, keeping only some of the events in it.

    Puff operates as follows. First, it determines the list of immediate subevents of the command indicated by cd. It causes an error if there is only one subevent and that subevent is identical to the command -- i.e., if the command at cd is a primitive. Next, puff undoes back through the indicated command. This not only erases the command at cd but all the commands executed after it. Finally, puff re-executes the subevents of (the now erased) cd followed by all the commands that were executed afterwards.

    Observe that the commands executed after cd will generally have higher command numbers than they did before the puff. For example, suppose 100 commands have been executed and that :puff 80 is then executed. Suppose command 80 contains 5 immediate subevents (i.e., is an encapsulation of five events). Then, after puffing, command 80 is the first event of the puffed command, command 81 is the second, and so on; 104 commands appear to have been executed.

    When puffing an encapsulate or include-book, the local commands are executed. Note that this will replace constrained functions by their witnesses.

    Finally, it is impossible to puff in the presence of include-book events for certified books that have been altered since they were included. (Note that this restriction only applies to include-book, not to certify-book.) To be specific, suppose "arith" is a certified book that has been included in a session. Suppose that after "arith" was included, the source file is modified. (This might happen if the user of "arith" is not its author and the author happens to be working on a new version of "arith" during the same time period.) Now suppose the user tries to puff the command that included "arith". The attempt to obtain the subevents in "arith" will discover that the check sum of "arith" has changed and an error will be caused. No change is made in the logical world. A similar error is caused if, in this same situation, the user tries to puff any command that occurred before the inclusion of "arith"! That is, puff may cause an error and leave the world unchanged even if the command puffed is not one involving the modified book. This happens because in order to reconstruct the world after the puffed command, puff must obtain the events in the book and if the book's source file has changed there is no assurance that the reconstructed world is the one the user intends.

    Warning: We do not detect changes to uncertified books that have been included and are then puffed or re-included! The act of including an uncertified book leaves no trace of the check sum of the book. Furthermore, the act prints a warning message disclaiming soundness. In light of this, :puff quietly ``re-''executes the current contents of the book.




    acl2-sources/doc/HTML/PUFF_star_.html0000664002132200015000000001041712222333517016705 0ustar kaufmannacl2 PUFF_star_.html -- ACL2 Version 6.3

    PUFF*

    replace a compound command by its subevents
    Major Section:  HISTORY
    

    Example Forms:
    ACL2 !>:puff* :max
    ACL2 !>:puff* :x
    ACL2 !>:puff* 15
    ACL2 !>:puff* "book"
    
    General Form:
    :puff* cd
    
    where cd is a command descriptor (see command-descriptor) for a ``puffable'' command. See puff for the definition of ``puffable'' and for a description of the basic act of ``puffing'' a command. In particular, see puff for a discussion of a sense in which puff, and hence puff*, should be viewed as a hack. Puff* is just the recursive application of puff. Puff* prints the region puffed, using pcs.

    To puff a command is to replace it by its immediate subevents, each of which is executed as a command. To puff* a command is to replace the command by each of its immediate subevents and then to puff* each of the puffable commands among the newly introduced ones.

    For example, suppose "ab" is a book containing the following

    (in-package "ACL2")
    (include-book "a")
    (include-book "b")
    
    Suppose that book "a" only contained defuns for the functions a1 and a2 and that "b" only contained defuns for b1 and b2.

    Now consider an ACL2 state in which only two commands have been executed, the first being (include-book "ab") and the second being (include-book "c"). Thus, the relevant part of the display produced by :pbt 1 would be:

    1 (INCLUDE-BOOK "ab")
    2 (INCLUDE-BOOK "c")
    
    Call this state the ``starting state'' in this example, because we will refer to it several times.

    Suppose :puff 1 is executed in the starting state. Then the first command is replaced by its immediate subevents and :pbt 1 would show:

    1 (INCLUDE-BOOK "a")
    2 (INCLUDE-BOOK "b")
    3 (INCLUDE-BOOK "c")
    
    Contrast this with the execution of :puff* 1 in the starting state. Puff* would first puff (include-book "ab") to get the state shown above. But then it would recursively puff* the puffable commands introduced by the first puff. This continues recursively as long as any puff introduced a puffable command. The end result of :puff* 1 in the starting state is
    1 (DEFUN A1 ...)
    2 (DEFUN A2 ...)
    3 (DEFUN B1 ...)
    4 (DEFUN B2 ...)
    5 (INCLUDE-BOOK "c")
    
    Observe that when puff* is done, the originally indicated command, (include-book "ab"), has been replaced by the corresponding sequence of primitive events. Observe also that puffable commands elsewhere in the history, for example, command 2 in the starting state, are not affected (except that their command numbers grow as a result of the splicing in of earlier commands).




    acl2-sources/doc/HTML/PUSH-UNTOUCHABLE.html0000664002132200015000000000424712222333531017303 0ustar kaufmannacl2 PUSH-UNTOUCHABLE.html -- ACL2 Version 6.3

    PUSH-UNTOUCHABLE

    add name or list of names to the list of untouchable symbols
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (push-untouchable my-var nil)
    (push-untouchable set-mem t)
    
    General Form:
    (push-untouchable name{s}  fn-p :doc doc-string)
    
    where name{s} is a non-nil symbol or a non-nil true list of symbols, fn-p is any value (but generally nil or t), and doc-string is an optional documentation string not beginning with ``:Doc-Section ...''. If name{s} is a symbol it is treated as the singleton list containing that symbol. The effect of this event is to union the given symbols into the list of ``untouchable variables'' in the current world if fn-p is nil, else to union the symbols into the list of ``untouchable functions''. This event is redundant if every symbol listed is already a member of the appropriate untouchables list (variables or functions).

    When a symbol is on the untouchables list it is syntactically illegal for any event to call a function or macro of that name, if fn-p is non-nil, or to change the value of a state global variable of that name, if fn-p is nil. Thus, the effect of pushing a function symbol, name, onto untouchables is to prevent any future event from using that symbol as a function or macro, or as a state global variable (according to fn-p). This is generally done to ``fence off'' some primitive function symbol from ``users'' after the developer has used the symbol freely in the development of some higher level mechanism.

    Also see remove-untouchable.




    acl2-sources/doc/HTML/PUT-ASSOC-EQ.html0000664002132200015000000000064312222333524016574 0ustar kaufmannacl2 PUT-ASSOC-EQ.html -- ACL2 Version 6.3

    PUT-ASSOC-EQ

    See put-assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/PUT-ASSOC-EQL.html0000664002132200015000000000064512222333524016712 0ustar kaufmannacl2 PUT-ASSOC-EQL.html -- ACL2 Version 6.3

    PUT-ASSOC-EQL

    See put-assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/PUT-ASSOC-EQUAL.html0000664002132200015000000000065112222333524017135 0ustar kaufmannacl2 PUT-ASSOC-EQUAL.html -- ACL2 Version 6.3

    PUT-ASSOC-EQUAL

    See put-assoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/PUT-ASSOC.html0000664002132200015000000000456312222333524016276 0ustar kaufmannacl2 PUT-ASSOC.html -- ACL2 Version 6.3

    PUT-ASSOC

    modify an association list by associating a value with a key
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (put-assoc name val alist)
    (put-assoc name val alist :test 'eql)   ; same as above (eql as equality test)
    (put-assoc name val alist :test 'eq)    ; same, but eq is equality test
    (put-assoc name val alist :test 'equal) ; same, but equal is equality test
    

    (Put-assoc name val alist) returns an alist that is the same as the list alist, except that the first pair in alist with a car of name is replaced by (cons name val), if there is one. If there is no such pair, then (cons name val) is added at the end. Note that the order of the keys occurring in alist is unchanged (though a new key may be added).

    The guard for a call of put-assoc depends on the test. In all cases, the last argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the last argument must satisfy eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the last argument must satisfy symbol-alistp.

    See equality-variants for a discussion of the relation between put-assoc and its variants:

    (put-assoc-eq name val alist) is equivalent to (put-assoc name val alist :test 'eq);

    (put-assoc-equal name val alist) is equivalent to (put-assoc name val alist :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function put-assoc-equal.




    acl2-sources/doc/HTML/PUTPROP.html0000664002132200015000000000144112222333524016121 0ustar kaufmannacl2 PUTPROP.html -- ACL2 Version 6.3

    PUTPROP

    update fast property lists
    Major Section:  ACL2-BUILT-INS
    

    General form:
    (putprop symbol key value world-alist)
    

    See community book books/misc/getprop.lisp for an example that illustrates the use of ACL2 utilities getprop and putprop to take advantage of under-the-hood Lisp (hashed) property lists.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/P_bang_.html0000664002132200015000000000351512222333521016276 0ustar kaufmannacl2 P_bang_.html -- ACL2 Version 6.3

    P!

    to pop up (at least) one level of ACL2's command loop
    Major Section:  MISCELLANEOUS
    

    Logically speaking, (p!) = nil. If you are already at the top level of the ACL2 command loop, rather than being in a subsidiary call of ld, then the keyword then a call of (p!) returns nil and has no other effect.

    Otherwise, (p!) is evaluated inside a call of ld that was made inside ACL2's command loop. In that case, the current computation is aborted and treating as causing an error, and control returns to the superior call of ld.

    Here is a more detailed description of the effect of (p!) when not at the top level of the ACL2 command loop. The current call of LD is treated as though ld-error-action is :RETURN! (the default) and hence immediately returns control to the superior call of ld. If all calls of ld were made with the default ld-error-action of :RETURN!, then all superior calls of ld will then complete until you are back at top level of the ACL2 loop. For more information, see ld-error-action.

    If you are at an ACL2 prompt (as opposed to a raw Lisp break), then you may type :p! in place of (p!); see keyword-commands.




    acl2-sources/doc/HTML/Pages_Written_Especially_for_the_Tours.html0000664002132200015000000004451212222333526024607 0ustar kaufmannacl2 Pages_Written_Especially_for_the_Tours.html -- ACL2 Version 6.3

    Pages Written Especially for the Tours

    Pages Written Especially for the Tours
    Major Section:  ACL2 Documentation
    

    The ACL2 Home Page is generated from ACL2's online documentation strings. (How else could we achieve the total integration of ACL2's online documentation with the home page?) This page is just an artifact of the structure of our documentation strings: each string must belong to a ``major section'' of the documentation database. This page is not structured to be used by a person browsing via the Web. It contains, in an arbitrary order, the pages written specificially for the Web user.

    Furthermore, browsing the pages below via the ACL2 :DOC command or via TexInfo is often unsatisfying because those browsers do not support gif files and the notion of going ``back'' to a node just visited. If you wish to look at the pages below, we strongly recommend that you do so via a HTML-based Web browser. Indeed, you should simply visit ACL2's Home Page and take one of the Tours.

    Some Related Topics

    Generally, the topics listed above will not be of use to the ACL2 user.




    acl2-sources/doc/HTML/Perhaps.html0000664002132200015000000000063412222333526016357 0ustar kaufmannacl2 Perhaps.html -- ACL2 Version 6.3

    Perhaps

    The theorem prover's proof is printed in real time. At the time it prints ``Perhaps'' it does not know the proof will succeed.




    acl2-sources/doc/HTML/Popping_out_of_an_Inductive_Proof.html0000664002132200015000000000111212222333526023571 0ustar kaufmannacl2 Popping_out_of_an_Inductive_Proof.html -- ACL2 Version 6.3

    Popping out of an Inductive Proof

    Recall that our induction scheme (click here to revisit it) had two cases, the induction step (Subgoal *1/2) and the base case (Subgoal *1/1). Both have been proved!




    acl2-sources/doc/HTML/Proving_Theorems_about_Models.html0000664002132200015000000000310712222333526022742 0ustar kaufmannacl2 Proving_Theorems_about_Models.html -- ACL2 Version 6.3

    Proving Theorems about Models

    But ACL2 is a logic. We can prove theorems about the model.

    Theorem.  MC 'mult is a multiplier
    (implies (and (natp x)
                  (natp y))
             (equal (lookup 'z (mc (s 'mult x y) (mclk x)))
                    (* x y))).
    

    This theorem says that a certain program running on the mc machine will correctly multiply any two natural numbers.

    It is a statement about an infinite number of test cases!

    We know it is true about the model because we proved it.

    Of course, models of actual machines usually only accept a finite number of different inputs. For example, engineers at Advanced Micro Devices (AMD), Centaur, and IBM have ACL2 models of floating point units that operate on double precision IEEE floating point numbers. These are finite models. But the size of their inputs is sufficiently large that they are verified by the same mathematical methods used to prove theorems about infinite state systems like our little mc.




    acl2-sources/doc/HTML/Q.html0000664002132200015000000000223612222333522015151 0ustar kaufmannacl2 Q.html -- ACL2 Version 6.3

    Q

    quit ACL2 (type :q) -- reenter with (lp)
    Major Section:  OTHER
    

    Example:
    ACL2 !>:Q
    

    The keyword command :q typed at the top-level of the ACL2 loop will terminate the loop and return control to the Common Lisp top-level (or, more precisely, to whatever program invoked lp). To reenter the ACL2 loop, execute (acl2::lp) in Common Lisp. You will be in the same state as you were when you exited with :q, unless during your stay in Common Lisp you messed the data structures representating the ACL2 state (including files, property lists, and single-threaded objects).

    Unlike all other keyword commands, typing :q is not equivalent to invoking the function q. There is no function q.




    acl2-sources/doc/HTML/QUANTIFIER-TUTORIAL.html0000664002132200015000000007166112222333517017675 0ustar kaufmannacl2 QUANTIFIER-TUTORIAL.html -- ACL2 Version 6.3

    QUANTIFIER-TUTORIAL

    A Beginner's Guide to Reasoning about Quantification in ACL2
    Major Section:  DEFUN-SK
    

    The initial version of this tutorial was written by Sandip Ray. Additions and revisions are welcome. Sandip has said:

    ``This is a collection of notes that I wrote to remind myself of how to reason about quantifiers when I just started. Most users after they have gotten the hang of quantifiers probably will not need this and will be able to use their intuitions to guide them in the process. But since many ACL2 users are not used to quantification, I am hoping that this set of notes might help them to think clearly while reasoning about quantifiers in ACL2.''

    Many ACL2 papers start with the sentence ``ACL2 is a quantifier-free first-order logic of recursive functions.'' It is true that the syntax of ACL2 is quantifier-free; every formula is assumed to be universally quantified over all free variables in the formula. But the logic in fact does afford arbitrary first-order quantification. This is obtained in ACL2 using a construct called defun-sk. See defun-sk.

    Many ACL2 users do not think in terms of quantifiers. The focus is almost always on defining recursive functions and reasoning about them using induction. That is entirely justified, in fact, since proving theorems about recursive functions by induction plays to the strengths of the theorem prover. Nevertheless there are situations where it is reasonable and often useful to think in terms of quantifiers. However, reasoning about quantifiers requires that you get into the mindset of thinking about theorems in terms of quantification. This note is about how to do this effectively given ACL2's implementation of quantification. This does not discuss defun-sk in detail, but merely shows some examples. A detailed explanation of the implementation is in the ACL2 documentation (see defun-sk); also see conservativity-of-defchoose.

    [Note: Quantifiers can be used for some pretty cool things in ACL2. Perhaps the most interesting example is the way of using quantifiers to introduce arbitrary tail-recursive equations; see the paper ``Partial Functions in ACL2'' by Panagiotis Manolios and J Strother Moore. This note does not address applications of quantifiers, but merely how you would reason about them once you think you want to use them.]

    Assume that you have some function P. I have just left P as a unary function stub below, since I do not care about what P is.

    (defstub P (*) => *)
    
    Now suppose you want to specify the concept that ``there exists some x such that (P x) holds''. ACL2 allows you to write that directly using quantifiers.
    (defun-sk exists-P () (exists x (P x)))
    
    If you submit the above form in ACL2 you will see that the theorem prover specifies two functions exists-p and exists-p-witness, and exports the following constraints:
    1.  (defun exists-P () (P (exists-P-witness)))
    2.  (defthm exists-P-suff (implies (p x) (exists-p)))
    
    Here exists-P-witness is a new function symbol in the current ACL2 theory. What do the constraints above say? Notice the constraint exists-p-suff. It says that if you can provide any x such that (P x) holds, then you know that exists-p holds. Think of the other constraint (definition of exists-p) as going the other way. That is, it says that if exists-p holds, then there is some x, call it (exists-p-witness), for which P holds. Notice that nothing else is known about exists-p-witness than the two constraints above.

    [Note: exists-p-witness above is actually defined in ACL2 using a special form called defchoose. See defchoose. This note does not talk about defchoose. So far as this note is concerned, think of exists-p-witness as a new function symbol that has been generated somehow in ACL2, about which nothing other than the two facts above is known.]

    Similarly, you can talk about the concept that ``for all x (P x) holds.'' This can be specified in ACL2 by the form:

    (defun-sk forall-P () (forall x (P x)))
    
    This produces the following two constraints:
    1.  (defun forall-P () (P (forall-p-witness)))
    2.  (defthm forall-p-necc (implies (not (P x)) (not (forall-p))))
    
    To understand these, think of for-all-p-witness as producing some x which does not satisfy P, if such a thing exists. The constraint forall-p-necc merely says that if forall-p holds then P is satisfied for every x. (To see this more clearly, just think of the contrapositive of the formula shown.) The other constraint (definition of forall-p) implies that if forall-p does not hold then there is some x, call it (forall-p-witness), which does not satisfy P. To see this, just consider the following formula which is immediately derivable from the definition.
    (implies (not (forall-p)) (not (P (forall-witness))))
    
    The description above suggests that to reason about quantifiers, the following Rules of Thumb, familiar to most any student of logic, are useful.

    RT1: To prove (exists-p), construct some object A such that P holds for A and then use exists-P-suff.

    RT2: If you assume exists-P in your hypothesis, use the definition of exists-p to know that P holds for exists-p-witness. To use this to prove a theorem, you must be able to derive the theorem based on the hypothesis that P holds for something, whatever the something is.

    RT3: To prove forall-P, prove the theorem (P x) (that is, that P holds for an arbitrary x), and then simply instantiate the definition of forall-p, that is, show that P holds for the witness.

    RT4: If you assume forall-p in the hypothesis of the theorem, see how you can prove your conclusion if indeed you were given (P x) as a theorem. Possibly for the conclusion to hold, you needed that P holds for some specific set of x values. Then use the theorem forall-p-necc by instantiating it for the specific x values you care about.

    Perhaps the above is too terse. In the remainder of the note, we will consider several examples of how this is done to prove theorems in ACL2 that involve quantified notions.

    Let us consider two trivial theorems. Assume that for some unary function r, you have proved (r x) as a theorem. Let us see how you can prove that (1) there exists some x such that (r x) holds, and (2) for all x (r x) holds.

    We first model these things using defun-sk. Below, r is simply some function for which (r x) is a theorem.

    (encapsulate
     (((r *) => *))
     (local (defun r (x) (declare (ignore x)) t))
     (defthm r-holds (r x)))
    
    (defun-sk exists-r () (exists x (r x)))
    (defun-sk forall-r () (forall x (r x)))
    
    ACL2 does not have too much reasoning support for quantifiers. So in most cases, one would need :use hints to reason about quantifiers. In order to apply :use hints, it is preferable to keep the function definitions and theorems disabled.
    (in-theory (disable exists-r exists-r-suff forall-r forall-r-necc))
    
    Let us now prove that there is some x such that (r x) holds. Since we want to prove exists-r, we must use exists-r-suff by RT1. We do not need to construct any instance here since r holds for all x by the theorem above.
    (defthm exists-r-holds
      (exists-r)
      :hints (("Goal" :use ((:instance exists-r-suff)))))
    
    Let us now prove the theorem that for all x, (r x) holds. By RT3, we must be able to prove it by definition of forall-r.
    (defthm forall-r-holds
      (forall-r)
      :hints (("Goal" :use ((:instance (:definition forall-r))))))
    
    [Note: Probably no ACL2 user in his or her right mind would prove the theorems exists-r-holds and forall-r-holds above. The theorems shown are only for demonstration purposes.]

    For the remainder of this note we will assume that we have two stubbed out unary functions M and N, and we will look at proving some quantified properties of these functions.

    (defstub M (*) => *)
    (defstub N (*) => *)
    
    Let us now define the predicates all-M, all-N, ex-M, and ex-N specifying the various quantifications.
    (defun-sk all-M () (forall x (M x)))
    (defun-sk all-N () (forall x (N x)))
    (defun-sk some-M () (exists x (M x)))
    (defun-sk some-N () (exists x (N x)))
    
    (in-theory (disable all-M all-N all-M-necc all-N-necc))
    (in-theory (disable some-M some-N some-M-suff some-N-suff))
    
    Let us prove the classic distributive properties of quantification: the distributivity of universal quantification over conjunction, and the distributivity of existential quantification over disjunction. We can state these properties informally in ``pseudo ACL2'' notation as follows:
    1.  (exists x: (M x)) or (exists x: (N x)) <=> (exists x: (M x) or (N x))
    2.  (forall x: (M x)) and (forall: x (N x)) <=> (forall x: (M x) and (N x))
    
    To make these notions formal we of course need to define the formulas at the right-hand sides of 1 and 2. So we define some-MN and all-MN to capture these concepts.
    (defun-sk some-MN () (exists x (or (M x) (N x))))
    (defun-sk all-MN () (forall x (and (M x) (N x))))
    
    (in-theory (disable all-MN all-MN-necc some-MN some-MN-suff))
    
    First consider proving property 1. The formal statement of this theorem would be: (iff (some-MN) (or (some-M) (some-N))).

    How do we prove this theorem? Looking at RT1-RT4 above, note that they suggest how one should reason about quantification when one has an ``implication''. But here we have an ``equivalence''. This suggests another rule of thumb.

    RT5: Whenever possible, prove an equivalence involving quantifiers by proving two implications.

    Let us apply RT5 to prove the theorems above. So we will first prove: (implies (some-MN) (or (some-M) (some-N)))

    How can we prove this? This involves assuming a quantified predicate (some-MN), so we must use RT2 and apply the definition of some-MN. Since the conclusion involves a disjunction of two quantified predicates, by RT1 we must be able to construct two objects A and B such that either M holds for A or N holds for B, so that we can then invoke some-M-suff and some-N-suff to prove the conclusion. But now notice that if some-MN is true, then there is already an object, in fact some-MN-witness, such that either M holds for it, or N holds for it. And we know this is the case from the definition of some-MN! So we will simply prove the theorem instantiating some-M-suff and some-N-suff with this witness. The conclusion is that the following event will go through with ACL2.

    (defthm le1
      (implies (some-MN)
               (or (some-M) (some-N)))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition some-MN))
                      (:instance some-M-suff
                                 (x (some-MN-witness)))
                      (:instance some-N-suff
                                 (x (some-MN-witness)))))))
    
    This also suggests the following rule of thumb:

    RT6: If a conjecture involves assuming an existentially quantified predicate in the hypothesis from which you are trying to prove an existentially quantified predicate, use the witness of the existential quantification in the hypothesis to construct the witness for the existential quantification in the conclusion.

    Let us now try to prove the converse of le1, that is: (implies (or (some-M) (some-N)) (some-MN))

    Since the hypothesis is a disjunction, we will just prove each case individually instead of proving the theorem by a :cases hint. So we prove the following two lemmas.

    (defthm le2
      (implies (some-M) (some-MN))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition some-M))
                      (:instance some-MN-suff
                                 (x (some-M-witness)))))))
    
    (defthm le3
      (implies (some-N) (some-MN))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition some-N))
                      (:instance some-MN-suff
                                 (x (some-N-witness)))))))
    
    Note that the hints above are simply applications of RT6 as in le1. With these lemmas, of course the main theorem is trivial.
    (defthmd |some disjunction|
      (iff (some-MN) (or (some-M) (some-N)))
      :hints (("Goal"
                :use ((:instance le1)
                      (:instance le2)
                      (:instance le3)))))
    
    Let us now prove the distributivity of universal quantification over conjunction, that is, the formula: (iff (all-MN) (and (all-M) (all-N)))

    Applying RT5, we will again decompose this into two implications. So consider first the one-way implication: (implies (and (all-M) (all-N)) (all-MN)).

    Here we get to assume all-M and all-N. Thus by RT4 we can use all-M-necc and all-N-necc to think as if we are given the formulas (M x) and (N x) as theorems. The conclusion here is also a universal quantification, namely we have to prove all-MN. Then RT3 tells us to proceed as follows. Take any object y. Try to find an instantiation z of the hypothesis that implies (and (M y) (N y)). Then instantiate y with all-MN-witness. Note that the hypothesis lets us assume (M x) and (N x) to be theorems. Thus to justify we need to instantiate x with y, and in this case, therefore, with all-MN-witness. To make the long story short, the following event goes through with ACL2:

    (defthm lf1
       (implies (and (all-M) (all-N))
                (all-MN))
        :rule-classes nil
        :hints (("Goal"
                  :use ((:instance (:definition all-MN))
                        (:instance all-M-necc (x (all-MN-witness)))
                        (:instance all-N-necc (x (all-MN-witness)))))))
    
    This suggests the following rule of thumb which is a dual of RT6:

    RT7: If a conjecture assumes some universally quantified predicate in the hypothesis and its conclusion asserts a universallly quantified predicate, then instantiate the ``necessary condition'' (forall-mn-necc) of the hypothesis with the witness of the conclusion to prove the conjecture.

    Applying RT7 now we can easily prove the other theorems that we need to show that universal quantification distributes over conjunction. Let us just go through this motion in ACL2.

    (defthm lf2
      (implies (all-MN)
               (all-M))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition all-M))
                      (:instance all-MN-necc
                                 (x (all-M-witness)))))))
    
    (defthm lf3
      (implies (all-MN)
               (all-N))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition all-N))
                      (:instance all-MN-necc
                                 (x (all-N-witness)))))))
    
    (defthmd |all conjunction|
      (iff (all-MN)
           (and (all-M) (all-N)))
     :hints (("Goal" :use ((:instance lf1)
                           (:instance lf2)
                           (:instance lf3)))))
    
    The rules of thumb for universal and existential quantification should make you realize the duality of their use. Every reasoning method about universal quantification can be cast as a way of reasoning about existential quantification, and vice versa. Whether you reason using universal and existential quantifiers depends on what is natural in a particular context. But just for the sake of completeness let us prove the duality of universal and existential quantifiers. So what we want to prove is the following:
    3.  (forall x (not (M x))) = (not (exists x (M x)))
    
    We first formalize the notion of (forall x (not (M x))) as a quantification.
    (defun-sk none-M () (forall x (not (M x))))
    (in-theory (disable none-M none-M-necc))
    
    So we now want to prove: (equal (none-M) (not (some-M))).

    As before, we should prove this as a pair of implications. So let us prove first: (implies (none-M) (not (some-M))).

    This may seem to assert an existential quantification in the conclusion, but rather, it asserts the negation of an existential quantification. We are now trying to prove that something does not exist. How do we do that? We can show that nothing satisfies M by just showing that (some-M-witness) does not satisfy M. This suggests the following rule of thumb:

    RT8: When you encounter the negation of an existential quantification think in terms of a universal quantification, and vice-versa.

    Ok, so now applying RT8 and RT3 you should be trying to apply the definition of some-M. The hypothesis is just a pure (non-negated) universal quantification so you should apply RT4. A blind application lets us prove the theorem as below.

    (defthm nl1
      (implies (none-M) (not (some-M)))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition some-M))
                      (:instance none-M-necc (x (some-M-witness)))))))
    
    How about the converse implication? I have deliberately written it as (implies (not (none-M)) (some-M)) instead of switching the left-hand and right-hand sides of nl1, which would have been equivalent. Again, RH8 tells us how to reason about it, in this case using RH2, and we succeed.
    (defthm nl2
      (implies (not (none-M)) (some-M))
      :rule-classes nil
      :hints (("Goal"
                :use ((:instance (:definition none-M))
                      (:instance some-M-suff (x (none-M-witness)))))))
    
    So finally we just go through the motions of proving the equality.
    (defthmd |forall not = not exists|
      (equal (none-M) (not (some-M)))
      :hints (("Goal"
                :use ((:instance nl1)
                      (:instance nl2)))))
    
    Let us now see if we can prove a slightly more advanced theorem which can be stated informally as: If there is a natural number x which satisfies M, then there is a least natural number y that satisfies M.

    [Note: Any time I have had to reason about existential quantification I have had to do this particular style of reasoning and state that if there is an object satisfying a predicate, then there is also a ``minimal'' object satisfying the predicate.]

    Let us formalize this concept. We first define the concept of existence of a natural number satisfying x.

    (defun-sk some-nat-M () (exists x (and (natp x) (M x))))
    (in-theory (disable some-nat-M some-nat-M-suff))
    
    We now talk about what it means to say that x is the least number satisfying M.
    (defun-sk none-below (y)
      (forall r (implies (and (natp r) (< r y)) (not (M r))))))
    (in-theory (disable none-below none-below-necc))
    
    (defun-sk min-M () (exists y (and (M y) (natp y) (none-below y))))
    (in-theory (disable min-M min-M-suff))
    
    The predicate none-below says that no natural number less than y satisfies M. The predicate min-M says that there is some natural number y satisfying M such that none-below holds for y.

    So the formula we want to prove is: (implies (some-nat-M) (min-M)).

    Since the formula requires that we prove an existential quantification, RT1 tells us to construct some object satisfying the predicate over which we are quantifying. We should then be able to instantiate min-M-suff with this object. That predicate says that the object must be the least natural number that satisfies M. Since such an object is uniquely computable if we know that there exists some natural number satisfying M, let us just write a recursive function to compute it. This function is least-M below.

    (defun least-M-aux (i bound)
      (declare (xargs :measure (nfix (- (1+ bound) i))))
      (cond ((or (not (natp i))
                 (not (natp bound))
                 (> i bound))
             0)
           ((M i) i)
           (t (least-M-aux (+ i 1) bound))))
    
    (defun least-M (bound) (least-M-aux 0 bound))
    
    Let us now reason about this function as one does typically. So we prove that this object is indeed the least natural number that satisfies M, assuming that bound is a natural number that satisfies M.
    (defthm least-aux-produces-an-M
      (implies (and (natp i)
                    (natp bound)
                    (<= i bound)
                    (M bound))
               (M (least-M-aux i bound))))
    
    (defthm least-<=bound
      (implies (<= 0 bound)
               (<= (least-M-aux i bound) bound)))
    
    (defthm least-aux-produces-least
      (implies (and (natp i)
                    (natp j)
                    (natp bound)
                    (<= i j)
                    (<= j bound)
                    (M j))
                (<= (least-M-aux i bound) j)))
    
    (defthm least-aux-produces-natp
      (natp (least-M-aux i bound)))
    
    (defthmd least-is-minimal-satisfying-m
      (implies (and (natp bound)
                    (natp i)
                     (< i (least-M bound)))
               (not (M i)))
      :hints (("Goal"
                :in-theory (disable least-aux-produces-least least-<=bound)
                :use ((:instance least-<=bound
                                 (i 0))
                      (:instance least-aux-produces-least
                                 (i 0)
                                 (j i))))))
    
    (defthm least-has-m
      (implies (and (natp bound)
                    (m bound))
               (M (least-M bound))))
    
    (defthm least-is-natp
      (natp (least-M bound)))
    
    So we have done that, and hopefully this is all that we need about least-M. So we disable everything.
    (in-theory (disable least-M natp))
    
    Now of course we note that the statement of the conjecture we are interested in has two quantifiers, an inner forall (from none-below) and an outer exists (from min-M). Since ACL2 is not very good with quantification, we hold its hands to reason with the quantifier part. So we will first prove something about the forall and then use it to prove what we need about the exists.

    RT9: When you face nested quantifiers, reason about each nesting separately.

    So what do we want to prove about the inner quantifier? Looking carefully at the definition of none-below we see that it is saying that for all natural numbers r < y, (M r) does not hold. Well, how would we want to use this fact when we want to prove our final theorem? We expect that we will instantiate min-M-suff with the object (least-M bound) where we know (via the outermost existential quantifier) that M holds for bound, and we will then want to show that none-below holds for (least-M bound). So let us prove that for any natural number (call it bound), none-below holds for (least-M bound). For the final theorem we only need it for natural numbers satisfying M, but note that from the lemma least-is-minimal-satisfying-m we really do not need that bound satisfies M.

    So we are now proving: (implies (natp bound) (none-below (least-M bound))).

    Well since this is a standard case of proving a universally quantified predicate, we just apply RT3. We have proved that for all naturals i < (least-M bound), i does not satisfy M (lemma least-is-minimal-satisfying-M), so we merely need the instantiation of that lemma with none-below-witness of the thing we are trying to prove, that is, (least-M bound). The theorem below thus goes through.

    (defthm least-is-minimal
      (implies (natp bound)
               (none-below (least-M bound)))
      :hints (("Goal"
                :use ((:instance (:definition none-below)
                                 (y (least-M bound)))
                      (:instance least-is-minimal-satisfying-m
                                 (i (none-below-witness (least-M bound))))))))
    
    Finally we are in the outermost existential quantifier, and are in the process of applying min-M-suff. What object should we instantiate it with? We must instantiate it with (least-M bound) where bound is an object which must satisfy M and is a natural. We have such an object, namely (some-nat-M-witness) which we know have all these qualities given the hypothesis. So the proof now is just RT1 and RT2.
    (defthm |minimal exists|
      (implies (some-nat-M) (min-M))
      :hints (("Goal"
                :use ((:instance min-M-suff
                                 (y (least-M (some-nat-M-witness))))
                      (:instance (:definition some-nat-M))))))
    

    If you are comfortable with the reasoning above, then you are comfortable with quantifiers and probably will not need these notes any more. In my opinion, the best way of dealing with ACL2 is to ask yourself why you think something is a theorem, and the rules of thumb above are simply guides to the questions that you need to ask when you are dealing with quantification.

    Here are a couple of simple exercises for you to test if you understand the reasoning process.

    Exercise 1. Formalize and prove the following theorem. Suppose there exists x such that (R x) and suppose that all x satisfy (P x). Then prove that there exists x such that (P x) & (R x). (See http://www.cs.utexas.edu/users/moore/acl2/contrib/quantifier-exercise-1-solution.html for a solution.)

    Exercise 2. Recall the example just before the preceding exercise, where we showed that if there exists a natural number x satisfying M then there is another natural number y such that y satisfies M and for every natural number z < y, z does not. What would happen if we remove the restriction of x, y, and z being naturals? Of course, we will not talk about < any more, but suppose you use the total order on all ACL2 objects (from community book "books/misc/total-order"). More concretely, consider the definition of some-M above. Let us now define two other functions:

    (include-book "misc/total-order" :dir :system)
    
    (defun-sk none-below-2 (y)
      (forall r (implies (<< r y) (not (M r)))))
    
    (defun-sk min-M2 () (exists y (and (M y) (none-below-2 y))))
    
    The question is whether (implies (some-M) (min-M2)) is a theorem. Can you prove it? Can you disprove it?




    acl2-sources/doc/HTML/QUANTIFIERS-USING-DEFUN-SK-EXTENDED.html0000664002132200015000000000510612222333517022001 0ustar kaufmannacl2 QUANTIFIERS-USING-DEFUN-SK-EXTENDED.html -- ACL2 Version 6.3

    QUANTIFIERS-USING-DEFUN-SK-EXTENDED

    quantification example with details
    Major Section:  QUANTIFIERS
    

    See quantifiers-using-defun-sk for the context of this example.

    (in-package "ACL2")
    
    ; We prove that if every member A of each of two lists satisfies the
    ; predicate (P A), then this holds of their append; and, conversely.
    
    ; Here is a solution using explicit quantification.
    
    (defstub p (x) t)
    
    (defun-sk forall-p (x)
      (forall a (implies (member a x)
                         (p a))))
    
    ; The defun-sk above introduces the following axioms.  The idea is that
    ; (FORALL-P-WITNESS X) picks a counterexample to (forall-p x) if there is one.
    
    ;   (DEFUN FORALL-P (X)
    ;     (LET ((A (FORALL-P-WITNESS X)))
    ;          (IMPLIES (MEMBER A X) (P A))))
    ;
    ;   (DEFTHM FORALL-P-NECC
    ;     (IMPLIES (NOT (IMPLIES (MEMBER A X) (P A)))
    ;              (NOT (FORALL-P X)))
    ;     :HINTS (("Goal" :USE FORALL-P-WITNESS)))
    
    ; The following lemma seems critical.
    
    (defthm member-append
      (iff (member a (append x1 x2))
           (or (member a x1) (member a x2))))
    
    ; The proof of forall-p-append seems to go out to lunch, so we break into
    ; directions as shown below.
    
    (defthm forall-p-append-forward
      (implies (forall-p (append x1 x2))
               (and (forall-p x1) (forall-p x2)))
      :hints (("Goal" ; ``should'' disable forall-p-necc, but no need
               :use
               ((:instance forall-p-necc
                           (x (append x1 x2))
                           (a (forall-p-witness x1)))
                (:instance forall-p-necc
                           (x (append x1 x2))
                           (a (forall-p-witness x2)))))))
    
    (defthm forall-p-append-reverse
      (implies (and (forall-p x1) (forall-p x2))
               (forall-p (append x1 x2)))
      :hints (("Goal"
               :use
               ((:instance forall-p-necc
                           (x x1)
                           (a (forall-p-witness (append x1 x2))))
                (:instance forall-p-necc
                           (x x2)
                           (a (forall-p-witness (append x1 x2))))))))
    
    (defthm forall-p-append
      (equal (forall-p (append x1 x2))
             (and (forall-p x1) (forall-p x2)))
      :hints (("Goal" :use (forall-p-append-forward
                            forall-p-append-reverse))))
    
    




    acl2-sources/doc/HTML/QUANTIFIERS-USING-DEFUN-SK.html0000664002132200015000000000372312222333517020606 0ustar kaufmannacl2 QUANTIFIERS-USING-DEFUN-SK.html -- ACL2 Version 6.3

    QUANTIFIERS-USING-DEFUN-SK

    quantification example
    Major Section:  QUANTIFIERS
    

    See quantifiers for the context of this example. It should be compared to a corresponding example in which a simpler proof is attained by using recursion in place of explicit quantification; see quantifiers-using-recursion.

    (in-package "ACL2")
    
    ; We prove that if every member A of each of two lists satisfies the
    ; predicate (P A), then this holds of their append; and, conversely.
    
    ; Here is a solution using explicit quantification.
    
    (defstub p (x) t)
    
    (defun-sk forall-p (x)
      (forall a (implies (member a x)
                         (p a))))
    
    (defthm member-append
      (iff (member a (append x1 x2))
           (or (member a x1) (member a x2))))
    
    (defthm forall-p-append
      (equal (forall-p (append x1 x2))
             (and (forall-p x1) (forall-p x2)))
      :hints (("Goal" ; ``should'' disable forall-p-necc, but no need
               :use
               ((:instance forall-p-necc
                           (x (append x1 x2))
                           (a (forall-p-witness x1)))
                (:instance forall-p-necc
                           (x (append x1 x2))
                           (a (forall-p-witness x2)))
                (:instance forall-p-necc
                           (x x1)
                           (a (forall-p-witness (append x1 x2))))
                (:instance forall-p-necc
                           (x x2)
                           (a (forall-p-witness (append x1 x2))))))))
    

    Also see quantifiers-using-defun-sk-extended for an elaboration on this example.




    acl2-sources/doc/HTML/QUANTIFIERS-USING-RECURSION.html0000664002132200015000000000223112222333517020774 0ustar kaufmannacl2 QUANTIFIERS-USING-RECURSION.html -- ACL2 Version 6.3

    QUANTIFIERS-USING-RECURSION

    recursion for implementing quantification
    Major Section:  QUANTIFIERS
    

    The following example illustrates the use of recursion as a means of avoiding proof difficulties that can arise from the use of explicit quantification (via defun-sk). See quantifiers for more about the context of this example.

    (in-package "ACL2")
    
    ; We prove that if every member A of each of two lists satisfies the
    ; predicate (P A), then this holds of their append; and, conversely.
    
    ; Here is a solution using recursively-defined functions.
    
    (defstub p (x) t)
    
    (defun all-p (x)
      (if (atom x)
          t
        (and (p (car x))
             (all-p (cdr x)))))
    
    (defthm all-p-append
      (equal (all-p (append x1 x2))
             (and (all-p x1) (all-p x2))))
    




    acl2-sources/doc/HTML/QUANTIFIERS.html0000664002132200015000000000566012222333517016553 0ustar kaufmannacl2 QUANTIFIERS.html -- ACL2 Version 6.3

    QUANTIFIERS

    issues about quantification in ACL2
    Major Section:  DEFUN-SK
    

    ACL2 supports first-order quantifiers exists and forall by way of the defun-sk event. However, proof support for quantification is quite limited. Therefore, you may prefer using recursion in place of defun-sk when possible (following common ACL2 practice).

    Some Related Topics

    For example, the notion ``every member of x has property p'' can be defined either with recursion or explicit quantification, but proofs may be simpler when recursion is used. We illustrate this point with two proofs of the same informal claim, one of which uses recursion which the other uses explicit quantification. Notice that with recursion, the proof goes through fully automatically; but this is far from true with explicit quantification (especially notable is the ugly hint).

    The informal claim for our examples is: If every member a of each of two lists satisfies the predicate (p a), then this holds of their append; and, conversely.

    See quantifiers-using-recursion for a solution to this example using recursion.

    See quantifiers-using-defun-sk for a solution to this example using defun-sk. Also See quantifiers-using-defun-sk-extended for an elaboration on that solution.

    But perhaps first, see defun-sk for an ACL2 utility to introduce first-order quantification in a direct way. Examples of the use of defun-sk are also available: see defun-sk-example and see Tutorial4-Defun-Sk-Example for basic examples, and see quantifier-tutorial for a more complete, careful beginner's introduction that takes you through typical kinds of quantifier-based reasoning in ACL2.




    acl2-sources/doc/HTML/QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP.html0000664002132200015000000000423712222333522023255 0ustar kaufmannacl2 QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP.html -- ACL2 Version 6.3

    QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP

    (advanced topic) controlling a heuristic in the prover's clausifier
    Major Section:  OTHER
    

    This topic is probably only of interest to those who are undertaking particularly complex proof developments.

    The ACL2 prover's handling of propositional logic uses a heuristic that we believe might cause the prover to slow down significantly or even to trigger a stack overflow. However, we believe these negative effects are only felt on very large formulas -- formulas that are likely to cause similar degradation of many other parts of ACL2.

    The following two events turn off that heuristic (by eliminating calls to source function quick-and-dirty-subsumption-replacement-step, after which this documentation topic is named).

    (defun quick-and-dirty-srs-off (cl1 ac)
      (declare (ignore cl1 ac)
               (xargs :mode :logic :guard t))
      nil)
    
    (defattach quick-and-dirty-srs quick-and-dirty-srs-off)
    

    However, if you feel the need to try this out, please remember that the proof is likely to fail anyway since other parts of ACL2 will probably be stressed. A more basic problem with your proof strategy may exist: the formulas are getting extraordinarily large. A common approach for avoiding such problem include disabling functions (see in-theory). Other less common approaches might help if you are sufficiently desperate, such as defining your own IF function to use in place of the built-in IF.

    If you do find an example for which the above two events provide significant benefit, we (the ACL2 implementors) would be interested in hearing about it.

    To turn the heuristic back on:

    (defattach quick-and-dirty-srs quick-and-dirty-srs-builtin)
    




    acl2-sources/doc/HTML/QUIT.html0000664002132200015000000000065312222333522015534 0ustar kaufmannacl2 QUIT.html -- ACL2 Version 6.3

    QUIT

    quit entirely out of Lisp
    Major Section:  OTHER
    

    Same as good-bye.




    acl2-sources/doc/HTML/QUOTE.html0000664002132200015000000000073512222333524015652 0ustar kaufmannacl2 QUOTE.html -- ACL2 Version 6.3

    QUOTE

    create a constant
    Major Section:  ACL2-BUILT-INS
    

    The form (quote x) evaluates to x. See any Common Lisp documentation.




    acl2-sources/doc/HTML/R-EQLABLE-ALISTP.html0000664002132200015000000000160412222333524017207 0ustar kaufmannacl2 R-EQLABLE-ALISTP.html -- ACL2 Version 6.3

    R-EQLABLE-ALISTP

    recognizer for a true list of pairs whose cdrs are suitable for eql
    Major Section:  ACL2-BUILT-INS
    

    The predicate r-eqlable-alistp tests whether its argument is a true-listp of consp objects whose cdrs all satisfy eqlablep.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/R-SYMBOL-ALISTP.html0000664002132200015000000000132612222333524017150 0ustar kaufmannacl2 R-SYMBOL-ALISTP.html -- ACL2 Version 6.3

    R-SYMBOL-ALISTP

    recognizer for association lists with symbols as values
    Major Section:  ACL2-BUILT-INS
    

    (R-symbol-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where val is a symbolp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/RANDOM$.html0000664002132200015000000000277412222333524016006 0ustar kaufmannacl2 RANDOM$.html -- ACL2 Version 6.3

    RANDOM$

    obtain a random value
    Major Section:  ACL2-BUILT-INS
    

    Example:
    (random$ 10 state) ==> (mv 7 <state>)
    

    (Random$ limit state), where limit is a positive integer, returns a random non-negative integer together with a new state. Logically, it simply returns the first element of a list that is a field of the ACL2 state, called the acl2-oracle, together with the new state resulting from removing that element from that list. (Except, if that element is not in range as specified above, then 0 is returned.) However, random$ actually invokes a Common Lisp function to choose the integer returned. Quoting from the Common Lisp HyperSpec(TM), http://www.lispworks.com/documentation/HyperSpec/Front: ``An approximately uniform choice distribution is used... each of the possible results occurs with (approximate) probability 1/limit.''

    Consider enabling rules natp-random$ and random$-linear if you want to reason about random$.




    acl2-sources/doc/HTML/RASSOC-EQ.html0000664002132200015000000000062712222333524016252 0ustar kaufmannacl2 RASSOC-EQ.html -- ACL2 Version 6.3

    RASSOC-EQ

    See rassoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/RASSOC-EQUAL.html0000664002132200015000000000063512222333524016613 0ustar kaufmannacl2 RASSOC-EQUAL.html -- ACL2 Version 6.3

    RASSOC-EQUAL

    See rassoc.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/RASSOC.html0000664002132200015000000000526212222333524015747 0ustar kaufmannacl2 RASSOC.html -- ACL2 Version 6.3

    RASSOC

    look up value in association list
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (rassoc x alist)
    (rassoc x alist :test 'eql)   ; same as above (eql as equality test)
    (rassoc x alist :test 'eq)    ; same, but eq is equality test
    (rassoc x alist :test 'equal) ; same, but equal is equality test
    

    (Rassoc x alist) is the first member of alist whose cdr is x, or nil if no such member exists. (rassoc x alist) is thus similar to (assoc x alist), the difference being that it looks for the first pair in the given alist whose cdr, rather than car, is eql to x. See assoc. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with the cdrs of successive elements of lst.

    The guard for a call of rassoc depends on the test. In all cases, the second argument must satisfy alistp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy r-eqlable-alistp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy r-symbol-alistp.

    See equality-variants for a discussion of the relation between rassoc and its variants:

    (rassoc-eq x lst) is equivalent to (rassoc x lst :test 'eq);

    (rassoc-equal x lst) is equivalent to (rassoc x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function rassoc-equal.

    Rassoc is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/RATIONAL-LISTP.html0000664002132200015000000000114512222333524017053 0ustar kaufmannacl2 RATIONAL-LISTP.html -- ACL2 Version 6.3

    RATIONAL-LISTP

    recognizer for a true list of rational numbers
    Major Section:  ACL2-BUILT-INS
    

    The predicate rational-listp tests whether its argument is a true list of rational numbers.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/RATIONALP.html0000664002132200015000000000100012222333524016270 0ustar kaufmannacl2 RATIONALP.html -- ACL2 Version 6.3

    RATIONALP

    recognizer for rational numbers (ratios and integers)
    Major Section:  ACL2-BUILT-INS
    

    (rationalp x) is true if and only if x is an rational number.




    acl2-sources/doc/HTML/READ-BYTE$.html0000664002132200015000000000062112222333524016267 0ustar kaufmannacl2 READ-BYTE$.html -- ACL2 Version 6.3

    READ-BYTE$

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/READ-CHAR$.html0000664002132200015000000000062112222333524016241 0ustar kaufmannacl2 READ-CHAR$.html -- ACL2 Version 6.3

    READ-CHAR$

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/READ-OBJECT.html0000664002132200015000000000062312222333524016430 0ustar kaufmannacl2 READ-OBJECT.html -- ACL2 Version 6.3

    READ-OBJECT

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/READ-RUN-TIME.html0000664002132200015000000000205412222333524016662 0ustar kaufmannacl2 READ-RUN-TIME.html -- ACL2 Version 6.3

    READ-RUN-TIME

    read elapsed runtime
    Major Section:  ACL2-BUILT-INS
    

    By default, (read-run-time state) returns (mv runtime state), where runtime is the elapsed runtime in seconds since the start of the current ACL2 session and state is the resulting ACL2 state. But read-run-time can be made to return elapsed realtime (wall clock time) instead; see get-internal-time.

    The logical definition probably won't concern many users, but for completeness, we say a word about it here. That definition uses the function read-acl2-oracle, which modifies state by popping the value to return from its acl2-oracle field.




    acl2-sources/doc/HTML/REAL-LISTP.html0000664002132200015000000000110312222333526016361 0ustar kaufmannacl2 REAL-LISTP.html -- ACL2 Version 6.3

    REAL-LISTP

    ACL2(r) recognizer for a true list of real numbers
    Major Section:  REAL
    

    The predicate real-listp tests whether its argument is a true list of real numbers. This predicate is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/REAL.html0000664002132200015000000000655212222333526015505 0ustar kaufmannacl2 REAL.html -- ACL2 Version 6.3

    REAL

    ACL2(r) support for real numbers
    Major Section:  ACL2 Documentation
    

    ACL2 supports rational numbers but not real numbers. However, starting with Version 2.5, a variant of ACL2 called ``ACL2(r)'' supports the real numbers by way of non-standard analysis. ACL2(r) was conceived and first implemented by Ruben Gamboa in his Ph.D. dissertation work, supervised by Bob Boyer with active participation by Matt Kaufmann.

    ACL2(r) has the same source files as ACL2. After you download ACL2, you can build ACL2(r) by executing the following command on the command line in your acl2-sources directory, replacing <your_lisp> with a path to your Lisp executable:

    make large-acl2r LISP=<your_lisp>
    
    This will create an executable in your acl2-sources directory named saved_acl2r.

    Note that if you download community books as tarfiles, then you should be sure to download the `nonstd' books, from http://acl2-books.googlecode.com/files/nonstd-6.3.tar.gz. Then certify them from your acl2-sources directory, shown here as <DIR>:

    make regression-nonstd ACL2=<DIR>/saved_acl2r
    

    To check that you are running ACL2(r), see if the prompt includes the string ``(r)'', e.g.:

    ACL2(r) !>
    
    Or, look at (@ acl2-version) and see if ``(r)'' is a substring.

    In ACL2 (as opposed to ACL2(r)), when we say ``real'' we mean ``rational.''

    Some Related Topics

    • I-CLOSE -- ACL2(r) test for whether two numbers are infinitesimally close

    • I-LARGE -- ACL2(r) recognizer for infinitely large numbers

    • I-LIMITED -- ACL2(r) recognizer for limited numbers

    • I-SMALL -- ACL2(r) recognizer for infinitesimal numbers

    • REAL-LISTP -- ACL2(r) recognizer for a true list of real numbers

    • STANDARD-PART -- ACL2(r) function mapping limited numbers to standard numbers

    • STANDARDP -- ACL2(r) recognizer for standard objects

    Caution: ACL2(r) should be considered experimental: although we (Kaufmann and Moore) have carefully completed Gamboa's integration of the reals into the ACL2 source code, our primary concern has been to ensure unchanged behavior when ACL2 is compiled in the default manner, i.e., without the non-standard extensions. As for every release of ACL2, at the time of a release we are unaware of soundness bugs in ACL2 or ACL2(r).

    There is only limited documentation on the non-standard features of ACL2(r). We hope to provide more documentation for such features in future releases. Please feel free to query the authors if you are interested in learning more about ACL2(r). Gamboa's dissertation may also be helpful.




    acl2-sources/doc/HTML/REALFIX.html0000664002132200015000000000163012222333524016042 0ustar kaufmannacl2 REALFIX.html -- ACL2 Version 6.3

    REALFIX

    coerce to a real number
    Major Section:  ACL2-BUILT-INS
    

    Realfix simply returns any real number argument unchanged, returning 0 on a non-real argument. Also see nfix, see ifix, see rfix, and see fix for analogous functions that coerce to a natural number, an integer, a rational, and a number, respectively.

    Realfix has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/REALPART.html0000664002132200015000000000121012222333524016154 0ustar kaufmannacl2 REALPART.html -- ACL2 Version 6.3

    REALPART

    real part of a complex number
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-realpart):

    (equal (realpart x)
           (if (acl2-numberp x)
               (realpart x)
             0))
    

    Guard for (realpart x):

    (acl2-numberp x)
    




    acl2-sources/doc/HTML/REAL_slash_RATIONALP.html0000664002132200015000000000166012222333524020301 0ustar kaufmannacl2 REAL_slash_RATIONALP.html -- ACL2 Version 6.3

    REAL/RATIONALP

    recognizer for rational numbers (including real number in ACL2(r))
    Major Section:  ACL2-BUILT-INS
    

    For most ACL2 users, this is a macro abbreviating rationalp. In ACL2(r) (see real), this macro abbreviates the predicate realp, which holds for real numbers as well (including rationals). Most ACL2 users can ignore this macro and use rationalp instead, but many community books use real/rationalp so that these books will be suitable for ACL2(r) as well.




    acl2-sources/doc/HTML/REBUILD.html0000664002132200015000000000635512222333522016045 0ustar kaufmannacl2 REBUILD.html -- ACL2 Version 6.3

    REBUILD

    a convenient way to reconstruct your old state
    Major Section:  OTHER
    

    Examples:
    ACL2 !>(rebuild "project.lisp")
    ACL2 !>(rebuild "project.lisp" t)
    ACL2 !>(rebuild "project.lisp" t :dir :system)
    ACL2 !>(rebuild "project.lisp" :all)
    ACL2 !>(rebuild "project.lisp" :query)
    ACL2 !>(rebuild "project.lisp" 'lemma-22)
    

    Rebuild allows you to assume all the commands in a given file or list, supplied in the first argument. Because rebuild processes an arbitrary sequence of commands with ld-skip-proofsp t, it is unsound! However, if each of these commands is in fact admissible, then processing them with rebuild will construct the same logical state that you would be in if you typed each command and waited through the proofs again. Thus, rebuild is a way to reconstruct a state previously obtained by proving your way through the commands.

    The second, optional argument to rebuild is a ``filter'' (see ld-pre-eval-filter) that lets you specify which commands to process. You may specify t, :all, :query, or a new logical name. t and :all both mean that you expect the entire file or list to be processed. :query means that you will be asked about each command in turn. A new name means that all commands will be processed as long as the name is new, i.e., rebuild will stop processing commands immediately after executing a command that introduces name. Rebuild will also stop if any command causes an error. You may therefore wish to plant an erroneous form in the file, e.g., (mv t nil state), (see ld-error-triples), to cause rebuild to stop there. The form (i-am-here) is such a pre-defined form. If you do not specify a filter, rebuild will query you for one.

    Inspection of the definition of rebuild, e.g., via :pc rebuild-fn, will reveal that it is just a glorified call to the function ld. See ld if you find yourself wishing that rebuild had additional functionality.

    If you supply the above ``filter'' argument, then you may also supply the keyword argument :dir, which is then passed to ld; see ld.




    acl2-sources/doc/HTML/REDEF+.html0000664002132200015000000000361712222333521015654 0ustar kaufmannacl2 REDEF+.html -- ACL2 Version 6.3

    REDEF+

    system hacker's redefinition command
    Major Section:  MISCELLANEOUS
    

    Example and General Form:
    ACL2 !>:redef+
    ACL2 p!>
    

    This command is intended only for system hackers, not typical users. It sets ld-redefinition-action to '(:warn! . :overwrite), sets the default defun-mode to :program, and invokes set-state-ok with value t. It also introduces (defttag :redef+), so that redefinition of system functions will be permitted; see defttag. Finally, it removes as untouchable (see push-untouchable) all variables and functions.

    WARNING: If the form (redef+) is used in a book, then including the book can leave you in a state in which dangerous actions are allowed, specifically: redefinition, and access to functions and variables normally prohibited because they are untouchable. To avoid this problem, insert the form (redef-) into your book after (redef+).

    To see the code for redef+, evaluate :trans1 (redef+). This command is intended for those who are modifying ACL2 source code definitions. Thus, note that even system functions can be redefined with a mere warning. Be careful!




    acl2-sources/doc/HTML/REDEF-.html0000664002132200015000000000163012222333521015647 0ustar kaufmannacl2 REDEF-.html -- ACL2 Version 6.3

    REDEF-

    turn off system hacker's redefinition command
    Major Section:  MISCELLANEOUS
    

    Example and General Form:
    ACL2 !>:redef-
    ACL2 p!>
    

    This macro, for system hackers only, is a convenient way to reverse the effects of :redef+. See redef+. We do not guarantee that :redef- will restore everything one might expect to its state before the earlier :redef+. To see exactly what :redef- does, look at its code, for example by evaluating :trans1 (redef-)




    acl2-sources/doc/HTML/REDEF.html0000664002132200015000000000174012222333521015574 0ustar kaufmannacl2 REDEF.html -- ACL2 Version 6.3

    REDEF

    a common way to set ld-redefinition-action
    Major Section:  MISCELLANEOUS
    

    Example and General Form:
    ACL2 !>:redef
    
    This command sets ld-redefinition-action to '(:query . :overwrite).

    This command allows redefinition of functions and other events without undoing, but with a query that requires the user to acknowledge that the redefinition is intentional. To avoid that query, see redef! or for more options, see ld-redefinition-action.




    acl2-sources/doc/HTML/REDEFINED-NAMES.html0000664002132200015000000000455612222333521017105 0ustar kaufmannacl2 REDEFINED-NAMES.html -- ACL2 Version 6.3

    REDEFINED-NAMES

    to collect the names that have been redefined
    Major Section:  MISCELLANEOUS
    

    Example and General Forms:
    (redefined-names state)
    

    This function collects names that have been redefined in the current ACL2 state. :Program mode functions that were reclassified to :logic functions are not collected, since such reclassification cannot imperil soundness because it is allowed only when the new and old definitions are identical.

    Thus, if (redefined-names state) returns nil then no unsafe definitions have been made, regardless of ld-redefinition-action. See ld-redefinition-action.

    WARNING: This function does not report names that are not explicitly redefined in the current logical world. Consider for example:

    (encapsulate
     ()
     (defun foo () t)
     (local (defun foo () nil))
     (defthm foo-prop
       (equal (foo) nil)
       :rule-classes nil))
    
    If you attempt to call certify-book in the resulting world, ACL2 will appropriately refuse to do the certification:
    ACL2 Error in (CERTIFY-BOOK "foo" ...):  At least one command in the
    current ACL2 world was executed while the value of state global variable
    'LD-REDEFINITION-ACTION was not nil:
    
      (DEFUN FOO NIL T)
    
    Certification is therefore not allowed in this world.  You can use
    :ubt to undo back through this command; see :DOC ubt.
    
    However, since the local redefinition of foo is gone in the certification world, (redefined-names state) will return nil. (Technical aside, to be ignored except for those interested in implementation details: ACL2 inhibits certify-book in this example because the definition of foo is the value of (global-val 'redef-seen (w state)).)




    acl2-sources/doc/HTML/REDEFINING-PROGRAMS.html0000664002132200015000000002061712222333525017621 0ustar kaufmannacl2 REDEFINING-PROGRAMS.html -- ACL2 Version 6.3

    REDEFINING-PROGRAMS

    an explanation of why we restrict redefinitions
    Major Section:  PROGRAMMING
    

    ACL2 does not in general allow the redefinition of functions because logical inconsistency can result: previously stored theorems can be rendered invalid if the axioms defining the functions involved are changed. However, to permit prototyping of both :program and :logic mode systems, ACL2 permits redefinition if the user has accepted logical responsibility for the consequences by setting ld-redefinition-action to an appropriate non-nil value. The refusal of ACL2 to support the unrestricted redefinition of :program mode functions may appear somewhat capricious. After all, what are the logical consequences of changing a definition if no axioms are involved?

    Three important points should be made before we discuss redefinition further.

    The first is that ACL2 does support redefinition (of both :program and :logic functions) when ld-redefinition-action is non-nil.

    The second is that a ``redefinition'' that does not change the mode, formals, guards, type declarations, stobjs, or body of a function is considered redundant and is permitted even when ld-redefinition-action is nil. We recognize and permit redundant definitions because it is not uncommon for two distinct books to share identical function definitions. When determining whether the body of a function is changed by a proposed redefinition, we actually compare the untranslated versions of the two bodies. See term. For example, redundancy is not recognized if the old body is (list a b) and the new body is (cons a (cons b nil)). We use the untranslated bodies because of the difficulty of translating the new body in the presence of the old syntactic information, given the possibility that the redefinition might attempt to change the signature of the function, i.e., the number of formals, the number of results, or the position of single-threaded objects in either.

    The third important point is that a ``redefinition'' that preserves the formals, guard, types, stobjs, and body but changes the mode from :program to :logic is permitted even when ld-redefinition-action is nil. That is what verify-termination does.

    This note addresses the temptation to allow redefinition of :program functions in situations other than the three described above. Therefore, suppose ld-redefinition-action is nil and consider the cases.

    Case 1. Suppose the new definition attempts to change the formals or more generally the signature of the function. Accepting such a redefinition would render ill-formed other :program functions which call the redefined function. Subsequent attempts to evaluate those callers could arbitrarily damage the Common Lisp image. Thus, redefinition of :program functions under these circumstances requires the user's active approval, as would be sought with ld-redefinition-action '(:query . :overwrite).

    Case 2. Suppose the new definition attempts to change the body (even though it preserves the signature). At one time we believed this was acceptable and ACL2 supported the quiet redefinition of :program mode functions in this circumstance. However, because such functions can be used in macros and redundancy checking is based on untranslated bodies, this turns out to be unsound! (Aside: Perhaps this is not an issue if the function takes state or a user-defined stobj argument; but we do not further consider this distinction.) Such redefinition is therefore now prohibited. We illustrate such an unsoundness below. Let foo-thm1.lisp be a book with the following contents.

    (in-package "ACL2")
    (defun p1 (x) (declare (xargs :mode :program)) (list 'if x 't 'nil))
    (defmacro p (x) (p1 x))
    (defun foo (x) (p x))
    (defthm foo-thm1 (iff (foo x) x) :rule-classes nil)
    
    Note that the macro form (p x) translates to (if x t nil). The :program function p1 is used to generate this translation. The function foo is defined so that (foo x) is (p x) and a theorem about foo is proved, namely, that (foo x) is true iff x is true.

    Now let foo-thm2.lisp be a book with the following contents.

    (in-package "ACL2")
    (defun p1 (x) (declare (xargs :mode :program)) (list 'if x 'nil 't))
    (defmacro p (x) (p1 x))
    (defun foo (x) (p x))
    (defthm foo-thm2 (iff (foo x) (not x)) :rule-classes nil)
    
    In this book, the :program function p1 is defined so that (p x) means just the negation of what it meant in the first book, namely, (if x nil t). The function foo is defined identically -- more precisely, the untranslated body of foo is identical in the two books, but because of the difference between the two versions of the :program function p1 the axioms defining the two foos are different. In the second book we prove the theorem that (foo x) is true iff x is nil.

    Now consider what would happen if the signature-preserving redefinition of :program functions were permitted and these two books were included. When the second book is included the redefinition of p1 would be permitted since the signature is preserved and p1 is just a :program. But then when the redefinition of foo is processed it would be considered redundant and thus be permitted. The result would be a logic in which it was possible to prove that (foo x) is equivalent to both x and (not x). In particular, the following sequence leads to a proof of nil:

    (include-book "foo-thm1")
    (include-book "foo-thm2")
    (thm nil :hints (("Goal" :use (foo-thm1 foo-thm2))))
    

    It might be possible to loosen the restrictions on the redefinition of :program functions by allowing signature-preserving redefinition of :program functions not involved in macro definitions. Alternatively, we could implement definition redundancy checking based on the translated bodies of functions (though that is quite problematic). Barring those two changes, we believe it is necessary simply to impose the same restrictions on the redefinition of :program mode functions as we do on :logic mode functions.




    acl2-sources/doc/HTML/REDEF_bang_.html0000664002132200015000000000176212222333521016726 0ustar kaufmannacl2 REDEF_bang_.html -- ACL2 Version 6.3

    REDEF!

    a common way to set ld-redefinition-action
    Major Section:  MISCELLANEOUS
    

    Example and General Form:
    ACL2 !>:redef!
    
    This command sets ld-redefinition-action to '(:warn! . :overwrite).

    This command allows redefinition of functions and other events without undoing, but with a warning. The command :redef is similar, but queries the user first before doing the redefinition. For more related options, see ld-redefinition-action.




    acl2-sources/doc/HTML/REDO-FLAT.html0000664002132200015000000001267512222333522016236 0ustar kaufmannacl2 REDO-FLAT.html -- ACL2 Version 6.3

    REDO-FLAT

    redo on failure of a progn, encapsulate, or certify-book
    Major Section:  OTHER
    

    When one submits an encapsulate, progn, or certify-book command and there is a failure, ACL2 restores its logical world as though the command had not been run. But sometimes one would like to debug the failure by re-executing all sub-events that succeeded up to the point of failure, and then re-executing the failed sub-event. Said differently, imagine that the events under an encapsulate, progn, or certify-book form were flattened into a list of events that were then submitted to ACL2 up to the point of failure. This would put us in the state in which the original failed event had failed, so we could now replay that failed event and try modifying it, or first proving additional events, in order to get it admitted.

    Redo-flat is provided for this purpose. Consider the following (rather nonsensical) example, in which the defun of f3 fails (the body is y but the formal parameter list is (x)).

    (encapsulate
     ()
     (defun f1 (x) x)
     (encapsulate ()
                  (local (defthm hack (equal (car (cons x y)) x))))
     (encapsulate ()
                  (local (defthm hack (equal (+ x y) (+ y x)))))
     (encapsulate ()
                  (make-event '(defun f2 (x) x))
                  (progn (defthm foo (equal x x) :rule-classes nil)
                         (defun f3 (x) y)))
     (defun f4 (x) x)
     (defun f5 (x) y))
    
    After this attempt fails, you can evaluate the following form.
    (redo-flat)
    
    This will first lay down a deflabel event, (deflabel r), so that you can eventually remove your debugging work with (:ubt! r). Then the successful sub-events that preceded the failure will be executed with proofs skipped (so that this execution is fast). Then, the failed event will be executed. Finally, a :pbt command is executed so that you can see a summary of the events that executed successfully.

    You can eliminate some of the steps above by supplying keyword values, as follows.

    (redo-flat
     :succ  succ ; Skip the successful sub-events if val is nil.
     :fail  fail ; Skip the failed sub-event if val is nil.
     :label lab  ; Skip deflabel if lab or succ is nil, else use (deflabel lab).
     :pbt   val  ; Skip the final :pbt if val, lab, or succ is nil.
     )
    
    Also, you can avoid skipping proofs for the successful sub-events by supplying keyword :succ-ld-skip-proofsp with a valid value for ld-skip-proofsp; see ld-skip-proofsp. For example, you might want to execute (redo-flat :succ-ld-skip-proofsp nil) if you use the must-fail utility from community book make-event/eval.lisp, since for example (must-fail (thm (equal x y))) normally succeeds but would cause an error if proofs are skipped.

    If you prefer only to see the successful and failed sub-events, without any events being re-executed, you may evaluate the following form instead.

    (redo-flat :show t)
    
    For the example above, this command produces the following output.
    
    List of events preceding the failure:
    
    ((DEFUN F1 (X) X)
     (ENCAPSULATE NIL
                  (LOCAL (DEFTHM HACK (EQUAL (CAR (CONS X Y)) X))))
     (ENCAPSULATE NIL
                  (LOCAL (DEFTHM HACK (EQUAL (+ X Y) (+ Y X)))))
     (MAKE-EVENT '(DEFUN F2 (X) X))
     (DEFTHM FOO (EQUAL X X)
             :RULE-CLASSES NIL))
    
    Failed event:
    
    (DEFUN F3 (X) Y)
    ACL2 !>
    

    Redo-flat uses a scheme that should not cause spurious name conflicts for local events. Above, it is mentioned that events are ``flattened''; now we clarify this notion. Each sub-event that succeeds and is an encapsulate or progn is left intact. Only such events that fail are replaced by their component events. Thus, in the example above, there is no conflict between the two local sub-events named ``hack,'' because these are contained in successful encapsulate sub-events, which are therefore not flattened. The progn and two encapsulate events surrounding the definition of f3 are, however, flattened, because that definition failed to be admitted.

    Normally, redo-flat will have the desired effect even if you interrupted a proof (with control-c). However, redo-flat will not produce the desired result after an interrupt if you have enabled the debugger using (set-debugger-enable t),




    acl2-sources/doc/HTML/REDUNDANT-ENCAPSULATE.html0000664002132200015000000001245312222333517020045 0ustar kaufmannacl2 REDUNDANT-ENCAPSULATE.html -- ACL2 Version 6.3

    REDUNDANT-ENCAPSULATE

    redundancy of encapsulate events
    Major Section:  ENCAPSULATE
    

    For this documentation topic we assume familiarity with encapsulate events and the notion of redundancy for events; see encapsulate and see redundant-events.

    The typical way for an encapsulate event to be redundant is when a syntactically identical encapsulate has already been executed under the same default-defun-mode, default-ruler-extenders, and default-verify-guards-eagerness. But more generally, the encapsulate events need not be syntactically identical; for example, it suffices that they agree when the contents of local sub-events are ignored. The precise criterion for redundancy is given below, but let us first look at a consequence of the point just made about ignoring the contents of local sub-events. Consider the following sequence of two events.

    (encapsulate
     ()
     (defun f (x) x)
     (local (defthm f-identity
              (equal (f x) x))))
    
    (encapsulate
     ()
     (defun f (x) x)
     (local (defthm false-claim
              (equal (f x) (not x)))))
    
    You might be surprised to learn that the second is actually redundant, even though false-claim is clearly not a theorem; indeed, its negation is a theorem! The following two points may soften the blow. First, this behavior is as specified above (and is specified more precisely below): the contents of local events are ignored when checking redundancy of encapsulate events. Second, this behavior is sound, because the logical meaning of an encapsulate event is taken from the events that it exports, which do not include events that are local to the encapsulate event.

    Some users, however, want to use encapsulate events for testing in a way that is thwarted by this ignoring of local sub-events. Consider the following sort of example.

    (encapsulate ()
                 (local (defthm test1 ...)))
    
    (encapsulate ()
                 (local (defthm test2 ...)))
    
    Since the contents of local events are ignored when checking redundancy of an encapsulate event, the second form just above is indeed redundant, presumably not as expected by whomever wrote these two tests. A solution is to add distinct non-local forms, for example as follows.
    (encapsulate ()
                 (value-triple "test1")
                 (local (defthm test1 ...)))
    
    (encapsulate ()
                 (value-triple "test2")
                 (local (defthm test2 ...)))
    
    A different solution is to use make-event for testing, as follows.
    (make-event (er-progn (defthm test1 ...)
                          (value '(value-triple nil))))
    (make-event (er-progn (defthm test2 ...)
                          (value '(value-triple nil))))
    
    Also see community books misc/eval.lisp, make-event/eval-check.lisp, and make-event/eval-tests.lisp for more ways to test in books.

    The precise criterion for redundancy of encapsulate events is that the existing and proposed encapsulate events contain the same signatures and the same number of top-level events -- let k be that number -- and for each i < k, the ith top-level events E1 and E2 from the earlier and current encapsulates have one of the following properties.

    o E1 and E2 are equal; or

    o E1 is of the form (record-expansion E2 ...); or else

    o E1 and E2 are equal after replacing each local sub-event by (local (value-triple :elided)), where a sub-event of an event E is either E itself, or a sub-event of a constituent event of E in the case that E is a call of with-output, with-prover-time-limit, with-prover-step-limit, record-expansion, time$, progn, progn!, or encapsulate itself.




    acl2-sources/doc/HTML/REDUNDANT-EVENTS.html0000664002132200015000000004512212222333521017277 0ustar kaufmannacl2 REDUNDANT-EVENTS.html -- ACL2 Version 6.3

    REDUNDANT-EVENTS

    allowing a name to be introduced ``twice''
    Major Section:  MISCELLANEOUS
    

    Sometimes an event will announce that it is ``redundant'', meaning that the the form is not evaluated because ACL2 determines that its effect is already incorporated into the logical world. Thus, when this happens, no change to the logical world takes place. This feature permits two independent books, each of which defines some name, to be included sequentially provided they use exactly the same definition.

    Note that by the definition above, a form can have no effect on the logical world yet not be considered to be redundant. Here is an example of such a form.

      (value-triple (cw "Hello world.~%"))
    

    When are two logical-name definitions considered ``the same''? It depends upon the kind of name being defined. We consider these below in alphabetical order. See also the Notes below.

    A defabsstobj is redundant if there is already an identical defabsstobj event in the logical world.

    A defattach event is never redundant. Note that it doesn't define any name.

    A defaxiom or defthm event is redundant if there is already an axiom or theorem of the given name and both the formula (after macroexpansion) and the rule-classes are syntactically identical. Note that a defaxiom can make a subsequent defthm redundant, and a defthm can make a subsequent defaxiom redundant as well.

    A defconst is redundant if the name is already defined either with a syntactically identical defconst event or one that defines it to have the same value.

    A defdoc event is never redundant because it doesn't define any name.

    A deflabel event is never redundant. This means that if you have a deflabel in a book and that book has been included (without error), then references to that label denote the point in history at which the book introduced the label. See the note about shifting logical names, below.

    A defmacro event is redundant if there is already a macro defined with the same name and syntactically identical arguments, guard, and body.

    A defpkg event is redundant if a package of the same name with exactly the same imports has been defined.

    A defstobj event is redundant if there is already a defstobj event with the same name that has exactly the same field descriptors (see defstobj), in the same order, and with the same :renaming value if :renaming is supplied for either event.

    A defthm event is redundant according to the criterion given above in the discussion of defaxiom.

    A deftheory is never redundant. The ``natural'' notion of equivalent deftheory forms is that the names and values of the two theory expressions are the same. But since most theory expressions are sensitive to the context in which they occur, it seems unlikely to us that two deftheorys coming from two sequentially included books will ever have the same values. So we prohibit redundant theory definitions. If you try to define the same theory name twice, you will get a ``name in use'' error.

    A defun, defuns, or mutual-recursion event is redundant if for each function to be introduced, there has already been introduced a function with the same name, the same formals, and syntactically identical :guard, :measure, type declarations, stobjs declarations and body (before macroexpansion), and an appropriate mode (see the ``Note About Appropriate Modes'' below). Moreover, the order of the combined :guard and type declarations must be the same in both cases. Exceptions:
    (1) If either definition is declared :non-executable (see defun-nx), then the two events must be identical.
    (2) It is permissible for one definition to have a :guard of t and the other to have no explicit guard (hence, the guard is implicitly t).
    (3) The :measure check is avoided if the old definition is non-recursive (and not defined within a mutual-recursion) or we are skipping proofs (for example, during include-book). Otherwise, the new definition may have a :measure of (:? v1 ... vk), where (v1 ... vk) enumerates the variables occurring in the measure stored for the old definition.
    (4) If either the old or new event is a mutual-recursion event, then redundancy requires that both are mutual-recursion events that define the same set of function symbols.

    An encapsulate event is most commonly redundant when a syntactically identical encapsulate has already been executed under the same default-defun-mode, default-ruler-extenders, and default-verify-guards-eagerness. The full criterion for redundancy of encapsulate events is more complex, for example ignoring contents of local events; see redundant-encapsulate.

    An in-theory event is never redundant. Note that it doesn't define any name.

    An include-book event is redundant if the book has already been included.

    A call of make-event is never redundant, as its argument is always evaluated to obtain the make-event expansion. However, that expansion may of course be redundant.

    A mutual-recursion event is redundant according to the criteria in the discussion above for the case of a defun event.

    A progn event is never redundant: its subsidiary events are always considered. Of course, its sub-events may themselves be redundant. If all of its sub-events are redundant -- or more generally, if none of the sub-events changes the logical world -- then the progn event also won't change the world.

    A push-untouchable event is redundant if every name supplied is already a member of the corresponding list of untouchable symbols.

    A regenerate-tau-database event is never redundant. Note that it doesn't define any name.

    A remove-untouchable event is redundant if no name supplied is a member of the corresponding list of untouchable symbols.

    A reset-prehistory event is redundant if it does not cause any change.

    A set-body event is redundant if the indicated body is already the current body.

    A table event not define any name. It is redundant when it sets the value already associated with a key of the table, or when it sets an entire table (using keyword :clear) to its existing value; see table.

    A verify-guards event is redundant if the function has already had its guards verified.

    Note About Built-in (Predefined) Functions and Macros:

    Redundancy is restricted for built-in macros and functions that have special raw Lisp code. Such redundancy is only legal in the context of LOCAL. This restriction is needed for soundness, for technical reasons omitted here (details may be found in a long comment about redundant-events in source function chk-acceptable-defuns-redundancy).

    Note About Appropriate Modes:

    Suppose a function is being redefined and that the formals, guards, types, stobjs, and bodies are identical. When are the modes (:program or :logic) ``appropriate?'' Identical modes are appropriate. But what if the old mode was :program and the new mode is :logic? This is appropriate, provided the definition meets the requirements of the logical definitional principle. That is, you may redefine ``redundantly'' a :program mode function as a :logic mode function provide the measure conjectures can be proved. This is what verify-termination does. Now consider the reverse style of redefinition. Suppose the function was defined in :logic mode and is being identically redefined in :program mode. ACL2 will treat the redefinition as redundant, provided the appropriate criteria are met (as though it were in :logic mode).

    Note About Shifting Logical Names:

    Suppose a book defines a function fn and later uses fn as a logical name in a theory expression. Consider the value of that theory expression in two different sessions. In session A, the book is included in a world in which fn is not already defined, i.e., in a world in which the book's definition of fn is not redundant. In session B, the book is included in a world in which fn is already identically defined. In session B, the book's definition of fn is redundant. When fn is used as a logical name in a theory expression, it denotes the point in history at which fn was introduced. Observe that those points are different in the two sessions. Hence, it is likely that theory expressions involving fn will have different values in session A than in session B.

    This may adversely affect the user of your book. For example, suppose your book creates a theory via deftheory that is advertised just to contain the names generated by the book. But suppose you compute the theory as the very last event in the book using:

      (set-difference-theories (universal-theory :here)
                               (universal-theory fn))
    
    where fn is the very first event in the book and happens to be a defun event. This expression returns the advertised set if fn is not already defined when the book is included. But if fn were previously (identically) defined, the theory is larger than advertised.

    The moral of this is simple: when building books that other people will use, it is best to describe your theories in terms of logical names that will not shift around when the books are included. The best such names are those created by deflabel.

    Note About Unfortunate Redundancies.

    Notice that our syntactic criterion for redundancy of defun events may not allow redefinition to take effect unless there is a syntactic change in the definition. The following example shows how an attempt to redefine a function can fail to make any change.

      (set-ld-redefinition-action '(:warn . :overwrite) state)
      (defmacro mac (x) x)
      (defun foo (x) (mac x))
      (defmacro mac (x) (list 'car x))
      (set-ld-redefinition-action nil state)
      (defun foo (x) (mac x)) ; redundant, unfortunately; foo does not change
      (thm (equal (foo 3) 3)) ; succeeds, showing that redef of foo didn't happen
    
    The call of macro mac was expanded away before storing the first definition of foo for the theorem prover. Therefore, the new definition of mac does not affect the expansion of foo by the theorem prover, because the new definition of foo is ignored.

    One workaround is first to supply a different definition of foo, just before the last definition of foo above. Then that final definition will no longer be redundant. However, as a courtesy to users, we strengthen the redundancy check for function definitions when redefinition is active. If in the example above we remove the form (set-ld-redefinition-action nil state), then the problem goes away:

      (set-ld-redefinition-action '(:warn . :overwrite) state)
      (defmacro mac (x) x)
      (defun foo (x) (mac x))
      (defmacro mac (x) (list 'car x))
      (defun foo (x) (mac x)) ; no longer redundant
      (thm (equal (foo 3) 3)) ; fails, as we would like
    

    To summarize: If a defun form is submitted that meets the usual redundancy criteria, then it may be considered redundant even if a macro called in the definition has since been redefined. The analogous problem applie to constants, i.e., symbols defined by defconst that occur in the definition body. However, if redefinition is currently active the problem goes away: that is, the redundancy check is strengthened to check the ``translated'' body, in which macro calls and constants defined by defconst are expanded away.

    The above discussion for defun forms applies to defconst forms as well. However, for defmacro forms ACL2 always checks translated bodies, so such bogus redundancy does not occur.

    Here is more complex example illustrating the limits of redefinition, based on one supplied by Grant Passmore.

      (defun n3 () 0)
      (defun n4 () 1)
      (defun n5 () (> (n3) (n4))) ; body is normalized to nil
      (thm (equal (n5) nil)) ; succeeds, trivially
      (set-ld-redefinition-action '(:warn . :overwrite) state)
      (defun n3 () 2)
      (thm (equal (n5) nil)) ; still succeeds, sadly
    
    We may expect the final thm call to fail because of the following reasoning: (n5) = (> (n3) (n4)) = (> 2 1) = t. Unfortunatly, the body of n5 was simplified (``normalized'') to nil when n5 was admitted, so the redefinition of n3 is ignored during the final thm call. (Such normalization can be avoided; see the brief discussion of :normalize in the documentation for defun.) So, given this unfortunate situation, one might expect at this point simply to redefine n5 using the same definition as before, in order to pick up the new definition of n3. Such ``redefinition'' would, however, be redundant, for the same reason as in the previous example: no syntactic change was made to the definition. Even with redefinition active, there is no change in the body of n5, even with macros and constants (defined by defconst) expanded; there are none such! The same workaround applies as before: redefine n5 to be something different, and then redefine n5 again to be as desired.

    A related phenomenon can occur for encapsulate. As explained above, an encapsulate event is redundant if it is identical to one already in the database. (But a weaker condition applies in general; see redundant-encapsulate.) Consider then the following contrived example.

      (defmacro mac (x) x)
      (encapsulate () (defun foo (x) (mac x)))
      (set-ld-redefinition-action '(:warn . :overwrite) state)
      (defmacro mac (x) (list 'car x))
      (encapsulate () (defun foo (x) (mac x)))
    
    The last encapsulate event is redundant because it meets the criterion for redundancy: it is identical to the earlier encapsulate event. Even though redefinition is active, and hence ACL2 ``should'' be able to see that the new defun of foo is not truly redundant, nevertheless the criterion for redundancy of encapsulate allows the new encapsulate form to be redundant.

    A workaround can be to add something trivial to the encapsulate, for example:

      (encapsulate ()
        (deflabel try2) ; ``Increment'' to try3 next time, and so on.
        (defun foo (x) x))
    




    acl2-sources/doc/HTML/REFINEMENT.html0000664002132200015000000000561012222333530016403 0ustar kaufmannacl2 REFINEMENT.html -- ACL2 Version 6.3

    REFINEMENT

    record that one equivalence relation refines another
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example:
    (defthm bag-equal-refines-set-equal
      (implies (bag-equal x y)
               (set-equal y x))
      :rule-classes :refinement)
    

    Also see defrefinement.

    General Form:
    (implies (equiv1 x y) (equiv2 x y))
    
    Equiv1 and equiv2 must be known equivalence relations. The effect of such a rule is to record that equiv1 is a refinement of equiv2. This means that equiv1 :rewrite rules may be used while trying to maintain equiv2. See equivalence for a general discussion of the issues.

    The macro form (defrefinement equiv1 equiv2) is an abbreviation for a defthm of rule-class :refinement that establishes that equiv1 is a refinement of equiv2. See defrefinement.

    Suppose we have the :rewrite rule

    (bag-equal (append a b) (append b a))
    
    which states that append is commutative modulo bag-equality. Suppose further we have established that bag-equality refines set-equality. Then when we are simplifying append expressions while maintaining set-equality we use append's commutativity property, even though it was proved for bag-equality.

    Equality is known to be a refinement of all equivalence relations. The transitive closure of the refinement relation is maintained, so if set-equality, say, is shown to be a refinement of some third sense of equivalence, then bag-equality will automatially be known as a refinement of that third equivalence.

    :refinement lemmas cannot be disabled. That is, once one equivalence relation has been shown to be a refinement of another, there is no way to prevent the system from using that information. Of course, individual :rewrite rules can be disabled.

    More will be written about this as we develop the techniques.




    acl2-sources/doc/HTML/REGENERATE-TAU-DATABASE.html0000664002132200015000000001024512222333517020166 0ustar kaufmannacl2 REGENERATE-TAU-DATABASE.html -- ACL2 Version 6.3

    REGENERATE-TAU-DATABASE

    regenerate the tau database relative to the current enabled theory
    Major Section:  EVENTS
    

    Example:
    (regenerate-tau-database)
    
    General Form:
    (regenerate-tau-database :doc doc-string)
    
    where doc-string is an optional documentation string not beginning with ``:Doc-Section ...''. Because no unique name is associated with a regenerate-tau-database event, there is no way we can store the documentation string doc-string in our il[documentation] database. Hence, we actually prohibit doc-string from having the form of an ACL2 documentation string; see doc-string.

    The tau database is regenerated by scanning the current logical world and re-processing every rule-generating event in it relative to the current enabled theory and current tau auto mode settings. See introduction-to-the-tau-system for background details.

    This command was intended to allow the user to remove a fact from the tau database, by regenerating the database without the fact. But as the following discussion highlights, regenerate-tau-database does not really solve the problem. We regard it as a placeholder for a more sophisticated mechanism. However, we have trouble understanding why a user might wish to remove a fact from the database and are awaiting further user experiences before designing the more sophisticated mechanism.

    Suppose, for example, that you wanted to remove a signature rule provided by some rule with name rune. You could disable rune and regenerate the database. We discuss why you might -- or might not -- want to do this later. But suppose you did it. Unfortunately, the database you get will not be just like the one you started with minus the signature rule. The reason is that the database you started with was generated incrementally and the current theory might have evolved. To take a simple example, your starting database might have included a rule that has been disabled since it was first added. Thus, the part of your starting database built before the disabling was constructed with the rule enabled and the part built afterwards has the rule disabled. You are unlikely to get the same database whether you enable or disable that rule now.

    You might hope that the avoidance of in-theory events would eliminate the problem but it does not because even the ground-zero theory is constructed incrementally from the ``pre-history'' commands used to boot up ACL2. Those pre-history commands include some global in-theory commands. Since every session starts from the ground-zero state, the starting database is already ``infected'' with global in-theory commands.

    The reason we hope that it will not be necessary to remove tau facts is that the tau system is designed merely to be fast and benign (see Design Philosophy in introduction-to-the-tau-system). The tau system's coverage should grows monotonically with the addition of rules. According to this understanding of tau, adding a signature rule, for example, may allow tau to prove some additional goals but will not prevent it from proving goals it could prove previously. If this is understanding of tau is accurate, we see no fundamental reason to support the removal of a fact. This, of course, ignores the possibility that the user wishes to explore alternative proof strategies or measure performance.

    We welcome user observations and experience on this issue.




    acl2-sources/doc/HTML/REGRESSION.html0000664002132200015000000000064112222333516016432 0ustar kaufmannacl2 REGRESSION.html -- ACL2 Version 6.3

    REGRESSION

    See books-certification.
    Major Section:  BOOKS
    




    acl2-sources/doc/HTML/RELEASE-NOTES.html0000664002132200015000000001630012222333526016720 0ustar kaufmannacl2 RELEASE-NOTES.html -- ACL2 Version 6.3

    RELEASE-NOTES

    pointers to what has changed
    Major Section:  ACL2 Documentation
    

    Some Related Topics

    • NOTE-2-0 -- ACL2 Version 2.0 (July, 1997) Notes

    • NOTE-2-1 -- ACL2 Version 2.1 (December, 1997) Notes

    • NOTE-2-2 -- ACL2 Version 2.2 (August, 1998) Notes

    • NOTE-2-3 -- ACL2 Version 2.3 (October, 1998) Notes

    • NOTE-2-4 -- ACL2 Version 2.4 (August, 1999) Notes

    • NOTE-2-5 -- ACL2 Version 2.5 (June, 2000) Notes

    • NOTE-2-5(R) -- ACL2 Version 2.5(r) (June, 2000) Notes

    • NOTE-2-6 -- ACL2 Version 2.6 (November, 2001) Notes

    • NOTE-2-6(R) -- ACL2 Version 2.6(r) (November, 2001) Notes

    • NOTE-2-7 -- ACL2 Version 2.7 (November, 2002) Notes

    • NOTE-2-7(R) -- ACL2 Version 2.7(r) (November, 2002) Notes

    • NOTE-2-8 -- ACL2 Version 2.8 (March, 2004) Notes

    • NOTE-2-8(R) -- ACL2 Version 2.8(r) (March, 2003) Notes

    • NOTE-2-9 -- ACL2 Version 2.9 (October, 2004) Notes

    • NOTE-2-9(R) -- ACL2 Version 2.9(r) (October, 2004) Notes

    • NOTE-2-9-1 -- ACL2 Version 2.9.1 (December, 2004) Notes

    • NOTE-2-9-2 -- ACL2 Version 2.9.2 (April, 2005) Notes

    • NOTE-2-9-3 -- ACL2 Version 2.9.3 (August, 2005) Notes

    • NOTE-2-9-4 -- ACL2 Version 2.9.4 (February, 2006) Notes

    • NOTE-2-9-5 -- Changes in Version 3.0 since Version 2.9.4

    • NOTE-3-0 -- ACL2 Version 3.0 (June, 2006) Notes

    • NOTE-3-0(R) -- ACL2 Version 3.0(r) (June, 2006) Notes

    • NOTE-3-0-1 -- ACL2 Version 3.0.1 (August, 2006) Notes

    • NOTE-3-0-1(R) -- ACL2 Version 3.0.1(r) (August, 2006) Notes

    • NOTE-3-0-2 -- ACL2 Version 3.0.2 (December, 2006) Notes

    • NOTE-3-1 -- ACL2 Version 3.1 (December, 2006) Notes

    • NOTE-3-1(R) -- ACL2 Version 3.1(r) (December, 2006) Notes

    • NOTE-3-2 -- ACL2 Version 3.2 (April, 2007) Notes

    • NOTE-3-2(R) -- ACL2 Version 3.2(r) (April, 2007) Notes

    • NOTE-3-2-1 -- ACL2 Version 3.2.1 (June, 2007) Notes

    • NOTE-3-2-1(R) -- ACL2 Version 3.2.1(r) (June, 2007) Notes

    • NOTE-3-3 -- ACL2 Version 3.3 (November, 2007) Notes

    • NOTE-3-3(R) -- ACL2 Version 3.3(r) (November, 2007) Notes

    • NOTE-3-4 -- ACL2 Version 3.4 (August, 2008) Notes

    • NOTE-3-4(R) -- ACL2 Version 3.4(r) (August, 2008) Notes

    • NOTE-3-5 -- ACL2 Version 3.5 (May, 2009) Notes

    • NOTE-3-5(R) -- ACL2 Version 3.5(r) (May, 2009) Notes

    • NOTE-3-6 -- ACL2 Version 3.6 (August, 2009) Notes

    • NOTE-3-6(R) -- ACL2 Version 3.6(r) (August, 2009) Notes

    • NOTE-3-6-1 -- ACL2 Version 3.6.1 (September, 2009) Notes

    • NOTE-4-0 -- ACL2 Version 4.0 (July, 2010) Notes

    • NOTE-4-0(R) -- ACL2 Version 4.0(r) (July, 2010) Notes

    • NOTE-4-1 -- ACL2 Version 4.1 (September, 2010) Notes

    • NOTE-4-1(R) -- ACL2 Version 4.1(r) (September, 2010) Notes

    • NOTE-4-2 -- ACL2 Version 4.2 (January, 2011) Notes

    • NOTE-4-2(R) -- ACL2 Version 4.2(r) (January, 2011) Notes

    • NOTE-4-3 -- ACL2 Version 4.3 (July, 2011) Notes

    • NOTE-4-3(R) -- ACL2 Version 4.3(r) (July, 2011) Notes

    • NOTE-5-0 -- ACL2 Version 5.0 (August, 2012) Notes

    • NOTE-6-0 -- ACL2 Version 6.0 (December, 2012) Notes

    • NOTE-6-1 -- ACL2 Version 6.1 (February, 2013) Notes

    • NOTE-6-2 -- ACL2 Version 6.2 (June, 2013) Notes

    • NOTE-6-3 -- ACL2 Version 6.3 (October, 2013) Notes

    • NOTE1 -- Acl2 Version 1.1 Notes

    • NOTE2 -- Acl2 Version 1.2 Notes

    • NOTE3 -- Acl2 Version 1.3 Notes

    • NOTE4 -- Acl2 Version 1.4 Notes

    • NOTE5 -- Acl2 Version 1.5 Notes

    • NOTE6 -- Acl2 Version 1.6 Notes

    • NOTE7 -- ACL2 Version 1.7 (released October 1994) Notes

    • NOTE8 -- ACL2 Version 1.8 (May, 1995) Notes

    • NOTE8-UPDATE -- ACL2 Version 1.8 (Summer, 1995) Notes

    • NOTE9 -- ACL2 Version 1.9 (Fall, 1996) Notes

    This section of the online documentation contains notes on the changes that distinguish successive released versions of ACL2.

    The current version of ACL2 is the value of the constant (@ acl2-version).




    acl2-sources/doc/HTML/REM.html0000664002132200015000000000212612222333524015374 0ustar kaufmannacl2 REM.html -- ACL2 Version 6.3

    REM

    remainder using truncate
    Major Section:  ACL2-BUILT-INS
    

    ACL2 !>(rem 14 3)
    2
    ACL2 !>(rem -14 3)
    -2
    ACL2 !>(rem 14 -3)
    2
    ACL2 !>(rem -14 -3)
    -2
    ACL2 !>(rem -15 -3)
    0
    ACL2 !>
    
    (Rem i j) is that number k for which (* j (truncate i j)) added to k equals i.

    The guard for (rem i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero.

    Rem is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/REMOVE-BINOP.html0000664002132200015000000000120212222333531016603 0ustar kaufmannacl2 REMOVE-BINOP.html -- ACL2 Version 6.3

    REMOVE-BINOP

    remove the association of a function name with a macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    The form (remove-binop macro-fn) is an abbreviation for the form (remove-macro-fn macro-fn). See remove-macro-fn.




    acl2-sources/doc/HTML/REMOVE-CUSTOM-KEYWORD-HINT.html0000664002132200015000000000173512222333517020647 0ustar kaufmannacl2 REMOVE-CUSTOM-KEYWORD-HINT.html -- ACL2 Version 6.3

    REMOVE-CUSTOM-KEYWORD-HINT

    remove a custom keyword hint
    Major Section:  EVENTS
    

    Example Forms:
    (remove-custom-keyword-hint :my-hint)
    
    
    General Form:
    (remove-custom-keyword-hint keyword)
    
    where keyword is a keywordp.

    For an explanation of how custom keyword hints are processed, see custom-keyword-hints; also see add-custom-keyword-hint.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.




    acl2-sources/doc/HTML/REMOVE-DEFAULT-HINTS.html0000664002132200015000000000460412222333531017714 0ustar kaufmannacl2 REMOVE-DEFAULT-HINTS.html -- ACL2 Version 6.3

    REMOVE-DEFAULT-HINTS

    remove from the default hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    
    Examples:
    (remove-default-hints '((computed-hint-1 clause)
                            (computed-hint-2 clause
                                             stable-under-simplificationp)))
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It is local to the book or encapsulate form in which it occurs (see remove-default-hints! for a corresponding non-local event).

    General Form:
    (remove-default-hints lst)
    
    where lst is a list. Generally speaking, the elements of lst should be suitable for use as computed-hints. Also see add-default-hints.

    If some elements of the given list do not belong to the existing default hints, they will simply be ignored by this event.

    Also See set-default-hints, see add-default-hints, and see default-hints.

    Finally, note that the effects of set-default-hints, add-default-hints, and remove-default-hints are local to the book in which they appear. Thus, users who include a book with such forms will not have their default hints affected by such forms. In order to export the effect of setting the default hints, use set-default-hints!, add-default-hints!, or remove-default-hints!.




    acl2-sources/doc/HTML/REMOVE-DEFAULT-HINTS_bang_.html0000664002132200015000000000204512222333531021037 0ustar kaufmannacl2 REMOVE-DEFAULT-HINTS_bang_.html -- ACL2 Version 6.3

    REMOVE-DEFAULT-HINTS!

    remove from the default hints non-locally
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please see remove-default-hints, which is the same as remove-default-hints! except that the latter is not local to the encapsulate or the book in which it occurs. Probably remove-default-hints is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing encapsulate or book.




    acl2-sources/doc/HTML/REMOVE-DIVE-INTO-MACRO.html0000664002132200015000000000200712222333531020075 0ustar kaufmannacl2 REMOVE-DIVE-INTO-MACRO.html -- ACL2 Version 6.3

    REMOVE-DIVE-INTO-MACRO

    removes association of proof-checker diving function with macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (remove-dive-into-macro logand)
    
    This feature undoes the effect of add-dive-into-macro, which is used so that the proof-checker's DV command and numeric diving commands (e.g., 3) will dive properly into subterms. Please see add-dive-into-macro and especially see dive-into-macros-table.




    acl2-sources/doc/HTML/REMOVE-DUPLICATES-EQ.html0000664002132200015000000000070312222333524017703 0ustar kaufmannacl2 REMOVE-DUPLICATES-EQ.html -- ACL2 Version 6.3

    REMOVE-DUPLICATES-EQ

    See remove-duplicates.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/REMOVE-DUPLICATES-EQUAL.html0000664002132200015000000000071112222333524020244 0ustar kaufmannacl2 REMOVE-DUPLICATES-EQUAL.html -- ACL2 Version 6.3

    REMOVE-DUPLICATES-EQUAL

    See remove-duplicates.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/REMOVE-DUPLICATES.html0000664002132200015000000000621012222333524017377 0ustar kaufmannacl2 REMOVE-DUPLICATES.html -- ACL2 Version 6.3

    REMOVE-DUPLICATES

    remove duplicates from a string or a list
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (remove-duplicates x)
    (remove-duplicates x :test 'eql)   ; same as above (eql as equality test)
    (remove-duplicates x :test 'eq)    ; same, but eq is equality test
    (remove-duplicates x :test 'equal) ; same, but equal is equality test
    

    (Remove-duplicates x) returns the result of deleting duplicate elements from the beginning of the list or string x. For example, (remove-duplicates '(1 2 3 2 4)) is equal to '(1 3 2 4). The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst.

    The guard for a call of remove-duplicates depends on the test. In all cases, the argument must satisfy stringp or true-listp. If the test is eql, then the argument must satisfy either stringp or eqlable-listp. If the test is eq, then the argument must satisfy symbol-listp.

    The relation between remove-duplicates and its variants is related to the usual pattern for equality variants; see equality-variants. However, the possibility of a string argument changes the usual pattern a bit. As one might expect:

    (remove-duplicates-eq lst) is equivalent to (remove-duplicates lst :test 'eq).

    However, remove-duplicates-equal is defined without consideration of strings, for backward compatibility with versions of ACL2 through Version_4.2. The macro remove-duplicates-logic has been introduced to model the behavior of remove-duplicates even on strings; use :pe if you wish to see its definition. So we can say the following.

    (remove-duplicates-logic lst) is equivalent to (remove-duplicates lst :test 'equal); and

    (remove-duplicates-logic lst) is equal to (remove-duplicates-equal lst) when lst is not a string.

    In particular, when the argument is not a string, reasoning about any of these primitives reduces to reasoning about the function remove-duplicates-equal.

    Remove-duplicates is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/REMOVE-EQ.html0000664002132200015000000000062712222333524016255 0ustar kaufmannacl2 REMOVE-EQ.html -- ACL2 Version 6.3

    REMOVE-EQ

    See remove.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/REMOVE-EQUAL.html0000664002132200015000000000063512222333524016616 0ustar kaufmannacl2 REMOVE-EQUAL.html -- ACL2 Version 6.3

    REMOVE-EQUAL

    See remove.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/REMOVE-INVISIBLE-FNS.html0000664002132200015000000000261112222333531017711 0ustar kaufmannacl2 REMOVE-INVISIBLE-FNS.html -- ACL2 Version 6.3

    REMOVE-INVISIBLE-FNS

    make some unary functions no longer invisible
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (remove-invisible-fns (binary-+ unary-- foo)
    (remove-invisible-fns (+ unary-- foo)
    
    The setting above has makes unary functions unary-- and foo no longer ``invisible'' for the purposes of applying permutative :rewrite rules to binary-+ trees.

    General Form:
    (remove-invisible-fns top-fn unary-fn1 ... unary-fnk)
    
    where top-fn is a function symbol and the unary-fni are unary function symbols, or more generally, these are all macro aliases for function symbols (see macro-aliases-table).

    See add-invisible-fns and also see invisible-fns-table and see set-invisible-fns-table.




    acl2-sources/doc/HTML/REMOVE-MACRO-ALIAS.html0000664002132200015000000000204112222333531017426 0ustar kaufmannacl2 REMOVE-MACRO-ALIAS.html -- ACL2 Version 6.3

    REMOVE-MACRO-ALIAS

    remove the association of a function name with a macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (remove-macro-alias append)
    
    General Form:
    (remove-macro-alias macro-name)
    
    See macro-aliases-table for a discussion of macro aliases; also see add-macro-alias. This form sets macro-aliases-table to the result of deleting the key macro-name from that table. If the name does not occur in the table, then this form still generates an event, but the event has no real effect.




    acl2-sources/doc/HTML/REMOVE-MACRO-FN.html0000664002132200015000000000171712222333531017111 0ustar kaufmannacl2 REMOVE-MACRO-FN.html -- ACL2 Version 6.3

    REMOVE-MACRO-FN

    remove the association of a function name with a macro name
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (remove-macro-fn binary-append)
    
    General Form:
    (remove-macro-fn macro-fn)
    
    See add-macro-fn for a discussion of how to associate a macro name with a function name. This form sets untrans-table to the result of deleting the association of a macro name with the given binary function name. If the function name has no such association, then this form still generates an event, but the event has no real effect.




    acl2-sources/doc/HTML/REMOVE-NTH-ALIAS.html0000664002132200015000000000212012222333531017214 0ustar kaufmannacl2 REMOVE-NTH-ALIAS.html -- ACL2 Version 6.3

    REMOVE-NTH-ALIAS

    remove a symbol alias for printing of nth/update-nth terms
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (remove-nth-alias append)
    
    General Form:
    (remove-nth-alias alias-name)
    
    See nth-aliases-table for further discussion; also see add-nth-alias. This form sets nth-aliases-table to the result of deleting the key alias-name from that table. If the name does not occur in the table, then this form still generates an event, but the event has no real effect.




    acl2-sources/doc/HTML/REMOVE-OVERRIDE-HINTS.html0000664002132200015000000000314612222333531020047 0ustar kaufmannacl2 REMOVE-OVERRIDE-HINTS.html -- ACL2 Version 6.3

    REMOVE-OVERRIDE-HINTS

    delete from the list of override-hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    See override-hints for a discussion of override-hints. Here we describe how to delete from the list of override-hints. Note that the effects of remove-override-hints events are local to the books or encapsulate events in which they reside; see remove-override-hints! to avoid that restriction. Also see add-override-hints and see set-override-hints.

    General Form:
    (remove-override-hints form)
    
    where form should evaluate to a list of computed hint forms. The effect of this event is to set the list of override-hints to the result of deleting each element of the evaluation result from the override-hints, if that element indeed belongs to the override-hints; no check is made that these elements are actually elements of the existing override-hints.




    acl2-sources/doc/HTML/REMOVE-OVERRIDE-HINTS_bang_.html0000664002132200015000000000206512222333531021174 0ustar kaufmannacl2 REMOVE-OVERRIDE-HINTS_bang_.html -- ACL2 Version 6.3

    REMOVE-OVERRIDE-HINTS!

    delete non-locally from the list of override-hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Remove-override-hints! is the same as remove-override-hints, except that the former is not local to books or encapsulate events in which it occurs. See remove-override-hints; also see add-override-hints and see set-override-hints.




    acl2-sources/doc/HTML/REMOVE-RAW-ARITY.html0000664002132200015000000000127312222333531017263 0ustar kaufmannacl2 REMOVE-RAW-ARITY.html -- ACL2 Version 6.3

    REMOVE-RAW-ARITY

    remove arity information for raw mode
    Major Section:  SET-RAW-MODE
    

    Technical note: This macro is a no-op, and is not necessary, when ACL2 is built with #-acl2-mv-as-values.

    The form (remove-raw-arity fn) undoes the effect of an earlier (remove-raw-arity fn val). See add-raw-arity.




    acl2-sources/doc/HTML/REMOVE-UNTOUCHABLE.html0000664002132200015000000001060712222333531017516 0ustar kaufmannacl2 REMOVE-UNTOUCHABLE.html -- ACL2 Version 6.3

    REMOVE-UNTOUCHABLE

    remove names from lists of untouchable symbols
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Forms:
    (remove-untouchable my-var nil) ; then state global my-var is not untouchable
    (remove-untouchable set-mem t)  ; then function set-mem is not untouchable
    

    Also see push-untouchable.

    This documentation topic is directed at those who build systems on top of ACL2. We first describe a means for removing restrictions related to so-called ``untouchables'': functions (or macros) that cannot be called, or state global variables that cannot be modified or unbound, without intervention that requires an active trust tag (see defttag). Then we describe the remove-untouchable event.

    We begin by discussing untouchable state global variables temp-touchable-vars and temp-touchable-fns, which initially have value nil. These can often be used in place of remove-untouchable. When the value is t, no variable (respectively, no function or macro) is treated as untouchable, regardless of the set of initial untouchables or the remove-untouchable or push-untouchable events that have been admitted. Otherwise the value of each of these two variables is a symbol-listp, and no member of this list is treated as an untouchable variable (in the case of temp-touchable-vars) or as an untouchable function or macro (in the case of temp-touchable-fns). These two state global variables can be set by set-temp-touchable-vars and set-temp-touchable-fns, respectively, provided there is an active trust tag (see defttag). Here is an illustrative example. This macro executes the indicated forms in a context where there are no untouchable variables, but requires an active trust tag when invoked.

    (defmacro with-all-touchable (&rest forms)
      `(progn!
        :state-global-bindings
        ((temp-touchable-vars t set-temp-touchable-vars))
        (progn! ,@forms)))
    
    An equivalent version, which however is not recommended since state-global-let* may have surprising behavior in raw Lisp, is as follows.
    (defmacro with-all-touchable (&rest forms)
      `(progn!
        (state-global-let*
         ((temp-touchable-vars t set-temp-touchable-vars))
         (progn! ,@forms))))
    
    Finally, the value t for temp-touchable-vars removes the requirement that built-in state globals cannot be made unbound (with makunbound-global).

    We now turn to the remove-untouchable event, in case the approach above is for some reason not adequate. This event is illegal by default, since it can be used to provide access to ACL2 internal functions and data structures that are intentionally made untouchable for the user. If you want to call it, you must first create an active trust tag; see defttag.

    General Form:
    (remove-untouchable name{s}  fn-p :doc doc-string)
    
    where name{s} is a non-nil symbol or a non-nil true list of symbols, fn-p is any value (but generally nil or t), and doc-string is an optional documentation string not beginning with ``:Doc-Section ...''. If name{s} is a symbol it is treated as the singleton list containing that symbol. The effect of this event is to remove the given symbols from the list of ``untouchable variables'' in the current world if fn-p is nil, else to remove the symbols into the list of ``untouchable functions''. This event is redundant if no symbol listed is a member of the appropriate untouchables list (variables or functions).




    acl2-sources/doc/HTML/REMOVE.html0000664002132200015000000000451712222333524015754 0ustar kaufmannacl2 REMOVE.html -- ACL2 Version 6.3

    REMOVE

    remove all occurrences
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (remove x lst)
    (remove x lst :test 'eql)   ; same as above (eql as equality test)
    (remove x lst :test 'eq)    ; same, but eq is equality test
    (remove x lst :test 'equal) ; same, but equal is equality test
    

    (Remove x lst) is equal to lst if x is not a member of lst, else is the result of removing all occurrences of x from lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst.

    Also see remove1.

    The guard for a call of remove depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between remove and its variants:

    (remove-eq x lst) is equivalent to (remove x lst :test 'eq);

    (remove-equal x lst) is equivalent to (remove x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function remove-equal.

    Remove is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/REMOVE1-EQ.html0000664002132200015000000000063312222333524016333 0ustar kaufmannacl2 REMOVE1-EQ.html -- ACL2 Version 6.3

    REMOVE1-EQ

    See remove1.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/REMOVE1-EQUAL.html0000664002132200015000000000064112222333524016674 0ustar kaufmannacl2 REMOVE1-EQUAL.html -- ACL2 Version 6.3

    REMOVE1-EQUAL

    See remove1.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/REMOVE1.html0000664002132200015000000000445312222333524016034 0ustar kaufmannacl2 REMOVE1.html -- ACL2 Version 6.3

    REMOVE1

    remove first occurrences, testing using eql
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (remove1 x lst)
    (remove1 x lst :test 'eql)   ; same as above (eql as equality test)
    (remove1 x lst :test 'eq)    ; same, but eq is equality test
    (remove1 x lst :test 'equal) ; same, but equal is equality test
    

    (Remove1 x lst) is equal to lst if x is not a member of lst, else is the result of removing the first occurrences of x from lst. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing x with successive elements of lst.

    Also see remove.

    The guard for a call of remove1 depends on the test. In all cases, the second argument must satisfy true-listp. If the test is eql, then either the first argument must be suitable for eql (see eqlablep) or the second argument must satisfy eqlable-listp. If the test is eq, then either the first argument must be a symbol or the second argument must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between remove1 and its variants:

    (remove1-eq x lst) is equivalent to (remove1 x lst :test 'eq);

    (remove1-equal x lst) is equivalent to (remove1 x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function remove1-equal.




    acl2-sources/doc/HTML/REORDER.html0000664002132200015000000000066412222333521016055 0ustar kaufmannacl2 REORDER.html -- ACL2 Version 6.3

    REORDER

    hints keyword :REORDER
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/RESET-FC-REPORTING.html0000664002132200015000000000166612222333517017502 0ustar kaufmannacl2 RESET-FC-REPORTING.html -- ACL2 Version 6.3

    RESET-FC-REPORTING

    reset the forward-chaining tracking state to its initial configuration
    Major Section:  FORWARD-CHAINING-REPORTS
    

    Example:  (reset-fc-reporting)
    

    This function erases all forward chaining tracking criteria and sets the on-the-fly reporting flag to nil. The next time you set the criteria (see set-fc-criteria) the short form of reports, in which only the caller and the fc-report number is printed, will appear in your proof logs.

    See forward-chaining-reports for details.




    acl2-sources/doc/HTML/RESET-KILL-RING.html0000664002132200015000000000361712222333517017131 0ustar kaufmannacl2 RESET-KILL-RING.html -- ACL2 Version 6.3

    RESET-KILL-RING

    save memory by resetting and perhaps resizing the kill ring used by oops
    Major Section:  HISTORY
    

    By default, ACL2 holds on to old logical worlds when you undo commands (see ubt), as documented elswhere; see oops. You can free up memory by deleting those old worlds using reset-kill-ring.

    Examples:
    (reset-kill-ring t state)   ; replace each element of the kill ring by nil
    (reset-kill-ring 2 state)   ; create a new kill ring of '(nil nil)
    (reset-kill-ring 0 state)   ; create a new kill ring that is empty
    (reset-kill-ring nil state) ; just return the length of the kill ring
    
    General form:
    (reset-kill-ring n state)
    
    where n evaluates either to t, to nil, or to a nonnegative integer (a natp). If n evaluates to t, it is treated as the length of the current kill ring. If n is nil, then the length k of the current kill ring is returned as a value triple (mv nil k state). If n is a natp, then the kill ring is replaced with a list of n nils.

    In particular, use (reset-kill-ring 0 state) to avoid saving any old logical worlds, at the cost of disabling the effect of the oops command.




    acl2-sources/doc/HTML/RESET-LD-SPECIALS.html0000664002132200015000000000412712222333522017332 0ustar kaufmannacl2 RESET-LD-SPECIALS.html -- ACL2 Version 6.3

    RESET-LD-SPECIALS

    restores initial settings of the ld specials
    Major Section:  OTHER
    

    Examples:
    (reset-ld-specials t)
    (reset-ld-specials nil)
    

    Roughly speaking, the ld specials are certain state global variables, such as current-package, ld-prompt, and ld-pre-eval-filter, which are managed by ld as though they were local variables. These variables determine the channels on which ld reads and prints and control many options of ld. See ld for the details on what the ld specials are.

    This function, reset-ld-specials, takes one Boolean argument, flg. The function resets all of the ld specials to their initial, top-level values, except for the three channel variables, standard-oi, standard-co, and proofs-co, which are reset to their initial values only if flg is non-nil. Of course, if you are in a recursive call of ld, then when you exit that call, the ld specials will be restored to the values they had at the time ld was called recursively. To see what the initial values are, inspect the value of the constant *initial-ld-special-bindings*.




    acl2-sources/doc/HTML/RESET-PREHISTORY.html0000664002132200015000000000504412222333531017341 0ustar kaufmannacl2 RESET-PREHISTORY.html -- ACL2 Version 6.3

    RESET-PREHISTORY

    reset the prehistory
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (reset-prehistory)     ; restart command numbering at 0
    (reset-prehistory nil) ; same as above
    (reset-prehistory t)   ; as above except also disable ubt-prehistory
    
    General Forms:
    (reset-prehistory)
    (reset-prehistory permanent-p)
    (reset-prehistory permanent-p doc-string)
    
    where permanent-p is t or nil, and doc-string is an optional documentation string not beginning with ``:Doc-Section ...''. After execution of this command, ACL2 will change the numbering provided by its history utilities so that this reset-prehistory command (or the top-level compound command containing it, which for example might be an include-book) is assigned the number 0. The only way to undo this command is with command ubt-prehistory. However, even that is disallowed if permanent-p is t.

    Note that the second argument of certify-book, which specifies the number of commands in the certification world (i.e., since ground-zero), is not sensitive to reset-prehistory; rather, it expects the number of commands since ground-zero. To see such commands, :pbt :start.

    A reset-prehistory event with value nil for permanent-p (the default) is always skipped during certify-book and include-book (indeed, whenever ld-skip-proofsp is t). Otherwise one would find the history numbering reset to 0 just by virtue of including (or certifying) a book -- probably not what was intended.

    See ubt-prehistory for how to undo a reset-prehistory command that does not have a permanent-p of t.




    acl2-sources/doc/HTML/RESET-PRINT-CONTROL.html0000664002132200015000000000064112222333520017637 0ustar kaufmannacl2 RESET-PRINT-CONTROL.html -- ACL2 Version 6.3

    RESET-PRINT-CONTROL

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/RESIZE-LIST.html0000664002132200015000000000170412222333530016521 0ustar kaufmannacl2 RESIZE-LIST.html -- ACL2 Version 6.3

    RESIZE-LIST

    list resizer in support of stobjs
    Major Section:  STOBJ
    

    (Resize-list lst n default-value) takes a list, lst, and a desired length, n, for the result list, as well as a default-value to use for the extra elements if n is greater than the length of lst.

    Resize-list has a guard of t. This function is called in the body of function, resize-<a> where <a> is an array field of a stobj. See stobj and see defstobj.




    acl2-sources/doc/HTML/REST.html0000664002132200015000000000114512222333524015526 0ustar kaufmannacl2 REST.html -- ACL2 Version 6.3

    REST

    rest (cdr) of the list
    Major Section:  ACL2-BUILT-INS
    

    In the logic, rest is just a macro for cdr.

    Rest is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/RESTORE-MEMOIZATION-SETTINGS.html0000664002132200015000000000164112222333517021126 0ustar kaufmannacl2 RESTORE-MEMOIZATION-SETTINGS.html -- ACL2 Version 6.3

    RESTORE-MEMOIZATION-SETTINGS

    restore the saved memoization settings
    Major Section:  EVENTS
    

    For background on memoization, see memoize.

    General Form:
    (restore-memoization-settings)
    

    Calls of this macro restore the memoization settings saved by save-and-clear-memoization-settings.

    Some Related Topics




    acl2-sources/doc/HTML/RESTRICT.html0000664002132200015000000000066712222333521016215 0ustar kaufmannacl2 RESTRICT.html -- ACL2 Version 6.3

    RESTRICT

    hints keyword :RESTRICT
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/RETRIEVE.html0000664002132200015000000000141512222333526016200 0ustar kaufmannacl2 RETRIEVE.html -- ACL2 Version 6.3

    RETRIEVE

    re-enter a (specified) proof-checker state
    Major Section:  PROOF-CHECKER
    

    Examples:
    (retrieve associativity-of-permutationp)
    retrieve
    
    General Form:
    (retrieve &optional name)
    
    See acl2-pc::retrieve, or use (help retrieve) inside the interactive proof-checker loop. Also see unsave.




    acl2-sources/doc/HTML/RETURN-LAST-TABLE.html0000664002132200015000000000343712222333531017362 0ustar kaufmannacl2 RETURN-LAST-TABLE.html -- ACL2 Version 6.3

    RETURN-LAST-TABLE

    install special raw Lisp behavior
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please first see return-last for relevant background.

    This table is for advanced users only, and requires an active trust tag (see defttag). We recommend that you do not modify this table directly, but instead use the macro defmacro-last. Here we augment that discussion with some highly technical observations that can probably be ignored if you use defmacro-last.

    This table has a :guard requiring that each key be a symbol defined in raw Lisp, generally as a macro, and requiring that each non-nil value be associated either with a symbol that names a macro defined in ACL2, or else with a list of one element containing such a symbol. The table can only be modified when there is an active trust tag; see defttag. If a key is associated with the value nil, then that key is treated as though it were not in the table.

    Note that keys of this table are not eligible to be bound by flet. The current value of this table may be obtained by evaluating the form (table-alist 'return-last-table (w state)). The built-in constant *initial-return-last-table* holds the initial value of this table.




    acl2-sources/doc/HTML/RETURN-LAST.html0000664002132200015000000005343412222333524016541 0ustar kaufmannacl2 RETURN-LAST.html -- ACL2 Version 6.3

    RETURN-LAST

    return the last argument, perhaps with side effects
    Major Section:  ACL2-BUILT-INS
    

    Return-last is an ACL2 function that returns its last argument. It is used to implement common utilities such as prog2$ and time$. For most users, this may already be more than one needs to know about return-last; for example, ACL2 tends to avoid printing calls of return-last in its output, printing calls of prog2$ or time$ (or other such utilities) instead.

    If you encounter a call of return-last during a proof, then you may find it most useful to consider return-last simply as a function defined by the following equation.

    (equal (return-last x y z) z)
    
    It may also be useful to know that unlike other ACL2 functions, return-last can take a multiple value as its last argument, in which case this multiple value is returned. The following contrived definition illustrates this point.
    ACL2 !>(defun foo (fn x y z)
             (return-last fn x (mv y z)))
    
    Since FOO is non-recursive, its admission is trivial.  We observe that
    the type of FOO is described by the theorem
    (AND (CONSP (FOO FN X Y Z)) (TRUE-LISTP (FOO FN X Y Z))).  We used
    primitive type reasoning.
    
    (FOO * * * *) => (MV * *).
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     FOO
    ACL2 !>(foo 'bar 3 4 5)
    (4 5)
    ACL2 !>(mv-let (a b)
                   (foo 'bar 3 4 5)
                   (cons b a))
    (5 . 4)
    ACL2 !>
    

    Most readers would be well served to avoid reading the rest of this documentation of return-last. For reference, however, below we document it in some detail. We include some discussion of its evaluation, in particular its behavior in raw Lisp, because we expect that most who read further are working with raw Lisp code (and trust tags).

    Return-last is an ACL2 function that can arise from macroexpansion of certain utilities that return their last argument, which may be a multiple value. Consider for example the simplest of these, prog2$:

    ACL2 !>:trans1 (prog2$ (cw "Some CW printing...~%") (+ 3 4))
     (RETURN-LAST 'PROGN
                  (CW "Some CW printing...~%")
                  (+ 3 4))
    ACL2 !>
    
    Notice that a call of prog2$ macroexpands to a call of return-last in which the first argument is (quote progn). Although return-last is a function in the ACL2 world, it is implemented ``under the hood'' as a macro in raw Lisp, as the following log (continuing the example above) illustrates.
    ACL2 !>:q
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ? [RAW LISP] (macroexpand-1 '(RETURN-LAST 'PROGN
                                               (CW "Some CW printing...~%")
                                               (+ 3 4)))
    (PROGN (LET ((*AOKP* T)) (CW "SOME CW PRINTING...~%")) (+ 3 4))
    T
    ? [RAW LISP]
    
    Thus, the original prog2$ call generates a corresponding call of progn in raw Lisp, which in turn causes evaluation of each argument and returns whatever is returned by evaluation of the last (second) argument.

    (Remark for those who use defattach. The binding of *aokp* to t is always included for the second argument as shown except when the first argument is of the form (QUOTE M) where M is a macro, or (less important) when the first argument is a symbol or a cons whose car is QUOTE. This binding allows ACL2 to use attachments in the second argument of return-last (hence, in the first argument of prog2$), even in contexts such as proofs in which attachments are normally not allowed. Those who use the experimental HONS version of ACL2 (see hons-and-memoization) will see an additional binding in the above single-step macroexpansion, which allows the storing of memoized results even when that would otherwise be prevented because of the use of attachments.)

    In general, a form (return-last (quote F) X Y) macroexpands to (F X Y), where F is defined in raw Lisp to return its last argument. The case that F is progn is a bit misleading, because it is so simple. More commonly, macroexpansion produces a call of a macro defined in raw Lisp that may produce side effects. Consider for example the ACL2 utility with-guard-checking, which is intended to change the guard-checking mode to the indicated value (see with-guard-checking).

    ACL2 !>(with-guard-checking :none (car 3)) ; no guard violation
    NIL
    ACL2 !>:trans1 (with-guard-checking :none (car 3))
     (WITH-GUARD-CHECKING1 (CHK-WITH-GUARD-CHECKING-ARG :NONE)
                           (CAR 3))
    ACL2 !>:trans1 (WITH-GUARD-CHECKING1 (CHK-WITH-GUARD-CHECKING-ARG :NONE)
                                         (CAR 3))
     (RETURN-LAST 'WITH-GUARD-CHECKING1-RAW
                  (CHK-WITH-GUARD-CHECKING-ARG :NONE)
                  (CAR 3))
    ACL2 !>:q
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ? [RAW LISP] (macroexpand-1
                  '(RETURN-LAST 'WITH-GUARD-CHECKING1-RAW
                                 (CHK-WITH-GUARD-CHECKING-ARG :NONE)
                                 (CAR 3)))
    (WITH-GUARD-CHECKING1-RAW (CHK-WITH-GUARD-CHECKING-ARG :NONE) (CAR 3))
    T
    ? [RAW LISP] (pprint
                  (macroexpand-1
                   '(WITH-GUARD-CHECKING1-RAW
                     (CHK-WITH-GUARD-CHECKING-ARG :NONE)
                     (CAR 3))))
    
    (LET ((ACL2_GLOBAL_ACL2::GUARD-CHECKING-ON
           (CHK-WITH-GUARD-CHECKING-ARG :NONE)))
      (DECLARE (SPECIAL ACL2_GLOBAL_ACL2::GUARD-CHECKING-ON))
      (CAR 3))
    ? [RAW LISP]
    
    The above raw Lisp code binds the state global variable guard-checking-on to :none, as chk-with-guard-checking-arg is just the identity function except for causing a hard error for an illegal input.

    The intended use of return-last is that the second argument is evaluated first in a normal manner, and then the third argument is evaluated in an environment that may depend on the value of the second argument. (For example, the macro with-prover-time-limit macroexpands to a call of return-last with a first argument of 'WITH-PROVER-TIME-LIMIT1-RAW, a second argument that evaluates to a numeric time limit, and a third argument that is evaluated in an environment where the theorem prover is restricted to avoid running longer than that time limit.) Although this intended usage model is not strictly enforced, it is useful to keep in mind in the following description of how calls of return-last are handled by the ACL2 evaluator.

    When a form is submitted in the top-level loop, it is handled by ACL2's custom evaluator. That evaluator is specified to respect the semantics of the expression supplied to it: briefly put, if an expression E evaluates to a value V, then the equality (equal E (quote V)) should be a theorem. Notice that this specification does not discuss the side-effects that may occur when evaluating a call of return-last, so we discuss that now. Suppose that the ACL2 evaluator encounters the call (return-last 'fn expr1 expr2). First it evaluates expr1. If this evaluation succeeds without error, then it constructs an expression of the form (fn *x* ev-form), where *x* is a Lisp variable bound to the result of evaluating expr1 and ev-form is a call of the evaluator for expr2. (Those who want implementation details are invited to look at function ev-rec-return-last in ACL2 source file translate.lisp.) There are exceptions if fn is progn, ec-call1-raw, with-guard-checking1-raw, or mbe1-raw, but the main idea is the same: do a reasonable job emulating the behavior of a raw-Lisp call of return-last.

    The following log shows how a time$ call can generate a call of the evaluator for the last argument of return-last (arguent expr2, above). We use :trans1 to show single-step macroexpansions, which indicate how a call of time$ expands to a call of return-last. The implementation actually binds the Lisp variable *RETURN-LAST-ARG3* to expr2 before calling the ACL2 evaluator, ev-rec.

    ACL2 !>:trans1 (time$ (+ 3 4))
     (TIME$1 (LIST 0 NIL NIL NIL NIL)
             (+ 3 4))
    ACL2 !>:trans1 (TIME$1 (LIST 0 NIL NIL NIL NIL)
                           (+ 3 4))
     (RETURN-LAST 'TIME$1-RAW
                  (LIST 0 NIL NIL NIL NIL)
                  (+ 3 4))
    ACL2 !>(time$ (+ 3 4))
    ; (EV-REC *RETURN-LAST-ARG3* ...) took
    ; 0.00 seconds realtime, 0.00 seconds runtime
    ; (1,120 bytes allocated).
    7
    ACL2 !>
    

    We now show how things can go wrong in other than the ``intended use'' case described above. In the example below, the macro mac-raw is operating directly on the syntactic representation of its first argument, which it obtains of course as the second argument of a return-last call. Again this ``intended use'' of return-last requires that argument to be evaluated and then only its result is relevant; its syntax is not supposed to matter. We emphasize that only top-level evaluation depends on this ``intended use''; once evaluation is passed to Lisp, the issue disappears. We illustrate below how to use the top-level utility to avoid this issue; see top-level. The example uses the utility defmacro-last to ``install'' special handling of the raw-Lisp macro mac-raw by return-last; later below we discuss defmacro-last.

    ACL2 !>(defttag t)
    
    TTAG NOTE: Adding ttag :T from the top level loop.
     T
    ACL2 !>(progn!
             (set-raw-mode t)
             (defmacro mac-raw (x y)
               `(progn (print (quote ,(cadr x)))
                       (terpri) ; newline
                       ,y)))
    
    Summary
    Form:  ( PROGN! (SET-RAW-MODE T) ...)
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     NIL
    ACL2 !>(defmacro-last mac)
    [[ ... output omitted ... ]]
     RETURN-LAST-TABLE
    ACL2 !>(return-last 'mac-raw '3 nil)
    
    ***********************************************
    ************ ABORTING from raw Lisp ***********
    Error:  Fault during read of memory address #x120000300006
    ***********************************************
    
    If you didn't cause an explicit interrupt (Control-C),
    then the root cause may be call of a :program mode
    function that has the wrong guard specified, or even no
    guard specified (i.e., an implicit guard of t).
    See :DOC guards.
    
    To enable breaks into the debugger (also see :DOC acl2-customization):
    (SET-DEBUGGER-ENABLE T)
    ACL2 !>(top-level (return-last 'mac-raw '3 nil))
    
    3
    NIL
    ACL2 !>
    

    We next describe how to extend the behavior of return-last. This requires an active trust tag (see defttag), and is accomplished by extending a table provided by ACL2, see return-last-table. Rather than using table events directly for this purpose, it is probably more convenient to use a macro, defmacro-last. We describe the community book books/misc/profiling.lisp in order to illustrate how this works. The events in that book include the following, which are described below.

    (defttag :profiling)
    
    (progn!
     (set-raw-mode t)
     (load (concatenate 'string (cbd) "profiling-raw.lsp")))
    
    (defmacro-last with-profiling)
    
    The first event introduces a trust tag. The second loads a file into raw Lisp that defines a macro, with-profiling-raw, which can do profiling for the form to be evaluated. The third introduces an ACL2 macro with-profiling, whose calls expand into calls of the form (return-last (quote with-profiling-raw) & &). The third event also extends return-last-table so that these calls will expand in raw Lisp to calls of with-profiling-raw.

    The example above illustrates the following methodology for introducing a macro that returns its last argument but produces useful side-effects with raw Lisp code.

    (1) Introduce a trust tag (see defttag).

    (2) Using progn!, load into raw Lisp a file defining a macro, RAW-NAME, that takes two arguments, returning its last (second) argument but using the first argument to produce desired side effects during evaluation of that last argument.

    (3) Evaluate the form (defmacro-last NAME :raw RAW-NAME). This will introduce NAME as an ACL2 macro that expands to a corresponding call of RAW-NAME (see (2) above) in raw Lisp. The specification of keyword value of :raw as RAW-NAME may be omitted if RAW-NAME is the result of modifying the symbol NAME by suffixing the string "-RAW" to the symbol-name of NAME.

    WARNING: Not every use of return-last can be soundly evaluated outside a function body. The reason is that ACL2's evaluator, ev-rec, recurs through terms that are presented in the top-level loop, and handles return-last calls in a special manner: basically, the call of ev-rec on the form (return-last 'mac-raw x y) leads to evaluation of a macro call of the form (mac-raw *return-last-arg2* (ev-rec ...)), where *return-last-arg2* is a global variable bound to the result of evaluating x with ev-rec. Consider the following example.

    (defttag t)
    (set-raw-mode-on state)
    (defmacro mac-raw (str y) ; print message is an atom
     `(let ((result (consp ,y))
            (str ,str))
        (or result
            (prog2$ (fmx ,str ',y)
                    nil))))
    (set-raw-mode-off state)
    (defmacro-last mac)
    ; Horrible error:
    (mac "Not a cons: ~x0~%" 17)
    ; Works, but probably many would consider it awkward to use top-level:
    (top-level (mac "Not a cons: ~x0~%" 17))
    
    In such cases we suggest supplying keyword :top-level-ok nil to the call of defmacro-last, for example:
    (defmacro-last mac :top-level-ok nil)
    
    Then any attempt to call mac at the top level, as opposed to inside a function body, will cause a clean error before evaluation begins.

    It is useful to explore what is done by defmacro-last.

    ACL2 !>:trans1 (defmacro-last with-profiling)
     (PROGN (DEFMACRO WITH-PROFILING (X Y)
                      (LIST 'RETURN-LAST
                            (LIST 'QUOTE 'WITH-PROFILING-RAW)
                            X Y))
            (TABLE RETURN-LAST-TABLE 'WITH-PROFILING-RAW
                   'WITH-PROFILING))
    ACL2 !>:trans1 (with-profiling '(assoc-eq fgetprop rewrite) (mini-proveall))
     (RETURN-LAST 'WITH-PROFILING-RAW
                  '(ASSOC-EQ FGETPROP REWRITE)
                  (MINI-PROVEALL))
    ACL2 !>:q
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ? [RAW LISP] (macroexpand-1
                  '(RETURN-LAST 'WITH-PROFILING-RAW
                                 '(ASSOC-EQ FGETPROP REWRITE)
                                 (MINI-PROVEALL)))
    (WITH-PROFILING-RAW '(ASSOC-EQ FGETPROP REWRITE) (MINI-PROVEALL))
    T
    ? [RAW LISP]
    
    To understand the macro with-profiling-raw you could look at the community book loaded above: books/misc/profiling-raw.lsp.

    We mentioned above that ACL2 tends to print calls of prog2$ or time$ (or other such utilities) instead of calls of return-last. Here we elaborate that point. ACL2's `untranslate' utility treats (return-last (quote F) X Y) as (G X Y) if F corresponds to the symbol G in return-last-table. However, it is generally rare to encounter such a term during a proof, since calls of return-last are generally expanded away early during a proof.

    Calls of return-last that occur in code -- forms submitted in the top-level ACL2 loop, and definition bodies other than those marked as non-executable (see defun-nx) -- have the following restriction: if the first argument is of the form (quote F), then F must be an entry in return-last-table. There are however four exceptions: the following symbols are considered to be keys of return-last-table even if they are no longer associated with non-nil values, say because of a table event with keyword :clear.

    * progn, associated with prog2$
    * mbe1-raw, associated with mbe1, a version of mbe
    * ec-call1-raw, associated with ec-call1 (a variant of ec-call)
    * with-guard-checking1-raw, associated with with-guard-checking1 (a variant of with-guard-checking)

    Note that because of its special status, it is illegal to trace return-last.

    We conclude by warning that as a user, you take responsibility for not compromising the soundness or error handling of ACL2 when you define a macro in raw Lisp and especially when you install it as a key of return-last-table, either directly or (more likely) using defmacro-last. In particular, be sure that you are defining a macro of two arguments that always returns the value of its last argument, returning the complete multiple value if that last argument evaluates to a multiple value.

    The following is correct, and illustrates care taken to return multiple values.

    :q
    (defmacro my-time1-raw (val form)
      (declare (ignore val))
      `(let  ((start-time (get-internal-run-time))
              (result (multiple-value-list ,form))
              (end-time (get-internal-run-time)))
         (format t "Total time: ~s~%"
                 (float (/ (- end-time start-time)
                           internal-time-units-per-second)))
         (values-list result)))
    (lp)
    (defttag t)
    (defmacro-last my-time1)
    (defmacro my-time (form)
      `(my-time1 nil ,form))
    
    Then for example:
    ACL2 !>(my-time (equal (make-list 1000000) (make-list 1000000)))
    Total time: 0.12
    T
    ACL2 !>
    
    But if instead we provide the following more naive implementation, of the above raw Lisp macro, the above evaluation can produce an error, for example if the host Lisp is CCL.
    (defmacro my-time1-raw (val form)
        (declare (ignore val))
        `(let  ((start-time (get-internal-run-time))
                (result ,form)
                (end-time (get-internal-run-time)))
           (format t "Total time: ~s~%"
                   (float (/ (- end-time start-time)
                             internal-time-units-per-second)))
           result)) ; WRONG -- need multiple values returned!
    

    Here is a second, similar example. This time we'll start with the error; can you spot it?

    (defttag t)
    (progn!
     (set-raw-mode t)
     (defmacro foo-raw (x y)
       `(prog1
            ,y
          (cw "Message showing argument 1: ~x0~%" ,x))))
    (defmacro-last foo)
    
    We then can wind up with a hard Lisp error:
    ACL2 !>(foo 3 (mv 4 5))
    Message showing argument 1: 3
    
    ***********************************************
    ************ ABORTING from raw Lisp ***********
    Error:  value NIL is not of the expected type REAL.
    ***********************************************
    
    If you didn't cause an explicit interrupt (Control-C),
    then the root cause may be call of a :program mode
    function that has the wrong guard specified, or even no
    guard specified (i.e., an implicit guard of t).
    See :DOC guards.
    
    To enable breaks into the debugger (also see :DOC acl2-customization):
    (SET-DEBUGGER-ENABLE T)
    ACL2 !>
    
    Here is a corrected version of the above macro. The point here is that prog1 returns a single value, while our-multiple-value-prog1 returns all the values that are returned by its first argument.
    (progn!
     (set-raw-mode t)
     (defmacro foo-raw (x y)
       `(our-multiple-value-prog1 ;; better
         ,y
         (cw "Message showing argument 1: ~x0~%" ,x))))
    

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/REVAPPEND.html0000664002132200015000000000245612222333524016303 0ustar kaufmannacl2 REVAPPEND.html -- ACL2 Version 6.3

    REVAPPEND

    concatentate the reverse of one list to another
    Major Section:  ACL2-BUILT-INS
    

    (Revappend x y) concatenates the reverse of the list x to y, which is also typically a list.

    The following theorem characterizes this English description.

    (equal (revappend x y)
           (append (reverse x) y))
    
    Hint: This lemma follows immediately from the definition of reverse and the following lemma.
    (defthm revappend-append
      (equal (append (revappend x y) z)
             (revappend x (append y z))))
    

    The guard for (revappend x y) requires that x is a true list.

    Revappend is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/REVERSE.html0000664002132200015000000000145512222333524016070 0ustar kaufmannacl2 REVERSE.html -- ACL2 Version 6.3

    REVERSE

    reverse a list or string
    Major Section:  ACL2-BUILT-INS
    

    (Reverse x) is the result of reversing the order of the elements of the list or string x.

    The guard for reverse requires that its argument is a true list or a string.

    Reverse is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/REWRITE-STACK-LIMIT.html0000664002132200015000000000356512222333521017616 0ustar kaufmannacl2 REWRITE-STACK-LIMIT.html -- ACL2 Version 6.3

    REWRITE-STACK-LIMIT

    limiting the stack depth of the ACL2 rewriter
    Major Section:  MISCELLANEOUS
    

    ACL2 users can create rules of class :rewrite (see rule-classes) that have the potential of causing an infinite loop in the ACL2 rewriter. This could lead to Lisp stack overflows and even segmentation faults. For this reason, the depth of certain calls of functions in the ACL2 rewriter is limited by default using the value of the form (rewrite-stack-limit (w state)). To see the limit in action, execute the following forms.

    (defthm app-assoc-1
      (equal (append (append x y) z)
             (append x y z)))
    (defthm app-assoc-2
      (equal (append x y z)
             (append (append x y) z)))
    (thm (equal (append a b c) xxx)
         ; try without these hints to see a slightly different error message
         :hints (("Goal" :do-not '(preprocess))))
    
    The ensuing error message shows a stack of one greater than the value of (rewrite-stack-limit (w state)), which by default is the value of the constant *default-rewrite-stack-limit*. The error message also explains how to use :brr t and (cw-gstack) to find looping rewrite rules.

    This limit can be changed; see set-rewrite-stack-limit.

    For a related limit, see backchain-limit.




    acl2-sources/doc/HTML/REWRITE.html0000664002132200015000000002125312222333530016071 0ustar kaufmannacl2 REWRITE.html -- ACL2 Version 6.3

    REWRITE

    make some :rewrite rules (possibly conditional ones)
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    This doc topic discusses the rule-class :rewrite. If you want a general discussion of how rewriting works in ACL2 and some guidance on how to construct effective rewrite rules, see introduction-to-rewrite-rules-part-1 and then see introduction-to-rewrite-rules-part-2.

    Examples:
    
    (defthm plus-commutes                 ; Replace (+ a b) by (+ b a) provided
      (equal (+ x y) (+ y x)))            ; certain heuristics approve the
                                          ; permutation.
    
    (defthm plus-commutes                 ; equivalent to the above
      (equal (+ x y) (+ y x))
      :rule-classes ((:rewrite :corollary (equal (+ x y) (+ y x))
                               :loop-stopper ((x y binary-+))
                               :match-free :all)))
    
    (defthm append-nil                    ; Replace (append a nil) by a, if
      (implies (true-listp x)             ; (true-listp a) rewrites to t.
               (equal (append x nil) x)))
    
    (defthm append-nil                    ; as above, but with defaults and
      (implies (true-listp x)             ; a backchain limit
               (equal (append x nil) x))
      :rule-classes ((:rewrite :corollary (implies (true-listp x)
                                                   (equal (append x nil) x))
                               :backchain-limit-lst (3) ; or equivalently, 3
                               :match-free :all)))
    
    (defthm member-append                 ; Replace (member e (append b c)) by
      (implies                            ; (or (member e b) (member e c) in
       (and                               ; contexts in which propositional
        (true-listp x)                    ; equivalence is sufficient, provided
        (true-listp y))                   ; b and c are true-lists.
       (iff (member e (append x y))
            (or (member e x) (member e y)))))
    
    
    

    Some Related Topics

    • BIND-FREE -- to bind free variables of a rewrite, definition, or linear rule

    • CASE-SPLIT -- like force but immediately splits the top-level goal on the hypothesis

    • DOUBLE-REWRITE -- cause a term to be rewritten twice

    • FORCE -- identity function used to force a hypothesis

    • FREE-VARIABLES -- free variables in rules

    • SYNTAXP -- attach a heuristic filter on a rule

    General Form: (and ... (implies (and ...hi...) (implies (and ...hk...) (and ... (equiv lhs rhs) ...))) ...)
    Note: One :rewrite rule class object might create many rewrite rules from the :corollary formula. To create the rules, we first translate the formula, expanding all macros (see trans) and also expanding away calls of all so-called ``guard holders,'' mv-list and return-last (the latter resulting for example from calls of prog2$, mbe, or ec-call), as well as expansions of the macro `the'. Next, we eliminate all lambdas; one may think of this step as simply substituting away every let, let*, and mv-let in the formula. We then flatten the AND and IMPLIES structure of the formula; for example, if the hypothesis or conclusion is of the form (and (and term1 term2) term3), then we replace that by the ``flat'' term (and term1 term2 term3). (The latter is actually an abbreviation for the right-associated term (and term1 (and term2 term3)).) The result is a conjunction of formulas, each of the form
    (implies (and h1 ... hn) concl)
    
    where no hypothesis is a conjunction and concl is neither a conjunction nor an implication. If necessary, the hypothesis of such a conjunct may be vacuous. We then further coerce each concl into the form (equiv lhs rhs), where equiv is a known equivalence relation, by replacing any concl not of that form by (iff concl t). A concl of the form (not term) is considered to be of the form (iff term nil). By these steps we reduce the given :corollary to a sequence of conjuncts, each of which is of the form
    (implies (and h1 ... hn)
             (equiv lhs rhs))
    
    where equiv is a known equivalence relation. See equivalence for a general discussion of the introduction of new equivalence relations. At this point, we check whether lhs and rhs are the same term; if so, we cause an error, since this rule will loop. (But this is just a basic check; the rule could loop in other cases, for example if rhs is an instance of lhs; see loop-stopper.)

    We create a :rewrite rule for each such conjunct, if possible, and otherwise cause an error. It is possible to create a rewrite rule from such a conjunct provided lhs is not a variable, a quoted constant, a let-expression, a lambda application, or an if-expression.

    A :rewrite rule is used when any instance of the lhs occurs in a context in which the equivalence relation is an admissible congruence relation. First, we find a substitution that makes lhs equal to the target term. Then we attempt to relieve the instantiated hypotheses of the rule. Hypotheses that are fully instantiated are relieved by recursive rewriting. Hypotheses that contain ``free variables'' (variables not assigned by the unifying substitution) are relieved by attempting to guess a suitable instance so as to make the hypothesis equal to some known assumption in the context of the target. If the hypotheses are relieved, and certain restrictions that prevent some forms of infinite regress are met (see loop-stopper), the target is replaced by the instantiated rhs, which is then recursively rewritten.

    ACL2's rewriting process has undergone some optimization. In particular, when a term t1 is rewritten to a new term t2, the rewriter is then immediately applied to t2. On rare occasions you may find that you do not want this behavior, in which case you may wish to use a trick involving hide; see meta, near the end of that documentation.

    In another optimization, when the hypotheses and right-hand side are rewritten, ACL2 does not really first apply the substitution and then rewrite; instead, it as it rewrites those terms it looks up the already rewritten values of the bound variables. Sometimes you may want those bindings rewritten again, e.g., because the variables occur in slots that admit additional equivalence relations. See double-rewrite.

    See introduction-to-rewrite-rules-part-1 and see introduction-to-rewrite-rules-part-2 for an extended discussion of how to create effective rewrite rules.




    acl2-sources/doc/HTML/RFIX.html0000664002132200015000000000163212222333524015522 0ustar kaufmannacl2 RFIX.html -- ACL2 Version 6.3

    RFIX

    coerce to a rational number
    Major Section:  ACL2-BUILT-INS
    

    Rfix simply returns any rational number argument unchanged, returning 0 on a non-rational argument. Also see nfix, see ifix, see realfix, and see fix for analogous functions that coerce to a natural number, an integer, a real, and a number, respectively.

    Rfix has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ROUND.html0000664002132200015000000000264212222333524015643 0ustar kaufmannacl2 ROUND.html -- ACL2 Version 6.3

    ROUND

    division returning an integer by rounding off
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    ACL2 !>(round 14 3)
    5
    ACL2 !>(round -14 3)
    -5
    ACL2 !>(round 14 -3)
    -5
    ACL2 !>(round -14 -3)
    5
    ACL2 !>(round 13 3)
    4
    ACL2 !>(round -13 3)
    -4
    ACL2 !>(round 13 -3)
    -4
    ACL2 !>(round -13 -3)
    4
    ACL2 !>(round -15 -3)
    5
    ACL2 !>(round 15 -2)
    -8
    
    (Round i j) is the result of taking the quotient of i and j and rounding off to the nearest integer. When the quotient is exactly halfway between consecutive integers, it rounds to the even one.

    The guard for (round i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero.

    Round is a Common Lisp function. See any Common Lisp documentation for more information. However, note that unlike Common Lisp, the ACL2 round function returns only a single value,

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/RULE-CLASSES.html0000664002132200015000000006141512222333530016616 0ustar kaufmannacl2 RULE-CLASSES.html -- ACL2 Version 6.3

    RULE-CLASSES

    adding rules to the database
    Major Section:  ACL2 Documentation
    

    Example Form (from community book finite-set-theory/total-ordering.lisp):
    (defthm <<-trichotomy
      (implies (and (ordinaryp x)
                    (ordinaryp y))
               (or (<< x y)
                   (equal x y)
                   (<< y x)))
      :rule-classes
      ((:rewrite :corollary
                 (implies (and (ordinaryp x)
                               (ordinaryp y)
                               (not (<< x y))
                               (not (equal x y)))
                          (<< y x)))))
    
    General Form:
    a true list of rule class objects as defined below
    
    Special Cases:
    a symbol abbreviating a single rule class object
    

    When defthm is used to prove a named theorem, rules may be derived from the proved formula and stored in the database. The user specifies which kinds of rules are to be built, by providing a list of rule class names or, more generally, rule class objects, which name the kind of rule to build and optionally specify varioius attributes of the desired rule. The rule class names are :REWRITE, :BUILT-IN-CLAUSE, :CLAUSE-PROCESSOR, :COMPOUND-RECOGNIZER, :CONGRUENCE, :DEFINITION, :ELIM, :EQUIVALENCE, :FORWARD-CHAINING, :GENERALIZE, :INDUCTION, :LINEAR, :META, :REFINEMENT, :TAU-SYSTEM, :TYPE-PRESCRIPTION, :TYPE-SET-INVERTER, and :WELL-FOUNDED-RELATION. Some classes require the user-specification of certain class-specific attributes. Each class of rule affects the theorem prover's behavior in a different way, as discussed in the corresponding documentation topic. In this topic we discuss the various attributes that may be attached to rule classes.

    A rule class object is either one of the :class keywords or else is a list of the form shown below. Those fields marked with ``(!)'' are required when the :class is as indicated.

    (:class
      :COROLLARY term
      :TRIGGER-FNS (fn1 ... fnk) ; provided :class = :META (!)
      :TRIGGER-TERMS (t1 ... tk) ; provided :class = :FORWARD-CHAINING
                                 ;       or :class = :LINEAR
      :TYPE-SET n                ; provided :class = :TYPE-SET-INVERTER
      :TYPED-TERM term           ; provided :class = :TYPE-PRESCRIPTION
      :CLIQUE (fn1 ... fnk)      ; provided :class = :DEFINITION
      :CONTROLLER-ALIST alist    ; provided :class = :DEFINITION
      :INSTALL-BODY directive    ; provided :class = :DEFINITION
      :LOOP-STOPPER alist        ; provided :class = :REWRITE
      :PATTERN term              ; provided :class = :INDUCTION (!)
      :CONDITION term            ; provided :class = :INDUCTION
      :SCHEME term               ; provided :class = :INDUCTION (!)
      :MATCH-FREE all-or-once    ; provided :class = :REWRITE
                                         or :class = :LINEAR
                                         or :class = :FORWARD-CHAINING
      :BACKCHAIN-LIMIT-LST limit ; provided :class = :REWRITE
                                         or :class = :META
                                         or :class = :LINEAR
                                         or :class = :TYPE-PRESCRIPTION
      :HINTS hints               ; provided instrs = nil
      :INSTRUCTIONS instrs       ; provided  hints = nil
      :OTF-FLG flg)
    
    When rule class objects are provided by the user, most of the fields are optional and their values are computed in a context sensitive way. When a :class keyword is used as a rule class object, all relevant fields are determined contextually. Each rule class object in :rule-classes causes one or more rules to be added to the database. The :class keywords are documented individually under the following names. Note that when one of these names is used as a :class, it is expected to be in the keyword package (i.e., the names below should be preceded by a colon but the ACL2 documentation facilities do not permit us to use keywords below).

    Some Related Topics

    • BUILT-IN-CLAUSE -- to build a clause into the simplifier

    • CLAUSE-PROCESSOR -- make or apply a :clause-processor rule (goal-level simplifier)

    • COMPOUND-RECOGNIZER -- make a rule used by the typing mechanism

    • CONGRUENCE -- the relations to maintain while simplifying arguments

    • DEFINITION -- make a rule that acts like a function definition

    • ELIM -- make a destructor elimination rule

    • EQUIVALENCE -- mark a relation as an equivalence relation

    • FORWARD-CHAINING -- make a rule to forward chain when a certain trigger arises

    • FREE-VARIABLES -- free variables in rules

    • GENERALIZE -- make a rule to restrict generalizations

    • INDUCTION -- make a rule that suggests a certain induction

    • LINEAR -- make some arithmetic inequality rules

    • META -- make a :meta rule (a hand-written simplifier)

    • REFINEMENT -- record that one equivalence relation refines another

    • REWRITE -- make some :rewrite rules (possibly conditional ones)

    • TAU-SYSTEM -- make a rule for the ACL2 ``type checker''

    • TYPE-PRESCRIPTION -- make a rule that specifies the type of a term

    • TYPE-SET-INVERTER -- exhibit a new decoding for an ACL2 type-set

    • WELL-FOUNDED-RELATION -- show that a relation is well-founded on a set

    See also force, case-split, syntaxp, and bind-free for ``pragmas'' one can wrap around individual hypotheses of certain classes of rules to affect how the hypothesis is relieved.

    Before we get into the discussion of rule classes, let us return to an important point. In spite of the large variety of rule classes available, at present we recommend that new ACL2 users rely almost exclusively on (conditional) rewrite rules. A reasonable but slightly bolder approach is to use :type-prescription and :forward-chaining rules for ``type-theoretic'' rules, especially ones whose top-level function symbol is a common one like true-listp or consp; see type-prescription and see forward-chaining. However, the rest of the rule classes are really not intended for widespread use, but rather are mainly for experts.

    We expect that we will write more about the question of which kind of rule to use. For now: when in doubt, use a :rewrite rule.

    :Rule-classes is an optional keyword argument of the defthm (and defaxiom) event. In the following, let name be the name of the event and let thm be the formula to be proved or added as an axiom.

    If :rule-classes is not specified in a defthm (or defaxiom) event, it is as though what was specified was to make one or more :rewrite rules, i.e., as though :rule-classes ((:rewrite)) had been used. Use :rule-classes nil to specify that no rules are to be generated.

    If :rule-classes class is specified, where class is a non-nil symbol, it is as though :rule-classes ((class)) had been used. Thus, :rule-classes :forward-chaining is equivalent to :rule-classes ((:forward-chaining)).

    We therefore now consider :rule-classes as a true list. If any element of that list is a keyword, replace it by the singleton list containing that keyword. Thus, :rule-classes (:rewrite :elim) is the same as :rule-classes ((:rewrite) (:elim)).

    Each element of the expanded value of :rule-classes must be a true list whose car is one of the rule class keyword tokens listed above, e.g., :rewrite, :elim, etc., and whose cdr is a ``keyword alist'' alternately listing keywords and values. The keywords in this alist must be taken from those shown below. They may be listed in any order and most may be omitted, as specified below.

    :Corollary -- its value, term, must be a term. If omitted, this field defaults to thm. The :corollary of a rule class object is the formula actually used to justify the rule created and thus determines the form of the rule. Nqthm provided no similar capability: each rule was determined by thm, the theorem or axiom added. ACL2 permits thm to be stated ``elegantly'' and then allows the :corollary of a rule class object to specify how that elegant statement is to be interpreted as a rule. For the rule class object to be well-formed, its (defaulted) :corollary, term, must follow from thm. Unless term follows trivially from thm using little more than propositional logic, the formula (implies thm term) is submitted to the theorem prover and the proof attempt must be successful. During that proof attempt the values of :hints, :instructions, and :otf-flg, as provided in the rule class object, are provided as arguments to the prover. Such auxiliary proofs give the sort of output that one expects from the prover. However, as noted above, corollaries that follow trivially are not submitted to the prover; thus, such corollaries cause no prover output.

    Note that before term is stored, all calls of macros in it are expanded away. See trans.

    :Hints, :instructions, :otf-flg -- the values of these fields must satisfy the same restrictions placed on the fields of the same names in defthm. These values are passed to the recursive call of the prover used to establish that the :corollary of the rule class object follows from the theorem or axiom thm.

    :Type-set -- this field may be supplied only if the :class is :type-set-inverter. When provided, the value must be a type-set, an integer in a certain range. If not provided, an attempt is made to compute it from the corollary. See type-set-inverter.

    :Typed-term -- this field may be supplied only if the :class is :type-prescription. When provided, the value is the term for which the :corollary is a type-prescription lemma. If no :typed-term is provided in a :type-prescription rule class object, we try to compute heuristically an acceptable term. See type-prescription.

    :Trigger-terms -- this field may be supplied only if the :class is :forward-chaining or :linear. When provided, the value is a list of terms, each of which is to trigger the attempted application of the rule. If no :trigger-terms is provided, we attempt to compute heuristically an appropriate set of triggers. See forward-chaining or see linear.

    :Trigger-fns -- this field must (and may only) be supplied if the :class is :meta. Its value must be a list of function symbols (except that a macro alias can stand in for a function symbol; see add-macro-alias). Terms with these symbols trigger the application of the rule. See meta.

    :Clique and :controller-alist -- these two fields may only be supplied if the :class is :definition. If they are omitted, then ACL2 will attempt to guess them. Suppose the :corollary of the rule is (implies hyp (equiv (fn a1 ... an) body)). The value of the :clique field should be a true list of function symbols, and if non-nil must include fn. These symbols are all the members of the mutually recursive clique containing this definition of fn. That is, a call of any function in :clique is considered a ``recursive call'' for purposes of the expansion heuristics. The value of the :controller-alist field should be an alist that maps each function symbol in the :clique to a list of t's and nil's of length equal to the arity of the function. For example, if :clique consists of just two symbols, fn1 and fn2, of arities 2 and 3 respectively, then ((fn1 t nil) (fn2 nil t t)) is a legal value of :controller-alist. The value associated with a function symbol in this alist is a ``mask'' specifying which argument slots of the function ``control'' the recursion for heuristic purposes. Sloppy choice of :clique or :controller-alist can result in infinite expansion and stack overflow.

    :Install-body -- this field may only be supplied if the :class is :definition. Its value must be t, nil, or the default, :normalize. A value of t or :normalize will cause ACL2 to install this rule as the new body of the function being ``defined'' (fn in the paragraph just above); hence this definition will be installed for future :expand hints. Furthermore, if this field is omitted or the value is :normalize, then this definition will be simplified using the so-called ``normalization'' procedure that is used when processing definitions made with defun. You must explicitly specify :install-body nil in the following cases: fn (as above) is a member of the value of constant *definition-minimal-theory*, the arguments are not a list of distinct variables, equiv (as above) is not equal, or there are free variables in the hypotheses or right-hand side (see free-variables). However, supplying :install-body nil will not affect the rewriter's application of the :definition rule, other than to avoid using the rule to apply :expand hints. If a definition rule equates (f a1 ... ak) with body but there are hypotheses, hyps, then :expand hints will replace terms (f term1 ... termk) by corresponding terms (if hyps body (hide (f term1 ... termk))).

    :Loop-stopper -- this field may only be supplied if the class is :rewrite. Its value must be a list of entries each consisting of two variables followed by a (possibly empty) list of functions, for example ((x y binary-+) (u v foo bar)). It will be used to restrict application of rewrite rules by requiring that the list of instances of the second variables must be ``smaller'' than the list of instances of the first variables in a sense related to the corresponding functions listed; see loop-stopper. The list as a whole is allowed to be nil, indicating that no such restriction shall be made. Note that any such entry that contains a variable not being instantiated, i.e., not occurring on the left side of the rewrite rule, will be ignored. However, for simplicity we merely require that every variable mentioned should appear somewhere in the corresponding :corollary formula.

    :Pattern, :Condition, :Scheme -- the first and last of these fields must (and may only) be supplied if the class is :induction. :Condition is optional but may only be supplied if the class is :induction. The values must all be terms and indicate, respectively, the pattern to which a new induction scheme is to be attached, the condition under which the suggestion is to be made, and a term which suggests the new scheme. See induction.

    :Match-free -- this field must be :all or :once and may be supplied only if the :class is either :rewrite, :linear, or :forward-chaining. (This field is not implemented for other rule classes, including the :type-prescription rule class.) See free-variables for a description of this field. Note: Although this field is intended to be used for controlling retries of matching free variables in hypotheses, it is legal to supply it even if there are no such free variables. This can simplify the automated generation of rules, but note that when :match-free is supplied, the warning otherwise provided for the presence of free variables in hypotheses will be suppressed.

    :Backchain-limit-lst -- this field may be supplied only if the :class is either :rewrite, :meta, :linear, or :type-prescription. It is further required either only one rule is generated from the formula or, at least, every such rule has the same list of hypotheses. The value for :backchain-limit-lst must be nil; a non-negative integer; or, except in the case of :meta rules, a true list each element of which is either nil or a non-negative integer. If it is a list, its length must be equal to the number of hypotheses of the rule and each item in the list is the ``backchain limit'' associated with the corresponding hypothesis. If backchain-limit-lst is a non-negative integer, it is defaulted to a list of the appropriate number of repetitions of that integer. The backchain limit of a hypothesis is used to limit the effort that ACL2 will expend when relieving the hypothesis. If it is NIL, no new limits are imposed; if it is an integer, the hypothesis will be limited to backchaining at most that many times. Note that backchaining may be further limited by a global backchain-limit; see backchain-limit for details. For different ways to reign in the rewriter, see rewrite-stack-limit and see set-prover-step-limit. Jared Davis has pointed out that you can set the :backchain-limit-lst to 0 to avoid any attempt to relieve forced hypotheses, which can lead to a significant speed-up in some cases.

    Once thm has been proved (in the case of defthm) and each rule class object has been checked for well-formedness (which might require additional proofs), we consider each rule class object in turn to generate and add rules. Let :class be the class keyword token of the ith class object (counting from left to right). Generate the rune (:class name . x), where x is nil if there is only one class and otherwise x is i. Then, from the :corollary of that object, generate one or more rules, each of which has the name (:class name . x). See the :doc entry for each rule class to see how formulas determine rules. Note that it is in principle possible for several rules to share the same name; it happens whenever a :corollary determines more than one rule. This in fact only occurs for :rewrite, :linear, and :forward-chaining class rules and only then if the :corollary is essentially a conjunction. (See the documentation for rewrite, linear, or forward-chaining for details.)




    acl2-sources/doc/HTML/RULE-NAMES.html0000664002132200015000000000103612222333531016356 0ustar kaufmannacl2 RULE-NAMES.html -- ACL2 Version 6.3

    RULE-NAMES

    How rules are named.
    Major Section:  THEORIES
    

    Examples:
    (:rewrite assoc-of-app)
    (:linear delta-aref . 2)
    (:definition length)
    (:executable-counterpart length)
    

    See rune.




    acl2-sources/doc/HTML/RULER-EXTENDERS.html0000664002132200015000000004333512222333531017206 0ustar kaufmannacl2 RULER-EXTENDERS.html -- ACL2 Version 6.3

    RULER-EXTENDERS

    control for ACL2's termination and induction analyses
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Introduction

    Consider the following recursive definition, which returns a list of threes of length one more than the length of x.

      (defun f (x)
        (cons 3
              (if (consp x)
                  (f (cdr x))
                nil)))
    
    One might expect ACL2's termination analysis to admit this function, since we know that (cdr x) is ``smaller'' than x if (consp x) is true. (By default, ACL2's notion of ``smaller'' is ordinary natural-number <, and the argument x is measured by applying function acl2-count to x.) However, that termination analysis does not consider IF tests, like (consp x) above, when they occur under calls of functions other than IF, such as CONS in the case above.

    One way to overcome this problem is to ``lift'' the IF test to the top level, as follows.

      (defun f (x)
        (if (consp x)
            (cons 3 (f (cdr x)))
          (cons 3 nil)))
    
    But another way to overcome the problem is to tell ACL2 to extend its termination (and induction) analysis through calls of cons, as follows.
      (defun f (x)
        (declare (xargs :ruler-extenders (cons)))
        (cons 3
              (if (consp x)
                  (f (cdr x))
                nil)))
    
    You may even wish to provide value :all instead of an explicit list of ruler-extenders, so that no function call blocks the termination analysis:
      (defun f (x)
        (declare (xargs :ruler-extenders :all))
        (cons 3
              (if (consp x)
                  (f (cdr x))
                nil)))
    
    Alternatively, you can omit the XARGS :RULER-EXTENDERS form, instead modifying the global default set of ruler-extenders:
      (set-ruler-extenders :all)
    
      ; or, for example:
      (set-ruler-extenders '(cons return-last))
    
    You can call the function default-ruler-extenders as follows to see the current global default set of ruler-extenders:
    (default-ruler-extenders (w state))
    

    We conclude this introduction by considering the handling of LET expressions by termination analysis. Consider the following example.

      (defun fact (n)
        (the (integer 1 *)
             (if (posp n)
                 (* n (fact (1- n)))
               1)))
    
    ACL2 treats the call of THE in the body of this definition as follows.
      (let ((var (if (posp n)
                     (* n (fact (1- n)))
                   1)))
        (if (and (integerp var) (<= 1 var))
            var
          <some_error>))
    
    A LET expression, in turn, is treated as a LAMBDA application:
      ((lambda (var)
         (if (if (integerp var)
                 (not (< var 1))
               nil)
             var
           <some_error>))
       (if (posp n)
           (* n (fact (1- n)))
         1))
    
    Notice that the posp test, which governs the recursive call of fact, is inside an argument of a function application, namely the application of the LAMBDA expression. So by default, ACL2 will not consider this posp test in its termination analysis. The keyword :LAMBDAS in the list of ruler-extenders denotes all calls of lambda expressions, much as the inclusion of CONS in the ruler-extenders denotes all calls of CONS. The following definition is thus accepted by ACL2.
      (defun fact (n)
        (declare (xargs :ruler-extenders (:lambdas)))
        (the (integer 1 *)
             (if (posp n)
                 (* n (fact (1- n)))
               1)))
    
    As a convenience, ACL2 allows the symbol :lambdas in place of (:lambdas), and in fact the former will also include the default ruler-extenders: RETURN-LAST (which comes from macroexpansion of calls of PROG2$, EC-CALL, and others) and MV-LIST.

    IMPORTANT REMARKS. (1) Notice that the argument to set-ruler-extenders is evaluated, but the argument to :RULER-EXTENDERS in XARGS is not evaluated. (2) Do not put macro names in your list of ruler-extenders. For example, if you intend that + should not block the termination analysis, in analogy to cons in the example above, then the list of ruler-extenders should include binary-+, not +. Of course, if you use :all then this is not an issue, but see the next remark. (3) Also please note that by taking advantage of the ruler-extenders, you may be complicating the induction scheme stored for the function, whose computation takes similar advantage of the additional IF structure that you are specifying.

    Below we describe the notion of ruler-extenders in detail, as well as how to set its default using set-ruler-extenders.

    Details

    We begin by discussing how to set the ruler-extenders by using the macro set-ruler-extenders; below we will discuss the use of keyword :ruler-extenders in XARGS declare forms.

    Examples:
    (set-ruler-extenders :basic) ; return to default
    (set-ruler-extenders *basic-ruler-extenders*) ; same as immediately above
    (set-ruler-extenders :all) ; every governing IF test rules a recursive call
    (set-ruler-extenders :lambdas) ; LET does not block termination analysis
    (set-ruler-extenders (cons :lambdas *basic-ruler-extenders*))
                                   ; same as immediately above
    (set-ruler-extenders '(f g)) ; termination analysis goes past calls of f, g
    
    General Form:
    (set-ruler-extenders val)
    
    where val evaluates to one of :basic, :all, :lambdas, or a true list of symbols containing no keyword other than, optionally, :lambdas.

    When a recursive definition is submitted to ACL2 (in :logic mode), the recursion must be proved to terminate; see defun. More precisely, ACL2 explores the IF structure of the body of the definition to accumulate the tests that ``rule'' any given recursive call. The following example reviews how this works. Suppose that f has already been defined.

      (defun g (x y)
        (declare (xargs :measure (+ (acl2-count x) (acl2-count y))))
        (if (consp x)
            (g (cdr x) y)
          (if (consp y)
              (f (g x (cdr y)))
            (f (list x y)))))
    
    ACL2 makes the following response to this proposed definition. Notice that the :measure proposed above must be proved to be an ACL2 ordinal -- that is, to satisfy O-P -- and that the arguments to each recursive call must be smaller (in the sense of that measure and O<, which here reduces to the ordinary < relation) than the formals under the assumption of the ruling IF tests. The first IMPLIES term below thus corresponds to the recursive call (g (cdr x) y), while the second corresponds to the recursive call (g x (cdr y)).
      For the admission of G we will use the relation O< (which is known
      to be well-founded on the domain recognized by O-P) and the measure
      (+ (ACL2-COUNT X) (ACL2-COUNT Y)).  The non-trivial part of the measure
      conjecture is
    
      Goal
      (AND (O-P (+ (ACL2-COUNT X) (ACL2-COUNT Y)))
           (IMPLIES (CONSP X)
                    (O< (+ (ACL2-COUNT (CDR X)) (ACL2-COUNT Y))
                        (+ (ACL2-COUNT X) (ACL2-COUNT Y))))
           (IMPLIES (AND (NOT (CONSP X)) (CONSP Y))
                    (O< (+ (ACL2-COUNT X) (ACL2-COUNT (CDR Y)))
                        (+ (ACL2-COUNT X) (ACL2-COUNT Y))))).
    
    Now consider the following alternate version of the above definition.
      (defun g (x y)
        (declare (xargs :measure (+ (acl2-count x) (acl2-count y))))
        (if (consp x)
            (g (cdr x) y)
          (f (if (consp y)
                 (g x (cdr y))
               (list x y)))))
    
    The first test, (consp x), still rules the first recursive call, (g (cdr x) y). And the negation of that test, namely (not (consp x)), still rules the second recursive call (g x (cdr y)). But the call of f blocks the top-down exploration of the IF structure of the body of g, so (consp y) does not rule that second recursive call, which (again) is (g x (cdr y)). As a result, ACL2 fails to admit the above definition.

    Set-ruler-extenders is provided to overcome the sort of blocking described above. Suppose for example that the following event is submitted:

      (set-ruler-extenders '(f))
    
    Then the alternate definition of g above is admissible, because the call of f no longer blocks the top-down exploration of the IF structure of the body of g: that is, (consp y) becomes a ruler of the recursive call (g x (cdr y)). In this case, we say that f is a ``ruler-extender''. The same result obtains if we first submit
      (set-ruler-extenders :all)
    
    as this removes all function calls as blockers of the top-down analysis. In other words, with :all it is the case that for every recursive call, every test argument of a superior call of IF contributes a ruler of that recursive call.

    ACL2 handles LET (and LET*) expressions by translating them to LAMBDA expressions (see term). The next examples illustrates termination analysis involving such expressions. First consider the following (admittedly inefficient) definition.

      (defun fact (n)
        (let ((k (if (natp n) n 0)))
          (if (equal k 0)
              1
            (* k (fact (+ -1 k))))))
    
    ACL2 translates the body of this definition to a LAMBDA application, essentially:
      ((lambda (k)
         (if (equal k 0)
             1
           (* k (fact (+ -1 k)))))
       (if (natp n) n 0))
    
    As with the application of any function other than IF, the top-down termination analysis does not dive into arguments: the LAMBDA blocks the continuation of the analysis into its argument. But here, the argument of the LAMBDA is (if (natp n) n 0), which has no recursive calls to consider anyhow. What is more interesting: ACL2 does continue its termination analysis into the body of the LAMBDA, in an environment binding the LAMBDA formals to its actuals. In this case, the termination analysis thus continues into the term
      (if (equal k 0)
          1
        (* k (fact (+ -1 k))))
    
    in the environment that binds k to the term (if (natp n) n 0). Thus, the proof obligation is successfully discharged, as reported by ACL2:
      For the admission of FACT we will use the relation O< (which is known
      to be well-founded on the domain recognized by O-P) and the measure
      (ACL2-COUNT N).  The non-trivial part of the measure conjecture is
    
      Goal
      (IMPLIES (NOT (EQUAL (IF (NATP N) N 0) 0))
               (O< (ACL2-COUNT (+ -1 (IF (NATP N) N 0)))
                   (ACL2-COUNT N))).
      .....
      Q.E.D.
    
      That completes the proof of the measure theorem for FACT.
    

    But now consider the following definition, in which the recursion takes place inside the argument of the LAMBDA rather than inside the LAMBDA body.

      (defun app (x y)
        (let ((result (if (endp x)
                          y
                        (cons (car x)
                              (app (cdr x) y)))))
          (if (our-test result)
              result
            0)))
    
    Writing the body in LAMBDA notation:
      ((lambda (result)
         (if (our-test result)
             result
           0))
       (if (endp x)
           y
         (cons (car x)
               (app (cdr x) y))))
    
    By default, the LAMBDA call blocks the top-down termination analysis from proceeding into the term (if (endp x) ...). To solve this, one can submit the event:
      (set-ruler-extenders :lambdas)
    
    The above definition of app is then admitted by ACL2, because the termination analysis is no longer blocked by the LAMBDA call.

    The example just above illustrates that the heuristically-chosen measure is suitably sensitive to the ruler-extenders. Specifically: that measure is the application of acl2-count to the first formal parameter of the function that is tested along every branch of the relevant IF structure (as determined by the rulers) and occurs as a proper subterm at the same argument position in every recursive call. The heuristics for choosing the controller-alist for a definition rule are similarly sensitive to the ruler-extenders (see definition).

    The remarks above for defun events are equally applicable when a definition sits inside a mutual-recursion event, except of course that in this case, a ``recursive call'' is a call of any function being defined by that mutual-recursion event.

    Rules of class :definition are sensitive to set-ruler-extenders in analogy to the case of defun events.

    This macro generates a call (table acl2-defaults-table :ruler-extenders val) and hence is local to any books and encapsulate events in which it occurs. See acl2-defaults-table. The current list of ruler-extenders may be obtained as

      (cdr (assoc-eq :ruler-extenders
           (table-alist 'acl2-defaults-table (w state))))
    
    or more conveniently, as:
      (default-ruler-extenders (w state))
    

    Note that evaluation of (set-ruler-extenders lst), where lst evaluates to a list, does not necessarily include the default ruler-extenders -- i.e., those included for the argument, :basic -- which are the elements of the list constant *basic-ruler-extenders*, namely return-last and mv-list. You may, of course, include these explicitly in your list argument.

    We conclude our discussion by noting that the set of ruler-extenders can affect the induction scheme that is stored with a recursive definition. The community book books/misc/misc2/ruler-extenders-tests.lisp explains how induction schemes are derived in this case. Consider the following example.

      (defun tree-of-nils-p (x)
        (if (consp x)
            (and (tree-of-nils-p (car x))
                 (tree-of-nils-p (cdr x)))
          (null x)))
    
    The above definition generates the following induction scheme. Note that (and u v) expands to (if u v nil), which explains why the term (tree-of-nils-p (car x)) rules the recursive call (tree-of-nils-p (cdr x)), resulting in the hypothesis (tree-of-nils-p (car x)) in the final conjunct below.
      (AND (IMPLIES (NOT (CONSP X)) (:P X))
           (IMPLIES (AND (CONSP X)
                         (NOT (TREE-OF-NILS-P (CAR X)))
                         (:P (CAR X)))
                    (:P X))
           (IMPLIES (AND (CONSP X)
                         (TREE-OF-NILS-P (CAR X))
                         (:P (CAR X))
                         (:P (CDR X)))
                    (:P X)))
    
    Now consider the following variant of the above definition, in which a call of the function identity blocks the termination analysis.
      (defun tree-of-nils-p (x)
        (if (consp x)
            (identity (and (tree-of-nils-p (car x))
                           (tree-of-nils-p (cdr x))))
          (null x)))
    
    This time the induction scheme is as follows, since only the top-level IF test contributes rulers to the termination analysis.
      (AND (IMPLIES (NOT (CONSP X)) (:P X))
           (IMPLIES (AND (CONSP X)
                         (:P (CAR X))
                         (:P (CDR X)))
                    (:P X)))
    
    But now suppose we first designate identity as a ruler-extender.
    (set-ruler-extenders '(identity))
    
    Then the induction scheme generated for the both of the above variants of tree-of-nils-p is the one shown for the first variant, which is reasonable because both definitions now produce essentially the same termination analysis.




    acl2-sources/doc/HTML/RUNE.html0000664002132200015000000002124112222333531015517 0ustar kaufmannacl2 RUNE.html -- ACL2 Version 6.3

    RUNE

    a rule name
    Major Section:  THEORIES
    

    Examples:
    (:rewrite assoc-of-app)
    (:linear delta-aref . 2)
    (:definition length)
    (:executable-counterpart length)
    

    Note: This topic discusses a basic notion of ``rule name'', or ``rune'' for short. Users often use abbrevitions for runes; for example, a theory expression (DISABLE APPEND) abbreviates the following set of runes: {(:DEFINITION BINARY-APPEND), (:INDUCTION BINARY-APPEND)}. See theories for a discussion of so-called ``runic designators'', which include expressions like APPEND (as above) as well as (APPEND) (for the executable-counterpart of BINARY-APPEND. Runic designators can also be abbreviations including (:d APPEND), (:e APPEND), (:i APPEND), and (:t APPEND), which designate the definition, executable-counterpart, induction, and type-prescription rules for BINARY-APPEND. For a complete description of runic designators, see theories; we return now to the more basic notion of a rune.

    Background: The theorem prover is driven from a database of rules. The most common rules are :rewrite rules, which cause the simplifier to replace one term with another. Events introduce rules into the database. For example, a defun event may introduce runes for symbolically replacing a function call by its instantiated body, for evaluating the function on constants, for determining the type of a call of the function, and for the induction scheme introduced upon defining the function. Defthm may introduce several rules, one for each of the :rule-classes specified (where one rule class is specified if :rule-classes is omitted, namely, :rewrite).

    Every rule in the system has a name. Each name is a structured object called a ``rune,'' which is short for ``rule name''. Runes are always of the form (:token symbol . x), where :token is some keyword symbol indicating what kind of rule is named, symbol is the event name that created the rule (and is called the ``base symbol'' of the rune), and x is either nil or a natural number that makes the rule name distinct from that of rules generated by other events or by other :rule-classes within the same event.

    For example, an event of the form

    (defthm name thm
      :rule-classes ((:REWRITE :COROLLARY term1)
                     (:REWRITE :COROLLARY term2)
                     (:ELIM    :COROLLARY term3)))
    
    typically creates three rules, each with a unique rune. The runes are
    (:REWRITE name . 1), (:REWRITE name . 2), and (:ELIM name).
    
    However, a given formula may create more than one rule, and all rules generated by the same :corollary formula will share the same rune. Consider the following example.
    (defthm my-thm
      (and (equal (foo (bar x)) x)
           (equal (bar (foo x)) x)))
    
    This is treated identically to the following.
    (defthm my-thm
      (and (equal (foo (bar x)) x)
           (equal (bar (foo x)) x))
      :rule-classes ((:rewrite
                      :corollary
                      (and (equal (foo (bar x)) x)
                           (equal (bar (foo x)) x)))))
    
    In either case, two rules are created: one rewriting (foo (bar x)) to x, and one rewriting (bar (foo x)) to x. However, only a single rune is created, (:REWRITE MY-THM), because there is only one rule class. But now consider the following example.
    (defthm my-thm2
      (and (equal (foo (bar x)) x)
           (equal (bar (foo x)) x))
      :rule-classes ((:rewrite
                      :corollary
                      (and (equal (foo (bar x)) x)
                           (equal (bar (foo x)) x)))
                     (:rewrite
                      :corollary
                      (and (equal (foo (bar (foo x))) (foo x))
                           (equal (bar (foo (bar x))) (bar x))))))
    
    This time there are four rules created. The first two rules are as before, and are assigned the rune (:REWRITE MY-THM . 1). The other two rules are similarly generated for the second :corollary, and are assigned the rune (:REWRITE MY-THM . 2).

    The function corollary will return the corollary term associated with a given rune in a given world. Example:

    (corollary '(:TYPE-PRESCRIPTION DIGIT-TO-CHAR) (w state))
    
    However, the preferred way to see the corollary term associated with a rune or a name is to use :pf; see pf.

    The defun event creates as many as four rules. (:definition fn) is the rune given to the equality axiom defining the function, fn. (:executable-counterpart fn) is the rune given to the rule for computing fn on known arguments. A type prescription rule may be created under the name (:type-prescription fn), and an induction rule may be created under the name (:induction fn).

    Runes may be individually enabled and disabled, according to whether they are included in the current theory. See theories. Thus, it is permitted to disable (:elim name), say, while enabling the other rules derived from name. Similarly, (:definition fn) may be disabled while (:executable-counterpart fn) and the type prescriptions for fn are enabled.

    Associated with most runes is the formula justifying the rule named. This is called the ``corollary formula'' of the rune and may be obtained via the function corollary, which takes as its argument a rune and a property list world. Also see pf. The corollary formula for (:rewrite name . 1) after the defthm event above is term1. The corollary formulas for (:definition fn) and (:executable-counterpart fn) are always identical: the defining axiom. Some runes, e.g., (:definition car), do not have corollary formulas. Corollary returns nil on such runes. In any case, the corollary formula of a rune, when it is non-nil, is a theorem and may be used in the :use and :by hints.

    Note: The system has many built in rules that, for regularity, ought to have names but don't because they can never be disabled. One such rule is that implemented by the linear arithmetic package. Because many of our subroutines are required by their calling conventions to return the justifying rune, we have invented the notion of ``fake runes.'' Fake runes always have the base symbol nil, use a keyword token that includes the phrase ``fake-rune'', and are always enabled. For example, (:fake-rune-for-linear nil) is a fake rune. Occasionally the system will print a fake rune where a rune is expected. For example, when the linear arithmetic fake rune is reported among the rules used in a proof, it is an indication that the linear arithmetic package was used. However, fake runes are not allowed in theories, they cannot be enabled or disabled, and they do not have associated corollary formulas. In short, despite the fact that the user may sometimes see fake runes printed, they should never be typed.




    acl2-sources/doc/HTML/Running_Models.html0000664002132200015000000000260512222333526017700 0ustar kaufmannacl2 Running_Models.html -- ACL2 Version 6.3

    Running Models

    Suppose the machine being modeled is some kind of arithmetic unit. Suppose the model can be initialized so as to multiply x times y and leave the answer in z. Then if we initialize s to multiply with x=5 and y=7 and run the machine long enough, we can read the answer 35 in the final state.

    Because ACL2 is a programming language, our model can be run or executed.

    If you defined the model in ACL2 and then typed

    (lookup 'z (mc (s 'mult 5 7) 29))
    

    then ACL2 would compute 35. (Here we assume that the function s creates a state ready to run a given application on given inputs x and y.) You can emulate or test the model of your machine.

    This is obvious because ACL2 is Common Lisp; and Common Lisp is a programming language.




    acl2-sources/doc/HTML/Revisiting_the_Admission_of_App.html0000664002132200015000000000366112222333526023235 0ustar kaufmannacl2 Revisiting_the_Admission_of_App.html -- ACL2 Version 6.3

    Revisiting the Admission of App

    Here is the definition of app again with certain parts highlighted. If you are taking the Walking Tour, please read the text carefully and click on each of the links below, except those marked . Then come back here.

    ACL2 !>(defun app (x y)
      (cond ((endp x) y)
            (t (cons (car x) 
                     (app (cdr x) y)))))
    
    
    The admission of APP is trivial, using the
    relation O<  (which is known to be well-founded on
    the domain recognized by O-P ) and the measure
    (ACL2-COUNT  X).  We observe that the
    type of APP is described by the theorem (OR
    (CONSP (APP X Y)) (EQUAL (APP X Y) Y)).  We used primitive type
    reasoning.
    
    Summary
    Form:  ( DEFUN APP ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.03 seconds (prove: 0.00, print: 0.00, other: 0.03)
     APP
    




    acl2-sources/doc/HTML/Rewrite_Rules_are_Generated_from_DEFTHM_Events.html0000664002132200015000000000256012222333526025753 0ustar kaufmannacl2 Rewrite_Rules_are_Generated_from_DEFTHM_Events.html -- ACL2 Version 6.3

    Rewrite Rules are Generated from DEFTHM Events

    By reading the documentation of defthm (and especially of its :rule-classes argument) you would learn that when we submitted the command

    (defthm associativity-of-app
      (equal (app (app a b) c)
             (app a (app b c))))
    
    
    we not only command the system to prove that app is an associative function but
      * we commanded it to use that fact as a rewrite rule.
    

    That means that every time the system encounters a term of the form

    (app (app x y) z)
    
    it will replace it with
    (app x (app y z))!
    




    acl2-sources/doc/HTML/SAVE-AND-CLEAR-MEMOIZATION-SETTINGS.html0000664002132200015000000000216212222333517021764 0ustar kaufmannacl2 SAVE-AND-CLEAR-MEMOIZATION-SETTINGS.html -- ACL2 Version 6.3

    SAVE-AND-CLEAR-MEMOIZATION-SETTINGS

    save and remove the current memoization settings
    Major Section:  EVENTS
    

    For background on memoization, see memoize.

    General Form:
    (save-and-clear-memoization-settings)
    

    Calls of this macro achieve two changes. The first copies the current memoization settings into an ACL2 table, and the second unmemoizes all functions that were memoized by calls of memoize. Also see restore-memoization-settings.

    Some Related Topics




    acl2-sources/doc/HTML/SAVE-EXEC.html0000664002132200015000000003237412222333522016237 0ustar kaufmannacl2 SAVE-EXEC.html -- ACL2 Version 6.3

    SAVE-EXEC

    save an executable image and a wrapper script
    Major Section:  OTHER
    

    Save-exec saves your ACL2 state so that you can immediately re-start later in that same state. This utility can be useful for a project with books to be included every time ACL2 is started, to avoid time taken to run include-book. Another use of save-exec is to save an executable that takes command-line arguments beyond those normally passed to the host Lisp executable. All arguments of a call of save-exec are evaluated.

    Examples:
    
    ; Save an executable script named my-saved_acl2, with the indicated message
    ; added to the start-up banner:
    (save-exec "my-saved_acl2"
               "This saved image includes Version 7 of Project Foo.")
    
    ; Same as above, but instead with a generic comment in the start-up banner:
    (save-exec "my-saved_acl2" nil)
    
    ; Arrange that the generated script passes the indicated arguments to be
    ; processed by the Lisp (ACL2) executable (where this example is specific to
    ; the case that CCL is the host Lisp):
    (save-exec "my-saved_acl2" nil
               :host-lisp-args "--no-init -Z 256M")
    
    ; Arrange that the generated script passes along the indicated arguments
    ; to Lisp (ACL2), but that they are not processed by Lisp other than to
    ; record the additional arguments (see (6) below).
    (save-exec "my-saved_acl2" nil
               :inert-args "abc xyz -i foo")
    
    ; Combining the preceding two examples:
    (save-exec "my-saved_acl2" nil
               :host-lisp-args "--no-init -Z 256M"
               :inert-args "abc xyz -i foo")
    
    ; Immediately exit the ACL2 read-eval-print loop after starting up.
    (save-exec "my-acl2" nil
               :return-from-lp t)
    
    ; Immediately exit the ACL2 read-eval-print loop after starting up and
    ; defining function FOO in the logic.
    (save-exec "my-acl2" "Start with foo defined."
               :return-from-lp '(with-output
                                 :off :all
                                 (defun foo (x) x)))
    
    ; Immediately exit the ACL2 read-eval-print loop after starting up and
    ; defining variable xxx in raw Lisp.
    (save-exec "my-acl2" "Start with xxx defined."
               :return-from-lp '(with-output
                                 :off :all
                                 (ld '((set-raw-mode-on!)
                                       (defvar xxx (make-list 10))
                                       (set-raw-mode nil)
                                       (u)))))
    

    Each example above generates a file named "my-saved_acl2". That file is quite similar in form to the script generated when building ACL2 directly from source code; details are below. For example, here are the contents of that generated file if the host Lisp is CCL (but where dates and pathnames are specific to one's environment). Here, we break lines using `\', but the exec command is actually on a single line.

    #!/bin/sh
    
    # Saved August 16, 2013  23:06:49
    #  then August 17, 2013  11:01:56
    
    export CCL_DEFAULT_DIRECTORY="/projects/acl2/lisps/ccl/15542/ccl"
    exec "/projects/ccl/lx86cl64" -I "/u/smith/my-saved_acl2.lx86cl64" \
         -Z 64M -K ISO-8859-1 -e "(acl2::acl2-default-restart)" \
         --no-init -Z 256M \
         -- \
         abc xyz -i foo \
         "$@"
    

    General Form:
    (save-exec exec-filename extra-startup-string
               :host-lisp-args host-lisp-args
               :inert-args inert-args
               :return-from-lp return-from-lp)
    
    where the keyword arguments are optional, and arguments are as follows.

    Exec-filename is the filename of the proposed executable.

    Extra-startup-string is a non-empty string to be printed after the normal ACL2 startup message when you start up the saved image. However, extra-startup-string is allowed to be nil, in which case a generic string will be printed instead.

    Host-lisp-args can be nil (the default), but if it is a non-nil value, then it is a string to be inserted into the command line in the saved script, specifying additional arguments that are to be processed by the host Lisp executable. (Note for SBCL only: these are runtime options; for toplevel options, see (8) below.)

    Inert-args can be nil (the default), but if it is a non-nil value, then it is a string to be inserted into the command line in the saved script, specifying additional arguments that are not to be processed by the host Lisp executable.

    Return-from-lp is nil by default. Regardless of the value of return-from-lp, ACL2 starts up and enters its read-eval-print loop as usual; see lp. Normally you'll stay inside that loop, but if return-from-lp is not nil, then it is evaluated in the loop, which is then exited, leaving you in raw Lisp. Evaluation of return-from-lp is done with ld options that minimize output; also see with-output to minimize output. Suggestion: let return-from-lp be t if you simply want to exit the read-eval-print loop at startup, without evaluating any (nontrivial) form.

    The remainder of this documentation focuses on the options other than return-from-lp.

    Details:

    (1) You must first exit the ACL2 read-eval-print loop, typically by executing :q, before evaluating a save-exec call; otherwise an error occurs.

    (2) The image will be saved so that in the new image, the raw Lisp package and the package in the ACL2 read-eval-print loop (see lp) will be the same as their respective values at the time save-exec is called.

    (3) Save-exec generates a small script file (e.g., "my-saved_acl2" in the examples above), similar in form (see (4) below) to the script generated when building ACL2 directly from source code, but with a comment line indicating the time at which the new script is written. Save-exec also saves an associated binary file. The binary file's name is obtained by putting a suffix on the script filename; for example, if the host Lisp is GCL running on a Linux or Darwin (MacOS) system, then that binary file has the name my-saved_acl2.gcl in the examples above.

    (4) If inert-args is nil (for example if keyword :inert-args is omitted), then when the generated ACL2 script is invoked with command line arguments, those arguments will be passed to the host Lisp; otherwise they will not. Thus for the example above, suppose we invoke the generated script as follows.

    my-saved_acl2 -a bcd -e fgh
    
    If my-saved_acl2 was generated using a save-exec command with a non-nil value specified for keyword :inert-args, then the arguments ``-a bcd -e fgh'' will not be passed to the host Lisp; otherwise, they will be. Note that for ACL2 executable scripts generated by an ordinary ACL2 build from sources, the latter case (i.e., without inert-args) takes place.

    (5) The generated script, which specifies execution with /bin/sh, will generally contain a line of one of the following forms. (But for SBCL, see (8) below.) In the examples that follow, ACL2_options is a suitable list of command-line arguments given to the ACL2 executable. The quoted string "$@" is intended to allow the user to pass additional command-line arguments to that executable.

    If host-lisp-args and inert-args are omitted (or nil):

    exec <lisp_executable> <ACL2_options> "$@"
    

    More generally, host-lisp-args is inserted immediately after <ACL2_options>, but only if it is non-nil (hence a string). If inert-args is nil, we thus get:

    exec <lisp_executable> <ACL2_options> host-lisp-args "$@"
    
    If host-lisp-args redefines a value from <ACL2_options>, then it is up to the host lisp which value to use. For example, experiments show that in CCL, if -Z appears twice, each with a legal value, then the second value is the one that is used (i.e. it does indeed override the original value written out by ACL2 in <ACL2_options>. But experiments also show that in LispWorks, where ``-init -'' is included in <ACL2_options>, then inclusion of ``-init foo.lisp'' in host-lisp-args is ignored.

    The remaining cases below are for a non-nil value of inert-args. In each case, if host-lisp-args is nil then it should be omitted from the displayed command.

    If inert-args is t then an additional argument, `--', indicates that when ACL2 is given command line arguments, these should not be processed by the host Lisp (other than recording them; see (6) below):

    exec <lisp_executable> <ACL2_options> host-lisp-args -- "$@"
    

    If inert-args is a string then the result is similar to the above, except that inert-args is added immediately after `--':

    exec <lisp_executable> <ACL2_options> host-lisp-args -- inert-args "$@"
    

    (6) See community books books/oslib/argv for a utility that returns a list of all inert-args from an invocation of ACL2.

    (7) Suppose that you invoke an ACL2 script, say "my-saved_acl2", that was generated by save-exec, and then optionally evaluate some forms. Then you may save a new ACL2 script with save-exec. The new script will contain comment lines that extend comment lines in "my-saved_acl2" with a new write date, but otherwise will be identical to the script that would have been generated by executing the new save-exec call after invoking the original ACL2 executable (built directly from ACL2 sources) instead of "my-saved_acl2". In other words, the options added by the earlier save-exec call that created "my-saved_acl2" are discarded by the new save-exec call. However, the .core file will built on top of the .core file that was consulted when "my-saved_acl2" was invoked.

    (8) The following note pertains only to the case that the host Lisp is SBCL. For SBCL, the scripts written are analogous to, but slightly different from, those shown above. Please note that for SBCL, the host-lisp-args are what the SBCL manual calls ``runtime options''. For SBCL only, an extra keyword argument, :toplevel-args, may be used for specifying what the SBCL manual calls ``toplevel options. As with :host-lisp-args, this value, toplevel-args, should be nil (the default) or a string. Here is an example.

    (save-exec "my-saved_acl2" nil
               :host-lisp-args "--dynamic-space-size 12000"
               :toplevel-args "--eval '(print \"HELLO\")'"
               :inert-args "--my-option my-value")
    
    The script generated by this example call of save-exec contains a line such as the following (with the same convention for `\' as before)
    exec "/projects/sbcl-1.1.7-x86-64-linux/src/runtime/sbcl" \
         --dynamic-space-size 2000 --control-stack-size 8 \
         --core "/u/smith/my-saved_acl2.core" --dynamic-space-size 12000 \
         --end-runtime-options \
         --no-userinit --eval '(acl2::sbcl-restart)' \
         --eval '(print "HELLO")' \
         --end-toplevel-options \
         --my-option my-value \
         "$@"
    

    In general, the generated script is of one of the following forms (with the same convention for `\' as before).

    For the case that inert-args is nil:

    exec <lisp_executable> \
         <ACL2_runtime_options> host-lisp-args --end-runtime-options \
         <ACL2_toplevel_options> host-lisp-args \
         "$@"
    

    For the case that inert-args is non-nil:

    exec <lisp_executable> \
         <ACL2_runtime_options> host-lisp-args --end-runtime-options \
         <ACL2_toplevel_options> host-lisp-args --end-toplevel-options \
         inert-args "$@"
    

    Notice that as before, when the generated script is invoked (for example, at the shell), additional command-line arguments provided at that time are passed to Lisp if and only if inert-args is nil. For SBCL, when they are passed to Lisp they are passed as toplevel options, not as runtime options.




    acl2-sources/doc/HTML/SAVING-AND-RESTORING.html0000664002132200015000000000066112222333521017711 0ustar kaufmannacl2 SAVING-AND-RESTORING.html -- ACL2 Version 6.3

    SAVING-AND-RESTORING

    See save-exec.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/SEARCH.html0000664002132200015000000000661412222333524015724 0ustar kaufmannacl2 SEARCH.html -- ACL2 Version 6.3

    SEARCH

    search for a string or list in another string or list
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    (search "cd" "Cdabcdefcde")                   ; = 4, index of first match
    (search "cd" "Cdabcdefcde" :test 'equal)      ; same as above
    (search "cd" "Cdabcdefcde" :from-end t)       ; = 8, index of last match
    (search "cd" "Cdabcdefcde" :start1 1)         ; = 1
    (search "cd" "Cdabcdefcde" :start2 5)         ; = 8
    (search "cd" "Cdabcdefcde" :test 'char-equal) ; = 0 (case-insensitive)
    (search "ac" "Cdabcdefcde")                   ; = nil
    (search '(a b) '(9 8 a b 7 6))                    ; = 2
    
    General Form:
    (search seq1 seq2 &key from-end test start1 start2 end1 end2)
    

    Search indicates whether one string or list occurs as a (contiguous) subsequence of another string or list, respectively. It returns nil if no such match is found; otherwise it returns the (zero-based) index of the first match by default, but a non-nil value of keyword argument :from-end causes it to return the last match. The :test is equal by default. The other legal value for :test is char-equal, which can be given only for two strings, in which case the match is case-insensitive. Finally, values of :start1 and :end1 for the first sequence, and of :start2 and :end2 for the second sequence, bound the portion of the respective sequence used for deciding on a match, though the index returned is always an index into the second sequence as a whole.

    The guard for calls of search is given by a function, search-fn-guard, which has the following requirements.

    o The two arguments much both satisfy true-listp or else must both be strings, which must consist of standard characters (see standard-char-p) if the :test is char-equal.

    o The :test must evaluate to one of the symbols equal or char-equal, where the latter is only allowed if the (first) two arguments are strings.

    o The values of :start1, :start2, :end1, and :end2 must all be natural numbers, where if omitted they default to 0, 0, the length len1 of the first argument, and the length len2 of the second argument, respectively.

    o If start1 is the value of :start1, defaulting as described just above, and similarly for the other start and end keywords and for lengths len1 and len2 as described just above, then: start1 <= end1 <= len1 and start2 <= end2 <= len2.

    Search is a Common Lisp function (actually, a macro in ACL2). See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/SECOND.html0000664002132200015000000000066512222333524015732 0ustar kaufmannacl2 SECOND.html -- ACL2 Version 6.3

    SECOND

    second member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/SERIALIZE-ALTERNATIVES.html0000664002132200015000000000213212222333530020171 0ustar kaufmannacl2 SERIALIZE-ALTERNATIVES.html -- ACL2 Version 6.3

    SERIALIZE-ALTERNATIVES

    alternatives to the serialize routines
    Major Section:  SERIALIZE
    

    Hons users could previously use the routines compact-print-file and compact-read-file. These are deprecated and are no longer built into ACL2. However, they are still available by loading the new community book, serialize/compact-print. Note that loading this book requires a ttag, and these routines are still only available in raw lisp.

    Another predecessor of the serialization routines were hons archives, which are still available in the hons-archive library. The serialization routines are generally better and we recommend against using hons archives for new projects.




    acl2-sources/doc/HTML/SERIALIZE-IN-BOOKS.html0000664002132200015000000000442412222333530017457 0ustar kaufmannacl2 SERIALIZE-IN-BOOKS.html -- ACL2 Version 6.3

    SERIALIZE-IN-BOOKS

    using serialization efficiently in books
    Major Section:  SERIALIZE
    

    Our serialize scheme was developed in order to allow very large ACL2 objects to be loaded into books. Ordinarily this is carried out using serialize-read within a make-event, e.g.,

      (make-event (mv-let (obj state)
                          (serialize-read "my-file")
                          (value `(defconst *my-file* ',obj))))
    

    But this scheme is not particularly efficient.

    During certify-book, the actual call of serialize-read is carried out, and this is typically pretty fast. But then a number of possibly inefficient things occur.

    - The ACL2 function bad-lisp-object is run on the resulting object. This is memoized for efficiency, but may still take considerable time when the file is very large.

    - The checksum of the resulting object is computed. This is also memoized, but as before may still take some time.

    - The object that was just read is then written into book.cert, essentially with serialize-write. This can take some time, and results in large certifiate files.

    Then, during include-book, the make-event expansion of is loaded. This is now basically just a serialize-read.

    The moral of the story is that using serialize will only help your certify-book time, and it only impacts a portion of the overall time.

    To avoid this overhead, we have developed an UNSOUND alternative to serialize-read, which is available only by loading an additional book. So, if the above scheme is not performing well for you, you may wish to see the community book serialize/unsound-read.




    acl2-sources/doc/HTML/SERIALIZE-READ.html0000664002132200015000000000433312222333530017010 0ustar kaufmannacl2 SERIALIZE-READ.html -- ACL2 Version 6.3

    SERIALIZE-READ

    read a serialized ACL2 object from a file
    Major Section:  SERIALIZE
    

    General form:

     (serialize-read filename
                     [:hons-mode {:always, :never, :smart}]   ; :smart by default
                     [:verbosep  {t, nil}])                   ; nil by default
      -->
     (mv obj state)
    

    In the logic this is an oracle read.

    Under the hood, we try to read and return a serialized object from a file that was presumably created by serialize-write. On success we return the contents of the file. Any failures (e.g., file not found, bad file contents, etc.) will result in a hard Lisp error.

    The filename should be a string that gives the path to the file.

    The hons-mode controls how whether to use hons or cons to restore the object. The default mode is :smart, which means that conses that were normed at the time of the file's creation should be restored with hons. But you can override this and insist that hons is to :always or :never be used, instead.

    Why would you use :never? If your object previously had a lot of honses, but you no longer have any need for them to be normed, then using :never may sometimes be a lot faster since it can avoid hons calls. On the other hand, if you are going to hons-copy some part of the file's contents, then it is likely faster to use :smart or :always instead of first creating normal conses and then copying them to build honses.

    The :verbosep flag just controls whether to print some low-level details related to timing and memory usage as the file is being read.




    acl2-sources/doc/HTML/SERIALIZE-WRITE.html0000664002132200015000000000240012222333530017160 0ustar kaufmannacl2 SERIALIZE-WRITE.html -- ACL2 Version 6.3

    SERIALIZE-WRITE

    write an ACL2 object into a file
    Major Section:  SERIALIZE
    

    General form:

     (serialize-write filename obj
                      [:verbosep  {t, nil}])    ; nil by default
      -->
     state
    

    In the logic this carries out an oracle read.

    Under the hood, we try to save obj into the file indicated by filename, which must be a string. The object can later be recovered with serialize-read. We just return state, and any failures (e.g., file not openable) will result in a hard Lisp error.

    Writing objects to disk is generally slower than reading them back in since some analysis is required to convert an object into our serialized object format.

    The verbosep flag just says whether to print some low-level details related to timing and memory usage as the file is being read.




    acl2-sources/doc/HTML/SERIALIZE.html0000664002132200015000000000371612222333530016303 0ustar kaufmannacl2 SERIALIZE.html -- ACL2 Version 6.3

    SERIALIZE

    routines for saving ACL2 objects to files, and later restoring them
    Major Section:  ACL2 Documentation
    

    This documentation topic relates to an experimental extension of ACL2 that supports hons, memoization, and fast alists. See hons-and-memoization. Thanks to Jared Davis for contributing the ``serialization'' routines for saving ACL2 objects in files for later loading.

    We implement some routines for writing arbitrary ACL2 objects to files, and for loading those files later. We usually call these ".sao" files, which stands for (S)erialized (A)CL2 (O)bject.

    Our serialization scheme uses a compact, binary format that preserves structure sharing in the original object. We optimize for read performance.

    Some Related Topics




    acl2-sources/doc/HTML/SET-ABSSTOBJ-DEBUG.html0000664002132200015000000001061512222333531017435 0ustar kaufmannacl2 SET-ABSSTOBJ-DEBUG.html -- ACL2 Version 6.3

    SET-ABSSTOBJ-DEBUG

    obtain debugging information upon atomicity violation for an abstract stobj
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic assumes familiarity with abstract stobjs. See defabsstobj.

    Below we explain what is meant by an error message such as the following.

    ACL2 Error in CHK-ABSSTOBJ-INVARIANTS:  Possible invariance violation
    for an abstract stobj!  See :DOC set-absstobj-debug, and PROCEED AT
    YOUR OWN RISK.
    

    The use of (set-absstobj-debug t) will make this error message more informative, as follows, at the cost of slower execution -- but in practice, the slowdown may be negligible (more on that below).

    ACL2 Error in CHK-ABSSTOBJ-INVARIANTS:  Possible invariance violation
    for an abstract stobj!  See :DOC set-absstobj-debug, and PROCEED AT
    YOUR OWN RISK.  Evaluation was aborted under a call of abstract stobj
    export UPDATE-FLD-NIL-BAD.
    

    You may be best off starting a new ACL2 session if you see one of the errors above. But you can continue at your own risk. With a trust tag (see defttag), you can even fool ACL2 into thinking nothing is wrong, and perhaps you can fix up the abstract stobj so that indeed, nothing really is wrong. See the community book books/misc/defabsstobj-example-4.lisp for how to do that. That book also documents the :always keyword and a special value for the first argument, :RESET.

    Examples:
    (set-absstobj-debug t)                 ; obtain extra debug info, as above
    (set-absstobj-debug t :event-p t)      ; same as above
    (set-absstobj-debug t
                        :on-skip-proofs t) ; as above, but even in include-book
    (set-absstobj-debug t :event-p nil)    ; returns one value, not error triple
    (set-absstobj-debug nil)               ; avoid extra debug info (default)
    
    General Form:
    (set-absstobj-debug val
                        :event-p        event-p        ; default t
                        :always         always         ; default nil
                        :on-skip-proofs on-skip-proofs ; default nil
                        )
    
    where the keyword arguments are optional with defaults as indicated above, and all supplied arguments are evaluated except for on-skip-proofsp, which must be Boolean (if supplied). Keyword arguments are discussed at the end of this topic.

    Recall (see defabsstobj) that for any exported function whose :EXEC function might (according to ACL2's heuristics) modify the concrete stobj non-atomically, one must specify :PROTECT t. This results in extra code generated for the exported function, which provides a check that atomicity was not actually violated by a call of the exported function. The extra code might slow down execution, but perhaps only negligibly in typical cases. If you can tolerate a bit extra slow-down, then evaluate the form (set-absstobj-debug t). Subsequent such errors will provide additional information, as in the example displayed earlier in this documentation topic.

    Finally we document the keyword arguments, other than :ALWAYS, which is discussed in a book as mentioned above. When the value of :EVENT-P is true, which it is by default, the call of set-absstobj-debug will expand to an event. That event is a call of value-triple. In that case, :ON-SKIP-PROOFS is passed to that call so that set-absstobj-debug has an effect even when proofs are being skipped, as during include-book. That behavior is the default; that is, :ON-SKIP-PROOFS is nil by default. Also see value-triple. The value of keyword :ON-SKIP-PROOFS must always be either t or nil, but other than that, it is ignored when EVENT-P is nil.




    acl2-sources/doc/HTML/SET-ACCUMULATED-PERSISTENCE.html0000664002132200015000000000071312222333522020711 0ustar kaufmannacl2 SET-ACCUMULATED-PERSISTENCE.html -- ACL2 Version 6.3

    SET-ACCUMULATED-PERSISTENCE

    See accumulated-persistence.
    Major Section:  OTHER
    




    acl2-sources/doc/HTML/SET-BACKCHAIN-LIMIT.html0000664002132200015000000000504212222333531017517 0ustar kaufmannacl2 SET-BACKCHAIN-LIMIT.html -- ACL2 Version 6.3

    SET-BACKCHAIN-LIMIT

    sets the backchain-limit used by the type-set and rewriting mechanisms
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    This event sets the global backchain-limit used by the ACL2 type-set and rewriting mechanisms. Its value may be a cons whose car and cdr are each either nil or a non-negative integer. Its value x may also be nil or a non-negative integer, which is treated as a cons whose car and cdr are both x.

    The car is used to limit backchaining used by the ACL2 type-set mechanism, while the cdr is used to limit backchaining used by the rewriting mechanism. See backchain-limit for details about how backchain-limits are used. Rewrite backchain limits may also be installed at the level of hints; see hints for a discussion of :backchain-limit-rw.

    :set-backchain-limit nil  ; do not impose any additional limits
    :set-backchain-limit 0    ; allow only type-set reasoning for rewriting
                              ; hypotheses
    :set-backchain-limit 500  ; allow backchaining to a depth of no more
                              ; than 500 for rewriting hypotheses
    (set-backchain-limit 500) ; same as above
    :set-backchain-limit (500 500)
                              ; same as above
    (set-backchain-limit '(500 500))
                              ; same as above
    (set-backchain-limit '(3 500))
                              ; allow type-set backchaining to a depth of no more
                              ; than 3 and rewriter backchaining to a depth of no
                              ; more than 500
    
    
    The default limit is (nil nil).




    acl2-sources/doc/HTML/SET-BODY.html0000664002132200015000000000361312222333517016143 0ustar kaufmannacl2 SET-BODY.html -- ACL2 Version 6.3

    SET-BODY

    set the definition body
    Major Section:  EVENTS
    

    Examples:
    (set-body foo (:definition foo)) ; restore original definition of foo
    (set-body foo foo) ; same as just above
    (set-body foo my-foo-def) ; use my-foo-def for the body of foo
    (set-body foo (:definition my-foo-def)) ; same as just above
    
    Rules of class :definition can install a new definition body, used for example by :expand hints. See definition and also see hints for a detailed discussion of the :install-body fields of :definition rules and their role in :expand hints.

    There may be several such definitions, but by default, the latest one is used by :expand hints. Although the :with keyword may be used in :expand hints to override this behavior locally (see hints), it may be convenient to install a definition for expansion other than the latest one -- for example, the original definition. Set-body may be used for this purpose.

    General Form:
    (set-body function-symbol rule-name)
    
    where rule-name is either a :definition rune or is a function symbol, sym, which represents the rune (:definition sym).

    You can view all definitions available for expansion; see show-bodies.




    acl2-sources/doc/HTML/SET-BOGUS-DEFUN-HINTS-OK.html0000664002132200015000000000201412222333531020304 0ustar kaufmannacl2 SET-BOGUS-DEFUN-HINTS-OK.html -- ACL2 Version 6.3

    SET-BOGUS-DEFUN-HINTS-OK

    allow unnecessary ``mutual recursion''
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Forms:
    (set-bogus-defun-hints-ok t)
    (set-bogus-defun-hints-ok nil)
    (set-bogus-defun-hints-ok :warn)
    
    By default, ACL2 causes an error when the keyword :hints is supplied in an xargs declare form for a definition (see defun). This behavior can be defeated with (set-bogus-defun-hints-ok t), or if you still want to see a warning in such cases, (set-bogus-defun-hints-ok :warn).




    acl2-sources/doc/HTML/SET-BOGUS-MUTUAL-RECURSION-OK.html0000664002132200015000000000473112222333531021146 0ustar kaufmannacl2 SET-BOGUS-MUTUAL-RECURSION-OK.html -- ACL2 Version 6.3

    SET-BOGUS-MUTUAL-RECURSION-OK

    allow unnecessary ``mutual recursion''
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-bogus-mutual-recursion-ok t)
    (set-bogus-mutual-recursion-ok nil)
    (set-bogus-mutual-recursion-ok :warn)
    
    By default, ACL2 checks that when a ``clique'' of more than one function is defined simultaneously (using mutual-recursion or defuns), then every body calls at least one of the functions in the ``clique.'' Below, we refer to definitional events that fail this check as ``bogus'' mutual recursions. The check is important because ACL2 does not store induction schemes for functions defined with other functions in a mutual-recursion or defuns event. Thus, ACL2 may have difficulty proving theorems by induction that involve such functions. Moreover, the check can call attention to bugs, since users generally intend that their mutual recursions are not bogus.

    Nevertheless, there are times when it is advantageous to allow bogus mutual recursions, for example when they are generated mechanically, even at the expense of losing stored induction schemes. The first example above allows bogus mutual recursion. The second example disallows bogus mutual recursion; this is the default. The third example allows bogus mutual recursion, but prints an appropriate warning.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    General Form:
    (set-bogus-mutual-recursion-ok flg)
    
    where flg is either t, nil, or :warn.




    acl2-sources/doc/HTML/SET-CASE-SPLIT-LIMITATIONS.html0000664002132200015000000001672112222333531020604 0ustar kaufmannacl2 SET-CASE-SPLIT-LIMITATIONS.html -- ACL2 Version 6.3

    SET-CASE-SPLIT-LIMITATIONS

    set the case-split-limitations
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    
    Examples:
    (set-case-split-limitations '(500 100))
    (set-case-split-limitations 'nil)
    (set-case-split-limitations '(500 nil))
    
    The first of these prevents clausify from trying the subsumption/replacement (see below) loop if more than 500 clauses are involved. It also discourages the clause simplifier from splitting into more than 100 cases at once.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    See hints for discussion of a related hint, :case-split-limitations. Also see splitter for information about reports on rules that may be responsible for case splits.

    General Form:
    (set-case-split-limitations lst)
    
    where lst is either nil (denoting a list of two nils), or a list of length two, each element of which is either nil or a natural number. When nil is used as an element, it is treated as positive infinity. The default setting is (500 100).

    The first number specifies the maximum number of clauses that may participate in the ``subsumption/replacement'' loop. Because of the expensive nature of that loop (which compares every clause to every other in a way that is quadratic in the number of literals in the clauses), when the number of clauses exceeds about 1000, the system tends to ``go into a black hole,'' printing nothing and not even doing many garbage collections (because the subsumption/replacement loop does not create new clauses so much as it just tries to delete old ones). The initial setting is lower than the threshold at which we see noticeably bad performance, so you probably will not see this behavior unless you have raised or disabled the limit.

    Why raise the subsumption/replacement limit? The subsumption/replacement loop cleans up the set of subgoals produced by the simplifier. For example, if one subgoal is

    (implies (and p q r) s)            [1]
    
    and another is
    (implies (and p (not q) r) s)      [2]
    
    then the subsumption/replacement loop would produce the single subgoal
    (implies (and p r) s)              [3]
    
    instead. This cleanup process is simply skipped when the number of subgoals exceeds the subsumption/replacement limit. But each subgoal must nonetheless be proved. The proofs of [1] and [2] are likely to duplicate much work, which is only done once in proving [3]. So with a low limit, you may find the system quickly produces a set of subgoals but then takes a long time to prove that set. With a higher limit, you may find the set of subgoals to be ``cleaner'' and faster to prove.

    Why lower the subsumption/replacement limit? If you see the system go into a ``black hole'' of the sort described above (no output, and few garbage collections), it could due to the subsumption/replacement loop working on a large set of large subgoals. You might temporarily lower the limit so that it begins to print the uncleaned set of subgoals. Perhaps by looking at the output you will realize that some function can be disabled so as to prevent the case explosion. Then raise or disable the limit again!

    The second number in the case-split-limitations specifies how many case splits the simplifier will allow before it begins to shut down case splitting. In normal operation, when a literal rewrites to a nest of IFs, the system simplifies all subsequent literals in all the contexts generated by walking through the nest in all possible ways. This can also cause the system to ``go into a black hole'' printing nothing except garbage collection messages. This ``black hole'' behavior is different from than mentioned above because space is typically being consumed at a prodigious rate, since the system is rewriting the literals over and over in many different contexts.

    As the simplifier sweeps across the clause, it keeps track of the number of cases that have been generated. When that number exceeds the second number in case-split-limitations, the simplifier stops rewriting literals. The remaining, unrewritten, literals are copied over into the output clauses. IFs in those literals are split out, but the literals themselves are not rewritten. Each output clause is then attacked again, by subsequent simplification, and eventually the unrewritten literals in the tail of the clause will be rewritten because the earlier literals will stabilize and stop producing case splits.

    The default setting of 100 is fairly low. We have seen successful proofs in which thousands of subgoals were created by a simplification. By setting the second number to small values, you can force the system to case split slowly. For example, a setting of 5 will cause it to generate ``about 5'' subgoals per simplification.

    You can read about how the simplifier works in the book Computer-Aided Reasoning: An Approach (Kaufmann, Manolios, Moore); also see introduction-to-the-theorem-prover for a detailed tutorial on using the ACL2 prover. If you think about it, you will see that with a low case limit, the initial literals of a goal are repeatedly simplified, because each time a subgoal is simplified we start at the left-most subterm. So when case splitting prevents the later subterms from being fully split out, we revisit the earlier terms before getting to the later ones. This can be good. Perhaps it takes several rounds of rewriting before the earlier terms are in normal form and then the later terms rewrite quickly. But it could happen that the earlier terms are expensive to rewrite and do not change, making the strategy of delayed case splits less efficient. It is up to you.

    Sometimes the simplifier produces more clauses than you might expect, even with case-split-limitations in effect. As noted above, once the limit has been exceeded, the simplifier does not rewrite subsequent literals. But IFs in those literals are split out nonetheless. Furthermore, the enforcement of the limit is -- as described above -- all or nothing: if the limit allows us to rewrite a literal then we rewrite the literal fully, without regard for limitations, and get as many cases as ``naturally'' are produced. It quite often happens that a single literal, when expanded, may grossly exceed the specified limits.

    If the second ``number'' is nil and the simplifier is going to produce more than 1000 clauses, a ``helpful little message'' to this effect is printed out. This output is printed to the system's ``comment window'' not the standard proofs output.




    acl2-sources/doc/HTML/SET-CBD.html0000664002132200015000000000303612222333516015774 0ustar kaufmannacl2 SET-CBD.html -- ACL2 Version 6.3

    SET-CBD

    to set the connected book directory
    Major Section:  BOOKS
    

    Example Forms:
    ACL2 !>:set-cbd "/usr/home/smith/"
    ACL2 !>:set-cbd "my-acl2/books"
    
    See cbd for a description of the connected book directory.

    General Form:
    (set-cbd str)
    

    where str is a nonempty string that represents the desired directory (see pathname). This command sets the connected book directory (see cbd) to the string representing the indicated directory. Thus, this command may determine which files are processed by include-book and certify-book commands typed at the top-level. However, the cbd is also temporarily set by those two book processing commands.

    IMPORTANT: Pathnames in ACL2 are in the Unix (trademark of AT&T) style. That is, the character ``/'' separates directory components of a pathname, and pathnames are absolute when they start with this character, and relative otherwise. See pathname.




    acl2-sources/doc/HTML/SET-CHECKPOINT-SUMMARY-LIMIT.html0000664002132200015000000000516612222333531021045 0ustar kaufmannacl2 SET-CHECKPOINT-SUMMARY-LIMIT.html -- ACL2 Version 6.3

    SET-CHECKPOINT-SUMMARY-LIMIT

    control printing of key checkpoints upon a proof's failure
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    See set-gag-mode for a discussion of key checkpoints.

    Examples:
    
    ; (Default) When a proof fails, print all key checkpoints before
    ; induction but at most 3 key checkpoints after induction:
    (set-checkpoint-summary-limit (nil . 3))
    
    ; When a proof fails, print at most 3 key checkpoints before
    ; induction but all 3 key checkpoints after induction:
    (set-checkpoint-summary-limit (3 . nil))
    
    ; When a proof fails, print at most 3 key checkpoints before
    ; induction and at most 5 key checkpoints after induction:
    (set-checkpoint-summary-limit (3 . 5))
    
    ; When a proof fails, print at most 3 key checkpoints before
    ; induction and at most 3 key checkpoints after induction:
    (set-checkpoint-summary-limit (3 . 3))
    ; or equivalently,
    (set-checkpoint-summary-limit 3)
    
    ; When a proof fails, print all key checkpoints:
    (set-checkpoint-summary-limit (nil . nil))
    ; or equivalently,
    (set-checkpoint-summary-limit nil)
    
    ; When a proof fails, print no information at all about key checkpoints:
    (set-checkpoint-summary-limit t)
    
    General Forms:
    (set-checkpoint-summary-limit (n1 . n2))
    (set-checkpoint-summary-limit n)
    (set-checkpoint-summary-limit t)
    
    where each of n1 and n2 is a natural number or nil. For the second form, n can be a natural number or nil and is treated as (n . n). The value t inhibits all printing of checkpoint summary information. The values n1 and n2 determine printing of key checkpoints generated before the first induction and generated after the first induction, respectively, where at most n1 or n2 (respectively) such key checkpoints are printed unless the value is nil, in which case there is no limitation.

    The argument x for set-checkpoint-summary-limit, as described above, may be quoted, i.e. supplied as 'x or (quote x). Thus, you may use the keyword form (see keyword-commands) if you prefer, for example:

    :set-checkpoint-summary-limit (nil . 3)
    




    acl2-sources/doc/HTML/SET-COMPILE-FNS.html0000664002132200015000000000633612222333531017123 0ustar kaufmannacl2 SET-COMPILE-FNS.html -- ACL2 Version 6.3

    SET-COMPILE-FNS

    have each function compiled as you go along.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Forms:
    (set-compile-fns t)    ; new functions compiled after DEFUN
    (set-compile-fns nil)  ; new functions not compiled after DEFUN
    
    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    Also see comp, because it may be more efficient in some Common Lisps to compile many functions at once rather than to compile each one as you go along.

    General Form:
    (set-compile-fns term)
    
    where term is a variable-free term that evaluates to t or nil. This macro is equivalent to
    (table acl2-defaults-table :compile-fns term)
    
    and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. However, unlike the above simple call of the table event function (see table), no output results from a set-compile-fns event.

    Set-compile-fns may be thought of as an event that merely sets a flag to t or nil. The flag's effect is felt when functions are defined, as with defun. If the flag is t, functions are automatically compiled after they are defined, as are their executable counterparts (see executable-counterpart). Otherwise, functions are not automatically compiled. Exception: The flag has no effect when explicit compilation is suppressed; see compilation.

    Because set-compile-fns is an event, the old value of the flag is restored when a set-compile-fns event is undone.

    Even when :set-compile-fns t has been executed, functions are not individually compiled when processing an include-book event. If you wish to include a book of compiled functions, we suggest that you first certify it with the compilation flag set (see certify-book) or else compile the book by supplying the appropriate load-compiled-file argument to include-book. More generally, compilation via set-compile-fns is suppressed when the state global variable ld-skip-proofsp has value 'include-book.




    acl2-sources/doc/HTML/SET-COMPILER-ENABLED.html0000664002132200015000000000072512222333531017645 0ustar kaufmannacl2 SET-COMPILER-ENABLED.html -- ACL2 Version 6.3

    SET-COMPILER-ENABLED

    See compilation.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-DEBUGGER-ENABLE.html0000664002132200015000000001471712222333531017521 0ustar kaufmannacl2 SET-DEBUGGER-ENABLE.html -- ACL2 Version 6.3

    SET-DEBUGGER-ENABLE

    control whether Lisp errors and breaks invoke the Lisp debugger
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Forms (see below for explanations and GCL exceptions):
    
    (set-debugger-enable t)         ; enable breaks into the raw Lisp debugger
    (set-debugger-enable :break)    ; same as above
    :set-debugger-enable t          ; same as above
    (set-debugger-enable :break-bt) ; as above, but print a backtrace first
    (set-debugger-enable :bt-break) ; as above, but print a backtrace first
    (set-debugger-enable :bt)       ; print a backtrace but do not enter debugger
    (set-debugger-enable :never)    ; disable all breaks into the debugger
    (set-debugger-enable nil)       ; disable debugger except when calling break$
    

    Introduction. Suppose we define foo in :program mode to take the car of its argument. This can cause a raw Lisp error. ACL2 will then return control to its top-level loop unless you enable the Lisp debugger, as shown below (except: the error message can take quite a different form in non-ANSI GCL).

      ACL2 !>(defun foo (x) (declare (xargs :mode :program)) (car x))
    
      Summary
      Form:  ( DEFUN FOO ...)
      Rules: NIL
      Warnings:  None
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
       FOO
      ACL2 !>(foo 3)
      ***********************************************
      ************ ABORTING from raw Lisp ***********
      Error:  Attempt to take the car of 3 which is not listp.
      ***********************************************
    
      If you didn't cause an explicit interrupt (Control-C),
      then the root cause may be call of a :program mode
      function that has the wrong guard specified, or even no
      guard specified (i.e., an implicit guard of t).
      See :DOC guards.
    
      To enable breaks into the debugger (also see :DOC acl2-customization):
      (SET-DEBUGGER-ENABLE T)
      ACL2 !>(SET-DEBUGGER-ENABLE T)
      <state>
      ACL2 !>(foo 3)
      Error: Attempt to take the car of 3 which is not listp.
        [condition type: TYPE-ERROR]
    
      Restart actions (select using :continue):
       0: Abort entirely from this (lisp) process.
      [Current process: Initial Lisp Listener]
      [1] ACL2(1): [RAW LISP]
    

    Details. ACL2 usage is intended to take place inside the ACL2 read-eval-print loop (see lp). Indeed, in most Lisp implementations ACL2 comes up inside that loop, as evidenced by the prompt:

    ACL2 !>
    
    However, one can occasionally hit a raw Lisp error. Here is the above example again, this time for a GCL implementation, which unfortunately gives a slightly less aesthetic report.
      ACL2 !>(foo 3)
    
      Error: 3 is not of type LIST.
      Fast links are on: do (si::use-fast-links nil) for debugging
      Error signalled by CAR.
      Backtrace: funcall > system:top-level > lisp:lambda-closure > lp > acl2_*1*_acl2::foo > foo > car > system:universal-error-handler > system::break-level-for-acl2 > let* > UNLESS
      ACL2 !>
    

    Here, the user has defined foo in :program mode, with an implicit guard of t. The ACL2 evaluator therefore called the Lisp evaluator, which expected nil or a consp argument to car.

    By default, ACL2 will return to its top-level loop (at the same level of LD) when there is a raw Lisp error, as though a call of ER with flag HARD has been evaluated. If instead you want to enter the raw Lisp debugger in such cases, evaluate the following form.

    (set-debugger-enable t)
    
    You can subsequently return to the default behavior with:
    (set-debugger-enable nil)
    
    Either way, you can enter the Lisp debugger from within the ACL2 loop by evaluating (break$). If you want break$ disabled, then evaluate the following, which disables entry to the Lisp debugger not only for Lisp errors but also when executing (break$).
    (set-debugger-enable :never)
    

    The discussion above also applies to interrupts (from Control-C) in some, but not all, host Common Lisps.

    It remains to discuss options :break, :bt, :break-bt, and :bt-break. Option :break is synonymous with option t, while option :bt prints a backtrace. Options :break-bt and :bt-break are equivalent, and each has the combined effect of :bt and :break: a backtrace is printed and then the debugger is entered.

    Note that set-debugger-enable applies not only to raw Lisp errors, but also to ACL2 errors: those affected by break-on-error. However, for ACL2 errors, entering the debugger is controlled only by break-on-error, not by set-debugger-enable. For ACL2 errors encountered after evaluating (break-on-error t), the set-debugger-enable values of :bt, :break-bt, and :bt-break will result in the same effect: in many host LIsps, this effect will be to cause a backtrace to be printed.

    Remark for Common Lisp hackers (except for the case that the host Lisp is non-ANSI GCL). You can customize the form of the backtrace printed by entering raw Lisp (with :q) and then redefining function print-call-history, whose definition immediately precedes that of break-on-error in ACL2 source file ld.lisp. Of course, all bets are off when defining any function in raw Lisp, but as a practical matter you are probably fine as long as your books are ultimately certified with an unmodified copy of ACL2. If you come up with improvements to print-call-history, please pass them along to the ACL2 implementors.




    acl2-sources/doc/HTML/SET-DEFAULT-BACKCHAIN-LIMIT.html0000664002132200015000000000720112222333531020600 0ustar kaufmannacl2 SET-DEFAULT-BACKCHAIN-LIMIT.html -- ACL2 Version 6.3

    SET-DEFAULT-BACKCHAIN-LIMIT

    sets the default backchain-limit used when admitting a rule
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    This event sets the default backchain-limit used when a new rewrite, linear, meta, or type-prescription rule is admitted. Its value may be a two-element list whose elements are each either nil or a non-negative integer. Its value x may also be nil or a non-negative integer, which is treated as the two element list (x x).

    The first element of the list is used to limit backchaining for a rule of class type-prescription while the second element is used to limit backchaining for the other three classes of rules mentioned above. See backchain-limit for details about how backchain-limits are used. The examples below assume that a new rule doesn't itself specify a value for :backchain-limit-lst.

    :set-default-backchain-limit nil  ; do not impose backchain limits for the
                                      ; rule
    :set-default-backchain-limit 0    ; allow only type-set reasoning for
                                      ; relieving a new rule's hypotheses
    :set-default-backchain-limit 500  ; allow backchaining through a new rewrite,
                                      ; linear, or meta rule's hypotheses to a
                                      ; depth of no more than 500
    (set-default-backchain-limit 500) ; same as above
    :set-default-backchain-limit (nil 500)
                                      ; same as above
    (set-default-backchain-limit '(nil 500))
                                      ; same as above
    (set-default-backchain-limit '(3 500))
                                      ; for a new :type-prescription rule, allow
                                      ; type-set backchaining to a depth
                                      ; of no more than 3; for a new
                                      ; rule of class :rewrite, :linear,
                                      ; or :meta, allow backchaining to
                                      ; a depth of no more than 50
    (set-default-backchain-limit '(nil 500))
                                      ; do not limit backchaining for a
                                      ; new :type-prescription rule; for
                                      ; a new rule of class :rewrite,
                                      ; :linear, or :meta, allow
                                      ; backchaining to a depth of no
                                      ; more than 50
    

    The initial default backchain-limit is nil.




    acl2-sources/doc/HTML/SET-DEFAULT-HINTS.html0000664002132200015000000000657612222333531017364 0ustar kaufmannacl2 SET-DEFAULT-HINTS.html -- ACL2 Version 6.3

    SET-DEFAULT-HINTS

    set the default hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    
    Examples:
    (set-default-hints '((computed-hint-1 clause)
                         (computed-hint-2 clause
                                          stable-under-simplificationp)))
    (set-default-hints nil)
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It is local to the book or encapsulate form in which it occurs; see set-default-hints! for a corresponding non-local event.

    General Form:
    (set-default-hints lst)
    
    where lst is a list. Generally speaking, the elements of lst should be suitable for use as computed-hints.

    Whenever a defthm or thm command is executed, the default hints are appended to the right of any explicitly provided :hints in the command. The same applies to defuns as well (:hints, :guard-hints, and (for ACL2(r)) :std-hints). The hints are then translated and processed just as though they had been explicitly included.

    Technically, we do not put restrictions on lst, beyond that it is a true list. It would be legal to execute

    (set-default-hints '(("Goal" :use lemma23)))
    
    with the effect that the given hint is added to subsequent hints supplied explicitly. An explicit "Goal" hint would, however, take priority, as suggested by the mention above of ``appended to the right.''

    Note that set-default-hints sets the default hints as specified. To add to or remove from the current default, see add-default-hints and see remove-default-hints. To see the current default hints, see default-hints.

    Finally, note that the effects of set-default-hints, add-default-hints, and remove-default-hints are local to the book in which they appear. Thus, users who include a book with such forms will not have their default hints affected by such forms. In order to export the effect of setting the default hints, use set-default-hints!, add-default-hints!, or remove-default-hints!.

    For a related feature, which however is only for advanced system builders, see override-hints.




    acl2-sources/doc/HTML/SET-DEFAULT-HINTS_bang_.html0000664002132200015000000000201012222333531020465 0ustar kaufmannacl2 SET-DEFAULT-HINTS_bang_.html -- ACL2 Version 6.3

    SET-DEFAULT-HINTS!

    set the default hints non-locally
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please see set-default-hints, which is the same as set-default-hints! except that the latter is not local to the encapsulate or the book in which it occurs. Probably set-default-hints is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing encapsulate or book.




    acl2-sources/doc/HTML/SET-DEFERRED-TTAG-NOTES.html0000664002132200015000000000336112222333531020205 0ustar kaufmannacl2 SET-DEFERRED-TTAG-NOTES.html -- ACL2 Version 6.3

    SET-DEFERRED-TTAG-NOTES

    modify the verbosity of TTAG NOTE printing
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Form:
    (set-deferred-ttag-notes val state)
    
    where val is t or nil.

    See defttag for a discussion of trust tags (ttags). By default, a ``TTAG NOTE'' is printed in order to indicate reliance on a ttag. When many such notes are printed, it may be desirable to avoid seeing them all. Upon evaluation of the form

    (set-deferred-ttag-notes t state)
    
    ACL2 will enter a deferred mode for the printing of ttag notes. In this mode, only the first ttag note is printed for each top-level command, and the remainder are summarized before the next top-level prompt (if any) is printed, hence before the next command is evaluated. That summary is merely a list of ttags, but the summary explains that the full ttag notes may be printed with the command (show-ttag-notes).

    Note that (show-ttag-notes) spares you duplicate ttag notes. If you want to see every ttag note as it would normally appear, for maximum security, do not evaluate the command (set-deferred-ttag-notes t state). You can undo the effect of this command, returning to an immediate mode for printing ttag notes, by evaluating:

    (set-deferred-ttag-notes nil state)
    




    acl2-sources/doc/HTML/SET-DIFFERENCE$.html0000664002132200015000000000502712222333524017043 0ustar kaufmannacl2 SET-DIFFERENCE$.html -- ACL2 Version 6.3

    SET-DIFFERENCE$

    elements of one list that are not elements of another
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (set-difference$ l1 l2)
    (set-difference$ l1 l2 :test 'eql)   ; same as above (eql as equality test)
    (set-difference$ l1 l2 :test 'eq)    ; same, but eq is equality test
    (set-difference$ l1 l2 :test 'equal) ; same, but equal is equality test
    

    (Set-difference$ l1 l2) equals a list that contains the members of l1 that are not members of l2. More precisely, the resulting list is the same as one gets by deleting the members of l2 from l1, leaving the remaining elements in the same order as in l1. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists.

    The guard for a call of set-difference$ depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between set-difference$ and its variants:

    (set-difference-eq l1 l2) is equivalent to (set-difference$ l1 l2 :test 'eq);

    (set-difference-equal l1 l2) is equivalent to (set-difference$ l1 l2 :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function set-difference-equal.

    Set-difference$ is similar to the Common Lisp primitive set-difference. However, Common Lisp does not specify the order of elements in the result of a call of set-difference.




    acl2-sources/doc/HTML/SET-DIFFERENCE-EQ.html0000664002132200015000000000067112222333524017302 0ustar kaufmannacl2 SET-DIFFERENCE-EQ.html -- ACL2 Version 6.3

    SET-DIFFERENCE-EQ

    See set-difference$.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/SET-DIFFERENCE-EQUAL.html0000664002132200015000000000067712222333524017652 0ustar kaufmannacl2 SET-DIFFERENCE-EQUAL.html -- ACL2 Version 6.3

    SET-DIFFERENCE-EQUAL

    See set-difference$.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/SET-DIFFERENCE-THEORIES.html0000664002132200015000000000313112222333531020207 0ustar kaufmannacl2 SET-DIFFERENCE-THEORIES.html -- ACL2 Version 6.3

    SET-DIFFERENCE-THEORIES

    difference of two theories
    Major Section:  THEORIES
    

    Example:
    (set-difference-theories (current-theory :here)
                             '(fact (fact)))
    
    General Form:
    (set-difference-theories th1 th2)
    
    where th1 and th2 are theories (see theories). To each of the arguments there corresponds a runic theory. This function returns the set-difference of those two runic theories, represented as a list and ordered chronologically. That is, a rune is in the result iff it is in the first runic theory but not in the second.

    The standard way to ``disable'' a theory, lst, is: (in-theory (set-difference-theories (current-theory :here) lst)).

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/SET-ENFORCE-REDUNDANCY.html0000664002132200015000000001157212222333531020120 0ustar kaufmannacl2 SET-ENFORCE-REDUNDANCY.html -- ACL2 Version 6.3

    SET-ENFORCE-REDUNDANCY

    require most events to be redundant
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Forms:
    (set-enforce-redundancy nil)   ; do not require redundancy (default)
    (set-enforce-redundancy t)     ; most events (see below) must be redundant
    (set-enforce-redundancy :warn) ; warn for most non-redundant events
    
    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    General Form:
    (set-enforce-redundancy flag)
    
    where flag is nil, t, or :warn, as indicated above. This macro is essentially equivalent to
    (table acl2-defaults-table :enforce-redundancy flag)
    
    and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. However, unlike the above simple call of the table event function (see table), no output results from a set-enforce-redundancy event.

    Set-enforce-redundancy may be thought of as an event that merely sets a flag as indicated above, which determines whether most events, including defun and defthm events, are allowed to be redundant; see redundant-events. The exceptions are deflabel, defpkg, encapsulate, include-book, push-untouchable, remove-untouchable, set-body, and table events. Any other type of non-redundant event will cause an error if flag is t and a warning if flag is nil, except in the course of carrying out an include-book form.

    Note that because table events that set the acl2-defaults-table are implicitly local, set-enforce-redundancy events are ignored when including books. However, the presence of the event (set-enforce-redundancy t) in a book guarantees that its subsequent definitions and theorems are redundant. This can be a useful property to maintain in library development, as we now describe.

    An example of the use of this form can be found in the community books under directory books/rtl/rel4/. The intention in that directory has been to put all the gory details in subdirectories support/ and arithmetic/, so that the books in subdirectory lib/ contain only the ``exported'' definitions and theorems. This approach is useful for human readability. Moreover, suppose we want to prove new theorems in lib/. Typically we wish to prove the new theorems using the existing books in lib/; however, our methodology demands that the proofs go into books in support/. If every theorem in lib/ is redundant, then we can develop the proofs in lib/ but then when we are done, move each book with such proofs into support/ as follows. In any such book, we first replace include-book forms referring to books in lib/ by include-book forms referring to corresponding books in support/ and/or arithmetic/. Then, we add suitable in-theory events to get us back into the original lib/ proof environment.

    The default behavior of the system is as though the :enforce-redundancy value is nil. The current behavior can be ascertained by evaluating the following form.

    (cdr (assoc-eq :enforce-redundancy (table-alist 'acl2-defaults-table wrld)))
    




    acl2-sources/doc/HTML/SET-EVISC-TUPLE.html0000664002132200015000000001420012222333520017132 0ustar kaufmannacl2 SET-EVISC-TUPLE.html -- ACL2 Version 6.3

    SET-EVISC-TUPLE

    control suppression of details when printing
    Major Section:  IO
    

    ACL2 output is generally printed in full. However, ACL2 can be directed to abbreviate, or ``eviscerate'', objects before printing them, though the use of a so-called ``evisc-tuple''. See evisc-tuple for a discussion of evisc-tuples. The utility set-evisc-tuple modifies certain global evisc-tuples, as explained below, to affect the extent to which ACL2 eviscerates objects during printing, for example during proof output or when printing top-level evaluation results.

    General Form:
    (set-evisc-tuple evisc-tuple ; a legal evisc-tuple, or :DEFAULT
                    :iprint val  ; one of *iprint-actions*
                    :sites sites ; either :ALL, or an element or a subset of the
                                 ;   list (:TERM :LD :TRACE :ABBREV)
    
    where the value of :iprint is passed to set-iprint, :sites :all abbreviates :sites '(:TERM :LD :TRACE :ABBREV), and other documentation is provided below. Note that all arguments are evaluated.

    See without-evisc for how to avoid evisceration for ACL2 output.

    The following example illustrates an invocation of set-evisc-tuple that limits the print-level to 3 -- only three descents into list structures are permitted before eviscerating a subterm -- and limits the print-length to 4 -- only the first four elements of any list structure will be printed.

    ACL2 !>(set-evisc-tuple (evisc-tuple 3   ; print-level
                                         4   ; print-length
                                         nil ; alist
                                         nil ; hiding-cars
                                         )
                            :iprint :same ; better yet, T
                            :sites :all)
     (:TERM :LD :TRACE :ABBREV)
    ACL2 !>'((a b ((c d)) e f g) u v w x y)
    ((A B (#) E ...) U V W ...)
    ACL2 !>
    
    We recommend however using :iprint t so that eviscerated terms may be read back in; see set-iprint. Indeed, the :iprint argument is required as a reminder to the user to consider that issue, unless iprinting has been enabled at least once. If :sites or a required :iprint argument is omitted, however, ACL2 will query the user for the missing arguments rather than causing an error.

    ACL2 eviscerates by default only in a few cases, primarily in informational messages for errors, warnings, and queries (i.e., in the :EVISC case below). Users can modify the default behavior by supplying a suitable argument to set-evisc-tuple. The argument may be :default, which denotes the evisceration provided when ACL2 starts up. Otherwise that argument is an evisc-tuple, which is either nil (no evisceration) or as described above. Moreover, there are four evisc-tuple ``evisceration contexts'', each with its own evisceration control. The value returned by set-evisc-tuple indicates the evisceration contexts whose evisc-tuple has been set. The evisceration contexts are as follows, all of which use a default value of nil for the hiding-cars. Accessors are also shown for retrieving the corresponding evisc-tuple.

    o :TERM -- used for printing terms. The accessor is (term-evisc-tuple flg state), where flg is ignored if set-evisc-tuple has been called for :term with value other than :default, and otherwise (hence initially): a flg of nil indicates an evisc-tuple of nil, and otherwise the term-evisc-tuple has a print-level of 3 and print-length of 4.

    o :ABBREV -- used for printing informational messages for errors, warnings, and queries. Initially, the alist abbreviates the ACL2 world, print-level is 5, and print-level is 7. The accessor is (abbrev-evisc-tuple state).

    o :GAG-MODE -- used for printing induction schemes (and perhaps, in the future, for other printing) when gag-mode is on. If gag-mode is off, the value used for this evisc-tuple is (term-evisc-tuple nil state). But if gag-mode is on (i.e., (gag-mode) evaluates to a non-nil value), then with one exception, the value is an evisc-tuple or nil, to be used in gag-mode for printing of induction schemes and, during proofs, the ``The non-trivial part of the guard conjecture''. The exceptional value is t, which indicates that in gag-mode, no printing of induction schemes should be and the guard conjecture should be printed using (term-evisc-tuple t state). The accessor is (gag-mode-evisc-tuple state).

    o :LD -- used by the ACL2 read-eval-print loop. The accessor is (ld-evisc-tuple state).

    o :TRACE -- used for printing trace output. No accessor is available (though in raw Lisp, (trace-evisc-tuple) returns the trace-evisc-tuple).

    Each context ectx also has an updater, (set-ectx-evisc-tuple val state), where val is a legal value for set-evisc-tuple as described above: :default or an evisc-tuple (possibly nil).

    Note that the break-rewrite commands and the proof-checker generally do their printing using the term-evisc-tuple.




    acl2-sources/doc/HTML/SET-FC-CRITERIA.html0000664002132200015000000000731212222333517017076 0ustar kaufmannacl2 SET-FC-CRITERIA.html -- ACL2 Version 6.3

    SET-FC-CRITERIA

    to set the tracking criteria for forward chaining reports
    Major Section:  FORWARD-CHAINING-REPORTS
    

    Examples:
    (set-fc-criteria)                 ; shut off all tracking
    (set-fc-criteria nil)
    
    (set-fc-criteria t)               ; to track all forward chaining
    (set-fc-criteria (t t t))
    
    (set-fc-criteria
     ((:FORWARD-CHAINING LEMMA1)      ; track all uses of LEMMA1,
       t
       t)
      ((:FORWARD-CHAINING LEMMA2)     ; uses of LEMMA2 triggered
       (ALISTP (BASIC-MAPPER A B))    ; by this specific ALISTP term
       t)
      (t t (TRUE-LISTP (DLT D))))     ; and every rule deriving
                                      ; this TRUE-LISTP term.
    
    General Forms:
    (set-fc-criteria nil)
    (set-fc-criteria t)
    (set-fc-criteria triple1 triple2 ...)
    
    where each triple is of the form (rune inst-trigger inst-concl). If rune is non-t is must be a forward chaining rune. The other two components, inst-trigger and inst-concl, if non-t, must be terms. The list of all the triples supplied is called the ``criteria.'' In the form (set-fc-criteria nil), the criteria used is the empty list of triples. (Technically, supplying nil as a triple ``ought'' to be an error, but it is a more ``natural'' way to say the list of criteria is empty than to use the correct form (set-fc-criteria).) In the form (set-fc-criteria t) the criteria used is the list containing the single triple (t t t).

    This function sets the tracking criteria for forward chaining reporting. See forward-chaining-reports for a general discussion of tracking and reporting forward chaining activity.

    The forward chaining criteria is a list of triples. Think of it as representing a disjunction of conjunctions. The activation of a :forward-chaining rune by some triggering term in the current context satisfies the criteria if it satisfies one of the triples. To satisfy the triple (rune inst-trigger inst-concl), the activation must satisfy each component of the triple. Any t component is always satisfied. If rune is non-t it is satisfied if the activation is for the given rune. If inst-trigger is non-t, it is satisfied if the activation is triggered by the given term. (``Inst-trigger'' stands for ``instantiated trigger.'' It is not the trigger term of the rule but is supposed to be an instance of that term that you believe will arise in some proof attempt you are debugging -- an instance you want to ``watch'' as it fires the rule.) If inst-concl is non-t, it is satisfied if the activation could possibly derive the conclusion given. (Again, ``inst-concl'' stands for ``instantiated conclusion'' and shows the term in your problem that you expect to be derived by forward chaining.)

    Note that if the criteria is empty, it is never satisfied, so tracking is turned off. If the criteria is the singleton list containing just the triple (t t t), then every activation satisfies it and so all :forward chaining rules are tracked.

    See forward-chaining-reports for details.




    acl2-sources/doc/HTML/SET-FC-REPORT-ON-THE-FLY.html0000664002132200015000000000256712222333517020276 0ustar kaufmannacl2 SET-FC-REPORT-ON-THE-FLY.html -- ACL2 Version 6.3

    SET-FC-REPORT-ON-THE-FLY

    to determine when forward-chaining reports are printed
    Major Section:  FORWARD-CHAINING-REPORTS
    

    Examples:  (set-fc-report-on-the-fly t)
               (set-fc-report-on-the-fly nil)
    

    If the flag is set to t, forward chaining tracking reports are printed when forward chaining occurs. If the flag is set to nil, very short reports (giving just the caller and the report number) are printed during forward chaining but you can use fc-report to see the full report afterwards.

    Since nothing is tracked when the criteria is nil, this function also prints out the current criteria to remind you of what it is.

    The flag manipulated by this function does not shut off tracking. It only determines whether reports are printed on-the-fly or not. To shut off tracking set-fc-criteria.

    See forward-chaining-reports for details.




    acl2-sources/doc/HTML/SET-FMT-HARD-RIGHT-MARGIN.html0000664002132200015000000000252312222333520020367 0ustar kaufmannacl2 SET-FMT-HARD-RIGHT-MARGIN.html -- ACL2 Version 6.3

    SET-FMT-HARD-RIGHT-MARGIN

    set the right margin for formatted output
    Major Section:  IO
    

    In this documentation topic we discuss setting of both a ``soft'' and a ``hard'' right margin.

    Example Forms:
    (set-fmt-soft-right-margin 55 state) ; set soft right margin to 55
    (set-fmt-hard-right-margin 68 state) ; set hard right margin to 68
    

    Fmt and related functions insert linebreaks when lines get too long. A linebreak is inserted at an aesthetically appropriate point once the column exceeds the value of (@ fmt-soft-right-margin). If however the column exceeds the value of (@ fmt-hard-right-margin), then a linebreak is soon inserted. Such a ``hard'' linebreak follows the insertion of a backslash (\) character unless fmt!, fms!, or fmt1! is used, or state global write-for-read is true.




    acl2-sources/doc/HTML/SET-FMT-SOFT-RIGHT-MARGIN.html0000664002132200015000000000107512222333520020425 0ustar kaufmannacl2 SET-FMT-SOFT-RIGHT-MARGIN.html -- ACL2 Version 6.3

    SET-FMT-SOFT-RIGHT-MARGIN

    set the soft right margin for formatted output
    Major Section:  IO
    

    See set-fmt-hard-right-margin for a discussion of the soft and hard right margin for formatted output.




    acl2-sources/doc/HTML/SET-GAG-MODE.html0000664002132200015000000001375712222333531016534 0ustar kaufmannacl2 SET-GAG-MODE.html -- ACL2 Version 6.3

    SET-GAG-MODE

    modify the nature of proof output
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    
    :set-gag-mode t      ; enable gag-mode, suppressing most proof commentary
    (set-gag-mode t)     ; same as above
    :set-gag-mode :goals ; same as above, but print names of goals when produced
    :set-gag-mode nil    ; disable gag-mode
    
    General Forms:
    (set-gag-mode val)
    :set-gag-mode val
    
    where val is one of t, nil, or :goals.

    The basic idea of gag-mode is to avoid much of the verbose output from the theorem prover, leaving only output that is expected to be helpful. You are strongly encouraged to put the form

    (set-gag-mode t) ; or, (set-gag-mode :goals)
    
    in your ACL2 customization file; see acl2-customization. The default value is :goals.

    The basic idea of gag-mode is to focus attention on so-called ``key checkpoints''. By default, a checkpoint is a goal that cannot be simplified. (Below we discuss how to change this default.) A key checkpoint is a checkpoint that is not descended from another checkpoint. (Technical point: ``Descended'' signifies that both goals are at the top level in the same forcing round, or are in the same proof by induction.) Successful ACL2 users generally focus their attention on key checkpoints; for a discussion of how to use ACL2 prover output in an effective manner, see the-method, and see introduction-to-the-theorem-prover for a more detailed tutorial. In gag-mode, a key checkpoint is only displayed when ACL2 is unable to make any further progress on that goal or some descendent of it, other than with a proof by induction.

    Evaluation of set-gag-mode t enters gag-mode, so that only key checkpoints are printed. Evaluation of set-gag-mode :goals also enters gag-mode, but will additionally cause the name of a goal to be printed as soon as it is generated (by invoking set-print-clause-ids). The :goals setting is useful for cases in which the prover spends very little of its time generating goals to be proved by induction, yet you want to see that it is making progress. For finer-grained feedback about the simplifier's activity, see dmr.

    The current value of gag-mode is returned by a macro of the same name:

    (gag-mode) ; evaluates to t, nil, or :goals
    

    An alternative to gag-mode is to use proof-trees; see proof-tree. With proof-trees it is not so important to avoid excessive prover output, since the proof-tree display provides structure that makes it easy to monitor proof attempts and navigate output for a proof that has failed or seems to be failing. Still, output can take time to print, so you may get better performance with gag-mode.

    The intention of gag-mode is to show you only the parts of a proof attempt that are relevant for debugging a failure; additional output is generally more likely to be distracting than truly helpful. But on occasion you may want to see the full proof output after an attempt made with gag-mode. If so, then see pso and see pso!. Since set-gag-mode takes responsibility for the saving of output, related utility set-saved-output is disabled when gag-mode is active. Also note that calling set-gag-mode erases the currently saved output, if any.

    You may notice that gag-mode tends to print relatively little information about goals pushed for proof by sub-induction -- i.e., a proof of *i.j, *i.j.k, etc. The principle here is that sub-inductions that do not succeed should generally be avoided, not analyzed for ways to make them succeed. Instead, the key checkpoint that generated the goal pushed for this induction is more appropriate to analyze. In general, the ``higher level'' the checkpoint, the more worthy it is of attention. Thus, we suggest that look at the top-level checkpoints before looking at those labeled ``Key checkpoints under a top-level induction''.

    We conclude with remarks for advanced users.

    The notion of ``checkpoint'' can be modified by the user. The default, as discussed above, is for a checkpoint to be a goal that cannot be simplified. Put differently, a checkpoint is acted on by one of the processes in the value of the form (@ checkpoint-processors); see @. Any or all of the symbols eliminate-destructors-clause, fertilize-clause, generalize-clause, or eliminate-irrelevance-clause can be removed from this value in order that invocation of the corresponding proof process does not cause its input goal to be labeled a checkpoint. For example, if you do not want destructor elimination to be treated differently from simplification for purposes of labeling checkpoints, you can evaluate the following form (see assign):

    (assign checkpoint-processors
            (remove 'eliminate-destructors-clause
                    (@ checkpoint-processors)))
    
    Note that the value of (@ checkpoint-processors) also affects the proof tree display; see proof-tree-details. End of Remark.)

    See set-evisc-tuple, in particular the discussion there of :GAG-MODE, for how to influence slightly just what is printed in gag-mode.




    acl2-sources/doc/HTML/SET-GUARD-CHECKING.html0000664002132200015000000002552012222333531017416 0ustar kaufmannacl2 SET-GUARD-CHECKING.html -- ACL2 Version 6.3

    SET-GUARD-CHECKING

    control checking guards during execution of top-level forms
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Detailed comments about the arguments of this function may be found elsewhere: see guard-evaluation-table. Here we provide an introduction to the use of set-guard-checking.

    New users are encouraged to execute one of the following forms in order to avoid evaluation errors due to guards:

    (set-guard-checking :none)
    (set-guard-checking nil)
    
    The former avoids all guard-checking on user-defined functions and should generally work fine for new users, the only drawback being efficiency loss on compute-intensive problems. All settings other than :none check guards, but a value of nil allows evaluation to continue in the logic when guards fail (avoiding the raw Lisp definition in that case).

    You may put one of the above forms in the "acl2-customization.lsp" file in your current directory (see cbd) or your home directory; see acl2-customization.

    Note that guards are not part of the ACL2 logic, and hence new users can completely ignore the notion of guard (and the rest of this documentation section after this paragraph!). For example, (car 3) and nil can be proved equal in the ACL2 logic, as follows, even though the guard on car requires its first argument to be a cons pair or nil.

    (thm (equal (car 3) nil))
    
    Moreover, unless your functions or top-level forms call built-in ACL2 functions that are defined in :program mode, the following property will hold.

    Evaluation of (set-guard-checking :none) will allow evaluation of forms such as (car 3) to take place without error in the top level loop, not only when proving theorems.

    If you feel bold, then you may wish to read the rest of this documentation topic; also see guard.

    See guard-evaluation-table for a succinct table, with associated discussion, that covers in detail the material presented in the rest of the present topic.

    The top-level ACL2 loop has a variable which controls which sense of execution is provided. To turn ``guard checking on,'' by which we mean that guards are checked at runtime, execute the top-level form :set-guard-checking t. To allow guard violations, do :set-guard-checking nil, or do :set-guard-checking :none to turn off all guard-checking, so that raw Lisp definitions of user-defined functions are avoided unless their guard is t. The status of guard-checking is reflected in the prompt.

    ACL2 !>
    
    means guard checking is on and
    ACL2 >
    
    means guard checking is off. The exclamation mark can be thought of as ``barring'' certain computations. The absence of the mark suggests the absence of error messages or unbarred access to the logical axioms. Thus, for example
    ACL2 !>(car 'abc)
    
    will signal an error, while
    ACL2 >(car 'abc)
    
    will return nil.

    We will return at the end of this documentation topic to discuss two other values, :all and :nowarn, for :set-guard-checking. We also note that evaluation of built-in :program mode functions always takes place in raw Lisp.

    Whether guards are checked during evaluation is independent of the default-defun-mode. We note this simply because it is easy to confuse ``:program mode'' with ``evaluation in Common Lisp'' and thus with ``guard checking on;'' and it is easy to confuse ``:logic mode'' with ``evaluation in the logic'' and with ``guard checking off.'' But the default-defun-mode determines whether newly submitted definitions introduce programs or add logical axioms. That mode is independent of whether evaluation checks guards or not. You can operate in :logic mode with runtime guard checking on or off. Analogously, you can operate in :program mode with runtime guard checking on or off.

    For further discussion on evaluation and guards see guards-and-evaluation, in particular the exception for safe-mode in the ``Aside'' there. See guard for a general discussion of guards.

    Now we fulfill our promise above to discuss two other values for :set-guard-checking:

    :set-guard-checking :nowarn
    :set-guard-checking :all
    
    The meaning of these values is perhaps best described by the following example provided by David Rager.
    ACL2 !>(defun my-test (expr)
             (declare (xargs :guard (true-listp expr)
                             :verify-guards nil))
             (if (atom expr)
                 expr
               (cons (my-test (car expr))
                     (my-test (cdr expr)))))
    
    The admission of MY-TEST is trivial, using the relation O< (which is
    known to be well-founded on the domain recognized by O-P) and the measure
    (ACL2-COUNT EXPR).  We could deduce no constraints on the type of MY-
    TEST.  However, in normalizing the definition we used primitive type
    reasoning.
    
    Summary
    Form:  ( DEFUN MY-TEST ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     MY-TEST
    ACL2 !>(my-test '(a b c))
    
    ACL2 Warning [Guards] in TOP-LEVEL:  Guard-checking will be inhibited
    on recursive calls of the executable counterpart (i.e., in the ACL2
    logic) of MY-TEST.  To check guards on all recursive calls:
      (set-guard-checking :all)
    To leave behavior unchanged except for inhibiting this message:
      (set-guard-checking :nowarn)
    
    (A B C)
    ACL2 !>
    
    If you think about evaluation of (my-test '(a b c)), you will see that it leads to the recursive call (my-test 'a), which one might expect to cause a guard violation since the symbol a is not a true-listp. However, as the warning above explains, we do not by default check guards on recursive calls. The reason is efficiency -- imagine a simple definition with a guard that is slow to evaluate. The values :nowarn and :all for :set-guard-checking have been introduced as ways of dealing with the above warning. The value :nowarn simply turns off the warning above. The value :all causes all guards to be checked, even on recursive calls and even on all calls of non-built-in :program mode functions -- unless, of course, a call is made of a function whose guard has been verified (see verify-guards), where the arguments satisfy the guard, in which case the corresponding call is made in raw Lisp without subsidiary guard-checking. We still say that ``guard-checking is on'' after :set-guard-checking is invoked with values t, :nowarn, and :all, otherwise (after value nil) we say ``guard-checking is off.

    For technical reasons, :all does not have its advertised effect in the case of built-in :program-mode functions. If you are interested in this technical detail, see the comment ``In the boot-strap world...'' in source function oneify-cltl-code.

    We conclude with a remark about the use of :set-guard-checking for experimenting with ACL2 as a logic or as a programming language. If one views ACL2 as a logic, one may wish to use :set-guard-checking :none, while if instead one views ACL2 as a functional programming language, one may wish to use :set-guard-checking :all. The following transcript illustrates this distinction by way of example. Specifically, (car 3) is equal to nil in the ACL2 logic, but may be viewed as a programming error. The default of :set-guard-checking t is problematic for learning ACL2 using :program mode functions, since one can get raw Lisp errors. In the example below, the raw Lisp error occurs because foo implicitly has a guard of t, hence (foo 3) is evaluated in raw Lisp, which leads to a raw Lisp call of c[(car 3)].

    ACL2 !>(defun foo (x)
             (declare (xargs :mode :program))
             (car x))
    
    Summary
    Form:  ( DEFUN FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     FOO
    ACL2 !>(foo 3)
    Error: Attempt to take the car of 3 which is not listp.
      [condition type: TYPE-ERROR]
    
    Restart actions (select using :continue):
     0: Abort entirely from this (lisp) process.
    [Current process: Initial Lisp Listener]
    [1] ACL2(1): [RAW LISP] :pop
    ACL2 !>:set-guard-checking :none
    
    Turning off guard checking entirely.  To allow execution in raw Lisp
    for functions with guards other than T, while continuing to mask guard
    violations, :SET-GUARD-CHECKING NIL.  See :DOC set-guard-checking.
    
    ACL2 >(foo 3)
    NIL
    ACL2 >:set-guard-checking :all
    
    Turning guard checking on, value :ALL.
    
    ACL2 !>(foo 3)
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol CAR, which
    is (OR (CONSP X) (EQUAL X NIL)), is violated by the arguments in the
    call (CAR 3).  See :DOC trace for a useful debugging utility.  See :DOC
    set-guard-checking for information about suppressing this check with
    (set-guard-checking :none), as recommended for new users.
    
    ACL2 !>
    




    acl2-sources/doc/HTML/SET-IGNORE-DOC-STRING-ERROR.html0000664002132200015000000000441112222333531020660 0ustar kaufmannacl2 SET-IGNORE-DOC-STRING-ERROR.html -- ACL2 Version 6.3

    SET-IGNORE-DOC-STRING-ERROR

    allow ill-formed documentation strings
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Forms:
    (set-ignore-doc-string-error nil)   ; :doc strings must be well-formed
    (set-ignore-doc-string-error t)     ; ill-formed :doc strings are ignored
    (set-ignore-doc-string-error :warn) ; ill-formed :doc strings are ignored
                                        ;   except for causing a warning
    
    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    General Form:
    (set-ignore-doc-string-error flag)
    
    where flag is nil, t, or :warn, as indicated above. This macro is essentially equivalent to
    (table acl2-defaults-table :ignore-doc-string-error flag)
    
    and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. However, unlike the above simple call of the table event function (see table), no output results from a set-ignore-doc-string-error event.

    Note that since defdoc events have the sole purpose of installing documentation strings, these require well-formed documentation strings even after executing a call of ignore-doc-string-error.

    The default behavior of the system is as though the :ignore-doc-string-error value is nil. The current behavior can be ascertained by evaluating the following form.

    (ignore-doc-string-error (w state))
    




    acl2-sources/doc/HTML/SET-IGNORE-OK.html0000664002132200015000000000401112222333531016665 0ustar kaufmannacl2 SET-IGNORE-OK.html -- ACL2 Version 6.3

    SET-IGNORE-OK

    allow unused formals and locals without an ignore or ignorable declaration
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-ignore-ok t)
    (set-ignore-ok nil)
    (set-ignore-ok :warn)
    
    The first example above allows unused formals and locals, i.e., variables that would normally have to be declared ignored or ignorable. The second example disallows unused formals and locals; this is the default. The third example allows them, but prints an appropriate warning.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    General Form:
    (set-ignore-ok flg)
    
    where flg is either t, nil, or :warn.

    One might find this event useful when one is generating function definitions by an automated procedure, when that procedure does not take care to make sure that all formals are actually used in the definitions that it generates.

    Note: Defun will continue to report irrelevant formals even if :set-ignore-ok has been set to t, unless you also use set-irrelevant-formals-ok to instruct it otherwise.




    acl2-sources/doc/HTML/SET-INHIBIT-OUTPUT-LST.html0000664002132200015000000000640712222333531020172 0ustar kaufmannacl2 SET-INHIBIT-OUTPUT-LST.html -- ACL2 Version 6.3

    SET-INHIBIT-OUTPUT-LST

    control output
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-inhibit-output-lst '(warning))
    (set-inhibit-output-lst '(proof-tree prove proof-checker))
    (set-inhibit-output-lst *valid-output-names*) ; inhibit all prover output
    :set-inhibit-output-lst (proof-tree prove)
    
    General Form:
    (set-inhibit-output-lst lst)
    
    where lst is a form (which may mention state) that evaluates to a list of names, each of which is the name of one of the following ``kinds'' of output produced by ACL2.
      error          error messages
      warning        warnings other than those related to soundness
      warning!       warnings (of all degrees of importance)
      observation    observations
      prove          commentary produced by the theorem prover
      proof-checker  commentary produced by the proof-checker
      event          non-proof commentary produced by events such as defun
                     and encapsulate
      expansion      commentary produced by make-event expansion
      summary        the summary at the successful conclusion of an event
      proof-tree     proof-tree output
    
    It is possible to inhibit each kind of output by putting the corresponding name into lst. For example, if 'warning is included in (the value of) lst, then no warnings are printed except those related to soundness, e.g., the inclusion of an uncertified book. Note that proof-tree output is affected by set-inhibit-output-lst; see proof-tree.

    See with-output for a variant of this utility that can be used in books. Also see set-inhibit-warnings for how to inhibit individual warning types and see set-inhibited-summary-types for how to inhibit individual parts of the summary.

    Printing of events on behalf of certify-book and encapsulate is inhibited when both 'event and 'prove belong to lst. Otherwise, printing of events is controlled by the ld special ld-pre-eval-print.

    Note for advanced users. By including warning! in lst, you are automatically including warning as well: all warnings will be inhibited. This is not the case if you modify value of state global variable 'inhibit-output-lst directly (with assign or f-put-global); then, if you include warning! but not warning, then warnings not related to soundness will still be printed (which is probably not what was intended).




    acl2-sources/doc/HTML/SET-INHIBIT-WARNINGS.html0000664002132200015000000000555512222333531017725 0ustar kaufmannacl2 SET-INHIBIT-WARNINGS.html -- ACL2 Version 6.3

    SET-INHIBIT-WARNINGS

    control warnings
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-inhibit-warnings "theory" "use")
    
    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It is local to the book or encapsulate form in which it occurs; see set-inhibit-warnings! for a corresponding non-local event. Indeed, (set-inhibit-warnings ...) is equivalent to (local (set-inhibit-warnings! ...)).

    General Form:
    (set-inhibit-warnings string1 string2 ...)
    
    where each string is considered without regard to case. This macro is equivalent to (local (table inhibit-warnings-table nil 'lst :clear)), where lst is the list of strings supplied. This macro is an event (see table), but no output results from a set-inhibit-warnings event.

    ACL2 prints warnings that may, from time to time, seem excessive to experienced users. Each warning is ``labeled'' with a string identifying the type of warning. Consider for example

    ACL2 Warning [Use] in ( THM ...):  It is unusual to :USE ....
    
    Here, the label is "Use". The argument list for set-inhibit-warnings is a list of such labels, each of which is a string. Any warning is suppressed if its label is a member of this list, where case is ignored, . Thus, for example, the warning above will not be printed after a call of set-inhibit-warnings that contains the string, "Use" (or any string that is string-equal to "Use", such as "use" or "USE"). In summary: the effect of this event is to suppress any warning whose label is a member of the given argument list, where case is ignored.

    The list of currently inhibited warnings is the list of keys in the table named inhibit-warnings-table. (The values in the table are irrelevant.) One way to get that value is to get the result from evaluating the following form: (table-alist 'inhibit-warnings-table (w state)). Of course, if warnings are inhibited overall -- see set-inhibit-output-lst -- then this value is entirely irrelevant.




    acl2-sources/doc/HTML/SET-INHIBIT-WARNINGS_bang_.html0000664002132200015000000000203112222333531021035 0ustar kaufmannacl2 SET-INHIBIT-WARNINGS_bang_.html -- ACL2 Version 6.3

    SET-INHIBIT-WARNINGS!

    control warnings non-locally
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please see set-inhibit-warnings, which is the same as set-inhibit-warnings! except that the latter is not local to the encapsulate or the book in which it occurs. Probably set-inhibit-warnings is to be preferred unless you have a good reason for wanting to export the effect of this event outside the enclosing encapsulate or book.




    acl2-sources/doc/HTML/SET-INHIBITED-SUMMARY-TYPES.html0000664002132200015000000000434512222333531020741 0ustar kaufmannacl2 SET-INHIBITED-SUMMARY-TYPES.html -- ACL2 Version 6.3

    SET-INHIBITED-SUMMARY-TYPES

    control which parts of the summary are printed
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (set-inhibited-summary-types '(rules time))
    
    Note: This is not an event. Rather, it changes the state, in analogy to set-inhibit-output-lst.

    General Form:
    (set-inhibited-summary-types form)
    
    where form evaluates to a true-list of symbols, each of which is among the values of the constant *summary-types*, i.e.: header, form, rules, hint-events warnings, time, steps, value, and splitter-rules. Each specified type inhibits printing of the corresponding portion of the summaries printed at the conclusions of events, where header refers to an initial newline followed by the line containing just the word Summary.

    Note the distinction between rules and hint-events. Rules provides a record of automatic rule usage by the prover, while hint-events shows the names of events given to :USE or :BY hints, as well as clause-processor functions given to :CLAUSE-PROCESSOR hints that have an effect on the proof.

    Also see set-inhibit-output-lst. Note that set-inhibited-summary-types has no effect when summary is one of the types inhibited by set-inhibit-output-lst, because in that case none of the summary will be printed.

    To control summary types for a single event, see with-output.




    acl2-sources/doc/HTML/SET-INVISIBLE-FNS-TABLE.html0000664002132200015000000000567312222333531020207 0ustar kaufmannacl2 SET-INVISIBLE-FNS-TABLE.html -- ACL2 Version 6.3

    SET-INVISIBLE-FNS-TABLE

    set the invisible functions table
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-invisible-fns-table ((binary-+ unary--)
                              (binary-* unary-/)
                              (unary-- unary--)
                              (unary-/ unary-/)))
    (set-invisible-fns-table t) ; restore original invisible-fns-table
    
    Among other things, the setting above has the effect of making unary-- ``invisible'' for the purposes of applying permutative :rewrite rules to binary-+ trees. Thus, arg and (unary-- arg) will be given the same weight and will be permuted so as to be adjacent. The form (invisible-fns-table (w state)) returns the current value of the invisible functions table.

    Also see add-invisible-fns and see remove-invisible-fns for events that add to and remove from the invisible functions table, while accounting for macro aliases (see macro-aliases-table).

    General Form:
    (set-invisible-fns-table alist)
    
    where alist is either t or a true list of pairs, each element of which is of the form (fn ufn1 ... ufnk), where fn is a function symbol and each ufni is a unary function symbol. When alist is t, the initial value of this table is used in its place. Modulo the replacement of alist by the default setting when alist is t, this macro is equivalent to
    (table invisible-fns-table nil 'alist :clear)
    
    which is also an event (see table).

    Note that set-invisible-fns-table does not evaluate its argument. However, you can call table directly for that purpose. For example,

    (set-invisible-fns-table ((binary-+ unary--)
                              (binary-* unary-/)
                              (unary-- unary--)
                              (unary-/ unary-/)))
    
    ie equivalent to the following; see table.
    (table invisible-fns-table nil
           (quote ((binary-+ unary--)
                   (binary-* unary-/)
                   (unary-- unary--)
                   (unary-/ unary-/)))
           :clear)
    

    See invisible-fns-table for a description of the invisible functions table.




    acl2-sources/doc/HTML/SET-IPRINT.html0000664002132200015000000002174212222333520016410 0ustar kaufmannacl2 SET-IPRINT.html -- ACL2 Version 6.3

    SET-IPRINT

    control whether abbreviated output can be read back in
    Major Section:  IO
    

    When ACL2 pretty-prints large expressions using formatted printing (see fmt), it may save time and space by printing tokens `#' or `...' in place of certain subexpressions. By default this only happens for a few settings such as error and warning messages; see set-evisc-tuple for controlling such elision in general. The full expression is unavailable to the user when `#' or `...' is printed, but that is easily solved by evaluating the form

      (set-iprint t)
    
    to enable a mode called ``iprinting''. Then, instead of printing `#' or `...', ACL2 prints `#@i#' for i = 1,2,3,... -- all in base 10. ACL2 can read back in such `#@i#' because under the hood, i is associated with its corresponding elided form. Thus the term ``iprint'' can be viewed as suggesting ``interactive print'' or ``index print''. We also think of ``iprint'' as suggesting ``i printing'', to suggest the printing of token `#@i#'. We call i the ``iprint index'' of that token.

    The following example should suffice to illustrate how to recover elided subexpressions. (Below this example we provide details that may be of interest only to advanced users.) Here we cause an error by defining a macro of one argument and then calling it with two arguments. By default, error messages abbreviate subexpressions deeper than level 5 with `#' and past the 7th element of a list with `...'. We see this behavior below.

    ACL2 !>(defmacro foo (x) x)
    
    Summary
    Form:  ( DEFMACRO FOO ...)
    Rules: NIL
    Warnings:  None
    Time:  0.02 seconds (prove: 0.00, print: 0.00, other: 0.02)
     FOO
    ACL2 !>(foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n))
    
    
    ACL2 Error in macro expansion:  Wrong number of args in macro expansion
    of (FOO ARG1 (A (B (C (D #))) H I J K L ...)).  (See :DOC set-iprint
    to be able to see elided values in this message.)
    
    ACL2 !>(set-iprint t)
    
    ACL2 Observation in SET-IPRINT:  Iprinting has been enabled.
    ACL2 !>(foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n))
    
    
    ACL2 Error in macro expansion:  Wrong number of args in macro expansion
    of (FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#)).
    
    ACL2 !>(acl2-count '(FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#)))
    23
    ACL2 !>
    
    Sometimes you may want to supply the abbreviated form not to compute with it, as in the acl2-count example just above, but so that you can see it. The macro without-evisc eliminates elision during printing. Below we show two ways to use this utility: first, we use it to print the elided form, and then, we use it instead on the original input form to print the entire error message.
    ACL2 !>(without-evisc '(FOO ARG1 (A (B (C (D #@1#))) H I J K L . #@2#)))
    (FOO ARG1
         (A (B (C (D (E (F (G))))))
            H I J K L M N))
    ACL2 !>(without-evisc (foo arg1 (a (b (c (d (e (f (g)))))) h i j k l m n)))
    
    
    ACL2 Error in macro expansion:  Wrong number of args in macro expansion
    of (FOO ARG1 (A (B (C (D (E (F (G)))))) H I J K L M N)).
    
    ACL2 !>
    

    The documentation above probably suffices for most users. For those who want more details, below we detail all the ways to use the set-iprint utility.

    Example Forms:
    (set-iprint t)   ; enable iprinting (elision with #@i@)
    (set-iprint nil) ; disable iprinting
    
    General Form:
    (set-iprint action        ; t, nil, :reset, :reset-enable, or :same
                :soft-bound s ; initially  1000
                :hard-bound h ; initially 10000)
    
    where all arguments are optional, but ACL2 queries for action if it is omitted. We defer the explanations of :soft-bound and :hard-bound. The values for action are as follows:

    T -- Enable iprinting.

    NIL -- Disable iprinting.

    :RESET -- Reset iprinting to its initial disabled state, so that when enabled, the first index i for which `#@i# is printed will be 1. Note that all stored information for existing iprint indices will be erased.

    :RESET-ENABLE -- Reset iprinting as with :reset, and then enable iprinting.

    :SAME -- Make no change to the iprinting state (other than setting the :soft-bound and/or :hard-bound if specified, as explained below).

    Immediately after a top-level form is read, hence before it is evaluated, a check is made for whether the latest iprint index exceeds a certain bound, (iprint-soft-bound state) -- 1000, by default. If so, then the (iprint-last-index state) is set back to 0. This soft bound can be changed to any positive integer k by calling set-iprint with :soft-bound k, typically (set-iprint :same :soft-bound k)].

    The above ``soft bound'' is applied once for each top-level form, but you may want finer control by applying a bound after the pretty-printing of each individual form (since many forms may be pretty-printed between successive evaluations of top-level forms). That bound is (iprint-hard-bound state), and can be set with the :hard-bound argument in analogy to how :soft-bound is set, as described above.

    A ``rollover'' is the detection that the soft or hard bound has been exceeded, along with a state update so that the next iprint index will be 1. When a rollover occurs, any index beyond the latest iprint index is no longer available for reading. At the top level of the ACL2 read-eval-print loop, this works as follows: ACL2 reads the next top-level form according to the current iprint state, then handles a rollover if the latest iprint index exceeded the current soft bound. The following log illustrates a rollover, which follows the description above.

    ACL2 !>(set-iprint t :soft-bound 3)
    
    ACL2 Observation in SET-IPRINT:  The soft-bound for iprinting has been
    set to 3.
    
    ACL2 Observation in SET-IPRINT:  Iprinting has been enabled.
    ACL2 !>(set-evisc-tuple (evisc-tuple 2 3 nil nil) :sites :ld)
     (:LD)
    ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g))
    ((A B C . #@1#) (A B C . #@2#) (A B C . #@3#))
    ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g))
    ((A B C . #@4#) (A B C . #@5#) (A B C . #@6#))
    ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#)))
    ((A B C D E F G)
     (A B C D E F G)
     (A B C D E F G))
    ACL2 !>'(1 2 3 4 5)
    (1 2 3 . #@1#)
    ACL2 !>'((a b c d e f g) (a b c d e f g) (a b c d e f g))
    ((A B C . #@2#) (A B C . #@3#) (A B C . #@4#))
    ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#)))
    ((A B C D E F G)
     (A B C D E F G)
     (A B C D E F G))
    ACL2 !>(without-evisc '((A B C . #@4#) (A B C . #@5#) (A B C . #@6#)))
    
    ***********************************************
    ************ ABORTING from raw Lisp ***********
    Error:  Out-of-bounds index in #@5#.  See :DOC set-iprint.
    ***********************************************
    
    If you didn't cause an explicit interrupt (Control-C),
    then the root cause may be call of a :program mode
    function that has the wrong guard specified, or even no
    guard specified (i.e., an implicit guard of t).
    See :DOC guards.
    
    To enable breaks into the debugger (also see :DOC acl2-customization):
    (SET-DEBUGGER-ENABLE T)
    ACL2 !>
    

    We conclude by mentioning two cases where iprinting and evisc-tuples are ignored. (1) This is typically the case when printing results in raw Lisp outside the ACL2 loop. To use iprinting and evisc-tuples in raw Lisp, use raw-mode; see set-raw-mode. In raw-mode, results that are ACL2 objects will be printed in the same way that ACL2 would print those results if not in raw-mode. (2) Iprinting and evisc-tuples are ignored by print-object$, which however is sensitive to many settings that do not affect formatted (fmt etc.) printing; see print-control.

    The reader interested in design and implementation issues considered during the addition of iprinting to ACL2 is invited to read the paper ``Abbreviated Output for Input in ACL2: An Implementation Case Study''; see the proceedings of ACL2 Workshop 2009, http://www.cs.utexas.edu/users/moore/acl2/workshop-2009/.




    acl2-sources/doc/HTML/SET-IRRELEVANT-FORMALS-OK.html0000664002132200015000000000276112222333531020470 0ustar kaufmannacl2 SET-IRRELEVANT-FORMALS-OK.html -- ACL2 Version 6.3

    SET-IRRELEVANT-FORMALS-OK

    allow irrelevant formals in definitions
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-irrelevant-formals-ok t)
    (set-irrelevant-formals-ok nil)
    (set-irrelevant-formals-ok :warn)
    
    The first example above allows irrelevant formals in definitions; see irrelevant-formals. The second example disallows irrelevant formals; this is the default. The third example allows irrelevant formals, but prints an appropriate warning.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    General Form:
    (set-irrelevant-formals-ok flg)
    
    where flg is either t, nil, or :warn.




    acl2-sources/doc/HTML/SET-LD-KEYWORD-ALIASES.html0000664002132200015000000000074712222333531020107 0ustar kaufmannacl2 SET-LD-KEYWORD-ALIASES.html -- ACL2 Version 6.3

    SET-LD-KEYWORD-ALIASES

    See ld-keyword-aliases.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-LD-KEYWORD-ALIASES_bang_.html0000664002132200015000000000075612222333531021235 0ustar kaufmannacl2 SET-LD-KEYWORD-ALIASES_bang_.html -- ACL2 Version 6.3

    SET-LD-KEYWORD-ALIASES!

    See ld-keyword-aliases.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-LD-REDEFINITION-ACTION.html0000664002132200015000000000076712222333531020540 0ustar kaufmannacl2 SET-LD-REDEFINITION-ACTION.html -- ACL2 Version 6.3

    SET-LD-REDEFINITION-ACTION

    See ld-redefinition-action.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-LD-SKIP-PROOFS.html0000664002132200015000000000074112222333531017452 0ustar kaufmannacl2 SET-LD-SKIP-PROOFS.html -- ACL2 Version 6.3

    SET-LD-SKIP-PROOFS

    See set-ld-skip-proofsp.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-LD-SKIP-PROOFSP.html0000664002132200015000000000073312222333531017573 0ustar kaufmannacl2 SET-LD-SKIP-PROOFSP.html -- ACL2 Version 6.3

    SET-LD-SKIP-PROOFSP

    See ld-skip-proofsp.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-LET_star_-ABSTRACTION.html0000664002132200015000000000076312222333531020770 0ustar kaufmannacl2 SET-LET_star_-ABSTRACTION.html -- ACL2 Version 6.3

    SET-LET*-ABSTRACTION

    See set-let*-abstractionp.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-LET_star_-ABSTRACTIONP.html0000664002132200015000000000430012222333531021077 0ustar kaufmannacl2 SET-LET_star_-ABSTRACTIONP.html -- ACL2 Version 6.3

    SET-LET*-ABSTRACTIONP

    to shorten many prettyprinted clauses
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    When this flag is set to t, subterms that occur more than once in a clause are abstracted away with let*, generally shortening the displayed size of the clauses. This flag only affects how clauses are printed. It does not change what terms the theorem prover manipulates.

    :set-let*-abstractionp t ;;; or, (set-let*-abstractionp t)
    
    will cause the prettyprinter to do ``let* abstraction'' on clauses before they are printed. The algorithm finds the maximal multiply-occuring subterm and extracts it, binding it to some new variable and replacing its occurrences by that variable. This produces a let* form. This process is iterated until no subterm occurs more than once. This process generally takes a little time, but less time than to print large clauses. The process can greatly reduce the amount of text produced by the prover.

    THIS ONLY AFFECTS HOW THE CLAUSES ARE PRINTED! The unabstracted clauses are manipulated by the theorem prover.

    :set-let*-abstractionp nil
    
    restores normal clause printing.

    The mode is stored in the defaults table, See acl2-defaults-table. Thus, the mode may be set locally in books.




    acl2-sources/doc/HTML/SET-MATCH-FREE-DEFAULT.html0000664002132200015000000000726312222333531020044 0ustar kaufmannacl2 SET-MATCH-FREE-DEFAULT.html -- ACL2 Version 6.3

    SET-MATCH-FREE-DEFAULT

    provide default for :match-free in future rules
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Forms:
    (set-match-free-default :once)
    (set-match-free-default :all)
    (set-match-free-default nil)
    

    Note: This utility does not apply to type-prescription rules; for a related topic pertinent to such rules, see free-variables-type-prescription.

    As described elsewhere (see free-variables), a rewrite, linear, or forward-chaining rule may have free variables in its hypotheses, and ACL2 can be directed either to try all bindings (``:all'') or just the first (``:once'') when relieving that hypothesis, as a basis for relieving subsequent hypotheses. This directing of :all or :once is generally provided by specifying either :match-free :once or :match-free :all in the :rule-classes of the rule. If neither of these is specified, then the most recent set-match-free-default is used by ACL2 to fill in this missing :match-free field. See rule-classes. Except: If the last set-match-free-default specifies nil, then ACL2 reverts to the behavior it had at start-up, as described in Remarks (2) and (3) below.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It uses the acl2-defaults-table, and hence its effect is local to the book or encapsulate form in which it occurs.

    Remarks.

    (1) The use of set-match-free-default has no effect on existing rules. In order to change the behavior of existing rules with respect to free-variable matching, see add-match-free-override.

    (2) If you submit a rewrite, linear, or forward-chaining rule with a free variable in a hypothesis, and no default setting was previously specified with set-match-free-default or the default setting is nil, and the rule is not within a book being processed with include-book, certify-book, or rebuild, then a warning or error is caused. In order to make this an error instead of a warning, see set-match-free-error.

    (3) If you submit a rewrite, linear, or forward-chaining rule with a free variable in a hypothesis, and no default setting has been previously specified with set-match-free-default or the default setting is nil, and no error is caused (see (2) above), then the default :all is used.




    acl2-sources/doc/HTML/SET-MATCH-FREE-ERROR.html0000664002132200015000000000547512222333531017654 0ustar kaufmannacl2 SET-MATCH-FREE-ERROR.html -- ACL2 Version 6.3

    SET-MATCH-FREE-ERROR

    control error vs. warning when :match-free is missing
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Legal Forms:
    (set-match-free-error nil)
    :set-match-free-error nil
    (set-match-free-error t)
    :set-match-free-error t
    

    As described elsewhere (see free-variables), when a rewrite, linear, or forward-chaining rule has free variables in its hypotheses, the user can specify whether to try all bindings (``:all'') or just the first (``:once'') when relieving its hypotheses, as a basis for relieving subsequent hypotheses. This direction of :all or :once is generally provided by specifying either :match-free :once or :match-free :all in the :rule-classes of the rule.

    But suppose that neither of these is specified for such a rule. (Note: set-match-free-error is not relevant for type-prescription rules.) Also suppose that set-match-free-default has not specified a default of :once or :all (see set-match-free-default). In this case a warning will occur except when in the context of include-book. If you prefer to see an error in such cases, except in the context of certify-book, execute (set-match-free-error t). If there is no error, then a default of :all is used.

    Note: This is NOT an event! Instead, set-match-free-error sets the state global 'match-free-error (see state and see assign). Thus, this form cannot be put into a book. If you are tempted to put it into a book, consider the fact that it really isn't needed there, since the absence of :match-free does not cause an error in the context of certify-book or include-book. If you still feel the need for such a form, consider using set-match-free-default to provide a default, at least within the scope of the current book (if any); see set-match-free-default.




    acl2-sources/doc/HTML/SET-MEASURE-FUNCTION.html0000664002132200015000000000400512222333531017722 0ustar kaufmannacl2 SET-MEASURE-FUNCTION.html -- ACL2 Version 6.3

    SET-MEASURE-FUNCTION

    set the default measure function symbol
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-measure-function nqthm::count)
    
    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    General Form:
    (set-measure-function name)
    
    where name is a function symbol of one argument. This macro is equivalent to (table acl2-defaults-table :measure-function 'name), and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. Although this is thus an event (see table), nevertheless no output results from a set-measure-function event.

    This event sets the default measure function to name. Subsequently, if a recursively defined function is submitted to defun with no explicitly given :measure argument, defun ``guesses'' the measure (name var), where name is the then current default measure function and var is the first formal found to be tested along every branch and changed in every recursive call.

    Note that if (table acl2-defaults-table :measure-function 'name) has its default value of nil, then the default measure function is acl2-count.




    acl2-sources/doc/HTML/SET-NON-LINEAR.html0000664002132200015000000000072112222333531017001 0ustar kaufmannacl2 SET-NON-LINEAR.html -- ACL2 Version 6.3

    SET-NON-LINEAR

    See set-non-linearp.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-NON-LINEARP.html0000664002132200015000000000176712222333531017134 0ustar kaufmannacl2 SET-NON-LINEARP.html -- ACL2 Version 6.3

    SET-NON-LINEARP

    to turn on or off non-linear arithmetic reasoning
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-non-linearp t)
    (set-non-linearp nil)
    

    See non-linear-arithmetic. This event is equivalent to (table acl2-defaults-table :non-linearp <t-or-nil>), and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table.

    The initial value is nil.




    acl2-sources/doc/HTML/SET-NU-REWRITER-MODE.html0000664002132200015000000000523712222333531017713 0ustar kaufmannacl2 SET-NU-REWRITER-MODE.html -- ACL2 Version 6.3

    SET-NU-REWRITER-MODE

    to turn on and off the nu-rewriter
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    This event sets a flag that controls whether the ACL2 rewriter uses the special-purpose nth/update-nth rewriter (nu-rewriter). The flag may have one of three values: nil, t, or :literals.

    :set-nu-rewriter-mode nil        ; do not use nu-rewriter
    :set-nu-rewriter-mode t          ; use nu-rewriter in rewriting
    :set-nu-rewriter-mode :literals  ; use nu-rewriter in rewriting after
                                     ;  a pre-pass through every literal
    (set-nu-rewriter-mode :literals) ; same as above
    

    The value nil prevents the use of the nu-rewriter. The other two values allow the use of the nu-rewriter.

    When the flag is non-nil and the rewriter encounters a term that ``begins with an nth'', the nu-rewriter is applied. By ``begins with an nth'' here we mean either the term is an application of nth or is an application of some nonrecursive function or lambda expression whose body contains an expression that begins with an nth.

    Note that the use of the nu-rewriter here described above is driven by the rewriter, i.e., the nu-rewriter is applied only to terms visited by the rewriter in its inside-out sweep. When the flag is set to :literals the system makes a pre-pass through every goal clause and applies the nu-rewriter to every subterm. The rewriter is then used on the output of that pre-pass.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    We expect to write more documentation as we gain experience with the nu-rewriter.




    acl2-sources/doc/HTML/SET-OVERRIDE-HINTS.html0000664002132200015000000000253012222333531017501 0ustar kaufmannacl2 SET-OVERRIDE-HINTS.html -- ACL2 Version 6.3

    SET-OVERRIDE-HINTS

    set the override-hints
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    See override-hints for a discussion of override-hints. Here we describe how to set them. Note that the effects of set-override-hints events are local to the books or encapsulate events in which they reside; see set-override-hints! to avoid that restriction. Also see add-override-hints to add to the list of override-hints, rather than setting a new list and ignoring the present list.

    General Form:
    (set-override-hints form)
    
    where form evaluates to a list of computed hint forms. The effect of this event is to set the list of override-hints to the result of that evaluation.




    acl2-sources/doc/HTML/SET-OVERRIDE-HINTS_bang_.html0000664002132200015000000000171712222333531020635 0ustar kaufmannacl2 SET-OVERRIDE-HINTS_bang_.html -- ACL2 Version 6.3

    SET-OVERRIDE-HINTS!

    set the override-hints non-locally
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Set-override-hints! is the same as set-override-hints, except that the former is not local to books or encapsulate events in which it occurs. See set-override-hints; also see add-override-hints.




    acl2-sources/doc/HTML/SET-PARALLEL-EXECUTION.html0000664002132200015000000000517412222333531020143 0ustar kaufmannacl2 SET-PARALLEL-EXECUTION.html -- ACL2 Version 6.3

    SET-PARALLEL-EXECUTION

    for ACL2(p): enabling parallel execution for four parallelism primitives
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism. See parallelism-tutorial for an introduction to parallel execution in ACL2.

    General Forms:
    (set-parallel-execution nil) ; default for images not built for parallelism
    (set-parallel-execution t)   ; default for images built for parallelism
    (set-parallel-execution :bogus-parallelism-ok)
    

    Set-parallel-execution takes an argument that specifies the enabling or disabling of parallel execution for the primitives pand, por, plet, and pargs (but not spec-mv-let, whose parallel execution remains enabled). However, without using top-level, calls of parallelism primitives made explicitly in the ACL2 top-level loop, as opposed to inside function bodies, will never cause parallel execution; see parallelism-at-the-top-level. Parallel execution is determined by the value of the argument to set-parallel-execution, as follows.

    Value t:
    All parallelism primitives used in bodies of function definitions are given the opportunity to execute in parallel. However, the use of parallelism primitives directly in the ACL2 top-level loop causes an error.

    Value :bogus-parallelism-ok:
    Parallel execution is enabled, as for value t. However, the use of parallelism primitives directly in the ACL2 top-level loop does not cause an error, but rather, simply results in serial execution for these primitives.

    Value nil:
    All parallelism primitives degrade to their serial equivalents, including their calls made directly in the ACL2 top-level loop. Thus, uses of parallelism primitives do not in themselves cause errors.




    acl2-sources/doc/HTML/SET-PRINT-BASE.html0000664002132200015000000000312612222333520017003 0ustar kaufmannacl2 SET-PRINT-BASE.html -- ACL2 Version 6.3

    SET-PRINT-BASE

    control radix in which numbers are printed
    Major Section:  IO
    

    By default, integers and ratios are printed in base 10. ACL2 also supports printing in radix 2, 8, or 16 by calling set-print-base with the desired radix (base).

      (set-print-base 10 state) ; Default printing
      (set-print-base 16 state) ; Print integers and ratios in hex
    

    Here is a sample log.

      ACL2 !>(list 25 25/3)
      (25 25/3)
      ACL2 !>(set-print-base 16 state)
      <state>
      ACL2 !>(list 25 25/3)
      (19 19/3)
      ACL2 !>
    

    See set-print-radix for how to print the radix, for example, printing the decimal number 25 in print-base 16 as ``#x25'' rather than ``25''. Also see print-control for other user-settable print controls.

    Note: ACL2 events and some other top-level commands (for example, thm, verify, and history commands such as :pe and :pbt) set the print base to 10 during their evaluation. So set-print-base has no effect while these forms are being processed.




    acl2-sources/doc/HTML/SET-PRINT-CASE.html0000664002132200015000000000373112222333520017006 0ustar kaufmannacl2 SET-PRINT-CASE.html -- ACL2 Version 6.3

    SET-PRINT-CASE

    control whether symbols are printed in upper case or in lower case
    Major Section:  IO
    

    By default, symbols are printed in upper case when vertical bars are not required, as specified by Common Lisp. As with Common Lisp, ACL2 supports printing in a "downcase" mode, where symbols are printed in lower case. Many printing functions (some details below) print characters in lower case for a symbol when the ACL2 state global variable print-case has value :downcase and vertical bars are not necessary for printing that symbol. (Thus, this state global functions in complete analogy to the Common Lisp global *print-case*.) The value print-case is returned by (print-case), and may be set using the function set-print-case as follows.

      (set-print-case :upcase   state) ; Default printing
      (set-print-case :downcase state) ; Print symbols in lower case when
                                       ; vertical bars are not required
    
    The ACL2 user can expect that the :downcase setting will have an effect for formatted output (see fmt and see fms) when the directives are ~p, ~P, ~q, or ~Q, for built-in functions princ$ and prin1$, and the ppr family of functions, and not for built-in function print-object$. For other printing functions, the effect of :downcase is unspecified.

    Also see print-control for other user-settable print controls.




    acl2-sources/doc/HTML/SET-PRINT-CIRCLE.html0000664002132200015000000000063312222333520017232 0ustar kaufmannacl2 SET-PRINT-CIRCLE.html -- ACL2 Version 6.3

    SET-PRINT-CIRCLE

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PRINT-CLAUSE-IDS.html0000664002132200015000000000447612222333531017675 0ustar kaufmannacl2 SET-PRINT-CLAUSE-IDS.html -- ACL2 Version 6.3

    SET-PRINT-CLAUSE-IDS

    cause subgoal numbers to be printed when 'prove output is inhibited
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Forms:
    (set-print-clause-ids t)
    :set-print-clause-ids t
    (set-print-clause-ids nil)
    :set-print-clause-ids nil
    
    This command affects output from the theorem prover only when 'prove output is inhibited (see set-inhibit-output-lst) or gag-mode is on (but in that case the :goals setting issues this command automatically; see set-gag-mode). Calling this macro with value t as shown above will cause subsequent proof attempts with 'prove output inhibited to print the subgoal number, so that you can see the progress of the proof; value nil reverts to the default behavior, where this is not the case. On a related note, we point out that you can cause output to be saved for later display; see pso and see pso!.

    If 'prove output is inhibited or gag-mode is on, and if you issue (set-print-clause-ids t) (either explicitly or with (set-gag-mode :goals)), then you can restrict when subgoal numbers are printed. In the following example we restrict to subgoals that are no more than four inductions deep, no more than four casesplits deep, and no more than four single-subgoals deep. For additional relevant explanation, see clause-identifier and see defattach.

    (defun print-clause-id-okp-level-4 (cl-id)
      (declare (xargs :mode :logic :guard (clause-id-p cl-id)))
      (and (<= (length (access clause-id cl-id :pool-lst))
               4)
           (<= (length (access clause-id cl-id :case-lst))
               4)
           (<= (access clause-id cl-id :primes)
               4)))
    
    (defattach print-clause-id-okp print-clause-id-okp-level-4)
    




    acl2-sources/doc/HTML/SET-PRINT-ESCAPE.html0000664002132200015000000000063312222333520017231 0ustar kaufmannacl2 SET-PRINT-ESCAPE.html -- ACL2 Version 6.3

    SET-PRINT-ESCAPE

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PRINT-LENGTH.html0000664002132200015000000000063312222333520017252 0ustar kaufmannacl2 SET-PRINT-LENGTH.html -- ACL2 Version 6.3

    SET-PRINT-LENGTH

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PRINT-LEVEL.html0000664002132200015000000000063112222333520017136 0ustar kaufmannacl2 SET-PRINT-LEVEL.html -- ACL2 Version 6.3

    SET-PRINT-LEVEL

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PRINT-LINES.html0000664002132200015000000000063112222333520017141 0ustar kaufmannacl2 SET-PRINT-LINES.html -- ACL2 Version 6.3

    SET-PRINT-LINES

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PRINT-RADIX.html0000664002132200015000000000273212222333520017142 0ustar kaufmannacl2 SET-PRINT-RADIX.html -- ACL2 Version 6.3

    SET-PRINT-RADIX

    control printing of the radix for numbers
    Major Section:  IO
    

    See set-print-base for background on how the print base affects the printing of numbers. set-print-radix affects whether a radix indicated when a number is printed. The radix is not indicated by default, or after evaluating (set-print-radix nil state). But if set-print-radix is called with a first argument that evaluates to a nonnil value -- for example, (set-print-radix t state) -- then the radix is shown when printing. (This behavior is consistent with the handling of Common Lisp global *print-radix*.) The following log illustrates how this works.

    ACL2 !>(list 25 25/3)
    (25 25/3)
    ACL2 !>(set-print-base 16 state)
    <state>
    ACL2 !>(list 25 25/3)
    (19 19/3)
    ACL2 !>(set-print-radix t state)
    <state>
    ACL2 !>(list 25 25/3)
    (#x19 #x19/3)
    ACL2 !>(set-print-base 10 state)
    <state>
    ACL2 !>(list 25 25/3)
    (25. #10r25/3)
    ACL2 !>(set-print-radix nil state)
    <state>
    ACL2 !>(list 25 25/3)
    (25 25/3)
    ACL2 !>
    




    acl2-sources/doc/HTML/SET-PRINT-READABLY.html0000664002132200015000000000063712222333520017460 0ustar kaufmannacl2 SET-PRINT-READABLY.html -- ACL2 Version 6.3

    SET-PRINT-READABLY

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PRINT-RIGHT-MARGIN.html0000664002132200015000000000064712222333520020126 0ustar kaufmannacl2 SET-PRINT-RIGHT-MARGIN.html -- ACL2 Version 6.3

    SET-PRINT-RIGHT-MARGIN

    See print-control.
    Major Section:  IO
    




    acl2-sources/doc/HTML/SET-PROVER-STEP-LIMIT.html0000664002132200015000000001626712222333531020055 0ustar kaufmannacl2 SET-PROVER-STEP-LIMIT.html -- ACL2 Version 6.3

    SET-PROVER-STEP-LIMIT

    sets the step-limit used by the ACL2 prover
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This event provides a way to limit the number of so-called ``prover steps'' permitted for an event. See with-prover-step-limit for a way to specify the limit on prover steps for a single event, rather than globally. For a related utility based on time instead of prover steps, see with-prover-time-limit. For examples of how step limits work, see the community book books/misc/misc2/step-limits.lisp. For a utility that returns an indicator of the number of prover steps most recently taken, see last-prover-steps.

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. Moreover, its effect is to set the acl2-defaults-table, and hence its effect is local to the book or encapsulate form containing it; see acl2-defaults-table.

    Example Forms:
    (set-prover-step-limit *default-step-limit*) ; no limit on prover steps
    (set-prover-step-limit nil)   ; abbreviation for the form just above
    (set-prover-step-limit 10000) ; allow at most 10,000 prover steps per event
    
    General Form:
    (set-prover-step-limit expr)
    
    where expr evaluates either to nil or else to a natural number not exceeding the value of *default-step-limit*. If that value is nil or the value of *default-step-limit*, then no default limit is placed on the number of prover ``steps'' (see below) during processing of an event. Otherwise, that value is the maximum number of prover steps permitted before an error occurs.

    This event specifies the limit on the number of ``steps'' counted by the ACL2 prover during processing of an event. Currently, a step is counted for each call of the system functions rewrite and expand-abbreviations. However, the steps counted may change in future releases of ACL2, so users would probably be well served by avoiding the assumption that only the above two calls are counted as prover steps.

    Depending on the computer you are using, you may have less than a half-hour of time before the number of prover steps exceeds the maximum step-limit, which is one less than the value of *default-step-limit*. Note however the exception stated above: if the ``limit'' is nil or is the value of *default-step-limit*, then no limit is imposed.

    There is at best a loose connection between the counting of steps and with-prover-time-limit. In particular, for a call of mfc-rw or any mfc- function (see extended-metafunctions), the steps taken during that call are forgotten when returning from that call.

    The limit is relevant for every event, as well as for calls of thm and certify-book -- and more generally, to any form that creates a ``summary context'' to print the usual event summary. The limit is also put in force when entering the proof-checker. A call of set-prover-step-limit applies to each subsequent form unless the call of set-prover-step-limit is within a summary context, in which case its effect disappears when exiting that summary context.

    The limit applies to each event, not just ``atomic'' events. Consider the following example.

    (set-prover-step-limit 500)
    
    (encapsulate
      ()
      (defthm lemma-1 ; takes 380 steps
        (equal (append (append x y) z) (append x y z))
        :rule-classes nil)
      (defthm lemma-2 ; would take 319 steps
        (equal (len (append x y)) (+ (len x) (len y)))
        :rule-classes nil))
    
    The first defthm event, lemma-1 takes 380 steps (as of this writing), as shown in the summary:
    Prover steps counted:  380
    LEMMA-1
    
    The second defthm event, lemma-2, takes 319 steps (as of this writing) when evaluated at the top level. However, in the context above, 380 steps of the available 500 steps (from the set-prover-step-limit event above) have already been taken under the above encapsulate event. Thus, when the number of steps would exceed 120, the proof of lemma-2 is aborted:
    ACL2 Error in STEP-LIMIT:  The prover step-limit, which is 120 in the
    current context, has been exceeded.  See :DOC set-prover-step-limit.
    
    The summary for lemma-2 reflects that situation:
    Prover steps counted:  More than 120
    
    The summary for the encapsulate event then indicates that the available steps for that event have also been exceeded:
    Prover steps counted:  More than 500
    
    The discussion above applies to any event that contains other events, hence applies similarly to progn events.

    For those who use make-event, we note that prover steps in the expansion phase similarly contribute to the total number of steps counted. For example, suppose that the limit is 500 prover steps as above, and you submit (make-event EXPR), where 300 prover steps take place during evaluation of EXPR, producing event EV. Then evaluation of EV will cause an error if it takes more than 200 prover steps. This observation actually can be used to count prover steps for sequences of forms that are not all legal events (see embedded-event-form), such as calls of thm. For example, a small built-in ACL2 test suite that includes thm forms can be run by evaluating the form (mini-proveall), and the steps can be counted as shown below. (Here we assume a fresh ACL2 session; an error would occur if first, we evaluate the event (set-prover-step-limit 500) displayed above.)

    ACL2 !>(make-event (er-progn (mini-proveall) (value '(value-triple nil))))
    [[... output omitted here ...]]
    Summary
    Form:  ( MAKE-EVENT (ER-PROGN ...))
    Rules: NIL
    Warnings:  Double-rewrite, Equiv, Subsume and Non-rec
    Time:  0.38 seconds (prove: 0.04, print: 0.29, other: 0.05)
    Prover steps counted:  41090
     NIL
    ACL2 !>
    




    acl2-sources/doc/HTML/SET-RAW-MODE-ON_bang_.html0000664002132200015000000000171512222333531020216 0ustar kaufmannacl2 SET-RAW-MODE-ON_bang_.html -- ACL2 Version 6.3

    SET-RAW-MODE-ON!

    enter ``raw mode,'' a raw Lisp environment
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This is the same as (set-raw-mode t) except that it first introduces a so-called ``trust tag'' (``ttag'') so that set-raw-mode will be legal. See defttag for a discussion of ttags and how they affect certify-book and include-book.

    See set-raw-mode for a discussion of raw-mode.




    acl2-sources/doc/HTML/SET-RAW-MODE.html0000664002132200015000000001736212222333531016563 0ustar kaufmannacl2 SET-RAW-MODE.html -- ACL2 Version 6.3

    SET-RAW-MODE

    enter or exit ``raw mode,'' a raw Lisp environment
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Below we discuss raw-mode. In brief: The simplest way to turn raw-mode on is :SET-RAW-MODE-ON!, and to turn it off, :SET-RAW-MODE NIL. Also see set-raw-mode-on!.

    Some Related Topics

    ACL2 users often find its careful syntax checking to be helpful during code development. Sometimes it is even useful to do code development in :logic mode, where ACL2 can be used to check termination of (mutually) recursive functions, verify guards, or even prove properties of the functions.

    However, loading code using include-book is much slower than using Common Lisp load in raw Lisp, and in this sense ACL2 can get in the way of efficient execution. Unfortunately, it is error-prone to use ACL2 sources (or their compilations) in raw Lisp, primarily because a number of ACL2 primitives will not let you do so. Perhaps you have seen this error message when trying to do so:

    HARD ACL2 ERROR in ACL2-UNWIND-PROTECT:  Apparently you have tried
    to execute a form in raw Lisp that is only intended to be executed
    inside the ACL2 loop.
    
    Even without this problem it is important to enter the ACL2 loop (see lp), for example in order to set the cbd and (to get more technical) the readtable.

    ACL2 provides a ``raw mode'' for execution of raw Lisp forms. In this mode, include-book reduces essentially to a Common Lisp load. More generally, the ACL2 logical world is not routinely extended in raw mode (some sneaky tricks are probably required to make that happen). To turn raw mode off or on:

    :set-raw-mode t   ; turn raw mode on
    :set-raw-mode nil ; turn raw mode off
    

    The way you can tell that you are in raw mode is by looking at the prompt (see default-print-prompt), which uses a capital ``P'' (suggesting something like program mode, but more so).

    ACL2 P>
    

    Typical benefits of raw mode are fast loading of source and compiled files and the capability to hack arbitrary Common Lisp code in an environment with the ACL2 sources loaded (and hence with ACL2 primitives available). In addition, ACL2 hard errors will put you into the Lisp debugger, rather than returning you to the ACL2 loop, and this may be helpful for debugging; see hard-error and see illegal, but also see break-on-error. However, it probably is generally best to avoid raw mode unless these advantages seem important. We expect the main benefit of raw mode to be in deployment of applications, where load time is much faster than the time required for a full-blown include-book, although in certain cases the fast loading of books and treatment of hard errors discussed above may be useful during development.

    Raw mode is also useful for those who want to build extensions of ACL2. For example, the following form can be put into a certifiable book to load an arbitrary Common Lisp source or compiled file.

    (progn (defttag my-application)
           (progn! (set-raw-mode t)
                   (load "some-file")))
    
    Also see with-raw-mode defined in community book books/hacking/hacker.lisp, see defttag, and see progn!.

    Below are several disadvantages to raw mode. These should discourage users from using it for general code development, as :program mode is generally preferable.

    -- Forms are in essence executed in raw Lisp.  Hence:
       -- Syntax checking is turned off; and
       -- Guard checking is completely disabled.
    -- Table events, including logic, are ignored, as are many
       other events, including defthm and comp.
    -- Soundness claims are weakened for any ACL2 session in which raw
       mode was ever entered; see defttag.
    -- The normal undoing mechanism (see ubt) is not supported.
    -- Unexpected behavior may occur when you return from raw-mode.
       For example, if you redefine a :logic mode function whose guards
       have not been verified, you will not see the change inside the
       ACL2 loop because there, the raw Common Lisp definition is only
       executed after guards have been verified; see guards-and-evaluation
       and see guard-evaluation-table.
    

    We conclude with some details.

    Printing results. The rules for printing results are unchanged for raw mode, with one exception. If the value to be printed would contain any Lisp object that is not a legal ACL2 object, then the print routine is used from the host Lisp, rather than the usual ACL2 printing routine. The following example illustrates the printing used when an illegal ACL2 object needs to be printed. Notice how that ``command conventions'' are observed (see ld-post-eval-print); the ``[Note'' occurs one space over in the second example, and no result is printed in the third example.

    ACL2 P>(find-package "ACL2")
    [Note:  Printing non-ACL2 result.]
    #<The ACL2 package>
    ACL2 P>(mv nil (find-package "ACL2") state)
     [Note:  Printing non-ACL2 result.]
    #<The ACL2 package>
    ACL2 P>(mv t (find-package "ACL2") state)
    ACL2 P>(mv 3 (find-package "ACL2"))
    [Note:  Printing non-ACL2 result.]
    (3 #<The ACL2 package>)
    ACL2 P>
    
    If you have trouble with large structures being printed out, you might want to execute appropriate Common Lisp forms in raw mode, for example, (setq *print-length* 5) and (setq *print-level* 5).

    Include-book. The events add-include-book-dir and delete-include-book-dir have been designed to work with raw mode. However, if you enter raw mode and then evaluate such forms, then the effects of these forms will disappear when you exit raw mode, in which case you can expect to see a suitable warning. Regarding include-book itself: it should work in raw mode as you might expect, at least if a compiled file or expansion file was created when the book was certified; see certify-book.

    Packages. Raw mode disallows the use of defpkg. If you want to create a new package, first exit raw mode with :set-raw-mode nil; you can subsequently re-enter raw mode with :set-raw-mode t if you wish.




    acl2-sources/doc/HTML/SET-RAW-PROOF-FORMAT.html0000664002132200015000000000244212222333531017703 0ustar kaufmannacl2 SET-RAW-PROOF-FORMAT.html -- ACL2 Version 6.3

    SET-RAW-PROOF-FORMAT

    print runes as lists in proof output from simplification
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    General Forms:
    (set-raw-proof-format t)
    :set-raw-proof-format t
    (set-raw-proof-format nil)
    :set-raw-proof-format nil
    
    This command affects output from the theorem prover only when 'prove output is not inhibited (see set-inhibit-output-lst) and gag-mode is off (see set-gag-mode). Calling this macro with value t as shown above will cause simplification steps from proof output, including steps from preprocess (see simple), to print the list of runes used in a list format, rather than in the English proof commentary. This ``raw'' format can be handy when you want to use that list as a basis for hints that you construct for a subsequent proof attempt.




    acl2-sources/doc/HTML/SET-REWRITE-STACK-LIMIT.html0000664002132200015000000000336612222333531020247 0ustar kaufmannacl2 SET-REWRITE-STACK-LIMIT.html -- ACL2 Version 6.3

    SET-REWRITE-STACK-LIMIT

    Sets the rewrite stack depth used by the rewriter
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    Example Forms:
    (set-rewrite-stack-limit 30)                            ; set to small limit
    :set-rewrite-stack-limit 30                             ; same as above
    (set-rewrite-stack-limit *default-rewrite-stack-limit*) ; the default
    (set-rewrite-stack-limit (1- (expt 2 28)))              ; maximum legal limit
    :set-rewrite-stack-limit nil         ; same as above -- essentially, no limit
    
    This event sets the maximum stack depth for calls of certain functions that implement the ACL2 rewriter; see rewrite-stack-limit. It must be a non-negative integer less than 2^28. A call (set-rewrite-stack-limit limit) is equivalent to:
    (table acl2-defaults-table :rewrite-stack-limit limit).
    
    The use of acl2-defaults-table ensures that this event's effect is implicitly local to the book or encapsulate form in which it occurs.

    For a different but somewhat related concept, see backchain-limit.




    acl2-sources/doc/HTML/SET-RULER-EXTENDERS.html0000664002132200015000000000073312222333531017632 0ustar kaufmannacl2 SET-RULER-EXTENDERS.html -- ACL2 Version 6.3

    SET-RULER-EXTENDERS

    See ruler-extenders.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/SET-RW-CACHE-STATE.html0000664002132200015000000001016612222333531017412 0ustar kaufmannacl2 SET-RW-CACHE-STATE.html -- ACL2 Version 6.3

    SET-RW-CACHE-STATE

    set the default rw-cache-state
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    The ACL2 rewriter uses a data structure, called the rw-cache (rewriter cache), to save failed attempts to apply conditional rewrite rules. The regression suite has taken approximately 11% less time with this mechanism. The rw-cache is active by default but this event allows it to be turned off or modified. Note that this event is local to its context (from encapsulate or include-book). For a non-local version, use set-rw-cache-state!.

    Example forms:
    (set-rw-cache-state :atom)     ; default: rw-cache cleared for each literal
                                   ;   (i.e., hypothesis or conclusion of a goal)
    (set-rw-cache-state nil)       ; rw-cache is inactive
    (set-rw-cache-state t)         ; rw-cache persists beyond each literal
    (set-rw-cache-state :disabled) ; rw-cache is inactive, but the rw-cache-state
                                   ;   transitions to state t after
                                   ;   simplification takes place
    
    General Form:
    (set-rw-cache-state val)
    
    where val evaluates to one of the four values shown in ``Example forms'' above. The default is :atom, which enables the rw-cache but clears it before rewriting a hypothesis or conclusion of any goal. The value t is provides more aggresive use of the rw-cache, basically preserving the rw-cache when there is a single subgoal. The value :disabled is the same as t, except that the rw-cache is initially inactive and only becomes active when some simplification has taken place. We have seen a few cases where value t will make a proof fail but :disabled does not.

    The following example illustrates the rw-cache in action. You will see a break during evaluation of the thm form. Type :eval and you will see a failed rewriting attempt. Type :go to continue, and at the next break type :eval again. This time you will see the same failed rewriting attempt, but this time labeled with a notation saying that the failure was cached earlier, which indicates that this time the rewriter did not even attempt to prove the hypothesis of the rewrite rule f1->f2.

    (defstub f1 (x) t)
    (defstub f2 (x) t)
    (defaxiom f1->f2
             (implies (f1 x) (equal (f2 x) t)))
    :brr t
    :monitor (:rewrite f1->f2) t
    (thm (equal (car (f2 a)) (cdr (f2 a))))
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded. It is local to the book or encapsulate form in which it occurs (see set-rw-cache-state! for a corresponding non-local event).

    We also note that rw-cache-state changes may also be caused at the subgoal level; see hints.

    We welcome you to experiment with different rw-cache states. If the more aggressive values of t and :disabled cause proofs to fail, then you can revert to the default of :atom or even turn off the rw-cache using (set-rw-cache-state nil). We don't expect users to need a deep knowledge of the rw-cache in order to do such experiments, but readers interested in details of the rw-cache implementation are invited to read the ``Essay on Rw-cache'' in the ACL2 source code.




    acl2-sources/doc/HTML/SET-RW-CACHE-STATE_bang_.html0000664002132200015000000000143612222333531020540 0ustar kaufmannacl2 SET-RW-CACHE-STATE_bang_.html -- ACL2 Version 6.3

    SET-RW-CACHE-STATE!

    set the default rw-cache-state non-locally
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Please see set-rw-cache-state, which is the same as set-rw-cache-state! except that the latter is not local to the encapsulate or the book in which it occurs.




    acl2-sources/doc/HTML/SET-SAVED-OUTPUT.html0000664002132200015000000000700312222333531017277 0ustar kaufmannacl2 SET-SAVED-OUTPUT.html -- ACL2 Version 6.3

    SET-SAVED-OUTPUT

    save proof output for later display with :pso or :pso!
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-saved-output t t)    ; save proof output for later, but inhibit it now
    (set-saved-output t :all) ; save proof output for later, but inhibit all
                              ;   output (except WARNING!, for critical warnings,
                              ;   and ERROR, unless these are already inhibited)
    :set-saved-output t :all  ; same as the line above
    (set-saved-output t nil)  ; save proof output for later, but print it now too
    (set-saved-output nil t)  ; do not save proof output, and inhibit it
    (set-saved-output nil nil); do not save proof output or inhibit output
    (set-saved-output nil :same), (set-saved-output t :same)
                              ; save proof output or not, as indicated, but do
                              ;   not change which output is inhibited
    (set-saved-output nil :normal)
                              ; the behavior when ACL2 first starts up: do not
                              ;   save output, and only inhibit proof-tree output
    (set-saved-output t '(warning observation proof-tree prove))
                              ; save proof output for later, and inhibit the
                              ;   indicated kinds of output
    
    General Form:
    (set-saved-output save-flg inhibit-flg)
    
    Parameter save-flg is t to cause output to be saved for later display using pso or pso!; see pso and see pso!, and see the documentation for proof-checker commands of the same names. Set save-flg to nil to turn off this feature; except, it always stays on in proof-checker sessions entered with verify. The other argument, inhibit-flg, controls whether output should be inhibited when it is created (normally, during a proof attempt). So a common combination is to set both arguments to t, to indicate that output should be suppressed for now but saved for printing with pso or pso!. The examples above give a good summary of the functionality for the second argument.

    Saved output is cleared at the start of every event, and also at the start of every proof-checker commands that invoke the prover. Note that interactive proof-checker commands, that is, from a proof-checker session entered with verify, are always run with output saved.

    Also see set-gag-mode; and see set-print-clause-ids, which causes subgoal numbers to be printed during proof attempts when output is inhibited.

    See set-inhibit-output-lst if you want to inhibit certain output from the prover but not other output (e.g., not the summary), and you don't want to save any output.




    acl2-sources/doc/HTML/SET-SERIALIZE-CHARACTER.html0000664002132200015000000000071512222333530020222 0ustar kaufmannacl2 SET-SERIALIZE-CHARACTER.html -- ACL2 Version 6.3

    SET-SERIALIZE-CHARACTER

    See with-serialize-character.
    Major Section:  SERIALIZE
    




    acl2-sources/doc/HTML/SET-SPLITTER-OUTPUT.html0000664002132200015000000000433512222333531017710 0ustar kaufmannacl2 SET-SPLITTER-OUTPUT.html -- ACL2 Version 6.3

    SET-SPLITTER-OUTPUT

    turn on or off reporting of rules that may have caused case splits
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-splitter-output t)   ; enable  reports of ``splitter'' rules (default)
    (set-splitter-output nil) ; disable reports of ``splitter'' rules
    

    After evaluation of the form (set-splitter-output t) (the default), then whenever prove output is not inhibited (see set-inhibit-output-lst), ACL2 will report rewrite and definition rules that may have reduced a goal to more than one subgoal. See splitter for how to interpret such reports. We call such rules ``splitter rules'' for the goal that is being split. This information can be useful in deciding how to eliminate large splits, for example of Goal into Subgoal 1000 through Subgoal 1, by disabling some splitter rules. If you want to avoid the printing of such information, you can put the form (set-splitter-output t) in your customization file; see acl2-customization.

    Note that this command does not change the ACL2 world; it only modifies the state. More precisely, it sets a state global to the indicated value. (See state for discussion of the ``global-table'' of the state.) When prove output is enabled (see set-inhibit-output-lst), the value of that state global is the value of the form (splitter-output); otherwise the value of that form is nil.

    Again, see splitter for the effects of turning on the reporting of splitter rules.




    acl2-sources/doc/HTML/SET-STATE-OK.html0000664002132200015000000000706012222333531016571 0ustar kaufmannacl2 SET-STATE-OK.html -- ACL2 Version 6.3

    SET-STATE-OK

    allow the use of STATE as a formal parameter
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    In brief: The variable symbol STATE has an unusual status in ACL2. In order to use it, you either need to issue :set-state-ok t, as we explain below, or you need to declare it to be a stobj, as explained elsewhere (see declare-stobjs). Now we explain in more detail.

    Because the variable symbol STATE denotes the ``current ACL2 state,'' ACL2 treats the symbol very restrictively when it occurs as a formal parameter of a defined function. The novice user, who is unlikely to be aware of the special status of that symbol, is likely to be confused when error messages about STATE are printed in response to the innocent choice of that symbol as a formal variable. Therefore the top-level ACL2 loop can operate in a mode in which STATE is simply disallowed as a formal parameter.

    For a discussion of STATE, See state and see stobj. Roughly speaking, at the top-level, the ``current ACL2 state'' is denoted by the variable symbol STATE. Only the current state may be passed into a function expecting a state as an argument. Furthermore, the name of the formal parameter into which the current state is passed must be STATE and nothing but the current state may be passed into a formal of that name. Therefore, only certain access and change functions can use that formal -- namely those with a STATE formal -- and if any such function produces a new state it becomes the ``current state'' and must be passed along in the STATE position thereafter. Thus, ACL2 requires that the state be single-threaded. This, in turn, allows us to represent only one state at a time and to produce new states from it destructively in a von Neumaneque fashion. The syntactic restrictions on the variable STATE are enforced by the translate mechanism (see trans and see term) when terms are read.

    To prevent the novice user from seeing messages prohibiting certain uses of the variable symbol STATE ACL2 has a mode in which it simply disallows the use of that symbol as a formal parameter. Use of the symbol causes a simple error message. The system is initially in that mode.

    To get out of that mode, execute:

    :set-state-ok t ;;; or, (set-state-ok t)
    
    It is not recommended that you do this until you have read the documentation of STATE.

    To enter the mode in which use of state is prohibited as a formal parameter, do:

    :set-state-ok nil
    

    The mode is stored in the defaults table, See acl2-defaults-table. Thus, the mode may be set locally in books.




    acl2-sources/doc/HTML/SET-TAU-AUTO-MODE.html0000664002132200015000000001756512222333531017336 0ustar kaufmannacl2 SET-TAU-AUTO-MODE.html -- ACL2 Version 6.3

    SET-TAU-AUTO-MODE

    turn on or off automatic (``greedy'') generation of :tau-system rules
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-tau-auto-mode t)      ; select automatic (``greedy'') mode
    (set-tau-auto-mode nil)    ; select manual mode
    

    This event is equivalent to (table acl2-defaults-table :tau-auto-modep <t-or-nil>), and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. See introduction-to-the-tau-system for background details.

    The tau system gathers rules for its database in one of two ways: greedily or only at the explicit command of the user. ``Greedy'' mode is officially called ``automatic mode'' and is the default. The other mode is called ``manual mode.''

    In automatic mode, all rules processed by ACL2 are also considered for inclusion in the tau database: if the :corollary of some proved theorem happens to fit into one of the forms described in :tau-system, that rule is quietly added to the tau database regardless of what :rule-classes the user named for the :corollary. Of course, such rules are also stored in the ways named by the user. See the Design Philosophy section of introduction-to-the-tau-system for a discussion of why the tau system is greedy by default. More details are given on automatic mode after we explain manual mode.

    To more tightly control the rules available to the tau system, the user may select manual mode by executing (set-tau-auto-mode nil). In manual mode, the only events that create :tau-system rules are defthm events explicitly specifying the :tau-system rule class in the :rule-classes argument. Of course, for a :tau-system rule to be created from a proved formula (or its specified :corollary), the formula must be of the appropriate shape (syntactic form). See tau-system. In manual mode, if the :tau-system rule class is specified but the formula is not of an appropriate form an error is signalled. (Note: even in manual mode, monadic functions that are recognized as Boolean are classified as tau predicates; but no rules are created for them.)

    Returning to our discussion of automatic mode, a :tau-system rule may be created by any of the events below, provided the definition or formula proved is of an appropriate shape:

    (1) defun events introducing ``big switch'' or ``mv-nth synonyms,''

    (2) defun events creating type-prescription rules that may be also represented as ``signature rules'' of form 1, and

    (3) any defthm event with a non-nil :rule-classes argument if no :tau-system rule is among the rule classes and the formula proved is in the shape of any tau-system rule.

    Of course, events such as defstobj and defevaluator may also add rules to the tau database when they execute the defun and defthm events implicit in their descriptions. See tau-system for a description of the various shapes mentioned above.

    Note that any rule (of any rule class) created when the tau system is in manual mode is also created in automatic mode. For example, if an event would create a :DEFINITION, :TYPE-PRESCRIPTION, FORWARD-CHAINING, or :REWRITE rule when the tau system is in manual mode, then the event will create that same rule when the tau system is in automatic mode. Automatic mode just means that some additional :tau-system rules may be created.

    Of course, if a defthm event explicitly specifies a :tau-system rule class, then even if the tau system is in automatic mode, that tau rule is created from the proved formula (or the specified :corollary) or else an error is caused. But if the tau system is in automatic mode and a defthm event doesn't explicitly specify a :tau-system rule class, then the system quietly checks each specified :corollary -- or, in the absence of any :corollary, it checks the proved formula -- for whether it can be stored as a tau rule. If so, then the system stores a tau rule, in addition to storing the specified rule. Of course, no error is signalled if a proved formula of some non-:tau-system rule class fails to be of an appropriate shape for the tau system.

    In automatic mode, if the :rule-classes specified for defthm included several corollaries and any one of them is of class :tau-system then the only tau system rules created are those explicitly classed as :tau-system rules. For example, suppose a defthm has one :corollary stored as a :rewrite rule and another :corollary stored as a :tau-system rule. But suppose the :rewrite rule happens to also to fit the form of a :tau-system rule. Is it added to the tau database or not? The answer is no. If you have taken the trouble to specify :tau-system corollaries for an event, then those corollaries are the only ones stored as tau sytem rules from that event. Note that had both corollaries been classed as :rewrite rules (and been of acceptable :tau-system form) both would have also been made :tau-system rules. This also allows you be in automatic mode and state a :rewrite or other non-:tau-system rule and prevent it from being also made a tau system rule: just add a frivolous :tau-system :corollary like (booleanp (integerp x)).

    Recall that the use of tau rules is controlled by the rune (:EXECUTABLE-COUNTERPART TAU-SYSTEM). When that rune is disabled, no tau rules are used in proofs. However, the tau system continues to collect tau rules if the system is in automatic mode. Thus, if and when the tau system is re-enabled, rules automatically generated while the tau system was disabled will be used as usual by the tau system.

    Finally, note that defthm events with :rule-classes nil do not create :tau-system rules even if the formula proved is of an appropriate shape, regardless of whether the tau system is in automatic or manual mode.

    The macro tau-status provides a convenient way to enable/disable the :executable-counterpart of tau-system and/or to switch between manual and automatic modes. It may also be used to determine the current settings of those two flags.




    acl2-sources/doc/HTML/SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR.html0000664002132200015000000000523312222333531022353 0ustar kaufmannacl2 SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR.html -- ACL2 Version 6.3

    SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR

    for ACL2(p): control the action taken when the thread limit is exceeded
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    General Forms:
    (set-total-parallelism-work-limit-error t)   ; cause error (default)
    (set-total-parallelism-work-limit-error nil) ; disable error and
                                                 ; continue serially
    

    See parallelism-tutorial, Section ``Another Granularity Issue Related to Thread Limitations'', for an explanation of how the host Lisp can run out of threads. See set-total-parallelism-work-limit for further details, including an explanation of how to manage the limit that triggers this error.

    The value of state global total-parallelism-work-limit-error dictates what occurs when the underlying runtime system runs reaches a limit on the number of threads for parallel computation. By default, when this limit is reached, the ACL2(p) user will receive an error and computation will halt. At this point, the ACL2(p) user has the following options.

    (1) Remove the limit by evaluating the following form.

    (set-total-parallelism-work-limit :none)
    

    (2) Disable the error so that execution continues serially once the available thread resources have been exhausted.

    (set-total-parallelism-work-limit-error nil)
    

    (3) Increase the limit on number of threads that ACL2(p) is willing to create, in spite of potential risk (see set-total-parallelism-work-limit). In this case, the following query returns the current limit.

    (f-get-global 'total-parallelism-work-limit)
    
    Then to increase that limit, evaluate the following form:
    (set-total-parallelism-work-limit <new-integer-value>)
    

    For example, suppose that the value of total-parallelism-work-limit was originally 10,000. Then evaluation of the following form increases that limit to 13,000.

    (set-total-parallelism-work-limit 13000)
    




    acl2-sources/doc/HTML/SET-TOTAL-PARALLELISM-WORK-LIMIT.html0000664002132200015000000000541012222333531021461 0ustar kaufmannacl2 SET-TOTAL-PARALLELISM-WORK-LIMIT.html -- ACL2 Version 6.3

    SET-TOTAL-PARALLELISM-WORK-LIMIT

    for ACL2(p): set thread limit for parallelism primitives
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism. While the most common use of the limit described below is in parallel proof (see parallel-proof), it also applies to all parallelism primitives (see parallel-programming) except spec-mv-let -- though we expect that rather few programming applications will encouter this limit.

    General Forms:
    (set-total-parallelism-work-limit :none)     ; disable the limit
    (set-total-parallelism-work-limit <integer>) ; set limit to <integer>
    

    See parallelism-tutorial, Section ``Another Granularity Issue Related to Thread Limitations'', for an explanation of how the host Lisp can run out of threads. Also see set-total-parallelism-work-limit-error.

    If the underlying runtime system (the Operating System, the host Lisp, etc.) is unable to provide enough threads to finish executing the parallelism work given to it, the runtime system can crash in a very unpleasant manner (ranging from a Lisp error to completely freezing the machine). To avoid this unpleasantness, ACL2(p) will attempt to avoid creating so much parallelism that the runtime system crashes.

    ACL2 initially uses a conservative estimate to limit the number of threads. To tell ACL2(p) to use a different limit, call set-total-parallelism-work-limit with the new limit. For example, if the current default limit is 10,000, then to allow 13,000 threads to exist, issue the following form at the top level.

    (set-total-parallelism-work-limit 13000)
    

    To disable this limit altogether, evaluate the following form:

    (set-total-parallelism-work-limit :none)
    

    The default value of total-parallelism-work-limit can be found by calling function default-total-parallelism-work-limit. If the default value is too high for your system please notify the ACL2 maintainers with a limit that does work for your system, as they might then lower the default limit.




    acl2-sources/doc/HTML/SET-TRACE-EVISC-TUPLE.html0000664002132200015000000000564512222333531020005 0ustar kaufmannacl2 SET-TRACE-EVISC-TUPLE.html -- ACL2 Version 6.3

    SET-TRACE-EVISC-TUPLE

    set the trace evisc tuple
    Major Section:  TRACE
    

    A trace evisc-tuple, which is set by this utility, provides a means to restrict printing during tracing. See evisc-tuple for an introduction to evisc-tuples; also see set-evisc-tuple and see set-iprint.

    By default the ACL2 trace mechanism, trace$, automatically deals with stobjs, the logical world, and certain other large structures. See trace$, in particular the documentation of trace$ option :hide. However, even with that default behavior you may want to restrict what is printed according to the print-level and print-length of an evisc-tuple; see evisc-tuple.

    Examples:
    
    ; Set trace evisc tuple to a standard value, using current Lisp *print-level*
    ; and *print-length* variables:
    (set-trace-evisc-tuple t state)
    
    ; Set trace evisc tuple back to its default:
    (set-trace-evisc-tuple nil state)
    
    ; Set trace evisc tuple to restrict print-level to 3 and print-length to 4,
    ; while hiding the logical world and suitably printing stobjs even if trace$
    ; option ``:hide nil'' is used.  (Note: calling trace-evisceration-alist
    ; directly requires removing this function as `untouchable', which requires a
    ; trust tag; see remove-untouchable.)
    (set-trace-evisc-tuple
     (evisc-tuple 3 4 (trace-evisceration-alist state) nil)
     state)
    
    General Forms:
    
    (set-trace-evisc-tuple nil state) ; trace evisc-tuple set to standard value
    (set-trace-evisc-tuple   t state) ; trace evisc-tuple set to hide the logical
                                      ;   world and deal with stobjs even when
                                      ;   trace$ option ``:hide nil'' is supplied
    (set-trace-evisc-tuple evisc-tuple state)
                                      ; tracing set to use indicated evisc-tuple
    

    See trace$ for a discussion of ACL2 tracing. The evisc-tuple used by that trace utility is the one last installed by set-trace-evisc-tuple (or by set-evisc-tuple for the trace-evisc-tuple) -- initially to the default of nil -- unless overriden by trace option :evisc-tuple.

    Remark. If you use value t, then ACL2 will ensure that the logical world and stobjs are kept up-to-date in the trace evisc-tuple.




    acl2-sources/doc/HTML/SET-VERIFY-GUARDS-EAGERNESS.html0000664002132200015000000000750612222333531020710 0ustar kaufmannacl2 SET-VERIFY-GUARDS-EAGERNESS.html -- ACL2 Version 6.3

    SET-VERIFY-GUARDS-EAGERNESS

    the eagerness with which guard verification is tried.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Forms:                        try guard verification?
    (set-verify-guards-eagerness 0) ; no, unless :verify-guards t
    (set-verify-guards-eagerness 1) ; yes if a guard or type is supplied
    (set-verify-guards-eagerness 2) ; yes, unless :verify-guards nil
    
    Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    General Form:
    (set-verify-guards-eagerness n)
    
    where n is a variable-free term that evaluates to 0, 1, or 2. This macro is essentially equivalent to
    (table acl2-defaults-table :verify-guards-eagerness n)
    
    and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table. However, unlike the above simple call of the table event function (see table), no output results from a set-verify-guards-eagerness event.

    Set-verify-guards-eagerness may be thought of as an event that merely sets a flag to 0, 1, or 2. The flag is used by certain defun events to determine whether guard verification is tried. The flag is irrelevant to those defun events in :program mode and to those defun events in which an explicit :verify-guards setting is provided among the xargs. In the former case, guard verification is not done because it can only be done when logical functions are being defined. In the latter case, the explicit :verify-guards setting determines whether guard verification is tried. So consider a :logic mode defun in which no :verify-guards setting is provided. Is guard verification tried? The answer depends on the eagerness setting as follows. If the eagerness is 0, guard verification is not tried. If the eagerness is 1, it is tried if and only if a guard is explicitly specified in the defun, in the following sense: there is an xargs keyword :guard or :stobjs or a type declaration. If the eagerness is 2, guard verification is tried.

    The default behavior of the system is as though the :verify-guards-eagerness is 1. The current behavior can be ascertained by evaluating the form (default-verify-guards-eagerness (w state)).




    acl2-sources/doc/HTML/SET-WATERFALL-PARALLELISM-HACKS-ENABLED.html0000664002132200015000000000343512222333531022347 0ustar kaufmannacl2 SET-WATERFALL-PARALLELISM-HACKS-ENABLED.html -- ACL2 Version 6.3

    SET-WATERFALL-PARALLELISM-HACKS-ENABLED

    for ACL2(p): enable waterfall-parallelism hacks
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    General Forms:
    (set-waterfall-parallelism-hacks-enabled t)
    (set-waterfall-parallelism-hacks-enabled nil)
    

    Some features (e.g., override-hints and clause-processors) of serial ACL2 are by default not available in ACL2(p) with waterfall parallelism enabled, because they offer a mechanism to modify state that is unsound. To allow or (once again) disallow the use the these features in ACL2(p), call set-waterfall-parallelism-hacks-enabled with argument t or nil, respectively.

    Set-waterfall-parallelism-hacks-enabled requires the use of a trust tag (see defttag). One can call set-waterfall-parallelism-hacks-enabled! instead, which will automatically install a trust tag named :waterfall-parallelism-hacks.

    See error-triples-and-parallelism for further related discussion.




    acl2-sources/doc/HTML/SET-WATERFALL-PARALLELISM-HACKS-ENABLED_bang_.html0000664002132200015000000000115512222333531023472 0ustar kaufmannacl2 SET-WATERFALL-PARALLELISM-HACKS-ENABLED_bang_.html -- ACL2 Version 6.3

    SET-WATERFALL-PARALLELISM-HACKS-ENABLED!

    for ACL2(p): enabling waterfall parallelism hacks
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    See set-waterfall-parallelism-hacks-enabled.




    acl2-sources/doc/HTML/SET-WATERFALL-PARALLELISM.html0000664002132200015000000001420212222333531020462 0ustar kaufmannacl2 SET-WATERFALL-PARALLELISM.html -- ACL2 Version 6.3

    SET-WATERFALL-PARALLELISM

    for ACL2(p): configuring the parallel execution of the waterfall
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    General Forms:
    (set-waterfall-parallelism nil)        ; never parallelize (serial execution)
    (set-waterfall-parallelism :full)      ; always parallelize
    (set-waterfall-parallelism :top-level) ; parallelize top-level subgoals
    (set-waterfall-parallelism             ; parallelize if sufficient resources
      :resource-based)                     ;   (recommended setting)
    (set-waterfall-parallelism t)          ; alias for :resource-based
    (set-waterfall-parallelism             ; parallelize if sufficient resources
      :resource-and-timing-based           ;   and suggested by prior attempts
    (set-waterfall-parallelism             ; never parallelize but use parallel
      :pseudo-parallel)                    ;   code base (a debug mode)
    

    Set-waterfall-parallelism evaluates its argument, which specifies the enabling or disabling of the parallel execution of ACL2's main proof process, the waterfall.

    It also sets state global waterfall-printing to an appropriate value. See set-waterfall-printing.

    Note that not all ACL2 features are supported when waterfall-parallelism is set to non-nil (see unsupported-waterfall-parallelism-features).

    A value of t is treated the same as a value of :resource-based and is provided for user convenience.

    :Resource-based waterfall parallelism typically achieves the best performance in ACL2(p), while maintaining system stability, so :resource-based (or equivalently, t) is the recommended value.

    A value of nil indicates that ACL2(p) should never prove subgoals in parallel.

    A value of :full indicates that ACL2(p) should always prove independent subgoals in parallel.

    A value of :top-level indicates that ACL2(p) should prove each of the top-level subgoals in parallel but otherwise prove subgoals in a serial manner. This mode is useful when the user knows that there are enough top-level subgoals, many of which take a non-trivial amount of time to be proved, such that proving them in parallel will result in a useful reduction in overall proof time.

    A value of :resource-based (or equivalently, t) indicates that ACL2(p) should use its built-in heuristics to determine whether CPU core resources are available for parallel execution. Note that ACL2(p) does not hook into the operating system to determine the workload on the machine. ACL2(p) works off the assumption that it is the only process using significant CPU resources, and it optimizes the amount of parallelism based on the number of CPU cores in the system. (Note that ACL2(p) knows how to obtain the number of CPU cores from the operating system in CCL, but that, in SBCL and in Lispworks, a constant is used instead).

    During the first proof attempt of a given conjecture, a value of :resource-and-timing-based results in the same behavior as with :resource-based. However, on subsequent proof attempts, the time it took to prove each subgoal will be considered when deciding whether to parallelize execution. If a particular theorem's proof is already achieving satisfactory speedup via :resource-based parallelism, there is no reason to try this setting. However, if the user wishes to experiment, the :resource-and-timing-based setting may improve performance. Note that since the initial run does not have the subgoal proof times available, this mode will never be better than the :resource-based setting for non-interactive theorem proving.

    A value of :pseudo-parallel results in using the parallel waterfall code, but with serial execution. This setting is useful for debugging the code base that supports parallel execution of the waterfall. For example, you may wish to use this mode if you are an ``ACL2 Hacker'' who would like to see comprehensible output from tracing (see trace$) the @par versions of the waterfall functions.

    The following remark pertains to those using the `HONS' experimental extension of ACL2 (see hons-and-memoization; in particular, see memoize). Since memoization is not supported when waterfall parallelism is enabled (see unsupported-waterfall-parallelism-features), then when set-waterfall-parallelism is called with a non-nil value, all memoized functions are unmemoized. When set-waterfall-parallelism is again called with a nil value, those memoization settings are restored.

    Set-waterfall-parallelism is an embedded event form. However, a call of this macro will not affect waterfall-parallelism when including a certified book that contains that call. For such an effect, you may use the following make-event form.

    (make-event (er-progn (set-waterfall-parallelism :full)
                          (value '(value-triple nil)))
                :check-expansion t)
    

    To enable waterfall parallelism for book certification using ACL2(p), see waterfall-parallelism-for-book-certification.




    acl2-sources/doc/HTML/SET-WATERFALL-PRINTING.html0000664002132200015000000000773612222333531020165 0ustar kaufmannacl2 SET-WATERFALL-PRINTING.html -- ACL2 Version 6.3

    SET-WATERFALL-PRINTING

    for ACL2(p): configuring the printing that occurs within the parallelized waterfall
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    General Forms:
    (set-waterfall-printing :full)    ; print everything
    (set-waterfall-printing :limited) ; print a subset that's thought to be useful
    (set-waterfall-printing :very-limited) ; print an even smaller subset
    

    Set-waterfall-printing evaluates its argument, which indicates how much printing should occur when executing ACL2 with the parallelized version of the waterfall. It only affects the printing that occurs when parallelism mode is enabled for the waterfall (see set-waterfall-parallelism).

    A value of :full is intended to print the same output as in serial mode. This output will be interleaved unless the waterfall-parallelism mode is one of nil or :pseudo-parallel.

    A value of :limited omits most of the output that occurs in the serial version of the waterfall. Instead, the proof attempt prints key checkpoints (see acl2p-key-checkpoints). The value of :limited also prints messages that indicate which subgoal is currently being proved, along with the wall-clock time elapsed since the theorem began its proof; and if state global 'waterfall-printing-when-finished has a non-nil value, then such a message will also be printed at the completion of each subgoal. The function print-clause-id-okp may receive an attachment to limit such printing; see set-print-clause-ids. Naturally, these subgoal numbers can appear out of order, because the subgoals can be proved in parallel.

    A value of :very-limited is treated the same as :limited, except that instead of printing subgoal numbers, the proof attempt prints a period (`.') each time it starts a new subgoal.

    Note that this form cannot be used at the top level of a book, or of a progn or encapsulate event. Here is a workaround for use in such contexts; of course, you may replace :very-limited with any other legal argument for set-waterfall-printing.

    (make-event (er-progn (set-waterfall-printing :very-limited)
                          (value '(value-triple nil))))
    
    (For more about event contexts and the use of make-event, see make-event, in particular the section ``Restriction to Event Contexts.'')

    The following form has the effect described above, except that it will affect waterfall-printing even when including a certified book that contains it.

    (make-event (er-progn (set-waterfall-printing :very-limited)
                          (value '(value-triple nil)))
                :check-expansion t)
    

    Note that set-waterfall-printing is automatically called by set-waterfall-parallelism.

    To enable the printing of information when a subgoal is finished, assign a non-nil value to global waterfall-printing-when-finished. This can be accomplished by entering the following at the top level:

    (f-put-global 'waterfall-printing-when-finished t state)
    




    acl2-sources/doc/HTML/SET-WELL-FOUNDED-RELATION.html0000664002132200015000000000434712222333531020447 0ustar kaufmannacl2 SET-WELL-FOUNDED-RELATION.html -- ACL2 Version 6.3

    SET-WELL-FOUNDED-RELATION

    set the default well-founded relation
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (set-well-founded-relation lex2)
    
    provided lex2 has been proved to be a well-founded relation (see well-founded-relation). Note: This is an event! It does not print the usual event summary but nevertheless changes the ACL2 logical world and is so recorded.

    General Form:
    (set-well-founded-relation rel)
    
    where rel has been proved to be a well-founded relation on objects satisfying some predicate, mp; see well-founded-relation. This macro is equivalent to (table acl2-defaults-table :well-founded-relation 'rel), and hence is local to any books and encapsulate events in which it occurs; see acl2-defaults-table.

    This event sets the default well-founded relation to be that imposed on mp-measures by the relation rel. Subsequently, if a recursively defined function is submitted to defun with no explicitly given :well-founded-relation argument, defun uses the default relation, rel, and the associated domain predicate mp used in its well-foundedness theorem. That is, the termination conditions generated will require proving that the measure used by the defun is an mp-measure and that in every recursive call the measure of the arguments decreases according to rel.




    acl2-sources/doc/HTML/SET-WORMHOLE-DATA.html0000664002132200015000000000220112222333521017334 0ustar kaufmannacl2 SET-WORMHOLE-DATA.html -- ACL2 Version 6.3

    SET-WORMHOLE-DATA

    sets the wormhole data object in a wormhole status object
    Major Section:  MISCELLANEOUS
    

    General Form:  (set-wormhole-data whs data)
    

    See wormhole. Whs should be a well-formed wormhole status; data is arbitrary. This function returns a new status with the same entry code as whs but with the new data. It avoids unnecessary consing if the data for whs is already set to data. This function does not affect state or a wormhole's hidden status. It just returns a (possibly) new status object suitable as the value of the lambda expressions in wormhole-eval and wormhole.




    acl2-sources/doc/HTML/SET-WORMHOLE-ENTRY-CODE.html0000664002132200015000000000226012222333521020241 0ustar kaufmannacl2 SET-WORMHOLE-ENTRY-CODE.html -- ACL2 Version 6.3

    SET-WORMHOLE-ENTRY-CODE

    sets the wormhole entry code in a wormhole status object
    Major Section:  MISCELLANEOUS
    

    General Form:  (set-wormhole-entry-code whs code)
    
    See wormhole. Whs should be a well-formed wormhole status and code should be :ENTER or :SKIP. This function returns a new status with the specified entry code but the same data as whs. It avoids unnecessary consing if the entry code for whs is already set to code. This function does not affect state or a wormhole's hidden status. It just returns a (possibly) new status object suitable as the value of the lambda expressions in wormhole-eval and wormhole.




    acl2-sources/doc/HTML/SET-WRITE-ACL2X.html0000664002132200015000000003243012222333531017102 0ustar kaufmannacl2 SET-WRITE-ACL2X.html -- ACL2 Version 6.3

    SET-WRITE-ACL2X

    cause certify-book to write out a .acl2x file
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example Forms:
    (set-write-acl2x nil state)
    (set-write-acl2x t state)
    (set-write-acl2x '(nil) state) ; same as just above, but allow inclusion of
                                   ; uncertified books during certify-book
    (set-write-acl2x '(t) state)
    (set-write-acl2x '(include-book-with-locals) state)
    

    General Form:
    (set-write-acl2x val state)
    
    where val evaluates to t, nil, or a one-element list whose element is a legal value for the global 'ld-skip-proofsp; see ld-skip-proofsp. The value returned is an error triple, which in the non-error case is (mv nil v state), where v is the value of val and state is the result of updating the input state by assigning state global 'write-acl2x the value v.

    The command (set-write-acl2x val state) assigns the value of val to the state global variable 'write-acl2x, affecting whether or not certify-book writes out a file with extension acl2x, called a ``.acl2x file'' and pronounced ``dot-acl2x file''. Such a file is read or written by certify-book when it is supplied with keyword argument :acl2x t. By default, such a call of certify-book reads a .acl2x file; but if the value of state global variable 'write-acl2x is not nil, then certify-book writes a .acl2x file (in which case it is illegal to specify a non-nil value for certify-book keyword argument :pcert). Consider for example (certify-book "foo" 0 nil :acl2x t). By default, this command reads file foo.acl2x, which supplies replacements for some forms in foo.lisp, as described later below. But if the value of state global 'write-acl2x is not nil, then instead, this certify-book command writes such a file foo.acl2x.

    Before we discuss the function of .acl2x files, we first explain more about how a non-nil value of state global 'write-acl2x affects the behavior of a command (certify-book ... :acl2x t ...). A significant effect on the behavior is that after processing events in the given book, ACL2 writes out a .acl2x file and then returns, skipping the other subsequent actions typically performed by certify-book: a local-incompatibility check, writing of a certificate file, and possibly compilation. Another effect is that proofs may be skipped when processing events assuming that the the certify-book command does not explicitly specify :skip-proofs-okp nil, as we now explain. A non-nil value of 'write-acl2x should either be t or a one-element list (x), where x is a legal value for the state global 'ld-skip-proofsp (see ld-skip-proofsp). In both cases, certify-book will process events to write out a .acl2x file as described above. But in the latter (list) case, event processing will take place according to the value of x: in particular, proofs will be skipped when x is not nil, and if moreover x is the symbol include-book-with-locals, then only one pass will be made through each encapsulate form. A third effect of a non-nil value of 'write-acl2x, which is restricted to the list case, is that include-book events encountered during event processing are allowed to succeed on uncertified books, something that is prohibited during most calls of certify-book.

    When certify-book is used to write out a .acl2x file, there is typically a subsequent run of certify-book that reads that file. Consider how this can work with a book foo.lisp. In the first call of certify-book, a file foo.acl2x is written that contains all make-event expansions, but foo.cert is not written. In the second call of certify-book, no make-event expansion typically takes place, because foo.acl2x supplies the expansions. The command (set-write-acl2x t state) should be evaluated before the first certification (though another legal non-nil value may be used in place of t), setting the value of state global 'write-acl2x to t, to enable writing of foo.acl2x; and the command (set-write-acl2x nil state) may be evaluated before the second run (though this is not necessary in a fresh ACL2 session) in order to complete the certification (writing out foo.cert) using foo.acl2x to supply the make-event expansions.

    When Certify-book is supplied with keyword argument :acl2x t it will read or write the book's .acl2x file; when supplied with :acl2x nil, it will not read or write that .acl2x file. The value of :acl2x is nil by default. The interaction of certify-book with the corresponding .acl2x file is as follows.

    o If :acl2x is t, then:
      - If set-write-acl2x has been (most recently) called with a
        value of t for its first argument, then ACL2 writes the
        corresponding .acl2x file.
      - If set-write-acl2x has been (most recently) called with a
        value of nil for its first argument, or not called at all,
        then ACL2 insists on a corresponding .acl2x file that is at
        least as recent as the corresponding .lisp file, causing an
        error otherwise.
    o If :acl2x is nil, then:
      - If set-write-acl2x has been (most recently) called with a
        value t for its first argument, or if argument :ttagsx
        is supplied, then an error occurs.
      - If the .acl2x file exists, then regardless of whether or how
        set-write-acl2x has been called, ACL2 ignores the .acl2x
        file but issues a warning about it.
    

    Suppose you use the two-runs approach: first write a .acl2x file, then certify using (reading) that .acl2x file. Then with scripts such as makefiles, then you may wish to provide a single certify-book command to use for both runs. For that purpose, certify-book supports the keyword argument :ttagsx. If this argument is supplied and write-acl2x is true, then this argument is treated as the :ttags argument, overriding a :ttags argument if present. That is, for the two runs, :ttagsx may be used to specify the trust tags used in the first certification while :ttags specifies the trust tags, if any (else :ttags may be omitted), used in the second certification. Note: If the argument :ttagsx is not supplied, then its value defaults to the (explicit or default) value of the :ttags argument.

    The built-in ACL2 Makefile support automatically generates suitable dependencies if you create a .acl2 file with a certify-book call matching the following regular expression, case-independent:

      (certify-book[^;]*:acl2x t
    
    For an example .acl2 file with a certify-book call matching the above pattern, see community books file books/make-event/double-cert-test-1.acl2.

    Note that include-book is generally not affected by set-write-acl2x, other than through the indirect effect on certify-book. More precisely: All expansions are stored in the certificate file, so when include-book is applied to a certified book, the .acl2x file is not consulted.

    An example of how to put this all together may be found in community book books/make-event/double-cert-test-1.lisp. There, we see the following form.

    (make-event
     (progn (defttag :my-ttag)
            (progn! (let ((val (sys-call "pwd" nil)))
                      (value (list 'defun 'foo () val))))))
    
    Imagine that in place of the binding computed using sys-call, which by the way requires a trust tag, is some computation of your choice (such as reading forms from a file) that is used to construct your own event, in place of the defun event constructed above. The Makefile in that directory contains the following added dependency, so that file double-cert-test-1.acl2x will be created:
    double-cert-test-1.cert: double-cert-test-1.acl2x
    
    There is also the file double-cert-test-1.acl2 in that directory, which contains a single form as follows.
    (certify-book "double-cert-test-1" ? t :ttagsx :all :ttags nil)
    
    Thus, a call of `make' first creates file double-cert-test-1.acl2x, which uses the above :ttagsx argument in order to support the use of defttag during make-event expansion. Then, `make' goes on to cause a second certification in which no trust tags are involved. As a result, the parent book double-cert-test.lisp is ultimately certified without requiring any trust tags.

    The discussion above is probably sufficient for most users of the two-run approach it describes. We conclude with further details for those who want more information. Those who wish to see a yet lower-level explanation of how all this works are invited to read the comment in the ACL2 source code entitled ``Essay on .acl2x Files (Double Certification).

    Consider the .acl2x file produced by the first run as described above. It contains a single expression, which is an association list whose keys are all positive integers, which occur in increasing order. When the .acl2x file is present and at least as recent as the corresponding .lisp file, then for a subsequent certify-book with argument :acl2x t and the (default) value of nil for state global 'write-acl2x, that association list will be applied to the top-level events in the book, as follows. Suppose the entry (n . ev) belongs to the association list in the .acl2x file. Then n is a positive integer, and the nth top-level event in the book -- where the 0th event is the initial in-package form -- will be replaced by ev. In practice, ev is the make-event expansion created during certification for the nth top-level event in the book; and this will always be the case if the .acl2x file is created by certify-book after execution of the form (set-write-acl2x t state). However, you are welcome to associate indices manually with any events you wish into the alist stored in the .acl2x file.

    Note: Also see the community book make-event/acl2x-help.lisp for a useful utility that can be used to skip proofs during the writing of .acl2x files.




    acl2-sources/doc/HTML/SETENV$.html0000664002132200015000000000243712222333524016026 0ustar kaufmannacl2 SETENV$.html -- ACL2 Version 6.3

    SETENV$

    set an environment variable
    Major Section:  ACL2-BUILT-INS
    

    (Setenv$ str val), where str and val are strings, sets the environment variable str to have value val, for subsequent read by getenv$ (see getenv$), and returns nil. Or, if this operation is not implemented for the host Common Lisp, an error will occur.

    Example:
    (setenv$ "FOO" "BAR")
    

    It may be surprising that setenv$ returns nil; indeed, it neither takes nor returns the ACL2 state. The reason is that getenv$ takes responsibility for trafficking in state; it is defined in the logic using the function read-acl2-oracle, which (again, in the logic) does modify state, by popping an entry from its acl2-oracle field. getenv$.




    acl2-sources/doc/HTML/SEVENTH.html0000664002132200015000000000067012222333524016067 0ustar kaufmannacl2 SEVENTH.html -- ACL2 Version 6.3

    SEVENTH

    seventh member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/SHARP-BANG-READER.html0000664002132200015000000000213512222333522017271 0ustar kaufmannacl2 SHARP-BANG-READER.html -- ACL2 Version 6.3

    SHARP-BANG-READER

    package prefix that is not restricted to symbols
    Major Section:  OTHER
    

    Examples:
    
    ACL2 !>(defpkg "FOO" nil)
    
    Summary
    Form:  ( DEFPKG "FOO" ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     "FOO"
    ACL2 !>'#!foo(a b)
    (FOO::A FOO::B)
    ACL2 !>'#!foo(a #!acl2 b)
    (FOO::A B)
    ACL2 !>'#!foo(#!acl2 a b)
    (A FOO::B)
    ACL2 !>'#!foo(#!"ACL2" a b)
    (A FOO::B)
    ACL2 !>
    

    The ACL2 reader supports the syntax #!pkg-name expr where pkg-name is a string or symbol that names a package known to ACL2. As illustrated above, this syntax nests as one might expect. In the special case that expr is a symbol, #!pkg-name expr is equivalent to pkg-name::expr.




    acl2-sources/doc/HTML/SHARP-COMMA-READER.html0000664002132200015000000000106212222333522017414 0ustar kaufmannacl2 SHARP-COMMA-READER.html -- ACL2 Version 6.3

    SHARP-COMMA-READER

    DEPRECATED read-time evaluation of constants
    Major Section:  OTHER
    

    The use of `#,' has been deprecated. Please use `#.' instead; see sharp-dot-reader.




    acl2-sources/doc/HTML/SHARP-DOT-READER.html0000664002132200015000000000353112222333522017211 0ustar kaufmannacl2 SHARP-DOT-READER.html -- ACL2 Version 6.3

    SHARP-DOT-READER

    read-time evaluation of constants
    Major Section:  OTHER
    

    Example:
    
    ACL2 !>(defconst *a* '(a b c))
    
    Summary
    Form:  ( DEFCONST *A* ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     *A*
    ACL2 !>(quote (1 2 #.*a* 3 4))
    (1 2 (A B C) 3 4)
    ACL2 !>
    

    The ACL2 reader supports the syntax #.*a* where *a* was defined by defconst. In this case, the reader treats #.*a* as though it were reading the value of *a*. This feature can be useful in conjunction with the use of evisc-table to abbreviate large constants, so that the abbreviation can be read back in; see evisc-table.

    Remarks.

    (1) The ACL2 reader only supports `#.' as described above, unlike Common Lisp. Older versions (preceding 3.5) used `#.' to abort, but that functionality is now carried out by (a!); see a!. For a related feature that only pops up one level, see p!.

    (2) If you call certify-book on a book that contains a form `#.*foo*', the *foo* must already be defined in the world in which you issue the certify-book command. The reason is that certify-book reads the entire book before evaluating its forms.




    acl2-sources/doc/HTML/SHARP-U-READER.html0000664002132200015000000000364012222333522016770 0ustar kaufmannacl2 SHARP-U-READER.html -- ACL2 Version 6.3

    SHARP-U-READER

    allow underscore characters in numbers
    Major Section:  OTHER
    

    Example:
    
    ACL2 !>#ub1000_1000_1000_
    2184
    ACL2 !>#b100010001000
    2184
    ACL2 !>#uo1_1
    9
    ACL2 !>#o11
    9
    ACL2 !>#u34_5
    345
    ACL2 !>#u345
    345
    ACL2 !>345
    345
    ACL2 !>#ux12_a
    298
    ACL2 !>#ux12a
    298
    ACL2 !>#u x12a
    298
    ACL2 !>#x12a
    298
    ACL2 !>#u123_456/7_919
    123456/7919
    ACL2 !>
    

    The ACL2 reader supports the use of #ub, #uo, and #ux where one would otherwise write #b, #o, and #x, respectively (for binary, octal, and hexadecimal numerals), but where underscore characters (`_') are allowed but ignored. Also supported is the prefix #u in front of a an expression that is a decimal numeral except that underscore characteres are allowed but ignored.

    The precise specification of #u is as follows. The Lisp reader reads one expression after the #u. If the result is a number, then that number is returned by the reader. Otherwise the result must be a symbol whose name begins with one of the characters `B', `O', or `X', or else a decimal digit (one of the characters `0, 1, ..., 9'). All underscores are removed from the name of that symbol to obtain a string and in the first three cases only, a `#' character is prepended to that string. The resulting string is then handed to the Lisp reader in order to obtain the final result, which must be a number or else an error occurs.




    acl2-sources/doc/HTML/SHOW-ACCUMULATED-PERSISTENCE.html0000664002132200015000000000071512222333522021040 0ustar kaufmannacl2 SHOW-ACCUMULATED-PERSISTENCE.html -- ACL2 Version 6.3

    SHOW-ACCUMULATED-PERSISTENCE

    See accumulated-persistence.
    Major Section:  OTHER
    




    acl2-sources/doc/HTML/SHOW-BDD.html0000664002132200015000000000613212222333516016122 0ustar kaufmannacl2 SHOW-BDD.html -- ACL2 Version 6.3

    SHOW-BDD

    inspect failed BDD proof attempts
    Major Section:  BDD
    

    Attempts to use BDDs (see bdd), using :bdd hints, can fail for various reasons. Sometimes it is useful to explore such failures. To do so, one may simply execute the form

    (show-bdd)
    
    inside the ACL2 loop. The system's response is generally self-explanatory. Perhaps you have already seen show-bdd used in some examples (see bdd-introduction and see if*). Here we give some details about show-bdd.

    (Show-bdd) prints the goal to which the BDD procedure was applied and reports the number of nodes created during the BDD computation, followed by additional information depending on whether or not the computation ran to completion or aborted (for reasons explained elsewhere; see bdd-algorithm). If the computation did abort, a backtrace is printed that should be useful in understanding where the problem lies. Otherwise, (show-bdd) prints out ``falsifying constraints.'' This list of pairs associates terms with values and suggests how to construct a binding list for the variables in the conjecture that will falsify the conjecture. It also prints out the term that is the result of simplifying the input term. In each of these cases, parts of the object may be hidden during printing, in order to avoid creating reams of uninteresting output. If so, the user will be queried about whether he wishes to see the entire object (alist or term), which may be quite large. The following responses are legal:

    w -- Walk around the object with a structure editor

    t -- Print the object in full

    nil -- Do not print any more of the object

    Show-bdd actually has four optional arguments, probably rarely used. The general form is

    (show-bdd goal-name goal-ans falsifying-ans term-ans)
    
    where goal-name is the name of the goal on which the :bdd hint was used (or, nil if the system should find such a goal), goal-ans is the answer to be used in place of the query for whether to print the input goal in full, falsifying-ans is the answer to be used in place of the query for whether to print the falsifying constraints in full, and term-ans is the answer to be used in place of the query for whether to print the resulting term in full.




    acl2-sources/doc/HTML/SHOW-BODIES.html0000664002132200015000000000273012222333521016472 0ustar kaufmannacl2 SHOW-BODIES.html -- ACL2 Version 6.3

    SHOW-BODIES

    show the potential definition bodies
    Major Section:  MISCELLANEOUS
    

    Examples:
    (show-bodies foo)
    :show-bodies foo
    
    A definition made using defun installs a so-called ``body'' of a function symbol, as do certain :definition rules. Such bodies are used in a number of ways, including the application of :expand hints; see definition, in particular the discussion of ``body'' there, and see hints for a discussion of the :expand hint. Also see set-body for how to change which of the available definitions (among the original definition and appropriate :definition rules) is the one that provides the body. The show-bodies command displays the available such bodies in an appropriate format, starting with the one that is currently used as the body.

    General Forms:
    (show-bodies function-symbol)
    :show-bodies function-symbol
    




    acl2-sources/doc/HTML/SHOW-CUSTOM-KEYWORD-HINT-EXPANSION.html0000664002132200015000000000224212222333520021760 0ustar kaufmannacl2 SHOW-CUSTOM-KEYWORD-HINT-EXPANSION.html -- ACL2 Version 6.3

    SHOW-CUSTOM-KEYWORD-HINT-EXPANSION

    print out custom keyword hints when they are expanded
    Major Section:  CUSTOM-KEYWORD-HINTS
    

      Examples:
      (show-custom-keyword-hint-expansion t)
      (show-custom-keyword-hint-expansion nil)
      

      General Form:
      (show-custom-keyword-hint-expansion flg)
      

    If the value of flg is non-nil, then when custom keyword hints are expanded, the system prints the results of each expansion. This is sometimes useful for debugging custom keyword hints and, from time to time, may be useful in understanding how a custom hint affects some proof attempt.

    The default setting is nil.

    For an explanation of how custom keyword hints are processed, see custom-keyword-hints.




    acl2-sources/doc/HTML/SHOW-FC-CRITERIA.html0000664002132200015000000000126212222333517017221 0ustar kaufmannacl2 SHOW-FC-CRITERIA.html -- ACL2 Version 6.3

    SHOW-FC-CRITERIA

    print the forward-chaining tracking criteria
    Major Section:  FORWARD-CHAINING-REPORTS
    

    Example:  (show-fc-criteria)
    

    This function prints the list of triples being used to determine what is tracked during forward chaining.

    See forward-chaining-reports for details.




    acl2-sources/doc/HTML/SIGNATURE.html0000664002132200015000000002147512222333521016317 0ustar kaufmannacl2 SIGNATURE.html -- ACL2 Version 6.3

    SIGNATURE

    how to specify the arity of a constrained function
    Major Section:  MISCELLANEOUS
    

    We start with a gentle introduction to signatures, where we pretend that there are no single-threaded objects (more on that below -- for now, if you don't know anything about single-threaded objects, that's fine!). Here are some simple examples of signatures.

    ((hd *) => *)
    ((pair * *) => *)
    ((foo * *) => (mv * * *))
    
    The first of these says that hd is a function of one argument, while the other two say that pair and foo are functions that each take two arguments. The first two say that hd and pair return a single value. The third says that foo returns three values, much as the following definition returns three values:
    (defun bar (x y)
      (mv y x (cons x y)))
    

    Corresponding ``old-style'' signatures are as follows. In each case, a function symbol is followed by a list of formal parameters and then either t, to denote a single value return, or (mv t t t), to denote a multiple value return (in this case, returning three values).

    (hd (x) t)
    (pair (x y) t)
    (foo (x y) (mv t t t))
    

    That concludes our gentle introduction. The documentation below is more general, for example covering single-threaded objects and keyword values such as :guard. When reading what follows below, it is sufficient to know about single-threaded objects (or ``stobjs'') that each has a unique symbolic name and that state is the name of the only built-in single-threaded object. All other stobjs are introduced by the user via defstobj or defabsstobj. An object that is not a single-threaded object is said to be ``ordinary.'' For a discussion of single-threaded objects, see stobj.

    Examples:
    ((hd *) => *)
    ((hd *) => * :formals (x) :guard (consp x))
    ((printer * state) => (mv * * state))
    ((mach * mach-state * state) => (mv * mach-state))
    
    General Form:
    ((fn ...) => *)
    ((fn ...) => stobj)
    or
    ((fn ...) => (mv ...))
    or for part1 and part2 as above,
    (part1 => part2 :kwd1 val1 ... :kwdn valn)
    
    where fn is the constrained function symbol, ... is a list of asterisks and/or the names of single-threaded objects, stobj is a single-threaded object name, and the optional :kwdi and :vali are as described below. ACL2 also supports an older style of signature, described below after we describe the preferred style.

    Signatures specify three syntactic aspects of a function symbol: (1) the ``arity'' or how many arguments the function takes, (2) the ``multiplicity'' or how many results it returns via MV, and (3) which of those arguments and results are single-threaded objects and which objects they are.

    A signature typically has the form ((fn x1 ... xn) => val). Such a signature has two parts, separated by the symbol ``=>''. The first part, (fn x1 ... xn), is suggestive of a call of the constrained function. The number of ``arguments,'' n, indicates the arity of fn. Each xi must be a symbol. If a given xi is the symbol ``*'' then the corresponding argument must be ordinary. If a given xi is any other symbol, that symbol must be the name of a single-threaded object and the corresponding argument must be that object. No stobj name may occur twice among the xi.

    The second part, val, of a signature is suggestive of a term and indicates the ``shape'' of the output of fn. If val is a symbol then it must be either the symbol ``*'' or the name of a single-threaded object. In either case, the multiplicity of fn is 1 and val indicates whether the result is ordinary or a stobj. Otherwise, val is of the form (mv y1 ... yk), where k > 1. Each yi must be either the symbol ``*'' or the name of a stobj. Such a val indicates that fn has multiplicity k and the yi indicate which results are ordinary and which are stobjs. No stobj name may occur twice among the yi, and a stobj name may appear in val only if appears among the xi.

    A signature may have the form ((fn x1 ... xn) => val . k), where k is a keyword-value-listp, i.e., an alternating list of keywords and values starting with a keyword. In this case ((fn x1 ... xn) => val) must be a legal signature as described above. The legal keywords in k are :GUARD and :FORMALS (except that for ACL2(r), also see the remark about :CLASSICALP later in this topic). The value following :FORMALS is to be the list of formal parameters of fn, while the value following :GUARD is a term that is to be the guard of fn. Note that this guard is never actually evaluated, and is not subject to the guard verification performed on functions introduced by defun (see verify-guards). Said differently: this guard need not itself have a guard of t. Indeed, the guard is only used for attachments; see defattach. Note that if :GUARD is supplied then :FORMALS must also be supplied (in order to related the variables occurring in the guard to the parameters of fn). One final observation about guards: if the :GUARD keyword is omitted, then the guard defaults to T.

    Before ACL2 supported user-declared single-threaded objects there was only one single-threaded object: ACL2's built-in notion of state. The notion of signature supported then gave a special role to the symbol state and all other symbols were considered to denote ordinary objects. ACL2 still supports the old form of signature, but it is limited to functions that operate on ordinary objects or ordinary objects and state.

    Old-Style General Form:
    (fn formals result . k)
    

    where fn is the constrained function symbol, formals is a suitable list of formal parameters for it, k is an optional keyword-value-listp (see below), and result is either a symbol denoting that the function returns one result or else result is an mv expression, (mv s1 ... sn), where n>1, each si is a symbol, indicating that the function returns n results. At most one of the formals may be the symbol STATE, indicating that corresponding argument must be ACL2's built-in state. If state appears in formals then state may appear once in result. All ``variable symbols'' other than state in old style signatures denote ordinary objects, regardless of whether the symbol has been defined to be a single-threaded object name!

    The optional k is as described above for newer-style signatures, except that the user is also allowed to declare which symbols (besides state) are to be considered single-threaded object names. Thus :STOBJS is also a legal keyword. The form

    (fn formals result ... :stobjs names ...)
    
    specifies that names is either the name of a single-threaded object or else is a list of such names. Every name in names must have been previously defined as a stobj via defstobj or defabsstobj.

    As promised above, we conclude with a remark about an additional keyword, :CLASSICALP, that is legal for ACL2(r) (see real). The value of this keyword must be t (the default) or nil, indicating respectively whether fn is classical or not.




    acl2-sources/doc/HTML/SIGNED-BYTE-P.html0000664002132200015000000000221212222333524016654 0ustar kaufmannacl2 SIGNED-BYTE-P.html -- ACL2 Version 6.3

    SIGNED-BYTE-P

    recognizer for signed integers that fit in a specified bit width
    Major Section:  ACL2-BUILT-INS
    

    (Signed-byte-p bits x) is T when bits is a positive integer and x is a signed integer whose 2's complement representation fits in a bit-width of bits, i.e., -2^(bits-1) <= x < 2^(bits-1).

    Note that a type-spec of (signed-byte i) for a variable x in a function's declare form translates to a guard condition of (signed-byte-p i x).

    The guard for signed-byte-p is T.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SIGNUM.html0000664002132200015000000000247112222333524015756 0ustar kaufmannacl2 SIGNUM.html -- ACL2 Version 6.3

    SIGNUM

    indicator for positive, negative, or zero
    Major Section:  ACL2-BUILT-INS
    

    (Signum x) is 0 if x is 0, -1 if x is negative, and is 1 otherwise.

    The guard for signum requires its argument to be rational (real, in ACL2(r)) number.

    Signum is a Common Lisp function. See any Common Lisp documentation for more information.

    From ``Common Lisp the Language'' page 206, we see a definition of signum in terms of abs. As explained elsewhere (see abs), the guard for abs requires its argument to be a rational (real, in ACL2(r)) number; hence, we make the same restriction for signum.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SIMPLE.html0000664002132200015000000000351512222333521015742 0ustar kaufmannacl2 SIMPLE.html -- ACL2 Version 6.3

    SIMPLE

    :definition and :rewrite rules used in preprocessing
    Major Section:  MISCELLANEOUS
    

    Example of simple rewrite rule:
    (equal (car (cons x y)) x)
    
    Examples of simple definition:
    (defun file-clock-p (x) (integerp x))
    (defun naturalp (x)
      (and (integerp x) (>= x 0)))
    

    The theorem prover output sometimes refers to ``simple'' definitions and rewrite rules. These rules can be used by the preprocessor, which is one of the theorem prover's ``processes'' understood by the :do-not hint; see hints.

    The preprocessor expands certain definitions and uses certain rewrite rules that it considers to be ``fast''. There are two ways to qualify as fast. One is to be an ``abbreviation'', where a rewrite rule with no hypotheses or loop stopper is an ``abbreviation'' if the right side contains no more variable occurrences than the left side, and the right side does not call the functions if, not or implies. Definitions and rewrite rules can both be abbreviations; the criterion for definitions is similar, except that the definition must not be recursive. The other way to qualify applies only to a non-recursive definition, and applies when its body is a disjunction or conjunction, according to a perhaps subtle criterion that is intended to avoid case splits.




    acl2-sources/doc/HTML/SINGLE-THREADED-OBJECTS.html0000664002132200015000000000065312222333525020203 0ustar kaufmannacl2 SINGLE-THREADED-OBJECTS.html -- ACL2 Version 6.3

    SINGLE-THREADED-OBJECTS

    See stobj.
    Major Section:  PROGRAMMING
    




    acl2-sources/doc/HTML/SIXTH.html0000664002132200015000000000066212222333524015653 0ustar kaufmannacl2 SIXTH.html -- ACL2 Version 6.3

    SIXTH

    sixth member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/SKIP-PROOFS.html0000664002132200015000000001471512222333522016532 0ustar kaufmannacl2 SKIP-PROOFS.html -- ACL2 Version 6.3

    SKIP-PROOFS

    skip proofs for a given form -- a quick way to introduce unsoundness
    Major Section:  OTHER
    

    Example Form:
    (skip-proofs
      (defun foo (x)
        (if (atom x) nil (cons (car x) (foo (reverse (cdr x)))))))
    
    General Form:
    (skip-proofs form)
    
    where form is processed as usual except that the proof obligations usually generated are merely assumed.

    Normally form is an event; see events. If you want to put skip-proofs around more than one event, consider the following (see progn): (skip-proofs (progn event1 event2 ... eventk)).

    WARNING: Skip-proofs allows inconsistent events to be admitted to the logic. Use it at your own risk!

    Sometimes in the development of a formal model or proof it is convenient to skip the proofs required by a given event. By embedding the event in a skip-proofs form, you can avoid the proof burdens generated by the event, at the risk of introducing unsoundness. Below we list four illustrative situations in which you might find skip-proofs useful.

    1. The termination argument for a proposed function definition is complicated. You presume you could admit it, but are not sure that your definition has the desired properties. By embedding the defun event in a skip-proofs you can ``admit'' the function and experiment with theorems about it before undoing (see ubt) and then paying the price of its admission. Note however that you might still have to supply a measure. The set of formals used in some valid measure, known as the ``measured subset'' of the set of formals, is used by ACL2's induction heuristics and therefore needs to be suitably specified. You may wish to specify the special measure of (:? v1 ... vk), where (v1 ... vk) enumerates the measured subset.

    2. You intend eventually to verify the guards for a definition but do not want to take the time now to pursue that. By embedding the verify-guards event in a skip-proofs you can get the system to behave as though the guards were verified.

    3. You are repeatedly recertifying a book while making many experimental changes. A certain defthm in the book takes a very long time to prove and you believe the proof is not affected by the changes you are making. By embedding the defthm event in a skip-proofs you allow the theorem to be assumed without proof during the experimental recertifications.

    4. You are constructing a proof top-down and wish to defer the proof of a defthm until you are convinced of its utility. You can embed the defthm in a skip-proofs. Of course, you may find later (when you attempt prove the theorem) that the proposed defthm is not a theorem.

    Unsoundness or Lisp errors may result if the presumptions underlying a use of skip-proofs are incorrect. Therefore, skip-proofs must be considered a dangerous (though useful) tool in system development.

    Roughly speaking, a defthm embedded in a skip-proofs is essentially a defaxiom, except that it is not noted as an axiom for the purposes of functional instantiation (see lemma-instance). But a skipped defun is much more subtle since not only is the definitional equation being assumed but so are formulas relating to termination and type. The situation is also difficult to characterize if the skip-proofs events are within the scope of an encapsulate in which constrained functions are being introduced. In such contexts no clear logical story is maintained; in particular, constraints aren't properly tracked for definitions. A proof script involving skip-proofs should be regarded as work-in-progress, not as a completed proof with some unproved assumptions. A skip-proofs event represents a promise by the author to admit the given event without further axioms. In other words, skip-proofs should only be used when the belief is that the proof obligations are indeed theorems in the existing ACL2 logical world.

    ACL2 allows the certification of books containing skip-proofs events by providing the keyword argument :skip-proofs-okp t to the certify-book command. This is contrary to the spirit of certified books, since one is supposedly assured by a valid certificate that a book has been ``blessed.'' But certification, too, takes the view of skip-proofs as ``work-in-progress'' and so allows the author of the book to promise to finish. When such books are certified, a warning to the author is printed, reminding him or her of the incurred obligation. When books containing skip-proofs are included into a session, a warning to the user is printed, reminding the user that the book is in fact incomplete and possibly inconsistent. This warning is in fact an error if :skip-proofs-okp is nil in the include-book form; see include-book.

    We conclude with a technical note. Skip-proofs works by binding the ld special ld-skip-proofsp to t unless it is already bound to a non-nil value; see ld-skip-proofsp.




    acl2-sources/doc/HTML/SLOW-ALIST-WARNING.html0000664002132200015000000000346712222333520017517 0ustar kaufmannacl2 SLOW-ALIST-WARNING.html -- ACL2 Version 6.3

    SLOW-ALIST-WARNING

    warnings issued when fast-alists are used inefficiently
    Major Section:  HONS-AND-MEMOIZATION
    

    This documentation topic relates to the experimental extension of ACL2 supporting hash cons, fast alists, and memoization; see hons-and-memoization.

    Obtaining hash-table performance from hons-get requires one to follow a certain discipline. If this discipline is violated, you may see a "slow alist warning". This warning means that the alist you are extending or accessing does not have a valid hash table associated with it, and hence any accesses must be carried out with hons-assoc-equal instead of gethash.

    You can control whether or not you get a warning and, if so, whether or not a break (an error from which you can continue) ensues. For instance:

     (set-slow-alist-action :warning)  ; warn on slow access (default)
     (set-slow-alist-action :break)    ; warn and also call break$
     (set-slow-alist-action nil)       ; do not warn or break
    

    The above forms expand to table events, so they can be embedded in encapsulates and books, wrapped in local, and so on.




    acl2-sources/doc/HTML/SLOW-ARRAY-WARNING.html0000664002132200015000000001466612222333525017531 0ustar kaufmannacl2 SLOW-ARRAY-WARNING.html -- ACL2 Version 6.3

    SLOW-ARRAY-WARNING

    a warning or error issued when arrays are used inefficiently
    Major Section:  ARRAYS
    

    If you use ACL2 arrays you may sometimes see a slow array warning. We explain below what that warning means and some likely ``mistakes'' it may signify.

    First, we note that you can control whether or not you get a warning and, if so, whether or not a break (error from which you can continue; see break$) ensues:

    (assign slow-array-action :warning) ; warn on slow array access (default)
    (assign slow-array-action :break)   ; warn as above, and then call break$
    (assign slow-array-action nil) ; do not warn or break on slow array access
    
    If you are using ACL2 arrays, then you probably care about performance, in which case it is probably best to avoid the nil setting. Below we assume the default behavior: a warning, but no break.

    The discussion in the documentation for arrays defines what we mean by the semantic value of a name. As noted there, behind the scenes ACL2 maintains the invariant that with some names there is associated a pair consisting of an ACL2 array alist, called the semantic value of the name, and an equivalent raw lisp array. Access to ACL2 array elements, as in (aref1 name alist i), is executed in constant time when the array alist is the semantic value of the name, because we can just use the corresponding raw lisp array to obtain the answer. Aset1 and compress1 modify the raw lisp array appropriately to maintain the invariant.

    If aref1 is called on a name and alist, and the alist is not the then-current semantic value of the name, the correct result is computed but it requires linear time because the alist must be searched. When this happens, aref1 prints a slow array warning message to the comment window. Aset1 behaves similarly because the array it returns will cause the slow array warning every time it is used.

    From the purely logical perspective there is nothing ``wrong'' about such use of arrays and it may be spurious to print a warning message. But because arrays are generally used to achieve efficiency, the slow array warning often means the user's intentions are not being realized. Sometimes merely performance expectations are not met; but the message may mean that the functional behavior of the program is different than intended.

    Here are some ``mistakes'' that might cause this behavior. In the following we suppose the message was printed by aset1 about an array named name. Suppose the alist supplied aset1 is alist.

    (1) Compress1 was never called on name and alist. That is, perhaps you created an alist that is an array1p and then proceeded to access it with aref1 but never gave ACL2 the chance to create a raw lisp array for it. After creating an alist that is intended for use as an array, you must do (compress1 name alist) and pass the resulting alist' as the array.

    (2) Name is misspelled. Perhaps the array was compressed under the name 'delta-1 but accessed under 'delta1?

    (3) An aset1 was done to modify alist, producing a new array, alist', but you subsequently used alist as an array. Inspect all (aset1 name ...) occurrences and make sure that the alist modified is never used subsequently (either in that function or any other). It is good practice to adopt the following syntactic style. Suppose the alist you are manipulating is the value of the local variable alist. Suppose at some point in a function definition you wish to modify alist with aset1. Then write

    (let ((alist (aset1 name alist i val))) ...)
    
    and make sure that the subsequent function body is entirely within the scope of the let. Any uses of alist subsequently will refer to the new alist and it is impossible to refer to the old alist. Note that if you write
     (foo (let ((alist (aset1 name alist i val))) ...)  ; arg 1
          (bar alist))                                  ; arg 2
    
    you have broken the rules, because in arg 1 you have modified alist but in arg 2 you refer to the old value. An appropriate rewriting is to lift the let out:
     (let ((alist (aset1 name alist alist i val)))
       (foo ...                                         ; arg 1
            (bar alist)))                               ; arg 2
    
    Of course, this may not mean the same thing.

    (4) A function which takes alist as an argument and modifies it with aset1 fails to return the modified version. This is really the same as (3) above, but focuses on function interfaces. If a function takes an array alist as an argument and the function uses aset1 (or a subfunction uses aset1, etc.), then the function probably ``ought'' to return the result produced by aset1. The reasoning is as follows. If the array is passed into the function, then the caller is holding the array. After the function modifies it, the caller's version of the array is obsolete. If the caller is going to make further use of the array, it must obtain the latest version, i.e., that produced by the function.




    acl2-sources/doc/HTML/SOLUTION-TO-SIMPLE-EXAMPLE.html0000664002132200015000000000522612222333515020631 0ustar kaufmannacl2 SOLUTION-TO-SIMPLE-EXAMPLE.html -- ACL2 Version 6.3

    SOLUTION-TO-SIMPLE-EXAMPLE

    solution to a simple example
    Major Section:  ANNOTATED-ACL2-SCRIPTS
    

    To see a statement of the problem solved below, see annotated-acl2-scripts (in particular the end of that topic).

    Here is a sequence of ACL2 events that illustrates the use of ACL2 to make definitions and prove theorems. We will introduce the notion of the fringe of a tree, as well as the notion of a leaf of a tree, and then prove that the members of the fringe are exactly the leaves.

    We begin by defining the fringe of a tree, where we identify trees simply as cons structures, with atoms at the leaves. The definition is recursive, breaking into two cases. If x is a cons, then the fringe of x is obtained by appending together the fringes of the car and cdr (left and right child) of x. Otherwise, x is an atom and its fringe is the one-element list containing only x.

    
      (defun fringe (x)
        (if (consp x)
            (append (fringe (car x))
                    (fringe (cdr x)))
          (list x)))
    
    
    Now that fringe has been defined, let us proceed by defining the notion of an atom appearing as a ``leaf'', with the goal of proving that the leaves of a tree are exactly the members of its fringe.
    
      (defun leaf-p (atm x)
        (if (consp x)
            (or (leaf-p atm (car x))
                (leaf-p atm (cdr x)))
          (equal atm x)))
    
    
    The main theorem is now as follows. Note that the rewrite rule below uses the equivalence relation iff (see equivalence) rather than equal, since member returns the tail of the given list that begins with the indicated member, rather than returning a Boolean. (Use :pe member to see the definition of member.)
    
      (defthm leaf-p-iff-member-fringe
        (iff (leaf-p atm x)
             (member-equal atm (fringe x))))
    
    




    acl2-sources/doc/HTML/SPEC-MV-LET.html0000664002132200015000000000662012222333522016446 0ustar kaufmannacl2 SPEC-MV-LET.html -- ACL2 Version 6.3

    SPEC-MV-LET

    modification of mv-let supporting speculative and parallel execution
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    Example Form:
    (defun pfib-with-step-count (x)
      (declare (xargs :mode :program))
      (if (or (zp x) (< x 33))
          (fib-with-step-count x)
        (spec-mv-let
         (a cnt1)
         (pfib-with-step-count (- x 1))
         (mv-let (b cnt2)
                 (pfib-with-step-count (- x 2))
                 (if t
                     (mv (+ a b)
                         (+ 1 cnt1 cnt2))
                   (mv "speculative result is always needed"
                       -1))))))
    
    General Form:
    (spec-mv-let
     (v1 ... vn)  ; bind distinct variables
     <spec>       ; evaluate speculatively; return n values
     (mv-let      ; or, use mv?-let if k=1 below
      (w1 ... wk) ; bind distinct variables
      <eager>     ; evaluate eagerly
      (if <test>  ; use results from <spec> if true
          <typical-case> ; may mention v1 ... vn
        <abort-case>)))  ; does not mention v1 ... vn
    

    Our design of spec-mv-let is guided by its use in ACL2 source code to parallelize part of ACL2's proof process, in the experimental parallel extension of ACL2. The user can think of spec-mv-let as a speculative version of mv-let. (In ordinary ACL2, the semantics agree with this description but without speculative or parallel execution.)

    Evaluation of the above general form proceeds as suggested by the comments. First, <spec> is executed speculatively. Control then passes immediately to the mv-let call, without waiting for the result of evaluating <spec>. The variables (w1 ... wk) are bound to the result of evaluating <eager>, and then <test> is evaluated. If the value of <test> is true, then the values of (v1 ... vn) are needed, and <typical-case> blocks until they are available. If the value of <test> is false, then the values of (v1 ... vn) are not needed, and the evaluation of <spec> may be aborted.

    The calls to mv-let and to if displayed above in the General Form are an essential part of the design of spec-mv-let, and are thus required.

    The following definition of fib-with-step-count completes the example above:

    (defun fib-with-step-count (x)
    (declare (xargs :mode :program))
    (cond ((<= x 0)
           (mv 0 1))
          ((= x 1) (mv 1 1))
          (t (mv-let (a cnt1)
                     (fib-with-step-count (- x 1))
                     (mv-let (b cnt2)
                             (fib-with-step-count (- x 2))
                             (mv (+ a b)
                                 (+ 1 cnt1 cnt2)))))))
    




    acl2-sources/doc/HTML/SPECIAL-CASES-FOR-REWRITE-RULES.html0000664002132200015000000000563412222333515021327 0ustar kaufmannacl2 SPECIAL-CASES-FOR-REWRITE-RULES.html -- ACL2 Version 6.3

    SPECIAL-CASES-FOR-REWRITE-RULES

    convenient short forms for rewrite rule formulas
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    In principle, every rewrite rule is made from a formula of this shape:

    (IMPLIES (AND hyp1 ... hypk)
             (eqv lhs rhs))
    
    where eqv is either EQUAL or IFF and the result of expanding any abbreviations in lhs is the application of some function symbol other than IF.

    * In the special case where there is only one hyp term, i.e., k=1, the (AND hyp1) can be written hyp1.

    * In the special case where there are no hyp terms, k=0, the (AND) term is logically just T and the whole IMPLIES can be dropped; such a formula may be written as an unconditional EQUAL or IFF term.

    * If you build a rewrite rule from a formula that concludes with (NOT x), it is treated as though it were (EQUAL x NIL), which is logically equivalent to what you typed.

    * If you build a rewrite rule from a formula that concludes with an AND, ACL2 will build a rewrite rule for each conjunct of the AND. This is because

    (IMPLIES hyp (AND concl1 concl2))
    
    is propositionally equivalent to
    (AND (IMPLIES hyp concl1)
         (IMPLIES hyp concl2)).
    
    However, if you use an OR-expression as a hypothesis, ACL2 does not do the dual transformation. Thus, (IMPLIES (OR hyp1 hyp2) concl) generates one rewrite rule.

    * Finally, if you build a rewrite rule from a formula that does not conclude with an EQUAL, an IFF, a NOT, or an AND, but with some other term, say, lhs, then ACL2 acts like you typed (IFF lhs T), which is logically equivalent to what you typed.

    Thus, regardless of what you type, every rule has k hypotheses. For unconditional rules, k is 0 and the hypotheses are vacuously true. Whether or not you write an EQUAL or an IFF in the conclusion, every rule is either an equality or a propositional equivalence, every rule has a left-hand side, and every rule has a right-hand side.

    Use your browser's Back Button now to return to introduction-to-rewrite-rules-part-1.




    acl2-sources/doc/HTML/SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES.html0000664002132200015000000001152012222333515023042 0ustar kaufmannacl2 SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES.html -- ACL2 Version 6.3

    SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES

    advice about how to handle commonly occurring formulas as rewrite rules
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Below we give you some guidelines for handling specific, commonly occurring situations.

    * Associativity: If a function f is associative, prove

    (equal (f (f x y) z) (f x (f y z)))
    
    ACL2 will use this to flatten f-nests ``to the right.''

    * Commutativity: If a function f is commutative, prove both

    (equal (f x y) (f y x))
    
    and
    (equal (f x (f y z)) (f y (f x z)))
    
    ACL2's heuristics will use these rules to order the arguments alphabetically, so that (f B (f D (f A C))) becomes (f A (f B (f C D))).

    * Distributivity: If you have a pair of functions f and g so that (f x (g y z)) is (g (f x y) (f x z)) or some other form of distributivity is provable, arrange your rules to move the lighter function symbol up and the heavier one toward the variable symbols. For example, our arithmetic libraries drive multiplication through addition, producing sums of products rather than products of sums.

    * Identity and Other Laws: Prove the obvious identity and zero laws (or at least anticipate that you might need them down the road) so as to eliminate operators.

    * Get Rid of Tail Recursive Functions: A corollary to the above advice concerns tail recursive functions that use auxiliary variables. New users often define concepts using tail recursions, accumulating partial results in auxiliary variables, because creating such functions is similar to programming with while loops. Expert users will use tail recursion when necessary for execution efficiency. But tail recursive functions are messy to reason about: their auxiliary variables have to be properly initialized to make the functions compute the expected results, but to state inductively provable properties of tail recursive functions you must identify the invariants on those auxiliary variables. This problem tends not to happen with primitive recursive functions. A primitive recursive function is one that recurs down one variable and holds all the other variables constant in recursion. Most tail-recursive functions can be written elegantly as primitive recursive functions, though one might have to ignore the programmer's desire to make things efficient and define auxiliary functions to appropriately transform the value returned by the recursive call. The classic example is reverse defined in terms of the auxiliary function append versus reverse defined tail recursively with an accumulator. By introducing append you introduce a concept about which you can state lemmas and decompose the proofs of properties of reverse. So if your problem involves tail recursive functions with auxiliary variables, define the primitive recursive version, prove that the tail recursive function is equivalent to the primitive recursive one, and arrange the rewrite rule to eliminate the tail recursive function.

    * Get Rid of Mutually Recursive Functions: Similarly, if you have used mutual-recursion to introduce a clique of mutually recursive functions, f1, f2, ..., you will find that to reason about any one function in the nest you have to reason about all of them. Any mutually recursive function can be defined in a singly recursive way. So do that and then prove a rewrite rule that gets rid of all the mutually recursive functions by proving

    (and (equal (f1 ...) (g1 ...))
         (equal (f2 ...) (g2 ...))
         ...)
    

    where the gi are singly recursive. You may need to appeal to a trick to define the gi: define a singly recursive function that takes a flag argument and mimics whichever mutually recursive function the flag specifies. See mutual-recursion and see mutual-recursion-proof-example .

    If you got to this documentation page from the tutorial discussion of rewrite rules, use your browser's Back Button now to return to introduction-to-rewrite-rules-part-2.




    acl2-sources/doc/HTML/SPECIOUS-SIMPLIFICATION.html0000664002132200015000000001440312222333521020311 0ustar kaufmannacl2 SPECIOUS-SIMPLIFICATION.html -- ACL2 Version 6.3

    SPECIOUS-SIMPLIFICATION

    nonproductive proof steps
    Major Section:  MISCELLANEOUS
    

    Occasionally the ACL2 theorem prover reports that the current goal simplifies to itself or to a set including itself. Such simplifications are said to be ``specious'' and are ignored in the sense that the theorem prover acts as though no simplification were possible and tries the next available proof technique. Specious simplifications are almost always caused by forcing.

    The simplification of a formula proceeds primarily by the local application of :rewrite, :type-prescription, and other rules to its various subterms. If no rewrite rules apply, the formula cannot be simplified and is passed to the next ACL2 proof technique, which is generally the elimination of destructors. The experienced ACL2 user pays special attention to such ``maximally simplified'' formulas; the presence of unexpected terms in them indicates the need for additional rules or the presence of some conflict that prevents existing rules from working harmoniously together.

    However, consider the following interesting possibility: local rewrite rules apply but, when applied, reproduce the goal as one of its own subgoals. How can rewrite rules apply and reproduce the goal? Of course, one way is for one rule application to undo the effect of another, as when commutativity is applied twice in succession to the same term. Another kind of example is when two rules conflict and undermine each other. For example, under suitable hypotheses, (length x) might be rewritten to (+ 1 (length (cdr x))) by the :definition of length and then a :rewrite rule might be used to ``fold'' that back to (length x). Generally speaking the presence of such ``looping'' rewrite rules causes ACL2's simplifier either to stop gracefully because of heuristics such as that described in the documentation for loop-stopper or to cause a stack overflow because of indefinite recursion.

    A more insidious kind of loop can be imagined: two rewrites in different parts of the formula undo each other's effects ``at a distance,'' that is, without ever being applied to one another's output. For example, perhaps the first hypothesis of the formula is simplified to the second, but then the second is simplified to the first, so that the end result is a formula propositionally equivalent to the original one but with the two hypotheses commuted. This is thought to be impossible unless forcing or case-splitting occurs, but if those features are exploited (see force and see case-split) it can be made to happen relatively easily.

    Here is a simple example. Declare foo to be a function of one argument returning one result:

    (defstub p1 (x) t)
    
    Prove the following silly rule:
    (defthm bad
      (implies (case-split (p1 x))
               (p1 x)))
    
    Now suppose we try the following.
    (thm (p1 x))
    
    The rewrite rule bad will rewrite (p1 x) to t, but it will be unable to prove the hypothesis (case-split (p1 x)). As a result, the prover will spawn a new goal, to prove (p1 x). However, since this new goal is the same as the original goal, ACL2 will recognize the simplification as specious and consider the attempted simplification to have failed.

    In other words, despite the rewriting, no progress was made! In more common cases, the original goal may simplify to a set of subgoals, one of which includes the original goal.

    If ACL2 were to adopt the new set of subgoals, it would loop indefinitely. Therefore, it checks whether the input goal is a member of the output subgoals. If so, it announces that the simplification is ``specious'' and pretends that no simplification occurred.

    ``Maximally simplified'' formulas that produce specious simplifications are maximally simplified in a very technical sense: were ACL2 to apply every applicable rule to them, no progress would be made. Since ACL2 can only apply every applicable rule, it cannot make further progress with the formula. But the informed user can perhaps identify some rule that should not be applied and make it inapplicable by disabling it, allowing the simplifier to apply all the others and thus make progress.

    When specious simplifications are a problem it might be helpful to disable all forcing (including case-splits) and resubmit the formula to observe whether forcing is involved in the loop or not. See force. The commands

    ACL2 !>:disable-forcing
    and
    ACL2 !>:enable-forcing
    
    disable and enable the pragmatic effects of both force and case-split. If the loop is broken when forcing is disabled, then it is very likely some forced hypothesis of some rule is ``undoing'' a prior simplification. The most common cause of this is when we force a hypothesis that is actually false but whose falsity is somehow temporarily hidden (more below). To find the offending rule, compare the specious simplification with its non-specious counterpart and look for rules that were speciously applied that are not applied in the non-specious case. Most likely you will find at least one such rule and it will have a forced hypothesis. By disabling that rule, at least for the subgoal in question, you may allow the simplifier to make progress on the subgoal.




    acl2-sources/doc/HTML/SPLITTER-OUTPUT.html0000664002132200015000000000205712222333521017255 0ustar kaufmannacl2 SPLITTER-OUTPUT.html -- ACL2 Version 6.3

    SPLITTER-OUTPUT

    status for reporting of splitter rules
    Major Section:  MISCELLANEOUS
    

    See splitter for a discussion of splitter rules. See set-splitter-output for how to turn off, or on, the reporting of splitter rules. When splitter-output is off, because either prove output is inhibited (see set-inhibit-output-lst) or (set-splitter-output nil) has been invoked, then the value of (splitter-output) is nil. Otherwise, such reporting is on and the value is non-nil.




    acl2-sources/doc/HTML/SPLITTER.html0000664002132200015000000002210112222333521016207 0ustar kaufmannacl2 SPLITTER.html -- ACL2 Version 6.3

    SPLITTER

    reporting of rules whose application may have caused case splits
    Major Section:  MISCELLANEOUS
    

    The application of a rule to a term may cause a goal to simplify to more than one subgoal. A rule with such an application is called a ``splitter''. Here, we explain the output produced for splitters when proof output is enabled (see set-inhibit-output-lst) and such reporting is turned on (as it is by default) -- that is, when the value of (splitter-output) is true.

    See set-splitter-output for how to turn off, or on, the reporting of splitters. Also see set-case-split-limitations for information on how to control case splits.

    We begin by describing three types of splitters.

    if-intro: The rule application may have introduced a call of IF, in the sense discussed at the end below.

    case-split: For the application of a rule with hypothesis of the form (case-split <hyp>), <hyp> did not simplify to true or false.

    immed-forced: For the application of a rule with hypothesis of the form (force <hyp>), <hyp> did not simplify to true or false, where immediate-force-modep is enabled (see immediate-force-modep).

    These three annotations -- if-intro, case-split, and immed-forced -- may be used in proof output and summaries for describing rule applications, as discussed below. A fourth annotation, forced, maybe also be used in proof output to indicate the application of a rule with hypothesis of the form (force <hyp>) when <hyp> did not simplify to true or false, where immediate-force-modep is disabled (see immediate-force-modep). We don't consider such uses of force to be splitters, because they do not cause case splits (though they do produce goals to prove after lower-case ``q.e.d.'' is printed); see force.

    There are three kinds of output affected by splitters, illustrated in turn below using examples.

    (a) During the proof, gag-mode off
    (b) During the proof, gag-mode on
    (c) Summary

    Of course, (a) and (b) are skipped if proof output is inhibited, which (c) is skipped if summary output is inhibited; see set-inhibit-output-lst.

    (a) During the proof, gag-mode off

    With gag-mode off (or when using :pso, :psof, or :psog) one normally gets an English commentary. The following output indicates that at least one application of each rule F and G is of type if-intro, at least one application of rules G and R1 are of type case-split, and at least one application of rule R3 is of type immed-forced. If immediate-force-modep is off then ``immed-forced'' would be replaced by ``forced''.

      This simplifies, using the :definitions F (if-intro), G (case-split and
      if-intro) and H and the :rewrite rules R1, R2 (case-split), and
      R3 (immed-forced), to the following two conjectures.
    

    Note that any such printing of ``forced'' is done even if (splitter-output) is false. Such forcing indication is also made when raw proof format is used -- see set-raw-proof-format -- but in that case, no indication is made for splitters in the proof output.

    (b) During the proof, gag-mode on

    With gag-mode on the proof output is greatly abbreviated. However, ``Splitter Notes'' are printed so that even with gag-mode on, one can get important information to help control large case splits, by disabling splitter rules as appropriate. These are printed at the point when a goal splits into subgoals. Here, for example, is the Splitter Note that corresponds to the output shown in (a) above. It shows the goal whose simplification has produced a split into more than one subgoal, and it shows how many subgoals have been created.

    Splitter note (see :DOC splitter) for Subgoal *1/2.2.1' (2 subgoals).
      case-split: ((:DEFINITION G) (:REWRITE R2))
      immed-forced: ((:REWRITE R3))
      if-intro: ((:DEFINITION G) (:DEFINITION F))
    
    No such splitter notes are printed for the use of force (when immediate-force-modep is off).

    (c) Summary

    Here is a possible summary corresponding to our running example. In the summary, ``Splitter rules'' is omitted if there are no splitter rules, and a splitter type is only mentioned if there is at least one corresponding splitter rule.

    Summary
    Form:  ( THM ...)
    Rules: ((:DEFINITION F)
            (:DEFINITION G)
            (:DEFINITION H)
            (:REWRITE R1)
            (:REWRITE R2)
            (:REWRITE R3))
    Splitter rules (see :DOC splitter):
      case-split: ((:DEFINITION G) (:REWRITE R2))
      immed-forced: ((:REWRITE R3))
      if-intro: ((:DEFINITION G) (:DEFINITION F))
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.00)
    Prover steps counted:  145
    
    No indication for ``forced'' is given for ``Splitter rules''. (As discussed earlier above, the forced hypotheses are not considered relevant for determining splitter rule applications unless immediate-force-modep is on.)

    We conclude by giving the criteria for a rewrite or definition rule application to be a splitter of type if-intro.

    o Reporting of splitter rules is on, i.e., the value of (splitter-output) is true.

    o At least two subgoals are created, even before considering subgoals generated by hypotheses that are calls of case-split or force.

    o The term to which the rule is applied is at the top level, rather than being encountered when trying to establish the hypothesis of a rule.

    o The rule is a rewrite rule, a definition rule, or a meta rule.

    o There is a call of the function symbol IF in the right-hand side of the rewrite rule; or, in the case of a definition rule, in the body of the definition; or, in the case of a meta rule, in the result of applying the metafunction.

    o There is a call of the function symbol IF in the result of rewriting: the right-hand side (for a rewrite rule), the definition body (for a definition rule), or the metafunction application (for a meta rule).

    Any rule application meeting the above criteria will be considered a splitter of type if-intro, even if the call does not actually cause a case split. For example, if you are proving (implies (hyp x) (conc x)) and rule R rewrites (hyp x) to (if (h1 x) (h2 x) nil), which is really the term (and (h1 x) (h2 x)), then R may be labelled as a splitter rule. If you want to find the causes of case-splitting, the list of if-intro splitters can help you narrow your search, but may include irrelevant rules as well.

    Finally, note that you may see splits not attributed to splitters. We believe that this will be uncommon during simplification, though it can occur for example when a call of IF is in the body of a LET expression, i.e., in a call of a LAMBDA expression. But splits caused by other processes, notably destructor elimination (see elim), will typically not be attributed to splitters.




    acl2-sources/doc/HTML/STANDARD-CHAR-LISTP.html0000664002132200015000000000151212222333524017553 0ustar kaufmannacl2 STANDARD-CHAR-LISTP.html -- ACL2 Version 6.3

    STANDARD-CHAR-LISTP

    recognizer for a true list of standard characters
    Major Section:  ACL2-BUILT-INS
    

    (standard-char-listp x) is true if and only if x is a null-terminated list all of whose members are standard characters. See standard-char-p.

    Standard-char-listp has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STANDARD-CHAR-P.html0000664002132200015000000000207412222333524017063 0ustar kaufmannacl2 STANDARD-CHAR-P.html -- ACL2 Version 6.3

    STANDARD-CHAR-P

    recognizer for standard characters
    Major Section:  ACL2-BUILT-INS
    

    (Standard-char-p x) is true if and only if x is a ``standard'' character, i.e., a member of the list *standard-chars*. This list includes #\Newline and #\Space characters, as well as the usual punctuation and alphanumeric characters.

    Standard-char-p has a guard requiring its argument to be a character.

    Standard-char-p is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STANDARD-CO.html0000664002132200015000000000312112222333524016444 0ustar kaufmannacl2 STANDARD-CO.html -- ACL2 Version 6.3

    STANDARD-CO

    the character output channel to which ld prints
    Major Section:  ACL2-BUILT-INS
    

    Standard-co is an ld special (see ld). The accessor is (standard-co state) and the updater is (set-standard-co val state). Standard-co must be an open character output channel. It is to this channel that ld prints the prompt, the form to be evaluated, and the results. The event commands such as defun, defthm, etc., which print extensive commentary do not print to standard-co but rather to a different channel, proofs-co, so that you may redirect this commentary while still interacting via standard-co. See proofs-co.

    ``Standard-co'' stands for ``standard character output.'' The initial value of standard-co is the same as the value of *standard-co* (see *standard-co*).




    acl2-sources/doc/HTML/STANDARD-OI.html0000664002132200015000000000356312222333524016464 0ustar kaufmannacl2 STANDARD-OI.html -- ACL2 Version 6.3

    STANDARD-OI

    the standard object input ``channel''
    Major Section:  ACL2-BUILT-INS
    

    Standard-oi is an ld special (see ld). The accessor is (standard-oi state) and the updater is (set-standard-oi val state). Standard-oi must be an open object input channel, a true list of objects, or a list of objects whose last cdr is an open object input channel. It is from this source that ld takes the input forms to process. When ld is called, if the value specified for standard-oi is a string or a list of objects whose last cdr is a string, then ld treats the string as a file name and opens an object input channel from that file, where the connected book directory (see cbd) is used to resolve relative pathnames. The channel opened by ld is closed by ld upon termination.

    ``Standard-oi'' stands for ``standard object input.'' The read-eval-print loop in ld reads the objects in standard-oi and treats them as forms to be evaluated. The initial value of standard-oi is the same as the value of *standard-oi* (see *standard-oi*).




    acl2-sources/doc/HTML/STANDARD-PART.html0000664002132200015000000000132412222333526016716 0ustar kaufmannacl2 STANDARD-PART.html -- ACL2 Version 6.3

    STANDARD-PART

    ACL2(r) function mapping limited numbers to standard numbers
    Major Section:  REAL
    

    (Standard-part x) is, for a given i-limited number x, the unique real number infinitesimally close (see i-close) to x. This function is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/STANDARD-STRING-ALISTP.html0000664002132200015000000000160212222333524020145 0ustar kaufmannacl2 STANDARD-STRING-ALISTP.html -- ACL2 Version 6.3

    STANDARD-STRING-ALISTP

    recognizer for association lists with standard strings as keys
    Major Section:  ACL2-BUILT-INS
    

    (Standard-string-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where key is a string all of whose characters are standard (see standard-char-p).

    Standard-string-alistp has a guard of t.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STANDARDP.html0000664002132200015000000000252312222333526016274 0ustar kaufmannacl2 STANDARDP.html -- ACL2 Version 6.3

    STANDARDP

    ACL2(r) recognizer for standard objects
    Major Section:  REAL
    

    (Standardp x) is true if and only if x is a ``standard'' object. This notion of ``standard'' comes from non-standard analysis and is discussed in Ruben Gamboa's dissertation. In brief, all the familiar objects are standard: e.g., the familiar real numbers are standard, but non-zero infinitesimals are not standard, and the familiar integers are standard, but not those that exceed every integer that you can express in the usual way (1, 2, 3, and so on). Similarly, the familiar lists are standard, but not so a list that contains a large number of integers, where ``large'' means more than the standard integers. The set of standard numbers is closed under the usual arithmetic operations, hence the sum of a standard number and a non-zero infinitesimal is not standard, though it is what is called ``limited'' (see i-limited).

    This predicate is only defined in ACL2(r) (see real).




    acl2-sources/doc/HTML/START-PROOF-TREE.html0000664002132200015000000000223412222333526017270 0ustar kaufmannacl2 START-PROOF-TREE.html -- ACL2 Version 6.3

    START-PROOF-TREE

    start displaying proof trees during proofs
    Major Section:  PROOF-TREE
    

    Also see proof-tree and see stop-proof-tree. Note that :start-proof-tree works by removing 'proof-tree from the inhibit-output-lst; see set-inhibit-output-lst.

    Explanations of proof tree displays may be found elsewhere: see proof-tree. In a nutshell: :start-proof-tree causes proof tree display to be turned on, once it has been turned off by :stop-proof-tree.

    Do not attempt to invoke start-proof-tree during an interrupt in the middle of a proof.




    acl2-sources/doc/HTML/STARTUP.html0000664002132200015000000000775112222333515016124 0ustar kaufmannacl2 STARTUP.html -- ACL2 Version 6.3

    STARTUP

    How to start using ACL2; the ACL2 command loop
    Major Section:  ACL2-TUTORIAL
    

    When you start up ACL2, you'll probably find yourself inside the ACL2 command loop, as indicated by the following prompt.

    
      ACL2 !>
    
    
    If not, you should type (LP). See lp, which has a lot more information about the ACL2 command loop.

    You should now be in ACL2. The current ``default-defun-mode'' is :logic; the other mode is :program, which would cause the letter p to be printed in the prompt. :Logic means that any function we define is not only executable but also is axiomatically defined in the ACL2 logic. See defun-mode and see default-defun-mode. For example we can define a function my-cons as follows. (You may find it useful to start up ACL2 and submit this and other commands below to the ACL2 command loop, as we won't include output below.)

    
      ACL2 !>(defun my-cons (x y) (cons x y))
    
    
    An easy theorem may then be proved: the car of (my-cons a b) is A.
    
      ACL2 !>(defthm car-my-cons (equal (car (my-cons a b)) a))
    
    

    You can place raw Lisp forms to evaluate at start-up into file acl2-init.lsp in your home directory, except on Windows systems. For example, if you put the following into acl2-init.lsp, then ACL2 will print "HI" when it starts up.

    (print "HI")
    
    But be careful; all bets are off when you submit forms to raw Lisp, so this capability should only be used when you are hacking or when you are setting some Lisp parameters (e.g., (setq si::*notify-gbc* nil) to turn off garbage collection notices in GCL).

    Notice that unlike Nqthm, the theorem command is defthm rather than prove-lemma. See defthm, which explains (among other things) that the default is to turn theorems into rewrite rules.

    Various keyword commands are available to query the ACL2 ``world'', or database. For example, we may view the definition of my-cons by invoking a command to print events, as follows.

    
      ACL2 !>:pe my-cons
    
    
    Also see pe. We may also view all the lemmas that rewrite terms whose top function symbol is car by using the following command, whose output will refer to the lemma car-my-cons proved above.
    
      ACL2 !>:pl car
    
    
    Also see pl. Finally, we may print all the commands back through the initial world as follows.
    
      ACL2 !>:pbt 0
    
    
    See history for a list of commands, including these, for viewing the current ACL2 world.

    Continue with the documentation for annotated-acl2-scripts to see a simple but illustrative example in the use of ACL2 for reasoning about functions.




    acl2-sources/doc/HTML/STATE-GLOBAL-LET_star_.html0000664002132200015000000001060512222333524020402 0ustar kaufmannacl2 STATE-GLOBAL-LET_star_.html -- ACL2 Version 6.3

    STATE-GLOBAL-LET*

    bind state global variables
    Major Section:  ACL2-BUILT-INS
    

    See programming-with-state for requisite background on programming with the ACL2 state.

    Example Forms:
    (state-global-let*
     ((inhibit-output-lst *valid-output-names*))
     (thm (equal x x)))
    
    (state-global-let*
     ((fmt-hard-right-margin 1000 set-fmt-hard-right-margin)
      (fmt-soft-right-margin 1000 set-fmt-soft-right-margin))
     (mini-proveall))
    
    General Form:
    (state-global-let* ((var1 form1) ; or (var1 form1 set-var1)
                        ...
                        (vark formk) ; or (vark formk set-vark)
                       )
                       body)
    
    where: each vari is a variable; each formi is an expression whose value is a single ordinary object (i.e. not multiple values, and not state or any other stobj); set-vari, if supplied, is a function with signature ((set-vari * state) => state); and body is an expression that evaluates to an error triple (see error-triples). Each formi is evaluated in order, starting with form1, and with each such binding the state global variable vari is bound to the value of formi, sequentially in the style of let*. More precisely, then meaning of this form is to set (in order) the global values of the indicated state global variables vari to the values of formi using f-put-global, execute body, restore the vari to their previous values (but see the discussion of setters below), and return the triple produced by body (with its state as modified by the restoration). The restoration is guaranteed even in the face of aborts. The ``bound'' variables may initially be unbound in state and restoration means to make them unbound again.

    Still referring to the General Form above, let old-vali be the value of state global variable vari at the time vari is about to be assigned the value of formi. If set-vari is not supplied, then as suggested above, the following form is evaluated at the conclusion of the evaluation of the state-global-let* form, whether or not an error has occurred: (f-put-global 'vari 'old-vali state). However, if set-vari is supplied, then instead the form evaluated will be (set-vari 'old-vali state). This capability is particularly useful if vari is untouchable (see push-untouchable), since the above call of f-put-global is illegal.

    Note that the scope of the bindings of a state-global-let* form is the body of that form. This may seem obvious, but to drive the point home, let's consider the following example (see set-print-base and see set-print-radix).

    ACL2 !>(state-global-let* ((print-base 16 set-print-base)
                               (print-radix t set-print-radix))
                              (mv nil 10 state))
     10
    ACL2 !>
    
    Why wasn't the result printed as #xA? The reason is that the result was printed after evaluation of the entire form had completed. If you want to see #xA, do the printing in the scope of the bindings, for example as follows.
    ACL2 !>(state-global-let* ((print-base 16 set-print-base)
                               (print-radix t set-print-radix))
                              (pprogn (fms "~x0~%"
                                           (list (cons #0 10))
                                           *standard-co* state nil)
                                      (mv nil 10 state)))
    
    #xA
     10
    ACL2 !>
    




    acl2-sources/doc/HTML/STATE.html0000664002132200015000000002613012222333525015633 0ustar kaufmannacl2 STATE.html -- ACL2 Version 6.3

    STATE

    the von Neumannesque ACL2 state object
    Major Section:  PROGRAMMING
    

    Note: If you are interested in programming with state, see programming-with-state after reading the information below.

    Some Related Topics

    The ACL2 state object is used extensively in programming the ACL2 system, and has been used in other ACL2 programs as well. However, most users, especially those interested in specification and verification (as opposed to programming per se), need not be aware of the role of the state object in ACL2, and will not write functions that use it explicitly. We say more about this point at the end of this documentation topic.

    The ACL2 state object is an example of a single-threaded object or stobj. ACL2 allows the user to define new single-threaded objects. Generally, ACL2 may need to access the ACL2 state but should not (cannot) change it except via a certain set of approved functions such as defun and defthm. If you need a state-like object to which you have complete rights, you may want a stobj.

    Key to the idea of our state is the notion of single-threadedness. For an explanation, see stobj. The upshot of it is that state is a variable symbol with severe restrictions on its use, so that it can be passed into only certain functions in certain slots, and must be returned by those functions that ``modify'' it. Henceforth, we do not discuss single-threaded objects in general (which the user can introduce with defstobj and defabsstobj) but one in particular, namely ACL2's state object.

    The global table is perhaps the most visible portion of the state object. Using the interface functions @ and assign, a user may bind global variables to the results of function evaluations (much as an Nqthm user exploits the Nqthm utility r-loop). See @, and see assign.

    ACL2 supports several facilities of a truly von Neumannesque state machine character, including file io and global variables. Logically speaking, the state is a true list of the 14 components described below. There is a ``current'' state object at the top-level of the ACL2 command loop. This object is understood to be the value of what would otherwise be the free variable state appearing in top-level input. When any command returns a state object as one of its values, that object becomes the new current state. But ACL2 provides von Neumann style speed for state operations by maintaining only one physical (as opposed to logical) state object. Operations on the state are in fact destructive. This implementation does not violate the applicative semantics because we enforce certain draconian syntactic rules regarding the use of state objects. For example, one cannot ``hold on'' to an old state, access the components of a state arbitrarily, or ``modify'' a state object without passing it on to subsequent state-sensitive functions.

    Every routine that uses the state facilities (e.g. does io, or calls a routine that does io), must be passed a ``state object.'' And a routine must return a state object if the routine modifies the state in any way. Rigid syntactic rules governing the use of state objects are enforced by the function translate, through which all ACL2 user input first passes. State objects can only be ``held'' in the formal parameter state, never in any other formal parameter and never in any structure (excepting a multiple-value return list field which is always a state object). State objects can only be accessed with the primitives we specifically permit. Thus, for example, one cannot ask, in code to be executed, for the length of state or the car of state. In the statement and proof of theorems, there are no syntactic rules prohibiting arbitrary treatment of state objects.

    Logically speaking, a state object is a true list whose members are as follows:

    Open-input-channels, an alist with keys that are symbols in package "ACL2-INPUT-CHANNEL". The value (cdr) of each pair has the form ((:header type file-name open-time) . elements), where type is one of :character, :byte, or :object and elements is a list of things of the corresponding type, i.e. characters, integers of type (mod 255), or lisp objects in our theory. File-name is a string. Open-time is an integer. See io.

    Open-output-channels, an alist with keys that are symbols in package "ACL2-OUTPUT-CHANNEL". The value of a pair has the form ((:header type file-name open-time) . current-contents). See io.

    Global-table, an alist associating symbols (to be used as ``global variables'') with values. See @, and see assign.

    T-stack, a list of arbitrary objects accessed and changed by the functions aref-t-stack and aset-t-stack.

    32-bit-integer-stack, a list of arbitrary 32-bit-integers accessed and changed by the functions aref-32-bit-integer-stack and aset-32-bit-integer-stack.

    Big-clock-entry, an integer, that is used logically to bound the amount of effort spent to evaluate a quoted form.

    Idates, a list of dates and times, used to implement the function print-current-idate, which prints the date and time.

    Acl2-oracle, a list of objects, used for example to implement the functions that let ACL2 report how much time was used, but inaccessible to the user. Also see with-prover-time-limit.

    File-clock, an integer that is increased on every file opening and closing, and on each call of sys-call, and is used to maintain the consistency of the io primitives.

    Readable-files, an alist whose keys have the form (string type time), where string is a file name and time is an integer. The value associated with such a key is a list of characters, bytes, or objects, according to type. The time field is used in the following way: when it comes time to open a file for input, we will only look for a file of the specified name and type whose time field is that of file-clock. This permits us to have a ``probe-file'' aspect to open-file: one can ask for a file, find it does not exist, but come back later and find that it does now exist.

    Written-files, an alist whose keys have the form (string type time1 time2), where string is a file name, type is one of :character, :byte or :object, and time1 and time2 are integers. Time1 and time2 correspond to the file-clock time at which the channel for the file was opened and closed. This field is write-only; the only operation that affects this field is close-output-channel, which conses a new entry on the front.

    Read-files, a list of the form (string type time1 time2), where string is a file name and time1 and time2 were the times at which the file was opened for reading and closed. This field is write only.

    Writeable-files, an alist whose keys have the form (string type time). To open a file for output, we require that the name, type, and time be on this list.

    List-all-package-names-lst, a list of true-listps. Roughly speaking, the car of this list is the list of all package names known to this Common Lisp right now and the cdr of this list is the value of this state variable after you look at its car. The function, list-all-package-names, which takes the state as an argument, returns the car and cdrs the list (returning a new state too). This essentially gives ACL2 access to what is provided by CLTL's list-all-packages. Defpkg uses this feature to ensure that the about-to-be-created package is new in this lisp. Thus, for example, in akcl it is impossible to create the package "COMPILER" with defpkg because it is on the list, while in Lucid that package name is not initially on the list.

    User-stobj-alist, an alist which associates user-defined single-threaded objects (see stobj) with their values.

    We recommend avoiding the use of the state object when writing ACL2 code intended to be used as a formal model of some system, for several reasons. First, the state object is complicated and contains many components that are oriented toward implementation and are likely to be irrelevant to the model in question. Second, there is currently not much support for reasoning about ACL2 functions that manipulate the state object, beyond their logical definitions. Third, the documentation about state is not as complete as one might wish.

    User-defined single-threaded objects offer the speed of state while giving the user complete access to all the fields. See stobj.

    Again, if you are interested in programming with state see programming-with-state.




    acl2-sources/doc/HTML/STOBJ-EXAMPLE-1-DEFUNS.html0000664002132200015000000001174712222333530020051 0ustar kaufmannacl2 STOBJ-EXAMPLE-1-DEFUNS.html -- ACL2 Version 6.3

    STOBJ-EXAMPLE-1-DEFUNS

    the defuns created by the counters stobj
    Major Section:  STOBJ
    

    Consider the event shown in stobj-example-1:

    (defstobj counters
      (NodeCnt     :type integer :initially 0)
      (TipCnt      :type integer :initially 0)
      (IntTipsSeen :type t       :initially nil))
    

    Here is a complete list of the defuns added by the event.

    The careful reader will note that the counters argument below is not declared with the :stobjs xarg even though we insist that the argument be a stobj in calls of these functions. This ``mystery'' is explained below.

    (defun NodeCntp (x)                 ;;; Recognizer for 1st field
      (declare (xargs :guard t :verify-guards t))
      (integerp x))
    
    (defun TipCntp (x)                  ;;; Recognizer for 2nd field
      (declare (xargs :guard t :verify-guards t))
      (integerp x))
    
    (defun IntTipsSeenp (x)             ;;; Recognizer for 3rd field
      (declare (xargs :guard t :verify-guards t) (ignore x))
      t)
    
    (defun countersp (counters)         ;;; Recognizer for object
      (declare (xargs :guard t :verify-guards t))
      (and (true-listp counters)
           (= (length counters) 3)
           (NodeCntp (nth 0 counters))
           (TipCntp (nth 1 counters))
           (IntTipsSeenp (nth 2 counters))
           t))
    
    (defun create-counters ()           ;;; Creator for object
      (declare (xargs :guard t :verify-guards t))
      (list '0 '0 'nil))
    
    (defun NodeCnt (counters)           ;;; Accessor for 1st field
      (declare (xargs :guard (countersp counters) :verify-guards t))
      (nth 0 counters))
    
    (defun update-NodeCnt (v counters)  ;;; Updater for 1st field
      (declare (xargs :guard
                      (and (integerp v)
                           (countersp counters))
                      :verify-guards t))
      (update-nth 0 v counters))
    
    (defun TipCnt (counters)            ;;; Accessor for 2nd field
      (declare (xargs :guard (countersp counters) :verify-guards t))
      (nth 1 counters))
    
    (defun update-TipCnt (v counters)   ;;; Updater for 2nd field
      (declare (xargs :guard
                      (and (integerp v)
                           (countersp counters))
                      :verify-guards t))
      (update-nth 1 v counters))
    
    (defun IntTipsSeen (counters)       ;;; Accessor for 3rd field
      (declare (xargs :guard (countersp counters) :verify-guards t))
      (nth 2 counters))
    
    (defun update-IntTipsSeen (v counters) ;;; Updater for 3rd field
      (declare (xargs :guard (countersp counters) :verify-guards t))
      (update-nth 2 v counters))
    

    Observe that there is a recognizer for each of the three fields and then a recognizer for the counters object itself. Then, for each field, there is an accessor and an updater.

    Observe also that the functions are guarded so that they expect a countersp for their counters argument and an appropriate value for the new field values.

    You can see all of the defuns added by a defstobj event by executing the event and then using the :pcb! command on the stobj name. E.g.,

    ACL2 !>:pcb! counters
    
    will print the defuns above.

    We now clear up the ``mystery'' mentioned above. Note, for example in TipCnt, that the formal counters is used. From the discussion in stobj-example-1 it has been made clear that TipCnt can only be called on the counters object. And yet, in that same discussion it was said that an argument is so treated only if it it declared among the :stobjs in the definition of the function. So why doesn't TipCnt include something like (declare (xargs :stobjs (counters)))?

    The explanation of this mystery is as follows. At the time TipCnt was defined, during the introduction of the counters stobj, the name ``counters'' was not yet a single-threaded object. The introduction of a new single-threaded object occurs in three steps: (1) The new primitive recognizers, accessors, and updaters are introduced as ``ordinary functions,'' producing their logical axiomatizations. (2) The executable counterparts are defined in raw Lisp to support destructive updating. (3) The new name is declared a single-threaded object to ensure that all future use of these primitives respects the single-threadedness of the object. The functions defined as part of the introduction of a new single-threaded object are the only functions in the system that have undeclared stobj formals other than state.

    You may return to stobj-example-1 here.




    acl2-sources/doc/HTML/STOBJ-EXAMPLE-1-IMPLEMENTATION.html0000664002132200015000000000644112222333530021205 0ustar kaufmannacl2 STOBJ-EXAMPLE-1-IMPLEMENTATION.html -- ACL2 Version 6.3

    STOBJ-EXAMPLE-1-IMPLEMENTATION

    the implementation of the counters stobj
    Major Section:  STOBJ
    

    the event

    (defstobj counters
      (NodeCnt     :type integer :initially 0)
      (TipCnt      :type integer :initially 0)
      (IntTipsSeen :type t       :initially nil))
    
    discussed in stobj-example-1, creates a Common Lisp object to represent the current value of counters. That object is created by evaluating either of the following ``raw'' (non-ACL2) Common Lisp forms:
    (create-counters)
    
    (vector (make-array 1 :element-type 'integer
                          :initial-element '0)
            (make-array 1 :element-type 'integer
                          :initial-element '0)
            'nil)
    
    and the value is stored in the Common Lisp global variable named *the-live-counters*.

    Thus, the counters object is an array of length three. The first two elements are arrays of size 1 and are used to hold the NodeCnt and TipCnt fields. The third element is the IntTipsSeen field. The first two fields are represented by arrays so that we can implement the integer type specification efficiently. Generally, integers are ``boxed'' in some Common Lisp implementations, for example, GCL. Creating a new integer requires creating a new box to put it in. But in some lisps, including GCL, the integers inside arrays of integers are not boxed.

    The function NodeCnt is defined in raw Lisp as:

    (defun NodeCnt (counters)
      (the integer
           (aref (the (simple-array integer (1))
                      (svref counters 0))
                 0)))
    
    Observe that the form (svref counters 0) is evaluated to get an array of size 1, which is followed by a call of aref to access the 0th element of that array.

    The function update-NodeCnt is defined in raw Lisp as:

    (defun update-NodeCnt (v counters)
      (declare (type integer v))
      (progn
       (setf (aref (the (simple-array integer (1))
                        (svref counters 0))
                   0)
             (the integer v))
       counters))
    
    Note that when this function is called, it does not create a new vector of length three, but ``smashes'' the existing one.

    One way to see all the raw Lisp functions defined by a given defstobj is to evaluate the defstobj event and then evaluate, in the ACL2 loop, the expression (nth 4 (global-val 'cltl-command (w state))). Those functions that contain (DECLARE (STOBJ-INLINE-FN T)) will generate defabbrev forms because the :inline keyword of defstobj was supplied the value t. The rest will generate defuns.

    We now recommend that you look at stobj-example-1-proofs.




    acl2-sources/doc/HTML/STOBJ-EXAMPLE-1-PROOFS.html0000664002132200015000000001405412222333530020067 0ustar kaufmannacl2 STOBJ-EXAMPLE-1-PROOFS.html -- ACL2 Version 6.3

    STOBJ-EXAMPLE-1-PROOFS

    some proofs involving the counters stobj
    Major Section:  STOBJ
    

    Consider again the event

    (defstobj counters
      (NodeCnt     :type integer :initially 0)
      (TipCnt      :type integer :initially 0)
      (IntTipsSeen :type t       :initially nil))
    
    discussed in stobj-example-1, followed by the definition
    (defun reset-counters (counters)
      (declare (xargs :stobjs (counters)))
      (seq counters
           (update-NodeCnt 0 counters)
           (update-TipCnt 0 counters)
           (update-IntTipsSeen nil counters)))
    
    which, because of the seq macro in stobj-example-1, is just syntactic sugar for
    (defun reset-counters (counters)
      (declare (xargs :stobjs (counters)))
      (let ((counters (update-NodeCnt 0 counters)))
        (let ((counters (update-TipCnt 0 counters)))
          (update-IntTipsSeen nil counters)))).
    

    Here is a simple theorem about reset-counters.

    (defthm reset-counters-is-constant
      (implies (countersp x)
               (equal (reset-counters x)
                      '(0 0 nil))))
    

    Before we talk about how to prove this theorem, note that the theorem is unusual in two respects.

    First, it calls reset-counters on an argument other than the variable counters! That is allowed in theorems; logically speaking, the stobj functions are indistinguishable from ordinary functions. Their use is syntactically restricted only in defuns, which might be compiled and run in raw Lisp. Those restrictions allow us to implement stobj modification destructively. But logically speaking, reset-counters and other stobj ``modifying'' functions just create new objects, constructively.

    Second, the theorem above explicitly provides the hypothesis that reset-counters is being applied to an object satisfying countersp. Such a hypothesis is not always required: reset-counters is total and will do something no matter what x is. But in this particular case, the result is not '(0 0 nil) unless x is, at least, a true-list of length three.

    To make a long story short, to prove theorems about stobj functions you behave in exactly the way you would to prove the same theorems about the same functions defined without the stobj features.

    How can we prove the above theorem? Unfolding the definition of reset-counters shows that (reset-counters x) is equal to

    (update-IntTipsSeen nil
      (update-TipCnt 0
        (update-NodeCnt 0 x)))
    
    which in turn is
    (update-nth 2 nil
     (update-nth 1 0
      (update-nth 0 0 x))).
    
    Opening up the definition of update-nth reduces this to
    (list* 0 0 nil (cdddr x)).
    
    This is clearly equal to '(0 0 nil), provided we know that (cdddr x) is nil.

    Unfortunately, that last fact requires a lemma. The most specific lemma we could provide is

    (defthm special-lemma-for-counters
      (implies (countersp x)
               (equal (cdddr x) nil)))
    
    but if you try to prove that lemma you will find that it requires some reasoning about len and true-listp. Furthermore, the special lemma above is of interest only for counters.

    The following lemma about len is the one we prefer.

    (defthm equal-len-n
      (implies (syntaxp (quotep n))
               (equal (equal (len x) n)
                      (if (integerp n)
                          (if (< n 0)
                              nil
                            (if (equal n 0)
                                (atom x)
                              (and (consp x)
                                   (equal (len (cdr x)) (- n 1)))))
                        nil))))
    
    This lemma will simplify any equality in which a len expression is equated to any explicitly given constant n, e.g., 3, reducing the equation to a conjunction of consp terms about the first n cdrs.

    If the above lemma is available then ACL2 immediately proves

    (defthm reset-counters-is-constant
      (implies (countersp x)
               (equal (reset-counters x)
                      '(0 0 nil))))
    

    The point is presumably well made: proving theorems about single-threaded object accessors and updaters is no different than proving theorems about other recursively defined functions on lists.

    As we have seen, operations on stobjs turn into definitions involving nth and update-nth in the logic. Here are two lemmas that are useful for simplifying terms involving nth and update-nth, which are therefore useful in reasoning about single-threaded objects.

    (defthm update-nth-update-nth-same
      (implies (equal (nfix i1) (nfix i2))
               (equal (update-nth i1 v1 (update-nth i2 v2 l))
                      (update-nth i1 v1 l))))
    
    (defthm update-nth-update-nth-diff
      (implies (not (equal (nfix i1) (nfix i2)))
               (equal (update-nth i1 v1 (update-nth i2 v2 l))
                      (update-nth i2 v2 (update-nth i1 v1 l))))
      :rule-classes ((:rewrite :loop-stopper ((i1 i2)))))
    
    These lemmas are due to Matt Wilding. See nu-rewriter for a discussion of the efficient simplification of terms of the form (nth n (update-nth key val lst)), which can be critical in settings involving sequential bindings that commonly arise in operations involving stobjs.

    We now recommend that you see stobj-example-2.




    acl2-sources/doc/HTML/STOBJ-EXAMPLE-1.html0000664002132200015000000002410012222333530017052 0ustar kaufmannacl2 STOBJ-EXAMPLE-1.html -- ACL2 Version 6.3

    STOBJ-EXAMPLE-1

    an example of the use of single-threaded objects
    Major Section:  STOBJ
    

    Suppose we want to sweep a tree and (1) count the number of interior nodes, (2) count the number of tips and (3) keep a record of every tip we encounter that is an integer. We could use a single-threaded object as our ``accumulator''. Such an object would have three fields, one holding the number of nodes seen so far, one holding the number of tips, and one holding all the integer tips seen.

    The following event declares counters to be a single-threaded object.

    (defstobj counters
      (NodeCnt     :type integer :initially 0)
      (TipCnt      :type integer :initially 0)
      (IntTipsSeen :type t       :initially nil))
    
    It has three fields, NodeCnt, TipCnt, and IntTipsSeen. (As always in ACL2, capitalization is irrelevant in simple symbol names, so the first name could be written nodecnt or NODECNT, etc.) Those are the name of the accessor functions for the object. The corresponding update functions are named update-NodeCnt, update-TipCnt and update-IntTipsSeen.

    If you do not like the default function names chosen above, there is a feature in the defstobj event that allows you to specify other names.

    If you want to see the ACL2 definitions of all the functions defined by this event, look at stobj-example-1-defuns.

    If, after this event, we evaluate the top-level ``global variable'' counters in the ACL2 read-eval-print loop we get:

    ACL2 !>counters
    <counters>
    
    Note that the value printed is ``<counters>''. Actually, the value of counters in the logic is (0 0 NIL). But ACL2 always prints single-threaded objects in this non-informative way because they are usually so big that to do otherwise would be unpleasant.

    Had you tried to evaluate the ``global variable'' counters before declaring it a single-threaded object, ACL2 would have complained that it does not support global variables. So a lesson here is that once you have declared a new single-threaded object your top-level forms can reference it. In versions of ACL2 prior to Version 2.4 the only variable enjoying this status was STATE. single-threaded objects are a straightforward generalization of the long-implemented von Neumann state feature of ACL2.

    We can access the fields of counters as with:

    ACL2 !>(NodeCnt counters)
    0
    ACL2 !>(IntTipsSeen counters)
    NIL
    
    and we can set the fields of counters as with:
    ACL2 !>(update-NodeCnt 3 counters)
    <counters>
    ACL2 !>(NodeCnt counters)
    3
    
    Observe that when we evaluate an expression that returns a counter object, that object becomes the ``current value'' of counters.

    Here is a function that ``converts'' the counters object to its ``ordinary'' representation:

    (defun show-counters (counters)
      (declare (xargs :stobjs (counters)))
      (list (NodeCnt counters)
            (TipCnt counters)
            (IntTipsSeen counters)))
    
    Observe that we must declare, at the top of the defun, that we mean to use the formal parameter counters as a single-threaded object! If we did not make this declaration, the body of show-counters would be processed as though counters were an ordinary object. An error would be caused because the accessors used above cannot be applied to anything but the single-threaded object counters. If you want to know why we insist on this declaration, see declare-stobjs.

    When show-counters is admitted, the following message is printed:

    Since SHOW-COUNTERS is non-recursive, its admission is trivial.  We
    observe that the type of SHOW-COUNTERS is described by the theorem
    (AND (CONSP (SHOW-COUNTERS COUNTERS))
         (TRUE-LISTP (SHOW-COUNTERS COUNTERS))).
    We used primitive type reasoning.
    
    (SHOW-COUNTERS COUNTERS) => *.
    
    The guard conjecture for SHOW-COUNTERS is trivial to prove.
    SHOW-COUNTERS is compliant with Common Lisp.
    
    The line above containing the ``=>'' is called the ``signature'' of show-counters; it conveys the information that the first argument is the single-threaded object counters and the only result is an ordinary object. Here is an example of another signature:
    (PROCESSOR * * COUNTERS) => (MV * COUNTERS)
    
    which indicates that the function PROCESSOR (which we haven't shown you) takes three arguments, the third of which is the COUNTERS stobj, and returns two results, the second of which is the modified COUNTERS.

    Returning to the admission of show-counters above, the last sentence printed indicates that the guard conjectures for the function were proved. When some argument of a function is declared to be a single-threaded object via the xargs :stobj, we automatically add (conjoin) to the guard the condition that the argument satisfy the recognizer for that single-threaded object. In the case of show-counters the guard is (countersp counters).

    Here is an example of show-counters being called:

    ACL2 !>(show-counters counters)
    (3 0 NIL)
    
    This is what we would see had we set the NodeCnt field of the initial value of counters to 3, as we did earlier in this example.

    We next wish to define a function to reset the counters object. We could define it this way:

    (defun reset-counters (counters)
      (declare (xargs :stobjs (counters)))
      (let ((counters (update-NodeCnt 0 counters)))
        (let ((counters (update-TipCnt 0 counters)))
          (update-IntTipsSeen nil counters))))
    
    which ``successively'' sets the NodeCnt field to 0, then the TipCnt field to 0, and then the IntTipsSeen field to nil and returns the resulting object.

    However, the nest of let expressions is tedious and we use this definition instead. This definition exploits a macro, here named ``seq'' (for ``sequentially'') which evaluates each of the forms given, binding their results successively to the stobj name given.

    (defun reset-counters (counters)
      (declare (xargs :stobjs (counters)))
      (seq counters
           (update-NodeCnt 0 counters)
           (update-TipCnt 0 counters)
           (update-IntTipsSeen nil counters)))
    
    This definition is syntactically identical to the one above, after macro expansion. Our definition of seq is shown below and is not part of native ACL2.
    (defmacro seq (stobj &rest rst)
      (cond ((endp rst) stobj)
            ((endp (cdr rst)) (car rst))
            (t `(let ((,stobj ,(car rst)))
                 (seq ,stobj ,@(cdr rst))))))
    

    The signature printed for reset-counters is

    (RESET-COUNTERS COUNTERS) => COUNTERS.
    

    Here is an example.

    ACL2 !>(show-counters counters)
    (3 0 NIL)
    ACL2 !>(reset-counters counters)
    <counters>
    ACL2 !>(show-counters counters)
    (0 0 NIL)
    

    Here finally is a function that uses counters as a single-threaded accumulator to collect the desired information about the tree x.

    (defun sweep-tree (x counters)
      (declare (xargs :stobjs (counters)))
      (cond ((atom x)
             (seq counters
                  (update-TipCnt (+ 1 (TipCnt counters)) counters)
                  (if (integerp x)
                      (update-IntTipsSeen (cons x (IntTipsSeen counters))
                                      counters)
                    counters)))
            (t (seq counters
                    (update-NodeCnt (+ 1 (NodeCnt counters)) counters)
                    (sweep-tree (car x) counters)
                    (sweep-tree (cdr x) counters)))))
    
    We can paraphrase this definition as follows. If x is an atom, then increment the TipCnt field of counters and then, if x is an integer, add x to the IntTipsSeen field, and return counters. On the other hand, if x is not an atom, then increment the NodeCnt field of counters, and then sweep the car of x and then sweep the cdr of x and return the result.

    Here is an example of its execution. We have displayed the input tree in full dot notation so that the number of interior nodes is just the number of dots.

    ACL2 !>(sweep-tree '((((a . 1) . (2 . b)) . 3)
                         . (4 . (5 . d)))
                       counters)
    <counters>
    ACL2 !>(show-counters counters)
    (7 8 (5 4 3 2 1))
    ACL2 !>(reset-counters counters)
    <counters>
    ACL2 !>(show-counters counters)
    (0 0 NIL)
    

    The counters object has two integer fields and a field whose type is unrestricted. single-threaded objects support other types of fields, such as arrays. We deal with that in the stobj-example-2. But we recommend that you first consider the implementation issues for the counters example (in stobj-example-1-implementation) and then consider the proof issues (in stobj-example-1-proofs).

    To continue the stobj tour, see stobj-example-2.




    acl2-sources/doc/HTML/STOBJ-EXAMPLE-2.html0000664002132200015000000000671712222333530017071 0ustar kaufmannacl2 STOBJ-EXAMPLE-2.html -- ACL2 Version 6.3

    STOBJ-EXAMPLE-2

    an example of the use of arrays in single-threaded objects
    Major Section:  STOBJ
    

    The following event

    (defstobj ms
      (pcn  :type integer                  :initially 0)
      (mem  :type (array integer (100000)) :initially -1)
      (code :type t                        :initially nil))
    
    introduces a single-threaded object named ms (which stands for ``machine state''). The object has three fields, a pcn or program counter, a mem or memory, and a code field.

    The mem field is occupied by an object initially of type (array integer (100000)). Logically speaking, this is a list of length 100000, each element of which is an integer. But in the underlying implementation of the ms object, this field is occupied by a raw Lisp array, initially of size 100000.

    You might expect the above defstobj to define the accessor function mem and the updater update-mem. That does not happen!.

    The above event defines the accessor function memi and the updater update-memi. These functions do not access/update the mem field of the ms object; they access/update the individual elements of the array in that field.

    In particular, the logical definitions of the two functions are:

    (defun memi (i ms)
      (declare (xargs :guard
                      (and (msp ms)
                           (integerp i)
                           (<= 0 i)
                           (< i (mem-length ms)))))
      (nth i (nth 1 ms)))
    
    (defun update-memi (i v ms)
      (declare (xargs :guard
                      (and (msp ms)
                           (integerp i)
                           (<= 0 i)
                           (< i (mem-length ms))
                           (integerp v))))
      (update-nth-array 1 i v ms))
    

    For example, to access the 511th (0-based) memory location of the current ms you could evaluate:

    ACL2 !>(memi 511 ms)
    -1
    
    The answer is -1 initially, because that is the above-specified initial value of the elements of the mem array.

    To set that element you could do

    ACL2 !>(update-memi 511 777 ms)
    <ms>
    ACL2 !>(memi 511 ms)
    777
    

    The raw Lisp implementing these two functions is shown below.

    (defun memi (i ms)
      (declare (type (and fixnum (integer 0 *)) i))
      (the integer
           (aref (the (simple-array integer (*))
                      (svref ms 1))
                 (the (and fixnum (integer 0 *)) i))))
    
    (defun update-memi (i v ms)
      (declare (type (and fixnum (integer 0 *)) i)
               (type integer v))
      (progn
       (setf (aref (the (simple-array integer (*))
                        (svref ms 1))
                   (the (and fixnum (integer 0 *)) i))
             (the integer v))
       ms))
    

    If you want to see the raw Lisp supporting a defstobj, execute the defstobj and then evaluate the ACL2 form (nth 4 (global-val 'cltl-command (w state))).

    To continue the stobj tour, see stobj-example-3.




    acl2-sources/doc/HTML/STOBJ-EXAMPLE-3.html0000664002132200015000000002316712222333530017070 0ustar kaufmannacl2 STOBJ-EXAMPLE-3.html -- ACL2 Version 6.3

    STOBJ-EXAMPLE-3

    another example of a single-threaded object
    Major Section:  STOBJ
    

    The event

    (defstobj $s
      (x :type integer :initially 0)
      (a :type (array (integer 0 9) (3)) :initially 9 :resizable t))
    
    introduces a stobj named $S. The stobj has two fields, X and A. The A field is an array. The X field contains an integer and is initially 0. The A field contains a list of integers, each between 0 and 9, inclusively. (Under the hood, this ``list'' is actually implemented as an array.) Initially, the A field has three elements, each of which is 9.

    This event introduces the following sequence of function definitions:

    (DEFUN XP (X) ...)               ; recognizer for X field
    (DEFUN AP (X) ...)               ; recognizer of A field
    (DEFUN $SP ($S) ...)             ; top-level recognizer for stobj $S
    (DEFUN CREATE-$S NIL ...)        ; creator for stobj $S
    (DEFUN X ($S) ...)               ; accessor for X field
    (DEFUN UPDATE-X (V $S) ...)      ; updater for X field
    (DEFUN A-LENGTH ($S) ...)        ; length of A field
    (DEFUN RESIZE-A (K $S) ...)      ; resizer for A field
    (DEFUN AI (I $S) ...)            ; accessor for A field at index I
    (DEFUN UPDATE-AI (I V $S) ...)   ; updater for A field at index I
    

    Here is the definition of $SP:

    (DEFUN $SP ($S)
      (DECLARE (XARGS :GUARD T :VERIFY-GUARDS T))
      (AND (TRUE-LISTP $S)
           (= (LENGTH $S) 2)
           (XP (NTH 0 $S))
           (AP (NTH 1 $S))
           T))
    
    This reveals that in order to satisfy $SP an object must be a true list of length 2 whose first element satisfies XP and whose second satisfies AP. By printing the definition of AP one learns that it requires its argument to be a true list, each element of which is an integer between 0 and 9.

    The initial value of stobj $S is given by zero-ary ``creator'' function CREATE-$S. Creator functions may only be used in limited contexts. See with-local-stobj.

    Here is the definition of UPDATE-AI, the updater for the A field at index I:

    (DEFUN UPDATE-AI (I V $S)
      (DECLARE (XARGS :GUARD
                      (AND ($SP $S)
                           (INTEGERP I)
                           (<= 0 I)
                           (< I (A-LENGTH $S))
                           (AND (INTEGERP V) (<= 0 V) (<= V 9)))
                      :VERIFY-GUARDS T))
      (UPDATE-NTH-ARRAY 1 I V $S))
    
    By definition (UPDATE-NTH-ARRAY 1 I V $S) is (UPDATE-NTH 1 (UPDATE-NTH I V (NTH 1 $S)) $S). This may be a little surprising but should be perfectly clear.

    First, ignore the guard, since it is irrelevant in the logic. Reading from the inside out, (UPDATE-AI I V $S) extracts (NTH 1 $S), which is array a of $S. (Recall that NTH is 0-based.) The next higher expression in the definition above, (UPDATE-NTH I V a), ``modifies'' a by setting its Ith element to V. Call this a'. The next higher expression, (UPDATE-NTH 1 a' $S), ``modifies'' $S by setting its 1st component to a'. Call this result $s'. Then $s' is the result returned by UPDATE-AI.

    So the first useful observation is that from the perspective of the logic, the type ``restrictions'' on stobjs are irrelevant. They are ``enforced'' by ACL2's guard mechanism, not by the definitions of the updater functions.

    As one might also imagine, the accessor functions do not really ``care,'' logically, whether they are applied to well-formed stobjs or not. For example, (AI I $S) is defined to be (NTH I (NTH 1 $S)).

    Thus, you will not be able to prove that (AI 2 $S) is an integer. That is,

    (integerp (AI 2 $S))
    
    is not a theorem, because $S may not be well-formed.

    Now (integerp (AI 2 $S)) will always evaluate to T in the top-level ACL2 command loop, because we insist that the current value of the stobj $S always satisfies $SP by enforcing the guards on the updaters, independent of whether guard checking is on or off; see set-guard-checking. But in a theorem $S is just another variable, implicitly universally quantified.

    So (integerp (AI 2 $S)) is not a theorem because it is not true when the variable $S is instantiated with, say,

    '(1 (0 1 TWO))
    
    because, logically speaking, (AI 2 '(1 (0 1 TWO))) evaluates to the symbol TWO. That is,
    (equal (AI 2 '(1 (0 1 TWO))) 'TWO)
    
    is true.

    However,

    (implies (and ($SP $S) (< 2 (A-LENGTH $S))) (integerp (AI 2 $S)))
    
    is a theorem. To prove it, you will have to prove a lemma about AP. The following will do:
    (defthm ap-nth
      (implies (and (AP x)
                    (integerp i)
                    (<= 0 i)
                    (< i (len x)))
               (integerp (nth i x)))).
    

    Similarly,

    (implies (and (integerp i)
                  (<= 0 i)
                  (< i (A-LENGTH $S))
                  (integerp v)
                  (<= 0 v)
                  (<= v 9))
             ($SP (UPDATE-AI i v $S)))
    
    is not a theorem until you add the additional hypothesis ($SP $S). To prove the resulting theorem, you will need a lemma such as the following.
    (defthm ap-update-nth
      (implies (and (AP a)
                    (integerp v)
                    (<= 0 v)
                    (<= v 9)
                    (integerp i)
                    (<= 0 i)
                    (< i (len a)))
               (AP (update-nth i v a))))
    

    The moral here is that from the logical perspective, you must provide the hypotheses that, as a programmer, you think are implicit on the structure of your stobjs, and you must prove their invariance. This is a good area for further support, perhaps in the form of a library of macros.

    Resizing Array Fields

    Recall the specification of the array field, A for the stobj $S introduced above:

    (a :type (array (integer 0 9) (3)) :initially 9 :resizable t)
    
    Logically, this field is a list, initially of length 3. Under the hood, this field is implemented using a Common Lisp array with 3 elements. In some applications, one may wish to lengthen an array field, or even (to reclaim space) to shrink an array field. The defstobj event provides functions to access the current length of an array field and to change the array field, with default names obtained by suffixing the field name with ``LENGTH-'' or prefixing it with ``RESIZE-,'' respectively. The following log shows the uses of these fields in the above example.
    ACL2 !>(A-LENGTH $S)
    3
    ACL2 !>(RESIZE-A 10 $S) ; change length of A to 10
    <$s>
    ACL2 !>(A-LENGTH $S)
    10
    ACL2 !>(AI 7 $S)        ; new elements get value from :initially
    9
    ACL2 !>(RESIZE-A 2 $S)  ; truncate A down to first 2 elements
    <$s>
    ACL2 !>(A-LENGTH $S)
    2
    ACL2 !>(AI 7 $S)        ; error:  access past array bound
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function symbol AI, which
    is (AND ($SP $S) (INTEGERP I) (<= 0 I) (< I (A-LENGTH $S))), is violated
    by the arguments in the call (AI 7 $S).
    
    ACL2 !>
    
    Here are the definitions of the relevant functions for the above example; also see resize-list.
    (DEFUN A-LENGTH ($S)
      (DECLARE (XARGS :GUARD ($SP $S) :VERIFY-GUARDS T))
      (LEN (NTH 1 $S)))
    
    (DEFUN RESIZE-A (K $S)
      (DECLARE (XARGS :GUARD ($SP $S) :VERIFY-GUARDS T))
      (UPDATE-NTH 1
                  (RESIZE-LIST (NTH 1 $S) K 9)
                  $S))
    

    It is important to note that the implementation of array resizing in ACL2 involves copying the entire array into a newly allocated space and thus can be quite costly if performed often. This approach was chosen in order to make array access and update as efficient as possible, with the suspicion that for most applications, array access and update are considerably more frequent than resizing (especially if the programmer is aware of the relative costs beforehand).

    It should also be noted that computations of lengths of stobj array fields should be fast (constant-time) in all or most Common Lisp implementations.

    Finally, if :resizable t is not supplied as shown above, then an attempt to resize the array will result in an error. If you do not intend to resize the array, it is better to omit the :resizable option (or to supply :resizable nil), since then the length function will be defined to return a constant, namely the initial length, which can simplify guard proofs (compare with the definition of A-LENGTH above).

    This completes the tour through the documentation of stobjs. However, you may now wish to read the documentation for the event that introduces a new single-threaded object; see defstobj.




    acl2-sources/doc/HTML/STOBJ-LET.html0000664002132200015000000000062312222333530016251 0ustar kaufmannacl2 STOBJ-LET.html -- ACL2 Version 6.3

    STOBJ-LET

    See nested-stobjs.
    Major Section:  STOBJ
    




    acl2-sources/doc/HTML/STOBJ.html0000664002132200015000000001741712222333530015640 0ustar kaufmannacl2 STOBJ.html -- ACL2 Version 6.3

    STOBJ

    single-threaded objects or ``von Neumann bottlenecks''
    Major Section:  ACL2 Documentation
    

    In ACL2, a ``single-threaded object'' is a data structure whose use is so syntactically restricted that only one instance of the object need ever exist and its fields can be updated by destructive assignments.

    Note: Novices are advised to avoid using single-threaded objects, perhaps instead using community book books/data-structures/structures.lisp. At the least, consider using (set-verify-guards-eagerness 0) to avoid guard verification.

    The documentation in this section is laid out in the form of a tour that visits the documented topics in a reasonable order. We recommend that you follow the tour the first time you read about stobjs. The list of all stobj topics is shown below. The tour starts immediately afterwards. Also see defstobj and, for so-called abstract stobjs, see defabsstobj.

    Some Related Topics

    As noted, a ``single-threaded object'' is a data structure whose use is so syntactically restricted that only one instance of the object need ever exist. Updates to the object must be sequentialized. This allows us to update its fields with destructive assignments without wrecking the axiomatic semantics of update-by-copy. For this reason, single-threaded objects are sometimes called ``von Neumann bottlenecks.''

    From the logical perspective, a single-threaded object is an ordinary ACL2 object, e.g., composed of integers and conses. Logically speaking, ordinary ACL2 functions are defined to allow the user to ``access'' and ``update'' its fields. Logically speaking, when fields in the object, obj, are ``updated'' with new values, a new object, obj', is constructed.

    But suppose that by syntactic means we could ensure that there were no more references to the ``old'' object, obj. Then we could create obj' by destructively modifying the memory locations involved in the representation of obj. The syntactic means is pretty simple but draconian: the only reference to obj is in the variable named OBJ.

    The consequences of this simple rule are far-reaching and require some getting used to. For example, if OBJ has been declared as a single-threaded object name, then the following consequences ensue (but see the discussion of congruent stobjs below for a slight relaxation).

    o OBJ is a top-level global variable that contains the current object, obj.

    o If a function uses the formal parameter OBJ, the only ``actual expression'' that can be passed into that slot is the variable OBJ, not merely a term that ``evaluates to an obj''; thus, such functions can only operate on the current object. So for example, instead of (FOO (UPDATE-FIELD1 3 ST)) write (LET ((ST (UPDATE-FIELD1 3 ST))) (FOO ST)).

    o The accessors and updaters have a formal parameter named OBJ, so by the rule just above, those functions can only be applied to the current object. The recognizer is the one exception to the rule: it may be applied either the OBJ or to an ordinary (non-stobj) object.

    o The ACL2 primitives, such as CONS, CAR and CDR, may not be applied to the variable OBJ. Thus, for example, obj may not be consed into a list (which would create another pointer to it) or accessed or copied via ``unapproved'' means.

    o The updaters return a ``new OBJ object'', i.e., obj'; thus, when an updater is called, the only variable which can hold its result is OBJ.

    o If a function calls an OBJ updater, it must return an OBJ object (either as the sole value returned, or in (mv ... OBJ ...); see mv).

    o When a top-level expression involving OBJ returns an OBJ object, that object becomes the new current value of OBJ.

    There are other functional languages supporting single-threadedness, for example Haskell's ``monads'' and Clean's ``uniqueness type system''. Of course, ACL2 provides a theorem prover that can prove theorems that involve such constructs.

    Note that the syntactic restrictions noted above are enforced only when single-threaded objects are encountered directly in the top-level loop or are used in function definitions; the accessor and update functions for single-threaded objects may be used without restriction in formulas to be proved. Since function evaluation is sometimes necessary during proofs, ACL2 must be able to evaluate these functions on logical constants representing the object, even when the constant is not ``the current object.'' Thus, ACL2 supports both the efficient von Neumann semantics and the clean applicative semantics, and uses the first in contexts where execution speed is paramount and the second during proofs.

    Defstobj and defabsstobj events introduce stobjs. See defstobj for more details about stobjs. In particular, a relatively advanced notion of ``congruent stobjs'' is discussed there. The idea is to allow a stobj, st2, of the same ``shape'' as a given stobj, st1, to be used in place of st1. Other defstobj keywords allow inlining and renaming of stobj accessors and updaters.

    But we are getting ahead of ourselves. To start the stobj tour, see stobj-example-1.




    acl2-sources/doc/HTML/STOBJS.html0000664002132200015000000000066112222333521015754 0ustar kaufmannacl2 STOBJS.html -- ACL2 Version 6.3

    STOBJS

    xargs keyword :STOBJS
    Major Section:  MISCELLANEOUS
    

    See xargs.




    acl2-sources/doc/HTML/STOP-PROOF-TREE.html0000664002132200015000000000225012222333526017156 0ustar kaufmannacl2 STOP-PROOF-TREE.html -- ACL2 Version 6.3

    STOP-PROOF-TREE

    stop displaying proof trees during proofs
    Major Section:  PROOF-TREE
    

    Also see proof-tree and see start-proof-tree. Note that :stop-proof-tree works by adding 'proof-tree to the inhibit-output-lst; see set-inhibit-output-lst.

    Proof tree displays are explained in the documentation for proof-tree. :Stop-proof-tree causes proof tree display to be turned off.

    It is permissible to submit the form (stop-proof-tree) during a break. Thus, you can actually turn off proof tree display in the middle of a proof by interrupting ACL2 and submitting the form (stop-proof-tree) in raw Lisp.




    acl2-sources/doc/HTML/STRING-APPEND.html0000664002132200015000000000173012222333524016724 0ustar kaufmannacl2 STRING-APPEND.html -- ACL2 Version 6.3

    STRING-APPEND

    concatenate two strings
    Major Section:  ACL2-BUILT-INS
    

    String-append takes two arguments, which are both strings (if the guard is to be met), and returns a string obtained by concatenating together the characters in the first string followed by those in the second. Also see concatenate, noting that the macro call

    (concatenate 'string str1 str2).
    
    expands to the call
    (string-append str1 str2).
    

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING-DOWNCASE.html0000664002132200015000000000175012222333524017162 0ustar kaufmannacl2 STRING-DOWNCASE.html -- ACL2 Version 6.3

    STRING-DOWNCASE

    in a given string, turn upper-case characters into lower-case
    Major Section:  ACL2-BUILT-INS
    

    For a string x, (string-downcase x) is the result of applying char-downcase to each character in x.

    The guard for string-downcase requires its argument to be a string containing only standard characters.

    String-downcase is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING-EQUAL.html0000664002132200015000000000204312222333524016622 0ustar kaufmannacl2 STRING-EQUAL.html -- ACL2 Version 6.3

    STRING-EQUAL

    string equality without regard to case
    Major Section:  ACL2-BUILT-INS
    

    For strings str1 and str2, (string-equal str1 str2) is true if and only str1 and str2 are the same except perhaps for the cases of their characters.

    The guard on string-equal requires that its arguments are strings consisting of standard characters (see standard-char-listp).

    String-equal is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING-LISTP.html0000664002132200015000000000117112222333524016647 0ustar kaufmannacl2 STRING-LISTP.html -- ACL2 Version 6.3

    STRING-LISTP

    recognizer for a true list of strings
    Major Section:  ACL2-BUILT-INS
    

    The predicate string-listp tests whether its argument is a true-listp of strings.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING-UPCASE.html0000664002132200015000000000173212222333524016737 0ustar kaufmannacl2 STRING-UPCASE.html -- ACL2 Version 6.3

    STRING-UPCASE

    in a given string, turn lower-case characters into upper-case
    Major Section:  ACL2-BUILT-INS
    

    For a string x, (string-upcase x) is the result of applying char-upcase to each character in x.

    The guard for string-upcase requires its argument to be a string containing only standard characters.

    String-upcase is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING.html0000664002132200015000000000206612222333524015762 0ustar kaufmannacl2 STRING.html -- ACL2 Version 6.3

    STRING

    coerce to a string
    Major Section:  ACL2-BUILT-INS
    

    (String x) coerces x to a string. If x is already a string, then it is returned unchanged; if x is a symbol, then its symbol-name is returned; and if x is a character, the corresponding one-character string is returned.

    The guard for string requires its argument to be a string, a symbol, or a character.

    String is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRINGP.html0000664002132200015000000000072112222333524016076 0ustar kaufmannacl2 STRINGP.html -- ACL2 Version 6.3

    STRINGP

    recognizer for strings
    Major Section:  ACL2-BUILT-INS
    

    (stringp x) is true if and only if x is a string.




    acl2-sources/doc/HTML/STRING_gt_.html0000664002132200015000000000160712222333524016613 0ustar kaufmannacl2 STRING_gt_.html -- ACL2 Version 6.3

    STRING>

    greater-than test for strings
    Major Section:  ACL2-BUILT-INS
    

    (String> str1 str2) is non-nil if and only if str2 precedes str1 lexicographically. When non-nil, (string> str1 str2) is the first position (zero-based) at which the strings differ. See string<.

    String> is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING_gt_=.html0000664002132200015000000000215012222333524016702 0ustar kaufmannacl2 STRING_gt_=.html -- ACL2 Version 6.3

    STRING>=

    less-than-or-equal test for strings
    Major Section:  ACL2-BUILT-INS
    

    (String>= str1 str2) is non-nil if and only if the string str2 precedes the string str1 lexicographically or the strings are equal. When non-nil, (string>= str1 str2) is the first position (zero-based) at which the strings differ, if they differ, and otherwise is their common length. See string>.

    The guard for string>= specifies that its arguments are strings.

    String>= is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING_lt_.html0000664002132200015000000000240312222333524016613 0ustar kaufmannacl2 STRING_lt_.html -- ACL2 Version 6.3

    STRING<

    less-than test for strings
    Major Section:  ACL2-BUILT-INS
    

    (String< str1 str2) is non-nil if and only if the string str1 precedes the string str2 lexicographically, where character inequalities are tested using char<. When non-nil, (string< str1 str2) is the first position (zero-based) at which the strings differ. Here are some examples.

    ACL2 !>(string< "abcd" "abu")
    2
    ACL2 !>(string< "abcd" "Abu")
    NIL
    ACL2 !>(string< "abc" "abcde")
    3
    ACL2 !>(string< "abcde" "abc")
    NIL
    

    The guard for string< specifies that its arguments are strings.

    String< is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRING_lt_=.html0000664002132200015000000000215012222333524016707 0ustar kaufmannacl2 STRING_lt_=.html -- ACL2 Version 6.3

    STRING<=

    less-than-or-equal test for strings
    Major Section:  ACL2-BUILT-INS
    

    (String<= str1 str2) is non-nil if and only if the string str1 precedes the string str2 lexicographically or the strings are equal. When non-nil, (string<= str1 str2) is the first position (zero-based) at which the strings differ, if they differ, and otherwise is their common length. See string<.

    The guard for string<= specifies that its arguments are strings.

    String<= is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRIP-CARS.html0000664002132200015000000000153212222333524016400 0ustar kaufmannacl2 STRIP-CARS.html -- ACL2 Version 6.3

    STRIP-CARS

    collect up all first components of pairs in a list
    Major Section:  ACL2-BUILT-INS
    

    (strip-cars x) is the list obtained by walking through the list x and collecting up all first components (cars). This function is implemented in a tail-recursive way, despite its logical definition.

    (strip-cars x) has a guard of (alistp x).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRIP-CDRS.html0000664002132200015000000000151212222333524016401 0ustar kaufmannacl2 STRIP-CDRS.html -- ACL2 Version 6.3

    STRIP-CDRS

    collect up all second components of pairs in a list
    Major Section:  ACL2-BUILT-INS
    

    (strip-cdrs x) has a guard of (alistp x), and returns the list obtained by walking through the list x and collecting up all second components (cdrs). This function is implemented in a tail-recursive way, despite its logical definition.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/STRONG-REWRITE-RULES.html0000664002132200015000000001075612222333515020004 0ustar kaufmannacl2 STRONG-REWRITE-RULES.html -- ACL2 Version 6.3

    STRONG-REWRITE-RULES

    formulating good rewrite rules
    Major Section:  INTRODUCTION-TO-THE-THEOREM-PROVER
    

    Suppose you notice the following term in a Key Checkpoint:

    (MEMBER (CAR A) (REV B)).
    
    You might think of two theorems for handling this term, which we'll call the ``weak'' and ``strong'' version of member-rev.

    (defthm member-rev-weak
      (implies (member e b)
               (member e (rev b)))).
    
    (defthm member-rev-strong
      (iff (member e (rev b))
           (member e b))).
    

    The ``strong'' version logically implies the ``weak'' version and so deserves the adjective. (Recall that ``p <---> q'' is just ``p ---> q'' and ``q ---> p.'' So the strong version quite literally says everything the weak version does and then some.)

    But the ``strong'' version also produces a better rewrite rule. Here are the rules generated from these two formulas, phrased as directives to ACL2's simplifier:

    member-rev-weak: If you ever see an instance of (MEMBER e (REV b)), backchain to (MEMBER e b) (i.e., turn your attention to that term) and if you can show that it is true, replace (MEMBER e (REV b)) by T.

    member-rev-strong: If you ever see an instance of (MEMBER e (REV b)), replace it by (MEMBER e b).

    Technical Note: Actually, both rules concern propositional occurrences of the ``target'' expression, (MEMBER e (REV b)), i.e., occurrences of the target in which its truthvalue is the only thing of relevance. (Recall that (MEMBER x y) returns the tail of y starting with x! Evaluate some simple MEMBER expressions, like (MEMBER 3 '(1 2 3 4 5)) to see what we mean.) Both theorems tell us about circumstances in which the target is non-NIL (i.e., ``true'') without telling us its identity. But ACL2 keeps track of when the target is in a propositional occurrence and can use such rules to rewrite the target to propositionally equivalent terms.

    So the strong version is better because it will always eliminate (MEMBER e (REV b)) in favor of (MEMBER e b). That simpler expression may be further reduced if the context allows it. But in any case, the strong version eliminates REV from the problem. The weak version only eliminates REV when a side-condition can be proved.

    While either version may permit us to prove the Key Checkpoint that ``suggested'' the rule, the strong version is a better rule to have in the database going forward.

    For example, suppose NATS-BELOW returns the list of natural numbers below its argument. Imagine two alternative scenarios in which a key checkpoint is about to arise involving this term:

    (MEMBER K (REV (NATS-BELOW J)))
    

    Scenario 1 is when you've got the strong version in your database: it will rewrite the key checkpoint term to

    (MEMBER K (NATS-BELOW J))
    
    and that's what you'll see in the printed checkpoint unless other rules reduce it further.

    Scenario 2 is when you have only the weak version in your database: the weak rule will attempt to reduce the term above to T and will, if there are sufficient rules and hypotheses about K's membership in (NATS-BELOW J). But if no such rules or hypotheses are available, you'll be confronted with a key checkpoint involving

    (MEMBER K (REV (NATS-BELOW J)))
    
    and it will not be obvious that the problem would go away if you could establish that K is in (NATS-BELOW J). Clearly, Scenario 1 is better.

    We recommend that you now practice formulating strong versions of rules suggested by terms you might see. See practice-formulating-strong-rules.

    When you are finished with that, use your browser's Back Button to return to introduction-to-key-checkpoints.




    acl2-sources/doc/HTML/SUBLIS.html0000664002132200015000000000233512222333524015754 0ustar kaufmannacl2 SUBLIS.html -- ACL2 Version 6.3

    SUBLIS

    substitute an alist into a tree
    Major Section:  ACL2-BUILT-INS
    

    (Sublis alist tree) is obtained by replacing every leaf of tree with the result of looking that leaf up in the association list alist. However, a leaf is left unchanged if it is not found as a key in alist.

    Leaves are looked up using the function assoc. The guard for (sublis alist tree) requires (eqlable-alistp alist). This guard ensures that the guard for assoc will be met for each lookup generated by sublis.

    Sublis is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SUBSEQ.html0000664002132200015000000000333612222333524015757 0ustar kaufmannacl2 SUBSEQ.html -- ACL2 Version 6.3

    SUBSEQ

    subsequence of a string or list
    Major Section:  ACL2-BUILT-INS
    

    For any natural numbers start and end, where start <= end <= (length seq), (subseq seq start end) is the subsequence of seq from index start up to, but not including, index end. End may be nil, which which case it is treated as though it is (length seq), i.e., we obtain the subsequence of seq from index start all the way to the end.

    The guard for (subseq seq start end) is that seq is a true list or a string, start and end are integers (except, end may be nil, in which case it is treated as (length seq) for the rest of this discussion), and 0 <= start <= end <= (length seq).

    Subseq is a Common Lisp function. See any Common Lisp documentation for more information. Note: In Common Lisp the third argument of subseq is optional, but in ACL2 it is required, though it may be nil as explained above.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SUBSETP-EQ.html0000664002132200015000000000063312222333524016402 0ustar kaufmannacl2 SUBSETP-EQ.html -- ACL2 Version 6.3

    SUBSETP-EQ

    See subsetp.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/SUBSETP-EQUAL.html0000664002132200015000000000064112222333524016743 0ustar kaufmannacl2 SUBSETP-EQUAL.html -- ACL2 Version 6.3

    SUBSETP-EQUAL

    See subsetp.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/SUBSETP.html0000664002132200015000000000425012222333524016076 0ustar kaufmannacl2 SUBSETP.html -- ACL2 Version 6.3

    SUBSETP

    test if every member of one list is a member of the other
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (subsetp x y)
    (subsetp x y :test 'eql)   ; same as above (eql as equality test)
    (subsetp x y :test 'eq)    ; same, but eq is equality test
    (subsetp x y :test 'equal) ; same, but equal is equality test
    

    (Subsetp x y) is true if and only if every member of the list x is a member of the list y. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists.

    The guard for a call of subsetp depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between subsetp and its variants:

    (subsetp-eq x lst) is equivalent to (subsetp x lst :test 'eq);

    (subsetp-equal x lst) is equivalent to (subsetp x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function subsetp-equal.

    Subsetp is defined by Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/SUBST.html0000664002132200015000000000211512222333524015647 0ustar kaufmannacl2 SUBST.html -- ACL2 Version 6.3

    SUBST

    a single substitution into a tree
    Major Section:  ACL2-BUILT-INS
    

    (Subst new old tree) is obtained by substituting new for every occurence of old in the given tree.

    Equality to old is determined using the function eql. The guard for (subst new old tree) requires (eqlablep old), which ensures that the guard for eql will be met for each comparison generated by subst.

    Subst is defined in Common Lisp. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SUBSTITUTE.html0000664002132200015000000000244012222333524016463 0ustar kaufmannacl2 SUBSTITUTE.html -- ACL2 Version 6.3

    SUBSTITUTE

    substitute into a string or a list, using eql as test
    Major Section:  ACL2-BUILT-INS
    

    (Substitute new old seq) is the result of replacing each occurrence of old in seq, which is a list or a string, with new.

    The guard for substitute requires that either seq is a string and new is a character, or else: seq is a true-listp such that either all of its members are eqlablep or old is eqlablep.

    Substitute is a Common Lisp function. See any Common Lisp documentation for more information. Since ACL2 functions cannot take keyword arguments (though macros can), the test used in substitute is eql.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SUBVERSIVE-INDUCTIONS.html0000664002132200015000000000103512222333521020116 0ustar kaufmannacl2 SUBVERSIVE-INDUCTIONS.html -- ACL2 Version 6.3

    SUBVERSIVE-INDUCTIONS

    why we restrict encapsulated recursive functions
    Major Section:  MISCELLANEOUS
    

    See subversive-recursions.




    acl2-sources/doc/HTML/SUBVERSIVE-RECURSIONS.html0000664002132200015000000002141712222333522020142 0ustar kaufmannacl2 SUBVERSIVE-RECURSIONS.html -- ACL2 Version 6.3

    SUBVERSIVE-RECURSIONS

    why we restrict encapsulated recursive functions
    Major Section:  MISCELLANEOUS
    

    Subtleties arise when one of the ``constrained'' functions, f, introduced in the signature of an encapsulate event, is involved in the termination argument for a non-local recursively defined function, g, in that encapsulate. During the processing of the encapsulated events, f is locally defined to be some witness function, f'. Properties of f' are explicitly proved and exported from the encapsulate as the constraints on the undefined function f. But if f is used in a recursive g defined within the encapsulate, then the termination proof for g may use properties of f' -- the witness -- that are not explicitly set forth in the constraints stated for f.

    Such recursive g are said be ``subversive'' because if naively treated they give rise to unsound induction schemes or (via functional instantiation) recurrence equations that are impossible to satisfy. We illustrate what could go wrong below.

    Subversive recursions are not banned outright. Instead, they are treated as part of the constraint. That is, in the case above, the definitional equation for g becomes one of the constraints on f. This is generally a severe restriction on future functional instantiations of f. In addition, ACL2 removes from its knowledge of g any suggestions about legal inductions to ``unwind'' its recursion.

    What should you do? Often, the simplest response is to move the offending recursive definition, e.g., g, out of the encapsulate. That is, introduce f by constraint and then define g as an ``independent'' event. You may need to constrain ``additional'' properties of f in order to admit g, e.g., constrain it to reduce some ordinal measure. However, by separating the introduction of f from the admission of g you will clearly identify the necessary constraints on f, functional instantiations of f will be simpler, and g will be a useful function which suggests inductions to the theorem prover.

    Note that the functions introduced in the signature should not even occur ancestrally in the termination proofs for non-local recursive functions in the encapsulate. That is, the constrained functions of an encapsulate should not be reachable in the dependency graph of the functions used in the termination arguments of recursive functions in encapsulate. If they are reachable, their definitions become part of the constraints.

    The following event illustrates the problem posed by subversive recursions.

    (encapsulate (((f *) => *))
      (local (defun f (x) (cdr x)))
      (defun g (x)
        (if (consp x) (not (g (f x))) t)))
    
    Suppose, contrary to how ACL2 works, that the encapsulate above were to introduce no constraints on f on the bogus grounds that the only use of f in the encapsulate is in an admissible function. We discuss the plausibility of this bogus argument in a moment.

    Then it would be possible to prove the theorem:

    (defthm f-not-identity
      (not (equal (f '(a . b)) '(a . b)))
      :rule-classes nil
      :hints (("Goal" :use (:instance g (x '(a . b))))))
    
    simply by observing that if (f '(a . b)) were '(a . b), then (g '(a . b)) would be (not (g '(a . b))), which is impossible.

    But then we could functionally instantiate f-not-identity, replacing f by the identity function, to prove nil! This is bad.

    (defthm bad
      nil
      :rule-classes nil
      :hints
      (("Goal" :use (:functional-instance f-not-identity (f identity)))))
    

    This sequence of events was legal in versions of ACL2 prior to Version 1.5. When we realized the problem we took steps to make it illegal. However, our steps were insufficient and it was possible to sneak in a subversive function (via mutual recursion) as late as Version 2.3.

    We now turn to the plausibility of the bogus argument above. Why might one even be tempted to think that the definition of g above poses no constraint on f? Here is a very similar encapsulate.

    (encapsulate (((f *) => *))
      (local (defun f (x) (cdr x)))
      (defun map (x)
        (if (consp x)
            (cons (f x) (map (cdr x)))
          nil)))
    
    Here map plays the role of g above. Like g, map calls the constrained function f. But map truly does not constrain f. In particular, the definition of map could be moved ``out'' of the encapsulate so that map is introduced afterwards. The difference between map and g is that the constrained function plays no role in the termination argument for the one but does for the other.

    As a ``user-friendly'' gesture, ACL2 implicitly moves map-like functions out of encapsulations; logically speaking, they are introduced after the encapsulation. This simplifies the constraint. When the constraint cannot be thus simplfied the user is advised, via the ``infected'' warning, to phrase the encapsulation in the simplest way possible.

    The lingering bug between Versions 1.5 and 2.3 mentioned above was due to our failure to detect the g-like nature of some functions when they were defined in mutually recursively cliques with other functions. The singly recursive case was recognized. The bug arose because our detection ``algorithm'' was based on the ``suggested inductions'' left behind by successful definitions. We failed to recall that mutually-recursive definitions do not, as of this writing, make any suggestions about inductions and so did not leave any traces of their subversive natures.

    We conclude by elaborating on the criterion ``involved in the termination argument'' mentioned at the outset. Suppose that function f is recursively defined in an encapsulate, where the body of f has no ``ancestor'' among the functions introduced in the signature of that encapsulate, i.e.: the body contains no call of a signature function, no call of a function whose body calls a signature function, and so on. Then ACL2 treats f as though it is defined in front of that encapsulate form; so f is not constrained by the encapsulate, and its definition is hence certainly not subversive in the sense discussed above. But suppose to the contrary that some function introduced in the signature is an ancestor of the body of f. Then the definition of f is subversive if moreover, its termination proof obligation has an ancestor among the signature functions. Now, that proof obligation involves terms from the first argument of selected calls of IF, as well as recursive calls; for a detailed discussion, see ruler-extenders. The important point here is that for the recursive calls, only the arguments in ``measured'' positions are relevant to the termination proof obligation. Consider the following example.

      (encapsulate
       (((h *) => *))
       (local (defun h (n) n))
       (defun f (i n)
         (if (zp i)
             n
           (f (1- i) (h n)))))
    
    ACL2 heuristically picks the measure (acl2-count i) for the definition of f; thus, i is the only ``measured formal'' of f. Since i is the first formal of f, then for the recursive call of f, only the first argument contributes to the termination proof obligation: in this case, (1- i) but not (h n). Thus, even though h is a signature function, the definition of f is not considered to be subversive; an induction scheme is thus stored for f. (This restriction to measured formal positions of recursive calls, for determining subversive definitions, is new in Version_3.5 of ACL2.)




    acl2-sources/doc/HTML/SWITCHES-PARAMETERS-AND-MODES.html0000664002132200015000000004231412222333530021110 0ustar kaufmannacl2 SWITCHES-PARAMETERS-AND-MODES.html -- ACL2 Version 6.3

    SWITCHES-PARAMETERS-AND-MODES

    a variety of ways to modify the ACL2 environment
    Major Section:  ACL2 Documentation
    

    The beginning user might pay special attention to documentation for logic and program. Other topics in this section can be read as one gains familiarity with ACL2.

    Some Related Topics




    acl2-sources/doc/HTML/SYMBOL-ALISTP.html0000664002132200015000000000131612222333524016750 0ustar kaufmannacl2 SYMBOL-ALISTP.html -- ACL2 Version 6.3

    SYMBOL-ALISTP

    recognizer for association lists with symbols as keys
    Major Section:  ACL2-BUILT-INS
    

    (Symbol-alistp x) is true if and only if x is a list of pairs of the form (cons key val) where key is a symbolp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SYMBOL-LISTP.html0000664002132200015000000000111512222333524016644 0ustar kaufmannacl2 SYMBOL-LISTP.html -- ACL2 Version 6.3

    SYMBOL-LISTP

    recognizer for a true list of symbols
    Major Section:  ACL2-BUILT-INS
    

    The predicate symbol-listp tests whether its argument is a true list of symbols.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SYMBOL-NAME.html0000664002132200015000000000122312222333524016471 0ustar kaufmannacl2 SYMBOL-NAME.html -- ACL2 Version 6.3

    SYMBOL-NAME

    the name of a symbol (a string)
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-symbol-name):

    (equal (symbol-name x)
           (if (symbolp x)
               (symbol-name x)
             ""))
    

    Guard for (symbol-name x):

    (symbolp x)
    




    acl2-sources/doc/HTML/SYMBOL-PACKAGE-NAME.html0000664002132200015000000000247312222333524017532 0ustar kaufmannacl2 SYMBOL-PACKAGE-NAME.html -- ACL2 Version 6.3

    SYMBOL-PACKAGE-NAME

    the name of the package of a symbol (a string)
    Major Section:  ACL2-BUILT-INS
    

    WARNING: While symbol-package-name behaves properly on all ACL2 objects, it may give surprising results when called in raw Lisp. For more details see pkg-imports, in particular the discussion there of the "COMMON-LISP" package.

    Completion Axiom (completion-of-symbol-package-name):

    (equal (symbol-package-name x)
           (if (symbolp x)
               (symbol-package-name x)
             ""))
    

    Guard for (symbol-package-name x):

    (symbolp x)
    
    Note: Symbol-package-name may diverge from the name of the symbol's package in raw Lisp, in the case that this package is the main Lisp package. For example, in GCL (symbol-package-name 'car) evaluates to "COMMON-LISP" even though the actual package name for the symbol, car, is "LISP".




    acl2-sources/doc/HTML/SYMBOL-_lt_.html0000664002132200015000000000242612222333524016674 0ustar kaufmannacl2 SYMBOL-_lt_.html -- ACL2 Version 6.3

    SYMBOL-<

    less-than test for symbols
    Major Section:  ACL2-BUILT-INS
    

    (symbol-< x y) is non-nil if and only if either the symbol-name of the symbol x lexicographially precedes the symbol-name of the symbol y (in the sense of string<) or else the symbol-names are equal and the symbol-package-name of x lexicographically precedes that of y (in the same sense). So for example, (symbol-< 'abcd 'abce) and (symbol-< 'acl2::abcd 'foo::abce) are true.

    The guard for symbol specifies that its arguments are symbols.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/SYMBOLP.html0000664002132200015000000000072112222333524016075 0ustar kaufmannacl2 SYMBOLP.html -- ACL2 Version 6.3

    SYMBOLP

    recognizer for symbols
    Major Section:  ACL2-BUILT-INS
    

    (symbolp x) is true if and only if x is a symbol.




    acl2-sources/doc/HTML/SYNTAX.html0000664002132200015000000000145112222333522015775 0ustar kaufmannacl2 SYNTAX.html -- ACL2 Version 6.3

    SYNTAX

    the syntax of ACL2 is that of Common Lisp
    Major Section:  MISCELLANEOUS
    

    For the details of ACL2 syntax, see CLTL. For examples of ACL2 syntax, use :pe to print some of the ACL2 system code. For example:

    :pe assoc-equal
    :pe dumb-occur
    :pe var-fn-count
    :pe add-linear-variable-to-alist
    

    There is no comprehensive description of the ACL2 syntax yet, except that found in CLTL. Also see term.




    acl2-sources/doc/HTML/SYNTAXP-EXAMPLES.html0000664002132200015000000001621512222333522017335 0ustar kaufmannacl2 SYNTAXP-EXAMPLES.html -- ACL2 Version 6.3

    SYNTAXP-EXAMPLES

    examples pertaining to syntaxp hypotheses
    Major Section:  SYNTAXP
    

    See syntaxp for a basic discussion of the use of syntaxp to control rewriting.

    A common syntactic restriction is

    (SYNTAXP (AND (CONSP X) (EQ (CAR X) 'QUOTE)))
    
    or, equivalently,
    (SYNTAXP (QUOTEP X)).
    
    A rule with such a hypothesis can be applied only if x is bound to a specific constant. Thus, if x is 23 (which is actually represented internally as (quote 23)), the test evaluates to t; but if x prints as (+ 11 12) then the test evaluates to nil (because (car x) is the symbol binary-+). We see the use of this restriction in the rule
    (implies (and (syntaxp (quotep c))
                  (syntaxp (quotep d)))
             (equal (+ c d x)
                    (+ (+ c d) x))).
    
    If c and d are constants, then the executable-counterpart of binary-+ will evaluate the sum of c and d. For instance, under the influence of this rule
    (+ 11 12 foo)
    
    rewrites to
    (+ (+ 11 12) foo)
    
    which in turn rewrites to (+ 23 foo). Without the syntactic restriction, this rule would loop with the built-in rules ASSOCIATIVITY-OF-+ or COMMUTATIVITY-OF-+.

    We here recommend that the reader try the affects of entering expressions such as the following at the top level ACL2 prompt.

    (+ 11 23)
    (+ '11 23)
    (+ '11 '23)
    (+ ''11 ''23)
    :trans (+ 11 23)
    :trans (+ '11 23)
    :trans (+ ''11 23)
    :trans (+ c d x)
    :trans (+ (+ c d) x)
    
    We also recommend that the reader verify our claim above about looping by trying the affect of each of the following rules individually.
    (defthm good
       (implies (and (syntaxp (quotep c))
                     (syntaxp (quotep d)))
                (equal (+ c d x)
                       (+ (+ c d) x))))
    
    (defthm bad
       (implies (and (acl2-numberp c)
                     (acl2-numberp d))
                (equal (+ c d x)
                       (+ (+ c d) x))))
    
    on (the false) theorems:
    (thm
      (equal (+ 11 12 x) y))
    
    (thm
      (implies (and (acl2-numberp c)
                    (acl2-numberp d)
                    (acl2-numberp x))
               (equal (+ c d x) y))).
    
    One can use :brr, perhaps in conjunction with cw-gstack, to investigate any looping.

    Here is a simple example showing the value of rule good above. Without good, the thm form below fails.

    (defstub foo (x) t)
    
    (thm (equal (foo (+ 3 4 x)) (foo (+ 7 x))))
    

    The next three examples further explore the use of quote in syntaxp hypotheses.

    We continue the examples of syntaxp hypotheses with a rule from community book books/finite-set-theory/set-theory.lisp. We will not discuss here the meaning of this rule, but it is necessary to point out that (ur-elementp nil) is true in this book.

    (defthm scons-nil
      (implies (and (syntaxp (not (equal a ''nil)))
                    (ur-elementp a))
               (= (scons e a)
                  (scons e nil)))).
    
    Here also, syntaxp is used to prevent looping. Without the restriction, (scons e nil) would be rewritten to itself since (ur-elementp nil) is true.
    Question: Why the use of two quotes in ''nil?
    Hints: Nil is a constant just as 23 is. Try :trans (cons a nil), :trans (cons 'a 'nil), and :trans (cons ''a ''nil). Also, don't forget that the arguments to a function are evaluated before the function is applied.

    The next two rules move negative constants to the other side of an inequality.

    (defthm |(< (+ (- c) x) y)|
      (implies (and (syntaxp (quotep c))
                    (syntaxp (< (cadr c) 0))
                    (acl2-numberp y))
               (equal (< (+ c x) y)
                      (< (fix x) (+ (- c) y)))))
    
    (defthm |(< y (+ (- c) x))|
      (implies (and (syntaxp (quotep c))
                    (syntaxp (< (cadr c) 0))
                    (acl2-numberp y))
               (equal (< y (+ c x))
                      (< (+ (- c) y) (fix x)))))
    
    Questions: What would happen if (< (cadr c) '0) were used? What about (< (cadr c) ''0)?

    One can also use syntaxp to restrict the application of a rule to a particular set of variable bindings as in the following taken from community book books/ihs/quotient-remainder-lemmas.lisp.

    (encapsulate ()
    
      (local
       (defthm floor-+-crock
         (implies
          (and (real/rationalp x)
               (real/rationalp y)
               (real/rationalp z)
               (syntaxp (and (eq x 'x) (eq y 'y) (eq z 'z))))
          (equal (floor (+ x y) z)
                 (floor (+ (+ (mod x z) (mod y z))
                           (* (+ (floor x z) (floor y z)) z)) z)))))
    
      (defthm floor-+
        (implies
         (and (force (real/rationalp x))
              (force (real/rationalp y))
              (force (real/rationalp z))
              (force (not (equal z 0))))
         (equal (floor (+ x y) z)
                (+ (floor (+ (mod x z) (mod y z)) z)
                   (+ (floor x z) (floor y z))))))
    
      )
    
    We recommend the use of :brr to investigate the use of floor-+-crock.

    Another useful restriction is defined by

    (defun rewriting-goal-literal (x mfc state)
    
      ;; Are we rewriting a top-level goal literal, rather than rewriting
      ;; to establish a hypothesis from a rewrite (or other) rule?
    
      (declare (ignore x state))
      (null (access metafunction-context mfc :ancestors))).
    
    We use this restriction in the rule
    (defthm |(< (* x y) 0)|
        (implies (and (syntaxp (rewriting-goal-literal x mfc state))
                      (rationalp x)
                      (rationalp y))
                 (equal (< (* x y) 0)
                        (cond ((equal x 0)
                               nil)
                              ((equal y 0)
                               nil)
                              ((< x 0)
                               (< 0 y))
                              ((< 0 x)
                               (< y 0))))))
    
    which has been found to be useful, but which also leads to excessive thrashing in the linear arithmetic package if used indiscriminately.

    See extended-metafunctions for information on the use of mfc and metafunction-context.




    acl2-sources/doc/HTML/SYNTAXP.html0000664002132200015000000003117212222333522016120 0ustar kaufmannacl2 SYNTAXP.html -- ACL2 Version 6.3

    SYNTAXP

    attach a heuristic filter on a rule
    Major Section:  MISCELLANEOUS
    

    A calls of syntaxp in the hypothesis of a :rewrite, :definition, or :linear rule is treated specially, as described below. Similar treatment is given to the evaluation of a :meta rule's hypothesis function call.

    For example, consider the :rewrite rule created from the following formula.

    Example:
    (IMPLIES (SYNTAXP (NOT (AND (CONSP X)
                                (EQ (CAR X) 'NORM))))
             (EQUAL (LXD X)
                    (LXD (NORM X)))).
    
    The syntaxp hypothesis in this rule will allow the rule to be applied to (lxd (trn a b)) but will not allow it to be applied to (lxd (norm a)).

    Some Related Topics

    General Form:
    (SYNTAXP test)
    
    Syntaxp always returns t and so may be added as a vacuous hypothesis. However, when relieving the hypothesis, the test ``inside'' the syntaxp form is actually treated as a meta-level proposition about the proposed instantiation of the rule's variables and that proposition must evaluate to true (non-nil) to ``establish'' the syntaxp hypothesis.

    Note that the test of a syntaxp hypothesis does not, in general, deal with the meaning or semantics or values of the terms, but rather with their syntactic forms. In the example above, the syntaxp hypothesis allows the rule to be applied to every target of the form (lxd u), provided u is not of the form (norm v). Observe that without this syntactic restriction the rule above could loop, producing a sequence of increasingly complex targets (lxd a), (lxd (norm a)), (lxd (norm (norm a))), etc. An intuitive reading of the rule might be ``norm the argument of lxd unless it has already been normed.''

    Note also that a syntaxp hypothesis deals with the syntactic form used internally by ACL2, rather than that seen by the user. In some cases these are the same, but there can be subtle differences with which the writer of a syntaxp hypothesis must be aware. You can use :trans to display this internal representation.

    There are two types of syntaxp hypotheses. The simpler type may be a hypothesis of a :rewrite, :definition, or :linear rule provided test contains at least one variable but no free variables (see free-variables). In particular, test may not use stobjs; any stobj name will be treated as an ordinary variable. The case of :meta rules is similar to the above, except that it applies to the result of applying the hypothesis metafunction. (Later below we will describe the second type, an extended syntaxp hypothesis, which may use state.)

    We illustrate the use of simple syntaxp hypotheses by slightly elaborating the example given above. Consider a :rewrite rule:

    (IMPLIES (AND (RATIONALP X)
                  (SYNTAXP (NOT (AND (CONSP X)
                                     (EQ (CAR X) 'NORM)))))
             (EQUAL (LXD X)
                    (LXD (NORM X))))
    
    How is this rule applied to (lxd (trn a b))? First, we form a substitution that instantiates the left-hand side of the conclusion of the rule so that it is identical to the target term. In the present case, the substitution replaces x with (trn a b).
    (LXD X) ==> (LXD (trn a b)).
    
    Then we backchain to establish the hypotheses, in order. Ordinarily this means that we instantiate each hypothesis with our substitution and then attempt to rewrite the resulting instance to true. Thus, in order to relieve the first hypothesis above, we rewrite
    (RATIONALP (trn a b)).
    
    If this rewrites to true, we continue.

    Of course, many users are aware of some exceptions to this general description of the way we relieve hypotheses. For example, if a hypothesis contains a ``free-variable'' -- one not bound by the current substitution -- we attempt to extend the substitution by searching for an instance of the hypothesis among known truths. See free-variables. Forced hypotheses are another exception to the general rule of how hypotheses are relieved.

    Hypotheses marked with syntaxp, as in (syntaxp test), are also exceptions. We instantiate such a hypothesis; but instead of rewriting the instantiated instance, we evaluate the instantiated test. More precisely, we evaluate test in an environment in which its variable symbols are bound to the quotations of the terms to which those variables are bound in the instantiating substitution. So in the case in point, we (in essence) evaluate

    (NOT (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))).
    
    This clearly evaluates to t. When a syntaxp test evaluates to true, we consider the syntaxp hypothesis to have been established; this is sound because logically (syntaxp test) is t regardless of test. If the test evaluates to nil (or fails to evaluate because of guard violations) we act as though we cannot establish the hypothesis and abandon the attempt to apply the rule; it is always sound to give up.

    The acute reader will have noticed something odd about the form

    (NOT (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))).
    
    When relieving the first hypothesis, (RATIONALP X), we substituted (trn a b) for X; but when relieving the second hypothesis, (SYNTAXP (NOT (AND (CONSP X) (EQ (CAR X) 'NORM)))), we substituted the quotation of (trn a b) for X. Why the difference? Remember that in the first hypothesis we are talking about the value of (trn a b) -- is it rational -- while in the second one we are talking about its syntactic form. Remember also that Lisp, and hence ACL2, evaluates the arguments to a function before applying the function to the resulting values. Thus, we are asking ``Is the list (trn a b) a consp and if so, is its car the symbol NORM?'' The quotes on both (trn a b) and NORM are therefore necessary. One can verify this by defining trn to be, say cons, and then evaluating forms such as
    (AND (CONSP '(trn a b)) (EQ (CAR '(trn a b)) 'NORM))
    (AND (CONSP (trn a b)) (EQ (CAR (trn a b)) NORM))
    (AND (CONSP (trn 'a 'b)) (EQ (CAR (trn 'a 'b)) NORM))
    (AND (CONSP '(trn a b)) (EQ '(CAR (trn a b)) ''NORM))
    
    at the top-level ACL2 prompt.

    See syntaxp-examples for more examples of the use of syntaxp.

    An extended syntaxp hypothesis is similar to the simple type described above, but it uses two additional variables, mfc and state, which must not be bound by the left hand side or an earlier hypothesis of the rule. They must be the last two variables mentioned by form; first mfc, then state. These two variables give access to the functions mfc-xxx; see extended-metafunctions. As described there, mfc is bound to the so-called metafunction-context and state to ACL2's state. See syntaxp-examples for an example of the use of these extended syntaxp hypotheses.

    We conclude with an example illustrating an error that may occur if you forget that a syntaxp hypothesis will be evaluated in an environment where variables are bound to syntactic terms, not to values. Consider the following stobj introduction (see defstobj).

      (defstobj st
        (fld1 :type (signed-byte 3) :initially 0)
        fld2)
    
    The following syntaxp hypothesis is ill-formed for evaluation. Indeed, ACL2 causes an error because it anticipates that when trying to relieve the syntaxp hypothesis of this rule, ACL2 would be evaluating (fld1 st) where st is bound to a term, not to an actual stobj as required by the function fld1. The error message is intended to explain this problem.
      ACL2 !>(defthm bad
               (implies (syntaxp (quotep (fld1 st)))
                        (equal (stp st)
                               (and (true-listp st)
                                    (equal (len st) 2)
                                    (fld1p (car st))))))
    
    
      ACL2 Error in ( DEFTHM BAD ...):  The form (QUOTEP (FLD1 ST)), from
      a SYNTAXP hypothesis, is not suitable for evaluation in an environment
      where its variables are bound to terms.  See :DOC SYNTAXP.  Here is
      further explanation:
           The form ST is being used, as an argument to a call of FLD1, where
      the single-threaded object of that name is required.  But in the current
      context, the only declared stobj name is STATE.  Note:  this error
      occurred in the context (FLD1 ST).
    
    
      Summary
      Form:  ( DEFTHM BAD ...)
      Rules: NIL
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
      ACL2 Error in ( DEFTHM BAD ...):  See :DOC failure.
    
      ******** FAILED ********
      ACL2 !>
    
    Presumably the intention was to rewrite the term (stp st) when the fld1 component of st is seen to be an explicit constant. As explained elsewhere (see free-variables), we can obtain the result of rewriting (fld1 st) by binding a fresh variable to that term using EQUAL, as follows.
      (defthm good
        (implies (and (equal f (fld1 st))
                      (syntaxp (quotep f)))
                 (equal (stp st)
                        (and (true-listp st)
                             (equal (len st) 2)
                             (fld1p (car st))))))
    
    The event above is admitted by ACL2. We can see it in action by disabling the definition of stp so that only the rule above, good, is available for reasoning about stp.
      (in-theory (disable stp))
    
    Then the proof fails for the following, because the syntaxp hypothesis of the rule, good, fails: (quotep f) evaluates to nil when f is bound to the term (fld1 st).
      (thm (stp st))
    
    However, the proof succeeds for the next form, as we explain below.
      (thm (stp (list 3 rest)))
    
    Consider what happens in that case when rule good is applied to the term (stp (list 3 rest)). (See free-variables for relevant background.) The first hypothesis of good binds the variable f to the result of rewriting (fld1 st), where st is bound to the (internal form of) the term (list 3 rest) -- and that result is clearly the term, '3. Then the syntaxp hypothesis is successfully relieved, because the evaluation of (quotep f) returns t in the environment that binds f to '3.




    acl2-sources/doc/HTML/SYS-CALL+.html0000664002132200015000000000637312222333524016223 0ustar kaufmannacl2 SYS-CALL+.html -- ACL2 Version 6.3

    SYS-CALL+

    make a system call to the host OS, returning status and output
    Major Section:  ACL2-BUILT-INS
    

    Example Form:
    ; The following returns (mv nil s state), where s is the standard output
    ; from the command: ls -l ./
    (sys-call+ "ls" '("-l" "./") state)
    
    General Form:
    (sys-call+ cmd args state)
    
    where cmd is a command to the host operating system and args is a list of strings. Also see sys-call; but there are two differences between sys-call and sys-call+. First, the latter takes an extra argument, state. Second, while sys-call returns nil, sys-call+ returns three values: a so-called error triple (see error-triples), (mv erp val state). While execution returns values as described just below, further below we explain the logical return values. In the following, please keep in mind that the exact behavior depends on the platform; the description is only a guide. For example, on some platforms erp might always be nil, even if in the error case, and val might or might not include error output. (For details, look at the ACL2 source code for function system-call+, whose output is converted by replacing an erp of nil by 0.)

    Erp is either nil or a non-zero integer. Normally, nil indicates that the command ran without error, and otherwise erp is the exit status.

    Val is a string, typically the output generated by the call of cmd.

    State is an ACL2 state.

    While the description above pertains to the values returned by executing sys-call+, the logical values are as follows. For a discussion of the acl2-oracle field of an ACL2 state, see state.

    Erp is the first element of the acl2-oracle of the input state if that element is a nonzero integer, and otherwise is nil.

    Val is the second element of the acl2-oracle of the input state if it is a string, else the empty string, "".

    State is the result of popping the acl2-oracle field twice from the input state.

    Note that unlike sys-call, a call of sys-call+ has no effect on subsequent calls of sys-call-status.

    As is the case for sys-call, a trust tag is required to call sys-call+. For discussion of this and more, see sys-call.




    acl2-sources/doc/HTML/SYS-CALL-STATUS.html0000664002132200015000000000222512222333524017161 0ustar kaufmannacl2 SYS-CALL-STATUS.html -- ACL2 Version 6.3

    SYS-CALL-STATUS

    exit status from the preceding system call
    Major Section:  ACL2-BUILT-INS
    

    This function returns two values, (mv status state). The first is the status resulting from the most recent invocation of function sys-call; see sys-call. The second is the ACL2 state object, which is also the input to this function.

    The function sys-call makes a system call to the host operating system using a function supplied ``under the hood'' by the underlying Lisp system. The status value is the value returned by that Lisp function, which may well be the numeric value returned by the host operating system for the underlying system call. For more information, see sys-call.




    acl2-sources/doc/HTML/SYS-CALL.html0000664002132200015000000000724712222333524016151 0ustar kaufmannacl2 SYS-CALL.html -- ACL2 Version 6.3

    SYS-CALL

    make a system call to the host operating system
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    (sys-call "cp" '("foo.lisp" "foo-copied.lisp"))
    (prog2$ (sys-call "cp" '("foo.lisp" "foo-copied.lisp"))
            (sys-call-status state))
    
    The first argument of sys-call is a command for the host operating system, and the second argument is a list of strings that are the arguments for that command. In GCL and perhaps some other lisps, you can put the arguments with the command; but this is not the case, for example, in Allegro CL running on Linux.

    For a related utility, see sys-call+.

    The use of prog2$ above is optional, but illustrates a typical sort of use when one wishes to get the return status. See sys-call-status.

    General Form:
    (sys-call cmd args)
    
    This function logically returns nil. However, it makes the indicated call to the host operating system, as described above, using a function supplied ``under the hood'' by the underlying Lisp system. On occasions where one wishes to obtain the numeric status returned by the host operating system (or more precisely, by the Lisp function under the hood that passes the system call to the host operating system), one may do so; see sys-call-status. The status value is the value returned by that Lisp function, which may well be the same numeric value returned by the host operating system for the underlying system call.

    Note that sys-call does not touch the ACL2 state; however, sys-call-status updates the acl2-oracle field of the state.

    Be careful if you use sys-call! It can be used for example to overwrite files, or worse! We view a use of sys-call as a call to the operating system that is made outside ACL2. The following example from Bob Boyer shows how to use sys-call to execute, in effect, arbitrary Lisp forms. ACL2 provides a ``trust tag'' mechanism that requires execution of a defttag form before you can use sys-call; see defttag. (Note: The setting of the raw Lisp variable *features* below is just to illustrate that any such mischief is possible. Normally *features* is a list with more than a few elements.)

    % cat foo
    print *0x85d2064=0x838E920
    detach
    q
    % acl2
    ... boilerplate deleted
    ACL2 !>(sys-call "gdb -p $PPID -w < foo >& /dev/null " nil)
    NIL
    ACL2 !>:q
    
    Exiting the ACL2 read-eval-print loop.  To re-enter, execute (LP).
    ACL2>*features*
    
    (:AKCL-SET-MV)
    
    ACL2>
    

    Finally, we make a comment about output redirection, which also applies to other related features that one may expect of a shell. Sys-call does not directly support output redirection. If you want to run a program, P, and redirect its output, one option is to create a wrapper script, W to call instead. Thus W might be a shell script containing the line:

    P $* >& foo.out
    
    For a different, more direct solution, see sys-call+.




    acl2-sources/doc/HTML/Subsumption_of_Induction_Candidates_in_App_Example.html0000664002132200015000000000165512222333526027071 0ustar kaufmannacl2 Subsumption_of_Induction_Candidates_in_App_Example.html -- ACL2 Version 6.3

    Subsumption of Induction Candidates in App Example

    After collecting induction suggestions from these three terms

    (app a b)
    
    (app b c)
    
    (app a (app b c))
    
    the system notices that the first and last suggest the same decomposition of a: case split on whether a is empty (i.e., (endp a)), and in the case where it is not empty, recursively process (cdr a). So we are left with two ideas about how to induct:

    Decompose a as we would to unwind (app a b).

    Decompose b as we would to unwind (app b c).




    acl2-sources/doc/HTML/Suggested_Inductions_in_the_Associativity_of_App_Example.html0000664002132200015000000000153412222333526030306 0ustar kaufmannacl2 Suggested_Inductions_in_the_Associativity_of_App_Example.html -- ACL2 Version 6.3

    Suggested Inductions in the Associativity of App Example

    To find a plausible induction argument, the system studies the recursions exhibited by the terms in the conjecture.

    Roughly speaking, a call of a recursive function ``suggests'' an induction if the argument position decomposed in recursion is occupied by a variable.

    In this conjecture, three terms suggest inductions:

    (app a b)
    
    (app b c)
    
    (app a (app b c))
    
    The variable recursively decomposed is indicated in bold.




    acl2-sources/doc/HTML/Symbolic_Execution_of_Models.html0000664002132200015000000000157712222333526022557 0ustar kaufmannacl2 Symbolic_Execution_of_Models.html -- ACL2 Version 6.3

    Symbolic Execution of Models

    But ACL2 is more than a programming language.

    Initialize x to 5 and let y be any legal value.

    Because ACL2 is a mathematical language, we can simplify the expression

    (lookup 'z (mc (s 'mult 5 y) 29))
    

    and get (+ y y y y y). This is symbolic execution because not all of the parameters are known.




    acl2-sources/doc/HTML/TABLE.html0000664002132200015000000003076112222333517015610 0ustar kaufmannacl2 TABLE.html -- ACL2 Version 6.3

    TABLE

    user-managed tables
    Major Section:  EVENTS
    

    Examples:
    (table tests 1 '(...))                ; set contents of tests[1] to '(...)
    (table tests 25)                      ; get contents of tests[25]
    (table tests)                         ; return table tests as an alist
    (table tests nil nil :clear)          ; clear table tests
    (table tests nil '((foo . 7)) :clear) ; set table tests to ((foo . 7))
    (table tests nil nil :guard)          ; fetch the table guard
    (table tests nil nil :guard term)     ; set the table guard
    
    
    

    Some Related Topics

    General Form: (table table-name key-term value-term op term)
    where table-name is a symbol that is the name of a (possibly new) table, key-term and value-term, if present, are arbitrary terms involving (at most) the single variable world, op, if present, is one of the table operations below, and term, if present, is a term. Table returns an ACL2 ``error triple'' (see error-triples). The effect of table on state depends on op and how many arguments are presented. Some invocations actually have no effect on the ACL2 world and hence an invocation of table is not always an ``event''. We explain below, after giving some background information.

    Important Note: The table forms above are calls of a macro that expand to involve the special variable state. This will prevent you from accessing a table from within a hint or theory where you do not have the state variable. However, the form

    (table-alist 'tests world)
    
    returns the alist representation of the table named test in the given world. Often you have access to world.

    The ACL2 system provides ``tables'' by which the user can associate one object with another. Tables are in essence just conventional association lists -- lists of pairs -- but the ACL2 environment provides a means of storing these lists in the ``ACL2 world'' of the current state. The ACL2 user could accomplish the same ends by using ACL2 ``global variables;'' however, limitations on global variable names are imposed to ensure ACL2's soundness. By convention, no table is important to ACL2's soundness, even though some features of the system use tables, and the user is invited to make free use of tables. Because tables are stored in the ACL2 world they are restored by include-book and undone by :ubt. Many users of Nqthm requested a facility by which user data could be saved in Nqthm ``lib files'' and tables are ACL2's answer to that request.

    Abstractly, each table is an association list mapping ``keys'' to ``values.'' In addition, each table has a ``:guard,'' which is a term that must be true of any key and value used. By setting the :guard on a table you may enforce an invariant on the objects in the table, e.g., that all keys are positive integers and all values are symbols. Each table has a ``name,'' which must be a symbol. Given a table name, the following operations can be performed on the table.

    :put -- associate a value with a key (possibly changing the value currently associated with that key).

    :get -- retrieve the value associated with a key (or nil if no value has been associated with that key).

    :alist -- return an alist showing all keys and non-nil values in the table.

    :clear -- clear the table (so that every value is nil), or if val is supplied then set table to that value (which must be an alist).

    :guard -- fetch or set the :guard of the table.

    When the operations above suggest that the table or its :guard are modified, what is actually meant is that the current state is redefined so that in it, the affected table name has the appropriate properties. in such cases, the table form is an event (see events). In the :put case, if the key is already in the table and associated with the proposed value, then the table event is redundant (see redundant-events).

    Table forms are commonly typed by the user while interacting with the system. :Put and :get forms are especially common. Therefore, we have adopted a positional syntax that is intended to be convenient for most applications. Essentially, some operations admit a ``short form'' of invocation.

    (table name key-term value-term :put)   ; long form
    (table name key-term value-term)        ; short form
    
    evaluates the key- and value-terms, obtaining two objects that we call key and value, checks that the key and value satisfy the :guard on the named table and then ``modifies'' the named table so that the value associated with key is value. When used like this, table is actually an event in the sense that it changes the ACL2 world. In general, the forms evaluated to obtain the key and value may involve the variable world, which is bound to the then-current world during the evaluation of the forms. However, in the special case that the table in question is named acl2-defaults-table, the key and value terms may not contain any variables. Essentially, the keys and values used in events setting the acl2-defaults-table must be explicitly given constants. See acl2-defaults-table.
    (table name key-term nil :get)          ; long form
    (table name key-term)                   ; short form
    
    evaluates the key-term (see note below), obtaining an object, key, and returns the value associated with key in the named table (or, nil if there is no value associated with key). When used like this, table is not an event; the value is simply returned.
    (table name nil nil :alist)             ; long form
    (table name)                            ; short form
    
    returns an alist representing the named table; for every key in the table with a non-nil associated value, the alist pairs the key and its value. The order in which the keys are presented is unspecified. When used like this, table is not an event; the alist is simply returned.
    (table name nil val :clear)
    
    sets the named table to the alist val, making the checks that :put makes for each key and value of val. When used like this, table is an event because it changes the ACL2 world.
    (table name nil nil :guard)
    
    returns the translated form of the guard of the named table.
    (table name nil nil :guard term)
    
    Provided the named table is empty and has not yet been assigned a :guard and term (which is not evaluated) is a term that mentions at most the variables key, val and world, this event sets the :guard of the named table to term. Whenever a subsequent :put occurs, term will be evaluated with key bound to the key argument of the :put, val bound to the val argument of the :put, and world bound to the then current world. An error will be caused by the :put if the result of the evaluation is nil.

    Note that it is not allowed to change the :guard on a table once it has been explicitly set. Before the :guard is explicitly set, it is effectively just t. After it is set it can be changed only by undoing the event that set it. The purpose of this restriction is to prevent the user from changing the :guards on tables provided by other people or the system.

    The intuition behind the :guard mechanism on tables is to enforce invariants on the keys and values in a table, so that the values, say, can be used without run-time checking. But if the :guard of a table is sensitive to the ACL2 world, it may be possible to cause some value in the table to cease satisfying the :guard without doing any operations on the table. Consider for example the :guard ``no value in this table is the name of an event.'' As described, that is enforced each time a value is stored. Thus, 'bang can be :put in the table provided there is no event named bang. But once it is in the table, there is nothing to prevent the user from defining bang as a function, causing the table to contain a value that could not be :put there anymore. Observe that not all state-sensitive :guards suffer this problem. The :guard ``every value is an event name'' remains invariant, courtesy of the fact that undoing back through an event name in the table would necessarily undo the :put of the name into the table.

    Table was designed primarily for convenient top-level use. Tables are not especially efficient. Each table is represented by an alist stored on the property list of the table name. :Get is just a getprop and assoc-equal. :Put does a getprop to the get the table alist, a put-assoc-equal to record the new association, and a putprop to store the new table alist -- plus the overhead associated with :guards and undoable events, and checking (for redundancy) if the key is already bound to its proposed value. Note that there are never duplicate keys in the resulting alist; in particular, when the operation :clear is used to install new alist, duplicate keys are removed from that alist.

    A table name may be any symbol whatsoever. Symbols already in use as function or theorem names, for example, may be used as table names. Symbols in use only as table names may be defined with defun, etc. Because there are no restrictions on the user's choice of table names, table names are not included among the logical names. Thus, :pe name will never display a table event (for a logical name other than :here). Either :pe name will display a ``normal'' event such as (defun name ...) or (defthm name ...) or else :pe name will cause an error indicating that name is not a logical name. This happens even if name is in use as a table name. Similarly, we do not permit table names to have documentation strings, since the same name might already have a documentation string. If you want to associate a documentation string with a table name that is being used no other way, define the name as a label and use the :doc feature of deflabel (see deflabel); also see defdoc.




    acl2-sources/doc/HTML/TAKE.html0000664002132200015000000000265612222333524015505 0ustar kaufmannacl2 TAKE.html -- ACL2 Version 6.3

    TAKE

    initial segment of a list
    Major Section:  ACL2-BUILT-INS
    

    For any natural number n not exceeding the length of l, (take n l) collects the first n elements of the list l.

    The following is a theorem (though it takes some effort, including lemmas, to get ACL2 to prove it):

    (equal (length (take n l)) (nfix n))
    
    If n is an integer greater than the length of l, then take pads the list with the appropriate number of nil elements. Thus, the following is also a theorem.
    (implies (and (integerp n)
                  (true-listp l)
                  (<= (length l) n))
             (equal (take n l)
                    (append l (make-list (- n (length l))))))
    
    For related functions, see nthcdr and see butlast.

    The guard for (take n l) is that n is a nonnegative integer and l is a true list.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/TAU-DATA.html0000664002132200015000000000320612222333517016113 0ustar kaufmannacl2 TAU-DATA.html -- ACL2 Version 6.3

    TAU-DATA

    to see what tau knows about a function symbol
    Major Section:  HISTORY
    

    Examples:
    (tau-data binary-+)
    
    General Form:
    (tau-data fn)
    

    This macro returns a list structure that indicates what facts about the symbol fn are known to the tau system. Fn should either be a function symbol or else a macro associated with fn; see macro-aliases-table. See introduction-to-the-tau-system for background details.

    The list structure should be self-explanatory given the following brief comments. The ``index'' of a function, when non-nil, means the function is a monadic Boolean function treated by the tau system as a tau predicate.

    The ``positive'' and ``negative implicants'' are conjunctions that indicate the tau implied by the given one or its negation.

    The ``signatures'' entry is a formula indicating all the known signatures. If the signatures formula is T it means there are no known signatures. (Signatures is the conjunction of all signature rules and the empty conjunction is T.)

    If you wish to see a long list of all the runes from which some tau information has been gleaned, evaluate (global-val 'tau-runes (w state)).




    acl2-sources/doc/HTML/TAU-DATABASE.html0000664002132200015000000000154712222333517016554 0ustar kaufmannacl2 TAU-DATABASE.html -- ACL2 Version 6.3

    TAU-DATABASE

    to see the tau database as a (very large) object
    Major Section:  HISTORY
    

    Example:
    (tau-database (w state))
    

    This function returns a large list object that shows in a human-readable way what the tau system knows about every function symbol. It is supposed to be self-explanatory. See introduction-to-the-tau-system for background details.

    If the output is not self-explanatory, please contact the implementors and we will improve the output or the documentation.




    acl2-sources/doc/HTML/TAU-INTERVAL-DOM.html0000664002132200015000000000162212222333530017236 0ustar kaufmannacl2 TAU-INTERVAL-DOM.html -- ACL2 Version 6.3

    TAU-INTERVAL-DOM

    access the domain of a tau interval
    Major Section:  TAU-SYSTEM
    

    It is the case that

    (tau-interval-dom (make-tau-interval dom lo-rel lo hi-rel hi)) = dom
    

    For a well-formed interval, dom is one of the symbols INTEGERP, RATIONALP, ACL2-NUMBERP, or NIL. When the domain is NIL there is no domain restriction.

    When the domain is INTEGERP, there are additional constraints on the other components. See make-tau-interval.




    acl2-sources/doc/HTML/TAU-INTERVAL-HI-REL.html0000664002132200015000000000210312222333530017532 0ustar kaufmannacl2 TAU-INTERVAL-HI-REL.html -- ACL2 Version 6.3

    TAU-INTERVAL-HI-REL

    access the upper bound relation of a tau interval
    Major Section:  TAU-SYSTEM
    

    It is the case that

    (tau-interval-hi-rel (make-tau-interval dom lo-rel lo hi-rel hi)) = hi-rel
    

    For a well-formed interval, hi-rel is a Boolean, where t denotes the < (strong inequality or ``less-than'') relation and nil denotes <= (weak inequality or ``less-than-or-equal'') relation between the elements of the interval and the upper bound.

    When the domain of an interval is INTEGERP, there are additional constraints on the other components. See make-tau-interval.




    acl2-sources/doc/HTML/TAU-INTERVAL-HI.html0000664002132200015000000000170012222333530017114 0ustar kaufmannacl2 TAU-INTERVAL-HI.html -- ACL2 Version 6.3

    TAU-INTERVAL-HI

    access the upper bound of a tau interval
    Major Section:  TAU-SYSTEM
    

    It is the case that

    (tau-interval-hi (make-tau-interval dom lo-rel lo hi-rel hi)) = hi
    

    For a well-formed interval, hi is either nil, denoting positive infinity, or a rational number giving the upper bound of the interval. It must be the case that the upper bound is weakly above the lower bound of a well-formed interval.

    When the domain of an interval is INTEGERP, there are additional constraints on the other components. See make-tau-interval.




    acl2-sources/doc/HTML/TAU-INTERVAL-LO-REL.html0000664002132200015000000000210312222333530017544 0ustar kaufmannacl2 TAU-INTERVAL-LO-REL.html -- ACL2 Version 6.3

    TAU-INTERVAL-LO-REL

    access the lower bound relation of a tau interval
    Major Section:  TAU-SYSTEM
    

    It is the case that

    (tau-interval-lo-rel (make-tau-interval dom lo-rel lo hi-rel hi)) = lo-rel
    

    For a well-formed interval, lo-rel is a Boolean, where t denotes the < (strong inequality or ``less-than'') relation and nil denotes <= (weak inequality or ``less-than-or-equal'') relation between the lower bound and the elements of the interval.

    When the domain of an interval is INTEGERP, there are additional constraints on the other components. See make-tau-interval.




    acl2-sources/doc/HTML/TAU-INTERVAL-LO.html0000664002132200015000000000170012222333530017126 0ustar kaufmannacl2 TAU-INTERVAL-LO.html -- ACL2 Version 6.3

    TAU-INTERVAL-LO

    access the lower bound of a tau interval
    Major Section:  TAU-SYSTEM
    

    It is the case that

    (tau-interval-lo (make-tau-interval dom lo-rel lo hi-rel hi)) = lo
    

    For a well-formed interval, lo is either nil, denoting negative infinity, or a rational number giving the lower bound of the interval. It must be the case that the lower bound is weakly below the upper bound of a well-formed interval.

    When the domain of an interval is INTEGERP, there are additional constraints on the other components. See make-tau-interval.




    acl2-sources/doc/HTML/TAU-INTERVALP.html0000664002132200015000000000701212222333530016740 0ustar kaufmannacl2 TAU-INTERVALP.html -- ACL2 Version 6.3

    TAU-INTERVALP

    Boolean recognizer for tau intervals
    Major Section:  TAU-SYSTEM
    

    General Form:
    (tau-intervalp x)
    

    An interval is a structure of the form: (dom (lo-rel . lo) . (hi-rel . hi)). Every tau contains an interval used to represent the domain and the upper and lower bounds of the objects recognized by the tau.

    Restrictions on the components of an interval are as follows. For an interpretation of the meaning of the components, see in-tau-intervalp or make-tau-interval.

    dom (``domain'') -- must be one of four symbols: INTEGERP, RATIONALP, ACL2-NUMBERP, or NIL.

    The two ``relations,'' lo-rel and hi-rel, must be Booleans.

    Lo and hi must be either nil or explicit rational numbers. Lo must be no greater than hi (where nils represent negative or positive infinity for lo and hi respectively.

    Finally, if the dom is INTEGERP, then both relations must nil and lo and hi must be integers when they are non-nil.

    Recall that make-tau-interval constructs intervals. The intervals it constructs are well-formed only if the arguments to make-tau-interval satisfy the rules above; make-tau-interval does not coerce or adjust its arguments in any way. Thus, it can be (mis-)used to create non-intervals. Here are examples of tau-intervalp using make-tau-interval.

    ; integers: 0 <= x <= 10:
    (tau-intervalp (make-tau-interval 'INTEGERP nil 0 nil 10))      = t
    
    ; integers: 0 <= x (i.e., the natural numbers):
    (tau-intervalp (make-tau-interval 'INTEGERP nil 0 nil nil))     = t
    
    ; violations of domain rules:
    (tau-intervalp (make-tau-interval 'INTEGERP t 0 t 10))          = nil
    (tau-intervalp (make-tau-interval 'INTEGERP nil 0 nil 10/11))   = nil
    
    ; violation of rule that bounds must be rational if non-nil:
    (tau-intervalp (make-tau-interval 'ACL2-NUMBERP t 0 t #c(3 5))) = nil
    
    ; violation of rule that lo <= hi:
    (tau-intervalp (make-tau-interval 'ACL2-NUMBERP t 0 t -10))     = nil
    
    ; rationals: 0 < x <= 22/7:
    (tau-intervalp (make-tau-interval 'RATIONALP t 0 nil 22/7))     = t
    
    ; numbers: -10 < x < 10:
    (tau-intervalp (make-tau-interval 'ACL2-NUMBERP t -10 t 10))    = t
    
    ; any: -10 < x < 10:
    (tau-intervalp (make-tau-interval nil t -10 t 10))              = t
    
    : any:
    (tau-intervalp (make-tau-interval nil nil nil nil nil))         = t
    
    Note that the second-to-last interval, with domain nil contains all non-numbers as well as numbers strictly between -10 and 10. The reason is that the interval contains 0 and all non-numbers are coerced to 0 by the inequality functions.

    Note that the last interval contains all ACL2 objects. It is called the ``universal interval.''




    acl2-sources/doc/HTML/TAU-STATUS.html0000664002132200015000000000465512222333531016432 0ustar kaufmannacl2 TAU-STATUS.html -- ACL2 Version 6.3

    TAU-STATUS

    query or set tau system status
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (tau-status)
    (tau-status :system t)
    (tau-status :auto-mode nil)
    (tau-status :system t :auto-mode nil)
    
    General Form:
    (tau-status :system a :auto-mode b)
    

    where a and b are Booleans. Both keyword arguments are optional and they may be presented in either order. Value a controls whether the tau-system is used during subsequent proofs. Value b controls whether tau system rules are added automatically (``greedily'') when rules of other rule-classes are added. If no arguments are supplied, this is not an event and just returns an error-triple (see error-triples) indicating the current settings. See introduction-to-the-tau-system for background details.

    The two flags are independent. For example, the tau system may be disabled in proof attempts even though it is automatically (and silently) extending its database as rules of other classes are added.

    Flag (a) is actually toggled by enabling or disabling the :executable-counterpart of tau-system. Flag (b) is toggled with the function set-tau-auto-mode, which manipulates the acl2-defaults-table.

    This macro expands into zero, one, or two events, as required by the supplied values of flags a and b.

    If no arguments are supplied the form is not an event and simply returns (as an error triple (mv nil ans state); see error-triples) the current settings of the two flags. For example:

    ACL2 !>(tau-system)
     ((:SYSTEM NIL) (:AUTO-MODE T))
    

    intended to be self-explanatory.




    acl2-sources/doc/HTML/TAU-SYSTEM.html0000664002132200015000000004664112222333530016433 0ustar kaufmannacl2 TAU-SYSTEM.html -- ACL2 Version 6.3

    TAU-SYSTEM

    make a rule for the ACL2 ``type checker''
    Major Section:  RULE-CLASSES
    

    This documentation topic describes the syntactic form of ``tau-system'' rules; these rules extend ACL2's ``type checker.'' For an introduction to the tau system, see introduction-to-the-tau-system.

    There happens to be a function named tau-system, defined as the identity function. Its only role is to provide the rune (:EXECUTABLE-COUNTERPART TAU-SYSTEM), which is used to enable and disable the tau system. Otherwise the function tau-system has no purpose and we recommend that you avoid using it so you are free to enable and disable the tau system.

    When in the default (``greedy'') mode (see set-tau-auto-mode), every defun and every :corollary (see :rule-classes) of every defthm stored as a rule of any :rule-class is inspected to determine if it is of one of the forms below. Rules of these forms are added to the tau database, even if they are not labeled as :tau-system rules, e.g., a :rewrite rule might contribute to the tau database! To add a rule to the tau database without adding any other kind of rule, tag it with :rule-classes :tau-system. If a theorem has :rule-classes nil, it is not considered for the tau database.

    General Forms:
    Boolean:
    (booleanp (p v))
    
    Eval:
    (p 'const) or
    (p *const*)
    
    Simple:
    (implies (p v) (q v))
    
    Conjunctive:
    (implies (and (p1 v) ... (pk v)) (q v)), ; Here k must exceed 1.
    
    Signature Form 1:
    (implies (and (p1 x1) (p2 x2) ...)
             (q (fn x1 x2 ...)))
    
    Signature Form 2:
    (implies (and (p1 x1) (p2 x2) ...)
             (q (mv-nth 'n (fn x1 x2 ...))))
    
    Bounder Form 1 (or Form 2):
    (implies (and (tau-intervalp i1)
                  ...
                  (or (equal (tau-interval-dom i1) 'dom1-1)
                      ...)
                  ...
                  (in-tau-intervalp x1 i1)
                  ...)
             (and (tau-intervalp (bounder-fn i1 ...))
                  (in-tau-intervalp target
                                    (bounder-fn i1 ...))))
    
    where target is
    (fn x1 ... y1 ...)             in Form 1, and
    (mv-nth 'n (fn x1 ... y1 ...)) in Form 2
    
    Big Switch:
    (equal (fn . formals) body)
    
    MV-NTH Synonym:
    (equal (nth-alt x y) (mv-nth x y)) or
    (equal (mv-nth x y) (nth-alt x y))
    

    The symbols p, q, p1, etc., denote monadic (one-argument) Boolean-valued function symbols, or equalities in which one argument is constant, arithmetic comparisons in which one argument is a rational or integer constant, or the logical negations of such terms. By ``equalities'' we allow EQUAL, EQ, EQL, and =. By ``arithmetic comparison'' we mean <, <=, >=, or >. Any of these tau predicates may appear negated.

    The notation (p v) above might stand for any one of:

    (INTEGERP X)
    (EQUAL V 'MONDAY)
    (<= I 16)
    (NOT (EQUAL X 'SUNDAY))
    

    The different rule forms above affect different aspects of the tau system. We discuss each form in more detail below.

    Some Related Topics

    The documentation below is written as though the tau system is in auto mode! To insure that the only rules added to the tau system are those explicitly assigned to :rule-class :tau-system, you should use set-tau-auto-mode to select manual mode.

    General Form: Boolean:
    (booleanp (p v))
    
    Here p must be a function symbol and v must be a variable. Such a :tau-system rule adds p to the list of tau predicates. If p was recognized as Boolean when it was defined, there is no need to state this rule. This form is needed if you define a monadic Boolean function in such a way that the system does not recognize that it is Boolean.

    General Form: Eval:
    (p 'const) or
    (p *const*)
    

    Here p must be a function symbol. In addition, recall that these general tau predicate forms may appear negated. So the form above includes such theorems as (NOT (GOOD-STATEP *INITIAL-STATE*)). A theorem of this form thus records whether a named predicate is true or false on the given constant.

    Generally, when the tau system must determine whether an enabled tau predicate is true or false on a constant, it simply evaluates the predicate on the constant. This can be impossible or very inefficient if p is not defined but constrained, or if p is defined in a hard-to-compute way (e.g., (defun p (x) (evenp (ack x x))) where ack is the Ackermann function), or perhaps if the constant is very large. By proving a :tau-system rule of Eval form, you cause the tau system to note the value of the predicate on the constant and henceforth to look it up instead of evaluating the definition.

    A difficulty, however, is determining that a slow down is due to the evaluation of tau predicates and not some other reason. The first step is determining that tau is slowing the proof down. See time-tracker-tau for an explanation of TIME-TRACKER-NOTEs output during some proofs involving tau reasoning. These notes can alert you to the fact that significant amounts of time are being spent in the tau system. Time-tracker-tau gives some ways of determining whether tau predicate evaluation is involved. (If worse comes to worst, consider the following hack: In the ACL2 source file tau.lisp, immediately after the definition of the system function ev-fncall-w-tau-recog, there is a comment which contains some raw Lisp code that can be used to investigate whether tau's use of evaluation on constants is causing a problem.) However, once a recognizer and the constants on which it is being evaluated are identified, the tau system can be sped up by proving Eval rules to pre-compute and store the values of the recognizer on those constants. Alternatively, at the possible loss of some completeness in the tau system, the executable counterpart of the recognizer can be disabled.

    General Form: Simple:
    (implies (p v) (q v))
    
    Here v must be a variable symbol. This rule builds-in the information that anything satisfying p must also satisfy q, i.e., the ``type'' q includes the ``type'' p. Recall that the forms may be negated. Most of the time, p and q will be predicate symbols but it is possible they will be equalities- or inequalities-with-constants. Examples of Simple rules include the following, which are in fact built-in:

    (implies (natp x) (integerp x))
    (implies (integerp x) (rationalp x))
    (implies (integerp x) (not (true-listp x)))
    (implies (natp x) (not (< x 0)))
    (implies (symbol-alistp x) (alistp x))
    
    Because the tau system records the transitive closure of the Simple rules, any time a term is known to satisfy natp it is also known to satisfy integerp and rationalp, and known not to satisfy true-listp, and known to be non-negative.

    General Form: Conjunctive:
    (implies (and (p1 v) ... (pk v)) (q v)), ; Here k must exceed 1.
    
    The pi and q may be any tau predicates or their negations, v must be a variable symbol, and i must exceed 1 or else this is a Simple rule. An obvious operational interpretation of this rule is that if an object is known to satisfy all of the pi, then it is known to satisfy q. However, the actual interpretation is more general. For example, if an object is known to satisfy all but one of the pi and is known not to satisfy q, then the object is known not to satisfy the ``missing'' pi.

    For example, the following Conjunctive rule allows tau to conclude that if weekday D is not MON, TUE, THU or FRI, then it is WED:

    (implies (and (weekdayp d)
                  (not (eq d 'MON))
                  (not (eq d 'TUE))
                  (not (eq d 'WED))
                  (not (eq d 'THU)))
             (eq d 'FRI))
    
    The tau database is not closed under conjunctive rules; they are applied dynamically.

    General Form: Signature Form 1:
    (implies (and (p1 x1) (p2 x2) ... (pn xn) dep-hyp)
             (q (fn x1 x2 ... xn)))
    
    The pi and q may be any tau predicates or their negations, fn must be a function symbol of arity n, the xi must be distinct variable symbols and dep-hyp may be any term, provided it is not of the (pi xi) shape and the only the variables in it are the xi.

    The Signature form actually allows multiple tau predicates to be applied to each variable, e.g., x1 might be required to be both an INTEGERP and EVENP. The Signature form allows there to be multiple hypotheses classified as dep-hyps, i.e., not fitting any of the previous shapes, and they are implicitly just conjoined. The name ``dep-hyp'' is an abbreviation of ``dependent hypothesis'' and stems from the fact they often express relations between several of the function's inputs rather than type-like constraints on individual inputs.

    A Signature rule informs tau that the function fn returns an object satisfying q provided that the arguments satisfy the respective pi and provided that dep-hyp occurs in the current context. Note: to be precise, dependent hypotheses are relieved only by applying ACL2's most primitive form of reasoning, type-set. In particular, tau reasoning is not used to establish dependent hypotheses. The presence of a dep-hyp in a signature rule may severely restrict its applicability. We discuss this after showing a few mundane examples.

    An example Signature rule is

    (implies (and (integer-listp x)
                  (integer-listp y))
             (integer-listp (append x y)))
    
    Of course, a function may have multiple signatures:
    (implies (and (symbol-listp x)
                  (symbol-listp y))
             (symbol-listp (append x y)))
    
    Here is a Signature rule for the function pairlis$:
    (implies (and (symbol-listp x)
                  (integer-listp y))
             (symbol-alistp (pairlis$ x y)))
    
    The tau system can consequently check this theorem by composing the last two rules shown and exploiting Simple rule stating that symbol-alists are also alists:
    (thm (implies (and (symbol-listp a)
                       (symbol-listp b)
                       (integer-listp y))
                  (alistp (pairlis$ (append a b) y))))
    
    Since a and b are known to be lists of symbols and a signature for append is that it preserves that predicate, the first argument to the pairlis$ expression is known to be a list of symbols. This means the Signature rule for pairlis$ tells us the result is a symbol-alistp, but the previously mentioned Simple rule, (implies (symbol-alistp x) (alistp x)), tells us the result is also an alistp.

    When a Signature rule has an dep-hyp, that hypothesis is not an expression in the tau system. Tau is not used to check that hypothesis. Instead, tau uses the more primitive type-set mechanism of ACL2. Here is an example of a Signature rule with a dep-hyp:

    (implies (and (natp n)
                  (integer-listp a)
                  (< n (len a)))
             (integerp (nth n a)))
    

    Note that the last hypothesis is a dependent hypothesis: it is not a tau predicate but a relationship between n and a. It is relieved by type-set. If one is trying to compute the signature of an (nth n a) expression in a context in which (< n (len a)) is explicitly assumed, then this mechanism would establish the dependent hypothesis. But one can easily imagine an almost identical context where, say (< n (len (rev a))) is explicitly assumed. In that context, the Signature rule would not be fired because type-set cannot establish (< n (len a)) from (< n (len (rev a))), even though it would be easily proved by rewriting using the theorem (equal (len (rev a)) (len a)).

    Note also that if this signature could be phrased in a way that eliminates the dependency between n and a it would be more effective. For example, here is a related Signature rule without a dependent hypothesis:

    (implies (and (natp n)
                  (register-filep a)
                  (< n 16))
             (integerp (nth n a)))
    
    In this theorem we require only that n be less than 16, which is a tau predicate and hence just an additional tau constraint on n.

    General Form: Signature Form 2:
    (implies (and (p1 x1) (p2 x2) ... (pn xn) dep-hyp)
    
             (q (mv-nth 'n (fn x1 x2 ... xn))))
    
    This form of signature rule is just like form 1 except that it is useful for functions that return multiple-values and allows us to ``type-check'' their individual outputs.

    General Form: Bounder Forms 1 and 2:
    (implies (and (tau-intervalp i1)
                  ...
                  (or (equal (tau-interval-dom i1) 'dom1-1)
                      ...)
                  ...
                  (in-tau-intervalp x1 i1)
                  ...)
             (and (tau-intervalp (bounder-fn i1 ...))
                  (in-tau-intervalp target
                                    (bounder-fn i1 ...))))
    
    where target is either (fn x1 ... y1 ...) in Form 1 or (mv-nth 'n (fn x1 ... y1 ...)) in Form 2.

    This form is for advanced users only and the schema given above is just a reminder of the general shape. A ``bounder'' for a given function symbol, fn, is a function symbol bounder-fn that computes an interval containing (fn x1 ... y1 ...) (or its nth component in the case of Form 2 rules) from the intervals containing certain of the arguments of fn. The correctness theorem for a bounder function informs the tau system that bounds for fn are computed by bounder-fn and sets up the correspondence between the relevant arguments, xi, of fn and the intervals containing those arguments, ii to which bounder-fn is applied. When the tau system computes the tau for a call of fn, it computes the tau of the relevant arguments and applies the bounder to the intervals of those tau. This provides a domain and upper and/or lower bounds for the value of the term. The tau system then further augments that with signature rules. See bounders for details on intervals, bounders, and bounder correctness theorems.

    General Form: Big Switch:
    (equal (fn . formals) body)
    
    In the Big Switch form, fn must be a function symbol, formals must be a list of distinct variable symbols, and body must be a ``big switch'' term, i.e., one that case splits on tau predicates about a single variable and produces a term not involving that variable. An example of a Big Switch rule is
    (equal (conditional-type x y)
           (if (consp x)
               (consp y)
               (integerp y)))
    
    The idea is that the tau system can treat calls of conditional-type as a tau-predicate after determining the tau of an argument.

    Since equality-to-constants are tau predicates, a more common example of a Big Switch rule is

    (equal (dtypep x expr)
           (case x
                 (STMT (stmt-typep expr))
                 (EXPR (expr-typep expr))
                 (MODULE (module-typep expr))
                 (otherwise nil)))
    
    This is because (case x (STMT ...) ...) macroexpands in ACL2 to (if (eql x 'STMT) ... ...) and (eql x 'STMT) is a tau predicate about x.

    Big Switch rules are recognized when a function is defined (if tau is in automatic mode). They generally do not have to be proved explicitly, though they might be when mutual recursion is involved. Only the first detected Big Switch rule about a function fn is recognized.

    General Form: MV-NTH Synonym:
    (equal (nth-alt x y) (mv-nth x y)) or
    (equal (mv-nth x y) (nth-alt x y))
    
    Rules of this form just tell the tau system that the user-defined function nth-alt is synonymous with the ACL2 primitive function mv-nth. Because ACL2's rewriter gives special handling to mv-nth, users sometimes define their own versions of that function so they can disable them and control rewriting better. By revealing to the tau system that such a synonym has been introduced you allow Signature rules of Form 2 to be used.




    acl2-sources/doc/HTML/TENTH.html0000664002132200015000000000066212222333524015636 0ustar kaufmannacl2 TENTH.html -- ACL2 Version 6.3

    TENTH

    tenth member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/TERM-ORDER.html0000664002132200015000000001771012222333524016376 0ustar kaufmannacl2 TERM-ORDER.html -- ACL2 Version 6.3

    TERM-ORDER

    the ordering relation on terms used by ACL2
    Major Section:  ACL2-BUILT-INS
    

    ACL2 must occasionally choose which of two terms is syntactically smaller. The need for such a choice arises, for example, when using equality hypotheses in conjectures (the smaller term is substituted for the larger elsewhere in the formula), in stopping loops in permutative rewrite rules (see loop-stopper), and in choosing the order in which to try to cancel the addends in linear arithmetic inequalities. When this notion of syntactic size is needed, ACL2 uses ``term order.'' Popularly speaking, term order is just a lexicographic ordering on terms. But the situation is actually more complicated.

    We define term order only with respect to terms in translated form. See trans. Constants are viewed as built up by pseudo-function applications, as described at the end of this documentation.

    Term1 comes before term2 in the term order iff

    (a) the number of variable occurrences in term1 is less than that in term2, or

    (b) the numbers of variable occurrences in the two terms are equal but the number of function applications in term1 is less than that in term2, or

    (c) the numbers of variable occurrences in the two terms are equal, the numbers of functions applications in the two terms are equal, but pseudo-function application count for term1 is less than that for term2, or

    (d) the numbers of variable occurrences in the two terms are equal, the numbers of functions applications in the two terms are equal, the pseudo-function application counts for the two terms are equal, and term1 comes before term2 in a lexicographic ordering, lexorder, based their structure as Lisp objects: see lexorder.

    The function term-order, when applied to the translations of two ACL2 terms, returns t iff the first is ``less than or equal'' to the second in the term order.

    By ``number of variable occurrences'' we do not mean ``number of distinct variables'' but ``number of times a variable symbol is mentioned.'' (Cons x x) has two variable occurrences, not one. Thus, perhaps counterintuitively, a large term that contains only one variable occurrence, e.g., (standard-char-p (car (reverse x))) comes before (cons x x) in the term order.

    Since constants contain no variable occurrences and non-constant expressions must contain at least one variable occurrence, constants come before non-constants in the term order, no matter how large the constants. For example, the list constant

    '(monday tuesday wednesday thursday friday)
    
    comes before x in the term order. Because term order is involved in the control of permutative rewrite rules and used to shift smaller terms to the left, a set of permutative rules designed to allow the permutation of any two tips in a tree representing the nested application of some function will always move the constants into the left-most tips. Thus,
    (+ x 3 (car (reverse klst)) (dx i j)) ,
    
    which in translated form is
    (binary-+ x
              (binary-+ '3
                        (binary-+ (dx i j)
                                  (car (reverse klst))))),
    
    will be permuted under the built-in commutativity rules to
    (binary-+ '3
              (binary-+ x
                        (binary-+ (car (reverse klst))
                                  (dx i j))))
    
    or
    (+ 3 x (car (reverse klst)) (dx i j)).
    
    Two terms with the same numbers of variable occurrences and function applications and the same pseudo-function application count are ordered by lexicographic means, based on their structures. See lexorder. Thus, if two terms (member ...) and (reverse ...) contain the same numbers of variable occurrences and function applications, and no quoted constants, then the member term is first in the term order because member comes before reverse in the term order (which is here reduced to alphabetic ordering).

    It remains to discuss the notion of pseudo-function application count.

    Clearly, two constants are ordered using cases (c) and (d) of term order, since they each contain 0 variable occurrences and no function calls. This raises the question ``How many function applications are in a constant?'' Because we regard the number of function applications as a more fundamental measure of the size of a constant than lexicographic considerations, we decided that for the purposes of term order, constants would be seen as being built by primitive constructor functions. These constructor functions are not actually defined in ACL2 but merely imagined for the purposes of term order. We here use suggestive names for these imagined functions, ignoring entirely the prior use of these names within ACL2. The imagined applications of these functions are what we refer to as pseudo-function applications.

    The constant function z constructs 0. Positive integers are constructed from (z) by the successor function, s. Thus 2 is (s (s (z))) and contains three function applications. 100 contains one hundred and one applications. Negative integers are constructed from their positive counterparts by -. Thus, -2 is (- (s (s (z)))) and has four applications. Ratios are constructed by the dyadic function /. Thus, -1/2 is

    (/ (- (s (z))) (s (s (z))))
    
    and contains seven applications. Complex rationals are similarly constructed from rationals. All character objects are considered primitive and are constructed by constant functions of the same name. Thus #\a and #\b both contain one application. Strings are built from the empty string, (o) by the ``string-cons'' function written cs. Thus "AB" is (cs (#\a) (cs (#\b) (o))) and contains five applications. Symbols are obtained from strings by ``packing'' the symbol-name with the unary function p. Thus 'ab is
    (p (cs (#\a) (cs (#\b) (o))))
    
    and has six applications. Note that packages are here ignored and thus 'acl2::ab and 'my-package::ab each contain just six applications. Finally, conses are built with cons, as usual. So '(1 . 2) is (cons '1 '2) and contains six applications, since '1 contains two and '2 contains three. This, for better or worse, answers the question ``How many function applications are in a constant?''

    Finally, when we refer to the ``pseudo-function application count'', we mean the number of pseudo-function applications as described above, except that we bound this number by the constant (fn-count-evg-max-val). (This bound is important for efficiency, so that constants that are very large cons structures do not cause significant slowdown as ACL2 attempts to walk through them while computing their pseudo-function application count.)




    acl2-sources/doc/HTML/TERM-TABLE.html0000664002132200015000000000311012222333531016335 0ustar kaufmannacl2 TERM-TABLE.html -- ACL2 Version 6.3

    TERM-TABLE

    a table used to validate meta rules
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    (table term-table t '((binary-+ x y) '3 'nil (car x)))
    

    See table for a general discussion of tables and the table event used to manipulate tables.

    The ``term-table'' is used at the time a meta rule is checked for syntactic correctness. Each proposed metafunction is run on each term in this table, and the result in each case is checked to make sure that it is a termp in the current world. In each case where this test fails, a warning is printed.

    Whenever a metafunction is run in support of the application of a meta rule, the result must be a term in the current world. When the result is not a term, a hard error arises. The term-table is simply a means for providing feedback to the user at the time a meta rule is submitted, warning of the definite possibility that such a hard error will occur at some point in the future.

    The key used in term-table is arbitrary. The top-most value is always the one that is used; it is the entire list of terms to be considered. Each must be a termp in the current ACL2 world.




    acl2-sources/doc/HTML/TERM.html0000664002132200015000000002666612222333522015535 0ustar kaufmannacl2 TERM.html -- ACL2 Version 6.3

    TERM

    the three senses of well-formed ACL2 expressions or formulas
    Major Section:  MISCELLANEOUS
    

    Examples of Terms:
    (cond ((caar x) (cons t x)) (t 0))   ; an untranslated term
    
    (if (car (car x)) (cons 't x) '0)    ; a translated term
    
    (car (cons x y) 'nil v)              ; a pseudo-term
    

    In traditional first-order predicate calculus a ``term'' is a syntactic entity denoting some object in the universe of individuals. Often, for example, the syntactic characterization of a term is that it is either a variable symbol or the application of a function symbol to the appropriate number of argument terms. Traditionally, ``atomic formulas'' are built from terms with predicate symbols such as ``equal'' and ``member;'' ``formulas'' are then built from atomic formulas with propositional ``operators'' like ``not,'' ``and,'' and ``implies.'' Theorems are formulas. Theorems are ``valid'' in the sense that the value of a theorem is true, in any model of the axioms and under all possible assignments of individuals to variables.

    However, in ACL2, terms are used in place of both atomic formulas and formulas. ACL2 does not have predicate symbols or propositional operators as distinguished syntactic entities. The ACL2 universe of individuals includes a ``true'' object (denoted by t) and a ``false'' object (denoted by nil), predicates and propositional operators are functions that return these objects. Theorems in ACL2 are terms and the ``validity'' of a term means that, under no assignment to the variables does the term evaluate to nil.

    We use the word ``term'' in ACL2 in three distinct senses. We will speak of ``translated'' terms, ``untranslated'' terms, and ``pseudo-'' terms.

    Translated Terms: The Strict Sense and Internal Form

    In its most strict sense, a ``term'' is either a legal variable symbol, a quoted constant, or the application of an n-ary function symbol or closed lambda expression to a true list of n terms.

    The legal variable symbols are symbols other than t or nil which are not in the keyword package, do not start with ampersand, do not start and end with asterisks, and if in the main Lisp package, do not violate an appropriate restriction (see name).

    Quoted constants are expressions of the form (quote x), where x is any ACL2 object. Such expressions may also be written 'x.

    Closed lambda expressions are expressions of the form (lambda (v1 ... vn) body) where the vi are distinct legal variable symbols, body is a term, and the only free variables in body are among the vi.

    The function termp, which takes two arguments, an alleged term x and a logical world w (see world), recognizes terms of a given extension of the logic. Termp is defined in :program mode. Its definition may be inspected with :pe termp for a complete specification of what we mean by ``term'' in the most strict sense. Most ACL2 term-processing functions deal with terms in this strict sense and use termp as a guard. That is, the ``internal form'' of a term satisfies termp, the strict sense of the word ``term.''

    Untranslated Terms: What the User Types

    While terms in the strict sense are easy to explore (because their structure is so regular and simple) they can be cumbersome to type. Thus, ACL2 supports a more sugary syntax that includes uses of macros and constant symbols. Very roughly speaking, macros are functions that produce terms as their results. Constants are symbols that are associated with quoted objects. Terms in this sugary syntax are ``translated'' to terms in the strict sense; the sugary syntax is more often called ``untranslated.'' Roughly speaking, translation just implements macroexpansion, the replacement of constant symbols by their quoted values, and the checking of all the rules governing the strict sense of ``term.''

    More precisely, macro symbols are as described in the documentation for defmacro. A macro, mac, can be thought of as a function, mac-fn, from ACL2 objects to an ACL2 object to be treated as an untranslated term. For example, caar is defined as a macro symbol; the associated macro function maps the object x into the object (car (car x)). A macro form is a ``call'' of a macro symbol, i.e., a list whose car is the macro symbol and whose cdr is an arbitrary true list of objects, used as a term. Macroexpansion is the process of replacing in an untranslated term every occurrence of a macro form by the result of applying the macro function to the appropriate arguments. The ``appropriate'' arguments are determined by the exact form of the definition of the macro; macros support positional, keyword, optional and other kinds of arguments. See defmacro.

    In addition to macroexpansion and constant symbol dereferencing, translation implements the mapping of let and let* forms into applications of lambda expressions and closes lambda expressions containing free variables. Thus, the translation of

    (let ((x (1+ i))) (cons x k))
    
    can be seen as a two-step process that first produces
    ((lambda (x) (cons x k)) (1+ i))
    
    and then
    ((lambda (x k) (cons x k)) (1+ i) k) .
    
    Observe that the body of the let and of the first lambda expression contains a free k which is finally bound and passed into the second lambda expression.

    Translation also maps flet forms into applications of lambda expressions. See flet.

    When we say, of an event-level function such as defun or defthm, that some argument ``must be a term'' we mean an untranslated term. The event functions translate their term-like arguments.

    To better understand the mapping between untranslated terms and translated terms it is convenient to use the keyword command :trans to see examples of translations. See trans and also see trans1.

    Finally, we note that the theorem prover prints terms in untranslated form. But there can be more than one correct untranslated term corresponding to a given translated term. For example, the translated term (if x y 'nil) can be untranslated as (if x y nil) and can also be untranslated as (and x y). The theorem prover attempts to print an untranslated term that is as helpful to the user as possible. In particular, consider a term of the form (nth k st) where st is a single-threaded object (see stobj) and the kth accessor of st is, say, kn. The theorem prover typically would expand (kn st) to (nth k st). If k is large then it could be difficult for the user to make sense out of a proof transcript that mentions the expanded term. Fortunately, the untranslation of (nth k st) would be (nth *kn* st); here *kn* would be a constant (see defconst) added by the defstobj event introducing st, defined to have value k. The user can extend this user-friendly style of printing applications of nth to stobjs; see add-nth-alias. These remarks about printing applications of function nth extend naturally to function update-nth. Moreover, the prover will attempt to treat terms as stobjs for the above purpose when appropriate. For example, if function foo has signature ((foo * st) => (mv * * * st)), where st is introduced with (defstobj st f0 f1), then the term (nth '1 (mv-nth '3 (foo x st0))) will be printed as (nth *f1* (mv-nth 3 (foo x st0))).

    Pseudo-Terms: A Common Guard for Metafunctions

    Because termp is defined in :program mode, it cannot be used effectively in conjectures to be proved. Furthermore, from the perspective of merely guarding a term processing function, termp often checks more than is required. Finally, because termp requires the logical world as one of its arguments it is impossible to use termp as a guard in places where the logical world is not itself one of the arguments.

    For these reasons we support the idea of ``pseudo-terms.'' A pseudo-term is either a symbol (but not necessarily one having the syntax of a legal variable symbol), a true list beginning with quote (but not necessarily well-formed), or the ``application of'' a symbol or pseudo lambda expression to a true list of pseudo-terms. A pseudo lambda expression is an expression of the form (lambda (v1 ... vn) body) where the vi are all symbols and body is a pseudo-term.

    Pseudo-terms are recognized by the unary function pseudo-termp. If (termp x w) is true, then (pseudo-termp x) is true. However, if x fails to be a (strict) term it may nevertheless still be a pseudo-term. For example, (car a b) is not a term, because car is applied to the wrong number of arguments, but it is a pseudo-term.

    The structures recognized by pseudo-termp can be recursively explored with the same simplicity that terms can be. In particular, if x is not a variablep or an fquotep, then (ffn-symb x) is the function (symbol or lambda expression) and (fargs x) is the list of argument pseudo-terms. A metafunction (see meta) or clause-processor (see clause-processor) may use pseudo-termp as the guard.




    acl2-sources/doc/HTML/THE-METHOD.html0000664002132200015000000001311212222333522016342 0ustar kaufmannacl2 THE-METHOD.html -- ACL2 Version 6.3

    THE-METHOD

    how to find proofs
    Major Section:  MISCELLANEOUS
    

    Also see introduction-to-the-theorem-prover for a more detailed tutorial on how to prove theorems with ACL2.

    Many users develop proof scripts in an Emacs buffer and submit one event at a time to the theorem prover running in a *shell* buffer. The script buffer is logically divided into two regions: the events that have been accepted by the theorem prover and those that have not yet been accepted. An imaginary ``barrier'' divides these two regions. The region above the barrier describes the state of the *shell* buffer (and ACL2's logical world). The region below the barrier is the ``to do'' list.

    We usually start a proof project by typing the key lemmas, and main goal into the to do list. Definitions are here just regarded as theorems to prove (i.e., the measure conjectures). Then we follow ``The Method.''

    (1) Think about the proof of the first theorem in the to do list. Structure the proof either as an induction followed by simplification or just simplification. Have the necessary lemmas been proved? That is, are the necessary lemmas in the done list already? If so, proceed to Step 2. Otherwise, add the necessary lemmas at the front of the to do list and repeat Step 1.

    (2) Call the theorem prover on the first theorem in the to do list and let the output stream into the *shell* buffer. Abort the proof if it runs more than a few seconds.

    (3) If the theorem prover succeeded, advance the barrier past the successful command and go to Step 1.

    (4) Otherwise, inspect the failed proof attempt, starting from the beginning, not the end. Basically you should look for the first place the proof attempt deviates from your imagined proof. If your imagined proof was inductive, inspect the induction scheme used by ACL2. If that is ok, then find the first subsequent subgoal that is stable under simplification and think about why it was not proved by the simplifier. If your imagined proof was not inductive, then think about the first subgoal stable under simplification, as above. Modify the script appropriately. It usually means adding lemmas to the to do list, just in front of the theorem just tried. It could mean adding hints to the current theorem. In any case, after the modifications go to Step 1.

    We do not seriously suggest that this or any rotely applied algorithm will let you drive ACL2 to difficult proofs. Indeed, to remind you of this we call this ``The Method'' rather than ``the method.'' That is, we are aware of the somewhat pretentious nature of any such advice. But these remarks have helped many users approach ACL2 in a constructive and disciplined way.

    We say much more about The Method in the ACL2 book. See the home page. Also see set-gag-mode for a discussion of a way for ACL2 to help you to use The Method. And again, see introduction-to-the-theorem-prover for a more detailed tutorial.

    Learning to read failed proofs is a useful skill. There are several kinds of ``checkpoints'' in a proof: (1) a formula to which induction is being (or would be) applied, (2) the first formula stable under simplification, (3) a formula that is possibly generalized, either by cross-fertilizing with and throwing away an equivalence hypothesis or by explicit generalization of a term with a new variable.

    At the induction checkpoint, confirm that you believe the formula being proved is a theorem and that it is appropriately strong for an inductive proof. Read the selected induction scheme and make sure it agrees with your idea of how the proof should go.

    At the post-simplification checkpoint, which is probably the most commonly seen, consider whether there are additional rewrite rules you could prove to make the formula simplify still further. Look for compositions of function symbols you could rewrite. Look for contradictions among hypotheses and prove the appropriate implications: for example, the checkpoint might contain the two hypotheses (P (F A)) and (NOT (Q (G (F A)))) and you might realize that (implies (p x) (q (g x))) is a theorem. Look for signs that your existing rules did not apply, e.g., for terms that should have been rewritten, and figure out why they were not. Possible causes include that they do not exactly match your old rules, that your old rules have hypotheses that cannot be relieved here -- perhaps because some other rules are missing, or perhaps your old rules are disabled. If you cannot find any further simplifications to make in the formula, ask yourself whether it is valid. If so, sketch a proof. Perhaps the proof is by appeal to a combination of lemmas you should now prove?

    At the two generalization checkpoints --- where hypotheses are discarded or terms are replaced by variables --- ask yourself whether the result is a theorem. It often is not. Think about rewrite rules that would prove the formula. These are often restricted versions of the overly-general formulas created by the system's heuristics.

    See proof-tree for a discussion of a tool to help you navigate through ACL2 proofs.




    acl2-sources/doc/HTML/THE.html0000664002132200015000000000412112222333524015366 0ustar kaufmannacl2 THE.html -- ACL2 Version 6.3

    THE

    run-time type check
    Major Section:  ACL2-BUILT-INS
    

    (The typ val) checks that val satisfies the type specification typ (see type-spec). An error is caused if the check fails, and otherwise, val is the value of this expression. Here are some examples.

    (the integer 3)       ; returns 3
    (the (integer 0 6) 3) ; returns 3
    (the (integer 0 6) 7) ; causes an error (see below for exception)
    
    See type-spec for a discussion of the legal type specifications.

    There is an exception to the rule that failure of the type-check causes an error: there is no error when guard-checking has been turned off with :set-guard-checking :NONE or (with-guard-checking :NONE ...). See set-guard-checking and see with-guard-checking.

    The following remark is for those who verify guards for their functions (see guard and see verify-guards). We remark that a call of (the TYPE EXPR) in the body of a function definition generates a guard proof obligation that the type, TYPE, holds for the value of the expression, EXPR. Consider the following example.

    (defun f (x)
      (declare (xargs :guard (p1 x)))
      (if (p2 x)
          (the integer x)
        17))
    
    The guard proof obligation generated for the THE expression above is as follows.
    (implies (and (p1 x) (p2 x))
             (let ((var x)) (integerp var)))
    

    THE is defined in Common Lisp. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/THEORIES-AND-PRIMITIVES.html0000664002132200015000000001057012222333531020264 0ustar kaufmannacl2 THEORIES-AND-PRIMITIVES.html -- ACL2 Version 6.3

    THEORIES-AND-PRIMITIVES

    warnings from disabling certain built-in functions
    Major Section:  THEORIES
    

    When you disable the definition or executable-counterpart of a built-in function, you may see a warning, for example as follows.

      ACL2 !>(in-theory (disable mv-nth))
    
      ACL2 Warning [Theory] in ( IN-THEORY (DISABLE ...)):  Although the
      theory expression (DISABLE MV-NTH) disables the :DEFINITION rule for
      MV-NTH, some expansions involving this function may still occur.  See
      :DOC theories-and-primitives.
    

    This warning can be eliminated by turning off all theory warnings (see set-inhibit-warnings) or simply by evaluating the following form.

      (assign verbose-theory-warning nil)
    
    But before you eliminate such warnings, you may wish to read the following to understand their significance.

    First consider the following example, evaluated after the in-theory event displayed above.

      ACL2 !>(thm (equal (mv-nth 2 (list a b c d e)) c))
    
      Q.E.D.
    
      Summary
      Form:  ( THM ...)
      Rules: ((:DEFINITION MV-NTH)
              (:FAKE-RUNE-FOR-TYPE-SET NIL))
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
      Prover steps counted:  19
    
      Proof succeeded.
      ACL2 !>
    

    Note that even though the definition of mv-nth had been disabled, nevertheless its definition rule was used in proving this theorem. It is as though mv-nth had not been been disabled after all! The warning is intended to indicate that expansion of mv-nth calls may be made by the theorem prover even when mv-nth is disabled. Indeed, the prover has special-purpose code for simplifying certain mv-nth calls.

    A similar issue can arise for executable-counterpart rules, as the following log illustrates.

      ACL2 !>(in-theory (disable (:executable-counterpart symbolp)))
    
      ACL2 Warning [Theory] in ( IN-THEORY (DISABLE ...)):  Although the
      theory expression (DISABLE (:EXECUTABLE-COUNTERPART SYMBOLP)) disables
      the :EXECUTABLE-COUNTERPART rule for SYMBOLP, some calls involving
      this function may still be made.  See :DOC theories-and-primitives.
    
    
      Summary
      Form:  ( IN-THEORY (DISABLE ...))
      Rules: NIL
      Warnings:  Theory
      Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
       2921
      ACL2 !>(thm (symbolp 'a))
    
      Q.E.D.
    
      Summary
      Form:  ( THM ...)
      Rules: NIL
      Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
      Proof succeeded.
      ACL2 !>
    

    In general, ACL2 warns when in-theory events or hints leave you in a theory where a rule for a built-in function is disabled but may be applied in some cases nonetheless, because of special-purpose prover code for handling calls of that function. The built-in function symbols with such definition rules or executable-counterpart rules are those in the following two lists, respectively.

      ACL2 !>*definition-minimal-theory*
      (MV-NTH IFF NOT
              IMPLIES EQ ATOM EQL = /= NULL ENDP ZEROP
              SYNP PLUSP MINUSP LISTP RETURN-LAST
              MV-LIST THE-CHECK WORMHOLE-EVAL
              FORCE CASE-SPLIT DOUBLE-REWRITE)
      ACL2 !>*built-in-executable-counterparts*
      (ACL2-NUMBERP BINARY-* BINARY-+ UNARY-- UNARY-/
                    < CAR CDR CHAR-CODE CHARACTERP CODE-CHAR
                    COMPLEX COMPLEX-RATIONALP COERCE
                    CONS CONSP DENOMINATOR EQUAL IF IMAGPART
                    INTEGERP INTERN-IN-PACKAGE-OF-SYMBOL
                    NUMERATOR PKG-WITNESS PKG-IMPORTS
                    RATIONALP REALPART STRINGP SYMBOL-NAME
                    SYMBOL-PACKAGE-NAME SYMBOLP NOT)
      ACL2 !>
    




    acl2-sources/doc/HTML/THEORIES.html0000664002132200015000000003234712222333531016201 0ustar kaufmannacl2 THEORIES.html -- ACL2 Version 6.3

    THEORIES

    sets of runes to enable/disable in concert
    Major Section:  ACL2 Documentation
    

    Example: '((:definition app) ; or (:d app)
               (:executable-counterpart app)
               (:i app)
               rv
               (rv)
               assoc-of-app)
    
    See:

    Some Related Topics

    A theory is a list of ``runic designators'' as described below. Each runic designator denotes a set of ``runes'' (see rune) and by unioning together the runes denoted by each member of a theory we define the set of runes corresponding to a theory. Theories are used to control which rules are ``enabled,'' i.e., available for automatic application by the theorem prover. There is always a ``current'' theory. A rule is enabled precisely if its rune is an element of the set of runes corresponding to the current theory. At the top-level, the current theory is the theory selected by the most recent in-theory event, extended with the rule names introduced since then. Inside the theorem prover, the :in-theory hint (see hints) can be used to select a particular theory as current during the proof attempt for a particular goal.

    Theories are generally constructed by ``theory expressions.'' Formally, a theory expression is any term, containing at most the single free variable world, that when evaluated with world bound to the current ACL2 world (see world) produces a theory. ACL2 provides various functions for the convenient construction and manipulation of theories. These are called ``theory functions''(see theory-functions). For example, the theory function union-theories takes two theories and produces their union. The theory function universal-theory returns the theory containing all known rule names as of the introduction of a given logical name. But a theory expression can contain constants, e.g.,

    '(len (len) (:rewrite car-cons) car-cdr-elim)
    
    and user-defined functions. The only important criterion is that a theory expression mention no variable freely except world and evaluate to a theory.

    More often than not, theory expressions typed by the user do not mention the variable world. This is because user-typed theory expressions are generally composed of applications of ACL2's theory functions. These ``functions'' are actually macros that expand into terms in which world is used freely and appropriately. Thus, the technical definition of ``theory expression'' should not mislead you into thinking that interestng theory expressions must mention world; they probably do and you just didn't know it!

    One aspect of this arrangement is that theory expressions cannot generally be evaluated at the top-level of ACL2, because world is not bound. To see the value of a theory expression, expr, at the top-level, type

    ACL2 !>(LET ((WORLD (W STATE))) expr).
    
    However, because the built-in theories are quite long, you may be sorry you printed the value of a theory expression!

    A theory is a true list of runic designators and to each theory there corresponds a set of runes, obtained by unioning together the sets of runes denoted by each runic designator. For example, the theory constant

       '(len (len) (:e nth) (:rewrite car-cons) car-cdr-elim)
    
    corresponds to the set of runes
       {(:definition len)
        (:induction len)
        (:executable-counterpart len)
        (:executable-counterpart nth)
        (:elim car-cdr-elim)
        (:rewrite car-cons)} .
    
    Observe that the theory contains five elements but its runic correspondent contains six. That is because runic designators can denote sets of several runes, as is the case for the first designator, len. If the above theory were selected as current then the six rules named in its runic counterpart would be enabled and all other rules would be disabled.

    We now precisely define the runic designators and the set of runes denoted by each. When we refer below to the ``macro-aliases dereference of'' a symbol, symb, we mean the (function) symbol corresponding symb in the macro-aliases-table if there is such a symbol, else symb itself; see macro-aliases-table. For example, the macro-aliases dereference of append is binary-append, and the macro-aliases dereference of nth is nth.

    o A rune is a runic designator and denotes the singleton set containing that rune.

    o Suppose that symb is a symbol and symb' is the macro-aliases dereference of symb, where symb' is a function symbol introduced with a defun (or defuns) event. Then symb is a runic designator and denotes the set containing the runes (:definition symb') and (:induction symb'), omitting the latter if no such induction rune exists (presumably because the definition of symb' is not singly recursive).

    o Suppose that symb is a symbol and symb' is the macro-aliases dereference of symb, where symb' is a function symbol introduced with a defun (or defuns) event. Then (symb) is a runic designator and denotes the singleton set containing the rune (:executable-counterpart symb').

    o If symb is the name of a defthm (or defaxiom) event that introduced at least one rule, then symb is a runic designator and denotes the set of the names of all rules introduced by the named event.

    o If str is the string naming some defpkg event and symb is the symbol returned by (intern str "ACL2"), then symb is a runic designator and denotes the singleton set containing (:rewrite symb), which is the name of the rule stating the conditions under which the symbol-package-name of (intern x str) is str.

    o If symb is the name of a deftheory event, then symb is a runic designator and denotes the runic theory corresponding to symb.

    o Finally, suppose that symb is a symbol and symb' is the macro-aliases dereference of symb. Then (:KWD symb . rest) is a runic designator if (:KWD' symb' . rest) is a rune, where :KWD is one of :d, :e, :i, or :t, and correspondingly :KWD' is :definition, :executable-counterpart, :induction, or :type-prescription, respectively. In this case, (:KWD symb . rest) denotes the runic theory corresponding to the rune (:KWD' symb' . rest).

    Note that including a function name, e.g., len, in the current theory enables that function but does not enable the executable counterpart. Similarly, including (len) or (:e len) enables the executable counterpart but not the symbolic definition. And including the name of a proved lemma enables all of the rules added by the event. Of course, one can include explicitly the runes naming the rules in question and so can avoid entirely the use of non-runic elements in theories.

    Because a rune is a runic designator denoting the set containing that rune, a list of runes is a theory and denotes itself. We call such theories ``runic theories.'' To every theory there corresponds a runic theory obtained by unioning together the sets denoted by each designator in the theory. When a theory is selected as ``current'' it is actually its runic correspondent that is effectively used. That is, a rune is enabled iff it is a member of the runic correspondent of the current theory. The value of a theory defined with deftheory is the runic correspondent of the theory computed by the defining theory expression. The theory manipulation functions, e.g., union-theories, actually convert their theory arguments to their runic correspondents before performing the required set operation. The manipulation functions always return runic theories. Thus, it is sometimes convenient to think of (non-runic) theories as merely abbreviations for their runic correspondents, abbreviations which are ``expanded'' at the first opportunity by theory manipulation functions and the ``theory consumer'' functions such as in-theory and deftheory.




    acl2-sources/doc/HTML/THEORY-FUNCTIONS.html0000664002132200015000000000753312222333531017336 0ustar kaufmannacl2 THEORY-FUNCTIONS.html -- ACL2 Version 6.3

    THEORY-FUNCTIONS

    functions for obtaining or producing theories
    Major Section:  THEORIES
    

    Example Calls of Theory Functions:
    (universal-theory :here)
    (union-theories th1 th2)
    (set-difference-theories th1 th2)
    
    The theory functions are documented individually:

    Some Related Topics

    The functions (actually, macros) mentioned above are convenient ways to produce theories. (See theories.) Some, like universal-theory, take a logical name (see logical-name) as an argument and return the relevant theory as of the time that name was introduced. Others, like union-theories, take two theories and produce a new one. See redundant-events for a caution about the use of logical names in theory expressions.

    Theory expressions are generally composed of applications of theory functions. Formally, theory expressions are expressions that involve, at most, the free variable world and that when evaluated with world bound to the current ACL2 world (see world) return theories. The ``theory functions'' are actually macros that expand into forms that involve the free variable world. Thus, for example (universal-theory :here) actually expands to (universal-theory-fn :here world) and when that form is evaluated with world bound to the current ACL2 world, universal-theory-fn scans the ACL2 property lists and computes the current universal theory. Because the theory functions all implicitly use world, the variable does not generally appear in anything the user types.




    acl2-sources/doc/HTML/THEORY-INVARIANT.html0000664002132200015000000001667612222333517017335 0ustar kaufmannacl2 THEORY-INVARIANT.html -- ACL2 Version 6.3

    THEORY-INVARIANT

    user-specified invariants on theories
    Major Section:  EVENTS
    

    Examples:
    (theory-invariant (not (and (active-runep '(:rewrite left-to-right))
                                (active-runep '(:rewrite right-to-left))))
                      :key my-invariant
                      :error nil)
    
    ; Equivalent to the above:
    (theory-invariant (incompatible (:rewrite left-to-right)
                                    (:rewrite right-to-left))
                      :key my-invariant
                      :error nil)
    
    General Form:
    (theory-invariant term &key key error)
    
    where:

    o term is a term that uses no variables other than ens and state;

    o key is an arbitrary ``name'' for this invariant (if omitted, an integer is generated and used); and

    o :error specifies the action to be taken when an invariant is violated -- either nil if a warning is to be printed, else t (the default) if an error is to be caused.

    Theory-invariant is an event that adds to or modifies the table of user-supplied theory invariants that are checked each time a theory expression is evaluated.

    The theory invariant mechanism is provided via a table (see table) named theory-invariant-table. In fact, the theory-invariant ``event'' is just a macro that expands into a use of the table event. More general access to the theory-invariant table is provided by table itself. For example, the table can be inspected or cleared with table; you can clear an individual theory invariant by setting the invariant to t, or eliminate all theory invariants with the command (table theory-invariant-table nil nil :clear).

    Theory-invariant-table maps arbitrary keys to records containing terms that mention, at most, the variables ens and state. Every time an alleged theory expression is evaluated, e.g., in the in-theory event or :in-theory hint, each of the terms in theory-invariant-table is evaluated with ens bound to a so-called ``enabled structure'' obtained from the theory expression and state bound to the current ACL2 state (see state). Users generally need not know about the enabled structure, other than that it can be accessed using the macros active-runep and incompatible; see active-runep and see incompatible. If the result is nil, a message is printed and an error occurs (except, only a warning occurs if :error nil is specified). Thus, the table can be thought of as a list of conjuncts. Each term in the table has a ``name,'' which is just the key under which the term is stored. When a theory violates the restrictions specified by some term, both the name and the term are printed. By calling theory-invariant with a new term but the same name, you can overwrite that conjunct of the theory invariant; but see the Local Redefinition Caveat at the end of this note. You may want to avoid using explicit names, since otherwise the subsequent inclusion of another book that defines a theory invariant with the same name will override your theory invariant.

    Theory invariants are particularly useful in the context of large rule sets intended for re-use. Such sets often contain conflicting rules, e.g., rules that are to be enabled when certain function symbols are disabled, rules that rewrite in opposite directions and thus loop if simultaneously enabled, groups of rules which should be enabled in concert, etc. The developer of such rule sets understands these restrictions and probably documents them. The theory invariant mechanism allows the developer to codify his restrictions so that the user is alerted when they are violated.

    Since theory invariants are arbitrary terms, macros may be used to express commonly used restrictions. For example, executing the event

    (theory-invariant (incompatible (:rewrite left-to-right)
                                    (:rewrite right-to-left)))
    
    would subsequently cause an error any time the current theory contained both of the two runes shown. Of course, incompatible is just defined as a macro. Its definition may be inspected with :pe incompatible.

    In order for a theory-invariant event to be accepted, the proposed theory invariant must be satisfied by the current theory (see current-theory). The value returned upon successful execution of the event is the key (whether user-supplied or generated).

    Local Redefinition Caveat. Care needs to be taken when redefining a theory invariant in a local context. Consider the following example.

    (theory-invariant
     (active-runep '(:definition binary-append))
     :key app-inv)
    
    (encapsulate
     ()
     (local (theory-invariant t :key app-inv))
     (in-theory (disable binary-append))
     (defthm ...))
    
    The second pass of the encapsulate will fail, because the in-theory event violates the original theory-invariant and the local theory-invariant is skipped in the second pass of the encapsulate. Of course, local theory-invariants in books can cause the analogous problem in the second (include-book) pass of a certify-book. In both cases, though, the theory invariants are only checked at the conclusion of the (include-book or encapsulate) event. Indeed, theory invariants are checked at the end of every event related to theories, including defun, defthm, in-theory, encapsulate, and include-book, except for events executed on behalf of an include-book or the second pass of an encapsulate.




    acl2-sources/doc/HTML/THEORY.html0000664002132200015000000000233612222333531015764 0ustar kaufmannacl2 THEORY.html -- ACL2 Version 6.3

    THEORY

    retrieve named theory
    Major Section:  THEORIES
    

    Example:
    (theory 'ground-zero)
    
    In the example above, the theory returned is the one in force when ACL2 is started up (see ground-zero).

    General Form:
    (theory name)
    
    where name is the name of a previously executed deftheory event (otherwise a hard error occurs). Returns the named theory. See theories.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/THIRD.html0000664002132200015000000000066212222333524015626 0ustar kaufmannacl2 THIRD.html -- ACL2 Version 6.3

    THIRD

    third member of the list
    Major Section:  ACL2-BUILT-INS
    

    See any Common Lisp documentation for details.




    acl2-sources/doc/HTML/THM.html0000664002132200015000000000215012222333522015374 0ustar kaufmannacl2 THM.html -- ACL2 Version 6.3

    THM

    prove a theorem
    Major Section:  OTHER
    

    Example:
    (thm (equal (app (app a b) c)
                (app a (app b c))))
    
    Also see defthm. Unlike defthm, thm does not create an event; it merely causes the theorem prover to attempt a proof.

    General Form:
    (thm term
         :hints        hints
         :otf-flg      otf-flg
         :doc          doc-string)
    
    where term is a term alleged to be a theorem, and hints, otf-flg and doc-string are as described in the corresponding :doc entries. The three keyword arguments above are all optional.




    acl2-sources/doc/HTML/TIDBITS.html0000664002132200015000000001216212222333515016054 0ustar kaufmannacl2 TIDBITS.html -- ACL2 Version 6.3

    TIDBITS

    some basic hints for using ACL2
    Major Section:  ACL2-TUTORIAL
    

    See books for a discussion of books. Briefly, a book is a file whose name ends in ``.lisp'' that contains ACL2 events; see events.

    See history for a list of useful commands. Some examples:

    
      :pbt :here      ; print the current event
      :pbt (:here -3) ; print the last four events
      :u              ; undo the last event
      :pe append      ; print the definition of append
    
    
    See documentation to learn how to print documentation to the terminal. There are also versions of the documentation for Mosaic, Emacs Info, and hardcopy.

    There are quite a few kinds of rules allowed in ACL2 besides :rewrite rules, though we hope that beginners won't usually need to be aware of them. See rule-classes for details. In particular, there is support for congruence rewriting. See rune (``RUle NamE'') for a description of the various kinds of rules in the system. Also see theories for a description of how to build theories of runes, which are often used in hints; see hints.

    A ``programming mode'' is supported; see program, see defun-mode, and see default-defun-mode. It can be useful to prototype functions after executing the command :program, which will cause definitions to be syntaxed-checked only.

    ACL2 supports mutual recursion, though this feature is not tied into the automatic discovery of induction schemas and is often not the best way to proceed when you expect to be reasoning about the functions. See defuns; also see mutual-recursion.

    See ld for discussion of how to load files of events. There are many options to ld, including ones to suppress proofs and to control output.

    The :otf-flg (Onward Thru the Fog FLaG) is a useful feature that Nqthm users have often wished for. It prevents the prover from aborting a proof attempt and inducting on the original conjecture. See otf-flg.

    ACL2 supports redefinition and redundancy in events; see ld-redefinition-action and see redundant-events.

    A proof-tree display feature is available for use with Emacs. This feature provides a view of ACL2 proofs that can be much more useful than reading the stream of characters output by the theorem prover as its ``proof.'' See proof-tree.

    An interactive feature similar to Pc-Nqthm is supported in ACL2. See verify and see proof-checker.

    ACL2 allows you to monitor the use of rewrite rules. See break-rewrite.

    See arrays to read about applicative, fast arrays in ACL2.

    To quit the ACL2 command loop, or (in akcl) to return to the ACL2 command loop after an interrupt, type :q. To continue (resume) after an interrupt (in akcl), type :r. To cause an interrupt (in akcl under Unix (trademark of AT&T)), hit control-C (twice, if inside Emacs). To exit ACL2 altogether, first type :q to exit the ACL2 command loop, and then exit Lisp (by typing (user::bye) in akcl).

    See state to read about the von Neumannesque ACL2 state object that records the ``current state'' of the ACL2 session. Also see @, and see assign, to learn about reading and setting global state variables.

    If you want your own von Neumannesque object, e.g., a structure that can be ``destructively modified'' but which must be used with some syntactic restrictions, see stobj.




    acl2-sources/doc/HTML/TIME$.html0000664002132200015000000001517312222333525015562 0ustar kaufmannacl2 TIME$.html -- ACL2 Version 6.3

    TIME$

    time an evaluation
    Major Section:  ACL2-BUILT-INS
    

    Semantically, (time$ x ...) equals x. However, its evaluation may write timing output to the trace output (which is usually the terminal), as explained further below.

    Examples:
    
    ; Basic examples:
    
    (time$ (foo 3 4))
    (time$ (mini-proveall))
    (defun bar (x) (time$ (f x)))
    
    ; Custom examples, which use a custom timing message rather than a built-in
    ; message from Lisp:
    
    ; Report only if real time is at least 1/2 second (two equivalent forms).
    (time$ (foo) :mintime 1/2)
    (time$ (foo) :real-mintime 1/2)
    
    ; Report only if allocation is at least 1000 bytes (and if the Lisp supports
    ; :minalloc).
    (time$ (foo) :minalloc 1000)
    
    ; Report only if real time is at least 1/2 second and (if the Lisp supports
    ; :minalloc) allocation is at least 931 bytes.
    (time$ (foo) :real-mintime 1/2 :minalloc 931)
    
    ; Print "Hello Moon, Goodbye World" instead of any timing data.
    (time$ (foo)
           :msg "Hello ~s0, ~s1 World."
           :args (list "Moon" "Goodbye"))
    
    ; Print default custom timing message (same as omitting :mintime 0):
    (time$ (foo)
           :mintime 0)
    
    ; Print supplied custom timing message.
    (let ((bar ...))
      (time$ (foo)
             :msg "The execution of ~xf took ~st seconds of real ~
                   time and ~sc seconds of run time (cpu time), and ~
                   allocated ~sa bytes.  In an unrelated note, bar ~
                   currently has the value ~x0.~%"
             :args (list bar)))
    
    General Forms:
    (time$ form)
    (time$ form ; arguments below are optional
           :real-mintime  <rational number of seconds>
           :run-mintime   <rational number of seconds>
           :minalloc      <number of bytes>
           :msg           <fmt string>
           :args          <list of arguments for msg>
           )
    ; Note: :real-mintime can be replaced by :mintime
    
    where form is processed as usual except that the host Common Lisp times its evaluation.

    The simplest form is (time$ x), which will call the time utility in the underlying Lisp, and will print a small default message. If you want to see a message printed by the host Lisp, use (time$ x :mintime nil) instead, which may provide detailed, implementation-specific data such as the amounts of time spent in user and system mode, the gc time, the number of page faults encountered, and so on. Of you can create a custom message, configured using the :msg and :args parameters. Time$ can also be made to report timing information only conditionally: the :real-mintime (or equivalently, :mintime), :run-mintime, and :minalloc arguments can be used to avoid reporting timing information for computations that take a small amount of time (perhaps as might be expected in ordinary cases), but to draw the user's attention to computations that take longer or allocate more memory than expected.

    We next document the keyword arguments in some detail.

    Keyword arguments :real-mintime (or :mintime) and :run-mintime can be used to specify a minimum time threshold for time reporting. That is, no timing information will be printed if the execution of form takes less than the specified number of seconds of real (total) time or run (cpu) time, respectively. Note that rational numbers like 1/2 may be used to specify a fractional amount of seconds. It is an error to specify both :real-mintime and its synonym, :mintime.

    Keyword argument :minalloc is not supported on all Lisps. When it is not supported, it is ignored. But on supported Lisps, :minalloc can be used to specify a minimum memory allocation threshold. If form results in fewer than this many bytes being allocated, then no timing information will be reported.

    Keyword argument :msg, when provided, should be a string accepted by the fmt family of functions (see fmt), and it may refer to the elements of :args by their positions, just as for cw (see cw).

    The following directives allow you to report timing information using the :msg string. The examples at the top of this documentation topic illustrate the use of these directives.

    ~xf -- the form that was executed

    ~sa -- the amount of memory allocated, in bytes (in supported Lisps)

    ~st -- the real time taken, in seconds

    ~sc -- the run time (cpu time) taken, in seconds

    We turn now to an example that illustrates how time$ can be called in function bodies. Consider the following definition of the Fibonacci function, followed by the definition of a function that times k calls of this function.

    (defun fib (n)
      (if (zp n)
          1
        (if (= n 1)
            1
          (+ (fib (- n 1))
             (fib (- n 2))))))
    
    (defun time-fib (k)
      (if (zp k)
          nil
        (prog2$
         (time$ (fib k)
                :mintime 1/2
                :msg "(fib ~x0) took ~st seconds, ~sa bytes allocated.~%"
                :args (list k))
         (time-fib (1- k)))))
    
    The following log shows a sample execution of the function defined just above.
    ACL2 !>(time-fib 36)
    (fib 36) took 3.19 seconds, 1280 bytes allocated.
    (fib 35) took 1.97 seconds, 1280 bytes allocated.
    (fib 34) took 1.21 seconds, 1280 bytes allocated.
    (fib 33) took 0.75 seconds, 1280 bytes allocated.
    NIL
    ACL2 !>
    

    Notes:

    (1) Common Lisp specifies that the time utility prints to ``trace output'', and time$ follows this convention. Thus, if you have opened a trace file (see open-trace-file), then you can expect to find the time$ output there.

    (2) Unless the :msg argument is supplied, an explicit call of time$ in the top-level loop will show that the form being timed is a call of the ACL2 evaluator function ev-rec. This is normal; the curious are invited, at their own risk, to see return-last for an explanation.




    acl2-sources/doc/HTML/TIME-TRACKER-TAU.html0000664002132200015000000000626312222333522017233 0ustar kaufmannacl2 TIME-TRACKER-TAU.html -- ACL2 Version 6.3

    TIME-TRACKER-TAU

    messages about expensive use of the tau-system
    Major Section:  MISCELLANEOUS
    

    This documentation topic explains messages printing by the theorem prover about the tau-system, as follows.

    During a proof you may see a message such as the following.

    TIME-TRACKER-NOTE [:TAU]: Elapsed runtime in tau is 4.95 secs; see
    :DOC time-tracker-tau.
    

    Just below a proof summary you may see a message such as the following.

    TIME-TRACKER-NOTE [:TAU]: For the proof above, the total runtime spent
    in the tau system was 30.01 seconds.  See :DOC time-tracker-tau.
    

    Both of these messages are intended to let you know that certain prover heuristics (see tau-system) may be slowing proofs down more than they are helping. If you are satisfied with the prover's performance, you may ignore these messages or even turn them off by disabling time-tracking entirely (see time-tracker). Otherwise, here are some actions that you can take to solve such a performance problem.

    The simplest solution is to disable the tau-system, in either of the following equivalent ways.

    (in-theory (disable (:executable-counterpart tau-system)))
    (in-theory (disable (tau-system)))
    

    But if you want to leave the tau-system enabled, you could investigate the possibility is that the tau-system is causing an expensive :logic-mode function to be executed. You can diagnose that problem by observing the rewriter -- see dmr -- or by interrupting the system and getting a backtrace (see set-debugger-enable). A solution in that case is to disable the executable-counterpart of that function, for example in either of these equivalent ways.

    (in-theory (disable (:executable-counterpart foo)))
    (in-theory (disable (foo)))
    
    As a result, the tau-system will be weakened, but perhaps only negligibly.

    In either case above, you may prefer to provide :in-theory hints rather than :in-theory events; see hints.

    A more sophisticated solution is to record values of the :logic-mode function in question, so that the tau-system will look up the necessary values rather than calling the function, whether or not the executable-counterpart of that function is enabled. Here is an example of a lemma that can provide such a solution. See tau-system.

    (defthm lemma
      (and (foo 0)
           (foo 17)
           (foo t)
           (not (foo '(a b c))))
      :rule-classes :tau-system)
    




    acl2-sources/doc/HTML/TIME-TRACKER.html0000664002132200015000000003327112222333525016606 0ustar kaufmannacl2 TIME-TRACKER.html -- ACL2 Version 6.3

    TIME-TRACKER

    display time spent during specified evaluation
    Major Section:  PROGRAMMING
    

    The time-tracker macro is a utility for displaying time spent during specified evaluation. In general, the user provides this specification. However, ACL2 itself uses this utility for tracking uses of its tau-system reasoning utility (see time-tracker-tau). We discuss that use as an example before discussing the general form for calls of time-tracker.

    Note that by default, the time being tracked is runtime (cpu time); to switch to realtime (elapsed time), see get-internal-time.

    Remark for ACL2(p) users (see parallelism): time-tracker is merely a no-op in ACL2(p).

    During the development of the tau-system, we were concerned about the possibility that it would slow down proofs without any indication of how one might avoid the problem. We wanted a utility that would alert the user in such situations. However, the tau-system code does not return state, so we could not track time spent in the state. We developed the time-tracker utility to track time and print messages, and we did it in a general way so that others can use it in their own code. Here is an example of such a message that could be printed during a proof.

    TIME-TRACKER-NOTE [:TAU]: Elapsed runtime in tau is 2.58 secs; see
    :DOC time-tracker-tau.
    
    And here is an example of such a message that could be printed at the end of the proof.
    TIME-TRACKER-NOTE [:TAU]: For the proof above, the total time spent
    in the tau system was 20.29 seconds.  See :DOC time-tracker-tau.
    

    The time-tracker utility tracks computation time spent on behalf of a user-specified ``tag''. In the case of the tau-system, we chose the tag, :tau. The first argument of time-tracker is the tag, which in our running example is always :tau. Note that although all arguments of time-tracker are evaluated, the first argument is typically a keyword and the second is always a keyword, and such arguments evaluate to themselves.

    An ACL2 function invoked at the start of a proof includes approximately the following code.

    (progn$
     (time-tracker :tau :end)
     (time-tracker :tau :init
                   :times '(1 2 3 4 5)
                   :interval 5
                   :msg "Elapsed runtime in tau is ~st secs; see :DOC ~
                         time-tracker-tau.~|~%")
     ...)
    

    The first time-tracker call (above) ends any existing time-tracking for tag :tau. One might have expected it be put into code managing the proof summary, but we decided not to rely on that code being executed, say, in case of an interrupt. When a given tag is not already being time-tracked, then :end is a no-op (rather than an error).

    The second time-tracker call (above) initiates time-tracking for the tag, :tau. Moreover, it specifies the effect of the :print? keyword. Consider the following abbreviated definition from the ACL2 source code.

    (defun tau-clausep-lst-rec (clauses ens wrld ans ttree state calist)
      (cond
       ((endp clauses)
        (mv (revappend ans nil) ttree calist))
       (t (mv-let
           (flg1 ttree1 calist)
           (tau-clausep (car clauses) ens wrld state calist)
           (prog2$ (time-tracker :tau :print?)
                   (tau-clausep-lst-rec (cdr clauses) ...))))))
    
    Notice that (time-tracker :tau :print?) is executed immediately after tau-clausep is called. The idea is to check whether the total time elapsed inside the tau-system justifies printing a message to the user. The specification of :times '(1 2 3 4 5) in the :init form above says that a message should be printed after 1 second, after 2 seconds, ..., and after 5 seconds. Thereafter, the specification of :interval 5 in the :init form above says that each time we print, at least 5 additional seconds should have been tracked before (time-tracker :tau :print?) prints again. Finally, the :msg keyword above specifies just what should be printed. If it is omitted, then a reasonable default message is printed (as discussed below), but in this case we wanted to print a custom message. The :msg string above is what is printed using formatted printing (see fmt), where the character #\t is bound to a string giving a decimal representation with two decimal points of the time tracked so far for tag :tau. (As our general description below points out, :msg can also be a ``message'' list rather than a string.)

    But when is time actually tracked for :tau? Consider the following definition from the ACL2 source code.

    (defun tau-clausep-lst (clauses ens wrld ans ttree state calist)
      (prog2$ (time-tracker :tau :start)
              (mv-let
               (clauses ttree calist)
               (tau-clausep-lst-rec clauses ens wrld ans ttree state calist)
               (prog2$ (time-tracker :tau :stop)
                       (mv clauses ttree calist)))))
    
    The two calls of time-tracker above first start, and then stop, time-tracking for the tag, :tau. Thus, time is tracked during evaluation of the call of tau-clausep-lst-rec, which is the function (discussed above) that does the tau-system's work.

    Finally, as noted earlier above, ACL2 may print a time-tracking message for tag :tau at the end of a proof. The ACL2 function print-summary contains essentially the following code.

    (time-tracker :tau :print?
                  :min-time 1
                  :msg "For the proof above, the total runtime ~
                        spent in the tau system was ~st seconds.  ~
                        See :DOC time-tracker-tau.~|~%")
    
    The use of :min-time says that we are to ignore the :times and :interval established by the :init call described above, and instead, print a message if and only if at least 1 second (since 1 is the value of keyword :min-time) has been tracked for tag :tau. Formatted printing (see fmt) is used for the value of :msg, where the character #\t is bound to a decimal string representation of the time in seconds, as described above.

    The example above covers all legal values for the second argument of time-tracker and discusses all the additional legal keyword arguments. We conclude with a precise discussion of all arguments. Note that all arguments are evaluated; thus when we refer to an argument, we are discussing the value of that argument. All times discussed are runtimes, i.e., cpu times, unless that default is changed; see get-internal-time.

    General forms:
    
    (time-tracker t)        ; enable time-tracking (default)
    
    (time-tracker nil)      ; disable time-tracking
    
    (time-tracker tag       ; a symbol other than t or nil
                  option    ; :init, :end, :start, :stop, or :print?
                  ;; keyword arguments:
                  :times    ; non-nil if and only if option is :init
                  :interval ; may only be non-nil with :init option
                  :min-time ; may only be non-nil with :print? option
                  :msg      ; may only be non-nil with :init and :print? options
    

    Time-tracking is enabled by default. If the first argument is t or nil, then no other arguments are permitted and time-tracking is enabled or disabled, respectively. When time-tracking is disabled, nothing below takes place. Thus we assume time-tracking is enabled for the remainder of this discussion. We also assume below that the first argument is neither t nor nil.

    We introduce some basic notions about time-tracking. A given tag, such as :tau in the example above, might or might not currently be ``tracked'': :init causes the specified tag to be tracked, while :end causes the specified tag not to be tracked. If the tag is being tracked, the tag might or might not be ``active'': :start causes the tag to be in an active state, whie :stop causes the tag not to be active. Note that only tracked tags can be in an active or inactive state. For a tag that is being tracked, the ``accumulated time'' is the total time spent in an active state since the time that the tag most recently started being tracked, and the ``checkpoint list'' is a non-empty list of rational numbers specifying when printing may take place, as described below.

    We now consider each legal value for the second argument, or ``option'', for a call of time-tracker on a given tag.

    :Init specifies that the tag is to be tracked. It also establishes defaults for the operation of :print?, as described below, using the :times, :interval, and :msg keywords. Of these three, only :times is required, and its value must be a non-empty list of rational numbers specifying the initial checkpoint list for the tag. It is an error to specify :init if the tag is already being tracked. (So if you don't care whether or not the tag is already being tracked and you want to initiate tracking for that tag, use :end first.)

    :End specifies that if the tag is being tracked, then it should nstop being tracked. If the tag is not being tracked, then :end has no effect.

    :Start specifies that the tag is to be active. It is an error to specify :start if the tag is not being tracked or is already active.

    :Stop specifies that the tag is to stop being active. It is an error to specify :stop if the tag is not being tracked or is not active.

    :Print? specifies that if the tag is being tracked (not necessarily active), then a message should be printed if a suitable condition is met. The nature of that message and that condition depend on the keyword options supplied with :print? as well as those supplied with the :init option that most recently initiated tracking. :Print? has no effect if the tag is not being tracked, except that if certain keyword values are checked if supplied with :print?: :min-time must be a rational number or nil, and :msg must be either a string, a true-list whose car is a string, or nil. The remainder of this documentation describes the :print? option in detail under the assumption that the tag is being tracked: first, giving the conditions under which it causes a message to be printed, and second, explaining what is printed.

    When :print? is supplied a non-nil value of :min-time, that value must be a rational number, in which case a message is printed if the accumulated time for the tag is at least that value. Otherwise a message is printed if the accumulated time is greater than or equal to the car of the checkpoint list for the tag. In that case, the tracking state for the tag is updated in the following two ways. First, the checkpoint list is scanned from the front and as long as the accumulated time is greater than or equal to the car of the remaining checkpoint list, that car is popped off the checkpoint list. Second, if the checkpoint list has been completely emptied and a non-nil :interval was supplied when tracking was most recently initiated with the :init option, then the checkpoint list is set to contain a single element, namely the sum of the accumulated time with that value of :interval.

    Finally, suppose the above criteria are met, so that :print? results in printing of a message. We describe below the message, msg, that is printed. Here is how it is printed (see fmt), where seconds-as-decimal-string is a string denoting the number of seconds of accumulated time for the tag, with two digits after the decimal point.

    (fms "TIME-TRACKER-NOTE [~x0]: ~@1~|"
         (list (cons #0 tag)
               (cons #1 msg)
               (cons #t seconds-as-decimal-string))
         (proofs-co state) state nil)
    
    The value of msg is the value of the :msg keyword supplied with :print?, if non-nil; else, the value of :msg supplied when most recently initialization with the :init option, if non-nil; and otherwise, the string "~st s" (the final ``s'' abbreviating the word ``seconds''). It is convenient to supply :msg as a call (msg str arg-0 arg-1 ... arg-k), where str is a string and each arg-i is the value to be associated with #\i upon formatted printing (as with fmt) of the string str.




    acl2-sources/doc/HTML/TIPS.html0000664002132200015000000006077412222333516015546 0ustar kaufmannacl2 TIPS.html -- ACL2 Version 6.3

    TIPS

    some hints for using the ACL2 prover
    Major Section:  ACL2-TUTORIAL
    

    We present here some tips for using ACL2 effectively. Though this collection is somewhat ad hoc, we try to provide some organization, albeit somewhat artificial: for example, the sections overlap, and no particular order is intended. This material has been adapted by Bill Young from a very similar list for Nqthm that appeared in the conclusion of: ``Interaction with the Boyer-Moore Theorem Prover: A Tutorial Study Using the Arithmetic-Geometric Mean Theorem,'' by Matt Kaufmann and Paolo Pecchiari, CLI Technical Report 100, June, 1995. We also draw from a similar list in Chapter 13 of ``A Computational Logic Handbook'' by R.S. Boyer and J S. Moore (Academic Press, 1988). We'll refer to this as ``ACLH'' below.

    These tips are organized roughly as follows.

    A. ACL2 Basics

    B. Strategies for creating events

    C. Dealing with failed proofs

    D. Performance tips

    E. Miscellaneous tips and knowledge

    F. Some things you DON'T need to know

    ACL2 BASICS

    A1. The ACL2 logic.
    This is a logic of total functions. For example, if A and B are less than or equal to each other, then we need to know something more in order to conclude that they are equal (e.g., that they are numbers). This kind of twist is important in writing definitions; for example, if you expect a function to return a number, you may want to apply the function fix or some variant (e.g., nfix or ifix) in case one of the formals is to be returned as the value.

    ACL2's notion of ordinals is important on occasion in supplying ``measure hints'' for the acceptance of recursive definitions. Be sure that your measure is really an ordinal. Consider the following example, which ACL2 fails to admit (as explained below).

    
      (defun cnt (name a i x)
        (declare (xargs :measure (+ 1 i)))
        (cond ((zp (+ 1 i))
               0)
              ((equal x (aref1 name a i))
               (1+ (cnt name a (1- i) x)))
              (t (cnt name a (1- i) x))))
    
    
    One might think that (+ 1 i) is a reasonable measure, since we know that (+ 1 i) is a positive integer in any recursive call of cnt, and positive integers are ACL2 ordinals (see o-p). However, the ACL2 logic requires that the measure be an ordinal unconditionally, not just under the governing assumptions that lead to recursive calls. An appropriate fix is to apply nfix to (+ 1 i), i.e., to use
    
      (declare (xargs :measure (nfix (+ 1 i))))
    
    
    in order to guarantee that the measure will always be an ordinal (in fact, a positive integer).

    For more about admissibility of recursive definitions, see defun, in particular the discussion of termination.

    A2. Simplification.
    The ACL2 simplifier is basically a rewriter, with some ``linear arithmetic'' thrown in. One needs to understand the notion of conditional rewriting. See rewrite.

    A3. Parsing of rewrite rules.

    ACL2 parses rewrite rules roughly as explained in ACLH, except that it never creates ``unusual'' rule classes. In ACL2, if you want a :linear rule, for example, you must specify :linear in the :rule-classes. See rule-classes, and also see rewrite and see linear.

    A4. Linear arithmetic.
    On this subject, it should suffice to know that the prover can handle truths about + and -, and that linear rules (see above) are somehow ``thrown in the pot'' when the prover is doing such reasoning. Perhaps it's also useful to know that linear rules can have hypotheses, and that conditional rewriting is used to relieve those hypotheses.

    A5. Events.
    Over time, the expert ACL2 user will know some subtleties of its events. For example, in-theory events and hints are important, and they distinguish between a function and its executable counterpart.

    B. STRATEGIES FOR CREATING EVENTS

    In this section, we concentrate on the use of definitions and rewrite rules. There are quite a few kinds of rules allowed in ACL2 besides rewrite rules, though most beginning users probably won't usually need to be aware of them. See rule-classes for details. In particular, there is support for congruence rewriting. Also see rune (``RUle NamE'') for a description of the various kinds of rules in the system.

    B1. Use high-level strategy.
    Decompose theorems into ``manageable'' lemmas (admittedly, experience helps here) that yield the main result ``easily.'' It's important to be able to outline non-trivial proofs by hand (or in your head). In particular, avoid submitting goals to the prover when there's no reason to believe that the goal will be proved and there's no ``sense'' of how an induction argument would apply. It is often a good idea to avoid induction in complicated theorems unless you have a reason to believe that it is appropriate.

    B2. Write elegant definitions.
    Try to write definitions in a reasonably modular style, especially recursive ones. Think of ACL2 as a programming language whose procedures are definitions and lemmas, hence we are really suggesting that one follow good programming style (in order to avoid duplication of ``code,'' for example).

    When possible, complex functions are best written as compositions of simpler functions. The theorem prover generally performs better on primitive recursive functions than on more complicated recursions (such as those using accumulating parameters).

    Avoid large non-recursive definitions which tend to lead to large case explosions. If such definitions are necessary, try to prove all relevant facts about the definitions and then disable them.

    Whenever possible, avoid mutual recursion if you care to prove anything about your functions. The induction heuristics provide essentially no help with reasoning about mutually defined functions. Mutually recursive functions can usually be combined into a single function with a ``flag'' argument. (However, see mutual-recursion-proof-example for a small example of proof involving mutually recursive functions.)

    B3. Look for analogies.
    Sometimes you can easily edit sequences of lemmas into sequences of lemmas about analogous functions.

    B4. Write useful rewrite rules.
    As explained in A3 above, every rewrite rule is a directive to the theorem prover, usually to replace one term by another. The directive generated is determined by the syntax of the defthm submitted. Never submit a rewrite rule unless you have considered its interpretation as a proof directive.

    B4a. Rewrite rules should simplify.
    Try to write rewrite rules whose right-hand sides are in some sense ``simpler than'' (or at worst, are variants of) the left-hand sides. This will help to avoid infinite loops in the rewriter.

    B4b. Avoid needlessly expensive rules.
    Consider a rule whose conclusion's left-hand side (or, the entire conclusion) is a term such as (consp x) that matches many terms encountered by the prover. If in addition the rule has complicated hypotheses, this rule could slow down the prover greatly. Consider switching the conclusion and a complicated hypothesis (negating each) in that case.

    B4c. The ``Knuth-Bendix problem''.
    Be aware that left sides of rewrite rules should match the ``normalized forms'', where ``normalization'' (rewriting) is inside out. Be sure to avoid the use of nonrecursive function symbols on left sides of rewrite rules, except when those function symbols are disabled, because they tend to be expanded away before the rewriter would encounter an instance of the left side of the rule. Also assure that subexpressions on the left hand side of a rule are in simplified form.

    B4d. Avoid proving useless rules.
    Sometimes it's tempting to prove a rewrite rule even before you see how it might find application. If the rule seems clean and important, and not unduly expensive, that's probably fine, especially if it's not too hard to prove. But unless it's either part of the high-level strategy or, on the other hand, intended to get the prover past a particular unproved goal, it may simply waste your time to prove the rule, and then clutter the database of rules if you are successful.

    B4e. State rules as strongly as possible, usually.
    It's usually a good idea to state a rule in the strongest way possible, both by eliminating unnecessary hypotheses and by generalizing subexpressions to variables.

    Advanced users may choose to violate this policy on occasion, for example in order to avoid slowing down the prover by excessive attempted application of the rule. However, it's a good rule of thumb to make the strongest rule possible, not only because it will then apply more often, but also because the rule will often be easier to prove (see also B6 below). New users are sometimes tempted to put in extra hypotheses that have a ``type restriction'' appearance, without realizing that the way ACL2 handles (total) functions generally lets it handle trivial cases easily.

    B4f. Avoid circularity.
    A stack overflow in a proof attempt almost always results from circular rewriting. Use brr to investigate the stack; see break-lemma. Because of the complex heuristics, it is not always easy to define just when a rewrite will cause circularity. See the very good discussion of this topic in ACLH.

    See break-lemma for a trick involving use of the forms brr t and (cw-gstack) for inspecting loops in the rewriter.

    B4g. Remember restrictions on permutative rules.
    Any rule that permutes the variables in its left hand side could cause circularity. For example, the following axiom is automatically supplied by the system:

    
      (defaxiom commutativity-of-+
                (equal (+ x y) (+ y x))).
    
    
    This would obviously lead to dangerous circular rewriting if such ``permutative'' rules were not governed by a further restriction. The restriction is that such rules will not produce a term that is ``lexicographically larger than'' the original term (see loop-stopper). However, this sometimes prevents intended rewrites. See Chapter 13 of ACLH for a discussion of this problem.

    B5. Conditional vs. unconditional rewrite rules.
    It's generally preferable to form unconditional rewrite rules unless there is a danger of case explosion. That is, rather than pairs of rules such as

    
    (implies p
             (equal term1 term2))
    
    and
    
    (implies (not p)
             (equal term1 term3))
    
    
    consider:
    
    (equal term1
           (if p term2 term3))
    
    
    However, sometimes this strategy can lead to case explosions: IF terms introduce cases in ACL2. Use your judgment. (On the subject of IF: COND, CASE, AND, and OR are macros that abbreviate IF forms, and propositional functions such as IMPLIES quickly expand into IF terms.)

    B6. Create elegant theorems.
    Try to formulate lemmas that are as simple and general as possible. For example, sometimes properties about several functions can be ``factored'' into lemmas about one function at a time. Sometimes the elimination of unnecessary hypotheses makes the theorem easier to prove, as does generalizing first by hand.

    B7. Use defaxioms temporarily to explore possibilities.
    When there is a difficult goal that seems to follow immediately (by a :use hint or by rewriting) from some other lemmas, you can create those lemmas as defaxiom events (or, the application of skip-proofs to defthm events) and then double-check that the difficult goal really does follow from them. Then you can go back and try to turn each defaxiom into a defthm. When you do that, it's often useful to disable any additional rewrite rules that you prove in the process, so that the ``difficult goal'' will still be proved from its lemmas when the process is complete.

    Better yet, rather than disabling rewrite rules, use the local mechanism offered by encapsulate to make temporary rules completely local to the problem at hand. See encapsulate and see local.

    B9. Use books.
    Consider using previously certified books, especially for arithmetic reasoning. This cuts down the duplication of effort and starts your specification and proof effort from a richer foundation. See community-books.

    C. DEALING WITH FAILED PROOFS

    C1. Look in proof output for goals that can't be further simplified.
    Use the ``proof-tree'' utility to explore the proof space. However, you don't need to use that tool to use the ``checkpoint'' strategy. The idea is to think of ACL2 as a ``simplifier'' that either proves the theorem or generates some goal to consider. That goal is the first ``checkpoint,'' i.e., the first goal that does not further simplify. Exception: it's also important to look at the induction scheme in a proof by induction, and if induction seems appropriate, then look at the first checkpoint after the induction has begun.

    Consider whether the goal on which you focus is even a theorem. Sometimes you can execute it for particular values to find a counterexample.

    When looking at checkpoints, remember that you are looking for any reason at all to believe the goal is a theorem. So for example, sometimes there may be a contradiction in the hypotheses.

    Don't be afraid to skip the first checkpoint if it doesn't seem very helpful. Also, be willing to look a few lines up or down from the checkpoint if you are stuck, bearing in mind however that this practice can be more distracting than helpful.

    C2. Use the ``break rewrite'' facility.
    Brr and related utilities let you inspect the ``rewrite stack.'' These can be valuable tools in large proof efforts. See break-lemma for an introduction to these tools, and see break-rewrite for more complete information.

    The break facility is especially helpful in showing you why a particular rewrite rule is not being applied.

    C3. Use induction hints when necessary. Of course, if you can define your functions so that they suggest the correct inductions to ACL2, so much the better! But for complicated inductions, induction hints are crucial. See hints for a description of :induct hints.

    C4. Use the ``Proof Checker'' to explore.
    The verify command supplied by ACL2 allows one to explore problem areas ``by hand.'' However, even if you succeed in proving a conjecture with verify, it is useful to prove it without using it, an activity that will often require the discovery of rewrite rules that will be useful in later proofs as well.

    C5. Don't have too much patience.
    Interrupt the prover fairly quickly when simplification isn't succeeding.

    C6. Simplify rewrite rules.
    When it looks difficult to relieve the hypotheses of an existing rewrite rule that ``should'' apply in a given setting, ask yourself if you can eliminate a hypothesis from the existing rewrite rule. If so, it may be easier to prove the new version from the old version (and some additional lemmas), rather than to start from scratch.

    C7. Deal with base cases first.
    Try getting past the base case(s) first in a difficult proof by induction. Usually they're easier than the inductive step(s), and rules developed in proving them can be useful in the inductive step(s) too. Moreover, it's pretty common that mistakes in the statement of a theorem show up in the base case(s) of its proof by induction.

    C8. Use :expand hints. Consider giving :expand hints. These are especially useful when a proof by induction is failing. It's almost always helpful to open up a recursively defined function that is supplying the induction scheme, but sometimes ACL2 is too timid to do so; or perhaps the function in question is disabled.

    D. PERFORMANCE TIPS

    D1. Disable rules.
    There are a number of instances when it is crucial to disable rules, including (often) those named explicitly in :use hints. Also, disable recursively defined functions for which you can prove what seem to be all the relevant properties. The prover can spend significant time ``behind the scenes'' trying to open up recursively defined functions, where the only visible effect is slowness.

    D2. Turn off the ``break rewrite'' facility. Remember to execute :brr nil after you've finished with the ``break rewrite'' utility (see break-rewrite), in order to bring the prover back up to full speed.

    E. MISCELLANEOUS TIPS AND KNOWLEDGE

    E1. Order of application of rewrite rules.
    Keep in mind that the most recent rewrite rules in the history are tried first.

    E2. Relieving hypotheses is not full-blown theorem proving.
    Relieving hypotheses on rewrite rules is done by rewriting and linear arithmetic alone, not by case splitting or by other prover processes ``below'' simplification.

    E3. ``Free variables'' in rewrite rules.
    The set of ``free variables'' of a rewrite rule is defined to contain those variables occurring in the rule that do not occur in the left-hand side of the rule. It's often a good idea to avoid rules containing free variables because they are ``weak,'' in the sense that hypotheses containing such variables can generally only be proved when they are ``obviously'' present in the current context. This weakness suggests that it's important to put the most ``interesting'' (specific) hypotheses about free variables first, so that the right instances are considered. For example, suppose you put a very general hypothesis such as (consp x) first. If the context has several terms around that are known to be consps, then x may be bound to the wrong one of them. For much more information on free variables, see free-variables.

    E4. Obtaining information
    Use :pl foo to inspect rewrite rules whose left hand sides are applications of the function foo. Another approach to seeing which rewrite rules apply is to enter the proof-checker with verify, and use the show-rewrites or sr command.

    E5. Consider esoteric rules with care.
    If you care to see rule-classes and peruse the list of subtopics (which will be listed right there in most versions of this documentation), you'll see that ACL2 supports a wide variety of rules in addition to :rewrite rules. Should you use them? This is a complex question that we are not ready to answer with any generality. Our general advice is to avoid relying on such rules as long as you doubt their utility. More specifically: be careful not to use conditional type prescription rules, as these have been known to bring ACL2 to its knees, unless you are conscious that you are doing so and have reason to believe that they are working well.

    F. SOME THINGS YOU DON'T NEED TO KNOW

    Most generally: you shouldn't usually need to be able to predict too much about ACL2's behavior. You should mainly just need to be able to react to it.

    F1. Induction heuristics.
    Although it is often important to read the part of the prover's output that gives the induction scheme chosen by the prover, it is not necessary to understand how the prover made that choice. (Granted, advanced users may occasionally gain minor insight from such knowledge. But it's truly minor in many cases.) What is important is to be able to tell it an appropriate induction when it doesn't pick the right one (after noticing that it doesn't). See C3 above.

    F2. Heuristics for expanding calls of recursively defined functions.
    As with the previous topic, the important thing isn't to understand these heuristics but, rather, to deal with cases where they don't seem to be working. That amounts to supplying :expand hints for those calls that you want opened up, which aren't. See also C8 above.

    F3. The ``waterfall''.
    As discussed many times already, a good strategy for using ACL2 is to look for checkpoints (goals stable under simplification) when a proof fails, perhaps using the proof-tree facility. Thus, it is reasonable to ignore almost all the prover output, and to avoid pondering the meaning of the other ``processes'' that ACL2 uses besides simplification (such as elimination, cross-fertilization, generalization, and elimination of irrelevance). For example, you don't need to worry about prover output that mentions ``type reasoning'' or ``abbreviations,'' for example.




    acl2-sources/doc/HTML/TOGGLE-PC-MACRO.html0000664002132200015000000000176612222333526017104 0ustar kaufmannacl2 TOGGLE-PC-MACRO.html -- ACL2 Version 6.3

    TOGGLE-PC-MACRO

    change an ordinary macro command to an atomic macro, or vice-versa
    Major Section:  PROOF-CHECKER
    

    Example:
    (toggle-pc-macro pro)
    
    Change pro from an atomic macro command to an ordinary one (or vice-versa, if pro happens to be an ordinary macro command)

    General Form:
    (toggle-pc-macro name &optional new-tp)
    
    If name is an atomic macro command then this turns it into an ordinary one, and vice-versa. However, if new-tp is supplied and not nil, then it should be the new type (the symbol macro or atomic-macro, in any package), or else there is no change.




    acl2-sources/doc/HTML/TOP-LEVEL.html0000664002132200015000000000562012222333522016260 0ustar kaufmannacl2 TOP-LEVEL.html -- ACL2 Version 6.3

    TOP-LEVEL

    evaluate a top-level form as a function body
    Major Section:  OTHER
    

    Some forms, such as calls of with-local-stobj, are illegal when supplied directly to the ACL2 top-level loop. The macro top-level provides a workaround in such cases, by defining a temporary :program-mode function named top-level-fn whose only argument is state and whose body is the form to be evaluated. When the call of top-level returns there is no change to the existing ACL2 logical world. The following edited log illustrates all of the above points.

    ACL2 !>:pbt 0
              0  (EXIT-BOOT-STRAP-MODE)
    ACL2 !>(defstobj st fld)
    
    Summary
    Form:  ( DEFSTOBJ ST ...)
    Rules: NIL
    Time:  0.01 seconds (prove: 0.00, print: 0.00, other: 0.01)
     ST
    ACL2 !>(top-level
            (with-local-stobj
             st
             (mv-let (result st)
                     (let ((st (update-fld 17 st)))
                       (mv (fld st) st))
                     result)))
    17
    ACL2 !>(top-level
            (with-local-stobj
             st
             (mv-let (result st)
                     (let ((st (update-fld 17 st)))
                       (mv (fld st) st))
                     (mv nil result state))))
     17
    ACL2 !>(top-level
            (with-local-stobj
             st
             (mv-let (result st)
                     (let ((st (update-fld 17 st)))
                       (mv (fld st) st))
                     (mv result state))))
    (17 <state>)
    ACL2 !>:pbt 0
              0  (EXIT-BOOT-STRAP-MODE)
       d      1:x(DEFSTOBJ ST FLD)
    ACL2 !>
    

    Each argument of top-level after the first should be a declare form or documentation string, as the list of these extra arguments will be placed before the first argument when forming the definition of top-level-fn.

    Top-level generates a call of ld, so that the value returned is printed in the normal way. The call of top-level itself actually evaluates to (mv erp :invisible state), where erp is t if and only evaluation of the call of top-level-fn caused an error, which normally results in no additional output. (For details about ``caused an error'', see the definition of top-level in the ACL2 source code, and see ld-error-action.)




    acl2-sources/doc/HTML/TRACE$.html0000664002132200015000000006313712222333531015662 0ustar kaufmannacl2 TRACE$.html -- ACL2 Version 6.3

    TRACE$

    trace function evaluations
    Major Section:  TRACE
    

    Examples:
    (trace$ foo bar)     ; trace foo and bar
    (trace$)             ; return current trace info (no new tracing specified)
    (trace$ (foo :entry  ; trace foo, printing first actual parameter upon entry
                 (car arglist)))
    (trace$ (foo :exit   ; trace foo, using fmt to print upon exit
                 (:fmt (msg "Exiting FOO with ~x0"
                            value))))
    (trace$ (foo :native t))
    
    General Forms:
    (trace$ spec1 spec2 ... specn) ; n >= 1
    (trace$)
    
    where the speci are trace specs, as described below.

    Trace$ installs alternate code for the indicated functions that prints information upon entry to, and exit from, calls of the functions. For an alternate tracing utility used for educational purposes in ACL2s (http://acl2s.ccs.neu.edu/acl2s/doc/), see community book books/misc/trace-star.lisp.

    From a logical perspective all trace printing is a fiction. (But see trace! for a way to get around this and modify state.) For a related fiction, see cw. (Trace$) returns the list of currently-active trace specs, while the application of trace$ to at least one argument returns the list of its arguments that are successfully acted on.

    Output from trace$ normally goes to the screen, i.e., to standard-co. But it can be redirected to a file; see open-trace-file.

    See untrace$ for how to undo the effect of trace$. Also see trace for mention of modifications made to raw Lisp trace, which is accessible (as described below) using the :native keyword.

    Note that when trace$ is applied to a function without option :native, that function's declarations and documentation are discarded.

    Next, we introduce tracing with some examples. After that, we provide reference documentation for individual trace options allowed in a trace spec. Note that although our example focuses on user-defined functions, trace$ can also be applied to built-in functions, though perhaps only system hackers should take advantage of this observation.

    We begin by illustrating the simplest sort of trace spec: a function symbol. For example, the form (trace$ foo bar) directs the tracing of functions foo and bar by virtue of the two trace specs foo and bar. We can see tracing in action by first defining:

    (defun f (x)
      (cons x x))
    
    (defun g (x)
      (list (f x) 3))
    

    The following log then illustrates tracing of these two functions. Notice that before guards have been verified, the so-called ``*1*'' functions (sometimes called ``executable counterpart functions'' or ``logic functions'') are called but the corresponding raw Lisp functions are not; but after guard verification of f, the raw Lisp counterpart of f is indeed called. (See guard and see guard-evaluation-examples-log.)

    ACL2 !>(trace$ f g)
     ((F) (G))
    ACL2 !>(g 7)
    1> (ACL2_*1*_ACL2::G 7)
      2> (ACL2_*1*_ACL2::F 7)
      <2 (ACL2_*1*_ACL2::F (7 . 7))
    <1 (ACL2_*1*_ACL2::G ((7 . 7) 3))
    ((7 . 7) 3)
    ACL2 !>(verify-guards f)
    
    Computing the guard conjecture for F....
    
    The guard conjecture for F is trivial to prove.  F is compliant with
    Common Lisp.
    
    Summary
    Form:  ( VERIFY-GUARDS F)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     F
    ACL2 !>(g 7)
    1> (ACL2_*1*_ACL2::G 7)
      2> (ACL2_*1*_ACL2::F 7)
        3> (F 7)
        <3 (F (7 . 7))
      <2 (ACL2_*1*_ACL2::F (7 . 7))
    <1 (ACL2_*1*_ACL2::G ((7 . 7) 3))
    ((7 . 7) 3)
    ACL2 !>
    

    The following example introduces trace specs other than function symbols. Consider the following definition.

    (defun fact (n)
      (declare (xargs :guard (natp n)))
      (if (zp n)
          1
        (* n (fact (1- n)))))
    
    The following log illustrates the use of trace options :cond (condition for entering trace), :entry (what to print on entry), and :exit (what to print on exit). The reason for two calls on argument 4 is that we are seeing such calls for the executable counterpart of fact and also its raw Lisp function.
    ACL2 !>(trace$ (fact :cond (evenp (car arglist))
                         :entry (cons 'factorial-call arglist)
                         :exit (car values)))
     ((FACT :COND (EVENP (CAR ARGLIST))
            :ENTRY (CONS 'FACTORIAL-CALL ARGLIST)
            :EXIT (CAR VALUES)))
    ACL2 !>(fact 4)
    1> (FACTORIAL-CALL 4)
      2> (FACTORIAL-CALL 4)
        3> (FACTORIAL-CALL 2)
          4> (FACTORIAL-CALL 0)
          <4 1
        <3 2
      <2 24
    <1 24
    24
    ACL2 !>
    
    Notice that VALUES above is the list of all values returned, which is a one-element list unless mv return is used, as illustrated in the following example, after defining: (defun two-vals (x) (mv x 7)).
    ACL2 !>(trace$ two-vals)
     ((TWO-VALS))
    ACL2 !>(two-vals 3)
    1> (ACL2_*1*_ACL2::TWO-VALS 3)
    <1 (ACL2_*1*_ACL2::TWO-VALS 3 7)
    (3 7)
    ACL2 !>(verify-guards two-vals)
    
    Computing the guard conjecture for TWO-VALS....
    
    The guard conjecture for TWO-VALS is trivial to prove, given the :executable-
    counterpart of CONS.  TWO-VALS is compliant with Common Lisp.
    
    Summary
    Form:  ( VERIFY-GUARDS TWO-VALS)
    Rules: ((:EXECUTABLE-COUNTERPART CONS))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     TWO-VALS
    ACL2 !>(two-vals 3)
    1> (ACL2_*1*_ACL2::TWO-VALS 3)
      2> (TWO-VALS 3)
      <2 (TWO-VALS 3 7)
    <1 (ACL2_*1*_ACL2::TWO-VALS 3 7)
    (3 7)
    ACL2 !>
    

    We now document all of the options that may appear in a trace spec. A trace spec with options is of the form

    (fn :kwd1 val1 :kwd2 val2 ... :kwdn valn)
    
    and here we document each legal keyword :kwdi and corresponding expected value vali. Note that trace$ is intended primarily for functions defined in the ACL2 command loop (see lp). If you want to trace a function that defined in raw Lisp, then you can use option :native (see below), but then many other trace$ options will not be available to you: all of them except :multiplicity and :native itself will be passed directly to the trace utility of the underlying Common Lisp.

    :COND, :ENTRY, and :EXIT

    Introduction. For each of these three options, the value is a (user-level) term, except that for :entry and :exit the value can be of the form (:fmt u) or (:fmt! u), where u is a user-level term. We skip these two latter cases for now and return to them later. Then the indicated term is evaluated as indicated in the next paragraph, and if the :cond term is omitted or evaluates to non-nil, then the value of the :entry term is printed on entry and the value of the :exit term is printed on exit. By default, where :entry is omitted or is specified as nil, the value printed for :entry is the list obtained by consing the calling function symbol onto the list of actual parameters: in the notation described below, this is (cons TRACED-FN ARGLIST). Similarly, the default for printing at the exit of the function call, i.e. where :exit is omitted or is specified as nil, is (cons TRACED-FN VALUES) where VALUES is the list of values returned as described below.

    In the evaluations of the term described below upon a call of fn, each formal parameter of the definition of fn will be bound to the corresponding actual of the call, the variable ARGLIST will be bound to the list of actuals, and the variable TRACED-FN will be bound to the function being called (either fn or its executable counterpart function; see above). Additionally in the case of :exit, the variable VALUES will be bound to the multiple values returned (thus, a one-element list if mv is not used in the return). Also for :exit, we bind VALUE to the logical value returned, i.e., to the suitable list of values returned in the mv case and otherwise to the single value returned. So in the mv case, VALUE is the same as VALUES, and otherwise VALUE is (car VALUES). Other than these variables and STATE, no other variable may occur in the term, whose value must be a single non-stobj value, unless there is an active trust tag (see defttag).

    Now suppose fn is called. First: If :cond is supplied and the result of evaluating the :cond term is nil, then no tracing is done. Otherwise tracing proceeds as follows. First the :entry form is evaluated, and the result is printed. Then the call of fn is evaluated. Finally the :exit term is evaluated and the result is printed. As indicated above, the default for the :entry term if omitted or explicitly nil is (cons TRACED-FN ARGLIST), and the default for the :exit term if omitted or explicitly nil is (cons TRACED-FN VALUES).

    Note that if the function has a formal named ARGLIST, then ARGLIST will nevertheless refer to the entire list of formals, not the single formal named ARGLIST; similarly for TRACED-FN, and additionally for VALUE and VALUES in the case of :exit.

    As mentioned above, for each of :entry and :exit, a value of nil specifies the default behavior. If you really want a value of nil, use a non-nil form that evaluates to nil, for example (car nil) or 'nil. However, for :cond a value of nil means what it says: do not evaluate the :entry or :exit forms.

    Finally we discuss the case that the :entry or :exit term is of the form (:fmt u) or (:fmt! u). In these cases, the term u is evaluated as described above to produce a value, say msg, but instead of printing msg directly, ACL2 calls fmt1 using the string "~@0" and the alist that binds just character #\0 to msg. The following example illustrates this point, where fact is defined as above. Also see fmt. Note that (msg string . vals) produces a value suitable for a "~@" directive to the fmt family of print functions.

    ACL2 !>(trace$
            (fact
             :entry (:fmt (msg "Tracing ~x0 on ~x1" traced-fn arglist))
             :exit (car values)))
     ((FACT :ENTRY (:FMT (MSG "Tracing ~x0 on ~x1" TRACED-FN ARGLIST))
            :EXIT (CAR VALUES)))
    ACL2 !>(fact 3)
    1> Tracing ACL2_*1*_ACL2::FACT on (3)
      2> Tracing FACT on (3)
        3> Tracing FACT on (2)
          4> Tracing FACT on (1)
            5> Tracing FACT on (0)
            <5 1
          <4 1
        <3 2
      <2 6
    <1 6
    6
    ACL2 !>
    
    If :fmt! is used instead of :fmt, then indentation as is the prefix string, "n> " or "<n ". The following example illustrates the use of :fmt!.
    ACL2 !>(trace$
            (fact
             :entry (:fmt! (msg "Tracing ~x0 on ~x1" traced-fn arglist))
             :exit (:fmt! (msg "From input ~x0: ~x1"
                               (car arglist) (car values)))))
     ((FACT :ENTRY (:FMT! (MSG "Tracing ~x0 on ~x1" TRACED-FN ARGLIST))
            :EXIT (:FMT! (MSG "From input ~x0: ~x1" (CAR ARGLIST)
                              (CAR VALUES)))))
    ACL2 !>(fact 3)
    Tracing ACL2_*1*_ACL2::FACT on (3)
    Tracing FACT on (3)
    Tracing FACT on (2)
    Tracing FACT on (1)
    Tracing FACT on (0)
    From input 0: 1
    From input 1: 1
    From input 2: 2
    From input 3: 6
    From input 3: 6
    6
    ACL2 !>
    
    Here is the same example, with user-managed indentation.
    ACL2 !>(trace$
            (fact
             :entry (:fmt! (msg "~t0Tracing ~x1 on ~x2"
                                (+ 3 (* 2 (@ trace-level)))
                                traced-fn arglist))
             :exit (:fmt! (msg "~t0From input ~x1: ~x2"
                               (1+ (* 2 (@ trace-level)))
                               (car arglist) (car values)))))
     ((FACT :ENTRY (:FMT! (MSG "~t0Tracing ~x1 on ~x2"
                               (+ 3 (* 2 (@ TRACE-LEVEL)))
                               TRACED-FN ARGLIST))
            :EXIT (:FMT! (MSG "~t0From input ~x1: ~x2"
                              (1+ (* 2 (@ TRACE-LEVEL)))
                              (CAR ARGLIST)
                              (CAR VALUES)))))
    ACL2 !>(fact 3)
       Tracing ACL2_*1*_ACL2::FACT on (3)
         Tracing FACT on (3)
           Tracing FACT on (2)
             Tracing FACT on (1)
               Tracing FACT on (0)
               From input 0: 1
             From input 1: 1
           From input 2: 2
         From input 3: 6
       From input 3: 6
    6
    ACL2 !>
    

    ADVANCED OPTIONS (alphabetical list)

    :COMPILE

    The tracing of fn installs a substitute definition of fn that prints trace information. If the :compile option is omitted or has value :same, then the new definition will be compiled if and only if the existing definition is already compiled. Otherwise, the new definition will be compiled exactly when the value of :compile is not nil.

    :DEF, :MULTIPLICITY

    ACL2's trace$ mechanism often needs to know the number of outputs of a traced function, in the sense of mv. If you trace a function that was not defined inside the ACL2 loop (hence you are using the :native option), or if you provide an alternative definition using option :def (see below) and the new definition changes the number of values returned, then a natural number value for :multiplicity informs the trace utility of the number of expected outputs of the function being traced. In the case that :native is supplied, the effect of a non-nil :multiplicity value depends on the host Lisp. In the case of Lisps for which ACL2 uses the built-in Lisp mechanism for returning multiple values (see mv), which are CCL and threaded SBCL as of June, 2010, :multiplicity is not needed and is ignored with :native t. For GCL and Allegro CL, :multiplicity is used to generate a suitable :exit form if the :exit keyword was not already supplied. For the other Lisps, the :multiplicity value is treated essentially as 1 whether it is supplied or not, because we do not know how to pass suitable information based on this value to the host Lisp's built-in tracing mechanism.

    Note that even supplying a :multiplicity option does not change the meaning of the variable values. See the discussion of :native below.

    A useful option can be to supply a definition as the value of :def. (Again, note that if :native is used, then all options other than :multiplicity are passed directly to the underlying Lisp; in particular, :def will have no effect with :native except in the unlikely case that the raw Lisp provides some sort of support for :def.) Note that this definition should be like a defun form, but without the leading defun symbol; and it should define the function symbol being traced, with the same formal parameter list. However, tracing of the so-called ``executable counterpart'' of a function (sometimes referred to as the ``*1* function'', for evaluation in the ACL2 loop; see guards for related discussion) is not sensitive to the :def option; rather, if a function has an executable counterpart then that executable counterpart is traced.

    :EVISC-TUPLE

    The printing described above is, by default, done using the current default trace evisc-tuple, which can be set using set-trace-evisc-tuple (for the shape of this tuple, see evisc-tuple); see set-trace-evisc-tuple. This tuple is based by default on the raw Lisp variables *print-level* and *print-length*, and will hide the ACL2 world and handle stobjs appropriately. You may override this default by supplying an evisc tuple with the :evisc-tuple argument in your trace spec. Be careful to supply a valid evisc-tuple, or you may get a raw Lisp error!

    A special value, :print, is useful if you are doing system hacking that can produce objects that are not valid ACL2 objects, such as raw Lisp arrays or objects in supporting packages not visible in the ACL2 read-eval-print loop. If you supply :evisc-tuple :print, then the printing described above will be done with raw Lisp printing rather than ACL2 printing: specifically, with (format *trace-output* "s%" x), where x is the value to be printed.

    A second special value for :evisc-tuple, :no-print, avoids printing the values of the :entry and :exit forms (or their defaults, if not specified). This option is of use for side effects; for an example see community book books/misc/wet.lisp.

    Note that if :evisc-tuple X is supplied, then the form X will be evaluated before the function body is entered. You can thus pull some tricks to print extra information before the :entry form is evaluated, for example as follows for a factorial function, fact.

    ACL2 !>(trace$ (fact :evisc-tuple
                         (prog2$ (cw "~|**** HERE IS CW ****~|")
                                 nil)))
     ((FACT :EVISC-TUPLE (PROG2$ (CW "~|**** HERE IS CW ****~|")
                                 NIL)))
    ACL2 !>(fact 3)
    **** HERE IS CW ****
    1> (ACL2_*1*_ACL2::FACT 3)
    **** HERE IS CW ****
      2> (ACL2_*1*_ACL2::FACT 2)
    **** HERE IS CW ****
        3> (ACL2_*1*_ACL2::FACT 1)
    **** HERE IS CW ****
          4> (ACL2_*1*_ACL2::FACT 0)
          <4 (ACL2_*1*_ACL2::FACT 1)
        <3 (ACL2_*1*_ACL2::FACT 1)
      <2 (ACL2_*1*_ACL2::FACT 2)
    <1 (ACL2_*1*_ACL2::FACT 6)
    6
    ACL2 !>
    

    :FORMALS

    Normally ACL2 can figure out the formals for a given function. This is always the case for functions defined in the ACL2 command loop and when option :def is supplied. If neither of these cases applies then you can still trace a function (even without using the :native option) by supplying option :notinline :fncall, but you will still need to supply the list of formal parameters. The value of the :formals option should be the list of formals in this case.

    :HIDE

    The default value for this advanced option is t, which causes stobjs and the logical world to be printed as single symbols, along with certain large structures of interest to developers (rewrite contants, enabled structures, and event and command index structures). If however the value nil is supplied, then this default behavior is defeated. In that case, you can still arrange to print the logical world as a symbol and to print stobjs without breaking the trace printing: see set-trace-evisc-tuple for how to do this globally, or similarly use the :evisc-tuple option to trace$ to do this with a single trace spec. Note however that with value nil specified for :hide, such use of an evisc-tuple will not deal properly with local stobjs (see with-local-stobj) or stobjs bound by stobj-let, or with the aforementioned large structures other than the logical world.

    :NATIVE

    If :native is supplied with a non-nil value, then the trace spec is passed to the native Lisp trace (after removing the :native option). A trust tag (see defttag) is required in order to use this option, because no syntactic check is made on the :cond, :entry, or :exit forms -- arbitrary raw Lisp may occur in them!

    Note that by ``native Lisp trace'' we mean the currently installed trace. As discussed briefly elsewhere (see trace), ACL2 has modified that trace to be more useful if the underlying host Lisp is GCL, Allegro CL, or CCL (OpenMCL). If you need the original trace utility supplied for those Lisps, quit the ACL2 loop with :q and call old-trace and old-untrace in raw Lisp where you would otherwise call trace and untrace. Note that the original trace utility supplied with a given Lisp will not hide the ACL2 logical world or give special treatment to stobjs.

    It is important to understand that if :native t is specified, then all other options are interpreted by the native Lisp trace. For example, that trace probably has no understanding of the use of :fmt described above for :entry or :exit. Indeed, the native trace may not even accept any of :cond, :entry or :exit, let alone any of the advanced options! Moreover, if :native t is specified, then even a :multiplicity option does not provide the meaning of the variable values that one might desire. In GCL for example, in the case of an mv return of a function defined only in raw Lisp (not in ACL2), this variable will be bound to a list containing only the first result.

    :NOTINLINE

    By default, a new definition installed by trace$ will include a notinline declaration so that recursive calls will always be traced. To avoid this declaration, supply value nil.

    A special value for :notinline, :fncall, will cause the traced function to call its original definition. Without this special value, the new installed definition for the traced function will include the body of the original definition. This :fncall behavior is the default only in the following cases:

    o for functions whose definitions are built into ACL2;

    o for functions that have been added (using a trust tag, an advanced feature, so most users can probably ignore this case) to either of the state global variables program-fns-with-raw-code or logic-fns-with-raw-code;

    o (`HONS' extension only; see hons-and-memoization) for memoized functions.

    The legal values for :notinline are t (the default for other than the cases displayed above), nil, and :fncall. (Except: For the 'HONS' extension, only :fncall is legal.)

    Remarks.

    (1) If some of the given trace specs have errors, then trace$ will generally print error messages for those but will still process those that do not have errors. The value returned will indicate the trace specs that were processed successfully.

    (2) If you certify or include a book that redundantly defines a function that is currently traced, then tracing behavior may disappear if a compiled definition is installed for the function or its in-the-logic (so-called `*1*') counterpart.

    (3) Some predefined functions are called during tracing. In order to avoid infinite loops, such calls of traced predefined functions will be made using the original predefined functions, not using their code installed by trace$.




    acl2-sources/doc/HTML/TRACE.html0000664002132200015000000000500112222333531015600 0ustar kaufmannacl2 TRACE.html -- ACL2 Version 6.3

    TRACE

    tracing functions in ACL2
    Major Section:  ACL2 Documentation
    

    ACL2 provides a trace utility, trace$, with corresponding reverse operation untrace$. These can be used without any dependence on the underlying Lisp utility, and are the tracing utilities of choice in ACL2; see trace$ and see untrace$.

    However, for advanced users we note that the underlying host Lisp may also provide a trace utility, trace, and corresponding untrace. Moreover, these have been modified in the case that the host Lisp is GCL, Allegro CL, or CCL (OpenMCL), to provide limited support for :entry, :exit, and perhaps :cond keywords, to hide certain large data structures (world, enabled structure, rewrite constant), and to trace executable counterparts. See source files *-trace.lisp. For the above Lisps, you can invoke the original trace and untrace by invoking old-trace and old-untrace, respectively, in raw Lisp rather than in the normal ACL2 loop.

    Some Related Topics




    acl2-sources/doc/HTML/TRACE_bang_.html0000664002132200015000000001576212222333531016745 0ustar kaufmannacl2 TRACE_bang_.html -- ACL2 Version 6.3

    TRACE!

    trace the indicated functions after creating an active trust tag
    Major Section:  TRACE
    

    Example:
    (trace! (fact :native t :entry *foo*))
    
    General Form:
    (trace! spec1 ... specn)
    
    where the fni are suitable arguments to trace$.

    Trace! is a version of trace$ that avoids the need for an already-active trust tag (or ``ttag''; see defttag), as explained below. See trace$ for when a trust tag can be necessary.

    See untrace$ for how to undo the effect of trace!.

    The evaluation of a trace! form causes temporary creation of an active trust tag, :trace!, followed by the corresponding trace$ form. The trust tag will disappear when the call to trace! completes. Even though trace! will remove its temporary ttag, it will still print a ``TTAG NOTE'', which indicates that the session is suspect. See defttag and see ttags-seen for further remarks on this issue.

    Because of the active trust tag, it is possible to do things with trace! that are useful but without logical justification. Below is an example of how to use trace! to cause a function call to change state, even though the function does not take state as a parameter.

    ACL2 !>(defun fact (n)
             (declare (xargs :guard (natp n) :verify-guards nil))
             (if (zp n)
                 1
               (* n (fact (1- n)))))
    
    The admission of FACT is trivial, using the relation O< (which is known
    to be well-founded on the domain recognized by O-P) and the measure
    (ACL2-COUNT N).  We observe that the type of FACT is described by the
    theorem (AND (INTEGERP (FACT N)) (< 0 (FACT N))).  We used the :compound-
    recognizer rule ZP-COMPOUND-RECOGNIZER and primitive type reasoning.
    
    Summary
    Form:  ( DEFUN FACT ...)
    Rules: ((:COMPOUND-RECOGNIZER ZP-COMPOUND-RECOGNIZER)
            (:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 !>(defun update-foo (n value state)
             (declare (xargs :stobjs state :verify-guards nil))
             (assign foo (cons (cons n value) (@ foo))))
    
    Since UPDATE-FOO is non-recursive, its admission is trivial.  We observe
    that the type of UPDATE-FOO is described by the theorem
    (AND (CONSP (UPDATE-FOO N VALUE STATE))
         (TRUE-LISTP (UPDATE-FOO N VALUE STATE))).
    We used primitive type reasoning.
    
    (UPDATE-FOO * * STATE) => (MV * * STATE).
    
    Summary
    Form:  ( DEFUN UPDATE-FOO ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     UPDATE-FOO
    ACL2 !>(trace! (fact :exit (update-foo n value state)))
    
    TTAG NOTE: Adding ttag :TRACE! from the top level loop.
     ((FACT :EXIT (UPDATE-FOO N VALUE STATE)))
    ACL2 !>(assign foo nil)
     NIL
    ACL2 !>(fact 7)
    1> (ACL2_*1*_ACL2::FACT 7)
      2> (ACL2_*1*_ACL2::FACT 6)
        3> (ACL2_*1*_ACL2::FACT 5)
          4> (ACL2_*1*_ACL2::FACT 4)
            5> (ACL2_*1*_ACL2::FACT 3)
              6> (ACL2_*1*_ACL2::FACT 2)
                7> (ACL2_*1*_ACL2::FACT 1)
                  8> (ACL2_*1*_ACL2::FACT 0)
                  <8 NIL
                <7 NIL
              <6 NIL
            <5 NIL
          <4 NIL
        <3 NIL
      <2 NIL
    <1 NIL
    5040
    ACL2 !>(@ foo)
    ((7 . 5040)
     (6 . 720)
     (5 . 120)
     (4 . 24)
     (3 . 6)
     (2 . 2)
     (1 . 1)
     (0 . 1))
    ACL2 !>(verify-guards fact)
    
    Computing the guard conjecture for FACT....
    
    The guard conjecture for FACT is trivial to prove, given the :compound-
    recognizer rules NATP-COMPOUND-RECOGNIZER and ZP-COMPOUND-RECOGNIZER,
    primitive type reasoning and the :type-prescription rule FACT.  FACT
    is compliant with Common Lisp.
    
    Summary
    Form:  ( VERIFY-GUARDS FACT)
    Rules: ((:COMPOUND-RECOGNIZER NATP-COMPOUND-RECOGNIZER)
            (:COMPOUND-RECOGNIZER ZP-COMPOUND-RECOGNIZER)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:TYPE-PRESCRIPTION FACT))
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
     FACT
    ACL2 !>(assign foo nil)
     NIL
    ACL2 !>(fact 7)
    1> (ACL2_*1*_ACL2::FACT 7)
      2> (FACT 7)
        3> (FACT 6)
          4> (FACT 5)
            5> (FACT 4)
              6> (FACT 3)
                7> (FACT 2)
                  8> (FACT 1)
                    9> (FACT 0)
                    <9 NIL
                  <8 NIL
                <7 NIL
              <6 NIL
            <5 NIL
          <4 NIL
        <3 NIL
      <2 NIL
    <1 NIL
    5040
    ACL2 !>(@ foo)
    ((7 . 5040)
     (7 . 5040)
     (6 . 720)
     (5 . 120)
     (4 . 24)
     (3 . 6)
     (2 . 2)
     (1 . 1)
     (0 . 1))
    ACL2 !>(trace! (fact :exit (progn (update-foo n value state)
                                      (cons traced-fn values))))
    
    TTAG NOTE: Adding ttag :TRACE! from the top level loop.
     ((FACT :EXIT (PROGN (UPDATE-FOO N VALUE STATE)
                         (CONS TRACED-FN VALUES))))
    ACL2 !>(assign foo nil)
     NIL
    ACL2 !>(fact 7)
    1> (ACL2_*1*_ACL2::FACT 7)
      2> (FACT 7)
        3> (FACT 6)
          4> (FACT 5)
            5> (FACT 4)
              6> (FACT 3)
                7> (FACT 2)
                  8> (FACT 1)
                    9> (FACT 0)
                    <9 (FACT 1)
                  <8 (FACT 1)
                <7 (FACT 2)
              <6 (FACT 6)
            <5 (FACT 24)
          <4 (FACT 120)
        <3 (FACT 720)
      <2 (FACT 5040)
    <1 (ACL2_*1*_ACL2::FACT 5040)
    5040
    ACL2 !>(@ foo)
    ((7 . 5040)
     (7 . 5040)
     (6 . 720)
     (5 . 120)
     (4 . 24)
     (3 . 6)
     (2 . 2)
     (1 . 1)
     (0 . 1))
    ACL2 !>
    

    Finally, we remark that the use trace! can cause errors in situations where tracing is automatically suspended and re-introduced. This is likely to be a rare occurrence, but consider the following example.

    (trace! (lexorder :native t :multiplicity 1))
    (certify-book "foo" 0 t)
    
    If the certify-book causes compilation, you may see an error such as the following.
    
    ACL2 Error in (CERTIFY-BOOK "foo" ...):  The keyword :NATIVE cannot
    be used in a trace spec unless there is an active trust tag.  The trace
    spec (LEXORDER :NATIVE T :MULTIPLICITY 1) is thus illegal.  Consider
    using trace! instead.  The complete list of keywords that require a
    trust tag for use in a trace spec is: (:NATIVE :DEF :MULTIPLICITY).
    
    
    This error is harmless. The function will appear, when calling (trace$), to remain traced, but in fact there will be no tracing behavior, so you may want to call untrace$ on the function symbol in question.




    acl2-sources/doc/HTML/TRANS.html0000664002132200015000000000341312222333522015636 0ustar kaufmannacl2 TRANS.html -- ACL2 Version 6.3

    TRANS

    print the macroexpansion of a form
    Major Section:  OTHER
    

    Examples:
    :trans (list a b c)
    :trans (caddr x)
    :trans (cond (p q) (r))
    

    This function takes one argument, an alleged term, and translates it, expanding the macros in it completely. Either an error is caused or the formal meaning of the term is printed. We also print the ``output signature'' which indicates how many results are returned and which are single-threaded objects. For example, a term that returns one ordinary object (e.g., an object other than STATE or a user-defined single-threaded object (see defstobj)) has the output signature

    => *
    
    A term that returns the single-threaded object STATE has the output signature
    => STATE
    
    and a term that returns four results might have the output signature
    => (MV $MEM * * STATE)
    
    This signature indicates that the first result is the (user defined) single-threaded object $MEM, that the next two results are ordinary, and that the last result is STATE.

    See trans! for a corresponding command that does not enforce restrictions of single-threaded objects.

    It is sometimes more convenient to use trans1 which is like trans but which only does top-level macroexpansion.

    For more, see term.




    acl2-sources/doc/HTML/TRANS1.html0000664002132200015000000000141412222333522015716 0ustar kaufmannacl2 TRANS1.html -- ACL2 Version 6.3

    TRANS1

    print the one-step macroexpansion of a form
    Major Section:  OTHER
    

    Examples:
    :trans1 (list a b c)
    :trans1 (caddr x)
    :trans1 (cond (p q) (r))
    

    This function takes one argument, an alleged term, and expands the top-level macro in it for one step only. Either an error is caused, which happens when the form is not a call of a macro, or the result is printed. Also see trans, which translates the given form completely.




    acl2-sources/doc/HTML/TRANS_bang_.html0000664002132200015000000000150712222333522016766 0ustar kaufmannacl2 TRANS_bang_.html -- ACL2 Version 6.3

    TRANS!

    print the macroexpansion of a form without single-threadedness concerns
    Major Section:  OTHER
    

    Examples:
    :trans! (list a b c)
    :trans! (append x state)
    

    :Trans! is identical to :trans, except that unlike :trans, :trans! ignores single-threadedness restrictions. Thus, the second form above is legal for :trans!. Also see trans and see trans1.




    acl2-sources/doc/HTML/TRUE-LIST-LISTP.html0000664002132200015000000000136012222333525017172 0ustar kaufmannacl2 TRUE-LIST-LISTP.html -- ACL2 Version 6.3

    TRUE-LIST-LISTP

    recognizer for true (proper) lists of true lists
    Major Section:  ACL2-BUILT-INS
    

    True-list-listp is the function that checks whether its argument is a list that ends in, or equals, nil, and furthermore, all of its elements have that property. Also see true-listp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/TRUE-LISTP.html0000664002132200015000000000116112222333525016420 0ustar kaufmannacl2 TRUE-LISTP.html -- ACL2 Version 6.3

    TRUE-LISTP

    recognizer for proper (null-terminated) lists
    Major Section:  ACL2-BUILT-INS
    

    True-listp is the function that checks whether its argument is a list that ends in, or equals, nil.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/TRUNCATE.html0000664002132200015000000000321412222333525016176 0ustar kaufmannacl2 TRUNCATE.html -- ACL2 Version 6.3

    TRUNCATE

    division returning an integer by truncating toward 0
    Major Section:  ACL2-BUILT-INS
    

    Example Forms:
    ACL2 !>(truncate 14 3)
    4
    ACL2 !>(truncate -14 3)
    -4
    ACL2 !>(truncate 14 -3)
    -4
    ACL2 !>(truncate -14 -3)
    4
    ACL2 !>(truncate -15 -3)
    5
    ACL2 !>(truncate 10/4 3/4)
    3
    
    (Truncate i j) is the result of taking the quotient of i and j and dropping the fraction. For example, the quotient of -14 by 3 is -4 2/3, so dropping the fraction 2/3, we obtain a result for (truncate -14 3) of -4.

    The guard for (truncate i j) requires that i and j are rational (real, in ACL2(r)) numbers and j is non-zero.

    Truncate is a Common Lisp function. However, note that unlike Common Lisp, the ACL2 truncate function returns only a single value, Also see nonnegative-integer-quotient, which is appropriate for integers and may simplify reasoning, unless a suitable arithmetic library is loaded, but be less efficient for evaluation on concrete objects.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/TRUST-TAG.html0000664002132200015000000000062712222333522016305 0ustar kaufmannacl2 TRUST-TAG.html -- ACL2 Version 6.3

    TRUST-TAG

    See defttag.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/TTAGS-SEEN.html0000664002132200015000000000502312222333522016360 0ustar kaufmannacl2 TTAGS-SEEN.html -- ACL2 Version 6.3

    TTAGS-SEEN

    list some declared trust tags (ttags)
    Major Section:  MISCELLANEOUS
    

    General Forms:
    :ttags-seen
    (ttags-seen)
    
    Suppose the output is as follows.
    (T NIL)
    (FOO "/home/bob/bar.lisp"
         "/home/cindy/bar.lisp")
    Warning: This output is minimally trustworthy (see :DOC TTAGS-SEEN).
    
    This output indicates that the current logical world has seen the declaration of trust tag T at the top-level (see defttag) and the declaration of trust tag FOO in the two books included from the listed locations. The warning emphasizes that this command cannot be used to validate the ``purity'' of an ACL2 session, because using a ttag renders enough power to hide from this or any other command the fact that the ttag was ever declared.

    As discussed elsewhere (see defttag), the only reliable way to validate the ``purity'' of a session is to watch for ``TTAG NOTE'' output.

    Another shortcoming of this command is that it only checks the current logical world for ttag declarations. For example, one could execute a defttag event; then use progn! and set-raw-mode to replace system functions with corrupt definitions or to introduce inconsistent axioms in the ground-zero world; and finally, execute :ubt! 1 to remove all evidence of the ttag in the world while leaving in place the corrupt definitions or axioms. The base world is now tainted, meaning we could prove nil or certify a book that proves nil, but the resulting session or book would contain no trace of the ttag that tainted it!

    Despite shortcomings, this command might be useful to system hackers. It also serves to illustrate the inherent flaw in asking a session whether or how it is ``tainted'', justifying the ``TTAG NOTE'' approach (see defttag).




    acl2-sources/doc/HTML/TTREE.html0000664002132200015000000000300312222333522015625 0ustar kaufmannacl2 TTREE.html -- ACL2 Version 6.3

    TTREE

    tag-trees
    Major Section:  MISCELLANEOUS
    

    Many low-level ACL2 functions take and return ``tag trees'' or ``ttrees'' (pronounced ``tee-trees'') which contain various useful bits of information such as the lemmas used, the linearize assumptions made, etc.

    Abstractly a tag-tree represents a list of sets, each member set having a name given by one of the ``tags'' (which are symbols) of the ttree. The elements of the set named tag are all of the objects tagged tag in the tree. You are invited to browse the source code. Definitions of primitives are labeled with the comment ``; Note: Tag-tree primitive''.

    The rewriter, for example, takes a term and a ttree (among other things), and returns a new term, term', and new ttree, ttree'. Term' is equivalent to term (under the current assumptions) and the ttree' is an extension of ttree. If we focus just on the set associated with the tag LEMMA in the ttrees, then the set for ttree' is the extension of that for ttree obtained by unioning into it all the runes used by the rewrite. The set associated with LEMMA can be obtained by (tagged-objects 'LEMMA ttree).




    acl2-sources/doc/HTML/TUTORIAL1-TOWERS-OF-HANOI.html0000664002132200015000000002354012222333515020457 0ustar kaufmannacl2 TUTORIAL1-TOWERS-OF-HANOI.html -- ACL2 Version 6.3

    TUTORIAL1-TOWERS-OF-HANOI

    The Towers of Hanoi Example
    Major Section:  ANNOTATED-ACL2-SCRIPTS
    

    This example was written almost entirely by Bill Young of Computational Logic, Inc.

    We will formalize and prove a small theorem about the famous ``Towers of Hanoi'' problem. This problem is illustrated by the following picture.

    
              |        |        |
              |        |        |
             ---       |        |
            -----      |        |
           -------     |        |
    
              A        B        C
    
    
    We have three pegs -- a, b, and c -- and n disks of different sizes. The disks are all initially on peg a. The goal is to move all disks to peg c while observing the following two rules.

    1. Only one disk may be moved at a time, and it must start and finish the move as the topmost disk on some peg;

    2. A disk can never be placed on top of a smaller disk.

    Let's consider some simple instances of this problem. If n = 1, i.e., only one disk is to be moved, simply move it from a to c. If n = 2, i.e., two disks are to be moved, the following sequence of moves suffices: move from a to b, move from a to c, move from b to c.

    In this doc topic we will show an ACL2 function that generates a suitable list of moves for a tower of n disks. Then we will use ACL2 to prove that the number of moves is (- (expt 2 n) 1). For an ACL2 script that proves the correctness of (a version of) this function, see the community book "misc/hanoi.lisp" in the books directory of your ACL2 sources.

    In general, this problem has a straightforward recursive solution. Suppose that we desire to move n disks from a to c, using b as the intermediate peg. For the basis, we saw above that we can always move a single disk from a to c. Now if we have n disks and assume that we can solve the problem for n-1 disks, we can move n disks as follows. First, move n-1 disks from a to b using c as the intermediate peg; move the single disk from a to c; then move n-1 disks from b to c using a as the intermediate peg.

    In ACL2, we can write a function that will return the sequence of moves. One such function is as follows. Notice that we have two base cases. If (zp n) then n is not a positive integer; we treat that case as if n were 0 and return an empty list of moves. If n is 1, then we return a list containing the single appropriate move. Otherwise, we return the list containing exactly the moves dictated by our recursive analysis above.

    
      (defun move (a b)
        (list 'move a 'to b))
    
      (defun hanoi (a b c n)
        (if (zp n)
            nil
          (if (equal n 1)
              (list (move a c))
            (append (hanoi a c b (1- n))
                    (cons (move a c)
                          (hanoi b a c (1- n)))))))
    
    
    Notice that we give hanoi four arguments: the three pegs, and the number of disks to move. It is necessary to supply the pegs because, in recursive calls, the roles of the pegs differ. In any execution of the algorithm, a given peg will sometimes be the source of a move, sometimes the destination, and sometimes the intermediate peg.

    After submitting these functions to ACL2, we can execute the hanoi function on various specific arguments. For example:

    
      ACL2 !>(hanoi 'a 'b 'c 1)
      ((MOVE A TO C))
    
      ACL2 !>(hanoi 'a 'b 'c 2)
      ((MOVE A TO B)
       (MOVE A TO C)
       (MOVE B TO C))
    
      ACL2 !>(hanoi 'a 'b 'c 3)
      ((MOVE A TO C)
       (MOVE A TO B)
       (MOVE C TO B)
       (MOVE A TO C)
       (MOVE B TO A)
       (MOVE B TO C)
       (MOVE A TO C))
    
    
    From the algorithm it is clear that if it takes m moves to transfer n disks, it will take (m + 1 + m) = 2m + 1 moves for n+1 disks. From some simple calculations, we see that we need the following number of moves in specific cases:
    
       Disks   0   1   2   3   4   5   6   7  ...
       Moves   0   1   3   7  15  31  63  127 ...
    
    
    The pattern is fairly clear. To move n disks requires (2^n - 1) moves. Let's attempt to use ACL2 to prove that fact.

    First of all, how do we state our conjecture? Recall that hanoi returns a list of moves. The length of the list (given by the function len) is the number of moves required. Thus, we can state the following conjecture.

    
      (defthm hanoi-moves-required-first-try
        (equal (len (hanoi a b c n))
               (1- (expt 2 n))))
    
    
    When we submit this to ACL2, the proof attempt fails. Along the way we notice subgoals such as:
    
      Subgoal *1/1'
      (IMPLIES (NOT (< 0 N))
               (EQUAL 0 (+ -1 (EXPT 2 N)))).
    
    

    This tells us that the prover is considering cases that are uninteresting to us, namely, cases in which n might be negative. The only cases that are really of interest are those in which n is a non-negative natural number. Therefore, we revise our theorem as follows:

    
      (defthm hanoi-moves-required
        (implies (and (integerp n)
                      (<= 0 n))    ;; n is at least 0
                 (equal (len (hanoi a b c n))
                        (1- (expt 2 n)))))
    
    
    and submit it to ACL2 again.

    Again the proof fails. Examining the proof script we encounter the following text. (How did we decide to focus on this goal? Some information is provided in ACLH, and the ACL2 documentation on tips may be helpful. But the simplest answer is: this was the first goal suggested by the ``proof-tree'' tool below the start of the proof by induction. See proof-tree.)

    
      Subgoal *1/5''
      (IMPLIES (AND (INTEGERP N)
                    (< 0 N)
                    (NOT (EQUAL N 1))
                    (EQUAL (LEN (HANOI A C B (+ -1 N)))
                           (+ -1 (EXPT 2 (+ -1 N))))
                    (EQUAL (LEN (HANOI B A C (+ -1 N)))
                           (+ -1 (EXPT 2 (+ -1 N)))))
               (EQUAL (LEN (APPEND (HANOI A C B (+ -1 N))
                                   (CONS (LIST 'MOVE A 'TO C)
                                         (HANOI B A C (+ -1 N)))))
                      (+ -1 (* 2 (EXPT 2 (+ -1 N))))))
    
    
    It is difficult to make much sense of such a complicated goal. However, we do notice something interesting. In the conclusion is a term of the following shape.
    
       (LEN (APPEND ... ...))
    
    
    We conjecture that the length of the append of two lists should be the sum of the lengths of the lists. If the prover knew that, it could possibly have simplified this term earlier and made more progress in the proof. Therefore, we need a rewrite rule that will suggest such a simplification to the prover. The appropriate rule is:
    
      (defthm len-append
        (equal (len (append x y))
               (+ (len x) (len y))))
    
    
    We submit this to the prover, which proves it by a straightforward induction. The prover stores this lemma as a rewrite rule and will subsequently (unless we disable the rule) replace terms matching the left hand side of the rule with the appropriate instance of the term on the right hand side.

    We now resubmit our lemma hanoi-moves-required to ACL2. On this attempt, the proof succeeds and we are done.

    One bit of cleaning up is useful. We needed the hypotheses that:

    
      (and (integerp n) (<= 0 n)).
    
    
    This is an awkward way of saying that n is a natural number; natural is not a primitive data type in ACL2. We could define a function naturalp, but it is somewhat more convenient to define a macro as follows:
    
      (defmacro naturalp (x)
        (list 'and (list 'integerp x)
                      (list '<= 0 x)))
    
    
    Subsequently, we can use (naturalp n) wherever we need to note that a quantity is a natural number. See defmacro for more information about ACL2 macros. With this macro, we can reformulate our theorem as follows:
    
      (defthm hanoi-moves-required
        (implies (naturalp n)
                 (equal (len (hanoi a b c n))
                        (1- (expt 2 n))))).
    
    
    Another interesting (but much harder) theorem asserts that the list of moves generated by our hanoi function actually accomplishes the desired goal while following the rules. When you can state and prove that theorem, you'll be a very competent ACL2 user.

    By the way, the name ``Towers of Hanoi'' derives from a legend that a group of Vietnamese monks works day and night to move a stack of 64 gold disks from one diamond peg to another, following the rules set out above. We're told that the world will end when they complete this task. From the theorem above, we know that this requires 18,446,744,073,709,551,615 moves:

    
      ACL2 !>(1- (expt 2 64))
      18446744073709551615
      ACL2 !>
    
    
    We're guessing they won't finish any time soon.




    acl2-sources/doc/HTML/TUTORIAL2-EIGHTS-PROBLEM.html0000664002132200015000000001110112222333515020346 0ustar kaufmannacl2 TUTORIAL2-EIGHTS-PROBLEM.html -- ACL2 Version 6.3

    TUTORIAL2-EIGHTS-PROBLEM

    The Eights Problem Example
    Major Section:  ANNOTATED-ACL2-SCRIPTS
    

    This example was written almost entirely by Bill Young of Computational Logic, Inc.

    This simple example was brought to our attention as one that Paul Jackson has solved using the NuPrl system. The challenge is to prove the theorem:

    
      for all n > 7, there exist naturals i and j such that: n = 3i + 5j.
    
    
    In ACL2, we could phrase this theorem using quantification. However we will start with a constructive approach, i.e., we will show that values of i and j exist by writing a function that will construct such values for given n. Suppose we had a function (split n) that returns an appropriate pair (i . j). Our theorem would be as follows:
    
      (defthm split-splits
        (let ((i (car (split n)))
              (j (cdr (split n))))
          (implies (and (integerp n)
                        (< 7 n))
                   (and (integerp i)
                        (<= 0 i)
                        (integerp j)
                        (<= 0 j)
                        (equal (+ (* 3 i) (* 5 j))
                               n)))))
    
    
    That is, assuming that n is a natural number greater than 7, (split n) returns values i and j that are in the appropriate relation to n.

    Let's look at a few cases:

    
      8 = 3x1 + 5x1;    11 = 3x2 + 5x1;     14 = 3x3 + 5x1;   ...
      9 = 3x3 + 5x0;    12 = 3x4 + 5x0;     15 = 3x5 + 5x0;   ...
     10 = 3x0 + 5x2;    13 = 3x1 + 5x2;     16 = 3x2 + 5x2;   ...
    
    
    Maybe you will have observed a pattern here; any natural number larger than 10 can be obtained by adding some multiple of 3 to 8, 9, or 10. This gives us the clue to constructing a proof. It is clear that we can write split as follows:
    
      (defun bump-i (x)
        ;; Bump the i component of the pair
        ;; (i . j) by 1.
        (cons (1+ (car x)) (cdr x)))
    
      (defun split (n)
        ;; Find a pair (i . j) such that
        ;; n = 3i + 5j.
        (if (or (zp n) (< n 8))
            nil ;; any value is really reasonable here
          (if (equal n 8)
              (cons 1 1)
            (if (equal n 9)
                (cons 3 0)
              (if (equal n 10)
                  (cons 0 2)
                (bump-i (split (- n 3))))))))
    
    
    Notice that we explicitly compute the values of i and j for the cases of 8, 9, and 10, and for the degenerate case when n is not a natural or is less than 8. For all naturals greater than n, we decrement n by 3 and bump the number of 3's (the value of i) by 1. We know that the recursion must terminate because any integer value greater than 10 can eventually be reduced to 8, 9, or 10 by successively subtracting 3.

    Let's try it on some examples:

    
      ACL2 !>(split 28)
      (6 . 2)
    
      ACL2 !>(split 45)
      (15 . 0)
    
      ACL2 !>(split 335)
      (110 . 1)
    
    
    Finally, we submit our theorem split-splits, and the proof succeeds. In this case, the prover is ``smart'' enough to induct according to the pattern indicated by the function split.

    For completeness, we'll note that we can state and prove a quantified version of this theorem. We introduce the notion split-able to mean that appropriate i and j exist for n.

    
      (defun-sk split-able (n)
        (exists (i j)
                (equal n (+ (* 3 i) (* 5 j)))))
    
    
    Then our theorem is given below. Notice that we prove it by observing that our previous function split delivers just such an i and j (as we proved above).
    
      (defthm split-splits2
        (implies (and (integerp n)
                      (< 7 n))
                 (split-able n))
        :hints (("Goal" :use (:instance split-able-suff
                                        (i (car (split n)))
                                        (j (cdr (split n)))))))
    
    
    Unfortunately, understanding the mechanics of the proof requires knowing something about the way defun-sk works. See defun-sk or see Tutorial4-Defun-Sk-Example for more on that subject.




    acl2-sources/doc/HTML/TUTORIAL3-PHONEBOOK-EXAMPLE.html0000664002132200015000000007721712222333515020727 0ustar kaufmannacl2 TUTORIAL3-PHONEBOOK-EXAMPLE.html -- ACL2 Version 6.3

    TUTORIAL3-PHONEBOOK-EXAMPLE

    A Phonebook Specification
    Major Section:  ANNOTATED-ACL2-SCRIPTS
    

    The other tutorial examples are rather small and entirely self contained. The present example is rather more elaborate, and makes use of a feature that really adds great power and versatility to ACL2, namely: the use of previously defined collections of lemmas, in the form of ``books.''

    This example was written almost entirely by Bill Young of Computational Logic, Inc.

    This example is based on one developed by Ricky Butler and Sally Johnson of NASA Langley for the PVS system, and subsequently revised by Judy Crow, et al, at SRI. It is a simple phone book specification. We will not bother to follow their versions closely, but will instead present a style of specification natural for ACL2.

    The idea is to model an electronic phone book with the following properties.

    Our phone book will store the phone numbers of a city.

    It must be possible to retrieve a phone number, given a name.

    It must be possible to add and delete entries.

    Of course, there are numerous ways to construct such a model. A natural approach within the Lisp/ACL2 context is to use ``association lists'' or ``alists.'' Briefly, an alist is a list of pairs (key . value) associating a value with a key. A phone book could be an alist of pairs (name . pnum). To find the phone number associated with a given name, we merely search the alist until we find the appropriate pair. For a large city, such a linear list would not be efficient, but at this point we are interested only in modeling the problem, not in deriving an efficient implementation. We could address that question later by proving our alist model equivalent, in some desired sense, to a more efficient data structure.

    We could build a theory of alists from scratch, or we can use a previously constructed theory (book) of alist definitions and facts. By using an existing book, we build upon the work of others, start our specification and proof effort from a much richer foundation, and hopefully devote more of our time to the problem at hand. Unfortunately, it is not completely simple for the new user to know what books are available and what they contain. We hope later to improve the documentation of the growing collection of community books that are typically downloaded with ACL2; for now, the reader is encouraged to look in the README.html file in the books' top-level directory. For present purposes, the beginning user can simply take our word that a book exists containing useful alist definitions and facts. These definitions and lemmas can be introduced into the current theory using the command:

    
      (include-book "data-structures/alist-defthms" :dir :system)
    
    
    This book has been ``certified,'' which means that the definitions and lemmas have been mechanically checked and stored in a safe manner. (See books and see include-book for details.)

    Including this book makes available a collection of functions including the following:

    
    (ALISTP A)    ; is A an alist (actually a primitive ACL2 function)
    
    (BIND X V A)  ; associate the key X with value V in alist A
    
    (BINDING X A) ; return the value associated with key X in alist A
    
    (BOUND? X A)  ; is key X associated with any value in alist A
    
    (DOMAIN A)    ; return the list of keys bound in alist A
    
    (RANGE A)     ; return the list of values bound to keys in alist A
    
    (REMBIND X A) ; remove the binding of key X in alist A
    
    
    Along with these function definitions, the book also provides a number of proved lemmas that aid in simplifying expressions involving these functions. (See rule-classes for the way in which lemmas are used in simplification and rewriting.) For example,
    
      (defthm bound?-bind
        (equal (bound? x (bind y v a))
               (or (equal x y)
                   (bound? x a))))
    
    
    asserts that x will be bound in (bind y v a) if and only if: either x = y or x was already bound in a. Also,
    
      (defthm binding-bind
        (equal (binding x (bind y v a))
               (if (equal x y)
                   v
                 (binding x a))))
    
    
    asserts that the resulting binding will be v, if x = y, or the binding that x had in a already, if not.

    Thus, the inclusion of this book essentially extends our specification and reasoning capabilities by the addition of new operations and facts about these operations that allow us to build further specifications on a richer and possibly more intuitive foundation.

    However, it must be admitted that the use of a book such as this has two potential limitations:

    the definitions available in a book may not be ideal for your particular problem;

    it is (extremely) likely that some useful facts (especially, rewrite rules) are not available in the book and will have to be proved.

    For example, what is the value of binding when given a key that is not bound in the alist? We can find out by examining the function definition. Look at the definition of the binding function (or any other defined function), using the :pe command:

    
      ACL2 !>:pe binding
         d     33  (INCLUDE-BOOK
                        "/slocal/src/acl2/v1-9/books/public/alist-defthms")
                   
      >V d          (DEFUN BINDING (X A)
                           "The value bound to X in alist A."
                           (DECLARE (XARGS :GUARD (ALISTP A)))
                           (CDR (ASSOC-EQUAL X A)))
    
    

    This tells us that binding was introduced by the given include-book form, is currently disabled in the current theory, and has the definition given by the displayed defun form. We see that binding is actually defined in terms of the primitive assoc-equal function. If we look at the definition of assoc-equal:

    
      ACL2 !>:pe assoc-equal
       V     -489  (DEFUN ASSOC-EQUAL (X ALIST)
                          (DECLARE (XARGS :GUARD (ALISTP ALIST)))
                          (COND ((ENDP ALIST) NIL)
                                ((EQUAL X (CAR (CAR ALIST)))
                                 (CAR ALIST))
                                (T (ASSOC-EQUAL X (CDR ALIST)))))
    
    

    we can see that assoc-equal returns nil upon reaching the end of an unsuccessful search down the alist. So binding returns (cdr nil) in that case, which is nil. Notice that we could also have investigated this question by trying some simple examples.

    
      ACL2 !>(binding 'a nil)
      NIL
    
      ACL2 !>(binding 'a (list (cons 'b 2)))
      NIL
    
    

    These definitions aren't ideal for all purposes. For one thing, there's nothing that keeps us from having nil as a value bound to some key in the alist. Thus, if binding returns nil we don't always know if that is the value associated with the key in the alist, or if that key is not bound. We'll have to keep that ambiguity in mind whenever we use binding in our specification. Suppose instead that we wanted binding to return some error string on unbound keys. Well, then we'd just have to write our own version of binding. But then we'd lose much of the value of using a previously defined book. As with any specification technique, certain tradeoffs are necessary.

    Why not take a look at the definitions of other alist functions and see how they work together to provide the ability to construct and search alists? We'll be using them rather heavily in what follows so it will be good if you understand basically how they work. Simply start up ACL2 and execute the form shown earlier, but substituting our directory name for the top-level ACL2 directory with yours. Alternatively, just

    
      (include-book "data-structures/alist-defthms" :dir :system)
    
    
    Then, you can use :pe to look at function definitions. You'll soon discover that almost all of the definitions are built on definitions of other, more primitive functions, as binding is built on assoc-equal. You can look at those as well, of course, or in many cases visit their documentation.

    The other problem with using a predefined book is that it will seldom be ``sufficiently complete,'' in the sense that the collection of rewrite rules supplied won't be adequate to prove everything we'd like to know about the interactions of the various functions. If it were, there'd be no real reason to know that binding is built on top of assoc-equal, because everything we'd need to know about binding would be nicely expressed in the collection of theorems supplied with the book. However, that's very seldom the case. Developing such a collection of rules is currently more art than science and requires considerable experience. We'll encounter examples later of ``missing'' facts about binding and our other alist functions. So, let's get on with the example.

    Notice that alists are mappings of keys to values; but, there is no notion of a ``type'' associated with the keys or with the values. Our phone book example, however, does have such a notion of types; we map names to phone numbers. We can introduce these ``types'' by explicitly defining them, e.g., names are strings and phone numbers are integers. Alternatively, we can partially define or axiomatize a recognizer for names without giving a full definition. A way to safely introduce such ``constrained'' function symbols in ACL2 is with the encapsulate form. For example, consider the following form.

    
      (encapsulate
        ;; Introduce a recognizer for names and give a ``type'' lemma.
        (((namep *) => *))
        ;;
        (local (defun namep (x)
                 ;; This declare is needed to tell
                 ;; ACL2 that we're aware that the
                 ;; argument x is not used in the body
                 ;; of the function.
                 (declare (ignore x))
                 t))
        ;;
        (defthm namep-booleanp
          (booleanp (namep x))))
    
    

    This encapsulate form introduces the new function namep of one argument and one result and constrains (namep x) to be Boolean, for all inputs x. More generally, an encapsulation establishes an environment in which functions can be defined and theorems and rules added without necessarily introducing those functions, theorems, and rules into the environment outside the encapsulation. To be admissible, all the events in the body of an encapsulate must be admissible. But the effect of an encapsulate is to assume only the non-local events.

    The first ``argument'' to encapsulate, ((namep (x) t)) above, declares the intended signatures of new function symbols that will be ``exported'' from the encapsulation without definition. The local defun of name defines name within the encapsulation always to return t. The defthm event establishes that namep is Boolean. By making the defun local but the defthm non-local this encapsulate constrains the undefined function namep to be Boolean; the admissibility of the encapsulation establishes that there exists a Boolean function (namely the constant function returning t).

    We can subsequently use namep as we use any other Boolean function, with the proviso that we know nothing about it except that it always returns either t or nil. We use namep to ``recognize'' legal keys for our phonebook alist.

    We wish to do something similar to define what it means to be a legal phone number. We submit the following form to ACL2:

    
      (encapsulate
        ;; Introduce a recognizer for phone numbers.
        (((pnump *) => *))
        ;;
        (local (defun pnump (x)
                 (not (equal x nil))))
        ;;
        (defthm pnump-booleanp
          (booleanp (pnump x)))
        ;;
        (defthm nil-not-pnump
          (not (pnump nil)))).
    
    
    This introduces a Boolean-valued recognizer pnump, with the additional proviso that the constant nil is not a pnump. We impose this restriction to guarantee that we'll never bind a name to nil in our phone book and thereby introduce the kind of ambiguity described above regarding the use of binding.

    Now a legal phone book is an alist mapping from nameps to pnumps. We can define this as follows:

    
      (defun name-phonenum-pairp (x)
        ;; Recognizes a pair of (name . pnum).
        (and (consp x)
             (namep (car x))
             (pnump (cdr x))))
    
      (defun phonebookp (l)
        ;; Recognizes a list of such pairs.
        (if (not (consp l))
            (null l)
          (and (name-phonenum-pairp (car l))
               (phonebookp (cdr l)))))
    
    
    Thus, a phone book is really a list of pairs (name . pnum). Notice that we have not assumed that the keys of the phone book are distinct. We'll worry about that question later. (It is not always desirable to insist that the keys of an alist be distinct. But it may be a useful requirement for our specific example.)

    Now we are ready to define some of the functions necessary for our phonebook example. The functions we need are:

    
    (IN-BOOK? NM BK)          ; does NM have a phone number in BK
    
    (FIND-PHONE NM BK)        ; find NM's phone number in phonebook BK
    
    (ADD-PHONE NM PNUM BK)    ; give NM the phone number PNUM in BK
    
    (CHANGE-PHONE NM PNUM BK) ; change NM's phone number to PNUM in BK
    
    (DEL-PHONE NM PNUM)       ; remove NM's phone number from BK
    
    

    Given our underlying theory of alists, it is easy to write these functions. But we must take care to specify appropriate ``boundary'' behavior. Thus, what behavior do we want when, say, we try to change the phone number of a client who is not currently in the book? As usual, there are numerous possibilities; here we'll assume that we return the phone book unchanged if we try anything ``illegal.''

    Possible definitions of our phone book functions are as follows. (Remember, an include-book form such as the ones shown earlier must be executed in order to provide definitions for functions such as bound?.)

    
      (defun in-book? (nm bk)
        (bound? nm bk))
    
      (defun find-phone (nm bk)
        (binding nm bk))
    
      (defun add-phone (nm pnum bk)
        ;; If nm already in-book?, make no change.
        (if (in-book? nm bk)
            bk
          (bind nm pnum bk)))
    
      (defun change-phone (nm pnum bk)
        ;; Make a change only if nm already has a phone number.
        (if (in-book? nm bk)
            (bind nm pnum bk)
          bk))
    
      (defun del-phone (nm bk)
        ;; Remove the binding from bk, if there is one.
        (rembind nm bk))
    
    
    Notice that we don't have to check whether a name is in the book before deleting, because rembind is essentially a no-op if nm is not bound in bk.

    In some sense, this completes our specification. But we can't have any real confidence in its correctness without validating our specification in some way. One way to do so is by proving some properties of our specification. Some candidate properties are:

    1. A name will be in the book after we add it.

    2. We will find the most recently added phone number for a client.

    3. If we change a number, we'll find the change.

    4. Changing and then deleting a number is the same as just deleting.

    5. A name will not be in the book after we delete it.

    Let's formulate some of these properties. The first one, for example, is:

    
      (defthm add-in-book
        (in-book? nm (add-phone nm pnum bk))).
    
    
    You may wonder why we didn't need any hypotheses about the ``types'' of the arguments. In fact, add-in-book is really expressing a property that is true of alists in general, not just of the particular variety of alists we are dealing with. Of course, we could have added some extraneous hypotheses and proved:
    
      (defthm add-in-book
        (implies (and (namep nm)
                      (pnump pnum)
                      (phonebookp bk))
                 (in-book? nm (add-phone nm pnum bk)))),
    
    
    but that would have yielded a weaker and less useful lemma because it would apply to fewer situations. In general, it is best to state lemmas in the most general form possible and to eliminate unnecessary hypotheses whenever possible. The reason for that is simple: lemmas are usually stored as rules and used in later proofs. For a lemma to be used, its hypotheses must be relieved (proved to hold in that instance); extra hypotheses require extra work. So we avoid them whenever possible.

    There is another, more important observation to make about our lemma. Even in its simpler form (without the extraneous hypotheses), the lemma add-in-book may be useless as a rewrite rule. Notice that it is stated in terms of the non-recursive functions in-book? and add-phone. If such functions appear in the left hand side of the conclusion of a lemma, the lemma may not ever be used. Suppose in a later proof, the theorem prover encountered a term of the form:

    
      (in-book? nm (add-phone nm pnum bk)).
    
    
    Since we've already proved add-in-book, you'd expect that this would be immediately reduced to true. However, the theorem prover will often ``expand'' the non-recursive definitions of in-book? and add-phone using their definitions before it attempts rewriting with lemmas. After this expansion, lemma add-in-book won't ``match'' the term and so won't be applied. Look back at the proof script for add-in-proof and you'll notice that at the very end the prover warned you of this potential difficulty when it printed:
    
      Warnings:  Non-rec
      Time:  0.18 seconds (prove: 0.05, print: 0.00, other: 0.13)
      ADD-IN-BOOK
    
    
    The ``Warnings'' line notifies you that there are non-recursive function calls in the left hand side of the conclusion and that this problem might arise. Of course, it may be that you don't ever plan to use the lemma for rewriting or that your intention is to disable these functions. Disabled functions are not expanded and the lemma should apply. However, you should always take note of such warnings and consider an appropriate response. By the way, we noted above that binding is disabled. If it were not, none of the lemmas about binding in the book we included would likely be of much use for exactly the reason we just gave.

    For our current example, let's assume that we're just investigating the properties of our specifications and not concerned about using our lemmas for rewriting. So let's go on. If we really want to avoid the warnings, we can add :rule-classes nil to each defthm event; see rule-classes.

    Property 2 is: we always find the most recently added phone number for a client. Try the following formalization:

    
      (defthm find-add-first-cut
        (equal (find-phone nm (add-phone nm pnum bk))
               pnum))
    
    
    and you'll find that the proof attempt fails. Examining the proof attempt and our function definitions, we see that the lemma is false if nm is already in the book. We can remedy this situation by reformulating our lemma in at least two different ways:
    
      (defthm find-add1
        (implies (not (in-book? nm bk))
                 (equal (find-phone nm (add-phone nm pnum bk))
                        pnum)))
    
      (defthm find-add2
        (equal (find-phone nm (add-phone nm pnum bk))
               (if (in-book? nm bk)
                   (find-phone nm bk)
                   pnum)))
    
    
    For technical reasons, lemmas such as find-add2, i.e., which do not have hypotheses, are usually slightly preferable. This lemma is stored as an ``unconditional'' rewrite rule (i.e., has no hypotheses) and, therefore, will apply more often than find-add1. However, for our current purposes either version is all right.

    Property 3 says: If we change a number, we'll find the change. This is very similar to the previous example. The formalization is as follows.

    
      (defthm find-change
        (equal (find-phone nm (change-phone nm pnum bk))
               (if (in-book? nm bk)
                   pnum
                 (find-phone nm bk))))
    
    
    Property 4 says: changing and then deleting a number is the same as just deleting. We can model this as follows.
    
      (defthm del-change
        (equal (del-phone nm (change-phone nm pnum bk))
               (del-phone nm bk)))
    
    
    Unfortunately, when we try to prove this, we encounter subgoals that seem to be true, but for which the prover is stumped. For example, consider the following goal. (Note: endp holds of lists that are empty.)
    
      Subgoal *1/4
      (IMPLIES (AND (NOT (ENDP BK))
                    (NOT (EQUAL NM (CAAR BK)))
                    (NOT (BOUND? NM (CDR BK)))
                    (BOUND? NM BK))
               (EQUAL (REMBIND NM (BIND NM PNUM BK))
                      (REMBIND NM BK))).
    
    
    Our intuition about rembind and bind tells us that this goal should be true even without the hypotheses. We attempt to prove the following lemma.
    
      (defthm rembind-bind
        (equal (rembind nm (bind nm pnum bk))
               (rembind nm bk)))
    
    
    The prover proves this by induction, and stores it as a rewrite rule. After that, the prover has no difficulty in proving del-change.

    The need to prove lemma rembind-bind illustrates a point we made early in this example: the collection of rewrite rules supplied by a previously certified book will almost never be everything you'll need. It would be nice if we could operate purely in the realm of names, phone numbers, and phone books without ever having to prove any new facts about alists. Unfortunately, we needed a fact about the relation between rembind and bind that wasn't supplied with the alists theory. Hopefully, such omissions will be rare.

    Finally, let's consider our property 5 above: a name will not be in the book after we delete it. We formalize this as follows:

    
      (defthm in-book-del
        (not (in-book? nm (del-phone nm bk))))
    
    
    This proves easily. But notice that it's only true because del-phone (actually rembind) removes all occurrences of a name from the phone book. If it only removed, say, the first one it encountered, we'd need a hypothesis that said that nm occurs at most once in bk. Ah, maybe that's a property you hadn't considered. Maybe you want to ensure that any name occurs at most once in any valid phonebook.

    To complete this example, let's consider adding an invariant to our specification. In particular, suppose we want to assure that no client has more than one associated phone number. One way to ensure this is to require that the domain of the alist is a ``set'' (has no duplicates).

    
      (defun setp (l)
        (if (atom l)
            (null l)
          (and (not (member-equal (car l) (cdr l)))
               (setp (cdr l)))))
    
      (defun valid-phonebookp (bk)
        (and (phonebookp bk)
             (setp (domain bk))))
    
    
    Now, we want to show under what conditions our operations preserve the property of being a valid-phonebookp. The operations in-book? and find-phone don't return a phone book, so we don't really need to worry about them. Since we're really interested in the ``types'' of values preserved by our phonebook functions, let's look at the types of those operations as well.
    
      (defthm in-book-booleanp
        (booleanp (in-book? nm bk)))
    
      (defthm in-book-namep
        (implies (and (phonebookp bk)
                      (in-book? nm bk))
                 (namep nm))
        :hints (("Goal" :in-theory (enable bound?))))
    
      (defthm find-phone-pnump
        (implies (and (phonebookp bk)
                      (in-book? nm bk))
                 (pnump (find-phone nm bk)))
        :hints (("Goal" :in-theory (enable bound? binding))))
    
    
    Note the ``:hints'' on the last two lemmas. Neither of these would prove without these hints, because once again there are some facts about bound? and binding not available in our current context. Now, we could figure out what those facts are and try to prove them. Alternatively, we can enable bound? and binding and hope that by opening up these functions, the conjectures will reduce to versions that the prover does know enough about or can prove by induction. In this case, this strategy works. The hints tell the prover to enable the functions in question when considering the designated goal.

    Below we develop the theorems showing that add-phone, change-phone, and del-phone preserve our proposed invariant. Notice that along the way we have to prove some subsidiary facts, some of which are pretty ugly. It would be a good idea for you to try, say, add-phone-preserves-invariant without introducing the following four lemmas first. See if you can develop the proof and only add these lemmas as you need assistance. Then try change-phone-preserves-invariant and del-phone-preserves-invariant. They will be easier. It is illuminating to think about why del-phone-preserves-invariant does not need any ``type'' hypotheses.

    
      (defthm bind-preserves-phonebookp
        (implies (and (phonebookp bk)
                      (namep nm)
                      (pnump num))
                 (phonebookp (bind nm num bk))))
    
      (defthm member-equal-strip-cars-bind
        (implies (and (not (equal x y))
                      (not (member-equal x (strip-cars a))))
                 (not (member-equal x (strip-cars (bind y z a))))))
    
      (defthm bind-preserves-domain-setp
        (implies (and (alistp bk)
                      (setp (domain bk)))
                 (setp (domain (bind nm num bk))))
        :hints (("Goal" :in-theory (enable domain))))
    
      (defthm phonebookp-alistp
        (implies (phonebookp bk)
                 (alistp bk)))
    
      (defthm ADD-PHONE-PRESERVES-INVARIANT
        (implies (and (valid-phonebookp bk)
                      (namep nm)
                      (pnump num))
                 (valid-phonebookp (add-phone nm num bk)))
        :hints (("Goal" :in-theory (disable domain-bind))))
    
      (defthm CHANGE-PHONE-PRESERVES-INVARIANT
        (implies (and (valid-phonebookp bk)
                      (namep nm)
                      (pnump num))
                 (valid-phonebookp (change-phone nm num bk)))
        :hints (("Goal" :in-theory (disable domain-bind))))
    
      (defthm remove-equal-preserves-setp
        (implies (setp l)
                 (setp (remove-equal x l))))
    
      (defthm rembind-preserves-phonebookp
        (implies (phonebookp bk)
                 (phonebookp (rembind nm bk))))
    
      (defthm DEL-PHONE-PRESERVES-INVARIANT
        (implies (valid-phonebookp bk)
                 (valid-phonebookp (del-phone nm bk))))
    

    As a final test of your understanding, try to formulate and prove an invariant that says that no phone number is assigned to more than one name. The following hints may help.

    1. Define the appropriate invariant. (Hint: remember the function range.)

    2. Do our current definitions of add-phone and change-phone necessarily preserve this property? If not, consider what hypotheses are necessary in order to guarantee that they do preserve this property.

    3. Study the definition of the function range and notice that it is defined in terms of the function strip-cdrs. Understand how this defines the range of an alist.

    4. Formulate the correctness theorems and attempt to prove them. You'll probably benefit from studying the invariant proof above. In particular, you may need some fact about the function strip-cdrs analogous to the lemma member-equal-strip-cars-bind above.

    Below is one solution to this exercise. Don't look at the solution, however, until you've struggled a bit with it. Notice that we didn't actually change the definitions of add-phone and change-phone, but added a hypothesis saying that the number is ``new.'' We could have changed the definitions to check this and return the phonebook unchanged if the number was already in use.

    
      (defun pnums-in-use (bk)
        (range bk))
    
      (defun phonenums-unique (bk)
        (setp (pnums-in-use bk)))
    
      (defun new-pnump (pnum bk)
        (not (member-equal pnum (pnums-in-use bk))))
    
      (defthm member-equal-strip-cdrs-rembind
        (implies (not (member-equal x (strip-cdrs y)))
                 (not (member-equal x (strip-cdrs (rembind z y))))))
    
      (defthm DEL-PHONE-PRESERVES-PHONENUMS-UNIQUE
        (implies (phonenums-unique bk)
                 (phonenums-unique (del-phone nm bk)))
        :hints (("Goal" :in-theory (enable range))))
    
      (defthm strip-cdrs-bind-non-member
        (implies (and (not (bound? x a))
                      (alistp a))
                 (equal (strip-cdrs (bind x y a))
                        (append (strip-cdrs a) (list y))))
        :hints (("Goal" :in-theory (enable bound?))))
    
      (defthm setp-append-list
        (implies (setp l)
                 (equal (setp (append l (list x)))
                        (not (member-equal x l)))))
    
      (defthm ADD-PHONE-PRESERVES-PHONENUMS-UNIQUE
        (implies (and (phonenums-unique bk)
                      (new-pnump pnum bk)
                      (alistp bk))
                 (phonenums-unique (add-phone nm pnum bk)))
        :hints (("Goal" :in-theory (enable range))))
    
      (defthm member-equal-strip-cdrs-bind
        (implies (and (not (member-equal z (strip-cdrs a)))
                      (not (equal z y)))
                 (not (member-equal z (strip-cdrs (bind x y a))))))
    
      (defthm CHANGE-PHONE-PRESERVES-PHONENUMS-UNIQUE
        (implies (and (phonenums-unique bk)
                      (new-pnump pnum bk)
                      (alistp bk))
                 (phonenums-unique (change-phone nm pnum bk)))
        :hints (("Goal" :in-theory (enable range))))
    




    acl2-sources/doc/HTML/TUTORIAL4-DEFUN-SK-EXAMPLE.html0000664002132200015000000001034012222333515020540 0ustar kaufmannacl2 TUTORIAL4-DEFUN-SK-EXAMPLE.html -- ACL2 Version 6.3

    TUTORIAL4-DEFUN-SK-EXAMPLE

    example of quantified notions
    Major Section:  ANNOTATED-ACL2-SCRIPTS
    

    This example illustrates the use of defun-sk and defthm events to reason about quantifiers. See defun-sk. For a more through, systematic beginner's introduction to quantification in ACL2, see quantifier-tutorial.

    Many users prefer to avoid the use of quantifiers, since ACL2 provides only very limited support for reasoning about quantifiers.

    Here is a list of events that proves that if there are arbitrarily large numbers satisfying the disjunction (OR P R), then either there are arbitrarily large numbers satisfying P or there are arbitrarily large numbers satisfying R.

    
    ; Introduce undefined predicates p and r.
    (defstub p (x) t)
    (defstub r (x) t)
    
    ; Define the notion that something bigger than x satisfies p.
    (defun-sk some-bigger-p (x)
      (exists y (and (< x y) (p y))))
    
    ; Define the notion that something bigger than x satisfies r.
    (defun-sk some-bigger-r (x)
      (exists y (and (< x y) (r y))))
    
    ; Define the notion that arbitrarily large x satisfy p.
    (defun-sk arb-lg-p ()
      (forall x (some-bigger-p x)))
    
    ; Define the notion that arbitrarily large x satisfy r.
    (defun-sk arb-lg-r ()
      (forall x (some-bigger-r x)))
    
    ; Define the notion that something bigger than x satisfies p or r.
    (defun-sk some-bigger-p-or-r (x)
      (exists y (and (< x y) (or (p y) (r y)))))
    
    ; Define the notion that arbitrarily large x satisfy p or r.
    (defun-sk arb-lg-p-or-r ()
      (forall x (some-bigger-p-or-r x)))
    
    ; Prove the theorem promised above.  Notice that the functions open
    ; automatically, but that we have to provide help for some rewrite
    ; rules because they have free variables in the hypotheses.  The
    ; ``witness functions'' mentioned below were introduced by DEFUN-SK.
    
    (thm
     (implies (arb-lg-p-or-r)
              (or (arb-lg-p)
                  (arb-lg-r)))
     :hints (("Goal"
              :use
              ((:instance some-bigger-p-suff
                          (x (arb-lg-p-witness))
                          (y (some-bigger-p-or-r-witness
                              (max (arb-lg-p-witness)
                                   (arb-lg-r-witness)))))
               (:instance some-bigger-r-suff
                          (x (arb-lg-r-witness))
                          (y (some-bigger-p-or-r-witness
                              (max (arb-lg-p-witness)
                                   (arb-lg-r-witness)))))
               (:instance arb-lg-p-or-r-necc
                          (x (max (arb-lg-p-witness)
                                  (arb-lg-r-witness))))))))
    
    ; And finally, here's a cute little example.  We have already
    ; defined above the notion (some-bigger-p x), which says that
    ; something bigger than x satisfies p.  Let us introduce a notion
    ; that asserts that there exists both y and z bigger than x which
    ; satisfy p.  On first glance this new notion may appear to be
    ; stronger than the old one, but careful inspection shows that y and
    ; z do not have to be distinct.  In fact ACL2 realizes this, and
    ; proves the theorem below automatically.
    
    (defun-sk two-bigger-p (x)
      (exists (y z) (and (< x y) (p y) (< x z) (p z))))
    
    (thm (implies (some-bigger-p x) (two-bigger-p x)))
    
    ; A technical point:  ACL2 fails to prove the theorem above
    ; automatically if we take its contrapositive, unless we disable
    ; two-bigger-p as shown below.  That is because ACL2 needs to expand
    ; some-bigger-p before applying the rewrite rule introduced for
    ; two-bigger-p, which contains free variables.  The moral of the
    ; story is:  Don't expect too much automatic support from ACL2 for
    ; reasoning about quantified notions.
    
    (thm (implies (not (two-bigger-p x)) (not (some-bigger-p x)))
         :hints (("Goal" :in-theory (disable two-bigger-p))))
    




    acl2-sources/doc/HTML/TUTORIAL5-MISCELLANEOUS-EXAMPLES.html0000664002132200015000000000230712222333515021517 0ustar kaufmannacl2 TUTORIAL5-MISCELLANEOUS-EXAMPLES.html -- ACL2 Version 6.3

    TUTORIAL5-MISCELLANEOUS-EXAMPLES

    miscellaneous ACL2 examples
    Major Section:  ANNOTATED-ACL2-SCRIPTS
    

    The following examples are more advanced examples of usage of ACL2. They are included largely for reference, in case someone finds them useful.

    Some Related Topics




    acl2-sources/doc/HTML/TYPE-PRESCRIPTION.html0000664002132200015000000002605112222333530017451 0ustar kaufmannacl2 TYPE-PRESCRIPTION.html -- ACL2 Version 6.3

    TYPE-PRESCRIPTION

    make a rule that specifies the type of a term
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Examples:
    (defthm integerp-foo                       ; Assumes that foo has been
      (integerp (foo x y))                     ; defined; then, states that
      :rule-classes :type-prescription)        ; (foo x y) is of type integer.
    
    (defthm characterp-nth-type-prescription   ; (Nth n lst) is of type character
      (implies                                 ; provided the hypotheses can be
       (and (character-listp lst)              ; established by type reasoning.
            (<= 0 n)
            (< n (len lst)))
       (characterp (nth n lst)))
      :rule-classes :type-prescription)
    
    (defthm characterp-nth-type-prescription-alt ; equivalent to the above
      (implies
       (and (character-listp lst)
            (<= 0 n)
            (< n (len lst)))
       (characterp (nth n lst)))
      :rule-classes ((:type-prescription :typed-term (nth n lst))))
    
    (defthm demodulize-type-for-quote-value  ; (Demodulize a lst 'value ans) is
      (implies                               ; either a nonnegative integer or
       (and (atom a)                         ; of the same type as ans, provided
            (true-listp lst)                 ; the hyps can be established by type
            (member-equal a lst))            ; reasoning
       (or (and (integerp (demodulize a lst 'value ans))
                (>= (demodulize a lst 'value ans) 0))
         (equal (demodulize a lst 'value ans) ans)))
      :rule-classes :type-prescription)
    

    To specify the term whose type (see type-set) is described by the rule, provide that term as the value of the :typed-term field of the rule class object.

    General Form (after preprocessing; see below):
    (implies hyps
             (or type-restriction1-on-pat
                 ...
                 type-restrictionk-on-pat
                 (equal pat var1)
                 ...
                 (equal pat varj)))
    
    where pat is the application of some function symbol to some arguments, each type-restrictioni-on-pat is a term involving pat and containing no variables outside of the occurrences of pat, and each vari is one of the variables of pat. Generally speaking, the type-restriction terms ought to be terms that inform us as to the type of pat. Ideally, they should be ``primitive recognizing expressions'' about pat; see compound-recognizer. We describe preprocessing at the end of this topic.

    If the :typed-term is not provided in the rule class object, it is computed heuristically by looking for a term in the conclusion whose type is being restricted. An error is caused if no such term is found.

    Roughly speaking, the effect of adding such a rule is to inform the ACL2 typing mechanism that pat has the type described by the conclusion, when the hypotheses are true. In particular, the type of pat is within the union of the types described by the several disjuncts. The ``type described by'' (equal pat vari) is the type of vari.

    More operationally, when asked to determine the type of a term that is an instance of pat, ACL2 will first attempt to establish the hypotheses. This is done by type reasoning alone, not rewriting! However, if some hypothesis is a call of force, then forcing may occur, which may ultimately invoke the rewriter; see force and see case-split. So-called free variables in hypotheses are treated specially; see free-variables. Provided the hypotheses are established by type reasoning, ACL2 then unions the types described by the type-restrictioni-on-pat terms together with the types of those subexpressions of pat identified by the vari. The final type computed for a term is the intersection of the types implied by each applicable rule. Type prescription rules may be disabled.

    You can limit the recursive establishment of hypotheses of rules; see set-backchain-limit.

    Because only type reasoning is used to establish the hypotheses of :type-prescription rules, some care must be taken with the hypotheses. Suppose, for example, that the non-recursive function my-statep is defined as

      (defun my-statep (x)
        (and (true-listp x)
             (equal (len x) 2)))
    
    and suppose (my-statep s) occurs as a hypothesis of a :type-prescription rule that is being considered for use in the proof attempt for a conjecture with the hypothesis (my-statep s). Since the hypothesis in the conjecture is rewritten, it will become the conjunction of (true-listp s) and (equal (len s) 2). Those two terms will be assumed to have type t in the context in which the :type-prescription rule is tried. But type reasoning will be unable to deduce that (my-statep s) has type t in this context. Thus, either my-statep should be disabled (see disable) during the proof attempt or else the occurrence of (my-statep s) in the :type-prescription rule should be replaced by the conjunction into which it rewrites.

    While this example makes it clear how non-recursive predicates can cause problems, non-recursive functions in general can cause problems. For example, if (mitigate x) is defined to be (if (rationalp x) (1- x) x) then the hypothesis (pred (mitigate s)) in the conjecture will rewrite, opening mitigate and splitting the conjecture into two subgoals, one in which (rationalp s) and (pred (1- x)) are assumed and the other in which (not (rationalp s)) and (pred x) are assumed. But (pred (mitigate s)) will not be typed as t in either of these contexts. The moral is: beware of non-recursive functions occuring in the hypotheses of :type-prescription rules.

    Because of the freedom one has in forming the conclusion of a type-prescription, we have to use heuristics to recover the pattern, pat, whose type is being specified. In some cases our heuristics may not identify the intended term and the :type-prescription rule will be rejected as illegal because the conclusion is not of the correct form. When this happens you may wish to specify the pat directly. This may be done by using a suitable rule class token. In particular, when the token :type-prescription is used it means ACL2 is to compute pat with its heuristics; otherwise the token should be of the form (:type-prescription :typed-term pat), where pat is the term whose type is being specified.

    The defun event may generate a :type-prescription rule. Suppose fn is the name of the function concerned. Then (:type-prescription fn) is the rune given to the type-prescription, if any, generated for fn by defun. (The trivial rule, saying fn has unknown type, is not stored, but defun still allocates the rune and the corollary of this rune is known to be t.)

    We close with a discussion of how, before a term is parsed into a :type-prescription rule, it is preprocessed. We describe this preprocessing in some detail below, but first consider the following (contrived) example.

    (defthm append-tp-example
      (let ((result (append x y)))
        (implies (nat-listp x)
                 (implies (let ((second-hyp (integer-listp y)))
                            second-hyp)
                          (true-listp result))))
      :rule-classes :type-prescription)
    
    This theorem is parsed into a type-prescription rule with the following hypotheses and conclusion.
    (nat-listp x) ; first hypothesis
    ((lambda (second-hyp) second-hyp) (integer-listp y)) ; second hypothesis
    (true-listp (binary-append x y)) ; conclusion
    
    Notice that the top-level LET was expanded, i.e., (append x y) was substituted for result -- more accurately, (binary-append x y) was substituted for result, since append is a macro that abbreviates binary-append. Also notice that the two hypotheses were ``flattened'' in the sense that they were gathered up into a list. Finally, notice that the LET in the second hypothesis was not expanded (it was merely translated to internal form, using LAMBDA). If you actually submit the theorem above, you will get warnings, which you may choose to ignore; the application of type-prescription rules is somewhat subtle, so if you use them then you may wish to experiment to see which forms work best for you.

    Here is the detail promised above, for parsing a term into a :type-prescription rule. There are two steps. (1) ACL2 first translates the term, expanding all macros (see trans) and also expanding away calls of all so-called ``guard holders,'' mv-list and return-last (the latter resulting for example from calls of prog2$, mbe, or ec-call), as well as expansions of the macro `the'. (2) Then the the translated term is traversed top-down, expanding away lambdas (let, let*, and mv-let expressions) and flattening the IMPLIES structure, until the conclusion is exposed; then the conclusion's lambdas are also expanded away. The simplest way to understand (2) may be to look at the definition of ACL2 source function unprettyify-tp, which implements Step (2), say by evaluating :pe unprettyify-tp.




    acl2-sources/doc/HTML/TYPE-SET-INVERTER.html0000664002132200015000000000636612222333530017426 0ustar kaufmannacl2 TYPE-SET-INVERTER.html -- ACL2 Version 6.3

    TYPE-SET-INVERTER

    exhibit a new decoding for an ACL2 type-set
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example Rule Class:
    (:type-set-inverter
      :corollary (equal (and (counting-number x) (not (equal x 0)))
                        (and (integerp x) (< 0 x)))
      :type-set 2)
    
    General Forms of Rule Class:
    :type-set-inverter, or
    (:type-set-inverter :type-set n)
    
    General Form of Theorem or Corollary:
    (EQUAL new-expr old-expr)
    
    where n is a type-set (see type-set) and old-expr is the term containing x as a free variable that ACL2 currently uses to recognize type-set n. For a given n, the exact form of old-expr is generated by
    (convert-type-set-to-term 'x n (ens state) (w state) nil)].
    

    If the :type-set field of the rule-class is omitted, we attempt to compute it from the right-hand side, old-expr, of the corollary. That computation is done by type-set-implied-by-term (see type-set). However, it is possible that the type-set we compute from lhs does not have the required property that when inverted with convert-type-set-to-term the result is lhs. If you omit :type-set and an error is caused because lhs has the incorrect form, you should manually specify both :type-set and the lhs generated by convert-type-set-to-term.

    The rule generated will henceforth make new-expr be the term used by ACL2 to recognize type-set n. If this rule is created by a defthm event named name then the rune of the rule is (:type-set-inverter name) and by disabling that rune you can prevent its being used to decode type-sets.

    Type-sets are inverted when forced assumptions are turned into formulas to be proved. In their internal form, assumptions are essentially pairs consisting of a context and a goal term, which was forced. Abstractly a context is just a list of hypotheses which may be assumed while proving the goal term. But actually contexts are alists which pair terms with type-sets, encoding the current hypotheses. For example, if the original conjecture contained the hypothesis (integerp x) then the context used while working on that conjecture will include the assignment to x of the type-set *ts-integer*.




    acl2-sources/doc/HTML/TYPE-SET.html0000664002132200015000000002045212222333522016163 0ustar kaufmannacl2 TYPE-SET.html -- ACL2 Version 6.3

    TYPE-SET

    how type information is encoded in ACL2
    Major Section:  MISCELLANEOUS
    

    To help you experiment with type-sets we briefly note the following utility functions.

    (type-set-quote x) will return the type-set of the object x. For example, (type-set-quote "test") is 2048 and (type-set-quote '(a b c)) is 512.

    (type-set 'term nil nil nil (ens state) (w state) nil nil nil) will return the type-set of term. For example,

    (type-set '(integerp x) nil nil nil (ens state) (w state) nil nil nil)
    
    will return (mv 192 nil). 192, otherwise known as *ts-boolean*, is the type-set containing t and nil. The second result may be ignored in these experiments. Term must be in the translated, internal form shown by :trans. See trans and see term.

    (type-set-implied-by-term 'x nil 'term (ens state)(w state) nil) will return the type-set deduced for the variable symbol x assuming the translated term, term, true. The second result may be ignored in these experiments. For example,

    (type-set-implied-by-term 'v nil '(integerp v)
                              (ens state) (w state) nil)
    
    returns 11.

    (convert-type-set-to-term 'x ts (ens state) (w state) nil) will return a term whose truth is equivalent to the assertion that the term x has type-set ts. The second result may be ignored in these experiments. For example

    (convert-type-set-to-term 'v 523 (ens state) (w state) nil)
    
    returns a term expressing the claim that v is either an integer or a non-nil true-list. 523 is the logical-or of 11 (which denotes the integers) with 512 (which denotes the non-nil true-lists).

    The ``actual primitive types'' of ACL2 are listed in *actual-primitive-types*, whose elements are shown below. Each actual primitive type denotes a set -- sometimes finite and sometimes not -- of ACL2 objects and these sets are pairwise disjoint. For example, *ts-zero* denotes the set containing 0 while *ts-positive-integer* denotes the set containing all of the positive integers.

    *TS-ZERO*                  ;;; {0}
    *TS-POSITIVE-INTEGER*      ;;; positive integers
    *TS-POSITIVE-RATIO*        ;;; positive non-integer rationals
    *TS-NEGATIVE-INTEGER*      ;;; negative integers
    *TS-NEGATIVE-RATIO*        ;;; negative non-integer rationals
    *TS-COMPLEX-RATIONAL*      ;;; complex rationals
    *TS-NIL*                   ;;; {nil}
    *TS-T*                     ;;; {t}
    *TS-NON-T-NON-NIL-SYMBOL*  ;;; symbols other than nil, t
    *TS-PROPER-CONS*           ;;; null-terminated non-empty lists
    *TS-IMPROPER-CONS*         ;;; conses that are not proper
    *TS-STRING*                ;;; strings
    *TS-CHARACTER*             ;;; characters
    

    The actual primitive types were chosen by us to make theorem proving convenient. Thus, for example, the actual primitive type *ts-nil* contains just nil so that we can encode the hypothesis ``x is nil'' by saying ``x has type *ts-nil*'' and the hypothesis ``x is non-nil'' by saying ``x has type complement of *ts-nil*.'' We similarly devote a primitive type to t, *ts-t*, and to a third type, *ts-non-t-non-nil-symbol*, to contain all the other ACL2 symbols.

    Let *ts-other* denote the set of all Common Lisp objects other than those in the actual primitive types. Thus, *ts-other* includes such things as floating point numbers and CLTL array objects. The actual primitive types together with *ts-other* constitute what we call *universe*. Note that *universe* is a finite set containing one more object than there are actual primitive types; that is, here we are using *universe* to mean the finite set of primitive types, not the infinite set of all objects in all of those primitive types. *Universe* is a partitioning of the set of all Common Lisp objects: every object belongs to exactly one of the sets in *universe*.

    Abstractly, a ``type-set'' is a subset of *universe*. To say that a term, x, ``has type-set ts'' means that under all possible assignments to the variables in x, the value of x is a member of some member of ts. Thus, (cons x y) has type-set {*ts-proper-cons* *ts-improper-cons*}. A term can have more than one type-set. For example, (cons x y) also has the type-set {*ts-proper-cons* *ts-improper-cons* *ts-nil*}. Extraneous types can be added to a type-set without invalidating the claim that a term ``has'' that type-set. Generally we are interested in the smallest type-set a term has, but because the entire theorem-proving problem for ACL2 can be encoded as a type-set question, namely, ``Does p have type-set complement of *ts-nil*?,'' finding the smallest type-set for a term is an undecidable problem. When we speak informally of ``the'' type-set we generally mean ``the type-set found by our heuristics'' or ``the type-set assumed in the current context.''

    Note that if a type-set, ts, does not contain *ts-other* as an element then it is just a subset of the actual primitive types. If it does contain *ts-other* it can be obtained by subtracting from *universe* the complement of ts. Thus, every type-set can be written as a (possibly complemented) subset of the actual primitive types.

    By assigning a unique bit position to each actual primitive type we can encode every subset, s, of the actual primitive types by the nonnegative integer whose ith bit is on precisely if s contains the ith actual primitive type. The type-sets written as the complement of s are encoded as the twos-complement of the encoding of s. Those type-sets are thus negative integers. The bit positions assigned to the actual primitive types are enumerated from 0 in the same order as the types are listed in *actual-primitive-types*. At the concrete level, a type-set is an integer between *min-type-set* and *max-type-set*, inclusive.

    For example, *ts-nil* has bit position 6. The type-set containing just *ts-nil* is thus represented by 64. If a term has type-set 64 then the term is always equal to nil. The type-set containing everything but *ts-nil* is the twos-complement of 64, which is -65. If a term has type-set -65, it is never equal to nil. By ``always'' and ``never'' we mean under all, or under no, assignments to the variables, respectively.

    Here is a more complicated example. Let s be the type-set containing all of the symbols and the natural numbers. The relevant actual primitive types, their bit positions and their encodings are:

    actual primitive type       bit    value
    
    *ts-zero*                    0       1
    *ts-positive-integer*        1       2
    *ts-nil*                     6      64
    *ts-t*                       7     128
    *ts-non-t-non-nil-symbol*    8     256
    
    Thus, the type-set s is represented by (+ 1 2 64 128 256) = 451. The complement of s, i.e., the set of all objects other than the natural numbers and the symbols, is -452.




    acl2-sources/doc/HTML/TYPE-SPEC.html0000664002132200015000000000702512222333525016266 0ustar kaufmannacl2 TYPE-SPEC.html -- ACL2 Version 6.3

    TYPE-SPEC

    type specifiers in declarations
    Major Section:  DECLARE
    

    Examples:
    The symbol INTEGER in (declare (type INTEGER i j k)) is a type-spec.  Other
    type-specs supported by ACL2 include RATIONAL, COMPLEX, (INTEGER 0 127),
    (RATIONAL 1 *), CHARACTER, and ATOM.  
    

    The type-specs and their meanings (when applied to the variable x as in (declare (type type-spec x)) are given below.

    type-spec              meaning
    (AND type1 ... typek)  (AND (p1 X) ... (pk X))
                           where (pj x) is the meaning for type-spec typej
    ATOM                   (ATOM X)
    BIT                    (OR (EQUAL X 1) (EQUAL X 0))
    CHARACTER              (CHARACTERP X)
    COMPLEX                (AND (COMPLEX-RATIONALP X)
                                (RATIONALP (REALPART X))
                                (RATIONALP (IMAGPART X)))
    (COMPLEX RATIONAL)     same as COMPLEX, above
    (COMPLEX type)         (AND (COMPLEX-RATIONALP X)
                                (p (REALPART X))
                                (p (IMAGPART X)))
                           where (p x) is the meaning for type-spec type
    CONS                   (CONSP X)
    INTEGER                (INTEGERP X)
    (INTEGER i j)          (AND (INTEGERP X)   ; See notes below
                                (<= i X)
                                (<= X j))
    (MEMBER x1 ... xn)     (MEMBER X '(x1 ... xn))
    (MOD i)                same as (INTEGER 0 i-1)
    NIL                    NIL
    (NOT type)             (NOT (p X))
                           where (p x) is the meaning for type-spec type
    NULL                   (EQ X NIL)
    (OR type1 ... typek)   (OR (p1 X) ... (pk X))
                           where (pj x) is the meaning for type-spec typej
    RATIO                  (AND (RATIONALP X) (NOT (INTEGERP X)))
    RATIONAL               (RATIONALP X)
    (RATIONAL i j)         (AND (RATIONALP X)  ; See notes below
                                (<= i X)
                                (<= X j))
    REAL                   (RATIONALP X)       ; (REALP X) in ACL2(r)
    (REAL i j)             (AND (RATIONALP X)  ; See notes below
                                (<= i X)
                                (<= X j))
    (SATISFIES pred)       (pred X) ; Lisp requires a unary function, not a macro
    SIGNED-BYTE            (INTEGERP X)
    (SIGNED-BYTE i)        same as (INTEGER k m) where k=-2^(i-1), m=2^(i-1)-1
    STANDARD-CHAR          (STANDARD-CHARP X)
    STRING                 (STRINGP X)
    (STRING max)           (AND (STRINGP X) (EQUAL (LENGTH X) max))
    SYMBOL                 (SYMBOLP X)
    T                      T
    UNSIGNED-BYTE          same as (INTEGER 0 *)
    (UNSIGNED-BYTE i)      same as (INTEGER 0 (2^i)-1)
    
    Notes:

    In general, (integer i j) means

         (AND (INTEGERP X)
              (<= i X)
              (<= X j)).
    
    But if i is the symbol *, the first inequality is omitted. If j is the symbol *, the second inequality is omitted. If instead of being an integer, the second element of the type specification is a list containing an integer, (i), then the first inequality is made strict. An analogous remark holds for the (j) case. The RATIONAL and REAL type specifiers are similarly generalized.




    acl2-sources/doc/HTML/TYPE.html0000664002132200015000000000061112222333525015530 0ustar kaufmannacl2 TYPE.html -- ACL2 Version 6.3

    TYPE

    See declare.
    Major Section:  PROGRAMMING
    




    acl2-sources/doc/HTML/TYPESPEC-CHECK.html0000664002132200015000000000065312222333522017021 0ustar kaufmannacl2 TYPESPEC-CHECK.html -- ACL2 Version 6.3

    TYPESPEC-CHECK

    See meta-extract.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/The_Admission_of_App.html0000664002132200015000000000323712222333526020771 0ustar kaufmannacl2 The_Admission_of_App.html -- ACL2 Version 6.3

    The Admission of App

    Here is what it looks like to submit the definition of app to ACL2:

    ACL2 !>(defun app (x y)
      (cond ((endp x) y)
            (t (cons (car x) 
                     (app (cdr x) y)))))
    
    The admission of APP is trivial, using the relation O< (which
    is known to be well-founded on the domain recognized by O-P)
    and the measure (ACL2-COUNT X).  We observe that the type of APP is
    described by the theorem (OR (CONSP (APP X Y)) (EQUAL (APP X Y) Y)).
    We used primitive type reasoning.
    
    Summary
    Form:  ( DEFUN APP ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.03 seconds (prove: 0.00, print: 0.00, other: 0.03)
     APP
    

    The text between the lines above is one interaction with the ACL2 command loop. Interacting with the latest version of ACL2 may not produce the very same output, but we trust you'll recognize the basics.

    Above you see the user's input and how the system responds. This little example shows you what the syntax looks like and is a very typical successful interaction with the definitional principle.

    Let's look at it a little more closely.




    acl2-sources/doc/HTML/The_Associativity_of_App.html0000664002132200015000000000336112222333526021674 0ustar kaufmannacl2 The_Associativity_of_App.html -- ACL2 Version 6.3

    The Associativity of App

    ACL2!>(let ((a '(1 2))
                (b '(3 4))
                (c '(5 6)))
            (equal (app (app a b) c)
                   (app a (app b c))))
    T
    

    Observe that, for the particular a, b, and c above, (app (app a b) c) returns the same thing as (app a (app b c)). Perhaps app is associative. Of course, to be associative means that the above property must hold for all values of a, b, and c, not just the ones tested above.

    Wouldn't it be cool if you could type

    ACL2!>(equal (app (app a b) c)
                 (app a (app b c)))
    
    
    and have ACL2 compute the value T? Well, you can't! If you try it, you'll get an error message! The message says we can't evaluate that form because it contains free variables, i.e., variables not given values. Click here to see the message.

    We cannot evaluate a form on an infinite number of cases. But we can prove that a form is a theorem and hence know that it will always evaluate to true.




    acl2-sources/doc/HTML/The_Base_Case_in_the_App_Example.html0000664002132200015000000000134112222333526023157 0ustar kaufmannacl2 The_Base_Case_in_the_App_Example.html -- ACL2 Version 6.3

    The Base Case in the App Example

    This formula is the Base Case. It consists of two parts, a test identifying the non-inductive case and the conjecture to prove.

    (IMPLIES (ENDP A)                 ; Test
             (:P A B C))              ; Conjecture
    

    When we prove this we can assume

      * A is empty
    
    and we have to prove the conjecture for A.




    acl2-sources/doc/HTML/The_End_of_the_Flying_Tour.html0000664002132200015000000000113712222333526022127 0ustar kaufmannacl2 The_End_of_the_Flying_Tour.html -- ACL2 Version 6.3

    The End of the Flying Tour

    This completes the Flying Tour.

    We recommend that you now take A Walking Tour of ACL2.

    Thanks.
    Matt Kaufmann and J Moore




    acl2-sources/doc/HTML/The_End_of_the_Proof_of_the_Associativity_of_App.html0000664002132200015000000000221312222333526026472 0ustar kaufmannacl2 The_End_of_the_Proof_of_the_Associativity_of_App.html -- ACL2 Version 6.3

    The End of the Proof of the Associativity of App

    That completes the proof of *1.
    
    Q.E.D.
    
    Summary
    Form:  ( DEFTHM ASSOCIATIVITY-OF-APP ...)
    Rules: ((:REWRITE CDR-CONS)
            (:REWRITE CAR-CONS)
            (:DEFINITION NOT)
            (:DEFINITION ENDP)
            (:FAKE-RUNE-FOR-TYPE-SET NIL)
            (:DEFINITION APP))
    Warnings:  None
    Time:  0.27 seconds (prove: 0.10, print: 0.05, other: 0.12)
     ASSOCIATIVITY-OF-APP
    




    acl2-sources/doc/HTML/The_End_of_the_Walking_Tour.html0000664002132200015000000000257312222333526022300 0ustar kaufmannacl2 The_End_of_the_Walking_Tour.html -- ACL2 Version 6.3

    The End of the Walking Tour

    This completes the Walking Tour.

    We intend to document many other parts of the system this way, but we just haven't gotten around to it.

    To start the two tours over again from the beginning, click on the icons below. If you are really interested in learning how to use ACL2, we recommend that you repeat each tour at least once more to explore branches of the tour that you might have missed.

    If you want to learn how to use the theorem prover, we now recommend that you devote the time necessary to work your way through the extended introduction to how to use the prover.

    See introduction-to-the-theorem-prover.

    This will explain how to interact with ACL2 and has some sample problems for you to solve including some challenge proofs to make ACL2 find.

    We hope you enjoy ACL2. We do.

    Matt Kaufmann and J Strother Moore




    acl2-sources/doc/HTML/The_Event_Summary.html0000664002132200015000000000614112222333526020352 0ustar kaufmannacl2 The_Event_Summary.html -- ACL2 Version 6.3

    The Event Summary

    At the conclusion of most events (click here for a brief discussion of events or see events ), ACL2 prints a summary. The summary for app is:

    Summary
    Form:  ( DEFUN APP ...)
    Rules: ((:FAKE-RUNE-FOR-TYPE-SET NIL))
    Warnings:  None
    Time:  0.03 seconds (prove: 0.00, print: 0.00, other: 0.03)
     APP
    

    The ``rules'' listed are those used in function admission or proof summarized. What is actually listed are ``runes'' (see rune) ) which are list-structured names for rules in the ACL2 database or ``world'' . Using theories you can ``enable'' and ``disable'' rules so as to make them available (or not) to the ACL2 theorem prover.

    The ``warnings'' mentioned (none are listed for app) remind the reader whether the event provoked any warnings. The warnings themselves would have been printed earlier in the processing and this part of the summary just names the earlier warnings printed.

    The ``time'' indicates how much processing time was used and is divided into three parts: the time devoted to proof, to printing, and to syntactic checks, pre-processing and database updates. Despite the fact that ACL2 is an applicative language it is possible to measure time with ACL2 programs. The state contains a clock. The times are printed in decimal notation but are actually counted in integral units. Note that by default, each time is a runtime, also known as a cpu time, as opposed to being a real time, also known as a wall clock time.

    The final APP is the value of the defun command and was printed by the read-eval-print loop. The fact that it is indented one space is a subtle reminder that the command actually returned an ``error triple'', consisting of a flag indicating (in this case) that no error occurred, a value (in this case the symbol APP), and the final state ). See ld-post-eval-print for some details. If you really want to follow that link, however, you might see ld first.

    You should now return to the Walking Tour.




    acl2-sources/doc/HTML/The_Expansion_of_ENDP_in_the_Induction_Step__lparen_Step_0_rparen_.html0000664002132200015000000000152712222333526031772 0ustar kaufmannacl2 The_Expansion_of_ENDP_in_the_Induction_Step__lparen_Step_0_rparen_.html -- ACL2 Version 6.3

    The Expansion of ENDP in the Induction Step (Step 0)

    Subgoal *1/2
    (IMPLIES (AND (NOT (ENDP A))
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    

    Click on the link above (the open parenthesis before ENDP) to replace (ENDP A) by its definition.




    acl2-sources/doc/HTML/The_Expansion_of_ENDP_in_the_Induction_Step__lparen_Step_1_rparen_.html0000664002132200015000000000156712222333526031777 0ustar kaufmannacl2 The_Expansion_of_ENDP_in_the_Induction_Step__lparen_Step_1_rparen_.html -- ACL2 Version 6.3

    The Expansion of ENDP in the Induction Step (Step 1)

    Subgoal *1/2
    (IMPLIES (AND (NOT (NOT (CONSP A)))
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    

    The bold text is the instantiated definition of ENDP.

    Now click on the link above to simplify (NOT (NOT (CONSP A)))




    acl2-sources/doc/HTML/The_Expansion_of_ENDP_in_the_Induction_Step__lparen_Step_2_rparen_.html0000664002132200015000000000147212222333526031773 0ustar kaufmannacl2 The_Expansion_of_ENDP_in_the_Induction_Step__lparen_Step_2_rparen_.html -- ACL2 Version 6.3

    The Expansion of ENDP in the Induction Step (Step 2)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    
    

    Note that this is Subgoal *1/2'.

    You may click here to return to the main proof.




    acl2-sources/doc/HTML/The_Falling_Body_Model.html0000664002132200015000000000157112222333526021227 0ustar kaufmannacl2 The_Falling_Body_Model.html -- ACL2 Version 6.3

    The Falling Body Model

    One particularly famous and very simple model is the equation of a falling body: the distance d an object falls is proportional to the square of the time t. If the time is measured in seconds and the distance in feet, the equation relating these two is

           2
    d = 16t
    

    This equation is a model of falling objects. It can be used to predict how long it takes a cannonball to fall from the top of a 200 foot tower (3.5 seconds). This might be important if your product is designed to drop cannonballs on moving targets.




    acl2-sources/doc/HTML/The_Final_Simplification_in_the_Base_Case__lparen_Step_0_rparen_.html0000664002132200015000000000153612222333526031475 0ustar kaufmannacl2 The_Final_Simplification_in_the_Base_Case__lparen_Step_0_rparen_.html -- ACL2 Version 6.3

    the Final Simplification in the Base Case (Step 0)

    Subgoal *1/1'
    (IMPLIES (NOT (CONSP A))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    

    Click on the link above to replace (APP A B) by its definition. Note that the hypothesis (NOT (CONSP A)) allows us to simplify the IF in APP to its false branch this time.




    acl2-sources/doc/HTML/The_Final_Simplification_in_the_Base_Case__lparen_Step_1_rparen_.html0000664002132200015000000000142012222333526031466 0ustar kaufmannacl2 The_Final_Simplification_in_the_Base_Case__lparen_Step_1_rparen_.html -- ACL2 Version 6.3

    the Final Simplification in the Base Case (Step 1)

    Subgoal *1/1'
    (IMPLIES (NOT (CONSP A))
             (EQUAL (APP B C)
                    (APP A (APP B C)))).
    

    Click on the link above to expand the definition of APP. Again, we come out through the false branch because of the hypothesis.




    acl2-sources/doc/HTML/The_Final_Simplification_in_the_Base_Case__lparen_Step_2_rparen_.html0000664002132200015000000000126312222333526031474 0ustar kaufmannacl2 The_Final_Simplification_in_the_Base_Case__lparen_Step_2_rparen_.html -- ACL2 Version 6.3

    the Final Simplification in the Base Case (Step 2)

    Subgoal *1/1'
    (IMPLIES (NOT (CONSP A))
             (EQUAL (APP B C)
                    (APP B C))).
    

    Click on the link above to use the Axiom (EQUAL x x) = t




    acl2-sources/doc/HTML/The_Final_Simplification_in_the_Base_Case__lparen_Step_3_rparen_.html0000664002132200015000000000143012222333526031471 0ustar kaufmannacl2 The_Final_Simplification_in_the_Base_Case__lparen_Step_3_rparen_.html -- ACL2 Version 6.3

    the Final Simplification in the Base Case (Step 3)

    Subgoal *1/1'
    (IMPLIES (NOT (CONSP A))
             T)
    

    Now that its conclusion is identically T the IMPLIES will simplify to T (not shown) and we are done with Subgoal *1/1'.

    You may click here to return to the main proof.




    acl2-sources/doc/HTML/The_First_Application_of_the_Associativity_Rule.html0000664002132200015000000000160012222333526026407 0ustar kaufmannacl2 The_First_Application_of_the_Associativity_Rule.html -- ACL2 Version 6.3

    The First Application of the Associativity Rule

    So here we see our associativity rule being used!

    The rewriter sweeps the conjecture in a leftmost innermost fashion, applying rewrite rules as it goes.

    The associativity rule is used many times in this sweep. The first ``target'' is highlighted below. Click on it to see what happens:

    Current Conjecture:
    (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7)
           (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7)))))
    




    acl2-sources/doc/HTML/The_Induction_Scheme_Selected_for_the_App_Example.html0000664002132200015000000000200412222333526026557 0ustar kaufmannacl2 The_Induction_Scheme_Selected_for_the_App_Example.html -- ACL2 Version 6.3

    The Induction Scheme Selected for the App Example

    (AND
       (IMPLIES (AND (NOT (ENDP A))         ; Induction Step: test
                     (:P (CDR A) B C))      ;  and induction hypothesis
                (:P A B C))                 ;  implies induction conclusion.
       (IMPLIES (ENDP A) (:P A B C)))       ; Base Case
    

    The formula beginning with this parenthesis is the induction scheme suggested by (APP A B) applied to (P A B C).

    It is a conjunction (AND ) of two formulas.

    The first is the induction step and the second is the base case.




    acl2-sources/doc/HTML/The_Induction_Step_in_the_App_Example.html0000664002132200015000000000201712222333526024302 0ustar kaufmannacl2 The_Induction_Step_in_the_App_Example.html -- ACL2 Version 6.3

    The Induction Step in the App Example

    This formula is the Induction Step. It basically consists of three parts, a test identifying the inductive case, an induction hypothesis and an induction conclusion.

    (IMPLIES (AND (NOT (ENDP A))      ; Test
                  (:P (CDR A) B C))   ; Induction Hypothesis
             (:P A B C))              ; Induction Conclusion
    

    When we prove this we can assume

      * A is not empty, and that
    
      * the associativity conjecture holds for a ``smaller'' version of
        A, namely, (CDR A).
    
    

    Under those hypotheses we have to prove the associativity conjecture for A itself.




    acl2-sources/doc/HTML/The_Instantiation_of_the_Induction_Scheme.html0000664002132200015000000000121612222333526025222 0ustar kaufmannacl2 The_Instantiation_of_the_Induction_Scheme.html -- ACL2 Version 6.3

    The Instantiation of the Induction Scheme

    The induction scheme just shown is just an abbreviation for our real goal.

    To obtain our actual goals we have to replace the schema :P by the associativity conjecture (instantiated as shown in the scheme).

    This produces two actual goals, the induction step and the base case.




    acl2-sources/doc/HTML/The_Justification_of_the_Induction_Scheme.html0000664002132200015000000000102412222333526025206 0ustar kaufmannacl2 The_Justification_of_the_Induction_Scheme.html -- ACL2 Version 6.3

    The Justification of the Induction Scheme

    This paragraph explains why the induction selected is legal. The explanation is basically the same as the explanation for why the recursion in (APP A B) terminates.




    acl2-sources/doc/HTML/The_Proof_of_the_Associativity_of_App.html0000664002132200015000000000627212222333526024371 0ustar kaufmannacl2 The_Proof_of_the_Associativity_of_App.html -- ACL2 Version 6.3

    The Proof of the Associativity of App

    Here is the theorem prover's output when it processes the defthm command for the associativity of app. We have highlighted text for which we offer some explanation, and broken the presentation into several pages. (The most recent version of ACL2 may print slightly different output but the basics are the same.) Just follow the Walking Tour after exploring the explanations.

    However, before exploring this output you should understand that ACL2 users rarely read successful proofs! Instead, they look at certain subgoals printed in failed proofs, figure whether and how those subgoals can be proved, and give ACL2 directions for proving them, usually by simply proving other lemmas. Furthermore, to be a good user of ACL2 you do not have to understand how the theorem prover works. You just have to understand how to interact with it. We explain this in great detail later. But basically all new users are curious to know how ACL2 works and this little tour attempts to give some answers, just to satisfy your curiosity.

    ACL2!>(defthm associativity-of-app
            (equal (app (app a b) c)
                   (app a (app b c))))
    
    Name the formula above *1.
    
    Perhaps we can prove *1 by induction.  Three induction schemes are
    suggested by this conjecture.  Subsumption reduces that number to two.
    However, one of these is flawed and so we are left with one viable
    candidate.
    
    We will induct according to a scheme suggested by (APP A B).  If we
    let  (:P A B C) denote *1 above then the induction scheme we'll use
    is
    (AND
       (IMPLIES (AND (NOT (ENDP A))
                     (:P (CDR A) B C))
                (:P A B C))
       (IMPLIES (ENDP A) (:P A B C))).
    This induction is justified by the same argument used to admit APP,
    namely, the measure (ACL2-COUNT A) is decreasing according to the relation
    O< (which is known to be well-founded on the domain recognized
    by O-P).  When applied to the goal at hand the above induction
    scheme produces the following two nontautological subgoals.
    
    




    acl2-sources/doc/HTML/The_Q.E.D._Message.html0000664002132200015000000000071612222333526020045 0ustar kaufmannacl2 The_Q.E.D._Message.html -- ACL2 Version 6.3

    The Q.E.D. Message

    Q.E.D. stands for ``quod erat demonstrandum'' which is Latin for ``which was to be demonstrated'' and is the signal that a proof is completely done.




    acl2-sources/doc/HTML/The_Rules_used_in_the_Associativity_of_App_Proof.html0000664002132200015000000000216712222333526026564 0ustar kaufmannacl2 The_Rules_used_in_the_Associativity_of_App_Proof.html -- ACL2 Version 6.3

    The Rules used in the Associativity of App Proof

    Note that under Rules we list the runes of all the rules used in the proof. This list says that we used the rewrite rules CAR-CONS and CDR-CONS, the definitions of the functions NOT, ENDP and APP, and primitive type reasoning (which is how we simplified the IF and EQUAL terms).

    For what it is worth, IMPLIES and AND are actually macros that are expanded into IF expressions before the proof ever begins. The use of macros is not reported among the rules.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_0_rparen_.html0000664002132200015000000000145512222333526032705 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_0_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 0)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP A B) C)
                    (APP A (APP B C)))).
    

    Click on the link above to replace (APP A B) by its definition.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_10_rparen_.html0000664002132200015000000000163312222333526032764 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_10_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 10)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (APP (CDR A) B) C)
                    (APP (CDR A) (APP B C)))).
    

    Click on the link above to use the Induction Hypothesis (which is the second of the two hypotheses above and which is identical to the rewritten conclusion).




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_11_rparen_.html0000664002132200015000000000155412222333526032767 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_11_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 11)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             T)
    

    Click on the link above to use the definition of IMPLIES. Since the conclusion of the implication is now identically T, the implication simplifies to T.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_12_rparen_.html0000664002132200015000000000140412222333526032762 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_12_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 12)

    Subgoal *1/2'
    T
    

    So, indeed, Subgoal *1/2' does simplify to T!

    You can see that even in an example as simple as this one, quite a lot happens in simplification.

    You may click here to return to the main proof.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_1_rparen_.html0000664002132200015000000000245412222333526032706 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_1_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 1)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (IF (CONSP A)
                             (CONS (CAR A) (APP (CDR A) B))
                             B)
                         C)
                    (APP A (APP B C)))).
    

    Note that the IF expression above is the simplified body of APP. But we know the test (CONSP A) is true, by the first hypothesis. Click on the link above to replace the test by T. Actually this step and several subsequent ones are done during the simplification of the body of APP but we want to illustrate the basic principles of simplification without bothering with every detail.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_2_rparen_.html0000664002132200015000000000162412222333526032705 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_2_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 2)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (IF T
                             (CONS (CAR A) (APP (CDR A) B))
                             B)
                         C)
                    (APP A (APP B C)))).
    

    Click on the link above to apply the Axiom (IF T x y) = x.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_3_rparen_.html0000664002132200015000000000153412222333526032706 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_3_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 3)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (APP (CONS (CAR A) (APP (CDR A) B))
                         C)
                    (APP A (APP B C)))).
    

    Click on the link above to expand the definition of APP here.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_4_rparen_.html0000664002132200015000000000205112222333526032702 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_4_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 4)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (IF (CONSP (CONS (CAR A) (APP (CDR A) B)))
                        (CONS (CAR (CONS (CAR A) (APP (CDR A) B)))
                              (APP (CDR (CONS (CAR A) (APP (CDR A) B)))
                                   C))
                        C)
                    (APP A (APP B C)))).
    

    Click on the link above to apply the Axiom (CONSP (CONS x y)) = T.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_5_rparen_.html0000664002132200015000000000174612222333526032715 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_5_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 5)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (IF T
                        (CONS (CAR (CONS (CAR A) (APP (CDR A) B)))
                              (APP (CDR (CONS (CAR A) (APP (CDR A) B)))
                                   C))
                        C)
                    (APP A (APP B C)))).
    

    Click on the link above to apply the Axiom (CAR (CONS x y)) = x.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_6_rparen_.html0000664002132200015000000000171112222333526032706 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_6_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 6)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (IF T
                        (CONS (CAR A)
                              (APP (CDR (CONS (CAR A) (APP (CDR A) B)))
                                   C))
                        C)
                    (APP A (APP B C)))).
    

    Click on the link above to apply the Axiom (CDR (CONS x y)) = y.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_7_rparen_.html0000664002132200015000000000165612222333526032717 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_7_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 7)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (IF T
                        (CONS (CAR A)
                              (APP (APP (CDR A) B)
                                   C))
                        C)
                    (APP A (APP B C)))).
    

    Click on the link above to apply the Axiom (IF T x y) = x.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_8_rparen_.html0000664002132200015000000000203312222333526032706 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_8_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 8)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (CONS (CAR A)
                          (APP (APP (CDR A) B)
                               C))
                    (APP A (APP B C)))).
    

    Click on the link above to expand the definition of APP here. This time, we'll do the whole expansion at once, including the simplification of the resulting IF. This is how ACL2 actually does it.




    acl2-sources/doc/HTML/The_Simplification_of_the_Induction_Conclusion__lparen_Step_9_rparen_.html0000664002132200015000000000215312222333526032712 0ustar kaufmannacl2 The_Simplification_of_the_Induction_Conclusion__lparen_Step_9_rparen_.html -- ACL2 Version 6.3

    the Simplification of the Induction Conclusion (Step 9)

    Subgoal *1/2'
    (IMPLIES (AND (CONSP A)
                  (EQUAL (APP (APP (CDR A) B) C)
                         (APP (CDR A) (APP B C))))
             (EQUAL (CONS (CAR A)
                          (APP (APP (CDR A) B)
                               C))
                    (CONS (CAR A)
                          (APP (CDR A) (APP B C))))).
    

    Click on the link above to apply the Axiom that (EQUAL (CONS x y) (CONS u v)) is equal to the conjunction of (EQUAL x u) and (EQUAL y v). In this case, (EQUAL x u) is trivial, (EQUAL (CAR A) (CAR A)).




    acl2-sources/doc/HTML/The_Summary_of_the_Proof_of_the_Trivial_Consequence.html0000664002132200015000000000106012222333526027243 0ustar kaufmannacl2 The_Summary_of_the_Proof_of_the_Trivial_Consequence.html -- ACL2 Version 6.3

    The Summary of the Proof of the Trivial Consequence

    Note that at the conclusion of the proof, the system reminds you of the earlier Warning.

    It is a good idea, when the Q.E.D. flys by, to see if there were any Warnings.




    acl2-sources/doc/HTML/The_Theorem_that_App_is_Associative.html0000664002132200015000000000325112222333526024023 0ustar kaufmannacl2 The_Theorem_that_App_is_Associative.html -- ACL2 Version 6.3

    The Theorem that App is Associative

    ACL2!>(defthm associativity-of-app
            (equal (app (app a b) c)
                   (app a (app b c))))
    

    The formula above says app is associative. The defthm command instructs ACL2 to prove the formula and to name it associativity-of-app. Actually, the defthm command also builds the formula into the database as a rewrite rule, but we won't go into that just yet.

    What we will consider is how the ACL2 theorem prover proves this formula.

    If you proceed you will find the actual output of ACL2 in response to the command above. Some of the text is highlighted for the purposes of the tour. ACL2 does not highlight its output.

    You will note that we sometimes highlight a single open parenthesis. This is our way of drawing your attention to the subformula that begins with that parenthesis. By clicking on the parenthesis you will get an explanation of the subformula or its processing.




    acl2-sources/doc/HTML/The_Time_Taken_to_do_the_Associativity_of_App_Proof.html0000664002132200015000000000154612222333526027170 0ustar kaufmannacl2 The_Time_Taken_to_do_the_Associativity_of_App_Proof.html -- ACL2 Version 6.3

    The Time Taken to do the Associativity of App Proof

    The time it took us to explain this proof may leave the impression that the proof is complicated. In a way, it is. But it happens quickly.

    The time taken to do this proof is about 1/10 second. The rest of the time (about 2/10 seconds) is spent in pre- and post-processing.

    Basically, this proof flashes across your screen before you can read it; you see the Q.E.D. and don't bother to scroll back to read it. You have more important things to do than read successful proofs.




    acl2-sources/doc/HTML/The_Tours.html0000664002132200015000000000441612222333526016673 0ustar kaufmannacl2 The_Tours.html -- ACL2 Version 6.3

    The Tours

    ACL2 is a very large, multipurpose system. You can use it as a programming language, a specification language, a modeling language, a formal mathematical logic, or a semi-automatic theorem prover, just to name its most common uses. It has been used on a number of industrial applications. If you're uncertain as to whether your project is appropriate for ACL2 we urge you to look over this list or contact the ACL2 developers.

    This home page includes all of ACL2's online documentation, which is quite extensive (over 4 megabytes). To help ease your introduction to ACL2, we have built two tours through this documentation.

    If you are familiar with at least some of the industrial applications of ACL2, then you will understand the distance between the simple examples we talk about in these tours and the kinds of things ACL2 users do with the system.

    Newcomers to ACL2 should first take the ``Flying Tour.'' Then, if you want to know more, take the ``Walking Tour.'' On your first reading, follow the two Tours linearly, clicking only on the icon of the Tour you're on. Beware of other links, which might jump you from one tour to the other or into the reference manual! Once you've had a coherent overview of the system, you might quickly repeat both Tours to see if there are unvisited links you're interested in, using your brower's Back Button to return to your starting points.

    If after all this you want to learn how to use the theorem prover (!), see introduction-to-the-theorem-prover.

    To start a tour, click on the appropriate icon below.

    For readers using our :DOC or our TexInfo format in Emacs: The tours will probably be unsatisfying because we use gif files and assume you can navigate ``back.''




    acl2-sources/doc/HTML/The_WARNING_about_the_Trivial_Consequence.html0000664002132200015000000000226612222333526024741 0ustar kaufmannacl2 The_WARNING_about_the_Trivial_Consequence.html -- ACL2 Version 6.3

    The WARNING about the Trivial Consequence

    This Warning alerts us to the fact that when treated as a rewrite rule, the new rule TRIVIAL-CONSEQUENCE, rewrites terms of the same form as a rule we have already proved, namely ASSOCIATIVITY-OF-APP.

    When you see this warning you should think about your rules!

    In the current case, it would be a good idea not to make TRIVIAL-CONSEQUENCE a rule at all. We could do this with :rule-classes nil.

    ACL2 proceeds to try to prove the theorem, even though it printed some warnings. The basic assumption in ACL2 is that the user understands what he or she is doing but may need a little reminding just to manage a complicated set of facts.




    acl2-sources/doc/HTML/U.html0000664002132200015000000000161212222333517015156 0ustar kaufmannacl2 U.html -- ACL2 Version 6.3

    U

    undo last command, without a query
    Major Section:  HISTORY
    

    Example:
    :u
    

    The keyword command :u is the same as :ubt :max, but with related queries suppressed appropriately. :Oops will undo the last :u. See ubt, see ubu, see ubt!, and see ubu!.




    acl2-sources/doc/HTML/UBT-PREHISTORY.html0000664002132200015000000000152112222333517017111 0ustar kaufmannacl2 UBT-PREHISTORY.html -- ACL2 Version 6.3

    UBT-PREHISTORY

    undo the commands back through the last reset-prehistory event
    Major Section:  HISTORY
    

    This command is only used to eliminate a reset-prehistory event. If your most recent reset-prehistory event does not have a flag argument of t, then :ubt-prehistory undoes all command back through, and including, that reset-prehistory event.




    acl2-sources/doc/HTML/UBT.html0000664002132200015000000000560712222333517015414 0ustar kaufmannacl2 UBT.html -- ACL2 Version 6.3

    UBT

    undo the commands back through a command descriptor
    Major Section:  HISTORY
    

    Examples:
    :ubt :max      ; undo back through the most recent command
                   ; (which just means undo the most recent command)
    :ubt :x        ; same as :ubt :max
    :u             ; same as :ubt :max with no questions asked
    :ubt fn        ; undo back through the introduction of fn
                   ; (including all the other events in fn's block)
    :ubt 5         ; undo back through the fifth command executed
    :ubt (:max -4) ; undo back through the most recent five commands
    :ubt (:x -4)   ; undo back through the most recent five commands
    
    See command-descriptor.

    Ubt takes one argument, a command descriptor, and undoes the commands from :max (aka :x) through the one described. See command-descriptor. Pbt will print the commands that ubt will undo. :Oops will undo the undo. See oops.

    Ubt can cause errors or queries. To avoid these, see ubt!.

    It is important to remember that a command may create several events. That is, the command that introduces fn1 may also introduce fn2. Undoing the command that created either of these will undo them both. The events created by a command constitute the command's ``block'' and we can only undo entire blocks. Use pcb to print the command block of a command if you wish to see what will be lost by undoing the command.

    Ubt will not undo into ``prehistory''. :Ubt 1 will undo all of your commands. But :ubt -5 will cause an error, warning you that :ubt cannot undo system initialization.

    See u for how to undo just the latest command, and see ubu and see ubu! for how to undo back up to, but not including, the current command.




    acl2-sources/doc/HTML/UBT_bang_.html0000664002132200015000000000175212222333517016537 0ustar kaufmannacl2 UBT_bang_.html -- ACL2 Version 6.3

    UBT!

    undo commands, without a query or an error
    Major Section:  HISTORY
    

    Example:
    :ubt! :x-4
    

    The keyword command :ubt! is the same as :ubt, but with related queries suppressed appropriately, and with a guarantee that it is ``error-free.'' More precisely, the value returned by :ubt! will always be of the form (mv nil val state). :Oops will undo the last :ubt!. See ubt, see ubu, and see u.




    acl2-sources/doc/HTML/UBU.html0000664002132200015000000000360612222333517015412 0ustar kaufmannacl2 UBU.html -- ACL2 Version 6.3

    UBU

    undo the commands back up to (not including) a command descriptor
    Major Section:  HISTORY
    

    Examples:
    :ubu :x-3      ; undo the last three commands (same as :ubt :x-2)
    :ubu (:x -3)   ; same as above
    :ubu fn        ; undo back up to, but not including the introduction of fn
                   ; (so fn will continue to be defined)
    :ubu 5         ; undo back up to, but not including, the fifth command
                   ; executed (leaving the first five commands in place)
    
    See command-descriptor.

    Ubu takes one argument, a command descriptor, and undoes the commands from :max (aka :x) up to, but not including, the indicated command. See command-descriptor.

    Ubu can cause errors or queries. To avoid these, see ubu!.

    Also see ubt, which is similar but also undoes the indicated command. As for :ubt, :oops will undo the undo (see oops) and ubu will not undo into ``prehistory''.

    See u for how to undo just the latest command, and see ubt and see ubt! for how to undo back through (that is, including) the current command.




    acl2-sources/doc/HTML/UBU_bang_.html0000664002132200015000000000205212222333517016532 0ustar kaufmannacl2 UBU_bang_.html -- ACL2 Version 6.3

    UBU!

    undo commands, without a query or an error
    Major Section:  HISTORY
    

    Example:
    :ubu! :x-4
    

    The keyword command :ubu! is the same as :ubu, but with related queries suppressed appropriately, and with a guarantee that it is ``error-free.'' More precisely, the error triple (see error-triples) returned by :ubu! will always be of the form (mv nil val state). :Oops will undo the last :ubu!. Also see ubu, see ubt, and see u.




    acl2-sources/doc/HTML/UNARY--.html0000664002132200015000000000162112222333525016001 0ustar kaufmannacl2 UNARY--.html -- ACL2 Version 6.3

    UNARY--

    arithmetic negation function
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-unary-minus):

    (equal (unary-- x)
           (if (acl2-numberp x)
               (unary-- x)
             0))
    

    Guard for (unary-- x):

    (acl2-numberp x)
    
    Notice that like all arithmetic functions, unary-- treats non-numeric inputs as 0.

    Calls of the macro - on one argument expand to calls of unary--; see -.




    acl2-sources/doc/HTML/UNARY-_slash_.html0000664002132200015000000000171512222333525017261 0ustar kaufmannacl2 UNARY-_slash_.html -- ACL2 Version 6.3

    UNARY-/

    reciprocal function
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-unary-/):

    (equal (unary-/ x)
           (if (and (acl2-numberp x)
                    (not (equal x 0)))
               (unary-/ x)
             0))
    

    Guard for (unary-/ x):

    (and (acl2-numberp x)
         (not (equal x 0)))
    
    Notice that like all arithmetic functions, unary-/ treats non-numeric inputs as 0.

    Calls of the macro / on one argument expand to calls of unary-/; see /.




    acl2-sources/doc/HTML/UNCERTIFIED-BOOKS.html0000664002132200015000000001064712222333516017375 0ustar kaufmannacl2 UNCERTIFIED-BOOKS.html -- ACL2 Version 6.3

    UNCERTIFIED-BOOKS

    invalid certificates and uncertified books
    Major Section:  BOOKS
    

    For relevant background see books, see certificate, and see portcullis.

    Include-book has a special provision for dealing with an uncertified book, i.e., a file with no certificate or an invalid certificate (i.e., one whose check sums describe files other than the ones actually read). In this case, a warning is printed and the book is otherwise processed much as though it were certified and had an open portcullis.

    If a book B.lisp is uncertified and a file B.port exists, then the forms in B.port are evaluated before the forms in B.lisp. Such a file B.port is typically created calling certify-book on book "B" with argument :write-port t, so that B.port contains the portcullis commands for B (the commands present in the world when that certification was attempted).

    Inclusion of uncertified books can be handy, but it can have disastrous consequences.

    The provision allowing uncertified books to be included can have disastrous consequences, ranging from hard lisp errors, to damaged memory, to quiet logical inconsistency.

    It is possible for the inclusion of an uncertified book to render the logic inconsistent. For example, one of its non-local events might be (defthm t-is-nil (equal t nil)). It is also possible for the inclusion of an uncertified book to cause hard errors or breaks into raw Common Lisp. For example, if the file has been edited since it was certified, it may contain too many open parentheses, causing Lisp to read past ``end of file.'' Similarly, it might contain non-ACL2 objects such as 3.1415 or ill-formed event forms that cause ACL2 code to break.

    Even if a book is perfectly well formed and could be certified (in a suitable extension of ACL2's initial world), its uncertified inclusion might cause Lisp errors or inconsistencies! For example, it might mention packages that do not exist in the host world, especially if the .port file (discussed above) does not exist from an earlier certification attempt. The portcullis of a certified book ensures that the correct defpkgs have been admitted, but if a book is read without actually raising its portcullis, symbols in the file, e.g., acl2-arithmetic::fn, could cause ``unknown package'' errors in Common Lisp. Perhaps the most subtle disaster occurs if the host world does have a defpkg for each package used in the book but the host defpkg imports different symbols than those required by the portcullis. In this case, it is possible that formulas which were theorems in the certified book are non-theorems in the host world, but those formulas can be read without error and will then be quietly assumed.

    In short, if you include an uncertified book, all bets are off regarding the validity of the future behavior of ACL2.

    That said, it should be noted that ACL2 is pretty tough and if errors don't occur, the chances are that deductions after the inclusion of an uncertified book are probably justified in the (possibly inconsistent) logical extension obtained by assuming the admissibility and validity of the definitions and conjectures in the book.




    acl2-sources/doc/HTML/UNION$.html0000664002132200015000000000616512222333525015715 0ustar kaufmannacl2 UNION$.html -- ACL2 Version 6.3

    UNION$

    elements of one list that are not elements of another
    Major Section:  ACL2-BUILT-INS
    

    General Forms:
    (union$ l1 l2 ... lk)
    (union$ l1 l2 ... lk :test 'eql) ; same as above
    (union$ l1 l2 ... lk :test 'eq)    ; same, but eq is equality test
    (union$ l1 l2 ... lk :test 'equal) ; same, but equal is equality test
    

    (Union$ x y) equals a list that contains both the members of x and the members of y. More precisely, the resulting list is the same as one would get by first deleting the members of y from x, and then concatenating the result to the front of y. The optional keyword, :TEST, has no effect logically, but provides the test (default eql) used for comparing members of the two lists.

    Union$ need not take exactly two arguments: (union$) is nil, (union$ x) is x, (union$ x y z ... :test test) is (union$ x (union$ y z ... :test test) :test test), and if :TEST is not supplied, then (union$ x y z ...) is (union$ x (union$ y z ...)). For the discussion below we restrict ourselves, then, to the cases (union$ x y) and (union$ x y :test test).

    The guard for a call of union$ (in the two cases just above) depends on the test. In all cases, both arguments must satisfy true-listp. If the test is eql, then one of the arguments must satisfy eqlable-listp. If the test is eq, then one of the arguments must satisfy symbol-listp.

    See equality-variants for a discussion of the relation between union$ and its variants:

    (union-eq x lst) is equivalent to (union$ x lst :test 'eq);

    (union-equal x lst) is equivalent to (union$ x lst :test 'equal).

    In particular, reasoning about any of these primitives reduces to reasoning about the function union-equal.

    Note that union-eq can take any number of arguments, in analogy to union$; indeed, (union-eq ...) expands to (union$ ... :test 'eq). However, union-equal is a function, not a macro, and takes exactly two arguments.

    Union$ is similar to the Common Lisp primitive union. However, Common Lisp does not specify the order of elements in the result of a call of union.




    acl2-sources/doc/HTML/UNION-EQ.html0000664002132200015000000000062512222333525016147 0ustar kaufmannacl2 UNION-EQ.html -- ACL2 Version 6.3

    UNION-EQ

    See union$.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/UNION-EQUAL.html0000664002132200015000000000063312222333525016510 0ustar kaufmannacl2 UNION-EQUAL.html -- ACL2 Version 6.3

    UNION-EQUAL

    See union$.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/UNION-THEORIES.html0000664002132200015000000000237612222333531017066 0ustar kaufmannacl2 UNION-THEORIES.html -- ACL2 Version 6.3

    UNION-THEORIES

    union two theories
    Major Section:  THEORIES
    

    Example:
    (union-theories (current-theory 'lemma3)
                    (theory 'arith-patch))
    
    General Form:
    (union-theories th1 th2)
    
    where th1 and th2 are theories (see theories). To each of the arguments there corresponds a runic theory. This function returns the union of those two runic theories, represented as a list and ordered chronologically.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.




    acl2-sources/doc/HTML/UNIVERSAL-THEORY.html0000664002132200015000000000570212222333531017332 0ustar kaufmannacl2 UNIVERSAL-THEORY.html -- ACL2 Version 6.3

    UNIVERSAL-THEORY

    all rules as of logical name
    Major Section:  THEORIES
    

    Examples:
    (universal-theory :here)
    (universal-theory 'lemma3)
    
    See logical-name.

    General Form:
    (universal-theory logical-name)
    
    Returns the theory consisting of all the runes that existed immediately after logical-name was introduced. See theories and see logical-name. The theory includes logical-name itself (if there is a rule by that name). (Note that since some events do not introduce rules (e.g., defmacro, defconst or defthm with :rule-classes nil), the universal-theory does not necessarily include a rune for every event name.) The universal-theory is very long and you will probably regret printing it.

    You may experience a fencepost problem in deciding which logical-name to use. Deflabel can always be used to mark unambiguously for future reference a particular point in the development of your theory. This is convenient because deflabel does not introduce any rules and hence it doesn't matter if you count it as being in the interval or not. The order of events in the vicinity of an encapsulate is confusing. See encapsulate.

    This ``function'' is actually a macro that expands to a term mentioning the single free variable world. When theory expressions are evaluated by in-theory or the :in-theory hint, world is bound to the current ACL2 world.

    Also see current-theory. Current-theory is much more commonly used than universal-theory. The former includes only the enabled runes as of the given logical-name, which is probably what you want, while the latter includes disabled ones as well.




    acl2-sources/doc/HTML/UNMEMOIZE.html0000664002132200015000000000312512222333517016323 0ustar kaufmannacl2 UNMEMOIZE.html -- ACL2 Version 6.3

    UNMEMOIZE

    turn off memoization for the specified function
    Major Section:  EVENTS
    

    Example:
    (unmemoize 'foo) ; turn off memoization of foo
    
    
    

    Some Related Topics

    General Form: (unmemoize fn)

    where fn evaluates to a function symbol that is currently memoized; see memoize. An exception is that as with memoize, fn may evaluate to the name of a macro that is associated with such a function symbol; see macro-aliases-table.

    Calls of this macro generate events of the form (table memoize-table fn nil). When successful, the returned value is of the form (mv nil function-symbol state).

    To remove the effects of all memoize events, evaluate: (clear-memo-table). To save and restore memoization, see save-and-clear-memoization-settings and see restore-memoization-settings.




    acl2-sources/doc/HTML/UNMONITOR.html0000664002132200015000000000235212222333516016345 0ustar kaufmannacl2 UNMONITOR.html -- ACL2 Version 6.3

    UNMONITOR

    to stop monitoring a rule name
    Major Section:  BREAK-REWRITE
    

    Examples:
    (unmonitor '(:rewrite assoc-of-app))
    :unmonitor (:rewrite assoc-of-app)
    :unmonitor :all
    
    General Forms:
    (unmonitor rune)
    (unmonitor :all)
    
    Here, rune is a rune that is currently among those with break points installed. This function removes the break.

    Subtle point: Because you may want to unmonitor a ``rune'' that is no longer a rune in the current ACL2 world, we don't actually check this about rune. Instead, we simply check that rune is a consp beginning with a keywordp. That way, you'll know you've made a mistake if you try to :unmonitor binary-append instead of :unmonitor (:definition binary-append), for example.




    acl2-sources/doc/HTML/UNSAVE.html0000664002132200015000000000147612222333526015763 0ustar kaufmannacl2 UNSAVE.html -- ACL2 Version 6.3

    UNSAVE

    remove a proof-checker state
    Major Section:  PROOF-CHECKER
    

    Example:
    (unsave assoc-of-append)
    
    General Form:
    (unsave name)
    
    Eliminates the association of a proof-checker state with name. See unsave or see acl2-pc::unsave.

    Also see acl2-pc::save and see retrieve.




    acl2-sources/doc/HTML/UNSIGNED-BYTE-P.html0000664002132200015000000000216012222333525017122 0ustar kaufmannacl2 UNSIGNED-BYTE-P.html -- ACL2 Version 6.3

    UNSIGNED-BYTE-P

    recognizer for natural numbers that fit in a specified bit width
    Major Section:  ACL2-BUILT-INS
    

    (Unsigned-byte-p bits x) is T when bits is a positive integer and x is a nonnegative integer that fits into a bit-width of bits, i.e., 0 <= x < 2^bits.

    Note that a type-spec of (unsigned-byte i) for a variable x in a function's declare form translates to a guard condition of (unsigned-byte-p i x).

    The guard for unsigned-byte-p is T.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/UNSUPPORTED-PARALLELISM-FEATURES.html0000664002132200015000000000527212222333523021564 0ustar kaufmannacl2 UNSUPPORTED-PARALLELISM-FEATURES.html -- ACL2 Version 6.3

    UNSUPPORTED-PARALLELISM-FEATURES

    ACL2 features not supported in ACL2(p)
    Major Section:  PARALLELISM
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism. See parallelism-tutorial for an introduction to parallel programming in ACL2.

    For proof features of ACL2 that are not yet supported when parallel execution is enabled for the primary ACL2 proof process, generally known as ``the waterfall'', see unsupported-waterfall-parallelism-features.

    Please note that this topic discusses ACL2 features that are disabled when using ACL2(p) (see compiling-acl2p). These features are disabled regardless of whether waterfall parallelism is enabled.

    Calls of observation-cw simply convert to calls of cw, so suppressing observations (see set-inhibit-output-lst) will not suppress these messages.

    Memoization is not supported when executing in parallel. See Unsupported-waterfall-parallelism-features for memoization details related to waterfall parallelism.

    Since, as of April 2012, garbage collection is inherently sequential, ACL2(p) minimizes the use of garbage collection by setting a high garbage collection threshold. As a result, ACL2(p) is not expected to perform well on machines with less memory than this threshold (1 gigabyte, as of April 2012).

    In CCL, the underlying parallel execution engine is tuned for the number of CPU cores (or hardware threads) actually available in the machine. SBCL and LispWorks are tuned for a machine with 16 CPU cores.

    CCL is considered to be the ``flagship Lisp'' for parallel execution in ACL2. The SBCL and LispWorks implementations are thought to be generally stable. However, due to their relatively less common use, the SBCL and LispWorks implementations are likely less robust than the CCL implementation.

    The time-tracker utility is a no-op for ACL2(p).




    acl2-sources/doc/HTML/UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES.html0000664002132200015000000002301112222333523023072 0ustar kaufmannacl2 UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES.html -- ACL2 Version 6.3

    UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES

    proof features not supported with waterfall-parallelism enabled
    Major Section:  PARALLEL-PROOF
    

    For a general introduction to ACL2(p), an experimental extension of ACL2 that supports parallel execution and proof, see parallelism. Please note that although this extension is usable and, we hope, robust in its behavior, there are still known issues to address beyond those listed explicitly below. While we expect ACL2(p) to perform correctly, it may never have the same level of attention to correctness as is given to ACL2; see parallelism, specifically the ``IMPORTANT NOTE'' there.

    Below we list proof features of ACL2 that are not yet supported when parallel execution is enabled for the primary ACL2 proof process, generally known as ``the waterfall'', typically by calling set-waterfall-parallelism.

    Please note that this topic is limited to the case that such waterfall parallelism is enabled. We believe that all ACL2 proof procedures are supported when waterfall parallelism is disabled, even in executables that support parallelism (see compiling-acl2p).

    Without a trust tag (see defttag): We support clause-processors, computed-hints, and custom-keyword-hints that do not modify state, but we do not permit override-hints, regardless of whether they modify state. With a trust tag, the user can use clause-processors that modify state and can also use override-hints (see set-waterfall-parallelism-hacks-enabled for a convenient mechanism for adding a trust tag). See error-triples-and-parallelism for a discussion of how to avoid modifying state in such situations. Regardless of whether a trust tag is active: We do not support checkers of custom-keyword-hints to be anything but the default checker.

    GNU Make versions 3.81 and 3.82 formerly caused a lot of problems (version 3.80 somewhat less so), at least on Linux, when certifying books with ACL2 built on a host Lisp of CCL using `make'. CCL was updated around March 23, 2011 to fix this problem, so if you get segfaults (for example) with CCL, try updating your CCL installation.

    Book certification should generally work but may present some issues, including the following.

    o The standard `make'-based process for book certification will not use waterfall-parallelism, which is disabled by default (even when compiling-acl2p by using the ACL2_PAR flag). See books-certification and see books-certification-classic, which explain that acl2-customization files are ignored during that process unless specified explicitly on the command line or in the environment.

    o A book certified with ACL2(p) might subsequently cause an error when included with ACL2. As of this writing, however, we have only seen this for a book in which deftheory-static is used.

    o In general, ACL2(p) is primarily intended to support more rapid interactive development. While we are unaware of an unsoundness likely to affect an ACL2(p) user, we suggest using ACL2 for final book certification, rather than ACL2(p), to lower the risk of unsound book certification.

    Proof output can contain repeated printing of the same subgoal name.

    Gag-mode isn't officially supported, although it has proved helpful to use ACL2(p) in conjunction with (set-gag-mode t) (because this setting suppresses the output that occurs outside the waterfall). This being said, ACL2(p) also prints key checkpoints (for example see introduction-to-key-checkpoints), but with a notion of ``key checkpoint'' that does not take into account whether the goal is later proved by induction. See acl2p-key-checkpoints for further explanation of these key checkpoints. Note that pso is also not supported.

    The :brr utility is not supported.

    The accumulated-persistence utility is not supported.

    Tracking for forward-chaining-reports is not supported (see set-fc-criteria).

    Time limits (see with-prover-time-limit) aren't supported.

    The timing information printed at the end of a proof attempt, which is intended to represent cpu time (not wall-clock time), may be somewhat inaccurate when waterfall-parallelism is non-nil. Consider using time$ to obtain timing information.

    The use of wormholes is not recommended, as there may be race conditions.

    Output specific to :OR hints is disabled.

    Proof trees are likely not to work as originally designed.

    The use of set-inhibit-output-lst may not fully inhibit proof output.

    Reporting of splitter rules is currently unsupported when waterfall-parallelism is on.

    Interrupting a proof attempt is not yet properly supported. At a minimum, interrupts are trickier with waterfall parallelism enabled. For one, the user typically needs to issue the interrupt twice before the proof attempt is actually interrupted. Additionally, on rare occasions the theorem is registered as proved, even though the prover did not finish the proof. If this occurs, issue a :u (see ubt) and you will likely be at a stable state.

    Also with regards to interrupting a proof attempt, sometimes the user may need to issue a :q and lp to reset properly the parallelism implementation to a stable state. The primary symptom that the user is experiencing this issue is that threads will continue to compute in the background, even though there should be no proof attempt in progress. The user can observe this symptom by examining the CPU utilization of their ACL2 process, for example on Linux/Unix with the shell process top. Lisp usage greater than a few percent is indicative of this problem.

    Because of how ACL2 arrays are designed, the user may find that, in practice, ACL2 arrays work (but perhaps with some slow-array-warning messages). However, we are aware of race conditions that can cause problems.

    Instead of dynamically monitoring rewrites, dmr instead dynamically outputs information helpful for debugging the performance of proof parallelism. The instructions concerning how to see this debugging information are the same as the instructions for enabling dmr mode.

    If you are working with LispWorks 6.0 or 6.0.1, then you may see messages about misaligned conses. The state of the system may be corrupted after such a message has been printed. This LispWorks bug is fixed in LispWorks 6.1.

    The waterfall parallelism mode :resource-and-timing-based is not fully supported when the host Lisp is other than CCL. It may work, but we have not attempted to address a potential race condition.

    Proof output for splitter rules (see splitter) is currently unsupported when waterfall-parallelism is enabled.

    (Comment for ACL2(h) users; see hons-and-memoization.) Memoization may not work as intended when executing in parallel (including the waterfall). In an effort to be helpful to the user, the functions automatically memoized by ACL2(h) are unmemoized when setting waterfall parallelism to anything but nil. Those exact functions are again memoized once waterfall parallelism is disabled. Additionally, any functions memoized within the ACL2 loop (by a call of memoize) are also unmemoized when enabling waterfall parallelism and once again memoized when disabling waterfall parallelism. This is implemented by returning the memoization state to what it was before enabling waterfall parallelism. As such, the user should be aware that any changes made to the memoization state while waterfall parallelism is enabled will be lost once waterfall parallelism is disabled.




    acl2-sources/doc/HTML/UNTRACE$.html0000664002132200015000000000211412222333531016111 0ustar kaufmannacl2 UNTRACE$.html -- ACL2 Version 6.3

    UNTRACE$

    untrace functions
    Major Section:  TRACE
    

    Examples:
    (untrace$)         ; untrace all functions previously
                       ; traced (e.g. with trace$ or trace!)
    (untrace$ foo bar) ; as above, except only untrace foo and bar
    
    General Forms:
    (untrace$)                 ; untrace all (as noted above)
    (untrace$ fn1 fn2 ... fnk) ; untrace the indicated functions
    
    where the fni were previously traced (e.g. with trace$ or trace!).

    Untrace$ undoes the effect of trace$. See trace$. The value returned by untrace$ gives the list of functions for which tracing is being removed.




    acl2-sources/doc/HTML/UNTRANS-TABLE.html0000664002132200015000000000201512222333531016723 0ustar kaufmannacl2 UNTRANS-TABLE.html -- ACL2 Version 6.3

    UNTRANS-TABLE

    associates a function symbol with a macro for printing user-level terms
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    ACL2 !>(untrans-table (w state))
    ((BINARY-+ + . T)
     (BINARY-* * . T)
     (BINARY-APPEND APPEND . T)
     (BINARY-LOGAND LOGAND . T)
     (BINARY-LOGIOR LOGIOR . T)
     (BINARY-LOGXOR LOGXOR . T)
     (BINARY-LOGEQV LOGEQV . T)
     (BINARY-POR POR . T)
     (BINARY-PAND PAND . T))
    

    See table for a general discussion of tables.

    See add-macro-fn for a more general discussion of this table and for a way to associate a macro name with a function name in theory events.




    acl2-sources/doc/HTML/UNTRANSLATE.html0000664002132200015000000000070712222333525016555 0ustar kaufmannacl2 UNTRANSLATE.html -- ACL2 Version 6.3

    UNTRANSLATE

    See user-defined-functions-table.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/UPDATE-NTH.html0000664002132200015000000000272212222333525016365 0ustar kaufmannacl2 UPDATE-NTH.html -- ACL2 Version 6.3

    UPDATE-NTH

    modify a list by putting the given value at the given position
    Major Section:  ACL2-BUILT-INS
    

    (Update-nth key val l) returns a list that is the same as the list l, except that the value at the 0-based position key (a natural number) is val.

    If key is an integer at least as large as the length of l, then l will be padded with the appropriate number of nil elements, as illustrated by the following example.

    ACL2 !>(update-nth 8 'z '(a b c d e))
    (A B C D E NIL NIL NIL Z)
    
    We have the following theorem.
    (implies (and (true-listp l)
                  (integerp key)
                  (<= 0 key))
             (equal (length (update-nth key val l))
                    (if (< key (length l))
                        (length l)
                      (+ 1 key))))
    

    The guard of update-nth requires that its first (position) argument is a natural number and its last (list) argument is a true list.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/UPPER-CASE-P.html0000664002132200015000000000170312222333525016553 0ustar kaufmannacl2 UPPER-CASE-P.html -- ACL2 Version 6.3

    UPPER-CASE-P

    recognizer for upper case characters
    Major Section:  ACL2-BUILT-INS
    

    (Upper-case-p x) is true if and only if x is an upper case character, i.e., a member of the list #\A, #\B, ..., #\Z.

    The guard for upper-case-p requires its argument to be a standard character (see standard-char-p).

    Upper-case-p is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/USE.html0000664002132200015000000000065012222333522015403 0ustar kaufmannacl2 USE.html -- ACL2 Version 6.3

    USE

    hints keyword :USE
    Major Section:  MISCELLANEOUS
    

    See hints.




    acl2-sources/doc/HTML/USER-DEFINED-FUNCTIONS-TABLE.html0000664002132200015000000000733212222333531020760 0ustar kaufmannacl2 USER-DEFINED-FUNCTIONS-TABLE.html -- ACL2 Version 6.3

    USER-DEFINED-FUNCTIONS-TABLE

    an advanced table used to replace certain system functions
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    (table user-defined-functions-table 'untranslate-preprocess 'my-preprocess)
    (table user-defined-functions-table 'untranslate 'my-untranslate)
    

    This feature should perhaps only be used by advanced users who have a thorough understanding of the system functions being replaced. There are currently two ways a user can affect the way ACL2 prints terms.

    The first example associates the user-defined function symbol my-preprocess with untranslate-preprocess. As a result, when ACL2 prints a term, say during a proof, using its so-called ``untranslate'' process the first thing it does is to call my-preprocess on two arguments: that term and the current ACL2 logical world. If the call produces a non-nil result, then that result is passed to the untranslate process.

    The second example associates the user-defined function symbol my-untranslate with the built-in function symbol untranslate. As a result, the code for my-untranslate will be run whenever the untranslate process is run. The formals of the two functions must agree and must not contain any stobj names. Note that these overrides fail to occur upon guard violations and some other evaluation errors.

    The untranslate-preprocess approach may suffice for most cases in which a user wants to modify the way output is produced by the theorem prover. We present an example immediately below, but see community book books/misc/untranslate-patterns.lisp for a more elaborate example. If the untranslate-preprocess approach does not seem sufficient for your purposes, you are invited to look at community book books/misc/rtl-untranslate.lisp for an example of user-defined untranslate (i.e., following the second example displayed above).

    Suppose you have a large constant that you would prefer not to see in proofs. For example, you may have submitted the following definition (but imagine a much larger constant, say, a list of length 1,000,000).

    (defconst *a* '(a b c d))
    
    If you submit the following (silly) theorem
    (thm (equal (cons x *a*) (car (cons yyy zzz))))
    
    then you will see the following output:
    (EQUAL (CONS X '(A B C D)) YYY).
    
    If *a* had represented a much larger structure, we would wish we could see the following instead.
    (EQUAL (CONS X *A*) YYY)
    
    That can be accomplished as follows. First we make the following definition.
    (defun my-preprocess (term wrld)
      (declare (ignore wrld))
      (if (equal term (list 'quote *a*))
          '*a*
        nil))
    
    Now we submit the following table event.
    (table user-defined-functions-table
           'untranslate-preprocess
           'my-preprocess)
    
    This will install my-preprocess as a preprocessor before the normal untranslation routine is applied to printing a term. When the untranslation routine encounters the constant (QUOTE (A B C D)), it will replace it with *a*, and the usual untranlation routine will print this as *A*.




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-1.html0000664002132200015000000000277412222333522020104 0ustar kaufmannacl2 USING-COMPUTED-HINTS-1.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-1

    Driving Home the Basics
    Major Section:  MISCELLANEOUS
    

    The common hint

    ("Subgoal 3.2.1''" :use lemma42)
    
    has the same effect as the computed hint
    (if (equal id '((0) (3 2 1) . 2))
        '(:use lemma42)
        nil)
    
    which, of course, is equivalent to
    (and (equal id '((0) (3 2 1) . 2))
         '(:use lemma42))
    
    which is also equivalent to the computed hint
    my-special-hint
    
    provided the following defun has first been executed
    (defun my-special-hint (id clause world)
      (declare (xargs :mode :program)
               (ignore clause world))
      (if (equal id '((0) (3 2 1) . 2))
          '(:use lemma42)
          nil))
    
    It is permitted for the defun to be in :LOGIC mode (see defun-mode) also.

    Just to be concrete, the following three events all behave the same way (if my-special-hint is as above):

    (defthm main (big-thm a b c)
      :hints (("Subgoal 3.2.1''" :use lemma42)))
    
    (defthm main (big-thm a b c)
      :hints ((and (equal id '((0) (3 2 1) . 2)) '(:use lemma42))))
    
    (defthm main (big-thm a b c)
      :hints (my-special-hint))
    




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-2.html0000664002132200015000000001076712222333522020106 0ustar kaufmannacl2 USING-COMPUTED-HINTS-2.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-2

    One Hint to Every Top-Level Goal in a Forcing Round
    Major Section:  MISCELLANEOUS
    

    Suppose the main proof completes with a forcing round on three subgoals, "[1]Subgoal 3", "[1]Subgoal 2", and "[1]Subgoal 1". Suppose you wish to :use lemma42 in all top-level goals of the first forcing round. This can be done supplying the hint

    (if test '(:use lemma42) nil),
    
    where test is an expression that returns t when ID is one of the clause ids in question.
        goal-spec     (parse-clause-id goal-spec)
    
    "[1]Subgoal 3"        ((1) (3) . 0)
    "[1]Subgoal 2"        ((1) (2) . 0)
    "[1]Subgoal 1"        ((1) (1) . 0)
    
    Recall (see clause-identifier) that parse-clause-id maps from a goal spec to a clause id, so you can use that function on the goal specs printed in the failed proof attempt to determine the clause ids in question.

    So one acceptable test is

    (member-equal id '(((1) (3) . 0)
                       ((1) (2) . 0)
                       ((1) (1) . 0)))
    
    or you could use parse-clause-id in your computed hint if you don't want to see clause ids in your script:
    (or (equal id (parse-clause-id "[1]Subgoal 3"))
        (equal id (parse-clause-id "[1]Subgoal 2"))
        (equal id (parse-clause-id "[1]Subgoal 1")))
    
    or you could use the inverse function (see clause-identifier):
    (member-equal (string-for-tilde-@-clause-id-phrase id)
                  '("[1]Subgoal 3"
                    "[1]Subgoal 2"
                    "[1]Subgoal 1"))
    

    Recall that what we've shown above are the tests to use in the computed hint. The hint itself is (if test '(:use lemma42) nil) or something equivalent like (and test '(:use lemma42)).

    The three tests above are all equivalent. They suffer from the problem of requiring the explicit enumeration of all the goal specs in the first forcing round. A change in the script might cause more forced subgoals and the ones other than those enumerated would not be given the hint.

    You could write a test that recognizes all first round top-level subgoals no matter how many there are. Just think of the programming problem: how do I recognize all the clause id's of the form ((1) (n) . 0)? Often you can come to this formulation of the problem by using parse-clause-id on a few of the candidate goal-specs to see the common structure. A suitable test in this case is:

    (and (equal (car id) '(1))     ; forcing round 1, top-level (pre-induction)
         (equal (len (cadr id)) 1) ; Subgoal n (not Subgoal n.i ...)
         (equal (cddr id) 0))      ; no primes
    

    The test above is ``overkill'' because it recognizes precisely the clause ids in question. But recall that once a computed hint is used, it is (by default) removed from the hints available to the children of the clause. Thus, we can widen the set of clause ids recognized to include all the children without worrying that the hint will be applied to those children.

    In particular, the following test supplies the hint to every top-level goal of the first forcing round:

    (equal (car id) '(1))
    
    You might worry that it would also supply the hint to the subgoal produced by the hint -- the cases we ruled out by the ``overkill'' above. But that doesn't happen since the hint is unavailable to the children. You could even write:
    (equal (car (car id)) 1)
    
    which would supply the hint to every goal of the form "[1]Subgoal ..." and again, because we see and fire on the top-level goals first, we will not fire on, say, "[1]Subgoal *1.3/2", i.e., the id '((1 1 3) (2) . 0) even though the test recognizes that id.

    Finally, the following test supplies the hint to every top-level goal of every forcing round (except the 0th, which is the ``gist'' of the proof, not ``really'' a forcing round):

    (not (equal (car (car id)) 0))
    

    Recall again that in all the examples above we have exhibited the test in a computed hint of the form (if test '(:key1 val1 ...) nil).




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-3.html0000664002132200015000000001231612222333522020077 0ustar kaufmannacl2 USING-COMPUTED-HINTS-3.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-3

    Hints as a Function of the Goal (not its Name)
    Major Section:  MISCELLANEOUS
    

    Sometimes it is desirable to supply a hint whenever a certain term arises in a conjecture. For example, suppose we have proved

    (defthm all-swaps-have-the-property
       (the-property (swap x))
       :rule-classes nil)
    
    and suppose that whenever (SWAP A) occurs in a goal, we wish to add the additional hypothesis that (THE-PROPERTY (SWAP A)). Note that this is equivalent supplying the hint
    (if test
        '(:use (:instance all-swaps-have-the-property (x A)))
        nil)
    
    where test answers the question ``does the clause contain (SWAP A)?'' That question can be asked with (occur-lst '(SWAP A) clause). Briefly, occur-lst takes the representation of a translated term, x, and a list of translated terms, y, and determines whether x occurs as a subterm of any term in y. (By ``subterm'' here we mean proper or improper, e.g., the subterms of (CAR X) are X and (CAR X).)

    Thus, the computed hint:

    (if (occur-lst '(swap a) clause)
        '(:use (:instance all-swaps-have-the-property (x A)))
        nil)
    
    will add the hypothesis (THE-PROPERTY (SWAP A)) to every goal containing (SWAP A) -- except the children of goals to which the hypothesis was added.

    A COMMON MISTAKE users are likely to make is to forget that they are dealing with translated terms. For example, suppose we wished to look for (SWAP (LIST 1 A)) with occur-lst. We would never find it with

    (occur-lst '(SWAP (LIST 1 A)) clause)
    
    because that presentation of the term contains macros and other abbreviations. By using :trans (see trans) we can obtain the translation of the target term. Then we can look for it with:
    (occur-lst '(SWAP (CONS '1 (CONS A 'NIL))) clause)
    
    Note in particular that you must
    * eliminate all macros and
    * explicitly quote all constants.
    
    We recommend using :trans to obtain the translated form of the terms in which you are interested, before programming your hints.

    An alternative is to use the expression (prettyify-clause clause nil nil) in your hint to convert the current goal clause into the s-expression that is actually printed. For example, the clause

    ((NOT (CONSP X)) (SYMBOLP Y) (EQUAL (CONS '1 (CAR X)) Y))
    
    ``prettyifies'' to
    (IMPLIES (AND (CONSP X)
                  (NOT (SYMBOLP Y)))
             (EQUAL (CONS 1 (CAR X)) Y))
    
    which is what you would see printed by ACL2 when the goal clause is that shown.

    However, if you choose to convert your clauses to prettyified form, you will have to write your own explorers (like our occur-lst), because all of the ACL2 term processing utilities work on translated and/or clausal forms. This should not be taken as a terrible burden. You will, at least, gain the benefit of knowing what you are really looking for, because your explorers will be looking at exactly the s-expressions you see at your terminal. And you won't have to wade through our still undocumented term/clause utilities. The approach will slow things down a little, since you will be paying the price of independently consing up the prettyified term.

    We make one more note on this example. We said above that the computed hint:

    (if (occur-lst '(swap a) clause)
        '(:use (:instance all-swaps-have-the-property (x A)))
        nil)
    
    will add the hypothesis (THE-PROPERTY (SWAP A)) to every goal containing (SWAP A) -- except the children of goals to which the hypothesis was added.

    It bears noting that the subgoals produced by induction and top-level forcing round goals are not children. For example, suppose the hint above fires on "Subgoal 3" and produces, say, "Subgoal 3'". Then the hint will not fire on "Subgoal 3'" even though it (still) contains (SWAP A) because "Subgoal 3'" is a child of a goal on which the hint fired.

    But now suppose that "Subgoal 3'" is pushed for induction. Then the goals created by that induction, i.e., the base case and induction step, are not considered children of "Subgoal 3'". All of the original hints are available.

    Alternatively, suppose that "Subgoal 3' is proved but forces some other subgoal, "[1]Subgoal 1" which is attacked in Forcing Round 1. That top-level forced subgoal is not a child. All the original hints are available to it. Thus, if it contains (SWAP A), the hint will fire and supply the hypothesis, producing "[1]Subgoal 1'". This may be unnecessary, as the hypothesis might already be present in "[1]Subgoal 1". In this case, no harm is done. The hint won't fire on "[1]Subgoal 1" because it is a child of "[1]Subgoal 1" and the hint fired on that.




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-4.html0000664002132200015000000001451712222333522020105 0ustar kaufmannacl2 USING-COMPUTED-HINTS-4.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-4

    Computing the Hints
    Major Section:  MISCELLANEOUS
    

    So far we have used computed hints only to compute when a fixed set of keys and values are to be used as a hint. But computed hints can, of course, compute the set of keys and values. You might, for example, write a hint that recognizes when a clause ``ought'' to be provable by a :BDD hint and generate the appropriate hint. You might build in a set of useful lemmas and check to see if the clause is provable :BY one of them. You can keep all function symbols disabled and use computed hints to compute which ones you want to :EXPAND. In general, you can write a theorem prover for use in your hints, provided you can get it to do its job by directing our theorem prover.

    Suppose for example we wish to find every occurrence of an instance of (SWAP x) and provide the corresponding instance of ALL-SWAPS-HAVE-THE-PROPERTY. Obviously, we must explore the clause looking for instances of (SWAP x) and build the appropriate instances of the lemma. We could do this in many different ways, but below we show a general purpose set of utilities for doing it. The functions are not defined in ACL2 but could be defined as shown.

    Our plan is: (1) Find all instances of a given pattern (term) in a clause, obtaining a set of substitutions. (2) Build a set of :instance expressions for a given lemma name and set of substitutions. (3) Generate a :use hint for those instances when instances are found.

    The pair of functions below find all instances of a given pattern term in either a term or a list of terms. The functions each return a list of substitutions, each substitution accounting for one of the matches of pat to a subterm. At this level in ACL2 substitutions are lists of pairs of the form (var . term). All terms mentioned here are presumed to be in translated form.

    The functions take as their third argument a list of substitutions accumulated to date and add to it the substitutions produced by matching pat to the subterms of the term. We intend this accumulator to be nil initially. If the returned value is nil, then no instances of pat occurred.

    (mutual-recursion
    
    (defun find-all-instances (pat term alists)
     (declare (xargs :mode :program))
     (mv-let
      (instancep alist)
      (one-way-unify pat term)
      (let ((alists (if instancep (add-to-set-equal alist alists) alists)))
        (cond
         ((variablep term) alists)
         ((fquotep term) alists)
         (t (find-all-instances-list pat (fargs term) alists))))))
    
    (defun find-all-instances-list (pat list-of-terms alists)
     (declare (xargs :mode :program))
     (cond
      ((null list-of-terms) alists)
      (t (find-all-instances pat
                             (car list-of-terms)
                             (find-all-instances-list pat
                                                      (cdr list-of-terms)
                                                      alists))))))
    

    Caveat: The following aside has nothing to do with computed hints. Does an instance of (CAR (CDR x)) occur in ((LAMBDA (V) (CAR V)) (CDR A))? It does if one beta-reduces the lambda-expression to (CAR (CDR A)); the appropriate substitution is to replace x by A. But the definition of find-all-instances above does not find this instance because it does not do beta-reduction.

    We now turn our attention to converting a list of substitutions into a list of lemma instances, each of the form

    (:INSTANCE name (var1 term1) ... (vark termk))
    
    as written in :use hints. In the code shown above, substitutions are lists of pairs of the form (var . term), but in lemma instances we must write ``doublets.'' So here we show how to convert from one to the other:
    (defun pairs-to-doublets (alist)
      (declare (xargs :mode :program))
      (cond ((null alist) nil)
            (t (cons (list (caar alist) (cdar alist))
                     (pairs-to-doublets (cdr alist))))))
    

    Now we can make a list of lemma instances:

    (defun make-lemma-instances (name alists)
      (declare (xargs :mode :program))
      (cond
       ((null alists) nil)
       (t (cons (list* :instance name (pairs-to-doublets (car alists)))
                (make-lemma-instances name (cdr alists))))))
    

    Finally, we can package it all together into a hint function. The function takes a pattern, pat, which must be a translated term, the name of a lemma, name, and a clause. If some instances of pat occur in clause, then the corresponding instances of name are :USEd in the computed hint. Otherwise, the hint does not apply.

    (defun add-corresponding-instances (pat name clause)
      (declare (xargs :mode :program))
      (let ((alists (find-all-instances-list pat clause nil)))
        (cond
         ((null alists) nil)
         (t (list :use (make-lemma-instances name alists))))))
    
    The design of this particular hint function makes it important that the variables of the pattern be the variables of the named lemma and that all of the variables we wish to instantiate occur in the pattern. We could, of course, redesign it to allow ``free variables'' or some sort of renaming.

    We could now use this hint as shown below:

    (defthm ... ...
      :hints ((add-corresponding-instances
               '(SWAP x)
               'ALL-SWAPS-HAVE-THE-PROPERTY
               clause)))
    
    The effect of the hint above is that any time a clause arises in which any instance of (SWAP x) appears, we add the corresponding instance of ALL-SWAPS-HAVE-THE-PROPERTY. So for example, if Subgoal *1/3.5 contains the subterm (SWAP (SWAP A)) then this hint fires and makes the system behave as though the hint:
    ("Subgoal *1/3.5"
     :USE ((:INSTANCE ALL-SWAPS-HAVE-THE-PROPERTY (X A))
           (:INSTANCE ALL-SWAPS-HAVE-THE-PROPERTY (X (SWAP A)))))
    
    had been present.




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-5.html0000664002132200015000000000521412222333522020100 0ustar kaufmannacl2 USING-COMPUTED-HINTS-5.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-5

    Debugging Computed Hints
    Major Section:  MISCELLANEOUS
    

    We have found that it is sometimes helpful to define hints so that they print out messages to the terminal when they fire, so you can see what hint was generated and which of your computed hints did it.

    To that end we have defined a macro we sometimes use. Suppose you have a :hints specification such as:

    :hints (computed-hint-fn (hint-expr id))
    
    If you defmacro the macro below you could then write instead:
    :hints ((show-hint computed-hint-fn 1)
            (show-hint (hint-expr id) 2))
    
    with the effect that whenever either hint is fired (i.e., returns non-nil), a message identifying the hint by the marker (1 or 2, above) and the non-nil value is printed.

    (defmacro show-hint (hint &optional marker)
      (cond
       ((and (consp hint)
             (stringp (car hint)))
        hint)
       (t
        `(let ((marker ,marker)
               (ans ,(if (symbolp hint)
                         `(,hint id clause world stable-under-simplificationp)
                       hint)))
           (if ans
               (prog2$
                (cw "~%***** Computed Hint~#0~[~/ (from hint ~x1)~]~%~x2~%~%"
                    (if (null marker) 0 1)
                    marker
                    (cons (string-for-tilde-@-clause-id-phrase id)
                          ans))
                ans)
             nil)))))
    
    Note that when show-hint is applied to a hint that is a symbol, e.g., computed-hint-fn, it applies the symbol to the four computed-hint arguments: id, clause, world, and stable-under-simplificationp. If computed-hint-fn is of arity 3 the code above would cause an error. One way to avoid it is to write
    :hints ((show-hints (computed-hint-fn id clause world) 1)
            (show-hint (hint-expr id) 2)).
    
    If you only use computed hints of arity 3, you might eliminate the occurrence of stable-under-simplificationp in the definition of show-hint above.

    Putting a show-hint around a common hint has no effect. If you find yourself using this utility let us know and we'll consider putting it into the system itself. But it does illustrate that you can use computed hints to do unusual things.




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-6.html0000664002132200015000000002462412222333522020107 0ustar kaufmannacl2 USING-COMPUTED-HINTS-6.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-6

    Using the computed-hint-replacement feature
    Major Section:  MISCELLANEOUS
    

    So far none of our computed hints have used the :COMPUTED-HINT-REPLACEMENT feature. We now illustrate that.

    The :computed-hint-replacement feature can easily lead to loops. So as you experiment with the examples in this section and your own hints using this feature, be ready to interrupt the theorem prover and abort!

    A non-looping use of the :computed-hint-replacement feature would be a hint like this:

    (if (certain-terms-present clause)
        '(:computed-hint-replacement t
          :in-theory (enable lemma25))
        '(:computed-hint-replacement t
          :in-theory (disable lemma25)))
    
    In this hint, if certain terms are present in clause, as determined by the function with the obvious name (here undefined), then this hint enables lemma25 and otherwise disables it. Lemma25 might be a very expensive lemma, e.g., one that matches frequently and has an expensive and rarely established hypothesis. One might wish it enabled only under certain conditions. Recall that theories are inherited by children. So once lemma25 is enabled it ``stays'' enabled for the children, until disabled; and vice versa. If the :computed-hint-replacement feature were not present and computed hints were always deleted after they had been used, then lemma25 would be left enabled (or disabled) for all the childen produced by the first firing of the hint. But with the arrangement here, every subgoal gets a theory deemed suitable by the hint, and the hint persists.

    Now we will set up a toy to allow us to play with computed hints to understand them more deeply. To follow the discussion it is best to execute the following events.

    (defstub wrapper (x) t)
    (defaxiom wrapper-axiom (wrapper x) :rule-classes nil)
    
    Now submit the following event and watch what happens.
    (thm (equal u v)
      :hints (`(:use (:instance wrapper-axiom (x a)))))
    
    The theorem prover adds (wrapper a) to the goal and then abandons the proof attempt because it cannot prove the subgoal. Since the computed hint is deleted upon use, the hint is not applied to the subgoal (i.e., the child of the goal).

    What happens if we do the following?

    (thm (equal u v)
      :hints (`(:computed-hint-replacement t
                :use (:instance wrapper-axiom (x a)))))
    

    As one might expect, this loops forever: The hint is applied to the child and adds the hypothesis again. When the hint fires, nothing is actually changed, since (wrapper a) is already in the subgoal.

    So let's change the experiment a little. Let's make the hint add the hypothesis (wrapper p) where p is the first literal of the clause. This is silly but it allows us to explore the behavior of computed hints a little more.

    (thm (equal u v)
      :hints (`(:use (:instance wrapper-axiom (x ,(car clause))))))
    
    So in this case, the theorem prover changes the goal to
    (IMPLIES (WRAPPER (EQUAL U V)) (EQUAL U V))
    
    which then simplifies to
    (IMPLIES (WRAPPER NIL) (EQUAL U V))
    
    because the concluding equality can be assumed false in the hypothesis (e.g., think of the contrapositive version). Nothing else happens because the hint has been removed and so is not applicable to the child.

    Now consider the following -- and be ready to interrupt it and abort!

    (thm (equal u v)
      :hints (`(:computed-hint-replacement t
                :use (:instance wrapper-axiom (x ,(car clause))))))
    
    This time the hint is not removed and so is applied to the child. So from Goal we get
    Goal'
    (IMPLIES (WRAPPER (EQUAL U V))
             (EQUAL U V))
    
    and then
    Goal''
    (IMPLIES (AND (WRAPPER (NOT (WRAPPER (EQUAL U V))))
                  (WRAPPER (EQUAL U V)))
             (EQUAL U V))
    
    etc.

    First, note that the hint is repeatedly applied to its children. That is because we wrote :computed-hint-replacement t. But second, note that Goal' is not even being simplified before Goal'' is produced from it. If it were being simplified, the (equal u v)'s in the hypotheses would be replaced by nil. This is a feature. It means after a computed hint has fired, other hints are given a chance at the result, even the hint itself unless it is removed from the list of hints.

    As an exercise, let's arrange for the hint to stay around and be applied indefinitely but with a simplification between each use of the the hint. To do this we need to pass information from one application of the hint to the next, essentially to say ``stay around but don't fire.''

    First, we will define a function to use in the hint. This is more than a mere convenience; it allows the hint to ``reproduce itself'' in the replacement.

    (defun wrapper-challenge (clause parity)
      (if parity
          `(:computed-hint-replacement ((wrapper-challenge clause nil))
            :use (:instance wrapper-axiom (x ,(car clause))))
          `(:computed-hint-replacement ((wrapper-challenge clause t)))))
    

    Note that this function is not recursive, even though it uses its own name. That is because the occurrence of its name is in a quoted constant.

    Now consider the following. What will it do?

    (thm (equal u v)
      :hints ((wrapper-challenge clause t)))
    

    First, observe that this is a legal hint because it is a term that mentions only the free variable CLAUSE. When defining hint functions you may sometimes think their only arguments are the four variables id, clause, world, and stable-under-simplificationp. That is not so. But in your hints you must call those functions so that those are the only free variables. Note also that the occurrence of clause inside the :computed-hint-replacement is not an occurrence of the variable clause but just a constant. Just store this note away for a moment. We'll return to it momentarily.

    Second, the basic cleverness of this hint is that every time it fires it reproduces itself with the opposite parity. When the parity is t it actually changes the goal by adding a hypothesis. When the parity is nil it doesn't change the goal and so allows simplification to proceed -- but it swaps the parity back to t. What you can see with this simple toy is that we can use the computed hints to pass information from parent to child.

    Ok, so what happens when the event above is executed? Try it. You will see that ACL2 applied the hint the first time. It doesn't get around to printing the output because an error is caused before it can print. But here is a blow-by-blow description of what happens. The hint is evaluated on Goal with the clause ((equal u v)). It produces a hint exactly as though we had typed:

    ("Goal" :use (:instance wrapper-axiom (x (equal u v))))
    
    which is applied to this goal. In addition, it produces the new hints argument
    :hints ((wrapper-challenge clause nil)).
    
    By applying the "Goal" hint we get the new subgoal
    Goal'
    (implies (wrapper (equal u v))
             (equal u v))
    
    but this is not printed because, before printing it, the theorem prover looks for hints to apply to it and finds
    (wrapper-challenge clause nil)
    
    That is evaluated and produces a hint exactly as though we had typed:
    ("Goal'" )
    
    and the new hints argument:
    :hints ((wrapper-challenge clause nil)).
    
    But if you supply the hint ("Goal'" ), ACL2 will signal an error because it does not allow you to specify an empty hint!

    So the definition of wrapper-challenge above is almost correct but fatally flawed. We need a non-empty ``no-op'' hint. One such hint is to tell the system to expand a term that will always be expanded anyway. So undo wrapper-challenge, redefine it, and try the proof again. Now remember the observation about clause that we asked you to ``store'' above. The new definition of wrapper-challenge illustrates what we meant. Note that the first formal parameter of wrapper-challenge, below, is no longer named clause but is called cl instead. But the ``call'' of wrapper-challenge in the replacements is on clause. This may seem to violate the rule that a function definition cannot use variables other than the formals. But the occurrences of clause below are not variables but constants in an object that will eventually be treated as hint term.

    :ubt wrapper-challenge
    
    (defun wrapper-challenge (cl parity)
      (if parity
          `(:computed-hint-replacement ((wrapper-challenge clause nil))
            :use (:instance wrapper-axiom (x ,(car cl))))
          `(:computed-hint-replacement ((wrapper-challenge clause t))
            :expand ((atom zzz)))))
    
    (thm (equal u v)
      :hints ((wrapper-challenge clause t)))
    

    This time, things go as you might have expected! Goal' is produced and simplified, to

    Goal''
    (implies (wrapper nil)
             (equal u v)).
    
    Simplification gets a chance because when the new hint (wrapper-challenge clause nil) is fired it does not change the goal. But it does change the parity in the hints argument so that before Goal'' is simplified again, the hint fires and adds the hypothesis:
    Goal'''
    (IMPLIES (AND (WRAPPER (NOT (WRAPPER NIL)))
                  (WRAPPER NIL))
             (EQUAL U V)).
    
    This simplifies, replacing the first (NOT (WRAPPER NIL)) by NIL, since (WRAPPER NIL) is known to be true here. Thus the goal simplifies to
    Goal'4'
    (IMPLIES (WRAPPER NIL) (EQUAL U V)).
    
    The process repeats indefinitely.

    So we succeeded in getting a hint to fire indefinitely but allow a full simplification between rounds.




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-7.html0000664002132200015000000002326312222333522020106 0ustar kaufmannacl2 USING-COMPUTED-HINTS-7.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-7

    Using the stable-under-simplificationp flag
    Major Section:  MISCELLANEOUS
    

    A problem with the example in using-computed-hints-6 is that exactly one simplification occurs between each (effective) firing of the hint. Much more commonly we wish to fire a hint once a subgoal has become stable under simplification.

    A classic example of this is when we are dealing with an interpreter for some state machine. We typically do not want the ``step'' function to open up on the symbolic representation of a state until that state has been maximally simplified. We will illustrate with a simple state machine.

    Let us start by defining the step function, stp, and the corresponding run function that applies it a given number of times.

    (defun stp (s)
      (+ 1 s))
    
    (defun run (s n)
      (if (zp n)
          s
          (run (stp s) (- n 1))))
    
    The step function here is trivial: a state is just a number and the step function increments it. In this example we will not be interested in the theorems we prove but in how we prove them. The formula we will focus on is
    (thm (equal (run s 7) xxx))
    
    This is not a theorem, of course. But we want to test our advice on non-theorems because we do not want the advice to work only for proofs that succeed. (In the past, we gave advice about using computed hints and that advice caused the theorem prover to run forever when given formulas that it couldn't prove -- but most of the time the system is presented with formulas it cannot prove!)

    Furthermore, without some kind of additional rules, the (run s 7) expression in the conjecture above will not expand at all, because ACL2's heuristics do not approve.

    In fact, we do not want to take chances that run will be expanded -- we want to control its expansion completely. Therefore, disable run.

    (in-theory (disable run))
    

    Now, what do we want? (That is always a good question to ask!) We want (run s 7) to expand ``slowly.'' In particular, we want it to expand once, to (run (stp s) 6). Then we want the stp to be expanded and fully simplified before the run expression is expanded again. That is, we want to force the expansion of run whenever the goal is stable under simplification. This is sometimes called ``staged simplification.''

    We can achieve staged simplification for any given function symbol by defining the functions shown below and then using a simple computed hint:

    (thm (equal (run s 7) xxx)
         :hints ((stage run)))
    

    By inspecting how stage is defined you can see how to extend it, but we explain as we go. To experiment, you can just paste the definitions (and defmacro) below into your ACL2 shell and then try the thm command.

    First, define this pair of mutually recursive functions. Find-first-call finds the first call of the function symbol fn in a given term.

    (mutual-recursion
     (defun find-first-call (fn term)
     ; Find the first call of fn in term.
      (cond ((variablep term) nil)
            ((fquotep term) nil)
            ((equal (ffn-symb term) fn)
             term)
            (t (find-first-call-lst fn (fargs term)))))
     (defun find-first-call-lst (fn lst)
     ; Find the first call of fn in a list of terms.
      (cond ((endp lst) nil)
            (t (or (find-first-call fn (car lst))
                   (find-first-call-lst fn (cdr lst)))))))
    

    We will arrange for the computed hint to generate an :EXPAND hint for the first call of fn, whenever the goal becomes stable under simplification. If no call is found, the hint will do nothing. To make sure the hint will not loop indefinitely (for example, by forcing fn to expand only to have the rewriter ``fold'' it back up again), we will provide the hint with a bound that stops it after some number of iterations. Here is the basic function that creates the expand hint and replaces itself to count down.

    (defun stage1 (fn max clause flg)
    ; If the clause is stable under simplification and there is a call of
    ; fn in it, expand it.  But don't do it more than max times.
     (let ((temp (and flg
                      (find-first-call-lst fn clause))))
       (if temp
           (if (zp max)
               (cw "~%~%HINT PROBLEM:  The maximum repetition count of ~
                    your STAGE hint been reached without eliminating ~
                    all of the calls of ~x0.  You could supply a larger ~
                    count with the optional second argument to STAGE ~
                    (which defaults to 100).  But think about what is ~
                    happening! Is each stage permanently eliminating a ~
                    call of ~x0?~%~%"
                   fn)
             `(:computed-hint-replacement
                ((stage1 ',fn ,(- max 1)
                         clause
                         stable-under-simplificationp))
               :expand (,temp)))
         nil)))
    

    Suppose that when stage1 is called, fn is the function we want to expand, max is the maximum number of iterations of this expansion, clause is the current goal clause, and flg is the value of the stable-under-simplificationp flag. Then if clause is stable and we can find a call of fn in it, we ask whether max is exhausted. If so, we print an ``error message'' to the comment window with cw and return nil (the value of cw). That nil means the hint does nothing. But if max is not yet exhausted, we return a new hint. As you can see above, the hint replaces itself with another stage1 hint with the same fn and a decremented max to be applied to the new clause and the then-current value of stable-under-simplificationp. The hint also contains an :expand directive for the call of fn found.

    Thus, if the computed hint was:

    (stage1 'run 5 clause stable-under-simplificationp)
    

    and (run s 7) occurs in the clause, then it will generate

    (:computed-hint-replacement
      ((stage1 'run 4 clause stable-under-simplificationp))
     :expand ((run s 7)))
    
    which will in turn replace the old stage1 hint with the new one and will apply :expand ((run s 7)) to the current goal.

    We can make this more convenient by defining the macro:

    (defmacro stage (fn &optional (max '100))
     `(stage1 ',fn ,max clause stable-under-simplificationp))
    
    Note that the macro allows us to either provide the maximum bound or let it default to 100.

    Henceforth, we can type

    (thm (equal (run s 7) xxx)
         :hints ((stage run)))
    
    to stage the opening of run up to 100 times, or we can write
    (thm (equal (run s 7) xxx)
         :hints ((stage run 5)))
    
    to stage it only 5 times. In the latter example, the system with print a ``error message'' after the fifth expansion.

    Note that if we executed

    (set-default-hints '((stage run)))
    
    then we could attack all theorems (involving run) with staged simplification (up to bound 100), without typing an explicit hint.
    (thm (equal (run s 7) xxx))
    

    Using techniques similar to those above we have implemented ``priority phased simplification'' and provided it as a book. See community book books/misc/priorities.lisp. This is an idea suggested by Pete Manolios, by which priorities may be assigned to rules and then the simplifier simplifies each subgoal maximally under the rules of a given priority before enabling the rules of the next priority level. The book above documents both how we implement it with computed hints and how to use it.

    Here is another example of using the stable-under-simplificationp flag to delay certain actions. It defines a default hint, see DEFAULT-HINTS, which will enable non-linear-arithmetic on precisely those goals which are stable-under-simplificationp. It also uses the HISTORY and PSPV variables to determine when toggling non-linear-arithmetic is appropriate. These variables are documented only in the source code. If you start using these variables extensively, please contact the developers of ACL2 or Robert Krug (rkrug@cs.utexas.edu) and let us know how we can help.

    (defun nonlinearp-default-hint (stable-under-simplificationp hist pspv)
      (cond (stable-under-simplificationp
             (if (not (access rewrite-constant
                              (access prove-spec-var pspv :rewrite-constant)
                              :nonlinearp))
                 '(:computed-hint-replacement t
                   :nonlinearp t)
               nil))
            ((access rewrite-constant
                     (access prove-spec-var pspv :rewrite-constant)
                     :nonlinearp)
             (if (not (equal (caar hist) 'SETTLED-DOWN-CLAUSE))
                 '(:computed-hint-replacement t
                   :nonlinearp nil)
               nil))
            (t
             nil)))
    




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS-8.html0000664002132200015000000000446412222333522020111 0ustar kaufmannacl2 USING-COMPUTED-HINTS-8.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS-8

    Some Final Comments
    Major Section:  MISCELLANEOUS
    

    None of the examples show the use of the variable WORLD, which is allowed in computed hints. There are some (undocumented) ACL2 utilities that might be useful in programming hints, but these utilities need access to the ACL2 logical world (see world).

    A very useful fact to know is that (table-alist name world) returns an alist representation of the current value of the table named name.

    The ACL2 source code is littered with :program mode functions for manipulating world. In our source code, the world is usually bound a variable named wrld; so searching our code for that name might be helpful.

    Using these utilities to look at the WORLD one can, for example, determine whether a symbol is defined recursively or not, get the body and formals of a defined function, or fetch the statement of a given lemma. Because these utilities are not yet documented, we do not expect users to employ WORLD in computed hints. But experts might and it might lead to the formulation of a more convenient language for computed hints.

    None of our examples illustrated the 7 argument form of a computed hint, (fn ID CLAUSE WORLD STABLE-UNDER-SIMPLIFICATIONP HIST PSPV CTX). When used, the variables HIST, PSPV, and CTX, are bound to the clause history, the package of ``special variables'' governing the clause, and the ``error message context.'' These variables are commonly used throughout our source code but are, unfortunately, undocumented. Again, we expect a few experts will find them useful in developing computed hints.

    If you start using computed hints extensively, please contact the developers of ACL2 and let us know what you are doing with them and how we can help.




    acl2-sources/doc/HTML/USING-COMPUTED-HINTS.html0000664002132200015000000000407112222333522017736 0ustar kaufmannacl2 USING-COMPUTED-HINTS.html -- ACL2 Version 6.3

    USING-COMPUTED-HINTS

    how to use computed hints
    Major Section:  MISCELLANEOUS
    

    Computed hints (see computed-hints) are extraordinarily powerful. We show a few examples here to illustrate their use. We recommend that the using-computed-hints-n topics be read in the order using-computed-hints-1, using-computed-hints-2, and so on.

    Some Related Topics




    acl2-sources/doc/HTML/USING-ENABLED-RULES.html0000664002132200015000000000677212222333522017567 0ustar kaufmannacl2 USING-ENABLED-RULES.html -- ACL2 Version 6.3

    USING-ENABLED-RULES

    avoiding :use hints for enabled :rewrite rules
    Major Section:  MISCELLANEOUS
    

    Consider the following (admittedly silly) example.

    (thm (equal (append (append x y) z) (append x y z))
         :hints (("Subgoal *1/1" :use cdr-cons)))
    
    ACL2's output includes the following warning.
    ACL2 Warning [Use] in ( THM ...):  It is unusual to :USE an enabled
    :REWRITE or :DEFINITION rule, so you may want to consider disabling
    (:REWRITE CDR-CONS) in the hint provided for Subgoal *1/1.
    
    The warning is saying that if you leave the rewrite rule enabled, ACL2 may simplify away the hypothesis added by the :use hint. We now explain this danger in more detail and show how disabling the rule can solve this problem.

    Recall (see hints) how :use hints work. Such a hint specifies a formula, F, which is based on an existing lemma. Then the indicated goal, G, is replaced by the implication (implies F G). The intention is that the truth of F will help in the simplification of G to T (true). The ``[Use]'' warning shown above is telling us of the danger that F may be rewritten to T, reducing the implication above to (implies T G) -- thus, sadly, F has disappeared and is not available to help with the simplification of G.

    Consider the following tiny example.

      (defun p (x) (cons x x))
      (defthm car-p
         (equal (car (p x)) x))
      (in-theory (disable p (:type-prescription p)))
      (thm (implies (equal (p x1) (p x2))
                    (equal x1 x2))
           :hints (("Goal"
                    :use ((:instance car-p (x x1))
                          (:instance car-p (x x2))))))
    
    The proof of the final thm form fails, because the new hypotheses are rewritten to t using the :rewrite rule CAR-P, in the manner described above. The following proof log shows the new hypotheses and their disappearance via rewriting.
      We augment the goal with the hypotheses provided by the :USE hint.
      These hypotheses can be derived from CAR-P via instantiation.  We are
      left with the following subgoal.
    
      Goal'
      (IMPLIES (AND (EQUAL (CAR (P X1)) X1)
                    (EQUAL (CAR (P X2)) X2))
               (IMPLIES (EQUAL (P X1) (P X2))
                        (EQUAL X1 X2))).
    
      By the simple :rewrite rule CAR-P we reduce the conjecture to
    
      Goal''
      (IMPLIES (EQUAL (P X1) (P X2))
               (EQUAL X1 X2)).
    
    When we disable the rule CAR-P as follows, the proof succeeds.
      (thm (implies (equal (p x1) (p x2))
                    (equal x1 x2))
           :hints (("Goal"
                    :use ((:instance car-p (x x1))
                          (:instance car-p (x x2)))
                    :in-theory (disable car-p))))
    
    In general, then, a solution is to disable the rewrite rule that you are supplying in a :use hint.




    acl2-sources/doc/HTML/USING-TABLES-EFFICIENTLY.html0000664002132200015000000000765212222333517020360 0ustar kaufmannacl2 USING-TABLES-EFFICIENTLY.html -- ACL2 Version 6.3

    USING-TABLES-EFFICIENTLY

    Notes on how to use tables efficiently
    Major Section:  TABLE
    

    (Thanks to Jared Davis for contributing this documentation topic, to which we have made only minor modifications.)

    Suppose your book contains table events, or macros that expand into table events, of the following form:

       (table my-table 'my-field <computation>)
    
    Then <computation> will be evaluated twice during certify-book and again every time you include the book with include-book. In some cases this overhead can be avoided using make-event.

    See also community book books/make-event/defconst-fast.lisp for an analogous trick involving defconst.

    Some Related Topics

    • MAKE-EVENT -- evaluate (expand) a given form and then evaluate the result

    As an example, suppose we want to store numbers in a table only if they satisfy some computationally expensive predicate. We'll introduce a new book, number-table.lisp, and create a table to store these numbers:

      (table number-table 'data nil)
    
    Instead of implementing a ``computationally expensive predicate,'' we'll write a function that just prints a message when it is called and accepts even numbers:
    (defun expensive-computation (n)
      (prog2$ (cw "Expensive computation on ~x0.~%" n)
              (evenp n)))
    
    Now we'll implement a macro, add-number, which will add its argument to the table only if it satisfies the expensive predicate:
    (defmacro add-number (n)
      `(table number-table 'data
              (let ((current-data
                     (cdr (assoc-eq 'data (table-alist 'number-table world)))))
                (if (expensive-computation ,n)
                    (cons ,n current-data)
                  current-data))))
    
    Finally, we'll call add-number a few times to finish the book.
    (add-number 1)
    (add-number 2)
    (add-number 3)
    
    When we now invoke (certify-book "number-table"), we see the expensive predicate being called twice for each number: once in Step 2, the main pass, then again in Step 3, the admissibility check. Worse, the computation is performed again for each number when we use include-book to load number-table, e.g.,
       ACL2 !>(include-book "number-table")
       Expensive computation on 1.
       Expensive computation on 2.
       Expensive computation on 3.
    
    To avoid these repeated executions, we can pull the test out of the table event using make-event. Here's an alternate implementation of add-number that won't repeat the computation:
    (defmacro add-number (n)
      `(make-event
        (if (expensive-computation ,n)
            '(table number-table 'data
                    (cons ,n (cdr (assoc 'data
                                         (table-alist 'number-table world)))))
          '(value-triple :expensive-computation-failed))))
    
    When we recertify number-table.lisp, we'll see the expensive computation is still called once for each number in Step 2, but is no longer called during Step 3. Similarly, the include-book no longer shows any calls of the expensive computation.




    acl2-sources/doc/HTML/Undocumented_Topic.html0000664002132200015000000000054112222333526020542 0ustar kaufmannacl2 Undocumented_Topic.html -- ACL2 Version 6.3

    Undocumented Topic

    This topic has not yet been documented. Sorry




    acl2-sources/doc/HTML/Using_the_Associativity_of_App_to_Prove_a_Trivial_Consequence.html0000664002132200015000000000157012222333526031300 0ustar kaufmannacl2 Using_the_Associativity_of_App_to_Prove_a_Trivial_Consequence.html -- ACL2 Version 6.3

    Using the Associativity of App to Prove a Trivial Consequence

    If we have proved the associativity-of-app rule, then the following theorem is trivial:

    (defthm trivial-consequence
      (equal (app (app (app (app x1 x2) (app x3 x4)) (app x5 x6)) x7)
             (app x1 (app (app x2 x3) (app (app x4 x5) (app x6 x7))))))
    
    Below we show the proof




    acl2-sources/doc/HTML/VALUE-TRIPLE.html0000664002132200015000000000464012222333517016627 0ustar kaufmannacl2 VALUE-TRIPLE.html -- ACL2 Version 6.3

    VALUE-TRIPLE

    compute a value, optionally checking that it is not nil
    Major Section:  EVENTS
    

    Examples:
    (value-triple (+ 3 4))
    (value-triple (cw "hi") :on-skip-proofs t)
    (value-triple (@ ld-pre-eval-print))
    (value-triple (@ ld-pre-eval-print) :check t)
    
    General Form:
    (value-triple form
                  :on-skip-proofs sp ; optional; nil by default
                  :check chk         ; optional; nil by default
                  )
    

    Value-triple provides a convenient way to evaluate a form in an event context, including progn and encapsulate and in books; see events. The form should evaluate to a single, non-stobj value.

    Calls of value-triple are generally skipped when proofs are being skipped, in particular when ACL2 is performing the second pass through the events of an encapsulate form or during an include-book, or indeed any time ld-skip-proofsp is non-nil. If you want the call evaluated during those times as well, use a non-nil value for :on-skip-proofs. Note that the argument to :on-skip-proofs is not evaluated.

    If you expect the form to evaluate to a non-nil value and you want an error to occur when that is not the case, you can use :check t. More generally, the argument of :check can be a form that evaluates to a single, non-stobj value. If this value is not nil, then the aforementioned test is made (that the given form is not nil). If an error occurs and the value of :check is a string or indeed any ``message'' suitable for printing by fmt when supplied as a value for tilde-directive ~@, then that string or message is printed.




    acl2-sources/doc/HTML/VERBOSE-PSTACK.html0000664002132200015000000000221312222333522017034 0ustar kaufmannacl2 VERBOSE-PSTACK.html -- ACL2 Version 6.3

    VERBOSE-PSTACK

    seeing what the prover is up to (for advanced users)
    Major Section:  PSTACK
    

    General Forms:
    (verbose-pstack t)   ; get trace-like information on prover during proofs
    (verbose-pstack '(fn1 fn2 ...))
                         ; as above, but omit calls of the indicated functions
    (verbose-pstack nil) ; turn off trace-like information on prover
    
    For example, (verbose-pstack '(ev-fncall)) will provide a trace of various prover functions during proofs, except for the function ev-fncall.

    By evaluating (verbose-pstack t) one can get trace-like information during subsequent proofs about prover functions, including time summaries, printed to the screen during a proof. To turn off this feature, evaluate (verbose-pstack nil). Also See pstack.




    acl2-sources/doc/HTML/VERIFY-GUARDS+.html0000664002132200015000000000666112222333517017025 0ustar kaufmannacl2 VERIFY-GUARDS+.html -- ACL2 Version 6.3

    VERIFY-GUARDS+

    verify the guards of a function
    Major Section:  EVENTS
    

    We assume familiarity with guard verification; see verify-guards. Unlike verify-guards, the macro call (verify-guards+ mac ...) will verify guards for a function, fn, such that the macro mac is associated with the function symbol fn in macro-aliases-table (also see add-macro-alias). For example, if you define a macro app and list append function binary-app, and you associate macro app with function symbol binary-app in macro-aliases-table, then evaluation of the form (verify-guard+ app) will have the effect of evaluating (verify-guards fn). Note that in this setting, evaluation of (verify-guard app) would cause an error, because app is a macro and verify-guards, unlike verify-guards+, cannot handle macros.

    The rest of this documentation topic discusses why we do not simply arrange that verify-guards be permitted to take a macro alias. The following example shows a soundness issue in doing so.

    (encapsulate
     ()
     (defun f1 (x)
       (declare (xargs :guard (consp x)
                       :verify-guards nil))
       (car x))
     (defun f2 (x)
       (declare (xargs :guard t
                       :verify-guards nil))
       (cdr x))
     (defmacro mac (x)
       x)
     (add-macro-alias mac f2) ; silly macro alias ;
     (local (add-macro-alias mac f1)) ; alternate silly macro alias ;
     (verify-guards mac))
    

    If we were to allow macro aliases in verify-guards, this event would be admitted, because on the first pass we are verifying guards of f1. But after the encapsulate form completes evaluation, it would appear that f2 is guard-verified. That could of course cause a raw Lisp error.

    The enhanced functionality provided by verify-guards+ does not have the above problem, because it takes advantage of make-event to avoid taking advantage of the contradictory results produced by the two calls of add-macro-alias. See make-event. If the specific example above is modified by replacing verify-guards with verify-guards+, then the first pass through the encapsulate form will expand the form (verify-guards+ mac) to (verify-guards f1). That same expansion will be used for the verify-guards+ call during the second pass through the encapsulate form, which is evaluated successfully and leaves us in a world where f1 is guard-verified and f2 is not.




    acl2-sources/doc/HTML/VERIFY-GUARDS-EAGERNESS.html0000664002132200015000000000077312222333531020256 0ustar kaufmannacl2 VERIFY-GUARDS-EAGERNESS.html -- ACL2 Version 6.3

    VERIFY-GUARDS-EAGERNESS

    See set-verify-guards-eagerness.
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    




    acl2-sources/doc/HTML/VERIFY-GUARDS-FORMULA.html0000664002132200015000000000313312222333522020040 0ustar kaufmannacl2 VERIFY-GUARDS-FORMULA.html -- ACL2 Version 6.3

    VERIFY-GUARDS-FORMULA

    view the guard proof obligation, without proving it
    Major Section:  OTHER
    

    See verify-guards and see guard for a discussion of guards. This utility is provided for viewing a guard proof obligation, without doing a proof.

    Example Forms:
    (verify-guards-formula foo)
    (verify-guards-formula foo :guard-debug t)
    (verify-guards-formula foo :otf-flg dont-care :xyz whatever)
    (verify-guards-formula (+ (foo x) (bar y)) :guard-debug t)
    
    Verify-guards-formula allows all keywords, but only pays attention to :guard-debug, which has the same effect as in verify-guards (see guard-debug). Apply verify-guards-formula to a name just as you would use verify-guards, but when you only want to view the formula rather than creating an event. If the first argument is not a symbol, then it is treated as the body of a defthm event for which you want the guard proof obligation.

    See guard-obligation if you want to obtain guard proof obligations for use in a program.




    acl2-sources/doc/HTML/VERIFY-GUARDS.html0000664002132200015000000004123312222333517016744 0ustar kaufmannacl2 VERIFY-GUARDS.html -- ACL2 Version 6.3

    VERIFY-GUARDS

    verify the guards of a function
    Major Section:  EVENTS
    

    See guard for a general discussion of guards.

    Before discussing the verify-guards event, we first discuss guard verification, which can take place at definition time or, later, using verify-guards. Typically, guard verification takes place at definition time if a guard (or type, or stobjs) has been supplied explicitly unless :verify-guards nil has been specified; see defun and see xargs, and see set-verify-guards-eagerness for how to change this default. The point of guard verification is to ensure that during evaluation of an expression without free variables, no guard violation takes place.

    Technical note: the first argument of verify-guards must be a function symbol or the name of a defthm or defaxiom event, not a macro-alias for a function symbol (see macro-aliases-table). See verify-guards+ for a utility that does not have this restriction.

    Guard verification is intended to guarantee that for any call of a given function, if its guard holds for that call then the guard will hold for every function call in the body of that function. Moreover, in order to avoid guard violations during evaluation of the function's guard itself, guard verification also is intended to guarantee that the guards are satisfied for all calls in the guard itself. Consider the following simple example.

    (defun f (x)
      (declare (xargs :guard (and (consp x)
                                  (integerp (car x)))))
      (if (rationalp (cdr x))
          (+ (car x) (cdr x))
        17))
    
    If you evaluate (f t), for example, in the top-level loop, you will (by default) get a guard error. The point of guard verification is to guarantee the absence of guard errors, and we start by using this example to illustrate the proof obligations that guarantee such absence.

    The body of the above definition has the following function calls, where the first is the entire body.

      (if (rationalp (cdr x))
          (< (car x) (cdr x))
        17)
      (rationalp (cdr x)) ; the test of the top-level IF call
      (cdr x)             ; from (rationalp (cdr x))
      (< (car x) (cdr x)) ; the true branch of the top-level IF call
      (car x)             ; from (< (car x) (cdr x))
      (cdr x)             ; from (< (car x) (cdr x))
    
    We thus see potentially six conditions to prove, one for each call. The guards of the function symbols of those calls are t for if and rationalp, (or (consp x) (equal x nil)) for both (car x) and (cdr x), and finally that both arguments are rationals for <. Moreover, we can take advantage of ``contextual assumptions'': the if-test conditions and the top-level :guard. Thus, for verify-guards the proof obligation from the body of f is as follows.
    (implies
     (and (consp x) (integerp (car x))) ; from the :guard
     (and t ; from the top-level IF call
          t ; from (rationalp (cdr x))
          (or (consp x) (equal x nil)) ; from the first (cdr x)
          (implies
           (rationalp (cdr x)) ; IF-test for calls in the true branch
           (and (or (consp x) (equal x nil)) ; from (car x)
                (or (consp x) (equal x nil)) ; from the second (cdr x)
                (and (rationalp (car x)) (rationalp (cdr x))) ; from the < call
                ))))
    
    But the :guard itself generates a similar sort of proof obligation. Note that the guard (and (consp x) (integerp (car x))) is really an abbreviation (i.e. via the macro AND) for the term (if (consp x) (integerp (car x)) nil). The guard proof obligation for the guard itself is thus as follows.
    (and t ; from (consp x)
         (implies (consp x)
                  (and t         ; from (integerp (car x)) ;
                       (consp x) ; from (car x) ;
                       )))
    
    All of the above proof obligations are indeed theorems, and guard verification succeeds for the above definition of f.

    The example above illustrates the general procedure for generating the guard proof obligation. Each function call is considered in the body or guard of the function, and it is required that the guard is met for that call, under certain ``contextual assumptions'', which are as follows. In the case of the body of the named function, it is assumed that the guard holds for that function on its formal parameters. And in both cases -- the body of the named function and also its guard -- the governing tests from superior calls of IF are also assumed.

    As mentioned above, if the guard on a function is not t, then guard verification requires not only consideration of the body under the assumption that the guard is true, but also consideration of the guard itself. Thus, for example, guard verification fails in the following example, even though there are no proof obligations arising from the body, because the guard itself can cause a guard violation when evaluated for an arbitrary value of x:

    (defun foo (x)
      (declare (xargs :guard (car x)))
      x)
    

    We turn now to the verify-guards event as a way of verifying the guards for a function or theorem.

    Examples:
    (verify-guards flatten)
    (verify-guards flatten
                   :hints (("Goal" :use (:instance assoc-of-app)))
                   :otf-flg t
                   :guard-debug t ; default = nil
                   :doc "string")
    
    General Form:
    (verify-guards name
            :hints        hints
            :otf-flg      otf-flg
            :guard-debug  t ; typically t, but any value is legal
            :doc          doc-string)
    
    In the General Form above, name is the name of a :logic function (see defun-mode) or of a theorem or axiom. In the most common case name is the name of a function that has not yet had its guards verified, each subroutine of which has had its guards verified. The values hints, otf-flg, and guard-debug are as described in the corresponding documentation entries; and doc-string, if supplied, is a string not beginning with ``:Doc-Section''. The four keyword arguments above are all optional. To admit this event, the conjunction of the guard proof obligations must be proved. If that proof is successful, name is considered to have had its guards verified.

    See verify-guards-formula for a utility that lets you view the formula to be proved by verify-guards, but without creating an event.

    If name is one of several functions in a mutually recursive clique, verify-guards will attempt to verify the guards of all of the functions.

    If name is a theorem or axiom name, verify-guards verifies the guards of the associated formula. When a theorem has had its guards verified then you know that the theorem will evaluate to non-nil in all Common Lisps, without causing a runtime error (other than possibly a resource error). In particular, you know that the theorem's validity does not depend upon ACL2's arbitrary completion of the domains of partial Common Lisp functions.

    For example, if app is defined as

    (defun app (x y)
      (declare (xargs :guard (true-listp x)))
      (if (endp x)
          y
          (cons (car x) (app (cdr x) y))))
    
    then we can verify the guards of app and we can prove the theorem:
    (defthm assoc-of-app
      (equal (app (app a b) c) (app a (app b c))))
    
    However, if you go into almost any Common Lisp in which app is defined as shown and evaluate
    (equal (app (app 1 2) 3) (app 1 (app 2 3)))
    
    we get an error or, perhaps, something worse like nil! How can this happen since the formula is an instance of a theorem? It is supposed to be true!

    It happens because the theorem exploits the fact that ACL2 has completed the domains of the partially defined Common Lisp functions like car and cdr, defining them to be nil on all non-conses. The formula above violates the guards on app. It is therefore ``unreasonable'' to expect it to be valid in Common Lisp.

    But the following formula is valid in Common Lisp:

    (if (and (true-listp a)
             (true-listp b))
        (equal (app (app a b) c) (app a (app b c)))
        t)
    
    That is, no matter what the values of a, b and c the formula above evaluates to t in all Common Lisps (unless the Lisp engine runs out of memory or stack computing it). Furthermore the above formula is a theorem:

    (defthm guarded-assoc-of-app
      (if (and (true-listp a)
               (true-listp b))
          (equal (app (app a b) c) (app a (app b c)))
          t))
    
    This formula, guarded-assoc-of-app, is very easy to prove from assoc-of-app. So why prove it? The interesting thing about guarded-assoc-of-app is that we can verify the guards of the formula. That is, (verify-guards guarded-assoc-of-app) succeeds. Note that it has to prove that if a and b are true lists then so is (app a b) to establish that the guard on the outermost app on the left is satisfied. By verifying the guards of the theorem we know it will evaluate to true in all Common Lisps. Put another way, we know that the validity of the formula does not depend on ACL2's completion of the partial functions or that the formula is ``well-typed.''

    One last complication: The careful reader might have thought we could state guarded-assoc-of-app as

    (implies (and (true-listp a)
                  (true-listp b))
             (equal (app (app a b) c)
                    (app a (app b c))))
    
    rather than using the if form of the theorem. We cannot! The reason is technical: implies is defined as a function in ACL2. When it is called, both arguments are evaluated and then the obvious truth table is checked. That is, implies is not ``lazy.'' Hence, when we write the guarded theorem in the implies form we have to prove the guards on the conclusion without knowing that the hypothesis is true. It would have been better had we defined implies as a macro that expanded to the if form, making it lazy. But we did not and after we introduced guards we did not want to make such a basic change.

    Recall however that verify-guards is almost always used to verify the guards on a function definition rather than a theorem. We now return to that discussion.

    Because name is not uniquely associated with the verify-guards event (it necessarily names a previously defined function) the documentation string, doc-string, is not stored in the documentation database. Thus, we actually prohibit doc-string from having the form of an ACL2 documentation string; see doc-string.

    Verify-guards must often be used when the value of a recursive call of a defined function is given as an argument to a subroutine that is guarded. An example of such a situation is given below. Suppose app (read ``append'') has a guard requiring its first argument to be a true-listp. Consider

    (defun rev (x)
      (declare (xargs :guard (true-listp x)))
      (cond ((endp x) nil)
            (t (app (rev (cdr x)) (list (car x))))))
    
    Observe that the value of a recursive call of rev is being passed into a guarded subroutine, app. In order to verify the guards of this definition we must show that (rev (cdr x)) produces a true-listp, since that is what the guard of app requires. How do we know that (rev (cdr x)) is a true-listp? The most elegant argument is a two-step one, appealing to the following two lemmas: (1) When x is a true-listp, (cdr x) is a true-listp. (2) When z is a true-listp, (rev z) is a true-listp. But the second lemma is a generalized property of rev, the function we are defining. This property could not be stated before rev is defined and so is not known to the theorem prover when rev is defined.

    Therefore, we might break the admission of rev into three steps: define rev without addressing its guard verification, prove some general properties about rev, and then verify the guards. This can be done as follows:

    (defun rev (x)
      (declare (xargs :guard (true-listp x)
                      :verify-guards nil))    ; Note this additional xarg.
      (cond ((endp x) nil)
            (t (app (rev (cdr x)) (list (car x))))))
    
    (defthm true-listp-rev
      (implies (true-listp x2)
               (true-listp (rev x2))))
    
    (verify-guards rev)
    
    The ACL2 system can actually admit the original definition of rev, verifying the guards as part of the defun event. The reason is that, in this particular case, the system's heuristics just happen to hit upon the lemma true-listp-rev. But in many more complicated functions it is necessary for the user to formulate the inductively provable properties before guard verification is attempted.

    Remark on computation of guard conjectures and evaluation. When ACL2 computes the guard conjecture for the body of a function, it evaluates any ground subexpressions (those with no free variables), for calls of functions whose :executable-counterpart runes are enabled. Note that here, ``enabled'' refers to the current global theory, not to any :hints given to the guard verification process; after all, the guard conjecture is computed even before its initial goal is produced. Also note that this evaluation is done in an environment as though :set-guard-checking :all had been executed, so that we can trust that this evaluation takes place without guard violations; see set-guard-checking.

    If you want to verify the guards on functions that are built into ACL2, you will first need to put them into :logic mode. See verify-termination, specifically the ``Remark on system functions'' in that documentation.




    acl2-sources/doc/HTML/VERIFY-TERMINATION.html0000664002132200015000000002354612222333517017557 0ustar kaufmannacl2 VERIFY-TERMINATION.html -- ACL2 Version 6.3

    VERIFY-TERMINATION

    convert a function from :program mode to :logic mode
    Major Section:  EVENTS
    

    Example:
    (verify-termination fact)
    
    General Forms:
    (verify-termination fn dcl ... dcl)
    (verify-termination (fn1 dcl ... dcl)
                        (fn2 dcl ... dcl)
                        ...)
    
    where fn and the fni are function symbols having :program mode (see defun-mode) and all of the dcls are either declare forms or documentation strings. The first form above is an abbreviation for
    (verify-termination (fn dcl ... dcl))
    
    so we limit our discussion to the second form. Each of the fni must be in the same clique of mutually recursively defined functions, but not every function in the clique need be among the fni.

    Verify-termination attempts to establish the admissibility of the fni. Verify-termination retrieves their definitions, creates modified definitions using the dcls supplied above, and resubmits these definitions. You could avoid using verify-termination by typing the new definitions yourself. So in that sense, verify-termination adds no new functionality. But if you have prototyped your system in :program mode and tested it, you can use verify-termination to resubmit your definitions and change their defun-modes to :logic, addings hints without having to retype or recopy the code.

    The defun command executed by verify-termination is obtained by retrieving the defun (or mutual-recursion) command that introduced the clique in question and then possibly modifying each definition as follows. Consider a function, fn, in the clique. If fn is not among the fni above, its definition is left unmodified other than to add (declare (xargs :mode :logic)). Otherwise, fn is some fni and we modify its definition by inserting into it the corresponding dcls listed with fni in the arguments to verify-termination, as well as (declare (xargs :mode :logic)). In addition, we throw out from the old declarations in fn the :mode specification and anything that is specified in the new dcls.

    For example, suppose that fact was introduced with:

    (defun fact (n)
      (declare (type integer n)
               (xargs :mode :program))
      (if (zp n) 1 (* n (fact (1- n))))).
    
    Suppose later we do (verify-termination fact). Then the following definition is submitted.
    (defun fact (n)
      (declare (type integer n))
      (if (zp n) 1 (* n (fact (1- n))))).
    
    Observe that this is the same definition as the original one, except the old specification of the :mode has been deleted so that the defun-mode now defaults to :logic. Although the termination proof succeeds, ACL2 also tries to verify the guard, because we have (implicitly) provided a guard, namely (integerp n), for this function. (See guard for a general discussion of guards, and see type-spec for a discussion of how type declarations are used in guards.) Unfortunately, the guard verification fails, because the subterm (zp n) requires that n be nonnegative, as can be seen by invoking :args zp. (For a discussion of termination issues relating to recursion on the naturals, see zero-test-idioms.) So we might be tempted to submit the following:
    (verify-termination
     fact
     (declare (xargs :guard (and (integerp n) (<= 0 n))))).
    
    However, this is considered a changing of the guard (from (integerp n)), which is illegal. If we instead change the guard in the earlier defun after undoing that earlier definition with :ubt fact, then (verify-termination fact) will succeed.

    Remark on system functions. There may be times when you want to apply verify-termination (and also, perhaps, verify-guards) to functions that are predefined in ACL2. It may be necessary in such cases to modify the system code first. See Part II of http://www.cs.utexas.edu/users/moore/acl2/open-architecture/ for a discussion of the process for contributing updates to the system code and books with such verify-termination or verify-guards events, perhaps resulting in more system functions being built-in as guard-verified. To see which built-in functions have already received such treatment, see community books directory books/system/; or, evaluate the constant *system-verify-guards-alist*, each of whose entries associates the name of a community book with a list of functions whose guard-verification is proved by including that book. See the above URL for more details.

    Note that if fn1 is already in :logic mode, then the verify-termination call has no effect. It is generally considered to be redundant, in the sense that it returns without error; but if the fn1 is a constrained function (i.e., introduced in the signature of an encapsulate, or by defchoose), then an error occurs. This error is intended to highlight unintended uses of verify-termination; but if you do not want to see an error in this case, you can write and use your own macro in place of verify-termination. The following explanation of the implementation of verify-termination may help with such a task.

    We conclude with a discussion of the use of make-event to implement verify-termination. This discussion can be skipped; we include it only for those who want to create variants of verify-termination, or who are interested in seeing an application of make-event.

    Consider the following proof of nil, which succeeded up through Version_3.4 of ACL2.

    (encapsulate
     ()
     (defun foo (x y)
       (declare (xargs :mode :program))
       (if (or (zp x) (zp y))
           (list x y)
         (foo (1+ x) (1- y))))
     (local (defun foo (x y)
              (declare (xargs :measure (acl2-count y)))
              (if (or (zp x) (zp y))
                  (list x y)
                (foo (1+ x) (1- y)))))
     (verify-termination foo))
    
    (defthm bad-lemma
      (zp x)
      :hints (("Goal" :induct (foo x 1)))
      :rule-classes nil)
    
    How did this work? In the first pass of the encapsulate, the second defun of foo promoted foo from :program to :logic mode, with y as the unique measured variable. The following call to verify-termination was then redundant. However, on the second pass of the encapsulate, the second (local) definition of foo was skipped, and the verify-termination event then used the first definition of foo to guess the measure, based (as with all guesses of measures) on a purely syntactic criterion. ACL2 incorrectly chose (acl2-count x) as the measure, installing x as the unique measured variable, which in turn led to an unsound induction scheme subsequently used to prove nil (lemma bad-lemma, above)

    Now, verify-termination is a macro whose calls expand to make-event calls. So in the first pass above, the verify-termination call generated a defun event identical to the local defun of foo, which was correctly identified as redundant. That expansion was recorded, and on the second pass of the encapsulate, the expansion was recalled and used in place of the verify-termination call (that is how make-event works). So instead of a measure being guessed for the verify-termination call on the second pass, the same measure was used as was used on the first pass, and a sound induction scheme was stored. The attempt to prove nil (lemma bad-lemma) then failed.




    acl2-sources/doc/HTML/VERIFY.html0000664002132200015000000000272512222333526015764 0ustar kaufmannacl2 VERIFY.html -- ACL2 Version 6.3

    VERIFY

    enter the interactive proof checker
    Major Section:  PROOF-CHECKER
    

    For proof-checker command summaries, see proof-checker.

    Examples:
    (VERIFY (implies (and (true-listp x) (true-listp y))
                          (equal (append (append x y) z)
                                 (append x (append y z)))))
       -- Attempt to prove the given term interactively.
    
    (VERIFY (p x)
            :event-name p-always-holds
            :rule-classes (:rewrite :generalize)
            :instructions ((rewrite p-always-holds-lemma)
                           change-goal))
       -- Attempt to prove (p x), where the intention is to call the
          resulting DEFTHM event by the name p-always-holds, with
          rule-classes as indicated.  The two indicated instructions
          will be run immediately to start the proof.
    
    (VERIFY)
       -- Re-enter the proof-checker in the state at which is was last
          left.
    
    General Form:
    (VERIFY &OPTIONAL raw-term
            &KEY
            event-name
            rule-classes
            instructions)
    
    Verify is the function used for entering the proof-checker's interactive loop.




    acl2-sources/doc/HTML/VERSION.html0000664002132200015000000001271412222333522016100 0ustar kaufmannacl2 VERSION.html -- ACL2 Version 6.3

    VERSION

    ACL2 Version Number
    Major Section:  MISCELLANEOUS
    

    To determine the version number of your copy of ACL2, evaluate the form (@ acl2-version). The value will be a string. For example,

    ACL2 !>(@ acl2-version)
    "ACL2 Version 3.4"
    

    The part of the string after "ACL2 Version " is of the form x.y or x.y.z, optionally followed by a succession of values in parentheses, where x, y, and z are natural numbers. If z is omitted then it is implicitly 0. We refer to X, y, and z as the ``major'', ``minor'', and ``incrl'' fields, respectively. The incrl field is used for incremental releases. The discussion just below assumes that incremental releases are not employed at the user's site, i.e., the incrl fields are always 0. We remove this assumption when we discuss incremental releases at the end of this documenttation topic.

    Books are considered certified only in the same version of ACL2 in which the certification was done. The certificate file records the version number of the certifying ACL2 and include-book considers the book uncertified if that does not match the current version number. Thus, each time we release a new version of ACL2, previously certified books should be recertified.

    Note that there are over 150 constants in the system, most having to do with the fact that ACL2 is coded in ACL2. Many of these, for example *common-lisp-specials-and-constants* and *acl2-exports*, may change from version to version, and this can cause unsoundness. For example, the symbol 'set-difference-eq was added to *acl2-exports* in Version_2.9, so we can certify a book in Version_2.8 containing the following theorem, which is false in Version_2.9.

    (null (member 'set-difference-eq *acl2-exports*))
    
    Therefore, we need to disallow inclusion of such a book in a Version_2.9 session, which otherwise would allow us to prove nil. Furthermore, it is possible that from one version of the system to another we might change, say, the default values on some system function or otherwise make ``intentional'' changes to the axioms. It is even possible one version of the system is discovered to be unsound and we release a new version to correct our error.

    Therefore we adopted the draconian policy that books are certified by a given version of ACL2 and ``must'' be recertified to be used in other versions. We put ``must'' in quotes because in fact, ACL2 allows a book that was certified in one ACL2 version to be included in a later version, using include-book. But ACL2 does not allow certify-book to succeed when such an include-book is executed on its behalf. Also, you may experience undesirable behavior if you avoid recertification when moving to a different version. Hence we recommend that you stick to the draconion policy of recertifying books when updating to a new ACL2 version.

    The string (@ acl2-version) can contain implementation-specific information in addition to the version number. For example, in Macintosh Common Lisp (MCL) (char-code #Newline) is 13, while as far as we know, it is 10 in every other Common Lisp. Our concern is that one could certify a book in an MCL-based ACL2 with the theorem

    (equal (char-code #Newline) 13)
    
    and then include this book in another Lisp and thereby prove nil. So, when a book is certified in an MCL-based ACL2, the book's certificate mentions ``MCL'' in its version string. Moreover, (@ acl2-version) similarly mentions ``MCL'' when the ACL2 image has been built on top of MCL. Thus, an attempt to include a book in an MCL-based ACL2 that was certified in a non-MCL-based ACL2, or vice-versa, will be treated like an attempt to include an uncertified book.

    Incremental releases.

    From time to time, so-called ``incremental releases'' of ACL2 are made available. These releases are thoroughly tested on at least two platforms; ``normal'' releases, on the other hand, are thoroughly tested on many more platforms (perhaps a dozen or so) and are accompanied by updates to the ACL2 home page. We provide incremental releases in order to provide timely updates for ACL2 users who want them, without imposing unnecessary burdens on either on the ACL2 implementors or on ACL2 users who prefer to update less frequently. The implementors expect users to update their copies of ACL2 when normal releases are made available, but not necessarily when incremental releases are made available.

    Incremental releases are accompanied by a bump in the incrl field of the version field, while normal releases are accompanied by a bump in the minor or (much less frequently) major field and zeroing out of the incrl field. Note that incremental releases are full-fledged releases.




    acl2-sources/doc/HTML/WALKABOUT.html0000664002132200015000000000575212222333522016310 0ustar kaufmannacl2 WALKABOUT.html -- ACL2 Version 6.3

    WALKABOUT

    explore an ACL2 cons tree
    Major Section:  OTHER
    

    By typing (walkabout x state) for an ACL2 term x whose value is a cons tree, you can walk around that tree. For example, ACL2 developers often use this utility to explore the ACL2 logical world.

    When you issue a walkabout command, a summary of commands will be printed before you enter an interactive loop.

    Commands:
    0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q.
    
    In the interactive walkabout loop, a positive integer n takes you to the nth position, while 0 takes you up a level. The commands nx and bk take you to the next and previous position, respectively, at the same level. The command pp prints the current object in full, while (pp level length) hides sub-objects below the indicated level and past the indicated length, if non-nil; see evisc-tuple. The command (pp n) abbreviates (pp n n), so in particular (pp nil) is equivalent to pp.

    The following example illustrates the commands described above.

    ACL2 !>(walkabout (append '(a (b1 b2 b3)) '(c d e f)) state)
    
    Commands:
    0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q.
    
    (A (B1 B2 B3) C ...)
    :2
    (B1 B2 B3)
    :3
    B3
    :0
    (B1 B2 B3)
    :nx
    C
    :nx
    D
    :0
    (A (B1 B2 B3) C ...)
    :pp
    (A (B1 B2 B3) C D E F)
    :(pp 4)
    (A (B1 B2 B3) C D ...)
    :(pp 1 4)
    (A # C D ...)
    :(pp nil 2)
    (A (B1 B2 ...) ...)
    :q
    ACL2 !>
    

    Finally we describe the commands q, =, and (= symb), where symb is a symbol. The command q simply causes an exit from the walkabout loop. The command = also exits, but causes the current object to be printed in full. The command (= symb) saves an association of symb with the current object, which can be retrieved outside the walkabout loop using the macro walkabout=, as illustrated below.

    :2
    (B1 B2 B3)
    :(= my-list)
    (walkabout= MY-LIST) is
    (B1 B2 B3)
    :q
    ACL2 !>(walkabout= MY-LIST)
    (B1 B2 B3)
    ACL2 !>
    

    Finally, we remark that for trees that are not true-lists, walkabout treats the dot as an object that can be ``walked about''. The following example illustrates this point.

    ACL2 !>(walkabout '(c d e . f) state)
    
    Commands:
    0, 1, 2, ..., nx, bk, pp, (pp n), (pp lev len), =, (= symb), and q.
    
    (C D E . F)
    :3
    E
    :nx
    .
    :nx
    F
    :0
    (C D E . F)
    :4
    .
    :0
    (C D E . F)
    :5
    F
    :
    




    acl2-sources/doc/HTML/WATERFALL-PARALLELISM.html0000664002132200015000000000102712222333523020033 0ustar kaufmannacl2 WATERFALL-PARALLELISM.html -- ACL2 Version 6.3

    WATERFALL-PARALLELISM

    for ACL2(p): configuring the parallel execution of the waterfall
    Major Section:  PARALLEL-PROOF
    

    See set-waterfall-parallelism.




    acl2-sources/doc/HTML/WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION.html0000664002132200015000000000343712222333523023177 0ustar kaufmannacl2 WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION.html -- ACL2 Version 6.3

    WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION

    for ACL2(p): using waterfall parallelism during book certification
    Major Section:  PARALLELISM
    

    This documentation topic relates to the experimental extension of ACL2 supporting parallel execution and proof; see parallelism.

    There are books whose certification can be sped up significantly by using waterfall parallelism. (See parallelism, including the caveat in its "IMPORTANT NOTE".) One such example in the ACL2 community books is models/jvm/m5/apprentice.lisp, which is typically excluded from regressions because of how long it takes to certify. In order to use waterfall parallelism during certification of a book <book-name>.lisp using `make' (see books-certification and see books-certification-classic), we recommend creating a file <book-name>.acl2 that includes the following forms.

    #+acl2-par
    (set-waterfall-parallelism t)
    
    (certify-book <book-name> ? t) ; other arguments may be preferable
    

    Note that there are books that will not certify when waterfall-parallelism is enabled. See file acl2-customization-files/README for more information, including how to certify essentially all books using waterfall parallelism.




    acl2-sources/doc/HTML/WATERFALL-PRINTING.html0000664002132200015000000000102212222333523017513 0ustar kaufmannacl2 WATERFALL-PRINTING.html -- ACL2 Version 6.3

    WATERFALL-PRINTING

    for ACL2(p): configuring the printing within the parallelized waterfall
    Major Section:  PARALLEL-PROOF
    

    See set-waterfall-printing.




    acl2-sources/doc/HTML/WATERFALL.html0000664002132200015000000000066712222333522016300 0ustar kaufmannacl2 WATERFALL.html -- ACL2 Version 6.3

    WATERFALL

    See hints-and-the-waterfall.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/WELL-FOUNDED-RELATION.html0000664002132200015000000002205012222333530020004 0ustar kaufmannacl2 WELL-FOUNDED-RELATION.html -- ACL2 Version 6.3

    WELL-FOUNDED-RELATION

    show that a relation is well-founded on a set
    Major Section:  RULE-CLASSES
    

    See rule-classes for a general discussion of rule classes, including how they are used to build rules from formulas and a discussion of the various keywords in a rule class description.

    Example:
    (defthm lex2p-is-well-founded-relation
      (and (implies (pairp x) (o-p (ordinate x)))
           (implies (and (pairp x)
                         (pairp y)
                         (lex2p x y))
                    (o< (ordinate x) (ordinate y))))
      :rule-classes :well-founded-relation)
    
    The example above creates a :well-founded-relation rule, where of course the functions pairp, lex2p, and ordinate would have to be defined first. It establishes that lex2p is a well-founded relation on pairps. We explain and give details below.

    Exactly two general forms are recognized:

    General Forms
    (AND (IMPLIES (mp x) (O-P (fn x)))              ; Property 1
         (IMPLIES (AND (mp x)                       ; Property 2
                       (mp y)
                       (rel x y))
                  (O< (fn x) (fn y)))),
    
    or
    (AND (O-P (fn x))                               ; Property 1
         (IMPLIES (rel x y)                         ; Property 2
                  (O< (fn x) (fn y))))
    
    where mp, fn, and rel are function symbols, x and y are distinct variable symbols, and no other :well-founded-relation theorem about fn has been proved. When the second general form is used, we act as though the first form were used with mp being the function that ignores its argument and returns t. The discussion below therefore considers only the first general form.

    Note: We are very rigid when checking that the submitted formula is of one of these forms. For example, in the first form, we insist that all the conjuncts appear in the order shown above. Thus, interchanging the two properties in the top-level conjunct or rearranging the hyptheses in the second conjunct both produce unrecognized forms. The requirement that each fn be proved well-founded at most once ensures that for each well-founded relation, fn, there is a unique mp that recognizes the domain on which rel is well-founded. We impose this requirement simply so that rel can be used as a short-hand when specifying the well-founded relations to be used in definitions; otherwise the specification would have to indicate which mp was to be used.

    We also insist that the new ordering be embedded into the ordinals as handled by o-p and o< and not some into previously admitted user-defined well-founded set and relation. This restriction should pose no hardship. If mp and rel were previously shown to be well-founded via the embedding fn, and you know how to embed some new set and relation into mp and rel, then by composing fn with your new embedding and using the previously proved well-founded relation lemma you can embed the new set and relation into the ordinals.

    Mp is a predicate that recognizes the objects that are supposedly ordered in a well-founded way by rel. We call such an object an ``mp-measure'' or simply a ``measure'' when mp is understood. Property 1 tells us that every measure can be mapped into an ACL2 ordinal. (See o-p.) This mapping is performed by fn. Property 2 tells us that if the measure x is smaller than the measure y according to rel then the image of x under fn is a smaller than that of y, according to the well-founded relation o<. (See o<.) Thus, the general form of a :well-founded-relation formula establishes that there exists a rel-order preserving embedding (namely via fn) of the mp-measures into the ordinals. We can thus conclude that rel is well-founded on mp-measures.

    Such well-founded relations are used in the admissibility test for recursive functions, in particular, to show that the recursion terminates. To illustrate how such information may be used, consider a generic function definition

    (defun g (x) (if (test x) (g (step x)) (base x))).
    
    If rel has been shown to be well-founded on mp-measures, then g's termination can be ensured by finding a measure, (m x), with the property that m produces a measure:
    (mp (m x)),                                     ; Defun-goal-1
    
    and that the argument to g gets smaller (when measured by m and compared by rel) in the recursion,
    (implies (test x) (rel (m (step x)) (m x))).    ; Defun-goal-2
    
    If rel is selected as the :well-founded-relation to be used in the definition of g, the definitional principal will generate and attempt to prove defun-goal-1 and defun-goal-2 to justify g. We show later why these two goals are sufficient to establish the termination of g. Observe that neither the ordinals nor the embedding, fn, of the mp-measures into the ordinals is involved in the goals generated by the definitional principal.

    Suppose now that a :well-founded-relation theorem has been proved for mp and rel. How can rel be ``selected as the :well-founded-relation'' by defun? There are two ways. First, an xargs keyword to the defun event allows the specification of a :well-founded-relation. Thus, the definition of g above might be written

    (defun g (x)
     (declare (xargs :well-founded-relation (mp . rel)))
     (if (test x) (g (step x)) (base x)))
    
    Alternatively, rel may be specified as the :default-well-founded-relation in acl2-defaults-table by executing the event
    (set-well-founded-relation rel).
    
    When a defun event does not explicitly specify the relation in its xargs the default relation is used. When ACL2 is initialized, the default relation is o<.

    Finally, though it is probably obvious, we now show that defun-goal-1 and defun-goal-2 are sufficient to ensure the termination of g provided property-1 and property-2 of mp and rel have been proved. To this end, assume we have proved defun-goal-1 and defun-goal-2 as well as property-1 and property-2 and we show how to admit g under the primitive ACL2 definitional principal (i.e., using only the ordinals). In particular, consider the definition event

    (defun g (x)
     (declare (xargs :well-founded-relation o<
                     :measure (fn (m x))))
     (if (test x) (g (step x)) (base x)))
    
    Proof that g is admissible: To admit the definition of g above we must prove
    (o-p (fn (m x)))                        ; *1
    
    and
    (implies (test x)                               ; *2
             (o< (fn (m (step x))) (fn (m x)))).
    
    But *1 can be proved by instantiating property-1 to get
    (implies (mp (m x)) (o-p (fn (m x)))),
    
    and then relieving the hypothesis with defun-goal-1, (mp (m x)).

    Similarly, *2 can be proved by instantiating property-2 to get

    (implies (and (mp (m (step x)))
                  (mp (m x))
                  (rel (m (step x)) (m x)))
             (o< (fn (m (step x))) (fn (m x))))
    
    and relieving the first two hypotheses by appealing to two instances of defun-goal-1, thus obtaining
    (implies (rel (m (step x)) (m x))
             (o< (fn (m (step x))) (fn (m x)))).
    
    By chaining this together with defun-goal-2,
    (implies (test x)
             (rel (m (step x)) (m x)))
    
    we obtain *2. Q.E.D.




    acl2-sources/doc/HTML/WET.html0000664002132200015000000002065012222333532015411 0ustar kaufmannacl2 WET.html -- ACL2 Version 6.3

    WET

    evaluate a form and print subsequent error trace
    Major Section:  TRACE
    

    The acronym ``wet'' stands for ``with-error-trace''. Wet provides a convenient way to obtain a backtrace when evaluation causes a guard violation or other error.

    The basic idea is that (wet form) evaluates form and, if there is an error, shows a backtrace of calls that led to that error. Note however that by default only calls of user-defined (not built-in) functions ``supporting'' form in the following sense will show up in the backtrace: those that occur in the macroexpansion of form or (recursively) support any of those functions. So for example, since (make-event form) macroexpands to (make-event-fn (quote form) ...), calls of functions occurring in form will likely not show up in the backtrace by default. The option :fns all overrides this default, with potential loss of speed; more on this below.

    The following example explains the use of wet. First, submit the following three definitions:

    (defun foo (x) (declare (xargs :guard (consp x))) (car x))
    (defun bar (x) (foo (cdr x)))
    (defun g (x) (bar (cdr x)))
    
    Now imagine you have obtained the following guard violation:
    ACL2 !>(g '(3 4))
    
    
    ACL2 Error in TOP-LEVEL:  The guard for the function call (FOO X),
    which is (CONSP X), is violated by the arguments in the call (FOO NIL).
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.  See
    :DOC set-guard-checking for information about suppressing this check
    with (set-guard-checking :none), as recommended for new users.
    
    ACL2 !>
    
    With wet, you can get a backtrace of user-defined functions. The package prefixes shown below, ACL2_*1*_, indicate that the executable (logical) counterparts of the corresponding raw Lisp functions are being called; see guard. Don't forget to start with (include-book "misc/wet" :dir :system).
    ACL2 !>(wet (g '(3 4)))
    ; Fast loading /projects/acl2/devel/books/misc/wet.fasl
    
    TTAG NOTE: Adding ttag :TRACE! from the top level loop.
    
    
    ACL2 Error in WET:  The guard for the function call (FOO X), which
    is (CONSP X), is violated by the arguments in the call (FOO NIL).
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.  See
    :DOC set-guard-checking for information about suppressing this check
    with (set-guard-checking :none), as recommended for new users.
    
    
    Backtrace stack:
    ----------------
    1. (ACL2_*1*_ACL2::FOO NIL)
    2. (ACL2_*1*_ACL2::BAR (4))
    3. (ACL2_*1*_ACL2::G (3 4))
    
    ACL2 !>
    
    By default, large structures are hidden during the printing of the backtrace stack. But you can supply a value for keyword argument :evisc-tuple to modify the printing: nil to avoid hiding, else a suitable evisc-tuple, as shown below (see evisc-tuple).
    ACL2 !>(wet (g '(3 4)) :evisc-tuple (evisc-tuple 1 1 nil nil))
    ; Fast loading /projects/acl2/devel/books/misc/wet.fasl
    
    TTAG NOTE: Adding ttag :TRACE! from the top level loop.
    
    
    ACL2 Error in WET:  The guard for the function call (FOO X), which
    is (CONSP X), is violated by the arguments in the call (FOO NIL).
    To debug see :DOC print-gv, see :DOC trace, and see :DOC wet.  See
    :DOC set-guard-checking for information about suppressing this check
    with (set-guard-checking :none), as recommended for new users.
    
    
    Backtrace stack:
    ----------------
    1. (ACL2_*1*_ACL2::FOO ...)
    2. (ACL2_*1*_ACL2::BAR ...)
    3. (ACL2_*1*_ACL2::G ...)
    
    ACL2 !>
    
    For a backtrace as a data object, evaluate the form (@ wet-stack). But note that this object may not be a legal ACL2 value, for example because of the ``*1*'' symbols shown above.

    General Form:
    (wet form           ; an arbitrary form
         :book bk-form  ; optional, not evaluated
         ;;; the rest are optional and evaluated:
         :evisc-tuple e ; an evisc-tuple
         :fns fns       ; :all, or a list of functions to show in a backtrace
         :compile c     ; :same, t, or nil; default :same (nil if :fns supplied)
    

    Form is evaluated. If there is an error, a backtrace stack is printed to the standard output (*standard-co*), containing (by default) the user-defined function calls made before the error. Such printing is controlled by the :evisc-tuple if supplied; otherwise, hiding of large structures will occur. (Technical detail: by default the global abbrev-evisc-tuple is used, if bound; see set-evisc-tuple.

    The :fns option. As mentioned above, by default the wet backtrace shows user-defined functions that syntactically ``support'' the form being evaluated. This default can be overridden by supplying an explicit list, fns, of functions, using option :fns fns; these will then be the functions whose calls are eligible for inclusion in the backtrace. The special value :fns :all will allow all user-defined function calls in the backtrace. This value can be useful when using oracle-apply, for example, since the function being applied isn't typically included as a syntactic supporter of the form being evaluated.

    The :compile option. Wet uses the trace$ utility to modify the definitions of affected functions so that they can record information for the backtrace. As described above, these affected functions are those that syntactically ``support'' the form unless specified by the :fns option. As is the case for trace$ -- see trace$ -- the new definitions of these affected functions may or may not be compiled. For trace$ and for wet, the default is to compile the new definition if and only if the original definition was compiled, except: For wet, if option :fns :all is provided, then the default is not to compile the affected definitions. And for trace$ and wet, the :compile option overrides the default, to specify what will be compiled: value :same to compile each affected function if and only if its original definition was compiled, value t to compile all affected functions, and value nil to skip compilation.

    The :book option. Wet actually works by temporarily including a community book,

    (include-book "misc/wet" :dir :system)
    
    and then passing its arguments to macro wet!, defined in that book. The keyword argument :book allows you to specify a different book that defines a macro wet! to which to pass its arguments. If the value of :book is a string, then the book named by that string is temporarily included using include-book: (include-book "bk"). Otherwise :book should be a list of arguments, to be provided (unevaluated) to include-book, for example ("my-wet" :dir :my-utils). Thus you can experiment by copying community book books/misc/wet.lisp to your own directory and making modifications to the copy. If you make changes, we invite you to share them with the ACL2 community (see books). Note that you can also supply :book nil, in which case the definition of wet! in your current session will be used without including a book.

    Also see trace$ for a general tracing utility. As mentioned above, wet is implemented using trace$. Wet actually first applies untrace$; upon completion, wet then applies trace$ to re-trace any functions that it had untraced, using their original trace specs.




    acl2-sources/doc/HTML/WHY-BRR.html0000664002132200015000000000705412222333522016046 0ustar kaufmannacl2 WHY-BRR.html -- ACL2 Version 6.3

    WHY-BRR

    an explanation of why ACL2 has an explicit brr mode
    Major Section:  MISCELLANEOUS
    

    Why isn't brr mode automatically disabled when there are no monitored runes? The reason is that the list of monitored runes is kept in a wormhole state.

    See wormhole for more information on wormholes in general. But the fundamental property of the wormhole function is that it is a logical no-op, a constant function that does not take state as an argument. When entering a wormhole, arbitrary information can be passed in (including the external state). That information is used to construct a near copy of the external state and that ``wormhole state'' is the one with respect to which interactions occur during breaks. But no information is carried by ACL2 out of a wormhole -- if that were allowed wormholes would not be logical no-ops. The only information carried out of a wormhole is in the user's head.

    Break-rewrite interacts with the user in a wormhole state because the signature of the ACL2 rewrite function does not permit it to modify state. Hence, only wormhole interaction is possible. (This has the additional desirable property that the correctness of the rewriter does not depend on what the user does during interactive breaks within it; indeed, it is logically impossible for the user to affect the course of rewrite.)

    Now consider the list of monitored runes. Is that kept in the external state as a normal state global or is it kept in the wormhole state? If it is in the external state then it can be inspected within the wormhole but not changed. This is unacceptable; it is common to change the monitored rules as the proof attempt progresses, installing monitors when certain rules are about to be used in certain contexts. Thus, the list of monitored runes must be kept as a wormhole variable. Hence, its value cannot be determined outside the wormhole, where the proof attempt is ongoing.

    This raises another question: If the list of monitored runes is unknown to the rewriter operating on the external state, how does the rewriter know when to break? The answer is simple: it breaks every time, for every rune, if brr mode is enabled. The wormhole is entered (silently), computations are done within the wormhole state to determine if the user wants to see the break, and if so, interactions begin. For unmonitored runes and runes with false break conditions, the silent wormhole entry is followed by a silent wormhole exit and the user perceives no break.

    Thus, the penalty for running with brr mode enabled when there are no monitored runes is high: a wormhole is entered on every application of every rune and the user is simply unware of it. The user who has finally unmonitored all runes is therefore strongly advised to carry this information out of the wormhole and to do :brr nil in the external state when the next opportunity arises.




    acl2-sources/doc/HTML/WITH-FAST-ALIST.html0000664002132200015000000000606412222333520017132 0ustar kaufmannacl2 WITH-FAST-ALIST.html -- ACL2 Version 6.3

    WITH-FAST-ALIST

    (with-fast-alist name form) causes name to be a fast alist for the execution of form.
    Major Section:  HONS-AND-MEMOIZATION
    

    Logically, with-fast-alist just returns form.

    Under the hood, we cause alist to become a fast alist before executing form. If doing so caused us to introduce a new hash table, the hash table is automatically freed after form completes.

    More accurately, under the hood (with-fast-alist name form) essentially expands to something like:

     (if (already-fast-alist-p name)
         form
       (let ((<temp> (make-fast-alist name)))
         (prog1 form
                (fast-alist-free <temp>))))
    

    Practically speaking, with-fast-alist is frequently a better choice then just using make-fast-alist, and is particularly useful for writing functions that can take either fast or slow alists as arguments. That is, consider the difference between:

     (defun bad (alist ...)
       (let* ((fast-alist (make-fast-alist alist))
              (answer     (expensive-computation fast-alist ...)))
        (prog2$ (fast-alist-free fast-alist)
                answer)))
    
     (defun good (alist ...)
        (with-fast-alist alist
          (expensive-computation alist ...)))
    

    Either approach is fine if the caller provides a slow alist. But if the input alist is already fast, bad will (perhaps unexpectedly) free it! On the other hand, good is able to take advantage of an already-fast argument and will not cause it to be inadvertently freed.

    See also the macro with-fast-alists defined in the community book "books/centaur/misc/hons-extra.lisp", which allows you to call with-fast-alist on several alists simultaneously.

    The community book "books/centaur/misc/hons-extra.lisp" extends the b* macro (defined in the community book "books/tools/bstar.lisp") with the with-fast pattern binder. That is, after executing (include-book "centaur/misc/hons-extra.lisp" :dir :system) you may write something like this:

     (b* (...
          ((with-fast a b c ...))
          ...)
       ...)
    

    which causes a, b, and c to become fast alists until the completion of the b* form.

    Note that with-fast-alist will cause logically tail-recursive functions not to execute tail-recursively if its cleanup phase happens after the tail-recursive call returns.




    acl2-sources/doc/HTML/WITH-GUARD-CHECKING.html0000664002132200015000000000231312222333531017531 0ustar kaufmannacl2 WITH-GUARD-CHECKING.html -- ACL2 Version 6.3

    WITH-GUARD-CHECKING

    suppressing or enable guard-checking for a form
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Example:
    
    ; Turn off all guard-checking for the indicated calls of append and car:
    (with-guard-checking :none
                         (car (append 3 4)))
    
    General Form:
    (with-guard-checking val form)
    
    where val evaluates to a legal guard-checking value (see set-guard-checking, or evaluate *guard-checking-values* to see the list of such values), and form is a form to be evaluated as though we had first executed (set-guard-checking val). Of course, this gaurd-checking setting is active only during evaluation of form, and is ignored once evaluation passes into raw Lisp functions (see guards-and-evaluation).




    acl2-sources/doc/HTML/WITH-LIVE-STATE.html0000664002132200015000000000362312222333525017143 0ustar kaufmannacl2 WITH-LIVE-STATE.html -- ACL2 Version 6.3

    WITH-LIVE-STATE

    allow a reference to state in raw Lisp
    Major Section:  ACL2-BUILT-INS
    

    The macro with-live-state is an advanced feature that very few users will need (basically, only system hackers). Indeed, it is untouchable; see remove-untouchable for how to enable calling with-live-state in the ACL2 loop.

    Example Form:
    (with-live-state (assign y 3))
    
    General form:
    (with-live-state form)
    
    where form is an arbitrary form with a free reference to the variable state.

    Logically, (with-live-state FORM) macroexpands to FORM. However, in raw Lisp it expands to:

    (let ((state *the-live-state*))
      FORM)
    

    If a form that mentions the variable state might be executed in raw Lisp -- that is, either outside the ACL2 loop or in raw mode (see set-raw-mode) -- then the surrounding the form with with-live-state as shown above can avoid potential warnings or (much less likely) errors. Note however that if state is lexically bound to a state other than the usual ``live'' state, surprising behavior may occur when evaluating a call of with-live-state in raw Lisp or raw mode (either directly by evaluation or at compile time), because with-live-state will override that lexical binding of state by a lexical binding of state to the usual ``live'' state.




    acl2-sources/doc/HTML/WITH-LOCAL-STATE.html0000664002132200015000000000652112222333530017232 0ustar kaufmannacl2 WITH-LOCAL-STATE.html -- ACL2 Version 6.3

    WITH-LOCAL-STATE

    locally bind state
    Major Section:  STOBJ
    

    This is an advanced topic, probably of interest only to system developers.

    Consider the following example form:

    (with-local-state
     (mv-let (result state)
             (compute-with-state x state)
             result))
    
    This is equivalent to the following form.
    (with-local-stobj
     state
     (mv-let (result state)
             (compute-with-state x state)
             result))
    
    By default, this form is illegal, because ACL2 does not have a way to unwind all changes to the ACL2 state; we say more on this issue below. There may however be situations where you are willing to manage or overlook this issue. In that case you may execute the following form to enable the use of with-local-state, by enabling the use of with-local-stobj on state; but note that it requires an active trust tag (see defttag).
    (remove-untouchable create-state t)
    

    Please be aware that no local state is actually created, however! In particular, users of with-local-state need either to ensure that channels are closed and state global variables are returned to their original values, or else be willing to live with changes made to state that are not justified by the code that has been evaluated. You are welcome to look in the the ACL2 source code at the definition of macro channel-to-string, which employs with-local-state to create a local state for the purpose of creating a string.

    Here is an example use of with-local-state. Notice the use of defttag -- and indeed, please understand that we are just hacking here, and in general it takes significant effort to be sure that one is using with-local-state correctly!

    (defttag t)
    
    (remove-untouchable create-state t)
    
    (set-state-ok t)
    
    (defun foo (state)
      (declare (xargs :mode :program))
      (mv-let
       (channel state)
       (open-input-channel "my-file" :object state)
       (mv-let (eofp obj state)
               (read-object channel state)
               (declare (ignore eofp))
               (let ((state (close-input-channel channel state)))
                 (mv obj state)))))
    
    (defun bar ()
      (declare (xargs :mode :program))
      (with-local-state (mv-let (result state)
                                (foo state)
                                result)))
    
    ; Multiple-value return version:
    
    (defun foo2 (state)
      (declare (xargs :mode :program))
      (mv-let
       (channel state)
       (open-input-channel "my-file" :object state)
       (mv-let (eofp obj state)
               (read-object channel state)
               (let ((state (close-input-channel channel state)))
                 (mv eofp obj state)))))
    
    (defun bar2 ()
      (declare (xargs :mode :program))
      (with-local-state (mv-let (eofp result state)
                                (foo2 state)
                                (mv eofp result))))
    




    acl2-sources/doc/HTML/WITH-LOCAL-STOBJ.html0000664002132200015000000000711112222333530017227 0ustar kaufmannacl2 WITH-LOCAL-STOBJ.html -- ACL2 Version 6.3

    WITH-LOCAL-STOBJ

    locally bind a single-threaded object
    Major Section:  STOBJ
    

    See stobj for an introduction to single-threaded objects.

    Example Form:
    (with-local-stobj
     st
     (mv-let (result st)
             (compute-with-st x st)
             result))
    
    With-local-stobj can be thought of as a macro, where the example form above expands as follows.
    (mv-let (result st)
            (let ((st (create-st)))
              (compute-with-st x st))
            (declare (ignore st))
            result)
    
    However, ACL2 expects you to use with-local-stobj, not its expansion. More precisely, stobj creator functions are not allowed except (implicitly) via with-local-stobj and in logic-only situations (like theorems and hints). Moreover, neither with-local-stobj nor its expansions are legal when typed directly at the top-level loop. See top-level for a way to use with-local-stobj in the top-level loop.

    General Forms:
    (with-local-stobj stobj-name mv-let-form)
    (with-local-stobj stobj-name mv-let-form creator-name)
    
    where stobj-name is the name of a stobj, mv-let-form is a call of mv-let, and if creator-name is supplied then it should be the name of the creator function for stobj-name; see defstobj. For the example form above, its expansion would use creator-name, if supplied, in place of create-st. Note that stobj-name must not be state (the ACL2 state), except in special situations probably of interest only to system developers; see with-local-state.

    With-local-stobj can be useful when a stobj is used to memoize intermediate results during a computation, yet it is desired not to make the stobj a formal parameter for the function and its callers.

    ACL2 can reason about these ``local stobjs,'' and in particular about stobj creator functions. For technical reasons, ACL2 will not allow you to enable the :EXECUTABLE-COUNTERPART rune of a stobj creator function.

    Finally, here is a small example concocted in order to illustrate that with-local-stobj calls can be nested.

    (defstobj st fld1)
    
    (defun foo ()
      (with-local-stobj
       st ; Let us call this the ``outer binding of st''.
       (mv-let (val10 val20 st)
         (let ((st (update-fld1 10 st)))
           ;; At this point the outer binding of st has fld1 = 10.
           (let ((result (with-local-stobj
                          st ; Let us call this the ``inner binding of st''.
                          (mv-let (val st)
                            (let ((st (update-fld1 20 st)))
                              ;; Now fld1 = 20 for the inner binding of st.
                              (mv (fld1 st) st))
                            val))))
             ;; So result has been bound to 20 above, but here we are once again
             ;; looking at the outer binding of st, where fld1 is still 10.
             (mv (fld1 st) result st)))
         (mv val10 val20))))
    
    (thm (equal (foo) (mv 10 20))) ; succeeds
    




    acl2-sources/doc/HTML/WITH-OUTPUT-LOCK.html0000664002132200015000000000613612222333522017313 0ustar kaufmannacl2 WITH-OUTPUT-LOCK.html -- ACL2 Version 6.3

    WITH-OUTPUT-LOCK

    provides a mutual-exclusion mechanism for performing output in parallel
    Major Section:  PARALLEL-PROGRAMMING
    

    This documentation topic relates to an experimental extension of ACL2, ACL2(p), created initially by David L. Rager. See compiling-acl2p for how to build an executable image that supports parallel execution. Also see community books directory books/parallel/ for examples.

    One may wish to perform output while executing code in parallel. If threads are allowed to print concurrently, the output will be interleaved and often unreadable. To avoid this, the user can surround forms that perform output with the with-output-lock macro.

    Take the following definition of pfib as an example.

    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((mbe :logic (or (zp x) (<= x 0))
                  :exec (<= x 0))
             0)
            ((= x 1) 1)
            (t (plet (declare (granularity t))
                     ((a (prog2$ (cw "Computing pfib ~x0~%" (- x 1))
                                 (pfib (- x 1))))
                      (b (prog2$ (cw "Computing pfib ~x0~%" (- x 2))
                                 (pfib (- x 2)))))
                     (+ a b)))))
    

    With parallel-execution enabled, a call of (pfib 5)results in non-deterministically interleaved output, for example as follows.

    ACL2 !>(pfib 5)
    CComputing pfib 4
    omputing pfib 3
    ComCpuotmipnugt ipnfgi bp fib3
    2
    Computing pCfiobm put2i
    ng pfib 1
    Computing pfib Co1mp
    uting pfib 0
    CCoommppuuttiinngg  ppffiibb  12
    
    ComCpuotmipnugt ipnfgi bp fib1
    0
    CoCmopmuptuitnign gp fpifbi b 1
    0
    5
    ACL2 !>
    

    If the user instead surrounds the calls to cw with the macro with-output-lock, as in the following session, the output will no longer be interleaved.

    ACL2 !>
    (defun pfib (x)
      (declare (xargs :guard (natp x)))
      (cond ((mbe :logic (or (zp x) (<= x 0))
                  :exec (<= x 0))
             0)
            ((= x 1) 1)
            (t (plet (declare (granularity t))
                     ((a (prog2$ (with-output-lock
                                  (cw "Computing pfib ~x0~%" (- x 1)))
                                 (pfib (- x 1))))
                      (b (prog2$ (with-output-lock
                                  (cw "Computing pfib ~x0~%" (- x 2)))
                                 (pfib (- x 2)))))
                     (+ a b)))))
    
    <snip>
    
    ACL2 !>(pfib 5)
    Computing pfib 4
    Computing pfib 3
    Computing pfib 3
    Computing pfib 2
    Computing pfib 2
    Computing pfib 1
    Computing pfib 2
    Computing pfib 1
    Computing pfib 1
    Computing pfib 0
    Computing pfib 1
    Computing pfib 0
    Computing pfib 1
    Computing pfib 0
    5
    ACL2 !>
    




    acl2-sources/doc/HTML/WITH-OUTPUT.html0000664002132200015000000002041112222333531016555 0ustar kaufmannacl2 WITH-OUTPUT.html -- ACL2 Version 6.3

    WITH-OUTPUT

    suppressing or turning on specified output for an event
    Major Section:  SWITCHES-PARAMETERS-AND-MODES
    

    Examples:
    
    ; Turn off all output during evaluation of the indicated thm form.
    (with-output
     :off :all
     :gag-mode nil
     (thm (equal (app (app x y) z) (app x (app y z)))))
    
    ; Prove the indicated theorem with the event summary turned off and
    ; using the :goals setting for gag-mode.
    (with-output
       :off summary
       :gag-mode :goals
       (defthm app-assoc (equal (app (app x y) z) (app x (app y z)))))
    
    ; Same effect as just above:
    (with-output
       :on summary
       :summary nil
       :gag-mode :goals
       (defthm app-assoc (equal (app (app x y) z) (app x (app y z)))))
    
    ; Turn on only the indicated parts of the summary.
    (with-output
       :on summary
       :summary (time rules)
       :gag-mode :goals  ; use gag-mode, with goal names printed
       (defthm app-assoc (equal (app (app x y) z) (app x (app y z)))))
    
    ; Same as specifying :off :all, but showing all output types:
    (with-output
     :off (error warning warning! observation prove proof-checker event expansion
                 summary proof-tree)
     :gag-mode nil
     (thm (equal (app (app x y) z) (app x (app y z)))))
    
    ; Same as above, but :stack :push says to save the current
    ; inhibit-output-lst, which can be restored in a subsidiary with-output call
    ; that specifies :stack :pop.
    (with-output
     :stack :push
     :off :all
     :gag-mode nil
     (thm (equal (app (app x y) z) (app x (app y z)))))
    
    General Form:
    (with-output :key1 val1 ... :keyk valk form)
    
    where each :keyi is either :off, :on, :stack, :summary, or :gag-mode; form evaluates to an error triple (see error-triples); and vali is as follows. If :keyi is :off or :on, then vali can be :all, and otherwise is a symbol or non-empty list of symbols representing output types that can be inhibited; see set-inhibit-output-lst. If :keyi is :gag-mode, then vali is one of the legal values for :set-gag-mode. If :keyi is :summary, then vali is either :all or a true-list of symbols each of which belongs to the list *summary-types*. Otherwise :keyi is :stack, in which case :vali is :push or :pop; for now assume that :stack is not specified (we'll return to it below). The result of evaluating the General Form above is to evaluate form, but in an environment where output occurs as follows. If :on :all is specified, then every output type is turned on except as inhibited by :off; else if :off :all is specified, then every output type is inhibited except as specified by :on; and otherwise, the currently-inhibited output types are reduced as specified by :on and then extended as specified by :off. But if :gag-mode is specified, then before modifying how output is inhibited, gag-mode is set for the evaluation of form as specified by the value of :gag-mode; see set-gag-mode. If summary is among the output types that are turned on (not inhibited), then if :summary is specified, the only parts of the summary to be printed will be those specified by the value of :summary. The correspondence should be clear, except perhaps that header refers to the line containing only the word Summary, and value refers to the value of the form printed during evaluation of sequences of events as for progn and encapsulate.

    Note that the handling of the :stack argument pays no attention to the :summary argument.

    Note: When the scope of with-output is exited, then all modifications are undone, reverting gag-mode and the state of output inhibition to those which were present before the with-output call was entered.

    The :stack keyword's effect is illustrated by the following example, where ``(encapsulate nil)'' may replaced by ``(progn'' without any change to the output that is printed.

    (with-output
     :stack :push :off :all
     (encapsulate ()
       (defun f1 (x) x)
       (with-output :stack :pop (defun f2 (x) x))
       (defun f3 (x) x)
       (with-output :stack :pop :off warning (in-theory nil))
       (defun f4 (x) x)))
    
    The outer with-output call saves the current output settings (as may have been modified by earlier calls of set-inhibit-output-lst), by pushing them onto a stack, and then turns off all output. Each inner with-output call temporarily pops that stack, restoring the starting output settings, until it completes and undoes the effects of that pop. Unless event output was inhibited at the top level (see set-inhibit-output-lst), the following output is shown:
    Since F2 is non-recursive, its admission is trivial.  We observe that
    the type of F2 is described by the theorem (EQUAL (F2 X) X).
    
    And then, if summary output was not inhibited at the top level, we get the rest of this output:
    Summary
    Form:  ( DEFUN F2 ...)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Summary
    Form:  ( IN-THEORY NIL)
    Rules: NIL
    Warnings:  None
    Time:  0.00 seconds (prove: 0.00, print: 0.00, other: 0.00)
    
    Note that the use of :off warning supresses a "Theory" warning for the (in-theory nil) event, and that in no case will output be printed for definitions of f1, f3, or f4, or for the encapsulate event itself.

    The following more detailed explanation of :stack is intended only for advanced users. After :gag-mode is handled (if present) but before :on or :off is handled, the value of :stack is handled as follows. If the value is :push, then state global inhibit-output-lst-stack is modified by pushing the value of state global inhibit-output-lst onto the value of state global inhibit-output-lst-stack, which is nil at the top level. If the value is :pop, then state global inhibit-output-lst-stack is modified only if non-nil, in which case its top element is popped and becomes the value of of state global inhibit-output-lst.

    Warning: With-output has no effect in raw Lisp, and hence is disallowed in function bodies. However, you can probably get the effect you want as illustrated below, where <form> must return an error triple (mv erp val state); see ld and see error-triples.

    Examples avoiding with-output, for use in function definitions:
    
    ; Inhibit all output:
    (state-global-let*
     ((inhibit-output-lst *valid-output-names*))
     <form>)
    
    ; Inhibit all warning output:
    (state-global-let*
     ((inhibit-output-lst
       (union-eq (f-get-global 'inhibit-output-lst state)
                 '(warning warning!))))
     <form>)
    

    Note that with-output is allowed in books. See embedded-event-form.




    acl2-sources/doc/HTML/WITH-PROVER-STEP-LIMIT.html0000664002132200015000000001610212222333522020161 0ustar kaufmannacl2 WITH-PROVER-STEP-LIMIT.html -- ACL2 Version 6.3

    WITH-PROVER-STEP-LIMIT

    limit the number of steps for proofs
    Major Section:  OTHER
    

    Logically, (with-prover-step-limit expr form) is equivalent to form, except that if the number of ``prover steps'' executed during evaluation of form exceeds a bound specified by the value of expr, then that proof will abort. See set-prover-step-limit for a related utility that sets the limit on prover steps globally instead of setting it for just one form, and for a discussion of the notion of ``prover steps'', which could change in future ACL2 releases. For a related utility based on time instead of prover steps, see with-prover-time-limit; but as discussed in the documentation for set-prover-step-limit, there is at best a loose connection between the counting of steps and with-prover-time-limit.

    The arguments of (with-prover-step-limit expr form) are evaluated. However, the (optional) argument flg is not evaluated in (with-prover-step-limit expr flg form).

    Depending on the machine you are using, you may have less than a half-hour of time before the number of prover steps exceeds the maximum limit on prover steps that may be imposed, which is one less than the value of *default-step-limit*. But there is no limit unless you explicitly call with-prover-step-limit or set-prover-step-limit.

    For examples of how step-limits work besides those presented below, see the community book books/misc/misc2/step-limits.lisp.

    For a utility that returns an indicator of the number of prover steps most recently taken, see last-prover-steps.

    Note that with-prover-step-limit may not be called inside definitions, and that it is simply the identity macro in raw Lisp. However, with-prover-step-limit! may be called in place of with-prover-step-limit, with the effect described here even in raw Lisp.

    Examples:
    
    ; Limit (mini-proveall) to 100,000 prover steps (which happens to suffice)
    (with-prover-step-limit 100000 (mini-proveall))
    
    ; Same as above for the inner call of with-prover-step-limit; so the
    ; mini-proveall call completes, but then we get an error because the second
    ; argument of the outer with-prover-step-limit call took more than 200
    ; steps.
    (with-prover-step-limit
     200
     (with-prover-step-limit 100000 (mini-proveall)))
    
    ; Same as preceding form just above, except that this time there is no error,
    ; because the inner call of with-prover-step-limit has an extra argument
    ; of t inserted into the second argument position, which specifies that this
    ; entire inner call is treated as though it uses no prover steps.
    (with-prover-step-limit
     200
     (with-prover-step-limit 100000 t (mini-proveall)))
    
    ; The inner call limits (mini-proveall) to 200 prover steps, which fails
    ; almost immediately.
    (with-prover-step-limit 100000 (with-prover-step-limit 200 (mini-proveall)))
    
    ; Do not limit the number of prover steps, regardless of such a limit imposed
    ; globally or by the surrounding context.
    (with-prover-step-limit nil (mini-proveall))
    
    ; Same as just above (indeed, nil above is converted to
    ; *default-step-limit*):
    (with-prover-step-limit *default-step-limit* (mini-proveall))
    
    ; Advanced example: Limit the indicated theorem to 100 steps, and when the
    ; proof does not complete, then put down a label instead.
    (mv-let (erp val state)
            (with-prover-step-limit
             100
             (thm (equal (append (append x x) x)
                         (append x x x))))
            (if erp
                (deflabel foo :doc "Attempt failed.")
              (value (list :succeeded-with val))))
    
    ; Use extra argument of t to avoid ``charging'' steps for the indicated
    ; form.
    (with-prover-step-limit
     500
     (encapsulate
      ()
      (with-prover-step-limit
       500
       t ; Don't charge prover steps for this first defthm.
       (defthm test1
         (equal (append x (append y z))
                (append (append x y) z))
         :rule-classes nil))
      (defthm test2
        (equal (append x (append y z))
               (append (append x y) z))
        :rule-classes nil)))
    
    General Forms:
    (with-prover-step-limit expr form)
    (with-prover-step-limit expr flg form)
    
    where form is an arbitrary form to evaluate, and expr evaluates to one of the following: nil; a natural number not exceeding the value of *default-step-limit*; or the special value, :START. The optional extra argument in the second position, flg, must be Boolean if supplied.

    If the value of expr is a natural number less than the value of *default-step-limit*, then that value is the maximum number of prover steps permitted during evaluation of form before an error occurs. If however the value of expr is nil or is the value of *default-step-limit*, then no limit is placed on the number of prover steps during processing of form. Otherwise, the value of expr should be the keyword :START, which is intended for use by the ACL2 implementation and may have semantics that change with new ACL2 versions.

    Finally we describe the optional extra Boolean argument, flg. If flg is nil or omitted, then a running count of prover steps is maintained after form is evaluated; otherwise, that count is not affected by evaluation of form. To see how this works when flg is nil or omitted, consider an event of shape (progn form1 form2), where we are tracking prover steps (say, because of a superior call of with-prover-step-limit). If n is the number of prover steps available when the progn form is entered, and if s prover steps are executed while evaluating form1, then n-s steps are available for evaluation of form2 provided s does not exceed n; otherwise, an error occurs. In particular, this is the case if form1 is an event (with-prover-step-limit k form1'). However, if form1 is an event (with-prover-step-limit k t form1'), then because of the extra argument of t, no steps are ``charged'' to form; that is, all n steps, rather than n-s steps, are available for evaluation of form2.




    acl2-sources/doc/HTML/WITH-PROVER-TIME-LIMIT.html0000664002132200015000000001253212222333522020147 0ustar kaufmannacl2 WITH-PROVER-TIME-LIMIT.html -- ACL2 Version 6.3

    WITH-PROVER-TIME-LIMIT

    limit the time for proofs
    Major Section:  OTHER
    

    Examples:
    
    ; Limit (mini-proveall) to about 1/4 second:
    (with-prover-time-limit 1/4 (mini-proveall))
    
    ; Limit (mini-proveall) to about 1/4 second, even if surrounding call of
    ; with-prover-time-limit provides for a more restrictive bound:
    (with-prover-time-limit '(1/4) (mini-proveall))
    
    ; Limit the indicated theorem to about 1/50 second, and if the proof does not
    ; complete or it fails, then put down a label instead.
    (mv-let (erp val state)
            (with-prover-time-limit
             1/50
             (thm (equal (append (append x x) x)
                         (append x x x))))
            (if erp
                (deflabel foo :doc "Attempt failed.")
              (value (list :succeeded-with val))))
    
    General Form:
    (with-prover-time-limit time form)
    
    where time evaluates to a positive rational number or to a list containing such, and form is arbitrary. Logically, (with-prover-time-limit time form) is equivalent to form. However, if the time for evaluation of form exceeds the value specified by time, and if ACL2 notices this fact during a proof, then that proof will abort, for example like this:
    ACL2 Error in ( DEFTHM PERM-REFLEXIVE ...):  Out of time in the rewriter.
    
    If there is already a surrounding call of with-prover-time-limit that has set up an expiration time, the inner with-prover-time-limit call is not allowed to push that time further into the future unless the inner time is specified as a list containing a rational, rather than as a rational.

    Note that by default, the time used is runtime (cpu time); to switch to realtime (elapsed time), see get-internal-time.

    For a related utility based on prover steps instead of time, see with-prover-step-limit; also see set-prover-step-limit. Those utilities have the advantage of having platform-independent behavior, unlike time limits, which of course are generally less restrictive for faster processors. But note that the prover steps counted need not correspond closely to prover time.

    Although with-prover-time-limit behaves like an ACL2 function in the sense that it evaluates both its arguments, it is however actually a macro that behaves as follows. (1) The value of its first (time limit) argument affects the evaluation of its second argument (by causing an error during that evaluation if the time for completion is insufficient). (2) The second argument can return multiple values (see mv), which are then returned by the call of with-prover-time-limit. (3) Thus, there is not a fixed number of values returned by with-prover-time-limit.

    If you find that the time limit appears to be implemented too loosely, it may be because the prover only checks the time elapsed at certain points during the proof process, for example at entry to the rewriter. For example, if you write your own clause-processor that does an expensive computation, the time is unlikely to be checked during its execution. If however you find the time limit seems to be ignored even during ordinary prover operation, you are encouraged to email an example to the ACL2 implementors with instructions on how to observe the undesirable behavior. This information can perhaps be used to improve ACL2 by the insertion of more checks for expiration of the time limit.

    The rest of this documentation topic explains the rather subtle logical story, and is not necessary for understanding how to use with-prover-time-limit. The ACL2 state object logically contains a field called the acl2-oracle, which is an arbitrary true list of objects. This field can be read by a function called read-acl2-oracle, which however is untouchable (see push-untouchable), meaning that it is cannot be called by ACL2 users. The acl2-oracle field is thus ``secret''. Our claim is that any ACL2 session makes sense for some value of acl2-oracle in the initial state for that session. Logically, with-prover-time-limit is a no-op, just returning its second value. But under the hood, it provides a ``hint'' for the acl2-oracle, so that (logically speaking) when its first element (car) is consulted by ACL2's prover to see if the time limit has expired, it gets the ``right'' answer (specifically, either nil if all is well or else a message to print if the time limit has expired). Logically, the acl2-oracle is then cdr'ed -- that is, its first element is popped off -- so that future results from read-acl2-oracle are independent of the one just obtained.




    acl2-sources/doc/HTML/WITH-SERIALIZE-CHARACTER.html0000664002132200015000000000731212222333530020342 0ustar kaufmannacl2 WITH-SERIALIZE-CHARACTER.html -- ACL2 Version 6.3

    WITH-SERIALIZE-CHARACTER

    control output mode for print-object$
    Major Section:  SERIALIZE
    

    This documentation topic relates to an experimental extension of ACL2 that supports hons, memoization, and fast alists. See hons-and-memoization. See serialize for a discussion of ``serialization'' routines, contributed by Jared Davis for saving ACL2 objects in files for later loading.

    The expression (with-serialize-character char form) evaluates to the value of form, but with the serialize-character of the state assigned to char, which should be one of nil, #\Y, or #\Z. We describe the effect of that assignment below. But note that if you are doing this because of one or more specific calls of print-object$, such as (print-object$ x channel state), then you may wish instead to evaluate (print-object$-ser x serialize-character channel state), in which case you will not need to use with-serialize-character. (Note however that serialize-character is treated as nil for other than a HONS version.)

    General forms:
    (with-serialize-character nil form)
    (with-serialize-character #Y form)
    (with-serialize-character #Z form)
    
    where form should evaluate to an error triple (see error-triples).

    Note that if you prefer to obtain the same behavior (as described below) globally, rather than only within the scope of with-serialize-character, then use set-serialize-character in a corresponding manner:

    (set-serialize-character nil state)
    (set-serialize-character #Y state)
    (set-serialize-character #Z state)
    

    In each case above, calls of print-object$ (see io) in form will produce an object that can be read by the HONS version of ACL2. In the first case, that object is printed as one might expect at the terminal, as an ordinary Lisp s-expression. But in the other cases, the object is printed by first laying down either #Y or #Z (as indicated) and then calling serialize-write (or more precisely, the underlying function called by serialize-write that prints to a stream).

    Consider what happens when the ACL2 reader encounters an object produced as described above (in the #Y or #Z case). When the object was written, information was recorded on whether that object was a hons. In the case of #Z, the object will be read as a hons if and only if it was originally written as a hons. But in the case of #Y, it will never be read as a hons. Thus, #Y and #Z will behave the same if the original written object was not a hons, creating an object that is not a hons. For an equivalent explanation and a bit more discussion, see serialize-read, in particular the discussion of the hons-mode. The value :smart described there corresponds to #Z, while :never corresponds to #Y.




    acl2-sources/doc/HTML/WITH-STOLEN-ALIST.html0000664002132200015000000000301512222333520017372 0ustar kaufmannacl2 WITH-STOLEN-ALIST.html -- ACL2 Version 6.3

    WITH-STOLEN-ALIST

    (with-stolen-alist name form) ensures that name is a fast alist at the start of the execution of form. At the end of execution, it ensures that name is a fast alist if and only if it was originally. That is, if name was not a fast alist originally, its hash table link is freed, and if it was a fast alist originally but its table was modified during the execution of form, that table is restored. Note that any extended table created from the original fast alist during form must be manually freed.
    Major Section:  HONS-AND-MEMOIZATION
    

    Logically, with-stolen-alist just returns form.

    Under the hood, we cause alist to become a fast alist before executing form, and we check the various conditions outlined above before returning the final value.

    Note that with-stolen-alist will cause logically tail-recursive functions not to execute tail-recursively if its cleanup phase happens after the tail-recursive call returns.




    acl2-sources/doc/HTML/WITHOUT-EVISC.html0000664002132200015000000000546612222333520016771 0ustar kaufmannacl2 WITHOUT-EVISC.html -- ACL2 Version 6.3

    WITHOUT-EVISC

    print output in full
    Major Section:  IO
    

    General Form:
    (without-evisc form)
    
    where form is any expression to evaluate. The effect is to evaluate form as though the without-evisc wrapper were absent, except that expressions are printed in full for the ensuing output, regardless of the current evisc-tuples (see set-evisc-tuple). See set-iprint for an example.

    More precisely, without-evisc binds each of the term-evisc-tuple, ld-evisc-tuple, abbrev-evisc-tuple and gag-mode-evisc-tuple to nil (see set-evisc-tuple). It does not modify the trace evisc-tuple, so trace output is not modified by without-evisc. Also note that calls of printing functions such as fmt that include explicit evisc-tuples will not have those evisc-tuples overridden. The following example illustrates this point.

    ACL2 !>(without-evisc
            (fms "~x0~%"
                 (list (cons #0 '((a b ((c d)) e f g) u v w x y)))
                 *standard-co*
                 state
                 (evisc-tuple 2 3 nil nil)))
    
    ((A B # ...) U V ...)
    <state>
    ACL2 !>
    

    We conclude with two remarks. (1) A call of without-evisc on expression exp actually invokes a specialized call of ld on a one-element list containing exp, which prints the value returned by evaluation of exp but actually returns the useless value (mv nil :invisible state). So do not use without-evisc in programs; just use it at the top level of the ACL2 read-eval-print loop, or at least the top level of ld. (2) Even when using without-evisc, if the ACL2 logical world is part of the value returned, it will be printed in abbreviated form because the ACL2 read-eval-print loop always arranges for this to be the case, regardless of the ld-evisc-tuple. For example:

    ACL2 !>(without-evisc (w state))
    <world>
    ACL2 !>
    

    An alternative to the use of without-evisc is to explore large objects using the ACL2 function (walkabout object state). Some brief documentation is printed when you enter an interactive loop upon evaluating a call of walkabout. We may add documentation for walkabout if that is requested.




    acl2-sources/doc/HTML/WOF.html0000664002132200015000000000235212222333522015403 0ustar kaufmannacl2 WOF.html -- ACL2 Version 6.3

    WOF

    direct standard output and proofs output to a file
    Major Section:  OTHER
    

    Example Form:
    (wof "tmp" (pso)) ; same as (psof "tmp")
    
    General Form:
    (wof filename form)
    
    where filename is a writable filename and form is any form that evaluates to an error triple (see programming-with-state), that is, a multiple value of the form (mv erp val state). All output to channels standard-co and proofs-co will be directed to the indicated file. It is acceptable to replace filename with (quote filename).

    Note that so-called comment-window output (see cw and see observation-cw) is not redirected by wof to a file, nor is printing from a wormhole.




    acl2-sources/doc/HTML/WORLD.html0000664002132200015000000001733012222333522015641 0ustar kaufmannacl2 WORLD.html -- ACL2 Version 6.3

    WORLD

    ACL2 property lists and the ACL2 logical database
    Major Section:  MISCELLANEOUS
    

    The ACL2 logical world is a data structure that includes all logical content resulting from the commands evaluated, back through and including initialization, but not including commands that have been undone (see ubt). Thus in particular, the world includes a represention of the current logical theory, as well as some extra-logical information such as the values of ACL2 tables. The rest of this topic focuses on the structure of the the ACL2 world and, more generally, the ``world'' data structure.

    A ``world'' is a list of triples, each of the form (sym prop . val), implementing the ACL2 notion of property lists. ACL2 permits the simultaneous existence of many property list worlds. ``The world'' is often used as a shorthand for ``the ACL2 logical world'' which is the particular property list world used within the ACL2 system to maintain a database that contiains rules, tables, and so on.

    Common Lisp provides the notion of ``property lists'' by which one can attach ``properties'' and their corresponding ``values'' to symbols. For example, one can arrange for the 'color property of the symbol 'box-14 to be 'purple and the 'color property of the symbol 'triangle-7 to be 'yellow. Access to property lists is given via the Common Lisp function get. Thus, (get 'box-14 'color) might return 'purple. Property lists can be changed via the special form setf. Thus, (setf (get 'box-14 'color) 'blue) changes the Common Lisp property list configuration so that (get 'box-14 'color) returns 'blue. It should be obvious that ACL2 cannot provide this facility, because Common Lisp's get ``function'' is not a function of its argument, but instead a function of some implicit state object representing the property list settings for all symbols.

    ACL2 provides the functions getprop and putprop which allow one to mimic the Common Lisp property list facility. However, ACL2's getprop takes as one of its arguments a list that is a direct encoding of what was above called the ``state object representing the property list settings for all symbols.'' Because ACL2 already has a notion of ``state'' that is quite distinct from that used here, we call this property list object a ``world.'' A world is just a true list of triples. Each triple is of the form (sym prop . val). This world can be thought of as a slightly elaborated form of association list and getprop is a slightly elaborated form of assoc that takes two keys. When getprop is called on a symbol, s, property p, and world, w, it scans w for the first triple whose sym is s and prop is p and returns the corresponding val. Getprop has two additional arguments, one of which that controls what it returns if no such sym and prop exist in w, and other other of which allows an extremely efficient implementation. To set some property's value for some symbol, ACL2 provides putprop. (putprop sym prop val w) merely returns a new world, w', in which (sym prop . val) has been consed onto the front of w, thus ``overwriting'' the prop value of sym in w to val and leaving all other properties in w unchanged.

    One aspect of ACL2's property list arrangment is that it is possible to have many different property list worlds. For example, 'box-14 can have 'color 'purple in one world and can have 'color 'yes in another, and these two worlds can exist simultaneously because getprop is explicitly provided the world from which the property value is to be extracted.

    The efficiency alluded to above stems from the fact that Common Lisp provides property lists. Using Common Lisp's provisions behind the scenes, ACL2 can ``install'' the properties of a given world into the Common Lisp property list state so as to make retrieval via getprop very fast in the special case that the world provided to getprop has been installed. To permit more than one installed world, each of which is permitted to be changed via putprop, ACL2 requires that worlds be named and these names are used to distinquish installed versions of the various worlds. At the moment we do not further document getprop and putprop.

    However, the ACL2 system uses a property list world, named 'current-acl2-world, in which to store the succession of user commands and their effects on the logic. This world is often referred to in our documentation as ``the world'' though it should be stressed that the user is permitted to have worlds and ACL2's is in no way distinguished except that the user is not permitted to modify it except via event commands. The ACL2 world is part of the ACL2 state and may be obtained via (w state).

    Warning: The ACL2 world is very large. Its length as of this writing (Version 2.5) is over 40,000 and it grows with each release. Furthermore, some of the values stored in it are pointers to old versions of itself. Printing (w state) is something you should avoid because you likely will not have the patience to await its completion. For these practical reasons, the only thing you should do with (w state) is provide it to getprop, as in the form

      (getprop sym prop default 'current-acl2-world (w state))
    
    to inspect properties within it, or to pass it to ACL2 primitives, such as theory functions, where it is expected.

    Some ACL2 command forms, such as theory expressions (see theories) and the values to be stored in tables (see table), are permitted to use the variable symbol world freely with the understanding that when these forms are evaluated that variable is bound to (w state). Theoretically, this gives those forms complete knowledge of the current logical configuration of ACL2. However, at the moment, few world scanning functions have been documented for the ACL2 user. Instead, supposedly convenient macro forms have been created and documented. For example, (current-theory :here), which is the theory expression which returns the currently enabled theory, actually macroexpands to (current-theory-fn :here world). When evaluated with world bound to (w state), current-theory-fn scans the current ACL2 world and computes the set of runes currently enabled in it.




    acl2-sources/doc/HTML/WORMHOLE-DATA.html0000664002132200015000000000130112222333522016704 0ustar kaufmannacl2 WORMHOLE-DATA.html -- ACL2 Version 6.3

    WORMHOLE-DATA

    determines the wormhole data object from a wormhole status object
    Major Section:  MISCELLANEOUS
    

    General Form:  (wormhole-data whs)
    
    See wormhole. Returns the wormhole data from a well-formed wormhole status whs. If whs is nil or not well-formed, the data is nil.




    acl2-sources/doc/HTML/WORMHOLE-ENTRY-CODE.html0000664002132200015000000000136412222333522017615 0ustar kaufmannacl2 WORMHOLE-ENTRY-CODE.html -- ACL2 Version 6.3

    WORMHOLE-ENTRY-CODE

    determines the wormhole entry code from a wormhole status object
    Major Section:  MISCELLANEOUS
    

    General Form:  (wormhole-entry-code whs)
    
    See wormhole. Returns :ENTER or :SKIP given a well-formed wormhole status whs. If whs is nil or not well-formed, the entry code is :ENTER.




    acl2-sources/doc/HTML/WORMHOLE-EVAL.html0000664002132200015000000001146512222333522016736 0ustar kaufmannacl2 WORMHOLE-EVAL.html -- ACL2 Version 6.3

    WORMHOLE-EVAL

    state-saving without state -- a short-cut to a parallel universe
    Major Section:  MISCELLANEOUS
    

    Example Form:
    (wormhole-eval 'demo
       '(lambda (whs)
          (set-wormhole-data whs
                             (cons (cons name info)
                                   (wormhole-data whs))))
       (prog2$ info name))
    
    General Form:
    (wormhole-eval name lambda varterm)
    
    where name must be a quoted wormhole name and lambda must be a quoted lambda-expression. The lambda-expression must have at most one formal parameter but the body of the lambda-expression may contain other variables. Note that in the example form given above, the lambda has one formal, whs, and uses name and info freely. Note that the lambda is quoted. The third argument of wormhole-eval, varterm, is an arbitrary term that should mention all of the free variables in the lambda-expression. That term establishes your ``right'' to refer to those free variables in the environment in which the wormhole-eval expression occurs. The value of varterm is irrelevant and if you provide nil ACL2 will automatically provide a suitable term, namely a prog2$ form like the one shown in the example above.

    Aside: Exception for ACL2(p) (see parallelism) to the irrelevance of varterm. By default, calls of wormhole-eval employ a lock, *wormhole-lock*. To avoid such a lock, include the symbol :NO-WORMHOLE-LOCK in varterm; for example, you might replace a last argument of nil in wormhole-eval by :NO-WORMHOLE-LOCK. End of Aside.

    See wormhole for a full explanation of wormholes. Most relevant here is that every wormhole has a name and a status. The status is generally a cons pair whose car is the keyword :ENTER or the keyword :SKIP and whose cdr is an arbitrary object used to store information from one wormhole call to the next.

    Here is a succinct summary of wormhole-eval. If the lambda-expression has a local variable, wormhole-eval applies the lambda-expression to the wormhole status of the named wormhole and remembers the value as the new wormhole status. If the lambda has no formal parameter, the lambda is applied to no arguments and the value is the new status. Wormhole-eval returns nil. Thus, the formal parameter of the lambda-expression, if provided, denotes the wormhole's hidden status information; the value of the lambda is the new status and is hidden away.

    The guard of a wormhole-eval call is the guard of the body of the lambda-expression, with a fresh variable symbol used in place of the formal so that no assumptions are possible about the hidden wormhole status. If the guard of a wormhole-eval is verified, the call is macroexpanded inline to the evaluation of the body in a suitable environment. Thus, it can be a very fast way to access and change hidden state information, but the results must remain hidden. To do arbitrary computations on the hidden state (i.e., to access the ACL2 state or logical world or to interact with the user) see wormhole.

    Functions that are probably useful in the body of the lambda or the guard of a function using wormhole-eval include the following: wormhole-statusp, wormhole-entry-code, wormhole-data, set-wormhole-entry-code, set-wormhole-data, and make-wormhole-status.

    See wormhole for a series of example uses of wormhole-eval and wormhole.

    For a behind-the-scenes description of how wormholes work, See wormhole-implementation.




    acl2-sources/doc/HTML/WORMHOLE-IMPLEMENTATION.html0000664002132200015000000001223112222333522020324 0ustar kaufmannacl2 WORMHOLE-IMPLEMENTATION.html -- ACL2 Version 6.3

    WORMHOLE-IMPLEMENTATION

    notes on how wormholes are implemented
    Major Section:  MISCELLANEOUS
    

    What happens when you call wormhole? Recall that a typical call of the function looks like this:

    (wormhole 'name
              '(lambda (whs) ...)
              input
              form
              :ld-verbose ...
              ...)
    

    A brief recap of the advertised semantics for wormhole establishes our terminology: When the above wormhole is evaluated, the lambda-expression is applied to the wormhole's status and the result is stored as the new status. Then, if the entry-code of the new status is :ENTER, ld is invoked on a copy of the ``current state'' with the specified ld- ``special variables;'' output is directed to the comment window. In that copy of the state, the state-global variable wormhole-input is set to the value of input and the state-global variable wormhole-status is set to the (new) status computed by the lambda-expression. Thus, inside the wormhole, (@ wormhole-input) returns the list of inputs, (@ wormhole-status) returns the current status, and (assign wormhole-status ...) sets the wormhole's status. The first form executed by the ld is the value of form and unless that form returns (value :q), causing the ld to quit, the ld proceeds to take subsequent input from the comment window. Upon exiting from ld, the wormhole state ``evaporates.'' The wormhole's status upon exit is remembered and restored the next time the wormhole is entered.

    Here is what really happens.

    Each wormhole's status is recorded in an alist stored in a Common Lisp global variable named *wormhole-status-alist*. This variable is not part of the ACL2 state. If you exit the ACL2 loop with :q you can inspect the value of *wormhole-status-alist*. When the lambda-expression is evaluated it is applied to the value associated with name in the alist and the result is stored back into that alist. This step is performed by wormhole-eval. To make things more efficient, wormhole-eval is just a macro that expands into a let that binds the lambda formal to the current status and whose body is the lambda body. Guard clauses are generated from the body, with one exception: the lambda formal is replaced by a new variable so that no prior assumptions are available about the value of the the wormhole status.

    If the newly computed status has an entry code of :ENTER ld will be invoked. But we don't really copy state, of course. Instead we will invoke ld on the live state, which is always available in the von Neumann world in which ACL2 is implemented. To give the illusion of copying state, we will undo changes to the state upon exiting. To support this, we do two things just before invoking ld: we bind a Common Lisp special variable is to t to record that ACL2 is in a wormhole, and we initialize an accumulator that will be used to record state changes made while in the wormhole.

    Then ld is invoked, with first argument, standard-oi, being set to (cons form *standard-oi*). According to the standard semantics of ld, this reads and evaluates form and then the forms in the specified channel. The standard channels are directed to and from the terminal, which is the physical realization of the comment window.

    All state modifying functions of ACL2 are sensitive to the special variable that indicates that evaluation is in a wormhole. Some ACL2 state-modifying functions (e.g., those that modify the file system like write-byte$) are made to cause an error if invoked inside a wormhole on a file other than the terminal. Others, like f-put-global (the function behind such features as assign and maintenance of the ACL2 logical world by such events as defun and defthm) are made to record the old value of the state component being changed; these records are kept in the accumulator initialized above.

    Upon exit from ld for any reason, the final value of (@ wormhole-status) is stored in *wormhole-status-alist* and then the accumulator is used to ``undo'' all the state changes.

    Wormhole always returns nil.




    acl2-sources/doc/HTML/WORMHOLE-P.html0000664002132200015000000000124612222333522016402 0ustar kaufmannacl2 WORMHOLE-P.html -- ACL2 Version 6.3

    WORMHOLE-P

    predicate to determine if you are inside a wormhole
    Major Section:  MISCELLANEOUS
    

    See wormhole for a discussion of wormholes. (Wormhole-p state) returns (mv nil t state) when evaluated inside a wormhole, else (mv nil nil state).




    acl2-sources/doc/HTML/WORMHOLE-STATUSP.html0000664002132200015000000000132012222333522017337 0ustar kaufmannacl2 WORMHOLE-STATUSP.html -- ACL2 Version 6.3

    WORMHOLE-STATUSP

    predicate recognizing well-formed wormhole status object
    Major Section:  MISCELLANEOUS
    

    General Form:  (wormhole-statusp whs)
    

    See wormhole. This predicate is useful in guards for wormholes. It checks whether whs is either nil or a cons whose car is :ENTER or :SKIP.




    acl2-sources/doc/HTML/WORMHOLE.html0000664002132200015000000005723712222333522016220 0ustar kaufmannacl2 WORMHOLE.html -- ACL2 Version 6.3

    WORMHOLE

    ld without state -- a short-cut to a parallel universe
    Major Section:  MISCELLANEOUS
    

    Example Form:
    ; The following form enters a recursive read-eval-print loop on a
    ; copy of the current state, allowing you to interact with that loop.
    ; Note that the form does not mention the ACL2 state variable!
    ; Evaluate the form below. Inside the resulting loop, define some function,
    ; e.g., with (defun foo (x) x).  Then exit with :q and observe,
    ; e.g., with :pe foo, that the external state did not change.
    
    (wormhole 'foo
              '(lambda (whs) (set-wormhole-entry-code whs :ENTER))
              nil
              '(list 'hello 'there))
    

    General Form:
    (wormhole name entry-lambda input form
      :current-package    ...  ; known package name
      :ld-skip-proofsp    ...  ; nil, t or 'include-book
      :ld-redefinition-action  ; nil or '(:a . :b)
      :ld-prompt          ...  ; nil, t, or some prompt printer fn
      :ld-missing-input-ok ... ; nil, t, :warn, or warning message
      :ld-pre-eval-filter ...  ; :all, :query, or some new name
      :ld-pre-eval-print  ...  ; nil, t, or :never
      :ld-post-eval-print ...  ; nil, t, or :command-conventions
      :ld-evisc-tuple     ...  ; nil or '(alist level length hiding-cars)
      :ld-error-triples   ...  ; nil or t
      :ld-error-action    ...  ; :return!, :return, :continue, or :error
      :ld-query-control-alist  ; alist supplying default responses
      :ld-verbose         ...) ; nil or t
    
    The keyword arguments above are exactly those of ld (see ld) except that three of ld's keyword arguments are missing: the three that specify the channels standard-oi, standard-co and proofs-co, which default in wormhole to ACL2's comment window.

    There are two ways to create and enter a wormhole: wormhole as described here and the simpler wormhole-eval. We recommend you read this full account of wormholes before using wormhole-eval.

    Ignoring the use of entry-lambda, wormhole manufactures a named ``wormhole state'' and calls the general-purpose ACL2 read-eval-print loop ld on it. However, when ld exits, the wormhole evaporates and the function wormhole returns nil. The manufactured state is like the ``current'' ACL2 state except for two things. First, some information from the last wormhole state of this name is transferred into the new state; this allows a wormhole to maintain some state from one call to the next. Second, some information from the wormhole call itself is transferred into the new state; this allows the wormhole to be sensitive to context. These two changes to the current state are reflected in the settings (@ wormhole-status) and (@ wormhole-input) discussed in detail below.

    Note that wormhole may be called from environments in which state is not bound. It is still applicative because it always returns nil.

    There are some restrictions about what can be done inside a wormhole. As you may imagine, we really do not ``copy the current state'' but rather just keep track of how we modified it and undo those modifications upon exit. An error is signalled if you try to modify state in an unsupported way. For this same reason, wormholes do not allow updating of any user-defined single-threaded objects. See stobj.

    One example wormhole is the implementation of the ACL2 accumulated-persistence facility for tracking the frequency with which rules are tried. To implement this feature directly the theorem prover would have to take the tracking data as an argument and pass it around so that updates could be accumulated. This would greatly clutter the code. Instead, the tracking data is maintained in a wormhole. The theorem prover enters the wormhole to update the data as rules are tried. When you request a display of the data, show-accumulated-persistence enters the wormhole and prints the data. But the data is never available outside that wormhole. The ACL2 system uses a second wormhole to implement the brr facility, allowing the user to interact with the rewriter as rules are applied.

    We now specify the arguments and behavior of wormhole.

    The name argument must be a quoted constant and is typically a symbol. It will be the ``name'' of the wormhole. A wormhole of that name will be created the first time either wormhole or wormhole-eval is called.

    Every wormhole name has a ``status.'' The status of a wormhole is stored outside of ACL2; it is inaccessible to the ACL2 user except when in the named wormhole. But the status of a wormhole may be set by the user from within the wormhole.

    Upon the first call of wormhole or wormhole-eval on a name, the status of that name is nil. But in general you should arrange for the status to be a cons. The status is set by the quoted lambda every time wormhole is called; but it may also be set in the form argument (the first form evaluated in the interactive loop) by assigning to the state global variable wormhole-status, as with

    (assign wormhole-status ...)
    
    or even by the user interacting with the loop if you do not exit the loop with the first form. The car of the cons should be either :ENTER or :SKIP and is called the wormhole's ``entry code.'' The entry code of nil or an unexpectedly shaped status is :ENTER. The cdr of the cons is arbitrary data maintained by you.

    When wormhole is invoked, the status of the specified name is incorporated into the manufactured wormhole state. In particular, inside the wormhole, the status is the value of the state global variable wormhole-status. That is, inside the wormhole, the status may be accessed by (@ wormhole-status) and set by (assign wormhole-status ...), f-get-global and f-put-global. When ld exits -- typically because the form :q was read by ld -- the then-current value of wormhole-status is hidden away so that it can be restored when this wormhole is entered again. The rest of the wormhole state is lost.

    This allows a sequence of entries and exits to a wormhole to maintain some history in the status and this information can be manipulated by ACL2 functions executing inside the wormhole.

    The second argument to wormhole must be a quoted lambda expression. We explain it later.

    The third argument, input, may be any term. The value of the term is passed into the manufactured wormhole state, allowing you to pass in information about the calling context. Inside the wormhole, the input is available via (@ wormhole-input). It could be reassigned via (assign wormhole-input ...), but there is no reason to do that.

    The fourth argument, form, may be any term; when ld is called on the manufactured wormhole state, the first form evaluated by ld will be the value of form. Note that form will be translated by ld. Errors, including guard violations, in the translation or execution of that first form will leave you in the interactive loop of the wormhole state.

    When used properly, the first form allows you to greet your user before reading the first interactive command or simply to do whatever computation you want to do inside the wormhole and exit silently. We give examples below.

    Manufacturing a wormhole state is relatively expensive; in addition, the forms executed by ld must be read, translated, and interpreted as with any user type-in. The entry-lambda offers a way to avoid this or, at least, to decide whether to incur that expense.

    Before the wormhole state is manufactured and entered, the entry-lambda is applied to the current wormhole status with wormhole-eval. That lambda application must produce a new wormhole status, which is stored as the wormhole's status. The entry code for the new status determines whether wormhole actually manufactures a wormhole state and calls ld.

    If the entry code for that new status is :ENTER the wormhole state is manufactured and entered; otherwise, the new status is simply saved as the most recent status but the wormhole state is not manufactured or entered. Note therefore that the entry-lambda may be used to perform two functions: (a) to determine if it is really necessary to manufacture a state and (b) to update the data in the wormhole status as a function of the old status without invoking ld.

    The entry-lambda must be a quoted lambda expression of at most one argument. Thus, the argument must be either

    '(lambda (whs) <body>)
    
    or
    '(lambda () <body>)
    
    Note the quote. If a formal, e.g., whs, is provided, it must be used as a variable in the lambda body. The lambda-expression may contain free variables, that is, the body may mention variables other than the lambda formal. These free variables are understood in the caller's environment. These conventions allow us to compile the entry-lambda application very efficiently when the guard has been verified.

    The guard on a call of wormhole is the conjunction of the guards on the arguments conjoined with the guard on the body of the entry-lambda. See wormhole-eval for a discussion of the guard on the lambda-expression.

    The functions wormhole-statusp, wormhole-entry-code, wormhole-data, set-wormhole-entry-code, set-wormhole-data, and make-wormhole-status may be useful in manipulating entry codes and data in the entry-lambda.

    Note that you access and manipulate the wormhole's status in two different ways depending on whether you're ``outside'' of the wormhole applying the quoted lambda or ``inside'' the read-eval-print loop of the wormhole.

    OUTSIDE (wormhole-eval): access via the value of the lambda formal and set by returning the new status as the value of the lambda body.

    INSIDE (ld phase of wormhole): access via (@ wormhole-status), and set via (assign wormhole-status ...).

    Pragmatic Advice on Designing a Wormhole: Suppose you are using wormholes to implement some extra-logical utility. You must contemplate how you will use your wormhole's status to store hidden information. You might be tempted to exploit the entry code as part of the status. For example, you may think of :ENTER as indicating that your utility is ``turned on'' and :SKIP as indicating that your utility is ``turned off.'' We advise against such a design. We recommend you base your decisions on the wormhole data. We recommend that you set but not read the wormhole entry code to signal whether you wish to enter a full-fledged wormhole. To use the entry code as a flag overloads it and invites confusion when your facility is ``turned off'' but you have to enter the wormhole for some reason.

    For a behind-the-scenes description of how wormholes work, See wormhole-implementation.

    Here are some sample situations handled by wormhole-eval and wormhole. Let the wormhole in question be named DEMO. Initially its status is NIL. The functions below all maintain the convention that the status is either nil or of the form (:key . lst), where :key is either :SKIP or :ENTER and lst is a true-list of arbitrary objects. But since there is no way to prevent the user from entering the DEMO wormhole interactively and doing something to the status, this convention cannot be enforced. Thus, the functions below do what we say they do, e.g., remember all the values of x ever seen, only if they're the only functions messing with the DEMO status. On the other hand, the guards of all the functions below can be verified. We have explicitly declared that the guards on the functions below are to be verified, to confirm that they can be. Guard verification is optional but wormholes (and wormhole-eval in particular) are more efficient when guards have been verified. All of the functions defined below return nil.

    The examples below build on each other. If you really want to understand wormholes we recommend that you evaluate each of the forms below, in the order they are discussed.

    Q. How do I create a wormhole that prints its status to the comment window?

    (defun demo-status ()
      (declare (xargs :verify-guards t))
      (wormhole-eval 'demo
                     '(lambda (whs)
                        (prog2$ (cw "DEMO status:~%~x0~%" whs)
                                whs))
                     nil))
    

    Note above that after printing the status to the comment window we return the new (unchanged) status whs. Had we just written the call of cw, which returns nil, the function would print the status and then set it to nil!

    Q. How do I use a wormhole to collect every symbol, x, passed to the function?

    (defun demo-collect (x)
      (declare (xargs :verify-guards t))
      (wormhole-eval 'demo
                     '(lambda (whs)
                        (make-wormhole-status whs
                                              (wormhole-entry-code whs)
                                              (if (symbolp x)
                                                  (cons x (wormhole-data whs))
                                                  (wormhole-data whs))))
                     nil))
    
    We could have also defined this function this way:
    (defun demo-collect (x)
      (declare (xargs :verify-guards t))
      (if (symbolp x)
          (wormhole-eval 'demo
                         '(lambda (whs)
                            (set-wormhole-data whs
                                               (cons x (wormhole-data whs))))
                         nil)
          nil))
    
    Both versions always return nil and both versions collect into the wormhole data field just the symbols x upon which demo-collect is called.

    Q. How do I use demo-collect? Below is a function that maps over a list and computes its length. But it has been annotated with a call to demo-collect on every element.

     (defun my-len (lst)
       (if (endp lst)
           0
           (+ 1
              (prog2$ (demo-collect (car lst))
                      (my-len (cdr lst))))))
    
    Thus, for example:
    ACL2 !>(my-len '(4 temp car "Hi" rfix))
    5
    ACL2 !>(demo-status)
    DEMO status:
    (:ENTER RFIX CAR TEMP)
    NIL
    ACL2 !>
    

    Q. How do I set the entry code to :ENTER or :SKIP according to whether name is a member-equal of the list of things seen so far? Note that we cannot check this condition outside the wormhole, because it depends on the list of things collected so far. We make the decision inside the lambda-expression. Note that we explicitly check that the guard of member-equal is satisfied by the current wormhole status, since we cannot rely on the invariant that no other function interferes with the status of the DEMO wormhole. In the case that the status is ``unexpected'' we act like the status is nil and set it to (:SKIP . NIL).

    (defun demo-set-entry-code (name)
      (declare (xargs :verify-guards t))
      (wormhole-eval 'demo
                     '(lambda (whs)
                        (if (true-listp (wormhole-data whs))
                            (set-wormhole-entry-code
                             whs
                             (if (member-equal name (wormhole-data whs))
                                 :ENTER
                                 :SKIP))
                            '(:SKIP . NIL)))
                     nil))
    
    Thus
    ACL2 !>(demo-set-entry-code 'monday)
    NIL
    ACL2 !>(demo-status)
    DEMO status:
    (:SKIP RFIX CAR TEMP)
    NIL
    ACL2 !>(demo-set-entry-code 'rfix)
    NIL
    ACL2 !>(demo-status)
    DEMO status:
    (:ENTER RFIX CAR TEMP)
    NIL
    ACL2 !>
    

    Q. Suppose I want to collect every symbol and then, if the symbol has an ABSOLUTE-EVENT-NUMBER property in the ACL2 logical world, print the defining event with :pe and then enter an interactive loop; but if the symbol does not have an ABSOLUTE-EVENT-NUMBER, don't print anything and don't enter an interactive loop.

    Here it is not important to know what ABSOLUTE-EVENT-NUMBER is; this example just shows that we can use a wormhole to access the ACL2 logical world, even in a function that does not take the state as an argument.

    In the code below, we use wormhole instead of wormhole-eval, because we might have to access the logical world and enter an interactive loop. But for efficiency we do as much as we can inside the entry lambda, where we can check whether x is symbol and collect it into the data field of the wormhole status. Note that if we collect x, we also set the entry code to :ENTER. If we don't collect x, we set the entry code to :SKIP.

    (defun collect-symbols-and-print-events (x)
      (declare (xargs :guard t))
      (wormhole 'demo
                '(lambda (whs)
                   (if (symbolp x)
                       (make-wormhole-status whs
                                             :ENTER
                                             (cons x (wormhole-data whs)))
                       (set-wormhole-entry-code whs :SKIP)))
    
    ; The wormhole will not get past here is unless the entry code is
    ; :ENTER.  If we get past here, we manufacture a state, put
    ; x into (@ wormhole-input) and call ld in such a way that the
    ; first form executed is the quoted if-expression below.
    
                x
                '(if (getprop (@ wormhole-input)
                              'absolute-event-number
                              nil
                              'CURRENT-ACL2-WORLD (w state))
                     (er-progn
                      (mv-let (col state)
                              (fmt "~%Entering a wormhole on the event name ~x0~%"
                                   (list (cons #\0 (@ wormhole-input)))
                                   *standard-co* state nil)
                              (declare (ignore col))
                              (value nil))
                      (pe (@ wormhole-input))
                      (set-ld-prompt 'wormhole-prompt state)
                      (value :invisible))
                     (value :q))
                :ld-verbose nil
                :ld-prompt nil))
    

    The ``first form'' (the if) asks whether the wormhole-input (i.e., x) has an ABSOLUTE-EVENT-NUMBER property. If so, it enters an er-progn to perform a sequence of commands, each of which returns an ACL2 error triple (see programming-with-state). The first form uses fmt to print a greeting. Since fmt returns (mv col state) and we must return an error triple, we embed the fmt term in an (mv-let (col state) ... (value nil)). The macro value takes an object and returns a ``normal return'' error triple. The second form in the er-progn uses the ACL2 history macro pe (see pe) to print the defining event for a name. The third form sets the prompt of this read-eval-print loop to the standard function for printing the wormhole prompt. We silenced the printing of the prompt when we called ld, thanks to the :ld-prompt nil keyword option. More on this below. The fourth form returns the error triple value :invisible as the value of the first form. This prevents ld from printing the value of the first form. Since we have not exited ld, that function just continues by reading the next form from the comment window. The user perceives this as entering a read-eval-print loop. We continue in the loop until the user types :q.

    On the other branch of the if, if the symbol has no ABSOLUTE-EVENT-NUMBER property, we execute the form (value :q), which is the programming equivalent of typing :q. That causes the ld to exit.

    The ld special variables set in the call to wormhole and further manipulated inside the first form to ld may require explanation. By setting :ld-verbose to nil, we prevent ld from printing the familiar ACL2 banner when ld is called. If :ld-verbose nil is deleted, then you would see something like

    ACL2 Version  4.0.  Level 2.
    ...
    Type (good-bye) to quit completely out of ACL2.
    
    before the first form is read and evaluated.

    By setting :ld-prompt to nil we prevent ld from printing the prompt before reading and evaluating the first form.

    As this example shows, to use full-blown wormholes you must understand the protocol for using wormhole status to control whether a wormhole state is manufactured for ld and you must also understand programming with state and the effects of the various ld ``special variables.''

    From the discussion above we see that wormholes can be used to create formatted output without passing in the ACL2 state. For examples see cw, in particular the discussion at the end of that documentation topic.




    acl2-sources/doc/HTML/WRITE-BYTE$.html0000664002132200015000000000062312222333525016451 0ustar kaufmannacl2 WRITE-BYTE$.html -- ACL2 Version 6.3

    WRITE-BYTE$

    See io.
    Major Section:  ACL2-BUILT-INS
    




    acl2-sources/doc/HTML/What_Is_ACL2_lparen_Q_rparen_.html0000664002132200015000000000224612222333526022404 0ustar kaufmannacl2 What_Is_ACL2_lparen_Q_rparen_.html -- ACL2 Version 6.3

    What Is ACL2?

    ACL2 is a mathematical logic together with a mechanical theorem prover to help you reason in the logic.

    The logic is just a subset of applicative Common Lisp. (This link takes you off the main route of the tour. You'll see some Common Lisp on the tour, so visit this later!)

    The theorem prover is an ``industrial strength'' version of the Boyer-Moore theorem prover, Nqthm.

    Models of all kinds of computing systems can be built in ACL2, just as in Nqthm, even though the formal logic is Lisp.

    Once you've built an ACL2 model of a system, you can run it.

    You can also use ACL2 to prove theorems about the model.




    acl2-sources/doc/HTML/What_is_Required_of_the_User_lparen_Q_rparen_.html0000664002132200015000000000230112222333526026055 0ustar kaufmannacl2 What_is_Required_of_the_User_lparen_Q_rparen_.html -- ACL2 Version 6.3

    What is Required of the User?

    It is not easy to build ACL2 models of complex systems. To do so, the user must understand

      * the system being modeled, and
    
      * ACL2, at least as a programming language.
    

    It is not easy to get ACL2 to prove hard theorems. To do so, the user must understand

      * the model,
    
      * ACL2 as a mathematical logic, and
    
      * be able to construct a proof (in interaction with ACL2).
    
    ACL2 will help construct the proof but its primary role is to prevent logical mistakes. The creative burden -- the mathematical insight into why the model has the desired property -- is the user's responsibility.




    acl2-sources/doc/HTML/What_is_a_Mathematical_Logic_lparen_Q_rparen_.html0000664002132200015000000000216712222333526025773 0ustar kaufmannacl2 What_is_a_Mathematical_Logic_lparen_Q_rparen_.html -- ACL2 Version 6.3

    What is a Mathematical Logic?

    A mathematical logic is a formal system of formulas (axioms) and rules for deriving other formulas, called theorems.

    A proof is a derivation of a theorem. To see a concrete proof tree, click here.

    Why should you care? The neat thing about Theorems is that they are ``true.'' More precisely, if all the axioms are valid and the rules are validity preserving, then anything derived from the axioms via the rules is valid.

    So, if you want to determine if some formula is true, prove it.




    acl2-sources/doc/HTML/What_is_a_Mechanical_Theorem_Prover_lparen_Q_rparen_.html0000664002132200015000000000216412222333526027326 0ustar kaufmannacl2 What_is_a_Mechanical_Theorem_Prover_lparen_Q_rparen_.html -- ACL2 Version 6.3

    What is a Mechanical Theorem Prover?

    A mechanical theorem prover is a computer program that finds proofs of theorems.

    The ideal mechanical theorem prover is automatic: you give it a formula and it gives you a proof of that formula or tells you there is no proof.

    Unfortunately, automatic theorem provers can be built only for very simple logics (e.g., propositional calculus) and even then practical considerations (e.g., how many centuries you are willing to wait) limit the problems they can solve.




    acl2-sources/doc/HTML/What_is_a_Mechanical_Theorem_Prover_lparen_Q_rparen___lparen_cont_rparen_.html0000664002132200015000000000133312222333526033554 0ustar kaufmannacl2 What_is_a_Mechanical_Theorem_Prover_lparen_Q_rparen___lparen_cont_rparen_.html -- ACL2 Version 6.3

    What is a Mechanical Theorem Prover? (cont)

    To get around this, mechanical theorem provers often require help from the user.

    Click here to continue downward.




    acl2-sources/doc/HTML/XARGS.html0000664002132200015000000002317112222333522015636 0ustar kaufmannacl2 XARGS.html -- ACL2 Version 6.3

    XARGS

    extra arguments, for example to give hints to defun
    Major Section:  MISCELLANEOUS
    

    Common Lisp's defun function does not easily allow one to pass extra arguments such as ``hints''. ACL2 therefore supports a peculiar new declaration (see declare) designed explicitly for passing additional arguments to defun via a keyword-like syntax. This declaration can also be used with defmacro, but only for xargs keyword :GUARD; so we restrict attention below to use of xargs in defun events.

    The following declaration is nonsensical but does illustrate all of the xargs keywords for defun (which are the members of the list *xargs-keywords*).

    (declare (xargs :guard (symbolp x)
                    :guard-hints (("Goal" :in-theory (theory batch1)))
                    :guard-debug t
                    :hints (("Goal" :in-theory (theory batch1)))
                    :measure (- i j)
                    :ruler-extenders :basic
                    :mode :logic
                    :non-executable t
                    :normalize nil
                    :otf-flg t
                    :stobjs ($s)
                    :verify-guards t
                    :split-types t
                    :well-founded-relation my-wfr))
    
    General Form:
    (xargs :key1 val1 ... :keyn valn)
    
    where the keywords and their respective values are as shown below. Note that once ``inside'' the xargs form, the ``extra arguments'' to defun are passed exactly as though they were keyword arguments.

    :GUARD
    Value is a term involving only the formals of the function being defined. The actual guard used for the definition is the conjunction of all the guards and types (see declare) declared.

    :GUARD-HINTS
    Value: hints (see hints), to be used during the guard verification proofs as opposed to the termination proofs of the defun.

    :GUARD-DEBUG
    Value: nil by default, else directs ACL2 to decorate each guard proof obligation with hypotheses indicating its sources. See guard-debug.

    :HINTS
    Value: hints (see hints), to be used during the termination proofs as opposed to the guard verification proofs of the defun.

    :MEASURE
    Value is a term involving only the formals of the function being defined. This term is indicates what is getting smaller in the recursion. The well-founded relation with which successive measures are compared is o<. Also allowed is a special case, (:? v1 ... vk), where (v1 ... vk) enumerates a subset of the formal parameters such that some valid measure involves only those formal parameters. However, this special case is only allowed for definitions that are redundant (see redundant-events) or are executed when skipping proofs (see skip-proofs). Another special case is :MEASURE nil, which is treated the same as if :MEASURE is omitted.

    :RULER-EXTENDERS
    For recursive definitions (possibly mutually recursive), value controls termination analysis and the resulting stored induction scheme. See ruler-extenders for a discussion of legal values and their effects.

    :MODE
    Value is :program or :logic, indicating the defun mode of the function introduced. See defun-mode. If unspecified, the defun mode defaults to the default defun mode of the current world. To convert a function from :program mode to :logic mode, see verify-termination.

    :NON-EXECUTABLE
    Value is normally t or nil (the default). Rather than stating :non-executable t directly, which requires :logic mode and that the definitional body has a certain form, we suggest using the macro defun-nx or defund-nx; see defun-nx. A third value of :non-executable for advanced users is :program, which is generated by expansion of defproxy forms; see defproxy. For another way to deal with non-executability, see non-exec.

    :NORMALIZE
    Value is a flag telling defun whether to propagate if tests upward. Since the default is to do so, the value supplied is only of interest when it is nil. (See defun).

    :OTF-FLG
    Value is a flag indicating ``onward through the fog'' (see otf-flg).

    :STOBJS
    Value is either a single stobj name or a true list of stobj names (see stobj and see defstobj, and perhaps see defabsstobj). Every stobj name among the formals of the function must be listed, if the corresponding actual is to be treated as a stobj. That is, if a function uses a stobj name as a formal parameter but the name is not declared among the :stobjs then the corresponding argument is treated as ordinary. The only exception to this rule is state: whether you include it or not, state is always treated as a single-threaded object. This declaration has two effects. One is to enforce the syntactic restrictions on single-threaded objects. The other is to strengthen the guard of the function being defined so that it includes conjuncts specifying that each declared single-threaded object argument satisfies the recognizer for the corresponding single-threaded object.

    :VERIFY-GUARDS
    Value is t or nil, indicating whether or not guards are to be verified upon completion of the termination proof. This flag should only be t if the :mode is unspecified but the default defun mode is :logic, or else the :mode is :logic.

    :SPLIT-TYPES
    Value is t or nil, indicating whether or not types are to be proved from the guards. The default is nil, indicating that type declarations (see declare) contribute to the guards. If the value is t, then instead, the expressions corresponding to the type declarations (see type-spec) are conjoined into a ``split-type expression,'' and guard verification insists that this term is implied by the specified :guard. Suppose for example that a definition has the following declare form.

      (declare (xargs :guard (p x y) :split-types t)
               (type integer x)
               (type (satisfies good-bar-p) y))
    
    Then for guard verification, (p x y) is assumed, and in addition to the usual proof obligations derived from the body of the definition, guard verification requires a proof that (p x y) implies both (integerp x) and (good-bar-p y). See community book demos/split-types-examples.lisp for small examples.

    :WELL-FOUNDED-RELATION
    Value is a function symbol that is known to be a well-founded relation in the sense that a rule of class :well-founded-relation has been proved about it. See well-founded-relation.




    acl2-sources/doc/HTML/XOR.html0000664002132200015000000000120112222333525015413 0ustar kaufmannacl2 XOR.html -- ACL2 Version 6.3

    XOR

    logical ``exclusive or''
    Major Section:  ACL2-BUILT-INS
    

    Xor is the ACL2 exclusive-or function. (xor P Q) means that either P or Q, but not both, is false (i.e., nil).

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/You_Must_Think_about_the_Use_of_a_Formula_as_a_Rule.html0000664002132200015000000000237412222333526027172 0ustar kaufmannacl2 You_Must_Think_about_the_Use_of_a_Formula_as_a_Rule.html -- ACL2 Version 6.3

    You Must Think about the Use of a Formula as a Rule

    This is good and bad.

    The good news is that you can program ACL2's simplifier.

    The bad news is that when you command ACL2 to prove a theorem you must give some thought to how that theorem is to be used as a rule!

    For example, if after proving associativity-of-app as previously shown, you engaged in the mathematically trivial act of proving it again but with the equality reversed, you would have programmed ACL2's rewriter to loop forever.

    You can avoid adding any rule by using the command:

    (defthm associativity-of-app
      (equal (app (app a b) c)
             (app a (app b c)))
      :rule-classes nil)
    




    acl2-sources/doc/HTML/ZERO-TEST-IDIOMS.html0000664002132200015000000002424212222333525017273 0ustar kaufmannacl2 ZERO-TEST-IDIOMS.html -- ACL2 Version 6.3

    ZERO-TEST-IDIOMS

    how to test for 0
    Major Section:  PROGRAMMING
    

    Below are six commonly used idioms for testing whether x is 0. Zip and zp are the preferred termination tests for recursions down the integers and naturals, respectively.

    idiom       logical              guard                 primary
                meaning                                 compiled code*
    
    (equal x 0) (equal x 0)          t                   (equal x 0)
    
    (eql x 0)   (equal x 0)          t                   (eql x 0)
    
    (zerop x)   (equal x 0)          x is a number       (= x 0)
    
    (= x 0)     (equal x 0)          x is a number       (= x 0)
    
    (zip x)     (equal (ifix x) 0)   x is an integer     (= x 0)
    
    (zp x)      (equal (nfix x) 0)   x is a natural      (int= x 0)
    
    (zpf x)     (equal (nfix x) 0)   x is a fixnum >= 0  (eql (the-fixnum x) 0)
    
    *See guards-and-evaluation, especially the subsection titled ``Guards and evaluation V: efficiency issues''. Primary code is relevant only if guards are verified. The ``compiled code'' shown is only suggestive.

    The first four idioms all have the same logical meaning and differ only with respect to their executability and efficiency. In the absence of compiler optimizing, (= x 0) is probably the most efficient, (equal x 0) is probably the least efficient, and (eql x 0) is in between. However, an optimizing compiler could always choose to compile (equal x 0) as (eql x 0) and, in situations where x is known at compile-time to be numeric, (eql x 0) as (= x 0). So efficiency considerations must, of course, be made in the context of the host compiler.

    Note also that (zerop x) and (= x 0) are indistinguishable. They have the same meaning and the same guard, and can reasonably be expected to generate equally efficient code.

    Note that (zip x) and (zp x) do not have the same logical meanings as the others or each other. They are not simple tests for equality to 0. They each coerce x into a restricted domain, zip to the integers and zp to the natural numbers, choosing 0 for x when x is outside the domain. Thus, 1/2, #c(1 3), and 'abc, for example, are all ``recognized'' as zero by both zip and zp. But zip reports that -1 is different from 0 while zp reports that -1 ``is'' 0. More precisely, (zip -1) is nil while (zp -1) is t.

    Note that the last five idioms all have guards that restrict their Common Lisp executability. If these last five are used in situations in which guards are to be verified, then proof obligations are incurred as the price of using them. If guard verification is not involved in your project, then the first five can be thought of as synonymous.

    Zip and zp are not provided by Common Lisp but are ACL2-specific functions. Why does ACL2 provide these functions? The answer has to do with the admission of recursively defined functions and efficiency. Zp is provided as the zero-test in situations where the controlling formal parameter is understood to be a natural number. Zip is analogously provided for the integer case. We illustrate below.

    Here is an admissible definition of factorial

    (defun fact (n) (if (zp n) 1 (* n (fact (1- n)))))
    
    Observe the classic recursion scheme: a test against 0 and recursion by 1-. Note however that the test against 0 is expressed with the zp idiom. Note also the absence of a guard making explicit our intention that n is a natural number.

    This definition of factorial is readily admitted because when (zp n)

    is false (i.e., nil) then n is a natural number other than 0 and so (1- n) is less than n. The base case, where (zp n) is true, handles all the ``unexpected'' inputs, such as arise with (fact -1) and (fact 'abc). When calls of fact are evaluated, (zp n) checks (integerp n) and (> n 0). Guard verification is unsuccessful for this definition of fact because zp requires its argument to be a natural number and there is no guard on fact, above. Thus the primary raw lisp for fact is inaccessible and only the :logic definition (which does runtime ``type'' checking) is used in computation. In summary, this definition of factorial is easily admitted and easily manipulated by the prover but is not executed as efficiently as it could be.

    Runtime efficiency can be improved by adding a guard to the definition.

    (defun fact (n)
      (declare (xargs :guard (and (integerp n) (>= n 0))))
      (if (zp n) 1 (* n (fact (1- n)))))
    
    This guarded definition has the same termination conditions as before -- termination is not sensitive to the guard. But the guards can be verified. This makes the primary raw lisp definition accessible during execution. In that definition, the (zp n) above is compiled as (= n 0), because n will always be a natural number when the primary code is executed. Thus, by adding a guard and verifying it, the elegant and easily used definition of factorial is also efficiently executed on natural numbers.

    Now let us consider an alternative definition of factorial in which (= n 0) is used in place of (zp n).

    (defun fact (n) (if (= n 0) 1 (* n (fact (1- n)))))
    
    This definition does not terminate. For example (fact -1) gives rise to a call of (fact -2), etc. Hence, this alternative is inadmissible. A plausible response is the addition of a guard restricting n to the naturals:
    (defun fact (n)
     (declare (xargs :guard (and (integerp n) (>= n 0))))
     (if (= n 0) 1 (* n (fact (1- n)))))
    
    But because the termination argument is not sensitive to the guard, it is still impossible to admit this definition. To influence the termination argument one must change the conditions tested. Adding a runtime test that n is a natural number would suffice and allow both admission and guard verification. But such a test would slow down the execution of the compiled function.

    The use of (zp n) as the test avoids this dilemma. Zp provides the logical equivalent of a runtime test that n is a natural number but the execution efficiency of a direct = comparison with 0, at the expense of a guard conjecture to prove. In addition, if guard verification and most-efficient execution are not needed, then the use of (zp n) allows the admission of the function without a guard or other extraneous verbiage.

    While general rules are made to be broken, it is probably a good idea to get into the habit of using (zp n) as your terminating ``0 test'' idiom when recursing down the natural numbers. It provides the logical power of testing that n is a non-0 natural number and allows efficient execution.

    We now turn to the analogous function, zip. Zip is the preferred 0-test idiom when recursing through the integers toward 0. Zip considers any non-integer to be 0 and otherwise just recognizes 0. A typical use of zip is in the definition of integer-length, shown below. (ACL2 can actually accept this definition, but only after appropriate lemmas have been proved.)

    (defun integer-length (i)
      (declare (xargs :guard (integerp i)))
      (if (zip i)
          0
        (if (= i -1)
          0
          (+ 1 (integer-length (floor i 2))))))
    
    Observe that the function recurses by (floor i 2). Hence, calling the function on 25 causes calls on 12, 6, 3, 1, and 0, while calling it on -25 generates calls on -13, -7, -4, -2, and -1. By making (zip i) the first test, we terminate the recursion immediately on non-integers. The guard, if present, can be verified and allows the primary raw lisp definition to check (= i 0) as the first terminating condition (because the primary code is executed only on integers).




    acl2-sources/doc/HTML/ZEROP.html0000664002132200015000000000245012222333525015651 0ustar kaufmannacl2 ZEROP.html -- ACL2 Version 6.3

    ZEROP

    test an acl2-number against 0
    Major Section:  ACL2-BUILT-INS
    

    (zerop x) is t if x is 0 and is nil otherwise. Thus, it is logically equivalent to (equal x 0).

    (Zerop x) has a guard requiring x to be numeric and can be expected to execute more efficiently than (equal x 0) in properly guarded compiled code.

    In recursions down the natural numbers, (zp x) is preferred over (zerop x) because the former coerces x to a natural and allows the termination proof. In recursions through the integers, (zip x) is preferred. See zero-test-idioms.

    Zerop is a Common Lisp function. See any Common Lisp documentation for more information.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/ZIP.html0000664002132200015000000000271112222333525015414 0ustar kaufmannacl2 ZIP.html -- ACL2 Version 6.3

    ZIP

    testing an ``integer'' against 0
    Major Section:  ACL2-BUILT-INS
    

    (Zip i) is logically equivalent to (equal (ifix i) 0) and is the preferred termination test for recursion through the integers. (Zip i) returns t if i is 0 or not an integer; it returns nil otherwise. Thus,

       i         (zip i)
       3         nil
       0         t
       -2        nil
       5/2       t
       #c(1 3)   t
       'abc      t
    

    (Zip i) has a guard requiring i to be an integer.

    For a discussion of the various idioms for testing against 0, see zero-test-idioms.

    Zip is typically used as the termination test in recursions through the integers. It has the advantage of ``coercing'' its argument to an integer and hence allows the definition to be admitted without an explicit type check in the body. Guard verification allows zip to be compiled as a direct =-comparision with 0.




    acl2-sources/doc/HTML/ZP.html0000664002132200015000000000303312222333525015301 0ustar kaufmannacl2 ZP.html -- ACL2 Version 6.3

    ZP

    testing a ``natural'' against 0
    Major Section:  ACL2-BUILT-INS
    

    (Zp n) is logically equivalent to (equal (nfix n) 0) and is the preferred termination test for recursion down the natural numbers. (Zp n) returns t if n is 0 or not a natural number; it returns nil otherwise. Thus, in the ACL2 logic (ignoring the issue of guards):

        n       (zp n)
       3         nil
       0         t
       -1        t
       5/2       t
       #c(1 3)   t
       'abc      t
    

    (Zp n) has a guard requiring n to be a natural number.

    For a discussion of the various idioms for testing against 0, see zero-test-idioms.

    Zp is typically used as the termination test in recursions down the natural numbers. It has the advantage of ``coercing'' its argument to a natural and hence allows the definition to be admitted without an explicit type check in the body. Guard verification allows zp to be compiled as a direct =-comparision with 0.




    acl2-sources/doc/HTML/ZPF.html0000664002132200015000000000152612222333525015414 0ustar kaufmannacl2 ZPF.html -- ACL2 Version 6.3

    ZPF

    testing a nonnegative fixnum against 0
    Major Section:  ACL2-BUILT-INS
    

    Zpf is exactly the same as zp, except that zpf is intended for, and faster for, fixnum arguments. Its guard is specified with a type declaration, (type (unsigned-byte 29) x). (See declare and see type-spec.) Also see zp.

    To see the ACL2 definition of this function, see pf.




    acl2-sources/doc/HTML/_ampersand_ALLOW-OTHER-KEYS.html0000664002132200015000000000067312222333520021451 0ustar kaufmannacl2 _ampersand_ALLOW-OTHER-KEYS.html -- ACL2 Version 6.3

    &ALLOW-OTHER-KEYS

    See macro-args.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/_ampersand_BODY.html0000664002132200015000000000064312222333520017675 0ustar kaufmannacl2 _ampersand_BODY.html -- ACL2 Version 6.3

    &BODY

    See macro-args.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/_ampersand_KEY.html0000664002132200015000000000064112222333520017566 0ustar kaufmannacl2 _ampersand_KEY.html -- ACL2 Version 6.3

    &KEY

    See macro-args.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/_ampersand_OPTIONAL.html0000664002132200015000000000065312222333520020366 0ustar kaufmannacl2 _ampersand_OPTIONAL.html -- ACL2 Version 6.3

    &OPTIONAL

    See macro-args.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/_ampersand_REST.html0000664002132200015000000000064312222333520017715 0ustar kaufmannacl2 _ampersand_REST.html -- ACL2 Version 6.3

    &REST

    See macro-args.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/_ampersand_WHOLE.html0000664002132200015000000000064512222333520020020 0ustar kaufmannacl2 _ampersand_WHOLE.html -- ACL2 Version 6.3

    &WHOLE

    See macro-args.
    Major Section:  MISCELLANEOUS
    




    acl2-sources/doc/HTML/_at_.html0000664002132200015000000000255012222333523015653 0ustar kaufmannacl2 _at_.html -- ACL2 Version 6.3

    @

    get the value of a global variable in state
    Major Section:  ACL2-BUILT-INS
    

    Examples:
    (+ (@ y) 1)
    (assign a (aset1 'ascii-map-array (@ a) 66 'Upper-case-B))
    
    General Form:
    (@ symbol)
    
    where symbol is any symbol to which you have assigned a global value. This macro expands into (f-get-global 'symbol state), which retrieves the stored value of the symbol.

    The macro f-get-global is closely related to @: (@ var) macroexpands to (f-get-global 'var state).

    The macro assign makes it convenient to set the value of a symbol. The :ubt operation has no effect on the global-table of state. Thus, you may use these globals to hang onto useful data structures even though you may undo back past where you computed and saved them.




    acl2-sources/doc/HTML/_gt_.html0000664002132200015000000000116212222333523015657 0ustar kaufmannacl2 _gt_.html -- ACL2 Version 6.3

    >

    greater-than test
    Major Section:  ACL2-BUILT-INS
    

    > is a macro, and (> x y) expands to the same thing as (< y x). See <.

    > is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/_gt_=.html0000664002132200015000000000120612222333523015753 0ustar kaufmannacl2 _gt_=.html -- ACL2 Version 6.3

    >=

    greater-than-or-equal test
    Major Section:  ACL2-BUILT-INS
    

    >= is a macro, and (>= x y) expands to the same thing as (not (< x y)). See <.

    >= is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/_hyphen_.html0000664002132200015000000000150312222333523016537 0ustar kaufmannacl2 _hyphen_.html -- ACL2 Version 6.3

    -

    macro for subtraction and negation
    Major Section:  ACL2-BUILT-INS
    

    See binary-+ for addition and see unary-- for negation.

    Note that - represents subtraction as follows:

    (- x y)
    
    represents the same term as
    (+ x (- y))
    
    which is really
    (binary-+ x (unary-- y)).
    
    Also note that - represents arithmetic negation as follows:
    (- x)
    
    expands to
    (unary-- x).
    




    acl2-sources/doc/HTML/_lt_.html0000664002132200015000000000234712222333523015672 0ustar kaufmannacl2 _lt_.html -- ACL2 Version 6.3

    <

    less-than
    Major Section:  ACL2-BUILT-INS
    

    Completion Axiom (completion-of-<):

    (equal (< x y)
           (if (and (rationalp x)
                    (rationalp y))
               (< x y)
             (let ((x1 (if (acl2-numberp x) x 0))
                   (y1 (if (acl2-numberp y) y 0)))
               (or (< (realpart x1) (realpart y1))
                   (and (equal (realpart x1) (realpart y1))
                        (< (imagpart x1) (imagpart y1)))))))
    

    Guard for (< x y):

    (and (rationalp x) (rationalp y))
    
    Notice that like all arithmetic functions, < treats non-numeric inputs as 0.

    This function has the usual meaning on the rational numbers, but is extended to the complex rational numbers using the lexicographic order: first the real parts are compared, and if they are equal, then the imaginary parts are compared.




    acl2-sources/doc/HTML/_lt_=.html0000664002132200015000000000120312222333523015755 0ustar kaufmannacl2 _lt_=.html -- ACL2 Version 6.3

    <=

    less-than-or-equal test
    Major Section:  ACL2-BUILT-INS
    

    <= is a macro, and (<= x y) expands to the same thing as (not (< y x)). See <.

    <= is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/_slash_.html0000664002132200015000000000164712222333523016367 0ustar kaufmannacl2 _slash_.html -- ACL2 Version 6.3

    /

    macro for division and reciprocal
    Major Section:  ACL2-BUILT-INS
    

    See binary-* for multiplication and see unary-/ for reciprocal.

    Note that / represents division as follows:

    (/ x y)
    
    represents the same term as
    (* x (/ y))
    
    which is really
    (binary-* x (unary-/ y)).
    
    Also note that / represents reciprocal as follows:
    (/ x)
    
    expands to
    (unary-/ x).
    
    / is a Common Lisp macro. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/_slash_=.html0000664002132200015000000000177312222333523016464 0ustar kaufmannacl2 _slash_=.html -- ACL2 Version 6.3

    /=

    test inequality of two numbers
    Major Section:  ACL2-BUILT-INS
    

    (/= x y) is logically equivalent to (not (equal x y)).

    Unlike equal, /= has a guard requiring both of its arguments to be numbers. Generally, /= is executed more efficiently than a combination of not and equal.

    For a discussion of the various ways to test against 0, See zero-test-idioms.

    /= is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/_star_.html0000664002132200015000000000142212222333523016215 0ustar kaufmannacl2 _star_.html -- ACL2 Version 6.3

    *

    multiplication macro
    Major Section:  ACL2-BUILT-INS
    

    * is really a macro that expands to calls of the function binary-*. So for example

    (* x y 4 z)
    
    represents the same term as
    (binary-* x (binary-* y (binary-* 4 z))).
    

    See binary-*.

    * is a Common Lisp function. See any Common Lisp documentation for more information.




    acl2-sources/doc/HTML/_star_STANDARD-CI_star_.html0000664002132200015000000000200712222333520021014 0ustar kaufmannacl2 _star_STANDARD-CI_star_.html -- ACL2 Version 6.3

    *STANDARD-CI*

    an ACL2 character-based analogue of CLTL's *standard-input*
    Major Section:  IO
    

    The value of the ACL2 constant *standard-ci* is an open character input channel that is synonymous to Common Lisp's *standard-input*.

    ACL2 character input from *standard-ci* is actually obtained by reading characters from the stream named by Common Lisp's *standard-input*. That is, by changing the setting of *standard-input* in raw Common Lisp you can change the source from which ACL2 reads on the channel *standard-ci*. See *standard-co*.




    acl2-sources/doc/HTML/_star_STANDARD-CO_star_.html0000664002132200015000000000550312222333520021026 0ustar kaufmannacl2 _star_STANDARD-CO_star_.html -- ACL2 Version 6.3

    *STANDARD-CO*

    the ACL2 analogue of CLTL's *standard-output*
    Major Section:  IO
    

    The value of the ACL2 constant *standard-co* is an open character output channel that is synonymous to Common Lisp's *standard-output*.

    ACL2 character output to *standard-co* will go to the stream named by Common Lisp's *standard-output*. That is, by changing the setting of *standard-output* in raw Common Lisp you can change the actual destination of ACL2 output on the channel named by *standard-co*. Observe that this happens without changing the logical value of *standard-co* (which is some channel symbol). Changing the setting of *standard-output* in raw Common Lisp essentially just changes the map that relates ACL2 to the physical world of terminals, files, etc.

    To see the value of this observation, consider the following. Suppose you write an ACL2 function which does character output to the constant channel *standard-co*. During testing you see that the output actually goes to your terminal. Can you use the function to output to a file? Yes, if you are willing to do a little work in raw Common Lisp: open a stream to the file in question, set *standard-output* to that stream, call your ACL2 function, and then close the stream and restore *standard-output* to its nominal value. Similar observations can be made about the two ACL2 input channels, *standard-oi* and *standard-ci*, which are analogues of *standard-input*.

    Another reason you might have for wanting to change the actual streams associated with *standard-oi* and *standard-co* is to drive the ACL2 top-level loop, ld, on alternative input and output streams. This end can be accomplished easily within ACL2 by either calling ld on the desired channels or file names or by resetting the ACL2 state global variables 'standard-oi and 'standard-co which are used by ld. See standard-oi and see standard-co.




    acl2-sources/doc/HTML/_star_STANDARD-OI_star_.html0000664002132200015000000000172612222333520021037 0ustar kaufmannacl2 _star_STANDARD-OI_star_.html -- ACL2 Version 6.3

    *STANDARD-OI*

    an ACL2 object-based analogue of CLTL's *standard-input*
    Major Section:  IO
    

    The value of the ACL2 constant *standard-oi* is an open object input channel that is synonymous to Common Lisp's *standard-input*.

    ACL2 object input from *standard-oi* is actually obtained by reading from the stream named by Common Lisp's *standard-input*. That is, by changing the setting of *standard-input* in raw Common Lisp you can change the source from which ACL2 reads on the channel *standard-oi*. See *standard-co*.




    acl2-sources/doc/HTML/_star_TERMINAL-MARKUP-TABLE_star_.html0000664002132200015000000000457112222333516022375 0ustar kaufmannacl2 _star_TERMINAL-MARKUP-TABLE_star_.html -- ACL2 Version 6.3

    *TERMINAL-MARKUP-TABLE*

    a markup table used for printing to the terminal
    Major Section:  DOCUMENTATION
    

    The value of the ACL2 constant *terminal-markup-table* is an association list pairing markup keys with strings, to be used for printing to the terminal. See markup for a description of the ACL2 markup language.

    The entries in *terminal-markup-table* are of the form

    (key flag . fmt-string)
    
    where key is one of the doc-string tilde directives (see markup), flag is a Boolean as described below, and fmt-string is a string as expected by the ACL2 printing function fmt. The system arranges that for any arg, when an expression ~key[arg] is encountered by the documentation printer, fmt will print fmt-string in an association list, binding keys based on arg as follows.
    #\p -- the `pointer'     ; only used if flag is t
    #\s -- the print name version of the pointer, e.g., |abc| or ABC
    #\c -- the parent file   ; only used if flag is t
    #\t -- the displayed text
    #\T -- uppercased displayed text
    
    The first three entries are used only when the flag associated with key is t, indicating that the argument arg of ~key is to be parsed as starting with a symbol; for example, ~key[foo bar] will bind #\p to the symbol FOO.

    The discussion of the above association list for printing fmt-string applies when printing documentation to other than the terminal as well. Such tools exist for Texinfo and for HTML; see files doc/write-acl2-html.lisp and doc/write-acl2-texinfo.lisp distributed with ACL2.




    acl2-sources/doc/HTML/acl2-doc-index.html0000664002132200015000000060665512222333532017462 0ustar kaufmannacl2 ACL2 Version 6.3

    Index

    This index lists all documented topics in ACL2, arranged into sections. The first section is devoted to those whose names begin with signs (and digits), such as *STANDARD-CI* and 1+. Thereafter we have one section for each of the 26 letters of the alphabet. The last section is devoted to those topics in the ACL2-PC package. By clicking on the appropriate entry of the line below you can go to the corresponding section of the index. You may use Find to search the Index. We also provide an index based on Major Topics.
    Signs A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ACL2-PC:: 
    
    


    Signs

    &ALLOW-OTHER-KEYS -- See macro-args.

    &BODY -- See macro-args.

    &KEY -- See macro-args.

    &OPTIONAL -- See macro-args.

    &REST -- See macro-args.

    &WHOLE -- See macro-args.

    * -- multiplication macro

    *STANDARD-CI* -- an ACL2 character-based analogue of CLTL's *standard-input*

    *STANDARD-CO* -- the ACL2 analogue of CLTL's *standard-output*

    *STANDARD-OI* -- an ACL2 object-based analogue of CLTL's *standard-input*

    *TERMINAL-MARKUP-TABLE* -- a markup table used for printing to the terminal

    + -- addition macro

    - -- macro for subtraction and negation

    / -- macro for division and reciprocal

    /= -- test inequality of two numbers

    1+ -- increment by 1

    1- -- decrement by 1

    < -- less-than

    <= -- less-than-or-equal test

    = -- test equality of two numbers

    > -- greater-than test

    >= -- greater-than-or-equal test

    @ -- get the value of a global variable in state



    A

    A Flying Tour of ACL2 -- A Flying Tour of ACL2

    A Sketch of How the Rewriter Works -- A Sketch of How the Rewriter Works

    A Tiny Warning Sign -- A Tiny Warning Sign

    A Trivial Proof -- A Trivial Proof

    A Typical State -- A Typical State

    A Walking Tour of ACL2 -- A Walking Tour of ACL2

    A! -- to return to the top-level of ACL2's command loop

    ABORT! -- to return to the top-level of ACL2's command loop

    ABOUT-ACL2 -- about ACL2

    ABS -- the absolute value of a real number

    ACCUMULATED-PERSISTENCE -- to get statistics on which runes are being tried

    ACCUMULATED-PERSISTENCE-SUBTLETIES -- some subtle aspects of the counting done by accumulated-persistence

    ACKNOWLEDGMENTS -- some contributors to the well-being of ACL2

    ACL2 Characters -- ACL2 Characters

    ACL2 Conses or Ordered Pairs -- ACL2 Conses or Ordered Pairs

    ACL2 Strings -- ACL2 Strings

    ACL2 Symbols -- ACL2 Symbols

    ACL2 System Architecture -- ACL2 System Architecture

    ACL2 as an Interactive Theorem Prover -- ACL2 as an Interactive Theorem Prover

    ACL2 as an Interactive Theorem Prover (cont) -- ACL2 as an Interactive Theorem Prover (cont)

    ACL2 is an Untyped Language -- ACL2 is an Untyped Language

    ACL2-AS-STANDALONE-PROGRAM -- Calling ACL2 from another program

    ACL2-BUILT-INS -- built-in ACL2 functions

    ACL2-COUNT -- a commonly used measure for justifying recursion

    ACL2-CUSTOMIZATION -- file of initial commands for ACL2 to run at startup

    ACL2-DEFAULTS-TABLE -- a table specifying certain defaults, e.g., the default defun-mode

    ACL2-HELP -- the acl2-help mailing list

    ACL2-NUMBER-LISTP -- recognizer for a true list of numbers

    ACL2-NUMBERP -- recognizer for numbers

    ACL2-SEDAN -- ACL2 Sedan interface

    ACL2-TUTORIAL -- tutorial introduction to ACL2

    ACL2-USER -- a package the ACL2 user may prefer

    ACL2P-KEY-CHECKPOINTS -- key checkpoints in ACL2(p)

    ACL2S -- See acl2-sedan.

    ACONS -- constructor for association lists

    ACTIVE-RUNEP -- check that a rune exists and is enabled

    ADD-BINOP -- associate a function name with a macro name

    ADD-CUSTOM-KEYWORD-HINT -- add a new custom keyword hint

    ADD-DEFAULT-HINTS -- add to the default hints

    ADD-DEFAULT-HINTS! -- add to the default hints non-locally

    ADD-DIVE-INTO-MACRO -- associate proof-checker diving function with macro name

    ADD-INCLUDE-BOOK-DIR -- link keyword for :dir argument of ld and include-book

    ADD-INVISIBLE-FNS -- make some unary functions invisible to the loop-stopper algorithm

    ADD-LD-KEYWORD-ALIAS -- See ld-keyword-aliases.

    ADD-LD-KEYWORD-ALIAS! -- See ld-keyword-aliases.

    ADD-MACRO-ALIAS -- associate a function name with a macro name

    ADD-MACRO-FN -- associate a function name with a macro name

    ADD-MATCH-FREE-OVERRIDE -- set :match-free value to :once or :all in existing rules

    ADD-NTH-ALIAS -- associate one symbol with another for printing of nth/update-nth terms

    ADD-OVERRIDE-HINTS -- add to the override-hints

    ADD-OVERRIDE-HINTS! -- add non-locally to the override-hints

    ADD-RAW-ARITY -- add arity information for raw mode

    ADD-TO-SET -- add a symbol to a list

    ADD-TO-SET-EQ -- See add-to-set.

    ADD-TO-SET-EQL -- See add-to-set.

    ADD-TO-SET-EQUAL -- See add-to-set.

    ADVANCED-FEATURES -- some advanced features of ACL2

    ALISTP -- recognizer for association lists

    ALLOCATE-FIXNUM-RANGE -- set aside fixnums in GCL

    ALPHA-CHAR-P -- recognizer for alphabetic characters

    ALPHORDER -- total order on atoms

    ALTERNATIVE-INTRODUCTION -- introduction to ACL2

    AND -- conjunction

    ANNOTATED-ACL2-SCRIPTS -- examples of ACL2 scripts

    APPEND -- concatenate zero or more lists

    APROPOS -- See finding-documentation.

    ARCHITECTURE-OF-THE-PROVER -- a simple overview of how the prover works

    AREF1 -- access the elements of a 1-dimensional array

    AREF2 -- access the elements of a 2-dimensional array

    ARGS -- args, guard, type, constraint, etc., of a function symbol

    ARRAY1P -- recognize a 1-dimensional array

    ARRAY2P -- recognize a 2-dimensional array

    ARRAYS -- an introduction to ACL2 arrays

    ARRAYS-EXAMPLE -- an example illustrating ACL2 arrays

    ASET1 -- set the elements of a 1-dimensional array

    ASET2 -- set the elements of a 2-dimensional array

    ASH -- arithmetic shift operation

    ASSERT$ -- cause a hard error if the given test is false

    ASSERT-EVENT -- assert that a given form returns a non-nil value

    ASSIGN -- assign to a global variable in state

    ASSOC -- look up key in association list

    ASSOC-EQ -- See assoc.

    ASSOC-EQUAL -- See assoc.

    ASSOC-KEYWORD -- look up key in a keyword-value-listp

    ASSOC-STRING-EQUAL -- look up key, a string, in association list

    ATOM -- recognizer for atoms

    ATOM-LISTP -- recognizer for a true list of atoms

    About Models -- About Models

    About Types -- About Types

    About the ACL2 Home Page -- About the ACL2 Home Page

    About the Admission of Recursive Definitions -- About the Admission of Recursive Definitions

    About the Prompt -- About the Prompt

    An Example Common Lisp Function Definition -- An Example Common Lisp Function Definition

    An Example of ACL2 in Use -- An Example of ACL2 in Use

    Analyzing Common Lisp Models -- Analyzing Common Lisp Models



    B

    BACKCHAIN-LIMIT -- limiting the effort expended on relieving hypotheses

    BACKCHAIN-LIMIT-RW -- hints keyword :BACKCHAIN-LIMIT-RW

    BACKTRACK -- hints keyword :BACKTRACK

    BDD -- ordered binary decision diagrams with rewriting

    BDD-ALGORITHM -- summary of the BDD algorithm in ACL2

    BDD-INTRODUCTION -- examples illustrating the use of BDDs in ACL2

    BIBLIOGRAPHY -- reports about ACL2

    BINARY-* -- multiplication function

    BINARY-+ -- addition function

    BINARY-APPEND -- concatenate two lists

    BIND-FREE -- to bind free variables of a rewrite, definition, or linear rule

    BIND-FREE-EXAMPLES -- examples pertaining to bind-free hypotheses

    BOOK-COMPILED-FILE -- creating and loading of compiled and expansion files for books

    BOOK-CONTENTS -- restrictions on the forms inside books

    BOOK-EXAMPLE -- how to create, certify, and use a simple book

    BOOK-MAKEFILES -- See books-certification.

    BOOK-NAME -- conventions associated with book names

    BOOKS -- files of ACL2 event forms

    BOOKS-CERTIFICATION -- certifying ACL2 community books

    BOOKS-CERTIFICATION-CLASSIC -- classic ACL2 `make'-based certification of books

    BOOLE$ -- perform a bit-wise logical operation on 2 two's complement integers

    BOOLEANP -- recognizer for booleans

    BOUNDERS -- intervals, bounder functions, and bounder correctness

    BREAK$ -- cause an immediate Lisp break

    BREAK-LEMMA -- a quick introduction to breaking rewrite rules in ACL2

    BREAK-ON-ERROR -- break when encountering a hard or soft error caused by ACL2

    BREAK-REWRITE -- the read-eval-print loop entered to monitor rewrite rules

    BREAKS -- Common Lisp breaks

    BRR -- to enable or disable the breaking of rewrite rules

    BRR-COMMANDS -- Break-Rewrite Commands

    BRR@ -- to access context sensitive information within break-rewrite

    BUILT-IN-CLAUSE -- to build a clause into the simplifier

    BUTLAST -- all but a final segment of a list

    BY -- hints keyword :BY



    C

    CAAAAR -- car of the caaar

    CAAADR -- car of the caadr

    CAAAR -- car of the caar

    CAADAR -- car of the cadar

    CAADDR -- car of the caddr

    CAADR -- car of the cadr

    CAAR -- car of the car

    CADAAR -- car of the cdaar

    CADADR -- car of the cdadr

    CADAR -- car of the cdar

    CADDAR -- car of the cddar

    CADDDR -- car of the cdddr

    CADDR -- car of the cddr

    CADR -- car of the cdr

    CALLING-LD-IN-BAD-CONTEXTS -- errors caused by calling ld in inappropriate contexts

    CANONICAL-PATHNAME -- the true absolute filename, with soft links resolved

    CAR -- returns the first element of a non-empty list, else nil

    CASE -- conditional based on if-then-else using eql

    CASE-MATCH -- pattern matching or destructuring

    CASE-SPLIT -- like force but immediately splits the top-level goal on the hypothesis

    CASE-SPLIT-LIMITATIONS -- a list of two ``numbers'' limiting the number of cases produced at once

    CASES -- hints keyword :CASES

    CBD -- connected book directory string

    CDAAAR -- cdr of the caaar

    CDAADR -- cdr of the caadr

    CDAAR -- cdr of the caar

    CDADAR -- cdr of the cadar

    CDADDR -- cdr of the caddr

    CDADR -- cdr of the cadr

    CDAR -- cdr of the car

    CDDAAR -- cdr of the cdaar

    CDDADR -- cdr of the cdadr

    CDDAR -- cdr of the cdar

    CDDDAR -- cdr of the cddar

    CDDDDR -- cdr of the cdddr

    CDDDR -- cdr of the cddr

    CDDR -- cdr of the cdr

    CDR -- returns the second element of a cons pair, else nil

    CEILING -- division returning an integer by truncating toward positive infinity

    CERTIFICATE -- how a book is known to be admissible and where its defpkgs reside

    CERTIFY-BOOK -- how to produce a certificate for a book

    CERTIFY-BOOK! -- a variant of certify-book

    CERTIFYING-BOOKS -- See books-certification.

    CHAR -- the nth element (zero-based) of a string

    CHAR-CODE -- the numeric code for a given character

    CHAR-DOWNCASE -- turn upper-case characters into lower-case characters

    CHAR-EQUAL -- character equality without regard to case

    CHAR-UPCASE -- turn lower-case characters into upper-case characters

    CHAR< -- less-than test for characters

    CHAR<= -- less-than-or-equal test for characters

    CHAR> -- greater-than test for characters

    CHAR>= -- greater-than-or-equal test for characters

    CHARACTER-ALISTP -- recognizer for association lists with characters as keys

    CHARACTER-ENCODING -- how bytes are parsed into characters

    CHARACTER-LISTP -- recognizer for a true list of characters

    CHARACTERP -- recognizer for characters

    CHARACTERS -- characters in ACL2

    CHECK-SUM -- assigning ``often unique'' integers to files and objects

    CHECKPOINT-FORCED-GOALS -- Cause forcing goals to be checkpointed in proof trees

    CLAUSE-IDENTIFIER -- the internal form of a goal-spec

    CLAUSE-PROCESSOR -- make or apply a :clause-processor rule (goal-level simplifier)

    CLEAR-HASH-TABLES -- deprecated feature

    CLEAR-MEMOIZE-STATISTICS -- clears all profiling info displayed by (memoize-summary)

    CLEAR-MEMOIZE-TABLE -- forget values remembered for the given function

    CLEAR-MEMOIZE-TABLES -- forget values remembered for all the memoized functions

    CLOSE-INPUT-CHANNEL -- See io.

    CLOSE-OUTPUT-CHANNEL -- See io.

    CLOSE-TRACE-FILE -- stop redirecting trace output to a file

    CODE-CHAR -- the character corresponding to a given numeric code

    COERCE -- coerce a character list to a string and a string to a list

    COMMAND -- forms you type at the top-level, but...

    COMMAND-DESCRIPTOR -- an object describing a particular command typed by the user

    COMMAND-LINE -- handling of command-line arguments when ACL2 is invoked

    COMMON-LISP -- relation to Common Lisp, including deviations from the spec

    COMMUNITY-BOOKS -- books contributed by the ACL2 community

    COMP -- compile some ACL2 functions

    COMP-GCL -- compile some ACL2 functions leaving .c and .h files

    COMPILATION -- compiling ACL2 functions

    COMPILING-ACL2P -- compiling ACL2(p)

    COMPLEX -- create an ACL2 number

    COMPLEX-RATIONALP -- recognizes complex rational numbers

    COMPLEX/COMPLEX-RATIONALP -- recognizer for complex numbers

    COMPOUND-RECOGNIZER -- make a rule used by the typing mechanism

    COMPRESS1 -- remove irrelevant pairs from a 1-dimensional array

    COMPRESS2 -- remove irrelevant pairs from a 2-dimensional array

    COMPUTED-HINTS -- computing advice to the theorem proving process

    CONCATENATE -- concatenate lists or strings together

    COND -- conditional based on if-then-else

    CONGRUENCE -- the relations to maintain while simplifying arguments

    CONJUGATE -- complex number conjugate

    CONS -- pair and list constructor

    CONS-SUBTREES -- (cons-subtrees x nil) builds a fast alist that associates each subtree
    of X with T, without duplication.


    CONSERVATIVITY-OF-DEFCHOOSE -- proof of conservativity of defchoose

    CONSP -- recognizer for cons pairs

    CONSTRAINT -- restrictions on certain functions introduced in encapsulate events

    COPYRIGHT -- ACL2 copyright, license, sponsorship

    COROLLARY -- the corollary formula of a rune

    COUNT -- count the number of occurrences of an item in a string or true-list

    CPU-CORE-COUNT -- the number of cpu cores

    CURRENT-PACKAGE -- the package used for reading and printing

    CURRENT-THEORY -- currently enabled rules as of logical name

    CUSTOM-KEYWORD-HINTS -- user-defined hints

    CW -- print to the comment window

    CW! -- print to the comment window

    CW-GSTACK -- debug a rewriting loop or stack overflow

    Common Lisp -- Common Lisp

    Common Lisp as a Modeling Language -- Common Lisp as a Modeling Language

    Conversion -- Conversion to Uppercase

    Corroborating Models -- Corroborating Models



    D

    DEAD-EVENTS -- using proof supporters to identify dead code and unused theorems

    DEALING-WITH-KEY-COMBINATIONS-OF-FUNCTION-SYMBOLS -- how to get rid of key combinations of function symbols

    DEALING-WITH-TAU-PROBLEMS -- some advice on dealing with problems caused by the tau system

    DECLARE -- declarations

    DECLARE-STOBJS -- declaring a formal parameter name to be a single-threaded object

    DEFABBREV -- a convenient form of macro definition for simple expansions

    DEFABSSTOBJ -- define a new abstract single-threaded object

    DEFABSSTOBJ-MISSING-EVENTS -- obtain the events needed to admit a defabsstobj event

    DEFATTACH -- execute constrained functions using corresponding attached functions

    DEFAULT -- return the :default from the header of a 1- or 2-dimensional array

    DEFAULT-BACKCHAIN-LIMIT -- specifying the backchain limit for a rule

    DEFAULT-DEFUN-MODE -- the default defun-mode of defun'd functions

    DEFAULT-HINTS -- a list of hints added to every proof attempt

    DEFAULT-HINTS-TABLE -- a table used to provide hints for proofs

    DEFAULT-PRINT-PROMPT -- the default prompt printed by ld

    DEFAULT-RULER-EXTENDERS -- the default ruler-extenders for defun'd functions

    DEFAULT-TOTAL-PARALLELISM-WORK-LIMIT -- for ACL2(p): returns the default value for global total-parallelism-work-limit

    DEFAULT-VERIFY-GUARDS-EAGERNESS -- See set-verify-guards-eagerness.

    DEFAXIOM -- add an axiom

    DEFCHOOSE -- define a Skolem (witnessing) function

    DEFCONG -- prove congruence rule

    DEFCONST -- define a constant

    DEFDOC -- add a documentation topic

    DEFEQUIV -- prove that a function is an equivalence relation

    DEFEVALUATOR -- introduce an evaluator function

    DEFEXEC -- attach a terminating executable function to a definition

    DEFINE-PC-HELP -- define a macro command whose purpose is to print something

    DEFINE-PC-MACRO -- define a proof-checker macro command

    DEFINE-PC-META -- define a proof-checker meta command

    DEFINE-TRUSTED-CLAUSE-PROCESSOR -- define a trusted (unverified) goal-level simplifier

    DEFINITION -- make a rule that acts like a function definition

    DEFLABEL -- build a landmark and/or add a documentation topic

    DEFLOCK -- define a wrapper macro that provides mutual exclusion in ACL2(p)

    DEFMACRO -- define a macro

    DEFMACRO-LAST -- define a macro that returns its last argument, but with side effects

    DEFN -- definition with guard t

    DEFND -- disabled definition with guard t

    DEFPKG -- define a new symbol package

    DEFPROXY -- define a non-executable :program-mode function for attachment

    DEFPUN -- define a tail-recursive function symbol

    DEFREFINEMENT -- prove that equiv1 refines equiv2

    DEFSTOBJ -- define a new single-threaded object

    DEFSTUB -- stub-out a function symbol

    DEFTHEORY -- define a theory (to enable or disable a set of rules)

    DEFTHEORY-STATIC -- define a `static' theory (to enable or disable a set of rules)

    DEFTHM -- prove and name a theorem

    DEFTHMD -- prove and name a theorem and then disable it

    DEFTTAG -- introduce a trust tag (ttag)

    DEFUN -- define a function symbol

    DEFUN-INLINE -- define a potentially inlined function symbol and associated macro

    DEFUN-MODE -- determines whether a function definition is a logical act

    DEFUN-MODE-CAVEAT -- potential soundness issue for functions with defun-mode :program

    DEFUN-NOTINLINE -- define a not-to-be-inlined function symbol and associated macro

    DEFUN-NX -- define a non-executable function symbol

    DEFUN-SK -- define a function whose body has an outermost quantifier

    DEFUN-SK-EXAMPLE -- a simple example using defun-sk

    DEFUND -- define a function symbol and then disable it

    DEFUND-INLINE -- define a potentially disabled, inlined function symbol and associated macro

    DEFUND-NOTINLINE -- define a disabled, not-to-be-inlined function symbol and associated macro

    DEFUNS -- an alternative to mutual-recursion

    DELETE-ASSOC -- remove the first pair from an association list for a given key

    DELETE-ASSOC-EQ -- See delete-assoc.

    DELETE-ASSOC-EQUAL -- See delete-assoc.

    DELETE-INCLUDE-BOOK-DIR -- unlink keyword for :dir argument of ld and include-book

    DENOMINATOR -- divisor of a ratio in lowest terms

    DIGIT-CHAR-P -- the number, if any, corresponding to a given character

    DIGIT-TO-CHAR -- map a digit to a character

    DIMENSIONS -- return the :dimensions from the header of a 1- or 2-dimensional array

    DISABLE -- deletes names from current theory

    DISABLE-FORCING -- to disallow forced case-splits

    DISABLE-IMMEDIATE-FORCE-MODEP -- forced hypotheses are not attacked immediately

    DISABLEDP -- determine whether a given name or rune is disabled

    DISASSEMBLE$ -- disassemble a function

    DIVE-INTO-MACROS-TABLE -- right-associated function information for the proof-checker

    DMR -- dynamically monitor rewrites and other prover activity

    DO-NOT -- hints keyword :DO-NOT

    DO-NOT-INDUCT -- hints keyword :DO-NOT-INDUCT

    DOC -- brief documentation (type :doc name)

    DOC! -- all the documentation for a name (type :doc! name)

    DOC-STRING -- formatted documentation strings

    DOCS -- available documentation topics (by section)

    DOCUMENTATION -- functions that display documentation

    DOUBLE-REWRITE -- cause a term to be rewritten twice

    DYNAMICALLY-MONITOR-REWRITES -- See dmr.



    E

    E/D -- enable/disable rules

    E0-ORD-< -- the old ordering function for ACL2 ordinals

    E0-ORDINALP -- the old recognizer for ACL2 ordinals

    EARLY-TERMINATION -- early termination for pand and por.

    EC-CALL -- execute a call in the ACL2 logic instead of raw Lisp

    EIGHTH -- eighth member of the list

    ELIM -- make a destructor elimination rule

    EMACS -- emacs support for ACL2

    EMBEDDED-EVENT-FORM -- forms that may be embedded in other events

    ENABLE -- adds names to current theory

    ENABLE-FORCING -- to allow forced splits

    ENABLE-IMMEDIATE-FORCE-MODEP -- forced hypotheses are attacked immediately

    ENCAPSULATE -- hide some events and/or constrain some functions

    ENDP -- recognizer for empty lists

    ENTER-BOOT-STRAP-MODE -- The first millisecond of the Big Bang

    EQ -- equality of symbols

    EQL -- test equality (of two numbers, symbols, or characters)

    EQLABLE-ALISTP -- recognizer for a true list of pairs whose cars are suitable for eql

    EQLABLE-LISTP -- recognizer for a true list of objects each suitable for eql

    EQLABLEP -- the guard for the function eql

    EQUAL -- true equality

    EQUALITY-VARIANTS -- versions of a function using different equality tests

    EQUALITY-VARIANTS-DETAILS -- details about equality-variants

    EQUIVALENCE -- mark a relation as an equivalence relation

    EQUIVALENT-FORMULAS-DIFFERENT-REWRITE-RULES -- logically equivalent formulas can generate radically different rules

    ER -- print an error message and ``cause an error''

    ER-PROGN -- perform a sequence of state-changing ``error triples''

    ERROR-TRIPLES -- a common ACL2 programming idiom

    ERROR-TRIPLES-AND-PARALLELISM -- how to avoid error triples in ACL2(p)

    ERROR1 -- print an error message and cause a ``soft error''

    ESCAPE-TO-COMMON-LISP -- escaping to Common Lisp

    EVALUATOR-RESTRICTIONS -- some restrictions on the use of evaluators in meta-level rules

    EVENP -- test whether an integer is even

    EVENTS -- functions that extend the logic

    EVISC-TABLE -- support for abbreviated output

    EVISC-TUPLE -- control suppression of details when printing

    EVISCERATE-HIDE-TERMS -- to print (hide ...) as <hidden>

    EXAMPLE-INDUCTION-SCHEME-BINARY-TREES -- induction on binary trees

    EXAMPLE-INDUCTION-SCHEME-DOWN-BY-2 -- induction downwards 2 steps at a time

    EXAMPLE-INDUCTION-SCHEME-NAT-RECURSION -- induction on natural numbers

    EXAMPLE-INDUCTION-SCHEME-ON-LISTS -- induction on lists

    EXAMPLE-INDUCTION-SCHEME-ON-SEVERAL-VARIABLES -- induction on several variables

    EXAMPLE-INDUCTION-SCHEME-UPWARDS -- induction upwards

    EXAMPLE-INDUCTION-SCHEME-WITH-ACCUMULATORS -- induction scheme with accumulators

    EXAMPLE-INDUCTION-SCHEME-WITH-MULTIPLE-INDUCTION-STEPS -- induction scheme with more than one induction step

    EXAMPLE-INDUCTIONS -- some examples of induction schemes in ACL2

    EXECUTABLE-COUNTERPART -- a rule for computing the value of a function

    EXECUTABLE-COUNTERPART-THEORY -- executable counterpart rules as of logical name

    EXISTS -- existential quantifier

    EXIT -- quit entirely out of Lisp

    EXIT-BOOT-STRAP-MODE -- the end of pre-history

    EXPAND -- hints keyword :EXPAND

    EXPLODE-NONNEGATIVE-INTEGER -- the list of characters in the radix-r form of a number

    EXPT -- exponential function

    EXTENDED-METAFUNCTIONS -- state and context sensitive metafunctions

    EXTERNAL-FORMAT -- See character-encoding.

    EXTRA-INFO -- generate markers to indicate sources of guard proof obligations

    Evaluating App on Sample Input -- Evaluating App on Sample Input



    F

    F-GET-GLOBAL -- get the value of a global variable in state

    F-PUT-GLOBAL -- assign to a global variable in state

    FAILED-FORCING -- how to deal with a proof failure in a forcing round

    FAILURE -- how to deal with a proof failure

    FAST-ALIST-FREE -- (fast-alist-free alist) throws away the hash table associated with a fast
    alist.


    FAST-ALIST-FREE-ON-EXIT -- Free a fast alist after the completion of some form.

    FAST-ALIST-LEN -- (fast-alist-len alist) counts the number of unique keys in a fast alist.

    FAST-ALIST-SUMMARY -- (fast-alist-summary) prints some basic statistics about any current fast
    alists.


    FAST-ALISTS -- alists with hidden hash tables for faster execution

    FC-REPORT -- to report on the forward chaining activity in the most recent proof

    FIFTH -- fifth member of the list

    FILE-READING-EXAMPLE -- example of reading files in ACL2

    FINALIZE-EVENT-USER -- user-supplied code to complete events, e.g., with extra summary output

    FIND-RULES-OF-RUNE -- find the rules named rune

    FINDING-DOCUMENTATION -- searching the documentation

    FIRST -- first member of the list

    FIX -- coerce to a number

    FIX-TRUE-LIST -- coerce to a true list

    FLET -- local binding of function symbols

    FLOOR -- division returning an integer by truncating toward negative infinity

    FLUSH-COMPRESS -- flush the under-the-hood array for the given name

    FLUSH-HONS-GET-HASH-TABLE-LINK -- deprecated feature

    FMS -- :(str alist co-channel state evisc) => state

    FMS! -- :(str alist co-channel state evisc) => state

    FMS!-TO-STRING -- See printing-to-strings.

    FMS-TO-STRING -- See printing-to-strings.

    FMT -- formatted printing

    FMT! -- :(str alist co-channel state evisc) => state

    FMT!-TO-STRING -- See printing-to-strings.

    FMT-TO-COMMENT-WINDOW -- print to the comment window

    FMT-TO-STRING -- See printing-to-strings.

    FMT1 -- :(str alist col co-channel state evisc) => (mv col state)

    FMT1! -- :(str alist col channel state evisc) => (mv col state)

    FMT1!-TO-STRING -- See printing-to-strings.

    FMT1-TO-STRING -- See printing-to-strings.

    FNCALL-TERM -- See meta-extract.

    FORALL -- universal quantifier

    FORCE -- identity function used to force a hypothesis

    FORCED -- See force.

    FORCING-ROUND -- a section of a proof dealing with forced assumptions

    FORWARD-CHAINING -- make a rule to forward chain when a certain trigger arises

    FORWARD-CHAINING-REPORTS -- to see reports about the forward chaining process

    FOURTH -- fourth member of the list

    FREE-VARIABLES -- free variables in rules

    FREE-VARIABLES-EXAMPLES -- examples pertaining to free variables in rules

    FREE-VARIABLES-EXAMPLES-FORWARD-CHAINING -- examples pertaining to free variables in forward-chaining rules

    FREE-VARIABLES-EXAMPLES-REWRITE -- examples pertaining to free variables in rewrite rules

    FREE-VARIABLES-TYPE-PRESCRIPTION -- matching for free variable in type-prescription rules

    FREQUENTLY-ASKED-QUESTIONS-BY-NEWCOMERS -- some questions newcomers frequently ask

    FULL-BOOK-NAME -- book naming conventions assumed by ACL2

    FUNCTION-THEORY -- function symbol rules as of logical name

    FUNCTIONAL-INSTANTIATION-EXAMPLE -- a small proof demonstrating functional instantiation

    FUNCTIONAL-INSTANTIATION-IN-ACL2R -- additional requirements for :functional-instance hints in ACL2(r)

    FURTHER-INFORMATION-ON-REWRITING -- a grab bag of advice and information on rewriting

    FUTURE-WORK-RELATED-TO-THE-TAU-SYSTEM -- some tau system problems that we hope someone will address

    Flawed Induction Candidates in App Example -- Flawed Induction Candidates in App Example

    Free Variables in Top-Level Input -- Free Variables in Top-Level Input

    Functions for Manipulating these Objects -- Functions for Manipulating these Objects



    G

    GAG-MODE -- verbosity of proof output

    GC$ -- invoke the garbage collector

    GC-VERBOSE -- control printing from the garbage collector

    GCL -- tips on building and using ACL2 based on Gnu Common Lisp

    GCS -- See get-command-sequence.

    GENERALIZE -- make a rule to restrict generalizations

    GENERALIZED-BOOLEANS -- potential soundness issues related to ACL2 predicates

    GENERALIZING-KEY-CHECKPOINTS -- getting rid of unnecessary specificity

    GET-COMMAND-SEQUENCE -- return list of commands that are between two command descriptors

    GET-INTERNAL-TIME -- runtime vs. realtime in ACL2 timings

    GET-OUTPUT-STREAM-STRING$ -- See io.

    GET-WORMHOLE-STATUS -- make a wormhole's status visible outside the wormhole

    GETENV$ -- read an environment variable

    GETPROP -- access fast property lists

    GOAL-SPEC -- to indicate where a hint is to be used

    GOOD-ATOM-LISTP -- recognizer for a true list of ``good'' atoms

    GOOD-BYE -- quit entirely out of Lisp

    GRANULARITY -- limit the amount of parallelism

    GROUND-ZERO -- enabled rules in the startup theory

    GUARD -- restricting the domain of a function

    GUARD-DEBUG -- generate markers to indicate sources of guard proof obligations

    GUARD-EVALUATION-EXAMPLES-LOG -- log showing combinations of defun-modes and guard-checking

    GUARD-EVALUATION-EXAMPLES-SCRIPT -- a script to show combinations of defun-modes and guard-checking

    GUARD-EVALUATION-TABLE -- a table that shows combinations of defun-modes and guard-checking

    GUARD-EXAMPLE -- a brief transcript illustrating guards in ACL2

    GUARD-HINTS -- xargs keyword :GUARD-HINTS

    GUARD-INTRODUCTION -- introduction to guards in ACL2

    GUARD-MISCELLANY -- miscellaneous remarks about guards

    GUARD-OBLIGATION -- the guard proof obligation

    GUARD-QUICK-REFERENCE -- brief summary of guard checking and guard verification

    GUARDS-AND-EVALUATION -- the relationship between guards and evaluation

    GUARDS-FOR-SPECIFICATION -- guards as a specification device

    Guards -- Guards

    Guessing the Type of a Newly Admitted Function -- Guessing the Type of a Newly Admitted Function

    Guiding the ACL2 Theorem Prover -- Guiding the ACL2 Theorem Prover



    H

    HANDS-OFF -- hints keyword :HANDS-OFF

    HARD-ERROR -- print an error message and stop execution

    HEADER -- return the header of a 1- or 2-dimensional array

    HELP -- brief survey of ACL2 features

    HIDDEN-DEATH-PACKAGE -- handling defpkg events that are local

    HIDDEN-DEFPKG -- handling defpkg events that are local

    HIDE -- hide a term from the rewriter

    HINTS -- advice to the theorem proving process

    HINTS-AND-THE-WATERFALL -- how hints fit into the ACL2 proof waterfall

    HISTORY -- functions that display or change history

    HONS -- (hons x y) returns a normed object equal to (cons x y).

    HONS-ACONS -- (hons-acons key val alist) is the main way to create or extend
    fast-alists.


    HONS-ACONS! -- (hons-acons! key val alist) is an alternative to hons-acons that
    produces normed, fast alists.


    HONS-AND-MEMOIZATION -- hash cons, function memoization, and applicative hash tables

    HONS-ASSOC-EQUAL -- (hons-assoc-equal key alist) is not fast; it serves as the logical
    definition for hons-get.


    HONS-CLEAR -- (hons-clear gc) is a drastic garbage collection mechanism that clears out
    the underlying Hons Space.


    HONS-COPY -- (hons-copy x) returns a normed object that is equal to X.

    HONS-COPY-PERSISTENT -- (hons-copy-persistent x) returns a normed object that is equal to X
    and which will be re-normed after any calls to hons-clear.


    HONS-EQUAL -- (hons-equal x y) is a recursive equality check that optimizes when parts of
    its arguments are normed.


    HONS-EQUAL-LITE -- (hons-equal-lite x y) is a non-recursive equality check that optimizes if
    its arguments are normed.


    HONS-GET -- (hons-get key alist) is the efficient lookup operation for
    fast-alists.


    HONS-NOTE -- notes about HONS, especially pertaining to expensive resizing operations

    HONS-RESIZE -- (hons-resize ...) can be used to manually adjust the sizes of the hash
    tables that govern which ACL2 Objects are considered normed.


    HONS-SHRINK-ALIST -- (hons-shrink-alist alist ans) can be used to eliminate "shadowed pairs"
    from an alist or to copy fast-alists.


    HONS-SHRINK-ALIST! -- (hons-shrink-alist! alist ans) is an alternative to hons-shrink-alist
    that produces a normed result.


    HONS-SUMMARY -- (hons-summary) prints basic information about the sizes of the tables in
    the current Hons Space.


    HONS-WASH -- (hons-wash) is like gc$ but can also garbage collect normed
    objects (CCL Only).


    Hey Wait! Is ACL2 Typed or Untyped(Q) -- Hey Wait! Is ACL2 Typed or Untyped?

    How Long Does It Take to Become an Effective User(Q) -- How Long Does It Take to Become an Effective User?

    How To Find Out about ACL2 Functions -- How To Find Out about ACL2 Functions

    How To Find Out about ACL2 Functions (cont) -- How To Find Out about ACL2 Functions (cont)



    I

    I-AM-HERE -- a convenient marker for use with rebuild

    I-CLOSE -- ACL2(r) test for whether two numbers are infinitesimally close

    I-LARGE -- ACL2(r) recognizer for infinitely large numbers

    I-LIMITED -- ACL2(r) recognizer for limited numbers

    I-SMALL -- ACL2(r) recognizer for infinitesimal numbers

    IDENTITY -- the identity function

    IF -- if-then-else function

    IF* -- for conditional rewriting with BDDs

    IF-INTRO -- See splitter.

    IFF -- logical ``if and only if''

    IFIX -- coerce to an integer

    IGNORABLE -- See declare.

    IGNORE -- See declare.

    IGNORED-ATTACHMENT -- why attachments are sometimes not used

    ILLEGAL -- print an error message and stop execution

    IMAGPART -- imaginary part of a complex number

    IMMED-FORCED -- See splitter.

    IMMEDIATE-FORCE-MODEP -- when executable counterpart is enabled,
    forced hypotheses are attacked immediately


    IMPLIES -- logical implication

    IMPROPER-CONSP -- recognizer for improper (non-null-terminated) non-empty lists

    IN-ARITHMETIC-THEORY -- designate theory for some rewriting done for non-linear arithmetic

    IN-PACKAGE -- select current package

    IN-TAU-INTERVALP -- Boolean membership in a tau interval

    IN-THEORY -- designate ``current'' theory (enabling its rules)

    INCLUDE-BOOK -- load the events in a file

    INCOMPATIBLE -- declaring that two rules should not both be enabled

    INDUCT -- hints keyword :INDUCT

    INDUCTION -- make a rule that suggests a certain induction

    INITIALIZE-EVENT-USER -- user-supplied code to initiate events

    INSTRUCTIONS -- instructions to the proof checker

    INT= -- test equality of two integers

    INTEGER-LENGTH -- number of bits in two's complement integer representation

    INTEGER-LISTP -- recognizer for a true list of integers

    INTEGERP -- recognizer for whole numbers

    INTERESTING-APPLICATIONS -- some industrial examples of ACL2 use

    INTERN -- create a new symbol in a given package

    INTERN$ -- create a new symbol in a given package

    INTERN-IN-PACKAGE-OF-SYMBOL -- create a symbol with a given name

    INTERSECTION$ -- elements of one list that are not elements of another

    INTERSECTION-EQ -- See intersection$.

    INTERSECTION-EQUAL -- See intersection$.

    INTERSECTION-THEORIES -- intersect two theories

    INTERSECTP -- test whether two lists intersect

    INTERSECTP-EQ -- See intersectp.

    INTERSECTP-EQUAL -- See intersectp.

    INTRODUCTION-TO-A-FEW-SYSTEM-CONSIDERATIONS -- the mechanics of interaction with the theorem prover

    INTRODUCTION-TO-HINTS -- how to provide hints to the theorem prover

    INTRODUCTION-TO-KEY-CHECKPOINTS -- What questions to ask at key checkpoints

    INTRODUCTION-TO-REWRITE-RULES-PART-1 -- introduction to ACL2's notion of rewrite rules

    INTRODUCTION-TO-REWRITE-RULES-PART-2 -- how to arrange rewrite rules

    INTRODUCTION-TO-THE-DATABASE -- how to update the database

    INTRODUCTION-TO-THE-TAU-SYSTEM -- a decision procedure for runtime types

    INTRODUCTION-TO-THE-THEOREM-PROVER -- how the theorem prover works -- level 0

    INTRODUCTORY-CHALLENGE-PROBLEM-1 -- challenge problem 1 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-1-ANSWER -- answer to challenge problem 1 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-2 -- challenge problem 2 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-2-ANSWER -- answer to challenge problem 2 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-3 -- challenge problem 3 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-3-ANSWER -- answer to challenge problem 3 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-4 -- challenge problem 4 for the new user of ACL2

    INTRODUCTORY-CHALLENGE-PROBLEM-4-ANSWER -- answer to challenge problem 4 for the new user of ACL2

    INTRODUCTORY-CHALLENGES -- challenge problems for the new ACL2 user

    INVISIBLE-FNS-TABLE -- functions that are invisible to the loop-stopper algorithm

    IO -- input/output facilities in ACL2

    IPRINT -- See set-iprint.

    IPRINTING -- See set-iprint.

    IRRELEVANT-FORMALS -- formals that are used but only insignificantly



    J



    K

    KEEP -- how we know if include-book read the correct files

    KEYWORD -- See keywordp.

    KEYWORD-COMMANDS -- how keyword commands are processed

    KEYWORD-VALUE-LISTP -- recognizer for true lists whose even-position elements are keywords

    KEYWORDP -- recognizer for keywords

    KWOTE -- quote an arbitrary object

    KWOTE-LST -- quote an arbitrary true list of objects



    L

    LAMBDA -- See term.

    LAST -- the last cons (not element) of a list

    LAST-PROVER-STEPS -- the number of prover steps most recently taken

    LD -- the ACL2 read-eval-print loop, file loader, and command processor

    LD-ERROR-ACTION -- determines ld's response to an error

    LD-ERROR-TRIPLES -- determines whether a form caused an error during ld

    LD-EVISC-TUPLE -- determines whether ld suppresses details when printing

    LD-KEYWORD-ALIASES -- abbreviation of some keyword commands

    LD-MISSING-INPUT-OK -- determines which forms ld evaluates

    LD-POST-EVAL-PRINT -- determines whether and how ld prints the result of evaluation

    LD-PRE-EVAL-FILTER -- determines which forms ld evaluates

    LD-PRE-EVAL-PRINT -- determines whether ld prints the forms to be eval'd

    LD-PROMPT -- determines the prompt printed by ld

    LD-QUERY-CONTROL-ALIST -- how to default answers to queries

    LD-REDEFINITION-ACTION -- to allow redefinition without undoing

    LD-SKIP-PROOFSP -- how carefully ACL2 processes your commands

    LD-VERBOSE -- determines whether ld prints ``ACL2 Loading ...''

    LEMMA-INSTANCE -- an object denoting an instance of a theorem

    LEN -- length of a list

    LENGTH -- length of a string or proper list

    LET -- binding of lexically scoped (local) variables

    LET* -- binding of lexically scoped (local) variables

    LEXORDER -- total order on ACL2 objects

    LINEAR -- make some arithmetic inequality rules

    LINEAR-ARITHMETIC -- A description of the linear arithmetic decision procedure

    LIST -- build a list

    LIST* -- build a list

    LISTP -- recognizer for (not necessarily proper) lists

    LOCAL -- hiding an event in an encapsulation or book

    LOCAL-INCOMPATIBILITY -- when non-local events won't replay in isolation

    LOGAND -- bitwise logical `and' of zero or more integers

    LOGANDC1 -- bitwise logical `and' of two ints, complementing the first

    LOGANDC2 -- bitwise logical `and' of two ints, complementing the second

    LOGBITP -- the ith bit of an integer

    LOGCOUNT -- number of ``on'' bits in a two's complement number

    LOGEQV -- bitwise logical equivalence of zero or more integers

    LOGIC -- to set the default defun-mode to :logic

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED -- background knowledge in ACL2 logic for theorem prover tutorial

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-BASE-CASE -- a brief explanation of base cases

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EQUALS-FOR-EQUALS -- substitution of equals for equals

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-EVALUATION -- evaluation during proofs

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INDUCTIVE-PROOF -- a brief explanation of induction

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-INSTANCE -- a brief explanation of substitution instances

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-PROPOSITIONAL-CALCULUS -- a brief explanation of propositional calculus

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q1-ANSWER -- the inductive step of the rev-rev proof -- Answer to Question 1

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q2-ANSWER -- the inductive step of the rev-rev proof -- Answer to Question 2

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-Q3-ANSWER -- the inductive step of the rev-rev proof -- Answer to Question 2

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING -- a brief explanation of rewriting from the logical perspective

    LOGIC-KNOWLEDGE-TAKEN-FOR-GRANTED-REWRITING-REPEATEDLY -- further information on expanding definitions via rewriting

    LOGICAL-NAME -- a name created by a logical event

    LOGIOR -- bitwise logical inclusive or of zero or more integers

    LOGNAND -- bitwise logical `nand' of two integers

    LOGNOR -- bitwise logical `nor' of two integers

    LOGNOT -- bitwise not of a two's complement number

    LOGORC1 -- bitwise logical inclusive or of two ints, complementing the first

    LOGORC2 -- bitwise logical inclusive or of two ints, complementing the second

    LOGTEST -- test if two integers share a `1' bit

    LOGXOR -- bitwise logical exclusive or of zero or more integers

    LOOP-STOPPER -- limit application of permutative rewrite rules

    LOWER-CASE-P -- recognizer for lower case characters

    LP -- the Common Lisp entry to ACL2



    M

    MACRO-ALIASES-TABLE -- a table used to associate function names with macro names

    MACRO-ARGS -- the formals list of a macro definition

    MACRO-COMMAND -- compound command for the proof-checker

    MAKE-CHARACTER-LIST -- coerce to a list of characters

    MAKE-EVENT -- evaluate (expand) a given form and then evaluate the result

    MAKE-EVENT-DETAILS -- details on make-event expansion

    MAKE-FAST-ALIST -- (make-fast-alist alist) creates a fast-alist from the input alist,
    returning alist itself or, in some cases, a new object equal to it.


    MAKE-LIST -- make a list of a given size

    MAKE-ORD -- a constructor for ordinals.

    MAKE-TAU-INTERVAL -- make a tau interval

    MAKE-WORMHOLE-STATUS -- creates a wormhole status object from given status, entry code, and data

    MANAGING-ACL2-PACKAGES -- user-contributed documentation on packages

    MARKUP -- the markup language for ACL2 documentation strings

    MAX -- the larger of two numbers

    MAXIMUM-LENGTH -- return the :maximum-length from the header of an array

    MBE -- attach code for execution

    MBE1 -- attach code for execution

    MBT -- introduce a test not to be evaluated

    MEASURE -- xargs keyword :MEASURE

    MEMBER -- membership predicate

    MEMBER-EQ -- See member.

    MEMBER-EQUAL -- See member.

    MEMOIZE -- turn on memoization for a specified function

    MEMOIZE-SUMMARY -- display all collected profiling and memoization table info

    MEMSUM -- display all collected profiling and memoization info

    META -- make a :meta rule (a hand-written simplifier)

    META-EXTRACT -- meta reasoning using valid terms extracted from context or world

    META-EXTRACT-CONTEXTUAL-FACT -- See meta-extract.

    META-EXTRACT-FORMULA -- See meta-extract.

    META-EXTRACT-GLOBAL-FACT -- See meta-extract.

    META-EXTRACT-GLOBAL-FACT+ -- See meta-extract.

    META-EXTRACT-RW+-TERM -- See meta-extract.

    MIN -- the smaller of two numbers

    MINIMAL-THEORY -- a minimal theory to enable

    MINUSP -- test whether a number is negative

    MISCELLANEOUS -- a miscellany of documented functions and concepts
    (often cited in more accessible documentation)


    MOD -- remainder using floor

    MOD-EXPT -- exponential function

    MODE -- xargs keyword :MODE

    MONITOR -- to monitor the attempted application of a rule name

    MONITORED-RUNES -- print the monitored runes and their break conditions

    MORE -- your response to :doc or :more's ``(type :more...)''

    MORE! -- another response to ``(type :more for more, :more! for the rest)''

    MORE-DOC -- a continuation of the :doc documentation

    MSG -- construct a ``message'' suitable for the ~@ directive of fmt

    MUST-BE-EQUAL -- attach code for execution

    MUTUAL-RECURSION -- define some mutually recursive functions

    MUTUAL-RECURSION-PROOF-EXAMPLE -- a small proof about mutually recursive functions

    MV -- returning a multiple value

    MV-LET -- calling multi-valued ACL2 functions

    MV-LIST -- converting multiple-valued result to a single-valued list

    MV-NTH -- the mv-nth element (zero-based) of a list

    MV? -- return one or more values

    MV?-LET -- calling possibly multi-valued ACL2 functions

    Modeling in ACL2 -- Modeling in ACL2

    Models in Engineering -- Models in Engineering

    Models of Computer Hardware and Software -- Models of Computer Hardware and Software



    N

    NAME -- syntactic rules on logical names

    NAT-LISTP -- recognizer for a true list of natural numbers

    NATP -- a recognizer for the natural numbers

    NESTED-STOBJS -- using stobjs that contain stobjs

    NEVER-MEMOIZE -- Mark a function as unsafe to memoize.

    NFIX -- coerce to a natural number

    NIL-GOAL -- how to proceed when the prover generates a goal of nil

    NINTH -- ninth member of the list

    NO-DUPLICATESP -- check for duplicates in a list

    NO-DUPLICATESP-EQ -- See no-duplicatesp.

    NO-DUPLICATESP-EQUAL -- See no-duplicatesp.

    NO-THANKS -- hints keyword :NO-THANKS

    NON-EXEC -- mark code as non-executable

    NON-EXECUTABLE -- xargs keyword :NON-EXECUTABLE

    NON-LINEAR-ARITHMETIC -- Non-linear Arithmetic

    NONLINEARP -- hints keyword :NONLINEARP

    NONNEGATIVE-INTEGER-QUOTIENT -- natural number division function

    NORMALIZE -- xargs keyword :NORMALIZE

    NORMED -- Normed objects are ACL2 Objects that are "canonical" or "unique" in a
    certain sense.


    NOT -- logical negation

    NOTE-2-0 -- ACL2 Version 2.0 (July, 1997) Notes

    NOTE-2-1 -- ACL2 Version 2.1 (December, 1997) Notes

    NOTE-2-2 -- ACL2 Version 2.2 (August, 1998) Notes

    NOTE-2-3 -- ACL2 Version 2.3 (October, 1998) Notes

    NOTE-2-4 -- ACL2 Version 2.4 (August, 1999) Notes

    NOTE-2-5 -- ACL2 Version 2.5 (June, 2000) Notes

    NOTE-2-5(R) -- ACL2 Version 2.5(r) (June, 2000) Notes

    NOTE-2-6 -- ACL2 Version 2.6 (November, 2001) Notes

    NOTE-2-6(R) -- ACL2 Version 2.6(r) (November, 2001) Notes

    NOTE-2-6-GUARDS -- ACL2 Version 2.6 Notes on Guard-related Changes

    NOTE-2-6-NEW-FUNCTIONALITY -- ACL2 Version 2.6 Notes on New Functionality

    NOTE-2-6-OTHER -- ACL2 Version 2.6 Notes on Other (Minor) Changes

    NOTE-2-6-PROOF-CHECKER -- ACL2 Version 2.6 Notes on Proof-checker Changes

    NOTE-2-6-PROOFS -- ACL2 Version 2.6 Notes on Changes in Proof Engine

    NOTE-2-6-RULES -- ACL2 Version 2.6 Notes on Changes in Rules and Constants

    NOTE-2-6-SYSTEM -- ACL2 Version 2.6 Notes on System-level Changes

    NOTE-2-7 -- ACL2 Version 2.7 (November, 2002) Notes

    NOTE-2-7(R) -- ACL2 Version 2.7(r) (November, 2002) Notes

    NOTE-2-7-BUG-FIXES -- ACL2 Version 2.7 Notes on Bug Fixes

    NOTE-2-7-GUARDS -- ACL2 Version 2.7 Notes on Guard-related Changes

    NOTE-2-7-NEW-FUNCTIONALITY -- ACL2 Version 2.7 Notes on New Functionality

    NOTE-2-7-OTHER -- ACL2 Version 2.7 Notes on Miscellaneous Changes

    NOTE-2-7-PROOF-CHECKER -- ACL2 Version 2.7 Notes on Proof-checker Changes

    NOTE-2-7-PROOFS -- ACL2 Version 2.7 Notes on Changes in Proof Engine

    NOTE-2-7-RULES -- ACL2 Version 2.7 Notes on Changes in Rules and Constants

    NOTE-2-7-SYSTEM -- ACL2 Version 2.7 Notes on System-level Changes

    NOTE-2-8 -- ACL2 Version 2.8 (March, 2004) Notes

    NOTE-2-8(R) -- ACL2 Version 2.8(r) (March, 2003) Notes

    NOTE-2-8-BUG-FIXES -- ACL2 Version 2.8 Notes on Bug Fixes

    NOTE-2-8-GUARDS -- ACL2 Version 2.8 Notes on Guard-related Changes

    NOTE-2-8-NEW-FUNCTIONALITY -- ACL2 Version 2.8 Notes on New Functionality

    NOTE-2-8-ORDINALS -- ACL2 Version 2.8 Notes on Changes to the Ordinals

    NOTE-2-8-OTHER -- ACL2 Version 2.8 Notes on Miscellaneous Changes

    NOTE-2-8-PROOF-CHECKER -- ACL2 Version 2.8 Notes on Proof-checker Changes

    NOTE-2-8-PROOFS -- ACL2 Version 2.8 Notes on Changes in Proof Engine

    NOTE-2-8-RULES -- ACL2 Version 2.8 Notes on Changes in Rules, Definitions, and Constants

    NOTE-2-8-SYSTEM -- ACL2 Version 2.8 Notes on System-level Changes

    NOTE-2-9 -- ACL2 Version 2.9 (October, 2004) Notes

    NOTE-2-9(R) -- ACL2 Version 2.9(r) (October, 2004) Notes

    NOTE-2-9-1 -- ACL2 Version 2.9.1 (December, 2004) Notes

    NOTE-2-9-2 -- ACL2 Version 2.9.2 (April, 2005) Notes

    NOTE-2-9-3 -- ACL2 Version 2.9.3 (August, 2005) Notes

    NOTE-2-9-3-PPR-CHANGE -- change in pretty-printing for ACL2 Version_2.9.3

    NOTE-2-9-4 -- ACL2 Version 2.9.4 (February, 2006) Notes

    NOTE-2-9-5 -- Changes in Version 3.0 since Version 2.9.4

    NOTE-3-0 -- ACL2 Version 3.0 (June, 2006) Notes

    NOTE-3-0(R) -- ACL2 Version 3.0(r) (June, 2006) Notes

    NOTE-3-0-1 -- ACL2 Version 3.0.1 (August, 2006) Notes

    NOTE-3-0-1(R) -- ACL2 Version 3.0.1(r) (August, 2006) Notes

    NOTE-3-0-2 -- ACL2 Version 3.0.2 (December, 2006) Notes

    NOTE-3-1 -- ACL2 Version 3.1 (December, 2006) Notes

    NOTE-3-1(R) -- ACL2 Version 3.1(r) (December, 2006) Notes

    NOTE-3-2 -- ACL2 Version 3.2 (April, 2007) Notes

    NOTE-3-2(R) -- ACL2 Version 3.2(r) (April, 2007) Notes

    NOTE-3-2-1 -- ACL2 Version 3.2.1 (June, 2007) Notes

    NOTE-3-2-1(R) -- ACL2 Version 3.2.1(r) (June, 2007) Notes

    NOTE-3-3 -- ACL2 Version 3.3 (November, 2007) Notes

    NOTE-3-3(R) -- ACL2 Version 3.3(r) (November, 2007) Notes

    NOTE-3-4 -- ACL2 Version 3.4 (August, 2008) Notes

    NOTE-3-4(R) -- ACL2 Version 3.4(r) (August, 2008) Notes

    NOTE-3-5 -- ACL2 Version 3.5 (May, 2009) Notes

    NOTE-3-5(R) -- ACL2 Version 3.5(r) (May, 2009) Notes

    NOTE-3-6 -- ACL2 Version 3.6 (August, 2009) Notes

    NOTE-3-6(R) -- ACL2 Version 3.6(r) (August, 2009) Notes

    NOTE-3-6-1 -- ACL2 Version 3.6.1 (September, 2009) Notes

    NOTE-4-0 -- ACL2 Version 4.0 (July, 2010) Notes

    NOTE-4-0(R) -- ACL2 Version 4.0(r) (July, 2010) Notes

    NOTE-4-0-WORMHOLE-CHANGES -- how to convert calls of wormhole for Version 4.0

    NOTE-4-1 -- ACL2 Version 4.1 (September, 2010) Notes

    NOTE-4-1(R) -- ACL2 Version 4.1(r) (September, 2010) Notes

    NOTE-4-2 -- ACL2 Version 4.2 (January, 2011) Notes

    NOTE-4-2(R) -- ACL2 Version 4.2(r) (January, 2011) Notes

    NOTE-4-3 -- ACL2 Version 4.3 (July, 2011) Notes

    NOTE-4-3(R) -- ACL2 Version 4.3(r) (July, 2011) Notes

    NOTE-5-0 -- ACL2 Version 5.0 (August, 2012) Notes

    NOTE-6-0 -- ACL2 Version 6.0 (December, 2012) Notes

    NOTE-6-1 -- ACL2 Version 6.1 (February, 2013) Notes

    NOTE-6-2 -- ACL2 Version 6.2 (June, 2013) Notes

    NOTE-6-3 -- ACL2 Version 6.3 (October, 2013) Notes

    NOTE1 -- Acl2 Version 1.1 Notes

    NOTE2 -- Acl2 Version 1.2 Notes

    NOTE3 -- Acl2 Version 1.3 Notes

    NOTE4 -- Acl2 Version 1.4 Notes

    NOTE5 -- Acl2 Version 1.5 Notes

    NOTE6 -- Acl2 Version 1.6 Notes

    NOTE7 -- ACL2 Version 1.7 (released October 1994) Notes

    NOTE8 -- ACL2 Version 1.8 (May, 1995) Notes

    NOTE8-UPDATE -- ACL2 Version 1.8 (Summer, 1995) Notes

    NOTE9 -- ACL2 Version 1.9 (Fall, 1996) Notes

    NQTHM-TO-ACL2 -- ACL2 analogues of Nqthm functions and commands

    NTH -- the nth element (zero-based) of a list

    NTH-ALIASES-TABLE -- a table used to associate names for nth/update-nth printing

    NTHCDR -- final segment of a list

    NU-REWRITER -- rewriting NTH/UPDATE-NTH expressions

    NULL -- recognizer for the empty list

    NUMBER-SUBTREES -- (number-subtrees x) returns the number of distinct subtrees of X, in the
    sense of equal


    NUMERATOR -- dividend of a ratio in lowest terms

    Name the Formula Above -- Name the Formula Above

    Nontautological Subgoals -- Prover output omits some details

    Numbers in ACL2 -- Numbers in ACL2



    O

    O-FINP -- recognizes if an ordinal is finite

    O-FIRST-COEFF -- returns the first coefficient of an ordinal

    O-FIRST-EXPT -- the first exponent of an ordinal

    O-INFP -- recognizes if an ordinal is infinite

    O-P -- a recognizer for the ordinals up to epsilon-0

    O-RST -- returns the rest of an infinite ordinal

    O< -- the well-founded less-than relation on ordinals up to epsilon-0

    O<= -- the less-than-or-equal relation for the ordinals

    O> -- the greater-than relation for the ordinals

    O>= -- the greater-than-or-equal relation for the ordinals

    OBDD -- ordered binary decision diagrams with rewriting

    OBSERVATION -- print an observation

    OBSERVATION-CW -- See observation.

    ODDP -- test whether an integer is odd

    OK-IF -- conditional exit from break-rewrite

    OOPS -- undo a :u or :ubt

    OPEN-INPUT-CHANNEL -- See io.

    OPEN-INPUT-CHANNEL-P -- See io.

    OPEN-OUTPUT-CHANNEL -- See io.

    OPEN-OUTPUT-CHANNEL! -- when trust tags are needed to open output channels

    OPEN-OUTPUT-CHANNEL-P -- See io.

    OPEN-TRACE-FILE -- redirect trace output to a file

    OPTIMIZE -- See declare.

    OR -- disjunction

    ORACLE-APPLY -- call a function argument on the given list of arguments

    ORACLE-APPLY-RAW -- call a function argument on the given list of arguments, no restrictions

    ORACLE-FUNCALL -- call a function argument on the remaining arguments

    ORDINALS -- ordinals in ACL2

    OTF-FLG -- allow more than one initial subgoal to be pushed for induction

    OTHER -- other commonly used top-level functions

    OUTPUT-TO-FILE -- redirecting output to a file

    OVERRIDE-HINTS -- a list of hints given priority in every proof attempt

    On the Naming of Subgoals -- On the Naming of Subgoals

    Other Requirements -- Other Requirements

    Overview of the Expansion of ENDP in the Base Case -- Overview of the Expansion of ENDP in the Base Case

    Overview of the Expansion of ENDP in the Induction Step -- Overview of the Expansion of ENDP in the Induction Step

    Overview of the Final Simplification in the Base Case -- Overview of the Final Simplification in the Base Case

    Overview of the Proof of a Trivial Consequence -- Overview of the Proof of a Trivial Consequence

    Overview of the Simplification of the Base Case to T -- Overview of the Simplification of the Base Case to T

    Overview of the Simplification of the Induction Conclusion -- Overview of the Simplification of the Induction Conclusion

    Overview of the Simplification of the Induction Step to T -- Overview of the Simplification of the Induction Step to T



    P

    P! -- to pop up (at least) one level of ACL2's command loop

    PACKAGE-REINCARNATION-IMPORT-RESTRICTIONS -- re-defining undone defpkgs

    PAIRLIS -- See pairlis$

    PAIRLIS$ -- zipper together two lists

    PAND -- parallel, Boolean version of and

    PARALLEL -- evaluating forms in parallel

    PARALLEL-EXECUTION -- for ACL2(p): configure parallel execution

    PARALLEL-PROGRAMMING -- parallel programming in ACL2(p)

    PARALLEL-PROOF -- parallel proof in ACL2(p)

    PARALLEL-PUSHING-OF-SUBGOALS-FOR-INDUCTION -- consequences of how parallelized proofs of subgoals are pushed for induction

    PARALLELISM -- experimental extension for parallel execution and proofs

    PARALLELISM-AT-THE-TOP-LEVEL -- parallel execution in the ACL2 top-level loop

    PARALLELISM-BUILD -- building an ACL2 executable with parallel execution enabled

    PARALLELISM-PERFORMANCE -- performance issues for parallel execution

    PARALLELISM-TUTORIAL -- a tutorial on how to use the parallelism library.

    PARGS -- parallel evaluation of arguments in a function call

    PATHNAME -- introduction to filename conventions in ACL2

    PBT -- print the commands back through a command descriptor

    PC -- print the command described by a command descriptor

    PCB -- print the command block described by a command descriptor

    PCB! -- print in full the command block described by a command descriptor

    PCS -- print the sequence of commands between two command descriptors

    PE -- print the events named by a logical name

    PE! -- deprecated (see pe)

    PEEK-CHAR$ -- See io.

    PF -- print the formula corresponding to the given name

    PKG-IMPORTS -- list of symbols imported into a given package

    PKG-WITNESS -- return a specific symbol in the indicated package

    PL -- print the rules for the given name or term

    PL2 -- print rule(s) for the given form

    PLET -- parallel version of let

    PLUSP -- test whether a number is positive

    POR -- parallel, Boolean version of or

    PORTCULLIS -- the gate guarding the entrance to a certified book

    POSITION -- position of an item in a string or a list

    POSITION-EQ -- See position.

    POSITION-EQUAL -- See position.

    POSP -- a recognizer for the positive integers

    POST-INDUCTION-KEY-CHECKPOINTS -- reading post-induction key checkpoints

    PPROGN -- evaluate a sequence of forms that return state

    PR -- print the rules stored by the event with the given name

    PR! -- print rules stored by the command with a given command descriptor

    PRACTICE-FORMULATING-STRONG-RULES -- a few simple checkpoints suggesting strong rules

    PRACTICE-FORMULATING-STRONG-RULES-1 -- rules suggested by (TRUE-LISTP (APPEND (FOO A) (BAR B)))

    PRACTICE-FORMULATING-STRONG-RULES-2 -- rules suggested by (TRUE-LISTP (REV (FOO A)))

    PRACTICE-FORMULATING-STRONG-RULES-3 -- rules suggested by (MEMBER (FOO A) (APPEND (BAR B) (MUM C)))

    PRACTICE-FORMULATING-STRONG-RULES-4 -- rules suggested by (SUBSETP (APPEND (FOO A) (BAR B)) (MUM C))

    PRACTICE-FORMULATING-STRONG-RULES-5 -- rules suggested by (SUBSETP (FOO A) (APPEND (BAR B) (MUM C)))

    PRACTICE-FORMULATING-STRONG-RULES-6 -- rules suggested by (MEMBER (FOO A) (NATS-BELOW (BAR B)))

    PRINC$ -- print an atom

    PRINT-CONTROL -- advanced controls of ACL2 printing

    PRINT-DOC-START-COLUMN -- printing the one-liner

    PRINT-GV -- print a form whose evaluation caused a guard violation

    PRINT-OBJECT$ -- See io.

    PRINT-SUMMARY-USER -- See finalize-event-user.

    PRINTING-TO-STRINGS -- printing to strings instead of files or standard output

    PROFILE -- turn on profiling for one function

    PROG2$ -- execute two forms and return the value of the second one

    PROGN -- evaluate some events

    PROGN! -- evaluate some forms, not necessarily events

    PROGN$ -- execute a sequence of forms and return the value of the last one

    PROGRAM -- to set the default defun-mode to :program

    PROGRAMMING -- programming in ACL2

    PROGRAMMING-KNOWLEDGE-TAKEN-FOR-GRANTED -- background knowledge in ACL2 programming for theorem prover tutorial

    PROGRAMMING-WITH-STATE -- programming using the von Neumannesque ACL2 state object

    PROMPT -- the prompt printed by ld

    PROOF-CHECKER -- support for low-level interaction

    PROOF-CHECKER-COMMANDS -- list of commands for the proof-checker

    PROOF-OF-WELL-FOUNDEDNESS -- a proof that o< is well-founded on o-ps

    PROOF-SUPPORTERS-ALIST -- See dead-events.

    PROOF-TREE -- proof tree displays

    PROOF-TREE-DETAILS -- proof tree details not covered elsewhere

    PROOF-TREE-EXAMPLES -- proof tree example

    PROOFS-CO -- the proofs character output channel

    PROPER-CONSP -- recognizer for proper (null-terminated) non-empty lists

    PROPS -- print the ACL2 properties on a symbol

    PROVISIONAL-CERTIFICATION -- certify a book in stages for improved parallelism

    PSEUDO-TERMP -- a predicate for recognizing term-like s-expressions

    PSO -- show the most recently saved output

    PSO! -- show the most recently saved output, including proof-tree output

    PSOF -- show the most recently saved output

    PSOG -- show the most recently saved output in gag-mode

    PSTACK -- seeing what the prover is up to

    PUFF -- replace a compound command by its immediate subevents

    PUFF* -- replace a compound command by its subevents

    PUSH-UNTOUCHABLE -- add name or list of names to the list of untouchable symbols

    PUT-ASSOC -- modify an association list by associating a value with a key

    PUT-ASSOC-EQ -- See put-assoc.

    PUT-ASSOC-EQL -- See put-assoc.

    PUT-ASSOC-EQUAL -- See put-assoc.

    PUTPROP -- update fast property lists

    Pages Written Especially for the Tours -- Pages Written Especially for the Tours

    Perhaps -- Perhaps

    Popping out of an Inductive Proof -- Popping out of an Inductive Proof

    Proving Theorems about Models -- Proving Theorems about Models



    Q

    Q -- quit ACL2 (type :q) -- reenter with (lp)

    QUANTIFIER-TUTORIAL -- A Beginner's Guide to Reasoning about Quantification in ACL2

    QUANTIFIERS -- issues about quantification in ACL2

    QUANTIFIERS-USING-DEFUN-SK -- quantification example

    QUANTIFIERS-USING-DEFUN-SK-EXTENDED -- quantification example with details

    QUANTIFIERS-USING-RECURSION -- recursion for implementing quantification

    QUICK-AND-DIRTY-SUBSUMPTION-REPLACEMENT-STEP -- (advanced topic) controlling a heuristic in the prover's clausifier

    QUIT -- quit entirely out of Lisp

    QUOTE -- create a constant



    R

    R-EQLABLE-ALISTP -- recognizer for a true list of pairs whose cdrs are suitable for eql

    R-SYMBOL-ALISTP -- recognizer for association lists with symbols as values

    RANDOM$ -- obtain a random value

    RASSOC -- look up value in association list

    RASSOC-EQ -- See rassoc.

    RASSOC-EQUAL -- See rassoc.

    RATIONAL-LISTP -- recognizer for a true list of rational numbers

    RATIONALP -- recognizer for rational numbers (ratios and integers)

    READ-BYTE$ -- See io.

    READ-CHAR$ -- See io.

    READ-OBJECT -- See io.

    READ-RUN-TIME -- read elapsed runtime

    REAL -- ACL2(r) support for real numbers

    REAL-LISTP -- ACL2(r) recognizer for a true list of real numbers

    REAL/RATIONALP -- recognizer for rational numbers (including real number in ACL2(r))

    REALFIX -- coerce to a real number

    REALPART -- real part of a complex number

    REBUILD -- a convenient way to reconstruct your old state

    REDEF -- a common way to set ld-redefinition-action

    REDEF! -- a common way to set ld-redefinition-action

    REDEF+ -- system hacker's redefinition command

    REDEF- -- turn off system hacker's redefinition command

    REDEFINED-NAMES -- to collect the names that have been redefined

    REDEFINING-PROGRAMS -- an explanation of why we restrict redefinitions

    REDO-FLAT -- redo on failure of a progn, encapsulate, or certify-book

    REDUNDANT-ENCAPSULATE -- redundancy of encapsulate events

    REDUNDANT-EVENTS -- allowing a name to be introduced ``twice''

    REFINEMENT -- record that one equivalence relation refines another

    REGENERATE-TAU-DATABASE -- regenerate the tau database relative to the current enabled theory

    REGRESSION -- See books-certification.

    RELEASE-NOTES -- pointers to what has changed

    REM -- remainder using truncate

    REMOVE -- remove all occurrences

    REMOVE-BINOP -- remove the association of a function name with a macro name

    REMOVE-CUSTOM-KEYWORD-HINT -- remove a custom keyword hint

    REMOVE-DEFAULT-HINTS -- remove from the default hints

    REMOVE-DEFAULT-HINTS! -- remove from the default hints non-locally

    REMOVE-DIVE-INTO-MACRO -- removes association of proof-checker diving function with macro name

    REMOVE-DUPLICATES -- remove duplicates from a string or a list

    REMOVE-DUPLICATES-EQ -- See remove-duplicates.

    REMOVE-DUPLICATES-EQUAL -- See remove-duplicates.

    REMOVE-EQ -- See remove.

    REMOVE-EQUAL -- See remove.

    REMOVE-INVISIBLE-FNS -- make some unary functions no longer invisible

    REMOVE-MACRO-ALIAS -- remove the association of a function name with a macro name

    REMOVE-MACRO-FN -- remove the association of a function name with a macro name

    REMOVE-NTH-ALIAS -- remove a symbol alias for printing of nth/update-nth terms

    REMOVE-OVERRIDE-HINTS -- delete from the list of override-hints

    REMOVE-OVERRIDE-HINTS! -- delete non-locally from the list of override-hints

    REMOVE-RAW-ARITY -- remove arity information for raw mode

    REMOVE-UNTOUCHABLE -- remove names from lists of untouchable symbols

    REMOVE1 -- remove first occurrences, testing using eql

    REMOVE1-EQ -- See remove1.

    REMOVE1-EQUAL -- See remove1.

    REORDER -- hints keyword :REORDER

    RESET-FC-REPORTING -- reset the forward-chaining tracking state to its initial configuration

    RESET-KILL-RING -- save memory by resetting and perhaps resizing the kill ring used by oops

    RESET-LD-SPECIALS -- restores initial settings of the ld specials

    RESET-PREHISTORY -- reset the prehistory

    RESET-PRINT-CONTROL -- See print-control.

    RESIZE-LIST -- list resizer in support of stobjs

    REST -- rest (cdr) of the list

    RESTORE-MEMOIZATION-SETTINGS -- restore the saved memoization settings

    RESTRICT -- hints keyword :RESTRICT

    RETRIEVE -- re-enter a (specified) proof-checker state

    RETURN-LAST -- return the last argument, perhaps with side effects

    RETURN-LAST-TABLE -- install special raw Lisp behavior

    REVAPPEND -- concatentate the reverse of one list to another

    REVERSE -- reverse a list or string

    REWRITE -- make some :rewrite rules (possibly conditional ones)

    REWRITE-STACK-LIMIT -- limiting the stack depth of the ACL2 rewriter

    RFIX -- coerce to a rational number

    ROUND -- division returning an integer by rounding off

    RULE-CLASSES -- adding rules to the database

    RULE-NAMES -- How rules are named.

    RULER-EXTENDERS -- control for ACL2's termination and induction analyses

    RUNE -- a rule name

    Revisiting the Admission of App -- Revisiting the Admission of App

    Rewrite Rules are Generated from DEFTHM Events -- Rewrite Rules are Generated from DEFTHM Events

    Running Models -- Running Models



    S

    SAVE-AND-CLEAR-MEMOIZATION-SETTINGS -- save and remove the current memoization settings

    SAVE-EXEC -- save an executable image and a wrapper script

    SAVING-AND-RESTORING -- See save-exec.

    SEARCH -- search for a string or list in another string or list

    SECOND -- second member of the list

    SERIALIZE -- routines for saving ACL2 objects to files, and later restoring them

    SERIALIZE-ALTERNATIVES -- alternatives to the serialize routines

    SERIALIZE-IN-BOOKS -- using serialization efficiently in books

    SERIALIZE-READ -- read a serialized ACL2 object from a file

    SERIALIZE-WRITE -- write an ACL2 object into a file

    SET-ABSSTOBJ-DEBUG -- obtain debugging information upon atomicity violation for an abstract stobj

    SET-ACCUMULATED-PERSISTENCE -- See accumulated-persistence.

    SET-BACKCHAIN-LIMIT -- sets the backchain-limit used by the type-set and rewriting mechanisms

    SET-BODY -- set the definition body

    SET-BOGUS-DEFUN-HINTS-OK -- allow unnecessary ``mutual recursion''

    SET-BOGUS-MUTUAL-RECURSION-OK -- allow unnecessary ``mutual recursion''

    SET-CASE-SPLIT-LIMITATIONS -- set the case-split-limitations

    SET-CBD -- to set the connected book directory

    SET-CHECKPOINT-SUMMARY-LIMIT -- control printing of key checkpoints upon a proof's failure

    SET-COMPILE-FNS -- have each function compiled as you go along.

    SET-COMPILER-ENABLED -- See compilation.

    SET-DEBUGGER-ENABLE -- control whether Lisp errors and breaks invoke the Lisp debugger

    SET-DEFAULT-BACKCHAIN-LIMIT -- sets the default backchain-limit used when admitting a rule

    SET-DEFAULT-HINTS -- set the default hints

    SET-DEFAULT-HINTS! -- set the default hints non-locally

    SET-DEFERRED-TTAG-NOTES -- modify the verbosity of TTAG NOTE printing

    SET-DIFFERENCE$ -- elements of one list that are not elements of another

    SET-DIFFERENCE-EQ -- See set-difference$.

    SET-DIFFERENCE-EQUAL -- See set-difference$.

    SET-DIFFERENCE-THEORIES -- difference of two theories

    SET-ENFORCE-REDUNDANCY -- require most events to be redundant

    SET-EVISC-TUPLE -- control suppression of details when printing

    SET-FC-CRITERIA -- to set the tracking criteria for forward chaining reports

    SET-FC-REPORT-ON-THE-FLY -- to determine when forward-chaining reports are printed

    SET-FMT-HARD-RIGHT-MARGIN -- set the right margin for formatted output

    SET-FMT-SOFT-RIGHT-MARGIN -- set the soft right margin for formatted output

    SET-GAG-MODE -- modify the nature of proof output

    SET-GUARD-CHECKING -- control checking guards during execution of top-level forms

    SET-IGNORE-DOC-STRING-ERROR -- allow ill-formed documentation strings

    SET-IGNORE-OK -- allow unused formals and locals without an ignore or ignorable declaration

    SET-INHIBIT-OUTPUT-LST -- control output

    SET-INHIBIT-WARNINGS -- control warnings

    SET-INHIBIT-WARNINGS! -- control warnings non-locally

    SET-INHIBITED-SUMMARY-TYPES -- control which parts of the summary are printed

    SET-INVISIBLE-FNS-TABLE -- set the invisible functions table

    SET-IPRINT -- control whether abbreviated output can be read back in

    SET-IRRELEVANT-FORMALS-OK -- allow irrelevant formals in definitions

    SET-LD-KEYWORD-ALIASES -- See ld-keyword-aliases.

    SET-LD-KEYWORD-ALIASES! -- See ld-keyword-aliases.

    SET-LD-REDEFINITION-ACTION -- See ld-redefinition-action.

    SET-LD-SKIP-PROOFS -- See set-ld-skip-proofsp.

    SET-LD-SKIP-PROOFSP -- See ld-skip-proofsp.

    SET-LET*-ABSTRACTION -- See set-let*-abstractionp.

    SET-LET*-ABSTRACTIONP -- to shorten many prettyprinted clauses

    SET-MATCH-FREE-DEFAULT -- provide default for :match-free in future rules

    SET-MATCH-FREE-ERROR -- control error vs. warning when :match-free is missing

    SET-MEASURE-FUNCTION -- set the default measure function symbol

    SET-NON-LINEAR -- See set-non-linearp.

    SET-NON-LINEARP -- to turn on or off non-linear arithmetic reasoning

    SET-NU-REWRITER-MODE -- to turn on and off the nu-rewriter

    SET-OVERRIDE-HINTS -- set the override-hints

    SET-OVERRIDE-HINTS! -- set the override-hints non-locally

    SET-PARALLEL-EXECUTION -- for ACL2(p): enabling parallel execution for four parallelism primitives

    SET-PRINT-BASE -- control radix in which numbers are printed

    SET-PRINT-CASE -- control whether symbols are printed in upper case or in lower case

    SET-PRINT-CIRCLE -- See print-control.

    SET-PRINT-CLAUSE-IDS -- cause subgoal numbers to be printed when 'prove output is inhibited

    SET-PRINT-ESCAPE -- See print-control.

    SET-PRINT-LENGTH -- See print-control.

    SET-PRINT-LEVEL -- See print-control.

    SET-PRINT-LINES -- See print-control.

    SET-PRINT-RADIX -- control printing of the radix for numbers

    SET-PRINT-READABLY -- See print-control.

    SET-PRINT-RIGHT-MARGIN -- See print-control.

    SET-PROVER-STEP-LIMIT -- sets the step-limit used by the ACL2 prover

    SET-RAW-MODE -- enter or exit ``raw mode,'' a raw Lisp environment

    SET-RAW-MODE-ON! -- enter ``raw mode,'' a raw Lisp environment

    SET-RAW-PROOF-FORMAT -- print runes as lists in proof output from simplification

    SET-REWRITE-STACK-LIMIT -- Sets the rewrite stack depth used by the rewriter

    SET-RULER-EXTENDERS -- See ruler-extenders.

    SET-RW-CACHE-STATE -- set the default rw-cache-state

    SET-RW-CACHE-STATE! -- set the default rw-cache-state non-locally

    SET-SAVED-OUTPUT -- save proof output for later display with :pso or :pso!

    SET-SERIALIZE-CHARACTER -- See with-serialize-character.

    SET-SPLITTER-OUTPUT -- turn on or off reporting of rules that may have caused case splits

    SET-STATE-OK -- allow the use of STATE as a formal parameter

    SET-TAU-AUTO-MODE -- turn on or off automatic (``greedy'') generation of :tau-system rules

    SET-TOTAL-PARALLELISM-WORK-LIMIT -- for ACL2(p): set thread limit for parallelism primitives

    SET-TOTAL-PARALLELISM-WORK-LIMIT-ERROR -- for ACL2(p): control the action taken when the thread limit is exceeded

    SET-TRACE-EVISC-TUPLE -- set the trace evisc tuple

    SET-VERIFY-GUARDS-EAGERNESS -- the eagerness with which guard verification is tried.

    SET-WATERFALL-PARALLELISM -- for ACL2(p): configuring the parallel execution of the waterfall

    SET-WATERFALL-PARALLELISM-HACKS-ENABLED -- for ACL2(p): enable waterfall-parallelism hacks

    SET-WATERFALL-PARALLELISM-HACKS-ENABLED! -- for ACL2(p): enabling waterfall parallelism hacks

    SET-WATERFALL-PRINTING -- for ACL2(p): configuring the printing that occurs within the parallelized waterfall

    SET-WELL-FOUNDED-RELATION -- set the default well-founded relation

    SET-WORMHOLE-DATA -- sets the wormhole data object in a wormhole status object

    SET-WORMHOLE-ENTRY-CODE -- sets the wormhole entry code in a wormhole status object

    SET-WRITE-ACL2X -- cause certify-book to write out a .acl2x file

    SETENV$ -- set an environment variable

    SEVENTH -- seventh member of the list

    SHARP-BANG-READER -- package prefix that is not restricted to symbols

    SHARP-COMMA-READER -- DEPRECATED read-time evaluation of constants

    SHARP-DOT-READER -- read-time evaluation of constants

    SHARP-U-READER -- allow underscore characters in numbers

    SHOW-ACCUMULATED-PERSISTENCE -- See accumulated-persistence.

    SHOW-BDD -- inspect failed BDD proof attempts

    SHOW-BODIES -- show the potential definition bodies

    SHOW-CUSTOM-KEYWORD-HINT-EXPANSION -- print out custom keyword hints when they are expanded

    SHOW-FC-CRITERIA -- print the forward-chaining tracking criteria

    SIGNATURE -- how to specify the arity of a constrained function

    SIGNED-BYTE-P -- recognizer for signed integers that fit in a specified bit width

    SIGNUM -- indicator for positive, negative, or zero

    SIMPLE -- :definition and :rewrite rules used in preprocessing

    SINGLE-THREADED-OBJECTS -- See stobj.

    SIXTH -- sixth member of the list

    SKIP-PROOFS -- skip proofs for a given form -- a quick way to introduce unsoundness

    SLOW-ALIST-WARNING -- warnings issued when fast-alists are used inefficiently

    SLOW-ARRAY-WARNING -- a warning or error issued when arrays are used inefficiently

    SOLUTION-TO-SIMPLE-EXAMPLE -- solution to a simple example

    SPEC-MV-LET -- modification of mv-let supporting speculative and parallel execution

    SPECIAL-CASES-FOR-REWRITE-RULES -- convenient short forms for rewrite rule formulas

    SPECIFIC-KINDS-OF-FORMULAS-AS-REWRITE-RULES -- advice about how to handle commonly occurring formulas as rewrite rules

    SPECIOUS-SIMPLIFICATION -- nonproductive proof steps

    SPLITTER -- reporting of rules whose application may have caused case splits

    SPLITTER-OUTPUT -- status for reporting of splitter rules

    STANDARD-CHAR-LISTP -- recognizer for a true list of standard characters

    STANDARD-CHAR-P -- recognizer for standard characters

    STANDARD-CO -- the character output channel to which ld prints

    STANDARD-OI -- the standard object input ``channel''

    STANDARD-PART -- ACL2(r) function mapping limited numbers to standard numbers

    STANDARD-STRING-ALISTP -- recognizer for association lists with standard strings as keys

    STANDARDP -- ACL2(r) recognizer for standard objects

    START-PROOF-TREE -- start displaying proof trees during proofs

    STARTUP -- How to start using ACL2; the ACL2 command loop

    STATE -- the von Neumannesque ACL2 state object

    STATE-GLOBAL-LET* -- bind state global variables

    STOBJ -- single-threaded objects or ``von Neumann bottlenecks''

    STOBJ-EXAMPLE-1 -- an example of the use of single-threaded objects

    STOBJ-EXAMPLE-1-DEFUNS -- the defuns created by the counters stobj

    STOBJ-EXAMPLE-1-IMPLEMENTATION -- the implementation of the counters stobj

    STOBJ-EXAMPLE-1-PROOFS -- some proofs involving the counters stobj

    STOBJ-EXAMPLE-2 -- an example of the use of arrays in single-threaded objects

    STOBJ-EXAMPLE-3 -- another example of a single-threaded object

    STOBJ-LET -- See nested-stobjs.

    STOBJS -- xargs keyword :STOBJS

    STOP-PROOF-TREE -- stop displaying proof trees during proofs

    STRING -- coerce to a string

    STRING-APPEND -- concatenate two strings

    STRING-DOWNCASE -- in a given string, turn upper-case characters into lower-case

    STRING-EQUAL -- string equality without regard to case

    STRING-LISTP -- recognizer for a true list of strings

    STRING-UPCASE -- in a given string, turn lower-case characters into upper-case

    STRING< -- less-than test for strings

    STRING<= -- less-than-or-equal test for strings

    STRING> -- greater-than test for strings

    STRING>= -- less-than-or-equal test for strings

    STRINGP -- recognizer for strings

    STRIP-CARS -- collect up all first components of pairs in a list

    STRIP-CDRS -- collect up all second components of pairs in a list

    STRONG-REWRITE-RULES -- formulating good rewrite rules

    SUBLIS -- substitute an alist into a tree

    SUBSEQ -- subsequence of a string or list

    SUBSETP -- test if every member of one list is a member of the other

    SUBSETP-EQ -- See subsetp.

    SUBSETP-EQUAL -- See subsetp.

    SUBST -- a single substitution into a tree

    SUBSTITUTE -- substitute into a string or a list, using eql as test

    SUBVERSIVE-INDUCTIONS -- why we restrict encapsulated recursive functions

    SUBVERSIVE-RECURSIONS -- why we restrict encapsulated recursive functions

    SWITCHES-PARAMETERS-AND-MODES -- a variety of ways to modify the ACL2 environment

    SYMBOL-< -- less-than test for symbols

    SYMBOL-ALISTP -- recognizer for association lists with symbols as keys

    SYMBOL-LISTP -- recognizer for a true list of symbols

    SYMBOL-NAME -- the name of a symbol (a string)

    SYMBOL-PACKAGE-NAME -- the name of the package of a symbol (a string)

    SYMBOLP -- recognizer for symbols

    SYNTAX -- the syntax of ACL2 is that of Common Lisp

    SYNTAXP -- attach a heuristic filter on a rule

    SYNTAXP-EXAMPLES -- examples pertaining to syntaxp hypotheses

    SYS-CALL -- make a system call to the host operating system

    SYS-CALL+ -- make a system call to the host OS, returning status and output

    SYS-CALL-STATUS -- exit status from the preceding system call

    Subsumption of Induction Candidates in App Example -- Subsumption of Induction Candidates in App Example

    Suggested Inductions in the Associativity of App Example -- Suggested Inductions in the Associativity of App Example

    Symbolic Execution of Models -- Symbolic Execution of Models



    T

    TABLE -- user-managed tables

    TAKE -- initial segment of a list

    TAU-DATA -- to see what tau knows about a function symbol

    TAU-DATABASE -- to see the tau database as a (very large) object

    TAU-INTERVAL-DOM -- access the domain of a tau interval

    TAU-INTERVAL-HI -- access the upper bound of a tau interval

    TAU-INTERVAL-HI-REL -- access the upper bound relation of a tau interval

    TAU-INTERVAL-LO -- access the lower bound of a tau interval

    TAU-INTERVAL-LO-REL -- access the lower bound relation of a tau interval

    TAU-INTERVALP -- Boolean recognizer for tau intervals

    TAU-STATUS -- query or set tau system status

    TAU-SYSTEM -- make a rule for the ACL2 ``type checker''

    TENTH -- tenth member of the list

    TERM -- the three senses of well-formed ACL2 expressions or formulas

    TERM-ORDER -- the ordering relation on terms used by ACL2

    TERM-TABLE -- a table used to validate meta rules

    THE -- run-time type check

    THE-METHOD -- how to find proofs

    THEORIES -- sets of runes to enable/disable in concert

    THEORIES-AND-PRIMITIVES -- warnings from disabling certain built-in functions

    THEORY -- retrieve named theory

    THEORY-FUNCTIONS -- functions for obtaining or producing theories

    THEORY-INVARIANT -- user-specified invariants on theories

    THIRD -- third member of the list

    THM -- prove a theorem

    TIDBITS -- some basic hints for using ACL2

    TIME$ -- time an evaluation

    TIME-TRACKER -- display time spent during specified evaluation

    TIME-TRACKER-TAU -- messages about expensive use of the tau-system

    TIPS -- some hints for using the ACL2 prover

    TOGGLE-PC-MACRO -- change an ordinary macro command to an atomic macro, or vice-versa

    TOP-LEVEL -- evaluate a top-level form as a function body

    TRACE -- tracing functions in ACL2

    TRACE! -- trace the indicated functions after creating an active trust tag

    TRACE$ -- trace function evaluations

    TRANS -- print the macroexpansion of a form

    TRANS! -- print the macroexpansion of a form without single-threadedness concerns

    TRANS1 -- print the one-step macroexpansion of a form

    TRUE-LIST-LISTP -- recognizer for true (proper) lists of true lists

    TRUE-LISTP -- recognizer for proper (null-terminated) lists

    TRUNCATE -- division returning an integer by truncating toward 0

    TRUST-TAG -- See defttag.

    TTAGS-SEEN -- list some declared trust tags (ttags)

    TTREE -- tag-trees

    TUTORIAL1-TOWERS-OF-HANOI -- The Towers of Hanoi Example

    TUTORIAL2-EIGHTS-PROBLEM -- The Eights Problem Example

    TUTORIAL3-PHONEBOOK-EXAMPLE -- A Phonebook Specification

    TUTORIAL4-DEFUN-SK-EXAMPLE -- example of quantified notions

    TUTORIAL5-MISCELLANEOUS-EXAMPLES -- miscellaneous ACL2 examples

    TYPE -- See declare.

    TYPE-PRESCRIPTION -- make a rule that specifies the type of a term

    TYPE-SET -- how type information is encoded in ACL2

    TYPE-SET-INVERTER -- exhibit a new decoding for an ACL2 type-set

    TYPE-SPEC -- type specifiers in declarations

    TYPESPEC-CHECK -- See meta-extract.

    The Admission of App -- The Admission of App

    The Associativity of App -- The Associativity of App

    The Base Case in the App Example -- The Base Case in the App Example

    The End of the Flying Tour -- The End of the Flying Tour

    The End of the Proof of the Associativity of App -- The End of the Proof of the Associativity of App

    The End of the Walking Tour -- The End of the Walking Tour

    The Event Summary -- The Event Summary

    The Expansion of ENDP in the Induction Step (Step 0) -- The Expansion of ENDP in the Induction Step (Step 0)

    The Expansion of ENDP in the Induction Step (Step 1) -- The Expansion of ENDP in the Induction Step (Step 1)

    The Expansion of ENDP in the Induction Step (Step 2) -- The Expansion of ENDP in the Induction Step (Step 2)

    The Falling Body Model -- The Falling Body Model

    The Final Simplification in the Base Case (Step 0) -- the Final Simplification in the Base Case (Step 0)

    The Final Simplification in the Base Case (Step 1) -- the Final Simplification in the Base Case (Step 1)

    The Final Simplification in the Base Case (Step 2) -- the Final Simplification in the Base Case (Step 2)

    The Final Simplification in the Base Case (Step 3) -- the Final Simplification in the Base Case (Step 3)

    The First Application of the Associativity Rule -- The First Application of the Associativity Rule

    The Induction Scheme Selected for the App Example -- The Induction Scheme Selected for the App Example

    The Induction Step in the App Example -- The Induction Step in the App Example

    The Instantiation of the Induction Scheme -- The Instantiation of the Induction Scheme

    The Justification of the Induction Scheme -- The Justification of the Induction Scheme

    The Proof of the Associativity of App -- The Proof of the Associativity of App

    The Q.E.D. Message -- The Q.E.D. Message

    The Rules used in the Associativity of App Proof -- The Rules used in the Associativity of App Proof

    The Simplification of the Induction Conclusion (Step 0) -- the Simplification of the Induction Conclusion (Step 0)

    The Simplification of the Induction Conclusion (Step 1) -- the Simplification of the Induction Conclusion (Step 1)

    The Simplification of the Induction Conclusion (Step 10) -- the Simplification of the Induction Conclusion (Step 10)

    The Simplification of the Induction Conclusion (Step 11) -- the Simplification of the Induction Conclusion (Step 11)

    The Simplification of the Induction Conclusion (Step 12) -- the Simplification of the Induction Conclusion (Step 12)

    The Simplification of the Induction Conclusion (Step 2) -- the Simplification of the Induction Conclusion (Step 2)

    The Simplification of the Induction Conclusion (Step 3) -- the Simplification of the Induction Conclusion (Step 3)

    The Simplification of the Induction Conclusion (Step 4) -- the Simplification of the Induction Conclusion (Step 4)

    The Simplification of the Induction Conclusion (Step 5) -- the Simplification of the Induction Conclusion (Step 5)

    The Simplification of the Induction Conclusion (Step 6) -- the Simplification of the Induction Conclusion (Step 6)

    The Simplification of the Induction Conclusion (Step 7) -- the Simplification of the Induction Conclusion (Step 7)

    The Simplification of the Induction Conclusion (Step 8) -- the Simplification of the Induction Conclusion (Step 8)

    The Simplification of the Induction Conclusion (Step 9) -- the Simplification of the Induction Conclusion (Step 9)

    The Summary of the Proof of the Trivial Consequence -- The Summary of the Proof of the Trivial Consequence

    The Theorem that App is Associative -- The Theorem that App is Associative

    The Time Taken to do the Associativity of App Proof -- The Time Taken to do the Associativity of App Proof

    The Tours -- The Tours

    The WARNING about the Trivial Consequence -- The WARNING about the Trivial Consequence



    U

    U -- undo last command, without a query

    UBT -- undo the commands back through a command descriptor

    UBT! -- undo commands, without a query or an error

    UBT-PREHISTORY -- undo the commands back through the last reset-prehistory event

    UBU -- undo the commands back up to (not including) a command descriptor

    UBU! -- undo commands, without a query or an error

    UNARY-- -- arithmetic negation function

    UNARY-/ -- reciprocal function

    UNCERTIFIED-BOOKS -- invalid certificates and uncertified books

    UNION$ -- elements of one list that are not elements of another

    UNION-EQ -- See union$.

    UNION-EQUAL -- See union$.

    UNION-THEORIES -- union two theories

    UNIVERSAL-THEORY -- all rules as of logical name

    UNMEMOIZE -- turn off memoization for the specified function

    UNMONITOR -- to stop monitoring a rule name

    UNSAVE -- remove a proof-checker state

    UNSIGNED-BYTE-P -- recognizer for natural numbers that fit in a specified bit width

    UNSUPPORTED-PARALLELISM-FEATURES -- ACL2 features not supported in ACL2(p)

    UNSUPPORTED-WATERFALL-PARALLELISM-FEATURES -- proof features not supported with waterfall-parallelism enabled

    UNTRACE$ -- untrace functions

    UNTRANS-TABLE -- associates a function symbol with a macro for printing user-level terms

    UNTRANSLATE -- See user-defined-functions-table.

    UPDATE-NTH -- modify a list by putting the given value at the given position

    UPPER-CASE-P -- recognizer for upper case characters

    USE -- hints keyword :USE

    USER-DEFINED-FUNCTIONS-TABLE -- an advanced table used to replace certain system functions

    USING-COMPUTED-HINTS -- how to use computed hints

    USING-COMPUTED-HINTS-1 -- Driving Home the Basics

    USING-COMPUTED-HINTS-2 -- One Hint to Every Top-Level Goal in a Forcing Round

    USING-COMPUTED-HINTS-3 -- Hints as a Function of the Goal (not its Name)

    USING-COMPUTED-HINTS-4 -- Computing the Hints

    USING-COMPUTED-HINTS-5 -- Debugging Computed Hints

    USING-COMPUTED-HINTS-6 -- Using the computed-hint-replacement feature

    USING-COMPUTED-HINTS-7 -- Using the stable-under-simplificationp flag

    USING-COMPUTED-HINTS-8 -- Some Final Comments

    USING-ENABLED-RULES -- avoiding :use hints for enabled :rewrite rules

    USING-TABLES-EFFICIENTLY -- Notes on how to use tables efficiently

    Undocumented Topic -- Undocumented Topic

    Using the Associativity of App to Prove a Trivial Consequence -- Using the Associativity of App to Prove a Trivial Consequence



    V

    VALUE-TRIPLE -- compute a value, optionally checking that it is not nil

    VERBOSE-PSTACK -- seeing what the prover is up to (for advanced users)

    VERIFY -- enter the interactive proof checker

    VERIFY-GUARDS -- verify the guards of a function

    VERIFY-GUARDS+ -- verify the guards of a function

    VERIFY-GUARDS-EAGERNESS -- See set-verify-guards-eagerness.

    VERIFY-GUARDS-FORMULA -- view the guard proof obligation, without proving it

    VERIFY-TERMINATION -- convert a function from :program mode to :logic mode

    VERSION -- ACL2 Version Number



    W

    WALKABOUT -- explore an ACL2 cons tree

    WATERFALL -- See hints-and-the-waterfall.

    WATERFALL-PARALLELISM -- for ACL2(p): configuring the parallel execution of the waterfall

    WATERFALL-PARALLELISM-FOR-BOOK-CERTIFICATION -- for ACL2(p): using waterfall parallelism during book certification

    WATERFALL-PRINTING -- for ACL2(p): configuring the printing within the parallelized waterfall

    WELL-FOUNDED-RELATION -- show that a relation is well-founded on a set

    WET -- evaluate a form and print subsequent error trace

    WHY-BRR -- an explanation of why ACL2 has an explicit brr mode

    WITH-FAST-ALIST -- (with-fast-alist name form) causes name to be a fast alist for the
    execution of form.


    WITH-GUARD-CHECKING -- suppressing or enable guard-checking for a form

    WITH-LIVE-STATE -- allow a reference to state in raw Lisp

    WITH-LOCAL-STATE -- locally bind state

    WITH-LOCAL-STOBJ -- locally bind a single-threaded object

    WITH-OUTPUT -- suppressing or turning on specified output for an event

    WITH-OUTPUT-LOCK -- provides a mutual-exclusion mechanism for performing output in parallel

    WITH-PROVER-STEP-LIMIT -- limit the number of steps for proofs

    WITH-PROVER-TIME-LIMIT -- limit the time for proofs

    WITH-SERIALIZE-CHARACTER -- control output mode for print-object$

    WITH-STOLEN-ALIST -- (with-stolen-alist name form) ensures that name is a fast alist at the
    start of the execution of form. At the end of execution, it ensures that
    name is a fast alist if and only if it was originally. That is, if
    name was not a fast alist originally, its hash table link is freed, and if
    it was a fast alist originally but its table was modified during the execution
    of form, that table is restored. Note that any extended table created from
    the original fast alist during form must be manually freed.


    WITHOUT-EVISC -- print output in full

    WOF -- direct standard output and proofs output to a file

    WORLD -- ACL2 property lists and the ACL2 logical database

    WORMHOLE -- ld without state -- a short-cut to a parallel universe

    WORMHOLE-DATA -- determines the wormhole data object from a wormhole status object

    WORMHOLE-ENTRY-CODE -- determines the wormhole entry code from a wormhole status object

    WORMHOLE-EVAL -- state-saving without state -- a short-cut to a parallel universe

    WORMHOLE-IMPLEMENTATION -- notes on how wormholes are implemented

    WORMHOLE-P -- predicate to determine if you are inside a wormhole

    WORMHOLE-STATUSP -- predicate recognizing well-formed wormhole status object

    WRITE-BYTE$ -- See io.

    What Is ACL2(Q) -- What Is ACL2?

    What is Required of the User(Q) -- What is Required of the User?

    What is a Mathematical Logic(Q) -- What is a Mathematical Logic?

    What is a Mechanical Theorem Prover(Q) -- What is a Mechanical Theorem Prover?

    What is a Mechanical Theorem Prover(Q) (cont) -- What is a Mechanical Theorem Prover? (cont)



    X

    XARGS -- extra arguments, for example to give hints to defun

    XOR -- logical ``exclusive or''



    Y

    You Must Think about the Use of a Formula as a Rule -- You Must Think about the Use of a Formula as a Rule



    Z

    ZERO-TEST-IDIOMS -- how to test for 0

    ZEROP -- test an acl2-number against 0

    ZIP -- testing an ``integer'' against 0

    ZP -- testing a ``natural'' against 0

    ZPF -- testing a nonnegative fixnum against 0



    ACL2-PC::

    ACL2-PC::= -- (atomic macro)
    attempt an equality (or equivalence) substitution


    ACL2-PC::ACL2-WRAP -- (macro)
    same as (lisp x)


    ACL2-PC::ADD-ABBREVIATION -- (primitive)
    add an abbreviation


    ACL2-PC::AL -- (macro)
    same as apply-linear


    ACL2-PC::APPLY-LINEAR -- (primitive)
    apply a linear rule


    ACL2-PC::BASH -- (atomic macro)
    call the ACL2 theorem prover's simplifier


    ACL2-PC::BDD -- (atomic macro)
    prove the current goal using bdds


    ACL2-PC::BK -- (atomic macro)
    move backward one argument in the enclosing term


    ACL2-PC::BOOKMARK -- (macro)
    insert matching ``bookends'' comments


    ACL2-PC::CASESPLIT -- (primitive)
    split into two cases


    ACL2-PC::CG -- (macro)
    change to another goal.


    ACL2-PC::CHANGE-GOAL -- (primitive)
    change to another goal.


    ACL2-PC::CL-PROC -- (macro)
    same as clause-processor


    ACL2-PC::CLAIM -- (atomic macro)
    add a new hypothesis


    ACL2-PC::CLAUSE-PROCESSOR -- (atomic macro)
    use a clause-processor


    ACL2-PC::COMM -- (macro)
    display instructions from the current interactive session


    ACL2-PC::COMMANDS -- (macro)
    display instructions from the current interactive session


    ACL2-PC::COMMENT -- (primitive)
    insert a comment


    ACL2-PC::CONTRADICT -- (macro)
    same as contrapose


    ACL2-PC::CONTRAPOSE -- (primitive)
    switch a hypothesis with the conclusion, negating both


    ACL2-PC::DEMOTE -- (primitive)
    move top-level hypotheses to the conclusion


    ACL2-PC::DIVE -- (primitive)
    move to the indicated subterm


    ACL2-PC::DO-ALL -- (macro)
    run the given instructions


    ACL2-PC::DO-ALL-NO-PROMPT -- (macro)
    run the given instructions, halting once there is a ``failure''


    ACL2-PC::DO-STRICT -- (macro)
    run the given instructions, halting once there is a ``failure''


    ACL2-PC::DROP -- (primitive)
    drop top-level hypotheses


    ACL2-PC::DV -- (atomic macro)
    move to the indicated subterm


    ACL2-PC::ELIM -- (atomic macro)
    call the ACL2 theorem prover's elimination process


    ACL2-PC::EQUIV -- (primitive)
    attempt an equality (or congruence-based) substitution


    ACL2-PC::EX -- (macro)
    exit after possibly saving the state


    ACL2-PC::EXIT -- (meta)
    exit the interactive proof-checker


    ACL2-PC::EXPAND -- (primitive)
    expand the current function call without simplification


    ACL2-PC::FAIL -- (macro)
    cause a failure


    ACL2-PC::FINISH -- (macro)
    require completion of instructions; save error if inside :hints


    ACL2-PC::FORWARDCHAIN -- (atomic macro)
    forward chain from an implication in the hyps


    ACL2-PC::FREE -- (atomic macro)
    create a ``free variable''


    ACL2-PC::GENEQV -- (macro)
    show the generated equivalence relation maintained at the current subterm


    ACL2-PC::GENERALIZE -- (primitive)
    perform a generalization


    ACL2-PC::GOALS -- (macro)
    list the names of goals on the stack


    ACL2-PC::HELP -- (macro)
    proof-checker help facility


    ACL2-PC::HELP! -- (macro)
    proof-checker help facility


    ACL2-PC::HELP-LONG -- (macro)
    same as help!


    ACL2-PC::HYPS -- (macro)
    print the hypotheses


    ACL2-PC::ILLEGAL -- (macro)
    illegal instruction


    ACL2-PC::IN-THEORY -- (primitive)
    set the current proof-checker theory


    ACL2-PC::INDUCT -- (atomic macro)
    generate subgoals using induction


    ACL2-PC::LEMMAS-USED -- (macro)
    print the runes (definitions, lemmas, ...) used


    ACL2-PC::LISP -- (meta)
    evaluate the given form in Lisp


    ACL2-PC::MORE -- (macro)
    proof-checker help facility


    ACL2-PC::MORE! -- (macro)
    proof-checker help facility


    ACL2-PC::NEGATE -- (macro)
    run the given instructions, and ``succeed'' if and only if they ``fail''


    ACL2-PC::NIL -- (macro)
    used for interpreting control-d


    ACL2-PC::NOISE -- (meta)
    run instructions with output


    ACL2-PC::NX -- (atomic macro)
    move forward one argument in the enclosing term


    ACL2-PC::ORELSE -- (macro)
    run the first instruction; if (and only if) it ``fails'', run the
    second


    ACL2-PC::P -- (macro)
    prettyprint the current term


    ACL2-PC::P-TOP -- (macro)
    prettyprint the conclusion, highlighting the current term


    ACL2-PC::PL -- (macro)
    print the rules for a given name


    ACL2-PC::PP -- (macro)
    prettyprint the current term


    ACL2-PC::PR -- (macro)
    print the rules for a given name


    ACL2-PC::PRINT -- (macro)
    print the result of evaluating the given form


    ACL2-PC::PRINT-ALL-CONCS -- (macro)
    print all the conclusions of (as yet unproved) goals


    ACL2-PC::PRINT-ALL-GOALS -- (macro)
    print all the (as yet unproved) goals


    ACL2-PC::PRINT-MAIN -- (macro)
    print the original goal


    ACL2-PC::PRO -- (atomic macro)
    repeatedly apply promote


    ACL2-PC::PROMOTE -- (primitive)
    move antecedents of conclusion's implies term to top-level
    hypotheses


    ACL2-PC::PROTECT -- (macro)
    run the given instructions, reverting to existing state upon
    failure


    ACL2-PC::PROVE -- (primitive)
    call the ACL2 theorem prover to prove the current goal


    ACL2-PC::PSO -- (macro)
    print the most recent proof attempt from inside the proof-checker


    ACL2-PC::PSO! -- (macro)
    print the most recent proof attempt from inside the proof-checker


    ACL2-PC::PSOG -- (macro)
    print the most recent proof attempt from inside the proof-checker


    ACL2-PC::PUT -- (macro)
    substitute for a ``free variable''


    ACL2-PC::QUIET -- (meta)
    run instructions without output


    ACL2-PC::R -- (macro)
    same as rewrite


    ACL2-PC::REDUCE -- (atomic macro)
    call the ACL2 theorem prover's simplifier


    ACL2-PC::REDUCE-BY-INDUCTION -- (macro)
    call the ACL2 prover without induction, after going into
    induction


    ACL2-PC::REMOVE-ABBREVIATIONS -- (primitive)
    remove one or more abbreviations


    ACL2-PC::REPEAT -- (macro)
    repeat the given instruction until it ``fails''


    ACL2-PC::REPEAT-REC -- (macro)
    auxiliary to repeat


    ACL2-PC::REPLAY -- (macro)
    replay one or more instructions


    ACL2-PC::RESTORE -- (meta)
    remove the effect of an UNDO command


    ACL2-PC::RETAIN -- (atomic macro)
    drop all but the indicated top-level hypotheses


    ACL2-PC::RETRIEVE -- (macro)
    re-enter the proof-checker


    ACL2-PC::REWRITE -- (primitive)
    apply a rewrite rule


    ACL2-PC::RUN-INSTR-ON-GOAL -- (macro)
    auxiliary to THEN


    ACL2-PC::RUN-INSTR-ON-NEW-GOALS -- (macro)
    auxiliary to then


    ACL2-PC::RUNES -- (macro)
    print the runes (definitions, lemmas, ...) used


    ACL2-PC::S -- (primitive)
    simplify the current subterm


    ACL2-PC::S-PROP -- (atomic macro)
    simplify propositionally


    ACL2-PC::SAVE -- (macro)
    save the proof-checker state (state-stack)


    ACL2-PC::SEQUENCE -- (meta)
    run the given list of instructions according to a multitude of
    options


    ACL2-PC::SHOW-ABBREVIATIONS -- (macro)
    display the current abbreviations


    ACL2-PC::SHOW-LINEARS -- (macro)
    display the applicable linear rules


    ACL2-PC::SHOW-REWRITES -- (macro)
    display the applicable rewrite rules


    ACL2-PC::SHOW-TYPE-PRESCRIPTIONS -- (macro)
    display the applicable type-prescription rules


    ACL2-PC::SKIP -- (macro)
    ``succeed'' without doing anything


    ACL2-PC::SL -- (atomic macro)
    simplify with lemmas


    ACL2-PC::SLS -- (macro)
    same as SHOW-LINEARS


    ACL2-PC::SPLIT -- (atomic macro)
    split the current goal into cases


    ACL2-PC::SR -- (macro)
    same as SHOW-REWRITES


    ACL2-PC::ST -- (macro)
    same as SHOW-TYPE-PRESCRIPTIONS


    ACL2-PC::SUCCEED -- (macro)
    run the given instructions, and ``succeed''


    ACL2-PC::TH -- (macro)
    print the top-level hypotheses and the current subterm


    ACL2-PC::THEN -- (macro)
    apply one instruction to current goal and another to new subgoals


    ACL2-PC::TOP -- (atomic macro)
    move to the top of the goal


    ACL2-PC::TYPE-ALIST -- (macro)
    display the type-alist from the current context


    ACL2-PC::UNDO -- (meta)
    undo some instructions


    ACL2-PC::UNSAVE -- (macro)
    remove a proof-checker state


    ACL2-PC::UP -- (primitive)
    move to the parent (or some ancestor) of the current subterm


    ACL2-PC::USE -- (atomic macro)
    use a lemma instance


    ACL2-PC::WRAP -- (atomic macro)
    execute the indicated instructions and combine all the new goals


    ACL2-PC::WRAP-INDUCT -- (atomic macro)
    same as induct, but create a single goal


    ACL2-PC::WRAP1 -- (primitive)
    combine goals into a single goal


    ACL2-PC::X -- (atomic macro)
    expand and (maybe) simplify function call at the current subterm


    ACL2-PC::X-DUMB -- (atomic macro)
    expand function call at the current subterm, without simplifying





    acl2-sources/doc/HTML/acl2-doc-major-topics.html0000664002132200015000000000672612222333514020753 0ustar kaufmannacl2 ACL2 Version 6.3

    Documentation for ACL2 Version 6.3

    The ACL2 Documentation is divided into the following Major Topics




    acl2-sources/doc/HTML/acl2-doc.html0000664002132200015000000003566312222333514016350 0ustar kaufmannacl2 ACL2 Version 6.3
    ACL2

    ACL2 Version 6.3

    ACL2 is both a programming language in which you can model computer systems and a tool to help you prove properties of those models.

    ACL2 is part of the Boyer-Moore family of provers, for which its authors have received the 2005 ACM Software System Award.

    SEARCH


    Start Here: Applications, Tours, and Tutorials/Demos ACL2 Workshops, UT Seminar, and Course Materials
    Publications about ACL2 and Its Applications The User's Manual and Hyper-Card
    Community Books: Lemma Libraries and Utilities Mailing Lists
    Recent changes to this page Obtaining, Installing, and License
    Differences from Version 6.2 Other Releases
    How to contribute libraries and documentation

    Matt Kaufmann and J Strother Moore
    University of Texas at Austin
    September 30, 2013


    Libraries of books (files containing definitions and theorems) extend the code that we have written. These community books are contributed and maintained by the members of the ACL2 community; see the acl2-books project page.

    The libraries and the ACL2 source code are under revision control, using svn. Experimental copies of ACL2 and the libraries are thus available between ACL2 releases. The authors of ACL2 consider svn distributions to be experimental; while they will likely be fully functional in most cases, they could however be incomplete, fragile, and unable to pass our own regression. If you decide to use svn versions of either the libraries or ACL2, then you should use both, as they tend to be kept in sync. See the project websites, acl2-books and acl2-devel, for the ACL2 libraries and development sources, respectively.

    A combined manual, known as the xdoc manual, incorporates not only The User's Manual for ACL2 (with some topics rearranged) but also documentation for many books. Thanks to Jared Davis for building the xdoc processor for creating this view of the documentation.

    The ACL2 distribution includes the following extensions, which were contributed by the individuals shown.

    • ACL2(r)
      Support for the real numbers by way of non-standard analysis
      Ruben Gamboa
    • ACL2(h)
      Support for hash cons, applicative hash tables, and function memoization for reuse of previously computed results
      Bob Boyer, Warren A. Hunt, Jr., Jared Davis, and Sol Swords
    • ACL2(p)
      Support for parallel evaluation
      David L. Rager

    Another extension of ACL2 is the Eclipse-based ACL2 Sedan (ACL2s). Unlike the systems above, ACL2s is distributed and maintained by Pete Manolios and his research group. ACL2s comes with a standard executable ACL2 image for Windows, but it also comes with pre-certified community books and an extension of ACL2 with additional features, including extra automation for termination proofs as well as counterexample generation.



    We gratefully acknowledge substantial support from the following. (These are included in a much more complete acknowledgments page.)
    • DARPA
    • National Science Foundation
      • This material is based upon work supported by the National Science Foundation under Grant Nos. EIA-0303609, CNS-0429591, ISS-0417413, CCF-0945316, and CNS-0910913.
      • Any opinions, findings and conclusions or recomendations expressed in this material are those of the authors and do not necessarily reflect the views of the National Science Foundation.
    • Advanced Micro Devices, Inc.
    • ForrestHunt, Inc.
    • Rockwell Collins, Inc.
    • Sun Microsystems, Inc.

    The User's Manual

    ACL2's user manual is a vast hypertext document. You can wander through it here, in its HTML format.

    Here are the two common entries to the documentation graph:
    Major Topics (Table of Contents)
    Index of all documented topics
    The tiny warning signs, , indicate that the links lead out of introductory level material and into reference manual material. Once in the reference manual, virtually all links are into the manual and none of them is marked with the warning sign! It is easy for the newcomer to get lost.

    Here is how we recommend you use this documentation.

    If you are a newcomer to ACL2, we do not recommend that you wander off into the full documentation. Instead start with the ACL2-TUTORIAL documentation topic. Experienced users tend mostly to use the ``Index'' to look up concepts mentioned in error messages or vaguely remembered from their past experiences with ACL2.

    Note: The documentation is available for reading in a Web browser (recommended), in Emacs Info, using the ACL2 :DOC command, or as a printed book (over 2200 pages). These are available as follows.

    • For web browsing using an HTML version of our documentation, click on Major Topics (here, or above). Better yet, view a local copy, which you can find under your local acl2-sources/ diretory at doc/HTML/acl2-doc.html.
    • Alternatively, for web browsing you can use documentation generated by Jared Davis's xdoc utility. You can build a local copy from the ACL2 Community Books, following instructions in books/Makefile.
    • Those familiar with Emacs Info can read the documentation in that format by loading the file emacs/emacs-acl2.el distributed with ACL2 (under the acl2-sources directory), and then evaluating meta-x acl2-info. Alternatively, within Emacs Info press g and then enter the following path (of course, replacing PATH_TO_ACL2-SOURCES by the path of your acl2-sources directory):
      (PATH_TO_ACL2-SOURCES/doc/EMACS/acl2-doc-emacs.info)top.
    • To read a printed copy, obtain a Postscript version here (about 2.6 MB).



    Community Books: Lemma Libraries and Utilities, and How to Contribute

    A companion to ACL2 is the library of community books, which have been developed by many users over the years. These books contain definitions and theorems that you might find useful in your models and proofs. In addition, some books contain ACL2 reasoning or analysis tools built by users. The installation instructions explain how to download and install the community books.

    We strongly encourage users to submit additional books by following the instructions at the acl2-books project page hosted at http://acl2-books.googlecode.com.

    We also distribute a few interface tools, such as support for infix printing. For these, see the Utilities section of Books and Papers about ACL2 and Its Applications. Some of the papers mentioned in that collection contain utilities, scripts, or ACL2 books for the problem domains in question.



    How to contribute libraries and documentation

    As mentioned above, we strongly encourage users to submit additional books by following the instructions at the acl2-books project page hosted at http://acl2-books.googlecode.com.

    Also, if you have written up (or are interested in writing) text that may be helpful to other ACL2 users, we invite you to contribute it to the community. Such user documentation may be in any format that is readable by web browsers (for example html, pdf, and plain text). User-contributed documentation can link back to the ACL2 documentation proper, by using links such as:
    http://www.cs.utexas.edu/users/moore/acl2/current/MAKE-EVENT.html
    (In general, substitute the upper-case of the documentation topic for "MAKE-EVENT" in the example above.)

    To contribute user documentation, send email to the ACL2 developers, Matt Kaufmann and J Strother Moore.

    Searching documentation and books

    The links below may help you to search the ACL2 documentation and the ACL2 community books, respectively. Our approach is low-tech, but intended to be reliable and easy to use on any platform. You might want to add a bookmark for each of these.
    • The following link will take you to a search box on a Google page, which has the following contents.
      search_term site:http://www.cs.utexas.edu/users/moore/acl2/v6-3
      
      Now simply replace the word `search_term' with your topic. For example, replace `search_term' by `tail recursion' to get documentation about tail recursion.
      tail recursion site:http://www.cs.utexas.edu/users/moore/acl2/v6-3
      
      Now you are ready to follow the link.

      SEARCH THE DOCUMENTATION

    • The next link will take you to the community books website (which is external to the present ACL2 website), specifically to the section entitled ``Searching and browsing the books.'' There, you will see a search box in which you can enter your search term. For example, if you type `tail recursion' and then <RETURN>, you will see text from several books in the svn trunk that deal with the topic of tail recursion, with an accompanying ``File Path'' shown at the end of each book's text.

      SEARCH THE COMMUNITY BOOKS

























































    acl2-sources/doc/HTML/new.html0000664002132200015000000000365212222333514015546 0ustar kaufmannacl2 ACL2 Version 6.3 News

    ACL2 Version 6.3 News

    Table of Contents

    ACL2 sources availability between releases

    ACL2 sources are now available between releases, as described in
    this announcement.

    VSTTE 2012 Competition

    A team of four ACL2 users entered the VSTTE 2012 competition. For information, including the team's solution, visit this
    link.

    ACL2 Books Repository

    The acl2-books Google group allows you to contribute ACL2 books (input files), and also to update to the latest version if you don't want to wait for the next ACL2 release. Quoting from that web site:
    This site is for community-driven development for the basic ACL2 libraries, which deal with topics like arithmetic, data structures, and hardware modelling. We're working with the authors of ACL2 and our changes are eventually incorporated into official ACL2 releases.

    Performance Comparisons

    To appear































































    acl2-sources/doc/HTML/other-releases.html0000664002132200015000000001027012222333514017671 0ustar kaufmannacl2 ACL2 Releases

    ACL2 Releases

    The current ACL2 release on the ACL2 home page is Version 6.3 (October, 2013).

    This page contains:

    • Incremental releases since Version 6.3 (if any)

    • Past releases


    Incremental releases since Version 6.3:

    None

    Past releases:









    acl2-sources/doc/HTML/workshops.html0000664002132200015000000001557512222333514017023 0ustar kaufmannacl2 ACL2 Workshops and UT ACL2 Seminar

    The ACL2 Workshop Series

    We hold regular workshops. In 2010, the ACL2 Workshop did a one-time merger with TPHOLs to form the first International Conference on Interactive Theorem Proving (ITP). Such a merger may occur again, but whether or not that happens, the ACL2 community is encouraged to participate in ITP. Other conferences of particular interest to the ACL2 community include CAV (Computer Aided Verification) and FMCAD (Formal Methods in Computer Aided Design).

    Here is a list of past ACL2 Workshop slogans.

    • 1999: It ain't over til the last Q.E.D.
    • 2000: Just prove it.
    • 2002: Accumulated Persistence
    • 2003: No software too trivial. No error too obscure.
    • 2004: Defun starts here
    • 2006: ACM Software Systems Award Winner!
    • 2007: Save the world. Use make-event.
    • 2009: None
    • 2011: We aim to prove
    • 2013: Pain is temporary; theorems are forever.

    Jared Davis has graciously supplied a listing of bibtex entries for the 2000 through 2004 ACL2 workshops..

    ACL2 input files (certifiable books) from the preceding workshops are available from the links above. WARNING: The above links point to the original versions of those books. In order to obtain up-to-date versions of those books that will certify in the latest version of ACL2, you should download workshops.tar.gz to the acl2-sources/books/ subdirectory of your ACL2 distribution, and then gunzip and extract it. On a Unix/Linux system you can then certify all the books by standing in the acl2-sources/ directory and issuing the command make regression.

    ACL2 Seminar at UT

    An ACL2 seminar meets regularly at the University of Texas. A list of past talks, generally accompanied by abstracts and sometimes slides, may be found on the UT ACL2 seminar page.

    ACL2 Course Materials

    The links listed below will take you to materials for some courses that involve ACL2. This list is loosely maintained and incomplete, and is given in no particular order. We strongly encourage you to send email to Matt Kaufmann and J Strother Moore if you have additional such links to contribute.







    acl2-sources/doc/HTML/acl2-logo-200-134.gif0000644002132200015000000002213712023157775017152 0ustar kaufmannacl2GIF89ackt j qqlyrly!"m"t#y*s,y,2u3{35:;tB|JS]<B;|B; 7w C ;} < C| C D I R `>E>|KL[@@QVkTA}AJPYcDI\\a"H}"T"^"b#F#G#Q#\#d&i(u*f+L+i+j-R-\-l1m1{2[2q4s4t5k8v:;];zD8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc.C, all rights reserved.sf32 B&l, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sDOwq6mڲeӬyC7Χ;GL>uJekO3ϦfB] S:kkA] ңE1#!ql P!H=#0c&.gˢj9o^He~} / xC=Ӑ.,jfycmB&jAP)P0Bl-.dl*e^}!(jf0ˆ!N\K-Kʲf%Q[g BRt A#ӑź /ZBm6g ^XpBؠ]@;8ز 0 [ʲhD"']UϫuQZpŤZP BuB{^':˺_M3,/n<PPч>B؏DO2t|-̢K0EA*4}]qRD ^Ă`#G%a4#ALR>MzGa e(C*,BvBd,rZE(=Rf0 eC0bf񎈨L%Lf.ÖDV8Xw4 5BpY_2XWs! wD^씈=% [؂< `S4>QDB4; BU `Dz^T4h+A ~CɗІCP6%\@P<-zb4O ]X;*ҏyA4 QTM,rV 2AΠCp[%(+M+Z5N]^*,}fЁdIp,XF4ei:))hmt/!lG: Mh!km@-me ٨b?s0ʛ9Cy3Y>QxC08эnl#o6Q_ppG<6tshLp cLcu=G<lB_p"@`  Ft&a}dkJY򠍢/<4|A 6Hlt-@d X Cnk[&k@0P;Q` 012w@E'\Aot(:GpA`] J|]9o}*$ZS¸s5\bs8CR@a%wz޳ @LꚺD8u>P dT Z t]6 IA8@g;,] ~sEB, UF;ڊܱ": B8mY~o[\ҏv@n`j[,LpZ& TH >"o8 @- ؛MloHn{*0ېGkJ )ԙ(_?\Є.i%,(xSd!@'N1+;˛|!辯硨1xA D(g_rrA R¡kuq[0` M8z"#yn*w-OaJm!Taj  ʾ#s}; Oym[Q;s{F7p0W~B{-`d/yMnSE%L}0 ۳ 37 A'6ݐ |$0rW}L7y'b=}?@sy6~8wKQtW`1JmMa~^bLmT}02c ` 0 S 0 e?#w6WdpQ~,85BM@ IPp0yW7]hĂdEdWڱS %!`NhfHu5K&R 9a@vU$BLe@5^Y%#}&( S0ubFYՂԇsUB1  !*@ 5PB P#!H@v`P/סJGUWnW %犰8nWЎv(~ㅋZD?#@$`&LhRB du$p{؍[w#2s&jY]Epfu5~:SZJB$!(ICK p& @ )(yX *Mlg qCp'n xYCȒ!9* NȲ נ:+ P!Q٘uH!DS% vAOVZ@ p ΐ _p 6!PUwSV[/ؖ29Z$J `I =y /3bru9LWz KWe6 0 Cfd 0 ` @49UО\B>dW19O2 H<겓B P p v Ki9xn]w ٠ep.%8dr ̀ p\`f>t'/`G( J C 4a &!bs:UU ZeN ~A2 \P / t~ds0~*bQ1 P N lj!r@>T~ ݠ9s* X,hT`p j @ @ J @ Af| PJ>{݁ pZ CCi BY| BڐY5 밠}D jWH LzPP p!Ar*0V@ٯ,"/ i @ò#MH@A0P @$z.lR&ီ&yA qLu @ɀ& x`D# @ dKp>+/C@ &e g~j   *#pN+C,JB 0l _   P Эgkq'@@ -#´Q `PNhpVP ΰ ߐ p7jq 5kea_P  łD `$P̐Q0k g "Wwp @”pGpЭ P *!`atq @ s `\-jNP`@N`ݐP `1 `Bw !H-= 1 ]P q'KgT,0 ǭ @^ -`8$>P `78 Aـ%k#` /dtF=j` 9) 68p H`-8wp$0+q`݀pF\г#Аq p-A?PF (#qFg   V0# !g pp E; )m Z0&iP 簝ܹ6=!@F@VFǡ=G!@ _+  !=$ PAK Ep{ ^mP A3? f+P tAN Fc>l@ 7@7P_@HGD-|@ h 00MF"`p q  }]^P& t Ԗ   L-E[G #M EC$EH: `  ^N mՁ  ޱ[@ n@}  A]F D-=H8~-@ plTɐWO܄OL~N ^mg Eu 0T}j"zSz1rpw 0 TMrϴ8r` ҍ$q0 z D MM9,R #0}]C`xA@q о 00 I` mB4>DHP ` U *E$ph*Q>@N- %m^`= ^ѡ 01w@̎)#`";2t Z0( Z %v0 `KP*Q`Z_ 0xBd101 Y3 #0V [. 0R0<Q$q$.0 0U]Zp X ` 1 0 k%A =܋@ . R00rq` ! nڡ? 2@0 PѪ!CxA"RN]C AJǏ H)UdR8 (kY&JpzpMA={.ܵi˖%{-ɬ{#V8ZLrRٚ/cD֣&7nn~yuRphpgME Vf]ʤ: #b@e!XJr 0xf+V4Zd[*prmB@}Va8ZVS[Mܕ\' ǷGe+{bɚxsd1KFi<)@&|D|rDB] y[a ^)nMJ6 b POw&I糺abY&3 -,L sPQ'vF^Z@g:lʛ`1ΖE#Hҁ7'eZKKk ${bm ''! ~pn@IrwG |E6 7`y| Or;<Na]f@0 *Q(#?q`8fyG 0/ݢC2Z[0;B%Yтl%ͪk¶Dcz9J<dg[JPFH=ca (=-|SҎ:U)HM4z#6ArIJ6 f(CqA pO LђăKaM$8R0q=#P@@0'd@g88O4LMqFX>gt#G)^ejp pt#u)x+367|;@FKS[l 9{dj^J@Q=WET]hKWcn J"G"V#i%u'g)\*L*l,f,j-S-c.z0R0^1m112T2`3q46y7q9u:X;D8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc.C, all rights reserved.sf32 B&l,>) H*\ȰÇ#F̷6lIOt„sP8J-hΤI6T+׮.NKX[3mۯd Z .ںo튅U>曇0M=I,ʟ|fM5S̡s& >C;(箻Jfl#GR ?gTo߫=H{B(a~)^@&{(8/ыTx7^p`%PgE q@ `B#$ w!_;Z5φ9XCfp>Ȉލ\F\kGC&2D0^3-^""1z/#_'.q m q7"32Mqc6C;JP Ƀ"ɰMxSk!lHJr{%uH42f+ ,Ix  JЍh+khZqd<%i$+j,?8FPl,IKiJ$Rvr݌=VBҕxsg{h2I'yj̴a:I1 =QЌ`<6ސKKeo=@(@jIr&x\bF0Ք2BG=Sla S*6]!NDќ h6C|Y,1cJUD^5beS4\1*_Q,kVơWzG/}1 1VZ(Nzw>"zv.-_`v/[(T;Fւ'g=[ۺK[)aҀ`q;\ٸcTn7\[)p{:wPCb,[ zKZ#2jn[W-]€ ݄'mֲך##pᚾ21o@}z݉+&%/Y ,FRٿXꢳH옭*Ec,ٹ<βJ=oe͸$*w@9=tc- aӳq9ir˫_^@Q2)S'<uЄq[-S;[U"&'][i?yưo]W v(jg Ip6)-vRg;#+'Xs3 < ҹ[NtB4[w5˺}%[aû+ uqu5 .7gZZnxs\7.(wpNnP=4C+9aTuح)Է׵sm-uWBO8as͞;˕;۴w0N+Fo+eÔeh?Ϣ/'{Y]_`O|3[_<|Mt7>߽ͥK&~ c컯 <~m~~FxW7P%wl7yUh考DӀb{fvwh|e,ȁXʧUo5Wzӗ~ǂgp/0(}4H,be88e'Swz+AqDAZxt]|RFftxG~hRovwg&WpqFhQH 1v'7U6bjȆ,^xr0wxznw2Ńwy(x8\.u٦il#z[u|Z!Uhhf8uɖp折Xtȉ;x,ȑrO8Xu&Hhkul`t)wgqVu?ؘpζ`gxh/GpfghZ(~gz爆 wpH87hd`y%ЈH0ghH~Xmy*9-b/m=)׃Է9<)/?]A+hLwhȔQIYUYzd?ƊŔAD89wSy=t {)JY8r {y9IwVQƇ(ysǎ:Yx6rY Y1{|w$I}'{1{֙s}GɆ a} HI0uSeٖԵqoɛ|᛿}~ E8 uXSti~Yh?)F&fx9HuȝGlxixc=d.sUyoGGi_e)pU0%lH詞i'F)ZYʀ00tMU"i)#jɣB5Ajpg,;p#J9k׎8س-kךh0DZp9KQJkfj*`E$+(# `;697r;t[v{xz+4ɲ]ٶ."q{۸;a J˪Ci'f`{~1TA9AXۡj;&غftdK#HN} iEa~+L{ kw4¼仸qvZK+~;:Jǽ)m ګ{q)ǧ|t+k0A˾K9y̻%6,[jk{ Yڿ%ܮ,ܠ{24\.;O:\''l/ KfK< 5l ãW\ I,S!һכyJ&a,՚e|1bÛAt܌ ,\{|<ćGp\\ ȗL,!~ wʄ;ʤ\ɖʚƨk`˞vūL˿\^)*a ˘L`;Iͷ^NŽھ^XA>~>xN^nX6^/[Ҿ O?tX_Ξ .&?͎*.*4n8_J>$/PnT:XM@^S_bWf/^jOlO1petZbmqNvkyO~^ފ_kPsOa@JtAĒ?@EǢIJ%JL377.OR1GHU>;XYU[\\BCNHa:)Kgfh%AfhͫZS[ѳsAіutxqzpW XX~Y9г^!^n8"X0C lKFQEi0VؑbE)ٺQj㜼/عr1erqG%buA_ XTCɂB0! w@:U 7lX@zԪ%alu$oJwe;3R%]sUCClprd:#5>Ml2+dfԼ&[z|VY1ACNUnױe^-a~;77Çor_ Noil"m9ua;>8 u nՕ)ostZ_BPAÏbȌMD# FLr=c¹м C';77DX1 i4B$;C-&dRB/ WPț^lQ@w#Q|rI. چlʚΫ2 EkRBQrS=E5& 0D ,PHU{4Q:KGӑL3M %Yǽ`;uɸtH1E\4F]4VBweSP$XXd=קruvbָiJuQ4Ug#jllW[u\z-'Fp5w;9z^^_9.* <;Vcp;ydK6 OsQvecy'~ cGʖ\@YfhhVz\l8mLOq M8Sle,jZ}[ۼv|R^D*2ĸ7`Xcls5Ww3Hs=p7>,M{\md.BT+w MKs,h/z>~uRku3 ;,N$KEN~']z?{NewjT@q/w"y/x";Z6߈VV" ԿP\`t8 ASbx@1A0r y2d6~zO('_ !P0gG ʨZAO*(FoLX,*pO8Ek^4/F u ^ >,7%PgZh7aLxoNG|E1:ubQX{}(elb8^qcCǕ=t"OJ_ẓr\'K>lϑF8uJZD`K Y2bV/y"Ds$ Jl|3g@ &tu!Z8ņf4NFG:Ѓt;B';%\;'Ÿz?l?+J?Hl @/LCn6";:ÄSC:|B/@C,CA|Cs9SDF+ @lDJ$0l2$; J,>@53DT@e[AbT4˽-9cIE^<]E`tXE 88X9Y FeBCS>X\Fiܹ%Իhc>iFC7>FnǦ[@c?#Gu8HԾ9,uGkPdG||8CWG븽dH\HlH|HHHHHHHHH II,IIxH#DKDDh%QN )g SJf%bR1%lzVX,9ty&9"Hs w╁zhƴfhJUt)f6MRj;фDeƺ*R+0zkKɬ*{nTkfv*+ (+!5K-&;N2/{V/jC !<p* j K y kʟ\+d1LB"1&˝L,0E?1nzsO8/k\2B }2JdUij}%8]3r@rP16͉)c)FMsfj>,j+M*Pxlrj3s{ܷ ;TkW]8dmxTkzR;梻>_z  5X;~r; mGb6&ikt;5 |u};wR9{=Л*2X:F%=X$:ŀ"q`lF10l s:`x>(ԇ m#` 0gD;pѡ@xH  9i]ZB HD1M^xE,jAB1%Xo-'55s+G>qbɈB1f }@"G@%W+&9Ux9 Nv EyNxf$BZ"9N+_X)xLJ+ܲM0)c3 sk.ބ gB'ڳ̈́Î{<u嵝jj@\Oҽ<ّ[;mN6%/)N;]ux݀O,?Wʾovg90N/뗰zzcXZCx?Xaٔv]Ɂr5׷,ﵿD IKgмwxyhi꘍؎b[gjgeSR= ԏyhBv\3Vpqq4"/9vn9Q18k?y|*-UƑ]Ƈ(Ǔ=nȑ\HZ TÔob1UV26clI oUy7}dّfZBc% 1pY6"-+`a'|2X b/9 C*^GVfY@ߤFDԈ򁥨)K`9GHFU TYD@AW)^ÅA# e`9bVX٤g06}Q2 aPAo6FjQzY@"felٚ;(}CmG)uZ~Y^}@fC%j١j*G'R6(G #iϓfy)S뵟$7-Ty9zrɡY@h`.O mח_T\}r O9,TUΡHq2U (Sn y p)b^*/&zQWz lz~76ڜ *0и4{|B@rxmjz4Zn[TШ2ìj 4E'Ӫ} v2ޚ:Qc,슓: )*D**)ʩ:غJZus'3 {0˰d5 @c2ձq =`";2{ o- K8v0;124[s%:NJ,>25DF /ڴpdPkRE@fqΑ)4Uk^~+;=H -QX{ `^iz8Ķ{*HUǷ/^@J1bddEa;~۹8 X;[۳tr [; TB[ 4gW{''3 -r[Λ%asHZ&kkM䛺) 8[;qz a{ H![R8RY¶  ; #ʋN|+"LA E'#< F'7PS[ToALƗ8;$ 6#6 QZQ*#>"%K%,#",bLP>\,oA#Ŷ b,L6k,2L b]A]n O>l r~l-.:\ƫ"<fvlk>V#|#9u$Nd<|c\xˀ¼;\pMu\|؜ڼ<\|;acl2-sources/doc/HTML/binary-trees-app-expl.gif0000664002132200015000000000230511605147052020704 0ustar kaufmannacl2GIF87au,u0I8ͻ`(dihlp,tmx|pH,HE` X- 1Un* ( ť&{-ިnHӤ4V!9nJ'pqnP9̻ (kY p2pc-R(Vz +lJ+[癀fV)W%s6+,1]t*\*M;mD9KmXg7Q혶kv؆""O&D ٛ$Dېf ܉Ŧ}q=W}q~؟zs qv@ovf}ՃC4Bm ;)=UIwE{"tԟ u*H$A;acl2-sources/doc/HTML/binary-trees-app.gif0000664002132200015000000000367511605147053017752 0ustar kaufmannacl2GIF87a,0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|Nq[E{56w 4iyJ{0pK ¥» oHɘ*nGƵl@ $ (ȸQc,ȁٿj8R<:ICw2EnTrh &Ο@w J藡t] ҦPJJuJjň֯ff6, b~iڻP辅@.u)qE\8/ nB.qf 3Gl%,LkFm i:H/sն/t+[Ek.I{GA.xdUAW΢s=6})?,ڄѤ_aZ4ïo}Оn~5` w| NA!=h~BnanX~,Hm7k#Jbׅ@ a0y %hf1Ý*vBMo&Pirc]IFiYbf>n\^䒛\Yё՚ǵdV NچgsR BnigSmWH-ME'QVT"edà)P(蘷bow0cS!z(2!Y3 U>8I DA:ZBᵊt ġ)s6Pn(%UK+h(-h-CpC_01 qLC; lwE8),h k_ѩiib\ɜ#͓m R@tGṣL.KxTA/58?Gʻ4pM,TkF 22j 5SuݶUo\_"GH걑yb$# z&0X>MCud,kȵXvQ b xrcHbS']&s + 'LE (f5w@VnIN3fnus3˙ "Od鼿M@A|8Nt4a"?s< A0sa0Ÿŋr@O Kb)eLYar>P\!3vT{X҂:B'eK,ZLZԖLixOV4UP!9)Ci'iҜVPYժw1W=4"QRe+OKjS9"ũrWw.PdS_#NԎ2uԚ֎debXC2"U괈frvodd}k66ZEֶ-p*' .r 72A#tˍ6R$v{F]ۮQW!;6$eVS2]Rh8Iݠz\vK,y[/^B2ZyJRXT%P) b GL(NWI;acl2-sources/doc/HTML/binary-trees-x-y.gif0000664002132200015000000000254611605147052017702 0ustar kaufmannacl2GIF87ab,b0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZj`  . .Yw,HK~;}*jxindPLJZ6KP0 ߾4ْ-LՋ͈ǀyd$N?uNs'0bǏ CѓD%Rx"݋YC7+xJ%qya?$}SΥrO7w5ΖSiFC$x` P؎%1;'2šw-}ދ$p}:%=Lcƅ 1#5V|dgȂK9qhm oöCZ7jTn}<l\#K_rFv܎ΉƓ06+(1niy뙶7Bw6Z17`~ПOW]?gR!PCsPAcON}Bat^H704d6pmU i՗"-r~CHGZP}vчB^?)Ch{9Ȁz2A6X.ŘS-z`_>ai&r9&FPVr曑97.ᆃbb@&pnُV)u(ᕩWjAQG媆|8Ť::ŦAzOdY,FnQ+:%I6PmU2YԶfsH\QFj/ (xBmd{/]Zb/EX:LXB;1]2`[& `l"3l^i/b軒F$$?}vI`X%}\#yM,%& `$`O]̽l]2\~Em}^uX6 eUtؒ!>رYNײ9ܐEҵkr㝯>uӭRROv>z+1DѼb&T{r*eߗ6u{>j'ث?j=0*k*EoH*BDseg ϊԱ ZcLh2Q-v1rFP.? \zXqۻAxЍІ#T´Lk! 慷#6.Kb$8RbҨ+ڎZ Nv/~NhTȕeiH:x̣> ;acl2-sources/doc/HTML/book04.gif0000664002132200015000000000173111605147053015655 0ustar kaufmannacl2GIF89a!,!$@)'C6|HB BG\HP(%rFbr)dȖS\eG3OrYeIyXѡȥ"d UËUjTA]T,ز^F5+2ٞbWEUmܱݦ{%u A+ŐU8r )̹;acl2-sources/doc/HTML/bridge-analysis.gif0000664002132200015000000000457511605147052017644 0ustar kaufmannacl2GIF87a/,/0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|Niz>s:6k2.~*q&h"¢ȚΒԊڂ vn+V/@PWzP, cFx/d,Q2S J*cʔ eŗb ')}zQh'UR[Lt;mɼ3 NIٳhӪ]˶m:]9 *I* 7kݬ[/K7}5WnZ52wͥ[͝3_ҥEEXuQw'']5Uڗeֆ%cokq͍v}K@ճ\_];oc][9i{_9yQ`_>x^~'$eÛ 5 %R[ 6Y> :R B!&" `Ȝ8E5H"XF(c4z ?6(L;ܐDcJepL695!LM9aUiOrॏv&V 5ٝJEp6)'/tWٹgX(n#di(i袘JeZʦ]:i6taСi*LS*|~JdZ+\uXīlPTH陕 Q;F-B5쑣:..^ чMd͖AE cg 7 A&p ̖4d&m\/7,_Ч = ht=l +@MĆ0#ˑWX \u0-(յpm__<uv3@t~Y M18GV Xy!<:闗:=%쭧zѹn{ҽ 3;ŧ;ɓ< |+?}}0KC<r}0˿>0?q?P7.) '5 ԟf4,z)x`0#P A0H7jbB/HC@P .zh9j( Lh }b Mu + EQb`H`ۃx2чA# -! )8ǩюF9!rd#,9HT|<$#E@FU$F y$NR&7pGqr#$Qf < HITВp+'UrTe-@\,J~,&% kib& LP*Ԭ&7rҲ̄&z6 ل2n4ͩxB7 aBӹPʓ,>NZ4'73ZJ&S`D,;ђjTgI Ґ$gJSИTulF.BӤO!ԀiM:SnL % jQmSjbRuBiVJӦ.DWV5c]=VpECW"_uk_z4thEl<*6=Rvx5!Y_ֶvr}]u1-_ع86ee&݉iS־|mis6-qo{sCMu[ ~-2Ļ]7}gnk90$V #;Y2q[qch8=.|o^89ʹk.brм9SlsV }NFғ.p1 z.!D8:oSU:NhOp;acl2-sources/doc/HTML/bridge.gif0000664002132200015000000001071511605147053016015 0ustar kaufmannacl2GIF87a(C0`ٳ⋙,(C0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJr جvzxL.zn|N~-~_юZ~Y Ե <pÇ!Po /jx ŏ9b(ICAL3.cʴq̈́3ɳg7U9l'ѣH *4/N\ʴ\Pj5Iǡ[ee KaW_-h'u-ݻ=8/)~ӻR+F0šCqᾔmUy彘;O,qCnDz\Ѫ~5۰b{Q;xƓ~r>Kܦxc>:hO^OϞOϿ}_lxƀ 6p :XVHƁ+YdnQЇrtHJ!q0\4ֈ6樣8(a>3cD$=IA&dLETgXfޖ9vez`(Yhr&lݛ1)tth|' ܠ(j(n袴5J⣐&釔Vjڥfhn駜jᨤRfj ٪b:(묂୸zׯ+[hj%̒lFմUkUQu[߷&%.}{Թ񥫮OS}:/3޽Ƥoz{ҿ,pIg% k0wG UlCcu< $t$C\ϥ<0+'Ԝ82=Ak9Eǧq<'؊Z`6M;}PS0׊KVuJ5ؕub2گ"T j]7w&w?P}3#jwq E{ͧw-v[*9?gds꬯n f妡:״ wޓS&|`oo5 V.ɞy^%&c=g!7nwQ<^sJbPs >!i0" >NV0, \y[L 6BI;0~a[XBFv:0! ?+I6,"D -?wht\I?uA9|b(%}tԢ?7.ѻXD=qw,E1EzcVѐ\#bELQdd(flIJx Jz]9KPqD)]b`MCYA.٣A1҅%G>d3$]S-_H{t̤49Lp0D'8Q3& 5q:y'8LO_ C.Q~&zr'D PTӢ=GJ}Q|3ВNB )I/:SZ' 6*ʕڔ Bq5h@z5oF>E*O!TO *P JCX5(=` 3l%+PzTjӤ AT. WcA:UeL7WɥIB"!ۚU*d,^'{WiZf9- "%[׃"R|.6Y!}+yv-.oⶱEmcE˴X\VuDboyAViK]8׽M}\gi ` zMK 'L Lj veE—]Rlaō:]k ,q}|YeǮ@ob&[D|c,O9yR./ee3p>sb'KٱˀvsLAKTqa ׬4Cѐ#IK͘.s8rsA]hJԆ6lgtaJZշuKk:cNd2ֳu{=ldﺀ-˫;Ze?ۺQ6S/VM jv]s&rKBGmunܖz3wb pxSJ;|ĉ7).pw1! x.7yA>rE@Tx/E~s[@{5_x.s#EfWcz΋IG;!ߡf~vbŽӁVW;(>xG|ֻN{FU^+;}uD/W63xɛ>򨿺ikr"=Gڗ>?KFGxug~~-O1{N_챿}?gm?+>?pSW_gx<cN}d}WGagX!$~g h (L x]hƷ(%XZߧwb؂x* x<(sX LE=އ:؁GCB ~&hMȄ9x4I 1 \6hHjHK5 3sZ8vGiȆuH,YXP1Rk~gXy聐!{XGx؈|Xg eV}ȉKȊmxhk?A72s xJȌmxwE&@lS،ȋѨxXbGh8؎8X`"MX؏y0) ؐ萐w&8DaY!s瑜rJ#"%iZ!6p#2.900iP<ٓ>@B9DYFyHJLٔNPR9TYVyXBy7ٕǕ^~bYf Yjlٖnpr9tYvyE|ٗ~9Yz4s٘xih-T`IH&ə'f shIyZhyԚBw6)]i"Ǒ9I?YI+YyМ|c9UTI`{)ɝ )C)t_bɞ9ɜyI92j*d9j4ڙ* zIZ0g4㢳(#, С ڟ)%z$: 3eֲ{Hď19,c(QPJ 2b7xJp4)8d~ JѣH*]ʴ36<%5*.T:5יm hVXpm[ qG l_RJk^+D0 ljiE<$UdIbʫ{% 4Yɑ*3+hkں^jբ?vjھA!o=Gf:uG{@‹C66Z~eп[NX\uZ\40z1s {W!vF(ag:( Ҧ\xw߈0_أ9F&"pP% fX#1FDV>) 2bjA9G 9%L`)9禅!9&heYXP,xg8'f$$plJ9~_Z& Zh`fRGa=P{hґs5.Ía{u~ih$Zvj[N+"6C"9VU6H r"(Sv+k覫+C;acl2-sources/doc/HTML/computing-machine-5x7.gif0000664002132200015000000001243311605147053020610 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|NKx><{yw${vW!(9Lz3~ţy˹|ʥЀνBm)XȰY #Ưd 4ō֝oy .× XJ)>)Y'#iw0Wh'ͧtl9L*]NH\5 t-̥Lf֍U׮,9V>\Y@ug4!ڴ1G]cMzT/:~G϶hb_epڵSIY$;oƒ&[V#fZq}!S1D8Ե+ňoҩWO^)Q]^t˗s0Ͽ/߀Mug 6({)VxZ"P(%(_0Zx1h~3x<=)d$99I$)w9 IZC`)dihlp)fbK:>R ID~ְGb^aΏ h@,vV9 &i}0)i3GcrC0臟*|j4J*)B*jP7J1.V(`l?qb}(k-u*&vSYZj_[ɶ` JGD-10L gBUyv¿ Rwk9\m{;q | />8u<I /MJ?<4l6_ ? "SKrV%5,Ou3u Y:6 QmAN5KdM+b'LO{{B{znm9Q>..E^GE˺ nثk+a{2z觯oq/?o@O 1+|X#?.` 3(?"qN ng@_9P p,D 7La[8@ ~%>G 4C6O _X*ZQ@`bDE+^Q[zL4jlp>DQ:ġs8?R c<]T$H5Zҍ'd%C 1ZҌLc*C>g4e'oGt XXl (R~B0`IYzRnz!t1ZL`e8uiMdbӜY# Izӛ *1BN9K=s@BІvSw&-hK!8f-Jo9`=Ra%9IQLnS!A%jPxyeFwvGK;E%M}yP )ÌiUM`j&?-,#Li;W:1ƌ;:w 3hMjAj G]OJ֚4U@(Vկl%^JͽLe:.#a'֟λ CڕhBzU- eDZ5*cs)S~6q:{;VrWէem"tQu"ka"oMru+@!s+]ʠٵ-B[Dƫ[Տ {{ Xl [w0]H+b?,8G\{W~HѸƘ\^t'`876NJ,d 6QW 1P)șg*E+ 2 K)5^2kB0g [hHrӬeT`s UrN޲B?rn}xeGը"v{!6MCP)In21: 8 46l0#0y L=𕧁 fys<_06[AV:b@2&zSaHg4h.1oӞ哖ߌȦxNXХ7mI6@qc-jN)$4AW lU uNjz .v&ؼ ^!Gҩc+ӗxX;BD=/$-$[\raZ˚>[n{]60A|Cd=@MyiⱢk_zM^MhQR*ؿBqu͛?\]Ik[/Ãʋ4Cb9 \')AZĵ 0GlڎyS6ھ\Iܸٞyҟ]-,YoՓ횹ͫl[̍D)Ŕy[ܒL3[#=߿͝޽XM  筚}. U[U6 kܠ ·. UAucb^^Nmc CqJK<{ }J8@~萰͢nk` B靾n ~] r@.뮞)N .< M!Ǿ;>< >;. .蚎"Ӿ:>핞ћ.N.츎#|n爝."9N?_ /?_ "?$_&(<;acl2-sources/doc/HTML/computing-machine-5xy.gif0000664002132200015000000001273311605147053020715 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~V",=Q '8;$5Z@ƒʺɨڱ˞y$p.~!78m )N. C~F$(n دA (_)tr[B/[t΢DfsF=D*]@Ҥ91|bL[7gFM9wȾ ޯb+MaUj'w]ܵYH[ 61cȬt3w)*{5XRo"lOzr \vЯn=Zw |8n$KcbM}b׭CG{t== ˶pL?fX`g;׿7Y_x3ˀm"L (D`^ Nh u"x#BGK]ኴ,o0XɆUV6a(*"@@*(ҏA66(׎PVi啛\v Rd e[fl)gfn9xR'yg%{)%J衈bbh6ǢF%t@*Uhvv' 3(`ꩨꪬ꫰*무j뭸ʪ- E(+[Ro3Dm-Tfgj*LK-~Bע&>+& /KrBƀo hoC8/*"\"pZ + þ8.q#_CPu}|&Z Ϝ,#y=<!-ڹɎ^IG9w+)l30CxYsR[Mb 44]#u]_2 %7vtS84v2j6;+$0SmΨLHkHS.=j^#. s襳l<]hNK+w';{# l`T4b`ǎ-w?_9,o~诋*>B p!~ST0:0PoE 1@ r-¶FHB(ݱRBv-adB%\D`=`RFC H&:3|(RS`ŒD,zGEPSh9zv"E.TZ|YE*0M'EPC $2#dIN^ғ &;IRoR)2:AKdyFAIIS0I42ILj0xLdbrhJͬ1M4\'A uR.YJzrw7 07)Nrb@Ykdh)MVӠلgD6Q`.DF ,&HrS}\GMp2]&S` CXKldP_TlCyZ$P%]0-pLR:U3$:ղcjn>pC_8bhm\$5^UI#E,_OIao^#5+%)Zႄ]XeF\Ҳo/kg]GaCtp$EqS,+v—=y`:4ռf-8q4'R^b4FKbzA"H/-H#<"lq;(|v-{[huݑf/vWk~P6#}%Cvčkcԁv=`kDx(4"[[]f}#097 A{oOw WBQJ>pi) W\\eLuwwIk7݋~7zR`xL*\ LW.%h783 _:+~%AR_/qr`*9AmƓ&)e)i@f=HÔn'oVZIx7K>A->Nhh,1da3 Bd'NrzDGvyey_T+T֘cZ&js wyjm6XpRJi1c6b$cM8I )XX)I99H_5 vcwis6d4eHiY^=KheqI~Zŝig\̉\)xe1U;Ն Slj9 A])X@*[ǕDdljFe7E*b f+)o( nբOU ⌓ȟ0*d6ryyat+ .Zsy{^ٞ٣׉L=Gw`ڵ KFjVZ?z2z)Tf bVfdƅছ',tJ4֥aUceZK*BIc%񊱧f]gHfN2㘣kvjɝ {Ø֨թV6k&Uj+Uz;|:dڧizB2c-9-n{ëh:q5uP#, fખ [D79IvF&Z8ڬfĥbQEy:Jx:S*agm2k k]5;hFl7+BDKEI3jM4' jK5,] XwYM6&&mP=b>֤_ arͪ"[`{?׆ۭW)g!@p=ؖ{[KkV`<ɔ<ࢠ̡ݑ 5 nߘ-TްiB8]I>1['~B)OnuN:^Yι#drh}^.hRn b~^O⇞n靮[^&% *ꍎV_>,0PAbʭnd\g%cNk<͂I9']ΘNn{f^ܠh.xr3tθkvnz 9?7hڸoo}#!/uO%?'ϔ/?",/.4ҥPr()>$@/W ӝ[/HJ=IB9p_RUqdr^ 8/:POk?7vz,:&4U/Tn`hR"N FX $F^a#l $h!'̱"Rیh㍢H8&:* DH&FE6)JF)L:Y$SfUZ9$[)]zc`) ț_I|f&O砄vY袌!x*褄饤fJVbirꤧMjJz pjkys(gk셾Y&F Jkm{^nm~+Mվ2l厫.5.g'oBUkn2KE' 7㦼- Y6oʝ`{Z&kvPr&LsJь/7ͅ &w^CA)'3#(nq9 KmupأR cch?rp?qO=>uۭח q\E}ƈ!1[]vLt7^k]x x~k΁V.9ݘ~:F.{n0.{";:UA$mg;ڮ~9+:Ol1u߬KC/c}⊦F?,m(l 6PZ4 Տiw# NC"A=Pxғ%.q|_ [XNl%B e@t7avxp8@9ч+t^[EeDÅ1cU(FtCg>Ƒ~cEuv<"F:\$JZ2&7)EV; e%3)R6 WaV*,%,cYRpKQڲ*`r2Ƽ/e2 (LL"fF̀`$g4NHRh|E9Yf Ayq4+ːD@ًyd>@,gDYkhz5HGj3vHF3яv4$I;BtK_*ɘ4o'#nҖ1jz jԨE*I:a0NeNw*ՑRUTNѮtK͈9Q>-)P5)H{ӯA}[ ׾que+qz굫 +Z?bucQq} hG;$敫*Pvz2vflEͭh-"zճn:w5YgthWk]Yղ S[vv`=^٨JWWk/Qb-{yڦ@HiZ|{XDn .8U@yW&9lX.umtC5*祮kXm{<- ~K?aWnqM'1x%2|O*yBLf5N]\b&kc®y4ZXgΝ3Ͷu|p_eF䉢B`T5:81 |ScՁfߧ_Mk*(re-^M3à S^0j%Km6F2`]iڠ|)d+'nd;Vl*2Ӧv / p;F86YnSlHخf.p]߭l mNnqo{_ýnZ[YC1P$O~q.#*~;9c؈`4*N #9 ~8pKiUOeҿg3 '"x}f?wծ:fsXv^vbV_D;3F|&J ;|ֹLCy^[>x["yQ^>ӽ%t>{2烏O}cCr їe_ȏ_} }cz&czvdK{`G7Hv|Pw(w GǀY'havB`kw֧C ؀}vTTcDr7(_9tD!{P{8pɑ}E{<淃>x[Fk%Ӆ K#)d7 Ćo'%dRgb&W217G}8!L?ex]vX6q8+('ܗGG(m؆؂R(wx1\{؈@$sxBW'O kP ʼn|Jyh"9ȘdhfsFiL8 G(SHxF%&gme6bv`Ȋxg1}وx^Aige'(&(p& Vb{Fh%f3Es38(/$pg؈%'I(9+y'v1)g )#&r(D׍8iu#xD9Y(;)YtTٍVkȕL)ya)H7Z؋‹_i[%)@)z)o|!|cx7&inY1pSItJ3 U4wg0&IKbP6,jfwKk𔦠iț\L J D*,upKaФ5I5ŦpѢ(PƖhggPxi`z 6Ccåsmc:m)lb"hZuhH eP'vfVLlVXzoQFIfgfWf3lR \'vOʩ9pT*5W(V $b03f hE:G bEŪTkjT[٣ZV3+yqtuEJYҫJ6m&ʐ:O[񊯎GzVdju)gZ_* Cڰv֔8bknz:vꨯu[ꩳKބ;q>-nʧmf1 Gpkz(%j*;kꌚrY T;b&g&nЃEFovcc{>gˣi4kPqpkvlXyK{KÆx˭mlp4oZ世 y$GKt'j0wG ;+6Q 2۱# »ח ~ϋś ̫΋{+b6wրʋ˽@uwxk"Q$ǻ 1j˾81+(tW ( gC "a|<I P+4LW-!\K ;;"T~Ȣ=L>e1ltGE鷠Cl/Lf 3|Ăc?%2uZX SLgc,`|2[̭]̮2S7,q.O\男C$qu ;ȃǭ@|T`r|,\{ +d4vQ0s}cK7|:\33Gh ;jۜMooP[10A\y.g^_ ʼ0j3.mt!=;r+$Te GP͇ߊ+Bܳ=ٌ߬m2DijN+ n$^TBy'N\ 3n0M7~?> ~5_)NEnDΔ wA.CNhh-G; =1)OKDDlDY,COW1QqǞ~&G Hf$o'Jer*cʑJ. 1,46\s'49M2:7QQC>O@tPB 5>=DtQFuQH#tRB[RL3tSHSPCuTQ-TTSDU[Cu+XuVZkV\s5YwW.{vXbYXdXeuv8fvZSev[p};acl2-sources/doc/HTML/computing-machine-xxy.gif0000664002132200015000000001420411605147053021013 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~6",=2\ '80&Q‘;-Ȣɒ}ؘΣͼݷʵ#X0޳ԎpĈN`YÌ=b\#C>ȜIS&8it8#=+XrK-9ԥO(tڎڅ4כ9þ@zhR&*gÑjM{ݷ'k_ӌXVnUSSEibx:w7?]K. UʗF>\*eL%6[,Q=Zᦓ;Ƹ0(Hf-Y[_[2;"k{`h6VAg*ꨤ@njr`꫰jƵ*뮼+k&첾J_f봱襷:{S,P+nt@Cp- [Bh^0k͹Ѯ0˯cMb+/`wo .\ \ 1Ѱ)l; JL! !\BqԪ2$L0r'r sD3 ? gM:u|tz:>-P* ՠZ: ';Zդw֠sG(mMs|tfwn7#$0~WZ^嘇9A\CNu|;~ꂎ >&L㚺v' f+䶣XC«Uxڃj!}z L-(sM<cMVJN>۔DZ#]?}- JI ^k`حTX@ ZBS`f$4!P,^S'C)а~* pJ=UeC{c)EDQ||")*1uM4x.z UTM o{#G"X2VBB*АT'F!v{RDQȤ&7Nz (GP򔣤#T|,3J]d6 !,M)LW<&2S9CV.|&,H*{/'Ji2󂴴&j3,'5s鄧8iNZ6F,)ZC%^Jr`$9x2D OX 6Qz㡥D':΋b;=ҊtI-єnԣDBz:|sGFNjLH,`@`ۊ׭-iQGcT&7npl~ ! 1el0OʙP@íPjcC=6e̅;!8ZQP6hXцA)S'_"r}䊺~ր̈V[-7t%oagڹvL X䞑Y?ڧC5nM()W=Lԡ/7jMرklfA)䳍qD|V`е(wE,= yɄ΋GM| c#Gb HOzD~5@\\ 2J.ˈk9` .*'eC/ȣgJ/VaG9/z6h: 3[Yzux~F7̅?|#~o5$`&/o{n5G? {D&3C`~ h4:̲,: !!!`&7r7AHg|yO`-Vr-GF .!n(g&&o1(3XAq{"$*G%#Ѓ,PbeEn/h[T4BQցMQ9aaVb%Ab%7]X`Kl(%oX%sp_ahvo`ԡsQG'}m2EkGŇg(xf/`ʁq,h.ZbPnHwea8w^q`bvf%$[RW}xWv׈j8o^cTUwuxv^}5qzXuB,+Y"߈$HhcdAt)hi+Hxh8y‚hh%6'TN?ń}@Y),YrHiIFo6)j&Zs($EIbUPELy&c}Ԕ-7~7v9%V @KْZDs5Hx>N5ى((eCtY8UKy`@C4'xPʢIn ;(4gsyt(yT({rI{'כUؓ=%zppLÕ Iɛ&Hǝv߹ٝ9晞蹞W9Ed(nVL&vp֟.t'I: z jʜ!5l)L)sٰvٞ ?$ &ڡ'D0YTע y|i5lWH hKq.D zCj+C#fcd֋%G Yh! i#izJh5evГ9@ /J+ c\b pv שW۽k[9ԫ;؛zKs˼;*Lsf w+(˻ 934jx ꃺbt{k 1k% /<[:G[leښvE3-çh:<>K+=6<#3RE\3z$ T,gW.KG;ZM̞y@,|쫱&tēg<':V dvsu\8LCVh|n<7?Ɩ l,l˫/& Ʃ0N,vl!exjL*[ήƒܽL=UTs[Q }#3ezX@ѹ:@-w}_3mcG`I9ʱΈdљr)ܙ"հ&Ky .02=ɦ cUB~{\Jg-em4=շx\(^#}<֊doQ&mo}qmRmvm-> Ԃӭ }5Gҿ3y&f=(l`Md LlF]ژ֦ -E ڗԲ ͻ|ҷr٬eX֒84 ;ߍ @wO]{;0Mq[SLz==anݍ]ȜaD5N;]ߘ&ՅO&bc,1|`+:MW〩۽R&*զ'ʨ.)RzSW9:^K)ml鬢NNKM՛H ;IjФ;?Y]߈^Ic9en}XכN)>xըV~߇aV ɟPOۨt2t ? |~k+~0j#Y޿M2_.4𐍭g!J8,PDz//;@~Yy1O4Y.O}HF Z Q Z|7 SnQ:mW_n]ǡJj57goJ_6O *qOt\qaZЎ鴊Ӛ_m 2U:֍HyS%4E7xִb:.OdN//M씿bd_Ǖf3M L~8 <N@ZY^[u0G4O u_8[e_P0&tVVUFSWZ$gtZҵu4=w/7'A(BBFGEG?JLMNOPѴQSͲKUVդXYZ[\֢W`a`` Lfghekllmporsrtwvyty{}}5 X@ DتB{F7"12^dcǃ }CRIu(A*`q.eƜ%̚gpٙSfO[Q<&Uu7|OЪ–5Vj 5jٲdђvZn•ۣmJt⭪X=;8_C'&1RďCDFKe0g6Yga.7A-h"G^uꎭI˖Jms}n G[q\]~wKI݂uc Qwŏ'_yg{ϧt}p@ 4@TpAtA#pB +B 3pC;C;acl2-sources/doc/HTML/computing-machine.gif0000664002132200015000000000777411605147053020203 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH>Ǣrl:Шt $9VvzpJ5o|NxT{NqeY|yChIo|}GWpjklqEp!Ǜʰ˨ ul ]z:o*̃p᱆#Jqb3j,qCqm*&Sߏ~&߀灀&h X r`6'Vx߅CXB! k %ވz02c-!5>sㆱ}=HsGt4ׁK"yIORSJ%ER[ZgqYbQiSfގ.ƒg} `S4J蒈&f5M*Vzs9zi&$nZ9, 锟"`ubM4riRh:)kn mi-'PiŜ[hɦSf9WI6pflLnbtˋ k. ѷjp_aqg <1/.K砫r1+7#4m4 m3C̯%Sp2i)-c SW ub45#ssm76-4Ќ-mvNo45n{ײHZ=3,|v'h^Wwb'*<wkwQnzH9Nz߫_pRio)*Tk<^&˛oC.diG~> ~n1E<3OrҔX8V!KN0dC투T++%9sBr(`Lia% 2MK21ɢb:󘊨Nidɸi v`q*i&,0 ~)Iy"6@.aL yt-mױDB EFilȾW&GMː !*jjM9>u`Mk4QEÜP0Y*BtvR%鿪N[8Z%W.Р3)Q:ϋ 'cJsKP' %߹5(MT=Qչ XMa.vuZ"U5W;~meUTyAmi:;8mۅꂶQn?=2o dsD0ҝrO.No3]:(/tbۭڵzվ|+rGiGKO3x0^3{UxJbi Ax>EsFB!$͌XAذENL U)q$#T̘?*nE <:é$[G!zS j;^[rw,Ѱb(1ʳ^97dJQ,O6loe5M!"|2{"ty7U031s%xI5Od3P|Z tP#M@ʖ+[rsVuL}W_ZyA}J:Θc=7PvY4+ם3݌jh:hKjlNzNgnv4dU/y^-Yp?D^~FrAnl!/2Ƕ`u@p

    ̇އ8MOo^~]Z%ZܯP򴯁~0eǞ0ǿ$#>8h}5\"qAWR c0b|AkjG'u|NGck+'WVHF'QnGwwaDF"wQ?i\nyCjhywt&h0(w$p3158'h,:&%hsessGE:Ճs0fkdhVV9468N;x-RX?xj6jCxc2I0K(Fȅ3c+Xmmnxd'"X}B_XVelHX5zH,ĄO hFO>o}v7iH~XeF| r[Ѩp# XH8(~ H@1gbx'CgxyDxV֏bc׏PFj2ovԐ()Ċ19$edJtE/iCY/U.NHLc@>uG{Jy~הNvLNIW ӧA 9 hKS6Eø$͸+E~1ӋAѕɀB+X)\ƌy\mgb[dd(o3d 8maYlBjtjJÜZ6!jٞ5qn975řf&)edɤwoG eşh xY#ae|@ p` ЦbyqOU j: n o:!b*@(+*کZ k:Ur/GJ:߈]X :ʦZ'׫)ūMŃǚP@p|ʒڭ1*ڪ ړ\ `JﺧPI0ꚰp*\pj}N9mKѥ; K|:1 wE+XV g";X {𪲊"2wRP):K"-K|6  3)H^J=kI CkGkBP^iZXKeu|Ui۵kkwt/ pǪu?]W{[}Ko~Z[e˸wt{cZ sչi[{۹ۺ;[{ۻ›;acl2-sources/doc/HTML/concrete-proof.gif0000664002132200015000000001234011605147052017501 0ustar kaufmannacl2GIF87a,0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~VeyQ`tF]n=[pj8a[!Vնu[pM_b{PC,c*?Z{Ef[NmTCwSMjWk lM-X䐄lqKVz%n_0vG&5y/a9!Uc_*ɱ>=:{[ |e^ 4DELHt'H)*#_Zrdu0 Jx80˞,iYBsVi/D1jq cҥqNMLU~4r$J$Qq;U|ɰsw_vrPcFdzMBmedٸswe63sfnS%:M GNl6U͞ ^|eP:\Kw*~ )%4]{mn(Vµ!m߄ۼ87=r:f5+yo6loϮ|u\^ 3צ@se"~ HM™=q"W<7>y.*w{Oⷆm b޴B{OLz20 >CC~J}}!f? Ҩ}_:b7;7i Dw5^AZP#@cI=I8'?[t0J'G<(9D:@wg/QRK~ c bC0,^T.~:XUJNV3X}d'vXb|hM$}Xo\5+nq8xYx؋kQɘyMb!&͈t0Xx\ڸy AVD A(nU5JHG !h~Ht}^&sȎ}%q6uJ@ӄ`wԆՄ`&zHU~C-4١V)aWyCrS`jܣx ,0"6tSUjRbg_=PBALv"Ef4b1zQ$W>`mg8RTygs8e("zn`L+S.w_L;>g9Nhrsh)4ݵ ŗFtZ)>3袖/M^im;kU$KgXٖ%xY`հ\x^i7ٔ 63pr?>T8Rז*qZKeęozPm*uc~O$aPgSi$RxHUHڨfdyfFuN9gTjw*Ƨ,Z1pCcRShihd9xʚ٪&Rd62C9C5sʜXr0)A8Hx>/DjĬJG>o8 ne@陝0nŮ:5CW导(gEhů3 c%\V?]&[?;sH3){Q{@')z-&뮴کGI7˦_jj8RBڒIipGR; ũiEBT"_;[Yws`XZux9 ~Zx_UYnG {7B Je+ s*$do8Xnjud̲yu:kݔQ[uD綕EEi `q%dr zPhjKڶZYpK5v=c@IT6s@h!W6׊9ˤ9s^~:˿G6Xߊ[<f,*Z*LPxs>lڳkEiсmzn揾VnnN$.fQ.駎ꁱ갾.>^~麾N>n^Ğ>KW^^~>^~?_ ;acl2-sources/doc/HTML/doc03.gif0000664002132200015000000000202511605147053015464 0ustar kaufmannacl2GIF89a" U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!," HC #6GbE2w`u̸Ŏ&At8RbJ/6#˜'![,⛍;<3Ö+Kk^ }$NDu3k۲};/yyƘʝ.]z@;acl2-sources/doc/HTML/docbag2.gif0000664002132200015000000000201111605147052016047 0ustar kaufmannacl2GIF89a Oe}66i66TTiTTiiȬiiMMiMMM6M6iM6M6MTMTiMTMTMMiMMMMiMMMMiMȬMMMiMMMMiMM66i66TTiTTiiiȬi欀ii66i66TTiTTiiii欱ii66i66TTiTT׀׀i׀׀פפiפפiȬiii66i66TTiTTiiiȬii=ElM@@ffMMffMMff83H6ZHlRs橀׬&&&666EEETTTiiifii٠!, H *\߿!ǰbwE'IE:ć -S^d%M WzLh3LJgD!;8j2eRDHTbǨ72%ɕȬ25د1Ŏq,ҡYՎ-uJS|e)| pbث);/aiK9u"B?( #*OgEP+?dWƍ7nO'e7K.XpUnܸqƍ w o| "†n'EL'-(6@Eh|+yx2 rB{bE"Gd7 +2W+a҄P VXpP+8+ bU4PEUt?3PEU4PEU4>5PE3PEU4PE46PE U4PEUYDTҋ/;acl2-sources/doc/HTML/file03.gif0000664002132200015000000000201311605147052015632 0ustar kaufmannacl2GIF89aOe}_?????????????_?__?_?_?_?_?_???_????????_????ߟ????_????߿????_????????_????????__?_?_?_?_?_?_?_______________?________?______ߟ__?______߿__?________?________??_???????________?_?_ߟ?_߿?_?_??_???????________?_?_ߟ?_߿?ߟ_ߟߟߟߟߟߟ?_??_???????________?_?_ߟ?_߿?߿_߿߿߿߿߿٠!,@*<ÇJTa N( 7俈#F r_Ƌ) 2HIOO6mL(`ϗH]ڱӧ"ѥKODJuIiK] t,ɡ^T5#Cq;>vjǓ'rpgRq[SRh12.(`R3 fp/[ngҠ>:93Ձg=zAkD\p7Ǭ2 ;acl2-sources/doc/HTML/file04.gif0000664002132200015000000000207711605147052015645 0ustar kaufmannacl2GIF89a Oe}_?????????????_?__?_?_?_?_?_???_????????_????ߟ????_????߿????_????????_????????__?_?_?_?_?_?_?_______________?________?______ߟ__?______߿__?________?________??_???????________?_?_ߟ?_߿?_?_??_???????________?_?_ߟ?_߿?ߟ_ߟߟߟߟߟߟ?_??_???????________?_?_ߟ?_߿?߿_߿߿߿߿߿٠!, p*ЯJDΉ/8" #rLƇ!n,cFb\ @͛h6*F|he qE dәa'UϙŠuгcӢ|jc>=K@x@_||wK8pdžK_sJVL1~MɄ+,[ r~|b~Nmົiܗ#q}rvU:ON;oH|1;uËO;o=zC$O~};acl2-sources/doc/HTML/flying.gif0000664002132200015000000000071111605147053016044 0ustar kaufmannacl2GIF87a'(0`ٳUeee3f@f@3f@f@f̀3ff̀̀f̿⋙==,'(pH,Hbbl:hB(Zԫ֚zi[  f\|ގ p{rO{xJ}MoHYw{CC|{R|d[We~jl^NXBKşbSϕnџiX٬~BО0sS#F"@!fܘ`@?II$S2<٩ ;acl2-sources/doc/HTML/ftp2.gif0000664002132200015000000000212711605147052015431 0ustar kaufmannacl2GIF89a-,Oe}``@3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3f (((555CCCPPP]]]kkkxxx??6Uv@"4@{$@5b>({$٠!,-,H*\ȰÇ#JHŋ3"ǑF q?Q,II-W v`?Gvgb-z?4>81nMX`ָ_+maߛWVݹAƄ8s ;봭o4}lDs :|{G7^++3BVH`]& ;acl2-sources/doc/HTML/gift.gif0000664002132200015000000000242011605147052015503 0ustar kaufmannacl2GIF87a           !%& *$*$+ ,'.'//$0" 0(1)1*52 5Lc7/#90$:,:";)&<$=$@ A7)H IJ2>NDFOSGDT(@UzV2V:9VI9WGMY)D\]O;__,I_d_``P:`SAb.KbS>cT?hmijYCkU^kWSkXDkb{lJJl[Dndntp_Gxy|hN}~YSZT\d@+^]"Wt\]Ba`i DBK- 1b1EW-=qxɜO"bBL%+D܇T\ry}@N" 0G_xMV(+rx.`2 @x}xr7I[hEr b˓\J\$B`E0 ( 'gad-,21rGh^زR[[` 6]tGh<`K1a)@`gDҙH;acl2-sources/doc/HTML/green-line.gif0000664002132200015000000000016211605147053016601 0ustar kaufmannacl2GIF87a0,'I8ͻ`(dihlp,tmx|;acl2-sources/doc/HTML/index.gif0000664002132200015000000000057211605147053015670 0ustar kaufmannacl2GIF87a#'0`ٳUf3f@f@3f@f@f̀3ff̀̀f̿⋙==,#'pH,Hrl:M N4rK+ի[msp;Nݪ{seqqwuSxinlxl`QpdY~[gzkULvygŜy´zV͌ԀHHA;acl2-sources/doc/HTML/info04.gif0000664002132200015000000000177511605147053015666 0ustar kaufmannacl2GIF89a Oe}66i66TTiTTiiȬiiMMiMMM6M6iM6M6MTMTiMTMTMMiMMMMiMMMMiMȬMMMiMMMMiMM66i66TTiTTiiiȬi欀ii66i66TTiTTiiii欱ii66i66TTiTT׀׀i׀׀פפiפפiȬiii66i66TTiTTiiiȬii=ElM@@ffMMffMMff83H6ZHlRs橀׬&&&666EEETTTiiifii٠!, H\p "J4!C3jh >Cf4ѠRE|"TB1gϦC!QѧIHCH2ӉK/iTHuZET+ҥ6W`?XSi}٠ibHUnˇ< 8` |Ҧ>ˡW-K ޛ??=}×IgT~߁E`?WY!(a .28SN!Zx](n1a_흘hM86:XҐ>d!%)exL$KPv.Y>eIdlٓYf|g)Ey֛xi'gn9ܞ|柋*(hJ袏5,F !yajAjꩨꪬ"+~ܡ)a;*k4iޡ맼jR"[ǰk,T|BbAֆўǖݹ.,z @op `1 0ٰ[=|:9v6G^Gg~o g қo{ɃH؟[ӝ?'?DЯe#W}X@w Z`YǺL'UV<YBn}k`>} l6ao(ӡ Z?{A & Qh5DM D@ȝ&:1<^d.F'^Tbcؕ%#G;qDD(6r@"Iы±#(I it!PE e#EX&#ˣI>1A$S ɎO`riS^ De/}qA*s%(שJc]SX!#,~-X4\_C89NMgwK e6;FW˅[+^r/ܷ-vnv?7/p [: fhCM`5uDc8IEL4Y+9ԛƈG际vfe;k)ViZ_Q!bpANz/meTŨǖ^SmN!KĦ]vصA7rl:e_!zٹvF]#owcJODlGѶ照F%Gю-Q1^gjОm=/l2=/5 NsX v>r5R7_wg}o֧^UqVaoh}0c7 1=nzyaoVWx|' Ȁ]Q7xe{3v?@FXz|wQ,vy;\(}F1Χd@13dkD&TiƃqndW\hHk„V3FFi'iw`\T1`#\jUZBbh f-6idq! X 8^=B8&B6bd/HFh%B^Q"^TbI Z]R{x{x?feCN\F9MʼnQc6k_&JnR_U"ShQ\+vduR6_荀T 7_蘎.b:Em#0eh  ِqe֍_=VQX.^XՑӥmQ"" ِ*O0A8%[c&"hV L@ %%z/JɒzNɒ~j$ae#DX[Y=PqNIhQg¡Z[TUD !GdiI JiMn05ֽ^.".WܾWALLۜ[GC DH'5\*dǗ(>pfC}2m,ɳK")Y Q9@ƔWӥEUI5[TsCjz놤P*}:Ԫh}: -8^:{/4TI$_o>迵ѪTN3>D4l|瘟h8 펁,KVDHa?lPzW:e5 `J( ȡ_XJD|UH_(΅aDp- DLqmh(Wz>xcTP֤11 PꜫT KW`صoGW^Td sI0g(b0}d:[CVX-Szv7e+37ej\Q$mm*Ƶ5'Y7q`܇4eM-sw=RɶrA6Y"2,Os١׼iѹ׽Mlc10_ ټ_nN~ 8~1kk8.kM֯=˱#8Xq ,c;P շ@}e=oDV$\[ H9"1f.b 38SFJ/+n Yf#翆9YD1$.^9!%yaMZlb-*] h)hE±]!m87N]t<\W[nj{tFAzrӜhE/Zq_T.hMoOu[ *xWTYQiul|uO9qn{Xz?z(B=5/kocUlqJ3s\mDC"QuUZ{8&JozO72lm\'w!썜T"T7$~w!Nc#Sh"w_\)yׇu$TAT~E'H'9#u/t7Bgac{?B7tw}F|Ig#6sf/n|6$8TP!h@~nr I,t%/GcV<"L0B>*l( (Gr%kgsס?(B ȣ~vHXr'zwpzJQ 6|q^mhz08 BEIAnVfm&6mRGaXz-|Ї$8dHn^7D`l0el/`nmxs>+" 11ԇ9)Wَ/cDQ\’GyUUq`Qz Pi#r9tvT7  !\kCd4Ci)\V_jIwOcu9iA; A]i3A9 sb LXkĘ#yP9 ؙ)!S@!bR D2]JŖ0ytEu3Hc2ŹAɚva xH sGܙpI/0q0 ђlWNjnJX^HFZ1./J)iE%iR)L=0(2ڡ9<ʟ80.':IɠvÏhh3J] 7;:Ndɣ#~bLʙpu(O3T4@YtjOMJa*'zAXʙ:Om*~49y*jxJЇЙ;:7Y)gDzY8xب(lryNJ5C*XfĦ9ɪ2)stŅdږ$_9ۙ::I Z9}*1׊}I&)׭FZP9ihY{ Z( ( )XdӬw P#9d_:Vٌ)ۛhg)ym'%v'{J kVI:_㰱5J9AUb;& 6{EJTgo@ŽN+zw~zSio 2a,KZZS[`^j1%3·}{4YbTzJ \jo<J0ۛ4ʧױQ KKI˵Ļ䠷C h@i1af "Ҹ:FaS\˝ l3-W0 k k{*=D7|i_; s{BAl[ ۛd0˾x6L~7Y yn*˰ڿšf- ?,{- MC+b'À6A,]Q\[EU9('B6ݖV)Lb,!x3nl aL/OkwMǷǴ7RqBݵIT]V8"PmR]5q]A ֎9 G L9D,]tjw]pm|{)ׂ ]؀}a ,ؓ+]jYنؠ=o-ڤ}{ب`j׬ڟհ=6ٴ}ۜϛ۵c۞m X}c* ܭܥܜp;W} wMm-==]\}mO} ;l܍}P]E{@,^~ >^~6RQ;{ >7$~(../ 4n5~㋋:<>@B>D^F~HJL ;acl2-sources/doc/HTML/landing.gif0000664002132200015000000000145211605147052016172 0ustar kaufmannacl2GIF87aZ[,Z[0I8`(dihzJj뾰tmp@/H1P2J-MX5r/4,f3^l;ӕ{ԮfiloI|)+xA?/x(1"6cƿVgZ+ɼ!Ϳئސ#8!b `@UBp6Ň/jܘqLj?rIt%OUa%o// 8MXr00ϔ(5) *]gˋHJ5AND`g`i݄hZVm%4O@ P˗j[o'\X%U7`t5xR#.Yu6-+/ΐ=KT4ؔR@ԂX WfڵV};7뫥O p\̓cZqխz; U1Ӯ=6< {=t6b!ӝ\vah&]Rz_ zdiيw,61JFߍF癉<޸#z^آsXdJdE'(eC^ieD6d_dzy˃YreX馚U3sYeӖm||ggx҉蝟ɂ曀*)*i&ʩ@jNJY,꫰*k;acl2-sources/doc/HTML/large-flying.gif0000664002132200015000000000226711605147052017143 0ustar kaufmannacl2GIF87a0,0I8ͻ"dihl뾦tm߯|pHTl:ģtzRS+vrC/x53zR@l@{%Nbuyx|9Us@1XZ,e1PM^# ļ)7Ҵr˻0z٪9ބ+fӫaJU HP@Եċ%>Ə #RCȓ(p/˗!%͛Ćg 1'h 6ڋ)FO"=5#7t~Z6Wsi2$6^Uj JOeS3f#\♏|@nIp{T qsc kjիY ۚl;L_~t0l - q~ZzI{noɻJ]楔OOd={![o(>Ϗz`1E&`PxɄ^!nؔabg#:!'z+&"+Rp-̨7a;cH(4W HI$|=Q^WuSf$B LfInLR\)fɉ@C栦l&si"m H=Bu9 jġ&2ј+AifLZhniOjt饩]d@''Z&K`l(Ŧ-n-~kK:irikJn>S/Lv[#RCjކJq^\ą1 Y#s[a h:[4޳²ް̪|ͷN̺{!.7tηDϻ{ }׎^׏;acl2-sources/doc/HTML/large-walking.gif0000664002132200015000000000244411605147052017304 0ustar kaufmannacl2GIF87a,0I8ͻY#dihltm߬Hx@Q`AȤiH'tc66M掓4.QKV{׷6Kuho|}xuARzrtvSc@T;gGw7]ƧȘ̼ΫЗ=Ӈ~PݩH>e~b0Vsз/\pRҖ (h(!:d K#[9;&ChbGSR˗5nY΀0c#OѴ8 -R4ҥ7tSQRj*HZ5hXc:Dt+ZFj- lVd\{$ n;|t3 T5<޾< đG:rGkKPYI#S1ZM"p+<m̸O{ 1⫘O/ۜs2I^um֩w\wŗU{рU wJrU_cÕ>9[W }%VFbTr4!~g \G9 *[ ] x߈~bX^SƜCZ 7@Eg8&K M (/IedYn&ٖ|\6 kM9p@YbJF=z k gxX lf'U{yHIi^fyɦj誰j$JOVi ')ll>l6Kz-ݪl+n˪-֊Yn䮻肫Ko;ʼio뫼쯷3KpIj /3<gLqc"ۆ,몼$\:353 ˼2hj:LFي4C74#>rKl9C-^Nʨ '_Kc_2gͳZMw]}/%S ]bsJvُl7U#8A3uc};nKN0F>ꃃwKx$7ڱnj8>z/ץ~sWnѝ+-=cm^UGC_{ovʼ>4k/G/~@&Ё @FP`-xA5pf[&:j$3APT/|` g<8̡w;acl2-sources/doc/HTML/llogo.gif0000775002132200015000000000110111605147052015664 0ustar kaufmannacl2GIF87a>(BBBƵZRZޜέJRsZ9J!B,>(I8ͻ`(SchljN,tm߸ 7E` 0 HAl8v̨@X,a. ҞZZ!F(p"<8x|[qq tOv W \pATTu F~q`=HW]ij   ^jj¨~Z oH ͥʷf_ JU_{[ [^|b Qf1( ) P@+zԔfn*a|t*'Q,Ѝǖp dF Z ($$Gn YO+Em2B`ր`ٳhӪ]˶08@qj .zn|q^EEb._ù˴6rSx>5Yࢽ*HȟOD;acl2-sources/doc/HTML/logo.gif0000775002132200015000000000756611605147052015535 0ustar kaufmannacl2GIF87aBBBƵZRZޜέJRsZ9J!B,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N/ !<|H~ҹǪΩEҶ ڶԱA^y>N/R2Tki ݯ>"]C#yГ"ЀaG/~ԺM>cboxC]*j? Q$̯~' @ T?P (؃rI(D=诵,اPbbv$́ 1x h:~E+ $&Q\FR@=΀d+P"şQ(#dE~bj\a]Xj 2tkK#2ڶ[!`RТ"G86\! 3".VPYM.(mx~Uw%*{Ar9YɊSe9] bBw$Ty}5o0)#9YgyTW5]vLZ)y>@$deu?ȓ<*QvU,h7:h0W`$btg&]WI g(%zSH4z\ ғԧ(.(mOi!IV+B\ Ң.qT[ARUG@=jX>:V"lݢ[GPhr6k;8 d#8X cmfit͞E+iw/v唰mm}ܵslEv6Oau-mwnn5)F-mX>8s ^7G"r]#j^׽*v;漫`QqTx!VPJpg @ ezA ?q&Aԁ1u"qJAp*q!`@APz> w1]fA3maq2Ȑ*) z w@ kuAˠ  6jYzPAjc:YJT0 F VJq$a*Ɗ J,xj:A_ ?` #+z}@Kkұzњ j*0JL۴N+PR;T[V{XZ\۵^`b;d[f{h);acl2-sources/doc/HTML/mailbox1.gif0000664002132200015000000000227211605147052016273 0ustar kaufmannacl2GIF89a"U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!,"H(TaA0!%R8Ə5((P$G?]"߿[9P+?$߿2 +?w%߿;Χ@ %Vo Hֈ@ɒ&/'#|"w\ [ w\o|>^8W|ؠuw|vh|s>`s> 0us>sWNH$СB0>&t">$q(BX/5w"=@>FVt#>;acl2-sources/doc/HTML/new04.gif0000664002132200015000000000215611605147052015515 0ustar kaufmannacl2GIF89a&'ȬB/~_g7<3ff3fffff3f3f̙3f !!!"""###$$$%%%3f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f33𠠤!,&'H \XpBHdHqĆ%*ТA,@&X$%CQܧqcKa改H.;D "eڬCK,񊥹rϣ_ꔻlᚊ5ƽJ0ĎN>8jV {=rhRkݙ(N*,Z\λQly d7_F2⿭C!ןSKrp^7x.ݸ.=jo~[t{矁BrBPVaA;acl2-sources/doc/HTML/note02.gif0000664002132200015000000000206511605147052015666 0ustar kaufmannacl2GIF89a""U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!,""H*<aÅ 0@"'VxB6Z8!G#Ibp}:%FANҗIвFEjC*@ӊ-3џ?.v߿/)q_=@k};xᒉи0ByoB2 }%-mI-l!TDt斈ooߦ=2{;>D %}1O:ɞuY' ;acl2-sources/doc/HTML/open-book.gif0000664002132200015000000000503611605147053016452 0ustar kaufmannacl2GIF87a^,^0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~ƴ0M/D+=,8*1PGpF 4oaJp|388_E>j cB a$2I(=X[ٲ/0s͟pT'p!MH#DäP-,e Q^:-E>wZ.uVƂ,ӳl* ӧܻsz[X#aˆ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺ븆C XوwI䋛ێpw) '>8Uh,gHsS?d +oOԽw ŏ^ֶp*|ow^ xH4 eQx>G/VQ>p``8 D"$&TJ#`T*2RI6JbK>J#|v>fw$~ԦF%Xy\ ,`)dihlp)tixy6#4|Iu=)h#;uh'h3~h֣ (iUx//z)B]L~D.ZE`*pȪQEDP*"*]A,=K**NmTNkKrD]m+.U;n {&ѨG:[/ -*{zϖg 7G,WLXp3rqQ &C8r2(|2st܌: G>AsoMFFJ+D͉o~y z?`N΃Ǔz9{ nH; ^TWN ?Z=χ]{G?p1Y}X}Ԍ`zGG,wOxMK~ tЉ[- ʟ}p>-6\{ruKYScr }Ѥ>Ig@PdBFYؿHE*HUY$ei8/3э D8qlo٨Gdt+_!! _^6D.Dd$EAZ!#)I/(}R XQCt'Ļϔr8v0)eRD]U27"-ªY]2)boR*˖-ѬZyĵ)茧:m+=)r)hɚAe)qBr@yE1mwg:YJ}e IM:It?a6i(PNc>zd2Ԋ擎!}zC05(BsjR^T68/gStR%jOJ !aiBPn "|kToU^uNf=Z# ;:8A[֔թKw8K'#+fJSu^ο$kzⵑ8Qtf7?8}SuW:7i,RƳ]FE _hV2BB&t,m&>nۮa{SВeEwׄ,ﱯZo ^Ya}u VK-or!꺼Z t5Wo<\ W8pmد&1C,LfX hnb131 dxL5vLbұJ񏹹-%|UetҼgRɔq`eܖ&kN&hr18I.8!Z7Q3fkă=xaj32K[AWI44] g'vzuiI_)*H8ȳ4Tecԩ@#tl?SSCdy.HƵ;)IUb>%)phlz̾ t3f7mOs^unw鼩T-#{m7{xl"?9yA򖻼fkAU<*Ϲ^%om4ǚ͉iK\|5 ء;Ul2TD 7I+ss3w׿R=/fzLˌvtڙv{:_Wc^}ȉytʽmzū:㎧<,="R0J1yD%$Mo$߈g\/M?5_86/_+=Hvw.}{ž{OOO;acl2-sources/doc/HTML/pisa.gif0000664002132200015000000000336611605147052015520 0ustar kaufmannacl2GIF87a,0Iͻ`(dihlp,lxF aK:tJewzwLV{sY{'V-$h r]|xt* 7MAH9B ҁM;OݓR?Wϟ7gp# ,[C|*ʻXJ9ىh'#8v4y%GjP#wnҌ'\??љ:U/Ӡ LFu*NW.uժѾK0I/_:mR8ʤAmہ[ƲQ!CE)G|"9Fh/8:\ɽt;?W8wE1ǎa)Oan^t{JꡗxfxC]vҐONHqt!TdȂb~)"0֘anTa"M= ZV⌡iu]DR[5hKi%}a1)֑T*)cnY_9T evex `w'jyjl> gI_i٨q(F^tBBx]Rf:eSzf!P'`:ֵץ w:(םyĒjVPੳe 7EYWԶ+G>݈ZaZ nIZJ.LiboD "c+p薕/ x \|[* %yf"Ki,{r)wg~W:l*T͎}F+ elR[LuQuoݩ+~a* Ǣo`oZ$9,2p$Cq4^R7 }O0>n3k<ulzxW>Snt R^#Lgho`Qw5#6(G*a?,FS }Vf>wou;5{:anvUb"5HC_S *j 2X8Jb uC I GvH kP{[x*qH .,D~ñD$ \b" =pE 6AmA`1 N|9Xq[\x 0%2vQ]ST+a8*w:,]!G2H(d} "E86!ӻm%{`d5MR I[ZAF [yu *֜]"937 J0Lb;.W<Ta jCn&|lu/m(5(27P S;;!#=%!QMFE55NeG1( rfjSe:Snl8NN)YcԵ}wժZ!2Gl+ bc{AZͬf7zH;acl2-sources/doc/HTML/proof.gif0000664002132200015000000000251611605147052015705 0ustar kaufmannacl2GIF87a,0I8ͻ'dihjjp,Glx/',8"rlΨTVIЩ6cO@&{==+u87zv'rmHyzqOf%|5` {X]^ /z4" !l,Wj&hįxWjMԨ^wXKXX:=Qd&$\4ؽH\ڬŵZ@cϤ\0CPL ϮΔ?#^ic}d!(gN>A&5'ޗ};u祈+_|yiZͣ7-dnߞ!CS?>ɟOB:> M4NE }Úv$`ǐBrQt]Ȝ{qěF~bnAfc:aw #7\FB"x6Ch@ȡy$~YWn,%"F)hn لj.1d~i\ƩB@aW"L%OzRӢ* FwFTj0iZ)ꚢF5x꫚zj rp[+UR\IڰpFZ VDkbeB2̵Bۭg-3|xW`Ζ+kn1)bM[k{-W~u֍{o_gaSϥ@ydiۖ,ʢ;+@oy̙҄}~O]Zm'|Q6X} w4`_7#[~7a qġfb^2b6#}uΨ_ ި^/Um QyAXأ!&dڑ]飒dc0J9`*$tQchؙ9h}nř{tfaf4ɜj"gqբbɥ',&\Y8 ؛aPY}zi=Z])*+~*,֚:j,> NlfF;acl2-sources/doc/HTML/stack.gif0000664002132200015000000000352311605147053015665 0ustar kaufmannacl2GIF87a,I8y`(duhl뾬)4 x[kpH͊#r,'tJ픪Nv rޯctom.v:>{<|I~BB89C S½'ǼXĵó@vϳf[ѿܭ[ 4v2a!C" J Iĉ/ЈaIIC*2h˗/T1Z8qJFM` *UPj"dt`&J!:V`%8 4SD D.D pQV<5(mtj'J6a(`bu4d 8HZ~|D-TO{Op5vfc63$w$Yꪔ@Crri4I'N@\RɈuҎ2~.Ȼؾ>"QaBi Nf,Y&Ps?AM,thiTc;gG.P<FZ鸸g3|.IQCTx75bN SJd+(6:Oy@:ɢd{U_@Uwa5NݪO}eY7ִMu+Ě1vB[W5|^Ghsa=X."-**1eنtݬ'Y*hkPa+mH[R-dY 72 m[ܨ:Ws;acl2-sources/doc/HTML/state-object.gif0000664002132200015000000001645711605147053017156 0ustar kaufmannacl2GIF87aЀ0, dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~,a/;-"#iP$=ўٷħĸ H*DrpÇq4HE/jܸ0# SlqN]Ȓ&9\ǓqK&̙.Yܩ&g'pSf2H͔Cmy+NV+2]mdnQv6hӪ]˶۴fmUa̫]DWۿo/J5<0#2/_'sf`KTmz3ePz5n`zwp۾T{}&i7Իr'ʃ4%?cThO-rz[f8TbJH2A[zGMb4܁R)KHԙZל;6(|TSK 2a7SQ3F(LjY6 *>5M6,4SYAh#RQԥtHd "_^ȸ#!G%>:5NOdDX}eHИVd|SZdƙ\$@rFAҟ*蠄i}mEgUVj饘f馜&(Bz1bBꫝ~:Pb&*ꪬ=Ɩs첕KVk.@{3vˬE>J ےK%ڑ̵U[/m7$;ipkϾ`G1(`gl1t wLc ^o .`2L4<#`$,Īhl4#ˀɛ<+lu5\0OO|=s'AO-1GJ7PK)U_ sրլSɈvcsUk ow^@v\s`sK>yчkL[C׏PWx.6赈N5# !3N8ԱWN{Z `F߁:l'rpvV|mJ}yj;3Af n\hVj!H ,F (a-_(e=ψ1L)dUA"esKJA;@@(81y9k"|L ǴE -Z!ĨFe_IT`gû=1ۚQż;=Na*'CB3tc<^c {HR#HFl2c׿.4Pa#/Qc[5]6[j|@ֻ̓"s`hBIknM}rւK疈fעXC}X{o/nM)O`V^G96+ =M&2RXP$lxP>N@< 1kן'}t]<@& |6 U*eSv3 qlW} 7y'_rG_޷k8 Hq hxg"}qxXs3S.x?` Gv%hr,8{#vp&H7 E92~moR Gh5r#HKA~TCP P17Fږm1R|mW d8fxq]LWH%iS|@*~DŽ.0 Wb'YlD=`PwwTitR"_~uLj Bl88<Jxa7RH((zЀ'I|bWfŒ"H`e8G KXpqoag$` W 5 0Xl Q8+ޤE\#-荞}e"6 %׏/Tt194q(wmx"ljkؐVV""vғ ّ+y#wXd$m~WJx18_])9BW|fI?)AY y$e6" K \YO%SQFV3/X|mxBka X`z'CEH$[ 3~b %yR)ٗ)pƗ%P] 7X薵4#s^h#f}5k)e(0tyŚ$pbIBy@ )a5/wiYI[ʙ0OQ)ҹբhX$YIaӦY IR.mIWxZ9x|aƞvY!z 7Xe߈c4Yw6u9B7v8v @TvYf,pW֢k2@)E8ʟY '@ʄBh]S9-`HqJ{LgȕP{ ǣV:6:iH0zȩY/чh~QUjWYO( *\/2cqQy񃟶 L*2& ;mzyN{ZYZb:nz6 XNءx0pRq yԪ+ږxZ7У sنz  tZIꚛnJ ^ Yvf$ M4а ]jQtʩY +A k֚&[FMԢ3zQ 5068 ;$;oYaIvjD3dNK:;h3ډ}ȨR٬F麟I`,kUz9nJ4ú{s5ٯ*=IW kr_8RkziT'ozk|{AFq KPVfi!R.o ,6yL}욫›Y e6?#`~ظ'E;۵b{T`YPx^@paA@[5)+x'æʿ~۽# ,墨r *HPH$sz:BXP9x ĖAˇXZV겛G+{v#YFq%ƴaıC~ۻ?L[m<'&`d$ Cg6ܚdL:9ǀȀ|̱k  l)4'2;z\ !V \qU{l09ǚ,Ȝ2 <{L ܡesr rK.hJ\KIɳ=|JurVO2{ ҅m52XB ȷ_Ki/Y̼#ؕ!aOG7G$G6͗rMgs f~ :1Wt&KFQY]ݒ& Հ>K]]wJ-n+]8 CX֙)(p]A:3qE Emh-k (Y]*~ L=]W]>:#FaQG=ز;{-+N*.K=5}$6`WޭvŒDX3j٘]׆H) Ns>&J\e>]`9>'Q-Ζ< ݧ~ñ|٘P!9#`d,cen9nn㟾4mbžk窮.|>Һ^¶NEPsV)n^.>Îh^Md촖)^3q-p{1 ~2QDiO}4a~qj 0 KN^mam !/ji@xmrD }&& :l0E1@ݜdP=D^8?eBV /핝SV[Rײr9_XXb/g?ʸU ۧL?`1X/j|o${ָ%=h_F(O׌f޳o_Jo!XԎ}-sMXʎhc2-:oJycL0~.-FܜP? g+a )pr #Y' /3 7~fГG@a[r8&4*)6rޯ/k^)ìNwp|/~ Z aN! bbc䔕de `M% h('Sj&*,#$R쭮 ٮoT/c e睝\]o-kpڳ!^vf78fU0RҢtj_g,m}u0?Mf ݞv6oBO[2zË:"(HV le^j%RhM "Ŝ$^cEl1a"uFO1)e(G?űm9Du!. G3 ҐQ#ozmq;S$&CH3dH)Uʒ dĠJV2r]򲗾%*wZS|$icf]`h:t%2`Ӛެ 5(M1KtY@m>K<'<NT'>93S?ȎuП}f8LebB aʖhҩN6䳣>GeQ4IKR.iԊ#])L!Q46Lқ9eiM{*TyԤA*RYTiI}-գBjSUrgX-JѮuMb+#:ֲUjEk޺ֶr+^Z׽0~*_+=,b2},d#+R,f3 ;acl2-sources/doc/HTML/teacher1.gif0000664002132200015000000000232311605147053016251 0ustar kaufmannacl2GIF89a !U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!, !H !Ç#Hx 3jԘb A I~$]6[^L9si2AC3&uyqF뷏*Ztz1@Rz -WPbkm@[o߾2cˋv{R~/ŷϪˋ{Jճˋoy5ˌ{RϥƷ˺4i:QTs~S^lyjY&5@l2"ǻ ‹9m(`Ղ6/2}cKSKR?OK??-=OK1OK ?ON}OK ??OK????t???BUBUVYUVYeQDE;acl2-sources/doc/HTML/teacher2.gif0000664002132200015000000000201211605147053016245 0ustar kaufmannacl2GIF89a U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUR-UUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ޳UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU/OOkkk!,  "HpÇ#J|@3jhƏ9AQt(}b/ʙ0Y $ˌ)@rѳhHTrg(OCfN1sZD+[* UbϕxV⎍ ۷q-nJ+*LW &ׯ[2'B.pYϠ-aҖV}VvM[`O٫k=[k|.7⭉#;acl2-sources/doc/HTML/time-out.gif0000664002132200015000000000204111605147052016314 0ustar kaufmannacl2GIF87a9,9I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~K>BhķȻƽ7Դ˭٢d妡Уܡ9նꧭSM_{!Fyڻ`ykHnۮg o-73R.m%^D_,G#&qP9kZQ 4FsNftn5AZBMA^ף(_򩵥N[ʴXUҾn]R-͐.kK ,"Zw\seE,&pȄVEلZ7ƝX*[fqS{H,ӐQ=-jO Ίw*ڶ(^ŵX{M!yDC6[2YwH&߁5ȑ?$ՖQԁ CU;5q#Jn8iH _hLV:?וa˖z5&m4+,xAǤ +pwr)<޽= ;acl2-sources/doc/HTML/twarning.gif0000664002132200015000000000010711605147052016403 0ustar kaufmannacl2GIF87a ,  =B%A/[\$;acl2-sources/doc/HTML/uaa-rewrite.gif0000664002132200015000000000664011605147052017007 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.q`nש|Nzz%ymkp$k_k#ELj Ϙʾq٩s8l19"[6*ڨݾI#GȵI,iIC1 08EWl#S@^b?RmLu[C<<†pɍDkltAd#"1O<1.l p'͚tZ*?E qo hMYquu՗wT&]eH+,l2:뤹}ӆja7޵Η*U3gw5>.[c+K̳#6ۻ * W9Ma ĉd,){vܻlH,d觋n3-hݛ>B߁PY4~YjM?TJ`+U+ݷw3'F"wPY/'`z?߀82|F(܄9X!? h!G2`l!PJ`j!s"/c~-g׵XBu1^4zBHb69z =ϒJYnoUɧt^W &t4&iiflhIg3֙ND'| *(gn2+7Y$~>NQJZ v\z̗8j4*nB^*+P6ipA,>뒆N[eUf/Q`Si?1;7X*gIoXs\oV$vŘ"+e-M&5ꖄHL'_tH{2/9-˄>Tt^,C]^r3uj.ܒKTꎞ[9Up}.*=Xavn]/P|Y;6x)λB o6!/~o~IYq`@o kH U/  BH .# gz0 aF0L AB'p08f2: CP @ȋ"rH!HO8qP4B."2 `̀ 3ZH2xt-@G1 dX>L"@H 2\$'YFJ8$%7XQd'GIJ|(KVπZt,g O2.w)[ &|U $f&Ғd3I{g=O~{7{NP$ ꡇTh"Ru7֭S"sa.L.RkYH)*G0TijWH\*M)>]|P6Ӝ_T)QWZIJmUy` 66%e#sSiK:)I H.9k-rc4E]ר"aM>8,orM'i߀X7,ih6ژ_WᶝdTeSrK[mqZ/Dkzi*7R(1bXɒq*&%=SFZ6e\Dܩ5[­ɼ_%p0wY96q^|S0}[+TN~2|a&ɵu.x r]'K,>6굚 YjUqPE(oq{ݳɉ85\Y"WVvO _|J87G+7 Tӄ_-[ %]nF2;oa^rhܕquu "'RFv =ej\slKeyӫ N3 Q7CPZNTϤn+Pd惶 o6QemΨ{}Ecy42iP혐!Ne~ Y_:vq?RX|2e,mhtօKϭF4JǝwyԕKA^ /+8Zg~ǻ)P[8G(΁6;F_T@gP8P<$sΓ}B:%`s|P9Nt|=/1sI_{<Q=Quà[n[ bO{^_j;^W`{mv] z5`~ 7P;9[^t?d/yD>@;O|Kz[OZF!}w~_y"ȟ]>_~ CgIA_!w"_!?߾:!a_P`~wٷ7rp5(7R NduVCvW)Ԗj('N\u+`K*X %o4Ђ#rbV+x .XF0HNA(`88rp)%;1׃̅g5R13fdFcsq0vW7=oKX1c$xgƅRI&)C=SpUC,` )5%&_:ց7rjօTwe4|8ehS"K+ږnp-6iı330[73RYeYmV5U1YV_z6Rňֆ8V:'6ӌFgd /]R+u]Rj#1(*n*fB.gC2o;.&oF/3sI/60h$0;3Bgֈ!dhd(1fvqf6}v8=&R2hEf^<pwp h cU(bp#c*}629=؈ؐ\ߨpS> 3Ye٥89%V"hԦ.r #&iX hX/A… )ܨ0Yy(d7Zos֓mQ:ҁo6'mI撏t#idb+X4IH3Rs] jɀ4l`TScH:i[j6tWЀi7(_b#95X"=XH29+9l8oi94x(ob"2,Ȕ'Bɣ<qcc!<ɟ'G<wn]I >5":á&z(*,ڢ.0;acl2-sources/doc/HTML/walking.gif0000664002132200015000000000045611605147053016216 0ustar kaufmannacl2GIF87a'(,'(I8LSiZhn;Ӝb;^WlE :"WӤytS!xnw/u)Yb,C`;34siAc*XnPwXvtf`nYj_Y{uq |~PPl& :w ĺ A ټؼA}p_*dBE ;acl2-sources/doc/HTML/warning.gif0000664002132200015000000000032711605147052016223 0ustar kaufmannacl2GIF87a&&,&&`ZnX ^@ _Xw9,.\޽LakTj~Ԩc"@WׯRɘf"y[գh;_֗DwXfPhiw8ɈB2s Dӈfk[YKz<R;acl2-sources/doc/HTML/installation/0002775002132200015000000000000012222334002016555 5ustar kaufmannacl2acl2-sources/doc/HTML/installation/installation.html0000664002132200015000000001231612222333514022154 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide

    ACL2 Version 6.3 Installation Guide

    NOTE: From time to time we may provide so-called incremental releases of ACL2. Please follow the recent changes to this page link on the ACL2 home page for more information.


    Easy Install for Unix-like Systems

    Here, "Unix-like Systems" includes Unix, GNU-Linux, and MacOS X.

    NOTE: A quicker and even easier install may be possible, by instead obtaining a pre-built binary distribution if one is available for your platform. Otherwise:

    1. Fetch acl2.tar.gz into a new directory, say acl2/v6-3/.
    2. Execute the following to create directory acl2-sources/:
      tar xfz acl2.tar.gz
    3. We strongly suggest that you fetch ACL2 community books, for example as a tarball from the Google Code website, into your new acl2-sources/ directory; then execute the following in acl2-sources/ to create directory books/:
      tar xfz books-6.3.tar.gz
    4. Obtain a Common Lisp implementation if you don't already have one.
    5. Build by connecting to your acl2-sources/ directory and typing the following, which may take a few minutes to complete. Note: You will need Gnu make. We have seen problems with Version 3.81 of that utility, so if you encounter errors, please consider our instructions for downloading and installing GNU make version 3.80. (That said, in builds with Allegro Common Lisp we have seen problems certifying books with GNU make version 3.80 that seemed to be solved with version 3.81.)
      make LISP=<path_to_your_lisp_executable>
      
    6. OPTIONAL: Download gzipped tarfiles for workshops books and/or nonstd books from the Google Code website into the books/ directory (but first create books/ if you skipped that step). Further instructions are here.
    7. Certify books (but first create books/ if you skipped that step), for example as follows.
      make regression
      (time nice make -j 8 ACL2=/u/smith/bin/acl2 regression) >& make-regression.log
      
      The resulting log file, make-regression.log, should contain no occurrences of the string ``CERTIFICATION FAILED''; a normal exit (status 0) should guarantee this. If you want additional explanation and further options, see the documentation for BOOKS-CERTIFICATION.
    You now have an ACL2 executable called saved_acl2 (from step 5) and access to certified community books (from step 7). Enjoy!
    THE ABOVE INSTRUCTIONS MAY BE ALL THAT YOU NEED. Otherwise, read on....

    More Information

    acl2-sources/doc/HTML/installation/installing-make.html0000664002132200015000000000334112222333514022530 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Installing Make

    Installing Make

    We have seen problems with Version 3.81 of GNU make on Linux (though not on a Mac). (And in builds with Allegro Common Lisp on 32-bit Linux we have seen problems with GNU make version 3.80 that seemed to be solved with version 3.81.)

    Here are instructions for installing Version 3.80 of GNU make on linux that worked for us. (Note: We have both 32-bit and 64-bit OS machines on your filesystem, and after we did the following on a 32-bit machine, we could use make 3.80 on either 32-bit or 64-bit machines.)

    1. Change to a directory under which you will put make 3.80:
      mkdir Make
      cd Make
      
    2. Download GNU make version 3.80 from:

      http://ftp.gnu.org/pub/gnu/make/make-3.80.tar.gz

    3. Extract/build/install:
      tar xfz make-3.80.tar.gz
      cd make-3.80
      ./configure
      make
      
    4. Arrange that the new make 3.80 is your default make. For example, if ~/bin at (or sufficiently near) the front of your PATH and DIR is the directory in which you downloaded make-3.80.tar.gz as above, you can do this:
      ln -s DIR/Make/make-3.80/make make
      rehash
      
    5. Test that you have installed make 3.80 as your default make. The following should print "GNU Make 3.80".
      make --version
      

    [Back to Installation Guide.]



















    acl2-sources/doc/HTML/installation/misc.html0000664002132200015000000001526112222333514020410 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Miscellaneous Information

    Miscellaneous Information

    [Back to main page of Installation Guide.]

    Table of Contents


    Please let us know if there is other information that you would find of use in this guide.



    Problems

    If you are having problems using the `make' utility, be sure that you are using GNU make. We have seen problems with Version 3.81 of that utility, so if you encounter errors, please consider our instructions for downloading and installing GNU make version 3.80.

    Building ACL2 may fail with 32-bit SBCL because of insufficient heap memory. Harsh Raju Chamarthi points out that a fix is to run SBCL with an increased heap size limit, like this:

    make LISP="/sbcl --core /sbcl.core --dynamic-space-size 1024"
    
    He adds that this issue occurred while building ACL2 4.0 with SBCL v1.0.40 on linux, and the default heap size for SBCL on a standard 32-bit linux is 512MB, while for SBCL (64-bit executable) on a 64-bit linux, the default is usually 8GB.


    Reasoning about the Real Numbers

    ACL2 supports rational numbers but not real numbers. However, starting with Version 2.5, a variant of ACL2 called "ACL2(r)" supports the real numbers by way of non-standard analysis. ACL2(r) was conceived and first implemented by Ruben Gamboa in his Ph.D. dissertation work, supervised by Bob Boyer with active participation by Matt Kaufmann. See the documentation topic REAL for information about this extension and how to build it, and a warning about its experimental nature.

    If you care to use ACL2(r), we strongly suggest that you first download the non-standard analysis community books (gzipped tar file) from the Google Code acl2-books project website, and save to the books/ subdirectory of your copy of the ACL2 distribution, say, dir/acl2-sources/books/. Then extract to create subdirectory nonstd/:

    tar xvfz nonstd-6.3.tar.gz
    

    Next build an executable image and certify books. First, connect to your dir/acl2-sources/ directory and execute

    cd acl2-sources
    make large-acl2r LISP=xxx

    where xxx is the command to run your local Common Lisp.

    By default, if no LISP=xxx is specified, LISP=ccl is used. On our hosts, ccl is the name of Clozure Common Lisp (CCL), which can be obtained as explained in the Requirements document.

    This will create executable saved_acl2r in the dir/acl2-sources directory (notice the trailing r in the executable filename).

    Finally, to certify books under directory dir/acl2-sources/books/nonstd/ with ACL2(r), stand in the dir/acl2-sources/ directory and execute the following command.

    make regression-nonstd ACL2=dir/acl2-sources/saved_acl2r
    



    Links and Mailing Lists

    There are three mailing lists for ACL2 users. You can post messages to these lists only if you are a member. You may subscribe to or unsubscribe from these lists, or view their archives, at:

    Finally, please report bugs in ACL2 to Matt Kaufmann and J Strother Moore.



    Export/Re-Export Limitations

    ACL2 may be exported to any countries except those subject to embargoes under various laws administered by the Office of Foreign Assets Control ("OFAC") of the U. S. Department of the Treasury.


    License and Copyright

    ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp
    Copyright (C) 2013, Regents of the University of Texas

    This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright (C) 1997 Computational Logic, Inc.

    This program is free software; you can redistribute it and/or modify it under the terms of the LICENSE file distributed with ACL2.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE for more details.

    Matt Kaufmann (Kaufmann@cs.utexas.edu)
    J Strother Moore (Moore@cs.utexas.edu)

    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.

    [Back to Installation Guide.]



















    acl2-sources/doc/HTML/installation/obtaining-and-installing.html0000664002132200015000000007016212222333514024332 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Obtaining and Installing ACL2

    Obtaining and Installing ACL2

    [Back to main page of Installation Guide.]

    Table of Contents


    ACL2 is more than just the executable image. You should typically obtain the distributed books and a local copy of the documentation. Start here and we will take you through the whole process of obtaining and installing ACL2.

    First, create a directory in which to store ACL2 Version 6.3. We will call this directory dir. For example, dir might be /home/jones/acl2/v6-3.

    NOTE: If you intend to obtain an incremental release (e.g. 2.9.4 as opposed to 2.9), please see the ACL2 News for instructions. Otherwise, continue reading here.

    A collection of books (ACL2 input files typically including definitions and theorems) has been developed over the years for use with ACL2, called the "community books". It is very useful to obtain these books, which are distributed from the Google Code acl2-books project website; directions are included below.



    Pre-built Binary Distributions

    Visit the "Recent changes to this page" link on the ACL2 home page to see if there are other shortcuts available.

    WARNING: Some of these packages might be for old version of ACL2. We recommend that you use the latest version of ACL2 (Version 6.3).

    • Linux/Mac/Windows Binaries in ACL2s

      The ACL2 Sedan (ACL2s) is an Eclipse-based IDE for ACL2 that is distributed with pre-certified books and pre-built binaries. If you use an alternative development environment (such as Emacs), you can still fetch a tarball for your x86-based Linux/Mac/Windows system that contains a pre-built binary of (pure) ACL2, using the following instructions based on information kindly provided by Harsh Raju Chamarthi. Just extract the appropriate tarball (using tar xfz on Linux or Mac and unzip on Windows) under a path with no spaces. Then run the script you will find, run_acl2 on Linux or Mac and run_acl2.exe on Windows, to start an ACL2 session. (Note that The first time you execute that command, the certificate (.cert) files are automatically fixed, according to the full pathname of your books/ directory.) Also see the ACL2 documentation topic on the ACL2 Sedan.

    • Windows

      In the past, a Windows Installer for ACL2 has included a Unix environment, pre-certified standard and workshop books, and a copy of Gnu Emacs. This capability has largely been superseded by the section on Building an Executable Image on Some Particular Systems and the Shortcut using the ACL2 Sedan, above. See also information about ACL2 on Windows.

    • MacPorts for Mac OS X

      ACL2 versions have sometimes been made available under MacPorts. If we learn that an up-to-date version is available, we will add instructions here.

    • Debian GNU Linux

      A Debian Gnu Linux package is available, which is likely to work on other Linux systems as well. Thanks to Camm Maguire for maintaining this package, and for pointing out that as Debian packages are simply ar and tar archives, they can be unpacked on any linux system, and who has said: "If someone is running Debian, all they want to do is 'apt-get install acl2', doing likewise for any optional add-on package they wish as well, e.g. emacs, infix, etc." Alternatively, Debian GNU Linux users may wish to download the Debian package for Linux. Or, see following annotated log, provided by Camm Maguire, to see another way to proceed.

           camm@localhost:~$ cd /tmp
           camm@localhost:/tmp$ mkdir a
           camm@localhost:/tmp$ cd a
           camm@localhost:/tmp/a$ wget ftp://ftp.debian.org/debian/pool/main/a/acl2/acl2*5.0*_{i386,all}*b
      
           ;;; or ftp, use browser, etc.  I.e. download the .deb files.  Replace
           ;;; i386 above with amd64 if the target is a 64bit Ubuntu machine.
           ;;; Other target names should be self explanatory.  The 'all'
           ;;; designation refers to binary independent data. 
      
           camm@localhost:/tmp/a$ ls
           acl2_5.0-1_i386.deb	   acl2-books-certs_5.0-1_all.deb   acl2-doc_5.0-1_all.deb    acl2-infix_5.0-1_i386.deb        acl2-source_5.0-1_all.deb
           acl2-books_5.0-1_i386.deb  acl2-books-source_5.0-1_all.deb  acl2-emacs_5.0-1_all.deb  acl2-infix-source_5.0-1_all.deb
           camm@localhost:/tmp/a$ for i in *b; do ar x $i; tar zxf data.tar.gz; done
      
           ;;; unpack files in current directory
      
           camm@localhost:/tmp/a$ sed -e 's,usr,tmp/a/usr,g' usr/bin/acl2 >tmp && chmod 755 tmp && mv tmp usr/bin/acl2
      
           ;;; Edit shell script wrapper to refer to the local path
      
           camm@localhost:/tmp/a$ usr/bin/acl2
           GCL (GNU Common Lisp)  2.6.7 CLtL1    Aug 22 2012 15:25:31
           Source License: LGPL(gcl,gmp), GPL(unexec,bfd,xgcl)
           Binary License:  GPL due to GPL'ed components: (XGCL READLINE UNEXEC)
           Modifications of this banner must retain notice of a compatible license
           Dedicated to the memory of W. Schelter
      
           Use (help) to get some basic information on how to use GCL.
           Temporary directory for compiler files set to /tmp/
      
            ACL2 Version 5.0 built August 25, 2012  11:53:08.
            Copyright (C) 2012  University of Texas at Austin
            ACL2 comes with ABSOLUTELY NO WARRANTY.  This is free software and you
            are welcome to redistribute it under certain conditions.  For details,
            see the GNU General Public License.
      
            Initialized with (INITIALIZE-ACL2 'INCLUDE-BOOK *ACL2-PASS-2-FILES*).
            See the documentation topic note-5-0 for recent changes.
            Note: We have modified the prompt in some underlying Lisps to further
            distinguish it from the ACL2 prompt.
      
           ACL2 Version 5.0.  Level 1.  Cbd "/tmp/a/".
           System books directory "/tmp/a/usr/share/acl2-5.0/books/".
           Type :help for help.
           Type (good-bye) to quit completely out of ACL2.
      
           ACL2 !>
      



    Obtaining the Sources and Community Books

    Obtain the sources and place them in directory dir as follows.

    (First, a note for Windows users only: we suggest that you obtain a Unix-like environment or, at least, download a utility such as djtarnt.exe to use with the -x option on gzipped tarfiles. WARNING: At least one user experienced CR/LF issues when using WinZIP, but we have received the suggestion that people untarring with that utility should probably turn off smart cr/lf conversion.)

    • Save acl2.tar.gz on directory dir. (You can run md5sum and compare with acl2-tar-gz-md5sum if you wish to verify the transmission.)

    • Execute the following four Unix commands. (Note: Gnu tar is preferred, as there have been some problems with long file names when using tar provided by SunOS. You may want to use the -i option, "tar xpvfi acl2.tar", if you have problems with other than Gnu tar. You can see if you have Gnu tar by running "tar -v".)

      cd dir
      tar xfz acl2.tar.gz
      rm acl2.tar.gz
      cd acl2-sources

    • We strongly suggest that you now install the community books, using one of the following methods, while still connected to the new directory.

      • Easy download. Fetch a gzipped tarfile of the community books from the Google Code website, and then extract as follows to create a books/ subdirectory:
             tar xfz books-6.3.tar.gz
      • OR, obtain a read-only copy using svn as a new subdirectory, books/:
             svn checkout http://acl2-books.googlecode.com/svn/branches/6.3 books
      • OR, for read-write access, become a member of the acl2-books project by contacting one of the project administrators (see the acl2-books project page). Then:
             svn checkout https://acl2-books.googlecode.com/svn/branches/6.3 books --username <name>

      NOTE: The second and third methods provide all of the community books, but the first omits the workshops/ and nonstd/ subdirectories of these books/, which (respectively) are books contributed in support of papers presented at ACL2 workshops, and books for use with ACL2(r). You can fetch these easily as gzipped tarfiles, as follows, using the following links to the Google Code website. Then put the gzipped tarfile(s) in the acl2-sources/books/ directory, connect to that directory, and extract to create workshops/ and nonstd/ subdirectories, respectively:
           tar xfz workshops-6.3.tar.gz
           tar xfz nonstd-6.3.tar.gz



    Creating or Obtaining an Executable Image

    The next step is to create an executable image. The common approach is to build that image from the sources you have already obtained. However, you may be able to take a short cut by downloading an ACL2 image, in which case you can skip ahead to Summary of Distribution. Otherwise you should click on one of the links just below. Choose the last option if you are using a Common Lisp on which you cannot save an image (e.g., a trial version of Allegro Common Lisp).

    PLEASE NOTE: The available memory for ACL2 is determined by the underlying Common Lisp executable. If you need more memory, refer to your Common Lisp's instructions for building an executable.




    Short Cut: Pre-Built ACL2 Images

    The site http://www.cs.utexas.edu/users/moore/acl2/v6-3/distrib/images/Readme.html contains links to ACL2 executables and packages. Each -md5sum file was created using md5sum. We may add additional links from time to time.

    Now proceed to Using ACL2.



    Building an Executable Image on a Unix-like System

    We assume you have obtained ACL2 and placed it in directory dir, as described above. If you downloaded a pre-built ACL2 image, you may skip this section. Connect to dir as above and execute

    cd acl2-sources
    make LISP=xxx

    where xxx is the command to run your local Common Lisp.

    By default, if no LISP=xxx is specified, LISP=ccl is used. On our hosts, ccl is the name of Clozure Common Lisp (CCL), which can be obtained as explained in the Requirements document.

    This will create executable saved_acl2 in the acl2-sources directory.

    The time taken to carry out this process depends on the host processor but may be only a few minutes for a fast processor. The size of the resulting binary image is dependent on which Lisp was used, but it may be up to a couple hundred megabytes or so.

    This make works for the Common Lisps listed in Requirements document on systems we have tested. See the file acl2-sources/GNUmakefile for further details. If this make command does not work for you, please see the instructions below for other than Unix-like systems.

    You can now skip to Using ACL2.




    Building an Executable Image on Other than a Unix-like System

    Next we describe how to create a binary image containing ACL2 without using the `make' utility. If you are using a trial version of Allegro Common Lisp, then you may not be able to save an image. In that case, skip to Running Without Building an Executable Image.

    See also Building an Executable Image on Some Particular Systems, in case you want to skip directly to the instructions in one of its subtopics.

    Otherwise, proceed as follows.

    Your Common Lisp should be one of those listed in Requirements document. Filenames below should default to the dir/acl2-sources directory; please connect to that directory before continuing.

    1. Remove file nsaved_acl2 if it exists.
    2. Start up Common Lisp in the acl2-sources directory and submit the following sequence of commands.
        ; Compile
        (load "init.lisp")
        (in-package "ACL2")
        (compile-acl2)
      
      The commands above will compile the ACL2 sources and create compiled object files on your acl2-sources subdirectory.
    3. Now exit your Common Lisp and invoke a fresh copy of it (mainly to avoid saving an image with the garbage created by the compilation process). Again arrange to connect to the acl2-sources subdirectory. In the fresh Lisp type:
        ; Initialization, first pass
        (load "init.lisp")
        (in-package "ACL2")
        (load-acl2)
        (initialize-acl2)
      
      This will load the new object files in the Lisp image and bootstrap ACL2 by reading and processing the source files. But the attempt at initialization will end in an error saying that it is impossible to finish because a certain file was compiled during the processing, thus dirtying the image yet again. (If however the attempt ends with an error during compilation of file TMP1.lisp, see the first troubleshooting tip below.)
    4. So now exit your Common Lisp and invoke a fresh copy of it (again arranging to connect to your acl2-sources subdirectory). Then, in the fresh Lisp type:
        ; Initialization, second pass
        (load "init.lisp")
        (in-package "ACL2")
        (save-acl2 (quote (initialize-acl2))
                   "saved_acl2")
      
      You have now saved an image. Exit Lisp now. Subsequent steps will put the image in the right place.

    5. Remove osaved_acl2 if it exists.

    6. IF saved_acl2 and saved_acl2.dxl both exist THEN:
      • move saved_acl2.dxl to osaved_acl2.dxl
      • move saved_acl2 to osaved_acl2 and edit osaved_acl2, changing saved_acl2.dxl (at end of line) to osaved_acl2.dxl
      ELSE IF saved_acl2 exists THEN:
      • move saved_acl2 to osaved_acl2
    7. Move nsaved_acl2 to saved_acl2.
    8. For some Common Lisps, a file nsaved_acl2.suffix is created for some suffix. Move it to: saved_acl2.suffix
    9. Make sure saved_acl2 is executable. For Windows this involves two mini-steps:
      (a) Remove the "$*" from the saved_acl2 script (because Windows does not understand $*). Consequently, any arguments you pass to ACL2 via the command line will be ignored.

      (b) Rename saved_acl2 to saved_acl2.bat, for example by executing the following command.

      rename saved_acl2 saved_acl2.bat




    Building an Executable Image on Some Particular Systems

    Subtopics of this section are as follows.


    Special Case: Building an Executable Image on a Windows System using GCL

    You may want to skip this section and instead read Instructions from David Rager for building ACL2 on Windows. Or, you may be able to download a pre-built ACL2 image for Windows instead of reading this section.

    Otherwise here are steps to follow.

    1. FIRST get GCL running on your Windows system using ONE of the following two options. Note that GCL can be unhappy with spaces in filenames, so you should probably save the GCL distribution to a directory whose path is free of spaces.
      • OR, obtain GCL for Windows systems from ftp://ftp.gnu.org/gnu/gcl/ or as explained above. You may wish to pick a .zip file from the cvs/ subdirectory (containing pre-releases) that has "mingw32" in the name.
      • OR ELSE, perhaps you can build GCL on your Windows system from the sources. The mingw tools and the cygnus bash shell have been used to build distributed GCL executables.
    2. SECOND, create an appropriate GCL batch file. When we tried running the script gclm/bin/gclm.bat that came with gcl-cvs-20021014-mingw32 from the above ftp site, a separate window popped up, and with an error. Many ACL2 users prefer running in an emacs shell buffer. (We obtained emacs for Windows from ftp://ftp.gnu.org/gnu/windows/emacs/21.2/emacs-21.2-fullbin-i386.tar.gz.) The following modification of gclm.bat seemed to solve the problem (your pathnames may vary).
      @
      % do not delete this line %
      @ECHO off
      set cwd=%cd%
      path C:\gcl\gclm\mingw\bin;%PATH%
      C:\gcl\gclm\lib\gcl-2.6.2\unixport\saved_gcl.exe -dir C:/gcl/gclm/lib/gcl-2.6.2/unixport/ -libdir  C:/gcl/gclm/lib/gcl-2.6.2/ -eval "(setq si::*allow-gzipped-file* t)" %1 %2 %3 %4 %5 %6 %7 %8 %9
      
    3. THIRD, follow the instructions for other than Unix-like systems above, though the resulting file may be called saved_acl2.exe rather than saved_acl2.
    4. FINALLY, create a file acl2.bat as explained in http://www.cs.utexas.edu/users/moore/acl2/v6-3/distrib/images/Readme.html.

    We hope that the above simply works. If you experience problems, the following hints may help.

    TROUBLESHOOTING:

    • We tried building ACL2 on Windows XP on top of GCL, our attempt broke at the end of the "Initialization, first pass" step, while compiling TMP1.lisp. That was easily remedied by starting up a fresh GCL session and invoking (compile-file "TMP1.lisp") before proceeding to the next step.
    • Yishai Feldman has provided some nice instructions at http://www.faculty.idc.ac.il/yishai/reasoning/win-install.htm, some of which we have tried to incorporate here. A useful point made there is that when you want to quit ACL2, use :good-bye (or (good-bye) which works even in raw Lisp). Or you can use (user::bye) in raw Lisp. The point is: Avoid control-c control-d, even thought that often works fine in emacs under Unix-like systems.
    • If the above batch file does not work for some reason, an alternate approach may be to set environment variables. You may be able to add to the PATH variable gcl-dir\gcc\bin, where gcl-dir is the directory where GCL is installed. To get to the place to set environment variables, you might be able to go to the control panel, under system, under advanced. Alternately, you might be able to get there by opening My Computer and right-clicking to get to Properties, then selecting the Advanced tab. At one time, when GCL/Windows was release as Maxima, Pete Manolios suggested adding the system variable LD_LIBRARY_PATH with the value "maxima-dir\gcc\i386-mingw32msvc\include"; this may or may not be necessary for your GCL installation (and the path would of course likely be different).



    Running Without Building an Executable Image

    The most convenient way to use ACL2 is first to install an executable image as described above for Unix-like systems and other platforms. However, in some cases this is not possible, for example if you are using a trial version of Allegro Common Lisp. In that case you should follow the steps below each time you want to start up ACL2.

    We assume you have obtained ACL2 and placed it in directory dir, as described above for Unix-like systems or other platforms. (If you downloaded a pre-built ACL2 image, then you may skip this section.) Connect to subdirectory acl2-sources of dir, start up your Common Lisp, and compile by executing the following forms. This sequence of steps need only be performed once.

      (load "init.lisp")
      (in-package "ACL2")
      (compile-acl2)
    
    Now each time you want to use ACL2, you need only execute the following forms after starting up Common Lisp in subdirectory acl2-sources of dir.
      (load "init.lisp")
      (in-package "ACL2")
      (load-acl2)
      (initialize-acl2)
    
    Note. The resulting process includes the ACL2 documentation, and hence will probably be considerably larger (perhaps twice the size) than the result of running an executable image created as described above.

    Now proceed to read more about Using ACL2.



    Summary of Distribution

    The ACL2 distribution, acl2.tar.gz, includes the ACL2 source files as well as the following files and directories (and a few others not mentioned here). Note that a books/ directory is not included; see instructions above to fetch the community books.
      LICENSE       ; ACL2 license file
      GNUmakefile   ; For GNU make
      TAGS          ; Handy for looking at source files with emacs
      doc/          ; ACL2 documentation in various formats
      emacs/        ; Some helpful emacs utilities
      installation/ ; Installation instructions (start with installation.html)
      saved/        ; Empty directory for backing up copies during make
    
    Also available are the following.

    Based on the preceding ACL2 release, we estimate that the entire acl2.tar.gz is about 7MB, which extracts to about 30MB, and the gzipped tarfile books-6.3.tar.gz (obtained as described above) is about 12MB, which extracts to about 70MB. Additional space is required to build an image, perhaps 50 to 300 megabytes depending on the platform (including the host Lisp); and more will be required to certify books.

    [Back to Installation Guide.]



















    acl2-sources/doc/HTML/installation/requirements.html0000664002132200015000000002471012222333514022177 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Requirements

    Requirements

    [Back to main page of Installation Guide.]


    ACL2 Version 6.3
    Copyright (C) 2013, Regents of the University of Texas
    ACL2 is licensed under the terms of the LICENSE file distributed with ACL2.


    Table of Contents


    Performance comparisons

    You can see recent performance numbers by following this link, or by going to the ACL2 home page and following the link "Recent changes to this page".

    Obtaining Common Lisp

    ACL2 works on Unix, GNU-Linux, and Mac OS X, which we call "Unix-like systems", as well as many Windows operating systems (at least including Windows 98, Windows 2000, and Windows XP). It can be built on top of any of the following Common Lisps, listed here alphabetically.

    Obtaining Allegro Common Lisp

    The website for Allegro Common Lisp, a commercial implementation, is http://www.franz.com/. You may be able to obtain a trial version there.

    Obtaining CCL (OpenMCL)

    Clozure CL (CCL) was formerly known as OpenMCL. Quoting from the Clozure CL web page: ``Clozure CL is a fast, mature, open source Common Lisp implementation that runs on Linux, Mac OS X and BSD on either Intel x86-64 or PPC.''

    For Windows users: We observed stalls using CCL 1.5 on Windows (in May, 2010), though not with CCL 1.4. We have been told by a CCL implementor that this bug has been fixed, and people running CCL 1.5 under Windows at a revision less than 13900 should update.

    Here is an easy way to obtain and build the latest version (generally recommended) for Linux on running on x86 or x86-64. First execute the following shell command to create a ccl directory, but substituting for linuxx86, if appropriate, any of darwinx86 (which we use for modern Macs), freebsdx86, solarisx86, windows, darwinppc, or linuxppc.

    svn co http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx86/ccl
    
    Note: if you prefer the latest release, you can obtain that instead, for example as follows (but replace "1.9" by the latest version, for example as described at http://ccl.clozure.com/download.html, and replace linuxx86 if appropriate as described above).
    svn co http://svn.clozure.com/publicsvn/openmcl/release/1.9/linuxx86/ccl
    
    Next rebuild the executable by issuing the following commands, but replace "./lx86cl64" by a suitable executable; e.g., for 64-bit Darwin (on Mac OS) use "./dx86cl64".
    ./lx86cl64
    (rebuild-ccl :full t)
    (quit)
    ./lx86cl64
    (rebuild-ccl :full t)
    (quit)
    

    Now your CCL executable is up to date. Next, create a suitable script, say as follows, where DIR is the full pathname for the directory above the new ccl directory.


    #!/bin/sh
    
    tmp=`uname -a | fgrep x86_64`
    export CCL_DEFAULT_DIRECTORY=DIR/ccl
    # Start up 64-bit or 32-bit lisp, respectively:
    if [ "$tmp" != "" ] ; then \
        DIR/ccl/scripts/ccl64 $* ; \
    else \
        DIR/ccl/scripts/ccl $* ; \
    fi
    

    Be sure to make your script executable. For example, if your script filename is my-script then on linux you might want to execute the following shell command.
    chmod +x my-script
    
    Your script (invoked with a suitable pathname, or just the filename if the directory is on your path) will now start the updated CCL lisp image.

    More details if you want or need them:
    Step 3 in http://trac.clozure.com/openmcl/wiki/UpdatingFromSource has more details on building from source. Alternatively, you can download a gzipped tar file; see the main CCL page, or visit the page of stable Clozure CL snapshots for ACL2 users. (Subversion and gzipped tar files are great, but not so much a CCL disk image (.dmg file), as we have had a report of the extracted CCL opening its own window when you start it up.) If you don't want to write your own script (as suggested above) then after obtaining CCL, you may wish to edit file ccl/scripts/ccl or file ccl/scripts/ccl64, depending on whether you want to use a 32-bit or 64-bit version (respectively).

    Obtaining CLISP

    CLISP is a non-commercial Common Lisp implementation, available from http://clisp.cons.org/.

    Obtaining CMU Common Lisp

    CMU Common Lisp (sometimes called CMUCL) is a non-commercial Common Lisp implementation, available from http://www.cons.org/cmucl/.

    Obtaining GCL

    You might be able to download a binary Debian package for ACL2. Thanks to Camm Maguire for maintaining this package. Note however that it may take some time after each ACL2 release for this binary Debian package to be updated for that release. Here is a shell command that might be used to obtain that package (if running Debian).

    apt-get -q install gcl gcl-doc
    
    Otherwise, it should be easy to obtain and build GCL yourself. There are two recommended versions of GCL for building ACL2: GCL 2.6.8 and GCL 2.6.10. (GCL 2.6.9, which is not recommended, has some issues that were fixed in GCL 2.6.10.) GCL 2.6.8 appears to be faster for ACL2 regressions than GCL 2.6.10; we obtained the following times on a 64-bit Linux system using a development snapshot of ACL2 as of Sept. 23, 2013.
       ACL2 built on non-ANSI GCL 2.6.8:
       33235.713u 1275.991s 1:29:23.91 643.4%	0+0k 156624+4638504io 25pf+0w
    
       ACL2 built on non-ANSI GCL 2.6.10pre:
       40678.982u 1013.967s 1:45:06.91 661.0%	0+0k 153400+6615176io 9pf+0w
    
       ACL2 built on ANSI GCL 2.6.10pre:
       67950.466u 1050.753s 2:44:58.65 697.0%	0+0k 327160+6955712io 7pf+0w
    
    On the other hand, modern Common Lisp implementations are ANSI compliant, and GCL 2.6.10 may have better ANSI support (which is needed if you choose to build ACL2(h)).

    As of this writing (late September 2013), it might be best to fetch GCL 2.6.8 for most purposes but GCL 2.6.10 if for some reason you want an ANSI build of GCL. You can fetch it as a tarball from main GNU website for GCL. From GCL source you can build an executable by extracting from the tarball, standing in the resulting gcl/ directory, and issuing one of the following commands.

    # Recommended for 64-bit Linux:
    ./configure --enable-maxpage=1048576 && make
    
    # Recommended for Mac OS:
    ./configure && make
    
    # If you want an ANSI build
    # (but add "--enable-maxpage=1048576" as above for linux):
    ./configure --enable-ansi && make
    
    As of late September 2013, GCL 2.6.10 is not yet available as a tarball. However, you can get a pre-release (which we expect to work fine, having used it successfully ourselves) like this (assuming you have git installed):
    git clone git://git.sv.gnu.org/gcl.git
    cd gcl
    git checkout Version_2_6_10pre
    cd gcl
    
    Then run configure and make as indicated above.

    Obtaining LispWorks

    LispWorks is a commercial Common Lisp implementation. You can download a free, restricted, version from http://www.lispworks.com/. You may ask the vendor for an evaluation license for the full product if you are considering purchasing a license.

    Obtaining SBCL

    SBCL (Steel Bank Common Lisp) is a non-commercial Common Lisp implementation, available from http://sbcl.sourceforge.net/.

    [Back to Installation Guide.]



















    acl2-sources/doc/HTML/installation/using.html0000664002132200015000000003000712222333514020575 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Using ACL2

    Using ACL2

    [Back to main page of Installation Guide.]

    Table of Contents


    Here we begin with a discussion of how to invoke ACL2 interactively. We then discuss testing as well as the certification of ACL2 community books. We conclude with a discussion of the documentation.



    Invoking ACL2

    At this point, dir has a subdirectory called acl2-sources. The sources and perhaps an executable image are located on that subdirectory. However, if you have not saved an image but instead use the directions for Running Without Building an Executable Image, skip to When ACL2 Starts Up below.

    The executable image is called acl2-sources/saved_acl2. You can invoke ACL2 by running that image, e.g.,

    mycomputer% dir/acl2-sources/saved_acl2

    If you on a Unix-like system, then to make it easy to invoke ACL2 by typing a short command, e.g.,

    mycomputer% acl2

    you may want to install an executable file on your path, e.g., /usr/local/bin/acl2, containing the following two lines:

    #!/bin/csh -f
    dir/acl2-sources/saved_acl2


    Note: A carriage return in the file after the last line above may be important!


    When ACL2 Starts Up

    When you invoke ACL2, you should see the host Common Lisp print a header concerning the ACL2 version, license and copyright.

    Most or all hosts then automatically enter the ACL2 ``command loop,'' an ACL2 read-eval-print loop with the prompt:

    ACL2 !>
    
    If however a host leaves you in Common Lisp's read-eval-print loop, then you'll need to evaluate the Common Lisp expression (ACL2::LP) or simply (LP) if the current package is "ACL2".

    Once in the ACL2 command loop, you can type an ACL2 term, typically followed by ``return'' or ``enter,'' and ACL2 will evaluate the term, print its value, and prompt you for another one. Below are three simple interactions:

    ACL2 !>t
    T
    ACL2 !>'abc
    ABC
    ACL2 !>(+ 2 2)
    4
    

    To get out of the ACL2 command loop, type the :q command. This returns you to the host Common Lisp. We sometimes call this ``raw Lisp.'' You may re-enter the command loop with (LP) as above.

    Note that when you are in raw Lisp you can overwrite or destroy ACL2 by executing inappropriate Common Lisp expressions. All bets are off once you've exited our loop. That said, many users do it. For example, you might exit our loop, activate some debugging or trace features in raw Lisp, and then reenter our loop. While developing proofs or tracking down problems, this is reasonable behavior.

    Now you are ready to test your image.



    Testing ACL2

    An easy way to test the theorem prover is to type the following term to the ACL2 command loop:

    :mini-proveall
    
    This will cause a moderately long sequence of commands to be processed, each of which is first printed out as though you had typed it. Each will print some text, generally a proof of some conjecture. None should fail.

    A more elaborate test is to certify the community books, which is a good idea anyhow; this is our next topic. On a Unix-like system, you can also certify just a small but useful subset of the books in a few minutes by executing, in directory dir/acl2-sources:

    make certify-books-short
    



    Certifying ACL2 Books

    The community books have been contributed mainly by users and may be obtained as explained elsewhere, to create subdirectory acl2-sources/books. The general topic of books is discussed thoroughly in the ACL2 documentation; see http://www.cs.utexas.edu/users/moore/acl2/current/BOOKS.html.

    Books should be ``certified'' before they are used. We do not distribute certificates with our books, mainly because certification produces compiled code specific to the host. You should certify the books locally, both as a test of your ACL2 image and because books generally need to be certified before they can be used.

    It is easy to re-certify all the community books on a Unix-like system. We recommend you do this. If you have entered ACL2, exit to the operating system, e.g., evaluting the form, (quit), or by control-d in many systems.

    While connected to dir/acl2-sources, execute

    make certify-books
    
    This will generate minimal output to the screen and will probably take an hour or two. Failure is indicated by the presence of CERTIFICATION FAILED in the log.

    To remove the files thus created, invoke:

    make clean-books
    

    The certify-books target does not cause workshops/ books to be certified. If you want to certify those books as well, you will first need to download the gzipped tar file of the workshops books from the Google Code website to the books/ directory, and then gunzip and extract it to create subdirectory workshops. You can certify all the community books, including books for the workshops (including those from the 1999 workshop as described in the (hardbound) book Computer-Aided Reasoning: ACL2 Case Studies), using the command:

    make regression
    
    Our main installation page contains a discussion of options for the above command, such as avoidance of `make' option -j.

    By default, certification uses the image dir/acl2-sources/saved_acl2. You may specify any ACL2 image, as long as it is either a command on your path or an absolute file name, for example as follows.

    make certify-books ACL2=my-acl2
    
    make regression ACL2=/u/smith/projects/acl2/saved_acl2
    

    We apologize to users of other than Unix-like systems (i.e., other than Unix, GNU-Linux, and Mac OS X): we do not provide instructions for recertifying all the community books on such systems, though there are such environments that can be installed on Windows (e.g., Cygwin). The certification methods provided by the authors of the books vary greatly and we codified them in the makefile, which is named GNUmakefile, used above. Some subdirectories of the community book (typically installed in acl2-sources/books/) contain either a README file or a certify.lsp file. Users who wish to certify one of these books and who cannot figure out (from these scant clues) what to type to ACL2 should not hesitate to contact the authors.

    Next proceed to the section on Documentation.



    Documentation

    ACL2's documentation is a hypertext document that, if printed in book form, is just over 2050 pages, or about 2.4 megabytes of gzipped postscript. Its hypertext character makes it far more pleasing to read with an interactive browser. The documentation is available in several formats: HTML, Texinfo, Postscript and ACL2 documentation strings, and more recently, in web-browsable form with some community books' documentation, courtesy of Jared Davis. The ACL2 documentation is copyrighted by the Regents of the University of Texas under the terms of the LICENSE file distributed with ACL2.

    Two Web-based guided tours of ACL2 are available from the home page noted below. If you are already familiar with Nqthm, you might find it useful to look at the documentation node NQTHM-TO-ACL2. Another useful documentation topic for beginning ACL2 users is the node TUTORIAL.

    HTML

    The ACL2 Home Page is

    http://www.cs.utexas.edu/users/moore/acl2/index.html

    The home page provides a selected bibliography, a search button (near the top of the page), guided tours of the system, and the complete hypertext documentation tree.

    Once you have installed ACL2, the HTML form of the documentation is available locally as dir/acl2-sources/doc/HTML/acl2-doc.html.

    We urge you to browse your local copy of the documentation rather than our Web copy, simply to reduce Web traffic and the demand on our server. (Macintosh users using MacOS 9 and earlier may, however, find filenames being truncated and hence will want to avoid the local documentation.)

    Emacs Info

    This is a very convenient format for accessing the ACL2 documentation from within Emacs. In Emacs, invoke

    meta-x info
    
    and then, if you are unfamiliar with Info, type
    control-h m
    
    to see a list of commands available. In particular, type

    g (dir/acl2-sources/doc/EMACS/acl2-doc-emacs.info)TOP

    to enter the ACL2 documentation. Alternatively, your system administrator can add an ACL2 node to the top-level Info menu. The appropriate entry might read:

    * ACL2 i.j: (dir/acl2-sources/doc/EMACS/acl2-doc-emacs).
              Documentation for ACL2 version i.j.

    Note: The Emacs Info and Postscript versions of our documentation were created using the file acl2-sources/doc/texinfo.tex which is copyrighted by the Free Software Foundation, Inc. (See that file for copyright and license information.)

    Users new to emacs may find it helpful to load into emacs the file dir/acl2-sources/emacs/emacs-acl2.el. Utilities offered by this file are documented near the top of the file.

    Postscript

    The Postscript version of the documentation is not included in our normal distribution because it is so much less useful than the hyper-text versions. But a gzipped Postscript (2.6 MB) version is available. It prints as a book of over 2200 pages and contains a Table of Contents and an index to all documented topics.

    ACL2 Documentation Strings

    The ACL2 system has facilities for browsing the documentation. When you are in the ACL2 command loop, you may query the documentation on a given topic by typing the command

    :doc topic

    where topic is the Lisp symbol naming the topic you want to learn about. To learn more about the on-line documentation, type :help and then return.

    Note, however, that you may find it more convenient to view the documentation in a web browser (starting at doc/HTML/acl2-doc.html) or in Emacs info (starting at doc/EMACS/acl2-doc-emacs.info).

    [Back to Installation Guide.]



















    acl2-sources/doc/HTML/installation/windows7.html0000664002132200015000000001006412222333514021232 0ustar kaufmannacl2 Helpful Instructions for Setting up ACL2 and Windows

    Helpful Instructions for Setting up ACL2 and Windows

    We thank David Rager for providing the following instructions, which we include verbatim and expect apply to future versions. Note: We recommend using CCL for Windows builds, in part so that interrupts (Control-C) will work. If you are using Windows, please note that there have been stalls using CCL 1.5 on Windows, though not with CCL 1.4. We have been told by a CCL implementor that this bug has been fixed, and people running CCL 1.5 under Windows at a revision less than 13900 should update.

    I was able to get ACL2 3.6.1 to install and build the regression suite on Windows 7 with the following steps. I did not have to install cygwin.

    1. Install MinGW. At the time of this writing, the following direct link works
      MinGW-5.1.6.exe

      If that direct link doesn't work, click on "Automated MinGW Installer" on the more general MinWG project files page.

    2. Install MSys. At the time of this writing, the following direct link works
      MSYS-1.0.11.exe

      If that direct link doesn't work, click on "MSYS Base System" on the more general MinWG project files page.

    3. Add "C:\msys\1.0\bin" to my environment variable "path". The way you do this varies with each Windows XP/Vista/7. Roughly speaking, you need to go to the control panel, and open up your system settings. Get to the advanced system settings and click on environment variables. Edit the "path" environment variable and add ";C:\msys\1.0\bin" to it. At this point you might need to restart your computer, but I did not have to do so on Windows 7. I did, however, have to restart my emacs.
    4. Realize that using "\" to indicate paths in windows confuses some linux programs and that you might need to use "/" sometimes.

    5. After expanding the ACL2 sources, cd to that directory and type something similar to the following (modify it to set LISP to your LISP executable1)
      make LISP=c:/ccl/wx86cl64.exe
      The "make.exe" that will be used is intentionally the MSys version, not the MinGW version.

    6. After your ACL2 image builds, make acl2 executable, specifically
      • Remove the "$*" from the saved_acl2 script (because Windows does not understand $*). Consequently, any arguments you pass to ACL2 via the command line will be ignored.
      • Rename saved_acl2 to saved_acl2.bat, for example by executing the following command: rename saved_acl2 saved_acl2.bat
    7. You can now make the regression suite by typing
      make regression ACL2=c:/acl2-3.6.1/acl2-sources/saved_acl2.bat

      [Note added later: you may need to add `make' option ACL2_CENTAUR=skip, for example if you have issues with Perl.]


    1I have intentionally ommitted instructions on how to setup a LISP on windows. However, I include one link that should suffice: Obtaining CCL

    acl2-sources/doc/HTML/installation/windows-gcl-jared.html0000664002132200015000000000330512222333514022771 0ustar kaufmannacl2 Instructions from Jared Davis for building ACL2 on Windows using mingw

    Instructions from Jared Davis for building ACL2 on Windows using mingw

    We thank Jared Davis for providing the following instructions for ACL2 Version 2.8, which we include verbatim and expect apply to future versions.
    	      Building ACL2 on Windows from Scratch
       _____________________________________________________________
    
       Note: The disk space requirements are large.  Not including
       emacs, I had about 275 MB taken up by msys/mingw32/gcl/acl2
       during the build process.  You can probably use much less
       space by removing files after you use them, but I didn't
       bother to do that.
    
       Here are the steps I took:
    
       Downloaded emacs 21.3 full distribution and installed
       Downloaded msys 1.10.10, installed to c:\acl2
       Downloaded mingw 3.1.0-1, installed to c:\acl2\mingw
       Downloaded gcl 2.5.3, extracted to c:\acl2\mingw
       Downloaded acl2 2.8, extracted to c:\acl2\sources
    
    
    
       Compiling gcl:
    
         in msys:
    
           cd /acl2/ming2/gcl-2.5.3
           ./configure
           make
           make install
    
    
    
       Compiling acl2:
    
         copy "etags.exe" to /mingw/bin.  you can find this program
         in your emacs folder, under "bin".
    
         in msys:
    
           cd /sources
           make
    
    
    
       Certifying ACL2 books:
       This took 111 minutes on my Athlon 2500+
    
         in msys:
    
           cd /sources
    
           mv nsaved_acl2.gcl.exe saved_acl2.exe
    
           vim books/Makefile-generic, remove "nice" from this line:
    	   ACL2=time nice ../../saved_acl2
    
           make certify-books ACL2=/sources/saved_acl2.exe
    
    
    acl2-sources/doc/TEX/0002775002132200015000000000000012222334002013750 5ustar kaufmannacl2acl2-sources/emacs/0002775002132200015000000000000012222334002013633 5ustar kaufmannacl2acl2-sources/emacs/emacs-acl2.el0000664002132200015000000011020112222120065016057 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTES-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of Version 2 of the GNU General Public License as ; published by the Free Software Foundation. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Sciences ; University of Texas at Austin ; Austin, TX 78712-1188 U.S.A. ; This file contains some emacs stuff for ACL2 users. It is intended ; to work both with GNU Emacs and XEmacs. ; Suggestion: look at the final section, "Some other features you may want." ;;; CONTENTS OF THIS FILE ;;; Here we summarize the functionality offered by this file. In many cases, ;;; lower-level details may be found later in the file where the functionality ;;; is actually provided. ; General shell stuff ; Starts up a shell buffer, *shell*. ; "meta-x new-shell" starts new shell buffers *shell-1*, *shell-2*, .... ; "control-x k" redefined to avoid accidentally killing shell buffer. ; "control-t e" sends the current form to the shell buffer. ; "control-t b" switches to the shell buffer. ; "control-t c" sets the shell buffer (initially, *shell*) to the current ; buffer ; "control-t control-e" sends the current form to the shell buffer, but ; in the other window. ; "control-d" is redefined in shell/telnet buffers to avoid ending process. ; "meta-p" and "meta-n" cycle backward/forward doing command completion in ; shell/telnet buffers. ; "control-" sets shell/telnet directory buffer to current directory. ; From current buffer to shell buffer ; "control-t l" prints appropiate ACL2 LD form to the end of the shell ; buffer, to cause evaluation of the active region in the current ; buffer. ; "control-t control-l" prints just as above, but inhibits output and proofs; ; can easily be edited to inhibit only one or the other ; "control-t u" puts an appropriate :ubt at the end of the shell buffer, based ; on the event in which you are currently standing. ; Some editing commands ; "meta-x find-unbalanced-parentheses" locates unbalanced parentheses. ; "control-t a" puts line with cursor at bottom of window. ; "control-t " completes filename in any buffer. ; "control-t control-v" scrolls half as far as "control-v". ; "control-t v" scrolls half as far as "meta-v". ; "control-t s" searches forward non-interactively, with string supplied in ; minibuffer, case-sensitive ; "control-t control-s": like "control-t s" above, but case-insensitive (at ; least by default). ; "control-meta-q" indents s-expression even when not in lisp-mode. ; "control-t control-p" executes "meta-x up-list", moving to end of enclosing ; s-expression. ; "control-t w" does "meta-x compare-windows" (see emacs documentation, ; "control-h f compare-windows", for more info). ; "control-t q" is like "control-t w" above, but ignores whitespace (and case ; too, with a positive prefix argument). ; Lisp mode comes up with auto-fill mode on, right margin set at column 79, ; and causes latin-1 (i.e., iso-8859-1) encoding (see :doc ; character-encoding) to be used when saving a buffer to a file. ; If X Windows is being run, then font-lock-mode is also turned on, ; which causes Emacs to color text in .lisp files. If you don't want ; colors in .lisp files, put this in your .emacs file after the load of ; "emacs-acl2.el": ; (if (equal window-system 'x) ; (remove-hook 'lisp-mode-hook '(lambda () (font-lock-mode 1)))) ; "meta-x visit-acl2-tags-table" sets the current tag table to the one in the ; ACL2 source directory. ; "control-t f" fills format strings; see documentation for more info ; ("control-h f fill-format-string"). ; "control-t control-f" buries the current buffer (puts it on the bottom of ; the buffer stack, out of the way, without killing the buffer) ; ACL2 proof-tree support ; NOTE: This works by default if you install the ACL2 community books, as ; most ACL2 users do, in the books/ directory of your ACL2 ; distribution. Otherwise, you will need to set the variable ; *acl2-interface-dir* to a directory string containing a file ; top-start-shell-acl2.el that defines the functions start-proof-tree ; and start-proof-tree-noninteractive in emacs. For user-level ; documentation provided in the ACL2 community books implementation, ; see the following file included there: ; books/interface/emacs/PROOF-TREE-EMACS.txt ; "meta-x start-proof-tree" starts proof-tree tracking in the current buffer ; (where ACL2 is running). See ACL2 documentation for PROOF-TREE for ; more information. ; Function start-proof-tree-noninteractive (see below) can be used to start ; proof-trees when emacs starts up; see below. ; Run ACL2 as inferior process ; NOTE: This works by default if you install the ACL2 community books. ; Otherwise, see the NOTE above on "ACL2 proof-tree support". ; "meta-x run-acl2" starts up acl2 as an inferior process in emacs. You may ; have better luck simply issuing your ACL2 command in an ordinary ; (emacs) shell. ; ACL2 proof-checker support ; "control-t d" prints an appropriate DV command at the end of the current ; buffer, suitable for diving to subexpression after printing with ; proof-checker "th" or "p" command and then positioning cursor on that ; subexpression. See ACL2 documentation for PROOF-CHECKER. ; "control-t control-d" is like "control-t d" above, but for DIVE instead ; (used with "pp" instead of "p") ; Support for Dynamic Monitoring of Rewrites (dmr) ; Miscellaneous ; "meta-x acl2-info" brings up ACL2 documentation in pleasant emacs-info ; format. ; "meta-x date" prints the current date and time (commented out). ; "control-meta-l" swaps top buffer with next-to-top buffer (same as ; "control-x b "). ; "control-t" is a prefix for other commands ; "control-t control-t" transposes characters (formerly "control-t") ; Other features: ; Turn on time/mail display on mode line. ; Disable a few commands. ; Calls of case, case!, case-match, and dolist will indent like ; calls of defun. ; Some other features you may want (these are commented out by default): ; Turn off menu bar. ; Turn off emacs auto-save feature. ; Start an abbrev table. ; Avoid getting two windows, for example with control-x control-b. ; Modify whitespace to ignore with "control-t q" (see above). ; Turn on version control. ; Arrange for "control-meta-l" to work as above even in rmail mode. ; If time and "mail" displays icons, this may turn them into ascii. ; Get TeX-style quotes with meta-". ; Debug emacs errors with backtrace and recursive edit. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; EDIT THIS SECTION! ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Edit the following to point to your ACL2 source directory. This is not ; necessary, however, if this file is located in the emacs/ subdirectory of the ; ACL2 source directory (as is the case when it is distributed). ; Example: ; (defvar *acl2-sources-dir* "/u/acl2/v2-9/acl2-sources/") ; It is tempting to add the following code. But we want the user to ; manage re-binding *acl2-sources-dir*, say by putting the following ; form in the .emacs file in front of the load of the present file. ; (if (boundp '*acl2-sources-dir*) ; (makunbound '*acl2-sources-dir*)) (defvar *acl2-sources-dir*) ; Attempt to set *acl2-sources-dir*. (if (and (not (boundp '*acl2-sources-dir*)) (file-name-absolute-p load-file-name)) (let ((pattern (if (string-match "[\\]" load-file-name) "\[^\\]+\\*$" "/[^/]+/*$")) (dir (file-name-directory load-file-name))) (let ((posn (string-match pattern dir))) (if posn (setq *acl2-sources-dir* (substring dir 0 (1+ posn))))))) ; The following causes, for every event, the event name to be given ; the same color (in font-lock mode) as when defun is called. If you ; don't like it, first copy this form into your .emacs file after the ; form that loads this emacs-acl2.el, and then change ; font-lock-add-keywords to font-lock-remove-keywords. (font-lock-add-keywords 'lisp-mode '(("\\((def[^ \t]*\\|(encapsulate\\|(in-theory\\|(include-book\\|(local\\)\\>" . font-lock-keyword-face) ("\\((make-event\\|(mutual-recursion\\|(prog[^ \t]*\\)\\>" . font-lock-keyword-face) ("\\((table\\|(theory-invariant\\)\\>" . font-lock-keyword-face) ("\\((value-triple\\|(verify-guards\\|(verify-termination\\)\\>" . font-lock-keyword-face))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Control-t keymap ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (when (not (boundp 'ctl-t-keymap)) ; This trick probably came from Bob Boyer, to define a new keymap; so now ; control-t is the first character of a complex command. (defvar ctl-t-keymap) (setq ctl-t-keymap (make-sparse-keymap)) (define-key (current-global-map) "\C-T" ctl-t-keymap) ; Control-t t now transposes characters, instead of the former control-t. (define-key ctl-t-keymap "\C-T" 'transpose-chars) (define-key ctl-t-keymap "\C-t" 'transpose-chars) ) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; General shell stuff ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; If you don't want to start up a shell when emacs starts but you load ; the present file emacs-acl2.el in your .emacs file, then put the ; following in your ~/.emacs file above the form that loads the ; present file: ; ; (defvar acl2-skip-shell nil) ; (setq acl2-skip-shell t) ; (defvar acl2-skip-shell nil) ; Start up a shell. This also loads in comint-mode, used below. (if (not acl2-skip-shell) (shell) ; Otherwise load the shell package in case it's used somewhere. (load "shell")) ; Do meta-x new-shell to start a new shell. (defvar number-of-other-shells 0) (defun new-shell () "Start up another shell." (interactive) (switch-to-buffer (make-comint (concat "shell-" (number-to-string (setq number-of-other-shells (+ 1 number-of-other-shells)))) (or (getenv "SHELL") "csh"))) (shell-mode)) ; Avoid killing shell buffers by accident: (defun kill-buffer-without-process (name) "Kill a buffer unless there's a process associated with it." (interactive (let (val (default-name (buffer-name (current-buffer))) (table (mapcar (function (lambda (x) (cons (buffer-name x) x))) (buffer-list)))) (setq val (completing-read (format "Kill buffer: (default: %s) " default-name) table nil t)) (list (if (equal val "") default-name val)))) (if (get-buffer-process name) (error "Process is active in the indicated buffer. Use meta-x kill-buffer instead.") (kill-buffer name))) (define-key (current-global-map) "\C-Xk" 'kill-buffer-without-process) ; Variable *acl2-shell* is the name of the "ACL2 shell", the buffer to which ; forms are written by various commands defined in this file. Control-t c ; (defined below) changes the ACL2 buffer. (defvar *acl2-shell* "*shell*") ; Set the ACL2 shell to the current buffer. (define-key ctl-t-keymap "c" 'set-shell-buffer) (defun set-shell-buffer () (interactive) (setq *acl2-shell* (buffer-name (current-buffer))) (message "Setting the ACL2 shell to buffer %s" *acl2-shell*) *acl2-shell*) ; Change to the ACL2 shell. (define-key ctl-t-keymap "b" 'switch-to-shell) (defun switch-to-shell () (interactive) (switch-to-buffer *acl2-shell*)) ; Send the current form to the ACL2 shell. Here, the "current form" is the one ; starting with the immediately preceding left parenthesis in column 0. (It is ; OK to stand on that parenthesis as well.) (define-key ctl-t-keymap "e" 'enter-theorem) (define-key ctl-t-keymap "\C-e" 'enter-theorem-other-window) ; Old version (before v2-8) hardwires in the use of *shell*. ;(defalias 'enter-theorem ; (read-kbd-macro ; "C-e C-M-a NUL C-M-f ESC w C-x b *shell* RET M-> C-y")) ; Versions after v3-4 allow us to open up ACL2 scopes. ; See the documentation for enter-theorem. ; This code is relatively less tested; please send bug reports ; to the ACL2 implementors or contribute fixes. (defun acl2-scope-start-p () (looking-at "(encapsulate[ \t]*\\(;;.*\\)?\n[ \t\n]*()[ \t]*;; start lemmas for")) (defun acl2-beginning-of-def () ; See the documentation for enter-theorem. We return nil unless we go ; to a preceding package marker, #!pkg, in which case we return t. (let ((saved-point (point)) (ans nil)) (end-of-line) (beginning-of-defun) (let ((temp-point (point))) (cond ((not (equal temp-point (point-min))) (forward-line -1) (cond ((looking-at "#!") (setq ans t)) (t (goto-char temp-point)))))) (cond ((acl2-scope-start-p) (goto-char saved-point) (if (not (looking-at "(")) (backward-up-list)) (let ((scope-p (acl2-scope-start-p))) (or scope-p (progn (while (not scope-p) (setq saved-point (point)) (backward-up-list) (setq scope-p (acl2-scope-start-p))) (goto-char saved-point)))))) ans)) (defun acl2-current-form-string (&optional ignore-pkg-marker) (save-excursion (end-of-line) (let ((temp (acl2-beginning-of-def))) (let ((beg (point))) (if (and temp (not ignore-pkg-marker)) (forward-line 1)) (forward-sexp) (buffer-substring beg (point)))))) (defun enter-theorem () "Normally just insert the last top-level form starting at or before the cursor, where a \"top-level\" form is one starting with a left parenthesis on the left margin. If that form is preceded by a line starting with #!pkg-name, then that line is included in the inserted string. However, if that form is an ACL2 scope -- an encapsulate with empty signature followed by \";; start lemmas for \" -- then first move up if necessary to a left parenthesis, and then keep moving up until hitting a \"top-level form\", i.e., either an ACL2 scope (in the above sense) or else a form immediately under an ACL2 scope. You can open a scope with control-t o." (interactive) (push-mark) ; I think I sometimes like to go back to the form. (let ((str (acl2-current-form-string))) (switch-to-buffer *acl2-shell*) (goto-char (point-max)) (insert str)) (goto-char (point-max)) ; harmless; seemed necessary at one point ) (defun enter-theorem-other-window () (interactive) (push-mark) ; I think I sometimes like to go back to the form. (let ((str (acl2-current-form-string))) (other-window 1) (switch-to-buffer *acl2-shell*) (goto-char (point-max)) (insert str))) (defun event-name () (save-excursion (let ((beg (point))) (forward-sexp) (let ((pair (read-from-string (buffer-substring beg (point))))) (let ((expr (car pair))) (if (and (consp expr) (consp (cdr expr))) (cadr expr) (error "Not in an event!"))))))) (defun acl2-open-scope () "Open a superior encapsulate that defines an ACL2 scope for the current top-level form. See the documentation for enter-theorem." (interactive) (save-excursion (acl2-beginning-of-def) (let ((name (event-name))) (beginning-of-line) (open-line 2) (lisp-indent-line) (insert "(encapsulate\n") (lisp-indent-line) (insert (format "() ;; start lemmas for %s" (or name "anonymous event"))) (forward-sexp) (let ((end (point))) (backward-sexp) (insert " ") (indent-rigidly (point) end 1)) (forward-sexp) (end-of-line) (open-line 1) (forward-line 1) (insert ")") (lisp-indent-line)))) (define-key ctl-t-keymap "o" 'acl2-open-scope) ; Avoid killing process with control-d in shell buffer: (define-key comint-mode-map "\C-d" 'delete-char) ; The following only seems necessary in gnu. (define-key comint-mode-map "" 'c-m-l) ; Allow use of meta-p and meta-n for command completion. Multiple ; meta-p/meta-n commands cycle backward/forward through previous matching ; commands. ; See also emacs lisp source file lisp/comint.el. (define-key comint-mode-map "\ep" 'comint-previous-matching-input-from-input) (define-key comint-mode-map "\en" 'comint-next-matching-input-from-input) ; Bind control- to the command that brings the current buffer's ; directory back to what it is supposed to be. (define-key global-map "\C-\M-M" 'shell-resync-dirs) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Write region to shell ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The next forms support control-t l (ell), which writes the current region to ; file "./temp-emacs-file.lsp" and puts an appropriate LD command in the shell ; buffer. (defvar *shell-temp-file-name* "temp-emacs-file.lsp") (if (boundp '*shell-temp-file-directory*) (makunbound '*shell-temp-file-directory*)) (defvar *shell-temp-file-directory*) (defun set-shell-temp-file-directory () (setq *shell-temp-file-directory* "./")) (defun shell-temp-file-name () (expand-file-name *shell-temp-file-name* (set-shell-temp-file-directory))) (defun write-region-for-shell (beg end) "Writes the current region to the shell temp file, with the header string at the top and the footer string at the bottom and separating each. Assumes beg < end." (let ((flg (buffer-modified-p))) (save-excursion (goto-char beg) (write-region beg end (shell-temp-file-name))) (set-buffer-modified-p flg))) (defun send-region-to-shell (message) "Writes the current region to the shell temp file and then puts one at the end of the ACL2 shell buffer, ready to submit that file." (let ((beg (min (point) (mark))) (end (max (point) (mark)))) (write-region-for-shell beg end) (switch-to-buffer *acl2-shell*) (goto-char (point-max)) (insert message))) (defun acl2-load () "Writes the current region to the shell temp file and then puts the cursor at the end of the ACL2 shell buffer, ready to execute an ld." (interactive) (send-region-to-shell (concat (format ";; Ready to execute ACL2-LOAD -- hit when ready\n") (format "(acl2::ld \"%s\" :LD-PRE-EVAL-PRINT acl2::t :ld-error-action :return)" (shell-temp-file-name))))) (defun acl2-load-inhibited () "Writes the current region to the shell temp file and then puts the cursor at the end of the ACL2 shell buffer, ready to execute an ld with output inhibited and proofs skipped." (interactive) (send-region-to-shell (concat (format ";; Ready to execute ACL2-LOAD -- hit when ready\n") (format "(acl2::with-output :off :all (acl2::ld \"%s\" :ld-error-action :return :ld-skip-proofsp t))" (shell-temp-file-name))))) (define-key ctl-t-keymap "l" 'acl2-load) (define-key ctl-t-keymap "\C-l" 'acl2-load-inhibited) (defun acl2-event-name (form allow-local) (and (consp form) (let ((hd (car form)) name) (cond ((eq hd 'encapsulate) (let ((form-list (cdr (cdr form)))) (while form-list (setq name (acl2-event-name (car form-list) nil)) (if name (setq form-list nil) ; exit loop (setq form-list (cdr form-list)))))) ((eq hd 'progn) (let ((form-list (cdr form))) (while form-list (setq name (acl2-event-name (car form-list) allow-local)) (if name (setq form-list nil) ; exit loop (setq form-list (cdr form-list)))))) ((eq hd 'local) (and allow-local (setq name (acl2-event-name (car (cdr form)) t)))) (t (setq name (and (consp (cdr form)) (car (cdr form)))))) (and (symbolp name) name)))) (defun acl2-undo () "Undoes back through the current event. Current weaknesses: Doesn't work for encapsulate or progn, and is ignorant of packages." (interactive) (let ((name (acl2-event-name (car (read-from-string (acl2-current-form-string t))) t))) (cond (name (switch-to-buffer *acl2-shell*) (goto-char (point-max)) (insert (format ":ubt! %s" name))) (t (error "ERROR: Unable to find event name for undoing."))))) (define-key ctl-t-keymap "u" 'acl2-undo) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Some editing commands ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Contributed by Bill Bevier: (defun find-unbalanced-parentheses () "Finds parenthesis mismatch error in buffer. Reads through all of the current buffer and tries to find places in which the parentheses do not balance. Positions point to possible trouble-spots, printing out a message that says what the trouble appears to be. This command only finds one such error; if you suspect more errors, run it again." (interactive) (let ((saved-point (point))) (goto-char (point-min));; Go to start of buffer. (let (old-point) (setq old-point (point)) (forward-sexp) (while (not (equal (point) old-point)) (setq old-point (point)) (forward-sexp))) (goto-char saved-point) (message "All parentheses appear balanced."))) (defun cursor-at-end-and-bottom () "Put cursor at the end of the buffer on the bottom line" (interactive) (recenter -1)) ; Control-t Control-a puts current line (line with cursor) at bottom of window: (define-key ctl-t-keymap "\C-a" 'cursor-at-end-and-bottom) ; Control-t completes filename in any buffer: (define-key ctl-t-keymap "\t" 'comint-dynamic-complete-filename) (defun scroll-up-half () (interactive) (scroll-up (/ (window-height) 2))) (defun scroll-down-half () (interactive) (scroll-down (/ (window-height) 2))) ; Like control-v, but only half a screen: (define-key ctl-t-keymap "\C-V" 'scroll-up-half) ; Like meta-v, but only half a screen: (define-key ctl-t-keymap "v" 'scroll-down-half) (defun search-forward-with-case (string) (interactive "sSearch: ") (let ((case-fold-search nil)) (search-forward string))) ; Case-sensitive forward search (i.e., searches forward non-interactively, with ; string supplied in minibuffer). (define-key ctl-t-keymap "s" 'search-forward-with-case) ; Forward search (case-insensitive by default): (define-key ctl-t-keymap "\C-s" 'search-forward) (define-key (current-global-map) "\C-\M-q" 'indent-sexp) (define-key ctl-t-keymap "" 'up-list) ; For the following, set compare-windows-whitespace to something other than "[ ; \t\n]+" ; if desired. (defun approx-compare-windows (&optional ignore-case) "Compare windows, ignoring whitespace. If optional argument is supplied, then also ignore case if that argument is positive, else do not ignore case." (interactive "P") (if ignore-case (let ((compare-ignore-case (> ignore-case 0))) (compare-windows "0")) (compare-windows "0"))) ; Set compare-windows-whitespace to something other than "[ \t\n]+" ; if desired. Also consider compare-ignore-case. (define-key ctl-t-keymap "w" 'compare-windows) (define-key ctl-t-keymap "q" 'approx-compare-windows) (defun my-lisp-mode-hook () (setq indent-tabs-mode nil) (setq comment-column 0) (turn-on-auto-fill) (setq save-buffer-coding-system 'iso-8859-1) ) (if (not (boundp 'lisp-mode-hook)) (setq lisp-mode-hook nil)) (add-hook 'lisp-mode-hook 'my-lisp-mode-hook) ; Other modes can be put below as well (asm, c++, c, perl, emacs-lisp). (if (equal window-system 'x) (add-hook 'lisp-mode-hook '(lambda () (font-lock-mode 1)))) (defun acl2-sources-dir () (let ((dir (if (boundp '*acl2-sources-dir*) *acl2-sources-dir* (setq *acl2-sources-dir* (expand-file-name (read-file-name "*acl2-sources-dir* (e.g. /u/acl2/v2-9/acl2-sources/): " nil nil t)))))) (if (or (equal dir "") (let ((lastch (aref "abc/" (1- (length "abc/"))))) (and (not (equal lastch ?/)) (not (equal lastch ?\\))))) (concat dir (if (and (string-match "[\\]" dir) (not (string-match "/" dir))) "\\" "/")) dir))) (defun visit-acl2-tags-table () "Visit the tags table for ACL2." (interactive) (visit-tags-table (concat (acl2-sources-dir) "TAGS"))) ; Set the right margin (used when auto-fill-mode is on). (add-hook 'lisp-mode-hook '(lambda () (setq fill-column 79))) ; Formerly: (set-default 'fill-column 79) ; The function fill-format-string below probably originated from Bob ; Boyer in the early 1990s. See documentation for fill-format-string. ; This is useful both for format and for ACL2's printing functions fmt ; and fms. Enhanced Nov. 2010 by incorporating a version of code from ; Jared Davis, so that this works even when the cursor is within the ; string rather at the start of it. (define-key ctl-t-keymap "f" 'fill-format-string) (defun fill-format-string () "Remove the ~'s from a Lisp format string, and put in new ones, after any space, in such a way that the next space does not pass fill-column. The point (i.e., the cursor) should initially be at the start of the string or anywhere within the string (but not on the closing double-quote). The final position of the cursor is the beginning of the string that was processed." (interactive "") ; First move the point to the beginning of the string, if possible. (or (and (equal (char-after (point)) ?\") (not (equal (char-before (point)) ?\\))) (let ((pos (point)) (not-done t)) (while not-done (if (> pos 0) (if (equal (char-after pos) ?\") (if (equal (char-before pos) ?\\) (setq pos (1- pos)) (goto-char pos) (setq not-done nil)) (setq pos (1- pos))) (error "Cannot find beginning of a format string to fill."))))) (save-excursion (let ((start-point (point)) (fill (make-string (+ 1 (current-column)) ? ))) (forward-sexp 1) (let ((end-point (point)) (new-end nil)) (save-restriction (narrow-to-region (+ 1 start-point) (- end-point 1)) (goto-char (point-min)) (while (re-search-forward "~\n" nil t) (delete-char -2) (while (or (looking-at " ") (looking-at "\t") (looking-at "\n")) (delete-char 1))) (goto-char (point-max)) (setq new-end (point))) (save-restriction (beginning-of-line) (narrow-to-region (point) new-end) (goto-char (+ 1 start-point)) (while (re-search-forward "[ \t]" nil t) (cond ((next-break-too-far) (insert "~\n") (insert fill))))))))) (defun next-break-too-far () (let ((p (point))) (cond ((equal (point) (point-max)) nil) (t (cond ((re-search-forward "[ \t\n]" nil t) (prog1 (>= (current-column) fill-column) (goto-char p))) (t (goto-char (point-max)) (prog1 (>= (current-column) fill-column) (goto-char p)))))))) ; Bury the current buffer, putting it on the bottom of the buffer stack, out of ; the way, without killing the buffer). (define-key ctl-t-keymap "\C-F" 'bury-buffer) ;; Make some functions' indentation behave as for defun. (put 'case 'lisp-indent-function 'defun) (put 'case! 'lisp-indent-function 'defun) (put 'case-match 'lisp-indent-function 'defun) (put 'dolist 'lisp-indent-function 'defun) (put 'er@par 'lisp-indent-function 'defun) (put 'warning$@par 'lisp-indent-function 'defun) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ACL2 proof-tree support ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (if (boundp '*acl2-interface-dir*) (makunbound '*acl2-interface-dir*)) (defvar *acl2-interface-dir*) (defun acl2-interface-dir () (if (boundp '*acl2-interface-dir*) *acl2-interface-dir* (setq *acl2-interface-dir* ; common location (i.e., for those who install ACL2 community books in books/): (concat (acl2-sources-dir) "books/interface/emacs/")))) (autoload 'start-proof-tree (concat (acl2-interface-dir) "top-start-shell-acl2") "Enable proof tree logging in a prooftree buffer." t) (autoload 'start-proof-tree-noninteractive (concat (acl2-interface-dir) "top-start-shell-acl2") "Enable proof tree logging in a prooftree buffer." t) ; You may find it useful to put some version of the following two forms in your ; .emacs file. It should start a new frame (perhaps after you click in the ; initial emacs window) to the side of the first frame, with the "prooftree" ; buffer displayed in the new frame. ; (start-proof-tree-noninteractive "*shell*") ; (cond ((and (eq window-system 'x) ; (fboundp 'x-display-pixel-width) ; (= (x-display-pixel-width) 2048) ; for a wide monitor ; ) ; (delete-other-windows) ; (if (boundp 'emacs-startup-hook) ; false in xemacs ; (push 'new-prooftree-frame emacs-startup-hook)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Run ACL2 as inferior process in emacs ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; You may have better luck simply issuing your ACL2 command in an ordinary ; (emacs) shell. But in case anyone wants to try this: (autoload 'run-acl2 (concat *acl2-interface-dir* "top-start-inferior-acl2") "Open communication between acl2 running in shell and prooftree." t) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; ACL2 proof-checker support ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; Insert DV command that gets to subexpression at the cursor. ; This is for use with the P and TH commands. (define-key ctl-t-keymap "d" 'dv-manual) ; Insert DIVE command that gets to subexpression at the cursor. ; This is for use with the PP command. (define-key ctl-t-keymap "\C-d" 'dive-manual) ; The rest of the functions in this section support \C-t d and \C-t \C-d. (defvar *acl2-pc-dive-syntax-table* nil) (defun maybe-set-acl2-pc-dive-syntax-table () (cond ((null *acl2-pc-dive-syntax-table*) (setq *acl2-pc-dive-syntax-table* (copy-syntax-table (syntax-table))) (modify-syntax-entry ?- "w" *acl2-pc-dive-syntax-table*) (modify-syntax-entry ?: "w" *acl2-pc-dive-syntax-table*) (modify-syntax-entry ?_ "w" *acl2-pc-dive-syntax-table*) (modify-syntax-entry ?+ "w" *acl2-pc-dive-syntax-table*) (modify-syntax-entry ?* "w" *acl2-pc-dive-syntax-table*) (modify-syntax-entry ?. "w" *acl2-pc-dive-syntax-table*) *acl2-pc-dive-syntax-table*))) (defun dive-manual () "Returns the 0-based address of the current s-expression inside the expression beginning at the margin, assuming that the point is properly inside the margin (otherwise causes an error), then moves to the end of the buffer and plops down the appropriate DIVE command for the proof-checker. Causes an error if one is already at the top." (interactive) (let ((addr (find-address))) (end-of-buffer) (if (null addr) (error "Null address.") (insert (prin1-to-string (cons 'dive addr)))))) (defun dv-manual () "Returns the 0-based address of the current s-expression inside the expression beginning at the margin, assuming that the point is properly inside the margin (otherwise causes an error), then moves to the end of the buffer and plops down the appropriate DV command for the proof-checker. Causes an error if one is already at the top." (interactive) (let ((addr (find-address))) (end-of-buffer) (if (null addr) (error "Null address.") (insert (prin1-to-string (cons 'dv addr)))))) (defun beginning-of-current-defun () "Causes an error if one is already at the beginning of defun, in the sense of c-m-a" ; (interactive) (let ((old-point (point))) (end-of-defun) (beginning-of-defun) (or (not (equal (point) old-point)) (error "Already at the beginning of the expression.")))) (defun find-address () "Returns the 0-based address of the current s-expression inside the expression beginning at the margin. Leaves one at the original point." (maybe-set-acl2-pc-dive-syntax-table) (with-syntax-table *acl2-pc-dive-syntax-table* (let (quit-point old-point result) (setq old-point (point)) (beginning-of-current-defun) (setq quit-point (point)) (goto-char old-point) (while (not (equal (point) quit-point)) (setq result (cons (move-up-one-level) result))) (goto-char old-point) result))) (defun move-up-one-level () "Like backward-up-list, except that it returns the position of the current s-expression in the enclosing list" ; (interactive) (let (saved-point final-point n) (forward-sexp) ; puts us just past the end of current sexp (setq saved-point (point)) (backward-up-list 1) (setq final-point (point)) (forward-char 1) (forward-sexp) (setq n 0) (while (not (equal (point) saved-point)) (setq n (1+ n)) (forward-sexp)) (goto-char final-point) n)) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Support for Dynamic Monitoring of Rewrites (dmr) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (load (concat (acl2-sources-dir) "emacs/monitor.el")) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Miscellaneous ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun acl2-info () "Starts up info pointing at top of acl2 documentation" (interactive) (info (concat (acl2-sources-dir) "doc/EMACS/acl2-doc-emacs.info")) ) ; From Bishop Brock: ;(defun date () ; "Inserts the date and time at point." ; (interactive) ; (insert (current-time-string))) ; Get control-meta-l to change buffers in rmail mode and perhaps some other ; modes where it otherwise doesn't work. (fset 'c-m-l "\C-Xb\C-M") (global-set-key "\214" 'c-m-l) ; (load "rmail") ; (define-key rmail-mode-map "\214" 'c-m-l) ; Turn on time/mail display on mode line. (setq display-time-interval 10) (display-time) ; turn off as described just below: ; Turn off display-time with: ; (display-time-mode) in emacs ; (display-time-stop) in xemacs ; Needed for displaying day and date in addition to time: ; (setq display-time-day-and-date t) ; Disable commands that we do not want to execute by mistake: (put 'shell-resync-dirs 'disabled t) (put 'suspend-or-iconify-emacs 'disabled t) (put 'suspend-emacs 'disabled t) (put 'iconify-or-deiconify-frame 'disabled t) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Some other features you may want ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Turn off menu bar: ; (menu-bar-mode 0) ;; Turn off auto-save (not actually a good idea unless you save your files ;; often). ; (setq auto-save-interval 0) ; (setq auto-save-timeout 0) ; Abbrevs are great! For example, if you type ; ac ; followed by ; control-x ' ; then (assuming the following form has been evaluated), the "ac" will be ; replaced by the value of *acl2-sources-dir*. ; (define-abbrev-table 'global-abbrev-table ; (list ; (list "ac" *acl2-sources-dir* nil 1) ; )) ;; Avoid getting two windows, for example with control-x control-b. ; (setq pop-up-windows nil) ;; For compare-windows ignoring whitespace (control-t q): ; Set compare-windows-whitespace to something other than "[ \t\n]+" ; if desired. Also consider compare-ignore-case. ;; Turn on version control (backup files *.~1~, *.~2~, ...): ; (setq version-control t) ;; If c-m-l does not work in rmail mode, you can do this: ; (load "rmail") ; (define-key rmail-mode-map "\214" 'c-m-l) ;; If time and "mail" displays icons, this may turn them into ordinary ascii. ; (setq display-time-show-icons-maybe nil) ;; To get ``real'' quotes with Escape-" (even without TeX mode): ; (autoload 'tex-insert-quote "tex-mode" nil t) ; (define-key global-map "\C-[\"" 'tex-insert-quote) ;; To debug emacs errors with backtrace and recursive edit: ; (setq debug-on-error t) acl2-sources/emacs/monitor.el0000664002132200015000000001765312222120075015661 0ustar kaufmannacl2; ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp ; Copyright (C) 2013, Regents of the University of Texas ; This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright ; (C) 1997 Computational Logic, Inc. See the documentation topic NOTES-2-0. ; This program is free software; you can redistribute it and/or modify ; it under the terms of Version 2 of the GNU General Public License as ; published by the Free Software Foundation. ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; Written by: Matt Kaufmann and J Strother Moore ; email: Kaufmann@cs.utexas.edu and Moore@cs.utexas.edu ; Department of Computer Sciences ; University of Texas at Austin ; Austin, TX 78712-1188 U.S.A. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; DOCUMENTATION ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; This file contains code for real-time monitoring of ACL2 rewrites ; (Dynamically Monitoring Rewrites, or "dmr"). This file is automatically ; loaded by emacs-acl2.el. ; We thank Robert Krug for useful contributions. ; To start (or restart) dynamically monitoring rewrites: ; control-t 1 ; To stop dynamically monitoring rewrites: ; control-t 2 ; or just hide the monitoring buffer ; See also "User-settable variables" below. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; User-settable dmr variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; The following may be set by the user to any positive number of seconds. ; If you set this, consider also setting Common Lisp variable *dmr-interval*. (defvar *acl2-timer-display-interval* 0.10) (defvar *dmr-buffer-name* (concat "acl2-dmr-" (getenv "USER"))) (defvar *dmr-file-name* ; Keep this in sync with *dmr-file-name* in the ACL2 Common Lisp sources. (concat "/tmp/" *dmr-buffer-name*)) ; See also "Debug" below, for advanced users. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Debug ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar *dmr-debug-p* nil) (defvar *dmr-debug-output* nil) (defvar *dmr-debug-output-raw* nil) (defun dmr-clear-debug () (interactive) (when *dmr-debug-output* (setq *dmr-debug-output* nil)) (when *dmr-debug-output-raw* (setq *dmr-debug-output-raw* nil))) (defun dmr-write-debug () (insert (format "%S" (reverse *dmr-debug-output*)))) (defun dmr-write-debug-raw () (insert (format "%S" (reverse *dmr-debug-output-raw*)))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;; Code ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defvar *dmr-delete-string* ; WARNING: Keep this in sync with corresponding ACL2 definition. "delete-from-here-to-end-of-buffer") (defvar *dmr-delete-string-length* (length *dmr-delete-string*)) (defun acl2-start-of-region-to-be-deleted () (goto-char (point-min)) (and (search-forward *dmr-delete-string* nil t) (match-beginning 0))) (defvar *dmr-previous-string* "") (defun dmr-star-lines-to-end () (let ((max-1 (1- (point-max)))) (while (progn (end-of-line) (< (point) max-1)) (forward-char 1) ; past newline (delete-char 1) ; delete space (insert "*")))) (defvar *dmr-finished-string* " No proof is in progress. ") (defun dmr () (when (file-exists-p *dmr-file-name*) (let ((buf (get-buffer-create *dmr-buffer-name*))) (if (get-buffer-window buf) ; Can we see the buffer? (with-current-buffer buf (let ((saved-point (point))) (insert-file-contents-literally *dmr-file-name* nil nil nil t) (let* ((new-string (buffer-string)) (max (length new-string))) (if (and (<= *dmr-delete-string-length* max) (equal (substring new-string 0 *dmr-delete-string-length*) *dmr-delete-string*)) ; This is the case where the proof has completed, indicated by nothing in the ; file before the delete string. (progn (setq new-string *dmr-finished-string*) (delete-region (point-min) (point-max)) (insert *dmr-finished-string*) (setq *dmr-previous-string* nil)) (let ((common (and *dmr-previous-string* (compare-strings new-string 0 max *dmr-previous-string* 0 max)))) (if *dmr-debug-p* (setq *dmr-debug-output-raw* (cons (buffer-string) *dmr-debug-output-raw*))) (setq *dmr-previous-string* new-string) (let ((start (acl2-start-of-region-to-be-deleted))) (and start (delete-region start (1+ max)))) (if (eq common t) ; very unlikely, given delete marker (progn (if (< saved-point (point-max)) (goto-char saved-point) (goto-char (point-max))) (if *dmr-debug-p* (setq *dmr-debug-output* (cons (buffer-string) *dmr-debug-output*)))) (goto-char (if common (min (abs common) (point-max)) (point-min))) (beginning-of-line) (if (< (point) (point-max)) (delete-char 1)) (let ((star-point (point))) (insert "*") (dmr-star-lines-to-end) (if *dmr-debug-p* (setq *dmr-debug-output* (cons (buffer-string) *dmr-debug-output*))) (if (< saved-point star-point) (goto-char saved-point) (goto-char star-point))))))))) (acl2-stop-monitor))))) (defvar *dmr-timer* nil) (defun acl2-start-monitor () (interactive) (when *dmr-timer* ; Restart the timer in case *acl2-timer-display-interval* has been changed. (cancel-timer *dmr-timer*)) (setq *dmr-timer* (run-with-timer *acl2-timer-display-interval* *acl2-timer-display-interval* 'dmr)) (switch-to-buffer (get-buffer-create *dmr-buffer-name*))) (defun acl2-stop-monitor () (interactive) (when *dmr-timer* (if (string-match "XEmacs" emacs-version) (delete-itimer *dmr-timer*) (cancel-timer *dmr-timer*)) (setq *dmr-timer* nil))) ; The following won't be necessary if emacs/emacs-acl2.el is loaded first. ; Keep this in sync with that code (the two should be identical). (when (not (boundp 'ctl-t-keymap)) ; This trick probably came from Bob Boyer, to define a new keymap; so now ; control-t is the first character of a complex command. (defvar ctl-t-keymap) (setq ctl-t-keymap (make-sparse-keymap)) (define-key (current-global-map) "\C-T" ctl-t-keymap) ; Control-t t now transposes characters, instead of the former control-t. (define-key ctl-t-keymap "\C-T" 'transpose-chars) (define-key ctl-t-keymap "\C-t" 'transpose-chars) ) (define-key ctl-t-keymap "0" 'dmr-clear-debug) (define-key ctl-t-keymap "1" 'acl2-start-monitor) (define-key ctl-t-keymap "2" 'acl2-stop-monitor) acl2-sources/emacs/README0000664002132200015000000000027011605147050014521 0ustar kaufmannacl2File emacs-acl2.el makes it easy to use proof-trees (see :DOC proof-tree) and contains other potentially useful functionality for ACL2 users. There is some documentation in the file. acl2-sources/graphics/0002775002132200015000000000000012222334002014343 5ustar kaufmannacl2acl2-sources/graphics/acl2-logo-200-134.gif0000644002132200015000000002213712023157775017441 0ustar kaufmannacl2GIF89ackt j qqlyrly!"m"t#y*s,y,2u3{35:;tB|JS]<B;|B; 7w C ;} < C| C D I R `>E>|KL[@@QVkTA}AJPYcDI\\a"H}"T"^"b#F#G#Q#\#d&i(u*f+L+i+j-R-\-l1m1{2[2q4s4t5k8v:;];zD8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc.C, all rights reserved.sf32 B&l, H*\ȰÇ#JHŋ3jȱǏ CIɓ(S\ɲ˗0cʜI͛8sDOwq6mڲeӬyC7Χ;GL>uJekO3ϦfB] S:kkA] ңE1#!ql P!H=#0c&.gˢj9o^He~} / xC=Ӑ.,jfycmB&jAP)P0Bl-.dl*e^}!(jf0ˆ!N\K-Kʲf%Q[g BRt A#ӑź /ZBm6g ^XpBؠ]@;8ز 0 [ʲhD"']UϫuQZpŤZP BuB{^':˺_M3,/n<PPч>B؏DO2t|-̢K0EA*4}]qRD ^Ă`#G%a4#ALR>MzGa e(C*,BvBd,rZE(=Rf0 eC0bf񎈨L%Lf.ÖDV8Xw4 5BpY_2XWs! wD^씈=% [؂< `S4>QDB4; BU `Dz^T4h+A ~CɗІCP6%\@P<-zb4O ]X;*ҏyA4 QTM,rV 2AΠCp[%(+M+Z5N]^*,}fЁdIp,XF4ei:))hmt/!lG: Mh!km@-me ٨b?s0ʛ9Cy3Y>QxC08эnl#o6Q_ppG<6tshLp cLcu=G<lB_p"@`  Ft&a}dkJY򠍢/<4|A 6Hlt-@d X Cnk[&k@0P;Q` 012w@E'\Aot(:GpA`] J|]9o}*$ZS¸s5\bs8CR@a%wz޳ @LꚺD8u>P dT Z t]6 IA8@g;,] ~sEB, UF;ڊܱ": B8mY~o[\ҏv@n`j[,LpZ& TH >"o8 @- ؛MloHn{*0ېGkJ )ԙ(_?\Є.i%,(xSd!@'N1+;˛|!辯硨1xA D(g_rrA R¡kuq[0` M8z"#yn*w-OaJm!Taj  ʾ#s}; Oym[Q;s{F7p0W~B{-`d/yMnSE%L}0 ۳ 37 A'6ݐ |$0rW}L7y'b=}?@sy6~8wKQtW`1JmMa~^bLmT}02c ` 0 S 0 e?#w6WdpQ~,85BM@ IPp0yW7]hĂdEdWڱS %!`NhfHu5K&R 9a@vU$BLe@5^Y%#}&( S0ubFYՂԇsUB1  !*@ 5PB P#!H@v`P/סJGUWnW %犰8nWЎv(~ㅋZD?#@$`&LhRB du$p{؍[w#2s&jY]Epfu5~:SZJB$!(ICK p& @ )(yX *Mlg qCp'n xYCȒ!9* NȲ נ:+ P!Q٘uH!DS% vAOVZ@ p ΐ _p 6!PUwSV[/ؖ29Z$J `I =y /3bru9LWz KWe6 0 Cfd 0 ` @49UО\B>dW19O2 H<겓B P p v Ki9xn]w ٠ep.%8dr ̀ p\`f>t'/`G( J C 4a &!bs:UU ZeN ~A2 \P / t~ds0~*bQ1 P N lj!r@>T~ ݠ9s* X,hT`p j @ @ J @ Af| PJ>{݁ pZ CCi BY| BڐY5 밠}D jWH LzPP p!Ar*0V@ٯ,"/ i @ò#MH@A0P @$z.lR&ီ&yA qLu @ɀ& x`D# @ dKp>+/C@ &e g~j   *#pN+C,JB 0l _   P Эgkq'@@ -#´Q `PNhpVP ΰ ߐ p7jq 5kea_P  łD `$P̐Q0k g "Wwp @”pGpЭ P *!`atq @ s `\-jNP`@N`ݐP `1 `Bw !H-= 1 ]P q'KgT,0 ǭ @^ -`8$>P `78 Aـ%k#` /dtF=j` 9) 68p H`-8wp$0+q`݀pF\г#Аq p-A?PF (#qFg   V0# !g pp E; )m Z0&iP 簝ܹ6=!@F@VFǡ=G!@ _+  !=$ PAK Ep{ ^mP A3? f+P tAN Fc>l@ 7@7P_@HGD-|@ h 00MF"`p q  }]^P& t Ԗ   L-E[G #M EC$EH: `  ^N mՁ  ޱ[@ n@}  A]F D-=H8~-@ plTɐWO܄OL~N ^mg Eu 0T}j"zSz1rpw 0 TMrϴ8r` ҍ$q0 z D MM9,R #0}]C`xA@q о 00 I` mB4>DHP ` U *E$ph*Q>@N- %m^`= ^ѡ 01w@̎)#`";2t Z0( Z %v0 `KP*Q`Z_ 0xBd101 Y3 #0V [. 0R0<Q$q$.0 0U]Zp X ` 1 0 k%A =܋@ . R00rq` ! nڡ? 2@0 PѪ!CxA"RN]C AJǏ H)UdR8 (kY&JpzpMA={.ܵi˖%{-ɬ{#V8ZLrRٚ/cD֣&7nn~yuRphpgME Vf]ʤ: #b@e!XJr 0xf+V4Zd[*prmB@}Va8ZVS[Mܕ\' ǷGe+{bɚxsd1KFi<)@&|D|rDB] y[a ^)nMJ6 b POw&I糺abY&3 -,L sPQ'vF^Z@g:lʛ`1ΖE#Hҁ7'eZKKk ${bm ''! ~pn@IrwG |E6 7`y| Or;<Na]f@0 *Q(#?q`8fyG 0/ݢC2Z[0;B%Yтl%ͪk¶Dcz9J<dg[JPFH=ca (=-|SҎ:U)HM4z#6ArIJ6 f(CqA pO LђăKaM$8R0q=#P@@0'd@g88O4LMqFX>gt#G)^ejp pt#u)x+367|;@FKS[l 9{dj^J@Q=WET]hKWcn J"G"V#i%u'g)\*L*l,f,j-S-c.z0R0^1m112T2`3q46y7q9u:X;D8;L RGBUniwersalny profil RGBdescGeneric RGB ProfileGeneric RGB ProfileXYZ Zus4XYZ RXYZ tM=XYZ (6curvtextCopyright 2007 Apple Inc.C, all rights reserved.sf32 B&l,>) H*\ȰÇ#F̷6lIOt„sP8J-hΤI6T+׮.NKX[3mۯd Z .ںo튅U>曇0M=I,ʟ|fM5S̡s& >C;(箻Jfl#GR ?gTo߫=H{B(a~)^@&{(8/ыTx7^p`%PgE q@ `B#$ w!_;Z5φ9XCfp>Ȉލ\F\kGC&2D0^3-^""1z/#_'.q m q7"32Mqc6C;JP Ƀ"ɰMxSk!lHJr{%uH42f+ ,Ix  JЍh+khZqd<%i$+j,?8FPl,IKiJ$Rvr݌=VBҕxsg{h2I'yj̴a:I1 =QЌ`<6ސKKeo=@(@jIr&x\bF0Ք2BG=Sla S*6]!NDќ h6C|Y,1cJUD^5beS4\1*_Q,kVơWzG/}1 1VZ(Nzw>"zv.-_`v/[(T;Fւ'g=[ۺK[)aҀ`q;\ٸcTn7\[)p{:wPCb,[ zKZ#2jn[W-]€ ݄'mֲך##pᚾ21o@}z݉+&%/Y ,FRٿXꢳH옭*Ec,ٹ<βJ=oe͸$*w@9=tc- aӳq9ir˫_^@Q2)S'<uЄq[-S;[U"&'][i?yưo]W v(jg Ip6)-vRg;#+'Xs3 < ҹ[NtB4[w5˺}%[aû+ uqu5 .7gZZnxs\7.(wpNnP=4C+9aTuح)Է׵sm-uWBO8as͞;˕;۴w0N+Fo+eÔeh?Ϣ/'{Y]_`O|3[_<|Mt7>߽ͥK&~ c컯 <~m~~FxW7P%wl7yUh考DӀb{fvwh|e,ȁXʧUo5Wzӗ~ǂgp/0(}4H,be88e'Swz+AqDAZxt]|RFftxG~hRovwg&WpqFhQH 1v'7U6bjȆ,^xr0wxznw2Ńwy(x8\.u٦il#z[u|Z!Uhhf8uɖp折Xtȉ;x,ȑrO8Xu&Hhkul`t)wgqVu?ؘpζ`gxh/GpfghZ(~gz爆 wpH87hd`y%ЈH0ghH~Xmy*9-b/m=)׃Է9<)/?]A+hLwhȔQIYUYzd?ƊŔAD89wSy=t {)JY8r {y9IwVQƇ(ysǎ:Yx6rY Y1{|w$I}'{1{֙s}GɆ a} HI0uSeٖԵqoɛ|᛿}~ E8 uXSti~Yh?)F&fx9HuȝGlxixc=d.sUyoGGi_e)pU0%lH詞i'F)ZYʀ00tMU"i)#jɣB5Ajpg,;p#J9k׎8س-kךh0DZp9KQJkfj*`E$+(# `;697r;t[v{xz+4ɲ]ٶ."q{۸;a J˪Ci'f`{~1TA9AXۡj;&غftdK#HN} iEa~+L{ kw4¼仸qvZK+~;:Jǽ)m ګ{q)ǧ|t+k0A˾K9y̻%6,[jk{ Yڿ%ܮ,ܠ{24\.;O:\''l/ KfK< 5l ãW\ I,S!һכyJ&a,՚e|1bÛAt܌ ,\{|<ćGp\\ ȗL,!~ wʄ;ʤ\ɖʚƨk`˞vūL˿\^)*a ˘L`;Iͷ^NŽھ^XA>~>xN^nX6^/[Ҿ O?tX_Ξ .&?͎*.*4n8_J>$/PnT:XM@^S_bWf/^jOlO1petZbmqNvkyO~^ފ_kPsOa@JtAĒ?@EǢIJ%JL377.OR1GHU>;XYU[\\BCNHa:)Kgfh%AfhͫZS[ѳsAіutxqzpW XX~Y9г^!^n8"X0C lKFQEi0VؑbE)ٺQj㜼/عr1erqG%buA_ XTCɂB0! w@:U 7lX@zԪ%alu$oJwe;3R%]sUCClprd:#5>Ml2+dfԼ&[z|VY1ACNUnױe^-a~;77Çor_ Noil"m9ua;>8 u nՕ)ostZ_BPAÏbȌMD# FLr=c¹м C';77DX1 i4B$;C-&dRB/ WPț^lQ@w#Q|rI. چlʚΫ2 EkRBQrS=E5& 0D ,PHU{4Q:KGӑL3M %Yǽ`;uɸtH1E\4F]4VBweSP$XXd=קruvbָiJuQ4Ug#jllW[u\z-'Fp5w;9z^^_9.* <;Vcp;ydK6 OsQvecy'~ cGʖ\@YfhhVz\l8mLOq M8Sle,jZ}[ۼv|R^D*2ĸ7`Xcls5Ww3Hs=p7>,M{\md.BT+w MKs,h/z>~uRku3 ;,N$KEN~']z?{NewjT@q/w"y/x";Z6߈VV" ԿP\`t8 ASbx@1A0r y2d6~zO('_ !P0gG ʨZAO*(FoLX,*pO8Ek^4/F u ^ >,7%PgZh7aLxoNG|E1:ubQX{}(elb8^qcCǕ=t"OJ_ẓr\'K>lϑF8uJZD`K Y2bV/y"Ds$ Jl|3g@ &tu!Z8ņf4NFG:Ѓt;B';%\;'Ÿz?l?+J?Hl @/LCn6";:ÄSC:|B/@C,CA|Cs9SDF+ @lDJ$0l2$; J,>@53DT@e[AbT4˽-9cIE^<]E`tXE 88X9Y FeBCS>X\Fiܹ%Իhc>iFC7>FnǦ[@c?#Gu8HԾ9,uGkPdG||8CWG븽dH\HlH|HHHHHHHHH II,IIxH#DKDDh%QN )g SJf%bR1%lzVX,9ty&9"Hs w╁zhƴfhJUt)f6MRj;фDeƺ*R+0zkKɬ*{nTkfv*+ (+!5K-&;N2/{V/jC !<p* j K y kʟ\+d1LB"1&˝L,0E?1nzsO8/k\2B }2JdUij}%8]3r@rP16͉)c)FMsfj>,j+M*Pxlrj3s{ܷ ;TkW]8dmxTkzR;梻>_z  5X;~r; mGb6&ikt;5 |u};wR9{=Л*2X:F%=X$:ŀ"q`lF10l s:`x>(ԇ m#` 0gD;pѡ@xH  9i]ZB HD1M^xE,jAB1%Xo-'55s+G>qbɈB1f }@"G@%W+&9Ux9 Nv EyNxf$BZ"9N+_X)xLJ+ܲM0)c3 sk.ބ gB'ڳ̈́Î{<u嵝jj@\Oҽ<ّ[;mN6%/)N;]ux݀O,?Wʾovg90N/뗰zzcXZCx?Xaٔv]Ɂr5׷,ﵿD IKgмwxyhi꘍؎b[gjgeSR= ԏyhBv\3Vpqq4"/9vn9Q18k?y|*-UƑ]Ƈ(Ǔ=nȑ\HZ TÔob1UV26clI oUy7}dّfZBc% 1pY6"-+`a'|2X b/9 C*^GVfY@ߤFDԈ򁥨)K`9GHFU TYD@AW)^ÅA# e`9bVX٤g06}Q2 aPAo6FjQzY@"felٚ;(}CmG)uZ~Y^}@fC%j١j*G'R6(G #iϓfy)S뵟$7-Ty9zrɡY@h`.O mח_T\}r O9,TUΡHq2U (Sn y p)b^*/&zQWz lz~76ڜ *0и4{|B@rxmjz4Zn[TШ2ìj 4E'Ӫ} v2ޚ:Qc,슓: )*D**)ʩ:غJZus'3 {0˰d5 @c2ձq =`";2{ o- K8v0;124[s%:NJ,>25DF /ڴpdPkRE@fqΑ)4Uk^~+;=H -QX{ `^iz8Ķ{*HUǷ/^@J1bddEa;~۹8 X;[۳tr [; TB[ 4gW{''3 -r[Λ%asHZ&kkM䛺) 8[;qz a{ H![R8RY¶  ; #ʋN|+"LA E'#< F'7PS[ToALƗ8;$ 6#6 QZQ*#>"%K%,#",bLP>\,oA#Ŷ b,L6k,2L b]A]n O>l r~l-.:\ƫ"<fvlk>V#|#9u$Nd<|c\xˀ¼;\pMu\|؜ڼ<\|;acl2-sources/graphics/binary-trees-app-expl.gif0000664002132200015000000000230511605147052021173 0ustar kaufmannacl2GIF87au,u0I8ͻ`(dihlp,tmx|pH,HE` X- 1Un* ( ť&{-ިnHӤ4V!9nJ'pqnP9̻ (kY p2pc-R(Vz +lJ+[癀fV)W%s6+,1]t*\*M;mD9KmXg7Q혶kv؆""O&D ٛ$Dېf ܉Ŧ}q=W}q~؟zs qv@ovf}ՃC4Bm ;)=UIwE{"tԟ u*H$A;acl2-sources/graphics/binary-trees-app.gif0000664002132200015000000000367511605147053020241 0ustar kaufmannacl2GIF87a,0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|Nq[E{56w 4iyJ{0pK ¥» oHɘ*nGƵl@ $ (ȸQc,ȁٿj8R<:ICw2EnTrh &Ο@w J藡t] ҦPJJuJjň֯ff6, b~iڻP辅@.u)qE\8/ nB.qf 3Gl%,LkFm i:H/sն/t+[Ek.I{GA.xdUAW΢s=6})?,ڄѤ_aZ4ïo}Оn~5` w| NA!=h~BnanX~,Hm7k#Jbׅ@ a0y %hf1Ý*vBMo&Pirc]IFiYbf>n\^䒛\Yё՚ǵdV NچgsR BnigSmWH-ME'QVT"edà)P(蘷bow0cS!z(2!Y3 U>8I DA:ZBᵊt ġ)s6Pn(%UK+h(-h-CpC_01 qLC; lwE8),h k_ѩiib\ɜ#͓m R@tGṣL.KxTA/58?Gʻ4pM,TkF 22j 5SuݶUo\_"GH걑yb$# z&0X>MCud,kȵXvQ b xrcHbS']&s + 'LE (f5w@VnIN3fnus3˙ "Od鼿M@A|8Nt4a"?s< A0sa0Ÿŋr@O Kb)eLYar>P\!3vT{X҂:B'eK,ZLZԖLixOV4UP!9)Ci'iҜVPYժw1W=4"QRe+OKjS9"ũrWw.PdS_#NԎ2uԚ֎debXC2"U괈frvodd}k66ZEֶ-p*' .r 72A#tˍ6R$v{F]ۮQW!;6$eVS2]Rh8Iݠz\vK,y[/^B2ZyJRXT%P) b GL(NWI;acl2-sources/graphics/binary-trees-x-y.gif0000664002132200015000000000254611605147052020171 0ustar kaufmannacl2GIF87ab,b0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZj`  . .Yw,HK~;}*jxindPLJZ6KP0 ߾4ْ-LՋ͈ǀyd$N?uNs'0bǏ CѓD%Rx"݋YC7+xJ%qya?$}SΥrO7w5ΖSiFC$x` P؎%1;'2šw-}ދ$p}:%=Lcƅ 1#5V|dgȂK9qhm oöCZ7jTn}<l\#K_rFv܎ΉƓ06+(1niy뙶7Bw6Z17`~ПOW]?gR!PCsPAcON}Bat^H704d6pmU i՗"-r~CHGZP}vчB^?)Ch{9Ȁz2A6X.ŘS-z`_>ai&r9&FPVr曑97.ᆃbb@&pnُV)u(ᕩWjAQG媆|8Ť::ŦAzOdY,FnQ+:%I6PmU2YԶfsH\QFj/ (xBmd{/]Zb/EX:LXB;1]2`[& `l"3l^i/b軒F$$?}vI`X%}\#yM,%& `$`O]̽l]2\~Em}^uX6 eUtؒ!>رYNײ9ܐEҵkr㝯>uӭRROv>z+1DѼb&T{r*eߗ6u{>j'ث?j=0*k*EoH*BDseg ϊԱ ZcLh2Q-v1rFP.? \zXqۻAxЍІ#T´Lk! 慷#6.Kb$8RbҨ+ڎZ Nv/~NhTȕeiH:x̣> ;acl2-sources/graphics/book04.gif0000664002132200015000000000173111605147053016144 0ustar kaufmannacl2GIF89a!,!$@)'C6|HB BG\HP(%rFbr)dȖS\eG3OrYeIyXѡȥ"d UËUjTA]T,ز^F5+2ٞbWEUmܱݦ{%u A+ŐU8r )̹;acl2-sources/graphics/bridge-analysis.gif0000664002132200015000000000457511605147052020133 0ustar kaufmannacl2GIF87a/,/0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|Niz>s:6k2.~*q&h"¢ȚΒԊڂ vn+V/@PWzP, cFx/d,Q2S J*cʔ eŗb ')}zQh'UR[Lt;mɼ3 NIٳhӪ]˶m:]9 *I* 7kݬ[/K7}5WnZ52wͥ[͝3_ҥEEXuQw'']5Uڗeֆ%cokq͍v}K@ճ\_];oc][9i{_9yQ`_>x^~'$eÛ 5 %R[ 6Y> :R B!&" `Ȝ8E5H"XF(c4z ?6(L;ܐDcJepL695!LM9aUiOrॏv&V 5ٝJEp6)'/tWٹgX(n#di(i袘JeZʦ]:i6taСi*LS*|~JdZ+\uXīlPTH陕 Q;F-B5쑣:..^ чMd͖AE cg 7 A&p ̖4d&m\/7,_Ч = ht=l +@MĆ0#ˑWX \u0-(յpm__<uv3@t~Y M18GV Xy!<:闗:=%쭧zѹn{ҽ 3;ŧ;ɓ< |+?}}0KC<r}0˿>0?q?P7.) '5 ԟf4,z)x`0#P A0H7jbB/HC@P .zh9j( Lh }b Mu + EQb`H`ۃx2чA# -! )8ǩюF9!rd#,9HT|<$#E@FU$F y$NR&7pGqr#$Qf < HITВp+'UrTe-@\,J~,&% kib& LP*Ԭ&7rҲ̄&z6 ل2n4ͩxB7 aBӹPʓ,>NZ4'73ZJ&S`D,;ђjTgI Ґ$gJSИTulF.BӤO!ԀiM:SnL % jQmSjbRuBiVJӦ.DWV5c]=VpECW"_uk_z4thEl<*6=Rvx5!Y_ֶvr}]u1-_ع86ee&݉iS־|mis6-qo{sCMu[ ~-2Ļ]7}gnk90$V #;Y2q[qch8=.|o^89ʹk.brм9SlsV }NFғ.p1 z.!D8:oSU:NhOp;acl2-sources/graphics/bridge.gif0000664002132200015000000001071511605147053016304 0ustar kaufmannacl2GIF87a(C0`ٳ⋙,(C0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJr جvzxL.zn|N~-~_юZ~Y Ե <pÇ!Po /jx ŏ9b(ICAL3.cʴq̈́3ɳg7U9l'ѣH *4/N\ʴ\Pj5Iǡ[ee KaW_-h'u-ݻ=8/)~ӻR+F0šCqᾔmUy彘;O,qCnDz\Ѫ~5۰b{Q;xƓ~r>Kܦxc>:hO^OϞOϿ}_lxƀ 6p :XVHƁ+YdnQЇrtHJ!q0\4ֈ6樣8(a>3cD$=IA&dLETgXfޖ9vez`(Yhr&lݛ1)tth|' ܠ(j(n袴5J⣐&釔Vjڥfhn駜jᨤRfj ٪b:(묂୸zׯ+[hj%̒lFմUkUQu[߷&%.}{Թ񥫮OS}:/3޽Ƥoz{ҿ,pIg% k0wG UlCcu< $t$C\ϥ<0+'Ԝ82=Ak9Eǧq<'؊Z`6M;}PS0׊KVuJ5ؕub2گ"T j]7w&w?P}3#jwq E{ͧw-v[*9?gds꬯n f妡:״ wޓS&|`oo5 V.ɞy^%&c=g!7nwQ<^sJbPs >!i0" >NV0, \y[L 6BI;0~a[XBFv:0! ?+I6,"D -?wht\I?uA9|b(%}tԢ?7.ѻXD=qw,E1EzcVѐ\#bELQdd(flIJx Jz]9KPqD)]b`MCYA.٣A1҅%G>d3$]S-_H{t̤49Lp0D'8Q3& 5q:y'8LO_ C.Q~&zr'D PTӢ=GJ}Q|3ВNB )I/:SZ' 6*ʕڔ Bq5h@z5oF>E*O!TO *P JCX5(=` 3l%+PzTjӤ AT. WcA:UeL7WɥIB"!ۚU*d,^'{WiZf9- "%[׃"R|.6Y!}+yv-.oⶱEmcE˴X\VuDboyAViK]8׽M}\gi ` zMK 'L Lj veE—]Rlaō:]k ,q}|YeǮ@ob&[D|c,O9yR./ee3p>sb'KٱˀvsLAKTqa ׬4Cѐ#IK͘.s8rsA]hJԆ6lgtaJZշuKk:cNd2ֳu{=ldﺀ-˫;Ze?ۺQ6S/VM jv]s&rKBGmunܖz3wb pxSJ;|ĉ7).pw1! x.7yA>rE@Tx/E~s[@{5_x.s#EfWcz΋IG;!ߡf~vbŽӁVW;(>xG|ֻN{FU^+;}uD/W63xɛ>򨿺ikr"=Gڗ>?KFGxug~~-O1{N_챿}?gm?+>?pSW_gx<cN}d}WGagX!$~g h (L x]hƷ(%XZߧwb؂x* x<(sX LE=އ:؁GCB ~&hMȄ9x4I 1 \6hHjHK5 3sZ8vGiȆuH,YXP1Rk~gXy聐!{XGx؈|Xg eV}ȉKȊmxhk?A72s xJȌmxwE&@lS،ȋѨxXbGh8؎8X`"MX؏y0) ؐ萐w&8DaY!s瑜rJ#"%iZ!6p#2.900iP<ٓ>@B9DYFyHJLٔNPR9TYVyXBy7ٕǕ^~bYf Yjlٖnpr9tYvyE|ٗ~9Yz4s٘xih-T`IH&ə'f shIyZhyԚBw6)]i"Ǒ9I?YI+YyМ|c9UTI`{)ɝ )C)t_bɞ9ɜyI92j*d9j4ڙ* zIZ0g4㢳(#, С ڟ)%z$: 3eֲ{Hď19,c(QPJ 2b7xJp4)8d~ JѣH*]ʴ36<%5*.T:5יm hVXpm[ qG l_RJk^+D0 ljiE<$UdIbʫ{% 4Yɑ*3+hkں^jբ?vjھA!o=Gf:uG{@‹C66Z~eп[NX\uZ\40z1s {W!vF(ag:( Ҧ\xw߈0_أ9F&"pP% fX#1FDV>) 2bjA9G 9%L`)9禅!9&heYXP,xg8'f$$plJ9~_Z& Zh`fRGa=P{hґs5.Ía{u~ih$Zvj[N+"6C"9VU6H r"(Sv+k覫+C;acl2-sources/graphics/computing-machine-5x7.gif0000664002132200015000000001243311605147053021077 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|NKx><{yw${vW!(9Lz3~ţy˹|ʥЀνBm)XȰY #Ưd 4ō֝oy .× XJ)>)Y'#iw0Wh'ͧtl9L*]NH\5 t-̥Lf֍U׮,9V>\Y@ug4!ڴ1G]cMzT/:~G϶hb_epڵSIY$;oƒ&[V#fZq}!S1D8Ե+ňoҩWO^)Q]^t˗s0Ͽ/߀Mug 6({)VxZ"P(%(_0Zx1h~3x<=)d$99I$)w9 IZC`)dihlp)fbK:>R ID~ְGb^aΏ h@,vV9 &i}0)i3GcrC0臟*|j4J*)B*jP7J1.V(`l?qb}(k-u*&vSYZj_[ɶ` JGD-10L gBUyv¿ Rwk9\m{;q | />8u<I /MJ?<4l6_ ? "SKrV%5,Ou3u Y:6 QmAN5KdM+b'LO{{B{znm9Q>..E^GE˺ nثk+a{2z觯oq/?o@O 1+|X#?.` 3(?"qN ng@_9P p,D 7La[8@ ~%>G 4C6O _X*ZQ@`bDE+^Q[zL4jlp>DQ:ġs8?R c<]T$H5Zҍ'd%C 1ZҌLc*C>g4e'oGt XXl (R~B0`IYzRnz!t1ZL`e8uiMdbӜY# Izӛ *1BN9K=s@BІvSw&-hK!8f-Jo9`=Ra%9IQLnS!A%jPxyeFwvGK;E%M}yP )ÌiUM`j&?-,#Li;W:1ƌ;:w 3hMjAj G]OJ֚4U@(Vկl%^JͽLe:.#a'֟λ CڕhBzU- eDZ5*cs)S~6q:{;VrWէem"tQu"ka"oMru+@!s+]ʠٵ-B[Dƫ[Տ {{ Xl [w0]H+b?,8G\{W~HѸƘ\^t'`876NJ,d 6QW 1P)șg*E+ 2 K)5^2kB0g [hHrӬeT`s UrN޲B?rn}xeGը"v{!6MCP)In21: 8 46l0#0y L=𕧁 fys<_06[AV:b@2&zSaHg4h.1oӞ哖ߌȦxNXХ7mI6@qc-jN)$4AW lU uNjz .v&ؼ ^!Gҩc+ӗxX;BD=/$-$[\raZ˚>[n{]60A|Cd=@MyiⱢk_zM^MhQR*ؿBqu͛?\]Ik[/Ãʋ4Cb9 \')AZĵ 0GlڎyS6ھ\Iܸٞyҟ]-,YoՓ횹ͫl[̍D)Ŕy[ܒL3[#=߿͝޽XM  筚}. U[U6 kܠ ·. UAucb^^Nmc CqJK<{ }J8@~萰͢nk` B靾n ~] r@.뮞)N .< M!Ǿ;>< >;. .蚎"Ӿ:>핞ћ.N.츎#|n爝."9N?_ /?_ "?$_&(<;acl2-sources/graphics/computing-machine-5xy.gif0000664002132200015000000001273311605147053021204 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~V",=Q '8;$5Z@ƒʺɨڱ˞y$p.~!78m )N. C~F$(n دA (_)tr[B/[t΢DfsF=D*]@Ҥ91|bL[7gFM9wȾ ޯb+MaUj'w]ܵYH[ 61cȬt3w)*{5XRo"lOzr \vЯn=Zw |8n$KcbM}b׭CG{t== ˶pL?fX`g;׿7Y_x3ˀm"L (D`^ Nh u"x#BGK]ኴ,o0XɆUV6a(*"@@*(ҏA66(׎PVi啛\v Rd e[fl)gfn9xR'yg%{)%J衈bbh6ǢF%t@*Uhvv' 3(`ꩨꪬ꫰*무j뭸ʪ- E(+[Ro3Dm-Tfgj*LK-~Bע&>+& /KrBƀo hoC8/*"\"pZ + þ8.q#_CPu}|&Z Ϝ,#y=<!-ڹɎ^IG9w+)l30CxYsR[Mb 44]#u]_2 %7vtS84v2j6;+$0SmΨLHkHS.=j^#. s襳l<]hNK+w';{# l`T4b`ǎ-w?_9,o~诋*>B p!~ST0:0PoE 1@ r-¶FHB(ݱRBv-adB%\D`=`RFC H&:3|(RS`ŒD,zGEPSh9zv"E.TZ|YE*0M'EPC $2#dIN^ғ &;IRoR)2:AKdyFAIIS0I42ILj0xLdbrhJͬ1M4\'A uR.YJzrw7 07)Nrb@Ykdh)MVӠلgD6Q`.DF ,&HrS}\GMp2]&S` CXKldP_TlCyZ$P%]0-pLR:U3$:ղcjn>pC_8bhm\$5^UI#E,_OIao^#5+%)Zႄ]XeF\Ҳo/kg]GaCtp$EqS,+v—=y`:4ռf-8q4'R^b4FKbzA"H/-H#<"lq;(|v-{[huݑf/vWk~P6#}%Cvčkcԁv=`kDx(4"[[]f}#097 A{oOw WBQJ>pi) W\\eLuwwIk7݋~7zR`xL*\ LW.%h783 _:+~%AR_/qr`*9AmƓ&)e)i@f=HÔn'oVZIx7K>A->Nhh,1da3 Bd'NrzDGvyey_T+T֘cZ&js wyjm6XpRJi1c6b$cM8I )XX)I99H_5 vcwis6d4eHiY^=KheqI~Zŝig\̉\)xe1U;Ն Slj9 A])X@*[ǕDdljFe7E*b f+)o( nբOU ⌓ȟ0*d6ryyat+ .Zsy{^ٞ٣׉L=Gw`ڵ KFjVZ?z2z)Tf bVfdƅছ',tJ4֥aUceZK*BIc%񊱧f]gHfN2㘣kvjɝ {Ø֨թV6k&Uj+Uz;|:dڧizB2c-9-n{ëh:q5uP#, fખ [D79IvF&Z8ڬfĥbQEy:Jx:S*agm2k k]5;hFl7+BDKEI3jM4' jK5,] XwYM6&&mP=b>֤_ arͪ"[`{?׆ۭW)g!@p=ؖ{[KkV`<ɔ<ࢠ̡ݑ 5 nߘ-TްiB8]I>1['~B)OnuN:^Yι#drh}^.hRn b~^O⇞n靮[^&% *ꍎV_>,0PAbʭnd\g%cNk<͂I9']ΘNn{f^ܠh.xr3tθkvnz 9?7hڸoo}#!/uO%?'ϔ/?",/.4ҥPr()>$@/W ӝ[/HJ=IB9p_RUqdr^ 8/:POk?7vz,:&4U/Tn`hR"N FX $F^a#l $h!'̱"Rیh㍢H8&:* DH&FE6)JF)L:Y$SfUZ9$[)]zc`) ț_I|f&O砄vY袌!x*褄饤fJVbirꤧMjJz pjkys(gk셾Y&F Jkm{^nm~+Mվ2l厫.5.g'oBUkn2KE' 7㦼- Y6oʝ`{Z&kvPr&LsJь/7ͅ &w^CA)'3#(nq9 KmupأR cch?rp?qO=>uۭח q\E}ƈ!1[]vLt7^k]x x~k΁V.9ݘ~:F.{n0.{";:UA$mg;ڮ~9+:Ol1u߬KC/c}⊦F?,m(l 6PZ4 Տiw# NC"A=Pxғ%.q|_ [XNl%B e@t7avxp8@9ч+t^[EeDÅ1cU(FtCg>Ƒ~cEuv<"F:\$JZ2&7)EV; e%3)R6 WaV*,%,cYRpKQڲ*`r2Ƽ/e2 (LL"fF̀`$g4NHRh|E9Yf Ayq4+ːD@ًyd>@,gDYkhz5HGj3vHF3яv4$I;BtK_*ɘ4o'#nҖ1jz jԨE*I:a0NeNw*ՑRUTNѮtK͈9Q>-)P5)H{ӯA}[ ׾que+qz굫 +Z?bucQq} hG;$敫*Pvz2vflEͭh-"zճn:w5YgthWk]Yղ S[vv`=^٨JWWk/Qb-{yڦ@HiZ|{XDn .8U@yW&9lX.umtC5*祮kXm{<- ~K?aWnqM'1x%2|O*yBLf5N]\b&kc®y4ZXgΝ3Ͷu|p_eF䉢B`T5:81 |ScՁfߧ_Mk*(re-^M3à S^0j%Km6F2`]iڠ|)d+'nd;Vl*2Ӧv / p;F86YnSlHخf.p]߭l mNnqo{_ýnZ[YC1P$O~q.#*~;9c؈`4*N #9 ~8pKiUOeҿg3 '"x}f?wծ:fsXv^vbV_D;3F|&J ;|ֹLCy^[>x["yQ^>ӽ%t>{2烏O}cCr їe_ȏ_} }cz&czvdK{`G7Hv|Pw(w GǀY'havB`kw֧C ؀}vTTcDr7(_9tD!{P{8pɑ}E{<淃>x[Fk%Ӆ K#)d7 Ćo'%dRgb&W217G}8!L?ex]vX6q8+('ܗGG(m؆؂R(wx1\{؈@$sxBW'O kP ʼn|Jyh"9ȘdhfsFiL8 G(SHxF%&gme6bv`Ȋxg1}وx^Aige'(&(p& Vb{Fh%f3Es38(/$pg؈%'I(9+y'v1)g )#&r(D׍8iu#xD9Y(;)YtTٍVkȕL)ya)H7Z؋‹_i[%)@)z)o|!|cx7&inY1pSItJ3 U4wg0&IKbP6,jfwKk𔦠iț\L J D*,upKaФ5I5ŦpѢ(PƖhggPxi`z 6Ccåsmc:m)lb"hZuhH eP'vfVLlVXzoQFIfgfWf3lR \'vOʩ9pT*5W(V $b03f hE:G bEŪTkjT[٣ZV3+yqtuEJYҫJ6m&ʐ:O[񊯎GzVdju)gZ_* Cڰv֔8bknz:vꨯu[ꩳKބ;q>-nʧmf1 Gpkz(%j*;kꌚrY T;b&g&nЃEFovcc{>gˣi4kPqpkvlXyK{KÆx˭mlp4oZ世 y$GKt'j0wG ;+6Q 2۱# »ח ~ϋś ̫΋{+b6wրʋ˽@uwxk"Q$ǻ 1j˾81+(tW ( gC "a|<I P+4LW-!\K ;;"T~Ȣ=L>e1ltGE鷠Cl/Lf 3|Ăc?%2uZX SLgc,`|2[̭]̮2S7,q.O\男C$qu ;ȃǭ@|T`r|,\{ +d4vQ0s}cK7|:\33Gh ;jۜMooP[10A\y.g^_ ʼ0j3.mt!=;r+$Te GP͇ߊ+Bܳ=ٌ߬m2DijN+ n$^TBy'N\ 3n0M7~?> ~5_)NEnDΔ wA.CNhh-G; =1)OKDDlDY,COW1QqǞ~&G Hf$o'Jer*cʑJ. 1,46\s'49M2:7QQC>O@tPB 5>=DtQFuQH#tRB[RL3tSHSPCuTQ-TTSDU[Cu+XuVZkV\s5YwW.{vXbYXdXeuv8fvZSev[p};acl2-sources/graphics/computing-machine-xxy.gif0000664002132200015000000001420411605147053021302 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~6",=2\ '80&Q‘;-Ȣɒ}ؘΣͼݷʵ#X0޳ԎpĈN`YÌ=b\#C>ȜIS&8it8#=+XrK-9ԥO(tڎڅ4כ9þ@zhR&*gÑjM{ݷ'k_ӌXVnUSSEibx:w7?]K. UʗF>\*eL%6[,Q=Zᦓ;Ƹ0(Hf-Y[_[2;"k{`h6VAg*ꨤ@njr`꫰jƵ*뮼+k&첾J_f봱襷:{S,P+nt@Cp- [Bh^0k͹Ѯ0˯cMb+/`wo .\ \ 1Ѱ)l; JL! !\BqԪ2$L0r'r sD3 ? gM:u|tz:>-P* ՠZ: ';Zդw֠sG(mMs|tfwn7#$0~WZ^嘇9A\CNu|;~ꂎ >&L㚺v' f+䶣XC«Uxڃj!}z L-(sM<cMVJN>۔DZ#]?}- JI ^k`حTX@ ZBS`f$4!P,^S'C)а~* pJ=UeC{c)EDQ||")*1uM4x.z UTM o{#G"X2VBB*АT'F!v{RDQȤ&7Nz (GP򔣤#T|,3J]d6 !,M)LW<&2S9CV.|&,H*{/'Ji2󂴴&j3,'5s鄧8iNZ6F,)ZC%^Jr`$9x2D OX 6Qz㡥D':΋b;=ҊtI-єnԣDBz:|sGFNjLH,`@`ۊ׭-iQGcT&7npl~ ! 1el0OʙP@íPjcC=6e̅;!8ZQP6hXцA)S'_"r}䊺~ր̈V[-7t%oagڹvL X䞑Y?ڧC5nM()W=Lԡ/7jMرklfA)䳍qD|V`е(wE,= yɄ΋GM| c#Gb HOzD~5@\\ 2J.ˈk9` .*'eC/ȣgJ/VaG9/z6h: 3[Yzux~F7̅?|#~o5$`&/o{n5G? {D&3C`~ h4:̲,: !!!`&7r7AHg|yO`-Vr-GF .!n(g&&o1(3XAq{"$*G%#Ѓ,PbeEn/h[T4BQցMQ9aaVb%Ab%7]X`Kl(%oX%sp_ahvo`ԡsQG'}m2EkGŇg(xf/`ʁq,h.ZbPnHwea8w^q`bvf%$[RW}xWv׈j8o^cTUwuxv^}5qzXuB,+Y"߈$HhcdAt)hi+Hxh8y‚hh%6'TN?ń}@Y),YrHiIFo6)j&Zs($EIbUPELy&c}Ԕ-7~7v9%V @KْZDs5Hx>N5ى((eCtY8UKy`@C4'xPʢIn ;(4gsyt(yT({rI{'כUؓ=%zppLÕ Iɛ&Hǝv߹ٝ9晞蹞W9Ed(nVL&vp֟.t'I: z jʜ!5l)L)sٰvٞ ?$ &ڡ'D0YTע y|i5lWH hKq.D zCj+C#fcd֋%G Yh! i#izJh5evГ9@ /J+ c\b pv שW۽k[9ԫ;؛zKs˼;*Lsf w+(˻ 934jx ꃺbt{k 1k% /<[:G[leښvE3-çh:<>K+=6<#3RE\3z$ T,gW.KG;ZM̞y@,|쫱&tēg<':V dvsu\8LCVh|n<7?Ɩ l,l˫/& Ʃ0N,vl!exjL*[ήƒܽL=UTs[Q }#3ezX@ѹ:@-w}_3mcG`I9ʱΈdљr)ܙ"հ&Ky .02=ɦ cUB~{\Jg-em4=շx\(^#}<֊doQ&mo}qmRmvm-> Ԃӭ }5Gҿ3y&f=(l`Md LlF]ژ֦ -E ڗԲ ͻ|ҷr٬eX֒84 ;ߍ @wO]{;0Mq[SLz==anݍ]ȜaD5N;]ߘ&ՅO&bc,1|`+:MW〩۽R&*զ'ʨ.)RzSW9:^K)ml鬢NNKM՛H ;IjФ;?Y]߈^Ic9en}XכN)>xըV~߇aV ɟPOۨt2t ? |~k+~0j#Y޿M2_.4𐍭g!J8,PDz//;@~Yy1O4Y.O}HF Z Q Z|7 SnQ:mW_n]ǡJj57goJ_6O *qOt\qaZЎ鴊Ӛ_m 2U:֍HyS%4E7xִb:.OdN//M씿bd_Ǖf3M L~8 <N@ZY^[u0G4O u_8[e_P0&tVVUFSWZ$gtZҵu4=w/7'A(BBFGEG?JLMNOPѴQSͲKUVդXYZ[\֢W`a`` Lfghekllmporsrtwvyty{}}5 X@ DتB{F7"12^dcǃ }CRIu(A*`q.eƜ%̚gpٙSfO[Q<&Uu7|OЪ–5Vj 5jٲdђvZn•ۣmJt⭪X=;8_C'&1RďCDFKe0g6Yga.7A-h"G^uꎭI˖Jms}n G[q\]~wKI݂uc Qwŏ'_yg{ϧt}p@ 4@TpAtA#pB +B 3pC;C;acl2-sources/graphics/computing-machine.gif0000664002132200015000000000777411605147053020472 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH>Ǣrl:Шt $9VvzpJ5o|NxT{NqeY|yChIo|}GWpjklqEp!Ǜʰ˨ ul ]z:o*̃p᱆#Jqb3j,qCqm*&Sߏ~&߀灀&h X r`6'Vx߅CXB! k %ވz02c-!5>sㆱ}=HsGt4ׁK"yIORSJ%ER[ZgqYbQiSfގ.ƒg} `S4J蒈&f5M*Vzs9zi&$nZ9, 锟"`ubM4riRh:)kn mi-'PiŜ[hɦSf9WI6pflLnbtˋ k. ѷjp_aqg <1/.K砫r1+7#4m4 m3C̯%Sp2i)-c SW ub45#ssm76-4Ќ-mvNo45n{ײHZ=3,|v'h^Wwb'*<wkwQnzH9Nz߫_pRio)*Tk<^&˛oC.diG~> ~n1E<3OrҔX8V!KN0dC투T++%9sBr(`Lia% 2MK21ɢb:󘊨Nidɸi v`q*i&,0 ~)Iy"6@.aL yt-mױDB EFilȾW&GMː !*jjM9>u`Mk4QEÜP0Y*BtvR%鿪N[8Z%W.Р3)Q:ϋ 'cJsKP' %߹5(MT=Qչ XMa.vuZ"U5W;~meUTyAmi:;8mۅꂶQn?=2o dsD0ҝrO.No3]:(/tbۭڵzվ|+rGiGKO3x0^3{UxJbi Ax>EsFB!$͌XAذENL U)q$#T̘?*nE <:é$[G!zS j;^[rw,Ѱb(1ʳ^97dJQ,O6loe5M!"|2{"ty7U031s%xI5Od3P|Z tP#M@ʖ+[rsVuL}W_ZyA}J:Θc=7PvY4+ם3݌jh:hKjlNzNgnv4dU/y^-Yp?D^~FrAnl!/2Ƕ`u@p

    ̇އ8MOo^~]Z%ZܯP򴯁~0eǞ0ǿ$#>8h}5\"qAWR c0b|AkjG'u|NGck+'WVHF'QnGwwaDF"wQ?i\nyCjhywt&h0(w$p3158'h,:&%hsessGE:Ճs0fkdhVV9468N;x-RX?xj6jCxc2I0K(Fȅ3c+Xmmnxd'"X}B_XVelHX5zH,ĄO hFO>o}v7iH~XeF| r[Ѩp# XH8(~ H@1gbx'CgxyDxV֏bc׏PFj2ovԐ()Ċ19$edJtE/iCY/U.NHLc@>uG{Jy~הNvLNIW ӧA 9 hKS6Eø$͸+E~1ӋAѕɀB+X)\ƌy\mgb[dd(o3d 8maYlBjtjJÜZ6!jٞ5qn975řf&)edɤwoG eşh xY#ae|@ p` ЦbyqOU j: n o:!b*@(+*کZ k:Ur/GJ:߈]X :ʦZ'׫)ūMŃǚP@p|ʒڭ1*ڪ ړ\ `JﺧPI0ꚰp*\pj}N9mKѥ; K|:1 wE+XV g";X {𪲊"2wRP):K"-K|6  3)H^J=kI CkGkBP^iZXKeu|Ui۵kkwt/ pǪu?]W{[}Ko~Z[e˸wt{cZ sչi[{۹ۺ;[{ۻ›;acl2-sources/graphics/concrete-proof.gif0000664002132200015000000001234011605147052017770 0ustar kaufmannacl2GIF87a,0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~VeyQ`tF]n=[pj8a[!Vնu[pM_b{PC,c*?Z{Ef[NmTCwSMjWk lM-X䐄lqKVz%n_0vG&5y/a9!Uc_*ɱ>=:{[ |e^ 4DELHt'H)*#_Zrdu0 Jx80˞,iYBsVi/D1jq cҥqNMLU~4r$J$Qq;U|ɰsw_vrPcFdzMBmedٸswe63sfnS%:M GNl6U͞ ^|eP:\Kw*~ )%4]{mn(Vµ!m߄ۼ87=r:f5+yo6loϮ|u\^ 3צ@se"~ HM™=q"W<7>y.*w{Oⷆm b޴B{OLz20 >CC~J}}!f? Ҩ}_:b7;7i Dw5^AZP#@cI=I8'?[t0J'G<(9D:@wg/QRK~ c bC0,^T.~:XUJNV3X}d'vXb|hM$}Xo\5+nq8xYx؋kQɘyMb!&͈t0Xx\ڸy AVD A(nU5JHG !h~Ht}^&sȎ}%q6uJ@ӄ`wԆՄ`&zHU~C-4١V)aWyCrS`jܣx ,0"6tSUjRbg_=PBALv"Ef4b1zQ$W>`mg8RTygs8e("zn`L+S.w_L;>g9Nhrsh)4ݵ ŗFtZ)>3袖/M^im;kU$KgXٖ%xY`հ\x^i7ٔ 63pr?>T8Rז*qZKeęozPm*uc~O$aPgSi$RxHUHڨfdyfFuN9gTjw*Ƨ,Z1pCcRShihd9xʚ٪&Rd62C9C5sʜXr0)A8Hx>/DjĬJG>o8 ne@陝0nŮ:5CW导(gEhů3 c%\V?]&[?;sH3){Q{@')z-&뮴کGI7˦_jj8RBڒIipGR; ũiEBT"_;[Yws`XZux9 ~Zx_UYnG {7B Je+ s*$do8Xnjud̲yu:kݔQ[uD綕EEi `q%dr zPhjKڶZYpK5v=c@IT6s@h!W6׊9ˤ9s^~:˿G6Xߊ[<f,*Z*LPxs>lڳkEiсmzn揾VnnN$.fQ.駎ꁱ갾.>^~麾N>n^Ğ>KW^^~>^~?_ ;acl2-sources/graphics/doc03.gif0000664002132200015000000000202511605147053015753 0ustar kaufmannacl2GIF89a" U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!," HC #6GbE2w`u̸Ŏ&At8RbJ/6#˜'![,⛍;<3Ö+Kk^ }$NDu3k۲};/yyƘʝ.]z@;acl2-sources/graphics/docbag2.gif0000664002132200015000000000201111605147052016336 0ustar kaufmannacl2GIF89a Oe}66i66TTiTTiiȬiiMMiMMM6M6iM6M6MTMTiMTMTMMiMMMMiMMMMiMȬMMMiMMMMiMM66i66TTiTTiiiȬi欀ii66i66TTiTTiiii欱ii66i66TTiTT׀׀i׀׀פפiפפiȬiii66i66TTiTTiiiȬii=ElM@@ffMMffMMff83H6ZHlRs橀׬&&&666EEETTTiiifii٠!, H *\߿!ǰbwE'IE:ć -S^d%M WzLh3LJgD!;8j2eRDHTbǨ72%ɕȬ25د1Ŏq,ҡYՎ-uJS|e)| pbث);/aiK9u"B?( #*OgEP+?dWƍ7nO'e7K.XpUnܸqƍ w o| "†n'EL'-(6@Eh|+yx2 rB{bE"Gd7 +2W+a҄P VXpP+8+ bU4PEUt?3PEU4PEU4>5PE3PEU4PE46PE U4PEUYDTҋ/;acl2-sources/graphics/file03.gif0000664002132200015000000000201311605147052016121 0ustar kaufmannacl2GIF89aOe}_?????????????_?__?_?_?_?_?_???_????????_????ߟ????_????߿????_????????_????????__?_?_?_?_?_?_?_______________?________?______ߟ__?______߿__?________?________??_???????________?_?_ߟ?_߿?_?_??_???????________?_?_ߟ?_߿?ߟ_ߟߟߟߟߟߟ?_??_???????________?_?_ߟ?_߿?߿_߿߿߿߿߿٠!,@*<ÇJTa N( 7俈#F r_Ƌ) 2HIOO6mL(`ϗH]ڱӧ"ѥKODJuIiK] t,ɡ^T5#Cq;>vjǓ'rpgRq[SRh12.(`R3 fp/[ngҠ>:93Ձg=zAkD\p7Ǭ2 ;acl2-sources/graphics/file04.gif0000664002132200015000000000207711605147052016134 0ustar kaufmannacl2GIF89a Oe}_?????????????_?__?_?_?_?_?_???_????????_????ߟ????_????߿????_????????_????????__?_?_?_?_?_?_?_______________?________?______ߟ__?______߿__?________?________??_???????________?_?_ߟ?_߿?_?_??_???????________?_?_ߟ?_߿?ߟ_ߟߟߟߟߟߟ?_??_???????________?_?_ߟ?_߿?߿_߿߿߿߿߿٠!, p*ЯJDΉ/8" #rLƇ!n,cFb\ @͛h6*F|he qE dәa'UϙŠuгcӢ|jc>=K@x@_||wK8pdžK_sJVL1~MɄ+,[ r~|b~Nmົiܗ#q}rvU:ON;oH|1;uËO;o=zC$O~};acl2-sources/graphics/flying.gif0000664002132200015000000000071111605147053016333 0ustar kaufmannacl2GIF87a'(0`ٳUeee3f@f@3f@f@f̀3ff̀̀f̿⋙==,'(pH,Hbbl:hB(Zԫ֚zi[  f\|ގ p{rO{xJ}MoHYw{CC|{R|d[We~jl^NXBKşbSϕnџiX٬~BО0sS#F"@!fܘ`@?II$S2<٩ ;acl2-sources/graphics/ftp2.gif0000664002132200015000000000212711605147052015720 0ustar kaufmannacl2GIF89a-,Oe}``@3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f333ff3fffff3f3f̙3f (((555CCCPPP]]]kkkxxx??6Uv@"4@{$@5b>({$٠!,-,H*\ȰÇ#JHŋ3"ǑF q?Q,II-W v`?Gvgb-z?4>81nMX`ָ_+maߛWVݹAƄ8s ;봭o4}lDs :|{G7^++3BVH`]& ;acl2-sources/graphics/gift.gif0000664002132200015000000000242011605147052015772 0ustar kaufmannacl2GIF87a           !%& *$*$+ ,'.'//$0" 0(1)1*52 5Lc7/#90$:,:";)&<$=$@ A7)H IJ2>NDFOSGDT(@UzV2V:9VI9WGMY)D\]O;__,I_d_``P:`SAb.KbS>cT?hmijYCkU^kWSkXDkb{lJJl[Dndntp_Gxy|hN}~YSZT\d@+^]"Wt\]Ba`i DBK- 1b1EW-=qxɜO"bBL%+D܇T\ry}@N" 0G_xMV(+rx.`2 @x}xr7I[hEr b˓\J\$B`E0 ( 'gad-,21rGh^زR[[` 6]tGh<`K1a)@`gDҙH;acl2-sources/graphics/green-line.gif0000664002132200015000000000016211605147053017070 0ustar kaufmannacl2GIF87a0,'I8ͻ`(dihlp,tmx|;acl2-sources/graphics/index.gif0000664002132200015000000000057211605147053016157 0ustar kaufmannacl2GIF87a#'0`ٳUf3f@f@3f@f@f̀3ff̀̀f̿⋙==,#'pH,Hrl:M N4rK+ի[msp;Nݪ{seqqwuSxinlxl`QpdY~[gzkULvygŜy´zV͌ԀHHA;acl2-sources/graphics/info04.gif0000664002132200015000000000177511605147053016155 0ustar kaufmannacl2GIF89a Oe}66i66TTiTTiiȬiiMMiMMM6M6iM6M6MTMTiMTMTMMiMMMMiMMMMiMȬMMMiMMMMiMM66i66TTiTTiiiȬi欀ii66i66TTiTTiiii欱ii66i66TTiTT׀׀i׀׀פפiפפiȬiii66i66TTiTTiiiȬii=ElM@@ffMMffMMff83H6ZHlRs橀׬&&&666EEETTTiiifii٠!, H\p "J4!C3jh >Cf4ѠRE|"TB1gϦC!QѧIHCH2ӉK/iTHuZET+ҥ6W`?XSi}٠ibHUnˇ< 8` |Ҧ>ˡW-K ޛ??=}×IgT~߁E`?WY!(a .28SN!Zx](n1a_흘hM86:XҐ>d!%)exL$KPv.Y>eIdlٓYf|g)Ey֛xi'gn9ܞ|柋*(hJ袏5,F !yajAjꩨꪬ"+~ܡ)a;*k4iޡ맼jR"[ǰk,T|BbAֆўǖݹ.,z @op `1 0ٰ[=|:9v6G^Gg~o g қo{ɃH؟[ӝ?'?DЯe#W}X@w Z`YǺL'UV<YBn}k`>} l6ao(ӡ Z?{A & Qh5DM D@ȝ&:1<^d.F'^Tbcؕ%#G;qDD(6r@"Iы±#(I it!PE e#EX&#ˣI>1A$S ɎO`riS^ De/}qA*s%(שJc]SX!#,~-X4\_C89NMgwK e6;FW˅[+^r/ܷ-vnv?7/p [: fhCM`5uDc8IEL4Y+9ԛƈG际vfe;k)ViZ_Q!bpANz/meTŨǖ^SmN!KĦ]vصA7rl:e_!zٹvF]#owcJODlGѶ照F%Gю-Q1^gjОm=/l2=/5 NsX v>r5R7_wg}o֧^UqVaoh}0c7 1=nzyaoVWx|' Ȁ]Q7xe{3v?@FXz|wQ,vy;\(}F1Χd@13dkD&TiƃqndW\hHk„V3FFi'iw`\T1`#\jUZBbh f-6idq! X 8^=B8&B6bd/HFh%B^Q"^TbI Z]R{x{x?feCN\F9MʼnQc6k_&JnR_U"ShQ\+vduR6_荀T 7_蘎.b:Em#0eh  ِqe֍_=VQX.^XՑӥmQ"" ِ*O0A8%[c&"hV L@ %%z/JɒzNɒ~j$ae#DX[Y=PqNIhQg¡Z[TUD !GdiI JiMn05ֽ^.".WܾWALLۜ[GC DH'5\*dǗ(>pfC}2m,ɳK")Y Q9@ƔWӥEUI5[TsCjz놤P*}:Ԫh}: -8^:{/4TI$_o>迵ѪTN3>D4l|瘟h8 펁,KVDHa?lPzW:e5 `J( ȡ_XJD|UH_(΅aDp- DLqmh(Wz>xcTP֤11 PꜫT KW`صoGW^Td sI0g(b0}d:[CVX-Szv7e+37ej\Q$mm*Ƶ5'Y7q`܇4eM-sw=RɶrA6Y"2,Os١׼iѹ׽Mlc10_ ټ_nN~ 8~1kk8.kM֯=˱#8Xq ,c;P շ@}e=oDV$\[ H9"1f.b 38SFJ/+n Yf#翆9YD1$.^9!%yaMZlb-*] h)hE±]!m87N]t<\W[nj{tFAzrӜhE/Zq_T.hMoOu[ *xWTYQiul|uO9qn{Xz?z(B=5/kocUlqJ3s\mDC"QuUZ{8&JozO72lm\'w!썜T"T7$~w!Nc#Sh"w_\)yׇu$TAT~E'H'9#u/t7Bgac{?B7tw}F|Ig#6sf/n|6$8TP!h@~nr I,t%/GcV<"L0B>*l( (Gr%kgsס?(B ȣ~vHXr'zwpzJQ 6|q^mhz08 BEIAnVfm&6mRGaXz-|Ї$8dHn^7D`l0el/`nmxs>+" 11ԇ9)Wَ/cDQ\’GyUUq`Qz Pi#r9tvT7  !\kCd4Ci)\V_jIwOcu9iA; A]i3A9 sb LXkĘ#yP9 ؙ)!S@!bR D2]JŖ0ytEu3Hc2ŹAɚva xH sGܙpI/0q0 ђlWNjnJX^HFZ1./J)iE%iR)L=0(2ڡ9<ʟ80.':IɠvÏhh3J] 7;:Ndɣ#~bLʙpu(O3T4@YtjOMJa*'zAXʙ:Om*~49y*jxJЇЙ;:7Y)gDzY8xب(lryNJ5C*XfĦ9ɪ2)stŅdږ$_9ۙ::I Z9}*1׊}I&)׭FZP9ihY{ Z( ( )XdӬw P#9d_:Vٌ)ۛhg)ym'%v'{J kVI:_㰱5J9AUb;& 6{EJTgo@ŽN+zw~zSio 2a,KZZS[`^j1%3·}{4YbTzJ \jo<J0ۛ4ʧױQ KKI˵Ļ䠷C h@i1af "Ҹ:FaS\˝ l3-W0 k k{*=D7|i_; s{BAl[ ۛd0˾x6L~7Y yn*˰ڿšf- ?,{- MC+b'À6A,]Q\[EU9('B6ݖV)Lb,!x3nl aL/OkwMǷǴ7RqBݵIT]V8"PmR]5q]A ֎9 G L9D,]tjw]pm|{)ׂ ]؀}a ,ؓ+]jYنؠ=o-ڤ}{ب`j׬ڟհ=6ٴ}ۜϛ۵c۞m X}c* ܭܥܜp;W} wMm-==]\}mO} ;l܍}P]E{@,^~ >^~6RQ;{ >7$~(../ 4n5~㋋:<>@B>D^F~HJL ;acl2-sources/graphics/landing.gif0000664002132200015000000000145211605147052016461 0ustar kaufmannacl2GIF87aZ[,Z[0I8`(dihzJj뾰tmp@/H1P2J-MX5r/4,f3^l;ӕ{ԮfiloI|)+xA?/x(1"6cƿVgZ+ɼ!Ϳئސ#8!b `@UBp6Ň/jܘqLj?rIt%OUa%o// 8MXr00ϔ(5) *]gˋHJ5AND`g`i݄hZVm%4O@ P˗j[o'\X%U7`t5xR#.Yu6-+/ΐ=KT4ؔR@ԂX WfڵV};7뫥O p\̓cZqխz; U1Ӯ=6< {=t6b!ӝ\vah&]Rz_ zdiيw,61JFߍF癉<޸#z^آsXdJdE'(eC^ieD6d_dzy˃YreX馚U3sYeӖm||ggx҉蝟ɂ曀*)*i&ʩ@jNJY,꫰*k;acl2-sources/graphics/large-flying.gif0000664002132200015000000000226711605147052017432 0ustar kaufmannacl2GIF87a0,0I8ͻ"dihl뾦tm߯|pHTl:ģtzRS+vrC/x53zR@l@{%Nbuyx|9Us@1XZ,e1PM^# ļ)7Ҵr˻0z٪9ބ+fӫaJU HP@Եċ%>Ə #RCȓ(p/˗!%͛Ćg 1'h 6ڋ)FO"=5#7t~Z6Wsi2$6^Uj JOeS3f#\♏|@nIp{T qsc kjիY ۚl;L_~t0l - q~ZzI{noɻJ]楔OOd={![o(>Ϗz`1E&`PxɄ^!nؔabg#:!'z+&"+Rp-̨7a;cH(4W HI$|=Q^WuSf$B LfInLR\)fɉ@C栦l&si"m H=Bu9 jġ&2ј+AifLZhniOjt饩]d@''Z&K`l(Ŧ-n-~kK:irikJn>S/Lv[#RCjކJq^\ą1 Y#s[a h:[4޳²ް̪|ͷN̺{!.7tηDϻ{ }׎^׏;acl2-sources/graphics/large-walking.gif0000664002132200015000000000244411605147052017573 0ustar kaufmannacl2GIF87a,0I8ͻY#dihltm߬Hx@Q`AȤiH'tc66M掓4.QKV{׷6Kuho|}xuARzrtvSc@T;gGw7]ƧȘ̼ΫЗ=Ӈ~PݩH>e~b0Vsз/\pRҖ (h(!:d K#[9;&ChbGSR˗5nY΀0c#OѴ8 -R4ҥ7tSQRj*HZ5hXc:Dt+ZFj- lVd\{$ n;|t3 T5<޾< đG:rGkKPYI#S1ZM"p+<m̸O{ 1⫘O/ۜs2I^um֩w\wŗU{рU wJrU_cÕ>9[W }%VFbTr4!~g \G9 *[ ] x߈~bX^SƜCZ 7@Eg8&K M (/IedYn&ٖ|\6 kM9p@YbJF=z k gxX lf'U{yHIi^fyɦj誰j$JOVi ')ll>l6Kz-ݪl+n˪-֊Yn䮻肫Ko;ʼio뫼쯷3KpIj /3<gLqc"ۆ,몼$\:353 ˼2hj:LFي4C74#>rKl9C-^Nʨ '_Kc_2gͳZMw]}/%S ]bsJvُl7U#8A3uc};nKN0F>ꃃwKx$7ڱnj8>z/ץ~sWnѝ+-=cm^UGC_{ovʼ>4k/G/~@&Ё @FP`-xA5pf[&:j$3APT/|` g<8̡w;acl2-sources/graphics/llogo.gif0000775002132200015000000000110111605147052016153 0ustar kaufmannacl2GIF87a>(BBBƵZRZޜέJRsZ9J!B,>(I8ͻ`(SchljN,tm߸ 7E` 0 HAl8v̨@X,a. ҞZZ!F(p"<8x|[qq tOv W \pATTu F~q`=HW]ij   ^jj¨~Z oH ͥʷf_ JU_{[ [^|b Qf1( ) P@+zԔfn*a|t*'Q,Ѝǖp dF Z ($$Gn YO+Em2B`ր`ٳhӪ]˶08@qj .zn|q^EEb._ù˴6rSx>5Yࢽ*HȟOD;acl2-sources/graphics/logo.gif0000775002132200015000000000756611605147052016024 0ustar kaufmannacl2GIF87aBBBƵZRZޜέJRsZ9J!B,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N/ !<|H~ҹǪΩEҶ ڶԱA^y>N/R2Tki ݯ>"]C#yГ"ЀaG/~ԺM>cboxC]*j? Q$̯~' @ T?P (؃rI(D=诵,اPbbv$́ 1x h:~E+ $&Q\FR@=΀d+P"şQ(#dE~bj\a]Xj 2tkK#2ڶ[!`RТ"G86\! 3".VPYM.(mx~Uw%*{Ar9YɊSe9] bBw$Ty}5o0)#9YgyTW5]vLZ)y>@$deu?ȓ<*QvU,h7:h0W`$btg&]WI g(%zSH4z\ ғԧ(.(mOi!IV+B\ Ң.qT[ARUG@=jX>:V"lݢ[GPhr6k;8 d#8X cmfit͞E+iw/v唰mm}ܵslEv6Oau-mwnn5)F-mX>8s ^7G"r]#j^׽*v;漫`QqTx!VPJpg @ ezA ?q&Aԁ1u"qJAp*q!`@APz> w1]fA3maq2Ȑ*) z w@ kuAˠ  6jYzPAjc:YJT0 F VJq$a*Ɗ J,xj:A_ ?` #+z}@Kkұzњ j*0JL۴N+PR;T[V{XZ\۵^`b;d[f{h);acl2-sources/graphics/mailbox1.gif0000664002132200015000000000227211605147052016562 0ustar kaufmannacl2GIF89a"U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!,"H(TaA0!%R8Ə5((P$G?]"߿[9P+?$߿2 +?w%߿;Χ@ %Vo Hֈ@ɒ&/'#|"w\ [ w\o|>^8W|ؠuw|vh|s>`s> 0us>sWNH$СB0>&t">$q(BX/5w"=@>FVt#>;acl2-sources/graphics/new04.gif0000664002132200015000000000215611605147052016004 0ustar kaufmannacl2GIF89a&'ȬB/~_g7<3ff3fffff3f3f̙3f !!!"""###$$$%%%3f3333f333ff3fffff3f3f̙3f3333f3333333333f3333333f3f33ff3f3f3f3333f3333333f3̙333333f333ff3ffffff3f33f3ff3f3f3ffff3fffffffffff3fffffff3fff̙ffff3fffff3f̙3333f33̙3ff3ffff̙f3f̙3f̙̙3f̙3f3333f333ff3fffff̙̙3̙f̙̙̙3f̙3f3f3333f33𠠤!,&'H \XpBHdHqĆ%*ТA,@&X$%CQܧqcKa改H.;D "eڬCK,񊥹rϣ_ꔻlᚊ5ƽJ0ĎN>8jV {=rhRkݙ(N*,Z\λQly d7_F2⿭C!ןSKrp^7x.ݸ.=jo~[t{矁BrBPVaA;acl2-sources/graphics/note02.gif0000664002132200015000000000206511605147052016155 0ustar kaufmannacl2GIF89a""U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!,""H*<aÅ 0@"'VxB6Z8!G#Ibp}:%FANҗIвFEjC*@ӊ-3џ?.v߿/)q_=@k};xᒉи0ByoB2 }%-mI-l!TDt斈ooߦ=2{;>D %}1O:ɞuY' ;acl2-sources/graphics/open-book.gif0000664002132200015000000000503611605147053016741 0ustar kaufmannacl2GIF87a^,^0I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~ƴ0M/D+=,8*1PGpF 4oaJp|388_E>j cB a$2I(=X[ٲ/0s͟pT'p!MH#DäP-,e Q^:-E>wZ.uVƂ,ӳl* ӧܻsz[X#aˆ+^̸ǐ#KL˘3k̹ϠCMӨS^ͺ븆C XوwI䋛ێpw) '>8Uh,gHsS?d +oOԽw ŏ^ֶp*|ow^ xH4 eQx>G/VQ>p``8 D"$&TJ#`T*2RI6JbK>J#|v>fw$~ԦF%Xy\ ,`)dihlp)tixy6#4|Iu=)h#;uh'h3~h֣ (iUx//z)B]L~D.ZE`*pȪQEDP*"*]A,=K**NmTNkKrD]m+.U;n {&ѨG:[/ -*{zϖg 7G,WLXp3rqQ &C8r2(|2st܌: G>AsoMFFJ+D͉o~y z?`N΃Ǔz9{ nH; ^TWN ?Z=χ]{G?p1Y}X}Ԍ`zGG,wOxMK~ tЉ[- ʟ}p>-6\{ruKYScr }Ѥ>Ig@PdBFYؿHE*HUY$ei8/3э D8qlo٨Gdt+_!! _^6D.Dd$EAZ!#)I/(}R XQCt'Ļϔr8v0)eRD]U27"-ªY]2)boR*˖-ѬZyĵ)茧:m+=)r)hɚAe)qBr@yE1mwg:YJ}e IM:It?a6i(PNc>zd2Ԋ擎!}zC05(BsjR^T68/gStR%jOJ !aiBPn "|kToU^uNf=Z# ;:8A[֔թKw8K'#+fJSu^ο$kzⵑ8Qtf7?8}SuW:7i,RƳ]FE _hV2BB&t,m&>nۮa{SВeEwׄ,ﱯZo ^Ya}u VK-or!꺼Z t5Wo<\ W8pmد&1C,LfX hnb131 dxL5vLbұJ񏹹-%|UetҼgRɔq`eܖ&kN&hr18I.8!Z7Q3fkă=xaj32K[AWI44] g'vzuiI_)*H8ȳ4Tecԩ@#tl?SSCdy.HƵ;)IUb>%)phlz̾ t3f7mOs^unw鼩T-#{m7{xl"?9yA򖻼fkAU<*Ϲ^%om4ǚ͉iK\|5 ء;Ul2TD 7I+ss3w׿R=/fzLˌvtڙv{:_Wc^}ȉytʽmzū:㎧<,="R0J1yD%$Mo$߈g\/M?5_86/_+=Hvw.}{ž{OOO;acl2-sources/graphics/pisa.gif0000664002132200015000000000336611605147052016007 0ustar kaufmannacl2GIF87a,0Iͻ`(dihlp,lxF aK:tJewzwLV{sY{'V-$h r]|xt* 7MAH9B ҁM;OݓR?Wϟ7gp# ,[C|*ʻXJ9ىh'#8v4y%GjP#wnҌ'\??љ:U/Ӡ LFu*NW.uժѾK0I/_:mR8ʤAmہ[ƲQ!CE)G|"9Fh/8:\ɽt;?W8wE1ǎa)Oan^t{JꡗxfxC]vҐONHqt!TdȂb~)"0֘anTa"M= ZV⌡iu]DR[5hKi%}a1)֑T*)cnY_9T evex `w'jyjl> gI_i٨q(F^tBBx]Rf:eSzf!P'`:ֵץ w:(םyĒjVPੳe 7EYWԶ+G>݈ZaZ nIZJ.LiboD "c+p薕/ x \|[* %yf"Ki,{r)wg~W:l*T͎}F+ elR[LuQuoݩ+~a* Ǣo`oZ$9,2p$Cq4^R7 }O0>n3k<ulzxW>Snt R^#Lgho`Qw5#6(G*a?,FS }Vf>wou;5{:anvUb"5HC_S *j 2X8Jb uC I GvH kP{[x*qH .,D~ñD$ \b" =pE 6AmA`1 N|9Xq[\x 0%2vQ]ST+a8*w:,]!G2H(d} "E86!ӻm%{`d5MR I[ZAF [yu *֜]"937 J0Lb;.W<Ta jCn&|lu/m(5(27P S;;!#=%!QMFE55NeG1( rfjSe:Snl8NN)YcԵ}wժZ!2Gl+ bc{AZͬf7zH;acl2-sources/graphics/proof.gif0000664002132200015000000000251611605147052016174 0ustar kaufmannacl2GIF87a,0I8ͻ'dihjjp,Glx/',8"rlΨTVIЩ6cO@&{==+u87zv'rmHyzqOf%|5` {X]^ /z4" !l,Wj&hįxWjMԨ^wXKXX:=Qd&$\4ؽH\ڬŵZ@cϤ\0CPL ϮΔ?#^ic}d!(gN>A&5'ޗ};u祈+_|yiZͣ7-dnߞ!CS?>ɟOB:> M4NE }Úv$`ǐBrQt]Ȝ{qěF~bnAfc:aw #7\FB"x6Ch@ȡy$~YWn,%"F)hn لj.1d~i\ƩB@aW"L%OzRӢ* FwFTj0iZ)ꚢF5x꫚zj rp[+UR\IڰpFZ VDkbeB2̵Bۭg-3|xW`Ζ+kn1)bM[k{-W~u֍{o_gaSϥ@ydiۖ,ʢ;+@oy̙҄}~O]Zm'|Q6X} w4`_7#[~7a qġfb^2b6#}uΨ_ ި^/Um QyAXأ!&dڑ]飒dc0J9`*$tQchؙ9h}nř{tfaf4ɜj"gqբbɥ',&\Y8 ؛aPY}zi=Z])*+~*,֚:j,> NlfF;acl2-sources/graphics/stack.gif0000664002132200015000000000352311605147053016154 0ustar kaufmannacl2GIF87a,I8y`(duhl뾬)4 x[kpH͊#r,'tJ픪Nv rޯctom.v:>{<|I~BB89C S½'ǼXĵó@vϳf[ѿܭ[ 4v2a!C" J Iĉ/ЈaIIC*2h˗/T1Z8qJFM` *UPj"dt`&J!:V`%8 4SD D.D pQV<5(mtj'J6a(`bu4d 8HZ~|D-TO{Op5vfc63$w$Yꪔ@Crri4I'N@\RɈuҎ2~.Ȼؾ>"QaBi Nf,Y&Ps?AM,thiTc;gG.P<FZ鸸g3|.IQCTx75bN SJd+(6:Oy@:ɢd{U_@Uwa5NݪO}eY7ִMu+Ě1vB[W5|^Ghsa=X."-**1eنtݬ'Y*hkPa+mH[R-dY 72 m[ܨ:Ws;acl2-sources/graphics/state-object.gif0000664002132200015000000001645711605147053017445 0ustar kaufmannacl2GIF87aЀ0, dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~,a/;-"#iP$=ўٷħĸ H*DrpÇq4HE/jܸ0# SlqN]Ȓ&9\ǓqK&̙.Yܩ&g'pSf2H͔Cmy+NV+2]mdnQv6hӪ]˶۴fmUa̫]DWۿo/J5<0#2/_'sf`KTmz3ePz5n`zwp۾T{}&i7Իr'ʃ4%?cThO-rz[f8TbJH2A[zGMb4܁R)KHԙZל;6(|TSK 2a7SQ3F(LjY6 *>5M6,4SYAh#RQԥtHd "_^ȸ#!G%>:5NOdDX}eHИVd|SZdƙ\$@rFAҟ*蠄i}mEgUVj饘f馜&(Bz1bBꫝ~:Pb&*ꪬ=Ɩs첕KVk.@{3vˬE>J ےK%ڑ̵U[/m7$;ipkϾ`G1(`gl1t wLc ^o .`2L4<#`$,Īhl4#ˀɛ<+lu5\0OO|=s'AO-1GJ7PK)U_ sրլSɈvcsUk ow^@v\s`sK>yчkL[C׏PWx.6赈N5# !3N8ԱWN{Z `F߁:l'rpvV|mJ}yj;3Af n\hVj!H ,F (a-_(e=ψ1L)dUA"esKJA;@@(81y9k"|L ǴE -Z!ĨFe_IT`gû=1ۚQż;=Na*'CB3tc<^c {HR#HFl2c׿.4Pa#/Qc[5]6[j|@ֻ̓"s`hBIknM}rւK疈fעXC}X{o/nM)O`V^G96+ =M&2RXP$lxP>N@< 1kן'}t]<@& |6 U*eSv3 qlW} 7y'_rG_޷k8 Hq hxg"}qxXs3S.x?` Gv%hr,8{#vp&H7 E92~moR Gh5r#HKA~TCP P17Fږm1R|mW d8fxq]LWH%iS|@*~DŽ.0 Wb'YlD=`PwwTitR"_~uLj Bl88<Jxa7RH((zЀ'I|bWfŒ"H`e8G KXpqoag$` W 5 0Xl Q8+ޤE\#-荞}e"6 %׏/Tt194q(wmx"ljkؐVV""vғ ّ+y#wXd$m~WJx18_])9BW|fI?)AY y$e6" K \YO%SQFV3/X|mxBka X`z'CEH$[ 3~b %yR)ٗ)pƗ%P] 7X薵4#s^h#f}5k)e(0tyŚ$pbIBy@ )a5/wiYI[ʙ0OQ)ҹբhX$YIaӦY IR.mIWxZ9x|aƞvY!z 7Xe߈c4Yw6u9B7v8v @TvYf,pW֢k2@)E8ʟY '@ʄBh]S9-`HqJ{LgȕP{ ǣV:6:iH0zȩY/чh~QUjWYO( *\/2cqQy񃟶 L*2& ;mzyN{ZYZb:nz6 XNءx0pRq yԪ+ږxZ7У sنz  tZIꚛnJ ^ Yvf$ M4а ]jQtʩY +A k֚&[FMԢ3zQ 5068 ;$;oYaIvjD3dNK:;h3ډ}ȨR٬F麟I`,kUz9nJ4ú{s5ٯ*=IW kr_8RkziT'ozk|{AFq KPVfi!R.o ,6yL}욫›Y e6?#`~ظ'E;۵b{T`YPx^@paA@[5)+x'æʿ~۽# ,墨r *HPH$sz:BXP9x ĖAˇXZV겛G+{v#YFq%ƴaıC~ۻ?L[m<'&`d$ Cg6ܚdL:9ǀȀ|̱k  l)4'2;z\ !V \qU{l09ǚ,Ȝ2 <{L ܡesr rK.hJ\KIɳ=|JurVO2{ ҅m52XB ȷ_Ki/Y̼#ؕ!aOG7G$G6͗rMgs f~ :1Wt&KFQY]ݒ& Հ>K]]wJ-n+]8 CX֙)(p]A:3qE Emh-k (Y]*~ L=]W]>:#FaQG=ز;{-+N*.K=5}$6`WޭvŒDX3j٘]׆H) Ns>&J\e>]`9>'Q-Ζ< ݧ~ñ|٘P!9#`d,cen9nn㟾4mbžk窮.|>Һ^¶NEPsV)n^.>Îh^Md촖)^3q-p{1 ~2QDiO}4a~qj 0 KN^mam !/ji@xmrD }&& :l0E1@ݜdP=D^8?eBV /핝SV[Rײr9_XXb/g?ʸU ۧL?`1X/j|o${ָ%=h_F(O׌f޳o_Jo!XԎ}-sMXʎhc2-:oJycL0~.-FܜP? g+a )pr #Y' /3 7~fГG@a[r8&4*)6rޯ/k^)ìNwp|/~ Z aN! bbc䔕de `M% h('Sj&*,#$R쭮 ٮoT/c e睝\]o-kpڳ!^vf78fU0RҢtj_g,m}u0?Mf ݞv6oBO[2zË:"(HV le^j%RhM "Ŝ$^cEl1a"uFO1)e(G?űm9Du!. G3 ҐQ#ozmq;S$&CH3dH)Uʒ dĠJV2r]򲗾%*wZS|$icf]`h:t%2`Ӛެ 5(M1KtY@m>K<'<NT'>93S?ȎuП}f8LebB aʖhҩN6䳣>GeQ4IKR.iԊ#])L!Q46Lқ9eiM{*TyԤA*RYTiI}-գBjSUrgX-JѮuMb+#:ֲUjEk޺ֶr+^Z׽0~*_+=,b2},d#+R,f3 ;acl2-sources/graphics/teacher1.gif0000664002132200015000000000232311605147053016540 0ustar kaufmannacl2GIF89a !U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUUUUUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU𠠤!, !H !Ç#Hx 3jԘb A I~$]6[^L9si2AC3&uyqF뷏*Ztz1@Rz -WPbkm@[o߾2cˋv{R~/ŷϪˋ{Jճˋoy5ˌ{RϥƷ˺4i:QTs~S^lyjY&5@l2"ǻ ‹9m(`Ղ6/2}cKSKR?OK??-=OK1OK ?ON}OK ??OK????t???BUBUVYUVYeQDE;acl2-sources/graphics/teacher2.gif0000664002132200015000000000201211605147053016534 0ustar kaufmannacl2GIF89a U+U+++++UUUUUUUUՀUժUUUUUUUUU+UU+U+U+U+U+UUUUUUUUR-UUUUUUUUUՀUUUUUUUժUUUUUUUUUUUUUUUUU+U+++++UUUUUUUUՀUժՀUՀՀՀՀՀUU+U+++++UUUUUUUUՀUժժUժժժժ޳UUՀժ+U+Հ+ժ+++UUUՀUժUUUUՀժՀUՀժժUՀժUՀժU+U+++++UUUUUUUUՀUժUU/OOkkk!,  "HpÇ#J|@3jhƏ9AQt(}b/ʙ0Y $ˌ)@rѳhHTrg(OCfN1sZD+[* UbϕxV⎍ ۷q-nJ+*LW &ׯ[2'B.pYϠ-aҖV}VvM[`O٫k=[k|.7⭉#;acl2-sources/graphics/time-out.gif0000664002132200015000000000204111605147052016603 0ustar kaufmannacl2GIF87a9,9I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.zn|N~K>BhķȻƽ7Դ˭٢d妡Уܡ9նꧭSM_{!Fyڻ`ykHnۮg o-73R.m%^D_,G#&qP9kZQ 4FsNftn5AZBMA^ף(_򩵥N[ʴXUҾn]R-͐.kK ,"Zw\seE,&pȄVEلZ7ƝX*[fqS{H,ӐQ=-jO Ίw*ڶ(^ŵX{M!yDC6[2YwH&߁5ȑ?$ՖQԁ CU;5q#Jn8iH _hLV:?וa˖z5&m4+,xAǤ +pwr)<޽= ;acl2-sources/graphics/twarning.gif0000664002132200015000000000010711605147052016672 0ustar kaufmannacl2GIF87a ,  =B%A/[\$;acl2-sources/graphics/uaa-rewrite.gif0000664002132200015000000000664011605147052017276 0ustar kaufmannacl2GIF87a,I8ͻ`(dihlp,tmx|pH,Ȥrl:ШtJZجvzxL.q`nש|Nzz%ymkp$k_k#ELj Ϙʾq٩s8l19"[6*ڨݾI#GȵI,iIC1 08EWl#S@^b?RmLu[C<<†pɍDkltAd#"1O<1.l p'͚tZ*?E qo hMYquu՗wT&]eH+,l2:뤹}ӆja7޵Η*U3gw5>.[c+K̳#6ۻ * W9Ma ĉd,){vܻlH,d觋n3-hݛ>B߁PY4~YjM?TJ`+U+ݷw3'F"wPY/'`z?߀82|F(܄9X!? h!G2`l!PJ`j!s"/c~-g׵XBu1^4zBHb69z =ϒJYnoUɧt^W &t4&iiflhIg3֙ND'| *(gn2+7Y$~>NQJZ v\z̗8j4*nB^*+P6ipA,>뒆N[eUf/Q`Si?1;7X*gIoXs\oV$vŘ"+e-M&5ꖄHL'_tH{2/9-˄>Tt^,C]^r3uj.ܒKTꎞ[9Up}.*=Xavn]/P|Y;6x)λB o6!/~o~IYq`@o kH U/  BH .# gz0 aF0L AB'p08f2: CP @ȋ"rH!HO8qP4B."2 `̀ 3ZH2xt-@G1 dX>L"@H 2\$'YFJ8$%7XQd'GIJ|(KVπZt,g O2.w)[ &|U $f&Ғd3I{g=O~{7{NP$ ꡇTh"Ru7֭S"sa.L.RkYH)*G0TijWH\*M)>]|P6Ӝ_T)QWZIJmUy` 66%e#sSiK:)I H.9k-rc4E]ר"aM>8,orM'i߀X7,ih6ژ_WᶝdTeSrK[mqZ/Dkzi*7R(1bXɒq*&%=SFZ6e\Dܩ5[­ɼ_%p0wY96q^|S0}[+TN~2|a&ɵu.x r]'K,>6굚 YjUqPE(oq{ݳɉ85\Y"WVvO _|J87G+7 Tӄ_-[ %]nF2;oa^rhܕquu "'RFv =ej\slKeyӫ N3 Q7CPZNTϤn+Pd惶 o6QemΨ{}Ecy42iP혐!Ne~ Y_:vq?RX|2e,mhtօKϭF4JǝwyԕKA^ /+8Zg~ǻ)P[8G(΁6;F_T@gP8P<$sΓ}B:%`s|P9Nt|=/1sI_{<Q=Quà[n[ bO{^_j;^W`{mv] z5`~ 7P;9[^t?d/yD>@;O|Kz[OZF!}w~_y"ȟ]>_~ CgIA_!w"_!?߾:!a_P`~wٷ7rp5(7R NduVCvW)Ԗj('N\u+`K*X %o4Ђ#rbV+x .XF0HNA(`88rp)%;1׃̅g5R13fdFcsq0vW7=oKX1c$xgƅRI&)C=SpUC,` )5%&_:ց7rjօTwe4|8ehS"K+ږnp-6iı330[73RYeYmV5U1YV_z6Rňֆ8V:'6ӌFgd /]R+u]Rj#1(*n*fB.gC2o;.&oF/3sI/60h$0;3Bgֈ!dhd(1fvqf6}v8=&R2hEf^<pwp h cU(bp#c*}629=؈ؐ\ߨpS> 3Ye٥89%V"hԦ.r #&iX hX/A… )ܨ0Yy(d7Zos֓mQ:ҁo6'mI撏t#idb+X4IH3Rs] jɀ4l`TScH:i[j6tWЀi7(_b#95X"=XH29+9l8oi94x(ob"2,Ȕ'Bɣ<qcc!<ɟ'G<wn]I >5":á&z(*,ڢ.0;acl2-sources/graphics/walking.gif0000664002132200015000000000045611605147053016505 0ustar kaufmannacl2GIF87a'(,'(I8LSiZhn;Ӝb;^WlE :"WӤytS!xnw/u)Yb,C`;34siAc*XnPwXvtf`nYj_Y{uq |~PPl& :w ĺ A ټؼA}p_*dBE ;acl2-sources/graphics/warning.gif0000664002132200015000000000032711605147052016512 0ustar kaufmannacl2GIF87a&&,&&`ZnX ^@ _Xw9,.\޽LakTj~Ԩc"@WׯRɘf"y[գh;_֗DwXfPhiw8ɈB2s Dӈfk[YKz<R;acl2-sources/installation/0002775002132200015000000000000012222334002015244 5ustar kaufmannacl2acl2-sources/installation/installation.html0000664002132200015000000001231612222120260020633 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide

    ACL2 Version 6.3 Installation Guide

    NOTE: From time to time we may provide so-called incremental releases of ACL2. Please follow the recent changes to this page link on the ACL2 home page for more information.


    Easy Install for Unix-like Systems

    Here, "Unix-like Systems" includes Unix, GNU-Linux, and MacOS X.

    NOTE: A quicker and even easier install may be possible, by instead obtaining a pre-built binary distribution if one is available for your platform. Otherwise:

    1. Fetch acl2.tar.gz into a new directory, say acl2/v6-3/.
    2. Execute the following to create directory acl2-sources/:
      tar xfz acl2.tar.gz
    3. We strongly suggest that you fetch ACL2 community books, for example as a tarball from the Google Code website, into your new acl2-sources/ directory; then execute the following in acl2-sources/ to create directory books/:
      tar xfz books-6.3.tar.gz
    4. Obtain a Common Lisp implementation if you don't already have one.
    5. Build by connecting to your acl2-sources/ directory and typing the following, which may take a few minutes to complete. Note: You will need Gnu make. We have seen problems with Version 3.81 of that utility, so if you encounter errors, please consider our instructions for downloading and installing GNU make version 3.80. (That said, in builds with Allegro Common Lisp we have seen problems certifying books with GNU make version 3.80 that seemed to be solved with version 3.81.)
      make LISP=<path_to_your_lisp_executable>
      
    6. OPTIONAL: Download gzipped tarfiles for workshops books and/or nonstd books from the Google Code website into the books/ directory (but first create books/ if you skipped that step). Further instructions are here.
    7. Certify books (but first create books/ if you skipped that step), for example as follows.
      make regression
      (time nice make -j 8 ACL2=/u/smith/bin/acl2 regression) >& make-regression.log
      
      The resulting log file, make-regression.log, should contain no occurrences of the string ``CERTIFICATION FAILED''; a normal exit (status 0) should guarantee this. If you want additional explanation and further options, see the documentation for BOOKS-CERTIFICATION.
    You now have an ACL2 executable called saved_acl2 (from step 5) and access to certified community books (from step 7). Enjoy!
    THE ABOVE INSTRUCTIONS MAY BE ALL THAT YOU NEED. Otherwise, read on....

    More Information

    acl2-sources/installation/installing-make.html0000664002132200015000000000334112222116140021211 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Installing Make

    Installing Make

    We have seen problems with Version 3.81 of GNU make on Linux (though not on a Mac). (And in builds with Allegro Common Lisp on 32-bit Linux we have seen problems with GNU make version 3.80 that seemed to be solved with version 3.81.)

    Here are instructions for installing Version 3.80 of GNU make on linux that worked for us. (Note: We have both 32-bit and 64-bit OS machines on your filesystem, and after we did the following on a 32-bit machine, we could use make 3.80 on either 32-bit or 64-bit machines.)

    1. Change to a directory under which you will put make 3.80:
      mkdir Make
      cd Make
      
    2. Download GNU make version 3.80 from:

      http://ftp.gnu.org/pub/gnu/make/make-3.80.tar.gz

    3. Extract/build/install:
      tar xfz make-3.80.tar.gz
      cd make-3.80
      ./configure
      make
      
    4. Arrange that the new make 3.80 is your default make. For example, if ~/bin at (or sufficiently near) the front of your PATH and DIR is the directory in which you downloaded make-3.80.tar.gz as above, you can do this:
      ln -s DIR/Make/make-3.80/make make
      rehash
      
    5. Test that you have installed make 3.80 as your default make. The following should print "GNU Make 3.80".
      make --version
      

    [Back to Installation Guide.]



















    acl2-sources/installation/misc.html0000664002132200015000000001526112222120266017075 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Miscellaneous Information

    Miscellaneous Information

    [Back to main page of Installation Guide.]

    Table of Contents


    Please let us know if there is other information that you would find of use in this guide.



    Problems

    If you are having problems using the `make' utility, be sure that you are using GNU make. We have seen problems with Version 3.81 of that utility, so if you encounter errors, please consider our instructions for downloading and installing GNU make version 3.80.

    Building ACL2 may fail with 32-bit SBCL because of insufficient heap memory. Harsh Raju Chamarthi points out that a fix is to run SBCL with an increased heap size limit, like this:

    make LISP="/sbcl --core /sbcl.core --dynamic-space-size 1024"
    
    He adds that this issue occurred while building ACL2 4.0 with SBCL v1.0.40 on linux, and the default heap size for SBCL on a standard 32-bit linux is 512MB, while for SBCL (64-bit executable) on a 64-bit linux, the default is usually 8GB.


    Reasoning about the Real Numbers

    ACL2 supports rational numbers but not real numbers. However, starting with Version 2.5, a variant of ACL2 called "ACL2(r)" supports the real numbers by way of non-standard analysis. ACL2(r) was conceived and first implemented by Ruben Gamboa in his Ph.D. dissertation work, supervised by Bob Boyer with active participation by Matt Kaufmann. See the documentation topic REAL for information about this extension and how to build it, and a warning about its experimental nature.

    If you care to use ACL2(r), we strongly suggest that you first download the non-standard analysis community books (gzipped tar file) from the Google Code acl2-books project website, and save to the books/ subdirectory of your copy of the ACL2 distribution, say, dir/acl2-sources/books/. Then extract to create subdirectory nonstd/:

    tar xvfz nonstd-6.3.tar.gz
    

    Next build an executable image and certify books. First, connect to your dir/acl2-sources/ directory and execute

    cd acl2-sources
    make large-acl2r LISP=xxx

    where xxx is the command to run your local Common Lisp.

    By default, if no LISP=xxx is specified, LISP=ccl is used. On our hosts, ccl is the name of Clozure Common Lisp (CCL), which can be obtained as explained in the Requirements document.

    This will create executable saved_acl2r in the dir/acl2-sources directory (notice the trailing r in the executable filename).

    Finally, to certify books under directory dir/acl2-sources/books/nonstd/ with ACL2(r), stand in the dir/acl2-sources/ directory and execute the following command.

    make regression-nonstd ACL2=dir/acl2-sources/saved_acl2r
    



    Links and Mailing Lists

    There are three mailing lists for ACL2 users. You can post messages to these lists only if you are a member. You may subscribe to or unsubscribe from these lists, or view their archives, at:

    Finally, please report bugs in ACL2 to Matt Kaufmann and J Strother Moore.



    Export/Re-Export Limitations

    ACL2 may be exported to any countries except those subject to embargoes under various laws administered by the Office of Foreign Assets Control ("OFAC") of the U. S. Department of the Treasury.


    License and Copyright

    ACL2 Version 6.3 -- A Computational Logic for Applicative Common Lisp
    Copyright (C) 2013, Regents of the University of Texas

    This version of ACL2 is a descendent of ACL2 Version 1.9, Copyright (C) 1997 Computational Logic, Inc.

    This program is free software; you can redistribute it and/or modify it under the terms of the LICENSE file distributed with ACL2.

    This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the LICENSE for more details.

    Matt Kaufmann (Kaufmann@cs.utexas.edu)
    J Strother Moore (Moore@cs.utexas.edu)

    Department of Computer Sciences
    University of Texas at Austin
    Austin, TX 78712-1188 U.S.A.

    [Back to Installation Guide.]



















    acl2-sources/installation/obtaining-and-installing.html0000664002132200015000000007016212222120313023010 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Obtaining and Installing ACL2

    Obtaining and Installing ACL2

    [Back to main page of Installation Guide.]

    Table of Contents


    ACL2 is more than just the executable image. You should typically obtain the distributed books and a local copy of the documentation. Start here and we will take you through the whole process of obtaining and installing ACL2.

    First, create a directory in which to store ACL2 Version 6.3. We will call this directory dir. For example, dir might be /home/jones/acl2/v6-3.

    NOTE: If you intend to obtain an incremental release (e.g. 2.9.4 as opposed to 2.9), please see the ACL2 News for instructions. Otherwise, continue reading here.

    A collection of books (ACL2 input files typically including definitions and theorems) has been developed over the years for use with ACL2, called the "community books". It is very useful to obtain these books, which are distributed from the Google Code acl2-books project website; directions are included below.



    Pre-built Binary Distributions

    Visit the "Recent changes to this page" link on the ACL2 home page to see if there are other shortcuts available.

    WARNING: Some of these packages might be for old version of ACL2. We recommend that you use the latest version of ACL2 (Version 6.3).

    • Linux/Mac/Windows Binaries in ACL2s

      The ACL2 Sedan (ACL2s) is an Eclipse-based IDE for ACL2 that is distributed with pre-certified books and pre-built binaries. If you use an alternative development environment (such as Emacs), you can still fetch a tarball for your x86-based Linux/Mac/Windows system that contains a pre-built binary of (pure) ACL2, using the following instructions based on information kindly provided by Harsh Raju Chamarthi. Just extract the appropriate tarball (using tar xfz on Linux or Mac and unzip on Windows) under a path with no spaces. Then run the script you will find, run_acl2 on Linux or Mac and run_acl2.exe on Windows, to start an ACL2 session. (Note that The first time you execute that command, the certificate (.cert) files are automatically fixed, according to the full pathname of your books/ directory.) Also see the ACL2 documentation topic on the ACL2 Sedan.

    • Windows

      In the past, a Windows Installer for ACL2 has included a Unix environment, pre-certified standard and workshop books, and a copy of Gnu Emacs. This capability has largely been superseded by the section on Building an Executable Image on Some Particular Systems and the Shortcut using the ACL2 Sedan, above. See also information about ACL2 on Windows.

    • MacPorts for Mac OS X

      ACL2 versions have sometimes been made available under MacPorts. If we learn that an up-to-date version is available, we will add instructions here.

    • Debian GNU Linux

      A Debian Gnu Linux package is available, which is likely to work on other Linux systems as well. Thanks to Camm Maguire for maintaining this package, and for pointing out that as Debian packages are simply ar and tar archives, they can be unpacked on any linux system, and who has said: "If someone is running Debian, all they want to do is 'apt-get install acl2', doing likewise for any optional add-on package they wish as well, e.g. emacs, infix, etc." Alternatively, Debian GNU Linux users may wish to download the Debian package for Linux. Or, see following annotated log, provided by Camm Maguire, to see another way to proceed.

           camm@localhost:~$ cd /tmp
           camm@localhost:/tmp$ mkdir a
           camm@localhost:/tmp$ cd a
           camm@localhost:/tmp/a$ wget ftp://ftp.debian.org/debian/pool/main/a/acl2/acl2*5.0*_{i386,all}*b
      
           ;;; or ftp, use browser, etc.  I.e. download the .deb files.  Replace
           ;;; i386 above with amd64 if the target is a 64bit Ubuntu machine.
           ;;; Other target names should be self explanatory.  The 'all'
           ;;; designation refers to binary independent data. 
      
           camm@localhost:/tmp/a$ ls
           acl2_5.0-1_i386.deb	   acl2-books-certs_5.0-1_all.deb   acl2-doc_5.0-1_all.deb    acl2-infix_5.0-1_i386.deb        acl2-source_5.0-1_all.deb
           acl2-books_5.0-1_i386.deb  acl2-books-source_5.0-1_all.deb  acl2-emacs_5.0-1_all.deb  acl2-infix-source_5.0-1_all.deb
           camm@localhost:/tmp/a$ for i in *b; do ar x $i; tar zxf data.tar.gz; done
      
           ;;; unpack files in current directory
      
           camm@localhost:/tmp/a$ sed -e 's,usr,tmp/a/usr,g' usr/bin/acl2 >tmp && chmod 755 tmp && mv tmp usr/bin/acl2
      
           ;;; Edit shell script wrapper to refer to the local path
      
           camm@localhost:/tmp/a$ usr/bin/acl2
           GCL (GNU Common Lisp)  2.6.7 CLtL1    Aug 22 2012 15:25:31
           Source License: LGPL(gcl,gmp), GPL(unexec,bfd,xgcl)
           Binary License:  GPL due to GPL'ed components: (XGCL READLINE UNEXEC)
           Modifications of this banner must retain notice of a compatible license
           Dedicated to the memory of W. Schelter
      
           Use (help) to get some basic information on how to use GCL.
           Temporary directory for compiler files set to /tmp/
      
            ACL2 Version 5.0 built August 25, 2012  11:53:08.
            Copyright (C) 2012  University of Texas at Austin
            ACL2 comes with ABSOLUTELY NO WARRANTY.  This is free software and you
            are welcome to redistribute it under certain conditions.  For details,
            see the GNU General Public License.
      
            Initialized with (INITIALIZE-ACL2 'INCLUDE-BOOK *ACL2-PASS-2-FILES*).
            See the documentation topic note-5-0 for recent changes.
            Note: We have modified the prompt in some underlying Lisps to further
            distinguish it from the ACL2 prompt.
      
           ACL2 Version 5.0.  Level 1.  Cbd "/tmp/a/".
           System books directory "/tmp/a/usr/share/acl2-5.0/books/".
           Type :help for help.
           Type (good-bye) to quit completely out of ACL2.
      
           ACL2 !>
      



    Obtaining the Sources and Community Books

    Obtain the sources and place them in directory dir as follows.

    (First, a note for Windows users only: we suggest that you obtain a Unix-like environment or, at least, download a utility such as djtarnt.exe to use with the -x option on gzipped tarfiles. WARNING: At least one user experienced CR/LF issues when using WinZIP, but we have received the suggestion that people untarring with that utility should probably turn off smart cr/lf conversion.)

    • Save acl2.tar.gz on directory dir. (You can run md5sum and compare with acl2-tar-gz-md5sum if you wish to verify the transmission.)

    • Execute the following four Unix commands. (Note: Gnu tar is preferred, as there have been some problems with long file names when using tar provided by SunOS. You may want to use the -i option, "tar xpvfi acl2.tar", if you have problems with other than Gnu tar. You can see if you have Gnu tar by running "tar -v".)

      cd dir
      tar xfz acl2.tar.gz
      rm acl2.tar.gz
      cd acl2-sources

    • We strongly suggest that you now install the community books, using one of the following methods, while still connected to the new directory.

      • Easy download. Fetch a gzipped tarfile of the community books from the Google Code website, and then extract as follows to create a books/ subdirectory:
             tar xfz books-6.3.tar.gz
      • OR, obtain a read-only copy using svn as a new subdirectory, books/:
             svn checkout http://acl2-books.googlecode.com/svn/branches/6.3 books
      • OR, for read-write access, become a member of the acl2-books project by contacting one of the project administrators (see the acl2-books project page). Then:
             svn checkout https://acl2-books.googlecode.com/svn/branches/6.3 books --username <name>

      NOTE: The second and third methods provide all of the community books, but the first omits the workshops/ and nonstd/ subdirectories of these books/, which (respectively) are books contributed in support of papers presented at ACL2 workshops, and books for use with ACL2(r). You can fetch these easily as gzipped tarfiles, as follows, using the following links to the Google Code website. Then put the gzipped tarfile(s) in the acl2-sources/books/ directory, connect to that directory, and extract to create workshops/ and nonstd/ subdirectories, respectively:
           tar xfz workshops-6.3.tar.gz
           tar xfz nonstd-6.3.tar.gz



    Creating or Obtaining an Executable Image

    The next step is to create an executable image. The common approach is to build that image from the sources you have already obtained. However, you may be able to take a short cut by downloading an ACL2 image, in which case you can skip ahead to Summary of Distribution. Otherwise you should click on one of the links just below. Choose the last option if you are using a Common Lisp on which you cannot save an image (e.g., a trial version of Allegro Common Lisp).

    PLEASE NOTE: The available memory for ACL2 is determined by the underlying Common Lisp executable. If you need more memory, refer to your Common Lisp's instructions for building an executable.




    Short Cut: Pre-Built ACL2 Images

    The site http://www.cs.utexas.edu/users/moore/acl2/v6-3/distrib/images/Readme.html contains links to ACL2 executables and packages. Each -md5sum file was created using md5sum. We may add additional links from time to time.

    Now proceed to Using ACL2.



    Building an Executable Image on a Unix-like System

    We assume you have obtained ACL2 and placed it in directory dir, as described above. If you downloaded a pre-built ACL2 image, you may skip this section. Connect to dir as above and execute

    cd acl2-sources
    make LISP=xxx

    where xxx is the command to run your local Common Lisp.

    By default, if no LISP=xxx is specified, LISP=ccl is used. On our hosts, ccl is the name of Clozure Common Lisp (CCL), which can be obtained as explained in the Requirements document.

    This will create executable saved_acl2 in the acl2-sources directory.

    The time taken to carry out this process depends on the host processor but may be only a few minutes for a fast processor. The size of the resulting binary image is dependent on which Lisp was used, but it may be up to a couple hundred megabytes or so.

    This make works for the Common Lisps listed in Requirements document on systems we have tested. See the file acl2-sources/GNUmakefile for further details. If this make command does not work for you, please see the instructions below for other than Unix-like systems.

    You can now skip to Using ACL2.




    Building an Executable Image on Other than a Unix-like System

    Next we describe how to create a binary image containing ACL2 without using the `make' utility. If you are using a trial version of Allegro Common Lisp, then you may not be able to save an image. In that case, skip to Running Without Building an Executable Image.

    See also Building an Executable Image on Some Particular Systems, in case you want to skip directly to the instructions in one of its subtopics.

    Otherwise, proceed as follows.

    Your Common Lisp should be one of those listed in Requirements document. Filenames below should default to the dir/acl2-sources directory; please connect to that directory before continuing.

    1. Remove file nsaved_acl2 if it exists.
    2. Start up Common Lisp in the acl2-sources directory and submit the following sequence of commands.
        ; Compile
        (load "init.lisp")
        (in-package "ACL2")
        (compile-acl2)
      
      The commands above will compile the ACL2 sources and create compiled object files on your acl2-sources subdirectory.
    3. Now exit your Common Lisp and invoke a fresh copy of it (mainly to avoid saving an image with the garbage created by the compilation process). Again arrange to connect to the acl2-sources subdirectory. In the fresh Lisp type:
        ; Initialization, first pass
        (load "init.lisp")
        (in-package "ACL2")
        (load-acl2)
        (initialize-acl2)
      
      This will load the new object files in the Lisp image and bootstrap ACL2 by reading and processing the source files. But the attempt at initialization will end in an error saying that it is impossible to finish because a certain file was compiled during the processing, thus dirtying the image yet again. (If however the attempt ends with an error during compilation of file TMP1.lisp, see the first troubleshooting tip below.)
    4. So now exit your Common Lisp and invoke a fresh copy of it (again arranging to connect to your acl2-sources subdirectory). Then, in the fresh Lisp type:
        ; Initialization, second pass
        (load "init.lisp")
        (in-package "ACL2")
        (save-acl2 (quote (initialize-acl2))
                   "saved_acl2")
      
      You have now saved an image. Exit Lisp now. Subsequent steps will put the image in the right place.

    5. Remove osaved_acl2 if it exists.

    6. IF saved_acl2 and saved_acl2.dxl both exist THEN:
      • move saved_acl2.dxl to osaved_acl2.dxl
      • move saved_acl2 to osaved_acl2 and edit osaved_acl2, changing saved_acl2.dxl (at end of line) to osaved_acl2.dxl
      ELSE IF saved_acl2 exists THEN:
      • move saved_acl2 to osaved_acl2
    7. Move nsaved_acl2 to saved_acl2.
    8. For some Common Lisps, a file nsaved_acl2.suffix is created for some suffix. Move it to: saved_acl2.suffix
    9. Make sure saved_acl2 is executable. For Windows this involves two mini-steps:
      (a) Remove the "$*" from the saved_acl2 script (because Windows does not understand $*). Consequently, any arguments you pass to ACL2 via the command line will be ignored.

      (b) Rename saved_acl2 to saved_acl2.bat, for example by executing the following command.

      rename saved_acl2 saved_acl2.bat




    Building an Executable Image on Some Particular Systems

    Subtopics of this section are as follows.


    Special Case: Building an Executable Image on a Windows System using GCL

    You may want to skip this section and instead read Instructions from David Rager for building ACL2 on Windows. Or, you may be able to download a pre-built ACL2 image for Windows instead of reading this section.

    Otherwise here are steps to follow.

    1. FIRST get GCL running on your Windows system using ONE of the following two options. Note that GCL can be unhappy with spaces in filenames, so you should probably save the GCL distribution to a directory whose path is free of spaces.
      • OR, obtain GCL for Windows systems from ftp://ftp.gnu.org/gnu/gcl/ or as explained above. You may wish to pick a .zip file from the cvs/ subdirectory (containing pre-releases) that has "mingw32" in the name.
      • OR ELSE, perhaps you can build GCL on your Windows system from the sources. The mingw tools and the cygnus bash shell have been used to build distributed GCL executables.
    2. SECOND, create an appropriate GCL batch file. When we tried running the script gclm/bin/gclm.bat that came with gcl-cvs-20021014-mingw32 from the above ftp site, a separate window popped up, and with an error. Many ACL2 users prefer running in an emacs shell buffer. (We obtained emacs for Windows from ftp://ftp.gnu.org/gnu/windows/emacs/21.2/emacs-21.2-fullbin-i386.tar.gz.) The following modification of gclm.bat seemed to solve the problem (your pathnames may vary).
      @
      % do not delete this line %
      @ECHO off
      set cwd=%cd%
      path C:\gcl\gclm\mingw\bin;%PATH%
      C:\gcl\gclm\lib\gcl-2.6.2\unixport\saved_gcl.exe -dir C:/gcl/gclm/lib/gcl-2.6.2/unixport/ -libdir  C:/gcl/gclm/lib/gcl-2.6.2/ -eval "(setq si::*allow-gzipped-file* t)" %1 %2 %3 %4 %5 %6 %7 %8 %9
      
    3. THIRD, follow the instructions for other than Unix-like systems above, though the resulting file may be called saved_acl2.exe rather than saved_acl2.
    4. FINALLY, create a file acl2.bat as explained in http://www.cs.utexas.edu/users/moore/acl2/v6-3/distrib/images/Readme.html.

    We hope that the above simply works. If you experience problems, the following hints may help.

    TROUBLESHOOTING:

    • We tried building ACL2 on Windows XP on top of GCL, our attempt broke at the end of the "Initialization, first pass" step, while compiling TMP1.lisp. That was easily remedied by starting up a fresh GCL session and invoking (compile-file "TMP1.lisp") before proceeding to the next step.
    • Yishai Feldman has provided some nice instructions at http://www.faculty.idc.ac.il/yishai/reasoning/win-install.htm, some of which we have tried to incorporate here. A useful point made there is that when you want to quit ACL2, use :good-bye (or (good-bye) which works even in raw Lisp). Or you can use (user::bye) in raw Lisp. The point is: Avoid control-c control-d, even thought that often works fine in emacs under Unix-like systems.
    • If the above batch file does not work for some reason, an alternate approach may be to set environment variables. You may be able to add to the PATH variable gcl-dir\gcc\bin, where gcl-dir is the directory where GCL is installed. To get to the place to set environment variables, you might be able to go to the control panel, under system, under advanced. Alternately, you might be able to get there by opening My Computer and right-clicking to get to Properties, then selecting the Advanced tab. At one time, when GCL/Windows was release as Maxima, Pete Manolios suggested adding the system variable LD_LIBRARY_PATH with the value "maxima-dir\gcc\i386-mingw32msvc\include"; this may or may not be necessary for your GCL installation (and the path would of course likely be different).



    Running Without Building an Executable Image

    The most convenient way to use ACL2 is first to install an executable image as described above for Unix-like systems and other platforms. However, in some cases this is not possible, for example if you are using a trial version of Allegro Common Lisp. In that case you should follow the steps below each time you want to start up ACL2.

    We assume you have obtained ACL2 and placed it in directory dir, as described above for Unix-like systems or other platforms. (If you downloaded a pre-built ACL2 image, then you may skip this section.) Connect to subdirectory acl2-sources of dir, start up your Common Lisp, and compile by executing the following forms. This sequence of steps need only be performed once.

      (load "init.lisp")
      (in-package "ACL2")
      (compile-acl2)
    
    Now each time you want to use ACL2, you need only execute the following forms after starting up Common Lisp in subdirectory acl2-sources of dir.
      (load "init.lisp")
      (in-package "ACL2")
      (load-acl2)
      (initialize-acl2)
    
    Note. The resulting process includes the ACL2 documentation, and hence will probably be considerably larger (perhaps twice the size) than the result of running an executable image created as described above.

    Now proceed to read more about Using ACL2.



    Summary of Distribution

    The ACL2 distribution, acl2.tar.gz, includes the ACL2 source files as well as the following files and directories (and a few others not mentioned here). Note that a books/ directory is not included; see instructions above to fetch the community books.
      LICENSE       ; ACL2 license file
      GNUmakefile   ; For GNU make
      TAGS          ; Handy for looking at source files with emacs
      doc/          ; ACL2 documentation in various formats
      emacs/        ; Some helpful emacs utilities
      installation/ ; Installation instructions (start with installation.html)
      saved/        ; Empty directory for backing up copies during make
    
    Also available are the following.

    Based on the preceding ACL2 release, we estimate that the entire acl2.tar.gz is about 7MB, which extracts to about 30MB, and the gzipped tarfile books-6.3.tar.gz (obtained as described above) is about 12MB, which extracts to about 70MB. Additional space is required to build an image, perhaps 50 to 300 megabytes depending on the platform (including the host Lisp); and more will be required to certify books.

    [Back to Installation Guide.]



















    acl2-sources/installation/requirements.html0000664002132200015000000002471012222120317020661 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Requirements

    Requirements

    [Back to main page of Installation Guide.]


    ACL2 Version 6.3
    Copyright (C) 2013, Regents of the University of Texas
    ACL2 is licensed under the terms of the LICENSE file distributed with ACL2.


    Table of Contents


    Performance comparisons

    You can see recent performance numbers by following this link, or by going to the ACL2 home page and following the link "Recent changes to this page".

    Obtaining Common Lisp

    ACL2 works on Unix, GNU-Linux, and Mac OS X, which we call "Unix-like systems", as well as many Windows operating systems (at least including Windows 98, Windows 2000, and Windows XP). It can be built on top of any of the following Common Lisps, listed here alphabetically.

    Obtaining Allegro Common Lisp

    The website for Allegro Common Lisp, a commercial implementation, is http://www.franz.com/. You may be able to obtain a trial version there.

    Obtaining CCL (OpenMCL)

    Clozure CL (CCL) was formerly known as OpenMCL. Quoting from the Clozure CL web page: ``Clozure CL is a fast, mature, open source Common Lisp implementation that runs on Linux, Mac OS X and BSD on either Intel x86-64 or PPC.''

    For Windows users: We observed stalls using CCL 1.5 on Windows (in May, 2010), though not with CCL 1.4. We have been told by a CCL implementor that this bug has been fixed, and people running CCL 1.5 under Windows at a revision less than 13900 should update.

    Here is an easy way to obtain and build the latest version (generally recommended) for Linux on running on x86 or x86-64. First execute the following shell command to create a ccl directory, but substituting for linuxx86, if appropriate, any of darwinx86 (which we use for modern Macs), freebsdx86, solarisx86, windows, darwinppc, or linuxppc.

    svn co http://svn.clozure.com/publicsvn/openmcl/trunk/linuxx86/ccl
    
    Note: if you prefer the latest release, you can obtain that instead, for example as follows (but replace "1.9" by the latest version, for example as described at http://ccl.clozure.com/download.html, and replace linuxx86 if appropriate as described above).
    svn co http://svn.clozure.com/publicsvn/openmcl/release/1.9/linuxx86/ccl
    
    Next rebuild the executable by issuing the following commands, but replace "./lx86cl64" by a suitable executable; e.g., for 64-bit Darwin (on Mac OS) use "./dx86cl64".
    ./lx86cl64
    (rebuild-ccl :full t)
    (quit)
    ./lx86cl64
    (rebuild-ccl :full t)
    (quit)
    

    Now your CCL executable is up to date. Next, create a suitable script, say as follows, where DIR is the full pathname for the directory above the new ccl directory.


    #!/bin/sh
    
    tmp=`uname -a | fgrep x86_64`
    export CCL_DEFAULT_DIRECTORY=DIR/ccl
    # Start up 64-bit or 32-bit lisp, respectively:
    if [ "$tmp" != "" ] ; then \
        DIR/ccl/scripts/ccl64 $* ; \
    else \
        DIR/ccl/scripts/ccl $* ; \
    fi
    

    Be sure to make your script executable. For example, if your script filename is my-script then on linux you might want to execute the following shell command.
    chmod +x my-script
    
    Your script (invoked with a suitable pathname, or just the filename if the directory is on your path) will now start the updated CCL lisp image.

    More details if you want or need them:
    Step 3 in http://trac.clozure.com/openmcl/wiki/UpdatingFromSource has more details on building from source. Alternatively, you can download a gzipped tar file; see the main CCL page, or visit the page of stable Clozure CL snapshots for ACL2 users. (Subversion and gzipped tar files are great, but not so much a CCL disk image (.dmg file), as we have had a report of the extracted CCL opening its own window when you start it up.) If you don't want to write your own script (as suggested above) then after obtaining CCL, you may wish to edit file ccl/scripts/ccl or file ccl/scripts/ccl64, depending on whether you want to use a 32-bit or 64-bit version (respectively).

    Obtaining CLISP

    CLISP is a non-commercial Common Lisp implementation, available from http://clisp.cons.org/.

    Obtaining CMU Common Lisp

    CMU Common Lisp (sometimes called CMUCL) is a non-commercial Common Lisp implementation, available from http://www.cons.org/cmucl/.

    Obtaining GCL

    You might be able to download a binary Debian package for ACL2. Thanks to Camm Maguire for maintaining this package. Note however that it may take some time after each ACL2 release for this binary Debian package to be updated for that release. Here is a shell command that might be used to obtain that package (if running Debian).

    apt-get -q install gcl gcl-doc
    
    Otherwise, it should be easy to obtain and build GCL yourself. There are two recommended versions of GCL for building ACL2: GCL 2.6.8 and GCL 2.6.10. (GCL 2.6.9, which is not recommended, has some issues that were fixed in GCL 2.6.10.) GCL 2.6.8 appears to be faster for ACL2 regressions than GCL 2.6.10; we obtained the following times on a 64-bit Linux system using a development snapshot of ACL2 as of Sept. 23, 2013.
       ACL2 built on non-ANSI GCL 2.6.8:
       33235.713u 1275.991s 1:29:23.91 643.4%	0+0k 156624+4638504io 25pf+0w
    
       ACL2 built on non-ANSI GCL 2.6.10pre:
       40678.982u 1013.967s 1:45:06.91 661.0%	0+0k 153400+6615176io 9pf+0w
    
       ACL2 built on ANSI GCL 2.6.10pre:
       67950.466u 1050.753s 2:44:58.65 697.0%	0+0k 327160+6955712io 7pf+0w
    
    On the other hand, modern Common Lisp implementations are ANSI compliant, and GCL 2.6.10 may have better ANSI support (which is needed if you choose to build ACL2(h)).

    As of this writing (late September 2013), it might be best to fetch GCL 2.6.8 for most purposes but GCL 2.6.10 if for some reason you want an ANSI build of GCL. You can fetch it as a tarball from main GNU website for GCL. From GCL source you can build an executable by extracting from the tarball, standing in the resulting gcl/ directory, and issuing one of the following commands.

    # Recommended for 64-bit Linux:
    ./configure --enable-maxpage=1048576 && make
    
    # Recommended for Mac OS:
    ./configure && make
    
    # If you want an ANSI build
    # (but add "--enable-maxpage=1048576" as above for linux):
    ./configure --enable-ansi && make
    
    As of late September 2013, GCL 2.6.10 is not yet available as a tarball. However, you can get a pre-release (which we expect to work fine, having used it successfully ourselves) like this (assuming you have git installed):
    git clone git://git.sv.gnu.org/gcl.git
    cd gcl
    git checkout Version_2_6_10pre
    cd gcl
    
    Then run configure and make as indicated above.

    Obtaining LispWorks

    LispWorks is a commercial Common Lisp implementation. You can download a free, restricted, version from http://www.lispworks.com/. You may ask the vendor for an evaluation license for the full product if you are considering purchasing a license.

    Obtaining SBCL

    SBCL (Steel Bank Common Lisp) is a non-commercial Common Lisp implementation, available from http://sbcl.sourceforge.net/.

    [Back to Installation Guide.]



















    acl2-sources/installation/using.html0000664002132200015000000003000712222116745017271 0ustar kaufmannacl2 ACL2 Version 6.3 Installation Guide: Using ACL2

    Using ACL2

    [Back to main page of Installation Guide.]

    Table of Contents


    Here we begin with a discussion of how to invoke ACL2 interactively. We then discuss testing as well as the certification of ACL2 community books. We conclude with a discussion of the documentation.



    Invoking ACL2

    At this point, dir has a subdirectory called acl2-sources. The sources and perhaps an executable image are located on that subdirectory. However, if you have not saved an image but instead use the directions for Running Without Building an Executable Image, skip to When ACL2 Starts Up below.

    The executable image is called acl2-sources/saved_acl2. You can invoke ACL2 by running that image, e.g.,

    mycomputer% dir/acl2-sources/saved_acl2

    If you on a Unix-like system, then to make it easy to invoke ACL2 by typing a short command, e.g.,

    mycomputer% acl2

    you may want to install an executable file on your path, e.g., /usr/local/bin/acl2, containing the following two lines:

    #!/bin/csh -f
    dir/acl2-sources/saved_acl2


    Note: A carriage return in the file after the last line above may be important!


    When ACL2 Starts Up

    When you invoke ACL2, you should see the host Common Lisp print a header concerning the ACL2 version, license and copyright.

    Most or all hosts then automatically enter the ACL2 ``command loop,'' an ACL2 read-eval-print loop with the prompt:

    ACL2 !>
    
    If however a host leaves you in Common Lisp's read-eval-print loop, then you'll need to evaluate the Common Lisp expression (ACL2::LP) or simply (LP) if the current package is "ACL2".

    Once in the ACL2 command loop, you can type an ACL2 term, typically followed by ``return'' or ``enter,'' and ACL2 will evaluate the term, print its value, and prompt you for another one. Below are three simple interactions:

    ACL2 !>t
    T
    ACL2 !>'abc
    ABC
    ACL2 !>(+ 2 2)
    4
    

    To get out of the ACL2 command loop, type the :q command. This returns you to the host Common Lisp. We sometimes call this ``raw Lisp.'' You may re-enter the command loop with (LP) as above.

    Note that when you are in raw Lisp you can overwrite or destroy ACL2 by executing inappropriate Common Lisp expressions. All bets are off once you've exited our loop. That said, many users do it. For example, you might exit our loop, activate some debugging or trace features in raw Lisp, and then reenter our loop. While developing proofs or tracking down problems, this is reasonable behavior.

    Now you are ready to test your image.



    Testing ACL2

    An easy way to test the theorem prover is to type the following term to the ACL2 command loop:

    :mini-proveall
    
    This will cause a moderately long sequence of commands to be processed, each of which is first printed out as though you had typed it. Each will print some text, generally a proof of some conjecture. None should fail.

    A more elaborate test is to certify the community books, which is a good idea anyhow; this is our next topic. On a Unix-like system, you can also certify just a small but useful subset of the books in a few minutes by executing, in directory dir/acl2-sources:

    make certify-books-short
    



    Certifying ACL2 Books

    The community books have been contributed mainly by users and may be obtained as explained elsewhere, to create subdirectory acl2-sources/books. The general topic of books is discussed thoroughly in the ACL2 documentation; see http://www.cs.utexas.edu/users/moore/acl2/current/BOOKS.html.

    Books should be ``certified'' before they are used. We do not distribute certificates with our books, mainly because certification produces compiled code specific to the host. You should certify the books locally, both as a test of your ACL2 image and because books generally need to be certified before they can be used.

    It is easy to re-certify all the community books on a Unix-like system. We recommend you do this. If you have entered ACL2, exit to the operating system, e.g., evaluting the form, (quit), or by control-d in many systems.

    While connected to dir/acl2-sources, execute

    make certify-books
    
    This will generate minimal output to the screen and will probably take an hour or two. Failure is indicated by the presence of CERTIFICATION FAILED in the log.

    To remove the files thus created, invoke:

    make clean-books
    

    The certify-books target does not cause workshops/ books to be certified. If you want to certify those books as well, you will first need to download the gzipped tar file of the workshops books from the Google Code website to the books/ directory, and then gunzip and extract it to create subdirectory workshops. You can certify all the community books, including books for the workshops (including those from the 1999 workshop as described in the (hardbound) book Computer-Aided Reasoning: ACL2 Case Studies), using the command:

    make regression
    
    Our main installation page contains a discussion of options for the above command, such as avoidance of `make' option -j.

    By default, certification uses the image dir/acl2-sources/saved_acl2. You may specify any ACL2 image, as long as it is either a command on your path or an absolute file name, for example as follows.

    make certify-books ACL2=my-acl2
    
    make regression ACL2=/u/smith/projects/acl2/saved_acl2
    

    We apologize to users of other than Unix-like systems (i.e., other than Unix, GNU-Linux, and Mac OS X): we do not provide instructions for recertifying all the community books on such systems, though there are such environments that can be installed on Windows (e.g., Cygwin). The certification methods provided by the authors of the books vary greatly and we codified them in the makefile, which is named GNUmakefile, used above. Some subdirectories of the community book (typically installed in acl2-sources/books/) contain either a README file or a certify.lsp file. Users who wish to certify one of these books and who cannot figure out (from these scant clues) what to type to ACL2 should not hesitate to contact the authors.

    Next proceed to the section on Documentation.



    Documentation

    ACL2's documentation is a hypertext document that, if printed in book form, is just over 2050 pages, or about 2.4 megabytes of gzipped postscript. Its hypertext character makes it far more pleasing to read with an interactive browser. The documentation is available in several formats: HTML, Texinfo, Postscript and ACL2 documentation strings, and more recently, in web-browsable form with some community books' documentation, courtesy of Jared Davis. The ACL2 documentation is copyrighted by the Regents of the University of Texas under the terms of the LICENSE file distributed with ACL2.

    Two Web-based guided tours of ACL2 are available from the home page noted below. If you are already familiar with Nqthm, you might find it useful to look at the documentation node NQTHM-TO-ACL2. Another useful documentation topic for beginning ACL2 users is the node TUTORIAL.

    HTML

    The ACL2 Home Page is

    http://www.cs.utexas.edu/users/moore/acl2/index.html

    The home page provides a selected bibliography, a search button (near the top of the page), guided tours of the system, and the complete hypertext documentation tree.

    Once you have installed ACL2, the HTML form of the documentation is available locally as dir/acl2-sources/doc/HTML/acl2-doc.html.

    We urge you to browse your local copy of the documentation rather than our Web copy, simply to reduce Web traffic and the demand on our server. (Macintosh users using MacOS 9 and earlier may, however, find filenames being truncated and hence will want to avoid the local documentation.)

    Emacs Info

    This is a very convenient format for accessing the ACL2 documentation from within Emacs. In Emacs, invoke

    meta-x info
    
    and then, if you are unfamiliar with Info, type
    control-h m
    
    to see a list of commands available. In particular, type

    g (dir/acl2-sources/doc/EMACS/acl2-doc-emacs.info)TOP

    to enter the ACL2 documentation. Alternatively, your system administrator can add an ACL2 node to the top-level Info menu. The appropriate entry might read:

    * ACL2 i.j: (dir/acl2-sources/doc/EMACS/acl2-doc-emacs).
              Documentation for ACL2 version i.j.

    Note: The Emacs Info and Postscript versions of our documentation were created using the file acl2-sources/doc/texinfo.tex which is copyrighted by the Free Software Foundation, Inc. (See that file for copyright and license information.)

    Users new to emacs may find it helpful to load into emacs the file dir/acl2-sources/emacs/emacs-acl2.el. Utilities offered by this file are documented near the top of the file.

    Postscript

    The Postscript version of the documentation is not included in our normal distribution because it is so much less useful than the hyper-text versions. But a gzipped Postscript (2.6 MB) version is available. It prints as a book of over 2200 pages and contains a Table of Contents and an index to all documented topics.

    ACL2 Documentation Strings

    The ACL2 system has facilities for browsing the documentation. When you are in the ACL2 command loop, you may query the documentation on a given topic by typing the command

    :doc topic

    where topic is the Lisp symbol naming the topic you want to learn about. To learn more about the on-line documentation, type :help and then return.

    Note, however, that you may find it more convenient to view the documentation in a web browser (starting at doc/HTML/acl2-doc.html) or in Emacs info (starting at doc/EMACS/acl2-doc-emacs.info).

    [Back to Installation Guide.]



















    acl2-sources/installation/windows7.html0000664002132200015000000001006412222111131017706 0ustar kaufmannacl2 Helpful Instructions for Setting up ACL2 and Windows

    Helpful Instructions for Setting up ACL2 and Windows

    We thank David Rager for providing the following instructions, which we include verbatim and expect apply to future versions. Note: We recommend using CCL for Windows builds, in part so that interrupts (Control-C) will work. If you are using Windows, please note that there have been stalls using CCL 1.5 on Windows, though not with CCL 1.4. We have been told by a CCL implementor that this bug has been fixed, and people running CCL 1.5 under Windows at a revision less than 13900 should update.

    I was able to get ACL2 3.6.1 to install and build the regression suite on Windows 7 with the following steps. I did not have to install cygwin.

    1. Install MinGW. At the time of this writing, the following direct link works
      MinGW-5.1.6.exe

      If that direct link doesn't work, click on "Automated MinGW Installer" on the more general MinWG project files page.

    2. Install MSys. At the time of this writing, the following direct link works
      MSYS-1.0.11.exe

      If that direct link doesn't work, click on "MSYS Base System" on the more general MinWG project files page.

    3. Add "C:\msys\1.0\bin" to my environment variable "path". The way you do this varies with each Windows XP/Vista/7. Roughly speaking, you need to go to the control panel, and open up your system settings. Get to the advanced system settings and click on environment variables. Edit the "path" environment variable and add ";C:\msys\1.0\bin" to it. At this point you might need to restart your computer, but I did not have to do so on Windows 7. I did, however, have to restart my emacs.
    4. Realize that using "\" to indicate paths in windows confuses some linux programs and that you might need to use "/" sometimes.

    5. After expanding the ACL2 sources, cd to that directory and type something similar to the following (modify it to set LISP to your LISP executable1)
      make LISP=c:/ccl/wx86cl64.exe
      The "make.exe" that will be used is intentionally the MSys version, not the MinGW version.

    6. After your ACL2 image builds, make acl2 executable, specifically
      • Remove the "$*" from the saved_acl2 script (because Windows does not understand $*). Consequently, any arguments you pass to ACL2 via the command line will be ignored.
      • Rename saved_acl2 to saved_acl2.bat, for example by executing the following command: rename saved_acl2 saved_acl2.bat
    7. You can now make the regression suite by typing
      make regression ACL2=c:/acl2-3.6.1/acl2-sources/saved_acl2.bat

      [Note added later: you may need to add `make' option ACL2_CENTAUR=skip, for example if you have issues with Perl.]


    1I have intentionally ommitted instructions on how to setup a LISP on windows. However, I include one link that should suffice: Obtaining CCL

    acl2-sources/installation/windows-gcl-jared.html0000664002132200015000000000330512222111131021445 0ustar kaufmannacl2 Instructions from Jared Davis for building ACL2 on Windows using mingw

    Instructions from Jared Davis for building ACL2 on Windows using mingw

    We thank Jared Davis for providing the following instructions for ACL2 Version 2.8, which we include verbatim and expect apply to future versions.
    	      Building ACL2 on Windows from Scratch
       _____________________________________________________________
    
       Note: The disk space requirements are large.  Not including
       emacs, I had about 275 MB taken up by msys/mingw32/gcl/acl2
       during the build process.  You can probably use much less
       space by removing files after you use them, but I didn't
       bother to do that.
    
       Here are the steps I took:
    
       Downloaded emacs 21.3 full distribution and installed
       Downloaded msys 1.10.10, installed to c:\acl2
       Downloaded mingw 3.1.0-1, installed to c:\acl2\mingw
       Downloaded gcl 2.5.3, extracted to c:\acl2\mingw
       Downloaded acl2 2.8, extracted to c:\acl2\sources
    
    
    
       Compiling gcl:
    
         in msys:
    
           cd /acl2/ming2/gcl-2.5.3
           ./configure
           make
           make install
    
    
    
       Compiling acl2:
    
         copy "etags.exe" to /mingw/bin.  you can find this program
         in your emacs folder, under "bin".
    
         in msys:
    
           cd /sources
           make
    
    
    
       Certifying ACL2 books:
       This took 111 minutes on my Athlon 2500+
    
         in msys:
    
           cd /sources
    
           mv nsaved_acl2.gcl.exe saved_acl2.exe
    
           vim books/Makefile-generic, remove "nice" from this line:
    	   ACL2=time nice ../../saved_acl2
    
           make certify-books ACL2=/sources/saved_acl2.exe
    
    
    acl2-sources/saved/0002775002132200015000000000000012222334002013645 5ustar kaufmannacl2